filters

mswriteexport.cpp
1/* This file is part of the KDE project
2 Copyright (C) 2002-2003 Clarence Dang <dang@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 Version 2 as published by the Free Software Foundation.
7
8 This library is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 Library General Public License Version 2 for more details.
12
13 You should have received a copy of the GNU Library General Public License
14 Version 2 along with this library; see the file COPYING.LIB. If not,
15 write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
16 * Boston, MA 02110-1301, USA.
17*/
18
19#include <limits.h>
20#include <stdio.h>
21#include <stdlib.h>
22#include <string.h>
23
24#include <tqbuffer.h>
25#include <tqcstring.h>
26#include <tqfile.h>
27#include <tqfont.h>
28#include <tqimage.h>
29#include <tqtextcodec.h>
30#include <tqvaluelist.h>
31#include <tqvaluestack.h>
32
33#include <kdebug.h>
34#include <kgenericfactory.h>
35
36#include <KoFilterChain.h>
37#include <kowmfpaint.h>
38
39#include <KWEFStructures.h>
40#include <KWEFBaseWorker.h>
41#include <KWEFKWordLeader.h>
42
43#include "libmswrite.h"
44
45#include "mswriteexport.h"
46
47
48class MSWriteExportFactory : KGenericFactory <MSWriteExport, KoFilter>
49{
50public:
51 MSWriteExportFactory () : KGenericFactory <MSWriteExport, KoFilter> ("kwordmswriteexport")
52 {
53 }
54
55protected:
56 virtual void setupTranslations (void)
57 {
58 TDEGlobal::locale()->insertCatalogue ("kofficefilters");
59 }
60};
61
62K_EXPORT_COMPONENT_FACTORY (libmswriteexport, MSWriteExportFactory ())
63
64
65class WRIDevice : public MSWrite::Device
66{
67private:
68 FILE *m_outfp;
69 long m_outfp_pos, m_outfp_eof;
70
71public:
72 WRIDevice () : m_outfp (NULL), m_outfp_pos (0), m_outfp_eof (0)
73 {
74 }
75
76 virtual ~WRIDevice ()
77 {
78 closeFile ();
79 }
80
81 bool openFile (const char *fileName)
82 {
83 m_outfp = fopen (fileName, "wb");
84 if (!m_outfp)
85 {
86 error (MSWrite::Error::FileError, "could not open file for writing\n");
87 return false;
88 }
89
90 return true;
91 }
92
93 bool closeFile (void)
94 {
95 if (m_outfp)
96 {
97 if (fclose (m_outfp))
98 {
99 error (MSWrite::Error::FileError, "could not close output file\n");
100 return false;
101 }
102
103 m_outfp = NULL;
104 }
105
106 return true;
107 }
108
109 bool read (MSWrite::Byte *, const MSWrite::DWord)
110 {
111 error (MSWrite::Error::InternalError, "reading from an output file?\n");
112 return false;
113 }
114
115 bool write (const MSWrite::Byte *buf, const MSWrite::DWord numBytes)
116 {
117 size_t cwrite = fwrite (buf, 1, numBytes, m_outfp);
118 if (cwrite != numBytes)
119 {
120 error (MSWrite::Error::FileError, "could not write to output file\n");
121 return false;
122 }
123
124 // keep track of where we are up to in the output file and where EOF is
125 m_outfp_pos += numBytes;
126 if (m_outfp_pos > m_outfp_eof)
127 m_outfp_eof = m_outfp_pos;
128
129 return true;
130 }
131
132 bool seek (const long offset, const int whence)
133 {
134 long absloc;
135 switch (whence)
136 {
137 case SEEK_SET:
138 absloc = offset;
139 break;
140 case SEEK_CUR:
141 absloc = m_outfp_pos + offset;
142 break;
143 case SEEK_END:
144 absloc = m_outfp_eof + offset;
145 break;
146 default:
147 error (MSWrite::Error::InternalError, "invalid whence passed to WRIDevice::seek\n");
148 return false;
149 }
150
151 if (absloc > m_outfp_eof)
152 {
153 kdDebug (30509) << "Want to seek to " << absloc
154 << " but EOF is at " << m_outfp_eof
155 << "; so writing " << absloc - m_outfp_eof
156 << " zeros" << endl;
157
158 if (fseek (m_outfp, m_outfp_eof, SEEK_SET))
159 {
160 error (MSWrite::Error::FileError,
161 "could not seek to EOF in output file\n");
162 return false;
163 }
164
165 MSWrite::Byte *zero = new MSWrite::Byte [absloc - m_outfp_eof];
166 if (!zero)
167 {
168 error (MSWrite::Error::OutOfMemory,
169 "could not allocate memory for zeros\n");
170 return false;
171 }
172 memset (zero, 0, absloc - m_outfp_eof);
173 if (!write (zero, absloc - m_outfp_eof)) return false;
174 delete [] zero;
175
176 m_outfp_eof = absloc;
177 m_outfp_pos = absloc;
178 return true;
179 }
180 else
181 {
182 if (fseek (m_outfp, offset, whence) == 0)
183 {
184 m_outfp_pos = absloc;
185 return true;
186 }
187 else
188 {
189 error (MSWrite::Error::FileError, "could not seek output file\n");
190 return false;
191 }
192 }
193 }
194
195 long tell (void)
196 {
197 return ftell (m_outfp);
198 }
199
200 void debug (const char *s)
201 {
202 kdDebug (30509) << s;
203 }
204 void debug (const int i)
205 {
206 kdDebug (30509) << i;
207 }
208 void error (const int errorCode, const char *message,
209 const char * /*file*/ = "", const int /*lineno*/ = 0,
210 MSWrite::DWord /*token*/ = MSWrite::Device::NoToken)
211 {
212 if (errorCode == MSWrite::Error::Warn)
213 kdWarning (30509) << message;
214 else
215 {
216 m_error = errorCode;
217 kdError (30509) << message;
218 }
219 }
220};
221
222
223class KWordMSWriteWorker : public KWEFBaseWorker
224{
225private:
226 WRIDevice *m_device;
227 MSWrite::InternalGenerator *m_generator;
228
229 MSWrite::PageLayout m_pageLayout;
230 MSWrite::Word m_pageHeight, m_pageWidth,
231 m_topMargin, m_leftMargin, m_bottomMargin, m_rightMargin;
232 MSWrite::Word m_pageNumberStart;
233
234 // for charset conversion
235 TQTextCodec *m_codec;
236 TQTextEncoder *m_encoder;
237
238 TQValueList <HeaderData> m_headerData;
239 TQValueList <FooterData> m_footerData;
240
241 int m_headerType, m_footerType;
242 bool m_hasHeader, m_isHeaderOnFirstPage;
243 bool m_hasFooter, m_isFooterOnFirstPage;
244
245 enum inWhatPossiblities
246 {
247 Nothing,
248 Header,
249 Footer,
250 Body
251 } m_inWhat;
252
253public:
254 KWordMSWriteWorker () : m_device (NULL), m_generator (NULL),
255 m_pageHeight (0xFFFF), m_pageWidth (0xFFFF),
256 m_topMargin (0xFFFF), m_leftMargin (0xFFFF),
257 m_bottomMargin (0xFFFF), m_rightMargin (0xFFFF),
258 m_encoder (NULL),
259 m_hasHeader (false), m_hasFooter (false),
260 m_inWhat (Nothing)
261 {
262 // just select windows-1252 until the "Select Encoding" dialog works
263 m_codec = TQTextCodec::codecForName ("CP 1252");
264
265 if (m_codec)
266 m_encoder = m_codec->makeEncoder();
267 else
268 kdWarning (30509) << "Cannot convert to Win Charset!" << endl;
269
270 m_device = new WRIDevice;
271 if (!m_device)
272 {
273 kdError (30509) << "Could not allocate memory for Device" << endl;
274 return;
275 }
276
277 m_generator = new MSWrite::InternalGenerator;
278 if (!m_generator)
279 {
280 m_device->error (MSWrite::Error::OutOfMemory, "could not allocate memory for InternalGenerator\n");
281 return;
282 }
283
284 m_generator->setDevice (m_device);
285 }
286
287 virtual ~KWordMSWriteWorker ()
288 {
289 delete m_generator;
290 delete m_device;
291 delete m_encoder;
292 }
293
294 int getError (void) const
295 {
296 return m_device->bad ();
297 }
298
299 bool doOpenFile (const TQString &outFileName, const TQString &)
300 {
301 // constructor failed?
302 if (!m_device || !m_generator)
303 return false;
304
305 if (!m_device->openFile (TQFile::encodeName (outFileName))) return false;
306
307 return true;
308 }
309
310 bool doCloseFile (void)
311 {
312 if (!m_device->closeFile ()) return false;
313 return true;
314 }
315
316 bool doOpenDocument (void)
317 {
318 kdDebug (30509) << "doOpenDocument ()" << endl;
319
320 // We can't open the document here because we don't yet have
321 // PageLayout * as doFullPaperFormat() and doFullPaperBorders()
322 // haven't been called yet.
323 //
324 // doTrulyOpenDocument truly opens the document and is called by
325 // doOpenBody()
326
327 return true;
328 }
329
330 bool doTrulyOpenDocument (void)
331 {
332 // TODO: Write's UI doesn't allow the user to change Height or Width so
333 // setting it here might not be a good idea...
334 m_pageLayout.setPageHeight (m_pageHeight);
335 m_pageLayout.setPageWidth (m_pageWidth);
336 m_pageLayout.setPageNumberStart (m_pageNumberStart);
337 m_pageLayout.setTopMargin (m_topMargin);
338 m_pageLayout.setLeftMargin (m_leftMargin);
339 m_pageLayout.setTextHeight (m_pageHeight - m_topMargin - m_bottomMargin);
340 m_pageLayout.setTextWidth (m_pageWidth - m_leftMargin - m_rightMargin);
341
342 // TODO: libexport
343 // headerFromTop
344 // footerFromTop
345
346 if (!m_generator->writeDocumentBegin (MSWrite::Format::Write_3_0,
347 &m_pageLayout)) return false;
348
349 return true;
350 }
351
352 bool doCloseDocument (void)
353 {
354 kdDebug (30509) << "doCloseDocument ()" << endl;
355
356 if (!m_generator->writeDocumentEnd (MSWrite::Format::Write_3_0,
357 &m_pageLayout)) return false;
358
359 return true;
360 }
361
362 bool doFullPaperFormat (const int format,
363 const double width, const double height,
364 const int orientation)
365 {
366 kdDebug (30509) << "doFullPaperFormat ("
367 << format << ", "
368 << width << ", "
369 << height << ", "
370 << orientation << ")" << endl;
371
372 // TODO: does "format" or "orientation" matter?
373
374 m_pageHeight = MSWrite::Word (Point2Twip (height));
375 m_pageWidth = MSWrite::Word (Point2Twip (width));
376
377 return true;
378 }
379
380 bool doFullPaperBorders (const double top, const double left,
381 const double bottom, const double right)
382 {
383 kdDebug (30509) << "doFullPaperBorders ("
384 << top << ", "
385 << left << ", "
386 << bottom << ", "
387 << right << ")" << endl;
388
389 m_topMargin = MSWrite::Word (Point2Twip (top));
390 m_leftMargin = MSWrite::Word (Point2Twip (left));
391 m_bottomMargin = MSWrite::Word (Point2Twip (bottom));
392 m_rightMargin = MSWrite::Word (Point2Twip (right));
393
394 return true;
395 }
396
397 bool doVariableSettings (const VariableSettingsData &varSettings)
398 {
399 m_pageNumberStart = MSWrite::Word (varSettings.startingPageNumber);
400
401 kdDebug (30509) << "doVariableSettings pageNumberStart="
402 << m_pageNumberStart << endl;
403 return true;
404 }
405
406 // In Write the header/footer must be the same for every page except that
407 // you can choose to not display it on the first page
408 //
409 // /*This filter aims to be as lossless as possible so if we can't
410 // accommodate the types of headers/footers found in KWord, we at least
411 // print out the paragraphs in the body*/
412 //
413 // Not anymore. Dumping headers & footers in the body didn't
414 // turn out to be that useful, nor was it expected by users.
415 //
416 bool doPageInfo (int headerType, int footerType)
417 {
418 kdDebug (30509) << "doPageInfo (headerType=" << headerType
419 << ", footerType=" << footerType
420 << ")" << endl;
421
422 m_headerType = headerType;
423 switch (headerType)
424 {
425 case 0: // same on all pages
426 case 3: // different on even and odd pages
427 m_isHeaderOnFirstPage = true;
428 break;
429 case 1: // different on first, even and odd pages
430 case 2: // different on first and other pages
431 m_isHeaderOnFirstPage = false;
432 break;
433 default:
434 kdWarning (30509) << "Unknown headerType: " << headerType << endl;
435 m_isHeaderOnFirstPage = false; // just a guess
436 break;
437 }
438
439 m_footerType = footerType;
440 switch (footerType)
441 {
442 case 0: // same on all pages
443 case 3: // different on even and odd pages
444 m_isFooterOnFirstPage = true;
445 break;
446 case 1: // different on first, even and odd pages
447 case 2: // different on first and other pages
448 m_isFooterOnFirstPage = false;
449 break;
450 default:
451 kdWarning (30590) << "Unknown footerType: " << footerType << endl;
452 m_isFooterOnFirstPage = false; // just a guess
453 break;
454 }
455
456 return true;
457 }
458
459 bool isParaListEmpty (const TQValueList <ParaData> &para)
460 {
461 if (para.count () == 1)
462 {
463 if (para.first ().text.isEmpty ())
464 return true;
465 }
466
467 return false;
468 }
469
470 bool doHeader (const HeaderData &header)
471 {
472 kdDebug (30509) << "doHeader (header.page=" << header.page << ")" << endl;
473
474 if (isParaListEmpty (header.para))
475 {
476 kdDebug (30509) << "\tEmpty, ignoring" << endl;
477 return true;
478 }
479
480 #if 0
481 switch (m_headerType)
482 {
483 case 0: // same on all pages
484 if (header.page != HeaderData::PAGE_ALL)
485 return true;
486 break;
487 case 3: // different on even and odd pages
488 if (header.page != HeaderData::PAGE_ODD &&
489 header.page != HeaderData::PAGE_EVEN)
490 return true;
491 break;
492 case 1: // different on first, even and odd pages
493 // accept everything
494 break;
495 case 2: // different on first and other pages
496 if (header.page != HeaderData::PAGE_FIRST &&
497 header.page != HeaderData::PAGE_ODD)
498 return true;
499 break;
500 }
501 #endif
502
503 m_hasHeader = true;
504 m_headerData.push_back (header);
505 return true;
506 }
507
508 bool doFooter (const FooterData &footer)
509 {
510 kdDebug (30509) << "doFooter (footer.page=" << footer.page << ")" << endl;
511
512 if (isParaListEmpty (footer.para))
513 {
514 kdDebug (30509) << "\tEmpty, ignoring" << endl;
515 return true;
516 }
517
518 #if 0
519 switch (m_footerType)
520 {
521 case 0: // same on all pages
522 if (footer.page != FooterData::PAGE_ALL)
523 return true;
524 break;
525 case 3: // different on even and odd pages
526 if (footer.page != FooterData::PAGE_ODD &&
527 footer.page != FooterData::PAGE_EVEN)
528 return true;
529 break;
530 case 1: // different on first, even and odd pages
531 // accept everything
532 break;
533 case 2: // different on first and other pages
534 if (footer.page != FooterData::PAGE_FIRST &&
535 footer.page != FooterData::PAGE_ODD)
536 return true;
537 break;
538 }
539 #endif
540
541 m_hasFooter = true;
542 m_footerData.push_back (footer);
543 return true;
544 }
545
546 bool doOpenBody (void)
547 {
548 kdDebug (30509) << "doOpenBody ()" << endl;
549
550 //
551 // Document Start
552 //
553 if (!doTrulyOpenDocument ()) return false;
554
555
556 //
557 // Footers followed by Headers (in this order)
558 //
559
560 bool wroteFooter = false;
561 m_inWhat = Footer;
562
563 for (TQValueList <FooterData>::Iterator it = m_footerData.begin ();
564 it != m_footerData.end ();
565 it++)
566 {
567 if ((*it).page != FooterData::PAGE_FIRST)
568 {
569 if (!wroteFooter)
570 {
571 if (!m_generator->writeFooterBegin ()) return false;
572 wroteFooter = true;
573 }
574
575 if (!doFullParagraphList ((*it).para)) return false;
576 it = --m_footerData.erase (it);
577 }
578 }
579 if (wroteFooter)
580 if (!m_generator->writeFooterEnd ()) return false;
581
582 bool wroteHeader = false;
583 m_inWhat = Header;
584
585 for (TQValueList <HeaderData>::Iterator it = m_headerData.begin ();
586 it != m_headerData.end ();
587 it++)
588 {
589 if ((*it).page != HeaderData::PAGE_FIRST)
590 {
591 if (!wroteHeader)
592 {
593 if (!m_generator->writeHeaderBegin ()) return false;
594 wroteHeader = true;
595 }
596
597 if (!doFullParagraphList ((*it).para)) return false;
598 it = --m_headerData.erase (it);
599 }
600 }
601 if (wroteHeader)
602 if (!m_generator->writeHeaderEnd ()) return false;
603
604
605 //
606 // Body Start
607 //
608
609 m_inWhat = Body;
610 if (!m_generator->writeBodyBegin ()) return false;
611 // KWord doesn't have a PageTable but we must emit the pageNew
612 // signal at least once
613 if (!m_generator->writePageNew ()) return false;
614
615#if 0
616 // dump remaining header paragraphs at the start of the body
617 for (TQValueList <HeaderData>::Iterator it = m_headerData.begin ();
618 it != m_headerData.end ();
619 it++)
620 {
621 kdDebug (30509) << "BODY START ADDING HEADER: " << (*it).page << endl;
622 if (!doFullParagraphList ((*it).para)) return false;
623 it = --m_headerData.erase (it);
624 }
625
626 // dump remaining footer paragraphs too
627 for (TQValueList <FooterData>::Iterator it = m_footerData.begin ();
628 it != m_footerData.end ();
629 it++)
630 {
631 kdDebug (30509) << "BODY START ADDING FOOTER: " << (*it).page << endl;
632 if (!doFullParagraphList ((*it).para)) return false;
633 it = --m_footerData.erase (it);
634 }
635#endif
636
637 return true;
638 }
639
640 bool doCloseBody (void)
641 {
642 kdDebug (30509) << "doCloseBody ()" << endl;
643
644 if (!m_generator->writeBodyEnd ()) return false;
645
646 return true;
647 }
648
649 // device that can either read from or write to a TQBuffer
650 // (but not both at the same time, please :))
651 class TQBufferDevice : public MSWrite::Device
652 {
653 private:
654 TQBuffer *m_buffer;
655
656 public:
657 TQBufferDevice (TQBuffer *buffer)
658 {
659 m_buffer = buffer;
660 }
661
662 bool read (MSWrite::Byte *buf, const MSWrite::DWord numBytes)
663 {
664 if (m_buffer->readBlock ((char *) buf, (TQ_ULONG) numBytes) != TQ_LONG (numBytes))
665 {
666 error (MSWrite::Error::FileError, "could not read from TQBuffer (not really a FileError)\n");
667 return false;
668 }
669 else
670 return true;
671 }
672
673 bool write (const MSWrite::Byte *buf, const MSWrite::DWord numBytes)
674 {
675 if (m_buffer->writeBlock ((char *) buf, (TQ_ULONG) numBytes) != TQ_LONG (numBytes))
676 {
677 error (MSWrite::Error::FileError, "could not write to TQBuffer (not really a FileError)\n");
678 return false;
679 }
680 else
681 return true;
682 }
683
684 // normally we must write zeros if we seek past EOF
685 // but we know that won't happen :)
686 bool seek (const long offset, const int whence)
687 {
688 long absoffset;
689 switch (whence)
690 {
691 case SEEK_SET:
692 absoffset = offset;
693 break;
694 case SEEK_CUR:
695 absoffset = m_buffer->at () + offset;
696 break;
697 case SEEK_END:
698 absoffset = m_buffer->size () + offset;
699 break;
700 default:
701 error (MSWrite::Error::InternalError, "unknown seek\n");
702 return false;
703 }
704
705 if (absoffset > long (m_buffer->size ()))
706 {
707 error (MSWrite::Error::InternalError, "seek past EOF unimplemented\n");
708 return false;
709 }
710
711 if (!m_buffer->at (absoffset))
712 {
713 error (MSWrite::Error::FileError, "TQBuffer could not seek (not really a FileError)\n");
714 return false;
715 }
716
717 return true;
718 }
719
720 long tell (void)
721 {
722 return long (m_buffer->at ());
723 }
724
725 void debug (const char *s)
726 {
727 kdDebug (30509) << s;
728 }
729 void debug (const int i)
730 {
731 kdDebug (30509) << i;
732 }
733
734 void error (const int errorCode, const char *message,
735 const char * /*file*/ = "", const int /*lineno*/ = 0,
736 MSWrite::DWord /*token*/ = MSWrite::Device::NoToken)
737 {
738 if (errorCode == MSWrite::Error::Warn)
739 kdWarning (30509) << message;
740 else
741 {
742 m_error = errorCode;
743 kdError (30509) << message;
744 }
745 }
746 };
747
748 class WMFRecord : public MSWrite::NeedsDevice
749 {
750 public:
751 static const int s_size = 6;
752
753 protected:
754 MSWrite::Byte m_data [s_size];
755
756 MSWrite::DWord m_size; // record size in Words including everything in this struct
757 MSWrite::Word m_function;
758
759 MSWrite::Short m_args [100]; // "ought to be enough for anybody"
760 int m_argUpto;
761
762 public:
763 WMFRecord () : m_argUpto (0)
764 {
765 }
766
767 WMFRecord (const MSWrite::DWord size, const MSWrite::Word function, MSWrite::Device *device)
768 : MSWrite::NeedsDevice (device),
769 m_size (size), m_function (function),
770 m_argUpto (0)
771 {
772 }
773
774 MSWrite::DWord getSize (void) const { return m_size; }
775 void setSize (const MSWrite::DWord size) { m_size = size; }
776
777 MSWrite::Word getFunction (void) const { return m_function; }
778 void setFunction (const MSWrite::Word function) { m_function = function; }
779
780 void add (const MSWrite::Short arg)
781 {
782 m_args [m_argUpto++] = arg;
783 }
784
785 bool readFromDevice (void)
786 {
787 if (!m_device->readInternal (m_data, 6)) return false;
788 MSWrite::ReadDWord (m_size, m_data + 0);
789 MSWrite::ReadWord (m_function, m_data + 4);
790 printf ("Size (Words): %i Size (Bytes): %i Function: %04X (func=%02X,numArgs=%i)\n",
791 m_size, m_size * sizeof (MSWrite::Word), m_function, m_function & 255, m_function >> 8);
792 #if 1
793 if (m_function == 0 && m_size == 3) // last record
794 return false;
795 #endif
796
797 switch (m_function)
798 {
799 case 0x0103:
800 printf ("\tSetMapMode\n");
801 break;
802 case 0x020c:
803 printf ("\tSetWindowExt\n");
804 break;
805 case 0x020b:
806 printf ("\tSetWindowOrg\n");
807 break;
808 case 0x0b41:
809 printf ("\tDibStretchBlt\n");
810 break;
811 default:
812 printf ("\tUnknown function\n");
813 }
814
815 long offset = m_device->tellInternal ();
816
817 for (int i = 0; i < ((m_function == 0x0b41) ? 10 : (m_function >> 8)); i++)
818 {
819 MSWrite::Byte data [2];
820 if (!m_device->readInternal (data, 2)) return false;
821 MSWrite::ReadShort (m_args [i], data);
822 printf ("\tArg (rev) #%i=%i\n", i, m_args [i]);
823 }
824
825 #if 0
826 // arguments are reversed normally
827 int u = 0;
828 for (int i = (m_function >> 8) - 1; i >= 0; i--, u++)
829 printf ("\tArg #%i=%u\n", u, m_args [i]);
830 #endif
831
832 #if 1
833 if (m_function == 0xb41)
834 {
835 // just curious but what's the infoHeader like?
836 MSWrite::BMP_BitmapInfoHeader bih;
837 bih.setDevice (m_device);
838 if (!bih.readFromDevice ()) return false;
839 }
840 #endif
841
842 m_size *= sizeof (MSWrite::Word); // in Bytes now
843 m_size -= 6; // skip past prefix
844 printf (">>> At: %li Next: %li\n", m_device->tellInternal (), offset + m_size);
845 if (!m_device->seekInternal (offset + m_size, SEEK_SET)) return false;
846 return true;
847 }
848
849 bool writeToDevice (void)
850 {
851 MSWrite::WriteDWord (m_size, m_data + 0);
852 MSWrite::WriteWord (m_function, m_data + 4);
853 if (!m_device->writeInternal (m_data, 6)) return false;
854
855 for (int i = 0; i < ((m_function == 0x0B41) ? 10/*not 11*/ : (m_function >> 8)); i++)
856 {
857 MSWrite::Byte data [2];
858 MSWrite::WriteShort (m_args [i], data);
859 if (!m_device->writeInternal (data, 2)) return false;
860 }
861
862 return true;
863 }
864 };
865
866 // converts a DIB to a Standard WMF
867 bool BMP2WMF (MSWrite::Device &readDevice, MSWrite::Device &writeDevice)
868 {
869 // read BMP's FileHeader
870 MSWrite::BMP_BitmapFileHeader bfh;
871 bfh.setDevice (&readDevice);
872 if (!bfh.readFromDevice ()) return false;
873
874 // WMF bitmap doesn't contain FileHeader
875 MSWrite::DWord totalBytes = bfh.getTotalBytes () - MSWrite::BMP_BitmapFileHeader::s_size;
876
877 // read BMP's InfoHeader to get some info...
878 MSWrite::BMP_BitmapInfoHeader bih;
879 bih.setDevice (&readDevice);
880 if (!bih.readFromDevice ()) return false;
881
882 // get some info about the image
883 MSWrite::Long width = bih.getWidth ();
884 MSWrite::Long height = bih.getHeight ();
885
886
887 // Note: not from LibMSWrite's wmf.cpp
888 kdDebug (30509) << "\t\tBIH: width(pt)=" << width
889 << " height(pt)=" << height
890 << " BPP=" << bih.getBitsPerPixel ()
891 << endl;
892 kdDebug (30509) << "\t\tBIH: xPixelsPerMeter=" << bih.getXPixelsPerMeter ()
893 << " yPixelsPerMeter=" << bih.getYPixelsPerMeter ()
894 << endl;
895
896
897 // write WMF Header
898 MSWrite::WMFHeader wmfHeader;
899 wmfHeader.setDevice (&writeDevice);
900
901 MSWrite::DWord maxRecordSizeBytes
902 = (totalBytes
903 + 10 * sizeof (MSWrite::Word)/*parameters for DibStretchBlt*/
904 + WMFRecord::s_size);
905 wmfHeader.setFileSize ((MSWrite::WMFHeader::s_size
906 + WMFRecord::s_size + 1 * sizeof (MSWrite::Word)/*SetMapMode*/
907 + WMFRecord::s_size + 2 * sizeof (MSWrite::Word)/*SetWindowExt*/
908 + WMFRecord::s_size + 2 * sizeof (MSWrite::Word)/*SetWindowOrg*/
909 + maxRecordSizeBytes/*DibStretchBlt*/
910 + WMFRecord::s_size/*Sentinel*/)
911 / sizeof (MSWrite::Word));
912 wmfHeader.setMaxRecordSize (maxRecordSizeBytes / sizeof (MSWrite::Word));
913 if (!wmfHeader.writeToDevice ()) return false;
914
915 WMFRecord wmfRecordSetMapMode (4/*(Words)*/, 0x0103, &writeDevice);
916 wmfRecordSetMapMode.add (8/*MM_ANISOTROPIC*/);
917 if (!wmfRecordSetMapMode.writeToDevice ()) return false;
918
919 WMFRecord wmfRecordSetWindowExt (5/*(Words)*/, 0x020C, &writeDevice);
920 wmfRecordSetWindowExt.add (-height);
921 wmfRecordSetWindowExt.add (width);
922 if (!wmfRecordSetWindowExt.writeToDevice ()) return false;
923
924 WMFRecord wmfRecordSetWindowOrg (5/*(Words)*/, 0x020B, &writeDevice);
925 wmfRecordSetWindowOrg.add (0);
926 wmfRecordSetWindowOrg.add (0);
927 if (!wmfRecordSetWindowOrg.writeToDevice ()) return false;
928
929 WMFRecord wmfRecordBMP (maxRecordSizeBytes / sizeof (MSWrite::Word),
930 0x0B41/*DibStretchBlt*/,
931 &writeDevice);
932 wmfRecordBMP.add (32); // ?
933 wmfRecordBMP.add (204); // ?
934 wmfRecordBMP.add (height); // src height
935 wmfRecordBMP.add (width); // src width
936 wmfRecordBMP.add (0); // src y
937 wmfRecordBMP.add (0); // src x
938 wmfRecordBMP.add (-height); // dest height
939 wmfRecordBMP.add (width); // dest width
940 wmfRecordBMP.add (0); // dest y
941 wmfRecordBMP.add (0); // dest x
942 if (!wmfRecordBMP.writeToDevice ()) return false;
943
944 // write BMP InfoHeader back to the device
945 bih.setDevice (&writeDevice);
946 if (!bih.writeToDevice ()) return false;
947
948 long left = totalBytes - MSWrite::BMP_BitmapInfoHeader::s_size;
949 while (left)
950 {
951 MSWrite::Byte data [1024];
952 long amountToRead = left > 1024 ? 1024 : left;
953 if (!readDevice.readInternal (data, amountToRead)) return false;
954 if (!writeDevice.writeInternal (data, amountToRead)) return false;
955
956 left -= amountToRead;
957 }
958
959 WMFRecord wmfRecordSentinel (3/*(Words)*/, 0x0000, &writeDevice);
960 if (!wmfRecordSentinel.writeToDevice ()) return false;
961
962 #if 1
963 // bug with Word97?
964 MSWrite::Byte zero = 0;
965 if (!writeDevice.writeInternal (&zero, sizeof (MSWrite::Byte))) return false;
966 #endif
967
968 return true;
969 }
970
971 // all windows measurements depend on there being 72 dots/points per inch
972 static double getDimen72DPI (const int measurement, const int dotsPerMeter)
973 {
974 kdDebug (30509) << "\t\tgetDimen72DPI (measurement=" << measurement
975 << ",dotsPerMeter=" << dotsPerMeter << ")" << endl;
976
977 // Can't get resolution?
978 // Assume that we are already 72dpi
979 if (dotsPerMeter <= 0)
980 return double (measurement);
981
982 // 2834.65 = 100 / 2.54 * 72
983 return double (measurement) * 2834.65 / double (dotsPerMeter);
984 }
985
986 //
987 // Note: if we suffer from a conversion error in this function (and can't
988 // export the image), we _still_ return true, not false because there is
989 // nothing worse than a filter that aborts due to its own incompetence [1]
990 // (don't flame the author please, just blame the function :)). But if we
991 // do suffer from a file-like error, we abort right away because something
992 // bad (memory corruption, internal error...) is happening!
993 //
994 // Why then _do_ we abort on text errors when images tell a thousand
995 // words? Because this saying is wrong and text is probably more
996 // important to the user.
997 //
998 // [1] yes, "worse than an itch you can't scratch"
999 //
1000 bool processImage (const FrameAnchor &frameAnchor,
1001 const MSWrite::FormatParaProperty *paraPropIn,
1002 const MSWrite::FormatCharProperty *charPropIn,
1003 const bool ignoreIndent)
1004 {
1005 kdDebug (30509) << "--------------------------" << endl
1006 << "processImage()" << endl;
1007
1008
1009 // Write supports images in 3 formats:
1010 //
1011 // 1. Monochrome BMP (not very useful)
1012 // 2. OLE (hard to work with and only supported in ver >= 3.1)
1013 // 3. Standard WMF
1014 //
1015 // So we convert all images to WMF for convenience.
1016 // We don't even bother saving Monochrome BMPs "as is" because
1017 // there's no point (just save it in WMF to make life easier)
1018 //
1019 // But a Standard WMF is basically a BMP with some headers/GDI calls
1020 // so the conversion process is like this:
1021 //
1022 // start->WMF->finish
1023 // start->BMP->WMF->finish
1024 // start->???->BMP->WMF->finish
1025 //
1026
1027 double imageActualWidth = -1, imageActualHeight = -1;
1028 MSWrite::DWord imageSize = 0;
1029
1030 TQString imageType;
1031 int pos = frameAnchor.picture.koStoreName.findRev ('.');
1032 if (pos != -1) imageType = frameAnchor.picture.koStoreName.mid (pos).lower ();
1033 kdDebug (30509) << "\timageType: " << imageType << endl;
1034
1035 TQByteArray imageData;
1036 kdDebug (30509) << "\tReading image: " << frameAnchor.picture.koStoreName << endl;
1037 if (!loadSubFile (frameAnchor.picture.koStoreName, imageData))
1038 ErrorAndQuit (MSWrite::Error::FileError, "could not open image from store\n");
1039
1040 // FSM
1041 for (;;)
1042 {
1043 if (imageType == ".wmf")
1044 {
1045 imageSize = imageData.size ();
1046 if (imageActualWidth == -1 && imageActualHeight == -1)
1047 {
1048 // load WMF
1049 KoWmfPaint wmf;
1050 if (!wmf.load (imageData))
1051 {
1052 kdError (30509) << "Could not open WMF - Invalid Format!" << endl;
1053 return true;
1054 }
1055
1056 // get raw dimensions
1057 TQRect dimen = wmf.boundingRect ();
1058 int width = abs (dimen.width ());
1059 int height = abs (dimen.height ());
1060 kdDebug (30509) << "\tRaw WMF dimensions: " << width << "x" << height << endl;
1061
1062 if (wmf.isPlaceable ())
1063 {
1064 kdDebug (30509) << "\tConverting Placeable WMF" << endl;
1065
1066 // convert twip measurements that aren't in 72dpi
1067 int defaultDpi = wmf.defaultDpi ();
1068 if (defaultDpi <= 0)
1069 {
1070 kdWarning (30509) << "Invalid defaultDPI: " << defaultDpi << endl;
1071 defaultDpi = 1440;
1072 }
1073 imageActualWidth = width * 1440 / defaultDpi;
1074 imageActualHeight = height * 1440 / defaultDpi;
1075
1076 // Remove Aldus Placeable WMF Header
1077 for (int i = 0; i < int (imageSize) - 22; i++)
1078 imageData [i] = imageData [i + 22];
1079
1080 imageData.resize (imageSize - 22);
1081 imageSize -= 22;
1082 }
1083 else if (wmf.isEnhanced ())
1084 {
1085 kdError (30509) << "Enhanced WMF unsupported by TQWmf, internal error!" << endl;
1086
1087 return true;
1088 }
1089 // Standard WMF
1090 else
1091 {
1092 kdDebug (30509) << "\tStandard WMF - no conversion required" << endl;
1093
1094 // assume width & height were in 72dpi points
1095 imageActualWidth = Point2Twip (width);
1096 imageActualHeight = Point2Twip (height);
1097 }
1098 }
1099
1100 kdDebug (30509) << "\tNow WMF: width=" << imageActualWidth
1101 << " height=" << imageActualHeight
1102 << " size=" << imageSize
1103 << endl;
1104
1105 // we're done!
1106 break;
1107 }
1108 // TODO: DDB?
1109 else if (imageType == ".bmp")
1110 {
1111 TQImage image (imageData);
1112 if (image.isNull ())
1113 {
1114 kdError (30509) << "TQImage IsNull: Line=" << __LINE__ << endl;
1115 return true;
1116 }
1117
1118 if (imageActualWidth == -1 && imageActualHeight == -1)
1119 {
1120 imageActualWidth = Point2Twip (getDimen72DPI (image.width (), image.dotsPerMeterX ()));
1121 imageActualHeight = Point2Twip (getDimen72DPI (image.height (), image.dotsPerMeterY ()));
1122 }
1123
1124 kdDebug (30509) << "\tNow BMP: width=" << imageActualWidth
1125 << " height=" << imageActualHeight
1126 << " size=" << imageSize
1127 << endl;
1128
1129 TQByteArray imageWMF;
1130 // input device
1131 TQBuffer inBuffer (imageData);
1132 inBuffer.open (IO_ReadOnly);
1133 TQBufferDevice inDevice (&inBuffer);
1134
1135 // output device
1136 TQBuffer outBuffer (imageWMF);
1137 outBuffer.open (IO_WriteOnly);
1138 TQBufferDevice outDevice (&outBuffer);
1139
1140 // BMP --> WMF
1141 if (!BMP2WMF (inDevice, outDevice))
1142 {
1143 kdError (30509) << "BMP to WMF conversion error" << endl;
1144 return true;
1145 }
1146
1147 outBuffer.close ();
1148 inBuffer.close ();
1149 imageData = imageWMF.copy ();
1150
1151 imageType = ".wmf";
1152 }
1153 else
1154 {
1155 if (imageActualWidth == -1 && imageActualHeight == -1)
1156 {
1157 TQImage image (imageData);
1158 if (image.isNull())
1159 {
1160 kdError (30509) << "TQImage isNull: Line=" << __LINE__ << endl;
1161 return true;
1162 }
1163
1164 imageActualWidth = Point2Twip (getDimen72DPI (image.width (), image.dotsPerMeterX ()));
1165 imageActualHeight = Point2Twip (getDimen72DPI (image.height (), image.dotsPerMeterY ()));
1166 }
1167
1168 kdDebug (30509) << "\tForeign format: width=" << imageActualWidth
1169 << " height=" << imageActualHeight
1170 << " size=" << imageSize
1171 << endl;
1172
1173 TQByteArray imageBMP;
1174 // input device
1175 TQBuffer inBuffer (imageData);
1176 inBuffer.open (IO_ReadOnly);
1177
1178 // read foreign image
1179 TQImageIO imageIO (&inBuffer, NULL);
1180 if (!imageIO.read ())
1181 {
1182 kdError (30509) << "Could not read foreign format" << endl;
1183 return true;
1184 }
1185
1186 // output device
1187 TQBuffer outBuffer (imageBMP);
1188 outBuffer.open (IO_WriteOnly);
1189
1190 // write BMP
1191 imageIO.setIODevice (&outBuffer);
1192 imageIO.setFormat ("BMP");
1193 if (!imageIO.write ())
1194 {
1195 kdError (30509) << "Could not convert to BMP" << endl;
1196 return true;
1197 }
1198
1199 outBuffer.close ();
1200 inBuffer.close ();
1201 imageData = imageBMP.copy ();
1202
1203 imageType = ".bmp";
1204 }
1205 }
1206
1207
1208 kdDebug (30509) << "\tActual dimensions: width=" << imageActualWidth
1209 << " height=" << imageActualHeight << endl;
1210
1211 kdDebug (30509) << "\tKOffice position: left=" << frameAnchor.frame.left
1212 << " right=" << frameAnchor.frame.right
1213 << " top=" << frameAnchor.frame.top
1214 << " bottom=" << frameAnchor.frame.bottom
1215 << endl;
1216
1217 kdDebug (30509) << "\tIndent=" << MSWrite::Word (Point2Twip (frameAnchor.frame.left)) - m_leftMargin << endl;
1218 if (ignoreIndent)
1219 kdDebug (30509) << "\t\tIgnoring indent - already exported at least one image in a KWord paragraph" << endl;
1220
1221 double displayedWidth = Point2Twip (frameAnchor.frame.right - frameAnchor.frame.left + 1);
1222 double displayedHeight = Point2Twip (frameAnchor.frame.bottom - frameAnchor.frame.top + 1);
1223
1224 kdDebug (30509) << "\tdisplayedWidth=" << displayedWidth
1225 << " displayedHeight=" << displayedHeight
1226 << endl;
1227
1228
1229 //
1230 // Start writing out the image now
1231 //
1232 // Note: here, we can start returning false again because the errors
1233 // won't be conversion-related
1234 //
1235
1236 MSWrite::Image image;
1237 image.setIsWMF (true);
1238 if (!ignoreIndent)
1239 {
1240 if (paraPropIn->getAlignment () != MSWrite::Alignment::Centre)
1241 image.setIndent (MSWrite::Word (Point2Twip (frameAnchor.frame.left)) - m_leftMargin);
1242 else
1243 {
1244 // TODO: what is the image offset relative to (it's not always rel. to the left margin)?
1245 kdDebug (30509) << "\tCentred paragraph, cannot position image" << endl;
1246 }
1247 }
1248
1249 image.setOriginalWidth (imageActualWidth);
1250 image.setOriginalHeight (imageActualHeight);
1251 image.setDisplayedWidth (displayedWidth);
1252 image.setDisplayedHeight (displayedHeight);
1253 image.setExternalImageSize (imageSize);
1254
1255 MSWrite::FormatParaProperty paraProp; paraProp = *paraPropIn;
1256 paraProp.setIsObject (true);
1257 paraProp.setLeftIndent (0); // not necessary but...
1258 if (!m_generator->writeParaInfoBegin (&paraProp, NULL, &image))
1259 return false;
1260
1261 // yes, images have character formatting as well
1262 // (character formatting _must_ cover entire document)
1263 // (but I think it's ignored)
1264 MSWrite::FormatCharProperty charProp; charProp = *charPropIn;
1265 if (!m_generator->writeCharInfoBegin (&charProp))
1266 return false;
1267
1268 // actually write image
1269 if (!m_generator->writeBinary ((const MSWrite::Byte *) (const char *) imageData.data (), imageSize)) return false;
1270
1271 // 2nd argument endOfParagraph is ignored by InternalGenerator so
1272 // there's no need to specify it
1273 if (!m_generator->writeCharInfoEnd (&charProp, true))
1274 return false;
1275;
1276 if (!m_generator->writeParaInfoEnd (&paraProp, NULL, &image))
1277 return false;
1278
1279
1280 kdDebug (30509) << "processImage() successful!" << endl
1281 << "==========================" << endl
1282 << endl
1283 << endl;
1284 return true;
1285 }
1286
1287 bool processTable (const Table &table)
1288 {
1289 // just dump the table out for now (no layout)
1290 for (TQValueList <TableCell>::ConstIterator it = table.cellList.begin ();
1291 it != table.cellList.end ();
1292 it++)
1293 {
1294 if (!doFullParagraphList (*(*it).paraList)) return false;
1295 }
1296
1297 return true;
1298 }
1299
1300 bool processCounter (const CounterData &counter)
1301 {
1302 //kdDebug (30509) << "processCounter(counter.text=" << counter.text << ")" << endl;
1303
1304 if (!counter.text.isEmpty ())
1305 {
1306 // isn't this wonderful? :)
1307 if (!processText (counter.text)) return false;
1308 if (!processText (" ")) return false;
1309 }
1310
1311 return true;
1312 }
1313
1314 #ifndef NDEBUG
1315 //#define KMF_DEBUG_FONT
1316 #endif
1317 void processFormatData (MSWrite::FormatCharProperty &charProp,
1318 const TextFormatting &f)
1319 {
1320 if (!f.fontName.isEmpty ())
1321 {
1322 // create new Font with Name
1323 MSWrite::Font font ((const MSWrite::Byte *) (const char *) f.fontName.utf8 ());
1324 #ifdef KMF_DEBUG_FONT
1325 kdDebug (30509) << "FontName " << f.fontName << endl;
1326 #endif
1327
1328 // get Font Family
1329 TQFont TQTFontInfo (f.fontName);
1330 switch (TQTFontInfo.styleHint ())
1331 {
1332 case TQFont::Serif:
1333 #ifdef KMF_DEBUG_FONT
1334 kdDebug (30509) << "FontFamily Serif" << endl;
1335 #endif
1336 font.setFamily (MSWrite::Font::Roman);
1337 break;
1338 case TQFont::SansSerif:
1339 #ifdef KMF_DEBUG_FONT
1340 kdDebug (30509) << "FontFamily SansSerif" << endl;
1341 #endif
1342 font.setFamily (MSWrite::Font::Swiss);
1343 break;
1344 case TQFont::Courier:
1345 #ifdef KMF_DEBUG_FONT
1346 kdDebug (30509) << "FontFamily Courier" << endl;
1347 #endif
1348 font.setFamily (MSWrite::Font::Modern);
1349 break;
1350 case TQFont::OldEnglish:
1351 #ifdef KMF_DEBUG_FONT
1352 kdDebug (30509) << "FontFamily OldEnglish" << endl;
1353 #endif
1354 font.setFamily (MSWrite::Font::Decorative);
1355 break;
1356 default:
1357 #ifdef KMF_DEBUG_FONT
1358 kdDebug (30509) << "FontFamily DontKnow" << endl;
1359 #endif
1360 // it's either DontCare or MSWrite::Font::Script
1361 font.setFamily (MSWrite::Font::DontCare);
1362 break;
1363 }
1364
1365 charProp.setFont (&font);
1366 }
1367 if (f.fontSize > 0) charProp.setFontSize (f.fontSize);
1368
1369 charProp.setIsItalic (f.italic);
1370 charProp.setIsUnderlined (f.underline); // TODO: underlineWord
1371 charProp.setIsBold (f.weight > (50/*normal*/ + 75/*bold*/) / 2);
1372
1373 switch (f.verticalAlignment)
1374 {
1375 case 0: // normal
1376 charProp.setIsNormalPosition ();
1377 break;
1378 case 1: // subscript
1379 charProp.setIsSubscript ();
1380 break;
1381 case 2: // superscript
1382 charProp.setIsSuperscript ();
1383 break;
1384 }
1385
1386 // TODO: fontAttribute;
1387 }
1388
1389 static MSWrite::Word getClosestLineSpacing (const double points)
1390 {
1391 const double twips = Point2Twip (points);
1392
1393 #if 1
1394 if (twips < double ((MSWrite::LineSpacing::Single + MSWrite::LineSpacing::OneAndAHalf) / 2))
1395 return MSWrite::LineSpacing::Single;
1396 else if (twips < double ((MSWrite::LineSpacing::OneAndAHalf + MSWrite::LineSpacing::Double) / 2))
1397 return MSWrite::LineSpacing::OneAndAHalf;
1398 else
1399 return MSWrite::LineSpacing::Double;
1400 #else // or do we want a non-"standard" linespacing?
1401 return MSWrite::Word (twips);
1402 #endif
1403 }
1404
1405 bool doFullParagraphList (const TQValueList <ParaData> &paraList)
1406 {
1407 for (TQValueList <ParaData>::ConstIterator it = paraList.begin ();
1408 it != paraList.end ();
1409 it ++)
1410 {
1411 if (!doFullParagraph (*it)) return false;
1412 }
1413
1414 return true;
1415 }
1416
1417 bool doFullParagraph (const ParaData &paraData)
1418 {
1419 return doFullParagraph (paraData.text,
1420 paraData.layout,
1421 paraData.formattingList);
1422 }
1423
1424 bool doFullParagraph (const TQString &paraText,
1425 const LayoutData &layout,
1426 const ValueListFormatData &paraFormatDataList)
1427 {
1428 MSWrite::FormatParaProperty paraProp;
1429
1430 if (m_inWhat == Body)
1431 paraProp.setIsNormalParagraph (true);
1432 else
1433 {
1434 if (m_inWhat == Header)
1435 {
1436 paraProp.setIsHeader (true);
1437 paraProp.setIsOnFirstPage (m_isHeaderOnFirstPage);
1438 }
1439 else if (m_inWhat == Footer)
1440 {
1441 paraProp.setIsFooter (true);
1442 paraProp.setIsOnFirstPage (m_isFooterOnFirstPage);
1443 }
1444 }
1445
1446 paraProp.setIsText (true);
1447
1448 // Alignment
1449 if (!layout.alignment.isEmpty ())
1450 {
1451 if (layout.alignment == "left")
1452 // quite useless since MSWrite::Alignment::Left is the default anyway
1453 paraProp.setAlignment (MSWrite::Alignment::Left);
1454 else if (layout.alignment == "right")
1455 paraProp.setAlignment (MSWrite::Alignment::Right);
1456 else if (layout.alignment == "center")
1457 paraProp.setAlignment (MSWrite::Alignment::Center);
1458 else if (layout.alignment == "justify")
1459 paraProp.setAlignment (MSWrite::Alignment::Justify);
1460 else
1461 kdWarning (30509) << "Unknown Alignment: " << layout.alignment << endl;
1462 }
1463
1464 // Indentation
1465 if (layout.indentFirst) paraProp.setLeftIndentFirstLine (MSWrite::Short (Point2Twip (layout.indentFirst)));
1466 if (layout.indentLeft >= 0) paraProp.setLeftIndent (MSWrite::Word (Point2Twip (layout.indentLeft)));
1467 if (layout.indentRight >= 0) paraProp.setRightIndent (MSWrite::Word (Point2Twip (layout.indentRight)));
1468 #if 0
1469 kdDebug (30509) << "Indent: " << Point2Twip (layout.indentFirst) << " "
1470 << Point2Twip (layout.indentLeft) << " "
1471 << Point2Twip (layout.indentRight) << endl;
1472 #endif
1473
1474 // Line Spacing
1475 MSWrite::Word lineSpacing = MSWrite::LineSpacing::Normal;
1476 switch (layout.lineSpacingType)
1477 {
1478 case LayoutData::LS_SINGLE:
1479 lineSpacing = MSWrite::LineSpacing::Normal;
1480 break;
1481 case LayoutData::LS_ONEANDHALF:
1482 lineSpacing = MSWrite::LineSpacing::OneAndAHalf;
1483 break;
1484 case LayoutData::LS_DOUBLE:
1485 lineSpacing = MSWrite::LineSpacing::Double;
1486 break;
1487 case LayoutData::LS_CUSTOM:
1488 case LayoutData::LS_FIXED:
1489 case LayoutData::LS_ATLEAST:
1490 lineSpacing = getClosestLineSpacing (layout.lineSpacing);
1491 break;
1492 case LayoutData::LS_MULTIPLE:
1493 break;
1494 default:
1495 kdWarning (30509) << "unknown lineSpacingType \'" << layout.lineSpacingType << "\'" << endl;
1496 }
1497 paraProp.setLineSpacing (lineSpacing);
1498
1499 // Tabs are a Document Property, not a Paragraph Property, in Write, yet are stored for each paragraph.
1500 // It seems that Write applies the 1st paragraph's Tabulator settings to the _entire_ document
1501 // Word97 and KWord, however, will treat them like a Paragraph Property
1502 int numTabs = 0;
1503 for (TabulatorList::ConstIterator tabIt = layout.tabulatorList.begin ();
1504 tabIt != layout.tabulatorList.end ();
1505 tabIt++)
1506 {
1507 MSWrite::FormatParaPropertyTabulator tab;
1508
1509 // Write's UI only supports 12 as opposed to the 14 supposedly
1510 // supported in the file so let's play it safe and quit when
1511 // we reach 12
1512 // Actually, KWord's UI also only supports 12 so this should never be true
1513 if (numTabs >= 12)
1514 {
1515 kdWarning (30509) << "Write does not support more 12 tabulators, not writing out all tabulators" << endl;
1516 break;
1517 }
1518
1519 // Write only supports Decimal and Left tabs
1520 // TODO: KOffice 1.3 alignchar (modify libexport)
1521 if ((*tabIt).m_type == 3 /* && (*tabIt).m_alignchar == '.' */)
1522 tab.setIsDecimal ();
1523 else
1524 tab.setIsNormal ();
1525
1526 tab.setIndent (MSWrite::Word (Point2Twip ((*tabIt).m_ptpos)));
1527
1528 // int m_filling;
1529 // double m_width;
1530 if ((*tabIt).m_filling != TabulatorData::TF_NONE)
1531 kdWarning (30509) << "Write does not support Tabulator Filling" << endl;
1532
1533 paraProp.addTabulator (&tab);
1534 numTabs++;
1535 }
1536
1537 // TODO: double marginTop; // space before the paragraph (a negative value means invalid)
1538 // TODO: double marginBottom; // space after the paragraph (a negative value means invalid)
1539
1540 // TODO: TQString styleName;
1541 // TODO: TQString styleFollowing;
1542
1543 if (!m_generator->writeParaInfoBegin (&paraProp)) return false;
1544
1545 // get this paragraph's "default formatting"
1546 MSWrite::FormatCharProperty charPropDefault;
1547 processFormatData (charPropDefault, layout.formatData.text);
1548
1549 MSWrite::DWord uptoByte = 0; // relative to start of KWord paragraph
1550 MSWrite::DWord numBytes = paraText.length (); // relative to start of KWord paragraph
1551
1552 bool startOfWRIParagraph = true;
1553 bool exportedAtLeastOneImage = false; // ...from the KWord paragraph
1554
1555 // empty paragraph
1556 if (numBytes == 0)
1557 {
1558 //kdDebug (30509) << "Outputting empty paragraph!" << endl;
1559
1560 // write default character property start
1561 if (!m_generator->writeCharInfoBegin (&charPropDefault)) return false;
1562
1563 // page break at start of paragraph?
1564 if (layout.pageBreakBefore)
1565 if (!m_generator->writePageBreak ()) return false;
1566
1567 // counter data
1568 processCounter (layout.counter);
1569
1570 // end of line
1571 if (!m_generator->writeCarriageReturn ()) return false;
1572 if (!m_generator->writeNewLine (true/*end of paragraph*/)) return false;
1573
1574 // page break at end of paragraph?
1575 if (layout.pageBreakAfter)
1576 if (!m_generator->writePageBreak ()) return false;
1577
1578 // write default character property end
1579 if (!m_generator->writeCharInfoEnd (&charPropDefault, true)) return false;
1580 }
1581 else
1582 {
1583 for (ValueListFormatData::ConstIterator formatIt = paraFormatDataList.begin ();
1584 formatIt != paraFormatDataList.end ();
1585 formatIt++)
1586 {
1587 bool textSegment = true;
1588
1589 // apply local <FORMAT> tag on top of "default formatting"
1590 MSWrite::FormatCharProperty charProp; charProp = charPropDefault;
1591 processFormatData (charProp, (*formatIt).text);
1592
1593 if (!m_generator->writeCharInfoBegin (&charProp)) return false;
1594
1595 if (uptoByte == 0)
1596 {
1597 // page break at start of paragraph?
1598 if (layout.pageBreakBefore)
1599 if (!m_generator->writePageBreak ()) return false;
1600
1601 // counter data
1602 processCounter (layout.counter);
1603 }
1604
1605 // yes, this is slightly premature but it doesn't matter
1606 // ... just be careful when using uptoByte
1607 uptoByte += (*formatIt).len;
1608
1609 switch ((*formatIt).id)
1610 {
1611 case 0: // none?
1612 ErrorAndQuit (MSWrite::Error::InternalError, "Format ID = 0\n");
1613 case 1: // text
1614 if (!processText (paraText.mid ((*formatIt).pos, (*formatIt).len)))
1615 /*uptoByte == numBytes))*/
1616 return false;
1617
1618 startOfWRIParagraph = false;
1619 break;
1620 case 2: // picture (deprecated)
1621 m_device->error (MSWrite::Error::Warn, "Picture (deprecated) unsupported\n");
1622 break;
1623 case 3: // tabulator (deprecated)
1624 m_device->error (MSWrite::Error::Warn, "Tabulator (deprecated) unsupported\n");
1625 break;
1626 case 4: // variable
1627 {
1628 bool justPrintText = true;
1629
1630 // Page Number / Number of Pages
1631 if ((*formatIt).variable.m_type == 4 &&
1632 (*formatIt).variable.isPageNumber () &&
1633 m_inWhat != Body/*Write replaces it with '*' in the body*/)
1634 {
1635 if (!m_generator->writeCharInfoEnd (&charProp)) return false;
1636 charProp.setIsPageNumber (true);
1637 // if you don't do this it will print out the character literally (char 1)
1638 if (!m_generator->writeCharInfoBegin (&charProp)) return false;
1639
1640 // only variable Write supports (and only in headers/footers)
1641 // if you don't write out char 1, Write will not treat it as a
1642 // variable anchor and will print the character out literally
1643 if (!m_generator->writePageNumber ()) return false;
1644
1645 if (!m_generator->writeCharInfoEnd (&charProp)) return false;
1646 charProp.setIsPageNumber (false);
1647 if (!m_generator->writeCharInfoBegin (&charProp)) return false;
1648
1649 justPrintText = false;
1650 }
1651
1652 if (justPrintText)
1653 {
1654 if (!processText ((*formatIt).variable.m_text))
1655 return false;
1656 }
1657
1658 startOfWRIParagraph = false;
1659 break;
1660 }
1661 case 5: // footnote (KOffice 1.1)
1662 m_device->error (MSWrite::Error::Warn, "Footnote unsupported\n");
1663 break;
1664 case 6: // anchor for frame
1665 //
1666 // Write does not support inline frames so:
1667 //
1668 // - we end the current paragraph
1669 // - dump the inline frame in a paragraph of its own
1670 // - continue with a new paragraph
1671 //
1672
1673 if (!startOfWRIParagraph)
1674 {
1675 kdDebug (30509) << "Writing CRLF to end text paragraph" << endl;
1676
1677 // If you don't have CRLF at the end of the text
1678 // paragraph, Write will think that the next paragraph
1679 // (the image) is part of the text...
1680 if (!m_generator->writeCarriageReturn ()) return false;
1681 if (!m_generator->writeNewLine (true/*end of paragraph*/)) return false;
1682 }
1683 else
1684 kdDebug (30509) << "Inline frame is anchored at start of paragraph, no CRLF" << endl;
1685
1686 if (!m_generator->writeCharInfoEnd (&charProp)) return false;
1687 if (!m_generator->writeParaInfoEnd (&paraProp)) return false;
1688
1689
1690 if ((*formatIt).frameAnchor.type == 6)
1691 {
1692 kdDebug (30509) << "Table detected" << endl;
1693
1694 // this will make its own paragraph(s)...
1695 processTable ((*formatIt).frameAnchor.table);
1696
1697 // HACK: inline tables are flushed to the left and right
1698 // margins, despite being inline, hence the next image
1699 // indent will be sensible and should not be ignored.
1700 kdDebug (30509) << "Table hack: resetting image-ignore-indent flag" << endl;
1701 exportedAtLeastOneImage = false;
1702 }
1703 else if ((*formatIt).frameAnchor.type == 2)
1704 {
1705 kdDebug (30509) << "Image detected" << endl;
1706
1707 // this will make its own paragraph...
1708 if (!processImage ((*formatIt).frameAnchor, &paraProp, &charProp,
1709 exportedAtLeastOneImage)) return false;
1710
1711 exportedAtLeastOneImage = true;
1712 }
1713 else
1714 kdWarning (30509) << "Unknown type of anchor: " << (*formatIt).frameAnchor.type << endl;
1715
1716
1717 // recontinue paragraph
1718 if (!m_generator->writeParaInfoBegin (&paraProp)) return false;
1719 startOfWRIParagraph = true;
1720 if (!m_generator->writeCharInfoBegin (&charProp)) return false;
1721
1722 textSegment = false;
1723 break;
1724 }
1725
1726 if (uptoByte == numBytes)
1727 {
1728 if (textSegment)
1729 {
1730 // end of line
1731 if (!m_generator->writeCarriageReturn ()) return false;
1732 if (!m_generator->writeNewLine (true/*end of paragraph*/)) return false;
1733 }
1734
1735 // page break at end of paragraph?
1736 if (layout.pageBreakAfter)
1737 if (!m_generator->writePageBreak ()) return false;
1738 }
1739
1740 if (!m_generator->writeCharInfoEnd (&charProp, uptoByte == numBytes)) return false;
1741 }
1742 }
1743
1744 if (!m_generator->writeParaInfoEnd (&paraProp)) return false;
1745 //if (numBytes) kdDebug (30509) << "Just Output " << uptoByte << "/" << numBytes << " with text \'" << paraText.utf8 () << "\'" << endl;
1746
1747 return true;
1748 }
1749
1750 template <class dtype>
1751 dtype min (const dtype a, const dtype b, const dtype c)
1752 {
1753 if (a <= b && a <= c) return a;
1754 if (b <= a && b <= c) return b;
1755 return c;
1756 }
1757
1758 #ifndef NDEBUG
1759 //#define DEBUG_PROCESS_TEXT
1760 #endif
1761 bool processText (const TQString &stringUnicode)
1762 {
1763 //
1764 // Look out for characters in the string and emit signals as appropriate:
1765 //
1766 // 1 pageNumber (already taken care of as a variable)
1767 // 10 newLine
1768 // 13 carriageReturn (TODO: Oh no! Can't happen!)
1769 // 12 pageBreak (TODO: we are in real trouble: this can actually happen without forcing a new paragraph!)
1770 // 31 optionalHyphen
1771 // ? text
1772 //
1773
1774 int softHyphen = -2, nonBreakingSpace = -2, newLine = -2;
1775
1776 int upto = 0;
1777 int stringUnicodeLength = stringUnicode.length ();
1778 while (upto < stringUnicodeLength)
1779 {
1780 //
1781 // look for KWord's special characters as defined in the DTD
1782 //
1783
1784 if (softHyphen == -2)
1785 {
1786 softHyphen = stringUnicode.find (TQChar (0xAD), upto);
1787 if (softHyphen == -1) softHyphen = INT_MAX;
1788 }
1789
1790 if (nonBreakingSpace == -2)
1791 {
1792 nonBreakingSpace = stringUnicode.find (TQChar (0xA0), upto);
1793 if (nonBreakingSpace == -1) nonBreakingSpace = INT_MAX;
1794 }
1795
1796 if (newLine == -2)
1797 {
1798 newLine = stringUnicode.find (TQChar ('\n'), upto);
1799 if (newLine == -1) newLine = INT_MAX;
1800 }
1801
1802 // look for the closest one
1803 int specialLocation = min (softHyphen, nonBreakingSpace, newLine);
1804
1805 // get substring (either to the end of the original string or before
1806 // the next closest special character, if any)
1807 int length = stringUnicodeLength - upto;
1808 if (specialLocation != INT_MAX)
1809 length = specialLocation - upto;
1810 TQString substring = stringUnicode.mid (upto, length);
1811
1812 #ifdef DEBUG_PROCESS_TEXT
1813 kdDebug (30509) << "Parent string: upto=" << upto
1814 << ",length=" << stringUnicode.length () << endl;
1815 kdDebug (30509) << "Child string: length=" << length
1816 << " (specialLoc=" << specialLocation << ")" << endl;
1817 #endif
1818
1819 //
1820 // convert substring to windows-1251
1821 //
1822
1823 TQCString stringWin;
1824
1825 // there is a codec, therefore there is an encoder...
1826 if (m_codec)
1827 {
1828 int len; // don't overwrite length, we need it later
1829
1830 // convert from Unicode (UTF8)
1831 stringWin = m_encoder->fromUnicode (substring, len = length);
1832 }
1833 else
1834 {
1835 // output a plain string still in wrong Character Set
1836 // (hopefully the user won't notice)
1837 stringWin = substring.utf8 ();
1838 }
1839
1840
1841 // output encoded text
1842 if (!m_generator->writeText ((const MSWrite::Byte *) (const char *) stringWin))
1843 return false;
1844
1845 upto += length;
1846
1847 // special character?
1848 if (specialLocation != INT_MAX)
1849 {
1850 #ifdef DEBUG_PROCESS_TEXT
1851 kdDebug (30509) << "Found special character!" << endl;
1852 #endif
1853
1854 // output special character
1855 if (specialLocation == softHyphen)
1856 {
1857 #ifdef DEBUG_PROCESS_TEXT
1858 kdDebug (30509) << "\tSoft Hyphen" << endl;
1859 #endif
1860 if (!m_generator->writeOptionalHyphen ()) return false;
1861 softHyphen = -2;
1862 }
1863 else if (specialLocation == nonBreakingSpace)
1864 {
1865 #ifdef DEBUG_PROCESS_TEXT
1866 kdDebug (30509) << "\tNon-breaking Space" << endl;
1867 #endif
1868 // don't think Write supports nonBreakingSpace
1869 if (!m_generator->writeText ((const MSWrite::Byte *) " ")) return false;
1870 nonBreakingSpace = -2;
1871 }
1872 else if (specialLocation == newLine)
1873 {
1874 #ifdef DEBUG_PROCESS_TEXT
1875 kdDebug (30509) << "\tNew Line" << endl;
1876 #endif
1877 // \r\n, not just \n
1878 if (!m_generator->writeCarriageReturn ()) return false;
1879 if (!m_generator->writeNewLine (true/*InternalGenerator doesn't care*/)) return false;
1880 newLine = -2;
1881 }
1882 else
1883 {
1884 ErrorAndQuit (MSWrite::Error::InternalError, "simply impossible specialLocation\n");
1885 }
1886
1887 // skip past special character
1888 upto++;
1889 }
1890 } // while (upto < stringUnicodeLength) {
1891
1892 return true;
1893 }
1894};
1895
1896
1897MSWriteExport::MSWriteExport (KoFilter *, const char *, const TQStringList &)
1898 : KoFilter()
1899{
1900}
1901
1902MSWriteExport::~MSWriteExport ()
1903{
1904}
1905
1906KoFilter::ConversionStatus MSWriteExport::convert (const TQCString &from, const TQCString &to)
1907{
1908 kdDebug (30509) << "MSWriteExport $Date: 2006-02-12 19:28:12 +0100 (Sun, 12 Feb 2006) $ using LibMSWrite "
1909 << MSWrite::Version << endl;
1910
1911 if (to != "application/x-mswrite" || from != "application/x-kword")
1912 {
1913 kdError (30509) << "Internal error! Filter not implemented?" << endl;
1914 return KoFilter::NotImplemented;
1915 }
1916
1917 KWordMSWriteWorker *worker = new KWordMSWriteWorker;
1918 if (!worker)
1919 {
1920 kdError (30509) << "Could not allocate memory for worker" << endl;
1921 return KoFilter::OutOfMemory;
1922 }
1923
1924 KWEFKWordLeader *leader = new KWEFKWordLeader (worker);
1925 if (!leader)
1926 {
1927 kdError (30509) << "Could not allocate memory for leader" << endl;
1928 delete worker;
1929 return KoFilter::OutOfMemory;
1930 }
1931
1932 KoFilter::ConversionStatus ret = leader->convert (m_chain, from, to);
1933 int errorCode = worker->getError ();
1934
1935 delete leader;
1936 delete worker;
1937
1938 // try to return somewhat more meaningful errors than KoFilter::StupidError
1939 // for the day that KOffice actually reports them to the user properly
1940 switch (errorCode)
1941 {
1942 case MSWrite::Error::Ok:
1943 kdDebug (30509) << "Returning error code " << ret << endl;
1944 return ret; // not KoFilter::OK in case KWEFKWordLeader wants to report something
1945
1946 case MSWrite::Error::Warn:
1947 kdDebug (30509) << "Error::Warn" << endl;
1948 return KoFilter::InternalError; // warnings should _never_ set m_error
1949
1950 case MSWrite::Error::InvalidFormat:
1951 kdDebug (30509) << "Error::InvalidFormat" << endl;
1952 return KoFilter::InternalError; // how can the file I'm _writing_ be of an invalid format?
1953
1954 case MSWrite::Error::OutOfMemory:
1955 kdDebug (30509) << "Error::OutOfMemory" << endl;
1956 return KoFilter::OutOfMemory;
1957
1958 case MSWrite::Error::InternalError:
1959 kdDebug (30509) << "Error::InternalError" << endl;
1960 return KoFilter::InternalError;
1961
1962 case MSWrite::Error::Unsupported:
1963 kdDebug (30509) << "Error::Unsupported" << endl;
1964 return KoFilter::InternalError;
1965
1966 case MSWrite::Error::FileError:
1967 kdDebug (30509) << "Error::FileError" << endl;
1968 return KoFilter::CreationError;
1969 }
1970
1971 kdWarning (30509) << "Unknown error" << endl;
1972 return KoFilter::StupidError;
1973}
1974
1975#include <mswriteexport.moc>
TQString text
text at the time of saving (no formatting info though)
Base worker of the libexport system.
virtual bool doCloseBody(void)
Like HTML's </BODY>.
virtual bool doOpenBody(void)
Like HTML's <BODY>.
double lineSpacing
Space between lines in pt, if lineSpacingType==0.
TabulatorList tabulatorList
List of tabulators.
double indentFirst
indentation correction of first line (can be negative!)
double indentRight
right indentation (a negative value means invalid)
double indentLeft
left indentation (a negative value means invalid)
TQString alignment
left, right, center, justify
int lineSpacingType
0=custom, 10=one line, 15=one and half lines, 20= 2 lines
bool underline
Any underline? (If true, we do not know if simple or double or what else).