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