generator_pdf.cpp 60.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
#include <okular/core/utils.h>
42

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

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

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

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

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

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

    private:
        QCheckBox *m_forceRaster;
};


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

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

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

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

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

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

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

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

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

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

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

    delete pdfOptionsPage;
283 284
}

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

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

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

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

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

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

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

Albert Astals Cid's avatar
Albert Astals Cid committed
395
    loadPages(pagesVector, 0, false);
396

397 398
    // update the configuration
    reparseConfig();
399

400 401 402 403
    // the file has been loaded correctly
    return true;
}

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

    return true;
}

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

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

466
#ifdef PDFGENERATOR_DEBUG
467
        kDebug(PDFDebug) << "load page" << i << "with rotation" << rotation << "and orientation" << orientation;
468
#endif
469
	delete p;
470

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

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

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

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

521 522 523
            docInfo.set( "format", "PDF", i18n( "Format" ) );
            docInfo.set( "encryption", i18n( "Unknown Encryption" ), i18n( "Security" ) );
            docInfo.set( "optimization", i18n( "Unknown Optimization" ), i18n( "Optimized" ) );
524

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

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

536
const Okular::DocumentSynopsis * PDFGenerator::generateDocumentSynopsis()
537 538 539 540 541 542 543
{
    if ( !docSynopsisDirty )
        return &docSyn;

    if ( !pdfdoc )
        return NULL;

544
    userMutex()->lock();
545
    QDomDocument *toc = pdfdoc->toc();
546
    userMutex()->unlock();
547
    if ( !toc )
548 549
        return NULL;

550 551
    addSynopsisChildren(toc, &docSyn);
    delete toc;
552 553 554 555 556

    docSynopsisDirty = false;
    return &docSyn;
}

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

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

617
Okular::FontInfo::List PDFGenerator::fontsForPage( int page )
618 619
{
    Okular::FontInfo::List list;
620

621 622 623
    if ( page != nextFontPage )
        return list;

624
    QList<Poppler::FontInfo> fonts;
625
    userMutex()->lock();
626
    pdfdoc->scanForFonts( 1, &fonts );
627
    userMutex()->unlock();
628 629

    foreach (const Poppler::FontInfo &font, fonts)
630
    {
631 632 633
        Okular::FontInfo of;
        of.setName( font.name() );
        of.setType( convertPopplerFontInfoTypeToOkularFontInfoType( font.type() ) );
634
        of.setEmbedType( embedTypeForPopplerFontInfo( font) );
635
        of.setFile( font.file() );
636 637 638 639 640 641 642
#ifdef HAVE_POPPLER_0_9
        of.setCanBeExtracted( of.embedType() != Okular::FontInfo::NotEmbedded );
        
        QVariant nativeId;
        nativeId.setValue( font );
        of.setNativeId( nativeId );
#endif
643

644
        list.append( of );
645
    }
646

647 648
    ++nextFontPage;

649
    return list;
650 651
}

652
const QList<Okular::EmbeddedFile*> *PDFGenerator::embeddedFiles() const
653 654 655
{
    if (docEmbeddedFilesDirty)
    {
656
        userMutex()->lock();
657 658 659 660 661
        const QList<Poppler::EmbeddedFile*> &popplerFiles = pdfdoc->embeddedFiles();
        foreach(Poppler::EmbeddedFile* pef, popplerFiles)
        {
            docEmbeddedFiles.append(new PDFEmbeddedFile(pef));
        }
662
        userMutex()->unlock();
663

664 665
        docEmbeddedFilesDirty = false;
    }
666

667 668 669
    return &docEmbeddedFiles;
}

670
bool PDFGenerator::isAllowed( Okular::Permission permission ) const
671 672
{
    bool b = true;
673 674 675 676 677 678 679 680 681 682 683 684 685 686
    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;
687 688 689
        case Okular::AllowFillForms:
            b = pdfdoc->okToFillForm();
            break;
690 691
        default: ;
    }
692 693 694
    return b;
}

695
bool PDFGenerator::canGeneratePixmap() const
696
{
Enrico Ros's avatar
Enrico Ros committed
697 698
    return ready;
}
699

700
void PDFGenerator::generatePixmap( Okular::PixmapRequest * request )
Enrico Ros's avatar
Enrico Ros committed
701 702 703
{
#ifndef NDEBUG
    if ( !ready )
704
        kDebug(PDFDebug) << "calling generatePixmap() when not in READY state!";
Enrico Ros's avatar
Enrico Ros committed
705 706 707 708
#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;
709

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

713
    /** asynchronous requests (generation in PDFPixmapGeneratorThread::run() **/
Tobias Koenig's avatar
Tobias Koenig committed
714
    if ( request->asynchronous() )
Enrico Ros's avatar
Enrico Ros committed
715 716 717 718
    {
        // start the generation into the thread
        generatorThread->startGeneration( request );
        return;
719
    }
720

721
    /** synchronous request: in-place generation **/
Enrico Ros's avatar
Enrico Ros committed
722
    // compute dpi used to get an image with desired width and height
Tobias Koenig's avatar
Tobias Koenig committed
723
    Okular::Page * page = request->page();
724 725 726 727 728 729 730 731 732

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

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

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

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

Enrico Ros's avatar
Enrico Ros committed
748
    // 2. Take data from outputdev and attach it to the Page
749 750 751 752 753 754 755 756
    {
        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 ) ) );
    }

757
    if ( genObjectRects )
758 759 760 761
    {
    	// 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
762
        page->setObjectRects( generateLinks(p->links()) );
763
        rectsGenerated[ request->page()->number() ] = true;
764
    }
765

Enrico Ros's avatar
Enrico Ros committed
766
    // 3. UNLOCK [re-enables shared access]
767
    userMutex()->unlock();
Piotr Szymanski's avatar
Piotr Szymanski committed
768
    if ( genTextPage )
769
    {
770 771 772
#ifdef HAVE_POPPLER_0_7
        QList<Poppler::TextBox*> textList = p->textList();
#else
773
        QList<Poppler::TextBox*> textList = p->textList((Poppler::Page::Rotation)request->page()->orientation());
774
#endif
775 776
        Okular::TextPage *tp = abstractTextPage(textList, page->height(), page->width(), request->page()->orientation());
        page->setTextPage( tp );
Albert Astals Cid's avatar
Albert Astals Cid committed
777
        qDeleteAll(textList);
778 779 780
        
        // notify the new generation
        signalTextGenerationDone( page, tp );
781
    }
782 783
    delete p;
    
Enrico Ros's avatar
Enrico Ros committed
784 785
    // update ready state
    ready = true;
786

Enrico Ros's avatar
Enrico Ros committed
787
    // notify the new generation
Tobias Koenig's avatar
Tobias Koenig committed
788
    signalPixmapRequestDone( request );
Enrico Ros's avatar
Enrico Ros committed
789 790
}

791
Okular::TextPage* PDFGenerator::textPage( Okular::Page *page )
Enrico Ros's avatar
Enrico Ros committed
792
{
Thiago Macieira's avatar
Thiago Macieira committed
793
    kDebug(PDFDebug) << "calling" ;
794 795
    // build a TextList...
    Poppler::Page *pp = pdfdoc->page( page->number() );
796
    userMutex()->lock();
797 798 799
#ifdef HAVE_POPPLER_0_7
    QList<Poppler::TextBox*> textList = pp->textList();
#else
800
    QList<Poppler::TextBox*> textList = pp->textList((Poppler::Page::Rotation)page->orientation());
801
#endif
802
    userMutex()->unlock();
803
    delete pp;
804

805 806 807 808
#ifdef HAVE_POPPLER_0_7
    const double pageWidth = page->width();
    const double pageHeight = page->height();
#else
809 810
    const double pageWidth = ( page->rotation() % 2 ? page->height() : page->width() );
    const double pageHeight = ( page->rotation() % 2 ? page->width() : page->height() );
811
#endif
812

813
    Okular::TextPage *tp = abstractTextPage(textList, pageHeight, pageWidth, (Poppler::Page::Rotation)page->orientation());
Albert Astals Cid's avatar
Albert Astals Cid committed
814
    qDeleteAll(textList);
815
    return tp;
816
}
817

818 819 820 821 822 823 824 825
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);
#endif
}

826
bool PDFGenerator::print( QPrinter& printer )
827
{
John Layt's avatar
John Layt committed
828 829 830 831 832 833 834 835
    // 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
836 837 838 839 840
    KTemporaryFile tf;
    tf.setSuffix( ".ps" );
    if ( !tf.open() )
        return false;
    QString tempfilename = tf.fileName();
841