summaryrefslogtreecommitdiffstats
path: root/filters/kword/mswrite/mswriteexport.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'filters/kword/mswrite/mswriteexport.cpp')
-rw-r--r--filters/kword/mswrite/mswriteexport.cpp1975
1 files changed, 1975 insertions, 0 deletions
diff --git a/filters/kword/mswrite/mswriteexport.cpp b/filters/kword/mswrite/mswriteexport.cpp
new file mode 100644
index 000000000..abf007f88
--- /dev/null
+++ b/filters/kword/mswrite/mswriteexport.cpp
@@ -0,0 +1,1975 @@
+/* This file is part of the KDE project
+ Copyright (C) 2002-2003 Clarence Dang <dang@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License Version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License Version 2 for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ Version 2 along with this library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <tqbuffer.h>
+#include <tqcstring.h>
+#include <tqfile.h>
+#include <tqfont.h>
+#include <tqimage.h>
+#include <tqtextcodec.h>
+#include <tqvaluelist.h>
+#include <tqvaluestack.h>
+
+#include <kdebug.h>
+#include <kgenericfactory.h>
+
+#include <KoFilterChain.h>
+#include <kowmfpaint.h>
+
+#include <KWEFStructures.h>
+#include <KWEFBaseWorker.h>
+#include <KWEFKWordLeader.h>
+
+#include "libmswrite.h"
+
+#include "mswriteexport.h"
+
+
+class MSWriteExportFactory : KGenericFactory <MSWriteExport, KoFilter>
+{
+public:
+ MSWriteExportFactory () : KGenericFactory <MSWriteExport, KoFilter> ("kwordmswriteexport")
+ {
+ }
+
+protected:
+ virtual void setupTranslations (void)
+ {
+ TDEGlobal::locale()->insertCatalogue ("kofficefilters");
+ }
+};
+
+K_EXPORT_COMPONENT_FACTORY (libmswriteexport, MSWriteExportFactory ())
+
+
+class WRIDevice : public MSWrite::Device
+{
+private:
+ FILE *m_outfp;
+ long m_outfp_pos, m_outfp_eof;
+
+public:
+ WRIDevice () : m_outfp (NULL), m_outfp_pos (0), m_outfp_eof (0)
+ {
+ }
+
+ virtual ~WRIDevice ()
+ {
+ closeFile ();
+ }
+
+ bool openFile (const char *fileName)
+ {
+ m_outfp = fopen (fileName, "wb");
+ if (!m_outfp)
+ {
+ error (MSWrite::Error::FileError, "could not open file for writing\n");
+ return false;
+ }
+
+ return true;
+ }
+
+ bool closeFile (void)
+ {
+ if (m_outfp)
+ {
+ if (fclose (m_outfp))
+ {
+ error (MSWrite::Error::FileError, "could not close output file\n");
+ return false;
+ }
+
+ m_outfp = NULL;
+ }
+
+ return true;
+ }
+
+ bool read (MSWrite::Byte *, const MSWrite::DWord)
+ {
+ error (MSWrite::Error::InternalError, "reading from an output file?\n");
+ return false;
+ }
+
+ bool write (const MSWrite::Byte *buf, const MSWrite::DWord numBytes)
+ {
+ size_t cwrite = fwrite (buf, 1, numBytes, m_outfp);
+ if (cwrite != numBytes)
+ {
+ error (MSWrite::Error::FileError, "could not write to output file\n");
+ return false;
+ }
+
+ // keep track of where we are up to in the output file and where EOF is
+ m_outfp_pos += numBytes;
+ if (m_outfp_pos > m_outfp_eof)
+ m_outfp_eof = m_outfp_pos;
+
+ return true;
+ }
+
+ bool seek (const long offset, const int whence)
+ {
+ long absloc;
+ switch (whence)
+ {
+ case SEEK_SET:
+ absloc = offset;
+ break;
+ case SEEK_CUR:
+ absloc = m_outfp_pos + offset;
+ break;
+ case SEEK_END:
+ absloc = m_outfp_eof + offset;
+ break;
+ default:
+ error (MSWrite::Error::InternalError, "invalid whence passed to WRIDevice::seek\n");
+ return false;
+ }
+
+ if (absloc > m_outfp_eof)
+ {
+ kdDebug (30509) << "Want to seek to " << absloc
+ << " but EOF is at " << m_outfp_eof
+ << "; so writing " << absloc - m_outfp_eof
+ << " zeros" << endl;
+
+ if (fseek (m_outfp, m_outfp_eof, SEEK_SET))
+ {
+ error (MSWrite::Error::FileError,
+ "could not seek to EOF in output file\n");
+ return false;
+ }
+
+ MSWrite::Byte *zero = new MSWrite::Byte [absloc - m_outfp_eof];
+ if (!zero)
+ {
+ error (MSWrite::Error::OutOfMemory,
+ "could not allocate memory for zeros\n");
+ return false;
+ }
+ memset (zero, 0, absloc - m_outfp_eof);
+ if (!write (zero, absloc - m_outfp_eof)) return false;
+ delete [] zero;
+
+ m_outfp_eof = absloc;
+ m_outfp_pos = absloc;
+ return true;
+ }
+ else
+ {
+ if (fseek (m_outfp, offset, whence) == 0)
+ {
+ m_outfp_pos = absloc;
+ return true;
+ }
+ else
+ {
+ error (MSWrite::Error::FileError, "could not seek output file\n");
+ return false;
+ }
+ }
+ }
+
+ long tell (void)
+ {
+ return ftell (m_outfp);
+ }
+
+ void debug (const char *s)
+ {
+ kdDebug (30509) << s;
+ }
+ void debug (const int i)
+ {
+ kdDebug (30509) << i;
+ }
+ void error (const int errorCode, const char *message,
+ const char * /*file*/ = "", const int /*lineno*/ = 0,
+ MSWrite::DWord /*token*/ = MSWrite::Device::NoToken)
+ {
+ if (errorCode == MSWrite::Error::Warn)
+ kdWarning (30509) << message;
+ else
+ {
+ m_error = errorCode;
+ kdError (30509) << message;
+ }
+ }
+};
+
+
+class KWordMSWriteWorker : public KWEFBaseWorker
+{
+private:
+ WRIDevice *m_device;
+ MSWrite::InternalGenerator *m_generator;
+
+ MSWrite::PageLayout m_pageLayout;
+ MSWrite::Word m_pageHeight, m_pageWidth,
+ m_topMargin, m_leftMargin, m_bottomMargin, m_rightMargin;
+ MSWrite::Word m_pageNumberStart;
+
+ // for charset conversion
+ TQTextCodec *m_codec;
+ TQTextEncoder *m_encoder;
+
+ TQValueList <HeaderData> m_headerData;
+ TQValueList <FooterData> m_footerData;
+
+ int m_headerType, m_footerType;
+ bool m_hasHeader, m_isHeaderOnFirstPage;
+ bool m_hasFooter, m_isFooterOnFirstPage;
+
+ enum inWhatPossiblities
+ {
+ Nothing,
+ Header,
+ Footer,
+ Body
+ } m_inWhat;
+
+public:
+ KWordMSWriteWorker () : m_device (NULL), m_generator (NULL),
+ m_pageHeight (0xFFFF), m_pageWidth (0xFFFF),
+ m_topMargin (0xFFFF), m_leftMargin (0xFFFF),
+ m_bottomMargin (0xFFFF), m_rightMargin (0xFFFF),
+ m_encoder (NULL),
+ m_hasHeader (false), m_hasFooter (false),
+ m_inWhat (Nothing)
+ {
+ // just select windows-1252 until the "Select Encoding" dialog works
+ m_codec = TQTextCodec::codecForName ("CP 1252");
+
+ if (m_codec)
+ m_encoder = m_codec->makeEncoder();
+ else
+ kdWarning (30509) << "Cannot convert to Win Charset!" << endl;
+
+ m_device = new WRIDevice;
+ if (!m_device)
+ {
+ kdError (30509) << "Could not allocate memory for Device" << endl;
+ return;
+ }
+
+ m_generator = new MSWrite::InternalGenerator;
+ if (!m_generator)
+ {
+ m_device->error (MSWrite::Error::OutOfMemory, "could not allocate memory for InternalGenerator\n");
+ return;
+ }
+
+ m_generator->setDevice (m_device);
+ }
+
+ virtual ~KWordMSWriteWorker ()
+ {
+ delete m_generator;
+ delete m_device;
+ delete m_encoder;
+ }
+
+ int getError (void) const
+ {
+ return m_device->bad ();
+ }
+
+ bool doOpenFile (const TQString &outFileName, const TQString &)
+ {
+ // constructor failed?
+ if (!m_device || !m_generator)
+ return false;
+
+ if (!m_device->openFile (TQFile::encodeName (outFileName))) return false;
+
+ return true;
+ }
+
+ bool doCloseFile (void)
+ {
+ if (!m_device->closeFile ()) return false;
+ return true;
+ }
+
+ bool doOpenDocument (void)
+ {
+ kdDebug (30509) << "doOpenDocument ()" << endl;
+
+ // We can't open the document here because we don't yet have
+ // PageLayout * as doFullPaperFormat() and doFullPaperBorders()
+ // haven't been called yet.
+ //
+ // doTrulyOpenDocument truly opens the document and is called by
+ // doOpenBody()
+
+ return true;
+ }
+
+ bool doTrulyOpenDocument (void)
+ {
+ // TODO: Write's UI doesn't allow the user to change Height or Width so
+ // setting it here might not be a good idea...
+ m_pageLayout.setPageHeight (m_pageHeight);
+ m_pageLayout.setPageWidth (m_pageWidth);
+ m_pageLayout.setPageNumberStart (m_pageNumberStart);
+ m_pageLayout.setTopMargin (m_topMargin);
+ m_pageLayout.setLeftMargin (m_leftMargin);
+ m_pageLayout.setTextHeight (m_pageHeight - m_topMargin - m_bottomMargin);
+ m_pageLayout.setTextWidth (m_pageWidth - m_leftMargin - m_rightMargin);
+
+ // TODO: libexport
+ // headerFromTop
+ // footerFromTop
+
+ if (!m_generator->writeDocumentBegin (MSWrite::Format::Write_3_0,
+ &m_pageLayout)) return false;
+
+ return true;
+ }
+
+ bool doCloseDocument (void)
+ {
+ kdDebug (30509) << "doCloseDocument ()" << endl;
+
+ if (!m_generator->writeDocumentEnd (MSWrite::Format::Write_3_0,
+ &m_pageLayout)) return false;
+
+ return true;
+ }
+
+ bool doFullPaperFormat (const int format,
+ const double width, const double height,
+ const int orientation)
+ {
+ kdDebug (30509) << "doFullPaperFormat ("
+ << format << ", "
+ << width << ", "
+ << height << ", "
+ << orientation << ")" << endl;
+
+ // TODO: does "format" or "orientation" matter?
+
+ m_pageHeight = MSWrite::Word (Point2Twip (height));
+ m_pageWidth = MSWrite::Word (Point2Twip (width));
+
+ return true;
+ }
+
+ bool doFullPaperBorders (const double top, const double left,
+ const double bottom, const double right)
+ {
+ kdDebug (30509) << "doFullPaperBorders ("
+ << top << ", "
+ << left << ", "
+ << bottom << ", "
+ << right << ")" << endl;
+
+ m_topMargin = MSWrite::Word (Point2Twip (top));
+ m_leftMargin = MSWrite::Word (Point2Twip (left));
+ m_bottomMargin = MSWrite::Word (Point2Twip (bottom));
+ m_rightMargin = MSWrite::Word (Point2Twip (right));
+
+ return true;
+ }
+
+ bool doVariableSettings (const VariableSettingsData &varSettings)
+ {
+ m_pageNumberStart = MSWrite::Word (varSettings.startingPageNumber);
+
+ kdDebug (30509) << "doVariableSettings pageNumberStart="
+ << m_pageNumberStart << endl;
+ return true;
+ }
+
+ // In Write the header/footer must be the same for every page except that
+ // you can choose to not display it on the first page
+ //
+ // /*This filter aims to be as lossless as possible so if we can't
+ // accommodate the types of headers/footers found in KWord, we at least
+ // print out the paragraphs in the body*/
+ //
+ // Not anymore. Dumping headers & footers in the body didn't
+ // turn out to be that useful, nor was it expected by users.
+ //
+ bool doPageInfo (int headerType, int footerType)
+ {
+ kdDebug (30509) << "doPageInfo (headerType=" << headerType
+ << ", footerType=" << footerType
+ << ")" << endl;
+
+ m_headerType = headerType;
+ switch (headerType)
+ {
+ case 0: // same on all pages
+ case 3: // different on even and odd pages
+ m_isHeaderOnFirstPage = true;
+ break;
+ case 1: // different on first, even and odd pages
+ case 2: // different on first and other pages
+ m_isHeaderOnFirstPage = false;
+ break;
+ default:
+ kdWarning (30509) << "Unknown headerType: " << headerType << endl;
+ m_isHeaderOnFirstPage = false; // just a guess
+ break;
+ }
+
+ m_footerType = footerType;
+ switch (footerType)
+ {
+ case 0: // same on all pages
+ case 3: // different on even and odd pages
+ m_isFooterOnFirstPage = true;
+ break;
+ case 1: // different on first, even and odd pages
+ case 2: // different on first and other pages
+ m_isFooterOnFirstPage = false;
+ break;
+ default:
+ kdWarning (30590) << "Unknown footerType: " << footerType << endl;
+ m_isFooterOnFirstPage = false; // just a guess
+ break;
+ }
+
+ return true;
+ }
+
+ bool isParaListEmpty (const TQValueList <ParaData> &para)
+ {
+ if (para.count () == 1)
+ {
+ if (para.first ().text.isEmpty ())
+ return true;
+ }
+
+ return false;
+ }
+
+ bool doHeader (const HeaderData &header)
+ {
+ kdDebug (30509) << "doHeader (header.page=" << header.page << ")" << endl;
+
+ if (isParaListEmpty (header.para))
+ {
+ kdDebug (30509) << "\tEmpty, ignoring" << endl;
+ return true;
+ }
+
+ #if 0
+ switch (m_headerType)
+ {
+ case 0: // same on all pages
+ if (header.page != HeaderData::PAGE_ALL)
+ return true;
+ break;
+ case 3: // different on even and odd pages
+ if (header.page != HeaderData::PAGE_ODD &&
+ header.page != HeaderData::PAGE_EVEN)
+ return true;
+ break;
+ case 1: // different on first, even and odd pages
+ // accept everything
+ break;
+ case 2: // different on first and other pages
+ if (header.page != HeaderData::PAGE_FIRST &&
+ header.page != HeaderData::PAGE_ODD)
+ return true;
+ break;
+ }
+ #endif
+
+ m_hasHeader = true;
+ m_headerData.push_back (header);
+ return true;
+ }
+
+ bool doFooter (const FooterData &footer)
+ {
+ kdDebug (30509) << "doFooter (footer.page=" << footer.page << ")" << endl;
+
+ if (isParaListEmpty (footer.para))
+ {
+ kdDebug (30509) << "\tEmpty, ignoring" << endl;
+ return true;
+ }
+
+ #if 0
+ switch (m_footerType)
+ {
+ case 0: // same on all pages
+ if (footer.page != FooterData::PAGE_ALL)
+ return true;
+ break;
+ case 3: // different on even and odd pages
+ if (footer.page != FooterData::PAGE_ODD &&
+ footer.page != FooterData::PAGE_EVEN)
+ return true;
+ break;
+ case 1: // different on first, even and odd pages
+ // accept everything
+ break;
+ case 2: // different on first and other pages
+ if (footer.page != FooterData::PAGE_FIRST &&
+ footer.page != FooterData::PAGE_ODD)
+ return true;
+ break;
+ }
+ #endif
+
+ m_hasFooter = true;
+ m_footerData.push_back (footer);
+ return true;
+ }
+
+ bool doOpenBody (void)
+ {
+ kdDebug (30509) << "doOpenBody ()" << endl;
+
+ //
+ // Document Start
+ //
+ if (!doTrulyOpenDocument ()) return false;
+
+
+ //
+ // Footers followed by Headers (in this order)
+ //
+
+ bool wroteFooter = false;
+ m_inWhat = Footer;
+
+ for (TQValueList <FooterData>::Iterator it = m_footerData.begin ();
+ it != m_footerData.end ();
+ it++)
+ {
+ if ((*it).page != FooterData::PAGE_FIRST)
+ {
+ if (!wroteFooter)
+ {
+ if (!m_generator->writeFooterBegin ()) return false;
+ wroteFooter = true;
+ }
+
+ if (!doFullParagraphList ((*it).para)) return false;
+ it = --m_footerData.erase (it);
+ }
+ }
+ if (wroteFooter)
+ if (!m_generator->writeFooterEnd ()) return false;
+
+ bool wroteHeader = false;
+ m_inWhat = Header;
+
+ for (TQValueList <HeaderData>::Iterator it = m_headerData.begin ();
+ it != m_headerData.end ();
+ it++)
+ {
+ if ((*it).page != HeaderData::PAGE_FIRST)
+ {
+ if (!wroteHeader)
+ {
+ if (!m_generator->writeHeaderBegin ()) return false;
+ wroteHeader = true;
+ }
+
+ if (!doFullParagraphList ((*it).para)) return false;
+ it = --m_headerData.erase (it);
+ }
+ }
+ if (wroteHeader)
+ if (!m_generator->writeHeaderEnd ()) return false;
+
+
+ //
+ // Body Start
+ //
+
+ m_inWhat = Body;
+ if (!m_generator->writeBodyBegin ()) return false;
+ // KWord doesn't have a PageTable but we must emit the pageNew
+ // signal at least once
+ if (!m_generator->writePageNew ()) return false;
+
+#if 0
+ // dump remaining header paragraphs at the start of the body
+ for (TQValueList <HeaderData>::Iterator it = m_headerData.begin ();
+ it != m_headerData.end ();
+ it++)
+ {
+ kdDebug (30509) << "BODY START ADDING HEADER: " << (*it).page << endl;
+ if (!doFullParagraphList ((*it).para)) return false;
+ it = --m_headerData.erase (it);
+ }
+
+ // dump remaining footer paragraphs too
+ for (TQValueList <FooterData>::Iterator it = m_footerData.begin ();
+ it != m_footerData.end ();
+ it++)
+ {
+ kdDebug (30509) << "BODY START ADDING FOOTER: " << (*it).page << endl;
+ if (!doFullParagraphList ((*it).para)) return false;
+ it = --m_footerData.erase (it);
+ }
+#endif
+
+ return true;
+ }
+
+ bool doCloseBody (void)
+ {
+ kdDebug (30509) << "doCloseBody ()" << endl;
+
+ if (!m_generator->writeBodyEnd ()) return false;
+
+ return true;
+ }
+
+ // device that can either read from or write to a TQBuffer
+ // (but not both at the same time, please :))
+ class TQBufferDevice : public MSWrite::Device
+ {
+ private:
+ TQBuffer *m_buffer;
+
+ public:
+ TQBufferDevice (TQBuffer *buffer)
+ {
+ m_buffer = buffer;
+ }
+
+ bool read (MSWrite::Byte *buf, const MSWrite::DWord numBytes)
+ {
+ if (m_buffer->readBlock ((char *) buf, (TQ_ULONG) numBytes) != TQ_LONG (numBytes))
+ {
+ error (MSWrite::Error::FileError, "could not read from TQBuffer (not really a FileError)\n");
+ return false;
+ }
+ else
+ return true;
+ }
+
+ bool write (const MSWrite::Byte *buf, const MSWrite::DWord numBytes)
+ {
+ if (m_buffer->writeBlock ((char *) buf, (TQ_ULONG) numBytes) != TQ_LONG (numBytes))
+ {
+ error (MSWrite::Error::FileError, "could not write to TQBuffer (not really a FileError)\n");
+ return false;
+ }
+ else
+ return true;
+ }
+
+ // normally we must write zeros if we seek past EOF
+ // but we know that won't happen :)
+ bool seek (const long offset, const int whence)
+ {
+ long absoffset;
+ switch (whence)
+ {
+ case SEEK_SET:
+ absoffset = offset;
+ break;
+ case SEEK_CUR:
+ absoffset = m_buffer->at () + offset;
+ break;
+ case SEEK_END:
+ absoffset = m_buffer->size () + offset;
+ break;
+ default:
+ error (MSWrite::Error::InternalError, "unknown seek\n");
+ return false;
+ }
+
+ if (absoffset > long (m_buffer->size ()))
+ {
+ error (MSWrite::Error::InternalError, "seek past EOF unimplemented\n");
+ return false;
+ }
+
+ if (!m_buffer->at (absoffset))
+ {
+ error (MSWrite::Error::FileError, "TQBuffer could not seek (not really a FileError)\n");
+ return false;
+ }
+
+ return true;
+ }
+
+ long tell (void)
+ {
+ return long (m_buffer->at ());
+ }
+
+ void debug (const char *s)
+ {
+ kdDebug (30509) << s;
+ }
+ void debug (const int i)
+ {
+ kdDebug (30509) << i;
+ }
+
+ void error (const int errorCode, const char *message,
+ const char * /*file*/ = "", const int /*lineno*/ = 0,
+ MSWrite::DWord /*token*/ = MSWrite::Device::NoToken)
+ {
+ if (errorCode == MSWrite::Error::Warn)
+ kdWarning (30509) << message;
+ else
+ {
+ m_error = errorCode;
+ kdError (30509) << message;
+ }
+ }
+ };
+
+ class WMFRecord : public MSWrite::NeedsDevice
+ {
+ public:
+ static const int s_size = 6;
+
+ protected:
+ MSWrite::Byte m_data [s_size];
+
+ MSWrite::DWord m_size; // record size in Words including everything in this struct
+ MSWrite::Word m_function;
+
+ MSWrite::Short m_args [100]; // "ought to be enough for anybody"
+ int m_argUpto;
+
+ public:
+ WMFRecord () : m_argUpto (0)
+ {
+ }
+
+ WMFRecord (const MSWrite::DWord size, const MSWrite::Word function, MSWrite::Device *device)
+ : MSWrite::NeedsDevice (device),
+ m_size (size), m_function (function),
+ m_argUpto (0)
+ {
+ }
+
+ MSWrite::DWord getSize (void) const { return m_size; }
+ void setSize (const MSWrite::DWord size) { m_size = size; }
+
+ MSWrite::Word getFunction (void) const { return m_function; }
+ void setFunction (const MSWrite::Word function) { m_function = function; }
+
+ void add (const MSWrite::Short arg)
+ {
+ m_args [m_argUpto++] = arg;
+ }
+
+ bool readFromDevice (void)
+ {
+ if (!m_device->readInternal (m_data, 6)) return false;
+ MSWrite::ReadDWord (m_size, m_data + 0);
+ MSWrite::ReadWord (m_function, m_data + 4);
+ printf ("Size (Words): %i Size (Bytes): %i Function: %04X (func=%02X,numArgs=%i)\n",
+ m_size, m_size * sizeof (MSWrite::Word), m_function, m_function & 255, m_function >> 8);
+ #if 1
+ if (m_function == 0 && m_size == 3) // last record
+ return false;
+ #endif
+
+ switch (m_function)
+ {
+ case 0x0103:
+ printf ("\tSetMapMode\n");
+ break;
+ case 0x020c:
+ printf ("\tSetWindowExt\n");
+ break;
+ case 0x020b:
+ printf ("\tSetWindowOrg\n");
+ break;
+ case 0x0b41:
+ printf ("\tDibStretchBlt\n");
+ break;
+ default:
+ printf ("\tUnknown function\n");
+ }
+
+ long offset = m_device->tellInternal ();
+
+ for (int i = 0; i < ((m_function == 0x0b41) ? 10 : (m_function >> 8)); i++)
+ {
+ MSWrite::Byte data [2];
+ if (!m_device->readInternal (data, 2)) return false;
+ MSWrite::ReadShort (m_args [i], data);
+ printf ("\tArg (rev) #%i=%i\n", i, m_args [i]);
+ }
+
+ #if 0
+ // arguments are reversed normally
+ int u = 0;
+ for (int i = (m_function >> 8) - 1; i >= 0; i--, u++)
+ printf ("\tArg #%i=%u\n", u, m_args [i]);
+ #endif
+
+ #if 1
+ if (m_function == 0xb41)
+ {
+ // just curious but what's the infoHeader like?
+ MSWrite::BMP_BitmapInfoHeader bih;
+ bih.setDevice (m_device);
+ if (!bih.readFromDevice ()) return false;
+ }
+ #endif
+
+ m_size *= sizeof (MSWrite::Word); // in Bytes now
+ m_size -= 6; // skip past prefix
+ printf (">>> At: %li Next: %li\n", m_device->tellInternal (), offset + m_size);
+ if (!m_device->seekInternal (offset + m_size, SEEK_SET)) return false;
+ return true;
+ }
+
+ bool writeToDevice (void)
+ {
+ MSWrite::WriteDWord (m_size, m_data + 0);
+ MSWrite::WriteWord (m_function, m_data + 4);
+ if (!m_device->writeInternal (m_data, 6)) return false;
+
+ for (int i = 0; i < ((m_function == 0x0B41) ? 10/*not 11*/ : (m_function >> 8)); i++)
+ {
+ MSWrite::Byte data [2];
+ MSWrite::WriteShort (m_args [i], data);
+ if (!m_device->writeInternal (data, 2)) return false;
+ }
+
+ return true;
+ }
+ };
+
+ // converts a DIB to a Standard WMF
+ bool BMP2WMF (MSWrite::Device &readDevice, MSWrite::Device &writeDevice)
+ {
+ // read BMP's FileHeader
+ MSWrite::BMP_BitmapFileHeader bfh;
+ bfh.setDevice (&readDevice);
+ if (!bfh.readFromDevice ()) return false;
+
+ // WMF bitmap doesn't contain FileHeader
+ MSWrite::DWord totalBytes = bfh.getTotalBytes () - MSWrite::BMP_BitmapFileHeader::s_size;
+
+ // read BMP's InfoHeader to get some info...
+ MSWrite::BMP_BitmapInfoHeader bih;
+ bih.setDevice (&readDevice);
+ if (!bih.readFromDevice ()) return false;
+
+ // get some info about the image
+ MSWrite::Long width = bih.getWidth ();
+ MSWrite::Long height = bih.getHeight ();
+
+
+ // Note: not from LibMSWrite's wmf.cpp
+ kdDebug (30509) << "\t\tBIH: width(pt)=" << width
+ << " height(pt)=" << height
+ << " BPP=" << bih.getBitsPerPixel ()
+ << endl;
+ kdDebug (30509) << "\t\tBIH: xPixelsPerMeter=" << bih.getXPixelsPerMeter ()
+ << " yPixelsPerMeter=" << bih.getYPixelsPerMeter ()
+ << endl;
+
+
+ // write WMF Header
+ MSWrite::WMFHeader wmfHeader;
+ wmfHeader.setDevice (&writeDevice);
+
+ MSWrite::DWord maxRecordSizeBytes
+ = (totalBytes
+ + 10 * sizeof (MSWrite::Word)/*parameters for DibStretchBlt*/
+ + WMFRecord::s_size);
+ wmfHeader.setFileSize ((MSWrite::WMFHeader::s_size
+ + WMFRecord::s_size + 1 * sizeof (MSWrite::Word)/*SetMapMode*/
+ + WMFRecord::s_size + 2 * sizeof (MSWrite::Word)/*SetWindowExt*/
+ + WMFRecord::s_size + 2 * sizeof (MSWrite::Word)/*SetWindowOrg*/
+ + maxRecordSizeBytes/*DibStretchBlt*/
+ + WMFRecord::s_size/*Sentinel*/)
+ / sizeof (MSWrite::Word));
+ wmfHeader.setMaxRecordSize (maxRecordSizeBytes / sizeof (MSWrite::Word));
+ if (!wmfHeader.writeToDevice ()) return false;
+
+ WMFRecord wmfRecordSetMapMode (4/*(Words)*/, 0x0103, &writeDevice);
+ wmfRecordSetMapMode.add (8/*MM_ANISOTROPIC*/);
+ if (!wmfRecordSetMapMode.writeToDevice ()) return false;
+
+ WMFRecord wmfRecordSetWindowExt (5/*(Words)*/, 0x020C, &writeDevice);
+ wmfRecordSetWindowExt.add (-height);
+ wmfRecordSetWindowExt.add (width);
+ if (!wmfRecordSetWindowExt.writeToDevice ()) return false;
+
+ WMFRecord wmfRecordSetWindowOrg (5/*(Words)*/, 0x020B, &writeDevice);
+ wmfRecordSetWindowOrg.add (0);
+ wmfRecordSetWindowOrg.add (0);
+ if (!wmfRecordSetWindowOrg.writeToDevice ()) return false;
+
+ WMFRecord wmfRecordBMP (maxRecordSizeBytes / sizeof (MSWrite::Word),
+ 0x0B41/*DibStretchBlt*/,
+ &writeDevice);
+ wmfRecordBMP.add (32); // ?
+ wmfRecordBMP.add (204); // ?
+ wmfRecordBMP.add (height); // src height
+ wmfRecordBMP.add (width); // src width
+ wmfRecordBMP.add (0); // src y
+ wmfRecordBMP.add (0); // src x
+ wmfRecordBMP.add (-height); // dest height
+ wmfRecordBMP.add (width); // dest width
+ wmfRecordBMP.add (0); // dest y
+ wmfRecordBMP.add (0); // dest x
+ if (!wmfRecordBMP.writeToDevice ()) return false;
+
+ // write BMP InfoHeader back to the device
+ bih.setDevice (&writeDevice);
+ if (!bih.writeToDevice ()) return false;
+
+ long left = totalBytes - MSWrite::BMP_BitmapInfoHeader::s_size;
+ while (left)
+ {
+ MSWrite::Byte data [1024];
+ long amountToRead = left > 1024 ? 1024 : left;
+ if (!readDevice.readInternal (data, amountToRead)) return false;
+ if (!writeDevice.writeInternal (data, amountToRead)) return false;
+
+ left -= amountToRead;
+ }
+
+ WMFRecord wmfRecordSentinel (3/*(Words)*/, 0x0000, &writeDevice);
+ if (!wmfRecordSentinel.writeToDevice ()) return false;
+
+ #if 1
+ // bug with Word97?
+ MSWrite::Byte zero = 0;
+ if (!writeDevice.writeInternal (&zero, sizeof (MSWrite::Byte))) return false;
+ #endif
+
+ return true;
+ }
+
+ // all windows measurements depend on there being 72 dots/points per inch
+ static double getDimen72DPI (const int measurement, const int dotsPerMeter)
+ {
+ kdDebug (30509) << "\t\tgetDimen72DPI (measurement=" << measurement
+ << ",dotsPerMeter=" << dotsPerMeter << ")" << endl;
+
+ // Can't get resolution?
+ // Assume that we are already 72dpi
+ if (dotsPerMeter <= 0)
+ return double (measurement);
+
+ // 2834.65 = 100 / 2.54 * 72
+ return double (measurement) * 2834.65 / double (dotsPerMeter);
+ }
+
+ //
+ // Note: if we suffer from a conversion error in this function (and can't
+ // export the image), we _still_ return true, not false because there is
+ // nothing worse than a filter that aborts due to its own incompetence [1]
+ // (don't flame the author please, just blame the function :)). But if we
+ // do suffer from a file-like error, we abort right away because something
+ // bad (memory corruption, internal error...) is happening!
+ //
+ // Why then _do_ we abort on text errors when images tell a thousand
+ // words? Because this saying is wrong and text is probably more
+ // important to the user.
+ //
+ // [1] yes, "worse than an itch you can't scratch"
+ //
+ bool processImage (const FrameAnchor &frameAnchor,
+ const MSWrite::FormatParaProperty *paraPropIn,
+ const MSWrite::FormatCharProperty *charPropIn,
+ const bool ignoreIndent)
+ {
+ kdDebug (30509) << "--------------------------" << endl
+ << "processImage()" << endl;
+
+
+ // Write supports images in 3 formats:
+ //
+ // 1. Monochrome BMP (not very useful)
+ // 2. OLE (hard to work with and only supported in ver >= 3.1)
+ // 3. Standard WMF
+ //
+ // So we convert all images to WMF for convenience.
+ // We don't even bother saving Monochrome BMPs "as is" because
+ // there's no point (just save it in WMF to make life easier)
+ //
+ // But a Standard WMF is basically a BMP with some headers/GDI calls
+ // so the conversion process is like this:
+ //
+ // start->WMF->finish
+ // start->BMP->WMF->finish
+ // start->???->BMP->WMF->finish
+ //
+
+ double imageActualWidth = -1, imageActualHeight = -1;
+ MSWrite::DWord imageSize = 0;
+
+ TQString imageType;
+ int pos = frameAnchor.picture.koStoreName.findRev ('.');
+ if (pos != -1) imageType = frameAnchor.picture.koStoreName.mid (pos).lower ();
+ kdDebug (30509) << "\timageType: " << imageType << endl;
+
+ TQByteArray imageData;
+ kdDebug (30509) << "\tReading image: " << frameAnchor.picture.koStoreName << endl;
+ if (!loadSubFile (frameAnchor.picture.koStoreName, imageData))
+ ErrorAndQuit (MSWrite::Error::FileError, "could not open image from store\n");
+
+ // FSM
+ for (;;)
+ {
+ if (imageType == ".wmf")
+ {
+ imageSize = imageData.size ();
+ if (imageActualWidth == -1 && imageActualHeight == -1)
+ {
+ // load WMF
+ KoWmfPaint wmf;
+ if (!wmf.load (imageData))
+ {
+ kdError (30509) << "Could not open WMF - Invalid Format!" << endl;
+ return true;
+ }
+
+ // get raw dimensions
+ TQRect dimen = wmf.boundingRect ();
+ int width = abs (dimen.width ());
+ int height = abs (dimen.height ());
+ kdDebug (30509) << "\tRaw WMF dimensions: " << width << "x" << height << endl;
+
+ if (wmf.isPlaceable ())
+ {
+ kdDebug (30509) << "\tConverting Placeable WMF" << endl;
+
+ // convert twip measurements that aren't in 72dpi
+ int defaultDpi = wmf.defaultDpi ();
+ if (defaultDpi <= 0)
+ {
+ kdWarning (30509) << "Invalid defaultDPI: " << defaultDpi << endl;
+ defaultDpi = 1440;
+ }
+ imageActualWidth = width * 1440 / defaultDpi;
+ imageActualHeight = height * 1440 / defaultDpi;
+
+ // Remove Aldus Placeable WMF Header
+ for (int i = 0; i < int (imageSize) - 22; i++)
+ imageData [i] = imageData [i + 22];
+
+ imageData.resize (imageSize - 22);
+ imageSize -= 22;
+ }
+ else if (wmf.isEnhanced ())
+ {
+ kdError (30509) << "Enhanced WMF unsupported by TQWmf, internal error!" << endl;
+
+ return true;
+ }
+ // Standard WMF
+ else
+ {
+ kdDebug (30509) << "\tStandard WMF - no conversion required" << endl;
+
+ // assume width & height were in 72dpi points
+ imageActualWidth = Point2Twip (width);
+ imageActualHeight = Point2Twip (height);
+ }
+ }
+
+ kdDebug (30509) << "\tNow WMF: width=" << imageActualWidth
+ << " height=" << imageActualHeight
+ << " size=" << imageSize
+ << endl;
+
+ // we're done!
+ break;
+ }
+ // TODO: DDB?
+ else if (imageType == ".bmp")
+ {
+ TQImage image (imageData);
+ if (image.isNull ())
+ {
+ kdError (30509) << "TQImage IsNull: Line=" << __LINE__ << endl;
+ return true;
+ }
+
+ if (imageActualWidth == -1 && imageActualHeight == -1)
+ {
+ imageActualWidth = Point2Twip (getDimen72DPI (image.width (), image.dotsPerMeterX ()));
+ imageActualHeight = Point2Twip (getDimen72DPI (image.height (), image.dotsPerMeterY ()));
+ }
+
+ kdDebug (30509) << "\tNow BMP: width=" << imageActualWidth
+ << " height=" << imageActualHeight
+ << " size=" << imageSize
+ << endl;
+
+ TQByteArray imageWMF;
+ // input device
+ TQBuffer inBuffer (imageData);
+ inBuffer.open (IO_ReadOnly);
+ TQBufferDevice inDevice (&inBuffer);
+
+ // output device
+ TQBuffer outBuffer (imageWMF);
+ outBuffer.open (IO_WriteOnly);
+ TQBufferDevice outDevice (&outBuffer);
+
+ // BMP --> WMF
+ if (!BMP2WMF (inDevice, outDevice))
+ {
+ kdError (30509) << "BMP to WMF conversion error" << endl;
+ return true;
+ }
+
+ outBuffer.close ();
+ inBuffer.close ();
+ imageData = imageWMF.copy ();
+
+ imageType = ".wmf";
+ }
+ else
+ {
+ if (imageActualWidth == -1 && imageActualHeight == -1)
+ {
+ TQImage image (imageData);
+ if (image.isNull())
+ {
+ kdError (30509) << "TQImage isNull: Line=" << __LINE__ << endl;
+ return true;
+ }
+
+ imageActualWidth = Point2Twip (getDimen72DPI (image.width (), image.dotsPerMeterX ()));
+ imageActualHeight = Point2Twip (getDimen72DPI (image.height (), image.dotsPerMeterY ()));
+ }
+
+ kdDebug (30509) << "\tForeign format: width=" << imageActualWidth
+ << " height=" << imageActualHeight
+ << " size=" << imageSize
+ << endl;
+
+ TQByteArray imageBMP;
+ // input device
+ TQBuffer inBuffer (imageData);
+ inBuffer.open (IO_ReadOnly);
+
+ // read foreign image
+ TQImageIO imageIO (&inBuffer, NULL);
+ if (!imageIO.read ())
+ {
+ kdError (30509) << "Could not read foreign format" << endl;
+ return true;
+ }
+
+ // output device
+ TQBuffer outBuffer (imageBMP);
+ outBuffer.open (IO_WriteOnly);
+
+ // write BMP
+ imageIO.setIODevice (TQT_TQIODEVICE(&outBuffer));
+ imageIO.setFormat ("BMP");
+ if (!imageIO.write ())
+ {
+ kdError (30509) << "Could not convert to BMP" << endl;
+ return true;
+ }
+
+ outBuffer.close ();
+ inBuffer.close ();
+ imageData = imageBMP.copy ();
+
+ imageType = ".bmp";
+ }
+ }
+
+
+ kdDebug (30509) << "\tActual dimensions: width=" << imageActualWidth
+ << " height=" << imageActualHeight << endl;
+
+ kdDebug (30509) << "\tKOffice position: left=" << frameAnchor.frame.left
+ << " right=" << frameAnchor.frame.right
+ << " top=" << frameAnchor.frame.top
+ << " bottom=" << frameAnchor.frame.bottom
+ << endl;
+
+ kdDebug (30509) << "\tIndent=" << MSWrite::Word (Point2Twip (frameAnchor.frame.left)) - m_leftMargin << endl;
+ if (ignoreIndent)
+ kdDebug (30509) << "\t\tIgnoring indent - already exported at least one image in a KWord paragraph" << endl;
+
+ double displayedWidth = Point2Twip (frameAnchor.frame.right - frameAnchor.frame.left + 1);
+ double displayedHeight = Point2Twip (frameAnchor.frame.bottom - frameAnchor.frame.top + 1);
+
+ kdDebug (30509) << "\tdisplayedWidth=" << displayedWidth
+ << " displayedHeight=" << displayedHeight
+ << endl;
+
+
+ //
+ // Start writing out the image now
+ //
+ // Note: here, we can start returning false again because the errors
+ // won't be conversion-related
+ //
+
+ MSWrite::Image image;
+ image.setIsWMF (true);
+ if (!ignoreIndent)
+ {
+ if (paraPropIn->getAlignment () != MSWrite::Alignment::Centre)
+ image.setIndent (MSWrite::Word (Point2Twip (frameAnchor.frame.left)) - m_leftMargin);
+ else
+ {
+ // TODO: what is the image offset relative to (it's not always rel. to the left margin)?
+ kdDebug (30509) << "\tCentred paragraph, cannot position image" << endl;
+ }
+ }
+
+ image.setOriginalWidth (imageActualWidth);
+ image.setOriginalHeight (imageActualHeight);
+ image.setDisplayedWidth (displayedWidth);
+ image.setDisplayedHeight (displayedHeight);
+ image.setExternalImageSize (imageSize);
+
+ MSWrite::FormatParaProperty paraProp; paraProp = *paraPropIn;
+ paraProp.setIsObject (true);
+ paraProp.setLeftIndent (0); // not necessary but...
+ if (!m_generator->writeParaInfoBegin (&paraProp, NULL, &image))
+ return false;
+
+ // yes, images have character formatting as well
+ // (character formatting _must_ cover entire document)
+ // (but I think it's ignored)
+ MSWrite::FormatCharProperty charProp; charProp = *charPropIn;
+ if (!m_generator->writeCharInfoBegin (&charProp))
+ return false;
+
+ // actually write image
+ if (!m_generator->writeBinary ((const MSWrite::Byte *) (const char *) imageData.data (), imageSize)) return false;
+
+ // 2nd argument endOfParagraph is ignored by InternalGenerator so
+ // there's no need to specify it
+ if (!m_generator->writeCharInfoEnd (&charProp, true))
+ return false;
+;
+ if (!m_generator->writeParaInfoEnd (&paraProp, NULL, &image))
+ return false;
+
+
+ kdDebug (30509) << "processImage() successful!" << endl
+ << "==========================" << endl
+ << endl
+ << endl;
+ return true;
+ }
+
+ bool processTable (const Table &table)
+ {
+ // just dump the table out for now (no layout)
+ for (TQValueList <TableCell>::ConstIterator it = table.cellList.begin ();
+ it != table.cellList.end ();
+ it++)
+ {
+ if (!doFullParagraphList (*(*it).paraList)) return false;
+ }
+
+ return true;
+ }
+
+ bool processCounter (const CounterData &counter)
+ {
+ //kdDebug (30509) << "processCounter(counter.text=" << counter.text << ")" << endl;
+
+ if (!counter.text.isEmpty ())
+ {
+ // isn't this wonderful? :)
+ if (!processText (counter.text)) return false;
+ if (!processText (" ")) return false;
+ }
+
+ return true;
+ }
+
+ #ifndef NDEBUG
+ //#define KMF_DEBUG_FONT
+ #endif
+ void processFormatData (MSWrite::FormatCharProperty &charProp,
+ const TextFormatting &f)
+ {
+ if (!f.fontName.isEmpty ())
+ {
+ // create new Font with Name
+ MSWrite::Font font ((const MSWrite::Byte *) (const char *) f.fontName.utf8 ());
+ #ifdef KMF_DEBUG_FONT
+ kdDebug (30509) << "FontName " << f.fontName << endl;
+ #endif
+
+ // get Font Family
+ TQFont TQTFontInfo (f.fontName);
+ switch (TQTFontInfo.styleHint ())
+ {
+ case TQFont::Serif:
+ #ifdef KMF_DEBUG_FONT
+ kdDebug (30509) << "FontFamily Serif" << endl;
+ #endif
+ font.setFamily (MSWrite::Font::Roman);
+ break;
+ case TQFont::SansSerif:
+ #ifdef KMF_DEBUG_FONT
+ kdDebug (30509) << "FontFamily SansSerif" << endl;
+ #endif
+ font.setFamily (MSWrite::Font::Swiss);
+ break;
+ case TQFont::Courier:
+ #ifdef KMF_DEBUG_FONT
+ kdDebug (30509) << "FontFamily Courier" << endl;
+ #endif
+ font.setFamily (MSWrite::Font::Modern);
+ break;
+ case TQFont::OldEnglish:
+ #ifdef KMF_DEBUG_FONT
+ kdDebug (30509) << "FontFamily OldEnglish" << endl;
+ #endif
+ font.setFamily (MSWrite::Font::Decorative);
+ break;
+ default:
+ #ifdef KMF_DEBUG_FONT
+ kdDebug (30509) << "FontFamily DontKnow" << endl;
+ #endif
+ // it's either DontCare or MSWrite::Font::Script
+ font.setFamily (MSWrite::Font::DontCare);
+ break;
+ }
+
+ charProp.setFont (&font);
+ }
+ if (f.fontSize > 0) charProp.setFontSize (f.fontSize);
+
+ charProp.setIsItalic (f.italic);
+ charProp.setIsUnderlined (f.underline); // TODO: underlineWord
+ charProp.setIsBold (f.weight > (50/*normal*/ + 75/*bold*/) / 2);
+
+ switch (f.verticalAlignment)
+ {
+ case 0: // normal
+ charProp.setIsNormalPosition ();
+ break;
+ case 1: // subscript
+ charProp.setIsSubscript ();
+ break;
+ case 2: // superscript
+ charProp.setIsSuperscript ();
+ break;
+ }
+
+ // TODO: fontAttribute;
+ }
+
+ static MSWrite::Word getClosestLineSpacing (const double points)
+ {
+ const double twips = Point2Twip (points);
+
+ #if 1
+ if (twips < double ((MSWrite::LineSpacing::Single + MSWrite::LineSpacing::OneAndAHalf) / 2))
+ return MSWrite::LineSpacing::Single;
+ else if (twips < double ((MSWrite::LineSpacing::OneAndAHalf + MSWrite::LineSpacing::Double) / 2))
+ return MSWrite::LineSpacing::OneAndAHalf;
+ else
+ return MSWrite::LineSpacing::Double;
+ #else // or do we want a non-"standard" linespacing?
+ return MSWrite::Word (twips);
+ #endif
+ }
+
+ bool doFullParagraphList (const TQValueList <ParaData> &paraList)
+ {
+ for (TQValueList <ParaData>::ConstIterator it = paraList.begin ();
+ it != paraList.end ();
+ it ++)
+ {
+ if (!doFullParagraph (*it)) return false;
+ }
+
+ return true;
+ }
+
+ bool doFullParagraph (const ParaData &paraData)
+ {
+ return doFullParagraph (paraData.text,
+ paraData.layout,
+ paraData.formattingList);
+ }
+
+ bool doFullParagraph (const TQString &paraText,
+ const LayoutData &layout,
+ const ValueListFormatData &paraFormatDataList)
+ {
+ MSWrite::FormatParaProperty paraProp;
+
+ if (m_inWhat == Body)
+ paraProp.setIsNormalParagraph (true);
+ else
+ {
+ if (m_inWhat == Header)
+ {
+ paraProp.setIsHeader (true);
+ paraProp.setIsOnFirstPage (m_isHeaderOnFirstPage);
+ }
+ else if (m_inWhat == Footer)
+ {
+ paraProp.setIsFooter (true);
+ paraProp.setIsOnFirstPage (m_isFooterOnFirstPage);
+ }
+ }
+
+ paraProp.setIsText (true);
+
+ // Alignment
+ if (!layout.alignment.isEmpty ())
+ {
+ if (layout.alignment == "left")
+ // quite useless since MSWrite::Alignment::Left is the default anyway
+ paraProp.setAlignment (MSWrite::Alignment::Left);
+ else if (layout.alignment == "right")
+ paraProp.setAlignment (MSWrite::Alignment::Right);
+ else if (layout.alignment == "center")
+ paraProp.setAlignment (MSWrite::Alignment::Center);
+ else if (layout.alignment == "justify")
+ paraProp.setAlignment (MSWrite::Alignment::Justify);
+ else
+ kdWarning (30509) << "Unknown Alignment: " << layout.alignment << endl;
+ }
+
+ // Indentation
+ if (layout.indentFirst) paraProp.setLeftIndentFirstLine (MSWrite::Short (Point2Twip (layout.indentFirst)));
+ if (layout.indentLeft >= 0) paraProp.setLeftIndent (MSWrite::Word (Point2Twip (layout.indentLeft)));
+ if (layout.indentRight >= 0) paraProp.setRightIndent (MSWrite::Word (Point2Twip (layout.indentRight)));
+ #if 0
+ kdDebug (30509) << "Indent: " << Point2Twip (layout.indentFirst) << " "
+ << Point2Twip (layout.indentLeft) << " "
+ << Point2Twip (layout.indentRight) << endl;
+ #endif
+
+ // Line Spacing
+ MSWrite::Word lineSpacing = MSWrite::LineSpacing::Normal;
+ switch (layout.lineSpacingType)
+ {
+ case LayoutData::LS_SINGLE:
+ lineSpacing = MSWrite::LineSpacing::Normal;
+ break;
+ case LayoutData::LS_ONEANDHALF:
+ lineSpacing = MSWrite::LineSpacing::OneAndAHalf;
+ break;
+ case LayoutData::LS_DOUBLE:
+ lineSpacing = MSWrite::LineSpacing::Double;
+ break;
+ case LayoutData::LS_CUSTOM:
+ case LayoutData::LS_FIXED:
+ case LayoutData::LS_ATLEAST:
+ lineSpacing = getClosestLineSpacing (layout.lineSpacing);
+ break;
+ case LayoutData::LS_MULTIPLE:
+ break;
+ default:
+ kdWarning (30509) << "unknown lineSpacingType \'" << layout.lineSpacingType << "\'" << endl;
+ }
+ paraProp.setLineSpacing (lineSpacing);
+
+ // Tabs are a Document Property, not a Paragraph Property, in Write, yet are stored for each paragraph.
+ // It seems that Write applies the 1st paragraph's Tabulator settings to the _entire_ document
+ // Word97 and KWord, however, will treat them like a Paragraph Property
+ int numTabs = 0;
+ for (TabulatorList::ConstIterator tabIt = layout.tabulatorList.begin ();
+ tabIt != layout.tabulatorList.end ();
+ tabIt++)
+ {
+ MSWrite::FormatParaPropertyTabulator tab;
+
+ // Write's UI only supports 12 as opposed to the 14 supposedly
+ // supported in the file so let's play it safe and quit when
+ // we reach 12
+ // Actually, KWord's UI also only supports 12 so this should never be true
+ if (numTabs >= 12)
+ {
+ kdWarning (30509) << "Write does not support more 12 tabulators, not writing out all tabulators" << endl;
+ break;
+ }
+
+ // Write only supports Decimal and Left tabs
+ // TODO: KOffice 1.3 alignchar (modify libexport)
+ if ((*tabIt).m_type == 3 /* && (*tabIt).m_alignchar == '.' */)
+ tab.setIsDecimal ();
+ else
+ tab.setIsNormal ();
+
+ tab.setIndent (MSWrite::Word (Point2Twip ((*tabIt).m_ptpos)));
+
+ // int m_filling;
+ // double m_width;
+ if ((*tabIt).m_filling != TabulatorData::TF_NONE)
+ kdWarning (30509) << "Write does not support Tabulator Filling" << endl;
+
+ paraProp.addTabulator (&tab);
+ numTabs++;
+ }
+
+ // TODO: double marginTop; // space before the paragraph (a negative value means invalid)
+ // TODO: double marginBottom; // space after the paragraph (a negative value means invalid)
+
+ // TODO: TQString styleName;
+ // TODO: TQString styleFollowing;
+
+ if (!m_generator->writeParaInfoBegin (&paraProp)) return false;
+
+ // get this paragraph's "default formatting"
+ MSWrite::FormatCharProperty charPropDefault;
+ processFormatData (charPropDefault, layout.formatData.text);
+
+ MSWrite::DWord uptoByte = 0; // relative to start of KWord paragraph
+ MSWrite::DWord numBytes = paraText.length (); // relative to start of KWord paragraph
+
+ bool startOfWRIParagraph = true;
+ bool exportedAtLeastOneImage = false; // ...from the KWord paragraph
+
+ // empty paragraph
+ if (numBytes == 0)
+ {
+ //kdDebug (30509) << "Outputting empty paragraph!" << endl;
+
+ // write default character property start
+ if (!m_generator->writeCharInfoBegin (&charPropDefault)) return false;
+
+ // page break at start of paragraph?
+ if (layout.pageBreakBefore)
+ if (!m_generator->writePageBreak ()) return false;
+
+ // counter data
+ processCounter (layout.counter);
+
+ // end of line
+ if (!m_generator->writeCarriageReturn ()) return false;
+ if (!m_generator->writeNewLine (true/*end of paragraph*/)) return false;
+
+ // page break at end of paragraph?
+ if (layout.pageBreakAfter)
+ if (!m_generator->writePageBreak ()) return false;
+
+ // write default character property end
+ if (!m_generator->writeCharInfoEnd (&charPropDefault, true)) return false;
+ }
+ else
+ {
+ for (ValueListFormatData::ConstIterator formatIt = paraFormatDataList.begin ();
+ formatIt != paraFormatDataList.end ();
+ formatIt++)
+ {
+ bool textSegment = true;
+
+ // apply local <FORMAT> tag on top of "default formatting"
+ MSWrite::FormatCharProperty charProp; charProp = charPropDefault;
+ processFormatData (charProp, (*formatIt).text);
+
+ if (!m_generator->writeCharInfoBegin (&charProp)) return false;
+
+ if (uptoByte == 0)
+ {
+ // page break at start of paragraph?
+ if (layout.pageBreakBefore)
+ if (!m_generator->writePageBreak ()) return false;
+
+ // counter data
+ processCounter (layout.counter);
+ }
+
+ // yes, this is slightly premature but it doesn't matter
+ // ... just be careful when using uptoByte
+ uptoByte += (*formatIt).len;
+
+ switch ((*formatIt).id)
+ {
+ case 0: // none?
+ ErrorAndQuit (MSWrite::Error::InternalError, "Format ID = 0\n");
+ case 1: // text
+ if (!processText (paraText.mid ((*formatIt).pos, (*formatIt).len)))
+ /*uptoByte == numBytes))*/
+ return false;
+
+ startOfWRIParagraph = false;
+ break;
+ case 2: // picture (deprecated)
+ m_device->error (MSWrite::Error::Warn, "Picture (deprecated) unsupported\n");
+ break;
+ case 3: // tabulator (deprecated)
+ m_device->error (MSWrite::Error::Warn, "Tabulator (deprecated) unsupported\n");
+ break;
+ case 4: // variable
+ {
+ bool justPrintText = true;
+
+ // Page Number / Number of Pages
+ if ((*formatIt).variable.m_type == 4 &&
+ (*formatIt).variable.isPageNumber () &&
+ m_inWhat != Body/*Write replaces it with '*' in the body*/)
+ {
+ if (!m_generator->writeCharInfoEnd (&charProp)) return false;
+ charProp.setIsPageNumber (true);
+ // if you don't do this it will print out the character literally (char 1)
+ if (!m_generator->writeCharInfoBegin (&charProp)) return false;
+
+ // only variable Write supports (and only in headers/footers)
+ // if you don't write out char 1, Write will not treat it as a
+ // variable anchor and will print the character out literally
+ if (!m_generator->writePageNumber ()) return false;
+
+ if (!m_generator->writeCharInfoEnd (&charProp)) return false;
+ charProp.setIsPageNumber (false);
+ if (!m_generator->writeCharInfoBegin (&charProp)) return false;
+
+ justPrintText = false;
+ }
+
+ if (justPrintText)
+ {
+ if (!processText ((*formatIt).variable.m_text))
+ return false;
+ }
+
+ startOfWRIParagraph = false;
+ break;
+ }
+ case 5: // footnote (KOffice 1.1)
+ m_device->error (MSWrite::Error::Warn, "Footnote unsupported\n");
+ break;
+ case 6: // anchor for frame
+ //
+ // Write does not support inline frames so:
+ //
+ // - we end the current paragraph
+ // - dump the inline frame in a paragraph of its own
+ // - continue with a new paragraph
+ //
+
+ if (!startOfWRIParagraph)
+ {
+ kdDebug (30509) << "Writing CRLF to end text paragraph" << endl;
+
+ // If you don't have CRLF at the end of the text
+ // paragraph, Write will think that the next paragraph
+ // (the image) is part of the text...
+ if (!m_generator->writeCarriageReturn ()) return false;
+ if (!m_generator->writeNewLine (true/*end of paragraph*/)) return false;
+ }
+ else
+ kdDebug (30509) << "Inline frame is anchored at start of paragraph, no CRLF" << endl;
+
+ if (!m_generator->writeCharInfoEnd (&charProp)) return false;
+ if (!m_generator->writeParaInfoEnd (&paraProp)) return false;
+
+
+ if ((*formatIt).frameAnchor.type == 6)
+ {
+ kdDebug (30509) << "Table detected" << endl;
+
+ // this will make its own paragraph(s)...
+ processTable ((*formatIt).frameAnchor.table);
+
+ // HACK: inline tables are flushed to the left and right
+ // margins, despite being inline, hence the next image
+ // indent will be sensible and should not be ignored.
+ kdDebug (30509) << "Table hack: resetting image-ignore-indent flag" << endl;
+ exportedAtLeastOneImage = false;
+ }
+ else if ((*formatIt).frameAnchor.type == 2)
+ {
+ kdDebug (30509) << "Image detected" << endl;
+
+ // this will make its own paragraph...
+ if (!processImage ((*formatIt).frameAnchor, &paraProp, &charProp,
+ exportedAtLeastOneImage)) return false;
+
+ exportedAtLeastOneImage = true;
+ }
+ else
+ kdWarning (30509) << "Unknown type of anchor: " << (*formatIt).frameAnchor.type << endl;
+
+
+ // recontinue paragraph
+ if (!m_generator->writeParaInfoBegin (&paraProp)) return false;
+ startOfWRIParagraph = true;
+ if (!m_generator->writeCharInfoBegin (&charProp)) return false;
+
+ textSegment = false;
+ break;
+ }
+
+ if (uptoByte == numBytes)
+ {
+ if (textSegment)
+ {
+ // end of line
+ if (!m_generator->writeCarriageReturn ()) return false;
+ if (!m_generator->writeNewLine (true/*end of paragraph*/)) return false;
+ }
+
+ // page break at end of paragraph?
+ if (layout.pageBreakAfter)
+ if (!m_generator->writePageBreak ()) return false;
+ }
+
+ if (!m_generator->writeCharInfoEnd (&charProp, uptoByte == numBytes)) return false;
+ }
+ }
+
+ if (!m_generator->writeParaInfoEnd (&paraProp)) return false;
+ //if (numBytes) kdDebug (30509) << "Just Output " << uptoByte << "/" << numBytes << " with text \'" << paraText.utf8 () << "\'" << endl;
+
+ return true;
+ }
+
+ template <class dtype>
+ dtype min (const dtype a, const dtype b, const dtype c)
+ {
+ if (a <= b && a <= c) return a;
+ if (b <= a && b <= c) return b;
+ return c;
+ }
+
+ #ifndef NDEBUG
+ //#define DEBUG_PROCESS_TEXT
+ #endif
+ bool processText (const TQString &stringUnicode)
+ {
+ //
+ // Look out for characters in the string and emit signals as appropriate:
+ //
+ // 1 pageNumber (already taken care of as a variable)
+ // 10 newLine
+ // 13 carriageReturn (TODO: Oh no! Can't happen!)
+ // 12 pageBreak (TODO: we are in real trouble: this can actually happen without forcing a new paragraph!)
+ // 31 optionalHyphen
+ // ? text
+ //
+
+ int softHyphen = -2, nonBreakingSpace = -2, newLine = -2;
+
+ int upto = 0;
+ int stringUnicodeLength = stringUnicode.length ();
+ while (upto < stringUnicodeLength)
+ {
+ //
+ // look for KWord's special characters as defined in the DTD
+ //
+
+ if (softHyphen == -2)
+ {
+ softHyphen = stringUnicode.find (TQChar (0xAD), upto);
+ if (softHyphen == -1) softHyphen = INT_MAX;
+ }
+
+ if (nonBreakingSpace == -2)
+ {
+ nonBreakingSpace = stringUnicode.find (TQChar (0xA0), upto);
+ if (nonBreakingSpace == -1) nonBreakingSpace = INT_MAX;
+ }
+
+ if (newLine == -2)
+ {
+ newLine = stringUnicode.find (TQChar ('\n'), upto);
+ if (newLine == -1) newLine = INT_MAX;
+ }
+
+ // look for the closest one
+ int specialLocation = min (softHyphen, nonBreakingSpace, newLine);
+
+ // get substring (either to the end of the original string or before
+ // the next closest special character, if any)
+ int length = stringUnicodeLength - upto;
+ if (specialLocation != INT_MAX)
+ length = specialLocation - upto;
+ TQString substring = stringUnicode.mid (upto, length);
+
+ #ifdef DEBUG_PROCESS_TEXT
+ kdDebug (30509) << "Parent string: upto=" << upto
+ << ",length=" << stringUnicode.length () << endl;
+ kdDebug (30509) << "Child string: length=" << length
+ << " (specialLoc=" << specialLocation << ")" << endl;
+ #endif
+
+ //
+ // convert substring to windows-1251
+ //
+
+ TQCString stringWin;
+
+ // there is a codec, therefore there is an encoder...
+ if (m_codec)
+ {
+ int len; // don't overwrite length, we need it later
+
+ // convert from Unicode (UTF8)
+ stringWin = m_encoder->fromUnicode (substring, len = length);
+ }
+ else
+ {
+ // output a plain string still in wrong Character Set
+ // (hopefully the user won't notice)
+ stringWin = substring.utf8 ();
+ }
+
+
+ // output encoded text
+ if (!m_generator->writeText ((const MSWrite::Byte *) (const char *) stringWin))
+ return false;
+
+ upto += length;
+
+ // special character?
+ if (specialLocation != INT_MAX)
+ {
+ #ifdef DEBUG_PROCESS_TEXT
+ kdDebug (30509) << "Found special character!" << endl;
+ #endif
+
+ // output special character
+ if (specialLocation == softHyphen)
+ {
+ #ifdef DEBUG_PROCESS_TEXT
+ kdDebug (30509) << "\tSoft Hyphen" << endl;
+ #endif
+ if (!m_generator->writeOptionalHyphen ()) return false;
+ softHyphen = -2;
+ }
+ else if (specialLocation == nonBreakingSpace)
+ {
+ #ifdef DEBUG_PROCESS_TEXT
+ kdDebug (30509) << "\tNon-breaking Space" << endl;
+ #endif
+ // don't think Write supports nonBreakingSpace
+ if (!m_generator->writeText ((const MSWrite::Byte *) " ")) return false;
+ nonBreakingSpace = -2;
+ }
+ else if (specialLocation == newLine)
+ {
+ #ifdef DEBUG_PROCESS_TEXT
+ kdDebug (30509) << "\tNew Line" << endl;
+ #endif
+ // \r\n, not just \n
+ if (!m_generator->writeCarriageReturn ()) return false;
+ if (!m_generator->writeNewLine (true/*InternalGenerator doesn't care*/)) return false;
+ newLine = -2;
+ }
+ else
+ {
+ ErrorAndQuit (MSWrite::Error::InternalError, "simply impossible specialLocation\n");
+ }
+
+ // skip past special character
+ upto++;
+ }
+ } // while (upto < stringUnicodeLength) {
+
+ return true;
+ }
+};
+
+
+MSWriteExport::MSWriteExport (KoFilter *, const char *, const TQStringList &)
+ : KoFilter()
+{
+}
+
+MSWriteExport::~MSWriteExport ()
+{
+}
+
+KoFilter::ConversionStatus MSWriteExport::convert (const TQCString &from, const TQCString &to)
+{
+ kdDebug (30509) << "MSWriteExport $Date: 2006-02-12 19:28:12 +0100 (Sun, 12 Feb 2006) $ using LibMSWrite "
+ << MSWrite::Version << endl;
+
+ if (to != "application/x-mswrite" || from != "application/x-kword")
+ {
+ kdError (30509) << "Internal error! Filter not implemented?" << endl;
+ return KoFilter::NotImplemented;
+ }
+
+ KWordMSWriteWorker *worker = new KWordMSWriteWorker;
+ if (!worker)
+ {
+ kdError (30509) << "Could not allocate memory for worker" << endl;
+ return KoFilter::OutOfMemory;
+ }
+
+ KWEFKWordLeader *leader = new KWEFKWordLeader (worker);
+ if (!leader)
+ {
+ kdError (30509) << "Could not allocate memory for leader" << endl;
+ delete worker;
+ return KoFilter::OutOfMemory;
+ }
+
+ KoFilter::ConversionStatus ret = leader->convert (m_chain, from, to);
+ int errorCode = worker->getError ();
+
+ delete leader;
+ delete worker;
+
+ // try to return somewhat more meaningful errors than KoFilter::StupidError
+ // for the day that KOffice actually reports them to the user properly
+ switch (errorCode)
+ {
+ case MSWrite::Error::Ok:
+ kdDebug (30509) << "Returning error code " << ret << endl;
+ return ret; // not KoFilter::OK in case KWEFKWordLeader wants to report something
+
+ case MSWrite::Error::Warn:
+ kdDebug (30509) << "Error::Warn" << endl;
+ return KoFilter::InternalError; // warnings should _never_ set m_error
+
+ case MSWrite::Error::InvalidFormat:
+ kdDebug (30509) << "Error::InvalidFormat" << endl;
+ return KoFilter::InternalError; // how can the file I'm _writing_ be of an invalid format?
+
+ case MSWrite::Error::OutOfMemory:
+ kdDebug (30509) << "Error::OutOfMemory" << endl;
+ return KoFilter::OutOfMemory;
+
+ case MSWrite::Error::InternalError:
+ kdDebug (30509) << "Error::InternalError" << endl;
+ return KoFilter::InternalError;
+
+ case MSWrite::Error::Unsupported:
+ kdDebug (30509) << "Error::Unsupported" << endl;
+ return KoFilter::InternalError;
+
+ case MSWrite::Error::FileError:
+ kdDebug (30509) << "Error::FileError" << endl;
+ return KoFilter::CreationError;
+ }
+
+ kdWarning (30509) << "Unknown error" << endl;
+ return KoFilter::StupidError;
+}
+
+#include <mswriteexport.moc>