diff options
Diffstat (limited to 'filters/kword/oowriter/ExportFilter.cpp')
-rw-r--r-- | filters/kword/oowriter/ExportFilter.cpp | 2229 |
1 files changed, 2229 insertions, 0 deletions
diff --git a/filters/kword/oowriter/ExportFilter.cpp b/filters/kword/oowriter/ExportFilter.cpp new file mode 100644 index 000000000..0894d0443 --- /dev/null +++ b/filters/kword/oowriter/ExportFilter.cpp @@ -0,0 +1,2229 @@ +/* This file is part of the KDE project + Copyright (C) 2001, 2002, 2003, 2004 Nicolas GOUTTE <goutte@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. +*/ + +/* + This file is based on the old file: + /home/kde/koffice/filters/kword/ascii/asciiexport.cpp + + The old file was copyrighted by + Copyright (C) 1998, 1999 Reginald Stadlbauer <reggie@kde.org> + Copyright (c) 2000 ID-PRO Deutschland GmbH. All rights reserved. + Contact: Wolf-Michael Bolle <Bolle@ID-PRO.de> + + The old file was licensed under the terms of the GNU Library General Public + License version 2. +*/ + +#include <tqmap.h> +#include <tqiodevice.h> +#include <tqbuffer.h> +#include <tqtextstream.h> +#include <tqdom.h> + +#include <kdebug.h> +#include <tdelocale.h> +#include <kzip.h> + +#include <KoPageLayout.h> +#include <KoPictureKey.h> +#include <KoPicture.h> + +#include <KWEFStructures.h> +#include <KWEFUtil.h> +#include <KWEFBaseWorker.h> +#include <KWEFKWordLeader.h> + +#include "ExportFilter.h" + +OOWriterWorker::OOWriterWorker(void) : m_streamOut(NULL), + m_paperBorderTop(0.0),m_paperBorderLeft(0.0), + m_paperBorderBottom(0.0),m_paperBorderRight(0.0), m_zip(NULL), m_pictureNumber(0), + m_automaticParagraphStyleNumber(0), m_automaticTextStyleNumber(0), + m_footnoteNumber(0), m_tableNumber(0), m_textBoxNumber( 0 ), + m_columnspacing( 36.0 ), m_columns( 1 ) +{ +} + +TQString OOWriterWorker::escapeOOText(const TQString& strText) const +{ + // Escape quotes (needed in attributes) + // Escape apostrophs (allowed by XML) + return KWEFUtil::EscapeSgmlText(NULL,strText,true,true); +} + +TQString OOWriterWorker::escapeOOSpan(const TQString& strText) const +// We need not only to escape the classical XML stuff but also to take care of spaces and tabs. +{ + TQString strReturn; + TQChar ch; + int spaceNumber = 0; // How many spaces should be written + + for (uint i=0; i<strText.length(); i++) + { + ch=strText[i]; + + if (ch!=' ') + { + // The next character is not a space (anymore) + if ( spaceNumber > 0 ) + { + strReturn += ' '; + --spaceNumber; + if ( spaceNumber > 0 ) + { + strReturn += "<text:s text:c=\""; + strReturn += TQString::number( spaceNumber ); + strReturn += "\"/>"; + } + spaceNumber = 0; + } + } + + // ### TODO: would be switch/case or if/elseif the best? + switch (ch.unicode()) + { + case 9: // Tab + { + strReturn+="<text:tab-stop/>"; + break; + } + case 10: // Line-feed + { + strReturn+="<text:line-break/>"; + break; + } + case 32: // Space + { + if ( spaceNumber > 0 ) + { + ++spaceNumber; + } + else + { + spaceNumber = 1; + } + break; + } + case 38: // & + { + strReturn+="&"; + break; + } + case 60: // < + { + strReturn+="<"; + break; + } + case 62: // > + { + strReturn+=">"; + break; + } + case 34: // " + { + strReturn+="""; + break; + } + case 39: // ' + { + strReturn+="'"; + break; + } + case 1: // (Non-XML-compatible) replacement character from KWord 0.8 + { + strReturn += '#'; //use KWord 1.[123] replacement character instead + break; + } + // Following characters are not allowed in XML (but some files from KWord 0.8 have some of them.) + case 0: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: + case 11: + case 12: + case 14: + case 15: + case 16: + case 17: + case 18: + case 19: + case 20: + case 21: + case 22: + case 23: + case 24: + case 25: + case 26: + case 27: + case 28: + case 29: + case 30: + case 31: + { + kdWarning(30518) << "Not allowed XML character: " << ch.unicode() << endl; + strReturn += '?'; + break; + } + case 13: // ### TODO: what to do with it? + default: + { + strReturn+=ch; + break; + } + } + } + + if ( spaceNumber > 0 ) + { + // The last characters were spaces + strReturn += ' '; + --spaceNumber; + if ( spaceNumber > 0 ) + { + strReturn += "<text:s text:c=\""; + strReturn += TQString::number( spaceNumber ); + strReturn += "\"/>"; + } + spaceNumber = 0; + } + + return strReturn; +} + +bool OOWriterWorker::doOpenFile(const TQString& filenameOut, const TQString& ) +{ + kdDebug(30518) << "Opening file: " << filenameOut + << " (in OOWriterWorker::doOpenFile)" << endl; + + m_zip=new KZip(filenameOut); // How to check failure? + + if (!m_zip->open(IO_WriteOnly)) + { + kdError(30518) << "Could not open ZIP file for writing! Aborting!" << endl; + delete m_zip; + m_zip=NULL; + return false; + } + + m_zip->setCompression( KZip::NoCompression ); + m_zip->setExtraField( KZip::NoExtraField ); + + const TQCString appId( "application/vnd.sun.xml.writer" ); + + m_zip->writeFile( "mimetype", TQString(), TQString(), appId.length(), appId.data() ); + + m_zip->setCompression( KZip::DeflateCompression ); + + m_streamOut=new TQTextStream(m_contentBody, IO_WriteOnly); + + m_streamOut->setEncoding( TQTextStream::UnicodeUTF8 ); + + return true; +} + +bool OOWriterWorker::zipPrepareWriting(const TQString& name) +{ + if (!m_zip) + return false; + m_size=0; + return m_zip->prepareWriting(name, TQString(), TQString(), 0); +} + +bool OOWriterWorker::zipDoneWriting(void) +{ + if (!m_zip) + return false; + return m_zip->doneWriting(m_size); +} + +bool OOWriterWorker::zipWriteData(const char* str) +{ + if (!m_zip) + return false; + const uint size=strlen(str); + m_size+=size; + return m_zip->writeData(str,size); +} + +bool OOWriterWorker::zipWriteData(const TQByteArray& array) +{ + if (!m_zip) + return false; + const uint size=array.size(); + m_size+=size; + return m_zip->writeData(array.data(),size); +} + +bool OOWriterWorker::zipWriteData(const TQCString& cstr) +{ + if (!m_zip) + return false; + const uint size=cstr.length(); + m_size+=size; + return m_zip->writeData(cstr.data(),size); +} + +bool OOWriterWorker::zipWriteData(const TQString& str) +{ + return zipWriteData(str.utf8()); +} + +void OOWriterWorker::writeStartOfFile(const TQString& type) +{ + const bool noType=type.isEmpty(); + zipWriteData("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"); + + zipWriteData("<!DOCTYPE office:document"); + if (!noType) + { + // No type might happen for raw XML documents (which this filter does not support yet.) + zipWriteData("-"); + zipWriteData(type); + } + zipWriteData(" PUBLIC \"-//OpenOffice.org//DTD OfficeDocument 1.0//EN\""); + zipWriteData(" \"office.dtd\""); + zipWriteData(">\n"); + + zipWriteData("<office:document"); + if (!noType) + { + zipWriteData("-"); + zipWriteData(type); + } + + // The name spaces used by OOWriter (those not used by this filter are commented out) + + // General namespaces + zipWriteData(" xmlns:office=\"http://openoffice.org/2000/office\""); + zipWriteData(" xmlns:xlink=\"http://www.w3.org/1999/xlink\""); + + // Namespaces for context.xml and style.xml + if ( type == "content" || type == "styles" || type.isEmpty() ) + { + zipWriteData(" xmlns:style=\"http://openoffice.org/2000/style\""); + zipWriteData(" xmlns:text=\"http://openoffice.org/2000/text\""); + zipWriteData(" xmlns:table=\"http://openoffice.org/2000/table\""); + zipWriteData(" xmlns:draw=\"http://openoffice.org/2000/drawing\""); + zipWriteData(" xmlns:fo=\"http://www.w3.org/1999/XSL/Format\""); + + //zipWriteData(" xmlns:number=\"http://openoffice.org/2000/datastyle\""); + zipWriteData(" xmlns:svg=\"http://www.w3.org/2000/svg\""); + //zipWriteData(" xmlns:chart=\"http://openoffice.org/2000/chart\""); + //zipWriteData(" xmlns:dr3d=\"http://openoffice.org/2000/dr3d\""); + //zipWriteData(" xmlns:math=\"http://www.w3.org/1998/Math/MathML""); + //zipWriteData(" xmlns:form=\"http://openoffice.org/2000/form\""); + //zipWriteData(" xmlns:script=\"http://openoffice.org/2000/script\""); + } + + // Namespaces For meta.xml + if ( type == "meta" || type.isEmpty() ) + { + zipWriteData(" xmlns:dc=\"http://purl.org/dc/elements/1.1/\""); + zipWriteData(" xmlns:meta=\"http://openoffice.org/2000/meta\""); + } + + zipWriteData(" office:class=\"text\""); + + +#ifdef STRICT_OOWRITER_VERSION_1 + zipWriteData(" office:version=\"1.0\""); +#else + // We are using an (rejected draft OASIS) extension compared to version 1.0, so we cannot write the version string. + // (We do not even write it for context.xml and meta.xml, as OOWriter 1.0.1 does not like it in this case.) +#endif + + zipWriteData(">\n"); +} + +void OOWriterWorker::writeFontDeclaration(void) +{ + zipWriteData( " <office:font-decls>\n"); + TQMap<TQString,TQString>::ConstIterator end(m_fontNames.end()); + for (TQMap<TQString,TQString>::ConstIterator it=m_fontNames.begin(); it!=end; ++it) + { + const bool space=(it.key().find(' ')>=0); // Does the font has at least a space in its name + const TQString fontName(escapeOOText(it.key())); + zipWriteData(" <style:font-decl style:name=\""); + zipWriteData(fontName); + zipWriteData("\" fo:font-family=\""); + if (space) + { // It has a space, so (simple) quote it + zipWriteData("'"); + zipWriteData(fontName); + zipWriteData("'"); + } + else + { // The font has no space in its name, so it can be written normally. + zipWriteData(fontName); + } + zipWriteData("\" "); + zipWriteData(it.data()); // already in XML, so do not escape + zipWriteData(" />\n"); + } + zipWriteData(" </office:font-decls>\n"); +} + +void OOWriterWorker::writeStylesXml(void) +{ + if (!m_zip) + return; + + zipPrepareWriting("styles.xml"); + + writeStartOfFile("styles"); + + writeFontDeclaration(); + + zipWriteData(m_styles); + + zipWriteData(" <office:automatic-styles>\n"); + zipWriteData(" <style:page-master style:name=\"pm1\">\n"); // ### TODO: verify if style name is unique + + zipWriteData(" <style:properties "); + zipWriteData( " style:page-usage=\"all\"" ); // ### TODO: check + + zipWriteData(" fo:page-width=\""); + zipWriteData(TQString::number(m_paperWidth)); + zipWriteData("pt\" fo:page-height=\""); + zipWriteData(TQString::number(m_paperHeight)); + zipWriteData("pt\" "); + + zipWriteData("style:print-orientation=\""); + if (1==m_paperOrientation) + { + zipWriteData("landscape"); + } + else + { + zipWriteData("portrait"); + } + + zipWriteData("\" fo:margin-top=\""); + zipWriteData(TQString::number(m_paperBorderTop)); + zipWriteData("pt\" fo:margin-bottom=\""); + zipWriteData(TQString::number(m_paperBorderBottom)); + zipWriteData("pt\" fo:margin-left=\""); + zipWriteData(TQString::number(m_paperBorderLeft)); + zipWriteData("pt\" fo:margin-right=\""); + zipWriteData(TQString::number(m_paperBorderRight)); + zipWriteData("pt\" style:first-page-number=\""); + zipWriteData(TQString::number(m_varSet.startingPageNumber)); + zipWriteData( "\">\n" ); + + if ( m_columns > 1 ) + { + zipWriteData( " <style:columns" ); + zipWriteData( " fo:column-count=\"" ); + zipWriteData( TQString::number( m_columns ) ); + zipWriteData( "\" fo:column-gap=\"" ); + zipWriteData( TQString::number( m_columnspacing ) ); + zipWriteData( "pt\">\n" ); + + for (int i=0; i < m_columns; ++i) + { + zipWriteData( " <style:column style:rel-width=\"1*\" fo:margin-left=\"0cm\" fo:margin-right=\"0cm\"/>\n" ); + } + + zipWriteData( " </style:columns>\n" ); + } + + zipWriteData(" </style:properties>\n"); + + zipWriteData(" </style:page-master>\n"); + zipWriteData(" </office:automatic-styles>\n"); + + zipWriteData(" <office:master-styles>\n"); + zipWriteData(" <style:master-page style:name=\"Standard\" style:page-master-name=\"pm1\" />\n"); + zipWriteData(" </office:master-styles>\n"); + + zipWriteData( "</office:document-styles>\n" ); + + zipDoneWriting(); +} + +void OOWriterWorker::writeContentXml(void) +{ + if (!m_zip) + return; + + zipPrepareWriting("content.xml"); + + writeStartOfFile("content"); + + writeFontDeclaration(); + + zipWriteData(" <office:automatic-styles>\n"); + zipWriteData(m_contentAutomaticStyles); + m_contentAutomaticStyles = TQString(); // Release memory + + zipWriteData(" </office:automatic-styles>\n"); + + zipWriteData(m_contentBody); + m_contentBody.resize( 0 ); // Release memory + + zipWriteData( "</office:document-content>\n" ); + + zipDoneWriting(); +} + +void OOWriterWorker::writeMetaXml(void) +{ + if (!m_zip) + return; + + zipPrepareWriting("meta.xml"); + + writeStartOfFile("meta"); + + zipWriteData(" <office:meta>\n"); + + // Tell who we are in case that we have a bug in our filter output! + zipWriteData(" <meta:generator>KWord's OOWriter Export Filter"); + zipWriteData(TQString("$Revision: 515673 $").mid(10).remove('$')); // has a leading and a trailing space. + + zipWriteData("</meta:generator>\n"); + + if (!m_docInfo.title.isEmpty()) + { + zipWriteData(" <dc:title>"); + zipWriteData(escapeOOText(m_docInfo.title)); + zipWriteData("</dc:title>\n"); + } + if (!m_docInfo.abstract.isEmpty()) + { + zipWriteData(" <dc:description>"); + zipWriteData(escapeOOText(m_docInfo.abstract)); + zipWriteData("</dc:description>\n"); + } + + if (m_varSet.creationTime.isValid()) + { + zipWriteData(" <meta:creation-date>"); + zipWriteData(escapeOOText(m_varSet.creationTime.toString(Qt::ISODate))); + zipWriteData("</meta:creation-date>\n"); + } + + if (m_varSet.modificationTime.isValid()) + { + zipWriteData(" <dc:date>"); + zipWriteData(escapeOOText(m_varSet.modificationTime.toString(Qt::ISODate))); + zipWriteData("</dc:date>\n"); + } + + if (m_varSet.printTime.isValid()) + { + zipWriteData(" <meta:print-date>"); + zipWriteData(escapeOOText(m_varSet.printTime.toString(Qt::ISODate))); + zipWriteData("</meta:print-date>\n"); + } + + zipWriteData( " <meta:document-statistic" ); + + // KWord files coming from import filters mostly do not have no page count + if ( m_numPages > 0 ) + { + zipWriteData( " meta:page-count=\"" ); + zipWriteData( TQString::number ( m_numPages ) ); + zipWriteData( "\"" ); + } + + zipWriteData( " meta:image-count=\"" ); // This is not specified in the OO specification section 2.1.19 + zipWriteData( TQString::number ( m_pictureNumber ) ); + zipWriteData( "\"" ); + + zipWriteData( " meta:table-count=\"" ); + zipWriteData( TQString::number ( m_tableNumber ) ); + zipWriteData( "\"" ); + + zipWriteData( "/>\n" ); // meta:document-statistic + + zipWriteData(" </office:meta>\n"); + zipWriteData("</office:document-meta>\n"); + + zipDoneWriting(); +} + +bool OOWriterWorker::doCloseFile(void) +{ + kdDebug(30518)<< "OOWriterWorker::doCloseFile" << endl; + if (m_zip) + { + writeContentXml(); + writeMetaXml(); + writeStylesXml(); + m_zip->close(); + } + + delete m_zip; + m_zip=NULL; + return true; +} + +bool OOWriterWorker::doOpenDocument(void) +{ + kdDebug(30518)<< "OOWriterWorker::doOpenDocument" << endl; + + *m_streamOut << " <office:body>\n"; + + return true; +} + +bool OOWriterWorker::doCloseDocument(void) +{ + *m_streamOut << " </office:body>\n"; + return true; +} + +bool OOWriterWorker::doOpenBody(void) +{ + TQValueList<FrameAnchor>::Iterator it; + + // We have to process all non-inline pictures + kdDebug(30518) << "=== Processing non-inlined pictures ===" << endl; + for ( it = m_nonInlinedPictureAnchors.begin(); it != m_nonInlinedPictureAnchors.end(); ++it ) + { + *m_streamOut << " "; + makePicture( *it, AnchorNonInlined ); + *m_streamOut << "\n"; + } + kdDebug(30518) << "=== Non-inlined pictures processed ===" << endl; + + // We have to process all non-inline tables + kdDebug(30518) << "=== Processing non-inlined tables ===" << endl; + for ( it = m_nonInlinedTableAnchors.begin(); it != m_nonInlinedTableAnchors.end(); ++it ) + { + *m_streamOut << " "; + makeTable( *it, AnchorNonInlined ); + *m_streamOut << "\n"; + } + kdDebug(30518) << "=== Non-inlined tables processed ===" << endl; + + return true; +} + +TQString OOWriterWorker::textFormatToStyle(const TextFormatting& formatOrigin, + const TextFormatting& formatData, const bool force, TQString& key) +{ + // TODO: rename variable formatData + TQString strElement; // TODO: rename this variable + + // Font name + TQString fontName = formatData.fontName; + declareFont(fontName); + if ( !fontName.isEmpty() + && (force || (formatOrigin.fontName!=formatData.fontName))) + { + strElement+="style:font-name=\""; + strElement+= escapeOOText(fontName); + strElement+="\" "; + key += fontName; + } + + key += ","; + + if (force || (formatOrigin.italic!=formatData.italic)) + { + // Font style + strElement+="fo:font-style=\""; + if ( formatData.italic ) + { + strElement+="italic"; + key+='I'; + } + else + { + strElement+="normal"; + key+='N'; + } + strElement+="\" "; + } + + key += ","; + + if (force || ((formatOrigin.weight>=75)!=(formatData.weight>=75))) + { + strElement+="fo:font-weight=\""; + if ( formatData.weight >= 75 ) + { + strElement+="bold"; + key+='B'; + } + else + { + strElement+="normal"; + key+='N'; + } + strElement+="\" "; + } + + key += ","; + + if (force || (formatOrigin.fontSize!=formatData.fontSize)) + { + const int size=formatData.fontSize; + if (size>0) + { + strElement+="fo:font-size=\""; + strElement+=TQString::number(size,10); + strElement+="pt\" "; + key+=TQString::number(size,10); + } + } + + key += ","; + + if (force || (formatOrigin.fgColor!=formatData.fgColor)) + { + if ( formatData.fgColor.isValid() ) + { + strElement+="fo:color=\""; + strElement+=formatData.fgColor.name(); + strElement+="\" "; + key+=formatData.fgColor.name(); + } + } + + key += ","; + + if (force || (formatOrigin.bgColor!=formatData.bgColor)) + { + if ( formatData.bgColor.isValid() ) + { + strElement+="style:text-background-color=\""; // ### what is fo:background-color ? + strElement+=formatData.bgColor.name(); + strElement+="\" "; + key+=formatData.bgColor.name(); + } + } + + key += ';'; // Another separator + + if ( force || ( formatOrigin.underline != formatData.underline ) + || ( formatOrigin.underlineColor != formatData.underlineColor ) + || ( formatOrigin.underlineValue != formatData.underlineValue ) + || ( formatOrigin.underlineStyle != formatData.underlineStyle ) ) + { + strElement+="style:text-underline=\""; + if ( formatData.underline ) + { + TQString underlineValue ( formatData.underlineValue ); + TQString underlineStyle ( formatData.underlineStyle ); + + if ( underlineStyle.isEmpty() ) + underlineStyle = "solid"; + if ( underlineValue == "1" ) + underlineValue = "single"; + + if ( underlineValue == "single" ) + { + if ( underlineStyle == "dash" ) + { + strElement += "dash"; + key += "DA"; + } + else if ( underlineStyle == "dot" ) + { + strElement += "dotted"; + key += "DO"; + } + else if ( underlineStyle == "dashdot" ) + { + strElement += "dot-dash"; + key += "DDA"; + } + else if ( underlineStyle == "dashdotdot" ) + { + strElement += "dot-dot-dash"; + key += "DDDA"; + } + else + { + strElement += "single"; + key += "1"; + } + } + else if ( underlineValue == "double" ) + { + strElement += "double"; + key += "2"; + } + else if ( underlineValue == "single-bold" ) + { + strElement += "bold"; + key += "BL"; + } + else if ( underlineValue == "wave" ) + { + strElement += "wave"; + key += "WV"; + } + else + { + strElement += "single"; + key += "?"; + } + } + else + { + strElement+="none"; + key += 'N'; + } + strElement += "\" "; + + if ( formatData.underline && formatData.underlineColor.isValid() ) + { + const TQString colorName( formatData.underlineColor.name() ); + strElement += "style:text-underline-color=\""; + strElement += colorName; + strElement += "\" "; + key += colorName; + } + + } + + key += ','; + + if ( force + || (formatOrigin.strikeout != formatData.strikeout ) + || (formatOrigin.strikeoutType != formatData.strikeoutType ) ) + { + // OOWriter can only do single, double, thick (and slash and X that KWord cannot do.) + // So no dash, dot and friends. + + strElement+="style:text-crossing-out=\""; + if ( ( formatData.strikeoutType == "single" ) || ( formatData.strikeoutType == "1" ) ) + { + strElement+="single-line"; + key += "1"; + } + else if ( formatData.strikeoutType == "double" ) + { + strElement+="double-line"; + key += "2"; + } + else if ( formatData.strikeoutType == "single-bold" ) + { + strElement+="thick"; + key += "T"; + } + else + { + strElement+="none"; + key += 'N'; + } + strElement+="\" "; + } + + key += ','; + + // It seems that OOWriter 1.1 does have problems with word-by-word (OO Issue #11873, #25187) + // It is supposed to be fixed in development versions of OO + if (force || ( formatOrigin.underlineWord != formatData.underlineWord ) + || (formatOrigin.strikeoutWord != formatData.strikeoutWord ) ) + { + // Strikeout and underline can only have one word-by-word behaviour in OO + // (OO Issue #????? ; will not be changed.) + strElement+="fo:score-spaces=\""; // Are space processed? + if ( formatData.underlineWord || formatData.strikeoutWord ) + { + strElement += "false"; + key += 'W'; + } + else + { + strElement += "true"; + key += 'N'; + } + strElement += "\" "; + } + + key += ','; + + if ( force || ( formatOrigin.language != formatData.language ) ) + { + const TQString lang ( formatData.language ); + if ( ! lang.isEmpty() ) + { + const int res = lang.find( '_' ); + + if ( res >= 0 ) + { + kdDebug(30518) << "Language: " << lang << " => " << lang.left( res ) << " - " << lang.mid( res + 1 ) << endl; + strElement += "fo:language=\""; + strElement += lang.left( res ); + strElement += "\" "; + strElement += "fo:country=\""; + strElement += lang.mid( res + 1 ); + strElement += "\" "; + } + else + { + kdDebug(30518) << "Language without country: " << lang << endl; + strElement += "fo:language=\""; + strElement += lang; + strElement += "\" "; + } + + key+=formatData.language; + } + } + + key += ","; + + if ( force || ( formatOrigin.fontAttribute != formatData.fontAttribute ) ) + { + // Note: OOWriter does not like when both fo:text-transform and fo:font-variant exist (except if both are none/normal) + // (It is documented so, see sections 3.10.1 and 3.10.2) + if ( formatData.fontAttribute == "uppercase" ) + { + strElement += "fo:text-transform=\"uppercase\" "; + key += 'U'; + } + else if ( formatData.fontAttribute == "lowercase" ) + { + strElement += "fo:text-transform=\"lowercase\" "; + key += 'L'; + } + else if ( formatData.fontAttribute == "smallcaps" ) + { + strElement += "fo:font-variant=\"small-caps\" "; + key += 'S'; + } + else + { + strElement += "fo:text-transform=\"none\" "; + strElement += "fo:font-variant=\"normal\" "; + key += 'N'; + } + } + + key += ","; + + if ( force || ( formatOrigin.verticalAlignment != formatData.verticalAlignment ) ) + { + if ( 1 == formatData.verticalAlignment ) + { + //Subscript + strElement += "style:text-position=\"sub\" "; + key += 'B'; + } + else if ( 2 == formatData.verticalAlignment ) + { + //Superscript + strElement += "style:text-position=\"super\" "; + key += 'P'; + } + // ### TODO: how to reset it? "0pt" ? + } + + return strElement.stripWhiteSpace(); // Remove especially trailing spaces +} + +#define ALLOW_TABLE + +TQString OOWriterWorker::cellToProperties( const TableCell& cell, TQString& key) const +{ +#ifdef ALLOW_TABLE + const FrameData& frame = cell.frame; + TQString properties; + + key += "!L"; // left border + key += frame.lColor.name(); + key += ","; + key += TQString::number( frame.lWidth ); + properties += " fo:border-left=\""; + if ( frame.lColor.isValid() && frame.lWidth > 0.0 ) + { + properties += TQString::number( frame.lWidth ); + properties += "pt"; + properties += " solid "; // ### TODO + properties += frame.lColor.name(); + } + else + { + properties += "0pt none #000000"; + } + properties += "\""; + + key += "!R"; // right border + key += frame.rColor.name(); + key += ","; + key += TQString::number( frame.rWidth ); + properties += " fo:border-right=\""; + if ( frame.rColor.isValid() && frame.rWidth > 0.0 ) + { + properties += TQString::number( frame.rWidth ); + properties += "pt"; + properties += " solid "; // ### TODO + properties += frame.rColor.name(); + } + else + { + properties += "0pt none #000000"; + } + properties += "\""; + + key += "!T"; // top border + key += frame.tColor.name(); + key += ","; + key += TQString::number( frame.tWidth ); + properties += " fo:border-top=\""; + if ( frame.tColor.isValid() && frame.tWidth > 0.0 ) + { + properties += TQString::number( frame.tWidth ); + properties += "pt"; + properties += " solid "; // ### TODO + properties += frame.tColor.name(); + } + else + { + properties += "0pt none #000000"; + } + properties += "\""; + + key += "!B"; // bottom border + key += frame.bColor.name(); + key += ","; + key += TQString::number( frame.bWidth ); + properties += " fo:border-bottom=\""; + if ( frame.bColor.isValid() && frame.bWidth > 0.0 ) + { + properties += TQString::number( frame.bWidth ); + properties += "pt"; + properties += " solid "; // ### TODO + properties += frame.bColor.name(); + } + else + { + properties += "0pt none #000000"; + } + properties += "\""; + + return properties; +#else + return TQString(); +#endif +} + +bool OOWriterWorker::makeTableRows( const TQString& tableName, const Table& table, int firstRowNumber ) +{ +#ifdef ALLOW_TABLE + // ### TODO: rows + // ### TODO: be careful that covered-cell can be due vertical spanning. + // ### TODO: One way to find is the difference between the row variables (or against the last known column) + // ### TODO: be careful that fully-covered rows might exist. + + *m_streamOut << "<table:table-row>\n"; + int rowCurrent = firstRowNumber; + + ulong cellNumber = 0L; + + TQMap<TQString,TQString> mapCellStyleKeys; + + for ( TQValueList<TableCell>::ConstIterator itCell ( table.cellList.begin() ); + itCell != table.cellList.end(); ++itCell) + { + if ( rowCurrent != (*itCell).row ) + { + rowCurrent = (*itCell).row; + *m_streamOut << "</table:table-row>\n"; + *m_streamOut << "<table:table-row>\n"; + } + + TQString key; + const TQString props ( cellToProperties( (*itCell), key ) ); + + TQString automaticCellStyle; + TQMap<TQString,TQString>::ConstIterator it ( mapCellStyleKeys.find( key ) ); + if ( it == mapCellStyleKeys.end() ) + { + automaticCellStyle = makeAutomaticStyleName( tableName + ".Cell", cellNumber ); + mapCellStyleKeys [ key ] = automaticCellStyle; + kdDebug(30518) << "Creating automatic cell style: " << automaticCellStyle << " key: " << key << endl; + m_contentAutomaticStyles += " <style:style"; + m_contentAutomaticStyles += " style:name=\"" + escapeOOText( automaticCellStyle ) + "\""; + m_contentAutomaticStyles += " style:family=\"table-cell\""; + m_contentAutomaticStyles += ">\n"; + m_contentAutomaticStyles += " <style:properties "; + m_contentAutomaticStyles += props; + m_contentAutomaticStyles += "/>\n"; + m_contentAutomaticStyles += " </style:style>\n"; + } + else + { + automaticCellStyle = it.data(); + kdDebug(30518) << "Using automatic cell style: " << automaticCellStyle << " key: " << key << endl; + } + + *m_streamOut << "<table:table-cell table:value-type=\"string\" table:style-name=\"" + << escapeOOText( automaticCellStyle) + << "\""; + + // More than one column width? + { + *m_streamOut << " table:number-columns-spanned=\"" << (*itCell).m_cols << "\""; + } + + *m_streamOut << ">\n"; + + if (!doFullAllParagraphs(*(*itCell).paraList)) + { + return false; + } + + *m_streamOut << "</table:table-cell>\n"; + + if ( (*itCell).m_cols > 1 ) + { + // We need to add some placeholder for the "covered" cells + for (int i = 1; i < (*itCell).m_cols; ++i) + { + *m_streamOut << "<table:covered-table-cell/>"; + } + } + } + + *m_streamOut << "</table:table-row>\n"; + return true; +#else + return false; +#endif +} + +#ifdef ALLOW_TABLE +static uint getColumnWidths( const Table& table, TQMemArray<double>& widthArray, int firstRowNumber ) +{ + bool uniqueColumns = true; // We have not found any horizontally spanned cells yet. + uint currentColumn = 0; + int tryingRow = firstRowNumber; // We are trying the first row + TQValueList<TableCell>::ConstIterator itCell; + + for ( itCell = table.cellList.begin(); + itCell != table.cellList.end(); ++itCell ) + { + kdDebug(30518) << "Column: " << (*itCell).col << " (Row: " << (*itCell).row << ")" << endl; + + if ( (*itCell).row != tryingRow ) + { + if ( uniqueColumns ) + { + // We had a full row without any horizontally spanned cell, so we have the needed data + return currentColumn; + } + else + { + // No luck in the previous row, so now try this new one + tryingRow = (*itCell).row; + uniqueColumns = true; + currentColumn = 0; + } + } + + if ( (*itCell).m_cols > 1 ) + { + // We have a horizontally spanned cell + uniqueColumns = false; + // Do not waste the time to calculate the width + continue; + } + + const double width = ( (*itCell).frame.right - (*itCell).frame.left ); + + if ( currentColumn >= widthArray.size() ) + widthArray.resize( currentColumn + 4, TQGArray::SpeedOptim); + + widthArray.at( currentColumn ) = width; + ++currentColumn; + } + + // If we are here, it can be: + // - the table is either empty or there is not any row without horizontally spanned cells + // - we have needed the last row for getting something usable + + return uniqueColumns ? currentColumn : 0; +} +#endif + +#ifdef ALLOW_TABLE +static uint getFirstRowColumnWidths( const Table& table, TQMemArray<double>& widthArray, int firstRowNumber ) +// Get the column widths only by the first row. +// This is used when all table rows have horizontally spanned cells. +{ + uint currentColumn = 0; + TQValueList<TableCell>::ConstIterator itCell; + + for ( itCell = table.cellList.begin(); + itCell != table.cellList.end(); ++itCell ) + { + kdDebug(30518) << "Column: " << (*itCell).col << " (Row: " << (*itCell).row << ")" << endl; + if ( (*itCell).row != firstRowNumber ) + break; // We have finished the first row + + int cols = (*itCell).m_cols; + if ( cols < 1) + cols = 1; + + // ### FIXME: the columns behind a larger cell do not need to be symmetrical + const double width = ( (*itCell).frame.right - (*itCell).frame.left ) / cols; + + if ( currentColumn + cols > widthArray.size() ) + widthArray.resize( currentColumn + 4, TQGArray::SpeedOptim); + + for ( int i = 0; i < cols; ++i ) + { + widthArray.at( currentColumn ) = width; + ++currentColumn; + } + } + return currentColumn; +} +#endif + +bool OOWriterWorker::makeTable( const FrameAnchor& anchor, const AnchorType anchorType ) +{ +#ifdef ALLOW_TABLE + + // Be careful that while being similar the following 5 strings have different purposes + const TQString automaticTableStyle ( makeAutomaticStyleName( "Table", m_tableNumber ) ); // It also increases m_tableNumber + const TQString tableName( TQString( "Table" ) + TQString::number( m_tableNumber ) ); // m_tableNumber was already increased + const TQString translatedName( i18n( "Object name", "Table %1").arg( m_tableNumber ) ); + const TQString automaticFrameStyle ( makeAutomaticStyleName( "TableFrame", m_textBoxNumber ) ); // It also increases m_textBoxNumber + const TQString translatedFrameName( i18n( "Object name", "Table Frame %1").arg( m_textBoxNumber ) ); + + kdDebug(30518) << "Processing table " << anchor.key.toString() << " => " << tableName << endl; + + const TQValueList<TableCell>::ConstIterator firstCell ( anchor.table.cellList.begin() ); + + if ( firstCell == anchor.table.cellList.end() ) + { + kdError(30518) << "Table has not any cell!" << endl; + return false; + } + + const int firstRowNumber = (*firstCell).row; + kdDebug(30518) << "First row: " << firstRowNumber << endl; + + TQMemArray<double> widthArray(4); + + uint numberColumns = getColumnWidths( anchor.table, widthArray, firstRowNumber ); + + if ( numberColumns <= 0 ) + { + kdDebug(30518) << "Could not get correct column widths, so approximating" << endl; + // There was a problem, the width array cannot be trusted, so try to do a column width array with the first row + numberColumns = getFirstRowColumnWidths( anchor.table, widthArray, firstRowNumber ); + if ( numberColumns <= 0 ) + { + // Still not right? Then it is an error! + kdError(30518) << "Cannot get column widths of table " << anchor.key.toString() << endl; + return false; + } + } + + kdDebug(30518) << "Number of columns: " << numberColumns << endl; + + + double tableWidth = 0.0; // total width of table + uint i; // We need the loop variable 2 times + for ( i=0; i < numberColumns; ++i ) + { + tableWidth += widthArray.at( i ); + } + kdDebug(30518) << "Table width: " << tableWidth << endl; + + // An inlined table, is an "as-char" text-box + *m_streamOut << "<draw:text-box"; + *m_streamOut << " style:name=\"" << escapeOOText( automaticFrameStyle ) << "\""; + *m_streamOut << " draw:name=\"" << escapeOOText( translatedFrameName ) << "\""; + if ( anchorType == AnchorNonInlined ) + { + // ### TODO: correctly set a OOWriter frame positioned on the page + *m_streamOut << " text:anchor-type=\"paragraph\""; + } + else + { + *m_streamOut << " text:anchor-type=\"as-char\""; + } + *m_streamOut << " svg:width=\"" << tableWidth << "pt\""; // ### TODO: any supplement to the width? + //*m_streamOut << " fo:min-height=\"1pt\"";// ### TODO: a better height (can be calulated from the KWord table frames) + *m_streamOut << ">\n"; + + *m_streamOut << "<table:table table:name=\"" + << escapeOOText( translatedName ) + << "\" table:style-name=\"" + << escapeOOText( automaticTableStyle ) + << "\" >\n"; + + + // Now we have enough information to generate the style for the table and its frame + + kdDebug(30518) << "Creating automatic frame style: " << automaticFrameStyle /* << " key: " << styleKey */ << endl; + m_contentAutomaticStyles += " <style:style"; // for frame + m_contentAutomaticStyles += " style:name=\"" + escapeOOText( automaticFrameStyle ) + "\""; + m_contentAutomaticStyles += " style:family=\"graphics\""; + m_contentAutomaticStyles += " style:parent-style-name=\"Frame\""; // ### TODO: parent style needs to be correctly defined + m_contentAutomaticStyles += ">\n"; + m_contentAutomaticStyles += " <style:properties "; // ### TODO + m_contentAutomaticStyles += " text:anchor-type=\"as-char\""; // ### TODO: needed? + m_contentAutomaticStyles += " fo:padding=\"0pt\" fo:border=\"none\""; + m_contentAutomaticStyles += " fo:margin-left=\"0pt\""; + m_contentAutomaticStyles += " fo:margin-top=\"0pt\""; + m_contentAutomaticStyles += " fo:margin-bottom=\"0pt\""; + m_contentAutomaticStyles += " fo:margin-right=\"0pt\""; + m_contentAutomaticStyles += "/>\n"; + m_contentAutomaticStyles += " </style:style>\n"; + + kdDebug(30518) << "Creating automatic table style: " << automaticTableStyle /* << " key: " << styleKey */ << endl; + m_contentAutomaticStyles += " <style:style"; // for table + m_contentAutomaticStyles += " style:name=\"" + escapeOOText( automaticTableStyle ) + "\""; + m_contentAutomaticStyles += " style:family=\"table\""; + m_contentAutomaticStyles += ">\n"; + m_contentAutomaticStyles += " <style:properties "; + m_contentAutomaticStyles += " style:width=\"" + TQString::number( tableWidth ) + "pt\" "; + m_contentAutomaticStyles += "/>\n"; + m_contentAutomaticStyles += " </style:style>\n"; + + TQValueList<TableCell>::ConstIterator itCell; + + ulong columnNumber = 0L; + + for ( i=0; i < numberColumns; ++i ) + { + const TQString automaticColumnStyle ( makeAutomaticStyleName( tableName + ".Column", columnNumber ) ); + kdDebug(30518) << "Creating automatic column style: " << automaticColumnStyle /* << " key: " << styleKey */ << endl; + + m_contentAutomaticStyles += " <style:style"; + m_contentAutomaticStyles += " style:name=\"" + escapeOOText( automaticColumnStyle ) + "\""; + m_contentAutomaticStyles += " style:family=\"table-column\""; + m_contentAutomaticStyles += ">\n"; + m_contentAutomaticStyles += " <style:properties "; + // Despite that some OO specification examples use fo:width, OO specification section 4.19 tells to use style:column-width + // and/or the relative variant: style:rel-column-width + m_contentAutomaticStyles += " style:column-width=\"" + TQString::number( widthArray.at( i ) ) + "pt\" "; + m_contentAutomaticStyles += "/>\n"; + m_contentAutomaticStyles += " </style:style>\n"; + + // ### TODO: find a way how to use table:number-columns-repeated for more that one cell's column(s) + *m_streamOut << "<table:table-column table:style-name=\"" + << escapeOOText( automaticColumnStyle ) + << "\" table:number-columns-repeated=\"1\"/>\n"; + } + + makeTableRows( tableName, anchor.table, firstRowNumber ); + + *m_streamOut << "</table:table>\n"; + + *m_streamOut << "</draw:text-box>"; // End of inline + +#endif + return true; +} + +bool OOWriterWorker::makePicture( const FrameAnchor& anchor, const AnchorType anchorType ) +{ + kdDebug(30518) << "New picture: " << anchor.picture.koStoreName + << " , " << anchor.picture.key.toString() << endl; + + const TQString koStoreName(anchor.picture.koStoreName); + + TQByteArray image; + + TQString strExtension(koStoreName.lower()); + const int result=koStoreName.findRev("."); + if (result>=0) + { + strExtension=koStoreName.mid(result+1); + } + + bool isImageLoaded=false; + + if (strExtension=="png") + { + isImageLoaded=loadSubFile(koStoreName,image); + } + else if ((strExtension=="jpg") || (strExtension=="jpeg")) + { + isImageLoaded=loadSubFile(koStoreName,image); + strExtension="jpg"; // ### TODO: verify + } + else if ((strExtension=="tif") || (strExtension=="tiff")) + { + isImageLoaded=loadSubFile(koStoreName,image); + strExtension="tif"; // ### TODO: verify + } + else if ((strExtension=="gif") || (strExtension=="wmf")) + // ### TODO: Which other image formats does OOWriter support directly? + { + isImageLoaded=loadSubFile(koStoreName,image); + } + else + { + // All other picture types must be converted to PNG + isImageLoaded=loadAndConvertToImage(koStoreName,strExtension,"PNG",image); + strExtension="png"; + } + + if (!isImageLoaded) + { + kdWarning(30518) << "Unable to load picture: " << koStoreName << endl; + return true; + } + + kdDebug(30518) << "Picture loaded: " << koStoreName << endl; + + double height = 0.0; + double width = 0.0; + + if ( anchorType == AnchorTextImage ) + { + // Text image have no frameset, so the only size information is in the picture itself. + TQBuffer buffer( image.copy() ); // Be more safe than sorry and do not allow shallow copy + KoPicture pic; + buffer.open( IO_ReadOnly ); + if ( pic.load( TQT_TQIODEVICE(&buffer), strExtension ) ) + { + const TQSize size ( pic.getOriginalSize() ); + height = size.height(); + width = size.width(); + } + else + { + kdWarning(30518) << "Could not load KoPicture: " << koStoreName << endl; + } + buffer.close(); + } + else + { + // Use frame size + height=anchor.frame.bottom - anchor.frame.top; + width =anchor.frame.right - anchor.frame.left; + } + + if ( height < 1.0 ) + { + kdWarning(30518) << "Silly height for " << koStoreName << " : " << height << endl; + height = 72.0; + } + if ( width < 1.0 ) + { + kdWarning(30518) << "Silly width for " << koStoreName << " : " << width << endl; + width = 72.0; + } + + // We need a 32 digit hex value of the picture number + // Please note: it is an exact 32 digit value, truncated if the value is more than 512 bits wide. :-) + TQString number; + number.fill('0',32); + number += TQString::number(++m_pictureNumber,16); // in hex + + TQString ooName("Pictures/"); + ooName += number.right(32); + ooName += '.'; + ooName += strExtension; + + kdDebug(30518) << "Picture " << koStoreName << " => " << ooName << endl; + + // TODO: we are only using the filename, not the rest of the key + // TODO: (bad if there are two images of the same name, but of a different key) + *m_streamOut << "<draw:image draw:name=\"" << anchor.picture.key.filename() << "\""; + *m_streamOut << " draw:style-name=\"Graphics\""; // ### TODO: should be an automatic "graphic" style name instead + if ( anchorType == AnchorNonInlined ) + { + // ### TODO: correctly set a OOWriter frame positioned on the page + *m_streamOut << " text:anchor-type=\"paragraph\""; + } + else + { + *m_streamOut << " text:anchor-type=\"as-char\""; + } + *m_streamOut << " svg:height=\"" << height << "pt\" svg:width=\"" << width << "pt\""; + *m_streamOut << " draw:z-index=\"0\" xlink:href=\"#" << ooName << "\""; + *m_streamOut << " xlink:type=\"simple\" xlink:show=\"embed\" xlink:actuate=\"onLoad\""; + *m_streamOut << "/>"; // NO end of line! + + if (m_zip) + { +#if 0 + // ### FIXME Why is the following line not working (at least with KDE 3.1)? (It makes unzip having problems with meta.xml) + m_zip->writeFile(ooName,TQString(), TQString(), image.size(), image.data()); +#else + zipPrepareWriting(ooName); + zipWriteData( image ); + zipDoneWriting(); +#endif + } + + return true; +} + +void OOWriterWorker::processNormalText ( const TQString ¶Text, + const TextFormatting& formatLayout, + const FormatData& formatData) +{ + // Retrieve text and escape it (and necessary space, tabs and line-break tags) + const TQString partialText( escapeOOSpan( paraText.mid( formatData.pos, formatData.len ) ) ); + + if (formatData.text.missing) + { + // It's just normal text, so we do not need a <text:span> element! + *m_streamOut << partialText; + } + else + { // Text with properties, so use a <text:span> element! + *m_streamOut << "<text:span"; + + TQString styleKey; + const TQString props ( textFormatToStyle(formatLayout,formatData.text,false,styleKey) ); + + TQMap<TQString,TQString>::ConstIterator it ( m_mapTextStyleKeys.find(styleKey) ); + kdDebug(30518) << "Searching text key: " << styleKey << endl; + + TQString automaticStyle; + if (it==m_mapTextStyleKeys.end()) + { + // We have not any match, so we need a new automatic text style + automaticStyle=makeAutomaticStyleName("T", m_automaticTextStyleNumber); + kdDebug(30518) << "Creating automatic text style: " << automaticStyle << " key: " << styleKey << endl; + m_mapTextStyleKeys[styleKey]=automaticStyle; + + m_contentAutomaticStyles += " <style:style"; + m_contentAutomaticStyles += " style:name=\"" + escapeOOText(automaticStyle) + "\""; + m_contentAutomaticStyles += " style:family=\"text\""; + m_contentAutomaticStyles += ">\n"; + m_contentAutomaticStyles += " <style:properties "; + m_contentAutomaticStyles += props; + m_contentAutomaticStyles += "/>\n"; + m_contentAutomaticStyles += " </style:style>\n"; + } + else + { + // We have a match, so use the already defined automatic text style + automaticStyle=it.data(); + kdDebug(30518) << "Using automatic text style: " << automaticStyle << " key: " << styleKey << endl; + } + + *m_streamOut << " text:style-name=\"" << escapeOOText(automaticStyle) << "\" "; + + *m_streamOut << ">" << partialText << "</text:span>"; + } +} + +void OOWriterWorker::processFootnote( const VariableData& variable ) +{ + // Footnote + const TQValueList<ParaData> *paraList = variable.getFootnotePara(); + if( paraList ) + { + const TQString value ( variable.getFootnoteValue() ); + //const bool automatic = formatData.variable.getFootnoteAuto(); + const bool flag = variable.getFootnoteType(); + + if ( flag ) + { + *m_streamOut << "<text:footnote text:id=\"ft"; + *m_streamOut << (++m_footnoteNumber); + *m_streamOut << "\">"; + *m_streamOut << "<text:footnote-citation>" << escapeOOText( value ) << "</text:footnote-citation>"; + *m_streamOut << "<text:footnote-body>\n"; + + doFullAllParagraphs( *paraList ); + + *m_streamOut << "\n</text:footnote-body>"; + *m_streamOut << "</text:footnote>"; + } + else + { + *m_streamOut << "<text:endnote text:id=\"ft"; + *m_streamOut << (++m_footnoteNumber); + *m_streamOut << "\">"; + *m_streamOut << "<text:endnote-citation>" << escapeOOText( value ) << "</text:endnote-citation>"; + *m_streamOut << "<text:endnote-body>\n"; + + doFullAllParagraphs( *paraList ); + + *m_streamOut << "\n</text:endnote-body>"; + *m_streamOut << "</text:endnote>"; + } + } +} + +void OOWriterWorker::processNote( const VariableData& variable ) +{ + // KWord 1.3's annotations are anonymous and undated, + // however the OO specification tells that author and date are mandatory (even if OOWriter 1.1 consider them optional) + + *m_streamOut << "<office:annotation office:create-date=\""; + + // We use the document creation date as creation date for the annotation + // (OOWriter uses only the date part, there is no time part) + if ( m_varSet.creationTime.isValid() ) + *m_streamOut << escapeOOText( m_varSet.creationTime.date().toString( Qt::ISODate ) ); + else + *m_streamOut << "1970-01-01"; + + *m_streamOut << "\" office:author=\""; + + // We try to use the document author's name as annotation author + if ( m_docInfo.fullName.isEmpty() ) + *m_streamOut << escapeOOText( i18n( "Pseudo-author for annotations", "KWord 1.3" ) ); + else + *m_streamOut << escapeOOText( m_docInfo.fullName ); + + *m_streamOut << "\">\n"; + *m_streamOut << "<text:p>" + << escapeOOSpan( variable.getGenericData( "note" ) ) + << "</text:p>\n" + << "</office:annotation>"; +} + +void OOWriterWorker::processVariable ( const TQString&, + const TextFormatting& /*formatLayout*/, + const FormatData& formatData) +{ + if (0==formatData.variable.m_type) + { + *m_streamOut << "<text:date/>"; // ### TODO: parameters + } + else if (2==formatData.variable.m_type) + { + *m_streamOut << "<text:time/>"; // ### TODO: parameters + } + else if (4==formatData.variable.m_type) + { + // ### TODO: the other under-types, other parameters + if (formatData.variable.isPageNumber()) + { + *m_streamOut << "<text:page-number text:select-page=\"current\"/>"; + } + else if (formatData.variable.isPageCount()) + { + *m_streamOut << "<text:page-count/>"; + } + else + { + // Unknown subtype, therefore write out the result + *m_streamOut << formatData.variable.m_text; + } + } + else if (9==formatData.variable.m_type) + { + // A link + *m_streamOut << "<text:a xlink:href=\"" + << escapeOOText(formatData.variable.getHrefName()) + << "\" xlink:type=\"simple\">" + << escapeOOText(formatData.variable.getLinkName()) + << "</text:a>"; + } + else if ( 10 == formatData.variable.m_type ) + { // Note (OOWriter: annotation) + processNote ( formatData.variable ); + } + else if (11==formatData.variable.m_type) + { + // Footnote + processFootnote ( formatData.variable ); + } + else + { + // Generic variable + *m_streamOut << formatData.variable.m_text; + } +} + +void OOWriterWorker::processAnchor ( const TQString&, + const TextFormatting& /*formatLayout*/, //TODO + const FormatData& formatData) +{ + // We have a picture or a table + if ( (2==formatData.frameAnchor.type) // <IMAGE> or <PICTURE> + || (5==formatData.frameAnchor.type) ) // <CLIPART> + { + makePicture( formatData.frameAnchor, AnchorInlined ); + } + else if (6==formatData.frameAnchor.type) + { + makeTable( formatData.frameAnchor, AnchorInlined ); + } + else + { + kdWarning(30518) << "Unsupported anchor type: " + << formatData.frameAnchor.type << endl; + } +} + +void OOWriterWorker::processTextImage ( const TQString&, + const TextFormatting& /*formatLayout*/, + const FormatData& formatData) +{ + kdDebug(30518) << "Text Image: " << formatData.frameAnchor.key.toString() << endl; + makePicture( formatData.frameAnchor, AnchorTextImage ); +} + +void OOWriterWorker::processParagraphData ( const TQString ¶Text, + const TextFormatting& formatLayout, + const ValueListFormatData ¶FormatDataList) +{ + if ( paraText.length () > 0 ) + { + ValueListFormatData::ConstIterator paraFormatDataIt; + + for ( paraFormatDataIt = paraFormatDataList.begin (); + paraFormatDataIt != paraFormatDataList.end (); + paraFormatDataIt++ ) + { + if (1==(*paraFormatDataIt).id) + { + processNormalText(paraText, formatLayout, (*paraFormatDataIt)); + } + else if (2==(*paraFormatDataIt).id) + { + processTextImage(paraText, formatLayout, (*paraFormatDataIt)); + } + else if ( 3 == (*paraFormatDataIt).id ) + { + // Just a (KWord 0.8) tab stop, nothing else to do! + *m_streamOut << "<text:tab-stop/>"; + } + else if (4==(*paraFormatDataIt).id) + { + processVariable(paraText, formatLayout, (*paraFormatDataIt)); + } + else if (6==(*paraFormatDataIt).id) + { + processAnchor(paraText, formatLayout, (*paraFormatDataIt)); + } + else if ( 1001 == (*paraFormatDataIt).id ) // Start of bookmark + { + *m_streamOut << "<text:bookmark-start text:name=\"" + << escapeOOText( (*paraFormatDataIt).variable.m_text ) + <<"\"/>"; + } + else if ( 1002 == (*paraFormatDataIt).id ) // End of bookmark + { + *m_streamOut << "<text:bookmark-end text:name=\"" + << escapeOOText( (*paraFormatDataIt).variable.m_text ) + <<"\"/>"; + } + } + } +} + +TQString OOWriterWorker::layoutToParagraphStyle(const LayoutData& layoutOrigin, + const LayoutData& layout, const bool force, TQString& styleKey) +{ + TQString props; // Props has to remain empty, if there is no difference. + + styleKey += layout.styleName; + styleKey += ','; + + if (force || (layoutOrigin.alignment!=layout.alignment)) + { + // NOTE: OO 1.0.x uses start and end like left and right (section 3.11.4) + // Unfortunately in XSL-FO's text-align, they are really supposed to be the start and the end. + if (layout.alignment == "left") + { + props += "fo:text-align=\"start\" "; + styleKey += 'L'; + } + else if (layout.alignment == "right") + { + props += "fo:text-align=\"end\" "; + styleKey += 'R'; + } + else if (layout.alignment == "center") + { + props += "fo:text-align=\"center\" "; + styleKey += 'C'; + } + else if (layout.alignment == "justify") + { + props += "fo:text-align=\"justify\" "; + styleKey += 'J'; + } + else if (layout.alignment == "auto") + { + props += "fo:text-align=\"start\" "; +#ifndef STRICT_OOWRITER_VERSION_1 + props += "style:text-auto-align=\"true\" "; // rejected draft OASIS extension +#endif + styleKey += 'A'; + } + else + { + kdWarning(30518) << "Unknown alignment: " << layout.alignment << endl; + } + } + + styleKey += ','; + + if ((layout.indentLeft>=0.0) + && (force || (layoutOrigin.indentLeft!=layout.indentLeft))) + { + props += TQString("fo:margin-left=\"%1pt\" ").arg(layout.indentLeft); + styleKey += TQString::number(layout.indentLeft); + } + + styleKey += ','; + + if ((layout.indentRight>=0.0) + && (force || (layoutOrigin.indentRight!=layout.indentRight))) + { + props += TQString("fo:margin-right=\"%1pt\" ").arg(layout.indentRight); + styleKey += TQString::number(layout.indentRight); + } + + styleKey += ','; + + if (force || (layoutOrigin.indentLeft!=layout.indentLeft)) + { + props += "fo:text-indent=\""; + props += TQString::number(layout.indentFirst); + props += "\" "; + styleKey += TQString::number(layout.indentFirst); + } + + styleKey += ','; + + if ((layout.marginBottom>=0.0) + && ( force || ( layoutOrigin.marginBottom != layout.marginBottom ) ) ) + { + props += TQString("fo:margin-bottom=\"%1pt\" ").arg(layout.marginBottom); + styleKey += TQString::number(layout.marginBottom); + } + + styleKey += ','; + + if ((layout.marginTop>=0.0) + && ( force || ( layoutOrigin.marginTop != layout.marginTop ) ) ) + { + props += TQString("fo:margin-top=\"%1pt\" ").arg(layout.marginTop); + styleKey += TQString::number(layout.marginTop); + } + + styleKey += ','; + + if (force + || ( layoutOrigin.lineSpacingType != layout.lineSpacingType ) + || ( layoutOrigin.lineSpacing != layout.lineSpacing ) ) + { + switch ( layout.lineSpacingType ) + { + case LayoutData::LS_CUSTOM: + { + // We have a custom line spacing (in points) + const TQString height ( TQString::number(layout.lineSpacing) ); // ### TODO: rounding? + props += "style:line-spacing=\""; + props += height; + props += "pt\" "; + styleKey += height; + styleKey += 'C'; + break; + } + case LayoutData::LS_SINGLE: + { + props += "fo:line-height=\"normal\" "; // One + styleKey += "100%"; // One + break; + } + case LayoutData::LS_ONEANDHALF: + { + props += "fo:line-height=\"150%\" "; // One-and-half + styleKey += "150%"; + break; + } + case LayoutData::LS_DOUBLE: + { + props += "fo:line-height=\"200%\" "; // Two + styleKey += "200%"; + break; + } + case LayoutData::LS_MULTIPLE: + { + // OOWriter 1.1 only allows up to 200% + const TQString mult ( TQString::number( tqRound( layout.lineSpacing * 100 ) ) ); + props += "fo:line-height=\""; + props += mult; + props += "%\" "; + styleKey += mult; + styleKey += "%"; + break; + } + case LayoutData::LS_FIXED: + { + // We have a fixed line height (in points) + const TQString height ( TQString::number(layout.lineSpacing) ); // ### TODO: rounding? + props += "fo:line-height=\""; + props += height; + props += "pt\" "; + styleKey += height; + styleKey += 'F'; + break; + } + case LayoutData::LS_ATLEAST: + { + // We have a at-least line height (in points) + const TQString height ( TQString::number(layout.lineSpacing) ); // ### TODO: rounding? + props += "style:line-height-at-least=\""; + props += height; + props += "pt\" "; + styleKey += height; + styleKey += 'A'; + break; + } + default: + { + kdWarning(30518) << "Unsupported lineSpacingType: " << layout.lineSpacingType << " (Ignoring!)" << endl; + break; + } + } + } + + styleKey += ','; + + if ( layout.pageBreakBefore ) + { + // We have a page break before the paragraph + props += "fo:page-break-before=\"page\" "; + styleKey += 'B'; + } + + styleKey += ','; + + if ( layout.pageBreakAfter ) + { + // We have a page break after the paragraph + props += "fo:page-break-after=\"page\" "; + styleKey += 'A'; + } + + styleKey += '@'; // A more visible seperator + + props += textFormatToStyle(layoutOrigin.formatData.text,layout.formatData.text,force,styleKey); + + props += ">"; + + styleKey += '@'; // A more visible seperator + + // ### TODO/FIXME: what if all tabulators must be erased? + if (!layout.tabulatorList.isEmpty() + && (force || (layoutOrigin.tabulatorList!=layout.tabulatorList) )) + { + props += "\n <style:tab-stops>\n"; + TabulatorList::ConstIterator it; + TabulatorList::ConstIterator end(layout.tabulatorList.end()); + for (it=layout.tabulatorList.begin();it!=end;++it) + { + props+=" <style:tab-stop style:position=\""; + props += TQString::number((*it).m_ptpos); + props += "pt\""; + styleKey += TQString::number((*it).m_ptpos); + switch ((*it).m_type) + { + case 0: props += " style:type=\"left\""; styleKey += "L"; break; + case 1: props += " style:type=\"center\""; styleKey += "C"; break; + case 2: props += " style:type=\"right\""; styleKey += "R"; break; + case 3: props += " style:type=\"char\" style:char=\".\""; styleKey += "D"; break; // decimal + default: props += " style:type=\"left\""; styleKey += "L"; break; + } + switch ((*it).m_filling) // ### TODO: check if the characters are right + { + case TabulatorData::TF_NONE: break; + case TabulatorData::TF_DOT: props += " style:leader-char=\".\""; break; + case TabulatorData::TF_LINE: props += " style:leader-char=\"_\""; break; + + case TabulatorData::TF_DASH: + case TabulatorData::TF_DASHDOT: + case TabulatorData::TF_DASHDOTDOT: props += " style:leader-char=\"-\""; break; + + default: break; + } + props += "/>\n"; + styleKey +='/'; + } + props += " </style:tab-stops>\n "; + } + + return props; +} + +bool OOWriterWorker::doFullParagraph(const TQString& paraText, const LayoutData& layout, + const ValueListFormatData& paraFormatDataList) +{ + const bool header = ( (layout.counter.numbering == CounterData::NUM_CHAPTER) + && (layout.counter.depth<10) ); // ### TODO: Does OOWriter really limits to 10? + + if (header) + { + *m_streamOut << " <text:h text:level=\""; + *m_streamOut << TQString::number(layout.counter.depth+1,10); + *m_streamOut << "\" "; + } + else + *m_streamOut << " <text:p "; + + const LayoutData& styleLayout=m_styleMap[layout.styleName]; + + TQString styleKey; + const TQString props(layoutToParagraphStyle(styleLayout,layout,false,styleKey)); + + TQString actualStyle(layout.styleName); + if (!props.isEmpty()) + { + TQMap<TQString,TQString>::ConstIterator it ( m_mapParaStyleKeys.find(styleKey) ); + kdDebug(30518) << "Searching paragraph key: " << styleKey << endl; + + TQString automaticStyle; + + if (it==m_mapParaStyleKeys.end()) + { + // We have additional properties, so we need an automatic style for the paragraph + automaticStyle = makeAutomaticStyleName("P", m_automaticParagraphStyleNumber); + kdDebug(30518) << "Creating automatic paragraph style: " << automaticStyle << " key: " << styleKey << endl; + m_mapParaStyleKeys[styleKey]=automaticStyle; + + m_contentAutomaticStyles += " <style:style"; + m_contentAutomaticStyles += " style:name=\"" + escapeOOText(automaticStyle) + "\""; + m_contentAutomaticStyles += " style:parent-style-name=\"" + escapeOOText(layout.styleName) + "\""; + m_contentAutomaticStyles += " style:family=\"paragraph\" style:class=\"text\""; + m_contentAutomaticStyles += ">\n"; + m_contentAutomaticStyles += " <style:properties "; + m_contentAutomaticStyles += props; + m_contentAutomaticStyles += "</style:properties>\n"; + m_contentAutomaticStyles += " </style:style>\n"; + } + else + { + // We have a match, so use the already defined automatic paragraph style + automaticStyle=it.data(); + kdDebug(30518) << "Using automatic paragraph style: " << automaticStyle << " key: " << styleKey << endl; + } + + actualStyle=automaticStyle; + } + + if (!actualStyle.isEmpty()) + { + *m_streamOut << "text:style-name=\"" << escapeOOText(actualStyle) << "\" "; + } + else + { // SHould not happen + kdWarning(30518) << "No style for a paragraph!" << endl; + } + + *m_streamOut << ">"; + + processParagraphData(paraText, layout.formatData.text, paraFormatDataList); + + if (header) + *m_streamOut << "</text:h>\n"; + else + *m_streamOut << "</text:p>\n"; + + return true; +} + +bool OOWriterWorker::doOpenStyles(void) +{ + m_styles += " <office:styles>\n"; + m_styles += " <style:style style:name=\"Graphics\" style:family=\"graphics\">\n"; // ### TODO: what if Graphics is a normal style + m_styles += " <style:properties text:anchor-type=\"paragraph\" style:wrap=\"none\"/>\n"; + m_styles += " </style:style>\n"; + m_styles += " <style:style style:name=\"Frame\" style:family=\"graphics\">\n"; // ### TODO: what if Frame is a normal style + m_styles += " <style:properties text:anchor-type=\"paragraph\" style:wrap=\"none\"/>\n"; + m_styles += " </style:style>\n"; + return true; +} + +bool OOWriterWorker::doFullDefineStyle(LayoutData& layout) +{ + //Register style in the style map + m_styleMap[layout.styleName]=layout; + + m_styles += " <style:style"; + + m_styles += " style:name=\"" + escapeOOText( layout.styleName ) + "\""; + m_styles += " style:next-style-name=\"" + escapeOOText( layout.styleFollowing ) + "\""; + m_styles += " style:family=\"paragraph\" style:class=\"text\""; + m_styles += ">\n"; + m_styles += " <style:properties "; + + TQString debugKey; // Not needed + m_styles += layoutToParagraphStyle(layout,layout,true,debugKey); + kdDebug(30518) << "Defining style: " << debugKey << endl; + + m_styles += "</style:properties>\n"; + m_styles += " </style:style>\n"; + + return true; +} + +bool OOWriterWorker::doCloseStyles(void) +{ + m_styles += " </office:styles>\n"; + return true; +} + +bool OOWriterWorker::doFullPaperFormat(const int format, + const double width, const double height, const int orientation) +{ + if ( ( format < 0 ) // Be careful that 0 is ISO A3 + || ( width < 1.0 ) + || ( height < 1.0 ) ) + { + kdWarning(30518) << "Page size problem: format: " << format << " width: " << width << " height: " << height << endl; + // Something is wrong with the page size + KoFormat newFormat = KoFormat ( format ); + if ( ( format < 0 ) || ( format > PG_LAST_FORMAT ) ) + { + // Bad or unknown format, so assume ISO A4 + newFormat = PG_DIN_A4; + } + m_paperWidth = KoPageFormat::width ( newFormat, KoOrientation( orientation ) ) * 72.0 / 25.4 ; + m_paperHeight = KoPageFormat::height ( newFormat, KoOrientation( orientation ) ) * 72.0 / 25.4 ; + m_paperFormat = newFormat; + } + else + { + m_paperFormat=format; + m_paperWidth=width; + m_paperHeight=height; + } + m_paperOrientation=orientation; // ### TODO: check if OOWriter needs the orignal size (without landscape) or the real size + return true; +} + +bool OOWriterWorker::doFullPaperBorders (const double top, const double left, + const double bottom, const double right) +{ + m_paperBorderTop=top; + m_paperBorderLeft=left; + m_paperBorderBottom=bottom; + m_paperBorderRight=right; + return true; +} + +bool OOWriterWorker::doFullPaperFormatOther ( const int columns, const double columnspacing, const int numPages ) +{ + m_columns = columns; + m_columnspacing = columnspacing; + m_numPages = numPages; + return true; +} + +bool OOWriterWorker::doFullDocumentInfo(const KWEFDocumentInfo& docInfo) +{ + m_docInfo=docInfo; + + return true; +} + +bool OOWriterWorker::doVariableSettings(const VariableSettingsData& vs) +{ + m_varSet=vs; + + return true; +} + +bool OOWriterWorker::doDeclareNonInlinedFramesets( TQValueList<FrameAnchor>& pictureAnchors, TQValueList<FrameAnchor>& tableAnchors ) +{ + m_nonInlinedPictureAnchors = pictureAnchors; + m_nonInlinedTableAnchors = tableAnchors; + return true; +} + +void OOWriterWorker::declareFont(const TQString& fontName) +{ + if (fontName.isEmpty()) + return; + + if (m_fontNames.find(fontName)==m_fontNames.end()) + { + TQString props; + + // Disabled, as TQFontInfo::styleHint() cannot guess +#if 0 + TQFont font(fontName); + TQFontInfo info(font); + props+="style:font-family-generic=\"" + switch (info.styleHint()) + { + case TQFont::SansSerif: + default: + { + props += "swiss"; + break; + } + case TQFont::Serif: + { + props += "roman"; + break; + } + case TQFont::Courier: + { + props += "modern"; + break; + } + case TQFont::OldEnglish: + { + props += "decorative"; + break; + } + } + props +="\" "; +#endif + + props +="style:font-pitch=\"variable\""; // ### TODO: check if font is variable or fixed + // New font, so register it + m_fontNames[fontName]=props; + } +} + +TQString OOWriterWorker::makeAutomaticStyleName(const TQString& prefix, ulong& counter) const +{ + const TQString str (prefix + TQString::number(++counter,10)); + + // Checks if the automatic style has not the same name as a user one. + // If it is the case, change it! + + if (m_styleMap.find(str)==m_styleMap.end()) + return str; // Unique, so let's go! + + TQString str2(str+"_bis"); + if (m_styleMap.find(str2)==m_styleMap.end()) + return str2; + + str2 = str+"_ter"; + if (m_styleMap.find(str2)==m_styleMap.end()) + return str2; + + // If it is still not unique, try a time stamp. + const TQDateTime dt(TQDateTime::currentDateTime(Qt::UTC)); + + str2 = str + "_" + TQString::number(dt.toTime_t(),16); + if (m_styleMap.find(str2)==m_styleMap.end()) + return str2; + + kdWarning(30518) << "Could not make an unique style name: " << str2 << endl; + return str2; // Still return, as we have nothing better +} + |