summaryrefslogtreecommitdiffstats
path: root/filters/kword/abiword/abiwordimport.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'filters/kword/abiword/abiwordimport.cpp')
-rw-r--r--filters/kword/abiword/abiwordimport.cpp1875
1 files changed, 1875 insertions, 0 deletions
diff --git a/filters/kword/abiword/abiwordimport.cpp b/filters/kword/abiword/abiwordimport.cpp
new file mode 100644
index 000000000..bd6ea1e38
--- /dev/null
+++ b/filters/kword/abiword/abiwordimport.cpp
@@ -0,0 +1,1875 @@
+/* This file is part of the KDE project
+ Copyright 2001, 2002, 2003, 2004 Nicolas GOUTTE <goutte@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#include <config.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <tqmap.h>
+#include <tqbuffer.h>
+#include <tqpicture.h>
+#include <tqxml.h>
+#include <tqdom.h>
+#include <tqdatetime.h>
+
+#include <kdebug.h>
+#include <kmdcodec.h>
+#include <kfilterdev.h>
+#include <kgenericfactory.h>
+#include <tdemessagebox.h>
+
+#include <KoPageLayout.h>
+#include <KoStore.h>
+#include <KoFilterChain.h>
+
+#include "ImportHelpers.h"
+#include "ImportFormatting.h"
+#include "ImportStyle.h"
+#include "ImportField.h"
+
+#include "abiwordimport.h"
+
+class ABIWORDImportFactory : KGenericFactory<ABIWORDImport, KoFilter>
+{
+public:
+ ABIWORDImportFactory(void) : KGenericFactory<ABIWORDImport, KoFilter> ("kwordabiwordimport")
+ {}
+protected:
+ virtual void setupTranslations( void )
+ {
+ TDEGlobal::locale()->insertCatalogue( "kofficefilters" );
+ }
+};
+
+K_EXPORT_COMPONENT_FACTORY( libabiwordimport, ABIWORDImportFactory() )
+
+// *Note for the reader of this code*
+// Tags in lower case (e.g. <c>) are AbiWord's ones.
+// Tags in upper case (e.g. <TEXT>) are KWord's ones.
+
+// enum StackItemElementType is now in the file ImportFormatting.h
+
+class StructureParser : public TQXmlDefaultHandler
+{
+public:
+ StructureParser(KoFilterChain* chain)
+ : m_chain(chain), m_pictureNumber(0), m_pictureFrameNumber(0), m_tableGroupNumber(0),
+ m_timepoint(TQDateTime::currentDateTime(Qt::UTC)), m_fatalerror(false)
+ {
+ createDocument();
+ structureStack.setAutoDelete(true);
+ StackItem *stackItem=new(StackItem);
+ stackItem->elementType=ElementTypeBottom;
+ stackItem->m_frameset=mainFramesetElement; // The default frame set.
+ stackItem->stackElementText=mainFramesetElement; // This is more for DEBUG
+ structureStack.push(stackItem); //Security item (not to empty the stack)
+ }
+ virtual ~StructureParser()
+ {
+ structureStack.clear();
+ }
+public:
+ virtual bool startDocument(void);
+ virtual bool endDocument(void);
+ virtual bool startElement( const TQString&, const TQString&, const TQString& name, const TQXmlAttributes& attributes);
+ virtual bool endElement( const TQString&, const TQString& , const TQString& qName);
+ virtual bool characters ( const TQString & ch );
+ virtual bool warning(const TQXmlParseException& exception);
+ virtual bool error(const TQXmlParseException& exception);
+ virtual bool fatalError(const TQXmlParseException& exception);
+public:
+ inline TQDomDocument getDocInfo(void) const { return m_info; }
+ inline TQDomDocument getDocument(void) const { return mainDocument; }
+ inline bool wasFatalError(void) const { return m_fatalerror; }
+protected:
+ bool clearStackUntilParagraph(StackItemStack& auxilaryStack);
+ bool complexForcedPageBreak(StackItem* stackItem);
+private:
+ // The methods that would need too much parameters are private instead of being static outside the class
+ bool StartElementC(StackItem* stackItem, StackItem* stackCurrent,
+ const TQXmlAttributes& attributes);
+ bool StartElementA(StackItem* stackItem, StackItem* stackCurrent,
+ const TQXmlAttributes& attributes);
+ bool StartElementImage(StackItem* stackItem, StackItem* stackCurrent,
+ const TQXmlAttributes& attributes);
+ bool EndElementD (StackItem* stackItem);
+ bool EndElementM (StackItem* stackItem);
+ bool StartElementSection(StackItem* stackItem, StackItem* stackCurrent,
+ const TQXmlAttributes& attributes);
+ bool StartElementFoot(StackItem* stackItem, StackItem* stackCurrent,
+ const TQXmlAttributes& attributes);
+ bool StartElementTable(StackItem* stackItem, StackItem* stackCurrent, const TQXmlAttributes& attributes);
+ bool StartElementCell(StackItem* stackItem, StackItem* stackCurrent,const TQXmlAttributes& attributes);
+private:
+ void createDocument(void);
+ void createDocInfo(void);
+ TQString indent; //DEBUG
+ StackItemStack structureStack;
+ TQDomDocument mainDocument;
+ TQDomDocument m_info;
+ TQDomElement framesetsPluralElement; // <FRAMESETS>
+ TQDomElement mainFramesetElement; // The main <FRAMESET> where the body text will be under.
+ TQDomElement m_picturesElement; // <PICTURES>
+ TQDomElement m_paperElement; // <PAPER>
+ TQDomElement m_paperBordersElement; // <PAPERBORDER>
+ TQDomElement m_ignoreWordsElement; // <SPELLCHECKIGNORELIST>
+ StyleDataMap styleDataMap;
+ KoFilterChain* m_chain;
+ uint m_pictureNumber; // unique: increment *before* use
+ uint m_pictureFrameNumber; // unique: increment *before* use
+ uint m_tableGroupNumber; // unique: increment *before* use
+ TQMap<TQString,TQString> m_metadataMap; // Map for <m> elements
+ TQDateTime m_timepoint; // Date/time (for pictures)
+ bool m_fatalerror; // Did a XML parsing fatal error happened?
+};
+
+// Element <c>
+
+bool StructureParser::StartElementC(StackItem* stackItem, StackItem* stackCurrent, const TQXmlAttributes& attributes)
+{
+ // <c> elements can be nested in <p> elements, in <a> elements or in other <c> elements
+ // AbiWord does not nest <c> elements in other <c> elements, but explicitly allows external programs to do it!
+
+ // <p> or <c> (not child of <a>)
+ if ((stackCurrent->elementType==ElementTypeParagraph)||(stackCurrent->elementType==ElementTypeContent))
+ {
+ // Contents can have styles, however KWord cannot have character style.
+ // Therefore we use the style if it exist, but we do not create it if not.
+ TQString strStyleProps;
+ TQString strStyleName=attributes.value("style").stripWhiteSpace();
+ if (!strStyleName.isEmpty())
+ {
+ StyleDataMap::Iterator it=styleDataMap.find(strStyleName);
+ if (it!=styleDataMap.end())
+ {
+ strStyleProps=it.data().m_props;
+ }
+ }
+
+ AbiPropsMap abiPropsMap;
+ PopulateProperties(stackItem,strStyleProps,attributes,abiPropsMap,true);
+
+ stackItem->elementType=ElementTypeContent;
+ stackItem->stackElementParagraph=stackCurrent->stackElementParagraph; // <PARAGRAPH>
+ stackItem->stackElementText=stackCurrent->stackElementText; // <TEXT>
+ stackItem->stackElementFormatsPlural=stackCurrent->stackElementFormatsPlural; // <FORMATS>
+ stackItem->pos=stackCurrent->pos; //Propagate the position
+ }
+ // <a> or <c> when child of <a>
+ else if ((stackCurrent->elementType==ElementTypeAnchor)||(stackCurrent->elementType==ElementTypeAnchorContent))
+ {
+ stackItem->elementType=ElementTypeAnchorContent;
+ }
+ else
+ {//we are not nested correctly, so consider it a parse error!
+ kdError(30506) << "parse error <c> tag nested neither in <p> nor in <c> nor in <a> but in "
+ << stackCurrent->itemName << endl;
+ return false;
+ }
+ return true;
+}
+
+bool charactersElementC (StackItem* stackItem, TQDomDocument& mainDocument, const TQString & ch)
+{
+ if (stackItem->elementType==ElementTypeContent)
+ { // Normal <c>
+ TQDomElement elementText=stackItem->stackElementText;
+ TQDomElement elementFormatsPlural=stackItem->stackElementFormatsPlural;
+ elementText.appendChild(mainDocument.createTextNode(ch));
+
+ TQDomElement formatElementOut=mainDocument.createElement("FORMAT");
+ formatElementOut.setAttribute("id",1); // Normal text!
+ formatElementOut.setAttribute("pos",stackItem->pos); // Start position
+ formatElementOut.setAttribute("len",ch.length()); // Start position
+ elementFormatsPlural.appendChild(formatElementOut); //Append to <FORMATS>
+ stackItem->pos+=ch.length(); // Adapt new starting position
+
+ AddFormat(formatElementOut, stackItem, mainDocument);
+ }
+ else if (stackItem->elementType==ElementTypeAnchorContent)
+ {
+ // Add characters to the link name
+ stackItem->strTemp2+=ch;
+ // TODO: how can we care about the text format?
+ }
+ else
+ {
+ kdError(30506) << "Internal error (in charactersElementC)" << endl;
+ }
+
+ return true;
+}
+
+bool EndElementC (StackItem* stackItem, StackItem* stackCurrent)
+{
+ if (stackItem->elementType==ElementTypeContent)
+ {
+ stackItem->stackElementText.normalize();
+ stackCurrent->pos=stackItem->pos; //Propagate the position back to the parent element
+ }
+ else if (stackItem->elementType==ElementTypeAnchorContent)
+ {
+ stackCurrent->strTemp2+=stackItem->strTemp2; //Propagate the link name back to the parent element
+ }
+ else
+ {
+ kdError(30506) << "Wrong element type!! Aborting! (</c> in StructureParser::endElement)" << endl;
+ return false;
+ }
+ return true;
+}
+
+// Element <a>
+bool StructureParser::StartElementA(StackItem* stackItem, StackItem* stackCurrent, const TQXmlAttributes& attributes)
+{
+ // <a> elements can be nested in <p> elements
+ if (stackCurrent->elementType==ElementTypeParagraph)
+ {
+
+ //AbiPropsMap abiPropsMap;
+ //PopulateProperties(stackItem,TQString(),attributes,abiPropsMap,true);
+
+ stackItem->elementType=ElementTypeAnchor;
+ stackItem->stackElementParagraph=stackCurrent->stackElementParagraph; // <PARAGRAPH>
+ stackItem->stackElementText=stackCurrent->stackElementText; // <TEXT>
+ stackItem->stackElementFormatsPlural=stackCurrent->stackElementFormatsPlural; // <FORMATS>
+ stackItem->pos=stackCurrent->pos; //Propagate the position
+ stackItem->strTemp1=attributes.value("xlink:href").stripWhiteSpace(); // link reference
+ stackItem->strTemp2=TQString(); // link name
+
+ // We must be careful: AbiWord permits anchors to bookmarks.
+ // However, KWord does not know what a bookmark is.
+ if (stackItem->strTemp1[0]=='#')
+ {
+ kdWarning(30506) << "Anchor <a> to bookmark: " << stackItem->strTemp1 << endl
+ << " Processing <a> like <c>" << endl;
+ // We have a reference to a bookmark. Therefore treat it as a normal content <c>
+ return StartElementC(stackItem, stackCurrent, attributes);
+ }
+ }
+ else
+ {//we are not nested correctly, so consider it a parse error!
+ kdError(30506) << "parse error <a> tag not a child of <p> but of "
+ << stackCurrent->itemName << endl;
+ return false;
+ }
+ return true;
+}
+
+static bool charactersElementA (StackItem* stackItem, const TQString & ch)
+{
+ // Add characters to the link name
+ stackItem->strTemp2+=ch;
+ return true;
+}
+
+static bool EndElementA (StackItem* stackItem, StackItem* stackCurrent, TQDomDocument& mainDocument)
+{
+ if (!stackItem->elementType==ElementTypeAnchor)
+ {
+ kdError(30506) << "Wrong element type!! Aborting! (</a> in StructureParser::endElement)" << endl;
+ return false;
+ }
+
+ TQDomElement elementText=stackItem->stackElementText;
+ elementText.appendChild(mainDocument.createTextNode("#"));
+
+ TQDomElement formatElement=mainDocument.createElement("FORMAT");
+ formatElement.setAttribute("id",4); // Variable
+ formatElement.setAttribute("pos",stackItem->pos); // Start position
+ formatElement.setAttribute("len",1); // Start position
+
+ TQDomElement variableElement=mainDocument.createElement("VARIABLE");
+ formatElement.appendChild(variableElement);
+
+ TQDomElement typeElement=mainDocument.createElement("TYPE");
+ typeElement.setAttribute("key","STRING");
+ typeElement.setAttribute("type",9); // link
+ typeElement.setAttribute("text",stackItem->strTemp2);
+ variableElement.appendChild(typeElement); //Append to <VARIABLE>
+
+ TQDomElement linkElement=mainDocument.createElement("LINK");
+ linkElement.setAttribute("hrefName",stackItem->strTemp1);
+ linkElement.setAttribute("linkName",stackItem->strTemp2);
+ variableElement.appendChild(linkElement); //Append to <VARIABLE>
+
+ // Now work on stackCurrent
+ stackCurrent->stackElementFormatsPlural.appendChild(formatElement);
+ stackCurrent->pos++; //Propagate the position back to the parent element
+
+ return true;
+}
+
+// Element <p>
+
+bool StartElementP(StackItem* stackItem, StackItem* stackCurrent,
+ TQDomDocument& mainDocument,
+ StyleDataMap& styleDataMap, const TQXmlAttributes& attributes)
+{
+ // We must prepare the style
+ TQString strStyle=attributes.value("style");
+ if (strStyle.isEmpty())
+ {
+ strStyle="Normal";
+ }
+ StyleDataMap::ConstIterator it=styleDataMap.useOrCreateStyle(strStyle);
+
+ TQString strLevel=attributes.value("level");
+ int level;
+ if (strLevel.isEmpty())
+ {
+ // We have not "level" attribute, so we must use the style's level.
+ level=it.data().m_level;
+ }
+ else
+ {
+ // We have a "level" attribute, so it overrides the style's level.
+ level=strStyle.toInt();
+ }
+
+ TQDomElement elementText=stackCurrent->stackElementText;
+ TQDomElement paragraphElementOut=mainDocument.createElement("PARAGRAPH");
+ stackCurrent->m_frameset.appendChild(paragraphElementOut);
+
+ TQDomElement textElementOut=mainDocument.createElement("TEXT");
+ paragraphElementOut.appendChild(textElementOut);
+ TQDomElement formatsPluralElementOut=mainDocument.createElement("FORMATS");
+ paragraphElementOut.appendChild(formatsPluralElementOut);
+
+ AbiPropsMap abiPropsMap;
+ PopulateProperties(stackItem,it.data().m_props,attributes,abiPropsMap,false);
+
+ stackItem->elementType=ElementTypeParagraph;
+ stackItem->stackElementParagraph=paragraphElementOut; // <PARAGRAPH>
+ stackItem->stackElementText=textElementOut; // <TEXT>
+ stackItem->stackElementFormatsPlural=formatsPluralElementOut; // <FORMATS>
+ stackItem->pos=0; // No text characters yet
+
+ // Now we populate the layout
+ TQDomElement layoutElement=mainDocument.createElement("LAYOUT");
+ paragraphElementOut.appendChild(layoutElement);
+
+ AddLayout(strStyle,layoutElement, stackItem, mainDocument, abiPropsMap, level, false);
+
+ return true;
+}
+
+bool charactersElementP (StackItem* stackItem, TQDomDocument& mainDocument, const TQString & ch)
+{
+ TQDomElement elementText=stackItem->stackElementText;
+
+ elementText.appendChild(mainDocument.createTextNode(ch));
+
+ stackItem->pos+=ch.length(); // Adapt new starting position
+
+ return true;
+}
+
+bool EndElementP (StackItem* stackItem)
+{
+ if (!stackItem->elementType==ElementTypeParagraph)
+ {
+ kdError(30506) << "Wrong element type!! Aborting! (in endElementP)" << endl;
+ return false;
+ }
+ stackItem->stackElementText.normalize();
+ return true;
+}
+
+static bool StartElementField(StackItem* stackItem, StackItem* stackCurrent,
+ TQDomDocument& mainDocument, const TQXmlAttributes& attributes)
+{
+ // <field> element elements can be nested in <p>
+ if (stackCurrent->elementType==ElementTypeParagraph)
+ {
+ TQString strType=attributes.value("type").stripWhiteSpace();
+ kdDebug(30506)<<"<field> type:"<<strType<<endl;
+
+ AbiPropsMap abiPropsMap;
+ PopulateProperties(stackItem,TQString(),attributes,abiPropsMap,true);
+
+ stackItem->elementType=ElementTypeEmpty;
+
+ // We create a format element
+ TQDomElement variableElement=mainDocument.createElement("VARIABLE");
+
+ if (!ProcessField(mainDocument, variableElement, strType, attributes))
+ {
+ // The field type was not recognised,
+ // therefore write the field type in red as normal text
+ kdWarning(30506) << "Unknown <field> type: " << strType << endl;
+ TQDomElement formatElement=mainDocument.createElement("FORMAT");
+ formatElement.setAttribute("id",1); // Variable
+ formatElement.setAttribute("pos",stackItem->pos); // Start position
+ formatElement.setAttribute("len",strType.length());
+
+ formatElement.appendChild(variableElement);
+
+ // Now work on stackCurrent
+ stackCurrent->stackElementFormatsPlural.appendChild(formatElement);
+ stackCurrent->stackElementText.appendChild(mainDocument.createTextNode(strType));
+ stackCurrent->pos+=strType.length(); // Adjust position
+
+ // Add formating (use stackItem)
+ stackItem->fgColor.setRgb(255,0,0);
+ AddFormat(formatElement, stackItem, mainDocument);
+ return true;
+ }
+
+ // We create a format element
+ TQDomElement formatElement=mainDocument.createElement("FORMAT");
+ formatElement.setAttribute("id",4); // Variable
+ formatElement.setAttribute("pos",stackItem->pos); // Start position
+ formatElement.setAttribute("len",1);
+
+ formatElement.appendChild(variableElement);
+
+ // Now work on stackCurrent
+ stackCurrent->stackElementFormatsPlural.appendChild(formatElement);
+ stackCurrent->stackElementText.appendChild(mainDocument.createTextNode("#"));
+ stackCurrent->pos++; // Adjust position
+
+ // Add formating (use stackItem)
+ AddFormat(formatElement, stackItem, mainDocument);
+
+ }
+ else
+ {//we are not nested correctly, so consider it a parse error!
+ kdError(30506) << "parse error <field> tag not nested in <p> but in "
+ << stackCurrent->itemName << endl;
+ return false;
+ }
+ return true;
+}
+
+// <s> (style)
+static bool StartElementS(StackItem* stackItem, StackItem* /*stackCurrent*/,
+ const TQXmlAttributes& attributes, StyleDataMap& styleDataMap)
+{
+ // We do not assume when we are called.
+ // We also do not care if a style is defined multiple times.
+ stackItem->elementType=ElementTypeEmpty;
+
+ TQString strStyleName=attributes.value("name").stripWhiteSpace();
+
+ if (strStyleName.isEmpty())
+ {
+ kdWarning(30506) << "Style has no name!" << endl;
+ }
+ else
+ {
+ TQString strLevel=attributes.value("level");
+ int level;
+ if (strLevel.isEmpty())
+ level=-1; //TODO/FIXME: might be wrong if the style is based on another
+ else
+ level=strLevel.toInt();
+ TQString strBasedOn=attributes.value("basedon").simplifyWhiteSpace();
+ styleDataMap.defineNewStyleFromOld(strStyleName,strBasedOn,level,attributes.value("props"));
+ kdDebug(30506) << " Style name: " << strStyleName << endl
+ << " Based on: " << strBasedOn << endl
+ << " Level: " << level << endl
+ << " Props: " << attributes.value("props") << endl;
+ }
+
+ return true;
+}
+
+// <image>
+bool StructureParser::StartElementImage(StackItem* stackItem, StackItem* stackCurrent,
+ const TQXmlAttributes& attributes)
+{
+ // <image> elements can be nested in <p> or <c> elements
+ if ((stackCurrent->elementType!=ElementTypeParagraph) && (stackCurrent->elementType!=ElementTypeContent))
+ {//we are not nested correctly, so consider it a parse error!
+ kdError(30506) << "parse error <image> tag nested neither in <p> nor in <c> but in "
+ << stackCurrent->itemName << endl;
+ return false;
+ }
+ stackItem->elementType=ElementTypeEmpty;
+
+ TQString strDataId=attributes.value("dataid").stripWhiteSpace();
+
+ AbiPropsMap abiPropsMap;
+ abiPropsMap.splitAndAddAbiProps(attributes.value("props"));
+
+ double height=ValueWithLengthUnit(abiPropsMap["height"].getValue());
+ double width =ValueWithLengthUnit(abiPropsMap["width" ].getValue());
+
+ kdDebug(30506) << "Image: " << strDataId << " height: " << height << " width: " << width << endl;
+
+ // TODO: image properties
+
+ if (strDataId.isEmpty())
+ {
+ kdWarning(30506) << "Image has no data id!" << endl;
+ }
+ else
+ {
+ kdDebug(30506) << "Image: " << strDataId << endl;
+ }
+
+ TQString strPictureFrameName(i18n("Frameset name","Picture %1").arg(++m_pictureFrameNumber));
+
+ // Create the frame set of the image
+
+ TQDomElement framesetElement=mainDocument.createElement("FRAMESET");
+ framesetElement.setAttribute("frameType",2);
+ framesetElement.setAttribute("frameInfo",0);
+ framesetElement.setAttribute("visible",1);
+ framesetElement.setAttribute("name",strPictureFrameName);
+ framesetsPluralElement.appendChild(framesetElement);
+
+ TQDomElement frameElementOut=mainDocument.createElement("FRAME");
+ frameElementOut.setAttribute("left",0);
+ frameElementOut.setAttribute("top",0);
+ frameElementOut.setAttribute("bottom",height);
+ frameElementOut.setAttribute("right" ,width );
+ frameElementOut.setAttribute("runaround",1);
+ // TODO: a few attributes are missing
+ framesetElement.appendChild(frameElementOut);
+
+ TQDomElement element=mainDocument.createElement("PICTURE");
+ element.setAttribute("keepAspectRatio","true");
+ framesetElement.setAttribute("frameType",2); // Picture
+ framesetElement.appendChild(element);
+
+ TQDomElement key=mainDocument.createElement("KEY");
+ key.setAttribute("filename",strDataId);
+ key.setAttribute("year",m_timepoint.date().year());
+ key.setAttribute("month",m_timepoint.date().month());
+ key.setAttribute("day",m_timepoint.date().day());
+ key.setAttribute("hour",m_timepoint.time().hour());
+ key.setAttribute("minute",m_timepoint.time().minute());
+ key.setAttribute("second",m_timepoint.time().second());
+ key.setAttribute("msec",m_timepoint.time().msec());
+ element.appendChild(key);
+
+ // Now use the image's frame set
+ TQDomElement elementText=stackItem->stackElementText;
+ TQDomElement elementFormatsPlural=stackItem->stackElementFormatsPlural;
+ elementText.appendChild(mainDocument.createTextNode("#"));
+
+ TQDomElement formatElementOut=mainDocument.createElement("FORMAT");
+ formatElementOut.setAttribute("id",6); // Normal text!
+ formatElementOut.setAttribute("pos",stackItem->pos); // Start position
+ formatElementOut.setAttribute("len",1); // Start position
+ elementFormatsPlural.appendChild(formatElementOut); //Append to <FORMATS>
+
+ // WARNING: we must change the position in stackCurrent!
+ stackCurrent->pos++; // Adapt new starting position
+
+ TQDomElement anchor=mainDocument.createElement("ANCHOR");
+ // No name attribute!
+ anchor.setAttribute("type","frameset");
+ anchor.setAttribute("instance",strPictureFrameName);
+ formatElementOut.appendChild(anchor);
+
+ return true;
+}
+
+// <d>
+static bool StartElementD(StackItem* stackItem, StackItem* /*stackCurrent*/,
+ const TQXmlAttributes& attributes)
+{
+ // We do not assume when we are called or if we are or not a child of <data>
+ // However, we assume that we are after all <image> elements
+ stackItem->elementType=ElementTypeRealData;
+
+ TQString strName=attributes.value("name").stripWhiteSpace();
+ kdDebug(30506) << "Data: " << strName << endl;
+
+ TQString strBase64=attributes.value("base64").stripWhiteSpace();
+ TQString strMime=attributes.value("mime").stripWhiteSpace();
+
+ if (strName.isEmpty())
+ {
+ kdWarning(30506) << "Data has no name!" << endl;
+ stackItem->elementType=ElementTypeEmpty;
+ return true;
+ }
+
+ if (strMime.isEmpty())
+ {
+ // Old AbiWord files had no mime types for images but the data were base64-coded PNG
+ strMime="image/png";
+ strBase64="yes";
+ }
+
+ stackItem->fontName=strName; // Store the data name as font name.
+ stackItem->bold=(strBase64=="yes"); // Store base64-coded as bold
+ stackItem->strTemp1=strMime; // Mime type
+ stackItem->strTemp2=TQString(); // Image data
+
+ return true;
+}
+
+static bool CharactersElementD (StackItem* stackItem, TQDomDocument& /*mainDocument*/, const TQString & ch)
+{
+ // As we have no guarantee to have the whole stream in one call, we must store the data.
+ stackItem->strTemp2+=ch;
+ return true;
+}
+
+bool StructureParser::EndElementD (StackItem* stackItem)
+{
+ if (!stackItem->elementType==ElementTypeRealData)
+ {
+ kdError(30506) << "Wrong element type!! Aborting! (in endElementD)" << endl;
+ return false;
+ }
+ if (!m_chain)
+ {
+ kdError(30506) << "No filter chain! Aborting! (in endElementD)" << endl;
+ return false;
+ }
+
+ bool isSvg=false; // SVG ?
+
+ TQString extension;
+
+ // stackItem->strTemp1 contains the mime type
+ if (stackItem->strTemp1=="image/png")
+ {
+ extension=".png";
+ }
+ else if (stackItem->strTemp1=="image/jpeg") // ### FIXME: in fact it does not exist in AbiWord
+ {
+ extension=".jpeg";
+ }
+ else if (stackItem->strTemp1=="image/svg-xml") //Yes it is - not +
+ {
+ extension=".svg";
+ isSvg=true;
+ }
+ else
+ {
+ kdWarning(30506) << "Unknown or unsupported mime type: "
+ << stackItem->strTemp1 << endl;
+ return true;
+ }
+
+ TQString strStoreName;
+ strStoreName="pictures/picture";
+ strStoreName+=TQString::number(++m_pictureNumber);
+ strStoreName+=extension;
+
+ TQString strDataId=stackItem->fontName; // AbiWord's data id
+ TQDomElement key=mainDocument.createElement("KEY");
+ key.setAttribute("filename",strDataId);
+ key.setAttribute("year",m_timepoint.date().year());
+ key.setAttribute("month",m_timepoint.date().month());
+ key.setAttribute("day",m_timepoint.date().day());
+ key.setAttribute("hour",m_timepoint.time().hour());
+ key.setAttribute("minute",m_timepoint.time().minute());
+ key.setAttribute("second",m_timepoint.time().second());
+ key.setAttribute("msec",m_timepoint.time().msec());
+ key.setAttribute("name",strStoreName);
+ m_picturesElement.appendChild(key);
+
+ KoStoreDevice* out=m_chain->storageFile(strStoreName, KoStore::Write);
+ if(!out)
+ {
+ kdError(30506) << "Unable to open output file for: " << stackItem->fontName << " Storage: " << strStoreName << endl;
+ return false;
+ }
+
+ if (stackItem->bold) // Is base64-coded?
+ {
+ kdDebug(30506) << "Decode and write base64 stream: " << stackItem->fontName << endl;
+ // We need to decode the base64 stream
+ // However KCodecs has no TQString to TQByteArray decoder!
+ TQByteArray base64Stream=stackItem->strTemp2.utf8(); // Use utf8 to avoid corruption of data
+ TQByteArray binaryStream;
+ KCodecs::base64Decode(base64Stream, binaryStream);
+ out->writeBlock(binaryStream, binaryStream.count());
+ }
+ else
+ {
+ // Unknown text format!
+ kdDebug(30506) << "Write character stream: " << stackItem->fontName << endl;
+ // We strip the white space in front to avoid white space before a XML declaration
+ TQCString strOut=stackItem->strTemp2.stripWhiteSpace().utf8();
+ out->writeBlock(strOut,strOut.length());
+ }
+
+ return true;
+}
+
+// <m>
+static bool StartElementM(StackItem* stackItem, StackItem* /*stackCurrent*/,
+ const TQXmlAttributes& attributes)
+{
+ // We do not assume when we are called or if we are or not a child of <metadata>
+ stackItem->elementType=ElementTypeRealMetaData;
+
+ TQString strKey=attributes.value("key").stripWhiteSpace();
+ kdDebug(30506) << "Metadata key: " << strKey << endl;
+
+ if (strKey.isEmpty())
+ {
+ kdWarning(30506) << "Metadata has no key!" << endl;
+ stackItem->elementType=ElementTypeIgnore;
+ return true;
+ }
+
+ stackItem->strTemp1=strKey; // Key
+ stackItem->strTemp2=TQString(); // Meta data
+
+ return true;
+}
+
+static bool CharactersElementM (StackItem* stackItem, const TQString & ch)
+{
+ // As we have no guarantee to have the whole data in one call, we must store the data.
+ stackItem->strTemp2+=ch;
+ return true;
+}
+
+bool StructureParser::EndElementM (StackItem* stackItem)
+{
+ if (!stackItem->elementType==ElementTypeRealData)
+ {
+ kdError(30506) << "Wrong element type!! Aborting! (in endElementM)" << endl;
+ return false;
+ }
+
+ if (stackItem->strTemp1.isEmpty())
+ {
+ // Probably an internal error!
+ kdError(30506) << "Key name was erased! Aborting! (in endElementM)" << endl;
+ return false;
+ }
+
+ // Just add it to the metadata map, we do not do something special with the values.
+ m_metadataMap[stackItem->strTemp1]=stackItem->strTemp2;
+
+ return true;
+}
+
+// <br> (forced line break)
+static bool StartElementBR(StackItem* stackItem, StackItem* stackCurrent,
+ TQDomDocument& mainDocument)
+{
+ // <br> elements are mostly in <c> but can also be in <p>
+ if ((stackCurrent->elementType==ElementTypeParagraph)
+ || (stackCurrent->elementType==ElementTypeContent))
+ {
+ stackItem->elementType=ElementTypeEmpty;
+
+ // Now work on stackCurrent
+
+ if (stackCurrent->elementType==ElementTypeContent)
+ {
+ // Child <c>, so we have to add formating of <c>
+ TQDomElement formatElement=mainDocument.createElement("FORMAT");
+ formatElement.setAttribute("id",1); // Normal text!
+ formatElement.setAttribute("pos",stackCurrent->pos); // Start position
+ formatElement.setAttribute("len",1);
+ AddFormat(formatElement, stackCurrent, mainDocument); // Add the format of the parent <c>
+ stackCurrent->stackElementFormatsPlural.appendChild(formatElement); //Append to <FORMATS>
+ }
+
+ stackCurrent->stackElementText.appendChild(mainDocument.createTextNode(TQChar(10))); // Add a LINE FEED
+ stackCurrent->pos++; // Adjust position
+
+ }
+ else
+ {//we are not nested correctly, so consider it a parse error!
+ kdError(30506) << "parse error <br> tag not nested in <p> or <c> but in "
+ << stackCurrent->itemName << endl;
+ return false;
+ }
+ return true;
+}
+
+// <cbr> (forced column break, not supported)
+// <pbr> (forced page break)
+static bool StartElementPBR(StackItem* /*stackItem*/, StackItem* stackCurrent,
+ TQDomDocument& mainDocument)
+{
+ // We are sure to be the child of a <p> element
+
+ // The following code is similar to the one in StartElementP
+ // We use mainFramesetElement here not to be dependant that <section> has happened before
+ TQDomElement paragraphElementOut=mainDocument.createElement("PARAGRAPH");
+ stackCurrent->m_frameset.appendChild(paragraphElementOut);
+ TQDomElement textElementOut=mainDocument.createElement("TEXT");
+ paragraphElementOut.appendChild(textElementOut);
+ TQDomElement formatsPluralElementOut=mainDocument.createElement("FORMATS");
+ paragraphElementOut.appendChild(formatsPluralElementOut);
+
+ // We must now copy/clone the layout of elementText.
+
+ TQDomNodeList nodeList=stackCurrent->stackElementParagraph.elementsByTagName("LAYOUT");
+
+ if (!nodeList.count())
+ {
+ kdError(30506) << "Unable to find <LAYOUT> element! Aborting! (in StartElementPBR)" <<endl;
+ return false;
+ }
+
+ // Now clone it
+ TQDomNode newNode=nodeList.item(0).cloneNode(true); // We make a deep cloning of the first element/node
+ if (newNode.isNull())
+ {
+ kdError(30506) << "Unable to clone <LAYOUT> element! Aborting! (in StartElementPBR)" <<endl;
+ return false;
+ }
+ paragraphElementOut.appendChild(newNode);
+
+ // We need a page break!
+ TQDomElement oldLayoutElement=nodeList.item(0).toElement();
+ if (oldLayoutElement.isNull())
+ {
+ kdError(30506) << "Cannot find old <LAYOUT> element! Aborting! (in StartElementPBR)" <<endl;
+ return false;
+ }
+ // We have now to add a element <PAGEBREAKING>
+ // TODO/FIXME: what if there is already one?
+ TQDomElement pagebreakingElement=mainDocument.createElement("PAGEBREAKING");
+ pagebreakingElement.setAttribute("linesTogether","false");
+ pagebreakingElement.setAttribute("hardFrameBreak","false");
+ pagebreakingElement.setAttribute("hardFrameBreakAfter","true");
+ oldLayoutElement.appendChild(pagebreakingElement);
+
+ // Now that we have done with the old paragraph,
+ // we can write stackCurrent with the data of the new one!
+ // NOTE: The following code is similar to the one in StartElementP but we are working on stackCurrent!
+ stackCurrent->elementType=ElementTypeParagraph;
+ stackCurrent->stackElementParagraph=paragraphElementOut; // <PARAGRAPH>
+ stackCurrent->stackElementText=textElementOut; // <TEXT>
+ stackCurrent->stackElementFormatsPlural=formatsPluralElementOut; // <FORMATS>
+ stackCurrent->pos=0; // No text character yet
+
+ return true;
+}
+
+// <pagesize>
+static bool StartElementPageSize(TQDomElement& paperElement, const TQXmlAttributes& attributes)
+{
+ if (attributes.value("page-scale").toDouble()!=1.0)
+ {
+ kdWarning(30506) << "Ignoring unsupported page scale: " << attributes.value("page-scale") << endl;
+ }
+
+ int kwordOrientation;
+ TQString strOrientation=attributes.value("orientation").stripWhiteSpace();
+
+ if (strOrientation=="portrait")
+ {
+ kwordOrientation=0;
+ }
+ else if (strOrientation=="landscape")
+ {
+ kwordOrientation=1;
+ }
+ else
+ {
+ kdWarning(30506) << "Unknown page orientation: " << strOrientation << "! Ignoring! " << endl;
+ kwordOrientation=0;
+ }
+
+ double kwordHeight;
+ double kwordWidth;
+
+ TQString strPageType=attributes.value("pagetype").stripWhiteSpace();
+
+ // Do we know the page size or do we need to measure?
+ // For page formats that KWord knows, use our own values in case the values in the file would be wrong.
+
+ KoFormat kwordFormat = KoPageFormat::formatFromString(strPageType);
+
+ if (kwordFormat==PG_CUSTOM)
+ {
+ kdDebug(30506) << "Custom or other page format found: " << strPageType << endl;
+
+ double height = attributes.value("height").toDouble();
+ double width = attributes.value("width" ).toDouble();
+
+ TQString strUnits = attributes.value("units").stripWhiteSpace();
+
+ kdDebug(30506) << "Explicit page size: "
+ << height << " " << strUnits << " x " << width << " " << strUnits
+ << endl;
+
+ if (strUnits=="cm")
+ {
+ kwordHeight = CentimetresToPoints(height);
+ kwordWidth = CentimetresToPoints(width);
+ }
+ else if (strUnits=="inch")
+ {
+ kwordHeight = InchesToPoints(height);
+ kwordWidth = InchesToPoints(width);
+ }
+ else if (strUnits=="mm")
+ {
+ kwordHeight = MillimetresToPoints(height);
+ kwordWidth = MillimetresToPoints(width);
+ }
+ else
+ {
+ kwordHeight = 0.0;
+ kwordWidth = 0.0;
+ kdWarning(30506) << "Unknown unit type: " << strUnits << endl;
+ }
+ }
+ else
+ {
+ // We have a format known by KOffice, so use KOffice's functions
+ kwordHeight = MillimetresToPoints(KoPageFormat::height(kwordFormat,PG_PORTRAIT));
+ kwordWidth = MillimetresToPoints(KoPageFormat::width (kwordFormat,PG_PORTRAIT));
+ }
+
+ if ((kwordHeight <= 1.0) || (kwordWidth <= 1.0))
+ // At least one of the two values is ridiculous
+ {
+ kdWarning(30506) << "Page width or height is too small: "
+ << kwordHeight << "x" << kwordWidth << endl;
+ // As we have no correct page size, we assume we have A4
+ kwordFormat = PG_DIN_A4;
+ kwordHeight = CentimetresToPoints(29.7);
+ kwordWidth = CentimetresToPoints(21.0);
+ }
+
+ // Now that we have gathered all the page size data, put it in the right element!
+
+ if (paperElement.isNull())
+ {
+ kdError(30506) << "<PAPER> element cannot be accessed! Aborting!" << endl;
+ return false;
+ }
+
+ paperElement.setAttribute("format",kwordFormat);
+ paperElement.setAttribute("width",kwordWidth);
+ paperElement.setAttribute("height",kwordHeight);
+ paperElement.setAttribute("orientation",kwordOrientation);
+
+ return true;
+}
+
+
+bool StructureParser::complexForcedPageBreak(StackItem* stackItem)
+{
+ // We are not a child of a <p> element, so we cannot use StartElementPBR directly
+
+ StackItemStack auxilaryStack;
+
+ if (!clearStackUntilParagraph(auxilaryStack))
+ {
+ kdError(30506) << "Could not clear stack until a paragraph!" << endl;
+ return false;
+ }
+
+ // Now we are a child of a <p> element!
+
+ bool success=StartElementPBR(stackItem,structureStack.current(),mainDocument);
+
+ // Now restore the stack
+
+ StackItem* stackCurrent=structureStack.current();
+ StackItem* item;
+ while (auxilaryStack.count()>0)
+ {
+ item=auxilaryStack.pop();
+ // We cannot put back the item on the stack like that.
+ // We must set a few values for each item.
+ item->pos=0; // Start at position 0
+ item->stackElementParagraph=stackCurrent->stackElementParagraph; // new <PARAGRAPH>
+ item->stackElementText=stackCurrent->stackElementText; // new <TEXT>
+ item->stackElementFormatsPlural=stackCurrent->stackElementFormatsPlural; // new <FORMATS>
+ structureStack.push(item);
+ }
+
+ return success;
+}
+
+
+// <section>
+bool StructureParser::StartElementSection(StackItem* stackItem, StackItem* /*stackCurrent*/,
+ const TQXmlAttributes& attributes)
+{
+ //TODO: non main text sections (e.g. footers)
+ stackItem->elementType=ElementTypeSection;
+
+ AbiPropsMap abiPropsMap;
+ // Treat the props attributes in the two available flavors: lower case and upper case.
+ kdDebug(30506)<< "========== props=\"" << attributes.value("props") << "\"" << endl;
+ abiPropsMap.splitAndAddAbiProps(attributes.value("props"));
+ abiPropsMap.splitAndAddAbiProps(attributes.value("PROPS")); // PROPS is deprecated
+
+ // TODO: only the first main text section should change the page margins
+ // TODO; (as KWord does not allow different page sizes/margins for the same document)
+ if (true && (!m_paperBordersElement.isNull()))
+ {
+ TQString str;
+ str=abiPropsMap["page-margin-top"].getValue();
+ if (!str.isEmpty())
+ {
+ m_paperBordersElement.setAttribute("top",ValueWithLengthUnit(str));
+ }
+ str=abiPropsMap["page-margin-left"].getValue();
+ if (!str.isEmpty())
+ {
+ m_paperBordersElement.setAttribute("left",ValueWithLengthUnit(str));
+ }
+ str=abiPropsMap["page-margin-bottom"].getValue();
+ if (!str.isEmpty())
+ {
+ m_paperBordersElement.setAttribute("bottom",ValueWithLengthUnit(str));
+ }
+ str=abiPropsMap["page-margin-right"].getValue();
+ if (!str.isEmpty())
+ {
+ m_paperBordersElement.setAttribute("right",ValueWithLengthUnit(str));
+ }
+ }
+ return true;
+}
+
+// <iw>
+
+static bool EndElementIW(StackItem* stackItem, StackItem* /*stackCurrent*/,
+ TQDomDocument& mainDocument, TQDomElement& m_ignoreWordsElement)
+{
+ if (!stackItem->elementType==ElementTypeIgnoreWord)
+ {
+ kdError(30506) << "Wrong element type!! Aborting! (in endElementIW)" << endl;
+ return false;
+ }
+ TQDomElement wordElement=mainDocument.createElement("SPELLCHECKIGNOREWORD");
+ wordElement.setAttribute("word",stackItem->strTemp2.stripWhiteSpace());
+ m_ignoreWordsElement.appendChild(wordElement);
+ return true;
+}
+
+// <foot>
+bool StructureParser::StartElementFoot(StackItem* stackItem, StackItem* /*stackCurrent*/,
+ const TQXmlAttributes& /*attributes*/)
+{
+#if 0
+ stackItem->elementType=ElementTypeFoot;
+
+ const TQString id(attributes.value("endnote-id").stripWhiteSpace());
+ kdDebug(30506) << "Foot note id: " << id << endl;
+
+ if (id.isEmpty())
+ {
+ kdWarning(30506) << "Footnote has no id!" << endl;
+ stackItem->elementType=ElementTypeIgnore;
+ return true;
+ }
+
+ // We need to create a frameset for the foot note.
+ TQDomElement framesetElement(mainDocument.createElement("FRAMESET"));
+ framesetElement.setAttribute("frameType",1);
+ framesetElement.setAttribute("frameInfo",7);
+ framesetElement.setAttribute("visible",1);
+ framesetElement.setAttribute("name",getFootnoteFramesetName(id));
+ framesetsPluralElement.appendChild(framesetElement);
+
+ TQDomElement frameElementOut(mainDocument.createElement("FRAME"));
+ //frameElementOut.setAttribute("left",28);
+ //frameElementOut.setAttribute("top",42);
+ //frameElementOut.setAttribute("bottom",566);
+ //frameElementOut.setAttribute("right",798);
+ frameElementOut.setAttribute("runaround",1);
+ // ### TODO: a few attributes are missing
+ framesetElement.appendChild(frameElementOut);
+
+ stackItem->m_frameset=framesetElement;
+#else
+ stackItem->elementType=ElementTypeIgnore;
+#endif
+ return true;
+}
+
+// Element <table>
+bool StructureParser::StartElementTable(StackItem* stackItem, StackItem* stackCurrent,
+ const TQXmlAttributes& attributes)
+{
+#if 1
+ // In KWord, inline tables are inside a paragraph.
+ // In AbiWord, tables are outside any paragraph.
+
+ TQStringList widthList;
+ widthList.split('/', attributes.value("table-column-props"), false);
+ const uint columns = widthList.size();
+ stackItem->m_doubleArray.detach(); // Be sure not to modify parents
+ stackItem->m_doubleArray.resize(columns+1); // All left positions but the last right one
+ stackItem->m_doubleArray[0] = 0.0;
+ TQStringList::ConstIterator it;
+ uint i;
+ for ( i=0, it=widthList.begin(); i<columns; ++i, ++it )
+ {
+ kdDebug(30506) << "Column width: " << (*it) << " cooked " << ValueWithLengthUnit(*it) << endl;
+ stackItem->m_doubleArray[i+1] = ValueWithLengthUnit(*it) + stackItem->m_doubleArray[i];
+ }
+ // ### TODO: in case of automatic column widths, we have not any width given by AbiWord
+
+ const uint tableNumber(++m_tableGroupNumber);
+ const TQString tableName(i18n("Table %1").arg(tableNumber));
+
+ TQDomElement elementText=stackCurrent->stackElementText;
+ TQDomElement paragraphElementOut=mainDocument.createElement("PARAGRAPH");
+ stackCurrent->m_frameset.appendChild(paragraphElementOut);
+
+ TQDomElement textElementOut(mainDocument.createElement("TEXT"));
+ textElementOut.appendChild(mainDocument.createTextNode("#"));
+ paragraphElementOut.appendChild(textElementOut);
+
+ TQDomElement formatsPluralElementOut=mainDocument.createElement("FORMATS");
+ paragraphElementOut.appendChild(formatsPluralElementOut);
+
+ TQDomElement elementFormat(mainDocument.createElement("FORMAT"));
+ elementFormat.setAttribute("id",6);
+ elementFormat.setAttribute("pos",0);
+ elementFormat.setAttribute("len",1);
+ formatsPluralElementOut.appendChild(elementFormat);
+
+ TQDomElement elementAnchor(mainDocument.createElement("ANCHOR"));
+ elementAnchor.setAttribute("type","frameset");
+ elementAnchor.setAttribute("instance",tableName);
+ elementFormat.appendChild(elementAnchor);
+
+ stackItem->elementType=ElementTypeTable;
+ stackItem->stackElementParagraph=paragraphElementOut; // <PARAGRAPH>
+ stackItem->stackElementText=textElementOut; // <TEXT>
+ stackItem->stackElementFormatsPlural=formatsPluralElementOut; // <FORMATS>
+ stackItem->strTemp1=tableName;
+ stackItem->strTemp2=TQString::number(tableNumber); // needed as I18N does not allow adding phrases
+ stackItem->pos=1; // Just #
+
+ // Now we populate the layout
+ TQDomElement layoutElement=mainDocument.createElement("LAYOUT");
+ paragraphElementOut.appendChild(layoutElement);
+
+ AbiPropsMap abiPropsMap;
+ styleDataMap.useOrCreateStyle("Normal"); // We might have to create the "Normal" style.
+ AddLayout("Normal", layoutElement, stackItem, mainDocument, abiPropsMap, 0, false);
+#else
+ stackItem->elementType=ElementTypeIgnore;
+#endif
+ return true;
+}
+
+// <cell>
+bool StructureParser::StartElementCell(StackItem* stackItem, StackItem* stackCurrent,
+ const TQXmlAttributes& attributes)
+{
+#if 1
+ if (stackCurrent->elementType!=ElementTypeTable)
+ {
+ kdError(30506) << "Wrong element type!! Aborting! (in StructureParser::endElementCell)" << endl;
+ return false;
+ }
+
+ stackItem->elementType=ElementTypeCell;
+
+ const TQString tableName(stackCurrent->strTemp1);
+ kdDebug(30506) << "Table name: " << tableName << endl;
+
+ if (tableName.isEmpty())
+ {
+ kdError(30506) << "Table name is empty! Aborting!" << endl;
+ return false;
+ }
+
+ AbiPropsMap abiPropsMap;
+ abiPropsMap.splitAndAddAbiProps(attributes.value("props")); // Do not check PROPS
+
+ // We abuse the attach number to know the row and col numbers.
+ const uint row=abiPropsMap["top-attach"].getValue().toUInt();
+ const uint col=abiPropsMap["left-attach"].getValue().toUInt();
+
+ if ( col >= stackItem->m_doubleArray.size() )
+ {
+ // We do not know the right position of this column, so improvise. (### TODO)
+ // We play on the fact that TQByteArray uses shallow copies by default.
+ // (We do want that the change is known at <table> level)
+ stackItem->m_doubleArray.resize( stackItem->m_doubleArray.size() + 1, TQGArray::SpeedOptim );
+ stackItem->m_doubleArray[col+1] = stackItem->m_doubleArray[col] + 72; // Try 1 inch
+ }
+
+ const TQString frameName(i18n("Frameset name","Table %3, row %1, column %2")
+ .arg(row).arg(col).arg(stackCurrent->strTemp2)); // As the stack could be wrong, be careful and use the string as last!
+
+ // We need to create a frameset for the cell
+ TQDomElement framesetElement(mainDocument.createElement("FRAMESET"));
+ framesetElement.setAttribute("frameType",1);
+ framesetElement.setAttribute("frameInfo",0);
+ framesetElement.setAttribute("visible",1);
+ framesetElement.setAttribute("name",frameName);
+ framesetElement.setAttribute("row",row);
+ framesetElement.setAttribute("col",col);
+ framesetElement.setAttribute("rows",1); // ### TODO: rowspan
+ framesetElement.setAttribute("cols",1); // ### TODO: colspan
+ framesetElement.setAttribute("grpMgr",tableName);
+ framesetsPluralElement.appendChild(framesetElement);
+
+ TQDomElement frameElementOut(mainDocument.createElement("FRAME"));
+ frameElementOut.setAttribute( "left", stackItem->m_doubleArray[col] );
+ frameElementOut.setAttribute( "right", stackItem->m_doubleArray[col+1] );
+ frameElementOut.setAttribute("top",0);
+ frameElementOut.setAttribute("bottom",0);
+ frameElementOut.setAttribute("runaround",1);
+ frameElementOut.setAttribute("autoCreateNewFrame",0); // Very important for cell growing!
+ // ### TODO: a few attributes are missing
+ framesetElement.appendChild(frameElementOut);
+
+ stackItem->m_frameset=framesetElement;
+ TQDomElement nullDummy;
+ stackItem->stackElementParagraph=nullDummy; // <PARAGRAPH>
+ stackItem->stackElementText=nullDummy; // <TEXT>
+ stackItem->stackElementFormatsPlural=nullDummy; // <FORMATS>
+
+#else
+ stackItem->elementType=ElementTypeIgnore;
+#endif
+ return true;
+}
+
+// Parser for SAX2
+
+bool StructureParser :: startElement( const TQString&, const TQString&, const TQString& name, const TQXmlAttributes& attributes)
+{
+ //Warning: be careful that some element names can be lower case or upper case (not very XML)
+ kdDebug(30506) << indent << " <" << name << ">" << endl; //DEBUG
+ indent += "*"; //DEBUG
+
+ if (structureStack.isEmpty())
+ {
+ kdError(30506) << "Stack is empty!! Aborting! (in StructureParser::startElement)" << endl;
+ return false;
+ }
+
+ // Create a new stack element copying the top of the stack.
+ StackItem *stackItem=new StackItem(*structureStack.current());
+
+ if (!stackItem)
+ {
+ kdError(30506) << "Could not create Stack Item! Aborting! (in StructureParser::startElement)" << endl;
+ return false;
+ }
+
+ stackItem->itemName=name;
+
+ bool success=false;
+
+ if ((name=="c")||(name=="C"))
+ {
+ success=StartElementC(stackItem,structureStack.current(),attributes);
+ }
+ else if ((name=="p")||(name=="P"))
+ {
+ success=StartElementP(stackItem,structureStack.current(),mainDocument,
+ styleDataMap,attributes);
+ }
+ else if ((name=="section")||(name=="SECTION"))
+ {
+ success=StartElementSection(stackItem,structureStack.current(),attributes);
+ }
+ else if (name=="a")
+ {
+ success=StartElementA(stackItem,structureStack.current(),attributes);
+ }
+ else if (name=="br") // NOTE: Not sure if it only exists in lower case!
+ {
+ // We have a forced line break
+ StackItem* stackCurrent=structureStack.current();
+ success=StartElementBR(stackItem,stackCurrent,mainDocument);
+ }
+ else if (name=="cbr") // NOTE: Not sure if it only exists in lower case!
+ {
+ // We have a forced column break (not supported by KWord)
+ stackItem->elementType=ElementTypeEmpty;
+ StackItem* stackCurrent=structureStack.current();
+ if (stackCurrent->elementType==ElementTypeContent)
+ {
+ kdWarning(30506) << "Forced column break found! Transforming to forced page break" << endl;
+ success=complexForcedPageBreak(stackItem);
+ }
+ else if (stackCurrent->elementType==ElementTypeParagraph)
+ {
+ kdWarning(30506) << "Forced column break found! Transforming to forced page break" << endl;
+ success=StartElementPBR(stackItem,stackCurrent,mainDocument);
+ }
+ else
+ {
+ kdError(30506) << "Forced column break found out of turn! Aborting! Parent: "
+ << stackCurrent->itemName <<endl;
+ success=false;
+ }
+ }
+ else if (name=="pbr") // NOTE: Not sure if it only exists in lower case!
+ {
+ // We have a forced page break
+ stackItem->elementType=ElementTypeEmpty;
+ StackItem* stackCurrent=structureStack.current();
+ if (stackCurrent->elementType==ElementTypeContent)
+ {
+ success=complexForcedPageBreak(stackItem);
+ }
+ else if (stackCurrent->elementType==ElementTypeParagraph)
+ {
+ success=StartElementPBR(stackItem,stackCurrent,mainDocument);
+ }
+ else
+ {
+ kdError(30506) << "Forced page break found out of turn! Aborting! Parent: "
+ << stackCurrent->itemName <<endl;
+ success=false;
+ }
+ }
+ else if (name=="pagesize")
+ // Does only exist as lower case tag!
+ {
+ stackItem->elementType=ElementTypeEmpty;
+ stackItem->stackElementText=structureStack.current()->stackElementText; // TODO: reason?
+ success=StartElementPageSize(m_paperElement,attributes);
+ }
+ else if ((name=="field") //TODO: upper-case?
+ || (name=="f")) // old deprecated name for <field>
+ {
+ success=StartElementField(stackItem,structureStack.current(),mainDocument,attributes);
+ }
+ else if (name=="s") // Seems only to exist as lower case
+ {
+ success=StartElementS(stackItem,structureStack.current(),attributes,styleDataMap);
+ }
+ else if ((name=="image") //TODO: upper-case?
+ || (name=="i")) // old deprecated name for <image>
+ {
+ success=StartElementImage(stackItem,structureStack.current(),attributes);
+ }
+ else if (name=="d") // TODO: upper-case?
+ {
+ success=StartElementD(stackItem,structureStack.current(),attributes);
+ }
+ else if (name=="iw") // No upper-case
+ {
+ stackItem->elementType=ElementTypeIgnoreWord;
+ success=true;
+ }
+ else if (name=="m") // No upper-case
+ {
+ success=StartElementM(stackItem,structureStack.current(),attributes);
+ }
+ else if (name=="foot") // No upper-case
+ {
+ success=StartElementFoot(stackItem,structureStack.current(),attributes);
+ }
+ else if (name=="table") // No upper-case
+ {
+ success=StartElementTable(stackItem,structureStack.current(), attributes);
+ }
+ else if (name=="cell") // No upper-case
+ {
+ success=StartElementCell(stackItem,structureStack.current(),attributes);
+ }
+ else
+ {
+ stackItem->elementType=ElementTypeUnknown;
+ stackItem->stackElementText=structureStack.current()->stackElementText; // TODO: reason?
+ success=true;
+ }
+ if (success)
+ {
+ structureStack.push(stackItem);
+ }
+ else
+ { // We have a problem so destroy our resources.
+ delete stackItem;
+ }
+ return success;
+}
+
+bool StructureParser :: endElement( const TQString&, const TQString& , const TQString& name)
+{
+ indent.remove( 0, 1 ); // DEBUG
+ kdDebug(30506) << indent << " </" << name << ">" << endl;
+
+ if (structureStack.isEmpty())
+ {
+ kdError(30506) << "Stack is empty!! Aborting! (in StructureParser::endElement)" << endl;
+ return false;
+ }
+
+ bool success=false;
+
+ StackItem *stackItem=structureStack.pop();
+ if ((name=="c")||(name=="C"))
+ {
+ success=EndElementC(stackItem,structureStack.current());
+ }
+ else if ((name=="p")||(name=="P"))
+ {
+ success=EndElementP(stackItem);
+ }
+ else if (name=="a")
+ {
+ if (stackItem->elementType==ElementTypeContent)
+ {
+ // Anchor to a bookmark (not supported by KWord))
+ success=EndElementC(stackItem,structureStack.current());
+ }
+ else
+ {
+ // Normal anchor
+ success=EndElementA(stackItem,structureStack.current(), mainDocument);
+ }
+ }
+ else if (name=="d")
+ {
+ success=EndElementD(stackItem);
+ }
+ else if (name=="iw") // No upper-case
+ {
+ success=EndElementIW(stackItem,structureStack.current(), mainDocument, m_ignoreWordsElement);
+ }
+ else if (name=="m") // No upper-case
+ {
+ success=EndElementM(stackItem);
+ }
+ else
+ {
+ success=true; // No problem, so authorisation to continue parsing
+ }
+ if (!success)
+ {
+ // If we have no success, then it was surely a tag mismatch. Help debugging!
+ kdError(30506) << "Found tag name: " << name
+ << " expected: " << stackItem->itemName << endl;
+ }
+ delete stackItem;
+ return success;
+}
+
+bool StructureParser :: characters ( const TQString & ch )
+{
+ // DEBUG start
+ if (ch=="\n")
+ {
+ kdDebug(30506) << indent << " (LINEFEED)" << endl;
+ }
+ else if (ch.length()> 40)
+ { // 40 characters are enough (especially for image data)
+ kdDebug(30506) << indent << " :" << ch.left(40) << "..." << endl;
+ }
+ else
+ {
+ kdDebug(30506) << indent << " :" << ch << ":" << endl;
+ }
+ // DEBUG end
+ if (structureStack.isEmpty())
+ {
+ kdError(30506) << "Stack is empty!! Aborting! (in StructureParser::characters)" << endl;
+ return false;
+ }
+
+ bool success=false;
+
+ StackItem *stackItem=structureStack.current();
+
+ if ((stackItem->elementType==ElementTypeContent)
+ || (stackItem->elementType==ElementTypeAnchorContent))
+ { // <c>
+ success=charactersElementC(stackItem,mainDocument,ch);
+ }
+ else if (stackItem->elementType==ElementTypeParagraph)
+ { // <p>
+ success=charactersElementP(stackItem,mainDocument,ch);
+ }
+ else if (stackItem->elementType==ElementTypeAnchor)
+ { // <a>
+ success=charactersElementA(stackItem,ch);
+ }
+ else if (stackItem->elementType==ElementTypeEmpty)
+ {
+ success=ch.stripWhiteSpace().isEmpty();
+ if (!success)
+ {
+ // We have a parsing error, so abort!
+ kdError(30506) << "Empty element "<< stackItem->itemName
+ <<" is not empty! Aborting! (in StructureParser::characters)" << endl;
+ }
+ }
+ else if (stackItem->elementType==ElementTypeRealData)
+ {
+ success=CharactersElementD(stackItem,mainDocument,ch);
+ }
+ else if (stackItem->elementType==ElementTypeIgnoreWord)
+ {
+ stackItem->strTemp2+=ch; // Just collect the data
+ success=true;
+ }
+ else if (stackItem->elementType==ElementTypeRealMetaData)
+ {
+ success=CharactersElementM(stackItem,ch);
+ }
+ else
+ {
+ success=true;
+ }
+
+ return success;
+}
+
+bool StructureParser::startDocument(void)
+{
+ indent = TQString(); //DEBUG
+ styleDataMap.defineDefaultStyles();
+ return true;
+}
+
+void StructureParser::createDocInfo(void)
+{
+ TQDomImplementation implementation;
+ TQDomDocument doc(implementation.createDocumentType("document-info",
+ "-//KDE//DTD document-info 1.2//EN", "http://www.koffice.org/DTD/document-info-1.2.dtd"));
+
+ m_info=doc;
+
+ m_info.appendChild(
+ mainDocument.createProcessingInstruction(
+ "xml","version=\"1.0\" encoding=\"UTF-8\""));
+
+ TQDomElement elementDoc(mainDocument.createElement("document-info"));
+ elementDoc.setAttribute("xmlns","http://www.koffice.org/DTD/document-info");
+ m_info.appendChild(elementDoc);
+
+ TQDomElement about(mainDocument.createElement("about"));
+ elementDoc.appendChild(about);
+
+ TQDomElement abstract(mainDocument.createElement("abstract"));
+ about.appendChild(abstract);
+ abstract.appendChild(mainDocument.createTextNode(m_metadataMap["dc.description"]));
+
+ TQDomElement title(mainDocument.createElement("title"));
+ about.appendChild(title);
+ title.appendChild(mainDocument.createTextNode(m_metadataMap["dc.title"]));
+
+ TQDomElement keyword(mainDocument.createElement("keyword"));
+ about.appendChild(keyword);
+ keyword.appendChild(mainDocument.createTextNode(m_metadataMap["abiword.keywords"]));
+
+ TQDomElement subject(mainDocument.createElement("subject"));
+ about.appendChild(subject);
+ subject.appendChild(mainDocument.createTextNode(m_metadataMap["dc.subject"]));
+}
+
+bool StructureParser::endDocument(void)
+{
+ TQDomElement stylesPluralElement=mainDocument.createElement("STYLES");
+ // insert before <PICTURES>, as <PICTURES> must remain last.
+ mainDocument.documentElement().insertBefore(stylesPluralElement,m_picturesElement);
+
+ kdDebug(30506) << "###### Start Style List ######" << endl;
+ StyleDataMap::ConstIterator it;
+
+ // At first, we put the Normal style
+ it=styleDataMap.find("Normal");
+ if (it!=styleDataMap.end())
+ {
+ kdDebug(30506) << "\"" << it.key() << "\" => " << it.data().m_props << endl;
+ TQDomElement styleElement=mainDocument.createElement("STYLE");
+ stylesPluralElement.appendChild(styleElement);
+ AddStyle(styleElement, it.key(),it.data(),mainDocument);
+ }
+ else
+ kdWarning(30506) << "No 'Normal' style" << endl;
+
+ for (it=styleDataMap.begin();it!=styleDataMap.end();++it)
+ {
+ if (it.key()=="Normal")
+ continue;
+
+ kdDebug(30506) << "\"" << it.key() << "\" => " << it.data().m_props << endl;
+
+ TQDomElement styleElement=mainDocument.createElement("STYLE");
+ stylesPluralElement.appendChild(styleElement);
+
+ AddStyle(styleElement, it.key(),it.data(),mainDocument);
+ }
+ kdDebug(30506) << "###### End Style List ######" << endl;
+
+ createDocInfo();
+
+ return true;
+}
+
+bool StructureParser::warning(const TQXmlParseException& exception)
+{
+ kdWarning(30506) << "XML parsing warning: line " << exception.lineNumber()
+ << " col " << exception.columnNumber() << " message: " << exception.message() << endl;
+ return true;
+}
+
+bool StructureParser::error(const TQXmlParseException& exception)
+{
+ // A XML error is recoverable, so it is only a KDE warning
+ kdWarning(30506) << "XML parsing error: line " << exception.lineNumber()
+ << " col " << exception.columnNumber() << " message: " << exception.message() << endl;
+ return true;
+}
+
+bool StructureParser::fatalError (const TQXmlParseException& exception)
+{
+ kdError(30506) << "XML parsing fatal error: line " << exception.lineNumber()
+ << " col " << exception.columnNumber() << " message: " << exception.message() << endl;
+ m_fatalerror=true;
+ KMessageBox::error(NULL, i18n("An error has occurred while parsing the AbiWord file.\nAt line: %1, column %2\nError message: %3")
+ .arg(exception.lineNumber()).arg(exception.columnNumber())
+ .arg( i18n( "TQXml", exception.message().utf8() ) ),
+ i18n("AbiWord Import Filter"),0);
+ return false; // Stop parsing now, we do not need further errors.
+}
+
+void StructureParser :: createDocument(void)
+{
+ TQDomImplementation implementation;
+ TQDomDocument doc(implementation.createDocumentType("DOC",
+ "-//KDE//DTD kword 1.2//EN", "http://www.koffice.org/DTD/kword-1.2.dtd"));
+
+ mainDocument=doc;
+
+ mainDocument.appendChild(
+ mainDocument.createProcessingInstruction(
+ "xml","version=\"1.0\" encoding=\"UTF-8\""));
+
+ TQDomElement elementDoc;
+ elementDoc=mainDocument.createElement("DOC");
+ elementDoc.setAttribute("xmlns","http://www.koffice.org/DTD/kword");
+ elementDoc.setAttribute("editor","AbiWord Import Filter");
+ elementDoc.setAttribute("mime","application/x-kword");
+ elementDoc.setAttribute( "syntaxVersion", 3 );
+ mainDocument.appendChild(elementDoc);
+
+ TQDomElement element;
+ element=mainDocument.createElement("ATTRIBUTES");
+ element.setAttribute("processing",0);
+ element.setAttribute("standardpage",1);
+ element.setAttribute("hasHeader",0);
+ element.setAttribute("hasFooter",0);
+ //element.setAttribute("unit","mm"); // use KWord default instead
+ element.setAttribute("tabStopValue",36); // AbiWord has a default of 0.5 inch tab stops
+ elementDoc.appendChild(element);
+
+ // <PAPER> will be partialy changed by an AbiWord <pagesize> element.
+ // Default paper format of AbiWord is "Letter"
+ m_paperElement=mainDocument.createElement("PAPER");
+ m_paperElement.setAttribute("format",PG_US_LETTER);
+ m_paperElement.setAttribute("width",MillimetresToPoints(KoPageFormat::width (PG_US_LETTER,PG_PORTRAIT)));
+ m_paperElement.setAttribute("height",MillimetresToPoints(KoPageFormat::height(PG_US_LETTER,PG_PORTRAIT)));
+ m_paperElement.setAttribute("orientation",PG_PORTRAIT);
+ m_paperElement.setAttribute("columns",1);
+ m_paperElement.setAttribute("columnspacing",2);
+ m_paperElement.setAttribute("hType",0);
+ m_paperElement.setAttribute("fType",0);
+ m_paperElement.setAttribute("spHeadBody",9);
+ m_paperElement.setAttribute("spFootBody",9);
+ m_paperElement.setAttribute("zoom",100);
+ elementDoc.appendChild(m_paperElement);
+
+ m_paperBordersElement=mainDocument.createElement("PAPERBORDERS");
+ m_paperBordersElement.setAttribute("left",28);
+ m_paperBordersElement.setAttribute("top",42);
+ m_paperBordersElement.setAttribute("right",28);
+ m_paperBordersElement.setAttribute("bottom",42);
+ m_paperElement.appendChild(m_paperBordersElement);
+
+ framesetsPluralElement=mainDocument.createElement("FRAMESETS");
+ mainDocument.documentElement().appendChild(framesetsPluralElement);
+
+ mainFramesetElement=mainDocument.createElement("FRAMESET");
+ mainFramesetElement.setAttribute("frameType",1);
+ mainFramesetElement.setAttribute("frameInfo",0);
+ mainFramesetElement.setAttribute("visible",1);
+ mainFramesetElement.setAttribute("name",i18n("Frameset name","Main Text Frameset"));
+ framesetsPluralElement.appendChild(mainFramesetElement);
+
+ TQDomElement frameElementOut=mainDocument.createElement("FRAME");
+ frameElementOut.setAttribute("left",28);
+ frameElementOut.setAttribute("top",42);
+ frameElementOut.setAttribute("bottom",566);
+ frameElementOut.setAttribute("right",798);
+ frameElementOut.setAttribute("runaround",1);
+ // TODO: a few attributes are missing
+ mainFramesetElement.appendChild(frameElementOut);
+
+ // As we are manipulating the document, create a few particular elements
+ m_ignoreWordsElement=mainDocument.createElement("SPELLCHECKIGNORELIST");
+ mainDocument.documentElement().appendChild(m_ignoreWordsElement);
+ m_picturesElement=mainDocument.createElement("PICTURES");
+ mainDocument.documentElement().appendChild(m_picturesElement);
+}
+
+bool StructureParser::clearStackUntilParagraph(StackItemStack& auxilaryStack)
+{
+ for (;;)
+ {
+ StackItem* item=structureStack.pop();
+ switch (item->elementType)
+ {
+ case ElementTypeContent:
+ {
+ // Push the item on the auxilary stack
+ auxilaryStack.push(item);
+ break;
+ }
+ case ElementTypeParagraph:
+ {
+ // Push back the item on this stack and then stop loop
+ structureStack.push(item);
+ return true;
+ }
+ default:
+ {
+ // Something has gone wrong!
+ kdError(30506) << "Cannot clear this element: "
+ << item->itemName << endl;
+ return false;
+ }
+ }
+ }
+}
+
+ABIWORDImport::ABIWORDImport(KoFilter */*parent*/, const char */*name*/, const TQStringList &) :
+ KoFilter() {
+}
+
+KoFilter::ConversionStatus ABIWORDImport::convert( const TQCString& from, const TQCString& to )
+{
+ if ((to != "application/x-kword") || (from != "application/x-abiword"))
+ return KoFilter::NotImplemented;
+
+ kdDebug(30506)<<"AbiWord to KWord Import filter"<<endl;
+
+ StructureParser handler(m_chain);
+
+ //We arbitrarily decide that TQt can handle the encoding in which the file was written!!
+ TQXmlSimpleReader reader;
+ reader.setContentHandler( &handler );
+ reader.setErrorHandler( &handler );
+
+ //Find the last extension
+ TQString strExt;
+ TQString fileIn = m_chain->inputFile();
+ const int result=fileIn.findRev('.');
+ if (result>=0)
+ {
+ strExt=fileIn.mid(result);
+ }
+
+ kdDebug(30506) << "File extension: -" << strExt << "-" << endl;
+
+ TQString strMime; // Mime type of the compressor (default: unknown)
+
+ if ((strExt==".gz")||(strExt==".GZ") //in case of .abw.gz (logical extension)
+ ||(strExt==".zabw")||(strExt==".ZABW")) //in case of .zabw (extension used prioritary with AbiWord)
+ {
+ // Compressed with gzip
+ strMime="application/x-gzip";
+ kdDebug(30506) << "Compression: gzip" << endl;
+ }
+ else if ((strExt==".bz2")||(strExt==".BZ2") //in case of .abw.bz2 (logical extension)
+ ||(strExt==".bzabw")||(strExt==".BZABW")) //in case of .bzabw (extension used prioritary with AbiWord)
+ {
+ // Compressed with bzip2
+ strMime="application/x-bzip2";
+ kdDebug(30506) << "Compression: bzip2" << endl;
+ }
+
+ TQIODevice* in = KFilterDev::deviceForFile(fileIn,strMime);
+
+ if ( !in )
+ {
+ kdError(30506) << "Cannot create device for uncompressing! Aborting!" << endl;
+ return KoFilter::FileNotFound; // ### TODO: better error?
+ }
+
+ if (!in->open(IO_ReadOnly))
+ {
+ kdError(30506) << "Cannot open file for uncompressing! Aborting!" << endl;
+ delete in;
+ return KoFilter::FileNotFound;
+ }
+
+ TQXmlInputSource source(in); // Read the file
+
+ in->close();
+
+ if (!reader.parse( source ))
+ {
+ kdError(30506) << "Import: Parsing unsuccessful. Aborting!" << endl;
+ delete in;
+ if (!handler.wasFatalError())
+ {
+ // As the parsing was stopped for something else than a fatal error, we have not yet get an error message. (Can it really happen?)
+ KMessageBox::error(NULL, i18n("An error occurred during the load of the AbiWord file: %1").arg(from.data()),
+ i18n("AbiWord Import Filter"),0);
+ }
+ return KoFilter::ParsingError;
+ }
+ delete in;
+
+ TQCString strOut;
+ KoStoreDevice* out;
+
+ kdDebug(30506) << "Creating documentinfo.xml" << endl;
+ out=m_chain->storageFile( "documentinfo.xml", KoStore::Write );
+ if(!out)
+ {
+ kdError(30506) << "AbiWord Import unable to open output file! (Documentinfo)" << endl;
+ KMessageBox::error(NULL, i18n("Unable to save document information."),i18n("AbiWord Import Filter"),0);
+ return KoFilter::StorageCreationError;
+ }
+
+ //Write the document information!
+ strOut=handler.getDocInfo().toCString(); // UTF-8
+ // WARNING: we cannot use KoStore::write(const TQByteArray&) because it writes an extra NULL character at the end.
+ out->writeBlock(strOut,strOut.length());
+
+ kdDebug(30506) << "Creating maindoc.xml" << endl;
+ out=m_chain->storageFile( "root", KoStore::Write );
+ if(!out)
+ {
+ kdError(30506) << "AbiWord Import unable to open output file! (Root)" << endl;
+ KMessageBox::error(NULL, i18n("Unable to save main document."),i18n("AbiWord Import Filter"),0);
+ return KoFilter::StorageCreationError;
+ }
+
+ //Write the document!
+ strOut=handler.getDocument().toCString(); // UTF-8
+ // WARNING: we cannot use KoStore::write(const TQByteArray&) because it writes an extra NULL character at the end.
+ out->writeBlock(strOut,strOut.length());
+
+#if 0
+ kdDebug(30506) << documentOut.toString();
+#endif
+
+ kdDebug(30506) << "Now importing to KWord!" << endl;
+
+ return KoFilter::OK;
+}
+
+#include "abiwordimport.moc"