Coverage Report

Created: 2026-09-28 08:21

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/poppler/cpp/poppler-document.cpp
Line
Count
Source
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, 2025, 2026, 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
 * Copyright (C) 2025, 2026 g10 Code GmbH, Author: Sune Stolborg Vuorela <sune@vuorela.dk>
9
 * Copyright (C) 2025 Nathanael d. Noblet <nathanael@noblet.ca>
10
 *
11
 * This program is free software; you can redistribute it and/or modify
12
 * it under the terms of the GNU General Public License as published by
13
 * the Free Software Foundation; either version 2, or (at your option)
14
 * any later version.
15
 *
16
 * This program is distributed in the hope that it will be useful,
17
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19
 * GNU General Public License for more details.
20
 *
21
 * You should have received a copy of the GNU General Public License
22
 * along with this program; if not, write to the Free Software
23
 * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
24
 */
25
26
/**
27
 \file poppler-document.h
28
 */
29
#include "poppler-destination.h"
30
#include "poppler-document.h"
31
#include "poppler-embedded-file.h"
32
#include "poppler-page.h"
33
#include "poppler-toc.h"
34
35
#include "poppler-destination-private.h"
36
#include "poppler-document-private.h"
37
#include "poppler-embedded-file-private.h"
38
#include "poppler-page-private.h"
39
#include "poppler-private.h"
40
#include "poppler-toc-private.h"
41
42
#include "Catalog.h"
43
#include "DateInfo.h"
44
#include "ErrorCodes.h"
45
#include "GlobalParams.h"
46
#include "Link.h"
47
#include "Outline.h"
48
49
#include <algorithm>
50
#include <iterator>
51
#include <memory>
52
53
using namespace poppler;
54
55
11.4k
document_private::document_private(std::unique_ptr<GooString> &&file_path, const std::string &owner_password, const std::string &user_password) : document_private()
56
11.4k
{
57
11.4k
    doc = std::make_unique<PDFDoc>(std::move(file_path), GooString(owner_password), GooString(user_password));
58
11.4k
}
59
60
0
document_private::document_private(byte_array *file_data, const std::string &owner_password, const std::string &user_password) : document_private()
61
0
{
62
0
    file_data->swap(doc_data);
63
0
    auto memstr = std::make_unique<MemStream>(doc_data.data(), 0, doc_data.size(), Object::null());
64
0
    doc = std::make_unique<PDFDoc>(std::move(memstr), GooString(owner_password), GooString(user_password));
65
0
}
66
67
43.0k
document_private::document_private(const char *file_data, int file_data_length, const std::string &owner_password, const std::string &user_password) : document_private()
68
43.0k
{
69
43.0k
    raw_doc_data = file_data;
70
43.0k
    raw_doc_data_length = file_data_length;
71
43.0k
    auto memstr = std::make_unique<MemStream>(raw_doc_data, 0, raw_doc_data_length, Object::null());
72
43.0k
    doc = std::make_unique<PDFDoc>(std::move(memstr), GooString(owner_password), GooString(user_password));
73
43.0k
}
74
75
54.4k
document_private::document_private() : GlobalParamsIniter(detail::error_function), doc(nullptr), raw_doc_data(nullptr), raw_doc_data_length(0), is_locked(false) { }
76
77
document_private::~document_private()
78
54.4k
{
79
54.4k
    delete_all(embedded_files);
80
54.4k
}
81
82
document *document_private::check_document(document_private *doc, byte_array *file_data)
83
54.4k
{
84
54.4k
    if (doc->doc->isOk() || doc->doc->getErrorCode() == errEncrypted) {
85
39.1k
        if (doc->doc->getErrorCode() == errEncrypted) {
86
124
            doc->is_locked = true;
87
124
        }
88
39.1k
        return new document(*doc);
89
39.1k
    }
90
    // put back the document data where it was before
91
15.3k
    if (file_data) {
92
0
        file_data->swap(doc->doc_data);
93
0
    }
94
15.3k
    delete doc;
95
96
15.3k
    return nullptr;
97
54.4k
}
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
39.1k
document::document(document_private &dd) : d(&dd) { }
145
146
document::~document()
147
39.1k
{
148
39.1k
    delete d;
149
39.1k
}
150
151
/**
152
 \returns whether the current %document is locked
153
 */
154
bool document::is_locked() const
155
39.1k
{
156
39.1k
    return d->is_locked;
157
39.1k
}
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
0
{
166
0
    if (d->is_locked) {
167
0
        document_private *newdoc = nullptr;
168
0
        if (!d->doc_data.empty()) {
169
0
            newdoc = new document_private(&d->doc_data, owner_password, user_password);
170
0
        } else if (d->raw_doc_data) {
171
0
            newdoc = new document_private(d->raw_doc_data, d->raw_doc_data_length, owner_password, user_password);
172
0
        } else {
173
0
            newdoc = new document_private(d->doc->getFileName()->copy(), owner_password, user_password);
174
0
        }
175
0
        if (!newdoc->doc->isOk()) {
176
0
            d->doc_data.swap(newdoc->doc_data);
177
0
            delete newdoc;
178
0
        } else {
179
0
            delete d;
180
0
            d = newdoc;
181
0
            d->is_locked = false;
182
0
        }
183
0
    }
184
0
    return d->is_locked;
185
0
}
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
0
{
192
0
    switch (d->doc->getCatalog()->getPageMode()) {
193
0
    case Catalog::pageModeNone:
194
0
        return use_none;
195
0
    case Catalog::pageModeOutlines:
196
0
        return use_outlines;
197
0
    case Catalog::pageModeThumbs:
198
0
        return use_thumbs;
199
0
    case Catalog::pageModeFullScreen:
200
0
        return fullscreen;
201
0
    case Catalog::pageModeOC:
202
0
        return use_oc;
203
0
    case Catalog::pageModeAttach:
204
0
        return use_attach;
205
0
    default:
206
0
        return use_none;
207
0
    }
208
0
}
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
0
{
215
0
    switch (d->doc->getCatalog()->getPageLayout()) {
216
0
    case Catalog::pageLayoutNone:
217
0
        return no_layout;
218
0
    case Catalog::pageLayoutSinglePage:
219
0
        return single_page;
220
0
    case Catalog::pageLayoutOneColumn:
221
0
        return one_column;
222
0
    case Catalog::pageLayoutTwoColumnLeft:
223
0
        return two_column_left;
224
0
    case Catalog::pageLayoutTwoColumnRight:
225
0
        return two_column_right;
226
0
    case Catalog::pageLayoutTwoPageLeft:
227
0
        return two_page_left;
228
0
    case Catalog::pageLayoutTwoPageRight:
229
0
        return two_page_right;
230
0
    default:
231
0
        return no_layout;
232
0
    }
233
0
}
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
0
{
253
0
    if (major) {
254
0
        *major = d->doc->getPDFMajorVersion();
255
0
    }
256
0
    if (minor) {
257
0
        *minor = d->doc->getPDFMinorVersion();
258
0
    }
259
0
}
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
0
{
267
0
    if (d->is_locked) {
268
0
        return std::vector<std::string>();
269
0
    }
270
271
0
    Object info = d->doc->getDocInfo();
272
0
    if (!info.isDict()) {
273
0
        return std::vector<std::string>();
274
0
    }
275
276
0
    Dict *info_dict = info.getDict();
277
0
    std::vector<std::string> keys(info_dict->getLength());
278
0
    for (int i = 0; i < info_dict->getLength(); ++i) {
279
0
        keys[i] = std::string(info_dict->getKey(i));
280
0
    }
281
282
0
    return keys;
283
0
}
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
0
{
293
0
    if (d->is_locked) {
294
0
        return ustring();
295
0
    }
296
297
0
    std::optional<std::string> goo_value(d->doc->getDocInfoStringEntry(key));
298
0
    if (!goo_value) {
299
0
        return ustring();
300
0
    }
301
302
0
    return detail::unicode_string_to_ustring(goo_value.value());
303
0
}
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
0
{
313
0
    if (d->is_locked) {
314
0
        return false;
315
0
    }
316
317
0
    std::unique_ptr<GooString> goo_val;
318
319
0
    if (!val.empty()) {
320
0
        goo_val = detail::ustring_to_unicode_GooString(val);
321
0
    }
322
323
0
    d->doc->setDocInfoStringEntry(key.c_str(), std::move(goo_val));
324
0
    return true;
325
0
}
326
327
/**
328
 Gets the time_t value of the specified \p key of the document
329
 information.
330
331
 \returns the time_t value for the \p key
332
 \see info_keys, info_date
333
 */
334
time_t document::info_date_t(const std::string &key) const
335
0
{
336
0
    if (d->is_locked) {
337
0
        return static_cast<time_t>(-1);
338
0
    }
339
340
0
    std::optional<std::string> goo_date(d->doc->getDocInfoStringEntry(key));
341
0
    if (!goo_date) {
342
0
        return static_cast<time_t>(-1);
343
0
    }
344
345
0
    return dateStringToTime(goo_date.value());
346
0
}
347
348
/**
349
 Sets the time_t value of the specified \p key of the %document information
350
 to \p val.
351
 If \p val == time_t(-1), the entry specified by \p key is removed.
352
353
 \returns true on success, false on failure
354
 */
355
bool document::set_info_date_t(const std::string &key, time_t val)
356
0
{
357
0
    if (d->is_locked) {
358
0
        return false;
359
0
    }
360
361
0
    std::unique_ptr<GooString> goo_date;
362
363
0
    if (val != static_cast<time_t>(-1)) {
364
0
        goo_date = timeToDateString(&val);
365
0
    }
366
367
0
    d->doc->setDocInfoStringEntry(key.c_str(), std::move(goo_date));
368
0
    return true;
369
0
}
370
371
/**
372
 Gets the %document's title.
373
374
 \returns the document's title, or an empty string if not available
375
 \see set_title, info_key
376
 */
377
ustring document::get_title() const
378
0
{
379
0
    if (d->is_locked) {
380
0
        return ustring();
381
0
    }
382
383
0
    std::optional<std::string> goo_title(d->doc->getDocInfoTitle());
384
0
    if (!goo_title) {
385
0
        return ustring();
386
0
    }
387
388
0
    return detail::unicode_string_to_ustring(goo_title.value());
389
0
}
390
391
/**
392
 Sets the %document's title to \p title.
393
 If \p title is empty, the %document's title is removed.
394
395
 \returns true on success, false on failure
396
 */
397
bool document::set_title(const ustring &title)
398
3.74k
{
399
3.74k
    if (d->is_locked) {
400
0
        return false;
401
0
    }
402
403
3.74k
    std::unique_ptr<GooString> goo_title;
404
405
3.74k
    if (!title.empty()) {
406
3.51k
        goo_title = detail::ustring_to_unicode_GooString(title);
407
3.51k
    }
408
409
3.74k
    d->doc->setDocInfoTitle(std::move(goo_title));
410
3.74k
    return true;
411
3.74k
}
412
413
/**
414
 Gets the document's author.
415
416
 \returns the document's author, or an empty string if not available
417
 \see set_author, info_key
418
 */
419
ustring document::get_author() const
420
0
{
421
0
    if (d->is_locked) {
422
0
        return ustring();
423
0
    }
424
425
0
    std::optional<std::string> goo_author(d->doc->getDocInfoAuthor());
426
0
    if (!goo_author) {
427
0
        return ustring();
428
0
    }
429
430
0
    return detail::unicode_string_to_ustring(goo_author.value());
431
0
}
432
433
/**
434
 Sets the %document's author to \p author.
435
 If \p author is empty, the %document's author is removed.
436
437
 \returns true on success, false on failure
438
 */
439
bool document::set_author(const ustring &author)
440
3.74k
{
441
3.74k
    if (d->is_locked) {
442
0
        return false;
443
0
    }
444
445
3.74k
    std::unique_ptr<GooString> goo_author;
446
447
3.74k
    if (!author.empty()) {
448
3.68k
        goo_author = detail::ustring_to_unicode_GooString(author);
449
3.68k
    }
450
451
3.74k
    d->doc->setDocInfoAuthor(std::move(goo_author));
452
3.74k
    return true;
453
3.74k
}
454
455
/**
456
 Gets the document's subject.
457
458
 \returns the document's subject, or an empty string if not available
459
 \see set_subject, info_key
460
 */
461
ustring document::get_subject() const
462
0
{
463
0
    if (d->is_locked) {
464
0
        return ustring();
465
0
    }
466
467
0
    std::optional<std::string> goo_subject(d->doc->getDocInfoSubject());
468
0
    if (!goo_subject) {
469
0
        return ustring();
470
0
    }
471
472
0
    return detail::unicode_string_to_ustring(goo_subject.value());
473
0
}
474
475
/**
476
 Sets the %document's subject to \p subject.
477
 If \p subject is empty, the %document's subject is removed.
478
479
 \returns true on success, false on failure
480
 */
481
bool document::set_subject(const ustring &subject)
482
3.74k
{
483
3.74k
    if (d->is_locked) {
484
0
        return false;
485
0
    }
486
487
3.74k
    std::unique_ptr<GooString> goo_subject;
488
489
3.74k
    if (!subject.empty()) {
490
3.56k
        goo_subject = detail::ustring_to_unicode_GooString(subject);
491
3.56k
    }
492
493
3.74k
    d->doc->setDocInfoSubject(std::move(goo_subject));
494
3.74k
    return true;
495
3.74k
}
496
497
/**
498
 Gets the document's keywords.
499
500
 \returns the document's keywords, or an empty string if not available
501
 \see set_keywords, info_key
502
 */
503
ustring document::get_keywords() const
504
0
{
505
0
    if (d->is_locked) {
506
0
        return ustring();
507
0
    }
508
509
0
    std::optional<std::string> goo_keywords(d->doc->getDocInfoKeywords());
510
0
    if (!goo_keywords) {
511
0
        return ustring();
512
0
    }
513
514
0
    return detail::unicode_string_to_ustring(goo_keywords.value());
515
0
}
516
517
/**
518
 Sets the %document's keywords to \p keywords.
519
 If \p keywords is empty, the %document's keywords are removed.
520
521
 \returns true on success, false on failure
522
 */
523
bool document::set_keywords(const ustring &keywords)
524
3.74k
{
525
3.74k
    if (d->is_locked) {
526
0
        return false;
527
0
    }
528
529
3.74k
    std::unique_ptr<GooString> goo_keywords;
530
531
3.74k
    if (!keywords.empty()) {
532
3.61k
        goo_keywords = detail::ustring_to_unicode_GooString(keywords);
533
3.61k
    }
534
535
3.74k
    d->doc->setDocInfoKeywords(std::move(goo_keywords));
536
3.74k
    return true;
537
3.74k
}
538
539
/**
540
 Gets the document's creator.
541
542
 \returns the document's creator, or an empty string if not available
543
 \see set_creator, info_key
544
 */
545
ustring document::get_creator() const
546
0
{
547
0
    if (d->is_locked) {
548
0
        return ustring();
549
0
    }
550
551
0
    std::optional<std::string> goo_creator(d->doc->getDocInfoCreator());
552
0
    if (!goo_creator) {
553
0
        return ustring();
554
0
    }
555
556
0
    return detail::unicode_string_to_ustring(goo_creator.value());
557
0
}
558
559
/**
560
 Sets the %document's creator to \p creator.
561
 If \p creator is empty, the %document's creator is removed.
562
563
 \returns true on success, false on failure
564
 */
565
bool document::set_creator(const ustring &creator)
566
3.74k
{
567
3.74k
    if (d->is_locked) {
568
0
        return false;
569
0
    }
570
571
3.74k
    std::unique_ptr<GooString> goo_creator;
572
573
3.74k
    if (!creator.empty()) {
574
3.49k
        goo_creator = detail::ustring_to_unicode_GooString(creator);
575
3.49k
    }
576
577
3.74k
    d->doc->setDocInfoCreator(std::move(goo_creator));
578
3.74k
    return true;
579
3.74k
}
580
581
/**
582
 Gets the document's producer.
583
584
 \returns the document's producer, or an empty string if not available
585
 \see set_producer, info_key
586
 */
587
ustring document::get_producer() const
588
0
{
589
0
    if (d->is_locked) {
590
0
        return ustring();
591
0
    }
592
593
0
    std::optional<std::string> goo_producer(d->doc->getDocInfoProducer());
594
0
    if (!goo_producer) {
595
0
        return ustring();
596
0
    }
597
598
0
    return detail::unicode_string_to_ustring(goo_producer.value());
599
0
}
600
601
/**
602
 Sets the %document's producer to \p producer.
603
 If \p producer is empty, the %document's producer is removed.
604
605
 \returns true on success, false on failure
606
 */
607
bool document::set_producer(const ustring &producer)
608
3.74k
{
609
3.74k
    if (d->is_locked) {
610
0
        return false;
611
0
    }
612
613
3.74k
    std::unique_ptr<GooString> goo_producer;
614
615
3.74k
    if (!producer.empty()) {
616
3.57k
        goo_producer = detail::ustring_to_unicode_GooString(producer);
617
3.57k
    }
618
619
3.74k
    d->doc->setDocInfoProducer(std::move(goo_producer));
620
3.74k
    return true;
621
3.74k
}
622
623
/**
624
 Gets the document's creation date as a time_t value.
625
626
 \returns the document's creation date as a time_t value
627
 \see set_creation_date, info_date
628
 */
629
time_t document::get_creation_date_t() const
630
0
{
631
0
    if (d->is_locked) {
632
0
        return static_cast<time_t>(-1);
633
0
    }
634
635
0
    std::optional<std::string> goo_creation_date(d->doc->getDocInfoCreatDate());
636
0
    if (!goo_creation_date) {
637
0
        return static_cast<time_t>(-1);
638
0
    }
639
640
0
    return dateStringToTime(goo_creation_date.value());
641
0
}
642
643
/**
644
 Sets the %document's creation date to \p creation_date.
645
 If \p creation_date == time_t(-1), the %document's creation date is removed.
646
647
 \returns true on success, false on failure
648
 */
649
bool document::set_creation_date_t(time_t creation_date)
650
0
{
651
0
    if (d->is_locked) {
652
0
        return false;
653
0
    }
654
655
0
    std::unique_ptr<GooString> goo_creation_date;
656
657
0
    if (creation_date != static_cast<time_t>(-1)) {
658
0
        goo_creation_date = timeToDateString(&creation_date);
659
0
    }
660
661
0
    d->doc->setDocInfoCreatDate(std::move(goo_creation_date));
662
0
    return true;
663
0
}
664
665
/**
666
 Gets the document's modification date as a time_t value.
667
668
 \returns the document's modification date as a time_t value
669
 \see set_modification_date, info_date
670
 */
671
time_t document::get_modification_date_t() const
672
0
{
673
0
    if (d->is_locked) {
674
0
        return static_cast<time_t>(-1);
675
0
    }
676
677
0
    std::optional<std::string> goo_modification_date(d->doc->getDocInfoModDate());
678
0
    if (!goo_modification_date) {
679
0
        return static_cast<time_t>(-1);
680
0
    }
681
682
0
    return dateStringToTime(goo_modification_date.value());
683
0
}
684
685
/**
686
 Sets the %document's modification date to \p mod_date.
687
 If \p mod_date == time_t(-1), the %document's modification date is removed.
688
689
 \returns true on success, false on failure
690
 */
691
bool document::set_modification_date_t(time_t mod_date)
692
0
{
693
0
    if (d->is_locked) {
694
0
        return false;
695
0
    }
696
697
0
    std::unique_ptr<GooString> goo_mod_date;
698
699
0
    if (mod_date != static_cast<time_t>(-1)) {
700
0
        goo_mod_date = timeToDateString(&mod_date);
701
0
    }
702
703
0
    d->doc->setDocInfoModDate(std::move(goo_mod_date));
704
0
    return true;
705
0
}
706
707
/**
708
 Removes the %document's Info dictionary.
709
710
 \returns true on success, false on failure
711
 */
712
bool document::remove_info()
713
0
{
714
0
    if (d->is_locked) {
715
0
        return false;
716
0
    }
717
718
0
    d->doc->removeDocInfo();
719
0
    return true;
720
0
}
721
722
/**
723
 \returns whether the document is encrypted
724
 */
725
bool document::is_encrypted() const
726
0
{
727
0
    return d->doc->isEncrypted();
728
0
}
729
730
/**
731
 \returns whether the document is linearized
732
 */
733
bool document::is_linearized() const
734
0
{
735
0
    return d->doc->isLinearized();
736
0
}
737
738
/**
739
 \returns the form type within the document
740
 \since 25.04
741
 */
742
enum document::form_type document::form_type() const
743
0
{
744
0
    switch (d->doc->getCatalog()->getFormType()) {
745
0
    case Catalog::AcroForm:
746
0
        return form_type::acro;
747
0
    case Catalog::XfaForm:
748
0
        return form_type::xfa;
749
0
    case Catalog::NoForm:
750
0
    default:
751
0
        return form_type::none;
752
0
    }
753
0
}
754
755
/**
756
 \returns true if the document contains javascript
757
 \since 25.04
758
 */
759
bool document::has_javascript() const
760
0
{
761
0
    return d->doc->getCatalog()->numJS() > 0;
762
0
}
763
764
/**
765
 Check for available "document permission".
766
767
 \returns whether the specified permission is allowed
768
 */
769
bool document::has_permission(permission_enum which) const
770
0
{
771
0
    switch (which) {
772
0
    case perm_print:
773
0
        return d->doc->okToPrint();
774
0
    case perm_change:
775
0
        return d->doc->okToChange();
776
0
    case perm_copy:
777
0
        return d->doc->okToCopy();
778
0
    case perm_add_notes:
779
0
        return d->doc->okToAddNotes();
780
0
    case perm_fill_forms:
781
0
        return d->doc->okToFillForm();
782
0
    case perm_accessibility:
783
0
        return d->doc->okToAccessibility();
784
0
    case perm_assemble:
785
0
        return d->doc->okToAssemble();
786
0
    case perm_print_high_resolution:
787
0
        return d->doc->okToPrintHighRes();
788
0
    }
789
0
    return true;
790
0
}
791
792
/**
793
 Reads the %document metadata string.
794
795
 \return the %document metadata string
796
 */
797
ustring document::metadata() const
798
12.4k
{
799
12.4k
    std::optional<std::string> md(d->doc->getCatalog()->readMetadata());
800
12.4k
    if (md) {
801
1.45k
        return detail::unicode_string_to_ustring(md.value());
802
1.45k
    }
803
10.9k
    return ustring();
804
12.4k
}
805
806
/**
807
 Gets the IDs of the current PDF %document, if available.
808
809
 \param permanent_id if not NULL, will be set to the permanent ID of the %document
810
 \param update_id if not NULL, will be set to the update ID of the %document
811
812
 \returns whether the document has the IDs
813
814
 \since 0.16
815
 */
816
bool document::get_pdf_id(std::string *permanent_id, std::string *update_id) const
817
0
{
818
0
    GooString goo_permanent_id;
819
0
    GooString goo_update_id;
820
821
0
    if (!d->doc->getID(permanent_id ? &goo_permanent_id : nullptr, update_id ? &goo_update_id : nullptr)) {
822
0
        return false;
823
0
    }
824
825
0
    if (permanent_id) {
826
0
        *permanent_id = goo_permanent_id.c_str();
827
0
    }
828
0
    if (update_id) {
829
0
        *update_id = goo_update_id.c_str();
830
0
    }
831
832
0
    return true;
833
0
}
834
835
/**
836
 Document page count.
837
838
 \returns the number of pages of the document
839
 */
840
int document::pages() const
841
281k
{
842
281k
    return d->doc->getNumPages();
843
281k
}
844
845
/**
846
 Document page by label reading.
847
848
 This creates a new page representing the %document %page whose label is the
849
 specified \p label. If there is no page with that \p label, NULL is returned.
850
851
 \returns a new page object or NULL
852
 */
853
page *document::create_page(const ustring &label) const
854
41.9k
{
855
41.9k
    std::unique_ptr<GooString> goolabel(detail::ustring_to_unicode_GooString(label));
856
41.9k
    int index = 0;
857
858
41.9k
    if (!d->doc->getCatalog()->labelToIndex(goolabel->toStr(), &index)) {
859
5.06k
        return nullptr;
860
5.06k
    }
861
36.9k
    return create_page(index);
862
41.9k
}
863
864
/**
865
 Document page by index reading.
866
867
 This creates a new page representing the \p index -th %page of the %document.
868
 \note the page indexes are in the range [0, pages()[.
869
870
 \returns a new page object or NULL
871
 */
872
page *document::create_page(int index) const
873
239k
{
874
239k
    if (index >= 0 && index < d->doc->getNumPages()) {
875
239k
        page *p = new page(d, index);
876
239k
        if (p->d->page) {
877
77.6k
            return p;
878
77.6k
        }
879
161k
        delete p;
880
161k
        return nullptr;
881
239k
    }
882
0
    return nullptr;
883
239k
}
884
885
/**
886
 Reads all the font information of the %document.
887
888
 \note this can be slow for big documents; prefer the use of a font_iterator
889
 to read incrementally page by page
890
 \see create_font_iterator
891
 */
892
std::vector<font_info> document::fonts() const
893
12.4k
{
894
12.4k
    std::vector<font_info> result;
895
12.4k
    font_iterator it(0, d);
896
89.1k
    while (it.has_next()) {
897
76.7k
        const std::vector<font_info> l = it.next();
898
76.7k
        std::ranges::copy(l, std::back_inserter(result));
899
76.7k
    }
900
12.4k
    return result;
901
12.4k
}
902
903
/**
904
 Creates a new font iterator.
905
906
 This creates a new font iterator for reading the font information of the
907
 %document page by page, starting at the specified \p start_page (0 if not
908
 specified).
909
910
 \returns a new font iterator
911
 */
912
font_iterator *document::create_font_iterator(int start_page) const
913
0
{
914
0
    return new font_iterator(start_page, d);
915
0
}
916
917
/**
918
 Reads the TOC (table of contents) of the %document.
919
920
 \returns a new toc object if a TOC is available, NULL otherwise
921
 */
922
toc *document::create_toc() const
923
0
{
924
0
    return toc_private::load_from_outline(d->doc->getOutline());
925
0
}
926
927
/**
928
 Reads whether the current document has %document-level embedded files
929
 (attachments).
930
931
 This is a very fast way to know whether there are embedded files (also known
932
 as "attachments") at the %document-level. Note this does not take into account
933
 files embedded in other ways (e.g. to annotations).
934
935
 \returns whether the document has embedded files
936
 */
937
bool document::has_embedded_files() const
938
0
{
939
0
    return d->doc->getCatalog()->numEmbeddedFiles() > 0;
940
0
}
941
942
/**
943
 Reads all the %document-level embedded files of the %document.
944
945
 \returns the %document-level embedded files
946
 */
947
std::vector<embedded_file *> document::embedded_files() const
948
12.4k
{
949
12.4k
    if (d->is_locked) {
950
0
        return std::vector<embedded_file *>();
951
0
    }
952
953
12.4k
    if (d->embedded_files.empty() && d->doc->getCatalog()->numEmbeddedFiles() > 0) {
954
196
        const int num = d->doc->getCatalog()->numEmbeddedFiles();
955
196
        d->embedded_files.resize(num);
956
59.2k
        for (int i = 0; i < num; ++i) {
957
59.0k
            std::unique_ptr<FileSpec> fs = d->doc->getCatalog()->embeddedFile(i);
958
59.0k
            d->embedded_files[i] = embedded_file_private::create(std::move(fs));
959
59.0k
        }
960
196
    }
961
12.4k
    return d->embedded_files;
962
12.4k
}
963
964
/**
965
 Creates a map of all the named destinations in the %document.
966
967
 \note The destination names may contain \\0 and other binary values
968
 so they are not printable and cannot convert to null-terminated C strings.
969
970
 \returns the map of the each name and destination
971
972
 \since 0.74
973
 */
974
std::map<std::string, destination> document::create_destination_map() const
975
12.4k
{
976
12.4k
    std::map<std::string, destination> m;
977
978
12.4k
    Catalog *catalog = d->doc->getCatalog();
979
12.4k
    if (!catalog) {
980
0
        return m;
981
0
    }
982
983
    // Iterate from name-dict
984
12.4k
    const int nDests = catalog->numDests();
985
13.0k
    for (int i = 0; i < nDests; ++i) {
986
681
        std::string key(catalog->getDestsName(i));
987
681
        std::unique_ptr<LinkDest> link_dest = catalog->getDestsDest(i);
988
989
681
        if (link_dest) {
990
507
            destination dest(new destination_private(link_dest.get(), d->doc.get()));
991
992
507
            m.emplace(std::move(key), std::move(dest));
993
507
        }
994
681
    }
995
996
    // Iterate from name-tree
997
12.4k
    const int nDestsNameTree = catalog->numDestNameTree();
998
13.8k
    for (int i = 0; i < nDestsNameTree; ++i) {
999
1.43k
        std::string key(catalog->getDestNameTreeName(i)->toStr());
1000
1.43k
        std::unique_ptr<LinkDest> link_dest = catalog->getDestNameTreeDest(i);
1001
1002
1.43k
        if (link_dest) {
1003
304
            destination dest(new destination_private(link_dest.get(), d->doc.get()));
1004
1005
304
            m.emplace(std::move(key), std::move(dest));
1006
304
        }
1007
1.43k
    }
1008
1009
12.4k
    return m;
1010
12.4k
}
1011
1012
/**
1013
 Saves the %document to file \p file_name.
1014
1015
 \returns true on success, false on failure
1016
 */
1017
bool document::save(const std::string &file_name) const
1018
0
{
1019
0
    if (d->is_locked) {
1020
0
        return false;
1021
0
    }
1022
1023
0
    return d->doc->saveAs(file_name) == errNone;
1024
0
}
1025
1026
/**
1027
 Saves the original version of the %document to file \p file_name.
1028
1029
 \returns true on success, false on failure
1030
 */
1031
bool document::save_a_copy(const std::string &file_name) const
1032
0
{
1033
0
    if (d->is_locked) {
1034
0
        return false;
1035
0
    }
1036
1037
0
    return d->doc->saveWithoutChangesAs(file_name) == errNone;
1038
0
}
1039
1040
/**
1041
 Tries to load a PDF %document from the specified file.
1042
1043
 \param file_name the file to open
1044
 \returns a new document if the load succeeded (even if the document is locked),
1045
          NULL otherwise
1046
 */
1047
document *document::load_from_file(const std::string &file_name, const std::string &owner_password, const std::string &user_password)
1048
11.4k
{
1049
11.4k
    auto *doc = new document_private(std::make_unique<GooString>(file_name.c_str()), owner_password, user_password);
1050
11.4k
    return document_private::check_document(doc, nullptr);
1051
11.4k
}
1052
1053
/**
1054
 Tries to load a PDF %document from the specified data.
1055
1056
 \note if the loading succeeds, the document takes ownership of the
1057
       \p file_data (swap()ing it)
1058
1059
 \param file_data the data representing a document to open
1060
 \returns a new document if the load succeeded (even if the document is locked),
1061
          NULL otherwise
1062
 */
1063
document *document::load_from_data(byte_array *file_data, const std::string &owner_password, const std::string &user_password)
1064
0
{
1065
0
    if (!file_data || file_data->size() < 10) {
1066
0
        return nullptr;
1067
0
    }
1068
1069
0
    auto *doc = new document_private(file_data, owner_password, user_password);
1070
0
    return document_private::check_document(doc, file_data);
1071
0
}
1072
1073
/**
1074
 Tries to load a PDF %document from the specified data buffer.
1075
1076
 \note the buffer must remain valid for the whole lifetime of the returned
1077
       document
1078
1079
 \param file_data the data buffer representing a document to open
1080
 \param file_data_length the length of the data buffer
1081
1082
 \returns a new document if the load succeeded (even if the document is locked),
1083
          NULL otherwise
1084
1085
 \since 0.16
1086
 */
1087
document *document::load_from_raw_data(const char *file_data, int file_data_length, const std::string &owner_password, const std::string &user_password)
1088
43.0k
{
1089
43.0k
    if (!file_data || file_data_length < 10) {
1090
4
        return nullptr;
1091
4
    }
1092
1093
43.0k
    auto *doc = new document_private(file_data, file_data_length, owner_password, user_password);
1094
43.0k
    return document_private::check_document(doc, nullptr);
1095
43.0k
}