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 | |
66 | namespace Poppler { |
67 | |
68 | std::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 | |
74 | std::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 | |
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, GooString(ownerPassword.data()), GooString(userPassword.data())); |
84 | return DocumentData::checkDocument(doc); |
85 | } |
86 | |
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 | |
104 | Document::Document(DocumentData *dataA) |
105 | { |
106 | m_doc = dataA; |
107 | } |
108 | |
109 | Document::~Document() |
110 | { |
111 | delete m_doc; |
112 | } |
113 | |
114 | 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 | |
125 | bool Document::isLocked() const |
126 | { |
127 | return m_doc->locked; |
128 | } |
129 | |
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, 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 | |
154 | 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 | |
174 | 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 | |
196 | Qt::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 | |
212 | int Document::numPages() const |
213 | { |
214 | return m_doc->doc->getNumPages(); |
215 | } |
216 | |
217 | 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 | |
227 | QList<EmbeddedFile *> Document::embeddedFiles() const |
228 | { |
229 | return m_doc->m_embeddedFiles; |
230 | } |
231 | |
232 | std::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 | |
238 | QByteArray 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 | |
259 | QString 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 | |
269 | bool 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 | |
280 | QString 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 | |
290 | bool 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 | |
300 | QString 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 | |
310 | bool 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 | |
320 | QString 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 | |
330 | bool 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 | |
340 | QString 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 | |
350 | bool 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 | |
360 | QString 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 | |
370 | bool 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 | |
380 | QString 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 | |
390 | bool 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 | |
400 | bool Document::removeInfo() |
401 | { |
402 | if (m_doc->locked) { |
403 | return false; |
404 | } |
405 | |
406 | m_doc->doc->removeDocInfo(); |
407 | return true; |
408 | } |
409 | |
410 | QStringList 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 | |
437 | QDateTime 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 | |
448 | bool 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 | |
458 | QDateTime 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 | |
469 | bool 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 | |
479 | QDateTime 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 | |
490 | bool 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 | |
500 | bool Document::isEncrypted() const |
501 | { |
502 | return m_doc->doc->isEncrypted(); |
503 | } |
504 | |
505 | bool Document::isLinearized() const |
506 | { |
507 | return m_doc->doc->isLinearized(); |
508 | } |
509 | |
510 | bool Document::okToPrint() const |
511 | { |
512 | return m_doc->doc->okToPrint(); |
513 | } |
514 | |
515 | bool Document::okToPrintHighRes() const |
516 | { |
517 | return m_doc->doc->okToPrintHighRes(); |
518 | } |
519 | |
520 | bool Document::okToChange() const |
521 | { |
522 | return m_doc->doc->okToChange(); |
523 | } |
524 | |
525 | bool Document::okToCopy() const |
526 | { |
527 | return m_doc->doc->okToCopy(); |
528 | } |
529 | |
530 | bool Document::okToAddNotes() const |
531 | { |
532 | return m_doc->doc->okToAddNotes(); |
533 | } |
534 | |
535 | bool Document::okToFillForm() const |
536 | { |
537 | return m_doc->doc->okToFillForm(); |
538 | } |
539 | |
540 | bool Document::okToCreateFormFields() const |
541 | { |
542 | return (okToFillForm() && okToChange()); |
543 | } |
544 | |
545 | bool Document::() const |
546 | { |
547 | return m_doc->doc->okToAccessibility(); |
548 | } |
549 | |
550 | bool Document::okToAssemble() const |
551 | { |
552 | return m_doc->doc->okToAssemble(); |
553 | } |
554 | |
555 | Document::PdfVersion Document::getPdfVersion() const |
556 | { |
557 | return PdfVersion { .major: m_doc->doc->getPDFMajorVersion(), .minor: m_doc->doc->getPDFMinorVersion() }; |
558 | } |
559 | |
560 | std::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 | |
575 | bool Document::hasEmbeddedFiles() const |
576 | { |
577 | return (!(0 == m_doc->doc->getCatalog()->numEmbeddedFiles())); |
578 | } |
579 | |
580 | QVector<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 | |
595 | std::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 | |
604 | void Document::setPaperColor(const QColor &color) |
605 | { |
606 | m_doc->setPaperColor(color); |
607 | } |
608 | |
609 | void 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 | |
627 | void 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 | |
637 | void *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 | |
649 | void *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 | |
658 | QColor Document::paperColor() const |
659 | { |
660 | return m_doc->paperColor; |
661 | } |
662 | |
663 | void 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 | |
672 | Document::RenderBackend Document::renderBackend() const |
673 | { |
674 | return m_doc->m_backend; |
675 | } |
676 | |
677 | QSet<Document::RenderBackend> Document::availableRenderBackends() |
678 | { |
679 | QSet<Document::RenderBackend> ret; |
680 | ret << Document::SplashBackend; |
681 | ret << Document::QPainterBackend; |
682 | return ret; |
683 | } |
684 | |
685 | void 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 | |
701 | Document::RenderHints Document::renderHints() const |
702 | { |
703 | return Document::RenderHints(m_doc->m_hints); |
704 | } |
705 | |
706 | std::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 | |
712 | std::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 | |
718 | QString 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 | |
731 | bool Document::hasOptionalContent() const |
732 | { |
733 | return (m_doc->doc->getOptContentConfig() && m_doc->doc->getOptContentConfig()->hasOCGs()); |
734 | } |
735 | |
736 | OptContentModel *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 | |
744 | QStringList 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 | |
759 | bool 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 | |
778 | Document::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 | |
792 | QVector<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 | |
811 | std::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 | |
826 | bool Document::xrefWasReconstructed() const |
827 | { |
828 | return m_doc->xrefReconstructed; |
829 | } |
830 | |
831 | void Document::setXRefReconstructedCallback(const std::function<void()> &callback) |
832 | { |
833 | m_doc->xrefReconstructedCallback = callback; |
834 | } |
835 | |
836 | QDateTime 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 | |
867 | bool isCmsAvailable() |
868 | { |
869 | #if defined(USE_CMS) |
870 | return true; |
871 | #else |
872 | return false; |
873 | #endif |
874 | } |
875 | |
876 | bool isOverprintPreviewAvailable() |
877 | { |
878 | return true; |
879 | } |
880 | |
881 | } |
882 | |