diff options
Diffstat (limited to 'filters/kspread/opencalc/opencalcexport.cpp')
-rw-r--r-- | filters/kspread/opencalc/opencalcexport.cpp | 1329 |
1 files changed, 1329 insertions, 0 deletions
diff --git a/filters/kspread/opencalc/opencalcexport.cpp b/filters/kspread/opencalc/opencalcexport.cpp new file mode 100644 index 000000000..2db60420f --- /dev/null +++ b/filters/kspread/opencalc/opencalcexport.cpp @@ -0,0 +1,1329 @@ +/* This file is part of the KDE project + Copyright (C) 2000 David Faure <faure@kde.org> + Copyright (C) 2000 Norbert Andres <nandres@web.de> + Copyright (C) 2005 Laurent Montel <montel@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#include <float.h> +#include <math.h> + +#include <opencalcexport.h> + +#include <tqdatetime.h> +#include <tqdom.h> +#include <tqfile.h> +#include <tqregexp.h> +#include <tqvaluelist.h> + +#include <kdebug.h> +#include <tdemessagebox.h> +#include <kmdcodec.h> +#include <kgenericfactory.h> +#include <tdelocale.h> + +#include <KoDocumentInfo.h> +#include <KoFilterChain.h> +#include <KoGlobal.h> + +#include <kspread_aboutdata.h> +#include <kspread_cell.h> +#include <kspread_doc.h> +#include <kspread_format.h> +#include <kspread_map.h> +#include <kspread_view.h> +#include <kspread_canvas.h> +#include <kspread_sheet.h> +#include <kspread_sheetprint.h> +#include <kspread_style.h> +#include <kspread_style_manager.h> +#include <kspread_util.h> + +using namespace KSpread; + +typedef TQValueList<Reference> AreaList; + +class OpenCalcExportFactory : KGenericFactory<OpenCalcExport, KoFilter> +{ +public: + OpenCalcExportFactory(void) : KGenericFactory<OpenCalcExport, KoFilter> ("kspreadopencalcexport") + {} +protected: + virtual void setupTranslations( void ) + { + TDEGlobal::locale()->insertCatalogue( "kofficefilters" ); + } +}; + +K_EXPORT_COMPONENT_FACTORY( libopencalcexport, OpenCalcExportFactory() ) + +#define STOPEXPORT \ + do \ + { \ + delete store; \ + return false; \ + } while(0) + +OpenCalcExport::OpenCalcExport( KoFilter *, const char *, const TQStringList & ) + : KoFilter(), m_locale( 0 ) +{ +} + +KoFilter::ConversionStatus OpenCalcExport::convert( const TQCString & from, + const TQCString & to ) +{ + /* later... + KSpreadLeader * leader = new KSpreadLeader( m_chain ); + OpenCalcWorker * worker = new OpenCalcWorker(); + leader->setWorker( worker ); + + KoFilter::ConversionStatus status = leader->convert(); + + delete worker; + delete leader; + + return status; + */ + + KoDocument * document = m_chain->inputDocument(); + + if ( !document ) + return KoFilter::StupidError; + + if ( !::tqqt_cast<const KSpread::Doc *>( document ) ) + { + kdWarning(30518) << "document isn't a KSpread::Doc but a " + << document->className() << endl; + return KoFilter::NotImplemented; + } + + if ( ( to != "application/vnd.sun.xml.calc") || (from != "application/x-kspread" ) ) + { + kdWarning(30518) << "Invalid mimetypes " << to << " " << from << endl; + return KoFilter::NotImplemented; + } + + const Doc * ksdoc = static_cast<const Doc *>(document); + + if ( ksdoc->mimeType() != "application/x-kspread" ) + { + kdWarning(30518) << "Invalid document mimetype " << ksdoc->mimeType() << endl; + return KoFilter::NotImplemented; + } + + m_locale = static_cast<Doc*>(document)->locale(); + if ( !writeFile( ksdoc ) ) + return KoFilter::CreationError; + + emit sigProgress( 100 ); + + return KoFilter::OK; +} + +bool OpenCalcExport::writeFile( const Doc * ksdoc ) +{ + KoStore * store = KoStore::createStore( m_chain->outputFile(), KoStore::Write, "", KoStore::Zip ); + + if ( !store ) + return false; + + uint filesWritten = 0; + + if ( !exportContent( store, ksdoc ) ) + STOPEXPORT; + else + filesWritten |= contentXML; + + // TODO: pass sheet number and cell number + if ( !exportDocInfo( store, ksdoc ) ) + STOPEXPORT; + else + filesWritten |= metaXML; + + if ( !exportStyles( store, ksdoc ) ) + STOPEXPORT; + else + filesWritten |= stylesXML; + + if ( !exportSettings( store, ksdoc ) ) + STOPEXPORT; + else + filesWritten |= settingsXML; + + if ( !writeMetaFile( store, filesWritten ) ) + STOPEXPORT; + + // writes zip file to disc + delete store; + store = 0; + + return true; +} + +bool OpenCalcExport::exportDocInfo( KoStore * store, const Doc* ksdoc ) +{ + if ( !store->open( "meta.xml" ) ) + return false; + + KoDocumentInfo * docInfo = ksdoc->documentInfo(); + KoDocumentInfoAbout * aboutPage = static_cast<KoDocumentInfoAbout *>( docInfo->page( "about" ) ); + KoDocumentInfoAuthor * authorPage = static_cast<KoDocumentInfoAuthor*>( docInfo->page( "author" ) ); + + TQDomDocument meta; + meta.appendChild( meta.createProcessingInstruction( "xml","version=\"1.0\" encoding=\"UTF-8\"" ) ); + + TQDomElement content = meta.createElement( "office:document-meta" ); + content.setAttribute( "xmlns:office", "http://openoffice.org/2000/office"); + content.setAttribute( "xmlns:xlink", "http://www.w3.org/1999/xlink" ); + content.setAttribute( "xmlns:dc", "http://purl.org/dc/elements/1.1/" ); + content.setAttribute( "xmlns:meta", "http://openoffice.org/2000/meta" ); + content.setAttribute( "office:version", "1.0" ); + + TQDomNode officeMeta = meta.createElement( "office:meta" ); + + TQDomElement data = meta.createElement( "meta:generator" ); + TQString app( "KSpread " ); + app += KSpread::version; + data.appendChild( meta.createTextNode( app ) ); + officeMeta.appendChild( data ); + + data = meta.createElement( "meta:initial-creator" ); + data.appendChild( meta.createTextNode( authorPage->fullName() ) ); + officeMeta.appendChild( data ); + + data = meta.createElement( "meta:creator" ); + data.appendChild( meta.createTextNode( authorPage->fullName() ) ); + officeMeta.appendChild( data ); + + data = meta.createElement( "dc:description" ); + data.appendChild( meta.createTextNode( aboutPage->abstract() ) ); + officeMeta.appendChild( data ); + + data = meta.createElement( "meta:keywords" ); + TQDomElement dataItem = meta.createElement( "meta:keyword" ); + dataItem.appendChild( meta.createTextNode( aboutPage->keywords() ) ); + data.appendChild( dataItem ); + officeMeta.appendChild( data ); + + data = meta.createElement( "dc:title" ); + data.appendChild( meta.createTextNode( aboutPage->title() ) ); + officeMeta.appendChild( data ); + + data = meta.createElement( "dc:subject" ); + data.appendChild( meta.createTextNode( aboutPage->subject() ) ); + officeMeta.appendChild( data ); + + const TQDateTime dt ( TQDateTime::currentDateTime() ); + if ( dt.isValid() ) + { + data = meta.createElement( "dc:date" ); + data.appendChild( meta.createTextNode( dt.toString( Qt::ISODate ) ) ); + officeMeta.appendChild( data ); + } + + /* TODO: + <meta:creation-date>2003-01-08T23:57:31</meta:creation-date> + <dc:language>en-US</dc:language> + <meta:editing-cycles>2</meta:editing-cycles> + <meta:editing-duration>PT38S</meta:editing-duration> + <meta:user-defined meta:name="Info 3"/> + <meta:user-defined meta:name="Info 4"/> + */ + + data = meta.createElement( "meta:document-statistic" ); + data.setAttribute( "meta:table-count", TQString::number( ksdoc->map()->count() ) ); + // TODO: data.setAttribute( "meta:cell-count", ); + officeMeta.appendChild( data ); + + content.appendChild( officeMeta ); + meta.appendChild( content ); + + TQCString doc( meta.toCString() ); + kdDebug(30518) << "Meta: " << doc << endl; + + store->write( doc, doc.length() ); + + if ( !store->close() ) + return false; + + return true; +} + +bool OpenCalcExport::exportSettings( KoStore * store, const Doc * ksdoc ) +{ + if ( !store->open( "settings.xml" ) ) + return false; + + TQDomDocument doc; + doc.appendChild( doc.createProcessingInstruction( "xml","version=\"1.0\" encoding=\"UTF-8\"" ) ); + + TQDomElement settings = doc.createElement( "office:document-settings" ); + settings.setAttribute( "xmlns:office", "http://openoffice.org/2000/office"); + settings.setAttribute( "xmlns:xlink", "http://www.w3.org/1999/xlink" ); + settings.setAttribute( "xmlns:config", "http://openoffice.org/2001/config" ); + settings.setAttribute( "office:version", "1.0" ); + + TQDomElement begin = doc.createElement( "office:settings" ); + + TQDomElement configItem = doc.createElement("config:config-item-set" ); + configItem.setAttribute( "config:name", "view-settings" ); + + TQDomElement mapIndexed = doc.createElement( "config:config-item-map-indexed" ); + mapIndexed.setAttribute("config:name", "Views" ); + configItem.appendChild( mapIndexed ); + + TQDomElement mapItem = doc.createElement("config:config-item-map-entry" ); + + TQDomElement attribute = doc.createElement("config:config-item" ); + attribute.setAttribute( "config:name", "ActiveTable" ); + attribute.setAttribute( "config:type", "string" ); + + View * view = static_cast<View*>( ksdoc->views().getFirst()); + TQString activeTable; + if ( view ) // no view if embedded document + { + Canvas * canvas = view->canvasWidget(); + activeTable = canvas->activeSheet()->sheetName(); + // save current sheet selection before to save marker, otherwise current pos is not saved + view->saveCurrentSheetSelection(); + } + attribute.appendChild( doc.createTextNode( activeTable ) ); + mapItem.appendChild( attribute ); + + TQDomElement configmaped = doc.createElement( "config:config-item-map-named" ); + configmaped.setAttribute( "config:name","Tables" ); + + TQPtrListIterator<Sheet> it( ksdoc->map()->sheetList() ); + for( ; it.current(); ++it ) + { + TQPoint marker; + if ( view ) + { + marker = view->markerFromSheet( *it ); + } + TQDomElement tmpItemMapNamed = doc.createElement( "config:config-item-map-entry" ); + tmpItemMapNamed.setAttribute( "config:name", ( *it )->tableName() ); + + TQDomElement sheetAttribute = doc.createElement( "config:config-item" ); + sheetAttribute.setAttribute( "config:name", "CursorPositionX" ); + sheetAttribute.setAttribute( "config:type", "int" ); + sheetAttribute.appendChild( doc.createTextNode( TQString::number(marker.x() ) ) ); + tmpItemMapNamed.appendChild( sheetAttribute ); + + sheetAttribute = doc.createElement( "config:config-item" ); + sheetAttribute.setAttribute( "config:name", "CursorPositionY" ); + sheetAttribute.setAttribute( "config:type", "int" ); + sheetAttribute.appendChild( doc.createTextNode( TQString::number(marker.y() ) ) ); + tmpItemMapNamed.appendChild( sheetAttribute ); + + configmaped.appendChild( tmpItemMapNamed ); + } + mapItem.appendChild( configmaped ); + + + + mapIndexed.appendChild( mapItem ); + + begin.appendChild( configItem ); + + settings.appendChild( begin ); + + doc.appendChild( settings ); + + TQCString f( doc.toCString() ); + kdDebug(30518) << "Settings: " << (char const * ) f << endl; + + store->write( f, f.length() ); + + if ( !store->close() ) + return false; + + return true; +} + +bool OpenCalcExport::exportContent( KoStore * store, const Doc * ksdoc ) +{ + if ( !store->open( "content.xml" ) ) + return false; + + createDefaultStyles(); + + TQDomDocument doc; + doc.appendChild( doc.createProcessingInstruction( "xml","version=\"1.0\" encoding=\"UTF-8\"" ) ); + + TQDomElement content = doc.createElement( "office:document-content" ); + content.setAttribute( "xmlns:office", "http://openoffice.org/2000/office"); + content.setAttribute( "xmlns:style", "http://openoffice.org/2000/style" ); + content.setAttribute( "xmlns:text", "http://openoffice.org/2000/text" ); + content.setAttribute( "xmlns:table", "http://openoffice.org/2000/table" ); + content.setAttribute( "xmlns:draw", "http://openoffice.org/2000/drawing" ); + content.setAttribute( "xmlns:fo", "http://www.w3.org/1999/XSL/Format" ); + content.setAttribute( "xmlns:xlink", "http://www.w3.org/1999/xlink" ); + content.setAttribute( "xmlns:number", "http://openoffice.org/2000/datastyle" ); + content.setAttribute( "xmlns:svg", "http://www.w3.org/2000/svg" ); + content.setAttribute( "xmlns:chart", "http://openoffice.org/2000/chart" ); + content.setAttribute( "xmlns:dr3d", "http://openoffice.org/2000/dr3d" ); + content.setAttribute( "xmlns:math", "http://www.w3.org/1998/Math/MathML" ); + content.setAttribute( "xmlns:form", "http://openoffice.org/2000/form" ); + content.setAttribute( "xmlns:script", "http://openoffice.org/2000/script" ); + content.setAttribute( "office:class", "spreadsheet" ); + content.setAttribute( "office:version", "1.0" ); + + TQDomElement data = doc.createElement( "office:script" ); + content.appendChild( data ); + + if ( !exportBody( doc, content, ksdoc ) ) + return false; + + doc.appendChild( content ); + + TQCString f( doc.toCString() ); + kdDebug(30518) << "Content: " << (char const * ) f << endl; + + store->write( f, f.length() ); + + if ( !store->close() ) + return false; + + return true; +} + +void exportNamedExpr( TQDomDocument & doc, TQDomElement & parent, + AreaList const & namedAreas ) +{ + AreaList::const_iterator it = namedAreas.begin(); + AreaList::const_iterator end = namedAreas.end(); + + while ( it != end ) + { + TQDomElement namedRange = doc.createElement( "table:named-range" ); + + Reference ref = *it; + + namedRange.setAttribute( "table:name", ref.ref_name ); + namedRange.setAttribute( "table:base-cell-address", convertRefToBase( ref.sheet_name, ref.rect ) ); + namedRange.setAttribute( "table:cell-range-address", convertRefToRange( ref.sheet_name, ref.rect ) ); + + parent.appendChild( namedRange ); + + ++it; + } +} + +bool OpenCalcExport::exportBody( TQDomDocument & doc, TQDomElement & content, const Doc * ksdoc ) +{ + TQDomElement fontDecls = doc.createElement( "office:font-decls" ); + TQDomElement autoStyles = doc.createElement( "office:automatic-styles" ); + TQDomElement body = doc.createElement( "office:body" ); + + if ( ksdoc->map()->isProtected() ) + { + body.setAttribute( "table:structure-protected", "true" ); + + TQCString passwd; + ksdoc->map()->password( passwd ); + if ( passwd.length() > 0 ) + { + TQCString str( KCodecs::base64Encode( passwd ) ); + body.setAttribute( "table:protection-key", TQString( str.data() ) ); + } + } + + + + TQPtrListIterator<Sheet> it( ksdoc->map()->sheetList() ); + + for( it.toFirst(); it.current(); ++it ) + { + SheetStyle ts; + int maxCols = 1; + int maxRows = 1; + Sheet * sheet = it.current(); + + ts.visible = !sheet->isHidden(); + + TQDomElement tabElem = doc.createElement( "table:table" ); + tabElem.setAttribute( "table:style-name", m_styles.sheetStyle( ts ) ); + + if ( sheet->isProtected() ) + { + tabElem.setAttribute( "table:protected", "true" ); + + TQCString passwd; + sheet->password( passwd ); + if ( passwd.length() > 0 ) + { + TQCString str( KCodecs::base64Encode( passwd ) ); + tabElem.setAttribute( "table:protection-key", TQString( str.data() ) ); + } + } + + TQString name( sheet->tableName() ); + + int n = name.find( ' ' ); + if ( n != -1 ) + { + kdDebug(30518) << "Sheet name converting: " << name << endl; + name[n] == '_'; + kdDebug(30518) << "Sheet name converted: " << name << endl; + } + name = name.replace( ' ', "_" ); + + TQRect _printRange = sheet->print()->printRange(); + if ( _printRange != ( TQRect( TQPoint( 1, 1 ), TQPoint( KS_colMax, KS_rowMax ) ) ) ) + { + TQString range= convertRangeToRef( name, _printRange ); + //kdDebug(30518)<<" range : "<<range<<endl; + tabElem.setAttribute( "table:print-ranges", range ); + } + + + tabElem.setAttribute( "table:name", name ); + + maxRowCols( sheet, maxCols, maxRows ); + + exportSheet( doc, tabElem, sheet, maxCols, maxRows ); + + body.appendChild( tabElem ); + } + + KoDocument * document = m_chain->inputDocument(); + Doc * kspreadDoc = static_cast<Doc *>( document ); + + AreaList namedAreas = kspreadDoc->listArea(); + if ( namedAreas.count() > 0 ) + { + TQDomElement namedExpr = doc.createElement( "table:named-expressions" ); + exportNamedExpr( doc, namedExpr, namedAreas ); + + body.appendChild( namedExpr ); + } + + m_styles.writeStyles( doc, autoStyles ); + m_styles.writeFontDecl( doc, fontDecls ); + + content.appendChild( fontDecls ); + content.appendChild( autoStyles ); + content.appendChild( body ); + + return true; +} + +void OpenCalcExport::exportSheet( TQDomDocument & doc, TQDomElement & tabElem, + const Sheet * sheet, int maxCols, int maxRows ) +{ + kdDebug(30518) << "exportSheet: " << sheet->tableName() << endl; + int i = 1; + + while ( i <= maxCols ) + { + const ColumnFormat * column = sheet->columnFormat( i ); + ColumnStyle cs; + cs.breakB = ::Style::automatic; + cs.size = column->mmWidth() / 10; + bool hide = column->isHide(); + + int j = i + 1; + int repeated = 1; + while ( j <= maxCols ) + { + const ColumnFormat *c = sheet->columnFormat( j ); + ColumnStyle cs1; + cs1.breakB = ::Style::automatic; + cs1.size = c->mmWidth() / 10; + + if ( ColumnStyle::isEqual( &cs, cs1 ) && ( hide == c->isHide() ) ) + ++repeated; + else + break; + ++j; + } + + TQDomElement colElem = doc.createElement( "table:table-column" ); + colElem.setAttribute( "table:style-name", m_styles.columnStyle( cs ) ); + colElem.setAttribute( "table:default-cell-style-name", "Default" );//todo fixme create style from cell + if ( hide ) + colElem.setAttribute( "table:visibility", "collapse" ); + + if ( repeated > 1 ) + colElem.setAttribute( "table:number-columns-repeated", TQString::number( repeated ) ); + + tabElem.appendChild( colElem ); + i += repeated; + } + + for ( i = 1; i <= maxRows; ++i ) + { + const RowFormat * row = sheet->rowFormat( i ); + RowStyle rs; + rs.breakB = ::Style::automatic; + rs.size = row->mmHeight() / 10; + + TQDomElement rowElem = doc.createElement( "table:table-row" ); + rowElem.setAttribute( "table:style-name", m_styles.rowStyle( rs ) ); + if ( row->isHide() ) + rowElem.setAttribute( "table:visibility", "collapse" ); + + exportCells( doc, rowElem, sheet, i, maxCols ); + + tabElem.appendChild( rowElem ); + } +} + +void OpenCalcExport::exportCells( TQDomDocument & doc, TQDomElement & rowElem, + const Sheet *sheet, int row, int maxCols ) +{ + int i = 1; + while ( i <= maxCols ) + { + int repeated = 1; + bool hasComment = false; + const Cell* cell = sheet->cellAt( i, row ); + TQDomElement cellElem; + + if ( !cell->isPartOfMerged() ) + cellElem = doc.createElement( "table:table-cell" ); + else + cellElem = doc.createElement( "table:covered-table-cell" ); + + TQFont font; + Value const value( cell->value() ); + if ( !cell->isDefault() ) + { + font = cell->format()->textFont( i, row ); + m_styles.addFont( font ); + + if ( cell->format()->hasProperty( Format::PComment ) ) + hasComment = true; + } + + CellStyle c; + CellStyle::loadData( c, cell ); // TODO: number style + + cellElem.setAttribute( "table:style-name", m_styles.cellStyle( c ) ); + + // group empty cells with the same style + if ( cell->isEmpty() && !hasComment && !cell->isPartOfMerged() && !cell->doesMergeCells() ) + { + int j = i + 1; + while ( j <= maxCols ) + { + const Cell *cell1 = sheet->cellAt( j, row ); + + CellStyle c1; + CellStyle::loadData( c1, cell1 ); // TODO: number style + + if ( cell1->isEmpty() && !cell->format()->hasProperty( Format::PComment ) + && CellStyle::isEqual( &c, c1 ) && !cell->isPartOfMerged() && !cell->doesMergeCells() ) + ++repeated; + else + break; + ++j; + } + if ( repeated > 1 ) + cellElem.setAttribute( "table:number-columns-repeated", TQString::number( repeated ) ); + } + + if ( value.isBoolean() ) + { + kdDebug(30518) << "Type: Boolean" << endl; + cellElem.setAttribute( "table:value-type", "boolean" ); + cellElem.setAttribute( "table:boolean-value", ( value.asBoolean() ? "true" : "false" ) ); + } + else if ( value.isNumber() ) + { + kdDebug(30518) << "Type: Number" << endl; + FormatType type = cell->format()->getFormatType( i, row ); + + if ( type == Percentage_format ) + cellElem.setAttribute( "table:value-type", "percentage" ); + else + cellElem.setAttribute( "table:value-type", "float" ); + + cellElem.setAttribute( "table:value", TQString::number( value.asFloat() ) ); + } + else + { + kdDebug(30518) << "Type: " << value.type() << endl; + } + + if ( cell->isFormula() ) + { + kdDebug(30518) << "Formula found" << endl; + + TQString formula( convertFormula( cell->text() ) ); + cellElem.setAttribute( "table:formula", formula ); + } + else if ( !cell->link().isEmpty() ) + { + TQDomElement link = doc.createElement( "text:p" ); + TQDomElement linkref = doc.createElement( "text:a" ); + + TQString tmp = cell->link(); + if ( localReferenceAnchor( tmp ) ) + linkref.setAttribute( "xlink:href", ( "#"+tmp ) ); + else + linkref.setAttribute( "xlink:href", tmp ); + + linkref.appendChild( doc.createTextNode( cell->text() ) ); + + link.appendChild( linkref ); + cellElem.appendChild( link ); + } + else if ( !cell->isEmpty() ) + { + TQDomElement textElem = doc.createElement( "text:p" ); + textElem.appendChild( doc.createTextNode( cell->strOutText() ) ); + + cellElem.appendChild( textElem ); + kdDebug(30518) << "Cell StrOut: " << cell->strOutText() << endl; + } + + if ( cell->doesMergeCells() ) + { + int colSpan = cell->mergedXCells() + 1; + int rowSpan = cell->mergedYCells() + 1; + + if ( colSpan > 1 ) + cellElem.setAttribute( "table:number-columns-spanned", TQString::number( colSpan ) ); + + if ( rowSpan > 1 ) + cellElem.setAttribute( "table:number-rows-spanned", TQString::number( rowSpan ) ); + } + + if ( hasComment ) + { + TQString comment( cell->format()->comment( i, row ) ); + TQDomElement annotation = doc.createElement( "office:annotation" ); + TQDomElement text = doc.createElement( "text:p" ); + text.appendChild( doc.createTextNode( comment ) ); + + annotation.appendChild( text ); + cellElem.appendChild( annotation ); + } + + rowElem.appendChild( cellElem ); + + i += repeated; + } +} + +void OpenCalcExport::maxRowCols( const Sheet *sheet, + int & maxCols, int & maxRows ) +{ + Cell const * cell = sheet->firstCell(); + + while ( cell ) + { + if ( cell->column() > maxCols ) + maxCols = cell->column(); + + if ( cell->row() > maxRows ) + maxRows = cell->row(); + + cell = cell->nextCell(); + } + + RowFormat const * row = sheet->firstRow(); + + while ( row ) + { + if ( row->row() > maxRows ) + maxRows = row->row(); + + row = row->next(); + } + + ColumnFormat const * col = sheet->firstCol(); + while ( col ) + { + if ( col->column() > maxCols ) + maxCols = col->column(); + + col = col->next(); + } + +} + +bool OpenCalcExport::exportStyles( KoStore * store, const Doc *ksdoc ) +{ + if ( !store->open( "styles.xml" ) ) + return false; + + TQDomDocument doc; + doc.appendChild( doc.createProcessingInstruction( "xml","version=\"1.0\" encoding=\"UTF-8\"" ) ); + + TQDomElement content = doc.createElement( "office:document-styles" ); + content.setAttribute( "xmlns:office", "http://openoffice.org/2000/office" ); + content.setAttribute( "xmlns:style", "http://openoffice.org/2000/style" ); + content.setAttribute( "xmlns:text", "http://openoffice.org/2000/text" ); + content.setAttribute( "xmlns:table", "http://openoffice.org/2000/table" ); + content.setAttribute( "xmlns:draw", "http://openoffice.org/2000/drawing" ); + content.setAttribute( "xmlns:fo", "http://www.w3.org/1999/XSL/Format" ); + content.setAttribute( "xmlns:xlink", "http://www.w3.org/1999/xlink" ); + content.setAttribute( "xmlns:number", "http://openoffice.org/2000/datastyle" ); + content.setAttribute( "xmlns:svg", "http://www.w3.org/2000/svg" ); + content.setAttribute( "xmlns:chart", "http://openoffice.org/2000/chart" ); + content.setAttribute( "xmlns:dr3d", "http://openoffice.org/2000/dr3d" ); + content.setAttribute( "xmlns:math", "http://www.w3.org/1998/Math/MathML" ); + content.setAttribute( "xmlns:form", "http://openoffice.org/2000/form" ); + content.setAttribute( "xmlns:script", "http://openoffice.org/2000/script" ); + content.setAttribute( "office:version", "1.0" ); + + // order important here! + TQDomElement officeStyles = doc.createElement( "office:styles" ); + exportDefaultCellStyle( doc, officeStyles ); + + TQDomElement fontDecls = doc.createElement( "office:font-decls" ); + m_styles.writeFontDecl( doc, fontDecls ); + + // TODO: needs in new number/date/time parser... + // exportDefaultNumberStyles( doc, officeStyles ); + + TQDomElement defaultStyle = doc.createElement( "style:style" ); + defaultStyle.setAttribute( "style:name", "Default" ); + defaultStyle.setAttribute( "style:family", "table-cell" ); + officeStyles.appendChild( defaultStyle ); + + TQDomElement autoStyles = doc.createElement( "office:automatic-styles" ); + exportPageAutoStyles( doc, autoStyles, ksdoc ); + + TQDomElement masterStyles = doc.createElement( "office:master-styles" ); + exportMasterStyles( doc, masterStyles, ksdoc ); + + content.appendChild( fontDecls ); + content.appendChild( officeStyles ); + content.appendChild( autoStyles ); + content.appendChild( masterStyles ); + + doc.appendChild( content ); + + TQCString f( doc.toCString() ); + kdDebug(30518) << "Content: " << (char const * ) f << endl; + + store->write( f, f.length() ); + + if ( !store->close() ) + return false; + + return true; +} + +void OpenCalcExport::exportDefaultCellStyle( TQDomDocument & doc, TQDomElement & officeStyles ) +{ + TQDomElement defStyle = doc.createElement( "style:default-style" ); + defStyle.setAttribute( "style:family", "table-cell" ); + + KoDocument * document = m_chain->inputDocument(); + Doc * ksdoc = static_cast<Doc *>(document); + + Format * format = new Format( 0, ksdoc->styleManager()->defaultStyle() ); + const TDELocale *locale = ksdoc->locale(); + TQString language; + TQString country; + TQString charSet; + + TQString l( locale->language() ); + TDELocale::splitLocale( l, language, country, charSet ); + TQFont font( format->font() ); + m_styles.addFont( font, true ); + + TQDomElement style = doc.createElement( "style:properties" ); + style.setAttribute( "style:font-name", font.family() ); + style.setAttribute( "fo:font-size", TQString( "%1pt" ).arg( font.pointSize() ) ); + style.setAttribute( "style:decimal-places", TQString::number( locale->fracDigits() ) ); + style.setAttribute( "fo:language", language ); + style.setAttribute( "fo:country", country ); + style.setAttribute( "style:font-name-asian", "HG Mincho Light J" ); + style.setAttribute( "style:language-asian", "none" ); + style.setAttribute( "style:country-asian", "none" ); + style.setAttribute( "style:font-name-complex", "Arial Unicode MS" ); + style.setAttribute( "style:language-complex", "none" ); + style.setAttribute( "style:country-complex", "none" ); + style.setAttribute( "style:tab-stop-distance", "1.25cm" ); + + defStyle.appendChild( style ); + officeStyles.appendChild( defStyle ); + delete format; +} + +void OpenCalcExport::createDefaultStyles() +{ + // TODO: default number styles, currency styles,... +} + +void OpenCalcExport::exportPageAutoStyles( TQDomDocument & doc, TQDomElement & autoStyles, + const Doc *ksdoc ) +{ + TQPtrListIterator<Sheet> it( ksdoc->map()->sheetList() ); + const Sheet * sheet = it.toFirst(); + + float width = 20.999; + float height = 29.699; + + if ( sheet ) + { + width = sheet->print()->paperWidth() / 10; + height = sheet->print()->paperHeight() / 10; + } + + TQString sWidth = TQString( "%1cm" ).arg( width ); + TQString sHeight = TQString( "%1cm" ).arg( height ); + + TQDomElement pageMaster = doc.createElement( "style:page-master" ); + pageMaster.setAttribute( "style:name", "pm1" ); + + TQDomElement properties = doc.createElement( "style:properties" ); + properties.setAttribute( "fo:page-width", sWidth ); + properties.setAttribute( "fo:page-height", sHeight ); + properties.setAttribute( "fo:border", "0.002cm solid #000000" ); + properties.setAttribute( "fo:padding", "0cm" ); + properties.setAttribute( "fo:background-color", "transparent" ); + + pageMaster.appendChild( properties ); + + TQDomElement header = doc.createElement( "style:header-style" ); + properties = doc.createElement( "style:properties" ); + properties.setAttribute( "fo:min-height", "0.75cm" ); + properties.setAttribute( "fo:margin-left", "0cm" ); + properties.setAttribute( "fo:margin-right", "0cm" ); + properties.setAttribute( "fo:margin-bottom", "0.25cm" ); + + header.appendChild( properties ); + + TQDomElement footer = doc.createElement( "style:header-style" ); + properties = doc.createElement( "style:properties" ); + properties.setAttribute( "fo:min-height", "0.75cm" ); + properties.setAttribute( "fo:margin-left", "0cm" ); + properties.setAttribute( "fo:margin-right", "0cm" ); + properties.setAttribute( "fo:margin-bottom", "0.25cm" ); + + footer.appendChild( properties ); + + pageMaster.appendChild( header ); + pageMaster.appendChild( footer ); + + autoStyles.appendChild( pageMaster ); +} + +void OpenCalcExport::exportMasterStyles( TQDomDocument & doc, TQDomElement & masterStyles, + const Doc * ksdoc ) +{ + TQDomElement masterPage = doc.createElement( "style:master-page" ); + masterPage.setAttribute( "style:name", "Default" ); + masterPage.setAttribute( "style:page-master-name", "pm1" ); + + TQPtrListIterator<Sheet> it( ksdoc->map()->sheetList() ); + const Sheet * sheet = it.toFirst(); + + TQString headerLeft; + TQString headerCenter; + TQString headerRight; + TQString footerLeft; + TQString footerCenter; + TQString footerRight; + + if ( sheet ) + { + headerLeft = sheet->print()->headLeft(); + headerCenter = sheet->print()->headMid(); + headerRight = sheet->print()->headRight(); + footerLeft = sheet->print()->footLeft(); + footerCenter = sheet->print()->footMid(); + footerRight = sheet->print()->footRight(); + } + + if ( ( headerLeft.length() > 0 ) || ( headerCenter.length() > 0 ) + || ( headerRight.length() > 0 ) ) + { + TQDomElement header = doc.createElement( "style:header" ); + TQDomElement left = doc.createElement( "style:region-left" ); + TQDomElement text = doc.createElement( "text:p" ); + convertPart( headerLeft, doc, text, ksdoc ); + left.appendChild( text ); + + TQDomElement center = doc.createElement( "style:region-center" ); + TQDomElement text1 = doc.createElement( "text:p" ); + convertPart( headerCenter, doc, text1, ksdoc ); + center.appendChild( text1 ); + + TQDomElement right = doc.createElement( "style:region-right" ); + TQDomElement text2 = doc.createElement( "text:p" ); + convertPart( headerRight, doc, text2, ksdoc ); + right.appendChild( text2 ); + + header.appendChild( left ); + header.appendChild( center ); + header.appendChild( right ); + + masterPage.appendChild( header ); + } + else + { + TQDomElement header = doc.createElement( "style:header" ); + TQDomElement text = doc.createElement( "text:p" ); + TQDomElement name = doc.createElement( "text:sheet-name" ); + name.appendChild( doc.createTextNode( "???" ) ); + text.appendChild( name ); + header.appendChild( text ); + + masterPage.appendChild( header ); + } + + if ( ( footerLeft.length() > 0 ) || ( footerCenter.length() > 0 ) + || ( footerRight.length() > 0 ) ) + { + TQDomElement footer = doc.createElement( "style:footer" ); + TQDomElement left = doc.createElement( "style:region-left" ); + TQDomElement text = doc.createElement( "text:p" ); + convertPart( footerLeft, doc, text, ksdoc ); + left.appendChild( text ); + + TQDomElement center = doc.createElement( "style:region-center" ); + TQDomElement text1 = doc.createElement( "text:p" ); + convertPart( footerCenter, doc, text1, ksdoc ); + center.appendChild( text1 ); + + TQDomElement right = doc.createElement( "style:region-right" ); + TQDomElement text2 = doc.createElement( "text:p" ); + convertPart( footerRight, doc, text2, ksdoc ); + right.appendChild( text2 ); + + footer.appendChild( left ); + footer.appendChild( center ); + footer.appendChild( right ); + + masterPage.appendChild( footer ); + } + else + { + TQDomElement footer = doc.createElement( "style:footer" ); + TQDomElement text = doc.createElement( "text:p" ); + text.appendChild( doc.createTextNode( i18n( "Page " ) ) ); + TQDomElement number = doc.createElement( "text:page-number" ); + number.appendChild( doc.createTextNode( "1" ) ); + text.appendChild( number ); + footer.appendChild( text ); + + masterPage.appendChild( footer ); + } + + masterStyles.appendChild( masterPage ); +} + +void OpenCalcExport::addText( TQString const & text, TQDomDocument & doc, + TQDomElement & parent ) +{ + if (text.length() > 0 ) + parent.appendChild( doc.createTextNode( text ) ); +} + +void OpenCalcExport::convertPart( TQString const & part, TQDomDocument & doc, + TQDomElement & parent, const Doc * ksdoc ) +{ + TQString text; + TQString var; + + bool inVar = false; + uint i = 0; + uint l = part.length(); + while ( i < l ) + { + if ( inVar || part[i] == '<' ) + { + inVar = true; + var += part[i]; + if ( part[i] == '>' ) + { + inVar = false; + if ( var == "<page>" ) + { + addText( text, doc, parent ); + + TQDomElement page = doc.createElement( "text:page-number" ); + page.appendChild( doc.createTextNode( "1" ) ); + parent.appendChild( page ); + } + else if ( var == "<pages>" ) + { + addText( text, doc, parent ); + + TQDomElement page = doc.createElement( "text:page-count" ); + page.appendChild( doc.createTextNode( "99" ) ); + parent.appendChild( page ); + } + else if ( var == "<date>" ) + { + addText( text, doc, parent ); + + TQDomElement t = doc.createElement( "text:date" ); + t.setAttribute( "text:date-value", "0-00-00" ); + // todo: "style:data-style-name", "N2" + t.appendChild( doc.createTextNode( TQDate::currentDate().toString() ) ); + parent.appendChild( t ); + } + else if ( var == "<time>" ) + { + addText( text, doc, parent ); + + TQDomElement t = doc.createElement( "text:time" ); + t.appendChild( doc.createTextNode( TQTime::currentTime().toString() ) ); + parent.appendChild( t ); + } + else if ( var == "<file>" ) // filepath + name + { + addText( text, doc, parent ); + + TQDomElement t = doc.createElement( "text:file-name" ); + t.setAttribute( "text:display", "full" ); + t.appendChild( doc.createTextNode( "???" ) ); + parent.appendChild( t ); + } + else if ( var == "<name>" ) // filename + { + addText( text, doc, parent ); + + TQDomElement t = doc.createElement( "text:title" ); + t.appendChild( doc.createTextNode( "???" ) ); + parent.appendChild( t ); + } + else if ( var == "<author>" ) + { + KoDocumentInfo * docInfo = ksdoc->documentInfo(); + KoDocumentInfoAuthor * authorPage = static_cast<KoDocumentInfoAuthor*>( docInfo->page( "author" ) ); + + text += authorPage->fullName(); + + addText( text, doc, parent ); + } + else if ( var == "<email>" ) + { + KoDocumentInfo * docInfo = ksdoc->documentInfo(); + KoDocumentInfoAuthor * authorPage = static_cast<KoDocumentInfoAuthor*>( docInfo->page( "author" ) ); + + text += authorPage->email(); + + addText( text, doc, parent ); + } + else if ( var == "<org>" ) + { + KoDocumentInfo * docInfo = ksdoc->documentInfo(); + KoDocumentInfoAuthor * authorPage = static_cast<KoDocumentInfoAuthor*>( docInfo->page( "author" ) ); + + text += authorPage->company(); + + addText( text, doc, parent ); + } + else if ( var == "<sheet>" ) + { + addText( text, doc, parent ); + + TQDomElement s = doc.createElement( "text:sheet-name" ); + s.appendChild( doc.createTextNode( "???" ) ); + parent.appendChild( s ); + } + else + { + // no known variable: + text += var; + addText( text, doc, parent ); + } + + text = ""; + var = ""; + } + } + else + { + text += part[i]; + } + ++i; + } + if ( !text.isEmpty() || !var.isEmpty() ) + { + //we don't have var at the end =>store it + addText( text+var, doc, parent ); + } +} + +TQString OpenCalcExport::convertFormula( TQString const & formula ) const +{ + TQChar decimalSymbol( '.' ); + if ( m_locale ) + { + const TQString decimal ( m_locale->decimalSymbol() ); + if ( !decimal.isEmpty() ) + { + decimalSymbol = decimal.at( 0 ); + } + } + + TQString s; + TQRegExp exp("(\\$?)([a-zA-Z]+)(\\$?)([0-9]+)"); + int n = exp.search( formula, 0 ); + kdDebug(30518) << "Exp: " << formula << ", n: " << n << ", Length: " << formula.length() + << ", Matched length: " << exp.matchedLength() << endl; + + bool inQuote1 = false; + bool inQuote2 = false; + int i = 0; + int l = (int) formula.length(); + if ( l <= 0 ) + return formula; + while ( i < l ) + { + if ( ( n != -1 ) && ( n < i ) ) + { + n = exp.search( formula, i ); + kdDebug(30518) << "Exp: " << formula.right( l - i ) << ", n: " << n << endl; + } + if ( formula[i] == '"' ) + { + inQuote1 = !inQuote1; + s += formula[i]; + ++i; + continue; + } + if ( formula[i] == '\'' ) + { + // named area + inQuote2 = !inQuote2; + ++i; + continue; + } + if ( inQuote1 || inQuote2 ) + { + s += formula[i]; + ++i; + continue; + } + if ( ( formula[i] == '=' ) && ( formula[i + 1] == '=' ) ) + { + s += '='; + ++i;++i; + continue; + } + if ( formula[i] == '!' ) + { + insertBracket( s ); + s += '.'; + ++i; + continue; + } + else if ( formula[i] == decimalSymbol ) + { + s += '.'; // decimal point + ++i; + continue; + } + if ( n == i ) + { + int ml = exp.matchedLength(); + if ( formula[ i + ml ] == '!' ) + { + kdDebug(30518) << "No cell ref but sheet name" << endl; + s += formula[i]; + ++i; + continue; + } + if ( ( i > 0 ) && ( formula[i - 1] != '!' ) ) + s += "[."; + for ( int j = 0; j < ml; ++j ) + { + s += formula[i]; + ++i; + } + s += ']'; + continue; + } + + s += formula[i]; + ++i; + } + + return s; +} + +bool OpenCalcExport::writeMetaFile( KoStore * store, uint filesWritten ) +{ + store->enterDirectory( "META-INF" ); + if ( !store->open( "manifest.xml" ) ) + return false; + + TQDomImplementation impl; + TQDomDocumentType type( impl.createDocumentType( "manifest:manifest", "-//OpenOffice.org//DTD Manifest 1.0//EN", "Manifest.dtd" ) ); + + TQDomDocument meta( type ); + meta.appendChild( meta.createProcessingInstruction( "xml","version=\"1.0\" encoding=\"UTF-8\"" ) ); + + TQDomElement content = meta.createElement( "manifest:manifest" ); + content.setAttribute( "xmlns:manifest", "http://openoffice.org/2001/manifest" ); + + TQDomElement entry = meta.createElement( "manifest:file-entry" ); + entry.setAttribute( "manifest:media-type", "application/vnd.sun.xml.calc" ); + entry.setAttribute( "manifest:full-path", "/" ); + content.appendChild( entry ); + + entry = meta.createElement( "manifest:file-entry" ); + content.appendChild( entry ); + + if ( filesWritten & contentXML ) + { + entry = meta.createElement( "manifest:file-entry" ); + entry.setAttribute( "manifest:media-type", "text/xml" ); + entry.setAttribute( "manifest:full-path", "content.xml" ); + content.appendChild( entry ); + } + + if ( filesWritten & stylesXML ) + { + entry = meta.createElement( "manifest:file-entry" ); + entry.setAttribute( "manifest:media-type", "text/xml" ); + entry.setAttribute( "manifest:full-path", "styles.xml" ); + content.appendChild( entry ); + } + + if ( filesWritten & metaXML ) + { + entry = meta.createElement( "manifest:file-entry" ); + entry.setAttribute( "manifest:media-type", "text/xml" ); + entry.setAttribute( "manifest:full-path", "meta.xml" ); + content.appendChild( entry ); + } + + if ( filesWritten & settingsXML ) + { + entry = meta.createElement( "manifest:file-entry" ); + entry.setAttribute( "manifest:media-type", "text/xml" ); + entry.setAttribute( "manifest:full-path", "settings.xml" ); + content.appendChild( entry ); + } + + meta.appendChild( content ); + + TQCString doc( meta.toCString() ); + kdDebug(30518) << "Manifest: " << doc << endl; + + store->write( doc, doc.length() ); + + if ( !store->close() ) + return false; + + return true; +} + +#include <opencalcexport.moc> |