1/*
2 * Copyright (C) 2009-2011, Pino Toscano <pino@kde.org>
3 * Copyright (C) 2016 Jakub Alba <jakubalba@gmail.com>
4 * Copyright (C) 2017, 2022, Albert Astals Cid <aacid@kde.org>
5 * Copyright (C) 2018, 2020, Adam Reichold <adam.reichold@t-online.de>
6 * Copyright (C) 2019, Masamichi Hosoda <trueroad@trueroad.jp>
7 * Copyright (C) 2019, 2020, Oliver Sander <oliver.sander@tu-dresden.de>
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2, or (at your option)
12 * any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
22 */
23
24/**
25 \file poppler-document.h
26 */
27#include "poppler-destination.h"
28#include "poppler-document.h"
29#include "poppler-embedded-file.h"
30#include "poppler-page.h"
31#include "poppler-toc.h"
32
33#include "poppler-destination-private.h"
34#include "poppler-document-private.h"
35#include "poppler-embedded-file-private.h"
36#include "poppler-page-private.h"
37#include "poppler-private.h"
38#include "poppler-toc-private.h"
39
40#include "Catalog.h"
41#include "DateInfo.h"
42#include "ErrorCodes.h"
43#include "GlobalParams.h"
44#include "Link.h"
45#include "Outline.h"
46
47#include <algorithm>
48#include <iterator>
49#include <memory>
50
51using namespace poppler;
52
53document_private::document_private(std::unique_ptr<GooString> &&file_path, const std::string &owner_password, const std::string &user_password) : document_private()
54{
55 doc = new PDFDoc(std::move(file_path), GooString(owner_password.c_str()), GooString(user_password.c_str()));
56}
57
58document_private::document_private(byte_array *file_data, const std::string &owner_password, const std::string &user_password) : document_private()
59{
60 file_data->swap(x&: doc_data);
61 MemStream *memstr = new MemStream(&doc_data[0], 0, doc_data.size(), Object(objNull));
62 doc = new PDFDoc(memstr, GooString(owner_password.c_str()), GooString(user_password.c_str()));
63}
64
65document_private::document_private(const char *file_data, int file_data_length, const std::string &owner_password, const std::string &user_password) : document_private()
66{
67 raw_doc_data = file_data;
68 raw_doc_data_length = file_data_length;
69 MemStream *memstr = new MemStream(raw_doc_data, 0, raw_doc_data_length, Object(objNull));
70 doc = new PDFDoc(memstr, GooString(owner_password.c_str()), GooString(user_password.c_str()));
71}
72
73document_private::document_private() : GlobalParamsIniter(detail::error_function), doc(nullptr), raw_doc_data(nullptr), raw_doc_data_length(0), is_locked(false) { }
74
75document_private::~document_private()
76{
77 delete_all(c: embedded_files);
78
79 delete doc;
80}
81
82document *document_private::check_document(document_private *doc, byte_array *file_data)
83{
84 if (doc->doc->isOk() || doc->doc->getErrorCode() == errEncrypted) {
85 if (doc->doc->getErrorCode() == errEncrypted) {
86 doc->is_locked = true;
87 }
88 return new document(*doc);
89 } else {
90 // put back the document data where it was before
91 if (file_data) {
92 file_data->swap(x&: doc->doc_data);
93 }
94 delete doc;
95 }
96 return nullptr;
97}
98
99/**
100 \class poppler::document poppler-document.h "poppler/cpp/poppler-document.h"
101
102 Represents a PDF %document.
103 */
104
105/**
106 \enum poppler::document::page_mode_enum
107
108 The various page modes available in a PDF %document.
109*/
110/**
111 \var poppler::document::page_mode_enum poppler::document::use_none
112
113 The %document specifies no particular page mode.
114*/
115/**
116 \var poppler::document::page_mode_enum poppler::document::use_outlines
117
118 The %document specifies its TOC (table of contents) should be open.
119*/
120/**
121 \var poppler::document::page_mode_enum poppler::document::use_thumbs
122
123 The %document specifies that should be open a view of the thumbnails of its
124 pages.
125*/
126/**
127 \var poppler::document::page_mode_enum poppler::document::fullscreen
128
129 The %document specifies it wants to be open in a fullscreen mode.
130*/
131/**
132 \var poppler::document::page_mode_enum poppler::document::use_oc
133
134 The %document specifies that should be open a view of its Optional Content
135 (also known as layers).
136*/
137/**
138 \var poppler::document::page_mode_enum poppler::document::use_attach
139
140 The %document specifies that should be open a view of its %document-level
141 attachments.
142 */
143
144document::document(document_private &dd) : d(&dd) { }
145
146document::~document()
147{
148 delete d;
149}
150
151/**
152 \returns whether the current %document is locked
153 */
154bool document::is_locked() const
155{
156 return d->is_locked;
157}
158
159/**
160 Unlocks the current document, if locked.
161
162 \returns the new locking status of the document
163 */
164bool document::unlock(const std::string &owner_password, const std::string &user_password)
165{
166 if (d->is_locked) {
167 document_private *newdoc = nullptr;
168 if (d->doc_data.size() > 0) {
169 newdoc = new document_private(&d->doc_data, owner_password, user_password);
170 } else if (d->raw_doc_data) {
171 newdoc = new document_private(d->raw_doc_data, d->raw_doc_data_length, owner_password, user_password);
172 } else {
173 newdoc = new document_private(std::make_unique<GooString>(args: d->doc->getFileName()), owner_password, user_password);
174 }
175 if (!newdoc->doc->isOk()) {
176 d->doc_data.swap(x&: newdoc->doc_data);
177 delete newdoc;
178 } else {
179 delete d;
180 d = newdoc;
181 d->is_locked = false;
182 }
183 }
184 return d->is_locked;
185}
186
187/**
188 \returns the eventual page mode specified by the current PDF %document
189 */
190document::page_mode_enum document::page_mode() const
191{
192 switch (d->doc->getCatalog()->getPageMode()) {
193 case Catalog::pageModeNone:
194 return use_none;
195 case Catalog::pageModeOutlines:
196 return use_outlines;
197 case Catalog::pageModeThumbs:
198 return use_thumbs;
199 case Catalog::pageModeFullScreen:
200 return fullscreen;
201 case Catalog::pageModeOC:
202 return use_oc;
203 case Catalog::pageModeAttach:
204 return use_attach;
205 default:
206 return use_none;
207 }
208}
209
210/**
211 \returns the eventual page layout specified by the current PDF %document
212 */
213document::page_layout_enum document::page_layout() const
214{
215 switch (d->doc->getCatalog()->getPageLayout()) {
216 case Catalog::pageLayoutNone:
217 return no_layout;
218 case Catalog::pageLayoutSinglePage:
219 return single_page;
220 case Catalog::pageLayoutOneColumn:
221 return one_column;
222 case Catalog::pageLayoutTwoColumnLeft:
223 return two_column_left;
224 case Catalog::pageLayoutTwoColumnRight:
225 return two_column_right;
226 case Catalog::pageLayoutTwoPageLeft:
227 return two_page_left;
228 case Catalog::pageLayoutTwoPageRight:
229 return two_page_right;
230 default:
231 return no_layout;
232 }
233}
234
235/**
236 Gets the version of the current PDF %document.
237
238 Example:
239 \code
240 poppler::document *doc = ...;
241 // for example, if the document is PDF 1.6:
242 int major = 0, minor = 0;
243 doc->get_pdf_version(&major, &minor);
244 // major == 1
245 // minor == 6
246 \endcode
247
248 \param major if not NULL, will be set to the "major" number of the version
249 \param minor if not NULL, will be set to the "minor" number of the version
250 */
251void document::get_pdf_version(int *major, int *minor) const
252{
253 if (major) {
254 *major = d->doc->getPDFMajorVersion();
255 }
256 if (minor) {
257 *minor = d->doc->getPDFMinorVersion();
258 }
259}
260
261/**
262 \returns all the information keys available in the %document
263 \see info_key, info_date
264 */
265std::vector<std::string> document::info_keys() const
266{
267 if (d->is_locked) {
268 return std::vector<std::string>();
269 }
270
271 Object info = d->doc->getDocInfo();
272 if (!info.isDict()) {
273 return std::vector<std::string>();
274 }
275
276 Dict *info_dict = info.getDict();
277 std::vector<std::string> keys(info_dict->getLength());
278 for (int i = 0; i < info_dict->getLength(); ++i) {
279 keys[i] = std::string(info_dict->getKey(i));
280 }
281
282 return keys;
283}
284
285/**
286 Gets the value of the specified \p key of the document information.
287
288 \returns the value for the \p key, or an empty string if not available
289 \see info_keys, info_date
290 */
291ustring document::info_key(const std::string &key) const
292{
293 if (d->is_locked) {
294 return ustring();
295 }
296
297 std::unique_ptr<GooString> goo_value(d->doc->getDocInfoStringEntry(key: key.c_str()));
298 if (!goo_value.get()) {
299 return ustring();
300 }
301
302 return detail::unicode_GooString_to_ustring(str: goo_value.get());
303}
304
305/**
306 Sets the value of the specified \p key of the %document information to \p val.
307 If \p val is empty, the entry specified by \p key is removed.
308
309 \returns true on success, false on failure
310 */
311bool document::set_info_key(const std::string &key, const ustring &val)
312{
313 if (d->is_locked) {
314 return false;
315 }
316
317 GooString *goo_val;
318
319 if (val.empty()) {
320 goo_val = nullptr;
321 } else {
322 goo_val = detail::ustring_to_unicode_GooString(str: val);
323 }
324
325 d->doc->setDocInfoStringEntry(key: key.c_str(), value: goo_val);
326 return true;
327}
328
329/**
330 Gets the time_type value of the specified \p key of the document
331 information.
332
333 \returns the time_t value for the \p key
334 \see info_keys, info_date
335 */
336time_type document::info_date(const std::string &key) const
337{
338 if (d->is_locked) {
339 return time_type(-1);
340 }
341
342 std::unique_ptr<GooString> goo_date(d->doc->getDocInfoStringEntry(key: key.c_str()));
343 if (!goo_date.get()) {
344 return time_type(-1);
345 }
346
347 return static_cast<time_type>(dateStringToTime(dateString: goo_date.get()));
348}
349
350/**
351 Gets the time_t value of the specified \p key of the document
352 information.
353
354 \returns the time_t value for the \p key
355 \see info_keys, info_date
356 */
357time_t document::info_date_t(const std::string &key) const
358{
359 if (d->is_locked) {
360 return time_t(-1);
361 }
362
363 std::unique_ptr<GooString> goo_date(d->doc->getDocInfoStringEntry(key: key.c_str()));
364 if (!goo_date.get()) {
365 return time_t(-1);
366 }
367
368 return dateStringToTime(dateString: goo_date.get());
369}
370
371/**
372 Sets the time_type value of the specified \p key of the %document information
373 to \p val.
374 If \p val == time_type(-1), the entry specified by \p key is removed.
375
376 \returns true on success, false on failure
377 */
378bool document::set_info_date(const std::string &key, time_type val)
379{
380 if (d->is_locked) {
381 return false;
382 }
383
384 GooString *goo_date;
385
386 if (val == time_type(-1)) {
387 goo_date = nullptr;
388 } else {
389 time_t t = static_cast<time_t>(val);
390 goo_date = timeToDateString(timeA: &t);
391 }
392
393 d->doc->setDocInfoStringEntry(key: key.c_str(), value: goo_date);
394 return true;
395}
396
397/**
398 Sets the time_t value of the specified \p key of the %document information
399 to \p val.
400 If \p val == time_t(-1), the entry specified by \p key is removed.
401
402 \returns true on success, false on failure
403 */
404bool document::set_info_date_t(const std::string &key, time_t val)
405{
406 if (d->is_locked) {
407 return false;
408 }
409
410 GooString *goo_date;
411
412 if (val == time_t(-1)) {
413 goo_date = nullptr;
414 } else {
415 goo_date = timeToDateString(timeA: &val);
416 }
417
418 d->doc->setDocInfoStringEntry(key: key.c_str(), value: goo_date);
419 return true;
420}
421
422/**
423 Gets the %document's title.
424
425 \returns the document's title, or an empty string if not available
426 \see set_title, info_key
427 */
428ustring document::get_title() const
429{
430 if (d->is_locked) {
431 return ustring();
432 }
433
434 std::unique_ptr<GooString> goo_title(d->doc->getDocInfoTitle());
435 if (!goo_title.get()) {
436 return ustring();
437 }
438
439 return detail::unicode_GooString_to_ustring(str: goo_title.get());
440}
441
442/**
443 Sets the %document's title to \p title.
444 If \p title is empty, the %document's title is removed.
445
446 \returns true on success, false on failure
447 */
448bool document::set_title(const ustring &title)
449{
450 if (d->is_locked) {
451 return false;
452 }
453
454 GooString *goo_title;
455
456 if (title.empty()) {
457 goo_title = nullptr;
458 } else {
459 goo_title = detail::ustring_to_unicode_GooString(str: title);
460 }
461
462 d->doc->setDocInfoTitle(goo_title);
463 return true;
464}
465
466/**
467 Gets the document's author.
468
469 \returns the document's author, or an empty string if not available
470 \see set_author, info_key
471 */
472ustring document::get_author() const
473{
474 if (d->is_locked) {
475 return ustring();
476 }
477
478 std::unique_ptr<GooString> goo_author(d->doc->getDocInfoAuthor());
479 if (!goo_author.get()) {
480 return ustring();
481 }
482
483 return detail::unicode_GooString_to_ustring(str: goo_author.get());
484}
485
486/**
487 Sets the %document's author to \p author.
488 If \p author is empty, the %document's author is removed.
489
490 \returns true on success, false on failure
491 */
492bool document::set_author(const ustring &author)
493{
494 if (d->is_locked) {
495 return false;
496 }
497
498 GooString *goo_author;
499
500 if (author.empty()) {
501 goo_author = nullptr;
502 } else {
503 goo_author = detail::ustring_to_unicode_GooString(str: author);
504 }
505
506 d->doc->setDocInfoAuthor(goo_author);
507 return true;
508}
509
510/**
511 Gets the document's subject.
512
513 \returns the document's subject, or an empty string if not available
514 \see set_subject, info_key
515 */
516ustring document::get_subject() const
517{
518 if (d->is_locked) {
519 return ustring();
520 }
521
522 std::unique_ptr<GooString> goo_subject(d->doc->getDocInfoSubject());
523 if (!goo_subject.get()) {
524 return ustring();
525 }
526
527 return detail::unicode_GooString_to_ustring(str: goo_subject.get());
528}
529
530/**
531 Sets the %document's subject to \p subject.
532 If \p subject is empty, the %document's subject is removed.
533
534 \returns true on success, false on failure
535 */
536bool document::set_subject(const ustring &subject)
537{
538 if (d->is_locked) {
539 return false;
540 }
541
542 GooString *goo_subject;
543
544 if (subject.empty()) {
545 goo_subject = nullptr;
546 } else {
547 goo_subject = detail::ustring_to_unicode_GooString(str: subject);
548 }
549
550 d->doc->setDocInfoSubject(goo_subject);
551 return true;
552}
553
554/**
555 Gets the document's keywords.
556
557 \returns the document's keywords, or an empty string if not available
558 \see set_keywords, info_key
559 */
560ustring document::get_keywords() const
561{
562 if (d->is_locked) {
563 return ustring();
564 }
565
566 std::unique_ptr<GooString> goo_keywords(d->doc->getDocInfoKeywords());
567 if (!goo_keywords.get()) {
568 return ustring();
569 }
570
571 return detail::unicode_GooString_to_ustring(str: goo_keywords.get());
572}
573
574/**
575 Sets the %document's keywords to \p keywords.
576 If \p keywords is empty, the %document's keywords are removed.
577
578 \returns true on success, false on failure
579 */
580bool document::set_keywords(const ustring &keywords)
581{
582 if (d->is_locked) {
583 return false;
584 }
585
586 GooString *goo_keywords;
587
588 if (keywords.empty()) {
589 goo_keywords = nullptr;
590 } else {
591 goo_keywords = detail::ustring_to_unicode_GooString(str: keywords);
592 }
593
594 d->doc->setDocInfoKeywords(goo_keywords);
595 return true;
596}
597
598/**
599 Gets the document's creator.
600
601 \returns the document's creator, or an empty string if not available
602 \see set_creator, info_key
603 */
604ustring document::get_creator() const
605{
606 if (d->is_locked) {
607 return ustring();
608 }
609
610 std::unique_ptr<GooString> goo_creator(d->doc->getDocInfoCreator());
611 if (!goo_creator.get()) {
612 return ustring();
613 }
614
615 return detail::unicode_GooString_to_ustring(str: goo_creator.get());
616}
617
618/**
619 Sets the %document's creator to \p creator.
620 If \p creator is empty, the %document's creator is removed.
621
622 \returns true on success, false on failure
623 */
624bool document::set_creator(const ustring &creator)
625{
626 if (d->is_locked) {
627 return false;
628 }
629
630 GooString *goo_creator;
631
632 if (creator.empty()) {
633 goo_creator = nullptr;
634 } else {
635 goo_creator = detail::ustring_to_unicode_GooString(str: creator);
636 }
637
638 d->doc->setDocInfoCreator(goo_creator);
639 return true;
640}
641
642/**
643 Gets the document's producer.
644
645 \returns the document's producer, or an empty string if not available
646 \see set_producer, info_key
647 */
648ustring document::get_producer() const
649{
650 if (d->is_locked) {
651 return ustring();
652 }
653
654 std::unique_ptr<GooString> goo_producer(d->doc->getDocInfoProducer());
655 if (!goo_producer.get()) {
656 return ustring();
657 }
658
659 return detail::unicode_GooString_to_ustring(str: goo_producer.get());
660}
661
662/**
663 Sets the %document's producer to \p producer.
664 If \p producer is empty, the %document's producer is removed.
665
666 \returns true on success, false on failure
667 */
668bool document::set_producer(const ustring &producer)
669{
670 if (d->is_locked) {
671 return false;
672 }
673
674 GooString *goo_producer;
675
676 if (producer.empty()) {
677 goo_producer = nullptr;
678 } else {
679 goo_producer = detail::ustring_to_unicode_GooString(str: producer);
680 }
681
682 d->doc->setDocInfoProducer(goo_producer);
683 return true;
684}
685
686/**
687 Gets the document's creation date as a time_type value.
688
689 \returns the document's creation date as a time_type value
690 \see set_creation_date, info_date
691 */
692time_type document::get_creation_date() const
693{
694 if (d->is_locked) {
695 return time_type(-1);
696 }
697
698 std::unique_ptr<GooString> goo_creation_date(d->doc->getDocInfoCreatDate());
699 if (!goo_creation_date.get()) {
700 return time_type(-1);
701 }
702
703 return static_cast<time_type>(dateStringToTime(dateString: goo_creation_date.get()));
704}
705
706/**
707 Gets the document's creation date as a time_t value.
708
709 \returns the document's creation date as a time_t value
710 \see set_creation_date, info_date
711 */
712time_t document::get_creation_date_t() const
713{
714 if (d->is_locked) {
715 return time_t(-1);
716 }
717
718 std::unique_ptr<GooString> goo_creation_date(d->doc->getDocInfoCreatDate());
719 if (!goo_creation_date.get()) {
720 return time_t(-1);
721 }
722
723 return dateStringToTime(dateString: goo_creation_date.get());
724}
725
726/**
727 Sets the %document's creation date to \p creation_date.
728 If \p creation_date == time_type(-1), the %document's creation date is removed.
729
730 \returns true on success, false on failure
731 */
732bool document::set_creation_date(time_type creation_date)
733{
734 if (d->is_locked) {
735 return false;
736 }
737
738 GooString *goo_creation_date;
739
740 if (creation_date == time_type(-1)) {
741 goo_creation_date = nullptr;
742 } else {
743 time_t t = static_cast<time_t>(creation_date);
744 goo_creation_date = timeToDateString(timeA: &t);
745 }
746
747 d->doc->setDocInfoCreatDate(goo_creation_date);
748 return true;
749}
750
751/**
752 Sets the %document's creation date to \p creation_date.
753 If \p creation_date == time_t(-1), the %document's creation date is removed.
754
755 \returns true on success, false on failure
756 */
757bool document::set_creation_date_t(time_t creation_date)
758{
759 if (d->is_locked) {
760 return false;
761 }
762
763 GooString *goo_creation_date;
764
765 if (creation_date == time_t(-1)) {
766 goo_creation_date = nullptr;
767 } else {
768 goo_creation_date = timeToDateString(timeA: &creation_date);
769 }
770
771 d->doc->setDocInfoCreatDate(goo_creation_date);
772 return true;
773}
774
775/**
776 Gets the document's modification date as a time_type value.
777
778 \returns the document's modification date as a time_type value
779 \see set_modification_date, info_date
780 */
781time_type document::get_modification_date() const
782{
783 if (d->is_locked) {
784 return time_type(-1);
785 }
786
787 std::unique_ptr<GooString> goo_modification_date(d->doc->getDocInfoModDate());
788 if (!goo_modification_date.get()) {
789 return time_type(-1);
790 }
791
792 return static_cast<time_type>(dateStringToTime(dateString: goo_modification_date.get()));
793}
794
795/**
796 Gets the document's modification date as a time_t value.
797
798 \returns the document's modification date as a time_t value
799 \see set_modification_date, info_date
800 */
801time_t document::get_modification_date_t() const
802{
803 if (d->is_locked) {
804 return time_t(-1);
805 }
806
807 std::unique_ptr<GooString> goo_modification_date(d->doc->getDocInfoModDate());
808 if (!goo_modification_date.get()) {
809 return time_t(-1);
810 }
811
812 return dateStringToTime(dateString: goo_modification_date.get());
813}
814
815/**
816 Sets the %document's modification date to \p mod_date.
817 If \p mod_date == time_type(-1), the %document's modification date is removed.
818
819 \returns true on success, false on failure
820 */
821bool document::set_modification_date(time_type mod_date)
822{
823 if (d->is_locked) {
824 return false;
825 }
826
827 GooString *goo_mod_date;
828
829 if (mod_date == time_type(-1)) {
830 goo_mod_date = nullptr;
831 } else {
832 time_t t = static_cast<time_t>(mod_date);
833 goo_mod_date = timeToDateString(timeA: &t);
834 }
835
836 d->doc->setDocInfoModDate(goo_mod_date);
837 return true;
838}
839
840/**
841 Sets the %document's modification date to \p mod_date.
842 If \p mod_date == time_t(-1), the %document's modification date is removed.
843
844 \returns true on success, false on failure
845 */
846bool document::set_modification_date_t(time_t mod_date)
847{
848 if (d->is_locked) {
849 return false;
850 }
851
852 GooString *goo_mod_date;
853
854 if (mod_date == time_t(-1)) {
855 goo_mod_date = nullptr;
856 } else {
857 goo_mod_date = timeToDateString(timeA: &mod_date);
858 }
859
860 d->doc->setDocInfoModDate(goo_mod_date);
861 return true;
862}
863
864/**
865 Removes the %document's Info dictionary.
866
867 \returns true on success, false on failure
868 */
869bool document::remove_info()
870{
871 if (d->is_locked) {
872 return false;
873 }
874
875 d->doc->removeDocInfo();
876 return true;
877}
878
879/**
880 \returns whether the document is encrypted
881 */
882bool document::is_encrypted() const
883{
884 return d->doc->isEncrypted();
885}
886
887/**
888 \returns whether the document is linearized
889 */
890bool document::is_linearized() const
891{
892 return d->doc->isLinearized();
893}
894
895/**
896 Check for available "document permission".
897
898 \returns whether the specified permission is allowed
899 */
900bool document::has_permission(permission_enum which) const
901{
902 switch (which) {
903 case perm_print:
904 return d->doc->okToPrint();
905 case perm_change:
906 return d->doc->okToChange();
907 case perm_copy:
908 return d->doc->okToCopy();
909 case perm_add_notes:
910 return d->doc->okToAddNotes();
911 case perm_fill_forms:
912 return d->doc->okToFillForm();
913 case perm_accessibility:
914 return d->doc->okToAccessibility();
915 case perm_assemble:
916 return d->doc->okToAssemble();
917 case perm_print_high_resolution:
918 return d->doc->okToPrintHighRes();
919 }
920 return true;
921}
922
923/**
924 Reads the %document metadata string.
925
926 \return the %document metadata string
927 */
928ustring document::metadata() const
929{
930 std::unique_ptr<GooString> md(d->doc->getCatalog()->readMetadata());
931 if (md.get()) {
932 return detail::unicode_GooString_to_ustring(str: md.get());
933 }
934 return ustring();
935}
936
937/**
938 Gets the IDs of the current PDF %document, if available.
939
940 \param permanent_id if not NULL, will be set to the permanent ID of the %document
941 \param update_id if not NULL, will be set to the update ID of the %document
942
943 \returns whether the document has the IDs
944
945 \since 0.16
946 */
947bool document::get_pdf_id(std::string *permanent_id, std::string *update_id) const
948{
949 GooString goo_permanent_id;
950 GooString goo_update_id;
951
952 if (!d->doc->getID(permanent_id: permanent_id ? &goo_permanent_id : nullptr, update_id: update_id ? &goo_update_id : nullptr)) {
953 return false;
954 }
955
956 if (permanent_id) {
957 *permanent_id = goo_permanent_id.c_str();
958 }
959 if (update_id) {
960 *update_id = goo_update_id.c_str();
961 }
962
963 return true;
964}
965
966/**
967 Document page count.
968
969 \returns the number of pages of the document
970 */
971int document::pages() const
972{
973 return d->doc->getNumPages();
974}
975
976/**
977 Document page by label reading.
978
979 This creates a new page representing the %document %page whose label is the
980 specified \p label. If there is no page with that \p label, NULL is returned.
981
982 \returns a new page object or NULL
983 */
984page *document::create_page(const ustring &label) const
985{
986 std::unique_ptr<GooString> goolabel(detail::ustring_to_unicode_GooString(str: label));
987 int index = 0;
988
989 if (!d->doc->getCatalog()->labelToIndex(label: goolabel.get(), index: &index)) {
990 return nullptr;
991 }
992 return create_page(index);
993}
994
995/**
996 Document page by index reading.
997
998 This creates a new page representing the \p index -th %page of the %document.
999 \note the page indexes are in the range [0, pages()[.
1000
1001 \returns a new page object or NULL
1002 */
1003page *document::create_page(int index) const
1004{
1005 if (index >= 0 && index < d->doc->getNumPages()) {
1006 page *p = new page(d, index);
1007 if (p->d->page) {
1008 return p;
1009 } else {
1010 delete p;
1011 return nullptr;
1012 }
1013 } else {
1014 return nullptr;
1015 }
1016}
1017
1018/**
1019 Reads all the font information of the %document.
1020
1021 \note this can be slow for big documents; prefer the use of a font_iterator
1022 to read incrementally page by page
1023 \see create_font_iterator
1024 */
1025std::vector<font_info> document::fonts() const
1026{
1027 std::vector<font_info> result;
1028 font_iterator it(0, d);
1029 while (it.has_next()) {
1030 const std::vector<font_info> l = it.next();
1031 std::copy(first: l.begin(), last: l.end(), result: std::back_inserter(x&: result));
1032 }
1033 return result;
1034}
1035
1036/**
1037 Creates a new font iterator.
1038
1039 This creates a new font iterator for reading the font information of the
1040 %document page by page, starting at the specified \p start_page (0 if not
1041 specified).
1042
1043 \returns a new font iterator
1044 */
1045font_iterator *document::create_font_iterator(int start_page) const
1046{
1047 return new font_iterator(start_page, d);
1048}
1049
1050/**
1051 Reads the TOC (table of contents) of the %document.
1052
1053 \returns a new toc object if a TOC is available, NULL otherwise
1054 */
1055toc *document::create_toc() const
1056{
1057 return toc_private::load_from_outline(outline: d->doc->getOutline());
1058}
1059
1060/**
1061 Reads whether the current document has %document-level embedded files
1062 (attachments).
1063
1064 This is a very fast way to know whether there are embedded files (also known
1065 as "attachments") at the %document-level. Note this does not take into account
1066 files embedded in other ways (e.g. to annotations).
1067
1068 \returns whether the document has embedded files
1069 */
1070bool document::has_embedded_files() const
1071{
1072 return d->doc->getCatalog()->numEmbeddedFiles() > 0;
1073}
1074
1075/**
1076 Reads all the %document-level embedded files of the %document.
1077
1078 \returns the %document-level embedded files
1079 */
1080std::vector<embedded_file *> document::embedded_files() const
1081{
1082 if (d->is_locked) {
1083 return std::vector<embedded_file *>();
1084 }
1085
1086 if (d->embedded_files.empty() && d->doc->getCatalog()->numEmbeddedFiles() > 0) {
1087 const int num = d->doc->getCatalog()->numEmbeddedFiles();
1088 d->embedded_files.resize(new_size: num);
1089 for (int i = 0; i < num; ++i) {
1090 std::unique_ptr<FileSpec> fs = d->doc->getCatalog()->embeddedFile(i);
1091 d->embedded_files[i] = embedded_file_private::create(fs: std::move(fs));
1092 }
1093 }
1094 return d->embedded_files;
1095}
1096
1097/**
1098 Creates a map of all the named destinations in the %document.
1099
1100 \note The destination names may contain \\0 and other binary values
1101 so they are not printable and cannot convert to null-terminated C strings.
1102
1103 \returns the map of the each name and destination
1104
1105 \since 0.74
1106 */
1107std::map<std::string, destination> document::create_destination_map() const
1108{
1109 std::map<std::string, destination> m;
1110
1111 Catalog *catalog = d->doc->getCatalog();
1112 if (!catalog) {
1113 return m;
1114 }
1115
1116 // Iterate from name-dict
1117 const int nDests = catalog->numDests();
1118 for (int i = 0; i < nDests; ++i) {
1119 std::string key(catalog->getDestsName(i));
1120 std::unique_ptr<LinkDest> link_dest = catalog->getDestsDest(i);
1121
1122 if (link_dest) {
1123 destination dest(new destination_private(link_dest.get(), d->doc));
1124
1125 m.emplace(args: std::move(key), args: std::move(dest));
1126 }
1127 }
1128
1129 // Iterate from name-tree
1130 const int nDestsNameTree = catalog->numDestNameTree();
1131 for (int i = 0; i < nDestsNameTree; ++i) {
1132 std::string key(catalog->getDestNameTreeName(i)->c_str(), catalog->getDestNameTreeName(i)->getLength());
1133 std::unique_ptr<LinkDest> link_dest = catalog->getDestNameTreeDest(i);
1134
1135 if (link_dest) {
1136 destination dest(new destination_private(link_dest.get(), d->doc));
1137
1138 m.emplace(args: std::move(key), args: std::move(dest));
1139 }
1140 }
1141
1142 return m;
1143}
1144
1145/**
1146 Saves the %document to file \p file_name.
1147
1148 \returns true on success, false on failure
1149 */
1150bool document::save(const std::string &file_name) const
1151{
1152 if (d->is_locked) {
1153 return false;
1154 }
1155
1156 GooString fname(file_name.c_str());
1157 return d->doc->saveAs(name: fname) == errNone;
1158}
1159
1160/**
1161 Saves the original version of the %document to file \p file_name.
1162
1163 \returns true on success, false on failure
1164 */
1165bool document::save_a_copy(const std::string &file_name) const
1166{
1167 if (d->is_locked) {
1168 return false;
1169 }
1170
1171 GooString fname(file_name.c_str());
1172 return d->doc->saveWithoutChangesAs(name: fname) == errNone;
1173}
1174
1175/**
1176 Tries to load a PDF %document from the specified file.
1177
1178 \param file_name the file to open
1179 \returns a new document if the load succeeded (even if the document is locked),
1180 NULL otherwise
1181 */
1182document *document::load_from_file(const std::string &file_name, const std::string &owner_password, const std::string &user_password)
1183{
1184 document_private *doc = new document_private(std::make_unique<GooString>(args: file_name.c_str()), owner_password, user_password);
1185 return document_private::check_document(doc, file_data: nullptr);
1186}
1187
1188/**
1189 Tries to load a PDF %document from the specified data.
1190
1191 \note if the loading succeeds, the document takes ownership of the
1192 \p file_data (swap()ing it)
1193
1194 \param file_data the data representing a document to open
1195 \returns a new document if the load succeeded (even if the document is locked),
1196 NULL otherwise
1197 */
1198document *document::load_from_data(byte_array *file_data, const std::string &owner_password, const std::string &user_password)
1199{
1200 if (!file_data || file_data->size() < 10) {
1201 return nullptr;
1202 }
1203
1204 document_private *doc = new document_private(file_data, owner_password, user_password);
1205 return document_private::check_document(doc, file_data);
1206}
1207
1208/**
1209 Tries to load a PDF %document from the specified data buffer.
1210
1211 \note the buffer must remain valid for the whole lifetime of the returned
1212 document
1213
1214 \param file_data the data buffer representing a document to open
1215 \param file_data_length the length of the data buffer
1216
1217 \returns a new document if the load succeeded (even if the document is locked),
1218 NULL otherwise
1219
1220 \since 0.16
1221 */
1222document *document::load_from_raw_data(const char *file_data, int file_data_length, const std::string &owner_password, const std::string &user_password)
1223{
1224 if (!file_data || file_data_length < 10) {
1225 return nullptr;
1226 }
1227
1228 document_private *doc = new document_private(file_data, file_data_length, owner_password, user_password);
1229 return document_private::check_document(doc, file_data: nullptr);
1230}
1231

source code of poppler/cpp/poppler-document.cpp