2006-09-11 10:44:17 +00:00
/*
2009-05-24 04:40:10 +00:00
Copyright ( C ) 2006 , 2009 Brad Hards < bradh @ frogmouth . net >
2006-09-11 10:44:17 +00:00
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 .
*/
2007-04-19 18:30:20 +00:00
# include "generator_xps.h"
2014-09-18 20:17:10 +00:00
# include <KAboutData>
2014-10-08 11:37:04 +00:00
# include <KLocalizedString>
2007-04-19 14:38:03 +00:00
# include <QBuffer>
2020-07-08 11:54:37 +00:00
# include <QDateTime>
# include <QFile>
2007-04-20 09:15:00 +00:00
# include <QImageReader>
2020-07-08 11:54:37 +00:00
# include <QList>
2009-04-22 23:02:00 +00:00
# include <QMutex>
2020-07-08 11:54:37 +00:00
# include <QPainter>
# include <QPrinter>
2015-01-29 19:55:57 +00:00
# include <QUrl>
2006-09-11 10:44:17 +00:00
2011-06-01 08:49:49 +00:00
# include <core/area.h>
# include <core/document.h>
# include <core/fileprinter.h>
# include <core/page.h>
2006-09-11 10:44:17 +00:00
2015-11-28 19:24:41 +00:00
OKULAR_EXPORT_PLUGIN ( XpsGenerator , " libokularGenerator_xps.json " )
2006-09-11 10:44:17 +00:00
2008-09-04 17:30:56 +00:00
Q_DECLARE_METATYPE ( QGradient * )
Q_DECLARE_METATYPE ( XpsPathFigure * )
Q_DECLARE_METATYPE ( XpsPathGeometry * )
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
2014-08-24 20:52:23 +00:00
static QColor hexToRgba ( const QByteArray & name )
2006-09-21 11:41:46 +00:00
{
2014-08-24 20:52:23 +00:00
const int len = name . length ( ) ;
if ( len = = 0 | | name [ 0 ] ! = ' # ' )
2006-09-21 11:41:46 +00:00
return QColor ( ) ;
int r , g , b ;
int a = 255 ;
2014-08-24 20:52:23 +00:00
if ( len = = 7 ) {
r = ( hex2int ( name [ 1 ] ) < < 4 ) + hex2int ( name [ 2 ] ) ;
g = ( hex2int ( name [ 3 ] ) < < 4 ) + hex2int ( name [ 4 ] ) ;
b = ( hex2int ( name [ 5 ] ) < < 4 ) + hex2int ( name [ 6 ] ) ;
} else if ( len = = 9 ) {
a = ( hex2int ( name [ 1 ] ) < < 4 ) + hex2int ( name [ 2 ] ) ;
r = ( hex2int ( name [ 3 ] ) < < 4 ) + hex2int ( name [ 4 ] ) ;
g = ( hex2int ( name [ 5 ] ) < < 4 ) + hex2int ( name [ 6 ] ) ;
b = ( hex2int ( name [ 7 ] ) < < 4 ) + hex2int ( name [ 8 ] ) ;
2006-09-21 11:41:46 +00:00
} 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 )
{
2016-07-11 19:59:14 +00:00
QStringList numbers = data . split ( QLatin1Char ( ' , ' ) ) ;
2006-09-21 11:41:46 +00:00
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-05-24 21:22:25 +00:00
static bool parseGUID ( const QString & guidString , unsigned short guid [ 16 ] )
{
2007-02-06 07:07:23 +00:00
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 ) ;
2016-07-11 19:59:14 +00:00
if ( ch . isNumber ( ) | | ( ch = = QLatin1Char ( ' + ' ) ) | | ( ch = = QLatin1Char ( ' - ' ) ) ) {
2007-02-08 15:07:54 +00:00
int start = * curPos ;
2016-07-11 19:59:14 +00:00
while ( ( * curPos < data . length ( ) ) & & ( ! data . at ( * curPos ) . isSpace ( ) ) & & ( data . at ( * curPos ) ! = QLatin1Char ( ' , ' ) & & ( ! data . at ( * curPos ) . isLetter ( ) | | data . at ( * curPos ) = = QLatin1Char ( ' e ' ) ) ) ) {
2007-02-08 16:41:53 +00:00
( * curPos ) + + ;
}
2015-10-29 12:37:11 +00:00
token - > number = data . midRef ( start , * curPos - start ) . toDouble ( ) ;
2007-02-08 16:41:53 +00:00
token - > type = abtNumber ;
2007-02-08 15:07:54 +00:00
2016-07-11 19:59:14 +00:00
} else if ( ch = = QLatin1Char ( ' , ' ) ) {
2007-02-08 15:07:54 +00:00
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-04-16 17:53:15 +00:00
( * curPos ) + + ;
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
*/
2020-02-20 17:45:46 +00:00
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
2008-06-27 11:52:45 +00:00
/**
Read point ( two reals delimited by comma ) from string
*/
static QPointF getPointFromString ( const QString & string )
{
2016-07-11 19:59:14 +00:00
const int commaPos = string . indexOf ( QLatin1Char ( QLatin1Char ( ' , ' ) ) ) ;
if ( commaPos = = - 1 | | string . indexOf ( QLatin1Char ( QLatin1Char ( ' , ' ) ) , commaPos + 1 ) ! = - 1 )
2008-06-27 11:52:45 +00:00
return QPointF ( ) ;
QPointF result ;
bool ok = false ;
QStringRef ref = string . midRef ( 0 , commaPos ) ;
result . setX ( QString : : fromRawData ( ref . constData ( ) , ref . count ( ) ) . toDouble ( & ok ) ) ;
if ( ! ok )
return QPointF ( ) ;
ref = string . midRef ( commaPos + 1 ) ;
result . setY ( QString : : fromRawData ( ref . constData ( ) , ref . count ( ) ) . toDouble ( & ok ) ) ;
if ( ! ok )
return QPointF ( ) ;
return result ;
}
2008-06-29 02:15:17 +00:00
static Qt : : FillRule fillRuleFromString ( const QString & data , Qt : : FillRule def = Qt : : OddEvenFill )
{
if ( data = = QLatin1String ( " EvenOdd " ) ) {
return Qt : : OddEvenFill ;
} else if ( data = = QLatin1String ( " NonZero " ) ) {
return Qt : : WindingFill ;
}
return def ;
}
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-05-24 21:22:25 +00:00
QPainterPath path ;
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 ) {
2014-09-11 19:12:27 +00:00
qCWarning ( OkularXpsDebug ) . nospace ( ) < < " Error in parsing abbreviated path data ( " < < token . type < < " @ " < < token . curPos < < " ): " < < data ;
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
}
2016-07-11 19:59:14 +00:00
char command = QChar : : fromLatin1 ( token . command ) . toLower ( ) . cell ( ) ;
bool isRelative = QChar : : fromLatin1 ( 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 ) ;
2020-07-10 22:15:05 +00:00
}
2007-02-08 15:07:54 +00:00
nextAbbPathToken ( & token ) ;
2020-07-10 22:15:05 +00:00
break ;
2007-02-08 15:07:54 +00:00
case ' m ' : // Move
while ( token . type = = abtNumber ) {
2007-02-10 17:31:25 +00:00
QPointF point = getPointFromString ( & token , isRelative , currPos ) ;
path . moveTo ( point ) ;
2020-07-10 22:15:05 +00:00
}
break ;
2007-02-08 15:07:54 +00:00
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
}
2020-07-10 22:15:05 +00:00
break ;
2007-02-08 15:07:54 +00:00
case ' h ' : // Horizontal line
while ( token . type = = abtNumber ) {
2008-06-27 17:15:05 +00:00
double x = token . number ;
if ( isRelative )
x + = path . currentPosition ( ) . x ( ) ;
2007-02-10 17:31:25 +00:00
path . lineTo ( x , path . currentPosition ( ) . y ( ) ) ;
2007-02-17 09:42:15 +00:00
nextAbbPathToken ( & token ) ;
2020-07-10 22:15:05 +00:00
}
2007-02-08 15:07:54 +00:00
break ;
case ' v ' : // Vertical line
while ( token . type = = abtNumber ) {
2008-06-27 17:15:05 +00:00
double y = token . number ;
if ( isRelative )
y + = path . currentPosition ( ) . y ( ) ;
2007-02-10 17:31:25 +00:00
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 ) ;
2020-07-10 22:15:05 +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 ) ;
2011-05-08 18:18:03 +00:00
path . quadTo ( point1 , point2 ) ;
2007-02-17 09:42:15 +00:00
}
2007-02-08 15:07:54 +00:00
break ;
case ' s ' : // Smooth cubic bezier curve
while ( token . type = = abtNumber ) {
2007-02-10 17:31:25 +00:00
QPointF firstControl ;
if ( ( lastCommand = = ' s ' ) | | ( lastCommand = = ' c ' ) ) {
firstControl = lastSecondControlPoint + ( lastSecondControlPoint + path . currentPosition ( ) ) ;
} else {
firstControl = path . currentPosition ( ) ;
2007-02-08 15:07:54 +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 ) ;
2020-07-10 22:15:05 +00:00
}
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 ) ;
2020-07-10 22:15:05 +00:00
}
break ;
2007-02-08 15:07:54 +00:00
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
2007-03-10 10:18:12 +00:00
/**
Parse a " Matrix " attribute string
\ param csv the comma separated list of values
2012-09-08 15:06:02 +00:00
\ return the QTransform corresponding to the affine transform
2007-03-10 10:18:12 +00:00
given in the attribute
\ see XPS specification 7.4 .1
*/
2012-09-08 15:06:02 +00:00
static QTransform attsToMatrix ( const QString & csv )
2006-09-26 11:53:27 +00:00
{
2016-07-11 19:59:14 +00:00
QStringList values = csv . split ( QLatin1Char ( ' , ' ) ) ;
2006-09-26 11:53:27 +00:00
if ( values . count ( ) ! = 6 ) {
2012-09-08 15:06:02 +00:00
return QTransform ( ) ; // that is an identity matrix - no effect
2007-02-06 07:07:23 +00:00
}
2012-09-08 15:06:02 +00:00
return QTransform ( values . at ( 0 ) . toDouble ( ) , values . at ( 1 ) . toDouble ( ) , values . at ( 2 ) . toDouble ( ) , values . at ( 3 ) . toDouble ( ) , 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-03-10 10:18:12 +00:00
/**
\ return Brush with given color or brush specified by reference to resource
*/
static QBrush parseRscRefColorForBrush ( const QString & data )
2007-02-10 17:31:25 +00:00
{
2016-07-11 19:59:14 +00:00
if ( data [ 0 ] = = QLatin1Char ( ' { ' ) ) {
2007-02-10 17:31:25 +00:00
// TODO
2014-09-11 19:12:27 +00:00
qCWarning ( OkularXpsDebug ) < < " Reference " < < data ;
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 ( ) ) ) ;
}
}
2007-03-10 10:18:12 +00:00
/**
\ return Pen with given color or Pen specified by reference to resource
*/
static QPen parseRscRefColorForPen ( const QString & data )
2007-02-17 11:18:06 +00:00
{
2016-07-11 19:59:14 +00:00
if ( data [ 0 ] = = QLatin1Char ( ' { ' ) ) {
2007-02-17 11:18:06 +00:00
// TODO
2014-09-11 19:12:27 +00:00
qCWarning ( OkularXpsDebug ) < < " Reference " < < data ;
2007-02-17 11:18:06 +00:00
return QPen ( ) ;
} else {
return QPen ( hexToRgba ( data . toLatin1 ( ) ) ) ;
2007-02-10 17:31:25 +00:00
}
}
2007-03-10 10:18:12 +00:00
/**
\ return Matrix specified by given data or by referenced dictionary
*/
2012-09-08 15:06:02 +00:00
static QTransform parseRscRefMatrix ( const QString & data )
2007-02-10 17:31:25 +00:00
{
2016-07-11 19:59:14 +00:00
if ( data [ 0 ] = = QLatin1Char ( ' { ' ) ) {
2007-02-10 17:31:25 +00:00
// TODO
2014-09-11 19:12:27 +00:00
qCWarning ( OkularXpsDebug ) < < " Reference " < < data ;
2012-09-08 15:06:02 +00:00
return QTransform ( ) ;
2007-02-10 17:31:25 +00:00
} else {
return attsToMatrix ( data ) ;
}
}
2011-06-04 21:32:52 +00:00
/**
\ return Path specified by given data or by referenced dictionary
*/
static QPainterPath parseRscRefPath ( const QString & data )
{
2016-07-11 19:59:14 +00:00
if ( data [ 0 ] = = QLatin1Char ( ' { ' ) ) {
2011-06-04 21:32:52 +00:00
// TODO
2014-09-11 19:12:27 +00:00
qCWarning ( OkularXpsDebug ) < < " Reference " < < data ;
2011-06-04 21:32:52 +00:00
return QPainterPath ( ) ;
} else {
return parseAbbreviatedPathData ( data ) ;
}
}
2008-03-08 23:11:52 +00:00
/**
\ return The path of the entry
*/
static QString entryPath ( const QString & entry )
{
2018-04-08 10:50:46 +00:00
const QChar slash = QChar : : fromLatin1 ( ' / ' ) ;
const int index = entry . lastIndexOf ( slash ) ;
QString ret = entry . mid ( 0 , index ) ;
2008-03-08 23:11:52 +00:00
if ( index > 0 ) {
2018-04-08 10:50:46 +00:00
ret . append ( slash ) ;
2008-03-08 23:11:52 +00:00
}
2018-04-08 10:50:46 +00:00
if ( ! ret . startsWith ( slash ) )
ret . prepend ( slash ) ;
2008-03-08 23:11:52 +00:00
return ret ;
}
/**
\ return The path of the entry
*/
static QString entryPath ( const KZipFileEntry * entry )
{
return entryPath ( entry - > path ( ) ) ;
}
/**
\ return The absolute path of the \ p location , according to \ p path if it ' s non - absolute
*/
static QString absolutePath ( const QString & path , const QString & location )
{
2008-06-27 23:10:18 +00:00
QString retPath ;
2015-01-29 19:55:57 +00:00
if ( location . startsWith ( QLatin1Char ( ' / ' ) ) ) {
2008-03-08 23:11:52 +00:00
// already absolute
2008-06-27 23:10:18 +00:00
retPath = location ;
} else {
2016-10-29 16:39:24 +00:00
QUrl u = QUrl : : fromLocalFile ( path ) ;
2018-02-14 00:40:42 +00:00
QUrl u2 = QUrl ( location ) ;
2016-10-29 16:39:24 +00:00
retPath = u . resolved ( u2 ) . toDisplayString ( QUrl : : PreferLocalFile ) ;
2008-03-08 23:11:52 +00:00
}
2008-06-27 23:10:18 +00:00
// it seems paths & file names can also be percent-encoded
// (XPS won't ever finish surprising me)
if ( retPath . contains ( QLatin1Char ( ' % ' ) ) ) {
retPath = QUrl : : fromPercentEncoding ( retPath . toUtf8 ( ) ) ;
}
return retPath ;
2008-03-08 23:11:52 +00:00
}
2008-03-09 00:54:49 +00:00
/**
Read the content of an archive entry in both the cases :
a ) single file
+ foobar
b ) directory
+ foobar /
+ [ 0 ] . piece
+ [ 1 ] . piece
+ . . .
+ [ x ] . last . piece
\ see XPS specification 10.1 .2
*/
2017-09-05 21:27:18 +00:00
static QByteArray readFileOrDirectoryParts ( const KArchiveEntry * entry , QString * pathOfFile = nullptr )
2008-03-09 00:54:49 +00:00
{
QByteArray data ;
if ( entry - > isDirectory ( ) ) {
const KArchiveDirectory * relDir = static_cast < const KArchiveDirectory * > ( entry ) ;
QStringList entries = relDir - > entries ( ) ;
2019-03-12 12:13:53 +00:00
std : : sort ( entries . begin ( ) , entries . end ( ) ) ;
2019-12-23 06:06:25 +00:00
for ( const QString & entry : qAsConst ( entries ) ) {
2008-03-09 00:54:49 +00:00
const KArchiveEntry * relSubEntry = relDir - > entry ( entry ) ;
if ( ! relSubEntry - > isFile ( ) )
continue ;
const KZipFileEntry * relSubFile = static_cast < const KZipFileEntry * > ( relSubEntry ) ;
data . append ( relSubFile - > data ( ) ) ;
}
} else {
const KZipFileEntry * relFile = static_cast < const KZipFileEntry * > ( entry ) ;
data . append ( relFile - > data ( ) ) ;
if ( pathOfFile ) {
* pathOfFile = entryPath ( relFile ) ;
}
}
return data ;
}
2008-06-28 10:15:43 +00:00
/**
Load the resource \ p fileName from the specified \ p archive using the case sensitivity \ p cs
*/
2011-06-04 18:05:06 +00:00
static const KArchiveEntry * loadEntry ( KZip * archive , const QString & fileName , Qt : : CaseSensitivity cs )
2008-06-28 10:15:43 +00:00
{
// first attempt: loading the entry straight as requested
2011-06-04 18:05:06 +00:00
const KArchiveEntry * entry = archive - > directory ( ) - > entry ( fileName ) ;
2008-06-28 10:15:43 +00:00
// in case sensitive mode, or if we actually found something, return what we found
if ( cs = = Qt : : CaseSensitive | | entry ) {
return entry ;
}
QString path ;
QString entryName ;
const int index = fileName . lastIndexOf ( QChar : : fromLatin1 ( ' / ' ) ) ;
if ( index > 0 ) {
path = fileName . left ( index ) ;
entryName = fileName . mid ( index + 1 ) ;
} else {
2016-07-11 19:59:14 +00:00
path = QLatin1Char ( ' / ' ) ;
2008-06-28 10:15:43 +00:00
entryName = fileName ;
}
const KArchiveEntry * newEntry = archive - > directory ( ) - > entry ( path ) ;
if ( newEntry - > isDirectory ( ) ) {
const KArchiveDirectory * relDir = static_cast < const KArchiveDirectory * > ( newEntry ) ;
QStringList relEntries = relDir - > entries ( ) ;
2019-03-12 12:13:53 +00:00
std : : sort ( relEntries . begin ( ) , relEntries . end ( ) ) ;
2019-12-23 06:06:25 +00:00
for ( const QString & relEntry : qAsConst ( relEntries ) ) {
2008-06-28 10:15:43 +00:00
if ( relEntry . compare ( entryName , Qt : : CaseInsensitive ) = = 0 ) {
2011-06-04 18:05:06 +00:00
return relDir - > entry ( relEntry ) ;
2008-06-28 10:15:43 +00:00
}
}
}
2017-09-05 21:27:18 +00:00
return nullptr ;
2008-06-28 10:15:43 +00:00
}
2011-06-04 18:05:06 +00:00
static const KZipFileEntry * loadFile ( KZip * archive , const QString & fileName , Qt : : CaseSensitivity cs )
{
const KArchiveEntry * entry = loadEntry ( archive , fileName , cs ) ;
2017-09-05 21:27:18 +00:00
return entry - > isFile ( ) ? static_cast < const KZipFileEntry * > ( entry ) : nullptr ;
2011-06-04 18:05:06 +00:00
}
2008-03-09 14:29:28 +00:00
/**
\ return The name of a resource from the \ p fileName
*/
static QString resourceName ( const QString & fileName )
{
QString resource = fileName ;
const int slashPos = fileName . lastIndexOf ( QLatin1Char ( ' / ' ) ) ;
const int dotPos = fileName . lastIndexOf ( QLatin1Char ( ' . ' ) ) ;
if ( slashPos > - 1 ) {
if ( dotPos > - 1 & & dotPos > slashPos ) {
resource = fileName . mid ( slashPos + 1 , dotPos - slashPos - 1 ) ;
} else {
resource = fileName . mid ( slashPos + 1 ) ;
}
}
return resource ;
}
2008-06-27 17:18:41 +00:00
static QColor interpolatedColor ( const QColor & c1 , const QColor & c2 )
{
QColor res ;
res . setAlpha ( ( c1 . alpha ( ) + c2 . alpha ( ) ) / 2 ) ;
res . setRed ( ( c1 . red ( ) + c2 . red ( ) ) / 2 ) ;
res . setGreen ( ( c1 . green ( ) + c2 . green ( ) ) / 2 ) ;
res . setBlue ( ( c1 . blue ( ) + c2 . blue ( ) ) / 2 ) ;
return res ;
}
static bool xpsGradientLessThan ( const XpsGradient & g1 , const XpsGradient & g2 )
{
return qFuzzyCompare ( g1 . offset , g2 . offset ) ? g1 . color . name ( ) < g2 . color . name ( ) : g1 . offset < g2 . offset ;
}
static int xpsGradientWithOffset ( const QList < XpsGradient > & gradients , double offset )
{
int i = 0 ;
2019-12-23 06:06:25 +00:00
for ( const XpsGradient & grad : gradients ) {
2008-06-27 17:18:41 +00:00
if ( grad . offset = = offset ) {
return i ;
}
+ + i ;
}
return - 1 ;
}
/**
Preprocess a list of gradients .
\ see XPS specification 11.3 .1 .1
*/
static void preprocessXpsGradients ( QList < XpsGradient > & gradients )
{
if ( gradients . isEmpty ( ) )
return ;
// sort the gradients (case 1.)
2019-03-12 12:15:34 +00:00
std : : stable_sort ( gradients . begin ( ) , gradients . end ( ) , xpsGradientLessThan ) ;
2008-06-27 17:18:41 +00:00
// no gradient with stop 0.0 (case 2.)
if ( xpsGradientWithOffset ( gradients , 0.0 ) = = - 1 ) {
int firstGreaterThanZero = 0 ;
while ( firstGreaterThanZero < gradients . count ( ) & & gradients . at ( firstGreaterThanZero ) . offset < 0.0 )
+ + firstGreaterThanZero ;
// case 2.a: no gradients with stop less than 0.0
if ( firstGreaterThanZero = = 0 ) {
gradients . prepend ( XpsGradient ( 0.0 , gradients . first ( ) . color ) ) ;
}
// case 2.b: some gradients with stop more than 0.0
2008-06-27 21:18:09 +00:00
else if ( firstGreaterThanZero ! = gradients . count ( ) ) {
2008-06-27 17:18:41 +00:00
QColor col1 = gradients . at ( firstGreaterThanZero - 1 ) . color ;
QColor col2 = gradients . at ( firstGreaterThanZero ) . color ;
for ( int i = 0 ; i < firstGreaterThanZero ; + + i ) {
gradients . removeFirst ( ) ;
}
gradients . prepend ( XpsGradient ( 0.0 , interpolatedColor ( col1 , col2 ) ) ) ;
}
// case 2.c: no gradients with stop more than 0.0
else {
XpsGradient newGrad ( 0.0 , gradients . last ( ) . color ) ;
gradients . clear ( ) ;
gradients . append ( newGrad ) ;
}
}
if ( gradients . isEmpty ( ) )
return ;
// no gradient with stop 1.0 (case 3.)
if ( xpsGradientWithOffset ( gradients , 1.0 ) = = - 1 ) {
int firstLessThanOne = gradients . count ( ) - 1 ;
while ( firstLessThanOne > = 0 & & gradients . at ( firstLessThanOne ) . offset > 1.0 )
- - firstLessThanOne ;
// case 2.a: no gradients with stop greater than 1.0
if ( firstLessThanOne = = gradients . count ( ) - 1 ) {
gradients . append ( XpsGradient ( 1.0 , gradients . last ( ) . color ) ) ;
}
// case 2.b: some gradients with stop more than 1.0
2008-06-27 21:18:09 +00:00
else if ( firstLessThanOne ! = - 1 ) {
2008-06-27 17:18:41 +00:00
QColor col1 = gradients . at ( firstLessThanOne ) . color ;
QColor col2 = gradients . at ( firstLessThanOne + 1 ) . color ;
for ( int i = firstLessThanOne + 1 ; i < gradients . count ( ) ; + + i ) {
gradients . removeLast ( ) ;
}
gradients . append ( XpsGradient ( 1.0 , interpolatedColor ( col1 , col2 ) ) ) ;
}
// case 2.c: no gradients with stop less than 1.0
else {
XpsGradient newGrad ( 1.0 , gradients . first ( ) . color ) ;
gradients . clear ( ) ;
gradients . append ( newGrad ) ;
}
}
}
static void addXpsGradientsToQGradient ( const QList < XpsGradient > & gradients , QGradient * qgrad )
{
2019-12-23 06:06:25 +00:00
for ( const XpsGradient & grad : gradients ) {
2008-06-27 17:18:41 +00:00
qgrad - > setColorAt ( grad . offset , grad . color ) ;
}
}
2008-06-27 21:02:36 +00:00
static void applySpreadStyleToQGradient ( const QString & style , QGradient * qgrad )
{
if ( style . isEmpty ( ) )
return ;
if ( style = = QLatin1String ( " Pad " ) ) {
qgrad - > setSpread ( QGradient : : PadSpread ) ;
} else if ( style = = QLatin1String ( " Reflect " ) ) {
qgrad - > setSpread ( QGradient : : ReflectSpread ) ;
} else if ( style = = QLatin1String ( " Repeat " ) ) {
qgrad - > setSpread ( QGradient : : RepeatSpread ) ;
}
}
2008-09-03 22:58:52 +00:00
/**
Read an UnicodeString
2018-10-18 19:04:49 +00:00
\ param raw the raw value of UnicodeString
2008-09-03 22:58:52 +00:00
\ see XPS specification 5.1 .4
*/
static QString unicodeString ( const QString & raw )
{
QString ret ;
if ( raw . startsWith ( QLatin1String ( " {} " ) ) ) {
ret = raw . mid ( 2 ) ;
2008-09-04 17:01:03 +00:00
} else {
ret = raw ;
2008-09-03 22:58:52 +00:00
}
return ret ;
}
2006-09-26 11:53:27 +00:00
XpsHandler : : XpsHandler ( XpsPage * page )
: m_page ( page )
2006-09-21 11:41:46 +00:00
{
2017-09-05 21:27:18 +00:00
m_painter = nullptr ;
2006-09-21 11:41:46 +00:00
}
XpsHandler : : ~ XpsHandler ( )
2007-02-09 08:11:47 +00:00
{
}
2006-09-21 11:41:46 +00:00
bool XpsHandler : : startDocument ( )
{
2014-09-11 19:12:27 +00:00
qCWarning ( OkularXpsDebug ) < < " start document " < < m_page - > m_fileName ;
2007-02-10 17:31:25 +00:00
XpsRenderNode node ;
2015-10-29 12:37:11 +00:00
node . name = QStringLiteral ( " document " ) ;
2007-02-10 17:31:25 +00:00
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 ;
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 ) {
2014-09-11 19:12:27 +00:00
qCWarning ( OkularXpsDebug ) < < " Name doesn't match " ;
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 )
{
2009-05-24 04:40:10 +00:00
// TODO Currently ignored attributes: CaretStops, DeviceFontName, IsSideways, OpacityMask, Name, FixedPage.NavigateURI, xml:lang, x:key
// TODO Indices is only partially implemented
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.
2015-10-29 12:37:11 +00:00
float fontSize = node . attributes . value ( QStringLiteral ( " FontRenderingEmSize " ) ) . toFloat ( ) ;
2014-09-11 19:12:27 +00:00
// qCWarning(OkularXpsDebug) << "Font Rendering EmSize:" << fontSize;
2008-06-27 22:18:00 +00:00
// a value of 0.0 means the text is not visible (see XPS specs, chapter 12, "Glyphs")
if ( fontSize < 0.1 ) {
m_painter - > restore ( ) ;
return ;
}
2018-04-08 10:50:46 +00:00
const QString absoluteFileName = absolutePath ( entryPath ( m_page - > fileName ( ) ) , node . attributes . value ( QStringLiteral ( " FontUri " ) ) ) ;
QFont font = m_page - > m_file - > getFontByName ( absoluteFileName , fontSize ) ;
2015-10-29 12:37:11 +00:00
att = node . attributes . value ( QStringLiteral ( " StyleSimulations " ) ) ;
2008-06-27 20:35:55 +00:00
if ( ! att . isEmpty ( ) ) {
if ( att = = QLatin1String ( " ItalicSimulation " ) ) {
font . setItalic ( true ) ;
} else if ( att = = QLatin1String ( " BoldSimulation " ) ) {
font . setBold ( true ) ;
} else if ( att = = QLatin1String ( " BoldItalicSimulation " ) ) {
font . setItalic ( true ) ;
font . setBold ( true ) ;
}
}
2007-02-10 17:31:25 +00:00
m_painter - > setFont ( font ) ;
// Origin
2015-10-29 12:37:11 +00:00
QPointF origin ( node . attributes . value ( QStringLiteral ( " OriginX " ) ) . toDouble ( ) , node . attributes . value ( QStringLiteral ( " OriginY " ) ) . toDouble ( ) ) ;
2007-02-10 17:31:25 +00:00
// Fill
QBrush brush ;
2015-10-29 12:37:11 +00:00
att = node . attributes . value ( QStringLiteral ( " Fill " ) ) ;
2007-02-10 17:31:25 +00:00
if ( att . isEmpty ( ) ) {
2015-10-29 12:37:11 +00:00
QVariant data = node . getChildData ( QStringLiteral ( " Glyphs.Fill " ) ) ;
2008-09-04 17:30:56 +00:00
if ( data . canConvert < QBrush > ( ) ) {
brush = data . value < QBrush > ( ) ;
2007-02-10 17:31:25 +00:00
} else {
2008-06-27 23:44:33 +00:00
// no "Fill" attribute and no "Glyphs.Fill" child, so show nothing
// (see XPS specs, 5.10)
m_painter - > restore ( ) ;
return ;
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 ) ;
2008-06-27 23:44:33 +00:00
if ( brush . style ( ) > Qt : : NoBrush & & brush . style ( ) < Qt : : LinearGradientPattern & & brush . color ( ) . alpha ( ) = = 0 ) {
m_painter - > restore ( ) ;
return ;
}
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
2015-10-29 12:37:11 +00:00
att = node . attributes . value ( QStringLiteral ( " Opacity " ) ) ;
2007-02-17 09:42:15 +00:00
if ( ! att . isEmpty ( ) ) {
2008-06-27 23:44:33 +00:00
bool ok = true ;
double value = att . toDouble ( & ok ) ;
if ( ok & & value > = 0.1 ) {
m_painter - > setOpacity ( value ) ;
} else {
m_painter - > restore ( ) ;
return ;
}
2007-02-17 09:42:15 +00:00
}
2007-02-10 17:31:25 +00:00
// RenderTransform
2015-10-29 12:37:11 +00:00
att = node . attributes . value ( QStringLiteral ( " RenderTransform " ) ) ;
2007-02-10 17:31:25 +00:00
if ( ! att . isEmpty ( ) ) {
2012-09-08 15:06:02 +00:00
m_painter - > setWorldTransform ( parseRscRefMatrix ( att ) , true ) ;
2007-02-10 17:31:25 +00:00
}
2008-06-29 04:26:09 +00:00
// Clip
2015-10-29 12:37:11 +00:00
att = node . attributes . value ( QStringLiteral ( " Clip " ) ) ;
2008-06-29 04:26:09 +00:00
if ( ! att . isEmpty ( ) ) {
2011-06-04 21:32:52 +00:00
QPainterPath clipPath = parseRscRefPath ( att ) ;
2008-06-29 04:26:09 +00:00
if ( ! clipPath . isEmpty ( ) ) {
m_painter - > setClipPath ( clipPath ) ;
}
}
2009-05-24 04:40:10 +00:00
// BiDiLevel - default Left-to-Right
m_painter - > setLayoutDirection ( Qt : : LeftToRight ) ;
2015-10-29 12:37:11 +00:00
att = node . attributes . value ( QStringLiteral ( " BiDiLevel " ) ) ;
2009-05-24 04:40:10 +00:00
if ( ! att . isEmpty ( ) ) {
if ( ( att . toInt ( ) % 2 ) = = 1 ) {
// odd BiDiLevel, so Right-to-Left
m_painter - > setLayoutDirection ( Qt : : RightToLeft ) ;
}
}
// Indices - partial handling only
2015-10-29 12:37:11 +00:00
att = node . attributes . value ( QStringLiteral ( " Indices " ) ) ;
2009-05-24 04:40:10 +00:00
QList < qreal > advanceWidths ;
if ( ! att . isEmpty ( ) ) {
2016-07-11 19:59:14 +00:00
QStringList indicesElements = att . split ( QLatin1Char ( ' ; ' ) ) ;
2009-05-24 04:40:10 +00:00
for ( int i = 0 ; i < indicesElements . size ( ) ; + + i ) {
2015-10-29 12:37:11 +00:00
if ( indicesElements . at ( i ) . contains ( QStringLiteral ( " , " ) ) ) {
2016-07-11 19:59:14 +00:00
QStringList parts = indicesElements . at ( i ) . split ( QLatin1Char ( ' , ' ) ) ;
2009-05-24 04:40:10 +00:00
if ( parts . size ( ) = = 2 ) {
// regular advance case, no offsets
advanceWidths . append ( parts . at ( 1 ) . toDouble ( ) * fontSize / 100.0 ) ;
} else if ( parts . size ( ) = = 3 ) {
// regular advance case, with uOffset
qreal AdvanceWidth = parts . at ( 1 ) . toDouble ( ) * fontSize / 100.0 ;
qreal uOffset = parts . at ( 2 ) . toDouble ( ) / 100.0 ;
advanceWidths . append ( AdvanceWidth + uOffset ) ;
} else {
// has vertical offset, but don't know how to handle that yet
2014-09-11 19:12:27 +00:00
qCWarning ( OkularXpsDebug ) < < " Unhandled Indices element: " < < indicesElements . at ( i ) ;
2009-05-24 04:40:10 +00:00
advanceWidths . append ( - 1.0 ) ;
}
} else {
// no special advance case
advanceWidths . append ( - 1.0 ) ;
}
}
}
// UnicodeString
2015-10-29 12:37:11 +00:00
QString stringToDraw ( unicodeString ( node . attributes . value ( QStringLiteral ( " UnicodeString " ) ) ) ) ;
2009-05-24 04:40:10 +00:00
QPointF originAdvance ( 0 , 0 ) ;
QFontMetrics metrics = m_painter - > fontMetrics ( ) ;
for ( int i = 0 ; i < stringToDraw . size ( ) ; + + i ) {
QChar thisChar = stringToDraw . at ( i ) ;
m_painter - > drawText ( origin + originAdvance , QString ( thisChar ) ) ;
const qreal advanceWidth = advanceWidths . value ( i , qreal ( - 1.0 ) ) ;
if ( advanceWidth > 0.0 ) {
originAdvance . rx ( ) + = advanceWidth ;
} else {
2020-09-24 14:21:28 +00:00
originAdvance . rx ( ) + = metrics . horizontalAdvance ( thisChar ) ;
2009-05-24 04:40:10 +00:00
}
}
2014-09-11 19:12:27 +00:00
// qCWarning(OkularXpsDebug) << "Glyphs: " << atts.value("Fill") << ", " << atts.value("FontUri");
// qCWarning(OkularXpsDebug) << " Origin: " << atts.value("OriginX") << "," << atts.value("OriginY");
// qCWarning(OkularXpsDebug) << " Unicode: " << atts.value("UnicodeString");
2007-02-17 09:42:15 +00:00
2007-02-10 17:31:25 +00:00
m_painter - > restore ( ) ;
}
void XpsHandler : : processFill ( XpsRenderNode & node )
{
2008-06-28 11:34:51 +00:00
// TODO Ignored child elements: VirtualBrush
2007-02-17 09:42:15 +00:00
2007-02-13 21:13:08 +00:00
if ( node . children . size ( ) ! = 1 ) {
2014-09-11 19:12:27 +00:00
qCWarning ( OkularXpsDebug ) < < " Fill element should have exactly one child " ;
2007-02-13 21:13:08 +00:00
} else {
node . data = node . children [ 0 ] . data ;
2007-02-10 17:31:25 +00:00
}
}
2008-06-28 10:58:57 +00:00
void XpsHandler : : processStroke ( XpsRenderNode & node )
{
2008-06-28 11:34:51 +00:00
// TODO Ignored child elements: VirtualBrush
2008-06-28 10:58:57 +00:00
if ( node . children . size ( ) ! = 1 ) {
2014-09-11 19:12:27 +00:00
qCWarning ( OkularXpsDebug ) < < " Stroke element should have exactly one child " ;
2008-06-28 10:58:57 +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
2015-10-29 12:37:11 +00:00
QRectF viewport = stringToRectF ( node . attributes . value ( QStringLiteral ( " Viewport " ) ) ) ;
QRectF viewbox = stringToRectF ( node . attributes . value ( QStringLiteral ( " Viewbox " ) ) ) ;
QImage image = m_page - > loadImageFromFile ( node . attributes . value ( QStringLiteral ( " 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
2012-09-08 15:06:02 +00:00
QTransform viewboxMatrix = QTransform ( viewbox . width ( ) * image . physicalDpiX ( ) / 96 , 0 , 0 , viewbox . height ( ) * image . physicalDpiY ( ) / 96 , viewbox . x ( ) , viewbox . y ( ) ) ;
2007-02-12 13:31:16 +00:00
// Matrix which can transform [0, 0, 1, 1] rectangle to given viewport
// TODO Take ViewPort into account
2012-09-08 15:06:02 +00:00
QTransform viewportMatrix ;
2015-10-29 12:37:11 +00:00
att = node . attributes . value ( QStringLiteral ( " Transform " ) ) ;
2007-02-12 13:31:16 +00:00
if ( att . isEmpty ( ) ) {
2015-10-29 12:37:11 +00:00
QVariant data = node . getChildData ( QStringLiteral ( " ImageBrush.Transform " ) ) ;
2012-09-08 15:06:02 +00:00
if ( data . canConvert < QTransform > ( ) ) {
viewportMatrix = data . value < QTransform > ( ) ;
2007-02-12 13:31:16 +00:00
} else {
2012-09-08 15:06:02 +00:00
viewportMatrix = QTransform ( ) ;
2007-02-12 13:31:16 +00:00
}
} else {
viewportMatrix = parseRscRefMatrix ( att ) ;
}
2012-09-08 15:06:02 +00:00
viewportMatrix = viewportMatrix * QTransform ( viewport . width ( ) , 0 , 0 , viewport . height ( ) , viewport . x ( ) , viewport . y ( ) ) ;
2007-02-17 09:42:15 +00:00
2008-06-27 12:38:06 +00:00
brush = QBrush ( image ) ;
2012-09-08 15:06:02 +00:00
brush . setTransform ( viewboxMatrix . inverted ( ) * viewportMatrix ) ;
2007-02-12 13:31:16 +00:00
2019-09-18 11:41:19 +00:00
node . data = QVariant : : fromValue ( brush ) ;
2007-02-10 17:31:25 +00:00
}
void XpsHandler : : processPath ( XpsRenderNode & node )
{
2008-06-27 19:50:44 +00:00
// TODO Ignored attributes: Clip, OpacityMask, StrokeEndLineCap, StorkeStartLineCap, Name, FixedPage.NavigateURI, xml:lang, x:key, AutomationProperties.Name, AutomationProperties.HelpText, SnapsToDevicePixels
2008-06-29 02:15:17 +00:00
// TODO Ignored child elements: RenderTransform, Clip, OpacityMask
2007-02-10 17:31:25 +00:00
// Handled separately: RenderTransform
m_painter - > save ( ) ;
QString att ;
2008-09-04 17:30:56 +00:00
QVariant data ;
2007-02-10 17:31:25 +00:00
// Get path
2015-10-29 12:37:11 +00:00
XpsPathGeometry * pathdata = node . getChildData ( QStringLiteral ( " Path.Data " ) ) . value < XpsPathGeometry * > ( ) ;
att = node . attributes . value ( QStringLiteral ( " Data " ) ) ;
2007-02-10 17:31:25 +00:00
if ( ! att . isEmpty ( ) ) {
2011-06-04 21:32:52 +00:00
QPainterPath path = parseRscRefPath ( att ) ;
2008-06-29 02:15:17 +00:00
delete pathdata ;
pathdata = new XpsPathGeometry ( ) ;
pathdata - > paths . append ( new XpsPathFigure ( path , true ) ) ;
}
if ( ! pathdata ) {
// nothing to draw
m_painter - > restore ( ) ;
return ;
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
2015-10-29 12:37:11 +00:00
att = node . attributes . value ( QStringLiteral ( " Fill " ) ) ;
2007-02-10 17:31:25 +00:00
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 {
2015-10-29 12:37:11 +00:00
data = node . getChildData ( QStringLiteral ( " Path.Fill " ) ) ;
2008-09-04 17:30:56 +00:00
if ( data . canConvert < QBrush > ( ) ) {
brush = data . value < QBrush > ( ) ;
2007-02-10 17:31:25 +00:00
}
}
m_painter - > setBrush ( brush ) ;
2007-02-17 10:45:07 +00:00
// Stroke (pen)
2015-10-29 12:37:11 +00:00
att = node . attributes . value ( QStringLiteral ( " Stroke " ) ) ;
2007-02-17 10:45:07 +00:00
QPen pen ( Qt : : transparent ) ;
if ( ! att . isEmpty ( ) ) {
2007-02-17 11:18:06 +00:00
pen = parseRscRefColorForPen ( att ) ;
2008-06-28 10:58:57 +00:00
} else {
2015-10-29 12:37:11 +00:00
data = node . getChildData ( QStringLiteral ( " Path.Stroke " ) ) ;
2008-09-04 17:30:56 +00:00
if ( data . canConvert < QBrush > ( ) ) {
pen . setBrush ( data . value < QBrush > ( ) ) ;
2008-06-28 10:58:57 +00:00
}
2007-02-17 10:45:07 +00:00
}
2015-10-29 12:37:11 +00:00
att = node . attributes . value ( QStringLiteral ( " StrokeThickness " ) ) ;
2007-02-17 10:45:07 +00:00
if ( ! att . isEmpty ( ) ) {
bool ok = false ;
int thickness = att . toInt ( & ok ) ;
if ( ok )
pen . setWidth ( thickness ) ;
}
2015-10-29 12:37:11 +00:00
att = node . attributes . value ( QStringLiteral ( " StrokeDashArray " ) ) ;
2008-06-27 19:40:14 +00:00
if ( ! att . isEmpty ( ) ) {
const QStringList pieces = att . split ( QLatin1Char ( ' ' ) , QString : : SkipEmptyParts ) ;
QVector < qreal > dashPattern ( pieces . count ( ) ) ;
bool ok = false ;
for ( int i = 0 ; i < pieces . count ( ) ; + + i ) {
2008-07-31 09:26:11 +00:00
qreal value = pieces . at ( i ) . toInt ( & ok ) ;
2008-06-27 19:40:14 +00:00
if ( ok ) {
dashPattern [ i ] = value ;
} else {
break ;
}
}
if ( ok ) {
pen . setDashPattern ( dashPattern ) ;
}
}
2015-10-29 12:37:11 +00:00
att = node . attributes . value ( QStringLiteral ( " StrokeDashOffset " ) ) ;
2008-06-27 19:40:14 +00:00
if ( ! att . isEmpty ( ) ) {
bool ok = false ;
int offset = att . toInt ( & ok ) ;
if ( ok )
pen . setDashOffset ( offset ) ;
}
2015-10-29 12:37:11 +00:00
att = node . attributes . value ( QStringLiteral ( " StrokeDashCap " ) ) ;
2008-06-27 19:40:14 +00:00
if ( ! att . isEmpty ( ) ) {
Qt : : PenCapStyle cap = Qt : : FlatCap ;
if ( att = = QLatin1String ( " Flat " ) ) {
cap = Qt : : FlatCap ;
} else if ( att = = QLatin1String ( " Round " ) ) {
cap = Qt : : RoundCap ;
} else if ( att = = QLatin1String ( " Square " ) ) {
cap = Qt : : SquareCap ;
}
// ### missing "Triangle"
pen . setCapStyle ( cap ) ;
}
2015-10-29 12:37:11 +00:00
att = node . attributes . value ( QStringLiteral ( " StrokeLineJoin " ) ) ;
2008-06-27 19:50:44 +00:00
if ( ! att . isEmpty ( ) ) {
Qt : : PenJoinStyle joinStyle = Qt : : MiterJoin ;
if ( att = = QLatin1String ( " Miter " ) ) {
joinStyle = Qt : : MiterJoin ;
} else if ( att = = QLatin1String ( " Bevel " ) ) {
joinStyle = Qt : : BevelJoin ;
} else if ( att = = QLatin1String ( " Round " ) ) {
joinStyle = Qt : : RoundJoin ;
}
pen . setJoinStyle ( joinStyle ) ;
}
2015-10-29 12:37:11 +00:00
att = node . attributes . value ( QStringLiteral ( " StrokeMiterLimit " ) ) ;
2008-06-27 19:50:44 +00:00
if ( ! att . isEmpty ( ) ) {
bool ok = false ;
double limit = att . toDouble ( & ok ) ;
if ( ok ) {
// we have to divide it by two, as XPS consider half of the stroke width,
// while Qt the whole of it
pen . setMiterLimit ( limit / 2 ) ;
}
}
2007-02-17 10:45:07 +00:00
m_painter - > setPen ( pen ) ;
2007-02-10 17:31:25 +00:00
2007-02-17 09:42:15 +00:00
// Opacity
2015-10-29 12:37:11 +00:00
att = node . attributes . value ( QStringLiteral ( " Opacity " ) ) ;
2007-02-17 09:42:15 +00:00
if ( ! att . isEmpty ( ) ) {
m_painter - > setOpacity ( att . toDouble ( ) ) ;
}
2007-02-10 17:31:25 +00:00
// RenderTransform
2015-10-29 12:37:11 +00:00
att = node . attributes . value ( QStringLiteral ( " RenderTransform " ) ) ;
2007-02-10 17:31:25 +00:00
if ( ! att . isEmpty ( ) ) {
2012-09-08 15:06:02 +00:00
m_painter - > setWorldTransform ( parseRscRefMatrix ( att ) , true ) ;
2007-02-10 17:31:25 +00:00
}
2008-06-29 03:09:08 +00:00
if ( ! pathdata - > transform . isIdentity ( ) ) {
2012-09-08 15:06:02 +00:00
m_painter - > setWorldTransform ( pathdata - > transform , true ) ;
2008-06-29 03:09:08 +00:00
}
2007-02-10 17:31:25 +00:00
2019-12-23 06:06:25 +00:00
for ( const XpsPathFigure * figure : qAsConst ( pathdata - > paths ) ) {
2008-06-29 02:15:17 +00:00
m_painter - > setBrush ( figure - > isFilled ? brush : QBrush ( ) ) ;
m_painter - > drawPath ( figure - > path ) ;
}
delete pathdata ;
2007-02-10 17:31:25 +00:00
m_painter - > restore ( ) ;
}
2008-06-29 02:15:17 +00:00
void XpsHandler : : processPathData ( XpsRenderNode & node )
{
if ( node . children . size ( ) ! = 1 ) {
2014-09-11 19:12:27 +00:00
qCWarning ( OkularXpsDebug ) < < " Path.Data element should have exactly one child " ;
2008-06-29 02:15:17 +00:00
} else {
node . data = node . children [ 0 ] . data ;
}
}
void XpsHandler : : processPathGeometry ( XpsRenderNode & node )
{
XpsPathGeometry * geom = new XpsPathGeometry ( ) ;
2019-12-23 06:06:25 +00:00
for ( const XpsRenderNode & child : qAsConst ( node . children ) ) {
2008-09-04 17:30:56 +00:00
if ( child . data . canConvert < XpsPathFigure * > ( ) ) {
XpsPathFigure * figure = child . data . value < XpsPathFigure * > ( ) ;
2008-06-29 02:15:17 +00:00
geom - > paths . append ( figure ) ;
}
}
QString att ;
2015-10-29 12:37:11 +00:00
att = node . attributes . value ( QStringLiteral ( " Figures " ) ) ;
2008-06-29 02:15:17 +00:00
if ( ! att . isEmpty ( ) ) {
2011-06-04 21:32:52 +00:00
QPainterPath path = parseRscRefPath ( att ) ;
2008-06-29 02:15:17 +00:00
qDeleteAll ( geom - > paths ) ;
geom - > paths . clear ( ) ;
geom - > paths . append ( new XpsPathFigure ( path , true ) ) ;
}
2015-10-29 12:37:11 +00:00
att = node . attributes . value ( QStringLiteral ( " FillRule " ) ) ;
2008-06-29 02:15:17 +00:00
if ( ! att . isEmpty ( ) ) {
geom - > fillRule = fillRuleFromString ( att ) ;
}
2008-06-29 03:09:08 +00:00
// Transform
2015-10-29 12:37:11 +00:00
att = node . attributes . value ( QStringLiteral ( " Transform " ) ) ;
2008-06-29 03:09:08 +00:00
if ( ! att . isEmpty ( ) ) {
geom - > transform = parseRscRefMatrix ( att ) ;
}
2008-06-29 02:15:17 +00:00
if ( ! geom - > paths . isEmpty ( ) ) {
2019-09-18 11:41:19 +00:00
node . data = QVariant : : fromValue ( geom ) ;
2008-06-29 02:15:17 +00:00
} else {
delete geom ;
}
}
void XpsHandler : : processPathFigure ( XpsRenderNode & node )
{
2008-06-29 10:40:49 +00:00
// TODO Ignored child elements: ArcSegment
2008-06-29 02:15:17 +00:00
QString att ;
QPainterPath path ;
2015-10-29 12:37:11 +00:00
att = node . attributes . value ( QStringLiteral ( " StartPoint " ) ) ;
2008-06-29 02:15:17 +00:00
if ( ! att . isEmpty ( ) ) {
QPointF point = getPointFromString ( att ) ;
path . moveTo ( point ) ;
} else {
return ;
}
2019-12-23 06:06:25 +00:00
for ( const XpsRenderNode & child : qAsConst ( node . children ) ) {
2008-06-29 02:15:17 +00:00
bool isStroked = true ;
2015-10-29 12:37:11 +00:00
att = node . attributes . value ( QStringLiteral ( " IsStroked " ) ) ;
2008-06-29 02:15:17 +00:00
if ( ! att . isEmpty ( ) ) {
isStroked = att = = QLatin1String ( " true " ) ;
}
if ( ! isStroked ) {
continue ;
}
// PolyLineSegment
if ( child . name = = QLatin1String ( " PolyLineSegment " ) ) {
2015-10-29 12:37:11 +00:00
att = child . attributes . value ( QStringLiteral ( " Points " ) ) ;
2008-06-29 02:15:17 +00:00
if ( ! att . isEmpty ( ) ) {
const QStringList points = att . split ( QLatin1Char ( ' ' ) , QString : : SkipEmptyParts ) ;
2019-12-23 06:06:25 +00:00
for ( const QString & p : points ) {
2008-06-29 02:15:17 +00:00
QPointF point = getPointFromString ( p ) ;
path . lineTo ( point ) ;
}
}
}
// PolyBezierSegment
else if ( child . name = = QLatin1String ( " PolyBezierSegment " ) ) {
2015-10-29 12:37:11 +00:00
att = child . attributes . value ( QStringLiteral ( " Points " ) ) ;
2008-06-29 02:15:17 +00:00
if ( ! att . isEmpty ( ) ) {
const QStringList points = att . split ( QLatin1Char ( ' ' ) , QString : : SkipEmptyParts ) ;
if ( points . count ( ) % 3 = = 0 ) {
for ( int i = 0 ; i < points . count ( ) ; ) {
QPointF firstControl = getPointFromString ( points . at ( i + + ) ) ;
QPointF secondControl = getPointFromString ( points . at ( i + + ) ) ;
QPointF endPoint = getPointFromString ( points . at ( i + + ) ) ;
path . cubicTo ( firstControl , secondControl , endPoint ) ;
}
}
}
}
2008-06-29 10:40:49 +00:00
// PolyQuadraticBezierSegment
else if ( child . name = = QLatin1String ( " PolyQuadraticBezierSegment " ) ) {
2015-10-29 12:37:11 +00:00
att = child . attributes . value ( QStringLiteral ( " Points " ) ) ;
2008-06-29 10:40:49 +00:00
if ( ! att . isEmpty ( ) ) {
const QStringList points = att . split ( QLatin1Char ( ' ' ) , QString : : SkipEmptyParts ) ;
if ( points . count ( ) % 2 = = 0 ) {
for ( int i = 0 ; i < points . count ( ) ; ) {
QPointF point1 = getPointFromString ( points . at ( i + + ) ) ;
QPointF point2 = getPointFromString ( points . at ( i + + ) ) ;
path . quadTo ( point1 , point2 ) ;
}
}
}
}
2008-06-29 02:15:17 +00:00
}
bool closePath = false ;
2015-10-29 12:37:11 +00:00
att = node . attributes . value ( QStringLiteral ( " IsClosed " ) ) ;
2008-06-29 02:15:17 +00:00
if ( ! att . isEmpty ( ) ) {
closePath = att = = QLatin1String ( " true " ) ;
}
if ( closePath ) {
path . closeSubpath ( ) ;
}
bool isFilled = true ;
2015-10-29 12:37:11 +00:00
att = node . attributes . value ( QStringLiteral ( " IsFilled " ) ) ;
2008-06-29 02:15:17 +00:00
if ( ! att . isEmpty ( ) ) {
isFilled = att = = QLatin1String ( " true " ) ;
}
if ( ! path . isEmpty ( ) ) {
2019-09-18 11:41:19 +00:00
node . data = QVariant : : fromValue ( new XpsPathFigure ( path , isFilled ) ) ;
2008-06-29 02:15:17 +00:00
}
}
2007-02-10 17:31:25 +00:00
void XpsHandler : : processStartElement ( XpsRenderNode & node )
{
2015-10-29 12:37:11 +00:00
if ( node . name = = QLatin1String ( " Canvas " ) ) {
2007-02-10 17:31:25 +00:00
m_painter - > save ( ) ;
2015-10-29 12:37:11 +00:00
QString att = node . attributes . value ( QStringLiteral ( " RenderTransform " ) ) ;
2008-06-29 03:09:08 +00:00
if ( ! att . isEmpty ( ) ) {
2012-09-08 15:06:02 +00:00
m_painter - > setWorldTransform ( parseRscRefMatrix ( att ) , true ) ;
2008-06-29 03:09:08 +00:00
}
2015-10-29 12:37:11 +00:00
att = node . attributes . value ( QStringLiteral ( " Opacity " ) ) ;
2008-06-29 08:00:17 +00:00
if ( ! att . isEmpty ( ) ) {
double value = att . toDouble ( ) ;
if ( value > 0.0 & & value < = 1.0 ) {
m_painter - > setOpacity ( m_painter - > opacity ( ) * value ) ;
} else {
// setting manually to 0 is necessary to "disable"
// all the stuff inside
m_painter - > setOpacity ( 0.0 ) ;
}
}
2007-02-10 17:31:25 +00:00
}
}
void XpsHandler : : processEndElement ( XpsRenderNode & node )
{
2015-10-29 12:37:11 +00:00
if ( node . name = = QLatin1String ( " Glyphs " ) ) {
2007-02-10 17:31:25 +00:00
processGlyph ( node ) ;
2015-10-29 12:37:11 +00:00
} else if ( node . name = = QLatin1String ( " Path " ) ) {
2007-02-10 17:31:25 +00:00
processPath ( node ) ;
2015-10-29 12:37:11 +00:00
} else if ( node . name = = QLatin1String ( " MatrixTransform " ) ) {
2007-02-10 17:31:25 +00:00
// TODO Ignoring x:key
2019-09-18 11:41:19 +00:00
node . data = QVariant : : fromValue ( QTransform ( attsToMatrix ( node . attributes . value ( QStringLiteral ( " Matrix " ) ) ) ) ) ;
2015-10-29 12:37:11 +00:00
} else if ( ( node . name = = QLatin1String ( " Canvas.RenderTransform " ) ) | | ( node . name = = QLatin1String ( " Glyphs.RenderTransform " ) ) | | ( node . name = = QLatin1String ( " Path.RenderTransform " ) ) ) {
QVariant data = node . getRequiredChildData ( QStringLiteral ( " MatrixTransform " ) ) ;
2012-09-08 15:06:02 +00:00
if ( data . canConvert < QTransform > ( ) ) {
m_painter - > setWorldTransform ( data . value < QTransform > ( ) , true ) ;
2007-02-10 17:31:25 +00:00
}
2015-10-29 12:37:11 +00:00
} else if ( node . name = = QLatin1String ( " Canvas " ) ) {
2006-09-21 11:41:46 +00:00
m_painter - > restore ( ) ;
2015-10-29 12:37:11 +00:00
} else if ( ( node . name = = QLatin1String ( " Path.Fill " ) ) | | ( node . name = = QLatin1String ( " Glyphs.Fill " ) ) ) {
2007-02-10 17:31:25 +00:00
processFill ( node ) ;
2015-10-29 12:37:11 +00:00
} else if ( node . name = = QLatin1String ( " Path.Stroke " ) ) {
2008-06-28 10:58:57 +00:00
processStroke ( node ) ;
2015-10-29 12:37:11 +00:00
} else if ( node . name = = QLatin1String ( " SolidColorBrush " ) ) {
2007-02-10 17:31:25 +00:00
// TODO Ignoring opacity, x:key
2019-09-18 11:41:19 +00:00
node . data = QVariant : : fromValue ( QBrush ( QColor ( hexToRgba ( node . attributes . value ( QStringLiteral ( " Color " ) ) . toLatin1 ( ) ) ) ) ) ;
2015-10-29 12:37:11 +00:00
} else if ( node . name = = QLatin1String ( " ImageBrush " ) ) {
2007-02-10 17:31:25 +00:00
processImageBrush ( node ) ;
2015-10-29 12:37:11 +00:00
} else if ( node . name = = QLatin1String ( " ImageBrush.Transform " ) ) {
node . data = node . getRequiredChildData ( QStringLiteral ( " MatrixTransform " ) ) ;
} else if ( node . name = = QLatin1String ( " LinearGradientBrush " ) ) {
2020-01-02 23:16:36 +00:00
const XpsRenderNode * gradients = node . findChild ( QStringLiteral ( " LinearGradientBrush.GradientStops " ) ) ;
2008-09-04 17:30:56 +00:00
if ( gradients & & gradients - > data . canConvert < QGradient * > ( ) ) {
2015-10-29 12:37:11 +00:00
QPointF start = getPointFromString ( node . attributes . value ( QStringLiteral ( " StartPoint " ) ) ) ;
QPointF end = getPointFromString ( node . attributes . value ( QStringLiteral ( " EndPoint " ) ) ) ;
2008-09-04 17:30:56 +00:00
QLinearGradient * qgrad = static_cast < QLinearGradient * > ( gradients - > data . value < QGradient * > ( ) ) ;
2008-06-27 11:52:45 +00:00
qgrad - > setStart ( start ) ;
qgrad - > setFinalStop ( end ) ;
2015-10-29 12:37:11 +00:00
applySpreadStyleToQGradient ( node . attributes . value ( QStringLiteral ( " SpreadMethod " ) ) , qgrad ) ;
2019-09-18 11:41:19 +00:00
node . data = QVariant : : fromValue ( QBrush ( * qgrad ) ) ;
2008-06-27 11:52:45 +00:00
delete qgrad ;
}
2015-10-29 12:37:11 +00:00
} else if ( node . name = = QLatin1String ( " RadialGradientBrush " ) ) {
2020-01-02 23:16:36 +00:00
const XpsRenderNode * gradients = node . findChild ( QStringLiteral ( " RadialGradientBrush.GradientStops " ) ) ;
2008-09-04 17:30:56 +00:00
if ( gradients & & gradients - > data . canConvert < QGradient * > ( ) ) {
2015-10-29 12:37:11 +00:00
QPointF center = getPointFromString ( node . attributes . value ( QStringLiteral ( " Center " ) ) ) ;
QPointF origin = getPointFromString ( node . attributes . value ( QStringLiteral ( " GradientOrigin " ) ) ) ;
double radiusX = node . attributes . value ( QStringLiteral ( " RadiusX " ) ) . toDouble ( ) ;
double radiusY = node . attributes . value ( QStringLiteral ( " RadiusY " ) ) . toDouble ( ) ;
2008-09-04 17:30:56 +00:00
QRadialGradient * qgrad = static_cast < QRadialGradient * > ( gradients - > data . value < QGradient * > ( ) ) ;
2008-06-27 17:18:41 +00:00
qgrad - > setCenter ( center ) ;
qgrad - > setFocalPoint ( origin ) ;
// TODO what in case of different radii?
qgrad - > setRadius ( qMin ( radiusX , radiusY ) ) ;
2015-10-29 12:37:11 +00:00
applySpreadStyleToQGradient ( node . attributes . value ( QStringLiteral ( " SpreadMethod " ) ) , qgrad ) ;
2019-09-18 11:41:19 +00:00
node . data = QVariant : : fromValue ( QBrush ( * qgrad ) ) ;
2008-06-27 17:18:41 +00:00
delete qgrad ;
}
2015-10-29 12:37:11 +00:00
} else if ( node . name = = QLatin1String ( " LinearGradientBrush.GradientStops " ) ) {
2008-06-27 17:18:41 +00:00
QList < XpsGradient > gradients ;
2019-12-23 06:06:25 +00:00
for ( const XpsRenderNode & child : qAsConst ( node . children ) ) {
2015-10-29 12:37:11 +00:00
double offset = child . attributes . value ( QStringLiteral ( " Offset " ) ) . toDouble ( ) ;
QColor color = hexToRgba ( child . attributes . value ( QStringLiteral ( " Color " ) ) . toLatin1 ( ) ) ;
2008-06-27 17:18:41 +00:00
gradients . append ( XpsGradient ( offset , color ) ) ;
}
preprocessXpsGradients ( gradients ) ;
if ( ! gradients . isEmpty ( ) ) {
QLinearGradient * qgrad = new QLinearGradient ( ) ;
addXpsGradientsToQGradient ( gradients , qgrad ) ;
2019-09-18 11:41:19 +00:00
node . data = QVariant : : fromValue < QGradient * > ( qgrad ) ;
2008-06-27 17:18:41 +00:00
}
2015-10-29 12:37:11 +00:00
} else if ( node . name = = QLatin1String ( " RadialGradientBrush.GradientStops " ) ) {
2008-06-27 17:18:41 +00:00
QList < XpsGradient > gradients ;
2019-12-23 06:06:25 +00:00
for ( const XpsRenderNode & child : qAsConst ( node . children ) ) {
2015-10-29 12:37:11 +00:00
double offset = child . attributes . value ( QStringLiteral ( " Offset " ) ) . toDouble ( ) ;
QColor color = hexToRgba ( child . attributes . value ( QStringLiteral ( " Color " ) ) . toLatin1 ( ) ) ;
2008-06-27 17:18:41 +00:00
gradients . append ( XpsGradient ( offset , color ) ) ;
}
preprocessXpsGradients ( gradients ) ;
if ( ! gradients . isEmpty ( ) ) {
QRadialGradient * qgrad = new QRadialGradient ( ) ;
addXpsGradientsToQGradient ( gradients , qgrad ) ;
2019-09-18 11:41:19 +00:00
node . data = QVariant : : fromValue < QGradient * > ( qgrad ) ;
2008-06-27 11:52:45 +00:00
}
2015-10-29 12:37:11 +00:00
} else if ( node . name = = QLatin1String ( " PathFigure " ) ) {
2008-06-29 02:15:17 +00:00
processPathFigure ( node ) ;
2015-10-29 12:37:11 +00:00
} else if ( node . name = = QLatin1String ( " PathGeometry " ) ) {
2008-06-29 02:15:17 +00:00
processPathGeometry ( node ) ;
2015-10-29 12:37:11 +00:00
} else if ( node . name = = QLatin1String ( " Path.Data " ) ) {
2008-06-29 02:15:17 +00:00
processPathData ( node ) ;
2007-02-10 17:31:25 +00:00
} else {
2014-09-11 19:12:27 +00:00
// qCWarning(OkularXpsDebug) << "Unknown element: " << node->name;
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
{
2017-09-05 21:27:18 +00:00
m_pageImage = nullptr ;
2007-02-08 18:09:15 +00:00
2014-09-11 19:12:27 +00:00
// qCWarning(OkularXpsDebug) << "page file name: " << fileName;
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-03-08 03:11:04 +00:00
QXmlStreamReader xml ;
2008-06-27 19:09:50 +00:00
xml . addData ( readFileOrDirectoryParts ( pageFile ) ) ;
2007-03-08 03:11:04 +00:00
while ( ! xml . atEnd ( ) ) {
xml . readNext ( ) ;
2016-07-11 19:59:14 +00:00
if ( xml . isStartElement ( ) & & ( xml . name ( ) = = QStringLiteral ( " FixedPage " ) ) ) {
2007-03-08 03:11:04 +00:00
QXmlStreamAttributes attributes = xml . attributes ( ) ;
2015-10-29 12:37:11 +00:00
m_pageSize . setWidth ( attributes . value ( QStringLiteral ( " Width " ) ) . toString ( ) . toDouble ( ) ) ;
m_pageSize . setHeight ( attributes . value ( QStringLiteral ( " Height " ) ) . toString ( ) . toDouble ( ) ) ;
2007-03-08 03:11:04 +00:00
break ;
}
2007-02-08 16:41:53 +00:00
}
2007-03-08 03:11:04 +00:00
if ( xml . error ( ) ) {
2014-09-11 19:12:27 +00:00
qCWarning ( OkularXpsDebug ) < < " Could not parse XPS page: " < < xml . errorString ( ) ;
2007-02-08 16:41:53 +00:00
}
2007-02-09 08:11:47 +00:00
}
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
{
2017-09-05 21:27:18 +00:00
if ( ( m_pageImage = = nullptr ) | | ( m_pageImage - > size ( ) ! = p - > size ( ) ) ) {
2007-02-08 18:09:15 +00:00
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 ) {
2008-08-22 00:41:17 +00:00
m_pageImage - > fill ( qRgba ( 255 , 255 , 255 , 255 ) ) ;
2008-08-03 10:19:05 +00:00
QPainter painter ( m_pageImage ) ;
renderToPainter ( & painter ) ;
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
2008-08-03 10:19:05 +00:00
bool XpsPage : : renderToPainter ( QPainter * painter )
{
XpsHandler handler ( this ) ;
handler . m_painter = painter ;
2012-09-08 15:06:02 +00:00
handler . m_painter - > setWorldTransform ( QTransform ( ) . scale ( ( qreal ) painter - > device ( ) - > width ( ) / size ( ) . width ( ) , ( qreal ) painter - > device ( ) - > height ( ) / size ( ) . height ( ) ) ) ;
2008-08-03 10:19:05 +00:00
QXmlSimpleReader parser ;
parser . setContentHandler ( & handler ) ;
parser . setErrorHandler ( & handler ) ;
const KZipFileEntry * pageFile = static_cast < const KZipFileEntry * > ( m_file - > xpsArchive ( ) - > directory ( ) - > entry ( m_fileName ) ) ;
QByteArray data = readFileOrDirectoryParts ( pageFile ) ;
QBuffer buffer ( & data ) ;
QXmlInputSource source ( & buffer ) ;
bool ok = parser . parse ( source ) ;
2014-09-11 19:12:27 +00:00
qCWarning ( OkularXpsDebug ) < < " Parse result: " < < ok ;
2008-08-03 10:19:05 +00:00
return true ;
}
2008-06-27 00:59:36 +00:00
QSizeF XpsPage : : size ( ) const
2006-09-11 10:44:17 +00:00
{
return m_pageSize ;
}
2018-04-08 10:50:46 +00:00
QFont XpsFile : : getFontByName ( const QString & absoluteFileName , float size )
2007-02-06 07:07:23 +00:00
{
2014-09-11 19:12:27 +00:00
// qCWarning(OkularXpsDebug) << "trying to get font: " << fileName << ", size: " << size;
2007-03-10 10:18:12 +00:00
2018-04-08 10:50:46 +00:00
int index = m_fontCache . value ( absoluteFileName , - 1 ) ;
2007-02-17 09:42:15 +00:00
if ( index = = - 1 ) {
2018-04-08 10:50:46 +00:00
index = loadFontByName ( absoluteFileName ) ;
m_fontCache [ absoluteFileName ] = index ;
2007-02-06 07:07:23 +00:00
}
2008-06-27 19:00:17 +00:00
if ( index = = - 1 ) {
2019-01-11 07:09:34 +00:00
qCWarning ( OkularXpsDebug ) < < " Requesting unknown font: " < < absoluteFileName ;
2008-06-27 19:00:17 +00:00
return QFont ( ) ;
}
2007-02-13 13:26:37 +00:00
2010-09-10 21:53:49 +00:00
const QStringList fontFamilies = m_fontDatabase . applicationFontFamilies ( index ) ;
if ( fontFamilies . isEmpty ( ) ) {
2018-04-08 10:50:46 +00:00
qCWarning ( OkularXpsDebug ) < < " The unexpected has happened. No font family for a known font: " < < absoluteFileName < < index ;
2010-09-10 21:53:49 +00:00
return QFont ( ) ;
}
const QString fontFamily = fontFamilies [ 0 ] ;
const QStringList fontStyles = m_fontDatabase . styles ( fontFamily ) ;
if ( fontStyles . isEmpty ( ) ) {
2018-04-08 10:50:46 +00:00
qCWarning ( OkularXpsDebug ) < < " The unexpected has happened. No font style for a known font family: " < < absoluteFileName < < index < < fontFamily ;
2010-09-10 21:53:49 +00:00
return QFont ( ) ;
}
const QString fontStyle = fontStyles [ 0 ] ;
return m_fontDatabase . font ( fontFamily , fontStyle , qRound ( size ) ) ;
2007-02-06 07:07:23 +00:00
}
2018-04-08 10:50:46 +00:00
int XpsFile : : loadFontByName ( const QString & absoluteFileName )
2006-09-21 11:41:46 +00:00
{
2018-04-08 10:50:46 +00:00
// qCWarning(OkularXpsDebug) << "font file name: " << absoluteFileName;
2006-09-21 11:41:46 +00:00
2018-04-08 10:50:46 +00:00
const KArchiveEntry * fontFile = loadEntry ( m_xpsArchive , absoluteFileName , Qt : : CaseInsensitive ) ;
2008-06-27 19:00:17 +00:00
if ( ! fontFile ) {
return - 1 ;
}
2006-09-21 11:41:46 +00:00
2011-06-04 18:19:39 +00:00
QByteArray fontData = readFileOrDirectoryParts ( fontFile ) ; // once per file, according to the docs
2006-09-21 11:41:46 +00:00
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
2018-04-08 10:50:46 +00:00
const QString baseName = resourceName ( absoluteFileName ) ;
2007-02-06 07:07:23 +00:00
unsigned short guid [ 16 ] ;
if ( ! parseGUID ( baseName , guid ) ) {
2014-09-11 19:12:27 +00:00
qCWarning ( OkularXpsDebug ) < < " File to load font - file name isn't a GUID " ;
2007-02-06 07:07:23 +00:00
} else {
2007-02-08 16:41:53 +00:00
if ( fontData . length ( ) < 32 ) {
2014-09-11 19:12:27 +00:00
qCWarning ( OkularXpsDebug ) < < " Font file is too small " ;
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
2014-09-11 19:12:27 +00:00
// qCWarning(OkularXpsDebug) << "Loaded font: " << m_fontDatabase.applicationFontFamilies( result );
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
{
2014-09-11 19:12:27 +00:00
// qCWarning(OkularXpsDebug) << "image file name: " << fileName;
2006-09-21 11:41:46 +00:00
2008-03-08 23:45:02 +00:00
if ( fileName . at ( 0 ) = = QLatin1Char ( ' { ' ) ) {
// for example: '{ColorConvertedBitmap /Resources/bla.wdp /Resources/foobar.icc}'
// TODO: properly read a ColorConvertedBitmap
return QImage ( ) ;
}
2008-06-27 17:42:42 +00:00
QString absoluteFileName = absolutePath ( entryPath ( m_fileName ) , fileName ) ;
2008-06-28 10:15:43 +00:00
const KZipFileEntry * imageFile = loadFile ( m_file - > xpsArchive ( ) , absoluteFileName , Qt : : CaseInsensitive ) ;
2008-06-27 16:37:58 +00:00
if ( ! imageFile ) {
2008-06-28 10:15:43 +00:00
// image not found
return QImage ( ) ;
2008-06-27 16:37:58 +00:00
}
2006-09-21 11:41:46 +00:00
2007-04-20 09:15:00 +00:00
/* WORKAROUND:
XPS standard requires to use 96 dpi for images which doesn ' t have dpi specified ( in file ) . When Qt loads such an image ,
it sets its dpi to qt_defaultDpi and doesn ' t allow to find out that it happend .
To workaround this I used this procedure : load image , set its dpi to 96 , load image again . When dpi isn ' t set in file ,
dpi set by me stays unchanged .
2008-03-11 12:57:43 +00:00
Trolltech task ID : 159527.
2007-04-20 09:15:00 +00:00
*/
2007-03-10 05:52:56 +00:00
QImage image ;
2007-04-20 09:15:00 +00:00
QByteArray data = imageFile - > data ( ) ;
QBuffer buffer ( & data ) ;
buffer . open ( QBuffer : : ReadOnly ) ;
2007-03-10 05:52:56 +00:00
2007-04-20 09:15:00 +00:00
QImageReader reader ( & buffer ) ;
image = reader . read ( ) ;
2006-09-21 11:41:46 +00:00
2007-04-20 09:15:00 +00:00
image . setDotsPerMeterX ( qRound ( 96 / 0.0254 ) ) ;
image . setDotsPerMeterY ( qRound ( 96 / 0.0254 ) ) ;
2020-07-10 22:15:05 +00:00
2007-04-20 09:15:00 +00:00
buffer . seek ( 0 ) ;
reader . setDevice ( & buffer ) ;
reader . read ( & image ) ;
2007-03-10 05:52:56 +00:00
2006-09-21 11:41:46 +00:00
return image ;
}
2007-03-10 10:18:12 +00:00
Okular : : TextPage * XpsPage : : textPage ( )
{
2014-09-11 19:12:27 +00:00
// qCWarning(OkularXpsDebug) << "Parsing XpsPage, text extraction";
2007-03-10 10:18:12 +00:00
Okular : : TextPage * textPage = new Okular : : TextPage ( ) ;
const KZipFileEntry * pageFile = static_cast < const KZipFileEntry * > ( m_file - > xpsArchive ( ) - > directory ( ) - > entry ( m_fileName ) ) ;
QXmlStreamReader xml ;
2011-06-04 18:52:46 +00:00
xml . addData ( readFileOrDirectoryParts ( pageFile ) ) ;
2007-03-10 10:18:12 +00:00
2012-09-08 15:06:02 +00:00
QTransform matrix = QTransform ( ) ;
QStack < QTransform > matrices ;
matrices . push ( QTransform ( ) ) ;
2007-03-10 10:18:12 +00:00
bool useMatrix = false ;
QXmlStreamAttributes glyphsAtts ;
while ( ! xml . atEnd ( ) ) {
xml . readNext ( ) ;
if ( xml . isStartElement ( ) ) {
2016-07-11 19:59:14 +00:00
if ( xml . name ( ) = = QStringLiteral ( " Canvas " ) ) {
2007-03-10 10:18:12 +00:00
matrices . push ( matrix ) ;
2015-10-29 12:37:11 +00:00
QString att = xml . attributes ( ) . value ( QStringLiteral ( " RenderTransform " ) ) . toString ( ) ;
2007-03-10 10:18:12 +00:00
if ( ! att . isEmpty ( ) ) {
matrix = parseRscRefMatrix ( att ) * matrix ;
}
2016-07-11 19:59:14 +00:00
} else if ( ( xml . name ( ) = = QStringLiteral ( " Canvas.RenderTransform " ) ) | | ( xml . name ( ) = = QStringLiteral ( " Glyphs.RenderTransform " ) ) ) {
2007-03-10 10:18:12 +00:00
useMatrix = true ;
2016-07-11 19:59:14 +00:00
} else if ( xml . name ( ) = = QStringLiteral ( " MatrixTransform " ) ) {
2007-03-11 01:14:33 +00:00
if ( useMatrix ) {
2015-10-29 12:37:11 +00:00
matrix = attsToMatrix ( xml . attributes ( ) . value ( QStringLiteral ( " Matrix " ) ) . toString ( ) ) * matrix ;
2007-03-11 01:14:33 +00:00
}
2016-07-11 19:59:14 +00:00
} else if ( xml . name ( ) = = QStringLiteral ( " Glyphs " ) ) {
2007-03-10 10:18:12 +00:00
matrices . push ( matrix ) ;
glyphsAtts = xml . attributes ( ) ;
2016-07-11 19:59:14 +00:00
} else if ( ( xml . name ( ) = = QStringLiteral ( " Path " ) ) | | ( xml . name ( ) = = QStringLiteral ( " Path.Fill " ) ) | | ( xml . name ( ) = = QStringLiteral ( " SolidColorBrush " ) ) | | ( xml . name ( ) = = QStringLiteral ( " ImageBrush " ) ) | |
( xml . name ( ) = = QStringLiteral ( " ImageBrush.Transform " ) ) | | ( xml . name ( ) = = QStringLiteral ( " Path.OpacityMask " ) ) | | ( xml . name ( ) = = QStringLiteral ( " Path.Data " ) ) | | ( xml . name ( ) = = QStringLiteral ( " PathGeometry " ) ) | |
( xml . name ( ) = = QStringLiteral ( " PathFigure " ) ) | | ( xml . name ( ) = = QStringLiteral ( " PolyLineSegment " ) ) ) {
2007-03-10 10:18:12 +00:00
// those are only graphical - no use in text handling
2016-07-11 19:59:14 +00:00
} else if ( ( xml . name ( ) = = QStringLiteral ( " FixedPage " ) ) | | ( xml . name ( ) = = QStringLiteral ( " FixedPage.Resources " ) ) ) {
2007-03-10 10:18:12 +00:00
// not useful for text extraction
} else {
2014-09-11 19:12:27 +00:00
qCWarning ( OkularXpsDebug ) < < " Unhandled element in Text Extraction start: " < < xml . name ( ) . toString ( ) ;
2007-03-10 10:18:12 +00:00
}
} else if ( xml . isEndElement ( ) ) {
2016-07-11 19:59:14 +00:00
if ( xml . name ( ) = = QStringLiteral ( " Canvas " ) ) {
2007-03-10 10:18:12 +00:00
matrix = matrices . pop ( ) ;
2016-07-11 19:59:14 +00:00
} else if ( ( xml . name ( ) = = QStringLiteral ( " Canvas.RenderTransform " ) ) | | ( xml . name ( ) = = QStringLiteral ( " Glyphs.RenderTransform " ) ) ) {
2007-03-10 10:18:12 +00:00
useMatrix = false ;
2016-07-11 19:59:14 +00:00
} else if ( xml . name ( ) = = QStringLiteral ( " MatrixTransform " ) ) {
2007-03-10 10:18:12 +00:00
// not clear if we need to do anything here yet.
2016-07-11 19:59:14 +00:00
} else if ( xml . name ( ) = = QStringLiteral ( " Glyphs " ) ) {
2015-10-29 12:37:11 +00:00
QString att = glyphsAtts . value ( QStringLiteral ( " RenderTransform " ) ) . toString ( ) ;
2007-03-10 10:18:12 +00:00
if ( ! att . isEmpty ( ) ) {
matrix = parseRscRefMatrix ( att ) * matrix ;
}
2015-10-29 12:37:11 +00:00
QString text = unicodeString ( glyphsAtts . value ( QStringLiteral ( " UnicodeString " ) ) . toString ( ) ) ;
2007-03-10 10:18:12 +00:00
// Get font (doesn't work well because qt doesn't allow to load font from file)
2018-04-08 10:50:46 +00:00
const QString absoluteFileName = absolutePath ( entryPath ( m_fileName ) , glyphsAtts . value ( QStringLiteral ( " FontUri " ) ) . toString ( ) ) ;
2015-10-29 12:37:11 +00:00
QFont font = m_file - > getFontByName ( absoluteFileName , glyphsAtts . value ( QStringLiteral ( " FontRenderingEmSize " ) ) . toString ( ) . toFloat ( ) * 72 / 96 ) ;
2007-03-10 10:18:12 +00:00
QFontMetrics metrics = QFontMetrics ( font ) ;
// Origin
2015-10-29 12:37:11 +00:00
QPointF origin ( glyphsAtts . value ( QStringLiteral ( " OriginX " ) ) . toString ( ) . toDouble ( ) , glyphsAtts . value ( QStringLiteral ( " OriginY " ) ) . toString ( ) . toDouble ( ) ) ;
2007-03-10 10:18:12 +00:00
int lastWidth = 0 ;
for ( int i = 0 ; i < text . length ( ) ; i + + ) {
2020-09-24 14:21:28 +00:00
const int width = metrics . horizontalAdvance ( text , i + 1 ) ;
2007-03-10 10:18:12 +00:00
Okular : : NormalizedRect * rect =
new Okular : : NormalizedRect ( ( origin . x ( ) + lastWidth ) / m_pageSize . width ( ) , ( origin . y ( ) - metrics . height ( ) ) / m_pageSize . height ( ) , ( origin . x ( ) + width ) / m_pageSize . width ( ) , origin . y ( ) / m_pageSize . height ( ) ) ;
rect - > transform ( matrix ) ;
textPage - > append ( text . mid ( i , 1 ) , rect ) ;
lastWidth = width ;
}
matrix = matrices . pop ( ) ;
2016-07-11 19:59:14 +00:00
} else if ( ( xml . name ( ) = = QStringLiteral ( " Path " ) ) | | ( xml . name ( ) = = QStringLiteral ( " Path.Fill " ) ) | | ( xml . name ( ) = = QStringLiteral ( " SolidColorBrush " ) ) | | ( xml . name ( ) = = QStringLiteral ( " ImageBrush " ) ) | |
( xml . name ( ) = = QStringLiteral ( " ImageBrush.Transform " ) ) | | ( xml . name ( ) = = QStringLiteral ( " Path.OpacityMask " ) ) | | ( xml . name ( ) = = QStringLiteral ( " Path.Data " ) ) | | ( xml . name ( ) = = QStringLiteral ( " PathGeometry " ) ) | |
( xml . name ( ) = = QStringLiteral ( " PathFigure " ) ) | | ( xml . name ( ) = = QStringLiteral ( " PolyLineSegment " ) ) ) {
2007-03-10 10:18:12 +00:00
// those are only graphical - no use in text handling
2016-07-11 19:59:14 +00:00
} else if ( ( xml . name ( ) = = QStringLiteral ( " FixedPage " ) ) | | ( xml . name ( ) = = QStringLiteral ( " FixedPage.Resources " ) ) ) {
2007-03-10 10:18:12 +00:00
// not useful for text extraction
} else {
2014-09-11 19:12:27 +00:00
qCWarning ( OkularXpsDebug ) < < " Unhandled element in Text Extraction end: " < < xml . name ( ) . toString ( ) ;
2007-03-10 10:18:12 +00:00
}
}
}
if ( xml . error ( ) ) {
2014-09-11 19:12:27 +00:00
qCWarning ( OkularXpsDebug ) < < " Error parsing XpsPage text: " < < xml . errorString ( ) ;
2007-03-10 10:18:12 +00:00
}
return textPage ;
}
2007-02-19 10:43:40 +00:00
void XpsDocument : : parseDocumentStructure ( const QString & documentStructureFileName )
{
2014-09-11 19:12:27 +00:00
qCWarning ( OkularXpsDebug ) < < " document structure file name: " < < documentStructureFileName ;
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 ) ) ;
2007-03-10 05:52:56 +00:00
QXmlStreamReader xml ;
xml . addData ( documentStructureFile - > data ( ) ) ;
2007-02-19 10:43:40 +00:00
2007-03-10 05:52:56 +00:00
while ( ! xml . atEnd ( ) ) {
xml . readNext ( ) ;
2007-02-19 10:43:40 +00:00
2007-03-10 05:52:56 +00:00
if ( xml . isStartElement ( ) ) {
2016-07-11 19:59:14 +00:00
if ( xml . name ( ) = = QStringLiteral ( " DocumentStructure " ) ) {
2007-03-10 05:52:56 +00:00
// just a container for optional outline and story elements - nothing to do here
2016-07-11 19:59:14 +00:00
} else if ( xml . name ( ) = = QStringLiteral ( " DocumentStructure.Outline " ) ) {
2014-09-11 19:12:27 +00:00
qCWarning ( OkularXpsDebug ) < < " found DocumentStructure.Outline " ;
2016-07-11 19:59:14 +00:00
} else if ( xml . name ( ) = = QStringLiteral ( " DocumentOutline " ) ) {
2014-09-11 19:12:27 +00:00
qCWarning ( OkularXpsDebug ) < < " found DocumentOutline " ;
2007-02-19 10:43:40 +00:00
m_docStructure = new Okular : : DocumentSynopsis ;
2016-07-11 19:59:14 +00:00
} else if ( xml . name ( ) = = QStringLiteral ( " OutlineEntry " ) ) {
2007-03-10 05:52:56 +00:00
m_haveDocumentStructure = true ;
QXmlStreamAttributes attributes = xml . attributes ( ) ;
2015-10-29 12:37:11 +00:00
int outlineLevel = attributes . value ( QStringLiteral ( " OutlineLevel " ) ) . toString ( ) . toInt ( ) ;
QString description = attributes . value ( QStringLiteral ( " Description " ) ) . toString ( ) ;
2007-03-10 05:52:56 +00:00
QDomElement synopsisElement = m_docStructure - > createElement ( description ) ;
2015-10-29 12:37:11 +00:00
synopsisElement . setAttribute ( QStringLiteral ( " OutlineLevel " ) , outlineLevel ) ;
QString target = attributes . value ( QStringLiteral ( " OutlineTarget " ) ) . toString ( ) ;
2016-07-11 19:59:14 +00:00
int hashPosition = target . lastIndexOf ( QLatin1Char ( ' # ' ) ) ;
2007-03-10 05:52:56 +00:00
target = target . mid ( hashPosition + 1 ) ;
2014-09-11 19:12:27 +00:00
// qCWarning(OkularXpsDebug) << "target: " << target;
2007-03-10 05:52:56 +00:00
Okular : : DocumentViewport viewport ;
viewport . pageNumber = m_docStructurePageMap . value ( target ) ;
2015-10-29 12:37:11 +00:00
synopsisElement . setAttribute ( QStringLiteral ( " Viewport " ) , viewport . toString ( ) ) ;
2007-03-10 05:52:56 +00:00
if ( outlineLevel = = 1 ) {
2014-09-11 19:12:27 +00:00
// qCWarning(OkularXpsDebug) << "Description: "
2020-01-17 20:04:27 +00:00
// << outlineEntryElement.attribute( "Description" );
2007-03-10 05:52:56 +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)
QDomNode maybeParentNode = m_docStructure - > lastChild ( ) ;
while ( ! maybeParentNode . isNull ( ) ) {
2015-10-29 12:37:11 +00:00
if ( maybeParentNode . toElement ( ) . attribute ( QStringLiteral ( " OutlineLevel " ) ) . toInt ( ) = = ( outlineLevel - 1 ) ) {
2007-03-10 05:52:56 +00:00
// we have the right parent
maybeParentNode . appendChild ( synopsisElement ) ;
break ;
2007-02-19 10:43:40 +00:00
}
2007-03-10 05:52:56 +00:00
maybeParentNode = maybeParentNode . lastChild ( ) ;
2007-02-19 10:43:40 +00:00
}
}
2016-07-11 19:59:14 +00:00
} else if ( xml . name ( ) = = QStringLiteral ( " Story " ) ) {
2007-03-10 05:52:56 +00:00
// we need to handle Story here, but I have no idea what to do with it.
2016-07-11 19:59:14 +00:00
} else if ( xml . name ( ) = = QStringLiteral ( " StoryFragment " ) ) {
2007-03-10 05:52:56 +00:00
// we need to handle StoryFragment here, but I have no idea what to do with it.
2016-07-11 19:59:14 +00:00
} else if ( xml . name ( ) = = QStringLiteral ( " StoryFragmentReference " ) ) {
2007-03-10 05:52:56 +00:00
// we need to handle StoryFragmentReference here, but I have no idea what to do with it.
2007-02-19 10:43:40 +00:00
} else {
2014-09-11 19:12:27 +00:00
qCWarning ( OkularXpsDebug ) < < " Unhandled entry in DocumentStructure: " < < xml . name ( ) . toString ( ) ;
2007-02-19 10:43:40 +00:00
}
}
}
}
const Okular : : DocumentSynopsis * XpsDocument : : documentStructure ( )
{
return m_docStructure ;
}
bool XpsDocument : : hasDocumentStructure ( )
{
return m_haveDocumentStructure ;
}
2017-09-05 21:27:18 +00:00
XpsDocument : : XpsDocument ( XpsFile * file , const QString & fileName )
: m_file ( file )
, m_haveDocumentStructure ( false )
, m_docStructure ( nullptr )
2006-09-11 10:44:17 +00:00
{
2014-09-11 19:12:27 +00:00
qCWarning ( OkularXpsDebug ) < < " document file name: " < < fileName ;
2006-09-12 10:31:09 +00:00
2008-03-09 00:54:49 +00:00
const KArchiveEntry * documentEntry = file - > xpsArchive ( ) - > directory ( ) - > entry ( fileName ) ;
QString documentFilePath = fileName ;
2008-03-09 16:09:31 +00:00
const QString documentEntryPath = entryPath ( fileName ) ;
2006-09-11 10:44:17 +00:00
2007-03-10 05:52:56 +00:00
QXmlStreamReader docXml ;
2008-03-09 00:54:49 +00:00
docXml . addData ( readFileOrDirectoryParts ( documentEntry , & documentFilePath ) ) ;
2007-03-10 05:52:56 +00:00
while ( ! docXml . atEnd ( ) ) {
docXml . readNext ( ) ;
if ( docXml . isStartElement ( ) ) {
2016-07-11 19:59:14 +00:00
if ( docXml . name ( ) = = QStringLiteral ( " PageContent " ) ) {
2015-10-29 12:37:11 +00:00
QString pagePath = docXml . attributes ( ) . value ( QStringLiteral ( " Source " ) ) . toString ( ) ;
2014-09-11 19:12:27 +00:00
qCWarning ( OkularXpsDebug ) < < " Page Path: " < < pagePath ;
2008-03-08 23:11:52 +00:00
XpsPage * page = new XpsPage ( file , absolutePath ( documentFilePath , pagePath ) ) ;
2006-09-11 10:44:17 +00:00
m_pages . append ( page ) ;
2016-07-11 19:59:14 +00:00
} else if ( docXml . name ( ) = = QStringLiteral ( " PageContent.LinkTargets " ) ) {
2007-03-10 05:52:56 +00:00
// do nothing - wait for the real LinkTarget elements
2016-07-11 19:59:14 +00:00
} else if ( docXml . name ( ) = = QStringLiteral ( " LinkTarget " ) ) {
2015-10-29 12:37:11 +00:00
QString targetName = docXml . attributes ( ) . value ( QStringLiteral ( " Name " ) ) . toString ( ) ;
2007-03-10 05:52:56 +00:00
if ( ! targetName . isEmpty ( ) ) {
m_docStructurePageMap [ targetName ] = m_pages . count ( ) - 1 ;
2007-02-19 10:43:40 +00:00
}
2016-07-11 19:59:14 +00:00
} else if ( docXml . name ( ) = = QStringLiteral ( " FixedDocument " ) ) {
2007-03-10 05:52:56 +00:00
// we just ignore this - it is just a container
2006-09-11 10:44:17 +00:00
} else {
2014-09-11 19:12:27 +00:00
qCWarning ( OkularXpsDebug ) < < " Unhandled entry in FixedDocument: " < < docXml . name ( ) . toString ( ) ;
2006-09-11 10:44:17 +00:00
}
}
}
2007-03-10 05:52:56 +00:00
if ( docXml . error ( ) ) {
2014-09-11 19:12:27 +00:00
qCWarning ( OkularXpsDebug ) < < " Could not parse main XPS document file: " < < docXml . errorString ( ) ;
2007-03-10 05:52:56 +00:00
}
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.
2016-07-11 19:59:14 +00:00
const int slashPosition = fileName . lastIndexOf ( QLatin1Char ( ' / ' ) ) ;
const QString documentRelationshipFile = absolutePath ( documentEntryPath , QStringLiteral ( " _rels/ " ) + fileName . mid ( slashPosition + 1 ) + QStringLiteral ( " .rels " ) ) ;
2007-02-19 10:43:40 +00:00
2008-03-09 16:09:31 +00:00
const KZipFileEntry * relFile = static_cast < const KZipFileEntry * > ( file - > xpsArchive ( ) - > directory ( ) - > entry ( documentRelationshipFile ) ) ;
2007-02-19 10:43:40 +00:00
QString documentStructureFile ;
if ( relFile ) {
2007-03-10 05:52:56 +00:00
QXmlStreamReader xml ;
2008-06-27 22:36:14 +00:00
xml . addData ( readFileOrDirectoryParts ( relFile ) ) ;
2007-03-10 05:52:56 +00:00
while ( ! xml . atEnd ( ) ) {
xml . readNext ( ) ;
2016-07-11 19:59:14 +00:00
if ( xml . isStartElement ( ) & & ( xml . name ( ) = = QStringLiteral ( " Relationship " ) ) ) {
2007-03-10 05:52:56 +00:00
QXmlStreamAttributes attributes = xml . attributes ( ) ;
2015-10-29 12:37:11 +00:00
if ( attributes . value ( QStringLiteral ( " Type " ) ) . toString ( ) = = QLatin1String ( " http://schemas.microsoft.com/xps/2005/06/documentstructure " ) ) {
documentStructureFile = attributes . value ( QStringLiteral ( " Target " ) ) . toString ( ) ;
2007-03-10 05:52:56 +00:00
} else {
2014-09-11 19:12:27 +00:00
qCWarning ( OkularXpsDebug ) < < " Unknown document relationships element: " < < attributes . value ( QStringLiteral ( " Type " ) ) . toString ( ) < < " : " < < attributes . value ( QStringLiteral ( " Target " ) ) . toString ( ) ;
2007-02-19 10:43:40 +00:00
}
}
}
2007-03-10 05:52:56 +00:00
if ( xml . error ( ) ) {
2014-09-11 19:12:27 +00:00
qCWarning ( OkularXpsDebug ) < < " Could not parse XPS page relationships file ( " < < documentRelationshipFile < < " ) - " < < xml . errorString ( ) ;
2007-03-10 05:52:56 +00:00
}
} else { // the page relationship file didn't exist in the zipfile
2007-02-19 10:43:40 +00:00
// this isn't fatal
2014-09-11 19:12:27 +00:00
qCWarning ( OkularXpsDebug ) < < " Could not open Document relationship file from " < < documentRelationshipFile ;
2007-02-19 10:43:40 +00:00
}
if ( ! documentStructureFile . isEmpty ( ) ) {
2014-09-11 19:12:27 +00:00
// qCWarning(OkularXpsDebug) << "Document structure filename: " << documentStructureFile;
2007-02-19 10:43:40 +00:00
// make the document path absolute
2008-03-09 16:09:31 +00:00
documentStructureFile = absolutePath ( documentEntryPath , documentStructureFile ) ;
2014-09-11 19:12:27 +00:00
// qCWarning(OkularXpsDebug) << "Document structure absolute path: " << documentStructureFile;
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 ( )
{
2020-01-02 23:16:36 +00:00
qDeleteAll ( m_pages ) ;
2007-02-09 08:11:47 +00:00
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 ) ;
}
2014-05-11 09:17:49 +00:00
XpsFile : : XpsFile ( )
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 ( )
{
2018-04-08 11:00:08 +00:00
for ( int fontId : qAsConst ( m_fontCache ) ) {
m_fontDatabase . removeApplicationFont ( fontId ) ;
}
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 ) {
2014-09-11 19:12:27 +00:00
qCWarning ( OkularXpsDebug ) < < " Successful open of " < < m_xpsArchive - > fileName ( ) ;
2006-09-11 10:44:17 +00:00
} else {
2014-09-11 19:12:27 +00:00
qCWarning ( OkularXpsDebug ) < < " Could not open XPS archive: " < < m_xpsArchive - > fileName ( ) ;
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
2015-10-29 12:37:11 +00:00
const KArchiveEntry * relEntry = m_xpsArchive - > directory ( ) - > entry ( QStringLiteral ( " _rels/.rels " ) ) ;
2008-03-09 00:54:49 +00:00
if ( ! relEntry ) {
2006-09-11 10:44:17 +00:00
// this might occur if we can't read the zip directory, or it doesn't have the relationships entry
return false ;
}
2007-03-10 05:52:56 +00:00
QXmlStreamReader relXml ;
2008-03-09 00:54:49 +00:00
relXml . addData ( readFileOrDirectoryParts ( relEntry ) ) ;
2006-09-11 10:44:17 +00:00
QString fixedRepresentationFileName ;
// We work through the relationships document and pull out each element.
2007-03-10 05:52:56 +00:00
while ( ! relXml . atEnd ( ) ) {
relXml . readNext ( ) ;
if ( relXml . isStartElement ( ) ) {
2016-07-11 19:59:14 +00:00
if ( relXml . name ( ) = = QStringLiteral ( " Relationship " ) ) {
2007-03-10 05:52:56 +00:00
QXmlStreamAttributes attributes = relXml . attributes ( ) ;
2015-10-29 12:37:11 +00:00
QString type = attributes . value ( QStringLiteral ( " Type " ) ) . toString ( ) ;
QString target = attributes . value ( QStringLiteral ( " Target " ) ) . toString ( ) ;
2016-07-11 19:59:14 +00:00
if ( QStringLiteral ( " http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail " ) = = type ) {
2007-03-10 05:52:56 +00:00
m_thumbnailFileName = target ;
2016-07-11 19:59:14 +00:00
} else if ( QStringLiteral ( " http://schemas.microsoft.com/xps/2005/06/fixedrepresentation " ) = = type ) {
2007-03-10 05:52:56 +00:00
fixedRepresentationFileName = target ;
2016-07-11 19:59:14 +00:00
} else if ( QStringLiteral ( " http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties " ) = = type ) {
2007-03-10 05:52:56 +00:00
m_corePropertiesFileName = target ;
2016-07-11 19:59:14 +00:00
} else if ( QStringLiteral ( " http://schemas.openxmlformats.org/package/2006/relationships/digital-signature/origin " ) = = type ) {
2007-03-10 05:52:56 +00:00
m_signatureOrigin = target ;
} else {
2014-09-11 19:12:27 +00:00
qCWarning ( OkularXpsDebug ) < < " Unknown relationships element: " < < type < < " : " < < target ;
2007-03-10 05:52:56 +00:00
}
2016-07-11 19:59:14 +00:00
} else if ( relXml . name ( ) = = QStringLiteral ( " Relationships " ) ) {
2007-03-10 05:52:56 +00:00
// nothing to do here - this is just the container level
2006-09-11 10:44:17 +00:00
} else {
2014-09-11 19:12:27 +00:00
qCWarning ( OkularXpsDebug ) < < " unexpected element in _rels/.rels: " < < relXml . name ( ) . toString ( ) ;
2006-09-11 10:44:17 +00:00
}
}
2007-03-10 05:52:56 +00:00
}
if ( relXml . error ( ) ) {
2014-09-11 19:12:27 +00:00
qCWarning ( OkularXpsDebug ) < < " Could not parse _rels/.rels: " < < relXml . errorString ( ) ;
2007-03-10 05:52:56 +00:00
return false ;
2006-09-11 10:44:17 +00:00
}
2007-03-10 05:52:56 +00:00
if ( fixedRepresentationFileName . isEmpty ( ) ) {
2006-09-11 10:44:17 +00:00
// FixedRepresentation is a required part of the XPS document
return false ;
}
2008-03-09 00:54:49 +00:00
const KArchiveEntry * fixedRepEntry = m_xpsArchive - > directory ( ) - > entry ( fixedRepresentationFileName ) ;
QString fixedRepresentationFilePath = fixedRepresentationFileName ;
2006-09-11 10:44:17 +00:00
2007-03-10 05:52:56 +00:00
QXmlStreamReader fixedRepXml ;
2008-03-09 00:54:49 +00:00
fixedRepXml . addData ( readFileOrDirectoryParts ( fixedRepEntry , & fixedRepresentationFileName ) ) ;
2006-09-11 10:44:17 +00:00
2007-03-10 05:52:56 +00:00
while ( ! fixedRepXml . atEnd ( ) ) {
fixedRepXml . readNext ( ) ;
if ( fixedRepXml . isStartElement ( ) ) {
2016-07-11 19:59:14 +00:00
if ( fixedRepXml . name ( ) = = QStringLiteral ( " DocumentReference " ) ) {
2015-10-29 12:37:11 +00:00
const QString source = fixedRepXml . attributes ( ) . value ( QStringLiteral ( " Source " ) ) . toString ( ) ;
2008-03-08 23:11:52 +00:00
XpsDocument * doc = new XpsDocument ( this , absolutePath ( fixedRepresentationFilePath , 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 ) ;
2016-07-11 19:59:14 +00:00
} else if ( fixedRepXml . name ( ) = = QStringLiteral ( " FixedDocumentSequence " ) ) {
2007-03-10 05:52:56 +00:00
// we don't do anything here - this is just a container for one or more DocumentReference elements
2006-09-11 10:44:17 +00:00
} else {
2014-09-11 19:12:27 +00:00
qCWarning ( OkularXpsDebug ) < < " Unhandled entry in FixedDocumentSequence: " < < fixedRepXml . name ( ) . toString ( ) ;
2006-09-11 10:44:17 +00:00
}
}
}
2007-03-10 05:52:56 +00:00
if ( fixedRepXml . error ( ) ) {
2014-09-11 19:12:27 +00:00
qCWarning ( OkularXpsDebug ) < < " Could not parse FixedRepresentation file: " < < fixedRepXml . errorString ( ) ;
2007-03-10 05:52:56 +00:00
return false ;
}
2006-09-11 10:44:17 +00:00
return true ;
}
2014-05-11 09:17:49 +00:00
Okular : : DocumentInfo XpsFile : : generateDocumentInfo ( ) const
2006-09-11 10:44:17 +00:00
{
2014-05-11 09:17:49 +00:00
Okular : : DocumentInfo docInfo ;
2006-09-11 10:44:17 +00:00
2015-10-29 12:37:11 +00:00
docInfo . set ( Okular : : DocumentInfo : : MimeType , QStringLiteral ( " application/oxps " ) ) ;
2006-09-11 10:44:17 +00:00
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-03-08 03:11:04 +00:00
QXmlStreamReader xml ;
2007-03-10 05:52:56 +00:00
xml . addData ( corepropsFile - > data ( ) ) ;
2007-03-08 03:11:04 +00:00
while ( ! xml . atEnd ( ) ) {
xml . readNext ( ) ;
if ( xml . isEndElement ( ) )
break ;
if ( xml . isStartElement ( ) ) {
2016-07-11 19:59:14 +00:00
if ( xml . name ( ) = = QStringLiteral ( " title " ) ) {
2014-05-11 09:17:49 +00:00
docInfo . set ( Okular : : DocumentInfo : : Title , xml . readElementText ( ) ) ;
2016-07-11 19:59:14 +00:00
} else if ( xml . name ( ) = = QStringLiteral ( " subject " ) ) {
2014-05-11 09:17:49 +00:00
docInfo . set ( Okular : : DocumentInfo : : Subject , xml . readElementText ( ) ) ;
2016-07-11 19:59:14 +00:00
} else if ( xml . name ( ) = = QStringLiteral ( " description " ) ) {
2014-05-11 09:17:49 +00:00
docInfo . set ( Okular : : DocumentInfo : : Description , xml . readElementText ( ) ) ;
2016-07-11 19:59:14 +00:00
} else if ( xml . name ( ) = = QStringLiteral ( " creator " ) ) {
2014-05-11 09:17:49 +00:00
docInfo . set ( Okular : : DocumentInfo : : Creator , xml . readElementText ( ) ) ;
2016-07-11 19:59:14 +00:00
} else if ( xml . name ( ) = = QStringLiteral ( " category " ) ) {
2014-05-11 09:17:49 +00:00
docInfo . set ( Okular : : DocumentInfo : : Category , xml . readElementText ( ) ) ;
2016-07-11 19:59:14 +00:00
} else if ( xml . name ( ) = = QStringLiteral ( " created " ) ) {
2015-10-29 12:37:11 +00:00
QDateTime createdDate = QDateTime : : fromString ( xml . readElementText ( ) , QStringLiteral ( " yyyy-MM-ddThh:mm:ssZ " ) ) ;
2015-01-29 20:48:47 +00:00
docInfo . set ( Okular : : DocumentInfo : : CreationDate , QLocale ( ) . toString ( createdDate , QLocale : : LongFormat ) ) ;
2016-07-11 19:59:14 +00:00
} else if ( xml . name ( ) = = QStringLiteral ( " modified " ) ) {
2015-10-29 12:37:11 +00:00
QDateTime modifiedDate = QDateTime : : fromString ( xml . readElementText ( ) , QStringLiteral ( " yyyy-MM-ddThh:mm:ssZ " ) ) ;
2015-01-29 20:48:47 +00:00
docInfo . set ( Okular : : DocumentInfo : : ModificationDate , QLocale ( ) . toString ( modifiedDate , QLocale : : LongFormat ) ) ;
2016-07-11 19:59:14 +00:00
} else if ( xml . name ( ) = = QStringLiteral ( " keywords " ) ) {
2014-05-11 09:17:49 +00:00
docInfo . set ( Okular : : DocumentInfo : : Keywords , xml . readElementText ( ) ) ;
2016-07-11 19:59:14 +00:00
} else if ( xml . name ( ) = = QStringLiteral ( " revision " ) ) {
2015-10-29 12:37:11 +00:00
docInfo . set ( QStringLiteral ( " revision " ) , xml . readElementText ( ) , i18n ( " Revision " ) ) ;
2006-09-11 10:44:17 +00:00
}
}
2007-03-08 03:11:04 +00:00
}
if ( xml . error ( ) ) {
2014-09-11 19:12:27 +00:00
qCWarning ( OkularXpsDebug ) < < " Could not parse XPS core properties: " < < xml . errorString ( ) ;
2006-09-11 10:44:17 +00:00
}
} else {
2014-09-11 19:12:27 +00:00
qCWarning ( OkularXpsDebug ) < < " No core properties filename " ;
2006-09-11 10:44:17 +00:00
}
2014-05-11 09:17:49 +00:00
docInfo . set ( Okular : : DocumentInfo : : Pages , QString : : number ( numPages ( ) ) ) ;
2006-09-11 10:44:17 +00:00
2014-05-11 09:17:49 +00:00
return docInfo ;
2006-09-11 10:44:17 +00:00
}
bool XpsFile : : closeDocument ( )
{
2007-09-30 20:51:19 +00:00
qDeleteAll ( m_documents ) ;
2006-09-11 10:44:17 +00:00
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
}
2007-12-02 20:57:24 +00:00
XpsGenerator : : XpsGenerator ( QObject * parent , const QVariantList & args )
2017-09-05 21:27:18 +00:00
: Okular : : Generator ( parent , args )
, m_xpsFile ( nullptr )
2006-09-11 10:44:17 +00:00
{
2007-02-13 11:13:35 +00:00
setFeature ( TextExtraction ) ;
2008-08-03 10:19:05 +00:00
setFeature ( PrintNative ) ;
setFeature ( PrintToFile ) ;
2019-07-03 22:09:04 +00:00
setFeature ( Threaded ) ;
2009-04-22 23:02:00 +00:00
userMutex ( ) ;
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 ) {
2008-06-27 00:59:36 +00:00
QSizeF 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 ;
}
2007-10-08 16:46:51 +00:00
bool XpsGenerator : : doCloseDocument ( )
2006-09-11 10:44:17 +00:00
{
m_xpsFile - > closeDocument ( ) ;
2007-02-10 17:47:44 +00:00
delete m_xpsFile ;
2017-09-05 21:27:18 +00:00
m_xpsFile = nullptr ;
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
{
2009-04-22 23:02:00 +00:00
QMutexLocker lock ( userMutex ( ) ) ;
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
}
2018-02-01 18:42:37 +00:00
Okular : : TextPage * XpsGenerator : : textPage ( Okular : : TextRequest * request )
2007-02-13 11:13:35 +00:00
{
2009-04-22 23:02:00 +00:00
QMutexLocker lock ( userMutex ( ) ) ;
2018-02-01 18:42:37 +00:00
XpsPage * xpsPage = m_xpsFile - > page ( request - > page ( ) - > number ( ) ) ;
2007-02-13 11:13:35 +00:00
return xpsPage - > textPage ( ) ;
}
2014-05-11 09:17:49 +00:00
Okular : : DocumentInfo XpsGenerator : : generateDocumentInfo ( const QSet < Okular : : DocumentInfo : : Key > & keys ) const
2006-09-11 10:44:17 +00:00
{
2016-07-11 20:57:11 +00:00
Q_UNUSED ( keys ) ;
2014-09-11 19:12:27 +00:00
qCWarning ( OkularXpsDebug ) < < " generating document metadata " ;
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 ( )
{
2014-09-11 19:12:27 +00:00
qCWarning ( OkularXpsDebug ) < < " generating document synopsis " ;
2007-02-19 10:43:40 +00:00
// we only generate the synopsis for the first file.
if ( ! m_xpsFile | | ! m_xpsFile - > document ( 0 ) )
2017-09-05 21:27:18 +00:00
return nullptr ;
2007-02-19 10:43:40 +00:00
if ( m_xpsFile - > document ( 0 ) - > hasDocumentStructure ( ) )
return m_xpsFile - > document ( 0 ) - > documentStructure ( ) ;
2017-09-05 21:27:18 +00:00
return nullptr ;
2007-02-19 10:43:40 +00:00
}
2007-03-10 10:18:12 +00:00
Okular : : ExportFormat : : List XpsGenerator : : exportFormats ( ) const
{
static Okular : : ExportFormat : : List formats ;
if ( formats . isEmpty ( ) ) {
2007-04-21 11:09:41 +00:00
formats . append ( Okular : : ExportFormat : : standardFormat ( Okular : : ExportFormat : : PlainText ) ) ;
2007-03-10 10:18:12 +00:00
}
return formats ;
}
bool XpsGenerator : : exportTo ( const QString & fileName , const Okular : : ExportFormat & format )
{
2015-10-29 12:37:11 +00:00
if ( format . mimeType ( ) . inherits ( QStringLiteral ( " text/plain " ) ) ) {
2007-03-10 10:18:12 +00:00
QFile f ( fileName ) ;
if ( ! f . open ( QIODevice : : WriteOnly ) )
return false ;
QTextStream ts ( & f ) ;
for ( int i = 0 ; i < m_xpsFile - > numPages ( ) ; + + i ) {
2007-05-18 11:49:19 +00:00
Okular : : TextPage * textPage = m_xpsFile - > page ( i ) - > textPage ( ) ;
QString text = textPage - > text ( ) ;
2007-03-10 10:18:12 +00:00
ts < < text ;
2016-07-11 19:59:14 +00:00
ts < < QLatin1Char ( ' \n ' ) ;
2007-05-18 11:49:19 +00:00
delete textPage ;
2007-03-10 10:18:12 +00:00
}
f . close ( ) ;
return true ;
}
return false ;
}
2008-08-03 10:19:05 +00:00
bool XpsGenerator : : print ( QPrinter & printer )
{
QList < int > pageList = Okular : : FilePrinter : : pageList ( printer , document ( ) - > pages ( ) , document ( ) - > currentPage ( ) + 1 , document ( ) - > bookmarkedPageList ( ) ) ;
QPainter painter ( & printer ) ;
for ( int i = 0 ; i < pageList . count ( ) ; + + i ) {
if ( i ! = 0 )
printer . newPage ( ) ;
const int page = pageList . at ( i ) - 1 ;
XpsPage * pageToRender = m_xpsFile - > page ( page ) ;
pageToRender - > renderToPainter ( & painter ) ;
}
return true ;
}
2020-01-02 23:16:36 +00:00
const XpsRenderNode * XpsRenderNode : : findChild ( const QString & name ) const
2007-02-10 17:31:25 +00:00
{
2020-01-02 23:16:36 +00:00
for ( const XpsRenderNode & child : children ) {
if ( child . name = = name ) {
return & child ;
2007-02-10 17:31:25 +00:00
}
}
2017-09-05 21:27:18 +00:00
return nullptr ;
2007-02-10 17:31:25 +00:00
}
2020-01-02 23:16:36 +00:00
QVariant XpsRenderNode : : getRequiredChildData ( const QString & name ) const
2007-02-12 13:31:16 +00:00
{
2020-01-02 23:16:36 +00:00
const XpsRenderNode * child = findChild ( name ) ;
2017-09-05 21:27:18 +00:00
if ( child = = nullptr ) {
2014-09-11 19:12:27 +00:00
qCWarning ( OkularXpsDebug ) < < " Required element " < < name < < " is missing in " < < this - > name ;
2008-09-04 17:30:56 +00:00
return QVariant ( ) ;
2007-02-12 13:31:16 +00:00
}
return child - > data ;
}
2020-01-02 23:16:36 +00:00
QVariant XpsRenderNode : : getChildData ( const QString & name ) const
2007-02-12 13:31:16 +00:00
{
2020-01-02 23:16:36 +00:00
const XpsRenderNode * child = findChild ( name ) ;
2017-09-05 21:27:18 +00:00
if ( child = = nullptr ) {
2008-09-04 17:30:56 +00:00
return QVariant ( ) ;
2007-02-12 13:31:16 +00:00
} else {
return child - > data ;
}
}
2017-01-14 18:19:12 +00:00
Q_LOGGING_CATEGORY ( OkularXpsDebug , " org.kde.okular.generators.xps " , QtWarningMsg )
2014-09-11 19:12:27 +00:00
2006-09-11 10:44:17 +00:00
# include "generator_xps.moc"