1/* poppler-document.cc: glib wrapper for poppler
2 * Copyright (C) 2005, Red Hat, Inc.
3 *
4 * Copyright (C) 2016 Jakub Alba <jakubalba@gmail.com>
5 * Copyright (C) 2018, 2019, 2021, 2022 Marek Kasik <mkasik@redhat.com>
6 * Copyright (C) 2019 Masamichi Hosoda <trueroad@trueroad.jp>
7 * Copyright (C) 2019, 2021, 2024 Oliver Sander <oliver.sander@tu-dresden.de>
8 * Copyright (C) 2020, 2022 Albert Astals Cid <aacid@kde.org>
9 * Copyright (C) 2021 André Guerreiro <aguerreiro1985@gmail.com>
10 * Copyright (C) 2024 g10 Code GmbH, Author: Sune Stolborg Vuorela <sune@vuorela.dk>
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2, or (at your option)
15 * any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
25 */
26
27#include "config.h"
28#include <cstring>
29
30#include <glib.h>
31
32#ifndef G_OS_WIN32
33# include <fcntl.h>
34# include <sys/stat.h>
35# include <sys/types.h>
36# include <unistd.h>
37#endif
38
39#ifndef __GI_SCANNER__
40# include <memory>
41
42# include <goo/gfile.h>
43# include <splash/SplashBitmap.h>
44# include <CachedFile.h>
45# include <DateInfo.h>
46# include <FILECacheLoader.h>
47# include <GlobalParams.h>
48# include <PDFDoc.h>
49# include <SignatureInfo.h>
50# include <Outline.h>
51# include <ErrorCodes.h>
52# include <UnicodeMap.h>
53# include <GfxState.h>
54# include <SplashOutputDev.h>
55# include <Stream.h>
56# include <FontInfo.h>
57# include <PDFDocEncoding.h>
58# include <OptionalContent.h>
59# include <ViewerPreferences.h>
60# include "UTF.h"
61#endif
62
63#include "poppler.h"
64#include "poppler-private.h"
65#include "poppler-enums.h"
66#include "poppler-input-stream.h"
67#include "poppler-cached-file-loader.h"
68
69#ifdef G_OS_WIN32
70# include <stringapiset.h>
71#endif
72
73/**
74 * SECTION:poppler-document
75 * @short_description: Information about a document
76 * @title: PopplerDocument
77 *
78 * The #PopplerDocument is an object used to refer to a main document.
79 */
80
81enum
82{
83 PROP_0,
84 PROP_TITLE,
85 PROP_FORMAT,
86 PROP_FORMAT_MAJOR,
87 PROP_FORMAT_MINOR,
88 PROP_SUBTYPE,
89 PROP_SUBTYPE_STRING,
90 PROP_SUBTYPE_PART,
91 PROP_SUBTYPE_CONF,
92 PROP_AUTHOR,
93 PROP_SUBJECT,
94 PROP_KEYWORDS,
95 PROP_CREATOR,
96 PROP_PRODUCER,
97 PROP_CREATION_DATE,
98 PROP_MOD_DATE,
99 PROP_LINEARIZED,
100 PROP_PAGE_LAYOUT,
101 PROP_PAGE_MODE,
102 PROP_VIEWER_PREFERENCES,
103 PROP_PERMISSIONS,
104 PROP_METADATA,
105 PROP_PRINT_SCALING,
106 PROP_PRINT_DUPLEX,
107 PROP_PRINT_N_COPIES,
108 PROP_CREATION_DATETIME,
109 PROP_MOD_DATETIME
110};
111
112static void poppler_document_layers_free(PopplerDocument *document);
113
114typedef struct _PopplerDocumentClass PopplerDocumentClass;
115struct _PopplerDocumentClass
116{
117 GObjectClass parent_class;
118};
119
120G_DEFINE_TYPE(PopplerDocument, poppler_document, G_TYPE_OBJECT)
121
122static PopplerDocument *_poppler_document_new_from_pdfdoc(std::unique_ptr<GlobalParamsIniter> &&initer, PDFDoc *newDoc, GError **error)
123{
124 PopplerDocument *document;
125
126 if (!newDoc->isOk()) {
127 int fopen_errno;
128 switch (newDoc->getErrorCode()) {
129 case errOpenFile:
130 // If there was an error opening the file, count it as a G_FILE_ERROR
131 // and set the GError parameters accordingly. (this assumes that the
132 // only way to get an errOpenFile error is if newDoc was created using
133 // a filename and thus fopen was called, which right now is true.
134 fopen_errno = newDoc->getFopenErrno();
135 g_set_error(err: error, G_FILE_ERROR, code: g_file_error_from_errno(err_no: fopen_errno), format: "%s", g_strerror(errnum: fopen_errno));
136 break;
137 case errBadCatalog:
138 g_set_error(err: error, POPPLER_ERROR, code: POPPLER_ERROR_BAD_CATALOG, format: "Failed to read the document catalog");
139 break;
140 case errDamaged:
141 g_set_error(err: error, POPPLER_ERROR, code: POPPLER_ERROR_DAMAGED, format: "PDF document is damaged");
142 break;
143 case errEncrypted:
144 g_set_error(err: error, POPPLER_ERROR, code: POPPLER_ERROR_ENCRYPTED, format: "Document is encrypted");
145 break;
146 default:
147 g_set_error(err: error, POPPLER_ERROR, code: POPPLER_ERROR_INVALID, format: "Failed to load document");
148 }
149
150 delete newDoc;
151 return nullptr;
152 }
153
154 document = (PopplerDocument *)g_object_new(POPPLER_TYPE_DOCUMENT, first_property_name: nullptr);
155 document->initer = std::move(initer);
156 document->doc = newDoc;
157
158 document->output_dev = new CairoOutputDev();
159 document->output_dev->startDoc(docA: document->doc);
160
161 return document;
162}
163
164static std::optional<GooString> poppler_password_to_latin1(const gchar *password)
165{
166 gchar *password_latin;
167
168 if (!password) {
169 return {};
170 }
171
172 password_latin = g_convert(str: password, len: -1, to_codeset: "ISO-8859-1", from_codeset: "UTF-8", bytes_read: nullptr, bytes_written: nullptr, error: nullptr);
173 std::optional<GooString> password_g = GooString(password_latin);
174 g_free(mem: password_latin);
175
176 return password_g;
177}
178
179/**
180 * poppler_document_new_from_file:
181 * @uri: uri of the file to load
182 * @password: (allow-none): password to unlock the file with, or %NULL
183 * @error: (allow-none): Return location for an error, or %NULL
184 *
185 * Creates a new #PopplerDocument. If %NULL is returned, then @error will be
186 * set. Possible errors include those in the #POPPLER_ERROR and #G_FILE_ERROR
187 * domains.
188 *
189 * Return value: A newly created #PopplerDocument, or %NULL
190 **/
191PopplerDocument *poppler_document_new_from_file(const char *uri, const char *password, GError **error)
192{
193 PDFDoc *newDoc;
194 char *filename;
195
196 auto initer = std::make_unique<GlobalParamsIniter>(args&: _poppler_error_cb);
197
198 filename = g_filename_from_uri(uri, hostname: nullptr, error);
199 if (!filename) {
200 return nullptr;
201 }
202
203 const std::optional<GooString> password_g = poppler_password_to_latin1(password);
204
205#ifdef G_OS_WIN32
206 wchar_t *filenameW;
207 int length;
208
209 length = MultiByteToWideChar(CP_UTF8, 0, filename, -1, nullptr, 0);
210
211 filenameW = new WCHAR[length];
212 if (!filenameW)
213 return nullptr;
214
215 length = MultiByteToWideChar(CP_UTF8, 0, filename, -1, filenameW, length);
216
217 newDoc = new PDFDoc(filenameW, length, password_g, password_g);
218 if (!newDoc->isOk() && newDoc->getErrorCode() == errEncrypted && password) {
219 /* Try again with original password (which comes from GTK in UTF8) Issue #824 */
220 delete newDoc;
221 newDoc = new PDFDoc(filenameW, length, GooString(password), GooString(password));
222 }
223 delete[] filenameW;
224#else
225 newDoc = new PDFDoc(std::make_unique<GooString>(args&: filename), password_g, password_g);
226 if (!newDoc->isOk() && newDoc->getErrorCode() == errEncrypted && password) {
227 /* Try again with original password (which comes from GTK in UTF8) Issue #824 */
228 delete newDoc;
229 newDoc = new PDFDoc(std::make_unique<GooString>(args&: filename), GooString(password), GooString(password));
230 }
231#endif
232 g_free(mem: filename);
233
234 return _poppler_document_new_from_pdfdoc(initer: std::move(initer), newDoc, error);
235}
236
237/**
238 * poppler_document_new_from_data:
239 * @data: (array length=length) (element-type guint8): the pdf data
240 * @length: the length of #data
241 * @password: (nullable): password to unlock the file with, or %NULL
242 * @error: (nullable): Return location for an error, or %NULL
243 *
244 * Creates a new #PopplerDocument. If %NULL is returned, then @error will be
245 * set. Possible errors include those in the #POPPLER_ERROR and #G_FILE_ERROR
246 * domains.
247 *
248 * Note that @data is not copied nor is a new reference to it created.
249 * It must remain valid and cannot be destroyed as long as the returned
250 * document exists.
251 *
252 * Return value: A newly created #PopplerDocument, or %NULL
253 *
254 * Deprecated: 0.82: This requires directly managing @length and @data.
255 * Use poppler_document_new_from_bytes() instead.
256 **/
257PopplerDocument *poppler_document_new_from_data(char *data, int length, const char *password, GError **error)
258{
259 PDFDoc *newDoc;
260 MemStream *str;
261
262 auto initer = std::make_unique<GlobalParamsIniter>(args&: _poppler_error_cb);
263
264 // create stream
265 str = new MemStream(data, 0, length, Object(objNull));
266
267 const std::optional<GooString> password_g = poppler_password_to_latin1(password);
268 newDoc = new PDFDoc(str, password_g, password_g);
269 if (!newDoc->isOk() && newDoc->getErrorCode() == errEncrypted && password) {
270 /* Try again with original password (which comes from GTK in UTF8) Issue #824 */
271 str = dynamic_cast<MemStream *>(str->copy());
272 delete newDoc;
273 newDoc = new PDFDoc(str, GooString(password), GooString(password));
274 }
275
276 return _poppler_document_new_from_pdfdoc(initer: std::move(initer), newDoc, error);
277}
278
279class BytesStream : public MemStream
280{
281 std::unique_ptr<GBytes, decltype(&g_bytes_unref)> m_bytes;
282
283public:
284 BytesStream(GBytes *bytes, Object &&dictA) : MemStream(static_cast<const char *>(g_bytes_get_data(bytes, size: nullptr)), 0, g_bytes_get_size(bytes), std::move(dictA)), m_bytes { g_bytes_ref(bytes), &g_bytes_unref } { }
285 ~BytesStream() override;
286};
287
288BytesStream::~BytesStream() = default;
289
290class OwningFileStream final : public FileStream
291{
292public:
293 OwningFileStream(std::unique_ptr<GooFile> fileA, Object &&dictA) : FileStream(fileA.get(), 0, false, fileA->size(), std::move(dictA)), file(std::move(fileA)) { }
294
295 ~OwningFileStream() override;
296
297private:
298 std::unique_ptr<GooFile> file;
299};
300
301OwningFileStream::~OwningFileStream() = default;
302
303/**
304 * poppler_document_new_from_bytes:
305 * @bytes: a #GBytes
306 * @password: (allow-none): password to unlock the file with, or %NULL
307 * @error: (allow-none): Return location for an error, or %NULL
308 *
309 * Creates a new #PopplerDocument from @bytes. The returned document
310 * will hold a reference to @bytes.
311 *
312 * On error, %NULL is returned, with @error set. Possible errors include
313 * those in the #POPPLER_ERROR and #G_FILE_ERROR domains.
314 *
315 * Return value: (transfer full): a newly created #PopplerDocument, or %NULL
316 *
317 * Since: 0.82
318 **/
319PopplerDocument *poppler_document_new_from_bytes(GBytes *bytes, const char *password, GError **error)
320{
321 PDFDoc *newDoc;
322 BytesStream *str;
323
324 g_return_val_if_fail(bytes != nullptr, nullptr);
325 g_return_val_if_fail(error == nullptr || *error == nullptr, nullptr);
326
327 auto initer = std::make_unique<GlobalParamsIniter>(args&: _poppler_error_cb);
328
329 // create stream
330 str = new BytesStream(bytes, Object(objNull));
331
332 const std::optional<GooString> password_g = poppler_password_to_latin1(password);
333 newDoc = new PDFDoc(str, password_g, password_g);
334 if (!newDoc->isOk() && newDoc->getErrorCode() == errEncrypted && password) {
335 /* Try again with original password (which comes from GTK in UTF8) Issue #824 */
336 str = dynamic_cast<BytesStream *>(str->copy());
337 delete newDoc;
338 newDoc = new PDFDoc(str, GooString(password), GooString(password));
339 }
340
341 return _poppler_document_new_from_pdfdoc(initer: std::move(initer), newDoc, error);
342}
343
344static inline gboolean stream_is_memory_buffer_or_local_file(GInputStream *stream)
345{
346 return G_IS_MEMORY_INPUT_STREAM(stream) || (G_IS_FILE_INPUT_STREAM(stream) && strcmp(s1: g_type_name_from_instance(instance: (GTypeInstance *)stream), s2: "GLocalFileInputStream") == 0);
347}
348
349/**
350 * poppler_document_new_from_stream:
351 * @stream: a #GInputStream to read from
352 * @length: the stream length, or -1 if not known
353 * @password: (allow-none): password to unlock the file with, or %NULL
354 * @cancellable: (allow-none): a #GCancellable, or %NULL
355 * @error: (allow-none): Return location for an error, or %NULL
356 *
357 * Creates a new #PopplerDocument reading the PDF contents from @stream.
358 * Note that the given #GInputStream must be seekable or %G_IO_ERROR_NOT_SUPPORTED
359 * will be returned.
360 * Possible errors include those in the #POPPLER_ERROR, #G_FILE_ERROR
361 * and #G_IO_ERROR domains.
362 *
363 * Returns: (transfer full): a new #PopplerDocument, or %NULL
364 *
365 * Since: 0.22
366 */
367PopplerDocument *poppler_document_new_from_stream(GInputStream *stream, goffset length, const char *password, GCancellable *cancellable, GError **error)
368{
369 PDFDoc *newDoc;
370 BaseStream *str;
371
372 g_return_val_if_fail(G_IS_INPUT_STREAM(stream), NULL);
373 g_return_val_if_fail(length == (goffset)-1 || length > 0, NULL);
374
375 auto initer = std::make_unique<GlobalParamsIniter>(args&: _poppler_error_cb);
376
377 if (!G_IS_SEEKABLE(stream) || !g_seekable_can_seek(G_SEEKABLE(stream))) {
378 g_set_error_literal(err: error, G_IO_ERROR, code: G_IO_ERROR_NOT_SUPPORTED, message: "Stream is not seekable");
379 return nullptr;
380 }
381
382 if (stream_is_memory_buffer_or_local_file(stream)) {
383 if (length == (goffset)-1) {
384 if (!g_seekable_seek(G_SEEKABLE(stream), offset: 0, type: G_SEEK_END, cancellable, error)) {
385 g_prefix_error(err: error, format: "Unable to determine length of stream: ");
386 return nullptr;
387 }
388 length = g_seekable_tell(G_SEEKABLE(stream));
389 }
390 str = new PopplerInputStream(stream, cancellable, 0, false, length, Object(objNull));
391 } else {
392 CachedFile *cachedFile = new CachedFile(new PopplerCachedFileLoader(stream, cancellable, length));
393 str = new CachedFileStream(cachedFile, 0, false, cachedFile->getLength(), Object(objNull));
394 }
395
396 const std::optional<GooString> password_g = poppler_password_to_latin1(password);
397 newDoc = new PDFDoc(str, password_g, password_g);
398 if (!newDoc->isOk() && newDoc->getErrorCode() == errEncrypted && password) {
399 /* Try again with original password (which comes from GTK in UTF8) Issue #824 */
400 str = str->copy();
401 delete newDoc;
402 newDoc = new PDFDoc(str, GooString(password), GooString(password));
403 }
404
405 return _poppler_document_new_from_pdfdoc(initer: std::move(initer), newDoc, error);
406}
407
408/**
409 * poppler_document_new_from_gfile:
410 * @file: a #GFile to load
411 * @password: (allow-none): password to unlock the file with, or %NULL
412 * @cancellable: (allow-none): a #GCancellable, or %NULL
413 * @error: (allow-none): Return location for an error, or %NULL
414 *
415 * Creates a new #PopplerDocument reading the PDF contents from @file.
416 * Possible errors include those in the #POPPLER_ERROR and #G_FILE_ERROR
417 * domains.
418 *
419 * Returns: (transfer full): a new #PopplerDocument, or %NULL
420 *
421 * Since: 0.22
422 */
423PopplerDocument *poppler_document_new_from_gfile(GFile *file, const char *password, GCancellable *cancellable, GError **error)
424{
425 PopplerDocument *document;
426 GFileInputStream *stream;
427
428 g_return_val_if_fail(G_IS_FILE(file), NULL);
429
430 if (g_file_is_native(file)) {
431 gchar *uri;
432
433 uri = g_file_get_uri(file);
434 document = poppler_document_new_from_file(uri, password, error);
435 g_free(mem: uri);
436
437 return document;
438 }
439
440 stream = g_file_read(file, cancellable, error);
441 if (!stream) {
442 return nullptr;
443 }
444
445 document = poppler_document_new_from_stream(G_INPUT_STREAM(stream), length: -1, password, cancellable, error);
446 g_object_unref(object: stream);
447
448 return document;
449}
450
451#ifndef G_OS_WIN32
452
453/**
454 * poppler_document_new_from_fd:
455 * @fd: a valid file descriptor
456 * @password: (allow-none): password to unlock the file with, or %NULL
457 * @error: (allow-none): Return location for an error, or %NULL
458 *
459 * Creates a new #PopplerDocument reading the PDF contents from the file
460 * descriptor @fd. @fd must refer to a regular file, or STDIN, and be open
461 * for reading.
462 * Possible errors include those in the #POPPLER_ERROR and #G_FILE_ERROR
463 * domains.
464 * Note that this function takes ownership of @fd; you must not operate on it
465 * again, nor close it.
466 *
467 * Returns: (transfer full): a new #PopplerDocument, or %NULL
468 *
469 * Since: 21.12.0
470 */
471PopplerDocument *poppler_document_new_from_fd(int fd, const char *password, GError **error)
472{
473 struct stat statbuf;
474 int flags;
475 BaseStream *stream;
476 PDFDoc *newDoc;
477
478 g_return_val_if_fail(fd != -1, nullptr);
479
480 auto initer = std::make_unique<GlobalParamsIniter>(args&: _poppler_error_cb);
481
482 if (fstat(fd: fd, buf: &statbuf) == -1 || (flags = fcntl(fd: fd, F_GETFL, &flags)) == -1) {
483 int errsv = errno;
484 g_set_error_literal(err: error, G_FILE_ERROR, code: g_file_error_from_errno(err_no: errsv), message: g_strerror(errnum: errsv));
485 close(fd: fd);
486 return nullptr;
487 }
488
489 switch (flags & O_ACCMODE) {
490 case O_RDONLY:
491 case O_RDWR:
492 break;
493 case O_WRONLY:
494 default:
495 g_set_error(err: error, G_FILE_ERROR, code: G_FILE_ERROR_BADF, format: "File descriptor %d is not readable", fd);
496 close(fd: fd);
497 return nullptr;
498 }
499
500 if (fd == fileno(stdin) || !S_ISREG(statbuf.st_mode)) {
501 FILE *file;
502 if (fd == fileno(stdin)) {
503 file = stdin;
504 } else {
505 file = fdopen(fd: fd, modes: "rb");
506 if (!file) {
507 int errsv = errno;
508 g_set_error_literal(err: error, G_FILE_ERROR, code: g_file_error_from_errno(err_no: errsv), message: g_strerror(errnum: errsv));
509 close(fd: fd);
510 return nullptr;
511 }
512 }
513
514 CachedFile *cachedFile = new CachedFile(new FILECacheLoader(file));
515 stream = new CachedFileStream(cachedFile, 0, false, cachedFile->getLength(), Object(objNull));
516 } else {
517 stream = new OwningFileStream(GooFile::open(fdA: fd), Object(objNull));
518 }
519
520 const std::optional<GooString> password_g = poppler_password_to_latin1(password);
521 newDoc = new PDFDoc(stream, password_g, password_g);
522 if (!newDoc->isOk() && newDoc->getErrorCode() == errEncrypted && password) {
523 /* Try again with original password (which comes from GTK in UTF8) Issue #824 */
524 stream = stream->copy();
525 delete newDoc;
526 newDoc = new PDFDoc(stream, GooString(password), GooString(password));
527 }
528
529 return _poppler_document_new_from_pdfdoc(initer: std::move(initer), newDoc, error);
530}
531
532#endif /* !G_OS_WIN32 */
533
534static gboolean handle_save_error(int err_code, GError **error)
535{
536 switch (err_code) {
537 case errNone:
538 break;
539 case errOpenFile:
540 g_set_error(err: error, POPPLER_ERROR, code: POPPLER_ERROR_OPEN_FILE, format: "Failed to open file for writing");
541 break;
542 case errEncrypted:
543 g_set_error(err: error, POPPLER_ERROR, code: POPPLER_ERROR_ENCRYPTED, format: "Document is encrypted");
544 break;
545 default:
546 g_set_error(err: error, POPPLER_ERROR, code: POPPLER_ERROR_INVALID, format: "Failed to save document");
547 }
548
549 return err_code == errNone;
550}
551
552/**
553 * poppler_document_save:
554 * @document: a #PopplerDocument
555 * @uri: uri of file to save
556 * @error: (allow-none): return location for an error, or %NULL
557 *
558 * Saves @document. Any change made in the document such as
559 * form fields filled, annotations added or modified
560 * will be saved.
561 * If @error is set, %FALSE will be returned. Possible errors
562 * include those in the #G_FILE_ERROR domain.
563 *
564 * Return value: %TRUE, if the document was successfully saved
565 **/
566gboolean poppler_document_save(PopplerDocument *document, const char *uri, GError **error)
567{
568 char *filename;
569 gboolean retval = FALSE;
570
571 g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), FALSE);
572
573 filename = g_filename_from_uri(uri, hostname: nullptr, error);
574 if (filename != nullptr) {
575 GooString fname(filename);
576 int err_code;
577 g_free(mem: filename);
578
579 err_code = document->doc->saveAs(name: fname);
580 retval = handle_save_error(err_code, error);
581 }
582
583 return retval;
584}
585
586/**
587 * poppler_document_save_a_copy:
588 * @document: a #PopplerDocument
589 * @uri: uri of file to save
590 * @error: (allow-none): return location for an error, or %NULL
591 *
592 * Saves a copy of the original @document.
593 * Any change made in the document such as
594 * form fields filled by the user will not be saved.
595 * If @error is set, %FALSE will be returned. Possible errors
596 * include those in the #G_FILE_ERROR domain.
597 *
598 * Return value: %TRUE, if the document was successfully saved
599 **/
600gboolean poppler_document_save_a_copy(PopplerDocument *document, const char *uri, GError **error)
601{
602 char *filename;
603 gboolean retval = FALSE;
604
605 g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), FALSE);
606
607 filename = g_filename_from_uri(uri, hostname: nullptr, error);
608 if (filename != nullptr) {
609 GooString fname(filename);
610 int err_code;
611 g_free(mem: filename);
612
613 err_code = document->doc->saveWithoutChangesAs(name: fname);
614 retval = handle_save_error(err_code, error);
615 }
616
617 return retval;
618}
619
620#ifndef G_OS_WIN32
621
622/**
623 * poppler_document_save_to_fd:
624 * @document: a #PopplerDocument
625 * @fd: a valid file descriptor open for writing
626 * @include_changes: whether to include user changes (e.g. form fills)
627 * @error: (allow-none): return location for an error, or %NULL
628 *
629 * Saves @document. Any change made in the document such as
630 * form fields filled, annotations added or modified
631 * will be saved if @include_changes is %TRUE, or discarded i
632 * @include_changes is %FALSE.
633 *
634 * Note that this function takes ownership of @fd; you must not operate on it
635 * again, nor close it.
636 *
637 * If @error is set, %FALSE will be returned. Possible errors
638 * include those in the #G_FILE_ERROR domain.
639 *
640 * Return value: %TRUE, if the document was successfully saved
641 *
642 * Since: 21.12.0
643 **/
644gboolean poppler_document_save_to_fd(PopplerDocument *document, int fd, gboolean include_changes, GError **error)
645{
646 FILE *file;
647 OutStream *stream;
648 int rv;
649
650 g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), FALSE);
651 g_return_val_if_fail(fd != -1, FALSE);
652
653 file = fdopen(fd: fd, modes: "wb");
654 if (file == nullptr) {
655 int errsv = errno;
656 g_set_error(err: error, G_FILE_ERROR, code: g_file_error_from_errno(err_no: errsv), format: "Failed to open FD %d for writing: %s", fd, g_strerror(errnum: errsv));
657 return FALSE;
658 }
659
660 stream = new FileOutStream(file, 0);
661 if (include_changes) {
662 rv = document->doc->saveAs(outStr: stream);
663 } else {
664 rv = document->doc->saveWithoutChangesAs(outStr: stream);
665 }
666 delete stream;
667
668 return handle_save_error(err_code: rv, error);
669}
670
671#endif /* !G_OS_WIN32 */
672
673static void poppler_document_finalize(GObject *object)
674{
675 PopplerDocument *document = POPPLER_DOCUMENT(object);
676
677 poppler_document_layers_free(document);
678 delete document->output_dev;
679 delete document->doc;
680 delete document->initer.release();
681
682 G_OBJECT_CLASS(poppler_document_parent_class)->finalize(object);
683}
684
685/**
686 * poppler_document_get_id:
687 * @document: A #PopplerDocument
688 * @permanent_id: (out) (allow-none): location to store an allocated string, use g_free() to free the returned string
689 * @update_id: (out) (allow-none): location to store an allocated string, use g_free() to free the returned string
690 *
691 * Returns the PDF file identifier represented as two byte string arrays of size 32.
692 * @permanent_id is the permanent identifier that is built based on the file
693 * contents at the time it was originally created, so that this identifer
694 * never changes. @update_id is the update identifier that is built based on
695 * the file contents at the time it was last updated.
696 *
697 * Note that returned strings are not null-terminated, they have a fixed
698 * size of 32 bytes.
699 *
700 * Returns: %TRUE if the @document contains an id, %FALSE otherwise
701 *
702 * Since: 0.16
703 */
704gboolean poppler_document_get_id(PopplerDocument *document, gchar **permanent_id, gchar **update_id)
705{
706 GooString permanent;
707 GooString update;
708 gboolean retval = FALSE;
709
710 g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), FALSE);
711
712 if (permanent_id) {
713 *permanent_id = nullptr;
714 }
715 if (update_id) {
716 *update_id = nullptr;
717 }
718
719 if (document->doc->getID(permanent_id: permanent_id ? &permanent : nullptr, update_id: update_id ? &update : nullptr)) {
720 if (permanent_id) {
721 *permanent_id = g_new(char, 32);
722 memcpy(dest: *permanent_id, src: permanent.c_str(), n: 32);
723 }
724 if (update_id) {
725 *update_id = g_new(char, 32);
726 memcpy(dest: *update_id, src: update.c_str(), n: 32);
727 }
728
729 retval = TRUE;
730 }
731
732 return retval;
733}
734
735/**
736 * poppler_document_get_n_pages:
737 * @document: A #PopplerDocument
738 *
739 * Returns the number of pages in a loaded document.
740 *
741 * Return value: Number of pages
742 **/
743int poppler_document_get_n_pages(PopplerDocument *document)
744{
745 g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), 0);
746
747 return document->doc->getNumPages();
748}
749
750/**
751 * poppler_document_get_page:
752 * @document: A #PopplerDocument
753 * @index: a page index
754 *
755 * Returns the #PopplerPage indexed at @index. This object is owned by the
756 * caller.
757 *
758 * Return value: (transfer full) : The #PopplerPage at @index
759 **/
760PopplerPage *poppler_document_get_page(PopplerDocument *document, int index)
761{
762 Page *page;
763
764 g_return_val_if_fail(0 <= index && index < poppler_document_get_n_pages(document), NULL);
765
766 page = document->doc->getPage(page: index + 1);
767 if (!page) {
768 return nullptr;
769 }
770
771 return _poppler_page_new(document, page, index);
772}
773
774/**
775 * poppler_document_get_page_by_label:
776 * @document: A #PopplerDocument
777 * @label: a page label
778 *
779 * Returns the #PopplerPage reference by @label. This object is owned by the
780 * caller. @label is a human-readable string representation of the page number,
781 * and can be document specific. Typically, it is a value such as "iii" or "3".
782 *
783 * By default, "1" refers to the first page.
784 *
785 * Return value: (transfer full) :The #PopplerPage referenced by @label
786 **/
787PopplerPage *poppler_document_get_page_by_label(PopplerDocument *document, const char *label)
788{
789 Catalog *catalog;
790 GooString label_g(label);
791 int index;
792
793 catalog = document->doc->getCatalog();
794 if (!catalog->labelToIndex(label: &label_g, index: &index)) {
795 return nullptr;
796 }
797
798 return poppler_document_get_page(document, index);
799}
800
801/**
802 * poppler_document_get_n_attachments:
803 * @document: A #PopplerDocument
804 *
805 * Returns the number of attachments in a loaded document.
806 * See also poppler_document_get_attachments()
807 *
808 * Return value: Number of attachments
809 *
810 * Since: 0.18
811 */
812guint poppler_document_get_n_attachments(PopplerDocument *document)
813{
814 Catalog *catalog;
815
816 g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), 0);
817
818 catalog = document->doc->getCatalog();
819
820 return catalog && catalog->isOk() ? catalog->numEmbeddedFiles() : 0;
821}
822
823/**
824 * poppler_document_has_attachments:
825 * @document: A #PopplerDocument
826 *
827 * Returns %TRUE of @document has any attachments.
828 *
829 * Return value: %TRUE, if @document has attachments.
830 **/
831gboolean poppler_document_has_attachments(PopplerDocument *document)
832{
833 g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), FALSE);
834
835 return (poppler_document_get_n_attachments(document) != 0);
836}
837
838/**
839 * poppler_document_get_attachments:
840 * @document: A #PopplerDocument
841 *
842 * Returns a #GList containing #PopplerAttachment<!-- -->s. These attachments
843 * are unowned, and must be unreffed, and the list must be freed with
844 * g_list_free().
845 *
846 * Return value: (element-type PopplerAttachment) (transfer full): a list of available attachments.
847 **/
848GList *poppler_document_get_attachments(PopplerDocument *document)
849{
850 Catalog *catalog;
851 int n_files, i;
852 GList *retval = nullptr;
853
854 g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), NULL);
855
856 catalog = document->doc->getCatalog();
857 if (catalog == nullptr || !catalog->isOk()) {
858 return nullptr;
859 }
860
861 n_files = catalog->numEmbeddedFiles();
862 for (i = 0; i < n_files; i++) {
863 PopplerAttachment *attachment;
864
865 const std::unique_ptr<FileSpec> emb_file = catalog->embeddedFile(i);
866 if (!emb_file->isOk() || !emb_file->getEmbeddedFile()->isOk()) {
867 continue;
868 }
869
870 attachment = _poppler_attachment_new(file: emb_file.get());
871
872 if (attachment != nullptr) {
873 retval = g_list_prepend(list: retval, data: attachment);
874 }
875 }
876 return g_list_reverse(list: retval);
877}
878
879/**
880 * poppler_named_dest_from_bytestring:
881 * @data: (array length=length): the bytestring data
882 * @length: the bytestring length
883 *
884 * Converts a bytestring into a zero-terminated string suitable to
885 * pass to poppler_document_find_dest().
886 *
887 * Note that the returned string has no defined encoding and is not
888 * suitable for display to the user.
889 *
890 * The returned data must be freed using g_free().
891 *
892 * Returns: (transfer full): the named dest
893 *
894 * Since: 0.73
895 */
896char *poppler_named_dest_from_bytestring(const guint8 *data, gsize length)
897{
898 const guint8 *p, *pend;
899 char *dest, *q;
900
901 g_return_val_if_fail(length != 0 || data != nullptr, nullptr);
902 /* Each source byte needs maximally 2 destination chars (\\ or \0) */
903 q = dest = (gchar *)g_malloc(n_bytes: length * 2 + 1);
904
905 pend = data + length;
906 for (p = data; p < pend; ++p) {
907 switch (*p) {
908 case '\0':
909 *q++ = '\\';
910 *q++ = '0';
911 break;
912 case '\\':
913 *q++ = '\\';
914 *q++ = '\\';
915 break;
916 default:
917 *q++ = *p;
918 break;
919 }
920 }
921
922 *q = 0; /* zero terminate */
923 return dest;
924}
925
926/**
927 * poppler_named_dest_to_bytestring:
928 * @name: the named dest string
929 * @length: (out): a location to store the length of the returned bytestring
930 *
931 * Converts a named dest string (e.g. from #PopplerDest.named_dest) into a
932 * bytestring, inverting the transformation of
933 * poppler_named_dest_from_bytestring().
934 *
935 * Note that the returned data is not zero terminated and may also
936 * contains embedded NUL bytes.
937 *
938 * If @name is not a valid named dest string, returns %NULL.
939 *
940 * The returned data must be freed using g_free().
941 *
942 * Returns: (array length=length) (transfer full) (nullable): a new bytestring,
943 * or %NULL
944 *
945 * Since: 0.73
946 */
947guint8 *poppler_named_dest_to_bytestring(const char *name, gsize *length)
948{
949 const char *p;
950 guint8 *data, *q;
951 gsize len;
952
953 g_return_val_if_fail(name != nullptr, nullptr);
954 g_return_val_if_fail(length != nullptr, nullptr);
955
956 len = strlen(s: name);
957 q = data = (guint8 *)g_malloc(n_bytes: len);
958 for (p = name; *p; ++p) {
959 if (*p == '\\') {
960 p++;
961 len--;
962 if (*p == '0') {
963 *q++ = '\0';
964 } else if (*p == '\\') {
965 *q++ = '\\';
966 } else {
967 goto invalid;
968 }
969 } else {
970 *q++ = *p;
971 }
972 }
973
974 *length = len;
975 return data;
976
977invalid:
978 g_free(mem: data);
979 *length = 0;
980 return nullptr;
981}
982
983/**
984 * poppler_document_find_dest:
985 * @document: A #PopplerDocument
986 * @link_name: a named destination
987 *
988 * Creates a #PopplerDest for the named destination @link_name in @document.
989 *
990 * Note that named destinations are bytestrings, not string. That means that
991 * unless @link_name was returned by a poppler function (e.g. is
992 * #PopplerDest.named_dest), it needs to be converted to string
993 * using poppler_named_dest_from_bytestring() before being passed to this
994 * function.
995 *
996 * The returned value must be freed with poppler_dest_free().
997 *
998 * Return value: (transfer full): a new #PopplerDest destination, or %NULL if
999 * @link_name is not a destination.
1000 **/
1001PopplerDest *poppler_document_find_dest(PopplerDocument *document, const gchar *link_name)
1002{
1003 g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), nullptr);
1004 g_return_val_if_fail(link_name != nullptr, nullptr);
1005
1006 gsize len;
1007 guint8 *data = poppler_named_dest_to_bytestring(name: link_name, length: &len);
1008 if (data == nullptr) {
1009 return nullptr;
1010 }
1011
1012 GooString g_link_name((const char *)data, (int)len);
1013 g_free(mem: data);
1014
1015 std::unique_ptr<LinkDest> link_dest = document->doc->findDest(name: &g_link_name);
1016 if (link_dest == nullptr) {
1017 return nullptr;
1018 }
1019
1020 PopplerDest *dest = _poppler_dest_new_goto(document, link_dest: link_dest.get());
1021
1022 return dest;
1023}
1024
1025static gint _poppler_dest_compare_keys(gconstpointer a, gconstpointer b, gpointer user_data)
1026{
1027 return g_strcmp0(str1: static_cast<const gchar *>(a), str2: static_cast<const gchar *>(b));
1028}
1029
1030static void _poppler_dest_destroy_value(gpointer value)
1031{
1032 poppler_dest_free(dest: static_cast<PopplerDest *>(value));
1033}
1034
1035/**
1036 * poppler_document_create_dests_tree:
1037 * @document: A #PopplerDocument
1038 *
1039 * Creates a balanced binary tree of all named destinations in @document
1040 *
1041 * The tree key is strings in the form returned by
1042 * poppler_named_dest_to_bytestring() which constains a destination name.
1043 * The tree value is the #PopplerDest which contains a named destination.
1044 * The return value must be freed with g_tree_destroy().
1045 *
1046 * Returns: (transfer full) (nullable): the #GTree, or %NULL
1047 * Since: 0.78
1048 **/
1049GTree *poppler_document_create_dests_tree(PopplerDocument *document)
1050{
1051 GTree *tree;
1052 Catalog *catalog;
1053 PopplerDest *dest;
1054 int i;
1055
1056 g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), nullptr);
1057
1058 catalog = document->doc->getCatalog();
1059 if (catalog == nullptr) {
1060 return nullptr;
1061 }
1062
1063 tree = g_tree_new_full(key_compare_func: _poppler_dest_compare_keys, key_compare_data: nullptr, key_destroy_func: g_free, value_destroy_func: _poppler_dest_destroy_value);
1064
1065 // Iterate from name-dict
1066 const int nDests = catalog->numDests();
1067 for (i = 0; i < nDests; ++i) {
1068 // The names of name-dict cannot contain \0,
1069 // so we can use strlen().
1070 auto name = catalog->getDestsName(i);
1071 std::unique_ptr<LinkDest> link_dest = catalog->getDestsDest(i);
1072 if (link_dest) {
1073 gchar *key = poppler_named_dest_from_bytestring(data: reinterpret_cast<const guint8 *>(name), length: strlen(s: name));
1074 dest = _poppler_dest_new_goto(document, link_dest: link_dest.get());
1075 g_tree_insert(tree, key, value: dest);
1076 }
1077 }
1078
1079 // Iterate form name-tree
1080 const int nDestsNameTree = catalog->numDestNameTree();
1081 for (i = 0; i < nDestsNameTree; ++i) {
1082 auto name = catalog->getDestNameTreeName(i);
1083 std::unique_ptr<LinkDest> link_dest = catalog->getDestNameTreeDest(i);
1084 if (link_dest) {
1085 gchar *key = poppler_named_dest_from_bytestring(data: reinterpret_cast<const guint8 *>(name->c_str()), length: name->getLength());
1086 dest = _poppler_dest_new_goto(document, link_dest: link_dest.get());
1087 g_tree_insert(tree, key, value: dest);
1088 }
1089 }
1090
1091 return tree;
1092}
1093
1094char *_poppler_goo_string_to_utf8(const GooString *s)
1095{
1096 if (s == nullptr) {
1097 return nullptr;
1098 }
1099
1100 char *result;
1101
1102 if (hasUnicodeByteOrderMark(s: s->toStr())) {
1103 result = g_convert(str: s->c_str() + 2, len: s->getLength() - 2, to_codeset: "UTF-8", from_codeset: "UTF-16BE", bytes_read: nullptr, bytes_written: nullptr, error: nullptr);
1104 } else if (hasUnicodeByteOrderMarkLE(s: s->toStr())) {
1105 result = g_convert(str: s->c_str() + 2, len: s->getLength() - 2, to_codeset: "UTF-8", from_codeset: "UTF-16LE", bytes_read: nullptr, bytes_written: nullptr, error: nullptr);
1106 } else {
1107 int len;
1108 gunichar *ucs4_temp;
1109 int i;
1110
1111 len = s->getLength();
1112 ucs4_temp = g_new(gunichar, len + 1);
1113 for (i = 0; i < len; ++i) {
1114 ucs4_temp[i] = pdfDocEncoding[(unsigned char)s->getChar(i)];
1115 }
1116 ucs4_temp[i] = 0;
1117
1118 result = g_ucs4_to_utf8(str: ucs4_temp, len: -1, items_read: nullptr, items_written: nullptr, error: nullptr);
1119
1120 g_free(mem: ucs4_temp);
1121 }
1122
1123 return result;
1124}
1125
1126static GooString *_poppler_goo_string_from_utf8(const gchar *src)
1127{
1128 if (src == nullptr) {
1129 return nullptr;
1130 }
1131
1132 gsize outlen;
1133
1134 gchar *utf16 = g_convert(str: src, len: -1, to_codeset: "UTF-16BE", from_codeset: "UTF-8", bytes_read: nullptr, bytes_written: &outlen, error: nullptr);
1135 if (utf16 == nullptr) {
1136 return nullptr;
1137 }
1138
1139 GooString *result = new GooString(utf16, outlen);
1140 g_free(mem: utf16);
1141
1142 if (!hasUnicodeByteOrderMark(s: result->toStr())) {
1143 prependUnicodeByteOrderMark(s&: result->toNonConstStr());
1144 }
1145
1146 return result;
1147}
1148
1149static PopplerPageLayout convert_page_layout(Catalog::PageLayout pageLayout)
1150{
1151 switch (pageLayout) {
1152 case Catalog::pageLayoutSinglePage:
1153 return POPPLER_PAGE_LAYOUT_SINGLE_PAGE;
1154 case Catalog::pageLayoutOneColumn:
1155 return POPPLER_PAGE_LAYOUT_ONE_COLUMN;
1156 case Catalog::pageLayoutTwoColumnLeft:
1157 return POPPLER_PAGE_LAYOUT_TWO_COLUMN_LEFT;
1158 case Catalog::pageLayoutTwoColumnRight:
1159 return POPPLER_PAGE_LAYOUT_TWO_COLUMN_RIGHT;
1160 case Catalog::pageLayoutTwoPageLeft:
1161 return POPPLER_PAGE_LAYOUT_TWO_PAGE_LEFT;
1162 case Catalog::pageLayoutTwoPageRight:
1163 return POPPLER_PAGE_LAYOUT_TWO_PAGE_RIGHT;
1164 case Catalog::pageLayoutNone:
1165 default:
1166 return POPPLER_PAGE_LAYOUT_UNSET;
1167 }
1168}
1169
1170static PopplerPageMode convert_page_mode(Catalog::PageMode pageMode)
1171{
1172 switch (pageMode) {
1173 case Catalog::pageModeOutlines:
1174 return POPPLER_PAGE_MODE_USE_OUTLINES;
1175 case Catalog::pageModeThumbs:
1176 return POPPLER_PAGE_MODE_USE_THUMBS;
1177 case Catalog::pageModeFullScreen:
1178 return POPPLER_PAGE_MODE_FULL_SCREEN;
1179 case Catalog::pageModeOC:
1180 return POPPLER_PAGE_MODE_USE_OC;
1181 case Catalog::pageModeAttach:
1182 return POPPLER_PAGE_MODE_USE_ATTACHMENTS;
1183 case Catalog::pageModeNone:
1184 default:
1185 return POPPLER_PAGE_MODE_UNSET;
1186 }
1187}
1188
1189static PopplerPDFSubtype convert_pdf_subtype(PDFSubtype pdfSubtype)
1190{
1191 switch (pdfSubtype) {
1192 case subtypePDFA:
1193 return POPPLER_PDF_SUBTYPE_PDF_A;
1194 case subtypePDFE:
1195 return POPPLER_PDF_SUBTYPE_PDF_E;
1196 case subtypePDFUA:
1197 return POPPLER_PDF_SUBTYPE_PDF_UA;
1198 case subtypePDFVT:
1199 return POPPLER_PDF_SUBTYPE_PDF_VT;
1200 case subtypePDFX:
1201 return POPPLER_PDF_SUBTYPE_PDF_X;
1202 case subtypeNone:
1203 return POPPLER_PDF_SUBTYPE_NONE;
1204 case subtypeNull:
1205 default:
1206 return POPPLER_PDF_SUBTYPE_UNSET;
1207 }
1208}
1209
1210static PopplerPDFPart convert_pdf_subtype_part(PDFSubtypePart pdfSubtypePart)
1211{
1212 switch (pdfSubtypePart) {
1213 case subtypePart1:
1214 return POPPLER_PDF_SUBTYPE_PART_1;
1215 case subtypePart2:
1216 return POPPLER_PDF_SUBTYPE_PART_2;
1217 case subtypePart3:
1218 return POPPLER_PDF_SUBTYPE_PART_3;
1219 case subtypePart4:
1220 return POPPLER_PDF_SUBTYPE_PART_4;
1221 case subtypePart5:
1222 return POPPLER_PDF_SUBTYPE_PART_5;
1223 case subtypePart6:
1224 return POPPLER_PDF_SUBTYPE_PART_6;
1225 case subtypePart7:
1226 return POPPLER_PDF_SUBTYPE_PART_7;
1227 case subtypePart8:
1228 return POPPLER_PDF_SUBTYPE_PART_8;
1229 case subtypePartNone:
1230 return POPPLER_PDF_SUBTYPE_PART_NONE;
1231 case subtypePartNull:
1232 default:
1233 return POPPLER_PDF_SUBTYPE_PART_UNSET;
1234 }
1235}
1236
1237static PopplerPDFConformance convert_pdf_subtype_conformance(PDFSubtypeConformance pdfSubtypeConf)
1238{
1239 switch (pdfSubtypeConf) {
1240 case subtypeConfA:
1241 return POPPLER_PDF_SUBTYPE_CONF_A;
1242 case subtypeConfB:
1243 return POPPLER_PDF_SUBTYPE_CONF_B;
1244 case subtypeConfG:
1245 return POPPLER_PDF_SUBTYPE_CONF_G;
1246 case subtypeConfN:
1247 return POPPLER_PDF_SUBTYPE_CONF_N;
1248 case subtypeConfP:
1249 return POPPLER_PDF_SUBTYPE_CONF_P;
1250 case subtypeConfPG:
1251 return POPPLER_PDF_SUBTYPE_CONF_PG;
1252 case subtypeConfU:
1253 return POPPLER_PDF_SUBTYPE_CONF_U;
1254 case subtypeConfNone:
1255 return POPPLER_PDF_SUBTYPE_CONF_NONE;
1256 case subtypeConfNull:
1257 default:
1258 return POPPLER_PDF_SUBTYPE_CONF_UNSET;
1259 }
1260}
1261
1262/**
1263 * poppler_document_get_pdf_version_string:
1264 * @document: A #PopplerDocument
1265 *
1266 * Returns the PDF version of @document as a string (e.g. PDF-1.6)
1267 *
1268 * Return value: a new allocated string containing the PDF version
1269 * of @document, or %NULL
1270 *
1271 * Since: 0.16
1272 **/
1273gchar *poppler_document_get_pdf_version_string(PopplerDocument *document)
1274{
1275 gchar *retval;
1276
1277 g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), NULL);
1278
1279 retval = g_strndup(str: "PDF-", n: 15); /* allocates 16 chars, pads with \0s */
1280 g_ascii_formatd(buffer: retval + 4, buf_len: 15 + 1 - 4, format: "%.2g", d: document->doc->getPDFMajorVersion() + document->doc->getPDFMinorVersion() / 10.0);
1281 return retval;
1282}
1283
1284/**
1285 * poppler_document_get_pdf_version:
1286 * @document: A #PopplerDocument
1287 * @major_version: (out) (nullable): return location for the PDF major version number
1288 * @minor_version: (out) (nullable): return location for the PDF minor version number
1289 *
1290 * Updates values referenced by @major_version & @minor_version with the
1291 * major and minor PDF versions of @document.
1292 *
1293 * Since: 0.16
1294 **/
1295void poppler_document_get_pdf_version(PopplerDocument *document, guint *major_version, guint *minor_version)
1296{
1297 g_return_if_fail(POPPLER_IS_DOCUMENT(document));
1298
1299 if (major_version) {
1300 *major_version = document->doc->getPDFMajorVersion();
1301 }
1302 if (minor_version) {
1303 *minor_version = document->doc->getPDFMinorVersion();
1304 }
1305}
1306
1307/**
1308 * poppler_document_get_title:
1309 * @document: A #PopplerDocument
1310 *
1311 * Returns the document's title
1312 *
1313 * Return value: a new allocated string containing the title
1314 * of @document, or %NULL
1315 *
1316 * Since: 0.16
1317 **/
1318gchar *poppler_document_get_title(PopplerDocument *document)
1319{
1320 g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), NULL);
1321
1322 const std::unique_ptr<GooString> goo_title = document->doc->getDocInfoTitle();
1323 return _poppler_goo_string_to_utf8(s: goo_title.get());
1324}
1325
1326/**
1327 * poppler_document_set_title:
1328 * @document: A #PopplerDocument
1329 * @title: A new title
1330 *
1331 * Sets the document's title. If @title is %NULL, Title entry
1332 * is removed from the document's Info dictionary.
1333 *
1334 * Since: 0.46
1335 **/
1336void poppler_document_set_title(PopplerDocument *document, const gchar *title)
1337{
1338 g_return_if_fail(POPPLER_IS_DOCUMENT(document));
1339
1340 GooString *goo_title;
1341 if (!title) {
1342 goo_title = nullptr;
1343 } else {
1344 goo_title = _poppler_goo_string_from_utf8(src: title);
1345 if (!goo_title) {
1346 return;
1347 }
1348 }
1349 document->doc->setDocInfoTitle(goo_title);
1350}
1351
1352/**
1353 * poppler_document_get_author:
1354 * @document: A #PopplerDocument
1355 *
1356 * Returns the author of the document
1357 *
1358 * Return value: a new allocated string containing the author
1359 * of @document, or %NULL
1360 *
1361 * Since: 0.16
1362 **/
1363gchar *poppler_document_get_author(PopplerDocument *document)
1364{
1365 g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), NULL);
1366
1367 const std::unique_ptr<GooString> goo_author = document->doc->getDocInfoAuthor();
1368 return _poppler_goo_string_to_utf8(s: goo_author.get());
1369}
1370
1371/**
1372 * poppler_document_set_author:
1373 * @document: A #PopplerDocument
1374 * @author: A new author
1375 *
1376 * Sets the document's author. If @author is %NULL, Author
1377 * entry is removed from the document's Info dictionary.
1378 *
1379 * Since: 0.46
1380 **/
1381void poppler_document_set_author(PopplerDocument *document, const gchar *author)
1382{
1383 g_return_if_fail(POPPLER_IS_DOCUMENT(document));
1384
1385 GooString *goo_author;
1386 if (!author) {
1387 goo_author = nullptr;
1388 } else {
1389 goo_author = _poppler_goo_string_from_utf8(src: author);
1390 if (!goo_author) {
1391 return;
1392 }
1393 }
1394 document->doc->setDocInfoAuthor(goo_author);
1395}
1396
1397/**
1398 * poppler_document_get_subject:
1399 * @document: A #PopplerDocument
1400 *
1401 * Returns the subject of the document
1402 *
1403 * Return value: a new allocated string containing the subject
1404 * of @document, or %NULL
1405 *
1406 * Since: 0.16
1407 **/
1408gchar *poppler_document_get_subject(PopplerDocument *document)
1409{
1410 g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), NULL);
1411
1412 const std::unique_ptr<GooString> goo_subject = document->doc->getDocInfoSubject();
1413 return _poppler_goo_string_to_utf8(s: goo_subject.get());
1414}
1415
1416/**
1417 * poppler_document_set_subject:
1418 * @document: A #PopplerDocument
1419 * @subject: A new subject
1420 *
1421 * Sets the document's subject. If @subject is %NULL, Subject
1422 * entry is removed from the document's Info dictionary.
1423 *
1424 * Since: 0.46
1425 **/
1426void poppler_document_set_subject(PopplerDocument *document, const gchar *subject)
1427{
1428 g_return_if_fail(POPPLER_IS_DOCUMENT(document));
1429
1430 GooString *goo_subject;
1431 if (!subject) {
1432 goo_subject = nullptr;
1433 } else {
1434 goo_subject = _poppler_goo_string_from_utf8(src: subject);
1435 if (!goo_subject) {
1436 return;
1437 }
1438 }
1439 document->doc->setDocInfoSubject(goo_subject);
1440}
1441
1442/**
1443 * poppler_document_get_keywords:
1444 * @document: A #PopplerDocument
1445 *
1446 * Returns the keywords associated to the document
1447 *
1448 * Return value: a new allocated string containing keywords associated
1449 * to @document, or %NULL
1450 *
1451 * Since: 0.16
1452 **/
1453gchar *poppler_document_get_keywords(PopplerDocument *document)
1454{
1455 g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), NULL);
1456
1457 const std::unique_ptr<GooString> goo_keywords = document->doc->getDocInfoKeywords();
1458 return _poppler_goo_string_to_utf8(s: goo_keywords.get());
1459}
1460
1461/**
1462 * poppler_document_set_keywords:
1463 * @document: A #PopplerDocument
1464 * @keywords: New keywords
1465 *
1466 * Sets the document's keywords. If @keywords is %NULL,
1467 * Keywords entry is removed from the document's Info dictionary.
1468 *
1469 * Since: 0.46
1470 **/
1471void poppler_document_set_keywords(PopplerDocument *document, const gchar *keywords)
1472{
1473 g_return_if_fail(POPPLER_IS_DOCUMENT(document));
1474
1475 GooString *goo_keywords;
1476 if (!keywords) {
1477 goo_keywords = nullptr;
1478 } else {
1479 goo_keywords = _poppler_goo_string_from_utf8(src: keywords);
1480 if (!goo_keywords) {
1481 return;
1482 }
1483 }
1484 document->doc->setDocInfoKeywords(goo_keywords);
1485}
1486
1487/**
1488 * poppler_document_get_creator:
1489 * @document: A #PopplerDocument
1490 *
1491 * Returns the creator of the document. If the document was converted
1492 * from another format, the creator is the name of the product
1493 * that created the original document from which it was converted.
1494 *
1495 * Return value: a new allocated string containing the creator
1496 * of @document, or %NULL
1497 *
1498 * Since: 0.16
1499 **/
1500gchar *poppler_document_get_creator(PopplerDocument *document)
1501{
1502 g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), NULL);
1503
1504 const std::unique_ptr<GooString> goo_creator = document->doc->getDocInfoCreator();
1505 return _poppler_goo_string_to_utf8(s: goo_creator.get());
1506}
1507
1508/**
1509 * poppler_document_set_creator:
1510 * @document: A #PopplerDocument
1511 * @creator: A new creator
1512 *
1513 * Sets the document's creator. If @creator is %NULL, Creator
1514 * entry is removed from the document's Info dictionary.
1515 *
1516 * Since: 0.46
1517 **/
1518void poppler_document_set_creator(PopplerDocument *document, const gchar *creator)
1519{
1520 g_return_if_fail(POPPLER_IS_DOCUMENT(document));
1521
1522 GooString *goo_creator;
1523 if (!creator) {
1524 goo_creator = nullptr;
1525 } else {
1526 goo_creator = _poppler_goo_string_from_utf8(src: creator);
1527 if (!goo_creator) {
1528 return;
1529 }
1530 }
1531 document->doc->setDocInfoCreator(goo_creator);
1532}
1533
1534/**
1535 * poppler_document_get_producer:
1536 * @document: A #PopplerDocument
1537 *
1538 * Returns the producer of the document. If the document was converted
1539 * from another format, the producer is the name of the product
1540 * that converted it to PDF
1541 *
1542 * Return value: a new allocated string containing the producer
1543 * of @document, or %NULL
1544 *
1545 * Since: 0.16
1546 **/
1547gchar *poppler_document_get_producer(PopplerDocument *document)
1548{
1549 g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), NULL);
1550
1551 const std::unique_ptr<GooString> goo_producer = document->doc->getDocInfoProducer();
1552 return _poppler_goo_string_to_utf8(s: goo_producer.get());
1553}
1554
1555/**
1556 * poppler_document_set_producer:
1557 * @document: A #PopplerDocument
1558 * @producer: A new producer
1559 *
1560 * Sets the document's producer. If @producer is %NULL,
1561 * Producer entry is removed from the document's Info dictionary.
1562 *
1563 * Since: 0.46
1564 **/
1565void poppler_document_set_producer(PopplerDocument *document, const gchar *producer)
1566{
1567 g_return_if_fail(POPPLER_IS_DOCUMENT(document));
1568
1569 GooString *goo_producer;
1570 if (!producer) {
1571 goo_producer = nullptr;
1572 } else {
1573 goo_producer = _poppler_goo_string_from_utf8(src: producer);
1574 if (!goo_producer) {
1575 return;
1576 }
1577 }
1578 document->doc->setDocInfoProducer(goo_producer);
1579}
1580
1581/**
1582 * poppler_document_get_creation_date:
1583 * @document: A #PopplerDocument
1584 *
1585 * Returns the date the document was created as seconds since the Epoch
1586 *
1587 * Return value: the date the document was created, or -1
1588 *
1589 * Since: 0.16
1590 **/
1591time_t poppler_document_get_creation_date(PopplerDocument *document)
1592{
1593 g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), (time_t)-1);
1594
1595 const std::unique_ptr<GooString> str = document->doc->getDocInfoCreatDate();
1596 if (!str) {
1597 return (time_t)-1;
1598 }
1599
1600 time_t date;
1601 gboolean success = _poppler_convert_pdf_date_to_gtime(date: str.get(), gdate: &date);
1602
1603 return (success) ? date : (time_t)-1;
1604}
1605
1606/**
1607 * poppler_document_set_creation_date:
1608 * @document: A #PopplerDocument
1609 * @creation_date: A new creation date
1610 *
1611 * Sets the document's creation date. If @creation_date is -1, CreationDate
1612 * entry is removed from the document's Info dictionary.
1613 *
1614 * Since: 0.46
1615 **/
1616void poppler_document_set_creation_date(PopplerDocument *document, time_t creation_date)
1617{
1618 g_return_if_fail(POPPLER_IS_DOCUMENT(document));
1619
1620 GooString *str = creation_date == (time_t)-1 ? nullptr : timeToDateString(timeA: &creation_date);
1621 document->doc->setDocInfoCreatDate(str);
1622}
1623
1624/**
1625 * poppler_document_get_creation_date_time:
1626 * @document: A #PopplerDocument
1627 *
1628 * Returns the date the document was created as a #GDateTime
1629 *
1630 * Returns: (nullable): the date the document was created, or %NULL
1631 *
1632 * Since: 20.09.0
1633 **/
1634GDateTime *poppler_document_get_creation_date_time(PopplerDocument *document)
1635{
1636 g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), nullptr);
1637
1638 std::unique_ptr<GooString> str { document->doc->getDocInfoCreatDate() };
1639
1640 if (!str) {
1641 return nullptr;
1642 }
1643
1644 return _poppler_convert_pdf_date_to_date_time(date: str.get());
1645}
1646
1647/**
1648 * poppler_document_set_creation_date_time:
1649 * @document: A #PopplerDocument
1650 * @creation_datetime: (nullable): A new creation #GDateTime
1651 *
1652 * Sets the document's creation date. If @creation_datetime is %NULL,
1653 * CreationDate entry is removed from the document's Info dictionary.
1654 *
1655 * Since: 20.09.0
1656 **/
1657void poppler_document_set_creation_date_time(PopplerDocument *document, GDateTime *creation_datetime)
1658{
1659 g_return_if_fail(POPPLER_IS_DOCUMENT(document));
1660
1661 GooString *str = nullptr;
1662
1663 if (creation_datetime) {
1664 str = _poppler_convert_date_time_to_pdf_date(datetime: creation_datetime);
1665 }
1666
1667 document->doc->setDocInfoCreatDate(str);
1668}
1669
1670/**
1671 * poppler_document_get_modification_date:
1672 * @document: A #PopplerDocument
1673 *
1674 * Returns the date the document was most recently modified as seconds since the Epoch
1675 *
1676 * Return value: the date the document was most recently modified, or -1
1677 *
1678 * Since: 0.16
1679 **/
1680time_t poppler_document_get_modification_date(PopplerDocument *document)
1681{
1682 g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), (time_t)-1);
1683
1684 const std::unique_ptr<GooString> str = document->doc->getDocInfoModDate();
1685 if (!str) {
1686 return (time_t)-1;
1687 }
1688
1689 time_t date;
1690 gboolean success = _poppler_convert_pdf_date_to_gtime(date: str.get(), gdate: &date);
1691
1692 return (success) ? date : (time_t)-1;
1693}
1694
1695/**
1696 * poppler_document_set_modification_date:
1697 * @document: A #PopplerDocument
1698 * @modification_date: A new modification date
1699 *
1700 * Sets the document's modification date. If @modification_date is -1, ModDate
1701 * entry is removed from the document's Info dictionary.
1702 *
1703 * Since: 0.46
1704 **/
1705void poppler_document_set_modification_date(PopplerDocument *document, time_t modification_date)
1706{
1707 g_return_if_fail(POPPLER_IS_DOCUMENT(document));
1708
1709 GooString *str = modification_date == (time_t)-1 ? nullptr : timeToDateString(timeA: &modification_date);
1710 document->doc->setDocInfoModDate(str);
1711}
1712
1713/**
1714 * poppler_document_get_modification_date_time:
1715 * @document: A #PopplerDocument
1716 *
1717 * Returns the date the document was most recently modified as a #GDateTime
1718 *
1719 * Returns: (nullable): the date the document was modified, or %NULL
1720 *
1721 * Since: 20.09.0
1722 **/
1723GDateTime *poppler_document_get_modification_date_time(PopplerDocument *document)
1724{
1725 g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), nullptr);
1726
1727 std::unique_ptr<GooString> str { document->doc->getDocInfoModDate() };
1728
1729 if (!str) {
1730 return nullptr;
1731 }
1732
1733 return _poppler_convert_pdf_date_to_date_time(date: str.get());
1734}
1735
1736/**
1737 * poppler_document_set_modification_date_time:
1738 * @document: A #PopplerDocument
1739 * @modification_datetime: (nullable): A new modification #GDateTime
1740 *
1741 * Sets the document's modification date. If @modification_datetime is %NULL,
1742 * ModDate entry is removed from the document's Info dictionary.
1743 *
1744 * Since: 20.09.0
1745 **/
1746void poppler_document_set_modification_date_time(PopplerDocument *document, GDateTime *modification_datetime)
1747{
1748 g_return_if_fail(POPPLER_IS_DOCUMENT(document));
1749
1750 GooString *str = nullptr;
1751
1752 if (modification_datetime) {
1753 str = _poppler_convert_date_time_to_pdf_date(datetime: modification_datetime);
1754 }
1755
1756 document->doc->setDocInfoModDate(str);
1757}
1758
1759/**
1760 * poppler_document_is_linearized:
1761 * @document: A #PopplerDocument
1762 *
1763 * Returns whether @document is linearized or not. Linearization of PDF
1764 * enables efficient incremental access of the PDF file in a network environment.
1765 *
1766 * Return value: %TRUE if @document is linearized, %FALSE otherwise
1767 *
1768 * Since: 0.16
1769 **/
1770gboolean poppler_document_is_linearized(PopplerDocument *document)
1771{
1772 g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), FALSE);
1773
1774 return document->doc->isLinearized();
1775}
1776
1777/**
1778 * poppler_document_get_n_signatures:
1779 * @document: A #PopplerDocument
1780 *
1781 * Returns how many digital signatures @document contains.
1782 * PDF digital signatures ensure that the content hash not been altered since last edit and
1783 * that it was produced by someone the user can trust
1784 *
1785 * Return value: The number of signatures found in the document
1786 *
1787 * Since: 21.12.0
1788 **/
1789gint poppler_document_get_n_signatures(const PopplerDocument *document)
1790{
1791 g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), 0);
1792
1793 return document->doc->getSignatureFields().size();
1794}
1795
1796/**
1797 * poppler_document_get_signature_fields:
1798 * @document: A #PopplerDocument
1799 *
1800 * Returns a #GList containing all signature #PopplerFormField<!-- -->s in the document.
1801 *
1802 * Return value: (element-type PopplerFormField) (transfer full): a list of all signature form fields.
1803 *
1804 * Since: 22.02.0
1805 **/
1806GList *poppler_document_get_signature_fields(PopplerDocument *document)
1807{
1808 std::vector<FormFieldSignature *> signature_fields;
1809 FormWidget *widget;
1810 GList *result = nullptr;
1811 gsize i;
1812
1813 signature_fields = document->doc->getSignatureFields();
1814
1815 for (i = 0; i < signature_fields.size(); i++) {
1816 widget = signature_fields[i]->getCreateWidget();
1817
1818 if (widget != nullptr) {
1819 result = g_list_prepend(list: result, data: _poppler_form_field_new(document, field: widget));
1820 }
1821 }
1822
1823 return g_list_reverse(list: result);
1824}
1825
1826/**
1827 * poppler_document_get_page_layout:
1828 * @document: A #PopplerDocument
1829 *
1830 * Returns the page layout that should be used when the document is opened
1831 *
1832 * Return value: a #PopplerPageLayout that should be used when the document is opened
1833 *
1834 * Since: 0.16
1835 **/
1836PopplerPageLayout poppler_document_get_page_layout(PopplerDocument *document)
1837{
1838 Catalog *catalog;
1839
1840 g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), POPPLER_PAGE_LAYOUT_UNSET);
1841
1842 catalog = document->doc->getCatalog();
1843 if (catalog && catalog->isOk()) {
1844 return convert_page_layout(pageLayout: catalog->getPageLayout());
1845 }
1846
1847 return POPPLER_PAGE_LAYOUT_UNSET;
1848}
1849
1850/**
1851 * poppler_document_get_page_mode:
1852 * @document: A #PopplerDocument
1853 *
1854 * Returns a #PopplerPageMode representing how the document should
1855 * be initially displayed when opened.
1856 *
1857 * Return value: a #PopplerPageMode that should be used when document is opened
1858 *
1859 * Since: 0.16
1860 **/
1861PopplerPageMode poppler_document_get_page_mode(PopplerDocument *document)
1862{
1863 Catalog *catalog;
1864
1865 g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), POPPLER_PAGE_MODE_UNSET);
1866
1867 catalog = document->doc->getCatalog();
1868 if (catalog && catalog->isOk()) {
1869 return convert_page_mode(pageMode: catalog->getPageMode());
1870 }
1871
1872 return POPPLER_PAGE_MODE_UNSET;
1873}
1874
1875/**
1876 * poppler_document_get_print_scaling:
1877 * @document: A #PopplerDocument
1878 *
1879 * Returns the print scaling value suggested by author of the document.
1880 *
1881 * Return value: a #PopplerPrintScaling that should be used when document is printed
1882 *
1883 * Since: 0.73
1884 **/
1885PopplerPrintScaling poppler_document_get_print_scaling(PopplerDocument *document)
1886{
1887 Catalog *catalog;
1888 ViewerPreferences *preferences;
1889 PopplerPrintScaling print_scaling = POPPLER_PRINT_SCALING_APP_DEFAULT;
1890
1891 g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), POPPLER_PRINT_SCALING_APP_DEFAULT);
1892
1893 catalog = document->doc->getCatalog();
1894 if (catalog && catalog->isOk()) {
1895 preferences = catalog->getViewerPreferences();
1896 if (preferences) {
1897 switch (preferences->getPrintScaling()) {
1898 default:
1899 case ViewerPreferences::PrintScaling::printScalingAppDefault:
1900 print_scaling = POPPLER_PRINT_SCALING_APP_DEFAULT;
1901 break;
1902 case ViewerPreferences::PrintScaling::printScalingNone:
1903 print_scaling = POPPLER_PRINT_SCALING_NONE;
1904 break;
1905 }
1906 }
1907 }
1908
1909 return print_scaling;
1910}
1911
1912/**
1913 * poppler_document_get_print_duplex:
1914 * @document: A #PopplerDocument
1915 *
1916 * Returns the duplex mode value suggested for printing by author of the document.
1917 * Value POPPLER_PRINT_DUPLEX_NONE means that the document does not specify this
1918 * preference.
1919 *
1920 * Returns: a #PopplerPrintDuplex that should be used when document is printed
1921 *
1922 * Since: 0.80
1923 **/
1924PopplerPrintDuplex poppler_document_get_print_duplex(PopplerDocument *document)
1925{
1926 Catalog *catalog;
1927 ViewerPreferences *preferences;
1928 PopplerPrintDuplex duplex = POPPLER_PRINT_DUPLEX_NONE;
1929
1930 g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), POPPLER_PRINT_DUPLEX_NONE);
1931
1932 catalog = document->doc->getCatalog();
1933 if (catalog && catalog->isOk()) {
1934 preferences = catalog->getViewerPreferences();
1935 if (preferences) {
1936 switch (preferences->getDuplex()) {
1937 default:
1938 case ViewerPreferences::Duplex::duplexNone:
1939 duplex = POPPLER_PRINT_DUPLEX_NONE;
1940 break;
1941 case ViewerPreferences::Duplex::duplexSimplex:
1942 duplex = POPPLER_PRINT_DUPLEX_SIMPLEX;
1943 break;
1944 case ViewerPreferences::Duplex::duplexDuplexFlipShortEdge:
1945 duplex = POPPLER_PRINT_DUPLEX_DUPLEX_FLIP_SHORT_EDGE;
1946 break;
1947 case ViewerPreferences::Duplex::duplexDuplexFlipLongEdge:
1948 duplex = POPPLER_PRINT_DUPLEX_DUPLEX_FLIP_LONG_EDGE;
1949 break;
1950 }
1951 }
1952 }
1953
1954 return duplex;
1955}
1956
1957/**
1958 * poppler_document_get_print_n_copies:
1959 * @document: A #PopplerDocument
1960 *
1961 * Returns the suggested number of copies to be printed.
1962 * This preference should be applied only if returned value
1963 * is greater than 1 since value 1 usually means that
1964 * the document does not specify it.
1965 *
1966 * Returns: Number of copies
1967 *
1968 * Since: 0.80
1969 **/
1970gint poppler_document_get_print_n_copies(PopplerDocument *document)
1971{
1972 Catalog *catalog;
1973 ViewerPreferences *preferences;
1974 gint retval = 1;
1975
1976 g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), 1);
1977
1978 catalog = document->doc->getCatalog();
1979 if (catalog && catalog->isOk()) {
1980 preferences = catalog->getViewerPreferences();
1981 if (preferences) {
1982 retval = preferences->getNumCopies();
1983 }
1984 }
1985
1986 return retval;
1987}
1988
1989/**
1990 * poppler_document_get_print_page_ranges:
1991 * @document: A #PopplerDocument
1992 * @n_ranges: (out): return location for number of ranges
1993 *
1994 * Returns the suggested page ranges to print in the form of array
1995 * of #PopplerPageRange<!-- -->s and number of ranges.
1996 * %NULL pointer means that the document does not specify page ranges
1997 * for printing.
1998 *
1999 * Returns: (array length=n_ranges) (transfer full): an array
2000 * of #PopplerPageRange<!-- -->s or %NULL. Free the array when
2001 * it is no longer needed.
2002 *
2003 * Since: 0.80
2004 **/
2005PopplerPageRange *poppler_document_get_print_page_ranges(PopplerDocument *document, int *n_ranges)
2006{
2007 Catalog *catalog;
2008 ViewerPreferences *preferences;
2009 std::vector<std::pair<int, int>> ranges;
2010 PopplerPageRange *result = nullptr;
2011
2012 g_return_val_if_fail(n_ranges != nullptr, nullptr);
2013 *n_ranges = 0;
2014 g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), nullptr);
2015
2016 catalog = document->doc->getCatalog();
2017 if (catalog && catalog->isOk()) {
2018 preferences = catalog->getViewerPreferences();
2019 if (preferences) {
2020 ranges = preferences->getPrintPageRange();
2021
2022 *n_ranges = ranges.size();
2023 result = g_new(PopplerPageRange, ranges.size());
2024 for (size_t i = 0; i < ranges.size(); ++i) {
2025 result[i].start_page = ranges[i].first;
2026 result[i].end_page = ranges[i].second;
2027 }
2028 }
2029 }
2030
2031 return result;
2032}
2033
2034/**
2035 * poppler_document_get_permissions:
2036 * @document: A #PopplerDocument
2037 *
2038 * Returns the flags specifying which operations are permitted when the document is opened.
2039 *
2040 * Return value: a set of flags from #PopplerPermissions enumeration
2041 *
2042 * Since: 0.16
2043 **/
2044PopplerPermissions poppler_document_get_permissions(PopplerDocument *document)
2045{
2046 guint flag = 0;
2047
2048 g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), POPPLER_PERMISSIONS_FULL);
2049
2050 if (document->doc->okToPrint()) {
2051 flag |= POPPLER_PERMISSIONS_OK_TO_PRINT;
2052 }
2053 if (document->doc->okToChange()) {
2054 flag |= POPPLER_PERMISSIONS_OK_TO_MODIFY;
2055 }
2056 if (document->doc->okToCopy()) {
2057 flag |= POPPLER_PERMISSIONS_OK_TO_COPY;
2058 }
2059 if (document->doc->okToAddNotes()) {
2060 flag |= POPPLER_PERMISSIONS_OK_TO_ADD_NOTES;
2061 }
2062 if (document->doc->okToFillForm()) {
2063 flag |= POPPLER_PERMISSIONS_OK_TO_FILL_FORM;
2064 }
2065 if (document->doc->okToAccessibility()) {
2066 flag |= POPPLER_PERMISSIONS_OK_TO_EXTRACT_CONTENTS;
2067 }
2068 if (document->doc->okToAssemble()) {
2069 flag |= POPPLER_PERMISSIONS_OK_TO_ASSEMBLE;
2070 }
2071 if (document->doc->okToPrintHighRes()) {
2072 flag |= POPPLER_PERMISSIONS_OK_TO_PRINT_HIGH_RESOLUTION;
2073 }
2074
2075 return (PopplerPermissions)flag;
2076}
2077
2078/**
2079 * poppler_document_get_pdf_subtype_string:
2080 * @document: A #PopplerDocument
2081 *
2082 * Returns the PDF subtype version of @document as a string.
2083 *
2084 * Returns: (transfer full) (nullable): a newly allocated string containing
2085 * the PDF subtype version of @document, or %NULL
2086 *
2087 * Since: 0.70
2088 **/
2089gchar *poppler_document_get_pdf_subtype_string(PopplerDocument *document)
2090{
2091 g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), NULL);
2092
2093 std::unique_ptr<GooString> infostring;
2094
2095 switch (document->doc->getPDFSubtype()) {
2096 case subtypePDFA:
2097 infostring = document->doc->getDocInfoStringEntry(key: "GTS_PDFA1Version");
2098 break;
2099 case subtypePDFE:
2100 infostring = document->doc->getDocInfoStringEntry(key: "GTS_PDFEVersion");
2101 break;
2102 case subtypePDFUA:
2103 infostring = document->doc->getDocInfoStringEntry(key: "GTS_PDFUAVersion");
2104 break;
2105 case subtypePDFVT:
2106 infostring = document->doc->getDocInfoStringEntry(key: "GTS_PDFVTVersion");
2107 break;
2108 case subtypePDFX:
2109 infostring = document->doc->getDocInfoStringEntry(key: "GTS_PDFXVersion");
2110 break;
2111 case subtypeNone:
2112 case subtypeNull:
2113 default: {
2114 /* nothing */
2115 }
2116 }
2117
2118 return _poppler_goo_string_to_utf8(s: infostring.get());
2119}
2120
2121/**
2122 * poppler_document_get_pdf_subtype:
2123 * @document: A #PopplerDocument
2124 *
2125 * Returns the subtype of @document as a #PopplerPDFSubtype.
2126 *
2127 * Returns: the document's subtype
2128 *
2129 * Since: 0.70
2130 **/
2131PopplerPDFSubtype poppler_document_get_pdf_subtype(PopplerDocument *document)
2132{
2133 g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), POPPLER_PDF_SUBTYPE_NONE);
2134
2135 return convert_pdf_subtype(pdfSubtype: document->doc->getPDFSubtype());
2136}
2137
2138/**
2139 * poppler_document_get_pdf_part:
2140 * @document: A #PopplerDocument
2141 *
2142 * Returns the part of the conforming standard that the @document adheres to
2143 * as a #PopplerPDFSubtype.
2144 *
2145 * Returns: the document's subtype part
2146 *
2147 * Since: 0.70
2148 **/
2149PopplerPDFPart poppler_document_get_pdf_part(PopplerDocument *document)
2150{
2151 g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), POPPLER_PDF_SUBTYPE_PART_NONE);
2152
2153 return convert_pdf_subtype_part(pdfSubtypePart: document->doc->getPDFSubtypePart());
2154}
2155
2156/**
2157 * poppler_document_get_pdf_conformance:
2158 * @document: A #PopplerDocument
2159 *
2160 * Returns the conformance level of the @document as #PopplerPDFConformance.
2161 *
2162 * Returns: the document's subtype conformance level
2163 *
2164 * Since: 0.70
2165 **/
2166PopplerPDFConformance poppler_document_get_pdf_conformance(PopplerDocument *document)
2167{
2168 g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), POPPLER_PDF_SUBTYPE_CONF_NONE);
2169
2170 return convert_pdf_subtype_conformance(pdfSubtypeConf: document->doc->getPDFSubtypeConformance());
2171}
2172
2173/**
2174 * poppler_document_get_metadata:
2175 * @document: A #PopplerDocument
2176 *
2177 * Returns the XML metadata string of the document
2178 *
2179 * Return value: a new allocated string containing the XML
2180 * metadata, or %NULL
2181 *
2182 * Since: 0.16
2183 **/
2184gchar *poppler_document_get_metadata(PopplerDocument *document)
2185{
2186 Catalog *catalog;
2187 gchar *retval = nullptr;
2188
2189 g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), NULL);
2190
2191 catalog = document->doc->getCatalog();
2192 if (catalog && catalog->isOk()) {
2193 std::unique_ptr<GooString> s = catalog->readMetadata();
2194
2195 if (s) {
2196 retval = g_strdup(str: s->c_str());
2197 }
2198 }
2199
2200 return retval;
2201}
2202
2203/**
2204 * poppler_document_reset_form:
2205 * @document: A #PopplerDocument
2206 * @fields: (element-type utf8) (nullable): list of fields to reset
2207 * @exclude_fields: whether to reset all fields except those in @fields
2208 *
2209 * Resets the form fields specified by fields if exclude_fields is FALSE.
2210 * Resets all others if exclude_fields is TRUE.
2211 * All form fields are reset regardless of the exclude_fields flag
2212 * if fields is empty.
2213 *
2214 * Since: 0.90
2215 **/
2216void poppler_document_reset_form(PopplerDocument *document, GList *fields, gboolean exclude_fields)
2217{
2218 std::vector<std::string> list;
2219 Catalog *catalog;
2220 GList *iter;
2221 Form *form;
2222
2223 g_return_if_fail(POPPLER_IS_DOCUMENT(document));
2224
2225 catalog = document->doc->getCatalog();
2226 if (catalog && catalog->isOk()) {
2227 form = catalog->getForm();
2228
2229 if (form) {
2230 for (iter = fields; iter != nullptr; iter = iter->next) {
2231 list.emplace_back(args: (char *)iter->data);
2232 }
2233
2234 form->reset(fields: list, excludeFields: exclude_fields);
2235 }
2236 }
2237}
2238
2239/**
2240 * poppler_document_has_javascript:
2241 * @document: A #PopplerDocument
2242 *
2243 * Returns whether @document has any javascript in it.
2244 *
2245 * Since: 0.90
2246 **/
2247gboolean poppler_document_has_javascript(PopplerDocument *document)
2248{
2249 g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), FALSE);
2250
2251 return document->doc->hasJavascript();
2252}
2253
2254static void poppler_document_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
2255{
2256 PopplerDocument *document = POPPLER_DOCUMENT(object);
2257 guint version;
2258
2259 switch (prop_id) {
2260 case PROP_TITLE:
2261 g_value_take_string(value, v_string: poppler_document_get_title(document));
2262 break;
2263 case PROP_FORMAT:
2264 g_value_take_string(value, v_string: poppler_document_get_pdf_version_string(document));
2265 break;
2266 case PROP_FORMAT_MAJOR:
2267 poppler_document_get_pdf_version(document, major_version: &version, minor_version: nullptr);
2268 g_value_set_uint(value, v_uint: version);
2269 break;
2270 case PROP_FORMAT_MINOR:
2271 poppler_document_get_pdf_version(document, major_version: nullptr, minor_version: &version);
2272 g_value_set_uint(value, v_uint: version);
2273 break;
2274 case PROP_AUTHOR:
2275 g_value_take_string(value, v_string: poppler_document_get_author(document));
2276 break;
2277 case PROP_SUBJECT:
2278 g_value_take_string(value, v_string: poppler_document_get_subject(document));
2279 break;
2280 case PROP_KEYWORDS:
2281 g_value_take_string(value, v_string: poppler_document_get_keywords(document));
2282 break;
2283 case PROP_CREATOR:
2284 g_value_take_string(value, v_string: poppler_document_get_creator(document));
2285 break;
2286 case PROP_PRODUCER:
2287 g_value_take_string(value, v_string: poppler_document_get_producer(document));
2288 break;
2289 case PROP_CREATION_DATE:
2290 g_value_set_int(value, v_int: poppler_document_get_creation_date(document));
2291 break;
2292 case PROP_CREATION_DATETIME:
2293 g_value_take_boxed(value, v_boxed: poppler_document_get_creation_date_time(document));
2294 break;
2295 case PROP_MOD_DATE:
2296 g_value_set_int(value, v_int: poppler_document_get_modification_date(document));
2297 break;
2298 case PROP_MOD_DATETIME:
2299 g_value_take_boxed(value, v_boxed: poppler_document_get_modification_date_time(document));
2300 break;
2301 case PROP_LINEARIZED:
2302 g_value_set_boolean(value, v_boolean: poppler_document_is_linearized(document));
2303 break;
2304 case PROP_PAGE_LAYOUT:
2305 g_value_set_enum(value, v_enum: poppler_document_get_page_layout(document));
2306 break;
2307 case PROP_PAGE_MODE:
2308 g_value_set_enum(value, v_enum: poppler_document_get_page_mode(document));
2309 break;
2310 case PROP_VIEWER_PREFERENCES:
2311 /* FIXME: write... */
2312 g_value_set_flags(value, v_flags: POPPLER_VIEWER_PREFERENCES_UNSET);
2313 break;
2314 case PROP_PRINT_SCALING:
2315 g_value_set_enum(value, v_enum: poppler_document_get_print_scaling(document));
2316 break;
2317 case PROP_PRINT_DUPLEX:
2318 g_value_set_enum(value, v_enum: poppler_document_get_print_duplex(document));
2319 break;
2320 case PROP_PRINT_N_COPIES:
2321 g_value_set_int(value, v_int: poppler_document_get_print_n_copies(document));
2322 break;
2323 case PROP_PERMISSIONS:
2324 g_value_set_flags(value, v_flags: poppler_document_get_permissions(document));
2325 break;
2326 case PROP_SUBTYPE:
2327 g_value_set_enum(value, v_enum: poppler_document_get_pdf_subtype(document));
2328 break;
2329 case PROP_SUBTYPE_STRING:
2330 g_value_take_string(value, v_string: poppler_document_get_pdf_subtype_string(document));
2331 break;
2332 case PROP_SUBTYPE_PART:
2333 g_value_set_enum(value, v_enum: poppler_document_get_pdf_part(document));
2334 break;
2335 case PROP_SUBTYPE_CONF:
2336 g_value_set_enum(value, v_enum: poppler_document_get_pdf_conformance(document));
2337 break;
2338 case PROP_METADATA:
2339 g_value_take_string(value, v_string: poppler_document_get_metadata(document));
2340 break;
2341 default:
2342 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
2343 }
2344}
2345
2346static void poppler_document_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
2347{
2348 PopplerDocument *document = POPPLER_DOCUMENT(object);
2349
2350 switch (prop_id) {
2351 case PROP_TITLE:
2352 poppler_document_set_title(document, title: g_value_get_string(value));
2353 break;
2354 case PROP_AUTHOR:
2355 poppler_document_set_author(document, author: g_value_get_string(value));
2356 break;
2357 case PROP_SUBJECT:
2358 poppler_document_set_subject(document, subject: g_value_get_string(value));
2359 break;
2360 case PROP_KEYWORDS:
2361 poppler_document_set_keywords(document, keywords: g_value_get_string(value));
2362 break;
2363 case PROP_CREATOR:
2364 poppler_document_set_creator(document, creator: g_value_get_string(value));
2365 break;
2366 case PROP_PRODUCER:
2367 poppler_document_set_producer(document, producer: g_value_get_string(value));
2368 break;
2369 case PROP_CREATION_DATE:
2370 poppler_document_set_creation_date(document, creation_date: g_value_get_int(value));
2371 break;
2372 case PROP_CREATION_DATETIME:
2373 poppler_document_set_creation_date_time(document, creation_datetime: (GDateTime *)g_value_get_boxed(value));
2374 break;
2375 case PROP_MOD_DATE:
2376 poppler_document_set_modification_date(document, modification_date: g_value_get_int(value));
2377 break;
2378 case PROP_MOD_DATETIME:
2379 poppler_document_set_modification_date_time(document, modification_datetime: (GDateTime *)g_value_get_boxed(value));
2380 break;
2381 default:
2382 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
2383 }
2384}
2385
2386static void poppler_document_class_init(PopplerDocumentClass *klass)
2387{
2388 GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
2389
2390 gobject_class->finalize = poppler_document_finalize;
2391 gobject_class->get_property = poppler_document_get_property;
2392 gobject_class->set_property = poppler_document_set_property;
2393
2394 /**
2395 * PopplerDocument:title:
2396 *
2397 * The document's title or %NULL
2398 */
2399 g_object_class_install_property(G_OBJECT_CLASS(klass), property_id: PROP_TITLE, pspec: g_param_spec_string(name: "title", nick: "Document Title", blurb: "The title of the document", default_value: nullptr, flags: G_PARAM_READWRITE));
2400
2401 /**
2402 * PopplerDocument:format:
2403 *
2404 * The PDF version as string. See also poppler_document_get_pdf_version_string()
2405 */
2406 g_object_class_install_property(G_OBJECT_CLASS(klass), property_id: PROP_FORMAT, pspec: g_param_spec_string(name: "format", nick: "PDF Format", blurb: "The PDF version of the document", default_value: nullptr, flags: G_PARAM_READABLE));
2407
2408 /**
2409 * PopplerDocument:format-major:
2410 *
2411 * The PDF major version number. See also poppler_document_get_pdf_version()
2412 */
2413 g_object_class_install_property(G_OBJECT_CLASS(klass), property_id: PROP_FORMAT_MAJOR, pspec: g_param_spec_uint(name: "format-major", nick: "PDF Format Major", blurb: "The PDF major version number of the document", minimum: 0, G_MAXUINT, default_value: 1, flags: G_PARAM_READABLE));
2414
2415 /**
2416 * PopplerDocument:format-minor:
2417 *
2418 * The PDF minor version number. See also poppler_document_get_pdf_version()
2419 */
2420 g_object_class_install_property(G_OBJECT_CLASS(klass), property_id: PROP_FORMAT_MINOR, pspec: g_param_spec_uint(name: "format-minor", nick: "PDF Format Minor", blurb: "The PDF minor version number of the document", minimum: 0, G_MAXUINT, default_value: 0, flags: G_PARAM_READABLE));
2421
2422 /**
2423 * PopplerDocument:author:
2424 *
2425 * The author of the document
2426 */
2427 g_object_class_install_property(G_OBJECT_CLASS(klass), property_id: PROP_AUTHOR, pspec: g_param_spec_string(name: "author", nick: "Author", blurb: "The author of the document", default_value: nullptr, flags: G_PARAM_READWRITE));
2428
2429 /**
2430 * PopplerDocument:subject:
2431 *
2432 * The subject of the document
2433 */
2434 g_object_class_install_property(G_OBJECT_CLASS(klass), property_id: PROP_SUBJECT, pspec: g_param_spec_string(name: "subject", nick: "Subject", blurb: "Subjects the document touches", default_value: nullptr, flags: G_PARAM_READWRITE));
2435
2436 /**
2437 * PopplerDocument:keywords:
2438 *
2439 * The keywords associated to the document
2440 */
2441 g_object_class_install_property(G_OBJECT_CLASS(klass), property_id: PROP_KEYWORDS, pspec: g_param_spec_string(name: "keywords", nick: "Keywords", blurb: "Keywords", default_value: nullptr, flags: G_PARAM_READWRITE));
2442
2443 /**
2444 * PopplerDocument:creator:
2445 *
2446 * The creator of the document. See also poppler_document_get_creator()
2447 */
2448 g_object_class_install_property(G_OBJECT_CLASS(klass), property_id: PROP_CREATOR, pspec: g_param_spec_string(name: "creator", nick: "Creator", blurb: "The software that created the document", default_value: nullptr, flags: G_PARAM_READWRITE));
2449
2450 /**
2451 * PopplerDocument:producer:
2452 *
2453 * The producer of the document. See also poppler_document_get_producer()
2454 */
2455 g_object_class_install_property(G_OBJECT_CLASS(klass), property_id: PROP_PRODUCER, pspec: g_param_spec_string(name: "producer", nick: "Producer", blurb: "The software that converted the document", default_value: nullptr, flags: G_PARAM_READWRITE));
2456
2457 /**
2458 * PopplerDocument:creation-date:
2459 *
2460 * The date the document was created as seconds since the Epoch, or -1
2461 *
2462 * Deprecated: 20.09.0: This will overflow in 2038. Use creation-datetime
2463 * instead.
2464 */
2465 g_object_class_install_property(G_OBJECT_CLASS(klass), property_id: PROP_CREATION_DATE,
2466 pspec: g_param_spec_int(name: "creation-date", nick: "Creation Date", blurb: "The date and time the document was created", minimum: -1, G_MAXINT, default_value: -1, flags: (GParamFlags)(G_PARAM_READWRITE | G_PARAM_DEPRECATED)));
2467
2468 /**
2469 * PopplerDocument:creation-datetime:
2470 * The #GDateTime the document was created.
2471 *
2472 * Since: 20.09.0
2473 */
2474 g_object_class_install_property(G_OBJECT_CLASS(klass), property_id: PROP_CREATION_DATETIME, pspec: g_param_spec_boxed(name: "creation-datetime", nick: "Creation DateTime", blurb: "The date and time the document was created", G_TYPE_DATE_TIME, flags: G_PARAM_READWRITE));
2475
2476 /**
2477 * PopplerDocument:mod-date:
2478 *
2479 * The date the document was most recently modified as seconds since the Epoch, or -1
2480 *
2481 * Deprecated: 20.09.0: This will overflow in 2038. Use mod-datetime instead.
2482 */
2483 g_object_class_install_property(G_OBJECT_CLASS(klass), property_id: PROP_MOD_DATE,
2484 pspec: g_param_spec_int(name: "mod-date", nick: "Modification Date", blurb: "The date and time the document was modified", minimum: -1, G_MAXINT, default_value: -1, flags: (GParamFlags)(G_PARAM_READWRITE | G_PARAM_DEPRECATED)));
2485
2486 /**
2487 * PopplerDocument:mod-datetime:
2488 *
2489 * The #GDateTime the document was most recently modified.
2490 *
2491 * Since: 20.09.0
2492 */
2493 g_object_class_install_property(G_OBJECT_CLASS(klass), property_id: PROP_MOD_DATETIME, pspec: g_param_spec_boxed(name: "mod-datetime", nick: "Modification DateTime", blurb: "The date and time the document was modified", G_TYPE_DATE_TIME, flags: G_PARAM_READWRITE));
2494
2495 /**
2496 * PopplerDocument:linearized:
2497 *
2498 * Whether document is linearized. See also poppler_document_is_linearized()
2499 */
2500 g_object_class_install_property(G_OBJECT_CLASS(klass), property_id: PROP_LINEARIZED, pspec: g_param_spec_boolean(name: "linearized", nick: "Fast Web View Enabled", blurb: "Is the document optimized for web viewing?", FALSE, flags: G_PARAM_READABLE));
2501
2502 /**
2503 * PopplerDocument:page-layout:
2504 *
2505 * The page layout that should be used when the document is opened
2506 */
2507 g_object_class_install_property(G_OBJECT_CLASS(klass), property_id: PROP_PAGE_LAYOUT, pspec: g_param_spec_enum(name: "page-layout", nick: "Page Layout", blurb: "Initial Page Layout", POPPLER_TYPE_PAGE_LAYOUT, default_value: POPPLER_PAGE_LAYOUT_UNSET, flags: G_PARAM_READABLE));
2508
2509 /**
2510 * PopplerDocument:page-mode:
2511 *
2512 * The mode that should be used when the document is opened
2513 */
2514 g_object_class_install_property(G_OBJECT_CLASS(klass), property_id: PROP_PAGE_MODE, pspec: g_param_spec_enum(name: "page-mode", nick: "Page Mode", blurb: "Page Mode", POPPLER_TYPE_PAGE_MODE, default_value: POPPLER_PAGE_MODE_UNSET, flags: G_PARAM_READABLE));
2515
2516 /**
2517 * PopplerDocument:viewer-preferences:
2518 */
2519 g_object_class_install_property(G_OBJECT_CLASS(klass), property_id: PROP_VIEWER_PREFERENCES,
2520 pspec: g_param_spec_flags(name: "viewer-preferences", nick: "Viewer Preferences", blurb: "Viewer Preferences", POPPLER_TYPE_VIEWER_PREFERENCES, default_value: POPPLER_VIEWER_PREFERENCES_UNSET, flags: G_PARAM_READABLE));
2521
2522 /**
2523 * PopplerDocument:print-scaling:
2524 *
2525 * Since: 0.73
2526 */
2527 g_object_class_install_property(
2528 G_OBJECT_CLASS(klass), property_id: PROP_PRINT_SCALING,
2529 pspec: g_param_spec_enum(name: "print-scaling", nick: "Print Scaling", blurb: "Print Scaling Viewer Preference", POPPLER_TYPE_PRINT_SCALING, default_value: POPPLER_PRINT_SCALING_APP_DEFAULT, flags: (GParamFlags)(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
2530
2531 /**
2532 * PopplerDocument:print-duplex:
2533 *
2534 * Since: 0.80
2535 */
2536 g_object_class_install_property(G_OBJECT_CLASS(klass), property_id: PROP_PRINT_DUPLEX,
2537 pspec: g_param_spec_enum(name: "print-duplex", nick: "Print Duplex", blurb: "Duplex Viewer Preference", POPPLER_TYPE_PRINT_DUPLEX, default_value: POPPLER_PRINT_DUPLEX_NONE, flags: (GParamFlags)(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
2538
2539 /**
2540 * PopplerDocument:print-n-copies:
2541 *
2542 * Suggested number of copies to be printed for this document
2543 *
2544 * Since: 0.80
2545 */
2546 g_object_class_install_property(G_OBJECT_CLASS(klass), property_id: PROP_PRINT_N_COPIES,
2547 pspec: g_param_spec_int(name: "print-n-copies", nick: "Number of Copies to Print", blurb: "Number of Copies Viewer Preference", minimum: 1, G_MAXINT, default_value: 1, flags: (GParamFlags)(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
2548
2549 /**
2550 * PopplerDocument:permissions:
2551 *
2552 * Flags specifying which operations are permitted when the document is opened
2553 */
2554 g_object_class_install_property(G_OBJECT_CLASS(klass), property_id: PROP_PERMISSIONS, pspec: g_param_spec_flags(name: "permissions", nick: "Permissions", blurb: "Permissions", POPPLER_TYPE_PERMISSIONS, default_value: POPPLER_PERMISSIONS_FULL, flags: G_PARAM_READABLE));
2555
2556 /**
2557 * PopplerDocument:subtype:
2558 *
2559 * Document PDF subtype type
2560 */
2561 g_object_class_install_property(G_OBJECT_CLASS(klass), property_id: PROP_SUBTYPE, pspec: g_param_spec_enum(name: "subtype", nick: "PDF Format Subtype Type", blurb: "The PDF subtype of the document", POPPLER_TYPE_PDF_SUBTYPE, default_value: POPPLER_PDF_SUBTYPE_UNSET, flags: G_PARAM_READABLE));
2562
2563 /**
2564 * PopplerDocument:subtype-string:
2565 *
2566 * Document PDF subtype. See also poppler_document_get_pdf_subtype_string()
2567 */
2568 g_object_class_install_property(G_OBJECT_CLASS(klass), property_id: PROP_SUBTYPE_STRING, pspec: g_param_spec_string(name: "subtype-string", nick: "PDF Format Subtype", blurb: "The PDF subtype of the document", default_value: nullptr, flags: G_PARAM_READABLE));
2569
2570 /**
2571 * PopplerDocument:subtype-part:
2572 *
2573 * Document PDF subtype part
2574 */
2575 g_object_class_install_property(G_OBJECT_CLASS(klass), property_id: PROP_SUBTYPE_PART,
2576 pspec: g_param_spec_enum(name: "subtype-part", nick: "PDF Format Subtype Part", blurb: "The part of PDF conformance", POPPLER_TYPE_PDF_PART, default_value: POPPLER_PDF_SUBTYPE_PART_UNSET, flags: G_PARAM_READABLE));
2577
2578 /**
2579 * PopplerDocument:subtype-conformance:
2580 *
2581 * Document PDF subtype conformance
2582 */
2583 g_object_class_install_property(G_OBJECT_CLASS(klass), property_id: PROP_SUBTYPE_CONF,
2584 pspec: g_param_spec_enum(name: "subtype-conformance", nick: "PDF Format Subtype Conformance", blurb: "The conformance level of PDF subtype", POPPLER_TYPE_PDF_CONFORMANCE, default_value: POPPLER_PDF_SUBTYPE_CONF_UNSET, flags: G_PARAM_READABLE));
2585
2586 /**
2587 * PopplerDocument:metadata:
2588 *
2589 * Document metadata in XML format, or %NULL
2590 */
2591 g_object_class_install_property(G_OBJECT_CLASS(klass), property_id: PROP_METADATA, pspec: g_param_spec_string(name: "metadata", nick: "XML Metadata", blurb: "Embedded XML metadata", default_value: nullptr, flags: G_PARAM_READABLE));
2592}
2593
2594static void poppler_document_init(PopplerDocument *document) { }
2595
2596/* PopplerIndexIter: For determining the index of a tree */
2597struct _PopplerIndexIter
2598{
2599 PopplerDocument *document;
2600 const std::vector<OutlineItem *> *items;
2601 int index;
2602};
2603
2604G_DEFINE_BOXED_TYPE(PopplerIndexIter, poppler_index_iter, poppler_index_iter_copy, poppler_index_iter_free)
2605
2606/**
2607 * poppler_index_iter_copy:
2608 * @iter: a #PopplerIndexIter
2609 *
2610 * Creates a new #PopplerIndexIter as a copy of @iter. This must be freed with
2611 * poppler_index_iter_free().
2612 *
2613 * Return value: a new #PopplerIndexIter
2614 **/
2615PopplerIndexIter *poppler_index_iter_copy(PopplerIndexIter *iter)
2616{
2617 PopplerIndexIter *new_iter;
2618
2619 g_return_val_if_fail(iter != nullptr, NULL);
2620
2621 new_iter = g_slice_dup(PopplerIndexIter, iter);
2622 new_iter->document = (PopplerDocument *)g_object_ref(new_iter->document);
2623
2624 return new_iter;
2625}
2626
2627/**
2628 * poppler_index_iter_new:
2629 * @document: a #PopplerDocument
2630 *
2631 * Returns the root #PopplerIndexIter for @document, or %NULL. This must be
2632 * freed with poppler_index_iter_free().
2633 *
2634 * Certain documents have an index associated with them. This index can be used
2635 * to help the user navigate the document, and is similar to a table of
2636 * contents. Each node in the index will contain a #PopplerAction that can be
2637 * displayed to the user &mdash; typically a #POPPLER_ACTION_GOTO_DEST or a
2638 * #POPPLER_ACTION_URI<!-- -->.
2639 *
2640 * Here is a simple example of some code that walks the full index:
2641 *
2642 * <informalexample><programlisting>
2643 * static void
2644 * walk_index (PopplerIndexIter *iter)
2645 * {
2646 * do
2647 * {
2648 * /<!-- -->* Get the action and do something with it *<!-- -->/
2649 * PopplerIndexIter *child = poppler_index_iter_get_child (iter);
2650 * if (child)
2651 * walk_index (child);
2652 * poppler_index_iter_free (child);
2653 * }
2654 * while (poppler_index_iter_next (iter));
2655 * }
2656 * ...
2657 * {
2658 * iter = poppler_index_iter_new (document);
2659 * walk_index (iter);
2660 * poppler_index_iter_free (iter);
2661 * }
2662 *</programlisting></informalexample>
2663 *
2664 * Return value: a new #PopplerIndexIter
2665 **/
2666PopplerIndexIter *poppler_index_iter_new(PopplerDocument *document)
2667{
2668 PopplerIndexIter *iter;
2669 Outline *outline;
2670 const std::vector<OutlineItem *> *items;
2671
2672 outline = document->doc->getOutline();
2673 if (outline == nullptr) {
2674 return nullptr;
2675 }
2676
2677 items = outline->getItems();
2678 if (items == nullptr) {
2679 return nullptr;
2680 }
2681
2682 iter = g_slice_new(PopplerIndexIter);
2683 iter->document = (PopplerDocument *)g_object_ref(document);
2684 iter->items = items;
2685 iter->index = 0;
2686
2687 return iter;
2688}
2689
2690/**
2691 * poppler_index_iter_get_child:
2692 * @parent: a #PopplerIndexIter
2693 *
2694 * Returns a newly created child of @parent, or %NULL if the iter has no child.
2695 * See poppler_index_iter_new() for more information on this function.
2696 *
2697 * Return value: a new #PopplerIndexIter
2698 **/
2699PopplerIndexIter *poppler_index_iter_get_child(PopplerIndexIter *parent)
2700{
2701 PopplerIndexIter *child;
2702 OutlineItem *item;
2703
2704 g_return_val_if_fail(parent != nullptr, NULL);
2705
2706 item = (*parent->items)[parent->index];
2707 item->open();
2708 if (!(item->hasKids() && item->getKids())) {
2709 return nullptr;
2710 }
2711
2712 child = g_slice_new0(PopplerIndexIter);
2713 child->document = (PopplerDocument *)g_object_ref(parent->document);
2714 child->items = item->getKids();
2715
2716 g_assert(child->items);
2717
2718 return child;
2719}
2720
2721static gchar *unicode_to_char(const Unicode *unicode, int len)
2722{
2723 const UnicodeMap *uMap = globalParams->getUtf8Map();
2724
2725 GooString gstr;
2726 gchar buf[8]; /* 8 is enough for mapping an unicode char to a string */
2727 int i, n;
2728
2729 for (i = 0; i < len; ++i) {
2730 n = uMap->mapUnicode(u: unicode[i], buf, bufSize: sizeof(buf));
2731 gstr.append(str: buf, lengthA: n);
2732 }
2733
2734 return g_strdup(str: gstr.c_str());
2735}
2736
2737/**
2738 * poppler_index_iter_is_open:
2739 * @iter: a #PopplerIndexIter
2740 *
2741 * Returns whether this node should be expanded by default to the user. The
2742 * document can provide a hint as to how the document's index should be expanded
2743 * initially.
2744 *
2745 * Return value: %TRUE, if the document wants @iter to be expanded
2746 **/
2747gboolean poppler_index_iter_is_open(PopplerIndexIter *iter)
2748{
2749 OutlineItem *item;
2750
2751 item = (*iter->items)[iter->index];
2752
2753 return item->isOpen();
2754}
2755
2756/**
2757 * poppler_index_iter_get_action:
2758 * @iter: a #PopplerIndexIter
2759 *
2760 * Returns the #PopplerAction associated with @iter. It must be freed with
2761 * poppler_action_free().
2762 *
2763 * Return value: a new #PopplerAction
2764 **/
2765PopplerAction *poppler_index_iter_get_action(PopplerIndexIter *iter)
2766{
2767 OutlineItem *item;
2768 const LinkAction *link_action;
2769 PopplerAction *action;
2770 gchar *title;
2771
2772 g_return_val_if_fail(iter != nullptr, NULL);
2773
2774 item = (*iter->items)[iter->index];
2775 link_action = item->getAction();
2776
2777 const std::vector<Unicode> &itemTitle = item->getTitle();
2778 title = unicode_to_char(unicode: itemTitle.data(), len: itemTitle.size());
2779
2780 action = _poppler_action_new(document: iter->document, link: link_action, title);
2781 g_free(mem: title);
2782
2783 return action;
2784}
2785
2786/**
2787 * poppler_index_iter_next:
2788 * @iter: a #PopplerIndexIter
2789 *
2790 * Sets @iter to point to the next action at the current level, if valid. See
2791 * poppler_index_iter_new() for more information.
2792 *
2793 * Return value: %TRUE, if @iter was set to the next action
2794 **/
2795gboolean poppler_index_iter_next(PopplerIndexIter *iter)
2796{
2797 g_return_val_if_fail(iter != nullptr, FALSE);
2798
2799 iter->index++;
2800 if (iter->index >= (int)iter->items->size()) {
2801 return FALSE;
2802 }
2803
2804 return TRUE;
2805}
2806
2807/**
2808 * poppler_index_iter_free:
2809 * @iter: a #PopplerIndexIter
2810 *
2811 * Frees @iter.
2812 **/
2813void poppler_index_iter_free(PopplerIndexIter *iter)
2814{
2815 if (G_UNLIKELY(iter == nullptr)) {
2816 return;
2817 }
2818
2819 g_object_unref(object: iter->document);
2820 g_slice_free(PopplerIndexIter, iter);
2821}
2822
2823struct _PopplerFontsIter
2824{
2825 std::vector<FontInfo *> items;
2826 int index;
2827};
2828
2829G_DEFINE_BOXED_TYPE(PopplerFontsIter, poppler_fonts_iter, poppler_fonts_iter_copy, poppler_fonts_iter_free)
2830
2831/**
2832 * poppler_fonts_iter_get_full_name:
2833 * @iter: a #PopplerFontsIter
2834 *
2835 * Returns the full name of the font associated with @iter
2836 *
2837 * Returns: the font full name
2838 */
2839const char *poppler_fonts_iter_get_full_name(PopplerFontsIter *iter)
2840{
2841 FontInfo *info;
2842
2843 info = iter->items[iter->index];
2844
2845 const std::optional<std::string> &name = info->getName();
2846 if (name) {
2847 return name->c_str();
2848 } else {
2849 return nullptr;
2850 }
2851}
2852
2853/**
2854 * poppler_fonts_iter_get_name:
2855 * @iter: a #PopplerFontsIter
2856 *
2857 * Returns the name of the font associated with @iter
2858 *
2859 * Returns: the font name
2860 */
2861const char *poppler_fonts_iter_get_name(PopplerFontsIter *iter)
2862{
2863 FontInfo *info;
2864 const char *name;
2865
2866 name = poppler_fonts_iter_get_full_name(iter);
2867 info = iter->items[iter->index];
2868
2869 if (info->getSubset() && name) {
2870 while (*name && *name != '+') {
2871 name++;
2872 }
2873
2874 if (*name) {
2875 name++;
2876 }
2877 }
2878
2879 return name;
2880}
2881
2882/**
2883 * poppler_fonts_iter_get_substitute_name:
2884 * @iter: a #PopplerFontsIter
2885 *
2886 * The name of the substitute font of the font associated with @iter or %NULL if
2887 * the font is embedded
2888 *
2889 * Returns: the name of the substitute font or %NULL if font is embedded
2890 *
2891 * Since: 0.20
2892 */
2893const char *poppler_fonts_iter_get_substitute_name(PopplerFontsIter *iter)
2894{
2895 FontInfo *info;
2896
2897 info = iter->items[iter->index];
2898
2899 const std::optional<std::string> &name = info->getSubstituteName();
2900 if (name) {
2901 return name->c_str();
2902 } else {
2903 return nullptr;
2904 }
2905}
2906
2907/**
2908 * poppler_fonts_iter_get_file_name:
2909 * @iter: a #PopplerFontsIter
2910 *
2911 * The filename of the font associated with @iter or %NULL if
2912 * the font is embedded
2913 *
2914 * Returns: the filename of the font or %NULL if font is embedded
2915 */
2916const char *poppler_fonts_iter_get_file_name(PopplerFontsIter *iter)
2917{
2918 FontInfo *info;
2919
2920 info = iter->items[iter->index];
2921
2922 const std::optional<std::string> &file = info->getFile();
2923 if (file) {
2924 return file->c_str();
2925 } else {
2926 return nullptr;
2927 }
2928}
2929
2930/**
2931 * poppler_fonts_iter_get_font_type:
2932 * @iter: a #PopplerFontsIter
2933 *
2934 * Returns the type of the font associated with @iter
2935 *
2936 * Returns: the font type
2937 */
2938PopplerFontType poppler_fonts_iter_get_font_type(PopplerFontsIter *iter)
2939{
2940 FontInfo *info;
2941
2942 g_return_val_if_fail(iter != nullptr, POPPLER_FONT_TYPE_UNKNOWN);
2943
2944 info = iter->items[iter->index];
2945
2946 return (PopplerFontType)info->getType();
2947}
2948
2949/**
2950 * poppler_fonts_iter_get_encoding:
2951 * @iter: a #PopplerFontsIter
2952 *
2953 * Returns the encoding of the font associated with @iter
2954 *
2955 * Returns: the font encoding
2956 *
2957 * Since: 0.20
2958 */
2959const char *poppler_fonts_iter_get_encoding(PopplerFontsIter *iter)
2960{
2961 FontInfo *info;
2962
2963 info = iter->items[iter->index];
2964
2965 const std::string &encoding = info->getEncoding();
2966 if (!encoding.empty()) {
2967 return encoding.c_str();
2968 } else {
2969 return nullptr;
2970 }
2971}
2972
2973/**
2974 * poppler_fonts_iter_is_embedded:
2975 * @iter: a #PopplerFontsIter
2976 *
2977 * Returns whether the font associated with @iter is embedded in the document
2978 *
2979 * Returns: %TRUE if font is embedded, %FALSE otherwise
2980 */
2981gboolean poppler_fonts_iter_is_embedded(PopplerFontsIter *iter)
2982{
2983 FontInfo *info;
2984
2985 info = iter->items[iter->index];
2986
2987 return info->getEmbedded();
2988}
2989
2990/**
2991 * poppler_fonts_iter_is_subset:
2992 * @iter: a #PopplerFontsIter
2993 *
2994 * Returns whether the font associated with @iter is a subset of another font
2995 *
2996 * Returns: %TRUE if font is a subset, %FALSE otherwise
2997 */
2998gboolean poppler_fonts_iter_is_subset(PopplerFontsIter *iter)
2999{
3000 FontInfo *info;
3001
3002 info = iter->items[iter->index];
3003
3004 return info->getSubset();
3005}
3006
3007/**
3008 * poppler_fonts_iter_next:
3009 * @iter: a #PopplerFontsIter
3010 *
3011 * Sets @iter to point to the next font
3012 *
3013 * Returns: %TRUE, if @iter was set to the next font
3014 **/
3015gboolean poppler_fonts_iter_next(PopplerFontsIter *iter)
3016{
3017 g_return_val_if_fail(iter != nullptr, FALSE);
3018
3019 iter->index++;
3020 if (iter->index >= (int)iter->items.size()) {
3021 return FALSE;
3022 }
3023
3024 return TRUE;
3025}
3026
3027/**
3028 * poppler_fonts_iter_copy:
3029 * @iter: a #PopplerFontsIter to copy
3030 *
3031 * Creates a copy of @iter
3032 *
3033 * Returns: a new allocated copy of @iter
3034 */
3035PopplerFontsIter *poppler_fonts_iter_copy(PopplerFontsIter *iter)
3036{
3037 PopplerFontsIter *new_iter;
3038
3039 g_return_val_if_fail(iter != nullptr, NULL);
3040
3041 new_iter = g_slice_dup(PopplerFontsIter, iter);
3042
3043 new_iter->items.resize(new_size: iter->items.size());
3044 for (std::size_t i = 0; i < iter->items.size(); i++) {
3045 FontInfo *info = iter->items[i];
3046 new_iter->items[i] = new FontInfo(*info);
3047 }
3048
3049 return new_iter;
3050}
3051
3052/**
3053 * poppler_fonts_iter_free:
3054 * @iter: a #PopplerFontsIter
3055 *
3056 * Frees the given #PopplerFontsIter
3057 */
3058void poppler_fonts_iter_free(PopplerFontsIter *iter)
3059{
3060 if (G_UNLIKELY(iter == nullptr)) {
3061 return;
3062 }
3063
3064 for (auto entry : iter->items) {
3065 delete entry;
3066 }
3067 iter->items.~vector<FontInfo *>();
3068
3069 g_slice_free(PopplerFontsIter, iter);
3070}
3071
3072static PopplerFontsIter *poppler_fonts_iter_new(std::vector<FontInfo *> &&items)
3073{
3074 PopplerFontsIter *iter;
3075
3076 iter = g_slice_new(PopplerFontsIter);
3077 new ((void *)&iter->items) std::vector<FontInfo *>(std::move(items));
3078 iter->index = 0;
3079
3080 return iter;
3081}
3082
3083typedef struct _PopplerFontInfoClass PopplerFontInfoClass;
3084struct _PopplerFontInfoClass
3085{
3086 GObjectClass parent_class;
3087};
3088
3089G_DEFINE_TYPE(PopplerFontInfo, poppler_font_info, G_TYPE_OBJECT)
3090
3091static void poppler_font_info_finalize(GObject *object);
3092
3093static void poppler_font_info_class_init(PopplerFontInfoClass *klass)
3094{
3095 GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
3096
3097 gobject_class->finalize = poppler_font_info_finalize;
3098}
3099
3100static void poppler_font_info_init(PopplerFontInfo *font_info)
3101{
3102 font_info->document = nullptr;
3103 font_info->scanner = nullptr;
3104}
3105
3106static void poppler_font_info_finalize(GObject *object)
3107{
3108 PopplerFontInfo *font_info = POPPLER_FONT_INFO(object);
3109
3110 delete font_info->scanner;
3111 g_object_unref(object: font_info->document);
3112
3113 G_OBJECT_CLASS(poppler_font_info_parent_class)->finalize(object);
3114}
3115
3116/**
3117 * poppler_font_info_new:
3118 * @document: a #PopplerDocument
3119 *
3120 * Creates a new #PopplerFontInfo object
3121 *
3122 * Returns: a new #PopplerFontInfo instance
3123 */
3124PopplerFontInfo *poppler_font_info_new(PopplerDocument *document)
3125{
3126 PopplerFontInfo *font_info;
3127
3128 g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), NULL);
3129
3130 font_info = (PopplerFontInfo *)g_object_new(POPPLER_TYPE_FONT_INFO, first_property_name: nullptr);
3131 font_info->document = (PopplerDocument *)g_object_ref(document);
3132 font_info->scanner = new FontInfoScanner(document->doc);
3133
3134 return font_info;
3135}
3136
3137/**
3138 * poppler_font_info_scan:
3139 * @font_info: a #PopplerFontInfo
3140 * @n_pages: number of pages to scan
3141 * @iter: (out): return location for a #PopplerFontsIter
3142 *
3143 * Scans the document associated with @font_info for fonts. At most
3144 * @n_pages will be scanned starting from the current iterator. @iter will
3145 * point to the first font scanned.
3146 *
3147 * Here is a simple example of code to scan fonts in a document
3148 *
3149 * <informalexample><programlisting>
3150 * font_info = poppler_font_info_new (document);
3151 * while (poppler_font_info_scan (font_info, 20, &fonts_iter)) {
3152 * if (!fonts_iter)
3153 * continue; /<!-- -->* No fonts found in these 20 pages *<!-- -->/
3154 * do {
3155 * /<!-- -->* Do something with font iter *<!-- -->/
3156 * g_print ("Font Name: %s\n", poppler_fonts_iter_get_name (fonts_iter));
3157 * } while (poppler_fonts_iter_next (fonts_iter));
3158 * poppler_fonts_iter_free (fonts_iter);
3159 * }
3160 * </programlisting></informalexample>
3161 *
3162 * Returns: %TRUE, if there are more fonts left to scan
3163 */
3164gboolean poppler_font_info_scan(PopplerFontInfo *font_info, int n_pages, PopplerFontsIter **iter)
3165{
3166 g_return_val_if_fail(iter != nullptr, FALSE);
3167
3168 std::vector<FontInfo *> items = font_info->scanner->scan(nPages: n_pages);
3169
3170 if (items.empty()) {
3171 *iter = nullptr;
3172 return FALSE;
3173 } else {
3174 *iter = poppler_fonts_iter_new(items: std::move(items));
3175 }
3176
3177 return TRUE;
3178}
3179
3180/* For backward compatibility */
3181void poppler_font_info_free(PopplerFontInfo *font_info)
3182{
3183 g_return_if_fail(font_info != nullptr);
3184
3185 g_object_unref(object: font_info);
3186}
3187
3188/* Optional content (layers) */
3189static Layer *layer_new(OptionalContentGroup *oc)
3190{
3191 Layer *layer;
3192
3193 layer = g_slice_new0(Layer);
3194 layer->oc = oc;
3195
3196 return layer;
3197}
3198
3199static void layer_free(Layer *layer)
3200{
3201 if (G_UNLIKELY(!layer)) {
3202 return;
3203 }
3204
3205 if (layer->kids) {
3206 g_list_free_full(list: layer->kids, free_func: (GDestroyNotify)layer_free);
3207 }
3208
3209 if (layer->label) {
3210 g_free(mem: layer->label);
3211 }
3212
3213 g_slice_free(Layer, layer);
3214}
3215
3216static GList *get_optional_content_rbgroups(OCGs *ocg)
3217{
3218 Array *rb;
3219 GList *groups = nullptr;
3220
3221 rb = ocg->getRBGroupsArray();
3222
3223 if (rb) {
3224 int i, j;
3225
3226 for (i = 0; i < rb->getLength(); ++i) {
3227 Array *rb_array;
3228 GList *group = nullptr;
3229
3230 Object obj = rb->get(i);
3231 if (!obj.isArray()) {
3232 continue;
3233 }
3234
3235 rb_array = obj.getArray();
3236 for (j = 0; j < rb_array->getLength(); ++j) {
3237 OptionalContentGroup *oc;
3238
3239 const Object &ref = rb_array->getNF(i: j);
3240 if (!ref.isRef()) {
3241 continue;
3242 }
3243
3244 oc = ocg->findOcgByRef(ref: ref.getRef());
3245 group = g_list_prepend(list: group, data: oc);
3246 }
3247
3248 groups = g_list_prepend(list: groups, data: group);
3249 }
3250 }
3251
3252 return groups;
3253}
3254
3255GList *_poppler_document_get_layer_rbgroup(PopplerDocument *document, Layer *layer)
3256{
3257 GList *l;
3258
3259 for (l = document->layers_rbgroups; l && l->data; l = g_list_next(l)) {
3260 GList *group = (GList *)l->data;
3261
3262 if (g_list_find(list: group, data: layer->oc)) {
3263 return group;
3264 }
3265 }
3266
3267 return nullptr;
3268}
3269
3270static GList *get_optional_content_items_sorted(OCGs *ocg, Layer *parent, Array *order)
3271{
3272 GList *items = nullptr;
3273 Layer *last_item = parent;
3274 int i;
3275
3276 for (i = 0; i < order->getLength(); ++i) {
3277 Object orderItem = order->get(i);
3278
3279 if (orderItem.isDict()) {
3280 const Object &ref = order->getNF(i);
3281 if (ref.isRef()) {
3282 OptionalContentGroup *oc = ocg->findOcgByRef(ref: ref.getRef());
3283 Layer *layer = layer_new(oc);
3284
3285 items = g_list_prepend(list: items, data: layer);
3286 last_item = layer;
3287 }
3288 } else if (orderItem.isArray() && orderItem.arrayGetLength() > 0) {
3289 if (!last_item) {
3290 last_item = layer_new(oc: nullptr);
3291 items = g_list_prepend(list: items, data: last_item);
3292 }
3293 last_item->kids = get_optional_content_items_sorted(ocg, parent: last_item, order: orderItem.getArray());
3294 last_item = nullptr;
3295 } else if (orderItem.isString()) {
3296 last_item->label = _poppler_goo_string_to_utf8(s: orderItem.getString());
3297 }
3298 }
3299
3300 return g_list_reverse(list: items);
3301}
3302
3303static GList *get_optional_content_items(OCGs *ocg)
3304{
3305 Array *order;
3306 GList *items = nullptr;
3307
3308 order = ocg->getOrderArray();
3309
3310 if (order) {
3311 items = get_optional_content_items_sorted(ocg, parent: nullptr, order);
3312 } else {
3313 const auto &ocgs = ocg->getOCGs();
3314
3315 for (const auto &oc : ocgs) {
3316 Layer *layer = layer_new(oc: oc.second.get());
3317
3318 items = g_list_prepend(list: items, data: layer);
3319 }
3320
3321 items = g_list_reverse(list: items);
3322 }
3323
3324 return items;
3325}
3326
3327GList *_poppler_document_get_layers(PopplerDocument *document)
3328{
3329 if (!document->layers) {
3330 Catalog *catalog = document->doc->getCatalog();
3331 OCGs *ocg = catalog->getOptContentConfig();
3332
3333 if (!ocg) {
3334 return nullptr;
3335 }
3336
3337 document->layers = get_optional_content_items(ocg);
3338 document->layers_rbgroups = get_optional_content_rbgroups(ocg);
3339 }
3340
3341 return document->layers;
3342}
3343
3344static void poppler_document_layers_free(PopplerDocument *document)
3345{
3346 if (G_UNLIKELY(!document->layers)) {
3347 return;
3348 }
3349
3350 g_list_free_full(list: document->layers, free_func: (GDestroyNotify)layer_free);
3351 g_list_free_full(list: document->layers_rbgroups, free_func: (GDestroyNotify)g_list_free);
3352
3353 document->layers = nullptr;
3354 document->layers_rbgroups = nullptr;
3355}
3356
3357/* PopplerLayersIter */
3358struct _PopplerLayersIter
3359{
3360 PopplerDocument *document;
3361 GList *items;
3362 int index;
3363};
3364
3365G_DEFINE_BOXED_TYPE(PopplerLayersIter, poppler_layers_iter, poppler_layers_iter_copy, poppler_layers_iter_free)
3366
3367/**
3368 * poppler_layers_iter_copy:
3369 * @iter: a #PopplerLayersIter
3370 *
3371 * Creates a new #PopplerLayersIter as a copy of @iter. This must be freed with
3372 * poppler_layers_iter_free().
3373 *
3374 * Return value: a new #PopplerLayersIter
3375 *
3376 * Since 0.12
3377 **/
3378PopplerLayersIter *poppler_layers_iter_copy(PopplerLayersIter *iter)
3379{
3380 PopplerLayersIter *new_iter;
3381
3382 g_return_val_if_fail(iter != nullptr, NULL);
3383
3384 new_iter = g_slice_dup(PopplerLayersIter, iter);
3385 new_iter->document = (PopplerDocument *)g_object_ref(new_iter->document);
3386
3387 return new_iter;
3388}
3389
3390/**
3391 * poppler_layers_iter_free:
3392 * @iter: a #PopplerLayersIter
3393 *
3394 * Frees @iter.
3395 *
3396 * Since: 0.12
3397 **/
3398void poppler_layers_iter_free(PopplerLayersIter *iter)
3399{
3400 if (G_UNLIKELY(iter == nullptr)) {
3401 return;
3402 }
3403
3404 g_object_unref(object: iter->document);
3405 g_slice_free(PopplerLayersIter, iter);
3406}
3407
3408/**
3409 * poppler_layers_iter_new:
3410 * @document: a #PopplerDocument
3411 *
3412 * Since: 0.12
3413 **/
3414PopplerLayersIter *poppler_layers_iter_new(PopplerDocument *document)
3415{
3416 PopplerLayersIter *iter;
3417 GList *items;
3418
3419 items = _poppler_document_get_layers(document);
3420
3421 if (!items) {
3422 return nullptr;
3423 }
3424
3425 iter = g_slice_new0(PopplerLayersIter);
3426 iter->document = (PopplerDocument *)g_object_ref(document);
3427 iter->items = items;
3428
3429 return iter;
3430}
3431
3432/**
3433 * poppler_layers_iter_get_child:
3434 * @parent: a #PopplerLayersIter
3435 *
3436 * Returns a newly created child of @parent, or %NULL if the iter has no child.
3437 * See poppler_layers_iter_new() for more information on this function.
3438 *
3439 * Return value: a new #PopplerLayersIter, or %NULL
3440 *
3441 * Since: 0.12
3442 **/
3443PopplerLayersIter *poppler_layers_iter_get_child(PopplerLayersIter *parent)
3444{
3445 PopplerLayersIter *child;
3446 Layer *layer;
3447
3448 g_return_val_if_fail(parent != nullptr, NULL);
3449
3450 layer = (Layer *)g_list_nth_data(list: parent->items, n: parent->index);
3451 if (!layer || !layer->kids) {
3452 return nullptr;
3453 }
3454
3455 child = g_slice_new0(PopplerLayersIter);
3456 child->document = (PopplerDocument *)g_object_ref(parent->document);
3457 child->items = layer->kids;
3458
3459 g_assert(child->items);
3460
3461 return child;
3462}
3463
3464/**
3465 * poppler_layers_iter_get_title:
3466 * @iter: a #PopplerLayersIter
3467 *
3468 * Returns the title associated with @iter. It must be freed with
3469 * g_free().
3470 *
3471 * Return value: a new string containing the @iter's title or %NULL if @iter doesn't have a title.
3472 * The returned string should be freed with g_free() when no longer needed.
3473 *
3474 * Since: 0.12
3475 **/
3476gchar *poppler_layers_iter_get_title(PopplerLayersIter *iter)
3477{
3478 Layer *layer;
3479
3480 g_return_val_if_fail(iter != nullptr, NULL);
3481
3482 layer = (Layer *)g_list_nth_data(list: iter->items, n: iter->index);
3483
3484 return layer->label ? g_strdup(str: layer->label) : nullptr;
3485}
3486
3487/**
3488 * poppler_layers_iter_get_layer:
3489 * @iter: a #PopplerLayersIter
3490 *
3491 * Returns the #PopplerLayer associated with @iter.
3492 *
3493 * Return value: (transfer full): a new #PopplerLayer, or %NULL if
3494 * there isn't any layer associated with @iter
3495 *
3496 * Since: 0.12
3497 **/
3498PopplerLayer *poppler_layers_iter_get_layer(PopplerLayersIter *iter)
3499{
3500 Layer *layer;
3501 PopplerLayer *poppler_layer = nullptr;
3502
3503 g_return_val_if_fail(iter != nullptr, NULL);
3504
3505 layer = (Layer *)g_list_nth_data(list: iter->items, n: iter->index);
3506 if (layer->oc) {
3507 GList *rb_group = nullptr;
3508
3509 rb_group = _poppler_document_get_layer_rbgroup(document: iter->document, layer);
3510 poppler_layer = _poppler_layer_new(document: iter->document, layer, rbgroup: rb_group);
3511 }
3512
3513 return poppler_layer;
3514}
3515
3516/**
3517 * poppler_layers_iter_next:
3518 * @iter: a #PopplerLayersIter
3519 *
3520 * Sets @iter to point to the next action at the current level, if valid. See
3521 * poppler_layers_iter_new() for more information.
3522 *
3523 * Return value: %TRUE, if @iter was set to the next action
3524 *
3525 * Since: 0.12
3526 **/
3527gboolean poppler_layers_iter_next(PopplerLayersIter *iter)
3528{
3529 g_return_val_if_fail(iter != nullptr, FALSE);
3530
3531 iter->index++;
3532 if (iter->index >= (gint)g_list_length(list: iter->items)) {
3533 return FALSE;
3534 }
3535
3536 return TRUE;
3537}
3538
3539typedef struct _PopplerPSFileClass PopplerPSFileClass;
3540struct _PopplerPSFileClass
3541{
3542 GObjectClass parent_class;
3543};
3544
3545G_DEFINE_TYPE(PopplerPSFile, poppler_ps_file, G_TYPE_OBJECT)
3546
3547static void poppler_ps_file_finalize(GObject *object);
3548
3549static void poppler_ps_file_class_init(PopplerPSFileClass *klass)
3550{
3551 GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
3552
3553 gobject_class->finalize = poppler_ps_file_finalize;
3554}
3555
3556static void poppler_ps_file_init(PopplerPSFile *ps_file)
3557{
3558 ps_file->out = nullptr;
3559 ps_file->fd = -1;
3560 ps_file->filename = nullptr;
3561 ps_file->paper_width = -1;
3562 ps_file->paper_height = -1;
3563 ps_file->duplex = FALSE;
3564}
3565
3566static void poppler_ps_file_finalize(GObject *object)
3567{
3568 PopplerPSFile *ps_file = POPPLER_PS_FILE(object);
3569
3570 delete ps_file->out;
3571 g_object_unref(object: ps_file->document);
3572 g_free(mem: ps_file->filename);
3573#ifndef G_OS_WIN32
3574 if (ps_file->fd != -1) {
3575 close(fd: ps_file->fd);
3576 }
3577#endif /* !G_OS_WIN32 */
3578
3579 G_OBJECT_CLASS(poppler_ps_file_parent_class)->finalize(object);
3580}
3581
3582/**
3583 * poppler_ps_file_new:
3584 * @document: a #PopplerDocument
3585 * @filename: the path of the output filename
3586 * @first_page: the first page to print
3587 * @n_pages: the number of pages to print
3588 *
3589 * Create a new postscript file to render to
3590 *
3591 * Return value: (transfer full): a PopplerPSFile
3592 **/
3593PopplerPSFile *poppler_ps_file_new(PopplerDocument *document, const char *filename, int first_page, int n_pages)
3594{
3595 PopplerPSFile *ps_file;
3596
3597 g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), NULL);
3598 g_return_val_if_fail(filename != nullptr, NULL);
3599 g_return_val_if_fail(n_pages > 0, NULL);
3600
3601 ps_file = (PopplerPSFile *)g_object_new(POPPLER_TYPE_PS_FILE, first_property_name: nullptr);
3602 ps_file->document = (PopplerDocument *)g_object_ref(document);
3603 ps_file->filename = g_strdup(str: filename);
3604 ps_file->first_page = first_page + 1;
3605 ps_file->last_page = first_page + 1 + n_pages - 1;
3606
3607 return ps_file;
3608}
3609
3610#ifndef G_OS_WIN32
3611
3612/**
3613 * poppler_ps_file_new_fd:
3614 * @document: a #PopplerDocument
3615 * @fd: a valid file descriptor open for writing
3616 * @first_page: the first page to print
3617 * @n_pages: the number of pages to print
3618 *
3619 * Create a new postscript file to render to.
3620 * Note that this function takes ownership of @fd; you must not operate on it
3621 * again, nor close it.
3622 *
3623 * Return value: (transfer full): a #PopplerPSFile
3624 *
3625 * Since: 21.12.0
3626 **/
3627PopplerPSFile *poppler_ps_file_new_fd(PopplerDocument *document, int fd, int first_page, int n_pages)
3628{
3629 PopplerPSFile *ps_file;
3630
3631 g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), nullptr);
3632 g_return_val_if_fail(fd != -1, nullptr);
3633 g_return_val_if_fail(n_pages > 0, nullptr);
3634
3635 ps_file = (PopplerPSFile *)g_object_new(POPPLER_TYPE_PS_FILE, first_property_name: nullptr);
3636 ps_file->document = (PopplerDocument *)g_object_ref(document);
3637 ps_file->fd = fd;
3638 ps_file->first_page = first_page + 1;
3639 ps_file->last_page = first_page + 1 + n_pages - 1;
3640
3641 return ps_file;
3642}
3643
3644#endif /* !G_OS_WIN32 */
3645
3646/**
3647 * poppler_ps_file_set_paper_size:
3648 * @ps_file: a PopplerPSFile which was not yet printed to.
3649 * @width: the paper width in 1/72 inch
3650 * @height: the paper height in 1/72 inch
3651 *
3652 * Set the output paper size. These values will end up in the
3653 * DocumentMedia, the BoundingBox DSC comments and other places in the
3654 * generated PostScript.
3655 *
3656 **/
3657void poppler_ps_file_set_paper_size(PopplerPSFile *ps_file, double width, double height)
3658{
3659 g_return_if_fail(ps_file->out == nullptr);
3660
3661 ps_file->paper_width = width;
3662 ps_file->paper_height = height;
3663}
3664
3665/**
3666 * poppler_ps_file_set_duplex:
3667 * @ps_file: a PopplerPSFile which was not yet printed to
3668 * @duplex: whether to force duplex printing (on printers which support this)
3669 *
3670 * Enable or disable Duplex printing.
3671 *
3672 **/
3673void poppler_ps_file_set_duplex(PopplerPSFile *ps_file, gboolean duplex)
3674{
3675 g_return_if_fail(ps_file->out == nullptr);
3676
3677 ps_file->duplex = duplex;
3678}
3679
3680/**
3681 * poppler_ps_file_free:
3682 * @ps_file: a PopplerPSFile
3683 *
3684 * Frees @ps_file
3685 *
3686 **/
3687void poppler_ps_file_free(PopplerPSFile *ps_file)
3688{
3689 g_return_if_fail(ps_file != nullptr);
3690 g_object_unref(object: ps_file);
3691}
3692
3693/**
3694 * poppler_document_get_form_field:
3695 * @document: a #PopplerDocument
3696 * @id: an id of a #PopplerFormField
3697 *
3698 * Returns the #PopplerFormField for the given @id. It must be freed with
3699 * g_object_unref()
3700 *
3701 * Return value: (transfer full): a new #PopplerFormField or %NULL if
3702 * not found
3703 **/
3704PopplerFormField *poppler_document_get_form_field(PopplerDocument *document, gint id)
3705{
3706 Page *page;
3707 unsigned pageNum;
3708 unsigned fieldNum;
3709 FormWidget *field;
3710
3711 FormWidget::decodeID(id, pageNum: &pageNum, fieldNum: &fieldNum);
3712
3713 page = document->doc->getPage(page: pageNum);
3714 if (!page) {
3715 return nullptr;
3716 }
3717
3718 const std::unique_ptr<FormPageWidgets> widgets = page->getFormWidgets();
3719 if (!widgets) {
3720 return nullptr;
3721 }
3722
3723 field = widgets->getWidget(i: fieldNum);
3724 if (field) {
3725 return _poppler_form_field_new(document, field);
3726 }
3727
3728 return nullptr;
3729}
3730
3731gboolean _poppler_convert_pdf_date_to_gtime(const GooString *date, time_t *gdate)
3732{
3733 gchar *date_string;
3734 gboolean retval;
3735
3736 if (hasUnicodeByteOrderMark(s: date->toStr())) {
3737 date_string = g_convert(str: date->c_str() + 2, len: date->getLength() - 2, to_codeset: "UTF-8", from_codeset: "UTF-16BE", bytes_read: nullptr, bytes_written: nullptr, error: nullptr);
3738 } else {
3739 date_string = g_strndup(str: date->c_str(), n: date->getLength());
3740 }
3741
3742 retval = poppler_date_parse(date: date_string, timet: gdate);
3743 g_free(mem: date_string);
3744
3745 return retval;
3746}
3747
3748/**
3749 * _poppler_convert_pdf_date_to_date_time:
3750 * @date: a PDF date
3751 *
3752 * Converts the PDF date in @date to a #GDateTime.
3753 *
3754 * Returns: The converted date, or %NULL on error.
3755 **/
3756GDateTime *_poppler_convert_pdf_date_to_date_time(const GooString *date)
3757{
3758 GDateTime *date_time = nullptr;
3759 GTimeZone *time_zone = nullptr;
3760 int year, mon, day, hour, min, sec, tzHours, tzMins;
3761 char tz;
3762
3763 if (parseDateString(date, year: &year, month: &mon, day: &day, hour: &hour, minute: &min, second: &sec, tz: &tz, tzHour: &tzHours, tzMinute: &tzMins)) {
3764 if (tz == '+' || tz == '-') {
3765 gchar *identifier;
3766
3767 identifier = g_strdup_printf(format: "%c%02u:%02u", tz, tzHours, tzMins);
3768#if GLIB_CHECK_VERSION(2, 68, 0)
3769 time_zone = g_time_zone_new_identifier(identifier);
3770 if (!time_zone) {
3771 g_debug("Failed to create time zone for identifier \"%s\"", identifier);
3772 time_zone = g_time_zone_new_utc();
3773 }
3774#else
3775 time_zone = g_time_zone_new(identifier);
3776#endif
3777 g_free(mem: identifier);
3778 } else if (tz == '\0' || tz == 'Z') {
3779 time_zone = g_time_zone_new_utc();
3780 } else {
3781 g_warning("unexpected tz val '%c'", tz);
3782 time_zone = g_time_zone_new_utc();
3783 }
3784
3785 date_time = g_date_time_new(tz: time_zone, year, month: mon, day, hour, minute: min, seconds: sec);
3786 g_time_zone_unref(tz: time_zone);
3787 }
3788
3789 return date_time;
3790}
3791
3792/**
3793 * _poppler_convert_date_time_to_pdf_date:
3794 * @datetime: a #GDateTime
3795 *
3796 * Converts a #GDateTime to a PDF date.
3797 *
3798 * Returns: The converted date
3799 **/
3800GooString *_poppler_convert_date_time_to_pdf_date(GDateTime *datetime)
3801{
3802 int offset_min;
3803 gchar *date_str;
3804 std::unique_ptr<GooString> out_str;
3805
3806 offset_min = g_date_time_get_utc_offset(datetime) / 1000000 / 60;
3807 date_str = g_date_time_format(datetime, format: "D:%Y%m%d%H%M%S");
3808
3809 if (offset_min == 0) {
3810 out_str = GooString::format(fmt: "{0:s}Z", date_str);
3811 } else {
3812 char tz = offset_min > 0 ? '+' : '-';
3813
3814 out_str = GooString::format(fmt: "{0:s}{1:c}{2:02d}'{3:02d}'", date_str, tz, offset_min / 60, offset_min % 60);
3815 }
3816
3817 g_free(mem: date_str);
3818 return out_str.release();
3819}
3820
3821static void _poppler_sign_document_thread(GTask *task, PopplerDocument *document, const PopplerSigningData *signing_data, GCancellable *cancellable)
3822{
3823 const PopplerCertificateInfo *certificate_info;
3824 const char *signing_data_signature_text;
3825 const PopplerColor *font_color;
3826 const PopplerColor *border_color;
3827 const PopplerColor *background_color;
3828 gboolean ret;
3829
3830 g_return_if_fail(POPPLER_IS_DOCUMENT(document));
3831 g_return_if_fail(signing_data != nullptr);
3832
3833 signing_data_signature_text = poppler_signing_data_get_signature_text(signing_data);
3834 if (signing_data_signature_text == nullptr) {
3835 g_task_return_new_error(task, POPPLER_ERROR, code: POPPLER_ERROR_SIGNING, format: "No signature given");
3836 return;
3837 }
3838
3839 certificate_info = poppler_signing_data_get_certificate_info(signing_data);
3840 if (certificate_info == nullptr) {
3841 g_task_return_new_error(task, POPPLER_ERROR, code: POPPLER_ERROR_SIGNING, format: "Invalid certificate information provided for signing");
3842 return;
3843 }
3844
3845 PopplerPage *page = poppler_document_get_page(document, index: poppler_signing_data_get_page(signing_data));
3846 if (page == nullptr) {
3847 g_task_return_new_error(task, POPPLER_ERROR, code: POPPLER_ERROR_SIGNING, format: "Invalid page number selected for signing");
3848 return;
3849 }
3850
3851 font_color = poppler_signing_data_get_font_color(signing_data);
3852 border_color = poppler_signing_data_get_border_color(signing_data);
3853 background_color = poppler_signing_data_get_background_color(signing_data);
3854
3855 std::unique_ptr<GooString> signature_text = std::make_unique<GooString>(args: utf8ToUtf16WithBom(utf8: signing_data_signature_text));
3856 std::unique_ptr<GooString> signature_text_left = std::make_unique<GooString>(args: utf8ToUtf16WithBom(utf8: poppler_signing_data_get_signature_text_left(signing_data)));
3857 const auto field_partial_name = new GooString(poppler_signing_data_get_field_partial_name(signing_data), strlen(s: poppler_signing_data_get_field_partial_name(signing_data)));
3858 const auto owner_pwd = std::optional<GooString>(poppler_signing_data_get_document_owner_password(signing_data));
3859 const auto user_pwd = std::optional<GooString>(poppler_signing_data_get_document_user_password(signing_data));
3860 const auto reason = std::unique_ptr<GooString>(poppler_signing_data_get_reason(signing_data) ? new GooString(poppler_signing_data_get_reason(signing_data), strlen(s: poppler_signing_data_get_reason(signing_data))) : nullptr);
3861 const auto location = std::unique_ptr<GooString>(poppler_signing_data_get_location(signing_data) ? new GooString(poppler_signing_data_get_location(signing_data), strlen(s: poppler_signing_data_get_location(signing_data))) : nullptr);
3862 const PopplerRectangle *rect = poppler_signing_data_get_signature_rectangle(signing_data);
3863
3864 ret = document->doc->sign(saveFilename: poppler_signing_data_get_destination_filename(signing_data), certNickname: poppler_certificate_info_get_id(certificate_info: (PopplerCertificateInfo *)certificate_info),
3865 password: poppler_signing_data_get_password(signing_data) ? poppler_signing_data_get_password(signing_data) : "", partialFieldName: field_partial_name, page: poppler_signing_data_get_page(signing_data) + 1,
3866 rect: PDFRectangle(rect->x1, rect->y1, rect->x2, rect->y2), signatureText: *signature_text, signatureTextLeft: *signature_text_left, fontSize: poppler_signing_data_get_font_size(signing_data), leftFontSize: poppler_signing_data_get_left_font_size(signing_data),
3867 fontColor: std::make_unique<AnnotColor>(args: font_color->red, args: font_color->green, args: font_color->blue), borderWidth: poppler_signing_data_get_border_width(signing_data),
3868 borderColor: std::make_unique<AnnotColor>(args: border_color->red, args: border_color->green, args: border_color->blue), backgroundColor: std::make_unique<AnnotColor>(args: background_color->red, args: background_color->green, args: background_color->blue), reason: reason.get(),
3869 location: location.get(), imagePath: poppler_signing_data_get_image_path(signing_data) ? poppler_signing_data_get_image_path(signing_data) : "", ownerPassword: owner_pwd, userPassword: user_pwd);
3870
3871 g_task_return_boolean(task, result: ret);
3872}
3873
3874/**
3875 * poppler_document_sign:
3876 * @document: a #PopplerDocument
3877 * @signing_data: a #PopplerSigningData
3878 * @cancellable: a #GCancellable
3879 * @callback: a #GAsyncReadyCallback
3880 * @user_data: user data used by callback function
3881 *
3882 * Sign #document using #signing_data.
3883 *
3884 * Since: 23.07.0
3885 **/
3886void poppler_document_sign(PopplerDocument *document, const PopplerSigningData *signing_data, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data)
3887{
3888 GTask *task;
3889
3890 g_return_if_fail(POPPLER_IS_DOCUMENT(document));
3891 g_return_if_fail(signing_data != nullptr);
3892
3893 task = g_task_new(source_object: document, cancellable, callback, callback_data: user_data);
3894 g_task_set_task_data(task, task_data: (void *)signing_data, task_data_destroy: nullptr);
3895
3896 g_task_run_in_thread(task, task_func: (GTaskThreadFunc)_poppler_sign_document_thread);
3897 g_object_unref(object: task);
3898}
3899
3900/**
3901 * poppler_document_sign_finish:
3902 * @document: a #PopplerDocument
3903 * @result: a #GAsyncResult
3904 * @error: a #GError
3905 *
3906 * Finish poppler_sign_document and get return status or error.
3907 *
3908 * Returns: %TRUE on successful signing a document, otherwise %FALSE and error is set.
3909 *
3910 * Since: 23.07.0
3911 **/
3912gboolean poppler_document_sign_finish(PopplerDocument *document, GAsyncResult *result, GError **error)
3913{
3914 g_return_val_if_fail(g_task_is_valid(result, document), FALSE);
3915
3916 return g_task_propagate_boolean(G_TASK(result), error);
3917}
3918

source code of poppler/glib/poppler-document.cc