filters

abiwordimport.cpp
1/* This file is part of the KDE project
2 Copyright 2001, 2002, 2003, 2004 Nicolas GOUTTE <goutte@kde.org>
3
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public
6 License as published by the Free Software Foundation; either
7 version 2 of the License, or (at your option) any later version.
8
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
13
14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to
16 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18*/
19
20#include <config.h>
21
22#ifdef HAVE_UNISTD_H
23#include <unistd.h>
24#endif
25
26#include <tqmap.h>
27#include <tqbuffer.h>
28#include <tqpicture.h>
29#include <tqxml.h>
30#include <tqdom.h>
31#include <tqdatetime.h>
32
33#include <kdebug.h>
34#include <kmdcodec.h>
35#include <kfilterdev.h>
36#include <kgenericfactory.h>
37#include <tdemessagebox.h>
38
39#include <KoPageLayout.h>
40#include <KoStore.h>
41#include <KoFilterChain.h>
42
43#include "ImportHelpers.h"
44#include "ImportFormatting.h"
45#include "ImportStyle.h"
46#include "ImportField.h"
47
48#include "abiwordimport.h"
49
50class ABIWORDImportFactory : KGenericFactory<ABIWORDImport, KoFilter>
51{
52public:
53 ABIWORDImportFactory(void) : KGenericFactory<ABIWORDImport, KoFilter> ("kwordabiwordimport")
54 {}
55protected:
56 virtual void setupTranslations( void )
57 {
58 TDEGlobal::locale()->insertCatalogue( "kofficefilters" );
59 }
60};
61
62K_EXPORT_COMPONENT_FACTORY( libabiwordimport, ABIWORDImportFactory() )
63
64// *Note for the reader of this code*
65// Tags in lower case (e.g. <c>) are AbiWord's ones.
66// Tags in upper case (e.g. <TEXT>) are KWord's ones.
67
68// enum StackItemElementType is now in the file ImportFormatting.h
69
70class StructureParser : public TQXmlDefaultHandler
71{
72public:
73 StructureParser(KoFilterChain* chain)
74 : m_chain(chain), m_pictureNumber(0), m_pictureFrameNumber(0), m_tableGroupNumber(0),
75 m_timepoint(TQDateTime::currentDateTime(TQt::UTC)), m_fatalerror(false)
76 {
77 createDocument();
78 structureStack.setAutoDelete(true);
79 StackItem *stackItem=new(StackItem);
80 stackItem->elementType=ElementTypeBottom;
81 stackItem->m_frameset=mainFramesetElement; // The default frame set.
82 stackItem->stackElementText=mainFramesetElement; // This is more for DEBUG
83 structureStack.push(stackItem); //Security item (not to empty the stack)
84 }
85 virtual ~StructureParser()
86 {
87 structureStack.clear();
88 }
89public:
90 virtual bool startDocument(void);
91 virtual bool endDocument(void);
92 virtual bool startElement( const TQString&, const TQString&, const TQString& name, const TQXmlAttributes& attributes);
93 virtual bool endElement( const TQString&, const TQString& , const TQString& qName);
94 virtual bool characters ( const TQString & ch );
95 virtual bool warning(const TQXmlParseException& exception);
96 virtual bool error(const TQXmlParseException& exception);
97 virtual bool fatalError(const TQXmlParseException& exception);
98public:
99 inline TQDomDocument getDocInfo(void) const { return m_info; }
100 inline TQDomDocument getDocument(void) const { return mainDocument; }
101 inline bool wasFatalError(void) const { return m_fatalerror; }
102protected:
103 bool clearStackUntilParagraph(StackItemStack& auxilaryStack);
104 bool complexForcedPageBreak(StackItem* stackItem);
105private:
106 // The methods that would need too much parameters are private instead of being static outside the class
107 bool StartElementC(StackItem* stackItem, StackItem* stackCurrent,
108 const TQXmlAttributes& attributes);
109 bool StartElementA(StackItem* stackItem, StackItem* stackCurrent,
110 const TQXmlAttributes& attributes);
111 bool StartElementImage(StackItem* stackItem, StackItem* stackCurrent,
112 const TQXmlAttributes& attributes);
113 bool EndElementD (StackItem* stackItem);
114 bool EndElementM (StackItem* stackItem);
115 bool StartElementSection(StackItem* stackItem, StackItem* stackCurrent,
116 const TQXmlAttributes& attributes);
117 bool StartElementFoot(StackItem* stackItem, StackItem* stackCurrent,
118 const TQXmlAttributes& attributes);
119 bool StartElementTable(StackItem* stackItem, StackItem* stackCurrent, const TQXmlAttributes& attributes);
120 bool StartElementCell(StackItem* stackItem, StackItem* stackCurrent,const TQXmlAttributes& attributes);
121private:
122 void createDocument(void);
123 void createDocInfo(void);
124 TQString indent; //DEBUG
125 StackItemStack structureStack;
126 TQDomDocument mainDocument;
127 TQDomDocument m_info;
128 TQDomElement framesetsPluralElement; // <FRAMESETS>
129 TQDomElement mainFramesetElement; // The main <FRAMESET> where the body text will be under.
130 TQDomElement m_picturesElement; // <PICTURES>
131 TQDomElement m_paperElement; // <PAPER>
132 TQDomElement m_paperBordersElement; // <PAPERBORDER>
133 TQDomElement m_ignoreWordsElement; // <SPELLCHECKIGNORELIST>
134 StyleDataMap styleDataMap;
135 KoFilterChain* m_chain;
136 uint m_pictureNumber; // unique: increment *before* use
137 uint m_pictureFrameNumber; // unique: increment *before* use
138 uint m_tableGroupNumber; // unique: increment *before* use
139 TQMap<TQString,TQString> m_metadataMap; // Map for <m> elements
140 TQDateTime m_timepoint; // Date/time (for pictures)
141 bool m_fatalerror; // Did a XML parsing fatal error happened?
142};
143
144// Element <c>
145
146bool StructureParser::StartElementC(StackItem* stackItem, StackItem* stackCurrent, const TQXmlAttributes& attributes)
147{
148 // <c> elements can be nested in <p> elements, in <a> elements or in other <c> elements
149 // AbiWord does not nest <c> elements in other <c> elements, but explicitly allows external programs to do it!
150
151 // <p> or <c> (not child of <a>)
152 if ((stackCurrent->elementType==ElementTypeParagraph)||(stackCurrent->elementType==ElementTypeContent))
153 {
154 // Contents can have styles, however KWord cannot have character style.
155 // Therefore we use the style if it exist, but we do not create it if not.
156 TQString strStyleProps;
157 TQString strStyleName=attributes.value("style").stripWhiteSpace();
158 if (!strStyleName.isEmpty())
159 {
160 StyleDataMap::Iterator it=styleDataMap.find(strStyleName);
161 if (it!=styleDataMap.end())
162 {
163 strStyleProps=it.data().m_props;
164 }
165 }
166
167 AbiPropsMap abiPropsMap;
168 PopulateProperties(stackItem,strStyleProps,attributes,abiPropsMap,true);
169
170 stackItem->elementType=ElementTypeContent;
171 stackItem->stackElementParagraph=stackCurrent->stackElementParagraph; // <PARAGRAPH>
172 stackItem->stackElementText=stackCurrent->stackElementText; // <TEXT>
173 stackItem->stackElementFormatsPlural=stackCurrent->stackElementFormatsPlural; // <FORMATS>
174 stackItem->pos=stackCurrent->pos; //Propagate the position
175 }
176 // <a> or <c> when child of <a>
177 else if ((stackCurrent->elementType==ElementTypeAnchor)||(stackCurrent->elementType==ElementTypeAnchorContent))
178 {
179 stackItem->elementType=ElementTypeAnchorContent;
180 }
181 else
182 {//we are not nested correctly, so consider it a parse error!
183 kdError(30506) << "parse error <c> tag nested neither in <p> nor in <c> nor in <a> but in "
184 << stackCurrent->itemName << endl;
185 return false;
186 }
187 return true;
188}
189
190bool charactersElementC (StackItem* stackItem, TQDomDocument& mainDocument, const TQString & ch)
191{
192 if (stackItem->elementType==ElementTypeContent)
193 { // Normal <c>
194 TQDomElement elementText=stackItem->stackElementText;
195 TQDomElement elementFormatsPlural=stackItem->stackElementFormatsPlural;
196 elementText.appendChild(mainDocument.createTextNode(ch));
197
198 TQDomElement formatElementOut=mainDocument.createElement("FORMAT");
199 formatElementOut.setAttribute("id",1); // Normal text!
200 formatElementOut.setAttribute("pos",stackItem->pos); // Start position
201 formatElementOut.setAttribute("len",ch.length()); // Start position
202 elementFormatsPlural.appendChild(formatElementOut); //Append to <FORMATS>
203 stackItem->pos+=ch.length(); // Adapt new starting position
204
205 AddFormat(formatElementOut, stackItem, mainDocument);
206 }
207 else if (stackItem->elementType==ElementTypeAnchorContent)
208 {
209 // Add characters to the link name
210 stackItem->strTemp2+=ch;
211 // TODO: how can we care about the text format?
212 }
213 else
214 {
215 kdError(30506) << "Internal error (in charactersElementC)" << endl;
216 }
217
218 return true;
219}
220
221bool EndElementC (StackItem* stackItem, StackItem* stackCurrent)
222{
223 if (stackItem->elementType==ElementTypeContent)
224 {
225 stackItem->stackElementText.normalize();
226 stackCurrent->pos=stackItem->pos; //Propagate the position back to the parent element
227 }
228 else if (stackItem->elementType==ElementTypeAnchorContent)
229 {
230 stackCurrent->strTemp2+=stackItem->strTemp2; //Propagate the link name back to the parent element
231 }
232 else
233 {
234 kdError(30506) << "Wrong element type!! Aborting! (</c> in StructureParser::endElement)" << endl;
235 return false;
236 }
237 return true;
238}
239
240// Element <a>
241bool StructureParser::StartElementA(StackItem* stackItem, StackItem* stackCurrent, const TQXmlAttributes& attributes)
242{
243 // <a> elements can be nested in <p> elements
244 if (stackCurrent->elementType==ElementTypeParagraph)
245 {
246
247 //AbiPropsMap abiPropsMap;
248 //PopulateProperties(stackItem,TQString(),attributes,abiPropsMap,true);
249
250 stackItem->elementType=ElementTypeAnchor;
251 stackItem->stackElementParagraph=stackCurrent->stackElementParagraph; // <PARAGRAPH>
252 stackItem->stackElementText=stackCurrent->stackElementText; // <TEXT>
253 stackItem->stackElementFormatsPlural=stackCurrent->stackElementFormatsPlural; // <FORMATS>
254 stackItem->pos=stackCurrent->pos; //Propagate the position
255 stackItem->strTemp1=attributes.value("xlink:href").stripWhiteSpace(); // link reference
256 stackItem->strTemp2=TQString(); // link name
257
258 // We must be careful: AbiWord permits anchors to bookmarks.
259 // However, KWord does not know what a bookmark is.
260 if (stackItem->strTemp1[0]=='#')
261 {
262 kdWarning(30506) << "Anchor <a> to bookmark: " << stackItem->strTemp1 << endl
263 << " Processing <a> like <c>" << endl;
264 // We have a reference to a bookmark. Therefore treat it as a normal content <c>
265 return StartElementC(stackItem, stackCurrent, attributes);
266 }
267 }
268 else
269 {//we are not nested correctly, so consider it a parse error!
270 kdError(30506) << "parse error <a> tag not a child of <p> but of "
271 << stackCurrent->itemName << endl;
272 return false;
273 }
274 return true;
275}
276
277static bool charactersElementA (StackItem* stackItem, const TQString & ch)
278{
279 // Add characters to the link name
280 stackItem->strTemp2+=ch;
281 return true;
282}
283
284static bool EndElementA (StackItem* stackItem, StackItem* stackCurrent, TQDomDocument& mainDocument)
285{
286 if (!stackItem->elementType==ElementTypeAnchor)
287 {
288 kdError(30506) << "Wrong element type!! Aborting! (</a> in StructureParser::endElement)" << endl;
289 return false;
290 }
291
292 TQDomElement elementText=stackItem->stackElementText;
293 elementText.appendChild(mainDocument.createTextNode("#"));
294
295 TQDomElement formatElement=mainDocument.createElement("FORMAT");
296 formatElement.setAttribute("id",4); // Variable
297 formatElement.setAttribute("pos",stackItem->pos); // Start position
298 formatElement.setAttribute("len",1); // Start position
299
300 TQDomElement variableElement=mainDocument.createElement("VARIABLE");
301 formatElement.appendChild(variableElement);
302
303 TQDomElement typeElement=mainDocument.createElement("TYPE");
304 typeElement.setAttribute("key","STRING");
305 typeElement.setAttribute("type",9); // link
306 typeElement.setAttribute("text",stackItem->strTemp2);
307 variableElement.appendChild(typeElement); //Append to <VARIABLE>
308
309 TQDomElement linkElement=mainDocument.createElement("LINK");
310 linkElement.setAttribute("hrefName",stackItem->strTemp1);
311 linkElement.setAttribute("linkName",stackItem->strTemp2);
312 variableElement.appendChild(linkElement); //Append to <VARIABLE>
313
314 // Now work on stackCurrent
315 stackCurrent->stackElementFormatsPlural.appendChild(formatElement);
316 stackCurrent->pos++; //Propagate the position back to the parent element
317
318 return true;
319}
320
321// Element <p>
322
323bool StartElementP(StackItem* stackItem, StackItem* stackCurrent,
324 TQDomDocument& mainDocument,
325 StyleDataMap& styleDataMap, const TQXmlAttributes& attributes)
326{
327 // We must prepare the style
328 TQString strStyle=attributes.value("style");
329 if (strStyle.isEmpty())
330 {
331 strStyle="Normal";
332 }
333 StyleDataMap::ConstIterator it=styleDataMap.useOrCreateStyle(strStyle);
334
335 TQString strLevel=attributes.value("level");
336 int level;
337 if (strLevel.isEmpty())
338 {
339 // We have not "level" attribute, so we must use the style's level.
340 level=it.data().m_level;
341 }
342 else
343 {
344 // We have a "level" attribute, so it overrides the style's level.
345 level=strStyle.toInt();
346 }
347
348 TQDomElement elementText=stackCurrent->stackElementText;
349 TQDomElement paragraphElementOut=mainDocument.createElement("PARAGRAPH");
350 stackCurrent->m_frameset.appendChild(paragraphElementOut);
351
352 TQDomElement textElementOut=mainDocument.createElement("TEXT");
353 paragraphElementOut.appendChild(textElementOut);
354 TQDomElement formatsPluralElementOut=mainDocument.createElement("FORMATS");
355 paragraphElementOut.appendChild(formatsPluralElementOut);
356
357 AbiPropsMap abiPropsMap;
358 PopulateProperties(stackItem,it.data().m_props,attributes,abiPropsMap,false);
359
360 stackItem->elementType=ElementTypeParagraph;
361 stackItem->stackElementParagraph=paragraphElementOut; // <PARAGRAPH>
362 stackItem->stackElementText=textElementOut; // <TEXT>
363 stackItem->stackElementFormatsPlural=formatsPluralElementOut; // <FORMATS>
364 stackItem->pos=0; // No text characters yet
365
366 // Now we populate the layout
367 TQDomElement layoutElement=mainDocument.createElement("LAYOUT");
368 paragraphElementOut.appendChild(layoutElement);
369
370 AddLayout(strStyle,layoutElement, stackItem, mainDocument, abiPropsMap, level, false);
371
372 return true;
373}
374
375bool charactersElementP (StackItem* stackItem, TQDomDocument& mainDocument, const TQString & ch)
376{
377 TQDomElement elementText=stackItem->stackElementText;
378
379 elementText.appendChild(mainDocument.createTextNode(ch));
380
381 stackItem->pos+=ch.length(); // Adapt new starting position
382
383 return true;
384}
385
386bool EndElementP (StackItem* stackItem)
387{
388 if (!stackItem->elementType==ElementTypeParagraph)
389 {
390 kdError(30506) << "Wrong element type!! Aborting! (in endElementP)" << endl;
391 return false;
392 }
393 stackItem->stackElementText.normalize();
394 return true;
395}
396
397static bool StartElementField(StackItem* stackItem, StackItem* stackCurrent,
398 TQDomDocument& mainDocument, const TQXmlAttributes& attributes)
399{
400 // <field> element elements can be nested in <p>
401 if (stackCurrent->elementType==ElementTypeParagraph)
402 {
403 TQString strType=attributes.value("type").stripWhiteSpace();
404 kdDebug(30506)<<"<field> type:"<<strType<<endl;
405
406 AbiPropsMap abiPropsMap;
407 PopulateProperties(stackItem,TQString(),attributes,abiPropsMap,true);
408
409 stackItem->elementType=ElementTypeEmpty;
410
411 // We create a format element
412 TQDomElement variableElement=mainDocument.createElement("VARIABLE");
413
414 if (!ProcessField(mainDocument, variableElement, strType, attributes))
415 {
416 // The field type was not recognised,
417 // therefore write the field type in red as normal text
418 kdWarning(30506) << "Unknown <field> type: " << strType << endl;
419 TQDomElement formatElement=mainDocument.createElement("FORMAT");
420 formatElement.setAttribute("id",1); // Variable
421 formatElement.setAttribute("pos",stackItem->pos); // Start position
422 formatElement.setAttribute("len",strType.length());
423
424 formatElement.appendChild(variableElement);
425
426 // Now work on stackCurrent
427 stackCurrent->stackElementFormatsPlural.appendChild(formatElement);
428 stackCurrent->stackElementText.appendChild(mainDocument.createTextNode(strType));
429 stackCurrent->pos+=strType.length(); // Adjust position
430
431 // Add formating (use stackItem)
432 stackItem->fgColor.setRgb(255,0,0);
433 AddFormat(formatElement, stackItem, mainDocument);
434 return true;
435 }
436
437 // We create a format element
438 TQDomElement formatElement=mainDocument.createElement("FORMAT");
439 formatElement.setAttribute("id",4); // Variable
440 formatElement.setAttribute("pos",stackItem->pos); // Start position
441 formatElement.setAttribute("len",1);
442
443 formatElement.appendChild(variableElement);
444
445 // Now work on stackCurrent
446 stackCurrent->stackElementFormatsPlural.appendChild(formatElement);
447 stackCurrent->stackElementText.appendChild(mainDocument.createTextNode("#"));
448 stackCurrent->pos++; // Adjust position
449
450 // Add formating (use stackItem)
451 AddFormat(formatElement, stackItem, mainDocument);
452
453 }
454 else
455 {//we are not nested correctly, so consider it a parse error!
456 kdError(30506) << "parse error <field> tag not nested in <p> but in "
457 << stackCurrent->itemName << endl;
458 return false;
459 }
460 return true;
461}
462
463// <s> (style)
464static bool StartElementS(StackItem* stackItem, StackItem* /*stackCurrent*/,
465 const TQXmlAttributes& attributes, StyleDataMap& styleDataMap)
466{
467 // We do not assume when we are called.
468 // We also do not care if a style is defined multiple times.
469 stackItem->elementType=ElementTypeEmpty;
470
471 TQString strStyleName=attributes.value("name").stripWhiteSpace();
472
473 if (strStyleName.isEmpty())
474 {
475 kdWarning(30506) << "Style has no name!" << endl;
476 }
477 else
478 {
479 TQString strLevel=attributes.value("level");
480 int level;
481 if (strLevel.isEmpty())
482 level=-1; //TODO/FIXME: might be wrong if the style is based on another
483 else
484 level=strLevel.toInt();
485 TQString strBasedOn=attributes.value("basedon").simplifyWhiteSpace();
486 styleDataMap.defineNewStyleFromOld(strStyleName,strBasedOn,level,attributes.value("props"));
487 kdDebug(30506) << " Style name: " << strStyleName << endl
488 << " Based on: " << strBasedOn << endl
489 << " Level: " << level << endl
490 << " Props: " << attributes.value("props") << endl;
491 }
492
493 return true;
494}
495
496// <image>
497bool StructureParser::StartElementImage(StackItem* stackItem, StackItem* stackCurrent,
498 const TQXmlAttributes& attributes)
499{
500 // <image> elements can be nested in <p> or <c> elements
501 if ((stackCurrent->elementType!=ElementTypeParagraph) && (stackCurrent->elementType!=ElementTypeContent))
502 {//we are not nested correctly, so consider it a parse error!
503 kdError(30506) << "parse error <image> tag nested neither in <p> nor in <c> but in "
504 << stackCurrent->itemName << endl;
505 return false;
506 }
507 stackItem->elementType=ElementTypeEmpty;
508
509 TQString strDataId=attributes.value("dataid").stripWhiteSpace();
510
511 AbiPropsMap abiPropsMap;
512 abiPropsMap.splitAndAddAbiProps(attributes.value("props"));
513
514 double height=ValueWithLengthUnit(abiPropsMap["height"].getValue());
515 double width =ValueWithLengthUnit(abiPropsMap["width" ].getValue());
516
517 kdDebug(30506) << "Image: " << strDataId << " height: " << height << " width: " << width << endl;
518
519 // TODO: image properties
520
521 if (strDataId.isEmpty())
522 {
523 kdWarning(30506) << "Image has no data id!" << endl;
524 }
525 else
526 {
527 kdDebug(30506) << "Image: " << strDataId << endl;
528 }
529
530 TQString strPictureFrameName(i18n("Frameset name","Picture %1").arg(++m_pictureFrameNumber));
531
532 // Create the frame set of the image
533
534 TQDomElement framesetElement=mainDocument.createElement("FRAMESET");
535 framesetElement.setAttribute("frameType",2);
536 framesetElement.setAttribute("frameInfo",0);
537 framesetElement.setAttribute("visible",1);
538 framesetElement.setAttribute("name",strPictureFrameName);
539 framesetsPluralElement.appendChild(framesetElement);
540
541 TQDomElement frameElementOut=mainDocument.createElement("FRAME");
542 frameElementOut.setAttribute("left",0);
543 frameElementOut.setAttribute("top",0);
544 frameElementOut.setAttribute("bottom",height);
545 frameElementOut.setAttribute("right" ,width );
546 frameElementOut.setAttribute("runaround",1);
547 // TODO: a few attributes are missing
548 framesetElement.appendChild(frameElementOut);
549
550 TQDomElement element=mainDocument.createElement("PICTURE");
551 element.setAttribute("keepAspectRatio","true");
552 framesetElement.setAttribute("frameType",2); // Picture
553 framesetElement.appendChild(element);
554
555 TQDomElement key=mainDocument.createElement("KEY");
556 key.setAttribute("filename",strDataId);
557 key.setAttribute("year",m_timepoint.date().year());
558 key.setAttribute("month",m_timepoint.date().month());
559 key.setAttribute("day",m_timepoint.date().day());
560 key.setAttribute("hour",m_timepoint.time().hour());
561 key.setAttribute("minute",m_timepoint.time().minute());
562 key.setAttribute("second",m_timepoint.time().second());
563 key.setAttribute("msec",m_timepoint.time().msec());
564 element.appendChild(key);
565
566 // Now use the image's frame set
567 TQDomElement elementText=stackItem->stackElementText;
568 TQDomElement elementFormatsPlural=stackItem->stackElementFormatsPlural;
569 elementText.appendChild(mainDocument.createTextNode("#"));
570
571 TQDomElement formatElementOut=mainDocument.createElement("FORMAT");
572 formatElementOut.setAttribute("id",6); // Normal text!
573 formatElementOut.setAttribute("pos",stackItem->pos); // Start position
574 formatElementOut.setAttribute("len",1); // Start position
575 elementFormatsPlural.appendChild(formatElementOut); //Append to <FORMATS>
576
577 // WARNING: we must change the position in stackCurrent!
578 stackCurrent->pos++; // Adapt new starting position
579
580 TQDomElement anchor=mainDocument.createElement("ANCHOR");
581 // No name attribute!
582 anchor.setAttribute("type","frameset");
583 anchor.setAttribute("instance",strPictureFrameName);
584 formatElementOut.appendChild(anchor);
585
586 return true;
587}
588
589// <d>
590static bool StartElementD(StackItem* stackItem, StackItem* /*stackCurrent*/,
591 const TQXmlAttributes& attributes)
592{
593 // We do not assume when we are called or if we are or not a child of <data>
594 // However, we assume that we are after all <image> elements
595 stackItem->elementType=ElementTypeRealData;
596
597 TQString strName=attributes.value("name").stripWhiteSpace();
598 kdDebug(30506) << "Data: " << strName << endl;
599
600 TQString strBase64=attributes.value("base64").stripWhiteSpace();
601 TQString strMime=attributes.value("mime").stripWhiteSpace();
602
603 if (strName.isEmpty())
604 {
605 kdWarning(30506) << "Data has no name!" << endl;
606 stackItem->elementType=ElementTypeEmpty;
607 return true;
608 }
609
610 if (strMime.isEmpty())
611 {
612 // Old AbiWord files had no mime types for images but the data were base64-coded PNG
613 strMime="image/png";
614 strBase64="yes";
615 }
616
617 stackItem->fontName=strName; // Store the data name as font name.
618 stackItem->bold=(strBase64=="yes"); // Store base64-coded as bold
619 stackItem->strTemp1=strMime; // Mime type
620 stackItem->strTemp2=TQString(); // Image data
621
622 return true;
623}
624
625static bool CharactersElementD (StackItem* stackItem, TQDomDocument& /*mainDocument*/, const TQString & ch)
626{
627 // As we have no guarantee to have the whole stream in one call, we must store the data.
628 stackItem->strTemp2+=ch;
629 return true;
630}
631
632bool StructureParser::EndElementD (StackItem* stackItem)
633{
634 if (!stackItem->elementType==ElementTypeRealData)
635 {
636 kdError(30506) << "Wrong element type!! Aborting! (in endElementD)" << endl;
637 return false;
638 }
639 if (!m_chain)
640 {
641 kdError(30506) << "No filter chain! Aborting! (in endElementD)" << endl;
642 return false;
643 }
644
645 bool isSvg=false; // SVG ?
646
647 TQString extension;
648
649 // stackItem->strTemp1 contains the mime type
650 if (stackItem->strTemp1=="image/png")
651 {
652 extension=".png";
653 }
654 else if (stackItem->strTemp1=="image/jpeg") // ### FIXME: in fact it does not exist in AbiWord
655 {
656 extension=".jpeg";
657 }
658 else if (stackItem->strTemp1=="image/svg-xml") //Yes it is - not +
659 {
660 extension=".svg";
661 isSvg=true;
662 }
663 else
664 {
665 kdWarning(30506) << "Unknown or unsupported mime type: "
666 << stackItem->strTemp1 << endl;
667 return true;
668 }
669
670 TQString strStoreName;
671 strStoreName="pictures/picture";
672 strStoreName+=TQString::number(++m_pictureNumber);
673 strStoreName+=extension;
674
675 TQString strDataId=stackItem->fontName; // AbiWord's data id
676 TQDomElement key=mainDocument.createElement("KEY");
677 key.setAttribute("filename",strDataId);
678 key.setAttribute("year",m_timepoint.date().year());
679 key.setAttribute("month",m_timepoint.date().month());
680 key.setAttribute("day",m_timepoint.date().day());
681 key.setAttribute("hour",m_timepoint.time().hour());
682 key.setAttribute("minute",m_timepoint.time().minute());
683 key.setAttribute("second",m_timepoint.time().second());
684 key.setAttribute("msec",m_timepoint.time().msec());
685 key.setAttribute("name",strStoreName);
686 m_picturesElement.appendChild(key);
687
688 KoStoreDevice* out=m_chain->storageFile(strStoreName, KoStore::Write);
689 if(!out)
690 {
691 kdError(30506) << "Unable to open output file for: " << stackItem->fontName << " Storage: " << strStoreName << endl;
692 return false;
693 }
694
695 if (stackItem->bold) // Is base64-coded?
696 {
697 kdDebug(30506) << "Decode and write base64 stream: " << stackItem->fontName << endl;
698 // We need to decode the base64 stream
699 // However KCodecs has no TQString to TQByteArray decoder!
700 TQByteArray base64Stream=stackItem->strTemp2.utf8(); // Use utf8 to avoid corruption of data
701 TQByteArray binaryStream;
702 KCodecs::base64Decode(base64Stream, binaryStream);
703 out->writeBlock(binaryStream, binaryStream.count());
704 }
705 else
706 {
707 // Unknown text format!
708 kdDebug(30506) << "Write character stream: " << stackItem->fontName << endl;
709 // We strip the white space in front to avoid white space before a XML declaration
710 TQCString strOut=stackItem->strTemp2.stripWhiteSpace().utf8();
711 out->writeBlock(strOut,strOut.length());
712 }
713
714 return true;
715}
716
717// <m>
718static bool StartElementM(StackItem* stackItem, StackItem* /*stackCurrent*/,
719 const TQXmlAttributes& attributes)
720{
721 // We do not assume when we are called or if we are or not a child of <metadata>
722 stackItem->elementType=ElementTypeRealMetaData;
723
724 TQString strKey=attributes.value("key").stripWhiteSpace();
725 kdDebug(30506) << "Metadata key: " << strKey << endl;
726
727 if (strKey.isEmpty())
728 {
729 kdWarning(30506) << "Metadata has no key!" << endl;
730 stackItem->elementType=ElementTypeIgnore;
731 return true;
732 }
733
734 stackItem->strTemp1=strKey; // Key
735 stackItem->strTemp2=TQString(); // Meta data
736
737 return true;
738}
739
740static bool CharactersElementM (StackItem* stackItem, const TQString & ch)
741{
742 // As we have no guarantee to have the whole data in one call, we must store the data.
743 stackItem->strTemp2+=ch;
744 return true;
745}
746
747bool StructureParser::EndElementM (StackItem* stackItem)
748{
749 if (!stackItem->elementType==ElementTypeRealData)
750 {
751 kdError(30506) << "Wrong element type!! Aborting! (in endElementM)" << endl;
752 return false;
753 }
754
755 if (stackItem->strTemp1.isEmpty())
756 {
757 // Probably an internal error!
758 kdError(30506) << "Key name was erased! Aborting! (in endElementM)" << endl;
759 return false;
760 }
761
762 // Just add it to the metadata map, we do not do something special with the values.
763 m_metadataMap[stackItem->strTemp1]=stackItem->strTemp2;
764
765 return true;
766}
767
768// <br> (forced line break)
769static bool StartElementBR(StackItem* stackItem, StackItem* stackCurrent,
770 TQDomDocument& mainDocument)
771{
772 // <br> elements are mostly in <c> but can also be in <p>
773 if ((stackCurrent->elementType==ElementTypeParagraph)
774 || (stackCurrent->elementType==ElementTypeContent))
775 {
776 stackItem->elementType=ElementTypeEmpty;
777
778 // Now work on stackCurrent
779
780 if (stackCurrent->elementType==ElementTypeContent)
781 {
782 // Child <c>, so we have to add formating of <c>
783 TQDomElement formatElement=mainDocument.createElement("FORMAT");
784 formatElement.setAttribute("id",1); // Normal text!
785 formatElement.setAttribute("pos",stackCurrent->pos); // Start position
786 formatElement.setAttribute("len",1);
787 AddFormat(formatElement, stackCurrent, mainDocument); // Add the format of the parent <c>
788 stackCurrent->stackElementFormatsPlural.appendChild(formatElement); //Append to <FORMATS>
789 }
790
791 stackCurrent->stackElementText.appendChild(mainDocument.createTextNode(TQChar(10))); // Add a LINE FEED
792 stackCurrent->pos++; // Adjust position
793
794 }
795 else
796 {//we are not nested correctly, so consider it a parse error!
797 kdError(30506) << "parse error <br> tag not nested in <p> or <c> but in "
798 << stackCurrent->itemName << endl;
799 return false;
800 }
801 return true;
802}
803
804// <cbr> (forced column break, not supported)
805// <pbr> (forced page break)
806static bool StartElementPBR(StackItem* /*stackItem*/, StackItem* stackCurrent,
807 TQDomDocument& mainDocument)
808{
809 // We are sure to be the child of a <p> element
810
811 // The following code is similar to the one in StartElementP
812 // We use mainFramesetElement here not to be dependant that <section> has happened before
813 TQDomElement paragraphElementOut=mainDocument.createElement("PARAGRAPH");
814 stackCurrent->m_frameset.appendChild(paragraphElementOut);
815 TQDomElement textElementOut=mainDocument.createElement("TEXT");
816 paragraphElementOut.appendChild(textElementOut);
817 TQDomElement formatsPluralElementOut=mainDocument.createElement("FORMATS");
818 paragraphElementOut.appendChild(formatsPluralElementOut);
819
820 // We must now copy/clone the layout of elementText.
821
822 TQDomNodeList nodeList=stackCurrent->stackElementParagraph.elementsByTagName("LAYOUT");
823
824 if (!nodeList.count())
825 {
826 kdError(30506) << "Unable to find <LAYOUT> element! Aborting! (in StartElementPBR)" <<endl;
827 return false;
828 }
829
830 // Now clone it
831 TQDomNode newNode=nodeList.item(0).cloneNode(true); // We make a deep cloning of the first element/node
832 if (newNode.isNull())
833 {
834 kdError(30506) << "Unable to clone <LAYOUT> element! Aborting! (in StartElementPBR)" <<endl;
835 return false;
836 }
837 paragraphElementOut.appendChild(newNode);
838
839 // We need a page break!
840 TQDomElement oldLayoutElement=nodeList.item(0).toElement();
841 if (oldLayoutElement.isNull())
842 {
843 kdError(30506) << "Cannot find old <LAYOUT> element! Aborting! (in StartElementPBR)" <<endl;
844 return false;
845 }
846 // We have now to add a element <PAGEBREAKING>
847 // TODO/FIXME: what if there is already one?
848 TQDomElement pagebreakingElement=mainDocument.createElement("PAGEBREAKING");
849 pagebreakingElement.setAttribute("linesTogether","false");
850 pagebreakingElement.setAttribute("hardFrameBreak","false");
851 pagebreakingElement.setAttribute("hardFrameBreakAfter","true");
852 oldLayoutElement.appendChild(pagebreakingElement);
853
854 // Now that we have done with the old paragraph,
855 // we can write stackCurrent with the data of the new one!
856 // NOTE: The following code is similar to the one in StartElementP but we are working on stackCurrent!
857 stackCurrent->elementType=ElementTypeParagraph;
858 stackCurrent->stackElementParagraph=paragraphElementOut; // <PARAGRAPH>
859 stackCurrent->stackElementText=textElementOut; // <TEXT>
860 stackCurrent->stackElementFormatsPlural=formatsPluralElementOut; // <FORMATS>
861 stackCurrent->pos=0; // No text character yet
862
863 return true;
864}
865
866// <pagesize>
867static bool StartElementPageSize(TQDomElement& paperElement, const TQXmlAttributes& attributes)
868{
869 if (attributes.value("page-scale").toDouble()!=1.0)
870 {
871 kdWarning(30506) << "Ignoring unsupported page scale: " << attributes.value("page-scale") << endl;
872 }
873
874 int kwordOrientation;
875 TQString strOrientation=attributes.value("orientation").stripWhiteSpace();
876
877 if (strOrientation=="portrait")
878 {
879 kwordOrientation=0;
880 }
881 else if (strOrientation=="landscape")
882 {
883 kwordOrientation=1;
884 }
885 else
886 {
887 kdWarning(30506) << "Unknown page orientation: " << strOrientation << "! Ignoring! " << endl;
888 kwordOrientation=0;
889 }
890
891 double kwordHeight;
892 double kwordWidth;
893
894 TQString strPageType=attributes.value("pagetype").stripWhiteSpace();
895
896 // Do we know the page size or do we need to measure?
897 // For page formats that KWord knows, use our own values in case the values in the file would be wrong.
898
899 KoFormat kwordFormat = KoPageFormat::formatFromString(strPageType);
900
901 if (kwordFormat==PG_CUSTOM)
902 {
903 kdDebug(30506) << "Custom or other page format found: " << strPageType << endl;
904
905 double height = attributes.value("height").toDouble();
906 double width = attributes.value("width" ).toDouble();
907
908 TQString strUnits = attributes.value("units").stripWhiteSpace();
909
910 kdDebug(30506) << "Explicit page size: "
911 << height << " " << strUnits << " x " << width << " " << strUnits
912 << endl;
913
914 if (strUnits=="cm")
915 {
916 kwordHeight = CentimetresToPoints(height);
917 kwordWidth = CentimetresToPoints(width);
918 }
919 else if (strUnits=="inch")
920 {
921 kwordHeight = InchesToPoints(height);
922 kwordWidth = InchesToPoints(width);
923 }
924 else if (strUnits=="mm")
925 {
926 kwordHeight = MillimetresToPoints(height);
927 kwordWidth = MillimetresToPoints(width);
928 }
929 else
930 {
931 kwordHeight = 0.0;
932 kwordWidth = 0.0;
933 kdWarning(30506) << "Unknown unit type: " << strUnits << endl;
934 }
935 }
936 else
937 {
938 // We have a format known by KOffice, so use KOffice's functions
939 kwordHeight = MillimetresToPoints(KoPageFormat::height(kwordFormat,PG_PORTRAIT));
940 kwordWidth = MillimetresToPoints(KoPageFormat::width (kwordFormat,PG_PORTRAIT));
941 }
942
943 if ((kwordHeight <= 1.0) || (kwordWidth <= 1.0))
944 // At least one of the two values is ridiculous
945 {
946 kdWarning(30506) << "Page width or height is too small: "
947 << kwordHeight << "x" << kwordWidth << endl;
948 // As we have no correct page size, we assume we have A4
949 kwordFormat = PG_DIN_A4;
950 kwordHeight = CentimetresToPoints(29.7);
951 kwordWidth = CentimetresToPoints(21.0);
952 }
953
954 // Now that we have gathered all the page size data, put it in the right element!
955
956 if (paperElement.isNull())
957 {
958 kdError(30506) << "<PAPER> element cannot be accessed! Aborting!" << endl;
959 return false;
960 }
961
962 paperElement.setAttribute("format",kwordFormat);
963 paperElement.setAttribute("width",kwordWidth);
964 paperElement.setAttribute("height",kwordHeight);
965 paperElement.setAttribute("orientation",kwordOrientation);
966
967 return true;
968}
969
970
971bool StructureParser::complexForcedPageBreak(StackItem* stackItem)
972{
973 // We are not a child of a <p> element, so we cannot use StartElementPBR directly
974
975 StackItemStack auxilaryStack;
976
977 if (!clearStackUntilParagraph(auxilaryStack))
978 {
979 kdError(30506) << "Could not clear stack until a paragraph!" << endl;
980 return false;
981 }
982
983 // Now we are a child of a <p> element!
984
985 bool success=StartElementPBR(stackItem,structureStack.current(),mainDocument);
986
987 // Now restore the stack
988
989 StackItem* stackCurrent=structureStack.current();
990 StackItem* item;
991 while (auxilaryStack.count()>0)
992 {
993 item=auxilaryStack.pop();
994 // We cannot put back the item on the stack like that.
995 // We must set a few values for each item.
996 item->pos=0; // Start at position 0
997 item->stackElementParagraph=stackCurrent->stackElementParagraph; // new <PARAGRAPH>
998 item->stackElementText=stackCurrent->stackElementText; // new <TEXT>
999 item->stackElementFormatsPlural=stackCurrent->stackElementFormatsPlural; // new <FORMATS>
1000 structureStack.push(item);
1001 }
1002
1003 return success;
1004}
1005
1006
1007// <section>
1008bool StructureParser::StartElementSection(StackItem* stackItem, StackItem* /*stackCurrent*/,
1009 const TQXmlAttributes& attributes)
1010{
1011 //TODO: non main text sections (e.g. footers)
1012 stackItem->elementType=ElementTypeSection;
1013
1014 AbiPropsMap abiPropsMap;
1015 // Treat the props attributes in the two available flavors: lower case and upper case.
1016 kdDebug(30506)<< "========== props=\"" << attributes.value("props") << "\"" << endl;
1017 abiPropsMap.splitAndAddAbiProps(attributes.value("props"));
1018 abiPropsMap.splitAndAddAbiProps(attributes.value("PROPS")); // PROPS is deprecated
1019
1020 // TODO: only the first main text section should change the page margins
1021 // TODO; (as KWord does not allow different page sizes/margins for the same document)
1022 if (true && (!m_paperBordersElement.isNull()))
1023 {
1024 TQString str;
1025 str=abiPropsMap["page-margin-top"].getValue();
1026 if (!str.isEmpty())
1027 {
1028 m_paperBordersElement.setAttribute("top",ValueWithLengthUnit(str));
1029 }
1030 str=abiPropsMap["page-margin-left"].getValue();
1031 if (!str.isEmpty())
1032 {
1033 m_paperBordersElement.setAttribute("left",ValueWithLengthUnit(str));
1034 }
1035 str=abiPropsMap["page-margin-bottom"].getValue();
1036 if (!str.isEmpty())
1037 {
1038 m_paperBordersElement.setAttribute("bottom",ValueWithLengthUnit(str));
1039 }
1040 str=abiPropsMap["page-margin-right"].getValue();
1041 if (!str.isEmpty())
1042 {
1043 m_paperBordersElement.setAttribute("right",ValueWithLengthUnit(str));
1044 }
1045 }
1046 return true;
1047}
1048
1049// <iw>
1050
1051static bool EndElementIW(StackItem* stackItem, StackItem* /*stackCurrent*/,
1052 TQDomDocument& mainDocument, TQDomElement& m_ignoreWordsElement)
1053{
1054 if (!stackItem->elementType==ElementTypeIgnoreWord)
1055 {
1056 kdError(30506) << "Wrong element type!! Aborting! (in endElementIW)" << endl;
1057 return false;
1058 }
1059 TQDomElement wordElement=mainDocument.createElement("SPELLCHECKIGNOREWORD");
1060 wordElement.setAttribute("word",stackItem->strTemp2.stripWhiteSpace());
1061 m_ignoreWordsElement.appendChild(wordElement);
1062 return true;
1063}
1064
1065// <foot>
1066bool StructureParser::StartElementFoot(StackItem* stackItem, StackItem* /*stackCurrent*/,
1067 const TQXmlAttributes& /*attributes*/)
1068{
1069#if 0
1070 stackItem->elementType=ElementTypeFoot;
1071
1072 const TQString id(attributes.value("endnote-id").stripWhiteSpace());
1073 kdDebug(30506) << "Foot note id: " << id << endl;
1074
1075 if (id.isEmpty())
1076 {
1077 kdWarning(30506) << "Footnote has no id!" << endl;
1078 stackItem->elementType=ElementTypeIgnore;
1079 return true;
1080 }
1081
1082 // We need to create a frameset for the foot note.
1083 TQDomElement framesetElement(mainDocument.createElement("FRAMESET"));
1084 framesetElement.setAttribute("frameType",1);
1085 framesetElement.setAttribute("frameInfo",7);
1086 framesetElement.setAttribute("visible",1);
1087 framesetElement.setAttribute("name",getFootnoteFramesetName(id));
1088 framesetsPluralElement.appendChild(framesetElement);
1089
1090 TQDomElement frameElementOut(mainDocument.createElement("FRAME"));
1091 //frameElementOut.setAttribute("left",28);
1092 //frameElementOut.setAttribute("top",42);
1093 //frameElementOut.setAttribute("bottom",566);
1094 //frameElementOut.setAttribute("right",798);
1095 frameElementOut.setAttribute("runaround",1);
1096 // ### TODO: a few attributes are missing
1097 framesetElement.appendChild(frameElementOut);
1098
1099 stackItem->m_frameset=framesetElement;
1100#else
1101 stackItem->elementType=ElementTypeIgnore;
1102#endif
1103 return true;
1104}
1105
1106// Element <table>
1107bool StructureParser::StartElementTable(StackItem* stackItem, StackItem* stackCurrent,
1108 const TQXmlAttributes& attributes)
1109{
1110#if 1
1111 // In KWord, inline tables are inside a paragraph.
1112 // In AbiWord, tables are outside any paragraph.
1113
1114 TQStringList widthList;
1115 widthList.split('/', attributes.value("table-column-props"), false);
1116 const uint columns = widthList.size();
1117 stackItem->m_doubleArray.detach(); // Be sure not to modify parents
1118 stackItem->m_doubleArray.resize(columns+1); // All left positions but the last right one
1119 stackItem->m_doubleArray[0] = 0.0;
1120 TQStringList::ConstIterator it;
1121 uint i;
1122 for ( i=0, it=widthList.begin(); i<columns; ++i, ++it )
1123 {
1124 kdDebug(30506) << "Column width: " << (*it) << " cooked " << ValueWithLengthUnit(*it) << endl;
1125 stackItem->m_doubleArray[i+1] = ValueWithLengthUnit(*it) + stackItem->m_doubleArray[i];
1126 }
1127 // ### TODO: in case of automatic column widths, we have not any width given by AbiWord
1128
1129 const uint tableNumber(++m_tableGroupNumber);
1130 const TQString tableName(i18n("Table %1").arg(tableNumber));
1131
1132 TQDomElement elementText=stackCurrent->stackElementText;
1133 TQDomElement paragraphElementOut=mainDocument.createElement("PARAGRAPH");
1134 stackCurrent->m_frameset.appendChild(paragraphElementOut);
1135
1136 TQDomElement textElementOut(mainDocument.createElement("TEXT"));
1137 textElementOut.appendChild(mainDocument.createTextNode("#"));
1138 paragraphElementOut.appendChild(textElementOut);
1139
1140 TQDomElement formatsPluralElementOut=mainDocument.createElement("FORMATS");
1141 paragraphElementOut.appendChild(formatsPluralElementOut);
1142
1143 TQDomElement elementFormat(mainDocument.createElement("FORMAT"));
1144 elementFormat.setAttribute("id",6);
1145 elementFormat.setAttribute("pos",0);
1146 elementFormat.setAttribute("len",1);
1147 formatsPluralElementOut.appendChild(elementFormat);
1148
1149 TQDomElement elementAnchor(mainDocument.createElement("ANCHOR"));
1150 elementAnchor.setAttribute("type","frameset");
1151 elementAnchor.setAttribute("instance",tableName);
1152 elementFormat.appendChild(elementAnchor);
1153
1154 stackItem->elementType=ElementTypeTable;
1155 stackItem->stackElementParagraph=paragraphElementOut; // <PARAGRAPH>
1156 stackItem->stackElementText=textElementOut; // <TEXT>
1157 stackItem->stackElementFormatsPlural=formatsPluralElementOut; // <FORMATS>
1158 stackItem->strTemp1=tableName;
1159 stackItem->strTemp2=TQString::number(tableNumber); // needed as I18N does not allow adding phrases
1160 stackItem->pos=1; // Just #
1161
1162 // Now we populate the layout
1163 TQDomElement layoutElement=mainDocument.createElement("LAYOUT");
1164 paragraphElementOut.appendChild(layoutElement);
1165
1166 AbiPropsMap abiPropsMap;
1167 styleDataMap.useOrCreateStyle("Normal"); // We might have to create the "Normal" style.
1168 AddLayout("Normal", layoutElement, stackItem, mainDocument, abiPropsMap, 0, false);
1169#else
1170 stackItem->elementType=ElementTypeIgnore;
1171#endif
1172 return true;
1173}
1174
1175// <cell>
1176bool StructureParser::StartElementCell(StackItem* stackItem, StackItem* stackCurrent,
1177 const TQXmlAttributes& attributes)
1178{
1179#if 1
1180 if (stackCurrent->elementType!=ElementTypeTable)
1181 {
1182 kdError(30506) << "Wrong element type!! Aborting! (in StructureParser::endElementCell)" << endl;
1183 return false;
1184 }
1185
1186 stackItem->elementType=ElementTypeCell;
1187
1188 const TQString tableName(stackCurrent->strTemp1);
1189 kdDebug(30506) << "Table name: " << tableName << endl;
1190
1191 if (tableName.isEmpty())
1192 {
1193 kdError(30506) << "Table name is empty! Aborting!" << endl;
1194 return false;
1195 }
1196
1197 AbiPropsMap abiPropsMap;
1198 abiPropsMap.splitAndAddAbiProps(attributes.value("props")); // Do not check PROPS
1199
1200 // We abuse the attach number to know the row and col numbers.
1201 const uint row=abiPropsMap["top-attach"].getValue().toUInt();
1202 const uint col=abiPropsMap["left-attach"].getValue().toUInt();
1203
1204 if ( col >= stackItem->m_doubleArray.size() )
1205 {
1206 // We do not know the right position of this column, so improvise. (### TODO)
1207 // We play on the fact that TQByteArray uses shallow copies by default.
1208 // (We do want that the change is known at <table> level)
1209 stackItem->m_doubleArray.resize( stackItem->m_doubleArray.size() + 1, TQGArray::SpeedOptim );
1210 stackItem->m_doubleArray[col+1] = stackItem->m_doubleArray[col] + 72; // Try 1 inch
1211 }
1212
1213 const TQString frameName(i18n("Frameset name","Table %3, row %1, column %2")
1214 .arg(row).arg(col).arg(stackCurrent->strTemp2)); // As the stack could be wrong, be careful and use the string as last!
1215
1216 // We need to create a frameset for the cell
1217 TQDomElement framesetElement(mainDocument.createElement("FRAMESET"));
1218 framesetElement.setAttribute("frameType",1);
1219 framesetElement.setAttribute("frameInfo",0);
1220 framesetElement.setAttribute("visible",1);
1221 framesetElement.setAttribute("name",frameName);
1222 framesetElement.setAttribute("row",row);
1223 framesetElement.setAttribute("col",col);
1224 framesetElement.setAttribute("rows",1); // ### TODO: rowspan
1225 framesetElement.setAttribute("cols",1); // ### TODO: colspan
1226 framesetElement.setAttribute("grpMgr",tableName);
1227 framesetsPluralElement.appendChild(framesetElement);
1228
1229 TQDomElement frameElementOut(mainDocument.createElement("FRAME"));
1230 frameElementOut.setAttribute( "left", stackItem->m_doubleArray[col] );
1231 frameElementOut.setAttribute( "right", stackItem->m_doubleArray[col+1] );
1232 frameElementOut.setAttribute("top",0);
1233 frameElementOut.setAttribute("bottom",0);
1234 frameElementOut.setAttribute("runaround",1);
1235 frameElementOut.setAttribute("autoCreateNewFrame",0); // Very important for cell growing!
1236 // ### TODO: a few attributes are missing
1237 framesetElement.appendChild(frameElementOut);
1238
1239 stackItem->m_frameset=framesetElement;
1240 TQDomElement nullDummy;
1241 stackItem->stackElementParagraph=nullDummy; // <PARAGRAPH>
1242 stackItem->stackElementText=nullDummy; // <TEXT>
1243 stackItem->stackElementFormatsPlural=nullDummy; // <FORMATS>
1244
1245#else
1246 stackItem->elementType=ElementTypeIgnore;
1247#endif
1248 return true;
1249}
1250
1251// Parser for SAX2
1252
1253bool StructureParser :: startElement( const TQString&, const TQString&, const TQString& name, const TQXmlAttributes& attributes)
1254{
1255 //Warning: be careful that some element names can be lower case or upper case (not very XML)
1256 kdDebug(30506) << indent << " <" << name << ">" << endl; //DEBUG
1257 indent += "*"; //DEBUG
1258
1259 if (structureStack.isEmpty())
1260 {
1261 kdError(30506) << "Stack is empty!! Aborting! (in StructureParser::startElement)" << endl;
1262 return false;
1263 }
1264
1265 // Create a new stack element copying the top of the stack.
1266 StackItem *stackItem=new StackItem(*structureStack.current());
1267
1268 if (!stackItem)
1269 {
1270 kdError(30506) << "Could not create Stack Item! Aborting! (in StructureParser::startElement)" << endl;
1271 return false;
1272 }
1273
1274 stackItem->itemName=name;
1275
1276 bool success=false;
1277
1278 if ((name=="c")||(name=="C"))
1279 {
1280 success=StartElementC(stackItem,structureStack.current(),attributes);
1281 }
1282 else if ((name=="p")||(name=="P"))
1283 {
1284 success=StartElementP(stackItem,structureStack.current(),mainDocument,
1285 styleDataMap,attributes);
1286 }
1287 else if ((name=="section")||(name=="SECTION"))
1288 {
1289 success=StartElementSection(stackItem,structureStack.current(),attributes);
1290 }
1291 else if (name=="a")
1292 {
1293 success=StartElementA(stackItem,structureStack.current(),attributes);
1294 }
1295 else if (name=="br") // NOTE: Not sure if it only exists in lower case!
1296 {
1297 // We have a forced line break
1298 StackItem* stackCurrent=structureStack.current();
1299 success=StartElementBR(stackItem,stackCurrent,mainDocument);
1300 }
1301 else if (name=="cbr") // NOTE: Not sure if it only exists in lower case!
1302 {
1303 // We have a forced column break (not supported by KWord)
1304 stackItem->elementType=ElementTypeEmpty;
1305 StackItem* stackCurrent=structureStack.current();
1306 if (stackCurrent->elementType==ElementTypeContent)
1307 {
1308 kdWarning(30506) << "Forced column break found! Transforming to forced page break" << endl;
1309 success=complexForcedPageBreak(stackItem);
1310 }
1311 else if (stackCurrent->elementType==ElementTypeParagraph)
1312 {
1313 kdWarning(30506) << "Forced column break found! Transforming to forced page break" << endl;
1314 success=StartElementPBR(stackItem,stackCurrent,mainDocument);
1315 }
1316 else
1317 {
1318 kdError(30506) << "Forced column break found out of turn! Aborting! Parent: "
1319 << stackCurrent->itemName <<endl;
1320 success=false;
1321 }
1322 }
1323 else if (name=="pbr") // NOTE: Not sure if it only exists in lower case!
1324 {
1325 // We have a forced page break
1326 stackItem->elementType=ElementTypeEmpty;
1327 StackItem* stackCurrent=structureStack.current();
1328 if (stackCurrent->elementType==ElementTypeContent)
1329 {
1330 success=complexForcedPageBreak(stackItem);
1331 }
1332 else if (stackCurrent->elementType==ElementTypeParagraph)
1333 {
1334 success=StartElementPBR(stackItem,stackCurrent,mainDocument);
1335 }
1336 else
1337 {
1338 kdError(30506) << "Forced page break found out of turn! Aborting! Parent: "
1339 << stackCurrent->itemName <<endl;
1340 success=false;
1341 }
1342 }
1343 else if (name=="pagesize")
1344 // Does only exist as lower case tag!
1345 {
1346 stackItem->elementType=ElementTypeEmpty;
1347 stackItem->stackElementText=structureStack.current()->stackElementText; // TODO: reason?
1348 success=StartElementPageSize(m_paperElement,attributes);
1349 }
1350 else if ((name=="field") //TODO: upper-case?
1351 || (name=="f")) // old deprecated name for <field>
1352 {
1353 success=StartElementField(stackItem,structureStack.current(),mainDocument,attributes);
1354 }
1355 else if (name=="s") // Seems only to exist as lower case
1356 {
1357 success=StartElementS(stackItem,structureStack.current(),attributes,styleDataMap);
1358 }
1359 else if ((name=="image") //TODO: upper-case?
1360 || (name=="i")) // old deprecated name for <image>
1361 {
1362 success=StartElementImage(stackItem,structureStack.current(),attributes);
1363 }
1364 else if (name=="d") // TODO: upper-case?
1365 {
1366 success=StartElementD(stackItem,structureStack.current(),attributes);
1367 }
1368 else if (name=="iw") // No upper-case
1369 {
1370 stackItem->elementType=ElementTypeIgnoreWord;
1371 success=true;
1372 }
1373 else if (name=="m") // No upper-case
1374 {
1375 success=StartElementM(stackItem,structureStack.current(),attributes);
1376 }
1377 else if (name=="foot") // No upper-case
1378 {
1379 success=StartElementFoot(stackItem,structureStack.current(),attributes);
1380 }
1381 else if (name=="table") // No upper-case
1382 {
1383 success=StartElementTable(stackItem,structureStack.current(), attributes);
1384 }
1385 else if (name=="cell") // No upper-case
1386 {
1387 success=StartElementCell(stackItem,structureStack.current(),attributes);
1388 }
1389 else
1390 {
1391 stackItem->elementType=ElementTypeUnknown;
1392 stackItem->stackElementText=structureStack.current()->stackElementText; // TODO: reason?
1393 success=true;
1394 }
1395 if (success)
1396 {
1397 structureStack.push(stackItem);
1398 }
1399 else
1400 { // We have a problem so destroy our resources.
1401 delete stackItem;
1402 }
1403 return success;
1404}
1405
1406bool StructureParser :: endElement( const TQString&, const TQString& , const TQString& name)
1407{
1408 indent.remove( 0, 1 ); // DEBUG
1409 kdDebug(30506) << indent << " </" << name << ">" << endl;
1410
1411 if (structureStack.isEmpty())
1412 {
1413 kdError(30506) << "Stack is empty!! Aborting! (in StructureParser::endElement)" << endl;
1414 return false;
1415 }
1416
1417 bool success=false;
1418
1419 StackItem *stackItem=structureStack.pop();
1420 if ((name=="c")||(name=="C"))
1421 {
1422 success=EndElementC(stackItem,structureStack.current());
1423 }
1424 else if ((name=="p")||(name=="P"))
1425 {
1426 success=EndElementP(stackItem);
1427 }
1428 else if (name=="a")
1429 {
1430 if (stackItem->elementType==ElementTypeContent)
1431 {
1432 // Anchor to a bookmark (not supported by KWord))
1433 success=EndElementC(stackItem,structureStack.current());
1434 }
1435 else
1436 {
1437 // Normal anchor
1438 success=EndElementA(stackItem,structureStack.current(), mainDocument);
1439 }
1440 }
1441 else if (name=="d")
1442 {
1443 success=EndElementD(stackItem);
1444 }
1445 else if (name=="iw") // No upper-case
1446 {
1447 success=EndElementIW(stackItem,structureStack.current(), mainDocument, m_ignoreWordsElement);
1448 }
1449 else if (name=="m") // No upper-case
1450 {
1451 success=EndElementM(stackItem);
1452 }
1453 else
1454 {
1455 success=true; // No problem, so authorisation to continue parsing
1456 }
1457 if (!success)
1458 {
1459 // If we have no success, then it was surely a tag mismatch. Help debugging!
1460 kdError(30506) << "Found tag name: " << name
1461 << " expected: " << stackItem->itemName << endl;
1462 }
1463 delete stackItem;
1464 return success;
1465}
1466
1467bool StructureParser :: characters ( const TQString & ch )
1468{
1469 // DEBUG start
1470 if (ch=="\n")
1471 {
1472 kdDebug(30506) << indent << " (LINEFEED)" << endl;
1473 }
1474 else if (ch.length()> 40)
1475 { // 40 characters are enough (especially for image data)
1476 kdDebug(30506) << indent << " :" << ch.left(40) << "..." << endl;
1477 }
1478 else
1479 {
1480 kdDebug(30506) << indent << " :" << ch << ":" << endl;
1481 }
1482 // DEBUG end
1483 if (structureStack.isEmpty())
1484 {
1485 kdError(30506) << "Stack is empty!! Aborting! (in StructureParser::characters)" << endl;
1486 return false;
1487 }
1488
1489 bool success=false;
1490
1491 StackItem *stackItem=structureStack.current();
1492
1493 if ((stackItem->elementType==ElementTypeContent)
1494 || (stackItem->elementType==ElementTypeAnchorContent))
1495 { // <c>
1496 success=charactersElementC(stackItem,mainDocument,ch);
1497 }
1498 else if (stackItem->elementType==ElementTypeParagraph)
1499 { // <p>
1500 success=charactersElementP(stackItem,mainDocument,ch);
1501 }
1502 else if (stackItem->elementType==ElementTypeAnchor)
1503 { // <a>
1504 success=charactersElementA(stackItem,ch);
1505 }
1506 else if (stackItem->elementType==ElementTypeEmpty)
1507 {
1508 success=ch.stripWhiteSpace().isEmpty();
1509 if (!success)
1510 {
1511 // We have a parsing error, so abort!
1512 kdError(30506) << "Empty element "<< stackItem->itemName
1513 <<" is not empty! Aborting! (in StructureParser::characters)" << endl;
1514 }
1515 }
1516 else if (stackItem->elementType==ElementTypeRealData)
1517 {
1518 success=CharactersElementD(stackItem,mainDocument,ch);
1519 }
1520 else if (stackItem->elementType==ElementTypeIgnoreWord)
1521 {
1522 stackItem->strTemp2+=ch; // Just collect the data
1523 success=true;
1524 }
1525 else if (stackItem->elementType==ElementTypeRealMetaData)
1526 {
1527 success=CharactersElementM(stackItem,ch);
1528 }
1529 else
1530 {
1531 success=true;
1532 }
1533
1534 return success;
1535}
1536
1537bool StructureParser::startDocument(void)
1538{
1539 indent = TQString(); //DEBUG
1540 styleDataMap.defineDefaultStyles();
1541 return true;
1542}
1543
1544void StructureParser::createDocInfo(void)
1545{
1546 TQDomImplementation implementation;
1547 TQDomDocument doc(implementation.createDocumentType("document-info",
1548 "-//KDE//DTD document-info 1.2//EN", "http://www.koffice.org/DTD/document-info-1.2.dtd"));
1549
1550 m_info=doc;
1551
1552 m_info.appendChild(
1553 mainDocument.createProcessingInstruction(
1554 "xml","version=\"1.0\" encoding=\"UTF-8\""));
1555
1556 TQDomElement elementDoc(mainDocument.createElement("document-info"));
1557 elementDoc.setAttribute("xmlns","http://www.koffice.org/DTD/document-info");
1558 m_info.appendChild(elementDoc);
1559
1560 TQDomElement about(mainDocument.createElement("about"));
1561 elementDoc.appendChild(about);
1562
1563 TQDomElement abstract(mainDocument.createElement("abstract"));
1564 about.appendChild(abstract);
1565 abstract.appendChild(mainDocument.createTextNode(m_metadataMap["dc.description"]));
1566
1567 TQDomElement title(mainDocument.createElement("title"));
1568 about.appendChild(title);
1569 title.appendChild(mainDocument.createTextNode(m_metadataMap["dc.title"]));
1570
1571 TQDomElement keyword(mainDocument.createElement("keyword"));
1572 about.appendChild(keyword);
1573 keyword.appendChild(mainDocument.createTextNode(m_metadataMap["abiword.keywords"]));
1574
1575 TQDomElement subject(mainDocument.createElement("subject"));
1576 about.appendChild(subject);
1577 subject.appendChild(mainDocument.createTextNode(m_metadataMap["dc.subject"]));
1578}
1579
1580bool StructureParser::endDocument(void)
1581{
1582 TQDomElement stylesPluralElement=mainDocument.createElement("STYLES");
1583 // insert before <PICTURES>, as <PICTURES> must remain last.
1584 mainDocument.documentElement().insertBefore(stylesPluralElement,m_picturesElement);
1585
1586 kdDebug(30506) << "###### Start Style List ######" << endl;
1587 StyleDataMap::ConstIterator it;
1588
1589 // At first, we put the Normal style
1590 it=styleDataMap.find("Normal");
1591 if (it!=styleDataMap.end())
1592 {
1593 kdDebug(30506) << "\"" << it.key() << "\" => " << it.data().m_props << endl;
1594 TQDomElement styleElement=mainDocument.createElement("STYLE");
1595 stylesPluralElement.appendChild(styleElement);
1596 AddStyle(styleElement, it.key(),it.data(),mainDocument);
1597 }
1598 else
1599 kdWarning(30506) << "No 'Normal' style" << endl;
1600
1601 for (it=styleDataMap.begin();it!=styleDataMap.end();++it)
1602 {
1603 if (it.key()=="Normal")
1604 continue;
1605
1606 kdDebug(30506) << "\"" << it.key() << "\" => " << it.data().m_props << endl;
1607
1608 TQDomElement styleElement=mainDocument.createElement("STYLE");
1609 stylesPluralElement.appendChild(styleElement);
1610
1611 AddStyle(styleElement, it.key(),it.data(),mainDocument);
1612 }
1613 kdDebug(30506) << "###### End Style List ######" << endl;
1614
1615 createDocInfo();
1616
1617 return true;
1618}
1619
1620bool StructureParser::warning(const TQXmlParseException& exception)
1621{
1622 kdWarning(30506) << "XML parsing warning: line " << exception.lineNumber()
1623 << " col " << exception.columnNumber() << " message: " << exception.message() << endl;
1624 return true;
1625}
1626
1627bool StructureParser::error(const TQXmlParseException& exception)
1628{
1629 // A XML error is recoverable, so it is only a KDE warning
1630 kdWarning(30506) << "XML parsing error: line " << exception.lineNumber()
1631 << " col " << exception.columnNumber() << " message: " << exception.message() << endl;
1632 return true;
1633}
1634
1635bool StructureParser::fatalError (const TQXmlParseException& exception)
1636{
1637 kdError(30506) << "XML parsing fatal error: line " << exception.lineNumber()
1638 << " col " << exception.columnNumber() << " message: " << exception.message() << endl;
1639 m_fatalerror=true;
1640 KMessageBox::error(NULL, i18n("An error has occurred while parsing the AbiWord file.\nAt line: %1, column %2\nError message: %3")
1641 .arg(exception.lineNumber()).arg(exception.columnNumber())
1642 .arg( i18n( "TQXml", exception.message().utf8() ) ),
1643 i18n("AbiWord Import Filter"),0);
1644 return false; // Stop parsing now, we do not need further errors.
1645}
1646
1647void StructureParser :: createDocument(void)
1648{
1649 TQDomImplementation implementation;
1650 TQDomDocument doc(implementation.createDocumentType("DOC",
1651 "-//KDE//DTD kword 1.2//EN", "http://www.koffice.org/DTD/kword-1.2.dtd"));
1652
1653 mainDocument=doc;
1654
1655 mainDocument.appendChild(
1656 mainDocument.createProcessingInstruction(
1657 "xml","version=\"1.0\" encoding=\"UTF-8\""));
1658
1659 TQDomElement elementDoc;
1660 elementDoc=mainDocument.createElement("DOC");
1661 elementDoc.setAttribute("xmlns","http://www.koffice.org/DTD/kword");
1662 elementDoc.setAttribute("editor","AbiWord Import Filter");
1663 elementDoc.setAttribute("mime","application/x-kword");
1664 elementDoc.setAttribute( "syntaxVersion", 3 );
1665 mainDocument.appendChild(elementDoc);
1666
1667 TQDomElement element;
1668 element=mainDocument.createElement("ATTRIBUTES");
1669 element.setAttribute("processing",0);
1670 element.setAttribute("standardpage",1);
1671 element.setAttribute("hasHeader",0);
1672 element.setAttribute("hasFooter",0);
1673 //element.setAttribute("unit","mm"); // use KWord default instead
1674 element.setAttribute("tabStopValue",36); // AbiWord has a default of 0.5 inch tab stops
1675 elementDoc.appendChild(element);
1676
1677 // <PAPER> will be partialy changed by an AbiWord <pagesize> element.
1678 // Default paper format of AbiWord is "Letter"
1679 m_paperElement=mainDocument.createElement("PAPER");
1680 m_paperElement.setAttribute("format",PG_US_LETTER);
1681 m_paperElement.setAttribute("width",MillimetresToPoints(KoPageFormat::width (PG_US_LETTER,PG_PORTRAIT)));
1682 m_paperElement.setAttribute("height",MillimetresToPoints(KoPageFormat::height(PG_US_LETTER,PG_PORTRAIT)));
1683 m_paperElement.setAttribute("orientation",PG_PORTRAIT);
1684 m_paperElement.setAttribute("columns",1);
1685 m_paperElement.setAttribute("columnspacing",2);
1686 m_paperElement.setAttribute("hType",0);
1687 m_paperElement.setAttribute("fType",0);
1688 m_paperElement.setAttribute("spHeadBody",9);
1689 m_paperElement.setAttribute("spFootBody",9);
1690 m_paperElement.setAttribute("zoom",100);
1691 elementDoc.appendChild(m_paperElement);
1692
1693 m_paperBordersElement=mainDocument.createElement("PAPERBORDERS");
1694 m_paperBordersElement.setAttribute("left",28);
1695 m_paperBordersElement.setAttribute("top",42);
1696 m_paperBordersElement.setAttribute("right",28);
1697 m_paperBordersElement.setAttribute("bottom",42);
1698 m_paperElement.appendChild(m_paperBordersElement);
1699
1700 framesetsPluralElement=mainDocument.createElement("FRAMESETS");
1701 mainDocument.documentElement().appendChild(framesetsPluralElement);
1702
1703 mainFramesetElement=mainDocument.createElement("FRAMESET");
1704 mainFramesetElement.setAttribute("frameType",1);
1705 mainFramesetElement.setAttribute("frameInfo",0);
1706 mainFramesetElement.setAttribute("visible",1);
1707 mainFramesetElement.setAttribute("name",i18n("Frameset name","Main Text Frameset"));
1708 framesetsPluralElement.appendChild(mainFramesetElement);
1709
1710 TQDomElement frameElementOut=mainDocument.createElement("FRAME");
1711 frameElementOut.setAttribute("left",28);
1712 frameElementOut.setAttribute("top",42);
1713 frameElementOut.setAttribute("bottom",566);
1714 frameElementOut.setAttribute("right",798);
1715 frameElementOut.setAttribute("runaround",1);
1716 // TODO: a few attributes are missing
1717 mainFramesetElement.appendChild(frameElementOut);
1718
1719 // As we are manipulating the document, create a few particular elements
1720 m_ignoreWordsElement=mainDocument.createElement("SPELLCHECKIGNORELIST");
1721 mainDocument.documentElement().appendChild(m_ignoreWordsElement);
1722 m_picturesElement=mainDocument.createElement("PICTURES");
1723 mainDocument.documentElement().appendChild(m_picturesElement);
1724}
1725
1726bool StructureParser::clearStackUntilParagraph(StackItemStack& auxilaryStack)
1727{
1728 for (;;)
1729 {
1730 StackItem* item=structureStack.pop();
1731 switch (item->elementType)
1732 {
1733 case ElementTypeContent:
1734 {
1735 // Push the item on the auxilary stack
1736 auxilaryStack.push(item);
1737 break;
1738 }
1739 case ElementTypeParagraph:
1740 {
1741 // Push back the item on this stack and then stop loop
1742 structureStack.push(item);
1743 return true;
1744 }
1745 default:
1746 {
1747 // Something has gone wrong!
1748 kdError(30506) << "Cannot clear this element: "
1749 << item->itemName << endl;
1750 return false;
1751 }
1752 }
1753 }
1754}
1755
1756ABIWORDImport::ABIWORDImport(KoFilter */*parent*/, const char */*name*/, const TQStringList &) :
1757 KoFilter() {
1758}
1759
1760KoFilter::ConversionStatus ABIWORDImport::convert( const TQCString& from, const TQCString& to )
1761{
1762 if ((to != "application/x-kword") || (from != "application/x-abiword"))
1763 return KoFilter::NotImplemented;
1764
1765 kdDebug(30506)<<"AbiWord to KWord Import filter"<<endl;
1766
1767 StructureParser handler(m_chain);
1768
1769 //We arbitrarily decide that TQt can handle the encoding in which the file was written!!
1770 TQXmlSimpleReader reader;
1771 reader.setContentHandler( &handler );
1772 reader.setErrorHandler( &handler );
1773
1774 //Find the last extension
1775 TQString strExt;
1776 TQString fileIn = m_chain->inputFile();
1777 const int result=fileIn.findRev('.');
1778 if (result>=0)
1779 {
1780 strExt=fileIn.mid(result);
1781 }
1782
1783 kdDebug(30506) << "File extension: -" << strExt << "-" << endl;
1784
1785 TQString strMime; // Mime type of the compressor (default: unknown)
1786
1787 if ((strExt==".gz")||(strExt==".GZ") //in case of .abw.gz (logical extension)
1788 ||(strExt==".zabw")||(strExt==".ZABW")) //in case of .zabw (extension used prioritary with AbiWord)
1789 {
1790 // Compressed with gzip
1791 strMime="application/x-gzip";
1792 kdDebug(30506) << "Compression: gzip" << endl;
1793 }
1794 else if ((strExt==".bz2")||(strExt==".BZ2") //in case of .abw.bz2 (logical extension)
1795 ||(strExt==".bzabw")||(strExt==".BZABW")) //in case of .bzabw (extension used prioritary with AbiWord)
1796 {
1797 // Compressed with bzip2
1798 strMime="application/x-bzip2";
1799 kdDebug(30506) << "Compression: bzip2" << endl;
1800 }
1801
1802 TQIODevice* in = KFilterDev::deviceForFile(fileIn,strMime);
1803
1804 if ( !in )
1805 {
1806 kdError(30506) << "Cannot create device for uncompressing! Aborting!" << endl;
1807 return KoFilter::FileNotFound; // ### TODO: better error?
1808 }
1809
1810 if (!in->open(IO_ReadOnly))
1811 {
1812 kdError(30506) << "Cannot open file for uncompressing! Aborting!" << endl;
1813 delete in;
1814 return KoFilter::FileNotFound;
1815 }
1816
1817 TQXmlInputSource source(in); // Read the file
1818
1819 in->close();
1820
1821 if (!reader.parse( source ))
1822 {
1823 kdError(30506) << "Import: Parsing unsuccessful. Aborting!" << endl;
1824 delete in;
1825 if (!handler.wasFatalError())
1826 {
1827 // As the parsing was stopped for something else than a fatal error, we have not yet get an error message. (Can it really happen?)
1828 KMessageBox::error(NULL, i18n("An error occurred during the load of the AbiWord file: %1").arg(from.data()),
1829 i18n("AbiWord Import Filter"),0);
1830 }
1831 return KoFilter::ParsingError;
1832 }
1833 delete in;
1834
1835 TQCString strOut;
1836 KoStoreDevice* out;
1837
1838 kdDebug(30506) << "Creating documentinfo.xml" << endl;
1839 out=m_chain->storageFile( "documentinfo.xml", KoStore::Write );
1840 if(!out)
1841 {
1842 kdError(30506) << "AbiWord Import unable to open output file! (Documentinfo)" << endl;
1843 KMessageBox::error(NULL, i18n("Unable to save document information."),i18n("AbiWord Import Filter"),0);
1844 return KoFilter::StorageCreationError;
1845 }
1846
1847 //Write the document information!
1848 strOut=handler.getDocInfo().toCString(); // UTF-8
1849 // WARNING: we cannot use KoStore::write(const TQByteArray&) because it writes an extra NULL character at the end.
1850 out->writeBlock(strOut,strOut.length());
1851
1852 kdDebug(30506) << "Creating maindoc.xml" << endl;
1853 out=m_chain->storageFile( "root", KoStore::Write );
1854 if(!out)
1855 {
1856 kdError(30506) << "AbiWord Import unable to open output file! (Root)" << endl;
1857 KMessageBox::error(NULL, i18n("Unable to save main document."),i18n("AbiWord Import Filter"),0);
1858 return KoFilter::StorageCreationError;
1859 }
1860
1861 //Write the document!
1862 strOut=handler.getDocument().toCString(); // UTF-8
1863 // WARNING: we cannot use KoStore::write(const TQByteArray&) because it writes an extra NULL character at the end.
1864 out->writeBlock(strOut,strOut.length());
1865
1866#if 0
1867 kdDebug(30506) << documentOut.toString();
1868#endif
1869
1870 kdDebug(30506) << "Now importing to KWord!" << endl;
1871
1872 return KoFilter::OK;
1873}
1874
1875#include "abiwordimport.moc"
Helpers for the AbiWord import filter.