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-qt6.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 std::unique_ptr<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 std::unique_ptr<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 std::unique_ptr<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 std::unique_ptr<Document> DocumentData::checkDocument(DocumentData *doc)
88 {
89     if (doc->doc->isOk() || doc->doc->getErrorCode() == errEncrypted) {
90         auto pdoc = std::unique_ptr<Document>(new Document(doc));
91         if (doc->doc->getErrorCode() == errEncrypted)
92             pdoc->m_doc->locked = true;
93         else {
94             pdoc->m_doc->locked = false;
95             pdoc->m_doc->fillMembers();
96         }
97         return pdoc;
98     } else {
99         delete doc;
100     }
101     return nullptr;
102 }
103 
Document(DocumentData * dataA)104 Document::Document(DocumentData *dataA)
105 {
106     m_doc = dataA;
107 }
108 
~Document()109 Document::~Document()
110 {
111     delete m_doc;
112 }
113 
page(int index) const114 std::unique_ptr<Page> Document::page(int index) const
115 {
116     // Cannot call std::make_unique, because the constructor of Page is private
117     auto page = std::unique_ptr<Page>(new Page(m_doc, index));
118     if (page->m_page->page == nullptr) {
119         page.reset();
120     }
121 
122     return page;
123 }
124 
isLocked() const125 bool Document::isLocked() const
126 {
127     return m_doc->locked;
128 }
129 
unlock(const QByteArray & ownerPassword,const QByteArray & userPassword)130 bool Document::unlock(const QByteArray &ownerPassword, const QByteArray &userPassword)
131 {
132     if (m_doc->locked) {
133         /* racier then it needs to be */
134         DocumentData *doc2;
135         if (!m_doc->fileContents.isEmpty()) {
136             doc2 = new DocumentData(m_doc->fileContents, new GooString(ownerPassword.data()), new GooString(userPassword.data()));
137         } else if (m_doc->m_device) {
138             doc2 = new DocumentData(m_doc->m_device, new GooString(ownerPassword.data()), new GooString(userPassword.data()));
139         } else {
140             doc2 = new DocumentData(m_doc->m_filePath, new GooString(ownerPassword.data()), new GooString(userPassword.data()));
141         }
142         if (!doc2->doc->isOk()) {
143             delete doc2;
144         } else {
145             delete m_doc;
146             m_doc = doc2;
147             m_doc->locked = false;
148             m_doc->fillMembers();
149         }
150     }
151     return m_doc->locked;
152 }
153 
pageMode() const154 Document::PageMode Document::pageMode() const
155 {
156     switch (m_doc->doc->getCatalog()->getPageMode()) {
157     case Catalog::pageModeNone:
158         return UseNone;
159     case Catalog::pageModeOutlines:
160         return UseOutlines;
161     case Catalog::pageModeThumbs:
162         return UseThumbs;
163     case Catalog::pageModeFullScreen:
164         return FullScreen;
165     case Catalog::pageModeOC:
166         return UseOC;
167     case Catalog::pageModeAttach:
168         return UseAttach;
169     default:
170         return UseNone;
171     }
172 }
173 
pageLayout() const174 Document::PageLayout Document::pageLayout() const
175 {
176     switch (m_doc->doc->getCatalog()->getPageLayout()) {
177     case Catalog::pageLayoutNone:
178         return NoLayout;
179     case Catalog::pageLayoutSinglePage:
180         return SinglePage;
181     case Catalog::pageLayoutOneColumn:
182         return OneColumn;
183     case Catalog::pageLayoutTwoColumnLeft:
184         return TwoColumnLeft;
185     case Catalog::pageLayoutTwoColumnRight:
186         return TwoColumnRight;
187     case Catalog::pageLayoutTwoPageLeft:
188         return TwoPageLeft;
189     case Catalog::pageLayoutTwoPageRight:
190         return TwoPageRight;
191     default:
192         return NoLayout;
193     }
194 }
195 
textDirection() const196 Qt::LayoutDirection Document::textDirection() const
197 {
198     if (!m_doc->doc->getCatalog()->getViewerPreferences())
199         return Qt::LayoutDirectionAuto;
200 
201     switch (m_doc->doc->getCatalog()->getViewerPreferences()->getDirection()) {
202     case ViewerPreferences::directionL2R:
203         return Qt::LeftToRight;
204     case ViewerPreferences::directionR2L:
205         return Qt::RightToLeft;
206     default:
207         return Qt::LayoutDirectionAuto;
208     }
209 }
210 
numPages() const211 int Document::numPages() const
212 {
213     return m_doc->doc->getNumPages();
214 }
215 
fonts() const216 QList<FontInfo> Document::fonts() const
217 {
218     QList<FontInfo> ourList;
219     FontIterator it(0, m_doc);
220     while (it.hasNext()) {
221         ourList += it.next();
222     }
223     return ourList;
224 }
225 
embeddedFiles() const226 QList<EmbeddedFile *> Document::embeddedFiles() const
227 {
228     return m_doc->m_embeddedFiles;
229 }
230 
newFontIterator(int startPage) const231 std::unique_ptr<FontIterator> Document::newFontIterator(int startPage) const
232 {
233     // Cannot use std::make_unique, because the FontIterator constructor is private
234     return std::unique_ptr<FontIterator>(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() const551 Document::PdfVersion Document::getPdfVersion() const
552 {
553     return PdfVersion { m_doc->doc->getPDFMajorVersion(), m_doc->doc->getPDFMinorVersion() };
554 }
555 
page(const QString & label) const556 std::unique_ptr<Page> Document::page(const QString &label) const
557 {
558     GooString label_g(label.toLatin1().data());
559     int index;
560 
561     if (!m_doc->doc->getCatalog()->labelToIndex(&label_g, &index)) {
562         std::unique_ptr<GooString> label_ug(QStringToUnicodeGooString(label));
563         if (!m_doc->doc->getCatalog()->labelToIndex(label_ug.get(), &index)) {
564             return nullptr;
565         }
566     }
567 
568     return page(index);
569 }
570 
hasEmbeddedFiles() const571 bool Document::hasEmbeddedFiles() const
572 {
573     return (!(0 == m_doc->doc->getCatalog()->numEmbeddedFiles()));
574 }
575 
outline() const576 QVector<OutlineItem> Document::outline() const
577 {
578     QVector<OutlineItem> result;
579 
580     if (::Outline *outline = m_doc->doc->getOutline()) {
581         if (const std::vector<::OutlineItem *> *items = outline->getItems()) {
582             for (void *item : *items) {
583                 result.push_back(OutlineItem { new OutlineItemData { static_cast<::OutlineItem *>(item), m_doc } });
584             }
585         }
586     }
587 
588     return result;
589 }
590 
linkDestination(const QString & name)591 std::unique_ptr<LinkDestination> Document::linkDestination(const QString &name)
592 {
593     GooString *namedDest = QStringToGooString(name);
594     LinkDestinationData ldd(nullptr, namedDest, m_doc, false);
595     auto ld = std::make_unique<LinkDestination>(ldd);
596     delete namedDest;
597     return ld;
598 }
599 
setPaperColor(const QColor & color)600 void Document::setPaperColor(const QColor &color)
601 {
602     m_doc->setPaperColor(color);
603 }
604 
setColorDisplayProfile(void * outputProfileA)605 void Document::setColorDisplayProfile(void *outputProfileA)
606 {
607 #if defined(USE_CMS)
608     if (m_doc->m_sRGBProfile && m_doc->m_sRGBProfile.get() == outputProfileA) {
609         // Catch the special case that the user passes the sRGB profile
610         m_doc->m_displayProfile = m_doc->m_sRGBProfile;
611         return;
612     }
613     if (m_doc->m_displayProfile && m_doc->m_displayProfile.get() == outputProfileA) {
614         // Catch the special case that the user passes the display profile
615         return;
616     }
617     m_doc->m_displayProfile = make_GfxLCMSProfilePtr(outputProfileA);
618 #else
619     Q_UNUSED(outputProfileA);
620 #endif
621 }
622 
setColorDisplayProfileName(const QString & name)623 void Document::setColorDisplayProfileName(const QString &name)
624 {
625 #if defined(USE_CMS)
626     void *rawprofile = cmsOpenProfileFromFile(name.toLocal8Bit().constData(), "r");
627     m_doc->m_displayProfile = make_GfxLCMSProfilePtr(rawprofile);
628 #else
629     Q_UNUSED(name);
630 #endif
631 }
632 
colorRgbProfile() const633 void *Document::colorRgbProfile() const
634 {
635 #if defined(USE_CMS)
636     if (!m_doc->m_sRGBProfile) {
637         m_doc->m_sRGBProfile = make_GfxLCMSProfilePtr(cmsCreate_sRGBProfile());
638     }
639     return m_doc->m_sRGBProfile.get();
640 #else
641     return nullptr;
642 #endif
643 }
644 
colorDisplayProfile() const645 void *Document::colorDisplayProfile() const
646 {
647 #if defined(USE_CMS)
648     return m_doc->m_displayProfile.get();
649 #else
650     return nullptr;
651 #endif
652 }
653 
paperColor() const654 QColor Document::paperColor() const
655 {
656     return m_doc->paperColor;
657 }
658 
setRenderBackend(Document::RenderBackend backend)659 void Document::setRenderBackend(Document::RenderBackend backend)
660 {
661     // no need to delete the outputdev as for the moment we always create a splash one
662     // as the QPainter one does not allow "precaching" due to its signature
663     // delete m_doc->m_outputDev;
664     // m_doc->m_outputDev = NULL;
665     m_doc->m_backend = backend;
666 }
667 
renderBackend() const668 Document::RenderBackend Document::renderBackend() const
669 {
670     return m_doc->m_backend;
671 }
672 
availableRenderBackends()673 QSet<Document::RenderBackend> Document::availableRenderBackends()
674 {
675     QSet<Document::RenderBackend> ret;
676     ret << Document::SplashBackend;
677     ret << Document::QPainterBackend;
678     return ret;
679 }
680 
setRenderHint(Document::RenderHint hint,bool on)681 void Document::setRenderHint(Document::RenderHint hint, bool on)
682 {
683     const bool touchesOverprinting = hint & Document::OverprintPreview;
684 
685     int hintForOperation = hint;
686     if (touchesOverprinting && !isOverprintPreviewAvailable())
687         hintForOperation = hintForOperation & ~(int)Document::OverprintPreview;
688 
689     if (on)
690         m_doc->m_hints |= hintForOperation;
691     else
692         m_doc->m_hints &= ~hintForOperation;
693 }
694 
renderHints() const695 Document::RenderHints Document::renderHints() const
696 {
697     return Document::RenderHints(m_doc->m_hints);
698 }
699 
psConverter() const700 std::unique_ptr<PSConverter> Document::psConverter() const
701 {
702     // Cannot use std::make_unique, because the PSConverter constructor is private
703     return std::unique_ptr<PSConverter>(new PSConverter(m_doc));
704 }
705 
pdfConverter() const706 std::unique_ptr<PDFConverter> Document::pdfConverter() const
707 {
708     // Cannot use std::make_unique, because the PDFConverter constructor is private
709     return std::unique_ptr<PDFConverter>(new PDFConverter(m_doc));
710 }
711 
metadata() const712 QString Document::metadata() const
713 {
714     QString result;
715     Catalog *catalog = m_doc->doc->getCatalog();
716     if (catalog && catalog->isOk()) {
717         std::unique_ptr<GooString> s = catalog->readMetadata();
718         if (s)
719             result = UnicodeParsedString(s.get());
720     }
721     return result;
722 }
723 
hasOptionalContent() const724 bool Document::hasOptionalContent() const
725 {
726     return (m_doc->doc->getOptContentConfig() && m_doc->doc->getOptContentConfig()->hasOCGs());
727 }
728 
optionalContentModel()729 OptContentModel *Document::optionalContentModel()
730 {
731     if (m_doc->m_optContentModel.isNull()) {
732         m_doc->m_optContentModel = new OptContentModel(m_doc->doc->getOptContentConfig(), nullptr);
733     }
734     return (OptContentModel *)m_doc->m_optContentModel;
735 }
736 
scripts() const737 QStringList Document::scripts() const
738 {
739     Catalog *catalog = m_doc->doc->getCatalog();
740     const int numScripts = catalog->numJS();
741     QStringList scripts;
742     for (int i = 0; i < numScripts; ++i) {
743         GooString *s = catalog->getJS(i);
744         if (s) {
745             scripts.append(UnicodeParsedString(s));
746             delete s;
747         }
748     }
749     return scripts;
750 }
751 
getPdfId(QByteArray * permanentId,QByteArray * updateId) const752 bool Document::getPdfId(QByteArray *permanentId, QByteArray *updateId) const
753 {
754     GooString gooPermanentId;
755     GooString gooUpdateId;
756 
757     if (!m_doc->doc->getID(permanentId ? &gooPermanentId : nullptr, updateId ? &gooUpdateId : nullptr))
758         return false;
759 
760     if (permanentId)
761         *permanentId = gooPermanentId.c_str();
762     if (updateId)
763         *updateId = gooUpdateId.c_str();
764 
765     return true;
766 }
767 
formType() const768 Document::FormType Document::formType() const
769 {
770     switch (m_doc->doc->getCatalog()->getFormType()) {
771     case Catalog::NoForm:
772         return Document::NoForm;
773     case Catalog::AcroForm:
774         return Document::AcroForm;
775     case Catalog::XfaForm:
776         return Document::XfaForm;
777     }
778 
779     return Document::NoForm; // make gcc happy
780 }
781 
formCalculateOrder() const782 QVector<int> Document::formCalculateOrder() const
783 {
784     Form *form = m_doc->doc->getCatalog()->getForm();
785     if (!form) {
786         return {};
787     }
788 
789     QVector<int> result;
790     const std::vector<Ref> &calculateOrder = form->getCalculateOrder();
791     for (Ref r : calculateOrder) {
792         FormWidget *w = form->findWidgetByRef(r);
793         if (w) {
794             result << w->getID();
795         }
796     }
797 
798     return result;
799 }
800 
signatures() const801 std::vector<std::unique_ptr<FormFieldSignature>> Document::signatures() const
802 {
803     std::vector<std::unique_ptr<FormFieldSignature>> result;
804 
805     const std::vector<::FormFieldSignature *> pSignatures = m_doc->doc->getSignatureFields();
806 
807     for (::FormFieldSignature *pSignature : pSignatures) {
808         ::FormWidget *fw = pSignature->getCreateWidget();
809         ::Page *p = m_doc->doc->getPage(fw->getWidgetAnnotation()->getPageNum());
810         result.push_back(std::make_unique<FormFieldSignature>(m_doc, p, static_cast<FormWidgetSignature *>(fw)));
811     }
812 
813     return result;
814 }
815 
xrefWasReconstructed() const816 bool Document::xrefWasReconstructed() const
817 {
818     return m_doc->xrefReconstructed;
819 }
820 
setXRefReconstructedCallback(const std::function<void ()> & callback)821 void Document::setXRefReconstructedCallback(const std::function<void()> &callback)
822 {
823     m_doc->xrefReconstructedCallback = callback;
824 }
825 
convertDate(const char * dateString)826 QDateTime convertDate(const char *dateString)
827 {
828     int year, mon, day, hour, min, sec, tzHours, tzMins;
829     char tz;
830 
831     GooString date(dateString);
832     if (parseDateString(&date, &year, &mon, &day, &hour, &min, &sec, &tz, &tzHours, &tzMins)) {
833         QDate d(year, mon, day);
834         QTime t(hour, min, sec);
835         if (d.isValid() && t.isValid()) {
836             QDateTime dt(d, t, Qt::UTC);
837             if (tz) {
838                 // then we have some form of timezone
839                 if ('Z' == tz) {
840                     // We are already at UTC
841                 } else if ('+' == tz) {
842                     // local time is ahead of UTC
843                     dt = dt.addSecs(-1 * ((tzHours * 60) + tzMins) * 60);
844                 } else if ('-' == tz) {
845                     // local time is behind UTC
846                     dt = dt.addSecs(((tzHours * 60) + tzMins) * 60);
847                 } else {
848                     qWarning("unexpected tz val");
849                 }
850             }
851             return dt;
852         }
853     }
854     return QDateTime();
855 }
856 
isCmsAvailable()857 bool isCmsAvailable()
858 {
859 #if defined(USE_CMS)
860     return true;
861 #else
862     return false;
863 #endif
864 }
865 
isOverprintPreviewAvailable()866 bool isOverprintPreviewAvailable()
867 {
868     return true;
869 }
870 
871 }
872