/* This file is part of the KDE project Copyright 2001, 2002, 2003, 2004 Nicolas GOUTTE 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 #ifdef HAVE_UNISTD_H #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ImportHelpers.h" #include "ImportFormatting.h" #include "ImportStyle.h" #include "ImportField.h" #include "abiwordimport.h" class ABIWORDImportFactory : KGenericFactory { public: ABIWORDImportFactory(void) : KGenericFactory ("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. ) are AbiWord's ones. // Tags in upper case (e.g. ) 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; // TQDomElement mainFramesetElement; // The main where the body text will be under. TQDomElement m_picturesElement; // TQDomElement m_paperElement; // TQDomElement m_paperBordersElement; // TQDomElement m_ignoreWordsElement; // 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 m_metadataMap; // Map for elements TQDateTime m_timepoint; // Date/time (for pictures) bool m_fatalerror; // Did a XML parsing fatal error happened? }; // Element bool StructureParser::StartElementC(StackItem* stackItem, StackItem* stackCurrent, const TQXmlAttributes& attributes) { // elements can be nested in

elements, in elements or in other elements // AbiWord does not nest elements in other elements, but explicitly allows external programs to do it! //

or (not child of ) 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; // stackItem->stackElementText=stackCurrent->stackElementText; // stackItem->stackElementFormatsPlural=stackCurrent->stackElementFormatsPlural; // stackItem->pos=stackCurrent->pos; //Propagate the position } // or when child of 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 tag nested neither in

nor in nor in but in " << stackCurrent->itemName << endl; return false; } return true; } bool charactersElementC (StackItem* stackItem, TQDomDocument& mainDocument, const TQString & ch) { if (stackItem->elementType==ElementTypeContent) { // Normal 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 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! ( in StructureParser::endElement)" << endl; return false; } return true; } // Element bool StructureParser::StartElementA(StackItem* stackItem, StackItem* stackCurrent, const TQXmlAttributes& attributes) { // elements can be nested in

elements if (stackCurrent->elementType==ElementTypeParagraph) { //AbiPropsMap abiPropsMap; //PopulateProperties(stackItem,TQString(),attributes,abiPropsMap,true); stackItem->elementType=ElementTypeAnchor; stackItem->stackElementParagraph=stackCurrent->stackElementParagraph; // stackItem->stackElementText=stackCurrent->stackElementText; // stackItem->stackElementFormatsPlural=stackCurrent->stackElementFormatsPlural; // 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 to bookmark: " << stackItem->strTemp1 << endl << " Processing like " << endl; // We have a reference to a bookmark. Therefore treat it as a normal content return StartElementC(stackItem, stackCurrent, attributes); } } else {//we are not nested correctly, so consider it a parse error! kdError(30506) << "parse error tag not a child of

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! ( 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 TQDomElement linkElement=mainDocument.createElement("LINK"); linkElement.setAttribute("hrefName",stackItem->strTemp1); linkElement.setAttribute("linkName",stackItem->strTemp2); variableElement.appendChild(linkElement); //Append to // Now work on stackCurrent stackCurrent->stackElementFormatsPlural.appendChild(formatElement); stackCurrent->pos++; //Propagate the position back to the parent element return true; } // Element

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; // stackItem->stackElementText=textElementOut; // stackItem->stackElementFormatsPlural=formatsPluralElementOut; // 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) { // element elements can be nested in

if (stackCurrent->elementType==ElementTypeParagraph) { TQString strType=attributes.value("type").stripWhiteSpace(); kdDebug(30506)<<" type:"<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 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 tag not nested in

but in " << stackCurrent->itemName << endl; return false; } return true; } // (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; } // bool StructureParser::StartElementImage(StackItem* stackItem, StackItem* stackCurrent, const TQXmlAttributes& attributes) { // elements can be nested in

or elements if ((stackCurrent->elementType!=ElementTypeParagraph) && (stackCurrent->elementType!=ElementTypeContent)) {//we are not nested correctly, so consider it a parse error! kdError(30506) << "parse error tag nested neither in

nor in 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 // 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; } // 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 // However, we assume that we are after all 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; } // 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 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; } //
(forced line break) static bool StartElementBR(StackItem* stackItem, StackItem* stackCurrent, TQDomDocument& mainDocument) { //
elements are mostly in but can also be in

if ((stackCurrent->elementType==ElementTypeParagraph) || (stackCurrent->elementType==ElementTypeContent)) { stackItem->elementType=ElementTypeEmpty; // Now work on stackCurrent if (stackCurrent->elementType==ElementTypeContent) { // Child , so we have to add formating of 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 stackCurrent->stackElementFormatsPlural.appendChild(formatElement); //Append to } 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
tag not nested in

or but in " << stackCurrent->itemName << endl; return false; } return true; } // (forced column break, not supported) // (forced page break) static bool StartElementPBR(StackItem* /*stackItem*/, StackItem* stackCurrent, TQDomDocument& mainDocument) { // We are sure to be the child of a

element // The following code is similar to the one in StartElementP // We use mainFramesetElement here not to be dependant that

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 element! Aborting! (in StartElementPBR)" < element! Aborting! (in StartElementPBR)" < element! Aborting! (in StartElementPBR)" < // 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; // stackCurrent->stackElementText=textElementOut; // stackCurrent->stackElementFormatsPlural=formatsPluralElementOut; // stackCurrent->pos=0; // No text character yet return true; } // 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) << " 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

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

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 item->stackElementText=stackCurrent->stackElementText; // new item->stackElementFormatsPlural=stackCurrent->stackElementFormatsPlural; // new structureStack.push(item); } return success; } //

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; } // 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; } // 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 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(); im_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; // stackItem->stackElementText=textElementOut; // stackItem->stackElementFormatsPlural=formatsPluralElementOut; // 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; } // 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
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; // stackItem->stackElementText=nullDummy; // stackItem->stackElementFormatsPlural=nullDummy; // #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 <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 <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 { 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 { 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 << " " << 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)) { // success=charactersElementC(stackItem,mainDocument,ch); } else if (stackItem->elementType==ElementTypeParagraph) { //

success=charactersElementP(stackItem,mainDocument,ch); } else if (stackItem->elementType==ElementTypeAnchor) { // 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 , as 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); // will be partialy changed by an AbiWord 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"<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"