filters

ExportFilter.cc

00001 /*
00002    This file is part of the KDE project
00003    Copuright (C) 2001 Michael Johnson <mikej@xnet.com>
00004    Copyright (C) 2001, 2002, 2003, 2004 Nicolas GOUTTE <goutte@kde.org>
00005    Copyright (C) 2002 Ariya Hidayat <ariya@kde.org>
00006 
00007    This library is free software; you can redistribute it and/or
00008    modify it under the terms of the GNU Library General Public
00009    License as published by the Free Software Foundation; either
00010    version 2 of the License, or (at your option) any later version.
00011 
00012    This library is distributed in the hope that it will be useful,
00013    but WITHOUT ANY WARRANTY; without even the implied warranty of
00014    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00015    Library General Public License for more details.
00016 
00017    You should have received a copy of the GNU Library General Public License
00018    along with this library; see the file COPYING.LIB.  If not, write to
00019    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00020  * Boston, MA 02110-1301, USA.
00021 */
00022 
00023 #include <qstring.h>
00024 #include <qtextcodec.h>
00025 #include <qfile.h>
00026 #include <qfileinfo.h>
00027 #include <qfontinfo.h>
00028 #include <qfontdatabase.h>
00029 #include <qpicture.h>
00030 #include <qimage.h>
00031 #include <qregexp.h>
00032 #include <qcolor.h>
00033 #include <qdatetime.h>
00034 
00035 #include <klocale.h>
00036 #include <kglobal.h>
00037 #include <kdebug.h>
00038 
00039 #include <KWEFUtil.h>
00040 #include <KWEFBaseWorker.h>
00041 
00042 #include "ExportFilter.h"
00043 
00044 // 1 twip = 1/20 pt = 1/1400 inch
00045 // 1 inch = 25.4 mm
00046 
00047 // some conversion macros
00048 #define TWIP_TO_MM(x) (x)*25.4/1440.0
00049 #define MM_TO_TWIP(x) (x)*1440.0/25.4
00050 #define PT_TO_TWIP(x) (x)*20
00051 #define TWIP_TO_PT(x) (x)/20
00052 
00053 // map KWord field name to RTF field name
00054 // e.g authorName -> AUTHOR
00055 static QString mapFieldName( const QString& kwordField )
00056 {
00057   QString rtfField;
00058 
00059   if( kwordField == "fileName" ) rtfField = "FILENAME";
00060   else if( kwordField == "authorName" ) rtfField = "AUTHOR";
00061   else if( kwordField == "docTitle" ) rtfField = "TITLE";
00062 
00063   return rtfField;
00064 }
00065 
00066 RTFWorker::RTFWorker():
00067     m_ioDevice(NULL), m_streamOut(NULL), m_eol("\r\n"), m_inTable(false),
00068     m_paperOrientation(false), m_paperWidth(20), m_paperHeight(20),
00069     m_paperMarginTop(72), m_paperMarginLeft(72),
00070     m_paperMarginBottom(72), m_paperMarginRight(72), m_startPageNumber(1)
00071 {
00072 }
00073 
00074 static QString WritePositiveKeyword(const QString& keyword, const int value)
00075 {
00076     QString str;
00077     str += keyword;
00078 
00079     if (value>0) // The value of the keyword cannot be negative
00080         str += QString::number( value );
00081     else
00082         str += '0';
00083 
00084     return str;
00085 }
00086 
00087 QString RTFWorker::writeRow(const QString& textCellHeader, const QString& rowText, const FrameData& frame)
00088 {
00089     QString row;
00090 
00091     row += "\\trowd\\trgaph60\\trql";  // start new row
00092     row += WritePositiveKeyword("\\trrh", qRound(PT_TO_TWIP(frame.minHeight)));
00093     row += WritePositiveKeyword("\\trleft", qRound(PT_TO_TWIP(frame.left) - m_paperMarginLeft));
00094     //row += "\\trautofit0"; // ### VERIFY
00095     row += textCellHeader;
00096     row += " "; // End of keyword
00097     row += rowText;
00098 
00099     return row;
00100 }
00101 
00102 QString RTFWorker::writeBorder(const char whichBorder, const int borderWidth, const QColor& color)
00103 {
00104     QString str;
00105     if (borderWidth > 0)
00106     {
00107         str += "\\clbrdr"; // Define border
00108         str += whichBorder; // t=top, l=left, b=bottom, r=right
00109         str += "\\brdrs\\brdrw"; // Single border; thickness
00110         str += QString::number(borderWidth);
00111         if (color.isValid())
00112         {
00113             str += lookupColor("\\brdrcf",color);
00114         }
00115     }
00116     return str;
00117 }
00118 
00119 QString RTFWorker::makeTable(const FrameAnchor& anchor)
00120 {
00121     QString textBody; // Text to be returned
00122     textBody += m_prefix;
00123     m_prefix = QString::null;
00124     QString rowText;
00125 
00126     int rowCurrent = 0;
00127     bool firstCellInRow = true;
00128     FrameData firstFrameData;
00129     int debugCellCurrent = 0; //DEBUG
00130     int debugRowCurrent = 0; //DEBUG
00131     QString textCellHeader; // <celldef>
00132 
00133     const bool oldInTable = m_inTable;
00134     m_inTable = true;
00135 
00136     QValueList<TableCell>::ConstIterator itCell;
00137     for (itCell=anchor.table.cellList.begin();
00138         itCell!=anchor.table.cellList.end(); itCell++)
00139     {
00140         // ### TODO: rowspan, colspan
00141         if (rowCurrent!=(*itCell).row)
00142         {
00143             rowCurrent = (*itCell).row;
00144             textBody += writeRow( textCellHeader, rowText, firstFrameData);
00145             textBody += "\\row";
00146             textBody += m_eol;
00147             rowText = QString::null;
00148             textCellHeader = QString::null;
00149             firstCellInRow=true;
00150             debugRowCurrent ++; // DEBUG
00151             debugCellCurrent = 0; //DEBUG
00152         }
00153 
00154         const FrameData& frame = (*itCell).frame;
00155 
00156         if (firstCellInRow) // Not yet set, so set the position of the left border.
00157         {
00158             firstFrameData=frame;
00159             firstCellInRow=false;
00160         }
00161 
00162         kdDebug(30515) << "Cell: " << debugRowCurrent << "," << debugCellCurrent
00163             << " left: " << frame.left << " right: " << frame.right << " top: " << frame.top << " bottom " << frame.bottom << endl;
00164         textCellHeader += writeBorder('t',qRound(PT_TO_TWIP(frame.tWidth)),frame.tColor);
00165         textCellHeader += writeBorder('l',qRound(PT_TO_TWIP(frame.lWidth)),frame.lColor);
00166         textCellHeader += writeBorder('b',qRound(PT_TO_TWIP(frame.bWidth)),frame.bColor);
00167         textCellHeader += writeBorder('r',qRound(PT_TO_TWIP(frame.rWidth)),frame.rColor);
00168         textCellHeader += WritePositiveKeyword("\\cellx",qRound(PT_TO_TWIP(frame.right) - m_paperMarginRight)); //right border of cell
00169 
00170         QString endOfParagraph;
00171         QValueList<ParaData> *paraList = (*itCell).paraList;
00172         QValueList<ParaData>::ConstIterator it;
00173         QValueList<ParaData>::ConstIterator end(paraList->end());
00174         for (it=paraList->begin();it!=end;++it)
00175         {
00176             rowText += endOfParagraph;
00177             rowText += ProcessParagraphData( (*it).text,(*it).layout,(*it).formattingList);
00178             rowText += m_eol;
00179             endOfParagraph = "\\par"; // The problem is that the last paragraph ends with \cell not with \par
00180         }
00181         rowText += "\\cell";
00182         debugCellCurrent ++; // DEBUG
00183     }
00184 
00185     textBody += writeRow( textCellHeader, rowText, firstFrameData);
00186     textBody += "\\row\\pard";  // delimit last row
00187     textBody += m_eol;
00188     m_inTable = oldInTable;
00189 
00190     return textBody;
00191 }
00192 
00193 QString RTFWorker::makeImage(const FrameAnchor& anchor)
00194 {
00195     QString textBody; // Text to be returned
00196 
00197     QString strImageName(anchor.picture.koStoreName);
00198     QString strExt;
00199     QByteArray image;
00200 
00201     kdDebug(30515) << "RTFWorker::makeImage" << endl << anchor.picture.koStoreName << endl;
00202 
00203     const int pos=strImageName.findRev('.');
00204     if(pos!=-1) strExt = strImageName.mid(pos+1).lower();
00205 
00206     QString strTag;
00207     if (strExt=="png")
00208         strTag="\\pngblip";
00209 #if 0
00210     else if (strExt=="bmp")
00211         strTag="\\dibitmap";
00212 #endif
00213     else if ( (strExt=="jpeg") || (strExt=="jpg") )
00214         strTag="\\jpegblip";
00215     else if (strExt=="wmf")
00216         strTag="\\wmetafile8"; // 8 == anisotropic
00217     else
00218     {
00219         // either without extension or format is unknown
00220         // let's try to convert it to PNG format
00221         kdDebug(30515) << "Converting image " << anchor.picture.koStoreName << endl;
00222 
00223         strTag="\\pngblip";
00224         if( !loadAndConvertToImage(anchor.picture.koStoreName,strExt,"PNG",image) )
00225         {
00226             kdWarning(30515) << "Unable to convert " << anchor.picture.koStoreName << endl;
00227             return QString::null;
00228         }
00229     }
00230     // ### TODO: SVG, QPicture
00231 
00232     // load the image, this isn't necessary for converted image
00233     if( !image.size() )
00234         if (!loadSubFile(anchor.picture.koStoreName,image))
00235         {
00236             kdWarning(30515) << "Unable to load picture " << anchor.picture.koStoreName << endl;
00237             return QString::null;
00238         }
00239 
00240 
00241     // find displayed width and height (in twips)
00242     const long width  = (long)(PT_TO_TWIP(anchor.frame.right  - anchor.frame.left));
00243     const long height = (long)(PT_TO_TWIP(anchor.frame.bottom - anchor.frame.top));
00244 
00245     // find original image width and height (in twips)
00246     long origWidth  = width;
00247     long origHeight = height;
00248     if( strExt == "wmf" )
00249     {
00250         // special treatment for WMF with metaheader
00251         // d7cdc69a is metaheader magic id
00252         Q_UINT8* data = (Q_UINT8*) image.data();
00253         if( ( data[0] == 0xd7 ) && ( data[1] == 0xcd ) &&
00254             ( data[2] == 0xc6 ) && ( data[3] == 0x9a ) &&
00255             ( image.size() > 22 ) )
00256         {
00257             // grab bounding box, find original size
00258             unsigned left = data[6]+(data[7]<<8);
00259             unsigned top = data[8]+(data[9]<<8);
00260             unsigned right = data[10]+(data[11]<<8);
00261             unsigned bottom = data[12]+(data[13]<<8);
00262             origWidth = (long) (MM_TO_TWIP(right-left)/100);
00263             origHeight = (long) (MM_TO_TWIP(bottom-top)/100);
00264 
00265             // throw away WMF metaheader (22 bytes)
00266             for( uint i=0; i<image.size()-22; i++)
00267                 image.at(i) = image.at(i+22); // As we use uint, we need at()  ( [] uses int only .)
00268             image.resize( image.size()-22 );
00269         }
00270     }
00271     else
00272     {
00273         // It must be an image
00274         QImage img( image );
00275         if( img.isNull() )
00276         {
00277             kdWarning(30515) << "Unable to load picture as image " << anchor.picture.koStoreName << endl;
00278             return QString::null;
00279         }
00280         // check resolution, assume 2835 dpm (72 dpi) if not available
00281         int resx = img.dotsPerMeterX();
00282         int resy = img.dotsPerMeterY();
00283         if( resx <= 0 ) resx = 2835;
00284         if( resy <= 0 ) resy = 2835;
00285 
00286         origWidth =  long(img.width() * 2834.65 * 20 / resx);
00287         origHeight = long(img.height() * 2834.65 * 20 / resy);
00288     }
00289 
00290     // Now that we are sure to have a valid image, we can write the RTF tags
00291     textBody += "{\\pict";
00292     textBody += strTag;
00293 
00294     // calculate scaling factor (in percentage)
00295     int scaleX = width * 100 / origWidth;
00296     int scaleY = height * 100 / origHeight;
00297 
00298     // size in 1/100 mm
00299     int picw = (int)(100 * TWIP_TO_MM(origWidth));
00300     int pich = (int)(100 * TWIP_TO_MM(origHeight));
00301 
00302     textBody += "\\picscalex";
00303     textBody += QString::number(scaleX, 10);
00304     textBody += "\\picscaley";
00305     textBody += QString::number(scaleY, 10);
00306     textBody += "\\picw";
00307     textBody += QString::number(picw,10);
00308     textBody += "\\pich";
00309     textBody += QString::number(pich,10);
00310     textBody += "\\picwgoal";
00311     textBody += QString::number(origWidth, 10);
00312     textBody += "\\pichgoal";
00313     textBody += QString::number(origHeight, 10);
00314 
00315     textBody+=" ";
00316     const char hex[] = "0123456789abcdef";
00317     for (uint i=0; i<image.size(); i++)
00318     {
00319         if (!(i%40))
00320             textBody += m_eol;
00321         const char ch=image.at(i);
00322         textBody += hex[(ch>>4)&0x0f]; // Done this way to avoid signed/unsigned problems
00323         textBody += hex[(ch&0x0f)];
00324     }
00325 
00326 
00327     textBody+="}";
00328 
00329     return textBody;
00330 }
00331 
00332 QString RTFWorker::formatTextParagraph(const QString& strText,
00333     const FormatData& formatOrigin, const FormatData& format)
00334 {
00335     QString str;
00336 
00337     if (!format.text.missing)
00338     {
00339         // Opening elements
00340         str+=openSpan(formatOrigin,format);
00341     }
00342 
00343     QString strEscaped = escapeRtfText(strText);
00344 
00345     // Replace line feeds by forced line breaks
00346     int pos;
00347     QString strBr("\\line ");
00348     while ((pos=strEscaped.find(QChar(10)))>-1)
00349     {
00350         strEscaped.replace(pos,1,strBr);
00351     }
00352 
00353     str+=strEscaped;
00354 
00355     if (!format.text.missing)
00356     {
00357         // Closing elements
00358         str+=closeSpan(formatOrigin,format);
00359     }
00360 
00361     return str;
00362 }
00363 
00364 QString RTFWorker::ProcessParagraphData ( const QString &paraText,
00365     const LayoutData& layout, const ValueListFormatData &paraFormatDataList)
00366 {
00367     QString str;
00368     QString content;
00369     QString markup;
00370     // open paragraph
00371     markup += "\\pard";
00372     markup += "\\plain";
00373     if (m_inTable)
00374         markup += "\\intbl";
00375 
00376 //lists
00377     if (layout.counter.style)
00378         {
00379         markup += "{\\pntext\\pard\\plain";
00380         if( layout.formatData.text.fontSize >= 0)
00381         {
00382                 markup += "\\fs";
00383                 markup += QString::number((2 * layout.formatData.text.fontSize));
00384                 markup += lookupFont("\\f",layout.formatData.text.fontName);
00385         }
00386         markup += " ";
00387         markup += layout.counter.text;
00388         markup += "\\tab}{\\*\\pn";
00389         if (layout.counter.style > 5)
00390         {
00391         markup += "\\pnlvlblt ";
00392         markup += "{\\pntxtb ";
00393         if (!layout.counter.lefttext.isEmpty() && layout.counter.lefttext != "{" && layout.counter.lefttext != "}")
00394         {
00395                 markup += layout.counter.lefttext;
00396         }
00397         switch (layout.counter.style)
00398         {
00399         case 6:
00400         {
00401                 //custom bullets (one char)
00402                 //TODO: use correct character/sign for bullet
00403 
00404             markup += layout.counter.customCharacter;
00405             break;
00406         }
00407         case 7:
00408         {
00409                 //custom bullets (complex)
00410             markup += layout.counter.text;
00411             break;
00412         }
00413         case 8:
00414         {
00415                 //circle bullets
00416                 //TODO: use correct character/sign for bullet
00417             markup += "\\bullet";
00418             break;
00419         }
00420         case 9:
00421         {
00422                 //square bullets
00423                 //TODO: use correct character/sign for bullet
00424             markup += "\\bullet";
00425             break;
00426         }
00427         case 10:
00428         {
00429                 //disc bullets
00430                 //TODO: make work in OO
00431             markup += "\\bullet";
00432             break;
00433         }
00434         case 11:
00435         {
00436                 //disc bullets
00437                 //TODO: use correct character/sign for bullet
00438             markup += layout.counter.text;
00439             break;
00440         }
00441         default:
00442             markup += "\\bullet";
00443         }
00444         markup += "}";
00445         }
00446         else
00447         {
00448             if (layout.counter.numbering!=0)
00449         {
00450                 markup += "\\pnlvl";
00451                 markup += QString::number(layout.counter.depth + 1);
00452                 markup += "\\pnprev1";
00453         }
00454         else if (layout.counter.style==1)
00455         {
00456         markup += "\\pnlvlbody";
00457         }
00458         else
00459         {
00460         markup += "\\pnlvl";
00461         markup += QString::number(11 - layout.counter.style);
00462         }
00463 
00464         switch (layout.counter.style)
00465         {
00466         case 1:
00467         {
00468         markup += "\\pndec";
00469         break;
00470         }
00471         case 2:
00472         {
00473         markup += "\\pnlcltr";
00474         break;
00475         }
00476         case 3:
00477         {
00478         markup += "\\pnucltr";
00479         break;
00480         }
00481         case 4:
00482         {
00483         markup += "\\pnlcrm";
00484         break;
00485         }
00486         case 5:
00487         {
00488         markup += "\\pnucrm";
00489         break;
00490         }
00491         default:
00492         markup += "\\pndec";
00493         }
00494         markup += "{\\pntxtb ";
00495         markup += layout.counter.lefttext;
00496         markup += " }";
00497     }
00498         markup += "{\\pntxta ";
00499         markup += layout.counter.righttext;
00500         markup += " }";
00501 
00502         // ### FIXME: that is too late! And why at every list paragraph? (See bug #88241)
00503         if (layout.counter.start!=0)
00504         {
00505         markup += "\\pnstart";
00506         markup += QString::number(layout.counter.start);
00507         }
00508         else
00509         {
00510         markup += "\\pnstart1";
00511         }
00512         markup += "\\pnindent0\\pnhang";
00513 
00514         if( layout.formatData.text.fontSize > 0 )
00515         {
00516         markup += "\\pnfs";
00517         markup += QString::number((2 * layout.formatData.text.fontSize));
00518         }
00519 
00520         if( !layout.formatData.text.fontName.isEmpty() )
00521         {
00522             markup += lookupFont("\\pnf", layout.formatData.text.fontName);
00523         }
00524 
00525         markup += "}";
00526 }
00527 
00528 
00529     LayoutData styleLayout;
00530     markup += lookupStyle(layout.styleName, styleLayout);
00531     markup += layoutToRtf(styleLayout,layout,true);
00532 
00533     if ( 1==layout.formatData.text.verticalAlignment )
00534     {
00535         markup += "\\sub"; //Subscript
00536     }
00537     else if ( 2==layout.formatData.text.verticalAlignment )
00538     {
00539         markup += "\\super"; //Superscript
00540     }
00541 
00542 
00543     if (layout.pageBreakBefore)
00544         content += "\\page ";
00545 
00546 
00547 
00548 
00549 
00550     if (!paraText.isEmpty())
00551     {
00552 
00553         ValueListFormatData::ConstIterator  paraFormatDataIt;
00554 
00555         QString partialText;
00556 
00557         FormatData formatRef = layout.formatData;
00558 
00559         for ( paraFormatDataIt = paraFormatDataList.begin ();
00560               paraFormatDataIt != paraFormatDataList.end ();
00561               paraFormatDataIt++ )
00562         {
00563             if (1==(*paraFormatDataIt).id)
00564             {
00565                 //Retrieve text
00566                 partialText=paraText.mid ( (*paraFormatDataIt).pos, (*paraFormatDataIt).len );
00567                 content +=formatTextParagraph(partialText, formatRef, *paraFormatDataIt);
00568         }
00569             else if (4==(*paraFormatDataIt).id)
00570             {
00571                 // ### TODO: put date/time fields into own method.
00572                 if ( (0==(*paraFormatDataIt).variable.m_type) // date
00573                     || (2==(*paraFormatDataIt).variable.m_type) ) // time
00574                 {
00575                     // ### TODO: fixed/variable date
00576                     content += "{\\field{\\*\\fldinst ";
00577                     if (0==(*paraFormatDataIt).variable.m_type)
00578                         content += "DATE ";
00579                     else
00580                         content += "TIME ";
00581                     QString key((*paraFormatDataIt).variable.m_key.mid(4));
00582                     kdDebug(30515) << "Time format: " << key << endl;
00583                     if (key.startsWith("locale"))
00584                     {
00585                         if (key == "locale" )
00586                         {
00587                             if (0==(*paraFormatDataIt).variable.m_type)
00588                                 key = KGlobal::locale()->dateFormat();
00589                             else
00590                                 key = KGlobal::locale()->timeFormat();
00591                         }
00592                         else if ( key == "localeshort" )
00593                             key = KGlobal::locale()->dateFormatShort();
00594                         else if ( key == "localedatetime" )
00595                         {
00596                             key = KGlobal::locale()->dateFormat();
00597                             key += ' ';
00598                             key += KGlobal::locale()->timeFormat();
00599                         }
00600                         else if ( key == "localedatetimeshort" )
00601                         {
00602                             key = KGlobal::locale()->dateFormat();
00603                             key += ' ';
00604                             key += KGlobal::locale()->timeFormat();
00605                         }
00606 
00607                         kdDebug(30515) << "Locale date in KLocale format:  " << key << endl;
00608 
00609                         // KLocale's key differ from KWord
00610 
00611                         // Date
00612                         key.replace( "%Y", "yyyy" );    // Year 4 digits
00613                         key.replace( "%y", "yy" );      // Year 2 digits
00614                         key.replace( "%n", "M" );       // Month 1 digit
00615                         key.replace( "%m", "MM" );      // Month 2 digits
00616                         key.replace( "%e", "d" );       // Day 1 digit
00617                         key.replace( "%d", "dd" );      // Day 2 digits
00618                         key.replace( "%b", "MMM" );     // Month 3 letters
00619                         key.replace( "%B", "MMMM" );    // Month all letters
00620                         key.replace( "%a", "ddd" );     // Day 3 letters
00621                         key.replace( "%A", "dddd" );    // Day all letters
00622                         // 12h
00623                         key.replace( "%p", "am/pm" );   // AM/PM (KLocale knows it only lower case)
00624                         key.replace( "%I", "hh" );      // 12 hour 2 digits
00625                         key.replace( "%l", "h" );       // 12 hour 1 digits
00626                         // 24h
00627                         key.replace( "%H", "HH" );      // 24 hour 2 digits
00628                         key.replace( "%k", "H" );       // 24 hour 1 digit
00629                         // Other times
00630                         key.replace( "%M", "mm" );      // minute 2 digits (KLocale knows it with 2 digits)
00631                         key.replace( "%S", "ss" );      // second 2 digits (KLocale knows it with 2 digits)
00632 
00633                         kdDebug(30515) << "Locale date in RTF format:  " << key << endl;
00634                     }
00635                     else if (!key.isEmpty())
00636                     {
00637                         const QRegExp regexp("AP",false); // Not case-sensitive
00638                         if (regexp.search(key)!=-1)
00639                         {
00640                             // 12h
00641                             key.replace("ap","am/pm");
00642                             key.replace("AP","AM/PM");
00643                         }
00644                         else
00645                         {
00646                             //24h
00647                             key.replace('h','H'); // MS Word uses H for 24hour times
00648                         }
00649                         // MS Word do not know possesive months
00650                         key.replace("PPP","MMM");
00651                         key.replace("PPPP","MMMM");
00652                         key.replace("zzz","000"); // replace microseconds by 000
00653                         kdDebug(30515) << "New format:  " << key << endl;
00654                         content += "\\@ \"";
00655                         content += key;
00656                         content += "\" ";
00657                     }
00658                     content += "\\* MERGEFORMAT }{\\fldrslt ";
00659                     content += escapeRtfText((*paraFormatDataIt).variable.m_text);
00660                     content += "}}";
00661                 }
00662                 else if (4==(*paraFormatDataIt).variable.m_type)
00663                 {
00664                     QString strFieldType;
00665                     if ((*paraFormatDataIt).variable.isPageNumber())
00666                     {
00667                         content += "{\\field{\\*\\fldinst PAGE \\* MERGEFORMAT }{\\fldrslt ";
00668                         content += escapeRtfText((*paraFormatDataIt).variable.m_text);
00669                         content += "}}";
00670                     }
00671                     else if ((*paraFormatDataIt).variable.isPageCount())
00672                     {
00673                         content += "{\\field{\\*\\fldinst NUMPAGES \\* MERGEFORMAT }{\\fldrslt ";
00674                         content += escapeRtfText((*paraFormatDataIt).variable.m_text);
00675                         content += "}}";
00676                     }
00677                     else
00678                     {
00679                         // Unknown subtype, therefore write out the result
00680                         content += escapeRtfText((*paraFormatDataIt).variable.m_text);
00681                     }
00682                 }
00683                 else if (8==(*paraFormatDataIt).variable.m_type)
00684                 {
00685                     // Field
00686                     QString name = escapeRtfText((*paraFormatDataIt).variable.getFieldName());
00687                     QString value = escapeRtfText((*paraFormatDataIt).variable.getFieldValue());
00688                     QString rtfField = mapFieldName(name);
00689 
00690                     if( rtfField.isEmpty() )
00691                         // Couldn't map field name, just write out the value
00692                         content += escapeRtfText((*paraFormatDataIt).variable.m_text);
00693                     else
00694                     {
00695                         content += "{\\field";
00696                         content += "{\\*\\fldinst ";
00697                         content +=  rtfField;
00698                         content += "  \\* MERGEFORMAT }";
00699                         content += "{\\fldrslt {";
00700                         content += value;
00701                         content += "}}}";
00702                     }
00703                 }
00704                 else if (9==(*paraFormatDataIt).variable.m_type)
00705                 {
00706                     // A link
00707                     content += "{\\field";
00708                     content += "{\\*\\fldinst HYPERLINK ";
00709                     content +=  escapeRtfText((*paraFormatDataIt).variable.getHrefName());
00710                     content += "}";
00711                     content += "{\\fldrslt ";
00712                     content += "{\\ul";   // underline, ### TODO: use style Hyperlink
00713                     content += lookupColor("\\cf",QColor(0,0,255));// blue
00714                     content += escapeRtfText((*paraFormatDataIt).variable.getLinkName());
00715                     content += "}}}";
00716                 }
00717                 else if (11==(*paraFormatDataIt).variable.m_type)
00718                 {
00719                     // Footnote
00720                     QString value = (*paraFormatDataIt).variable.getFootnoteValue();
00721                     bool automatic = (*paraFormatDataIt).variable.getFootnoteAuto();
00722                     QValueList<ParaData> *paraList = (*paraFormatDataIt).variable.getFootnotePara();
00723                     if( paraList )
00724                     {
00725                         QString fstr;
00726                         QValueList<ParaData>::ConstIterator it;
00727                         QValueList<ParaData>::ConstIterator end(paraList->end());
00728                         const QString prefixSaved = m_prefix;
00729                         m_prefix = QString::null;
00730                         for (it=paraList->begin();it!=end;++it)
00731                             fstr += ProcessParagraphData( (*it).text, (*it).layout,(*it).formattingList);
00732                         m_prefix = prefixSaved;
00733                         content += "{\\super ";
00734                         content += automatic ? "\\chftn " : value;
00735                         content += "{\\footnote ";
00736                         content += "{\\super ";
00737                         content += automatic ? "\\chftn " : value;
00738                         content += fstr;
00739                         content += "}}}";
00740                     }
00741                 }
00742                 else
00743                 {
00744                     // Generic variable
00745                     content += escapeRtfText((*paraFormatDataIt).variable.m_text);
00746                 }
00747             }
00748             else if (6==(*paraFormatDataIt).id)
00749             {
00750                 kdDebug(30515) << "Found an anchor of type: " << (*paraFormatDataIt).frameAnchor.type << endl;
00751                 // We have an image, a clipart or a table
00752 
00753         if (6==(*paraFormatDataIt).frameAnchor.type)
00754                 {
00755 
00756 
00757             if (!content.isEmpty())
00758             {
00759             str += m_prefix;
00760             str += markup;
00761             str += " {";
00762             str += content;
00763             str += "}";
00764             str += m_eol;
00765             content = QString::null;
00766             if (!m_inTable)
00767             {
00768                 m_prefix = "\\par";
00769             }
00770             }
00771             str += makeTable((*paraFormatDataIt).frameAnchor);
00772         }
00773                 else if ((2==(*paraFormatDataIt).frameAnchor.type) || (5==(*paraFormatDataIt).frameAnchor.type))
00774                 {
00775                     content += makeImage((*paraFormatDataIt).frameAnchor);
00776 
00777                 }
00778         }
00779         }
00780     }
00781 
00782     if (layout.pageBreakAfter)
00783         content += "\\page";
00784 
00785     if (!content.isEmpty())
00786     {
00787         str += m_prefix;
00788     str += markup;
00789     str += " {";
00790     str += content;
00791     str += "}";
00792     str += m_eol;
00793     if (m_inTable==false)
00794     {
00795        m_prefix = "\\par";
00796         }
00797     }
00798     if (str.isEmpty())
00799     {
00800      str ="\\par\\pard\\plain";
00801     if (m_inTable==false)
00802     {
00803        m_prefix = "\\par";
00804     }
00805     }
00806     return str;
00807 }
00808 
00809 bool RTFWorker::doFullParagraph(const QString& paraText,
00810     const LayoutData& layout, const ValueListFormatData& paraFormatDataList)
00811 {
00812     kdDebug(30515) << "Entering RTFWorker::doFullParagraph" << endl << paraText << endl;
00813     QString par = ProcessParagraphData( paraText, layout, paraFormatDataList);
00814     m_textBody += par;
00815     kdDebug(30515) << "Quiting RTFWorker::doFullParagraph" << endl;
00816     return true;
00817 }
00818 
00819 bool RTFWorker::doHeader(const HeaderData& header)
00820 {
00821     QString str;
00822     QString content;
00823     if( header.page == HeaderData::PAGE_ODD )
00824         str = "\\facingp{\\headerr";
00825     else if( header.page == HeaderData::PAGE_EVEN )
00826         str = "\\facingp{\\headerl";
00827     else if( header.page == HeaderData::PAGE_FIRST )
00828         str = "\\facingp{\\headerl";
00829     else if( header.page == HeaderData::PAGE_ALL )
00830         str = "{\\header";
00831     else
00832         return false;
00833 
00834     str += " {";
00835 
00836     QValueList<ParaData>::ConstIterator it;
00837     QValueList<ParaData>::ConstIterator end(header.para.end());
00838     for (it=header.para.begin();it!=end;++it)
00839         content += ProcessParagraphData( (*it).text,(*it).layout,(*it).formattingList);
00840 
00841     if (content!="\\par\\pard\\plain")
00842     {
00843     str += content;
00844     str += "}";
00845 
00846     str += "}";
00847 
00848     m_textBody += str;
00849     }
00850     m_prefix=QString::null;
00851     return true;
00852 }
00853 
00854 bool RTFWorker::doFooter(const FooterData& footer)
00855 {
00856     QString str;
00857     QString content;
00858     if( footer.page == FooterData::PAGE_ODD )
00859         str = "\\facingp{\\footerr";
00860     else if( footer.page == FooterData::PAGE_EVEN )
00861         str = "\\facingp{\\footerl";
00862     else if( footer.page == FooterData::PAGE_FIRST )
00863         str = "\\facingp{\\headerl";
00864     else if( footer.page == FooterData::PAGE_ALL )
00865         str = "{\\footer";
00866     else
00867         return false;
00868 
00869     str += " {";
00870 
00871     QValueList<ParaData>::ConstIterator it;
00872     QValueList<ParaData>::ConstIterator end(footer.para.end());
00873     for (it=footer.para.begin();it!=end;++it)
00874         content += ProcessParagraphData( (*it).text,(*it).layout,(*it).formattingList);
00875 
00876     if (content!="\\par\\pard\\plain")
00877     {
00878     str += content;
00879     str += "}";
00880 
00881     str += "}";
00882 
00883     m_textBody += str;
00884     }
00885     m_prefix=QString::null;
00886     return true;
00887 }
00888 
00889 bool RTFWorker::doOpenFile(const QString& filenameOut, const QString& /*to*/)
00890 {
00891     m_ioDevice=new QFile(filenameOut);
00892 
00893     if (!m_ioDevice)
00894     {
00895         kdError(30515) << "No output file! Aborting!" << endl;
00896         return false;
00897     }
00898 
00899     if ( !m_ioDevice->open (IO_WriteOnly) )
00900     {
00901         kdError(30515) << "Unable to open output file!" << endl;
00902         return false;
00903     }
00904 
00905     m_streamOut=new QTextStream(m_ioDevice);
00906 
00907     // ### TODO: should "CP 1252" be used directly? (But RTFWorker::escapeRtfText is beased on ISO-8859-1 only.)
00908     m_streamOut->setEncoding(QTextStream::Latin1); // We are declaring the RTF document as CP 1252, so use ISO-8859-1
00909 
00910     m_fileName=filenameOut;
00911 
00912     return true;
00913 }
00914 
00915 bool RTFWorker::doCloseFile(void)
00916 {
00917     kdDebug(30515) << __FILE__ << ":" << __LINE__ << endl;
00918     delete m_streamOut;
00919     m_streamOut=NULL;
00920     if (m_ioDevice)
00921         m_ioDevice->close();
00922     return true;
00923 }
00924 
00925 bool RTFWorker::doOpenDocument(void)
00926 {
00927     // Make the file header
00928 
00929     // Note: we use \\ansicpg1252 because 1200 is not supposed to be supported
00930     // Note2: we assume using \\ansicpg1252 in RTFWorker::escapeRtfText and in RTFWorker::doOpenFile
00931     *m_streamOut << "{\\rtf1\\ansi\\ansicpg1252\\uc1\\deff0" << m_eol;
00932 
00933     // Default color table
00934     m_colorList
00935         << QColor(0,0,0)     << QColor(0,0,255)     << QColor(0,255,255)
00936         << QColor(0,255,0)   << QColor(255,0,255)   << QColor(255,0,0)
00937         << QColor(255,255,0) << QColor(255,255,255) << QColor(0,0,128)
00938         << QColor(0,128,128) << QColor(0,128,0)     << QColor(128,0,128)
00939         << QColor(128,0,0)   << QColor(128,128,0)   << QColor(128,128,128);
00940 
00941     return true;
00942 }
00943 
00944 void RTFWorker::writeFontData(void)
00945 {
00946     kdDebug(30515) << "Fonts:" << m_fontList << endl;
00947     *m_streamOut << "{\\fonttbl";
00948     uint count;
00949     QFontDatabase fontDatabase;
00950     QStringList::ConstIterator it;
00951     for (count=0, it=m_fontList.begin();
00952         it!=m_fontList.end();
00953         count++, it++)
00954     {
00955         const QString strLower( (*it).lower() );
00956         *m_streamOut << "{\\f" << count;
00957         if ( (strLower.find("symbol")>-1) || (strLower.find("dingbat")>-1) )
00958             *m_streamOut << "\\ftech";
00959         else if ( (strLower.find("script")>-1) )
00960             *m_streamOut << "\\fscript";
00961 
00962 #if 1
00963         else
00964         {
00965             // We do not know the font type that we have
00966             *m_streamOut << "\\fnil";
00967         }
00968 #else
00969         else
00970         // QFontInfo:styleHint() does not guess anything, it just returns what is in the QFont. Nothing put in, nothing gets out.
00971         {
00972             QFontInfo info(*it);
00973             switch (info.styleHint())
00974             {
00975             case QFont::SansSerif:
00976             default:
00977                 {
00978                     *m_streamOut << "\\fswiss";
00979                     break;
00980                 }
00981             case QFont::Serif:
00982                 {
00983                     *m_streamOut << "\\froman";
00984                     break;
00985                 }
00986             case QFont::Courier:
00987                 {
00988                     *m_streamOut << "\\fmodern";
00989                     break;
00990                 }
00991             case QFont::OldEnglish:
00992                 {
00993                     *m_streamOut << "\\fdecor";
00994                     break;
00995                 }
00996             }
00997         }
00998 #endif
00999         // ### TODO: \fcharset would be mandatory but Qt3 does not give us the font charset. :-(
01000         *m_streamOut << "\\fprq" << ( fontDatabase.isFixedPitch( *it ) ? 1 : 2 ) << " "; // font definition
01001         *m_streamOut << escapeRtfText( *it );
01002         *m_streamOut <<  ";}" << m_eol; // end font table entry
01003     }
01004     *m_streamOut << "}"; // end of font table
01005 }
01006 
01007 void RTFWorker::writeColorData(void)
01008 {
01009     *m_streamOut << "{\\colortbl;";
01010     uint count;
01011     QValueList<QColor>::ConstIterator it;
01012     for (count=0, it=m_colorList.begin();
01013         it!=m_colorList.end();
01014         count++, it++)
01015     {
01016         *m_streamOut << "\\red" << (*it).red();
01017         *m_streamOut << "\\green" << (*it).green();
01018         *m_streamOut << "\\blue" << (*it).blue();
01019         *m_streamOut <<  ";"; // end of entry
01020     }
01021     *m_streamOut << "}"; // end of color table
01022 }
01023 
01024 void RTFWorker::writeStyleData(void)
01025 {
01026     *m_streamOut << "{\\stylesheet" << m_eol;
01027 
01028     uint count;
01029     QValueList<LayoutData>::ConstIterator it;
01030     for (count=0, it=m_styleList.begin();
01031         it!=m_styleList.end();
01032         count++, it++)
01033     {
01034         *m_streamOut << "{";
01035         if (count>0) // \s0 is not written out
01036             *m_streamOut << "\\s" << count;
01037 
01038         *m_streamOut << layoutToRtf((*it),(*it),true);
01039 
01040         // \snext must be the last keyword before the style name
01041         // Find the number of the following style
01042         uint counter=0;  // counts position in style table starting at 0
01043         QValueList < LayoutData > ::ConstIterator it2;
01044         for( it2 =  m_styleList.begin(); it2 != m_styleList.end(); counter++, ++it2 )
01045         {
01046             if ( (*it2).styleName == (*it).styleFollowing )
01047             {
01048                 *m_streamOut << "\\snext" << counter;
01049                 break;
01050             }
01051         }
01052 
01053         *m_streamOut << " " << (*it).styleName << ";";
01054         *m_streamOut << "}";
01055         *m_streamOut << m_eol;
01056     }
01057 
01058     *m_streamOut << "}";
01059 }
01060 
01061 bool RTFWorker::doCloseDocument(void)
01062 {
01063 
01064     writeFontData();
01065     writeColorData();
01066     writeStyleData();
01067 
01068     if (!m_textDocInfo.isEmpty())
01069     {
01070         *m_streamOut << "{\\info ";  // string of document information markup
01071         *m_streamOut << m_textDocInfo;   // add document author, title, operator
01072         *m_streamOut << "}";
01073     }
01074     *m_streamOut << "\\paperw" << int(m_paperWidth);
01075     *m_streamOut << "\\paperh" << int(m_paperHeight);
01076     if (1==m_paperOrientation)
01077         *m_streamOut << "\\landscape";
01078     *m_streamOut << "\\margl" << int(m_paperMarginLeft);
01079     *m_streamOut << "\\margr" << int(m_paperMarginRight);
01080     *m_streamOut << "\\margt" << int(m_paperMarginTop);
01081     *m_streamOut << "\\margb" << int(m_paperMarginBottom);
01082     *m_streamOut << m_textPage;  // add page size, margins, etc.
01083     *m_streamOut << "\\widowctrl\\ftnbj\\aenddoc\\formshade \\fet0\\sectd\n";
01084     if (m_startPageNumber >= 1)
01085         *m_streamOut << "\\pgnstart" << m_startPageNumber << endl;
01086     //*m_streamOut << "\\linex0\\endnhere\\plain";
01087     *m_streamOut << "\\pard\\plain";
01088     *m_streamOut << m_textBody;
01089 
01090     *m_streamOut << "}" << m_eol;
01091     return true;
01092 }
01093 
01094 bool RTFWorker::doFullDocumentInfo(const KWEFDocumentInfo& docInfo)
01095 {
01096 
01097     if ( !docInfo.title.isEmpty() )
01098     {
01099         m_textDocInfo += "{\\title ";
01100         m_textDocInfo += escapeRtfText( docInfo.title );
01101         m_textDocInfo += "}";
01102     }
01103 
01104     if ( !docInfo.fullName.isEmpty() )
01105     {
01106         m_textDocInfo += "{\\author ";
01107         m_textDocInfo += escapeRtfText( docInfo.fullName );
01108         m_textDocInfo += "}";
01109     }
01110 
01111     if ( !docInfo.keywords.isEmpty() )
01112     {
01113         m_textDocInfo += "{\\keywords ";
01114         m_textDocInfo += escapeRtfText( docInfo.keywords );
01115         m_textDocInfo += "}";
01116     }
01117     if ( !docInfo.subject.isEmpty() )
01118     {
01119         m_textDocInfo += "{\\subject ";
01120         m_textDocInfo += escapeRtfText( docInfo.subject );
01121         m_textDocInfo += "}";
01122     }
01123 
01124     if ( !docInfo.company.isEmpty() )
01125     {
01126         m_textDocInfo += "{\\company ";
01127         m_textDocInfo += escapeRtfText( docInfo.company );
01128         m_textDocInfo += "}";
01129     }
01130 
01131     // Now add who we are in a \comment
01132     QString revision("$Revision: 541888 $");
01133     m_textDocInfo += "{\\comment ";
01134     m_textDocInfo += "Generated by KWord's RTF Export Filter";
01135     m_textDocInfo += revision.mid(10).remove('$'); // has a leading and a trailing space.
01136     m_textDocInfo += "}";
01137 
01138     if ( !docInfo.abstract.isEmpty() )
01139     {
01140         m_textDocInfo += "{\\doccomm ";
01141         m_textDocInfo += escapeRtfText( docInfo.abstract );
01142         m_textDocInfo += "}";
01143     }
01144 
01145     return true;
01146 }
01147 
01148 bool RTFWorker::doOpenTextFrameSet(void)
01149 {
01150     return true;
01151 }
01152 
01153 bool RTFWorker::doCloseTextFrameSet(void)
01154 {
01155     return true;
01156 }
01157 
01158 QString RTFWorker::openSpan(const FormatData& formatOrigin, const FormatData& format)
01159 {
01160     QString result;
01161 
01162     result += "{";
01163     result += textFormatToRtf(formatOrigin.text,format.text,false);
01164 
01165     if ( 1==format.text.verticalAlignment )
01166     {
01167         result += "\\sub"; //Subscript
01168     }
01169     else if ( 2==format.text.verticalAlignment )
01170     {
01171         result += "\\super"; //Superscript
01172     }
01173 
01174     result += " ";
01175     return result;
01176 }
01177 
01178 QString RTFWorker::closeSpan(const FormatData& , const FormatData& )
01179 {
01180     QString result;
01181     result += "}";
01182     return result;
01183 }
01184 
01185 // The following function encodes the kword unicode characters into
01186 // RTF seven bit ASCII. This affects any 8 bit characters.
01187 // They are encoded either with \' or with \u
01188 QString RTFWorker::escapeRtfText ( const QString& text ) const
01189 {
01190     // initialize strings
01191     QString escapedText;
01192     const uint length = text.length();
01193     for ( uint i = 0; i < length; i++ )
01194     {
01195         QChar QCh ( text.at( i ) );  // get out one unicode char from the string
01196         const ushort ch = QCh.unicode();  // take unicode value of the char
01197 
01198         if ( QCh == '\\' )  escapedText += "\\\\"; // back-slash
01199         else if ( QCh == '{' )   escapedText += "\\{";
01200         else if ( QCh == '}' )   escapedText += "\\}";
01201         else if ( ch >= 32 && ch <= 127) // ASCII character
01202             escapedText += QCh;
01203         else if ( ch == 0x0009 ) escapedText += "\\tab "; // tabulator
01204         else if ( ch == 0x00a0 ) escapedText += "\\~"; // Non-breaking space
01205         else if ( ch == 0x00ad ) escapedText += "\\-"; // Soft hyphen
01206         else if ( ch == 0x00b7 ) escapedText += "\\|";
01207         else if ( ch == 0x2011 ) escapedText += "\\_"; // Non-breaking hyphen
01208         else if ( ch == 0x2002 ) escapedText += "\\enspace ";
01209         else if ( ch == 0x2003 ) escapedText += "\\emspace ";
01210         else if ( ch == 0x2004 ) escapedText += "\\qmspace ";
01211         else if ( ch == 0x200c ) escapedText += "\\zwnj ";
01212         else if ( ch == 0x200d ) escapedText += "\\zwj ";
01213         else if ( ch == 0x200e ) escapedText += "\\ltrmark ";
01214         else if ( ch == 0x200f ) escapedText += "\\rtlmark ";
01215         else if ( ch == 0x2013 ) escapedText += "\\endash ";
01216         else if ( ch == 0x2014 ) escapedText += "\\emdash ";
01217         else if ( ch == 0x2018 ) escapedText += "\\lquote ";
01218         else if ( ch == 0x2019 ) escapedText += "\\rquote ";
01219         else if ( ch == 0x201c ) escapedText += "\\ldblquote ";
01220         else if ( ch == 0x201d ) escapedText += "\\rdblquote ";
01221         else if ( ch == 0x2022 ) escapedText += "\\bullet ";
01222         else if ( ch >= 160 && ch < 256) // check for characters common between ISO-8859-1 and CP1252
01223         {   // NOTE: 128 to 159 in CP1252 are somewhere else in UTF-8 and do not exist in ISO-8859-1 (### TODO?)
01224             escapedText += "\\\'";   // escape upper page character to 7 bit
01225             escapedText += QString::number ( ch, 16 );
01226         }
01227         else if ( ch >= 256) // check for a higher code non-ASCII character
01228         {
01229             // encode this as decimal unicode with a replacement character.
01230             escapedText += "\\u";
01231             escapedText += QString::number ( ch, 10 );
01232             // We decompose the character. If it works, the first character is whitout any accent.
01233             // (Of course this only works with Latin letters.)
01234             // WARNING: QChar::decomposition is not re-entrant in Qt 3.x
01235             QChar replacement ( QCh.decomposition().at(0) );
01236             kdDebug(30515) << "Proposed replacement character: " << QString(replacement) << endl;
01237 
01238             if (replacement.isNull() || replacement<=' ' || replacement>=char(127)
01239                 || replacement=='{' || replacement=='}' || replacement=='\\')
01240                 replacement='?'; // Not a normal ASCII character, so default to show a ? sign
01241 
01242             escapedText += replacement; // The \uc1 dummy character.
01243 
01244         }
01245         else
01246             escapedText += QCh ;
01247 
01248     }
01249 
01250     return escapedText;
01251 
01252 }
01253 
01254 bool RTFWorker::doFullPaperFormat(const int /*format*/,
01255     const double width, const double height, const int orientation)
01256 {
01257     m_paperWidth=width*20;
01258     m_paperHeight=height*20;
01259     m_paperOrientation=orientation;
01260     return true;
01261 }
01262 
01263 bool RTFWorker::doFullPaperBorders (const double top, const double left,
01264     const double bottom, const double right)
01265 {
01266     m_paperMarginTop=top*20;
01267     m_paperMarginLeft=left*20;
01268     m_paperMarginBottom=bottom*20;
01269     m_paperMarginRight=right*20;
01270     return true;
01271 }
01272 
01273 bool RTFWorker::doFullDefineStyle(LayoutData& layout)
01274 {
01275     //Register the new style in the style list
01276     m_styleList << layout;
01277 
01278     // Now we must register a few things (with help of the lookup methods.)
01279     lookupFont("\\f", layout.formatData.text.fontName);
01280     lookupColor(QString::null, layout.formatData.text.fgColor);
01281     lookupColor(QString::null, layout.formatData.text.bgColor);
01282 
01283     return true;
01284 }
01285 
01286 static QString writeDate(const QString keyword, const QDateTime& now)
01287 {
01288     QString str;
01289     if (now.isValid())
01290     {
01291         kdDebug(30515) << "Date " << keyword << " " << now.toString() << endl;
01292         str += '{';
01293         str += keyword;
01294         const QDate nowDate(now.date());
01295         str += "\\yr";
01296         str += QString::number(nowDate.year());
01297         str += "\\mo";
01298         str += QString::number(nowDate.month());
01299         str += "\\dy";
01300         str += QString::number(nowDate.day());
01301         const QTime nowTime(now.time());
01302         str += "\\hr";
01303         str += QString::number(nowTime.hour());
01304         str += "\\min";
01305         str += QString::number(nowTime.minute());
01306         str += "\\sec";
01307         str += QString::number(nowTime.second());
01308         str += '}';
01309     }
01310     else
01311         kdWarning(30515) << "Date " << keyword << " is not valid! Skipping!" << endl;
01312 
01313     return str;
01314 }
01315 
01316 bool RTFWorker::doVariableSettings(const VariableSettingsData& vs)
01317 {
01318     m_textDocInfo += writeDate("\\creatim",vs.creationTime);
01319     m_textDocInfo += writeDate("\\revtim", vs.modificationTime);
01320     m_textDocInfo += writeDate("\\printim",vs.printTime);
01321     m_startPageNumber = vs.startingPageNumber;
01322 
01323     return true;
01324 }
01325 
01326 QString RTFWorker::textFormatToRtf(const TextFormatting& formatOrigin,
01327     const TextFormatting& formatData, const bool force)
01328 {
01329     // TODO: rename variable formatData
01330     QString strElement; // TODO: rename this variable
01331 
01332     // Font name
01333     const QString fontName(formatData.fontName);
01334     if (!fontName.isEmpty()
01335         && (force || (formatOrigin.fontName!=formatData.fontName)))
01336     {
01337         strElement+=lookupFont("\\f", fontName);
01338     }
01339 
01340     if (force || (formatOrigin.fontSize!=formatData.fontSize))
01341     {
01342         const int size=formatData.fontSize;
01343         if (size>0)
01344         {
01345             strElement+="\\fs";
01346             strElement+=QString::number(2*size,10);
01347         }
01348     }
01349 
01350     if (force || (formatOrigin.italic!=formatData.italic))
01351     {
01352         // Font style
01353         if ( formatData.italic )
01354         {
01355             strElement+="\\i";
01356         }
01357         else
01358         {
01359             strElement+="\\i0";
01360         }
01361     }
01362 
01363     if (force || ((formatOrigin.weight>=75)!=(formatData.weight>=75)))
01364     {
01365         if ( formatData.weight >= 75 )
01366         {
01367             strElement+="\\b";
01368         }
01369         else
01370         {
01371             strElement+="\\b0";
01372         }
01373     }
01374 
01375     if (force || (formatOrigin.fgColor!=formatData.fgColor))
01376     {
01377         if ( formatData.fgColor.isValid() )
01378         {
01379             strElement+=lookupColor("\\cf", formatData.fgColor);
01380         }
01381     }
01382 
01383     if (force || (formatOrigin.bgColor!=formatData.bgColor))
01384     {
01385         if ( formatData.bgColor.isValid() )
01386         {
01387             strElement+=lookupColor("\\cb", formatData.bgColor);
01388             strElement+=lookupColor("\\highlight", formatData.bgColor); // MS Word wants this
01389             // ### FIXME: \highlight is not allowed in style definitions (RTF 1.6)
01390         }
01391     }
01392 
01393     if ( force
01394         || ( formatOrigin.underline != formatData.underline )
01395         || ( formatOrigin.underlineValue != formatData.underlineValue )
01396         || ( formatOrigin.underlineStyle != formatData.underlineStyle )
01397         || ( formatOrigin.underlineWord != formatData.underlineWord ) )
01398     {
01399         if ( formatData.underline )
01400         {
01401             QString underlineValue = formatData.underlineValue;
01402             QString underlineStyle = formatData.underlineStyle;
01403             bool underlineWord = formatData.underlineWord;
01404             QString ul ( "\\ul" );  // fall-back: simple underline
01405 
01406             if( underlineStyle.isEmpty() ) underlineStyle = "solid";
01407             if( underlineValue == "1" ) underlineValue = "single";
01408 
01409             if( underlineValue == "double" )
01410                 ul = "\\uldb";
01411             else if( underlineValue == "single-bold" )
01412                 ul = "\\ulth";
01413             else if( underlineValue == "wave" )
01414                 ul = "\\ulwave";
01415             else if( underlineValue == "single" )
01416             {
01417                 if( underlineStyle == "dash" )
01418                     ul = "\\uldash";
01419                 else if( underlineStyle == "dot" )
01420                     ul = "\\uld";
01421                 else if( underlineStyle == "dashdot" )
01422                     ul = "\\uldashd";
01423                 else if( underlineStyle == "dashdotdot" )
01424                     ul = "\\uldashdd";
01425                 else if( underlineWord )
01426                     ul = "\\ulw";
01427             };
01428 
01429             strElement+= ul;
01430             if (formatData.underlineColor.isValid())
01431             {
01432                 strElement+=lookupColor("\\ulc",formatData.underlineColor);
01433             }
01434         }
01435         else
01436         {
01437             strElement+="\\ul0";
01438         }
01439     }
01440 
01441     if ( force
01442         || ( formatOrigin.strikeout != formatData.strikeout )
01443         || ( formatOrigin.strikeoutType != formatData.strikeoutType ) )
01444     {
01445         if ( formatData.strikeout )
01446         {
01447             if( formatData.strikeoutType == "double" )
01448                 strElement+="\\striked1"; // 1 needed! (The exception that confirms the rule!)
01449             else
01450                 strElement+="\\strike";
01451         }
01452         else
01453         {
01454             strElement+="\\strike0"; // ### TODO: \striked0 too?
01455         }
01456     }
01457 
01458     return strElement;
01459 }
01460 
01461 QString RTFWorker::layoutToRtf(const LayoutData& layoutOrigin,
01462     const LayoutData& layout, const bool force)
01463 {
01464     QString strLayout;
01465 
01466 
01467     if (force || (layoutOrigin.alignment!=layout.alignment))
01468     {
01469         if (layout.alignment=="left")
01470             strLayout += "\\ql";
01471         else if (layout.alignment== "right")
01472             strLayout += "\\qr";
01473         else if (layout.alignment=="center")
01474             strLayout += "\\qc";
01475         else if (layout.alignment=="justify")
01476             strLayout += "\\qj";
01477         else if ( layout.alignment=="auto")
01478         {
01479             // ### TODO: what for BIDI?
01480             //strLayout += "\\ql";
01481         }
01482         else
01483         {
01484             kdWarning(30515) << "Unknown alignment: " << layout.alignment << endl;
01485         }
01486     }
01487 
01488     if ((layout.indentLeft>=0.0)
01489         && (force || (layoutOrigin.indentLeft!=layout.indentLeft)))
01490     {
01491        strLayout += "\\li";
01492        strLayout += QString::number(int(layout.indentLeft)*20, 10);
01493     }
01494 
01495     if ((layout.indentRight>=0.0)
01496         && (force || (layoutOrigin.indentRight!=layout.indentRight)))
01497     {
01498        strLayout += "\\ri";
01499        strLayout += QString::number(int(layout.indentRight)*20, 10);
01500     }
01501 
01502     if (force || (layoutOrigin.indentFirst!=layout.indentFirst))
01503     {
01504        strLayout += "\\fi";
01505        strLayout += QString::number(int(layout.indentFirst)*20, 10);
01506     }
01507 
01508     if ((layout.marginBottom>=0.0)
01509         && (force || (layoutOrigin.marginBottom!=layout.marginBottom)))
01510     {
01511        strLayout += "\\sa";
01512        strLayout += QString::number(int(layout.marginBottom)*20 ,10);
01513     }
01514 
01515     if ((layout.marginTop>=0.0)
01516         && (force || (layoutOrigin.marginTop!=layout.marginTop)))
01517     {
01518        strLayout += "\\sb";
01519        strLayout += QString::number(int(layout.marginTop)*20, 10);
01520     }
01521 
01522     if (force || (layoutOrigin.keepLinesTogether!=layout.keepLinesTogether))
01523     {
01524        if(layout.keepLinesTogether) strLayout += "\\keep";
01525     }
01526 
01527     // Note: there seems to be too many problems of using a page break in a layout
01528     // - KWord's RTF import filter makes the page break immediately (also in styles)
01529     // - AbiWord's RTF import does not like \*\pgbrk
01530     // ### TODO: decide if we really remove this code
01531 #if 0
01532     if (force || (layoutOrigin.pageBreakBefore!=layout.pageBreakBefore))
01533     {
01534        if(layout.pageBreakBefore) strLayout += "\\pagebb";
01535     }
01536 
01537     // Note: RTF doesn't specify "page break after"
01538     // \*\pgbrk0 is used after OpenOffice.org Writer
01539     if (force || (layoutOrigin.pageBreakAfter!=layout.pageBreakAfter))
01540     {
01541        if(layout.pageBreakAfter) strLayout += "\\*\\pgbrk0";
01542     }
01543 #endif
01544 
01545     if (force
01546         || ( layoutOrigin.lineSpacingType != layout.lineSpacingType )
01547         || ( layoutOrigin.lineSpacing != layout.lineSpacing ) )
01548     {
01549         if ( layout.lineSpacingType==LayoutData::LS_SINGLE  )
01550            ;// do nothing, single linespace is default in RTF
01551 
01552         else if ( layout.lineSpacingType==LayoutData::LS_ONEANDHALF  )
01553            strLayout += "\\sl360\\slmult1"; // one-and-half linespace
01554 
01555         else if ( layout.lineSpacingType==LayoutData::LS_DOUBLE  )
01556            strLayout += "\\sl480\\slmult1"; // double linespace
01557 
01558         else if ( layout.lineSpacingType==LayoutData::LS_ATLEAST  )
01559            strLayout += QString("\\sl%1\\slmult0").arg(int(layout.lineSpacing)*20);
01560 
01561         else if ( layout.lineSpacingType==LayoutData::LS_MULTIPLE  )
01562            strLayout += QString("\\sl%1\\slmult1").arg( int(layout.lineSpacing)*240 );
01563 
01564         else if ( layout.lineSpacingType==LayoutData::LS_CUSTOM )
01565            // "Custom" in KWord is like "Exactly" in MS Word
01566            strLayout += QString("\\sl-%1\\slmult0").arg(int(layout.lineSpacing)*20);
01567 
01568         else
01569             kdWarning(30515) << "Unsupported lineSpacingType: " << layout.lineSpacingType << " (Ignoring!)" << endl;
01570     }
01571 
01572     if (!layout.tabulatorList.isEmpty()
01573         && (force || (layoutOrigin.tabulatorList!=layout.tabulatorList) ))
01574     {
01575         TabulatorList::ConstIterator it;
01576         for (it=layout.tabulatorList.begin();it!=layout.tabulatorList.end();++it)
01577         {
01578             switch ((*it).m_type)
01579             {
01580                 case 0: default:  break; // left tab is default
01581                 case 1:  strLayout += "\\tqc"; break;
01582                 case 2:  strLayout += "\\tqr"; break;
01583                 case 3:  strLayout += "\\tqdec"; break;
01584             }
01585 
01586             switch ((*it).m_filling)
01587             {
01588                 case TabulatorData::TF_NONE: default: break; // without leader/filling
01589                 case TabulatorData::TF_DOT:  strLayout += "\\tldot"; break;
01590                 case TabulatorData::TF_LINE:  strLayout += "\\tlul"; break;
01591 
01592                 // these belows are all treated as RTF's \tqul
01593                 case TabulatorData::TF_DASH:
01594                 case TabulatorData::TF_DASHDOT:
01595                 case TabulatorData::TF_DASHDOTDOT:
01596                     strLayout += "\\tlul"; break;
01597             }
01598 
01599             // must be the last
01600             strLayout += "\\tx";
01601             strLayout += QString::number(int((*it).m_ptpos)*20, 10);
01602 
01603         }
01604     }
01605 
01606     // shadow support
01607     // note shadow in KWord is more full-feature/sophisticated than RTF
01608     // here we just treat KWord's shadow as simple \shad mark-up
01609     if( layout.shadowDistance > 0 )
01610     {
01611        strLayout += "\\shad";
01612     }
01613 
01614     // TODO: borders
01615 
01616     // This must remain last, as it adds a terminating space.
01617     strLayout+=textFormatToRtf(layoutOrigin.formatData.text,
01618         layout.formatData.text,force);
01619 
01620     return strLayout;
01621 }
01622 
01623 
01624 QString RTFWorker::lookupFont(const QString& markup, const QString& fontName)
01625 {
01626     if (fontName.isEmpty())
01627         return QString::null;
01628 
01629     // First we have to remove Qt-typical foundry names, as some RTF readers are confused by them.
01630     QString cookedFontName(fontName);
01631     QRegExp regexp("\\s*\\[\\S*\\]"); // Some white space, opening square bracket, some non-white-space, ending square bracket
01632     cookedFontName.remove(regexp);
01633     // But we cannot have an empty name font
01634     if (cookedFontName.isEmpty())
01635         cookedFontName=fontName;
01636 
01637     kdDebug(30515) << "RTFWorker::lookupFont " << fontName << " cooked: " << cookedFontName << endl;
01638 
01639     uint counter=0;  // counts position in font table (starts at 0)
01640     QString strFont(markup); // markup for font selection
01641     QStringList::ConstIterator it;
01642 
01643     // search font table for this font
01644     for( it = m_fontList.begin(); it != m_fontList.end(); counter++, ++it )
01645     {
01646         if((*it) == cookedFontName)  // check for match
01647         {
01648             strFont += QString::number(counter);
01649             kdDebug(30515) << strFont << endl;
01650             return strFont;
01651         }
01652     }  // end for()
01653 
01654     kdDebug(30515) << "New font: " << cookedFontName << " count: " << counter << endl;
01655     m_fontList << cookedFontName;
01656 
01657     strFont += QString::number(counter);
01658     return strFont;
01659 }
01660 
01661 QString RTFWorker::lookupColor(const QString& markup, const QColor& color)
01662 {
01663     if (!color.isValid())
01664         return QString::null;
01665 
01666     uint counter=1;  // counts position in color table starting at 1
01667     QString strColor(markup);  // Holds RTF markup for the color
01668 
01669     QValueList < QColor > ::ConstIterator it;
01670 
01671     // search color table for this color
01672     for( it =  m_colorList.begin(); it != m_colorList.end(); counter++, ++it )
01673     {
01674         if ( (*it) == color )
01675         {
01676             strColor += QString::number(counter);
01677             return strColor;
01678         }
01679     }
01680 
01681     kdDebug(30515) << "New color: " << color.name() << " count: " << counter << endl;
01682     m_colorList << color;
01683 
01684     strColor += QString::number(counter);
01685     return strColor;
01686 }
01687 
01688 QString RTFWorker::lookupStyle(const QString& styleName, LayoutData& returnLayout)
01689 {
01690     if (styleName.isEmpty())
01691         return QString::null;
01692 
01693     uint counter=0;  // counts position in style table starting at 0
01694     QString strMarkup("\\s");  // Holds RTF markup for the style
01695 
01696     QValueList < LayoutData > ::ConstIterator it;
01697     QValueList < LayoutData > ::ConstIterator end(m_styleList.end());
01698 
01699     // search color table for this color
01700     for( it =  m_styleList.begin(); it != end; counter++, ++it )
01701     {
01702         if ( (*it).styleName == styleName )
01703         {
01704             strMarkup += QString::number(counter);
01705             returnLayout=(*it);
01706             return strMarkup;
01707         }
01708     }
01709 
01710     kdDebug(30515) << "New style: " << styleName << " count: " << counter << endl;
01711     LayoutData layout;
01712     m_styleList << layout;
01713     returnLayout=layout;
01714 
01715     strMarkup += QString::number(counter);
01716     return strMarkup;
01717 }
KDE Home | KDE Accessibility Home | Description of Access Keys