You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
680 lines
24 KiB
680 lines
24 KiB
/* |
|
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 |
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
|
02110-1301, USA. |
|
*/ |
|
|
|
#include <qdatetime.h> |
|
#include <qfile.h> |
|
#include <qfontdatabase.h> |
|
#include <qimage.h> |
|
#include <qlist.h> |
|
#include <qpainter.h> |
|
#include <qpixmap.h> |
|
#include <qthread.h> |
|
#include <kglobal.h> |
|
#include <kimageeffect.h> |
|
#include <klocale.h> |
|
|
|
#include "core/document.h" |
|
#include "core/page.h" |
|
#include "generator_xps.h" |
|
|
|
OKULAR_EXPORT_PLUGIN(XpsGenerator) |
|
|
|
// 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 ); |
|
} |
|
|
|
static QPointF getPointFromString( const QString &data, int *curPos ) |
|
{ |
|
// find the first ',' after the current position |
|
int endOfNumberPos = data.indexOf(',', *curPos); |
|
QString numberPart = data.mid(*curPos, endOfNumberPos - *curPos); |
|
// kDebug() << "Number part 1: " << numberPart << endl; |
|
double firstVal = numberPart.toDouble(); |
|
*curPos = endOfNumberPos + 1; //eat the number and the following comma |
|
|
|
endOfNumberPos = data.indexOf(' ', *curPos); |
|
numberPart = data.mid (*curPos, endOfNumberPos - *curPos); |
|
// kDebug() << "Number part 2: " << numberPart << endl; |
|
double secondVal = numberPart.toDouble(); |
|
*curPos = endOfNumberPos; |
|
// kDebug() << "firstVal: " << firstVal << endl; |
|
// kDebug() << "secondVal: " << secondVal << endl; |
|
return QPointF( firstVal, secondVal ); |
|
} |
|
|
|
void XpsHandler::parseAbbreviatedPathData( const QString &data) |
|
{ |
|
// kDebug() << data << endl; |
|
|
|
enum OperationType { moveTo, relMoveTo, lineTo, relLineTo, cubicTo, relCubicTo }; |
|
OperationType operation = moveTo; |
|
// TODO: Implement a proper parser here. |
|
for (int curPos = 0; curPos < data.length(); ++curPos) { |
|
// kDebug() << "curPos: " << curPos << endl; |
|
if (data.at(curPos) == 'M') { |
|
// kDebug() << "operation moveTo" << endl; |
|
operation = moveTo; |
|
} else if (data.at(curPos) == 'L') { |
|
// kDebug() << "operation lineTo" << endl; |
|
operation = lineTo; |
|
} else if (data.at(curPos) == 'C') { |
|
operation = cubicTo; |
|
} else if ( (data.at(curPos) == 'z') || (data.at(curPos) == 'Z') ){ |
|
// kDebug() << "operation close" << endl; |
|
m_currentPath.closeSubpath(); |
|
} else if (data.at(curPos).isNumber()) { |
|
if ( operation == moveTo ) { |
|
QPointF point = getPointFromString( data, &curPos ); |
|
m_currentPath.moveTo( point ); |
|
} else if ( operation == lineTo ) { |
|
QPointF point = getPointFromString( data, &curPos ); |
|
m_currentPath.lineTo( point ); |
|
} else if (operation == cubicTo ) { |
|
QPointF point1 = getPointFromString( data, &curPos ); |
|
while ((data.at(curPos).isSpace())) { ++curPos; } |
|
QPointF point2 = getPointFromString( data, &curPos ); |
|
while (data.at(curPos).isSpace()) { curPos++; } |
|
QPointF point3 = getPointFromString( data, &curPos ); |
|
// kDebug() << "cubic" << point1 << " : " << point2 << " : " << point3 << endl; |
|
m_currentPath.cubicTo( point1, point2, point3 ); |
|
} |
|
} else if (data.at(curPos) == ' ') { |
|
// Do nothing |
|
// kDebug() << "eating a space" << endl; |
|
} else { |
|
kDebug() << "Unexpected data: " << data.at(curPos) << endl; |
|
} |
|
} |
|
} |
|
QMatrix XpsHandler::attsToMatrix( const QString &csv ) |
|
{ |
|
QStringList values = csv.split( ',' ); |
|
if ( values.count() != 6 ) { |
|
return QMatrix(); // that is an identity matrix - no effect |
|
} |
|
return QMatrix( values.at(0).toDouble(), values.at(1).toDouble(), |
|
values.at(2).toDouble(), values.at(3).toDouble(), |
|
values.at(4).toDouble(), values.at(5).toDouble() ); |
|
} |
|
|
|
XpsHandler::XpsHandler(XpsPage *page): m_page(page) |
|
{ |
|
} |
|
|
|
XpsHandler::~XpsHandler() |
|
{} |
|
|
|
bool XpsHandler::startDocument() |
|
{ |
|
// kDebug() << "start document" << endl; |
|
m_page->m_pageImage->fill( QColor("White").rgba() ); |
|
m_painter = new QPainter(m_page->m_pageImage); |
|
return true; |
|
} |
|
|
|
|
|
bool XpsHandler::startElement( const QString &nameSpace, |
|
const QString &localName, |
|
const QString &qname, |
|
const QXmlAttributes & atts ) |
|
{ |
|
Q_UNUSED(nameSpace); |
|
Q_UNUSED(qname); |
|
if (localName == "Glyphs") { |
|
m_painter->save(); |
|
int fontId = m_page->loadFontByName( atts.value("FontUri") ); |
|
// kDebug() << "Font families: (" << fontId << ") " << QFontDatabase::applicationFontFamilies( fontId ).at(0) << endl; |
|
QString fontFamily = m_page->m_fontDatabase.applicationFontFamilies( fontId ).at(0); |
|
// kDebug() << "Styles: " << m_page->m_fontDatabase.styles( fontFamily ) << endl; |
|
QString fontStyle = m_page->m_fontDatabase.styles( fontFamily ).at(0); |
|
// TODO: We may not be picking the best font size here |
|
QFont font = m_page->m_fontDatabase.font(fontFamily, fontStyle, qRound(atts.value("FontRenderingEmSize").toFloat()) ); |
|
m_painter->setFont(font); |
|
QPointF origin( atts.value("OriginX").toDouble(), atts.value("OriginY").toDouble() ); |
|
QColor fillColor = hexToRgba( atts.value("Fill").toLatin1() ); |
|
kDebug() << "Indices " << atts.value("Indices") << endl; |
|
m_painter->setBrush(fillColor); |
|
m_painter->setPen(fillColor); |
|
m_painter->drawText( origin, atts.value("UnicodeString") ); |
|
m_painter->restore(); |
|
// kDebug() << "Glyphs: " << atts.value("Fill") << ", " << atts.value("FontUri") << endl; |
|
// kDebug() << " Origin: " << atts.value("OriginX") << "," << atts.value("OriginY") << endl; |
|
// kDebug() << " Unicode: " << atts.value("UnicodeString") << endl; |
|
} else if (localName == "Path") { |
|
// kDebug() << "Path: " << atts.value("Data") << ", " << atts.value("Fill") << endl; |
|
if (! atts.value("Data").isEmpty() ) { |
|
parseAbbreviatedPathData( atts.value("Data") ); |
|
} |
|
if (! atts.value("Fill").isEmpty() ) { |
|
QColor fillColor; |
|
if ( atts.value("Fill").startsWith('#') ) { |
|
fillColor = hexToRgba( atts.value("Fill").toLatin1() ); |
|
} else { |
|
kDebug() << "Unknown / unhandled fill color representation:" << atts.value("Fill") << ":ende" << endl; |
|
} |
|
m_currentBrush = QBrush( fillColor ); |
|
m_currentPen = QPen ( fillColor ); |
|
} |
|
} else if ( localName == "SolidColorBrush" ) { |
|
if (! atts.value("Color").isEmpty() ) { |
|
QColor fillColor; |
|
if (atts.value("Color").startsWith('#') ) { |
|
fillColor = hexToRgba( atts.value("Color").toLatin1() ); |
|
// kDebug() << "Solid colour: " << fillColor << endl; |
|
} else { |
|
kDebug() << "Unknown / unhandled fill color representation:" << atts.value("Color") << ":ende" << endl; |
|
} |
|
m_currentBrush = QBrush( fillColor ); |
|
} |
|
} else if ( localName == "ImageBrush" ) { |
|
kDebug() << "ImageBrush Attributes: " << atts.count() << ", " << atts.value("Transform") << ", " << atts.value("TileMode") << endl; |
|
m_image = m_page->loadImageFromFile( atts.value("ImageSource" ) ); |
|
m_viewbox = stringToRectF( atts.value("Viewbox") ); |
|
m_viewport = stringToRectF( atts.value("Viewport") ); |
|
} else if ( localName == "Canvas" ) { |
|
// TODO |
|
m_painter->save(); |
|
} else if ( localName == "MatrixTransform" ) { |
|
// kDebug() << "Matrix transform: " << atts.value("Matrix") << endl; |
|
m_painter->setWorldMatrix( attsToMatrix( atts.value("Matrix") ), true ); |
|
} else if ( localName == "Path.Fill" ) { |
|
// this doesn't have any attributes - just other elements that we handle elsewhere |
|
} else { |
|
kDebug() << "unknown start element: " << localName << endl; |
|
} |
|
|
|
return true; |
|
} |
|
|
|
bool XpsHandler::endElement( const QString &nameSpace, |
|
const QString &localName, |
|
const QString &qname) |
|
{ |
|
Q_UNUSED(nameSpace); |
|
Q_UNUSED(qname); |
|
if ( localName == "Path" ) { |
|
m_painter->save(); |
|
m_painter->setBrush( m_currentBrush ); |
|
m_painter->setPen( m_currentPen ); |
|
m_painter->drawPath( m_currentPath ); |
|
|
|
if (! m_image.isNull() ) { |
|
m_painter->drawImage( m_viewport, m_image, m_viewbox ); |
|
} |
|
|
|
m_painter->restore(); |
|
m_currentPath = QPainterPath(); |
|
m_currentBrush = QBrush(); |
|
m_currentPen = QPen(); |
|
m_image = QImage(); |
|
} else if ( localName == "Canvas" ) { |
|
m_painter->restore(); |
|
} |
|
return true; |
|
} |
|
|
|
XpsPage::XpsPage(KZip *archive, const QString &fileName): m_archive( archive ), |
|
m_fileName( fileName ), m_pageIsRendered(false) |
|
{ |
|
kDebug() << "page file name: " << fileName << endl; |
|
|
|
const KZipFileEntry* pageFile = static_cast<const KZipFileEntry *>(archive->directory()->entry( fileName )); |
|
|
|
QIODevice* pageDevice = pageFile->device(); |
|
|
|
QString errMsg; |
|
int errLine, errCol; |
|
if ( m_dom.setContent( pageDevice, true, &errMsg, &errLine, &errCol ) == false ) { |
|
// parse error |
|
kDebug() << "Could not parse XPS page: " << errMsg << " : " |
|
<< errLine << " : " << errCol << endl; |
|
} |
|
|
|
QDomElement element = m_dom.documentElement().toElement(); |
|
m_pageSize.setWidth( element.attribute("Width").toInt() ); |
|
m_pageSize.setHeight( element.attribute("Height").toInt() ); |
|
m_pageImage = new QImage( m_pageSize, QImage::Format_ARGB32 ); |
|
delete pageDevice; |
|
} |
|
|
|
bool XpsPage::renderToImage( QImage *p ) |
|
{ |
|
if (! m_pageIsRendered) { |
|
XpsHandler *handler = new XpsHandler( this ); |
|
QXmlSimpleReader *parser = new QXmlSimpleReader(); |
|
parser->setContentHandler( handler ); |
|
parser->setErrorHandler( handler ); |
|
QXmlInputSource *source = new QXmlInputSource(); |
|
source->setData( m_dom.toString() ); |
|
bool ok = parser->parse( source ); |
|
kDebug() << "Parse result: " << ok << endl; |
|
delete source; |
|
delete parser; |
|
delete handler; |
|
m_pageIsRendered = true; |
|
} |
|
|
|
if ( size() == p->size() ) |
|
*p = *m_pageImage; |
|
else |
|
*p = m_pageImage->scaled( p->size(), Qt::KeepAspectRatio ); |
|
|
|
return true; |
|
} |
|
QSize XpsPage::size() const |
|
{ |
|
return m_pageSize; |
|
} |
|
|
|
int XpsPage::loadFontByName( const QString &fileName ) |
|
{ |
|
// kDebug() << "font file name: " << fileName << endl; |
|
|
|
const KZipFileEntry* fontFile = static_cast<const KZipFileEntry *>(m_archive->directory()->entry( fileName )); |
|
|
|
QByteArray fontData = fontFile->data(); // once per file, according to the docs |
|
|
|
int result = m_fontDatabase.addApplicationFontFromData( fontData ); |
|
if (-1 == result) { |
|
kDebug() << "Failed to load application font from data" << endl; |
|
} |
|
|
|
// kDebug() << "Loaded font: " << m_fontDatabase.applicationFontFamilies( result ) << endl; |
|
|
|
return result; // a font ID |
|
} |
|
|
|
QImage XpsPage::loadImageFromFile( const QString &fileName ) |
|
{ |
|
kDebug() << "image file name: " << fileName << endl; |
|
|
|
const KZipFileEntry* imageFile = static_cast<const KZipFileEntry *>(m_archive->directory()->entry( fileName )); |
|
|
|
QByteArray imageData = imageFile->data(); // once per file, according to the docs |
|
|
|
QImage image; |
|
bool result = image.loadFromData( imageData ); |
|
kDebug() << "Image load result: " << result << ", " << image.size() << endl; |
|
return image; |
|
} |
|
|
|
XpsDocument::XpsDocument(KZip *archive, const QString &fileName) |
|
{ |
|
kDebug() << "document file name: " << fileName << endl; |
|
|
|
const KZipFileEntry* documentFile = static_cast<const KZipFileEntry *>(archive->directory()->entry( fileName )); |
|
|
|
QIODevice* documentDevice = documentFile->device(); |
|
|
|
QDomDocument documentDom; |
|
QString errMsg; |
|
int errLine, errCol; |
|
if ( documentDom.setContent( documentDevice, true, &errMsg, &errLine, &errCol ) == false ) { |
|
// parse error |
|
kDebug() << "Could not parse XPS document: " << errMsg << " : " |
|
<< errLine << " : " << errCol << endl; |
|
} |
|
|
|
QDomNode node = documentDom.documentElement().firstChild(); |
|
|
|
while( !node.isNull() ) { |
|
QDomElement element = node.toElement(); |
|
if( !element.isNull() ) { |
|
if (element.tagName() == "PageContent") { |
|
QString pagePath = element.attribute("Source"); |
|
if (pagePath.startsWith('/') == false ) { |
|
int offset = fileName.lastIndexOf('/'); |
|
QString thisDir = fileName.mid(0, offset) + '/'; |
|
pagePath.prepend(thisDir); |
|
} |
|
XpsPage *page = new XpsPage( archive, pagePath ); |
|
m_pages.append(page); |
|
} else { |
|
kDebug() << "Unhandled entry in FixedDocument" << element.tagName() << endl; |
|
} |
|
} |
|
node = node.nextSibling(); |
|
} |
|
|
|
delete documentDevice; |
|
} |
|
|
|
int XpsDocument::numPages() const |
|
{ |
|
return m_pages.size(); |
|
} |
|
|
|
XpsPage* XpsDocument::page(int pageNum) const |
|
{ |
|
return m_pages.at(pageNum); |
|
} |
|
|
|
XpsFile::XpsFile() : m_docInfo( 0 ) |
|
{ |
|
} |
|
|
|
|
|
XpsFile::~XpsFile() |
|
{ |
|
} |
|
|
|
|
|
bool XpsFile::loadDocument(const QString &filename) |
|
{ |
|
xpsArchive = new KZip( filename ); |
|
if ( xpsArchive->open( QIODevice::ReadOnly ) == true ) { |
|
kDebug() << "Successful open of " << xpsArchive->fileName() << endl; |
|
} else { |
|
kDebug() << "Could not open XPS archive: " << xpsArchive->fileName() << endl; |
|
delete xpsArchive; |
|
return false; |
|
} |
|
|
|
// The only fixed entry in XPS is _rels/.rels |
|
const KZipFileEntry* relFile = static_cast<const KZipFileEntry *>(xpsArchive->directory()->entry("_rels/.rels")); |
|
|
|
if ( !relFile ) { |
|
// this might occur if we can't read the zip directory, or it doesn't have the relationships entry |
|
return false; |
|
} |
|
|
|
QIODevice* relDevice = relFile->device(); |
|
QDomDocument relDom; |
|
QString errMsg; |
|
int errLine, errCol; |
|
if ( relDom.setContent( relDevice, true, &errMsg, &errLine, &errCol ) == false ) { |
|
// parse error |
|
kDebug() << "Could not parse relationship document: " << errMsg << " : " |
|
<< errLine << " : " << errCol << endl; |
|
return false; |
|
} |
|
|
|
QString fixedRepresentationFileName; |
|
// We work through the relationships document and pull out each element. |
|
QDomNode n = relDom.documentElement().firstChild(); |
|
while( !n.isNull() ) { |
|
QDomElement e = n.toElement(); |
|
if( !e.isNull() ) { |
|
if ("http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail" == e.attribute("Type") ) { |
|
m_thumbnailFileName = e.attribute("Target"); |
|
} else if ("http://schemas.microsoft.com/xps/2005/06/fixedrepresentation" == e.attribute("Type") ) { |
|
fixedRepresentationFileName = e.attribute("Target"); |
|
} else if ("http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties" == e.attribute("Type") ) { |
|
m_corePropertiesFileName = e.attribute("Target"); |
|
} else { |
|
kDebug() << "Unknown relationships element: " << e.attribute("Type") << " : " << e.attribute("Target") << endl; |
|
} |
|
} |
|
n = n.nextSibling(); |
|
} |
|
|
|
if ( fixedRepresentationFileName.isEmpty() ) { |
|
// FixedRepresentation is a required part of the XPS document |
|
return false; |
|
} |
|
|
|
delete relDevice; |
|
|
|
|
|
const KZipFileEntry* fixedRepFile = static_cast<const KZipFileEntry *>(xpsArchive->directory()->entry( fixedRepresentationFileName )); |
|
|
|
QIODevice* fixedRepDevice = fixedRepFile->device(); |
|
|
|
QDomDocument fixedRepDom; |
|
if ( fixedRepDom.setContent( fixedRepDevice, true, &errMsg, &errLine, &errCol ) == false ) { |
|
// parse error |
|
kDebug() << "Could not parse Fixed Representation document: " << errMsg << " : " |
|
<< errLine << " : " << errCol << endl; |
|
return false; |
|
} |
|
|
|
n = fixedRepDom.documentElement().firstChild(); |
|
while( !n.isNull() ) { |
|
QDomElement e = n.toElement(); |
|
if( !e.isNull() ) { |
|
if (e.tagName() == "DocumentReference") { |
|
XpsDocument *doc = new XpsDocument( xpsArchive, e.attribute("Source") ); |
|
for (int lv = 0; lv < doc->numPages(); ++lv) { |
|
// our own copy of the pages list |
|
m_pages.append( doc->page( lv ) ); |
|
} |
|
m_documents.append(doc); |
|
} else { |
|
kDebug() << "Unhandled entry in FixedDocumentSequence" << e.tagName() << endl; |
|
} |
|
} |
|
n = n.nextSibling(); |
|
} |
|
|
|
delete fixedRepDevice; |
|
|
|
return true; |
|
} |
|
|
|
const Okular::DocumentInfo * XpsFile::generateDocumentInfo() |
|
{ |
|
if ( m_docInfo ) |
|
return m_docInfo; |
|
|
|
m_docInfo = new Okular::DocumentInfo(); |
|
|
|
m_docInfo->set( "mimeType", "application/vnd.ms-xpsdocument" ); |
|
|
|
if ( ! m_corePropertiesFileName.isEmpty() ) { |
|
const KZipFileEntry* corepropsFile = static_cast<const KZipFileEntry *>(xpsArchive->directory()->entry(m_corePropertiesFileName)); |
|
|
|
QDomDocument corePropertiesDocumentDom; |
|
QString errMsg; |
|
int errLine, errCol; |
|
|
|
QIODevice *corepropsDevice = corepropsFile->device(); |
|
|
|
if ( corePropertiesDocumentDom.setContent( corepropsDevice, true, &errMsg, &errLine, &errCol ) == false ) { |
|
// parse error |
|
kDebug() << "Could not parse core properties (metadata) document: " << errMsg << " : " |
|
<< errLine << " : " << errCol << endl; |
|
// return whatever we have |
|
return m_docInfo; |
|
} |
|
|
|
QDomNode n = corePropertiesDocumentDom.documentElement().firstChild(); // the <coreProperties> level |
|
while( !n.isNull() ) { |
|
QDomElement e = n.toElement(); |
|
if( !e.isNull() ) { |
|
if (e.tagName() == "title") { |
|
m_docInfo->set( "title", e.text(), i18n("Title") ); |
|
} else if (e.tagName() == "subject") { |
|
m_docInfo->set( "subject", e.text(), i18n("Subject") ); |
|
} else if (e.tagName() == "description") { |
|
m_docInfo->set( "description", e.text(), i18n("Description") ); |
|
} else if (e.tagName() == "creator") { |
|
m_docInfo->set( "creator", e.text(), i18n("Author") ); |
|
} else if (e.tagName() == "created") { |
|
QDateTime createdDate = QDateTime::fromString( e.text(), "yyyy-MM-ddThh:mm:ssZ" ); |
|
m_docInfo->set( "creationDate", KGlobal::locale()->formatDateTime( createdDate, false, true ), i18n("Created" ) ); |
|
} else if (e.tagName() == "modified") { |
|
QDateTime modifiedDate = QDateTime::fromString( e.text(), "yyyy-MM-ddThh:mm:ssZ" ); |
|
m_docInfo->set( "modifiedDate", KGlobal::locale()->formatDateTime( modifiedDate, false, true ), i18n("Modified" ) ); |
|
} else if (e.tagName() == "keywords") { |
|
m_docInfo->set( "keywords", e.text(), i18n("Keywords") ); |
|
} else { |
|
kDebug() << "unhandled metadata tag: " << e.tagName() << " : " << e.text() << endl; |
|
} |
|
} |
|
n = n.nextSibling(); |
|
} |
|
|
|
delete corepropsDevice; |
|
} else { |
|
kDebug() << "No core properties filename" << endl; |
|
} |
|
|
|
m_docInfo->set( "pages", QString::number(numPages()), i18n("Pages") ); |
|
|
|
return m_docInfo; |
|
} |
|
|
|
bool XpsFile::closeDocument() |
|
{ |
|
|
|
if ( m_docInfo ) |
|
delete m_docInfo; |
|
|
|
m_docInfo = 0; |
|
|
|
m_documents.clear(); |
|
|
|
delete xpsArchive; |
|
|
|
return true; |
|
} |
|
|
|
int XpsFile::numPages() const |
|
{ |
|
return m_pages.size(); |
|
} |
|
|
|
int XpsFile::numDocuments() const |
|
{ |
|
return m_documents.size(); |
|
} |
|
|
|
XpsDocument* XpsFile::document(int documentNum) const |
|
{ |
|
return m_documents.at( documentNum ); |
|
} |
|
|
|
XpsPage* XpsFile::page(int pageNum) const |
|
{ |
|
return m_pages.at( pageNum ); |
|
} |
|
|
|
XpsGenerator::XpsGenerator() |
|
: Okular::Generator() |
|
{ |
|
m_xpsFile = new XpsFile; |
|
} |
|
|
|
XpsGenerator::~XpsGenerator() |
|
{ |
|
delete m_xpsFile; |
|
} |
|
|
|
bool XpsGenerator::loadDocument( const QString & fileName, QVector<Okular::Page*> & pagesVector ) |
|
{ |
|
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(); |
|
pagesVector[pagesVectorOffset] = new Okular::Page( pagesVectorOffset, pageSize.width(), pageSize.height(), 0 ); |
|
++pagesVectorOffset; |
|
} |
|
} |
|
|
|
return true; |
|
} |
|
|
|
bool XpsGenerator::closeDocument() |
|
{ |
|
m_xpsFile->closeDocument(); |
|
|
|
return true; |
|
} |
|
|
|
bool XpsGenerator::canGeneratePixmap( bool /*async*/ ) const |
|
{ |
|
return true; |
|
} |
|
|
|
void XpsGenerator::generatePixmap( Okular::PixmapRequest * request ) |
|
{ |
|
QSize size( (int)request->page()->width(), (int)request->page()->height() ); |
|
QImage image( size, QImage::Format_RGB32 ); |
|
XpsPage *pageToRender = m_xpsFile->page( request->page()->number() ); |
|
pageToRender->renderToImage( &image ); |
|
request->page()->setPixmap( request->id(), new QPixmap( QPixmap::fromImage( image ) ) ); |
|
|
|
// signal that the request has been accomplished |
|
signalRequestDone( request ); |
|
} |
|
|
|
const Okular::DocumentInfo * XpsGenerator::generateDocumentInfo() |
|
{ |
|
kDebug() << "generating document metadata" << endl; |
|
|
|
return m_xpsFile->generateDocumentInfo(); |
|
} |
|
|
|
|
|
#include "generator_xps.moc" |
|
|
|
|