filters

opencalcexport.cpp
1/* This file is part of the KDE project
2 Copyright (C) 2000 David Faure <faure@kde.org>
3 Copyright (C) 2000 Norbert Andres <nandres@web.de>
4 Copyright (C) 2005 Laurent Montel <montel@kde.org>
5
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Library General Public
8 License as published by the Free Software Foundation; either
9 version 2 of the License, or (at your option) any later version.
10
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Library General Public License for more details.
15
16 You should have received a copy of the GNU Library General Public License
17 along with this library; see the file COPYING.LIB. If not, write to
18 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
20*/
21
22#include <float.h>
23#include <math.h>
24
25#include <opencalcexport.h>
26
27#include <tqdatetime.h>
28#include <tqdom.h>
29#include <tqfile.h>
30#include <tqregexp.h>
31#include <tqvaluelist.h>
32
33#include <kdebug.h>
34#include <tdemessagebox.h>
35#include <kmdcodec.h>
36#include <kgenericfactory.h>
37#include <tdelocale.h>
38
39#include <KoDocumentInfo.h>
40#include <KoFilterChain.h>
41#include <KoGlobal.h>
42
43#include <kspread_aboutdata.h>
44#include <kspread_cell.h>
45#include <kspread_doc.h>
46#include <kspread_format.h>
47#include <kspread_map.h>
48#include <kspread_view.h>
49#include <kspread_canvas.h>
50#include <kspread_sheet.h>
51#include <kspread_sheetprint.h>
52#include <kspread_style.h>
53#include <kspread_style_manager.h>
54#include <kspread_util.h>
55
56using namespace KSpread;
57
58typedef TQValueList<Reference> AreaList;
59
60class OpenCalcExportFactory : KGenericFactory<OpenCalcExport, KoFilter>
61{
62public:
63 OpenCalcExportFactory(void) : KGenericFactory<OpenCalcExport, KoFilter> ("kspreadopencalcexport")
64 {}
65protected:
66 virtual void setupTranslations( void )
67 {
68 TDEGlobal::locale()->insertCatalogue( "kofficefilters" );
69 }
70};
71
72K_EXPORT_COMPONENT_FACTORY( libopencalcexport, OpenCalcExportFactory() )
73
74#define STOPEXPORT \
75 do \
76 { \
77 delete store; \
78 return false; \
79 } while(0)
80
81OpenCalcExport::OpenCalcExport( KoFilter *, const char *, const TQStringList & )
82 : KoFilter(), m_locale( 0 )
83{
84}
85
86KoFilter::ConversionStatus OpenCalcExport::convert( const TQCString & from,
87 const TQCString & to )
88{
89 /* later...
90 KSpreadLeader * leader = new KSpreadLeader( m_chain );
91 OpenCalcWorker * worker = new OpenCalcWorker();
92 leader->setWorker( worker );
93
94 KoFilter::ConversionStatus status = leader->convert();
95
96 delete worker;
97 delete leader;
98
99 return status;
100 */
101
102 KoDocument * document = m_chain->inputDocument();
103
104 if ( !document )
105 return KoFilter::StupidError;
106
107 if ( !::tqt_cast<const KSpread::Doc *>( document ) )
108 {
109 kdWarning(30518) << "document isn't a KSpread::Doc but a "
110 << document->className() << endl;
111 return KoFilter::NotImplemented;
112 }
113
114 if ( ( to != "application/vnd.sun.xml.calc") || (from != "application/x-kspread" ) )
115 {
116 kdWarning(30518) << "Invalid mimetypes " << to << " " << from << endl;
117 return KoFilter::NotImplemented;
118 }
119
120 const Doc * ksdoc = static_cast<const Doc *>(document);
121
122 if ( ksdoc->mimeType() != "application/x-kspread" )
123 {
124 kdWarning(30518) << "Invalid document mimetype " << ksdoc->mimeType() << endl;
125 return KoFilter::NotImplemented;
126 }
127
128 m_locale = static_cast<Doc*>(document)->locale();
129 if ( !writeFile( ksdoc ) )
130 return KoFilter::CreationError;
131
132 emit sigProgress( 100 );
133
134 return KoFilter::OK;
135}
136
137bool OpenCalcExport::writeFile( const Doc * ksdoc )
138{
139 KoStore * store = KoStore::createStore( m_chain->outputFile(), KoStore::Write, "", KoStore::Zip );
140
141 if ( !store )
142 return false;
143
144 uint filesWritten = 0;
145
146 if ( !exportContent( store, ksdoc ) )
147 STOPEXPORT;
148 else
149 filesWritten |= contentXML;
150
151 // TODO: pass sheet number and cell number
152 if ( !exportDocInfo( store, ksdoc ) )
153 STOPEXPORT;
154 else
155 filesWritten |= metaXML;
156
157 if ( !exportStyles( store, ksdoc ) )
158 STOPEXPORT;
159 else
160 filesWritten |= stylesXML;
161
162 if ( !exportSettings( store, ksdoc ) )
163 STOPEXPORT;
164 else
165 filesWritten |= settingsXML;
166
167 if ( !writeMetaFile( store, filesWritten ) )
168 STOPEXPORT;
169
170 // writes zip file to disc
171 delete store;
172 store = 0;
173
174 return true;
175}
176
177bool OpenCalcExport::exportDocInfo( KoStore * store, const Doc* ksdoc )
178{
179 if ( !store->open( "meta.xml" ) )
180 return false;
181
182 KoDocumentInfo * docInfo = ksdoc->documentInfo();
183 KoDocumentInfoAbout * aboutPage = static_cast<KoDocumentInfoAbout *>( docInfo->page( "about" ) );
184 KoDocumentInfoAuthor * authorPage = static_cast<KoDocumentInfoAuthor*>( docInfo->page( "author" ) );
185
186 TQDomDocument meta;
187 meta.appendChild( meta.createProcessingInstruction( "xml","version=\"1.0\" encoding=\"UTF-8\"" ) );
188
189 TQDomElement content = meta.createElement( "office:document-meta" );
190 content.setAttribute( "xmlns:office", "http://openoffice.org/2000/office");
191 content.setAttribute( "xmlns:xlink", "http://www.w3.org/1999/xlink" );
192 content.setAttribute( "xmlns:dc", "http://purl.org/dc/elements/1.1/" );
193 content.setAttribute( "xmlns:meta", "http://openoffice.org/2000/meta" );
194 content.setAttribute( "office:version", "1.0" );
195
196 TQDomNode officeMeta = meta.createElement( "office:meta" );
197
198 TQDomElement data = meta.createElement( "meta:generator" );
199 TQString app( "KSpread " );
200 app += KSpread::version;
201 data.appendChild( meta.createTextNode( app ) );
202 officeMeta.appendChild( data );
203
204 data = meta.createElement( "meta:initial-creator" );
205 data.appendChild( meta.createTextNode( authorPage->fullName() ) );
206 officeMeta.appendChild( data );
207
208 data = meta.createElement( "meta:creator" );
209 data.appendChild( meta.createTextNode( authorPage->fullName() ) );
210 officeMeta.appendChild( data );
211
212 data = meta.createElement( "dc:description" );
213 data.appendChild( meta.createTextNode( aboutPage->abstract() ) );
214 officeMeta.appendChild( data );
215
216 data = meta.createElement( "meta:keywords" );
217 TQDomElement dataItem = meta.createElement( "meta:keyword" );
218 dataItem.appendChild( meta.createTextNode( aboutPage->keywords() ) );
219 data.appendChild( dataItem );
220 officeMeta.appendChild( data );
221
222 data = meta.createElement( "dc:title" );
223 data.appendChild( meta.createTextNode( aboutPage->title() ) );
224 officeMeta.appendChild( data );
225
226 data = meta.createElement( "dc:subject" );
227 data.appendChild( meta.createTextNode( aboutPage->subject() ) );
228 officeMeta.appendChild( data );
229
230 const TQDateTime dt ( TQDateTime::currentDateTime() );
231 if ( dt.isValid() )
232 {
233 data = meta.createElement( "dc:date" );
234 data.appendChild( meta.createTextNode( dt.toString( TQt::ISODate ) ) );
235 officeMeta.appendChild( data );
236 }
237
238 /* TODO:
239 <meta:creation-date>2003-01-08T23:57:31</meta:creation-date>
240 <dc:language>en-US</dc:language>
241 <meta:editing-cycles>2</meta:editing-cycles>
242 <meta:editing-duration>PT38S</meta:editing-duration>
243 <meta:user-defined meta:name="Info 3"/>
244 <meta:user-defined meta:name="Info 4"/>
245 */
246
247 data = meta.createElement( "meta:document-statistic" );
248 data.setAttribute( "meta:table-count", TQString::number( ksdoc->map()->count() ) );
249 // TODO: data.setAttribute( "meta:cell-count", );
250 officeMeta.appendChild( data );
251
252 content.appendChild( officeMeta );
253 meta.appendChild( content );
254
255 TQCString doc( meta.toCString() );
256 kdDebug(30518) << "Meta: " << doc << endl;
257
258 store->write( doc, doc.length() );
259
260 if ( !store->close() )
261 return false;
262
263 return true;
264}
265
266bool OpenCalcExport::exportSettings( KoStore * store, const Doc * ksdoc )
267{
268 if ( !store->open( "settings.xml" ) )
269 return false;
270
271 TQDomDocument doc;
272 doc.appendChild( doc.createProcessingInstruction( "xml","version=\"1.0\" encoding=\"UTF-8\"" ) );
273
274 TQDomElement settings = doc.createElement( "office:document-settings" );
275 settings.setAttribute( "xmlns:office", "http://openoffice.org/2000/office");
276 settings.setAttribute( "xmlns:xlink", "http://www.w3.org/1999/xlink" );
277 settings.setAttribute( "xmlns:config", "http://openoffice.org/2001/config" );
278 settings.setAttribute( "office:version", "1.0" );
279
280 TQDomElement begin = doc.createElement( "office:settings" );
281
282 TQDomElement configItem = doc.createElement("config:config-item-set" );
283 configItem.setAttribute( "config:name", "view-settings" );
284
285 TQDomElement mapIndexed = doc.createElement( "config:config-item-map-indexed" );
286 mapIndexed.setAttribute("config:name", "Views" );
287 configItem.appendChild( mapIndexed );
288
289 TQDomElement mapItem = doc.createElement("config:config-item-map-entry" );
290
291 TQDomElement attribute = doc.createElement("config:config-item" );
292 attribute.setAttribute( "config:name", "ActiveTable" );
293 attribute.setAttribute( "config:type", "string" );
294
295 View * view = static_cast<View*>( ksdoc->views().getFirst());
296 TQString activeTable;
297 if ( view ) // no view if embedded document
298 {
299 Canvas * canvas = view->canvasWidget();
300 activeTable = canvas->activeSheet()->sheetName();
301 // save current sheet selection before to save marker, otherwise current pos is not saved
302 view->saveCurrentSheetSelection();
303 }
304 attribute.appendChild( doc.createTextNode( activeTable ) );
305 mapItem.appendChild( attribute );
306
307 TQDomElement configmaped = doc.createElement( "config:config-item-map-named" );
308 configmaped.setAttribute( "config:name","Tables" );
309
310 TQPtrListIterator<Sheet> it( ksdoc->map()->sheetList() );
311 for( ; it.current(); ++it )
312 {
313 TQPoint marker;
314 if ( view )
315 {
316 marker = view->markerFromSheet( *it );
317 }
318 TQDomElement tmpItemMapNamed = doc.createElement( "config:config-item-map-entry" );
319 tmpItemMapNamed.setAttribute( "config:name", ( *it )->tableName() );
320
321 TQDomElement sheetAttribute = doc.createElement( "config:config-item" );
322 sheetAttribute.setAttribute( "config:name", "CursorPositionX" );
323 sheetAttribute.setAttribute( "config:type", "int" );
324 sheetAttribute.appendChild( doc.createTextNode( TQString::number(marker.x() ) ) );
325 tmpItemMapNamed.appendChild( sheetAttribute );
326
327 sheetAttribute = doc.createElement( "config:config-item" );
328 sheetAttribute.setAttribute( "config:name", "CursorPositionY" );
329 sheetAttribute.setAttribute( "config:type", "int" );
330 sheetAttribute.appendChild( doc.createTextNode( TQString::number(marker.y() ) ) );
331 tmpItemMapNamed.appendChild( sheetAttribute );
332
333 configmaped.appendChild( tmpItemMapNamed );
334 }
335 mapItem.appendChild( configmaped );
336
337
338
339 mapIndexed.appendChild( mapItem );
340
341 begin.appendChild( configItem );
342
343 settings.appendChild( begin );
344
345 doc.appendChild( settings );
346
347 TQCString f( doc.toCString() );
348 kdDebug(30518) << "Settings: " << (char const * ) f << endl;
349
350 store->write( f, f.length() );
351
352 if ( !store->close() )
353 return false;
354
355 return true;
356}
357
358bool OpenCalcExport::exportContent( KoStore * store, const Doc * ksdoc )
359{
360 if ( !store->open( "content.xml" ) )
361 return false;
362
363 createDefaultStyles();
364
365 TQDomDocument doc;
366 doc.appendChild( doc.createProcessingInstruction( "xml","version=\"1.0\" encoding=\"UTF-8\"" ) );
367
368 TQDomElement content = doc.createElement( "office:document-content" );
369 content.setAttribute( "xmlns:office", "http://openoffice.org/2000/office");
370 content.setAttribute( "xmlns:style", "http://openoffice.org/2000/style" );
371 content.setAttribute( "xmlns:text", "http://openoffice.org/2000/text" );
372 content.setAttribute( "xmlns:table", "http://openoffice.org/2000/table" );
373 content.setAttribute( "xmlns:draw", "http://openoffice.org/2000/drawing" );
374 content.setAttribute( "xmlns:fo", "http://www.w3.org/1999/XSL/Format" );
375 content.setAttribute( "xmlns:xlink", "http://www.w3.org/1999/xlink" );
376 content.setAttribute( "xmlns:number", "http://openoffice.org/2000/datastyle" );
377 content.setAttribute( "xmlns:svg", "http://www.w3.org/2000/svg" );
378 content.setAttribute( "xmlns:chart", "http://openoffice.org/2000/chart" );
379 content.setAttribute( "xmlns:dr3d", "http://openoffice.org/2000/dr3d" );
380 content.setAttribute( "xmlns:math", "http://www.w3.org/1998/Math/MathML" );
381 content.setAttribute( "xmlns:form", "http://openoffice.org/2000/form" );
382 content.setAttribute( "xmlns:script", "http://openoffice.org/2000/script" );
383 content.setAttribute( "office:class", "spreadsheet" );
384 content.setAttribute( "office:version", "1.0" );
385
386 TQDomElement data = doc.createElement( "office:script" );
387 content.appendChild( data );
388
389 if ( !exportBody( doc, content, ksdoc ) )
390 return false;
391
392 doc.appendChild( content );
393
394 TQCString f( doc.toCString() );
395 kdDebug(30518) << "Content: " << (char const * ) f << endl;
396
397 store->write( f, f.length() );
398
399 if ( !store->close() )
400 return false;
401
402 return true;
403}
404
405void exportNamedExpr( TQDomDocument & doc, TQDomElement & parent,
406 AreaList const & namedAreas )
407{
408 AreaList::const_iterator it = namedAreas.begin();
409 AreaList::const_iterator end = namedAreas.end();
410
411 while ( it != end )
412 {
413 TQDomElement namedRange = doc.createElement( "table:named-range" );
414
415 Reference ref = *it;
416
417 namedRange.setAttribute( "table:name", ref.ref_name );
418 namedRange.setAttribute( "table:base-cell-address", convertRefToBase( ref.sheet_name, ref.rect ) );
419 namedRange.setAttribute( "table:cell-range-address", convertRefToRange( ref.sheet_name, ref.rect ) );
420
421 parent.appendChild( namedRange );
422
423 ++it;
424 }
425}
426
427bool OpenCalcExport::exportBody( TQDomDocument & doc, TQDomElement & content, const Doc * ksdoc )
428{
429 TQDomElement fontDecls = doc.createElement( "office:font-decls" );
430 TQDomElement autoStyles = doc.createElement( "office:automatic-styles" );
431 TQDomElement body = doc.createElement( "office:body" );
432
433 if ( ksdoc->map()->isProtected() )
434 {
435 body.setAttribute( "table:structure-protected", "true" );
436
437 TQCString passwd;
438 ksdoc->map()->password( passwd );
439 if ( passwd.length() > 0 )
440 {
441 TQCString str( KCodecs::base64Encode( passwd ) );
442 body.setAttribute( "table:protection-key", TQString( str.data() ) );
443 }
444 }
445
446
447
448 TQPtrListIterator<Sheet> it( ksdoc->map()->sheetList() );
449
450 for( it.toFirst(); it.current(); ++it )
451 {
452 SheetStyle ts;
453 int maxCols = 1;
454 int maxRows = 1;
455 Sheet * sheet = it.current();
456
457 ts.visible = !sheet->isHidden();
458
459 TQDomElement tabElem = doc.createElement( "table:table" );
460 tabElem.setAttribute( "table:style-name", m_styles.sheetStyle( ts ) );
461
462 if ( sheet->isProtected() )
463 {
464 tabElem.setAttribute( "table:protected", "true" );
465
466 TQCString passwd;
467 sheet->password( passwd );
468 if ( passwd.length() > 0 )
469 {
470 TQCString str( KCodecs::base64Encode( passwd ) );
471 tabElem.setAttribute( "table:protection-key", TQString( str.data() ) );
472 }
473 }
474
475 TQString name( sheet->tableName() );
476
477 int n = name.find( ' ' );
478 if ( n != -1 )
479 {
480 kdDebug(30518) << "Sheet name converting: " << name << endl;
481 name[n] == '_';
482 kdDebug(30518) << "Sheet name converted: " << name << endl;
483 }
484 name = name.replace( ' ', "_" );
485
486 TQRect _printRange = sheet->print()->printRange();
487 if ( _printRange != ( TQRect( TQPoint( 1, 1 ), TQPoint( KS_colMax, KS_rowMax ) ) ) )
488 {
489 TQString range= convertRangeToRef( name, _printRange );
490 //kdDebug(30518)<<" range : "<<range<<endl;
491 tabElem.setAttribute( "table:print-ranges", range );
492 }
493
494
495 tabElem.setAttribute( "table:name", name );
496
497 maxRowCols( sheet, maxCols, maxRows );
498
499 exportSheet( doc, tabElem, sheet, maxCols, maxRows );
500
501 body.appendChild( tabElem );
502 }
503
504 KoDocument * document = m_chain->inputDocument();
505 Doc * kspreadDoc = static_cast<Doc *>( document );
506
507 AreaList namedAreas = kspreadDoc->listArea();
508 if ( namedAreas.count() > 0 )
509 {
510 TQDomElement namedExpr = doc.createElement( "table:named-expressions" );
511 exportNamedExpr( doc, namedExpr, namedAreas );
512
513 body.appendChild( namedExpr );
514 }
515
516 m_styles.writeStyles( doc, autoStyles );
517 m_styles.writeFontDecl( doc, fontDecls );
518
519 content.appendChild( fontDecls );
520 content.appendChild( autoStyles );
521 content.appendChild( body );
522
523 return true;
524}
525
526void OpenCalcExport::exportSheet( TQDomDocument & doc, TQDomElement & tabElem,
527 const Sheet * sheet, int maxCols, int maxRows )
528{
529 kdDebug(30518) << "exportSheet: " << sheet->tableName() << endl;
530 int i = 1;
531
532 while ( i <= maxCols )
533 {
534 const ColumnFormat * column = sheet->columnFormat( i );
535 ColumnStyle cs;
536 cs.breakB = ::Style::automatic;
537 cs.size = column->mmWidth() / 10;
538 bool hide = column->isHide();
539
540 int j = i + 1;
541 int repeated = 1;
542 while ( j <= maxCols )
543 {
544 const ColumnFormat *c = sheet->columnFormat( j );
545 ColumnStyle cs1;
546 cs1.breakB = ::Style::automatic;
547 cs1.size = c->mmWidth() / 10;
548
549 if ( ColumnStyle::isEqual( &cs, cs1 ) && ( hide == c->isHide() ) )
550 ++repeated;
551 else
552 break;
553 ++j;
554 }
555
556 TQDomElement colElem = doc.createElement( "table:table-column" );
557 colElem.setAttribute( "table:style-name", m_styles.columnStyle( cs ) );
558 colElem.setAttribute( "table:default-cell-style-name", "Default" );//todo fixme create style from cell
559 if ( hide )
560 colElem.setAttribute( "table:visibility", "collapse" );
561
562 if ( repeated > 1 )
563 colElem.setAttribute( "table:number-columns-repeated", TQString::number( repeated ) );
564
565 tabElem.appendChild( colElem );
566 i += repeated;
567 }
568
569 for ( i = 1; i <= maxRows; ++i )
570 {
571 const RowFormat * row = sheet->rowFormat( i );
572 RowStyle rs;
573 rs.breakB = ::Style::automatic;
574 rs.size = row->mmHeight() / 10;
575
576 TQDomElement rowElem = doc.createElement( "table:table-row" );
577 rowElem.setAttribute( "table:style-name", m_styles.rowStyle( rs ) );
578 if ( row->isHide() )
579 rowElem.setAttribute( "table:visibility", "collapse" );
580
581 exportCells( doc, rowElem, sheet, i, maxCols );
582
583 tabElem.appendChild( rowElem );
584 }
585}
586
587void OpenCalcExport::exportCells( TQDomDocument & doc, TQDomElement & rowElem,
588 const Sheet *sheet, int row, int maxCols )
589{
590 int i = 1;
591 while ( i <= maxCols )
592 {
593 int repeated = 1;
594 bool hasComment = false;
595 const Cell* cell = sheet->cellAt( i, row );
596 TQDomElement cellElem;
597
598 if ( !cell->isPartOfMerged() )
599 cellElem = doc.createElement( "table:table-cell" );
600 else
601 cellElem = doc.createElement( "table:covered-table-cell" );
602
603 TQFont font;
604 Value const value( cell->value() );
605 if ( !cell->isDefault() )
606 {
607 font = cell->format()->textFont( i, row );
608 m_styles.addFont( font );
609
610 if ( cell->format()->hasProperty( Format::PComment ) )
611 hasComment = true;
612 }
613
614 CellStyle c;
615 CellStyle::loadData( c, cell ); // TODO: number style
616
617 cellElem.setAttribute( "table:style-name", m_styles.cellStyle( c ) );
618
619 // group empty cells with the same style
620 if ( cell->isEmpty() && !hasComment && !cell->isPartOfMerged() && !cell->doesMergeCells() )
621 {
622 int j = i + 1;
623 while ( j <= maxCols )
624 {
625 const Cell *cell1 = sheet->cellAt( j, row );
626
627 CellStyle c1;
628 CellStyle::loadData( c1, cell1 ); // TODO: number style
629
630 if ( cell1->isEmpty() && !cell->format()->hasProperty( Format::PComment )
631 && CellStyle::isEqual( &c, c1 ) && !cell->isPartOfMerged() && !cell->doesMergeCells() )
632 ++repeated;
633 else
634 break;
635 ++j;
636 }
637 if ( repeated > 1 )
638 cellElem.setAttribute( "table:number-columns-repeated", TQString::number( repeated ) );
639 }
640
641 if ( value.isBoolean() )
642 {
643 kdDebug(30518) << "Type: Boolean" << endl;
644 cellElem.setAttribute( "table:value-type", "boolean" );
645 cellElem.setAttribute( "table:boolean-value", ( value.asBoolean() ? "true" : "false" ) );
646 }
647 else if ( value.isNumber() )
648 {
649 kdDebug(30518) << "Type: Number" << endl;
650 FormatType type = cell->format()->getFormatType( i, row );
651
652 if ( type == Percentage_format )
653 cellElem.setAttribute( "table:value-type", "percentage" );
654 else
655 cellElem.setAttribute( "table:value-type", "float" );
656
657 cellElem.setAttribute( "table:value", TQString::number( value.asFloat() ) );
658 }
659 else
660 {
661 kdDebug(30518) << "Type: " << value.type() << endl;
662 }
663
664 if ( cell->isFormula() )
665 {
666 kdDebug(30518) << "Formula found" << endl;
667
668 TQString formula( convertFormula( cell->text() ) );
669 cellElem.setAttribute( "table:formula", formula );
670 }
671 else if ( !cell->link().isEmpty() )
672 {
673 TQDomElement link = doc.createElement( "text:p" );
674 TQDomElement linkref = doc.createElement( "text:a" );
675
676 TQString tmp = cell->link();
677 if ( localReferenceAnchor( tmp ) )
678 linkref.setAttribute( "xlink:href", ( "#"+tmp ) );
679 else
680 linkref.setAttribute( "xlink:href", tmp );
681
682 linkref.appendChild( doc.createTextNode( cell->text() ) );
683
684 link.appendChild( linkref );
685 cellElem.appendChild( link );
686 }
687 else if ( !cell->isEmpty() )
688 {
689 TQDomElement textElem = doc.createElement( "text:p" );
690 textElem.appendChild( doc.createTextNode( cell->strOutText() ) );
691
692 cellElem.appendChild( textElem );
693 kdDebug(30518) << "Cell StrOut: " << cell->strOutText() << endl;
694 }
695
696 if ( cell->doesMergeCells() )
697 {
698 int colSpan = cell->mergedXCells() + 1;
699 int rowSpan = cell->mergedYCells() + 1;
700
701 if ( colSpan > 1 )
702 cellElem.setAttribute( "table:number-columns-spanned", TQString::number( colSpan ) );
703
704 if ( rowSpan > 1 )
705 cellElem.setAttribute( "table:number-rows-spanned", TQString::number( rowSpan ) );
706 }
707
708 if ( hasComment )
709 {
710 TQString comment( cell->format()->comment( i, row ) );
711 TQDomElement annotation = doc.createElement( "office:annotation" );
712 TQDomElement text = doc.createElement( "text:p" );
713 text.appendChild( doc.createTextNode( comment ) );
714
715 annotation.appendChild( text );
716 cellElem.appendChild( annotation );
717 }
718
719 rowElem.appendChild( cellElem );
720
721 i += repeated;
722 }
723}
724
725void OpenCalcExport::maxRowCols( const Sheet *sheet,
726 int & maxCols, int & maxRows )
727{
728 Cell const * cell = sheet->firstCell();
729
730 while ( cell )
731 {
732 if ( cell->column() > maxCols )
733 maxCols = cell->column();
734
735 if ( cell->row() > maxRows )
736 maxRows = cell->row();
737
738 cell = cell->nextCell();
739 }
740
741 RowFormat const * row = sheet->firstRow();
742
743 while ( row )
744 {
745 if ( row->row() > maxRows )
746 maxRows = row->row();
747
748 row = row->next();
749 }
750
751 ColumnFormat const * col = sheet->firstCol();
752 while ( col )
753 {
754 if ( col->column() > maxCols )
755 maxCols = col->column();
756
757 col = col->next();
758 }
759
760}
761
762bool OpenCalcExport::exportStyles( KoStore * store, const Doc *ksdoc )
763{
764 if ( !store->open( "styles.xml" ) )
765 return false;
766
767 TQDomDocument doc;
768 doc.appendChild( doc.createProcessingInstruction( "xml","version=\"1.0\" encoding=\"UTF-8\"" ) );
769
770 TQDomElement content = doc.createElement( "office:document-styles" );
771 content.setAttribute( "xmlns:office", "http://openoffice.org/2000/office" );
772 content.setAttribute( "xmlns:style", "http://openoffice.org/2000/style" );
773 content.setAttribute( "xmlns:text", "http://openoffice.org/2000/text" );
774 content.setAttribute( "xmlns:table", "http://openoffice.org/2000/table" );
775 content.setAttribute( "xmlns:draw", "http://openoffice.org/2000/drawing" );
776 content.setAttribute( "xmlns:fo", "http://www.w3.org/1999/XSL/Format" );
777 content.setAttribute( "xmlns:xlink", "http://www.w3.org/1999/xlink" );
778 content.setAttribute( "xmlns:number", "http://openoffice.org/2000/datastyle" );
779 content.setAttribute( "xmlns:svg", "http://www.w3.org/2000/svg" );
780 content.setAttribute( "xmlns:chart", "http://openoffice.org/2000/chart" );
781 content.setAttribute( "xmlns:dr3d", "http://openoffice.org/2000/dr3d" );
782 content.setAttribute( "xmlns:math", "http://www.w3.org/1998/Math/MathML" );
783 content.setAttribute( "xmlns:form", "http://openoffice.org/2000/form" );
784 content.setAttribute( "xmlns:script", "http://openoffice.org/2000/script" );
785 content.setAttribute( "office:version", "1.0" );
786
787 // order important here!
788 TQDomElement officeStyles = doc.createElement( "office:styles" );
789 exportDefaultCellStyle( doc, officeStyles );
790
791 TQDomElement fontDecls = doc.createElement( "office:font-decls" );
792 m_styles.writeFontDecl( doc, fontDecls );
793
794 // TODO: needs in new number/date/time parser...
795 // exportDefaultNumberStyles( doc, officeStyles );
796
797 TQDomElement defaultStyle = doc.createElement( "style:style" );
798 defaultStyle.setAttribute( "style:name", "Default" );
799 defaultStyle.setAttribute( "style:family", "table-cell" );
800 officeStyles.appendChild( defaultStyle );
801
802 TQDomElement autoStyles = doc.createElement( "office:automatic-styles" );
803 exportPageAutoStyles( doc, autoStyles, ksdoc );
804
805 TQDomElement masterStyles = doc.createElement( "office:master-styles" );
806 exportMasterStyles( doc, masterStyles, ksdoc );
807
808 content.appendChild( fontDecls );
809 content.appendChild( officeStyles );
810 content.appendChild( autoStyles );
811 content.appendChild( masterStyles );
812
813 doc.appendChild( content );
814
815 TQCString f( doc.toCString() );
816 kdDebug(30518) << "Content: " << (char const * ) f << endl;
817
818 store->write( f, f.length() );
819
820 if ( !store->close() )
821 return false;
822
823 return true;
824}
825
826void OpenCalcExport::exportDefaultCellStyle( TQDomDocument & doc, TQDomElement & officeStyles )
827{
828 TQDomElement defStyle = doc.createElement( "style:default-style" );
829 defStyle.setAttribute( "style:family", "table-cell" );
830
831 KoDocument * document = m_chain->inputDocument();
832 Doc * ksdoc = static_cast<Doc *>(document);
833
834 Format * format = new Format( 0, ksdoc->styleManager()->defaultStyle() );
835 const TDELocale *locale = ksdoc->locale();
836 TQString language;
837 TQString country;
838 TQString charSet;
839
840 TQString l( locale->language() );
841 TDELocale::splitLocale( l, language, country, charSet );
842 TQFont font( format->font() );
843 m_styles.addFont( font, true );
844
845 TQDomElement style = doc.createElement( "style:properties" );
846 style.setAttribute( "style:font-name", font.family() );
847 style.setAttribute( "fo:font-size", TQString( "%1pt" ).arg( font.pointSize() ) );
848 style.setAttribute( "style:decimal-places", TQString::number( locale->fracDigits() ) );
849 style.setAttribute( "fo:language", language );
850 style.setAttribute( "fo:country", country );
851 style.setAttribute( "style:font-name-asian", "HG Mincho Light J" );
852 style.setAttribute( "style:language-asian", "none" );
853 style.setAttribute( "style:country-asian", "none" );
854 style.setAttribute( "style:font-name-complex", "Arial Unicode MS" );
855 style.setAttribute( "style:language-complex", "none" );
856 style.setAttribute( "style:country-complex", "none" );
857 style.setAttribute( "style:tab-stop-distance", "1.25cm" );
858
859 defStyle.appendChild( style );
860 officeStyles.appendChild( defStyle );
861 delete format;
862}
863
864void OpenCalcExport::createDefaultStyles()
865{
866 // TODO: default number styles, currency styles,...
867}
868
869void OpenCalcExport::exportPageAutoStyles( TQDomDocument & doc, TQDomElement & autoStyles,
870 const Doc *ksdoc )
871{
872 TQPtrListIterator<Sheet> it( ksdoc->map()->sheetList() );
873 const Sheet * sheet = it.toFirst();
874
875 float width = 20.999;
876 float height = 29.699;
877
878 if ( sheet )
879 {
880 width = sheet->print()->paperWidth() / 10;
881 height = sheet->print()->paperHeight() / 10;
882 }
883
884 TQString sWidth = TQString( "%1cm" ).arg( width );
885 TQString sHeight = TQString( "%1cm" ).arg( height );
886
887 TQDomElement pageMaster = doc.createElement( "style:page-master" );
888 pageMaster.setAttribute( "style:name", "pm1" );
889
890 TQDomElement properties = doc.createElement( "style:properties" );
891 properties.setAttribute( "fo:page-width", sWidth );
892 properties.setAttribute( "fo:page-height", sHeight );
893 properties.setAttribute( "fo:border", "0.002cm solid #000000" );
894 properties.setAttribute( "fo:padding", "0cm" );
895 properties.setAttribute( "fo:background-color", "transparent" );
896
897 pageMaster.appendChild( properties );
898
899 TQDomElement header = doc.createElement( "style:header-style" );
900 properties = doc.createElement( "style:properties" );
901 properties.setAttribute( "fo:min-height", "0.75cm" );
902 properties.setAttribute( "fo:margin-left", "0cm" );
903 properties.setAttribute( "fo:margin-right", "0cm" );
904 properties.setAttribute( "fo:margin-bottom", "0.25cm" );
905
906 header.appendChild( properties );
907
908 TQDomElement footer = doc.createElement( "style:header-style" );
909 properties = doc.createElement( "style:properties" );
910 properties.setAttribute( "fo:min-height", "0.75cm" );
911 properties.setAttribute( "fo:margin-left", "0cm" );
912 properties.setAttribute( "fo:margin-right", "0cm" );
913 properties.setAttribute( "fo:margin-bottom", "0.25cm" );
914
915 footer.appendChild( properties );
916
917 pageMaster.appendChild( header );
918 pageMaster.appendChild( footer );
919
920 autoStyles.appendChild( pageMaster );
921}
922
923void OpenCalcExport::exportMasterStyles( TQDomDocument & doc, TQDomElement & masterStyles,
924 const Doc * ksdoc )
925{
926 TQDomElement masterPage = doc.createElement( "style:master-page" );
927 masterPage.setAttribute( "style:name", "Default" );
928 masterPage.setAttribute( "style:page-master-name", "pm1" );
929
930 TQPtrListIterator<Sheet> it( ksdoc->map()->sheetList() );
931 const Sheet * sheet = it.toFirst();
932
933 TQString headerLeft;
934 TQString headerCenter;
935 TQString headerRight;
936 TQString footerLeft;
937 TQString footerCenter;
938 TQString footerRight;
939
940 if ( sheet )
941 {
942 headerLeft = sheet->print()->headLeft();
943 headerCenter = sheet->print()->headMid();
944 headerRight = sheet->print()->headRight();
945 footerLeft = sheet->print()->footLeft();
946 footerCenter = sheet->print()->footMid();
947 footerRight = sheet->print()->footRight();
948 }
949
950 if ( ( headerLeft.length() > 0 ) || ( headerCenter.length() > 0 )
951 || ( headerRight.length() > 0 ) )
952 {
953 TQDomElement header = doc.createElement( "style:header" );
954 TQDomElement left = doc.createElement( "style:region-left" );
955 TQDomElement text = doc.createElement( "text:p" );
956 convertPart( headerLeft, doc, text, ksdoc );
957 left.appendChild( text );
958
959 TQDomElement center = doc.createElement( "style:region-center" );
960 TQDomElement text1 = doc.createElement( "text:p" );
961 convertPart( headerCenter, doc, text1, ksdoc );
962 center.appendChild( text1 );
963
964 TQDomElement right = doc.createElement( "style:region-right" );
965 TQDomElement text2 = doc.createElement( "text:p" );
966 convertPart( headerRight, doc, text2, ksdoc );
967 right.appendChild( text2 );
968
969 header.appendChild( left );
970 header.appendChild( center );
971 header.appendChild( right );
972
973 masterPage.appendChild( header );
974 }
975 else
976 {
977 TQDomElement header = doc.createElement( "style:header" );
978 TQDomElement text = doc.createElement( "text:p" );
979 TQDomElement name = doc.createElement( "text:sheet-name" );
980 name.appendChild( doc.createTextNode( "???" ) );
981 text.appendChild( name );
982 header.appendChild( text );
983
984 masterPage.appendChild( header );
985 }
986
987 if ( ( footerLeft.length() > 0 ) || ( footerCenter.length() > 0 )
988 || ( footerRight.length() > 0 ) )
989 {
990 TQDomElement footer = doc.createElement( "style:footer" );
991 TQDomElement left = doc.createElement( "style:region-left" );
992 TQDomElement text = doc.createElement( "text:p" );
993 convertPart( footerLeft, doc, text, ksdoc );
994 left.appendChild( text );
995
996 TQDomElement center = doc.createElement( "style:region-center" );
997 TQDomElement text1 = doc.createElement( "text:p" );
998 convertPart( footerCenter, doc, text1, ksdoc );
999 center.appendChild( text1 );
1000
1001 TQDomElement right = doc.createElement( "style:region-right" );
1002 TQDomElement text2 = doc.createElement( "text:p" );
1003 convertPart( footerRight, doc, text2, ksdoc );
1004 right.appendChild( text2 );
1005
1006 footer.appendChild( left );
1007 footer.appendChild( center );
1008 footer.appendChild( right );
1009
1010 masterPage.appendChild( footer );
1011 }
1012 else
1013 {
1014 TQDomElement footer = doc.createElement( "style:footer" );
1015 TQDomElement text = doc.createElement( "text:p" );
1016 text.appendChild( doc.createTextNode( i18n( "Page " ) ) );
1017 TQDomElement number = doc.createElement( "text:page-number" );
1018 number.appendChild( doc.createTextNode( "1" ) );
1019 text.appendChild( number );
1020 footer.appendChild( text );
1021
1022 masterPage.appendChild( footer );
1023 }
1024
1025 masterStyles.appendChild( masterPage );
1026}
1027
1028void OpenCalcExport::addText( TQString const & text, TQDomDocument & doc,
1029 TQDomElement & parent )
1030{
1031 if (text.length() > 0 )
1032 parent.appendChild( doc.createTextNode( text ) );
1033}
1034
1035void OpenCalcExport::convertPart( TQString const & part, TQDomDocument & doc,
1036 TQDomElement & parent, const Doc * ksdoc )
1037{
1038 TQString text;
1039 TQString var;
1040
1041 bool inVar = false;
1042 uint i = 0;
1043 uint l = part.length();
1044 while ( i < l )
1045 {
1046 if ( inVar || part[i] == '<' )
1047 {
1048 inVar = true;
1049 var += part[i];
1050 if ( part[i] == '>' )
1051 {
1052 inVar = false;
1053 if ( var == "<page>" )
1054 {
1055 addText( text, doc, parent );
1056
1057 TQDomElement page = doc.createElement( "text:page-number" );
1058 page.appendChild( doc.createTextNode( "1" ) );
1059 parent.appendChild( page );
1060 }
1061 else if ( var == "<pages>" )
1062 {
1063 addText( text, doc, parent );
1064
1065 TQDomElement page = doc.createElement( "text:page-count" );
1066 page.appendChild( doc.createTextNode( "99" ) );
1067 parent.appendChild( page );
1068 }
1069 else if ( var == "<date>" )
1070 {
1071 addText( text, doc, parent );
1072
1073 TQDomElement t = doc.createElement( "text:date" );
1074 t.setAttribute( "text:date-value", "0-00-00" );
1075 // todo: "style:data-style-name", "N2"
1076 t.appendChild( doc.createTextNode( TQDate::currentDate().toString() ) );
1077 parent.appendChild( t );
1078 }
1079 else if ( var == "<time>" )
1080 {
1081 addText( text, doc, parent );
1082
1083 TQDomElement t = doc.createElement( "text:time" );
1084 t.appendChild( doc.createTextNode( TQTime::currentTime().toString() ) );
1085 parent.appendChild( t );
1086 }
1087 else if ( var == "<file>" ) // filepath + name
1088 {
1089 addText( text, doc, parent );
1090
1091 TQDomElement t = doc.createElement( "text:file-name" );
1092 t.setAttribute( "text:display", "full" );
1093 t.appendChild( doc.createTextNode( "???" ) );
1094 parent.appendChild( t );
1095 }
1096 else if ( var == "<name>" ) // filename
1097 {
1098 addText( text, doc, parent );
1099
1100 TQDomElement t = doc.createElement( "text:title" );
1101 t.appendChild( doc.createTextNode( "???" ) );
1102 parent.appendChild( t );
1103 }
1104 else if ( var == "<author>" )
1105 {
1106 KoDocumentInfo * docInfo = ksdoc->documentInfo();
1107 KoDocumentInfoAuthor * authorPage = static_cast<KoDocumentInfoAuthor*>( docInfo->page( "author" ) );
1108
1109 text += authorPage->fullName();
1110
1111 addText( text, doc, parent );
1112 }
1113 else if ( var == "<email>" )
1114 {
1115 KoDocumentInfo * docInfo = ksdoc->documentInfo();
1116 KoDocumentInfoAuthor * authorPage = static_cast<KoDocumentInfoAuthor*>( docInfo->page( "author" ) );
1117
1118 text += authorPage->email();
1119
1120 addText( text, doc, parent );
1121 }
1122 else if ( var == "<org>" )
1123 {
1124 KoDocumentInfo * docInfo = ksdoc->documentInfo();
1125 KoDocumentInfoAuthor * authorPage = static_cast<KoDocumentInfoAuthor*>( docInfo->page( "author" ) );
1126
1127 text += authorPage->company();
1128
1129 addText( text, doc, parent );
1130 }
1131 else if ( var == "<sheet>" )
1132 {
1133 addText( text, doc, parent );
1134
1135 TQDomElement s = doc.createElement( "text:sheet-name" );
1136 s.appendChild( doc.createTextNode( "???" ) );
1137 parent.appendChild( s );
1138 }
1139 else
1140 {
1141 // no known variable:
1142 text += var;
1143 addText( text, doc, parent );
1144 }
1145
1146 text = "";
1147 var = "";
1148 }
1149 }
1150 else
1151 {
1152 text += part[i];
1153 }
1154 ++i;
1155 }
1156 if ( !text.isEmpty() || !var.isEmpty() )
1157 {
1158 //we don't have var at the end =>store it
1159 addText( text+var, doc, parent );
1160 }
1161}
1162
1163TQString OpenCalcExport::convertFormula( TQString const & formula ) const
1164{
1165 TQChar decimalSymbol( '.' );
1166 if ( m_locale )
1167 {
1168 const TQString decimal ( m_locale->decimalSymbol() );
1169 if ( !decimal.isEmpty() )
1170 {
1171 decimalSymbol = decimal.at( 0 );
1172 }
1173 }
1174
1175 TQString s;
1176 TQRegExp exp("(\\$?)([a-zA-Z]+)(\\$?)([0-9]+)");
1177 int n = exp.search( formula, 0 );
1178 kdDebug(30518) << "Exp: " << formula << ", n: " << n << ", Length: " << formula.length()
1179 << ", Matched length: " << exp.matchedLength() << endl;
1180
1181 bool inQuote1 = false;
1182 bool inQuote2 = false;
1183 int i = 0;
1184 int l = (int) formula.length();
1185 if ( l <= 0 )
1186 return formula;
1187 while ( i < l )
1188 {
1189 if ( ( n != -1 ) && ( n < i ) )
1190 {
1191 n = exp.search( formula, i );
1192 kdDebug(30518) << "Exp: " << formula.right( l - i ) << ", n: " << n << endl;
1193 }
1194 if ( formula[i] == '"' )
1195 {
1196 inQuote1 = !inQuote1;
1197 s += formula[i];
1198 ++i;
1199 continue;
1200 }
1201 if ( formula[i] == '\'' )
1202 {
1203 // named area
1204 inQuote2 = !inQuote2;
1205 ++i;
1206 continue;
1207 }
1208 if ( inQuote1 || inQuote2 )
1209 {
1210 s += formula[i];
1211 ++i;
1212 continue;
1213 }
1214 if ( ( formula[i] == '=' ) && ( formula[i + 1] == '=' ) )
1215 {
1216 s += '=';
1217 ++i;++i;
1218 continue;
1219 }
1220 if ( formula[i] == '!' )
1221 {
1222 insertBracket( s );
1223 s += '.';
1224 ++i;
1225 continue;
1226 }
1227 else if ( formula[i] == decimalSymbol )
1228 {
1229 s += '.'; // decimal point
1230 ++i;
1231 continue;
1232 }
1233 if ( n == i )
1234 {
1235 int ml = exp.matchedLength();
1236 if ( formula[ i + ml ] == '!' )
1237 {
1238 kdDebug(30518) << "No cell ref but sheet name" << endl;
1239 s += formula[i];
1240 ++i;
1241 continue;
1242 }
1243 if ( ( i > 0 ) && ( formula[i - 1] != '!' ) )
1244 s += "[.";
1245 for ( int j = 0; j < ml; ++j )
1246 {
1247 s += formula[i];
1248 ++i;
1249 }
1250 s += ']';
1251 continue;
1252 }
1253
1254 s += formula[i];
1255 ++i;
1256 }
1257
1258 return s;
1259}
1260
1261bool OpenCalcExport::writeMetaFile( KoStore * store, uint filesWritten )
1262{
1263 store->enterDirectory( "META-INF" );
1264 if ( !store->open( "manifest.xml" ) )
1265 return false;
1266
1267 TQDomImplementation impl;
1268 TQDomDocumentType type( impl.createDocumentType( "manifest:manifest", "-//OpenOffice.org//DTD Manifest 1.0//EN", "Manifest.dtd" ) );
1269
1270 TQDomDocument meta( type );
1271 meta.appendChild( meta.createProcessingInstruction( "xml","version=\"1.0\" encoding=\"UTF-8\"" ) );
1272
1273 TQDomElement content = meta.createElement( "manifest:manifest" );
1274 content.setAttribute( "xmlns:manifest", "http://openoffice.org/2001/manifest" );
1275
1276 TQDomElement entry = meta.createElement( "manifest:file-entry" );
1277 entry.setAttribute( "manifest:media-type", "application/vnd.sun.xml.calc" );
1278 entry.setAttribute( "manifest:full-path", "/" );
1279 content.appendChild( entry );
1280
1281 entry = meta.createElement( "manifest:file-entry" );
1282 content.appendChild( entry );
1283
1284 if ( filesWritten & contentXML )
1285 {
1286 entry = meta.createElement( "manifest:file-entry" );
1287 entry.setAttribute( "manifest:media-type", "text/xml" );
1288 entry.setAttribute( "manifest:full-path", "content.xml" );
1289 content.appendChild( entry );
1290 }
1291
1292 if ( filesWritten & stylesXML )
1293 {
1294 entry = meta.createElement( "manifest:file-entry" );
1295 entry.setAttribute( "manifest:media-type", "text/xml" );
1296 entry.setAttribute( "manifest:full-path", "styles.xml" );
1297 content.appendChild( entry );
1298 }
1299
1300 if ( filesWritten & metaXML )
1301 {
1302 entry = meta.createElement( "manifest:file-entry" );
1303 entry.setAttribute( "manifest:media-type", "text/xml" );
1304 entry.setAttribute( "manifest:full-path", "meta.xml" );
1305 content.appendChild( entry );
1306 }
1307
1308 if ( filesWritten & settingsXML )
1309 {
1310 entry = meta.createElement( "manifest:file-entry" );
1311 entry.setAttribute( "manifest:media-type", "text/xml" );
1312 entry.setAttribute( "manifest:full-path", "settings.xml" );
1313 content.appendChild( entry );
1314 }
1315
1316 meta.appendChild( content );
1317
1318 TQCString doc( meta.toCString() );
1319 kdDebug(30518) << "Manifest: " << doc << endl;
1320
1321 store->write( doc, doc.length() );
1322
1323 if ( !store->close() )
1324 return false;
1325
1326 return true;
1327}
1328
1329#include <opencalcexport.moc>