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.
koffice/filters/kword/wordperfect/import/kwordfilter.cpp

423 lines
13 KiB

/* This file is part of the KDE project
Copyright (C) 2001 Ariya Hidayat <ariyahidayat@yahoo.de>
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.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "kwordfilter.h"
#include "parser.h"
#include <tqfileinfo.h>
#include <tqptrlist.h>
#include <tqstring.h>
#include <tqregexp.h>
using namespace WP;
// NOTE: 1 wpu = 1/1200 inch and 1 inch = 72 point
static double WPUToPoint( unsigned wpu )
{
return wpu * 72 / 1200;
}
// helper class
class KWordFormat
{
public:
bool bold, italic, underline, doubleunderline;
bool striked, superscript, subscript, redline;
bool color, highlight;
int red, green, blue;
int bgred, bggreen, bgblue;
double fontsize;
TQString fontface;
KWordFormat();
TQString asXML();
};
KWordFormat::KWordFormat()
{
bold = italic = underline = doubleunderline = FALSE;
striked = superscript = subscript = redline = FALSE;
color = highlight = FALSE;
red = green = blue = 0;
bgred = bggreen = bgblue = 255;
fontsize = 0.0;
fontface = "";
}
TQString KWordFormat::asXML()
{
TQString result;
if( bold ) result.append( " <WEIGHT value=\"75\" />\n" );
if( italic ) result.append( " <ITALIC value=\"1\" />\n" );
if( underline ) result.append( " <UNDERLINE value=\"1\" />\n" );
if( doubleunderline ) result.append( " <UNDERLINE value=\"double\" />\n" );
if( striked ) result.append( " <STRIKEOUT value=\"1\" />\n" );
if( subscript ) result.append( "<VERTALIGN value=\"1\" />\n" );
if( superscript ) result.append( "<VERTALIGN value=\"2\" />\n" );
if( !fontface.isEmpty() )
result.append( "<FONT name=\"" + fontface + "\" />\n" );
if( fontsize > 0.0 )
result.append( " <SIZE value=\"" + TQString::number(fontsize) + "\" />\n" );
if( color )
result.append( " <COLOR red=\"" + TQString::number(red) +
"\" green=\"" + TQString::number(green) +
"\" blue=\"" + TQString::number(blue) + "\" />\n" );
if( highlight )
result.append( " <TEXTBACKGROUNDCOLOR red=\"" + TQString::number(bgred) +
"\" green=\"" + TQString::number(bggreen) +
"\" blue=\"" + TQString::number(bgblue) + "\" />\n" );
return result;
}
static TQString mapAlign( Token::Align align )
{
switch( align )
{
case Token::Left: return "left";
case Token::Right: return "right";
case Token::Center: return "center";
case Token::Full: return "justify";
case Token::All: return "justify";
}
return "left";
}
// NOTE: KWord value for linespace: 72=one, 144=double, ..
// Special case: "0" is normal, "oneandhalf" is 108, "double" is 144.
static TQString mapLinespace( double linespace )
{
return TQString::number( linespace );
}
KWordFilter::KWordFilter ():Parser ()
{
}
bool
KWordFilter::parse (const TQString & filename)
{
int frameLeftMargin = 36, frameRightMargin = 36; // quick hack
if (!Parser::parse (filename))
return FALSE;
// this will force very last text and formatting to be flushed as well
tokens.append( new Token( Token::HardReturn ) );
TQString text;
TQString layout;
TQString formats;
int LeftMargin = 0;
int TopMargin = 36;
int RightMargin = 0;
int BottomMargin = 36;
int LeftMarginAdjust = 0;
int RightMarginAdjust = 0;
int lm = 0, rm = 0;
Token::Align align = Token::Left;
double linespace = 1.0;
root = "";
KWordFormat flag;
int format_pos;
TQString fmt;
// FIXME replace with doc initial code or default style
format_pos = 0;
fmt = flag.asXML();
for (TQPtrListIterator < Token > it (tokens); it; ++it)
{
unsigned int ucode;
int attr;
int len;
Token *t = it.current ();
Token::Type type = t->type ();
switch (type)
{
case Token::Text:
text.append( t->text() );
break;
case Token::SoftSpace:
case Token::HardSpace:
text.append( " " );
break;
case Token::SoftReturn:
// ignore
break;
case Token::AttrOff:
case Token::AttrOn:
case Token::FontColor:
case Token::FontSize:
case Token::FontFace:
case Token::HighlightOn:
case Token::HighlightOff:
if( type == Token::FontColor )
{
flag.color = true;
flag.red = t->red();
flag.green = t->green();
flag.blue= t->blue();
}
else if( type == Token::HighlightOn )
{
flag.highlight = true;
flag.bgred = t->red();
flag.bggreen = t->green();
flag.bgblue = t->blue();
}
else if( type == Token::HighlightOff )
{
// RGB in the data is last used highlight color
// to go back to normal color, simply XOR would do the trick
flag.highlight = false;
flag.bgred ^= t->red();
flag.bggreen ^= t->green();
flag.bgblue ^= t->blue();
}
else if( type == Token::FontSize )
{
// WP font size is 1/3600th inch
// NOTE 72 pt is 1 inch
if( t->value() > 50 )
flag.fontsize = t->value()*72.0/3600;
}
else if( type == Token::FontFace )
{
flag.fontface = t->fontface();
}
else
{
attr = t->attr();
if( attr == Token::Bold ) flag.bold = ( type == Token::AttrOn );
if( attr == Token::Italic ) flag.italic = ( type == Token::AttrOn );
if( attr == Token::Underline) flag.underline = ( type == Token::AttrOn );
if( attr == Token::DoubleUnderline ) flag.doubleunderline = ( type == Token::AttrOn );
if( attr == Token::StrikedOut ) flag.striked = ( type == Token::AttrOn );
if( attr == Token::Subscript ) flag.subscript = ( type == Token::AttrOn );
if( attr == Token::Superscript ) flag.superscript = ( type == Token::AttrOn );
if( attr == Token::Redline ) flag.redline = ( type == Token::AttrOn );
}
// process previous fmt first
len = text.length() - format_pos;
formats.append ( "<FORMAT id=\"1\" pos=\"" + TQString::number( format_pos ) +
"\" len=\"" + TQString::number( len )+ "\">\n" );
formats.append( fmt );
formats.append ( "</FORMAT>\n" );
// now current format
fmt = flag.asXML();
format_pos= text.length();
break;
case Token::HardReturn:
case Token::DormantHardReturn:
// last formatting not flushed
// SEE ABOVE
len = text.length() - format_pos;
formats.append ( " <FORMAT id=\"1\" pos=\"" + TQString::number( format_pos ) +
"\" len=\"" + TQString::number( len )+ "\">\n" );
formats.append( " " + fmt );
formats.append ( " </FORMAT>\n" );
layout = "";
layout.append( "<LAYOUT>\n" );
layout.append( " <NAME value=\"Standard\" />\n" );
layout.append( " <FLOW align=\"" + mapAlign( align ) + "\" />\n" );
layout.append( " <LINESPACING value=\"" + mapLinespace( linespace) + "\" />\n" );
layout.append( " <LEFTBORDER width=\"0\" style=\"0\" />\n" );
layout.append( " <RIGHTBORDER width=\"0\" style=\"0\" />\n" );
layout.append( " <TOPBORDER width=\"0\" style=\"0\" />\n" );
layout.append( " <BOTTOMBORDER width=\"0\" style=\"0\" />\n" );
lm = LeftMargin + LeftMarginAdjust - frameLeftMargin;
rm = RightMargin + RightMarginAdjust - frameRightMargin;
layout.append( " <INDENTS left=\"" + TQString::number( TQMAX( 0, lm ) ) + "\"" +
" right=\"" + TQString::number( TQMAX( 0 , rm ) ) + "\"" +
" first=\"0\" />\n" );
layout.append( " <OFFSETS />\n" );
layout.append( " <PAGEBREAKING />\n" );
layout.append( " <COUNTER />\n" );
layout.append( " <FORMAT id=\"1\">\n" );
layout.append( " <WEIGHT value=\"50\" />\n" );
layout.append( " <ITALIC value=\"0\" />\n" );
layout.append( " <UNDERLINE value=\"0\" />\n" );
layout.append( " <STRIKEOUT value=\"0\" />\n" );
layout.append( " <CHARSET value=\"0\" />\n" );
layout.append( " <VERTALIGN value=\"0\" />\n" );
layout.append( " </FORMAT>\n" );
layout.append( "</LAYOUT>\n" );
// encode text for XML-ness
// FIXME could be faster without TQRegExp
text.replace( TQRegExp("&"), "&amp;" );
text.replace( TQRegExp("<"), "&lt;" );
text.replace( TQRegExp(">"), "&gt;" );
text.replace( TQRegExp("\""), "&quot;" );
text.replace( TQRegExp("'"), "&apos;" );
// construct the <PARAGRAPH>
root.append( "<PARAGRAPH>\n" );
root.append( "<TEXT>" + text + "</TEXT>\n" );
root.append( "<FORMATS>\n");
root.append( formats );
root.append( "</FORMATS>\n");
root.append( layout );
root.append( "</PARAGRAPH>\n" );
// for the next paragraph
text = "";
formats = "";
format_pos = 0;
fmt = flag.asXML();
break;
case Token::HardHyphen:
text.append( "-" );
break;
case Token::LeftMargin:
LeftMargin = (int) WPUToPoint( t->value() );
break;
case Token::RightMargin:
RightMargin = (int) WPUToPoint( t->value() );
break;
case Token::TopMargin:
TopMargin = (int) WPUToPoint( t->value() );
break;
case Token::BottomMargin:
BottomMargin = (int) WPUToPoint( t->value() );
break;
case Token::LeftMarginAdjust:
LeftMarginAdjust = (int)WPUToPoint( t->value() );
break;
case Token::RightMarginAdjust:
RightMarginAdjust = (int)WPUToPoint( t->value() );
break;
case Token::Justification:
align = t->align();
break;
case Token::Linespace:
// NOTE assume 1.0 = 12 pt, 2.0 = 24 pt, 1.5=18
// from parser.cpp, linespace is stored as 1/65536th inch
linespace = t->value() * 12.0 / 65536;
break;
case Token::ExtChar:
ucode = Parser::ExtCharToUnicode (t->charset (), t->charcode ());
if (ucode == 0) ucode = 32;
text.append( TQChar (ucode) );
break;
case Token::TabHardFlushRight:
// FIXME
text.append( " " );
break;
case Token::None:
default:
break;
};
}
TQString content = root;
root = "<!DOCTYPE DOC>\n";
root.append( "<DOC mime=\"application/x-kword\" syntaxVersion=\"2\" editor=\"KWord\">\n");
// quick hack, think of something better in the future
LeftMargin = RightMargin = 36;
// paper definition
root.append( "<PAPER width=\"595\" height=\"841\" format=\"1\" fType=\"0\" orientation=\"0\" hType=\"0\" columns=\"1\">\n" );
root.append( " <PAPERBORDERS left=\"" + TQString::number(frameLeftMargin) +
"\" right=\"" + TQString::number(frameRightMargin) +
"\" top=\"" + TQString::number(TopMargin) +
"\" bottom=\"" + TQString::number(BottomMargin) + "\" />\n" );
root.append( "</PAPER>\n" );
root.append( "<ATTRIBUTES standardpage=\"1\" hasFooter=\"0\" hasHeader=\"0\" processing=\"0\" />\n" );
root.append( "<FRAMESETS>\n" );
root.append( "<FRAMESET removable=\"0\" frameType=\"1\" frameInfo=\"0\" autoCreateNewFrame=\"1\">\n" );
root.append( "<FRAME right=\"567\" left=\"28\" top=\"42\" bottom=\"799\" />\n" );
root.append( content );
root.append( "</FRAMESET>\n" );
root.append( "</FRAMESETS>\n" );
root.append( "</DOC>\n" );
// in case no document summary is available, then make default
// set so that basename of the filename becomes the document title
// e.g /home/ariya/test/resume.wpd will have 'resume' as the title
if( docTitle.isEmpty() )
{
TQFileInfo info( filename );
docTitle = info.baseName();
}
// create document information
documentInfo = "<!DOCTYPE document-info>\n";
documentInfo += "<document-info>\n";
documentInfo += "<log><text></text></log>\n";
documentInfo += "<author>\n";
documentInfo += "<full-name>" + docAuthor + "</full-name>\n";
documentInfo += "<title></title>\n";
documentInfo += "<company></company>\n";
documentInfo += "<email></email>\n";
documentInfo += "<telephone></telephone>\n";
documentInfo += "</author>\n";
documentInfo += "<about>\n";
documentInfo += "<abstract><![CDATA[" + docAbstract + "]]></abstract>\n";
documentInfo += "<title>" + docTitle + "</title>\n";
documentInfo += "</about>\n";
documentInfo += "</document-info>";
return TRUE;
}