okular/core/generator.cpp
Brad Hards cfb62ab4d5 Add support for export to OpenDocument text and HTML
for TextDocumentGenerator-based generators:
 - EPub format
 - Fictionbook format
 - OOO.

This depends on Qt 4.5. Thanks to Qt Software (or wherever Thomas Z.
works) for QTextDocumentWriter!

It wouldn't be too hard to make this work for plucker (.pdb)
format, but that format doesn't work for me. It would be very
hard to make it work for other formats.


svn path=/trunk/KDE/kdegraphics/okular/; revision=855771
2008-09-01 12:00:50 +00:00

589 lines
13 KiB
C++

/***************************************************************************
* Copyright (C) 2005 by Piotr Szymanski <niedakh@gmail.com> *
* Copyright (C) 2008 by Albert Astals Cid <aacid@kde.org> *
* *
* 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. *
***************************************************************************/
#include "generator.h"
#include "generator_p.h"
#include <qeventloop.h>
#include <QtGui/QPrinter>
#include <kdebug.h>
#include <kicon.h>
#include <klocale.h>
#include "document.h"
#include "document_p.h"
#include "page.h"
#include "textpage.h"
#include "utils.h"
using namespace Okular;
GeneratorPrivate::GeneratorPrivate()
: m_document( 0 ),
mPixmapGenerationThread( 0 ), mTextPageGenerationThread( 0 ),
m_mutex( 0 ), m_threadsMutex( 0 ), mPixmapReady( true ), mTextPageReady( true ),
m_closing( false ), m_closingLoop( 0 )
{
}
GeneratorPrivate::~GeneratorPrivate()
{
if ( mPixmapGenerationThread )
mPixmapGenerationThread->wait();
delete mPixmapGenerationThread;
if ( mTextPageGenerationThread )
mTextPageGenerationThread->wait();
delete mTextPageGenerationThread;
delete m_mutex;
delete m_threadsMutex;
}
PixmapGenerationThread* GeneratorPrivate::pixmapGenerationThread()
{
if ( mPixmapGenerationThread )
return mPixmapGenerationThread;
Q_Q( Generator );
mPixmapGenerationThread = new PixmapGenerationThread( q );
QObject::connect( mPixmapGenerationThread, SIGNAL( finished() ),
q, SLOT( pixmapGenerationFinished() ),
Qt::QueuedConnection );
return mPixmapGenerationThread;
}
TextPageGenerationThread* GeneratorPrivate::textPageGenerationThread()
{
if ( mTextPageGenerationThread )
return mTextPageGenerationThread;
Q_Q( Generator );
mTextPageGenerationThread = new TextPageGenerationThread( q );
QObject::connect( mTextPageGenerationThread, SIGNAL( finished() ),
q, SLOT( textpageGenerationFinished() ),
Qt::QueuedConnection );
return mTextPageGenerationThread;
}
void GeneratorPrivate::pixmapGenerationFinished()
{
Q_Q( Generator );
PixmapRequest *request = mPixmapGenerationThread->request();
mPixmapGenerationThread->endGeneration();
QMutexLocker locker( threadsLock() );
mPixmapReady = true;
if ( m_closing )
{
delete request;
if ( mTextPageReady )
{
locker.unlock();
m_closingLoop->quit();
}
return;
}
const QImage& img = mPixmapGenerationThread->image();
request->page()->setPixmap( request->id(), new QPixmap( QPixmap::fromImage( img ) ) );
const int pageNumber = request->page()->number();
q->signalPixmapRequestDone( request );
if ( mPixmapGenerationThread->calcBoundingBox() )
q->updatePageBoundingBox( pageNumber, mPixmapGenerationThread->boundingBox() );
}
void GeneratorPrivate::textpageGenerationFinished()
{
Q_Q( Generator );
Page *page = mTextPageGenerationThread->page();
mTextPageGenerationThread->endGeneration();
QMutexLocker locker( threadsLock() );
mTextPageReady = true;
if ( m_closing )
{
delete mTextPageGenerationThread->textPage();
if ( mPixmapReady )
{
locker.unlock();
m_closingLoop->quit();
}
return;
}
if ( mTextPageGenerationThread->textPage() )
{
TextPage *tp = mTextPageGenerationThread->textPage();
page->setTextPage( tp );
q->signalTextGenerationDone( page, tp );
}
}
QMutex* GeneratorPrivate::threadsLock()
{
if ( !m_threadsMutex )
m_threadsMutex = new QMutex();
return m_threadsMutex;
}
QVariant GeneratorPrivate::metaData( const QString &, const QVariant & ) const
{
return QVariant();
}
QImage GeneratorPrivate::image( PixmapRequest * )
{
return QImage();
}
Generator::Generator( QObject *parent, const QVariantList &args )
: QObject( parent ), d_ptr( new GeneratorPrivate() )
{
d_ptr->q_ptr = this;
Q_UNUSED( args )
}
Generator::Generator( GeneratorPrivate &dd, QObject *parent, const QVariantList &args )
: QObject( parent ), d_ptr( &dd )
{
d_ptr->q_ptr = this;
Q_UNUSED( args )
}
Generator::~Generator()
{
delete d_ptr;
}
bool Generator::loadDocumentFromData( const QByteArray &, QVector< Page * > & )
{
return false;
}
bool Generator::closeDocument()
{
Q_D( Generator );
d->m_closing = true;
d->threadsLock()->lock();
if ( !( d->mPixmapReady && d->mTextPageReady ) )
{
QEventLoop loop;
d->m_closingLoop = &loop;
d->threadsLock()->unlock();
loop.exec();
d->m_closingLoop = 0;
}
else
{
d->threadsLock()->unlock();
}
bool ret = doCloseDocument();
d->m_closing = false;
return ret;
}
bool Generator::canGeneratePixmap() const
{
Q_D( const Generator );
return d->mPixmapReady;
}
void Generator::generatePixmap( PixmapRequest *request )
{
Q_D( Generator );
d->mPixmapReady = false;
if ( hasFeature( Threaded ) )
{
d->pixmapGenerationThread()->startGeneration( request, !request->page()->isBoundingBoxKnown() );
/**
* We create the text page for every page that is visible to the
* user, so he can use the text extraction tools without a delay.
*/
if ( hasFeature( TextExtraction ) && !request->page()->hasTextPage() && canGenerateTextPage() ) {
d->mTextPageReady = false;
d->textPageGenerationThread()->startGeneration( request->page() );
}
return;
}
const QImage& img = image( request );
request->page()->setPixmap( request->id(), new QPixmap( QPixmap::fromImage( img ) ) );
const bool bboxKnown = request->page()->isBoundingBoxKnown();
const int pageNumber = request->page()->number();
d->mPixmapReady = true;
signalPixmapRequestDone( request );
if ( !bboxKnown )
updatePageBoundingBox( pageNumber, Utils::imageBoundingBox( &img ) );
}
bool Generator::canGenerateTextPage() const
{
Q_D( const Generator );
return d->mTextPageReady;
}
void Generator::generateTextPage( Page *page )
{
Q_D( Generator );
TextPage *tp = textPage( page );
page->setTextPage( tp );
d->mTextPageReady = true;
signalTextGenerationDone( page, tp );
}
QImage Generator::image( PixmapRequest *request )
{
Q_D( Generator );
return d->image( request );
}
TextPage* Generator::textPage( Page* )
{
return 0;
}
const DocumentInfo * Generator::generateDocumentInfo()
{
return 0;
}
const DocumentSynopsis * Generator::generateDocumentSynopsis()
{
return 0;
}
FontInfo::List Generator::fontsForPage( int )
{
return FontInfo::List();
}
const QList<EmbeddedFile*> * Generator::embeddedFiles() const
{
return 0;
}
Generator::PageSizeMetric Generator::pagesSizeMetric() const
{
return None;
}
bool Generator::isAllowed( Permission ) const
{
return true;
}
void Generator::rotationChanged( Rotation, Rotation )
{
}
PageSize::List Generator::pageSizes() const
{
return PageSize::List();
}
void Generator::pageSizeChanged( const PageSize &, const PageSize & )
{
}
bool Generator::print( QPrinter& )
{
return false;
}
QVariant Generator::metaData( const QString &key, const QVariant &option ) const
{
Q_D( const Generator );
return d->metaData( key, option );
}
ExportFormat::List Generator::exportFormats() const
{
return ExportFormat::List();
}
bool Generator::exportTo( const QString&, const ExportFormat& )
{
return false;
}
bool Generator::hasFeature( GeneratorFeature feature ) const
{
Q_D( const Generator );
return d->m_features.contains( feature );
}
void Generator::signalPixmapRequestDone( PixmapRequest * request )
{
Q_D( Generator );
if ( d->m_document )
d->m_document->requestDone( request );
else
{
delete request;
}
}
void Generator::signalTextGenerationDone( Page *page, TextPage *textPage )
{
Q_D( Generator );
if ( d->m_document )
d->m_document->textGenerationDone( page );
else
delete textPage;
}
const Document * Generator::document() const
{
Q_D( const Generator );
if ( d->m_document )
{
return d->m_document->m_parent;
}
return 0;
}
void Generator::setFeature( GeneratorFeature feature, bool on )
{
Q_D( Generator );
if ( on )
d->m_features.insert( feature );
else
d->m_features.remove( feature );
}
QVariant Generator::documentMetaData( const QString &key, const QVariant &option ) const
{
Q_D( const Generator );
if ( !d->m_document )
return QVariant();
return d->m_document->documentMetaData( key, option );
}
QMutex* Generator::userMutex() const
{
Q_D( const Generator );
if ( !d->m_mutex )
{
d->m_mutex = new QMutex();
}
return d->m_mutex;
}
void Generator::updatePageBoundingBox( int page, const NormalizedRect & boundingBox )
{
Q_D( Generator );
if ( d->m_document ) // still connected to document?
d->m_document->setPageBoundingBox( page, boundingBox );
}
void Generator::requestFontData(const Okular::FontInfo & /*font*/, QByteArray * /*data*/)
{
}
PixmapRequest::PixmapRequest( int id, int pageNumber, int width, int height, int priority, bool asynchronous )
: d( new PixmapRequestPrivate )
{
d->mId = id;
d->mPageNumber = pageNumber;
d->mWidth = width;
d->mHeight = height;
d->mPriority = priority;
d->mAsynchronous = asynchronous;
d->mForce = false;
}
PixmapRequest::~PixmapRequest()
{
delete d;
}
int PixmapRequest::id() const
{
return d->mId;
}
int PixmapRequest::pageNumber() const
{
return d->mPageNumber;
}
int PixmapRequest::width() const
{
return d->mWidth;
}
int PixmapRequest::height() const
{
return d->mHeight;
}
int PixmapRequest::priority() const
{
return d->mPriority;
}
bool PixmapRequest::asynchronous() const
{
return d->mAsynchronous;
}
Page* PixmapRequest::page() const
{
return d->mPage;
}
void PixmapRequestPrivate::swap()
{
qSwap( mWidth, mHeight );
}
class Okular::ExportFormatPrivate : public QSharedData
{
public:
ExportFormatPrivate( const QString &description, const KMimeType::Ptr &mimeType, const KIcon &icon = KIcon() )
: QSharedData(), mDescription( description ), mMimeType( mimeType ), mIcon( icon )
{
}
~ExportFormatPrivate()
{
}
QString mDescription;
KMimeType::Ptr mMimeType;
KIcon mIcon;
};
ExportFormat::ExportFormat()
: d( new ExportFormatPrivate( QString(), KMimeType::Ptr() ) )
{
}
ExportFormat::ExportFormat( const QString &description, const KMimeType::Ptr &mimeType )
: d( new ExportFormatPrivate( description, mimeType ) )
{
}
ExportFormat::ExportFormat( const KIcon &icon, const QString &description, const KMimeType::Ptr &mimeType )
: d( new ExportFormatPrivate( description, mimeType, icon ) )
{
}
ExportFormat::~ExportFormat()
{
}
ExportFormat::ExportFormat( const ExportFormat &other )
: d( other.d )
{
}
ExportFormat& ExportFormat::operator=( const ExportFormat &other )
{
if ( this == &other )
return *this;
d = other.d;
return *this;
}
QString ExportFormat::description() const
{
return d->mDescription;
}
KMimeType::Ptr ExportFormat::mimeType() const
{
return d->mMimeType;
}
KIcon ExportFormat::icon() const
{
return d->mIcon;
}
bool ExportFormat::isNull() const
{
return d->mMimeType.isNull() || d->mDescription.isNull();
}
ExportFormat ExportFormat::standardFormat( StandardExportFormat type )
{
switch ( type )
{
case PlainText:
return ExportFormat( KIcon( "text-x-generic" ), i18n( "Plain &Text..." ), KMimeType::mimeType( "text/plain" ) );
break;
case PDF:
return ExportFormat( KIcon( "application-pdf" ), i18n( "PDF" ), KMimeType::mimeType( "application/pdf" ) );
break;
case OpenDocumentText:
return ExportFormat(
KIcon( "application-vnd.oasis.opendocument.text" ),
i18nc( "This is the document format", "OpenDocument Text" ),
KMimeType::mimeType( "application/vnd.oasis.opendocument.text" ) );
break;
case HTML:
return ExportFormat( KIcon( "text-html" ), i18nc( "This is the document format", "HTML" ), KMimeType::mimeType( "text/html" ) );
break;
}
return ExportFormat();
}
bool ExportFormat::operator==( const ExportFormat &other ) const
{
return d == other.d;
}
bool ExportFormat::operator!=( const ExportFormat &other ) const
{
return d != other.d;
}
QDebug operator<<( QDebug str, const Okular::PixmapRequest &req )
{
QString s = QString( "%1 PixmapRequest (id: %2) (%3x%4), prio %5, pageNo %6" )
.arg( QString( req.asynchronous() ? "Async" : "Sync" ) )
.arg( req.id() )
.arg( req.width() )
.arg( req.height() )
.arg( req.priority() )
.arg( req.pageNumber() );
str << qPrintable( s );
return str;
}
#include "generator.moc"