diff --git a/core/generator.cpp b/core/generator.cpp index 9e43375a9..3da0b5ae1 100644 --- a/core/generator.cpp +++ b/core/generator.cpp @@ -1,821 +1,812 @@ /*************************************************************************** * Copyright (C) 2005 by Piotr Szymanski * * Copyright (C) 2008 by Albert Astals Cid * * Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group * * company, info@kdab.com. Work sponsored by the * * LiMux project of the city of Munich * * * * 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 "observer.h" #include #include #include #include #include #include #include #include #ifdef WITH_KWALLET #include #endif #include "document.h" #include "document_p.h" #include "page.h" #include "page_p.h" #include "textpage.h" #include "utils.h" using namespace Okular; GeneratorPrivate::GeneratorPrivate() : m_document( nullptr ), mPixmapGenerationThread( nullptr ), mTextPageGenerationThread( nullptr ), - m_mutex( nullptr ), m_threadsMutex( nullptr ), mPixmapReady( true ), mTextPageReady( true ), + mPixmapReady( true ), mTextPageReady( true ), m_closing( false ), m_closingLoop( nullptr ), m_dpi(72.0, 72.0) { qRegisterMetaType(); } 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(); const QImage& img = mPixmapGenerationThread->image(); mPixmapGenerationThread->endGeneration(); QMutexLocker locker( threadsLock() ); if ( m_closing ) { mPixmapReady = true; delete request; if ( mTextPageReady ) { locker.unlock(); m_closingLoop->quit(); } return; } if ( !request->shouldAbortRender() ) { request->page()->setPixmap( request->observer(), new QPixmap( QPixmap::fromImage( img ) ), request->normalizedRect() ); const int pageNumber = request->page()->number(); if ( mPixmapGenerationThread->calcBoundingBox() ) q->updatePageBoundingBox( pageNumber, mPixmapGenerationThread->boundingBox() ); } else { // Cancel the text page generation too if it's still running if ( mTextPageGenerationThread && mTextPageGenerationThread->isRunning() ) { mTextPageGenerationThread->abortExtraction(); mTextPageGenerationThread->wait(); } } mPixmapReady = true; q->signalPixmapRequestDone( request ); } 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; + 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) : Generator( *new GeneratorPrivate(), parent, args ) { // the delegated constructor does it all } 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::loadDocument( const QString & fileName, QVector< Page * > & pagesVector ) { Q_UNUSED(fileName); Q_UNUSED(pagesVector); return false; } bool Generator::loadDocumentFromData( const QByteArray &, QVector< Page * > & ) { return false; } Document::OpenResult Generator::loadDocumentWithPassword( const QString & fileName, QVector< Page * > & pagesVector, const QString & ) { return loadDocument( fileName, pagesVector ) ? Document::OpenSuccess : Document::OpenError; } Document::OpenResult Generator::loadDocumentFromDataWithPassword( const QByteArray & fileData, QVector< Page * > & pagesVector, const QString & ) { return loadDocumentFromData( fileData, pagesVector ) ? Document::OpenSuccess : Document::OpenError; } Generator::SwapBackingFileResult Generator::swapBackingFile( QString const &/*newFileName */, QVector & /*newPagesVector*/ ) { return SwapBackingFileError; } 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 = nullptr; } 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; const bool calcBoundingBox = !request->isTile() && !request->page()->isBoundingBoxKnown(); if ( request->asynchronous() && hasFeature( Threaded ) ) { if ( d->textPageGenerationThread()->isFinished() && !canGenerateTextPage() ) { // It can happen that the text generation has already finished but // mTextPageReady is still false because textpageGenerationFinished // didn't have time to run, if so queue ourselves QTimer::singleShot(0, this, [this, request] { generatePixmap(request); }); return; } /** * 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->m_closing ) { d->mTextPageReady = false; d->textPageGenerationThread()->setPage( request->page() ); // dummy is used as a way to make sure the lambda gets disconnected each time it is executed // since not all the times the pixmap generation thread starts we want the text generation thread to also start QObject *dummy = new QObject(); connect(d_ptr->pixmapGenerationThread(), &QThread::started, dummy, [this, dummy] { delete dummy; d_ptr->textPageGenerationThread()->startGeneration(); }); } // pixmap generation thread must be started *after* connect(), else we may miss the start signal and get lock-ups (see bug 396137) d->pixmapGenerationThread()->startGeneration( request, calcBoundingBox ); return; } const QImage& img = image( request ); request->page()->setPixmap( request->observer(), new QPixmap( QPixmap::fromImage( img ) ), request->normalizedRect() ); const int pageNumber = request->page()->number(); d->mPixmapReady = true; signalPixmapRequestDone( request ); if ( calcBoundingBox ) updatePageBoundingBox( pageNumber, Utils::imageBoundingBox( &img ) ); } bool Generator::canGenerateTextPage() const { Q_D( const Generator ); return d->mTextPageReady; } void Generator::generateTextPage( Page *page ) { TextRequest treq( page ); TextPage *tp = textPage( &treq ); page->setTextPage( tp ); signalTextGenerationDone( page, tp ); } QImage Generator::image( PixmapRequest *request ) { Q_D( Generator ); return d->image( request ); } TextPage* Generator::textPage( TextRequest * ) { return nullptr; } DocumentInfo Generator::generateDocumentInfo(const QSet &keys) const { Q_UNUSED(keys); return DocumentInfo(); } const DocumentSynopsis * Generator::generateDocumentSynopsis() { return nullptr; } FontInfo::List Generator::fontsForPage( int ) { return FontInfo::List(); } const QList * Generator::embeddedFiles() const { return nullptr; } 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; } Generator::PrintError Generator::printError() const { return UnknownPrintError; } void Generator::opaqueAction( const BackendOpaqueAction * /*action*/ ) { } 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; } void Generator::walletDataForFile( const QString &fileName, QString *walletName, QString *walletFolder, QString *walletKey ) const { #ifdef WITH_KWALLET *walletKey = fileName.section( QLatin1Char('/'), -1, -1); *walletName = KWallet::Wallet::NetworkWallet(); *walletFolder = QStringLiteral("KPdf"); #endif } 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; } void Generator::signalPartialPixmapRequest( PixmapRequest *request, const QImage &image ) { if ( request->shouldAbortRender() ) return; PagePrivate *pagePrivate = PagePrivate::get( request->page() ); pagePrivate->setPixmap( request->observer(), new QPixmap( QPixmap::fromImage( image ) ), request->normalizedRect(), true /* isPartialPixmap */ ); const int pageNumber = request->page()->number(); request->observer()->notifyPageChanged( pageNumber, Okular::DocumentObserver::Pixmap ); } const Document * Generator::document() const { Q_D( const Generator ); if ( d->m_document ) { return d->m_document->m_parent; } return nullptr; } 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(); if (key == QLatin1String("PaperColor")) return documentMetaData(PaperColorMetaData, option); if (key == QLatin1String("GraphicsAntialias")) return documentMetaData(GraphicsAntialiasMetaData, option); if (key == QLatin1String("TextAntialias")) return documentMetaData(TextAntialiasMetaData, option); if (key == QLatin1String("TextHinting")) return documentMetaData(TextHintingMetaData, option); return QVariant(); } QVariant Generator::documentMetaData( const DocumentMetaDataKey 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; + 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*/) { } void Generator::setDPI(const QSizeF & dpi) { Q_D( Generator ); d->m_dpi = dpi; } QSizeF Generator::dpi() const { Q_D( const Generator ); return d->m_dpi; } QAbstractItemModel * Generator::layersModel() const { return nullptr; } TextRequest::TextRequest() : d( new TextRequestPrivate ) { d->mPage = nullptr; d->mShouldAbortExtraction = 0; } TextRequest::TextRequest( Page *page ) : d( new TextRequestPrivate ) { d->mPage = page; d->mShouldAbortExtraction = 0; } TextRequest::~TextRequest() { delete d; } Page *TextRequest::page() const { return d->mPage; } bool TextRequest::shouldAbortExtraction() const { return d->mShouldAbortExtraction != 0; } TextRequestPrivate *TextRequestPrivate::get(const TextRequest *req) { return req->d; } PixmapRequest::PixmapRequest( DocumentObserver *observer, int pageNumber, int width, int height, int priority, PixmapRequestFeatures features ) : d( new PixmapRequestPrivate ) { d->mObserver = observer; d->mPageNumber = pageNumber; d->mWidth = ceil(width * qApp->devicePixelRatio()); d->mHeight = ceil(height * qApp->devicePixelRatio()); d->mPriority = priority; d->mFeatures = features; d->mForce = false; d->mTile = false; d->mNormalizedRect = NormalizedRect(); d->mPartialUpdatesWanted = false; d->mShouldAbortRender = 0; } PixmapRequest::~PixmapRequest() { delete d; } DocumentObserver *PixmapRequest::observer() const { return d->mObserver; } 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->mFeatures & Asynchronous; } bool PixmapRequest::preload() const { return d->mFeatures & Preload; } Page* PixmapRequest::page() const { return d->mPage; } void PixmapRequest::setTile( bool tile ) { d->mTile = tile; } bool PixmapRequest::isTile() const { return d->mTile; } void PixmapRequest::setNormalizedRect( const NormalizedRect &rect ) { if ( d->mNormalizedRect == rect ) return; d->mNormalizedRect = rect; } const NormalizedRect& PixmapRequest::normalizedRect() const { return d->mNormalizedRect; } void PixmapRequest::setPartialUpdatesWanted(bool partialUpdatesWanted) { d->mPartialUpdatesWanted = partialUpdatesWanted; } bool PixmapRequest::partialUpdatesWanted() const { return d->mPartialUpdatesWanted; } bool PixmapRequest::shouldAbortRender() const { return d->mShouldAbortRender != 0; } Okular::TilesManager* PixmapRequestPrivate::tilesManager() const { return mPage->d->tilesManager(mObserver); } PixmapRequestPrivate *PixmapRequestPrivate::get(const PixmapRequest *req) { return req->d; } void PixmapRequestPrivate::swap() { qSwap( mWidth, mHeight ); } class Okular::ExportFormatPrivate : public QSharedData { public: ExportFormatPrivate( const QString &description, const QMimeType &mimeType, const QIcon &icon = QIcon() ) : QSharedData(), mDescription( description ), mMimeType( mimeType ), mIcon( icon ) { } ~ExportFormatPrivate() { } QString mDescription; QMimeType mMimeType; QIcon mIcon; }; ExportFormat::ExportFormat() : d( new ExportFormatPrivate( QString(), QMimeType() ) ) { } ExportFormat::ExportFormat( const QString &description, const QMimeType &mimeType ) : d( new ExportFormatPrivate( description, mimeType ) ) { } ExportFormat::ExportFormat( const QIcon &icon, const QString &description, const QMimeType &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; } QMimeType ExportFormat::mimeType() const { return d->mMimeType; } QIcon ExportFormat::icon() const { return d->mIcon; } bool ExportFormat::isNull() const { return !d->mMimeType.isValid() || d->mDescription.isNull(); } ExportFormat ExportFormat::standardFormat( StandardExportFormat type ) { QMimeDatabase db; switch ( type ) { case PlainText: return ExportFormat( QIcon::fromTheme( QStringLiteral("text-x-generic") ), i18n( "Plain &Text..." ), db.mimeTypeForName( QStringLiteral("text/plain") ) ); break; case PDF: return ExportFormat( QIcon::fromTheme( QStringLiteral("application-pdf") ), i18n( "PDF" ), db.mimeTypeForName( QStringLiteral("application/pdf") ) ); break; case OpenDocumentText: return ExportFormat( QIcon::fromTheme( QStringLiteral("application-vnd.oasis.opendocument.text") ), i18nc( "This is the document format", "OpenDocument Text" ), db.mimeTypeForName( QStringLiteral("application/vnd.oasis.opendocument.text") ) ); break; case HTML: return ExportFormat( QIcon::fromTheme( QStringLiteral("text-html") ), i18nc( "This is the document format", "HTML" ), db.mimeTypeForName( QStringLiteral("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 ) { PixmapRequestPrivate *reqPriv = PixmapRequestPrivate::get(&req); str << "PixmapRequest:" << &req; str << "- observer:" << (qulonglong)req.observer(); str << "- page:" << req.pageNumber(); str << "- width:" << req.width(); str << "- height:" << req.height(); str << "- priority:" << req.priority(); str << "- async:" << ( req.asynchronous() ? "true" : "false" ); str << "- tile:" << ( req.isTile() ? "true" : "false" ); str << "- rect:" << req.normalizedRect(); str << "- preload:" << ( req.preload() ? "true" : "false" ); str << "- partialUpdates:" << ( req.partialUpdatesWanted() ? "true" : "false" ); str << "- shouldAbort:" << ( req.shouldAbortRender() ? "true" : "false" ); str << "- force:" << ( reqPriv->mForce ? "true" : "false" ); return str; } #include "moc_generator.cpp" /* kate: replace-tabs on; indent-width 4; */ diff --git a/core/generator_p.h b/core/generator_p.h index 239be0a79..e5854c81f 100644 --- a/core/generator_p.h +++ b/core/generator_p.h @@ -1,196 +1,196 @@ /*************************************************************************** * Copyright (C) 2007 Tobias Koenig * * Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group * * company, info@kdab.com. Work sponsored by the * * LiMux project of the city of Munich * * * * 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. * ***************************************************************************/ #ifndef OKULAR_THREADEDGENERATOR_P_H #define OKULAR_THREADEDGENERATOR_P_H #include "area.h" +#include #include #include #include class QEventLoop; -class QMutex; #include "generator.h" #include "page.h" namespace Okular { class DocumentObserver; class DocumentPrivate; class FontInfo; class Generator; class Page; class PixmapGenerationThread; class PixmapRequest; class TextPage; class TextPageGenerationThread; class TilesManager; class GeneratorPrivate { public: GeneratorPrivate(); virtual ~GeneratorPrivate(); Q_DECLARE_PUBLIC( Generator ) Generator *q_ptr; PixmapGenerationThread* pixmapGenerationThread(); TextPageGenerationThread* textPageGenerationThread(); void pixmapGenerationFinished(); void textpageGenerationFinished(); QMutex* threadsLock(); virtual QVariant metaData( const QString &key, const QVariant &option ) const; virtual QImage image( PixmapRequest * ); DocumentPrivate *m_document; // NOTE: the following should be a QSet< GeneratorFeature >, // but it is not to avoid #include'ing generator.h QSet< int > m_features; PixmapGenerationThread *mPixmapGenerationThread; TextPageGenerationThread *mTextPageGenerationThread; - mutable QMutex *m_mutex; - QMutex *m_threadsMutex; + mutable QMutex m_mutex; + QMutex m_threadsMutex; bool mPixmapReady : 1; bool mTextPageReady : 1; bool m_closing : 1; QEventLoop *m_closingLoop; QSizeF m_dpi; }; class PixmapRequestPrivate { public: void swap(); TilesManager *tilesManager() const; static PixmapRequestPrivate *get(const PixmapRequest *req); DocumentObserver *mObserver; int mPageNumber; int mWidth; int mHeight; int mPriority; int mFeatures; bool mForce : 1; bool mTile : 1; bool mPartialUpdatesWanted : 1; Page *mPage; NormalizedRect mNormalizedRect; QAtomicInt mShouldAbortRender; QImage mResultImage; }; class TextRequestPrivate { public: static TextRequestPrivate *get(const TextRequest *req); Page *mPage; QAtomicInt mShouldAbortExtraction; }; class PixmapGenerationThread : public QThread { Q_OBJECT public: explicit PixmapGenerationThread( Generator *generator ); void startGeneration( PixmapRequest *request, bool calcBoundingRect ); void endGeneration(); PixmapRequest *request() const; QImage image() const; bool calcBoundingBox() const; NormalizedRect boundingBox() const; protected: void run() override; private: Generator *mGenerator; PixmapRequest *mRequest; NormalizedRect mBoundingBox; bool mCalcBoundingBox : 1; }; class TextPageGenerationThread : public QThread { Q_OBJECT public: explicit TextPageGenerationThread( Generator *generator ); void endGeneration(); void setPage( Page *page ); Page *page() const; TextPage* textPage() const; void abortExtraction(); bool shouldAbortExtraction() const; public slots: void startGeneration(); protected: void run() override; private: Generator *mGenerator; TextPage *mTextPage; TextRequest mTextRequest; }; class FontExtractionThread : public QThread { Q_OBJECT public: FontExtractionThread( Generator *generator, int pages ); void startExtraction( bool async ); void stopExtraction(); Q_SIGNALS: void gotFont( const Okular::FontInfo& ); void progress( int page ); protected: void run() override; private: Generator *mGenerator; int mNumOfPages; bool mGoOn; }; } Q_DECLARE_METATYPE(Okular::Page*) #endif