Coverage Report

Created: 2026-11-10 07:41

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