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