kwordfilter.cpp
1/* This file is part of the KDE project
2 Copyright (C) 2001 Ariya Hidayat <ariyahidayat@yahoo.de>
3
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public
6 License as published by the Free Software Foundation; either
7 version 2 of the License, or (at your option) any later version.
8
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
13
14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to
16 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18*/
19
20
21#include "kwordfilter.h"
22#include "parser.h"
23
24#include <tqfileinfo.h>
25#include <tqptrlist.h>
26#include <tqstring.h>
27#include <tqregexp.h>
28
29using namespace WP;
30
31// NOTE: 1 wpu = 1/1200 inch and 1 inch = 72 point
32static double WPUToPoint( unsigned wpu )
33{
34 return wpu * 72 / 1200;
35}
36
37// helper class
38class KWordFormat
39{
40 public:
41 bool bold, italic, underline, doubleunderline;
42 bool striked, superscript, subscript, redline;
43 bool color, highlight;
44 int red, green, blue;
45 int bgred, bggreen, bgblue;
46 double fontsize;
47 TQString fontface;
48
49 KWordFormat();
50 TQString asXML();
51};
52
53KWordFormat::KWordFormat()
54{
55 bold = italic = underline = doubleunderline = false;
56 striked = superscript = subscript = redline = false;
57 color = highlight = false;
58 red = green = blue = 0;
59 bgred = bggreen = bgblue = 255;
60 fontsize = 0.0;
61 fontface = "";
62}
63
64TQString KWordFormat::asXML()
65{
66 TQString result;
67
68 if( bold ) result.append( " <WEIGHT value=\"75\" />\n" );
69 if( italic ) result.append( " <ITALIC value=\"1\" />\n" );
70 if( underline ) result.append( " <UNDERLINE value=\"1\" />\n" );
71 if( doubleunderline ) result.append( " <UNDERLINE value=\"double\" />\n" );
72 if( striked ) result.append( " <STRIKEOUT value=\"1\" />\n" );
73 if( subscript ) result.append( "<VERTALIGN value=\"1\" />\n" );
74 if( superscript ) result.append( "<VERTALIGN value=\"2\" />\n" );
75
76 if( !fontface.isEmpty() )
77 result.append( "<FONT name=\"" + fontface + "\" />\n" );
78
79 if( fontsize > 0.0 )
80 result.append( " <SIZE value=\"" + TQString::number(fontsize) + "\" />\n" );
81
82 if( color )
83 result.append( " <COLOR red=\"" + TQString::number(red) +
84 "\" green=\"" + TQString::number(green) +
85 "\" blue=\"" + TQString::number(blue) + "\" />\n" );
86
87 if( highlight )
88 result.append( " <TEXTBACKGROUNDCOLOR red=\"" + TQString::number(bgred) +
89 "\" green=\"" + TQString::number(bggreen) +
90 "\" blue=\"" + TQString::number(bgblue) + "\" />\n" );
91
92 return result;
93}
94
95static TQString mapAlign( Token::Align align )
96{
97 switch( align )
98 {
99 case Token::Left: return "left";
100 case Token::Right: return "right";
101 case Token::Center: return "center";
102 case Token::Full: return "justify";
103 case Token::All: return "justify";
104 }
105 return "left";
106}
107
108// NOTE: KWord value for linespace: 72=one, 144=double, ..
109// Special case: "0" is normal, "oneandhalf" is 108, "double" is 144.
110static TQString mapLinespace( double linespace )
111{
112 return TQString::number( linespace );
113}
114
115KWordFilter::KWordFilter ():Parser ()
116{
117}
118
119bool
120KWordFilter::parse (const TQString & filename)
121{
122 int frameLeftMargin = 36, frameRightMargin = 36; // quick hack
123
124 if (!Parser::parse (filename))
125 return false;
126
127 // this will force very last text and formatting to be flushed as well
128 tokens.append( new Token( Token::HardReturn ) );
129
130 TQString text;
131 TQString layout;
132 TQString formats;
133 int LeftMargin = 0;
134 int TopMargin = 36;
135 int RightMargin = 0;
136 int BottomMargin = 36;
137 int LeftMarginAdjust = 0;
138 int RightMarginAdjust = 0;
139 int lm = 0, rm = 0;
140 Token::Align align = Token::Left;
141 double linespace = 1.0;
142
143 root = "";
144
145 KWordFormat flag;
146 int format_pos;
147 TQString fmt;
148
149 // FIXME replace with doc initial code or default style
150 format_pos = 0;
151 fmt = flag.asXML();
152
153 for (TQPtrListIterator < Token > it (tokens); it; ++it)
154 {
155 unsigned int ucode;
156 int attr;
157 int len;
158 Token *t = it.current ();
159 Token::Type type = t->type ();
160
161 switch (type)
162 {
163
164 case Token::Text:
165 text.append( t->text() );
166 break;
167
168 case Token::SoftSpace:
169 case Token::HardSpace:
170 text.append( " " );
171 break;
172
173 case Token::SoftReturn:
174 // ignore
175 break;
176
177 case Token::AttrOff:
178 case Token::AttrOn:
179 case Token::FontColor:
180 case Token::FontSize:
181 case Token::FontFace:
182 case Token::HighlightOn:
183 case Token::HighlightOff:
184
185 if( type == Token::FontColor )
186 {
187 flag.color = true;
188 flag.red = t->red();
189 flag.green = t->green();
190 flag.blue= t->blue();
191 }
192 else if( type == Token::HighlightOn )
193 {
194 flag.highlight = true;
195 flag.bgred = t->red();
196 flag.bggreen = t->green();
197 flag.bgblue = t->blue();
198 }
199 else if( type == Token::HighlightOff )
200 {
201 // RGB in the data is last used highlight color
202 // to go back to normal color, simply XOR would do the trick
203 flag.highlight = false;
204 flag.bgred ^= t->red();
205 flag.bggreen ^= t->green();
206 flag.bgblue ^= t->blue();
207 }
208 else if( type == Token::FontSize )
209 {
210 // WP font size is 1/3600th inch
211 // NOTE 72 pt is 1 inch
212 if( t->value() > 50 )
213 flag.fontsize = t->value()*72.0/3600;
214 }
215 else if( type == Token::FontFace )
216 {
217 flag.fontface = t->fontface();
218 }
219 else
220 {
221 attr = t->attr();
222 if( attr == Token::Bold ) flag.bold = ( type == Token::AttrOn );
223 if( attr == Token::Italic ) flag.italic = ( type == Token::AttrOn );
224 if( attr == Token::Underline) flag.underline = ( type == Token::AttrOn );
225 if( attr == Token::DoubleUnderline ) flag.doubleunderline = ( type == Token::AttrOn );
226 if( attr == Token::StrikedOut ) flag.striked = ( type == Token::AttrOn );
227 if( attr == Token::Subscript ) flag.subscript = ( type == Token::AttrOn );
228 if( attr == Token::Superscript ) flag.superscript = ( type == Token::AttrOn );
229 if( attr == Token::Redline ) flag.redline = ( type == Token::AttrOn );
230 }
231
232 // process previous fmt first
233 len = text.length() - format_pos;
234 formats.append ( "<FORMAT id=\"1\" pos=\"" + TQString::number( format_pos ) +
235 "\" len=\"" + TQString::number( len )+ "\">\n" );
236 formats.append( fmt );
237 formats.append ( "</FORMAT>\n" );
238
239 // now current format
240 fmt = flag.asXML();
241 format_pos= text.length();
242
243 break;
244
245 case Token::HardReturn:
246 case Token::DormantHardReturn:
247
248 // last formatting not flushed
249 // SEE ABOVE
250 len = text.length() - format_pos;
251 formats.append ( " <FORMAT id=\"1\" pos=\"" + TQString::number( format_pos ) +
252 "\" len=\"" + TQString::number( len )+ "\">\n" );
253 formats.append( " " + fmt );
254 formats.append ( " </FORMAT>\n" );
255
256 layout = "";
257 layout.append( "<LAYOUT>\n" );
258 layout.append( " <NAME value=\"Standard\" />\n" );
259 layout.append( " <FLOW align=\"" + mapAlign( align ) + "\" />\n" );
260 layout.append( " <LINESPACING value=\"" + mapLinespace( linespace) + "\" />\n" );
261 layout.append( " <LEFTBORDER width=\"0\" style=\"0\" />\n" );
262 layout.append( " <RIGHTBORDER width=\"0\" style=\"0\" />\n" );
263 layout.append( " <TOPBORDER width=\"0\" style=\"0\" />\n" );
264 layout.append( " <BOTTOMBORDER width=\"0\" style=\"0\" />\n" );
265 lm = LeftMargin + LeftMarginAdjust - frameLeftMargin;
266 rm = RightMargin + RightMarginAdjust - frameRightMargin;
267 layout.append( " <INDENTS left=\"" + TQString::number( TQMAX( 0, lm ) ) + "\"" +
268 " right=\"" + TQString::number( TQMAX( 0 , rm ) ) + "\"" +
269 " first=\"0\" />\n" );
270 layout.append( " <OFFSETS />\n" );
271 layout.append( " <PAGEBREAKING />\n" );
272 layout.append( " <COUNTER />\n" );
273 layout.append( " <FORMAT id=\"1\">\n" );
274 layout.append( " <WEIGHT value=\"50\" />\n" );
275 layout.append( " <ITALIC value=\"0\" />\n" );
276 layout.append( " <UNDERLINE value=\"0\" />\n" );
277 layout.append( " <STRIKEOUT value=\"0\" />\n" );
278 layout.append( " <CHARSET value=\"0\" />\n" );
279 layout.append( " <VERTALIGN value=\"0\" />\n" );
280 layout.append( " </FORMAT>\n" );
281 layout.append( "</LAYOUT>\n" );
282
283 // encode text for XML-ness
284 // FIXME could be faster without TQRegExp
285 text.replace( TQRegExp("&"), "&amp;" );
286 text.replace( TQRegExp("<"), "&lt;" );
287 text.replace( TQRegExp(">"), "&gt;" );
288 text.replace( TQRegExp("\""), "&quot;" );
289 text.replace( TQRegExp("'"), "&apos;" );
290
291 // construct the <PARAGRAPH>
292 root.append( "<PARAGRAPH>\n" );
293 root.append( "<TEXT>" + text + "</TEXT>\n" );
294 root.append( "<FORMATS>\n");
295 root.append( formats );
296 root.append( "</FORMATS>\n");
297 root.append( layout );
298 root.append( "</PARAGRAPH>\n" );
299
300 // for the next paragraph
301 text = "";
302 formats = "";
303 format_pos = 0;
304 fmt = flag.asXML();
305
306 break;
307
308 case Token::HardHyphen:
309 text.append( "-" );
310 break;
311
312 case Token::LeftMargin:
313 LeftMargin = (int) WPUToPoint( t->value() );
314 break;
315
316 case Token::RightMargin:
317 RightMargin = (int) WPUToPoint( t->value() );
318 break;
319
320 case Token::TopMargin:
321 TopMargin = (int) WPUToPoint( t->value() );
322 break;
323
324 case Token::BottomMargin:
325 BottomMargin = (int) WPUToPoint( t->value() );
326 break;
327
328 case Token::LeftMarginAdjust:
329 LeftMarginAdjust = (int)WPUToPoint( t->value() );
330 break;
331
332 case Token::RightMarginAdjust:
333 RightMarginAdjust = (int)WPUToPoint( t->value() );
334 break;
335
336 case Token::Justification:
337 align = t->align();
338 break;
339
340 case Token::Linespace:
341 // NOTE assume 1.0 = 12 pt, 2.0 = 24 pt, 1.5=18
342 // from parser.cpp, linespace is stored as 1/65536th inch
343 linespace = t->value() * 12.0 / 65536;
344 break;
345
346 case Token::ExtChar:
347 ucode = Parser::ExtCharToUnicode (t->charset (), t->charcode ());
348 if (ucode == 0) ucode = 32;
349 text.append( TQChar (ucode) );
350 break;
351
352 case Token::TabHardFlushRight:
353 // FIXME
354 text.append( " " );
355 break;
356
357 case Token::None:
358 default:
359 break;
360 };
361
362 }
363
364 TQString content = root;
365
366 root = "<!DOCTYPE DOC>\n";
367 root.append( "<DOC mime=\"application/x-kword\" syntaxVersion=\"2\" editor=\"KWord\">\n");
368
369 // quick hack, think of something better in the future
370 LeftMargin = RightMargin = 36;
371
372 // paper definition
373 root.append( "<PAPER width=\"595\" height=\"841\" format=\"1\" fType=\"0\" orientation=\"0\" hType=\"0\" columns=\"1\">\n" );
374 root.append( " <PAPERBORDERS left=\"" + TQString::number(frameLeftMargin) +
375 "\" right=\"" + TQString::number(frameRightMargin) +
376 "\" top=\"" + TQString::number(TopMargin) +
377 "\" bottom=\"" + TQString::number(BottomMargin) + "\" />\n" );
378 root.append( "</PAPER>\n" );
379
380 root.append( "<ATTRIBUTES standardpage=\"1\" hasFooter=\"0\" hasHeader=\"0\" processing=\"0\" />\n" );
381
382 root.append( "<FRAMESETS>\n" );
383 root.append( "<FRAMESET removable=\"0\" frameType=\"1\" frameInfo=\"0\" autoCreateNewFrame=\"1\">\n" );
384 root.append( "<FRAME right=\"567\" left=\"28\" top=\"42\" bottom=\"799\" />\n" );
385 root.append( content );
386 root.append( "</FRAMESET>\n" );
387 root.append( "</FRAMESETS>\n" );
388
389 root.append( "</DOC>\n" );
390
391 // in case no document summary is available, then make default
392 // set so that basename of the filename becomes the document title
393 // e.g /home/ariya/test/resume.wpd will have 'resume' as the title
394 if( docTitle.isEmpty() )
395 {
396 TQFileInfo info( filename );
397 docTitle = info.baseName();
398 }
399
400 // create document information
401 documentInfo = "<!DOCTYPE document-info>\n";
402
403 documentInfo += "<document-info>\n";
404 documentInfo += "<log><text></text></log>\n";
405
406 documentInfo += "<author>\n";
407 documentInfo += "<full-name>" + docAuthor + "</full-name>\n";
408 documentInfo += "<title></title>\n";
409 documentInfo += "<company></company>\n";
410 documentInfo += "<email></email>\n";
411 documentInfo += "<telephone></telephone>\n";
412 documentInfo += "</author>\n";
413
414 documentInfo += "<about>\n";
415 documentInfo += "<abstract><![CDATA[" + docAbstract + "]]></abstract>\n";
416 documentInfo += "<title>" + docTitle + "</title>\n";
417 documentInfo += "</about>\n";
418
419 documentInfo += "</document-info>";
420
421 return true;
422}
This class implements a functional parser for WordPerfect documents.
Definition parser.h:166
This class is an abstract encapsulation of a token to be use in the parser.
Definition parser.h:37