filters

rtfimport.cpp
1/*
2 This file is part of the KDE project
3 Copyright (C) 2001 Ewald Snel <ewald@rambo.its.tudelft.nl>
4 Copyright (C) 2001 Tomasz Grobelny <grotk@poczta.onet.pl>
5 Copyright (C) 2003, 2004 Nicolas GOUTTE <goutte@kde.org>
6
7 This library is free software; you can redistribute it and/or
8 modify it under the terms of the GNU Library General Public
9 License as published by the Free Software Foundation; either
10 version 2 of the License, or (at your option) any later version.
11*/
12
13// ### FIXME: copyright holders/date
14
15#include <kdebug.h>
16#include <tqfontinfo.h>
17#include <stddef.h>
18#include <string.h>
19#include <KoFilterChain.h>
20#include <kgenericfactory.h>
21
22#include <tqcstring.h>
23#include <tqstringlist.h>
24#include <tqdir.h>
25#include <tqfileinfo.h>
26#include <tqregexp.h>
27#include <tqvaluelist.h>
28
29#include <kurl.h>
30#include <tdemessagebox.h>
31
32#include <KoPicture.h>
33#include <KoFilterManager.h>
34
35#include "rtfimport.h"
36#include "rtfimport.moc"
37
38typedef KGenericFactory<RTFImport, KoFilter> RTFImportFactory;
39K_EXPORT_COMPONENT_FACTORY( librtfimport, RTFImportFactory( "kofficefilters" ) )
40
41// defines a property
42#define PROP(a,b,c,d,e) { a, b, &RTFImport::c, d, e }
43
44// defines a member variable of RTFImport as a property (DEPRECATED)
45#define MEMBER(a,b,c,d,e) PROP(a,b,c,offsetof(RTFImport,d),e)
46
47static RTFProperty destinationPropertyTable[] =
48 {
49// only-valid-in control word function offset, value
50 PROP( 0L, "@*", skipGroup, 0L, false ),
51 MEMBER( "@info", "@author", parsePlainText, author, false ),
52 PROP( "@pict", "@blipuid", parseBlipUid, 0, 0 ),
53 PROP( "@rtf", "@colortbl", parseColorTable, 0L, true ),
54 MEMBER( "@info", "@company", parsePlainText, company, false ),
55 MEMBER( "@info", "@doccomm", parsePlainText, doccomm, false ),
56 PROP( "Text", "@field", parseField, 0L, false ),
57 PROP( "@field", "@fldinst", parseFldinst, 0L, false ),
58 PROP( "@field", "@fldrslt", parseFldrslt, 0L, false ),
59 PROP( "@rtf", "@fonttbl", parseFontTable, 0L, true ),
60 MEMBER( "@rtf", "@footer", parseRichText, oddPagesFooter, true ),
61 PROP( "@rtf", "@footnote", parseFootNote, 0L, true ),
62 MEMBER( "@rtf", "@footerf", parseRichText, firstPageFooter, true ),
63 MEMBER( "@rtf", "@footerl", parseRichText, oddPagesFooter, true ),
64 MEMBER( "@rtf", "@footerr", parseRichText, evenPagesFooter, true ),
65 MEMBER( "@rtf", "@header", parseRichText, oddPagesHeader, true ),
66 MEMBER( "@rtf", "@headerf", parseRichText, firstPageHeader, true ),
67 MEMBER( "@rtf", "@headerl", parseRichText, oddPagesHeader, true ),
68 MEMBER( "@rtf", "@headerr", parseRichText, evenPagesHeader, true ),
69 PROP( "@rtf", "@info", parseGroup, 0L, true ),
70 PROP( "Text", "@nonshppict", skipGroup, 0L, false ),
71 PROP( 0L, "@panose", skipGroup, 0L, 0 ), // Not supported
72 PROP( "Text", "@pict", parsePicture, 0L, true ),
73 MEMBER( "@", "@rtf", parseRichText, bodyText, true ),
74 PROP( "Text", "@shpinst", skipGroup, 0L, true ),
75 PROP( "Text", "@shppict", parseGroup, 0L, false ),
76 PROP( "@rtf", "@stylesheet", parseStyleSheet, 0L, true ),
77 MEMBER( "@info", "@title", parsePlainText, title, false ),
78};
79
80static RTFProperty propertyTable[] =
81 // Alphabetical order
82 {
83// only-valid-in control word function offset, value
84 PROP( "Text", "\n", insertParagraph, 0L, 0 ),
85 PROP( "Text", "\r", insertParagraph, 0L, 0 ),
86 PROP( 0L, "\'", insertHexSymbol, 0L, 0 ),
87 PROP( 0L, "\\", insertSymbol, 0L, '\\' ),
88 PROP( 0L, "_", insertSymbol, 0L, 0x2011 ),
89 PROP( 0L, "{", insertSymbol, 0L, '{' ),
90 PROP( 0L, "|", insertSymbol, 0L, 0x00b7 ),
91 PROP( 0L, "}", insertSymbol, 0L, '}' ),
92 PROP( 0L, "~", insertSymbol, 0L, 0x00a0 ),
93 PROP( 0L, "-", insertSymbol, 0L, 0x00ad ),
94 PROP( 0L, "adjustright", ignoreKeyword, 0L, 0 ), // Not supported, KWord has no grid
95 PROP( 0L, "ansi", setAnsiCodepage, 0L, 0 ),
96 PROP( 0L, "ansicpg", setCodepage, 0L, 0 ),
97 MEMBER( 0L, "b", setToggleProperty, state.format.bold, 0 ),
98 // \bin is handled in the tokenizer
99 MEMBER( "@colortbl", "blue", setNumericProperty, blue, 0 ),
100 MEMBER( 0L, "box", setEnumProperty, state.layout.border, 0 ),
101 PROP( 0L, "brdrb", selectLayoutBorder, 0L, 3 ),
102 PROP( 0L, "brdrcf", setBorderColor, 0L, 0 ),
103 PROP( 0L, "brdrdash", setBorderStyle, 0L, RTFBorder::Dashes ),
104 PROP( 0L, "brdrdashd", setBorderStyle, 0L, RTFBorder::DashDot ),
105 PROP( 0L, "brdrdashdd", setBorderStyle, 0L, RTFBorder::DashDotDot ),
106 PROP( 0L, "brdrdashsm", setBorderStyle, 0L, RTFBorder::Dashes ),
107 PROP( 0L, "brdrdb", setBorderStyle, 0L, RTFBorder::Solid ),
108 PROP( 0L, "brdrdot", setBorderStyle, 0L, RTFBorder::Dots ),
109 PROP( 0L, "brdrhairline", setBorderStyle, 0L, RTFBorder::Solid ),
110 PROP( 0L, "brdrl", selectLayoutBorder, 0L, 0 ),
111 PROP( 0L, "brdrr", selectLayoutBorder, 0L, 1 ),
112 PROP( 0L, "brdrs", setBorderStyle, 0L, RTFBorder::Solid ),
113 PROP( 0L, "brdrsh", setBorderStyle, 0L, RTFBorder::Solid ),
114 PROP( 0L, "brdrt", selectLayoutBorder, 0L, 2 ),
115 PROP( 0L, "brdrth", setBorderStyle, 0L, RTFBorder::Solid ),
116 PROP( 0L, "brdrw", setBorderProperty, offsetof(RTFBorder,width), 0 ),
117 PROP( 0L, "bullet", insertSymbol, 0L, 0x2022 ),
118 PROP( 0L, "brsp", setBorderProperty, offsetof(RTFBorder,space), 0 ),
119 MEMBER( 0L, "caps", setToggleProperty, state.format.caps, 0 ),
120 MEMBER( 0L, "cb", setNumericProperty, state.format.bgcolor, 0 ),
121 MEMBER( 0L, "highlight", setNumericProperty, state.format.bgcolor, 0 ),
122 PROP( "Text", "cell", insertTableCell, 0L, 0 ),
123 PROP( 0L, "cellx", insertCellDef, 0L, 0 ),
124 MEMBER( 0L, "cf", setNumericProperty, state.format.color, 0 ),
125 PROP( 0L, "chdate", insertDateTime, 0L, 1 ),
126 PROP( 0L, "chpgn", insertPageNumber, 0L, 0 ),
127 PROP( 0L, "chtime", insertDateTime, 0L, 0 ),
128 PROP( 0L, "clbrdrb", selectLayoutBorderFromCell, 0L, 3 ),
129 PROP( 0L, "clbrdrl", selectLayoutBorderFromCell, 0L, 0 ),
130 PROP( 0L, "clbrdrr", selectLayoutBorderFromCell, 0L, 1 ),
131 PROP( 0L, "clbrdrt", selectLayoutBorderFromCell, 0L, 2 ),
132 MEMBER( 0L, "clcbpat", setNumericProperty, state.tableCell.bgcolor, 0 ),
133 PROP( 0L, "cs", ignoreKeyword, 0L, 0 ), // Not supported by KWord 1.3
134 PROP( 0L, "datafield", skipGroup, 0L, 0 ), // Binary data in variables are not supported
135 MEMBER( "@rtf", "deff", setNumericProperty, defaultFont, 0 ),
136 MEMBER( "@rtf", "deftab", setNumericProperty, defaultTab, 0 ),
137 PROP( "@pict", "dibitmap", setPictureType, 0L, RTFPicture::BMP ),
138 MEMBER( 0L, "dn", setNumericProperty, state.format.baseline, 6 ),
139 PROP( 0L, "emdash", insertSymbol, 0L, 0x2014 ),
140 PROP( "@pict", "emfblip", setPictureType, 0L, RTFPicture::EMF ),
141 PROP( 0L, "emspace", insertSymbol, 0L, 0x2003 ),
142 PROP( 0L, "endash", insertSymbol, 0L, 0x2013 ),
143 PROP( 0L, "enspace", insertSymbol, 0L, 0x2002 ),
144 PROP( 0L, "expnd", ignoreKeyword, 0L, 0 ), // Expansion/compression of character inter-space not supported
145 PROP( 0L, "expndtw", ignoreKeyword, 0L, 0 ), // Expansion/compression of character inter-space not supported
146 MEMBER( 0L, "f", setNumericProperty, state.format.font, 0 ),
147 MEMBER( "@rtf", "facingp", setFlagProperty, facingPages, true ),
148 PROP( 0L, "fcharset", setCharset, 0L, 0 ), // Not needed with TQt
149 PROP( "@fonttbl", "fdecor", setFontStyleHint, 0L, TQFont::Decorative ),
150 MEMBER( 0L, "fi", setNumericProperty, state.layout.firstIndent, 0 ),
151 PROP( "@fonttbl", "fmodern", setFontStyleHint, 0L, TQFont::TypeWriter ),
152 PROP( "@fonttbl", "fnil", setFontStyleHint, 0L, TQFont::AnyStyle ),
153 MEMBER( 0L, "footery", setNumericProperty, state.section.footerMargin, 0 ),
154 PROP( 0L, "formshade", ignoreKeyword, 0L, 0 ), // Not supported, KWord has no form support
155 MEMBER( "@fonttbl", "fprq", setNumericProperty, font.fixedPitch, 0 ),
156 PROP( "@fonttbl", "froman", setFontStyleHint, 0L, TQFont::Serif ),
157 MEMBER( 0L, "fs", setNumericProperty, state.format.fontSize, 0 ),
158 PROP( "@fonttbl", "fscript", setFontStyleHint, 0L, TQFont::AnyStyle ),
159 PROP( "@fonttbl", "fswiss", setFontStyleHint, 0L, TQFont::SansSerif ),
160 PROP( "@fonttbl", "ftech", setFontStyleHint, 0L, TQFont::AnyStyle ),
161 MEMBER( "@colortbl", "green", setNumericProperty, green, 0 ),
162 MEMBER( 0L, "headery", setNumericProperty, state.section.headerMargin, 0 ),
163 MEMBER( 0L, "i", setToggleProperty, state.format.italic, 0 ),
164 MEMBER( 0L, "intbl", setFlagProperty, state.layout.inTable, true ),
165 PROP( "@pict", "jpegblip", setPictureType, 0L, RTFPicture::JPEG ),
166 MEMBER( 0L, "keep", setFlagProperty, state.layout.keep, true ),
167 MEMBER( 0L, "keepn", setFlagProperty, state.layout.keepNext, true ),
168 MEMBER( "@rtf", "landscape", setFlagProperty, landscape, true ),
169 PROP( 0L, "ldblquote", insertSymbol, 0L, 0x201c ),
170 MEMBER( 0L, "li", setNumericProperty, state.layout.leftIndent, 0 ),
171 PROP( 0L, "line", insertSymbol, 0L, 0x000a ),
172 PROP( 0L, "lquote", insertSymbol, 0L, 0x2018 ),
173 PROP( 0L, "ltrmark", insertSymbol, 0L, 0x200e ),
174 PROP( 0L, "mac", setMacCodepage, 0L, 0 ),
175 PROP( "@pict", "macpict", setPictureType, 0L, RTFPicture::MacPict ),
176 MEMBER( "@rtf", "margb", setNumericProperty, bottomMargin, 0 ),
177 MEMBER( "@rtf", "margl", setNumericProperty, leftMargin, 0 ),
178 MEMBER( "@rtf", "margr", setNumericProperty, rightMargin, 0 ),
179 MEMBER( "@rtf", "margt", setNumericProperty, topMargin, 0 ),
180 MEMBER( 0L, "nosupersub", setEnumProperty, state.format.vertAlign, RTFFormat::Normal ),
181 PROP( "Text", "page", insertPageBreak, 0L, 0 ),
182 MEMBER( 0L, "pagebb", setFlagProperty, state.layout.pageBB, true ),
183 MEMBER( "@rtf", "paperh", setNumericProperty, paperHeight, 0 ),
184 MEMBER( "@rtf", "paperw", setNumericProperty, paperWidth, 0 ),
185 PROP( "Text", "par", insertParagraph, 0L, 0 ),
186 PROP( 0L, "pard", setParagraphDefaults, 0L, 0 ),
187 PROP( 0L, "pc", setPcCodepage, 0L, 0 ),
188 PROP( 0L, "pca", setPcaCodepage, 0L, 0 ),
189 MEMBER( 0L, "pgbrk", setToggleProperty, state.layout.pageBA, true ),
190 MEMBER( "@pict", "piccropb", setNumericProperty, picture.cropBottom, 0 ),
191 MEMBER( "@pict", "piccropl", setNumericProperty, picture.cropLeft, 0 ),
192 MEMBER( "@pict", "piccropr", setNumericProperty, picture.cropRight, 0 ),
193 MEMBER( "@pict", "piccropt", setNumericProperty, picture.cropTop, 0 ),
194 MEMBER( "@pict", "pich", setNumericProperty, picture.height, 0 ),
195 MEMBER( "@pict", "pichgoal", setNumericProperty, picture.desiredHeight, 0 ),
196 MEMBER( "@pict", "picscaled", setFlagProperty, picture.scaled, true ),
197 MEMBER( "@pict", "picscalex", setNumericProperty, picture.scalex, 0 ),
198 MEMBER( "@pict", "picscaley", setNumericProperty, picture.scaley, 0 ),
199 MEMBER( "@pict", "picw", setNumericProperty, picture.width, 0 ),
200 MEMBER( "@pict", "picwgoal", setNumericProperty, picture.desiredWidth, 0 ),
201 PROP( 0L, "plain", setPlainFormatting, 0L, 0 ),
202 PROP( "@pict", "pmmetafile", setPictureType, 0L, RTFPicture::WMF ),
203 PROP( "@pict", "pngblip", setPictureType, 0L, RTFPicture::PNG ),
204 MEMBER( 0L, "qc", setEnumProperty, state.layout.alignment, RTFLayout::Centered ),
205 MEMBER( 0L, "qj", setEnumProperty, state.layout.alignment, RTFLayout::Justified ),
206 MEMBER( 0L, "ql", setEnumProperty, state.layout.alignment, RTFLayout::Left ),
207 PROP( 0L, "qmspace", insertSymbol, 0L, 0x2004 ),
208 MEMBER( 0L, "qr", setEnumProperty, state.layout.alignment, RTFLayout::Right ),
209 PROP( 0L, "rdblquote", insertSymbol, 0L, 0x201d ),
210 MEMBER( "@colortbl", "red", setNumericProperty, red, 0 ),
211 MEMBER( 0L, "ri", setNumericProperty, state.layout.rightIndent, 0 ),
212 PROP( "Text", "row", insertTableRow, 0L, 0 ),
213 PROP( 0L, "rquote", insertSymbol, 0L, 0x2019 ),
214 PROP( 0L, "rtlmark", insertSymbol, 0L, 0x200f ),
215 MEMBER( 0L, "s", setNumericProperty, state.layout.style, 0 ),
216 MEMBER( 0L, "sa", setNumericProperty, state.layout.spaceAfter, 0 ),
217 MEMBER( 0L, "sb", setNumericProperty, state.layout.spaceBefore, 0 ),
218 MEMBER( 0L, "scaps", setToggleProperty, state.format.smallCaps, 0 ),
219 PROP( "Text", "sect", insertPageBreak, 0L, 0 ),
220 PROP( 0L, "sectd", setSectionDefaults, 0L, 0 ),
221 MEMBER( 0L, "sl", setNumericProperty, state.layout.spaceBetween, 0 ),
222 MEMBER( 0L, "slmult", setToggleProperty, state.layout.spaceBetweenMultiple, 0 ),
223 MEMBER( "@stylesheet", "snext", setNumericProperty, style.next, 0 ),
224 MEMBER( 0L, "strike", setToggleProperty, state.format.strike, 0 ),
225 MEMBER( 0L, "striked", setToggleProperty, state.format.striked, 0 ),
226 MEMBER( 0L, "sub", setEnumProperty, state.format.vertAlign, RTFFormat::SubScript ),
227 MEMBER( 0L, "super", setEnumProperty, state.format.vertAlign, RTFFormat::SuperScript ),
228 PROP( 0L, "tab", insertSymbol, 0L, 0x0009 ),
229 MEMBER( 0L, "titlepg", setFlagProperty, state.section.titlePage, true ),
230 MEMBER( 0L, "tldot", setEnumProperty, state.layout.tab.leader, RTFTab::Dots ),
231 MEMBER( 0L, "tlhyph", setEnumProperty, state.layout.tab.leader, RTFTab::Hyphens ),
232 MEMBER( 0L, "tlth", setEnumProperty, state.layout.tab.leader, RTFTab::ThickLine ),
233 MEMBER( 0L, "tlul", setEnumProperty, state.layout.tab.leader, RTFTab::Underline ),
234 MEMBER( 0L, "tqc", setEnumProperty, state.layout.tab.type, RTFTab::Centered ),
235 MEMBER( 0L, "tqdec", setEnumProperty, state.layout.tab.type, RTFTab::Decimal ),
236 MEMBER( 0L, "tqr", setEnumProperty, state.layout.tab.type, RTFTab::FlushRight ),
237 MEMBER( 0L, "trleft", setNumericProperty, state.tableRow.left, 0 ),
238 MEMBER( 0L, "trowd", setTableRowDefaults, state.tableRow, 0 ),
239 MEMBER( 0L, "trqc", setEnumProperty, state.tableRow.alignment, RTFLayout::Centered ),
240 MEMBER( 0L, "trql", setEnumProperty, state.tableRow.alignment, RTFLayout::Left ),
241 MEMBER( 0L, "trqr", setEnumProperty, state.tableRow.alignment, RTFLayout::Right ),
242 MEMBER( 0L, "trrh", setNumericProperty, state.tableRow.height, 0 ),
243 PROP( 0L, "tx", insertTabDef, 0L, 0 ),
244 MEMBER( 0L, "u", insertUnicodeSymbol, state.format.uc, 0 ),
245 MEMBER( 0L, "uc", setNumericProperty, state.format.uc, 0 ),
246 PROP( 0L, "ul", setSimpleUnderlineProperty, 0L, 0 ),
247 MEMBER( 0L, "ulc", setNumericProperty, state.format.underlinecolor, 0 ),
248 PROP( 0L, "uld", setUnderlineProperty, 0L, RTFFormat::UnderlineDot ),
249 PROP( 0L, "uldash", setUnderlineProperty, 0L, RTFFormat::UnderlineDash ),
250 PROP( 0L, "uldashd", setUnderlineProperty, 0L, RTFFormat::UnderlineDashDot ),
251 PROP( 0L, "uldashdd", setUnderlineProperty, 0L, RTFFormat::UnderlineDashDotDot ),
252 PROP( 0L, "uldb", setUnderlineProperty, 0L, RTFFormat::UnderlineDouble ),
253 PROP( 0L, "ulnone", setUnderlineProperty, 0L, RTFFormat::UnderlineNone ),
254 PROP( 0L, "ulth", setUnderlineProperty, 0L, RTFFormat::UnderlineThick ),
255 PROP( 0L, "ulw", setUnderlineProperty, 0L, RTFFormat::UnderlineWordByWord ),
256 PROP( 0L, "ulwave", setUnderlineProperty, 0L, RTFFormat::UnderlineWave ),
257 PROP( 0L, "ulhwave", setUnderlineProperty, 0L, RTFFormat::UnderlineWave ),
258 PROP( 0L, "ululdbwave", setUnderlineProperty, 0L, RTFFormat::UnderlineWave ),
259 MEMBER( 0L, "up", setUpProperty, state.format.baseline, 6 ),
260 MEMBER( 0L, "v", setToggleProperty, state.format.hidden, 0 ),
261 // ### TODO: \wbitmap: a Windows Device-Dependant Bitmap is not a BMP
262 PROP( "@pict", "wbitmap", setPictureType, 0L, RTFPicture::BMP ),
263 PROP( "@pict", "wmetafile", setPictureType, 0L, RTFPicture::EMF ),
264 PROP( 0L, "zwj", insertSymbol, 0L, 0x200d ),
265 PROP( 0L, "zwnj", insertSymbol, 0L, 0x200c )
266};
267
268static RTFField fieldTable[] =
269 {
270// id type subtype default value
271 { "AUTHOR", 8, 2, "NO AUTHOR" },
272 { "FILENAME", 8, 0, "NO FILENAME" },
273 { "TITLE", 8, 10, "NO TITLE" },
274 { "NUMPAGES", 4, 1, 0 },
275 { "PAGE", 4, 0, 0 },
276 { "TIME", -1, -1, 0 },
277 { "DATE", -1, -1, 0 },
278 { "HYPERLINK", 9, -1, 0 },
279 { "SYMBOL", -1, -1, 0 },
280 { "IMPORT", -1, -1, 0 }
281};
282
283
284// KWord attributes
285static const char *alignN[4] = { "left", "right", "justify", "center" };
286static const char *boolN[2] = { "false", "true" };
287static const char *borderN[4] = { "LEFTBORDER", "RIGHTBORDER", "TOPBORDER", "BOTTOMBORDER" };
288
289RTFImport::RTFImport( KoFilter *, const char *, const TQStringList& )
290 : KoFilter(), properties(181), destinationProperties(29), textCodec(0), utf8TextCodec(0)
291{
292 for (uint i=0; i < sizeof(propertyTable) / sizeof(propertyTable[0]); i++)
293 {
294 properties.insert( propertyTable[i].name, &propertyTable[i] );
295 }
296 for (uint i=0; i < sizeof(destinationPropertyTable) / sizeof(destinationPropertyTable[0]); i++)
297 {
298 destinationProperties.insert( destinationPropertyTable[i].name, &destinationPropertyTable[i] );
299 }
300 // DEBUG START
301 // Check the hash size (see TQDict doc)
302 kdDebug(30515) << properties.count() << " normal and " << destinationProperties.count() << " destination keywords loaded" << endl;
303 if (properties.size() < properties.count())
304 kdWarning(30515) << "Hash size of properties too small: " << properties.size() << ". It should be at least " << properties.count() << " and be a prime number"<< endl;
305 if (destinationProperties.size() < destinationProperties.count())
306 kdWarning(30515) << "Hash size of destinationProperties too small: " << destinationProperties.size() << ". It should be at least " << destinationProperties.count() << " and be a prime number"<< endl;
307 // DEBUG END
308 fnnum=0;
309}
310
311KoFilter::ConversionStatus RTFImport::convert( const TQCString& from, const TQCString& to )
312{
313 // This filter only supports RTF to KWord conversion
314 if ((from != "text/rtf") || (to != "application/x-kword"))
315 return KoFilter::NotImplemented;
316
317 TQTime debugTime;
318 debugTime.start();
319
320 // Are we in batch mode, i.e. non-interactive
321 m_batch=false;
322 if ( m_chain->manager() )
323 m_batch = m_chain->manager()->getBatchMode();
324
325 // Open input file
326 inFileName = m_chain->inputFile();
327 TQFile in( inFileName );
328
329 if (!in.open( IO_ReadOnly ))
330 {
331 kdError(30515) << "Unable to open input file!" << endl;
332 in.close();
333 if ( !m_batch )
334 {
335 KMessageBox::error( 0L,
336 i18n("The file cannot be loaded, as it cannot be opened."),
337 i18n("KWord's RTF Import Filter"), 0 );
338 }
339 return KoFilter::FileNotFound;
340 }
341
342 // Document should start with an opening brace
343 token.open( &in );
344 token.next();
345
346 if (token.type != RTFTokenizer::OpenGroup)
347 {
348 kdError(30515) << "Not an RTF file" << endl;
349 in.close();
350 if ( !m_batch )
351 {
352 KMessageBox::error( 0L,
353 i18n("The file cannot be loaded, as it seems not to be an RTF document."),
354 i18n("KWord's RTF Import Filter"), 0 );
355 }
356 return KoFilter::WrongFormat;
357 }
358
359 // Verify document type and version (RTF version 1.x)
360
361 token.next();
362
363 if (token.type != RTFTokenizer::ControlWord)
364 {
365 kdError(30515) << "Wrong document type" << endl;
366 in.close();
367 if ( !m_batch )
368 {
369 KMessageBox::error( 0L,
370 i18n("The document cannot be loaded, as it seems not to follow the RTF syntax."),
371 i18n("KWord's RTF Import Filter"), 0 );
372 }
373 return KoFilter::WrongFormat;
374 }
375
376 bool force = false; // By default do not force, despite an unknown keyword or version
377 if ( !qstrcmp( token.text, "rtf" ) )
378 {
379 // RTF is normally version 1 but at least Ted uses 0 as version number
380 if ( token.value > 1 )
381 {
382 kdError(30515) << "Wrong RTF version (" << token.value << "); version 0 or 1 expected" << endl;
383 if ( !m_batch )
384 {
385 force = ( KMessageBox::warningYesNo( 0L,
386 i18n("The RTF (Rich Text Format) document has an unexpected version number: %1. Continuing might result in an erroneous conversion. Do you want to continue?").arg( token.value ),
387 i18n("KWord's RTF Import Filter") ) == KMessageBox::Yes );
388 }
389 if ( !force )
390 {
391 in.close();
392 return KoFilter::WrongFormat;
393 }
394 }
395 }
396 else if ( !qstrcmp( token.text, "pwd" ) )
397 {
398 // PocketWord's PWD format is similar to RTF but has a version number of 2.
399 if ( token.value != 2 )
400 {
401 kdError(30515) << "Wrong PWD version (" << token.value << "); version 2 expected" << endl;
402 if ( !m_batch )
403 {
404 force = ( KMessageBox::warningYesNo( 0L,
405 i18n("The PWD (PocketWord's Rich Text Format) document has an unexpected version number: %1. Continuing might result in an erroneous conversion. Do you want to continue?").arg( token.value ),
406 i18n("KWord's RTF Import Filter") ) == KMessageBox::Yes );
407 }
408 if ( !force )
409 {
410 in.close();
411 return KoFilter::WrongFormat;
412 }
413 }
414 }
415 else if ( !qstrcmp( token.text, "urtf" ) )
416 {
417 // URTF seems to have either no version or having version 1
418 if ( token.value > 1 )
419 {
420 kdError(30515) << "Wrong URTF version (" << token.value << "); version 0 or 1 expected" << endl;
421 if ( !m_batch )
422 {
423 force = ( KMessageBox::warningYesNo( 0L,
424 i18n("The URTF (\"Unicode Rich Text Format\") document has an unexpected version number: %1. Continuing might result in an erroneous conversion. Do you want to continue?").arg( token.value ),
425 i18n("KWord's RTF Import Filter") ) == KMessageBox::Yes );
426 }
427 if ( !force )
428 {
429 in.close();
430 return KoFilter::WrongFormat;
431 }
432 }
433 }
434 else
435 {
436 kdError(30515) << "Wrong RTF document type (\\" << token.text << "); \\rtf, \\pwd or \\urtf expected" << endl;
437 in.close();
438 if ( !m_batch )
439 {
440 KMessageBox::error( 0L,
441 i18n("The RTF document cannot be loaded, as it has an unexpected first keyword: \\%1.").arg( token.text ),
442 i18n("KWord's RTF Import Filter"), 0 );
443 }
444 return KoFilter::WrongFormat;
445 }
446
447 table = 0;
448 pictureNumber = 0;
449
450 // Document-formatting properties
451 paperWidth = 12240;
452 paperHeight = 15840;
453 leftMargin = 1800;
454 topMargin = 1440;
455 rightMargin = 1800;
456 bottomMargin= 1440;
457 defaultTab = 720;
458 defaultFont = 0;
459 landscape = false;
460 facingPages = false;
461
462 // Create main document
463 frameSets.clear( 2 );
464 pictures.clear();
465 bodyText.node.clear( 3 );
466 firstPageHeader.node.clear( 3 );
467 oddPagesHeader.node.clear( 3 );
468 evenPagesHeader.node.clear( 3 );
469 firstPageFooter.node.clear( 3 );
470 oddPagesFooter.node.clear( 3 );
471 evenPagesFooter.node.clear( 3 );
472 author.clear();
473 company.clear();
474 title.clear();
475 doccomm.clear();
476
477 stateStack.push( state );
478
479 // Add a security item for the destination stack
480 destination.name = "!stackbottom";
481
482 changeDestination( destinationProperties["@rtf"] );
483
484 flddst = -1;
485 emptyCell = state.tableCell;
486 state.format.uc=1;
487 state.ignoreGroup = false;
488
489 utf8TextCodec=TQTextCodec::codecForName("UTF-8");
490 kdDebug(30515) << "UTF-8 asked, given: " << (utf8TextCodec?TQString(utf8TextCodec->name()):TQString("-none-")) << endl;
491
492 // There is no default encoding in RTF, it must be always declared. (But beware of buggy files!)
493 textCodec=TQTextCodec::codecForName("CP 1252"); // Or IBM 437 ?
494 kdDebug(30515) << "CP 1252 asked, given: " << (textCodec?TQString(textCodec->name()):TQString("-none-")) << endl;
495
496 // Parse RTF document
497 while (true)
498 {
499 bool firstToken = false;
500 bool ignoreUnknown = false;
501
502 token.next();
503
504 while (token.type == RTFTokenizer::OpenGroup)
505 {
506 // Store the current state on the stack
507 stateStack.push( state );
508 state.brace0 = false;
509 firstToken = true;
510 ignoreUnknown = false;
511
512 token.next();
513
514 if (token.type == RTFTokenizer::ControlWord && !qstrcmp( token.text, "*" ))
515 {
516 // {\*\control ...} destination
517 ignoreUnknown = true;
518 token.next();
519 }
520 }
521 if (token.type == RTFTokenizer::CloseGroup)
522 {
523 if (state.brace0)
524 {
525 // Close the current destination
526 (this->*destination.destproc)(0L);
527 //kdDebug(30515) << "Closing destination... " << destinationStack.count() << endl;
528 if (destinationStack.isEmpty())
529 {
530 kdWarning(30515) << "Destination stack is empty! Document might be buggy!" << endl;
531 // Keep the destination to save what can still be saved!
532 }
533 else
534 {
535 destination = destinationStack.pop();
536 }
537 }
538 // ### TODO: why can this not be simplified to use TQValueList::isEmpty()
539 if (stateStack.count() <= 1)
540 {
541 // End-of-document, keep formatting properties
542 stateStack.pop();
543 break;
544 }
545 else
546 {
547 // Retrieve the current state from the stack
548 state = stateStack.pop();
549 }
550 }
551 else if (token.type == RTFTokenizer::ControlWord)
552 {
553 RTFProperty *property = properties[token.text];
554
555 if (property != 0L)
556 {
557 if (property->onlyValidIn == 0L ||
558 property->onlyValidIn == destination.name ||
559 property->onlyValidIn == destination.group)
560 {
561 (this->*property->cwproc)( property );
562 }
563 }
564 else if (firstToken)
565 {
566 // Possible destination change
567 *(--token.text) = '@';
568 property = destinationProperties[token.text];
569
570 if ((property != 0L) &&
571 (property->onlyValidIn == 0L ||
572 property->onlyValidIn == destination.name ||
573 property->onlyValidIn == destination.group))
574 {
575 // Change destination
576 changeDestination( property );
577 }
578 else if (ignoreUnknown)
579 {
580 // Skip unknown {\* ...} destination
581 changeDestination( destinationProperties["@*"] );
582 debugUnknownKeywords[token.text]++;
583 }
584 else if ( !property )
585 {
586 kdWarning(30515) << "Unknown first non-ignorable token of a group: " << token.text << endl; kdDebug(30515) << "Destination: " << ( (void*) destination.name ) << " Destination stack depth: " << destinationStack.count() << endl;
587 // Put the second warning separately, as it can crash if destination.name is dangling
588 kdWarning(30515) << " Assuming destination: " << destination.name << endl;
589 debugUnknownKeywords[token.text]++;
590 }
591 }
592 else
593 {
594 debugUnknownKeywords[token.text]++;
595 }
596 }
597 else if (token.type == RTFTokenizer::PlainText || token.type == RTFTokenizer::BinaryData)
598 {
599 (this->*destination.destproc)(0L);
600 }
601 }
602
603 // Determine header and footer type
604 const int hType = facingPages
605 ? (state.section.titlePage ? 3 : 1) : (state.section.titlePage ? 2 : 0);
606
607 const bool hasHeader = !oddPagesHeader.node.isEmpty() ||
608 (facingPages &&!evenPagesHeader.node.isEmpty()) ||
609 (state.section.titlePage && !firstPageHeader.node.isEmpty());
610 const bool hasFooter = !oddPagesFooter.node.isEmpty() ||
611 (facingPages && !evenPagesFooter.node.isEmpty()) ||
612 (state.section.titlePage && !firstPageFooter.node.isEmpty());
613
614 kdDebug(30515) << "hType " << hType << " hasHeader " << hasHeader << " hasFooter " << hasFooter << endl;
615
616 // Create main document
617 DomNode mainDoc( "DOC" );
618 mainDoc.setAttribute( "mime", "application/x-kword" );
619 mainDoc.setAttribute( "syntaxVersion", "3" );
620 mainDoc.setAttribute( "editor", "KWord's RTF Import Filter" );
621 mainDoc.addNode( "PAPER" );
622 mainDoc.setAttribute( "format", 6 );
623 mainDoc.setAttribute( "columns", 1 );
624 mainDoc.setAttribute( "columnspacing", 2 );
625 mainDoc.setAttribute( "spHeadBody", 4 );
626 mainDoc.setAttribute( "spFootBody", 4 );
627 mainDoc.setAttribute( "zoom", 100 );
628 mainDoc.setAttribute( "width", .05*paperWidth );
629 mainDoc.setAttribute( "height", .05*paperHeight );
630 mainDoc.setAttribute( "orientation", landscape );
631 mainDoc.setAttribute( "hType", hType );
632 mainDoc.setAttribute( "fType", hType );
633 mainDoc.addNode( "PAPERBORDERS" );
634 mainDoc.addRect( leftMargin,
635 (hasHeader ? state.section.headerMargin : topMargin),
636 rightMargin,
637 (hasFooter ? state.section.footerMargin : bottomMargin) );
638 mainDoc.closeNode( "PAPERBORDERS" );
639 mainDoc.closeNode( "PAPER" );
640 mainDoc.addNode( "ATTRIBUTES" );
641 mainDoc.setAttribute( "standardpage", 1 );
642 mainDoc.setAttribute( "processing", 0 );
643 //mainDoc.setAttribute( "unit", "pt" ); // use KWord default instead
644 mainDoc.setAttribute( "hasHeader", hasHeader );
645 mainDoc.setAttribute( "hasFooter", hasFooter );
646 mainDoc.closeNode( "ATTRIBUTES" );
647 mainDoc.addNode( "FRAMESETS" );
648 mainDoc.addFrameSet( "Frameset 1", 1, 0 );
649 mainDoc.addFrame( leftMargin, topMargin, (paperWidth - rightMargin),
650 (paperHeight - bottomMargin), 1, 0, 0 );
651 mainDoc.closeNode( "FRAME" );
652 mainDoc.appendNode( bodyText.node );
653 mainDoc.closeNode( "FRAMESET" );
654 // Write out headers
655 if (hasHeader)
656 {
657 mainDoc.addFrameSet( "First Page Header", 1, 1 );
658 mainDoc.addFrame( leftMargin, state.section.headerMargin,
659 (paperWidth - rightMargin), (topMargin - 80), 0, 2, 0 );
660 mainDoc.closeNode( "FRAME" );
661 mainDoc.appendNode( firstPageHeader.node );
662 mainDoc.closeNode( "FRAMESET" );
663 mainDoc.addFrameSet( "Odd Pages Header", 1, 2 );
664 mainDoc.addFrame( leftMargin, state.section.headerMargin,
665 (paperWidth - rightMargin), (topMargin - 80), 0, 2, 1 );
666 mainDoc.closeNode( "FRAME" );
667 mainDoc.appendNode( oddPagesHeader.node );
668 mainDoc.closeNode( "FRAMESET" );
669 mainDoc.addFrameSet( "Even Pages Header", 1, 3 );
670 mainDoc.addFrame( leftMargin, state.section.headerMargin,
671 (paperWidth - rightMargin), (topMargin - 80), 0, 2, 2 );
672 mainDoc.closeNode( "FRAME" );
673 mainDoc.appendNode( evenPagesHeader.node );
674 mainDoc.closeNode( "FRAMESET" );
675 }
676 // Write out footers
677 if (hasFooter)
678 {
679 mainDoc.addFrameSet( "First Page Footer", 1, 4 );
680 mainDoc.addFrame( leftMargin, state.section.headerMargin,
681 (paperWidth - rightMargin), (topMargin - 80), 0, 2, 0 );
682 mainDoc.closeNode( "FRAME" );
683 mainDoc.appendNode( firstPageFooter.node );
684 mainDoc.closeNode( "FRAMESET" );
685 mainDoc.addFrameSet( "Odd Pages Footer", 1, 5 );
686 mainDoc.addFrame( leftMargin, state.section.headerMargin,
687 (paperWidth - rightMargin), (topMargin - 80), 0, 2, 1 );
688 mainDoc.closeNode( "FRAME" );
689 mainDoc.appendNode( oddPagesFooter.node );
690 mainDoc.closeNode( "FRAMESET" );
691 mainDoc.addFrameSet( "Even Pages Footer", 1, 6 );
692 mainDoc.addFrame( leftMargin, state.section.headerMargin,
693 (paperWidth - rightMargin), (topMargin - 80), 0, 2, 2 );
694 mainDoc.closeNode( "FRAME" );
695 mainDoc.appendNode( evenPagesFooter.node );
696 mainDoc.closeNode( "FRAMESET" );
697 }
698 // Write out footnotes
699 int num=1;
700 for(RTFTextState* i=footnotes.first();i;i=footnotes.next())
701 {
702 TQCString str;
703 str.setNum(num);
704 str.prepend("Footnote ");
705 num++;
706 mainDoc.addFrameSet( str, 1, 7 );
707 mainDoc.addFrame( leftMargin, paperHeight - bottomMargin-80,
708 (paperWidth - rightMargin), paperHeight-bottomMargin, 0, 2, 0 );
709 mainDoc.closeNode( "FRAME" );
710 mainDoc.appendNode( i->node );
711 mainDoc.closeNode( "FRAMESET" );
712 }
713 mainDoc.appendNode( frameSets );
714 mainDoc.closeNode( "FRAMESETS" );
715 mainDoc.addNode( "PICTURES" );
716 mainDoc.appendNode( pictures );
717 mainDoc.closeNode( "PICTURES" );
718 mainDoc.addNode( "STYLES" );
719 kwFormat.id = 1;
720 kwFormat.pos = 0;
721 kwFormat.len = 0;
722
723 // Process all styles in the style sheet
724 const TQValueList<RTFStyle>::ConstIterator endStyleSheet=styleSheet.end();
725 for (TQValueList<RTFStyle>::ConstIterator it=styleSheet.begin();it!=endStyleSheet;++it)
726 {
727 mainDoc.addNode( "STYLE" );
728 kwFormat.fmt = (*it).format;
729
730 // Search for 'following' style
731 for (TQValueList<RTFStyle>::ConstIterator it2=styleSheet.begin();it2!=endStyleSheet;++it2)
732 {
733 if ((*it2).layout.style == (*it).next)
734 {
735 mainDoc.addNode( "FOLLOWING" );
736 mainDoc.setAttribute( "name", CheckAndEscapeXmlText( (*it2).name ));
737 mainDoc.closeNode( "FOLLOWING");
738 break;
739 }
740 }
741 addLayout( mainDoc, (*it).name, (*it).layout, false );
742 addFormat( mainDoc, kwFormat, 0L );
743 mainDoc.closeNode( "STYLE" );
744 }
745 mainDoc.closeNode( "STYLES" );
746 mainDoc.closeNode( "DOC" );
747
748 // Create document info
749 DomNode docInfo( "document-info" );
750 docInfo.addNode( "log" );
751 docInfo.addNode( "text" );
752 docInfo.closeNode( "text" );
753 docInfo.closeNode( "log" );
754 docInfo.addNode( "author" );
755 docInfo.addNode( "company" );
756 docInfo.appendNode( company );
757 docInfo.closeNode( "company" );
758 docInfo.addNode( "full-name" );
759 docInfo.appendNode( author );
760 docInfo.closeNode( "full-name" );
761 docInfo.addNode( "email" );
762 docInfo.closeNode( "email" );
763 docInfo.addNode( "telephone" );
764 docInfo.closeNode( "telephone" );
765 docInfo.addNode( "fax" );
766 docInfo.closeNode( "fax" );
767 docInfo.addNode( "country" );
768 docInfo.closeNode( "country" );
769 docInfo.addNode( "postal-code" );
770 docInfo.closeNode( "postal-code" );
771 docInfo.addNode( "city" );
772 docInfo.closeNode( "city" );
773 docInfo.addNode( "street" );
774 docInfo.closeNode( "street" );
775 docInfo.closeNode( "author" );
776 docInfo.addNode( "about" );
777 docInfo.addNode( "abstract" );
778 docInfo.appendNode( doccomm );
779 docInfo.closeNode( "abstract" );
780 docInfo.addNode( "title" );
781 docInfo.appendNode( title );
782 docInfo.closeNode( "title" );
783 docInfo.closeNode( "about" );
784 docInfo.closeNode( "document-info" );
785
786 // Write out main document and document info
787 writeOutPart( "root", mainDoc );
788 writeOutPart( "documentinfo.xml", docInfo );
789 in.close();
790
791 kdDebug(30515) << "RTF FILTER TIME: " << debugTime.elapsed() << endl;
792
793 for (TQMap<TQString,int>::ConstIterator it=debugUnknownKeywords.begin();
794 it!=debugUnknownKeywords.end();it++)
795 kdDebug(30515) << "Unknown keyword: " << TQString( "%1" ).arg( it.data(), 4 ) << " * " << it.key() << endl;
796
797 return KoFilter::OK;
798}
799
800void RTFImport::ignoreKeyword( RTFProperty * )
801{
802}
803
804void RTFImport::setCodepage( RTFProperty * )
805{
806 TQTextCodec* oldCodec = textCodec;
807 TQCString cp;
808 if ( token.value == 10000 )
809 {
810 cp = "Apple Roman"; // ### TODO: how to support the other ones (TQt does not know them!)
811 }
812 else
813 {
814 cp.setNum( token.value );
815 cp.prepend("CP");
816 }
817 textCodec=TQTextCodec::codecForName(cp);
818 kdDebug(30515) << "\\ansicpg: codepage: " << token.value << "asked: "<< cp << " given: " << (textCodec?TQString(textCodec->name()):TQString("-none-")) << endl;
819 if ( ! textCodec )
820 textCodec = oldCodec;
821}
822
823void RTFImport::setMacCodepage( RTFProperty * )
824{
825 TQTextCodec* oldCodec = textCodec;
826 textCodec=TQTextCodec::codecForName("Apple Roman");
827 kdDebug(30515) << "\\mac " << (textCodec?TQString(textCodec->name()):TQString("-none-")) << endl;
828 if ( ! textCodec )
829 textCodec = oldCodec;
830}
831
832void RTFImport::setAnsiCodepage( RTFProperty * )
833{
834 TQTextCodec* oldCodec = textCodec;
835 textCodec=TQTextCodec::codecForName("CP1252");
836 kdDebug(30515) << "\\ansi " << (textCodec?TQString(textCodec->name()):TQString("-none-")) << endl;
837 if ( ! textCodec )
838 textCodec = oldCodec;
839}
840
841void RTFImport::setPcaCodepage( RTFProperty * )
842{
843 TQTextCodec* oldCodec = textCodec;
844 textCodec=TQTextCodec::codecForName("IBM 850"); // TQt writes the name with a space
845 kdDebug(30515) << "\\pca " << (textCodec?TQString(textCodec->name()):TQString("-none-")) << endl;
846 if ( ! textCodec )
847 textCodec = oldCodec;
848}
849
850void RTFImport::setPcCodepage( RTFProperty * )
851{
852 TQTextCodec* oldCodec = textCodec;
853 textCodec=TQTextCodec::codecForName("IBM 850"); // This is an approximation
854 kdDebug(30515) << "\\pc (approximation) " << (textCodec?TQString(textCodec->name()):TQString("-none-")) << endl;
855 if ( ! textCodec )
856 textCodec = oldCodec;
857}
858
859void RTFImport::setToggleProperty( RTFProperty *property )
860{
861 ((bool *)this)[property->offset] = (!token.hasParam || token.value != 0);
862}
863
864void RTFImport::setFlagProperty( RTFProperty *property )
865{
866 ((bool *)this)[property->offset] = property->value;
867}
868
869void RTFImport::setCharset( RTFProperty *property )
870{
871 TQCString cp;
872 switch(token.value) {
873 case 0: cp = "CP1252"; break; // ANSI_CHARSET
874 case 1: cp = "CP1252"; break; // DEFAULT_CHARSET
875 //case 2: cp = ""; break; // SYMBOL_CHARSET not supported yet.
876 case 77: cp = "Apple Roman"; break; // MAC_CHARSET
877 case 128: cp = "Shift-JIS"; break; // SHIFTJIS_CHARSET "CP932"
878 case 129: cp = "eucKR"; break; // HANGUL_CHARSET "CP949"
879 case 130: cp = "CP1361"; break; // JOHAB_CHARSET doesn't really seem to be supported by TQt :-(
880 case 134: cp = "GB2312"; break; // GB2312_CHARSET "CP936"
881 case 136: cp = "Big5-HKSCS"; break; // CHINESEBIG5_CHARSET "CP950"
882 case 161: cp = "CP1253"; break; // GREEK_CHARSET
883 case 162: cp = "CP1254"; break; // TURKISH_CHARSET
884 case 163: cp = "CP1258"; break; // VIETNAMESE_CHARSET
885 case 177: cp = "CP1255"; break; // HEBREW_CHARSET
886 case 178: cp = "CP1256"; break; // ARABIC_CHARSET / ARABICSIMPLIFIED_CHARSET
887 case 186: cp = "CP1257"; break; // BALTIC_CHARSET
888 case 204: cp = "CP1251"; break; // RUSSIAN_CHARSET / CYRILLIC_CHARSET
889 case 222: cp = "CP874"; break; // THAI_CHARSET
890 case 238: cp = "CP1250"; break; // EASTEUROPE_CHARSET / EASTERNEUROPE_CHARSET
891 case 255: cp = "CP850"; break; // OEM_CHARSET "IBM 850"
892 default: return;
893 }
894 TQTextCodec* oldCodec = textCodec;
895 textCodec=TQTextCodec::codecForName(cp);
896 kdDebug(30515) << "\\fcharset: charset: " << token.value << " codepage: "<< cp << " given: " << (textCodec?TQString(textCodec->name()):TQString("-none-")) << endl;
897 if ( ! textCodec )
898 textCodec = oldCodec;
899}
900
901void RTFImport::setNumericProperty( RTFProperty *property )
902{
903 *((int *)(((char *)this) + property->offset)) = token.hasParam ? token.value : property->value;
904}
905
906void RTFImport::setEnumProperty( RTFProperty *property )
907{
908 *((int *)(((char *)this) + property->offset)) = property->value;
909}
910
911void RTFImport::setFontStyleHint( RTFProperty* property )
912{
913 font.styleHint = TQFont::StyleHint( property->value );
914}
915
916void RTFImport::setPictureType( RTFProperty* property )
917{
918 picture.type = RTFPicture::PictureType( property->value );
919}
920
921void RTFImport::setSimpleUnderlineProperty( RTFProperty* )
922{
923 state.format.underline
924 = (!token.hasParam || token.value != 0)
925 ? RTFFormat::UnderlineSimple : RTFFormat::UnderlineNone;
926}
927
928void RTFImport::setUnderlineProperty( RTFProperty* property )
929{
930 state.format.underline = RTFFormat::Underline( property->value );
931}
932
933void RTFImport::setBorderStyle( RTFProperty *property )
934{
935 if (state.layout.border)
936 {
937 state.layout.border->style = static_cast <RTFBorder::BorderStyle> ( property->value );
938 }
939 else
940 {
941 for (uint i=0; i < 4; i++)
942 {
943 state.layout.borders[i].style = static_cast <RTFBorder::BorderStyle> ( property->value );
944 }
945 }
946}
947
948void RTFImport::setBorderProperty( RTFProperty *property )
949{
950 //kdDebug() << "setBorderProperty: " << endl;
951 if (state.layout.border)
952 {
953 state.layout.border->width = token.value;
954 }
955 else
956 {
957 for (uint i=0; i < 4; i++)
958 {
959 state.layout.borders[i].width = token.value;
960 }
961 }
962}
963
964void RTFImport::setBorderColor( RTFProperty * )
965{
966 if (state.layout.border)
967 {
968 state.layout.border->color = token.value;
969 }
970 else
971 {
972 for (uint i=0; i < 4; i++)
973 {
974 state.layout.borders[i].color = token.value;
975 }
976 }
977}
978
979void RTFImport::setUpProperty( RTFProperty * )
980{
981 state.format.baseline = token.hasParam ? -token.value : -6;
982}
983
984void RTFImport::setPlainFormatting( RTFProperty * )
985{
986 RTFFormat &format = state.format;
987
988 format.font = defaultFont;
989 format.fontSize = 24;
990 format.baseline = 0;
991 format.color = -1;
992 format.bgcolor = -1;
993 format.underlinecolor = -1;
994 format.vertAlign = RTFFormat::Normal;
995 format.bold = false;
996 format.italic = false;
997 format.strike = false;
998 format.striked = false;
999 format.hidden = false;
1000 format.caps = false;
1001 format.smallCaps = false;
1002
1003 format.underline = RTFFormat::UnderlineNone;
1004
1005 // Do not reset format.uc !
1006}
1007
1008void RTFImport::setParagraphDefaults( RTFProperty * )
1009{
1010 RTFLayout &layout = state.layout;
1011
1012 layout.tablist.clear();
1013 layout.tab.type = RTFTab::Left;
1014 layout.tab.leader = RTFTab::None;
1015
1016 for (uint i=0; i < 4; i++)
1017 {
1018 RTFBorder &border = layout.borders[i];
1019 border.color = -1;
1020 border.width = 0;
1021 border.style = RTFBorder::None;
1022 }
1023 layout.firstIndent = 0;
1024 layout.leftIndent = 0;
1025 layout.rightIndent = 0;
1026 layout.spaceBefore = 0;
1027 layout.spaceAfter = 0;
1028 layout.spaceBetween = 0;
1029 layout.spaceBetweenMultiple = false;
1030 layout.style = 0;
1031 layout.alignment = RTFLayout::Left;
1032 layout.border = 0L;
1033 layout.inTable = false;
1034 layout.keep = false;
1035 layout.keepNext = false;
1036 layout.pageBB = false;
1037 layout.pageBA = false;
1038}
1039
1040void RTFImport::setSectionDefaults( RTFProperty * )
1041{
1042 RTFSectionLayout &section = state.section;
1043
1044 section.headerMargin = 720;
1045 section.footerMargin = 720;
1046 section.titlePage = false;
1047}
1048
1049void RTFImport::setTableRowDefaults( RTFProperty * )
1050{
1051 RTFTableRow &tableRow = state.tableRow;
1052 RTFTableCell &tableCell = state.tableCell;
1053
1054 tableRow.height = 0;
1055 tableRow.left = 0;
1056 tableRow.alignment = RTFLayout::Left;
1057 tableRow.cells.clear();
1058 tableCell.bgcolor = -1;
1059
1060 for (uint i=0; i < 4; i++)
1061 {
1062 RTFBorder &border = tableCell.borders[i];
1063 border.color = -1;
1064 border.width = 0;
1065 border.style = RTFBorder::None;
1066 }
1067}
1068
1069void RTFImport::selectLayoutBorder( RTFProperty * property )
1070{
1071 state.layout.border = & state.layout.borders [ property->value ];
1072}
1073
1074void RTFImport::selectLayoutBorderFromCell( RTFProperty * property )
1075{
1076 state.layout.border = & state.tableCell.borders [ property->value ];
1077}
1078
1079void RTFImport::insertParagraph( RTFProperty * )
1080{
1081 if (state.layout.inTable)
1082 {
1083 if (textState->table == 0)
1084 {
1085 // Create a new table cell
1086 textState->table = ++table;
1087 }
1088 addParagraph( textState->cell, false );
1089 }
1090 else
1091 {
1092 if (textState->table)
1093 {
1094 finishTable();
1095 }
1096 addParagraph( textState->node, false );
1097 }
1098}
1099
1100void RTFImport::insertPageBreak( RTFProperty * )
1101{
1102 if (textState->length > 0)
1103 {
1104 insertParagraph();
1105 }
1106 addParagraph( textState->node, true );
1107}
1108
1109void RTFImport::insertTableCell( RTFProperty * )
1110{
1111 //{{
1112 bool b = state.layout.inTable;
1113 state.layout.inTable = true;
1114 insertParagraph();
1115 state.layout.inTable = b;
1116 //}}
1117 textState->frameSets << textState->cell.toString();
1118 textState->cell.clear( 3 );
1119}
1120
1121void RTFImport::insertTableRow( RTFProperty * )
1122{
1123 if (!textState->frameSets.isEmpty())
1124 {
1125 RTFTableRow row = state.tableRow;
1126 row.frameSets = textState->frameSets;
1127
1128 if (textState->rows.isEmpty())
1129 {
1130 char buf[64];
1131 sprintf( buf, "Table %d", textState->table );
1132 RTFLayout::Alignment align = row.alignment;
1133
1134 // Store the current state on the stack
1135 stateStack.push( state );
1136 resetState();
1137 state.layout.alignment = align; // table alignment
1138
1139 // Add anchor for new table (default layout)
1140 addAnchor( buf );
1141 addParagraph( textState->node, false );
1142
1143 // Retrieve the current state from the stack
1144 state = stateStack.pop();
1145 }
1146
1147 // Number of cell definitions should equal the number of cells
1148 while (row.cells.count() > row.frameSets.count())
1149 {
1150 // ### TODO: verify if it is the right action and how we have come here at all.
1151 row.cells.pop_back();
1152 }
1153 while (row.cells.count() < row.frameSets.count())
1154 {
1155 row.cells << row.cells.last();
1156 }
1157 int lx = row.left;
1158
1159 // Each cell should be at least 1x1 in size
1160 if (row.height == 0)
1161 {
1162 row.height = 1;
1163 }
1164 // ### TODO: use ConstIterator
1165 for (uint k=0; k < row.cells.count(); k++)
1166 {
1167 if ((row.cells[k].x - lx) < 1)
1168 row.cells[k].x = ++lx;
1169 else
1170 lx = row.cells[k].x;
1171 }
1172 if (row.left < 0)
1173 {
1174 // ### TODO: use ConstIterator
1175 for (uint k=0; k < row.cells.count(); k++)
1176 {
1177 row.cells[k].x -= row.left;
1178 }
1179 row.left = 0;
1180 }
1181 textState->rows << row;
1182 textState->frameSets.clear();
1183 }
1184}
1185
1186void RTFImport::insertCellDef( RTFProperty * )
1187{
1188 RTFTableCell &cell = state.tableCell;
1189 cell.x = token.value;
1190 state.tableRow.cells << cell;
1191 cell.bgcolor = -1;
1192
1193 for (uint i=0; i < 4; i++)
1194 {
1195 RTFBorder &border = cell.borders[i];
1196 border.color = -1;
1197 border.width = 0;
1198 border.style = RTFBorder::None;
1199 }
1200}
1201
1202void RTFImport::insertTabDef( RTFProperty * )
1203{
1204 RTFTab tab = state.layout.tab;
1205 tab.position = token.value;
1206 state.layout.tablist.push( tab );
1207 tab.type = RTFTab::Left;
1208 tab.leader = RTFTab::None;
1209}
1210
1211void RTFImport::insertUTF8( int ch )
1212{
1213 kdDebug(30515) << "insertUTF8: " << ch << endl;
1214 char buf[4];
1215 char *text = buf;
1216 char *tk = token.text;
1217 token.type = RTFTokenizer::PlainText;
1218 token.text = buf;
1219
1220 // We do not test if the character is not allowed in XML:
1221 // - it will be done later
1222 // - list definitions need to use char(1), char(2)...
1223 // ### FIXME: for high Unicode values, RTF uses negative values
1224 if (ch > 0x007f)
1225 {
1226 if (ch > 0x07ff)
1227 {
1228 *text++ = 0xe0 | (ch >> 12);
1229 ch = (ch & 0xfff) | 0x1000;
1230 }
1231 *text++ = ((ch >> 6) | 0x80) ^ 0x40;
1232 ch = (ch & 0x3f) | 0x80;
1233 }
1234 *text++ = ch;
1235 *text++ = 0;
1236
1237 TQTextCodec* oldCodec=textCodec;
1238
1239 if (utf8TextCodec)
1240 textCodec=utf8TextCodec;
1241 else
1242 kdError(30515) << "No UTF-8 TQTextCodec available" << endl;
1243
1244 (this->*destination.destproc)(0L);
1245
1246 textCodec=oldCodec;
1247 token.text = tk;
1248}
1249
1250void RTFImport::insertSymbol( RTFProperty *property )
1251{
1252 insertUTF8( property->value );
1253}
1254
1255void RTFImport::insertHexSymbol( RTFProperty * )
1256{
1257 //kdDebug(30515) << "insertHexSymbol: " << token.value << endl;
1258
1259 // Be careful, the value given in \' could be only one byte of a multi-byte character.
1260 // So it cannot be assumed that it will result in one character.
1261
1262 // Some files have \'00 which is pretty bad, as NUL is end of string for us.
1263 // (e.g. attachment #7758 of bug #90649)
1264 if ( !token.value )
1265 {
1266 kdWarning(30515) << "Trying to insert NUL character!" << endl;
1267 return;
1268 }
1269
1270 char tmpch[2] = {(char)(token.value), '\0'};
1271
1272 char *tk = token.text;
1273 token.type = RTFTokenizer::PlainText;
1274 token.text = tmpch;
1275
1276 (this->*destination.destproc)(0L);
1277
1278 token.text = tk;
1279}
1280
1281void RTFImport::insertUnicodeSymbol( RTFProperty * )
1282{
1283 const int ch = token.value;
1284
1285 // Ignore the next N characters (or control words)
1286 for (uint i=state.format.uc; i > 0; )
1287 {
1288 token.next();
1289
1290 if (token.type == RTFTokenizer::ControlWord)
1291 --i; // Ignore as single token
1292 else if (token.type == RTFTokenizer::OpenGroup ||
1293 token.type == RTFTokenizer::CloseGroup)
1294 {
1295 break;
1296 }
1297 else if (token.type == RTFTokenizer::PlainText)
1298 {
1299 const uint len = tqstrlen( token.text );
1300 if ( len < i )
1301 i -= len;
1302 else
1303 {
1304 token.text += i;
1305 break;
1306 }
1307 }
1308 }
1309 if (token.type != RTFTokenizer::PlainText)
1310 {
1311 token.type = RTFTokenizer::PlainText;
1312 token.text[0] = 0;
1313 }
1314 insertUTF8( ch ); // ### TODO: put it higher in this function
1315 (this->*destination.destproc)(0L);
1316}
1317
1318void RTFImport::parseFontTable( RTFProperty * )
1319{
1320 if (token.type == RTFTokenizer::OpenGroup)
1321 {
1322 font.name = TQString();
1323 font.styleHint = TQFont::AnyStyle;
1324 font.fixedPitch = 0;
1325 }
1326 else if (token.type == RTFTokenizer::PlainText)
1327 {
1328 if (!textCodec)
1329 {
1330 kdError(30515) << "No text codec for font!" << endl;
1331 return; // We have no text codec, so we cannot proceed!
1332 }
1333 // ### TODO VERIFY: a RTF group could be in the middle of the font name
1334 // ### TODO VERIFY: I do not now if it is specified in RTF but attachment #7758 of bug #90649 has it.
1335 // Semicolons separate fonts
1336 if (strchr( token.text, ';' ) == 0L) // ### TODO: is this allowed with multi-byte Asian characters?
1337 font.name += textCodec->toUnicode( token.text );
1338 else
1339 {
1340 // Add font to font table
1341 *strchr( token.text, ';' ) = 0; // ### TODO: is this allowed with multi-byte Asian characters?
1342 font.name += textCodec->toUnicode( token.text );
1343 // Use TQt to look up the closest matching installed font
1344 TQFont qFont( font.name );
1345 qFont.setFixedPitch( (font.fixedPitch == 1) );
1346 qFont.setStyleHint( font.styleHint );
1347 for(;!qFont.exactMatch();)
1348 {
1349 int space=font.name.findRev(' ', font.name.length());
1350 if(space==-1)
1351 break;
1352 font.name.truncate(space);
1353 qFont.setFamily( font.name );
1354 }
1355 const TQFontInfo info( qFont );
1356 const TQString newFontName ( info.family() );
1357 kdDebug(30515) << "Font " << state.format.font << " asked: " << font.name << " given: " << newFontName << endl;
1358
1359 if ( newFontName.isEmpty() )
1360 fontTable.insert( state.format.font, font.name );
1361 else
1362 fontTable.insert( state.format.font, newFontName );
1363 font.name.truncate( 0 );
1364 font.styleHint = TQFont::AnyStyle;
1365 font.fixedPitch = 0;
1366 }
1367 }
1368}
1369
1370void RTFImport::parseStyleSheet( RTFProperty * )
1371{
1372 if (token.type == RTFTokenizer::OpenGroup)
1373 {
1374 style.name = "";
1375 style.next = -1;
1376 }
1377 else if (token.type == RTFTokenizer::PlainText)
1378 {
1379 // Semicolons separate styles
1380 if (strchr( token.text, ';' ) == 0L) // ### TODO: is this allowed with multi-byte Asian characters?
1381 style.name += textCodec->toUnicode( token.text );
1382 else
1383 {
1384 // Add style to style sheet
1385 *strchr( token.text, ';' ) = 0; // ### TODO: is this allowed with multi-byte Asian characters?
1386 style.name += textCodec->toUnicode( token.text );
1387 style.format = state.format;
1388 style.layout = state.layout;
1389 style.next = (style.next == -1) ? style.layout.style : style.next;
1390 styleSheet << style;
1391 style.name.truncate( 0 );
1392 style.next = -1;
1393 }
1394 }
1395}
1396
1397void RTFImport::parseColorTable( RTFProperty * )
1398{
1399 if (token.type == RTFTokenizer::OpenGroup)
1400 {
1401 red = 0;
1402 green = 0;
1403 blue = 0;
1404 }
1405 else if (token.type == RTFTokenizer::PlainText)
1406 {
1407 // Note: the color table can be a simple ; character only, especially in PWD files
1408 // Search for semicolon(s)
1409 while ((token.text = strchr( token.text, ';' )))
1410 {
1411 colorTable << TQColor( red, green, blue );
1412 red = green = blue = 0;
1413 ++token.text;
1414 }
1415 }
1416}
1417
1418void RTFImport::parseBlipUid( RTFProperty * )
1419{
1420 if (token.type == RTFTokenizer::OpenGroup)
1421 {
1422 picture.identifier = TQString();
1423 }
1424 else if (token.type == RTFTokenizer::PlainText)
1425 {
1426 picture.identifier += TQString::fromUtf8( token.text );
1427 }
1428 else if (token.type == RTFTokenizer::CloseGroup)
1429 {
1430 kdDebug(30515) << "\\blipuid: " << picture.identifier << endl;
1431 }
1432}
1433
1434void RTFImport::parsePicture( RTFProperty * )
1435{
1436 if (state.ignoreGroup)
1437 return;
1438
1439 if (token.type == RTFTokenizer::OpenGroup)
1440 {
1441 picture.type = RTFPicture::PNG;
1442 picture.width = 0;
1443 picture.height = 0;
1444 picture.desiredWidth = 0;
1445 picture.desiredHeight = 0;
1446 picture.scalex = 100; // Default is 100%
1447 picture.scaley = 100; // Default is 100%
1448 picture.cropLeft = 0;
1449 picture.cropTop = 0;
1450 picture.cropRight = 0;
1451 picture.cropBottom = 0;
1452 picture.nibble = 0;
1453 picture.bits.truncate( 0 );
1454 picture.identifier = TQString();
1455 }
1456 else if (token.type == RTFTokenizer::PlainText)
1457 {
1458 if (picture.nibble)
1459 {
1460 *(--token.text) = picture.nibble;
1461 }
1462 uint n = tqstrlen( token.text ) >> 1;
1463 picture.bits.resize( picture.bits.size() + n );
1464 char *src = token.text;
1465 char *dst = (picture.bits.data() + picture.bits.size() - n);
1466
1467 // Store hexadecimal data
1468 while (n-- > 0)
1469 {
1470 int k = *src++;
1471 int l = *src++;
1472 *dst++ = (((k + ((k & 16) ? 0 : 9)) & 0xf) << 4) |
1473 ((l + ((l & 16) ? 0 : 9)) & 0xf);
1474 }
1475 picture.nibble = *src;
1476 }
1477 else if (token.type == RTFTokenizer::BinaryData)
1478 {
1479 picture.bits = token.binaryData;
1480 kdDebug(30515) << "Binary data of length: " << picture.bits.size() << endl;
1481 }
1482 else if (token.type == RTFTokenizer::CloseGroup)
1483 {
1484 const char *ext;
1485
1486 // Select file extension based on picture type
1487 switch (picture.type)
1488 {
1489 case RTFPicture::WMF:
1490 case RTFPicture::EMF:
1491 ext = ".wmf";
1492 break;
1493 case RTFPicture::BMP:
1494 ext = ".bmp";
1495 break;
1496 case RTFPicture::MacPict:
1497 ext = ".pict";
1498 break;
1499 case RTFPicture::JPEG:
1500 ext = ".jpg";
1501 break;
1502 case RTFPicture::PNG:
1503 default:
1504 ext = ".png";
1505 break;
1506 }
1507 const int id = ++pictureNumber;
1508 TQString pictName("pictures/picture");
1509 pictName += TQString::number(id);
1510 pictName += ext;
1511
1512 TQCString frameName;
1513 frameName.setNum(id);
1514 frameName.prepend("Picture ");
1515
1516 TQString idStr;
1517 if (picture.identifier.isEmpty())
1518 {
1519 idStr = pictName;
1520 }
1521 else
1522 {
1523 idStr += picture.identifier.stripWhiteSpace();
1524 idStr += ext;
1525 }
1526
1527 kdDebug(30515) << "Picture: " << pictName << " Frame: " << frameName << endl;
1528
1529 // Store picture
1530 KoStoreDevice* dev = m_chain->storageFile( pictName, KoStore::Write );
1531 if ( dev )
1532 dev->writeBlock(picture.bits.data(),picture.bits.size());
1533 else
1534 kdError(30515) << "Could not save: " << pictName << endl;
1535
1536
1537 // Add anchor to rich text destination
1538 addAnchor( frameName );
1539
1540 // It is safe, as we call currentDateTime only once for each picture
1541 const TQDateTime dt(TQDateTime::currentDateTime());
1542
1543 // Add pixmap or clipart (key)
1544 pictures.addKey( dt, idStr, pictName );
1545
1546 // Add picture or clipart frameset
1547 frameSets.addFrameSet( frameName, 2, 0 );
1548 //kdDebug(30515) << "Width: " << picture.desiredWidth << " scalex: " << picture.scalex << "%" << endl;
1549 //kdDebug(30515) << "Height: " << picture.desiredHeight<< " scaley: " << picture.scaley << "%" << endl;
1550 frameSets.addFrame( 0, 0,
1551 (picture.desiredWidth * picture.scalex) /100 ,
1552 (picture.desiredHeight * picture.scaley) /100 , 0, 1, 0 );
1553 frameSets.closeNode( "FRAME" );
1554 frameSets.addNode( "PICTURE" );
1555 frameSets.addKey( dt, idStr );
1556 frameSets.closeNode( "PICTURE" );
1557 frameSets.closeNode( "FRAMESET" );
1558 picture.identifier = TQString();
1559 }
1560}
1561
1562void RTFImport::addImportedPicture( const TQString& rawFileName )
1563{
1564 kdDebug(30515) << "Import field: reading " << rawFileName << endl;
1565
1566 if (rawFileName=="\\*")
1567 {
1568 kdError(30515) << "Import field without file name!" << endl;
1569 return;
1570 }
1571
1572 TQString slashPath( rawFileName );
1573 slashPath.replace('\\','/'); // Replace directory separators.
1574 // ### TODO: what with MS-DOS absolute paths? (Will only work for KOffice on Win32)
1575 TQFileInfo info;
1576 info.setFile( inFileName );
1577 TQDir dir( info.dirPath() );
1578
1579 KURL url;
1580 url.setPath(dir.filePath( rawFileName ));
1581
1582 kdDebug(30515) << "Path: " << url.prettyURL() << endl;
1583
1584 KoPicture pic;
1585 pic.setKeyAndDownloadPicture(url, 0); // ### TODO: find a better parent if possible
1586 if (pic.isNull())
1587 {
1588 kdError(30515) << "Import field: file is empty: " << rawFileName << endl;
1589 return;
1590 }
1591
1592 const uint id = ++pictureNumber;
1593
1594 TQString pictName("pictures/picture");
1595 pictName += TQString::number(id);
1596 pictName += '.';
1597 pictName += pic.getExtension();
1598
1599 TQCString frameName;
1600 frameName.setNum(id);
1601 frameName.prepend("Picture ");
1602
1603
1604 kdDebug(30515) << "Imported picture: " << pictName << " Frame: " << frameName << endl;
1605
1606 // Store picture
1607 KoStoreDevice* dev = m_chain->storageFile( pictName, KoStore::Write );
1608 if ( dev )
1609 pic.save(dev);
1610 else
1611 kdError(30515) << "Could not save: " << pictName << endl;
1612
1613 // Add anchor to rich text destination
1614 addAnchor( frameName );
1615
1616 // It is safe, as we call currentDateTime only once for each picture
1617 const TQDateTime dt( pic.getKey().lastModified() );
1618
1619 // Add picture key
1620 pictures.addKey( dt, rawFileName, pictName );
1621
1622 // Add picture frameset
1623 const TQSize size ( pic.getOriginalSize() * 20 ); // We need twips for addFrame
1624 frameSets.addFrameSet( frameName, 2, 0 );
1625 frameSets.addFrame( 0, 0, size.width(), size.height(), 0, 1, 0 );
1626 frameSets.closeNode( "FRAME" );
1627 frameSets.addNode( "PICTURE" );
1628 frameSets.addKey( dt, rawFileName );
1629 frameSets.closeNode( "PICTURE" );
1630 frameSets.closeNode( "FRAMESET" );
1631}
1632
1633void RTFImport::insertPageNumber( RTFProperty * )
1634{
1635 DomNode node;
1636 node.addNode( "PGNUM" );
1637 node.setAttribute( "subtype", 0 );
1638 node.setAttribute( "value", 0 );
1639 node.closeNode("PGNUM");
1640 addVariable( node, 4, "NUMBER", &state.format);
1641}
1642
1643void RTFImport::insertDateTime( RTFProperty *property )
1644{
1645 kdDebug(30515) << "insertDateTime: " << property->value << endl;
1646 addDateTime( TQString(), bool(property->value), state.format );
1647}
1648
1649void RTFImport::addDateTime( const TQString& format, const bool isDate, RTFFormat& fmt )
1650{
1651 bool asDate=isDate; // Should the variable be a date variable?
1652 TQString kwordFormat(format);
1653 if (format.isEmpty())
1654 {
1655 if (isDate)
1656 kwordFormat = "DATElocale";
1657 else
1658 kwordFormat = "TIMElocale";
1659 }
1660 else if (!isDate)
1661 {
1662 // It is a time with a specified format, so check if it is really a time
1663 // (as in KWord 1.3, a date can have a time format but a time cannot have a date format
1664 const TQRegExp regexp ("[yMd]"); // any date format character?
1665 asDate = (regexp.search(format)>-1); // if yes, then it is a date
1666 }
1667 DomNode node;
1668 if (asDate)
1669 {
1670 node.clear(7);
1671 node.addNode("DATE");
1672 node.setAttribute("year", 0);
1673 node.setAttribute("month", 0);
1674 node.setAttribute("day", 0);
1675 node.setAttribute("fix", 0);
1676 node.closeNode("DATE");
1677 addVariable(node, 0, kwordFormat, &fmt);
1678 }
1679 else
1680 {
1681 node.clear(7);
1682 node.addNode("TIME");
1683 node.setAttribute("hour", 0);
1684 node.setAttribute("minute", 0);
1685 node.setAttribute("second", 0);
1686 node.setAttribute("fix", 0);
1687 node.closeNode("TIME");
1688 addVariable(node, 2, kwordFormat, &fmt);
1689 }
1690}
1691
1692void RTFImport::parseField( RTFProperty * )
1693{
1694 if (token.type == RTFTokenizer::OpenGroup)
1695 {
1696 if (flddst == -1)
1697 {
1698 // Destination for unsupported fields
1699 flddst = (destinationStack.count() - 1);
1700 }
1701 fldinst = "";
1702 fldrslt = "";
1703 destination.group = 0L;
1704 }
1705 else if (token.type == RTFTokenizer::CloseGroup)
1706 {
1707 if (!fldinst.isEmpty())
1708 {
1709 DomNode node;
1710 TQStringList list ( TQStringList::split( ' ', fldinst, false ) );
1711 kdDebug(30515) << "Field: " << list << endl;
1712 uint i;
1713
1714 TQString fieldName ( list[0].upper() );
1715 fieldName.remove('\\'); // Remove \, especialy leading ones in OOWriter RTF files
1716 node.clear(7);
1717
1718 bool ok=false;
1719 for (i=0; i < sizeof(fieldTable) /sizeof(fieldTable[0]); i++)
1720 {
1721 if (fieldName == fieldTable[i].id)
1722 {
1723 kdDebug(30515) << "Field found: " << fieldTable[i].id << endl;
1724 ok=true;
1725 break;
1726 }
1727 }
1728 if (!ok)
1729 {
1730 kdWarning(30515) << "Field not supported: " << fieldName << endl;
1731 return;
1732 }
1733 if (fieldTable[i].type == 4)
1734 {
1735 node.addNode( "PGNUM" );
1736 node.setAttribute( "subtype", fieldTable[i].subtype );
1737 node.setAttribute( "value", 0 );
1738 node.closeNode("PGNUM");
1739 addVariable( node, 4, "NUMBER", &fldfmt);
1740 }
1741 else if (fieldTable[i].type == 8)
1742 {
1743 node.addNode( "FIELD" );
1744 node.setAttribute( "subtype", fieldTable[i].subtype );
1745 node.setAttribute( "value", fieldTable[i].value );
1746 node.closeNode("FIELD");
1747 addVariable( node, 8, "STRING", &fldfmt);
1748 }
1749 else if (fieldTable[i].type == 9)
1750 {
1751 TQString hrefName = TQString();
1752
1753 // Use ConstIterator
1754 for (uint i=1; i < list.count(); i++)
1755 {
1756 if (list[i] == "\\l")
1757 {
1758 hrefName += '#';
1759 }
1760 else if (list[i].startsWith( "\"" ) && list[i].endsWith( "\"" ))
1761 {
1762 hrefName += list[i].mid( 1, (list[i].length() - 2) );
1763 }
1764 else if (list[i].startsWith("http"))
1765 {
1766 hrefName += list[i];
1767 }
1768 }
1769 node.addNode( "LINK" );
1770 node.setAttribute( "linkName", !fldrslt.isNull() );
1771 node.setAttribute( "hrefName", hrefName );
1772 node.closeNode( "LINK" );
1773 addVariable( node, 9, "STRING", &fldfmt);
1774 }
1775 else if (fieldName == "SYMBOL")
1776 {
1777 if (list.count() >= 2)
1778 {
1779 int ch = list[1].toInt();
1780
1781 if (ch > 0)
1782 {
1783 // ### TODO: some error control (the destination might be invalid!)
1784 destination = destinationStack[flddst];
1785 state.format = fldfmt;
1786 insertUTF8( ch );
1787 }
1788 }
1789 }
1790 else if (fieldName == "TIME" || fieldName == "DATE")
1791 {
1792 TQString strFldinst( TQString::fromUtf8(fldinst) );
1793 TQRegExp regexp("\\\\@\\s*\"(.+)\""); // \@ "Text"
1794 if (regexp.search(strFldinst)==-1)
1795 { // Not found? Perhaps it is not in quotes (even if it is rare)
1796 kdWarning(30515) << "Date/time field format not in quotes!" << endl;
1797 strFldinst += ' '; // Add a space at the end to simplify the regular expression
1798 regexp = TQRegExp("\\\\@(\\S+)\\s+"); // \@some_text_up_to_a_space
1799 regexp.search(strFldinst);
1800 }
1801 TQString format(regexp.cap(1));
1802 kdDebug(30515) << "Date/time field format: " << format << endl;
1803 format.replace("am/pm", "ap");
1804 format.replace("a/p", "ap"); // Approximation
1805 format.replace("AM/PM", "AP");
1806 format.replace("A/P", "AP"); // Approximation
1807 format.remove("'"); // KWord 1.3 cannot protect text in date/time
1808 addDateTime( format, (fieldName == "DATE"), fldfmt );
1809 }
1810 else if (fieldName == "IMPORT")
1811 {
1812 addImportedPicture( list[1] );
1813 }
1814
1815 fldinst = "";
1816 }
1817
1818 if (flddst == (int) (destinationStack.count() - 1))
1819 {
1820 // Top-level field closed, clear field destination
1821 flddst = -1;
1822 }
1823 }
1824}
1825
1826void RTFImport::parseFldinst( RTFProperty * )
1827{
1828 if (token.type == RTFTokenizer::OpenGroup)
1829 {
1830 fldinst = "";
1831 }
1832 else if (token.type == RTFTokenizer::PlainText)
1833 {
1834 fldinst += token.text;
1835 }
1836}
1837
1838void RTFImport::parseFldrslt( RTFProperty * )
1839{
1840 if (fldinst.isEmpty())
1841 {
1842 if (token.type == RTFTokenizer::OpenGroup)
1843 {
1844 // ### TODO: why is this destination change not done with the corresponding procedure "changeDestination"?
1845 destination = destinationStack[flddst];
1846 destination.destproc = &RTFImport::parseFldrslt;
1847 }
1848 else if (token.type != RTFTokenizer::CloseGroup)
1849 {
1850 (this->*destinationStack[flddst].destproc)(0L);
1851 }
1852 }
1853 else if (token.type == RTFTokenizer::OpenGroup)
1854 {
1855 fldrslt = "";
1856 }
1857 else if (token.type == RTFTokenizer::PlainText)
1858 {
1859 fldrslt += token.text;
1860 }
1861 else if (token.type == RTFTokenizer::CloseGroup)
1862 {
1863 fldfmt = state.format;
1864 }
1865}
1866
1867void RTFImport::addVariable (const DomNode& spec, int type, const TQString& key, const RTFFormat* fmt)
1868{
1869 DomNode node;
1870
1871 node.clear( 6 );
1872 node.addNode( "VARIABLE" );
1873 node.closeTag(true);
1874 node.addNode("TYPE");
1875 node.setAttribute( "type", type );
1876 node.setAttribute( "key", CheckAndEscapeXmlText(key) );
1877 node.setAttribute( "text", 1 );
1878 node.closeNode("TYPE");
1879
1880 node.appendNode(spec);
1881 node.closeNode( "VARIABLE" );
1882 kwFormat.xmldata = node.toString();
1883 kwFormat.id = 4;
1884 kwFormat.pos = textState->length++;
1885 kwFormat.len = 1;
1886 if (fmt)
1887 kwFormat.fmt = *fmt;
1888 textState->text.append( '#' );
1889 textState->formats << kwFormat;
1890}
1891
1892void RTFImport::parseFootNote( RTFProperty * property)
1893{
1894 if(token.type==RTFTokenizer::OpenGroup)
1895 {
1896 RTFTextState* newTextState=new RTFTextState;
1897 footnotes.append(newTextState);
1898 fnnum++;
1899 destination.target = newTextState;
1900
1901 TQCString str;
1902 str.setNum(fnnum);
1903 str.prepend("Footnote ");
1904
1905 DomNode node;
1906
1907 node.clear( 7 );
1908 node.addNode("FOOTNOTE");
1909 node.setAttribute("numberingtype", "auto");
1910 node.setAttribute("notetype", "footnote");
1911 node.setAttribute("frameset", !str.isNull());
1912 node.setAttribute("value", fnnum);
1913 node.closeNode("FOOTNOTE");
1914 addVariable(node, 11, "STRING");
1915 }
1916 parseRichText(property);
1917}
1918
1919void RTFImport::parseRichText( RTFProperty * )
1920{
1921 if (token.type == RTFTokenizer::OpenGroup)
1922 {
1923 // Save and change rich text destination
1924 RTFTextState *oldState = textState;
1925 textState = destination.target;
1926 destination.target = oldState;
1927 destination.group = "Text";
1928
1929 // Initialize rich text state
1930 textState->text.clear();
1931 textState->node.clear( 3 );
1932 textState->cell.clear( 3 );
1933 textState->formats.clear();
1934 textState->frameSets.clear();
1935 textState->rows.clear();
1936 textState->table = 0;
1937 textState->length = 0;
1938 }
1939 else if (token.type == RTFTokenizer::PlainText)
1940 {
1941 // Ignore hidden text
1942 if (!state.format.hidden)
1943 {
1944 const int len = (token.text[0] < 0) ? 1 : tqstrlen( token.text );
1945
1946 // Check and store format changes
1947 if ( textState->formats.isEmpty() ||
1948 textState->formats.last().fmt != state.format ||
1949 ( !textState->formats.last().xmldata.isEmpty() ) )
1950 {
1951 kwFormat.fmt = state.format;
1952 kwFormat.id = 1;
1953 kwFormat.pos = textState->length;
1954 kwFormat.len = len;
1955 textState->formats << kwFormat;
1956 kwFormat.xmldata = TQString();
1957 }
1958 else
1959 {
1960 textState->formats.last().len += len;
1961 }
1962 textState->length += len;
1963 textState->text.addTextNode( token.text, textCodec );
1964 }
1965 }
1966 else if (token.type == RTFTokenizer::CloseGroup)
1967 {
1968 if (textState->length)
1969 insertParagraph();
1970 if (textState->table)
1971 finishTable();
1972
1973 // Restore rich text destination
1974 textState = destination.target;
1975 }
1976}
1977
1978void RTFImport::parsePlainText( RTFProperty * )
1979{
1980 if (token.type == RTFTokenizer::OpenGroup)
1981 {
1982 destination.target->node.clear();
1983 }
1984 else if (token.type == RTFTokenizer::PlainText)
1985 {
1986 destination.target->node.addTextNode( token.text, textCodec );
1987 }
1988}
1989
1990void RTFImport::parseGroup( RTFProperty * )
1991{
1992}
1993
1994void RTFImport::skipGroup( RTFProperty * )
1995{
1996 kdDebug(30515) << "Skip Group: " << token.type << endl;
1997 state.ignoreGroup = true;
1998}
1999
2000void RTFImport::resetState()
2001{
2002 setPlainFormatting();
2003 setParagraphDefaults();
2004 setSectionDefaults();
2005 setTableRowDefaults();
2006}
2007
2008void RTFImport::changeDestination( RTFProperty *property )
2009{
2010 kdDebug(30515) << "changeDestination: " << property->name << endl;
2011 destinationStack.push( destination );
2012 destination.name = property->name;
2013 destination.destproc = property->cwproc;
2014 if ( property->offset )
2015 destination.target = (RTFTextState*) ( (char *)this + property->offset );
2016 else
2017 destination.target = &m_dummyTextState;
2018
2019 state.brace0 = true;
2020
2021 if (property->value)
2022 {
2023 resetState();
2024 destination.group = 0L;
2025 }
2026
2027 // Send OpenGroup to destination
2028 token.type = RTFTokenizer::OpenGroup;
2029 (this->*destination.destproc)(0L);
2030}
2031
2032void RTFImport::addAnchor( const char *instance )
2033{
2034 DomNode node;
2035
2036 node.clear( 6 );
2037 node.addNode( "ANCHOR" );
2038 node.setAttribute( "type", "frameset" );
2039 node.setAttribute( "instance", instance );
2040 node.closeNode( "ANCHOR" );
2041 kwFormat.xmldata = node.toString();
2042 kwFormat.id = 6;
2043 kwFormat.pos = textState->length++;
2044 kwFormat.len = 1;
2045 textState->text.append( '#' );
2046 textState->formats << kwFormat;
2047}
2048
2049void RTFImport::addFormat( DomNode &node, const KWFormat& format, const RTFFormat* baseFormat )
2050{
2051 // Support both (\dn, \up) and (\sub, \super) for super/sub script
2052 int vertAlign = format.fmt.vertAlign;
2053 int fontSize = (format.fmt.fontSize >> 1);
2054 int vertAlign0 = ~vertAlign;
2055 int fontSize0 = ~fontSize;
2056
2057 // Adjust vertical alignment and font size if (\dn, \up) are used
2058 if (format.fmt.vertAlign == RTFFormat::Normal && format.fmt.baseline)
2059 {
2060 if (format.fmt.baseline < 0)
2061 vertAlign = RTFFormat::SuperScript;
2062 else // (format.baseline > 0)
2063 vertAlign = RTFFormat::SubScript;
2064
2065 fontSize += (fontSize >> 1);
2066 }
2067 if (baseFormat)
2068 {
2069 vertAlign0 = baseFormat->vertAlign;
2070 fontSize0 = (baseFormat->fontSize >> 1);
2071
2072 if (vertAlign0 == RTFFormat::Normal && baseFormat->baseline)
2073 {
2074 if (baseFormat->baseline < 0)
2075 vertAlign0 = RTFFormat::SuperScript;
2076 else // (baseFormat.baseline > 0)
2077 vertAlign0 = RTFFormat::SubScript;
2078
2079 fontSize0 += (fontSize0 >> 1);
2080 }
2081 }
2082 node.addNode( "FORMAT" );
2083 node.setAttribute( "id", (int)format.id );
2084
2085 if (format.len != 0)
2086 {
2087 // Add pos and len if this is not a style sheet definition
2088 node.setAttribute( "pos", (int)format.pos );
2089 node.setAttribute( "len", (int)format.len );
2090 }
2091 if ((format.id == 1)||(format.id == 4))
2092 {
2093 // Normal text, store changes between format and base format
2094 if (!baseFormat || format.fmt.color != baseFormat->color)
2095 {
2096 node.addNode( "COLOR" );
2097 node.addColor( ((uint)format.fmt.color >= colorTable.count())
2098 ? (TQColor &)TQt::black : colorTable[format.fmt.color] );
2099 node.closeNode( "COLOR" );
2100 }
2101 if ((uint)format.fmt.bgcolor < colorTable.count() &&
2102 (!baseFormat || format.fmt.bgcolor != baseFormat->bgcolor))
2103 {
2104 node.addNode( "TEXTBACKGROUNDCOLOR" );
2105 node.addColor( colorTable[format.fmt.bgcolor] );
2106 node.closeNode( "TEXTBACKGROUNDCOLOR" );
2107 }
2108 if (!baseFormat || format.fmt.font != baseFormat->font)
2109 {
2110 node.addNode( "FONT" );
2111
2112 if (fontTable.contains( format.fmt.font ))
2113 {
2114 node.setAttribute( "name", fontTable[format.fmt.font] );
2115 }
2116 node.closeNode( "FONT" );
2117 }
2118 if (!baseFormat || format.fmt.bold != baseFormat->bold)
2119 {
2120 node.addNode( "WEIGHT" );
2121 node.setAttribute( "value", (format.fmt.bold ? 75 : 50) );
2122 node.closeNode( "WEIGHT" );
2123 }
2124 if (fontSize != fontSize0)
2125 {
2126 node.addNode( "SIZE" );
2127 node.setAttribute( "value", fontSize );
2128 node.closeNode( "SIZE" );
2129 }
2130 if (!baseFormat || format.fmt.italic != baseFormat->italic)
2131 {
2132 node.addNode( "ITALIC" );
2133 node.setAttribute( "value", format.fmt.italic );
2134 node.closeNode( "ITALIC" );
2135 }
2136 if (!baseFormat || format.fmt.underline != baseFormat->underline )
2137 {
2138 node.addNode( "UNDERLINE" );
2139 TQCString st,styleline,wordbyword("0");
2140 st.setNum(format.fmt.underline);
2141 int underlinecolor = format.fmt.underlinecolor;
2142
2143 switch (format.fmt.underline)
2144 {
2145 case RTFFormat::UnderlineNone:
2146 default:
2147 {
2148 st="0";
2149 underlinecolor=-1; // Reset underline color
2150 break;
2151 }
2152 case RTFFormat::UnderlineSimple:
2153 {
2154 st="single";
2155 break;
2156 }
2157 case RTFFormat::UnderlineDouble:
2158 {
2159 st="double";
2160 break;
2161 }
2162 case RTFFormat::UnderlineThick:
2163 {
2164 st="single-bold";
2165 styleline="solid";
2166 break;
2167 }
2168
2169 case RTFFormat::UnderlineWordByWord:
2170 {
2171 st="single";
2172 styleline="solid";
2173 wordbyword="1";
2174 break;
2175 }
2176 case RTFFormat::UnderlineDash:
2177 {
2178 st="single";
2179 styleline="dash";
2180 break;
2181 }
2182 case RTFFormat::UnderlineDot:
2183 {
2184 st="single";
2185 styleline="dot";
2186 break;
2187 }
2188 case RTFFormat::UnderlineDashDot:
2189 {
2190 st="single";
2191 styleline="dashdot";
2192 break;
2193 }
2194 case RTFFormat::UnderlineDashDotDot:
2195 {
2196 st="single";
2197 styleline="dashdotdot";
2198 break;
2199 }
2200 case RTFFormat::UnderlineWave:
2201 {
2202 st="single";
2203 styleline="wave";
2204 break;
2205 }
2206 } // end of switch
2207 node.setAttribute( "value", !st.isNull() );
2208 node.setAttribute( "wordbyword", !wordbyword.isNull() );
2209 if ( !styleline.isEmpty() )
2210 node.setAttribute( "styleline", !styleline.isNull() );
2211 if ( underlinecolor >= 0 && uint(underlinecolor) < colorTable.count() )
2212 {
2213 node.setAttribute( "underlinecolor", colorTable[underlinecolor].name() );
2214 }
2215
2216 node.closeNode( "UNDERLINE" );
2217 }
2218 if (!baseFormat || format.fmt.strike != baseFormat->strike || format.fmt.striked != baseFormat->striked)
2219 {
2220 node.addNode( "STRIKEOUT" );
2221 TQCString st;
2222 st.setNum(format.fmt.strike);
2223 if(format.fmt.striked)
2224 st="double";
2225 node.setAttribute( "value", !st.isNull() );
2226 node.closeNode( "STRIKEOUT" );
2227 }
2228 if (vertAlign != vertAlign0)
2229 {
2230 node.addNode( "VERTALIGN" );
2231 node.setAttribute( "value", vertAlign );
2232 node.closeNode( "VERTALIGN" );
2233 }
2234 if (!baseFormat || format.fmt.caps != baseFormat->caps || format.fmt.smallCaps != baseFormat->smallCaps)
2235 {
2236 node.addNode( "FONTATTRIBUTE" );
2237 TQCString fontattr;
2238 if ( format.fmt.caps )
2239 fontattr="uppercase";
2240 else if ( format.fmt.smallCaps )
2241 fontattr="smallcaps";
2242 else
2243 fontattr="none";
2244 node.setAttribute( "value", !fontattr.isNull() );
2245 node.closeNode( "FONTATTRIBUTE" );
2246 }
2247 if (!baseFormat)
2248 {
2249 node.addNode( "CHARSET" );
2250 node.setAttribute( "value", (int)TQFont::Unicode );
2251 node.closeNode( "CHARSET" );
2252 }
2253 }
2254 if (format.id == 4 || format.id == 6)
2255 {
2256 // Variable or anchor
2257 node.closeTag( true );
2258 node.append( format.xmldata );
2259 }
2260 node.closeNode( "FORMAT" );
2261}
2262
2263void RTFImport::addLayout( DomNode &node, const TQString &name, const RTFLayout &layout, bool frameBreak )
2264{
2265 // Style name and alignment
2266 node.addNode( "NAME" );
2267 node.setAttribute( "value", CheckAndEscapeXmlText(name) );
2268 node.closeNode( "NAME" );
2269 node.addNode( "FLOW" );
2270 node.setAttribute( "align", alignN[layout.alignment] );
2271 node.closeNode( "FLOW" );
2272
2273 // Indents
2274 if (layout.firstIndent || layout.leftIndent || layout.rightIndent)
2275 {
2276 node.addNode( "INDENTS" );
2277
2278 if (layout.firstIndent)
2279 node.setAttribute( "first", .05*layout.firstIndent );
2280 if (layout.leftIndent)
2281 node.setAttribute( "left", .05*layout.leftIndent );
2282 if (layout.rightIndent)
2283 node.setAttribute( "right", .05*layout.rightIndent );
2284
2285 node.closeNode( "INDENTS" );
2286 }
2287
2288 // Offets
2289 if (layout.spaceBefore || layout.spaceAfter)
2290 {
2291 node.addNode( "OFFSETS" );
2292
2293 if (layout.spaceBefore)
2294 node.setAttribute( "before", .05*layout.spaceBefore );
2295 if (layout.spaceAfter)
2296 node.setAttribute( "after", .05*layout.spaceAfter );
2297
2298 node.closeNode( "OFFSETS" );
2299 }
2300
2301
2302 // Linespacing
2303
2304 TQString lineSpacingType;
2305 TQString lineSpacingValue;
2306 if ( layout.spaceBetweenMultiple )
2307 {
2308 // Note: 240 is a sort of magic value for one line (Once upon a time, it meant 12pt for a single line)
2309 switch (layout.spaceBetween )
2310 {
2311 case 240:
2312 {
2313 lineSpacingType = "single"; // ### TODO: does KWord really supports this?
2314 break;
2315 }
2316 case 360:
2317 {
2318 lineSpacingType = "oneandhalf";
2319 break;
2320 }
2321 case 480 :
2322 {
2323 lineSpacingType = "double";
2324 break;
2325 }
2326 default:
2327 {
2328 if ( layout.spaceBetween > 0 )
2329 {
2330 lineSpacingType = "multiple";
2331 lineSpacingValue.setNum( layout.spaceBetween / 240.0 );
2332 }
2333 break;
2334 }
2335 }
2336 }
2337 else
2338 {
2339 if (layout.spaceBetween > 0)
2340 {
2341 lineSpacingType = "atleast";
2342 lineSpacingValue.setNum( 0.05*layout.spaceBetween );
2343 }
2344 if (layout.spaceBetween < 0)
2345 {
2346 // negative linespace means "exact"
2347 lineSpacingType = "fixed" ;
2348 lineSpacingValue.setNum( -0.05*layout.spaceBetween );
2349 }
2350 }
2351
2352 if ( ! lineSpacingType.isEmpty() )
2353 {
2354 node.addNode( "LINESPACING" );
2355 node.setAttribute( "type", lineSpacingType );
2356 if ( ! lineSpacingValue.isEmpty() )
2357 node.setAttribute( "spacingvalue", lineSpacingValue );
2358 node.closeNode( "LINESPACING" );
2359 }
2360
2361 if (layout.keep || layout.pageBB || layout.pageBA || frameBreak || layout.keepNext)
2362 {
2363 node.addNode( "PAGEBREAKING" );
2364 node.setAttribute( "linesTogether", boolN[layout.keep] );
2365 node.setAttribute( "hardFrameBreak", boolN[layout.pageBB] );
2366 node.setAttribute( "hardFrameBreakAfter", boolN[layout.pageBA || frameBreak] );
2367 node.setAttribute( "keepWithNext", boolN[layout.keepNext] );
2368 node.closeNode( "PAGEBREAKING" );
2369 }
2370
2371 // Paragraph borders
2372 for (uint i=0; i < 4; i++)
2373 {
2374 const RTFBorder &border = layout.borders[i];
2375
2376 if (border.style != RTFBorder::None || border.width > 0)
2377 {
2378 node.addNode( borderN[i] );
2379 node.addColor( ((uint)border.color >= colorTable.count())
2380 ? (TQColor &)TQt::black : colorTable[border.color] );
2381 node.setAttribute( "style", (int)border.style & 0xf );
2382 node.setAttribute( "width", (border.width < 20) ? 1 : border.width /20 );
2383 node.closeNode( borderN[i] );
2384 }
2385 }
2386
2387 // Add automatic tab stop for hanging indent
2388 if (layout.firstIndent < 0 && layout.leftIndent > 0)
2389 {
2390 node.addNode( "TABULATOR" );
2391 node.setAttribute( "type", 0 );
2392 node.setAttribute( "ptpos", .05*layout.leftIndent );
2393 node.closeNode( "TABULATOR" );
2394 }
2395
2396 // Tabulators
2397 if (!layout.tablist.isEmpty())
2398 {
2399 // ### TODO: use ConstIterator
2400 for (uint i=0; i < layout.tablist.count(); i++)
2401 {
2402 const RTFTab &tab = layout.tablist[i];
2403 int l = (int)tab.leader;
2404 node.addNode( "TABULATOR" );
2405 node.setAttribute( "type", tab.type );
2406 node.setAttribute( "ptpos", .05*tab.position );
2407 node.setAttribute( "filling", (l < 2) ? l : ((l == 2) ? 1 : 2) );
2408 node.setAttribute( "width", (l == 4) ? 1. : 0.5 );
2409 node.closeNode( "TABULATOR" );
2410 }
2411 }
2412}
2413
2414void RTFImport::addParagraph( DomNode &node, bool frameBreak )
2415{
2416 node.addNode( "PARAGRAPH" );
2417 node.addNode( "TEXT" );
2418 node.appendNode( textState->text );
2419 node.closeNode( "TEXT" );
2420
2421 // Search for style in style sheet
2422 TQString name;
2423 const RTFFormat* format = &state.format;
2424 const int styleNum = state.layout.style;
2425
2426 const TQValueList<RTFStyle>::ConstIterator endStyleSheet = styleSheet.end();
2427 for ( TQValueList<RTFStyle>::ConstIterator it=styleSheet.begin(); it!=endStyleSheet; ++it )
2428 {
2429 if ( (*it).layout.style == styleNum )
2430 {
2431 if ( textState->length > 0 )
2432 {
2433 format = &(*it).format;
2434 }
2435 name = (*it).name;
2436 break;
2437 }
2438 }
2439 kwFormat.fmt = *format;
2440 kwFormat.id = 1;
2441 kwFormat.pos = 0;
2442 kwFormat.len = textState->length;
2443
2444 if ( name.isEmpty() )
2445 {
2446 kdWarning(30515) << "Style name empty! Assuming Standard!" << endl;
2447 name = "Standard";
2448 }
2449
2450 // Insert character formatting
2451 bool hasFormats = false;
2452
2453 for ( TQValueList<KWFormat>::ConstIterator it = textState->formats.begin(); it != textState->formats.end(); ++it )
2454 {
2455 if ( (*it).id != 1 || (*it).fmt != *format )
2456 {
2457 if (!hasFormats)
2458 {
2459 node.addNode( "FORMATS" );
2460 hasFormats = true;
2461 }
2462 addFormat( node, (*it), format );
2463 }
2464 }
2465 if (hasFormats)
2466 {
2467 node.closeNode( "FORMATS" );
2468 }
2469
2470 // Write out layout and format
2471 node.addNode( "LAYOUT" );
2472 addLayout( node, name, state.layout, frameBreak );
2473 addFormat( node, kwFormat, 0L );
2474 node.closeNode( "LAYOUT" );
2475 node.closeNode( "PARAGRAPH" );
2476
2477 // Clear plain text and formats for next paragraph
2478 textState->text.clear();
2479 textState->length = 0;
2480 textState->formats.clear();
2481}
2482
2483void RTFImport::finishTable()
2484{
2485 kdDebug(30515) << "Starting TFImport::finishTable..." << endl;
2486 TQCString emptyArray;
2487 TQValueList<int> cellx;
2488 int left = 0, right = 0;
2489
2490 insertTableRow();
2491
2492 // Calculate maximum horizontal extents
2493 // ### TODO: use ConstIterator
2494 for (uint i=0; i < textState->rows.count(); i++)
2495 {
2496 RTFTableRow &row = textState->rows[i];
2497
2498 if (row.left < left || i == 0)
2499 left = row.left;
2500 if (row.cells.last().x > right || i == 0)
2501 right = row.cells.last().x;
2502 }
2503
2504 // Force rectangular table (fill gaps with empty cells)
2505 // ### TODO: use ConstIterator
2506 for (uint i=0; i < textState->rows.count(); i++)
2507 {
2508 RTFTableRow &row = textState->rows[i];
2509
2510 if (row.left > left)
2511 {
2512 row.frameSets.prepend( emptyArray );
2513 emptyCell.x = row.left;
2514 row.cells.prepend( emptyCell );
2515 row.left = left;
2516 }
2517 if (row.cells.last().x < right)
2518 {
2519 row.frameSets << emptyArray;
2520 emptyCell.x = right;
2521 row.cells << emptyCell;
2522 }
2523 // ### TODO: use ConstIterator
2524 for (uint k=0; k < row.cells.count(); k++)
2525 {
2526 if (!cellx.contains( row.cells[k].x ))
2527 cellx << row.cells[k].x;
2528 }
2529 if (!cellx.contains( row.left ))
2530 {
2531 cellx << row.left;
2532 }
2533 }
2534
2535 // Sort vertical cell boundaries
2536 // ### TODO: use ConstIterator
2537 for (uint k=0; k < cellx.count(); k++)
2538 {
2539 for (uint l=k+1; l < cellx.count(); l++)
2540 {
2541 if (cellx[l] < cellx[k])
2542 {
2543 int tmp = cellx[l];
2544 cellx[l] = cellx[k];
2545 cellx[k] = tmp;
2546 }
2547 }
2548 }
2549 int y1 = 0;
2550
2551 // Store cell frame and table information
2552 // ### TODO: use ConstIterator
2553 for (uint i=0; i < textState->rows.count(); i++)
2554 {
2555 RTFTableRow &row = textState->rows[i];
2556 int h = abs( row.height );
2557 int y2 = y1 + ((h < 400) ? 400 : h); // KWord work-around
2558 int x1 = row.left;
2559
2560 for (uint k=0; k < row.cells.count(); k++)
2561 {
2562 char buf[64];
2563 int x2 = row.cells[k].x;
2564 int col = cellx.findIndex( x1 );
2565
2566 sprintf( buf, "Table %d Cell %d,%d", textState->table, i, col );
2567 frameSets.addFrameSet( buf, 1, 0 );
2568 sprintf( buf, "Table %d", textState->table );
2569 frameSets.setAttribute( "grpMgr", buf );
2570 frameSets.setAttribute( "row", (int)i );
2571 frameSets.setAttribute( "col", col );
2572 frameSets.setAttribute( "rows", 1 );
2573 frameSets.setAttribute( "cols", cellx.findIndex( x2 ) - col );
2574
2575 frameSets.addFrame( x1, y1, x2, y2, (row.height < 0) ? 2 : 0, 1, 0 );
2576
2577 // Frame borders
2578 for (uint i=0; i < 4; i++)
2579 {
2580 RTFBorder &border = row.cells[k].borders[i];
2581
2582 if (border.style != RTFBorder::None || border.width > 0)
2583 {
2584 const char *id = "lrtb";
2585 TQColor &c = ((uint)border.color >= colorTable.count())
2586 ? (TQColor &)TQt::black : colorTable[border.color];
2587 frameSets.addBorder( (int)id[i], c, (int)border.style & 0x0f,
2588 .05*(!border.width ? 10 : border.width) );
2589 }
2590 }
2591
2592 // Frame background color
2593 if ((uint)row.cells[k].bgcolor < colorTable.count())
2594 {
2595 TQColor &color = colorTable[row.cells[k].bgcolor];
2596 frameSets.setAttribute( "bkRed", color.red() );
2597 frameSets.setAttribute( "bkGreen", color.green() );
2598 frameSets.setAttribute( "bkBlue", color.blue() );
2599 }
2600 frameSets.closeNode( "FRAME" );
2601 frameSets.append( row.frameSets[k] );
2602 frameSets.closeNode( "FRAMESET" );
2603 x1 = x2;
2604 }
2605 y1 = y2;
2606 }
2607 textState->table = 0;
2608 textState->rows.clear();
2609 kdDebug(30515) << "Quitting TFImport::finishTable..." << endl;
2610}
2611
2612void RTFImport::writeOutPart( const char *name, const DomNode& node )
2613{
2614 KoStoreDevice* dev = m_chain->storageFile( name, KoStore::Write );
2615 if ( dev )
2616 {
2617 TQTextStream stream( dev );
2618 stream.setEncoding( TQTextStream::UnicodeUTF8 );
2619 stream << node.toString();
2620 }
2621 else
2622 kdError(30515) << "Could not write part " << name << endl;
2623}
KWord format.
Definition rtfimport.h:208
Paragraph border.
Definition rtfimport.h:72
RTF field description.
Definition rtfimport.h:52
Character-formatting properties.
Definition rtfimport.h:140
Paragraph-formatting properties.
Definition rtfimport.h:120
bool spaceBetweenMultiple
Is the linespacing defined as multiple of line height (true) or by a height in 1/20 pt (false)?
Definition rtfimport.h:132
int spaceBetween
Line spacing.
Definition rtfimport.h:131
RTF property (control word table entry).
Definition rtfimport.h:40
int value
default value
Definition rtfimport.h:47
int offset
offset of formatting property
Definition rtfimport.h:45
DomNode text
plain text (for paragraph or table cell)
Definition rtfimport.h:222