filters

abiwordimport.cc

00001 /* This file is part of the KDE project
00002    Copyright 2001, 2002, 2003, 2004 Nicolas GOUTTE <goutte@kde.org>
00003 
00004    This library is free software; you can redistribute it and/or
00005    modify it under the terms of the GNU Library General Public
00006    License as published by the Free Software Foundation; either
00007    version 2 of the License, or (at your option) any later version.
00008 
00009    This library is distributed in the hope that it will be useful,
00010    but WITHOUT ANY WARRANTY; without even the implied warranty of
00011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012    Library General Public License for more details.
00013 
00014    You should have received a copy of the GNU Library General Public License
00015    along with this library; see the file COPYING.LIB.  If not, write to
00016    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00017  * Boston, MA 02110-1301, USA.
00018 */
00019 
00020 #include <config.h>
00021 
00022 #ifdef HAVE_UNISTD_H
00023 #include <unistd.h>
00024 #endif
00025 
00026 #include <qmap.h>
00027 #include <qbuffer.h>
00028 #include <qpicture.h>
00029 #include <qxml.h>
00030 #include <qdom.h>
00031 #include <qdatetime.h>
00032 
00033 #include <kdebug.h>
00034 #include <kmdcodec.h>
00035 #include <kfilterdev.h>
00036 #include <kgenericfactory.h>
00037 #include <kmessagebox.h>
00038 
00039 #include <KoPageLayout.h>
00040 #include <KoStore.h>
00041 #include <KoFilterChain.h>
00042 
00043 #include "ImportHelpers.h"
00044 #include "ImportFormatting.h"
00045 #include "ImportStyle.h"
00046 #include "ImportField.h"
00047 
00048 #include "abiwordimport.h"
00049 
00050 class ABIWORDImportFactory : KGenericFactory<ABIWORDImport, KoFilter>
00051 {
00052 public:
00053     ABIWORDImportFactory(void) : KGenericFactory<ABIWORDImport, KoFilter> ("kwordabiwordimport")
00054     {}
00055 protected:
00056     virtual void setupTranslations( void )
00057     {
00058         KGlobal::locale()->insertCatalogue( "kofficefilters" );
00059     }
00060 };
00061 
00062 K_EXPORT_COMPONENT_FACTORY( libabiwordimport, ABIWORDImportFactory() )
00063 
00064 // *Note for the reader of this code*
00065 // Tags in lower case (e.g. <c>) are AbiWord's ones.
00066 // Tags in upper case (e.g. <TEXT>) are KWord's ones.
00067 
00068 // enum StackItemElementType is now in the file ImportFormatting.h
00069 
00070 class StructureParser : public QXmlDefaultHandler
00071 {
00072 public:
00073     StructureParser(KoFilterChain* chain)
00074         :  m_chain(chain), m_pictureNumber(0), m_pictureFrameNumber(0), m_tableGroupNumber(0),
00075         m_timepoint(QDateTime::currentDateTime(Qt::UTC)), m_fatalerror(false)
00076     {
00077         createDocument();
00078         structureStack.setAutoDelete(true);
00079         StackItem *stackItem=new(StackItem);
00080         stackItem->elementType=ElementTypeBottom;
00081         stackItem->m_frameset=mainFramesetElement; // The default frame set.
00082         stackItem->stackElementText=mainFramesetElement; // This is more for DEBUG
00083         structureStack.push(stackItem); //Security item (not to empty the stack)
00084     }
00085     virtual ~StructureParser()
00086     {
00087         structureStack.clear();
00088     }
00089 public:
00090     virtual bool startDocument(void);
00091     virtual bool endDocument(void);
00092     virtual bool startElement( const QString&, const QString&, const QString& name, const QXmlAttributes& attributes);
00093     virtual bool endElement( const QString&, const QString& , const QString& qName);
00094     virtual bool characters ( const QString & ch );
00095     virtual bool warning(const QXmlParseException& exception);
00096     virtual bool error(const QXmlParseException& exception);
00097     virtual bool fatalError(const QXmlParseException& exception);
00098 public:
00099     inline QDomDocument getDocInfo(void) const { return m_info; }
00100     inline QDomDocument getDocument(void) const { return mainDocument; }
00101     inline bool wasFatalError(void) const { return m_fatalerror; }
00102 protected:
00103     bool clearStackUntilParagraph(StackItemStack& auxilaryStack);
00104     bool complexForcedPageBreak(StackItem* stackItem);
00105 private:
00106     // The methods that would need too much parameters are private instead of being static outside the class
00107     bool StartElementC(StackItem* stackItem, StackItem* stackCurrent,
00108         const QXmlAttributes& attributes);
00109     bool StartElementA(StackItem* stackItem, StackItem* stackCurrent,
00110         const QXmlAttributes& attributes);
00111     bool StartElementImage(StackItem* stackItem, StackItem* stackCurrent,
00112         const QXmlAttributes& attributes);
00113     bool EndElementD (StackItem* stackItem);
00114     bool EndElementM (StackItem* stackItem);
00115     bool StartElementSection(StackItem* stackItem, StackItem* stackCurrent,
00116         const QXmlAttributes& attributes);
00117     bool StartElementFoot(StackItem* stackItem, StackItem* stackCurrent,
00118         const QXmlAttributes& attributes);
00119     bool StartElementTable(StackItem* stackItem, StackItem* stackCurrent, const QXmlAttributes& attributes);
00120     bool StartElementCell(StackItem* stackItem, StackItem* stackCurrent,const QXmlAttributes& attributes);
00121 private:
00122     void createDocument(void);
00123     void createDocInfo(void);
00124     QString indent; //DEBUG
00125     StackItemStack structureStack;
00126     QDomDocument mainDocument;
00127     QDomDocument m_info;
00128     QDomElement framesetsPluralElement; // <FRAMESETS>
00129     QDomElement mainFramesetElement;    // The main <FRAMESET> where the body text will be under.
00130     QDomElement m_picturesElement;      // <PICTURES>
00131     QDomElement m_paperElement;         // <PAPER>
00132     QDomElement m_paperBordersElement;  // <PAPERBORDER>
00133     QDomElement m_ignoreWordsElement;    // <SPELLCHECKIGNORELIST>
00134     StyleDataMap styleDataMap;
00135     KoFilterChain* m_chain;
00136     uint m_pictureNumber;                   // unique: increment *before* use
00137     uint m_pictureFrameNumber;              // unique: increment *before* use
00138     uint m_tableGroupNumber;                // unique: increment *before* use
00139     QMap<QString,QString> m_metadataMap;    // Map for <m> elements
00140     QDateTime m_timepoint;              // Date/time (for pictures)
00141     bool m_fatalerror;                  // Did a XML parsing fatal error happened?
00142 };
00143 
00144 // Element <c>
00145 
00146 bool StructureParser::StartElementC(StackItem* stackItem, StackItem* stackCurrent, const QXmlAttributes& attributes)
00147 {
00148     // <c> elements can be nested in <p> elements, in <a> elements or in other <c> elements
00149     // AbiWord does not nest <c> elements in other <c> elements, but explicitly allows external programs to do it!
00150 
00151     // <p> or <c> (not child of <a>)
00152     if ((stackCurrent->elementType==ElementTypeParagraph)||(stackCurrent->elementType==ElementTypeContent))
00153     {
00154         // Contents can have styles, however KWord cannot have character style.
00155         // Therefore we use the style if it exist, but we do not create it if not.
00156         QString strStyleProps;
00157         QString strStyleName=attributes.value("style").stripWhiteSpace();
00158         if (!strStyleName.isEmpty())
00159         {
00160             StyleDataMap::Iterator it=styleDataMap.find(strStyleName);
00161             if (it!=styleDataMap.end())
00162             {
00163                 strStyleProps=it.data().m_props;
00164             }
00165         }
00166 
00167         AbiPropsMap abiPropsMap;
00168         PopulateProperties(stackItem,strStyleProps,attributes,abiPropsMap,true);
00169 
00170         stackItem->elementType=ElementTypeContent;
00171         stackItem->stackElementParagraph=stackCurrent->stackElementParagraph;   // <PARAGRAPH>
00172         stackItem->stackElementText=stackCurrent->stackElementText;   // <TEXT>
00173         stackItem->stackElementFormatsPlural=stackCurrent->stackElementFormatsPlural; // <FORMATS>
00174         stackItem->pos=stackCurrent->pos; //Propagate the position
00175     }
00176     // <a> or <c> when child of <a>
00177     else if ((stackCurrent->elementType==ElementTypeAnchor)||(stackCurrent->elementType==ElementTypeAnchorContent))
00178     {
00179         stackItem->elementType=ElementTypeAnchorContent;
00180     }
00181     else
00182     {//we are not nested correctly, so consider it a parse error!
00183         kdError(30506) << "parse error <c> tag nested neither in <p> nor in <c> nor in <a> but in "
00184             << stackCurrent->itemName << endl;
00185         return false;
00186     }
00187     return true;
00188 }
00189 
00190 bool charactersElementC (StackItem* stackItem, QDomDocument& mainDocument, const QString & ch)
00191 {
00192     if  (stackItem->elementType==ElementTypeContent)
00193     {   // Normal <c>
00194         QDomElement elementText=stackItem->stackElementText;
00195         QDomElement elementFormatsPlural=stackItem->stackElementFormatsPlural;
00196         elementText.appendChild(mainDocument.createTextNode(ch));
00197 
00198         QDomElement formatElementOut=mainDocument.createElement("FORMAT");
00199         formatElementOut.setAttribute("id",1); // Normal text!
00200         formatElementOut.setAttribute("pos",stackItem->pos); // Start position
00201         formatElementOut.setAttribute("len",ch.length()); // Start position
00202         elementFormatsPlural.appendChild(formatElementOut); //Append to <FORMATS>
00203         stackItem->pos+=ch.length(); // Adapt new starting position
00204 
00205         AddFormat(formatElementOut, stackItem, mainDocument);
00206     }
00207     else if (stackItem->elementType==ElementTypeAnchorContent)
00208     {
00209         // Add characters to the link name
00210         stackItem->strTemp2+=ch;
00211         // TODO: how can we care about the text format?
00212     }
00213     else
00214     {
00215         kdError(30506) << "Internal error (in charactersElementC)" << endl;
00216     }
00217 
00218     return true;
00219 }
00220 
00221 bool EndElementC (StackItem* stackItem, StackItem* stackCurrent)
00222 {
00223     if (stackItem->elementType==ElementTypeContent)
00224     {
00225         stackItem->stackElementText.normalize();
00226         stackCurrent->pos=stackItem->pos; //Propagate the position back to the parent element
00227     }
00228     else if (stackItem->elementType==ElementTypeAnchorContent)
00229     {
00230         stackCurrent->strTemp2+=stackItem->strTemp2; //Propagate the link name back to the parent element
00231     }
00232     else
00233     {
00234         kdError(30506) << "Wrong element type!! Aborting! (</c> in StructureParser::endElement)" << endl;
00235         return false;
00236     }
00237     return true;
00238 }
00239 
00240 // Element <a>
00241 bool StructureParser::StartElementA(StackItem* stackItem, StackItem* stackCurrent, const QXmlAttributes& attributes)
00242 {
00243     // <a> elements can be nested in <p> elements
00244     if (stackCurrent->elementType==ElementTypeParagraph)
00245     {
00246 
00247         //AbiPropsMap abiPropsMap;
00248         //PopulateProperties(stackItem,QString::null,attributes,abiPropsMap,true);
00249 
00250         stackItem->elementType=ElementTypeAnchor;
00251         stackItem->stackElementParagraph=stackCurrent->stackElementParagraph;   // <PARAGRAPH>
00252         stackItem->stackElementText=stackCurrent->stackElementText;   // <TEXT>
00253         stackItem->stackElementFormatsPlural=stackCurrent->stackElementFormatsPlural; // <FORMATS>
00254         stackItem->pos=stackCurrent->pos; //Propagate the position
00255         stackItem->strTemp1=attributes.value("xlink:href").stripWhiteSpace(); // link reference
00256         stackItem->strTemp2=QString::null; // link name
00257 
00258         // We must be careful: AbiWord permits anchors to bookmarks.
00259         //  However, KWord does not know what a bookmark is.
00260         if (stackItem->strTemp1[0]=='#')
00261         {
00262             kdWarning(30506) << "Anchor <a> to bookmark: " << stackItem->strTemp1 << endl
00263                 << " Processing <a> like <c>" << endl;
00264             // We have a reference to a bookmark. Therefore treat it as a normal content <c>
00265             return StartElementC(stackItem, stackCurrent, attributes);
00266         }
00267     }
00268     else
00269     {//we are not nested correctly, so consider it a parse error!
00270         kdError(30506) << "parse error <a> tag not a child of <p> but of "
00271             << stackCurrent->itemName << endl;
00272         return false;
00273     }
00274     return true;
00275 }
00276 
00277 static bool charactersElementA (StackItem* stackItem, const QString & ch)
00278 {
00279     // Add characters to the link name
00280     stackItem->strTemp2+=ch;
00281     return true;
00282 }
00283 
00284 static bool EndElementA (StackItem* stackItem, StackItem* stackCurrent, QDomDocument& mainDocument)
00285 {
00286     if (!stackItem->elementType==ElementTypeAnchor)
00287     {
00288         kdError(30506) << "Wrong element type!! Aborting! (</a> in StructureParser::endElement)" << endl;
00289         return false;
00290     }
00291 
00292     QDomElement elementText=stackItem->stackElementText;
00293     elementText.appendChild(mainDocument.createTextNode("#"));
00294 
00295     QDomElement formatElement=mainDocument.createElement("FORMAT");
00296     formatElement.setAttribute("id",4); // Variable
00297     formatElement.setAttribute("pos",stackItem->pos); // Start position
00298     formatElement.setAttribute("len",1); // Start position
00299 
00300     QDomElement variableElement=mainDocument.createElement("VARIABLE");
00301     formatElement.appendChild(variableElement);
00302 
00303     QDomElement typeElement=mainDocument.createElement("TYPE");
00304     typeElement.setAttribute("key","STRING");
00305     typeElement.setAttribute("type",9); // link
00306     typeElement.setAttribute("text",stackItem->strTemp2);
00307     variableElement.appendChild(typeElement); //Append to <VARIABLE>
00308 
00309     QDomElement linkElement=mainDocument.createElement("LINK");
00310     linkElement.setAttribute("hrefName",stackItem->strTemp1);
00311     linkElement.setAttribute("linkName",stackItem->strTemp2);
00312     variableElement.appendChild(linkElement); //Append to <VARIABLE>
00313 
00314     // Now work on stackCurrent
00315     stackCurrent->stackElementFormatsPlural.appendChild(formatElement);
00316     stackCurrent->pos++; //Propagate the position back to the parent element
00317 
00318     return true;
00319 }
00320 
00321 // Element <p>
00322 
00323 bool StartElementP(StackItem* stackItem, StackItem* stackCurrent,
00324     QDomDocument& mainDocument,
00325     StyleDataMap& styleDataMap, const QXmlAttributes& attributes)
00326 {
00327     // We must prepare the style
00328     QString strStyle=attributes.value("style");
00329     if (strStyle.isEmpty())
00330     {
00331         strStyle="Normal";
00332     }
00333     StyleDataMap::ConstIterator it=styleDataMap.useOrCreateStyle(strStyle);
00334 
00335     QString strLevel=attributes.value("level");
00336     int level;
00337     if (strLevel.isEmpty())
00338     {
00339         // We have not "level" attribute, so we must use the style's level.
00340         level=it.data().m_level;
00341     }
00342     else
00343     {
00344         // We have a "level" attribute, so it overrides the style's level.
00345         level=strStyle.toInt();
00346     }
00347 
00348     QDomElement elementText=stackCurrent->stackElementText;
00349     QDomElement paragraphElementOut=mainDocument.createElement("PARAGRAPH");
00350     stackCurrent->m_frameset.appendChild(paragraphElementOut);
00351 
00352     QDomElement textElementOut=mainDocument.createElement("TEXT");
00353     paragraphElementOut.appendChild(textElementOut);
00354     QDomElement formatsPluralElementOut=mainDocument.createElement("FORMATS");
00355     paragraphElementOut.appendChild(formatsPluralElementOut);
00356 
00357     AbiPropsMap abiPropsMap;
00358     PopulateProperties(stackItem,it.data().m_props,attributes,abiPropsMap,false);
00359 
00360     stackItem->elementType=ElementTypeParagraph;
00361     stackItem->stackElementParagraph=paragraphElementOut; // <PARAGRAPH>
00362     stackItem->stackElementText=textElementOut; // <TEXT>
00363     stackItem->stackElementFormatsPlural=formatsPluralElementOut; // <FORMATS>
00364     stackItem->pos=0; // No text characters yet
00365 
00366     // Now we populate the layout
00367     QDomElement layoutElement=mainDocument.createElement("LAYOUT");
00368     paragraphElementOut.appendChild(layoutElement);
00369 
00370     AddLayout(strStyle,layoutElement, stackItem, mainDocument, abiPropsMap, level, false);
00371 
00372     return true;
00373 }
00374 
00375 bool charactersElementP (StackItem* stackItem, QDomDocument& mainDocument, const QString & ch)
00376 {
00377     QDomElement elementText=stackItem->stackElementText;
00378 
00379     elementText.appendChild(mainDocument.createTextNode(ch));
00380 
00381     stackItem->pos+=ch.length(); // Adapt new starting position
00382 
00383     return true;
00384 }
00385 
00386 bool EndElementP (StackItem* stackItem)
00387 {
00388     if (!stackItem->elementType==ElementTypeParagraph)
00389     {
00390         kdError(30506) << "Wrong element type!! Aborting! (in endElementP)" << endl;
00391         return false;
00392     }
00393     stackItem->stackElementText.normalize();
00394     return true;
00395 }
00396 
00397 static bool StartElementField(StackItem* stackItem, StackItem* stackCurrent,
00398     QDomDocument& mainDocument, const QXmlAttributes& attributes)
00399 {
00400     // <field> element elements can be nested in <p>
00401     if (stackCurrent->elementType==ElementTypeParagraph)
00402     {
00403         QString strType=attributes.value("type").stripWhiteSpace();
00404         kdDebug(30506)<<"<field> type:"<<strType<<endl;
00405 
00406         AbiPropsMap abiPropsMap;
00407         PopulateProperties(stackItem,QString::null,attributes,abiPropsMap,true);
00408 
00409         stackItem->elementType=ElementTypeEmpty;
00410 
00411         // We create a format element
00412         QDomElement variableElement=mainDocument.createElement("VARIABLE");
00413 
00414         if (!ProcessField(mainDocument, variableElement, strType, attributes))
00415         {
00416             // The field type was not recognised,
00417             //   therefore write the field type in red as normal text
00418             kdWarning(30506) << "Unknown <field> type: " << strType << endl;
00419             QDomElement formatElement=mainDocument.createElement("FORMAT");
00420             formatElement.setAttribute("id",1); // Variable
00421             formatElement.setAttribute("pos",stackItem->pos); // Start position
00422             formatElement.setAttribute("len",strType.length());
00423 
00424             formatElement.appendChild(variableElement);
00425 
00426             // Now work on stackCurrent
00427             stackCurrent->stackElementFormatsPlural.appendChild(formatElement);
00428             stackCurrent->stackElementText.appendChild(mainDocument.createTextNode(strType));
00429             stackCurrent->pos+=strType.length(); // Adjust position
00430 
00431             // Add formating (use stackItem)
00432             stackItem->fgColor.setRgb(255,0,0);
00433             AddFormat(formatElement, stackItem, mainDocument);
00434             return true;
00435         }
00436 
00437         // We create a format element
00438         QDomElement formatElement=mainDocument.createElement("FORMAT");
00439         formatElement.setAttribute("id",4); // Variable
00440         formatElement.setAttribute("pos",stackItem->pos); // Start position
00441         formatElement.setAttribute("len",1);
00442 
00443         formatElement.appendChild(variableElement);
00444 
00445         // Now work on stackCurrent
00446         stackCurrent->stackElementFormatsPlural.appendChild(formatElement);
00447         stackCurrent->stackElementText.appendChild(mainDocument.createTextNode("#"));
00448         stackCurrent->pos++; // Adjust position
00449 
00450         // Add formating (use stackItem)
00451         AddFormat(formatElement, stackItem, mainDocument);
00452 
00453     }
00454     else
00455     {//we are not nested correctly, so consider it a parse error!
00456         kdError(30506) << "parse error <field> tag not nested in <p> but in "
00457             << stackCurrent->itemName << endl;
00458         return false;
00459     }
00460     return true;
00461 }
00462 
00463 // <s> (style)
00464 static bool StartElementS(StackItem* stackItem, StackItem* /*stackCurrent*/,
00465     const QXmlAttributes& attributes, StyleDataMap& styleDataMap)
00466 {
00467     // We do not assume when we are called.
00468     // We also do not care if a style is defined multiple times.
00469     stackItem->elementType=ElementTypeEmpty;
00470 
00471     QString strStyleName=attributes.value("name").stripWhiteSpace();
00472 
00473     if (strStyleName.isEmpty())
00474     {
00475         kdWarning(30506) << "Style has no name!" << endl;
00476     }
00477     else
00478     {
00479         QString strLevel=attributes.value("level");
00480         int level;
00481         if (strLevel.isEmpty())
00482             level=-1; //TODO/FIXME: might be wrong if the style is based on another
00483         else
00484             level=strLevel.toInt();
00485         QString strBasedOn=attributes.value("basedon").simplifyWhiteSpace();
00486         styleDataMap.defineNewStyleFromOld(strStyleName,strBasedOn,level,attributes.value("props"));
00487         kdDebug(30506) << " Style name: " << strStyleName << endl
00488             << " Based on: " << strBasedOn  << endl
00489             << " Level: " << level << endl
00490             << " Props: " << attributes.value("props") << endl;
00491     }
00492 
00493     return true;
00494 }
00495 
00496 // <image>
00497 bool StructureParser::StartElementImage(StackItem* stackItem, StackItem* stackCurrent,
00498     const QXmlAttributes& attributes)
00499 {
00500     // <image> elements can be nested in <p> or <c> elements
00501     if ((stackCurrent->elementType!=ElementTypeParagraph) && (stackCurrent->elementType!=ElementTypeContent))
00502     {//we are not nested correctly, so consider it a parse error!
00503         kdError(30506) << "parse error <image> tag nested neither in <p> nor in <c> but in "
00504             << stackCurrent->itemName << endl;
00505         return false;
00506     }
00507     stackItem->elementType=ElementTypeEmpty;
00508 
00509     QString strDataId=attributes.value("dataid").stripWhiteSpace();
00510 
00511     AbiPropsMap abiPropsMap;
00512     abiPropsMap.splitAndAddAbiProps(attributes.value("props"));
00513 
00514     double height=ValueWithLengthUnit(abiPropsMap["height"].getValue());
00515     double width =ValueWithLengthUnit(abiPropsMap["width" ].getValue());
00516 
00517     kdDebug(30506) << "Image: " << strDataId << " height: " << height << " width: " << width << endl;
00518 
00519     // TODO: image properties
00520 
00521     if (strDataId.isEmpty())
00522     {
00523         kdWarning(30506) << "Image has no data id!" << endl;
00524     }
00525     else
00526     {
00527         kdDebug(30506) << "Image: " << strDataId << endl;
00528     }
00529 
00530     QString strPictureFrameName(i18n("Frameset name","Picture %1").arg(++m_pictureFrameNumber));
00531 
00532     // Create the frame set of the image
00533 
00534     QDomElement framesetElement=mainDocument.createElement("FRAMESET");
00535     framesetElement.setAttribute("frameType",2);
00536     framesetElement.setAttribute("frameInfo",0);
00537     framesetElement.setAttribute("visible",1);
00538     framesetElement.setAttribute("name",strPictureFrameName);
00539     framesetsPluralElement.appendChild(framesetElement);
00540 
00541     QDomElement frameElementOut=mainDocument.createElement("FRAME");
00542     frameElementOut.setAttribute("left",0);
00543     frameElementOut.setAttribute("top",0);
00544     frameElementOut.setAttribute("bottom",height);
00545     frameElementOut.setAttribute("right" ,width );
00546     frameElementOut.setAttribute("runaround",1);
00547     // TODO: a few attributes are missing
00548     framesetElement.appendChild(frameElementOut);
00549 
00550     QDomElement element=mainDocument.createElement("PICTURE");
00551     element.setAttribute("keepAspectRatio","true");
00552     framesetElement.setAttribute("frameType",2); // Picture
00553     framesetElement.appendChild(element);
00554 
00555     QDomElement key=mainDocument.createElement("KEY");
00556     key.setAttribute("filename",strDataId);
00557     key.setAttribute("year",m_timepoint.date().year());
00558     key.setAttribute("month",m_timepoint.date().month());
00559     key.setAttribute("day",m_timepoint.date().day());
00560     key.setAttribute("hour",m_timepoint.time().hour());
00561     key.setAttribute("minute",m_timepoint.time().minute());
00562     key.setAttribute("second",m_timepoint.time().second());
00563     key.setAttribute("msec",m_timepoint.time().msec());
00564     element.appendChild(key);
00565 
00566     // Now use the image's frame set
00567     QDomElement elementText=stackItem->stackElementText;
00568     QDomElement elementFormatsPlural=stackItem->stackElementFormatsPlural;
00569     elementText.appendChild(mainDocument.createTextNode("#"));
00570 
00571     QDomElement formatElementOut=mainDocument.createElement("FORMAT");
00572     formatElementOut.setAttribute("id",6); // Normal text!
00573     formatElementOut.setAttribute("pos",stackItem->pos); // Start position
00574     formatElementOut.setAttribute("len",1); // Start position
00575     elementFormatsPlural.appendChild(formatElementOut); //Append to <FORMATS>
00576 
00577     // WARNING: we must change the position in stackCurrent!
00578     stackCurrent->pos++; // Adapt new starting position
00579 
00580     QDomElement anchor=mainDocument.createElement("ANCHOR");
00581     // No name attribute!
00582     anchor.setAttribute("type","frameset");
00583     anchor.setAttribute("instance",strPictureFrameName);
00584     formatElementOut.appendChild(anchor);
00585 
00586     return true;
00587 }
00588 
00589 // <d>
00590 static bool StartElementD(StackItem* stackItem, StackItem* /*stackCurrent*/,
00591     const QXmlAttributes& attributes)
00592 {
00593     // We do not assume when we are called or if we are or not a child of <data>
00594     // However, we assume that we are after all <image> elements
00595     stackItem->elementType=ElementTypeRealData;
00596 
00597     QString strName=attributes.value("name").stripWhiteSpace();
00598     kdDebug(30506) << "Data: " << strName << endl;
00599 
00600     QString strBase64=attributes.value("base64").stripWhiteSpace();
00601     QString strMime=attributes.value("mime").stripWhiteSpace();
00602 
00603     if (strName.isEmpty())
00604     {
00605         kdWarning(30506) << "Data has no name!" << endl;
00606         stackItem->elementType=ElementTypeEmpty;
00607         return true;
00608     }
00609 
00610     if (strMime.isEmpty())
00611     {
00612         // Old AbiWord files had no mime types for images but the data were base64-coded PNG
00613         strMime="image/png";
00614         strBase64="yes";
00615     }
00616 
00617     stackItem->fontName=strName;        // Store the data name as font name.
00618     stackItem->bold=(strBase64=="yes"); // Store base64-coded as bold
00619     stackItem->strTemp1=strMime;        // Mime type
00620     stackItem->strTemp2=QString::null;  // Image data
00621 
00622     return true;
00623 }
00624 
00625 static bool CharactersElementD (StackItem* stackItem, QDomDocument& /*mainDocument*/, const QString & ch)
00626 {
00627     // As we have no guarantee to have the whole stream in one call, we must store the data.
00628     stackItem->strTemp2+=ch;
00629     return true;
00630 }
00631 
00632 bool StructureParser::EndElementD (StackItem* stackItem)
00633 {
00634     if (!stackItem->elementType==ElementTypeRealData)
00635     {
00636         kdError(30506) << "Wrong element type!! Aborting! (in endElementD)" << endl;
00637         return false;
00638     }
00639     if (!m_chain)
00640     {
00641         kdError(30506) << "No filter chain! Aborting! (in endElementD)" << endl;
00642         return false;
00643     }
00644 
00645     bool isSvg=false;  // SVG ?
00646 
00647     QString extension;
00648 
00649     // stackItem->strTemp1 contains the mime type
00650     if (stackItem->strTemp1=="image/png")
00651     {
00652         extension=".png";
00653     }
00654     else if (stackItem->strTemp1=="image/jpeg") // ### FIXME: in fact it does not exist in AbiWord
00655     {
00656         extension=".jpeg";
00657     }
00658     else if (stackItem->strTemp1=="image/svg-xml") //Yes it is - not +
00659     {
00660         extension=".svg";
00661         isSvg=true;
00662     }
00663     else
00664     {
00665         kdWarning(30506) << "Unknown or unsupported mime type: "
00666             << stackItem->strTemp1 << endl;
00667         return true;
00668     }
00669 
00670     QString strStoreName;
00671     strStoreName="pictures/picture";
00672     strStoreName+=QString::number(++m_pictureNumber);
00673     strStoreName+=extension;
00674 
00675     QString strDataId=stackItem->fontName;  // AbiWord's data id
00676     QDomElement key=mainDocument.createElement("KEY");
00677     key.setAttribute("filename",strDataId);
00678     key.setAttribute("year",m_timepoint.date().year());
00679     key.setAttribute("month",m_timepoint.date().month());
00680     key.setAttribute("day",m_timepoint.date().day());
00681     key.setAttribute("hour",m_timepoint.time().hour());
00682     key.setAttribute("minute",m_timepoint.time().minute());
00683     key.setAttribute("second",m_timepoint.time().second());
00684     key.setAttribute("msec",m_timepoint.time().msec());
00685     key.setAttribute("name",strStoreName);
00686     m_picturesElement.appendChild(key);
00687 
00688     KoStoreDevice* out=m_chain->storageFile(strStoreName, KoStore::Write);
00689     if(!out)
00690     {
00691         kdError(30506) << "Unable to open output file for: " << stackItem->fontName << " Storage: " << strStoreName << endl;
00692         return false;
00693     }
00694 
00695     if (stackItem->bold) // Is base64-coded?
00696     {
00697         kdDebug(30506) << "Decode and write base64 stream: " << stackItem->fontName << endl;
00698         // We need to decode the base64 stream
00699         // However KCodecs has no QString to QByteArray decoder!
00700         QByteArray base64Stream=stackItem->strTemp2.utf8(); // Use utf8 to avoid corruption of data
00701         QByteArray binaryStream;
00702         KCodecs::base64Decode(base64Stream, binaryStream);
00703         out->writeBlock(binaryStream, binaryStream.count());
00704     }
00705     else
00706     {
00707         // Unknown text format!
00708         kdDebug(30506) << "Write character stream: " << stackItem->fontName << endl;
00709         // We strip the white space in front to avoid white space before a XML declaration
00710         QCString strOut=stackItem->strTemp2.stripWhiteSpace().utf8();
00711         out->writeBlock(strOut,strOut.length());
00712     }
00713 
00714     return true;
00715 }
00716 
00717 // <m>
00718 static bool StartElementM(StackItem* stackItem, StackItem* /*stackCurrent*/,
00719     const QXmlAttributes& attributes)
00720 {
00721     // We do not assume when we are called or if we are or not a child of <metadata>
00722     stackItem->elementType=ElementTypeRealMetaData;
00723 
00724     QString strKey=attributes.value("key").stripWhiteSpace();
00725     kdDebug(30506) << "Metadata key: " << strKey << endl;
00726 
00727     if (strKey.isEmpty())
00728     {
00729         kdWarning(30506) << "Metadata has no key!" << endl;
00730         stackItem->elementType=ElementTypeIgnore;
00731         return true;
00732     }
00733 
00734     stackItem->strTemp1=strKey;        // Key
00735     stackItem->strTemp2=QString::null;  // Meta data
00736 
00737     return true;
00738 }
00739 
00740 static bool CharactersElementM (StackItem* stackItem, const QString & ch)
00741 {
00742     // As we have no guarantee to have the whole data in one call, we must store the data.
00743     stackItem->strTemp2+=ch;
00744     return true;
00745 }
00746 
00747 bool StructureParser::EndElementM (StackItem* stackItem)
00748 {
00749     if (!stackItem->elementType==ElementTypeRealData)
00750     {
00751         kdError(30506) << "Wrong element type!! Aborting! (in endElementM)" << endl;
00752         return false;
00753     }
00754 
00755     if (stackItem->strTemp1.isEmpty())
00756     {
00757         // Probably an internal error!
00758         kdError(30506) << "Key name was erased! Aborting! (in endElementM)" << endl;
00759         return false;
00760     }
00761 
00762     // Just add it to the metadata map, we do not do something special with the values.
00763     m_metadataMap[stackItem->strTemp1]=stackItem->strTemp2;
00764 
00765     return true;
00766 }
00767 
00768 // <br> (forced line break)
00769 static bool StartElementBR(StackItem* stackItem, StackItem* stackCurrent,
00770     QDomDocument& mainDocument)
00771 {
00772     // <br> elements are mostly in <c> but can also be in <p>
00773     if ((stackCurrent->elementType==ElementTypeParagraph)
00774         || (stackCurrent->elementType==ElementTypeContent))
00775     {
00776         stackItem->elementType=ElementTypeEmpty;
00777 
00778         // Now work on stackCurrent
00779 
00780         if  (stackCurrent->elementType==ElementTypeContent)
00781         {
00782             // Child <c>, so we have to add formating of <c>
00783             QDomElement formatElement=mainDocument.createElement("FORMAT");
00784             formatElement.setAttribute("id",1); // Normal text!
00785             formatElement.setAttribute("pos",stackCurrent->pos); // Start position
00786             formatElement.setAttribute("len",1);
00787             AddFormat(formatElement, stackCurrent, mainDocument); // Add the format of the parent <c>
00788             stackCurrent->stackElementFormatsPlural.appendChild(formatElement); //Append to <FORMATS>
00789         }
00790 
00791         stackCurrent->stackElementText.appendChild(mainDocument.createTextNode(QChar(10))); // Add a LINE FEED
00792         stackCurrent->pos++; // Adjust position
00793 
00794     }
00795     else
00796     {//we are not nested correctly, so consider it a parse error!
00797         kdError(30506) << "parse error <br> tag not nested in <p> or <c> but in "
00798             << stackCurrent->itemName << endl;
00799         return false;
00800     }
00801     return true;
00802 }
00803 
00804 // <cbr> (forced column break, not supported)
00805 // <pbr> (forced page break)
00806 static bool StartElementPBR(StackItem* /*stackItem*/, StackItem* stackCurrent,
00807     QDomDocument& mainDocument)
00808 {
00809     // We are sure to be the child of a <p> element
00810 
00811     // The following code is similar to the one in StartElementP
00812     // We use mainFramesetElement here not to be dependant that <section> has happened before
00813     QDomElement paragraphElementOut=mainDocument.createElement("PARAGRAPH");
00814     stackCurrent->m_frameset.appendChild(paragraphElementOut);
00815     QDomElement textElementOut=mainDocument.createElement("TEXT");
00816     paragraphElementOut.appendChild(textElementOut);
00817     QDomElement formatsPluralElementOut=mainDocument.createElement("FORMATS");
00818     paragraphElementOut.appendChild(formatsPluralElementOut);
00819 
00820     // We must now copy/clone the layout of elementText.
00821 
00822     QDomNodeList nodeList=stackCurrent->stackElementParagraph.elementsByTagName("LAYOUT");
00823 
00824     if (!nodeList.count())
00825     {
00826         kdError(30506) << "Unable to find <LAYOUT> element! Aborting! (in StartElementPBR)" <<endl;
00827         return false;
00828     }
00829 
00830     // Now clone it
00831     QDomNode newNode=nodeList.item(0).cloneNode(true); // We make a deep cloning of the first element/node
00832     if (newNode.isNull())
00833     {
00834         kdError(30506) << "Unable to clone <LAYOUT> element! Aborting! (in StartElementPBR)" <<endl;
00835         return false;
00836     }
00837     paragraphElementOut.appendChild(newNode);
00838 
00839     // We need a page break!
00840     QDomElement oldLayoutElement=nodeList.item(0).toElement();
00841     if (oldLayoutElement.isNull())
00842     {
00843         kdError(30506) << "Cannot find old <LAYOUT> element! Aborting! (in StartElementPBR)" <<endl;
00844         return false;
00845     }
00846     // We have now to add a element <PAGEBREAKING>
00847     // TODO/FIXME: what if there is already one?
00848     QDomElement pagebreakingElement=mainDocument.createElement("PAGEBREAKING");
00849     pagebreakingElement.setAttribute("linesTogether","false");
00850     pagebreakingElement.setAttribute("hardFrameBreak","false");
00851     pagebreakingElement.setAttribute("hardFrameBreakAfter","true");
00852     oldLayoutElement.appendChild(pagebreakingElement);
00853 
00854     // Now that we have done with the old paragraph,
00855     //  we can write stackCurrent with the data of the new one!
00856     // NOTE: The following code is similar to the one in StartElementP but we are working on stackCurrent!
00857     stackCurrent->elementType=ElementTypeParagraph;
00858     stackCurrent->stackElementParagraph=paragraphElementOut; // <PARAGRAPH>
00859     stackCurrent->stackElementText=textElementOut; // <TEXT>
00860     stackCurrent->stackElementFormatsPlural=formatsPluralElementOut; // <FORMATS>
00861     stackCurrent->pos=0; // No text character yet
00862 
00863     return true;
00864 }
00865 
00866 // <pagesize>
00867 static bool StartElementPageSize(QDomElement& paperElement, const QXmlAttributes& attributes)
00868 {
00869     if (attributes.value("page-scale").toDouble()!=1.0)
00870     {
00871         kdWarning(30506) << "Ignoring unsupported page scale: " << attributes.value("page-scale") << endl;
00872     }
00873 
00874     int kwordOrientation;
00875     QString strOrientation=attributes.value("orientation").stripWhiteSpace();
00876 
00877     if (strOrientation=="portrait")
00878     {
00879         kwordOrientation=0;
00880     }
00881     else if (strOrientation=="landscape")
00882     {
00883         kwordOrientation=1;
00884     }
00885     else
00886     {
00887         kdWarning(30506) << "Unknown page orientation: " << strOrientation << "! Ignoring! " << endl;
00888         kwordOrientation=0;
00889     }
00890 
00891     double kwordHeight;
00892     double kwordWidth;
00893 
00894     QString strPageType=attributes.value("pagetype").stripWhiteSpace();
00895 
00896     // Do we know the page size or do we need to measure?
00897     // For page formats that KWord knows, use our own values in case the values in the file would be wrong.
00898 
00899     KoFormat kwordFormat = KoPageFormat::formatFromString(strPageType);
00900 
00901     if (kwordFormat==PG_CUSTOM)
00902     {
00903         kdDebug(30506) << "Custom or other page format found: " << strPageType << endl;
00904 
00905         double height = attributes.value("height").toDouble();
00906         double width  = attributes.value("width" ).toDouble();
00907 
00908         QString strUnits = attributes.value("units").stripWhiteSpace();
00909 
00910         kdDebug(30506) << "Explicit page size: "
00911          << height << " " << strUnits << " x " << width << " " << strUnits
00912          << endl;
00913 
00914         if (strUnits=="cm")
00915         {
00916             kwordHeight = CentimetresToPoints(height);
00917             kwordWidth  = CentimetresToPoints(width);
00918         }
00919         else if (strUnits=="inch")
00920         {
00921             kwordHeight = InchesToPoints(height);
00922             kwordWidth  = InchesToPoints(width);
00923         }
00924         else if (strUnits=="mm")
00925         {
00926             kwordHeight = MillimetresToPoints(height);
00927             kwordWidth  = MillimetresToPoints(width);
00928         }
00929         else
00930         {
00931             kwordHeight = 0.0;
00932             kwordWidth  = 0.0;
00933             kdWarning(30506) << "Unknown unit type: " << strUnits << endl;
00934         }
00935     }
00936     else
00937     {
00938         // We have a format known by KOffice, so use KOffice's functions
00939         kwordHeight = MillimetresToPoints(KoPageFormat::height(kwordFormat,PG_PORTRAIT));
00940         kwordWidth  = MillimetresToPoints(KoPageFormat::width (kwordFormat,PG_PORTRAIT));
00941     }
00942 
00943     if ((kwordHeight <= 1.0) || (kwordWidth <= 1.0))
00944         // At least one of the two values is ridiculous
00945     {
00946         kdWarning(30506) << "Page width or height is too small: "
00947          << kwordHeight << "x" << kwordWidth << endl;
00948         // As we have no correct page size, we assume we have A4
00949         kwordFormat = PG_DIN_A4;
00950         kwordHeight = CentimetresToPoints(29.7);
00951         kwordWidth  = CentimetresToPoints(21.0);
00952     }
00953 
00954     // Now that we have gathered all the page size data, put it in the right element!
00955 
00956     if (paperElement.isNull())
00957     {
00958         kdError(30506) << "<PAPER> element cannot be accessed! Aborting!" << endl;
00959         return false;
00960     }
00961 
00962     paperElement.setAttribute("format",kwordFormat);
00963     paperElement.setAttribute("width",kwordWidth);
00964     paperElement.setAttribute("height",kwordHeight);
00965     paperElement.setAttribute("orientation",kwordOrientation);
00966 
00967     return true;
00968 }
00969 
00970 
00971 bool StructureParser::complexForcedPageBreak(StackItem* stackItem)
00972 {
00973     // We are not a child of a <p> element, so we cannot use StartElementPBR directly
00974 
00975     StackItemStack auxilaryStack;
00976 
00977     if (!clearStackUntilParagraph(auxilaryStack))
00978     {
00979         kdError(30506) << "Could not clear stack until a paragraph!" << endl;
00980         return false;
00981     }
00982 
00983     // Now we are a child of a <p> element!
00984 
00985     bool success=StartElementPBR(stackItem,structureStack.current(),mainDocument);
00986 
00987     // Now restore the stack
00988 
00989     StackItem* stackCurrent=structureStack.current();
00990     StackItem* item;
00991     while (auxilaryStack.count()>0)
00992     {
00993         item=auxilaryStack.pop();
00994         // We cannot put back the item on the stack like that.
00995         // We must set a few values for each item.
00996         item->pos=0; // Start at position 0
00997         item->stackElementParagraph=stackCurrent->stackElementParagraph; // new <PARAGRAPH>
00998         item->stackElementText=stackCurrent->stackElementText; // new <TEXT>
00999         item->stackElementFormatsPlural=stackCurrent->stackElementFormatsPlural; // new <FORMATS>
01000         structureStack.push(item);
01001     }
01002 
01003     return success;
01004 }
01005 
01006 
01007 // <section>
01008 bool StructureParser::StartElementSection(StackItem* stackItem, StackItem* /*stackCurrent*/,
01009     const QXmlAttributes& attributes)
01010 {
01011     //TODO: non main text sections (e.g. footers)
01012     stackItem->elementType=ElementTypeSection;
01013 
01014     AbiPropsMap abiPropsMap;
01015     // Treat the props attributes in the two available flavors: lower case and upper case.
01016     kdDebug(30506)<< "========== props=\"" << attributes.value("props") << "\"" << endl;
01017     abiPropsMap.splitAndAddAbiProps(attributes.value("props"));
01018     abiPropsMap.splitAndAddAbiProps(attributes.value("PROPS")); // PROPS is deprecated
01019 
01020     // TODO: only the first main text section should change the page margins
01021     // TODO;   (as KWord does not allow different page sizes/margins for the same document)
01022     if (true && (!m_paperBordersElement.isNull()))
01023     {
01024         QString str;
01025         str=abiPropsMap["page-margin-top"].getValue();
01026         if (!str.isEmpty())
01027         {
01028             m_paperBordersElement.setAttribute("top",ValueWithLengthUnit(str));
01029         }
01030         str=abiPropsMap["page-margin-left"].getValue();
01031         if (!str.isEmpty())
01032         {
01033             m_paperBordersElement.setAttribute("left",ValueWithLengthUnit(str));
01034         }
01035         str=abiPropsMap["page-margin-bottom"].getValue();
01036         if (!str.isEmpty())
01037         {
01038             m_paperBordersElement.setAttribute("bottom",ValueWithLengthUnit(str));
01039         }
01040         str=abiPropsMap["page-margin-right"].getValue();
01041         if (!str.isEmpty())
01042         {
01043             m_paperBordersElement.setAttribute("right",ValueWithLengthUnit(str));
01044         }
01045     }
01046     return true;
01047 }
01048 
01049 // <iw>
01050 
01051 static bool EndElementIW(StackItem* stackItem, StackItem* /*stackCurrent*/,
01052     QDomDocument& mainDocument, QDomElement& m_ignoreWordsElement)
01053 {
01054     if (!stackItem->elementType==ElementTypeIgnoreWord)
01055     {
01056         kdError(30506) << "Wrong element type!! Aborting! (in endElementIW)" << endl;
01057         return false;
01058     }
01059     QDomElement wordElement=mainDocument.createElement("SPELLCHECKIGNOREWORD");
01060     wordElement.setAttribute("word",stackItem->strTemp2.stripWhiteSpace());
01061     m_ignoreWordsElement.appendChild(wordElement);
01062     return true;
01063 }
01064 
01065 // <foot>
01066 bool StructureParser::StartElementFoot(StackItem* stackItem, StackItem* /*stackCurrent*/,
01067     const QXmlAttributes& /*attributes*/)
01068 {
01069 #if 0
01070     stackItem->elementType=ElementTypeFoot;
01071 
01072     const QString id(attributes.value("endnote-id").stripWhiteSpace());
01073     kdDebug(30506) << "Foot note id: " << id << endl;
01074 
01075     if (id.isEmpty())
01076     {
01077         kdWarning(30506) << "Footnote has no id!" << endl;
01078         stackItem->elementType=ElementTypeIgnore;
01079         return true;
01080     }
01081 
01082     // We need to create a frameset for the foot note.
01083     QDomElement framesetElement(mainDocument.createElement("FRAMESET"));
01084     framesetElement.setAttribute("frameType",1);
01085     framesetElement.setAttribute("frameInfo",7);
01086     framesetElement.setAttribute("visible",1);
01087     framesetElement.setAttribute("name",getFootnoteFramesetName(id));
01088     framesetsPluralElement.appendChild(framesetElement);
01089 
01090     QDomElement frameElementOut(mainDocument.createElement("FRAME"));
01091     //frameElementOut.setAttribute("left",28);
01092     //frameElementOut.setAttribute("top",42);
01093     //frameElementOut.setAttribute("bottom",566);
01094     //frameElementOut.setAttribute("right",798);
01095     frameElementOut.setAttribute("runaround",1);
01096     // ### TODO: a few attributes are missing
01097     framesetElement.appendChild(frameElementOut);
01098 
01099     stackItem->m_frameset=framesetElement;
01100 #else
01101     stackItem->elementType=ElementTypeIgnore;
01102 #endif
01103     return true;
01104 }
01105 
01106 // Element <table>
01107 bool StructureParser::StartElementTable(StackItem* stackItem, StackItem* stackCurrent,
01108     const QXmlAttributes& attributes)
01109 {
01110 #if 1
01111     // In KWord, inline tables are inside a paragraph.
01112     // In AbiWord, tables are outside any paragraph.
01113 
01114     QStringList widthList;
01115     widthList.split('/', attributes.value("table-column-props"), false);
01116     const uint columns = widthList.size();
01117     stackItem->m_doubleArray.detach(); // Be sure not to modify parents
01118     stackItem->m_doubleArray.resize(columns+1); // All left positions but the last right one
01119     stackItem->m_doubleArray[0] = 0.0;
01120     QStringList::ConstIterator it;
01121     uint i;
01122     for ( i=0, it=widthList.begin(); i<columns; ++i, ++it )
01123     {
01124         kdDebug(30506) << "Column width: " << (*it) << " cooked " << ValueWithLengthUnit(*it) << endl;
01125         stackItem->m_doubleArray[i+1] = ValueWithLengthUnit(*it) + stackItem->m_doubleArray[i];
01126     }
01127     // ### TODO: in case of automatic column widths, we have not any width given by AbiWord
01128 
01129     const uint tableNumber(++m_tableGroupNumber);
01130     const QString tableName(i18n("Table %1").arg(tableNumber));
01131 
01132     QDomElement elementText=stackCurrent->stackElementText;
01133     QDomElement paragraphElementOut=mainDocument.createElement("PARAGRAPH");
01134     stackCurrent->m_frameset.appendChild(paragraphElementOut);
01135 
01136     QDomElement textElementOut(mainDocument.createElement("TEXT"));
01137     textElementOut.appendChild(mainDocument.createTextNode("#"));
01138     paragraphElementOut.appendChild(textElementOut);
01139 
01140     QDomElement formatsPluralElementOut=mainDocument.createElement("FORMATS");
01141     paragraphElementOut.appendChild(formatsPluralElementOut);
01142 
01143     QDomElement elementFormat(mainDocument.createElement("FORMAT"));
01144     elementFormat.setAttribute("id",6);
01145     elementFormat.setAttribute("pos",0);
01146     elementFormat.setAttribute("len",1);
01147     formatsPluralElementOut.appendChild(elementFormat);
01148 
01149     QDomElement elementAnchor(mainDocument.createElement("ANCHOR"));
01150     elementAnchor.setAttribute("type","frameset");
01151     elementAnchor.setAttribute("instance",tableName);
01152     elementFormat.appendChild(elementAnchor);
01153 
01154     stackItem->elementType=ElementTypeTable;
01155     stackItem->stackElementParagraph=paragraphElementOut; // <PARAGRAPH>
01156     stackItem->stackElementText=textElementOut; // <TEXT>
01157     stackItem->stackElementFormatsPlural=formatsPluralElementOut; // <FORMATS>
01158     stackItem->strTemp1=tableName;
01159     stackItem->strTemp2=QString::number(tableNumber); // needed as I18N does not allow adding phrases
01160     stackItem->pos=1; // Just #
01161 
01162     // Now we populate the layout
01163     QDomElement layoutElement=mainDocument.createElement("LAYOUT");
01164     paragraphElementOut.appendChild(layoutElement);
01165 
01166     AbiPropsMap abiPropsMap;
01167     styleDataMap.useOrCreateStyle("Normal"); // We might have to create the "Normal" style.
01168     AddLayout("Normal", layoutElement, stackItem, mainDocument, abiPropsMap, 0, false);
01169 #else
01170     stackItem->elementType=ElementTypeIgnore;
01171 #endif
01172     return true;
01173 }
01174 
01175 // <cell>
01176 bool StructureParser::StartElementCell(StackItem* stackItem, StackItem* stackCurrent,
01177     const QXmlAttributes& attributes)
01178 {
01179 #if 1
01180     if (stackCurrent->elementType!=ElementTypeTable)
01181     {
01182         kdError(30506) << "Wrong element type!! Aborting! (in StructureParser::endElementCell)" << endl;
01183         return false;
01184     }
01185 
01186     stackItem->elementType=ElementTypeCell;
01187 
01188     const QString tableName(stackCurrent->strTemp1);
01189     kdDebug(30506) << "Table name: " << tableName << endl;
01190 
01191     if (tableName.isEmpty())
01192     {
01193         kdError(30506) << "Table name is empty! Aborting!" << endl;
01194         return false;
01195     }
01196 
01197     AbiPropsMap abiPropsMap;
01198     abiPropsMap.splitAndAddAbiProps(attributes.value("props")); // Do not check PROPS
01199 
01200     // We abuse the attach number to know the row and col numbers.
01201     const uint row=abiPropsMap["top-attach"].getValue().toUInt();
01202     const uint col=abiPropsMap["left-attach"].getValue().toUInt();
01203 
01204     if ( col >= stackItem->m_doubleArray.size() )
01205     {
01206         // We do not know the right position of this column, so improvise. (### TODO)
01207         // We play on the fact that QByteArray uses shallow copies by default.
01208         //  (We do want that the change is known at <table> level)
01209         stackItem->m_doubleArray.resize( stackItem->m_doubleArray.size() + 1, QGArray::SpeedOptim );
01210         stackItem->m_doubleArray[col+1] = stackItem->m_doubleArray[col] + 72; // Try 1 inch
01211     }
01212 
01213     const QString frameName(i18n("Frameset name","Table %3, row %1, column %2")
01214         .arg(row).arg(col).arg(stackCurrent->strTemp2)); // As the stack could be wrong, be careful and use the string as last!
01215 
01216     // We need to create a frameset for the cell
01217     QDomElement framesetElement(mainDocument.createElement("FRAMESET"));
01218     framesetElement.setAttribute("frameType",1);
01219     framesetElement.setAttribute("frameInfo",0);
01220     framesetElement.setAttribute("visible",1);
01221     framesetElement.setAttribute("name",frameName);
01222     framesetElement.setAttribute("row",row);
01223     framesetElement.setAttribute("col",col);
01224     framesetElement.setAttribute("rows",1); // ### TODO: rowspan
01225     framesetElement.setAttribute("cols",1); // ### TODO: colspan
01226     framesetElement.setAttribute("grpMgr",tableName);
01227     framesetsPluralElement.appendChild(framesetElement);
01228 
01229     QDomElement frameElementOut(mainDocument.createElement("FRAME"));
01230     frameElementOut.setAttribute( "left", stackItem->m_doubleArray[col] );
01231     frameElementOut.setAttribute( "right", stackItem->m_doubleArray[col+1] );
01232     frameElementOut.setAttribute("top",0);
01233     frameElementOut.setAttribute("bottom",0);
01234     frameElementOut.setAttribute("runaround",1);
01235     frameElementOut.setAttribute("autoCreateNewFrame",0); // Very important for cell growing!
01236     // ### TODO: a few attributes are missing
01237     framesetElement.appendChild(frameElementOut);
01238 
01239     stackItem->m_frameset=framesetElement;
01240     QDomElement nullDummy;
01241     stackItem->stackElementParagraph=nullDummy; // <PARAGRAPH>
01242     stackItem->stackElementText=nullDummy; // <TEXT>
01243     stackItem->stackElementFormatsPlural=nullDummy; // <FORMATS>
01244 
01245 #else
01246     stackItem->elementType=ElementTypeIgnore;
01247 #endif
01248     return true;
01249 }
01250 
01251 // Parser for SAX2
01252 
01253 bool StructureParser :: startElement( const QString&, const QString&, const QString& name, const QXmlAttributes& attributes)
01254 {
01255     //Warning: be careful that some element names can be lower case or upper case (not very XML)
01256     kdDebug(30506) << indent << " <" << name << ">" << endl; //DEBUG
01257     indent += "*"; //DEBUG
01258 
01259     if (structureStack.isEmpty())
01260     {
01261         kdError(30506) << "Stack is empty!! Aborting! (in StructureParser::startElement)" << endl;
01262         return false;
01263     }
01264 
01265     // Create a new stack element copying the top of the stack.
01266     StackItem *stackItem=new StackItem(*structureStack.current());
01267 
01268     if (!stackItem)
01269     {
01270         kdError(30506) << "Could not create Stack Item! Aborting! (in StructureParser::startElement)" << endl;
01271         return false;
01272     }
01273 
01274     stackItem->itemName=name;
01275 
01276     bool success=false;
01277 
01278     if ((name=="c")||(name=="C"))
01279     {
01280         success=StartElementC(stackItem,structureStack.current(),attributes);
01281     }
01282     else if ((name=="p")||(name=="P"))
01283     {
01284         success=StartElementP(stackItem,structureStack.current(),mainDocument,
01285             styleDataMap,attributes);
01286     }
01287     else if ((name=="section")||(name=="SECTION"))
01288     {
01289         success=StartElementSection(stackItem,structureStack.current(),attributes);
01290     }
01291     else if (name=="a")
01292     {
01293         success=StartElementA(stackItem,structureStack.current(),attributes);
01294     }
01295     else if (name=="br") // NOTE: Not sure if it only exists in lower case!
01296     {
01297         // We have a forced line break
01298         StackItem* stackCurrent=structureStack.current();
01299         success=StartElementBR(stackItem,stackCurrent,mainDocument);
01300     }
01301     else if (name=="cbr") // NOTE: Not sure if it only exists in lower case!
01302     {
01303         // We have a forced column break (not supported by KWord)
01304         stackItem->elementType=ElementTypeEmpty;
01305         StackItem* stackCurrent=structureStack.current();
01306         if (stackCurrent->elementType==ElementTypeContent)
01307         {
01308             kdWarning(30506) << "Forced column break found! Transforming to forced page break" << endl;
01309             success=complexForcedPageBreak(stackItem);
01310         }
01311         else if (stackCurrent->elementType==ElementTypeParagraph)
01312         {
01313             kdWarning(30506) << "Forced column break found! Transforming to forced page break" << endl;
01314             success=StartElementPBR(stackItem,stackCurrent,mainDocument);
01315         }
01316         else
01317         {
01318             kdError(30506) << "Forced column break found out of turn! Aborting! Parent: "
01319                 << stackCurrent->itemName <<endl;
01320             success=false;
01321         }
01322     }
01323     else if (name=="pbr") // NOTE: Not sure if it only exists in lower case!
01324     {
01325         // We have a forced page break
01326         stackItem->elementType=ElementTypeEmpty;
01327         StackItem* stackCurrent=structureStack.current();
01328         if (stackCurrent->elementType==ElementTypeContent)
01329         {
01330             success=complexForcedPageBreak(stackItem);
01331         }
01332         else if (stackCurrent->elementType==ElementTypeParagraph)
01333         {
01334             success=StartElementPBR(stackItem,stackCurrent,mainDocument);
01335         }
01336         else
01337         {
01338             kdError(30506) << "Forced page break found out of turn! Aborting! Parent: "
01339                 << stackCurrent->itemName <<endl;
01340             success=false;
01341         }
01342     }
01343     else if (name=="pagesize")
01344         // Does only exist as lower case tag!
01345     {
01346         stackItem->elementType=ElementTypeEmpty;
01347         stackItem->stackElementText=structureStack.current()->stackElementText; // TODO: reason?
01348         success=StartElementPageSize(m_paperElement,attributes);
01349     }
01350     else if ((name=="field") //TODO: upper-case?
01351         || (name=="f")) // old deprecated name for <field>
01352     {
01353         success=StartElementField(stackItem,structureStack.current(),mainDocument,attributes);
01354     }
01355     else if (name=="s") // Seems only to exist as lower case
01356     {
01357         success=StartElementS(stackItem,structureStack.current(),attributes,styleDataMap);
01358     }
01359     else if ((name=="image") //TODO: upper-case?
01360         || (name=="i")) // old deprecated name for <image>
01361     {
01362         success=StartElementImage(stackItem,structureStack.current(),attributes);
01363     }
01364     else if (name=="d") // TODO: upper-case?
01365     {
01366         success=StartElementD(stackItem,structureStack.current(),attributes);
01367     }
01368     else if (name=="iw") // No upper-case
01369     {
01370         stackItem->elementType=ElementTypeIgnoreWord;
01371         success=true;
01372     }
01373     else if (name=="m") // No upper-case
01374     {
01375         success=StartElementM(stackItem,structureStack.current(),attributes);
01376     }
01377     else if (name=="foot") // No upper-case
01378     {
01379         success=StartElementFoot(stackItem,structureStack.current(),attributes);
01380     }
01381     else if (name=="table") // No upper-case
01382     {
01383         success=StartElementTable(stackItem,structureStack.current(), attributes);
01384     }
01385     else if (name=="cell") // No upper-case
01386     {
01387         success=StartElementCell(stackItem,structureStack.current(),attributes);
01388     }
01389     else
01390     {
01391         stackItem->elementType=ElementTypeUnknown;
01392         stackItem->stackElementText=structureStack.current()->stackElementText; // TODO: reason?
01393         success=true;
01394     }
01395     if (success)
01396     {
01397         structureStack.push(stackItem);
01398     }
01399     else
01400     {   // We have a problem so destroy our resources.
01401         delete stackItem;
01402     }
01403     return success;
01404 }
01405 
01406 bool StructureParser :: endElement( const QString&, const QString& , const QString& name)
01407 {
01408     indent.remove( 0, 1 ); // DEBUG
01409     kdDebug(30506) << indent << " </" << name << ">" << endl;
01410 
01411     if (structureStack.isEmpty())
01412     {
01413         kdError(30506) << "Stack is empty!! Aborting! (in StructureParser::endElement)" << endl;
01414         return false;
01415     }
01416 
01417     bool success=false;
01418 
01419     StackItem *stackItem=structureStack.pop();
01420     if ((name=="c")||(name=="C"))
01421     {
01422         success=EndElementC(stackItem,structureStack.current());
01423     }
01424     else if ((name=="p")||(name=="P"))
01425     {
01426         success=EndElementP(stackItem);
01427     }
01428     else if (name=="a")
01429     {
01430         if (stackItem->elementType==ElementTypeContent)
01431         {
01432             // Anchor to a bookmark (not supported by KWord))
01433             success=EndElementC(stackItem,structureStack.current());
01434         }
01435         else
01436         {
01437             // Normal anchor
01438             success=EndElementA(stackItem,structureStack.current(), mainDocument);
01439         }
01440     }
01441     else if (name=="d")
01442     {
01443         success=EndElementD(stackItem);
01444     }
01445     else if (name=="iw") // No upper-case
01446     {
01447         success=EndElementIW(stackItem,structureStack.current(), mainDocument, m_ignoreWordsElement);
01448     }
01449     else if (name=="m") // No upper-case
01450     {
01451         success=EndElementM(stackItem);
01452     }
01453     else
01454     {
01455         success=true; // No problem, so authorisation to continue parsing
01456     }
01457     if (!success)
01458     {
01459         // If we have no success, then it was surely a tag mismatch. Help debugging!
01460         kdError(30506) << "Found tag name: " << name
01461             << " expected: " << stackItem->itemName << endl;
01462     }
01463     delete stackItem;
01464     return success;
01465 }
01466 
01467 bool StructureParser :: characters ( const QString & ch )
01468 {
01469     // DEBUG start
01470     if (ch=="\n")
01471     {
01472         kdDebug(30506) << indent << " (LINEFEED)" << endl;
01473     }
01474     else if (ch.length()> 40)
01475     {   // 40 characters are enough (especially for image data)
01476         kdDebug(30506) << indent << " :" << ch.left(40) << "..." << endl;
01477     }
01478     else
01479     {
01480         kdDebug(30506) << indent << " :" << ch << ":" << endl;
01481     }
01482     // DEBUG end
01483     if (structureStack.isEmpty())
01484     {
01485         kdError(30506) << "Stack is empty!! Aborting! (in StructureParser::characters)" << endl;
01486         return false;
01487     }
01488 
01489     bool success=false;
01490 
01491     StackItem *stackItem=structureStack.current();
01492 
01493     if ((stackItem->elementType==ElementTypeContent)
01494         || (stackItem->elementType==ElementTypeAnchorContent))
01495     { // <c>
01496         success=charactersElementC(stackItem,mainDocument,ch);
01497     }
01498     else if (stackItem->elementType==ElementTypeParagraph)
01499     { // <p>
01500         success=charactersElementP(stackItem,mainDocument,ch);
01501     }
01502     else if (stackItem->elementType==ElementTypeAnchor)
01503     { // <a>
01504         success=charactersElementA(stackItem,ch);
01505     }
01506     else if (stackItem->elementType==ElementTypeEmpty)
01507     {
01508         success=ch.stripWhiteSpace().isEmpty();
01509         if (!success)
01510         {
01511             // We have a parsing error, so abort!
01512             kdError(30506) << "Empty element "<< stackItem->itemName
01513                 <<" is not empty! Aborting! (in StructureParser::characters)" << endl;
01514         }
01515     }
01516     else if (stackItem->elementType==ElementTypeRealData)
01517     {
01518         success=CharactersElementD(stackItem,mainDocument,ch);
01519     }
01520     else if (stackItem->elementType==ElementTypeIgnoreWord)
01521     {
01522         stackItem->strTemp2+=ch; // Just collect the data
01523         success=true;
01524     }
01525     else if (stackItem->elementType==ElementTypeRealMetaData)
01526     {
01527         success=CharactersElementM(stackItem,ch);
01528     }
01529     else
01530     {
01531         success=true;
01532     }
01533 
01534     return success;
01535 }
01536 
01537 bool StructureParser::startDocument(void)
01538 {
01539     indent = QString::null;  //DEBUG
01540     styleDataMap.defineDefaultStyles();
01541     return true;
01542 }
01543 
01544 void StructureParser::createDocInfo(void)
01545 {
01546     QDomImplementation implementation;
01547     QDomDocument doc(implementation.createDocumentType("document-info",
01548         "-//KDE//DTD document-info 1.2//EN", "http://www.koffice.org/DTD/document-info-1.2.dtd"));
01549 
01550     m_info=doc;
01551 
01552     m_info.appendChild(
01553         mainDocument.createProcessingInstruction(
01554         "xml","version=\"1.0\" encoding=\"UTF-8\""));
01555 
01556     QDomElement elementDoc(mainDocument.createElement("document-info"));
01557     elementDoc.setAttribute("xmlns","http://www.koffice.org/DTD/document-info");
01558     m_info.appendChild(elementDoc);
01559 
01560     QDomElement about(mainDocument.createElement("about"));
01561     elementDoc.appendChild(about);
01562 
01563     QDomElement abstract(mainDocument.createElement("abstract"));
01564     about.appendChild(abstract);
01565     abstract.appendChild(mainDocument.createTextNode(m_metadataMap["dc.description"]));
01566 
01567     QDomElement title(mainDocument.createElement("title"));
01568     about.appendChild(title);
01569     title.appendChild(mainDocument.createTextNode(m_metadataMap["dc.title"]));
01570 
01571     QDomElement keyword(mainDocument.createElement("keyword"));
01572     about.appendChild(keyword);
01573     keyword.appendChild(mainDocument.createTextNode(m_metadataMap["abiword.keywords"]));
01574 
01575     QDomElement subject(mainDocument.createElement("subject"));
01576     about.appendChild(subject);
01577     subject.appendChild(mainDocument.createTextNode(m_metadataMap["dc.subject"]));
01578 }
01579 
01580 bool StructureParser::endDocument(void)
01581 {
01582     QDomElement stylesPluralElement=mainDocument.createElement("STYLES");
01583     // insert before <PICTURES>, as <PICTURES> must remain last.
01584     mainDocument.documentElement().insertBefore(stylesPluralElement,m_picturesElement);
01585 
01586     kdDebug(30506) << "###### Start Style List ######" << endl;
01587     StyleDataMap::ConstIterator it;
01588 
01589     // At first, we put the Normal style
01590     it=styleDataMap.find("Normal");
01591     if (it!=styleDataMap.end())
01592     {
01593         kdDebug(30506) << "\"" << it.key() << "\" => " << it.data().m_props << endl;
01594         QDomElement styleElement=mainDocument.createElement("STYLE");
01595         stylesPluralElement.appendChild(styleElement);
01596         AddStyle(styleElement, it.key(),it.data(),mainDocument);
01597     }
01598     else
01599         kdWarning(30506) << "No 'Normal' style" << endl;
01600 
01601     for (it=styleDataMap.begin();it!=styleDataMap.end();++it)
01602     {
01603         if (it.key()=="Normal")
01604             continue;
01605 
01606         kdDebug(30506) << "\"" << it.key() << "\" => " << it.data().m_props << endl;
01607 
01608         QDomElement styleElement=mainDocument.createElement("STYLE");
01609         stylesPluralElement.appendChild(styleElement);
01610 
01611         AddStyle(styleElement, it.key(),it.data(),mainDocument);
01612     }
01613     kdDebug(30506) << "######  End Style List  ######" << endl;
01614 
01615     createDocInfo();
01616 
01617     return true;
01618 }
01619 
01620 bool StructureParser::warning(const QXmlParseException& exception)
01621 {
01622     kdWarning(30506) << "XML parsing warning: line " << exception.lineNumber()
01623         << " col " << exception.columnNumber() << " message: " << exception.message() << endl;
01624     return true;
01625 }
01626 
01627 bool StructureParser::error(const QXmlParseException& exception)
01628 {
01629     // A XML error is recoverable, so it is only a KDE warning
01630     kdWarning(30506) << "XML parsing error: line " << exception.lineNumber()
01631         << " col " << exception.columnNumber() << " message: " << exception.message() << endl;
01632     return true;
01633 }
01634 
01635 bool StructureParser::fatalError (const QXmlParseException& exception)
01636 {
01637     kdError(30506) << "XML parsing fatal error: line " << exception.lineNumber()
01638         << " col " << exception.columnNumber() << " message: " << exception.message() << endl;
01639     m_fatalerror=true;
01640     KMessageBox::error(NULL, i18n("An error has occurred while parsing the AbiWord file.\nAt line: %1, column %2\nError message: %3")
01641         .arg(exception.lineNumber()).arg(exception.columnNumber())
01642         .arg( i18n( "QXml", exception.message().utf8() ) ),
01643         i18n("AbiWord Import Filter"),0);
01644     return false; // Stop parsing now, we do not need further errors.
01645 }
01646 
01647 void StructureParser :: createDocument(void)
01648 {
01649     QDomImplementation implementation;
01650     QDomDocument doc(implementation.createDocumentType("DOC",
01651         "-//KDE//DTD kword 1.2//EN", "http://www.koffice.org/DTD/kword-1.2.dtd"));
01652 
01653     mainDocument=doc;
01654 
01655     mainDocument.appendChild(
01656         mainDocument.createProcessingInstruction(
01657         "xml","version=\"1.0\" encoding=\"UTF-8\""));
01658 
01659     QDomElement elementDoc;
01660     elementDoc=mainDocument.createElement("DOC");
01661     elementDoc.setAttribute("xmlns","http://www.koffice.org/DTD/kword");
01662     elementDoc.setAttribute("editor","AbiWord Import Filter");
01663     elementDoc.setAttribute("mime","application/x-kword");
01664     elementDoc.setAttribute( "syntaxVersion", 3 );
01665     mainDocument.appendChild(elementDoc);
01666 
01667     QDomElement element;
01668     element=mainDocument.createElement("ATTRIBUTES");
01669     element.setAttribute("processing",0);
01670     element.setAttribute("standardpage",1);
01671     element.setAttribute("hasHeader",0);
01672     element.setAttribute("hasFooter",0);
01673     //element.setAttribute("unit","mm"); // use KWord default instead
01674     element.setAttribute("tabStopValue",36); // AbiWord has a default of 0.5 inch tab stops
01675     elementDoc.appendChild(element);
01676 
01677     // <PAPER> will be partialy changed by an AbiWord <pagesize> element.
01678     // Default paper format of AbiWord is "Letter"
01679     m_paperElement=mainDocument.createElement("PAPER");
01680     m_paperElement.setAttribute("format",PG_US_LETTER);
01681     m_paperElement.setAttribute("width",MillimetresToPoints(KoPageFormat::width (PG_US_LETTER,PG_PORTRAIT)));
01682     m_paperElement.setAttribute("height",MillimetresToPoints(KoPageFormat::height(PG_US_LETTER,PG_PORTRAIT)));
01683     m_paperElement.setAttribute("orientation",PG_PORTRAIT);
01684     m_paperElement.setAttribute("columns",1);
01685     m_paperElement.setAttribute("columnspacing",2);
01686     m_paperElement.setAttribute("hType",0);
01687     m_paperElement.setAttribute("fType",0);
01688     m_paperElement.setAttribute("spHeadBody",9);
01689     m_paperElement.setAttribute("spFootBody",9);
01690     m_paperElement.setAttribute("zoom",100);
01691     elementDoc.appendChild(m_paperElement);
01692 
01693     m_paperBordersElement=mainDocument.createElement("PAPERBORDERS");
01694     m_paperBordersElement.setAttribute("left",28);
01695     m_paperBordersElement.setAttribute("top",42);
01696     m_paperBordersElement.setAttribute("right",28);
01697     m_paperBordersElement.setAttribute("bottom",42);
01698     m_paperElement.appendChild(m_paperBordersElement);
01699 
01700     framesetsPluralElement=mainDocument.createElement("FRAMESETS");
01701     mainDocument.documentElement().appendChild(framesetsPluralElement);
01702 
01703     mainFramesetElement=mainDocument.createElement("FRAMESET");
01704     mainFramesetElement.setAttribute("frameType",1);
01705     mainFramesetElement.setAttribute("frameInfo",0);
01706     mainFramesetElement.setAttribute("visible",1);
01707     mainFramesetElement.setAttribute("name",i18n("Frameset name","Main Text Frameset"));
01708     framesetsPluralElement.appendChild(mainFramesetElement);
01709 
01710     QDomElement frameElementOut=mainDocument.createElement("FRAME");
01711     frameElementOut.setAttribute("left",28);
01712     frameElementOut.setAttribute("top",42);
01713     frameElementOut.setAttribute("bottom",566);
01714     frameElementOut.setAttribute("right",798);
01715     frameElementOut.setAttribute("runaround",1);
01716     // TODO: a few attributes are missing
01717     mainFramesetElement.appendChild(frameElementOut);
01718 
01719     // As we are manipulating the document, create a few particular elements
01720     m_ignoreWordsElement=mainDocument.createElement("SPELLCHECKIGNORELIST");
01721     mainDocument.documentElement().appendChild(m_ignoreWordsElement);
01722     m_picturesElement=mainDocument.createElement("PICTURES");
01723     mainDocument.documentElement().appendChild(m_picturesElement);
01724 }
01725 
01726 bool StructureParser::clearStackUntilParagraph(StackItemStack& auxilaryStack)
01727 {
01728     for (;;)
01729     {
01730         StackItem* item=structureStack.pop();
01731         switch (item->elementType)
01732         {
01733         case ElementTypeContent:
01734             {
01735                 // Push the item on the auxilary stack
01736                 auxilaryStack.push(item);
01737                 break;
01738             }
01739         case ElementTypeParagraph:
01740             {
01741                 // Push back the item on this stack and then stop loop
01742                 structureStack.push(item);
01743                 return true;
01744             }
01745         default:
01746             {
01747                 // Something has gone wrong!
01748                 kdError(30506) << "Cannot clear this element: "
01749                     << item->itemName << endl;
01750                 return false;
01751             }
01752         }
01753     }
01754 }
01755 
01756 ABIWORDImport::ABIWORDImport(KoFilter */*parent*/, const char */*name*/, const QStringList &) :
01757                      KoFilter() {
01758 }
01759 
01760 KoFilter::ConversionStatus ABIWORDImport::convert( const QCString& from, const QCString& to )
01761 {
01762     if ((to != "application/x-kword") || (from != "application/x-abiword"))
01763         return KoFilter::NotImplemented;
01764 
01765     kdDebug(30506)<<"AbiWord to KWord Import filter"<<endl;
01766 
01767     StructureParser handler(m_chain);
01768 
01769     //We arbitrarily decide that Qt can handle the encoding in which the file was written!!
01770     QXmlSimpleReader reader;
01771     reader.setContentHandler( &handler );
01772     reader.setErrorHandler( &handler );
01773 
01774     //Find the last extension
01775     QString strExt;
01776     QString fileIn = m_chain->inputFile();
01777     const int result=fileIn.findRev('.');
01778     if (result>=0)
01779     {
01780         strExt=fileIn.mid(result);
01781     }
01782 
01783     kdDebug(30506) << "File extension: -" << strExt << "-" << endl;
01784 
01785     QString strMime; // Mime type of the compressor (default: unknown)
01786 
01787     if ((strExt==".gz")||(strExt==".GZ")        //in case of .abw.gz (logical extension)
01788         ||(strExt==".zabw")||(strExt==".ZABW")) //in case of .zabw (extension used prioritary with AbiWord)
01789     {
01790         // Compressed with gzip
01791         strMime="application/x-gzip";
01792         kdDebug(30506) << "Compression: gzip" << endl;
01793     }
01794     else if ((strExt==".bz2")||(strExt==".BZ2") //in case of .abw.bz2 (logical extension)
01795         ||(strExt==".bzabw")||(strExt==".BZABW")) //in case of .bzabw (extension used prioritary with AbiWord)
01796     {
01797         // Compressed with bzip2
01798         strMime="application/x-bzip2";
01799         kdDebug(30506) << "Compression: bzip2" << endl;
01800     }
01801 
01802     QIODevice* in = KFilterDev::deviceForFile(fileIn,strMime);
01803 
01804     if ( !in )
01805     {
01806         kdError(30506) << "Cannot create device for uncompressing! Aborting!" << endl;
01807         return KoFilter::FileNotFound; // ### TODO: better error?
01808     }
01809 
01810     if (!in->open(IO_ReadOnly))
01811     {
01812         kdError(30506) << "Cannot open file for uncompressing! Aborting!" << endl;
01813         delete in;
01814         return KoFilter::FileNotFound;
01815     }
01816 
01817     QXmlInputSource source(in); // Read the file
01818 
01819     in->close();
01820 
01821     if (!reader.parse( source ))
01822     {
01823         kdError(30506) << "Import: Parsing unsuccessful. Aborting!" << endl;
01824         delete in;
01825         if (!handler.wasFatalError())
01826         {
01827             // As the parsing was stopped for something else than a fatal error, we have not yet get an error message. (Can it really happen?)
01828             KMessageBox::error(NULL, i18n("An error occurred during the load of the AbiWord file: %1").arg(from),
01829                 i18n("AbiWord Import Filter"),0);
01830         }
01831         return KoFilter::ParsingError;
01832     }
01833     delete in;
01834 
01835     QCString strOut;
01836     KoStoreDevice* out;
01837 
01838     kdDebug(30506) << "Creating documentinfo.xml" << endl;
01839     out=m_chain->storageFile( "documentinfo.xml", KoStore::Write );
01840     if(!out)
01841     {
01842         kdError(30506) << "AbiWord Import unable to open output file! (Documentinfo)" << endl;
01843         KMessageBox::error(NULL, i18n("Unable to save document information."),i18n("AbiWord Import Filter"),0);
01844         return KoFilter::StorageCreationError;
01845     }
01846 
01847     //Write the document information!
01848     strOut=handler.getDocInfo().toCString(); // UTF-8
01849     // WARNING: we cannot use KoStore::write(const QByteArray&) because it writes an extra NULL character at the end.
01850     out->writeBlock(strOut,strOut.length());
01851 
01852     kdDebug(30506) << "Creating maindoc.xml" << endl;
01853     out=m_chain->storageFile( "root", KoStore::Write );
01854     if(!out)
01855     {
01856         kdError(30506) << "AbiWord Import unable to open output file! (Root)" << endl;
01857         KMessageBox::error(NULL, i18n("Unable to save main document."),i18n("AbiWord Import Filter"),0);
01858         return KoFilter::StorageCreationError;
01859     }
01860 
01861     //Write the document!
01862     strOut=handler.getDocument().toCString(); // UTF-8
01863     // WARNING: we cannot use KoStore::write(const QByteArray&) because it writes an extra NULL character at the end.
01864     out->writeBlock(strOut,strOut.length());
01865 
01866 #if 0
01867     kdDebug(30506) << documentOut.toString();
01868 #endif
01869 
01870     kdDebug(30506) << "Now importing to KWord!" << endl;
01871 
01872     return KoFilter::OK;
01873 }
01874 
01875 #include "abiwordimport.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys