generator_pdf.cpp 61.4 KB
Newer Older
Albert Astals Cid's avatar
Albert Astals Cid committed
1
/***************************************************************************
2
 *   Copyright (C) 2004-2008 by Albert Astals Cid <tsdgeos@terra.es>       *
3 4 5 6 7 8 9 10
 *   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.                                   *
 ***************************************************************************/

Albert Astals Cid's avatar
Albert Astals Cid committed
11 12
#include "generator_pdf.h"

13
// qt/kde includes
14
#include <qcheckbox.h>
Pino Toscano's avatar
Pino Toscano committed
15
#include <qcolor.h>
16
#include <qfile.h>
17
#include <qimage.h>
18
#include <qlayout.h>
19
#include <qmutex.h>
20
#include <qregexp.h>
21
#include <qtextstream.h>
22 23
#include <QtGui/QPrinter>

24
#include <kaboutdata.h>
25
#include <klocale.h>
26
#include <kmessagebox.h>
27
#include <kpassworddialog.h>
28
#include <kwallet.h>
29
#include <ktemporaryfile.h>
30
#include <kdebug.h>
Laurent Montel's avatar
Laurent Montel committed
31
#include <kglobal.h>
32

Pino Toscano's avatar
Pino Toscano committed
33
#include <okular/core/action.h>
34 35
#include <okular/core/page.h>
#include <okular/core/annotations.h>
36
#include <okular/core/movie.h>
37 38 39
#include <okular/core/pagetransition.h>
#include <okular/core/sound.h>
#include <okular/core/sourcereference.h>
Pino Toscano's avatar
Pino Toscano committed
40
#include <okular/core/textpage.h>
John Layt's avatar
John Layt committed
41
#include <okular/core/fileprinter.h>
42
#include <okular/core/utils.h>
43

44
#include <config-okular-poppler.h>
45

46
#include "formfields.h"
47
#include "popplerembeddedfile.h"
48

49 50 51 52
#ifdef HAVE_POPPLER_0_9
Q_DECLARE_METATYPE(Poppler::FontInfo)
#endif

Pino Toscano's avatar
Pino Toscano committed
53
static const int PDFDebug = 4710;
54

55
class PDFOptionsPage : public QWidget
56 57 58 59
{
   public:
       PDFOptionsPage()
       {
60
           setWindowTitle( i18n( "PDF Options" ) );
61 62 63 64 65 66 67 68
           QVBoxLayout *layout = new QVBoxLayout(this);
           m_forceRaster = new QCheckBox(i18n("Force rasterization"), this);
           m_forceRaster->setToolTip(i18n("Rasterize into an image before printing"));
           m_forceRaster->setWhatsThis(i18n("Forces the rasterization of each page into an image before printing it. This usually gives somewhat worse results, but is useful when printing documents that appear to print incorrectly."));
           layout->addWidget(m_forceRaster);
           layout->addStretch(1);
       }

John Layt's avatar
John Layt committed
69
       bool printForceRaster()
70
       {
John Layt's avatar
John Layt committed
71
           return m_forceRaster->isChecked();
72 73
       }

John Layt's avatar
John Layt committed
74
       void setPrintForceRaster( bool forceRaster )
75
       {
John Layt's avatar
John Layt committed
76
           m_forceRaster->setChecked( forceRaster );
77 78 79 80 81 82 83
       }

    private:
        QCheckBox *m_forceRaster;
};


Pino Toscano's avatar
Pino Toscano committed
84
static void fillViewportFromLinkDestination( Okular::DocumentViewport &viewport, const Poppler::LinkDestination &destination )
85
{
86
    viewport.pageNumber = destination.pageNumber() - 1;
87

88
    if (!viewport.isValid()) return;
89 90 91 92 93 94 95 96 97 98 99 100 101

    // get destination position
    // TODO add other attributes to the viewport (taken from link)
//     switch ( destination->getKind() )
//     {
//         case destXYZ:
            if (destination.isChangeLeft() || destination.isChangeTop())
            {
                // TODO remember to change this if we implement DPI and/or rotation
                double left, top;
                left = destination.left();
                top = destination.top();

102 103
                viewport.rePos.normalizedX = left;
                viewport.rePos.normalizedY = top;
104
                viewport.rePos.enabled = true;
105
                viewport.rePos.pos = Okular::DocumentViewport::TopLeft;
106 107 108 109 110 111 112 113 114 115 116 117
            }
            /* TODO
            if ( dest->getChangeZoom() )
                make zoom change*/
/*        break;

        default:
            // implement the others cases
        break;*/
//     }
}

118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
Okular::Sound* createSoundFromPopplerSound( const Poppler::SoundObject *popplerSound )
{
    Okular::Sound *sound = popplerSound->soundType() == Poppler::SoundObject::Embedded ? new Okular::Sound( popplerSound->data() ) : new Okular::Sound( popplerSound->url() );
    sound->setSamplingRate( popplerSound->samplingRate() );
    sound->setChannels( popplerSound->channels() );
    sound->setBitsPerSample( popplerSound->bitsPerSample() );
    switch ( popplerSound->soundEncoding() )
    {
        case Poppler::SoundObject::Raw:
            sound->setSoundEncoding( Okular::Sound::Raw );
            break;
        case Poppler::SoundObject::Signed:
            sound->setSoundEncoding( Okular::Sound::Signed );
            break;
        case Poppler::SoundObject::muLaw:
            sound->setSoundEncoding( Okular::Sound::muLaw );
            break;
        case Poppler::SoundObject::ALaw:
            sound->setSoundEncoding( Okular::Sound::ALaw );
            break;
    }
    return sound;
}

142 143 144 145 146 147
#ifdef HAVE_POPPLER_0_9
Okular::Movie* createMovieFromPopplerMovie( const Poppler::MovieObject *popplerMovie )
{
    Okular::Movie *movie = new Okular::Movie( popplerMovie->url() );
    movie->setSize( popplerMovie->size() );
    movie->setRotation( (Okular::Rotation)( popplerMovie->rotation() / 90 ) );
148 149
    movie->setShowControls( popplerMovie->showControls() );
    movie->setPlayMode( (Okular::Movie::PlayMode)popplerMovie->playMode() );
150 151 152 153
    return movie;
}
#endif

154
Okular::Action* createLinkFromPopplerLink(const Poppler::Link *popplerLink)
155
{
Pino Toscano's avatar
Pino Toscano committed
156
	Okular::Action *link = 0;
157 158 159 160
	const Poppler::LinkGoto *popplerLinkGoto;
	const Poppler::LinkExecute *popplerLinkExecute;
	const Poppler::LinkBrowse *popplerLinkBrowse;
	const Poppler::LinkAction *popplerLinkAction;
161
	const Poppler::LinkSound *popplerLinkSound;
162 163 164
#ifdef HAVE_POPPLER_0_9
	const Poppler::LinkJavaScript *popplerLinkJS;
#endif
165
	Okular::DocumentViewport viewport;
166 167 168 169 170 171 172 173
	
	switch(popplerLink->linkType())
	{
		case Poppler::Link::None:
		break;
	
		case Poppler::Link::Goto:
			popplerLinkGoto = static_cast<const Poppler::LinkGoto *>(popplerLink);
Pino Toscano's avatar
Pino Toscano committed
174
			fillViewportFromLinkDestination( viewport, popplerLinkGoto->destination() );
175
			link = new Okular::GotoAction(popplerLinkGoto->fileName(), viewport);
176 177 178 179
		break;
		
		case Poppler::Link::Execute:
			popplerLinkExecute = static_cast<const Poppler::LinkExecute *>(popplerLink);
180
			link = new Okular::ExecuteAction( popplerLinkExecute->fileName(), popplerLinkExecute->parameters() );
181 182 183 184
		break;
		
		case Poppler::Link::Browse:
			popplerLinkBrowse = static_cast<const Poppler::LinkBrowse *>(popplerLink);
185
			link = new Okular::BrowseAction( popplerLinkBrowse->url() );
186 187 188 189
		break;
		
		case Poppler::Link::Action:
			popplerLinkAction = static_cast<const Poppler::LinkAction *>(popplerLink);
190
			link = new Okular::DocumentAction( (Okular::DocumentAction::DocumentActionType)popplerLinkAction->actionType() );
191 192
		break;
		
193 194 195 196
		case Poppler::Link::Sound:
		{
			popplerLinkSound = static_cast<const Poppler::LinkSound *>(popplerLink);
			Poppler::SoundObject *popplerSound = popplerLinkSound->sound();
197
			Okular::Sound *sound = createSoundFromPopplerSound( popplerSound );
198
			link = new Okular::SoundAction( popplerLinkSound->volume(), popplerLinkSound->synchronous(), popplerLinkSound->repeat(), popplerLinkSound->mix(), sound );
199 200 201
		}
		break;
		
202 203 204 205 206 207 208 209 210
#ifdef HAVE_POPPLER_0_9
		case Poppler::Link::JavaScript:
		{
			popplerLinkJS = static_cast<const Poppler::LinkJavaScript *>(popplerLink);
			link = new Okular::ScriptAction( Okular::JavaScript, popplerLinkJS->script() );
		}
		break;
#endif
		
211 212 213 214 215
		case Poppler::Link::Movie:
			// not implemented
		break;
	}
	
216
	return link;
217 218
}

Pino Toscano's avatar
Pino Toscano committed
219
static QLinkedList<Okular::ObjectRect*> generateLinks( const QList<Poppler::Link*> &popplerLinks )
220
{
221
	QLinkedList<Okular::ObjectRect*> links;
222 223 224
	foreach(const Poppler::Link *popplerLink, popplerLinks)
	{
		QRectF linkArea = popplerLink->linkArea();
225 226 227 228
		double nl = linkArea.left(),
		       nt = linkArea.top(),
		       nr = linkArea.right(),
		       nb = linkArea.bottom();
229
		// create the rect using normalized coords and attach the Okular::Link to it
Pino Toscano's avatar
Pino Toscano committed
230
		Okular::ObjectRect * rect = new Okular::ObjectRect( nl, nt, nr, nb, false, Okular::ObjectRect::Action, createLinkFromPopplerLink(popplerLink) );
231
		// add the ObjectRect to the container
232
		links.push_front( rect );
233 234
	}
	qDeleteAll(popplerLinks);
235
	return links;
236 237
}

Pino Toscano's avatar
Pino Toscano committed
238
extern Okular::Annotation* createAnnotationFromPopplerAnnotation( Poppler::Annotation *ann, bool * doDelete );
239

240 241
/** NOTES on threading:
 * internal: thread race prevention is done via the 'docLock' mutex. the
242
 *           mutex is needed only because we have the asynchronous thread; else
243 244 245 246 247 248 249
 *           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.
250
 * So, as example, printing while generating a pixmap asynchronously is safe,
251 252 253 254
 * 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.
 */

255
static KAboutData createAboutData()
256
{
257
    KAboutData aboutData(
258 259
         "okular_poppler",
         "okular_poppler",
260
         ki18n( "PDF Backend" ),
261
         "0.1",
262
         ki18n( "A PDF file renderer" ),
263
         KAboutData::License_GPL,
264
         ki18n( "© 2005-2008 Albert Astals Cid" )
265
    );
Pino Toscano's avatar
Pino Toscano committed
266
    aboutData.addAuthor( ki18n( "Albert Astals Cid" ), KLocalizedString(), "aacid@kde.org" );
267 268 269 270 271 272 273 274
    return aboutData;
}

OKULAR_EXPORT_PLUGIN(PDFGenerator, createAboutData())

PDFGenerator::PDFGenerator( QObject *parent, const QVariantList &args )
    : Generator( parent, args ), pdfdoc( 0 ), ready( true ),
    pixmapRequest( 0 ), docInfoDirty( true ), docSynopsisDirty( true ),
275
    docEmbeddedFilesDirty( true ), nextFontPage( 0 )
276
{
277
    setFeature( TextExtraction );
278
    setFeature( FontInfo );
John Layt's avatar
John Layt committed
279
    setFeature( PrintPostscript );
280
    setFeature( ReadRawData );
281 282
    // generate the pixmapGeneratorThread
    generatorThread = new PDFPixmapGeneratorThread( this );
283
    connect(generatorThread, SIGNAL(finished()), this, SLOT(threadFinished()), Qt::QueuedConnection);
284 285
}

286
PDFGenerator::~PDFGenerator()
287
{
288
    // stop and delete the generator thread
289 290 291 292 293
    if ( generatorThread )
    {
        generatorThread->wait();
        delete generatorThread;
    }
John Layt's avatar
John Layt committed
294 295

    delete pdfOptionsPage;
296 297
}

Enrico Ros's avatar
Enrico Ros committed
298
//BEGIN Generator inherited functions
299
bool PDFGenerator::loadDocument( const QString & filePath, QVector<Okular::Page*> & pagesVector )
300
{
301 302 303
#ifndef NDEBUG
    if ( pdfdoc )
    {
304
        kDebug(PDFDebug) << "PDFGenerator: multiple calls to loadDocument. Check it.";
305 306 307
        return false;
    }
#endif
308
    // create PDFDoc for the given file
309
    pdfdoc = Poppler::Document::load( filePath, 0, 0 );
310 311 312 313 314 315 316
    bool success = init(pagesVector, filePath.section('/', -1, -1));
    if (success && QFile::exists(filePath + QLatin1String( "sync" )))
    {
        loadPdfSync(filePath, pagesVector);
    }
    return success;
}
317

318 319 320 321 322
bool PDFGenerator::loadDocumentFromData( const QByteArray & fileData, QVector<Okular::Page*> & pagesVector )
{
#ifndef NDEBUG
    if ( pdfdoc )
    {
323
        kDebug(PDFDebug) << "PDFGenerator: multiple calls to loadDocument. Check it.";
324 325 326 327 328 329 330 331 332 333
        return false;
    }
#endif
    // create PDFDoc for the given file
    pdfdoc = Poppler::Document::loadFromData( fileData, 0, 0 );
    return init(pagesVector, QString());
}

bool PDFGenerator::init(QVector<Okular::Page*> & pagesVector, const QString &walletKey)
{
334
    // if the file didn't open correctly it might be encrypted, so ask for a pass
335 336 337
    bool firstInput = true;
    bool triedWallet = false;
    KWallet::Wallet * wallet = 0;
338
    bool keep = true;
Albert Astals Cid's avatar
Albert Astals Cid committed
339
    while ( pdfdoc && pdfdoc->isLocked() )
340
    {
Tobias Koenig's avatar
Tobias Koenig committed
341
        QString password;
342 343

        // 1.A. try to retrieve the first password from the kde wallet system
344
        if ( !triedWallet && !walletKey.isNull() )
345 346
        {
            QString walletName = KWallet::Wallet::NetworkWallet();
347 348
            WId parentwid = 0;
            if ( document() && document()->widget() )
Albert Astals Cid's avatar
Albert Astals Cid committed
349
                parentwid = document()->widget()->effectiveWinId();
350
            wallet = KWallet::Wallet::openWallet( walletName, parentwid );
351 352
            if ( wallet )
            {
353 354 355 356
                // use the KPdf folder (and create if missing)
                if ( !wallet->hasFolder( "KPdf" ) )
                    wallet->createFolder( "KPdf" );
                wallet->setFolder( "KPdf" );
357 358 359

                // look for the pass in that folder
                QString retrievedPass;
360
                if ( !wallet->readPassword( walletKey, retrievedPass ) )
Tobias Koenig's avatar
Tobias Koenig committed
361
                    password = retrievedPass;
362 363 364 365 366 367 368 369 370 371 372 373 374 375 376
            }
            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
377
            KPasswordDialog dlg( document()->widget(), wallet ? KPasswordDialog::ShowKeepPassword : KPasswordDialog::KPasswordDialogFlags() );
Pino Toscano's avatar
Pino Toscano committed
378 379
            dlg.setCaption( i18n( "Document Password" ) );
            dlg.setPrompt( prompt );
Laurent Montel's avatar
Laurent Montel committed
380
            if( !dlg.exec() )
381
                break;
Laurent Montel's avatar
Laurent Montel committed
382
            password = dlg.password();
383 384
            if ( wallet )
                keep = dlg.keepPassword();
385 386 387
        }

        // 2. reopen the document using the password
388
        pdfdoc->unlock( password.toLatin1(), password.toLatin1() );
389

390 391
        // 3. if the password is correct and the user chose to remember it, store it to the wallet
        if ( !pdfdoc->isLocked() && wallet && /*safety check*/ wallet->isOpen() && keep )
392
        {
393
            wallet->writePassword( walletKey, password );
394 395
        }
    }
396
    if ( !pdfdoc || pdfdoc->isLocked() )
397
    {
398
        delete pdfdoc;
399 400 401
        pdfdoc = 0;
        return false;
    }
402 403

    // build Pages (currentPage was set -1 by deletePages)
404 405
    uint pageCount = pdfdoc->numPages();
    pagesVector.resize(pageCount);
406
    rectsGenerated.fill(false, pageCount);
407

Albert Astals Cid's avatar
Albert Astals Cid committed
408
    loadPages(pagesVector, 0, false);
409

410 411
    // update the configuration
    reparseConfig();
412

413 414 415 416
    // the file has been loaded correctly
    return true;
}

417
bool PDFGenerator::doCloseDocument()
418 419
{
    // remove internal objects
420
    userMutex()->lock();
421 422
    delete pdfdoc;
    pdfdoc = 0;
423
    userMutex()->unlock();
424 425
    docInfoDirty = true;
    docSynopsisDirty = true;
426
    docSyn.clear();
427 428 429
    docEmbeddedFilesDirty = true;
    qDeleteAll(docEmbeddedFiles);
    docEmbeddedFiles.clear();
430
    nextFontPage = 0;
431 432 433 434

    return true;
}

435
void PDFGenerator::loadPages(QVector<Okular::Page*> &pagesVector, int rotation, bool clear)
436
{
Piotr Szymanski's avatar
Piotr Szymanski committed
437
    // TODO XPDF 3.01 check
438
    int count=pagesVector.count(),w=0,h=0;
439
    for ( int i = 0; i < count ; i++ )
440
    {
441
        // get xpdf page
442 443 444 445
        Poppler::Page * p = pdfdoc->page( i );
        QSize pSize = p->pageSize();
        w = pSize.width();
        h = pSize.height();
446
        Okular::Rotation orientation = Okular::Rotation0;
Albert Astals Cid's avatar
Albert Astals Cid committed
447
        switch (p->orientation())
448
        {
449 450 451 452
          case Poppler::Page::Landscape: orientation = Okular::Rotation90; break;
          case Poppler::Page::UpsideDown: orientation = Okular::Rotation180; break;
          case Poppler::Page::Seascape: orientation = Okular::Rotation270; break;
          case Poppler::Page::Portrait: orientation = Okular::Rotation0; break;
453 454
        }
        if (rotation % 2 == 1)
455
          qSwap(w,h);
456 457
        // init a Okular::page, add transition and annotation information
        Okular::Page * page = new Okular::Page( i, w, h, orientation );
458 459
        addTransition( p, page );
        if ( true ) //TODO real check
460
          addAnnotations( p, page );
461 462
        Poppler::Link * tmplink = p->action( Poppler::Page::Opening );
        if ( tmplink )
463
        {
Pino Toscano's avatar
Pino Toscano committed
464
            page->setPageAction( Okular::Page::Opening, createLinkFromPopplerLink( tmplink ) );
465 466
            delete tmplink;
        }
467 468
        tmplink = p->action( Poppler::Page::Closing );
        if ( tmplink )
469
        {
Pino Toscano's avatar
Pino Toscano committed
470
            page->setPageAction( Okular::Page::Closing, createLinkFromPopplerLink( tmplink ) );
471 472
            delete tmplink;
        }
473
        page->setDuration( p->duration() );
474
        page->setLabel( p->label() );
475 476

        addFormFields( p, page );
477
//        kWarning(PDFDebug).nospace() << page->width() << "x" << page->height();
478

479
#ifdef PDFGENERATOR_DEBUG
480
        kDebug(PDFDebug) << "load page" << i << "with rotation" << rotation << "and orientation" << orientation;
481
#endif
482
	delete p;
483

484 485
        if (clear && pagesVector[i])
            delete pagesVector[i];
486
        // set the Okular::page at the right position in document's pages vector
487 488
        pagesVector[i] = page;
    }
489 490
}

491
const Okular::DocumentInfo * PDFGenerator::generateDocumentInfo()
492 493 494
{
    if ( docInfoDirty )
    {
495
        userMutex()->lock();
496
        
497
        docInfo.set( Okular::DocumentInfo::MimeType, "application/pdf" );
498
        
499 500
        if ( pdfdoc )
        {
501
            // compile internal structure reading properties from PDFDoc
502 503 504 505 506 507 508 509 510 511
            docInfo.set( Okular::DocumentInfo::Title, pdfdoc->info("Title") );
            docInfo.set( Okular::DocumentInfo::Subject, pdfdoc->info("Subject") );
            docInfo.set( Okular::DocumentInfo::Author, pdfdoc->info("Author") );
            docInfo.set( Okular::DocumentInfo::Keywords, pdfdoc->info("Keywords") );
            docInfo.set( Okular::DocumentInfo::Creator, pdfdoc->info("Creator") );
            docInfo.set( Okular::DocumentInfo::Producer, pdfdoc->info("Producer") );
            docInfo.set( Okular::DocumentInfo::CreationDate,
                         KGlobal::locale()->formatDateTime( pdfdoc->date("CreationDate"), KLocale::LongDate, true ) );
            docInfo.set( Okular::DocumentInfo::ModificationDate,
                         KGlobal::locale()->formatDateTime( pdfdoc->date("ModDate"), KLocale::LongDate, true ) );
512

Pino Toscano's avatar
Pino Toscano committed
513
            docInfo.set( "format", i18nc( "PDF v. <version>", "PDF v. %1",
Chusslove Illich's avatar
Chusslove Illich committed
514
                          pdfdoc->pdfVersion() ), i18n( "Format" ) );
515 516 517 518
            docInfo.set( "encryption", pdfdoc->isEncrypted() ? i18n( "Encrypted" ) : i18n( "Unencrypted" ),
                         i18n("Security") );
            docInfo.set( "optimization", pdfdoc->isLinearized() ? i18n( "Yes" ) : i18n( "No" ),
                         i18n("Optimized") );
519

520
            docInfo.set( Okular::DocumentInfo::Pages, QString::number( pdfdoc->numPages() ) );
521 522 523
        }
        else
        {
524
            // TODO not sure one can reach here, check and if it is not possible, remove the code
525 526 527 528 529 530 531 532
            docInfo.set( Okular::DocumentInfo::Title, i18n("Unknown") );
            docInfo.set( Okular::DocumentInfo::Subject, i18n("Unknown") );
            docInfo.set( Okular::DocumentInfo::Author, i18n("Unknown") );
            docInfo.set( Okular::DocumentInfo::Keywords, i18n("Unknown") );
            docInfo.set( Okular::DocumentInfo::Creator, i18n("Unknown") );
            docInfo.set( Okular::DocumentInfo::Producer, i18n("Unknown") );
            docInfo.set( Okular::DocumentInfo::CreationDate, i18n("Unknown Date") );
            docInfo.set( Okular::DocumentInfo::ModificationDate, i18n("Unknown Date") );
533

534 535 536
            docInfo.set( "format", "PDF", i18n( "Format" ) );
            docInfo.set( "encryption", i18n( "Unknown Encryption" ), i18n( "Security" ) );
            docInfo.set( "optimization", i18n( "Unknown Optimization" ), i18n( "Optimized" ) );
537

538
            docInfo.set( Okular::DocumentInfo::Pages, i18n("Unknown") );
539
        }
540
        userMutex()->unlock();
541 542 543 544 545

        // if pdfdoc is valid then we cached good info -> don't cache them again
        if ( pdfdoc )
            docInfoDirty = false;
    }
546 547 548
    return &docInfo;
}

549
const Okular::DocumentSynopsis * PDFGenerator::generateDocumentSynopsis()
550 551 552 553 554 555 556
{
    if ( !docSynopsisDirty )
        return &docSyn;

    if ( !pdfdoc )
        return NULL;

557
    userMutex()->lock();
558
    QDomDocument *toc = pdfdoc->toc();
559
    userMutex()->unlock();
560
    if ( !toc )
561 562
        return NULL;

563 564
    addSynopsisChildren(toc, &docSyn);
    delete toc;
565 566 567 568 569

    docSynopsisDirty = false;
    return &docSyn;
}

570
static Okular::FontInfo::FontType convertPopplerFontInfoTypeToOkularFontInfoType( Poppler::FontInfo::Type type )
571
{
572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594
    switch ( type )
    {
        case Poppler::FontInfo::Type1:
            return Okular::FontInfo::Type1;
            break;
        case Poppler::FontInfo::Type1C:
            return Okular::FontInfo::Type1C;
            break;
        case Poppler::FontInfo::Type3:
            return Okular::FontInfo::Type3;
            break;
        case Poppler::FontInfo::TrueType:
            return Okular::FontInfo::TrueType;
            break;
        case Poppler::FontInfo::CIDType0:
            return Okular::FontInfo::CIDType0;
            break;
        case Poppler::FontInfo::CIDType0C:
            return Okular::FontInfo::CIDType0C;
            break;
        case Poppler::FontInfo::CIDTrueType:
            return Okular::FontInfo::CIDTrueType;
            break;
595 596 597 598 599 600 601 602 603
        case Poppler::FontInfo::Type1COT:
            return Okular::FontInfo::Type1COT;
            break;
        case Poppler::FontInfo::TrueTypeOT:
            return Okular::FontInfo::TrueTypeOT;
            break;
        case Poppler::FontInfo::CIDType0COT:
            return Okular::FontInfo::CIDType0COT;
            break;
604 605 606 607 608 609 610 611
        case Poppler::FontInfo::CIDTrueTypeOT:
            return Okular::FontInfo::CIDTrueTypeOT;
            break;
        case Poppler::FontInfo::unknown:
        default: ;
     }
     return Okular::FontInfo::Unknown;
}
612

613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629
static Okular::FontInfo::EmbedType embedTypeForPopplerFontInfo( const Poppler::FontInfo &fi )
{
    Okular::FontInfo::EmbedType ret = Okular::FontInfo::NotEmbedded;
    if ( fi.isEmbedded() )
    {
        if ( fi.isSubset() )
        {
            ret = Okular::FontInfo::EmbeddedSubset;
        }
        else
        {
            ret = Okular::FontInfo::FullyEmbedded;
        }
    }
     return ret;
}

630
Okular::FontInfo::List PDFGenerator::fontsForPage( int page )
631 632
{
    Okular::FontInfo::List list;
633

634 635 636
    if ( page != nextFontPage )
        return list;

637
    QList<Poppler::FontInfo> fonts;
638
    userMutex()->lock();
639
    pdfdoc->scanForFonts( 1, &fonts );
640
    userMutex()->unlock();
641 642

    foreach (const Poppler::FontInfo &font, fonts)
643
    {
644 645 646
        Okular::FontInfo of;
        of.setName( font.name() );
        of.setType( convertPopplerFontInfoTypeToOkularFontInfoType( font.type() ) );
647
        of.setEmbedType( embedTypeForPopplerFontInfo( font) );
648
        of.setFile( font.file() );
649 650 651 652 653 654 655
#ifdef HAVE_POPPLER_0_9
        of.setCanBeExtracted( of.embedType() != Okular::FontInfo::NotEmbedded );
        
        QVariant nativeId;
        nativeId.setValue( font );
        of.setNativeId( nativeId );
#endif
656

657
        list.append( of );
658
    }
659

660 661
    ++nextFontPage;

662
    return list;
663 664
}

665
const QList<Okular::EmbeddedFile*> *PDFGenerator::embeddedFiles() const
666 667 668
{
    if (docEmbeddedFilesDirty)
    {
669
        userMutex()->lock();
670 671 672 673 674
        const QList<Poppler::EmbeddedFile*> &popplerFiles = pdfdoc->embeddedFiles();
        foreach(Poppler::EmbeddedFile* pef, popplerFiles)
        {
            docEmbeddedFiles.append(new PDFEmbeddedFile(pef));
        }
675
        userMutex()->unlock();
676

677 678
        docEmbeddedFilesDirty = false;
    }
679

680 681 682
    return &docEmbeddedFiles;
}

683
bool PDFGenerator::isAllowed( Okular::Permission permission ) const
684 685
{
    bool b = true;
686 687 688 689 690 691 692 693 694 695 696 697 698 699
    switch ( permission )
    {
        case Okular::AllowModify:
            b = pdfdoc->okToChange();
            break;
        case Okular::AllowCopy:
            b = pdfdoc->okToCopy();
            break;
        case Okular::AllowPrint:
            b = pdfdoc->okToPrint();
            break;
        case Okular::AllowNotes:
            b = pdfdoc->okToAddNotes();
            break;
700 701 702
        case Okular::AllowFillForms:
            b = pdfdoc->okToFillForm();
            break;
703 704
        default: ;
    }
705 706 707
    return b;
}

708
bool PDFGenerator::canGeneratePixmap() const
709
{
Enrico Ros's avatar
Enrico Ros committed
710 711
    return ready;
}
712

713
void PDFGenerator::generatePixmap( Okular::PixmapRequest * request )
Enrico Ros's avatar
Enrico Ros committed
714 715 716
{
#ifndef NDEBUG
    if ( !ready )
717
        kDebug(PDFDebug) << "calling generatePixmap() when not in READY state!";
Enrico Ros's avatar
Enrico Ros committed
718 719 720 721
#endif
    // update busy state (not really needed here, because the flag needs to
    // be set only to prevent asking a pixmap while the thread is running)
    ready = false;
722

723
    // debug requests to this (xpdf) generator
724
    //kDebug(PDFDebug) << "id: " << request->id << " is requesting " << (request->async ? "ASYNC" : "sync") <<  " pixmap for page " << request->page->number() << " [" << request->width << " x " << request->height << "].";
725

726
    /** asynchronous requests (generation in PDFPixmapGeneratorThread::run() **/
Tobias Koenig's avatar
Tobias Koenig committed
727
    if ( request->asynchronous() )
Enrico Ros's avatar
Enrico Ros committed
728 729 730 731
    {
        // start the generation into the thread
        generatorThread->startGeneration( request );
        return;
732
    }
733

734
    /** synchronous request: in-place generation **/
Enrico Ros's avatar
Enrico Ros committed
735
    // compute dpi used to get an image with desired width and height
Tobias Koenig's avatar
Tobias Koenig committed
736
    Okular::Page * page = request->page();
737 738 739 740 741 742 743 744 745

    double pageWidth = page->width(),
           pageHeight = page->height();

    if ( page->rotation() % 2 )
        qSwap( pageWidth, pageHeight );

    double fakeDpiX = request->width() * 72.0 / pageWidth,
           fakeDpiY = request->height() * 72.0 / pageHeight;
746

747
    // setup Okular:: output device: text page is generated only if we are at 72dpi.
Enrico Ros's avatar
Enrico Ros committed
748
    // since we can pre-generate the TextPage at the right res.. why not?
Tobias Koenig's avatar
Tobias Koenig committed
749
    bool genTextPage = !page->hasTextPage() && (request->width() == page->width()) &&
Tobias Koenig's avatar
Tobias Koenig committed
750
                       (request->height() == page->height());
751 752
    // generate links rects only the first time
    bool genObjectRects = !rectsGenerated.at( page->number() );
753

Enrico Ros's avatar
Enrico Ros committed
754
    // 0. LOCK [waits for the thread end]
755
    userMutex()->lock();
756

Enrico Ros's avatar
Enrico Ros committed
757 758
    // 1. Set OutputDev parameters and Generate contents
    // note: thread safety is set on 'false' for the GUI (this) thread
759
    Poppler::Page *p = pdfdoc->page(page->number());
760

Enrico Ros's avatar
Enrico Ros committed
761
    // 2. Take data from outputdev and attach it to the Page
762 763 764 765 766 767 768 769
    {
        QImage img( p->renderToImage(fakeDpiX, fakeDpiY, -1, -1, -1, -1, Poppler::Page::Rotate0 ) );
        if ( !page->isBoundingBoxKnown() )
            updatePageBoundingBox( page->number(), Okular::Utils::imageBoundingBox( &img ) );

        page->setPixmap( request->id(), new QPixmap( QPixmap::fromImage( img ) ) );
    }

770
    if ( genObjectRects )
771 772 773 774
    {
    	// TODO previously we extracted Image type rects too, but that needed porting to poppler
        // and as we are not doing anything with Image type rects i did not port it, have a look at
        // dead gp_outputdev.cpp on image extraction
Pino Toscano's avatar
Pino Toscano committed
775
        page->setObjectRects( generateLinks(p->links()) );
776
        rectsGenerated[ request->page()->number() ] = true;
777
    }
778

Enrico Ros's avatar
Enrico Ros committed
779
    // 3. UNLOCK [re-enables shared access]
780
    userMutex()->unlock();
Piotr Szymanski's avatar
Piotr Szymanski committed
781
    if ( genTextPage )
782
    {
783 784 785
#ifdef HAVE_POPPLER_0_7
        QList<Poppler::TextBox*> textList = p->textList();
#else
786
        QList<Poppler::TextBox*> textList = p->textList((Poppler::Page::Rotation)request->page()->orientation());
787
#endif
788 789
        Okular::TextPage *tp = abstractTextPage(textList, page->height(), page->width(), request->page()->orientation());
        page->setTextPage( tp );
Albert Astals Cid's avatar
Albert Astals Cid committed
790
        qDeleteAll(textList);
791 792 793
        
        // notify the new generation
        signalTextGenerationDone( page, tp );
794
    }
795 796
    delete p;
    
Enrico Ros's avatar
Enrico Ros committed
797 798
    // update ready state
    ready = true;
799

Enrico Ros's avatar
Enrico Ros committed
800
    // notify the new generation
Tobias Koenig's avatar
Tobias Koenig committed
801
    signalPixmapRequestDone( request );
Enrico Ros's avatar
Enrico Ros committed
802 803
}

804
Okular::TextPage* PDFGenerator::textPage( Okular::Page *page )
Enrico Ros's avatar
Enrico Ros committed
805
{
Thiago Macieira's avatar
Thiago Macieira committed
806
    kDebug(PDFDebug) << "calling" ;
807 808
    // build a TextList...
    Poppler::Page *pp = pdfdoc->page( page->number() );
809
    userMutex()->lock();
810 811 812
#ifdef HAVE_POPPLER_0_7
    QList<Poppler::TextBox*> textList = pp->textList();
#else
813
    QList<Poppler::TextBox*> textList = pp->textList((Poppler::Page::Rotation)page->orientation());
814
#endif
815
    userMutex()->unlock();
816
    delete pp;
817

818 819 820 821
#ifdef HAVE_POPPLER_0_7
    const double pageWidth = page->width();
    const double pageHeight = page->height();
#else
822 823
    const double pageWidth = ( page->rotation() % 2 ? page->height() : page->width() );
    const double pageHeight = ( page->rotation() % 2 ? page->width() : page->height() );
824
#endif
825

826
    Okular::TextPage *tp = abstractTextPage(textList, pageHeight, pageWidth, (Poppler::Page::Rotation)page->orientation());
Albert Astals Cid's avatar
Albert Astals Cid committed
827
    qDeleteAll(textList);
828
    return tp;
829
}
830

831 832 833 834 835
void PDFGenerator::requestFontData(const Okular::FontInfo &font, QByteArray *data)
{
#ifdef HAVE_POPPLER_0_9
    Poppler::FontInfo fi = font.nativeId().value<Poppler::FontInfo>();
    *data = pdfdoc->fontData(fi);
836 837 838
#else
    Q_UNUSED( font )
    Q_UNUSED( data )
839 840 841
#endif
}

842
bool PDFGenerator::print( QPrinter& printer )
843
{
John Layt's avatar
John Layt committed
844 845 846 847
    // Get the real page size to pass to the ps generator
    QPrinter dummy( QPrinter::PrinterResolution );
    dummy.setFullPage( true );
    dummy.setOrientation( printer.orientation() );
848
    dummy.setPageSize( printer.pageSize() );
John Layt's avatar
John Layt committed
849 850 851 852
    int width = dummy.width();
    int height = dummy.height();

    // Create the tempfile to send to FilePrinter, which will manage the deletion
853 854 855 856 857
    KTemporaryFile tf;
    tf.setSuffix( ".ps" );
    if ( !tf.open() )
        return false;
    QString tempfilename = tf.fileName();
858

John Layt's avatar
John Layt committed
859 860 861 862
    // Generate the list of pages to be printed as selected in the print dialog
    QList<int> pageList = Okular::FilePrinter::pageList( printer, pdfdoc->numPages(),
                                                         document()->bookmarkedPageList() );

863
    // TODO rotation
John Layt's avatar
John Layt committed
864

865
#ifdef HAVE_POPPLER_0_7
John Layt's avatar
John Layt committed
866 867 868 869 870
    tf.setAutoRemove(false);
#else
    tf.close();
#endif