2006-09-11 10:44:17 +00:00
/*
Copyright ( C ) 2006 Brad Hards < bradh @ frogmouth . net >
This program is free software ; you can redistribute it and / or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation ; either version 2
of the License , or ( at your option ) any later version
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program ; if not , write to the Free Software
2006-10-21 22:10:09 +00:00
Foundation , Inc . , 51 Franklin Street , Fifth Floor , Boston , MA
2006-09-11 10:44:17 +00:00
02110 - 1301 , USA .
*/
2007-04-19 18:30:20 +00:00
# include "generator_xps.h"
2006-09-11 10:44:17 +00:00
# include <qdatetime.h>
# include <qfile.h>
# include <qlist.h>
2006-09-21 11:41:46 +00:00
# include <qpainter.h>
2006-09-11 10:44:17 +00:00
# include <qpixmap.h>
# include <qthread.h>
2007-07-14 14:00:19 +00:00
# include <kaboutdata.h>
2006-09-11 10:44:17 +00:00
# include <kglobal.h>
# include <klocale.h>
2007-02-06 07:07:23 +00:00
# include <QFileInfo>
2007-04-19 14:38:03 +00:00
# include <QBuffer>
2007-04-20 09:15:00 +00:00
# include <QImageReader>
2006-09-11 10:44:17 +00:00
2007-01-03 14:30:48 +00:00
# include <okular/core/document.h>
# include <okular/core/page.h>
2007-02-13 11:13:35 +00:00
# include <okular/core/area.h>
2006-09-11 10:44:17 +00:00
2007-03-08 03:11:04 +00:00
const int XpsDebug = 4658 ;
2006-09-19 09:09:30 +00:00
OKULAR_EXPORT_PLUGIN ( XpsGenerator )
2006-09-11 10:44:17 +00:00
2006-09-21 11:41:46 +00:00
// From Qt4
static int hex2int ( char hex )
{
QChar hexchar = QLatin1Char ( hex ) ;
int v ;
if ( hexchar . isDigit ( ) )
v = hexchar . digitValue ( ) ;
else if ( hexchar > = QLatin1Char ( ' A ' ) & & hexchar < = QLatin1Char ( ' F ' ) )
v = hexchar . cell ( ) - ' A ' + 10 ;
else if ( hexchar > = QLatin1Char ( ' a ' ) & & hexchar < = QLatin1Char ( ' f ' ) )
v = hexchar . cell ( ) - ' a ' + 10 ;
else
v = - 1 ;
return v ;
}
// Modified from Qt4
static QColor hexToRgba ( const char * name )
{
if ( name [ 0 ] ! = ' # ' )
return QColor ( ) ;
name + + ; // eat the leading '#'
int len = qstrlen ( name ) ;
int r , g , b ;
int a = 255 ;
if ( len = = 6 ) {
r = ( hex2int ( name [ 0 ] ) < < 4 ) + hex2int ( name [ 1 ] ) ;
g = ( hex2int ( name [ 2 ] ) < < 4 ) + hex2int ( name [ 3 ] ) ;
b = ( hex2int ( name [ 4 ] ) < < 4 ) + hex2int ( name [ 5 ] ) ;
} else if ( len = = 8 ) {
a = ( hex2int ( name [ 0 ] ) < < 4 ) + hex2int ( name [ 1 ] ) ;
r = ( hex2int ( name [ 2 ] ) < < 4 ) + hex2int ( name [ 3 ] ) ;
g = ( hex2int ( name [ 4 ] ) < < 4 ) + hex2int ( name [ 5 ] ) ;
b = ( hex2int ( name [ 6 ] ) < < 4 ) + hex2int ( name [ 7 ] ) ;
} else {
r = g = b = - 1 ;
}
if ( ( uint ) r > 255 | | ( uint ) g > 255 | | ( uint ) b > 255 ) {
return QColor ( ) ;
}
return QColor ( r , g , b , a ) ;
}
static QRectF stringToRectF ( const QString & data )
{
QStringList numbers = data . split ( ' , ' ) ;
QPointF origin ( numbers . at ( 0 ) . toDouble ( ) , numbers . at ( 1 ) . toDouble ( ) ) ;
QSizeF size ( numbers . at ( 2 ) . toDouble ( ) , numbers . at ( 3 ) . toDouble ( ) ) ;
return QRectF ( origin , size ) ;
}
2007-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 ) ;
if ( ch . isNumber ( ) | | ( ch = = ' + ' ) | | ( ch = = ' - ' ) )
{
int start = * curPos ;
while ( ( * curPos < data . length ( ) ) & & ( ! data . at ( * curPos ) . isSpace ( ) ) & & ( data . at ( * curPos ) ! = ' , ' ) & & ! data . at ( * curPos ) . isLetter ( ) )
2007-02-08 16:41:53 +00:00
{
( * curPos ) + + ;
}
token - > number = data . mid ( start , * curPos - start ) . toDouble ( ) ;
token - > type = abtNumber ;
2007-02-08 15:07:54 +00:00
} else if ( ch = = ' , ' )
{
token - > type = abtComma ;
2007-02-08 16:41:53 +00:00
( * curPos ) + + ;
2007-02-17 09:42:15 +00:00
} else if ( ch . isLetter ( ) )
2007-02-08 15:07:54 +00:00
{
token - > type = abtCommand ;
2007-02-08 16:41:53 +00:00
token - > command = data . at ( * curPos ) . cell ( ) ;
( * curPos ) + + ;
2007-02-17 09:42:15 +00:00
} else
2007-02-08 15:07:54 +00:00
{
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
*/
2007-05-24 21:22:25 +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
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 )
{
2007-08-02 23:54:14 +00:00
kDebug ( XpsDebug ) < < " Error in parsing abbreviated path 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
}
char command = QChar ( token . command ) . toLower ( ) . cell ( ) ;
bool isRelative = QChar ( token . command ) . isLower ( ) ;
2007-02-10 17:31:25 +00:00
QPointF currPos = path . currentPosition ( ) ;
2007-02-08 15:07:54 +00:00
nextAbbPathToken ( & token ) ;
switch ( command ) {
case ' f ' :
int rule ;
rule = ( int ) token . number ;
if ( rule = = 0 )
{
2007-02-10 17:31:25 +00:00
path . setFillRule ( Qt : : OddEvenFill ) ;
2007-02-08 15:07:54 +00:00
}
else if ( rule = = 1 )
{
// In xps specs rule 1 means NonZero fill. I think it's equivalent to WindingFill but I'm not sure
2007-02-10 17:31:25 +00:00
path . setFillRule ( Qt : : WindingFill ) ;
2007-02-08 15:07:54 +00:00
}
2007-02-17 09:42:15 +00:00
nextAbbPathToken ( & token ) ;
2007-02-08 15:07:54 +00:00
break ;
case ' m ' : // Move
while ( token . type = = abtNumber )
{
2007-02-10 17:31:25 +00:00
QPointF point = getPointFromString ( & token , isRelative , currPos ) ;
path . moveTo ( point ) ;
2007-02-08 15:07:54 +00:00
}
break ;
case ' l ' : // Line
while ( token . type = = abtNumber )
{
2007-02-10 17:31:25 +00:00
QPointF point = getPointFromString ( & token , isRelative , currPos ) ;
path . lineTo ( point ) ;
2007-02-08 15:07:54 +00:00
}
break ;
case ' h ' : // Horizontal line
while ( token . type = = abtNumber )
{
2007-02-10 17:31:25 +00:00
double x = token . number + isRelative ? path . currentPosition ( ) . x ( ) : 0 ;
path . lineTo ( x , path . currentPosition ( ) . y ( ) ) ;
2007-02-08 15:07:54 +00:00
nextAbbPathToken ( & token ) ;
}
break ;
case ' v ' : // Vertical line
while ( token . type = = abtNumber )
{
2007-02-10 17:31:25 +00:00
double y = token . number + isRelative ? path . currentPosition ( ) . y ( ) : 0 ;
path . lineTo ( path . currentPosition ( ) . x ( ) , y ) ;
2007-02-08 15:07:54 +00:00
nextAbbPathToken ( & token ) ;
}
break ;
case ' c ' : // Cubic bezier curve
while ( token . type = = abtNumber )
{
2007-02-10 17:31:25 +00:00
QPointF firstControl = getPointFromString ( & token , isRelative , currPos ) ;
QPointF secondControl = getPointFromString ( & token , isRelative , currPos ) ;
QPointF endPoint = getPointFromString ( & token , isRelative , currPos ) ;
path . cubicTo ( firstControl , secondControl , endPoint ) ;
2007-02-08 15:07:54 +00:00
2007-02-08 16:41:53 +00:00
lastSecondControlPoint = secondControl ;
2007-02-08 15:07:54 +00:00
}
break ;
case ' q ' : // Quadratic bezier curve
while ( token . type = = abtNumber )
{
2007-02-10 17:31:25 +00:00
QPointF point1 = getPointFromString ( & token , isRelative , currPos ) ;
QPointF point2 = getPointFromString ( & token , isRelative , currPos ) ;
path . quadTo ( point2 , point2 ) ;
2007-02-08 15:07:54 +00:00
}
break ;
case ' s ' : // Smooth cubic bezier curve
while ( token . type = = abtNumber )
{
2007-02-08 16:41:53 +00:00
QPointF firstControl ;
if ( ( lastCommand = = ' s ' ) | | ( lastCommand = = ' c ' ) )
{
2007-02-10 17:31:25 +00:00
firstControl = lastSecondControlPoint + ( lastSecondControlPoint + path . currentPosition ( ) ) ;
2007-02-17 09:42:15 +00:00
}
2007-02-08 16:41:53 +00:00
else
{
2007-02-10 17:31:25 +00:00
firstControl = path . currentPosition ( ) ;
2007-02-08 16:41:53 +00:00
}
2007-02-10 17:31:25 +00:00
QPointF secondControl = getPointFromString ( & token , isRelative , currPos ) ;
QPointF endPoint = getPointFromString ( & token , isRelative , currPos ) ;
path . cubicTo ( firstControl , secondControl , endPoint ) ;
2007-02-08 15:07:54 +00:00
}
break ;
case ' a ' : // Arc
//TODO Implement Arc drawing
while ( token . type = = abtNumber )
{
2007-02-10 17:31:25 +00:00
/*QPointF rp =*/ getPointFromString ( & token , isRelative , currPos ) ;
2007-02-08 15:07:54 +00:00
/*double r = token.number;*/
nextAbbPathToken ( & token ) ;
2007-02-08 16:20:55 +00:00
/*double fArc = token.number; */
2007-02-08 15:07:54 +00:00
nextAbbPathToken ( & token ) ;
/*double fSweep = token.number; */
nextAbbPathToken ( & token ) ;
2007-02-10 17:31:25 +00:00
/*QPointF point = */ getPointFromString ( & token , isRelative , currPos ) ;
2007-02-08 15:07:54 +00:00
}
break ;
case ' z ' : // Close path
2007-02-10 17:31:25 +00:00
path . closeSubpath ( ) ;
2007-02-08 15:07:54 +00:00
break ;
2006-09-21 11:41:46 +00:00
}
2007-02-08 15:07:54 +00:00
2007-02-08 16:41:53 +00:00
lastCommand = command ;
2006-09-21 11:41:46 +00:00
}
2007-02-10 17:31:25 +00:00
return path ;
2006-09-26 11:53:27 +00:00
}
2007-02-08 15:07:54 +00:00
2007-03-10 10:18:12 +00:00
/**
Parse a " Matrix " attribute string
\ param csv the comma separated list of values
\ return the QMatrix corresponding to the affine transform
given in the attribute
\ see XPS specification 7.4 .1
*/
static QMatrix attsToMatrix ( const QString & csv )
2006-09-26 11:53:27 +00:00
{
QStringList values = csv . split ( ' , ' ) ;
if ( values . count ( ) ! = 6 ) {
return QMatrix ( ) ; // that is an identity matrix - no effect
2007-02-06 07:07:23 +00:00
}
2006-09-26 11:53:27 +00:00
return QMatrix ( values . at ( 0 ) . toDouble ( ) , values . at ( 1 ) . toDouble ( ) ,
values . at ( 2 ) . toDouble ( ) , values . at ( 3 ) . toDouble ( ) ,
2007-02-17 09:42:15 +00:00
values . at ( 4 ) . toDouble ( ) , values . at ( 5 ) . toDouble ( ) ) ;
2006-09-21 11:41:46 +00:00
}
2006-09-11 10:44:17 +00:00
2007-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
{
if ( data [ 0 ] = = ' { ' ) {
//TODO
2007-08-02 23:54:14 +00:00
kDebug ( XpsDebug ) < < " 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
{
if ( data [ 0 ] = = ' { ' ) {
//TODO
2007-08-02 23:54:14 +00:00
kDebug ( XpsDebug ) < < " 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
*/
static QMatrix parseRscRefMatrix ( const QString & data )
2007-02-10 17:31:25 +00:00
{
if ( data [ 0 ] = = ' { ' ) {
//TODO
2007-08-02 23:54:14 +00:00
kDebug ( XpsDebug ) < < " Reference " < < data ;
2007-02-10 17:31:25 +00:00
return QMatrix ( ) ;
} else {
return attsToMatrix ( data ) ;
}
}
2006-09-26 11:53:27 +00:00
XpsHandler : : XpsHandler ( XpsPage * page ) : m_page ( page )
2006-09-21 11:41:46 +00:00
{
2007-02-13 11:13:35 +00:00
m_painter = NULL ;
2006-09-21 11:41:46 +00:00
}
XpsHandler : : ~ XpsHandler ( )
2007-02-09 08:11:47 +00:00
{
delete m_painter ;
}
2006-09-21 11:41:46 +00:00
bool XpsHandler : : startDocument ( )
{
2007-08-02 23:54:14 +00:00
kDebug ( XpsDebug ) < < " start document " < < m_page - > m_fileName ;
2007-02-06 07:07:23 +00:00
m_page - > m_pageImage - > fill ( QColor ( " White " ) . rgba ( ) ) ;
2007-02-10 17:31:25 +00:00
XpsRenderNode node ;
node . name = " document " ;
m_nodes . push ( node ) ;
2006-09-21 11:41:46 +00:00
return true ;
}
bool XpsHandler : : startElement ( const QString & nameSpace ,
const QString & localName ,
const QString & qname ,
const QXmlAttributes & atts )
{
2007-02-10 17:31:25 +00:00
Q_UNUSED ( nameSpace )
Q_UNUSED ( qname )
XpsRenderNode node ;
node . name = localName ;
node . attributes = atts ;
2007-02-12 13:31:16 +00:00
node . data = NULL ;
2007-02-10 17:31:25 +00:00
processStartElement ( node ) ;
m_nodes . push ( node ) ;
2006-09-21 11:41:46 +00:00
return true ;
}
bool XpsHandler : : endElement ( const QString & nameSpace ,
const QString & localName ,
const QString & qname )
{
2007-02-10 17:31:25 +00:00
Q_UNUSED ( nameSpace )
Q_UNUSED ( qname )
XpsRenderNode node = m_nodes . pop ( ) ;
if ( node . name ! = localName ) {
2007-08-02 23:54:14 +00:00
kDebug ( XpsDebug ) < < " 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 )
{
2007-02-17 09:42:15 +00:00
//TODO Currently ignored attributes: BidiLevel, CaretStops, DeviceFontName, IsSideways, Indices, StyleSimulation, Clip, OpacityMask, Name, FixedPage.NavigateURI, xml:lang, x:key
2007-02-10 17:31:25 +00:00
//TODO Currently ignored child elements: Clip, OpacityMask
//Handled separately: RenderTransform
2007-02-17 09:42:15 +00:00
2007-02-10 17:31:25 +00:00
QString att ;
m_painter - > save ( ) ;
// Get font (doesn't work well because qt doesn't allow to load font from file)
2007-02-10 17:41:11 +00:00
// This works despite the fact that font size isn't specified in points as required by qt. It's because I set point size to be equal to drawing unit.
2007-08-02 23:54:14 +00:00
// kDebug(XpsDebug) << "Font Rendering EmSize: " << node.attributes.value("FontRenderingEmSize").toFloat();
2007-02-13 13:26:37 +00:00
QFont font = m_page - > m_file - > getFontByName ( node . attributes . value ( " FontUri " ) , node . attributes . value ( " FontRenderingEmSize " ) . toFloat ( ) ) ;
2007-02-10 17:31:25 +00:00
m_painter - > setFont ( font ) ;
//Origin
QPointF origin ( node . attributes . value ( " OriginX " ) . toDouble ( ) , node . attributes . value ( " OriginY " ) . toDouble ( ) ) ;
//Fill
QBrush brush ;
att = node . attributes . value ( " Fill " ) ;
if ( att . isEmpty ( ) ) {
2007-02-13 21:13:08 +00:00
XpsFill * data = ( XpsFill * ) node . getChildData ( " Glyphs.Fill " ) ;
2007-02-12 13:31:16 +00:00
if ( data ! = NULL ) {
2007-02-13 21:13:08 +00:00
brush = * data ;
delete data ;
2007-02-10 17:31:25 +00:00
} else {
brush = QBrush ( ) ;
2006-09-21 11:41:46 +00:00
}
2007-02-10 17:31:25 +00:00
} else {
2007-02-17 11:18:06 +00:00
brush = parseRscRefColorForBrush ( att ) ;
2007-02-10 17:31:25 +00:00
}
m_painter - > setBrush ( brush ) ;
m_painter - > setPen ( QPen ( brush , 0 ) ) ;
2006-09-21 11:41:46 +00:00
2007-02-17 09:42:15 +00:00
// Opacity
att = node . attributes . value ( " Opacity " ) ;
if ( ! att . isEmpty ( ) ) {
m_painter - > setOpacity ( att . toDouble ( ) ) ;
}
2007-02-10 17:31:25 +00:00
//RenderTransform
att = node . attributes . value ( " RenderTransform " ) ;
if ( ! att . isEmpty ( ) ) {
m_painter - > setWorldMatrix ( parseRscRefMatrix ( att ) , true ) ;
}
m_painter - > drawText ( origin , node . attributes . value ( " UnicodeString " ) ) ;
2007-08-02 23:54:14 +00:00
// kDebug(XpsDebug) << "Glyphs: " << atts.value("Fill") << ", " << atts.value("FontUri");
// kDebug(XpsDebug) << " Origin: " << atts.value("OriginX") << "," << atts.value("OriginY");
// kDebug(XpsDebug) << " 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 )
{
2007-02-12 13:31:16 +00:00
//TODO Ignored child elements: LinearGradientBrush, RadialGradientBrush, VirtualBrush
2007-02-17 09:42:15 +00:00
2007-02-13 21:13:08 +00:00
if ( node . children . size ( ) ! = 1 ) {
2007-08-02 23:54:14 +00:00
kDebug ( XpsDebug ) < < " 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
}
}
void XpsHandler : : processImageBrush ( XpsRenderNode & node )
{
2007-02-12 13:31:16 +00:00
//TODO Ignored attributes: Opacity, x:key, TileMode, ViewBoxUnits, ViewPortUnits
//TODO Check whether transformation works for non standard situations (viewbox different that whole image, Transform different that simple move & scale, Viewport different than [0, 0, 1, 1]
2007-02-17 09:42:15 +00:00
2007-02-12 13:31:16 +00:00
QString att ;
QBrush brush ;
2007-02-17 09:42:15 +00:00
2007-02-12 13:31:16 +00:00
QRectF viewport = stringToRectF ( node . attributes . value ( " Viewport " ) ) ;
QRectF viewbox = stringToRectF ( node . attributes . value ( " Viewbox " ) ) ;
QImage image = m_page - > loadImageFromFile ( node . attributes . value ( " ImageSource " ) ) ;
2007-02-17 09:42:15 +00:00
2007-02-12 13:31:16 +00:00
// Matrix which can transform [0, 0, 1, 1] rectangle to given viewbox
QMatrix viewboxMatrix = QMatrix ( viewbox . width ( ) * image . physicalDpiX ( ) / 96 , 0 , 0 , viewbox . height ( ) * image . physicalDpiY ( ) / 96 , viewbox . x ( ) , viewbox . y ( ) ) ;
// Matrix which can transform [0, 0, 1, 1] rectangle to given viewport
//TODO Take ViewPort into account
QMatrix viewportMatrix ;
att = node . attributes . value ( " Transform " ) ;
if ( att . isEmpty ( ) ) {
2007-02-13 21:13:08 +00:00
XpsMatrixTransform * data = ( XpsMatrixTransform * ) node . getChildData ( " ImageBrush.Transform " ) ;
2007-02-12 13:31:16 +00:00
if ( data ! = NULL ) {
2007-02-13 21:13:08 +00:00
viewportMatrix = * data ;
delete data ;
2007-02-12 13:31:16 +00:00
} else {
viewportMatrix = QMatrix ( ) ;
}
} else {
viewportMatrix = parseRscRefMatrix ( att ) ;
}
viewportMatrix = viewportMatrix * QMatrix ( viewport . width ( ) , 0 , 0 , viewport . height ( ) , viewport . x ( ) , viewbox . y ( ) ) ;
2007-02-17 09:42:15 +00:00
2007-02-12 13:31:16 +00:00
// TODO Brush should work also for QImage, not only QPixmap. But for some images it doesn't work
brush = QBrush ( QPixmap : : fromImage ( image ) ) ;
brush . setMatrix ( viewboxMatrix . inverted ( ) * viewportMatrix ) ;
node . data = new QBrush ( brush ) ;
2007-02-10 17:31:25 +00:00
}
void XpsHandler : : processPath ( XpsRenderNode & node )
{
2007-02-17 10:45:07 +00:00
//TODO Ignored attributes: Clip, OpacityMask, StrokeDashArray, StrokeDashCap, StrokeDashOffset, StrokeEndLineCap, StorkeStartLineCap, StrokeLineJoin, StrokeMiterLimit, Name, FixedPage.NavigateURI, xml:lang, x:key, AutomationProperties.Name, AutomationProperties.HelpText, SnapsToDevicePixels
2007-02-10 17:31:25 +00:00
//TODO Ignored child elements: RenderTransform, Clip, OpacityMask, Stroke, Data
// Handled separately: RenderTransform
m_painter - > save ( ) ;
QString att ;
QPainterPath path ;
// Get path
att = node . attributes . value ( " Data " ) ;
if ( ! att . isEmpty ( ) ) {
path = parseAbbreviatedPathData ( att ) ;
} else {
2007-02-17 09:42:15 +00:00
path = QPainterPath ( ) ; //TODO
2007-02-10 17:31:25 +00:00
}
2007-02-17 09:42:15 +00:00
2007-02-10 17:31:25 +00:00
// Set Fill
att = node . attributes . value ( " Fill " ) ;
QBrush brush ;
if ( ! att . isEmpty ( ) ) {
2007-02-17 11:18:06 +00:00
brush = parseRscRefColorForBrush ( att ) ;
2007-02-10 17:31:25 +00:00
} else {
2007-02-13 21:13:08 +00:00
XpsFill * data = ( XpsFill * ) node . getChildData ( " Path.Fill " ) ;
2007-02-12 13:31:16 +00:00
if ( data ! = NULL ) {
2007-02-13 21:13:08 +00:00
brush = * data ;
delete data ;
2007-02-10 17:31:25 +00:00
} else {
brush = QBrush ( ) ;
}
}
m_painter - > setBrush ( brush ) ;
2007-02-17 10:45:07 +00:00
// Stroke (pen)
// We don't handle the child elements (Path.Stroke) yet.
att = node . attributes . value ( " Stroke " ) ;
QPen pen ( Qt : : transparent ) ;
if ( ! att . isEmpty ( ) ) {
2007-02-17 11:18:06 +00:00
pen = parseRscRefColorForPen ( att ) ;
2007-02-17 10:45:07 +00:00
}
att = node . attributes . value ( " StrokeThickness " ) ;
if ( ! att . isEmpty ( ) ) {
bool ok = false ;
int thickness = att . toInt ( & ok ) ;
if ( ok )
pen . setWidth ( thickness ) ;
}
m_painter - > setPen ( pen ) ;
2007-02-10 17:31:25 +00:00
2007-02-17 09:42:15 +00:00
// Opacity
att = node . attributes . value ( " Opacity " ) ;
if ( ! att . isEmpty ( ) ) {
m_painter - > setOpacity ( att . toDouble ( ) ) ;
}
2007-02-10 17:31:25 +00:00
// RenderTransform
att = node . attributes . value ( " RenderTransform " ) ;
if ( ! att . isEmpty ( ) ) {
m_painter - > setWorldMatrix ( parseRscRefMatrix ( att ) , true ) ;
}
2007-03-11 01:14:33 +00:00
m_painter - > drawPath ( path ) ; //TODO Valgrind sometimes say that path drawing depends on uninitialized value in blend_texture
2007-02-10 17:31:25 +00:00
m_painter - > restore ( ) ;
}
void XpsHandler : : processStartElement ( XpsRenderNode & node )
{
if ( node . name = = " Canvas " ) {
m_painter - > save ( ) ;
}
}
void XpsHandler : : processEndElement ( XpsRenderNode & node )
{
if ( node . name = = " Glyphs " ) {
processGlyph ( node ) ;
} else if ( node . name = = " Path " ) {
processPath ( node ) ;
} else if ( node . name = = " MatrixTransform " ) {
//TODO Ignoring x:key
2007-02-17 09:42:15 +00:00
node . data = new QMatrix ( attsToMatrix ( node . attributes . value ( " Matrix " ) ) ) ;
2007-02-10 17:31:25 +00:00
} else if ( ( node . name = = " Canvas.RenderTransform " ) | | ( node . name = = " Glyphs.RenderTransform " ) | | ( node . name = = " Path.RenderTransform " ) ) {
2007-02-13 21:13:08 +00:00
XpsMatrixTransform * data = ( XpsMatrixTransform * ) node . getRequiredChildData ( " MatrixTransform " ) ;
2007-02-12 13:31:16 +00:00
if ( data ! = NULL ) {
2007-02-13 21:13:08 +00:00
m_painter - > setWorldMatrix ( * data , true ) ;
delete data ;
2007-02-10 17:31:25 +00:00
}
} else if ( node . name = = " Canvas " ) {
2006-09-21 11:41:46 +00:00
m_painter - > restore ( ) ;
2007-02-10 17:31:25 +00:00
} else if ( ( node . name = = " Path.Fill " ) | | ( node . name = = " Glyphs.Fill " ) ) {
processFill ( node ) ;
} else if ( node . name = = " SolidColorBrush " ) {
//TODO Ignoring opacity, x:key
2007-02-13 21:13:08 +00:00
node . data = new QBrush ( QColor ( hexToRgba ( node . attributes . value ( " Color " ) . toLatin1 ( ) ) ) ) ;
2007-02-10 17:31:25 +00:00
} else if ( node . name = = " ImageBrush " ) {
processImageBrush ( node ) ;
2007-02-12 13:31:16 +00:00
} else if ( node . name = = " ImageBrush.Transform " ) {
node . data = node . getRequiredChildData ( " MatrixTransform " ) ;
2007-02-10 17:31:25 +00:00
} else {
2007-08-02 23:54:14 +00:00
//kDebug(XpsDebug) << "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
{
2007-02-08 18:09:15 +00:00
m_pageImage = NULL ;
2007-08-02 23:54:14 +00:00
// kDebug(XpsDebug) << "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 ;
2007-03-10 05:52:56 +00:00
xml . addData ( pageFile - > data ( ) ) ;
2007-03-08 03:11:04 +00:00
while ( ! xml . atEnd ( ) )
2007-02-08 16:41:53 +00:00
{
2007-03-08 03:11:04 +00:00
xml . readNext ( ) ;
if ( xml . isStartElement ( ) & & ( xml . name ( ) = = " FixedPage " ) )
{
QXmlStreamAttributes attributes = xml . attributes ( ) ;
m_pageSize . setWidth ( attributes . value ( " Width " ) . toString ( ) . toInt ( ) ) ;
m_pageSize . setHeight ( attributes . value ( " Height " ) . toString ( ) . toInt ( ) ) ;
break ;
}
2007-02-08 16:41:53 +00:00
}
2007-03-08 03:11:04 +00:00
if ( xml . error ( ) )
2007-02-08 16:41:53 +00:00
{
2007-08-02 23:54:14 +00:00
kDebug ( XpsDebug ) < < " 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
{
2007-02-17 09:42:15 +00:00
2007-02-08 18:09:15 +00:00
if ( ( m_pageImage = = NULL ) | | ( m_pageImage - > size ( ) ! = p - > size ( ) ) ) {
delete m_pageImage ;
2007-02-10 17:31:25 +00:00
m_pageImage = new QImage ( p - > size ( ) , QImage : : Format_ARGB32 ) ;
2007-03-08 03:11:04 +00:00
// Set one point = one drawing unit. Useful for fonts, because xps specifies font size using drawing units, not points as usual
2007-02-10 17:41:11 +00:00
m_pageImage - > setDotsPerMeterX ( 2835 ) ;
m_pageImage - > setDotsPerMeterY ( 2835 ) ;
2007-02-08 18:09:15 +00:00
m_pageIsRendered = false ;
}
2006-09-21 11:41:46 +00:00
if ( ! m_pageIsRendered ) {
2006-09-26 11:53:27 +00:00
XpsHandler * handler = new XpsHandler ( this ) ;
2007-02-13 11:13:35 +00:00
handler - > m_painter = new QPainter ( m_pageImage ) ;
2007-02-08 18:09:15 +00:00
handler - > m_painter - > setWorldMatrix ( QMatrix ( ) . scale ( ( qreal ) p - > size ( ) . width ( ) / size ( ) . width ( ) , ( qreal ) p - > size ( ) . height ( ) / size ( ) . height ( ) ) ) ;
2006-09-21 11:41:46 +00:00
QXmlSimpleReader * parser = new QXmlSimpleReader ( ) ;
parser - > setContentHandler ( handler ) ;
parser - > setErrorHandler ( handler ) ;
2007-02-13 13:26:37 +00:00
const KZipFileEntry * pageFile = static_cast < const KZipFileEntry * > ( m_file - > xpsArchive ( ) - > directory ( ) - > entry ( m_fileName ) ) ;
2007-04-19 14:38:03 +00:00
QByteArray data = pageFile - > data ( ) ;
QBuffer * buffer = new QBuffer ( & data ) ;
QXmlInputSource * source = new QXmlInputSource ( buffer ) ;
2006-09-21 11:41:46 +00:00
bool ok = parser - > parse ( source ) ;
2007-08-02 23:54:14 +00:00
kDebug ( XpsDebug ) < < " Parse result: " < < ok ;
2006-09-21 11:41:46 +00:00
delete source ;
delete parser ;
delete handler ;
2007-04-19 14:38:03 +00:00
delete buffer ;
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
2006-09-11 10:44:17 +00:00
QSize XpsPage : : size ( ) const
{
return m_pageSize ;
}
2007-02-13 13:26:37 +00:00
QFont XpsFile : : getFontByName ( const QString & fileName , float size )
2007-02-06 07:07:23 +00:00
{
2007-08-02 23:54:14 +00:00
// kDebug(XpsDebug) << "trying to get font: " << fileName << ", size: " << size;
2007-03-10 10:18:12 +00:00
2007-02-06 07:07:23 +00:00
int index = m_fontCache . value ( fileName , - 1 ) ;
2007-02-17 09:42:15 +00:00
if ( index = = - 1 )
2007-02-06 07:07:23 +00:00
{
index = loadFontByName ( fileName ) ;
m_fontCache [ fileName ] = index ;
}
2007-02-13 13:26:37 +00:00
QString fontFamily = m_fontDatabase . applicationFontFamilies ( index ) . at ( 0 ) ;
QString fontStyle = m_fontDatabase . styles ( fontFamily ) . at ( 0 ) ;
QFont font = m_fontDatabase . font ( fontFamily , fontStyle , qRound ( size ) ) ;
return font ;
2007-02-06 07:07:23 +00:00
}
2007-02-13 13:26:37 +00:00
int XpsFile : : loadFontByName ( const QString & fileName )
2006-09-21 11:41:46 +00:00
{
2007-08-02 23:54:14 +00:00
// kDebug(XpsDebug) << "font file name: " << fileName;
2006-09-21 11:41:46 +00:00
2007-02-13 13:26:37 +00:00
const KZipFileEntry * fontFile = static_cast < const KZipFileEntry * > ( m_xpsArchive - > directory ( ) - > entry ( fileName ) ) ;
2006-09-21 11:41:46 +00:00
QByteArray fontData = fontFile - > data ( ) ; // once per file, according to the docs
2006-09-23 00:42:42 +00:00
int result = m_fontDatabase . addApplicationFontFromData ( fontData ) ;
2006-09-21 11:41:46 +00:00
if ( - 1 = = result ) {
2007-02-17 09:42:15 +00:00
// Try to deobfuscate font
2007-02-08 16:41:53 +00:00
// TODO Use deobfuscation depending on font content type, don't do it always when standard loading fails
2007-02-17 09:42:15 +00:00
2007-02-08 16:41:53 +00:00
QFileInfo * fileInfo = new QFileInfo ( fileName ) ;
QString baseName = fileInfo - > baseName ( ) ;
delete fileInfo ;
2007-02-06 07:07:23 +00:00
unsigned short guid [ 16 ] ;
if ( ! parseGUID ( baseName , guid ) )
{
2007-08-02 23:54:14 +00:00
kDebug ( XpsDebug ) < < " 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 )
2007-02-06 07:07:23 +00:00
{
2007-08-02 23:54:14 +00:00
kDebug ( XpsDebug ) < < " 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
2007-08-02 23:54:14 +00:00
// kDebug(XpsDebug) << "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
{
2007-08-02 23:54:14 +00:00
// kDebug(XpsDebug) << "image file name: " << fileName;
2006-09-21 11:41:46 +00:00
2007-02-13 13:26:37 +00:00
const KZipFileEntry * imageFile = static_cast < const KZipFileEntry * > ( m_file - > xpsArchive ( ) - > directory ( ) - > entry ( fileName ) ) ;
2006-09-21 11:41:46 +00:00
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 .
I ' ve posted a bug to qt .
*/
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 ) ) ;
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 ( )
{
2007-08-02 23:54:14 +00:00
// kDebug(XpsDebug) << "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 ;
xml . addData ( pageFile - > data ( ) ) ;
QMatrix matrix = QMatrix ( ) ;
QStack < QMatrix > matrices ;
matrices . push ( QMatrix ( ) ) ;
bool useMatrix = false ;
QXmlStreamAttributes glyphsAtts ;
while ( ! xml . atEnd ( ) ) {
xml . readNext ( ) ;
if ( xml . isStartElement ( ) ) {
if ( xml . name ( ) = = " Canvas " ) {
matrices . push ( matrix ) ;
QString att = xml . attributes ( ) . value ( " RenderTransform " ) . toString ( ) ;
if ( ! att . isEmpty ( ) ) {
matrix = parseRscRefMatrix ( att ) * matrix ;
}
} else if ( ( xml . name ( ) = = " Canvas.RenderTransform " ) | | ( xml . name ( ) = = " Glyphs.RenderTransform " ) ) {
useMatrix = true ;
2007-03-11 01:14:33 +00:00
} else if ( xml . name ( ) = = " MatrixTransform " ) {
if ( useMatrix ) {
matrix = attsToMatrix ( xml . attributes ( ) . value ( " Matrix " ) . toString ( ) ) * matrix ;
}
2007-03-10 10:18:12 +00:00
} else if ( xml . name ( ) = = " Glyphs " ) {
matrices . push ( matrix ) ;
glyphsAtts = xml . attributes ( ) ;
} else if ( ( xml . name ( ) = = " Path " ) | | ( xml . name ( ) = = " Path.Fill " ) | | ( xml . name ( ) = = " SolidColorBrush " )
| | ( xml . name ( ) = = " ImageBrush " ) | | ( xml . name ( ) = = " ImageBrush.Transform " )
| | ( xml . name ( ) = = " Path.OpacityMask " ) ) {
// those are only graphical - no use in text handling
} else if ( xml . name ( ) = = " FixedPage " ) {
// not useful for text extraction
} else {
2007-08-02 23:54:14 +00:00
kDebug ( XpsDebug ) < < " Unhandled element in Text Extraction start: " < < xml . name ( ) . toString ( ) ;
2007-03-10 10:18:12 +00:00
}
} else if ( xml . isEndElement ( ) ) {
if ( xml . name ( ) = = " Canvas " ) {
matrix = matrices . pop ( ) ;
} else if ( ( xml . name ( ) = = " Canvas.RenderTransform " ) | | ( xml . name ( ) = = " Glyphs.RenderTransform " ) ) {
useMatrix = false ;
} else if ( xml . name ( ) = = " MatrixTransform " ) {
// not clear if we need to do anything here yet.
} else if ( xml . name ( ) = = " Glyphs " ) {
QString att = glyphsAtts . value ( " RenderTransform " ) . toString ( ) ;
if ( ! att . isEmpty ( ) ) {
matrix = parseRscRefMatrix ( att ) * matrix ;
}
QString text = glyphsAtts . value ( " UnicodeString " ) . toString ( ) ;
// Get font (doesn't work well because qt doesn't allow to load font from file)
QFont font = m_file - > getFontByName ( glyphsAtts . value ( " FontUri " ) . toString ( ) ,
glyphsAtts . value ( " FontRenderingEmSize " ) . toString ( ) . toFloat ( ) * 72 / 96 ) ;
QFontMetrics metrics = QFontMetrics ( font ) ;
// Origin
QPointF origin ( glyphsAtts . value ( " OriginX " ) . toString ( ) . toDouble ( ) ,
glyphsAtts . value ( " OriginY " ) . toString ( ) . toDouble ( ) ) ;
int lastWidth = 0 ;
for ( int i = 0 ; i < text . length ( ) ; i + + ) {
int width = metrics . width ( text , i + 1 ) ;
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 ( ) ;
} else if ( ( xml . name ( ) = = " Path " ) | | ( xml . name ( ) = = " Path.Fill " ) | | ( xml . name ( ) = = " SolidColorBrush " )
| | ( xml . name ( ) = = " ImageBrush " ) | | ( xml . name ( ) = = " ImageBrush.Transform " )
| | ( xml . name ( ) = = " Path.OpacityMask " ) ) {
// those are only graphical - no use in text handling
} else if ( xml . name ( ) = = " FixedPage " ) {
// not useful for text extraction
} else {
2007-08-02 23:54:14 +00:00
kDebug ( XpsDebug ) < < " Unhandled element in Text Extraction end: " < < xml . name ( ) . toString ( ) ;
2007-03-10 10:18:12 +00:00
}
}
}
if ( xml . error ( ) ) {
2007-08-02 23:54:14 +00:00
kDebug ( XpsDebug ) < < " 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 )
{
2007-08-02 23:54:14 +00:00
kDebug ( XpsDebug ) < < " 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 ( ) ) {
if ( xml . name ( ) = = " DocumentStructure " ) {
// just a container for optional outline and story elements - nothing to do here
} else if ( xml . name ( ) = = " DocumentStructure.Outline " ) {
2007-08-02 23:54:14 +00:00
kDebug ( XpsDebug ) < < " found DocumentStructure.Outline " ;
2007-03-10 05:52:56 +00:00
} else if ( xml . name ( ) = = " DocumentOutline " ) {
2007-08-02 23:54:14 +00:00
kDebug ( XpsDebug ) < < " found DocumentOutline " ;
2007-02-19 10:43:40 +00:00
m_docStructure = new Okular : : DocumentSynopsis ;
2007-03-10 05:52:56 +00:00
} else if ( xml . name ( ) = = " OutlineEntry " ) {
m_haveDocumentStructure = true ;
QXmlStreamAttributes attributes = xml . attributes ( ) ;
int outlineLevel = attributes . value ( " OutlineLevel " ) . toString ( ) . toInt ( ) ;
QString description = attributes . value ( " Description " ) . toString ( ) ;
QDomElement synopsisElement = m_docStructure - > createElement ( description ) ;
synopsisElement . setAttribute ( " OutlineLevel " , outlineLevel ) ;
QString target = attributes . value ( " OutlineTarget " ) . toString ( ) ;
int hashPosition = target . lastIndexOf ( ' # ' ) ;
target = target . mid ( hashPosition + 1 ) ;
2007-08-02 23:54:14 +00:00
// kDebug(XpsDebug) << "target: " << target;
2007-03-10 05:52:56 +00:00
Okular : : DocumentViewport viewport ;
viewport . pageNumber = m_docStructurePageMap . value ( target ) ;
synopsisElement . setAttribute ( " Viewport " , viewport . toString ( ) ) ;
if ( outlineLevel = = 1 ) {
// kDebug(XpsDebug) << "Description: "
// << outlineEntryElement.attribute( "Description" ) << endl;
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 ( ) )
2007-02-19 10:43:40 +00:00
{
2007-03-10 05:52:56 +00:00
if ( maybeParentNode . toElement ( ) . attribute ( " OutlineLevel " ) . toInt ( ) = = ( outlineLevel - 1 ) ) {
// 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
}
}
2007-03-10 05:52:56 +00:00
} else if ( xml . name ( ) = = " Story " ) {
// we need to handle Story here, but I have no idea what to do with it.
} else if ( xml . name ( ) = = " StoryFragment " ) {
// we need to handle StoryFragment here, but I have no idea what to do with it.
} else if ( xml . name ( ) = = " StoryFragmentReference " ) {
// 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 {
2007-08-02 23:54:14 +00:00
kDebug ( XpsDebug ) < < " 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 ;
}
XpsDocument : : XpsDocument ( XpsFile * file , const QString & fileName ) : m_file ( file ) , m_haveDocumentStructure ( false )
2006-09-11 10:44:17 +00:00
{
2007-08-02 23:54:14 +00:00
kDebug ( XpsDebug ) < < " document file name: " < < fileName ;
2006-09-12 10:31:09 +00:00
2007-02-13 13:26:37 +00:00
const KZipFileEntry * documentFile = static_cast < const KZipFileEntry * > ( file - > xpsArchive ( ) - > directory ( ) - > entry ( fileName ) ) ;
2006-09-11 10:44:17 +00:00
2007-03-10 05:52:56 +00:00
QXmlStreamReader docXml ;
docXml . addData ( documentFile - > data ( ) ) ;
while ( ! docXml . atEnd ( ) ) {
docXml . readNext ( ) ;
if ( docXml . isStartElement ( ) ) {
if ( docXml . name ( ) = = " PageContent " ) {
QString pagePath = docXml . attributes ( ) . value ( " Source " ) . toString ( ) ;
2007-08-02 23:54:14 +00:00
kDebug ( XpsDebug ) < < " Page Path: " < < pagePath ;
2006-09-12 10:31:09 +00:00
if ( pagePath . startsWith ( ' / ' ) = = false ) {
int offset = fileName . lastIndexOf ( ' / ' ) ;
QString thisDir = fileName . mid ( 0 , offset ) + ' / ' ;
pagePath . prepend ( thisDir ) ;
}
2007-02-13 13:26:37 +00:00
XpsPage * page = new XpsPage ( file , pagePath ) ;
2006-09-11 10:44:17 +00:00
m_pages . append ( page ) ;
2007-03-10 05:52:56 +00:00
} else if ( docXml . name ( ) = = " PageContent.LinkTargets " ) {
// do nothing - wait for the real LinkTarget elements
} else if ( docXml . name ( ) = = " LinkTarget " ) {
QString targetName = docXml . attributes ( ) . value ( " Name " ) . toString ( ) ;
if ( ! targetName . isEmpty ( ) ) {
m_docStructurePageMap [ targetName ] = m_pages . count ( ) - 1 ;
2007-02-19 10:43:40 +00:00
}
2007-03-10 05:52:56 +00:00
} else if ( docXml . name ( ) = = " FixedDocument " ) {
// we just ignore this - it is just a container
2006-09-11 10:44:17 +00:00
} else {
2007-08-02 23:54:14 +00:00
kDebug ( XpsDebug ) < < " 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 ( ) ) {
2007-08-02 23:54:14 +00:00
kDebug ( XpsDebug ) < < " 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.
QString maybeDocumentRelationshipPath = fileName ;
// trim off the old filename
int slashPosition = maybeDocumentRelationshipPath . lastIndexOf ( ' / ' ) ;
maybeDocumentRelationshipPath . truncate ( slashPosition ) ;
// add in the path to the document relationships
maybeDocumentRelationshipPath . append ( " /_rels/FixedDoc.fdoc.rels " ) ;
const KZipFileEntry * relFile = static_cast < const KZipFileEntry * > ( file - > xpsArchive ( ) - > directory ( ) - > entry ( maybeDocumentRelationshipPath ) ) ;
QString documentStructureFile ;
if ( relFile ) {
2007-03-10 05:52:56 +00:00
QXmlStreamReader xml ;
xml . addData ( relFile - > data ( ) ) ;
while ( ! xml . atEnd ( ) )
{
xml . readNext ( ) ;
if ( xml . isStartElement ( ) & & ( xml . name ( ) = = " Relationship " ) ) {
QXmlStreamAttributes attributes = xml . attributes ( ) ;
if ( attributes . value ( " Type " ) . toString ( ) = = " http://schemas.microsoft.com/xps/2005/06/documentstructure " ) {
documentStructureFile = attributes . value ( " Target " ) . toString ( ) ;
} else {
kDebug ( XpsDebug ) < < " Unknown document relationships element: "
< < attributes . value ( " Type " ) . toString ( ) < < " : "
< < attributes . value ( " Target " ) . toString ( ) < < endl ;
2007-02-19 10:43:40 +00:00
}
}
}
2007-03-10 05:52:56 +00:00
if ( xml . error ( ) ) {
kDebug ( XpsDebug ) < < " Could not parse XPS page relationships file ( "
< < maybeDocumentRelationshipPath
< < " ) - " < < xml . errorString ( ) < < endl ;
}
} else { // the page relationship file didn't exist in the zipfile
2007-02-19 10:43:40 +00:00
// this isn't fatal
2007-08-02 23:54:14 +00:00
kDebug ( XpsDebug ) < < " Could not open Document relationship file from " < < maybeDocumentRelationshipPath ;
2007-02-19 10:43:40 +00:00
}
if ( ! documentStructureFile . isEmpty ( ) )
{
2007-08-02 23:54:14 +00:00
// kDebug(XpsDebug) << "Document structure filename: " << documentStructureFile;
2007-02-19 10:43:40 +00:00
// make the document path absolute
if ( documentStructureFile . startsWith ( ' / ' ) )
{
// it is already absolute, don't do anything
} else
{
// we reuse the relationship string
maybeDocumentRelationshipPath . truncate ( slashPosition ) ;
documentStructureFile . prepend ( maybeDocumentRelationshipPath + ' / ' ) ;
}
2007-08-02 23:54:14 +00:00
// kDebug(XpsDebug) << "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 ( )
{
for ( int i = 0 ; i < m_pages . size ( ) ; i + + ) {
delete m_pages . at ( i ) ;
}
m_pages . clear ( ) ;
2007-02-19 10:43:40 +00:00
2007-02-20 09:29:32 +00:00
if ( m_docStructure )
2007-02-19 10:43:40 +00:00
delete m_docStructure ;
2007-02-09 08:11:47 +00:00
}
2006-09-11 10:44:17 +00:00
int XpsDocument : : numPages ( ) const
{
return m_pages . size ( ) ;
}
XpsPage * XpsDocument : : page ( int pageNum ) const
{
return m_pages . at ( pageNum ) ;
}
2006-09-11 11:05:40 +00:00
XpsFile : : XpsFile ( ) : m_docInfo ( 0 )
2006-09-11 10:44:17 +00:00
{
2006-09-19 09:09:30 +00:00
}
2006-09-11 10:44:17 +00:00
XpsFile : : ~ XpsFile ( )
{
2007-02-13 13:26:37 +00:00
m_fontCache . clear ( ) ;
2007-02-13 21:13:08 +00:00
m_fontDatabase . removeAllApplicationFonts ( ) ;
2006-09-19 09:09:30 +00:00
}
2006-09-11 10:44:17 +00:00
bool XpsFile : : loadDocument ( const QString & filename )
{
2007-02-13 13:26:37 +00:00
m_xpsArchive = new KZip ( filename ) ;
if ( m_xpsArchive - > open ( QIODevice : : ReadOnly ) = = true ) {
2007-08-02 23:54:14 +00:00
kDebug ( XpsDebug ) < < " Successful open of " < < m_xpsArchive - > fileName ( ) ;
2006-09-11 10:44:17 +00:00
} else {
2007-08-02 23:54:14 +00:00
kDebug ( XpsDebug ) < < " 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
2007-02-13 13:26:37 +00:00
const KZipFileEntry * relFile = static_cast < const KZipFileEntry * > ( m_xpsArchive - > directory ( ) - > entry ( " _rels/.rels " ) ) ;
2006-09-11 10:44:17 +00:00
if ( ! relFile ) {
// this might occur if we can't read the zip directory, or it doesn't have the relationships entry
return false ;
}
2007-03-10 05:52:56 +00:00
QXmlStreamReader relXml ;
relXml . addData ( relFile - > data ( ) ) ;
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 ( ) ) {
if ( relXml . name ( ) = = " Relationship " ) {
QXmlStreamAttributes attributes = relXml . attributes ( ) ;
QString type = attributes . value ( " Type " ) . toString ( ) ;
QString target = attributes . value ( " Target " ) . toString ( ) ;
if ( " http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail " = = type ) {
m_thumbnailFileName = target ;
} else if ( " http://schemas.microsoft.com/xps/2005/06/fixedrepresentation " = = type ) {
fixedRepresentationFileName = target ;
} else if ( " http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties " = = type ) {
m_corePropertiesFileName = target ;
} else if ( " http://schemas.openxmlformats.org/package/2006/relationships/digital-signature/origin " = = type ) {
m_signatureOrigin = target ;
} else {
2007-08-02 23:54:14 +00:00
kDebug ( XpsDebug ) < < " Unknown relationships element: " < < type < < " : " < < target ;
2007-03-10 05:52:56 +00:00
}
} else if ( relXml . name ( ) = = " Relationships " ) {
// nothing to do here - this is just the container level
2006-09-11 10:44:17 +00:00
} else {
2007-08-02 23:54:14 +00:00
kDebug ( XpsDebug ) < < " 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 ( ) ) {
2007-08-02 23:54:14 +00:00
kDebug ( XpsDebug ) < < " 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 ;
}
2007-02-13 13:26:37 +00:00
const KZipFileEntry * fixedRepFile = static_cast < const KZipFileEntry * > ( m_xpsArchive - > directory ( ) - > entry ( fixedRepresentationFileName ) ) ;
2006-09-11 10:44:17 +00:00
2007-03-10 05:52:56 +00:00
QXmlStreamReader fixedRepXml ;
fixedRepXml . addData ( fixedRepFile - > data ( ) ) ;
2006-09-11 10:44:17 +00:00
2007-03-10 05:52:56 +00:00
while ( ! fixedRepXml . atEnd ( ) )
{
fixedRepXml . readNext ( ) ;
if ( fixedRepXml . isStartElement ( ) ) {
if ( fixedRepXml . name ( ) = = " DocumentReference " ) {
XpsDocument * doc = new XpsDocument ( this , fixedRepXml . attributes ( ) . value ( " Source " ) . toString ( ) ) ;
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 ) ;
2007-03-10 05:52:56 +00:00
} else if ( fixedRepXml . name ( ) = = " FixedDocumentSequence " ) {
// 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 {
2007-08-02 23:54:14 +00:00
kDebug ( XpsDebug ) < < " 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 ( ) ) {
2007-08-02 23:54:14 +00:00
kDebug ( XpsDebug ) < < " 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 ;
}
2006-09-21 08:45:36 +00:00
const Okular : : DocumentInfo * XpsFile : : generateDocumentInfo ( )
2006-09-11 10:44:17 +00:00
{
if ( m_docInfo )
return m_docInfo ;
2006-09-21 08:45:36 +00:00
m_docInfo = new Okular : : DocumentInfo ( ) ;
2006-09-11 10:44:17 +00:00
2007-05-13 12:54:46 +00:00
m_docInfo - > set ( Okular : : DocumentInfo : : MimeType , " application/vnd.ms-xpsdocument " ) ;
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 ( ) )
{
if ( xml . name ( ) = = " title " ) {
2007-05-13 12:54:46 +00:00
m_docInfo - > set ( Okular : : DocumentInfo : : Title , xml . readElementText ( ) ) ;
2007-03-08 03:11:04 +00:00
} else if ( xml . name ( ) = = " subject " ) {
2007-05-13 12:54:46 +00:00
m_docInfo - > set ( Okular : : DocumentInfo : : Subject , xml . readElementText ( ) ) ;
2007-03-08 03:11:04 +00:00
} else if ( xml . name ( ) = = " description " ) {
2007-05-13 12:54:46 +00:00
m_docInfo - > set ( Okular : : DocumentInfo : : Description , xml . readElementText ( ) ) ;
2007-03-08 03:11:04 +00:00
} else if ( xml . name ( ) = = " creator " ) {
2007-05-13 12:54:46 +00:00
m_docInfo - > set ( Okular : : DocumentInfo : : Creator , xml . readElementText ( ) ) ;
2007-03-08 03:11:04 +00:00
} else if ( xml . name ( ) = = " category " ) {
2007-05-13 12:54:46 +00:00
m_docInfo - > set ( Okular : : DocumentInfo : : Category , xml . readElementText ( ) ) ;
2007-03-08 03:11:04 +00:00
} else if ( xml . name ( ) = = " created " ) {
QDateTime createdDate = QDateTime : : fromString ( xml . readElementText ( ) , " yyyy-MM-ddThh:mm:ssZ " ) ;
2007-05-13 12:54:46 +00:00
m_docInfo - > set ( Okular : : DocumentInfo : : CreationDate , KGlobal : : locale ( ) - > formatDateTime ( createdDate , KLocale : : LongDate , true ) ) ;
2007-03-08 03:11:04 +00:00
} else if ( xml . name ( ) = = " modified " ) {
QDateTime modifiedDate = QDateTime : : fromString ( xml . readElementText ( ) , " yyyy-MM-ddThh:mm:ssZ " ) ;
2007-05-13 12:54:46 +00:00
m_docInfo - > set ( Okular : : DocumentInfo : : ModificationDate , KGlobal : : locale ( ) - > formatDateTime ( modifiedDate , KLocale : : LongDate , true ) ) ;
2007-03-08 03:11:04 +00:00
} else if ( xml . name ( ) = = " keywords " ) {
2007-05-13 12:54:46 +00:00
m_docInfo - > set ( Okular : : DocumentInfo : : Keywords , xml . readElementText ( ) ) ;
2006-09-11 10:44:17 +00:00
}
}
2007-03-08 03:11:04 +00:00
}
if ( xml . error ( ) )
{
2007-08-02 23:54:14 +00:00
kDebug ( XpsDebug ) < < " Could not parse XPS core properties: " < < xml . errorString ( ) ;
2006-09-11 10:44:17 +00:00
}
} else {
2007-08-02 23:54:14 +00:00
kDebug ( XpsDebug ) < < " No core properties filename " ;
2006-09-11 10:44:17 +00:00
}
2007-05-13 12:54:46 +00:00
m_docInfo - > set ( Okular : : DocumentInfo : : Pages , QString : : number ( numPages ( ) ) ) ;
2006-09-11 10:44:17 +00:00
return m_docInfo ;
}
bool XpsFile : : closeDocument ( )
{
if ( m_docInfo )
delete m_docInfo ;
m_docInfo = 0 ;
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
}
2006-10-22 11:25:08 +00:00
XpsGenerator : : XpsGenerator ( )
2007-02-10 17:47:44 +00:00
: Okular : : Generator ( ) , m_xpsFile ( 0 )
2006-09-11 10:44:17 +00:00
{
2007-02-13 11:13:35 +00:00
setFeature ( TextExtraction ) ;
2007-07-14 14:00:19 +00:00
KAboutData * about = new KAboutData (
2007-08-28 23:27:37 +00:00
" okular_xps " ,
" okular_xps " ,
2007-07-14 14:00:19 +00:00
ki18n ( " XPS Backend " ) ,
" 0.1 " ,
ki18n ( " An XPS backend " ) ,
KAboutData : : License_GPL ,
ki18n ( " © 2006-2007 Brad Hards \n "
" © 2007 Jiri Klement " )
) ;
about - > addAuthor ( ki18n ( " Brad Hards " ) , KLocalizedString ( ) , " bradh@frogmouth.net " ) ;
about - > addAuthor ( ki18n ( " Jiri Klement " ) , KLocalizedString ( ) , " jiri.klement@gmail.com " ) ;
setAboutData ( about ) ;
2006-09-11 10:44:17 +00:00
}
XpsGenerator : : ~ XpsGenerator ( )
{
}
2006-09-21 08:45:36 +00:00
bool XpsGenerator : : loadDocument ( const QString & fileName , QVector < Okular : : Page * > & pagesVector )
2006-09-11 10:44:17 +00:00
{
2007-02-06 07:07:23 +00:00
m_xpsFile = new XpsFile ( ) ;
2006-09-11 10:44:17 +00:00
m_xpsFile - > loadDocument ( fileName ) ;
pagesVector . resize ( m_xpsFile - > numPages ( ) ) ;
int pagesVectorOffset = 0 ;
for ( int docNum = 0 ; docNum < m_xpsFile - > numDocuments ( ) ; + + docNum )
{
XpsDocument * doc = m_xpsFile - > document ( docNum ) ;
for ( int pageNum = 0 ; pageNum < doc - > numPages ( ) ; + + pageNum )
{
QSize pageSize = doc - > page ( pageNum ) - > size ( ) ;
2007-01-05 17:09:47 +00:00
pagesVector [ pagesVectorOffset ] = new Okular : : Page ( pagesVectorOffset , pageSize . width ( ) , pageSize . height ( ) , Okular : : Rotation0 ) ;
2006-09-11 10:44:17 +00:00
+ + pagesVectorOffset ;
}
}
return true ;
}
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 ;
m_xpsFile = 0 ;
2006-09-11 10:44:17 +00:00
return true ;
}
2007-01-31 18:31:19 +00:00
QImage XpsGenerator : : image ( Okular : : PixmapRequest * request )
2006-09-11 10:44:17 +00:00
{
2007-02-08 18:09:15 +00:00
QSize size ( ( int ) request - > width ( ) , ( int ) request - > height ( ) ) ;
2006-09-26 11:53:27 +00:00
QImage image ( size , QImage : : Format_RGB32 ) ;
2006-10-25 15:35:53 +00:00
XpsPage * pageToRender = m_xpsFile - > page ( request - > page ( ) - > number ( ) ) ;
2006-09-26 11:53:27 +00:00
pageToRender - > renderToImage ( & image ) ;
2007-01-31 18:31:19 +00:00
return image ;
2006-09-11 10:44:17 +00:00
}
2007-02-13 11:13:35 +00:00
Okular : : TextPage * XpsGenerator : : textPage ( Okular : : Page * page )
{
XpsPage * xpsPage = m_xpsFile - > page ( page - > number ( ) ) ;
return xpsPage - > textPage ( ) ;
}
2006-09-21 08:45:36 +00:00
const Okular : : DocumentInfo * XpsGenerator : : generateDocumentInfo ( )
2006-09-11 10:44:17 +00:00
{
2007-08-02 23:54:14 +00:00
kDebug ( XpsDebug ) < < " 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 ( )
{
2007-08-02 23:54:14 +00:00
kDebug ( XpsDebug ) < < " 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 ) )
return NULL ;
if ( m_xpsFile - > document ( 0 ) - > hasDocumentStructure ( ) )
return m_xpsFile - > document ( 0 ) - > documentStructure ( ) ;
return NULL ;
}
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 )
{
if ( format . mimeType ( ) - > name ( ) = = QLatin1String ( " text/plain " ) ) {
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 ;
2007-03-11 01:14:33 +00:00
ts < < QChar ( ' \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 ;
}
2007-02-10 17:31:25 +00:00
XpsRenderNode * XpsRenderNode : : findChild ( const QString & name )
{
for ( int i = 0 ; i < children . size ( ) ; i + + ) {
if ( children [ i ] . name = = name ) {
return & children [ i ] ;
}
}
return NULL ;
}
2007-02-12 13:31:16 +00:00
void * XpsRenderNode : : getRequiredChildData ( const QString & name )
{
XpsRenderNode * child = findChild ( name ) ;
if ( child = = NULL ) {
2007-08-02 23:54:14 +00:00
kDebug ( XpsDebug ) < < " Required element " < < name < < " is missing in " < < this - > name ;
2007-02-12 13:31:16 +00:00
return NULL ;
}
return child - > data ;
}
void * XpsRenderNode : : getChildData ( const QString & name )
{
XpsRenderNode * child = findChild ( name ) ;
if ( child = = NULL ) {
return NULL ;
} else {
return child - > data ;
}
}
2006-09-11 10:44:17 +00:00
# include "generator_xps.moc"