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.
 
 
 
 
 

426 lines
13 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 Steet, Fifth Floor, Boston, MA
02110-1301, USA.
*/
#include <qdatetime.h>
#include <qfile.h>
#include <qimage.h>
#include <qlist.h>
#include <qpixmap.h>
#include <qthread.h>
#include <kglobal.h>
#include <kimageeffect.h>
#include <klocale.h>
#include "core/page.h"
#include "generator_xps.h"
OKULAR_EXPORT_PLUGIN(XpsGenerator)
XpsPage::XpsPage(KZip *archive, const QString &fileName)
{
kDebug() << "page file name: " << fileName << endl;
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;
}
QDomNode node = m_dom.documentElement();
QDomElement element = node.toElement();
m_pageSize.setWidth( element.attribute("Width").toInt() );
m_pageSize.setHeight( element.attribute("Height").toInt() );
delete pageDevice;
}
QSize XpsPage::size() const
{
return m_pageSize;
}
XpsDocument::XpsDocument(KZip *archive, const QString &fileName)
{
kDebug() << "document file name: " << fileName << endl;
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
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;
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") );
m_documents.append(doc);
} else {
kDebug() << "Unhandled entry in FixedDocumentSequence" << e.tagName() << endl;
}
}
n = n.nextSibling();
}
delete fixedRepDevice;
return true;
}
const DocumentInfo * XpsFile::generateDocumentInfo()
{
if ( m_docInfo )
return m_docInfo;
m_docInfo = new DocumentInfo();
m_docInfo->set( "mimeType", "application/vnd.ms-xpsdocument" );
if ( ! m_corePropertiesFileName.isEmpty() ) {
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
{
int pages = 0;
foreach( const XpsDocument *doc, m_documents ) {
pages += doc->numPages();
}
return pages;
}
int XpsFile::numDocuments() const
{
return m_documents.size();
}
XpsDocument* XpsFile::document(int documentNum) const
{
return m_documents.at(documentNum);
}
XpsGenerator::XpsGenerator( KPDFDocument * document ) : Generator( document )
{
m_xpsFile = new XpsFile;
}
XpsGenerator::~XpsGenerator()
{
delete m_xpsFile;
}
bool XpsGenerator::loadDocument( const QString & fileName, QVector<KPDFPage*> & 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 KPDFPage( pagesVectorOffset, pageSize.width(), pageSize.height(), 0 );
++pagesVectorOffset;
}
}
return true;
}
bool XpsGenerator::closeDocument()
{
m_xpsFile->closeDocument();
return true;
}
bool XpsGenerator::canGeneratePixmap( bool /*async*/ )
{
return false; // for now
}
void XpsGenerator::generatePixmap( PixmapRequest * request )
{
QPixmap * p = new QPixmap( request->width, request->height );
#if 0
if ( TIFFSetDirectory( d->tiff, request->page->number() ) )
{
int rotation = request->documentRotation;
uint32 width = (uint32)request->page->width();
uint32 height = (uint32)request->page->height();
if ( rotation % 2 == 1 )
qSwap( width, height );
QImage image( width, height, QImage::Format_RGB32 );
uint32 * data = (uint32 *)image.bits();
// read data
if ( TIFFReadRGBAImageOriented( d->tiff, width, height, data, ORIENTATION_TOPLEFT ) != 0 )
{
// an image read by ReadRGBAImage is ABGR, we need ARGB, so swap red and blue
uint32 size = width * height;
for ( uint32 i = 0; i < size; ++i )
{
uint32 red = ( data[i] & 0x00FF0000 ) >> 16;
uint32 blue = ( data[i] & 0x000000FF ) << 16;
data[i] = ( data[i] & 0xFF00FF00 ) + red + blue;
}
int reqwidth = request->width;
int reqheight = request->height;
if ( rotation % 2 == 1 )
qSwap( reqwidth, reqheight );
QImage smoothImage = image.scaled( reqwidth, reqheight, Qt::IgnoreAspectRatio, Qt::SmoothTransformation );
QImage finalImage = rotation > 0
? KImageEffect::rotate( smoothImage, (KImageEffect::RotateDirection)( rotation - 1 ) )
: smoothImage;
*p = QPixmap::fromImage( finalImage );
generated = true;
}
}
if ( !generated )
{
p->fill();
}
request->page->setPixmap( request->id, p );
ready = true;
// signal that the request has been accomplished
signalRequestDone( request );
#endif
}
const DocumentInfo * XpsGenerator::generateDocumentInfo()
{
kDebug() << "generating document metadata" << endl;
return m_xpsFile->generateDocumentInfo();
}
void XpsGenerator::setOrientation( QVector<KPDFPage*> & pagesVector, int orientation )
{
// TODO
}
#include "generator_xps.moc"