You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2624 lines
84 KiB
2624 lines
84 KiB
/*
|
|
This file is part of the KDE project
|
|
Copyright (C) 2001 Ewald Snel <ewald@rambo.its.tudelft.nl>
|
|
Copyright (C) 2001 Tomasz Grobelny <grotk@poczta.onet.pl>
|
|
Copyright (C) 2003, 2004 Nicolas GOUTTE <goutte@kde.org>
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Library General Public
|
|
License as published by the Free Software Foundation; either
|
|
version 2 of the License, or (at your option) any later version.
|
|
*/
|
|
|
|
// ### FIXME: copyright holders/date
|
|
|
|
#include <kdebug.h>
|
|
#include <tqfontinfo.h>
|
|
#include <stddef.h>
|
|
#include <string.h>
|
|
#include <KoFilterChain.h>
|
|
#include <kgenericfactory.h>
|
|
|
|
#include <tqcstring.h>
|
|
#include <tqstringlist.h>
|
|
#include <tqdir.h>
|
|
#include <tqfileinfo.h>
|
|
#include <tqregexp.h>
|
|
#include <tqvaluelist.h>
|
|
|
|
#include <kurl.h>
|
|
#include <tdemessagebox.h>
|
|
|
|
#include <KoPicture.h>
|
|
#include <KoFilterManager.h>
|
|
|
|
#include "rtfimport.h"
|
|
#include "rtfimport.moc"
|
|
|
|
typedef KGenericFactory<RTFImport, KoFilter> RTFImportFactory;
|
|
K_EXPORT_COMPONENT_FACTORY( librtfimport, RTFImportFactory( "kofficefilters" ) )
|
|
|
|
// defines a property
|
|
#define PROP(a,b,c,d,e) { a, b, &RTFImport::c, d, e }
|
|
|
|
// defines a member variable of RTFImport as a property (DEPRECATED)
|
|
#define MEMBER(a,b,c,d,e) PROP(a,b,c,offsetof(RTFImport,d),e)
|
|
|
|
static RTFProperty destinationPropertyTable[] =
|
|
{
|
|
// only-valid-in control word function offset, value
|
|
PROP( 0L, "@*", skipGroup, 0L, false ),
|
|
MEMBER( "@info", "@author", parsePlainText, author, false ),
|
|
PROP( "@pict", "@blipuid", parseBlipUid, 0, 0 ),
|
|
PROP( "@rtf", "@colortbl", parseColorTable, 0L, true ),
|
|
MEMBER( "@info", "@company", parsePlainText, company, false ),
|
|
MEMBER( "@info", "@doccomm", parsePlainText, doccomm, false ),
|
|
PROP( "Text", "@field", parseField, 0L, false ),
|
|
PROP( "@field", "@fldinst", parseFldinst, 0L, false ),
|
|
PROP( "@field", "@fldrslt", parseFldrslt, 0L, false ),
|
|
PROP( "@rtf", "@fonttbl", parseFontTable, 0L, true ),
|
|
MEMBER( "@rtf", "@footer", parseRichText, oddPagesFooter, true ),
|
|
PROP( "@rtf", "@footnote", parseFootNote, 0L, true ),
|
|
MEMBER( "@rtf", "@footerf", parseRichText, firstPageFooter, true ),
|
|
MEMBER( "@rtf", "@footerl", parseRichText, oddPagesFooter, true ),
|
|
MEMBER( "@rtf", "@footerr", parseRichText, evenPagesFooter, true ),
|
|
MEMBER( "@rtf", "@header", parseRichText, oddPagesHeader, true ),
|
|
MEMBER( "@rtf", "@headerf", parseRichText, firstPageHeader, true ),
|
|
MEMBER( "@rtf", "@headerl", parseRichText, oddPagesHeader, true ),
|
|
MEMBER( "@rtf", "@headerr", parseRichText, evenPagesHeader, true ),
|
|
PROP( "@rtf", "@info", parseGroup, 0L, true ),
|
|
PROP( "Text", "@nonshppict", skipGroup, 0L, false ),
|
|
PROP( 0L, "@panose", skipGroup, 0L, 0 ), // Not supported
|
|
PROP( "Text", "@pict", parsePicture, 0L, true ),
|
|
MEMBER( "@", "@rtf", parseRichText, bodyText, true ),
|
|
PROP( "Text", "@shpinst", skipGroup, 0L, true ),
|
|
PROP( "Text", "@shppict", parseGroup, 0L, false ),
|
|
PROP( "@rtf", "@stylesheet", parseStyleSheet, 0L, true ),
|
|
MEMBER( "@info", "@title", parsePlainText, title, false ),
|
|
};
|
|
|
|
static RTFProperty propertyTable[] =
|
|
// Alphabetical order
|
|
{
|
|
// only-valid-in control word function offset, value
|
|
PROP( "Text", "\n", insertParagraph, 0L, 0 ),
|
|
PROP( "Text", "\r", insertParagraph, 0L, 0 ),
|
|
PROP( 0L, "\'", insertHexSymbol, 0L, 0 ),
|
|
PROP( 0L, "\\", insertSymbol, 0L, '\\' ),
|
|
PROP( 0L, "_", insertSymbol, 0L, 0x2011 ),
|
|
PROP( 0L, "{", insertSymbol, 0L, '{' ),
|
|
PROP( 0L, "|", insertSymbol, 0L, 0x00b7 ),
|
|
PROP( 0L, "}", insertSymbol, 0L, '}' ),
|
|
PROP( 0L, "~", insertSymbol, 0L, 0x00a0 ),
|
|
PROP( 0L, "-", insertSymbol, 0L, 0x00ad ),
|
|
PROP( 0L, "adjustright", ignoreKeyword, 0L, 0 ), // Not supported, KWord has no grid
|
|
PROP( 0L, "ansi", setAnsiCodepage, 0L, 0 ),
|
|
PROP( 0L, "ansicpg", setCodepage, 0L, 0 ),
|
|
MEMBER( 0L, "b", setToggleProperty, state.format.bold, 0 ),
|
|
// \bin is handled in the tokenizer
|
|
MEMBER( "@colortbl", "blue", setNumericProperty, blue, 0 ),
|
|
MEMBER( 0L, "box", setEnumProperty, state.layout.border, 0 ),
|
|
PROP( 0L, "brdrb", selectLayoutBorder, 0L, 3 ),
|
|
PROP( 0L, "brdrcf", setBorderColor, 0L, 0 ),
|
|
PROP( 0L, "brdrdash", setBorderStyle, 0L, RTFBorder::Dashes ),
|
|
PROP( 0L, "brdrdashd", setBorderStyle, 0L, RTFBorder::DashDot ),
|
|
PROP( 0L, "brdrdashdd", setBorderStyle, 0L, RTFBorder::DashDotDot ),
|
|
PROP( 0L, "brdrdashsm", setBorderStyle, 0L, RTFBorder::Dashes ),
|
|
PROP( 0L, "brdrdb", setBorderStyle, 0L, RTFBorder::Solid ),
|
|
PROP( 0L, "brdrdot", setBorderStyle, 0L, RTFBorder::Dots ),
|
|
PROP( 0L, "brdrhairline", setBorderStyle, 0L, RTFBorder::Solid ),
|
|
PROP( 0L, "brdrl", selectLayoutBorder, 0L, 0 ),
|
|
PROP( 0L, "brdrr", selectLayoutBorder, 0L, 1 ),
|
|
PROP( 0L, "brdrs", setBorderStyle, 0L, RTFBorder::Solid ),
|
|
PROP( 0L, "brdrsh", setBorderStyle, 0L, RTFBorder::Solid ),
|
|
PROP( 0L, "brdrt", selectLayoutBorder, 0L, 2 ),
|
|
PROP( 0L, "brdrth", setBorderStyle, 0L, RTFBorder::Solid ),
|
|
PROP( 0L, "brdrw", setBorderProperty, offsetof(RTFBorder,width), 0 ),
|
|
PROP( 0L, "bullet", insertSymbol, 0L, 0x2022 ),
|
|
PROP( 0L, "brsp", setBorderProperty, offsetof(RTFBorder,space), 0 ),
|
|
MEMBER( 0L, "caps", setToggleProperty, state.format.caps, 0 ),
|
|
MEMBER( 0L, "cb", setNumericProperty, state.format.bgcolor, 0 ),
|
|
MEMBER( 0L, "highlight", setNumericProperty, state.format.bgcolor, 0 ),
|
|
PROP( "Text", "cell", insertTableCell, 0L, 0 ),
|
|
PROP( 0L, "cellx", insertCellDef, 0L, 0 ),
|
|
MEMBER( 0L, "cf", setNumericProperty, state.format.color, 0 ),
|
|
PROP( 0L, "chdate", insertDateTime, 0L, TRUE ),
|
|
PROP( 0L, "chpgn", insertPageNumber, 0L, 0 ),
|
|
PROP( 0L, "chtime", insertDateTime, 0L, FALSE ),
|
|
PROP( 0L, "clbrdrb", selectLayoutBorderFromCell, 0L, 3 ),
|
|
PROP( 0L, "clbrdrl", selectLayoutBorderFromCell, 0L, 0 ),
|
|
PROP( 0L, "clbrdrr", selectLayoutBorderFromCell, 0L, 1 ),
|
|
PROP( 0L, "clbrdrt", selectLayoutBorderFromCell, 0L, 2 ),
|
|
MEMBER( 0L, "clcbpat", setNumericProperty, state.tableCell.bgcolor, 0 ),
|
|
PROP( 0L, "cs", ignoreKeyword, 0L, 0 ), // Not supported by KWord 1.3
|
|
PROP( 0L, "datafield", skipGroup, 0L, 0 ), // Binary data in variables are not supported
|
|
MEMBER( "@rtf", "deff", setNumericProperty, defaultFont, 0 ),
|
|
MEMBER( "@rtf", "deftab", setNumericProperty, defaultTab, 0 ),
|
|
PROP( "@pict", "dibitmap", setPictureType, 0L, RTFPicture::BMP ),
|
|
MEMBER( 0L, "dn", setNumericProperty, state.format.baseline, 6 ),
|
|
PROP( 0L, "emdash", insertSymbol, 0L, 0x2014 ),
|
|
PROP( "@pict", "emfblip", setPictureType, 0L, RTFPicture::EMF ),
|
|
PROP( 0L, "emspace", insertSymbol, 0L, 0x2003 ),
|
|
PROP( 0L, "endash", insertSymbol, 0L, 0x2013 ),
|
|
PROP( 0L, "enspace", insertSymbol, 0L, 0x2002 ),
|
|
PROP( 0L, "expnd", ignoreKeyword, 0L, 0 ), // Expansion/compression of character inter-space not supported
|
|
PROP( 0L, "expndtw", ignoreKeyword, 0L, 0 ), // Expansion/compression of character inter-space not supported
|
|
MEMBER( 0L, "f", setNumericProperty, state.format.font, 0 ),
|
|
MEMBER( "@rtf", "facingp", setFlagProperty, facingPages, true ),
|
|
PROP( 0L, "fcharset", setCharset, 0L, 0 ), // Not needed with TQt
|
|
PROP( "@fonttbl", "fdecor", setFontStyleHint, 0L, TQFont::Decorative ),
|
|
MEMBER( 0L, "fi", setNumericProperty, state.layout.firstIndent, 0 ),
|
|
PROP( "@fonttbl", "fmodern", setFontStyleHint, 0L, TQFont::TypeWriter ),
|
|
PROP( "@fonttbl", "fnil", setFontStyleHint, 0L, TQFont::AnyStyle ),
|
|
MEMBER( 0L, "footery", setNumericProperty, state.section.footerMargin, 0 ),
|
|
PROP( 0L, "formshade", ignoreKeyword, 0L, 0 ), // Not supported, KWord has no form support
|
|
MEMBER( "@fonttbl", "fprq", setNumericProperty, font.fixedPitch, 0 ),
|
|
PROP( "@fonttbl", "froman", setFontStyleHint, 0L, TQFont::Serif ),
|
|
MEMBER( 0L, "fs", setNumericProperty, state.format.fontSize, 0 ),
|
|
PROP( "@fonttbl", "fscript", setFontStyleHint, 0L, TQFont::AnyStyle ),
|
|
PROP( "@fonttbl", "fswiss", setFontStyleHint, 0L, TQFont::SansSerif ),
|
|
PROP( "@fonttbl", "ftech", setFontStyleHint, 0L, TQFont::AnyStyle ),
|
|
MEMBER( "@colortbl", "green", setNumericProperty, green, 0 ),
|
|
MEMBER( 0L, "headery", setNumericProperty, state.section.headerMargin, 0 ),
|
|
MEMBER( 0L, "i", setToggleProperty, state.format.italic, 0 ),
|
|
MEMBER( 0L, "intbl", setFlagProperty, state.layout.inTable, true ),
|
|
PROP( "@pict", "jpegblip", setPictureType, 0L, RTFPicture::JPEG ),
|
|
MEMBER( 0L, "keep", setFlagProperty, state.layout.keep, true ),
|
|
MEMBER( 0L, "keepn", setFlagProperty, state.layout.keepNext, true ),
|
|
MEMBER( "@rtf", "landscape", setFlagProperty, landscape, true ),
|
|
PROP( 0L, "ldblquote", insertSymbol, 0L, 0x201c ),
|
|
MEMBER( 0L, "li", setNumericProperty, state.layout.leftIndent, 0 ),
|
|
PROP( 0L, "line", insertSymbol, 0L, 0x000a ),
|
|
PROP( 0L, "lquote", insertSymbol, 0L, 0x2018 ),
|
|
PROP( 0L, "ltrmark", insertSymbol, 0L, 0x200e ),
|
|
PROP( 0L, "mac", setMacCodepage, 0L, 0 ),
|
|
PROP( "@pict", "macpict", setPictureType, 0L, RTFPicture::MacPict ),
|
|
MEMBER( "@rtf", "margb", setNumericProperty, bottomMargin, 0 ),
|
|
MEMBER( "@rtf", "margl", setNumericProperty, leftMargin, 0 ),
|
|
MEMBER( "@rtf", "margr", setNumericProperty, rightMargin, 0 ),
|
|
MEMBER( "@rtf", "margt", setNumericProperty, topMargin, 0 ),
|
|
MEMBER( 0L, "nosupersub", setEnumProperty, state.format.vertAlign, RTFFormat::Normal ),
|
|
PROP( "Text", "page", insertPageBreak, 0L, 0 ),
|
|
MEMBER( 0L, "pagebb", setFlagProperty, state.layout.pageBB, true ),
|
|
MEMBER( "@rtf", "paperh", setNumericProperty, paperHeight, 0 ),
|
|
MEMBER( "@rtf", "paperw", setNumericProperty, paperWidth, 0 ),
|
|
PROP( "Text", "par", insertParagraph, 0L, 0 ),
|
|
PROP( 0L, "pard", setParagraphDefaults, 0L, 0 ),
|
|
PROP( 0L, "pc", setPcCodepage, 0L, 0 ),
|
|
PROP( 0L, "pca", setPcaCodepage, 0L, 0 ),
|
|
MEMBER( 0L, "pgbrk", setToggleProperty, state.layout.pageBA, true ),
|
|
MEMBER( "@pict", "piccropb", setNumericProperty, picture.cropBottom, 0 ),
|
|
MEMBER( "@pict", "piccropl", setNumericProperty, picture.cropLeft, 0 ),
|
|
MEMBER( "@pict", "piccropr", setNumericProperty, picture.cropRight, 0 ),
|
|
MEMBER( "@pict", "piccropt", setNumericProperty, picture.cropTop, 0 ),
|
|
MEMBER( "@pict", "pich", setNumericProperty, picture.height, 0 ),
|
|
MEMBER( "@pict", "pichgoal", setNumericProperty, picture.desiredHeight, 0 ),
|
|
MEMBER( "@pict", "picscaled", setFlagProperty, picture.scaled, true ),
|
|
MEMBER( "@pict", "picscalex", setNumericProperty, picture.scalex, 0 ),
|
|
MEMBER( "@pict", "picscaley", setNumericProperty, picture.scaley, 0 ),
|
|
MEMBER( "@pict", "picw", setNumericProperty, picture.width, 0 ),
|
|
MEMBER( "@pict", "picwgoal", setNumericProperty, picture.desiredWidth, 0 ),
|
|
PROP( 0L, "plain", setPlainFormatting, 0L, 0 ),
|
|
PROP( "@pict", "pmmetafile", setPictureType, 0L, RTFPicture::WMF ),
|
|
PROP( "@pict", "pngblip", setPictureType, 0L, RTFPicture::PNG ),
|
|
MEMBER( 0L, "qc", setEnumProperty, state.layout.alignment, RTFLayout::Centered ),
|
|
MEMBER( 0L, "qj", setEnumProperty, state.layout.alignment, RTFLayout::Justified ),
|
|
MEMBER( 0L, "ql", setEnumProperty, state.layout.alignment, RTFLayout::Left ),
|
|
PROP( 0L, "qmspace", insertSymbol, 0L, 0x2004 ),
|
|
MEMBER( 0L, "qr", setEnumProperty, state.layout.alignment, RTFLayout::Right ),
|
|
PROP( 0L, "rdblquote", insertSymbol, 0L, 0x201d ),
|
|
MEMBER( "@colortbl", "red", setNumericProperty, red, 0 ),
|
|
MEMBER( 0L, "ri", setNumericProperty, state.layout.rightIndent, 0 ),
|
|
PROP( "Text", "row", insertTableRow, 0L, 0 ),
|
|
PROP( 0L, "rquote", insertSymbol, 0L, 0x2019 ),
|
|
PROP( 0L, "rtlmark", insertSymbol, 0L, 0x200f ),
|
|
MEMBER( 0L, "s", setNumericProperty, state.layout.style, 0 ),
|
|
MEMBER( 0L, "sa", setNumericProperty, state.layout.spaceAfter, 0 ),
|
|
MEMBER( 0L, "sb", setNumericProperty, state.layout.spaceBefore, 0 ),
|
|
MEMBER( 0L, "scaps", setToggleProperty, state.format.smallCaps, 0 ),
|
|
PROP( "Text", "sect", insertPageBreak, 0L, 0 ),
|
|
PROP( 0L, "sectd", setSectionDefaults, 0L, 0 ),
|
|
MEMBER( 0L, "sl", setNumericProperty, state.layout.spaceBetween, 0 ),
|
|
MEMBER( 0L, "slmult", setToggleProperty, state.layout.spaceBetweenMultiple, 0 ),
|
|
MEMBER( "@stylesheet", "snext", setNumericProperty, style.next, 0 ),
|
|
MEMBER( 0L, "strike", setToggleProperty, state.format.strike, 0 ),
|
|
MEMBER( 0L, "striked", setToggleProperty, state.format.striked, 0 ),
|
|
MEMBER( 0L, "sub", setEnumProperty, state.format.vertAlign, RTFFormat::SubScript ),
|
|
MEMBER( 0L, "super", setEnumProperty, state.format.vertAlign, RTFFormat::SuperScript ),
|
|
PROP( 0L, "tab", insertSymbol, 0L, 0x0009 ),
|
|
MEMBER( 0L, "titlepg", setFlagProperty, state.section.titlePage, true ),
|
|
MEMBER( 0L, "tldot", setEnumProperty, state.layout.tab.leader, RTFTab::Dots ),
|
|
MEMBER( 0L, "tlhyph", setEnumProperty, state.layout.tab.leader, RTFTab::Hyphens ),
|
|
MEMBER( 0L, "tlth", setEnumProperty, state.layout.tab.leader, RTFTab::ThickLine ),
|
|
MEMBER( 0L, "tlul", setEnumProperty, state.layout.tab.leader, RTFTab::Underline ),
|
|
MEMBER( 0L, "tqc", setEnumProperty, state.layout.tab.type, RTFTab::Centered ),
|
|
MEMBER( 0L, "tqdec", setEnumProperty, state.layout.tab.type, RTFTab::Decimal ),
|
|
MEMBER( 0L, "tqr", setEnumProperty, state.layout.tab.type, RTFTab::FlushRight ),
|
|
MEMBER( 0L, "trleft", setNumericProperty, state.tableRow.left, 0 ),
|
|
MEMBER( 0L, "trowd", setTableRowDefaults, state.tableRow, 0 ),
|
|
MEMBER( 0L, "trqc", setEnumProperty, state.tableRow.alignment, RTFLayout::Centered ),
|
|
MEMBER( 0L, "trql", setEnumProperty, state.tableRow.alignment, RTFLayout::Left ),
|
|
MEMBER( 0L, "trqr", setEnumProperty, state.tableRow.alignment, RTFLayout::Right ),
|
|
MEMBER( 0L, "trrh", setNumericProperty, state.tableRow.height, 0 ),
|
|
PROP( 0L, "tx", insertTabDef, 0L, 0 ),
|
|
MEMBER( 0L, "u", insertUnicodeSymbol, state.format.uc, 0 ),
|
|
MEMBER( 0L, "uc", setNumericProperty, state.format.uc, 0 ),
|
|
PROP( 0L, "ul", setSimpleUnderlineProperty, 0L, 0 ),
|
|
MEMBER( 0L, "ulc", setNumericProperty, state.format.underlinecolor, 0 ),
|
|
PROP( 0L, "uld", setUnderlineProperty, 0L, RTFFormat::UnderlineDot ),
|
|
PROP( 0L, "uldash", setUnderlineProperty, 0L, RTFFormat::UnderlineDash ),
|
|
PROP( 0L, "uldashd", setUnderlineProperty, 0L, RTFFormat::UnderlineDashDot ),
|
|
PROP( 0L, "uldashdd", setUnderlineProperty, 0L, RTFFormat::UnderlineDashDotDot ),
|
|
PROP( 0L, "uldb", setUnderlineProperty, 0L, RTFFormat::UnderlineDouble ),
|
|
PROP( 0L, "ulnone", setUnderlineProperty, 0L, RTFFormat::UnderlineNone ),
|
|
PROP( 0L, "ulth", setUnderlineProperty, 0L, RTFFormat::UnderlineThick ),
|
|
PROP( 0L, "ulw", setUnderlineProperty, 0L, RTFFormat::UnderlineWordByWord ),
|
|
PROP( 0L, "ulwave", setUnderlineProperty, 0L, RTFFormat::UnderlineWave ),
|
|
PROP( 0L, "ulhwave", setUnderlineProperty, 0L, RTFFormat::UnderlineWave ),
|
|
PROP( 0L, "ululdbwave", setUnderlineProperty, 0L, RTFFormat::UnderlineWave ),
|
|
MEMBER( 0L, "up", setUpProperty, state.format.baseline, 6 ),
|
|
MEMBER( 0L, "v", setToggleProperty, state.format.hidden, 0 ),
|
|
// ### TODO: \wbitmap: a Windows Device-Dependant Bitmap is not a BMP
|
|
PROP( "@pict", "wbitmap", setPictureType, 0L, RTFPicture::BMP ),
|
|
PROP( "@pict", "wmetafile", setPictureType, 0L, RTFPicture::EMF ),
|
|
PROP( 0L, "zwj", insertSymbol, 0L, 0x200d ),
|
|
PROP( 0L, "zwnj", insertSymbol, 0L, 0x200c )
|
|
};
|
|
|
|
static RTFField fieldTable[] =
|
|
{
|
|
// id type subtype default value
|
|
{ "AUTHOR", 8, 2, "NO AUTHOR" },
|
|
{ "FILENAME", 8, 0, "NO FILENAME" },
|
|
{ "TITLE", 8, 10, "NO TITLE" },
|
|
{ "NUMPAGES", 4, 1, 0 },
|
|
{ "PAGE", 4, 0, 0 },
|
|
{ "TIME", -1, -1, 0 },
|
|
{ "DATE", -1, -1, 0 },
|
|
{ "HYPERLINK", 9, -1, 0 },
|
|
{ "SYMBOL", -1, -1, 0 },
|
|
{ "IMPORT", -1, -1, 0 }
|
|
};
|
|
|
|
|
|
// KWord attributes
|
|
static const char *alignN[4] = { "left", "right", "justify", "center" };
|
|
static const char *boolN[2] = { "false", "true" };
|
|
static const char *borderN[4] = { "LEFTBORDER", "RIGHTBORDER", "TOPBORDER", "BOTTOMBORDER" };
|
|
|
|
RTFImport::RTFImport( KoFilter *, const char *, const TQStringList& )
|
|
: KoFilter(), properties(181), destinationProperties(29), textCodec(0), utf8TextCodec(0)
|
|
{
|
|
for (uint i=0; i < sizeof(propertyTable) / sizeof(propertyTable[0]); i++)
|
|
{
|
|
properties.insert( propertyTable[i].name, &propertyTable[i] );
|
|
}
|
|
for (uint i=0; i < sizeof(destinationPropertyTable) / sizeof(destinationPropertyTable[0]); i++)
|
|
{
|
|
destinationProperties.insert( destinationPropertyTable[i].name, &destinationPropertyTable[i] );
|
|
}
|
|
// DEBUG START
|
|
// Check the hash size (see TQDict doc)
|
|
kdDebug(30515) << properties.count() << " normal and " << destinationProperties.count() << " destination keywords loaded" << endl;
|
|
if (properties.size() < properties.count())
|
|
kdWarning(30515) << "Hash size of properties too small: " << properties.size() << ". It should be at least " << properties.count() << " and be a prime number"<< endl;
|
|
if (destinationProperties.size() < destinationProperties.count())
|
|
kdWarning(30515) << "Hash size of destinationProperties too small: " << destinationProperties.size() << ". It should be at least " << destinationProperties.count() << " and be a prime number"<< endl;
|
|
// DEBUG END
|
|
fnnum=0;
|
|
}
|
|
|
|
KoFilter::ConversionStatus RTFImport::convert( const TQCString& from, const TQCString& to )
|
|
{
|
|
// This filter only supports RTF to KWord conversion
|
|
if ((from != "text/rtf") || (to != "application/x-kword"))
|
|
return KoFilter::NotImplemented;
|
|
|
|
TQTime debugTime;
|
|
debugTime.start();
|
|
|
|
// Are we in batch mode, i.e. non-interactive
|
|
m_batch=false;
|
|
if ( m_chain->manager() )
|
|
m_batch = m_chain->manager()->getBatchMode();
|
|
|
|
// Open input file
|
|
inFileName = m_chain->inputFile();
|
|
TQFile in( inFileName );
|
|
|
|
if (!in.open( IO_ReadOnly ))
|
|
{
|
|
kdError(30515) << "Unable to open input file!" << endl;
|
|
in.close();
|
|
if ( !m_batch )
|
|
{
|
|
KMessageBox::error( 0L,
|
|
i18n("The file cannot be loaded, as it cannot be opened."),
|
|
i18n("KWord's RTF Import Filter"), 0 );
|
|
}
|
|
return KoFilter::FileNotFound;
|
|
}
|
|
|
|
// Document should start with an opening brace
|
|
token.open( &in );
|
|
token.next();
|
|
|
|
if (token.type != RTFTokenizer::OpenGroup)
|
|
{
|
|
kdError(30515) << "Not an RTF file" << endl;
|
|
in.close();
|
|
if ( !m_batch )
|
|
{
|
|
KMessageBox::error( 0L,
|
|
i18n("The file cannot be loaded, as it seems not to be an RTF document."),
|
|
i18n("KWord's RTF Import Filter"), 0 );
|
|
}
|
|
return KoFilter::WrongFormat;
|
|
}
|
|
|
|
// Verify document type and version (RTF version 1.x)
|
|
|
|
token.next();
|
|
|
|
if (token.type != RTFTokenizer::ControlWord)
|
|
{
|
|
kdError(30515) << "Wrong document type" << endl;
|
|
in.close();
|
|
if ( !m_batch )
|
|
{
|
|
KMessageBox::error( 0L,
|
|
i18n("The document cannot be loaded, as it seems not to follow the RTF syntax."),
|
|
i18n("KWord's RTF Import Filter"), 0 );
|
|
}
|
|
return KoFilter::WrongFormat;
|
|
}
|
|
|
|
bool force = false; // By default do not force, despite an unknown keyword or version
|
|
if ( !qstrcmp( token.text, "rtf" ) )
|
|
{
|
|
// RTF is normally version 1 but at least Ted uses 0 as version number
|
|
if ( token.value > 1 )
|
|
{
|
|
kdError(30515) << "Wrong RTF version (" << token.value << "); version 0 or 1 expected" << endl;
|
|
if ( !m_batch )
|
|
{
|
|
force = ( KMessageBox::warningYesNo( 0L,
|
|
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 ),
|
|
i18n("KWord's RTF Import Filter") ) == KMessageBox::Yes );
|
|
}
|
|
if ( !force )
|
|
{
|
|
in.close();
|
|
return KoFilter::WrongFormat;
|
|
}
|
|
}
|
|
}
|
|
else if ( !qstrcmp( token.text, "pwd" ) )
|
|
{
|
|
// PocketWord's PWD format is similar to RTF but has a version number of 2.
|
|
if ( token.value != 2 )
|
|
{
|
|
kdError(30515) << "Wrong PWD version (" << token.value << "); version 2 expected" << endl;
|
|
if ( !m_batch )
|
|
{
|
|
force = ( KMessageBox::warningYesNo( 0L,
|
|
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 ),
|
|
i18n("KWord's RTF Import Filter") ) == KMessageBox::Yes );
|
|
}
|
|
if ( !force )
|
|
{
|
|
in.close();
|
|
return KoFilter::WrongFormat;
|
|
}
|
|
}
|
|
}
|
|
else if ( !qstrcmp( token.text, "urtf" ) )
|
|
{
|
|
// URTF seems to have either no version or having version 1
|
|
if ( token.value > 1 )
|
|
{
|
|
kdError(30515) << "Wrong URTF version (" << token.value << "); version 0 or 1 expected" << endl;
|
|
if ( !m_batch )
|
|
{
|
|
force = ( KMessageBox::warningYesNo( 0L,
|
|
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 ),
|
|
i18n("KWord's RTF Import Filter") ) == KMessageBox::Yes );
|
|
}
|
|
if ( !force )
|
|
{
|
|
in.close();
|
|
return KoFilter::WrongFormat;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
kdError(30515) << "Wrong RTF document type (\\" << token.text << "); \\rtf, \\pwd or \\urtf expected" << endl;
|
|
in.close();
|
|
if ( !m_batch )
|
|
{
|
|
KMessageBox::error( 0L,
|
|
i18n("The RTF document cannot be loaded, as it has an unexpected first keyword: \\%1.").arg( token.text ),
|
|
i18n("KWord's RTF Import Filter"), 0 );
|
|
}
|
|
return KoFilter::WrongFormat;
|
|
}
|
|
|
|
table = 0;
|
|
pictureNumber = 0;
|
|
|
|
// Document-formatting properties
|
|
paperWidth = 12240;
|
|
paperHeight = 15840;
|
|
leftMargin = 1800;
|
|
topMargin = 1440;
|
|
rightMargin = 1800;
|
|
bottomMargin= 1440;
|
|
defaultTab = 720;
|
|
defaultFont = 0;
|
|
landscape = false;
|
|
facingPages = false;
|
|
|
|
// Create main document
|
|
frameSets.clear( 2 );
|
|
pictures.clear();
|
|
bodyText.node.clear( 3 );
|
|
firstPageHeader.node.clear( 3 );
|
|
oddPagesHeader.node.clear( 3 );
|
|
evenPagesHeader.node.clear( 3 );
|
|
firstPageFooter.node.clear( 3 );
|
|
oddPagesFooter.node.clear( 3 );
|
|
evenPagesFooter.node.clear( 3 );
|
|
author.clear();
|
|
company.clear();
|
|
title.clear();
|
|
doccomm.clear();
|
|
|
|
stateStack.push( state );
|
|
|
|
// Add a security item for the destination stack
|
|
destination.name = "!stackbottom";
|
|
|
|
changeDestination( destinationProperties["@rtf"] );
|
|
|
|
flddst = -1;
|
|
emptyCell = state.tableCell;
|
|
state.format.uc=1;
|
|
state.ignoreGroup = false;
|
|
|
|
utf8TextCodec=TQTextCodec::codecForName("UTF-8");
|
|
kdDebug(30515) << "UTF-8 asked, given: " << (utf8TextCodec?TQString(utf8TextCodec->name()):TQString("-none-")) << endl;
|
|
|
|
// There is no default encoding in RTF, it must be always declared. (But beware of buggy files!)
|
|
textCodec=TQTextCodec::codecForName("CP 1252"); // Or IBM 437 ?
|
|
kdDebug(30515) << "CP 1252 asked, given: " << (textCodec?TQString(textCodec->name()):TQString("-none-")) << endl;
|
|
|
|
// Parse RTF document
|
|
while (true)
|
|
{
|
|
bool firstToken = false;
|
|
bool ignoreUnknown = false;
|
|
|
|
token.next();
|
|
|
|
while (token.type == RTFTokenizer::OpenGroup)
|
|
{
|
|
// Store the current state on the stack
|
|
stateStack.push( state );
|
|
state.brace0 = false;
|
|
firstToken = true;
|
|
ignoreUnknown = false;
|
|
|
|
token.next();
|
|
|
|
if (token.type == RTFTokenizer::ControlWord && !qstrcmp( token.text, "*" ))
|
|
{
|
|
// {\*\control ...} destination
|
|
ignoreUnknown = true;
|
|
token.next();
|
|
}
|
|
}
|
|
if (token.type == RTFTokenizer::CloseGroup)
|
|
{
|
|
if (state.brace0)
|
|
{
|
|
// Close the current destination
|
|
(this->*destination.destproc)(0L);
|
|
//kdDebug(30515) << "Closing destination... " << destinationStack.count() << endl;
|
|
if (destinationStack.isEmpty())
|
|
{
|
|
kdWarning(30515) << "Destination stack is empty! Document might be buggy!" << endl;
|
|
// Keep the destination to save what can still be saved!
|
|
}
|
|
else
|
|
{
|
|
destination = destinationStack.pop();
|
|
}
|
|
}
|
|
// ### TODO: why can this not be simplified to use TQValueList::isEmpty()
|
|
if (stateStack.count() <= 1)
|
|
{
|
|
// End-of-document, keep formatting properties
|
|
stateStack.pop();
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
// Retrieve the current state from the stack
|
|
state = stateStack.pop();
|
|
}
|
|
}
|
|
else if (token.type == RTFTokenizer::ControlWord)
|
|
{
|
|
RTFProperty *property = properties[token.text];
|
|
|
|
if (property != 0L)
|
|
{
|
|
if (property->onlyValidIn == 0L ||
|
|
property->onlyValidIn == destination.name ||
|
|
property->onlyValidIn == destination.group)
|
|
{
|
|
(this->*property->cwproc)( property );
|
|
}
|
|
}
|
|
else if (firstToken)
|
|
{
|
|
// Possible destination change
|
|
*(--token.text) = '@';
|
|
property = destinationProperties[token.text];
|
|
|
|
if ((property != 0L) &&
|
|
(property->onlyValidIn == 0L ||
|
|
property->onlyValidIn == destination.name ||
|
|
property->onlyValidIn == destination.group))
|
|
{
|
|
// Change destination
|
|
changeDestination( property );
|
|
}
|
|
else if (ignoreUnknown)
|
|
{
|
|
// Skip unknown {\* ...} destination
|
|
changeDestination( destinationProperties["@*"] );
|
|
debugUnknownKeywords[token.text]++;
|
|
}
|
|
else if ( !property )
|
|
{
|
|
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;
|
|
// Put the second warning separately, as it can crash if destination.name is dangling
|
|
kdWarning(30515) << " Assuming destination: " << destination.name << endl;
|
|
debugUnknownKeywords[token.text]++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
debugUnknownKeywords[token.text]++;
|
|
}
|
|
}
|
|
else if (token.type == RTFTokenizer::PlainText || token.type == RTFTokenizer::BinaryData)
|
|
{
|
|
(this->*destination.destproc)(0L);
|
|
}
|
|
}
|
|
|
|
// Determine header and footer type
|
|
const int hType = facingPages
|
|
? (state.section.titlePage ? 3 : 1) : (state.section.titlePage ? 2 : 0);
|
|
|
|
const bool hasHeader = !oddPagesHeader.node.isEmpty() ||
|
|
(facingPages &&!evenPagesHeader.node.isEmpty()) ||
|
|
(state.section.titlePage && !firstPageHeader.node.isEmpty());
|
|
const bool hasFooter = !oddPagesFooter.node.isEmpty() ||
|
|
(facingPages && !evenPagesFooter.node.isEmpty()) ||
|
|
(state.section.titlePage && !firstPageFooter.node.isEmpty());
|
|
|
|
kdDebug(30515) << "hType " << hType << " hasHeader " << hasHeader << " hasFooter " << hasFooter << endl;
|
|
|
|
// Create main document
|
|
DomNode mainDoc( "DOC" );
|
|
mainDoc.setAttribute( "mime", "application/x-kword" );
|
|
mainDoc.setAttribute( "syntaxVersion", "3" );
|
|
mainDoc.setAttribute( "editor", "KWord's RTF Import Filter" );
|
|
mainDoc.addNode( "PAPER" );
|
|
mainDoc.setAttribute( "format", 6 );
|
|
mainDoc.setAttribute( "columns", 1 );
|
|
mainDoc.setAttribute( "columnspacing", 2 );
|
|
mainDoc.setAttribute( "spHeadBody", 4 );
|
|
mainDoc.setAttribute( "spFootBody", 4 );
|
|
mainDoc.setAttribute( "zoom", 100 );
|
|
mainDoc.setAttribute( "width", .05*paperWidth );
|
|
mainDoc.setAttribute( "height", .05*paperHeight );
|
|
mainDoc.setAttribute( "orientation", landscape );
|
|
mainDoc.setAttribute( "hType", hType );
|
|
mainDoc.setAttribute( "fType", hType );
|
|
mainDoc.addNode( "PAPERBORDERS" );
|
|
mainDoc.addRect( leftMargin,
|
|
(hasHeader ? state.section.headerMargin : topMargin),
|
|
rightMargin,
|
|
(hasFooter ? state.section.footerMargin : bottomMargin) );
|
|
mainDoc.closeNode( "PAPERBORDERS" );
|
|
mainDoc.closeNode( "PAPER" );
|
|
mainDoc.addNode( "ATTRIBUTES" );
|
|
mainDoc.setAttribute( "standardpage", 1 );
|
|
mainDoc.setAttribute( "processing", 0 );
|
|
//mainDoc.setAttribute( "unit", "pt" ); // use KWord default instead
|
|
mainDoc.setAttribute( "hasHeader", hasHeader );
|
|
mainDoc.setAttribute( "hasFooter", hasFooter );
|
|
mainDoc.closeNode( "ATTRIBUTES" );
|
|
mainDoc.addNode( "FRAMESETS" );
|
|
mainDoc.addFrameSet( "Frameset 1", 1, 0 );
|
|
mainDoc.addFrame( leftMargin, topMargin, (paperWidth - rightMargin),
|
|
(paperHeight - bottomMargin), 1, 0, 0 );
|
|
mainDoc.closeNode( "FRAME" );
|
|
mainDoc.appendNode( bodyText.node );
|
|
mainDoc.closeNode( "FRAMESET" );
|
|
// Write out headers
|
|
if (hasHeader)
|
|
{
|
|
mainDoc.addFrameSet( "First Page Header", 1, 1 );
|
|
mainDoc.addFrame( leftMargin, state.section.headerMargin,
|
|
(paperWidth - rightMargin), (topMargin - 80), 0, 2, 0 );
|
|
mainDoc.closeNode( "FRAME" );
|
|
mainDoc.appendNode( firstPageHeader.node );
|
|
mainDoc.closeNode( "FRAMESET" );
|
|
mainDoc.addFrameSet( "Odd Pages Header", 1, 2 );
|
|
mainDoc.addFrame( leftMargin, state.section.headerMargin,
|
|
(paperWidth - rightMargin), (topMargin - 80), 0, 2, 1 );
|
|
mainDoc.closeNode( "FRAME" );
|
|
mainDoc.appendNode( oddPagesHeader.node );
|
|
mainDoc.closeNode( "FRAMESET" );
|
|
mainDoc.addFrameSet( "Even Pages Header", 1, 3 );
|
|
mainDoc.addFrame( leftMargin, state.section.headerMargin,
|
|
(paperWidth - rightMargin), (topMargin - 80), 0, 2, 2 );
|
|
mainDoc.closeNode( "FRAME" );
|
|
mainDoc.appendNode( evenPagesHeader.node );
|
|
mainDoc.closeNode( "FRAMESET" );
|
|
}
|
|
// Write out footers
|
|
if (hasFooter)
|
|
{
|
|
mainDoc.addFrameSet( "First Page Footer", 1, 4 );
|
|
mainDoc.addFrame( leftMargin, state.section.headerMargin,
|
|
(paperWidth - rightMargin), (topMargin - 80), 0, 2, 0 );
|
|
mainDoc.closeNode( "FRAME" );
|
|
mainDoc.appendNode( firstPageFooter.node );
|
|
mainDoc.closeNode( "FRAMESET" );
|
|
mainDoc.addFrameSet( "Odd Pages Footer", 1, 5 );
|
|
mainDoc.addFrame( leftMargin, state.section.headerMargin,
|
|
(paperWidth - rightMargin), (topMargin - 80), 0, 2, 1 );
|
|
mainDoc.closeNode( "FRAME" );
|
|
mainDoc.appendNode( oddPagesFooter.node );
|
|
mainDoc.closeNode( "FRAMESET" );
|
|
mainDoc.addFrameSet( "Even Pages Footer", 1, 6 );
|
|
mainDoc.addFrame( leftMargin, state.section.headerMargin,
|
|
(paperWidth - rightMargin), (topMargin - 80), 0, 2, 2 );
|
|
mainDoc.closeNode( "FRAME" );
|
|
mainDoc.appendNode( evenPagesFooter.node );
|
|
mainDoc.closeNode( "FRAMESET" );
|
|
}
|
|
// Write out footnotes
|
|
int num=1;
|
|
for(RTFTextState* i=footnotes.first();i;i=footnotes.next())
|
|
{
|
|
TQCString str;
|
|
str.setNum(num);
|
|
str.prepend("Footnote ");
|
|
num++;
|
|
mainDoc.addFrameSet( str, 1, 7 );
|
|
mainDoc.addFrame( leftMargin, paperHeight - bottomMargin-80,
|
|
(paperWidth - rightMargin), paperHeight-bottomMargin, 0, 2, 0 );
|
|
mainDoc.closeNode( "FRAME" );
|
|
mainDoc.appendNode( i->node );
|
|
mainDoc.closeNode( "FRAMESET" );
|
|
}
|
|
mainDoc.appendNode( frameSets );
|
|
mainDoc.closeNode( "FRAMESETS" );
|
|
mainDoc.addNode( "PICTURES" );
|
|
mainDoc.appendNode( pictures );
|
|
mainDoc.closeNode( "PICTURES" );
|
|
mainDoc.addNode( "STYLES" );
|
|
kwFormat.id = 1;
|
|
kwFormat.pos = 0;
|
|
kwFormat.len = 0;
|
|
|
|
// Process all styles in the style sheet
|
|
const TQValueList<RTFStyle>::ConstIterator endStyleSheet=styleSheet.end();
|
|
for (TQValueList<RTFStyle>::ConstIterator it=styleSheet.begin();it!=endStyleSheet;++it)
|
|
{
|
|
mainDoc.addNode( "STYLE" );
|
|
kwFormat.fmt = (*it).format;
|
|
|
|
// Search for 'following' style
|
|
for (TQValueList<RTFStyle>::ConstIterator it2=styleSheet.begin();it2!=endStyleSheet;++it2)
|
|
{
|
|
if ((*it2).layout.style == (*it).next)
|
|
{
|
|
mainDoc.addNode( "FOLLOWING" );
|
|
mainDoc.setAttribute( "name", CheckAndEscapeXmlText( (*it2).name ));
|
|
mainDoc.closeNode( "FOLLOWING");
|
|
break;
|
|
}
|
|
}
|
|
addLayout( mainDoc, (*it).name, (*it).layout, false );
|
|
addFormat( mainDoc, kwFormat, 0L );
|
|
mainDoc.closeNode( "STYLE" );
|
|
}
|
|
mainDoc.closeNode( "STYLES" );
|
|
mainDoc.closeNode( "DOC" );
|
|
|
|
// Create document info
|
|
DomNode docInfo( "document-info" );
|
|
docInfo.addNode( "log" );
|
|
docInfo.addNode( "text" );
|
|
docInfo.closeNode( "text" );
|
|
docInfo.closeNode( "log" );
|
|
docInfo.addNode( "author" );
|
|
docInfo.addNode( "company" );
|
|
docInfo.appendNode( company );
|
|
docInfo.closeNode( "company" );
|
|
docInfo.addNode( "full-name" );
|
|
docInfo.appendNode( author );
|
|
docInfo.closeNode( "full-name" );
|
|
docInfo.addNode( "email" );
|
|
docInfo.closeNode( "email" );
|
|
docInfo.addNode( "telephone" );
|
|
docInfo.closeNode( "telephone" );
|
|
docInfo.addNode( "fax" );
|
|
docInfo.closeNode( "fax" );
|
|
docInfo.addNode( "country" );
|
|
docInfo.closeNode( "country" );
|
|
docInfo.addNode( "postal-code" );
|
|
docInfo.closeNode( "postal-code" );
|
|
docInfo.addNode( "city" );
|
|
docInfo.closeNode( "city" );
|
|
docInfo.addNode( "street" );
|
|
docInfo.closeNode( "street" );
|
|
docInfo.closeNode( "author" );
|
|
docInfo.addNode( "about" );
|
|
docInfo.addNode( "abstract" );
|
|
docInfo.appendNode( doccomm );
|
|
docInfo.closeNode( "abstract" );
|
|
docInfo.addNode( "title" );
|
|
docInfo.appendNode( title );
|
|
docInfo.closeNode( "title" );
|
|
docInfo.closeNode( "about" );
|
|
docInfo.closeNode( "document-info" );
|
|
|
|
// Write out main document and document info
|
|
writeOutPart( "root", mainDoc );
|
|
writeOutPart( "documentinfo.xml", docInfo );
|
|
in.close();
|
|
|
|
kdDebug(30515) << "RTF FILTER TIME: " << debugTime.elapsed() << endl;
|
|
|
|
for (TQMap<TQString,int>::ConstIterator it=debugUnknownKeywords.begin();
|
|
it!=debugUnknownKeywords.end();it++)
|
|
kdDebug(30515) << "Unknown keyword: " << TQString( "%1" ).arg( it.data(), 4 ) << " * " << it.key() << endl;
|
|
|
|
return KoFilter::OK;
|
|
}
|
|
|
|
void RTFImport::ignoreKeyword( RTFProperty * )
|
|
{
|
|
}
|
|
|
|
void RTFImport::setCodepage( RTFProperty * )
|
|
{
|
|
TQTextCodec* oldCodec = textCodec;
|
|
TQCString cp;
|
|
if ( token.value == 10000 )
|
|
{
|
|
cp = "Apple Roman"; // ### TODO: how to support the other ones (TQt does not know them!)
|
|
}
|
|
else
|
|
{
|
|
cp.setNum( token.value );
|
|
cp.prepend("CP");
|
|
}
|
|
textCodec=TQTextCodec::codecForName(cp);
|
|
kdDebug(30515) << "\\ansicpg: codepage: " << token.value << "asked: "<< cp << " given: " << (textCodec?TQString(textCodec->name()):TQString("-none-")) << endl;
|
|
if ( ! textCodec )
|
|
textCodec = oldCodec;
|
|
}
|
|
|
|
void RTFImport::setMacCodepage( RTFProperty * )
|
|
{
|
|
TQTextCodec* oldCodec = textCodec;
|
|
textCodec=TQTextCodec::codecForName("Apple Roman");
|
|
kdDebug(30515) << "\\mac " << (textCodec?TQString(textCodec->name()):TQString("-none-")) << endl;
|
|
if ( ! textCodec )
|
|
textCodec = oldCodec;
|
|
}
|
|
|
|
void RTFImport::setAnsiCodepage( RTFProperty * )
|
|
{
|
|
TQTextCodec* oldCodec = textCodec;
|
|
textCodec=TQTextCodec::codecForName("CP1252");
|
|
kdDebug(30515) << "\\ansi " << (textCodec?TQString(textCodec->name()):TQString("-none-")) << endl;
|
|
if ( ! textCodec )
|
|
textCodec = oldCodec;
|
|
}
|
|
|
|
void RTFImport::setPcaCodepage( RTFProperty * )
|
|
{
|
|
TQTextCodec* oldCodec = textCodec;
|
|
textCodec=TQTextCodec::codecForName("IBM 850"); // TQt writes the name with a space
|
|
kdDebug(30515) << "\\pca " << (textCodec?TQString(textCodec->name()):TQString("-none-")) << endl;
|
|
if ( ! textCodec )
|
|
textCodec = oldCodec;
|
|
}
|
|
|
|
void RTFImport::setPcCodepage( RTFProperty * )
|
|
{
|
|
TQTextCodec* oldCodec = textCodec;
|
|
textCodec=TQTextCodec::codecForName("IBM 850"); // This is an approximation
|
|
kdDebug(30515) << "\\pc (approximation) " << (textCodec?TQString(textCodec->name()):TQString("-none-")) << endl;
|
|
if ( ! textCodec )
|
|
textCodec = oldCodec;
|
|
}
|
|
|
|
void RTFImport::setToggleProperty( RTFProperty *property )
|
|
{
|
|
((bool *)this)[property->offset] = (!token.hasParam || token.value != 0);
|
|
}
|
|
|
|
void RTFImport::setFlagProperty( RTFProperty *property )
|
|
{
|
|
((bool *)this)[property->offset] = property->value;
|
|
}
|
|
|
|
void RTFImport::setCharset( RTFProperty *property )
|
|
{
|
|
TQCString cp;
|
|
switch(token.value) {
|
|
case 0: cp = "CP1252"; break; // ANSI_CHARSET
|
|
case 1: cp = "CP1252"; break; // DEFAULT_CHARSET
|
|
//case 2: cp = ""; break; // SYMBOL_CHARSET not supported yet.
|
|
case 77: cp = "Apple Roman"; break; // MAC_CHARSET
|
|
case 128: cp = "Shift-JIS"; break; // SHIFTJIS_CHARSET "CP932"
|
|
case 129: cp = "eucKR"; break; // HANGUL_CHARSET "CP949"
|
|
case 130: cp = "CP1361"; break; // JOHAB_CHARSET doesn't really seem to be supported by TQt :-(
|
|
case 134: cp = "GB2312"; break; // GB2312_CHARSET "CP936"
|
|
case 136: cp = "Big5-HKSCS"; break; // CHINESEBIG5_CHARSET "CP950"
|
|
case 161: cp = "CP1253"; break; // GREEK_CHARSET
|
|
case 162: cp = "CP1254"; break; // TURKISH_CHARSET
|
|
case 163: cp = "CP1258"; break; // VIETNAMESE_CHARSET
|
|
case 177: cp = "CP1255"; break; // HEBREW_CHARSET
|
|
case 178: cp = "CP1256"; break; // ARABIC_CHARSET / ARABICSIMPLIFIED_CHARSET
|
|
case 186: cp = "CP1257"; break; // BALTIC_CHARSET
|
|
case 204: cp = "CP1251"; break; // RUSSIAN_CHARSET / CYRILLIC_CHARSET
|
|
case 222: cp = "CP874"; break; // THAI_CHARSET
|
|
case 238: cp = "CP1250"; break; // EASTEUROPE_CHARSET / EASTERNEUROPE_CHARSET
|
|
case 255: cp = "CP850"; break; // OEM_CHARSET "IBM 850"
|
|
default: return;
|
|
}
|
|
TQTextCodec* oldCodec = textCodec;
|
|
textCodec=TQTextCodec::codecForName(cp);
|
|
kdDebug(30515) << "\\fcharset: charset: " << token.value << " codepage: "<< cp << " given: " << (textCodec?TQString(textCodec->name()):TQString("-none-")) << endl;
|
|
if ( ! textCodec )
|
|
textCodec = oldCodec;
|
|
}
|
|
|
|
void RTFImport::setNumericProperty( RTFProperty *property )
|
|
{
|
|
*((int *)(((char *)this) + property->offset)) = token.hasParam ? token.value : property->value;
|
|
}
|
|
|
|
void RTFImport::setEnumProperty( RTFProperty *property )
|
|
{
|
|
*((int *)(((char *)this) + property->offset)) = property->value;
|
|
}
|
|
|
|
void RTFImport::setFontStyleHint( RTFProperty* property )
|
|
{
|
|
font.styleHint = TQFont::StyleHint( property->value );
|
|
}
|
|
|
|
void RTFImport::setPictureType( RTFProperty* property )
|
|
{
|
|
picture.type = RTFPicture::PictureType( property->value );
|
|
}
|
|
|
|
void RTFImport::setSimpleUnderlineProperty( RTFProperty* )
|
|
{
|
|
state.format.underline
|
|
= (!token.hasParam || token.value != 0)
|
|
? RTFFormat::UnderlineSimple : RTFFormat::UnderlineNone;
|
|
}
|
|
|
|
void RTFImport::setUnderlineProperty( RTFProperty* property )
|
|
{
|
|
state.format.underline = RTFFormat::Underline( property->value );
|
|
}
|
|
|
|
void RTFImport::setBorderStyle( RTFProperty *property )
|
|
{
|
|
if (state.layout.border)
|
|
{
|
|
state.layout.border->style = static_cast <RTFBorder::BorderStyle> ( property->value );
|
|
}
|
|
else
|
|
{
|
|
for (uint i=0; i < 4; i++)
|
|
{
|
|
state.layout.borders[i].style = static_cast <RTFBorder::BorderStyle> ( property->value );
|
|
}
|
|
}
|
|
}
|
|
|
|
void RTFImport::setBorderProperty( RTFProperty *property )
|
|
{
|
|
//kdDebug() << "setBorderProperty: " << endl;
|
|
if (state.layout.border)
|
|
{
|
|
state.layout.border->width = token.value;
|
|
}
|
|
else
|
|
{
|
|
for (uint i=0; i < 4; i++)
|
|
{
|
|
state.layout.borders[i].width = token.value;
|
|
}
|
|
}
|
|
}
|
|
|
|
void RTFImport::setBorderColor( RTFProperty * )
|
|
{
|
|
if (state.layout.border)
|
|
{
|
|
state.layout.border->color = token.value;
|
|
}
|
|
else
|
|
{
|
|
for (uint i=0; i < 4; i++)
|
|
{
|
|
state.layout.borders[i].color = token.value;
|
|
}
|
|
}
|
|
}
|
|
|
|
void RTFImport::setUpProperty( RTFProperty * )
|
|
{
|
|
state.format.baseline = token.hasParam ? -token.value : -6;
|
|
}
|
|
|
|
void RTFImport::setPlainFormatting( RTFProperty * )
|
|
{
|
|
RTFFormat &format = state.format;
|
|
|
|
format.font = defaultFont;
|
|
format.fontSize = 24;
|
|
format.baseline = 0;
|
|
format.color = -1;
|
|
format.bgcolor = -1;
|
|
format.underlinecolor = -1;
|
|
format.vertAlign = RTFFormat::Normal;
|
|
format.bold = false;
|
|
format.italic = false;
|
|
format.strike = false;
|
|
format.striked = false;
|
|
format.hidden = false;
|
|
format.caps = false;
|
|
format.smallCaps = false;
|
|
|
|
format.underline = RTFFormat::UnderlineNone;
|
|
|
|
// Do not reset format.uc !
|
|
}
|
|
|
|
void RTFImport::setParagraphDefaults( RTFProperty * )
|
|
{
|
|
RTFLayout &layout = state.layout;
|
|
|
|
layout.tablist.clear();
|
|
layout.tab.type = RTFTab::Left;
|
|
layout.tab.leader = RTFTab::None;
|
|
|
|
for (uint i=0; i < 4; i++)
|
|
{
|
|
RTFBorder &border = layout.borders[i];
|
|
border.color = -1;
|
|
border.width = 0;
|
|
border.style = RTFBorder::None;
|
|
}
|
|
layout.firstIndent = 0;
|
|
layout.leftIndent = 0;
|
|
layout.rightIndent = 0;
|
|
layout.spaceBefore = 0;
|
|
layout.spaceAfter = 0;
|
|
layout.spaceBetween = 0;
|
|
layout.spaceBetweenMultiple = false;
|
|
layout.style = 0;
|
|
layout.alignment = RTFLayout::Left;
|
|
layout.border = 0L;
|
|
layout.inTable = false;
|
|
layout.keep = false;
|
|
layout.keepNext = false;
|
|
layout.pageBB = false;
|
|
layout.pageBA = false;
|
|
}
|
|
|
|
void RTFImport::setSectionDefaults( RTFProperty * )
|
|
{
|
|
RTFSectionLayout §ion = state.section;
|
|
|
|
section.headerMargin = 720;
|
|
section.footerMargin = 720;
|
|
section.titlePage = false;
|
|
}
|
|
|
|
void RTFImport::setTableRowDefaults( RTFProperty * )
|
|
{
|
|
RTFTableRow &tableRow = state.tableRow;
|
|
RTFTableCell &tableCell = state.tableCell;
|
|
|
|
tableRow.height = 0;
|
|
tableRow.left = 0;
|
|
tableRow.alignment = RTFLayout::Left;
|
|
tableRow.cells.clear();
|
|
tableCell.bgcolor = -1;
|
|
|
|
for (uint i=0; i < 4; i++)
|
|
{
|
|
RTFBorder &border = tableCell.borders[i];
|
|
border.color = -1;
|
|
border.width = 0;
|
|
border.style = RTFBorder::None;
|
|
}
|
|
}
|
|
|
|
void RTFImport::selectLayoutBorder( RTFProperty * property )
|
|
{
|
|
state.layout.border = & state.layout.borders [ property->value ];
|
|
}
|
|
|
|
void RTFImport::selectLayoutBorderFromCell( RTFProperty * property )
|
|
{
|
|
state.layout.border = & state.tableCell.borders [ property->value ];
|
|
}
|
|
|
|
void RTFImport::insertParagraph( RTFProperty * )
|
|
{
|
|
if (state.layout.inTable)
|
|
{
|
|
if (textState->table == 0)
|
|
{
|
|
// Create a new table cell
|
|
textState->table = ++table;
|
|
}
|
|
addParagraph( textState->cell, false );
|
|
}
|
|
else
|
|
{
|
|
if (textState->table)
|
|
{
|
|
finishTable();
|
|
}
|
|
addParagraph( textState->node, false );
|
|
}
|
|
}
|
|
|
|
void RTFImport::insertPageBreak( RTFProperty * )
|
|
{
|
|
if (textState->length > 0)
|
|
{
|
|
insertParagraph();
|
|
}
|
|
addParagraph( textState->node, true );
|
|
}
|
|
|
|
void RTFImport::insertTableCell( RTFProperty * )
|
|
{
|
|
//{{
|
|
bool b = state.layout.inTable;
|
|
state.layout.inTable = true;
|
|
insertParagraph();
|
|
state.layout.inTable = b;
|
|
//}}
|
|
textState->frameSets << textState->cell.toString();
|
|
textState->cell.clear( 3 );
|
|
}
|
|
|
|
void RTFImport::insertTableRow( RTFProperty * )
|
|
{
|
|
if (!textState->frameSets.isEmpty())
|
|
{
|
|
RTFTableRow row = state.tableRow;
|
|
row.frameSets = textState->frameSets;
|
|
|
|
if (textState->rows.isEmpty())
|
|
{
|
|
char buf[64];
|
|
sprintf( buf, "Table %d", textState->table );
|
|
RTFLayout::Alignment align = row.alignment;
|
|
|
|
// Store the current state on the stack
|
|
stateStack.push( state );
|
|
resetState();
|
|
state.layout.alignment = align; // table alignment
|
|
|
|
// Add anchor for new table (default layout)
|
|
addAnchor( buf );
|
|
addParagraph( textState->node, false );
|
|
|
|
// Retrieve the current state from the stack
|
|
state = stateStack.pop();
|
|
}
|
|
|
|
// Number of cell definitions should equal the number of cells
|
|
while (row.cells.count() > row.frameSets.count())
|
|
{
|
|
// ### TODO: verify if it is the right action and how we have come here at all.
|
|
row.cells.pop_back();
|
|
}
|
|
while (row.cells.count() < row.frameSets.count())
|
|
{
|
|
row.cells << row.cells.last();
|
|
}
|
|
int lx = row.left;
|
|
|
|
// Each cell should be at least 1x1 in size
|
|
if (row.height == 0)
|
|
{
|
|
row.height = 1;
|
|
}
|
|
// ### TODO: use ConstIterator
|
|
for (uint k=0; k < row.cells.count(); k++)
|
|
{
|
|
if ((row.cells[k].x - lx) < 1)
|
|
row.cells[k].x = ++lx;
|
|
else
|
|
lx = row.cells[k].x;
|
|
}
|
|
if (row.left < 0)
|
|
{
|
|
// ### TODO: use ConstIterator
|
|
for (uint k=0; k < row.cells.count(); k++)
|
|
{
|
|
row.cells[k].x -= row.left;
|
|
}
|
|
row.left = 0;
|
|
}
|
|
textState->rows << row;
|
|
textState->frameSets.clear();
|
|
}
|
|
}
|
|
|
|
void RTFImport::insertCellDef( RTFProperty * )
|
|
{
|
|
RTFTableCell &cell = state.tableCell;
|
|
cell.x = token.value;
|
|
state.tableRow.cells << cell;
|
|
cell.bgcolor = -1;
|
|
|
|
for (uint i=0; i < 4; i++)
|
|
{
|
|
RTFBorder &border = cell.borders[i];
|
|
border.color = -1;
|
|
border.width = 0;
|
|
border.style = RTFBorder::None;
|
|
}
|
|
}
|
|
|
|
void RTFImport::insertTabDef( RTFProperty * )
|
|
{
|
|
RTFTab tab = state.layout.tab;
|
|
tab.position = token.value;
|
|
state.layout.tablist.push( tab );
|
|
tab.type = RTFTab::Left;
|
|
tab.leader = RTFTab::None;
|
|
}
|
|
|
|
void RTFImport::insertUTF8( int ch )
|
|
{
|
|
kdDebug(30515) << "insertUTF8: " << ch << endl;
|
|
char buf[4];
|
|
char *text = buf;
|
|
char *tk = token.text;
|
|
token.type = RTFTokenizer::PlainText;
|
|
token.text = buf;
|
|
|
|
// We do not test if the character is not allowed in XML:
|
|
// - it will be done later
|
|
// - list definitions need to use char(1), char(2)...
|
|
// ### FIXME: for high Unicode values, RTF uses negative values
|
|
if (ch > 0x007f)
|
|
{
|
|
if (ch > 0x07ff)
|
|
{
|
|
*text++ = 0xe0 | (ch >> 12);
|
|
ch = (ch & 0xfff) | 0x1000;
|
|
}
|
|
*text++ = ((ch >> 6) | 0x80) ^ 0x40;
|
|
ch = (ch & 0x3f) | 0x80;
|
|
}
|
|
*text++ = ch;
|
|
*text++ = 0;
|
|
|
|
TQTextCodec* oldCodec=textCodec;
|
|
|
|
if (utf8TextCodec)
|
|
textCodec=utf8TextCodec;
|
|
else
|
|
kdError(30515) << "No UTF-8 TQTextCodec available" << endl;
|
|
|
|
(this->*destination.destproc)(0L);
|
|
|
|
textCodec=oldCodec;
|
|
token.text = tk;
|
|
}
|
|
|
|
void RTFImport::insertSymbol( RTFProperty *property )
|
|
{
|
|
insertUTF8( property->value );
|
|
}
|
|
|
|
void RTFImport::insertHexSymbol( RTFProperty * )
|
|
{
|
|
//kdDebug(30515) << "insertHexSymbol: " << token.value << endl;
|
|
|
|
// Be careful, the value given in \' could be only one byte of a multi-byte character.
|
|
// So it cannot be assumed that it will result in one character.
|
|
|
|
// Some files have \'00 which is pretty bad, as NUL is end of string for us.
|
|
// (e.g. attachment #7758 of bug #90649)
|
|
if ( !token.value )
|
|
{
|
|
kdWarning(30515) << "Trying to insert NUL character!" << endl;
|
|
return;
|
|
}
|
|
|
|
char tmpch[2] = {(char)(token.value), '\0'};
|
|
|
|
char *tk = token.text;
|
|
token.type = RTFTokenizer::PlainText;
|
|
token.text = tmpch;
|
|
|
|
(this->*destination.destproc)(0L);
|
|
|
|
token.text = tk;
|
|
}
|
|
|
|
void RTFImport::insertUnicodeSymbol( RTFProperty * )
|
|
{
|
|
const int ch = token.value;
|
|
|
|
// Ignore the next N characters (or control words)
|
|
for (uint i=state.format.uc; i > 0; )
|
|
{
|
|
token.next();
|
|
|
|
if (token.type == RTFTokenizer::ControlWord)
|
|
--i; // Ignore as single token
|
|
else if (token.type == RTFTokenizer::OpenGroup ||
|
|
token.type == RTFTokenizer::CloseGroup)
|
|
{
|
|
break;
|
|
}
|
|
else if (token.type == RTFTokenizer::PlainText)
|
|
{
|
|
const uint len = tqstrlen( token.text );
|
|
if ( len < i )
|
|
i -= len;
|
|
else
|
|
{
|
|
token.text += i;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (token.type != RTFTokenizer::PlainText)
|
|
{
|
|
token.type = RTFTokenizer::PlainText;
|
|
token.text[0] = 0;
|
|
}
|
|
insertUTF8( ch ); // ### TODO: put it higher in this function
|
|
(this->*destination.destproc)(0L);
|
|
}
|
|
|
|
void RTFImport::parseFontTable( RTFProperty * )
|
|
{
|
|
if (token.type == RTFTokenizer::OpenGroup)
|
|
{
|
|
font.name = TQString();
|
|
font.styleHint = TQFont::AnyStyle;
|
|
font.fixedPitch = 0;
|
|
}
|
|
else if (token.type == RTFTokenizer::PlainText)
|
|
{
|
|
if (!textCodec)
|
|
{
|
|
kdError(30515) << "No text codec for font!" << endl;
|
|
return; // We have no text codec, so we cannot proceed!
|
|
}
|
|
// ### TODO VERIFY: a RTF group could be in the middle of the font name
|
|
// ### TODO VERIFY: I do not now if it is specified in RTF but attachment #7758 of bug #90649 has it.
|
|
// Semicolons separate fonts
|
|
if (strchr( token.text, ';' ) == 0L) // ### TODO: is this allowed with multi-byte Asian characters?
|
|
font.name += textCodec->toUnicode( token.text );
|
|
else
|
|
{
|
|
// Add font to font table
|
|
*strchr( token.text, ';' ) = 0; // ### TODO: is this allowed with multi-byte Asian characters?
|
|
font.name += textCodec->toUnicode( token.text );
|
|
// Use TQt to look up the closest matching installed font
|
|
TQFont qFont( font.name );
|
|
qFont.setFixedPitch( (font.fixedPitch == 1) );
|
|
qFont.setStyleHint( font.styleHint );
|
|
for(;!qFont.exactMatch();)
|
|
{
|
|
int space=font.name.findRev(' ', font.name.length());
|
|
if(space==-1)
|
|
break;
|
|
font.name.truncate(space);
|
|
qFont.setFamily( font.name );
|
|
}
|
|
const TQFontInfo info( qFont );
|
|
const TQString newFontName ( info.family() );
|
|
kdDebug(30515) << "Font " << state.format.font << " asked: " << font.name << " given: " << newFontName << endl;
|
|
|
|
if ( newFontName.isEmpty() )
|
|
fontTable.insert( state.format.font, font.name );
|
|
else
|
|
fontTable.insert( state.format.font, newFontName );
|
|
font.name.truncate( 0 );
|
|
font.styleHint = TQFont::AnyStyle;
|
|
font.fixedPitch = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
void RTFImport::parseStyleSheet( RTFProperty * )
|
|
{
|
|
if (token.type == RTFTokenizer::OpenGroup)
|
|
{
|
|
style.name = "";
|
|
style.next = -1;
|
|
}
|
|
else if (token.type == RTFTokenizer::PlainText)
|
|
{
|
|
// Semicolons separate styles
|
|
if (strchr( token.text, ';' ) == 0L) // ### TODO: is this allowed with multi-byte Asian characters?
|
|
style.name += textCodec->toUnicode( token.text );
|
|
else
|
|
{
|
|
// Add style to style sheet
|
|
*strchr( token.text, ';' ) = 0; // ### TODO: is this allowed with multi-byte Asian characters?
|
|
style.name += textCodec->toUnicode( token.text );
|
|
style.format = state.format;
|
|
style.layout = state.layout;
|
|
style.next = (style.next == -1) ? style.layout.style : style.next;
|
|
styleSheet << style;
|
|
style.name.truncate( 0 );
|
|
style.next = -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
void RTFImport::parseColorTable( RTFProperty * )
|
|
{
|
|
if (token.type == RTFTokenizer::OpenGroup)
|
|
{
|
|
red = 0;
|
|
green = 0;
|
|
blue = 0;
|
|
}
|
|
else if (token.type == RTFTokenizer::PlainText)
|
|
{
|
|
// Note: the color table can be a simple ; character only, especially in PWD files
|
|
// Search for semicolon(s)
|
|
while ((token.text = strchr( token.text, ';' )))
|
|
{
|
|
colorTable << TQColor( red, green, blue );
|
|
red = green = blue = 0;
|
|
++token.text;
|
|
}
|
|
}
|
|
}
|
|
|
|
void RTFImport::parseBlipUid( RTFProperty * )
|
|
{
|
|
if (token.type == RTFTokenizer::OpenGroup)
|
|
{
|
|
picture.identifier = TQString();
|
|
}
|
|
else if (token.type == RTFTokenizer::PlainText)
|
|
{
|
|
picture.identifier += TQString::fromUtf8( token.text );
|
|
}
|
|
else if (token.type == RTFTokenizer::CloseGroup)
|
|
{
|
|
kdDebug(30515) << "\\blipuid: " << picture.identifier << endl;
|
|
}
|
|
}
|
|
|
|
void RTFImport::parsePicture( RTFProperty * )
|
|
{
|
|
if (state.ignoreGroup)
|
|
return;
|
|
|
|
if (token.type == RTFTokenizer::OpenGroup)
|
|
{
|
|
picture.type = RTFPicture::PNG;
|
|
picture.width = 0;
|
|
picture.height = 0;
|
|
picture.desiredWidth = 0;
|
|
picture.desiredHeight = 0;
|
|
picture.scalex = 100; // Default is 100%
|
|
picture.scaley = 100; // Default is 100%
|
|
picture.cropLeft = 0;
|
|
picture.cropTop = 0;
|
|
picture.cropRight = 0;
|
|
picture.cropBottom = 0;
|
|
picture.nibble = 0;
|
|
picture.bits.truncate( 0 );
|
|
picture.identifier = TQString();
|
|
}
|
|
else if (token.type == RTFTokenizer::PlainText)
|
|
{
|
|
if (picture.nibble)
|
|
{
|
|
*(--token.text) = picture.nibble;
|
|
}
|
|
uint n = tqstrlen( token.text ) >> 1;
|
|
picture.bits.resize( picture.bits.size() + n );
|
|
char *src = token.text;
|
|
char *dst = (picture.bits.data() + picture.bits.size() - n);
|
|
|
|
// Store hexadecimal data
|
|
while (n-- > 0)
|
|
{
|
|
int k = *src++;
|
|
int l = *src++;
|
|
*dst++ = (((k + ((k & 16) ? 0 : 9)) & 0xf) << 4) |
|
|
((l + ((l & 16) ? 0 : 9)) & 0xf);
|
|
}
|
|
picture.nibble = *src;
|
|
}
|
|
else if (token.type == RTFTokenizer::BinaryData)
|
|
{
|
|
picture.bits = token.binaryData;
|
|
kdDebug(30515) << "Binary data of length: " << picture.bits.size() << endl;
|
|
}
|
|
else if (token.type == RTFTokenizer::CloseGroup)
|
|
{
|
|
const char *ext;
|
|
|
|
// Select file extension based on picture type
|
|
switch (picture.type)
|
|
{
|
|
case RTFPicture::WMF:
|
|
case RTFPicture::EMF:
|
|
ext = ".wmf";
|
|
break;
|
|
case RTFPicture::BMP:
|
|
ext = ".bmp";
|
|
break;
|
|
case RTFPicture::MacPict:
|
|
ext = ".pict";
|
|
break;
|
|
case RTFPicture::JPEG:
|
|
ext = ".jpg";
|
|
break;
|
|
case RTFPicture::PNG:
|
|
default:
|
|
ext = ".png";
|
|
break;
|
|
}
|
|
const int id = ++pictureNumber;
|
|
TQString pictName("pictures/picture");
|
|
pictName += TQString::number(id);
|
|
pictName += ext;
|
|
|
|
TQCString frameName;
|
|
frameName.setNum(id);
|
|
frameName.prepend("Picture ");
|
|
|
|
TQString idStr;
|
|
if (picture.identifier.isEmpty())
|
|
{
|
|
idStr = pictName;
|
|
}
|
|
else
|
|
{
|
|
idStr += picture.identifier.stripWhiteSpace();
|
|
idStr += ext;
|
|
}
|
|
|
|
kdDebug(30515) << "Picture: " << pictName << " Frame: " << frameName << endl;
|
|
|
|
// Store picture
|
|
KoStoreDevice* dev = m_chain->storageFile( pictName, KoStore::Write );
|
|
if ( dev )
|
|
dev->writeBlock(picture.bits.data(),picture.bits.size());
|
|
else
|
|
kdError(30515) << "Could not save: " << pictName << endl;
|
|
|
|
|
|
// Add anchor to rich text destination
|
|
addAnchor( frameName );
|
|
|
|
// It is safe, as we call currentDateTime only once for each picture
|
|
const TQDateTime dt(TQDateTime::currentDateTime());
|
|
|
|
// Add pixmap or clipart (key)
|
|
pictures.addKey( dt, idStr, pictName );
|
|
|
|
// Add picture or clipart frameset
|
|
frameSets.addFrameSet( frameName, 2, 0 );
|
|
//kdDebug(30515) << "Width: " << picture.desiredWidth << " scalex: " << picture.scalex << "%" << endl;
|
|
//kdDebug(30515) << "Height: " << picture.desiredHeight<< " scaley: " << picture.scaley << "%" << endl;
|
|
frameSets.addFrame( 0, 0,
|
|
(picture.desiredWidth * picture.scalex) /100 ,
|
|
(picture.desiredHeight * picture.scaley) /100 , 0, 1, 0 );
|
|
frameSets.closeNode( "FRAME" );
|
|
frameSets.addNode( "PICTURE" );
|
|
frameSets.addKey( dt, idStr );
|
|
frameSets.closeNode( "PICTURE" );
|
|
frameSets.closeNode( "FRAMESET" );
|
|
picture.identifier = TQString();
|
|
}
|
|
}
|
|
|
|
void RTFImport::addImportedPicture( const TQString& rawFileName )
|
|
{
|
|
kdDebug(30515) << "Import field: reading " << rawFileName << endl;
|
|
|
|
if (rawFileName=="\\*")
|
|
{
|
|
kdError(30515) << "Import field without file name!" << endl;
|
|
return;
|
|
}
|
|
|
|
TQString slashPath( rawFileName );
|
|
slashPath.replace('\\','/'); // Replace directory separators.
|
|
// ### TODO: what with MS-DOS absolute paths? (Will only work for KOffice on Win32)
|
|
TQFileInfo info;
|
|
info.setFile( inFileName );
|
|
TQDir dir( info.dirPath() );
|
|
|
|
KURL url;
|
|
url.setPath(dir.filePath( rawFileName ));
|
|
|
|
kdDebug(30515) << "Path: " << url.prettyURL() << endl;
|
|
|
|
KoPicture pic;
|
|
pic.setKeyAndDownloadPicture(url, 0); // ### TODO: find a better parent if possible
|
|
if (pic.isNull())
|
|
{
|
|
kdError(30515) << "Import field: file is empty: " << rawFileName << endl;
|
|
return;
|
|
}
|
|
|
|
const uint id = ++pictureNumber;
|
|
|
|
TQString pictName("pictures/picture");
|
|
pictName += TQString::number(id);
|
|
pictName += '.';
|
|
pictName += pic.getExtension();
|
|
|
|
TQCString frameName;
|
|
frameName.setNum(id);
|
|
frameName.prepend("Picture ");
|
|
|
|
|
|
kdDebug(30515) << "Imported picture: " << pictName << " Frame: " << frameName << endl;
|
|
|
|
// Store picture
|
|
KoStoreDevice* dev = m_chain->storageFile( pictName, KoStore::Write );
|
|
if ( dev )
|
|
pic.save(dev);
|
|
else
|
|
kdError(30515) << "Could not save: " << pictName << endl;
|
|
|
|
// Add anchor to rich text destination
|
|
addAnchor( frameName );
|
|
|
|
// It is safe, as we call currentDateTime only once for each picture
|
|
const TQDateTime dt( pic.getKey().lastModified() );
|
|
|
|
// Add picture key
|
|
pictures.addKey( dt, rawFileName, pictName );
|
|
|
|
// Add picture frameset
|
|
const TQSize size ( pic.getOriginalSize() * 20 ); // We need twips for addFrame
|
|
frameSets.addFrameSet( frameName, 2, 0 );
|
|
frameSets.addFrame( 0, 0, size.width(), size.height(), 0, 1, 0 );
|
|
frameSets.closeNode( "FRAME" );
|
|
frameSets.addNode( "PICTURE" );
|
|
frameSets.addKey( dt, rawFileName );
|
|
frameSets.closeNode( "PICTURE" );
|
|
frameSets.closeNode( "FRAMESET" );
|
|
}
|
|
|
|
void RTFImport::insertPageNumber( RTFProperty * )
|
|
{
|
|
DomNode node;
|
|
node.addNode( "PGNUM" );
|
|
node.setAttribute( "subtype", 0 );
|
|
node.setAttribute( "value", 0 );
|
|
node.closeNode("PGNUM");
|
|
addVariable( node, 4, "NUMBER", &state.format);
|
|
}
|
|
|
|
void RTFImport::insertDateTime( RTFProperty *property )
|
|
{
|
|
kdDebug(30515) << "insertDateTime: " << property->value << endl;
|
|
addDateTime( TQString(), bool(property->value), state.format );
|
|
}
|
|
|
|
void RTFImport::addDateTime( const TQString& format, const bool isDate, RTFFormat& fmt )
|
|
{
|
|
bool asDate=isDate; // Should the variable be a date variable?
|
|
TQString kwordFormat(format);
|
|
if (format.isEmpty())
|
|
{
|
|
if (isDate)
|
|
kwordFormat = "DATElocale";
|
|
else
|
|
kwordFormat = "TIMElocale";
|
|
}
|
|
else if (!isDate)
|
|
{
|
|
// It is a time with a specified format, so check if it is really a time
|
|
// (as in KWord 1.3, a date can have a time format but a time cannot have a date format
|
|
const TQRegExp regexp ("[yMd]"); // any date format character?
|
|
asDate = (regexp.search(format)>-1); // if yes, then it is a date
|
|
}
|
|
DomNode node;
|
|
if (asDate)
|
|
{
|
|
node.clear(7);
|
|
node.addNode("DATE");
|
|
node.setAttribute("year", 0);
|
|
node.setAttribute("month", 0);
|
|
node.setAttribute("day", 0);
|
|
node.setAttribute("fix", 0);
|
|
node.closeNode("DATE");
|
|
addVariable(node, 0, kwordFormat, &fmt);
|
|
}
|
|
else
|
|
{
|
|
node.clear(7);
|
|
node.addNode("TIME");
|
|
node.setAttribute("hour", 0);
|
|
node.setAttribute("minute", 0);
|
|
node.setAttribute("second", 0);
|
|
node.setAttribute("fix", 0);
|
|
node.closeNode("TIME");
|
|
addVariable(node, 2, kwordFormat, &fmt);
|
|
}
|
|
}
|
|
|
|
void RTFImport::parseField( RTFProperty * )
|
|
{
|
|
if (token.type == RTFTokenizer::OpenGroup)
|
|
{
|
|
if (flddst == -1)
|
|
{
|
|
// Destination for unsupported fields
|
|
flddst = (destinationStack.count() - 1);
|
|
}
|
|
fldinst = "";
|
|
fldrslt = "";
|
|
destination.group = 0L;
|
|
}
|
|
else if (token.type == RTFTokenizer::CloseGroup)
|
|
{
|
|
if (!fldinst.isEmpty())
|
|
{
|
|
DomNode node;
|
|
TQStringList list ( TQStringList::split( ' ', fldinst, false ) );
|
|
kdDebug(30515) << "Field: " << list << endl;
|
|
uint i;
|
|
|
|
TQString fieldName ( list[0].upper() );
|
|
fieldName.remove('\\'); // Remove \, especialy leading ones in OOWriter RTF files
|
|
node.clear(7);
|
|
|
|
bool ok=false;
|
|
for (i=0; i < sizeof(fieldTable) /sizeof(fieldTable[0]); i++)
|
|
{
|
|
if (fieldName == fieldTable[i].id)
|
|
{
|
|
kdDebug(30515) << "Field found: " << fieldTable[i].id << endl;
|
|
ok=true;
|
|
break;
|
|
}
|
|
}
|
|
if (!ok)
|
|
{
|
|
kdWarning(30515) << "Field not supported: " << fieldName << endl;
|
|
return;
|
|
}
|
|
if (fieldTable[i].type == 4)
|
|
{
|
|
node.addNode( "PGNUM" );
|
|
node.setAttribute( "subtype", fieldTable[i].subtype );
|
|
node.setAttribute( "value", 0 );
|
|
node.closeNode("PGNUM");
|
|
addVariable( node, 4, "NUMBER", &fldfmt);
|
|
}
|
|
else if (fieldTable[i].type == 8)
|
|
{
|
|
node.addNode( "FIELD" );
|
|
node.setAttribute( "subtype", fieldTable[i].subtype );
|
|
node.setAttribute( "value", fieldTable[i].value );
|
|
node.closeNode("FIELD");
|
|
addVariable( node, 8, "STRING", &fldfmt);
|
|
}
|
|
else if (fieldTable[i].type == 9)
|
|
{
|
|
TQString hrefName = TQString();
|
|
|
|
// Use ConstIterator
|
|
for (uint i=1; i < list.count(); i++)
|
|
{
|
|
if (list[i] == "\\l")
|
|
{
|
|
hrefName += '#';
|
|
}
|
|
else if (list[i].startsWith( "\"" ) && list[i].endsWith( "\"" ))
|
|
{
|
|
hrefName += list[i].mid( 1, (list[i].length() - 2) );
|
|
}
|
|
else if (list[i].startsWith("http"))
|
|
{
|
|
hrefName += list[i];
|
|
}
|
|
}
|
|
node.addNode( "LINK" );
|
|
node.setAttribute( "linkName", !fldrslt.isNull() );
|
|
node.setAttribute( "hrefName", hrefName );
|
|
node.closeNode( "LINK" );
|
|
addVariable( node, 9, "STRING", &fldfmt);
|
|
}
|
|
else if (fieldName == "SYMBOL")
|
|
{
|
|
if (list.count() >= 2)
|
|
{
|
|
int ch = list[1].toInt();
|
|
|
|
if (ch > 0)
|
|
{
|
|
// ### TODO: some error control (the destination might be invalid!)
|
|
destination = destinationStack[flddst];
|
|
state.format = fldfmt;
|
|
insertUTF8( ch );
|
|
}
|
|
}
|
|
}
|
|
else if (fieldName == "TIME" || fieldName == "DATE")
|
|
{
|
|
TQString strFldinst( TQString::fromUtf8(fldinst) );
|
|
TQRegExp regexp("\\\\@\\s*\"(.+)\""); // \@ "Text"
|
|
if (regexp.search(strFldinst)==-1)
|
|
{ // Not found? Perhaps it is not in quotes (even if it is rare)
|
|
kdWarning(30515) << "Date/time field format not in quotes!" << endl;
|
|
strFldinst += ' '; // Add a space at the end to simplify the regular expression
|
|
regexp = TQRegExp("\\\\@(\\S+)\\s+"); // \@some_text_up_to_a_space
|
|
regexp.search(strFldinst);
|
|
}
|
|
TQString format(regexp.cap(1));
|
|
kdDebug(30515) << "Date/time field format: " << format << endl;
|
|
format.replace("am/pm", "ap");
|
|
format.replace("a/p", "ap"); // Approximation
|
|
format.replace("AM/PM", "AP");
|
|
format.replace("A/P", "AP"); // Approximation
|
|
format.remove("'"); // KWord 1.3 cannot protect text in date/time
|
|
addDateTime( format, (fieldName == "DATE"), fldfmt );
|
|
}
|
|
else if (fieldName == "IMPORT")
|
|
{
|
|
addImportedPicture( list[1] );
|
|
}
|
|
|
|
fldinst = "";
|
|
}
|
|
|
|
if (flddst == (int) (destinationStack.count() - 1))
|
|
{
|
|
// Top-level field closed, clear field destination
|
|
flddst = -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
void RTFImport::parseFldinst( RTFProperty * )
|
|
{
|
|
if (token.type == RTFTokenizer::OpenGroup)
|
|
{
|
|
fldinst = "";
|
|
}
|
|
else if (token.type == RTFTokenizer::PlainText)
|
|
{
|
|
fldinst += token.text;
|
|
}
|
|
}
|
|
|
|
void RTFImport::parseFldrslt( RTFProperty * )
|
|
{
|
|
if (fldinst.isEmpty())
|
|
{
|
|
if (token.type == RTFTokenizer::OpenGroup)
|
|
{
|
|
// ### TODO: why is this destination change not done with the corresponding procedure "changeDestination"?
|
|
destination = destinationStack[flddst];
|
|
destination.destproc = &RTFImport::parseFldrslt;
|
|
}
|
|
else if (token.type != RTFTokenizer::CloseGroup)
|
|
{
|
|
(this->*destinationStack[flddst].destproc)(0L);
|
|
}
|
|
}
|
|
else if (token.type == RTFTokenizer::OpenGroup)
|
|
{
|
|
fldrslt = "";
|
|
}
|
|
else if (token.type == RTFTokenizer::PlainText)
|
|
{
|
|
fldrslt += token.text;
|
|
}
|
|
else if (token.type == RTFTokenizer::CloseGroup)
|
|
{
|
|
fldfmt = state.format;
|
|
}
|
|
}
|
|
|
|
void RTFImport::addVariable (const DomNode& spec, int type, const TQString& key, const RTFFormat* fmt)
|
|
{
|
|
DomNode node;
|
|
|
|
node.clear( 6 );
|
|
node.addNode( "VARIABLE" );
|
|
node.closeTag(true);
|
|
node.addNode("TYPE");
|
|
node.setAttribute( "type", type );
|
|
node.setAttribute( "key", CheckAndEscapeXmlText(key) );
|
|
node.setAttribute( "text", 1 );
|
|
node.closeNode("TYPE");
|
|
|
|
node.appendNode(spec);
|
|
node.closeNode( "VARIABLE" );
|
|
kwFormat.xmldata = node.toString();
|
|
kwFormat.id = 4;
|
|
kwFormat.pos = textState->length++;
|
|
kwFormat.len = 1;
|
|
if (fmt)
|
|
kwFormat.fmt = *fmt;
|
|
textState->text.append( '#' );
|
|
textState->formats << kwFormat;
|
|
}
|
|
|
|
void RTFImport::parseFootNote( RTFProperty * property)
|
|
{
|
|
if(token.type==RTFTokenizer::OpenGroup)
|
|
{
|
|
RTFTextState* newTextState=new RTFTextState;
|
|
footnotes.append(newTextState);
|
|
fnnum++;
|
|
destination.target = newTextState;
|
|
|
|
TQCString str;
|
|
str.setNum(fnnum);
|
|
str.prepend("Footnote ");
|
|
|
|
DomNode node;
|
|
|
|
node.clear( 7 );
|
|
node.addNode("FOOTNOTE");
|
|
node.setAttribute("numberingtype", "auto");
|
|
node.setAttribute("notetype", "footnote");
|
|
node.setAttribute("frameset", !str.isNull());
|
|
node.setAttribute("value", fnnum);
|
|
node.closeNode("FOOTNOTE");
|
|
addVariable(node, 11, "STRING");
|
|
}
|
|
parseRichText(property);
|
|
}
|
|
|
|
void RTFImport::parseRichText( RTFProperty * )
|
|
{
|
|
if (token.type == RTFTokenizer::OpenGroup)
|
|
{
|
|
// Save and change rich text destination
|
|
RTFTextState *oldState = textState;
|
|
textState = destination.target;
|
|
destination.target = oldState;
|
|
destination.group = "Text";
|
|
|
|
// Initialize rich text state
|
|
textState->text.clear();
|
|
textState->node.clear( 3 );
|
|
textState->cell.clear( 3 );
|
|
textState->formats.clear();
|
|
textState->frameSets.clear();
|
|
textState->rows.clear();
|
|
textState->table = 0;
|
|
textState->length = 0;
|
|
}
|
|
else if (token.type == RTFTokenizer::PlainText)
|
|
{
|
|
// Ignore hidden text
|
|
if (!state.format.hidden)
|
|
{
|
|
const int len = (token.text[0] < 0) ? 1 : tqstrlen( token.text );
|
|
|
|
// Check and store format changes
|
|
if ( textState->formats.isEmpty() ||
|
|
textState->formats.last().fmt != state.format ||
|
|
( !textState->formats.last().xmldata.isEmpty() ) )
|
|
{
|
|
kwFormat.fmt = state.format;
|
|
kwFormat.id = 1;
|
|
kwFormat.pos = textState->length;
|
|
kwFormat.len = len;
|
|
textState->formats << kwFormat;
|
|
kwFormat.xmldata = TQString();
|
|
}
|
|
else
|
|
{
|
|
textState->formats.last().len += len;
|
|
}
|
|
textState->length += len;
|
|
textState->text.addTextNode( token.text, textCodec );
|
|
}
|
|
}
|
|
else if (token.type == RTFTokenizer::CloseGroup)
|
|
{
|
|
if (textState->length)
|
|
insertParagraph();
|
|
if (textState->table)
|
|
finishTable();
|
|
|
|
// Restore rich text destination
|
|
textState = destination.target;
|
|
}
|
|
}
|
|
|
|
void RTFImport::parsePlainText( RTFProperty * )
|
|
{
|
|
if (token.type == RTFTokenizer::OpenGroup)
|
|
{
|
|
destination.target->node.clear();
|
|
}
|
|
else if (token.type == RTFTokenizer::PlainText)
|
|
{
|
|
destination.target->node.addTextNode( token.text, textCodec );
|
|
}
|
|
}
|
|
|
|
void RTFImport::parseGroup( RTFProperty * )
|
|
{
|
|
}
|
|
|
|
void RTFImport::skipGroup( RTFProperty * )
|
|
{
|
|
kdDebug(30515) << "Skip Group: " << token.type << endl;
|
|
state.ignoreGroup = true;
|
|
}
|
|
|
|
void RTFImport::resetState()
|
|
{
|
|
setPlainFormatting();
|
|
setParagraphDefaults();
|
|
setSectionDefaults();
|
|
setTableRowDefaults();
|
|
}
|
|
|
|
void RTFImport::changeDestination( RTFProperty *property )
|
|
{
|
|
kdDebug(30515) << "changeDestination: " << property->name << endl;
|
|
destinationStack.push( destination );
|
|
destination.name = property->name;
|
|
destination.destproc = property->cwproc;
|
|
if ( property->offset )
|
|
destination.target = (RTFTextState*) ( (char *)this + property->offset );
|
|
else
|
|
destination.target = &m_dummyTextState;
|
|
|
|
state.brace0 = true;
|
|
|
|
if (property->value)
|
|
{
|
|
resetState();
|
|
destination.group = 0L;
|
|
}
|
|
|
|
// Send OpenGroup to destination
|
|
token.type = RTFTokenizer::OpenGroup;
|
|
(this->*destination.destproc)(0L);
|
|
}
|
|
|
|
void RTFImport::addAnchor( const char *instance )
|
|
{
|
|
DomNode node;
|
|
|
|
node.clear( 6 );
|
|
node.addNode( "ANCHOR" );
|
|
node.setAttribute( "type", "frameset" );
|
|
node.setAttribute( "instance", instance );
|
|
node.closeNode( "ANCHOR" );
|
|
kwFormat.xmldata = node.toString();
|
|
kwFormat.id = 6;
|
|
kwFormat.pos = textState->length++;
|
|
kwFormat.len = 1;
|
|
textState->text.append( '#' );
|
|
textState->formats << kwFormat;
|
|
}
|
|
|
|
void RTFImport::addFormat( DomNode &node, const KWFormat& format, const RTFFormat* baseFormat )
|
|
{
|
|
// Support both (\dn, \up) and (\sub, \super) for super/sub script
|
|
int vertAlign = format.fmt.vertAlign;
|
|
int fontSize = (format.fmt.fontSize >> 1);
|
|
int vertAlign0 = ~vertAlign;
|
|
int fontSize0 = ~fontSize;
|
|
|
|
// Adjust vertical alignment and font size if (\dn, \up) are used
|
|
if (format.fmt.vertAlign == RTFFormat::Normal && format.fmt.baseline)
|
|
{
|
|
if (format.fmt.baseline < 0)
|
|
vertAlign = RTFFormat::SuperScript;
|
|
else // (format.baseline > 0)
|
|
vertAlign = RTFFormat::SubScript;
|
|
|
|
fontSize += (fontSize >> 1);
|
|
}
|
|
if (baseFormat)
|
|
{
|
|
vertAlign0 = baseFormat->vertAlign;
|
|
fontSize0 = (baseFormat->fontSize >> 1);
|
|
|
|
if (vertAlign0 == RTFFormat::Normal && baseFormat->baseline)
|
|
{
|
|
if (baseFormat->baseline < 0)
|
|
vertAlign0 = RTFFormat::SuperScript;
|
|
else // (baseFormat.baseline > 0)
|
|
vertAlign0 = RTFFormat::SubScript;
|
|
|
|
fontSize0 += (fontSize0 >> 1);
|
|
}
|
|
}
|
|
node.addNode( "FORMAT" );
|
|
node.setAttribute( "id", (int)format.id );
|
|
|
|
if (format.len != 0)
|
|
{
|
|
// Add pos and len if this is not a style sheet definition
|
|
node.setAttribute( "pos", (int)format.pos );
|
|
node.setAttribute( "len", (int)format.len );
|
|
}
|
|
if ((format.id == 1)||(format.id == 4))
|
|
{
|
|
// Normal text, store changes between format and base format
|
|
if (!baseFormat || format.fmt.color != baseFormat->color)
|
|
{
|
|
node.addNode( "COLOR" );
|
|
node.addColor( ((uint)format.fmt.color >= colorTable.count())
|
|
? (TQColor &)TQt::black : colorTable[format.fmt.color] );
|
|
node.closeNode( "COLOR" );
|
|
}
|
|
if ((uint)format.fmt.bgcolor < colorTable.count() &&
|
|
(!baseFormat || format.fmt.bgcolor != baseFormat->bgcolor))
|
|
{
|
|
node.addNode( "TEXTBACKGROUNDCOLOR" );
|
|
node.addColor( colorTable[format.fmt.bgcolor] );
|
|
node.closeNode( "TEXTBACKGROUNDCOLOR" );
|
|
}
|
|
if (!baseFormat || format.fmt.font != baseFormat->font)
|
|
{
|
|
node.addNode( "FONT" );
|
|
|
|
if (fontTable.contains( format.fmt.font ))
|
|
{
|
|
node.setAttribute( "name", fontTable[format.fmt.font] );
|
|
}
|
|
node.closeNode( "FONT" );
|
|
}
|
|
if (!baseFormat || format.fmt.bold != baseFormat->bold)
|
|
{
|
|
node.addNode( "WEIGHT" );
|
|
node.setAttribute( "value", (format.fmt.bold ? 75 : 50) );
|
|
node.closeNode( "WEIGHT" );
|
|
}
|
|
if (fontSize != fontSize0)
|
|
{
|
|
node.addNode( "SIZE" );
|
|
node.setAttribute( "value", fontSize );
|
|
node.closeNode( "SIZE" );
|
|
}
|
|
if (!baseFormat || format.fmt.italic != baseFormat->italic)
|
|
{
|
|
node.addNode( "ITALIC" );
|
|
node.setAttribute( "value", format.fmt.italic );
|
|
node.closeNode( "ITALIC" );
|
|
}
|
|
if (!baseFormat || format.fmt.underline != baseFormat->underline )
|
|
{
|
|
node.addNode( "UNDERLINE" );
|
|
TQCString st,styleline,wordbyword("0");
|
|
st.setNum(format.fmt.underline);
|
|
int underlinecolor = format.fmt.underlinecolor;
|
|
|
|
switch (format.fmt.underline)
|
|
{
|
|
case RTFFormat::UnderlineNone:
|
|
default:
|
|
{
|
|
st="0";
|
|
underlinecolor=-1; // Reset underline color
|
|
break;
|
|
}
|
|
case RTFFormat::UnderlineSimple:
|
|
{
|
|
st="single";
|
|
break;
|
|
}
|
|
case RTFFormat::UnderlineDouble:
|
|
{
|
|
st="double";
|
|
break;
|
|
}
|
|
case RTFFormat::UnderlineThick:
|
|
{
|
|
st="single-bold";
|
|
styleline="solid";
|
|
break;
|
|
}
|
|
|
|
case RTFFormat::UnderlineWordByWord:
|
|
{
|
|
st="single";
|
|
styleline="solid";
|
|
wordbyword="1";
|
|
break;
|
|
}
|
|
case RTFFormat::UnderlineDash:
|
|
{
|
|
st="single";
|
|
styleline="dash";
|
|
break;
|
|
}
|
|
case RTFFormat::UnderlineDot:
|
|
{
|
|
st="single";
|
|
styleline="dot";
|
|
break;
|
|
}
|
|
case RTFFormat::UnderlineDashDot:
|
|
{
|
|
st="single";
|
|
styleline="dashdot";
|
|
break;
|
|
}
|
|
case RTFFormat::UnderlineDashDotDot:
|
|
{
|
|
st="single";
|
|
styleline="dashdotdot";
|
|
break;
|
|
}
|
|
case RTFFormat::UnderlineWave:
|
|
{
|
|
st="single";
|
|
styleline="wave";
|
|
break;
|
|
}
|
|
} // end of switch
|
|
node.setAttribute( "value", !st.isNull() );
|
|
node.setAttribute( "wordbyword", !wordbyword.isNull() );
|
|
if ( !styleline.isEmpty() )
|
|
node.setAttribute( "styleline", !styleline.isNull() );
|
|
if ( underlinecolor >= 0 && uint(underlinecolor) < colorTable.count() )
|
|
{
|
|
node.setAttribute( "underlinecolor", colorTable[underlinecolor].name() );
|
|
}
|
|
|
|
node.closeNode( "UNDERLINE" );
|
|
}
|
|
if (!baseFormat || format.fmt.strike != baseFormat->strike || format.fmt.striked != baseFormat->striked)
|
|
{
|
|
node.addNode( "STRIKEOUT" );
|
|
TQCString st;
|
|
st.setNum(format.fmt.strike);
|
|
if(format.fmt.striked)
|
|
st="double";
|
|
node.setAttribute( "value", !st.isNull() );
|
|
node.closeNode( "STRIKEOUT" );
|
|
}
|
|
if (vertAlign != vertAlign0)
|
|
{
|
|
node.addNode( "VERTALIGN" );
|
|
node.setAttribute( "value", vertAlign );
|
|
node.closeNode( "VERTALIGN" );
|
|
}
|
|
if (!baseFormat || format.fmt.caps != baseFormat->caps || format.fmt.smallCaps != baseFormat->smallCaps)
|
|
{
|
|
node.addNode( "FONTATTRIBUTE" );
|
|
TQCString fontattr;
|
|
if ( format.fmt.caps )
|
|
fontattr="uppercase";
|
|
else if ( format.fmt.smallCaps )
|
|
fontattr="smallcaps";
|
|
else
|
|
fontattr="none";
|
|
node.setAttribute( "value", !fontattr.isNull() );
|
|
node.closeNode( "FONTATTRIBUTE" );
|
|
}
|
|
if (!baseFormat)
|
|
{
|
|
node.addNode( "CHARSET" );
|
|
node.setAttribute( "value", (int)TQFont::Unicode );
|
|
node.closeNode( "CHARSET" );
|
|
}
|
|
}
|
|
if (format.id == 4 || format.id == 6)
|
|
{
|
|
// Variable or anchor
|
|
node.closeTag( true );
|
|
node.append( format.xmldata );
|
|
}
|
|
node.closeNode( "FORMAT" );
|
|
}
|
|
|
|
void RTFImport::addLayout( DomNode &node, const TQString &name, const RTFLayout &layout, bool frameBreak )
|
|
{
|
|
// Style name and alignment
|
|
node.addNode( "NAME" );
|
|
node.setAttribute( "value", CheckAndEscapeXmlText(name) );
|
|
node.closeNode( "NAME" );
|
|
node.addNode( "FLOW" );
|
|
node.setAttribute( "align", alignN[layout.alignment] );
|
|
node.closeNode( "FLOW" );
|
|
|
|
// Indents
|
|
if (layout.firstIndent || layout.leftIndent || layout.rightIndent)
|
|
{
|
|
node.addNode( "INDENTS" );
|
|
|
|
if (layout.firstIndent)
|
|
node.setAttribute( "first", .05*layout.firstIndent );
|
|
if (layout.leftIndent)
|
|
node.setAttribute( "left", .05*layout.leftIndent );
|
|
if (layout.rightIndent)
|
|
node.setAttribute( "right", .05*layout.rightIndent );
|
|
|
|
node.closeNode( "INDENTS" );
|
|
}
|
|
|
|
// Offets
|
|
if (layout.spaceBefore || layout.spaceAfter)
|
|
{
|
|
node.addNode( "OFFSETS" );
|
|
|
|
if (layout.spaceBefore)
|
|
node.setAttribute( "before", .05*layout.spaceBefore );
|
|
if (layout.spaceAfter)
|
|
node.setAttribute( "after", .05*layout.spaceAfter );
|
|
|
|
node.closeNode( "OFFSETS" );
|
|
}
|
|
|
|
|
|
// Linespacing
|
|
|
|
TQString lineSpacingType;
|
|
TQString lineSpacingValue;
|
|
if ( layout.spaceBetweenMultiple )
|
|
{
|
|
// Note: 240 is a sort of magic value for one line (Once upon a time, it meant 12pt for a single line)
|
|
switch (layout.spaceBetween )
|
|
{
|
|
case 240:
|
|
{
|
|
lineSpacingType = "single"; // ### TODO: does KWord really supports this?
|
|
break;
|
|
}
|
|
case 360:
|
|
{
|
|
lineSpacingType = "oneandhalf";
|
|
break;
|
|
}
|
|
case 480 :
|
|
{
|
|
lineSpacingType = "double";
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
if ( layout.spaceBetween > 0 )
|
|
{
|
|
lineSpacingType = "multiple";
|
|
lineSpacingValue.setNum( layout.spaceBetween / 240.0 );
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (layout.spaceBetween > 0)
|
|
{
|
|
lineSpacingType = "atleast";
|
|
lineSpacingValue.setNum( 0.05*layout.spaceBetween );
|
|
}
|
|
if (layout.spaceBetween < 0)
|
|
{
|
|
// negative linespace means "exact"
|
|
lineSpacingType = "fixed" ;
|
|
lineSpacingValue.setNum( -0.05*layout.spaceBetween );
|
|
}
|
|
}
|
|
|
|
if ( ! lineSpacingType.isEmpty() )
|
|
{
|
|
node.addNode( "LINESPACING" );
|
|
node.setAttribute( "type", lineSpacingType );
|
|
if ( ! lineSpacingValue.isEmpty() )
|
|
node.setAttribute( "spacingvalue", lineSpacingValue );
|
|
node.closeNode( "LINESPACING" );
|
|
}
|
|
|
|
if (layout.keep || layout.pageBB || layout.pageBA || frameBreak || layout.keepNext)
|
|
{
|
|
node.addNode( "PAGEBREAKING" );
|
|
node.setAttribute( "linesTogether", boolN[layout.keep] );
|
|
node.setAttribute( "hardFrameBreak", boolN[layout.pageBB] );
|
|
node.setAttribute( "hardFrameBreakAfter", boolN[layout.pageBA || frameBreak] );
|
|
node.setAttribute( "keepWithNext", boolN[layout.keepNext] );
|
|
node.closeNode( "PAGEBREAKING" );
|
|
}
|
|
|
|
// Paragraph borders
|
|
for (uint i=0; i < 4; i++)
|
|
{
|
|
const RTFBorder &border = layout.borders[i];
|
|
|
|
if (border.style != RTFBorder::None || border.width > 0)
|
|
{
|
|
node.addNode( borderN[i] );
|
|
node.addColor( ((uint)border.color >= colorTable.count())
|
|
? (TQColor &)TQt::black : colorTable[border.color] );
|
|
node.setAttribute( "style", (int)border.style & 0xf );
|
|
node.setAttribute( "width", (border.width < 20) ? 1 : border.width /20 );
|
|
node.closeNode( borderN[i] );
|
|
}
|
|
}
|
|
|
|
// Add automatic tab stop for hanging indent
|
|
if (layout.firstIndent < 0 && layout.leftIndent > 0)
|
|
{
|
|
node.addNode( "TABULATOR" );
|
|
node.setAttribute( "type", 0 );
|
|
node.setAttribute( "ptpos", .05*layout.leftIndent );
|
|
node.closeNode( "TABULATOR" );
|
|
}
|
|
|
|
// Tabulators
|
|
if (!layout.tablist.isEmpty())
|
|
{
|
|
// ### TODO: use ConstIterator
|
|
for (uint i=0; i < layout.tablist.count(); i++)
|
|
{
|
|
const RTFTab &tab = layout.tablist[i];
|
|
int l = (int)tab.leader;
|
|
node.addNode( "TABULATOR" );
|
|
node.setAttribute( "type", tab.type );
|
|
node.setAttribute( "ptpos", .05*tab.position );
|
|
node.setAttribute( "filling", (l < 2) ? l : ((l == 2) ? 1 : 2) );
|
|
node.setAttribute( "width", (l == 4) ? 1. : 0.5 );
|
|
node.closeNode( "TABULATOR" );
|
|
}
|
|
}
|
|
}
|
|
|
|
void RTFImport::addParagraph( DomNode &node, bool frameBreak )
|
|
{
|
|
node.addNode( "PARAGRAPH" );
|
|
node.addNode( "TEXT" );
|
|
node.appendNode( textState->text );
|
|
node.closeNode( "TEXT" );
|
|
|
|
// Search for style in style sheet
|
|
TQString name;
|
|
const RTFFormat* format = &state.format;
|
|
const int styleNum = state.layout.style;
|
|
|
|
const TQValueList<RTFStyle>::ConstIterator endStyleSheet = styleSheet.end();
|
|
for ( TQValueList<RTFStyle>::ConstIterator it=styleSheet.begin(); it!=endStyleSheet; ++it )
|
|
{
|
|
if ( (*it).layout.style == styleNum )
|
|
{
|
|
if ( textState->length > 0 )
|
|
{
|
|
format = &(*it).format;
|
|
}
|
|
name = (*it).name;
|
|
break;
|
|
}
|
|
}
|
|
kwFormat.fmt = *format;
|
|
kwFormat.id = 1;
|
|
kwFormat.pos = 0;
|
|
kwFormat.len = textState->length;
|
|
|
|
if ( name.isEmpty() )
|
|
{
|
|
kdWarning(30515) << "Style name empty! Assuming Standard!" << endl;
|
|
name = "Standard";
|
|
}
|
|
|
|
// Insert character formatting
|
|
bool hasFormats = false;
|
|
|
|
for ( TQValueList<KWFormat>::ConstIterator it = textState->formats.begin(); it != textState->formats.end(); ++it )
|
|
{
|
|
if ( (*it).id != 1 || (*it).fmt != *format )
|
|
{
|
|
if (!hasFormats)
|
|
{
|
|
node.addNode( "FORMATS" );
|
|
hasFormats = true;
|
|
}
|
|
addFormat( node, (*it), format );
|
|
}
|
|
}
|
|
if (hasFormats)
|
|
{
|
|
node.closeNode( "FORMATS" );
|
|
}
|
|
|
|
// Write out layout and format
|
|
node.addNode( "LAYOUT" );
|
|
addLayout( node, name, state.layout, frameBreak );
|
|
addFormat( node, kwFormat, 0L );
|
|
node.closeNode( "LAYOUT" );
|
|
node.closeNode( "PARAGRAPH" );
|
|
|
|
// Clear plain text and formats for next paragraph
|
|
textState->text.clear();
|
|
textState->length = 0;
|
|
textState->formats.clear();
|
|
}
|
|
|
|
void RTFImport::finishTable()
|
|
{
|
|
kdDebug(30515) << "Starting TFImport::finishTable..." << endl;
|
|
TQCString emptyArray;
|
|
TQValueList<int> cellx;
|
|
int left = 0, right = 0;
|
|
|
|
insertTableRow();
|
|
|
|
// Calculate maximum horizontal extents
|
|
// ### TODO: use ConstIterator
|
|
for (uint i=0; i < textState->rows.count(); i++)
|
|
{
|
|
RTFTableRow &row = textState->rows[i];
|
|
|
|
if (row.left < left || i == 0)
|
|
left = row.left;
|
|
if (row.cells.last().x > right || i == 0)
|
|
right = row.cells.last().x;
|
|
}
|
|
|
|
// Force rectangular table (fill gaps with empty cells)
|
|
// ### TODO: use ConstIterator
|
|
for (uint i=0; i < textState->rows.count(); i++)
|
|
{
|
|
RTFTableRow &row = textState->rows[i];
|
|
|
|
if (row.left > left)
|
|
{
|
|
row.frameSets.prepend( emptyArray );
|
|
emptyCell.x = row.left;
|
|
row.cells.prepend( emptyCell );
|
|
row.left = left;
|
|
}
|
|
if (row.cells.last().x < right)
|
|
{
|
|
row.frameSets << emptyArray;
|
|
emptyCell.x = right;
|
|
row.cells << emptyCell;
|
|
}
|
|
// ### TODO: use ConstIterator
|
|
for (uint k=0; k < row.cells.count(); k++)
|
|
{
|
|
if (!cellx.contains( row.cells[k].x ))
|
|
cellx << row.cells[k].x;
|
|
}
|
|
if (!cellx.contains( row.left ))
|
|
{
|
|
cellx << row.left;
|
|
}
|
|
}
|
|
|
|
// Sort vertical cell boundaries
|
|
// ### TODO: use ConstIterator
|
|
for (uint k=0; k < cellx.count(); k++)
|
|
{
|
|
for (uint l=k+1; l < cellx.count(); l++)
|
|
{
|
|
if (cellx[l] < cellx[k])
|
|
{
|
|
int tmp = cellx[l];
|
|
cellx[l] = cellx[k];
|
|
cellx[k] = tmp;
|
|
}
|
|
}
|
|
}
|
|
int y1 = 0;
|
|
|
|
// Store cell frame and table information
|
|
// ### TODO: use ConstIterator
|
|
for (uint i=0; i < textState->rows.count(); i++)
|
|
{
|
|
RTFTableRow &row = textState->rows[i];
|
|
int h = abs( row.height );
|
|
int y2 = y1 + ((h < 400) ? 400 : h); // KWord work-around
|
|
int x1 = row.left;
|
|
|
|
for (uint k=0; k < row.cells.count(); k++)
|
|
{
|
|
char buf[64];
|
|
int x2 = row.cells[k].x;
|
|
int col = cellx.findIndex( x1 );
|
|
|
|
sprintf( buf, "Table %d Cell %d,%d", textState->table, i, col );
|
|
frameSets.addFrameSet( buf, 1, 0 );
|
|
sprintf( buf, "Table %d", textState->table );
|
|
frameSets.setAttribute( "grpMgr", buf );
|
|
frameSets.setAttribute( "row", (int)i );
|
|
frameSets.setAttribute( "col", col );
|
|
frameSets.setAttribute( "rows", 1 );
|
|
frameSets.setAttribute( "cols", cellx.findIndex( x2 ) - col );
|
|
|
|
frameSets.addFrame( x1, y1, x2, y2, (row.height < 0) ? 2 : 0, 1, 0 );
|
|
|
|
// Frame borders
|
|
for (uint i=0; i < 4; i++)
|
|
{
|
|
RTFBorder &border = row.cells[k].borders[i];
|
|
|
|
if (border.style != RTFBorder::None || border.width > 0)
|
|
{
|
|
const char *id = "lrtb";
|
|
TQColor &c = ((uint)border.color >= colorTable.count())
|
|
? (TQColor &)TQt::black : colorTable[border.color];
|
|
frameSets.addBorder( (int)id[i], c, (int)border.style & 0x0f,
|
|
.05*(!border.width ? 10 : border.width) );
|
|
}
|
|
}
|
|
|
|
// Frame background color
|
|
if ((uint)row.cells[k].bgcolor < colorTable.count())
|
|
{
|
|
TQColor &color = colorTable[row.cells[k].bgcolor];
|
|
frameSets.setAttribute( "bkRed", color.red() );
|
|
frameSets.setAttribute( "bkGreen", color.green() );
|
|
frameSets.setAttribute( "bkBlue", color.blue() );
|
|
}
|
|
frameSets.closeNode( "FRAME" );
|
|
frameSets.append( row.frameSets[k] );
|
|
frameSets.closeNode( "FRAMESET" );
|
|
x1 = x2;
|
|
}
|
|
y1 = y2;
|
|
}
|
|
textState->table = 0;
|
|
textState->rows.clear();
|
|
kdDebug(30515) << "Quitting TFImport::finishTable..." << endl;
|
|
}
|
|
|
|
void RTFImport::writeOutPart( const char *name, const DomNode& node )
|
|
{
|
|
KoStoreDevice* dev = m_chain->storageFile( name, KoStore::Write );
|
|
if ( dev )
|
|
{
|
|
TQTextStream stream( dev );
|
|
stream.setEncoding( TQTextStream::UnicodeUTF8 );
|
|
stream << node.toString();
|
|
}
|
|
else
|
|
kdError(30515) << "Could not write part " << name << endl;
|
|
}
|