2006-09-11 10:44:17 +00:00
/*
Copyright ( C ) 2006 Brad Hards < bradh @ frogmouth . net >
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
This program 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 General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program ; if not , write to the Free Software
2006-10-21 22:10:09 +00:00
Foundation , Inc . , 51 Franklin Street , Fifth Floor , Boston , MA
2006-09-11 10:44:17 +00:00
02110 - 1301 , USA .
*/
# include <qdatetime.h>
# include <qfile.h>
2006-09-21 11:41:46 +00:00
# include <qfontdatabase.h>
2006-09-11 10:44:17 +00:00
# include <qimage.h>
# include <qlist.h>
2006-09-21 11:41:46 +00:00
# include <qpainter.h>
2006-09-11 10:44:17 +00:00
# include <qpixmap.h>
# include <qthread.h>
# include <kglobal.h>
# include <kimageeffect.h>
# include <klocale.h>
2007-02-06 07:07:23 +00:00
# include <QFileInfo>
2006-09-11 10:44:17 +00:00
2007-01-03 14:30:48 +00:00
# include <okular/core/document.h>
# include <okular/core/page.h>
2007-02-13 11:13:35 +00:00
# include <okular/core/area.h>
2006-09-11 10:44:17 +00:00
# include "generator_xps.h"
2007-03-08 03:11:04 +00:00
const int XpsDebug = 4658 ;
2006-09-19 09:09:30 +00:00
OKULAR_EXPORT_PLUGIN ( XpsGenerator )
2006-09-11 10:44:17 +00:00
2006-09-21 11:41:46 +00:00
// From Qt4
static int hex2int ( char hex )
{
QChar hexchar = QLatin1Char ( hex ) ;
int v ;
if ( hexchar . isDigit ( ) )
v = hexchar . digitValue ( ) ;
else if ( hexchar > = QLatin1Char ( ' A ' ) & & hexchar < = QLatin1Char ( ' F ' ) )
v = hexchar . cell ( ) - ' A ' + 10 ;
else if ( hexchar > = QLatin1Char ( ' a ' ) & & hexchar < = QLatin1Char ( ' f ' ) )
v = hexchar . cell ( ) - ' a ' + 10 ;
else
v = - 1 ;
return v ;
}
// Modified from Qt4
static QColor hexToRgba ( const char * name )
{
if ( name [ 0 ] ! = ' # ' )
return QColor ( ) ;
name + + ; // eat the leading '#'
int len = qstrlen ( name ) ;
int r , g , b ;
int a = 255 ;
if ( len = = 6 ) {
r = ( hex2int ( name [ 0 ] ) < < 4 ) + hex2int ( name [ 1 ] ) ;
g = ( hex2int ( name [ 2 ] ) < < 4 ) + hex2int ( name [ 3 ] ) ;
b = ( hex2int ( name [ 4 ] ) < < 4 ) + hex2int ( name [ 5 ] ) ;
} else if ( len = = 8 ) {
a = ( hex2int ( name [ 0 ] ) < < 4 ) + hex2int ( name [ 1 ] ) ;
r = ( hex2int ( name [ 2 ] ) < < 4 ) + hex2int ( name [ 3 ] ) ;
g = ( hex2int ( name [ 4 ] ) < < 4 ) + hex2int ( name [ 5 ] ) ;
b = ( hex2int ( name [ 6 ] ) < < 4 ) + hex2int ( name [ 7 ] ) ;
} else {
r = g = b = - 1 ;
}
if ( ( uint ) r > 255 | | ( uint ) g > 255 | | ( uint ) b > 255 ) {
return QColor ( ) ;
}
return QColor ( r , g , b , a ) ;
}
static QRectF stringToRectF ( const QString & data )
{
QStringList numbers = data . split ( ' , ' ) ;
QPointF origin ( numbers . at ( 0 ) . toDouble ( ) , numbers . at ( 1 ) . toDouble ( ) ) ;
QSizeF size ( numbers . at ( 2 ) . toDouble ( ) , numbers . at ( 3 ) . toDouble ( ) ) ;
return QRectF ( origin , size ) ;
}
2007-02-06 07:07:23 +00:00
static bool parseGUID ( const QString guidString , unsigned short guid [ 16 ] ) {
if ( guidString . length ( ) < = 35 ) {
return false ;
}
// Maps bytes to positions in guidString
const static int indexes [ ] = { 6 , 4 , 2 , 0 , 11 , 9 , 16 , 14 , 19 , 21 , 24 , 26 , 28 , 30 , 32 , 34 } ;
for ( int i = 0 ; i < 16 ; i + + ) {
int hex1 = hex2int ( guidString [ indexes [ i ] ] . cell ( ) ) ;
2007-02-08 16:41:53 +00:00
int hex2 = hex2int ( guidString [ indexes [ i ] + 1 ] . cell ( ) ) ;
2007-02-06 07:07:23 +00:00
2007-02-08 16:41:53 +00:00
if ( ( hex1 < 0 ) | | ( hex2 < 0 ) )
{
return false ;
}
2007-02-06 07:07:23 +00:00
guid [ i ] = hex1 * 16 + hex2 ;
}
return true ;
}
2007-02-08 15:07:54 +00:00
// Read next token of abbreviated path data
static bool nextAbbPathToken ( AbbPathToken * token )
{
int * curPos = & token - > curPos ;
QString data = token - > data ;
2007-02-17 09:42:15 +00:00
while ( ( * curPos < data . length ( ) ) & & ( data . at ( * curPos ) . isSpace ( ) ) )
{
2007-02-08 15:07:54 +00:00
( * curPos ) + + ;
}
if ( * curPos = = data . length ( ) )
{
token - > type = abtEOF ;
return true ;
}
QChar ch = data . at ( * curPos ) ;
if ( ch . isNumber ( ) | | ( ch = = ' + ' ) | | ( ch = = ' - ' ) )
{
int start = * curPos ;
while ( ( * curPos < data . length ( ) ) & & ( ! data . at ( * curPos ) . isSpace ( ) ) & & ( data . at ( * curPos ) ! = ' , ' ) & & ! data . at ( * curPos ) . isLetter ( ) )
2007-02-08 16:41:53 +00:00
{
( * curPos ) + + ;
}
token - > number = data . mid ( start , * curPos - start ) . toDouble ( ) ;
token - > type = abtNumber ;
2007-02-08 15:07:54 +00:00
} else if ( ch = = ' , ' )
{
token - > type = abtComma ;
2007-02-08 16:41:53 +00:00
( * curPos ) + + ;
2007-02-17 09:42:15 +00:00
} else if ( ch . isLetter ( ) )
2007-02-08 15:07:54 +00:00
{
token - > type = abtCommand ;
2007-02-08 16:41:53 +00:00
token - > command = data . at ( * curPos ) . cell ( ) ;
( * curPos ) + + ;
2007-02-17 09:42:15 +00:00
} else
2007-02-08 15:07:54 +00:00
{
return false ;
}
return true ;
}
2007-02-10 17:31:25 +00:00
/**
Read point ( two reals delimited by comma ) from abbreviated path data
*/
static QPointF getPointFromString ( AbbPathToken * token , bool relative , const QPointF currentPosition ) {
2007-02-08 15:07:54 +00:00
//TODO Check grammar
2007-02-17 09:42:15 +00:00
2007-02-08 15:07:54 +00:00
QPointF result ;
result . rx ( ) = token - > number ;
nextAbbPathToken ( token ) ;
nextAbbPathToken ( token ) ; // ,
result . ry ( ) = token - > number ;
nextAbbPathToken ( token ) ;
if ( relative )
{
2007-02-10 17:31:25 +00:00
result + = currentPosition ;
2007-02-17 09:42:15 +00:00
}
2007-02-08 15:07:54 +00:00
return result ;
}
2007-02-06 07:07:23 +00:00
2007-02-10 17:31:25 +00:00
/**
Parse an abbreviated path " Data " description
\ param data the string containing the whitespace separated values
2007-02-17 09:42:15 +00:00
2007-02-10 17:31:25 +00:00
\ see XPS specification 4.2 .3 and Appendix G
*/
static QPainterPath parseAbbreviatedPathData ( const QString & data )
2006-09-21 11:41:46 +00:00
{
2007-02-10 17:31:25 +00:00
QPainterPath path = QPainterPath ( ) ;
2007-02-08 15:07:54 +00:00
2007-02-10 17:31:25 +00:00
AbbPathToken token ;
2007-02-08 15:07:54 +00:00
token . data = data ;
token . curPos = 0 ;
nextAbbPathToken ( & token ) ;
2007-02-17 09:42:15 +00:00
2007-02-08 16:41:53 +00:00
// Used by Smooth cubic curve (command s)
2007-02-17 09:42:15 +00:00
char lastCommand = ' ' ;
2007-02-08 16:41:53 +00:00
QPointF lastSecondControlPoint ;
2007-02-08 15:07:54 +00:00
while ( true )
{
if ( token . type ! = abtCommand )
{
if ( token . type ! = abtEOF )
{
2007-03-08 03:11:04 +00:00
kDebug ( XpsDebug ) < < " Error in parsing abbreviated path data " < < endl ;
2006-09-21 11:41:46 +00:00
}
2007-02-10 17:31:25 +00:00
return path ;
2007-02-08 15:07:54 +00:00
}
char command = QChar ( token . command ) . toLower ( ) . cell ( ) ;
bool isRelative = QChar ( token . command ) . isLower ( ) ;
2007-02-10 17:31:25 +00:00
QPointF currPos = path . currentPosition ( ) ;
2007-02-08 15:07:54 +00:00
nextAbbPathToken ( & token ) ;
switch ( command ) {
case ' f ' :
int rule ;
rule = ( int ) token . number ;
if ( rule = = 0 )
{
2007-02-10 17:31:25 +00:00
path . setFillRule ( Qt : : OddEvenFill ) ;
2007-02-08 15:07:54 +00:00
}
else if ( rule = = 1 )
{
// In xps specs rule 1 means NonZero fill. I think it's equivalent to WindingFill but I'm not sure
2007-02-10 17:31:25 +00:00
path . setFillRule ( Qt : : WindingFill ) ;
2007-02-08 15:07:54 +00:00
}
2007-02-17 09:42:15 +00:00
nextAbbPathToken ( & token ) ;
2007-02-08 15:07:54 +00:00
break ;
case ' m ' : // Move
while ( token . type = = abtNumber )
{
2007-02-10 17:31:25 +00:00
QPointF point = getPointFromString ( & token , isRelative , currPos ) ;
path . moveTo ( point ) ;
2007-02-08 15:07:54 +00:00
}
break ;
case ' l ' : // Line
while ( token . type = = abtNumber )
{
2007-02-10 17:31:25 +00:00
QPointF point = getPointFromString ( & token , isRelative , currPos ) ;
path . lineTo ( point ) ;
2007-02-08 15:07:54 +00:00
}
break ;
case ' h ' : // Horizontal line
while ( token . type = = abtNumber )
{
2007-02-10 17:31:25 +00:00
double x = token . number + isRelative ? path . currentPosition ( ) . x ( ) : 0 ;
path . lineTo ( x , path . currentPosition ( ) . y ( ) ) ;
2007-02-08 15:07:54 +00:00
nextAbbPathToken ( & token ) ;
}
break ;
case ' v ' : // Vertical line
while ( token . type = = abtNumber )
{
2007-02-10 17:31:25 +00:00
double y = token . number + isRelative ? path . currentPosition ( ) . y ( ) : 0 ;
path . lineTo ( path . currentPosition ( ) . x ( ) , y ) ;
2007-02-08 15:07:54 +00:00
nextAbbPathToken ( & token ) ;
}
break ;
case ' c ' : // Cubic bezier curve
while ( token . type = = abtNumber )
{
2007-02-10 17:31:25 +00:00
QPointF firstControl = getPointFromString ( & token , isRelative , currPos ) ;
QPointF secondControl = getPointFromString ( & token , isRelative , currPos ) ;
QPointF endPoint = getPointFromString ( & token , isRelative , currPos ) ;
path . cubicTo ( firstControl , secondControl , endPoint ) ;
2007-02-08 15:07:54 +00:00
2007-02-08 16:41:53 +00:00
lastSecondControlPoint = secondControl ;
2007-02-08 15:07:54 +00:00
}
break ;
case ' q ' : // Quadratic bezier curve
while ( token . type = = abtNumber )
{
2007-02-10 17:31:25 +00:00
QPointF point1 = getPointFromString ( & token , isRelative , currPos ) ;
QPointF point2 = getPointFromString ( & token , isRelative , currPos ) ;
path . quadTo ( point2 , point2 ) ;
2007-02-08 15:07:54 +00:00
}
break ;
case ' s ' : // Smooth cubic bezier curve
while ( token . type = = abtNumber )
{
2007-02-08 16:41:53 +00:00
QPointF firstControl ;
if ( ( lastCommand = = ' s ' ) | | ( lastCommand = = ' c ' ) )
{
2007-02-10 17:31:25 +00:00
firstControl = lastSecondControlPoint + ( lastSecondControlPoint + path . currentPosition ( ) ) ;
2007-02-17 09:42:15 +00:00
}
2007-02-08 16:41:53 +00:00
else
{
2007-02-10 17:31:25 +00:00
firstControl = path . currentPosition ( ) ;
2007-02-08 16:41:53 +00:00
}
2007-02-10 17:31:25 +00:00
QPointF secondControl = getPointFromString ( & token , isRelative , currPos ) ;
QPointF endPoint = getPointFromString ( & token , isRelative , currPos ) ;
path . cubicTo ( firstControl , secondControl , endPoint ) ;
2007-02-08 15:07:54 +00:00
}
break ;
case ' a ' : // Arc
//TODO Implement Arc drawing
while ( token . type = = abtNumber )
{
2007-02-10 17:31:25 +00:00
/*QPointF rp =*/ getPointFromString ( & token , isRelative , currPos ) ;
2007-02-08 15:07:54 +00:00
/*double r = token.number;*/
nextAbbPathToken ( & token ) ;
2007-02-08 16:20:55 +00:00
/*double fArc = token.number; */
2007-02-08 15:07:54 +00:00
nextAbbPathToken ( & token ) ;
/*double fSweep = token.number; */
nextAbbPathToken ( & token ) ;
2007-02-10 17:31:25 +00:00
/*QPointF point = */ getPointFromString ( & token , isRelative , currPos ) ;
2007-02-08 15:07:54 +00:00
}
break ;
case ' z ' : // Close path
2007-02-10 17:31:25 +00:00
path . closeSubpath ( ) ;
2007-02-08 15:07:54 +00:00
break ;
2006-09-21 11:41:46 +00:00
}
2007-02-08 15:07:54 +00:00
2007-02-08 16:41:53 +00:00
lastCommand = command ;
2006-09-21 11:41:46 +00:00
}
2007-02-10 17:31:25 +00:00
return path ;
2006-09-26 11:53:27 +00:00
}
2007-02-08 15:07:54 +00:00
2006-09-26 11:53:27 +00:00
QMatrix XpsHandler : : attsToMatrix ( const QString & csv )
{
QStringList values = csv . split ( ' , ' ) ;
if ( values . count ( ) ! = 6 ) {
return QMatrix ( ) ; // that is an identity matrix - no effect
2007-02-06 07:07:23 +00:00
}
2006-09-26 11:53:27 +00:00
return QMatrix ( values . at ( 0 ) . toDouble ( ) , values . at ( 1 ) . toDouble ( ) ,
values . at ( 2 ) . toDouble ( ) , values . at ( 3 ) . toDouble ( ) ,
2007-02-17 09:42:15 +00:00
values . at ( 4 ) . toDouble ( ) , values . at ( 5 ) . toDouble ( ) ) ;
2006-09-21 11:41:46 +00:00
}
2006-09-11 10:44:17 +00:00
2007-02-17 11:18:06 +00:00
QBrush XpsHandler : : parseRscRefColorForBrush ( const QString & data )
2007-02-10 17:31:25 +00:00
{
if ( data [ 0 ] = = ' { ' ) {
//TODO
2007-03-08 03:11:04 +00:00
kDebug ( XpsDebug ) < < " Reference " < < data < < endl ;
2007-02-17 11:18:06 +00:00
return QBrush ( ) ;
2007-02-10 17:31:25 +00:00
} else {
2007-02-17 11:18:06 +00:00
return QBrush ( hexToRgba ( data . toLatin1 ( ) ) ) ;
}
}
QPen XpsHandler : : parseRscRefColorForPen ( const QString & data )
{
if ( data [ 0 ] = = ' { ' ) {
//TODO
2007-03-08 03:11:04 +00:00
kDebug ( XpsDebug ) < < " Reference " < < data < < endl ;
2007-02-17 11:18:06 +00:00
return QPen ( ) ;
} else {
return QPen ( hexToRgba ( data . toLatin1 ( ) ) ) ;
2007-02-10 17:31:25 +00:00
}
}
QMatrix XpsHandler : : parseRscRefMatrix ( const QString & data )
{
if ( data [ 0 ] = = ' { ' ) {
//TODO
2007-03-08 03:11:04 +00:00
kDebug ( XpsDebug ) < < " Reference " < < data < < endl ;
2007-02-10 17:31:25 +00:00
return QMatrix ( ) ;
} else {
return attsToMatrix ( data ) ;
}
}
2006-09-26 11:53:27 +00:00
XpsHandler : : XpsHandler ( XpsPage * page ) : m_page ( page )
2006-09-21 11:41:46 +00:00
{
2007-02-13 11:13:35 +00:00
m_painter = NULL ;
2006-09-21 11:41:46 +00:00
}
XpsHandler : : ~ XpsHandler ( )
2007-02-09 08:11:47 +00:00
{
delete m_painter ;
}
2006-09-21 11:41:46 +00:00
bool XpsHandler : : startDocument ( )
{
2007-03-08 03:11:04 +00:00
kDebug ( XpsDebug ) < < " start document " < < m_page - > m_fileName < < endl ;
2007-02-06 07:07:23 +00:00
m_page - > m_pageImage - > fill ( QColor ( " White " ) . rgba ( ) ) ;
2007-02-10 17:31:25 +00:00
XpsRenderNode node ;
node . name = " document " ;
m_nodes . push ( node ) ;
2006-09-21 11:41:46 +00:00
return true ;
}
bool XpsHandler : : startElement ( const QString & nameSpace ,
const QString & localName ,
const QString & qname ,
const QXmlAttributes & atts )
{
2007-02-10 17:31:25 +00:00
Q_UNUSED ( nameSpace )
Q_UNUSED ( qname )
XpsRenderNode node ;
node . name = localName ;
node . attributes = atts ;
2007-02-12 13:31:16 +00:00
node . data = NULL ;
2007-02-10 17:31:25 +00:00
processStartElement ( node ) ;
m_nodes . push ( node ) ;
2006-09-21 11:41:46 +00:00
return true ;
}
bool XpsHandler : : endElement ( const QString & nameSpace ,
const QString & localName ,
const QString & qname )
{
2007-02-10 17:31:25 +00:00
Q_UNUSED ( nameSpace )
Q_UNUSED ( qname )
XpsRenderNode node = m_nodes . pop ( ) ;
if ( node . name ! = localName ) {
2007-03-08 03:11:04 +00:00
kDebug ( XpsDebug ) < < " Name doesn't match " < < endl ;
2007-02-10 17:31:25 +00:00
}
processEndElement ( node ) ;
node . children . clear ( ) ;
m_nodes . top ( ) . children . append ( node ) ;
return true ;
}
2006-09-21 11:41:46 +00:00
2007-02-10 17:31:25 +00:00
void XpsHandler : : processGlyph ( XpsRenderNode & node )
{
2007-02-17 09:42:15 +00:00
//TODO Currently ignored attributes: BidiLevel, CaretStops, DeviceFontName, IsSideways, Indices, StyleSimulation, Clip, OpacityMask, Name, FixedPage.NavigateURI, xml:lang, x:key
2007-02-10 17:31:25 +00:00
//TODO Currently ignored child elements: Clip, OpacityMask
//Handled separately: RenderTransform
2007-02-17 09:42:15 +00:00
2007-02-10 17:31:25 +00:00
QString att ;
m_painter - > save ( ) ;
// Get font (doesn't work well because qt doesn't allow to load font from file)
2007-02-10 17:41:11 +00:00
// This works despite the fact that font size isn't specified in points as required by qt. It's because I set point size to be equal to drawing unit.
2007-03-08 03:11:04 +00:00
kDebug ( XpsDebug ) < < " Font Rendering EmSize: " < < node . attributes . value ( " FontRenderingEmSize " ) . toFloat ( ) < < endl ;
2007-02-13 13:26:37 +00:00
QFont font = m_page - > m_file - > getFontByName ( node . attributes . value ( " FontUri " ) , node . attributes . value ( " FontRenderingEmSize " ) . toFloat ( ) ) ;
2007-02-10 17:31:25 +00:00
m_painter - > setFont ( font ) ;
//Origin
QPointF origin ( node . attributes . value ( " OriginX " ) . toDouble ( ) , node . attributes . value ( " OriginY " ) . toDouble ( ) ) ;
//Fill
QBrush brush ;
att = node . attributes . value ( " Fill " ) ;
if ( att . isEmpty ( ) ) {
2007-02-13 21:13:08 +00:00
XpsFill * data = ( XpsFill * ) node . getChildData ( " Glyphs.Fill " ) ;
2007-02-12 13:31:16 +00:00
if ( data ! = NULL ) {
2007-02-13 21:13:08 +00:00
brush = * data ;
delete data ;
2007-02-10 17:31:25 +00:00
} else {
brush = QBrush ( ) ;
2006-09-21 11:41:46 +00:00
}
2007-02-10 17:31:25 +00:00
} else {
2007-02-17 11:18:06 +00:00
brush = parseRscRefColorForBrush ( att ) ;
2007-02-10 17:31:25 +00:00
}
m_painter - > setBrush ( brush ) ;
m_painter - > setPen ( QPen ( brush , 0 ) ) ;
2006-09-21 11:41:46 +00:00
2007-02-17 09:42:15 +00:00
// Opacity
att = node . attributes . value ( " Opacity " ) ;
if ( ! att . isEmpty ( ) ) {
m_painter - > setOpacity ( att . toDouble ( ) ) ;
}
2007-02-10 17:31:25 +00:00
//RenderTransform
att = node . attributes . value ( " RenderTransform " ) ;
if ( ! att . isEmpty ( ) ) {
m_painter - > setWorldMatrix ( parseRscRefMatrix ( att ) , true ) ;
}
m_painter - > drawText ( origin , node . attributes . value ( " UnicodeString " ) ) ;
2007-03-08 03:11:04 +00:00
// kDebug(XpsDebug) << "Glyphs: " << atts.value("Fill") << ", " << atts.value("FontUri") << endl;
// kDebug(XpsDebug) << " Origin: " << atts.value("OriginX") << "," << atts.value("OriginY") << endl;
// kDebug(XpsDebug) << " Unicode: " << atts.value("UnicodeString") << endl;
2007-02-17 09:42:15 +00:00
2007-02-10 17:31:25 +00:00
m_painter - > restore ( ) ;
}
void XpsHandler : : processFill ( XpsRenderNode & node )
{
2007-02-12 13:31:16 +00:00
//TODO Ignored child elements: LinearGradientBrush, RadialGradientBrush, VirtualBrush
2007-02-17 09:42:15 +00:00
2007-02-13 21:13:08 +00:00
if ( node . children . size ( ) ! = 1 ) {
2007-03-08 03:11:04 +00:00
kDebug ( XpsDebug ) < < " Fill element should have exactly one child " < < endl ;
2007-02-13 21:13:08 +00:00
} else {
node . data = node . children [ 0 ] . data ;
2007-02-10 17:31:25 +00:00
}
}
void XpsHandler : : processImageBrush ( XpsRenderNode & node )
{
2007-02-12 13:31:16 +00:00
//TODO Ignored attributes: Opacity, x:key, TileMode, ViewBoxUnits, ViewPortUnits
//TODO Check whether transformation works for non standard situations (viewbox different that whole image, Transform different that simple move & scale, Viewport different than [0, 0, 1, 1]
2007-02-17 09:42:15 +00:00
2007-02-12 13:31:16 +00:00
QString att ;
QBrush brush ;
2007-02-17 09:42:15 +00:00
2007-02-12 13:31:16 +00:00
QRectF viewport = stringToRectF ( node . attributes . value ( " Viewport " ) ) ;
QRectF viewbox = stringToRectF ( node . attributes . value ( " Viewbox " ) ) ;
QImage image = m_page - > loadImageFromFile ( node . attributes . value ( " ImageSource " ) ) ;
2007-02-17 09:42:15 +00:00
2007-02-12 13:31:16 +00:00
// Matrix which can transform [0, 0, 1, 1] rectangle to given viewbox
QMatrix viewboxMatrix = QMatrix ( viewbox . width ( ) * image . physicalDpiX ( ) / 96 , 0 , 0 , viewbox . height ( ) * image . physicalDpiY ( ) / 96 , viewbox . x ( ) , viewbox . y ( ) ) ;
// Matrix which can transform [0, 0, 1, 1] rectangle to given viewport
//TODO Take ViewPort into account
QMatrix viewportMatrix ;
att = node . attributes . value ( " Transform " ) ;
if ( att . isEmpty ( ) ) {
2007-02-13 21:13:08 +00:00
XpsMatrixTransform * data = ( XpsMatrixTransform * ) node . getChildData ( " ImageBrush.Transform " ) ;
2007-02-12 13:31:16 +00:00
if ( data ! = NULL ) {
2007-02-13 21:13:08 +00:00
viewportMatrix = * data ;
delete data ;
2007-02-12 13:31:16 +00:00
} else {
viewportMatrix = QMatrix ( ) ;
}
} else {
viewportMatrix = parseRscRefMatrix ( att ) ;
}
viewportMatrix = viewportMatrix * QMatrix ( viewport . width ( ) , 0 , 0 , viewport . height ( ) , viewport . x ( ) , viewbox . y ( ) ) ;
2007-02-17 09:42:15 +00:00
2007-02-12 13:31:16 +00:00
// TODO Brush should work also for QImage, not only QPixmap. But for some images it doesn't work
brush = QBrush ( QPixmap : : fromImage ( image ) ) ;
brush . setMatrix ( viewboxMatrix . inverted ( ) * viewportMatrix ) ;
node . data = new QBrush ( brush ) ;
2007-02-10 17:31:25 +00:00
}
void XpsHandler : : processPath ( XpsRenderNode & node )
{
2007-02-17 10:45:07 +00:00
//TODO Ignored attributes: Clip, OpacityMask, StrokeDashArray, StrokeDashCap, StrokeDashOffset, StrokeEndLineCap, StorkeStartLineCap, StrokeLineJoin, StrokeMiterLimit, Name, FixedPage.NavigateURI, xml:lang, x:key, AutomationProperties.Name, AutomationProperties.HelpText, SnapsToDevicePixels
2007-02-10 17:31:25 +00:00
//TODO Ignored child elements: RenderTransform, Clip, OpacityMask, Stroke, Data
// Handled separately: RenderTransform
m_painter - > save ( ) ;
QString att ;
QPainterPath path ;
// Get path
att = node . attributes . value ( " Data " ) ;
if ( ! att . isEmpty ( ) ) {
path = parseAbbreviatedPathData ( att ) ;
} else {
2007-02-17 09:42:15 +00:00
path = QPainterPath ( ) ; //TODO
2007-02-10 17:31:25 +00:00
}
2007-02-17 09:42:15 +00:00
2007-02-10 17:31:25 +00:00
// Set Fill
att = node . attributes . value ( " Fill " ) ;
QBrush brush ;
if ( ! att . isEmpty ( ) ) {
2007-02-17 11:18:06 +00:00
brush = parseRscRefColorForBrush ( att ) ;
2007-02-10 17:31:25 +00:00
} else {
2007-02-13 21:13:08 +00:00
XpsFill * data = ( XpsFill * ) node . getChildData ( " Path.Fill " ) ;
2007-02-12 13:31:16 +00:00
if ( data ! = NULL ) {
2007-02-13 21:13:08 +00:00
brush = * data ;
delete data ;
2007-02-10 17:31:25 +00:00
} else {
brush = QBrush ( ) ;
}
}
m_painter - > setBrush ( brush ) ;
2007-02-17 10:45:07 +00:00
// Stroke (pen)
// We don't handle the child elements (Path.Stroke) yet.
att = node . attributes . value ( " Stroke " ) ;
QPen pen ( Qt : : transparent ) ;
if ( ! att . isEmpty ( ) ) {
2007-02-17 11:18:06 +00:00
pen = parseRscRefColorForPen ( att ) ;
2007-02-17 10:45:07 +00:00
}
att = node . attributes . value ( " StrokeThickness " ) ;
if ( ! att . isEmpty ( ) ) {
bool ok = false ;
int thickness = att . toInt ( & ok ) ;
if ( ok )
pen . setWidth ( thickness ) ;
}
m_painter - > setPen ( pen ) ;
2007-02-10 17:31:25 +00:00
2007-02-17 09:42:15 +00:00
// Opacity
att = node . attributes . value ( " Opacity " ) ;
if ( ! att . isEmpty ( ) ) {
m_painter - > setOpacity ( att . toDouble ( ) ) ;
}
2007-02-10 17:31:25 +00:00
// RenderTransform
att = node . attributes . value ( " RenderTransform " ) ;
if ( ! att . isEmpty ( ) ) {
m_painter - > setWorldMatrix ( parseRscRefMatrix ( att ) , true ) ;
}
2007-02-13 21:13:08 +00:00
m_painter - > drawPath ( path ) ; //TODO Valgrind sometimes say that path drawing depends on unitialized value in blend_texture
2007-02-10 17:31:25 +00:00
m_painter - > restore ( ) ;
}
void XpsHandler : : processStartElement ( XpsRenderNode & node )
{
if ( node . name = = " Canvas " ) {
m_painter - > save ( ) ;
}
}
void XpsHandler : : processEndElement ( XpsRenderNode & node )
{
if ( node . name = = " Glyphs " ) {
processGlyph ( node ) ;
} else if ( node . name = = " Path " ) {
processPath ( node ) ;
} else if ( node . name = = " MatrixTransform " ) {
//TODO Ignoring x:key
2007-02-17 09:42:15 +00:00
node . data = new QMatrix ( attsToMatrix ( node . attributes . value ( " Matrix " ) ) ) ;
2007-02-10 17:31:25 +00:00
} else if ( ( node . name = = " Canvas.RenderTransform " ) | | ( node . name = = " Glyphs.RenderTransform " ) | | ( node . name = = " Path.RenderTransform " ) ) {
2007-02-13 21:13:08 +00:00
XpsMatrixTransform * data = ( XpsMatrixTransform * ) node . getRequiredChildData ( " MatrixTransform " ) ;
2007-02-12 13:31:16 +00:00
if ( data ! = NULL ) {
2007-02-13 21:13:08 +00:00
m_painter - > setWorldMatrix ( * data , true ) ;
delete data ;
2007-02-10 17:31:25 +00:00
}
} else if ( node . name = = " Canvas " ) {
2006-09-21 11:41:46 +00:00
m_painter - > restore ( ) ;
2007-02-10 17:31:25 +00:00
} else if ( ( node . name = = " Path.Fill " ) | | ( node . name = = " Glyphs.Fill " ) ) {
processFill ( node ) ;
} else if ( node . name = = " SolidColorBrush " ) {
//TODO Ignoring opacity, x:key
2007-02-13 21:13:08 +00:00
node . data = new QBrush ( QColor ( hexToRgba ( node . attributes . value ( " Color " ) . toLatin1 ( ) ) ) ) ;
2007-02-10 17:31:25 +00:00
} else if ( node . name = = " ImageBrush " ) {
processImageBrush ( node ) ;
2007-02-12 13:31:16 +00:00
} else if ( node . name = = " ImageBrush.Transform " ) {
node . data = node . getRequiredChildData ( " MatrixTransform " ) ;
2007-02-10 17:31:25 +00:00
} else {
2007-03-08 03:11:04 +00:00
//kDebug(XpsDebug) << "Unknown element: " << node->name << endl;
2007-02-08 16:41:53 +00:00
}
2007-02-08 16:20:55 +00:00
}
2007-02-13 13:26:37 +00:00
XpsPage : : XpsPage ( XpsFile * file , const QString & fileName ) : m_file ( file ) ,
2006-09-21 11:41:46 +00:00
m_fileName ( fileName ) , m_pageIsRendered ( false )
2006-09-11 10:44:17 +00:00
{
2007-02-08 18:09:15 +00:00
m_pageImage = NULL ;
2007-03-08 03:11:04 +00:00
kDebug ( XpsDebug ) < < " page file name: " < < fileName < < endl ;
2006-09-11 10:44:17 +00:00
2007-02-13 13:26:37 +00:00
const KZipFileEntry * pageFile = static_cast < const KZipFileEntry * > ( m_file - > xpsArchive ( ) - > directory ( ) - > entry ( fileName ) ) ;
2006-09-11 10:44:17 +00:00
2007-02-08 16:20:55 +00:00
QIODevice * pageDevice = pageFile - > createDevice ( ) ;
2006-09-11 10:44:17 +00:00
2007-03-08 03:11:04 +00:00
QXmlStreamReader xml ;
xml . setDevice ( pageDevice ) ;
while ( ! xml . atEnd ( ) )
2007-02-08 16:41:53 +00:00
{
2007-03-08 03:11:04 +00:00
xml . readNext ( ) ;
if ( xml . isStartElement ( ) & & ( xml . name ( ) = = " FixedPage " ) )
{
QXmlStreamAttributes attributes = xml . attributes ( ) ;
m_pageSize . setWidth ( attributes . value ( " Width " ) . toString ( ) . toInt ( ) ) ;
m_pageSize . setHeight ( attributes . value ( " Height " ) . toString ( ) . toInt ( ) ) ;
break ;
}
2007-02-08 16:41:53 +00:00
}
2007-03-08 03:11:04 +00:00
if ( xml . error ( ) )
2007-02-08 16:41:53 +00:00
{
2007-03-08 03:11:04 +00:00
kDebug ( XpsDebug ) < < " Could not parse XPS page: " < < xml . errorString ( ) < < endl ;
2007-02-08 16:41:53 +00:00
}
2007-02-09 08:11:47 +00:00
delete pageDevice ;
}
XpsPage : : ~ XpsPage ( )
{
delete m_pageImage ;
2006-09-11 10:44:17 +00:00
}
2006-09-26 11:53:27 +00:00
bool XpsPage : : renderToImage ( QImage * p )
2006-09-21 11:41:46 +00:00
{
2007-02-17 09:42:15 +00:00
2007-02-08 18:09:15 +00:00
if ( ( m_pageImage = = NULL ) | | ( m_pageImage - > size ( ) ! = p - > size ( ) ) ) {
delete m_pageImage ;
2007-02-10 17:31:25 +00:00
m_pageImage = new QImage ( p - > size ( ) , QImage : : Format_ARGB32 ) ;
2007-03-08 03:11:04 +00:00
// Set one point = one drawing unit. Useful for fonts, because xps specifies font size using drawing units, not points as usual
2007-02-10 17:41:11 +00:00
m_pageImage - > setDotsPerMeterX ( 2835 ) ;
m_pageImage - > setDotsPerMeterY ( 2835 ) ;
2007-02-08 18:09:15 +00:00
m_pageIsRendered = false ;
}
2006-09-21 11:41:46 +00:00
if ( ! m_pageIsRendered ) {
2006-09-26 11:53:27 +00:00
XpsHandler * handler = new XpsHandler ( this ) ;
2007-02-13 11:13:35 +00:00
handler - > m_painter = new QPainter ( m_pageImage ) ;
2007-02-08 18:09:15 +00:00
handler - > m_painter - > setWorldMatrix ( QMatrix ( ) . scale ( ( qreal ) p - > size ( ) . width ( ) / size ( ) . width ( ) , ( qreal ) p - > size ( ) . height ( ) / size ( ) . height ( ) ) ) ;
2006-09-21 11:41:46 +00:00
QXmlSimpleReader * parser = new QXmlSimpleReader ( ) ;
parser - > setContentHandler ( handler ) ;
parser - > setErrorHandler ( handler ) ;
2007-02-13 13:26:37 +00:00
const KZipFileEntry * pageFile = static_cast < const KZipFileEntry * > ( m_file - > xpsArchive ( ) - > directory ( ) - > entry ( m_fileName ) ) ;
2007-02-08 16:41:53 +00:00
QIODevice * pageDevice = pageFile - > createDevice ( ) ;
2007-02-08 16:20:55 +00:00
QXmlInputSource * source = new QXmlInputSource ( pageDevice ) ;
2006-09-21 11:41:46 +00:00
bool ok = parser - > parse ( source ) ;
2007-03-08 03:11:04 +00:00
kDebug ( XpsDebug ) < < " Parse result: " < < ok < < endl ;
2006-09-21 11:41:46 +00:00
delete source ;
delete parser ;
delete handler ;
2007-02-09 08:11:47 +00:00
delete pageDevice ;
2006-09-21 11:41:46 +00:00
m_pageIsRendered = true ;
}
2007-02-08 18:09:15 +00:00
* p = * m_pageImage ;
2006-09-21 11:41:46 +00:00
return true ;
}
2007-02-13 11:13:35 +00:00
Okular : : TextPage * XpsPage : : textPage ( )
{
Okular : : TextPage * tp = new Okular : : TextPage ( ) ;
XpsTextExtractionHandler handler ( this , tp ) ;
QXmlSimpleReader * parser = new QXmlSimpleReader ( ) ;
parser - > setContentHandler ( & handler ) ;
parser - > setErrorHandler ( & handler ) ;
2007-02-13 13:26:37 +00:00
const KZipFileEntry * pageFile = static_cast < const KZipFileEntry * > ( m_file - > xpsArchive ( ) - > directory ( ) - > entry ( m_fileName ) ) ;
2007-02-13 11:13:35 +00:00
QIODevice * pageDevice = pageFile - > createDevice ( ) ;
QXmlInputSource source = QXmlInputSource ( pageDevice ) ;
2007-02-17 09:42:15 +00:00
2007-02-13 11:13:35 +00:00
if ( ! parser - > parse ( source ) ) {
delete tp ;
tp = NULL ;
}
delete pageDevice ;
return tp ;
}
2006-09-11 10:44:17 +00:00
QSize XpsPage : : size ( ) const
{
return m_pageSize ;
}
2007-02-13 13:26:37 +00:00
QFont XpsFile : : getFontByName ( const QString & fileName , float size )
2007-02-06 07:07:23 +00:00
{
int index = m_fontCache . value ( fileName , - 1 ) ;
2007-02-17 09:42:15 +00:00
if ( index = = - 1 )
2007-02-06 07:07:23 +00:00
{
index = loadFontByName ( fileName ) ;
m_fontCache [ fileName ] = index ;
}
2007-02-13 13:26:37 +00:00
QString fontFamily = m_fontDatabase . applicationFontFamilies ( index ) . at ( 0 ) ;
QString fontStyle = m_fontDatabase . styles ( fontFamily ) . at ( 0 ) ;
QFont font = m_fontDatabase . font ( fontFamily , fontStyle , qRound ( size ) ) ;
return font ;
2007-02-06 07:07:23 +00:00
}
2007-02-13 13:26:37 +00:00
int XpsFile : : loadFontByName ( const QString & fileName )
2006-09-21 11:41:46 +00:00
{
2007-03-08 03:11:04 +00:00
// kDebug(XpsDebug) << "font file name: " << fileName << endl;
2006-09-21 11:41:46 +00:00
2007-02-13 13:26:37 +00:00
const KZipFileEntry * fontFile = static_cast < const KZipFileEntry * > ( m_xpsArchive - > directory ( ) - > entry ( fileName ) ) ;
2006-09-21 11:41:46 +00:00
QByteArray fontData = fontFile - > data ( ) ; // once per file, according to the docs
2006-09-23 00:42:42 +00:00
int result = m_fontDatabase . addApplicationFontFromData ( fontData ) ;
2006-09-21 11:41:46 +00:00
if ( - 1 = = result ) {
2007-02-17 09:42:15 +00:00
// Try to deobfuscate font
2007-02-08 16:41:53 +00:00
// TODO Use deobfuscation depending on font content type, don't do it always when standard loading fails
2007-02-17 09:42:15 +00:00
2007-02-08 16:41:53 +00:00
QFileInfo * fileInfo = new QFileInfo ( fileName ) ;
QString baseName = fileInfo - > baseName ( ) ;
delete fileInfo ;
2007-02-06 07:07:23 +00:00
unsigned short guid [ 16 ] ;
if ( ! parseGUID ( baseName , guid ) )
{
2007-03-08 03:11:04 +00:00
kDebug ( XpsDebug ) < < " File to load font - file name isn't a GUID " < < endl ;
2007-02-06 07:07:23 +00:00
}
else
{
2007-02-08 16:41:53 +00:00
if ( fontData . length ( ) < 32 )
2007-02-06 07:07:23 +00:00
{
2007-03-08 03:11:04 +00:00
kDebug ( XpsDebug ) < < " Font file is too small " < < endl ;
2007-02-06 07:07:23 +00:00
} else {
// Obfuscation - xor bytes in font binary with bytes from guid (font's filename)
const static int mapping [ ] = { 15 , 14 , 13 , 12 , 11 , 10 , 9 , 8 , 6 , 7 , 4 , 5 , 0 , 1 , 2 , 3 } ;
for ( int i = 0 ; i < 16 ; i + + ) {
fontData [ i ] = fontData [ i ] ^ guid [ mapping [ i ] ] ;
fontData [ i + 16 ] = fontData [ i + 16 ] ^ guid [ mapping [ i ] ] ;
}
result = m_fontDatabase . addApplicationFontFromData ( fontData ) ;
}
}
2006-09-21 11:41:46 +00:00
}
2007-02-06 07:07:23 +00:00
2007-03-08 03:11:04 +00:00
// kDebug(XpsDebug) << "Loaded font: " << m_fontDatabase.applicationFontFamilies( result ) << endl;
2007-02-06 07:07:23 +00:00
2006-09-21 11:41:46 +00:00
return result ; // a font ID
}
2007-02-13 13:26:37 +00:00
KZip * XpsFile : : xpsArchive ( ) {
return m_xpsArchive ;
}
2007-02-12 13:31:16 +00:00
QImage XpsPage : : loadImageFromFile ( const QString & fileName )
2006-09-21 11:41:46 +00:00
{
2007-03-08 03:11:04 +00:00
//kDebug(XpsDebug) << "image file name: " << fileName << endl;
2006-09-21 11:41:46 +00:00
2007-02-13 13:26:37 +00:00
const KZipFileEntry * imageFile = static_cast < const KZipFileEntry * > ( m_file - > xpsArchive ( ) - > directory ( ) - > entry ( fileName ) ) ;
2006-09-21 11:41:46 +00:00
QByteArray imageData = imageFile - > data ( ) ; // once per file, according to the docs
2007-02-12 13:31:16 +00:00
QImage image ;
2007-02-10 17:31:25 +00:00
image . loadFromData ( imageData ) ;
2007-03-08 03:11:04 +00:00
//kDebug(XpsDebug) << "Image load result: " << result << ", " << image.size() << endl;
2006-09-21 11:41:46 +00:00
return image ;
}
2007-02-19 10:43:40 +00:00
void XpsDocument : : parseDocumentStructure ( const QString & documentStructureFileName )
{
2007-03-08 03:11:04 +00:00
kDebug ( XpsDebug ) < < " document structure file name: " < < documentStructureFileName < < endl ;
2007-02-20 09:29:32 +00:00
m_haveDocumentStructure = false ;
2007-02-19 10:43:40 +00:00
const KZipFileEntry * documentStructureFile = static_cast < const KZipFileEntry * > ( m_file - > xpsArchive ( ) - > directory ( ) - > entry ( documentStructureFileName ) ) ;
QIODevice * documentStructureDevice = documentStructureFile - > createDevice ( ) ;
QDomDocument documentStructureDom ;
QString errMsg ;
int errLine , errCol ;
if ( documentStructureDom . setContent ( documentStructureDevice , true , & errMsg , & errLine , & errCol ) = = false ) {
// parse error
2007-03-08 03:11:04 +00:00
kDebug ( XpsDebug ) < < " Could not parse XPS structure document: " < < errMsg < < " : "
2007-02-19 10:43:40 +00:00
< < errLine < < " : " < < errCol < < endl ;
m_haveDocumentStructure = false ;
return ;
}
QDomNode node = documentStructureDom . documentElement ( ) . firstChild ( ) ;
while ( ! node . isNull ( ) ) {
QDomElement element = node . toElement ( ) ;
if ( ! element . isNull ( ) ) {
if ( element . tagName ( ) = = " DocumentStructure.Outline " ) {
2007-03-08 03:11:04 +00:00
kDebug ( XpsDebug ) < < " found DocumentStructure.Outline " < < endl ;
2007-02-19 10:43:40 +00:00
// there now has to be one DocumentOutline element
QDomNode documentOutlineNode = node . firstChild ( ) ;
if ( node . isNull ( ) )
{
m_haveDocumentStructure = false ;
return ;
}
QDomElement documentOutlineElement = documentOutlineNode . toElement ( ) ;
if ( ( documentOutlineElement . isNull ( ) ) | | ( documentOutlineElement . tagName ( ) ! = " DocumentOutline " ) )
{
m_haveDocumentStructure = false ;
return ;
}
2007-03-08 03:11:04 +00:00
kDebug ( XpsDebug ) < < " found DocumentOutline " < < endl ;
2007-02-19 10:43:40 +00:00
m_docStructure = new Okular : : DocumentSynopsis ;
// now we get a series of OutlineEntry nodes
QDomNode outlineEntryNode = documentOutlineNode . firstChild ( ) ;
while ( ! outlineEntryNode . isNull ( ) )
{
QDomElement outlineEntryElement = outlineEntryNode . toElement ( ) ;
if ( ( outlineEntryElement . isNull ( ) ) | | ( outlineEntryElement . tagName ( ) ! = " OutlineEntry " ) )
{
m_haveDocumentStructure = false ;
return ;
}
2007-02-20 09:29:32 +00:00
m_haveDocumentStructure = true ;
2007-02-19 10:43:40 +00:00
int outlineLevel = outlineEntryElement . attribute ( " OutlineLevel " ) . toInt ( ) ;
QDomElement synopsisElement = m_docStructure - > createElement ( outlineEntryElement . attribute ( " Description " ) ) ;
synopsisElement . setAttribute ( " OutlineLevel " , outlineLevel ) ;
QString target = outlineEntryElement . attribute ( " OutlineTarget " ) ;
int hashPosition = target . lastIndexOf ( ' # ' ) ;
target = target . mid ( hashPosition + 1 ) ;
2007-03-08 03:11:04 +00:00
// kDebug(XpsDebug) << "target: " << target << endl;
2007-02-19 10:43:40 +00:00
Okular : : DocumentViewport viewport ;
viewport . pageNumber = m_docStructurePageMap . value ( target ) ;
synopsisElement . setAttribute ( " Viewport " , viewport . toString ( ) ) ;
if ( outlineLevel = = 1 )
{
2007-03-08 03:11:04 +00:00
// kDebug(XpsDebug) << "Description: " << outlineEntryElement.attribute( "Description" ) << endl;
2007-02-19 10:43:40 +00:00
m_docStructure - > appendChild ( synopsisElement ) ;
} else {
// find the last next highest element (so it this is level 3, we need to find the most recent level 2 node)
2007-03-08 03:11:04 +00:00
// kDebug(XpsDebug) << "Description: (" << outlineEntryElement.attribute( "OutlineLevel" ) << ") "
2007-02-19 10:43:40 +00:00
// << outlineEntryElement.attribute( "Description" ) << endl;
QDomNode maybeParentNode = m_docStructure - > lastChild ( ) ;
while ( ! maybeParentNode . isNull ( ) )
{
if ( maybeParentNode . toElement ( ) . attribute ( " OutlineLevel " ) . toInt ( ) = = ( outlineLevel - 1 ) )
{
// we have the right parent
maybeParentNode . appendChild ( synopsisElement ) ;
break ;
}
maybeParentNode = maybeParentNode . lastChild ( ) ;
}
}
outlineEntryNode = outlineEntryNode . nextSibling ( ) ;
}
} else {
// we need to handle Story here, but I have no examples to test, and no idea what
// to do with it anyway.
2007-03-08 03:11:04 +00:00
kDebug ( XpsDebug ) < < " Unhandled entry in DocumentStructure: " < < element . tagName ( ) < < endl ;
2007-02-19 10:43:40 +00:00
}
}
node = node . nextSibling ( ) ;
}
delete documentStructureDevice ;
}
const Okular : : DocumentSynopsis * XpsDocument : : documentStructure ( )
{
return m_docStructure ;
}
bool XpsDocument : : hasDocumentStructure ( )
{
return m_haveDocumentStructure ;
}
XpsDocument : : XpsDocument ( XpsFile * file , const QString & fileName ) : m_file ( file ) , m_haveDocumentStructure ( false )
2006-09-11 10:44:17 +00:00
{
2007-03-08 03:11:04 +00:00
kDebug ( XpsDebug ) < < " document file name: " < < fileName < < endl ;
2006-09-12 10:31:09 +00:00
2007-02-13 13:26:37 +00:00
const KZipFileEntry * documentFile = static_cast < const KZipFileEntry * > ( file - > xpsArchive ( ) - > directory ( ) - > entry ( fileName ) ) ;
2006-09-11 10:44:17 +00:00
2007-01-23 01:02:17 +00:00
QIODevice * documentDevice = documentFile - > createDevice ( ) ;
2006-09-11 10:44:17 +00:00
QDomDocument documentDom ;
QString errMsg ;
int errLine , errCol ;
if ( documentDom . setContent ( documentDevice , true , & errMsg , & errLine , & errCol ) = = false ) {
// parse error
2007-03-08 03:11:04 +00:00
kDebug ( XpsDebug ) < < " Could not parse XPS document: " < < errMsg < < " : "
2006-09-11 10:44:17 +00:00
< < errLine < < " : " < < errCol < < endl ;
}
QDomNode node = documentDom . documentElement ( ) . firstChild ( ) ;
while ( ! node . isNull ( ) ) {
QDomElement element = node . toElement ( ) ;
if ( ! element . isNull ( ) ) {
if ( element . tagName ( ) = = " PageContent " ) {
2006-09-12 10:31:09 +00:00
QString pagePath = element . attribute ( " Source " ) ;
if ( pagePath . startsWith ( ' / ' ) = = false ) {
int offset = fileName . lastIndexOf ( ' / ' ) ;
QString thisDir = fileName . mid ( 0 , offset ) + ' / ' ;
pagePath . prepend ( thisDir ) ;
}
2007-02-13 13:26:37 +00:00
XpsPage * page = new XpsPage ( file , pagePath ) ;
2006-09-11 10:44:17 +00:00
m_pages . append ( page ) ;
2007-02-19 10:43:40 +00:00
// There can be a single LinkTarget child node here
QDomNode maybeLinkTargetsNode = node . firstChild ( ) ;
if ( ( ! maybeLinkTargetsNode . isNull ( ) ) | | ( maybeLinkTargetsNode . toElement ( ) . tagName ( ) = = " PageContent.LinkTargets " ) )
{
2007-03-08 03:11:04 +00:00
// kDebug(XpsDebug) << "Found link target nodes" << endl;
2007-02-19 10:43:40 +00:00
QDomNode linkTargetNode = maybeLinkTargetsNode . firstChild ( ) ;
while ( ! linkTargetNode . isNull ( ) ) {
2007-03-08 03:11:04 +00:00
// kDebug(XpsDebug) << "looking for a LinkTarget" << endl;
2007-02-19 10:43:40 +00:00
QDomElement linkTargetElement = linkTargetNode . toElement ( ) ;
if ( ! linkTargetElement . isNull ( ) ) {
if ( linkTargetElement . tagName ( ) = = " LinkTarget " )
{
2007-03-08 03:11:04 +00:00
// kDebug(XpsDebug) << "Found linktarget" << endl;
2007-02-19 10:43:40 +00:00
// we have a valid LinkTarget element node
QString targetName = linkTargetElement . attribute ( " Name " ) ;
if ( ! targetName . isEmpty ( ) )
{
m_docStructurePageMap [ targetName ] = m_pages . count ( ) - 1 ;
}
} else {
2007-03-08 03:11:04 +00:00
kDebug ( XpsDebug ) < < " Unexpected tagname. Expected LinkTarget, got " < < element . tagName ( ) < < endl ;
2007-02-19 10:43:40 +00:00
}
} else {
2007-03-08 03:11:04 +00:00
kDebug ( XpsDebug ) < < " Null LinkTarget " < < endl ;
2007-02-19 10:43:40 +00:00
}
linkTargetNode = linkTargetNode . nextSibling ( ) ;
}
}
2006-09-11 10:44:17 +00:00
} else {
2007-03-08 03:11:04 +00:00
kDebug ( XpsDebug ) < < " Unhandled entry in FixedDocument " < < element . tagName ( ) < < endl ;
2006-09-11 10:44:17 +00:00
}
}
node = node . nextSibling ( ) ;
}
delete documentDevice ;
2007-02-19 10:43:40 +00:00
// There might be a relationships entry for this document - typically used to tell us where to find the
// content structure description
// We should be able to find this using a reference from some other part of the document, but I can't see it.
QString maybeDocumentRelationshipPath = fileName ;
// trim off the old filename
int slashPosition = maybeDocumentRelationshipPath . lastIndexOf ( ' / ' ) ;
maybeDocumentRelationshipPath . truncate ( slashPosition ) ;
// add in the path to the document relationships
maybeDocumentRelationshipPath . append ( " /_rels/FixedDoc.fdoc.rels " ) ;
const KZipFileEntry * relFile = static_cast < const KZipFileEntry * > ( file - > xpsArchive ( ) - > directory ( ) - > entry ( maybeDocumentRelationshipPath ) ) ;
QString documentStructureFile ;
if ( relFile ) {
QIODevice * relDevice = relFile - > createDevice ( ) ;
QDomDocument relDom ;
QString errMsg ;
int errLine , errCol ;
if ( relDom . setContent ( relDevice , true , & errMsg , & errLine , & errCol ) = = false ) {
// parse error
2007-03-08 03:11:04 +00:00
kDebug ( XpsDebug ) < < " Could not parse relationship document: " < < errMsg < < " : "
2007-02-19 10:43:40 +00:00
< < errLine < < " : " < < errCol < < endl ;
// try to continue.
} else {
// We work through the relationships document and pull out each element.
QDomNode n = relDom . documentElement ( ) . firstChild ( ) ;
while ( ! n . isNull ( ) ) {
QDomElement e = n . toElement ( ) ;
if ( ! e . isNull ( ) ) {
if ( " http://schemas.microsoft.com/xps/2005/06/documentstructure " = = e . attribute ( " Type " ) ) {
documentStructureFile = e . attribute ( " Target " ) ;
} else {
2007-03-08 03:11:04 +00:00
kDebug ( XpsDebug ) < < " Unknown document relationships element: " < < e . attribute ( " Type " ) < < " : " < < e . attribute ( " Target " ) < < endl ;
2007-02-19 10:43:40 +00:00
}
}
n = n . nextSibling ( ) ;
}
}
delete relDevice ;
} else {
// this isn't fatal
2007-03-08 03:11:04 +00:00
kDebug ( XpsDebug ) < < " Could not open Document relationship file from " < < maybeDocumentRelationshipPath < < endl ;
2007-02-19 10:43:40 +00:00
}
if ( ! documentStructureFile . isEmpty ( ) )
{
2007-03-08 03:11:04 +00:00
// kDebug(XpsDebug) << "Document structure filename: " << documentStructureFile << endl;
2007-02-19 10:43:40 +00:00
// make the document path absolute
if ( documentStructureFile . startsWith ( ' / ' ) )
{
// it is already absolute, don't do anything
} else
{
// we reuse the relationship string
maybeDocumentRelationshipPath . truncate ( slashPosition ) ;
documentStructureFile . prepend ( maybeDocumentRelationshipPath + ' / ' ) ;
}
2007-03-08 03:11:04 +00:00
// kDebug(XpsDebug) << "Document structure absolute path: " << documentStructureFile << endl;
2007-02-19 10:43:40 +00:00
parseDocumentStructure ( documentStructureFile ) ;
}
2006-09-11 10:44:17 +00:00
}
2007-02-09 08:11:47 +00:00
XpsDocument : : ~ XpsDocument ( )
{
for ( int i = 0 ; i < m_pages . size ( ) ; i + + ) {
delete m_pages . at ( i ) ;
}
m_pages . clear ( ) ;
2007-02-19 10:43:40 +00:00
2007-02-20 09:29:32 +00:00
if ( m_docStructure )
2007-02-19 10:43:40 +00:00
delete m_docStructure ;
2007-02-09 08:11:47 +00:00
}
2006-09-11 10:44:17 +00:00
int XpsDocument : : numPages ( ) const
{
return m_pages . size ( ) ;
}
XpsPage * XpsDocument : : page ( int pageNum ) const
{
return m_pages . at ( pageNum ) ;
}
2006-09-11 11:05:40 +00:00
XpsFile : : XpsFile ( ) : m_docInfo ( 0 )
2006-09-11 10:44:17 +00:00
{
2006-09-19 09:09:30 +00:00
}
2006-09-11 10:44:17 +00:00
XpsFile : : ~ XpsFile ( )
{
2007-02-13 13:26:37 +00:00
m_fontCache . clear ( ) ;
2007-02-13 21:13:08 +00:00
m_fontDatabase . removeAllApplicationFonts ( ) ;
2006-09-19 09:09:30 +00:00
}
2006-09-11 10:44:17 +00:00
bool XpsFile : : loadDocument ( const QString & filename )
{
2007-02-13 13:26:37 +00:00
m_xpsArchive = new KZip ( filename ) ;
if ( m_xpsArchive - > open ( QIODevice : : ReadOnly ) = = true ) {
2007-03-08 03:11:04 +00:00
kDebug ( XpsDebug ) < < " Successful open of " < < m_xpsArchive - > fileName ( ) < < endl ;
2006-09-11 10:44:17 +00:00
} else {
2007-03-08 03:11:04 +00:00
kDebug ( XpsDebug ) < < " Could not open XPS archive: " < < m_xpsArchive - > fileName ( ) < < endl ;
2007-02-13 13:26:37 +00:00
delete m_xpsArchive ;
2006-09-11 10:44:17 +00:00
return false ;
}
2007-02-19 10:43:40 +00:00
// The only fixed entry in XPS is /_rels/.rels
2007-02-13 13:26:37 +00:00
const KZipFileEntry * relFile = static_cast < const KZipFileEntry * > ( m_xpsArchive - > directory ( ) - > entry ( " _rels/.rels " ) ) ;
2006-09-11 10:44:17 +00:00
if ( ! relFile ) {
// this might occur if we can't read the zip directory, or it doesn't have the relationships entry
return false ;
}
2007-01-23 01:02:17 +00:00
QIODevice * relDevice = relFile - > createDevice ( ) ;
2006-09-11 10:44:17 +00:00
QDomDocument relDom ;
QString errMsg ;
int errLine , errCol ;
if ( relDom . setContent ( relDevice , true , & errMsg , & errLine , & errCol ) = = false ) {
// parse error
2007-03-08 03:11:04 +00:00
kDebug ( XpsDebug ) < < " Could not parse relationship document: " < < errMsg < < " : "
2006-09-11 10:44:17 +00:00
< < errLine < < " : " < < errCol < < endl ;
return false ;
}
QString fixedRepresentationFileName ;
// We work through the relationships document and pull out each element.
QDomNode n = relDom . documentElement ( ) . firstChild ( ) ;
while ( ! n . isNull ( ) ) {
QDomElement e = n . toElement ( ) ;
if ( ! e . isNull ( ) ) {
if ( " http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail " = = e . attribute ( " Type " ) ) {
m_thumbnailFileName = e . attribute ( " Target " ) ;
} else if ( " http://schemas.microsoft.com/xps/2005/06/fixedrepresentation " = = e . attribute ( " Type " ) ) {
fixedRepresentationFileName = e . attribute ( " Target " ) ;
} else if ( " http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties " = = e . attribute ( " Type " ) ) {
m_corePropertiesFileName = e . attribute ( " Target " ) ;
2007-02-17 09:56:08 +00:00
} else if ( " http://schemas.openxmlformats.org/package/2006/relationships/digital-signature/origin " = = e . attribute ( " Type " ) ) {
m_signatureOrigin = e . attribute ( " Target " ) ;
2006-09-11 10:44:17 +00:00
} else {
2007-03-08 03:11:04 +00:00
kDebug ( XpsDebug ) < < " Unknown relationships element: " < < e . attribute ( " Type " ) < < " : " < < e . attribute ( " Target " ) < < endl ;
2006-09-11 10:44:17 +00:00
}
}
n = n . nextSibling ( ) ;
}
if ( fixedRepresentationFileName . isEmpty ( ) ) {
// FixedRepresentation is a required part of the XPS document
return false ;
}
delete relDevice ;
2007-02-13 13:26:37 +00:00
const KZipFileEntry * fixedRepFile = static_cast < const KZipFileEntry * > ( m_xpsArchive - > directory ( ) - > entry ( fixedRepresentationFileName ) ) ;
2006-09-11 10:44:17 +00:00
2007-01-23 01:02:17 +00:00
QIODevice * fixedRepDevice = fixedRepFile - > createDevice ( ) ;
2006-09-11 10:44:17 +00:00
QDomDocument fixedRepDom ;
if ( fixedRepDom . setContent ( fixedRepDevice , true , & errMsg , & errLine , & errCol ) = = false ) {
// parse error
2007-03-08 03:11:04 +00:00
kDebug ( XpsDebug ) < < " Could not parse Fixed Representation document: " < < errMsg < < " : "
2006-09-11 10:44:17 +00:00
< < errLine < < " : " < < errCol < < endl ;
return false ;
}
n = fixedRepDom . documentElement ( ) . firstChild ( ) ;
while ( ! n . isNull ( ) ) {
QDomElement e = n . toElement ( ) ;
if ( ! e . isNull ( ) ) {
if ( e . tagName ( ) = = " DocumentReference " ) {
2007-02-13 13:26:37 +00:00
XpsDocument * doc = new XpsDocument ( this , e . attribute ( " Source " ) ) ;
2006-09-21 11:41:46 +00:00
for ( int lv = 0 ; lv < doc - > numPages ( ) ; + + lv ) {
// our own copy of the pages list
m_pages . append ( doc - > page ( lv ) ) ;
}
2006-09-11 10:44:17 +00:00
m_documents . append ( doc ) ;
} else {
2007-03-08 03:11:04 +00:00
kDebug ( XpsDebug ) < < " Unhandled entry in FixedDocumentSequence " < < e . tagName ( ) < < endl ;
2006-09-11 10:44:17 +00:00
}
}
n = n . nextSibling ( ) ;
}
delete fixedRepDevice ;
return true ;
}
2006-09-21 08:45:36 +00:00
const Okular : : DocumentInfo * XpsFile : : generateDocumentInfo ( )
2006-09-11 10:44:17 +00:00
{
if ( m_docInfo )
return m_docInfo ;
2006-09-21 08:45:36 +00:00
m_docInfo = new Okular : : DocumentInfo ( ) ;
2006-09-11 10:44:17 +00:00
m_docInfo - > set ( " mimeType " , " application/vnd.ms-xpsdocument " ) ;
if ( ! m_corePropertiesFileName . isEmpty ( ) ) {
2007-02-13 13:26:37 +00:00
const KZipFileEntry * corepropsFile = static_cast < const KZipFileEntry * > ( m_xpsArchive - > directory ( ) - > entry ( m_corePropertiesFileName ) ) ;
2006-09-11 10:44:17 +00:00
2007-01-23 01:02:17 +00:00
QIODevice * corepropsDevice = corepropsFile - > createDevice ( ) ;
2006-09-11 10:44:17 +00:00
2007-03-08 03:11:04 +00:00
QXmlStreamReader xml ;
xml . setDevice ( corepropsDevice ) ;
while ( ! xml . atEnd ( ) )
{
xml . readNext ( ) ;
if ( xml . isEndElement ( ) )
break ;
if ( xml . isStartElement ( ) )
{
if ( xml . name ( ) = = " title " ) {
m_docInfo - > set ( " title " , xml . readElementText ( ) , i18n ( " Title " ) ) ;
} else if ( xml . name ( ) = = " subject " ) {
m_docInfo - > set ( " subject " , xml . readElementText ( ) , i18n ( " Subject " ) ) ;
} else if ( xml . name ( ) = = " description " ) {
m_docInfo - > set ( " description " , xml . readElementText ( ) , i18n ( " Description " ) ) ;
} else if ( xml . name ( ) = = " creator " ) {
m_docInfo - > set ( " creator " , xml . readElementText ( ) , i18n ( " Author " ) ) ;
} else if ( xml . name ( ) = = " category " ) {
m_docInfo - > set ( " category " , xml . readElementText ( ) , i18n ( " Category " ) ) ;
} else if ( xml . name ( ) = = " created " ) {
QDateTime createdDate = QDateTime : : fromString ( xml . readElementText ( ) , " yyyy-MM-ddThh:mm:ssZ " ) ;
2006-09-11 10:44:17 +00:00
m_docInfo - > set ( " creationDate " , KGlobal : : locale ( ) - > formatDateTime ( createdDate , false , true ) , i18n ( " Created " ) ) ;
2007-03-08 03:11:04 +00:00
} else if ( xml . name ( ) = = " modified " ) {
QDateTime modifiedDate = QDateTime : : fromString ( xml . readElementText ( ) , " yyyy-MM-ddThh:mm:ssZ " ) ;
2006-09-11 10:44:17 +00:00
m_docInfo - > set ( " modifiedDate " , KGlobal : : locale ( ) - > formatDateTime ( modifiedDate , false , true ) , i18n ( " Modified " ) ) ;
2007-03-08 03:11:04 +00:00
} else if ( xml . name ( ) = = " keywords " ) {
m_docInfo - > set ( " keywords " , xml . readElementText ( ) , i18n ( " Keywords " ) ) ;
2006-09-11 10:44:17 +00:00
}
}
2007-03-08 03:11:04 +00:00
}
if ( xml . error ( ) )
{
kDebug ( XpsDebug ) < < " Could not parse XPS core properties: " < < xml . errorString ( ) < < endl ;
2006-09-11 10:44:17 +00:00
}
delete corepropsDevice ;
} else {
2007-03-08 03:11:04 +00:00
kDebug ( XpsDebug ) < < " No core properties filename " < < endl ;
2006-09-11 10:44:17 +00:00
}
m_docInfo - > set ( " pages " , QString : : number ( numPages ( ) ) , i18n ( " Pages " ) ) ;
return m_docInfo ;
}
bool XpsFile : : closeDocument ( )
{
if ( m_docInfo )
delete m_docInfo ;
m_docInfo = 0 ;
m_documents . clear ( ) ;
2007-02-13 13:26:37 +00:00
delete m_xpsArchive ;
2006-09-11 10:44:17 +00:00
return true ;
}
int XpsFile : : numPages ( ) const
{
2006-09-21 11:41:46 +00:00
return m_pages . size ( ) ;
2006-09-11 10:44:17 +00:00
}
int XpsFile : : numDocuments ( ) const
{
return m_documents . size ( ) ;
}
XpsDocument * XpsFile : : document ( int documentNum ) const
{
2006-09-21 11:41:46 +00:00
return m_documents . at ( documentNum ) ;
}
XpsPage * XpsFile : : page ( int pageNum ) const
{
return m_pages . at ( pageNum ) ;
2006-09-11 10:44:17 +00:00
}
2006-10-22 11:25:08 +00:00
XpsGenerator : : XpsGenerator ( )
2007-02-10 17:47:44 +00:00
: Okular : : Generator ( ) , m_xpsFile ( 0 )
2006-09-11 10:44:17 +00:00
{
2007-02-13 11:13:35 +00:00
setFeature ( TextExtraction ) ;
2006-09-11 10:44:17 +00:00
}
XpsGenerator : : ~ XpsGenerator ( )
{
}
2006-09-21 08:45:36 +00:00
bool XpsGenerator : : loadDocument ( const QString & fileName , QVector < Okular : : Page * > & pagesVector )
2006-09-11 10:44:17 +00:00
{
2007-02-06 07:07:23 +00:00
m_xpsFile = new XpsFile ( ) ;
2006-09-11 10:44:17 +00:00
m_xpsFile - > loadDocument ( fileName ) ;
pagesVector . resize ( m_xpsFile - > numPages ( ) ) ;
int pagesVectorOffset = 0 ;
for ( int docNum = 0 ; docNum < m_xpsFile - > numDocuments ( ) ; + + docNum )
{
XpsDocument * doc = m_xpsFile - > document ( docNum ) ;
for ( int pageNum = 0 ; pageNum < doc - > numPages ( ) ; + + pageNum )
{
QSize pageSize = doc - > page ( pageNum ) - > size ( ) ;
2007-01-05 17:09:47 +00:00
pagesVector [ pagesVectorOffset ] = new Okular : : Page ( pagesVectorOffset , pageSize . width ( ) , pageSize . height ( ) , Okular : : Rotation0 ) ;
2006-09-11 10:44:17 +00:00
+ + pagesVectorOffset ;
}
}
return true ;
}
bool XpsGenerator : : closeDocument ( )
{
m_xpsFile - > closeDocument ( ) ;
2007-02-10 17:47:44 +00:00
delete m_xpsFile ;
m_xpsFile = 0 ;
2006-09-11 10:44:17 +00:00
return true ;
}
2007-01-31 18:31:19 +00:00
QImage XpsGenerator : : image ( Okular : : PixmapRequest * request )
2006-09-11 10:44:17 +00:00
{
2007-02-08 18:09:15 +00:00
QSize size ( ( int ) request - > width ( ) , ( int ) request - > height ( ) ) ;
2006-09-26 11:53:27 +00:00
QImage image ( size , QImage : : Format_RGB32 ) ;
2006-10-25 15:35:53 +00:00
XpsPage * pageToRender = m_xpsFile - > page ( request - > page ( ) - > number ( ) ) ;
2006-09-26 11:53:27 +00:00
pageToRender - > renderToImage ( & image ) ;
2007-01-31 18:31:19 +00:00
return image ;
2006-09-11 10:44:17 +00:00
}
2007-02-13 11:13:35 +00:00
Okular : : TextPage * XpsGenerator : : textPage ( Okular : : Page * page )
{
XpsPage * xpsPage = m_xpsFile - > page ( page - > number ( ) ) ;
return xpsPage - > textPage ( ) ;
}
2006-09-21 08:45:36 +00:00
const Okular : : DocumentInfo * XpsGenerator : : generateDocumentInfo ( )
2006-09-11 10:44:17 +00:00
{
2007-03-08 03:11:04 +00:00
kDebug ( XpsDebug ) < < " generating document metadata " < < endl ;
2006-09-11 10:44:17 +00:00
return m_xpsFile - > generateDocumentInfo ( ) ;
}
2007-02-19 10:43:40 +00:00
const Okular : : DocumentSynopsis * XpsGenerator : : generateDocumentSynopsis ( )
{
2007-03-08 03:11:04 +00:00
kDebug ( XpsDebug ) < < " generating document synopsis " < < endl ;
2007-02-19 10:43:40 +00:00
// we only generate the synopsis for the first file.
if ( ! m_xpsFile | | ! m_xpsFile - > document ( 0 ) )
return NULL ;
if ( m_xpsFile - > document ( 0 ) - > hasDocumentStructure ( ) )
return m_xpsFile - > document ( 0 ) - > documentStructure ( ) ;
return NULL ;
}
2007-02-10 17:31:25 +00:00
XpsRenderNode * XpsRenderNode : : findChild ( const QString & name )
{
for ( int i = 0 ; i < children . size ( ) ; i + + ) {
if ( children [ i ] . name = = name ) {
return & children [ i ] ;
}
}
return NULL ;
}
2007-02-12 13:31:16 +00:00
void * XpsRenderNode : : getRequiredChildData ( const QString & name )
{
XpsRenderNode * child = findChild ( name ) ;
if ( child = = NULL ) {
2007-03-08 03:11:04 +00:00
kDebug ( XpsDebug ) < < " Required element " < < name < < " is missing in " < < this - > name < < endl ;
2007-02-12 13:31:16 +00:00
return NULL ;
}
return child - > data ;
}
void * XpsRenderNode : : getChildData ( const QString & name )
{
XpsRenderNode * child = findChild ( name ) ;
if ( child = = NULL ) {
return NULL ;
} else {
return child - > data ;
}
}
2007-02-13 11:13:35 +00:00
XpsTextExtractionHandler : : XpsTextExtractionHandler ( XpsPage * page , Okular : : TextPage * textPage ) : XpsHandler ( page ) , m_textPage ( textPage ) { }
bool XpsTextExtractionHandler : : startDocument ( )
{
m_matrixes . push ( QMatrix ( ) ) ;
m_matrix = QMatrix ( ) ;
m_useMatrix = false ;
return true ;
}
bool XpsTextExtractionHandler : : startElement ( const QString & nameSpace ,
const QString & localName ,
const QString & qname ,
const QXmlAttributes & atts )
{
Q_UNUSED ( nameSpace ) ;
Q_UNUSED ( qname ) ;
if ( localName = = " Canvas " ) {
m_matrixes . push ( m_matrix ) ;
2007-02-17 09:42:15 +00:00
2007-02-13 11:13:35 +00:00
QString att = atts . value ( " RenderTransform " ) ;
if ( ! att . isEmpty ( ) ) {
m_matrix = parseRscRefMatrix ( att ) * m_matrix ;
}
} else if ( ( localName = = " Canvas.RenderTransform " ) | | ( localName = = " Glyphs.RenderTransform " ) ) {
m_useMatrix = true ;
} else if ( m_useMatrix & & ( localName = = " MatrixTransform " ) ) {
m_matrix = attsToMatrix ( atts . value ( " Matrix " ) ) * m_matrix ;
} else if ( localName = = " Glyphs " ) {
m_matrixes . push ( m_matrix ) ;
m_glyphsAtts = atts ;
}
return true ;
}
bool XpsTextExtractionHandler : : endElement ( const QString & nameSpace ,
const QString & localName ,
const QString & qname )
{
Q_UNUSED ( nameSpace ) ;
Q_UNUSED ( qname ) ;
if ( localName = = " Canvas " ) {
m_matrix = m_matrixes . pop ( ) ;
} else if ( ( localName = = " Canvas.RenderTransform " ) | | ( localName = = " Glyphs.RenderTransform " ) ) {
m_useMatrix = false ;
} else if ( localName = = " Glyphs " ) {
2007-02-17 09:42:15 +00:00
2007-02-13 11:13:35 +00:00
QString att ;
att = m_glyphsAtts . value ( " RenderTransform " ) ;
if ( ! att . isEmpty ( ) ) {
m_matrix = parseRscRefMatrix ( att ) * m_matrix ;
}
QString text = m_glyphsAtts . value ( " UnicodeString " ) ;
// Get font (doesn't work well because qt doesn't allow to load font from file)
2007-02-13 13:26:37 +00:00
QFont font = m_page - > m_file - > getFontByName ( m_glyphsAtts . value ( " FontUri " ) , m_glyphsAtts . value ( " FontRenderingEmSize " ) . toFloat ( ) * 72 / 96 ) ;
2007-02-13 11:13:35 +00:00
QFontMetrics metrics = QFontMetrics ( font ) ;
// Origin
QPointF origin ( m_glyphsAtts . value ( " OriginX " ) . toDouble ( ) , m_glyphsAtts . value ( " OriginY " ) . toDouble ( ) ) ;
QSize s = m_page - > m_pageSize ;
int lastWidth = 0 ;
for ( int i = 0 ; i < text . length ( ) ; i + + ) {
int width = metrics . width ( text , i + 1 ) ;
int charWidth = width - lastWidth ;
Okular : : NormalizedRect * rect = new Okular : : NormalizedRect ( ( origin . x ( ) + lastWidth ) / s . width ( ) , ( origin . y ( ) - metrics . height ( ) ) / s . height ( ) ,
( origin . x ( ) + width ) / s . width ( ) , origin . y ( ) / s . height ( ) ) ;
rect - > transform ( m_matrix ) ;
m_textPage - > append ( text . mid ( i , 1 ) , rect ) ;
lastWidth = width ;
}
// QRectF textRect = metrics.boundingRect( text );
// textRect.moveTo( origin.x(), origin.y() - textRect.height() );
// textRect = m_matrix.mapRect( textRect );
// Okular::NormalizedRect * rect = new Okular::NormalizedRect( textRect.x() / s.width(), textRect.y() / s.height(), (textRect.x() + textRect.width()) / s.width(), (textRect.y() + textRect.height()) / s.height() );
2007-03-08 03:11:04 +00:00
// kDebug(XpsDebug) << rect->left << " " << rect->top << " " << rect->right << " " << rect->bottom << " " << text << endl;
2007-02-13 11:13:35 +00:00
m_matrix = m_matrixes . pop ( ) ;
2007-02-17 09:42:15 +00:00
2007-02-13 11:13:35 +00:00
}
return true ;
}
2006-09-11 10:44:17 +00:00
# include "generator_xps.moc"