00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include <float.h>
00023 #include <math.h>
00024
00025 #include <opencalcexport.h>
00026
00027 #include <qdatetime.h>
00028 #include <qdom.h>
00029 #include <qfile.h>
00030 #include <qregexp.h>
00031 #include <qvaluelist.h>
00032
00033 #include <kdebug.h>
00034 #include <kmessagebox.h>
00035 #include <kmdcodec.h>
00036 #include <kgenericfactory.h>
00037 #include <klocale.h>
00038
00039 #include <KoDocumentInfo.h>
00040 #include <KoFilterChain.h>
00041 #include <KoGlobal.h>
00042
00043 #include <kspread_aboutdata.h>
00044 #include <kspread_cell.h>
00045 #include <kspread_doc.h>
00046 #include <kspread_format.h>
00047 #include <kspread_map.h>
00048 #include <kspread_view.h>
00049 #include <kspread_canvas.h>
00050 #include <kspread_sheet.h>
00051 #include <kspread_sheetprint.h>
00052 #include <kspread_style.h>
00053 #include <kspread_style_manager.h>
00054 #include <kspread_util.h>
00055
00056 using namespace KSpread;
00057
00058 typedef QValueList<Reference> AreaList;
00059
00060 class OpenCalcExportFactory : KGenericFactory<OpenCalcExport, KoFilter>
00061 {
00062 public:
00063 OpenCalcExportFactory(void) : KGenericFactory<OpenCalcExport, KoFilter> ("kspreadopencalcexport")
00064 {}
00065 protected:
00066 virtual void setupTranslations( void )
00067 {
00068 KGlobal::locale()->insertCatalogue( "kofficefilters" );
00069 }
00070 };
00071
00072 K_EXPORT_COMPONENT_FACTORY( libopencalcexport, OpenCalcExportFactory() )
00073
00074 #define STOPEXPORT \
00075 do \
00076 { \
00077 delete store; \
00078 return false; \
00079 } while(0)
00080
00081 OpenCalcExport::OpenCalcExport( KoFilter *, const char *, const QStringList & )
00082 : KoFilter(), m_locale( 0 )
00083 {
00084 }
00085
00086 KoFilter::ConversionStatus OpenCalcExport::convert( const QCString & from,
00087 const QCString & to )
00088 {
00089
00090
00091
00092
00093
00094
00095
00096
00097
00098
00099
00100
00101
00102 KoDocument * document = m_chain->inputDocument();
00103
00104 if ( !document )
00105 return KoFilter::StupidError;
00106
00107 if ( !::qt_cast<const KSpread::Doc *>( document ) )
00108 {
00109 kdWarning(30518) << "document isn't a KSpread::Doc but a "
00110 << document->className() << endl;
00111 return KoFilter::NotImplemented;
00112 }
00113
00114 if ( ( to != "application/vnd.sun.xml.calc") || (from != "application/x-kspread" ) )
00115 {
00116 kdWarning(30518) << "Invalid mimetypes " << to << " " << from << endl;
00117 return KoFilter::NotImplemented;
00118 }
00119
00120 const Doc * ksdoc = static_cast<const Doc *>(document);
00121
00122 if ( ksdoc->mimeType() != "application/x-kspread" )
00123 {
00124 kdWarning(30518) << "Invalid document mimetype " << ksdoc->mimeType() << endl;
00125 return KoFilter::NotImplemented;
00126 }
00127
00128 m_locale = static_cast<Doc*>(document)->locale();
00129 if ( !writeFile( ksdoc ) )
00130 return KoFilter::CreationError;
00131
00132 emit sigProgress( 100 );
00133
00134 return KoFilter::OK;
00135 }
00136
00137 bool OpenCalcExport::writeFile( const Doc * ksdoc )
00138 {
00139 KoStore * store = KoStore::createStore( m_chain->outputFile(), KoStore::Write, "", KoStore::Zip );
00140
00141 if ( !store )
00142 return false;
00143
00144 uint filesWritten = 0;
00145
00146 if ( !exportContent( store, ksdoc ) )
00147 STOPEXPORT;
00148 else
00149 filesWritten |= contentXML;
00150
00151
00152 if ( !exportDocInfo( store, ksdoc ) )
00153 STOPEXPORT;
00154 else
00155 filesWritten |= metaXML;
00156
00157 if ( !exportStyles( store, ksdoc ) )
00158 STOPEXPORT;
00159 else
00160 filesWritten |= stylesXML;
00161
00162 if ( !exportSettings( store, ksdoc ) )
00163 STOPEXPORT;
00164 else
00165 filesWritten |= settingsXML;
00166
00167 if ( !writeMetaFile( store, filesWritten ) )
00168 STOPEXPORT;
00169
00170
00171 delete store;
00172 store = 0;
00173
00174 return true;
00175 }
00176
00177 bool OpenCalcExport::exportDocInfo( KoStore * store, const Doc* ksdoc )
00178 {
00179 if ( !store->open( "meta.xml" ) )
00180 return false;
00181
00182 KoDocumentInfo * docInfo = ksdoc->documentInfo();
00183 KoDocumentInfoAbout * aboutPage = static_cast<KoDocumentInfoAbout *>( docInfo->page( "about" ) );
00184 KoDocumentInfoAuthor * authorPage = static_cast<KoDocumentInfoAuthor*>( docInfo->page( "author" ) );
00185
00186 QDomDocument meta;
00187 meta.appendChild( meta.createProcessingInstruction( "xml","version=\"1.0\" encoding=\"UTF-8\"" ) );
00188
00189 QDomElement content = meta.createElement( "office:document-meta" );
00190 content.setAttribute( "xmlns:office", "http://openoffice.org/2000/office");
00191 content.setAttribute( "xmlns:xlink", "http://www.w3.org/1999/xlink" );
00192 content.setAttribute( "xmlns:dc", "http://purl.org/dc/elements/1.1/" );
00193 content.setAttribute( "xmlns:meta", "http://openoffice.org/2000/meta" );
00194 content.setAttribute( "office:version", "1.0" );
00195
00196 QDomNode officeMeta = meta.createElement( "office:meta" );
00197
00198 QDomElement data = meta.createElement( "meta:generator" );
00199 QString app( "KSpread " );
00200 app += KSpread::version;
00201 data.appendChild( meta.createTextNode( app ) );
00202 officeMeta.appendChild( data );
00203
00204 data = meta.createElement( "meta:initial-creator" );
00205 data.appendChild( meta.createTextNode( authorPage->fullName() ) );
00206 officeMeta.appendChild( data );
00207
00208 data = meta.createElement( "meta:creator" );
00209 data.appendChild( meta.createTextNode( authorPage->fullName() ) );
00210 officeMeta.appendChild( data );
00211
00212 data = meta.createElement( "dc:description" );
00213 data.appendChild( meta.createTextNode( aboutPage->abstract() ) );
00214 officeMeta.appendChild( data );
00215
00216 data = meta.createElement( "meta:keywords" );
00217 QDomElement dataItem = meta.createElement( "meta:keyword" );
00218 dataItem.appendChild( meta.createTextNode( aboutPage->keywords() ) );
00219 data.appendChild( dataItem );
00220 officeMeta.appendChild( data );
00221
00222 data = meta.createElement( "dc:title" );
00223 data.appendChild( meta.createTextNode( aboutPage->title() ) );
00224 officeMeta.appendChild( data );
00225
00226 data = meta.createElement( "dc:subject" );
00227 data.appendChild( meta.createTextNode( aboutPage->subject() ) );
00228 officeMeta.appendChild( data );
00229
00230 const QDateTime dt ( QDateTime::currentDateTime() );
00231 if ( dt.isValid() )
00232 {
00233 data = meta.createElement( "dc:date" );
00234 data.appendChild( meta.createTextNode( dt.toString( Qt::ISODate ) ) );
00235 officeMeta.appendChild( data );
00236 }
00237
00238
00239
00240
00241
00242
00243
00244
00245
00246
00247 data = meta.createElement( "meta:document-statistic" );
00248 data.setAttribute( "meta:table-count", QString::number( ksdoc->map()->count() ) );
00249
00250 officeMeta.appendChild( data );
00251
00252 content.appendChild( officeMeta );
00253 meta.appendChild( content );
00254
00255 QCString doc( meta.toCString() );
00256 kdDebug(30518) << "Meta: " << doc << endl;
00257
00258 store->write( doc, doc.length() );
00259
00260 if ( !store->close() )
00261 return false;
00262
00263 return true;
00264 }
00265
00266 bool OpenCalcExport::exportSettings( KoStore * store, const Doc * ksdoc )
00267 {
00268 if ( !store->open( "settings.xml" ) )
00269 return false;
00270
00271 QDomDocument doc;
00272 doc.appendChild( doc.createProcessingInstruction( "xml","version=\"1.0\" encoding=\"UTF-8\"" ) );
00273
00274 QDomElement settings = doc.createElement( "office:document-settings" );
00275 settings.setAttribute( "xmlns:office", "http://openoffice.org/2000/office");
00276 settings.setAttribute( "xmlns:xlink", "http://www.w3.org/1999/xlink" );
00277 settings.setAttribute( "xmlns:config", "http://openoffice.org/2001/config" );
00278 settings.setAttribute( "office:version", "1.0" );
00279
00280 QDomElement begin = doc.createElement( "office:settings" );
00281
00282 QDomElement configItem = doc.createElement("config:config-item-set" );
00283 configItem.setAttribute( "config:name", "view-settings" );
00284
00285 QDomElement mapIndexed = doc.createElement( "config:config-item-map-indexed" );
00286 mapIndexed.setAttribute("config:name", "Views" );
00287 configItem.appendChild( mapIndexed );
00288
00289 QDomElement mapItem = doc.createElement("config:config-item-map-entry" );
00290
00291 QDomElement attribute = doc.createElement("config:config-item" );
00292 attribute.setAttribute( "config:name", "ActiveTable" );
00293 attribute.setAttribute( "config:type", "string" );
00294
00295 View * view = static_cast<View*>( ksdoc->views().getFirst());
00296 QString activeTable;
00297 if ( view )
00298 {
00299 Canvas * canvas = view->canvasWidget();
00300 activeTable = canvas->activeSheet()->sheetName();
00301
00302 view->saveCurrentSheetSelection();
00303 }
00304 attribute.appendChild( doc.createTextNode( activeTable ) );
00305 mapItem.appendChild( attribute );
00306
00307 QDomElement configmaped = doc.createElement( "config:config-item-map-named" );
00308 configmaped.setAttribute( "config:name","Tables" );
00309
00310 QPtrListIterator<Sheet> it( ksdoc->map()->sheetList() );
00311 for( ; it.current(); ++it )
00312 {
00313 QPoint marker;
00314 if ( view )
00315 {
00316 marker = view->markerFromSheet( *it );
00317 }
00318 QDomElement tmpItemMapNamed = doc.createElement( "config:config-item-map-entry" );
00319 tmpItemMapNamed.setAttribute( "config:name", ( *it )->tableName() );
00320
00321 QDomElement sheetAttribute = doc.createElement( "config:config-item" );
00322 sheetAttribute.setAttribute( "config:name", "CursorPositionX" );
00323 sheetAttribute.setAttribute( "config:type", "int" );
00324 sheetAttribute.appendChild( doc.createTextNode( QString::number(marker.x() ) ) );
00325 tmpItemMapNamed.appendChild( sheetAttribute );
00326
00327 sheetAttribute = doc.createElement( "config:config-item" );
00328 sheetAttribute.setAttribute( "config:name", "CursorPositionY" );
00329 sheetAttribute.setAttribute( "config:type", "int" );
00330 sheetAttribute.appendChild( doc.createTextNode( QString::number(marker.y() ) ) );
00331 tmpItemMapNamed.appendChild( sheetAttribute );
00332
00333 configmaped.appendChild( tmpItemMapNamed );
00334 }
00335 mapItem.appendChild( configmaped );
00336
00337
00338
00339 mapIndexed.appendChild( mapItem );
00340
00341 begin.appendChild( configItem );
00342
00343 settings.appendChild( begin );
00344
00345 doc.appendChild( settings );
00346
00347 QCString f( doc.toCString() );
00348 kdDebug(30518) << "Settings: " << (char const * ) f << endl;
00349
00350 store->write( f, f.length() );
00351
00352 if ( !store->close() )
00353 return false;
00354
00355 return true;
00356 }
00357
00358 bool OpenCalcExport::exportContent( KoStore * store, const Doc * ksdoc )
00359 {
00360 if ( !store->open( "content.xml" ) )
00361 return false;
00362
00363 createDefaultStyles();
00364
00365 QDomDocument doc;
00366 doc.appendChild( doc.createProcessingInstruction( "xml","version=\"1.0\" encoding=\"UTF-8\"" ) );
00367
00368 QDomElement content = doc.createElement( "office:document-content" );
00369 content.setAttribute( "xmlns:office", "http://openoffice.org/2000/office");
00370 content.setAttribute( "xmlns:style", "http://openoffice.org/2000/style" );
00371 content.setAttribute( "xmlns:text", "http://openoffice.org/2000/text" );
00372 content.setAttribute( "xmlns:table", "http://openoffice.org/2000/table" );
00373 content.setAttribute( "xmlns:draw", "http://openoffice.org/2000/drawing" );
00374 content.setAttribute( "xmlns:fo", "http://www.w3.org/1999/XSL/Format" );
00375 content.setAttribute( "xmlns:xlink", "http://www.w3.org/1999/xlink" );
00376 content.setAttribute( "xmlns:number", "http://openoffice.org/2000/datastyle" );
00377 content.setAttribute( "xmlns:svg", "http://www.w3.org/2000/svg" );
00378 content.setAttribute( "xmlns:chart", "http://openoffice.org/2000/chart" );
00379 content.setAttribute( "xmlns:dr3d", "http://openoffice.org/2000/dr3d" );
00380 content.setAttribute( "xmlns:math", "http://www.w3.org/1998/Math/MathML" );
00381 content.setAttribute( "xmlns:form", "http://openoffice.org/2000/form" );
00382 content.setAttribute( "xmlns:script", "http://openoffice.org/2000/script" );
00383 content.setAttribute( "office:class", "spreadsheet" );
00384 content.setAttribute( "office:version", "1.0" );
00385
00386 QDomElement data = doc.createElement( "office:script" );
00387 content.appendChild( data );
00388
00389 if ( !exportBody( doc, content, ksdoc ) )
00390 return false;
00391
00392 doc.appendChild( content );
00393
00394 QCString f( doc.toCString() );
00395 kdDebug(30518) << "Content: " << (char const * ) f << endl;
00396
00397 store->write( f, f.length() );
00398
00399 if ( !store->close() )
00400 return false;
00401
00402 return true;
00403 }
00404
00405 void exportNamedExpr( QDomDocument & doc, QDomElement & parent,
00406 AreaList const & namedAreas )
00407 {
00408 AreaList::const_iterator it = namedAreas.begin();
00409 AreaList::const_iterator end = namedAreas.end();
00410
00411 while ( it != end )
00412 {
00413 QDomElement namedRange = doc.createElement( "table:named-range" );
00414
00415 Reference ref = *it;
00416
00417 namedRange.setAttribute( "table:name", ref.ref_name );
00418 namedRange.setAttribute( "table:base-cell-address", convertRefToBase( ref.sheet_name, ref.rect ) );
00419 namedRange.setAttribute( "table:cell-range-address", convertRefToRange( ref.sheet_name, ref.rect ) );
00420
00421 parent.appendChild( namedRange );
00422
00423 ++it;
00424 }
00425 }
00426
00427 bool OpenCalcExport::exportBody( QDomDocument & doc, QDomElement & content, const Doc * ksdoc )
00428 {
00429 QDomElement fontDecls = doc.createElement( "office:font-decls" );
00430 QDomElement autoStyles = doc.createElement( "office:automatic-styles" );
00431 QDomElement body = doc.createElement( "office:body" );
00432
00433 if ( ksdoc->map()->isProtected() )
00434 {
00435 body.setAttribute( "table:structure-protected", "true" );
00436
00437 QCString passwd;
00438 ksdoc->map()->password( passwd );
00439 if ( passwd.length() > 0 )
00440 {
00441 QCString str( KCodecs::base64Encode( passwd ) );
00442 body.setAttribute( "table:protection-key", QString( str.data() ) );
00443 }
00444 }
00445
00446
00447
00448 QPtrListIterator<Sheet> it( ksdoc->map()->sheetList() );
00449
00450 for( it.toFirst(); it.current(); ++it )
00451 {
00452 SheetStyle ts;
00453 int maxCols = 1;
00454 int maxRows = 1;
00455 Sheet * sheet = it.current();
00456
00457 ts.visible = !sheet->isHidden();
00458
00459 QDomElement tabElem = doc.createElement( "table:table" );
00460 tabElem.setAttribute( "table:style-name", m_styles.sheetStyle( ts ) );
00461
00462 if ( sheet->isProtected() )
00463 {
00464 tabElem.setAttribute( "table:protected", "true" );
00465
00466 QCString passwd;
00467 sheet->password( passwd );
00468 if ( passwd.length() > 0 )
00469 {
00470 QCString str( KCodecs::base64Encode( passwd ) );
00471 tabElem.setAttribute( "table:protection-key", QString( str.data() ) );
00472 }
00473 }
00474
00475 QString name( sheet->tableName() );
00476
00477 int n = name.find( ' ' );
00478 if ( n != -1 )
00479 {
00480 kdDebug(30518) << "Sheet name converting: " << name << endl;
00481 name[n] == '_';
00482 kdDebug(30518) << "Sheet name converted: " << name << endl;
00483 }
00484 name = name.replace( ' ', "_" );
00485
00486 QRect _printRange = sheet->print()->printRange();
00487 if ( _printRange != ( QRect( QPoint( 1, 1 ), QPoint( KS_colMax, KS_rowMax ) ) ) )
00488 {
00489 QString range= convertRangeToRef( name, _printRange );
00490
00491 tabElem.setAttribute( "table:print-ranges", range );
00492 }
00493
00494
00495 tabElem.setAttribute( "table:name", name );
00496
00497 maxRowCols( sheet, maxCols, maxRows );
00498
00499 exportSheet( doc, tabElem, sheet, maxCols, maxRows );
00500
00501 body.appendChild( tabElem );
00502 }
00503
00504 KoDocument * document = m_chain->inputDocument();
00505 Doc * kspreadDoc = static_cast<Doc *>( document );
00506
00507 AreaList namedAreas = kspreadDoc->listArea();
00508 if ( namedAreas.count() > 0 )
00509 {
00510 QDomElement namedExpr = doc.createElement( "table:named-expressions" );
00511 exportNamedExpr( doc, namedExpr, namedAreas );
00512
00513 body.appendChild( namedExpr );
00514 }
00515
00516 m_styles.writeStyles( doc, autoStyles );
00517 m_styles.writeFontDecl( doc, fontDecls );
00518
00519 content.appendChild( fontDecls );
00520 content.appendChild( autoStyles );
00521 content.appendChild( body );
00522
00523 return true;
00524 }
00525
00526 void OpenCalcExport::exportSheet( QDomDocument & doc, QDomElement & tabElem,
00527 const Sheet * sheet, int maxCols, int maxRows )
00528 {
00529 kdDebug(30518) << "exportSheet: " << sheet->tableName() << endl;
00530 int i = 1;
00531
00532 while ( i <= maxCols )
00533 {
00534 const ColumnFormat * column = sheet->columnFormat( i );
00535 ColumnStyle cs;
00536 cs.breakB = ::Style::automatic;
00537 cs.size = column->mmWidth() / 10;
00538 bool hide = column->isHide();
00539
00540 int j = i + 1;
00541 int repeated = 1;
00542 while ( j <= maxCols )
00543 {
00544 const ColumnFormat *c = sheet->columnFormat( j );
00545 ColumnStyle cs1;
00546 cs1.breakB = ::Style::automatic;
00547 cs1.size = c->mmWidth() / 10;
00548
00549 if ( ColumnStyle::isEqual( &cs, cs1 ) && ( hide == c->isHide() ) )
00550 ++repeated;
00551 else
00552 break;
00553 ++j;
00554 }
00555
00556 QDomElement colElem = doc.createElement( "table:table-column" );
00557 colElem.setAttribute( "table:style-name", m_styles.columnStyle( cs ) );
00558 colElem.setAttribute( "table:default-cell-style-name", "Default" );
00559 if ( hide )
00560 colElem.setAttribute( "table:visibility", "collapse" );
00561
00562 if ( repeated > 1 )
00563 colElem.setAttribute( "table:number-columns-repeated", QString::number( repeated ) );
00564
00565 tabElem.appendChild( colElem );
00566 i += repeated;
00567 }
00568
00569 for ( i = 1; i <= maxRows; ++i )
00570 {
00571 const RowFormat * row = sheet->rowFormat( i );
00572 RowStyle rs;
00573 rs.breakB = ::Style::automatic;
00574 rs.size = row->mmHeight() / 10;
00575
00576 QDomElement rowElem = doc.createElement( "table:table-row" );
00577 rowElem.setAttribute( "table:style-name", m_styles.rowStyle( rs ) );
00578 if ( row->isHide() )
00579 rowElem.setAttribute( "table:visibility", "collapse" );
00580
00581 exportCells( doc, rowElem, sheet, i, maxCols );
00582
00583 tabElem.appendChild( rowElem );
00584 }
00585 }
00586
00587 void OpenCalcExport::exportCells( QDomDocument & doc, QDomElement & rowElem,
00588 const Sheet *sheet, int row, int maxCols )
00589 {
00590 int i = 1;
00591 while ( i <= maxCols )
00592 {
00593 int repeated = 1;
00594 bool hasComment = false;
00595 const Cell* cell = sheet->cellAt( i, row );
00596 QDomElement cellElem;
00597
00598 if ( !cell->isPartOfMerged() )
00599 cellElem = doc.createElement( "table:table-cell" );
00600 else
00601 cellElem = doc.createElement( "table:covered-table-cell" );
00602
00603 QFont font;
00604 Value const value( cell->value() );
00605 if ( !cell->isDefault() )
00606 {
00607 font = cell->format()->textFont( i, row );
00608 m_styles.addFont( font );
00609
00610 if ( cell->format()->hasProperty( Format::PComment ) )
00611 hasComment = true;
00612 }
00613
00614 CellStyle c;
00615 CellStyle::loadData( c, cell );
00616
00617 cellElem.setAttribute( "table:style-name", m_styles.cellStyle( c ) );
00618
00619
00620 if ( cell->isEmpty() && !hasComment && !cell->isPartOfMerged() && !cell->doesMergeCells() )
00621 {
00622 int j = i + 1;
00623 while ( j <= maxCols )
00624 {
00625 const Cell *cell1 = sheet->cellAt( j, row );
00626
00627 CellStyle c1;
00628 CellStyle::loadData( c1, cell1 );
00629
00630 if ( cell1->isEmpty() && !cell->format()->hasProperty( Format::PComment )
00631 && CellStyle::isEqual( &c, c1 ) && !cell->isPartOfMerged() && !cell->doesMergeCells() )
00632 ++repeated;
00633 else
00634 break;
00635 ++j;
00636 }
00637 if ( repeated > 1 )
00638 cellElem.setAttribute( "table:number-columns-repeated", QString::number( repeated ) );
00639 }
00640
00641 if ( value.isBoolean() )
00642 {
00643 kdDebug(30518) << "Type: Boolean" << endl;
00644 cellElem.setAttribute( "table:value-type", "boolean" );
00645 cellElem.setAttribute( "table:boolean-value", ( value.asBoolean() ? "true" : "false" ) );
00646 }
00647 else if ( value.isNumber() )
00648 {
00649 kdDebug(30518) << "Type: Number" << endl;
00650 FormatType type = cell->format()->getFormatType( i, row );
00651
00652 if ( type == Percentage_format )
00653 cellElem.setAttribute( "table:value-type", "percentage" );
00654 else
00655 cellElem.setAttribute( "table:value-type", "float" );
00656
00657 cellElem.setAttribute( "table:value", QString::number( value.asFloat() ) );
00658 }
00659 else
00660 {
00661 kdDebug(30518) << "Type: " << value.type() << endl;
00662 }
00663
00664 if ( cell->isFormula() )
00665 {
00666 kdDebug(30518) << "Formula found" << endl;
00667
00668 QString formula( convertFormula( cell->text() ) );
00669 cellElem.setAttribute( "table:formula", formula );
00670 }
00671 else if ( !cell->link().isEmpty() )
00672 {
00673 QDomElement link = doc.createElement( "text:p" );
00674 QDomElement linkref = doc.createElement( "text:a" );
00675
00676 QString tmp = cell->link();
00677 if ( localReferenceAnchor( tmp ) )
00678 linkref.setAttribute( "xlink:href", ( "#"+tmp ) );
00679 else
00680 linkref.setAttribute( "xlink:href", tmp );
00681
00682 linkref.appendChild( doc.createTextNode( cell->text() ) );
00683
00684 link.appendChild( linkref );
00685 cellElem.appendChild( link );
00686 }
00687 else if ( !cell->isEmpty() )
00688 {
00689 QDomElement textElem = doc.createElement( "text:p" );
00690 textElem.appendChild( doc.createTextNode( cell->strOutText() ) );
00691
00692 cellElem.appendChild( textElem );
00693 kdDebug(30518) << "Cell StrOut: " << cell->strOutText() << endl;
00694 }
00695
00696 if ( cell->doesMergeCells() )
00697 {
00698 int colSpan = cell->mergedXCells() + 1;
00699 int rowSpan = cell->mergedYCells() + 1;
00700
00701 if ( colSpan > 1 )
00702 cellElem.setAttribute( "table:number-columns-spanned", QString::number( colSpan ) );
00703
00704 if ( rowSpan > 1 )
00705 cellElem.setAttribute( "table:number-rows-spanned", QString::number( rowSpan ) );
00706 }
00707
00708 if ( hasComment )
00709 {
00710 QString comment( cell->format()->comment( i, row ) );
00711 QDomElement annotation = doc.createElement( "office:annotation" );
00712 QDomElement text = doc.createElement( "text:p" );
00713 text.appendChild( doc.createTextNode( comment ) );
00714
00715 annotation.appendChild( text );
00716 cellElem.appendChild( annotation );
00717 }
00718
00719 rowElem.appendChild( cellElem );
00720
00721 i += repeated;
00722 }
00723 }
00724
00725 void OpenCalcExport::maxRowCols( const Sheet *sheet,
00726 int & maxCols, int & maxRows )
00727 {
00728 Cell const * cell = sheet->firstCell();
00729
00730 while ( cell )
00731 {
00732 if ( cell->column() > maxCols )
00733 maxCols = cell->column();
00734
00735 if ( cell->row() > maxRows )
00736 maxRows = cell->row();
00737
00738 cell = cell->nextCell();
00739 }
00740
00741 RowFormat const * row = sheet->firstRow();
00742
00743 while ( row )
00744 {
00745 if ( row->row() > maxRows )
00746 maxRows = row->row();
00747
00748 row = row->next();
00749 }
00750
00751 ColumnFormat const * col = sheet->firstCol();
00752 while ( col )
00753 {
00754 if ( col->column() > maxCols )
00755 maxCols = col->column();
00756
00757 col = col->next();
00758 }
00759
00760 }
00761
00762 bool OpenCalcExport::exportStyles( KoStore * store, const Doc *ksdoc )
00763 {
00764 if ( !store->open( "styles.xml" ) )
00765 return false;
00766
00767 QDomDocument doc;
00768 doc.appendChild( doc.createProcessingInstruction( "xml","version=\"1.0\" encoding=\"UTF-8\"" ) );
00769
00770 QDomElement content = doc.createElement( "office:document-styles" );
00771 content.setAttribute( "xmlns:office", "http://openoffice.org/2000/office" );
00772 content.setAttribute( "xmlns:style", "http://openoffice.org/2000/style" );
00773 content.setAttribute( "xmlns:text", "http://openoffice.org/2000/text" );
00774 content.setAttribute( "xmlns:table", "http://openoffice.org/2000/table" );
00775 content.setAttribute( "xmlns:draw", "http://openoffice.org/2000/drawing" );
00776 content.setAttribute( "xmlns:fo", "http://www.w3.org/1999/XSL/Format" );
00777 content.setAttribute( "xmlns:xlink", "http://www.w3.org/1999/xlink" );
00778 content.setAttribute( "xmlns:number", "http://openoffice.org/2000/datastyle" );
00779 content.setAttribute( "xmlns:svg", "http://www.w3.org/2000/svg" );
00780 content.setAttribute( "xmlns:chart", "http://openoffice.org/2000/chart" );
00781 content.setAttribute( "xmlns:dr3d", "http://openoffice.org/2000/dr3d" );
00782 content.setAttribute( "xmlns:math", "http://www.w3.org/1998/Math/MathML" );
00783 content.setAttribute( "xmlns:form", "http://openoffice.org/2000/form" );
00784 content.setAttribute( "xmlns:script", "http://openoffice.org/2000/script" );
00785 content.setAttribute( "office:version", "1.0" );
00786
00787
00788 QDomElement officeStyles = doc.createElement( "office:styles" );
00789 exportDefaultCellStyle( doc, officeStyles );
00790
00791 QDomElement fontDecls = doc.createElement( "office:font-decls" );
00792 m_styles.writeFontDecl( doc, fontDecls );
00793
00794
00795
00796
00797 QDomElement defaultStyle = doc.createElement( "style:style" );
00798 defaultStyle.setAttribute( "style:name", "Default" );
00799 defaultStyle.setAttribute( "style:family", "table-cell" );
00800 officeStyles.appendChild( defaultStyle );
00801
00802 QDomElement autoStyles = doc.createElement( "office:automatic-styles" );
00803 exportPageAutoStyles( doc, autoStyles, ksdoc );
00804
00805 QDomElement masterStyles = doc.createElement( "office:master-styles" );
00806 exportMasterStyles( doc, masterStyles, ksdoc );
00807
00808 content.appendChild( fontDecls );
00809 content.appendChild( officeStyles );
00810 content.appendChild( autoStyles );
00811 content.appendChild( masterStyles );
00812
00813 doc.appendChild( content );
00814
00815 QCString f( doc.toCString() );
00816 kdDebug(30518) << "Content: " << (char const * ) f << endl;
00817
00818 store->write( f, f.length() );
00819
00820 if ( !store->close() )
00821 return false;
00822
00823 return true;
00824 }
00825
00826 void OpenCalcExport::exportDefaultCellStyle( QDomDocument & doc, QDomElement & officeStyles )
00827 {
00828 QDomElement defStyle = doc.createElement( "style:default-style" );
00829 defStyle.setAttribute( "style:family", "table-cell" );
00830
00831 KoDocument * document = m_chain->inputDocument();
00832 Doc * ksdoc = static_cast<Doc *>(document);
00833
00834 Format * format = new Format( 0, ksdoc->styleManager()->defaultStyle() );
00835 const KLocale *locale = ksdoc->locale();
00836 QString language;
00837 QString country;
00838 QString charSet;
00839
00840 QString l( locale->language() );
00841 KLocale::splitLocale( l, language, country, charSet );
00842 QFont font( format->font() );
00843 m_styles.addFont( font, true );
00844
00845 QDomElement style = doc.createElement( "style:properties" );
00846 style.setAttribute( "style:font-name", font.family() );
00847 style.setAttribute( "fo:font-size", QString( "%1pt" ).arg( font.pointSize() ) );
00848 style.setAttribute( "style:decimal-places", QString::number( locale->fracDigits() ) );
00849 style.setAttribute( "fo:language", language );
00850 style.setAttribute( "fo:country", country );
00851 style.setAttribute( "style:font-name-asian", "HG Mincho Light J" );
00852 style.setAttribute( "style:language-asian", "none" );
00853 style.setAttribute( "style:country-asian", "none" );
00854 style.setAttribute( "style:font-name-complex", "Arial Unicode MS" );
00855 style.setAttribute( "style:language-complex", "none" );
00856 style.setAttribute( "style:country-complex", "none" );
00857 style.setAttribute( "style:tab-stop-distance", "1.25cm" );
00858
00859 defStyle.appendChild( style );
00860 officeStyles.appendChild( defStyle );
00861 delete format;
00862 }
00863
00864 void OpenCalcExport::createDefaultStyles()
00865 {
00866
00867 }
00868
00869 void OpenCalcExport::exportPageAutoStyles( QDomDocument & doc, QDomElement & autoStyles,
00870 const Doc *ksdoc )
00871 {
00872 QPtrListIterator<Sheet> it( ksdoc->map()->sheetList() );
00873 const Sheet * sheet = it.toFirst();
00874
00875 float width = 20.999;
00876 float height = 29.699;
00877
00878 if ( sheet )
00879 {
00880 width = sheet->print()->paperWidth() / 10;
00881 height = sheet->print()->paperHeight() / 10;
00882 }
00883
00884 QString sWidth = QString( "%1cm" ).arg( width );
00885 QString sHeight = QString( "%1cm" ).arg( height );
00886
00887 QDomElement pageMaster = doc.createElement( "style:page-master" );
00888 pageMaster.setAttribute( "style:name", "pm1" );
00889
00890 QDomElement properties = doc.createElement( "style:properties" );
00891 properties.setAttribute( "fo:page-width", sWidth );
00892 properties.setAttribute( "fo:page-height", sHeight );
00893 properties.setAttribute( "fo:border", "0.002cm solid #000000" );
00894 properties.setAttribute( "fo:padding", "0cm" );
00895 properties.setAttribute( "fo:background-color", "transparent" );
00896
00897 pageMaster.appendChild( properties );
00898
00899 QDomElement header = doc.createElement( "style:header-style" );
00900 properties = doc.createElement( "style:properties" );
00901 properties.setAttribute( "fo:min-height", "0.75cm" );
00902 properties.setAttribute( "fo:margin-left", "0cm" );
00903 properties.setAttribute( "fo:margin-right", "0cm" );
00904 properties.setAttribute( "fo:margin-bottom", "0.25cm" );
00905
00906 header.appendChild( properties );
00907
00908 QDomElement footer = doc.createElement( "style:header-style" );
00909 properties = doc.createElement( "style:properties" );
00910 properties.setAttribute( "fo:min-height", "0.75cm" );
00911 properties.setAttribute( "fo:margin-left", "0cm" );
00912 properties.setAttribute( "fo:margin-right", "0cm" );
00913 properties.setAttribute( "fo:margin-bottom", "0.25cm" );
00914
00915 footer.appendChild( properties );
00916
00917 pageMaster.appendChild( header );
00918 pageMaster.appendChild( footer );
00919
00920 autoStyles.appendChild( pageMaster );
00921 }
00922
00923 void OpenCalcExport::exportMasterStyles( QDomDocument & doc, QDomElement & masterStyles,
00924 const Doc * ksdoc )
00925 {
00926 QDomElement masterPage = doc.createElement( "style:master-page" );
00927 masterPage.setAttribute( "style:name", "Default" );
00928 masterPage.setAttribute( "style:page-master-name", "pm1" );
00929
00930 QPtrListIterator<Sheet> it( ksdoc->map()->sheetList() );
00931 const Sheet * sheet = it.toFirst();
00932
00933 QString headerLeft;
00934 QString headerCenter;
00935 QString headerRight;
00936 QString footerLeft;
00937 QString footerCenter;
00938 QString footerRight;
00939
00940 if ( sheet )
00941 {
00942 headerLeft = sheet->print()->headLeft();
00943 headerCenter = sheet->print()->headMid();
00944 headerRight = sheet->print()->headRight();
00945 footerLeft = sheet->print()->footLeft();
00946 footerCenter = sheet->print()->footMid();
00947 footerRight = sheet->print()->footRight();
00948 }
00949
00950 if ( ( headerLeft.length() > 0 ) || ( headerCenter.length() > 0 )
00951 || ( headerRight.length() > 0 ) )
00952 {
00953 QDomElement header = doc.createElement( "style:header" );
00954 QDomElement left = doc.createElement( "style:region-left" );
00955 QDomElement text = doc.createElement( "text:p" );
00956 convertPart( headerLeft, doc, text, ksdoc );
00957 left.appendChild( text );
00958
00959 QDomElement center = doc.createElement( "style:region-center" );
00960 QDomElement text1 = doc.createElement( "text:p" );
00961 convertPart( headerCenter, doc, text1, ksdoc );
00962 center.appendChild( text1 );
00963
00964 QDomElement right = doc.createElement( "style:region-right" );
00965 QDomElement text2 = doc.createElement( "text:p" );
00966 convertPart( headerRight, doc, text2, ksdoc );
00967 right.appendChild( text2 );
00968
00969 header.appendChild( left );
00970 header.appendChild( center );
00971 header.appendChild( right );
00972
00973 masterPage.appendChild( header );
00974 }
00975 else
00976 {
00977 QDomElement header = doc.createElement( "style:header" );
00978 QDomElement text = doc.createElement( "text:p" );
00979 QDomElement name = doc.createElement( "text:sheet-name" );
00980 name.appendChild( doc.createTextNode( "???" ) );
00981 text.appendChild( name );
00982 header.appendChild( text );
00983
00984 masterPage.appendChild( header );
00985 }
00986
00987 if ( ( footerLeft.length() > 0 ) || ( footerCenter.length() > 0 )
00988 || ( footerRight.length() > 0 ) )
00989 {
00990 QDomElement footer = doc.createElement( "style:footer" );
00991 QDomElement left = doc.createElement( "style:region-left" );
00992 QDomElement text = doc.createElement( "text:p" );
00993 convertPart( footerLeft, doc, text, ksdoc );
00994 left.appendChild( text );
00995
00996 QDomElement center = doc.createElement( "style:region-center" );
00997 QDomElement text1 = doc.createElement( "text:p" );
00998 convertPart( footerCenter, doc, text1, ksdoc );
00999 center.appendChild( text1 );
01000
01001 QDomElement right = doc.createElement( "style:region-right" );
01002 QDomElement text2 = doc.createElement( "text:p" );
01003 convertPart( footerRight, doc, text2, ksdoc );
01004 right.appendChild( text2 );
01005
01006 footer.appendChild( left );
01007 footer.appendChild( center );
01008 footer.appendChild( right );
01009
01010 masterPage.appendChild( footer );
01011 }
01012 else
01013 {
01014 QDomElement footer = doc.createElement( "style:footer" );
01015 QDomElement text = doc.createElement( "text:p" );
01016 text.appendChild( doc.createTextNode( i18n( "Page " ) ) );
01017 QDomElement number = doc.createElement( "text:page-number" );
01018 number.appendChild( doc.createTextNode( "1" ) );
01019 text.appendChild( number );
01020 footer.appendChild( text );
01021
01022 masterPage.appendChild( footer );
01023 }
01024
01025 masterStyles.appendChild( masterPage );
01026 }
01027
01028 void OpenCalcExport::addText( QString const & text, QDomDocument & doc,
01029 QDomElement & parent )
01030 {
01031 if (text.length() > 0 )
01032 parent.appendChild( doc.createTextNode( text ) );
01033 }
01034
01035 void OpenCalcExport::convertPart( QString const & part, QDomDocument & doc,
01036 QDomElement & parent, const Doc * ksdoc )
01037 {
01038 QString text;
01039 QString var;
01040
01041 bool inVar = false;
01042 uint i = 0;
01043 uint l = part.length();
01044 while ( i < l )
01045 {
01046 if ( inVar || part[i] == '<' )
01047 {
01048 inVar = true;
01049 var += part[i];
01050 if ( part[i] == '>' )
01051 {
01052 inVar = false;
01053 if ( var == "<page>" )
01054 {
01055 addText( text, doc, parent );
01056
01057 QDomElement page = doc.createElement( "text:page-number" );
01058 page.appendChild( doc.createTextNode( "1" ) );
01059 parent.appendChild( page );
01060 }
01061 else if ( var == "<pages>" )
01062 {
01063 addText( text, doc, parent );
01064
01065 QDomElement page = doc.createElement( "text:page-count" );
01066 page.appendChild( doc.createTextNode( "99" ) );
01067 parent.appendChild( page );
01068 }
01069 else if ( var == "<date>" )
01070 {
01071 addText( text, doc, parent );
01072
01073 QDomElement t = doc.createElement( "text:date" );
01074 t.setAttribute( "text:date-value", "0-00-00" );
01075
01076 t.appendChild( doc.createTextNode( QDate::currentDate().toString() ) );
01077 parent.appendChild( t );
01078 }
01079 else if ( var == "<time>" )
01080 {
01081 addText( text, doc, parent );
01082
01083 QDomElement t = doc.createElement( "text:time" );
01084 t.appendChild( doc.createTextNode( QTime::currentTime().toString() ) );
01085 parent.appendChild( t );
01086 }
01087 else if ( var == "<file>" )
01088 {
01089 addText( text, doc, parent );
01090
01091 QDomElement t = doc.createElement( "text:file-name" );
01092 t.setAttribute( "text:display", "full" );
01093 t.appendChild( doc.createTextNode( "???" ) );
01094 parent.appendChild( t );
01095 }
01096 else if ( var == "<name>" )
01097 {
01098 addText( text, doc, parent );
01099
01100 QDomElement t = doc.createElement( "text:title" );
01101 t.appendChild( doc.createTextNode( "???" ) );
01102 parent.appendChild( t );
01103 }
01104 else if ( var == "<author>" )
01105 {
01106 KoDocumentInfo * docInfo = ksdoc->documentInfo();
01107 KoDocumentInfoAuthor * authorPage = static_cast<KoDocumentInfoAuthor*>( docInfo->page( "author" ) );
01108
01109 text += authorPage->fullName();
01110
01111 addText( text, doc, parent );
01112 }
01113 else if ( var == "<email>" )
01114 {
01115 KoDocumentInfo * docInfo = ksdoc->documentInfo();
01116 KoDocumentInfoAuthor * authorPage = static_cast<KoDocumentInfoAuthor*>( docInfo->page( "author" ) );
01117
01118 text += authorPage->email();
01119
01120 addText( text, doc, parent );
01121 }
01122 else if ( var == "<org>" )
01123 {
01124 KoDocumentInfo * docInfo = ksdoc->documentInfo();
01125 KoDocumentInfoAuthor * authorPage = static_cast<KoDocumentInfoAuthor*>( docInfo->page( "author" ) );
01126
01127 text += authorPage->company();
01128
01129 addText( text, doc, parent );
01130 }
01131 else if ( var == "<sheet>" )
01132 {
01133 addText( text, doc, parent );
01134
01135 QDomElement s = doc.createElement( "text:sheet-name" );
01136 s.appendChild( doc.createTextNode( "???" ) );
01137 parent.appendChild( s );
01138 }
01139 else
01140 {
01141
01142 text += var;
01143 addText( text, doc, parent );
01144 }
01145
01146 text = "";
01147 var = "";
01148 }
01149 }
01150 else
01151 {
01152 text += part[i];
01153 }
01154 ++i;
01155 }
01156 if ( !text.isEmpty() || !var.isEmpty() )
01157 {
01158
01159 addText( text+var, doc, parent );
01160 }
01161 }
01162
01163 QString OpenCalcExport::convertFormula( QString const & formula ) const
01164 {
01165 QChar decimalSymbol( '.' );
01166 if ( m_locale )
01167 {
01168 const QString decimal ( m_locale->decimalSymbol() );
01169 if ( !decimal.isEmpty() )
01170 {
01171 decimalSymbol = decimal.at( 0 );
01172 }
01173 }
01174
01175 QString s;
01176 QRegExp exp("(\\$?)([a-zA-Z]+)(\\$?)([0-9]+)");
01177 int n = exp.search( formula, 0 );
01178 kdDebug(30518) << "Exp: " << formula << ", n: " << n << ", Length: " << formula.length()
01179 << ", Matched length: " << exp.matchedLength() << endl;
01180
01181 bool inQuote1 = false;
01182 bool inQuote2 = false;
01183 int i = 0;
01184 int l = (int) formula.length();
01185 if ( l <= 0 )
01186 return formula;
01187 while ( i < l )
01188 {
01189 if ( ( n != -1 ) && ( n < i ) )
01190 {
01191 n = exp.search( formula, i );
01192 kdDebug(30518) << "Exp: " << formula.right( l - i ) << ", n: " << n << endl;
01193 }
01194 if ( formula[i] == '"' )
01195 {
01196 inQuote1 = !inQuote1;
01197 s += formula[i];
01198 ++i;
01199 continue;
01200 }
01201 if ( formula[i] == '\'' )
01202 {
01203
01204 inQuote2 = !inQuote2;
01205 ++i;
01206 continue;
01207 }
01208 if ( inQuote1 || inQuote2 )
01209 {
01210 s += formula[i];
01211 ++i;
01212 continue;
01213 }
01214 if ( ( formula[i] == '=' ) && ( formula[i + 1] == '=' ) )
01215 {
01216 s += '=';
01217 ++i;++i;
01218 continue;
01219 }
01220 if ( formula[i] == '!' )
01221 {
01222 insertBracket( s );
01223 s += '.';
01224 ++i;
01225 continue;
01226 }
01227 else if ( formula[i] == decimalSymbol )
01228 {
01229 s += '.';
01230 ++i;
01231 continue;
01232 }
01233 if ( n == i )
01234 {
01235 int ml = exp.matchedLength();
01236 if ( formula[ i + ml ] == '!' )
01237 {
01238 kdDebug(30518) << "No cell ref but sheet name" << endl;
01239 s += formula[i];
01240 ++i;
01241 continue;
01242 }
01243 if ( ( i > 0 ) && ( formula[i - 1] != '!' ) )
01244 s += "[.";
01245 for ( int j = 0; j < ml; ++j )
01246 {
01247 s += formula[i];
01248 ++i;
01249 }
01250 s += ']';
01251 continue;
01252 }
01253
01254 s += formula[i];
01255 ++i;
01256 }
01257
01258 return s;
01259 }
01260
01261 bool OpenCalcExport::writeMetaFile( KoStore * store, uint filesWritten )
01262 {
01263 store->enterDirectory( "META-INF" );
01264 if ( !store->open( "manifest.xml" ) )
01265 return false;
01266
01267 QDomImplementation impl;
01268 QDomDocumentType type( impl.createDocumentType( "manifest:manifest", "-//OpenOffice.org//DTD Manifest 1.0//EN", "Manifest.dtd" ) );
01269
01270 QDomDocument meta( type );
01271 meta.appendChild( meta.createProcessingInstruction( "xml","version=\"1.0\" encoding=\"UTF-8\"" ) );
01272
01273 QDomElement content = meta.createElement( "manifest:manifest" );
01274 content.setAttribute( "xmlns:manifest", "http://openoffice.org/2001/manifest" );
01275
01276 QDomElement entry = meta.createElement( "manifest:file-entry" );
01277 entry.setAttribute( "manifest:media-type", "application/vnd.sun.xml.calc" );
01278 entry.setAttribute( "manifest:full-path", "/" );
01279 content.appendChild( entry );
01280
01281 entry = meta.createElement( "manifest:file-entry" );
01282 content.appendChild( entry );
01283
01284 if ( filesWritten & contentXML )
01285 {
01286 entry = meta.createElement( "manifest:file-entry" );
01287 entry.setAttribute( "manifest:media-type", "text/xml" );
01288 entry.setAttribute( "manifest:full-path", "content.xml" );
01289 content.appendChild( entry );
01290 }
01291
01292 if ( filesWritten & stylesXML )
01293 {
01294 entry = meta.createElement( "manifest:file-entry" );
01295 entry.setAttribute( "manifest:media-type", "text/xml" );
01296 entry.setAttribute( "manifest:full-path", "styles.xml" );
01297 content.appendChild( entry );
01298 }
01299
01300 if ( filesWritten & metaXML )
01301 {
01302 entry = meta.createElement( "manifest:file-entry" );
01303 entry.setAttribute( "manifest:media-type", "text/xml" );
01304 entry.setAttribute( "manifest:full-path", "meta.xml" );
01305 content.appendChild( entry );
01306 }
01307
01308 if ( filesWritten & settingsXML )
01309 {
01310 entry = meta.createElement( "manifest:file-entry" );
01311 entry.setAttribute( "manifest:media-type", "text/xml" );
01312 entry.setAttribute( "manifest:full-path", "settings.xml" );
01313 content.appendChild( entry );
01314 }
01315
01316 meta.appendChild( content );
01317
01318 QCString doc( meta.toCString() );
01319 kdDebug(30518) << "Manifest: " << doc << endl;
01320
01321 store->write( doc, doc.length() );
01322
01323 if ( !store->close() )
01324 return false;
01325
01326 return true;
01327 }
01328
01329 #include <opencalcexport.moc>