/* This file is part of the KDE project Copyright (C) 2002 Ariya Hidayat 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 "amiproparser.h" #include #include #include #include const float AmiPro::LS_Single = -1; const float AmiPro::LS_OneAndHalf = -1.5; const float AmiPro::LS_Double = -2; // helper function to "unescape" AmiPro string static TQString AmiProUnescape( const TQString& str ) { TQString result; for( unsigned i=0; i< str.length(); i++ ) { TQChar c = str[i]; result.append( c ); // check for "@@", decoded as '@' if( c == '@' ) if( str[i+1] == '@' ) i++ ; // eat ! // a few possible escape sequence if( c == '<' ) { // check for "<<", decoded as '<' if( str[i+1] == '<' ) { result.truncate( result.length() - 1 ); // remove the '<' result.append( '<' ); i++; } // check for "<;>", decoded as '>' if( str[i+1] == ';' ) { result.truncate( result.length() - 1 ); // remove the '<' result.append( '>' ); i+=2; } // check for "<[>", decoded as '[' if( str[i+1] == '[' ) { result.truncate( result.length() - 1 ); // remove the '<' result.append( '[' ); i+=2; } // some special characters if( str[i+1] == '/' ) { if( str[i+2] == 'R' ) { result.truncate( result.length() - 1 ); // remove the '<' result.append( '\'' ); // decoded as ' i += 3; } else { result.truncate( result.length() - 1 ); // remove the '<' result.append( TQChar(str[i+2].unicode() + 0x40 ) ); i += 3; } } // yet another special characters if( str[i+1] == '\\' ) { result.truncate( result.length() - 1 ); // remove the '<' result.append( TQChar(str[i+2].unicode() | 0x80 ) ); i += 3; } } } return result; } AmiProParser::AmiProParser() { m_result = OK; m_listener = NULL; } AmiProParser::~AmiProParser() { } bool AmiProParser::setResult( int result ) { m_result = result; return m_result == OK; } void AmiProParser::setListener( AmiProListener *listener ) { m_listener = listener; } bool AmiProParser::process( const TQString& filename ) { TQString line; // open input file TQFile in( filename ); if( !in.open( IO_ReadOnly)) return setResult( FileError ); TQTextStream stream; stream.setDevice( &in ); // the first should be "[ver]" line = stream.readLine(); if( line != "[ver]" ) return setResult( InvalidFormat ); // get format version, typically 4 line = stream.readLine(); int format_version = line.toInt(); // FIXME is this necessary ? // accept only format version 4 if( format_version != 4 ) return setResult( InvalidFormat ); // initialize m_currentFormat = AmiProFormat(); m_formatList.clear(); m_styleList.clear(); m_currentSection = ""; TQStringList lines; // parse line-by-line for( ;; ) { line = stream.readLine(); if( line.isNull() ) break; TQString old_section = m_currentSection; bool enter_new_section = false; // new main section ? if( !line.isEmpty() ) if( line[0] == '[' ) { enter_new_section = true; m_currentSection = ""; for( unsigned i=1; i 0 ) parseParagraph( lines.join( " " ) ); processCloseDocument(); return true; } bool AmiProParser::processOpenDocument() { if( m_listener ) return m_listener->doOpenDocument(); return true; } bool AmiProParser::processCloseDocument() { if( m_listener ) return m_listener->doCloseDocument(); return true; } bool AmiProParser::parseParagraph( const TQStringList& lines ) { m_text = ""; m_formatList.clear(); m_layout = AmiProLayout(); // join the lines, up until first char in a line is '>' TQString partext = ""; for( unsigned i=0; i' ) break; else partext.append( lines[i] + "\n" ); TQChar ch = partext[partext.length()-1]; while( ( ch == '\n' ) || ( ch == '\r' ) ) { partext.remove( partext.length()-1, 1 ); ch = partext[partext.length()-1]; } // "unescape", process special chars and such TQString text = AmiProUnescape( partext ); // apply default style first m_layout.applyStyle( findStyle( "Body Text" ) ); for( unsigned i=0; idoParagraph( m_text, m_formatList, m_layout ); return true; } bool AmiProParser::parseStyle( const TQStringList& lines ) { AmiProStyle style; style.name = AmiProUnescape( lines[0].stripWhiteSpace() ); if( style.name.isEmpty() ) return true; // font if( lines[2].stripWhiteSpace() != "[fnt]" ) return true; style.fontFamily = lines[3].stripWhiteSpace(); style.fontSize = lines[4].stripWhiteSpace().toFloat() / 20.0; unsigned color = lines[5].stripWhiteSpace().toUInt(); style.fontColor.setRgb( color&255, (color>>8)&255, (color>>16)&255); unsigned flag = lines[6].stripWhiteSpace().toUInt(); style.bold = flag & 1; style.italic = flag & 2; style.underline = flag & 4; style.word_underline = flag & 8; style.double_underline = flag & 64; // alignment if( lines[7].stripWhiteSpace() != "[algn]" ) return true; unsigned align_flag = lines[8].stripWhiteSpace().toUInt(); if( align_flag & 1 ) style.align = TQt::AlignLeft; if( align_flag & 2 ) style.align = TQt::AlignRight; if( align_flag & 4 ) style.align = TQt::AlignCenter; if( align_flag & 8 ) style.align = TQt::AlignJustify; // linespace if( lines[13].stripWhiteSpace() != "[spc]" ) return true; unsigned ls_flag = lines[14].stripWhiteSpace().toUInt(); if( ls_flag & 1 ) style.linespace = AmiPro::LS_Single; if( ls_flag & 2 ) style.linespace = AmiPro::LS_OneAndHalf; if( ls_flag & 4 ) style.linespace = AmiPro::LS_Double; if( ls_flag & 8 ) style.linespace = lines[15].stripWhiteSpace().toFloat() / 20.0; style.spaceBefore = lines[17].stripWhiteSpace().toFloat() / 20.0; style.spaceAfter = lines[18].stripWhiteSpace().toFloat() / 20.0; m_styleList.append( style ); // "Style #0", "Style #1" and such are special styles // do not import these styles if( style.name.left( 7 ) != "Style #" ) if( m_listener ) return m_listener->doDefineStyle( style ); return true; } AmiProStyle AmiProParser::findStyle( const TQString& name ) { AmiProStyleList::iterator it; for( it=m_styleList.begin(); it!=m_styleList.end(); ++it ) { AmiProStyle& style = *it; if( style.name == name ) return style; } return AmiProStyle(); } bool AmiProParser::handleTag( const TQString& tag ) { // > (actually encoded as <;>) if( tag == ";" ) m_text.append( ">" ); // [ (actually encoded as <[>) if( tag == "[" ) m_text.append( "[" ); // bold on if( tag == "+!" ) { m_currentFormat.bold = true; m_currentFormat.pos = m_text.length(); m_formatList.append( m_currentFormat ); } // bold off if( tag == "-!" ) { m_currentFormat.bold = false; m_currentFormat.pos = m_text.length(); m_formatList.append( m_currentFormat ); } // italic on if( tag == "+\"" ) { m_currentFormat.italic = true; m_currentFormat.pos = m_text.length(); m_formatList.append( m_currentFormat ); } // italic off if( tag == "-\"" ) { m_currentFormat.italic = false; m_currentFormat.pos = m_text.length(); m_formatList.append( m_currentFormat ); } // underline on if( tag == "+#" ) { m_currentFormat.underline = true; m_currentFormat.pos = m_text.length(); m_formatList.append( m_currentFormat ); } // underline off if( tag == "-#" ) { m_currentFormat.underline = false; m_currentFormat.pos = m_text.length(); m_formatList.append( m_currentFormat ); } // double underline on if( tag == "+)" ) { m_currentFormat.double_underline = true; m_currentFormat.pos = m_text.length(); m_formatList.append( m_currentFormat ); } // double underline off if( tag == "-)" ) { m_currentFormat.double_underline = false; m_currentFormat.pos = m_text.length(); m_formatList.append( m_currentFormat ); } // word underline on if( tag == "+$" ) { m_currentFormat.word_underline = true; m_currentFormat.pos = m_text.length(); m_formatList.append( m_currentFormat ); } // word underline off if( tag == "-$" ) { m_currentFormat.word_underline = false; m_currentFormat.pos = m_text.length(); m_formatList.append( m_currentFormat ); } // superscript on if( tag == "+&" ) { m_currentFormat.superscript = true; m_currentFormat.pos = m_text.length(); m_formatList.append( m_currentFormat ); } // superscript off if( tag == "-&" ) { m_currentFormat.superscript = false; m_currentFormat.pos = m_text.length(); m_formatList.append( m_currentFormat ); } // subscript on if( tag == "+'" ) { m_currentFormat.subscript = true; m_currentFormat.pos = m_text.length(); m_formatList.append( m_currentFormat ); } // subscript off if( tag == "-'" ) { m_currentFormat.subscript = false; m_currentFormat.pos = m_text.length(); m_formatList.append( m_currentFormat ); } // strikethrough on if( tag == "+%" ) { m_currentFormat.strikethrough = true; m_currentFormat.pos = m_text.length(); m_formatList.append( m_currentFormat ); } // strikethrough off if( tag == "-%" ) { m_currentFormat.strikethrough = false; m_currentFormat.pos = m_text.length(); m_formatList.append( m_currentFormat ); } // paragraph left-align if( tag == "+@" ) m_layout.align = TQt::AlignLeft; // paragraph right-align if( tag == "+A" ) m_layout.align = TQt::AlignRight; // paragraph center if( tag == "+B" ) m_layout.align = TQt::AlignCenter; // paragraph justify if( tag == "+C" ) m_layout.align = TQt::AlignJustify; // linespace if( tag.left( 3 ) == ":S+" ) { float ls = tag.right( tag.length() - 3 ).toFloat(); m_layout.linespace = (ls == -1) ? AmiPro::LS_Single : (ls == -2) ? AmiPro::LS_OneAndHalf : (ls == -3) ? AmiPro::LS_Double : ls / 20.0; } // font if( tag.left( 2 ) == ":f" ) { TQString fontdesc = tag.right( tag.length()-2 ); TQStringList desc = TQStringList::split( ",", fontdesc ); if( desc.count() > 0 ) m_currentFormat.fontSize = desc[0].toFloat() / 20.0; if( desc.count() > 1 ) { TQString fontFamily = desc[1]; if( fontFamily[0].isDigit() ) fontFamily.remove( 0, 1 ); m_currentFormat.fontFamily = fontFamily; } if( desc.count() > 4 ) { unsigned red = desc[2].toUInt(); unsigned green = desc[3].toUInt(); unsigned blue = desc[4].toUInt(); m_currentFormat.fontColor.setRgb( red, green, blue ); } m_formatList.append( m_currentFormat ); } return true; } // text formatting AmiProFormat::AmiProFormat() { pos = len = 0; bold = italic = underline = word_underline = double_underline = subscript = superscript = strikethrough = FALSE; fontFamily = ""; fontSize = 12; fontColor = TQt::black; } void AmiProFormat::assign( const AmiProFormat& f ) { pos = f.pos; len = f.len; bold = f.bold; italic = f.italic; underline = f.underline; word_underline = f.word_underline; double_underline = f.double_underline; subscript = f.subscript; superscript = f.superscript; strikethrough = f.strikethrough; fontFamily = f.fontFamily; fontSize = f.fontSize; fontColor = f.fontColor; } AmiProFormat::AmiProFormat( const AmiProFormat& f ) { assign( f ); } AmiProFormat& AmiProFormat::operator=( const AmiProFormat& f ) { assign( f ); return *this; } void AmiProFormat::applyStyle( const AmiProStyle& style ) { fontFamily = style.fontFamily; fontSize = style.fontSize; fontColor = style.fontColor; bold = style.bold; italic = style.italic; underline = style.underline; word_underline = style.word_underline; double_underline = style.double_underline; subscript = style.subscript; superscript = style.superscript; strikethrough = style.strikethrough; } // paragraph layout AmiProLayout::AmiProLayout() { name = ""; fontFamily = ""; fontSize = 12; fontColor = TQt::black; bold = italic = underline = word_underline = double_underline = subscript = superscript = strikethrough = FALSE; align = TQt::AlignLeft; linespace = AmiPro::LS_Single; spaceBefore = spaceAfter = 0; } void AmiProLayout::assign( const AmiProLayout &l ) { name = l.name; fontFamily = l.fontFamily; fontSize = l.fontSize; fontColor = l.fontColor; bold = l.bold; italic = l.italic; underline = l.underline; word_underline = l.word_underline; double_underline = l.double_underline; subscript = l.subscript; superscript = l.superscript; strikethrough = l.strikethrough; align = l.align; linespace = l.linespace; spaceBefore = l.spaceBefore; spaceAfter = l.spaceAfter; } AmiProLayout::AmiProLayout( const AmiProLayout& l ) { assign( l ); } AmiProLayout& AmiProLayout::operator=( const AmiProLayout& l ) { assign( l ); return *this; } void AmiProLayout::applyStyle( const AmiProStyle& style ) { fontFamily = style.fontFamily; fontSize = style.fontSize; fontColor = style.fontColor; bold = style.bold; italic = style.italic; underline = style.underline; word_underline = style.word_underline; double_underline = style.double_underline; subscript = style.subscript; superscript = style.superscript; strikethrough = style.strikethrough; align = style.align; linespace = style.linespace; spaceBefore = style.spaceBefore; spaceAfter = style.spaceAfter; } // style definition AmiProStyle::AmiProStyle() { name = "Unnamed"; fontFamily = ""; fontSize = 12; fontColor = TQt::black; bold = italic = underline = word_underline = double_underline = subscript = superscript = strikethrough = FALSE; linespace = AmiPro::LS_Single; spaceBefore = spaceAfter = 0; } void AmiProStyle::assign( const AmiProStyle& s ) { name = s.name; fontFamily = s.fontFamily; fontSize = s.fontSize; fontColor = s.fontColor; bold = s.bold; italic = s.italic; underline = s.underline; word_underline = s.word_underline; double_underline = s.double_underline; subscript = s.subscript; superscript = s.superscript; strikethrough = s.strikethrough; align = s.align; linespace = s.linespace; spaceBefore = s.spaceBefore; spaceAfter = s.spaceAfter; } AmiProStyle::AmiProStyle( const AmiProStyle& s ) { assign( s ); } AmiProStyle& AmiProStyle::operator=( const AmiProStyle& s ) { assign( s ); return *this; } // base listener for the parser AmiProListener::AmiProListener() { } AmiProListener::~AmiProListener() { } #define DO_TRUE_DEFINITION(string) \ bool AmiProListener::string \ {\ return true;\ } DO_TRUE_DEFINITION(doOpenDocument()) DO_TRUE_DEFINITION(doCloseDocument()) DO_TRUE_DEFINITION(doDefineStyle(const AmiProStyle& style)) DO_TRUE_DEFINITION(doParagraph(const TQString& text, AmiProFormatList formatList, AmiProLayout& ))