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 | |
51 | using namespace poppler; |
52 | |
53 | document_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 | |
58 | document_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 | |
65 | document_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 | |
73 | document_private::document_private() : GlobalParamsIniter(detail::error_function), doc(nullptr), raw_doc_data(nullptr), raw_doc_data_length(0), is_locked(false) { } |
74 | |
75 | document_private::~document_private() |
76 | { |
77 | delete_all(c: embedded_files); |
78 | |
79 | delete doc; |
80 | } |
81 | |
82 | document *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 | |
144 | document::document(document_private &dd) : d(&dd) { } |
145 | |
146 | document::~document() |
147 | { |
148 | delete d; |
149 | } |
150 | |
151 | /** |
152 | \returns whether the current %document is locked |
153 | */ |
154 | bool 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 | */ |
164 | bool 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 | */ |
190 | document::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 | */ |
213 | document::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 | */ |
251 | void 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 | */ |
265 | std::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 | */ |
291 | ustring 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 | */ |
311 | bool 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 | */ |
336 | time_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 | */ |
357 | time_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 | */ |
378 | bool 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 | */ |
404 | bool 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 | */ |
428 | ustring 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 | */ |
448 | bool 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 | */ |
472 | ustring 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 | */ |
492 | bool 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 | */ |
516 | ustring 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 | */ |
536 | bool 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 | */ |
560 | ustring 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 | */ |
580 | bool 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 | */ |
604 | ustring 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 | */ |
624 | bool 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 | */ |
648 | ustring 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 | */ |
668 | bool 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 | */ |
692 | time_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 | */ |
712 | time_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 | */ |
732 | bool 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 | */ |
757 | bool 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 | */ |
781 | time_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 | */ |
801 | time_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 | */ |
821 | bool 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 | */ |
846 | bool 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 | */ |
869 | bool 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 | */ |
882 | bool document::is_encrypted() const |
883 | { |
884 | return d->doc->isEncrypted(); |
885 | } |
886 | |
887 | /** |
888 | \returns whether the document is linearized |
889 | */ |
890 | bool 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 | */ |
900 | bool 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 | */ |
928 | ustring 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 | */ |
947 | bool 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 | */ |
971 | int 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 | */ |
984 | page *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 | */ |
1003 | page *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 | */ |
1025 | std::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 | */ |
1045 | font_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 | */ |
1055 | toc *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 | */ |
1070 | bool 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 | */ |
1080 | std::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 | */ |
1107 | std::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 | */ |
1150 | bool 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 | */ |
1165 | bool 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 | */ |
1182 | document *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 | */ |
1198 | document *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 | */ |
1222 | document *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 | |