1
/* This file is part of the KDE project
2
Copyright (C) 2001-2003 Clarence Dang <dang@kde.org>
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.
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.
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., 59 Temple Place - Suite 330,
16
Boston, MA 02111-1307, USA.
20
//#define DEBUG_XML_OUTPUT
30
#include <qtextcodec.h>
31
#include <qtextstream.h>
34
#include <kgenericfactory.h>
36
#include <koFilterChain.h>
40
#include "libmswrite.h"
42
#include "ImportDialog.h"
43
#include "mswriteimport.h"
46
class MSWriteImportFactory : KGenericFactory <MSWriteImport, KoFilter>
49
MSWriteImportFactory () : KGenericFactory <MSWriteImport, KoFilter> ("kwordmswriteimport")
54
virtual void setupTranslations (void)
56
KGlobal::locale()->insertCatalogue ("kwordmswritefilter");
60
K_EXPORT_COMPONENT_FACTORY (libmswriteimport, MSWriteImportFactory ())
64
// Device that reads from .WRI file
67
class WRIDevice : public MSWrite::Device
73
WRIDevice () : m_infp (NULL)
82
bool openFile (const char *fileName)
84
m_infp = fopen (fileName, "rb");
87
error (MSWrite::Error::FileError, "could not open file for reading\n");
100
error (MSWrite::Error::FileError, "could not close input file\n");
110
bool read (MSWrite::Byte *buf, const MSWrite::DWord numBytes)
112
if (fread (buf, 1, (size_t) numBytes, m_infp) != (size_t) numBytes)
114
error (MSWrite::Error::FileError, "could not read from input file\n");
121
bool write (const MSWrite::Byte *, const MSWrite::DWord)
123
error (MSWrite::Error::InternalError, "writing to an input file?\n");
127
bool seek (const long offset, const int whence)
129
if (fseek (m_infp, offset, whence))
131
error (MSWrite::Error::InternalError, "could not seek input file\n");
140
return ftell (m_infp);
143
void debug (const char *s)
145
kdDebug (30509) << s;
148
void debug (const int i)
150
kdDebug (30509) << i;
153
void error (const int errorCode, const char *message,
154
const char * /*file*/ = "", const int /*lineno*/ = 0,
155
MSWrite::DWord /*tokenValue*/ = NoToken)
157
if (errorCode == MSWrite::Error::Warn)
158
kdWarning (30509) << message;
162
kdError (30509) << message;
169
// Generator that creates the KWord file
172
class KWordGenerator : public MSWrite::Generator, public MSWrite::NeedsDevice
175
// KoStore can only have 1 file open at a time
176
// so we store objects in this structure temporarily
180
WRIObject (const WRIObject &rhs);
183
MSWrite::Byte *m_data;
184
MSWrite::DWord m_dataLength;
185
MSWrite::DWord m_dataUpto;
186
QString m_nameInStore;
188
WRIObject () : m_data (NULL), m_dataLength (0), m_dataUpto (0)
197
WRIObject operator= (const WRIObject &rhs)
201
m_dataLength = rhs.m_dataLength;
202
m_dataUpto = rhs.m_dataUpto;
203
m_nameInStore = rhs.m_nameInStore;
207
m_data = new MSWrite::Byte [m_dataLength];
209
memcpy (m_data, rhs.m_data, m_dataLength);
210
// remember to check m_data before use
219
// page/margin dimensions
220
int m_pageWidth, m_pageHeight;
221
int m_left, m_right, m_top, m_bottom; // describing border of Text Frameset (position, not magnitude)
222
int m_leftMargin, m_rightMargin, m_topMargin, m_bottomMargin;
223
int m_headerFromTop, m_footerFromTop;
224
bool m_hasHeader, m_isHeaderOnFirstPage;
225
bool m_hasFooter, m_isFooterOnFirstPage;
226
bool m_writeHeaderFirstTime, m_writeFooterFirstTime;
230
enum inWhatPossiblities
238
int m_startingPageNumber;
240
KoFilterChain *m_chain;
241
KoStoreDevice *m_outfile;
243
// for charset conversion
245
QTextDecoder *m_decoder;
247
// import options (compensate for differences between KWord and MS Write)
248
bool m_simulateLineSpacing;
249
bool m_simulateImageOffset;
252
QString m_formatOutput;
253
int m_charInfoCountStart, m_charInfoCountLen;
254
bool m_pageBreak, m_needAnotherParagraph;
255
int m_pageBreakOffset;
256
int m_lineSpacingFromAbove;
262
QString m_objectFrameset;
264
MSWrite::List <WRIObject> m_objectList;
266
double m_objectHorizOffset;
269
MSWriteImport *m_koLink;
271
// XML output that is held back until after "Text Frameset 1" is output
272
// (i.e. header & footer must come after the Body in KWord)
274
QString m_heldOutput;
276
void delayOutput (const bool yes)
281
bool delayOutputFlush (void)
283
QCString strUtf8 = m_heldOutput.utf8 ();
284
int strLength = strUtf8.length ();
286
if (m_outfile->writeBlock (strUtf8, strLength) != strLength)
287
ErrorAndQuit (MSWrite::Error::FileError, "could not write delayed output\n");
294
KWordGenerator () : m_hasHeader (false), m_isHeaderOnFirstPage (false),
295
m_hasFooter (false), m_isFooterOnFirstPage (false),
296
m_writeHeaderFirstTime (true), m_writeFooterFirstTime (true),
299
m_simulateLineSpacing (false),
300
m_simulateImageOffset (true),
301
m_pageBreak (false), m_needAnotherParagraph (false),
302
m_lineSpacingFromAbove (0),
307
// just select windows-1252 until the "Select Encoding" dialog works
308
m_codec = QTextCodec::codecForName ("CP 1252");
311
m_decoder = m_codec->makeDecoder();
313
kdWarning (30509) << "Cannot convert from Win Charset!" << endl;
316
virtual ~KWordGenerator ()
321
void setFilterChain (KoFilterChain *chain)
326
bool writeDocumentBegin (const MSWrite::Word,
327
const MSWrite::PageLayout *pageLayout)
329
kdDebug (30509) << "writeDocumentBegin()" << endl;
332
m_outfile = m_chain->storageFile ("root", KoStore::Write);
334
ErrorAndQuit (MSWrite::Error::FileError, "could not open root in store\n");
338
// store page dimensions for now
341
// page width & height
342
m_pageWidth = Twip2Point (pageLayout->getPageWidth ());
343
m_pageHeight = Twip2Point (pageLayout->getPageHeight ());
346
m_left = Twip2Point (pageLayout->getLeftMargin ());
347
m_right = m_left + Twip2Point (pageLayout->getTextWidth ()) - 1;
348
m_top = Twip2Point (pageLayout->getTopMargin ());
349
m_bottom = m_top + Twip2Point (pageLayout->getTextHeight ()) - 1;
352
m_leftMargin = m_left;
353
m_rightMargin = Twip2Point (pageLayout->getRightMargin ());
355
m_bottomMargin = Twip2Point (pageLayout->getBottomMargin ());
357
kdDebug (30509) << "leftMargin: " << m_leftMargin << endl;
358
kdDebug (30509) << "rightMargin: " << m_rightMargin << endl;
359
kdDebug (30509) << "topMargin: " << m_topMargin << endl;
360
kdDebug (30509) << "bottomMargin: " << m_bottomMargin << endl;
362
// offset of header & footer
363
m_headerFromTop = Twip2Point (pageLayout->getHeaderFromTop ());
364
m_footerFromTop = Twip2Point (pageLayout->getFooterFromTop ());
366
kdDebug (30509) << "headerFromTop: " << m_headerFromTop
367
<< " footerFromTop: " << m_footerFromTop << endl;
369
m_startingPageNumber = pageLayout->getPageNumberStart ();
374
bool writeDocumentBeginForReal (void)
376
kdDebug (30509) << "writeDocumentBeginForReal()" << endl;
378
// adjust margins/PAPERBORDERS to ensure that the header & footer are
380
// TODO: stop header & footer from changing body's location
381
// TODO: recompute offset of margins after recomputing margins
383
if (m_headerFromTop < m_topMargin)
384
m_topMargin = m_headerFromTop;
387
if (m_pageHeight - m_footerFromTop < m_bottomMargin)
388
m_bottomMargin = m_pageHeight - m_footerFromTop;
390
kdDebug (30509) << "adjusted::: leftMargin: " << m_leftMargin
391
<< " rightMargin: " << m_rightMargin
392
<< " topMargin: " << m_topMargin
393
<< " bottomMargin: " << m_bottomMargin
397
// TODO: error checking
398
writeTextInternal ("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
399
writeTextInternal ("<!DOCTYPE DOC PUBLIC \"-//KDE//DTD kword 1.3//EN\" "
400
"\"http://www.koffice.org/DTD/kword-1.3.dtd\">");
401
writeTextInternal ("<DOC xmlns=\"http://www.koffice.org/DTD/kword\" "
402
"mime=\"application/x-kword\" "
403
"syntaxVersion=\"3\" editor=\"KWord\">");
405
writeTextInternal ("<PAPER format=\"1\" "
406
"width=\"%i\" height=\"%i\" "
407
"orientation=\"0\" columns=\"1\" "
408
"hType=\"%i\" fType=\"%i\">",
409
m_pageWidth, m_pageHeight,
410
m_isHeaderOnFirstPage ? 0 : 2,
411
m_isFooterOnFirstPage ? 0 : 2);
413
writeTextInternal ("<PAPERBORDERS left=\"%i\" right=\"%i\" "
414
"top=\"%i\" bottom=\"%i\"/>",
415
m_leftMargin, m_rightMargin,
416
m_topMargin, m_bottomMargin);
418
writeTextInternal ("</PAPER>");
420
writeTextInternal ("<ATTRIBUTES processing=\"0\" "
421
"tabStopValue=\"%lf\" "
422
"hasHeader=\"%i\" hasFooter=\"%i\"/>",
424
m_hasHeader ? 1 : 0, m_hasFooter ? 1 : 0);
426
// handle page numbering not starting from 1
427
if (m_startingPageNumber != 1)
428
writeTextInternal ("<VARIABLESETTINGS startingPageNumber=\"%i\"/>",
429
m_startingPageNumber);
431
writeTextInternal ("<FRAMESETS>");
436
bool writeDocumentEnd (const MSWrite::Word, const MSWrite::PageLayout *)
438
kdDebug (30509) << "writeDocumentEnd()" << endl;
440
// write framesets for the objects
441
writeTextInternal (m_objectFrameset);
443
writeTextInternal ("</FRAMESETS>");
444
writeTextInternal ("<STYLES>");
445
writeTextInternal ("<STYLE>");
446
writeTextInternal ("<NAME value=\"Standard\"/>");
447
writeTextInternal ("<FLOW align=\"left\"/>");
448
writeTextInternal ("<INDENTS first=\"0\" left=\"0\" right=\"0\"/>");
449
writeTextInternal ("<OFFSETS before=\"0\" after=\"0\"/>");
451
writeTextInternal ("<FORMAT id=\"1\">");
452
writeTextInternal ("<COLOR blue=\"0\" red=\"0\" green=\"0\"/>");
453
writeTextInternal ("<FONT name=\"helvetica\"/>");
454
writeTextInternal ("<SIZE value=\"12\"/>");
455
writeTextInternal ("<WEIGHT value=\"50\"/>");
456
writeTextInternal ("<ITALIC value=\"0\"/>");
457
writeTextInternal ("<UNDERLINE value=\"0\"/>");
458
writeTextInternal ("<STRIKEOUT value=\"0\"/>");
459
writeTextInternal ("<VERTALIGN value=\"0\"/>");
460
writeTextInternal ("</FORMAT>");
462
writeTextInternal ("<FOLLOWING name=\"Standard\"/>");
463
writeTextInternal ("</STYLE>");
464
writeTextInternal ("</STYLES>");
466
// write out image keys
467
writeTextInternal ("<PICTURES>");
468
writeTextInternal (m_pictures);
469
writeTextInternal ("</PICTURES>");
472
writeTextInternal ("</DOC>");
476
m_outfile = (KoStoreDevice *) NULL;
479
// output object data
481
/*if (m_objectUpto != getNumObjects ())
482
warning ("m_objectUpto (%i) != getNumObjects() (%i) -- this is probably because OLE is unimplemented\n",
483
m_objectUpto, getNumObjects ());*/
485
MSWrite::List <WRIObject>::Iterator it;
486
for (it = m_objectList.begin (); it != m_objectList.end (); it++)
488
kdDebug (30509) << "outputting object \'" << (*it).m_nameInStore
489
<< "\' (length: " << (*it).m_dataLength << ")"
493
ErrorAndQuit (MSWrite::Error::InternalError, "image data not initialised\n");
495
// open file for object in store
496
m_outfile = m_chain->storageFile ((*it).m_nameInStore, KoStore::Write);
498
ErrorAndQuit (MSWrite::Error::FileError, "could not open image in store\n");
500
if (m_outfile->writeBlock ((const char *) (*it).m_data, (*it).m_dataLength)
501
!= (Q_LONG) (*it).m_dataLength)
502
ErrorAndQuit (MSWrite::Error::FileError, "could not write image to store\n");
504
// close object in store
513
bool writeFooterBegin (void)
515
kdDebug (30509) << "writeFooterBegin()" << endl;
520
// footers must go after body in KWord
523
// footer frameset will be written in writeParaInfoBegin()
528
bool writeFooterEnd (void)
530
kdDebug (30509) << "writeFooterEnd()" << endl;
534
if (!m_writeFooterFirstTime)
535
writeTextInternal ("</FRAMESET>");
541
bool writeHeaderBegin (void)
543
kdDebug (30509) << "writeHeaderBegin()" << endl;
548
// headers must go after body in KWord
551
// header frameset will be written in writeParaInfoBegin()
556
bool writeHeaderEnd (void)
558
kdDebug (30509) << "writeHeaderEnd()" << endl;
562
if (!m_writeHeaderFirstTime)
563
writeTextInternal ("</FRAMESET>");
569
bool writeBodyBegin (void)
571
kdDebug (30509) << "writeBodyBegin()" << endl;
575
// writeFooterBegin() and writeHeaderBegin() have been called by now
576
// so we have enough information to actually write about them
577
writeDocumentBeginForReal ();
579
writeTextInternal ("<FRAMESET frameType=\"1\" frameInfo=\"0\" name=\"Text Frameset 1\" visible=\"1\">");
581
writeTextInternal ("<FRAME runaround=\"1\" autoCreateNewFrame=\"1\" newFrameBehavior=\"0\" copy=\"0\""
582
" top=\"%i\" bottom=\"%i\" left=\"%i\" right=\"%i\"/>",
583
m_top, m_bottom, m_left, m_right);
588
bool writeBodyEnd (void)
590
kdDebug (30509) << "writeBodyEnd()" << endl;
594
// <PAGEBREAKING hardFrameBreakAfter=\"true\"/>" may have been in the last paragraph
595
// and for "hardFrameBreakAfter" to do its work, we need one more final paragraph!
596
if (m_needAnotherParagraph)
598
kdDebug (30509) << "needAnotherParagraph in bodyEndWrite()" << endl;
599
writeTextInternal ("<PARAGRAPH><TEXT></TEXT><LAYOUT></LAYOUT></PARAGRAPH>");
600
m_needAnotherParagraph = false;
603
writeTextInternal ("</FRAMESET>");
605
// since "Text Frameset 1" has ended, we can output the header & footer, now
611
bool writeParaInfoBegin (const MSWrite::FormatParaProperty *paraProperty,
612
const MSWrite::OLE *ole,
613
const MSWrite::Image *image)
615
//kdDebug (30509) << "writeParaInfoBegin()" << endl;
617
// reset charInfo counters
618
m_charInfoCountStart = 0;
619
m_charInfoCountLen = 0;
621
if (inWhat == Header)
623
m_isHeaderOnFirstPage = paraProperty->getIsOnFirstPage ();
625
if (m_writeHeaderFirstTime)
627
// dummy header frames
630
// except, if the header is NOT on the first page, then make an empty "First Page Header"
631
// by setting "visible=1"
632
writeTextInternal ("<FRAMESET frameType=\"1\" frameInfo=\"1\" name=\"First Page Header\" visible=\"%i\">",
633
m_isHeaderOnFirstPage ? 1 : 0);
634
writeTextInternal ("<FRAME runaround=\"1\" copy=\"0\" newFrameBehavior=\"2\" autoCreateNewFrame=\"0\""
635
" top=\"%i\" bottom=\"%i\" left=\"%i\" right=\"%i\"/>",
636
m_headerFromTop, m_headerFromTop, m_left, m_right);
637
writeTextInternal ("</FRAMESET>");
639
writeTextInternal ("<FRAMESET frameType=\"1\" frameInfo=\"2\" name=\"Even Pages Header\" visible=\"0\">");
640
writeTextInternal ("<FRAME runaround=\"1\" copy=\"0\" newFrameBehavior=\"2\" autoCreateNewFrame=\"0\""
641
" top=\"%i\" bottom=\"%i\" left=\"%i\" right=\"%i\"/>",
642
m_headerFromTop, m_headerFromTop, m_left, m_right);
643
writeTextInternal ("</FRAMESET>");
646
writeTextInternal ("<FRAMESET frameType=\"1\" frameInfo=\"3\" name=\"Odd Pages Header\" visible=\"1\">");
647
writeTextInternal ("<FRAME runaround=\"1\" copy=\"1\" newFrameBehavior=\"2\" autoCreateNewFrame=\"0\""
648
" top=\"%i\" bottom=\"%i\" left=\"%i\" right=\"%i\"/>",
649
m_headerFromTop, m_headerFromTop, m_left, m_right);
651
m_writeHeaderFirstTime = false;
654
else if (inWhat == Footer)
656
m_isFooterOnFirstPage = paraProperty->getIsOnFirstPage ();
658
if (m_writeFooterFirstTime)
660
// dummy footer frames
663
// except, if the footer is NOT on the first page, then make an empty "First Page Footer"
664
// by setting "visible=1"
665
writeTextInternal ("<FRAMESET frameType=\"1\" frameInfo=\"4\" name=\"First Page Footer\" visible=\"%i\">",
666
m_isFooterOnFirstPage ? 1 : 0);
667
writeTextInternal ("<FRAME runaround=\"1\" copy=\"0\" newFrameBehavior=\"2\" autoCreateNewFrame=\"0\""
668
" top=\"%i\" bottom=\"%i\" left=\"%i\" right=\"%i\"/>",
669
m_footerFromTop, m_footerFromTop, m_left, m_right);
670
writeTextInternal ("</FRAMESET>");
672
writeTextInternal ("<FRAMESET frameType=\"1\" frameInfo=\"5\" name=\"Even Pages Footer\" visible=\"0\">");
673
writeTextInternal ("<FRAME runaround=\"1\" copy=\"0\" newFrameBehavior=\"2\" autoCreateNewFrame=\"0\""
674
" top=\"%i\" bottom=\"%i\" left=\"%i\" right=\"%i\"/>",
675
m_footerFromTop, m_footerFromTop, m_left, m_right);
676
writeTextInternal ("</FRAMESET>");
679
writeTextInternal ("<FRAMESET frameType=\"1\" frameInfo=\"6\" name=\"Odd Pages Footer\" visible=\"1\">");
680
writeTextInternal ("<FRAME runaround=\"1\" copy=\"1\" newFrameBehavior=\"2\" autoCreateNewFrame=\"0\""
681
" top=\"%i\" bottom=\"%i\" left=\"%i\" right=\"%i\"/>",
682
m_footerFromTop, m_footerFromTop, m_left, m_right);
684
m_writeFooterFirstTime = false;
688
if (!writeTextInternal ("<PARAGRAPH><TEXT>")) return false;
692
kdDebug (30509) << "Paragraph is an image!" << endl;
698
// give picture a name
701
imageName = "Picture ";
702
imageName += QString::number (m_numPictures + 1); // image numbers start at 1...
705
// give picture a filename
708
fileInStore = "pictures/picture" + QString::number (m_numPictures + 1);
710
kdDebug (30509) << "\tGetting type..." << endl;
713
if (image->getIsBMP ())
714
fileInStore += ".bmp";
715
else if (image->getIsWMF () )
716
fileInStore += ".wmf";
718
ErrorAndQuit (MSWrite::Error::InternalError, "unsupported picture type\n");
721
// indicate anchored image in formatting
723
kdDebug (30509) << "\tIndicating anchored image in formatting" << endl;
724
if (!writeTextInternal ("#")) return false;
726
m_formatOutput += "<FORMAT id=\"6\" pos=\"0\" len=\"1\">";
727
m_formatOutput += "<ANCHOR type=\"frameset\" instance=\"";
728
m_formatOutput += imageName;
729
m_formatOutput += "\"/>";
730
m_formatOutput += "</FORMAT>";
733
// write framesets (truly written in documentEndWrite())
735
kdDebug (30509) << "\tWriting framesets!" << endl;
737
m_objectFrameset += "<FRAMESET frameType=\"2\" frameInfo=\"0\" name=\"";
738
m_objectFrameset += imageName;
739
m_objectFrameset += "\" visible=\"1\">";
741
m_objectFrameset += "<FRAME runaround=\"1\" copy=\"0\" newFrameBehavior=\"1\"";
743
const double imageLeft = double (m_left) + Twip2Point (double (image->getIndent ()));
744
m_objectFrameset += " left=\"";
745
m_objectFrameset += QString::number (imageLeft);
746
m_objectFrameset += "\"";
748
const double imageWidth = Twip2Point (double (image->getDisplayedWidth ()));
749
m_objectFrameset += " right=\"";
750
m_objectFrameset += QString::number (imageLeft + imageWidth - 1);
751
m_objectFrameset += "\"";
753
m_objectFrameset += " top=\"";
754
m_objectFrameset += QString::number (m_top);
755
m_objectFrameset += "\"";
757
const double imageHeight = Twip2Point (double (image->getDisplayedHeight ()));
758
m_objectFrameset += " bottom=\"";
759
m_objectFrameset += QString::number (double (m_top) + imageHeight - 1);
760
m_objectFrameset += "\"/>";
762
m_objectFrameset += "<PICTURE keepAspectRatio=\"false\">";
763
m_objectFrameset += "<KEY msec=\"0\" hour=\"0\" second=\"0\" minute=\"0\" day=\"1\" month=\"1\" year=\"1970\"";
764
m_objectFrameset += " filename=\"";
765
m_objectFrameset += fileInStore;
766
m_objectFrameset += "\"/>";
767
m_objectFrameset += "</PICTURE>";
769
m_objectFrameset += "</FRAMESET>";
770
#ifdef DEBUG_XML_OUTPUT
771
m_objectFrameset += "\n";
774
m_pictures += "<KEY msec=\"0\" hour=\"0\" second=\"0\" minute=\"0\" day=\"1\" month=\"1\" year=\"1970\"";
775
m_pictures += " name=\"";
776
m_pictures += fileInStore;
778
m_pictures += " filename=\"";
779
m_pictures += fileInStore;
780
m_pictures += "\"/>";
785
// store object properties
787
kdDebug (30509) << "\tStoring object" << endl;
789
if (!m_objectList.addToBack ())
790
ErrorAndQuit (MSWrite::Error::OutOfMemory, "could not allocate memory for object\n");
792
WRIObject &obj = *m_objectList.begin (false);
793
obj.m_nameInStore = fileInStore;
794
obj.m_dataLength = image->getExternalImageSize ();
795
obj.m_data = new MSWrite::Byte [obj.m_dataLength];
797
ErrorAndQuit (MSWrite::Error::OutOfMemory, "could not allocate memory for object data\n");
799
// if anchored images could be positioned properly, this wouldn't be needed
800
m_objectHorizOffset = double (Twip2Point (image->getIndent ()));
801
m_paraIsImage = true;
807
if (!writeTextInternal ("[OLE unsupported]")) return false;
810
m_paraIsImage = false;
817
bool writeParaInfoEnd (const MSWrite::FormatParaProperty *paraProperty,
818
const MSWrite::OLE * /*ole*/,
819
const MSWrite::Image *image)
822
//kdDebug (30509) << "writeParaInfoEnd()" << endl;
826
WRIObject &obj = *m_objectList.begin (false);
828
// consistency check: wrote exactly the right amount of data?
829
if (obj.m_dataUpto != obj.m_dataLength)
830
kdWarning (30509) << "obj.dataUpto (" << obj.m_dataUpto
831
<< ") != obj.dataLength (" << obj.m_dataLength
838
output += "<LAYOUT>";
839
output += "<NAME value=\"Standard\"/>";
841
int align = paraProperty->getAlignment ();
843
if (align != MSWrite::Alignment::Left)
845
output += "<FLOW align=\"";
848
/*case MSWrite::Alignment::Left:
851
case MSWrite::Alignment::Centre:
854
case MSWrite::Alignment::Right:
857
case MSWrite::Alignment::Justify:
864
double indentFirst = Twip2Point (double (paraProperty->getLeftIndentFirstLine ()));
865
double indentLeft = Twip2Point (double (paraProperty->getLeftIndent ()));
866
double indentRight = Twip2Point (double (paraProperty->getRightIndent ()));
869
debug ("raw indent: first: %i left: %i right: %i\n",
870
indentFirst, indentLeft, indentRight);
873
if (m_paraIsImage /*paraProperty->isObject ()*/ && m_objectHorizOffset)
875
if (align == MSWrite::Align::Center)
877
// TODO: I don't know what m_objectHorizOffset is relative to!
878
kdDebug (30509) << "ignoring image offset with centred image" << endl;
879
m_objectHorizOffset = 0;
883
// MSWrite does not add the horizontal offset of the image from the left margin to the Left Indent
884
// -- instead, it selects the bigger one
885
// TODO: proper image positioning (see doc IMPERFECT)
886
if (m_simulateImageOffset && m_objectHorizOffset > indentLeft)
888
kdDebug (30509) << "image is further away from left margin by itself, rather than using indentLeft ("
889
<< m_objectHorizOffset << " > " << indentLeft << ")" << endl;
890
indentLeft = m_objectHorizOffset;
895
// hopefully these test operations will be cheaper than the XML ones :)
896
if (indentFirst || indentLeft || indentRight)
898
output += "<INDENTS";
899
if (indentFirst) output += " first=\"" + QString::number (indentFirst) + "\"";
900
if (indentLeft) output += " left=\"" + QString::number (indentLeft) + "\"";
901
if (indentRight) output += " right=\"" + QString::number (indentRight) + "\"";
905
MSWrite::Word lineSpacing = paraProperty->getLineSpacing ();
907
if (lineSpacing != MSWrite::LineSpacing::Single)
910
output += "<LINESPACING type=\"atleast\" spacingvalue=\"" + QString::number (Twip2Point (lineSpacing)) + "\"/>";
912
output += "<LINESPACING type=\"";
915
//case MSWrite:;LineSpacing::Single:
917
case MSWrite::LineSpacing::OneAndAHalf:
918
output += "oneandhalf";
920
case MSWrite::LineSpacing::Double:
924
kdWarning (30509) << "non-\"standard\" linespacing value: " << lineSpacing << endl;
925
output += "atleast\" ";
926
output += "spacingvalue=\"";
927
output += QString::number (Twip2Point (lineSpacing));
934
// Do we want the linespacing to _look_ like it does in Write?
935
// (this adds extra space before each paragraph)
936
if (m_simulateLineSpacing)
938
// emulate Write's linespacing (aligned to bottom)
939
// by using varying amounts of space before the paragraph
940
// TODO: test if it works nicely enough (what if you have several different sized fonts in paragraph?)
941
if (lineSpacing != MSWrite::LineSpacing::Single) // if not normal linespacing...
943
output += "<OFFSETS before=\"";
948
/*case MSWrite::LineSpacing::Single:
950
case MSWrite::LineSpacing::OneAndAHalf:
953
case MSWrite::LineSpacing::Double:
957
kdWarning (30509) << "unknown linespacing value: " << lineSpacing << endl;
961
// subtract the amount of trailing linespace from last paragraph
962
amount -= m_lineSpacingFromAbove;
963
if (amount <= 0) amount = 0; // no emulation can be perfect...
965
output += QString::number (amount);
969
// GUESS (TODO: fix) the amount of trailing linespace
972
case MSWrite::LineSpacing::Single:
973
m_lineSpacingFromAbove = 0;
975
case MSWrite::LineSpacing::OneAndAHalf:
976
m_lineSpacingFromAbove = 7;
978
case MSWrite::LineSpacing::Double:
979
m_lineSpacingFromAbove = 14;
982
m_lineSpacingFromAbove = 0;
985
} // if (m_simulateLineSpacing) {
990
debug ("\tpagebrk: output: offset: %i chars in paragraph: %i\n",
991
m_pageBreakOffset, m_charInfoCountStart + m_charInfoCountLen);
994
// page break before all the text
995
if (m_pageBreakOffset == 0 && m_charInfoCountStart + m_charInfoCountLen > 0)
997
output += "<PAGEBREAKING hardFrameBreak=\"true\"/>";
998
m_needAnotherParagraph = false; // this paragraph is on first page so we don't need another one
1000
// we assume that the pageBreak was after all the text (TODO: don't assume this)
1003
output += "<PAGEBREAKING hardFrameBreakAfter=\"true\"/>";
1004
m_needAnotherParagraph = true; // need another paragraph for hardFrameBreakAfter to work
1007
m_pageBreak = false; // reset flag
1010
m_needAnotherParagraph = false;
1013
for (int i = 0; i < paraProperty->getNumTabulator (); i++)
1015
const MSWrite::FormatParaPropertyTabulator *tab = paraProperty->getTabulator (i);
1016
if (tab->getIsDummy ()) break;
1018
output += "<TABULATOR";
1020
if (tab->getIsDecimal ())
1021
output += " type=\"3\" alignchar=\".\"";
1023
output += " type=\"0\"";
1025
output += " ptpos=\"" + QString::number (Twip2Point (double (tab->getIndent ()))) + "\"/>";
1027
//debug ("Tab: isNormal: %i ptPos: %i\n",
1028
// paraProperty->tbd [i].isTabNormal (), paraProperty->tbd [i].getTabNumPoints ());
1031
output += "</LAYOUT>";
1032
#ifdef DEBUG_XML_OUTPUT
1036
output += "<FORMATS>";
1037
#ifdef DEBUG_XML_OUTPUT
1040
// output all the charInfo for this paragraph
1041
output += m_formatOutput; m_formatOutput = "";
1042
output += "</FORMATS>";
1043
#ifdef DEBUG_XML_OUTPUT
1047
output += "</PARAGRAPH>";
1048
#ifdef DEBUG_XML_OUTPUT
1052
if (!writeTextInternal (output)) return false;
1057
bool writeCharInfoBegin (const MSWrite::FormatCharProperty * /*charProperty*/)
1059
//kdDebug (30509) << "writeCharInfoBegin()" << endl;
1064
// outputs character formatting tags
1065
bool writeCharInfoEnd (const MSWrite::FormatCharProperty *charProperty,
1068
//kdDebug (30509) << "writeCharInfoEnd()" << endl;
1070
// output type of format information (page number or normal text)
1071
m_formatOutput += "<FORMAT id=\"";
1072
if (charProperty->getIsPageNumber ())
1073
m_formatOutput += "4";
1075
m_formatOutput += "1";
1076
m_formatOutput += "\" ";
1078
m_formatOutput += "pos=\""; m_formatOutput += QString::number (m_charInfoCountStart); m_formatOutput += "\" ";
1079
m_formatOutput += "len=\""; m_formatOutput += QString::number (m_charInfoCountLen); m_formatOutput += "\">";
1081
m_charInfoCountStart += m_charInfoCountLen;
1082
m_charInfoCountLen = 0;
1084
if (charProperty->getIsPageNumber ())
1086
m_formatOutput += "<VARIABLE>";
1087
m_formatOutput += "<TYPE key=\"NUMBER\" type=\"4\"/>";
1088
m_formatOutput += "<PGNUM subtype=\"0\" value=\"1\"/>";
1089
m_formatOutput += "</VARIABLE>";
1092
m_formatOutput += "<FONT name=\"";
1093
m_formatOutput += (const char *) (charProperty->getFont ()->getName ());
1094
m_formatOutput += "\"/>";
1095
m_formatOutput += "<SIZE value=\"";
1096
m_formatOutput += QString::number (charProperty->getFontSize ());
1097
m_formatOutput += "\"/>";
1099
if (charProperty->getIsBold ())
1100
m_formatOutput += "<WEIGHT value=\"75\"/>";
1102
// m_formatOutput += "<WEIGHT value=\"50\" />";
1104
if (charProperty->getIsItalic ())
1105
m_formatOutput += "<ITALIC value=\"1\"/>";
1107
// m_formatOutput += "<ITALIC value=\"0\" />";
1109
if (charProperty->getIsUnderlined ())
1110
m_formatOutput += "<UNDERLINE value=\"1\"/>";
1112
// m_formatOutput += "<UNDERLINE value=\"0\" />";
1114
/*if (charProperty->isNormalPosition ())
1115
m_formatOutput += "<VERTALIGN value=\"0\" />";
1116
else*/ if (charProperty->getIsSubscript ())
1117
m_formatOutput += "<VERTALIGN value=\"1\"/>";
1118
else if (charProperty->getIsSuperscript ())
1119
m_formatOutput += "<VERTALIGN value=\"2\"/>";
1121
error ("unknown valign\n");*/
1123
m_formatOutput += "</FORMAT>";
1128
bool writeBinary (const MSWrite::Byte *buffer, const MSWrite::DWord length)
1130
kdDebug (30509) << "writeBinary()" << endl;
1132
// must be OLE, TODO: implement OLE properly
1136
WRIObject &obj = *m_objectList.begin (false);
1139
ErrorAndQuit (MSWrite::Error::InternalError, "object data not initialised\n");
1141
// consistency check: aren't going to write past end of array?
1142
if (obj.m_dataUpto + length > obj.m_dataLength)
1144
kdDebug (30509) << "object image overrun: "
1145
<< obj.m_dataUpto << " + " << length
1146
<< " > " << obj.m_dataLength << endl;
1147
ErrorAndQuit (MSWrite::Error::InternalError, "object image overrun\n");
1150
memcpy (obj.m_data + obj.m_dataUpto, buffer, length);
1151
obj.m_dataUpto += length;
1159
// text output functions
1162
bool writeText (const MSWrite::Byte *string)
1164
// from Win Character Set...
1167
// there is a codec, therefore there is a decoder...
1170
// output Unicode (UTF8)
1171
strUnicode = m_decoder->toUnicode ((const char *) string, strlen ((const char *) string));
1175
// output a plain string still in wrong Character Set
1176
// (hopefully the user won't notice)
1177
strUnicode = (const char *) string;
1180
// update character information counter (after charset conversion)
1181
m_charInfoCountLen += strUnicode.length ();
1183
// make string XML-friendly (TODO: speed up)
1184
strUnicode.replace ('&', "&");
1185
strUnicode.replace ('<', "<");
1186
strUnicode.replace ('>', ">");
1187
strUnicode.replace ('\"', """);
1188
strUnicode.replace ('\'', "'");
1190
return writeTextInternal (strUnicode);
1193
bool writeTextInternal (const MSWrite::Byte *str)
1196
return textWrite_lowLevel (QString (str));
1197
#else // while this is code duplication (of below func), this ensures that no
1198
// characters are mysteriously converted (this makes writeOptionalHyphen () work)
1201
// header/footer must be written after main body
1202
m_heldOutput += (const char *) str;
1207
int strLength = strlen ((const char *) str);
1209
if (m_outfile->writeBlock ((const char *) str, strLength) != strLength)
1211
ErrorAndQuit (MSWrite::Error::FileError, "could not write to maindoc.xml\n");
1219
bool writeTextInternal (const QString &str)
1223
// header/footer must be written after main body
1224
m_heldOutput += str;
1229
QCString strUtf8 = str.utf8 ();
1230
int strLength = strUtf8.length ();
1232
if (m_outfile->writeBlock (strUtf8, strLength) != strLength)
1234
ErrorAndQuit (MSWrite::Error::FileError, "could not write to maindoc.xml (2)\n");
1241
bool writeTextInternal (const int num)
1243
return writeTextInternal (QString::number (num));
1246
bool writeTextInternal (const char *format, ...)
1249
va_start (list, format);
1252
// This function is mainly for outputting tags (where XML characters are
1253
// already escaped and the text is in the right character set...ASCII
1254
// = UTF-8 for alphanumeric chars I hope). So _don't_ pass user text
1255
// to this function (that's what writeText() is for); otherwise you might
1256
// exceed this 1024 limit.
1259
vsnprintf (string, sizeof (string) - 1, format, list);
1260
string [sizeof (string) - 1] = '\0';
1262
ret = writeTextInternal ((const MSWrite::Byte *) string);
1269
// writePageNew() is called for the pageTable
1270
// -- however, pageTable can be very inaccurate, so we ignore it
1271
bool writePageNew (const int)
1276
// handles explicit page breaks
1277
bool writePageBreak (void)
1279
// later used in paraEndWrite
1281
m_pageBreakOffset = m_charInfoCountStart + m_charInfoCountLen;
1286
// handle "(page)" number
1287
bool writePageNumber (void)
1289
m_charInfoCountLen++; // not incremented by writeTextInternal()
1290
return writeTextInternal ("#");
1293
bool writeCarriageReturn (void)
1295
return true; // ignore CR
1298
// write newline unless end-of-paragraph
1299
// (this is the support for paragraphs with multiple newlines)
1300
bool writeNewLine (const bool endOfParagraph)
1302
if (!endOfParagraph)
1304
m_charInfoCountLen++; // not incremented by writeTextInternal()
1305
return writeTextInternal ("\n");
1311
// aka "soft hyphen"
1312
bool writeOptionalHyphen (void)
1314
m_charInfoCountLen++; // not incremented by writeTextInternal()
1315
return writeTextInternal ("\xC2\xAD");
1318
void setKOfficeLink (MSWriteImport *kofficeLink)
1320
m_koLink = kofficeLink;
1323
void sigProgress (const int value)
1325
m_koLink->sigProgress (value);
1335
MSWriteImport::MSWriteImport (KoFilter *, const char *, const QStringList &)
1336
: m_device (NULL), m_parser (NULL), m_generator (NULL)
1340
MSWriteImport::~MSWriteImport ()
1347
KoFilter::ConversionStatus MSWriteImport::convert (const QCString &from, const QCString &to)
1349
kdDebug (30509) << "MSWriteImport $Date: 2003/08/31 10:59:34 $ using LibMSWrite "
1350
<< MSWrite::Version << endl;
1352
if (to != "application/x-kword" || from != "application/x-mswrite")
1354
kdError (30509) << "Internal error! Filter not implemented?" << endl;
1355
return KoFilter::NotImplemented;
1359
//MSWriteImportDialog *dialog = new MSWriteImportDialog ();
1360
MSWriteImportDialog dialog;
1362
/*debug ("DIALOG check alloc\n");
1365
error ("Could not allocate memory for dialog\n");
1366
return KoFilter::StupidError;
1369
debug ("DIALOG EXEC!!!\n");
1370
if (!dialog.exec ())
1372
error ("Dialog was aborted! Aborting filter!\n");
1373
return KoFilter::UserCancelled;
1376
debug ("DIALOG GET!!!\n");
1378
// read settings from dialog
1379
m_codec = dialog.getCodec ();
1380
m_simulateLinespacing = dialog.getSimulateLinespacing ();
1381
m_simulateImageOffset = dialog.getSimulateImageOffset ();
1382
debug ("Import options: simulateLinespacing: %i\tsimulateImageOffset: %i\n",
1383
m_simulateLinespacing, m_simulateImageOffset);
1385
debug ("DIALOG DELETE\n");
1389
// create the Device that will read from the .WRI file
1390
m_device = new WRIDevice;
1393
kdError (30509) << "Could not allocate memory for device" << endl;
1394
return KoFilter::OutOfMemory;
1397
// open the .WRI file
1398
if (!m_device->openFile (QFile::encodeName (m_chain->inputFile ())))
1400
kdError (30509) << "Could not open \'" << m_chain->inputFile () << "\'" << endl;
1401
return KoFilter::FileNotFound;
1405
// create Parser that will interpret the .WRI file and call the Generator
1406
m_parser = new MSWrite::InternalParser;
1409
kdError (30509) << "Could not allocate memory for parser" << endl;
1410
return KoFilter::OutOfMemory;
1413
// tell the Parser to use the Device to read from the .WRI file
1414
m_parser->setDevice (m_device);
1417
// create Generator that will produce the .KWD file
1418
m_generator = new KWordGenerator;
1421
kdError (30509) << "Could not allocate memory for generator" << endl;
1422
return KoFilter::OutOfMemory;
1425
// give the Generator the Device for error-handling purposes
1426
m_generator->setDevice (m_device);
1428
// give the Generator the chain
1429
m_generator->setFilterChain (m_chain);
1431
// hand over sigProgess to give some feedback to the user
1432
m_generator->setKOfficeLink (this);
1435
// hook up Generator to Parser
1436
m_parser->setGenerator (m_generator);
1440
if (!m_parser->parse ())
1442
// try to return somewhat more meaningful errors than StupidError
1443
// for the day that KOffice actually reports them to the user properly
1444
int errorCode = m_device->bad ();
1447
case MSWrite::Error::Ok:
1448
kdDebug (30509) << "Error::Ok but aborted???" << endl;
1449
return KoFilter::InternalError;
1451
case MSWrite::Error::Warn:
1452
kdDebug (30509) << "Error::Warn" << endl;
1453
return KoFilter::InternalError; // warnings should _never_ set m_error
1455
case MSWrite::Error::InvalidFormat:
1456
kdDebug (30509) << "Error::InvalidFormat" << endl;
1457
return KoFilter::WrongFormat;
1459
case MSWrite::Error::OutOfMemory:
1460
kdDebug (30509) << "Error::OutOfMemory" << endl;
1461
return KoFilter::OutOfMemory;
1463
case MSWrite::Error::InternalError:
1464
kdDebug (30509) << "Error::InternalError" << endl;
1465
return KoFilter::InternalError;
1467
case MSWrite::Error::Unsupported:
1468
kdDebug (30509) << "Error::Unsupported" << endl;
1469
return KoFilter::InternalError;
1471
case MSWrite::Error::FileError:
1472
kdDebug (30509) << "Error::FileError" << endl;
1473
return KoFilter::StupidError; // got a better return value?
1476
kdWarning (30509) << "Unknown error: " << errorCode << endl;
1477
return KoFilter::StupidError;
1480
return KoFilter::OK;
1483
#include <mswriteimport.moc>