/***************************************************************************
dsrichtext . cpp - description
- - - - - - - - - - - - - - - - - - -
begin : Fre Okt 17 2003
copyright : ( C ) 2003 by Dominik Seichter
email : domseichter @ web . de
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/***************************************************************************
* *
* This program is free software ; you can redistribute it and / or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation ; either version 2 of the License , or *
* ( at your option ) any later version . *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
# include "dsrichtext.h"
# ifdef TQT_TEXT_BUG
// TQt includes
# include <tqfontmetrics.h>
# include <tqpainter.h>
# include <tqregexp.h>
// for DSREPLACE
# include "sqltables.h"
/*
EXAMPLE TEXT :
" <html><head><meta name= \" qrichtext \" content= \" 1 \" /></head><body style= \" font-size:10pt;font-family:Nimbus Sans l \" > "
" <p>A little bit <span style= \" font-style:italic;font-weight:600 \" >formated</span><span style= \" color:#4400ff \" > "
" richtext</span> which should be printed to a <span style= \" font-weight:600 \" >TQPrinter</span> with <span style= \" text-decoration:underline \" >TQSimpleRichText</span>. </p> "
" <p><span style= \" font-family:Times New Roman;font-size:16pt \" >Printing should work in ScreenResolution as well as in HighResolution, but as you can see it will only work in ScreenResolution.</span></p> "
" </body></html> " ;
Another example
< html > < head > < meta name = " qrichtext " content = " 1 " / > < / head > < body style = " font-size:10pt;font-family:Nimbus Sans l " >
< p align = " center " > < span style = " font-size:12pt;font-weight:600;text-decoration:underline " > Text test < / span > < span style = " font-size:12pt;font-weight:600;text-decoration:underline;color:#ff0000 " > label < / span > < / p >
< p > Resolution < span style = " font-weight:600 " > formated < / span > 108 dpi . This text & amp ; text should now overlap with the first line . 4 & lt ; 6 a < / p >
< p > < span style = " font-family:Nimbus Sans L;font-weight:600 " > A < / span > < span style = " font-family:Nimbus Sans L " > longer paragraph with some text , so that I can test wether wordwrap is working . Another & quot ; line & quot ; to test line spacing without a bigger font ( like the one found in the next sentence ) messing up the spacing . < / span > < span style = " font-family:Courier [Adobe] ; font - size : 16 pt " > Some text "in" a bigger courier font.</span><span style= " font - family : Nimbus Sans L " > Also this paragraph should be justified as blockquote.</span><span style= " font - family : Nimbus Sans L ; color : # ff0004 " > I hope it works</span><span style= " font - family : Nimbus Sans L " > and KBarcodes text redering will be better than the one found in TQt.</span></p>
< / body > < / html >
*/
DSRichText : : DSRichText ( const TQString & t )
: text ( t )
{
if ( text . find ( " <html> " ) = = - 1 )
text = " <html><head><meta name= \" qrichtext \" content= \" 1 \" /></head><body style= \" font-size:10pt;font-family:Nimbus Sans l \" ><p> "
+ t + " </p></body></html> " ;
//qDebug( text );
start = end = pos = 0 ;
x = y = 0 ;
sx = sy = 1.0 ;
m_base = TQFont ( ) ;
TQString tmp = parse ( text , " <body " , " > " , 0 ) ;
if ( ! tmp . isNull ( ) ) {
pos + = tmp . length ( ) ;
tmp = tmp . left ( tmp . length ( ) - 1 ) ;
tmp = tmp . mid ( 5 , tmp . length ( ) - 5 ) ;
m_base = parseStyle ( parse ( tmp , " style= \" " , " \" " , pos ) , & m_color ) ;
}
pos = text . find ( " <p " , 0 ) ; // initalize pos correctly
while ( parseParagraph ( ) ) ; // empty while loop
}
DSRichText : : ~ DSRichText ( )
{
}
void DSRichText : : initFormat ( formated_word * f , int alignment )
{
f - > text = TQString ( ) ;
f - > line = false ;
f - > alignment = alignment ;
f - > font = m_base ;
f - > color = m_color ;
}
void DSRichText : : initLine ( TQValueList < formated_line > * l )
{
formated_line li ;
li . width = 0 ;
li . ascent = 0 ;
li . leading = 0 ;
li . lineSpacing = 0 ;
li . line = false ;
l - > append ( li ) ;
}
void DSRichText : : updateSpacing ( TQValueList < formated_line > * l , TQFontMetrics * fm )
{
l - > last ( ) . lineSpacing = ( l - > last ( ) . lineSpacing < fm - > lineSpacing ( ) ) ? fm - > lineSpacing ( ) : l - > last ( ) . lineSpacing ;
l - > last ( ) . ascent = ( l - > last ( ) . ascent < fm - > ascent ( ) ) ? fm - > ascent ( ) : l - > last ( ) . ascent ;
l - > last ( ) . leading = ( l - > last ( ) . leading < fm - > leading ( ) ) ? fm - > leading ( ) : l - > last ( ) . leading ;
}
void DSRichText : : fillLines ( )
{
for ( unsigned int z = 0 ; z < word_p . count ( ) ; z + + ) {
WordList words = word_p [ z ] ;
LineList lines ;
initLine ( & lines ) ;
for ( unsigned int i = 0 ; i < words . count ( ) ; i + + ) {
formated_word word = words [ i ] ;
lines . last ( ) . line = lines . last ( ) . line | word . line ;
TQFontMetrics fm ( word . font ) ;
updateSpacing ( & lines , & fm ) ;
int tw = fm . width ( word . text ) ;
// word does not fit in the current line, create a new one
if ( lines . last ( ) . width + tw > = w )
initLine ( & lines ) ;
// TODO: check here for words longer than one line
lines . last ( ) . formats . append ( word ) ;
lines . last ( ) . width + = tw ;
}
line_p . append ( lines ) ;
}
}
void DSRichText : : draw ( TQPainter * p )
{
/* don't try to draw if there is no space to draw */
if ( ! w | | ! h )
return ;
fillLines ( ) ;
painter = p ;
painter - > save ( ) ;
painter - > setClipRect ( xpos , ypos , int ( w * sx ) , int ( h * sy ) , TQPainter : : CoordPainter ) ;
for ( unsigned int z = 0 ; z < line_p . count ( ) ; z + + ) {
LineList lines = line_p [ z ] ;
if ( lines . count ( ) & & z )
y + = int ( lines [ 0 ] . lineSpacing * 0.5 ) ;
for ( unsigned int i = 0 ; i < lines . count ( ) ; i + + ) {
formated_line l = lines [ i ] ;
if ( l . formats . count ( ) & & l . formats [ 0 ] . alignment = = TQt : : AlignJustify & & i ! = lines . count ( ) - 1 ) {
// last line in a paragraph is not justified (in blocksatz mode)!
drawJustified ( & l ) ;
} else {
for ( unsigned int z = 0 ; z < l . formats . count ( ) ; z + + ) {
formated_word f = l . formats [ z ] ;
painter - > setFont ( f . font ) ;
painter - > setPen ( TQPen ( f . color ) ) ;
int a = f . alignment ;
if ( a = = TQt : : AlignRight ) {
a = TQt : : AlignLeft ;
if ( ! x )
x = w - l . width ;
} else if ( a = = TQt : : AlignHCenter ) {
a = TQt : : AlignLeft ;
if ( ! x )
x = ( w - l . width ) / 2 ;
}
int ya = yDeviation ( & l ) ;
painter - > drawText ( xpos + int ( x * sx ) , ypos + int ( ( y + ya ) * sy ) , int ( l . width * sx ) , int ( l . lineSpacing * sy ) , a , f . text ) ;
x + = painter - > fontMetrics ( ) . width ( f . text ) ;
}
}
x = 0 ;
y + = l . lineSpacing ;
}
}
painter - > restore ( ) ;
}
void DSRichText : : drawJustified ( formated_line * line )
{
int all = 0 ;
for ( unsigned int z = 0 ; z < line - > formats . count ( ) ; z + + ) {
line - > formats [ z ] . text = line - > formats [ z ] . text . stripWhiteSpace ( ) ;
TQFontMetrics fm ( line - > formats [ z ] . font ) ;
all + = fm . width ( line - > formats [ z ] . text ) ;
}
int x = 0 ;
int space = ( w - all ) / ( line - > formats . count ( ) - 1 ) ;
for ( unsigned int z = 0 ; z < line - > formats . count ( ) ; z + + ) {
painter - > setFont ( line - > formats [ z ] . font ) ;
painter - > setPen ( TQPen ( line - > formats [ z ] . color ) ) ;
int ya = yDeviation ( line ) ;
int tw = painter - > fontMetrics ( ) . width ( line - > formats [ z ] . text ) ;
painter - > drawText ( int ( x * sx ) , int ( ( y + ya ) * sy ) , int ( tw * sx ) , int ( line - > lineSpacing * sy ) , TQt : : AlignAuto , line - > formats [ z ] . text ) ;
x + = tw ;
x + = space ;
}
}
bool DSRichText : : parseParagraph ( )
{
int alignment = 0 ;
formated_word f ;
TQString d = parse ( text , " <p " , " </p> " , pos ) ;
//qDebug("D=" + d );
//qDebug("POS=%i", pos );
pos + = d . length ( ) ;
if ( d . isNull ( ) )
return false ;
d = parseParagraphTag ( d , & alignment ) ;
WordList words ;
TQString data ;
initFormat ( & f , alignment ) ;
if ( d . isEmpty ( ) ) {
// found empty paragraph
words . append ( f ) ;
word_p . append ( words ) ;
return true ;
}
for ( unsigned int i = 0 ; i < d . length ( ) ; ) {
if ( d [ i ] = = ' < ' | | i = = ( d . length ( ) - 1 ) ) {
if ( i = = ( d . length ( ) - 1 ) )
data . append ( d [ i ] ) ;
parseWords ( data , & f , & words ) ;
initFormat ( & f , alignment ) ;
data = TQString ( ) ;
// bail out now, otherwise there is
// and endless loop for e.g. <p>a</p>
if ( i = = ( d . length ( ) - 1 ) )
break ;
if ( d [ i ] = = ' < ' ) {
TQString span = d . mid ( i , d . find ( " > " , i ) - i + 1 ) ;
i + = span . length ( ) ;
if ( span . startsWith ( " <span " ) ) {
initFormat ( & f , alignment ) ;
f . font = parseStyle ( parse ( span , " style= \" " , " \" " , 0 ) , & f . color ) ;
}
}
} else {
data . append ( d [ i ] ) ;
i + + ;
}
}
word_p . append ( words ) ;
return true ;
}
TQFont DSRichText : : parseStyle ( const TQString & s , TQColor * color )
{
TQString style = TQString ( s ) ;
style = style . left ( style . length ( ) - 1 ) ;
if ( style . startsWith ( " style= \" " ) )
style = style . mid ( 7 , style . length ( ) - 7 ) ;
TQFont f = m_base ;
* color = m_color ;
for ( int i = 0 ; i < style . contains ( ' ; ' ) + 1 ; i + + ) {
TQString s = style . section ( ' ; ' , i , i ) ;
if ( s . isEmpty ( ) )
continue ;
if ( s . startsWith ( " font-size: " ) ) {
f . setPointSize ( s . mid ( 10 , s . length ( ) - 12 ) . toInt ( ) ) ;
} else if ( s . startsWith ( " font-family: " ) ) {
f . setFamily ( s . right ( s . length ( ) - 12 ) ) ;
} else if ( s . startsWith ( " color: " ) ) {
color - > setNamedColor ( s . right ( s . length ( ) - 6 ) ) ;
} else if ( s . startsWith ( " text-decoration: " ) ) {
if ( s . endsWith ( " underline " ) )
f . setUnderline ( true ) ;
/*#if [[[TQT_VERSION IS DEPRECATED]]] >= 0x030200
else if ( s . endsWith ( " overline " ) )
f . setOverline ( true ) ;
# endif * /
else if ( s . endsWith ( " line-through " ) )
f . setStrikeOut ( true ) ;
} else if ( s . startsWith ( " font-style: " ) ) {
if ( s . endsWith ( " italic " ) | | s . endsWith ( " oblique " ) )
f . setItalic ( true ) ;
} else if ( s . startsWith ( " font-weight: " ) ) {
bool ok = false ;
int n = s . right ( s . length ( ) - 12 ) . toInt ( & ok ) ;
if ( s . endsWith ( " bold " ) )
f . setBold ( true ) ;
else if ( ok )
f . setWeight ( n / 8 ) ; // convert CSS values to TQt values
}
}
return f ;
}
TQString DSRichText : : parse ( const TQString & t , const TQString & find , const TQString & find2 , int start )
{
int s = t . find ( find , start ) ;
if ( s = = - 1 | | s < start )
return TQString ( ) ;
int pend = t . find ( find2 , s + find . length ( ) ) ;
if ( pend = = - 1 | | pend < ( signed int ) ( s + find . length ( ) ) )
return TQString ( ) ;
TQString text = t . mid ( s , pend - s + find2 . length ( ) ) ;
return text ;
}
void DSRichText : : setX ( int x )
{
xpos = x ;
}
void DSRichText : : setY ( int y )
{
ypos = y ;
}
void DSRichText : : setWidth ( int width )
{
w = width ;
}
void DSRichText : : setHeight ( int height )
{
h = height ;
}
int DSRichText : : parseAlignment ( const TQString & align )
{
TQString a = TQString ( align ) ;
if ( a . endsWith ( " \" " ) )
a = a . left ( a . length ( ) - 1 ) ;
if ( a . startsWith ( " align= \" " ) )
a = a . mid ( 7 , a . length ( ) - 7 ) ;
if ( a = = " left " )
return TQt : : AlignLeft ;
else if ( a = = " center " )
return TQt : : AlignHCenter ;
else if ( a = = " right " )
return TQt : : AlignRight ;
else if ( a = = " justify " )
return TQt : : AlignJustify ;
return 0 ;
}
void DSRichText : : parseWords ( const TQString & t , formated_word * w , WordList * words )
{
unsigned int p = 0 ;
for ( unsigned int i = 0 ; i < t . length ( ) ; i + + ) {
if ( ( t [ i ] . isSpace ( ) & & p ! = i ) | | i = = t . length ( ) - 1 ) {
formated_word word = * w ;
word . text = replaceEscapeSequences ( t . mid ( p , i + 1 - p ) ) ;
p = i + 1 ;
words - > append ( word ) ;
}
}
}
inline int DSRichText : : yDeviation ( const formated_line * line )
{
// leading = 1 (almost ever)
// linespacing = leading + height
// height = ascent + descent + 1
if ( line - > lineSpacing ! = painter - > fontMetrics ( ) . lineSpacing ( ) ) {
return line - > ascent + line - > leading - painter - > fontMetrics ( ) . ascent ( ) - painter - > fontMetrics ( ) . leading ( ) ;
} else {
return 0 ;
}
}
TQString DSRichText : : replaceEscapeSequences ( const TQString & t )
{
TQString tmp = TQString ( t ) ;
tmp = tmp . replace ( DSREPLACE ( " < " ) , " < " ) ;
tmp = tmp . replace ( DSREPLACE ( " > " ) , " > " ) ;
tmp = tmp . replace ( DSREPLACE ( " & " ) , " & " ) ;
tmp = tmp . replace ( DSREPLACE ( " " " ) , " \" " ) ;
return tmp ;
}
TQString DSRichText : : parseParagraphTag ( const TQString & t , int * alignment )
{
TQString d = TQString ( t ) ;
if ( d . startsWith ( " <p> " ) ) {
d = d . mid ( 3 , d . length ( ) - 3 ) ;
} else if ( d . startsWith ( " <p " ) ) {
* alignment = parseAlignment ( parse ( d , " align= \" " , " \" " , 0 ) ) ;
if ( d . contains ( " > " ) ) {
int x = d . find ( " > " ) + 1 ;
d = d . mid ( x , d . length ( ) - x ) ;
}
}
if ( d . endsWith ( " </p> " ) )
d = d . left ( d . length ( ) - 4 ) ; // strlen("</p>");
return d ;
}
# endif // TQT_TEXT_BUG