generator_pdf.cpp 58.9 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
	Okular::DocumentViewport viewport;
145 146 147 148 149 150 151 152
	
	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
153
			fillViewportFromLinkDestination( viewport, popplerLinkGoto->destination() );
154
			link = new Okular::GotoAction(popplerLinkGoto->fileName(), viewport);
155 156 157 158
		break;
		
		case Poppler::Link::Execute:
			popplerLinkExecute = static_cast<const Poppler::LinkExecute *>(popplerLink);
159
			link = new Okular::ExecuteAction( popplerLinkExecute->fileName(), popplerLinkExecute->parameters() );
160 161 162 163
		break;
		
		case Poppler::Link::Browse:
			popplerLinkBrowse = static_cast<const Poppler::LinkBrowse *>(popplerLink);
164
			link = new Okular::BrowseAction( popplerLinkBrowse->url() );
165 166 167 168
		break;
		
		case Poppler::Link::Action:
			popplerLinkAction = static_cast<const Poppler::LinkAction *>(popplerLink);
169
			link = new Okular::DocumentAction( (Okular::DocumentAction::DocumentActionType)popplerLinkAction->actionType() );
170 171
		break;
		
172 173 174 175
		case Poppler::Link::Sound:
		{
			popplerLinkSound = static_cast<const Poppler::LinkSound *>(popplerLink);
			Poppler::SoundObject *popplerSound = popplerLinkSound->sound();
176
			Okular::Sound *sound = createSoundFromPopplerSound( popplerSound );
177
			link = new Okular::SoundAction( popplerLinkSound->volume(), popplerLinkSound->synchronous(), popplerLinkSound->repeat(), popplerLinkSound->mix(), sound );
178 179 180
		}
		break;
		
181 182 183 184 185
		case Poppler::Link::Movie:
			// not implemented
		break;
	}
	
186
	return link;
187 188
}

Pino Toscano's avatar
Pino Toscano committed
189
static QLinkedList<Okular::ObjectRect*> generateLinks( const QList<Poppler::Link*> &popplerLinks )
190
{
191
	QLinkedList<Okular::ObjectRect*> links;
192 193 194
	foreach(const Poppler::Link *popplerLink, popplerLinks)
	{
		QRectF linkArea = popplerLink->linkArea();
195 196 197 198
		double nl = linkArea.left(),
		       nt = linkArea.top(),
		       nr = linkArea.right(),
		       nb = linkArea.bottom();
199
		// create the rect using normalized coords and attach the Okular::Link to it
Pino Toscano's avatar
Pino Toscano committed
200
		Okular::ObjectRect * rect = new Okular::ObjectRect( nl, nt, nr, nb, false, Okular::ObjectRect::Action, createLinkFromPopplerLink(popplerLink) );
201
		// add the ObjectRect to the container
202
		links.push_front( rect );
203 204
	}
	qDeleteAll(popplerLinks);
205
	return links;
206 207
}

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

210 211
/** NOTES on threading:
 * internal: thread race prevention is done via the 'docLock' mutex. the
212
 *           mutex is needed only because we have the asynchronous thread; else
213 214 215 216 217 218 219
 *           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.
220
 * So, as example, printing while generating a pixmap asynchronously is safe,
221 222 223 224
 * 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.
 */

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

259
PDFGenerator::~PDFGenerator()
260
{
261
    // stop and delete the generator thread
262 263 264 265 266
    if ( generatorThread )
    {
        generatorThread->wait();
        delete generatorThread;
    }
John Layt's avatar
John Layt committed
267 268

    delete pdfOptionsPage;
269 270
}

Enrico Ros's avatar
Enrico Ros committed
271
//BEGIN Generator inherited functions
272
bool PDFGenerator::loadDocument( const QString & filePath, QVector<Okular::Page*> & pagesVector )
273
{
274 275 276
#ifndef NDEBUG
    if ( pdfdoc )
    {
277
        kDebug(PDFDebug) << "PDFGenerator: multiple calls to loadDocument. Check it.";
278 279 280
        return false;
    }
#endif
281
    // create PDFDoc for the given file
282
    pdfdoc = Poppler::Document::load( filePath, 0, 0 );
283 284 285 286 287 288 289
    bool success = init(pagesVector, filePath.section('/', -1, -1));
    if (success && QFile::exists(filePath + QLatin1String( "sync" )))
    {
        loadPdfSync(filePath, pagesVector);
    }
    return success;
}
290

291 292 293 294 295
bool PDFGenerator::loadDocumentFromData( const QByteArray & fileData, QVector<Okular::Page*> & pagesVector )
{
#ifndef NDEBUG
    if ( pdfdoc )
    {
296
        kDebug(PDFDebug) << "PDFGenerator: multiple calls to loadDocument. Check it.";
297 298 299 300 301 302 303 304 305 306
        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)
{
307
    // if the file didn't open correctly it might be encrypted, so ask for a pass
308 309 310
    bool firstInput = true;
    bool triedWallet = false;
    KWallet::Wallet * wallet = 0;
311
    bool keep = true;
Albert Astals Cid's avatar
Albert Astals Cid committed
312
    while ( pdfdoc && pdfdoc->isLocked() )
313
    {
Tobias Koenig's avatar
Tobias Koenig committed
314
        QString password;
315 316

        // 1.A. try to retrieve the first password from the kde wallet system
317
        if ( !triedWallet && !walletKey.isNull() )
318 319
        {
            QString walletName = KWallet::Wallet::NetworkWallet();
320 321 322 323
            WId parentwid = 0;
            if ( document() && document()->widget() )
                parentwid = document()->widget()->winId();
            wallet = KWallet::Wallet::openWallet( walletName, parentwid );
324 325
            if ( wallet )
            {
326 327 328 329
                // use the KPdf folder (and create if missing)
                if ( !wallet->hasFolder( "KPdf" ) )
                    wallet->createFolder( "KPdf" );
                wallet->setFolder( "KPdf" );
330 331 332

                // look for the pass in that folder
                QString retrievedPass;
333
                if ( !wallet->readPassword( walletKey, retrievedPass ) )
Tobias Koenig's avatar
Tobias Koenig committed
334
                    password = retrievedPass;
335 336 337 338 339 340 341 342 343 344 345 346 347 348 349
            }
            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
Pino Toscano's avatar
Pino Toscano committed
350 351 352
            KPasswordDialog dlg( 0, wallet ? KPasswordDialog::ShowKeepPassword : KPasswordDialog::KPasswordDialogFlags() );
            dlg.setCaption( i18n( "Document Password" ) );
            dlg.setPrompt( prompt );
Laurent Montel's avatar
Laurent Montel committed
353
            if( !dlg.exec() )
354
                break;
Laurent Montel's avatar
Laurent Montel committed
355
            password = dlg.password();
356 357
            if ( wallet )
                keep = dlg.keepPassword();
358 359 360
        }

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

363 364
        // 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 )
365
        {
366
            wallet->writePassword( walletKey, password );
367 368
        }
    }
369
    if ( !pdfdoc || pdfdoc->isLocked() )
370
    {
371
        delete pdfdoc;
372 373 374
        pdfdoc = 0;
        return false;
    }
375 376

    // build Pages (currentPage was set -1 by deletePages)
377 378
    uint pageCount = pdfdoc->numPages();
    pagesVector.resize(pageCount);
379
    rectsGenerated.fill(false, pageCount);
380

Albert Astals Cid's avatar
Albert Astals Cid committed
381
    loadPages(pagesVector, 0, false);
382

383
    setAAOptions();
384

385 386 387 388
    // the file has been loaded correctly
    return true;
}

389
bool PDFGenerator::doCloseDocument()
390 391
{
    // remove internal objects
392
    userMutex()->lock();
393 394
    delete pdfdoc;
    pdfdoc = 0;
395
    userMutex()->unlock();
396 397
    docInfoDirty = true;
    docSynopsisDirty = true;
398
    docSyn.clear();
399 400 401
    docEmbeddedFilesDirty = true;
    qDeleteAll(docEmbeddedFiles);
    docEmbeddedFiles.clear();
402
    nextFontPage = 0;
403 404 405 406

    return true;
}

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

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

451
#ifdef PDFGENERATOR_DEBUG
452
        kDebug(PDFDebug) << "load page" << i << "with rotation" << rotation << "and orientation" << orientation;
453
#endif
454
	delete p;
455

456 457
        if (clear && pagesVector[i])
            delete pagesVector[i];
458
        // set the Okular::page at the right position in document's pages vector
459 460
        pagesVector[i] = page;
    }
461 462
}

463
const Okular::DocumentInfo * PDFGenerator::generateDocumentInfo()
464 465 466
{
    if ( docInfoDirty )
    {
467
        userMutex()->lock();
468
        
469
        docInfo.set( Okular::DocumentInfo::MimeType, "application/pdf" );
470
        
471 472
        if ( pdfdoc )
        {
473
            // compile internal structure reading properties from PDFDoc
474 475 476 477 478 479 480 481 482 483
            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 ) );
484

Pino Toscano's avatar
Pino Toscano committed
485
            docInfo.set( "format", i18nc( "PDF v. <version>", "PDF v. %1",
Chusslove Illich's avatar
Chusslove Illich committed
486
                          pdfdoc->pdfVersion() ), i18n( "Format" ) );
487 488 489 490
            docInfo.set( "encryption", pdfdoc->isEncrypted() ? i18n( "Encrypted" ) : i18n( "Unencrypted" ),
                         i18n("Security") );
            docInfo.set( "optimization", pdfdoc->isLinearized() ? i18n( "Yes" ) : i18n( "No" ),
                         i18n("Optimized") );
491

492
            docInfo.set( Okular::DocumentInfo::Pages, QString::number( pdfdoc->numPages() ) );
493 494 495
        }
        else
        {
496
            // TODO not sure one can reach here, check and if it is not possible, remove the code
497 498 499 500 501 502 503 504
            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") );
505

506 507 508
            docInfo.set( "format", "PDF", i18n( "Format" ) );
            docInfo.set( "encryption", i18n( "Unknown Encryption" ), i18n( "Security" ) );
            docInfo.set( "optimization", i18n( "Unknown Optimization" ), i18n( "Optimized" ) );
509

510
            docInfo.set( Okular::DocumentInfo::Pages, i18n("Unknown") );
511
        }
512
        userMutex()->unlock();
513 514 515 516 517

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

521
const Okular::DocumentSynopsis * PDFGenerator::generateDocumentSynopsis()
522 523 524 525 526 527 528
{
    if ( !docSynopsisDirty )
        return &docSyn;

    if ( !pdfdoc )
        return NULL;

529
    userMutex()->lock();
530
    QDomDocument *toc = pdfdoc->toc();
531
    userMutex()->unlock();
532
    if ( !toc )
533 534
        return NULL;

535 536
    addSynopsisChildren(toc, &docSyn);
    delete toc;
537 538 539 540 541

    docSynopsisDirty = false;
    return &docSyn;
}

542
static Okular::FontInfo::FontType convertPopplerFontInfoTypeToOkularFontInfoType( Poppler::FontInfo::Type type )
543
{
544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566
    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;
567 568 569 570 571 572 573 574 575
        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;
576 577 578 579 580 581 582 583
        case Poppler::FontInfo::CIDTrueTypeOT:
            return Okular::FontInfo::CIDTrueTypeOT;
            break;
        case Poppler::FontInfo::unknown:
        default: ;
     }
     return Okular::FontInfo::Unknown;
}
584

585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601
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;
}

602
Okular::FontInfo::List PDFGenerator::fontsForPage( int page )
603 604
{
    Okular::FontInfo::List list;
605

606 607 608
    if ( page != nextFontPage )
        return list;

609
    QList<Poppler::FontInfo> fonts;
610
    userMutex()->lock();
611
    pdfdoc->scanForFonts( 1, &fonts );
612
    userMutex()->unlock();
613 614

    foreach (const Poppler::FontInfo &font, fonts)
615
    {
616 617 618
        Okular::FontInfo of;
        of.setName( font.name() );
        of.setType( convertPopplerFontInfoTypeToOkularFontInfoType( font.type() ) );
619
        of.setEmbedType( embedTypeForPopplerFontInfo( font) );
620
        of.setFile( font.file() );
621

622
        list.append( of );
623
    }
624

625 626
    ++nextFontPage;

627
    return list;
628 629
}

630
const QList<Okular::EmbeddedFile*> *PDFGenerator::embeddedFiles() const
631 632 633
{
    if (docEmbeddedFilesDirty)
    {
634
        userMutex()->lock();
635 636 637 638 639
        const QList<Poppler::EmbeddedFile*> &popplerFiles = pdfdoc->embeddedFiles();
        foreach(Poppler::EmbeddedFile* pef, popplerFiles)
        {
            docEmbeddedFiles.append(new PDFEmbeddedFile(pef));
        }
640
        userMutex()->unlock();
641

642 643
        docEmbeddedFilesDirty = false;
    }
644

645 646 647
    return &docEmbeddedFiles;
}

648
bool PDFGenerator::isAllowed( Okular::Permission permission ) const
649 650
{
    bool b = true;
651 652 653 654 655 656 657 658 659 660 661 662 663 664
    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;
665 666 667
        case Okular::AllowFillForms:
            b = pdfdoc->okToFillForm();
            break;
668 669
        default: ;
    }
670 671 672
    return b;
}

673
bool PDFGenerator::canGeneratePixmap() const
674
{
Enrico Ros's avatar
Enrico Ros committed
675 676
    return ready;
}
677

678
void PDFGenerator::generatePixmap( Okular::PixmapRequest * request )
Enrico Ros's avatar
Enrico Ros committed
679 680 681
{
#ifndef NDEBUG
    if ( !ready )
682
        kDebug(PDFDebug) << "calling generatePixmap() when not in READY state!";
Enrico Ros's avatar
Enrico Ros committed
683 684 685 686
#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;
687

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

691
    /** asynchronous requests (generation in PDFPixmapGeneratorThread::run() **/
Tobias Koenig's avatar
Tobias Koenig committed
692
    if ( request->asynchronous() )
Enrico Ros's avatar
Enrico Ros committed
693 694 695 696
    {
        // start the generation into the thread
        generatorThread->startGeneration( request );
        return;
697
    }
698

699
    /** synchronous request: in-place generation **/
Enrico Ros's avatar
Enrico Ros committed
700
    // compute dpi used to get an image with desired width and height
Tobias Koenig's avatar
Tobias Koenig committed
701
    Okular::Page * page = request->page();
702 703 704 705 706 707 708 709 710

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

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

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

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

Enrico Ros's avatar
Enrico Ros committed
726
    // 2. Take data from outputdev and attach it to the Page
727
    page->setPixmap( request->id(), new QPixmap( QPixmap::fromImage( p->renderToImage(fakeDpiX, fakeDpiY, -1, -1, -1, -1, Poppler::Page::Rotate0 ) ) ) );
728
    
729
    if ( genObjectRects )
730 731 732 733
    {
    	// 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
734
        page->setObjectRects( generateLinks(p->links()) );
735
        rectsGenerated[ request->page()->number() ] = true;
736
    }
737

Enrico Ros's avatar
Enrico Ros committed
738
    // 3. UNLOCK [re-enables shared access]
739
    userMutex()->unlock();
Piotr Szymanski's avatar
Piotr Szymanski committed
740
    if ( genTextPage )
741
    {
742 743 744
#ifdef HAVE_POPPLER_0_7
        QList<Poppler::TextBox*> textList = p->textList();
#else
745
        QList<Poppler::TextBox*> textList = p->textList((Poppler::Page::Rotation)request->page()->orientation());
746
#endif
747
        page->setTextPage( abstractTextPage(textList, page->height(), page->width(), request->page()->orientation()) );
Albert Astals Cid's avatar
Albert Astals Cid committed
748
        qDeleteAll(textList);
749
    }
750 751
    delete p;
    
Enrico Ros's avatar
Enrico Ros committed
752 753
    // update ready state
    ready = true;
754

Enrico Ros's avatar
Enrico Ros committed
755
    // notify the new generation
Tobias Koenig's avatar
Tobias Koenig committed
756
    signalPixmapRequestDone( request );
Enrico Ros's avatar
Enrico Ros committed
757 758
}

759
Okular::TextPage* PDFGenerator::textPage( Okular::Page *page )
Enrico Ros's avatar
Enrico Ros committed
760
{
Thiago Macieira's avatar
Thiago Macieira committed
761
    kDebug(PDFDebug) << "calling" ;
762 763
    // build a TextList...
    Poppler::Page *pp = pdfdoc->page( page->number() );
764
    userMutex()->lock();
765 766 767
#ifdef HAVE_POPPLER_0_7
    QList<Poppler::TextBox*> textList = pp->textList();
#else
768
    QList<Poppler::TextBox*> textList = pp->textList((Poppler::Page::Rotation)page->orientation());
769
#endif
770
    userMutex()->unlock();
771
    delete pp;
772

773 774 775 776
#ifdef HAVE_POPPLER_0_7
    const double pageWidth = page->width();
    const double pageHeight = page->height();
#else
777 778
    const double pageWidth = ( page->rotation() % 2 ? page->height() : page->width() );
    const double pageHeight = ( page->rotation() % 2 ? page->width() : page->height() );
779
#endif
780

781
    Okular::TextPage *tp = abstractTextPage(textList, pageHeight, pageWidth, (Poppler::Page::Rotation)page->orientation());
Albert Astals Cid's avatar
Albert Astals Cid committed
782
    qDeleteAll(textList);
783
    return tp;
784
}
785

786
bool PDFGenerator::print( QPrinter& printer )
787
{
John Layt's avatar
John Layt committed
788 789 790 791 792 793 794 795
    // 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
796 797 798 799 800
    KTemporaryFile tf;
    tf.setSuffix( ".ps" );
    if ( !tf.open() )
        return false;
    QString tempfilename = tf.fileName();
801

John Layt's avatar
John Layt committed
802 803 804 805
    // 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() );

806
    // TODO rotation
John Layt's avatar
John Layt committed
807

808
#if HAVE_POPPLER_0_7
John Layt's avatar
John Layt committed
809 810 811 812 813
    tf.setAutoRemove(false);
#else
    tf.close();
#endif

814 815 816
    QString pstitle = metaData(QLatin1String("Title"), QVariant()).toString();
    if ( pstitle.trimmed().isEmpty() )
    {
817
        pstitle = document()->currentDocument().fileName();
818
    }
John Layt's avatar
John Layt committed
819 820 821

    bool forceRasterize = pdfOptionsPage->printForceRaster();

822
    Poppler::PSConverter *psConverter = pdfdoc->psConverter();
John Layt's avatar
John Layt committed
823