rtf/export/ExportFilter.cpp
1/*
2 This file is part of the KDE project
3 Copuright (C) 2001 Michael Johnson <mikej@xnet.com>
4 Copyright (C) 2001, 2002, 2003, 2004 Nicolas GOUTTE <goutte@kde.org>
5 Copyright (C) 2002 Ariya Hidayat <ariya@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 This library is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Library General Public License for more details.
16
17 You should have received a copy of the GNU Library General Public License
18 along with this library; see the file COPYING.LIB. If not, write to
19 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 * Boston, MA 02110-1301, USA.
21*/
22
23#include <tqstring.h>
24#include <tqtextcodec.h>
25#include <tqfile.h>
26#include <tqfileinfo.h>
27#include <tqfontinfo.h>
28#include <tqfontdatabase.h>
29#include <tqpicture.h>
30#include <tqimage.h>
31#include <tqregexp.h>
32#include <tqcolor.h>
33#include <tqdatetime.h>
34
35#include <tdelocale.h>
36#include <tdeglobal.h>
37#include <kdebug.h>
38
39#include <KWEFUtil.h>
40#include <KWEFBaseWorker.h>
41
42#include "ExportFilter.h"
43
44// 1 twip = 1/20 pt = 1/1400 inch
45// 1 inch = 25.4 mm
46
47// some conversion macros
48#define TWIP_TO_MM(x) (x)*25.4/1440.0
49#define MM_TO_TWIP(x) (x)*1440.0/25.4
50#define PT_TO_TWIP(x) (x)*20
51#define TWIP_TO_PT(x) (x)/20
52
53// map KWord field name to RTF field name
54// e.g authorName -> AUTHOR
55static TQString mapFieldName( const TQString& kwordField )
56{
57 TQString rtfField;
58
59 if( kwordField == "fileName" ) rtfField = "FILENAME";
60 else if( kwordField == "authorName" ) rtfField = "AUTHOR";
61 else if( kwordField == "docTitle" ) rtfField = "TITLE";
62
63 return rtfField;
64}
65
66RTFWorker::RTFWorker():
67 m_ioDevice(NULL), m_streamOut(NULL), m_eol("\r\n"), m_inTable(false),
68 m_paperOrientation(false), m_paperWidth(20), m_paperHeight(20),
69 m_paperMarginTop(72), m_paperMarginLeft(72),
70 m_paperMarginBottom(72), m_paperMarginRight(72), m_startPageNumber(1)
71{
72}
73
74static TQString WritePositiveKeyword(const TQString& keyword, const int value)
75{
76 TQString str;
77 str += keyword;
78
79 if (value>0) // The value of the keyword cannot be negative
80 str += TQString::number( value );
81 else
82 str += '0';
83
84 return str;
85}
86
87TQString RTFWorker::writeRow(const TQString& textCellHeader, const TQString& rowText, const FrameData& frame)
88{
89 TQString row;
90
91 row += "\\trowd\\trgaph60\\trql"; // start new row
92 row += WritePositiveKeyword("\\trrh", tqRound(PT_TO_TWIP(frame.minHeight)));
93 row += WritePositiveKeyword("\\trleft", tqRound(PT_TO_TWIP(frame.left) - m_paperMarginLeft));
94 //row += "\\trautofit0"; // ### VERIFY
95 row += textCellHeader;
96 row += " "; // End of keyword
97 row += rowText;
98
99 return row;
100}
101
102TQString RTFWorker::writeBorder(const char whichBorder, const int borderWidth, const TQColor& color)
103{
104 TQString str;
105 if (borderWidth > 0)
106 {
107 str += "\\clbrdr"; // Define border
108 str += whichBorder; // t=top, l=left, b=bottom, r=right
109 str += "\\brdrs\\brdrw"; // Single border; thickness
110 str += TQString::number(borderWidth);
111 if (color.isValid())
112 {
113 str += lookupColor("\\brdrcf",color);
114 }
115 }
116 return str;
117}
118
119TQString RTFWorker::makeTable(const FrameAnchor& anchor)
120{
121 TQString textBody; // Text to be returned
122 textBody += m_prefix;
123 m_prefix = TQString();
124 TQString rowText;
125
126 int rowCurrent = 0;
127 bool firstCellInRow = true;
128 FrameData firstFrameData;
129 int debugCellCurrent = 0; //DEBUG
130 int debugRowCurrent = 0; //DEBUG
131 TQString textCellHeader; // <celldef>
132
133 const bool oldInTable = m_inTable;
134 m_inTable = true;
135
136 TQValueList<TableCell>::ConstIterator itCell;
137 for (itCell=anchor.table.cellList.begin();
138 itCell!=anchor.table.cellList.end(); itCell++)
139 {
140 // ### TODO: rowspan, colspan
141 if (rowCurrent!=(*itCell).row)
142 {
143 rowCurrent = (*itCell).row;
144 textBody += writeRow( textCellHeader, rowText, firstFrameData);
145 textBody += "\\row";
146 textBody += m_eol;
147 rowText = TQString();
148 textCellHeader = TQString();
149 firstCellInRow=true;
150 debugRowCurrent ++; // DEBUG
151 debugCellCurrent = 0; //DEBUG
152 }
153
154 const FrameData& frame = (*itCell).frame;
155
156 if (firstCellInRow) // Not yet set, so set the position of the left border.
157 {
158 firstFrameData=frame;
159 firstCellInRow=false;
160 }
161
162 kdDebug(30515) << "Cell: " << debugRowCurrent << "," << debugCellCurrent
163 << " left: " << frame.left << " right: " << frame.right << " top: " << frame.top << " bottom " << frame.bottom << endl;
164 textCellHeader += writeBorder('t',tqRound(PT_TO_TWIP(frame.tWidth)),frame.tColor);
165 textCellHeader += writeBorder('l',tqRound(PT_TO_TWIP(frame.lWidth)),frame.lColor);
166 textCellHeader += writeBorder('b',tqRound(PT_TO_TWIP(frame.bWidth)),frame.bColor);
167 textCellHeader += writeBorder('r',tqRound(PT_TO_TWIP(frame.rWidth)),frame.rColor);
168 textCellHeader += WritePositiveKeyword("\\cellx",tqRound(PT_TO_TWIP(frame.right) - m_paperMarginRight)); //right border of cell
169
170 TQString endOfParagraph;
171 TQValueList<ParaData> *paraList = (*itCell).paraList;
172 TQValueList<ParaData>::ConstIterator it;
173 TQValueList<ParaData>::ConstIterator end(paraList->end());
174 for (it=paraList->begin();it!=end;++it)
175 {
176 rowText += endOfParagraph;
177 rowText += ProcessParagraphData( (*it).text,(*it).layout,(*it).formattingList);
178 rowText += m_eol;
179 endOfParagraph = "\\par"; // The problem is that the last paragraph ends with \cell not with \par
180 }
181 rowText += "\\cell";
182 debugCellCurrent ++; // DEBUG
183 }
184
185 textBody += writeRow( textCellHeader, rowText, firstFrameData);
186 textBody += "\\row\\pard"; // delimit last row
187 textBody += m_eol;
188 m_inTable = oldInTable;
189
190 return textBody;
191}
192
193TQString RTFWorker::makeImage(const FrameAnchor& anchor)
194{
195 TQString textBody; // Text to be returned
196
197 TQString strImageName(anchor.picture.koStoreName);
198 TQString strExt;
199 TQByteArray image;
200
201 kdDebug(30515) << "RTFWorker::makeImage" << endl << anchor.picture.koStoreName << endl;
202
203 const int pos=strImageName.findRev('.');
204 if(pos!=-1) strExt = strImageName.mid(pos+1).lower();
205
206 TQString strTag;
207 if (strExt=="png")
208 strTag="\\pngblip";
209#if 0
210 else if (strExt=="bmp")
211 strTag="\\dibitmap";
212#endif
213 else if ( (strExt=="jpeg") || (strExt=="jpg") )
214 strTag="\\jpegblip";
215 else if (strExt=="wmf")
216 strTag="\\wmetafile8"; // 8 == anisotropic
217 else
218 {
219 // either without extension or format is unknown
220 // let's try to convert it to PNG format
221 kdDebug(30515) << "Converting image " << anchor.picture.koStoreName << endl;
222
223 strTag="\\pngblip";
224 if( !loadAndConvertToImage(anchor.picture.koStoreName,strExt,"PNG",image) )
225 {
226 kdWarning(30515) << "Unable to convert " << anchor.picture.koStoreName << endl;
227 return TQString();
228 }
229 }
230 // ### TODO: SVG, TQPicture
231
232 // load the image, this isn't necessary for converted image
233 if( !image.size() )
234 if (!loadSubFile(anchor.picture.koStoreName,image))
235 {
236 kdWarning(30515) << "Unable to load picture " << anchor.picture.koStoreName << endl;
237 return TQString();
238 }
239
240
241 // find displayed width and height (in twips)
242 const long width = (long)(PT_TO_TWIP(anchor.frame.right - anchor.frame.left));
243 const long height = (long)(PT_TO_TWIP(anchor.frame.bottom - anchor.frame.top));
244
245 // find original image width and height (in twips)
246 long origWidth = width;
247 long origHeight = height;
248 if( strExt == "wmf" )
249 {
250 // special treatment for WMF with metaheader
251 // d7cdc69a is metaheader magic id
252 TQ_UINT8* data = (TQ_UINT8*) image.data();
253 if( ( data[0] == 0xd7 ) && ( data[1] == 0xcd ) &&
254 ( data[2] == 0xc6 ) && ( data[3] == 0x9a ) &&
255 ( image.size() > 22 ) )
256 {
257 // grab bounding box, find original size
258 unsigned left = data[6]+(data[7]<<8);
259 unsigned top = data[8]+(data[9]<<8);
260 unsigned right = data[10]+(data[11]<<8);
261 unsigned bottom = data[12]+(data[13]<<8);
262 origWidth = (long) (MM_TO_TWIP(right-left)/100);
263 origHeight = (long) (MM_TO_TWIP(bottom-top)/100);
264
265 // throw away WMF metaheader (22 bytes)
266 for( uint i=0; i<image.size()-22; i++)
267 image.at(i) = image.at(i+22); // As we use uint, we need at() ( [] uses int only .)
268 image.resize( image.size()-22 );
269 }
270 }
271 else
272 {
273 // It must be an image
274 TQImage img( image );
275 if( img.isNull() )
276 {
277 kdWarning(30515) << "Unable to load picture as image " << anchor.picture.koStoreName << endl;
278 return TQString();
279 }
280 // check resolution, assume 2835 dpm (72 dpi) if not available
281 int resx = img.dotsPerMeterX();
282 int resy = img.dotsPerMeterY();
283 if( resx <= 0 ) resx = 2835;
284 if( resy <= 0 ) resy = 2835;
285
286 origWidth = long(img.width() * 2834.65 * 20 / resx);
287 origHeight = long(img.height() * 2834.65 * 20 / resy);
288 }
289
290 // Now that we are sure to have a valid image, we can write the RTF tags
291 textBody += "{\\pict";
292 textBody += strTag;
293
294 // calculate scaling factor (in percentage)
295 int scaleX = width * 100 / origWidth;
296 int scaleY = height * 100 / origHeight;
297
298 // size in 1/100 mm
299 int picw = (int)(100 * TWIP_TO_MM(origWidth));
300 int pich = (int)(100 * TWIP_TO_MM(origHeight));
301
302 textBody += "\\picscalex";
303 textBody += TQString::number(scaleX, 10);
304 textBody += "\\picscaley";
305 textBody += TQString::number(scaleY, 10);
306 textBody += "\\picw";
307 textBody += TQString::number(picw,10);
308 textBody += "\\pich";
309 textBody += TQString::number(pich,10);
310 textBody += "\\picwgoal";
311 textBody += TQString::number(origWidth, 10);
312 textBody += "\\pichgoal";
313 textBody += TQString::number(origHeight, 10);
314
315 textBody+=" ";
316 const char hex[] = "0123456789abcdef";
317 for (uint i=0; i<image.size(); i++)
318 {
319 if (!(i%40))
320 textBody += m_eol;
321 const char ch=image.at(i);
322 textBody += hex[(ch>>4)&0x0f]; // Done this way to avoid signed/unsigned problems
323 textBody += hex[(ch&0x0f)];
324 }
325
326
327 textBody+="}";
328
329 return textBody;
330}
331
332TQString RTFWorker::formatTextParagraph(const TQString& strText,
333 const FormatData& formatOrigin, const FormatData& format)
334{
335 TQString str;
336
337 if (!format.text.missing)
338 {
339 // Opening elements
340 str+=openSpan(formatOrigin,format);
341 }
342
343 TQString strEscaped = escapeRtfText(strText);
344
345 // Replace line feeds by forced line breaks
346 int pos;
347 TQString strBr("\\line ");
348 while ((pos=strEscaped.find(TQChar(10)))>-1)
349 {
350 strEscaped.replace(pos,1,strBr);
351 }
352
353 str+=strEscaped;
354
355 if (!format.text.missing)
356 {
357 // Closing elements
358 str+=closeSpan(formatOrigin,format);
359 }
360
361 return str;
362}
363
364TQString RTFWorker::ProcessParagraphData ( const TQString &paraText,
365 const LayoutData& layout, const ValueListFormatData &paraFormatDataList)
366{
367 TQString str;
368 TQString content;
369 TQString markup;
370 // open paragraph
371 markup += "\\pard";
372 markup += "\\plain";
373 if (m_inTable)
374 markup += "\\intbl";
375
376//lists
377 if (layout.counter.style)
378 {
379 markup += "{\\pntext\\pard\\plain";
380 if( layout.formatData.text.fontSize >= 0)
381 {
382 markup += "\\fs";
383 markup += TQString::number((2 * layout.formatData.text.fontSize));
384 markup += lookupFont("\\f",layout.formatData.text.fontName);
385 }
386 markup += " ";
387 markup += layout.counter.text;
388 markup += "\\tab}{\\*\\pn";
389 if (layout.counter.style > 5)
390 {
391 markup += "\\pnlvlblt ";
392 markup += "{\\pntxtb ";
393 if (!layout.counter.lefttext.isEmpty() && layout.counter.lefttext != "{" && layout.counter.lefttext != "}")
394 {
395 markup += layout.counter.lefttext;
396 }
397 switch (layout.counter.style)
398 {
399 case 6:
400 {
401 //custom bullets (one char)
402 //TODO: use correct character/sign for bullet
403
404 markup += layout.counter.customCharacter;
405 break;
406 }
407 case 7:
408 {
409 //custom bullets (complex)
410 markup += layout.counter.text;
411 break;
412 }
413 case 8:
414 {
415 //circle bullets
416 //TODO: use correct character/sign for bullet
417 markup += "\\bullet";
418 break;
419 }
420 case 9:
421 {
422 //square bullets
423 //TODO: use correct character/sign for bullet
424 markup += "\\bullet";
425 break;
426 }
427 case 10:
428 {
429 //disc bullets
430 //TODO: make work in OO
431 markup += "\\bullet";
432 break;
433 }
434 case 11:
435 {
436 //disc bullets
437 //TODO: use correct character/sign for bullet
438 markup += layout.counter.text;
439 break;
440 }
441 default:
442 markup += "\\bullet";
443 }
444 markup += "}";
445 }
446 else
447 {
448 if (layout.counter.numbering!=0)
449 {
450 markup += "\\pnlvl";
451 markup += TQString::number(layout.counter.depth + 1);
452 markup += "\\pnprev1";
453 }
454 else if (layout.counter.style==1)
455 {
456 markup += "\\pnlvlbody";
457 }
458 else
459 {
460 markup += "\\pnlvl";
461 markup += TQString::number(11 - layout.counter.style);
462 }
463
464 switch (layout.counter.style)
465 {
466 case 1:
467 {
468 markup += "\\pndec";
469 break;
470 }
471 case 2:
472 {
473 markup += "\\pnlcltr";
474 break;
475 }
476 case 3:
477 {
478 markup += "\\pnucltr";
479 break;
480 }
481 case 4:
482 {
483 markup += "\\pnlcrm";
484 break;
485 }
486 case 5:
487 {
488 markup += "\\pnucrm";
489 break;
490 }
491 default:
492 markup += "\\pndec";
493 }
494 markup += "{\\pntxtb ";
495 markup += layout.counter.lefttext;
496 markup += " }";
497 }
498 markup += "{\\pntxta ";
499 markup += layout.counter.righttext;
500 markup += " }";
501
502 // ### FIXME: that is too late! And why at every list paragraph? (See bug #88241)
503 if (layout.counter.start!=0)
504 {
505 markup += "\\pnstart";
506 markup += TQString::number(layout.counter.start);
507 }
508 else
509 {
510 markup += "\\pnstart1";
511 }
512 markup += "\\pnindent0\\pnhang";
513
514 if( layout.formatData.text.fontSize > 0 )
515 {
516 markup += "\\pnfs";
517 markup += TQString::number((2 * layout.formatData.text.fontSize));
518 }
519
520 if( !layout.formatData.text.fontName.isEmpty() )
521 {
522 markup += lookupFont("\\pnf", layout.formatData.text.fontName);
523 }
524
525 markup += "}";
526}
527
528
529 LayoutData styleLayout;
530 markup += lookupStyle(layout.styleName, styleLayout);
531 markup += layoutToRtf(styleLayout,layout,true);
532
533 if ( 1==layout.formatData.text.verticalAlignment )
534 {
535 markup += "\\sub"; //Subscript
536 }
537 else if ( 2==layout.formatData.text.verticalAlignment )
538 {
539 markup += "\\super"; //Superscript
540 }
541
542
543 if (layout.pageBreakBefore)
544 content += "\\page ";
545
546
547
548
549
550 if (!paraText.isEmpty())
551 {
552
553 ValueListFormatData::ConstIterator paraFormatDataIt;
554
555 TQString partialText;
556
557 FormatData formatRef = layout.formatData;
558
559 for ( paraFormatDataIt = paraFormatDataList.begin ();
560 paraFormatDataIt != paraFormatDataList.end ();
561 paraFormatDataIt++ )
562 {
563 if (1==(*paraFormatDataIt).id)
564 {
565 //Retrieve text
566 partialText=paraText.mid ( (*paraFormatDataIt).pos, (*paraFormatDataIt).len );
567 content +=formatTextParagraph(partialText, formatRef, *paraFormatDataIt);
568 }
569 else if (4==(*paraFormatDataIt).id)
570 {
571 // ### TODO: put date/time fields into own method.
572 if ( (0==(*paraFormatDataIt).variable.m_type) // date
573 || (2==(*paraFormatDataIt).variable.m_type) ) // time
574 {
575 // ### TODO: fixed/variable date
576 content += "{\\field{\\*\\fldinst ";
577 if (0==(*paraFormatDataIt).variable.m_type)
578 content += "DATE ";
579 else
580 content += "TIME ";
581 TQString key((*paraFormatDataIt).variable.m_key.mid(4));
582 kdDebug(30515) << "Time format: " << key << endl;
583 if (key.startsWith("locale"))
584 {
585 if (key == "locale" )
586 {
587 if (0==(*paraFormatDataIt).variable.m_type)
588 key = TDEGlobal::locale()->dateFormat();
589 else
590 key = TDEGlobal::locale()->timeFormat();
591 }
592 else if ( key == "localeshort" )
593 key = TDEGlobal::locale()->dateFormatShort();
594 else if ( key == "localedatetime" )
595 {
596 key = TDEGlobal::locale()->dateFormat();
597 key += ' ';
598 key += TDEGlobal::locale()->timeFormat();
599 }
600 else if ( key == "localedatetimeshort" )
601 {
602 key = TDEGlobal::locale()->dateFormat();
603 key += ' ';
604 key += TDEGlobal::locale()->timeFormat();
605 }
606
607 kdDebug(30515) << "Locale date in TDELocale format: " << key << endl;
608
609 // TDELocale's key differ from KWord
610
611 // Date
612 key.replace( "%Y", "yyyy" ); // Year 4 digits
613 key.replace( "%y", "yy" ); // Year 2 digits
614 key.replace( "%n", "M" ); // Month 1 digit
615 key.replace( "%m", "MM" ); // Month 2 digits
616 key.replace( "%e", "d" ); // Day 1 digit
617 key.replace( "%d", "dd" ); // Day 2 digits
618 key.replace( "%b", "MMM" ); // Month 3 letters
619 key.replace( "%B", "MMMM" ); // Month all letters
620 key.replace( "%a", "ddd" ); // Day 3 letters
621 key.replace( "%A", "dddd" ); // Day all letters
622 // 12h
623 key.replace( "%p", "am/pm" ); // AM/PM (TDELocale knows it only lower case)
624 key.replace( "%I", "hh" ); // 12 hour 2 digits
625 key.replace( "%l", "h" ); // 12 hour 1 digits
626 // 24h
627 key.replace( "%H", "HH" ); // 24 hour 2 digits
628 key.replace( "%k", "H" ); // 24 hour 1 digit
629 // Other times
630 key.replace( "%M", "mm" ); // minute 2 digits (TDELocale knows it with 2 digits)
631 key.replace( "%S", "ss" ); // second 2 digits (TDELocale knows it with 2 digits)
632
633 kdDebug(30515) << "Locale date in RTF format: " << key << endl;
634 }
635 else if (!key.isEmpty())
636 {
637 const TQRegExp regexp("AP",false); // Not case-sensitive
638 if (regexp.search(key)!=-1)
639 {
640 // 12h
641 key.replace("ap","am/pm");
642 key.replace("AP","AM/PM");
643 }
644 else
645 {
646 //24h
647 key.replace('h','H'); // MS Word uses H for 24hour times
648 }
649 // MS Word do not know possesive months
650 key.replace("PPP","MMM");
651 key.replace("PPPP","MMMM");
652 key.replace("zzz","000"); // replace microseconds by 000
653 kdDebug(30515) << "New format: " << key << endl;
654 content += "\\@ \"";
655 content += key;
656 content += "\" ";
657 }
658 content += "\\* MERGEFORMAT }{\\fldrslt ";
659 content += escapeRtfText((*paraFormatDataIt).variable.m_text);
660 content += "}}";
661 }
662 else if (4==(*paraFormatDataIt).variable.m_type)
663 {
664 TQString strFieldType;
665 if ((*paraFormatDataIt).variable.isPageNumber())
666 {
667 content += "{\\field{\\*\\fldinst PAGE \\* MERGEFORMAT }{\\fldrslt ";
668 content += escapeRtfText((*paraFormatDataIt).variable.m_text);
669 content += "}}";
670 }
671 else if ((*paraFormatDataIt).variable.isPageCount())
672 {
673 content += "{\\field{\\*\\fldinst NUMPAGES \\* MERGEFORMAT }{\\fldrslt ";
674 content += escapeRtfText((*paraFormatDataIt).variable.m_text);
675 content += "}}";
676 }
677 else
678 {
679 // Unknown subtype, therefore write out the result
680 content += escapeRtfText((*paraFormatDataIt).variable.m_text);
681 }
682 }
683 else if (8==(*paraFormatDataIt).variable.m_type)
684 {
685 // Field
686 TQString name = escapeRtfText((*paraFormatDataIt).variable.getFieldName());
687 TQString value = escapeRtfText((*paraFormatDataIt).variable.getFieldValue());
688 TQString rtfField = mapFieldName(name);
689
690 if( rtfField.isEmpty() )
691 // Couldn't map field name, just write out the value
692 content += escapeRtfText((*paraFormatDataIt).variable.m_text);
693 else
694 {
695 content += "{\\field";
696 content += "{\\*\\fldinst ";
697 content += rtfField;
698 content += " \\* MERGEFORMAT }";
699 content += "{\\fldrslt {";
700 content += value;
701 content += "}}}";
702 }
703 }
704 else if (9==(*paraFormatDataIt).variable.m_type)
705 {
706 // A link
707 content += "{\\field";
708 content += "{\\*\\fldinst HYPERLINK ";
709 content += escapeRtfText((*paraFormatDataIt).variable.getHrefName());
710 content += "}";
711 content += "{\\fldrslt ";
712 content += "{\\ul"; // underline, ### TODO: use style Hyperlink
713 content += lookupColor("\\cf",TQColor(0,0,255));// blue
714 content += escapeRtfText((*paraFormatDataIt).variable.getLinkName());
715 content += "}}}";
716 }
717 else if (11==(*paraFormatDataIt).variable.m_type)
718 {
719 // Footnote
720 TQString value = (*paraFormatDataIt).variable.getFootnoteValue();
721 bool automatic = (*paraFormatDataIt).variable.getFootnoteAuto();
722 TQValueList<ParaData> *paraList = (*paraFormatDataIt).variable.getFootnotePara();
723 if( paraList )
724 {
725 TQString fstr;
726 TQValueList<ParaData>::ConstIterator it;
727 TQValueList<ParaData>::ConstIterator end(paraList->end());
728 const TQString prefixSaved = m_prefix;
729 m_prefix = TQString();
730 for (it=paraList->begin();it!=end;++it)
731 fstr += ProcessParagraphData( (*it).text, (*it).layout,(*it).formattingList);
732 m_prefix = prefixSaved;
733 content += "{\\super ";
734 content += automatic ? "\\chftn " : value;
735 content += "{\\footnote ";
736 content += "{\\super ";
737 content += automatic ? "\\chftn " : value;
738 content += fstr;
739 content += "}}}";
740 }
741 }
742 else
743 {
744 // Generic variable
745 content += escapeRtfText((*paraFormatDataIt).variable.m_text);
746 }
747 }
748 else if (6==(*paraFormatDataIt).id)
749 {
750 kdDebug(30515) << "Found an anchor of type: " << (*paraFormatDataIt).frameAnchor.type << endl;
751 // We have an image, a clipart or a table
752
753 if (6==(*paraFormatDataIt).frameAnchor.type)
754 {
755
756
757 if (!content.isEmpty())
758 {
759 str += m_prefix;
760 str += markup;
761 str += " {";
762 str += content;
763 str += "}";
764 str += m_eol;
765 content = TQString();
766 if (!m_inTable)
767 {
768 m_prefix = "\\par";
769 }
770 }
771 str += makeTable((*paraFormatDataIt).frameAnchor);
772 }
773 else if ((2==(*paraFormatDataIt).frameAnchor.type) || (5==(*paraFormatDataIt).frameAnchor.type))
774 {
775 content += makeImage((*paraFormatDataIt).frameAnchor);
776
777 }
778 }
779 }
780 }
781
782 if (layout.pageBreakAfter)
783 content += "\\page";
784
785 if (!content.isEmpty())
786 {
787 str += m_prefix;
788 str += markup;
789 str += " {";
790 str += content;
791 str += "}";
792 str += m_eol;
793 if (m_inTable==false)
794 {
795 m_prefix = "\\par";
796 }
797 }
798 if (str.isEmpty())
799 {
800 str ="\\par\\pard\\plain";
801 if (m_inTable==false)
802 {
803 m_prefix = "\\par";
804 }
805 }
806 return str;
807}
808
809bool RTFWorker::doFullParagraph(const TQString& paraText,
810 const LayoutData& layout, const ValueListFormatData& paraFormatDataList)
811{
812 kdDebug(30515) << "Entering RTFWorker::doFullParagraph" << endl << paraText << endl;
813 TQString par = ProcessParagraphData( paraText, layout, paraFormatDataList);
814 m_textBody += par;
815 kdDebug(30515) << "Quiting RTFWorker::doFullParagraph" << endl;
816 return true;
817}
818
819bool RTFWorker::doHeader(const HeaderData& header)
820{
821 TQString str;
822 TQString content;
823 if( header.page == HeaderData::PAGE_ODD )
824 str = "\\facingp{\\headerr";
825 else if( header.page == HeaderData::PAGE_EVEN )
826 str = "\\facingp{\\headerl";
827 else if( header.page == HeaderData::PAGE_FIRST )
828 str = "\\facingp{\\headerl";
829 else if( header.page == HeaderData::PAGE_ALL )
830 str = "{\\header";
831 else
832 return false;
833
834 str += " {";
835
836 TQValueList<ParaData>::ConstIterator it;
837 TQValueList<ParaData>::ConstIterator end(header.para.end());
838 for (it=header.para.begin();it!=end;++it)
839 content += ProcessParagraphData( (*it).text,(*it).layout,(*it).formattingList);
840
841 if (content!="\\par\\pard\\plain")
842 {
843 str += content;
844 str += "}";
845
846 str += "}";
847
848 m_textBody += str;
849 }
850 m_prefix=TQString();
851 return true;
852}
853
854bool RTFWorker::doFooter(const FooterData& footer)
855{
856 TQString str;
857 TQString content;
858 if( footer.page == FooterData::PAGE_ODD )
859 str = "\\facingp{\\footerr";
860 else if( footer.page == FooterData::PAGE_EVEN )
861 str = "\\facingp{\\footerl";
862 else if( footer.page == FooterData::PAGE_FIRST )
863 str = "\\facingp{\\headerl";
864 else if( footer.page == FooterData::PAGE_ALL )
865 str = "{\\footer";
866 else
867 return false;
868
869 str += " {";
870
871 TQValueList<ParaData>::ConstIterator it;
872 TQValueList<ParaData>::ConstIterator end(footer.para.end());
873 for (it=footer.para.begin();it!=end;++it)
874 content += ProcessParagraphData( (*it).text,(*it).layout,(*it).formattingList);
875
876 if (content!="\\par\\pard\\plain")
877 {
878 str += content;
879 str += "}";
880
881 str += "}";
882
883 m_textBody += str;
884 }
885 m_prefix=TQString();
886 return true;
887}
888
889bool RTFWorker::doOpenFile(const TQString& filenameOut, const TQString& /*to*/)
890{
891 m_ioDevice=new TQFile(filenameOut);
892
893 if (!m_ioDevice)
894 {
895 kdError(30515) << "No output file! Aborting!" << endl;
896 return false;
897 }
898
899 if ( !m_ioDevice->open (IO_WriteOnly) )
900 {
901 kdError(30515) << "Unable to open output file!" << endl;
902 return false;
903 }
904
905 m_streamOut=new TQTextStream(m_ioDevice);
906
907 // ### TODO: should "CP 1252" be used directly? (But RTFWorker::escapeRtfText is beased on ISO-8859-1 only.)
908 m_streamOut->setEncoding(TQTextStream::Latin1); // We are declaring the RTF document as CP 1252, so use ISO-8859-1
909
910 m_fileName=filenameOut;
911
912 return true;
913}
914
915bool RTFWorker::doCloseFile(void)
916{
917 kdDebug(30515) << __FILE__ << ":" << __LINE__ << endl;
918 delete m_streamOut;
919 m_streamOut=NULL;
920 if (m_ioDevice)
921 m_ioDevice->close();
922 return true;
923}
924
925bool RTFWorker::doOpenDocument(void)
926{
927 // Make the file header
928
929 // Note: we use \\ansicpg1252 because 1200 is not supposed to be supported
930 // Note2: we assume using \\ansicpg1252 in RTFWorker::escapeRtfText and in RTFWorker::doOpenFile
931 *m_streamOut << "{\\rtf1\\ansi\\ansicpg1252\\uc1\\deff0" << m_eol;
932
933 // Default color table
934 m_colorList
935 << TQColor(0,0,0) << TQColor(0,0,255) << TQColor(0,255,255)
936 << TQColor(0,255,0) << TQColor(255,0,255) << TQColor(255,0,0)
937 << TQColor(255,255,0) << TQColor(255,255,255) << TQColor(0,0,128)
938 << TQColor(0,128,128) << TQColor(0,128,0) << TQColor(128,0,128)
939 << TQColor(128,0,0) << TQColor(128,128,0) << TQColor(128,128,128);
940
941 return true;
942}
943
944void RTFWorker::writeFontData(void)
945{
946 kdDebug(30515) << "Fonts:" << m_fontList << endl;
947 *m_streamOut << "{\\fonttbl";
948 uint count;
949 TQFontDatabase fontDatabase;
950 TQStringList::ConstIterator it;
951 for (count=0, it=m_fontList.begin();
952 it!=m_fontList.end();
953 count++, it++)
954 {
955 const TQString strLower( (*it).lower() );
956 *m_streamOut << "{\\f" << count;
957 if ( (strLower.find("symbol")>-1) || (strLower.find("dingbat")>-1) )
958 *m_streamOut << "\\ftech";
959 else if ( (strLower.find("script")>-1) )
960 *m_streamOut << "\\fscript";
961
962#if 1
963 else
964 {
965 // We do not know the font type that we have
966 *m_streamOut << "\\fnil";
967 }
968#else
969 else
970 // TQFontInfo:styleHint() does not guess anything, it just returns what is in the TQFont. Nothing put in, nothing gets out.
971 {
972 TQFontInfo info(*it);
973 switch (info.styleHint())
974 {
975 case TQFont::SansSerif:
976 default:
977 {
978 *m_streamOut << "\\fswiss";
979 break;
980 }
981 case TQFont::Serif:
982 {
983 *m_streamOut << "\\froman";
984 break;
985 }
986 case TQFont::Courier:
987 {
988 *m_streamOut << "\\fmodern";
989 break;
990 }
991 case TQFont::OldEnglish:
992 {
993 *m_streamOut << "\\fdecor";
994 break;
995 }
996 }
997 }
998#endif
999 // ### TODO: \fcharset would be mandatory but TQt3 does not give us the font charset. :-(
1000 *m_streamOut << "\\fprq" << ( fontDatabase.isFixedPitch( *it ) ? 1 : 2 ) << " "; // font definition
1001 *m_streamOut << escapeRtfText( *it );
1002 *m_streamOut << ";}" << m_eol; // end font table entry
1003 }
1004 *m_streamOut << "}"; // end of font table
1005}
1006
1007void RTFWorker::writeColorData(void)
1008{
1009 *m_streamOut << "{\\colortbl;";
1010 uint count;
1011 TQValueList<TQColor>::ConstIterator it;
1012 for (count=0, it=m_colorList.begin();
1013 it!=m_colorList.end();
1014 count++, it++)
1015 {
1016 *m_streamOut << "\\red" << (*it).red();
1017 *m_streamOut << "\\green" << (*it).green();
1018 *m_streamOut << "\\blue" << (*it).blue();
1019 *m_streamOut << ";"; // end of entry
1020 }
1021 *m_streamOut << "}"; // end of color table
1022}
1023
1024void RTFWorker::writeStyleData(void)
1025{
1026 *m_streamOut << "{\\stylesheet" << m_eol;
1027
1028 uint count;
1029 TQValueList<LayoutData>::ConstIterator it;
1030 for (count=0, it=m_styleList.begin();
1031 it!=m_styleList.end();
1032 count++, it++)
1033 {
1034 *m_streamOut << "{";
1035 if (count>0) // \s0 is not written out
1036 *m_streamOut << "\\s" << count;
1037
1038 *m_streamOut << layoutToRtf((*it),(*it),true);
1039
1040 // \snext must be the last keyword before the style name
1041 // Find the number of the following style
1042 uint counter=0; // counts position in style table starting at 0
1043 TQValueList < LayoutData > ::ConstIterator it2;
1044 for( it2 = m_styleList.begin(); it2 != m_styleList.end(); counter++, ++it2 )
1045 {
1046 if ( (*it2).styleName == (*it).styleFollowing )
1047 {
1048 *m_streamOut << "\\snext" << counter;
1049 break;
1050 }
1051 }
1052
1053 *m_streamOut << " " << (*it).styleName << ";";
1054 *m_streamOut << "}";
1055 *m_streamOut << m_eol;
1056 }
1057
1058 *m_streamOut << "}";
1059}
1060
1061bool RTFWorker::doCloseDocument(void)
1062{
1063
1064 writeFontData();
1065 writeColorData();
1066 writeStyleData();
1067
1068 if (!m_textDocInfo.isEmpty())
1069 {
1070 *m_streamOut << "{\\info "; // string of document information markup
1071 *m_streamOut << m_textDocInfo; // add document author, title, operator
1072 *m_streamOut << "}";
1073 }
1074 *m_streamOut << "\\paperw" << int(m_paperWidth);
1075 *m_streamOut << "\\paperh" << int(m_paperHeight);
1076 if (1==m_paperOrientation)
1077 *m_streamOut << "\\landscape";
1078 *m_streamOut << "\\margl" << int(m_paperMarginLeft);
1079 *m_streamOut << "\\margr" << int(m_paperMarginRight);
1080 *m_streamOut << "\\margt" << int(m_paperMarginTop);
1081 *m_streamOut << "\\margb" << int(m_paperMarginBottom);
1082 *m_streamOut << m_textPage; // add page size, margins, etc.
1083 *m_streamOut << "\\widowctrl\\ftnbj\\aenddoc\\formshade \\fet0\\sectd\n";
1084 if (m_startPageNumber >= 1)
1085 *m_streamOut << "\\pgnstart" << m_startPageNumber << endl;
1086 //*m_streamOut << "\\linex0\\endnhere\\plain";
1087 *m_streamOut << "\\pard\\plain";
1088 *m_streamOut << m_textBody;
1089
1090 *m_streamOut << "}" << m_eol;
1091 return true;
1092}
1093
1094bool RTFWorker::doFullDocumentInfo(const KWEFDocumentInfo& docInfo)
1095{
1096
1097 if ( !docInfo.title.isEmpty() )
1098 {
1099 m_textDocInfo += "{\\title ";
1100 m_textDocInfo += escapeRtfText( docInfo.title );
1101 m_textDocInfo += "}";
1102 }
1103
1104 if ( !docInfo.fullName.isEmpty() )
1105 {
1106 m_textDocInfo += "{\\author ";
1107 m_textDocInfo += escapeRtfText( docInfo.fullName );
1108 m_textDocInfo += "}";
1109 }
1110
1111 if ( !docInfo.keywords.isEmpty() )
1112 {
1113 m_textDocInfo += "{\\keywords ";
1114 m_textDocInfo += escapeRtfText( docInfo.keywords );
1115 m_textDocInfo += "}";
1116 }
1117 if ( !docInfo.subject.isEmpty() )
1118 {
1119 m_textDocInfo += "{\\subject ";
1120 m_textDocInfo += escapeRtfText( docInfo.subject );
1121 m_textDocInfo += "}";
1122 }
1123
1124 if ( !docInfo.company.isEmpty() )
1125 {
1126 m_textDocInfo += "{\\company ";
1127 m_textDocInfo += escapeRtfText( docInfo.company );
1128 m_textDocInfo += "}";
1129 }
1130
1131 // Now add who we are in a \comment
1132 TQString revision("$Revision: 549975 $");
1133 m_textDocInfo += "{\\comment ";
1134 m_textDocInfo += "Generated by KWord's RTF Export Filter";
1135 m_textDocInfo += revision.mid(10).remove('$'); // has a leading and a trailing space.
1136 m_textDocInfo += "}";
1137
1138 if ( !docInfo.abstract.isEmpty() )
1139 {
1140 m_textDocInfo += "{\\doccomm ";
1141 m_textDocInfo += escapeRtfText( docInfo.abstract );
1142 m_textDocInfo += "}";
1143 }
1144
1145 return true;
1146}
1147
1148bool RTFWorker::doOpenTextFrameSet(void)
1149{
1150 return true;
1151}
1152
1153bool RTFWorker::doCloseTextFrameSet(void)
1154{
1155 return true;
1156}
1157
1158TQString RTFWorker::openSpan(const FormatData& formatOrigin, const FormatData& format)
1159{
1160 TQString result;
1161
1162 result += "{";
1163 result += textFormatToRtf(formatOrigin.text,format.text,false);
1164
1165 if ( 1==format.text.verticalAlignment )
1166 {
1167 result += "\\sub"; //Subscript
1168 }
1169 else if ( 2==format.text.verticalAlignment )
1170 {
1171 result += "\\super"; //Superscript
1172 }
1173
1174 result += " ";
1175 return result;
1176}
1177
1178TQString RTFWorker::closeSpan(const FormatData& , const FormatData& )
1179{
1180 TQString result;
1181 result += "}";
1182 return result;
1183}
1184
1185// The following function encodes the kword unicode characters into
1186// RTF seven bit ASCII. This affects any 8 bit characters.
1187// They are encoded either with \' or with \u
1188TQString RTFWorker::escapeRtfText ( const TQString& text ) const
1189{
1190 // initialize strings
1191 TQString escapedText;
1192 const uint length = text.length();
1193 for ( uint i = 0; i < length; i++ )
1194 {
1195 TQChar TQCh ( text.at( i ) ); // get out one unicode char from the string
1196 const ushort ch = TQCh.unicode(); // take unicode value of the char
1197
1198 if ( TQCh == '\\' ) escapedText += "\\\\"; // back-slash
1199 else if ( TQCh == '{' ) escapedText += "\\{";
1200 else if ( TQCh == '}' ) escapedText += "\\}";
1201 else if ( ch >= 32 && ch <= 127) // ASCII character
1202 escapedText += TQCh;
1203 else if ( ch == 0x0009 ) escapedText += "\\tab "; // tabulator
1204 else if ( ch == 0x00a0 ) escapedText += "\\~"; // Non-breaking space
1205 else if ( ch == 0x00ad ) escapedText += "\\-"; // Soft hyphen
1206 else if ( ch == 0x00b7 ) escapedText += "\\|";
1207 else if ( ch == 0x2011 ) escapedText += "\\_"; // Non-breaking hyphen
1208 else if ( ch == 0x2002 ) escapedText += "\\enspace ";
1209 else if ( ch == 0x2003 ) escapedText += "\\emspace ";
1210 else if ( ch == 0x2004 ) escapedText += "\\qmspace ";
1211 else if ( ch == 0x200c ) escapedText += "\\zwnj ";
1212 else if ( ch == 0x200d ) escapedText += "\\zwj ";
1213 else if ( ch == 0x200e ) escapedText += "\\ltrmark ";
1214 else if ( ch == 0x200f ) escapedText += "\\rtlmark ";
1215 else if ( ch == 0x2013 ) escapedText += "\\endash ";
1216 else if ( ch == 0x2014 ) escapedText += "\\emdash ";
1217 else if ( ch == 0x2018 ) escapedText += "\\lquote ";
1218 else if ( ch == 0x2019 ) escapedText += "\\rquote ";
1219 else if ( ch == 0x201c ) escapedText += "\\ldblquote ";
1220 else if ( ch == 0x201d ) escapedText += "\\rdblquote ";
1221 else if ( ch == 0x2022 ) escapedText += "\\bullet ";
1222 else if ( ch >= 160 && ch < 256) // check for characters common between ISO-8859-1 and CP1252
1223 { // NOTE: 128 to 159 in CP1252 are somewhere else in UTF-8 and do not exist in ISO-8859-1 (### TODO?)
1224 escapedText += "\\\'"; // escape upper page character to 7 bit
1225 escapedText += TQString::number ( ch, 16 );
1226 }
1227 else if ( ch >= 256) // check for a higher code non-ASCII character
1228 {
1229 // encode this as decimal unicode with a replacement character.
1230 escapedText += "\\u";
1231 escapedText += TQString::number ( ch, 10 );
1232 // We decompose the character. If it works, the first character is whitout any accent.
1233 // (Of course this only works with Latin letters.)
1234 // WARNING: TQChar::decomposition is not re-entrant in TQt 3.x
1235 TQChar replacement ( TQCh.decomposition().at(0) );
1236 kdDebug(30515) << "Proposed replacement character: " << TQString(replacement) << endl;
1237
1238 if (replacement.isNull() || (int)replacement<=' ' || (int)replacement>=char(127)
1239 || replacement=='{' || replacement=='}' || replacement=='\\')
1240 replacement='?'; // Not a normal ASCII character, so default to show a ? sign
1241
1242 escapedText += replacement; // The \uc1 dummy character.
1243
1244 }
1245 else
1246 escapedText += TQCh ;
1247
1248 }
1249
1250 return escapedText;
1251
1252}
1253
1254bool RTFWorker::doFullPaperFormat(const int /*format*/,
1255 const double width, const double height, const int orientation)
1256{
1257 m_paperWidth=width*20;
1258 m_paperHeight=height*20;
1259 m_paperOrientation=orientation;
1260 return true;
1261}
1262
1263bool RTFWorker::doFullPaperBorders (const double top, const double left,
1264 const double bottom, const double right)
1265{
1266 m_paperMarginTop=top*20;
1267 m_paperMarginLeft=left*20;
1268 m_paperMarginBottom=bottom*20;
1269 m_paperMarginRight=right*20;
1270 return true;
1271}
1272
1273bool RTFWorker::doFullDefineStyle(LayoutData& layout)
1274{
1275 //Register the new style in the style list
1276 m_styleList << layout;
1277
1278 // Now we must register a few things (with help of the lookup methods.)
1279 lookupFont("\\f", layout.formatData.text.fontName);
1280 lookupColor(TQString(), layout.formatData.text.fgColor);
1281 lookupColor(TQString(), layout.formatData.text.bgColor);
1282
1283 return true;
1284}
1285
1286static TQString writeDate(const TQString keyword, const TQDateTime& now)
1287{
1288 TQString str;
1289 if (now.isValid())
1290 {
1291 kdDebug(30515) << "Date " << keyword << " " << now.toString() << endl;
1292 str += '{';
1293 str += keyword;
1294 const TQDate nowDate(now.date());
1295 str += "\\yr";
1296 str += TQString::number(nowDate.year());
1297 str += "\\mo";
1298 str += TQString::number(nowDate.month());
1299 str += "\\dy";
1300 str += TQString::number(nowDate.day());
1301 const TQTime nowTime(now.time());
1302 str += "\\hr";
1303 str += TQString::number(nowTime.hour());
1304 str += "\\min";
1305 str += TQString::number(nowTime.minute());
1306 str += "\\sec";
1307 str += TQString::number(nowTime.second());
1308 str += '}';
1309 }
1310 else
1311 kdWarning(30515) << "Date " << keyword << " is not valid! Skipping!" << endl;
1312
1313 return str;
1314}
1315
1316bool RTFWorker::doVariableSettings(const VariableSettingsData& vs)
1317{
1318 m_textDocInfo += writeDate("\\creatim",vs.creationTime);
1319 m_textDocInfo += writeDate("\\revtim", vs.modificationTime);
1320 m_textDocInfo += writeDate("\\printim",vs.printTime);
1321 m_startPageNumber = vs.startingPageNumber;
1322
1323 return true;
1324}
1325
1326TQString RTFWorker::textFormatToRtf(const TextFormatting& formatOrigin,
1327 const TextFormatting& formatData, const bool force)
1328{
1329 // TODO: rename variable formatData
1330 TQString strElement; // TODO: rename this variable
1331
1332 // Font name
1333 const TQString fontName(formatData.fontName);
1334 if (!fontName.isEmpty()
1335 && (force || (formatOrigin.fontName!=formatData.fontName)))
1336 {
1337 strElement+=lookupFont("\\f", fontName);
1338 }
1339
1340 if (force || (formatOrigin.fontSize!=formatData.fontSize))
1341 {
1342 const int size=formatData.fontSize;
1343 if (size>0)
1344 {
1345 strElement+="\\fs";
1346 strElement+=TQString::number(2*size,10);
1347 }
1348 }
1349
1350 if (force || (formatOrigin.italic!=formatData.italic))
1351 {
1352 // Font style
1353 if ( formatData.italic )
1354 {
1355 strElement+="\\i";
1356 }
1357 else
1358 {
1359 strElement+="\\i0";
1360 }
1361 }
1362
1363 if (force || ((formatOrigin.weight>=75)!=(formatData.weight>=75)))
1364 {
1365 if ( formatData.weight >= 75 )
1366 {
1367 strElement+="\\b";
1368 }
1369 else
1370 {
1371 strElement+="\\b0";
1372 }
1373 }
1374
1375 if (force || (formatOrigin.fgColor!=formatData.fgColor))
1376 {
1377 if ( formatData.fgColor.isValid() )
1378 {
1379 strElement+=lookupColor("\\cf", formatData.fgColor);
1380 }
1381 }
1382
1383 if (force || (formatOrigin.bgColor!=formatData.bgColor))
1384 {
1385 if ( formatData.bgColor.isValid() )
1386 {
1387 strElement+=lookupColor("\\cb", formatData.bgColor);
1388 strElement+=lookupColor("\\highlight", formatData.bgColor); // MS Word wants this
1389 // ### FIXME: \highlight is not allowed in style definitions (RTF 1.6)
1390 }
1391 }
1392
1393 if ( force
1394 || ( formatOrigin.underline != formatData.underline )
1395 || ( formatOrigin.underlineValue != formatData.underlineValue )
1396 || ( formatOrigin.underlineStyle != formatData.underlineStyle )
1397 || ( formatOrigin.underlineWord != formatData.underlineWord ) )
1398 {
1399 if ( formatData.underline )
1400 {
1401 TQString underlineValue = formatData.underlineValue;
1402 TQString underlineStyle = formatData.underlineStyle;
1403 bool underlineWord = formatData.underlineWord;
1404 TQString ul ( "\\ul" ); // fall-back: simple underline
1405
1406 if( underlineStyle.isEmpty() ) underlineStyle = "solid";
1407 if( underlineValue == "1" ) underlineValue = "single";
1408
1409 if( underlineValue == "double" )
1410 ul = "\\uldb";
1411 else if( underlineValue == "single-bold" )
1412 ul = "\\ulth";
1413 else if( underlineValue == "wave" )
1414 ul = "\\ulwave";
1415 else if( underlineValue == "single" )
1416 {
1417 if( underlineStyle == "dash" )
1418 ul = "\\uldash";
1419 else if( underlineStyle == "dot" )
1420 ul = "\\uld";
1421 else if( underlineStyle == "dashdot" )
1422 ul = "\\uldashd";
1423 else if( underlineStyle == "dashdotdot" )
1424 ul = "\\uldashdd";
1425 else if( underlineWord )
1426 ul = "\\ulw";
1427 };
1428
1429 strElement+= ul;
1430 if (formatData.underlineColor.isValid())
1431 {
1432 strElement+=lookupColor("\\ulc",formatData.underlineColor);
1433 }
1434 }
1435 else
1436 {
1437 strElement+="\\ul0";
1438 }
1439 }
1440
1441 if ( force
1442 || ( formatOrigin.strikeout != formatData.strikeout )
1443 || ( formatOrigin.strikeoutType != formatData.strikeoutType ) )
1444 {
1445 if ( formatData.strikeout )
1446 {
1447 if( formatData.strikeoutType == "double" )
1448 strElement+="\\striked1"; // 1 needed! (The exception that confirms the rule!)
1449 else
1450 strElement+="\\strike";
1451 }
1452 else
1453 {
1454 strElement+="\\strike0"; // ### TODO: \striked0 too?
1455 }
1456 }
1457
1458 return strElement;
1459}
1460
1461TQString RTFWorker::layoutToRtf(const LayoutData& layoutOrigin,
1462 const LayoutData& layout, const bool force)
1463{
1464 TQString strLayout;
1465
1466
1467 if (force || (layoutOrigin.alignment!=layout.alignment))
1468 {
1469 if (layout.alignment=="left")
1470 strLayout += "\\ql";
1471 else if (layout.alignment== "right")
1472 strLayout += "\\qr";
1473 else if (layout.alignment=="center")
1474 strLayout += "\\qc";
1475 else if (layout.alignment=="justify")
1476 strLayout += "\\qj";
1477 else if ( layout.alignment=="auto")
1478 {
1479 // ### TODO: what for BIDI?
1480 //strLayout += "\\ql";
1481 }
1482 else
1483 {
1484 kdWarning(30515) << "Unknown alignment: " << layout.alignment << endl;
1485 }
1486 }
1487
1488 if ((layout.indentLeft>=0.0)
1489 && (force || (layoutOrigin.indentLeft!=layout.indentLeft)))
1490 {
1491 strLayout += "\\li";
1492 strLayout += TQString::number(int(layout.indentLeft)*20, 10);
1493 }
1494
1495 if ((layout.indentRight>=0.0)
1496 && (force || (layoutOrigin.indentRight!=layout.indentRight)))
1497 {
1498 strLayout += "\\ri";
1499 strLayout += TQString::number(int(layout.indentRight)*20, 10);
1500 }
1501
1502 if (force || (layoutOrigin.indentFirst!=layout.indentFirst))
1503 {
1504 strLayout += "\\fi";
1505 strLayout += TQString::number(int(layout.indentFirst)*20, 10);
1506 }
1507
1508 if ((layout.marginBottom>=0.0)
1509 && (force || (layoutOrigin.marginBottom!=layout.marginBottom)))
1510 {
1511 strLayout += "\\sa";
1512 strLayout += TQString::number(int(layout.marginBottom)*20 ,10);
1513 }
1514
1515 if ((layout.marginTop>=0.0)
1516 && (force || (layoutOrigin.marginTop!=layout.marginTop)))
1517 {
1518 strLayout += "\\sb";
1519 strLayout += TQString::number(int(layout.marginTop)*20, 10);
1520 }
1521
1522 if (force || (layoutOrigin.keepLinesTogether!=layout.keepLinesTogether))
1523 {
1524 if(layout.keepLinesTogether) strLayout += "\\keep";
1525 }
1526
1527 // Note: there seems to be too many problems of using a page break in a layout
1528 // - KWord's RTF import filter makes the page break immediately (also in styles)
1529 // - AbiWord's RTF import does not like \*\pgbrk
1530 // ### TODO: decide if we really remove this code
1531#if 0
1532 if (force || (layoutOrigin.pageBreakBefore!=layout.pageBreakBefore))
1533 {
1534 if(layout.pageBreakBefore) strLayout += "\\pagebb";
1535 }
1536
1537 // Note: RTF doesn't specify "page break after"
1538 // \*\pgbrk0 is used after OpenOffice.org Writer
1539 if (force || (layoutOrigin.pageBreakAfter!=layout.pageBreakAfter))
1540 {
1541 if(layout.pageBreakAfter) strLayout += "\\*\\pgbrk0";
1542 }
1543#endif
1544
1545 if (force
1546 || ( layoutOrigin.lineSpacingType != layout.lineSpacingType )
1547 || ( layoutOrigin.lineSpacing != layout.lineSpacing ) )
1548 {
1549 if ( layout.lineSpacingType==LayoutData::LS_SINGLE )
1550 ;// do nothing, single linespace is default in RTF
1551
1552 else if ( layout.lineSpacingType==LayoutData::LS_ONEANDHALF )
1553 strLayout += "\\sl360\\slmult1"; // one-and-half linespace
1554
1555 else if ( layout.lineSpacingType==LayoutData::LS_DOUBLE )
1556 strLayout += "\\sl480\\slmult1"; // double linespace
1557
1558 else if ( layout.lineSpacingType==LayoutData::LS_ATLEAST )
1559 strLayout += TQString("\\sl%1\\slmult0").arg(int(layout.lineSpacing)*20);
1560
1561 else if ( layout.lineSpacingType==LayoutData::LS_MULTIPLE )
1562 strLayout += TQString("\\sl%1\\slmult1").arg( int(layout.lineSpacing)*240 );
1563
1564 else if ( layout.lineSpacingType==LayoutData::LS_CUSTOM )
1565 // "Custom" in KWord is like "Exactly" in MS Word
1566 strLayout += TQString("\\sl-%1\\slmult0").arg(int(layout.lineSpacing)*20);
1567
1568 else
1569 kdWarning(30515) << "Unsupported lineSpacingType: " << layout.lineSpacingType << " (Ignoring!)" << endl;
1570 }
1571
1572 if (!layout.tabulatorList.isEmpty()
1573 && (force || (layoutOrigin.tabulatorList!=layout.tabulatorList) ))
1574 {
1575 TabulatorList::ConstIterator it;
1576 for (it=layout.tabulatorList.begin();it!=layout.tabulatorList.end();++it)
1577 {
1578 switch ((*it).m_type)
1579 {
1580 case 0: default: break; // left tab is default
1581 case 1: strLayout += "\\tqc"; break;
1582 case 2: strLayout += "\\tqr"; break;
1583 case 3: strLayout += "\\tqdec"; break;
1584 }
1585
1586 switch ((*it).m_filling)
1587 {
1588 case TabulatorData::TF_NONE: default: break; // without leader/filling
1589 case TabulatorData::TF_DOT: strLayout += "\\tldot"; break;
1590 case TabulatorData::TF_LINE: strLayout += "\\tlul"; break;
1591
1592 // these belows are all treated as RTF's \tqul
1593 case TabulatorData::TF_DASH:
1594 case TabulatorData::TF_DASHDOT:
1595 case TabulatorData::TF_DASHDOTDOT:
1596 strLayout += "\\tlul"; break;
1597 }
1598
1599 // must be the last
1600 strLayout += "\\tx";
1601 strLayout += TQString::number(int((*it).m_ptpos)*20, 10);
1602
1603 }
1604 }
1605
1606 // shadow support
1607 // note shadow in KWord is more full-feature/sophisticated than RTF
1608 // here we just treat KWord's shadow as simple \shad mark-up
1609 if( layout.shadowDistance > 0 )
1610 {
1611 strLayout += "\\shad";
1612 }
1613
1614 // TODO: borders
1615
1616 // This must remain last, as it adds a terminating space.
1617 strLayout+=textFormatToRtf(layoutOrigin.formatData.text,
1618 layout.formatData.text,force);
1619
1620 return strLayout;
1621}
1622
1623
1624TQString RTFWorker::lookupFont(const TQString& markup, const TQString& fontName)
1625{
1626 if (fontName.isEmpty())
1627 return TQString();
1628
1629 // First we have to remove TQt-typical foundry names, as some RTF readers are confused by them.
1630 TQString cookedFontName(fontName);
1631 TQRegExp regexp("\\s*\\[\\S*\\]"); // Some white space, opening square bracket, some non-white-space, ending square bracket
1632 cookedFontName.remove(regexp);
1633 // But we cannot have an empty name font
1634 if (cookedFontName.isEmpty())
1635 cookedFontName=fontName;
1636
1637 kdDebug(30515) << "RTFWorker::lookupFont " << fontName << " cooked: " << cookedFontName << endl;
1638
1639 uint counter=0; // counts position in font table (starts at 0)
1640 TQString strFont(markup); // markup for font selection
1641 TQStringList::ConstIterator it;
1642
1643 // search font table for this font
1644 for( it = m_fontList.begin(); it != m_fontList.end(); counter++, ++it )
1645 {
1646 if((*it) == cookedFontName) // check for match
1647 {
1648 strFont += TQString::number(counter);
1649 kdDebug(30515) << strFont << endl;
1650 return strFont;
1651 }
1652 } // end for()
1653
1654 kdDebug(30515) << "New font: " << cookedFontName << " count: " << counter << endl;
1655 m_fontList << cookedFontName;
1656
1657 strFont += TQString::number(counter);
1658 return strFont;
1659}
1660
1661TQString RTFWorker::lookupColor(const TQString& markup, const TQColor& color)
1662{
1663 if (!color.isValid())
1664 return TQString();
1665
1666 uint counter=1; // counts position in color table starting at 1
1667 TQString strColor(markup); // Holds RTF markup for the color
1668
1669 TQValueList < TQColor > ::ConstIterator it;
1670
1671 // search color table for this color
1672 for( it = m_colorList.begin(); it != m_colorList.end(); counter++, ++it )
1673 {
1674 if ( (*it) == color )
1675 {
1676 strColor += TQString::number(counter);
1677 return strColor;
1678 }
1679 }
1680
1681 kdDebug(30515) << "New color: " << TQString(color.name()) << " count: " << counter << endl;
1682 m_colorList << color;
1683
1684 strColor += TQString::number(counter);
1685 return strColor;
1686}
1687
1688TQString RTFWorker::lookupStyle(const TQString& styleName, LayoutData& returnLayout)
1689{
1690 if (styleName.isEmpty())
1691 return TQString();
1692
1693 uint counter=0; // counts position in style table starting at 0
1694 TQString strMarkup("\\s"); // Holds RTF markup for the style
1695
1696 TQValueList < LayoutData > ::ConstIterator it;
1697 TQValueList < LayoutData > ::ConstIterator end(m_styleList.end());
1698
1699 // search color table for this color
1700 for( it = m_styleList.begin(); it != end; counter++, ++it )
1701 {
1702 if ( (*it).styleName == styleName )
1703 {
1704 strMarkup += TQString::number(counter);
1705 returnLayout=(*it);
1706 return strMarkup;
1707 }
1708 }
1709
1710 kdDebug(30515) << "New style: " << styleName << " count: " << counter << endl;
1711 LayoutData layout;
1712 m_styleList << layout;
1713 returnLayout=layout;
1714
1715 strMarkup += TQString::number(counter);
1716 return strMarkup;
1717}
TQString text
text at the time of saving (no formatting info though)
This is basically <FRAMESET> tag.
For <FRAME> Tag.
Paragraph layout.
double lineSpacing
Space between lines in pt, if lineSpacingType==0.
TabulatorList tabulatorList
List of tabulators.
double indentFirst
indentation correction of first line (can be negative!)
double indentRight
right indentation (a negative value means invalid)
double shadowDistance
distance of <SHADOW>
double indentLeft
left indentation (a negative value means invalid)
TQString alignment
left, right, center, justify
int lineSpacingType
0=custom, 10=one line, 15=one and half lines, 20= 2 lines
double marginTop
space before the paragraph (a negative value means invalid)
double marginBottom
space after the paragraph (a negative value means invalid)
TextFormatting is a container for data retrieved from the <FORMAT> tag and its subtags to be used in ...
bool underlineWord
true if word-by-word (i.e spaces are not underlined)
bool underline
Any underline? (If true, we do not know if simple or double or what else)
TQColor underlineColor
color of the line
bool strikeout
true if strikeout
TQString underlineStyle
underline style: solid, dash, dot dash, ....
TQString underlineValue
"value" of the underline: single, double, bold-single...
TQString strikeoutType
type of strikeout: single, bold, double
see <VARIABLESETTINGS> in the KWord DTD