diff options
author | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-01-20 01:29:50 +0000 |
---|---|---|
committer | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-01-20 01:29:50 +0000 |
commit | 8362bf63dea22bbf6736609b0f49c152f975eb63 (patch) | |
tree | 0eea3928e39e50fae91d4e68b21b1e6cbae25604 /filters/kword/abiword | |
download | koffice-8362bf63dea22bbf6736609b0f49c152f975eb63.tar.gz koffice-8362bf63dea22bbf6736609b0f49c152f975eb63.zip |
Added old abandoned KDE3 version of koffice
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/koffice@1077364 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'filters/kword/abiword')
20 files changed, 4973 insertions, 0 deletions
diff --git a/filters/kword/abiword/CHANGES b/filters/kword/abiword/CHANGES new file mode 100644 index 000000000..53dea7e91 --- /dev/null +++ b/filters/kword/abiword/CHANGES @@ -0,0 +1,9 @@ +=== Post-1.2 === +- import/export: support for <metadata> +- export: file format like AbiWord 1.1.2 +- export: fix bug when font name has an & +- export: table (just the structure, no colspan, no rowspan) + +=== KWord 1.4 Alpha === +- export: fix linespacing +- import subject/keyword in docinformation diff --git a/filters/kword/abiword/FileFormats.html b/filters/kword/abiword/FileFormats.html new file mode 100644 index 000000000..3b45df878 --- /dev/null +++ b/filters/kword/abiword/FileFormats.html @@ -0,0 +1,124 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "DTD/xhtml1-strict.dtd"> +<!-- $Id: FileFormats.html 428241 2005-06-23 14:07:40Z pino $ --> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> +<title>FileFormats.html</title> +<style type="text/css"> +BODY +{ + background-color: #FFFFFF +} +DT +{ + font-weight:bold; +} +SPAN.bold +{ + font-weight: bold; +} +/* The colors are PROVISORY */ +SPAN.tags +{ + color:red; +} +SPAN.doctype +{ + color:green; +} +SPAN.attribute +{ + color:blue; +} +</style> +</head> +<body> +<h1>1 Foreword</h1> +<p>AbiWord's file format has evolved with the time. The older file formats are +XML-like, the newer ones are in XML.</p> + +<p>The names given for each file format are <span class="bold">not</span> +official, it is just how I have named them.</p> + +<p>They might be also other variations of the file format than those described below.</p> + +<p>The CVS dates are the dates when I have tested and found out things. So any +feature described must have appeared earlier. The dates are in format year-month-day.</p> + +<h1>2 AbiWord's File Formats</h1> + +<p>Here are a few versions (from oldest to newest):</p> + +<dt>awml</dt> +<dd> +The "awml" format starts with a tag <span class="tags"><awml></span> +and has no other header before. Some tags have different names than in later +versions. +</dd> + +<dt> Anonymous</dt> +<dd> +The "anonymous" format is the same but the tag +<span class="tags"><awml></span> is now named +<span class="tags"><abiword></span>. Some of these files have upper case +tag and attribute names. +</dd> + +<dt>Numbered</dt> +<dd> +The "numbered" version has a special header embeded in XML comments and the +tag <span class="tags"><abiword></span> has now a +<span class="attribute">version</span> attribute with a version number as +parameter. +</dd> + +<dt>Unnumbered</dt> +<dd> +The "unnumbered" version has the version number of the +<span class="attribute">version</span> attribute with the value "unnumbered". +</dd> + +<dt>XML</dt> +<dd> +The "xml" version has a XML declaration ( +<span class="tags"><?xml</span> ) and but has still the special header after +the XML declaration. Note: the encoding (e.g. UTF-8) is not always given. +</dd> + +<dt>File Format 1.0</dt> +<dd> +The "fileformat 1.0" version: the <span class="tags"><abiword></span> +tag has an attribute <span class="attribute">fileformat</span> (set to 1.0) +</dd> + +<dt>Wrong Doc Type</dt> +<dd> +The "wrong doctype": a DOCTYPE was added with a wrong public type of: +<span class="doctype">"-//W3C//DTD ABW 1.0 Strict//EN"</span>. +(Wrong is the W3C part! W3C has nothing to do with AbiWord. AbiWord Bug #1882) +</dd> + +<dt>Correct Doc Type</dt> +<dd> +The "correct doctype": (AbiWord CVS 2001-08-21) with a DOCTYPE of: +<span class="doctype"><!DOCTYPE abw PUBLIC +"-//ABISOURCE//DTD ABW 1.0 Strict//EN" "http://www.abisource.com/awml.dtd"> +</span> +</dd> + +<dt>New Doc Type / Wrong Name Space Definition</dt> +<dd> +The "new doctype": (AbiWord CVS 2002-02-??) with a DOCTYPE of: +<span class="doctype"><!DOCTYPE abiword PUBLIC +"-//ABISOURCE//DTD AWML 1.0 Strict//EN" "http://www.abisource.com/awml.dtd"> +</span> +The name space definition is wrong because it uses <span class="attribute">xmlns:awml</span>. +However it is the default name space of the file so it should be defined by +<span class="attribute">xmlns</span>. The <span class="tags"><abiword></span> +tag has new attributes: <span class="attribute">version</span> and +<span class="attribute">styles</span>. +</dd> + +</body> +</html> diff --git a/filters/kword/abiword/ImportField.cc b/filters/kword/abiword/ImportField.cc new file mode 100644 index 000000000..bfcc5a8c2 --- /dev/null +++ b/filters/kword/abiword/ImportField.cc @@ -0,0 +1,216 @@ +/* This file is part of the KDE project + Copyright (C) 2002 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 <qstring.h> +#include <qdom.h> + +#include <klocale.h> + +#include "ImportField.h" + +QString getFootnoteFramesetName(const QString& id) +{ + return i18n("Frameset name","Footnote %1").arg(id); +} + +static void InsertTimeVariable(QDomDocument& mainDocument, + QDomElement& variableElement, QString strKey) +{ + QDomElement typeElement=mainDocument.createElement("TYPE"); + typeElement.setAttribute("key",strKey); + typeElement.setAttribute("type",2); // Time + typeElement.setAttribute("text","-"); // Dummy, we let KWord do the work! + variableElement.appendChild(typeElement); //Append to <VARIABLE> + QDomElement timeElement=mainDocument.createElement("TIME"); + // We cannot calculate the time, so default to midnight + timeElement.setAttribute("hour",0); + timeElement.setAttribute("minute",0); + timeElement.setAttribute("second",0); + timeElement.setAttribute("fix",0); // AbiWord's <field> is never fixed + variableElement.appendChild(timeElement); //Append to <VARIABLE> +} + +static void InsertDateVariable(QDomDocument& mainDocument, + QDomElement& variableElement, QString strKey) +{ + QDomElement typeElement=mainDocument.createElement("TYPE"); + typeElement.setAttribute("key",strKey); + typeElement.setAttribute("type",0); // date + typeElement.setAttribute("text","-"); // Just a dummy, KWord will do the work + variableElement.appendChild(typeElement); //Append to <VARIABLE> + QDomElement dateElement=mainDocument.createElement("DATE"); + // As we have no idea about the current date, use the *nix epoch 1970-01-01 + dateElement.setAttribute("year",1970); + dateElement.setAttribute("month",1); + dateElement.setAttribute("day",1); + dateElement.setAttribute("fix",0); // AbiWord's <field> is never fixed + variableElement.appendChild(dateElement); //Append to <VARIABLE> +} + +static bool ProcessTimeField(QDomDocument& mainDocument, + QDomElement& variableElement, QString strType) +// strType: AbiWord's type +{ + if (strType=="time") + { + InsertTimeVariable(mainDocument, variableElement, "TIMELocale"); + } + else if (strType=="time_miltime") + { + // AbiWord's military time is just the standard 24h time (with seconds) + InsertTimeVariable(mainDocument, variableElement, "TIMEhh:mm:ss"); + } + else if (strType=="time_ampm") + { + InsertTimeVariable(mainDocument, variableElement, "TIMEam"); + } + else + { + // time_zone: not supported due to KWord + // time_epoch: not supported due to KWord (%Z) + return false; + } + return true; +} + +static bool ProcessDateField(QDomDocument& mainDocument, + QDomElement& variableElement, QString strType) +// strType: AbiWord's type +// Help for the % formats: +// man date +// or +// info date +{ + if (strType=="date") + { + InsertDateVariable(mainDocument, variableElement, "DATE0dddd mmmm dd, yyyy"); + } + else if (strType=="date_mmddyy") + { + InsertDateVariable(mainDocument, variableElement, "DATE0mm/dd/yy"); + } + else if (strType=="date_ddmmyy") + { + InsertDateVariable(mainDocument, variableElement, "DATE0dd/mm/yy"); + } + else if (strType=="date_mdy") + { + InsertDateVariable(mainDocument, variableElement, "DATE0mmmm dd, yyyy"); + } + else if (strType=="date_mthdy") + { + InsertDateVariable(mainDocument, variableElement, "DATE0mmm dd, yyyy"); + } + else if (strType=="date_dfl") + { // Should be %c, but we cannot do time zones, so for now: Locale, no time! (TODO) + InsertDateVariable(mainDocument, variableElement, "DATE0Locale"); + } + else if (strType=="date_ntdfl") + { + InsertDateVariable(mainDocument, variableElement, "DATE0Locale"); + } + else if (strType=="date_wkday") + { + InsertDateVariable(mainDocument, variableElement, "DATE0dddd"); + } + else + { + // date_doy: not supported (%j) + return false; + } + return true; +} + +bool ProcessField(QDomDocument& mainDocument, + QDomElement& variableElement, QString strType, const QXmlAttributes& attributes) +{ + // In AbiWord: + // field names are in the file: src/text/fmt/xp/fp_Fields.h + // field contents are in the file: src/text/fmt/xp/fp_Run.cpp + + bool done=false; + if (strType.startsWith("time")) + { + done=ProcessTimeField(mainDocument, variableElement, strType); + } + else if (strType.startsWith("date")) + { + done=ProcessDateField(mainDocument, variableElement, strType); + } + else if ((strType=="page_number")||(strType=="page_count")) + { + QDomElement typeElement=mainDocument.createElement("TYPE"); + typeElement.setAttribute("key","NUMBER"); + typeElement.setAttribute("type",4); // page number/count + typeElement.setAttribute("text",1); // We cannot count the pages, so give a default value + variableElement.appendChild(typeElement); //Append to <VARIABLE> + QDomElement pgnumElement=mainDocument.createElement("PGNUM"); + pgnumElement.setAttribute("subtype",(strType=="page_count")?1:0); + pgnumElement.setAttribute("value",1); + variableElement.appendChild(pgnumElement); //Append to <VARIABLE> + done=true; + } + else if (strType=="file_name") + { + QDomElement typeElement=mainDocument.createElement("TYPE"); + typeElement.setAttribute("key","STRING"); + typeElement.setAttribute("type",8); + typeElement.setAttribute("text","?"); // TODO: do we need this information right now? + variableElement.appendChild(typeElement); //Append to <VARIABLE> + QDomElement fieldElement=mainDocument.createElement("FIELD"); + fieldElement.setAttribute("subtype",0); + fieldElement.setAttribute("value","?"); // Should be the same as the text attribute + variableElement.appendChild(fieldElement); //Append to <VARIABLE> + done=true; + } + else if (strType=="endnote_ref") + { + QDomElement typeElement=mainDocument.createElement("TYPE"); + typeElement.setAttribute("key","STRING"); + typeElement.setAttribute("type",11); + typeElement.setAttribute("text","?"); // ### TODO: do we need this information right now? + variableElement.appendChild(typeElement); //Append to <VARIABLE> + QDomElement element=mainDocument.createElement("FOOTNOTE"); + element.setAttribute("numberingtype","auto"); // ### TODO: support other types + element.setAttribute("notetype","footnote"); + QString reference(attributes.value("endnote-id").stripWhiteSpace()); + element.setAttribute("frameset", getFootnoteFramesetName(reference)); // ### TODO: better name + element.setAttribute("value","?"); // Should be the same as the text attribute + variableElement.appendChild(element); //Append to <VARIABLE> + done=true; + } + + // Not supported: + // app_ver + // app_id + // app_options + // app_target + // app_compiledate + // app_compiletime + // list_label + // word_count + // char_count + // line_count + // para_count + // nbsp_count + // page_ref + // ... + + return done; +} diff --git a/filters/kword/abiword/ImportField.h b/filters/kword/abiword/ImportField.h new file mode 100644 index 000000000..e357754d2 --- /dev/null +++ b/filters/kword/abiword/ImportField.h @@ -0,0 +1,31 @@ +/* This file is part of the KDE project + Copyright (C) 2001, 2002 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. +*/ + +#ifndef IMPORTFIELD_H +#define IMPORTFIELD_H + +#include <qxml.h> + +QString getFootnoteFramesetName(const QString& id); +bool ProcessField(QDomDocument& mainDocument, + QDomElement& variableElement, QString strType, const QXmlAttributes& attributes); + + +#endif /* IMPORTFIELD_H */ + diff --git a/filters/kword/abiword/ImportFormatting.cc b/filters/kword/abiword/ImportFormatting.cc new file mode 100644 index 000000000..950d41cc8 --- /dev/null +++ b/filters/kword/abiword/ImportFormatting.cc @@ -0,0 +1,421 @@ +/* This file is part of the KDE project + Copyright (C) 2001, 2002, 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 <kdebug.h> + +#include "ImportFormatting.h" +#include "ImportStyle.h" + +StackItem::StackItem() : fontSize(0), /* No explicit font size */ + pos(0), italic(false), bold(false), underline(false), strikeout(false), + textPosition(0) +{ +} + +StackItem::~StackItem() +{ +} + +void PopulateProperties(StackItem* stackItem, const QString& strStyleProps, + const QXmlAttributes& attributes, AbiPropsMap& abiPropsMap, + const bool allowInit) +// TODO: find a better name for this function +{ + if (allowInit) + { + // Initialize the QStrings with the previous values of the properties + // TODO: any others needed? + if (stackItem->italic) + { + abiPropsMap.setProperty("font-style","italic"); + } + if (stackItem->bold) + { + abiPropsMap.setProperty("font-weight","bold"); + } + + if (stackItem->underline) + { + abiPropsMap.setProperty("text-decoration","underline"); + } + else if (stackItem->strikeout) + { + abiPropsMap.setProperty("text-decoration","line-through"); + } + } + + // Style goes first + kdDebug(30506)<< "===== from style=\"" << strStyleProps << "\"" << endl; + abiPropsMap.splitAndAddAbiProps(strStyleProps); + // 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 + + stackItem->italic=(abiPropsMap["font-style"].getValue()=="italic"); + stackItem->bold=(abiPropsMap["font-weight"].getValue()=="bold"); + + QString strDecoration=abiPropsMap["text-decoration"].getValue(); + stackItem->underline=(strDecoration=="underline"); + stackItem->strikeout=(strDecoration=="line-through"); + + QString strTextPosition=abiPropsMap["text-position"].getValue(); + if (strTextPosition=="subscript") + { + stackItem->textPosition=1; + } + else if (strTextPosition=="superscript") + { + stackItem->textPosition=2; + } + else if (!strTextPosition.isEmpty()) + { + // we have any other new value, assume it means normal! + stackItem->textPosition=0; + } + + QString strColour=abiPropsMap["color"].getValue(); + if (!strColour.isEmpty()) + { + // The colour information is *not* lead by a hash (#) + stackItem->fgColor.setNamedColor("#"+strColour); + } + + QString strBackgroundTextColor=abiPropsMap["bgcolor"].getValue(); + if (strBackgroundTextColor=="transparent") + { + // KWord has no idea what transparency is, so we use white + stackItem->bgColor.setRgb(255,255,255); + } + else if(!strBackgroundTextColor.isEmpty()) + { + // The colour information is *not* lead by a hash (#) + stackItem->bgColor.setNamedColor("#"+strBackgroundTextColor); + } + + QString strFontSize=abiPropsMap["font-size"].getValue(); + if (!strFontSize.isEmpty()) + { + const int size=int(ValueWithLengthUnit(strFontSize)); + if (size>0) + { + stackItem->fontSize=size; + } + } + + QString strFontFamily=abiPropsMap["font-family"].getValue(); + if (!strFontFamily.isEmpty() && (strFontFamily!="(null)")) + { + // TODO: transform the font-family in a font that we have on the system on which KWord runs. + stackItem->fontName=strFontFamily; + } +} + +void AddFormat(QDomElement& formatElementOut, StackItem* stackItem, QDomDocument& mainDocument) +{ + QDomElement element; + if (!stackItem->fontName.isEmpty()) + { + element=mainDocument.createElement("FONT"); + element.setAttribute("name",stackItem->fontName); // Font name + formatElementOut.appendChild(element); //Append to <FORMAT> + } + + if (stackItem->fontSize>0) + { + element=mainDocument.createElement("SIZE"); + element.setAttribute("value",stackItem->fontSize); + formatElementOut.appendChild(element); //Append to <FORMAT> + } + + element=mainDocument.createElement("ITALIC"); + element.setAttribute("value",stackItem->italic?1:0); + formatElementOut.appendChild(element); //Append to <FORMAT> + + element=mainDocument.createElement("WEIGHT"); + element.setAttribute("value",stackItem->bold?75:50); + formatElementOut.appendChild(element); //Append to <FORMAT> + + element=mainDocument.createElement("UNDERLINE"); + element.setAttribute("value",stackItem->underline?1:0); + formatElementOut.appendChild(element); //Append to <FORMAT> + + element=mainDocument.createElement("STRIKEOUT"); + element.setAttribute("value",stackItem->strikeout?1:0); + formatElementOut.appendChild(element); //Append to <FORMAT> + + if ((stackItem->textPosition>=0) && (stackItem->textPosition<=2)) + { + element=mainDocument.createElement("VERTALIGN"); + element.setAttribute("value",stackItem->textPosition); + formatElementOut.appendChild(element); //Append to <FORMAT> + } + + if (stackItem->fgColor.isValid()) + { + element=mainDocument.createElement("COLOR"); + element.setAttribute("red", stackItem->fgColor.red()); + element.setAttribute("green",stackItem->fgColor.green()); + element.setAttribute("blue", stackItem->fgColor.blue()); + formatElementOut.appendChild(element); //Append to <FORMAT> + } + + if (stackItem->bgColor.isValid()) + { + element=mainDocument.createElement("TEXTBACKGROUNDCOLOR"); + element.setAttribute("red", stackItem->bgColor.red()); + element.setAttribute("green",stackItem->bgColor.green()); + element.setAttribute("blue", stackItem->bgColor.blue()); + formatElementOut.appendChild(element); //Append to <FORMAT> + } +} + +void AddLayout(const QString& strStyleName, QDomElement& layoutElement, + StackItem* stackItem, QDomDocument& mainDocument, + const AbiPropsMap& abiPropsMap, const int level, const bool isStyle) +{ + QDomElement element; + element=mainDocument.createElement("NAME"); + element.setAttribute("value",strStyleName); + layoutElement.appendChild(element); + + QString strFollowing=abiPropsMap["followedby"].getValue(); + QDomElement followingElement=mainDocument.createElement("FOLLOWING"); + followingElement.setAttribute("name",strFollowing); + if ((strFollowing.isEmpty()) + || (strFollowing=="Current Settings")) // "Current Settings" is only a pseudo-style! + { + // We have no idea what style follows + if (isStyle) + { + // We are a style, so we need a default + followingElement.setAttribute("name","Normal"); + layoutElement.appendChild(followingElement); + } + // Else: we are a layout, so we leave the work to KWord (from the style) + } + else + { + // Following style is defined + // TODO: we should be sure that this style is defined! + layoutElement.appendChild(followingElement); + } + + QString strFlow=abiPropsMap["text-align"].getValue(); + element=mainDocument.createElement("FLOW"); + if ((strFlow=="left") || (strFlow=="center") || (strFlow=="right") || (strFlow=="justify")) + { + element.setAttribute("align",strFlow); + } + else + { + element.setAttribute("align","left"); + } + layoutElement.appendChild(element); + + int kwordDepth; + int kwordNumberingType; + int kwordType; + QString kwordRightText; + // level is 1 based like AbiWord, any value lower than 1 means no level! + if ((level<=0) || (level>=15)) + { + kwordDepth=0; + kwordNumberingType=2; + kwordType=0; + } + else + { + kwordDepth=level-1; + kwordNumberingType=1; + kwordType=1; // PROVISORY + kwordRightText="."; + } + + element=mainDocument.createElement("COUNTER"); + element.setAttribute("type",kwordType); + element.setAttribute("depth",kwordDepth); + element.setAttribute("start",1); + element.setAttribute("numberingtype",kwordNumberingType); + element.setAttribute("lefttext",""); + element.setAttribute("righttext",kwordRightText); + element.setAttribute("bullet",64); + element.setAttribute("bulletfont","Symbol"); + element.setAttribute("customdef",""); + layoutElement.appendChild(element); + + QString strLeftMargin=abiPropsMap["margin-left"].getValue(); + QString strRightMargin=abiPropsMap["margin-right"].getValue(); + QString strTextIndent=abiPropsMap["text-indent"].getValue(); + + if ( !strLeftMargin.isEmpty() + || !strRightMargin.isEmpty() + || !strTextIndent.isEmpty() ) + { + element=mainDocument.createElement("INDENTS"); + if (!strLeftMargin.isEmpty()) + element.setAttribute("left",ValueWithLengthUnit(strLeftMargin)); + if (!strRightMargin.isEmpty()) + element.setAttribute("right",ValueWithLengthUnit(strRightMargin)); + if (!strTextIndent.isEmpty()) + element.setAttribute("first",ValueWithLengthUnit(strTextIndent)); + layoutElement.appendChild(element); + } + + QString strTopMargin=abiPropsMap["margin-top"].getValue(); + QString strBottomMargin=abiPropsMap["margin-bottom"].getValue(); + if (!strTopMargin.isEmpty() || !strBottomMargin.isEmpty() ) + { + element=mainDocument.createElement("OFFSETS"); + const double margin_top=ValueWithLengthUnit(strTopMargin); + const double margin_bottom=ValueWithLengthUnit(strBottomMargin); + // Zero is propably a valid value! + if (!strBottomMargin.isEmpty()) + element.setAttribute("after",margin_bottom); + if (!strTopMargin.isEmpty()) + element.setAttribute("before",margin_top); + layoutElement.appendChild(element); + } + + QString strLineHeight=abiPropsMap["line-height"].getValue(); + if(!strLineHeight.isEmpty()) + { + element=mainDocument.createElement("LINESPACING"); + double lineHeight; + // Do we have a unit symbol or not? + bool flag=false; + lineHeight=strLineHeight.toDouble(&flag); + + if (flag) + { + if ( lineHeight == 1.0 ) + { + element.setAttribute( "value", "single" ); + element.setAttribute( "type", "single" ); + } + else if (lineHeight==1.5) + { + element.setAttribute( "value", "oneandhalf" ); + element.setAttribute( "type", "oneandhalf" ); + } + else if (lineHeight==2.0) + { + element.setAttribute( "value", "double" ); + element.setAttribute( "type", "double" ); + } + else if ( lineHeight > 0.0 ) + { + element.setAttribute( "type", "multiple" ); + element.setAttribute( "spacingvalue", lineHeight ); + } + else + { + kdWarning(30506) << "Unsupported line height " << lineHeight << " (Ignoring !)" << endl; + } + } + else + { + // Something went wrong, so we assume that an unit is specified + bool atleast = false; + lineHeight = ValueWithLengthUnit( strLineHeight, &atleast ); + if (lineHeight>1.0) + { + if ( atleast ) + { + kdDebug(30506) << "at-least" << endl; + element.setAttribute( "type", "atleast" ); + } + else + { + element.setAttribute( "type", "exact" ); + } + + // We have a meaningful value, so use it! + //element.setAttribute( "value", lineHeight ); + element.setAttribute( "spacingvalue", lineHeight ); + } + } + layoutElement.appendChild(element); + } + + QString strTab=abiPropsMap["tabstops"].getValue(); + if(!strTab.isEmpty()) + { + QStringList listTab=QStringList::split(",",strTab); + for ( QStringList::Iterator it = listTab.begin(); it != listTab.end(); ++it ) + { + QStringList tab=QStringList::split("/",*it); + const QChar tabType=tab[1].at(0); + const QChar tabFilling=tab[1].at(1); // Might be empty in old AbiWord files + int type; + if (tabType=='L') // left + type=0; + else if (tabType=='C') // center + type=1; + else if (tabType=='R') // right + type=2; + else if(tabType=='D') // decimal + type=3; + else if(tabType=='B') // bar (unsupported by KWord) + type=0; + else + { + kdWarning(30506)<<"Unknown tabulator type: " << QString(tabType) << endl; + type=0; + } + int filling; + int width=72; // Any non-null value + if (tabFilling.isNull() || tabFilling=='0') // No filling + filling=0; + else if (tabFilling=='1') // dot + { + filling=1; + width=2; // TODO: which width? + } + else if (tabFilling=='3') // underline + filling=2; + else + filling=0; + element=mainDocument.createElement("TABULATOR"); + element.setAttribute("ptpos",ValueWithLengthUnit(tab[0])); + element.setAttribute("type",type); + element.setAttribute("filling",filling); + element.setAttribute("width",width); + layoutElement.appendChild(element); + } + } + + QDomElement formatElementOut=mainDocument.createElement("FORMAT"); + layoutElement.appendChild(formatElementOut); + + AddFormat(formatElementOut, stackItem, mainDocument); +} + +void AddStyle(QDomElement& styleElement, const QString& strStyleName, + const StyleData& styleData, QDomDocument& mainDocument) +{ + // NOTE; styleElement is <STYLE> (singular), not <STYLES> (plural) + + StackItem stackItem; + QXmlAttributes attributes; // This is just a dummy for reusing already existing functions (TODO) + AbiPropsMap abiPropsMap; + + PopulateProperties(&stackItem, styleData.m_props, attributes, abiPropsMap, false); + AddLayout(strStyleName, styleElement, &stackItem, mainDocument, abiPropsMap, styleData.m_level, true); +} diff --git a/filters/kword/abiword/ImportFormatting.h b/filters/kword/abiword/ImportFormatting.h new file mode 100644 index 000000000..e7f9e000d --- /dev/null +++ b/filters/kword/abiword/ImportFormatting.h @@ -0,0 +1,132 @@ +/* This file is part of the KDE project + Copyright (C) 2001, 2002, 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. +*/ + +#ifndef _IMPORT_FORMATTING_H +#define _IMPORT_FORMATTING_H + +#include <qptrstack.h> +#include <qstring.h> +#include <qcolor.h> +#include <qxml.h> +#include <qdom.h> + +#include "ImportHelpers.h" + +// Tags in lower case (e.g. <c>) are AbiWord's ones. +// Tags in upper case (e.g. <TEXT>) are KWord's ones. + +// Note: as we are not validating anything, we are quite tolerant about the file +// that we will read. + +/** + * States + * + * Tags that we do not care of: + * \<abiword\> (or \<awml\>), \<data\>, \<styles\>, \<ignorewords\>, \<lists\>, \<metadata\> + * + * Tags that we do not support (however KWord could): + * \<bookmark\>, \<l\> + * + * Tags that we cannot support (lack of support in KWord): + * N/A + * + * Properties that we do not or cannot support: + * page-margin-footer, page-margin-header, lang, font-stretch, keep-with-next... + */ +enum StackItemElementType{ + ElementTypeUnknown = 0, + ElementTypeBottom, ///< Bottom of the stack + ElementTypeIgnore, ///< Element is known but ignored + ElementTypeEmpty, ///< Element is empty (\<pagesize\>, \<s\>, \<image\>, \<field\>, \<br\>, \<cbr\>, \<pbr\>) + ElementTypeSection, ///< \<section\> + ElementTypeParagraph, ///< \<p\> + ElementTypeContent, ///< \<c\> (not child of \<a\>), also \<a\> if it points to a bookmark + ElementTypeRealData, ///< \<d\> + ElementTypeAnchor, ///< \<a\> + ElementTypeAnchorContent,///< \<c\> when child of \<a\> + ElementTypeIgnoreWord, ///< \<iw\> + ElementTypeRealMetaData,///< \<m\> + ElementTypeFoot, ///< \<foot\> + ElementTypeTable, ///< \<table\> + ElementTypeCell ///< \<cell\> +}; + + +class StackItem +{ +public: + StackItem(); + ~StackItem(); +public: + QString itemName; ///< Name of the tag (only for error purposes) + StackItemElementType elementType; + QDomElement m_frameset; ///< current \<FRAMESET\> + QDomElement stackElementParagraph; ///< \<PARAGRAPH\> + QDomElement stackElementText; ///< \<TEXT\> + QDomElement stackElementFormatsPlural; ///< \<FORMATS\> + QString fontName; ///< font name but for \<d\>: name + int fontSize; + int pos; ///< Position + bool italic; + bool bold; ///< bold but for \<d\>: is base64 coded? + bool underline; + bool strikeout; + QColor fgColor; + QColor bgColor; + int textPosition; ///< Normal (0), subscript(1), superscript (2) + QString strTemp1; /**< for \<d\>: mime type + * for \<a\>: link reference + * for \<m\>: key + * for \<table\>: KWord's table name + */ + QString strTemp2; /**< for \<d\>: collecting the data + * for \<a\>: link name + * for \<iw\>: collecting the data (i.e. word to ignore) + * for \<m\>: value of the meta data + * for \<table\>: Number of the table (needed as I18N does not allow adding phrases) + */ + /** + * for tables (\<table\>, \<cell\>): defines the widths of the columns. + * Be careful: we are using the shallow copy mechanism of Qt. Use QMemArray::detach when needed to avoid modifying the parents + */ + QMemArray<double> m_doubleArray; +}; + +class StackItemStack : public QPtrStack<StackItem> +{ +public: + StackItemStack(void) { } + virtual ~StackItemStack(void) { } +}; + +class StyleData; + +void PopulateProperties(StackItem* stackItem, const QString& strStyleProps, + const QXmlAttributes& attributes, AbiPropsMap& abiPropsMap, + const bool allowInit); +void AddFormat(QDomElement& formatElementOut, StackItem* stackItem, + QDomDocument& mainDocument); +void AddLayout(const QString& strStyleName, QDomElement& layoutElement, + StackItem* stackItem, QDomDocument& mainDocument, + const AbiPropsMap& abiPropsMap, const int level, const bool isStyle); +void AddStyle(QDomElement& styleElement, const QString& strStyleName, + const StyleData& styleData, QDomDocument& mainDocument); + + +#endif // _IMPORT_FORMATTING_H diff --git a/filters/kword/abiword/ImportHelpers.cc b/filters/kword/abiword/ImportHelpers.cc new file mode 100644 index 000000000..19b4bac46 --- /dev/null +++ b/filters/kword/abiword/ImportHelpers.cc @@ -0,0 +1,109 @@ +/* This file is part of the KDE project + Copyright (C) 2001 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 <qstringlist.h> +#include <qregexp.h> + +#include <kdebug.h> + +#include "ImportHelpers.h" + +bool AbiPropsMap::setProperty(const QString& newName, const QString& newValue) +{ + replace(newName,AbiProps(newValue)); + return true; +} + +// Treat the "props" attribute of AbiWord's tags and split it in separates names and values +void AbiPropsMap::splitAndAddAbiProps(const QString& strProps) +{ + if (strProps.isEmpty()) + return; + // Split the properties (we do not want empty ones) + QStringList list=QStringList::split(';',strProps,false); + QString name,value; + + QStringList::ConstIterator it; + QStringList::ConstIterator end(list.end()); + for (it=list.begin();it!=end;++it) + { + const int result=(*it).find(':'); + if (result==-1) + { + name=(*it); + value=QString::null; + kdWarning(30506) << "Property without value: " << name << endl; + } + else + { + name=(*it).left(result); + value=(*it).mid(result+1); + } + // kdDebug(30506) << "========== (Property :" << name.stripWhiteSpace()<< "=" << value.stripWhiteSpace() <<":)"<<endl; + // Now set the property + setProperty(name.stripWhiteSpace(),value.stripWhiteSpace()); + } +} + +double ValueWithLengthUnit( const QString& _str, bool* atleast ) +{ + if ( atleast ) + *atleast = false; + + double result; + // We search an unit (defined by a sequence of lower case characters), with possibly a + sign after it + QRegExp unitExp("([a-z]+)\\s*(\\+?)"); + const int pos=unitExp.search(_str); + if (pos==-1) + { + bool flag=false; + result=_str.toDouble(&flag); + if (!flag) + kdWarning(30506) << "Unknown value: " << _str << " (ValueWithLengthUnit)" << endl; + } + else + { + const double rawValue=_str.left(pos).toDouble(); + const QString strUnit ( unitExp.cap(1) ); + if (strUnit=="cm") + result=CentimetresToPoints(rawValue); + else if (strUnit=="in") + result=InchesToPoints(rawValue); + else if (strUnit=="mm") + result=MillimetresToPoints(rawValue); + else if (strUnit=="pt") + result=rawValue; + else if(strUnit=="pi") + result=PicaToPoints(rawValue); + else + { + kdWarning(30506) << "Value " << _str << " has non-supported unit: " + << strUnit << " (ValueWithLengthUnit)" << endl; + result=rawValue; + } + + if ( atleast ) + { + *atleast = ( unitExp.cap(2) == "+" ); + } + + // kdDebug(30506) << "Value: " << _str << " Unit: " << strUnit << " Result: " << result << endl; + } + return result; +} diff --git a/filters/kword/abiword/ImportHelpers.h b/filters/kword/abiword/ImportHelpers.h new file mode 100644 index 000000000..bc72c4ab3 --- /dev/null +++ b/filters/kword/abiword/ImportHelpers.h @@ -0,0 +1,85 @@ +/* This file is part of the KDE project + Copyright (C) 2001, 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. +*/ + +#ifndef _IMPORT_HELPERS_H +#define _IMPORT_HELPERS_H + +#include <qstring.h> +#include <qmap.h> + +/** + * \file ImportHelpers.h + * Helpers for the AbiWord import filter + * + * Rough rule for including code in this file: + * use nothing of QT except QMap, QValueList and QString + */ + +class AbiProps +{ +public: + AbiProps() {}; + AbiProps(QString newValue) : m_value(newValue) {}; + virtual ~AbiProps() {}; +public: + inline QString getValue(void) const { return m_value; } +private: + QString m_value; +}; + +class AbiPropsMap : public QMap<QString,AbiProps> +{ +public: + AbiPropsMap() {}; + virtual ~AbiPropsMap() {}; +public: + bool setProperty(const QString& newName, const QString& newValue); + void splitAndAddAbiProps(const QString& strProps); +}; + +inline double CentimetresToPoints(const double d) +{ + return d * 72.0 / 2.54; +} + +inline double MillimetresToPoints(const double d) +{ + return d * 72.0 / 25.4; +} + +inline double InchesToPoints(const double d) +{ + return d * 72.0; +} + +inline double PicaToPoints(const double d) +{ + // 1 pica = 12 pt + return d * 12.0; +} + +/** + * Transform a value with unit (for example "12cm") into a double + * @param _str the value as string + * @param atleast was there a + character after the unit to denote an "at-least" property + * @return the value as double (in points) + */ +double ValueWithLengthUnit( const QString& _str, bool* atleast = NULL ); + +#endif // _IMPORT_HELPERS_H diff --git a/filters/kword/abiword/ImportStyle.cc b/filters/kword/abiword/ImportStyle.cc new file mode 100644 index 000000000..e5d371de1 --- /dev/null +++ b/filters/kword/abiword/ImportStyle.cc @@ -0,0 +1,128 @@ +/* This file is part of the KDE project + Copyright (C) 2001 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 <qfontinfo.h> + +#include <kglobalsettings.h> +#include <kdebug.h> + +#include <KoGlobal.h> + +#include "ImportStyle.h" + +StyleData::StyleData(void): m_level(-1) +{ +} + +StyleDataMap::StyleDataMap(void) +{ +} + +QString StyleDataMap::getDefaultStyle(void) +{ + // We use QFontInfo, as it return real values + QFontInfo fontInfo(KoGlobal::defaultFont()); + QString strReturn; + + strReturn += "font-family:"; + strReturn += fontInfo.family(); // TODO: should be "Times New Roman" + strReturn += "; font-size: 12pt;"; + // Note: the last property must have a semi-colon! + + return strReturn; +} + +void StyleDataMap::defineNewStyleFromOld(const QString& strName, const QString& strOld, + const int level, const QString& strProps) +{ + if (strOld.isEmpty()) + { + defineNewStyle(strName,level,strProps); + return; + } + + StyleDataMap::Iterator it=find(strOld); + if (it==end()) + { + defineNewStyle(strName,level,strProps); + } + else + { + QString strAllProps=it.data().m_props; + strAllProps+=strProps; + defineNewStyle(strName,level,strAllProps); + } +} + + +void StyleDataMap::defineNewStyle(const QString& strName, const int level, + const QString& strProps) +{ + // Despite its name, this method can be called multiple times + // We must take care that KWord just gets it only one time. + StyleDataMap::Iterator it=find(strName); + if (it==end()) + { + // The style does not exist yet, so we must define it. + it=insert(strName,StyleData()); + } + StyleData& styleData=it.data(); + styleData.m_level=level; + styleData.m_props+=getDefaultStyle(); + if (!strProps.isEmpty()) + { + styleData.m_props+=strProps; + styleData.m_props+=";"; // Security if other properties are appended later + } +} + +StyleDataMap::Iterator StyleDataMap::useOrCreateStyle(const QString& strName) +{ + // We are using a style but we are not sure if it is defined + StyleDataMap::Iterator it=find(strName); + if (it==end()) + { + // The style is not yet defined! + StyleData data; + data.m_level=-1; + data.m_props=getDefaultStyle(); + it=insert(strName,data); + } + return it; +} + +void StyleDataMap::defineDefaultStyles(void) +{ + // Add a few of AbiWord predefined style sheets + // AbiWord file: src/text/ptbl/xp/pt_PT_Styles.cpp + defineNewStyle("Normal",-1,QString::null); + // TODO: font should be "Arial" + // TODO: "keep with next" + QString strHeading("font-weight: bold; margin-top: 22pt; margin-bottom: 3pt; "); + defineNewStyle("Heading 1",1,strHeading+"font-size: 17pt"); + defineNewStyle("Heading 2",2,strHeading+"font-size: 14pt"); + defineNewStyle("Heading 3",3,strHeading+"font-size: 12pt"); + defineNewStyle("Block Text",-1,"margin-left: 1in; margin-right: 1in; margin-bottom: 6pt"); + QFontInfo fixedInfo(KGlobalSettings::fixedFont()); + QString strPlainText=QString("font-family: %1") + .arg(fixedInfo.family()); // TODO: should be "Courier New" + kdDebug(30506) << "Plain Text: " << strPlainText << endl; + defineNewStyle("Plain Text",-1,strPlainText); + // TODO: all list and numbered types +} diff --git a/filters/kword/abiword/ImportStyle.h b/filters/kword/abiword/ImportStyle.h new file mode 100644 index 000000000..b812640ad --- /dev/null +++ b/filters/kword/abiword/ImportStyle.h @@ -0,0 +1,51 @@ +/* This file is part of the KDE project + Copyright (C) 2001 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. +*/ + +#ifndef _IMPORT_STYLE_H +#define _IMPORT_STYLE_H + +#include <qvaluelist.h> +#include <qstring.h> +#include <qmap.h> + +class StyleData +{ +public: + StyleData(void); +public: + int m_level; // Depth of heading (as defined by AbiWord!) + QString m_props; // properties +}; + +class StyleDataMap : public QMap<QString,StyleData> +{ +public: + StyleDataMap(void); +public: + void defineNewStyle(const QString& strName, const int level, const QString& strProps); + void defineNewStyleFromOld(const QString& strName, const QString& strOld, + const int level, const QString& strProps); + StyleDataMap::Iterator useOrCreateStyle(const QString& strName); + void defineDefaultStyles(void); +private: + QString getDefaultStyle(void); +}; + +#endif // _IMPORT_STYLE_H + diff --git a/filters/kword/abiword/Makefile.am b/filters/kword/abiword/Makefile.am new file mode 100644 index 000000000..0a0f1f824 --- /dev/null +++ b/filters/kword/abiword/Makefile.am @@ -0,0 +1,26 @@ +####### General stuff + +INCLUDES= -I$(srcdir) $(KOFFICE_INCLUDES) -I$(srcdir)/../libexport $(all_includes) + +libabiwordimport_la_LDFLAGS = $(all_libraries) -module -avoid-version -no-undefined +libabiwordimport_la_LIBADD = $(KOFFICE_LIBS) + +libabiwordexport_la_LDFLAGS = $(all_libraries) -module -avoid-version -no-undefined +libabiwordexport_la_LIBADD = $(KOFFICE_LIBS) ../libexport/libkwordexportfilters.la + +####### Files + +kde_module_LTLIBRARIES = libabiwordimport.la libabiwordexport.la + +libabiwordimport_la_SOURCES = abiwordimport.cc ImportHelpers.cc\ +ImportFormatting.cc ImportStyle.cc ImportField.cc + +libabiwordexport_la_SOURCES = abiwordexport.cc + +noinst_HEADERS = abiwordimport.h ImportHelpers.h ImportFormatting.h\ +ImportStyle.h ImportField.h abiwordexport.h + +METASOURCES = AUTO + +service_DATA = kword_abiword_import.desktop kword_abiword_export.desktop +servicedir = $(kde_servicesdir) diff --git a/filters/kword/abiword/NOTES b/filters/kword/abiword/NOTES new file mode 100644 index 000000000..bdca2d26f --- /dev/null +++ b/filters/kword/abiword/NOTES @@ -0,0 +1,34 @@ +1. Welcome + + Welcome to KWord's AbiWord import/export filter. + + For now, this file is just a storage for some technical notes. + +2. AbiWord's Multiple File Formats + + AbiWord's file format has evolved with the time. The older file formats are + XML-like, the newer ones are in XML. + + See the file FileFormats.html for an overview! + + The AbiWord filter for KWord *must* be able to read all those formats. A user + will not be happy if you tell him that his sub-version is not supported! + + However to make it easier all these formats differ mainly only in the header. + (Only the "awml" format has different names for some tags.) + + Additionally, some files ("anonymous" ones?) seems to have tags and attributes + in upper case. This must also be taken care of. + + Files in all those formats can be found in the source distribution of AbiWord + (www.abisource.com), see in AbiWord's source code under the sub-directory + test/wp and under its own sub-directories. + +3. A Few Problems + + AbiWord is still in development. Be careful that future file formats does not + contain potential problems. At least they should not crash the import filter. + + You must really look into AbiWord's source code, as you will have the surprise + to find that the documented file format (also in the source distribution) does + not tell everything. diff --git a/filters/kword/abiword/TODO b/filters/kword/abiword/TODO new file mode 100644 index 000000000..b3dcbffcf --- /dev/null +++ b/filters/kword/abiword/TODO @@ -0,0 +1 @@ +- substitution of fonts at import (also at export?) diff --git a/filters/kword/abiword/abiwordexport.cc b/filters/kword/abiword/abiwordexport.cc new file mode 100644 index 000000000..31733bd6e --- /dev/null +++ b/filters/kword/abiword/abiwordexport.cc @@ -0,0 +1,1261 @@ +/* This file is part of the KDE project + Copyright (C) 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. +*/ + +/* + This file is based on the old file: + /home/kde/koffice/filters/kword/ascii/asciiexport.cc + + The old file was copyrighted by + Copyright (C) 1998, 1999 Reginald Stadlbauer <reggie@kde.org> + Copyright (c) 2000 ID-PRO Deutschland GmbH. All rights reserved. + Contact: Wolf-Michael Bolle <Bolle@ID-PRO.de> + + The old file was licensed under the terms of the GNU Library General Public + License version 2. +*/ + +#include <qmap.h> +#include <qiodevice.h> +#include <qtextstream.h> +#include <qdom.h> + +#include <kdebug.h> +#include <kmdcodec.h> +#include <kfilterdev.h> +#include <kgenericfactory.h> +#include <kimageio.h> + +#include <KoPageLayout.h> +#include <KoFilterChain.h> +#include <KoPictureKey.h> + +#include <KWEFStructures.h> +#include <KWEFUtil.h> +#include <KWEFBaseWorker.h> +#include <KWEFKWordLeader.h> + +#include <abiwordexport.h> +#include <abiwordexport.moc> + +class ABIWORDExportFactory : KGenericFactory<ABIWORDExport, KoFilter> +{ +public: + ABIWORDExportFactory(void) : KGenericFactory<ABIWORDExport, KoFilter> ("kwordabiwordexport") + {} +protected: + virtual void setupTranslations( void ) + { + KGlobal::locale()->insertCatalogue( "kofficefilters" ); + } +}; + +K_EXPORT_COMPONENT_FACTORY( libabiwordexport, ABIWORDExportFactory() ) + +class StyleMap : public QMap<QString,LayoutData> +{ +public: + StyleMap(void) {} + ~StyleMap(void) {} +}; + +class AbiWordWorker : public KWEFBaseWorker +{ +public: + AbiWordWorker(void); + virtual ~AbiWordWorker(void) { delete m_streamOut; delete m_ioDevice; } +public: + virtual bool doOpenFile(const QString& filenameOut, const QString& to); + virtual bool doCloseFile(void); // Close file in normal conditions + virtual bool doOpenDocument(void); + virtual bool doCloseDocument(void); + virtual bool doFullParagraph(const QString& paraText, const LayoutData& layout, + const ValueListFormatData& paraFormatDataList); + virtual bool doOpenTextFrameSet(void); // AbiWord's <section> + virtual bool doCloseTextFrameSet(void); // AbiWord's </section> + virtual bool doFullPaperFormat(const int format, + const double width, const double height, const int orientation); // Calc AbiWord's <papersize> + virtual bool doFullPaperBorders (const double top, const double left, + const double bottom, const double right); // Like KWord's <PAPERBORDERS> + virtual bool doCloseHead(void); // Write <papersize> + virtual bool doOpenStyles(void); // AbiWord's <styles> + virtual bool doCloseStyles(void); // AbiWord's </styles> + virtual bool doFullDefineStyle(LayoutData& layout); // AbiWord's <s></s> + virtual bool doOpenSpellCheckIgnoreList (void); // AbiWord's <ignorewords> + virtual bool doCloseSpellCheckIgnoreList (void); // AbiWord's </ignorewords> + virtual bool doFullSpellCheckIgnoreWord (const QString& ignoreword); // AbiWord's <iw> + virtual bool doFullDocumentInfo(const KWEFDocumentInfo& docInfo); // AbiWord's <metadata> +private: + void processParagraphData (const QString& paraText, + const TextFormatting& formatLayout, + const ValueListFormatData& paraFormatDataList); + void processNormalText ( const QString& paraText, + const TextFormatting& formatLayout, + const FormatData& formatData); + void processVariable ( const QString& paraText, + const TextFormatting& formatLayout, + const FormatData& formatData); + void processAnchor ( const QString& paraText, + const TextFormatting& formatLayout, + const FormatData& formatData); + QString textFormatToAbiProps(const TextFormatting& formatOrigin, + const TextFormatting& formatData, const bool force) const; + QString layoutToCss(const LayoutData& layoutOrigin, + const LayoutData& layout, const bool force) const; + QString escapeAbiWordText(const QString& strText) const; + bool makeTable(const FrameAnchor& anchor); + bool makePicture(const FrameAnchor& anchor); + void writeAbiProps(const TextFormatting& formatLayout, const TextFormatting& format); + void writePictureData(const QString& koStoreName, const QString& keyName); + QString transformToTextDate(const QDateTime& dt); +private: + QIODevice* m_ioDevice; + QTextStream* m_streamOut; + QString m_pagesize; // Buffer for the <pagesize> tag + QMap<QString,KoPictureKey> m_mapPictureData; + StyleMap m_styleMap; + double m_paperBorderTop,m_paperBorderLeft,m_paperBorderBottom,m_paperBorderRight; + bool m_inIgnoreWords; // true if <ignorewords> has been written + KWEFDocumentInfo m_docInfo; // document information +}; + +AbiWordWorker::AbiWordWorker(void) : m_ioDevice(NULL), m_streamOut(NULL), + m_paperBorderTop(0.0),m_paperBorderLeft(0.0), + m_paperBorderBottom(0.0),m_paperBorderRight(0.0) +{ +} + +QString AbiWordWorker::escapeAbiWordText(const QString& strText) const +{ + // Escape quotes (needed in attributes) + // Escape apostrophs (allowed by XML) + return KWEFUtil::EscapeSgmlText(NULL,strText,true,true); +} + +bool AbiWordWorker::doOpenFile(const QString& filenameOut, const QString& ) +{ + kdDebug(30506) << "Opening file: " << filenameOut + << " (in AbiWordWorker::doOpenFile)" << endl; + //Find the last extension + QString strExt; + const int result=filenameOut.findRev('.'); + if (result>=0) + { + strExt=filenameOut.mid(result); + } + + QString strMimeType; // Mime type of the compressor + + 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 + strMimeType="application/x-gzip"; + } + 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 + strMimeType="application/x-bzip2"; + } + else + { + // No compression + strMimeType="text/plain"; + } + + kdDebug(30506) << "Compression: " << strMimeType << endl; + + m_ioDevice = KFilterDev::deviceForFile(filenameOut,strMimeType); + + if (!m_ioDevice) + { + kdError(30506) << "No output file! Aborting!" << endl; + return false; + } + + if ( !m_ioDevice->open (IO_WriteOnly) ) + { + kdError(30506) << "Unable to open output file! Aborting!" << endl; + return false; + } + + m_streamOut=new QTextStream(m_ioDevice); + + // We only export in UTF-8 (are there AbiWord ports that cannot read UTF-8? Be careful SVG uses UTF-8 too!) + m_streamOut->setEncoding( QTextStream::UnicodeUTF8 ); + return true; +} + +bool AbiWordWorker::doCloseFile(void) +{ + delete m_streamOut; + m_streamOut=NULL; + if (m_ioDevice) + m_ioDevice->close(); + return (m_ioDevice); +} + +bool AbiWordWorker::doOpenDocument(void) +{ + kdDebug(30506)<< "AbiWordWorker::doOpenDocument" << endl; + // Make the file header + + // First the XML header in UTF-8 version + // (AbiWord and QT handle UTF-8 well, so we stay with this encoding!) + *m_streamOut << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"; + + // NOTE: AbiWord CVS 2002-02-?? has a new DOCTYPE + *m_streamOut << "<!DOCTYPE abiword PUBLIC \"-//ABISOURCE//DTD AWML 1.0 Strict//EN\""; + *m_streamOut << " \"http://www.abisource.com/awml.dtd\">\n"; + + // First magic: "<abiword" + *m_streamOut << "<abiword"; + // AbiWord CVS 2002-02-23 defines a default namespace. + *m_streamOut << " xmlns=\"http://www.abisource.com/awml.dtd\""; + // As we do not use xmlns:awml, do we need to define it? + // *m_streamOut << " xmlns:awml=\"http://www.abisource.com/awml.dtd\""; + *m_streamOut << " xmlns:xlink=\"http://www.w3.org/1999/xlink\""; + // AbiWord CVS 2002-02-22 defines other namespaces, which we are not using. + // AbiWord CVS 2002-12-23 has no fileformat attribute anymore + // ### TODO: add document language and document direction of writing. + *m_streamOut << " xml:space=\"preserve\" version=\"1.1.2\" template=\"false\" styles=\"unlocked\">\n"; + // Second magic: "<!-- This file is an AbiWord document." + // TODO/FIXME: write as much spaces as AbiWord does for the following line. + *m_streamOut << "<!-- This file is an AbiWord document. -->\n"; + // We have chosen NOT to have the full comment header that AbiWord files normally have. + // ### TODO: perhaps we should add the comment: do not edit the file + *m_streamOut << "\n"; + + + return true; +} + +void AbiWordWorker::writePictureData(const QString& koStoreName, const QString& keyName) +{ + kdDebug(30506) << "AbiWordWorker::writeImageData" << endl; + + QByteArray image; + + QString strExtension(koStoreName.lower()); + const int result=koStoreName.findRev("."); + if (result>=0) + { + strExtension=koStoreName.mid(result+1); + } + + bool isImageLoaded=false; + + if (strExtension=="png") + { + isImageLoaded=loadSubFile(koStoreName,image); + } + else + { + // All other picture types must be converted to PNG + // (yes, even JPEG, SVG or WMF!) + isImageLoaded=loadAndConvertToImage(koStoreName,strExtension,"PNG",image); + } + + if (isImageLoaded) + { + *m_streamOut << "<d name=\"" << keyName << "\"" + << " base64=\"yes\"" + << " mime=\"image/png\">\n"; + + QCString base64=KCodecs::base64Encode(image,true); + + *m_streamOut << base64 << "\n"; // QCString is taken as Latin1 by QTextStream + + *m_streamOut << "</d>\n"; + } + else + { + kdWarning(30506) << "Unable to load picture: " << koStoreName << endl; + } +} + +bool AbiWordWorker::doCloseDocument(void) +{ + // Before writing the <data> element, + // we must be sure that we have data and that we can retrieve it. + + if (m_kwordLeader && !m_mapPictureData.isEmpty()) + { + *m_streamOut << "<data>\n"; + + QMap<QString,KoPictureKey>::ConstIterator it; + QMap<QString,KoPictureKey>::ConstIterator end(m_mapPictureData.end()); + // all images first + for (it=m_mapPictureData.begin(); it!=end; ++it) + { + // Warning: do not mix up KWord's key and the iterator's key! + writePictureData(it.key(),it.data().filename()); + } + + *m_streamOut << "</data>\n"; + } + + *m_streamOut << "</abiword>\n"; //Close the file for XML + return true; +} + +bool AbiWordWorker::doOpenTextFrameSet(void) +{ + *m_streamOut << "<section props=\""; + *m_streamOut << "page-margin-top: "; + *m_streamOut << m_paperBorderTop; + *m_streamOut << "pt; "; + *m_streamOut << "page-margin-left: "; + *m_streamOut << m_paperBorderLeft; + *m_streamOut << "pt; "; + *m_streamOut << "page-margin-bottom: "; + *m_streamOut << m_paperBorderBottom; + *m_streamOut << "pt; "; + *m_streamOut << "page-margin-right: "; + *m_streamOut << m_paperBorderRight; + *m_streamOut << "pt"; // Last one, so no semi-comma + *m_streamOut << "\">\n"; + return true; +} + +bool AbiWordWorker::doCloseTextFrameSet(void) +{ + *m_streamOut << "</section>\n"; + return true; +} + +bool AbiWordWorker::doOpenStyles(void) +{ + *m_streamOut << "<styles>\n"; + return true; +} + +bool AbiWordWorker::doCloseStyles(void) +{ + *m_streamOut << "</styles>\n"; + return true; +} + +QString AbiWordWorker::textFormatToAbiProps(const TextFormatting& formatOrigin, + const TextFormatting& formatData, const bool force) const +{ + // TODO: rename variable formatData + QString strElement; // TODO: rename this variable + + // Font name + QString fontName = formatData.fontName; + if ( !fontName.isEmpty() + && (force || (formatOrigin.fontName!=formatData.fontName))) + { + strElement+="font-family: "; + strElement+= escapeAbiWordText(fontName); // TODO: add alternative font names + strElement+="; "; + } + + if (force || (formatOrigin.italic!=formatData.italic)) + { + // Font style + strElement+="font-style: "; + if ( formatData.italic ) + { + strElement+="italic"; + } + else + { + strElement+="normal"; + } + strElement+="; "; + } + + if (force || ((formatOrigin.weight>=75)!=(formatData.weight>=75))) + { + strElement+="font-weight: "; + if ( formatData.weight >= 75 ) + { + strElement+="bold"; + } + else + { + strElement+="normal"; + } + strElement+="; "; + } + + if (force || (formatOrigin.fontSize!=formatData.fontSize)) + { + const int size=formatData.fontSize; + if (size>0) + { + // We use absolute font sizes. + strElement+="font-size: "; + strElement+=QString::number(size,10); + strElement+="pt; "; + } + } + + if (force || (formatOrigin.fgColor!=formatData.fgColor)) + { + if ( formatData.fgColor.isValid() ) + { + // Give colour + strElement+="color: "; + + // No leading # (unlike CSS2) + // We must have two hex digits for each colour channel! + const int red=formatData.fgColor.red(); + strElement += QString::number((red&0xf0)>>4,16); + strElement += QString::number(red&0x0f,16); + + const int green=formatData.fgColor.green(); + strElement += QString::number((green&0xf0)>>4,16); + strElement += QString::number(green&0x0f,16); + + const int blue=formatData.fgColor.blue(); + strElement += QString::number((blue&0xf0)>>4,16); + strElement += QString::number(blue&0x0f,16); + + strElement+="; "; + } + } + + if (force || (formatOrigin.bgColor!=formatData.bgColor)) + { + if ( formatData.bgColor.isValid() ) + { + // Give background colour + strElement+="bgcolor: "; + + // No leading # (unlike CSS2) + // We must have two hex digits for each colour channel! + const int red=formatData.bgColor.red(); + strElement += QString::number((red&0xf0)>>4,16); + strElement += QString::number(red&0x0f,16); + + const int green=formatData.bgColor.green(); + strElement += QString::number((green&0xf0)>>4,16); + strElement += QString::number(green&0x0f,16); + + const int blue=formatData.bgColor.blue(); + strElement += QString::number((blue&0xf0)>>4,16); + strElement += QString::number(blue&0x0f,16); + + strElement+="; "; + } + } + + if (force || (formatOrigin.underline!=formatData.underline) + || (formatOrigin.strikeout!=formatData.strikeout)) + { + strElement+="text-decoration: "; + if ( formatData.underline ) + { + strElement+="underline"; + } + else if ( formatData.strikeout ) + { + strElement+="line-through"; + } + else + { + strElement+="none"; + } + strElement+="; "; + } + + return strElement; +} + +bool AbiWordWorker::makeTable(const FrameAnchor& anchor) +{ +#if 0 + *m_streamOut << "</p>\n"; // Close previous paragraph ### TODO: do it correctly like for HTML + *m_streamOut << "<table>\n"; +#endif + + QValueList<TableCell>::ConstIterator itCell; + for (itCell=anchor.table.cellList.begin(); + itCell!=anchor.table.cellList.end(); itCell++) + { +#if 0 + // ### TODO: rowspan, colspan + + // AbiWord seems to work by attaching to the cell borders + *m_streamOut << "<cell props=\""; + *m_streamOut << "left-attach:" << (*itCell).col << "; "; + *m_streamOut << "right-attach:" << (*itCell).col + 1 << "; "; + *m_streamOut << "top-attach:" << (*itCell).row << "; "; + *m_streamOut << "bot-attach:" << (*itCell).row + 1; + *m_streamOut << "\">\n"; +#endif + if (!doFullAllParagraphs(*(*itCell).paraList)) + { + return false; + } +#if 0 + *m_streamOut << "</cell>\n"; +#endif + } +#if 0 + *m_streamOut << "</table>\n"; + *m_streamOut << "<p>\n"; // Re-open the "previous" paragraph ### TODO: do it correctly like for HTML +#endif + return true; +} + +bool AbiWordWorker::makePicture(const FrameAnchor& anchor) +{ + kdDebug(30506) << "New image/clipart: " << anchor.picture.koStoreName + << " , " << anchor.picture.key.toString() << endl; + + const double height=anchor.frame.bottom - anchor.frame.top; + const double width =anchor.frame.right - anchor.frame.left; + + // TODO: we are only using the filename, not the rest of the key + // TODO: (bad if there are two images of the same name, but of a different key) + *m_streamOut << "<image dataid=\"" << anchor.picture.key.filename() << "\""; + *m_streamOut << " props= \"height:" << height << "pt;width:" << width << "pt\""; + *m_streamOut << "/>"; // NO end of line! + // TODO: other props for image + + m_mapPictureData[anchor.picture.koStoreName]=anchor.picture.key; + + return true; +} + +void AbiWordWorker::writeAbiProps (const TextFormatting& formatLayout, const TextFormatting& format) +{ + QString abiprops=textFormatToAbiProps(formatLayout,format,false); + + // Erase the last semi-comma (as in CSS2, semi-commas only separate instructions and do not terminate them) + const int result=abiprops.findRev(";"); + + if (result>=0) + { + // Remove the last semi-comma and the space thereafter + abiprops.remove(result,2); + } + + if (!abiprops.isEmpty()) + { + *m_streamOut << " props=\"" << abiprops << "\""; + } +} + +void AbiWordWorker::processNormalText ( const QString ¶Text, + const TextFormatting& formatLayout, + const FormatData& formatData) +{ + // Retrieve text and escape it + QString partialText=escapeAbiWordText(paraText.mid(formatData.pos,formatData.len)); + + // Replace line feeds by line breaks + int pos; + while ((pos=partialText.find(QChar(10)))>-1) + { + partialText.replace(pos,1,"<br/>"); + } + + if (formatData.text.missing) + { + // It's just normal text, so we do not need a <c> element! + *m_streamOut << partialText; + } + else + { // Text with properties, so use a <c> element! + *m_streamOut << "<c"; + writeAbiProps(formatLayout,formatData.text); + *m_streamOut << ">" << partialText << "</c>"; + } +} + +void AbiWordWorker::processVariable ( const QString&, + const TextFormatting& formatLayout, + const FormatData& formatData) +{ + if (0==formatData.variable.m_type) + { + // As AbiWord's field is inflexible, we cannot make the date custom + *m_streamOut << "<field type=\"date_ntdfl\""; + writeAbiProps(formatLayout,formatData.text); + *m_streamOut << "/>"; + } + else if (2==formatData.variable.m_type) + { + // As AbiWord's field is inflexible, we cannot make the time custom + *m_streamOut << "<field type=\"time\""; + writeAbiProps(formatLayout,formatData.text); + *m_streamOut << "/>"; + } + else if (4==formatData.variable.m_type) + { + // As AbiWord's field is inflexible, we cannot make the time custom + QString strFieldType; + if (formatData.variable.isPageNumber()) + { + strFieldType="page_number"; + } + else if (formatData.variable.isPageCount()) + { + strFieldType="page_count"; + } + if (strFieldType.isEmpty()) + { + // Unknown subtype, therefore write out the result + *m_streamOut << formatData.variable.m_text; + } + else + { + *m_streamOut << "<field type=\"" << strFieldType <<"\""; + writeAbiProps(formatLayout,formatData.text); + *m_streamOut << "/>"; + } + } + else if (9==formatData.variable.m_type) + { + // A link + *m_streamOut << "<a xlink:href=\"" + << escapeAbiWordText(formatData.variable.getHrefName()) + << "\"><c"; // In AbiWord, an anchor <a> has always a <c> child + writeAbiProps(formatLayout,formatData.text); + *m_streamOut << ">" + << escapeAbiWordText(formatData.variable.getLinkName()) + << "</c></a>"; + } +#if 0 + else if (11==(*paraFormatDataIt).variable.m_type) + { + // Footnote + QString value = (*paraFormatDataIt).variable.getFootnoteValue(); + bool automatic = (*paraFormatDataIt).variable.getFootnoteAuto(); + QValueList<ParaData> *paraList = (*paraFormatDataIt).variable.getFootnotePara(); + if( paraList ) + { + QString fstr; + QValueList<ParaData>::ConstIterator it; + for (it=paraList->begin();it!=paraList->end();it++) + fstr += ProcessParagraphData( (*it).text, (*it).layout,(*it).formattingList); + str += "{\\super "; + str += automatic ? "\\chftn " : value; + str += "{\\footnote "; + str += "{\\super "; + str += automatic ? "\\chftn " : value; + str += fstr; + str += " }"; + str += " }"; + str += " }"; + } + } +#endif + else + { + // Generic variable + *m_streamOut << formatData.variable.m_text; + } +} + +void AbiWordWorker::processAnchor ( const QString&, + const TextFormatting& /*formatLayout*/, //TODO + const FormatData& formatData) +{ + // We have an image or a table + if ( (2==formatData.frameAnchor.type) // <IMAGE> or <PICTURE> + || (5==formatData.frameAnchor.type) ) // <CLIPART> + { + makePicture(formatData.frameAnchor); + } + else if (6==formatData.frameAnchor.type) + { + makeTable(formatData.frameAnchor); + } + else + { + kdWarning(30506) << "Unsupported anchor type: " + << formatData.frameAnchor.type << endl; + } +} + +void AbiWordWorker::processParagraphData ( const QString ¶Text, + const TextFormatting& formatLayout, + const ValueListFormatData ¶FormatDataList) +{ + if ( paraText.length () > 0 ) + { + ValueListFormatData::ConstIterator paraFormatDataIt; + + for ( paraFormatDataIt = paraFormatDataList.begin (); + paraFormatDataIt != paraFormatDataList.end (); + paraFormatDataIt++ ) + { + if (1==(*paraFormatDataIt).id) + { + processNormalText(paraText, formatLayout, (*paraFormatDataIt)); + } + else if (4==(*paraFormatDataIt).id) + { + processVariable(paraText, formatLayout, (*paraFormatDataIt)); + } + else if (6==(*paraFormatDataIt).id) + { + processAnchor(paraText, formatLayout, (*paraFormatDataIt)); + } + } + } +} + +QString AbiWordWorker::layoutToCss(const LayoutData& layoutOrigin, + const LayoutData& layout, const bool force) const +{ + QString props; + + if (force || (layoutOrigin.alignment!=layout.alignment)) + { + // Check if the current alignment is a valid one for AbiWord. + if ((layout.alignment == "left") || (layout.alignment == "right") + || (layout.alignment == "center") || (layout.alignment == "justify")) + { + props += "text-align:"; + props += layout.alignment; + props += "; "; + } + else if (layout.alignment == "auto") + { + // We assume a left alignment as AbiWord is not really bi-di (and this filter even less.) + props += "text-align:left; "; + } + else + { + kdWarning(30506) << "Unknown alignment: " << layout.alignment << endl; + } + } + + // TODO/FIXME: what if all tabulators must be erased? +#if 0 + // DEPRECATED! + if (!layout.tabulator.isEmpty() + && (force || (layoutOrigin.tabulator!=layout.tabulator))) + { + props += "tabstops:"; + props += layout.tabulator; + props += "; "; + } +#endif + if (!layout.tabulatorList.isEmpty() + && (force || (layoutOrigin.tabulatorList!=layout.tabulatorList) )) + { + props += "tabstops:"; + bool first=true; + TabulatorList::ConstIterator it; + TabulatorList::ConstIterator end(layout.tabulatorList.end()); + for (it=layout.tabulatorList.begin();it!=end;++it) + { + if (first) + { + first=false; + } + else + { + props += ","; + } + props += QString::number((*it).m_ptpos); + props += "pt"; + + switch ((*it).m_type) + { + case 0: props += "/L"; break; + case 1: props += "/C"; break; + case 2: props += "/R"; break; + case 3: props += "/D"; break; + default: props += "/L"; + } + + props += "0"; // No filling + } + props += "; "; + } + + if ((layout.indentLeft>=0.0) + && (force || (layoutOrigin.indentLeft!=layout.indentLeft))) + { + props += QString("margin-left:%1pt; ").arg(layout.indentLeft); + } + + if ((layout.indentRight>=0.0) + && (force || (layoutOrigin.indentRight!=layout.indentRight))) + { + props += QString("margin-right:%1pt; ").arg(layout.indentRight); + } + + if (force || (layoutOrigin.indentLeft!=layout.indentLeft)) + { + props += "text-indent: "; + props += QString::number(layout.indentFirst); + props += "pt; "; + } + + if ((layout.marginBottom>=0.0) + && ( force || ( layoutOrigin.marginBottom != layout.marginBottom ) ) ) + { + props += QString("margin-bottom:%1pt; ").arg(layout.marginBottom); + } + + if ((layout.marginTop>=0.0) + && ( force || ( layoutOrigin.marginTop != layout.marginTop ) ) ) + { + props += QString("margin-top:%1pt; ").arg(layout.marginTop); + } + + if (force + || ( layoutOrigin.lineSpacingType != layout.lineSpacingType ) + || ( layoutOrigin.lineSpacing != layout.lineSpacing ) ) + { + switch ( layout.lineSpacingType ) + { + case LayoutData::LS_CUSTOM: + { + // We have a custom line spacing (in points). However AbiWord cannot do it, so transform in "at-least" + props += "line-height=:"; + props += QString::number( layout.lineSpacing ); // ### TODO: rounding? + props += "pt+; "; + break; + } + case LayoutData::LS_SINGLE: + { + props += "line-height:1.0; "; // One + break; + } + case LayoutData::LS_ONEANDHALF: + { + props += "line-height:1.5; "; // One-and-half + break; + } + case LayoutData::LS_DOUBLE: + { + props += "line-height:2.0; "; // Two + break; + } + case LayoutData::LS_MULTIPLE: + { + props += "line-height:"; + props += QString::number( layout.lineSpacing ); // ### TODO: rounding? + props += "; "; + break; + } + case LayoutData::LS_FIXED: + { + // We have a fixed line height (in points) + props += "line-height:"; + props += QString::number( layout.lineSpacing ); // ### TODO: rounding? + props += "pt; "; + break; + } + case LayoutData::LS_ATLEAST: + { + // We have an "at-least" line height (in points) + props += "line-height=:"; + props += QString::number( layout.lineSpacing ); // ### TODO: rounding? + props += "pt+; "; // The + makes the difference + break; + } + default: + { + kdWarning(30506) << "Unsupported lineSpacingType: " << layout.lineSpacingType << " (Ignoring!)" << endl; + break; + } + } + } + + // Add all AbiWord properties collected in the <FORMAT> element + props += textFormatToAbiProps(layoutOrigin.formatData.text,layout.formatData.text,force); + + return props; +} + +bool AbiWordWorker::doFullParagraph(const QString& paraText, const LayoutData& layout, + const ValueListFormatData& paraFormatDataList) +{ + QString style=layout.styleName; + + const LayoutData& styleLayout=m_styleMap[style]; + + QString props=layoutToCss(styleLayout,layout,false); + + *m_streamOut << "<p"; + if (!style.isEmpty()) + { + *m_streamOut << " style=\"" << EscapeXmlText(style,true,true) << "\""; + } + if (!props.isEmpty()) + { + // Find the last semi-comma + // Note: as in CSS2, semi-commas only separates instructions (like in PASCAL) and do not terminate them (like in C) + const int result=props.findRev(";"); + if (result>=0) + { + // Remove the last semi-comma and the space thereafter + props.remove(result,2); + } + + *m_streamOut << " props=\"" << props << "\""; + } + *m_streamOut << ">"; //Warning: No trailing white space or else it's in the text!!! + + // Before processing the text, test if we have a page break + if (layout.pageBreakBefore) + { + // We have a page break before the paragraph + *m_streamOut << "<pbr/>"; + } + + processParagraphData(paraText, layout.formatData.text, paraFormatDataList); + + // Before closing the paragraph, test if we have a page break + if (layout.pageBreakAfter) + { + // We have a page break after the paragraph + *m_streamOut << "<pbr/>"; + } + + *m_streamOut << "</p>\n"; + return true; +} + +bool AbiWordWorker::doFullDefineStyle(LayoutData& layout) +{ + //Register style in the style map + m_styleMap[layout.styleName]=layout; + + *m_streamOut << "<s"; + + // TODO: cook the style name to the standard style names in AbiWord + *m_streamOut << " name=\"" << EscapeXmlText(layout.styleName,true,true) << "\""; + *m_streamOut << " followedby=\"" << EscapeXmlText(layout.styleFollowing,true,true) << "\""; + + if ( (layout.counter.numbering == CounterData::NUM_CHAPTER) + && (layout.counter.depth<10) ) + { + *m_streamOut << " level=\""; + *m_streamOut << QString::number(layout.counter.depth+1,10); + *m_streamOut << "\""; + } + + QString abiprops=layoutToCss(layout,layout,true); + + const int result=abiprops.findRev(";"); + if (result>=0) + { + // Remove the last semi-comma and the space thereafter + abiprops.remove(result,2); + } + + *m_streamOut << " props=\"" << abiprops << "\""; + + *m_streamOut << "/>\n"; + + return true; +} + +bool AbiWordWorker::doFullPaperFormat(const int format, + const double width, const double height, const int orientation) +{ + QString outputText = "<pagesize "; + + switch (format) + { + // ISO A formats + case PG_DIN_A0: // ISO A0 + case PG_DIN_A1: // ISO A1 + case PG_DIN_A2: // ISO A2 + case PG_DIN_A3: // ISO A3 + case PG_DIN_A4: // ISO A4 + case PG_DIN_A5: // ISO A5 + case PG_DIN_A6: // ISO A6 + // ISO B formats + case PG_DIN_B0: // ISO B0 + case PG_DIN_B1: // ISO B1 + case PG_DIN_B2: // ISO B2 + case PG_DIN_B3: // ISO B3 + case PG_DIN_B4: // ISO B4 + case PG_DIN_B5: // ISO B5 + case PG_DIN_B6: // ISO B6 + // American formats + case PG_US_LETTER: // US Letter + case PG_US_LEGAL: // US Legal + { + QString pagetype=KoPageFormat::formatString(KoFormat(format)); + outputText+="pagetype=\""; + outputText+=pagetype; + + QString strWidth, strHeight, strUnits; + KWEFUtil::GetNativePaperFormat(format, strWidth, strHeight, strUnits); + outputText+="\" width=\""; + outputText+=strWidth; + outputText+="\" height=\""; + outputText+=strHeight; + outputText+="\" units=\""; + outputText+=strUnits; + outputText+="\" "; + break; + } + case PG_US_EXECUTIVE: // US Executive (does not exists in AbiWord!) + { + // FIXME/TODO: AbiWord (CVS 2001-04-25) seems not to like custom formats, so avoid them for now! +#if 0 + outputText += "pagetype=\"Custom\" width=\"7.5\" height=\"10.0\" units=\"inch\" "; +#else + // As replacement, use the slightly bigger "letter" format. + outputText += "pagetype=\"Letter\" width=\"8.5\" height=\"11.0\" units=\"inch\" "; +#endif + break; + } + // Other format not supported yet by AbiWord CVS 2001-04-25) + case PG_DIN_A7: // ISO A7 + case PG_DIN_A8: // ISO A8 + case PG_DIN_A9: // ISO A9 + case PG_DIN_B10: // ISO B10 + // Other formats + case PG_SCREEN: // Screen + case PG_CUSTOM: // Custom + default: + { + // FIXME/TODO: AbiWord (CVS 2001-04-25) seems not to like custom formats, so avoid them for now! + if ((width<=1.0) || (height<=1.0) || true) + { + // Height or width is ridiculous, so assume A4 format + outputText += "pagetype=\"A4\" width=\"21.0\" height=\"29.7\" units=\"cm\" "; + } + else + { // We prefer to use inches, as to limit rounding errors (page size is in points!) + outputText += QString("pagetype=\"Custom\" width=\"%1\" height=\"%2\" units=\"inch\" ").arg(width/72.0).arg(height/72.0); + } + break; + } + } + + outputText += "orientation=\""; + if (1==orientation) + { + outputText += "landscape"; + } + else + { + outputText += "portrait"; + } + outputText += "\" "; + + outputText += "page-scale=\"1.0\"/>\n"; // KWord has no page scale, so assume 100% + + m_pagesize=outputText; + return true; +} + +bool AbiWordWorker::doFullPaperBorders (const double top, const double left, + const double bottom, const double right) +{ + m_paperBorderTop=top; + m_paperBorderLeft=left; + m_paperBorderBottom=bottom; + m_paperBorderRight=right; + return true; +} + +bool AbiWordWorker::doCloseHead(void) +{ + if (!m_pagesize.isEmpty()) + { + *m_streamOut << m_pagesize; + } + return true; +} + +bool AbiWordWorker::doOpenSpellCheckIgnoreList (void) +{ + kdDebug(30506) << "AbiWordWorker::doOpenSpellCheckIgnoreList" << endl; + m_inIgnoreWords=false; // reset + return true; +} + +bool AbiWordWorker::doCloseSpellCheckIgnoreList (void) +{ + kdDebug(30506) << "AbiWordWorker::doCloseSpellCheckIgnoreList" << endl; + if (m_inIgnoreWords) + *m_streamOut << "</ignorewords>\n"; + return true; +} + +bool AbiWordWorker::doFullSpellCheckIgnoreWord (const QString& ignoreword) +{ + kdDebug(30506) << "AbiWordWorker::doFullSpellCheckIgnoreWord: " << ignoreword << endl; + if (!m_inIgnoreWords) + { + *m_streamOut << "<ignorewords>\n"; + m_inIgnoreWords=true; + } + *m_streamOut << " <iw>" << ignoreword << "</iw>\n"; + return true; +} + +// Similar to QDateTime::toString, but guaranteed *not* to be translated +QString AbiWordWorker::transformToTextDate(const QDateTime& dt) +{ + if (dt.isValid()) + { + QString result; + + const QDate date(dt.date()); + + const char* dayName[7] = { "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun" }; + const int dow = date.dayOfWeek() - 1; + if ((dow<0) || (dow>6)) + result += "Mon"; // Unknown day, rename it Monday. + else + result += dayName[dow]; + result += ' '; + + const char* monthName[12] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; + const int month = date.month() - 1; + if ((month<0) || (month>11)) + result += "Jan"; // Unknown month, rename it January + else + result += monthName[month]; + result += ' '; + + QString temp; + + temp = "00"; + temp += QString::number(date.day(), 10); + result += temp.right(2); + result += ' '; + + const QTime time(dt.time()); + + temp = "00"; + temp += QString::number(time.hour(), 10); + result += temp.right(2); + result += ':'; + + temp = "00"; + temp += QString::number(time.minute(), 10); + result += temp.right(2); + result += ':'; + + temp = "00"; + temp += QString::number(time.second(), 10); + result += temp.right(2); + result += ' '; + + temp = "0000"; + temp += QString::number(date.year(), 10); + result += temp.right(4); + + return result; + } + else + { + // Invalid, so give back 1970-01-01 + return "Thu Jan 01 00:00:00 1970"; + } +} + +bool AbiWordWorker::doFullDocumentInfo(const KWEFDocumentInfo& docInfo) +{ + m_docInfo=docInfo; + + *m_streamOut << "<metadata>\n"; + + // First all Dublin Core data + *m_streamOut << "<m key=\"dc.format\">application/x-abiword</m>\n"; + if (!m_docInfo.title.isEmpty()) + { + *m_streamOut << "<m key=\"dc.title\">" << escapeAbiWordText(m_docInfo.title) << "</m>\n"; + } + if (!m_docInfo.abstract.isEmpty()) + { + *m_streamOut << "<m key=\"dc.description\">" << escapeAbiWordText(m_docInfo.abstract) << "</m>\n"; + } + + if ( !m_docInfo.keywords.isEmpty() ) + { + *m_streamOut << "<m key=\"abiword.keywords\">" << escapeAbiWordText(m_docInfo.keywords) << "</m>\n"; + } + if ( !m_docInfo.subject.isEmpty() ) + { + *m_streamOut << "<m key=\"dc.subject\">" << escapeAbiWordText(m_docInfo.subject) << "</m>\n"; + } + + // Say who we are (with the CVS revision number) in case we have a bug in our filter output! + *m_streamOut << "<m key=\"abiword.generator\">KWord Export Filter"; + + QString strVersion("$Revision: 508787 $"); + // Remove the dollar signs + // (We don't want that the version number changes if the AbiWord file is itself put in a CVS storage.) + *m_streamOut << strVersion.mid(10).remove('$'); + + *m_streamOut << "</m>\n"; + + QDateTime now (QDateTime::currentDateTime(Qt::UTC)); // current time in UTC + *m_streamOut << "<m key=\"abiword.date_last_changed\">" + << escapeAbiWordText(transformToTextDate(now)) + << "</m>\n"; + + *m_streamOut << "</metadata>\n"; + + return true; +} + + +// ========================================================================================== + +ABIWORDExport::ABIWORDExport(KoFilter */*parent*/, const char */*name*/, const QStringList &) : + KoFilter() { +} + +KoFilter::ConversionStatus ABIWORDExport::convert( const QCString& from, const QCString& to ) +{ + if ( to != "application/x-abiword" || from != "application/x-kword" ) + { + return KoFilter::NotImplemented; + } + + // We need KimageIO's help in AbiWordWorker::convertUnknownImage + KImageIO::registerFormats(); + + AbiWordWorker* worker=new AbiWordWorker(); + + if (!worker) + { + kdError(30506) << "Cannot create Worker! Aborting!" << endl; + return KoFilter::StupidError; + } + + KWEFKWordLeader* leader=new KWEFKWordLeader(worker); + + if (!leader) + { + kdError(30506) << "Cannot create Worker! Aborting!" << endl; + delete worker; + return KoFilter::StupidError; + } + + KoFilter::ConversionStatus result=leader->convert(m_chain,from,to); + + delete leader; + delete worker; + + return result; +} diff --git a/filters/kword/abiword/abiwordexport.h b/filters/kword/abiword/abiwordexport.h new file mode 100644 index 000000000..a373f5e45 --- /dev/null +++ b/filters/kword/abiword/abiwordexport.h @@ -0,0 +1,51 @@ +/* + This file is part of the KDE project + Copyright (C) 2001 Nicolas GOUTTE <goutte@kde.org> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +/* + This file is based on the old file: + koffice/filters/kword/ascii/asciiexport.h + + The old file was copyrighted by + Copyright (C) 1998, 1999 Reginald Stadlbauer <reggie@kde.org> + + The old file was licensed under the terms of the GNU Library General Public + License version 2. +*/ + +#ifndef ABIWORDEXPORT_H +#define ABIWORDEXPORT_H + +#include <qstring.h> +#include <qcstring.h> + +#include <KoFilter.h> + + +class ABIWORDExport : public KoFilter { + + Q_OBJECT + +public: + ABIWORDExport(KoFilter *parent, const char *name, const QStringList &); + virtual ~ABIWORDExport() {} + + virtual KoFilter::ConversionStatus convert( const QCString& from, const QCString& to ); +}; +#endif // ABIWORDEXPORT_H diff --git a/filters/kword/abiword/abiwordimport.cc b/filters/kword/abiword/abiwordimport.cc new file mode 100644 index 000000000..b872e8750 --- /dev/null +++ b/filters/kword/abiword/abiwordimport.cc @@ -0,0 +1,1875 @@ +/* This file is part of the KDE project + Copyright 2001, 2002, 2003, 2004 Nicolas GOUTTE <goutte@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#include <config.h> + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +#include <qmap.h> +#include <qbuffer.h> +#include <qpicture.h> +#include <qxml.h> +#include <qdom.h> +#include <qdatetime.h> + +#include <kdebug.h> +#include <kmdcodec.h> +#include <kfilterdev.h> +#include <kgenericfactory.h> +#include <kmessagebox.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 ) + { + KGlobal::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 QXmlDefaultHandler +{ +public: + StructureParser(KoFilterChain* chain) + : m_chain(chain), m_pictureNumber(0), m_pictureFrameNumber(0), m_tableGroupNumber(0), + m_timepoint(QDateTime::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 QString&, const QString&, const QString& name, const QXmlAttributes& attributes); + virtual bool endElement( const QString&, const QString& , const QString& qName); + virtual bool characters ( const QString & ch ); + virtual bool warning(const QXmlParseException& exception); + virtual bool error(const QXmlParseException& exception); + virtual bool fatalError(const QXmlParseException& exception); +public: + inline QDomDocument getDocInfo(void) const { return m_info; } + inline QDomDocument 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 QXmlAttributes& attributes); + bool StartElementA(StackItem* stackItem, StackItem* stackCurrent, + const QXmlAttributes& attributes); + bool StartElementImage(StackItem* stackItem, StackItem* stackCurrent, + const QXmlAttributes& attributes); + bool EndElementD (StackItem* stackItem); + bool EndElementM (StackItem* stackItem); + bool StartElementSection(StackItem* stackItem, StackItem* stackCurrent, + const QXmlAttributes& attributes); + bool StartElementFoot(StackItem* stackItem, StackItem* stackCurrent, + const QXmlAttributes& attributes); + bool StartElementTable(StackItem* stackItem, StackItem* stackCurrent, const QXmlAttributes& attributes); + bool StartElementCell(StackItem* stackItem, StackItem* stackCurrent,const QXmlAttributes& attributes); +private: + void createDocument(void); + void createDocInfo(void); + QString indent; //DEBUG + StackItemStack structureStack; + QDomDocument mainDocument; + QDomDocument m_info; + QDomElement framesetsPluralElement; // <FRAMESETS> + QDomElement mainFramesetElement; // The main <FRAMESET> where the body text will be under. + QDomElement m_picturesElement; // <PICTURES> + QDomElement m_paperElement; // <PAPER> + QDomElement m_paperBordersElement; // <PAPERBORDER> + QDomElement 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 + QMap<QString,QString> m_metadataMap; // Map for <m> elements + QDateTime 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 QXmlAttributes& 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. + QString strStyleProps; + QString 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, QDomDocument& mainDocument, const QString & ch) +{ + if (stackItem->elementType==ElementTypeContent) + { // Normal <c> + QDomElement elementText=stackItem->stackElementText; + QDomElement elementFormatsPlural=stackItem->stackElementFormatsPlural; + elementText.appendChild(mainDocument.createTextNode(ch)); + + QDomElement 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 QXmlAttributes& attributes) +{ + // <a> elements can be nested in <p> elements + if (stackCurrent->elementType==ElementTypeParagraph) + { + + //AbiPropsMap abiPropsMap; + //PopulateProperties(stackItem,QString::null,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=QString::null; // 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 QString & ch) +{ + // Add characters to the link name + stackItem->strTemp2+=ch; + return true; +} + +static bool EndElementA (StackItem* stackItem, StackItem* stackCurrent, QDomDocument& mainDocument) +{ + if (!stackItem->elementType==ElementTypeAnchor) + { + kdError(30506) << "Wrong element type!! Aborting! (</a> in StructureParser::endElement)" << endl; + return false; + } + + QDomElement elementText=stackItem->stackElementText; + elementText.appendChild(mainDocument.createTextNode("#")); + + QDomElement formatElement=mainDocument.createElement("FORMAT"); + formatElement.setAttribute("id",4); // Variable + formatElement.setAttribute("pos",stackItem->pos); // Start position + formatElement.setAttribute("len",1); // Start position + + QDomElement variableElement=mainDocument.createElement("VARIABLE"); + formatElement.appendChild(variableElement); + + QDomElement typeElement=mainDocument.createElement("TYPE"); + typeElement.setAttribute("key","STRING"); + typeElement.setAttribute("type",9); // link + typeElement.setAttribute("text",stackItem->strTemp2); + variableElement.appendChild(typeElement); //Append to <VARIABLE> + + QDomElement 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, + QDomDocument& mainDocument, + StyleDataMap& styleDataMap, const QXmlAttributes& attributes) +{ + // We must prepare the style + QString strStyle=attributes.value("style"); + if (strStyle.isEmpty()) + { + strStyle="Normal"; + } + StyleDataMap::ConstIterator it=styleDataMap.useOrCreateStyle(strStyle); + + QString 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(); + } + + QDomElement elementText=stackCurrent->stackElementText; + QDomElement paragraphElementOut=mainDocument.createElement("PARAGRAPH"); + stackCurrent->m_frameset.appendChild(paragraphElementOut); + + QDomElement textElementOut=mainDocument.createElement("TEXT"); + paragraphElementOut.appendChild(textElementOut); + QDomElement 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 + QDomElement layoutElement=mainDocument.createElement("LAYOUT"); + paragraphElementOut.appendChild(layoutElement); + + AddLayout(strStyle,layoutElement, stackItem, mainDocument, abiPropsMap, level, false); + + return true; +} + +bool charactersElementP (StackItem* stackItem, QDomDocument& mainDocument, const QString & ch) +{ + QDomElement 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, + QDomDocument& mainDocument, const QXmlAttributes& attributes) +{ + // <field> element elements can be nested in <p> + if (stackCurrent->elementType==ElementTypeParagraph) + { + QString strType=attributes.value("type").stripWhiteSpace(); + kdDebug(30506)<<"<field> type:"<<strType<<endl; + + AbiPropsMap abiPropsMap; + PopulateProperties(stackItem,QString::null,attributes,abiPropsMap,true); + + stackItem->elementType=ElementTypeEmpty; + + // We create a format element + QDomElement 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; + QDomElement 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 + QDomElement 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 QXmlAttributes& 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; + + QString strStyleName=attributes.value("name").stripWhiteSpace(); + + if (strStyleName.isEmpty()) + { + kdWarning(30506) << "Style has no name!" << endl; + } + else + { + QString 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(); + QString 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 QXmlAttributes& 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; + + QString 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; + } + + QString strPictureFrameName(i18n("Frameset name","Picture %1").arg(++m_pictureFrameNumber)); + + // Create the frame set of the image + + QDomElement framesetElement=mainDocument.createElement("FRAMESET"); + framesetElement.setAttribute("frameType",2); + framesetElement.setAttribute("frameInfo",0); + framesetElement.setAttribute("visible",1); + framesetElement.setAttribute("name",strPictureFrameName); + framesetsPluralElement.appendChild(framesetElement); + + QDomElement 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); + + QDomElement element=mainDocument.createElement("PICTURE"); + element.setAttribute("keepAspectRatio","true"); + framesetElement.setAttribute("frameType",2); // Picture + framesetElement.appendChild(element); + + QDomElement 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 + QDomElement elementText=stackItem->stackElementText; + QDomElement elementFormatsPlural=stackItem->stackElementFormatsPlural; + elementText.appendChild(mainDocument.createTextNode("#")); + + QDomElement 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 + + QDomElement 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 QXmlAttributes& 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; + + QString strName=attributes.value("name").stripWhiteSpace(); + kdDebug(30506) << "Data: " << strName << endl; + + QString strBase64=attributes.value("base64").stripWhiteSpace(); + QString 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=QString::null; // Image data + + return true; +} + +static bool CharactersElementD (StackItem* stackItem, QDomDocument& /*mainDocument*/, const QString & 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 ? + + QString 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; + } + + QString strStoreName; + strStoreName="pictures/picture"; + strStoreName+=QString::number(++m_pictureNumber); + strStoreName+=extension; + + QString strDataId=stackItem->fontName; // AbiWord's data id + QDomElement 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 QString to QByteArray decoder! + QByteArray base64Stream=stackItem->strTemp2.utf8(); // Use utf8 to avoid corruption of data + QByteArray 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 + QCString strOut=stackItem->strTemp2.stripWhiteSpace().utf8(); + out->writeBlock(strOut,strOut.length()); + } + + return true; +} + +// <m> +static bool StartElementM(StackItem* stackItem, StackItem* /*stackCurrent*/, + const QXmlAttributes& attributes) +{ + // We do not assume when we are called or if we are or not a child of <metadata> + stackItem->elementType=ElementTypeRealMetaData; + + QString 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=QString::null; // Meta data + + return true; +} + +static bool CharactersElementM (StackItem* stackItem, const QString & 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, + QDomDocument& 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> + QDomElement 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(QChar(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, + QDomDocument& 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 + QDomElement paragraphElementOut=mainDocument.createElement("PARAGRAPH"); + stackCurrent->m_frameset.appendChild(paragraphElementOut); + QDomElement textElementOut=mainDocument.createElement("TEXT"); + paragraphElementOut.appendChild(textElementOut); + QDomElement formatsPluralElementOut=mainDocument.createElement("FORMATS"); + paragraphElementOut.appendChild(formatsPluralElementOut); + + // We must now copy/clone the layout of elementText. + + QDomNodeList nodeList=stackCurrent->stackElementParagraph.elementsByTagName("LAYOUT"); + + if (!nodeList.count()) + { + kdError(30506) << "Unable to find <LAYOUT> element! Aborting! (in StartElementPBR)" <<endl; + return false; + } + + // Now clone it + QDomNode 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! + QDomElement 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? + QDomElement 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(QDomElement& paperElement, const QXmlAttributes& attributes) +{ + if (attributes.value("page-scale").toDouble()!=1.0) + { + kdWarning(30506) << "Ignoring unsupported page scale: " << attributes.value("page-scale") << endl; + } + + int kwordOrientation; + QString 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; + + QString 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(); + + QString 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 QXmlAttributes& 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())) + { + QString 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*/, + QDomDocument& mainDocument, QDomElement& m_ignoreWordsElement) +{ + if (!stackItem->elementType==ElementTypeIgnoreWord) + { + kdError(30506) << "Wrong element type!! Aborting! (in endElementIW)" << endl; + return false; + } + QDomElement 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 QXmlAttributes& /*attributes*/) +{ +#if 0 + stackItem->elementType=ElementTypeFoot; + + const QString 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. + QDomElement framesetElement(mainDocument.createElement("FRAMESET")); + framesetElement.setAttribute("frameType",1); + framesetElement.setAttribute("frameInfo",7); + framesetElement.setAttribute("visible",1); + framesetElement.setAttribute("name",getFootnoteFramesetName(id)); + framesetsPluralElement.appendChild(framesetElement); + + QDomElement 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 QXmlAttributes& attributes) +{ +#if 1 + // In KWord, inline tables are inside a paragraph. + // In AbiWord, tables are outside any paragraph. + + QStringList 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; + QStringList::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 QString tableName(i18n("Table %1").arg(tableNumber)); + + QDomElement elementText=stackCurrent->stackElementText; + QDomElement paragraphElementOut=mainDocument.createElement("PARAGRAPH"); + stackCurrent->m_frameset.appendChild(paragraphElementOut); + + QDomElement textElementOut(mainDocument.createElement("TEXT")); + textElementOut.appendChild(mainDocument.createTextNode("#")); + paragraphElementOut.appendChild(textElementOut); + + QDomElement formatsPluralElementOut=mainDocument.createElement("FORMATS"); + paragraphElementOut.appendChild(formatsPluralElementOut); + + QDomElement elementFormat(mainDocument.createElement("FORMAT")); + elementFormat.setAttribute("id",6); + elementFormat.setAttribute("pos",0); + elementFormat.setAttribute("len",1); + formatsPluralElementOut.appendChild(elementFormat); + + QDomElement 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=QString::number(tableNumber); // needed as I18N does not allow adding phrases + stackItem->pos=1; // Just # + + // Now we populate the layout + QDomElement 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 QXmlAttributes& attributes) +{ +#if 1 + if (stackCurrent->elementType!=ElementTypeTable) + { + kdError(30506) << "Wrong element type!! Aborting! (in StructureParser::endElementCell)" << endl; + return false; + } + + stackItem->elementType=ElementTypeCell; + + const QString 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 QByteArray 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, QGArray::SpeedOptim ); + stackItem->m_doubleArray[col+1] = stackItem->m_doubleArray[col] + 72; // Try 1 inch + } + + const QString 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 + QDomElement 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); + + QDomElement 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; + QDomElement 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 QString&, const QString&, const QString& name, const QXmlAttributes& 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 QString&, const QString& , const QString& 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 QString & 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 = QString::null; //DEBUG + styleDataMap.defineDefaultStyles(); + return true; +} + +void StructureParser::createDocInfo(void) +{ + QDomImplementation implementation; + QDomDocument 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\"")); + + QDomElement elementDoc(mainDocument.createElement("document-info")); + elementDoc.setAttribute("xmlns","http://www.koffice.org/DTD/document-info"); + m_info.appendChild(elementDoc); + + QDomElement about(mainDocument.createElement("about")); + elementDoc.appendChild(about); + + QDomElement abstract(mainDocument.createElement("abstract")); + about.appendChild(abstract); + abstract.appendChild(mainDocument.createTextNode(m_metadataMap["dc.description"])); + + QDomElement title(mainDocument.createElement("title")); + about.appendChild(title); + title.appendChild(mainDocument.createTextNode(m_metadataMap["dc.title"])); + + QDomElement keyword(mainDocument.createElement("keyword")); + about.appendChild(keyword); + keyword.appendChild(mainDocument.createTextNode(m_metadataMap["abiword.keywords"])); + + QDomElement subject(mainDocument.createElement("subject")); + about.appendChild(subject); + subject.appendChild(mainDocument.createTextNode(m_metadataMap["dc.subject"])); +} + +bool StructureParser::endDocument(void) +{ + QDomElement 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; + QDomElement 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; + + QDomElement 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 QXmlParseException& exception) +{ + kdWarning(30506) << "XML parsing warning: line " << exception.lineNumber() + << " col " << exception.columnNumber() << " message: " << exception.message() << endl; + return true; +} + +bool StructureParser::error(const QXmlParseException& 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 QXmlParseException& 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( "QXml", exception.message().utf8() ) ), + i18n("AbiWord Import Filter"),0); + return false; // Stop parsing now, we do not need further errors. +} + +void StructureParser :: createDocument(void) +{ + QDomImplementation implementation; + QDomDocument 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\"")); + + QDomElement 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); + + QDomElement 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); + + QDomElement 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 QStringList &) : + KoFilter() { +} + +KoFilter::ConversionStatus ABIWORDImport::convert( const QCString& from, const QCString& 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 Qt can handle the encoding in which the file was written!! + QXmlSimpleReader reader; + reader.setContentHandler( &handler ); + reader.setErrorHandler( &handler ); + + //Find the last extension + QString strExt; + QString fileIn = m_chain->inputFile(); + const int result=fileIn.findRev('.'); + if (result>=0) + { + strExt=fileIn.mid(result); + } + + kdDebug(30506) << "File extension: -" << strExt << "-" << endl; + + QString 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; + } + + QIODevice* 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; + } + + QXmlInputSource 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), + i18n("AbiWord Import Filter"),0); + } + return KoFilter::ParsingError; + } + delete in; + + QCString 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 QByteArray&) 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 QByteArray&) 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" diff --git a/filters/kword/abiword/abiwordimport.h b/filters/kword/abiword/abiwordimport.h new file mode 100644 index 000000000..3ead0283f --- /dev/null +++ b/filters/kword/abiword/abiwordimport.h @@ -0,0 +1,54 @@ +/* + This file is part of the KDE project + Copyright (C) 2001 Nicolas GOUTTE <goutte@kde.org> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +/* + This file is based on the old file: + koffice/filters/kword/ascii/asciiimport.h + + The old file was copyrighted by + Copyright (C) 1998, 1999 Reginald Stadlbauer <reggie@kde.org> + Copyright (C) 2000 Michael Johnson <mikej@xnet.com> + + The old file was licensed under the terms of the GNU Library General Public + License version 2. +*/ + +#ifndef ABIWORDIMPORT_H +#define ABIWORDIMPORT_H + +#include <qstring.h> +#include <qfile.h> +#include <qobject.h> + +#include <KoFilter.h> +#include <KoStore.h> + +class ABIWORDImport : public KoFilter { + + Q_OBJECT + +public: + ABIWORDImport(KoFilter *parent, const char *name, const QStringList &); + ABIWORDImport(); + virtual ~ABIWORDImport() {} + + virtual KoFilter::ConversionStatus convert( const QCString& from, const QCString& to ); +}; +#endif // ABIWORDIMPORT_H diff --git a/filters/kword/abiword/kword_abiword_export.desktop b/filters/kword/abiword/kword_abiword_export.desktop new file mode 100644 index 000000000..0125bc1d1 --- /dev/null +++ b/filters/kword/abiword/kword_abiword_export.desktop @@ -0,0 +1,73 @@ +[Desktop Entry] +Type=Service +Name=KWord AbiWord Export Filter +Name[af]=Kword Abiword Voer uit Filter +Name[ar]=مِرْشَح تصدير AbiWord لدى KWord +Name[az]=KWord AbiWord Alma Süzgəci +Name[bg]=Филтър за експортиране от KWord в AbiWord +Name[br]=Sil ezporzh AbiWord evit KWord +Name[ca]=Filtre d'exportació AbiWord per a KWord +Name[cs]=KWord AbiWord exportní filtr +Name[cy]=Hidlen Allforio AbiWord KWord +Name[da]=KWord AbiWord-eksportfilter +Name[de]=KWord AbiWord-Exportfilter +Name[el]=Φίλτρο εξαγωγής Abiword του KWord +Name[eo]=KWord AbiWord-eksportfiltrilo +Name[es]=Filtro de exportación AbiWord de KWord +Name[et]=KWordi AbiWord'i ekspordifilter +Name[eu]=KWord-en AbiWord esportaziorako iragazkia +Name[fa]=پالایۀ صادرات KWord AbiWord +Name[fi]=KWord AbiWord -vientisuodin +Name[fo]=KOrð til AbiWord-umsetari +Name[fr]=Filtre d'exportation AbiWord de KWord +Name[fy]=KWord AbiWord Eksportfilter +Name[ga]=Scagaire Easpórtála AbiWord KWord +Name[gl]=Filtro de exportación para AbiWord de KWord +Name[he]=מסנן ייצוא מ־KWord ל־AbiWord +Name[hi]=के-वर्ड एबीवर्ड निर्यात छननी +Name[hr]=KWord Abiword filtar izvoza +Name[hu]=KWord AbiWord exportszűrő +Name[id]=Filter Ekspor AbiWord KWord +Name[is]=KWord AbiWord útflutningssía +Name[it]=Filtro di esportazione AbiWord per KWord +Name[ja]=KWord AbiWord エクスポートフィルタ +Name[km]=តម្រងនាំចេញ AbiWord សម្រាប់ KWord +Name[lo]= ຕົວຕອງການສົ່ງອອກ AbiWord ຂອງເອກະສານຂໍ້ຄວາມ K +Name[lt]=KWord AbiWord eksporto filtras +Name[lv]=KWord AbiWord eksporta filtrs +Name[ms]=Penapis Eksport KWord AbiWord +Name[mt]=Filtru għall-esportazzjoni ta' AbiWord minn ġo KWord +Name[nb]=AbiWord-eksportfilter for KWord +Name[nds]=AbiWord-Exportfilter för KWord +Name[ne]=केडीई शब्द एबीआई वर्ड निर्यात फिल्टर +Name[nl]=KWord AbiWord-Exportfilter +Name[nn]=AbiWord-eksportfilter for KWord +Name[pl]=Filtr eksportu do formatu AbiWord z KWord +Name[pt]=Filtro de exportação para AbiWord do KWord +Name[pt_BR]=Filtro de exportação AbiWord para o KWord +Name[ro]=Filtru exportare KWord pentru AbiWord +Name[ru]=Фильтр экспорта документов KWord в AbiWord +Name[se]=KWord:a AbiWord-olggosfievrridansilli +Name[sk]=AbiWord filter pre export z KWord-u +Name[sl]=Izvozni filter AbiWord za KWord +Name[sr]=KWord-ов филтер за извоз у AbiWord +Name[sr@Latn]=KWord-ov filter za izvoz u AbiWord +Name[sv]=Kword-exportfilter för Abiword +Name[ta]=KWord AbiWord ஏற்றுமதி வடிகட்டி +Name[tg]=Филтри Содироти KWord AbiWord +Name[th]=ตัวกรองการส่งออก AbiWord ของเอกสารข้อความ K +Name[tr]=KWord için AbiWord Aktarma Filtresi +Name[uk]=Фільтр експорту AbiWord для KWord +Name[uz]=KWord AbiWord eksport filteri +Name[uz@cyrillic]=KWord AbiWord экспорт филтери +Name[ven]=Maipfi a K maipfi a Abi U bvisela nga nnda filithara +Name[wa]=Passete AbiWord di rexhowe po KWord +Name[xh]=Isihluzi Sokurhweba ngaphandle se KWord AbiWord +Name[zh_CN]=KWord AbiWord 导出过滤器 +Name[zh_TW]=KWord AbiWord 匯出過濾程式 +Name[zu]=KWord AbiWord Icebo Export Filter +X-KDE-Export=application/x-abiword +X-KDE-Import=application/x-kword +X-KDE-Weight=1 +X-KDE-Library=libabiwordexport +ServiceTypes=KOfficeFilter diff --git a/filters/kword/abiword/kword_abiword_import.desktop b/filters/kword/abiword/kword_abiword_import.desktop new file mode 100644 index 000000000..e0b11cfb4 --- /dev/null +++ b/filters/kword/abiword/kword_abiword_import.desktop @@ -0,0 +1,73 @@ +[Desktop Entry] +Type=Service +Name=KWord AbiWord Import Filter +Name[af]=Kword Abiword In voer Filter +Name[ar]=مِرْشَح استيراد AbiWord لدى KWord +Name[az]=KWord AbiWord Qeyd Süzgəci +Name[bg]=Филтър за импортиране от AbiWord в KWord +Name[br]=Sil enporzh AbiWord evit KWord +Name[ca]=Filtre d'importació AbiWord per a KWord +Name[cs]=KWord AbiWord importní filtr +Name[cy]=Hidlen Fewnforio AbiWord KWord +Name[da]=KWord Applixword-importfilter +Name[de]=KWord AbiWord-Importfilter +Name[el]=Φίλτρο εισαγωγής Abiword του KWord +Name[eo]=KWord AbiWord-importfiltrilo +Name[es]=Filtro de importación AbiWord de KWord +Name[et]=KWordi AbiWord'i impordifilter +Name[eu]=KWord-en AbiWord inportaziorako iragazkia +Name[fa]=پالایۀ واردات KWord AbiWord +Name[fi]=KWord AbiWord -tuontisuodin +Name[fo]=AbiWord til KOrð-umsetari +Name[fr]=Filtre d'importation AbiWord de KWord +Name[fy]=KWord AbiWord Ymportfilter +Name[ga]=Scagaire Iompórtála AbiWord KWord +Name[gl]=Filtro de importación de AbiWord para KWord +Name[he]=מסנן ייבוא מ־AbiWord ל־KWord +Name[hi]=के-वर्ड एबीवर्ड आयात छननी +Name[hr]=KWord Abiword filtar uvoza +Name[hu]=KWord AbiWord importszűrő +Name[id]=Filter Impor AbiWord KWord +Name[is]=KWord AbiWord innflutningssía +Name[it]=Filtro di importazione AbiWord per KWord +Name[ja]=KWord AbiWord インポートフィルタ +Name[km]=តម្រងនាំចូល AbiWord សម្រាប់ KWord +Name[lo]= ຕົວຕອງການນຳເຂົ້າ AbiWord ຂອງເອກະສານຂໍ້ຄວາມ K +Name[lt]=KWord AbiWord importo filtras +Name[lv]=KWord AbiWord importa filtrs +Name[ms]=Penapis Import KWord AbiWord +Name[mt]=Filtru għall-importazzjoni ta' AbiWord ġo KWord +Name[nb]=AbiWord-importfilter for KWord +Name[nds]=AbiWord-Importfilter för KWord +Name[ne]=केडीई शब्द एबीआई वर्ड आयात फिल्टर +Name[nl]=KWord AbiWord-importfilter +Name[nn]=AbiWord-importfilter for KWord +Name[pl]=Filtr importu formatu AbiWord do KWord +Name[pt]=Filtro de importação do AbiWord para o KWord +Name[pt_BR]=Filtro de importação AbiWord para o KWord +Name[ro]=Filtru importare KWord pentru AbiWord +Name[ru]=Фильтр импорта документов AbiWord в KWord +Name[se]=KWord:a AbiWord-sisafievrridansilli +Name[sk]=AbiWord filter pre import do KWord-u +Name[sl]=Uvozni filter AbiWord za KWord +Name[sr]=KWord-ов филтер за увоз из AbiWord-а +Name[sr@Latn]=KWord-ov filter za uvoz iz AbiWord-a +Name[sv]=Kword-importfilter för Abiword +Name[ta]=KWord AbiWord இறக்குமதி வடிகட்டி +Name[tg]=Филтри Воридоти KWord AbiWord +Name[th]=ตัวกรองการนำเข้า AbiWord ของเอกสารข้อความ K +Name[tr]=KWord için AbiWord Alma Filtresi +Name[uk]=Фільтр імпорту AbiWord для KWord +Name[uz]=KWord AbiWord import filteri +Name[uz@cyrillic]=KWord AbiWord импорт филтери +Name[ven]=Maipfi a K Maipfi a Abi u Dzhenisa nga ngomu filithara +Name[wa]=Passete Abiword d' intrêye po KWord +Name[xh]=Isihluzi Sokurhweba se KWord AbiWord +Name[zh_CN]=KWord AbiWord 导入过滤器 +Name[zh_TW]=KWord AbiWord 匯入過濾程式 +Name[zu]=KWord AbiWord Icebo lokucoca ulwelo olurhwetyiweyo ngaphakathi +X-KDE-Export=application/x-kword +X-KDE-Import=application/x-abiword +X-KDE-Weight=1 +X-KDE-Library=libabiwordimport +ServiceTypes=KOfficeFilter diff --git a/filters/kword/abiword/status.html b/filters/kword/abiword/status.html new file mode 100644 index 000000000..de78dd484 --- /dev/null +++ b/filters/kword/abiword/status.html @@ -0,0 +1,219 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> +<html> +<head> + <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> + <title>KOffice filters status: AbiWord Filter</title> +</head> +<body text="#000000" bgcolor="#FFFFFF" link="#000099" vlink="#666666" alink="#666666"> +<a NAME="START"> </a> + +<BR> +<center> + <h1> + KOffice filters status: <i>AbiWord Filter</i> + </h1> +</center> + +<hr NOSHADE SIZE=2 WIDTH="70%"> + +<font size="-1"><b> + <a HREF="#import">Import</a> | + <a HREF="#export">Export</a> +</b></font> + +<BR><BR><BR> +<center><a NAME="import"> </a></center> + +<a HREF="#START"><font size="-1"><b>Up</b></font></a> +<center> +<table BORDER=0 CELLSPACING=0 BGCOLOR="#000000" WIDTH="100%"> + <tr> + <td> + <table BORDER=0 CELLPADDING=2 BGCOLOR="#FFFFFF" WIDTH="100%"> + + <tr BGCOLOR="#DDFFDD"> + <td COLSPAN="2"> + <center><b><i><font size="+1"> + Import AbiWord for KWord<BR> + </font></i></b></center> + </td> + </tr> + + <tr BGCOLOR="#EEEEFF"> + <td VALIGN=TOP WIDTH="1%" NOWRAP><b><font size="+1">Last update</font></b></td> + <td>12 Jun 2005</td> + </tr> + + <tr BGCOLOR="#CCCCFF"> + <td VALIGN=TOP><b><font size="+1">Features</font></b></td> + <td> + - Import text<br> + - Can import formatting<br> + - Can read gzip-compressed files (just use .abw.gz or .zabw as an extension of the filename!)<br> + - Can read bzip2-compressed files (just use .abw.bz2 or .bzabw as an extension of the filename!)<br> + - Styles<br> + - Result of character styles<br> + - Images (PNG)<br> + - Fields (as far as KWord supports them.)<br> + - Page size, including page margins<br> + - Import ignored word list (for spell checking)<br> + </td> + </tr> + + <tr BGCOLOR="#EEEEFF"> + <td VALIGN=TOP><b><font size="+1">Todo</font></b></td> + <td> + - Multiple columns<br> + - Lists<br> + - Header/footer<br> + - Bookmarks<br> + - Tables<br> + - Footnotes<br> + </td> + </tr> + + <tr BGCOLOR="#CCCCFF"> + <td VALIGN=TOP><b><font size="+1">History</font></b></td> + <td> + 19 Jan 2001: First half working import filter.<br> + 24 Jan 2001: Can read gzip compressed AbiWord files.<br> + 31 Jan 2001: Can import all formating at character level.<br> + 09 Jan 2002: Can import styles.<br> + 27 Feb 2002: Fields.<br> + 19 Mar 2002: More styles, inlcuding character ones.<br> + 07 Jun 2002: Ignored Word List<br> + 28 Jan 2003: new XML parsing error message box<br> + 12 Jun 2005: import keyword/subject in documentinfo<br> + </td> + </tr> + + <tr BGCOLOR="#EEEEFF"> + <td VALIGN=TOP><b><font size="+1">Authors</font></b></td> + <td><a href="mailto:goutte@kde.org">Nicolas GOUTTE</a></td> + </tr> + + <tr BGCOLOR="#CCCCFF"> + <td VALIGN=TOP><b><font size="+1">Links</font></b></td> + <td> + <a href="http://www.abisource.com">AbiSource</a> (AbiWord's homepage)<br> + <a href="http://www.abisource.com/awml.dtd">AbiWord's DTD</a><br> + </td> + </tr> + + <tr BGCOLOR="#EEEEFF"> + <td VALIGN=TOP><b><font size="+1">Comment</font></b></td> + <td> + NOTE: this status describes the version of the filter for KOffice 1.3 (CVS HEAD)<br> + </td> + </tr> + </table> + </td> + </tr> +</table> +</center> +<a HREF="#START"><font size="-1"><b>Up</b></font></a> + +<br><br><br> + +<hr NOSHADE SIZE=1> +<br><br><br> + + +<center> + <a NAME="export"> </a> +</center> + +<a HREF="#START"><font size="-1"><b>Up</b></font></a> +<center> +<table BORDER=0 CELLSPACING=0 BGCOLOR="#000000" WIDTH="100%"> + <tr> + <td> + <table BORDER=0 CELLPADDING=2 BGCOLOR="#FFFFFF" WIDTH="100%"> + <tr BGCOLOR="#FFDDDD"> + <td COLSPAN="2"> + <center><b><i><font size="+1"> + Export KWord to AbiWord + </font></i></b></center> + </td> + </tr> + + <tr BGCOLOR="#EEEEFF"> + <td VALIGN=TOP WIDTH="1%" NOWRAP><b><font size="+1">Last update</font></b></td> + <td>25 Jan 2003</td> + </tr> + + <tr BGCOLOR="#CCCCFF"> + <td VALIGN=TOP><b><font size="+1">Features</font></b></td> + <td> + - Can export simple text<br> + - Can export formatting<br> + - Can write gzip-compressed files (just use .abw.gz or .zabw as an extension of the filename!)<br> + - Can handle forced page breaks<br> + - Can export inlined pictures (converted to PNG)<br> + - Can export styles<br> + - Can export variables into fields (no custom variables!)<br> + - Can export page size, including page margins<br> + - Can export the ignored word list (for spell checking)<br> + </td> + </tr> + + <tr BGCOLOR="#EEEEFF"> + <td VALIGN=TOP><b><font size="+1">Todo</font></b></td> + <td> + - Multiple columns.<br> + - Lists.<br> + - Inlined formula (MATHML)<br> + - Non-inlined pictures<br> + - Tables<br> + - KWord's DTP modus (How can you export that in AbiWord?)<br> + - Bookmarks<br> + - Footnotes<br> + - Many other things...<br> + </td> + </tr> + + <tr BGCOLOR="#CCCCFF"> + <td VALIGN=TOP><b><font size="+1">History</font></b></td> + <td> + 17 Jan 2001: First filter version (can export simple texts and formats)<br> + 21 Jan 2001: Added colours and corrected bug in font size<br> + 31 Jan 2001: New form of comment header in the exported file<br> + 19 Mar 2001: Fix the bug due to the new KWord<br> + 16 Aug 2001: Fix forced page break handling<br> + 29 Nov 2001: Export of inlined images (PNG)<br> + 24 Feb 2002: Simplify the written file by avoiding redundant information<br> + 27 Feb 2002: Fields<br> + 19 Mar 2002: Page margins<br> + 09 Jun 2002: Ignored Word List<br> + 25 Jan 2003: Fix export of pictures<br> + </td> + </tr> + + <tr BGCOLOR="#EEEEFF"> + <td VALIGN=TOP><b><font size="+1">Authors</font></b></td> + <td><a href="mailto:goutte@kde.org">Nicolas GOUTTE</a></td> + </tr> + + <tr BGCOLOR="#CCCCFF"> + <td VALIGN=TOP><b><font size="+1">Links</font></b></td> + <td> + <a href="http://www.abisource.com">Abisource</a> (AbiWord's homepage)<br> + <a href="http://www.abisource.com/awml.dtd">AbiWord's DTD</a><br> + </td> + </tr> + + <tr BGCOLOR="#EEEEFF"> + <td VALIGN=TOP><b><font size="+1">Comment</font></b></td> + <td> + NOTE: this status describes the version of the filter for KOffice 1.3 (CVS HEAD)<br> + </td> + </tr> + </table> + </td> + </tr> +</table> +</center> +<a HREF="#START"><font size="-1"><b>Up</b></font></a> + +</body> +</html> |