📄 generator_pdf.cpp.svn-base
字号:
/*************************************************************************** * Copyright (C) 2004-2006 by Albert Astals Cid <tsdgeos@terra.es> * * Copyright (C) 2004 by Enrico Ros <eros.kde@email.it> * * * * 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. * ***************************************************************************/// qt/kde includes#include <qfile.h>#include <qimage.h>#include <qregexp.h>#include <qtextstream.h>#include <kauthorized.h>#include <klocale.h>#include <kpassworddialog.h>#include <kwallet.h>#include <kprinter.h>#include <ktempfile.h>#include <kmessagebox.h>#include <kdebug.h>// local includes#include "generator_pdf.h"#include "core/observer.h" //for PAGEVIEW_ID#include "core/page.h"#include "core/annotations.h"#include "core/pagetransition.h"#include "settings.h"#include <config.h>#include <config-okular.h>class PDFEmbeddedFile : public EmbeddedFile{ public: PDFEmbeddedFile(Poppler::EmbeddedFile *f) : ef(f) { } QString name() const { return ef->name(); } QString description() const { return ef->description(); } QByteArray data() const { return ef->data(); } QDateTime modificationDate() const { return ef->modDate(); } QDateTime creationDate() const { return ef->createDate(); } private: Poppler::EmbeddedFile *ef;};static void fillViewportFromLinkDestination( DocumentViewport &viewport, const Poppler::LinkDestination &destination, const Poppler::Document *pdfdoc ){ viewport.pageNumber = destination.pageNumber() - 1; if (viewport.pageNumber == -1) return; // get destination position // TODO add other attributes to the viewport (taken from link)// switch ( destination->getKind() )// {// case destXYZ: if (destination.isChangeLeft() || destination.isChangeTop()) { Poppler::Page *page = pdfdoc->page( viewport.pageNumber ); // TODO remember to change this if we implement DPI and/or rotation double left, top; left = destination.left(); top = destination.top(); QSize pageSize = page->pageSize(); delete page; viewport.rePos.normalizedX = (double)left / (double)pageSize.width(); viewport.rePos.normalizedY = (double)top / (double)pageSize.height(); viewport.rePos.enabled = true; viewport.rePos.pos = DocumentViewport::TopLeft; } /* TODO if ( dest->getChangeZoom() ) make zoom change*//* break; default: // implement the others cases break;*/// }}static KPDFLink* createKPDFLinkFromPopplerLink(const Poppler::Link *popplerLink, const Poppler::Document *pdfdoc){ KPDFLink *kpdfLink = 0; const Poppler::LinkGoto *popplerLinkGoto; const Poppler::LinkExecute *popplerLinkExecute; const Poppler::LinkBrowse *popplerLinkBrowse; const Poppler::LinkAction *popplerLinkAction; DocumentViewport viewport; switch(popplerLink->linkType()) { case Poppler::Link::None: break; case Poppler::Link::Goto: popplerLinkGoto = static_cast<const Poppler::LinkGoto *>(popplerLink); fillViewportFromLinkDestination( viewport, popplerLinkGoto->destination(), pdfdoc ); kpdfLink = new KPDFLinkGoto(popplerLinkGoto->fileName(), viewport); break; case Poppler::Link::Execute: popplerLinkExecute = static_cast<const Poppler::LinkExecute *>(popplerLink); kpdfLink = new KPDFLinkExecute( popplerLinkExecute->fileName(), popplerLinkExecute->parameters() ); break; case Poppler::Link::Browse: popplerLinkBrowse = static_cast<const Poppler::LinkBrowse *>(popplerLink); kpdfLink = new KPDFLinkBrowse( popplerLinkBrowse->url() ); break; case Poppler::Link::Action: popplerLinkAction = static_cast<const Poppler::LinkAction *>(popplerLink); kpdfLink = new KPDFLinkAction( (KPDFLinkAction::ActionType)popplerLinkAction->actionType() ); break; case Poppler::Link::Movie: // not implemented break; } return kpdfLink;}static QLinkedList<ObjectRect*> generateKPDFLinks( const QList<Poppler::Link*> &popplerLinks, int width, int height, const Poppler::Document *pdfdoc ){ QLinkedList<ObjectRect*> kpdfLinks; foreach(const Poppler::Link *popplerLink, popplerLinks) { QRectF linkArea = popplerLink->linkArea(); double nl = linkArea.left() / (double)width, nt = linkArea.top() / (double)height, nr = linkArea.right() / (double)width, nb = linkArea.bottom() / (double)height; // create the rect using normalized coords and attach the KPDFLink to it ObjectRect * rect = new ObjectRect( nl, nt, nr, nb, false, ObjectRect::Link, createKPDFLinkFromPopplerLink(popplerLink, pdfdoc) ); // add the ObjectRect to the container kpdfLinks.push_front( rect ); } qDeleteAll(popplerLinks); return kpdfLinks;}/** NOTES on threading: * internal: thread race prevention is done via the 'docLock' mutex. the * mutex is needed only because we have the asynchronous thread; else * the operations are all within the 'gui' thread, scheduled by the * Qt scheduler and no mutex is needed. * external: dangerous operations are all locked via mutex internally, and the * only needed external thing is the 'canGeneratePixmap' method * that tells if the generator is free (since we don't want an * internal queue to store PixmapRequests). A generatedPixmap call * without the 'ready' flag set, results in undefined behavior. * So, as example, printing while generating a pixmap asynchronously is safe, * it might only block the gui thread by 1) waiting for the mutex to unlock * in async thread and 2) doing the 'heavy' print operation. */OKULAR_EXPORT_PLUGIN(PDFGenerator)PDFGenerator::PDFGenerator( KPDFDocument * doc ) : Generator( doc ), pdfdoc( 0 ), ready( true ), pixmapRequest( 0 ), docInfoDirty( true ), docSynopsisDirty( true ), docFontsDirty( true ), docEmbeddedFilesDirty( true ){ // update the configuration reparseConfig(); // generate the pixmapGeneratorThread generatorThread = new PDFPixmapGeneratorThread( this ); connect(generatorThread, SIGNAL(finished()), this, SLOT(threadFinished()), Qt::QueuedConnection);}PDFGenerator::~PDFGenerator(){ // stop and delete the generator thread if ( generatorThread ) { generatorThread->wait(); delete generatorThread; }}void PDFGenerator::setOrientation(QVector<KPDFPage*> & pagesVector, int orientation){ loadPages(pagesVector,orientation,true);}//BEGIN Generator inherited functionsbool PDFGenerator::loadDocument( const QString & filePath, QVector<KPDFPage*> & pagesVector ){#ifndef NDEBUG if ( pdfdoc ) { kDebug() << "PDFGenerator: multiple calls to loadDocument. Check it." << endl; return false; }#endif // create PDFDoc for the given file pdfdoc = Poppler::Document::load( filePath, 0, 0 ); // if the file didn't open correctly it might be encrypted, so ask for a pass bool firstInput = true; bool triedWallet = false; KWallet::Wallet * wallet = 0; while ( !pdfdoc && pdfdoc->isLocked() ) { QByteArray password; // 1.A. try to retrieve the first password from the kde wallet system if ( !triedWallet ) { QString walletName = KWallet::Wallet::NetworkWallet(); wallet = KWallet::Wallet::openWallet( walletName ); if ( wallet ) { // use the KPdf folder (and create if missing) if ( !wallet->hasFolder( "KPdf" ) ) wallet->createFolder( "KPdf" ); wallet->setFolder( "KPdf" ); // look for the pass in that folder QString retrievedPass; if ( !wallet->readPassword( filePath.section('/', -1, -1), retrievedPass ) ) password = retrievedPass.toLocal8Bit(); } triedWallet = true; } // 1.B. if not retrieved, ask the password using the kde password dialog if ( password.isNull() ) { QString prompt; if ( firstInput ) prompt = i18n( "Please insert the password to read the document:" ); else prompt = i18n( "Incorrect password. Try again:" ); firstInput = false; // if the user presses cancel, abort opening if ( KPasswordDialog::getPassword( 0, password, prompt ) != KPasswordDialog::Accepted ) break; } // 2. reopen the document using the password pdfdoc->unlock( password, password ); // 3. if the password is correct, store it to the wallet if ( !pdfdoc->isLocked() && wallet && /*safety check*/ wallet->isOpen() ) { QString goodPass = QString::fromLocal8Bit( password.data() ); wallet->writePassword( filePath.section('/', -1, -1), goodPass ); } } if ( !pdfdoc ) { pdfdoc = 0; return false; } // build Pages (currentPage was set -1 by deletePages) uint pageCount = pdfdoc->numPages(); pagesVector.resize(pageCount); loadPages(pagesVector, 0, false); // the file has been loaded correctly return true;}bool PDFGenerator::closeDocument(){ // remove internal objects docLock.lock(); delete pdfdoc; pdfdoc = 0; docLock.unlock(); return true;}void PDFGenerator::loadPages(QVector<KPDFPage*> &pagesVector, int rotation, bool clear){ // TODO XPDF 3.01 check int count=pagesVector.count(),w=0,h=0; for ( int i = 0; i < count ; i++ ) { // get xpdf page Poppler::Page * p = pdfdoc->page( i ); QSize pSize = p->pageSize(); w = pSize.width(); h = pSize.height(); int orientation; switch (p->orientation()) { case Poppler::Page::Landscape: orientation = 1; break; case Poppler::Page::UpsideDown: orientation = 2; break; case Poppler::Page::Seascape: orientation = 3; break; case Poppler::Page::Portrait: orientation = 0; break; } if (rotation % 2 == 1) qSwap(w,h); // init a kpdfpage, add transition and annotation information KPDFPage * page = new KPDFPage( i, w, h, orientation ); addTransition( p, page ); if ( true ) //TODO real check addAnnotations( p, page );// kWarning() << page->width() << "x" << page->height() << endl;// need a way to find efficient (maybe background textpage generation) kDebug() << "loadpages with rotation" << rotation << " and orientation " << orientation << endl; docLock.lock(); QList<Poppler::TextBox*> textList = p->textList((Poppler::Page::Rotation)rotation); docLock.unlock(); page->setSearchPage(abstractTextPage(textList, page->height(), page->width(), orientation)); qDeleteAll(textList); delete p; if (clear && pagesVector[i]) delete pagesVector[i]; // set the kpdfpage at the right position in document's pages vector pagesVector[i] = page;// kWarning() << page->width() << "x" << page->height() << endl; }}QString PDFGenerator::getText( const RegularAreaRect * area, KPDFPage * page ){ QRect rect = area->first()->geometry((int)page->width(),(int)page->height()); Poppler::Page *pp = pdfdoc->page( page->number() ); QString text = pp->text(rect); delete pp; return text;}RegularAreaRect * PDFGenerator::findText (const QString & text, SearchDir dir, const bool strictCase, const RegularAreaRect * sRect, KPDFPage * page ){ dir = sRect ? NextRes : FromTop; QRectF rect; if ( dir == NextRes ) { // when using thein ternal search we only play with normrects rect.setLeft( sRect->first()->left * page->width() ); rect.setTop( sRect->first()->top * page->height() ); rect.setRight( sRect->first()->right * page->width() ); rect.setBottom( sRect->first()->bottom * page->height() ); } // this loop is only for 'bad case' Reses bool found = false; Poppler::Page *pp = pdfdoc->page( page->number() ); docLock.lock(); Poppler::Page::SearchMode sm; if (strictCase) sm = Poppler::Page::CaseSensitive; else sm = Poppler::Page::CaseInsensitive; while ( !found ) { if ( dir == FromTop ) found = pp->search(text, rect, Poppler::Page::FromTop, sm); else if ( dir == NextRes ) found = pp->search(text, rect, Poppler::Page::NextResult, sm); else if ( dir == PrevRes ) found = pp->search(text, rect, Poppler::Page::PreviousResult, sm); // if not found (even in case unsensitive search), terminate if ( !found ) break; } docLock.unlock(); delete pp; // if the page was found, return a new normalizedRect if ( found ) { RegularAreaRect *ret=new RegularAreaRect; ret->append (new NormalizedRect( rect.left() / page->width(), rect.top() / page->height(), rect.right() / page->width(), rect.bottom() / page->height() ) ); return ret; } return 0;}const DocumentInfo * PDFGenerator::generateDocumentInfo(){ if ( docInfoDirty ) { docLock.lock(); docInfo.set( "mimeType", "application/pdf" ); if ( pdfdoc ) { // compile internal structure reading properties from PDFDoc docInfo.set( "title", pdfdoc->info("Title"), i18n("Title") ); docInfo.set( "subject", pdfdoc->info("Subject"), i18n("Subject") ); docInfo.set( "author", pdfdoc->info("Author"), i18n("Author") ); docInfo.set( "keywords", pdfdoc->info("Keywords"), i18n("Keywords") ); docInfo.set( "creator", pdfdoc->info("Creator"), i18n("Creator") ); docInfo.set( "producer", pdfdoc->info("Producer"), i18n("Producer") ); docInfo.set( "creationDate", KGlobal::locale()->formatDateTime( pdfdoc->date("CreationDate"), false, true ), i18n("Created") ); docInfo.set( "modificationDate", KGlobal::locale()->formatDateTime( pdfdoc->date("ModDate"), false, true ), i18n("Modified") ); docInfo.set( "format", i18nc( "PDF v. <version>", "PDF v. %1", QString::number( pdfdoc->pdfVersion() ) ), i18n( "Format" ) ); docInfo.set( "encryption", pdfdoc->isEncrypted() ? i18n( "Encrypted" ) : i18n( "Unencrypted" ),
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -