filters

mswriteexport.cc

00001 /* This file is part of the KDE project
00002    Copyright (C) 2002-2003 Clarence Dang <dang@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 Version 2 as published by the Free Software Foundation.
00007 
00008    This library is distributed in the hope that it will be useful,
00009    but WITHOUT ANY WARRANTY; without even the implied warranty of
00010    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00011    Library General Public License Version 2 for more details.
00012 
00013    You should have received a copy of the GNU Library General Public License
00014    Version 2 along with this library; see the file COPYING.LIB.  If not,
00015    write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00016  * Boston, MA 02110-1301, USA.
00017 */
00018 
00019 #include <limits.h>
00020 #include <stdio.h>
00021 #include <stdlib.h>
00022 #include <string.h>
00023 
00024 #include <qbuffer.h>
00025 #include <qcstring.h>
00026 #include <qfile.h>
00027 #include <qfont.h>
00028 #include <qimage.h>
00029 #include <qtextcodec.h>
00030 #include <qvaluelist.h>
00031 #include <qvaluestack.h>
00032 
00033 #include <kdebug.h>
00034 #include <kgenericfactory.h>
00035 
00036 #include <KoFilterChain.h>
00037 #include <kowmfpaint.h>
00038 
00039 #include <KWEFStructures.h>
00040 #include <KWEFBaseWorker.h>
00041 #include <KWEFKWordLeader.h>
00042 
00043 #include "libmswrite.h"
00044 
00045 #include "mswriteexport.h"
00046 
00047 
00048 class MSWriteExportFactory : KGenericFactory <MSWriteExport, KoFilter>
00049 {
00050 public:
00051     MSWriteExportFactory () : KGenericFactory <MSWriteExport, KoFilter> ("kwordmswriteexport")
00052     {
00053     }
00054 
00055 protected:
00056     virtual void setupTranslations (void)
00057     {
00058         KGlobal::locale()->insertCatalogue ("kofficefilters");
00059     }
00060 };
00061 
00062 K_EXPORT_COMPONENT_FACTORY (libmswriteexport, MSWriteExportFactory ())
00063 
00064 
00065 class WRIDevice : public MSWrite::Device
00066 {
00067 private:
00068     FILE *m_outfp;
00069     long m_outfp_pos, m_outfp_eof;
00070 
00071 public:
00072     WRIDevice () : m_outfp (NULL), m_outfp_pos (0), m_outfp_eof (0)
00073     {
00074     }
00075 
00076     virtual ~WRIDevice ()
00077     {
00078         closeFile ();
00079     }
00080 
00081     bool openFile (const char *fileName)
00082     {
00083         m_outfp = fopen (fileName, "wb");
00084         if (!m_outfp)
00085         {
00086             error (MSWrite::Error::FileError, "could not open file for writing\n");
00087             return false;
00088         }
00089 
00090         return true;
00091     }
00092 
00093     bool closeFile (void)
00094     {
00095         if (m_outfp)
00096         {
00097             if (fclose (m_outfp))
00098             {
00099                 error (MSWrite::Error::FileError, "could not close output file\n");
00100                 return false;
00101             }
00102 
00103             m_outfp = NULL;
00104         }
00105 
00106         return true;
00107     }
00108 
00109     bool read (MSWrite::Byte *, const MSWrite::DWord)
00110     {
00111         error (MSWrite::Error::InternalError, "reading from an output file?\n");
00112         return false;
00113     }
00114 
00115     bool write (const MSWrite::Byte *buf, const MSWrite::DWord numBytes)
00116     {
00117         size_t cwrite = fwrite (buf, 1, numBytes, m_outfp);
00118         if (cwrite != numBytes)
00119         {
00120             error (MSWrite::Error::FileError, "could not write to output file\n");
00121             return false;
00122         }
00123 
00124         // keep track of where we are up to in the output file and where EOF is
00125         m_outfp_pos += numBytes;
00126         if (m_outfp_pos > m_outfp_eof)
00127             m_outfp_eof = m_outfp_pos;
00128 
00129         return true;
00130     }
00131 
00132     bool seek (const long offset, const int whence)
00133     {
00134         long absloc;
00135         switch (whence)
00136         {
00137         case SEEK_SET:
00138             absloc = offset;
00139             break;
00140         case SEEK_CUR:
00141             absloc = m_outfp_pos + offset;
00142             break;
00143         case SEEK_END:
00144             absloc = m_outfp_eof + offset;
00145             break;
00146         default:
00147             error (MSWrite::Error::InternalError, "invalid whence passed to WRIDevice::seek\n");
00148             return false;
00149         }
00150 
00151         if (absloc > m_outfp_eof)
00152         {
00153             kdDebug (30509) << "Want to seek to " << absloc
00154                                     << " but EOF is at " << m_outfp_eof
00155                                     << "; so writing " << absloc - m_outfp_eof
00156                                     << " zeros" << endl;
00157 
00158             if (fseek (m_outfp, m_outfp_eof, SEEK_SET))
00159             {
00160                 error (MSWrite::Error::FileError,
00161                             "could not seek to EOF in output file\n");
00162                 return false;
00163             }
00164 
00165             MSWrite::Byte *zero = new MSWrite::Byte [absloc - m_outfp_eof];
00166             if (!zero)
00167             {
00168                 error (MSWrite::Error::OutOfMemory,
00169                             "could not allocate memory for zeros\n");
00170                 return false;
00171             }
00172             memset (zero, 0, absloc - m_outfp_eof);
00173             if (!write (zero, absloc - m_outfp_eof)) return false;
00174             delete [] zero;
00175 
00176             m_outfp_eof = absloc;
00177             m_outfp_pos = absloc;
00178             return true;
00179         }
00180         else
00181         {
00182             if (fseek (m_outfp, offset, whence) == 0)
00183             {
00184                 m_outfp_pos = absloc;
00185                 return true;
00186             }
00187             else
00188             {
00189                 error (MSWrite::Error::FileError, "could not seek output file\n");
00190                 return false;
00191             }
00192         }
00193     }
00194 
00195     long tell (void)
00196     {
00197         return ftell (m_outfp);
00198     }
00199 
00200     void debug (const char *s)
00201     {
00202         kdDebug (30509) << s;
00203     }
00204     void debug (const int i)
00205     {
00206         kdDebug (30509) << i;
00207     }
00208     void error (const int errorCode, const char *message,
00209                     const char * /*file*/ = "", const int /*lineno*/ = 0,
00210                     MSWrite::DWord /*token*/ = MSWrite::Device::NoToken)
00211     {
00212         if (errorCode == MSWrite::Error::Warn)
00213             kdWarning (30509) << message;
00214         else
00215         {
00216             m_error = errorCode;
00217             kdError (30509) << message;
00218         }
00219     }
00220 };
00221 
00222 
00223 class KWordMSWriteWorker : public KWEFBaseWorker
00224 {
00225 private:
00226     WRIDevice *m_device;
00227     MSWrite::InternalGenerator *m_generator;
00228 
00229     MSWrite::PageLayout m_pageLayout;
00230     MSWrite::Word m_pageHeight, m_pageWidth,
00231                         m_topMargin, m_leftMargin, m_bottomMargin, m_rightMargin;
00232     MSWrite::Word m_pageNumberStart;
00233 
00234     // for charset conversion
00235     QTextCodec *m_codec;
00236     QTextEncoder *m_encoder;
00237 
00238     QValueList <HeaderData> m_headerData;
00239     QValueList <FooterData> m_footerData;
00240 
00241     int m_headerType, m_footerType;
00242     bool m_hasHeader, m_isHeaderOnFirstPage;
00243     bool m_hasFooter, m_isFooterOnFirstPage;
00244 
00245     enum inWhatPossiblities
00246     {
00247         Nothing,
00248         Header,
00249         Footer,
00250         Body
00251     } m_inWhat;
00252 
00253 public:
00254     KWordMSWriteWorker () : m_device (NULL), m_generator (NULL),
00255                                     m_pageHeight (0xFFFF), m_pageWidth (0xFFFF),
00256                                     m_topMargin (0xFFFF), m_leftMargin (0xFFFF),
00257                                     m_bottomMargin (0xFFFF), m_rightMargin (0xFFFF),
00258                                     m_encoder (NULL),
00259                                     m_hasHeader (false), m_hasFooter (false),
00260                                     m_inWhat (Nothing)
00261     {
00262         // just select windows-1252 until the "Select Encoding" dialog works
00263         m_codec = QTextCodec::codecForName ("CP 1252");
00264 
00265         if (m_codec)
00266             m_encoder = m_codec->makeEncoder();
00267         else
00268             kdWarning (30509) << "Cannot convert to Win Charset!" << endl;
00269 
00270         m_device = new WRIDevice;
00271         if (!m_device)
00272         {
00273             kdError (30509) << "Could not allocate memory for Device" << endl;
00274             return;
00275         }
00276 
00277         m_generator = new MSWrite::InternalGenerator;
00278         if (!m_generator)
00279         {
00280             m_device->error (MSWrite::Error::OutOfMemory, "could not allocate memory for InternalGenerator\n");
00281             return;
00282         }
00283 
00284         m_generator->setDevice (m_device);
00285     }
00286 
00287     virtual ~KWordMSWriteWorker ()
00288     {
00289         delete m_generator;
00290         delete m_device;
00291         delete m_encoder;
00292     }
00293 
00294     int getError (void) const
00295     {
00296         return m_device->bad ();
00297     }
00298 
00299     bool doOpenFile (const QString &outFileName, const QString &)
00300     {
00301         // constructor failed?
00302         if (!m_device || !m_generator)
00303             return false;
00304 
00305         if (!m_device->openFile (QFile::encodeName (outFileName))) return false;
00306 
00307         return true;
00308     }
00309 
00310     bool doCloseFile (void)
00311     {
00312         if (!m_device->closeFile ()) return false;
00313         return true;
00314     }
00315 
00316     bool doOpenDocument (void)
00317     {
00318         kdDebug (30509) << "doOpenDocument ()" << endl;
00319 
00320         // We can't open the document here because we don't yet have
00321         // PageLayout * as doFullPaperFormat() and doFullPaperBorders()
00322         // haven't been called yet.
00323         //
00324         // doTrulyOpenDocument truly opens the document and is called by
00325         // doOpenBody()
00326 
00327         return true;
00328     }
00329 
00330     bool doTrulyOpenDocument (void)
00331     {
00332         // TODO: Write's UI doesn't allow the user to change Height or Width so
00333         //       setting it here might not be a good idea...
00334         m_pageLayout.setPageHeight (m_pageHeight);
00335         m_pageLayout.setPageWidth (m_pageWidth);
00336         m_pageLayout.setPageNumberStart (m_pageNumberStart);
00337         m_pageLayout.setTopMargin (m_topMargin);
00338         m_pageLayout.setLeftMargin (m_leftMargin);
00339         m_pageLayout.setTextHeight (m_pageHeight - m_topMargin - m_bottomMargin);
00340         m_pageLayout.setTextWidth (m_pageWidth - m_leftMargin - m_rightMargin);
00341 
00342         // TODO: libexport
00343         // headerFromTop
00344         // footerFromTop
00345 
00346         if (!m_generator->writeDocumentBegin (MSWrite::Format::Write_3_0,
00347                                                             &m_pageLayout)) return false;
00348 
00349         return true;
00350     }
00351 
00352     bool doCloseDocument (void)
00353     {
00354         kdDebug (30509) << "doCloseDocument ()" << endl;
00355 
00356         if (!m_generator->writeDocumentEnd (MSWrite::Format::Write_3_0,
00357                                                         &m_pageLayout)) return false;
00358 
00359         return true;
00360     }
00361 
00362     bool doFullPaperFormat (const int format,
00363                                     const double width, const double height,
00364                                     const int orientation)
00365     {
00366         kdDebug (30509) << "doFullPaperFormat ("
00367                                 << format << ", "
00368                                 << width << ", "
00369                                 << height << ", "
00370                                 << orientation << ")" << endl;
00371 
00372         // TODO: does "format" or "orientation" matter?
00373 
00374         m_pageHeight = MSWrite::Word (Point2Twip (height));
00375         m_pageWidth = MSWrite::Word (Point2Twip (width));
00376 
00377         return true;
00378     }
00379 
00380     bool doFullPaperBorders (const double top, const double left,
00381                                         const double bottom, const double right)
00382     {
00383         kdDebug (30509) << "doFullPaperBorders ("
00384                                 << top << ", "
00385                                 << left << ", "
00386                                 << bottom << ", "
00387                                 << right << ")" << endl;
00388 
00389         m_topMargin = MSWrite::Word (Point2Twip (top));
00390         m_leftMargin = MSWrite::Word (Point2Twip (left));
00391         m_bottomMargin = MSWrite::Word (Point2Twip (bottom));
00392         m_rightMargin = MSWrite::Word (Point2Twip (right));
00393 
00394         return true;
00395     }
00396 
00397     bool doVariableSettings (const VariableSettingsData &varSettings)
00398     {
00399         m_pageNumberStart = MSWrite::Word (varSettings.startingPageNumber);
00400 
00401         kdDebug (30509) << "doVariableSettings pageNumberStart="
00402                                 << m_pageNumberStart << endl;
00403         return true;
00404     }
00405 
00406     // In Write the header/footer must be the same for every page except that
00407     // you can choose to not display it on the first page
00408     //
00409     // /*This filter aims to be as lossless as possible so if we can't
00410     // accommodate the types of headers/footers found in KWord, we at least
00411     // print out the paragraphs in the body*/
00412     //
00413     //    Not anymore. Dumping headers & footers in the body didn't
00414     //    turn out to be that useful, nor was it expected by users.
00415     //
00416     bool doPageInfo (int headerType, int footerType)
00417     {
00418         kdDebug (30509) << "doPageInfo (headerType=" << headerType
00419                                 << ", footerType=" << footerType
00420                                 << ")" << endl;
00421 
00422         m_headerType = headerType;
00423         switch (headerType)
00424         {
00425         case 0: // same on all pages
00426         case 3: // different on even and odd pages
00427             m_isHeaderOnFirstPage = true;
00428             break;
00429         case 1: // different on first, even and odd pages
00430         case 2: // different on first and other pages
00431             m_isHeaderOnFirstPage = false;
00432             break;
00433         default:
00434             kdWarning (30509) << "Unknown headerType: " << headerType << endl;
00435             m_isHeaderOnFirstPage = false;  // just a guess
00436             break;
00437         }
00438 
00439         m_footerType = footerType;
00440         switch (footerType)
00441         {
00442         case 0: // same on all pages
00443         case 3: // different on even and odd pages
00444             m_isFooterOnFirstPage = true;
00445             break;
00446         case 1: // different on first, even and odd pages
00447         case 2: // different on first and other pages
00448             m_isFooterOnFirstPage = false;
00449             break;
00450         default:
00451             kdWarning (30590) << "Unknown footerType: " << footerType << endl;
00452             m_isFooterOnFirstPage = false;  // just a guess
00453             break;
00454         }
00455 
00456         return true;
00457     }
00458 
00459     bool isParaListEmpty (const QValueList <ParaData> &para)
00460     {
00461         if (para.count () == 1)
00462         {
00463             if (para.first ().text.isEmpty ())
00464                 return true;
00465         }
00466 
00467         return false;
00468     }
00469 
00470     bool doHeader (const HeaderData &header)
00471     {
00472         kdDebug (30509) << "doHeader (header.page=" << header.page << ")" << endl;
00473 
00474         if (isParaListEmpty (header.para))
00475         {
00476             kdDebug (30509) << "\tEmpty, ignoring" << endl;
00477             return true;
00478         }
00479 
00480     #if 0
00481         switch (m_headerType)
00482         {
00483         case 0: // same on all pages
00484             if (header.page != HeaderData::PAGE_ALL)
00485                 return true;
00486             break;
00487         case 3: // different on even and odd pages
00488             if (header.page != HeaderData::PAGE_ODD &&
00489                     header.page != HeaderData::PAGE_EVEN)
00490                 return true;
00491             break;
00492         case 1: // different on first, even and odd pages
00493             // accept everything
00494             break;
00495         case 2: // different on first and other pages
00496             if (header.page != HeaderData::PAGE_FIRST &&
00497                     header.page != HeaderData::PAGE_ODD)
00498                 return true;
00499             break;
00500         }
00501     #endif
00502 
00503         m_hasHeader = true;
00504         m_headerData.push_back (header);
00505         return true;
00506     }
00507 
00508     bool doFooter (const FooterData &footer)
00509     {
00510         kdDebug (30509) << "doFooter (footer.page=" << footer.page << ")" << endl;
00511 
00512         if (isParaListEmpty (footer.para))
00513         {
00514             kdDebug (30509) << "\tEmpty, ignoring" << endl;
00515             return true;
00516         }
00517 
00518     #if 0
00519         switch (m_footerType)
00520         {
00521         case 0: // same on all pages
00522             if (footer.page != FooterData::PAGE_ALL)
00523                 return true;
00524             break;
00525         case 3: // different on even and odd pages
00526             if (footer.page != FooterData::PAGE_ODD &&
00527                     footer.page != FooterData::PAGE_EVEN)
00528                 return true;
00529             break;
00530         case 1: // different on first, even and odd pages
00531             // accept everything
00532             break;
00533         case 2: // different on first and other pages
00534             if (footer.page != FooterData::PAGE_FIRST &&
00535                     footer.page != FooterData::PAGE_ODD)
00536                 return true;
00537             break;
00538         }
00539     #endif
00540 
00541         m_hasFooter = true;
00542         m_footerData.push_back (footer);
00543         return true;
00544     }
00545 
00546     bool doOpenBody (void)
00547     {
00548         kdDebug (30509) << "doOpenBody ()" << endl;
00549 
00550         //
00551         // Document Start
00552         //
00553         if (!doTrulyOpenDocument ()) return false;
00554 
00555 
00556         //
00557         // Footers followed by Headers (in this order)
00558         //
00559 
00560         bool wroteFooter = false;
00561         m_inWhat = Footer;
00562 
00563         for (QValueList <FooterData>::Iterator it = m_footerData.begin ();
00564                 it != m_footerData.end ();
00565                 it++)
00566         {
00567             if ((*it).page != FooterData::PAGE_FIRST)
00568             {
00569                 if (!wroteFooter)
00570                 {
00571                     if (!m_generator->writeFooterBegin ()) return false;
00572                     wroteFooter = true;
00573                 }
00574 
00575                 if (!doFullParagraphList ((*it).para)) return false;
00576                 it = --m_footerData.erase (it);
00577             }
00578         }
00579         if (wroteFooter)
00580             if (!m_generator->writeFooterEnd ()) return false;
00581 
00582         bool wroteHeader = false;
00583         m_inWhat = Header;
00584 
00585         for (QValueList <HeaderData>::Iterator it = m_headerData.begin ();
00586             it != m_headerData.end ();
00587             it++)
00588         {
00589             if ((*it).page != HeaderData::PAGE_FIRST)
00590             {
00591                 if (!wroteHeader)
00592                 {
00593                     if (!m_generator->writeHeaderBegin ()) return false;
00594                     wroteHeader = true;
00595                 }
00596 
00597                 if (!doFullParagraphList ((*it).para)) return false;
00598                 it = --m_headerData.erase (it);
00599             }
00600         }
00601         if (wroteHeader)
00602             if (!m_generator->writeHeaderEnd ()) return false;
00603 
00604 
00605         //
00606         // Body Start
00607         //
00608 
00609         m_inWhat = Body;
00610         if (!m_generator->writeBodyBegin ()) return false;
00611         // KWord doesn't have a PageTable but we must emit the pageNew
00612         // signal at least once
00613         if (!m_generator->writePageNew ()) return false;
00614 
00615 #if 0
00616         // dump remaining header paragraphs at the start of the body
00617         for (QValueList <HeaderData>::Iterator it = m_headerData.begin ();
00618                 it != m_headerData.end ();
00619                 it++)
00620         {
00621             kdDebug (30509) << "BODY START ADDING HEADER: " << (*it).page << endl;
00622             if (!doFullParagraphList ((*it).para)) return false;
00623             it = --m_headerData.erase (it);
00624         }
00625 
00626         // dump remaining footer paragraphs too
00627         for (QValueList <FooterData>::Iterator it = m_footerData.begin ();
00628                 it != m_footerData.end ();
00629                 it++)
00630         {
00631             kdDebug (30509) << "BODY START ADDING FOOTER: " << (*it).page << endl;
00632             if (!doFullParagraphList ((*it).para)) return false;
00633             it = --m_footerData.erase (it);
00634         }
00635 #endif
00636 
00637         return true;
00638     }
00639 
00640     bool doCloseBody (void)
00641     {
00642         kdDebug (30509) << "doCloseBody ()" << endl;
00643 
00644         if (!m_generator->writeBodyEnd ()) return false;
00645 
00646         return true;
00647     }
00648 
00649     // device that can either read from or write to a QBuffer
00650     // (but not both at the same time, please :))
00651     class QBufferDevice : public MSWrite::Device
00652     {
00653     private:
00654         QBuffer *m_buffer;
00655 
00656     public:
00657         QBufferDevice (QBuffer *buffer)
00658         {
00659             m_buffer = buffer;
00660         }
00661 
00662         bool read (MSWrite::Byte *buf, const MSWrite::DWord numBytes)
00663         {
00664             if (m_buffer->readBlock ((char *) buf, (Q_ULONG) numBytes) != Q_LONG (numBytes))
00665             {
00666                 error (MSWrite::Error::FileError, "could not read from QBuffer (not really a FileError)\n");
00667                 return false;
00668             }
00669             else
00670                 return true;
00671         }
00672 
00673         bool write (const MSWrite::Byte *buf, const MSWrite::DWord numBytes)
00674         {
00675             if (m_buffer->writeBlock ((char *) buf, (Q_ULONG) numBytes) != Q_LONG (numBytes))
00676             {
00677                 error (MSWrite::Error::FileError, "could not write to QBuffer (not really a FileError)\n");
00678                 return false;
00679             }
00680             else
00681                 return true;
00682         }
00683 
00684         // normally we must write zeros if we seek past EOF
00685         // but we know that won't happen :)
00686         bool seek (const long offset, const int whence)
00687         {
00688             long absoffset;
00689             switch (whence)
00690             {
00691             case SEEK_SET:
00692                 absoffset = offset;
00693                 break;
00694             case SEEK_CUR:
00695                 absoffset = m_buffer->at () + offset;
00696                 break;
00697             case SEEK_END:
00698                 absoffset = m_buffer->size () + offset;
00699                 break;
00700             default:
00701                 error (MSWrite::Error::InternalError, "unknown seek\n");
00702                 return false;
00703             }
00704 
00705             if (absoffset > long (m_buffer->size ()))
00706             {
00707                 error (MSWrite::Error::InternalError, "seek past EOF unimplemented\n");
00708                 return false;
00709             }
00710 
00711             if (!m_buffer->at (absoffset))
00712             {
00713                 error (MSWrite::Error::FileError, "QBuffer could not seek (not really a FileError)\n");
00714                 return false;
00715             }
00716 
00717             return true;
00718         }
00719 
00720         long tell (void)
00721         {
00722             return long (m_buffer->at ());
00723         }
00724 
00725         void debug (const char *s)
00726         {
00727             kdDebug (30509) << s;
00728         }
00729         void debug (const int i)
00730         {
00731             kdDebug (30509) << i;
00732         }
00733 
00734         void error (const int errorCode, const char *message,
00735                         const char * /*file*/ = "", const int /*lineno*/ = 0,
00736                         MSWrite::DWord /*token*/ = MSWrite::Device::NoToken)
00737         {
00738             if (errorCode == MSWrite::Error::Warn)
00739                 kdWarning (30509) << message;
00740             else
00741             {
00742                 m_error = errorCode;
00743                 kdError (30509) << message;
00744             }
00745         }
00746     };
00747 
00748     class WMFRecord : public MSWrite::NeedsDevice
00749     {
00750     public:
00751         static const int s_size = 6;
00752 
00753     protected:
00754         MSWrite::Byte m_data [s_size];
00755 
00756         MSWrite::DWord m_size;  // record size in Words including everything in this struct
00757         MSWrite::Word m_function;
00758 
00759         MSWrite::Short m_args [100];    // "ought to be enough for anybody"
00760         int m_argUpto;
00761 
00762     public:
00763         WMFRecord () : m_argUpto (0)
00764         {
00765         }
00766 
00767         WMFRecord (const MSWrite::DWord size, const MSWrite::Word function, MSWrite::Device *device)
00768                         : MSWrite::NeedsDevice (device),
00769                             m_size (size), m_function (function),
00770                             m_argUpto (0)
00771         {
00772         }
00773 
00774         MSWrite::DWord getSize (void) const {   return m_size;  }
00775         void setSize (const MSWrite::DWord size)    {   m_size = size;  }
00776 
00777         MSWrite::Word getFunction (void) const  {   return m_function;  }
00778         void setFunction (const MSWrite::Word function) {   m_function = function;  }
00779 
00780         void add (const MSWrite::Short arg)
00781         {
00782             m_args [m_argUpto++] = arg;
00783         }
00784 
00785         bool readFromDevice (void)
00786         {
00787             if (!m_device->readInternal (m_data, 6)) return false;
00788             MSWrite::ReadDWord (m_size, m_data + 0);
00789             MSWrite::ReadWord (m_function, m_data + 4);
00790             printf ("Size (Words): %i  Size (Bytes): %i  Function: %04X  (func=%02X,numArgs=%i)\n",
00791                         m_size, m_size * sizeof (MSWrite::Word), m_function, m_function & 255, m_function >> 8);
00792     #if 1
00793             if (m_function == 0 && m_size == 3) // last record
00794                 return false;
00795     #endif
00796 
00797             switch (m_function)
00798             {
00799             case 0x0103:
00800                 printf ("\tSetMapMode\n");
00801                 break;
00802             case 0x020c:
00803                 printf ("\tSetWindowExt\n");
00804                 break;
00805             case 0x020b:
00806                 printf ("\tSetWindowOrg\n");
00807                 break;
00808             case 0x0b41:
00809                 printf ("\tDibStretchBlt\n");
00810                 break;
00811             default:
00812                 printf ("\tUnknown function\n");
00813             }
00814 
00815             long offset = m_device->tellInternal ();
00816 
00817             for (int i = 0; i < ((m_function == 0x0b41) ? 10 : (m_function >> 8)); i++)
00818             {
00819                 MSWrite::Byte data [2];
00820                 if (!m_device->readInternal (data, 2)) return false;
00821                 MSWrite::ReadShort (m_args [i], data);
00822                 printf ("\tArg (rev) #%i=%i\n", i, m_args [i]);
00823             }
00824 
00825     #if 0
00826             // arguments are reversed normally
00827             int u = 0;
00828             for (int i = (m_function >> 8) - 1; i >= 0; i--, u++)
00829                 printf ("\tArg #%i=%u\n", u, m_args [i]);
00830     #endif
00831 
00832         #if 1
00833             if (m_function == 0xb41)
00834             {
00835                 // just curious but what's the infoHeader like?
00836                 MSWrite::BMP_BitmapInfoHeader bih;
00837                 bih.setDevice (m_device);
00838                 if (!bih.readFromDevice ()) return false;
00839             }
00840         #endif
00841 
00842             m_size *= sizeof (MSWrite::Word);   // in Bytes now
00843             m_size -= 6;    // skip past prefix
00844             printf (">>> At: %li  Next: %li\n", m_device->tellInternal (), offset + m_size);
00845             if (!m_device->seekInternal (offset + m_size, SEEK_SET)) return false;
00846             return true;
00847         }
00848 
00849         bool writeToDevice (void)
00850         {
00851             MSWrite::WriteDWord (m_size, m_data + 0);
00852             MSWrite::WriteWord (m_function, m_data + 4);
00853             if (!m_device->writeInternal (m_data, 6)) return false;
00854 
00855             for (int i = 0; i < ((m_function == 0x0B41) ? 10/*not 11*/ : (m_function >> 8)); i++)
00856             {
00857                 MSWrite::Byte data [2];
00858                 MSWrite::WriteShort (m_args [i], data);
00859                 if (!m_device->writeInternal (data, 2)) return false;
00860             }
00861 
00862             return true;
00863         }
00864     };
00865 
00866     // converts a DIB to a Standard WMF
00867     bool BMP2WMF (MSWrite::Device &readDevice, MSWrite::Device &writeDevice)
00868     {
00869         // read BMP's FileHeader
00870         MSWrite::BMP_BitmapFileHeader bfh;
00871         bfh.setDevice (&readDevice);
00872         if (!bfh.readFromDevice ()) return false;
00873 
00874         // WMF bitmap doesn't contain FileHeader
00875         MSWrite::DWord totalBytes = bfh.getTotalBytes () - MSWrite::BMP_BitmapFileHeader::s_size;
00876 
00877         // read BMP's InfoHeader to get some info...
00878         MSWrite::BMP_BitmapInfoHeader bih;
00879         bih.setDevice (&readDevice);
00880         if (!bih.readFromDevice ()) return false;
00881 
00882         // get some info about the image
00883         MSWrite::Long width = bih.getWidth ();
00884         MSWrite::Long height = bih.getHeight ();
00885 
00886 
00887         // Note: not from LibMSWrite's wmf.cpp
00888         kdDebug (30509) << "\t\tBIH: width(pt)=" << width
00889                                 << " height(pt)=" << height
00890                                 << " BPP=" << bih.getBitsPerPixel ()
00891                                 << endl;
00892         kdDebug (30509) << "\t\tBIH: xPixelsPerMeter=" << bih.getXPixelsPerMeter ()
00893                                 << " yPixelsPerMeter=" << bih.getYPixelsPerMeter ()
00894                                 << endl;
00895 
00896 
00897         // write WMF Header
00898         MSWrite::WMFHeader wmfHeader;
00899         wmfHeader.setDevice (&writeDevice);
00900 
00901         MSWrite::DWord maxRecordSizeBytes
00902             = (totalBytes
00903                 + 10 * sizeof (MSWrite::Word)/*parameters for DibStretchBlt*/
00904                 + WMFRecord::s_size);
00905         wmfHeader.setFileSize ((MSWrite::WMFHeader::s_size
00906                                         + WMFRecord::s_size + 1 * sizeof (MSWrite::Word)/*SetMapMode*/
00907                                         + WMFRecord::s_size + 2 * sizeof (MSWrite::Word)/*SetWindowExt*/
00908                                         + WMFRecord::s_size + 2 * sizeof (MSWrite::Word)/*SetWindowOrg*/
00909                                         + maxRecordSizeBytes/*DibStretchBlt*/
00910                                         + WMFRecord::s_size/*Sentinel*/)
00911                                             / sizeof (MSWrite::Word));
00912         wmfHeader.setMaxRecordSize (maxRecordSizeBytes / sizeof (MSWrite::Word));
00913         if (!wmfHeader.writeToDevice ()) return false;
00914 
00915         WMFRecord wmfRecordSetMapMode (4/*(Words)*/, 0x0103, &writeDevice);
00916         wmfRecordSetMapMode.add (8/*MM_ANISOTROPIC*/);
00917         if (!wmfRecordSetMapMode.writeToDevice ()) return false;
00918 
00919         WMFRecord wmfRecordSetWindowExt (5/*(Words)*/, 0x020C, &writeDevice);
00920         wmfRecordSetWindowExt.add (-height);
00921         wmfRecordSetWindowExt.add (width);
00922         if (!wmfRecordSetWindowExt.writeToDevice ()) return false;
00923 
00924         WMFRecord wmfRecordSetWindowOrg (5/*(Words)*/, 0x020B, &writeDevice);
00925         wmfRecordSetWindowOrg.add (0);
00926         wmfRecordSetWindowOrg.add (0);
00927         if (!wmfRecordSetWindowOrg.writeToDevice ()) return false;
00928 
00929         WMFRecord wmfRecordBMP (maxRecordSizeBytes / sizeof (MSWrite::Word),
00930                                         0x0B41/*DibStretchBlt*/,
00931                                         &writeDevice);
00932         wmfRecordBMP.add (32);  // ?
00933         wmfRecordBMP.add (204); // ?
00934         wmfRecordBMP.add (height);  // src height
00935         wmfRecordBMP.add (width);   // src width
00936         wmfRecordBMP.add (0);   // src y
00937         wmfRecordBMP.add (0);   // src x
00938         wmfRecordBMP.add (-height); // dest height
00939         wmfRecordBMP.add (width);   // dest width
00940         wmfRecordBMP.add (0);   // dest y
00941         wmfRecordBMP.add (0);   // dest x
00942         if (!wmfRecordBMP.writeToDevice ()) return false;
00943 
00944         // write BMP InfoHeader back to the device
00945         bih.setDevice (&writeDevice);
00946         if (!bih.writeToDevice ()) return false;
00947 
00948         long left = totalBytes - MSWrite::BMP_BitmapInfoHeader::s_size;
00949         while (left)
00950         {
00951             MSWrite::Byte data [1024];
00952             long amountToRead = left > 1024 ? 1024 : left;
00953             if (!readDevice.readInternal (data, amountToRead)) return false;
00954             if (!writeDevice.writeInternal (data, amountToRead)) return false;
00955 
00956             left -= amountToRead;
00957         }
00958 
00959         WMFRecord wmfRecordSentinel (3/*(Words)*/, 0x0000, &writeDevice);
00960         if (!wmfRecordSentinel.writeToDevice ()) return false;
00961 
00962     #if 1
00963         // bug with Word97?
00964         MSWrite::Byte zero = 0;
00965         if (!writeDevice.writeInternal (&zero, sizeof (MSWrite::Byte))) return false;
00966     #endif
00967 
00968         return true;
00969     }
00970 
00971     // all windows measurements depend on there being 72 dots/points per inch
00972     static double getDimen72DPI (const int measurement, const int dotsPerMeter)
00973     {
00974         kdDebug (30509) << "\t\tgetDimen72DPI (measurement=" << measurement
00975                                 << ",dotsPerMeter=" << dotsPerMeter << ")" << endl;
00976 
00977         // Can't get resolution?
00978         // Assume that we are already 72dpi
00979         if (dotsPerMeter <= 0)
00980             return double (measurement);
00981 
00982         // 2834.65 = 100 / 2.54 * 72
00983         return double (measurement) * 2834.65 / double (dotsPerMeter);
00984     }
00985 
00986     //
00987     // Note: if we suffer from a conversion error in this function (and can't
00988     // export the image), we _still_ return true, not false because there is
00989     // nothing worse than a filter that aborts due to its own incompetence [1]
00990     // (don't flame the author please, just blame the function :)).  But if we
00991     // do suffer from a file-like error, we abort right away because something
00992     // bad (memory corruption, internal error...) is happening!
00993     //
00994     // Why then _do_ we abort on text errors when images tell a thousand
00995     // words?  Because this saying is wrong and text is probably more
00996     // important to the user.
00997     //
00998     // [1] yes, "worse than an itch you can't scratch"
00999     //
01000     bool processImage (const FrameAnchor &frameAnchor,
01001                                 const MSWrite::FormatParaProperty *paraPropIn,
01002                                 const MSWrite::FormatCharProperty *charPropIn,
01003                                 const bool ignoreIndent)
01004     {
01005         kdDebug (30509) << "--------------------------" << endl
01006                                 << "processImage()" << endl;
01007 
01008 
01009         // Write supports images in 3 formats:
01010         //
01011         // 1. Monochrome BMP (not very useful)
01012         // 2. OLE (hard to work with and only supported in ver >= 3.1)
01013         // 3. Standard WMF
01014         //
01015         // So we convert all images to WMF for convenience.
01016         // We don't even bother saving Monochrome BMPs "as is" because
01017         // there's no point (just save it in WMF to make life easier)
01018         //
01019         // But a Standard WMF is basically a BMP with some headers/GDI calls
01020         // so the conversion process is like this:
01021         //
01022         // start->WMF->finish
01023         // start->BMP->WMF->finish
01024         // start->???->BMP->WMF->finish
01025         //
01026 
01027         double imageActualWidth = -1, imageActualHeight = -1;
01028         MSWrite::DWord imageSize = 0;
01029 
01030         QString imageType;
01031         int pos = frameAnchor.picture.koStoreName.findRev ('.');
01032         if (pos != -1) imageType = frameAnchor.picture.koStoreName.mid (pos).lower ();
01033         kdDebug (30509) << "\timageType: " << imageType << endl;
01034 
01035         QByteArray imageData;
01036         kdDebug (30509) << "\tReading image: " << frameAnchor.picture.koStoreName << endl;
01037         if (!loadSubFile (frameAnchor.picture.koStoreName, imageData))
01038             ErrorAndQuit (MSWrite::Error::FileError, "could not open image from store\n");
01039 
01040         // FSM
01041         for (;;)
01042         {
01043             if (imageType == ".wmf")
01044             {
01045                 imageSize = imageData.size ();
01046                 if (imageActualWidth == -1 && imageActualHeight == -1)
01047                 {
01048                     // load WMF
01049                     KoWmfPaint wmf;
01050                     if (!wmf.load (imageData))
01051                     {
01052                         kdError (30509) << "Could not open WMF - Invalid Format!" << endl;
01053                         return true;
01054                     }
01055 
01056                     // get raw dimensions
01057                     QRect dimen = wmf.boundingRect ();
01058                     int width = abs (dimen.width ());
01059                     int height = abs (dimen.height ());
01060                     kdDebug (30509) << "\tRaw WMF dimensions: " << width << "x" << height << endl;
01061 
01062                     if (wmf.isPlaceable ())
01063                     {
01064                         kdDebug (30509) << "\tConverting Placeable WMF" << endl;
01065 
01066                         // convert twip measurements that aren't in 72dpi
01067                         int defaultDpi = wmf.defaultDpi ();
01068                         if (defaultDpi <= 0)
01069                         {
01070                             kdWarning (30509) << "Invalid defaultDPI: " << defaultDpi << endl;
01071                             defaultDpi = 1440;
01072                         }
01073                         imageActualWidth = width * 1440 / defaultDpi;
01074                         imageActualHeight = height * 1440 / defaultDpi;
01075 
01076                         // Remove Aldus Placeable WMF Header
01077                         for (int i = 0; i < int (imageSize) - 22; i++)
01078                             imageData [i] = imageData [i + 22];
01079 
01080                         imageData.resize (imageSize - 22);
01081                         imageSize -= 22;
01082                     }
01083                     else if (wmf.isEnhanced ())
01084                     {
01085                         kdError (30509) << "Enhanced WMF unsupported by QWmf, internal error!" << endl;
01086 
01087                         return true;
01088                     }
01089                     // Standard WMF
01090                     else
01091                     {
01092                         kdDebug (30509) << "\tStandard WMF - no conversion required" << endl;
01093 
01094                         // assume width & height were in 72dpi points
01095                         imageActualWidth = Point2Twip (width);
01096                         imageActualHeight = Point2Twip (height);
01097                     }
01098                 }
01099 
01100                 kdDebug (30509) << "\tNow WMF: width=" << imageActualWidth
01101                                         << " height=" << imageActualHeight
01102                                         << " size=" << imageSize
01103                                         << endl;
01104 
01105                 // we're done!
01106                 break;
01107             }
01108             // TODO: DDB?
01109             else if (imageType == ".bmp")
01110             {
01111                 QImage image (imageData);
01112                 if (image.isNull ())
01113                 {
01114                     kdError (30509) << "QImage IsNull: Line=" << __LINE__ << endl;
01115                     return true;
01116                 }
01117 
01118                 if (imageActualWidth == -1 && imageActualHeight == -1)
01119                 {
01120                     imageActualWidth = Point2Twip (getDimen72DPI (image.width (), image.dotsPerMeterX ()));
01121                     imageActualHeight = Point2Twip (getDimen72DPI (image.height (), image.dotsPerMeterY ()));
01122                 }
01123 
01124                 kdDebug (30509) << "\tNow BMP: width=" << imageActualWidth
01125                                         << " height=" << imageActualHeight
01126                                         << " size=" << imageSize
01127                                         << endl;
01128 
01129                 QByteArray imageWMF;
01130                     // input device
01131                     QBuffer inBuffer (imageData);
01132                     inBuffer.open (IO_ReadOnly);
01133                     QBufferDevice inDevice (&inBuffer);
01134 
01135                     // output device
01136                     QBuffer outBuffer (imageWMF);
01137                     outBuffer.open (IO_WriteOnly);
01138                     QBufferDevice outDevice (&outBuffer);
01139 
01140                     // BMP --> WMF
01141                     if (!BMP2WMF (inDevice, outDevice))
01142                     {
01143                         kdError (30509) << "BMP to WMF conversion error" << endl;
01144                         return true;
01145                     }
01146 
01147                     outBuffer.close ();
01148                     inBuffer.close ();
01149                 imageData = imageWMF.copy ();
01150 
01151                 imageType = ".wmf";
01152             }
01153             else
01154             {
01155                 if (imageActualWidth == -1 && imageActualHeight == -1)
01156                 {
01157                     QImage image (imageData);
01158                     if (image.isNull())
01159                     {
01160                         kdError (30509) << "QImage isNull: Line=" << __LINE__ << endl;
01161                         return true;
01162                     }
01163 
01164                     imageActualWidth = Point2Twip (getDimen72DPI (image.width (), image.dotsPerMeterX ()));
01165                     imageActualHeight = Point2Twip (getDimen72DPI (image.height (), image.dotsPerMeterY ()));
01166                 }
01167 
01168                 kdDebug (30509) << "\tForeign format: width=" << imageActualWidth
01169                                         << " height=" << imageActualHeight
01170                                         << " size=" << imageSize
01171                                         << endl;
01172 
01173                 QByteArray imageBMP;
01174                     // input device
01175                     QBuffer inBuffer (imageData);
01176                     inBuffer.open (IO_ReadOnly);
01177 
01178                     // read foreign image
01179                     QImageIO imageIO (&inBuffer, NULL);
01180                     if (!imageIO.read ())
01181                     {
01182                         kdError (30509) << "Could not read foreign format" << endl;
01183                         return true;
01184                     }
01185 
01186                     // output device
01187                     QBuffer outBuffer (imageBMP);
01188                     outBuffer.open (IO_WriteOnly);
01189 
01190                     // write BMP
01191                     imageIO.setIODevice (&outBuffer);
01192                     imageIO.setFormat ("BMP");
01193                     if (!imageIO.write ())
01194                     {
01195                         kdError (30509) << "Could not convert to BMP" << endl;
01196                         return true;
01197                     }
01198 
01199                     outBuffer.close ();
01200                     inBuffer.close ();
01201                 imageData = imageBMP.copy ();
01202 
01203                 imageType = ".bmp";
01204             }
01205         }
01206 
01207 
01208         kdDebug (30509) << "\tActual dimensions: width=" << imageActualWidth
01209                                 << " height=" << imageActualHeight << endl;
01210 
01211         kdDebug (30509) << "\tKOffice position: left=" << frameAnchor.frame.left
01212                                 << " right=" << frameAnchor.frame.right
01213                                 << " top=" << frameAnchor.frame.top
01214                                 << " bottom=" << frameAnchor.frame.bottom
01215                                 << endl;
01216 
01217         kdDebug (30509) << "\tIndent=" << MSWrite::Word (Point2Twip (frameAnchor.frame.left)) - m_leftMargin << endl;
01218         if (ignoreIndent)
01219             kdDebug (30509) << "\t\tIgnoring indent - already exported at least one image in a KWord paragraph" << endl;
01220 
01221         double displayedWidth = Point2Twip (frameAnchor.frame.right - frameAnchor.frame.left + 1);
01222         double displayedHeight = Point2Twip (frameAnchor.frame.bottom - frameAnchor.frame.top + 1);
01223 
01224         kdDebug (30509) << "\tdisplayedWidth=" << displayedWidth
01225                                 << " displayedHeight=" << displayedHeight
01226                                 << endl;
01227 
01228 
01229         //
01230         // Start writing out the image now
01231         //
01232         // Note: here, we can start returning false again because the errors
01233         // won't be conversion-related
01234         //
01235 
01236         MSWrite::Image image;
01237         image.setIsWMF (true);
01238         if (!ignoreIndent)
01239         {
01240             if (paraPropIn->getAlignment () != MSWrite::Alignment::Centre)
01241                 image.setIndent (MSWrite::Word (Point2Twip (frameAnchor.frame.left)) - m_leftMargin);
01242             else
01243             {
01244                 // TODO: what is the image offset relative to (it's not always rel. to the left margin)?
01245                 kdDebug (30509) << "\tCentred paragraph, cannot position image" << endl;
01246             }
01247         }
01248 
01249         image.setOriginalWidth (imageActualWidth);
01250         image.setOriginalHeight (imageActualHeight);
01251         image.setDisplayedWidth (displayedWidth);
01252         image.setDisplayedHeight (displayedHeight);
01253         image.setExternalImageSize (imageSize);
01254 
01255         MSWrite::FormatParaProperty paraProp; paraProp = *paraPropIn;
01256         paraProp.setIsObject (true);
01257         paraProp.setLeftIndent (0); // not necessary but...
01258         if (!m_generator->writeParaInfoBegin (&paraProp, NULL, &image))
01259             return false;
01260 
01261         // yes, images have character formatting as well
01262         // (character formatting _must_ cover entire document)
01263         // (but I think it's ignored)
01264         MSWrite::FormatCharProperty charProp; charProp = *charPropIn;
01265         if (!m_generator->writeCharInfoBegin (&charProp))
01266             return false;
01267 
01268         // actually write image
01269         if (!m_generator->writeBinary ((const MSWrite::Byte *) (const char *) imageData.data (), imageSize)) return false;
01270 
01271         // 2nd argument endOfParagraph is ignored by InternalGenerator so
01272         // there's no need to specify it
01273         if (!m_generator->writeCharInfoEnd (&charProp, true))
01274             return false;
01275 ;
01276         if (!m_generator->writeParaInfoEnd (&paraProp, NULL, &image))
01277             return false;
01278 
01279 
01280         kdDebug (30509) << "processImage() successful!" << endl
01281                                 << "==========================" << endl
01282                                                                             << endl
01283                                                                             << endl;
01284         return true;
01285     }
01286 
01287     bool processTable (const Table &table)
01288     {
01289         // just dump the table out for now (no layout)
01290         for (QValueList <TableCell>::ConstIterator it = table.cellList.begin ();
01291                 it != table.cellList.end ();
01292                 it++)
01293         {
01294             if (!doFullParagraphList (*(*it).paraList)) return false;
01295         }
01296 
01297         return true;
01298     }
01299 
01300     bool processCounter (const CounterData &counter)
01301     {
01302         //kdDebug (30509) << "processCounter(counter.text=" << counter.text << ")" << endl;
01303 
01304         if (!counter.text.isEmpty ())
01305         {
01306             // isn't this wonderful? :)
01307             if (!processText (counter.text)) return false;
01308             if (!processText (" ")) return false;
01309         }
01310 
01311         return true;
01312     }
01313 
01314     #ifndef NDEBUG
01315         //#define KMF_DEBUG_FONT
01316     #endif
01317     void processFormatData (MSWrite::FormatCharProperty &charProp,
01318                                     const TextFormatting &f)
01319     {
01320         if (!f.fontName.isEmpty ())
01321         {
01322             // create new Font with Name
01323             MSWrite::Font font ((const MSWrite::Byte *) (const char *) f.fontName.utf8 ());
01324         #ifdef KMF_DEBUG_FONT
01325             kdDebug (30509) << "FontName " << f.fontName << endl;
01326         #endif
01327 
01328             // get Font Family
01329             QFont QTFontInfo (f.fontName);
01330             switch (QTFontInfo.styleHint ())
01331             {
01332             case QFont::Serif:
01333             #ifdef KMF_DEBUG_FONT
01334                 kdDebug (30509) << "FontFamily Serif" << endl;
01335             #endif
01336                 font.setFamily (MSWrite::Font::Roman);
01337                 break;
01338             case QFont::SansSerif:
01339             #ifdef KMF_DEBUG_FONT
01340                 kdDebug (30509) << "FontFamily SansSerif" << endl;
01341             #endif
01342                 font.setFamily (MSWrite::Font::Swiss);
01343                 break;
01344             case QFont::Courier:
01345             #ifdef KMF_DEBUG_FONT
01346                 kdDebug (30509) << "FontFamily Courier" << endl;
01347             #endif
01348                 font.setFamily (MSWrite::Font::Modern);
01349                 break;
01350             case QFont::OldEnglish:
01351             #ifdef KMF_DEBUG_FONT
01352                 kdDebug (30509) << "FontFamily OldEnglish" << endl;
01353             #endif
01354                 font.setFamily (MSWrite::Font::Decorative);
01355                 break;
01356             default:
01357             #ifdef KMF_DEBUG_FONT
01358                 kdDebug (30509) << "FontFamily DontKnow" << endl;
01359             #endif
01360                 // it's either DontCare or MSWrite::Font::Script
01361                 font.setFamily (MSWrite::Font::DontCare);
01362                 break;
01363             }
01364 
01365             charProp.setFont (&font);
01366         }
01367         if (f.fontSize > 0) charProp.setFontSize (f.fontSize);
01368 
01369         charProp.setIsItalic (f.italic);
01370         charProp.setIsUnderlined (f.underline); // TODO: underlineWord
01371         charProp.setIsBold (f.weight > (50/*normal*/ + 75/*bold*/) / 2);
01372 
01373         switch (f.verticalAlignment)
01374         {
01375         case 0: // normal
01376             charProp.setIsNormalPosition ();
01377             break;
01378         case 1: // subscript
01379             charProp.setIsSubscript ();
01380             break;
01381         case 2: // superscript
01382             charProp.setIsSuperscript ();
01383             break;
01384         }
01385 
01386         // TODO: fontAttribute;
01387     }
01388 
01389     static MSWrite::Word getClosestLineSpacing (const double points)
01390     {
01391         const double twips = Point2Twip (points);
01392 
01393     #if 1
01394         if (twips < double ((MSWrite::LineSpacing::Single + MSWrite::LineSpacing::OneAndAHalf) / 2))
01395             return MSWrite::LineSpacing::Single;
01396         else if (twips < double ((MSWrite::LineSpacing::OneAndAHalf + MSWrite::LineSpacing::Double) / 2))
01397             return MSWrite::LineSpacing::OneAndAHalf;
01398         else
01399             return MSWrite::LineSpacing::Double;
01400     #else   // or do we want a non-"standard" linespacing?
01401         return MSWrite::Word (twips);
01402     #endif
01403     }
01404 
01405     bool doFullParagraphList (const QValueList <ParaData> &paraList)
01406     {
01407         for (QValueList <ParaData>::ConstIterator it = paraList.begin ();
01408                 it != paraList.end ();
01409                 it ++)
01410         {
01411             if (!doFullParagraph (*it)) return false;
01412         }
01413 
01414         return true;
01415     }
01416 
01417     bool doFullParagraph (const ParaData &paraData)
01418     {
01419         return doFullParagraph (paraData.text,
01420                                         paraData.layout,
01421                                         paraData.formattingList);
01422     }
01423 
01424     bool doFullParagraph (const QString &paraText,
01425                                     const LayoutData &layout,
01426                                     const ValueListFormatData &paraFormatDataList)
01427     {
01428         MSWrite::FormatParaProperty paraProp;
01429 
01430         if (m_inWhat == Body)
01431             paraProp.setIsNormalParagraph (true);
01432         else
01433         {
01434             if (m_inWhat == Header)
01435             {
01436                 paraProp.setIsHeader (true);
01437                 paraProp.setIsOnFirstPage (m_isHeaderOnFirstPage);
01438             }
01439             else if (m_inWhat == Footer)
01440             {
01441                 paraProp.setIsFooter (true);
01442                 paraProp.setIsOnFirstPage (m_isFooterOnFirstPage);
01443             }
01444         }
01445 
01446         paraProp.setIsText (true);
01447 
01448         // Alignment
01449         if (!layout.alignment.isEmpty ())
01450         {
01451             if (layout.alignment == "left")
01452                 // quite useless since MSWrite::Alignment::Left is the default anyway
01453                 paraProp.setAlignment (MSWrite::Alignment::Left);
01454             else if (layout.alignment == "right")
01455                 paraProp.setAlignment (MSWrite::Alignment::Right);
01456             else if (layout.alignment == "center")
01457                 paraProp.setAlignment (MSWrite::Alignment::Center);
01458             else if (layout.alignment == "justify")
01459                 paraProp.setAlignment (MSWrite::Alignment::Justify);
01460             else
01461                 kdWarning (30509) << "Unknown Alignment: " << layout.alignment << endl;
01462         }
01463 
01464         // Indentation
01465         if (layout.indentFirst) paraProp.setLeftIndentFirstLine (MSWrite::Short (Point2Twip (layout.indentFirst)));
01466         if (layout.indentLeft >= 0) paraProp.setLeftIndent (MSWrite::Word (Point2Twip (layout.indentLeft)));
01467         if (layout.indentRight >= 0) paraProp.setRightIndent (MSWrite::Word (Point2Twip (layout.indentRight)));
01468     #if 0
01469         kdDebug (30509) << "Indent: " << Point2Twip (layout.indentFirst) << " "
01470                                                 << Point2Twip (layout.indentLeft) << " "
01471                                                 << Point2Twip (layout.indentRight) << endl;
01472     #endif
01473 
01474         // Line Spacing
01475         MSWrite::Word lineSpacing = MSWrite::LineSpacing::Normal;
01476         switch (layout.lineSpacingType)
01477         {
01478         case LayoutData::LS_SINGLE:
01479             lineSpacing = MSWrite::LineSpacing::Normal;
01480             break;
01481         case LayoutData::LS_ONEANDHALF:
01482             lineSpacing = MSWrite::LineSpacing::OneAndAHalf;
01483             break;
01484         case LayoutData::LS_DOUBLE:
01485             lineSpacing = MSWrite::LineSpacing::Double;
01486             break;
01487         case LayoutData::LS_CUSTOM:
01488         case LayoutData::LS_FIXED:
01489         case LayoutData::LS_ATLEAST:
01490             lineSpacing = getClosestLineSpacing (layout.lineSpacing);
01491             break;
01492         case LayoutData::LS_MULTIPLE:
01493             break;
01494         default:
01495             kdWarning (30509) << "unknown lineSpacingType \'" << layout.lineSpacingType << "\'" << endl;
01496         }
01497         paraProp.setLineSpacing (lineSpacing);
01498 
01499         // Tabs are a Document Property, not a Paragraph Property, in Write, yet are stored for each paragraph.
01500         // It seems that Write applies the 1st paragraph's Tabulator settings to the _entire_ document
01501         // Word97 and KWord, however, will treat them like a Paragraph Property
01502         int numTabs = 0;
01503         for (TabulatorList::ConstIterator tabIt = layout.tabulatorList.begin ();
01504                 tabIt != layout.tabulatorList.end ();
01505                 tabIt++)
01506         {
01507             MSWrite::FormatParaPropertyTabulator tab;
01508 
01509             // Write's UI only supports 12 as opposed to the 14 supposedly
01510             // supported in the file so let's play it safe and quit when
01511             // we reach 12
01512             // Actually, KWord's UI also only supports 12 so this should never be true
01513             if (numTabs >= 12)
01514             {
01515                 kdWarning (30509) << "Write does not support more 12 tabulators, not writing out all tabulators" << endl;
01516                 break;
01517             }
01518 
01519             // Write only supports Decimal and Left tabs
01520             // TODO: KOffice 1.3 alignchar (modify libexport)
01521             if ((*tabIt).m_type == 3 /* && (*tabIt).m_alignchar == '.' */)
01522                 tab.setIsDecimal ();
01523             else
01524                 tab.setIsNormal ();
01525 
01526             tab.setIndent (MSWrite::Word (Point2Twip ((*tabIt).m_ptpos)));
01527 
01528             // int m_filling;
01529             // double m_width;
01530             if ((*tabIt).m_filling != TabulatorData::TF_NONE)
01531                 kdWarning (30509) <<  "Write does not support Tabulator Filling" << endl;
01532 
01533             paraProp.addTabulator (&tab);
01534             numTabs++;
01535         }
01536 
01537         // TODO: double      marginTop;      // space before the paragraph  (a negative value means invalid)
01538         // TODO: double      marginBottom;   // space after the paragraph (a negative value means invalid)
01539 
01540         // TODO: QString     styleName;
01541         // TODO: QString     styleFollowing;
01542 
01543         if (!m_generator->writeParaInfoBegin (&paraProp)) return false;
01544 
01545         // get this paragraph's "default formatting"
01546         MSWrite::FormatCharProperty charPropDefault;
01547         processFormatData (charPropDefault, layout.formatData.text);
01548 
01549         MSWrite::DWord uptoByte = 0;    // relative to start of KWord paragraph
01550         MSWrite::DWord numBytes = paraText.length ();   // relative to start of KWord paragraph
01551 
01552         bool startOfWRIParagraph = true;
01553         bool exportedAtLeastOneImage = false;   // ...from the KWord paragraph
01554 
01555         // empty paragraph
01556         if (numBytes == 0)
01557         {
01558             //kdDebug (30509) << "Outputting empty paragraph!" << endl;
01559 
01560             // write default character property start
01561             if (!m_generator->writeCharInfoBegin (&charPropDefault)) return false;
01562 
01563             // page break at start of paragraph?
01564             if (layout.pageBreakBefore)
01565                 if (!m_generator->writePageBreak ()) return false;
01566 
01567             // counter data
01568             processCounter (layout.counter);
01569 
01570             // end of line
01571             if (!m_generator->writeCarriageReturn ()) return false;
01572             if (!m_generator->writeNewLine (true/*end of paragraph*/)) return false;
01573 
01574             // page break at end of paragraph?
01575             if (layout.pageBreakAfter)
01576                 if (!m_generator->writePageBreak ()) return false;
01577 
01578             // write default character property end
01579             if (!m_generator->writeCharInfoEnd (&charPropDefault, true)) return false;
01580         }
01581         else
01582         {
01583             for (ValueListFormatData::ConstIterator formatIt = paraFormatDataList.begin ();
01584                     formatIt != paraFormatDataList.end ();
01585                     formatIt++)
01586             {
01587                 bool textSegment = true;
01588 
01589                 // apply local <FORMAT> tag on top of "default formatting"
01590                 MSWrite::FormatCharProperty charProp; charProp = charPropDefault;
01591                 processFormatData (charProp, (*formatIt).text);
01592 
01593                 if (!m_generator->writeCharInfoBegin (&charProp)) return false;
01594 
01595                 if (uptoByte == 0)
01596                 {
01597                     // page break at start of paragraph?
01598                     if (layout.pageBreakBefore)
01599                         if (!m_generator->writePageBreak ()) return false;
01600 
01601                     // counter data
01602                     processCounter (layout.counter);
01603                 }
01604 
01605                 // yes, this is slightly premature but it doesn't matter
01606                 // ... just be careful when using uptoByte
01607                 uptoByte += (*formatIt).len;
01608 
01609                 switch ((*formatIt).id)
01610                 {
01611                 case 0: // none?
01612                     ErrorAndQuit (MSWrite::Error::InternalError, "Format ID = 0\n");
01613                 case 1: // text
01614                     if (!processText (paraText.mid ((*formatIt).pos, (*formatIt).len)))
01615                                             /*uptoByte == numBytes))*/
01616                             return false;
01617 
01618                     startOfWRIParagraph = false;
01619                     break;
01620                 case 2: // picture (deprecated)
01621                     m_device->error (MSWrite::Error::Warn, "Picture (deprecated) unsupported\n");
01622                     break;
01623                 case 3: // tabulator (deprecated)
01624                     m_device->error (MSWrite::Error::Warn, "Tabulator (deprecated) unsupported\n");
01625                     break;
01626                 case 4: // variable
01627                 {
01628                     bool justPrintText = true;
01629 
01630                     // Page Number / Number of Pages
01631                     if ((*formatIt).variable.m_type == 4 &&
01632                             (*formatIt).variable.isPageNumber () &&
01633                             m_inWhat != Body/*Write replaces it with '*' in the body*/)
01634                     {
01635                         if (!m_generator->writeCharInfoEnd (&charProp)) return false;
01636                         charProp.setIsPageNumber (true);
01637                         // if you don't do this it will print out the character literally (char 1)
01638                         if (!m_generator->writeCharInfoBegin (&charProp)) return false;
01639 
01640                         // only variable Write supports (and only in headers/footers)
01641                         // if you don't write out char 1, Write will not treat it as a
01642                         // variable anchor and will print the character out literally
01643                         if (!m_generator->writePageNumber ()) return false;
01644 
01645                         if (!m_generator->writeCharInfoEnd (&charProp)) return false;
01646                         charProp.setIsPageNumber (false);
01647                         if (!m_generator->writeCharInfoBegin (&charProp)) return false;
01648 
01649                         justPrintText = false;
01650                     }
01651 
01652                     if (justPrintText)
01653                     {
01654                         if (!processText ((*formatIt).variable.m_text))
01655                             return false;
01656                     }
01657 
01658                     startOfWRIParagraph = false;
01659                     break;
01660                 }
01661                 case 5: // footnote (KOffice 1.1)
01662                     m_device->error (MSWrite::Error::Warn, "Footnote unsupported\n");
01663                     break;
01664                 case 6: // anchor for frame
01665                     //
01666                     // Write does not support inline frames so:
01667                     //
01668                     // - we end the current paragraph
01669                     // - dump the inline frame in a paragraph of its own
01670                     // - continue with a new paragraph
01671                     //
01672 
01673                     if (!startOfWRIParagraph)
01674                     {
01675                         kdDebug (30509) << "Writing CRLF to end text paragraph" << endl;
01676 
01677                         // If you don't have CRLF at the end of the text
01678                         // paragraph, Write will think that the next paragraph
01679                         // (the image) is part of the text...
01680                         if (!m_generator->writeCarriageReturn ()) return false;
01681                         if (!m_generator->writeNewLine (true/*end of paragraph*/)) return false;
01682                     }
01683                     else
01684                         kdDebug (30509) << "Inline frame is anchored at start of paragraph, no CRLF" << endl;
01685 
01686                     if (!m_generator->writeCharInfoEnd (&charProp)) return false;
01687                     if (!m_generator->writeParaInfoEnd (&paraProp)) return false;
01688 
01689 
01690                     if ((*formatIt).frameAnchor.type == 6)
01691                     {
01692                         kdDebug (30509) << "Table detected" << endl;
01693 
01694                         // this will make its own paragraph(s)...
01695                         processTable ((*formatIt).frameAnchor.table);
01696 
01697                         // HACK: inline tables are flushed to the left and right
01698                         // margins, despite being inline, hence the next image
01699                         // indent will be sensible and should not be ignored.
01700                         kdDebug (30509) << "Table hack: resetting image-ignore-indent flag" << endl;
01701                         exportedAtLeastOneImage = false;
01702                     }
01703                     else if ((*formatIt).frameAnchor.type == 2)
01704                     {
01705                         kdDebug (30509) << "Image detected" << endl;
01706 
01707                         // this will make its own paragraph...
01708                         if (!processImage ((*formatIt).frameAnchor, &paraProp, &charProp,
01709                                                     exportedAtLeastOneImage)) return false;
01710 
01711                         exportedAtLeastOneImage = true;
01712                     }
01713                     else
01714                         kdWarning (30509) << "Unknown type of anchor: " << (*formatIt).frameAnchor.type << endl;
01715 
01716 
01717                     // recontinue paragraph
01718                     if (!m_generator->writeParaInfoBegin (&paraProp)) return false;
01719                     startOfWRIParagraph = true;
01720                     if (!m_generator->writeCharInfoBegin (&charProp)) return false;
01721 
01722                     textSegment = false;
01723                     break;
01724                 }
01725 
01726                 if (uptoByte == numBytes)
01727                 {
01728                     if (textSegment)
01729                     {
01730                         // end of line
01731                         if (!m_generator->writeCarriageReturn ()) return false;
01732                         if (!m_generator->writeNewLine (true/*end of paragraph*/)) return false;
01733                     }
01734 
01735                     // page break at end of paragraph?
01736                     if (layout.pageBreakAfter)
01737                         if (!m_generator->writePageBreak ()) return false;
01738                 }
01739 
01740                 if (!m_generator->writeCharInfoEnd (&charProp, uptoByte == numBytes)) return false;
01741             }
01742         }
01743 
01744         if (!m_generator->writeParaInfoEnd (&paraProp)) return false;
01745         //if (numBytes) kdDebug (30509) << "Just Output " << uptoByte << "/" << numBytes << " with text \'" << paraText.utf8 () << "\'" << endl;
01746 
01747         return true;
01748     }
01749 
01750     template <class dtype>
01751     dtype min (const dtype a, const dtype b, const dtype c)
01752     {
01753         if (a <= b && a <= c) return a;
01754         if (b <= a && b <= c) return b;
01755         return c;
01756     }
01757 
01758     #ifndef NDEBUG
01759         //#define DEBUG_PROCESS_TEXT
01760     #endif
01761     bool processText (const QString &stringUnicode)
01762     {
01763         //
01764         // Look out for characters in the string and emit signals as appropriate:
01765         //
01766         // 1  pageNumber (already taken care of as a variable)
01767         // 10 newLine
01768         // 13 carriageReturn (TODO: Oh no!  Can't happen!)
01769         // 12 pageBreak (TODO: we are in real trouble: this can actually happen without forcing a new paragraph!)
01770         // 31 optionalHyphen
01771         // ?  text
01772         //
01773 
01774         int softHyphen = -2, nonBreakingSpace = -2, newLine = -2;
01775 
01776         int upto = 0;
01777         int stringUnicodeLength = stringUnicode.length ();
01778         while (upto < stringUnicodeLength)
01779         {
01780             //
01781             // look for KWord's special characters as defined in the DTD
01782             //
01783 
01784             if (softHyphen == -2)
01785             {
01786                 softHyphen = stringUnicode.find (QChar (0xAD), upto);
01787                 if (softHyphen == -1) softHyphen = INT_MAX;
01788             }
01789 
01790             if (nonBreakingSpace == -2)
01791             {
01792                 nonBreakingSpace = stringUnicode.find (QChar (0xA0), upto);
01793                 if (nonBreakingSpace == -1) nonBreakingSpace = INT_MAX;
01794             }
01795 
01796             if (newLine == -2)
01797             {
01798                 newLine = stringUnicode.find (QChar ('\n'), upto);
01799                 if (newLine == -1) newLine = INT_MAX;
01800             }
01801 
01802             // look for the closest one
01803             int specialLocation = min (softHyphen, nonBreakingSpace, newLine);
01804 
01805             // get substring (either to the end of the original string or before
01806             // the next closest special character, if any)
01807             int length = stringUnicodeLength - upto;
01808             if (specialLocation != INT_MAX)
01809                 length = specialLocation - upto;
01810             QString substring = stringUnicode.mid (upto, length);
01811 
01812         #ifdef DEBUG_PROCESS_TEXT
01813             kdDebug (30509) << "Parent string:  upto=" << upto
01814                                     << ",length=" << stringUnicode.length () << endl;
01815             kdDebug (30509) << "Child string:   length=" << length
01816                                     << " (specialLoc=" << specialLocation << ")" << endl;
01817         #endif
01818 
01819             //
01820             // convert substring to windows-1251
01821             //
01822 
01823             QCString stringWin;
01824 
01825             // there is a codec, therefore there is an encoder...
01826             if (m_codec)
01827             {
01828                 int len;    // don't overwrite length, we need it later
01829 
01830                 // convert from Unicode (UTF8)
01831                 stringWin = m_encoder->fromUnicode (substring, len = length);
01832             }
01833             else
01834             {
01835                 // output a plain string still in wrong Character Set
01836                 // (hopefully the user won't notice)
01837                 stringWin = substring.utf8 ();
01838             }
01839 
01840 
01841             // output encoded text
01842             if (!m_generator->writeText ((const MSWrite::Byte *) (const char *) stringWin))
01843                 return false;
01844 
01845             upto += length;
01846 
01847             // special character?
01848             if (specialLocation != INT_MAX)
01849             {
01850             #ifdef DEBUG_PROCESS_TEXT
01851                 kdDebug (30509) << "Found special character!" << endl;
01852             #endif
01853 
01854                 // output special character
01855                 if (specialLocation == softHyphen)
01856                 {
01857                 #ifdef DEBUG_PROCESS_TEXT
01858                     kdDebug (30509) << "\tSoft Hyphen" << endl;
01859                 #endif
01860                     if (!m_generator->writeOptionalHyphen ()) return false;
01861                     softHyphen = -2;
01862                 }
01863                 else if (specialLocation == nonBreakingSpace)
01864                 {
01865                 #ifdef DEBUG_PROCESS_TEXT
01866                     kdDebug (30509) << "\tNon-breaking Space" << endl;
01867                 #endif
01868                     // don't think Write supports nonBreakingSpace
01869                     if (!m_generator->writeText ((const MSWrite::Byte *) " ")) return false;
01870                     nonBreakingSpace = -2;
01871                 }
01872                 else if (specialLocation == newLine)
01873                 {
01874                 #ifdef DEBUG_PROCESS_TEXT
01875                     kdDebug (30509) << "\tNew Line" << endl;
01876                 #endif
01877                     // \r\n, not just \n
01878                     if (!m_generator->writeCarriageReturn ()) return false;
01879                     if (!m_generator->writeNewLine (true/*InternalGenerator doesn't care*/)) return false;
01880                     newLine = -2;
01881                 }
01882                 else
01883                 {
01884                     ErrorAndQuit (MSWrite::Error::InternalError, "simply impossible specialLocation\n");
01885                 }
01886 
01887                 // skip past special character
01888                 upto++;
01889             }
01890         }   // while (upto < stringUnicodeLength)   {
01891 
01892         return true;
01893     }
01894 };
01895 
01896 
01897 MSWriteExport::MSWriteExport (KoFilter *, const char *, const QStringList &)
01898                     : KoFilter()
01899 {
01900 }
01901 
01902 MSWriteExport::~MSWriteExport ()
01903 {
01904 }
01905 
01906 KoFilter::ConversionStatus MSWriteExport::convert (const QCString &from, const QCString &to)
01907 {
01908     kdDebug (30509) << "MSWriteExport $Date: 2006-02-12 19:28:12 +0100 (dim, 12 fév 2006) $ using LibMSWrite "
01909                             << MSWrite::Version << endl;
01910 
01911     if (to != "application/x-mswrite" || from != "application/x-kword")
01912     {
01913         kdError (30509) << "Internal error!  Filter not implemented?" << endl;
01914         return KoFilter::NotImplemented;
01915     }
01916 
01917     KWordMSWriteWorker *worker = new KWordMSWriteWorker;
01918     if (!worker)
01919     {
01920         kdError (30509) << "Could not allocate memory for worker" << endl;
01921         return KoFilter::OutOfMemory;
01922     }
01923 
01924     KWEFKWordLeader *leader = new KWEFKWordLeader (worker);
01925     if (!leader)
01926     {
01927         kdError (30509) << "Could not allocate memory for leader" << endl;
01928         delete worker;
01929         return KoFilter::OutOfMemory;
01930     }
01931 
01932     KoFilter::ConversionStatus ret = leader->convert (m_chain, from, to);
01933     int errorCode = worker->getError ();
01934 
01935     delete leader;
01936     delete worker;
01937 
01938     // try to return somewhat more meaningful errors than KoFilter::StupidError
01939     // for the day that KOffice actually reports them to the user properly
01940     switch (errorCode)
01941     {
01942     case MSWrite::Error::Ok:
01943         kdDebug (30509) << "Returning error code " << ret << endl;
01944         return ret; // not KoFilter::OK in case KWEFKWordLeader wants to report something
01945 
01946     case MSWrite::Error::Warn:
01947         kdDebug (30509) << "Error::Warn" << endl;
01948         return KoFilter::InternalError; // warnings should _never_ set m_error
01949 
01950     case MSWrite::Error::InvalidFormat:
01951         kdDebug (30509) << "Error::InvalidFormat" << endl;
01952         return KoFilter::InternalError; // how can the file I'm _writing_ be of an invalid format?
01953 
01954     case MSWrite::Error::OutOfMemory:
01955         kdDebug (30509) << "Error::OutOfMemory" << endl;
01956         return KoFilter::OutOfMemory;
01957 
01958     case MSWrite::Error::InternalError:
01959         kdDebug (30509) << "Error::InternalError" << endl;
01960         return KoFilter::InternalError;
01961 
01962     case MSWrite::Error::Unsupported:
01963         kdDebug (30509) << "Error::Unsupported" << endl;
01964         return KoFilter::InternalError;
01965 
01966     case MSWrite::Error::FileError:
01967         kdDebug (30509) << "Error::FileError" << endl;
01968         return KoFilter::CreationError;
01969     }
01970 
01971     kdWarning (30509) << "Unknown error" << endl;
01972     return KoFilter::StupidError;
01973 }
01974 
01975 #include <mswriteexport.moc>
KDE Home | KDE Accessibility Home | Description of Access Keys