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-2022, 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
66namespace Poppler {
67
68std::unique_ptr<Document> Document::load(const QString &filePath, const QByteArray &ownerPassword, const QByteArray &userPassword)
69{
70 DocumentData *doc = new DocumentData(filePath, GooString(ownerPassword.data()), GooString(userPassword.data()));
71 return DocumentData::checkDocument(doc);
72}
73
74std::unique_ptr<Document> Document::load(QIODevice *device, const QByteArray &ownerPassword, const QByteArray &userPassword)
75{
76 DocumentData *doc = new DocumentData(device, GooString(ownerPassword.data()), GooString(userPassword.data()));
77 return DocumentData::checkDocument(doc);
78}
79
80std::unique_ptr<Document> Document::loadFromData(const QByteArray &fileContents, const QByteArray &ownerPassword, const QByteArray &userPassword)
81{
82 // create stream
83 DocumentData *doc = new DocumentData(fileContents, GooString(ownerPassword.data()), GooString(userPassword.data()));
84 return DocumentData::checkDocument(doc);
85}
86
87std::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
104Document::Document(DocumentData *dataA)
105{
106 m_doc = dataA;
107}
108
109Document::~Document()
110{
111 delete m_doc;
112}
113
114std::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
125bool Document::isLocked() const
126{
127 return m_doc->locked;
128}
129
130bool 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, GooString(ownerPassword.data()), GooString(userPassword.data()));
137 } else if (m_doc->m_device) {
138 doc2 = new DocumentData(m_doc->m_device, GooString(ownerPassword.data()), GooString(userPassword.data()));
139 } else {
140 doc2 = new DocumentData(m_doc->m_filePath, GooString(ownerPassword.data()), 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
154Document::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
174Document::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
196Qt::LayoutDirection Document::textDirection() const
197{
198 if (!m_doc->doc->getCatalog()->getViewerPreferences()) {
199 return Qt::LayoutDirectionAuto;
200 }
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
212int Document::numPages() const
213{
214 return m_doc->doc->getNumPages();
215}
216
217QList<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
227QList<EmbeddedFile *> Document::embeddedFiles() const
228{
229 return m_doc->m_embeddedFiles;
230}
231
232std::unique_ptr<FontIterator> Document::newFontIterator(int startPage) const
233{
234 // Cannot use std::make_unique, because the FontIterator constructor is private
235 return std::unique_ptr<FontIterator>(new FontIterator(startPage, m_doc));
236}
237
238QByteArray Document::fontData(const FontInfo &fi) const
239{
240 QByteArray result;
241 if (fi.isEmbedded()) {
242 XRef *xref = m_doc->doc->getXRef()->copy();
243
244 Object refObj(fi.m_data->embRef);
245 Object strObj = refObj.fetch(xref);
246 if (strObj.isStream()) {
247 int c;
248 strObj.streamReset();
249 while ((c = strObj.streamGetChar()) != EOF) {
250 result.append(c: (char)c);
251 }
252 strObj.streamClose();
253 }
254 delete xref;
255 }
256 return result;
257}
258
259QString Document::info(const QString &type) const
260{
261 if (m_doc->locked) {
262 return QString();
263 }
264
265 std::unique_ptr<GooString> goo(m_doc->doc->getDocInfoStringEntry(key: type.toLatin1().constData()));
266 return UnicodeParsedString(s1: goo.get());
267}
268
269bool Document::setInfo(const QString &key, const QString &val)
270{
271 if (m_doc->locked) {
272 return false;
273 }
274
275 GooString *goo = QStringToUnicodeGooString(s: val);
276 m_doc->doc->setDocInfoStringEntry(key: key.toLatin1().constData(), value: goo);
277 return true;
278}
279
280QString Document::title() const
281{
282 if (m_doc->locked) {
283 return QString();
284 }
285
286 std::unique_ptr<GooString> goo(m_doc->doc->getDocInfoTitle());
287 return UnicodeParsedString(s1: goo.get());
288}
289
290bool Document::setTitle(const QString &val)
291{
292 if (m_doc->locked) {
293 return false;
294 }
295
296 m_doc->doc->setDocInfoTitle(QStringToUnicodeGooString(s: val));
297 return true;
298}
299
300QString Document::author() const
301{
302 if (m_doc->locked) {
303 return QString();
304 }
305
306 std::unique_ptr<GooString> goo(m_doc->doc->getDocInfoAuthor());
307 return UnicodeParsedString(s1: goo.get());
308}
309
310bool Document::setAuthor(const QString &val)
311{
312 if (m_doc->locked) {
313 return false;
314 }
315
316 m_doc->doc->setDocInfoAuthor(QStringToUnicodeGooString(s: val));
317 return true;
318}
319
320QString Document::subject() const
321{
322 if (m_doc->locked) {
323 return QString();
324 }
325
326 std::unique_ptr<GooString> goo(m_doc->doc->getDocInfoSubject());
327 return UnicodeParsedString(s1: goo.get());
328}
329
330bool Document::setSubject(const QString &val)
331{
332 if (m_doc->locked) {
333 return false;
334 }
335
336 m_doc->doc->setDocInfoSubject(QStringToUnicodeGooString(s: val));
337 return true;
338}
339
340QString Document::keywords() const
341{
342 if (m_doc->locked) {
343 return QString();
344 }
345
346 std::unique_ptr<GooString> goo(m_doc->doc->getDocInfoKeywords());
347 return UnicodeParsedString(s1: goo.get());
348}
349
350bool Document::setKeywords(const QString &val)
351{
352 if (m_doc->locked) {
353 return false;
354 }
355
356 m_doc->doc->setDocInfoKeywords(QStringToUnicodeGooString(s: val));
357 return true;
358}
359
360QString Document::creator() const
361{
362 if (m_doc->locked) {
363 return QString();
364 }
365
366 std::unique_ptr<GooString> goo(m_doc->doc->getDocInfoCreator());
367 return UnicodeParsedString(s1: goo.get());
368}
369
370bool Document::setCreator(const QString &val)
371{
372 if (m_doc->locked) {
373 return false;
374 }
375
376 m_doc->doc->setDocInfoCreator(QStringToUnicodeGooString(s: val));
377 return true;
378}
379
380QString Document::producer() const
381{
382 if (m_doc->locked) {
383 return QString();
384 }
385
386 std::unique_ptr<GooString> goo(m_doc->doc->getDocInfoProducer());
387 return UnicodeParsedString(s1: goo.get());
388}
389
390bool Document::setProducer(const QString &val)
391{
392 if (m_doc->locked) {
393 return false;
394 }
395
396 m_doc->doc->setDocInfoProducer(QStringToUnicodeGooString(s: val));
397 return true;
398}
399
400bool Document::removeInfo()
401{
402 if (m_doc->locked) {
403 return false;
404 }
405
406 m_doc->doc->removeDocInfo();
407 return true;
408}
409
410QStringList Document::infoKeys() const
411{
412 QStringList keys;
413
414 if (m_doc->locked) {
415 return QStringList();
416 }
417
418 QScopedPointer<XRef> xref(m_doc->doc->getXRef()->copy());
419 if (!xref) {
420 return QStringList();
421 }
422 Object info = xref->getDocInfo();
423 if (!info.isDict()) {
424 return QStringList();
425 }
426
427 Dict *infoDict = info.getDict();
428 // somehow iterate over keys in infoDict
429 keys.reserve(asize: infoDict->getLength());
430 for (int i = 0; i < infoDict->getLength(); ++i) {
431 keys.append(t: QString::fromLatin1(ba: infoDict->getKey(i)));
432 }
433
434 return keys;
435}
436
437QDateTime Document::date(const QString &type) const
438{
439 if (m_doc->locked) {
440 return QDateTime();
441 }
442
443 std::unique_ptr<GooString> goo(m_doc->doc->getDocInfoStringEntry(key: type.toLatin1().constData()));
444 QString str = UnicodeParsedString(s1: goo.get());
445 return Poppler::convertDate(dateString: str.toLatin1().constData());
446}
447
448bool Document::setDate(const QString &key, const QDateTime &val)
449{
450 if (m_doc->locked) {
451 return false;
452 }
453
454 m_doc->doc->setDocInfoStringEntry(key: key.toLatin1().constData(), value: QDateTimeToUnicodeGooString(dt: val));
455 return true;
456}
457
458QDateTime Document::creationDate() const
459{
460 if (m_doc->locked) {
461 return QDateTime();
462 }
463
464 std::unique_ptr<GooString> goo(m_doc->doc->getDocInfoCreatDate());
465 QString str = UnicodeParsedString(s1: goo.get());
466 return Poppler::convertDate(dateString: str.toLatin1().constData());
467}
468
469bool Document::setCreationDate(const QDateTime &val)
470{
471 if (m_doc->locked) {
472 return false;
473 }
474
475 m_doc->doc->setDocInfoCreatDate(QDateTimeToUnicodeGooString(dt: val));
476 return true;
477}
478
479QDateTime Document::modificationDate() const
480{
481 if (m_doc->locked) {
482 return QDateTime();
483 }
484
485 std::unique_ptr<GooString> goo(m_doc->doc->getDocInfoModDate());
486 QString str = UnicodeParsedString(s1: goo.get());
487 return Poppler::convertDate(dateString: str.toLatin1().constData());
488}
489
490bool Document::setModificationDate(const QDateTime &val)
491{
492 if (m_doc->locked) {
493 return false;
494 }
495
496 m_doc->doc->setDocInfoModDate(QDateTimeToUnicodeGooString(dt: val));
497 return true;
498}
499
500bool Document::isEncrypted() const
501{
502 return m_doc->doc->isEncrypted();
503}
504
505bool Document::isLinearized() const
506{
507 return m_doc->doc->isLinearized();
508}
509
510bool Document::okToPrint() const
511{
512 return m_doc->doc->okToPrint();
513}
514
515bool Document::okToPrintHighRes() const
516{
517 return m_doc->doc->okToPrintHighRes();
518}
519
520bool Document::okToChange() const
521{
522 return m_doc->doc->okToChange();
523}
524
525bool Document::okToCopy() const
526{
527 return m_doc->doc->okToCopy();
528}
529
530bool Document::okToAddNotes() const
531{
532 return m_doc->doc->okToAddNotes();
533}
534
535bool Document::okToFillForm() const
536{
537 return m_doc->doc->okToFillForm();
538}
539
540bool Document::okToCreateFormFields() const
541{
542 return (okToFillForm() && okToChange());
543}
544
545bool Document::okToExtractForAccessibility() const
546{
547 return m_doc->doc->okToAccessibility();
548}
549
550bool Document::okToAssemble() const
551{
552 return m_doc->doc->okToAssemble();
553}
554
555Document::PdfVersion Document::getPdfVersion() const
556{
557 return PdfVersion { .major: m_doc->doc->getPDFMajorVersion(), .minor: m_doc->doc->getPDFMinorVersion() };
558}
559
560std::unique_ptr<Page> Document::page(const QString &label) const
561{
562 GooString label_g(label.toLatin1().data());
563 int index;
564
565 if (!m_doc->doc->getCatalog()->labelToIndex(label: &label_g, index: &index)) {
566 std::unique_ptr<GooString> label_ug(QStringToUnicodeGooString(s: label));
567 if (!m_doc->doc->getCatalog()->labelToIndex(label: label_ug.get(), index: &index)) {
568 return nullptr;
569 }
570 }
571
572 return page(index);
573}
574
575bool Document::hasEmbeddedFiles() const
576{
577 return (!(0 == m_doc->doc->getCatalog()->numEmbeddedFiles()));
578}
579
580QVector<OutlineItem> Document::outline() const
581{
582 QVector<OutlineItem> result;
583
584 if (::Outline *outline = m_doc->doc->getOutline()) {
585 if (const std::vector<::OutlineItem *> *items = outline->getItems()) {
586 for (void *item : *items) {
587 result.push_back(t: OutlineItem { new OutlineItemData { static_cast<::OutlineItem *>(item), m_doc } });
588 }
589 }
590 }
591
592 return result;
593}
594
595std::unique_ptr<LinkDestination> Document::linkDestination(const QString &name)
596{
597 GooString *namedDest = QStringToGooString(s: name);
598 LinkDestinationData ldd(nullptr, namedDest, m_doc, false);
599 auto ld = std::make_unique<LinkDestination>(args&: ldd);
600 delete namedDest;
601 return ld;
602}
603
604void Document::setPaperColor(const QColor &color)
605{
606 m_doc->setPaperColor(color);
607}
608
609void Document::setColorDisplayProfile(void *outputProfileA)
610{
611#if defined(USE_CMS)
612 if (m_doc->m_sRGBProfile && m_doc->m_sRGBProfile.get() == outputProfileA) {
613 // Catch the special case that the user passes the sRGB profile
614 m_doc->m_displayProfile = m_doc->m_sRGBProfile;
615 return;
616 }
617 if (m_doc->m_displayProfile && m_doc->m_displayProfile.get() == outputProfileA) {
618 // Catch the special case that the user passes the display profile
619 return;
620 }
621 m_doc->m_displayProfile = make_GfxLCMSProfilePtr(outputProfileA);
622#else
623 Q_UNUSED(outputProfileA);
624#endif
625}
626
627void Document::setColorDisplayProfileName(const QString &name)
628{
629#if defined(USE_CMS)
630 void *rawprofile = cmsOpenProfileFromFile(name.toLocal8Bit().constData(), "r");
631 m_doc->m_displayProfile = make_GfxLCMSProfilePtr(rawprofile);
632#else
633 Q_UNUSED(name);
634#endif
635}
636
637void *Document::colorRgbProfile() const
638{
639#if defined(USE_CMS)
640 if (!m_doc->m_sRGBProfile) {
641 m_doc->m_sRGBProfile = make_GfxLCMSProfilePtr(cmsCreate_sRGBProfile());
642 }
643 return m_doc->m_sRGBProfile.get();
644#else
645 return nullptr;
646#endif
647}
648
649void *Document::colorDisplayProfile() const
650{
651#if defined(USE_CMS)
652 return m_doc->m_displayProfile.get();
653#else
654 return nullptr;
655#endif
656}
657
658QColor Document::paperColor() const
659{
660 return m_doc->paperColor;
661}
662
663void Document::setRenderBackend(Document::RenderBackend backend)
664{
665 // no need to delete the outputdev as for the moment we always create a splash one
666 // as the QPainter one does not allow "precaching" due to its signature
667 // delete m_doc->m_outputDev;
668 // m_doc->m_outputDev = NULL;
669 m_doc->m_backend = backend;
670}
671
672Document::RenderBackend Document::renderBackend() const
673{
674 return m_doc->m_backend;
675}
676
677QSet<Document::RenderBackend> Document::availableRenderBackends()
678{
679 QSet<Document::RenderBackend> ret;
680 ret << Document::SplashBackend;
681 ret << Document::QPainterBackend;
682 return ret;
683}
684
685void Document::setRenderHint(Document::RenderHint hint, bool on)
686{
687 const bool touchesOverprinting = hint & Document::OverprintPreview;
688
689 int hintForOperation = hint;
690 if (touchesOverprinting && !isOverprintPreviewAvailable()) {
691 hintForOperation = hintForOperation & ~(int)Document::OverprintPreview;
692 }
693
694 if (on) {
695 m_doc->m_hints |= hintForOperation;
696 } else {
697 m_doc->m_hints &= ~hintForOperation;
698 }
699}
700
701Document::RenderHints Document::renderHints() const
702{
703 return Document::RenderHints(m_doc->m_hints);
704}
705
706std::unique_ptr<PSConverter> Document::psConverter() const
707{
708 // Cannot use std::make_unique, because the PSConverter constructor is private
709 return std::unique_ptr<PSConverter>(new PSConverter(m_doc));
710}
711
712std::unique_ptr<PDFConverter> Document::pdfConverter() const
713{
714 // Cannot use std::make_unique, because the PDFConverter constructor is private
715 return std::unique_ptr<PDFConverter>(new PDFConverter(m_doc));
716}
717
718QString Document::metadata() const
719{
720 QString result;
721 Catalog *catalog = m_doc->doc->getCatalog();
722 if (catalog && catalog->isOk()) {
723 std::unique_ptr<GooString> s = catalog->readMetadata();
724 if (s) {
725 result = UnicodeParsedString(s1: s.get());
726 }
727 }
728 return result;
729}
730
731bool Document::hasOptionalContent() const
732{
733 return (m_doc->doc->getOptContentConfig() && m_doc->doc->getOptContentConfig()->hasOCGs());
734}
735
736OptContentModel *Document::optionalContentModel()
737{
738 if (m_doc->m_optContentModel.isNull()) {
739 m_doc->m_optContentModel = new OptContentModel(m_doc->doc->getOptContentConfig(), nullptr);
740 }
741 return (OptContentModel *)m_doc->m_optContentModel;
742}
743
744QStringList Document::scripts() const
745{
746 Catalog *catalog = m_doc->doc->getCatalog();
747 const int numScripts = catalog->numJS();
748 QStringList scripts;
749 for (int i = 0; i < numScripts; ++i) {
750 GooString *s = catalog->getJS(i);
751 if (s) {
752 scripts.append(t: UnicodeParsedString(s1: s));
753 delete s;
754 }
755 }
756 return scripts;
757}
758
759bool Document::getPdfId(QByteArray *permanentId, QByteArray *updateId) const
760{
761 GooString gooPermanentId;
762 GooString gooUpdateId;
763
764 if (!m_doc->doc->getID(permanent_id: permanentId ? &gooPermanentId : nullptr, update_id: updateId ? &gooUpdateId : nullptr)) {
765 return false;
766 }
767
768 if (permanentId) {
769 *permanentId = gooPermanentId.c_str();
770 }
771 if (updateId) {
772 *updateId = gooUpdateId.c_str();
773 }
774
775 return true;
776}
777
778Document::FormType Document::formType() const
779{
780 switch (m_doc->doc->getCatalog()->getFormType()) {
781 case Catalog::NoForm:
782 return Document::NoForm;
783 case Catalog::AcroForm:
784 return Document::AcroForm;
785 case Catalog::XfaForm:
786 return Document::XfaForm;
787 }
788
789 return Document::NoForm; // make gcc happy
790}
791
792QVector<int> Document::formCalculateOrder() const
793{
794 Form *form = m_doc->doc->getCatalog()->getForm();
795 if (!form) {
796 return {};
797 }
798
799 QVector<int> result;
800 const std::vector<Ref> &calculateOrder = form->getCalculateOrder();
801 for (Ref r : calculateOrder) {
802 FormWidget *w = form->findWidgetByRef(aref: r);
803 if (w) {
804 result << w->getID();
805 }
806 }
807
808 return result;
809}
810
811std::vector<std::unique_ptr<FormFieldSignature>> Document::signatures() const
812{
813 std::vector<std::unique_ptr<FormFieldSignature>> result;
814
815 const std::vector<::FormFieldSignature *> pSignatures = m_doc->doc->getSignatureFields();
816
817 for (::FormFieldSignature *pSignature : pSignatures) {
818 ::FormWidget *fw = pSignature->getCreateWidget();
819 ::Page *p = m_doc->doc->getPage(page: fw->getWidgetAnnotation()->getPageNum());
820 result.push_back(x: std::make_unique<FormFieldSignature>(args: m_doc, args&: p, args: static_cast<FormWidgetSignature *>(fw)));
821 }
822
823 return result;
824}
825
826bool Document::xrefWasReconstructed() const
827{
828 return m_doc->xrefReconstructed;
829}
830
831void Document::setXRefReconstructedCallback(const std::function<void()> &callback)
832{
833 m_doc->xrefReconstructedCallback = callback;
834}
835
836QDateTime convertDate(const char *dateString)
837{
838 int year, mon, day, hour, min, sec, tzHours, tzMins;
839 char tz;
840
841 GooString date(dateString);
842 if (parseDateString(date: &date, year: &year, month: &mon, day: &day, hour: &hour, minute: &min, second: &sec, tz: &tz, tzHour: &tzHours, tzMinute: &tzMins)) {
843 QDate d(year, mon, day);
844 QTime t(hour, min, sec);
845 if (d.isValid() && t.isValid()) {
846 QDateTime dt(d, t, Qt::UTC);
847 if (tz) {
848 // then we have some form of timezone
849 if ('Z' == tz) {
850 // We are already at UTC
851 } else if ('+' == tz) {
852 // local time is ahead of UTC
853 dt = dt.addSecs(secs: -1 * ((tzHours * 60) + tzMins) * 60);
854 } else if ('-' == tz) {
855 // local time is behind UTC
856 dt = dt.addSecs(secs: ((tzHours * 60) + tzMins) * 60);
857 } else {
858 qWarning(msg: "unexpected tz val");
859 }
860 }
861 return dt;
862 }
863 }
864 return QDateTime();
865}
866
867bool isCmsAvailable()
868{
869#if defined(USE_CMS)
870 return true;
871#else
872 return false;
873#endif
874}
875
876bool isOverprintPreviewAvailable()
877{
878 return true;
879}
880
881}
882

source code of poppler/qt6/src/poppler-document.cc