1
/* This file is part of the KDE project
2
Copyright (C) 2002 Ariya Hidayat <ariyahidayat@yahoo.de>
4
This library is free software; you can redistribute it and/or
5
modify it under the terms of the GNU Library General Public
6
License as published by the Free Software Foundation; either
7
version 2 of the License, or (at your option) any later version.
9
This library is distributed in the hope that it will be useful,
10
but WITHOUT ANY WARRANTY; without even the implied warranty of
11
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12
Library General Public License for more details.
14
You should have received a copy of the GNU Library General Public License
15
along with this library; see the file COPYING.LIB. If not, write to
16
the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17
Boston, MA 02111-1307, USA.
20
#include "amiproparser.h"
24
#include <qstringlist.h>
25
#include <qtextstream.h>
27
const float AmiPro::LS_Single = -1;
28
const float AmiPro::LS_OneAndHalf = -1.5;
29
const float AmiPro::LS_Double = -2;
31
// helper function to "unescape" AmiPro string
32
static QString AmiProUnescape( const QString& str )
36
for( unsigned i=0; i< str.length(); i++ )
41
// check for "@@", decoded as '@'
46
// a few possible escape sequence
50
// check for "<<", decoded as '<'
53
result.truncate( result.length() - 1 ); // remove the '<'
58
// check for "<;>", decoded as '>'
61
result.truncate( result.length() - 1 ); // remove the '<'
66
// check for "<[>", decoded as '['
69
result.truncate( result.length() - 1 ); // remove the '<'
74
// some special characters
77
result.truncate( result.length() - 1 ); // remove the '<'
78
result.append( QChar(str[i+2].unicode() + 0x40 ) );
82
// yet another special characters
83
if( str[i+1] == '\\' )
85
result.truncate( result.length() - 1 ); // remove the '<'
86
result.append( QChar(str[i+2].unicode() | 0x80 ) );
97
AmiProParser::AmiProParser()
103
AmiProParser::~AmiProParser()
107
bool AmiProParser::setResult( int result )
110
return m_result == OK;
113
void AmiProParser::setListener( AmiProListener *listener )
115
m_listener = listener;
118
bool AmiProParser::process( const QString& filename )
123
QFile in( filename );
124
if( !in.open( IO_ReadOnly))
125
return setResult( FileError );
128
stream.setDevice( &in );
130
// the first should be "[ver]"
131
line = stream.readLine();
132
if( line != "[ver]" )
133
return setResult( InvalidFormat );
135
// get format version, typically 4
136
line = stream.readLine();
137
int format_version = line.toInt();
139
// FIXME is this necessary ?
140
// accept only format version 4
141
if( format_version != 4 )
142
return setResult( InvalidFormat );
145
m_currentFormat = AmiProFormat();
146
m_formatList.clear();
148
m_currentSection = "";
151
// parse line-by-line
155
line = stream.readLine();
156
if( line.isNull() ) break;
158
QString old_section = m_currentSection;
159
bool enter_new_section = false;
161
// new main section ?
162
if( !line.isEmpty() ) if( line[0] == '[' )
164
enter_new_section = true;
165
m_currentSection = "";
166
for( unsigned i=1; i<line.length(); i++ )
167
if( line[i] == ']' ) break;
168
else m_currentSection += line[i];
172
if( enter_new_section && ( old_section == "tag" ) )
179
if( enter_new_section && ( old_section == "edoc" ) )
181
parseParagraph( lines.join(" ") );
186
if( !enter_new_section && ( old_section == "tag" ) )
188
lines.append( line );
192
if( !enter_new_section && ( old_section == "edoc" ) )
196
parseParagraph( lines );
199
lines.append( line );
203
if( enter_new_section && ( m_currentSection == "tag" ) )
209
if( enter_new_section && ( m_currentSection == "edoc" ) )
211
processOpenDocument();
218
if( lines.count() > 0 ) parseParagraph( lines.join( " " ) );
220
processCloseDocument();
225
bool AmiProParser::processOpenDocument()
227
if( m_listener ) return m_listener->doOpenDocument();
231
bool AmiProParser::processCloseDocument()
234
return m_listener->doCloseDocument();
238
bool AmiProParser::parseParagraph( const QStringList& lines )
241
m_formatList.clear();
242
m_layout = AmiProLayout();
244
// join the lines, up until first char in a line is '>'
245
QString partext = "";
246
for( unsigned i=0; i<lines.count(); i++ )
247
if( lines[i][0] == '>' ) break;
248
else partext.append( lines[i] + "\n" );
250
QChar ch = partext[partext.length()-1];
251
while( ( ch == '\n' ) || ( ch == '\r' ) )
253
partext.remove( partext.length()-1, 1 );
254
ch = partext[partext.length()-1];
257
// "unescape", process special chars and such
258
QString text = AmiProUnescape( partext );
260
// apply default style first
261
m_layout.applyStyle( findStyle( "Body Text" ) );
263
for( unsigned i=0; i<text.length(); i++ )
271
for( i++; (i < text.length()) &&
272
(text[i] != '>'); i++) tag.append( text[i] );
278
// handle style change
282
for( i++; (i < partext.length()) && (partext[i] != '@'); i++)
283
styleName += partext[i];
284
m_layout.name = styleName;
285
AmiProStyle style = findStyle( styleName );
286
m_currentFormat.applyStyle( style );
287
m_formatList.append( m_currentFormat );
288
m_layout.applyStyle( style );
296
// calc length of each format tag
297
for( unsigned j=0; j<m_formatList.count(); j++ )
300
AmiProFormat& format = m_formatList[j];
301
if( j < m_formatList.count()-1 )
303
AmiProFormat& nextformat = m_formatList[j+1];
304
nextpos = nextformat.pos;
306
else nextpos = m_text.length();
307
format.len = nextpos - format.pos;
311
return m_listener->doParagraph( m_text, m_formatList, m_layout );
316
bool AmiProParser::parseStyle( const QStringList& lines )
320
style.name = AmiProUnescape( lines[0].stripWhiteSpace() );
321
if( style.name.isEmpty() ) return true;
324
if( lines[2].stripWhiteSpace() != "[fnt]" ) return true;
325
style.fontFamily = lines[3].stripWhiteSpace();
326
style.fontSize = lines[4].stripWhiteSpace().toFloat() / 20.0;
328
unsigned color = lines[5].stripWhiteSpace().toUInt();
329
style.fontColor.setRgb( color&255, (color>>8)&255, (color>>16)&255);
331
unsigned flag = lines[6].stripWhiteSpace().toUInt();
332
style.bold = flag & 1;
333
style.italic = flag & 2;
334
style.underline = flag & 4;
335
style.word_underline = flag & 8;
336
style.double_underline = flag & 64;
339
if( lines[7].stripWhiteSpace() != "[algn]" ) return true;
340
unsigned align_flag = lines[8].stripWhiteSpace().toUInt();
341
if( align_flag & 1 ) style.align = Qt::AlignLeft;
342
if( align_flag & 2 ) style.align = Qt::AlignRight;
343
if( align_flag & 4 ) style.align = Qt::AlignCenter;
344
if( align_flag & 8 ) style.align = Qt::AlignJustify;
347
if( lines[13].stripWhiteSpace() != "[spc]" ) return true;
348
unsigned ls_flag = lines[14].stripWhiteSpace().toUInt();
349
if( ls_flag & 1 ) style.linespace = AmiPro::LS_Single;
350
if( ls_flag & 2 ) style.linespace = AmiPro::LS_OneAndHalf;
351
if( ls_flag & 4 ) style.linespace = AmiPro::LS_Double;
353
style.linespace = lines[15].stripWhiteSpace().toFloat() / 20.0;
354
style.spaceBefore = lines[17].stripWhiteSpace().toFloat() / 20.0;
355
style.spaceAfter = lines[18].stripWhiteSpace().toFloat() / 20.0;
357
m_styleList.append( style );
359
// "Style #0", "Style #1" and such are special styles
360
// do not import these styles
361
if( style.name.left( 7 ) != "Style #" )
363
return m_listener->doDefineStyle( style );
367
AmiProStyle AmiProParser::findStyle( const QString& name )
369
AmiProStyleList::iterator it;
370
for( it=m_styleList.begin(); it!=m_styleList.end(); ++it )
372
AmiProStyle& style = *it;
373
if( style.name == name )
376
return AmiProStyle();
379
bool AmiProParser::handleTag( const QString& tag )
381
// > (actually encoded as <;>)
383
m_text.append( ">" );
385
// [ (actually encoded as <[>)
387
m_text.append( "[" );
392
m_currentFormat.bold = true;
393
m_currentFormat.pos = m_text.length();
394
m_formatList.append( m_currentFormat );
400
m_currentFormat.bold = false;
401
m_currentFormat.pos = m_text.length();
402
m_formatList.append( m_currentFormat );
408
m_currentFormat.italic = true;
409
m_currentFormat.pos = m_text.length();
410
m_formatList.append( m_currentFormat );
416
m_currentFormat.italic = false;
417
m_currentFormat.pos = m_text.length();
418
m_formatList.append( m_currentFormat );
424
m_currentFormat.underline = true;
425
m_currentFormat.pos = m_text.length();
426
m_formatList.append( m_currentFormat );
432
m_currentFormat.underline = false;
433
m_currentFormat.pos = m_text.length();
434
m_formatList.append( m_currentFormat );
437
// double underline on
440
m_currentFormat.double_underline = true;
441
m_currentFormat.pos = m_text.length();
442
m_formatList.append( m_currentFormat );
445
// double underline off
448
m_currentFormat.double_underline = false;
449
m_currentFormat.pos = m_text.length();
450
m_formatList.append( m_currentFormat );
456
m_currentFormat.word_underline = true;
457
m_currentFormat.pos = m_text.length();
458
m_formatList.append( m_currentFormat );
461
// word underline off
464
m_currentFormat.word_underline = false;
465
m_currentFormat.pos = m_text.length();
466
m_formatList.append( m_currentFormat );
472
m_currentFormat.superscript = true;
473
m_currentFormat.pos = m_text.length();
474
m_formatList.append( m_currentFormat );
480
m_currentFormat.superscript = false;
481
m_currentFormat.pos = m_text.length();
482
m_formatList.append( m_currentFormat );
488
m_currentFormat.subscript = true;
489
m_currentFormat.pos = m_text.length();
490
m_formatList.append( m_currentFormat );
496
m_currentFormat.subscript = false;
497
m_currentFormat.pos = m_text.length();
498
m_formatList.append( m_currentFormat );
504
m_currentFormat.strikethrough = true;
505
m_currentFormat.pos = m_text.length();
506
m_formatList.append( m_currentFormat );
512
m_currentFormat.strikethrough = false;
513
m_currentFormat.pos = m_text.length();
514
m_formatList.append( m_currentFormat );
517
// paragraph left-align
519
m_layout.align = Qt::AlignLeft;
521
// paragraph right-align
523
m_layout.align = Qt::AlignRight;
527
m_layout.align = Qt::AlignCenter;
531
m_layout.align = Qt::AlignJustify;
534
if( tag.left( 3 ) == ":S+" )
536
float ls = tag.right( tag.length() - 3 ).toFloat();
537
m_layout.linespace = (ls == -1) ? AmiPro::LS_Single :
538
(ls == -2) ? AmiPro::LS_OneAndHalf :
539
(ls == -3) ? AmiPro::LS_Double : ls / 20.0;
543
if( tag.left( 2 ) == ":f" )
545
QString fontdesc = tag.right( tag.length()-2 );
546
QStringList desc = QStringList::split( ",", fontdesc );
547
if( desc.count() > 0 ) m_currentFormat.fontSize = desc[0].toFloat() / 20.0;
548
if( desc.count() > 1 )
550
QString fontFamily = desc[1];
551
if( fontFamily[0].isDigit() ) fontFamily.remove( 0, 1 );
552
m_currentFormat.fontFamily = fontFamily;
554
if( desc.count() > 4 )
556
unsigned red = desc[2].toUInt();
557
unsigned green = desc[3].toUInt();
558
unsigned blue = desc[4].toUInt();
559
m_currentFormat.fontColor.setRgb( red, green, blue );
561
m_formatList.append( m_currentFormat );
568
AmiProFormat::AmiProFormat()
571
bold = italic = underline =
572
word_underline = double_underline =
573
subscript = superscript = strikethrough = FALSE;
576
fontColor = Qt::black;
579
void AmiProFormat::assign( const AmiProFormat& f )
585
underline = f.underline;
586
word_underline = f.word_underline;
587
double_underline = f.double_underline;
588
subscript = f.subscript;
589
superscript = f.superscript;
590
strikethrough = f.strikethrough;
591
fontFamily = f.fontFamily;
592
fontSize = f.fontSize;
593
fontColor = f.fontColor;
596
AmiProFormat::AmiProFormat( const AmiProFormat& f )
601
AmiProFormat& AmiProFormat::operator=( const AmiProFormat& f )
607
void AmiProFormat::applyStyle( const AmiProStyle& style )
609
fontFamily = style.fontFamily;
610
fontSize = style.fontSize;
611
fontColor = style.fontColor;
613
italic = style.italic;
614
underline = style.underline;
615
word_underline = style.word_underline;
616
double_underline = style.double_underline;
617
subscript = style.subscript;
618
superscript = style.superscript;
619
strikethrough = style.strikethrough;
623
AmiProLayout::AmiProLayout()
628
fontColor = Qt::black;
629
bold = italic = underline =
630
word_underline = double_underline =
631
subscript = superscript = strikethrough = FALSE;
632
align = Qt::AlignLeft;
633
linespace = AmiPro::LS_Single;
634
spaceBefore = spaceAfter = 0;
637
void AmiProLayout::assign( const AmiProLayout &l )
640
fontFamily = l.fontFamily;
641
fontSize = l.fontSize;
642
fontColor = l.fontColor;
645
underline = l.underline;
646
word_underline = l.word_underline;
647
double_underline = l.double_underline;
648
subscript = l.subscript;
649
superscript = l.superscript;
650
strikethrough = l.strikethrough;
652
linespace = l.linespace;
653
spaceBefore = l.spaceBefore;
654
spaceAfter = l.spaceAfter;
657
AmiProLayout::AmiProLayout( const AmiProLayout& l )
662
AmiProLayout& AmiProLayout::operator=( const AmiProLayout& l )
668
void AmiProLayout::applyStyle( const AmiProStyle& style )
670
fontFamily = style.fontFamily;
671
fontSize = style.fontSize;
672
fontColor = style.fontColor;
674
italic = style.italic;
675
underline = style.underline;
676
word_underline = style.word_underline;
677
double_underline = style.double_underline;
678
subscript = style.subscript;
679
superscript = style.superscript;
680
strikethrough = style.strikethrough;
682
linespace = style.linespace;
683
spaceBefore = style.spaceBefore;
684
spaceAfter = style.spaceAfter;
688
AmiProStyle::AmiProStyle()
693
fontColor = Qt::black;
694
bold = italic = underline =
695
word_underline = double_underline =
696
subscript = superscript = strikethrough = FALSE;
697
linespace = AmiPro::LS_Single;
698
spaceBefore = spaceAfter = 0;
701
void AmiProStyle::assign( const AmiProStyle& s )
704
fontFamily = s.fontFamily;
705
fontSize = s.fontSize;
706
fontColor = s.fontColor;
709
underline = s.underline;
710
word_underline = s.word_underline;
711
double_underline = s.double_underline;
712
subscript = s.subscript;
713
superscript = s.superscript;
714
strikethrough = s.strikethrough;
716
linespace = s.linespace;
717
spaceBefore = s.spaceBefore;
718
spaceAfter = s.spaceAfter;
721
AmiProStyle::AmiProStyle( const AmiProStyle& s )
726
AmiProStyle& AmiProStyle::operator=( const AmiProStyle& s )
732
// base listener for the parser
733
AmiProListener::AmiProListener()
737
AmiProListener::~AmiProListener()
741
#define DO_TRUE_DEFINITION(string) \
742
bool AmiProListener::string \
747
DO_TRUE_DEFINITION(doOpenDocument())
748
DO_TRUE_DEFINITION(doCloseDocument())
749
DO_TRUE_DEFINITION(doDefineStyle(const AmiProStyle& style))
750
DO_TRUE_DEFINITION(doParagraph(const QString& text, AmiProFormatList formatList,