filters

ExportFilter.cc

00001 /* This file is part of the KDE project
00002    Copyright (C) 2001, 2002, 2003, 2004 Nicolas GOUTTE <goutte@kde.org>
00003 
00004    This library is free software; you can redistribute it and/or
00005    modify it under the terms of the GNU Library General Public
00006    License as published by the Free Software Foundation; either
00007    version 2 of the License, or (at your option) any later version.
00008 
00009    This library is distributed in the hope that it will be useful,
00010    but WITHOUT ANY WARRANTY; without even the implied warranty of
00011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012    Library General Public License for more details.
00013 
00014    You should have received a copy of the GNU Library General Public License
00015    along with this library; see the file COPYING.LIB.  If not, write to
00016    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00017  * Boston, MA 02110-1301, USA.
00018 */
00019 
00020 /*
00021    This file is based on the old file:
00022     /home/kde/koffice/filters/kword/ascii/asciiexport.cc
00023 
00024    The old file was copyrighted by
00025     Copyright (C) 1998, 1999 Reginald Stadlbauer <reggie@kde.org>
00026     Copyright (c) 2000 ID-PRO Deutschland GmbH. All rights reserved.
00027                        Contact: Wolf-Michael Bolle <Bolle@ID-PRO.de>
00028 
00029    The old file was licensed under the terms of the GNU Library General Public
00030    License version 2.
00031 */
00032 
00033 #include <qmap.h>
00034 #include <qiodevice.h>
00035 #include <qbuffer.h>
00036 #include <qtextstream.h>
00037 #include <qdom.h>
00038 
00039 #include <kdebug.h>
00040 #include <klocale.h>
00041 #include <kzip.h>
00042 
00043 #include <KoPageLayout.h>
00044 #include <KoPictureKey.h>
00045 #include <KoPicture.h>
00046 
00047 #include <KWEFStructures.h>
00048 #include <KWEFUtil.h>
00049 #include <KWEFBaseWorker.h>
00050 #include <KWEFKWordLeader.h>
00051 
00052 #include "ExportFilter.h"
00053 
00054 OOWriterWorker::OOWriterWorker(void) : m_streamOut(NULL),
00055     m_paperBorderTop(0.0),m_paperBorderLeft(0.0),
00056     m_paperBorderBottom(0.0),m_paperBorderRight(0.0), m_zip(NULL), m_pictureNumber(0),
00057     m_automaticParagraphStyleNumber(0), m_automaticTextStyleNumber(0),
00058     m_footnoteNumber(0), m_tableNumber(0), m_textBoxNumber( 0 ),
00059     m_columnspacing( 36.0 ), m_columns( 1 )
00060 {
00061 }
00062 
00063 QString OOWriterWorker::escapeOOText(const QString& strText) const
00064 {
00065     // Escape quotes (needed in attributes)
00066     // Escape apostrophs (allowed by XML)
00067     return KWEFUtil::EscapeSgmlText(NULL,strText,true,true);
00068 }
00069 
00070 QString OOWriterWorker::escapeOOSpan(const QString& strText) const
00071 // We need not only to escape the classical XML stuff but also to take care of spaces and tabs.
00072 {
00073     QString strReturn;
00074     QChar ch;
00075     int spaceNumber = 0; // How many spaces should be written
00076     
00077     for (uint i=0; i<strText.length(); i++)
00078     {
00079         ch=strText[i];
00080 
00081         if (ch!=' ')
00082         {
00083             // The next character is not a space (anymore)
00084             if ( spaceNumber > 0 )
00085             {
00086                 strReturn += ' ';
00087                 --spaceNumber;
00088                 if ( spaceNumber > 0 )
00089                 {
00090                     strReturn += "<text:s text:c=\"";
00091                     strReturn += QString::number( spaceNumber );
00092                     strReturn += "\"/>";
00093                 }
00094                 spaceNumber = 0;
00095             }
00096         }
00097 
00098         // ### TODO: would be switch/case or if/elseif the best?
00099         switch (ch.unicode())
00100         {
00101         case 9: // Tab
00102             {
00103                 strReturn+="<text:tab-stop/>";
00104                 break;
00105             }
00106         case 10: // Line-feed
00107             {
00108                 strReturn+="<text:line-break/>";
00109                 break;
00110             }
00111         case 32: // Space
00112             {
00113                 if ( spaceNumber > 0 )
00114                 {
00115                     ++spaceNumber;
00116                 }
00117                 else
00118                 {
00119                     spaceNumber = 1;
00120                 }
00121                 break;
00122             }
00123         case 38: // &
00124             {
00125                 strReturn+="&amp;";
00126                 break;
00127             }
00128         case 60: // <
00129             {
00130                 strReturn+="&lt;";
00131                 break;
00132             }
00133         case 62: // >
00134             {
00135                 strReturn+="&gt;";
00136                 break;
00137             }
00138         case 34: // "
00139             {
00140                 strReturn+="&quot;";
00141                 break;
00142             }
00143         case 39: // '
00144             {
00145                 strReturn+="&apos;";
00146                 break;
00147             }
00148         case 1: // (Non-XML-compatible) replacement character from KWord 0.8
00149             {
00150                 strReturn += '#'; //use KWord 1.[123] replacement character instead
00151                 break;
00152             }
00153         // Following characters are not allowed in XML (but some files from KWord 0.8 have some of them.)
00154         case  0: 
00155         case  2:
00156         case  3:
00157         case  4:
00158         case  5:
00159         case  6:
00160         case  7:
00161         case  8:
00162         case 11: 
00163         case 12: 
00164         case 14:
00165         case 15:
00166         case 16:
00167         case 17:
00168         case 18:
00169         case 19:
00170         case 20:
00171         case 21:
00172         case 22:
00173         case 23:
00174         case 24:
00175         case 25:
00176         case 26:
00177         case 27:
00178         case 28:
00179         case 29:
00180         case 30:
00181         case 31:
00182             {
00183                 kdWarning(30518) << "Not allowed XML character: " << ch.unicode() << endl;
00184                 strReturn += '?';
00185                 break;
00186             }
00187         case 13: // ### TODO: what to do with it?
00188         default:
00189             {
00190                 strReturn+=ch;
00191                 break;
00192             }
00193         }
00194     }
00195 
00196     if ( spaceNumber > 0 )
00197     {
00198         // The last characters were spaces
00199         strReturn += ' ';
00200         --spaceNumber;
00201         if ( spaceNumber > 0 )
00202         {
00203             strReturn += "<text:s text:c=\"";
00204             strReturn += QString::number( spaceNumber );
00205             strReturn += "\"/>";
00206         }
00207         spaceNumber = 0;
00208     }
00209 
00210     return strReturn;
00211 }
00212 
00213 bool OOWriterWorker::doOpenFile(const QString& filenameOut, const QString& )
00214 {
00215     kdDebug(30518) << "Opening file: " << filenameOut
00216         << " (in OOWriterWorker::doOpenFile)" << endl;
00217 
00218     m_zip=new KZip(filenameOut); // How to check failure?
00219 
00220     if (!m_zip->open(IO_WriteOnly))
00221     {
00222         kdError(30518) << "Could not open ZIP file for writing! Aborting!" << endl;
00223         delete m_zip;
00224         m_zip=NULL;
00225         return false;
00226     }
00227 
00228     m_zip->setCompression( KZip::NoCompression );
00229     m_zip->setExtraField( KZip::NoExtraField );
00230 
00231     const QCString appId( "application/vnd.sun.xml.writer" );
00232 
00233     m_zip->writeFile( "mimetype", QString::null, QString::null, appId.length(), appId.data() );
00234 
00235     m_zip->setCompression( KZip::DeflateCompression );
00236 
00237     m_streamOut=new QTextStream(m_contentBody, IO_WriteOnly);
00238 
00239     m_streamOut->setEncoding( QTextStream::UnicodeUTF8 );
00240 
00241     return true;
00242 }
00243 
00244 bool OOWriterWorker::zipPrepareWriting(const QString& name)
00245 {
00246     if (!m_zip)
00247         return false;
00248     m_size=0;
00249     return m_zip->prepareWriting(name, QString::null, QString::null, 0);
00250 }
00251 
00252 bool OOWriterWorker::zipDoneWriting(void)
00253 {
00254     if (!m_zip)
00255         return false;
00256     return m_zip->doneWriting(m_size);
00257 }
00258 
00259 bool OOWriterWorker::zipWriteData(const char* str)
00260 {
00261     if (!m_zip)
00262         return false;
00263     const uint size=strlen(str);
00264     m_size+=size;
00265     return m_zip->writeData(str,size);
00266 }
00267 
00268 bool OOWriterWorker::zipWriteData(const QByteArray& array)
00269 {
00270     if (!m_zip)
00271         return false;
00272     const uint size=array.size();
00273     m_size+=size;
00274     return m_zip->writeData(array.data(),size);
00275 }
00276 
00277 bool OOWriterWorker::zipWriteData(const QCString& cstr)
00278 {
00279     if (!m_zip)
00280         return false;
00281     const uint size=cstr.length();
00282     m_size+=size;
00283     return m_zip->writeData(cstr.data(),size);
00284 }
00285 
00286 bool OOWriterWorker::zipWriteData(const QString& str)
00287 {
00288     return zipWriteData(str.utf8());
00289 }
00290 
00291 void OOWriterWorker::writeStartOfFile(const QString& type)
00292 {
00293     const bool noType=type.isEmpty();
00294     zipWriteData("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
00295 
00296     zipWriteData("<!DOCTYPE office:document");
00297     if (!noType)
00298     {
00299         // No type might happen for raw XML documents (which this filter does not support yet.)
00300         zipWriteData("-");
00301         zipWriteData(type);
00302     }
00303     zipWriteData(" PUBLIC \"-//OpenOffice.org//DTD OfficeDocument 1.0//EN\"");
00304     zipWriteData(" \"office.dtd\"");
00305     zipWriteData(">\n");
00306 
00307     zipWriteData("<office:document");
00308     if (!noType)
00309     {
00310         zipWriteData("-");
00311         zipWriteData(type);
00312     }
00313 
00314     // The name spaces used by OOWriter (those not used by this filter are commented out)
00315 
00316     // General namespaces
00317     zipWriteData(" xmlns:office=\"http://openoffice.org/2000/office\"");
00318     zipWriteData(" xmlns:xlink=\"http://www.w3.org/1999/xlink\"");
00319 
00320     // Namespaces for context.xml and style.xml
00321     if ( type == "content" || type == "styles" || type.isEmpty() )
00322     {
00323         zipWriteData(" xmlns:style=\"http://openoffice.org/2000/style\"");
00324         zipWriteData(" xmlns:text=\"http://openoffice.org/2000/text\"");
00325         zipWriteData(" xmlns:table=\"http://openoffice.org/2000/table\"");
00326         zipWriteData(" xmlns:draw=\"http://openoffice.org/2000/drawing\"");
00327         zipWriteData(" xmlns:fo=\"http://www.w3.org/1999/XSL/Format\"");
00328 
00329         //zipWriteData(" xmlns:number=\"http://openoffice.org/2000/datastyle\"");
00330         zipWriteData(" xmlns:svg=\"http://www.w3.org/2000/svg\"");
00331         //zipWriteData(" xmlns:chart=\"http://openoffice.org/2000/chart\"");
00332         //zipWriteData(" xmlns:dr3d=\"http://openoffice.org/2000/dr3d\"");
00333         //zipWriteData(" xmlns:math=\"http://www.w3.org/1998/Math/MathML"");
00334         //zipWriteData(" xmlns:form=\"http://openoffice.org/2000/form\"");
00335         //zipWriteData(" xmlns:script=\"http://openoffice.org/2000/script\"");
00336     }
00337 
00338     // Namespaces For meta.xml
00339     if ( type == "meta" || type.isEmpty() )
00340     {
00341         zipWriteData(" xmlns:dc=\"http://purl.org/dc/elements/1.1/\"");
00342         zipWriteData(" xmlns:meta=\"http://openoffice.org/2000/meta\"");
00343     }
00344 
00345     zipWriteData(" office:class=\"text\"");
00346 
00347  
00348 #ifdef STRICT_OOWRITER_VERSION_1
00349     zipWriteData(" office:version=\"1.0\"");
00350 #else
00351     // We are using an (rejected draft OASIS) extension compared to version 1.0, so we cannot write the version string.
00352     // (We do not even write it for context.xml and meta.xml, as OOWriter 1.0.1 does not like it in this case.)
00353 #endif
00354 
00355     zipWriteData(">\n");
00356 }
00357 
00358 void OOWriterWorker::writeFontDeclaration(void)
00359 {
00360     zipWriteData( " <office:font-decls>\n");
00361     QMap<QString,QString>::ConstIterator end(m_fontNames.end());
00362     for (QMap<QString,QString>::ConstIterator it=m_fontNames.begin(); it!=end; ++it)
00363     {
00364         const bool space=(it.key().find(' ')>=0); // Does the font has at least a space in its name
00365         const QString fontName(escapeOOText(it.key()));
00366         zipWriteData("  <style:font-decl style:name=\"");
00367         zipWriteData(fontName);
00368         zipWriteData("\" fo:font-family=\"");
00369         if (space)
00370         { // It has a space, so (simple) quote it
00371             zipWriteData("&apos;");
00372             zipWriteData(fontName);
00373             zipWriteData("&apos;");
00374         }
00375         else
00376         { // The font has no space in its name, so it can be written normally.
00377             zipWriteData(fontName);
00378         }
00379         zipWriteData("\" ");
00380         zipWriteData(it.data()); // already in XML, so do not escape
00381         zipWriteData(" />\n");
00382     }
00383     zipWriteData(" </office:font-decls>\n");
00384 }
00385 
00386 void OOWriterWorker::writeStylesXml(void)
00387 {
00388     if (!m_zip)
00389         return;
00390 
00391     zipPrepareWriting("styles.xml");
00392 
00393     writeStartOfFile("styles");
00394 
00395     writeFontDeclaration();
00396 
00397     zipWriteData(m_styles);
00398 
00399     zipWriteData(" <office:automatic-styles>\n");
00400     zipWriteData("  <style:page-master style:name=\"pm1\">\n"); // ### TODO: verify if style name is unique
00401 
00402     zipWriteData("   <style:properties ");
00403     zipWriteData( " style:page-usage=\"all\"" ); // ### TODO: check
00404 
00405     zipWriteData(" fo:page-width=\"");
00406     zipWriteData(QString::number(m_paperWidth));
00407     zipWriteData("pt\" fo:page-height=\"");
00408     zipWriteData(QString::number(m_paperHeight));
00409     zipWriteData("pt\" ");
00410 
00411     zipWriteData("style:print-orientation=\"");
00412     if (1==m_paperOrientation)
00413     {
00414         zipWriteData("landscape");
00415     }
00416     else
00417     {
00418         zipWriteData("portrait");
00419     }
00420 
00421     zipWriteData("\" fo:margin-top=\"");
00422     zipWriteData(QString::number(m_paperBorderTop));
00423     zipWriteData("pt\" fo:margin-bottom=\"");
00424     zipWriteData(QString::number(m_paperBorderBottom));
00425     zipWriteData("pt\" fo:margin-left=\"");
00426     zipWriteData(QString::number(m_paperBorderLeft));
00427     zipWriteData("pt\" fo:margin-right=\"");
00428     zipWriteData(QString::number(m_paperBorderRight));
00429     zipWriteData("pt\" style:first-page-number=\"");
00430     zipWriteData(QString::number(m_varSet.startingPageNumber));
00431     zipWriteData( "\">\n" );
00432 
00433     if ( m_columns > 1 )
00434     {
00435         zipWriteData( "    <style:columns" );
00436         zipWriteData( " fo:column-count=\"" );
00437         zipWriteData( QString::number( m_columns ) );
00438         zipWriteData( "\" fo:column-gap=\"" );
00439         zipWriteData( QString::number( m_columnspacing ) );
00440         zipWriteData( "pt\">\n" );
00441 
00442         for (int i=0; i < m_columns; ++i)
00443         {
00444             zipWriteData( "     <style:column style:rel-width=\"1*\" fo:margin-left=\"0cm\" fo:margin-right=\"0cm\"/>\n" );
00445         }
00446 
00447         zipWriteData( "    </style:columns>\n" );
00448     }
00449 
00450     zipWriteData("   </style:properties>\n");
00451 
00452     zipWriteData("  </style:page-master>\n");
00453     zipWriteData(" </office:automatic-styles>\n");
00454 
00455     zipWriteData(" <office:master-styles>\n");
00456     zipWriteData("  <style:master-page style:name=\"Standard\" style:page-master-name=\"pm1\" />\n");
00457     zipWriteData(" </office:master-styles>\n");
00458 
00459     zipWriteData( "</office:document-styles>\n" );
00460 
00461     zipDoneWriting();
00462 }
00463 
00464 void OOWriterWorker::writeContentXml(void)
00465 {
00466     if (!m_zip)
00467         return;
00468 
00469     zipPrepareWriting("content.xml");
00470 
00471     writeStartOfFile("content");
00472 
00473     writeFontDeclaration();
00474 
00475     zipWriteData(" <office:automatic-styles>\n");
00476     zipWriteData(m_contentAutomaticStyles);
00477     m_contentAutomaticStyles = QString::null; // Release memory
00478     
00479     zipWriteData(" </office:automatic-styles>\n");
00480 
00481     zipWriteData(m_contentBody);
00482     m_contentBody.resize( 0 ); // Release memory
00483 
00484     zipWriteData( "</office:document-content>\n" );
00485 
00486     zipDoneWriting();
00487 }
00488 
00489 void OOWriterWorker::writeMetaXml(void)
00490 {
00491     if (!m_zip)
00492         return;
00493 
00494     zipPrepareWriting("meta.xml");
00495 
00496     writeStartOfFile("meta");
00497 
00498     zipWriteData(" <office:meta>\n");
00499 
00500     // Tell who we are in case that we have a bug in our filter output!
00501     zipWriteData("  <meta:generator>KWord's OOWriter Export Filter");
00502     zipWriteData(QString("$Revision: 515673 $").mid(10).remove('$')); // has a leading and a trailing space.
00503 
00504     zipWriteData("</meta:generator>\n");
00505 
00506     if (!m_docInfo.title.isEmpty())
00507     {
00508         zipWriteData("  <dc:title>");
00509         zipWriteData(escapeOOText(m_docInfo.title));
00510         zipWriteData("</dc:title>\n");
00511     }
00512     if (!m_docInfo.abstract.isEmpty())
00513     {
00514         zipWriteData("  <dc:description>");
00515         zipWriteData(escapeOOText(m_docInfo.abstract));
00516         zipWriteData("</dc:description>\n");
00517     }
00518 
00519     if (m_varSet.creationTime.isValid())
00520     {
00521         zipWriteData("  <meta:creation-date>");
00522         zipWriteData(escapeOOText(m_varSet.creationTime.toString(Qt::ISODate)));
00523         zipWriteData("</meta:creation-date>\n");
00524     }
00525 
00526     if (m_varSet.modificationTime.isValid())
00527     {
00528         zipWriteData("  <dc:date>");
00529         zipWriteData(escapeOOText(m_varSet.modificationTime.toString(Qt::ISODate)));
00530         zipWriteData("</dc:date>\n");
00531     }
00532 
00533     if (m_varSet.printTime.isValid())
00534     {
00535         zipWriteData("  <meta:print-date>");
00536         zipWriteData(escapeOOText(m_varSet.printTime.toString(Qt::ISODate)));
00537         zipWriteData("</meta:print-date>\n");
00538     }
00539 
00540     zipWriteData( "  <meta:document-statistic" );
00541 
00542     // KWord files coming from import filters mostly do not have no page count
00543     if ( m_numPages > 0 )
00544     {
00545         zipWriteData( " meta:page-count=\"" );
00546         zipWriteData( QString::number ( m_numPages ) );
00547         zipWriteData( "\"" );
00548     }
00549 
00550     zipWriteData( " meta:image-count=\"" ); // This is not specified in the OO specification section 2.1.19
00551     zipWriteData( QString::number ( m_pictureNumber ) );
00552     zipWriteData( "\"" );
00553 
00554     zipWriteData( " meta:table-count=\"" );
00555     zipWriteData( QString::number ( m_tableNumber ) );
00556     zipWriteData( "\"" );
00557 
00558     zipWriteData( "/>\n" ); // meta:document-statistic
00559     
00560     zipWriteData(" </office:meta>\n");
00561     zipWriteData("</office:document-meta>\n");
00562 
00563     zipDoneWriting();
00564 }
00565 
00566 bool OOWriterWorker::doCloseFile(void)
00567 {
00568     kdDebug(30518)<< "OOWriterWorker::doCloseFile" << endl;
00569     if (m_zip)
00570     {
00571         writeContentXml();
00572         writeMetaXml();
00573         writeStylesXml();
00574         m_zip->close();
00575     }
00576 
00577     delete m_zip;
00578     m_zip=NULL;
00579     return true;
00580 }
00581 
00582 bool OOWriterWorker::doOpenDocument(void)
00583 {
00584     kdDebug(30518)<< "OOWriterWorker::doOpenDocument" << endl;
00585 
00586     *m_streamOut << " <office:body>\n";
00587 
00588     return true;
00589 }
00590 
00591 bool OOWriterWorker::doCloseDocument(void)
00592 {
00593     *m_streamOut << " </office:body>\n";
00594     return true;
00595 }
00596 
00597 bool OOWriterWorker::doOpenBody(void)
00598 {
00599     QValueList<FrameAnchor>::Iterator it;
00600 
00601     // We have to process all non-inline pictures
00602     kdDebug(30518) << "=== Processing non-inlined pictures ===" << endl;
00603     for ( it = m_nonInlinedPictureAnchors.begin(); it != m_nonInlinedPictureAnchors.end(); ++it )
00604     {
00605         *m_streamOut << "  ";
00606         makePicture( *it, AnchorNonInlined );
00607         *m_streamOut << "\n";
00608     }
00609     kdDebug(30518) << "=== Non-inlined pictures processed ===" << endl;
00610 
00611     // We have to process all non-inline tables
00612     kdDebug(30518) << "=== Processing non-inlined tables ===" << endl;
00613     for ( it = m_nonInlinedTableAnchors.begin(); it != m_nonInlinedTableAnchors.end(); ++it )
00614     {
00615         *m_streamOut << "  ";
00616         makeTable( *it, AnchorNonInlined );
00617         *m_streamOut << "\n";
00618     }
00619     kdDebug(30518) << "=== Non-inlined tables processed ===" << endl;
00620 
00621     return true;
00622 }
00623 
00624 QString OOWriterWorker::textFormatToStyle(const TextFormatting& formatOrigin,
00625     const TextFormatting& formatData, const bool force, QString& key)
00626 {
00627     // TODO: rename variable formatData
00628     QString strElement; // TODO: rename this variable
00629 
00630     // Font name
00631     QString fontName = formatData.fontName;
00632     declareFont(fontName);
00633     if ( !fontName.isEmpty()
00634         && (force || (formatOrigin.fontName!=formatData.fontName)))
00635     {
00636         strElement+="style:font-name=\"";
00637         strElement+= escapeOOText(fontName);
00638         strElement+="\" ";
00639         key += fontName;
00640     }
00641 
00642     key += ",";
00643 
00644     if (force || (formatOrigin.italic!=formatData.italic))
00645     {
00646         // Font style
00647         strElement+="fo:font-style=\"";
00648         if ( formatData.italic )
00649         {
00650             strElement+="italic";
00651             key+='I';
00652         }
00653         else
00654         {
00655             strElement+="normal";
00656             key+='N';
00657         }
00658         strElement+="\" ";
00659     }
00660 
00661     key += ",";
00662 
00663     if (force || ((formatOrigin.weight>=75)!=(formatData.weight>=75)))
00664     {
00665         strElement+="fo:font-weight=\"";
00666         if ( formatData.weight >= 75 )
00667         {
00668             strElement+="bold";
00669             key+='B';
00670         }
00671         else
00672         {
00673             strElement+="normal";
00674             key+='N';
00675         }
00676         strElement+="\" ";
00677     }
00678 
00679     key += ",";
00680 
00681     if (force || (formatOrigin.fontSize!=formatData.fontSize))
00682     {
00683         const int size=formatData.fontSize;
00684         if (size>0)
00685         {
00686             strElement+="fo:font-size=\"";
00687             strElement+=QString::number(size,10);
00688             strElement+="pt\" ";
00689             key+=QString::number(size,10);
00690         }
00691     }
00692 
00693     key += ",";
00694 
00695     if (force || (formatOrigin.fgColor!=formatData.fgColor))
00696     {
00697         if ( formatData.fgColor.isValid() )
00698         {
00699             strElement+="fo:color=\"";
00700             strElement+=formatData.fgColor.name();
00701             strElement+="\" ";
00702             key+=formatData.fgColor.name();
00703         }
00704     }
00705 
00706     key += ",";
00707 
00708     if (force || (formatOrigin.bgColor!=formatData.bgColor))
00709     {
00710         if ( formatData.bgColor.isValid() )
00711         {
00712             strElement+="style:text-background-color=\"";  // ### what is fo:background-color ?
00713             strElement+=formatData.bgColor.name();
00714             strElement+="\" ";
00715             key+=formatData.bgColor.name();
00716         }
00717     }
00718 
00719     key += ';'; // Another separator
00720 
00721     if ( force || ( formatOrigin.underline != formatData.underline )
00722         || ( formatOrigin.underlineColor != formatData.underlineColor )
00723         || ( formatOrigin.underlineValue != formatData.underlineValue )
00724         || ( formatOrigin.underlineStyle != formatData.underlineStyle ) )
00725     {
00726         strElement+="style:text-underline=\"";
00727         if ( formatData.underline )
00728         {
00729             QString underlineValue ( formatData.underlineValue );
00730             QString underlineStyle ( formatData.underlineStyle );
00731 
00732             if ( underlineStyle.isEmpty() )
00733                 underlineStyle = "solid";
00734             if  ( underlineValue == "1" )
00735                 underlineValue = "single";
00736 
00737             if ( underlineValue == "single" )
00738             {
00739                 if ( underlineStyle == "dash" )
00740                 {
00741                     strElement += "dash";
00742                     key += "DA";
00743                 }
00744                 else if ( underlineStyle == "dot" )
00745                 {
00746                     strElement += "dotted";
00747                     key += "DO";
00748                 }
00749                 else if ( underlineStyle == "dashdot" )
00750                 {
00751                     strElement += "dot-dash";
00752                     key += "DDA";
00753                 }
00754                 else if ( underlineStyle == "dashdotdot" )
00755                 {
00756                     strElement += "dot-dot-dash";
00757                     key += "DDDA";
00758                 }
00759                 else
00760                 {
00761                     strElement += "single";
00762                     key += "1";
00763                 }
00764             }
00765             else if ( underlineValue == "double" )
00766             {
00767                 strElement += "double";
00768                 key += "2";
00769             }
00770             else if ( underlineValue == "single-bold" )
00771             {
00772                 strElement += "bold";
00773                 key += "BL";
00774             }
00775             else if ( underlineValue == "wave" )
00776             {
00777                 strElement += "wave";
00778                 key += "WV";
00779             }
00780             else
00781             {
00782                 strElement += "single";
00783                 key += "?";
00784             }
00785         }
00786         else
00787         {
00788             strElement+="none";
00789             key += 'N';
00790         }
00791         strElement += "\" ";
00792 
00793         if ( formatData.underline && formatData.underlineColor.isValid() )
00794         {
00795             const QString colorName( formatData.underlineColor.name() );
00796             strElement += "style:text-underline-color=\"";
00797             strElement += colorName;
00798             strElement += "\" ";
00799             key += colorName;
00800         }
00801 
00802     }
00803 
00804     key += ',';
00805 
00806     if ( force
00807         || (formatOrigin.strikeout != formatData.strikeout )
00808         || (formatOrigin.strikeoutType != formatData.strikeoutType ) )
00809     {
00810         // OOWriter can only do single, double, thick (and slash and X that KWord cannot do.)
00811         //  So no dash, dot and friends.
00812 
00813         strElement+="style:text-crossing-out=\"";
00814         if ( ( formatData.strikeoutType == "single" ) || ( formatData.strikeoutType == "1" ) )
00815         {
00816             strElement+="single-line";
00817             key += "1";
00818         }
00819         else if ( formatData.strikeoutType == "double" )
00820         {
00821             strElement+="double-line";
00822             key += "2";
00823         }
00824         else if ( formatData.strikeoutType == "single-bold" )
00825         {
00826             strElement+="thick";
00827             key += "T";
00828         }
00829         else
00830         {
00831             strElement+="none";
00832             key += 'N';
00833         }
00834         strElement+="\" ";
00835     }
00836 
00837     key += ',';
00838 
00839     // It seems that OOWriter 1.1 does have problems with word-by-word (OO Issue #11873, #25187)
00840     // It is supposed to be fixed in development versions of OO
00841     if (force || ( formatOrigin.underlineWord != formatData.underlineWord )
00842         || (formatOrigin.strikeoutWord != formatData.strikeoutWord ) )
00843     {
00844         // Strikeout and underline can only have one word-by-word behaviour in OO
00845         // (OO Issue #????? ; will not be changed.)
00846         strElement+="fo:score-spaces=\""; // Are space processed?
00847         if ( formatData.underlineWord || formatData.strikeoutWord )
00848         {
00849             strElement += "false";
00850             key += 'W';
00851         }
00852         else
00853         {
00854             strElement += "true";
00855             key += 'N';
00856         }
00857         strElement += "\" ";
00858     }
00859 
00860     key += ',';
00861 
00862     if ( force || ( formatOrigin.language != formatData.language ) )
00863     {
00864         const QString lang ( formatData.language );
00865         if ( ! lang.isEmpty() )
00866         {
00867             const int res = lang.find( '_' );
00868 
00869             if ( res >= 0 )
00870             {
00871                 kdDebug(30518) << "Language: " << lang << " => " << lang.left( res ) << " - " << lang.mid( res + 1 ) << endl;
00872                 strElement += "fo:language=\"";
00873                 strElement += lang.left( res );
00874                 strElement += "\" ";
00875                 strElement += "fo:country=\"";
00876                 strElement += lang.mid( res + 1 );
00877                 strElement += "\" ";
00878             }
00879             else
00880             {
00881                 kdDebug(30518) << "Language without country: " << lang << endl;
00882                 strElement += "fo:language=\"";
00883                 strElement += lang;
00884                 strElement += "\" ";
00885             }
00886 
00887             key+=formatData.language;
00888         }
00889     }
00890 
00891     key += ",";
00892 
00893     if ( force || ( formatOrigin.fontAttribute != formatData.fontAttribute ) )
00894     {
00895         // Note: OOWriter does not like when both fo:text-transform and fo:font-variant exist (except if both are none/normal)
00896         // (It is documented so, see sections 3.10.1 and 3.10.2)
00897         if ( formatData.fontAttribute == "uppercase" )
00898         {
00899             strElement += "fo:text-transform=\"uppercase\" ";
00900             key += 'U';
00901         }
00902         else if ( formatData.fontAttribute == "lowercase" )
00903         {
00904             strElement += "fo:text-transform=\"lowercase\" ";
00905             key += 'L';
00906         }
00907         else if ( formatData.fontAttribute == "smallcaps" )
00908         {
00909             strElement += "fo:font-variant=\"small-caps\" ";
00910             key += 'S';
00911         }
00912         else
00913         {
00914             strElement += "fo:text-transform=\"none\" ";
00915             strElement += "fo:font-variant=\"normal\" ";
00916             key += 'N';
00917         }
00918     }
00919 
00920     key += ",";
00921 
00922     if ( force || ( formatOrigin.verticalAlignment != formatData.verticalAlignment ) )
00923     {
00924         if ( 1 == formatData.verticalAlignment )
00925         {
00926             //Subscript
00927             strElement += "style:text-position=\"sub\" ";
00928             key += 'B';
00929         }
00930         else if ( 2 == formatData.verticalAlignment )
00931         {
00932             //Superscript
00933             strElement += "style:text-position=\"super\" ";
00934             key += 'P';
00935         }
00936         // ### TODO: how to reset it? "0pt" ?
00937     }
00938 
00939     return strElement.stripWhiteSpace(); // Remove especially trailing spaces
00940 }
00941 
00942 #define ALLOW_TABLE
00943 
00944 QString OOWriterWorker::cellToProperties( const TableCell& cell, QString& key) const
00945 {
00946 #ifdef ALLOW_TABLE
00947     const FrameData& frame = cell.frame;
00948     QString properties;
00949 
00950     key += "!L"; // left border
00951     key += frame.lColor.name();
00952     key += ",";
00953     key += QString::number( frame.lWidth );
00954     properties += " fo:border-left=\"";
00955     if ( frame.lColor.isValid() && frame.lWidth > 0.0 )
00956     {
00957         properties += QString::number( frame.lWidth );
00958         properties += "pt";
00959         properties += " solid "; // ### TODO
00960         properties += frame.lColor.name();
00961     }
00962     else
00963     {
00964         properties += "0pt none #000000";
00965     }
00966     properties += "\"";
00967 
00968     key += "!R"; // right border
00969     key += frame.rColor.name();
00970     key += ",";
00971     key += QString::number( frame.rWidth );
00972     properties += " fo:border-right=\"";
00973     if ( frame.rColor.isValid() && frame.rWidth > 0.0 )
00974     {
00975         properties += QString::number( frame.rWidth );
00976         properties += "pt";
00977         properties += " solid "; // ### TODO
00978         properties += frame.rColor.name();
00979     }
00980     else
00981     {
00982         properties += "0pt none #000000";
00983     }
00984     properties += "\"";
00985 
00986     key += "!T"; // top border
00987     key += frame.tColor.name();
00988     key += ",";
00989     key += QString::number( frame.tWidth );
00990     properties += " fo:border-top=\"";
00991     if ( frame.tColor.isValid() && frame.tWidth > 0.0 )
00992     {
00993         properties += QString::number( frame.tWidth );
00994         properties += "pt";
00995         properties += " solid "; // ### TODO
00996         properties += frame.tColor.name();
00997     }
00998     else
00999     {
01000         properties += "0pt none #000000";
01001     }
01002     properties += "\"";
01003 
01004     key += "!B"; // bottom border
01005     key += frame.bColor.name();
01006     key += ",";
01007     key += QString::number( frame.bWidth );
01008     properties += " fo:border-bottom=\"";
01009     if ( frame.bColor.isValid() && frame.bWidth > 0.0 )
01010     {
01011         properties += QString::number( frame.bWidth );
01012         properties += "pt";
01013         properties += " solid "; // ### TODO
01014         properties += frame.bColor.name();
01015     }
01016     else
01017     {
01018         properties += "0pt none #000000";
01019     }
01020     properties += "\"";
01021 
01022     return properties;
01023 #else
01024     return QString::null;
01025 #endif
01026 }
01027 
01028 bool OOWriterWorker::makeTableRows( const QString& tableName, const Table& table, int firstRowNumber )
01029 {
01030 #ifdef ALLOW_TABLE
01031     // ### TODO: rows
01032     // ### TODO:  be careful that covered-cell can be due vertical spanning.
01033     // ### TODO:  One way to find is the difference between the row variables (or against the last known column)
01034     // ### TODO:  be careful that fully-covered rows might exist.
01035 
01036     *m_streamOut << "<table:table-row>\n";
01037     int rowCurrent = firstRowNumber;
01038 
01039     ulong cellNumber = 0L;
01040 
01041     QMap<QString,QString> mapCellStyleKeys;
01042 
01043     for ( QValueList<TableCell>::ConstIterator itCell ( table.cellList.begin() );
01044         itCell != table.cellList.end(); ++itCell)
01045     {
01046         if ( rowCurrent != (*itCell).row )
01047         {
01048             rowCurrent = (*itCell).row;
01049             *m_streamOut << "</table:table-row>\n";
01050             *m_streamOut << "<table:table-row>\n";
01051         }
01052 
01053         QString key;
01054         const QString props ( cellToProperties( (*itCell), key ) );
01055 
01056         QString automaticCellStyle;
01057         QMap<QString,QString>::ConstIterator it ( mapCellStyleKeys.find( key ) );
01058         if ( it == mapCellStyleKeys.end() )
01059         {
01060             automaticCellStyle = makeAutomaticStyleName( tableName + ".Cell", cellNumber );
01061             mapCellStyleKeys [ key ] = automaticCellStyle;
01062             kdDebug(30518) << "Creating automatic cell style: " << automaticCellStyle  << " key: " << key << endl;
01063             m_contentAutomaticStyles += "  <style:style";
01064             m_contentAutomaticStyles += " style:name=\"" + escapeOOText( automaticCellStyle ) + "\"";
01065             m_contentAutomaticStyles += " style:family=\"table-cell\"";
01066             m_contentAutomaticStyles += ">\n";
01067             m_contentAutomaticStyles += "   <style:properties ";
01068             m_contentAutomaticStyles += props;
01069             m_contentAutomaticStyles += "/>\n";
01070             m_contentAutomaticStyles += "  </style:style>\n";
01071         }
01072         else
01073         {
01074             automaticCellStyle = it.data();
01075             kdDebug(30518) << "Using automatic cell style: " << automaticCellStyle  << " key: " << key << endl;
01076         }
01077 
01078         *m_streamOut << "<table:table-cell table:value-type=\"string\" table:style-name=\""
01079             << escapeOOText( automaticCellStyle)
01080             << "\"";
01081 
01082         // More than one column width?
01083         {
01084             *m_streamOut << " table:number-columns-spanned=\"" << (*itCell).m_cols << "\"";
01085         }
01086 
01087         *m_streamOut << ">\n";
01088 
01089         if (!doFullAllParagraphs(*(*itCell).paraList))
01090         {
01091             return false;
01092         }
01093 
01094         *m_streamOut << "</table:table-cell>\n";
01095 
01096         if ( (*itCell).m_cols > 1 )
01097         {
01098             // We need to add some placeholder for the "covered" cells
01099             for (int i = 1; i < (*itCell).m_cols; ++i)
01100             {
01101                 *m_streamOut << "<table:covered-table-cell/>";
01102             }
01103         }
01104     }
01105 
01106     *m_streamOut << "</table:table-row>\n";
01107     return true;
01108 #else
01109     return false;
01110 #endif
01111 }
01112 
01113 #ifdef ALLOW_TABLE
01114 static uint getColumnWidths( const Table& table, QMemArray<double>& widthArray, int firstRowNumber )
01115 {
01116     bool uniqueColumns = true; // We have not found any horizontally spanned cells yet.
01117     uint currentColumn = 0;
01118     int tryingRow = firstRowNumber; // We are trying the first row
01119     QValueList<TableCell>::ConstIterator itCell;
01120 
01121     for ( itCell = table.cellList.begin();
01122         itCell != table.cellList.end(); ++itCell )
01123     {
01124         kdDebug(30518) << "Column: " << (*itCell).col << " (Row: " << (*itCell).row << ")" << endl;
01125 
01126         if ( (*itCell).row != tryingRow )
01127         {
01128             if ( uniqueColumns )
01129             {
01130                  // We had a full row without any horizontally spanned cell, so we have the needed data
01131                 return currentColumn;
01132             }
01133             else
01134             {
01135                 // No luck in the previous row, so now try this new one
01136                 tryingRow = (*itCell).row;
01137                 uniqueColumns = true;
01138                 currentColumn = 0;
01139             }
01140         }
01141 
01142         if ( (*itCell).m_cols > 1 )
01143         {
01144             // We have a horizontally spanned cell
01145             uniqueColumns = false;
01146             // Do not waste the time to calculate the width
01147             continue;
01148         }
01149 
01150         const double width = ( (*itCell).frame.right - (*itCell).frame.left );
01151 
01152         if ( currentColumn >= widthArray.size() )
01153             widthArray.resize( currentColumn + 4, QGArray::SpeedOptim);
01154 
01155         widthArray.at( currentColumn ) = width;
01156         ++currentColumn;
01157     }
01158 
01159     // If we are here, it can be:
01160     // - the table is either empty or there is not any row without horizontally spanned cells
01161     // - we have needed the last row for getting something usable
01162 
01163     return uniqueColumns ? currentColumn : 0;
01164 }
01165 #endif
01166 
01167 #ifdef ALLOW_TABLE
01168 static uint getFirstRowColumnWidths( const Table& table, QMemArray<double>& widthArray, int firstRowNumber )
01169 // Get the column widths only by the first row.
01170 // This is used when all table rows have horizontally spanned cells.
01171 {
01172     uint currentColumn = 0;
01173     QValueList<TableCell>::ConstIterator itCell;
01174 
01175     for ( itCell = table.cellList.begin();
01176         itCell != table.cellList.end(); ++itCell )
01177     {
01178         kdDebug(30518) << "Column: " << (*itCell).col << " (Row: " << (*itCell).row << ")" << endl;
01179         if ( (*itCell).row != firstRowNumber )
01180             break; // We have finished the first row
01181 
01182         int cols = (*itCell).m_cols;
01183         if ( cols < 1)
01184             cols = 1;
01185 
01186         // ### FIXME: the columns behind a larger cell do not need to be symmetrical
01187         const double width = ( (*itCell).frame.right - (*itCell).frame.left ) / cols;
01188 
01189         if ( currentColumn + cols > widthArray.size() )
01190             widthArray.resize( currentColumn + 4, QGArray::SpeedOptim);
01191 
01192         for ( int i = 0; i < cols; ++i )
01193         {
01194             widthArray.at( currentColumn ) = width;
01195             ++currentColumn;
01196         }
01197     }
01198     return currentColumn;
01199 }
01200 #endif
01201 
01202 bool OOWriterWorker::makeTable( const FrameAnchor& anchor, const AnchorType anchorType )
01203 {
01204 #ifdef ALLOW_TABLE
01205 
01206     // Be careful that while being similar the following 5 strings have different purposes
01207     const QString automaticTableStyle ( makeAutomaticStyleName( "Table", m_tableNumber ) ); // It also increases m_tableNumber
01208     const QString tableName( QString( "Table" ) + QString::number( m_tableNumber ) ); // m_tableNumber was already increased
01209     const QString translatedName( i18n( "Object name", "Table %1").arg( m_tableNumber ) );
01210     const QString automaticFrameStyle ( makeAutomaticStyleName( "TableFrame", m_textBoxNumber ) ); // It also increases m_textBoxNumber
01211     const QString translatedFrameName( i18n( "Object name", "Table Frame %1").arg( m_textBoxNumber ) );
01212 
01213     kdDebug(30518) << "Processing table " << anchor.key.toString() << " => " << tableName << endl;
01214 
01215     const QValueList<TableCell>::ConstIterator firstCell ( anchor.table.cellList.begin() );
01216 
01217     if ( firstCell == anchor.table.cellList.end() )
01218     {
01219         kdError(30518) << "Table has not any cell!" << endl;
01220         return false;
01221     }
01222 
01223     const int firstRowNumber = (*firstCell).row;
01224     kdDebug(30518) << "First row: " << firstRowNumber << endl;
01225 
01226     QMemArray<double> widthArray(4);
01227 
01228     uint numberColumns = getColumnWidths( anchor.table, widthArray, firstRowNumber );
01229 
01230     if ( numberColumns <= 0 )
01231     {
01232         kdDebug(30518) << "Could not get correct column widths, so approximating" << endl;
01233         // There was a problem, the width array cannot be trusted, so try to do a column width array with the first row
01234         numberColumns = getFirstRowColumnWidths( anchor.table, widthArray, firstRowNumber );
01235         if ( numberColumns <= 0 )
01236         {
01237             // Still not right? Then it is an error!
01238             kdError(30518) << "Cannot get column widths of table " << anchor.key.toString() << endl;
01239             return false;
01240         }
01241     }
01242 
01243     kdDebug(30518) << "Number of columns: " << numberColumns << endl;
01244 
01245 
01246     double tableWidth = 0.0; // total width of table
01247     uint i; // We need the loop variable 2 times
01248     for ( i=0; i < numberColumns; ++i )
01249     {
01250         tableWidth += widthArray.at( i );
01251     }
01252     kdDebug(30518) << "Table width: " << tableWidth << endl;
01253 
01254     // An inlined table, is an "as-char" text-box
01255     *m_streamOut << "<draw:text-box";
01256     *m_streamOut << " style:name=\"" << escapeOOText( automaticFrameStyle ) << "\"";
01257     *m_streamOut << " draw:name=\"" << escapeOOText( translatedFrameName ) << "\"";
01258     if ( anchorType == AnchorNonInlined )
01259     {
01260         // ### TODO: correctly set a OOWriter frame positioned on the page
01261         *m_streamOut << " text:anchor-type=\"paragraph\"";
01262     }
01263     else
01264     {
01265         *m_streamOut << " text:anchor-type=\"as-char\"";
01266     }
01267     *m_streamOut << " svg:width=\"" << tableWidth << "pt\""; // ### TODO: any supplement to the width?
01268     //*m_streamOut << " fo:min-height=\"1pt\"";// ### TODO: a better height (can be calulated from the KWord table frames)
01269     *m_streamOut << ">\n";
01270 
01271     *m_streamOut << "<table:table table:name=\""
01272         << escapeOOText( translatedName )
01273         << "\" table:style-name=\""
01274         << escapeOOText( automaticTableStyle )
01275         << "\" >\n";
01276 
01277 
01278     // Now we have enough information to generate the style for the table and its frame
01279 
01280     kdDebug(30518) << "Creating automatic frame style: " << automaticFrameStyle /* << " key: " << styleKey */ << endl;
01281     m_contentAutomaticStyles += "  <style:style"; // for frame
01282     m_contentAutomaticStyles += " style:name=\"" + escapeOOText( automaticFrameStyle ) + "\"";
01283     m_contentAutomaticStyles += " style:family=\"graphics\"";
01284     m_contentAutomaticStyles += " style:parent-style-name=\"Frame\""; // ### TODO: parent style needs to be correctly defined
01285     m_contentAutomaticStyles += ">\n";
01286     m_contentAutomaticStyles += "   <style:properties "; // ### TODO
01287     m_contentAutomaticStyles += " text:anchor-type=\"as-char\""; // ### TODO: needed?
01288     m_contentAutomaticStyles += " fo:padding=\"0pt\" fo:border=\"none\"";
01289     m_contentAutomaticStyles += " fo:margin-left=\"0pt\"";
01290     m_contentAutomaticStyles += " fo:margin-top=\"0pt\"";
01291     m_contentAutomaticStyles += " fo:margin-bottom=\"0pt\"";
01292     m_contentAutomaticStyles += " fo:margin-right=\"0pt\"";
01293     m_contentAutomaticStyles += "/>\n";
01294     m_contentAutomaticStyles += "  </style:style>\n";
01295 
01296     kdDebug(30518) << "Creating automatic table style: " << automaticTableStyle /* << " key: " << styleKey */ << endl;
01297     m_contentAutomaticStyles += "  <style:style"; // for table
01298     m_contentAutomaticStyles += " style:name=\"" + escapeOOText( automaticTableStyle ) + "\"";
01299     m_contentAutomaticStyles += " style:family=\"table\"";
01300     m_contentAutomaticStyles += ">\n";
01301     m_contentAutomaticStyles += "   <style:properties ";
01302     m_contentAutomaticStyles += " style:width=\"" + QString::number( tableWidth ) + "pt\" ";
01303     m_contentAutomaticStyles += "/>\n";
01304     m_contentAutomaticStyles += "  </style:style>\n";
01305 
01306     QValueList<TableCell>::ConstIterator itCell;
01307 
01308     ulong columnNumber = 0L;
01309 
01310     for ( i=0; i < numberColumns; ++i )
01311     {
01312         const QString automaticColumnStyle ( makeAutomaticStyleName( tableName + ".Column", columnNumber ) );
01313         kdDebug(30518) << "Creating automatic column style: " << automaticColumnStyle /* << " key: " << styleKey */ << endl;
01314 
01315         m_contentAutomaticStyles += "  <style:style";
01316         m_contentAutomaticStyles += " style:name=\"" + escapeOOText( automaticColumnStyle ) + "\"";
01317         m_contentAutomaticStyles += " style:family=\"table-column\"";
01318         m_contentAutomaticStyles += ">\n";
01319         m_contentAutomaticStyles += "   <style:properties ";
01320         // Despite that some OO specification examples use fo:width, OO specification section 4.19 tells to use style:column-width
01321         //  and/or the relative variant: style:rel-column-width
01322         m_contentAutomaticStyles += " style:column-width=\"" + QString::number( widthArray.at( i ) ) + "pt\" ";
01323         m_contentAutomaticStyles += "/>\n";
01324         m_contentAutomaticStyles += "  </style:style>\n";
01325 
01326         // ### TODO: find a way how to use table:number-columns-repeated for more that one cell's column(s)
01327         *m_streamOut << "<table:table-column table:style-name=\""
01328             << escapeOOText( automaticColumnStyle )
01329             << "\" table:number-columns-repeated=\"1\"/>\n";
01330     }
01331 
01332     makeTableRows( tableName, anchor.table, firstRowNumber );
01333 
01334     *m_streamOut << "</table:table>\n";
01335 
01336     *m_streamOut << "</draw:text-box>"; // End of inline
01337 
01338 #endif
01339     return true;
01340 }
01341 
01342 bool OOWriterWorker::makePicture( const FrameAnchor& anchor, const AnchorType anchorType )
01343 {
01344     kdDebug(30518) << "New picture: " << anchor.picture.koStoreName
01345         << " , " << anchor.picture.key.toString() << endl;
01346 
01347     const QString koStoreName(anchor.picture.koStoreName);
01348 
01349     QByteArray image;
01350 
01351     QString strExtension(koStoreName.lower());
01352     const int result=koStoreName.findRev(".");
01353     if (result>=0)
01354     {
01355         strExtension=koStoreName.mid(result+1);
01356     }
01357 
01358     bool isImageLoaded=false;
01359 
01360     if (strExtension=="png")
01361     {
01362         isImageLoaded=loadSubFile(koStoreName,image);
01363     }
01364     else if ((strExtension=="jpg") || (strExtension=="jpeg"))
01365     {
01366         isImageLoaded=loadSubFile(koStoreName,image);
01367         strExtension="jpg"; // ### TODO: verify
01368     }
01369     else if ((strExtension=="tif") || (strExtension=="tiff"))
01370     {
01371         isImageLoaded=loadSubFile(koStoreName,image);
01372         strExtension="tif"; // ### TODO: verify
01373     }
01374     else if ((strExtension=="gif") || (strExtension=="wmf"))
01375         // ### TODO: Which other image formats does OOWriter support directly?
01376     {
01377         isImageLoaded=loadSubFile(koStoreName,image);
01378     }
01379     else
01380     {
01381         // All other picture types must be converted to PNG
01382         isImageLoaded=loadAndConvertToImage(koStoreName,strExtension,"PNG",image);
01383         strExtension="png";
01384     }
01385 
01386     if (!isImageLoaded)
01387     {
01388         kdWarning(30518) << "Unable to load picture: " << koStoreName << endl;
01389         return true;
01390     }
01391 
01392     kdDebug(30518) << "Picture loaded: " << koStoreName << endl;
01393 
01394     double height = 0.0;
01395     double width = 0.0;
01396 
01397     if ( anchorType == AnchorTextImage )
01398     {
01399         // Text image have no frameset, so the only size information is in the picture itself.
01400         QBuffer buffer( image.copy() ); // Be more safe than sorry and do not allow shallow copy
01401         KoPicture pic;
01402         buffer.open( IO_ReadOnly );
01403         if ( pic.load( &buffer, strExtension ) )
01404         {
01405             const QSize size ( pic.getOriginalSize() );
01406             height = size.height();
01407             width = size.width();
01408         }
01409         else
01410         {
01411             kdWarning(30518) << "Could not load KoPicture: " << koStoreName << endl;
01412         }
01413         buffer.close();
01414     }
01415     else
01416     {
01417         // Use frame size
01418         height=anchor.frame.bottom - anchor.frame.top;
01419         width =anchor.frame.right  - anchor.frame.left;
01420     }
01421 
01422     if ( height < 1.0 )
01423     {
01424         kdWarning(30518) << "Silly height for " << koStoreName << " : "  << height << endl;
01425         height = 72.0;
01426     }
01427     if ( width < 1.0 )
01428     {
01429         kdWarning(30518) << "Silly width for " << koStoreName << " : "  << width << endl;
01430         width = 72.0;
01431     }
01432 
01433      // We need a 32 digit hex value of the picture number
01434      // Please note: it is an exact 32 digit value, truncated if the value is more than 512 bits wide. :-)
01435     QString number;
01436     number.fill('0',32);
01437     number += QString::number(++m_pictureNumber,16); // in hex
01438 
01439     QString ooName("Pictures/");
01440     ooName += number.right(32);
01441     ooName += '.';
01442     ooName += strExtension;
01443 
01444     kdDebug(30518) << "Picture " << koStoreName << " => " << ooName << endl;
01445 
01446     // TODO: we are only using the filename, not the rest of the key
01447     // TODO:  (bad if there are two images of the same name, but of a different key)
01448     *m_streamOut << "<draw:image draw:name=\"" << anchor.picture.key.filename() << "\"";
01449     *m_streamOut << " draw:style-name=\"Graphics\""; // ### TODO: should be an automatic "graphic" style name instead
01450     if ( anchorType == AnchorNonInlined )
01451     {
01452         // ### TODO: correctly set a OOWriter frame positioned on the page
01453         *m_streamOut << " text:anchor-type=\"paragraph\"";
01454     }
01455     else
01456     {
01457         *m_streamOut << " text:anchor-type=\"as-char\"";
01458     }
01459     *m_streamOut << " svg:height=\"" << height << "pt\" svg:width=\"" << width << "pt\"";
01460     *m_streamOut << " draw:z-index=\"0\" xlink:href=\"#" << ooName << "\"";
01461     *m_streamOut << " xlink:type=\"simple\" xlink:show=\"embed\" xlink:actuate=\"onLoad\"";
01462     *m_streamOut << "/>"; // NO end of line!
01463 
01464     if (m_zip)
01465     {
01466 #if 0
01467         // ### FIXME Why is the following line not working (at least with KDE 3.1)? (It makes unzip having problems with meta.xml)
01468         m_zip->writeFile(ooName,QString::null, QString::null, image.size(), image.data());
01469 #else
01470         zipPrepareWriting(ooName);
01471         zipWriteData( image );
01472         zipDoneWriting();
01473 #endif
01474     }
01475 
01476     return true;
01477 }
01478 
01479 void OOWriterWorker::processNormalText ( const QString &paraText,
01480     const TextFormatting& formatLayout,
01481     const FormatData& formatData)
01482 {
01483     // Retrieve text and escape it (and necessary space, tabs and line-break tags)
01484     const QString partialText( escapeOOSpan( paraText.mid( formatData.pos, formatData.len ) ) );
01485 
01486     if (formatData.text.missing)
01487     {
01488         // It's just normal text, so we do not need a <text:span> element!
01489         *m_streamOut << partialText;
01490     }
01491     else
01492     { // Text with properties, so use a <text:span> element!
01493         *m_streamOut << "<text:span";
01494 
01495         QString styleKey;
01496         const QString props ( textFormatToStyle(formatLayout,formatData.text,false,styleKey) );
01497 
01498         QMap<QString,QString>::ConstIterator it ( m_mapTextStyleKeys.find(styleKey) );
01499         kdDebug(30518) << "Searching text key: " << styleKey << endl;
01500 
01501         QString automaticStyle;
01502         if (it==m_mapTextStyleKeys.end())
01503         {
01504             // We have not any match, so we need a new automatic text style
01505             automaticStyle=makeAutomaticStyleName("T", m_automaticTextStyleNumber);
01506             kdDebug(30518) << "Creating automatic text style: " << automaticStyle << " key: " << styleKey << endl;
01507             m_mapTextStyleKeys[styleKey]=automaticStyle;
01508 
01509             m_contentAutomaticStyles += "  <style:style";
01510             m_contentAutomaticStyles += " style:name=\"" + escapeOOText(automaticStyle) + "\"";
01511             m_contentAutomaticStyles += " style:family=\"text\"";
01512             m_contentAutomaticStyles += ">\n";
01513             m_contentAutomaticStyles += "   <style:properties ";
01514             m_contentAutomaticStyles += props;
01515             m_contentAutomaticStyles += "/>\n";
01516             m_contentAutomaticStyles += "  </style:style>\n";
01517         }
01518         else
01519         {
01520             // We have a match, so use the already defined automatic text style
01521             automaticStyle=it.data();
01522             kdDebug(30518) << "Using automatic text style: " << automaticStyle << " key: " << styleKey << endl;
01523         }
01524 
01525         *m_streamOut << " text:style-name=\"" << escapeOOText(automaticStyle) << "\" ";
01526 
01527         *m_streamOut << ">" << partialText << "</text:span>";
01528     }
01529 }
01530 
01531 void OOWriterWorker::processFootnote( const VariableData& variable )
01532 {
01533     // Footnote
01534     const QValueList<ParaData> *paraList = variable.getFootnotePara();
01535     if( paraList )
01536     {
01537         const QString value ( variable.getFootnoteValue() );
01538         //const bool automatic = formatData.variable.getFootnoteAuto();
01539         const bool flag = variable.getFootnoteType();
01540 
01541         if ( flag )
01542         {
01543             *m_streamOut << "<text:footnote text:id=\"ft";
01544             *m_streamOut << (++m_footnoteNumber);
01545             *m_streamOut << "\">";
01546             *m_streamOut << "<text:footnote-citation>" << escapeOOText( value ) << "</text:footnote-citation>";
01547             *m_streamOut << "<text:footnote-body>\n";
01548 
01549             doFullAllParagraphs( *paraList );
01550 
01551             *m_streamOut << "\n</text:footnote-body>";
01552             *m_streamOut << "</text:footnote>";
01553         }
01554         else
01555         {
01556             *m_streamOut << "<text:endnote text:id=\"ft";
01557             *m_streamOut << (++m_footnoteNumber);
01558             *m_streamOut << "\">";
01559             *m_streamOut << "<text:endnote-citation>" << escapeOOText( value ) << "</text:endnote-citation>";
01560             *m_streamOut << "<text:endnote-body>\n";
01561 
01562             doFullAllParagraphs( *paraList );
01563 
01564             *m_streamOut << "\n</text:endnote-body>";
01565             *m_streamOut << "</text:endnote>";
01566         }
01567     }
01568 }
01569 
01570 void OOWriterWorker::processNote( const VariableData& variable )
01571 {
01572     // KWord 1.3's annotations are anonymous and undated,
01573     //  however the OO specification tells that author and date are mandatory (even if OOWriter 1.1 consider them optional)
01574 
01575     *m_streamOut << "<office:annotation office:create-date=\"";
01576 
01577     // We use the document creation date as creation date for the annotation
01578     // (OOWriter uses only the date part, there is no time part)
01579     if ( m_varSet.creationTime.isValid() )
01580         *m_streamOut << escapeOOText( m_varSet.creationTime.date().toString( Qt::ISODate ) );
01581     else
01582         *m_streamOut << "1970-01-01";
01583 
01584     *m_streamOut << "\" office:author=\"";
01585 
01586     // We try to use the document author's name as annotation author
01587     if ( m_docInfo.fullName.isEmpty() )
01588         *m_streamOut << escapeOOText( i18n( "Pseudo-author for annotations", "KWord 1.3" ) );
01589     else
01590         *m_streamOut << escapeOOText( m_docInfo.fullName );
01591 
01592     *m_streamOut << "\">\n";
01593     *m_streamOut << "<text:p>"
01594         << escapeOOSpan( variable.getGenericData( "note" ) )
01595         << "</text:p>\n"
01596         << "</office:annotation>";
01597 }
01598 
01599 void OOWriterWorker::processVariable ( const QString&,
01600     const TextFormatting& /*formatLayout*/,
01601     const FormatData& formatData)
01602 {
01603     if (0==formatData.variable.m_type)
01604     {
01605         *m_streamOut << "<text:date/>"; // ### TODO: parameters
01606     }
01607     else if (2==formatData.variable.m_type)
01608     {
01609         *m_streamOut << "<text:time/>"; // ### TODO: parameters
01610     }
01611     else if (4==formatData.variable.m_type)
01612     {
01613         // ### TODO: the other under-types, other parameters
01614         if (formatData.variable.isPageNumber())
01615         {
01616             *m_streamOut << "<text:page-number text:select-page=\"current\"/>";
01617         }
01618         else if (formatData.variable.isPageCount())
01619         {
01620             *m_streamOut << "<text:page-count/>";
01621         }
01622         else
01623         {
01624             // Unknown subtype, therefore write out the result
01625             *m_streamOut << formatData.variable.m_text;
01626         }
01627     }
01628     else if (9==formatData.variable.m_type)
01629     {
01630         // A link
01631         *m_streamOut << "<text:a xlink:href=\""
01632             << escapeOOText(formatData.variable.getHrefName())
01633             << "\" xlink:type=\"simple\">"
01634             << escapeOOText(formatData.variable.getLinkName())
01635             << "</text:a>";
01636     }
01637     else if ( 10 == formatData.variable.m_type )
01638     {   // Note (OOWriter: annotation)
01639         processNote ( formatData.variable );
01640     }
01641     else if (11==formatData.variable.m_type)
01642     {
01643         // Footnote
01644         processFootnote ( formatData.variable );
01645     }
01646     else
01647     {
01648         // Generic variable
01649         *m_streamOut << formatData.variable.m_text;
01650     }
01651 }
01652 
01653 void OOWriterWorker::processAnchor ( const QString&,
01654     const TextFormatting& /*formatLayout*/, //TODO
01655     const FormatData& formatData)
01656 {
01657     // We have a picture or a table
01658     if ( (2==formatData.frameAnchor.type) // <IMAGE> or <PICTURE>
01659         || (5==formatData.frameAnchor.type) ) // <CLIPART>
01660     {
01661         makePicture( formatData.frameAnchor, AnchorInlined );
01662     }
01663     else if (6==formatData.frameAnchor.type)
01664     {
01665         makeTable( formatData.frameAnchor, AnchorInlined );
01666     }
01667     else
01668     {
01669         kdWarning(30518) << "Unsupported anchor type: "
01670             << formatData.frameAnchor.type << endl;
01671     }
01672 }
01673 
01674 void OOWriterWorker::processTextImage ( const QString&,
01675     const TextFormatting& /*formatLayout*/,
01676     const FormatData& formatData)
01677 {
01678     kdDebug(30518) << "Text Image: " << formatData.frameAnchor.key.toString() << endl;
01679     makePicture( formatData.frameAnchor, AnchorTextImage );
01680 }
01681 
01682 void OOWriterWorker::processParagraphData ( const QString &paraText,
01683     const TextFormatting& formatLayout,
01684     const ValueListFormatData &paraFormatDataList)
01685 {
01686     if ( paraText.length () > 0 )
01687     {
01688         ValueListFormatData::ConstIterator  paraFormatDataIt;
01689 
01690         for ( paraFormatDataIt = paraFormatDataList.begin ();
01691               paraFormatDataIt != paraFormatDataList.end ();
01692               paraFormatDataIt++ )
01693         {
01694             if (1==(*paraFormatDataIt).id)
01695             {
01696                 processNormalText(paraText, formatLayout, (*paraFormatDataIt));
01697             }
01698             else if (2==(*paraFormatDataIt).id)
01699             {
01700                 processTextImage(paraText, formatLayout, (*paraFormatDataIt));
01701             }
01702             else if ( 3 == (*paraFormatDataIt).id )
01703             {
01704                 // Just a (KWord 0.8) tab stop, nothing else to do!
01705                 *m_streamOut << "<text:tab-stop/>";
01706             }
01707             else if (4==(*paraFormatDataIt).id)
01708             {
01709                 processVariable(paraText, formatLayout, (*paraFormatDataIt));
01710             }
01711             else if (6==(*paraFormatDataIt).id)
01712             {
01713                 processAnchor(paraText, formatLayout, (*paraFormatDataIt));
01714             }
01715             else if ( 1001 == (*paraFormatDataIt).id ) // Start of bookmark
01716             {
01717                 *m_streamOut << "<text:bookmark-start text:name=\""
01718                     << escapeOOText( (*paraFormatDataIt).variable.m_text )
01719                     <<"\"/>";
01720             }
01721             else if ( 1002 == (*paraFormatDataIt).id ) // End of bookmark
01722             {
01723                 *m_streamOut << "<text:bookmark-end text:name=\""
01724                     << escapeOOText( (*paraFormatDataIt).variable.m_text )
01725                     <<"\"/>";
01726             }
01727         }
01728     }
01729 }
01730 
01731 QString OOWriterWorker::layoutToParagraphStyle(const LayoutData& layoutOrigin,
01732     const LayoutData& layout, const bool force, QString& styleKey)
01733 {
01734     QString props; // Props has to remain empty, if there is no difference.
01735 
01736     styleKey += layout.styleName;
01737     styleKey += ',';
01738 
01739     if (force || (layoutOrigin.alignment!=layout.alignment))
01740     {
01741         // NOTE: OO 1.0.x uses start and end like left and right (section 3.11.4)
01742         // Unfortunately in XSL-FO's text-align, they are really supposed to be the start and the end.
01743         if (layout.alignment == "left")
01744         {
01745             props += "fo:text-align=\"start\" ";
01746             styleKey += 'L';
01747         }
01748         else if (layout.alignment == "right")
01749         {
01750             props += "fo:text-align=\"end\" ";
01751             styleKey += 'R';
01752         }
01753         else if (layout.alignment == "center")
01754         {
01755             props += "fo:text-align=\"center\" ";
01756             styleKey += 'C';
01757         }
01758         else if (layout.alignment == "justify")
01759         {
01760             props += "fo:text-align=\"justify\" ";
01761             styleKey += 'J';
01762         }
01763         else if (layout.alignment == "auto")
01764         {
01765             props += "fo:text-align=\"start\" ";
01766 #ifndef STRICT_OOWRITER_VERSION_1
01767             props += "style:text-auto-align=\"true\" "; // rejected draft OASIS extension
01768 #endif      
01769             styleKey += 'A';
01770         }
01771         else
01772         {
01773             kdWarning(30518) << "Unknown alignment: " << layout.alignment << endl;
01774         }
01775     }
01776 
01777     styleKey += ',';
01778 
01779     if ((layout.indentLeft>=0.0)
01780         && (force || (layoutOrigin.indentLeft!=layout.indentLeft)))
01781     {
01782         props += QString("fo:margin-left=\"%1pt\" ").arg(layout.indentLeft);
01783         styleKey += QString::number(layout.indentLeft);
01784     }
01785 
01786     styleKey += ',';
01787 
01788     if ((layout.indentRight>=0.0)
01789         && (force || (layoutOrigin.indentRight!=layout.indentRight)))
01790     {
01791         props += QString("fo:margin-right=\"%1pt\" ").arg(layout.indentRight);
01792         styleKey += QString::number(layout.indentRight);
01793     }
01794 
01795     styleKey += ',';
01796 
01797     if (force || (layoutOrigin.indentLeft!=layout.indentLeft))
01798     {
01799         props += "fo:text-indent=\"";
01800         props += QString::number(layout.indentFirst);
01801         props += "\" ";
01802         styleKey += QString::number(layout.indentFirst);
01803     }
01804 
01805     styleKey += ',';
01806 
01807     if ((layout.marginBottom>=0.0)
01808         && ( force || ( layoutOrigin.marginBottom != layout.marginBottom ) ) )
01809     {
01810        props += QString("fo:margin-bottom=\"%1pt\" ").arg(layout.marginBottom);
01811        styleKey += QString::number(layout.marginBottom);
01812     }
01813 
01814     styleKey += ',';
01815 
01816     if ((layout.marginTop>=0.0)
01817         && ( force || ( layoutOrigin.marginTop != layout.marginTop ) ) )
01818     {
01819        props += QString("fo:margin-top=\"%1pt\" ").arg(layout.marginTop);
01820        styleKey += QString::number(layout.marginTop);
01821     }
01822 
01823     styleKey += ',';
01824 
01825     if (force
01826         || ( layoutOrigin.lineSpacingType != layout.lineSpacingType )
01827         || ( layoutOrigin.lineSpacing != layout.lineSpacing ) )
01828     {
01829         switch ( layout.lineSpacingType )
01830         {
01831         case LayoutData::LS_CUSTOM:
01832             {
01833                 // We have a custom line spacing (in points)
01834                 const QString height ( QString::number(layout.lineSpacing) ); // ### TODO: rounding?
01835                 props += "style:line-spacing=\"";
01836                 props += height;
01837                 props += "pt\" ";
01838                 styleKey += height;
01839                 styleKey += 'C';
01840                 break;
01841             }
01842         case LayoutData::LS_SINGLE:
01843             {
01844                 props += "fo:line-height=\"normal\" "; // One
01845                 styleKey += "100%"; // One
01846                 break;
01847             }
01848         case LayoutData::LS_ONEANDHALF:
01849             {
01850                 props += "fo:line-height=\"150%\" "; // One-and-half
01851                 styleKey += "150%";
01852                 break;
01853             }
01854         case LayoutData::LS_DOUBLE:
01855             {
01856                 props += "fo:line-height=\"200%\" "; // Two
01857                 styleKey += "200%";
01858                 break;
01859             }
01860         case LayoutData::LS_MULTIPLE:
01861             {
01862                 // OOWriter 1.1 only allows up to 200%
01863                 const QString mult ( QString::number( qRound( layout.lineSpacing * 100 ) ) );
01864                 props += "fo:line-height=\"";
01865                 props += mult;
01866                 props += "%\" ";
01867                 styleKey += mult;
01868                 styleKey += "%";
01869                 break;
01870             }
01871         case LayoutData::LS_FIXED:
01872             {
01873                 // We have a fixed line height (in points)
01874                 const QString height ( QString::number(layout.lineSpacing) ); // ### TODO: rounding?
01875                 props += "fo:line-height=\"";
01876                 props += height;
01877                 props += "pt\" ";
01878                 styleKey += height;
01879                 styleKey += 'F';
01880                 break;
01881             }
01882         case LayoutData::LS_ATLEAST:
01883             {
01884                 // We have a at-least line height (in points)
01885                 const QString height ( QString::number(layout.lineSpacing) ); // ### TODO: rounding?
01886                 props += "style:line-height-at-least=\"";
01887                 props += height;
01888                 props += "pt\" ";
01889                 styleKey += height;
01890                 styleKey += 'A';
01891                 break;
01892             }
01893         default:
01894             {
01895                 kdWarning(30518) << "Unsupported lineSpacingType: " << layout.lineSpacingType << " (Ignoring!)" << endl;
01896                 break;
01897             }
01898         }
01899     }
01900     
01901     styleKey += ',';
01902 
01903     if ( layout.pageBreakBefore )
01904     {
01905         // We have a page break before the paragraph
01906         props += "fo:page-break-before=\"page\" ";
01907         styleKey += 'B';
01908     }
01909 
01910     styleKey += ',';
01911 
01912     if ( layout.pageBreakAfter )
01913     {
01914         // We have a page break after the paragraph
01915         props += "fo:page-break-after=\"page\" ";
01916         styleKey += 'A';
01917     }
01918 
01919     styleKey += '@'; // A more visible seperator
01920 
01921     props += textFormatToStyle(layoutOrigin.formatData.text,layout.formatData.text,force,styleKey);
01922 
01923     props += ">";
01924 
01925     styleKey += '@'; // A more visible seperator
01926 
01927     // ### TODO/FIXME: what if all tabulators must be erased?
01928     if (!layout.tabulatorList.isEmpty()
01929         && (force || (layoutOrigin.tabulatorList!=layout.tabulatorList) ))
01930     {
01931         props += "\n    <style:tab-stops>\n";
01932         TabulatorList::ConstIterator it;
01933         TabulatorList::ConstIterator end(layout.tabulatorList.end());
01934         for (it=layout.tabulatorList.begin();it!=end;++it)
01935         {
01936             props+="     <style:tab-stop style:position=\"";
01937             props += QString::number((*it).m_ptpos);
01938             props += "pt\"";
01939             styleKey += QString::number((*it).m_ptpos);
01940             switch ((*it).m_type)
01941             {
01942                 case 0:  props += " style:type=\"left\""; styleKey += "L"; break;
01943                 case 1:  props += " style:type=\"center\""; styleKey += "C"; break;
01944                 case 2:  props += " style:type=\"right\""; styleKey += "R"; break;
01945                 case 3:  props += " style:type=\"char\" style:char=\".\""; styleKey += "D"; break; // decimal
01946                 default: props += " style:type=\"left\""; styleKey += "L"; break;
01947             }
01948             switch ((*it).m_filling) // ### TODO: check if the characters are right
01949             {
01950                 case TabulatorData::TF_NONE: break;
01951                 case TabulatorData::TF_DOT:  props += " style:leader-char=\".\""; break;
01952                 case TabulatorData::TF_LINE: props += " style:leader-char=\"_\""; break;
01953 
01954                 case TabulatorData::TF_DASH:
01955                 case TabulatorData::TF_DASHDOT:
01956                 case TabulatorData::TF_DASHDOTDOT: props += " style:leader-char=\"-\""; break;
01957 
01958                 default: break;
01959             }
01960             props += "/>\n";
01961             styleKey +='/';
01962         }
01963         props += "    </style:tab-stops>\n   ";
01964     }
01965 
01966     return props;
01967 }
01968 
01969 bool OOWriterWorker::doFullParagraph(const QString& paraText, const LayoutData& layout,
01970     const ValueListFormatData& paraFormatDataList)
01971 {
01972     const bool header = ( (layout.counter.numbering == CounterData::NUM_CHAPTER)
01973         && (layout.counter.depth<10) ); // ### TODO: Does OOWriter really limits to 10?
01974 
01975     if (header)
01976     {
01977         *m_streamOut << "  <text:h text:level=\"";
01978         *m_streamOut << QString::number(layout.counter.depth+1,10);
01979         *m_streamOut << "\" ";
01980     }
01981     else
01982         *m_streamOut << "  <text:p ";
01983 
01984     const LayoutData& styleLayout=m_styleMap[layout.styleName];
01985 
01986     QString styleKey;
01987     const QString props(layoutToParagraphStyle(styleLayout,layout,false,styleKey));
01988 
01989     QString actualStyle(layout.styleName);
01990     if (!props.isEmpty())
01991     {
01992         QMap<QString,QString>::ConstIterator it ( m_mapParaStyleKeys.find(styleKey) );
01993         kdDebug(30518) << "Searching paragraph key: " << styleKey << endl;
01994 
01995         QString automaticStyle;
01996 
01997         if (it==m_mapParaStyleKeys.end())
01998         {
01999             // We have additional properties, so we need an automatic style for the paragraph
02000             automaticStyle = makeAutomaticStyleName("P", m_automaticParagraphStyleNumber);
02001             kdDebug(30518) << "Creating automatic paragraph style: " << automaticStyle << " key: " << styleKey << endl;
02002             m_mapParaStyleKeys[styleKey]=automaticStyle;
02003 
02004             m_contentAutomaticStyles += "  <style:style";
02005             m_contentAutomaticStyles += " style:name=\"" + escapeOOText(automaticStyle) + "\"";
02006             m_contentAutomaticStyles += " style:parent-style-name=\"" + escapeOOText(layout.styleName) + "\"";
02007             m_contentAutomaticStyles += " style:family=\"paragraph\" style:class=\"text\"";
02008             m_contentAutomaticStyles += ">\n";
02009             m_contentAutomaticStyles += "   <style:properties ";
02010             m_contentAutomaticStyles += props;
02011             m_contentAutomaticStyles += "</style:properties>\n";
02012             m_contentAutomaticStyles += "  </style:style>\n";
02013         }
02014         else
02015         {
02016             // We have a match, so use the already defined automatic paragraph style
02017             automaticStyle=it.data();
02018             kdDebug(30518) << "Using automatic paragraph style: " << automaticStyle << " key: " << styleKey << endl;
02019         }
02020 
02021         actualStyle=automaticStyle;
02022     }
02023 
02024     if (!actualStyle.isEmpty())
02025     {
02026         *m_streamOut << "text:style-name=\"" << escapeOOText(actualStyle) << "\" ";
02027     }
02028     else
02029     {   // SHould not happen
02030         kdWarning(30518) << "No style for a paragraph!" << endl;
02031     }
02032 
02033     *m_streamOut << ">";
02034 
02035     processParagraphData(paraText, layout.formatData.text, paraFormatDataList);
02036 
02037     if (header)
02038         *m_streamOut << "</text:h>\n";
02039     else
02040         *m_streamOut << "</text:p>\n";
02041 
02042     return true;
02043 }
02044 
02045 bool OOWriterWorker::doOpenStyles(void)
02046 {
02047     m_styles += " <office:styles>\n";
02048     m_styles += "  <style:style style:name=\"Graphics\" style:family=\"graphics\">\n"; // ### TODO: what if Graphics is a normal style
02049     m_styles += "   <style:properties text:anchor-type=\"paragraph\" style:wrap=\"none\"/>\n";
02050     m_styles += "  </style:style>\n";
02051     m_styles += "  <style:style style:name=\"Frame\" style:family=\"graphics\">\n"; // ### TODO: what if Frame is a normal style
02052     m_styles += "   <style:properties text:anchor-type=\"paragraph\" style:wrap=\"none\"/>\n";
02053     m_styles += "  </style:style>\n";
02054     return true;
02055 }
02056 
02057 bool OOWriterWorker::doFullDefineStyle(LayoutData& layout)
02058 {
02059     //Register style in the style map
02060     m_styleMap[layout.styleName]=layout;
02061 
02062     m_styles += "  <style:style";
02063 
02064     m_styles += " style:name=\"" + escapeOOText( layout.styleName ) + "\"";
02065     m_styles += " style:next-style-name=\"" + escapeOOText( layout.styleFollowing ) + "\"";
02066     m_styles += " style:family=\"paragraph\" style:class=\"text\"";
02067     m_styles += ">\n";
02068     m_styles += "   <style:properties ";
02069 
02070     QString debugKey; // Not needed
02071     m_styles += layoutToParagraphStyle(layout,layout,true,debugKey);
02072     kdDebug(30518) << "Defining style: " << debugKey << endl;
02073 
02074     m_styles += "</style:properties>\n";
02075     m_styles += "  </style:style>\n";
02076 
02077     return true;
02078 }
02079 
02080 bool OOWriterWorker::doCloseStyles(void)
02081 {
02082     m_styles += " </office:styles>\n";
02083     return true;
02084 }
02085 
02086 bool OOWriterWorker::doFullPaperFormat(const int format,
02087             const double width, const double height, const int orientation)
02088 {
02089     if ( ( format < 0 ) // Be careful that 0 is ISO A3
02090         || ( width < 1.0 )
02091         || ( height < 1.0 ) )
02092     {
02093         kdWarning(30518) << "Page size problem: format: " << format << " width: " << width << " height: " << height << endl;
02094         // Something is wrong with the page size
02095         KoFormat newFormat = KoFormat ( format );
02096         if ( ( format < 0 ) || ( format > PG_LAST_FORMAT ) )
02097         {
02098             // Bad or unknown format, so assume ISO A4
02099             newFormat = PG_DIN_A4;
02100         }
02101         m_paperWidth = KoPageFormat::width ( newFormat, KoOrientation( orientation ) ) * 72.0 / 25.4 ;
02102         m_paperHeight = KoPageFormat::height ( newFormat, KoOrientation( orientation ) ) * 72.0 / 25.4 ;
02103         m_paperFormat = newFormat;
02104     }
02105     else
02106     {
02107         m_paperFormat=format;
02108         m_paperWidth=width;
02109         m_paperHeight=height;
02110     }
02111     m_paperOrientation=orientation; // ### TODO: check if OOWriter needs the orignal size (without landscape) or the real size
02112     return true;
02113 }
02114 
02115 bool OOWriterWorker::doFullPaperBorders (const double top, const double left,
02116     const double bottom, const double right)
02117 {
02118     m_paperBorderTop=top;
02119     m_paperBorderLeft=left;
02120     m_paperBorderBottom=bottom;
02121     m_paperBorderRight=right;
02122     return true;
02123 }
02124 
02125 bool OOWriterWorker::doFullPaperFormatOther ( const int columns, const double columnspacing, const int numPages )
02126 {
02127     m_columns = columns;
02128     m_columnspacing = columnspacing;
02129     m_numPages = numPages;
02130     return true;
02131 }
02132 
02133 bool OOWriterWorker::doFullDocumentInfo(const KWEFDocumentInfo& docInfo)
02134 {
02135     m_docInfo=docInfo;
02136 
02137     return true;
02138 }
02139 
02140 bool OOWriterWorker::doVariableSettings(const VariableSettingsData& vs)
02141 {
02142     m_varSet=vs;
02143 
02144     return true;
02145 }
02146 
02147 bool OOWriterWorker::doDeclareNonInlinedFramesets( QValueList<FrameAnchor>& pictureAnchors, QValueList<FrameAnchor>& tableAnchors )
02148 {
02149     m_nonInlinedPictureAnchors = pictureAnchors;
02150     m_nonInlinedTableAnchors = tableAnchors;
02151     return true;
02152 }
02153 
02154 void OOWriterWorker::declareFont(const QString& fontName)
02155 {
02156     if (fontName.isEmpty())
02157         return;
02158         
02159     if (m_fontNames.find(fontName)==m_fontNames.end())
02160     {
02161         QString props;
02162 
02163         // Disabled, as QFontInfo::styleHint() cannot guess
02164 #if 0
02165         QFont font(fontName);
02166         QFontInfo info(font);
02167         props+="style:font-family-generic=\""
02168         switch (info.styleHint())
02169         {
02170         case QFont::SansSerif:
02171         default:
02172             {
02173                 props += "swiss";
02174                 break;
02175             }
02176         case QFont::Serif:
02177             {
02178                 props +=  "roman";
02179                 break;
02180             }
02181         case QFont::Courier:
02182             {
02183                 props +=  "modern";
02184                 break;
02185             }
02186         case QFont::OldEnglish:
02187             {
02188                 props +=  "decorative";
02189                 break;
02190             }
02191         }
02192         props +="\" ";
02193 #endif
02194 
02195         props +="style:font-pitch=\"variable\""; // ### TODO: check if font is variable or fixed
02196         // New font, so register it
02197         m_fontNames[fontName]=props;
02198     }
02199 }
02200 
02201 QString OOWriterWorker::makeAutomaticStyleName(const QString& prefix, ulong& counter) const
02202 {
02203     const QString str (prefix + QString::number(++counter,10));
02204 
02205     // Checks if the automatic style has not the same name as a user one.
02206     // If it is the case, change it!
02207 
02208     if (m_styleMap.find(str)==m_styleMap.end())
02209         return str; // Unique, so let's go!
02210 
02211     QString str2(str+"_bis");
02212     if (m_styleMap.find(str2)==m_styleMap.end())
02213         return str2;
02214 
02215     str2 = str+"_ter";
02216     if (m_styleMap.find(str2)==m_styleMap.end())
02217         return str2;
02218 
02219     // If it is still not unique, try a time stamp.
02220     const QDateTime dt(QDateTime::currentDateTime(Qt::UTC));
02221 
02222     str2 = str + "_" + QString::number(dt.toTime_t(),16);
02223     if (m_styleMap.find(str2)==m_styleMap.end())
02224         return str2;
02225 
02226     kdWarning(30518) << "Could not make an unique style name: " << str2 << endl;
02227     return str2; // Still return, as we have nothing better
02228 }
02229 
KDE Home | KDE Accessibility Home | Description of Access Keys