File indexing completed on 2024-04-28 04:32:43

0001 /*
0002     SPDX-FileCopyrightText: 2005 Piotr Szymanski <niedakh@gmail.com>
0003     SPDX-FileCopyrightText: 2008 Albert Astals Cid <aacid@kde.org>
0004 
0005     Work sponsored by the LiMux project of the city of Munich:
0006     SPDX-FileCopyrightText: 2017 Klarälvdalens Datakonsult AB a KDAB Group company <info@kdab.com>
0007 
0008     SPDX-License-Identifier: GPL-2.0-or-later
0009 */
0010 
0011 #include "config-okular.h"
0012 
0013 #include "generator.h"
0014 #include "generator_p.h"
0015 #include "observer.h"
0016 
0017 #include <QApplication>
0018 #include <QEventLoop>
0019 #include <QPrinter>
0020 
0021 #include <KLocalizedString>
0022 #include <QDebug>
0023 #include <QIcon>
0024 #include <QMimeDatabase>
0025 #include <QTimer>
0026 
0027 #if HAVE_KWALLET
0028 #include <KWallet>
0029 #endif
0030 
0031 #include "document_p.h"
0032 #include "page.h"
0033 #include "page_p.h"
0034 #include "textpage.h"
0035 #include "utils.h"
0036 
0037 using namespace Okular;
0038 
0039 GeneratorPrivate::GeneratorPrivate()
0040     : m_document(nullptr)
0041     , mPixmapGenerationThread(nullptr)
0042     , mTextPageGenerationThread(nullptr)
0043     , mPixmapReady(true)
0044     , mTextPageReady(true)
0045     , m_closing(false)
0046     , m_closingLoop(nullptr)
0047     , m_dpi(72.0, 72.0)
0048 {
0049     qRegisterMetaType<Okular::Page *>();
0050 }
0051 
0052 GeneratorPrivate::~GeneratorPrivate()
0053 {
0054     if (mPixmapGenerationThread) {
0055         mPixmapGenerationThread->wait();
0056     }
0057 
0058     delete mPixmapGenerationThread;
0059 
0060     if (mTextPageGenerationThread) {
0061         mTextPageGenerationThread->wait();
0062     }
0063 
0064     delete mTextPageGenerationThread;
0065 }
0066 
0067 PixmapGenerationThread *GeneratorPrivate::pixmapGenerationThread()
0068 {
0069     if (mPixmapGenerationThread) {
0070         return mPixmapGenerationThread;
0071     }
0072 
0073     Q_Q(Generator);
0074     mPixmapGenerationThread = new PixmapGenerationThread(q);
0075     QObject::connect(
0076         mPixmapGenerationThread, &PixmapGenerationThread::finished, q, [this] { pixmapGenerationFinished(); }, Qt::QueuedConnection);
0077 
0078     return mPixmapGenerationThread;
0079 }
0080 
0081 TextPageGenerationThread *GeneratorPrivate::textPageGenerationThread()
0082 {
0083     if (mTextPageGenerationThread) {
0084         return mTextPageGenerationThread;
0085     }
0086 
0087     Q_Q(Generator);
0088     mTextPageGenerationThread = new TextPageGenerationThread(q);
0089     QObject::connect(
0090         mTextPageGenerationThread, &TextPageGenerationThread::finished, q, [this] { textpageGenerationFinished(); }, Qt::QueuedConnection);
0091 
0092     return mTextPageGenerationThread;
0093 }
0094 
0095 void GeneratorPrivate::pixmapGenerationFinished()
0096 {
0097     Q_Q(Generator);
0098     PixmapRequest *request = mPixmapGenerationThread->request();
0099     const QImage &img = mPixmapGenerationThread->image();
0100     mPixmapGenerationThread->endGeneration();
0101 
0102     QMutexLocker locker(threadsLock());
0103 
0104     if (m_closing) {
0105         mPixmapReady = true;
0106         delete request;
0107         if (mTextPageReady) {
0108             locker.unlock();
0109             m_closingLoop->quit();
0110         }
0111         return;
0112     }
0113 
0114     if (!request->shouldAbortRender()) {
0115         request->page()->setPixmap(request->observer(), new QPixmap(QPixmap::fromImage(img)), request->normalizedRect());
0116         const int pageNumber = request->page()->number();
0117 
0118         if (mPixmapGenerationThread->calcBoundingBox()) {
0119             q->updatePageBoundingBox(pageNumber, mPixmapGenerationThread->boundingBox());
0120         }
0121     } else {
0122         // Cancel the text page generation too if it's still running
0123         if (mTextPageGenerationThread && mTextPageGenerationThread->isRunning()) {
0124             mTextPageGenerationThread->abortExtraction();
0125             mTextPageGenerationThread->wait();
0126         }
0127     }
0128 
0129     mPixmapReady = true;
0130     q->signalPixmapRequestDone(request);
0131 }
0132 
0133 void GeneratorPrivate::textpageGenerationFinished()
0134 {
0135     Q_Q(Generator);
0136     Page *page = mTextPageGenerationThread->page();
0137     mTextPageGenerationThread->endGeneration();
0138 
0139     QMutexLocker locker(threadsLock());
0140     mTextPageReady = true;
0141 
0142     if (m_closing) {
0143         delete mTextPageGenerationThread->textPage();
0144         if (mPixmapReady) {
0145             locker.unlock();
0146             m_closingLoop->quit();
0147         }
0148         return;
0149     }
0150 
0151     if (mTextPageGenerationThread->textPage()) {
0152         TextPage *tp = mTextPageGenerationThread->textPage();
0153         page->setTextPage(tp);
0154         q->signalTextGenerationDone(page, tp);
0155     }
0156 }
0157 
0158 QMutex *GeneratorPrivate::threadsLock()
0159 {
0160     return &m_threadsMutex;
0161 }
0162 
0163 QVariant GeneratorPrivate::metaData(const QString &, const QVariant &) const
0164 {
0165     return QVariant();
0166 }
0167 
0168 QImage GeneratorPrivate::image(PixmapRequest *)
0169 {
0170     return QImage();
0171 }
0172 
0173 Generator::Generator(QObject *parent, const QVariantList &args)
0174     : Generator(*new GeneratorPrivate(), parent, args)
0175 {
0176     // the delegated constructor does it all
0177 }
0178 
0179 Generator::Generator(GeneratorPrivate &dd, QObject *parent, const QVariantList &args)
0180     : QObject(parent)
0181     , d_ptr(&dd)
0182 {
0183     d_ptr->q_ptr = this;
0184     Q_UNUSED(args)
0185 }
0186 
0187 Generator::~Generator()
0188 {
0189     delete d_ptr;
0190 }
0191 
0192 bool Generator::loadDocument(const QString &fileName, QVector<Page *> &pagesVector)
0193 {
0194     Q_UNUSED(fileName);
0195     Q_UNUSED(pagesVector);
0196 
0197     return false;
0198 }
0199 
0200 bool Generator::loadDocumentFromData(const QByteArray &, QVector<Page *> &)
0201 {
0202     return false;
0203 }
0204 
0205 Document::OpenResult Generator::loadDocumentWithPassword(const QString &fileName, QVector<Page *> &pagesVector, const QString &)
0206 {
0207     return loadDocument(fileName, pagesVector) ? Document::OpenSuccess : Document::OpenError;
0208 }
0209 
0210 Document::OpenResult Generator::loadDocumentFromDataWithPassword(const QByteArray &fileData, QVector<Page *> &pagesVector, const QString &)
0211 {
0212     return loadDocumentFromData(fileData, pagesVector) ? Document::OpenSuccess : Document::OpenError;
0213 }
0214 
0215 Generator::SwapBackingFileResult Generator::swapBackingFile(QString const & /*newFileName */, QVector<Okular::Page *> & /*newPagesVector*/)
0216 {
0217     return SwapBackingFileError;
0218 }
0219 
0220 bool Generator::closeDocument()
0221 {
0222     Q_D(Generator);
0223 
0224     d->m_closing = true;
0225 
0226     d->threadsLock()->lock();
0227     if (!(d->mPixmapReady && d->mTextPageReady)) {
0228         QEventLoop loop;
0229         d->m_closingLoop = &loop;
0230 
0231         d->threadsLock()->unlock();
0232 
0233         loop.exec();
0234 
0235         d->m_closingLoop = nullptr;
0236     } else {
0237         d->threadsLock()->unlock();
0238     }
0239 
0240     bool ret = doCloseDocument();
0241 
0242     d->m_closing = false;
0243 
0244     return ret;
0245 }
0246 
0247 bool Generator::canGeneratePixmap() const
0248 {
0249     Q_D(const Generator);
0250     return d->mPixmapReady;
0251 }
0252 
0253 bool Generator::canSign() const
0254 {
0255     return false;
0256 }
0257 
0258 bool Generator::sign(const NewSignatureData &, const QString &)
0259 {
0260     return false;
0261 }
0262 
0263 CertificateStore *Generator::certificateStore() const
0264 {
0265     return nullptr;
0266 }
0267 
0268 void Generator::generatePixmap(PixmapRequest *request)
0269 {
0270     Q_D(Generator);
0271     d->mPixmapReady = false;
0272 
0273     const bool calcBoundingBox = !request->isTile() && !request->page()->isBoundingBoxKnown();
0274 
0275     if (request->asynchronous() && hasFeature(Threaded)) {
0276         if (d->textPageGenerationThread()->isFinished() && !canGenerateTextPage()) {
0277             // It can happen that the text generation has already finished but
0278             // mTextPageReady is still false because textpageGenerationFinished
0279             // didn't have time to run, if so queue ourselves
0280             QTimer::singleShot(0, this, [this, request] { generatePixmap(request); });
0281             return;
0282         }
0283 
0284         /**
0285          * We create the text page for every page that is visible to the
0286          * user, so he can use the text extraction tools without a delay.
0287          */
0288         if (hasFeature(TextExtraction) && !request->page()->hasTextPage() && canGenerateTextPage() && !d->m_closing) {
0289             d->mTextPageReady = false;
0290             d->textPageGenerationThread()->setPage(request->page());
0291 
0292             // dummy is used as a way to make sure the lambda gets disconnected each time it is executed
0293             // since not all the times the pixmap generation thread starts we want the text generation thread to also start
0294             QObject *dummy = new QObject();
0295             connect(d_ptr->pixmapGenerationThread(), &QThread::started, dummy, [this, dummy] {
0296                 delete dummy;
0297                 d_ptr->textPageGenerationThread()->startGeneration();
0298             });
0299         }
0300         // pixmap generation thread must be started *after* connect(), else we may miss the start signal and get lock-ups (see bug 396137)
0301         d->pixmapGenerationThread()->startGeneration(request, calcBoundingBox);
0302 
0303         return;
0304     }
0305 
0306     const QImage &img = image(request);
0307     request->page()->setPixmap(request->observer(), new QPixmap(QPixmap::fromImage(img)), request->normalizedRect());
0308     const int pageNumber = request->page()->number();
0309 
0310     d->mPixmapReady = true;
0311 
0312     signalPixmapRequestDone(request);
0313     if (calcBoundingBox) {
0314         updatePageBoundingBox(pageNumber, Utils::imageBoundingBox(&img));
0315     }
0316 }
0317 
0318 bool Generator::canGenerateTextPage() const
0319 {
0320     Q_D(const Generator);
0321     return d->mTextPageReady;
0322 }
0323 
0324 void Generator::generateTextPage(Page *page)
0325 {
0326     TextRequest treq(page);
0327     TextPage *tp = textPage(&treq);
0328     page->setTextPage(tp);
0329     signalTextGenerationDone(page, tp);
0330 }
0331 
0332 QImage Generator::image(PixmapRequest *request)
0333 {
0334     Q_D(Generator);
0335     return d->image(request);
0336 }
0337 
0338 TextPage *Generator::textPage(TextRequest *)
0339 {
0340     return nullptr;
0341 }
0342 
0343 DocumentInfo Generator::generateDocumentInfo(const QSet<DocumentInfo::Key> &keys) const
0344 {
0345     Q_UNUSED(keys);
0346 
0347     return DocumentInfo();
0348 }
0349 
0350 const DocumentSynopsis *Generator::generateDocumentSynopsis()
0351 {
0352     return nullptr;
0353 }
0354 
0355 FontInfo::List Generator::fontsForPage(int)
0356 {
0357     return FontInfo::List();
0358 }
0359 
0360 const QList<EmbeddedFile *> *Generator::embeddedFiles() const
0361 {
0362     return nullptr;
0363 }
0364 
0365 Generator::PageSizeMetric Generator::pagesSizeMetric() const
0366 {
0367     return None;
0368 }
0369 
0370 bool Generator::isAllowed(Permission) const
0371 {
0372     return true;
0373 }
0374 
0375 void Generator::rotationChanged(Rotation, Rotation)
0376 {
0377 }
0378 
0379 PageSize::List Generator::pageSizes() const
0380 {
0381     return PageSize::List();
0382 }
0383 
0384 void Generator::pageSizeChanged(const PageSize &, const PageSize &)
0385 {
0386 }
0387 
0388 Document::PrintError Generator::print(QPrinter &)
0389 {
0390     return Document::UnknownPrintError;
0391 }
0392 
0393 void Generator::opaqueAction(const BackendOpaqueAction * /*action*/)
0394 {
0395 }
0396 
0397 void Generator::freeOpaqueActionContents(const BackendOpaqueAction & /*action*/)
0398 {
0399 }
0400 
0401 QVariant Generator::metaData(const QString &key, const QVariant &option) const
0402 {
0403     Q_D(const Generator);
0404     return d->metaData(key, option);
0405 }
0406 
0407 ExportFormat::List Generator::exportFormats() const
0408 {
0409     return ExportFormat::List();
0410 }
0411 
0412 bool Generator::exportTo(const QString &, const ExportFormat &)
0413 {
0414     return false;
0415 }
0416 
0417 void Generator::walletDataForFile(const QString &fileName, QString *walletName, QString *walletFolder, QString *walletKey) const
0418 {
0419 #if HAVE_KWALLET
0420     *walletKey = fileName.section(QLatin1Char('/'), -1, -1);
0421     *walletName = KWallet::Wallet::NetworkWallet();
0422     *walletFolder = QStringLiteral("KPdf");
0423 #endif
0424 }
0425 
0426 bool Generator::hasFeature(GeneratorFeature feature) const
0427 {
0428     Q_D(const Generator);
0429     return d->m_features.contains(feature);
0430 }
0431 
0432 void Generator::signalPixmapRequestDone(PixmapRequest *request)
0433 {
0434     Q_D(Generator);
0435     if (d->m_document) {
0436         d->m_document->requestDone(request);
0437     } else {
0438         delete request;
0439     }
0440 }
0441 
0442 void Generator::signalTextGenerationDone(Page *page, TextPage *textPage)
0443 {
0444     Q_D(Generator);
0445     if (d->m_document) {
0446         d->m_document->textGenerationDone(page);
0447     } else {
0448         delete textPage;
0449     }
0450 }
0451 
0452 void Generator::signalPartialPixmapRequest(PixmapRequest *request, const QImage &image)
0453 {
0454     if (request->shouldAbortRender()) {
0455         return;
0456     }
0457 
0458     PagePrivate *pagePrivate = PagePrivate::get(request->page());
0459     pagePrivate->setPixmap(request->observer(), new QPixmap(QPixmap::fromImage(image)), request->normalizedRect(), true /* isPartialPixmap */);
0460 
0461     const int pageNumber = request->page()->number();
0462     request->observer()->notifyPageChanged(pageNumber, Okular::DocumentObserver::Pixmap);
0463 }
0464 
0465 const Document *Generator::document() const
0466 {
0467     Q_D(const Generator);
0468     if (d->m_document) {
0469         return d->m_document->m_parent;
0470     }
0471     return nullptr;
0472 }
0473 
0474 void Generator::setFeature(GeneratorFeature feature, bool on)
0475 {
0476     Q_D(Generator);
0477     if (on) {
0478         d->m_features.insert(feature);
0479     } else {
0480         d->m_features.remove(feature);
0481     }
0482 }
0483 
0484 QVariant Generator::documentMetaData(const DocumentMetaDataKey key, const QVariant &option) const
0485 {
0486     Q_D(const Generator);
0487     if (!d->m_document) {
0488         return QVariant();
0489     }
0490 
0491     return d->m_document->documentMetaData(key, option);
0492 }
0493 
0494 QMutex *Generator::userMutex() const
0495 {
0496     Q_D(const Generator);
0497     return &d->m_mutex;
0498 }
0499 
0500 void Generator::updatePageBoundingBox(int page, const NormalizedRect &boundingBox)
0501 {
0502     Q_D(Generator);
0503     if (d->m_document) { // still connected to document?
0504         d->m_document->setPageBoundingBox(page, boundingBox);
0505     }
0506 }
0507 
0508 QByteArray Generator::requestFontData(const Okular::FontInfo & /*font*/)
0509 {
0510     return {};
0511 }
0512 
0513 void Generator::setDPI(const QSizeF dpi)
0514 {
0515     Q_D(Generator);
0516     d->m_dpi = dpi;
0517 }
0518 
0519 QSizeF Generator::dpi() const
0520 {
0521     Q_D(const Generator);
0522     return d->m_dpi;
0523 }
0524 
0525 QAbstractItemModel *Generator::layersModel() const
0526 {
0527     return nullptr;
0528 }
0529 
0530 TextRequest::TextRequest()
0531     : d(new TextRequestPrivate)
0532 {
0533     d->mPage = nullptr;
0534     d->mShouldAbortExtraction = 0;
0535 }
0536 
0537 TextRequest::TextRequest(Page *page)
0538     : d(new TextRequestPrivate)
0539 {
0540     d->mPage = page;
0541     d->mShouldAbortExtraction = 0;
0542 }
0543 
0544 TextRequest::~TextRequest()
0545 {
0546     delete d;
0547 }
0548 
0549 Page *TextRequest::page() const
0550 {
0551     return d->mPage;
0552 }
0553 
0554 bool TextRequest::shouldAbortExtraction() const
0555 {
0556     return d->mShouldAbortExtraction != 0;
0557 }
0558 
0559 TextRequestPrivate *TextRequestPrivate::get(const TextRequest *req)
0560 {
0561     return req->d;
0562 }
0563 
0564 PixmapRequest::PixmapRequest(DocumentObserver *observer, int pageNumber, int width, int height, qreal dpr, int priority, PixmapRequestFeatures features)
0565     : d(new PixmapRequestPrivate)
0566 {
0567     d->mObserver = observer;
0568     d->mPageNumber = pageNumber;
0569     d->mWidth = ceil(width * dpr);
0570     d->mHeight = ceil(height * dpr);
0571     d->mPriority = priority;
0572     d->mFeatures = features;
0573     d->mForce = false;
0574     d->mTile = false;
0575     d->mNormalizedRect = NormalizedRect();
0576     d->mPartialUpdatesWanted = false;
0577     d->mShouldAbortRender = 0;
0578 }
0579 
0580 PixmapRequest::~PixmapRequest()
0581 {
0582     delete d;
0583 }
0584 
0585 DocumentObserver *PixmapRequest::observer() const
0586 {
0587     return d->mObserver;
0588 }
0589 
0590 int PixmapRequest::pageNumber() const
0591 {
0592     return d->mPageNumber;
0593 }
0594 
0595 int PixmapRequest::width() const
0596 {
0597     return d->mWidth;
0598 }
0599 
0600 int PixmapRequest::height() const
0601 {
0602     return d->mHeight;
0603 }
0604 
0605 int PixmapRequest::priority() const
0606 {
0607     return d->mPriority;
0608 }
0609 
0610 bool PixmapRequest::asynchronous() const
0611 {
0612     return d->mFeatures & Asynchronous;
0613 }
0614 
0615 bool PixmapRequest::preload() const
0616 {
0617     return d->mFeatures & Preload;
0618 }
0619 
0620 Page *PixmapRequest::page() const
0621 {
0622     return d->mPage;
0623 }
0624 
0625 void PixmapRequest::setTile(bool tile)
0626 {
0627     d->mTile = tile;
0628 }
0629 
0630 bool PixmapRequest::isTile() const
0631 {
0632     return d->mTile;
0633 }
0634 
0635 void PixmapRequest::setNormalizedRect(const NormalizedRect &rect)
0636 {
0637     if (d->mNormalizedRect == rect) {
0638         return;
0639     }
0640 
0641     d->mNormalizedRect = rect;
0642 }
0643 
0644 const NormalizedRect &PixmapRequest::normalizedRect() const
0645 {
0646     return d->mNormalizedRect;
0647 }
0648 
0649 void PixmapRequest::setPartialUpdatesWanted(bool partialUpdatesWanted)
0650 {
0651     d->mPartialUpdatesWanted = partialUpdatesWanted;
0652 }
0653 
0654 bool PixmapRequest::partialUpdatesWanted() const
0655 {
0656     return d->mPartialUpdatesWanted;
0657 }
0658 
0659 bool PixmapRequest::shouldAbortRender() const
0660 {
0661     return d->mShouldAbortRender != 0;
0662 }
0663 
0664 Okular::TilesManager *PixmapRequestPrivate::tilesManager() const
0665 {
0666     return mPage->d->tilesManager(mObserver);
0667 }
0668 
0669 PixmapRequestPrivate *PixmapRequestPrivate::get(const PixmapRequest *req)
0670 {
0671     return req->d;
0672 }
0673 
0674 void PixmapRequestPrivate::swap()
0675 {
0676     std::swap(mWidth, mHeight);
0677 }
0678 
0679 class Okular::ExportFormatPrivate : public QSharedData
0680 {
0681 public:
0682     ExportFormatPrivate(const QString &description, const QMimeType &mimeType, const QIcon &icon = QIcon())
0683         : QSharedData()
0684         , mDescription(description)
0685         , mMimeType(mimeType)
0686         , mIcon(icon)
0687     {
0688     }
0689     ~ExportFormatPrivate()
0690     {
0691     }
0692 
0693     QString mDescription;
0694     QMimeType mMimeType;
0695     QIcon mIcon;
0696 };
0697 
0698 ExportFormat::ExportFormat()
0699     : d(new ExportFormatPrivate(QString(), QMimeType()))
0700 {
0701 }
0702 
0703 ExportFormat::ExportFormat(const QString &description, const QMimeType &mimeType)
0704     : d(new ExportFormatPrivate(description, mimeType))
0705 {
0706 }
0707 
0708 ExportFormat::ExportFormat(const QIcon &icon, const QString &description, const QMimeType &mimeType)
0709     : d(new ExportFormatPrivate(description, mimeType, icon))
0710 {
0711 }
0712 
0713 ExportFormat::~ExportFormat()
0714 {
0715 }
0716 
0717 ExportFormat::ExportFormat(const ExportFormat &other)
0718     : d(other.d)
0719 {
0720 }
0721 
0722 ExportFormat &ExportFormat::operator=(const ExportFormat &other)
0723 {
0724     if (this == &other) {
0725         return *this;
0726     }
0727 
0728     d = other.d;
0729 
0730     return *this;
0731 }
0732 
0733 QString ExportFormat::description() const
0734 {
0735     return d->mDescription;
0736 }
0737 
0738 QMimeType ExportFormat::mimeType() const
0739 {
0740     return d->mMimeType;
0741 }
0742 
0743 QIcon ExportFormat::icon() const
0744 {
0745     return d->mIcon;
0746 }
0747 
0748 bool ExportFormat::isNull() const
0749 {
0750     return !d->mMimeType.isValid() || d->mDescription.isNull();
0751 }
0752 
0753 ExportFormat ExportFormat::standardFormat(StandardExportFormat type)
0754 {
0755     QMimeDatabase db;
0756     switch (type) {
0757     case PlainText:
0758         return ExportFormat(QIcon::fromTheme(QStringLiteral("text-x-generic")), i18n("Plain &Text..."), db.mimeTypeForName(QStringLiteral("text/plain")));
0759         break;
0760     case PDF:
0761         return ExportFormat(QIcon::fromTheme(QStringLiteral("application-pdf")), i18n("PDF"), db.mimeTypeForName(QStringLiteral("application/pdf")));
0762         break;
0763     case OpenDocumentText:
0764         return ExportFormat(
0765             QIcon::fromTheme(QStringLiteral("application-vnd.oasis.opendocument.text")), i18nc("This is the document format", "OpenDocument Text"), db.mimeTypeForName(QStringLiteral("application/vnd.oasis.opendocument.text")));
0766         break;
0767     case HTML:
0768         return ExportFormat(QIcon::fromTheme(QStringLiteral("text-html")), i18nc("This is the document format", "HTML"), db.mimeTypeForName(QStringLiteral("text/html")));
0769         break;
0770     }
0771     return ExportFormat();
0772 }
0773 
0774 bool ExportFormat::operator==(const ExportFormat &other) const
0775 {
0776     return d == other.d;
0777 }
0778 
0779 bool ExportFormat::operator!=(const ExportFormat &other) const
0780 {
0781     return d != other.d;
0782 }
0783 
0784 QDebug operator<<(QDebug str, const Okular::PixmapRequest &req)
0785 {
0786     PixmapRequestPrivate *reqPriv = PixmapRequestPrivate::get(&req);
0787 
0788     str << "PixmapRequest:" << &req;
0789     str << "- observer:" << (qulonglong)req.observer();
0790     str << "- page:" << req.pageNumber();
0791     str << "- width:" << req.width();
0792     str << "- height:" << req.height();
0793     str << "- priority:" << req.priority();
0794     str << "- async:" << (req.asynchronous() ? "true" : "false");
0795     str << "- tile:" << (req.isTile() ? "true" : "false");
0796     str << "- rect:" << req.normalizedRect();
0797     str << "- preload:" << (req.preload() ? "true" : "false");
0798     str << "- partialUpdates:" << (req.partialUpdatesWanted() ? "true" : "false");
0799     str << "- shouldAbort:" << (req.shouldAbortRender() ? "true" : "false");
0800     str << "- force:" << (reqPriv->mForce ? "true" : "false");
0801     return str;
0802 }
0803 
0804 /* kate: replace-tabs on; indent-width 4; */