generator_pdf.cpp 59.7 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 36 37 38
#include <okular/core/page.h>
#include <okular/core/annotations.h>
#include <okular/core/pagetransition.h>
#include <okular/core/sound.h>
#include <okular/core/sourcereference.h>
Pino Toscano's avatar
Pino Toscano committed
39
#include <okular/core/textpage.h>
John Layt's avatar
John Layt committed
40
#include <okular/core/fileprinter.h>
41

42
#include <config-okular-poppler.h>
43

44
#include "formfields.h"
45
#include "popplerembeddedfile.h"
46

Pino Toscano's avatar
Pino Toscano committed
47
static const int PDFDebug = 4710;
48

49
class PDFOptionsPage : public QWidget
50 51 52 53
{
   public:
       PDFOptionsPage()
       {
54
           setWindowTitle( i18n( "PDF Options" ) );
55 56 57 58 59 60 61 62
           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
63
       bool printForceRaster()
64
       {
John Layt's avatar
John Layt committed
65
           return m_forceRaster->isChecked();
66 67
       }

John Layt's avatar
John Layt committed
68
       void setPrintForceRaster( bool forceRaster )
69
       {
John Layt's avatar
John Layt committed
70
           m_forceRaster->setChecked( forceRaster );
71 72 73 74 75 76 77
       }

    private:
        QCheckBox *m_forceRaster;
};


Pino Toscano's avatar
Pino Toscano committed
78
static void fillViewportFromLinkDestination( Okular::DocumentViewport &viewport, const Poppler::LinkDestination &destination )
79
{
80
    viewport.pageNumber = destination.pageNumber() - 1;
81

82
    if (!viewport.isValid()) return;
83 84 85 86 87 88 89 90 91 92 93 94 95

    // 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();

96 97
                viewport.rePos.normalizedX = left;
                viewport.rePos.normalizedY = top;
98
                viewport.rePos.enabled = true;
99
                viewport.rePos.pos = Okular::DocumentViewport::TopLeft;
100 101 102 103 104 105 106 107 108 109 110 111
            }
            /* TODO
            if ( dest->getChangeZoom() )
                make zoom change*/
/*        break;

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

112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
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;
}

Pino Toscano's avatar
Pino Toscano committed
136
static Okular::Action* createLinkFromPopplerLink(const Poppler::Link *popplerLink)
137
{
Pino Toscano's avatar
Pino Toscano committed
138
	Okular::Action *link = 0;
139 140 141 142
	const Poppler::LinkGoto *popplerLinkGoto;
	const Poppler::LinkExecute *popplerLinkExecute;
	const Poppler::LinkBrowse *popplerLinkBrowse;
	const Poppler::LinkAction *popplerLinkAction;
143
	const Poppler::LinkSound *popplerLinkSound;
144 145 146
#ifdef HAVE_POPPLER_0_9
	const Poppler::LinkJavaScript *popplerLinkJS;
#endif
147
	Okular::DocumentViewport viewport;
148 149 150 151 152 153 154 155
	
	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
156
			fillViewportFromLinkDestination( viewport, popplerLinkGoto->destination() );
157
			link = new Okular::GotoAction(popplerLinkGoto->fileName(), viewport);
158 159 160 161
		break;
		
		case Poppler::Link::Execute:
			popplerLinkExecute = static_cast<const Poppler::LinkExecute *>(popplerLink);
162
			link = new Okular::ExecuteAction( popplerLinkExecute->fileName(), popplerLinkExecute->parameters() );
163 164 165 166
		break;
		
		case Poppler::Link::Browse:
			popplerLinkBrowse = static_cast<const Poppler::LinkBrowse *>(popplerLink);
167
			link = new Okular::BrowseAction( popplerLinkBrowse->url() );
168 169 170 171
		break;
		
		case Poppler::Link::Action:
			popplerLinkAction = static_cast<const Poppler::LinkAction *>(popplerLink);
172
			link = new Okular::DocumentAction( (Okular::DocumentAction::DocumentActionType)popplerLinkAction->actionType() );
173 174
		break;
		
175 176 177 178
		case Poppler::Link::Sound:
		{
			popplerLinkSound = static_cast<const Poppler::LinkSound *>(popplerLink);
			Poppler::SoundObject *popplerSound = popplerLinkSound->sound();
179
			Okular::Sound *sound = createSoundFromPopplerSound( popplerSound );
180
			link = new Okular::SoundAction( popplerLinkSound->volume(), popplerLinkSound->synchronous(), popplerLinkSound->repeat(), popplerLinkSound->mix(), sound );
181 182 183
		}
		break;
		
184 185 186 187 188 189 190 191 192
#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
		
193 194 195 196 197
		case Poppler::Link::Movie:
			// not implemented
		break;
	}
	
198
	return link;
199 200
}

Pino Toscano's avatar
Pino Toscano committed
201
static QLinkedList<Okular::ObjectRect*> generateLinks( const QList<Poppler::Link*> &popplerLinks )
202
{
203
	QLinkedList<Okular::ObjectRect*> links;
204 205 206
	foreach(const Poppler::Link *popplerLink, popplerLinks)
	{
		QRectF linkArea = popplerLink->linkArea();
207 208 209 210
		double nl = linkArea.left(),
		       nt = linkArea.top(),
		       nr = linkArea.right(),
		       nb = linkArea.bottom();
211
		// create the rect using normalized coords and attach the Okular::Link to it
Pino Toscano's avatar
Pino Toscano committed
212
		Okular::ObjectRect * rect = new Okular::ObjectRect( nl, nt, nr, nb, false, Okular::ObjectRect::Action, createLinkFromPopplerLink(popplerLink) );
213
		// add the ObjectRect to the container
214
		links.push_front( rect );
215 216
	}
	qDeleteAll(popplerLinks);
217
	return links;
218 219
}

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

222 223
/** NOTES on threading:
 * internal: thread race prevention is done via the 'docLock' mutex. the
224
 *           mutex is needed only because we have the asynchronous thread; else
225 226 227 228 229 230 231
 *           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.
232
 * So, as example, printing while generating a pixmap asynchronously is safe,
233 234 235 236
 * 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.
 */

237
static KAboutData createAboutData()
238
{
239
    KAboutData aboutData(
240 241
         "okular_poppler",
         "okular_poppler",
242
         ki18n( "PDF Backend" ),
243
         "0.1",
244
         ki18n( "A PDF file renderer" ),
245
         KAboutData::License_GPL,
246
         ki18n( "© 2005-2008 Albert Astals Cid" )
247
    );
Pino Toscano's avatar
Pino Toscano committed
248
    aboutData.addAuthor( ki18n( "Albert Astals Cid" ), KLocalizedString(), "aacid@kde.org" );
249 250 251 252 253 254 255 256
    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 ),
257
    docEmbeddedFilesDirty( true ), nextFontPage( 0 ), pdfOptionsPage( 0 )
258
{
259
    setFeature( TextExtraction );
260
    setFeature( FontInfo );
John Layt's avatar
John Layt committed
261
    setFeature( PrintPostscript );
262
    setFeature( ReadRawData );
John Layt's avatar
John Layt committed
263
    pdfOptionsPage = new PDFOptionsPage();
264
    // update the configuration
265
    reparseConfig();
266 267
    // generate the pixmapGeneratorThread
    generatorThread = new PDFPixmapGeneratorThread( this );
268
    connect(generatorThread, SIGNAL(finished()), this, SLOT(threadFinished()), Qt::QueuedConnection);
269 270
}

271
PDFGenerator::~PDFGenerator()
272
{
273
    // stop and delete the generator thread
274 275 276 277 278
    if ( generatorThread )
    {
        generatorThread->wait();
        delete generatorThread;
    }
John Layt's avatar
John Layt committed
279 280

    delete pdfOptionsPage;
281 282
}

Enrico Ros's avatar
Enrico Ros committed
283
//BEGIN Generator inherited functions
284
bool PDFGenerator::loadDocument( const QString & filePath, QVector<Okular::Page*> & pagesVector )
285
{
286 287 288
#ifndef NDEBUG
    if ( pdfdoc )
    {
289
        kDebug(PDFDebug) << "PDFGenerator: multiple calls to loadDocument. Check it.";
290 291 292
        return false;
    }
#endif
293
    // create PDFDoc for the given file
294
    pdfdoc = Poppler::Document::load( filePath, 0, 0 );
295 296 297 298 299 300 301
    bool success = init(pagesVector, filePath.section('/', -1, -1));
    if (success && QFile::exists(filePath + QLatin1String( "sync" )))
    {
        loadPdfSync(filePath, pagesVector);
    }
    return success;
}
302

303 304 305 306 307
bool PDFGenerator::loadDocumentFromData( const QByteArray & fileData, QVector<Okular::Page*> & pagesVector )
{
#ifndef NDEBUG
    if ( pdfdoc )
    {
308
        kDebug(PDFDebug) << "PDFGenerator: multiple calls to loadDocument. Check it.";
309 310 311 312 313 314 315 316 317 318
        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)
{
319
    // if the file didn't open correctly it might be encrypted, so ask for a pass
320 321 322
    bool firstInput = true;
    bool triedWallet = false;
    KWallet::Wallet * wallet = 0;
323
    bool keep = true;
Albert Astals Cid's avatar
Albert Astals Cid committed
324
    while ( pdfdoc && pdfdoc->isLocked() )
325
    {
Tobias Koenig's avatar
Tobias Koenig committed
326
        QString password;
327 328

        // 1.A. try to retrieve the first password from the kde wallet system
329
        if ( !triedWallet && !walletKey.isNull() )
330 331
        {
            QString walletName = KWallet::Wallet::NetworkWallet();
332 333
            WId parentwid = 0;
            if ( document() && document()->widget() )
Albert Astals Cid's avatar
Albert Astals Cid committed
334
                parentwid = document()->widget()->effectiveWinId();
335
            wallet = KWallet::Wallet::openWallet( walletName, parentwid );
336 337
            if ( wallet )
            {
338 339 340 341
                // use the KPdf folder (and create if missing)
                if ( !wallet->hasFolder( "KPdf" ) )
                    wallet->createFolder( "KPdf" );
                wallet->setFolder( "KPdf" );
342 343 344

                // look for the pass in that folder
                QString retrievedPass;
345
                if ( !wallet->readPassword( walletKey, retrievedPass ) )
Tobias Koenig's avatar
Tobias Koenig committed
346
                    password = retrievedPass;
347 348 349 350 351 352 353 354 355 356 357 358 359 360 361
            }
            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
362
            KPasswordDialog dlg( document()->widget(), wallet ? KPasswordDialog::ShowKeepPassword : KPasswordDialog::KPasswordDialogFlags() );
Pino Toscano's avatar
Pino Toscano committed
363 364
            dlg.setCaption( i18n( "Document Password" ) );
            dlg.setPrompt( prompt );
Laurent Montel's avatar
Laurent Montel committed
365
            if( !dlg.exec() )
366
                break;
Laurent Montel's avatar
Laurent Montel committed
367
            password = dlg.password();
368 369
            if ( wallet )
                keep = dlg.keepPassword();
370 371 372
        }

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

375 376
        // 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 )
377
        {
378
            wallet->writePassword( walletKey, password );
379 380
        }
    }
381
    if ( !pdfdoc || pdfdoc->isLocked() )
382
    {
383
        delete pdfdoc;
384 385 386
        pdfdoc = 0;
        return false;
    }
387 388

    // build Pages (currentPage was set -1 by deletePages)
389 390
    uint pageCount = pdfdoc->numPages();
    pagesVector.resize(pageCount);
391
    rectsGenerated.fill(false, pageCount);
392

Albert Astals Cid's avatar
Albert Astals Cid committed
393
    loadPages(pagesVector, 0, false);
394

395
    setAAOptions();
396

397 398 399 400
    // the file has been loaded correctly
    return true;
}

401
bool PDFGenerator::doCloseDocument()
402 403
{
    // remove internal objects
404
    userMutex()->lock();
405 406
    delete pdfdoc;
    pdfdoc = 0;
407
    userMutex()->unlock();
408 409
    docInfoDirty = true;
    docSynopsisDirty = true;
410
    docSyn.clear();
411 412 413
    docEmbeddedFilesDirty = true;
    qDeleteAll(docEmbeddedFiles);
    docEmbeddedFiles.clear();
414
    nextFontPage = 0;
415 416 417 418

    return true;
}

419
void PDFGenerator::loadPages(QVector<Okular::Page*> &pagesVector, int rotation, bool clear)
420
{
Piotr Szymanski's avatar
Piotr Szymanski committed
421
    // TODO XPDF 3.01 check
422
    int count=pagesVector.count(),w=0,h=0;
423
    for ( int i = 0; i < count ; i++ )
424
    {
425
        // get xpdf page
426 427 428 429
        Poppler::Page * p = pdfdoc->page( i );
        QSize pSize = p->pageSize();
        w = pSize.width();
        h = pSize.height();
430
        Okular::Rotation orientation = Okular::Rotation0;
Albert Astals Cid's avatar
Albert Astals Cid committed
431
        switch (p->orientation())
432
        {
433 434 435 436
          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;
437 438
        }
        if (rotation % 2 == 1)
439
          qSwap(w,h);
440 441
        // init a Okular::page, add transition and annotation information
        Okular::Page * page = new Okular::Page( i, w, h, orientation );
442 443
        addTransition( p, page );
        if ( true ) //TODO real check
444
          addAnnotations( p, page );
445 446
        Poppler::Link * tmplink = p->action( Poppler::Page::Opening );
        if ( tmplink )
447
        {
Pino Toscano's avatar
Pino Toscano committed
448
            page->setPageAction( Okular::Page::Opening, createLinkFromPopplerLink( tmplink ) );
449 450
            delete tmplink;
        }
451 452
        tmplink = p->action( Poppler::Page::Closing );
        if ( tmplink )
453
        {
Pino Toscano's avatar
Pino Toscano committed
454
            page->setPageAction( Okular::Page::Closing, createLinkFromPopplerLink( tmplink ) );
455 456
            delete tmplink;
        }
457
        page->setDuration( p->duration() );
458
        page->setLabel( p->label() );
459 460

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

463
#ifdef PDFGENERATOR_DEBUG
464
        kDebug(PDFDebug) << "load page" << i << "with rotation" << rotation << "and orientation" << orientation;
465
#endif
466
	delete p;
467

468 469
        if (clear && pagesVector[i])
            delete pagesVector[i];
470
        // set the Okular::page at the right position in document's pages vector
471 472
        pagesVector[i] = page;
    }
473 474
}

475
const Okular::DocumentInfo * PDFGenerator::generateDocumentInfo()
476 477 478
{
    if ( docInfoDirty )
    {
479
        userMutex()->lock();
480
        
481
        docInfo.set( Okular::DocumentInfo::MimeType, "application/pdf" );
482
        
483 484
        if ( pdfdoc )
        {
485
            // compile internal structure reading properties from PDFDoc
486 487 488 489 490 491 492 493 494 495
            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 ) );
496

Pino Toscano's avatar
Pino Toscano committed
497
            docInfo.set( "format", i18nc( "PDF v. <version>", "PDF v. %1",
Chusslove Illich's avatar
Chusslove Illich committed
498
                          pdfdoc->pdfVersion() ), i18n( "Format" ) );
499 500 501 502
            docInfo.set( "encryption", pdfdoc->isEncrypted() ? i18n( "Encrypted" ) : i18n( "Unencrypted" ),
                         i18n("Security") );
            docInfo.set( "optimization", pdfdoc->isLinearized() ? i18n( "Yes" ) : i18n( "No" ),
                         i18n("Optimized") );
503

504
            docInfo.set( Okular::DocumentInfo::Pages, QString::number( pdfdoc->numPages() ) );
505 506 507
        }
        else
        {
508
            // TODO not sure one can reach here, check and if it is not possible, remove the code
509 510 511 512 513 514 515 516
            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") );
517

518 519 520
            docInfo.set( "format", "PDF", i18n( "Format" ) );
            docInfo.set( "encryption", i18n( "Unknown Encryption" ), i18n( "Security" ) );
            docInfo.set( "optimization", i18n( "Unknown Optimization" ), i18n( "Optimized" ) );
521

522
            docInfo.set( Okular::DocumentInfo::Pages, i18n("Unknown") );
523
        }
524
        userMutex()->unlock();
525 526 527 528 529

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

533
const Okular::DocumentSynopsis * PDFGenerator::generateDocumentSynopsis()
534 535 536 537 538 539 540
{
    if ( !docSynopsisDirty )
        return &docSyn;

    if ( !pdfdoc )
        return NULL;

541
    userMutex()->lock();
542
    QDomDocument *toc = pdfdoc->toc();
543
    userMutex()->unlock();
544
    if ( !toc )
545 546
        return NULL;

547 548
    addSynopsisChildren(toc, &docSyn);
    delete toc;
549 550 551 552 553

    docSynopsisDirty = false;
    return &docSyn;
}

554
static Okular::FontInfo::FontType convertPopplerFontInfoTypeToOkularFontInfoType( Poppler::FontInfo::Type type )
555
{
556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578
    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;
579 580 581 582 583 584 585 586 587
        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;
588 589 590 591 592 593 594 595
        case Poppler::FontInfo::CIDTrueTypeOT:
            return Okular::FontInfo::CIDTrueTypeOT;
            break;
        case Poppler::FontInfo::unknown:
        default: ;
     }
     return Okular::FontInfo::Unknown;
}
596

597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613
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;
}

614
Okular::FontInfo::List PDFGenerator::fontsForPage( int page )
615 616
{
    Okular::FontInfo::List list;
617

618 619 620
    if ( page != nextFontPage )
        return list;

621
    QList<Poppler::FontInfo> fonts;
622
    userMutex()->lock();
623
    pdfdoc->scanForFonts( 1, &fonts );
624
    userMutex()->unlock();
625 626

    foreach (const Poppler::FontInfo &font, fonts)
627
    {
628 629 630
        Okular::FontInfo of;
        of.setName( font.name() );
        of.setType( convertPopplerFontInfoTypeToOkularFontInfoType( font.type() ) );
631
        of.setEmbedType( embedTypeForPopplerFontInfo( font) );
632
        of.setFile( font.file() );
633

634
        list.append( of );
635
    }
636

637 638
    ++nextFontPage;

639
    return list;
640 641
}

642
const QList<Okular::EmbeddedFile*> *PDFGenerator::embeddedFiles() const
643 644 645
{
    if (docEmbeddedFilesDirty)
    {
646
        userMutex()->lock();
647 648 649 650 651
        const QList<Poppler::EmbeddedFile*> &popplerFiles = pdfdoc->embeddedFiles();
        foreach(Poppler::EmbeddedFile* pef, popplerFiles)
        {
            docEmbeddedFiles.append(new PDFEmbeddedFile(pef));
        }
652
        userMutex()->unlock();
653

654 655
        docEmbeddedFilesDirty = false;
    }
656

657 658 659
    return &docEmbeddedFiles;
}

660
bool PDFGenerator::isAllowed( Okular::Permission permission ) const
661 662
{
    bool b = true;
663 664 665 666 667 668 669 670 671 672 673 674 675 676
    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;
677 678 679
        case Okular::AllowFillForms:
            b = pdfdoc->okToFillForm();
            break;
680 681
        default: ;
    }
682 683 684
    return b;
}

685
bool PDFGenerator::canGeneratePixmap() const
686
{
Enrico Ros's avatar
Enrico Ros committed
687 688
    return ready;
}
689

690
void PDFGenerator::generatePixmap( Okular::PixmapRequest * request )
Enrico Ros's avatar
Enrico Ros committed
691 692 693
{
#ifndef NDEBUG
    if ( !ready )
694
        kDebug(PDFDebug) << "calling generatePixmap() when not in READY state!";
Enrico Ros's avatar
Enrico Ros committed
695 696 697 698
#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;
699

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

703
    /** asynchronous requests (generation in PDFPixmapGeneratorThread::run() **/
Tobias Koenig's avatar
Tobias Koenig committed
704
    if ( request->asynchronous() )
Enrico Ros's avatar
Enrico Ros committed
705 706 707 708
    {
        // start the generation into the thread
        generatorThread->startGeneration( request );
        return;
709
    }
710

711
    /** synchronous request: in-place generation **/
Enrico Ros's avatar
Enrico Ros committed
712
    // compute dpi used to get an image with desired width and height
Tobias Koenig's avatar
Tobias Koenig committed
713
    Okular::Page * page = request->page();
714 715 716 717 718 719 720 721 722

    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;
723

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

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

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

Enrico Ros's avatar
Enrico Ros committed
738
    // 2. Take data from outputdev and attach it to the Page
739
    page->setPixmap( request->id(), new QPixmap( QPixmap::fromImage( p->renderToImage(fakeDpiX, fakeDpiY, -1, -1, -1, -1, Poppler::Page::Rotate0 ) ) ) );
740
    
741
    if ( genObjectRects )
742 743 744 745
    {
    	// 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
746
        page->setObjectRects( generateLinks(p->links()) );
747
        rectsGenerated[ request->page()->number() ] = true;
748
    }
749

Enrico Ros's avatar
Enrico Ros committed
750
    // 3. UNLOCK [re-enables shared access]
751
    userMutex()->unlock();
Piotr Szymanski's avatar
Piotr Szymanski committed
752
    if ( genTextPage )
753
    {
754 755 756
#ifdef HAVE_POPPLER_0_7
        QList<Poppler::TextBox*> textList = p->textList();
#else
757
        QList<Poppler::TextBox*> textList = p->textList((Poppler::Page::Rotation)request->page()->orientation());
758
#endif
759 760
        Okular::TextPage *tp = abstractTextPage(textList, page->height(), page->width(), request->page()->orientation());
        page->setTextPage( tp );
Albert Astals Cid's avatar
Albert Astals Cid committed
761
        qDeleteAll(textList);
762 763 764
        
        // notify the new generation
        signalTextGenerationDone( page, tp );
765
    }
766 767
    delete p;
    
Enrico Ros's avatar
Enrico Ros committed
768 769
    // update ready state
    ready = true;
770

Enrico Ros's avatar
Enrico Ros committed
771
    // notify the new generation
Tobias Koenig's avatar
Tobias Koenig committed
772
    signalPixmapRequestDone( request );
Enrico Ros's avatar
Enrico Ros committed
773 774
}

775
Okular::TextPage* PDFGenerator::textPage( Okular::Page *page )
Enrico Ros's avatar
Enrico Ros committed
776
{
Thiago Macieira's avatar
Thiago Macieira committed
777
    kDebug(PDFDebug) << "calling" ;
778 779
    // build a TextList...
    Poppler::Page *pp = pdfdoc->page( page->number() );
780
    userMutex()->lock();
781 782 783
#ifdef HAVE_POPPLER_0_7
    QList<Poppler::TextBox*> textList = pp->textList();
#else
784
    QList<Poppler::TextBox*> textList = pp->textList((Poppler::Page::Rotation)page->orientation());
785
#endif
786
    userMutex()->unlock();
787
    delete pp;
788

789 790 791 792
#ifdef HAVE_POPPLER_0_7
    const double pageWidth = page->width();
    const double pageHeight = page->height();
#else
793 794
    const double pageWidth = ( page->rotation() % 2 ? page->height() : page->width() );
    const double pageHeight = ( page->rotation() % 2 ? page->width() : page->height() );
795
#endif
796

797
    Okular::TextPage *tp = abstractTextPage(textList, pageHeight, pageWidth, (Poppler::Page::Rotation)page->orientation());
Albert Astals Cid's avatar
Albert Astals Cid committed
798
    qDeleteAll(textList);
799
    return tp;
800
}
801

802
bool PDFGenerator::print( QPrinter& printer )
803
{
John Layt's avatar
John Layt committed
804 805 806 807 808 809 810 811
    // Get the real page size to pass to the ps generator
    QPrinter dummy( QPrinter::PrinterResolution );
    dummy.setFullPage( true );
    dummy.setOrientation( printer.orientation() );
    int width = dummy.width();
    int height = dummy.height();

    // Create the tempfile to send to FilePrinter, which will manage the deletion
812 813 814 815 816
    KTemporaryFile tf;
    tf.setSuffix( ".ps" );
    if ( !tf.open() )
        return false;
    QString tempfilename = tf.fileName();
817

John Layt's avatar
John Layt committed
818 819 820 821
    // 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() );

822
    // TODO rotation
John Layt's avatar
John Layt committed
823

824
#if HAVE_POPPLER_0_7