Coverage Report

Created: 2026-06-07 08:13

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