filters

rtfimport.cpp

00001 // kate: space-indent on; indent-width 4; replace-tabs off;
00002 /*
00003    This file is part of the KDE project
00004    Copyright (C) 2001 Ewald Snel <ewald@rambo.its.tudelft.nl>
00005    Copyright (C) 2001 Tomasz Grobelny <grotk@poczta.onet.pl>
00006    Copyright (C) 2003, 2004 Nicolas GOUTTE <goutte@kde.org>
00007 
00008    This library is free software; you can redistribute it and/or
00009    modify it under the terms of the GNU Library General Public
00010    License as published by the Free Software Foundation; either
00011    version 2 of the License, or (at your option) any later version.
00012 */
00013 
00014 // ### FIXME: copyright holders/date
00015 
00016 #include <kdebug.h>
00017 #include <qfontinfo.h>
00018 #include <stddef.h>
00019 #include <string.h>
00020 #include <KoFilterChain.h>
00021 #include <kgenericfactory.h>
00022 
00023 #include <qcstring.h>
00024 #include <qstringlist.h>
00025 #include <qdir.h>
00026 #include <qfileinfo.h>
00027 #include <qregexp.h>
00028 #include <qvaluelist.h>
00029 
00030 #include <kurl.h>
00031 #include <kmessagebox.h>
00032 
00033 #include <KoPicture.h>
00034 #include <KoFilterManager.h>
00035 
00036 #include "rtfimport.h"
00037 #include "rtfimport.moc"
00038 
00039 typedef KGenericFactory<RTFImport, KoFilter> RTFImportFactory;
00040 K_EXPORT_COMPONENT_FACTORY( librtfimport, RTFImportFactory( "kofficefilters" ) )
00041 
00042 // defines a property
00043 #define PROP(a,b,c,d,e)     { a, b, &RTFImport::c, d, e }
00044 
00045 // defines a member variable of RTFImport as a property (DEPRECATED)
00046 #define MEMBER(a,b,c,d,e)   PROP(a,b,c,offsetof(RTFImport,d),e)
00047 
00048 static RTFProperty destinationPropertyTable[] =
00049     {
00050 //      only-valid-in   control word    function        offset, value
00051     PROP(   0L,     "@*",       skipGroup,      0L, false ),
00052     MEMBER( "@info",    "@author",  parsePlainText,     author, false ),
00053     PROP(   "@pict",    "@blipuid", parseBlipUid,   0, 0 ),
00054     PROP(   "@rtf",     "@colortbl",    parseColorTable,    0L, true ),
00055     MEMBER( "@info",    "@company", parsePlainText,     company, false ),
00056     MEMBER( "@info",    "@doccomm", parsePlainText,     doccomm, false ),
00057     PROP(   "Text",     "@field",   parseField,     0L, false ),
00058     PROP(   "@field",   "@fldinst", parseFldinst,       0L, false ),
00059     PROP(   "@field",   "@fldrslt", parseFldrslt,       0L, false ),
00060     PROP(   "@rtf",     "@fonttbl", parseFontTable,     0L, true ),
00061     MEMBER( "@rtf",     "@footer",  parseRichText,      oddPagesFooter, true ),
00062     PROP(   "@rtf",     "@footnote",    parseFootNote,      0L, true ),
00063     MEMBER( "@rtf",     "@footerf", parseRichText,      firstPageFooter, true ),
00064     MEMBER( "@rtf",     "@footerl", parseRichText,      oddPagesFooter, true ),
00065     MEMBER( "@rtf",     "@footerr", parseRichText,      evenPagesFooter, true ),
00066     MEMBER( "@rtf",     "@header",  parseRichText,      oddPagesHeader, true ),
00067     MEMBER( "@rtf",     "@headerf", parseRichText,      firstPageHeader, true ),
00068     MEMBER( "@rtf",     "@headerl", parseRichText,      oddPagesHeader, true ),
00069     MEMBER( "@rtf",     "@headerr", parseRichText,      evenPagesHeader, true ),
00070     PROP(   "@rtf",     "@info",    parseGroup,     0L, true ),
00071     PROP(   "Text",     "@nonshppict",  skipGroup,      0L, false ),
00072     PROP(   0L,     "@panose",      skipGroup,  0L, 0 ), // Not supported
00073     PROP(   "Text",     "@pict",    parsePicture,       0L, true ),
00074     MEMBER( "@",        "@rtf",     parseRichText,      bodyText, true ),
00075     PROP(   "Text",     "@shpinst", skipGroup,      0L, true ),
00076     PROP(   "Text",     "@shppict", parseGroup,     0L, false ),
00077     PROP(   "@rtf",     "@stylesheet",  parseStyleSheet,    0L, true ),
00078     MEMBER( "@info",    "@title",   parsePlainText,     title, false ),
00079 };
00080 
00081 static RTFProperty propertyTable[] =
00082     // Alphabetical order
00083     {
00084 //      only-valid-in   control word    function        offset, value
00085     PROP(   "Text",     "\n",       insertParagraph,    0L, 0 ),
00086     PROP(   "Text",     "\r",       insertParagraph,    0L, 0 ),
00087     PROP(   0L,     "\'",       insertHexSymbol,    0L, 0 ),
00088     PROP(   0L,     "\\",       insertSymbol,       0L, '\\' ),
00089     PROP(   0L,     "_",        insertSymbol,       0L, 0x2011 ),
00090     PROP(   0L,     "{",        insertSymbol,       0L, '{' ),
00091     PROP(   0L,     "|",        insertSymbol,       0L, 0x00b7 ),
00092     PROP(   0L,     "}",        insertSymbol,       0L, '}' ),
00093     PROP(   0L,     "~",        insertSymbol,       0L, 0x00a0 ),
00094     PROP(   0L,     "-",        insertSymbol,       0L, 0x00ad ),
00095     PROP(   0L,     "adjustright",  ignoreKeyword,      0L, 0 ), // Not supported, KWord has no grid
00096     PROP(   0L,     "ansi", setAnsiCodepage,        0L, 0 ),
00097     PROP(   0L,     "ansicpg",  setCodepage,        0L, 0 ),
00098     MEMBER( 0L,     "b",        setToggleProperty,  state.format.bold, 0 ),
00099         // \bin is handled in the tokenizer
00100     MEMBER( "@colortbl",    "blue",     setNumericProperty, blue, 0 ),
00101     MEMBER( 0L,     "box",      setEnumProperty,    state.layout.border, 0 ),
00102     PROP(   0L,     "brdrb",    selectLayoutBorder, 0L, 3 ),
00103     PROP(   0L,     "brdrcf",   setBorderColor,           0L, 0 ),
00104     PROP(   0L,     "brdrdash", setBorderStyle,     0L, RTFBorder::Dashes ),
00105     PROP(   0L,     "brdrdashd",    setBorderStyle,     0L, RTFBorder::DashDot ),
00106     PROP(   0L,     "brdrdashdd",   setBorderStyle,     0L, RTFBorder::DashDotDot ),
00107     PROP(   0L,     "brdrdashsm",   setBorderStyle,     0L, RTFBorder::Dashes ),
00108     PROP(   0L,     "brdrdb",   setBorderStyle,     0L, RTFBorder::Solid ),
00109     PROP(   0L,     "brdrdot",  setBorderStyle,     0L, RTFBorder::Dots ),
00110     PROP(   0L,     "brdrhairline", setBorderStyle,     0L, RTFBorder::Solid ),
00111     PROP(   0L,     "brdrl",    selectLayoutBorder, 0L, 0 ),
00112     PROP(   0L,     "brdrr",    selectLayoutBorder, 0L, 1 ),
00113     PROP(   0L,     "brdrs",    setBorderStyle,     0L, RTFBorder::Solid ),
00114     PROP(   0L,     "brdrsh",   setBorderStyle,     0L, RTFBorder::Solid ),
00115     PROP(   0L,     "brdrt",    selectLayoutBorder, 0L, 2 ),
00116     PROP(   0L,     "brdrth",   setBorderStyle,     0L, RTFBorder::Solid ),
00117     PROP(   0L,     "brdrw",    setBorderProperty,  offsetof(RTFBorder,width), 0 ),
00118     PROP(   0L,     "bullet",   insertSymbol,       0L, 0x2022 ),
00119     PROP(   0L,     "brsp",     setBorderProperty,  offsetof(RTFBorder,space), 0 ),
00120     MEMBER( 0L,     "caps",     setToggleProperty,  state.format.caps, 0 ),
00121     MEMBER( 0L,     "cb",       setNumericProperty, state.format.bgcolor, 0 ),
00122     MEMBER( 0L,     "highlight",    setNumericProperty, state.format.bgcolor, 0 ),
00123     PROP(   "Text",     "cell",     insertTableCell,    0L, 0 ),
00124     PROP(   0L,     "cellx",    insertCellDef,      0L, 0 ),
00125     MEMBER( 0L,     "cf",       setNumericProperty, state.format.color, 0 ),
00126     PROP(   0L,     "chdate",   insertDateTime,     0L, TRUE ),
00127     PROP(   0L,     "chpgn",    insertPageNumber,       0L, 0 ),
00128     PROP(   0L,     "chtime",   insertDateTime,     0L, FALSE ),
00129     PROP(   0L,     "clbrdrb",  selectLayoutBorderFromCell, 0L, 3 ),
00130     PROP(   0L,     "clbrdrl",  selectLayoutBorderFromCell, 0L, 0 ),
00131     PROP(   0L,     "clbrdrr",  selectLayoutBorderFromCell, 0L, 1 ),
00132     PROP(   0L,     "clbrdrt",  selectLayoutBorderFromCell, 0L, 2 ),
00133     MEMBER( 0L,     "clcbpat",  setNumericProperty, state.tableCell.bgcolor, 0 ),
00134     PROP(   0L,     "cs",   ignoreKeyword,      0L, 0 ), // Not supported by KWord 1.3
00135     PROP(   0L,     "datafield",    skipGroup,      0L, 0 ), // Binary data in variables are not supported
00136     MEMBER( "@rtf",     "deff",     setNumericProperty, defaultFont, 0 ),
00137     MEMBER( "@rtf",     "deftab",   setNumericProperty, defaultTab, 0 ),
00138     PROP(   "@pict",    "dibitmap", setPictureType,         0L, RTFPicture::BMP ),
00139     MEMBER( 0L,     "dn",       setNumericProperty, state.format.baseline, 6 ),
00140     PROP(   0L,     "emdash",   insertSymbol,       0L, 0x2014 ),
00141     PROP(   "@pict",    "emfblip",  setPictureType,         0L, RTFPicture::EMF ),
00142     PROP(   0L,     "emspace",  insertSymbol,       0L, 0x2003 ),
00143     PROP(   0L,     "endash",   insertSymbol,       0L, 0x2013 ),
00144     PROP(   0L,     "enspace",  insertSymbol,       0L, 0x2002 ),
00145     PROP(   0L,     "expnd",    ignoreKeyword,      0L, 0 ), // Expansion/compression of character inter-space not supported
00146     PROP(   0L,     "expndtw",  ignoreKeyword,      0L, 0 ), // Expansion/compression of character inter-space not supported
00147     MEMBER( 0L,     "f",        setNumericProperty, state.format.font, 0 ),
00148     MEMBER( "@rtf",     "facingp",  setFlagProperty,    facingPages, true ),
00149     PROP(   0L,     "fcharset", setCharset,     0L, 0 ), // Not needed with Qt
00150     PROP(   "@fonttbl", "fdecor",   setFontStyleHint,   0L, QFont::Decorative ),
00151     MEMBER( 0L,     "fi",       setNumericProperty, state.layout.firstIndent, 0 ),
00152     PROP(   "@fonttbl", "fmodern",  setFontStyleHint,   0L, QFont::TypeWriter ),
00153     PROP(   "@fonttbl", "fnil",     setFontStyleHint,   0L, QFont::AnyStyle ),
00154     MEMBER( 0L,     "footery",  setNumericProperty, state.section.footerMargin, 0 ),
00155     PROP(   0L,     "formshade",    ignoreKeyword,      0L, 0 ), // Not supported, KWord has no form support
00156     MEMBER( "@fonttbl", "fprq",     setNumericProperty, font.fixedPitch, 0 ),
00157     PROP(   "@fonttbl", "froman",   setFontStyleHint,   0L, QFont::Serif ),
00158     MEMBER( 0L,     "fs",       setNumericProperty, state.format.fontSize, 0 ),
00159     PROP(   "@fonttbl", "fscript",  setFontStyleHint,   0L, QFont::AnyStyle ),
00160     PROP(   "@fonttbl", "fswiss",   setFontStyleHint,   0L, QFont::SansSerif ),
00161     PROP(   "@fonttbl", "ftech",    setFontStyleHint,   0L, QFont::AnyStyle ),
00162     MEMBER( "@colortbl",    "green",    setNumericProperty, green, 0 ),
00163     MEMBER( 0L,     "headery",  setNumericProperty, state.section.headerMargin, 0 ),
00164     MEMBER( 0L,     "i",        setToggleProperty,  state.format.italic, 0 ),
00165     MEMBER( 0L,     "intbl",    setFlagProperty,    state.layout.inTable, true ),
00166     PROP(   "@pict",    "jpegblip", setPictureType,         0L, RTFPicture::JPEG ),
00167     MEMBER( 0L,     "keep",     setFlagProperty,    state.layout.keep, true ),
00168     MEMBER( 0L,     "keepn",    setFlagProperty,    state.layout.keepNext, true ),
00169     MEMBER( "@rtf",     "landscape",    setFlagProperty,    landscape, true ),
00170     PROP(   0L,     "ldblquote",    insertSymbol,       0L, 0x201c ),
00171     MEMBER( 0L,     "li",       setNumericProperty, state.layout.leftIndent, 0 ),
00172     PROP(   0L,     "line",     insertSymbol,       0L, 0x000a ),
00173     PROP(   0L,     "lquote",   insertSymbol,       0L, 0x2018 ),
00174     PROP(   0L,     "ltrmark",  insertSymbol,       0L, 0x200e ),
00175     PROP(   0L,     "mac",  setMacCodepage,     0L, 0 ),
00176     PROP(   "@pict",    "macpict",  setPictureType,         0L, RTFPicture::MacPict ),
00177     MEMBER( "@rtf",     "margb",    setNumericProperty, bottomMargin, 0 ),
00178     MEMBER( "@rtf",     "margl",    setNumericProperty, leftMargin, 0 ),
00179     MEMBER( "@rtf",     "margr",    setNumericProperty, rightMargin, 0 ),
00180     MEMBER( "@rtf",     "margt",    setNumericProperty, topMargin, 0 ),
00181     MEMBER( 0L,     "nosupersub",   setEnumProperty,    state.format.vertAlign, RTFFormat::Normal ),
00182     PROP(   "Text",     "page",     insertPageBreak,    0L, 0 ),
00183     MEMBER( 0L,     "pagebb",   setFlagProperty,    state.layout.pageBB, true ),
00184     MEMBER( "@rtf",     "paperh",   setNumericProperty, paperHeight, 0 ),
00185     MEMBER( "@rtf",     "paperw",   setNumericProperty, paperWidth, 0 ),
00186     PROP(   "Text",     "par",      insertParagraph,    0L, 0 ),
00187     PROP(   0L,     "pard",     setParagraphDefaults,   0L, 0 ),
00188     PROP(   0L,     "pc",   setPcCodepage,      0L, 0 ),
00189     PROP(   0L,     "pca",  setPcaCodepage,     0L, 0 ),
00190     MEMBER( 0L,     "pgbrk",    setToggleProperty,  state.layout.pageBA, true ),
00191     MEMBER( "@pict",    "piccropb", setNumericProperty, picture.cropBottom, 0 ),
00192     MEMBER( "@pict",    "piccropl", setNumericProperty, picture.cropLeft, 0 ),
00193     MEMBER( "@pict",    "piccropr", setNumericProperty, picture.cropRight, 0 ),
00194     MEMBER( "@pict",    "piccropt", setNumericProperty, picture.cropTop, 0 ),
00195     MEMBER( "@pict",    "pich",     setNumericProperty, picture.height, 0 ),
00196     MEMBER( "@pict",    "pichgoal", setNumericProperty, picture.desiredHeight, 0 ),
00197     MEMBER( "@pict",    "picscaled",    setFlagProperty,    picture.scaled, true ),
00198     MEMBER( "@pict",    "picscalex",    setNumericProperty, picture.scalex, 0 ),
00199     MEMBER( "@pict",    "picscaley",    setNumericProperty, picture.scaley, 0 ),
00200     MEMBER( "@pict",    "picw",     setNumericProperty, picture.width, 0 ),
00201     MEMBER( "@pict",    "picwgoal", setNumericProperty, picture.desiredWidth, 0 ),
00202     PROP(   0L,     "plain",    setPlainFormatting, 0L, 0 ),
00203     PROP(   "@pict",    "pmmetafile",   setPictureType,         0L, RTFPicture::WMF ),
00204     PROP(   "@pict",    "pngblip",  setPictureType,         0L, RTFPicture::PNG ),
00205     MEMBER( 0L,     "qc",       setEnumProperty,    state.layout.alignment, RTFLayout::Centered ),
00206     MEMBER( 0L,     "qj",       setEnumProperty,    state.layout.alignment, RTFLayout::Justified ),
00207     MEMBER( 0L,     "ql",       setEnumProperty,    state.layout.alignment, RTFLayout::Left ),
00208     PROP(   0L,     "qmspace",  insertSymbol,       0L, 0x2004 ),
00209     MEMBER( 0L,     "qr",       setEnumProperty,    state.layout.alignment, RTFLayout::Right ),
00210     PROP(   0L,     "rdblquote",    insertSymbol,       0L, 0x201d ),
00211     MEMBER( "@colortbl",    "red",      setNumericProperty, red, 0 ),
00212     MEMBER( 0L,     "ri",       setNumericProperty, state.layout.rightIndent, 0 ),
00213     PROP(   "Text",     "row",      insertTableRow,     0L, 0 ),
00214     PROP(   0L,     "rquote",   insertSymbol,       0L, 0x2019 ),
00215     PROP(   0L,     "rtlmark",  insertSymbol,       0L, 0x200f ),
00216     MEMBER( 0L,     "s",        setNumericProperty, state.layout.style, 0 ),
00217     MEMBER( 0L,     "sa",       setNumericProperty, state.layout.spaceAfter, 0 ),
00218     MEMBER( 0L,     "sb",       setNumericProperty, state.layout.spaceBefore, 0 ),
00219     MEMBER( 0L,     "scaps",        setToggleProperty,  state.format.smallCaps, 0 ),
00220     PROP(   "Text",     "sect",     insertPageBreak,    0L, 0 ),
00221     PROP(   0L,     "sectd",    setSectionDefaults, 0L, 0 ),
00222     MEMBER( 0L,     "sl",       setNumericProperty, state.layout.spaceBetween, 0 ),
00223     MEMBER( 0L,     "slmult",   setToggleProperty,  state.layout.spaceBetweenMultiple, 0 ),
00224     MEMBER( "@stylesheet",  "snext",    setNumericProperty, style.next, 0 ),
00225     MEMBER( 0L,     "strike",   setToggleProperty,  state.format.strike, 0 ),
00226     MEMBER( 0L,     "striked",  setToggleProperty,  state.format.striked, 0 ),
00227     MEMBER( 0L,     "sub",      setEnumProperty,    state.format.vertAlign, RTFFormat::SubScript ),
00228     MEMBER( 0L,     "super",    setEnumProperty,    state.format.vertAlign, RTFFormat::SuperScript ),
00229     PROP(   0L,     "tab",      insertSymbol,       0L, 0x0009 ),
00230     MEMBER( 0L,     "titlepg",  setFlagProperty,    state.section.titlePage, true ),
00231     MEMBER( 0L,     "tldot",    setEnumProperty,    state.layout.tab.leader, RTFTab::Dots ),
00232     MEMBER( 0L,     "tlhyph",   setEnumProperty,    state.layout.tab.leader, RTFTab::Hyphens ),
00233     MEMBER( 0L,     "tlth",     setEnumProperty,    state.layout.tab.leader, RTFTab::ThickLine ),
00234     MEMBER( 0L,     "tlul",     setEnumProperty,    state.layout.tab.leader, RTFTab::Underline ),
00235     MEMBER( 0L,     "tqc",      setEnumProperty,    state.layout.tab.type, RTFTab::Centered ),
00236     MEMBER( 0L,     "tqdec",    setEnumProperty,    state.layout.tab.type, RTFTab::Decimal ),
00237     MEMBER( 0L,     "tqr",      setEnumProperty,    state.layout.tab.type, RTFTab::FlushRight ),
00238     MEMBER( 0L,     "trleft",   setNumericProperty, state.tableRow.left, 0 ),
00239     MEMBER( 0L,     "trowd",    setTableRowDefaults,    state.tableRow, 0 ),
00240     MEMBER( 0L,     "trqc",     setEnumProperty,    state.tableRow.alignment, RTFLayout::Centered ),
00241     MEMBER( 0L,     "trql",     setEnumProperty,    state.tableRow.alignment, RTFLayout::Left ),
00242     MEMBER( 0L,     "trqr",     setEnumProperty,    state.tableRow.alignment, RTFLayout::Right ),
00243     MEMBER( 0L,     "trrh",     setNumericProperty, state.tableRow.height, 0 ),
00244     PROP(   0L,     "tx",       insertTabDef,       0L, 0 ),
00245     MEMBER( 0L,     "u",        insertUnicodeSymbol,    state.format.uc, 0 ),
00246     MEMBER( 0L,     "uc",       setNumericProperty, state.format.uc, 0 ),
00247     PROP(   0L,     "ul",       setSimpleUnderlineProperty, 0L, 0 ),
00248     MEMBER( 0L,     "ulc",      setNumericProperty, state.format.underlinecolor, 0 ),
00249     PROP(   0L,     "uld",      setUnderlineProperty,   0L, RTFFormat::UnderlineDot ),
00250     PROP(   0L,     "uldash",   setUnderlineProperty,   0L, RTFFormat::UnderlineDash ),
00251     PROP(   0L,     "uldashd",  setUnderlineProperty,   0L, RTFFormat::UnderlineDashDot ),
00252     PROP(   0L,     "uldashdd", setUnderlineProperty,   0L, RTFFormat::UnderlineDashDotDot ),
00253     PROP(   0L,     "uldb",     setUnderlineProperty,   0L, RTFFormat::UnderlineDouble ),
00254     PROP(   0L,     "ulnone",   setUnderlineProperty,   0L, RTFFormat::UnderlineNone ),
00255     PROP(   0L,     "ulth",     setUnderlineProperty,   0L, RTFFormat::UnderlineThick ),
00256     PROP(   0L,     "ulw",      setUnderlineProperty,   0L, RTFFormat::UnderlineWordByWord ),
00257     PROP(   0L,     "ulwave",   setUnderlineProperty,   0L, RTFFormat::UnderlineWave ),
00258     PROP(   0L,     "ulhwave",  setUnderlineProperty,   0L, RTFFormat::UnderlineWave ),
00259     PROP(   0L,     "ululdbwave",   setUnderlineProperty,   0L, RTFFormat::UnderlineWave ),
00260     MEMBER( 0L,     "up",       setUpProperty,      state.format.baseline, 6 ),
00261     MEMBER( 0L,     "v",        setToggleProperty,  state.format.hidden, 0 ),
00262         // ### TODO: \wbitmap: a Windows Device-Dependant Bitmap is not a BMP
00263     PROP(   "@pict",    "wbitmap",  setPictureType,         0L, RTFPicture::BMP ),
00264     PROP(   "@pict",    "wmetafile",    setPictureType,         0L, RTFPicture::EMF ),
00265     PROP(   0L,     "zwj",      insertSymbol,       0L, 0x200d ),
00266     PROP(   0L,     "zwnj",     insertSymbol,       0L, 0x200c )
00267 };
00268 
00269 static RTFField fieldTable[] =
00270     {
00271 //    id         type  subtype  default value
00272     { "AUTHOR",  8,    2,   "NO AUTHOR" },
00273     { "FILENAME",    8,    0,   "NO FILENAME" },
00274     { "TITLE",   8,   10,   "NO TITLE" },
00275     { "NUMPAGES",    4,    1,   0 },
00276     { "PAGE",    4,    0,   0 },
00277     { "TIME",   -1,   -1,   0 },
00278     { "DATE",   -1,   -1,   0 },
00279     { "HYPERLINK",   9,   -1,   0 },
00280     { "SYMBOL", -1,   -1,   0 },
00281     { "IMPORT", -1,   -1,   0 }
00282 };
00283 
00284 
00285 // KWord attributes
00286 static const char *alignN[4]    = { "left", "right", "justify", "center" };
00287 static const char *boolN[2] = { "false", "true" };
00288 static const char *borderN[4]   = { "LEFTBORDER", "RIGHTBORDER", "TOPBORDER", "BOTTOMBORDER" };
00289 
00290 RTFImport::RTFImport( KoFilter *, const char *, const QStringList& )
00291     : KoFilter(), properties(181), destinationProperties(29), textCodec(0), utf8TextCodec(0)
00292 {
00293     for (uint i=0; i < sizeof(propertyTable) / sizeof(propertyTable[0]); i++)
00294     {
00295         properties.insert( propertyTable[i].name, &propertyTable[i] );
00296     }
00297     for (uint i=0; i < sizeof(destinationPropertyTable) / sizeof(destinationPropertyTable[0]); i++)
00298     {
00299         destinationProperties.insert( destinationPropertyTable[i].name, &destinationPropertyTable[i] );
00300     }
00301     // DEBUG START
00302     // Check the hash size (see QDict doc)
00303     kdDebug(30515) << properties.count() << " normal and " << destinationProperties.count() << " destination keywords loaded" << endl;
00304     if (properties.size() < properties.count())
00305         kdWarning(30515) << "Hash size of properties too small: " << properties.size() << ". It should be at least " << properties.count() << " and be a prime number"<< endl;
00306     if (destinationProperties.size() < destinationProperties.count())
00307         kdWarning(30515) << "Hash size of destinationProperties too small: " << destinationProperties.size() << ". It should be at least " << destinationProperties.count() << " and be a prime number"<< endl;
00308     // DEBUG END
00309     fnnum=0;
00310 }
00311 
00312 KoFilter::ConversionStatus RTFImport::convert( const QCString& from, const QCString& to )
00313 {
00314     // This filter only supports RTF to KWord conversion
00315     if ((from != "text/rtf") || (to != "application/x-kword"))
00316         return KoFilter::NotImplemented;
00317 
00318     QTime debugTime;
00319     debugTime.start();
00320 
00321     // Are we in batch mode, i.e. non-interactive
00322     m_batch=false;
00323     if ( m_chain->manager() )
00324         m_batch = m_chain->manager()->getBatchMode();
00325 
00326     // Open input file
00327     inFileName = m_chain->inputFile();
00328     QFile in( inFileName );
00329 
00330     if (!in.open( IO_ReadOnly ))
00331     {
00332     kdError(30515) << "Unable to open input file!" << endl;
00333     in.close();
00334         if ( !m_batch )
00335         {
00336             KMessageBox::error( 0L,
00337                 i18n("The file cannot be loaded, as it cannot be opened."),
00338                 i18n("KWord's RTF Import Filter"), 0 );
00339         }
00340     return KoFilter::FileNotFound;
00341     }
00342 
00343     // Document should start with an opening brace
00344     token.open( &in );
00345     token.next();
00346 
00347     if (token.type != RTFTokenizer::OpenGroup)
00348     {
00349     kdError(30515) << "Not an RTF file" << endl;
00350     in.close();
00351         if ( !m_batch )
00352         {
00353             KMessageBox::error( 0L,
00354                 i18n("The file cannot be loaded, as it seems not to be an RTF document."),
00355                 i18n("KWord's RTF Import Filter"), 0 );
00356         }
00357     return KoFilter::WrongFormat;
00358     }
00359 
00360     // Verify document type and version (RTF version 1.x)
00361 
00362     token.next();
00363 
00364     if (token.type != RTFTokenizer::ControlWord)
00365     {
00366     kdError(30515) << "Wrong document type" << endl;
00367     in.close();
00368         if ( !m_batch )
00369         {
00370             KMessageBox::error( 0L,
00371                 i18n("The document cannot be loaded, as it seems not to follow the RTF syntax."),
00372                 i18n("KWord's RTF Import Filter"), 0 );
00373         }
00374     return KoFilter::WrongFormat;
00375     }
00376 
00377     bool force = false; // By default do not force, despite an unknown keyword or version
00378     if ( !qstrcmp( token.text, "rtf" ) )
00379     {
00380         // RTF is normally version 1 but at least Ted uses 0 as version number
00381         if ( token.value > 1 )
00382         {
00383             kdError(30515) << "Wrong RTF version (" << token.value << "); version 0 or 1 expected" << endl;
00384             if ( !m_batch )
00385             {
00386                 force = ( KMessageBox::warningYesNo( 0L,
00387                     i18n("The RTF (Rich Text Format) document has an unexpected version number: %1. Continuing might result in an erroneous conversion. Do you want to continue?").arg( token.value ),
00388                     i18n("KWord's RTF Import Filter") ) == KMessageBox::Yes );
00389             }
00390             if ( !force )
00391             {
00392                 in.close();
00393                 return KoFilter::WrongFormat;
00394             }
00395         }
00396     }
00397     else if ( !qstrcmp( token.text, "pwd" ) )
00398     {
00399         // PocketWord's PWD format is similar to RTF but has a version number of 2.
00400         if ( token.value != 2 )
00401         {
00402             kdError(30515) << "Wrong PWD version (" << token.value << "); version 2 expected" << endl;
00403             if ( !m_batch )
00404             {
00405                 force = ( KMessageBox::warningYesNo( 0L,
00406                     i18n("The PWD (PocketWord's Rich Text Format) document has an unexpected version number: %1. Continuing might result in an erroneous conversion. Do you want to continue?").arg( token.value ),
00407                     i18n("KWord's RTF Import Filter") ) == KMessageBox::Yes );
00408             }
00409             if ( !force )
00410             {
00411                 in.close();
00412                 return KoFilter::WrongFormat;
00413             }
00414         }
00415     }
00416     else if ( !qstrcmp( token.text, "urtf" ) )
00417     {
00418         // URTF seems to have either no version or having version 1
00419         if ( token.value > 1 )
00420         {
00421             kdError(30515) << "Wrong URTF version (" << token.value << "); version 0 or 1 expected" << endl;
00422             if ( !m_batch )
00423             {
00424                 force = ( KMessageBox::warningYesNo( 0L,
00425                     i18n("The URTF (\"Unicode Rich Text Format\") document has an unexpected version number: %1. Continuing might result in an erroneous conversion. Do you want to continue?").arg( token.value ),
00426                     i18n("KWord's RTF Import Filter") ) == KMessageBox::Yes );
00427             }
00428             if ( !force )
00429             {
00430                 in.close();
00431                 return KoFilter::WrongFormat;
00432             }
00433         }
00434     }
00435     else
00436     {
00437         kdError(30515) << "Wrong RTF document type (\\" << token.text << "); \\rtf, \\pwd or \\urtf expected" << endl;
00438         in.close();
00439         if ( !m_batch )
00440         {
00441             KMessageBox::error( 0L,
00442                 i18n("The RTF document cannot be loaded, as it has an unexpected first keyword: \\%1.").arg( token.text ),
00443                 i18n("KWord's RTF Import Filter"), 0 );
00444         }
00445         return KoFilter::WrongFormat;
00446     }
00447 
00448     table   = 0;
00449     pictureNumber   = 0;
00450 
00451     // Document-formatting properties
00452     paperWidth  = 12240;
00453     paperHeight = 15840;
00454     leftMargin  = 1800;
00455     topMargin   = 1440;
00456     rightMargin = 1800;
00457     bottomMargin= 1440;
00458     defaultTab  = 720;
00459     defaultFont = 0;
00460     landscape   = false;
00461     facingPages = false;
00462 
00463     // Create main document
00464     frameSets.clear( 2 );
00465     pictures.clear();
00466     bodyText.node.clear( 3 );
00467     firstPageHeader.node.clear( 3 );
00468     oddPagesHeader.node.clear( 3 );
00469     evenPagesHeader.node.clear( 3 );
00470     firstPageFooter.node.clear( 3 );
00471     oddPagesFooter.node.clear( 3 );
00472     evenPagesFooter.node.clear( 3 );
00473     author.clear();
00474     company.clear();
00475     title.clear();
00476     doccomm.clear();
00477 
00478     stateStack.push( state );
00479 
00480     // Add a security item for the destination stack
00481     destination.name = "!stackbottom";
00482     
00483     changeDestination( destinationProperties["@rtf"] );
00484 
00485     flddst = -1;
00486     emptyCell = state.tableCell;
00487     state.format.uc=1;
00488     state.ignoreGroup = false;
00489 
00490     utf8TextCodec=QTextCodec::codecForName("UTF-8");
00491     kdDebug(30515) << "UTF-8 asked, given: " << (utf8TextCodec?utf8TextCodec->name():QString("-none-")) << endl;
00492 
00493     // There is no default encoding in RTF, it must be always declared. (But beware of buggy files!)
00494     textCodec=QTextCodec::codecForName("CP 1252"); // Or IBM 437 ?
00495     kdDebug(30515) << "CP 1252 asked, given: " << (textCodec?textCodec->name():QString("-none-")) << endl;
00496 
00497     // Parse RTF document
00498     while (true)
00499     {
00500     bool firstToken = false;
00501     bool ignoreUnknown = false;
00502 
00503     token.next();
00504 
00505     while (token.type == RTFTokenizer::OpenGroup)
00506     {
00507         // Store the current state on the stack
00508         stateStack.push( state );
00509         state.brace0 = false;
00510         firstToken = true;
00511         ignoreUnknown = false;
00512 
00513         token.next();
00514 
00515         if (token.type == RTFTokenizer::ControlWord && !qstrcmp( token.text, "*" ))
00516         {
00517         // {\*\control ...} destination
00518         ignoreUnknown = true;
00519         token.next();
00520         }
00521     }
00522     if (token.type == RTFTokenizer::CloseGroup)
00523     {
00524         if (state.brace0)
00525         {
00526         // Close the current destination
00527         (this->*destination.destproc)(0L);
00528                 //kdDebug(30515) << "Closing destination... " << destinationStack.count() << endl;
00529                 if (destinationStack.isEmpty())
00530                 {
00531                     kdWarning(30515) << "Destination stack is empty! Document might be buggy!" << endl;
00532                     // Keep the destination to save what can still be saved!
00533                 }
00534                 else
00535                 {
00536                     destination = destinationStack.pop();
00537                 }
00538         }
00539             // ### TODO: why can this not be simplified to use QValueList::isEmpty()
00540         if (stateStack.count() <= 1)
00541         {
00542         // End-of-document, keep formatting properties
00543         stateStack.pop();
00544         break;
00545         }
00546             else
00547         {
00548         // Retrieve the current state from the stack
00549         state = stateStack.pop();
00550         }
00551     }
00552     else if (token.type == RTFTokenizer::ControlWord)
00553     {
00554         RTFProperty *property = properties[token.text];
00555 
00556         if (property != 0L)
00557         {
00558         if (property->onlyValidIn == 0L ||
00559             property->onlyValidIn == destination.name ||
00560             property->onlyValidIn == destination.group)
00561         {
00562             (this->*property->cwproc)( property );
00563         }
00564         }
00565         else if (firstToken)
00566         {
00567         // Possible destination change
00568         *(--token.text) = '@';
00569         property = destinationProperties[token.text];
00570 
00571         if ((property != 0L) &&
00572             (property->onlyValidIn == 0L ||
00573              property->onlyValidIn == destination.name ||
00574              property->onlyValidIn == destination.group))
00575         {
00576             // Change destination
00577             changeDestination( property );
00578         }
00579         else if (ignoreUnknown)
00580         {
00581             // Skip unknown {\* ...} destination
00582             changeDestination( destinationProperties["@*"] );
00583             debugUnknownKeywords[token.text]++;
00584         }
00585                 else if ( !property )
00586                 {
00587                     kdWarning(30515) << "Unknown first non-ignorable token of a group: " << token.text << endl; kdDebug(30515) << "Destination: " << ( (void*) destination.name ) << " Destination stack depth: " << destinationStack.count() << endl;
00588                     // Put the second warning separately, as it can crash if destination.name is dangling
00589                     kdWarning(30515) << " Assuming destination: " << destination.name << endl;
00590                     debugUnknownKeywords[token.text]++;
00591                 }
00592         }
00593             else
00594             {
00595                 debugUnknownKeywords[token.text]++;
00596             }
00597     }
00598     else if (token.type == RTFTokenizer::PlainText || token.type == RTFTokenizer::BinaryData)
00599     {
00600         (this->*destination.destproc)(0L);
00601     }
00602     }
00603 
00604     // Determine header and footer type
00605     const int hType = facingPages
00606         ? (state.section.titlePage ? 3 : 1) : (state.section.titlePage ? 2 : 0);
00607 
00608     const bool hasHeader = !oddPagesHeader.node.isEmpty() ||
00609         (facingPages &&!evenPagesHeader.node.isEmpty()) ||
00610         (state.section.titlePage && !firstPageHeader.node.isEmpty());
00611     const bool hasFooter = !oddPagesFooter.node.isEmpty() ||
00612         (facingPages && !evenPagesFooter.node.isEmpty()) ||
00613         (state.section.titlePage && !firstPageFooter.node.isEmpty());
00614 
00615     kdDebug(30515) << "hType " << hType << " hasHeader " << hasHeader << " hasFooter " << hasFooter << endl;
00616 
00617     // Create main document
00618     DomNode mainDoc( "DOC" );
00619       mainDoc.setAttribute( "mime", "application/x-kword" );
00620       mainDoc.setAttribute( "syntaxVersion", "3" );
00621       mainDoc.setAttribute( "editor", "KWord's RTF Import Filter" );
00622       mainDoc.addNode( "PAPER" );
00623     mainDoc.setAttribute( "format", 6 );
00624     mainDoc.setAttribute( "columns", 1 );
00625     mainDoc.setAttribute( "columnspacing", 2 );
00626     mainDoc.setAttribute( "spHeadBody", 4 );
00627     mainDoc.setAttribute( "spFootBody", 4 );
00628     mainDoc.setAttribute( "zoom", 100 );
00629     mainDoc.setAttribute( "width", .05*paperWidth );
00630     mainDoc.setAttribute( "height", .05*paperHeight );
00631     mainDoc.setAttribute( "orientation", landscape );
00632     mainDoc.setAttribute( "hType", hType );
00633     mainDoc.setAttribute( "fType", hType );
00634     mainDoc.addNode( "PAPERBORDERS" );
00635       mainDoc.addRect( leftMargin,
00636            (hasHeader ? state.section.headerMargin : topMargin),
00637            rightMargin,
00638            (hasFooter ? state.section.footerMargin : bottomMargin) );
00639     mainDoc.closeNode( "PAPERBORDERS" );
00640       mainDoc.closeNode( "PAPER" );
00641       mainDoc.addNode( "ATTRIBUTES" );
00642     mainDoc.setAttribute( "standardpage", 1 );
00643     mainDoc.setAttribute( "processing", 0 );
00644     //mainDoc.setAttribute( "unit", "pt" ); // use KWord default instead
00645     mainDoc.setAttribute( "hasHeader", hasHeader );
00646     mainDoc.setAttribute( "hasFooter", hasFooter );
00647       mainDoc.closeNode( "ATTRIBUTES" );
00648       mainDoc.addNode( "FRAMESETS" );
00649     mainDoc.addFrameSet( "Frameset 1", 1, 0 );
00650       mainDoc.addFrame( leftMargin, topMargin, (paperWidth - rightMargin),
00651             (paperHeight - bottomMargin), 1, 0, 0 );
00652       mainDoc.closeNode( "FRAME" );
00653       mainDoc.appendNode( bodyText.node );
00654     mainDoc.closeNode( "FRAMESET" );
00655     // Write out headers
00656     if (hasHeader)
00657     {
00658         mainDoc.addFrameSet( "First Page Header", 1, 1 );
00659           mainDoc.addFrame( leftMargin, state.section.headerMargin,
00660             (paperWidth - rightMargin), (topMargin - 80), 0, 2, 0 );
00661           mainDoc.closeNode( "FRAME" );
00662           mainDoc.appendNode( firstPageHeader.node );
00663         mainDoc.closeNode( "FRAMESET" );
00664         mainDoc.addFrameSet( "Odd Pages Header", 1, 2 );
00665           mainDoc.addFrame( leftMargin, state.section.headerMargin,
00666             (paperWidth - rightMargin), (topMargin - 80), 0, 2, 1 );
00667           mainDoc.closeNode( "FRAME" );
00668           mainDoc.appendNode( oddPagesHeader.node );
00669         mainDoc.closeNode( "FRAMESET" );
00670         mainDoc.addFrameSet( "Even Pages Header", 1, 3 );
00671           mainDoc.addFrame( leftMargin, state.section.headerMargin,
00672             (paperWidth - rightMargin), (topMargin - 80), 0, 2, 2 );
00673           mainDoc.closeNode( "FRAME" );
00674           mainDoc.appendNode( evenPagesHeader.node );
00675         mainDoc.closeNode( "FRAMESET" );
00676     }
00677     // Write out footers
00678     if (hasFooter)
00679     {
00680         mainDoc.addFrameSet( "First Page Footer", 1, 4 );
00681           mainDoc.addFrame( leftMargin, state.section.headerMargin,
00682             (paperWidth - rightMargin), (topMargin - 80), 0, 2, 0 );
00683           mainDoc.closeNode( "FRAME" );
00684           mainDoc.appendNode( firstPageFooter.node );
00685         mainDoc.closeNode( "FRAMESET" );
00686         mainDoc.addFrameSet( "Odd Pages Footer", 1, 5 );
00687           mainDoc.addFrame( leftMargin, state.section.headerMargin,
00688             (paperWidth - rightMargin), (topMargin - 80), 0, 2, 1 );
00689           mainDoc.closeNode( "FRAME" );
00690           mainDoc.appendNode( oddPagesFooter.node );
00691         mainDoc.closeNode( "FRAMESET" );
00692         mainDoc.addFrameSet( "Even Pages Footer", 1, 6 );
00693           mainDoc.addFrame( leftMargin, state.section.headerMargin,
00694             (paperWidth - rightMargin), (topMargin - 80), 0, 2, 2 );
00695           mainDoc.closeNode( "FRAME" );
00696           mainDoc.appendNode( evenPagesFooter.node );
00697         mainDoc.closeNode( "FRAMESET" );
00698     }
00699     // Write out footnotes
00700     int num=1;
00701     for(RTFTextState* i=footnotes.first();i;i=footnotes.next())
00702     {
00703         QCString str;
00704         str.setNum(num);
00705         str.prepend("Footnote ");
00706         num++;
00707         mainDoc.addFrameSet( str, 1, 7 );
00708           mainDoc.addFrame( leftMargin, paperHeight - bottomMargin-80,
00709             (paperWidth - rightMargin), paperHeight-bottomMargin, 0, 2, 0 );
00710           mainDoc.closeNode( "FRAME" );
00711           mainDoc.appendNode( i->node );
00712         mainDoc.closeNode( "FRAMESET" );
00713     }
00714     mainDoc.appendNode( frameSets );
00715         mainDoc.closeNode( "FRAMESETS" );
00716         mainDoc.addNode( "PICTURES" );
00717         mainDoc.appendNode( pictures );
00718         mainDoc.closeNode( "PICTURES" );
00719         mainDoc.addNode( "STYLES" );
00720     kwFormat.id  = 1;
00721     kwFormat.pos = 0;
00722     kwFormat.len = 0;
00723 
00724     // Process all styles in the style sheet
00725         const QValueList<RTFStyle>::ConstIterator endStyleSheet=styleSheet.end();
00726         for (QValueList<RTFStyle>::ConstIterator it=styleSheet.begin();it!=endStyleSheet;++it)
00727     {
00728         mainDoc.addNode( "STYLE" );
00729         kwFormat.fmt = (*it).format;
00730 
00731         // Search for 'following' style
00732             for (QValueList<RTFStyle>::ConstIterator it2=styleSheet.begin();it2!=endStyleSheet;++it2)
00733         {
00734         if ((*it2).layout.style == (*it).next)
00735         {
00736                     mainDoc.addNode( "FOLLOWING" );
00737                     mainDoc.setAttribute( "name", CheckAndEscapeXmlText( (*it2).name ));
00738                     mainDoc.closeNode( "FOLLOWING");
00739             break;
00740         }
00741         }
00742         addLayout( mainDoc, (*it).name, (*it).layout, false );
00743         addFormat( mainDoc, kwFormat, 0L );
00744         mainDoc.closeNode( "STYLE" );
00745     }
00746       mainDoc.closeNode( "STYLES" );
00747     mainDoc.closeNode( "DOC" );
00748 
00749     // Create document info
00750     DomNode docInfo( "document-info" );
00751       docInfo.addNode( "log" );
00752     docInfo.addNode( "text" );
00753     docInfo.closeNode( "text" );
00754       docInfo.closeNode( "log" );
00755       docInfo.addNode( "author" );
00756     docInfo.addNode( "company" );
00757       docInfo.appendNode( company );
00758     docInfo.closeNode( "company" );
00759     docInfo.addNode( "full-name" );
00760       docInfo.appendNode( author );
00761     docInfo.closeNode( "full-name" );
00762     docInfo.addNode( "email" );
00763     docInfo.closeNode( "email" );
00764     docInfo.addNode( "telephone" );
00765     docInfo.closeNode( "telephone" );
00766     docInfo.addNode( "fax" );
00767     docInfo.closeNode( "fax" );
00768     docInfo.addNode( "country" );
00769     docInfo.closeNode( "country" );
00770     docInfo.addNode( "postal-code" );
00771     docInfo.closeNode( "postal-code" );
00772     docInfo.addNode( "city" );
00773     docInfo.closeNode( "city" );
00774     docInfo.addNode( "street" );
00775     docInfo.closeNode( "street" );
00776       docInfo.closeNode( "author" );
00777       docInfo.addNode( "about" );
00778     docInfo.addNode( "abstract" );
00779       docInfo.appendNode( doccomm );
00780     docInfo.closeNode( "abstract" );
00781       docInfo.addNode( "title" );
00782     docInfo.appendNode( title );
00783       docInfo.closeNode( "title" );
00784       docInfo.closeNode( "about" );
00785     docInfo.closeNode( "document-info" );
00786 
00787     // Write out main document and document info
00788     writeOutPart( "root", mainDoc );
00789     writeOutPart( "documentinfo.xml", docInfo );
00790     in.close();
00791 
00792     kdDebug(30515) << "RTF FILTER TIME: " << debugTime.elapsed() << endl;
00793 
00794     for (QMap<QString,int>::ConstIterator it=debugUnknownKeywords.begin();
00795         it!=debugUnknownKeywords.end();it++)
00796         kdDebug(30515) << "Unknown keyword: " << QString( "%1" ).arg( it.data(), 4 )  << " * " << it.key() << endl;
00797 
00798     return KoFilter::OK;
00799 }
00800 
00801 void RTFImport::ignoreKeyword( RTFProperty * )
00802 {
00803 }
00804 
00805 void RTFImport::setCodepage( RTFProperty * )
00806 {
00807     QTextCodec* oldCodec = textCodec;
00808     QCString cp;
00809     if ( token.value == 10000 )
00810     {
00811         cp = "Apple Roman"; // ### TODO: how to support the other ones (Qt does not know them!)
00812     } 
00813     else
00814     {
00815         cp.setNum( token.value );
00816         cp.prepend("CP");
00817     }
00818     textCodec=QTextCodec::codecForName(cp);
00819     kdDebug(30515) << "\\ansicpg: codepage: " << token.value << "asked: "<< cp << " given: " << (textCodec?textCodec->name():QString("-none-")) << endl;
00820     if ( ! textCodec )
00821         textCodec = oldCodec;
00822 }
00823 
00824 void RTFImport::setMacCodepage( RTFProperty * )
00825 {
00826     QTextCodec* oldCodec = textCodec;
00827     textCodec=QTextCodec::codecForName("Apple Roman");
00828     kdDebug(30515) << "\\mac " << (textCodec?textCodec->name():QString("-none-")) << endl;
00829     if ( ! textCodec )
00830         textCodec = oldCodec;
00831 }
00832 
00833 void RTFImport::setAnsiCodepage( RTFProperty * )
00834 {
00835     QTextCodec* oldCodec = textCodec;
00836     textCodec=QTextCodec::codecForName("CP1252");
00837     kdDebug(30515) << "\\ansi " << (textCodec?textCodec->name():QString("-none-")) << endl;
00838     if ( ! textCodec )
00839         textCodec = oldCodec;
00840 }
00841 
00842 void RTFImport::setPcaCodepage( RTFProperty * )
00843 {
00844     QTextCodec* oldCodec = textCodec;
00845     textCodec=QTextCodec::codecForName("IBM 850"); // Qt writes the name with a space
00846     kdDebug(30515) << "\\pca " << (textCodec?textCodec->name():QString("-none-")) << endl;
00847     if ( ! textCodec )
00848         textCodec = oldCodec;
00849 }
00850 
00851 void RTFImport::setPcCodepage( RTFProperty * )
00852 {
00853     QTextCodec* oldCodec = textCodec;
00854     textCodec=QTextCodec::codecForName("IBM 850"); // This is an approximation
00855     kdDebug(30515) << "\\pc (approximation) " << (textCodec?textCodec->name():QString("-none-")) << endl;
00856     if ( ! textCodec )
00857         textCodec = oldCodec;
00858 }
00859 
00860 void RTFImport::setToggleProperty( RTFProperty *property )
00861 {
00862     ((bool *)this)[property->offset] = (!token.hasParam || token.value != 0);
00863 }
00864 
00865 void RTFImport::setFlagProperty( RTFProperty *property )
00866 {
00867     ((bool *)this)[property->offset] = property->value;
00868 }
00869 
00870 void RTFImport::setCharset( RTFProperty *property )
00871 {
00872     QCString cp;
00873     switch(token.value) {
00874         case 0: cp = "CP1252"; break; // ANSI_CHARSET
00875         case 1: cp = "CP1252"; break; // DEFAULT_CHARSET
00876         //case 2: cp = ""; break; // SYMBOL_CHARSET not supported yet.
00877         case 77: cp = "Apple Roman"; break; // MAC_CHARSET
00878         case 128: cp = "Shift-JIS"; break; // SHIFTJIS_CHARSET "CP932"
00879         case 129: cp = "eucKR"; break; // HANGUL_CHARSET "CP949"
00880         case 130: cp = "CP1361"; break; // JOHAB_CHARSET doesn't really seem to be supported by Qt :-(
00881         case 134: cp = "GB2312"; break; // GB2312_CHARSET "CP936"
00882         case 136: cp = "Big5-HKSCS"; break; // CHINESEBIG5_CHARSET "CP950"
00883         case 161: cp = "CP1253"; break; // GREEK_CHARSET
00884         case 162: cp = "CP1254"; break; // TURKISH_CHARSET
00885         case 163: cp = "CP1258"; break; // VIETNAMESE_CHARSET
00886         case 177: cp = "CP1255"; break; // HEBREW_CHARSET
00887         case 178: cp = "CP1256"; break; // ARABIC_CHARSET / ARABICSIMPLIFIED_CHARSET
00888         case 186: cp = "CP1257"; break; // BALTIC_CHARSET
00889         case 204: cp = "CP1251"; break; // RUSSIAN_CHARSET / CYRILLIC_CHARSET
00890         case 222: cp = "CP874"; break; // THAI_CHARSET
00891         case 238: cp = "CP1250"; break; // EASTEUROPE_CHARSET / EASTERNEUROPE_CHARSET
00892         case 255: cp = "CP850"; break; // OEM_CHARSET "IBM 850"
00893         default: return;
00894     }
00895     QTextCodec* oldCodec = textCodec;
00896     textCodec=QTextCodec::codecForName(cp);
00897     kdDebug(30515) << "\\fcharset: charset: " << token.value << " codepage: "<< cp << " given: " << (textCodec?textCodec->name():QString("-none-")) << endl;
00898     if ( ! textCodec )
00899         textCodec = oldCodec;
00900 }
00901 
00902 void RTFImport::setNumericProperty( RTFProperty *property )
00903 {
00904     *((int *)(((char *)this) + property->offset)) = token.hasParam ? token.value : property->value;
00905 }
00906 
00907 void RTFImport::setEnumProperty( RTFProperty *property )
00908 {
00909     *((int *)(((char *)this) + property->offset)) = property->value;
00910 }
00911 
00912 void RTFImport::setFontStyleHint( RTFProperty* property )
00913 {
00914     font.styleHint = QFont::StyleHint( property->value );
00915 }
00916 
00917 void RTFImport::setPictureType( RTFProperty* property )
00918 {
00919     picture.type = RTFPicture::PictureType( property->value );
00920 }
00921 
00922 void RTFImport::setSimpleUnderlineProperty( RTFProperty* )
00923 {
00924     state.format.underline
00925          = (!token.hasParam || token.value != 0)
00926          ? RTFFormat::UnderlineSimple : RTFFormat::UnderlineNone;
00927 }
00928 
00929 void RTFImport::setUnderlineProperty( RTFProperty* property )
00930 {
00931     state.format.underline = RTFFormat::Underline( property->value );
00932 }
00933 
00934 void RTFImport::setBorderStyle( RTFProperty *property )
00935 {
00936     if (state.layout.border)
00937     {
00938         state.layout.border->style = static_cast <RTFBorder::BorderStyle> ( property->value );
00939     }
00940     else
00941     {
00942         for (uint i=0; i < 4; i++)
00943         {
00944             state.layout.borders[i].style = static_cast <RTFBorder::BorderStyle> ( property->value );
00945         }
00946     }
00947 }
00948 
00949 void RTFImport::setBorderProperty( RTFProperty *property )
00950 {
00951     //kdDebug() << "setBorderProperty: " << endl;
00952     if (state.layout.border)
00953     {
00954         state.layout.border->width = token.value;
00955     }
00956     else
00957     {
00958     for (uint i=0; i < 4; i++)
00959     {
00960             state.layout.borders[i].width = token.value;
00961     }
00962     }
00963 }
00964 
00965 void RTFImport::setBorderColor( RTFProperty * )
00966 {
00967     if (state.layout.border)
00968     {
00969         state.layout.border->color = token.value;
00970     }
00971     else
00972     {
00973     for (uint i=0; i < 4; i++)
00974     {
00975         state.layout.borders[i].color = token.value;
00976     }
00977     }
00978 }
00979 
00980 void RTFImport::setUpProperty( RTFProperty * )
00981 {
00982     state.format.baseline = token.hasParam ? -token.value : -6;
00983 }
00984 
00985 void RTFImport::setPlainFormatting( RTFProperty * )
00986 {
00987     RTFFormat &format = state.format;
00988 
00989     format.font     = defaultFont;
00990     format.fontSize = 24;
00991     format.baseline = 0;
00992     format.color    = -1;
00993     format.bgcolor  = -1;
00994     format.underlinecolor   = -1;
00995     format.vertAlign    = RTFFormat::Normal;
00996     format.bold     = false;
00997     format.italic   = false;
00998     format.strike   = false;
00999     format.striked  = false;
01000     format.hidden   = false;
01001     format.caps     = false;
01002     format.smallCaps    = false;
01003 
01004     format.underline        = RTFFormat::UnderlineNone;
01005 
01006     // Do not reset format.uc !
01007 }
01008 
01009 void RTFImport::setParagraphDefaults( RTFProperty * )
01010 {
01011     RTFLayout &layout = state.layout;
01012 
01013     layout.tablist.clear();
01014     layout.tab.type = RTFTab::Left;
01015     layout.tab.leader   = RTFTab::None;
01016 
01017     for (uint i=0; i < 4; i++)
01018     {
01019     RTFBorder &border = layout.borders[i];
01020     border.color = -1;
01021     border.width = 0;
01022     border.style = RTFBorder::None;
01023     }
01024     layout.firstIndent  = 0;
01025     layout.leftIndent   = 0;
01026     layout.rightIndent  = 0;
01027     layout.spaceBefore  = 0;
01028     layout.spaceAfter   = 0;
01029     layout.spaceBetween = 0;
01030     layout.spaceBetweenMultiple = false;
01031     layout.style    = 0;
01032     layout.alignment    = RTFLayout::Left;
01033     layout.border   = 0L;
01034     layout.inTable  = false;
01035     layout.keep     = false;
01036     layout.keepNext = false;
01037     layout.pageBB   = false;
01038     layout.pageBA   = false;
01039 }
01040 
01041 void RTFImport::setSectionDefaults( RTFProperty * )
01042 {
01043     RTFSectionLayout &section = state.section;
01044 
01045     section.headerMargin    = 720;
01046     section.footerMargin    = 720;
01047     section.titlePage       = false;
01048 }
01049 
01050 void RTFImport::setTableRowDefaults( RTFProperty * )
01051 {
01052     RTFTableRow &tableRow = state.tableRow;
01053     RTFTableCell &tableCell = state.tableCell;
01054 
01055     tableRow.height = 0;
01056     tableRow.left   = 0;
01057     tableRow.alignment  = RTFLayout::Left;
01058     tableRow.cells.clear();
01059     tableCell.bgcolor   = -1;
01060 
01061     for (uint i=0; i < 4; i++)
01062     {
01063     RTFBorder &border = tableCell.borders[i];
01064     border.color = -1;
01065     border.width = 0;
01066     border.style = RTFBorder::None;
01067     }
01068 }
01069 
01070 void RTFImport::selectLayoutBorder( RTFProperty * property )
01071 {
01072     state.layout.border = & state.layout.borders [ property->value ];
01073 }
01074 
01075 void RTFImport::selectLayoutBorderFromCell( RTFProperty * property )
01076 {
01077     state.layout.border = & state.tableCell.borders [ property->value ];
01078 }
01079 
01080 void RTFImport::insertParagraph( RTFProperty * )
01081 {
01082     if (state.layout.inTable)
01083     {
01084     if (textState->table == 0)
01085     {
01086         // Create a new table cell
01087         textState->table = ++table;
01088     }
01089     addParagraph( textState->cell, false );
01090     }
01091     else
01092     {
01093     if (textState->table)
01094     {
01095         finishTable();
01096     }
01097     addParagraph( textState->node, false );
01098     }
01099 }
01100 
01101 void RTFImport::insertPageBreak( RTFProperty * )
01102 {
01103     if (textState->length > 0)
01104     {
01105     insertParagraph();
01106     }
01107     addParagraph( textState->node, true );
01108 }
01109 
01110 void RTFImport::insertTableCell( RTFProperty * )
01111 {
01112     //{{
01113     bool b = state.layout.inTable;
01114     state.layout.inTable = true;
01115     insertParagraph();
01116     state.layout.inTable = b;
01117     //}}
01118     textState->frameSets << textState->cell.toString();
01119     textState->cell.clear( 3 );
01120 }
01121 
01122 void RTFImport::insertTableRow( RTFProperty * )
01123 {
01124     if (!textState->frameSets.isEmpty())
01125     {
01126     RTFTableRow row = state.tableRow;
01127     row.frameSets = textState->frameSets;
01128 
01129     if (textState->rows.isEmpty())
01130     {
01131         char buf[64];
01132         sprintf( buf, "Table %d", textState->table );
01133         RTFLayout::Alignment align = row.alignment;
01134 
01135         // Store the current state on the stack
01136         stateStack.push( state );
01137         resetState();
01138         state.layout.alignment = align; // table alignment
01139 
01140         // Add anchor for new table (default layout)
01141         addAnchor( buf );
01142         addParagraph( textState->node, false );
01143 
01144         // Retrieve the current state from the stack
01145         state = stateStack.pop();
01146     }
01147 
01148     // Number of cell definitions should equal the number of cells
01149     while (row.cells.count() > row.frameSets.count())
01150     {
01151             // ### TODO: verify if it is the right action and how we have come here at all.
01152         row.cells.pop_back();
01153     }
01154     while (row.cells.count() < row.frameSets.count())
01155     {
01156         row.cells << row.cells.last();
01157     }
01158     int lx = row.left;
01159 
01160     // Each cell should be at least 1x1 in size
01161     if (row.height == 0)
01162     {
01163         row.height = 1;
01164     }
01165         // ### TODO: use ConstIterator
01166     for (uint k=0; k < row.cells.count(); k++)
01167     {
01168         if ((row.cells[k].x - lx) < 1)
01169         row.cells[k].x = ++lx;
01170         else
01171         lx = row.cells[k].x;
01172     }
01173     if (row.left < 0)
01174     {
01175             // ### TODO: use ConstIterator
01176         for (uint k=0; k < row.cells.count(); k++)
01177         {
01178         row.cells[k].x -= row.left;
01179         }
01180         row.left = 0;
01181     }
01182     textState->rows << row;
01183     textState->frameSets.clear();
01184     }
01185 }
01186 
01187 void RTFImport::insertCellDef( RTFProperty * )
01188 {
01189     RTFTableCell &cell = state.tableCell;
01190     cell.x      = token.value;
01191     state.tableRow.cells << cell;
01192     cell.bgcolor    = -1;
01193 
01194     for (uint i=0; i < 4; i++)
01195     {
01196     RTFBorder &border = cell.borders[i];
01197     border.color = -1;
01198     border.width = 0;
01199     border.style = RTFBorder::None;
01200     }
01201 }
01202 
01203 void RTFImport::insertTabDef( RTFProperty * )
01204 {
01205     RTFTab tab = state.layout.tab;
01206     tab.position    = token.value;
01207     state.layout.tablist.push( tab );
01208     tab.type        = RTFTab::Left;
01209     tab.leader      = RTFTab::None;
01210 }
01211 
01212 void RTFImport::insertUTF8( int ch )
01213 {
01214     kdDebug(30515) << "insertUTF8: " << ch << endl;
01215     char buf[4];
01216     char *text = buf;
01217     char *tk = token.text;
01218     token.type = RTFTokenizer::PlainText;
01219     token.text = buf;
01220 
01221     // We do not test if the character is not allowed in XML:
01222     // - it will be done later
01223     // - list definitions need to use char(1), char(2)...
01224     // ### FIXME: for high Unicode values, RTF uses negative values
01225     if (ch > 0x007f)
01226     {
01227         if (ch > 0x07ff)
01228         {
01229             *text++ = 0xe0 | (ch >> 12);
01230             ch = (ch & 0xfff) | 0x1000;
01231         }
01232         *text++ = ((ch >> 6) | 0x80) ^ 0x40;
01233         ch = (ch & 0x3f) | 0x80;
01234     }
01235     *text++ = ch;
01236     *text++ = 0;
01237 
01238     QTextCodec* oldCodec=textCodec;
01239 
01240     if (utf8TextCodec)
01241         textCodec=utf8TextCodec;
01242     else
01243         kdError(30515) << "No UTF-8 QTextCodec available" << endl;
01244 
01245     (this->*destination.destproc)(0L);
01246 
01247     textCodec=oldCodec;
01248     token.text = tk;
01249 }
01250 
01251 void RTFImport::insertSymbol( RTFProperty *property )
01252 {
01253     insertUTF8( property->value );
01254 }
01255 
01256 void RTFImport::insertHexSymbol( RTFProperty * )
01257 {
01258     //kdDebug(30515) << "insertHexSymbol: " << token.value << endl;
01259 
01260     // Be careful, the value given in \' could be only one byte of a multi-byte character.
01261     // So it cannot be assumed that it will result in one character.
01262 
01263     // Some files have \'00 which is pretty bad, as NUL is end of string for us.
01264     // (e.g. attachment #7758 of bug #90649)
01265     if ( !token.value )
01266     {
01267         kdWarning(30515) << "Trying to insert NUL character!" << endl;
01268         return;
01269     }
01270 
01271     char tmpch[2] = {token.value, '\0'};
01272 
01273     char *tk = token.text;
01274     token.type = RTFTokenizer::PlainText;
01275     token.text = tmpch;
01276 
01277     (this->*destination.destproc)(0L);
01278 
01279     token.text = tk;
01280 }
01281 
01282 void RTFImport::insertUnicodeSymbol( RTFProperty * )
01283 {
01284     const int ch = token.value;
01285 
01286     // Ignore the next N characters (or control words)
01287     for (uint i=state.format.uc; i > 0; )
01288     {
01289     token.next();
01290 
01291     if (token.type == RTFTokenizer::ControlWord)
01292         --i;    // Ignore as single token
01293     else if (token.type == RTFTokenizer::OpenGroup ||
01294          token.type == RTFTokenizer::CloseGroup)
01295     {
01296         break;
01297     }
01298     else if (token.type == RTFTokenizer::PlainText)
01299     {
01300             const uint len = qstrlen( token.text );
01301         if ( len < i )
01302         i -= len;
01303         else
01304         {
01305         token.text += i;
01306         break;
01307         }
01308     }
01309     }
01310     if (token.type != RTFTokenizer::PlainText)
01311     {
01312     token.type = RTFTokenizer::PlainText;
01313     token.text[0] = 0;
01314     }
01315     insertUTF8( ch ); // ### TODO: put it higher in this function
01316     (this->*destination.destproc)(0L);
01317 }
01318 
01319 void RTFImport::parseFontTable( RTFProperty * )
01320 {
01321     if (token.type == RTFTokenizer::OpenGroup)
01322     {
01323     font.name = QString::null;
01324     font.styleHint = QFont::AnyStyle;
01325     font.fixedPitch = 0;
01326     }
01327     else if (token.type == RTFTokenizer::PlainText)
01328     {
01329         if (!textCodec)
01330         {
01331             kdError(30515) << "No text codec for font!" << endl;
01332             return; // We have no text codec, so we cannot proceed!
01333         }
01334         // ### TODO VERIFY: a RTF group could be in the middle of the font name
01335         // ### TODO VERIFY:  I do not now if it is specified in RTF but attachment #7758 of bug #90649 has it.
01336     // Semicolons separate fonts
01337     if (strchr( token.text, ';' ) == 0L) // ### TODO: is this allowed with multi-byte Asian characters?
01338         font.name += textCodec->toUnicode( token.text );
01339     else
01340     {
01341         // Add font to font table
01342         *strchr( token.text, ';' ) = 0; // ### TODO: is this allowed with multi-byte Asian characters?
01343         font.name += textCodec->toUnicode( token.text );
01344         // Use Qt to look up the closest matching installed font
01345         QFont qFont( font.name );
01346         qFont.setFixedPitch( (font.fixedPitch == 1) );
01347         qFont.setStyleHint( font.styleHint );
01348         for(;!qFont.exactMatch();)
01349         {
01350         int space=font.name.findRev(' ', font.name.length());
01351         if(space==-1)
01352             break;
01353         font.name.truncate(space);
01354         qFont.setFamily( font.name );
01355         }
01356             const QFontInfo info( qFont );
01357             const QString newFontName ( info.family() );
01358             kdDebug(30515) << "Font " << state.format.font << " asked: " << font.name << " given: " << newFontName << endl;
01359 
01360             if ( newFontName.isEmpty() )
01361                fontTable.insert(  state.format.font, font.name );
01362             else
01363                fontTable.insert( state.format.font, newFontName );
01364         font.name.truncate( 0 );
01365         font.styleHint = QFont::AnyStyle;
01366         font.fixedPitch = 0;
01367     }
01368     }
01369 }
01370 
01371 void RTFImport::parseStyleSheet( RTFProperty * )
01372 {
01373     if (token.type == RTFTokenizer::OpenGroup)
01374     {
01375     style.name = "";
01376     style.next = -1;
01377     }
01378     else if (token.type == RTFTokenizer::PlainText)
01379     {
01380     // Semicolons separate styles
01381     if (strchr( token.text, ';' ) == 0L) // ### TODO: is this allowed with multi-byte Asian characters?
01382         style.name += textCodec->toUnicode( token.text );
01383     else
01384     {
01385         // Add style to style sheet
01386         *strchr( token.text, ';' ) = 0; // ### TODO: is this allowed with multi-byte Asian characters?
01387         style.name  += textCodec->toUnicode( token.text );
01388         style.format = state.format;
01389         style.layout = state.layout;
01390         style.next   = (style.next == -1) ? style.layout.style : style.next;
01391         styleSheet << style;
01392         style.name.truncate( 0 );
01393         style.next   = -1;
01394     }
01395     }
01396 }
01397 
01398 void RTFImport::parseColorTable( RTFProperty * )
01399 {
01400     if (token.type == RTFTokenizer::OpenGroup)
01401     {
01402     red = 0;
01403     green = 0;
01404     blue = 0;
01405     }
01406     else if (token.type == RTFTokenizer::PlainText)
01407     {
01408         // Note: the color table can be a simple ; character only, especially in PWD files
01409     // Search for semicolon(s)
01410     while ((token.text = strchr( token.text, ';' )))
01411     {
01412         colorTable << QColor( red, green, blue );
01413         red = green = blue = 0;
01414         ++token.text;
01415     }
01416     }
01417 }
01418 
01419 void RTFImport::parseBlipUid( RTFProperty * )
01420 {
01421     if (token.type == RTFTokenizer::OpenGroup)
01422     {
01423         picture.identifier = QString::null;
01424     }
01425     else if (token.type == RTFTokenizer::PlainText)
01426     {
01427         picture.identifier += QString::fromUtf8( token.text );
01428     }
01429     else if (token.type == RTFTokenizer::CloseGroup)
01430     {
01431         kdDebug(30515) << "\\blipuid: " << picture.identifier << endl;
01432     }
01433 }
01434 
01435 void RTFImport::parsePicture( RTFProperty * )
01436 {
01437     if (state.ignoreGroup)
01438         return;
01439 
01440     if (token.type == RTFTokenizer::OpenGroup)
01441     {
01442     picture.type        = RTFPicture::PNG;
01443     picture.width       = 0;
01444     picture.height      = 0;
01445     picture.desiredWidth    = 0;
01446     picture.desiredHeight   = 0;
01447     picture.scalex      = 100; // Default is 100%
01448     picture.scaley      = 100; // Default is 100%
01449     picture.cropLeft    = 0;
01450     picture.cropTop     = 0;
01451     picture.cropRight   = 0;
01452     picture.cropBottom  = 0;
01453     picture.nibble      = 0;
01454     picture.bits.truncate( 0 );
01455     picture.identifier = QString::null;
01456     }
01457     else if (token.type == RTFTokenizer::PlainText)
01458     {
01459     if (picture.nibble)
01460     {
01461         *(--token.text) = picture.nibble;
01462     }
01463     uint n = qstrlen( token.text ) >> 1;
01464     picture.bits.resize( picture.bits.size() + n );
01465     char *src = token.text;
01466     char *dst = (picture.bits.data() + picture.bits.size() - n);
01467 
01468     // Store hexadecimal data
01469     while (n-- > 0)
01470     {
01471         int k = *src++;
01472         int l = *src++;
01473         *dst++ = (((k + ((k & 16) ? 0 : 9)) & 0xf) << 4) |
01474               ((l + ((l & 16) ? 0 : 9)) & 0xf);
01475     }
01476     picture.nibble = *src;
01477     }
01478     else if (token.type == RTFTokenizer::BinaryData)
01479     {
01480         picture.bits = token.binaryData;
01481         kdDebug(30515) << "Binary data of length: " << picture.bits.size() << endl;
01482     }
01483     else if (token.type == RTFTokenizer::CloseGroup)
01484     {
01485         const char *ext;
01486 
01487         // Select file extension based on picture type
01488         switch (picture.type)
01489         {
01490         case RTFPicture::WMF:
01491         case RTFPicture::EMF:
01492             ext = ".wmf";
01493             break;
01494         case RTFPicture::BMP:
01495             ext = ".bmp";
01496             break;
01497         case RTFPicture::MacPict:
01498             ext = ".pict";
01499             break;
01500         case RTFPicture::JPEG:
01501             ext = ".jpg";
01502             break;
01503         case RTFPicture::PNG:
01504         default:
01505             ext = ".png";
01506             break;
01507         }
01508         const int id = ++pictureNumber;
01509         QString pictName("pictures/picture");
01510         pictName += QString::number(id);
01511         pictName += ext;
01512 
01513         QCString frameName;
01514         frameName.setNum(id);
01515         frameName.prepend("Picture ");
01516 
01517         QString idStr;
01518         if (picture.identifier.isEmpty())
01519         {
01520             idStr = pictName;
01521         }
01522         else
01523         {
01524             idStr += picture.identifier.stripWhiteSpace();
01525             idStr += ext;
01526         }
01527 
01528         kdDebug(30515) << "Picture: " << pictName << " Frame: " << frameName << endl;
01529 
01530         // Store picture
01531         KoStoreDevice* dev = m_chain->storageFile( pictName, KoStore::Write );
01532         if ( dev )
01533             dev->writeBlock(picture.bits.data(),picture.bits.size());
01534         else
01535             kdError(30515) << "Could not save: " << pictName << endl;
01536 
01537 
01538         // Add anchor to rich text destination
01539         addAnchor( frameName );
01540 
01541         // It is safe, as we call currentDateTime only once for each picture
01542         const QDateTime dt(QDateTime::currentDateTime());
01543 
01544         // Add pixmap or clipart (key)
01545         pictures.addKey( dt, idStr, pictName );
01546 
01547         // Add picture or clipart frameset
01548         frameSets.addFrameSet( frameName, 2, 0 );
01549         //kdDebug(30515) << "Width: " << picture.desiredWidth << " scalex: " << picture.scalex << "%" << endl;
01550         //kdDebug(30515) << "Height: " << picture.desiredHeight<< " scaley: " << picture.scaley << "%" << endl;
01551         frameSets.addFrame( 0, 0,
01552                 (picture.desiredWidth  * picture.scalex) /100 ,
01553                 (picture.desiredHeight * picture.scaley) /100 , 0, 1, 0 );
01554         frameSets.closeNode( "FRAME" );
01555         frameSets.addNode( "PICTURE" );
01556         frameSets.addKey( dt, idStr );
01557         frameSets.closeNode( "PICTURE" );
01558         frameSets.closeNode( "FRAMESET" );
01559         picture.identifier = QString::null;
01560     }
01561 }
01562 
01563 void RTFImport::addImportedPicture( const QString& rawFileName )
01564 {
01565     kdDebug(30515) << "Import field: reading " << rawFileName << endl;
01566 
01567     if (rawFileName=="\\*")
01568     {
01569         kdError(30515) << "Import field without file name!" << endl;
01570         return;
01571     }
01572 
01573     QString slashPath( rawFileName );
01574     slashPath.replace('\\','/'); // Replace directory separators.
01575     // ### TODO: what with MS-DOS absolute paths? (Will only work for KOffice on Win32)
01576     QFileInfo info;
01577     info.setFile( inFileName );
01578     QDir dir( info.dirPath() );
01579 
01580     KURL url;
01581     url.setPath(dir.filePath( rawFileName ));
01582 
01583     kdDebug(30515) << "Path: " << url.prettyURL() << endl;
01584 
01585     KoPicture pic;
01586     pic.setKeyAndDownloadPicture(url, 0); // ### TODO: find a better parent if possible
01587     if (pic.isNull())
01588     {
01589         kdError(30515) << "Import field: file is empty: " << rawFileName << endl;
01590         return;
01591     }
01592 
01593     const uint id = ++pictureNumber;
01594 
01595     QString pictName("pictures/picture");
01596     pictName += QString::number(id);
01597     pictName += '.';
01598     pictName += pic.getExtension();
01599 
01600     QCString frameName;
01601     frameName.setNum(id);
01602     frameName.prepend("Picture ");
01603 
01604 
01605     kdDebug(30515) << "Imported picture: " << pictName << " Frame: " << frameName << endl;
01606 
01607     // Store picture
01608     KoStoreDevice* dev = m_chain->storageFile( pictName, KoStore::Write );
01609     if ( dev )
01610         pic.save(dev);
01611     else
01612         kdError(30515) << "Could not save: " << pictName << endl;
01613 
01614     // Add anchor to rich text destination
01615     addAnchor( frameName );
01616 
01617     // It is safe, as we call currentDateTime only once for each picture
01618     const QDateTime dt( pic.getKey().lastModified() );
01619 
01620     // Add picture key
01621     pictures.addKey( dt, rawFileName, pictName );
01622 
01623     // Add picture frameset
01624     const QSize size ( pic.getOriginalSize() * 20 );  // We need twips for addFrame
01625     frameSets.addFrameSet( frameName, 2, 0 );
01626     frameSets.addFrame( 0, 0, size.width(), size.height(), 0, 1, 0 );
01627     frameSets.closeNode( "FRAME" );
01628     frameSets.addNode( "PICTURE" );
01629     frameSets.addKey( dt, rawFileName );
01630     frameSets.closeNode( "PICTURE" );
01631     frameSets.closeNode( "FRAMESET" );
01632 }
01633 
01634 void RTFImport::insertPageNumber( RTFProperty * )
01635 {
01636     DomNode node;
01637     node.addNode( "PGNUM" );
01638     node.setAttribute( "subtype", 0 );
01639     node.setAttribute( "value", 0 );
01640     node.closeNode("PGNUM");
01641     addVariable( node, 4, "NUMBER", &state.format);
01642 }
01643 
01644 void RTFImport::insertDateTime( RTFProperty *property )
01645 {
01646     kdDebug(30515) << "insertDateTime: " << property->value << endl;
01647     addDateTime( QString::null, bool(property->value), state.format );
01648 }
01649 
01650 void RTFImport::addDateTime( const QString& format, const bool isDate, RTFFormat& fmt )
01651 {
01652     bool asDate=isDate; // Should the variable be a date variable?
01653     QString kwordFormat(format);
01654     if (format.isEmpty())
01655     {
01656         if (isDate)
01657             kwordFormat = "DATElocale";
01658         else
01659             kwordFormat = "TIMElocale";
01660     }
01661     else if (!isDate)
01662     {
01663         // It is a time with a specified format, so check if it is really a time
01664         // (as in KWord 1.3, a date can have a time format but a time cannot have a date format
01665         const QRegExp regexp ("[yMd]"); // any date format character?
01666         asDate = (regexp.search(format)>-1);  // if yes, then it is a date
01667     }
01668     DomNode node;
01669     if (asDate)
01670     {
01671         node.clear(7);
01672         node.addNode("DATE");
01673         node.setAttribute("year", 0);
01674         node.setAttribute("month", 0);
01675         node.setAttribute("day", 0);
01676         node.setAttribute("fix", 0);
01677         node.closeNode("DATE");
01678         addVariable(node, 0, kwordFormat, &fmt);
01679     }
01680     else
01681     {
01682         node.clear(7);
01683         node.addNode("TIME");
01684         node.setAttribute("hour", 0);
01685         node.setAttribute("minute", 0);
01686         node.setAttribute("second", 0);
01687         node.setAttribute("fix", 0);
01688         node.closeNode("TIME");
01689         addVariable(node, 2, kwordFormat, &fmt);
01690     }
01691 }
01692 
01693 void RTFImport::parseField( RTFProperty * )
01694 {
01695     if (token.type == RTFTokenizer::OpenGroup)
01696     {
01697     if (flddst == -1)
01698     {
01699         // Destination for unsupported fields
01700         flddst = (destinationStack.count() - 1);
01701     }
01702     fldinst = "";
01703     fldrslt = "";
01704     destination.group = 0L;
01705     }
01706     else if (token.type == RTFTokenizer::CloseGroup)
01707     {
01708     if (!fldinst.isEmpty())
01709     {
01710         DomNode node;
01711         QStringList list ( QStringList::split( ' ', fldinst, false ) );
01712             kdDebug(30515) << "Field: " << list << endl;
01713         uint i;
01714 
01715             QString fieldName ( list[0].upper() );
01716             fieldName.remove('\\'); // Remove \, especialy leading ones in OOWriter RTF files
01717         node.clear(7);
01718 
01719                 bool ok=false;
01720         for (i=0; i < sizeof(fieldTable) /sizeof(fieldTable[0]); i++)
01721         {
01722         if (fieldName == fieldTable[i].id)
01723         {
01724             kdDebug(30515) << "Field found: " << fieldTable[i].id << endl;
01725             ok=true;
01726             break;
01727         }
01728         }
01729                 if (!ok)
01730                 {
01731                     kdWarning(30515) << "Field not supported: " << fieldName << endl;
01732                     return;
01733                 }
01734         if (fieldTable[i].type == 4)
01735         {
01736         node.addNode( "PGNUM" );
01737         node.setAttribute( "subtype", fieldTable[i].subtype );
01738         node.setAttribute( "value", 0 );
01739         node.closeNode("PGNUM");
01740         addVariable( node, 4, "NUMBER", &fldfmt);
01741         }
01742         else if (fieldTable[i].type == 8)
01743         {
01744         node.addNode( "FIELD" );
01745         node.setAttribute( "subtype", fieldTable[i].subtype );
01746         node.setAttribute( "value", fieldTable[i].value );
01747         node.closeNode("FIELD");
01748         addVariable( node, 8, "STRING", &fldfmt);
01749         }
01750         else if (fieldTable[i].type == 9)
01751         {
01752         QString hrefName = QString::null;
01753 
01754                 // Use ConstIterator
01755         for (uint i=1; i < list.count(); i++)
01756         {
01757             if (list[i] == "\\l")
01758             {
01759             hrefName += '#';
01760             }
01761             else if (list[i].startsWith( "\"" ) && list[i].endsWith( "\"" ))
01762             {
01763             hrefName += list[i].mid( 1, (list[i].length() - 2) );
01764             }
01765             else if (list[i].startsWith("http"))
01766             {
01767             hrefName += list[i];
01768             }
01769         }
01770         node.addNode( "LINK" );
01771         node.setAttribute( "linkName", fldrslt );
01772         node.setAttribute( "hrefName", hrefName );
01773         node.closeNode( "LINK" );
01774         addVariable( node, 9, "STRING", &fldfmt);
01775         }
01776         else if (fieldName == "SYMBOL")
01777         {
01778         if (list.count() >= 2)
01779         {
01780             int ch = list[1].toInt();
01781 
01782             if (ch > 0)
01783             {
01784                         // ### TODO: some error control (the destination might be invalid!)
01785             destination = destinationStack[flddst];
01786             state.format = fldfmt;
01787             insertUTF8( ch );
01788             }
01789         }
01790         }
01791         else if (fieldName == "TIME" || fieldName == "DATE")
01792         {
01793                 QString strFldinst( QString::fromUtf8(fldinst) );
01794                 QRegExp regexp("\\\\@\\s*\"(.+)\""); // \@ "Text"
01795                 if (regexp.search(strFldinst)==-1)
01796                 { // Not found? Perhaps it is not in quotes (even if it is rare)
01797                     kdWarning(30515) << "Date/time field format not in quotes!" << endl;
01798                     strFldinst += ' '; // Add a space at the end to simplify the regular expression
01799                     regexp = QRegExp("\\\\@(\\S+)\\s+"); // \@some_text_up_to_a_space
01800                     regexp.search(strFldinst);
01801                 }
01802                 QString format(regexp.cap(1));
01803                 kdDebug(30515) << "Date/time field format: " << format << endl;
01804         format.replace("am/pm", "ap");
01805         format.replace("a/p", "ap"); // Approximation
01806         format.replace("AM/PM", "AP");
01807         format.replace("A/P", "AP"); // Approximation
01808         format.remove("'"); // KWord 1.3 cannot protect text in date/time
01809                 addDateTime( format, (fieldName == "DATE"), fldfmt );
01810             }
01811             else if (fieldName == "IMPORT")
01812             {
01813                 addImportedPicture( list[1] );
01814             }
01815 
01816         fldinst = "";
01817     }
01818 
01819     if (flddst == (int) (destinationStack.count() - 1))
01820     {
01821         // Top-level field closed, clear field destination
01822         flddst = -1;
01823     }
01824     }
01825 }
01826 
01827 void RTFImport::parseFldinst( RTFProperty * )
01828 {
01829     if (token.type == RTFTokenizer::OpenGroup)
01830     {
01831     fldinst = "";
01832     }
01833     else if (token.type == RTFTokenizer::PlainText)
01834     {
01835     fldinst += token.text;
01836     }
01837 }
01838 
01839 void RTFImport::parseFldrslt( RTFProperty * )
01840 {
01841     if (fldinst.isEmpty())
01842     {
01843     if (token.type == RTFTokenizer::OpenGroup)
01844     {
01845             // ### TODO: why is this destination change not done with the corresponding procedure "changeDestination"?
01846         destination = destinationStack[flddst];
01847         destination.destproc = &RTFImport::parseFldrslt;
01848     }
01849     else if (token.type != RTFTokenizer::CloseGroup)
01850     {
01851         (this->*destinationStack[flddst].destproc)(0L);
01852     }
01853     }
01854     else if (token.type == RTFTokenizer::OpenGroup)
01855     {
01856     fldrslt = "";
01857     }
01858     else if (token.type == RTFTokenizer::PlainText)
01859     {
01860     fldrslt += token.text;
01861     }
01862     else if (token.type == RTFTokenizer::CloseGroup)
01863     {
01864     fldfmt = state.format;
01865     }
01866 }
01867 
01868 void RTFImport::addVariable (const DomNode& spec, int type, const QString& key, const RTFFormat* fmt)
01869 {
01870     DomNode node;
01871 
01872     node.clear( 6 );
01873     node.addNode( "VARIABLE" );
01874     node.closeTag(true);
01875         node.addNode("TYPE");
01876         node.setAttribute( "type", type );
01877         node.setAttribute( "key", CheckAndEscapeXmlText(key) );
01878         node.setAttribute( "text", 1 );
01879         node.closeNode("TYPE");
01880 
01881     node.appendNode(spec);
01882     node.closeNode( "VARIABLE" );
01883     kwFormat.xmldata = node.toString();
01884     kwFormat.id  = 4;
01885     kwFormat.pos = textState->length++;
01886     kwFormat.len = 1;
01887     if (fmt)
01888         kwFormat.fmt = *fmt;
01889     textState->text.append( '#' );
01890     textState->formats << kwFormat;
01891 }
01892 
01893 void RTFImport::parseFootNote( RTFProperty * property)
01894 {
01895     if(token.type==RTFTokenizer::OpenGroup)
01896     {
01897         RTFTextState* newTextState=new RTFTextState;
01898         footnotes.append(newTextState);
01899         fnnum++;
01900         destination.target = newTextState;
01901 
01902         QCString str;
01903         str.setNum(fnnum);
01904         str.prepend("Footnote ");
01905 
01906         DomNode node;
01907 
01908         node.clear( 7 );
01909             node.addNode("FOOTNOTE");
01910             node.setAttribute("numberingtype", "auto");
01911             node.setAttribute("notetype", "footnote");
01912             node.setAttribute("frameset", str);
01913             node.setAttribute("value", fnnum);
01914             node.closeNode("FOOTNOTE");
01915     addVariable(node, 11, "STRING");
01916     }
01917     parseRichText(property);
01918 }
01919 
01920 void RTFImport::parseRichText( RTFProperty * )
01921 {
01922     if (token.type == RTFTokenizer::OpenGroup)
01923     {
01924     // Save and change rich text destination
01925     RTFTextState *oldState = textState;
01926     textState = destination.target;
01927     destination.target = oldState;
01928     destination.group = "Text";
01929 
01930     // Initialize rich text state
01931     textState->text.clear();
01932     textState->node.clear( 3 );
01933     textState->cell.clear( 3 );
01934     textState->formats.clear();
01935     textState->frameSets.clear();
01936     textState->rows.clear();
01937     textState->table  = 0;
01938     textState->length = 0;
01939     }
01940     else if (token.type == RTFTokenizer::PlainText)
01941     {
01942     // Ignore hidden text
01943     if (!state.format.hidden)
01944     {
01945         const int len = (token.text[0] < 0) ? 1 : qstrlen( token.text );
01946 
01947         // Check and store format changes
01948         if ( textState->formats.isEmpty() ||
01949         textState->formats.last().fmt != state.format ||
01950                 ( !textState->formats.last().xmldata.isEmpty() ) )
01951         {
01952         kwFormat.fmt = state.format;
01953         kwFormat.id  = 1;
01954         kwFormat.pos = textState->length;
01955         kwFormat.len = len;
01956         textState->formats << kwFormat;
01957         kwFormat.xmldata = QString::null;
01958         }
01959         else
01960         {
01961         textState->formats.last().len += len;
01962         }
01963         textState->length += len;
01964         textState->text.addTextNode( token.text, textCodec );
01965     }
01966     }
01967     else if (token.type == RTFTokenizer::CloseGroup)
01968     {
01969     if (textState->length)
01970         insertParagraph();
01971     if (textState->table)
01972         finishTable();
01973 
01974     // Restore rich text destination
01975     textState = destination.target;
01976     }
01977 }
01978 
01979 void RTFImport::parsePlainText( RTFProperty * )
01980 {
01981     if (token.type == RTFTokenizer::OpenGroup)
01982     {
01983     destination.target->node.clear();
01984     }
01985     else if (token.type == RTFTokenizer::PlainText)
01986     {
01987         destination.target->node.addTextNode( token.text, textCodec );
01988     }
01989 }
01990 
01991 void RTFImport::parseGroup( RTFProperty * )
01992 {
01993 }
01994 
01995 void RTFImport::skipGroup( RTFProperty * )
01996 {
01997     kdDebug(30515) << "Skip Group: " << token.type << endl;
01998     state.ignoreGroup = true;
01999 }
02000 
02001 void RTFImport::resetState()
02002 {
02003     setPlainFormatting();
02004     setParagraphDefaults();
02005     setSectionDefaults();
02006     setTableRowDefaults();
02007 }
02008 
02009 void RTFImport::changeDestination( RTFProperty *property )
02010 {
02011     kdDebug(30515) << "changeDestination: " << property->name << endl;
02012     destinationStack.push( destination );
02013     destination.name     = property->name;
02014     destination.destproc = property->cwproc;
02015     if ( property->offset )
02016         destination.target = (RTFTextState*) ( (char *)this + property->offset );
02017     else
02018         destination.target = &m_dummyTextState;
02019 
02020     state.brace0 = true;
02021 
02022     if (property->value)
02023     {
02024     resetState();
02025     destination.group = 0L;
02026     }
02027 
02028     // Send OpenGroup to destination
02029     token.type = RTFTokenizer::OpenGroup;
02030     (this->*destination.destproc)(0L);
02031 }
02032 
02033 void RTFImport::addAnchor( const char *instance )
02034 {
02035     DomNode node;
02036 
02037     node.clear( 6 );
02038     node.addNode( "ANCHOR" );
02039     node.setAttribute( "type", "frameset" );
02040     node.setAttribute( "instance", instance );
02041     node.closeNode( "ANCHOR" );
02042     kwFormat.xmldata = node.toString();
02043     kwFormat.id  = 6;
02044     kwFormat.pos = textState->length++;
02045     kwFormat.len = 1;
02046     textState->text.append( '#' );
02047     textState->formats << kwFormat;
02048 }
02049 
02050 void RTFImport::addFormat( DomNode &node, const KWFormat& format, const RTFFormat* baseFormat )
02051 {
02052     // Support both (\dn, \up) and (\sub, \super) for super/sub script
02053     int vertAlign  = format.fmt.vertAlign;
02054     int fontSize   = (format.fmt.fontSize >> 1);
02055     int vertAlign0 = ~vertAlign;
02056     int fontSize0  = ~fontSize;
02057 
02058     // Adjust vertical alignment and font size if (\dn, \up) are used
02059     if (format.fmt.vertAlign == RTFFormat::Normal && format.fmt.baseline)
02060     {
02061     if (format.fmt.baseline < 0)
02062         vertAlign = RTFFormat::SuperScript;
02063     else    // (format.baseline > 0)
02064         vertAlign = RTFFormat::SubScript;
02065 
02066     fontSize += (fontSize >> 1);
02067     }
02068     if (baseFormat)
02069     {
02070     vertAlign0 = baseFormat->vertAlign;
02071     fontSize0  = (baseFormat->fontSize >> 1);
02072 
02073     if (vertAlign0 == RTFFormat::Normal && baseFormat->baseline)
02074     {
02075         if (baseFormat->baseline < 0)
02076         vertAlign0 = RTFFormat::SuperScript;
02077         else    // (baseFormat.baseline > 0)
02078         vertAlign0 = RTFFormat::SubScript;
02079 
02080         fontSize0 += (fontSize0 >> 1);
02081     }
02082     }
02083     node.addNode( "FORMAT" );
02084     node.setAttribute( "id", (int)format.id );
02085 
02086     if (format.len != 0)
02087     {
02088     // Add pos and len if this is not a style sheet definition
02089     node.setAttribute( "pos", (int)format.pos );
02090     node.setAttribute( "len", (int)format.len );
02091     }
02092     if ((format.id == 1)||(format.id == 4))
02093     {
02094     // Normal text, store changes between format and base format
02095     if (!baseFormat || format.fmt.color != baseFormat->color)
02096     {
02097         node.addNode( "COLOR" );
02098         node.addColor( ((uint)format.fmt.color >= colorTable.count())
02099                ? (QColor &)Qt::black : colorTable[format.fmt.color] );
02100         node.closeNode( "COLOR" );
02101     }
02102     if ((uint)format.fmt.bgcolor < colorTable.count() &&
02103         (!baseFormat || format.fmt.bgcolor != baseFormat->bgcolor))
02104     {
02105         node.addNode( "TEXTBACKGROUNDCOLOR" );
02106         node.addColor( colorTable[format.fmt.bgcolor] );
02107         node.closeNode( "TEXTBACKGROUNDCOLOR" );
02108     }
02109     if (!baseFormat || format.fmt.font != baseFormat->font)
02110     {
02111         node.addNode( "FONT" );
02112 
02113         if (fontTable.contains( format.fmt.font ))
02114         {
02115         node.setAttribute( "name", fontTable[format.fmt.font] );
02116         }
02117         node.closeNode( "FONT" );
02118     }
02119     if (!baseFormat || format.fmt.bold != baseFormat->bold)
02120     {
02121         node.addNode( "WEIGHT" );
02122         node.setAttribute( "value", (format.fmt.bold ? 75 : 50) );
02123         node.closeNode( "WEIGHT" );
02124     }
02125     if (fontSize != fontSize0)
02126     {
02127         node.addNode( "SIZE" );
02128         node.setAttribute( "value", fontSize );
02129         node.closeNode( "SIZE" );
02130     }
02131     if (!baseFormat || format.fmt.italic != baseFormat->italic)
02132     {
02133         node.addNode( "ITALIC" );
02134         node.setAttribute( "value", format.fmt.italic );
02135         node.closeNode( "ITALIC" );
02136     }
02137         if (!baseFormat || format.fmt.underline != baseFormat->underline )
02138     {
02139         node.addNode( "UNDERLINE" );
02140             QCString st,styleline,wordbyword("0");
02141             st.setNum(format.fmt.underline);
02142             int underlinecolor = format.fmt.underlinecolor;
02143 
02144             switch (format.fmt.underline)
02145             {
02146             case RTFFormat::UnderlineNone:
02147             default:
02148                 {
02149                     st="0";
02150                     underlinecolor=-1; // Reset underline color
02151                     break;
02152                 }
02153             case RTFFormat::UnderlineSimple:
02154                 {
02155                     st="single";
02156                     break;
02157                 }
02158             case RTFFormat::UnderlineDouble:
02159                 {
02160                     st="double";
02161                     break;
02162                 }
02163             case RTFFormat::UnderlineThick:
02164                 {
02165                     st="single-bold";
02166                     styleline="solid";
02167                     break;
02168                 }
02169 
02170             case RTFFormat::UnderlineWordByWord:
02171                 {
02172                     st="single";
02173                     styleline="solid";
02174                     wordbyword="1";
02175                     break;
02176                 }
02177             case RTFFormat::UnderlineDash:
02178                 {
02179                     st="single";
02180                     styleline="dash";
02181                     break;
02182                 }
02183             case RTFFormat::UnderlineDot:
02184                 {
02185                     st="single";
02186                     styleline="dot";
02187                     break;
02188                 }
02189             case RTFFormat::UnderlineDashDot:
02190                 {
02191                     st="single";
02192                     styleline="dashdot";
02193                     break;
02194                 }
02195             case RTFFormat::UnderlineDashDotDot:
02196                 {
02197                     st="single";
02198                     styleline="dashdotdot";
02199                     break;
02200                 }
02201             case RTFFormat::UnderlineWave:
02202                 {
02203                     st="single";
02204                     styleline="wave";
02205                     break;
02206                 }
02207             } // end of switch
02208             node.setAttribute( "value", st );
02209             node.setAttribute( "wordbyword", wordbyword );
02210             if ( !styleline.isEmpty() )
02211                 node.setAttribute( "styleline", styleline );
02212             if ( underlinecolor >= 0 && uint(underlinecolor) < colorTable.count() )
02213             {
02214                 node.setAttribute( "underlinecolor", colorTable[underlinecolor].name() );
02215             }
02216 
02217         node.closeNode( "UNDERLINE" );
02218     }
02219     if (!baseFormat || format.fmt.strike != baseFormat->strike || format.fmt.striked != baseFormat->striked)
02220     {
02221         node.addNode( "STRIKEOUT" );
02222         QCString st;
02223         st.setNum(format.fmt.strike);
02224         if(format.fmt.striked)
02225         st="double";
02226         node.setAttribute( "value", st );
02227         node.closeNode( "STRIKEOUT" );
02228     }
02229     if (vertAlign != vertAlign0)
02230     {
02231         node.addNode( "VERTALIGN" );
02232         node.setAttribute( "value", vertAlign );
02233         node.closeNode( "VERTALIGN" );
02234     }
02235     if (!baseFormat || format.fmt.caps != baseFormat->caps || format.fmt.smallCaps != baseFormat->smallCaps)
02236     {
02237         node.addNode( "FONTATTRIBUTE" );
02238             QCString fontattr;
02239             if ( format.fmt.caps )
02240                 fontattr="uppercase";
02241             else if ( format.fmt.smallCaps )
02242                 fontattr="smallcaps";
02243             else
02244                 fontattr="none";
02245         node.setAttribute( "value", fontattr );
02246         node.closeNode( "FONTATTRIBUTE" );
02247     }
02248     if (!baseFormat)
02249     {
02250         node.addNode( "CHARSET" );
02251         node.setAttribute( "value", (int)QFont::Unicode );
02252         node.closeNode( "CHARSET" );
02253     }
02254     }
02255     if (format.id == 4 || format.id == 6)
02256     {
02257     // Variable or anchor
02258     node.closeTag( true );
02259     node.append( format.xmldata );
02260     }
02261     node.closeNode( "FORMAT" );
02262 }
02263 
02264 void RTFImport::addLayout( DomNode &node, const QString &name, const RTFLayout &layout, bool frameBreak )
02265 {
02266     // Style name and alignment
02267     node.addNode( "NAME" );
02268       node.setAttribute( "value", CheckAndEscapeXmlText(name) );
02269     node.closeNode( "NAME" );
02270     node.addNode( "FLOW" );
02271       node.setAttribute( "align", alignN[layout.alignment] );
02272     node.closeNode( "FLOW" );
02273 
02274     // Indents
02275     if (layout.firstIndent || layout.leftIndent || layout.rightIndent)
02276     {
02277     node.addNode( "INDENTS" );
02278 
02279     if (layout.firstIndent)
02280         node.setAttribute( "first", .05*layout.firstIndent );
02281     if (layout.leftIndent)
02282         node.setAttribute( "left", .05*layout.leftIndent );
02283     if (layout.rightIndent)
02284         node.setAttribute( "right", .05*layout.rightIndent );
02285 
02286     node.closeNode( "INDENTS" );
02287     }
02288 
02289     // Offets
02290     if (layout.spaceBefore || layout.spaceAfter)
02291     {
02292     node.addNode( "OFFSETS" );
02293 
02294     if (layout.spaceBefore)
02295         node.setAttribute( "before", .05*layout.spaceBefore );
02296     if (layout.spaceAfter)
02297         node.setAttribute( "after", .05*layout.spaceAfter );
02298 
02299     node.closeNode( "OFFSETS" );
02300     }
02301 
02302 
02303     // Linespacing
02304 
02305     QString lineSpacingType;
02306     QString lineSpacingValue;  
02307     if ( layout.spaceBetweenMultiple )
02308     {
02309         // Note: 240 is a sort of magic value for one line (Once upon a time, it meant 12pt for a single line)
02310         switch (layout.spaceBetween )
02311         {
02312         case 240:
02313             {
02314                 lineSpacingType = "single"; // ### TODO: does KWord really supports this?
02315                 break;
02316             }
02317         case 360:
02318             {
02319                 lineSpacingType = "oneandhalf";
02320                 break;
02321             }
02322         case 480 :
02323             {
02324                 lineSpacingType = "double";
02325                 break;
02326             }
02327         default:
02328             {    
02329                 if ( layout.spaceBetween > 0 )
02330                 {
02331                     lineSpacingType = "multiple";
02332                     lineSpacingValue.setNum( layout.spaceBetween / 240.0 );
02333                 }
02334                 break;
02335             }
02336         }
02337     }
02338     else
02339     {
02340         if (layout.spaceBetween > 0)
02341         {
02342             lineSpacingType = "atleast";
02343             lineSpacingValue.setNum( 0.05*layout.spaceBetween );
02344         }
02345         if (layout.spaceBetween < 0)
02346         {
02347             // negative linespace means "exact"
02348             lineSpacingType = "fixed" ;
02349             lineSpacingValue.setNum( -0.05*layout.spaceBetween );
02350         }
02351     }
02352     
02353     if ( ! lineSpacingType.isEmpty() )
02354     {
02355         node.addNode( "LINESPACING" );
02356         node.setAttribute( "type", lineSpacingType );
02357         if ( ! lineSpacingValue.isEmpty() )
02358             node.setAttribute( "spacingvalue", lineSpacingValue );
02359         node.closeNode( "LINESPACING" );
02360     }
02361 
02362     if (layout.keep || layout.pageBB || layout.pageBA || frameBreak || layout.keepNext)
02363     {
02364     node.addNode( "PAGEBREAKING" );
02365       node.setAttribute( "linesTogether", boolN[layout.keep] );
02366       node.setAttribute( "hardFrameBreak", boolN[layout.pageBB] );
02367       node.setAttribute( "hardFrameBreakAfter", boolN[layout.pageBA || frameBreak] );
02368       node.setAttribute( "keepWithNext", boolN[layout.keepNext] );
02369     node.closeNode( "PAGEBREAKING" );
02370     }
02371 
02372     // Paragraph borders
02373     for (uint i=0; i < 4; i++)
02374     {
02375     const RTFBorder &border = layout.borders[i];
02376 
02377     if (border.style != RTFBorder::None || border.width > 0)
02378     {
02379         node.addNode( borderN[i] );
02380           node.addColor( ((uint)border.color >= colorTable.count())
02381                  ? (QColor &)Qt::black : colorTable[border.color] );
02382           node.setAttribute( "style", (int)border.style & 0xf );
02383           node.setAttribute( "width", (border.width < 20) ? 1 : border.width /20 );
02384         node.closeNode( borderN[i] );
02385     }
02386     }
02387 
02388     // Add automatic tab stop for hanging indent
02389     if (layout.firstIndent < 0 && layout.leftIndent > 0)
02390     {
02391     node.addNode( "TABULATOR" );
02392       node.setAttribute( "type", 0 );
02393       node.setAttribute( "ptpos", .05*layout.leftIndent );
02394     node.closeNode( "TABULATOR" );
02395     }
02396 
02397     // Tabulators
02398     if (!layout.tablist.isEmpty())
02399     {
02400         // ### TODO: use ConstIterator
02401     for (uint i=0; i < layout.tablist.count(); i++)
02402     {
02403         const RTFTab &tab = layout.tablist[i];
02404         int l = (int)tab.leader;
02405         node.addNode( "TABULATOR" );
02406           node.setAttribute( "type", tab.type );
02407           node.setAttribute( "ptpos", .05*tab.position );
02408           node.setAttribute( "filling", (l < 2) ? l : ((l == 2) ? 1 : 2) );
02409           node.setAttribute( "width", (l == 4) ? 1. : 0.5 );
02410         node.closeNode( "TABULATOR" );
02411     }
02412     }
02413 }
02414 
02415 void RTFImport::addParagraph( DomNode &node, bool frameBreak )
02416 {
02417     node.addNode( "PARAGRAPH" );
02418       node.addNode( "TEXT" );
02419     node.appendNode( textState->text );
02420       node.closeNode( "TEXT" );
02421 
02422     // Search for style in style sheet
02423     QString name;
02424     const RTFFormat* format = &state.format;
02425     const int styleNum = state.layout.style;
02426 
02427     const QValueList<RTFStyle>::ConstIterator endStyleSheet = styleSheet.end();
02428     for ( QValueList<RTFStyle>::ConstIterator it=styleSheet.begin(); it!=endStyleSheet; ++it )
02429     {
02430     if ( (*it).layout.style == styleNum )
02431     {
02432         if ( textState->length > 0 )
02433         {
02434         format = &(*it).format;
02435         }
02436         name = (*it).name;
02437         break;
02438     }
02439     }
02440     kwFormat.fmt = *format;
02441     kwFormat.id  = 1;
02442     kwFormat.pos = 0;
02443     kwFormat.len = textState->length;
02444 
02445     if ( name.isEmpty() )
02446     {
02447         kdWarning(30515) << "Style name empty! Assuming Standard!" << endl;
02448         name = "Standard";
02449     }
02450     
02451     // Insert character formatting
02452     bool hasFormats = false;
02453 
02454     for ( QValueList<KWFormat>::ConstIterator it = textState->formats.begin(); it != textState->formats.end(); ++it )
02455     {
02456     if ( (*it).id != 1 || (*it).fmt != *format )
02457     {
02458         if (!hasFormats)
02459         {
02460         node.addNode( "FORMATS" );
02461         hasFormats = true;
02462         }
02463         addFormat( node, (*it), format );
02464     }
02465     }
02466     if (hasFormats)
02467     {
02468     node.closeNode( "FORMATS" );
02469     }
02470 
02471     // Write out layout and format
02472       node.addNode( "LAYOUT" );
02473     addLayout( node, name, state.layout, frameBreak );
02474     addFormat( node, kwFormat, 0L );
02475       node.closeNode( "LAYOUT" );
02476     node.closeNode( "PARAGRAPH" );
02477 
02478     // Clear plain text and formats for next paragraph
02479     textState->text.clear();
02480     textState->length = 0;
02481     textState->formats.clear();
02482 }
02483 
02484 void RTFImport::finishTable()
02485 {
02486     kdDebug(30515) << "Starting TFImport::finishTable..." << endl;
02487     QCString emptyArray;
02488     QValueList<int> cellx;
02489     int left = 0, right = 0;
02490 
02491     insertTableRow();
02492 
02493     // Calculate maximum horizontal extents
02494     // ### TODO: use ConstIterator
02495     for (uint i=0; i < textState->rows.count(); i++)
02496     {
02497     RTFTableRow &row = textState->rows[i];
02498 
02499     if (row.left < left || i == 0)
02500         left = row.left;
02501     if (row.cells.last().x > right || i == 0)
02502         right = row.cells.last().x;
02503     }
02504 
02505     // Force rectangular table (fill gaps with empty cells)
02506     // ### TODO: use ConstIterator
02507     for (uint i=0; i < textState->rows.count(); i++)
02508     {
02509     RTFTableRow &row = textState->rows[i];
02510 
02511     if (row.left > left)
02512     {
02513         row.frameSets.prepend( emptyArray );
02514         emptyCell.x = row.left;
02515         row.cells.prepend( emptyCell );
02516         row.left = left;
02517     }
02518     if (row.cells.last().x < right)
02519     {
02520         row.frameSets << emptyArray;
02521         emptyCell.x = right;
02522         row.cells << emptyCell;
02523     }
02524         // ### TODO: use ConstIterator
02525     for (uint k=0; k < row.cells.count(); k++)
02526     {
02527         if (!cellx.contains( row.cells[k].x ))
02528         cellx << row.cells[k].x;
02529     }
02530     if (!cellx.contains( row.left ))
02531     {
02532         cellx << row.left;
02533     }
02534     }
02535 
02536     // Sort vertical cell boundaries
02537     // ### TODO: use ConstIterator
02538     for (uint k=0; k < cellx.count(); k++)
02539     {
02540     for (uint l=k+1; l < cellx.count(); l++)
02541     {
02542         if (cellx[l] < cellx[k])
02543         {
02544         int tmp = cellx[l];
02545         cellx[l] = cellx[k];
02546         cellx[k] = tmp;
02547         }
02548     }
02549     }
02550     int y1 = 0;
02551 
02552     // Store cell frame and table information
02553     // ### TODO: use ConstIterator
02554     for (uint i=0; i < textState->rows.count(); i++)
02555     {
02556     RTFTableRow &row = textState->rows[i];
02557     int h  = abs( row.height );
02558     int y2 = y1 + ((h < 400) ? 400 : h);    // KWord work-around
02559     int x1 = row.left;
02560 
02561     for (uint k=0; k < row.cells.count(); k++)
02562     {
02563         char buf[64];
02564         int x2 = row.cells[k].x;
02565         int col = cellx.findIndex( x1 );
02566 
02567         sprintf( buf, "Table %d Cell %d,%d", textState->table, i, col );
02568         frameSets.addFrameSet( buf, 1, 0 );
02569         sprintf( buf, "Table %d", textState->table );
02570         frameSets.setAttribute( "grpMgr", buf );
02571         frameSets.setAttribute( "row", (int)i );
02572         frameSets.setAttribute( "col", col );
02573         frameSets.setAttribute( "rows", 1 );
02574         frameSets.setAttribute( "cols", cellx.findIndex( x2 ) - col );
02575 
02576         frameSets.addFrame( x1, y1, x2, y2, (row.height < 0) ? 2 : 0, 1, 0 );
02577 
02578         // Frame borders
02579         for (uint i=0; i < 4; i++)
02580         {
02581         RTFBorder &border = row.cells[k].borders[i];
02582 
02583         if (border.style != RTFBorder::None || border.width > 0)
02584         {
02585             const char *id = "lrtb";
02586             QColor &c = ((uint)border.color >= colorTable.count())
02587                 ? (QColor &)Qt::black : colorTable[border.color];
02588             frameSets.addBorder( (int)id[i], c, (int)border.style & 0x0f,
02589                      .05*(!border.width ? 10 : border.width) );
02590         }
02591         }
02592 
02593         // Frame background color
02594         if ((uint)row.cells[k].bgcolor < colorTable.count())
02595         {
02596         QColor &color = colorTable[row.cells[k].bgcolor];
02597         frameSets.setAttribute( "bkRed", color.red() );
02598         frameSets.setAttribute( "bkGreen", color.green() );
02599         frameSets.setAttribute( "bkBlue", color.blue() );
02600         }
02601         frameSets.closeNode( "FRAME" );
02602         frameSets.append( row.frameSets[k] );
02603         frameSets.closeNode( "FRAMESET" );
02604         x1 = x2;
02605     }
02606     y1 = y2;
02607     }
02608     textState->table = 0;
02609     textState->rows.clear();
02610     kdDebug(30515) << "Quitting TFImport::finishTable..." << endl;
02611 }
02612 
02613 void RTFImport::writeOutPart( const char *name, const DomNode& node )
02614 {
02615     KoStoreDevice* dev = m_chain->storageFile( name, KoStore::Write );
02616     if ( dev )
02617     {
02618         QTextStream stream( dev );
02619         stream.setEncoding( QTextStream::UnicodeUTF8 );
02620         stream << node.toString();
02621     }
02622     else
02623         kdError(30515) << "Could not write part " << name << endl;
02624 }
KDE Home | KDE Accessibility Home | Description of Access Keys