1 /* poppler-document.cc: qt interface to poppler
2  * Copyright (C) 2005, Net Integration Technologies, Inc.
3  * Copyright (C) 2005, 2008, Brad Hards <bradh@frogmouth.net>
4  * Copyright (C) 2005-2010, 2012, 2013, 2015, 2017-2021, Albert Astals Cid <aacid@kde.org>
5  * Copyright (C) 2006-2010, Pino Toscano <pino@kde.org>
6  * Copyright (C) 2010, 2011 Hib Eris <hib@hiberis.nl>
7  * Copyright (C) 2012 Koji Otani <sho@bbr.jp>
8  * Copyright (C) 2012, 2013 Thomas Freitag <Thomas.Freitag@alfa.de>
9  * Copyright (C) 2012 Fabio D'Urso <fabiodurso@hotmail.it>
10  * Copyright (C) 2014, 2018, 2020 Adam Reichold <adam.reichold@t-online.de>
11  * Copyright (C) 2015 William Bader <williambader@hotmail.com>
12  * Copyright (C) 2016 Jakub Alba <jakubalba@gmail.com>
13  * Copyright (C) 2017, 2021 Adrian Johnson <ajohnson@redneon.com>
14  * Copyright (C) 2017 Suzuki Toshiya <mpsuzuki@hiroshima-u.ac.jp>
15  * Copyright (C) 2018 Klarälvdalens Datakonsult AB, a KDAB Group company, <info@kdab.com>. Work sponsored by the LiMux project of the city of Munich
16  * Copyright (C) 2019-2021 Oliver Sander <oliver.sander@tu-dresden.de>
17  * Copyright (C) 2019 Alexander Volkov <a.volkov@rusbitech.ru>
18  * Copyright (C) 2020 Philipp Knechtges <philipp-dev@knechtges.com>
19  * Copyright (C) 2020 Katarina Behrens <Katarina.Behrens@cib.de>
20  * Copyright (C) 2020 Thorsten Behrens <Thorsten.Behrens@CIB.de>
21  * Copyright (C) 2021 Mahmoud Khalil <mahmoudkhalil11@gmail.com>
22  * Copyright (C) 2021 Hubert Figuiere <hub@figuiere.net>
23  *
24  * This program is free software; you can redistribute it and/or modify
25  * it under the terms of the GNU General Public License as published by
26  * the Free Software Foundation; either version 2, or (at your option)
27  * any later version.
28  *
29  * This program is distributed in the hope that it will be useful,
30  * but WITHOUT ANY WARRANTY; without even the implied warranty of
31  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
32  * GNU General Public License for more details.
33  *
34  * You should have received a copy of the GNU General Public License
35  * along with this program; if not, write to the Free Software
36  * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
37  */
38 
39 #include "poppler-qt5.h"
40 
41 #include <config.h>
42 #include <poppler-config.h>
43 #include <ErrorCodes.h>
44 #include <GlobalParams.h>
45 #include <Outline.h>
46 #include <PDFDoc.h>
47 #include <Stream.h>
48 #include <Catalog.h>
49 #include <ViewerPreferences.h>
50 #include <DateInfo.h>
51 #include <GfxState.h>
52 
53 #include <QtCore/QDebug>
54 #include <QtCore/QFile>
55 #include <QtCore/QByteArray>
56 
57 #include "poppler-form.h"
58 #include "poppler-private.h"
59 #include "poppler-page-private.h"
60 #include "poppler-outline-private.h"
61 
62 #if defined(USE_CMS)
63 #    include <lcms2.h>
64 #endif
65 
66 namespace Poppler {
67 
load(const QString & filePath,const QByteArray & ownerPassword,const QByteArray & userPassword)68 Document *Document::load(const QString &filePath, const QByteArray &ownerPassword, const QByteArray &userPassword)
69 {
70     DocumentData *doc = new DocumentData(filePath, new GooString(ownerPassword.data()), new GooString(userPassword.data()));
71     return DocumentData::checkDocument(doc);
72 }
73 
load(QIODevice * device,const QByteArray & ownerPassword,const QByteArray & userPassword)74 Document *Document::load(QIODevice *device, const QByteArray &ownerPassword, const QByteArray &userPassword)
75 {
76     DocumentData *doc = new DocumentData(device, new GooString(ownerPassword.data()), new GooString(userPassword.data()));
77     return DocumentData::checkDocument(doc);
78 }
79 
loadFromData(const QByteArray & fileContents,const QByteArray & ownerPassword,const QByteArray & userPassword)80 Document *Document::loadFromData(const QByteArray &fileContents, const QByteArray &ownerPassword, const QByteArray &userPassword)
81 {
82     // create stream
83     DocumentData *doc = new DocumentData(fileContents, new GooString(ownerPassword.data()), new GooString(userPassword.data()));
84     return DocumentData::checkDocument(doc);
85 }
86 
checkDocument(DocumentData * doc)87 Document *DocumentData::checkDocument(DocumentData *doc)
88 {
89     Document *pdoc;
90     if (doc->doc->isOk() || doc->doc->getErrorCode() == errEncrypted) {
91         pdoc = new Document(doc);
92         if (doc->doc->getErrorCode() == errEncrypted)
93             pdoc->m_doc->locked = true;
94         else {
95             pdoc->m_doc->locked = false;
96             pdoc->m_doc->fillMembers();
97         }
98         return pdoc;
99     } else {
100         delete doc;
101     }
102     return nullptr;
103 }
104 
Document(DocumentData * dataA)105 Document::Document(DocumentData *dataA)
106 {
107     m_doc = dataA;
108 }
109 
~Document()110 Document::~Document()
111 {
112     delete m_doc;
113 }
114 
page(int index) const115 Page *Document::page(int index) const
116 {
117     Page *page = new Page(m_doc, index);
118     if (page->m_page->page == nullptr) {
119         delete page;
120         return nullptr;
121     }
122 
123     return page;
124 }
125 
isLocked() const126 bool Document::isLocked() const
127 {
128     return m_doc->locked;
129 }
130 
unlock(const QByteArray & ownerPassword,const QByteArray & userPassword)131 bool Document::unlock(const QByteArray &ownerPassword, const QByteArray &userPassword)
132 {
133     if (m_doc->locked) {
134         /* racier then it needs to be */
135         DocumentData *doc2;
136         if (!m_doc->fileContents.isEmpty()) {
137             doc2 = new DocumentData(m_doc->fileContents, new GooString(ownerPassword.data()), new GooString(userPassword.data()));
138         } else if (m_doc->m_device) {
139             doc2 = new DocumentData(m_doc->m_device, new GooString(ownerPassword.data()), new GooString(userPassword.data()));
140         } else {
141             doc2 = new DocumentData(m_doc->m_filePath, new GooString(ownerPassword.data()), new GooString(userPassword.data()));
142         }
143         if (!doc2->doc->isOk()) {
144             delete doc2;
145         } else {
146             delete m_doc;
147             m_doc = doc2;
148             m_doc->locked = false;
149             m_doc->fillMembers();
150         }
151     }
152     return m_doc->locked;
153 }
154 
pageMode() const155 Document::PageMode Document::pageMode() const
156 {
157     switch (m_doc->doc->getCatalog()->getPageMode()) {
158     case Catalog::pageModeNone:
159         return UseNone;
160     case Catalog::pageModeOutlines:
161         return UseOutlines;
162     case Catalog::pageModeThumbs:
163         return UseThumbs;
164     case Catalog::pageModeFullScreen:
165         return FullScreen;
166     case Catalog::pageModeOC:
167         return UseOC;
168     case Catalog::pageModeAttach:
169         return UseAttach;
170     default:
171         return UseNone;
172     }
173 }
174 
pageLayout() const175 Document::PageLayout Document::pageLayout() const
176 {
177     switch (m_doc->doc->getCatalog()->getPageLayout()) {
178     case Catalog::pageLayoutNone:
179         return NoLayout;
180     case Catalog::pageLayoutSinglePage:
181         return SinglePage;
182     case Catalog::pageLayoutOneColumn:
183         return OneColumn;
184     case Catalog::pageLayoutTwoColumnLeft:
185         return TwoColumnLeft;
186     case Catalog::pageLayoutTwoColumnRight:
187         return TwoColumnRight;
188     case Catalog::pageLayoutTwoPageLeft:
189         return TwoPageLeft;
190     case Catalog::pageLayoutTwoPageRight:
191         return TwoPageRight;
192     default:
193         return NoLayout;
194     }
195 }
196 
textDirection() const197 Qt::LayoutDirection Document::textDirection() const
198 {
199     if (!m_doc->doc->getCatalog()->getViewerPreferences())
200         return Qt::LayoutDirectionAuto;
201 
202     switch (m_doc->doc->getCatalog()->getViewerPreferences()->getDirection()) {
203     case ViewerPreferences::directionL2R:
204         return Qt::LeftToRight;
205     case ViewerPreferences::directionR2L:
206         return Qt::RightToLeft;
207     default:
208         return Qt::LayoutDirectionAuto;
209     }
210 }
211 
numPages() const212 int Document::numPages() const
213 {
214     return m_doc->doc->getNumPages();
215 }
216 
fonts() const217 QList<FontInfo> Document::fonts() const
218 {
219     QList<FontInfo> ourList;
220     FontIterator it(0, m_doc);
221     while (it.hasNext()) {
222         ourList += it.next();
223     }
224     return ourList;
225 }
226 
embeddedFiles() const227 QList<EmbeddedFile *> Document::embeddedFiles() const
228 {
229     return m_doc->m_embeddedFiles;
230 }
231 
newFontIterator(int startPage) const232 FontIterator *Document::newFontIterator(int startPage) const
233 {
234     return new FontIterator(startPage, m_doc);
235 }
236 
fontData(const FontInfo & fi) const237 QByteArray Document::fontData(const FontInfo &fi) const
238 {
239     QByteArray result;
240     if (fi.isEmbedded()) {
241         XRef *xref = m_doc->doc->getXRef()->copy();
242 
243         Object refObj(fi.m_data->embRef);
244         Object strObj = refObj.fetch(xref);
245         if (strObj.isStream()) {
246             int c;
247             strObj.streamReset();
248             while ((c = strObj.streamGetChar()) != EOF) {
249                 result.append((char)c);
250             }
251             strObj.streamClose();
252         }
253         delete xref;
254     }
255     return result;
256 }
257 
info(const QString & type) const258 QString Document::info(const QString &type) const
259 {
260     if (m_doc->locked) {
261         return QString();
262     }
263 
264     QScopedPointer<GooString> goo(m_doc->doc->getDocInfoStringEntry(type.toLatin1().constData()));
265     return UnicodeParsedString(goo.data());
266 }
267 
setInfo(const QString & key,const QString & val)268 bool Document::setInfo(const QString &key, const QString &val)
269 {
270     if (m_doc->locked) {
271         return false;
272     }
273 
274     GooString *goo = QStringToUnicodeGooString(val);
275     m_doc->doc->setDocInfoStringEntry(key.toLatin1().constData(), goo);
276     return true;
277 }
278 
title() const279 QString Document::title() const
280 {
281     if (m_doc->locked) {
282         return QString();
283     }
284 
285     QScopedPointer<GooString> goo(m_doc->doc->getDocInfoTitle());
286     return UnicodeParsedString(goo.data());
287 }
288 
setTitle(const QString & val)289 bool Document::setTitle(const QString &val)
290 {
291     if (m_doc->locked) {
292         return false;
293     }
294 
295     m_doc->doc->setDocInfoTitle(QStringToUnicodeGooString(val));
296     return true;
297 }
298 
author() const299 QString Document::author() const
300 {
301     if (m_doc->locked) {
302         return QString();
303     }
304 
305     QScopedPointer<GooString> goo(m_doc->doc->getDocInfoAuthor());
306     return UnicodeParsedString(goo.data());
307 }
308 
setAuthor(const QString & val)309 bool Document::setAuthor(const QString &val)
310 {
311     if (m_doc->locked) {
312         return false;
313     }
314 
315     m_doc->doc->setDocInfoAuthor(QStringToUnicodeGooString(val));
316     return true;
317 }
318 
subject() const319 QString Document::subject() const
320 {
321     if (m_doc->locked) {
322         return QString();
323     }
324 
325     QScopedPointer<GooString> goo(m_doc->doc->getDocInfoSubject());
326     return UnicodeParsedString(goo.data());
327 }
328 
setSubject(const QString & val)329 bool Document::setSubject(const QString &val)
330 {
331     if (m_doc->locked) {
332         return false;
333     }
334 
335     m_doc->doc->setDocInfoSubject(QStringToUnicodeGooString(val));
336     return true;
337 }
338 
keywords() const339 QString Document::keywords() const
340 {
341     if (m_doc->locked) {
342         return QString();
343     }
344 
345     QScopedPointer<GooString> goo(m_doc->doc->getDocInfoKeywords());
346     return UnicodeParsedString(goo.data());
347 }
348 
setKeywords(const QString & val)349 bool Document::setKeywords(const QString &val)
350 {
351     if (m_doc->locked) {
352         return false;
353     }
354 
355     m_doc->doc->setDocInfoKeywords(QStringToUnicodeGooString(val));
356     return true;
357 }
358 
creator() const359 QString Document::creator() const
360 {
361     if (m_doc->locked) {
362         return QString();
363     }
364 
365     QScopedPointer<GooString> goo(m_doc->doc->getDocInfoCreator());
366     return UnicodeParsedString(goo.data());
367 }
368 
setCreator(const QString & val)369 bool Document::setCreator(const QString &val)
370 {
371     if (m_doc->locked) {
372         return false;
373     }
374 
375     m_doc->doc->setDocInfoCreator(QStringToUnicodeGooString(val));
376     return true;
377 }
378 
producer() const379 QString Document::producer() const
380 {
381     if (m_doc->locked) {
382         return QString();
383     }
384 
385     QScopedPointer<GooString> goo(m_doc->doc->getDocInfoProducer());
386     return UnicodeParsedString(goo.data());
387 }
388 
setProducer(const QString & val)389 bool Document::setProducer(const QString &val)
390 {
391     if (m_doc->locked) {
392         return false;
393     }
394 
395     m_doc->doc->setDocInfoProducer(QStringToUnicodeGooString(val));
396     return true;
397 }
398 
removeInfo()399 bool Document::removeInfo()
400 {
401     if (m_doc->locked) {
402         return false;
403     }
404 
405     m_doc->doc->removeDocInfo();
406     return true;
407 }
408 
infoKeys() const409 QStringList Document::infoKeys() const
410 {
411     QStringList keys;
412 
413     if (m_doc->locked)
414         return QStringList();
415 
416     QScopedPointer<XRef> xref(m_doc->doc->getXRef()->copy());
417     if (!xref)
418         return QStringList();
419     Object info = xref->getDocInfo();
420     if (!info.isDict())
421         return QStringList();
422 
423     Dict *infoDict = info.getDict();
424     // somehow iterate over keys in infoDict
425     keys.reserve(infoDict->getLength());
426     for (int i = 0; i < infoDict->getLength(); ++i) {
427         keys.append(QString::fromLatin1(infoDict->getKey(i)));
428     }
429 
430     return keys;
431 }
432 
date(const QString & type) const433 QDateTime Document::date(const QString &type) const
434 {
435     if (m_doc->locked) {
436         return QDateTime();
437     }
438 
439     QScopedPointer<GooString> goo(m_doc->doc->getDocInfoStringEntry(type.toLatin1().constData()));
440     QString str = UnicodeParsedString(goo.data());
441     return Poppler::convertDate(str.toLatin1().constData());
442 }
443 
setDate(const QString & key,const QDateTime & val)444 bool Document::setDate(const QString &key, const QDateTime &val)
445 {
446     if (m_doc->locked) {
447         return false;
448     }
449 
450     m_doc->doc->setDocInfoStringEntry(key.toLatin1().constData(), QDateTimeToUnicodeGooString(val));
451     return true;
452 }
453 
creationDate() const454 QDateTime Document::creationDate() const
455 {
456     if (m_doc->locked) {
457         return QDateTime();
458     }
459 
460     QScopedPointer<GooString> goo(m_doc->doc->getDocInfoCreatDate());
461     QString str = UnicodeParsedString(goo.data());
462     return Poppler::convertDate(str.toLatin1().constData());
463 }
464 
setCreationDate(const QDateTime & val)465 bool Document::setCreationDate(const QDateTime &val)
466 {
467     if (m_doc->locked) {
468         return false;
469     }
470 
471     m_doc->doc->setDocInfoCreatDate(QDateTimeToUnicodeGooString(val));
472     return true;
473 }
474 
modificationDate() const475 QDateTime Document::modificationDate() const
476 {
477     if (m_doc->locked) {
478         return QDateTime();
479     }
480 
481     QScopedPointer<GooString> goo(m_doc->doc->getDocInfoModDate());
482     QString str = UnicodeParsedString(goo.data());
483     return Poppler::convertDate(str.toLatin1().constData());
484 }
485 
setModificationDate(const QDateTime & val)486 bool Document::setModificationDate(const QDateTime &val)
487 {
488     if (m_doc->locked) {
489         return false;
490     }
491 
492     m_doc->doc->setDocInfoModDate(QDateTimeToUnicodeGooString(val));
493     return true;
494 }
495 
isEncrypted() const496 bool Document::isEncrypted() const
497 {
498     return m_doc->doc->isEncrypted();
499 }
500 
isLinearized() const501 bool Document::isLinearized() const
502 {
503     return m_doc->doc->isLinearized();
504 }
505 
okToPrint() const506 bool Document::okToPrint() const
507 {
508     return m_doc->doc->okToPrint();
509 }
510 
okToPrintHighRes() const511 bool Document::okToPrintHighRes() const
512 {
513     return m_doc->doc->okToPrintHighRes();
514 }
515 
okToChange() const516 bool Document::okToChange() const
517 {
518     return m_doc->doc->okToChange();
519 }
520 
okToCopy() const521 bool Document::okToCopy() const
522 {
523     return m_doc->doc->okToCopy();
524 }
525 
okToAddNotes() const526 bool Document::okToAddNotes() const
527 {
528     return m_doc->doc->okToAddNotes();
529 }
530 
okToFillForm() const531 bool Document::okToFillForm() const
532 {
533     return m_doc->doc->okToFillForm();
534 }
535 
okToCreateFormFields() const536 bool Document::okToCreateFormFields() const
537 {
538     return (okToFillForm() && okToChange());
539 }
540 
okToExtractForAccessibility() const541 bool Document::okToExtractForAccessibility() const
542 {
543     return m_doc->doc->okToAccessibility();
544 }
545 
okToAssemble() const546 bool Document::okToAssemble() const
547 {
548     return m_doc->doc->okToAssemble();
549 }
550 
getPdfVersion(int * major,int * minor) const551 void Document::getPdfVersion(int *major, int *minor) const
552 {
553     if (major)
554         *major = m_doc->doc->getPDFMajorVersion();
555     if (minor)
556         *minor = m_doc->doc->getPDFMinorVersion();
557 }
558 
getPdfVersion() const559 Document::PdfVersion Document::getPdfVersion() const
560 {
561     return PdfVersion { m_doc->doc->getPDFMajorVersion(), m_doc->doc->getPDFMinorVersion() };
562 }
563 
page(const QString & label) const564 Page *Document::page(const QString &label) const
565 {
566     GooString label_g(label.toLatin1().data());
567     int index;
568 
569     if (!m_doc->doc->getCatalog()->labelToIndex(&label_g, &index)) {
570         std::unique_ptr<GooString> label_ug(QStringToUnicodeGooString(label));
571         if (!m_doc->doc->getCatalog()->labelToIndex(label_ug.get(), &index)) {
572             return nullptr;
573         }
574     }
575 
576     return page(index);
577 }
578 
hasEmbeddedFiles() const579 bool Document::hasEmbeddedFiles() const
580 {
581     return (!(0 == m_doc->doc->getCatalog()->numEmbeddedFiles()));
582 }
583 
toc() const584 QDomDocument *Document::toc() const
585 {
586     Outline *outline = m_doc->doc->getOutline();
587     if (!outline)
588         return nullptr;
589 
590     const std::vector<::OutlineItem *> *items = outline->getItems();
591     if (!items || items->size() < 1)
592         return nullptr;
593 
594     QDomDocument *toc = new QDomDocument();
595     if (items->size() > 0)
596         m_doc->addTocChildren(toc, toc, items);
597 
598     return toc;
599 }
600 
outline() const601 QVector<OutlineItem> Document::outline() const
602 {
603     QVector<OutlineItem> result;
604 
605     if (::Outline *outline = m_doc->doc->getOutline()) {
606         if (const std::vector<::OutlineItem *> *items = outline->getItems()) {
607             for (void *item : *items) {
608                 result.push_back(OutlineItem { new OutlineItemData { static_cast<::OutlineItem *>(item), m_doc } });
609             }
610         }
611     }
612 
613     return result;
614 }
615 
linkDestination(const QString & name)616 LinkDestination *Document::linkDestination(const QString &name)
617 {
618     GooString *namedDest = QStringToGooString(name);
619     LinkDestinationData ldd(nullptr, namedDest, m_doc, false);
620     LinkDestination *ld = new LinkDestination(ldd);
621     delete namedDest;
622     return ld;
623 }
624 
setPaperColor(const QColor & color)625 void Document::setPaperColor(const QColor &color)
626 {
627     m_doc->setPaperColor(color);
628 }
629 
setColorDisplayProfile(void * outputProfileA)630 void Document::setColorDisplayProfile(void *outputProfileA)
631 {
632 #if defined(USE_CMS)
633     if (m_doc->m_sRGBProfile && m_doc->m_sRGBProfile.get() == outputProfileA) {
634         // Catch the special case that the user passes the sRGB profile
635         m_doc->m_displayProfile = m_doc->m_sRGBProfile;
636         return;
637     }
638     if (m_doc->m_displayProfile && m_doc->m_displayProfile.get() == outputProfileA) {
639         // Catch the special case that the user passes the display profile
640         return;
641     }
642     m_doc->m_displayProfile = make_GfxLCMSProfilePtr(outputProfileA);
643 #else
644     Q_UNUSED(outputProfileA);
645 #endif
646 }
647 
setColorDisplayProfileName(const QString & name)648 void Document::setColorDisplayProfileName(const QString &name)
649 {
650 #if defined(USE_CMS)
651     void *rawprofile = cmsOpenProfileFromFile(name.toLocal8Bit().constData(), "r");
652     m_doc->m_displayProfile = make_GfxLCMSProfilePtr(rawprofile);
653 #else
654     Q_UNUSED(name);
655 #endif
656 }
657 
colorRgbProfile() const658 void *Document::colorRgbProfile() const
659 {
660 #if defined(USE_CMS)
661     if (!m_doc->m_sRGBProfile) {
662         m_doc->m_sRGBProfile = make_GfxLCMSProfilePtr(cmsCreate_sRGBProfile());
663     }
664     return m_doc->m_sRGBProfile.get();
665 #else
666     return nullptr;
667 #endif
668 }
669 
colorDisplayProfile() const670 void *Document::colorDisplayProfile() const
671 {
672 #if defined(USE_CMS)
673     return m_doc->m_displayProfile.get();
674 #else
675     return nullptr;
676 #endif
677 }
678 
paperColor() const679 QColor Document::paperColor() const
680 {
681     return m_doc->paperColor;
682 }
683 
setRenderBackend(Document::RenderBackend backend)684 void Document::setRenderBackend(Document::RenderBackend backend)
685 {
686     // no need to delete the outputdev as for the moment we always create a splash one
687     // as the QPainter one does not allow "precaching" due to it's signature
688     // delete m_doc->m_outputDev;
689     // m_doc->m_outputDev = NULL;
690     m_doc->m_backend = backend;
691 }
692 
renderBackend() const693 Document::RenderBackend Document::renderBackend() const
694 {
695     return m_doc->m_backend;
696 }
697 
availableRenderBackends()698 QSet<Document::RenderBackend> Document::availableRenderBackends()
699 {
700     QSet<Document::RenderBackend> ret;
701     ret << Document::SplashBackend;
702     ret << Document::QPainterBackend;
703     ret << Document::ArthurBackend; // For backward compatibility
704     return ret;
705 }
706 
setRenderHint(Document::RenderHint hint,bool on)707 void Document::setRenderHint(Document::RenderHint hint, bool on)
708 {
709     const bool touchesOverprinting = hint & Document::OverprintPreview;
710 
711     int hintForOperation = hint;
712     if (touchesOverprinting && !isOverprintPreviewAvailable())
713         hintForOperation = hintForOperation & ~(int)Document::OverprintPreview;
714 
715     if (on)
716         m_doc->m_hints |= hintForOperation;
717     else
718         m_doc->m_hints &= ~hintForOperation;
719 }
720 
renderHints() const721 Document::RenderHints Document::renderHints() const
722 {
723     return Document::RenderHints(m_doc->m_hints);
724 }
725 
psConverter() const726 PSConverter *Document::psConverter() const
727 {
728     return new PSConverter(m_doc);
729 }
730 
pdfConverter() const731 PDFConverter *Document::pdfConverter() const
732 {
733     return new PDFConverter(m_doc);
734 }
735 
metadata() const736 QString Document::metadata() const
737 {
738     QString result;
739     Catalog *catalog = m_doc->doc->getCatalog();
740     if (catalog && catalog->isOk()) {
741         std::unique_ptr<GooString> s = catalog->readMetadata();
742         if (s)
743             result = UnicodeParsedString(s.get());
744     }
745     return result;
746 }
747 
hasOptionalContent() const748 bool Document::hasOptionalContent() const
749 {
750     return (m_doc->doc->getOptContentConfig() && m_doc->doc->getOptContentConfig()->hasOCGs());
751 }
752 
optionalContentModel()753 OptContentModel *Document::optionalContentModel()
754 {
755     if (m_doc->m_optContentModel.isNull()) {
756         m_doc->m_optContentModel = new OptContentModel(m_doc->doc->getOptContentConfig(), nullptr);
757     }
758     return (OptContentModel *)m_doc->m_optContentModel;
759 }
760 
scripts() const761 QStringList Document::scripts() const
762 {
763     Catalog *catalog = m_doc->doc->getCatalog();
764     const int numScripts = catalog->numJS();
765     QStringList scripts;
766     for (int i = 0; i < numScripts; ++i) {
767         GooString *s = catalog->getJS(i);
768         if (s) {
769             scripts.append(UnicodeParsedString(s));
770             delete s;
771         }
772     }
773     return scripts;
774 }
775 
getPdfId(QByteArray * permanentId,QByteArray * updateId) const776 bool Document::getPdfId(QByteArray *permanentId, QByteArray *updateId) const
777 {
778     GooString gooPermanentId;
779     GooString gooUpdateId;
780 
781     if (!m_doc->doc->getID(permanentId ? &gooPermanentId : nullptr, updateId ? &gooUpdateId : nullptr))
782         return false;
783 
784     if (permanentId)
785         *permanentId = gooPermanentId.c_str();
786     if (updateId)
787         *updateId = gooUpdateId.c_str();
788 
789     return true;
790 }
791 
formType() const792 Document::FormType Document::formType() const
793 {
794     switch (m_doc->doc->getCatalog()->getFormType()) {
795     case Catalog::NoForm:
796         return Document::NoForm;
797     case Catalog::AcroForm:
798         return Document::AcroForm;
799     case Catalog::XfaForm:
800         return Document::XfaForm;
801     }
802 
803     return Document::NoForm; // make gcc happy
804 }
805 
formCalculateOrder() const806 QVector<int> Document::formCalculateOrder() const
807 {
808     Form *form = m_doc->doc->getCatalog()->getForm();
809     if (!form) {
810         return {};
811     }
812 
813     QVector<int> result;
814     const std::vector<Ref> &calculateOrder = form->getCalculateOrder();
815     for (Ref r : calculateOrder) {
816         FormWidget *w = form->findWidgetByRef(r);
817         if (w) {
818             result << w->getID();
819         }
820     }
821 
822     return result;
823 }
824 
signatures() const825 QVector<FormFieldSignature *> Document::signatures() const
826 {
827     QVector<FormFieldSignature *> result;
828 
829     const std::vector<::FormFieldSignature *> pSignatures = m_doc->doc->getSignatureFields();
830 
831     for (::FormFieldSignature *pSignature : pSignatures) {
832         ::FormWidget *fw = pSignature->getCreateWidget();
833         ::Page *p = m_doc->doc->getPage(fw->getWidgetAnnotation()->getPageNum());
834         result.append(new FormFieldSignature(m_doc, p, static_cast<FormWidgetSignature *>(fw)));
835     }
836 
837     return result;
838 }
839 
xrefWasReconstructed() const840 bool Document::xrefWasReconstructed() const
841 {
842     return m_doc->xrefReconstructed;
843 }
844 
setXRefReconstructedCallback(const std::function<void ()> & callback)845 void Document::setXRefReconstructedCallback(const std::function<void()> &callback)
846 {
847     m_doc->xrefReconstructedCallback = callback;
848 }
849 
convertDate(const char * dateString)850 QDateTime convertDate(const char *dateString)
851 {
852     int year, mon, day, hour, min, sec, tzHours, tzMins;
853     char tz;
854 
855     GooString date(dateString);
856     if (parseDateString(&date, &year, &mon, &day, &hour, &min, &sec, &tz, &tzHours, &tzMins)) {
857         QDate d(year, mon, day);
858         QTime t(hour, min, sec);
859         if (d.isValid() && t.isValid()) {
860             QDateTime dt(d, t, Qt::UTC);
861             if (tz) {
862                 // then we have some form of timezone
863                 if ('Z' == tz) {
864                     // We are already at UTC
865                 } else if ('+' == tz) {
866                     // local time is ahead of UTC
867                     dt = dt.addSecs(-1 * ((tzHours * 60) + tzMins) * 60);
868                 } else if ('-' == tz) {
869                     // local time is behind UTC
870                     dt = dt.addSecs(((tzHours * 60) + tzMins) * 60);
871                 } else {
872                     qWarning("unexpected tz val");
873                 }
874             }
875             return dt;
876         }
877     }
878     return QDateTime();
879 }
880 
convertDate(char * dateString)881 QDateTime convertDate(char *dateString)
882 {
883     return convertDate((const char *)dateString);
884 }
885 
isCmsAvailable()886 bool isCmsAvailable()
887 {
888 #if defined(USE_CMS)
889     return true;
890 #else
891     return false;
892 #endif
893 }
894 
isOverprintPreviewAvailable()895 bool isOverprintPreviewAvailable()
896 {
897     return true;
898 }
899 
900 }
901