diff options
Diffstat (limited to 'filters/kword/mswrite/mswriteimport.cpp')
-rw-r--r-- | filters/kword/mswrite/mswriteimport.cpp | 1484 |
1 files changed, 1484 insertions, 0 deletions
diff --git a/filters/kword/mswrite/mswriteimport.cpp b/filters/kword/mswrite/mswriteimport.cpp new file mode 100644 index 000000000..7fd3b63e3 --- /dev/null +++ b/filters/kword/mswrite/mswriteimport.cpp @@ -0,0 +1,1484 @@ +/* This file is part of the KDE project + Copyright (C) 2001-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. +*/ + +#ifndef NDEBUG + //#define DEBUG_XML_OUTPUT +#endif + +#include <stdio.h> +#include <string.h> +#include <stdarg.h> + +#include <tqfile.h> +#include <tqobject.h> +#include <tqstring.h> +#include <tqtextcodec.h> +#include <tqtextstream.h> + +#include <kdebug.h> +#include <kgenericfactory.h> + +#include <KoFilterChain.h> +#include <KoStore.h> + +#include "list.h" +#include "libmswrite.h" + +#include "ImportDialog.h" +#include "mswriteimport.h" + + +class MSWriteImportFactory : KGenericFactory <MSWriteImport, KoFilter> +{ +public: + MSWriteImportFactory () : KGenericFactory <MSWriteImport, KoFilter> ("kwordmswriteimport") + { + } + +protected: + virtual void setupTranslations (void) + { + TDEGlobal::locale()->insertCatalogue ("kofficefilters"); + } +}; + +K_EXPORT_COMPONENT_FACTORY (libmswriteimport, MSWriteImportFactory ()) + + +// +// Device that reads from .WRI file +// + +class WRIDevice : public MSWrite::Device +{ +private: + FILE *m_infp; + +public: + WRIDevice () : m_infp (NULL) + { + } + + virtual ~WRIDevice () + { + closeFile (); + } + + bool openFile (const char *fileName) + { + m_infp = fopen (fileName, "rb"); + if (!m_infp) + { + error (MSWrite::Error::FileError, "could not open file for reading\n"); + return false; + } + + return true; + } + + bool closeFile (void) + { + if (m_infp) + { + if (fclose (m_infp)) + { + error (MSWrite::Error::FileError, "could not close input file\n"); + return false; + } + + m_infp = NULL; + } + + return true; + } + + bool read (MSWrite::Byte *buf, const MSWrite::DWord numBytes) + { + if (fread (buf, 1, (size_t) numBytes, m_infp) != (size_t) numBytes) + { + error (MSWrite::Error::FileError, "could not read from input file\n"); + return false; + } + + return true; + } + + bool write (const MSWrite::Byte *, const MSWrite::DWord) + { + error (MSWrite::Error::InternalError, "writing to an input file?\n"); + return false; + } + + bool seek (const long offset, const int whence) + { + if (fseek (m_infp, offset, whence)) + { + error (MSWrite::Error::InternalError, "could not seek input file\n"); + return false; + } + + return true; + } + + long tell (void) + { + return ftell (m_infp); + } + + 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 /*tokenValue*/ = NoToken) + { + if (errorCode == MSWrite::Error::Warn) + kdWarning (30509) << message; + else + { + m_error = errorCode; + kdError (30509) << message; + } + } +}; + + +// +// Generator that creates the KWord file +// + +class KWordGenerator : public MSWrite::Generator, public MSWrite::NeedsDevice +{ +private: + // KoStore can only have 1 file open at a time + // so we store objects in this structure temporarily + class WRIObject + { + private: + WRIObject (const WRIObject &rhs); + + public: + MSWrite::Byte *m_data; + MSWrite::DWord m_dataLength; + MSWrite::DWord m_dataUpto; + TQString m_nameInStore; + + WRIObject () : m_data (NULL), m_dataLength (0), m_dataUpto (0) + { + } + + ~WRIObject () + { + delete [] m_data; + } + + WRIObject operator= (const WRIObject &rhs) + { + delete [] m_data; + + m_dataLength = rhs.m_dataLength; + m_dataUpto = rhs.m_dataUpto; + m_nameInStore = rhs.m_nameInStore; + + if (rhs.m_data) + { + m_data = new MSWrite::Byte [m_dataLength]; + if (m_data) + memcpy (m_data, rhs.m_data, m_dataLength); + // remember to check m_data before use + } + else + m_data = NULL; + + return *this; + } + }; + + // page/margin dimensions + int m_pageWidth, m_pageHeight; + int m_left, m_right, m_top, m_bottom; // describing border of Text Frameset (position, not magnitude) + int m_leftMargin, m_rightMargin, m_topMargin, m_bottomMargin; + int m_headerFromTop, m_footerFromTop; + bool m_hasHeader, m_isHeaderOnFirstPage; + bool m_hasFooter, m_isFooterOnFirstPage; + bool m_writeHeaderFirstTime, m_writeFooterFirstTime; + + int inWhat; + + enum inWhatPossiblities + { + Nothing, + Header, + Footer, + Body + }; + + int m_startingPageNumber; + + KoFilterChain *m_chain; + KoStoreDevice *m_outfile; + + // for charset conversion + TQTextCodec *m_codec; + TQTextDecoder *m_decoder; + + // import options (compensate for differences between KWord and MS Write) + bool m_simulateLineSpacing; + bool m_simulateImageOffset; + + // formatting + TQString m_formatOutput; + int m_charInfoCountStart, m_charInfoCountLen; + bool m_pageBreak, m_needAnotherParagraph; + int m_pageBreakOffset; + int m_lineSpacingFromAbove; + + // picture counters + int m_numPictures; + TQString m_pictures; + + TQString m_objectFrameset; + + MSWrite::List <WRIObject> m_objectList; + + double m_objectHorizOffset; + bool m_paraIsImage; + + MSWriteImport *m_koLink; + + // XML output that is held back until after "Text Frameset 1" is output + // (i.e. header & footer must come after the Body in KWord) + bool m_delayOutput; + TQString m_heldOutput; + + void delayOutput (const bool yes) + { + m_delayOutput = yes; + } + + bool delayOutputFlush (void) + { + TQCString strUtf8 = m_heldOutput.utf8 (); + int strLength = strUtf8.length (); + + if (m_outfile->writeBlock (strUtf8, strLength) != strLength) + ErrorAndQuit (MSWrite::Error::FileError, "could not write delayed output\n"); + + m_heldOutput = ""; + return true; + } + +public: + KWordGenerator () : m_hasHeader (false), m_isHeaderOnFirstPage (false), + m_hasFooter (false), m_isFooterOnFirstPage (false), + m_writeHeaderFirstTime (true), m_writeFooterFirstTime (true), + inWhat (Nothing), + m_decoder (NULL), + m_simulateLineSpacing (false), + m_simulateImageOffset (true), + m_pageBreak (false), m_needAnotherParagraph (false), + m_lineSpacingFromAbove (0), + m_numPictures (0) + { + delayOutput (false); + + // just select windows-1252 until the "Select Encoding" dialog works + m_codec = TQTextCodec::codecForName ("CP 1252"); + + if (m_codec) + m_decoder = m_codec->makeDecoder(); + else + kdWarning (30509) << "Cannot convert from Win Charset!" << endl; + } + + virtual ~KWordGenerator () + { + delete m_decoder; + } + + void setFilterChain (KoFilterChain *chain) + { + m_chain = chain; + } + + bool writeDocumentBegin (const MSWrite::Word, + const MSWrite::PageLayout *pageLayout) + { + kdDebug (30509) << "writeDocumentBegin()" << endl; + + // open maindoc.xml + m_outfile = m_chain->storageFile ("root", KoStore::Write); + if (!m_outfile) + ErrorAndQuit (MSWrite::Error::FileError, "could not open root in store\n"); + + + // + // store page dimensions for now + // + + // page width & height + m_pageWidth = Twip2Point (pageLayout->getPageWidth ()); + m_pageHeight = Twip2Point (pageLayout->getPageHeight ()); + + // offset of margins + m_left = Twip2Point (pageLayout->getLeftMargin ()); + m_right = m_left + Twip2Point (pageLayout->getTextWidth ()) - 1; + m_top = Twip2Point (pageLayout->getTopMargin ()); + m_bottom = m_top + Twip2Point (pageLayout->getTextHeight ()) - 1; + + // size of margins + m_leftMargin = m_left; + m_rightMargin = Twip2Point (pageLayout->getRightMargin ()); + m_topMargin = m_top; + m_bottomMargin = Twip2Point (pageLayout->getBottomMargin ()); + + kdDebug (30509) << "leftMargin: " << m_leftMargin << endl; + kdDebug (30509) << "rightMargin: " << m_rightMargin << endl; + kdDebug (30509) << "topMargin: " << m_topMargin << endl; + kdDebug (30509) << "bottomMargin: " << m_bottomMargin << endl; + + // offset of header & footer + m_headerFromTop = Twip2Point (pageLayout->getHeaderFromTop ()); + m_footerFromTop = Twip2Point (pageLayout->getFooterFromTop ()); + + kdDebug (30509) << "headerFromTop: " << m_headerFromTop + << " footerFromTop: " << m_footerFromTop << endl; + + m_startingPageNumber = pageLayout->getPageNumberStart (); + + return true; + } + + bool writeDocumentBeginForReal (void) + { + kdDebug (30509) << "writeDocumentBeginForReal()" << endl; + + // adjust margins/PAPERBORDERS to ensure that the header & footer are + // within them + // TODO: stop header & footer from changing body's location + // TODO: recompute offset of margins after recomputing margins + if (m_hasHeader) + if (m_headerFromTop < m_topMargin) + m_topMargin = m_headerFromTop; + + if (m_hasFooter) + if (m_pageHeight - m_footerFromTop < m_bottomMargin) + m_bottomMargin = m_pageHeight - m_footerFromTop; + + kdDebug (30509) << "adjusted::: leftMargin: " << m_leftMargin + << " rightMargin: " << m_rightMargin + << " topMargin: " << m_topMargin + << " bottomMargin: " << m_bottomMargin + << endl; + + // start document + // TODO: error checking + writeTextInternal ("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); + writeTextInternal ("<!DOCTYPE DOC PUBLIC \"-//KDE//DTD kword 1.3//EN\" " + "\"http://www.koffice.org/DTD/kword-1.3.dtd\">"); + writeTextInternal ("<DOC xmlns=\"http://www.koffice.org/DTD/kword\" " + "mime=\"application/x-kword\" " + "syntaxVersion=\"3\" editor=\"KWord\">"); + + writeTextInternal ("<PAPER format=\"1\" " + "width=\"%i\" height=\"%i\" " + "orientation=\"0\" columns=\"1\" " + "hType=\"%i\" fType=\"%i\">", + m_pageWidth, m_pageHeight, + m_isHeaderOnFirstPage ? 0 : 2, + m_isFooterOnFirstPage ? 0 : 2); + + writeTextInternal ("<PAPERBORDERS left=\"%i\" right=\"%i\" " + "top=\"%i\" bottom=\"%i\"/>", + m_leftMargin, m_rightMargin, + m_topMargin, m_bottomMargin); + + writeTextInternal ("</PAPER>"); + + writeTextInternal ("<ATTRIBUTES processing=\"0\" " + "tabStopValue=\"%lf\" " + "hasHeader=\"%i\" hasFooter=\"%i\"/>", + Inch2Point (0.5), + m_hasHeader ? 1 : 0, m_hasFooter ? 1 : 0); + + // handle page numbering not starting from 1 + if (m_startingPageNumber != 1) + writeTextInternal ("<VARIABLESETTINGS startingPageNumber=\"%i\"/>", + m_startingPageNumber); + + writeTextInternal ("<FRAMESETS>"); + + return true; + } + + bool writeDocumentEnd (const MSWrite::Word, const MSWrite::PageLayout *) + { + kdDebug (30509) << "writeDocumentEnd()" << endl; + + // write framesets for the objects + writeTextInternal (m_objectFrameset); + + writeTextInternal ("</FRAMESETS>"); + writeTextInternal ("<STYLES>"); + writeTextInternal ("<STYLE>"); + writeTextInternal ("<NAME value=\"Standard\"/>"); + writeTextInternal ("<FLOW align=\"left\"/>"); + writeTextInternal ("<INDENTS first=\"0\" left=\"0\" right=\"0\"/>"); + writeTextInternal ("<OFFSETS before=\"0\" after=\"0\"/>"); + + writeTextInternal ("<FORMAT id=\"1\">"); + writeTextInternal ("<COLOR blue=\"0\" red=\"0\" green=\"0\"/>"); + writeTextInternal ("<FONT name=\"helvetica\"/>"); + writeTextInternal ("<SIZE value=\"12\"/>"); + writeTextInternal ("<WEIGHT value=\"50\"/>"); + writeTextInternal ("<ITALIC value=\"0\"/>"); + writeTextInternal ("<UNDERLINE value=\"0\"/>"); + writeTextInternal ("<STRIKEOUT value=\"0\"/>"); + writeTextInternal ("<VERTALIGN value=\"0\"/>"); + writeTextInternal ("</FORMAT>"); + + writeTextInternal ("<FOLLOWING name=\"Standard\"/>"); + writeTextInternal ("</STYLE>"); + writeTextInternal ("</STYLES>"); + + // write out image keys + writeTextInternal ("<PICTURES>"); + writeTextInternal (m_pictures); + writeTextInternal ("</PICTURES>"); + + // end document + writeTextInternal ("</DOC>"); + + // close maindoc.xml + m_outfile->close (); + m_outfile = (KoStoreDevice *) NULL; + + // + // output object data + + /*if (m_objectUpto != getNumObjects ()) + warning ("m_objectUpto (%i) != getNumObjects() (%i) -- this is probably because OLE is unimplemented\n", + m_objectUpto, getNumObjects ());*/ + + MSWrite::List <WRIObject>::Iterator it; + MSWrite::List <WRIObject>::Iterator end(m_objectList.end ()); + for (it = m_objectList.begin (); it != end; ++it) + { + kdDebug (30509) << "outputting object \'" << (*it).m_nameInStore + << "\' (length: " << (*it).m_dataLength << ")" + << endl; + + if (!(*it).m_data) + ErrorAndQuit (MSWrite::Error::InternalError, "image data not initialised\n"); + + // open file for object in store + m_outfile = m_chain->storageFile ((*it).m_nameInStore, KoStore::Write); + if (!m_outfile) + ErrorAndQuit (MSWrite::Error::FileError, "could not open image in store\n"); + + if (m_outfile->writeBlock ((const char *) (*it).m_data, (*it).m_dataLength) + != (TQ_LONG) (*it).m_dataLength) + ErrorAndQuit (MSWrite::Error::FileError, "could not write image to store\n"); + + // close object in store + m_outfile->close (); + m_outfile = NULL; + } + + return true; + } + + + bool writeFooterBegin (void) + { + kdDebug (30509) << "writeFooterBegin()" << endl; + + inWhat = Footer; + m_hasFooter = true; + + // footers must go after body in KWord + delayOutput (true); + + // footer frameset will be written in writeParaInfoBegin() + + return true; + } + + bool writeFooterEnd (void) + { + kdDebug (30509) << "writeFooterEnd()" << endl; + + inWhat = Nothing; + + if (!m_writeFooterFirstTime) + writeTextInternal ("</FRAMESET>"); + delayOutput (false); + + return true; + } + + bool writeHeaderBegin (void) + { + kdDebug (30509) << "writeHeaderBegin()" << endl; + + inWhat = Header; + m_hasHeader = true; + + // headers must go after body in KWord + delayOutput (true); + + // header frameset will be written in writeParaInfoBegin() + + return true; + } + + bool writeHeaderEnd (void) + { + kdDebug (30509) << "writeHeaderEnd()" << endl; + + inWhat = Nothing; + + if (!m_writeHeaderFirstTime) + writeTextInternal ("</FRAMESET>"); + delayOutput (false); + + return true; + } + + bool writeBodyBegin (void) + { + kdDebug (30509) << "writeBodyBegin()" << endl; + + inWhat = Body; + + // writeFooterBegin() and writeHeaderBegin() have been called by now + // so we have enough information to actually write about them + writeDocumentBeginForReal (); + + writeTextInternal ("<FRAMESET frameType=\"1\" frameInfo=\"0\" name=\"Text Frameset 1\" visible=\"1\">"); + // TODO: runaround? + writeTextInternal ("<FRAME runaround=\"1\" autoCreateNewFrame=\"1\" newFrameBehavior=\"0\" copy=\"0\"" + " top=\"%i\" bottom=\"%i\" left=\"%i\" right=\"%i\"/>", + m_top, m_bottom, m_left, m_right); + + return true; + } + + bool writeBodyEnd (void) + { + kdDebug (30509) << "writeBodyEnd()" << endl; + + inWhat = Nothing; + + // <PAGEBREAKING hardFrameBreakAfter=\"true\"/>" may have been in the last paragraph + // and for "hardFrameBreakAfter" to do its work, we need one more final paragraph! + if (m_needAnotherParagraph) + { + kdDebug (30509) << "needAnotherParagraph in bodyEndWrite()" << endl; + writeTextInternal ("<PARAGRAPH><TEXT></TEXT><LAYOUT></LAYOUT></PARAGRAPH>"); + m_needAnotherParagraph = false; + } + + writeTextInternal ("</FRAMESET>"); + + // since "Text Frameset 1" has ended, we can output the header & footer, now + delayOutputFlush (); + + return true; + } + + bool writeParaInfoBegin (const MSWrite::FormatParaProperty *paraProperty, + const MSWrite::OLE *ole, + const MSWrite::Image *image) + { + //kdDebug (30509) << "writeParaInfoBegin()" << endl; + + // reset charInfo counters + m_charInfoCountStart = 0; + m_charInfoCountLen = 0; + + if (inWhat == Header) + { + m_isHeaderOnFirstPage = paraProperty->getIsOnFirstPage (); + + if (m_writeHeaderFirstTime) + { + // dummy header frames + // + + // except, if the header is NOT on the first page, then make an empty "First Page Header" + // by setting "visible=1" + writeTextInternal ("<FRAMESET frameType=\"1\" frameInfo=\"1\" name=\"First Page Header\" visible=\"%i\">", + m_isHeaderOnFirstPage ? 1 : 0); + writeTextInternal ("<FRAME runaround=\"1\" copy=\"0\" newFrameBehavior=\"2\" autoCreateNewFrame=\"0\"" + " top=\"%i\" bottom=\"%i\" left=\"%i\" right=\"%i\"/>", + m_headerFromTop, m_headerFromTop, m_left, m_right); + writeTextInternal ("</FRAMESET>"); + + writeTextInternal ("<FRAMESET frameType=\"1\" frameInfo=\"2\" name=\"Even Pages Header\" visible=\"0\">"); + writeTextInternal ("<FRAME runaround=\"1\" copy=\"0\" newFrameBehavior=\"2\" autoCreateNewFrame=\"0\"" + " top=\"%i\" bottom=\"%i\" left=\"%i\" right=\"%i\"/>", + m_headerFromTop, m_headerFromTop, m_left, m_right); + writeTextInternal ("</FRAMESET>"); + + // real header frame + writeTextInternal ("<FRAMESET frameType=\"1\" frameInfo=\"3\" name=\"Odd Pages Header\" visible=\"1\">"); + writeTextInternal ("<FRAME runaround=\"1\" copy=\"1\" newFrameBehavior=\"2\" autoCreateNewFrame=\"0\"" + " top=\"%i\" bottom=\"%i\" left=\"%i\" right=\"%i\"/>", + m_headerFromTop, m_headerFromTop, m_left, m_right); + + m_writeHeaderFirstTime = false; + } + } + else if (inWhat == Footer) + { + m_isFooterOnFirstPage = paraProperty->getIsOnFirstPage (); + + if (m_writeFooterFirstTime) + { + // dummy footer frames + // + + // except, if the footer is NOT on the first page, then make an empty "First Page Footer" + // by setting "visible=1" + writeTextInternal ("<FRAMESET frameType=\"1\" frameInfo=\"4\" name=\"First Page Footer\" visible=\"%i\">", + m_isFooterOnFirstPage ? 1 : 0); + writeTextInternal ("<FRAME runaround=\"1\" copy=\"0\" newFrameBehavior=\"2\" autoCreateNewFrame=\"0\"" + " top=\"%i\" bottom=\"%i\" left=\"%i\" right=\"%i\"/>", + m_footerFromTop, m_footerFromTop, m_left, m_right); + writeTextInternal ("</FRAMESET>"); + + writeTextInternal ("<FRAMESET frameType=\"1\" frameInfo=\"5\" name=\"Even Pages Footer\" visible=\"0\">"); + writeTextInternal ("<FRAME runaround=\"1\" copy=\"0\" newFrameBehavior=\"2\" autoCreateNewFrame=\"0\"" + " top=\"%i\" bottom=\"%i\" left=\"%i\" right=\"%i\"/>", + m_footerFromTop, m_footerFromTop, m_left, m_right); + writeTextInternal ("</FRAMESET>"); + + // real footer frame + writeTextInternal ("<FRAMESET frameType=\"1\" frameInfo=\"6\" name=\"Odd Pages Footer\" visible=\"1\">"); + writeTextInternal ("<FRAME runaround=\"1\" copy=\"1\" newFrameBehavior=\"2\" autoCreateNewFrame=\"0\"" + " top=\"%i\" bottom=\"%i\" left=\"%i\" right=\"%i\"/>", + m_footerFromTop, m_footerFromTop, m_left, m_right); + + m_writeFooterFirstTime = false; + } + } + + if (!writeTextInternal ("<PARAGRAPH><TEXT>")) return false; + + if (image) + { + kdDebug (30509) << "Paragraph is an image!" << endl; + + TQString imageName; + TQString fileInStore; + + + // give picture a name + // + + imageName = "Picture "; + imageName += TQString::number (m_numPictures + 1); // image numbers start at 1... + + + // give picture a filename + // + + fileInStore = "pictures/picture" + TQString::number (m_numPictures + 1); + + kdDebug (30509) << "\tGetting type..." << endl; + + // append extension + if (image->getIsBMP ()) + fileInStore += ".bmp"; + else if (image->getIsWMF () ) + fileInStore += ".wmf"; + else + ErrorAndQuit (MSWrite::Error::InternalError, "unsupported picture type\n"); + + + // indicate anchored image in formatting + // + kdDebug (30509) << "\tIndicating anchored image in formatting" << endl; + if (!writeTextInternal ("#")) return false; + + m_formatOutput += "<FORMAT id=\"6\" pos=\"0\" len=\"1\">"; + m_formatOutput += "<ANCHOR type=\"frameset\" instance=\""; + m_formatOutput += imageName; + m_formatOutput += "\"/>"; + m_formatOutput += "</FORMAT>"; + + + // write framesets (truly written in documentEndWrite()) + // + kdDebug (30509) << "\tWriting framesets!" << endl; + + m_objectFrameset += "<FRAMESET frameType=\"2\" frameInfo=\"0\" name=\""; + m_objectFrameset += imageName; + m_objectFrameset += "\" visible=\"1\">"; + + m_objectFrameset += "<FRAME runaround=\"1\" copy=\"0\" newFrameBehavior=\"1\""; + + const double imageLeft = double (m_left) + Twip2Point (double (image->getIndent ())); + m_objectFrameset += " left=\""; + m_objectFrameset += TQString::number (imageLeft); + m_objectFrameset += "\""; + + const double imageWidth = Twip2Point (double (image->getDisplayedWidth ())); + m_objectFrameset += " right=\""; + m_objectFrameset += TQString::number (imageLeft + imageWidth - 1); + m_objectFrameset += "\""; + + m_objectFrameset += " top=\""; + m_objectFrameset += TQString::number (m_top); + m_objectFrameset += "\""; + + const double imageHeight = Twip2Point (double (image->getDisplayedHeight ())); + m_objectFrameset += " bottom=\""; + m_objectFrameset += TQString::number (double (m_top) + imageHeight - 1); + m_objectFrameset += "\"/>"; + + m_objectFrameset += "<PICTURE keepAspectRatio=\"false\">"; + m_objectFrameset += "<KEY msec=\"0\" hour=\"0\" second=\"0\" minute=\"0\" day=\"1\" month=\"1\" year=\"1970\""; + m_objectFrameset += " filename=\""; + m_objectFrameset += fileInStore; + m_objectFrameset += "\"/>"; + m_objectFrameset += "</PICTURE>"; + + m_objectFrameset += "</FRAMESET>"; + #ifdef DEBUG_XML_OUTPUT + m_objectFrameset += "\n"; + #endif + + m_pictures += "<KEY msec=\"0\" hour=\"0\" second=\"0\" minute=\"0\" day=\"1\" month=\"1\" year=\"1970\""; + m_pictures += " name=\""; + m_pictures += fileInStore; + m_pictures += "\""; + m_pictures += " filename=\""; + m_pictures += fileInStore; + m_pictures += "\"/>"; + + m_numPictures++; + + + // store object properties + // + kdDebug (30509) << "\tStoring object" << endl; + + if (!m_objectList.addToBack ()) + ErrorAndQuit (MSWrite::Error::OutOfMemory, "could not allocate memory for object\n"); + + WRIObject &obj = *m_objectList.begin (false); + obj.m_nameInStore = fileInStore; + obj.m_dataLength = image->getExternalImageSize (); + obj.m_data = new MSWrite::Byte [obj.m_dataLength]; + if (!obj.m_data) + ErrorAndQuit (MSWrite::Error::OutOfMemory, "could not allocate memory for object data\n"); + + // if anchored images could be positioned properly, this wouldn't be needed + m_objectHorizOffset = double (Twip2Point (image->getIndent ())); + m_paraIsImage = true; + } + else + { + if (ole) + { + if (!writeTextInternal ("[OLE unsupported]")) return false; + } + + m_paraIsImage = false; + } + + + return true; + } + + bool writeParaInfoEnd (const MSWrite::FormatParaProperty *paraProperty, + const MSWrite::OLE * /*ole*/, + const MSWrite::Image *image) + + { + //kdDebug (30509) << "writeParaInfoEnd()" << endl; + + if (image) + { + WRIObject &obj = *m_objectList.begin (false); + + // consistency check: wrote exactly the right amount of data? + if (obj.m_dataUpto != obj.m_dataLength) + kdWarning (30509) << "obj.dataUpto (" << obj.m_dataUpto + << ") != obj.dataLength (" << obj.m_dataLength + << ")" << endl; + } + + TQString output; + output += "</TEXT>"; + + output += "<LAYOUT>"; + output += "<NAME value=\"Standard\"/>"; + + int align = paraProperty->getAlignment (); + + if (align != MSWrite::Alignment::Left) + { + output += "<FLOW align=\""; + switch (align) + { + /*case MSWrite::Alignment::Left: + output += "left"; + break;*/ + case MSWrite::Alignment::Centre: + output += "center"; + break; + case MSWrite::Alignment::Right: + output += "right"; + break; + case MSWrite::Alignment::Justify: + output += "justify"; + break; + } + output += "\"/>"; + } + + double indentFirst = Twip2Point (double (paraProperty->getLeftIndentFirstLine ())); + double indentLeft = Twip2Point (double (paraProperty->getLeftIndent ())); + double indentRight = Twip2Point (double (paraProperty->getRightIndent ())); + + #if 0 + debug ("raw indent: first: %i left: %i right: %i\n", + indentFirst, indentLeft, indentRight); + #endif + + if (m_paraIsImage /*paraProperty->isObject ()*/ && m_objectHorizOffset) + { + if (align == MSWrite::Align::Center) + { + // TODO: I don't know what m_objectHorizOffset is relative to! + kdDebug (30509) << "ignoring image offset with centred image" << endl; + m_objectHorizOffset = 0; + } + else + { + // MSWrite does not add the horizontal offset of the image from the left margin to the Left Indent + // -- instead, it selects the bigger one + // TODO: proper image positioning (see doc IMPERFECT) + if (m_simulateImageOffset && m_objectHorizOffset > indentLeft) + { + kdDebug (30509) << "image is further away from left margin by itself, rather than using indentLeft (" + << m_objectHorizOffset << " > " << indentLeft << ")" << endl; + indentLeft = m_objectHorizOffset; + } + } + } + + // hopefully these test operations will be cheaper than the XML ones :) + if (indentFirst || indentLeft || indentRight) + { + output += "<INDENTS"; + if (indentFirst) output += " first=\"" + TQString::number (indentFirst) + "\""; + if (indentLeft) output += " left=\"" + TQString::number (indentLeft) + "\""; + if (indentRight) output += " right=\"" + TQString::number (indentRight) + "\""; + output += "/>"; + } + + MSWrite::Word lineSpacing = paraProperty->getLineSpacing (); + + if (lineSpacing != MSWrite::LineSpacing::Single) + { + #if 1 + output += "<LINESPACING type=\"atleast\" spacingvalue=\"" + TQString::number (Twip2Point (lineSpacing)) + "\"/>"; + #else // old way + output += "<LINESPACING type=\""; + switch (lineSpacing) + { + //case MSWrite:;LineSpacing::Single: + // break; + case MSWrite::LineSpacing::OneAndAHalf: + output += "oneandhalf"; + break; + case MSWrite::LineSpacing::Double: + output += "double"; + break; + default: + kdWarning (30509) << "non-\"standard\" linespacing value: " << lineSpacing << endl; + output += "atleast\" "; + output += "spacingvalue=\""; + output += TQString::number (Twip2Point (lineSpacing)); + break; + } + output += "\"/>"; + #endif + } + + // Do we want the linespacing to _look_ like it does in Write? + // (this adds extra space before each paragraph) + if (m_simulateLineSpacing) + { + // emulate Write's linespacing (aligned to bottom) + // by using varying amounts of space before the paragraph + // TODO: test if it works nicely enough (what if you have several different sized fonts in paragraph?) + if (lineSpacing != MSWrite::LineSpacing::Single) // if not normal linespacing... + { + output += "<OFFSETS before=\""; + + int amount = 0; + switch (lineSpacing) + { + /*case MSWrite::LineSpacing::Single: + break;*/ + case MSWrite::LineSpacing::OneAndAHalf: + amount = 7; + break; + case MSWrite::LineSpacing::Double: + amount = 14; + break; + default: + kdWarning (30509) << "unknown linespacing value: " << lineSpacing << endl; + break; + } + + // subtract the amount of trailing linespace from last paragraph + amount -= m_lineSpacingFromAbove; + if (amount <= 0) amount = 0; // no emulation can be perfect... + + output += TQString::number (amount); + output += "\" />"; + } + + // GUESS (TODO: fix) the amount of trailing linespace + switch (lineSpacing) + { + case MSWrite::LineSpacing::Single: + m_lineSpacingFromAbove = 0; + break; + case MSWrite::LineSpacing::OneAndAHalf: + m_lineSpacingFromAbove = 7; + break; + case MSWrite::LineSpacing::Double: + m_lineSpacingFromAbove = 14; + break; + default: // unknown + m_lineSpacingFromAbove = 0; + break; + } + } // if (m_simulateLineSpacing) { + + if (m_pageBreak) + { + #if 0 + debug ("\tpagebrk: output: offset: %i chars in paragraph: %i\n", + m_pageBreakOffset, m_charInfoCountStart + m_charInfoCountLen); + #endif + + // page break before all the text + if (m_pageBreakOffset == 0 && m_charInfoCountStart + m_charInfoCountLen > 0) + { + output += "<PAGEBREAKING hardFrameBreak=\"true\"/>"; + m_needAnotherParagraph = false; // this paragraph is on first page so we don't need another one + } + // we assume that the pageBreak was after all the text (TODO: don't assume this) + else + { + output += "<PAGEBREAKING hardFrameBreakAfter=\"true\"/>"; + m_needAnotherParagraph = true; // need another paragraph for hardFrameBreakAfter to work + } + + m_pageBreak = false; // reset flag + } + else + m_needAnotherParagraph = false; + + // Tabulators + for (int i = 0; i < paraProperty->getNumTabulator (); i++) + { + const MSWrite::FormatParaPropertyTabulator *tab = paraProperty->getTabulator (i); + if (tab->getIsDummy ()) break; + + output += "<TABULATOR"; + + if (tab->getIsDecimal ()) + output += " type=\"3\" alignchar=\".\""; + else + output += " type=\"0\""; + + output += " ptpos=\"" + TQString::number (Twip2Point (double (tab->getIndent ()))) + "\"/>"; + + //debug ("Tab: isNormal: %i ptPos: %i\n", + // paraProperty->tbd [i].isTabNormal (), paraProperty->tbd [i].getTabNumPoints ()); + } + + output += "</LAYOUT>"; + #ifdef DEBUG_XML_OUTPUT + output += "\n"; + #endif + + output += "<FORMATS>"; + #ifdef DEBUG_XML_OUTPUT + output += "\n"; + #endif + // output all the charInfo for this paragraph + output += m_formatOutput; m_formatOutput = ""; + output += "</FORMATS>"; + #ifdef DEBUG_XML_OUTPUT + output += "\n"; + #endif + + output += "</PARAGRAPH>"; + #ifdef DEBUG_XML_OUTPUT + output += "\n"; + #endif + + if (!writeTextInternal (output)) return false; + + return true; + } + + bool writeCharInfoBegin (const MSWrite::FormatCharProperty * /*charProperty*/) + { + //kdDebug (30509) << "writeCharInfoBegin()" << endl; + + return true; + } + + // outputs character formatting tags + bool writeCharInfoEnd (const MSWrite::FormatCharProperty *charProperty, + const bool = false) + { + //kdDebug (30509) << "writeCharInfoEnd()" << endl; + + // output type of format information (page number or normal text) + m_formatOutput += "<FORMAT id=\""; + if (charProperty->getIsPageNumber ()) + m_formatOutput += "4"; + else + m_formatOutput += "1"; + m_formatOutput += "\" "; + + m_formatOutput += "pos=\""; m_formatOutput += TQString::number (m_charInfoCountStart); m_formatOutput += "\" "; + m_formatOutput += "len=\""; m_formatOutput += TQString::number (m_charInfoCountLen); m_formatOutput += "\">"; + + m_charInfoCountStart += m_charInfoCountLen; + m_charInfoCountLen = 0; + + if (charProperty->getIsPageNumber ()) + { + m_formatOutput += "<VARIABLE>"; + m_formatOutput += "<TYPE key=\"NUMBER\" type=\"4\"/>"; + m_formatOutput += "<PGNUM subtype=\"0\" value=\"1\"/>"; + m_formatOutput += "</VARIABLE>"; + } + + m_formatOutput += "<FONT name=\""; + m_formatOutput += (const char *) (charProperty->getFont ()->getName ()); + m_formatOutput += "\"/>"; + m_formatOutput += "<SIZE value=\""; + m_formatOutput += TQString::number (charProperty->getFontSize ()); + m_formatOutput += "\"/>"; + + if (charProperty->getIsBold ()) + m_formatOutput += "<WEIGHT value=\"75\"/>"; + //else + // m_formatOutput += "<WEIGHT value=\"50\" />"; + + if (charProperty->getIsItalic ()) + m_formatOutput += "<ITALIC value=\"1\"/>"; + // else + // m_formatOutput += "<ITALIC value=\"0\" />"; + + if (charProperty->getIsUnderlined ()) + m_formatOutput += "<UNDERLINE value=\"1\"/>"; + // else + // m_formatOutput += "<UNDERLINE value=\"0\" />"; + + /*if (charProperty->isNormalPosition ()) + m_formatOutput += "<VERTALIGN value=\"0\" />"; + else*/ if (charProperty->getIsSubscript ()) + m_formatOutput += "<VERTALIGN value=\"1\"/>"; + else if (charProperty->getIsSuperscript ()) + m_formatOutput += "<VERTALIGN value=\"2\"/>"; + /*else + error ("unknown valign\n");*/ + + m_formatOutput += "</FORMAT>"; + + return true; + } + + bool writeBinary (const MSWrite::Byte *buffer, const MSWrite::DWord length) + { + kdDebug (30509) << "writeBinary()" << endl; + + // must be OLE, TODO: implement OLE properly + if (!m_paraIsImage) + return true; + + WRIObject &obj = *m_objectList.begin (false); + + if (!obj.m_data) + ErrorAndQuit (MSWrite::Error::InternalError, "object data not initialised\n"); + + // consistency check: aren't going to write past end of array? + if (obj.m_dataUpto + length > obj.m_dataLength) + { + kdDebug (30509) << "object image overrun: " + << obj.m_dataUpto << " + " << length + << " > " << obj.m_dataLength << endl; + ErrorAndQuit (MSWrite::Error::InternalError, "object image overrun\n"); + } + + memcpy (obj.m_data + obj.m_dataUpto, buffer, length); + obj.m_dataUpto += length; + + return true; + } + + + + // + // text output functions + // + + bool writeText (const MSWrite::Byte *string) + { + // from Win Character Set... + TQString strUnicode; + + // there is a codec, therefore there is a decoder... + if (m_codec) + { + // output Unicode (UTF8) + strUnicode = m_decoder->toUnicode ((const char *) string, strlen ((const char *) string)); + } + else + { + // output a plain string still in wrong Character Set + // (hopefully the user won't notice) + strUnicode = (const char *) string; + } + + // update character information counter (after charset conversion) + m_charInfoCountLen += strUnicode.length (); + + // make string XML-friendly (TODO: speed up) + strUnicode.replace ('&', "&"); + strUnicode.replace ('<', "<"); + strUnicode.replace ('>', ">"); + strUnicode.replace ('\"', """); + strUnicode.replace ('\'', "'"); + + return writeTextInternal (strUnicode); + } + + bool writeTextInternal (const MSWrite::Byte *str) + { + #if 0 + return textWrite_lowLevel (TQString (str)); + #else // while this is code duplication (of below func), this ensures that no + // characters are mysteriously converted (this makes writeOptionalHyphen () work) + if (m_delayOutput) + { + // header/footer must be written after main body + m_heldOutput += (const char *) str; + return true; + } + else + { + int strLength = strlen ((const char *) str); + + if (m_outfile->writeBlock ((const char *) str, strLength) != strLength) + { + ErrorAndQuit (MSWrite::Error::FileError, "could not write to maindoc.xml\n"); + } + else + return true; + } + #endif + } + + bool writeTextInternal (const TQString &str) + { + if (m_delayOutput) + { + // header/footer must be written after main body + m_heldOutput += str; + return true; + } + else + { + TQCString strUtf8 = str.utf8 (); + int strLength = strUtf8.length (); + + if (m_outfile->writeBlock (strUtf8, strLength) != strLength) + { + ErrorAndQuit (MSWrite::Error::FileError, "could not write to maindoc.xml (2)\n"); + } + else + return true; + } + } + + bool writeTextInternal (const int num) + { + return writeTextInternal (TQString::number (num)); + } + + bool writeTextInternal (const char *format, ...) + { + va_list list; + va_start (list, format); + + bool ret; + // This function is mainly for outputting tags (where XML characters are + // already escaped and the text is in the right character set...ASCII + // = UTF-8 for alphanumeric chars I hope). So _don't_ pass user text + // to this function (that's what writeText() is for); otherwise you might + // exceed this 1024 limit. + char string [1024]; + + vsnprintf (string, sizeof (string) - 1, format, list); + string [sizeof (string) - 1] = '\0'; + + ret = writeTextInternal ((const MSWrite::Byte *) string); + + va_end (list); + return ret; + } + + + // writePageNew() is called for the pageTable + // -- however, pageTable can be very inaccurate, so we ignore it + bool writePageNew (const int) + { + return true; + } + + // handles explicit page breaks + bool writePageBreak (void) + { + // later used in paraEndWrite + m_pageBreak = true; + m_pageBreakOffset = m_charInfoCountStart + m_charInfoCountLen; + + return true; + } + + // handle "(page)" number + bool writePageNumber (void) + { + m_charInfoCountLen++; // not incremented by writeTextInternal() + return writeTextInternal ("#"); + } + + bool writeCarriageReturn (void) + { + return true; // ignore CR + } + + // write newline unless end-of-paragraph + // (this is the support for paragraphs with multiple newlines) + bool writeNewLine (const bool endOfParagraph) + { + if (!endOfParagraph) + { + m_charInfoCountLen++; // not incremented by writeTextInternal() + return writeTextInternal ("\n"); + } + else + return true; + } + + // aka "soft hyphen" + bool writeOptionalHyphen (void) + { + m_charInfoCountLen++; // not incremented by writeTextInternal() + return writeTextInternal ("\xC2\xAD"); + } + + void setKOfficeLink (MSWriteImport *kofficeLink) + { + m_koLink = kofficeLink; + } + + void sigProgress (const int value) + { + m_koLink->sigProgress (value); + } +}; + + + +// +// KoFilter +// + +MSWriteImport::MSWriteImport (KoFilter *, const char *, const TQStringList &) + : m_device (NULL), m_parser (NULL), m_generator (NULL) +{ +} + +MSWriteImport::~MSWriteImport () +{ + delete m_generator; + delete m_parser; + delete m_device; +} + +KoFilter::ConversionStatus MSWriteImport::convert (const TQCString &from, const TQCString &to) +{ + kdDebug (30509) << "MSWriteImport $Date: 2006-02-12 19:28:12 +0100 (Sun, 12 Feb 2006) $ using LibMSWrite " + << MSWrite::Version << endl; + + if (to != "application/x-kword" || from != "application/x-mswrite") + { + kdError (30509) << "Internal error! Filter not implemented?" << endl; + return KoFilter::NotImplemented; + } + +#if 0 + //MSWriteImportDialog *dialog = new MSWriteImportDialog (); + MSWriteImportDialog dialog; + + /*debug ("DIALOG check alloc\n"); + if (!dialog) + { + error ("Could not allocate memory for dialog\n"); + return KoFilter::StupidError; + }*/ + + debug ("DIALOG EXEC!!!\n"); + if (!dialog.exec ()) + { + error ("Dialog was aborted! Aborting filter!\n"); + return KoFilter::UserCancelled; + } + + debug ("DIALOG GET!!!\n"); + + // read settings from dialog + m_codec = dialog.getCodec (); + m_simulateLinespacing = dialog.getSimulateLinespacing (); + m_simulateImageOffset = dialog.getSimulateImageOffset (); + debug ("Import options: simulateLinespacing: %i\tsimulateImageOffset: %i\n", + m_simulateLinespacing, m_simulateImageOffset); + + debug ("DIALOG DELETE\n"); + //delete dialog; +#endif + + // create the Device that will read from the .WRI file + m_device = new WRIDevice; + if (!m_device) + { + kdError (30509) << "Could not allocate memory for device" << endl; + return KoFilter::OutOfMemory; + } + + // open the .WRI file + if (!m_device->openFile (TQFile::encodeName (m_chain->inputFile ()))) + { + kdError (30509) << "Could not open \'" << m_chain->inputFile () << "\'" << endl; + return KoFilter::FileNotFound; + } + + + // create Parser that will interpret the .WRI file and call the Generator + m_parser = new MSWrite::InternalParser; + if (!m_parser) + { + kdError (30509) << "Could not allocate memory for parser" << endl; + return KoFilter::OutOfMemory; + } + + // tell the Parser to use the Device to read from the .WRI file + m_parser->setDevice (m_device); + + + // create Generator that will produce the .KWD file + m_generator = new KWordGenerator; + if (!m_generator) + { + kdError (30509) << "Could not allocate memory for generator" << endl; + return KoFilter::OutOfMemory; + } + + // give the Generator the Device for error-handling purposes + m_generator->setDevice (m_device); + + // give the Generator the chain + m_generator->setFilterChain (m_chain); + + // hand over sigProgess to give some feedback to the user + m_generator->setKOfficeLink (this); + + + // hook up Generator to Parser + m_parser->setGenerator (m_generator); + + + // filter! + if (!m_parser->parse ()) + { + // try to return somewhat more meaningful errors than StupidError + // for the day that KOffice actually reports them to the user properly + int errorCode = m_device->bad (); + switch (errorCode) + { + case MSWrite::Error::Ok: + kdDebug (30509) << "Error::Ok but aborted???" << endl; + return KoFilter::InternalError; + + 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::WrongFormat; + + 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::StupidError; // got a better return value? + } + + kdWarning (30509) << "Unknown error: " << errorCode << endl; + return KoFilter::StupidError; + } + + return KoFilter::OK; +} + +#include <mswriteimport.moc> |