filters

docbookexport.cpp
1/* This file is part of the KDE project
2 Copyright (C) 1998, 1999 Reginald Stadlbauer <reggie@kde.org>
3 Copyright (c) 2000 ID-PRO Deutschland GmbH. All rights reserved.
4 Contact: Wolf-Michael Bolle <Wolf-Michael.Bolle@GMX.de>
5 Copyright (C) 2001, 2002 Nicolas GOUTTE <goutte@kde.org>
6 Copyright (c) 2001 IABG mbH. All rights reserved.
7 Contact: Wolf-Michael Bolle <Bolle@IABG.de>
8
9 This library is free software; you can redistribute it and/or
10 modify it under the terms of the GNU Library General Public
11 License as published by the Free Software Foundation; either
12 version 2 of the License, or (at your option) any later version.
13
14 This library is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 Library General Public License for more details.
18
19 You should have received a copy of the GNU Library General Public License
20 along with this library; see the file COPYING.LIB. If not, write to
21 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22 * Boston, MA 02110-1301, USA.
23*/
24
25#include <docbookexport.h>
26#include <docbookexport.moc>
27#include <kdebug.h>
28#include <kgenericfactory.h>
29#include <tqdir.h>
30#include <tqdom.h>
31
32#include <KoFilterChain.h>
33#include <KWEFStructures.h>
34#include <KWEFUtil.h>
35#include <KWEFKWordLeader.h>
36#include <ProcessDocument.h>
37#include <KWEFBaseWorker.h>
38
39typedef KGenericFactory<DocBookExport, KoFilter> DocBookExportFactory;
40K_EXPORT_COMPONENT_FACTORY( libdocbookexport, DocBookExportFactory( "kofficefilters" ) )
41
42DocBookExport::DocBookExport ( KoFilter *,
43 const char *,
44 const TQStringList & ) : KoFilter ()
45{
46}
47
48
49#define INSERT_TABLE_IN_PARA 1 // Do not change this!
50#define TABLES_WITH_TITLES 0
51
52
53struct DocData
54{
55 bool article;
56 bool head1;
57 bool head2;
58 bool head3;
59 bool head4;
60 bool bulletList;
61 bool enumeratedList;
62 bool alphabeticalList;
63};
64
65
66class DocBookWorker : public KWEFBaseWorker
67{
68 public:
69 DocBookWorker (void) {}
70
71 bool doOpenDocument ( void );
72 bool doCloseDocument ( void );
73
74 bool doOpenFile ( const TQString &, const TQString & );
75 bool doCloseFile ( void );
76
77 bool doOpenBody ( void );
78 bool doCloseBody ( void );
79
80 bool doFullDocumentInfo ( const KWEFDocumentInfo & );
81
82 bool doFullDocument ( const TQValueList<ParaData> &paraList );
83
84 private:
85 void ProcessPictureData ( const Picture &picture );
86
87 void ProcessTableData ( const Table &table );
88
89 void ProcessParagraphData ( const ParaData &para,
90 TQString tag );
91
92 void CloseItemizedList ( void );
93 void CloseEnumeratedList ( void );
94 void CloseAlphabeticalList ( void );
95 void CloseLists ( void );
96
97 void CloseHead4 ( void );
98 void CloseHead3 ( void );
99 void CloseHead2 ( void );
100 void CloseHead1AndArticle ( void );
101
102 void OpenArticleUnlessHead1 ( void );
103
104 TQString outputText;
105 DocData docData;
106 TQFile *fileOut;
107 TQString exportFileName;
108};
109
110
111// ProcessPictureData () takes the available picture data, makes a
112// copy of the image file into *.sgml.d/pictures/*.* from KoStore
113// pictures/*.*, and creates the necessary DocBook tags for it.
114
115void DocBookWorker::ProcessPictureData ( const Picture &picture )
116{
117 TQByteArray byteArray;
118
119 if ( loadSubFile ( picture.koStoreName,byteArray ) )
120 {
121 TQFileInfo fileInfo (exportFileName);
122 TQDir dir ( fileInfo.dirPath () );
123 TQString subDirName = fileInfo.fileName () + ".d";
124
125 if ( !dir.exists (subDirName) )
126 {
127 dir.mkdir (subDirName);
128 }
129
130 dir.cd (subDirName);
131
132 if ( !dir.exists ("pictures") )
133 {
134 dir.mkdir ("pictures");
135 }
136
137 TQString pictureFileName = dir.filePath (picture.koStoreName);
138
139 TQFile pictureFile (pictureFileName);
140
141 if ( pictureFile.open (IO_WriteOnly) )
142 {
143 pictureFile.writeBlock ( byteArray, byteArray.size () );
144
145 TQString pictureText;
146
147#if TABLES_WITH_TITLES
148 pictureText += "<FIGURE>\n";
149
150#if 1
151 pictureText += " <TITLE>" + picture.name + "</TITLE>\n";
152#else
153 pictureText += " <TITLE></TITLE>\n";
154#endif
155#else
156 pictureText += "<INFORMALFIGURE>\n";
157#endif
158 pictureText += " <MEDIAOBJECT>\n";
159 pictureText += " <IMAGEOBJECT>\n";
160 pictureText += " <IMAGEDATA FILEREF=\"" + pictureFileName + "\">\n";
161 pictureText += " </IMAGEOBJECT>\n";
162 pictureText += " </MEDIAOBJECT>\n";
163#if TABLES_WITH_TITLES
164 pictureText += "</FIGURE>\n";
165#else
166 pictureText += "</INFORMALFIGURE>\n";
167#endif
168
169 outputText += pictureText;
170 }
171 else
172 {
173 kdError (30507) << "Unable to open picture file " << pictureFileName << "!" << endl;
174
175 pictureFile.close ();
176 }
177 }
178 else
179 {
180 kdError (30507) << "Unable to open KoStore file " << picture.koStoreName << "!" << endl;
181 }
182}
183
184
185// ProcessTableData () takes the table data and creates the necessary
186// DocBook tags for it.
187
188void DocBookWorker::ProcessTableData ( const Table &table )
189{
190#if 0
191 kdError (30507) << "DEBUG: ProcessTableData ()" << endl;
192#endif
193
194 TQString tableText;
195
196#if TABLES_WITH_TITLES
197 tableText += "<TABLE>\n";
198
199#if 1
200 tableText += " <TITLE>" + table.name + "</TITLE>\n";
201#else
202 tableText += " <TITLE></TITLE>\n";
203#endif
204#else
205 tableText += "<INFORMALTABLE>\n";
206#endif
207
208 tableText += " <TGROUP COLS=\"" + TQString::number (table.cols) + "\">\n";
209 tableText += " <TBODY>\n";
210
211 int currentRow = -1;
212
213 TQValueList<TableCell>::ConstIterator cellIt;
214
215 for ( cellIt = table.cellList.begin ();
216 cellIt != table.cellList.end ();
217 cellIt++ )
218 {
219 if ( (*cellIt).row != currentRow )
220 {
221 if ( currentRow >= 0 )
222 {
223 tableText += " </ROW>\n";
224 }
225
226 currentRow = (*cellIt).row;
227
228 tableText += " <ROW>\n";
229 }
230
231 TQString tmpBuf;
232 tmpBuf = outputText;
233 outputText = "";
234
235 doFullDocument ( *(*cellIt).paraList );
236
237 tableText += " <ENTRY>" + outputText.remove ( '\n' ) + "</ENTRY>\n";
238
239 outputText = tmpBuf;
240 }
241
242 if ( currentRow >= 0 )
243 {
244 tableText += " </ROW>\n";
245 }
246
247 tableText += " </TBODY>\n";
248 tableText += " </TGROUP>\n";
249
250#if TABLES_WITH_TITLES
251 tableText += "</TABLE>\n";
252#else
253 tableText += "</INFORMALTABLE>\n";
254#endif
255
256 outputText += tableText;
257
258#if 0
259 kdError (30507) << "DEBUG: ProcessTableData (): " << tableText << endl;
260#endif
261}
262
263
264// ProcessParagraphData () mangles the pure text through the
265// formatting information stored in the FormatData list and prints it
266// out to the export file.
267
268void DocBookWorker::ProcessParagraphData ( const ParaData &para,
269 TQString tag )
270{
271#if !INSERT_TABLE_IN_PARA
272 TQValueList<AnchoredInsert> tmpAnchoredInsertList;
273#endif
274
275 outputText += "<" + tag + ">";
276
277 if ( para.text.length () > 0 )
278 {
279 ValueListFormatData::ConstIterator formattingIt;
280
281 for ( formattingIt = para.formattingList.begin ();
282 formattingIt != para.formattingList.end ();
283 formattingIt++ )
284 {
285 switch ( (*formattingIt).id )
286 {
287 case 1: // texts
288 {
289
290 bool fixedFont = false;
291
292 if ( (*formattingIt).text.fontName == "courier" ||
293 (*formattingIt).text.fontName == "Courier" ||
294 (*formattingIt).text.fontName == "Courier New" )
295 {
296 fixedFont = true;
297 }
298
299 if ( (*formattingIt).text.italic && !para.layout.formatData.text.italic )
300 {
301 outputText += "<EMPHASIS>";
302 }
303
304 if ( (*formattingIt).text.weight > para.layout.formatData.text.weight )
305 {
306 outputText += "<EMPHASIS ROLE=bold>";
307 }
308
309 if ( fixedFont )
310 {
311 outputText += "<LITERAL>";
312 }
313
314 outputText += EscapeXmlText (para.text.mid ( (*formattingIt).pos, (*formattingIt).len ));
315
316 if ( fixedFont )
317 {
318 outputText += "</LITERAL>";
319 }
320
321 if ( (*formattingIt).text.weight > para.layout.formatData.text.weight )
322 {
323 outputText += "</EMPHASIS>";
324 }
325
326 if ( (*formattingIt).text.italic && !para.layout.formatData.text.italic )
327 {
328 outputText += "</EMPHASIS>";
329 }
330 }
331 break;
332
333 case 4: // variables
334 if (9 == (*formattingIt).variable.m_type)
335 {
336 // A link (TODO: verify the code, as the tags were copied from a XML DocBook file)
337 outputText += "<ULINK URL=\"";
338 outputText += EscapeXmlText ( (*formattingIt).variable.getHrefName(), true, true );
339 outputText += "\">";
340 outputText += EscapeXmlText ( (*formattingIt).variable.getLinkName() );
341 outputText += "</ULINK>";
342 }
343 else
344 {
345 outputText += EscapeXmlText ( (*formattingIt).variable.m_text );
346 }
347 break;
348
349 case 6: // anchors
350#if 0
351 kdError (30507) << "Processing anchor " << (*formattingIt).frameAnchor.name << endl;
352#endif
353
354#if INSERT_TABLE_IN_PARA
355 outputText += "</" + tag + ">\n";
356
357#if 0
358 anchoredInsertList.prepend ( AnchoredInsert ( (*formattingIt).frameAnchor.name,
359 outputText.length () ) );
360#endif
361
362 switch ( (*formattingIt).frameAnchor.type )
363 {
364 case 2:
365 ProcessPictureData ( (*formattingIt).frameAnchor.picture );
366 break;
367
368 case 6:
369 ProcessTableData ( (*formattingIt).frameAnchor.table );
370 break;
371
372 default:
373 kdError (30507) << "Unhandled anchor type "
374 << (*formattingIt).frameAnchor.type << "!" << endl;
375 }
376#else
377 tmpAnchoredInsertList << AnchoredInsert ( (*formattingIt).frameAnchor.name, 0 );
378#endif
379
380 outputText += "<" + tag + ">";
381
382 break;
383
384 default:
385 kdError (30507) << "Unhandled format id "
386 << (*formattingIt).id << "!" << endl;
387 }
388 }
389 }
390
391 outputText += "</" + tag + ">\n";
392
393#if !INSERT_TABLE_IN_PARA
394 TQValueList<AnchoredInsert>::Iterator anchoredInsert;
395
396 for ( anchoredInsert = tmpAnchoredInsertList.begin ();
397 anchoredInsert != tmpAnchoredInsertList.end ();
398 anchoredInsert++ )
399 {
400 (*anchoredInsert).pos = outputText.length ();
401 anchoredInsertList.prepend (*anchoredInsert);
402 }
403#endif
404}
405
406
407void DocBookWorker::CloseItemizedList ( void )
408{
409 if ( docData.bulletList )
410 {
411 outputText += "</ITEMIZEDLIST> <!-- End of Bullet List -->\n";
412 docData.bulletList = false;
413 }
414}
415
416
417void DocBookWorker::CloseEnumeratedList ( void )
418{
419 if ( docData.enumeratedList )
420 {
421 outputText += "</ORDEREDLIST> <!-- End of Enumerated List -->\n";
422 docData.enumeratedList = false;
423 }
424}
425
426
427void DocBookWorker::CloseAlphabeticalList ( void )
428{
429 if ( docData.alphabeticalList )
430 {
431 outputText += "</ORDEREDLIST> <!-- End of Alphabetical List -->\n";
432 docData.alphabeticalList = false;
433 }
434}
435
436
437void DocBookWorker::CloseLists ( void )
438{
439 CloseItemizedList ();
440 CloseEnumeratedList ();
441 CloseAlphabeticalList ();
442}
443
444
445void DocBookWorker::CloseHead4 ( void )
446{
447 CloseLists ();
448
449 if ( docData.head4 )
450 {
451 outputText += "</SECTION> <!-- End of Head 4 -->\n";
452 docData.head4 = false;
453 }
454}
455
456
457void DocBookWorker::CloseHead3 ( void )
458{
459 CloseHead4 ();
460
461 if ( docData.head3 )
462 {
463 outputText += "</SECTION> <!-- End of Head 3 -->\n";
464 docData.head3 = false;
465 }
466}
467
468
469void DocBookWorker::CloseHead2 ( void )
470{
471 CloseHead3 ();
472
473 if ( docData.head2 )
474 {
475 outputText += "</SECTION> <!-- End of Head 2 -->\n";
476 docData.head2 = false;
477 }
478}
479
480
481void DocBookWorker::CloseHead1AndArticle ( void )
482{
483 CloseHead2 ();
484
485 if ( docData.article )
486 {
487 outputText += "</ARTICLE>\n";
488 docData.article = false;
489 }
490
491 if ( docData.head1 )
492 {
493 outputText += "</CHAPTER> <!-- End of Head 1 -->\n";
494 docData.head1 = false;
495 }
496}
497
498
499void DocBookWorker::OpenArticleUnlessHead1 ( void )
500{
501 if ( !docData.head1 && !docData.article )
502 {
503 outputText += "<ARTICLE> <!-- Begin of Article -->\n";
504 docData.article = true;
505 }
506}
507
508
509bool DocBookWorker::doFullDocument ( const TQValueList<ParaData> &paraList )
510{
511#if 0
512 kdError (30507) << "doFullDocument () - Begin" << endl;
513#endif
514
515 TQValueList<ParaData>::ConstIterator paraIt;
516 TQValueList<ParaData>::ConstIterator end(paraList.end ());
517 for ( paraIt = paraList.begin (); paraIt != end ; ++paraIt )
518 {
519 switch ( (*paraIt).layout.counter.numbering )
520 {
522 switch ( (*paraIt).layout.counter.style )
523 {
524 case CounterData::STYLE_CUSTOMBULLET:
525 case CounterData::STYLE_CIRCLEBULLET:
526 case CounterData::STYLE_SQUAREBULLET:
527 case CounterData::STYLE_DISCBULLET:
528 case CounterData::STYLE_CUSTOM:
529 case CounterData::STYLE_NONE:
530 CloseEnumeratedList ();
531 CloseAlphabeticalList ();
532
533 OpenArticleUnlessHead1 ();
534
535 if ( !docData.bulletList )
536 {
537 outputText += "<ITEMIZEDLIST> <!-- Begin of Bullet List -->\n";
538 docData.bulletList = true;
539 }
540
541 outputText += "<LISTITEM>\n";
542 ProcessParagraphData (*paraIt, "PARA" );
543 outputText += "</LISTITEM>\n";
544 break;
545
546 case CounterData::STYLE_NUM:
547 case CounterData::STYLE_ROM_NUM_L:
548 case CounterData::STYLE_ROM_NUM_U:
549 CloseItemizedList ();
550 CloseAlphabeticalList ();
551
552 OpenArticleUnlessHead1 ();
553
554 if ( !docData.enumeratedList )
555 {
556 outputText += "<ORDEREDLIST NUMERATION=\"Arabic\"> <!-- Begin of Enumerated List -->\n";
557 docData.enumeratedList = true;
558 }
559
560 outputText += "<LISTITEM>\n";
561 ProcessParagraphData (*paraIt, "PARA" );
562 outputText += "</LISTITEM>\n";
563 break;
564
565 case CounterData::STYLE_ALPHAB_L:
566 case CounterData::STYLE_ALPHAB_U:
567 CloseItemizedList ();
568 CloseEnumeratedList ();
569
570 OpenArticleUnlessHead1 ();
571
572 if ( !docData.alphabeticalList )
573 {
574 outputText += "<ORDEREDLIST NUMERATION=\"Loweralpha\"> <!-- Begin of Alphabetical List -->\n";
575 docData.alphabeticalList = true;
576 }
577
578 outputText += "<LISTITEM>\n";
579 ProcessParagraphData (*paraIt, "PARA" );
580 outputText += "</LISTITEM>\n";
581 break;
582
583 default:
584 kdError (30507) << "Unknown counter style " << (*paraIt).layout.counter.style << "!" << endl;
585 CloseLists ();
586 OpenArticleUnlessHead1 ();
587 ProcessParagraphData (*paraIt, "PARA" );
588 }
589
590 break;
591
593 switch ( (*paraIt).layout.counter.depth )
594 {
595 case 0:
596 CloseHead1AndArticle ();
597
598 outputText += "<CHAPTER> <!-- Begin of Head 1 -->\n";
599 docData.head1 = true;
600
601 ProcessParagraphData (*paraIt, "TITLE" );
602 break;
603
604 case 1:
605 CloseHead2 ();
606
607 outputText += "<SECTION> <!-- Begin of Head 2 -->\n";
608 docData.head2 = true;
609
610 ProcessParagraphData (*paraIt, "TITLE" );
611 break;
612
613 case 2:
614 CloseHead3 ();
615
616 outputText += "<SECTION> <!-- Begin of Head 3 -->\n";
617 docData.head3 = true;
618
619 ProcessParagraphData (*paraIt, "TITLE" );
620 break;
621
622 case 3:
623 CloseHead4 ();
624
625 outputText += "<SECTION> <!-- Begin of Head 4 -->\n";
626 docData.head4 = true;
627
628 ProcessParagraphData (*paraIt, "TITLE" );
629 break;
630
631 default:
632 kdError (30507) << "Unexpected chapter depth " << (*paraIt).layout.counter.depth << "!" << endl;
633 CloseLists ();
634 OpenArticleUnlessHead1 ();
635 ProcessParagraphData (*paraIt, "PARA" );
636 }
637
638 break;
639
640 default:
641 CloseLists ();
642 OpenArticleUnlessHead1 ();
643 ProcessParagraphData (*paraIt, "PARA" );
644 }
645 }
646
647#if 0
648 kdError (30507) << "doFullDocument () - End" << outputText << endl;
649#endif
650 return true;
651}
652
653
654bool DocBookWorker::doOpenDocument ( void )
655{
656 outputText += "<!DOCTYPE BOOK PUBLIC \"-//OASIS//DTD DocBook V3.1//EN\">\n";
657 outputText += "<BOOK>\n";
658
659 return true;
660}
661
662
663bool DocBookWorker::doOpenBody ( void )
664{
665 docData.article = false;
666 docData.head1 = false;
667 docData.head2 = false;
668 docData.head3 = false;
669 docData.head4 = false;
670 docData.bulletList = false;
671 docData.enumeratedList = false;
672 docData.alphabeticalList = false;
673
674 return true;
675}
676
677
678bool DocBookWorker::doCloseBody ( void )
679{
680 CloseHead1AndArticle ();
681
682 return true;
683}
684
685
686bool DocBookWorker::doCloseDocument ( void )
687{
688 outputText += "</BOOK>\n";
689
690 return true;
691}
692
693
694bool DocBookWorker::doOpenFile ( const TQString &filenameOut, const TQString & /*to*/ )
695{
696 fileOut = new TQFile(filenameOut);
697
698 if ( !fileOut )
699 {
700 kdError(30507) << "No output file! Aborting!" << endl;
701 return false;
702 }
703
704 if ( !fileOut->open (IO_WriteOnly) )
705 {
706 kdError(30507) << "Unable to open output file!" << endl;
707
708 fileOut->close ();
709 delete fileOut;
710 fileOut = NULL;
711
712 return false;
713 }
714
715 exportFileName=filenameOut;
716
717 return true;
718}
719
720
721bool DocBookWorker::doCloseFile ( void )
722{
723 if ( !fileOut ) return true;
724
725 // As a TQChar can be transformed into many bytes,
726 // we need to use TQCString::length instead of TQString::length
727 TQCString cstr = outputText.local8Bit ();
728 fileOut->writeBlock ( cstr, cstr.length () );
729
730 fileOut->close ();
731 delete fileOut;
732 fileOut = NULL;
733
734 return true;
735}
736
737
738// ProcessInfoData () creates a subtag to the current tag level with
739// text that was created earlier by ProcessInfoData () and adds it to
740// the current tag level. It is used by ProcessDocumentIntoTag () to
741// assemble the diverse levels of information of the BOOKINFO tag.
742
743static void ProcessInfoData ( const TQString &tagName,
744 const TQString & tagText,
745 TQString &outputText)
746{
747 if ( tagText.length () )
748 {
749 outputText += "<" + tagName + ">" + tagText + "</" + tagName + ">\n";
750 }
751}
752
753
754bool DocBookWorker::doFullDocumentInfo ( const KWEFDocumentInfo &docInfo )
755{
756 TQString bookInfoText;
757 TQString abstractText;
758 TQString authorText;
759 TQString affiliationText;
760 TQString addressText;
761
762 ProcessInfoData ( "TITLE", docInfo.title, bookInfoText );
763 ProcessInfoData ( "PARA", docInfo.abstract, abstractText );
764 ProcessInfoData ( "SURNAME", docInfo.fullName, authorText );
765 ProcessInfoData ( "JOBTITLE", docInfo.jobTitle, affiliationText );
766 ProcessInfoData ( "ORGNAME", docInfo.company, affiliationText );
767 ProcessInfoData ( "STREET", docInfo.street, addressText );
768 ProcessInfoData ( "CITY", docInfo.city, addressText );
769 ProcessInfoData ( "POSTCODE", docInfo.postalCode, addressText );
770 ProcessInfoData ( "COUNTRY", docInfo.country, addressText );
771 ProcessInfoData ( "EMAIL", docInfo.email, addressText );
772 ProcessInfoData ( "PHONE", docInfo.telephone, addressText );
773 ProcessInfoData ( "FAX", docInfo.fax, addressText );
774
775 ProcessInfoData ( "ADDRESS", addressText, affiliationText );
776 ProcessInfoData ( "AFFILIATION", affiliationText, authorText );
777 ProcessInfoData ( "ABSTRACT", abstractText, bookInfoText );
778 ProcessInfoData ( "AUTHOR", authorText, bookInfoText );
779 ProcessInfoData ( "BOOKINFO", bookInfoText, outputText );
780
781 return true;
782}
783
784
785KoFilter::ConversionStatus DocBookExport::convert( const TQCString& from, const TQCString& to )
786{
787#if 0
788 kdError (30507) << "to = " << to << ", from = " << from << endl;
789#endif
790
791 if ( to != "text/sgml" && to != "text/docbook" || from != "application/x-kword" )
792 {
793 return KoFilter::NotImplemented;
794 }
795
796#if 1
797 kdError (30507) << "let's get on with it" << endl;
798#endif
799
800 DocBookWorker worker;
801 KWEFKWordLeader leader (&worker);
802 leader.convert (m_chain, from, to);
803
804#if 1
805 kdError (30507) << "done here" << endl;
806#endif
807
808 return KoFilter::OK;
809}
@ NUM_LIST
Numbered as a list item.
@ NUM_CHAPTER
Numbered as a heading.
Base worker of the libexport system.
virtual bool doCloseBody(void)
Like HTML's </BODY>.
virtual bool doOpenBody(void)
Like HTML's <BODY>.
This class hold a table.