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