blob: fac52af86a7e087d567ecbd857d692d90dc69688 [file] [log] [blame]
/* poppler-document.cc: glib wrapper for poppler
* Copyright (C) 2005, Red Hat, Inc.
*
* Copyright (C) 2016 Jakub Alba <jakubalba@gmail.com>
* Copyright (C) 2018, 2019, 2021, 2022 Marek Kasik <mkasik@redhat.com>
* Copyright (C) 2019 Masamichi Hosoda <trueroad@trueroad.jp>
* Copyright (C) 2019, 2021, 2024 Oliver Sander <oliver.sander@tu-dresden.de>
* Copyright (C) 2020, 2022 Albert Astals Cid <aacid@kde.org>
* Copyright (C) 2021 André Guerreiro <aguerreiro1985@gmail.com>
* Copyright (C) 2024 g10 Code GmbH, Author: Sune Stolborg Vuorela <sune@vuorela.dk>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "config.h"
#include <cstring>
#include <glib.h>
#ifndef G_OS_WIN32
# include <fcntl.h>
# include <sys/stat.h>
# include <sys/types.h>
# include <unistd.h>
#endif
#ifndef __GI_SCANNER__
# include <memory>
# include <goo/gfile.h>
# include <splash/SplashBitmap.h>
# include <CachedFile.h>
# include <DateInfo.h>
# include <FILECacheLoader.h>
# include <GlobalParams.h>
# include <PDFDoc.h>
# include <SignatureInfo.h>
# include <Outline.h>
# include <ErrorCodes.h>
# include <UnicodeMap.h>
# include <GfxState.h>
# include <SplashOutputDev.h>
# include <Stream.h>
# include <FontInfo.h>
# include <PDFDocEncoding.h>
# include <OptionalContent.h>
# include <ViewerPreferences.h>
# include "UTF.h"
#endif
#include "poppler.h"
#include "poppler-private.h"
#include "poppler-enums.h"
#include "poppler-input-stream.h"
#include "poppler-cached-file-loader.h"
#ifdef G_OS_WIN32
# include <stringapiset.h>
#endif
/**
* SECTION:poppler-document
* @short_description: Information about a document
* @title: PopplerDocument
*
* The #PopplerDocument is an object used to refer to a main document.
*/
enum
{
PROP_0,
PROP_TITLE,
PROP_FORMAT,
PROP_FORMAT_MAJOR,
PROP_FORMAT_MINOR,
PROP_SUBTYPE,
PROP_SUBTYPE_STRING,
PROP_SUBTYPE_PART,
PROP_SUBTYPE_CONF,
PROP_AUTHOR,
PROP_SUBJECT,
PROP_KEYWORDS,
PROP_CREATOR,
PROP_PRODUCER,
PROP_CREATION_DATE,
PROP_MOD_DATE,
PROP_LINEARIZED,
PROP_PAGE_LAYOUT,
PROP_PAGE_MODE,
PROP_VIEWER_PREFERENCES,
PROP_PERMISSIONS,
PROP_METADATA,
PROP_PRINT_SCALING,
PROP_PRINT_DUPLEX,
PROP_PRINT_N_COPIES,
PROP_CREATION_DATETIME,
PROP_MOD_DATETIME
};
static void poppler_document_layers_free(PopplerDocument *document);
typedef struct _PopplerDocumentClass PopplerDocumentClass;
struct _PopplerDocumentClass
{
GObjectClass parent_class;
};
G_DEFINE_TYPE(PopplerDocument, poppler_document, G_TYPE_OBJECT)
static PopplerDocument *_poppler_document_new_from_pdfdoc(std::unique_ptr<GlobalParamsIniter> &&initer, PDFDoc *newDoc, GError **error)
{
PopplerDocument *document;
if (!newDoc->isOk()) {
int fopen_errno;
switch (newDoc->getErrorCode()) {
case errOpenFile:
// If there was an error opening the file, count it as a G_FILE_ERROR
// and set the GError parameters accordingly. (this assumes that the
// only way to get an errOpenFile error is if newDoc was created using
// a filename and thus fopen was called, which right now is true.
fopen_errno = newDoc->getFopenErrno();
g_set_error(error, G_FILE_ERROR, g_file_error_from_errno(fopen_errno), "%s", g_strerror(fopen_errno));
break;
case errBadCatalog:
g_set_error(error, POPPLER_ERROR, POPPLER_ERROR_BAD_CATALOG, "Failed to read the document catalog");
break;
case errDamaged:
g_set_error(error, POPPLER_ERROR, POPPLER_ERROR_DAMAGED, "PDF document is damaged");
break;
case errEncrypted:
g_set_error(error, POPPLER_ERROR, POPPLER_ERROR_ENCRYPTED, "Document is encrypted");
break;
default:
g_set_error(error, POPPLER_ERROR, POPPLER_ERROR_INVALID, "Failed to load document");
}
delete newDoc;
return nullptr;
}
document = (PopplerDocument *)g_object_new(POPPLER_TYPE_DOCUMENT, nullptr);
document->initer = std::move(initer);
document->doc = newDoc;
document->output_dev = new CairoOutputDev();
document->output_dev->startDoc(document->doc);
return document;
}
static std::optional<GooString> poppler_password_to_latin1(const gchar *password)
{
gchar *password_latin;
if (!password) {
return {};
}
password_latin = g_convert(password, -1, "ISO-8859-1", "UTF-8", nullptr, nullptr, nullptr);
std::optional<GooString> password_g = GooString(password_latin);
g_free(password_latin);
return password_g;
}
/**
* poppler_document_new_from_file:
* @uri: uri of the file to load
* @password: (allow-none): password to unlock the file with, or %NULL
* @error: (allow-none): Return location for an error, or %NULL
*
* Creates a new #PopplerDocument. If %NULL is returned, then @error will be
* set. Possible errors include those in the #POPPLER_ERROR and #G_FILE_ERROR
* domains.
*
* Return value: A newly created #PopplerDocument, or %NULL
**/
PopplerDocument *poppler_document_new_from_file(const char *uri, const char *password, GError **error)
{
PDFDoc *newDoc;
char *filename;
auto initer = std::make_unique<GlobalParamsIniter>(_poppler_error_cb);
filename = g_filename_from_uri(uri, nullptr, error);
if (!filename) {
return nullptr;
}
const std::optional<GooString> password_g = poppler_password_to_latin1(password);
#ifdef G_OS_WIN32
wchar_t *filenameW;
int length;
length = MultiByteToWideChar(CP_UTF8, 0, filename, -1, nullptr, 0);
filenameW = new WCHAR[length];
if (!filenameW)
return nullptr;
length = MultiByteToWideChar(CP_UTF8, 0, filename, -1, filenameW, length);
newDoc = new PDFDoc(filenameW, length, password_g, password_g);
if (!newDoc->isOk() && newDoc->getErrorCode() == errEncrypted && password) {
/* Try again with original password (which comes from GTK in UTF8) Issue #824 */
delete newDoc;
newDoc = new PDFDoc(filenameW, length, GooString(password), GooString(password));
}
delete[] filenameW;
#else
newDoc = new PDFDoc(std::make_unique<GooString>(filename), password_g, password_g);
if (!newDoc->isOk() && newDoc->getErrorCode() == errEncrypted && password) {
/* Try again with original password (which comes from GTK in UTF8) Issue #824 */
delete newDoc;
newDoc = new PDFDoc(std::make_unique<GooString>(filename), GooString(password), GooString(password));
}
#endif
g_free(filename);
return _poppler_document_new_from_pdfdoc(std::move(initer), newDoc, error);
}
/**
* poppler_document_new_from_data:
* @data: (array length=length) (element-type guint8): the pdf data
* @length: the length of #data
* @password: (nullable): password to unlock the file with, or %NULL
* @error: (nullable): Return location for an error, or %NULL
*
* Creates a new #PopplerDocument. If %NULL is returned, then @error will be
* set. Possible errors include those in the #POPPLER_ERROR and #G_FILE_ERROR
* domains.
*
* Note that @data is not copied nor is a new reference to it created.
* It must remain valid and cannot be destroyed as long as the returned
* document exists.
*
* Return value: A newly created #PopplerDocument, or %NULL
*
* Deprecated: 0.82: This requires directly managing @length and @data.
* Use poppler_document_new_from_bytes() instead.
**/
PopplerDocument *poppler_document_new_from_data(char *data, int length, const char *password, GError **error)
{
PDFDoc *newDoc;
MemStream *str;
auto initer = std::make_unique<GlobalParamsIniter>(_poppler_error_cb);
// create stream
str = new MemStream(data, 0, length, Object(objNull));
const std::optional<GooString> password_g = poppler_password_to_latin1(password);
newDoc = new PDFDoc(str, password_g, password_g);
if (!newDoc->isOk() && newDoc->getErrorCode() == errEncrypted && password) {
/* Try again with original password (which comes from GTK in UTF8) Issue #824 */
str = dynamic_cast<MemStream *>(str->copy());
delete newDoc;
newDoc = new PDFDoc(str, GooString(password), GooString(password));
}
return _poppler_document_new_from_pdfdoc(std::move(initer), newDoc, error);
}
class BytesStream : public MemStream
{
std::unique_ptr<GBytes, decltype(&g_bytes_unref)> m_bytes;
public:
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 } { }
~BytesStream() override;
};
BytesStream::~BytesStream() = default;
class OwningFileStream final : public FileStream
{
public:
OwningFileStream(std::unique_ptr<GooFile> fileA, Object &&dictA) : FileStream(fileA.get(), 0, false, fileA->size(), std::move(dictA)), file(std::move(fileA)) { }
~OwningFileStream() override;
private:
std::unique_ptr<GooFile> file;
};
OwningFileStream::~OwningFileStream() = default;
/**
* poppler_document_new_from_bytes:
* @bytes: a #GBytes
* @password: (allow-none): password to unlock the file with, or %NULL
* @error: (allow-none): Return location for an error, or %NULL
*
* Creates a new #PopplerDocument from @bytes. The returned document
* will hold a reference to @bytes.
*
* On error, %NULL is returned, with @error set. Possible errors include
* those in the #POPPLER_ERROR and #G_FILE_ERROR domains.
*
* Return value: (transfer full): a newly created #PopplerDocument, or %NULL
*
* Since: 0.82
**/
PopplerDocument *poppler_document_new_from_bytes(GBytes *bytes, const char *password, GError **error)
{
PDFDoc *newDoc;
BytesStream *str;
g_return_val_if_fail(bytes != nullptr, nullptr);
g_return_val_if_fail(error == nullptr || *error == nullptr, nullptr);
auto initer = std::make_unique<GlobalParamsIniter>(_poppler_error_cb);
// create stream
str = new BytesStream(bytes, Object(objNull));
const std::optional<GooString> password_g = poppler_password_to_latin1(password);
newDoc = new PDFDoc(str, password_g, password_g);
if (!newDoc->isOk() && newDoc->getErrorCode() == errEncrypted && password) {
/* Try again with original password (which comes from GTK in UTF8) Issue #824 */
str = dynamic_cast<BytesStream *>(str->copy());
delete newDoc;
newDoc = new PDFDoc(str, GooString(password), GooString(password));
}
return _poppler_document_new_from_pdfdoc(std::move(initer), newDoc, error);
}
static inline gboolean stream_is_memory_buffer_or_local_file(GInputStream *stream)
{
return G_IS_MEMORY_INPUT_STREAM(stream) || (G_IS_FILE_INPUT_STREAM(stream) && strcmp(g_type_name_from_instance((GTypeInstance *)stream), "GLocalFileInputStream") == 0);
}
/**
* poppler_document_new_from_stream:
* @stream: a #GInputStream to read from
* @length: the stream length, or -1 if not known
* @password: (allow-none): password to unlock the file with, or %NULL
* @cancellable: (allow-none): a #GCancellable, or %NULL
* @error: (allow-none): Return location for an error, or %NULL
*
* Creates a new #PopplerDocument reading the PDF contents from @stream.
* Note that the given #GInputStream must be seekable or %G_IO_ERROR_NOT_SUPPORTED
* will be returned.
* Possible errors include those in the #POPPLER_ERROR, #G_FILE_ERROR
* and #G_IO_ERROR domains.
*
* Returns: (transfer full): a new #PopplerDocument, or %NULL
*
* Since: 0.22
*/
PopplerDocument *poppler_document_new_from_stream(GInputStream *stream, goffset length, const char *password, GCancellable *cancellable, GError **error)
{
PDFDoc *newDoc;
BaseStream *str;
g_return_val_if_fail(G_IS_INPUT_STREAM(stream), NULL);
g_return_val_if_fail(length == (goffset)-1 || length > 0, NULL);
auto initer = std::make_unique<GlobalParamsIniter>(_poppler_error_cb);
if (!G_IS_SEEKABLE(stream) || !g_seekable_can_seek(G_SEEKABLE(stream))) {
g_set_error_literal(error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "Stream is not seekable");
return nullptr;
}
if (stream_is_memory_buffer_or_local_file(stream)) {
if (length == (goffset)-1) {
if (!g_seekable_seek(G_SEEKABLE(stream), 0, G_SEEK_END, cancellable, error)) {
g_prefix_error(error, "Unable to determine length of stream: ");
return nullptr;
}
length = g_seekable_tell(G_SEEKABLE(stream));
}
str = new PopplerInputStream(stream, cancellable, 0, false, length, Object(objNull));
} else {
CachedFile *cachedFile = new CachedFile(new PopplerCachedFileLoader(stream, cancellable, length));
str = new CachedFileStream(cachedFile, 0, false, cachedFile->getLength(), Object(objNull));
}
const std::optional<GooString> password_g = poppler_password_to_latin1(password);
newDoc = new PDFDoc(str, password_g, password_g);
if (!newDoc->isOk() && newDoc->getErrorCode() == errEncrypted && password) {
/* Try again with original password (which comes from GTK in UTF8) Issue #824 */
str = str->copy();
delete newDoc;
newDoc = new PDFDoc(str, GooString(password), GooString(password));
}
return _poppler_document_new_from_pdfdoc(std::move(initer), newDoc, error);
}
/**
* poppler_document_new_from_gfile:
* @file: a #GFile to load
* @password: (allow-none): password to unlock the file with, or %NULL
* @cancellable: (allow-none): a #GCancellable, or %NULL
* @error: (allow-none): Return location for an error, or %NULL
*
* Creates a new #PopplerDocument reading the PDF contents from @file.
* Possible errors include those in the #POPPLER_ERROR and #G_FILE_ERROR
* domains.
*
* Returns: (transfer full): a new #PopplerDocument, or %NULL
*
* Since: 0.22
*/
PopplerDocument *poppler_document_new_from_gfile(GFile *file, const char *password, GCancellable *cancellable, GError **error)
{
PopplerDocument *document;
GFileInputStream *stream;
g_return_val_if_fail(G_IS_FILE(file), NULL);
if (g_file_is_native(file)) {
gchar *uri;
uri = g_file_get_uri(file);
document = poppler_document_new_from_file(uri, password, error);
g_free(uri);
return document;
}
stream = g_file_read(file, cancellable, error);
if (!stream) {
return nullptr;
}
document = poppler_document_new_from_stream(G_INPUT_STREAM(stream), -1, password, cancellable, error);
g_object_unref(stream);
return document;
}
#ifndef G_OS_WIN32
/**
* poppler_document_new_from_fd:
* @fd: a valid file descriptor
* @password: (allow-none): password to unlock the file with, or %NULL
* @error: (allow-none): Return location for an error, or %NULL
*
* Creates a new #PopplerDocument reading the PDF contents from the file
* descriptor @fd. @fd must refer to a regular file, or STDIN, and be open
* for reading.
* Possible errors include those in the #POPPLER_ERROR and #G_FILE_ERROR
* domains.
* Note that this function takes ownership of @fd; you must not operate on it
* again, nor close it.
*
* Returns: (transfer full): a new #PopplerDocument, or %NULL
*
* Since: 21.12.0
*/
PopplerDocument *poppler_document_new_from_fd(int fd, const char *password, GError **error)
{
struct stat statbuf;
int flags;
BaseStream *stream;
PDFDoc *newDoc;
g_return_val_if_fail(fd != -1, nullptr);
auto initer = std::make_unique<GlobalParamsIniter>(_poppler_error_cb);
if (fstat(fd, &statbuf) == -1 || (flags = fcntl(fd, F_GETFL, &flags)) == -1) {
int errsv = errno;
g_set_error_literal(error, G_FILE_ERROR, g_file_error_from_errno(errsv), g_strerror(errsv));
close(fd);
return nullptr;
}
switch (flags & O_ACCMODE) {
case O_RDONLY:
case O_RDWR:
break;
case O_WRONLY:
default:
g_set_error(error, G_FILE_ERROR, G_FILE_ERROR_BADF, "File descriptor %d is not readable", fd);
close(fd);
return nullptr;
}
if (fd == fileno(stdin) || !S_ISREG(statbuf.st_mode)) {
FILE *file;
if (fd == fileno(stdin)) {
file = stdin;
} else {
file = fdopen(fd, "rb");
if (!file) {
int errsv = errno;
g_set_error_literal(error, G_FILE_ERROR, g_file_error_from_errno(errsv), g_strerror(errsv));
close(fd);
return nullptr;
}
}
CachedFile *cachedFile = new CachedFile(new FILECacheLoader(file));
stream = new CachedFileStream(cachedFile, 0, false, cachedFile->getLength(), Object(objNull));
} else {
stream = new OwningFileStream(GooFile::open(fd), Object(objNull));
}
const std::optional<GooString> password_g = poppler_password_to_latin1(password);
newDoc = new PDFDoc(stream, password_g, password_g);
if (!newDoc->isOk() && newDoc->getErrorCode() == errEncrypted && password) {
/* Try again with original password (which comes from GTK in UTF8) Issue #824 */
stream = stream->copy();
delete newDoc;
newDoc = new PDFDoc(stream, GooString(password), GooString(password));
}
return _poppler_document_new_from_pdfdoc(std::move(initer), newDoc, error);
}
#endif /* !G_OS_WIN32 */
static gboolean handle_save_error(int err_code, GError **error)
{
switch (err_code) {
case errNone:
break;
case errOpenFile:
g_set_error(error, POPPLER_ERROR, POPPLER_ERROR_OPEN_FILE, "Failed to open file for writing");
break;
case errEncrypted:
g_set_error(error, POPPLER_ERROR, POPPLER_ERROR_ENCRYPTED, "Document is encrypted");
break;
default:
g_set_error(error, POPPLER_ERROR, POPPLER_ERROR_INVALID, "Failed to save document");
}
return err_code == errNone;
}
/**
* poppler_document_save:
* @document: a #PopplerDocument
* @uri: uri of file to save
* @error: (allow-none): return location for an error, or %NULL
*
* Saves @document. Any change made in the document such as
* form fields filled, annotations added or modified
* will be saved.
* If @error is set, %FALSE will be returned. Possible errors
* include those in the #G_FILE_ERROR domain.
*
* Return value: %TRUE, if the document was successfully saved
**/
gboolean poppler_document_save(PopplerDocument *document, const char *uri, GError **error)
{
char *filename;
gboolean retval = FALSE;
g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), FALSE);
filename = g_filename_from_uri(uri, nullptr, error);
if (filename != nullptr) {
GooString fname(filename);
int err_code;
g_free(filename);
err_code = document->doc->saveAs(fname);
retval = handle_save_error(err_code, error);
}
return retval;
}
/**
* poppler_document_save_a_copy:
* @document: a #PopplerDocument
* @uri: uri of file to save
* @error: (allow-none): return location for an error, or %NULL
*
* Saves a copy of the original @document.
* Any change made in the document such as
* form fields filled by the user will not be saved.
* If @error is set, %FALSE will be returned. Possible errors
* include those in the #G_FILE_ERROR domain.
*
* Return value: %TRUE, if the document was successfully saved
**/
gboolean poppler_document_save_a_copy(PopplerDocument *document, const char *uri, GError **error)
{
char *filename;
gboolean retval = FALSE;
g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), FALSE);
filename = g_filename_from_uri(uri, nullptr, error);
if (filename != nullptr) {
GooString fname(filename);
int err_code;
g_free(filename);
err_code = document->doc->saveWithoutChangesAs(fname);
retval = handle_save_error(err_code, error);
}
return retval;
}
#ifndef G_OS_WIN32
/**
* poppler_document_save_to_fd:
* @document: a #PopplerDocument
* @fd: a valid file descriptor open for writing
* @include_changes: whether to include user changes (e.g. form fills)
* @error: (allow-none): return location for an error, or %NULL
*
* Saves @document. Any change made in the document such as
* form fields filled, annotations added or modified
* will be saved if @include_changes is %TRUE, or discarded i
* @include_changes is %FALSE.
*
* Note that this function takes ownership of @fd; you must not operate on it
* again, nor close it.
*
* If @error is set, %FALSE will be returned. Possible errors
* include those in the #G_FILE_ERROR domain.
*
* Return value: %TRUE, if the document was successfully saved
*
* Since: 21.12.0
**/
gboolean poppler_document_save_to_fd(PopplerDocument *document, int fd, gboolean include_changes, GError **error)
{
FILE *file;
OutStream *stream;
int rv;
g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), FALSE);
g_return_val_if_fail(fd != -1, FALSE);
file = fdopen(fd, "wb");
if (file == nullptr) {
int errsv = errno;
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));
return FALSE;
}
stream = new FileOutStream(file, 0);
if (include_changes) {
rv = document->doc->saveAs(stream);
} else {
rv = document->doc->saveWithoutChangesAs(stream);
}
delete stream;
return handle_save_error(rv, error);
}
#endif /* !G_OS_WIN32 */
static void poppler_document_finalize(GObject *object)
{
PopplerDocument *document = POPPLER_DOCUMENT(object);
poppler_document_layers_free(document);
delete document->output_dev;
delete document->doc;
delete document->initer.release();
G_OBJECT_CLASS(poppler_document_parent_class)->finalize(object);
}
/**
* poppler_document_get_id:
* @document: A #PopplerDocument
* @permanent_id: (out) (allow-none): location to store an allocated string, use g_free() to free the returned string
* @update_id: (out) (allow-none): location to store an allocated string, use g_free() to free the returned string
*
* Returns the PDF file identifier represented as two byte string arrays of size 32.
* @permanent_id is the permanent identifier that is built based on the file
* contents at the time it was originally created, so that this identifer
* never changes. @update_id is the update identifier that is built based on
* the file contents at the time it was last updated.
*
* Note that returned strings are not null-terminated, they have a fixed
* size of 32 bytes.
*
* Returns: %TRUE if the @document contains an id, %FALSE otherwise
*
* Since: 0.16
*/
gboolean poppler_document_get_id(PopplerDocument *document, gchar **permanent_id, gchar **update_id)
{
GooString permanent;
GooString update;
gboolean retval = FALSE;
g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), FALSE);
if (permanent_id) {
*permanent_id = nullptr;
}
if (update_id) {
*update_id = nullptr;
}
if (document->doc->getID(permanent_id ? &permanent : nullptr, update_id ? &update : nullptr)) {
if (permanent_id) {
*permanent_id = g_new(char, 32);
memcpy(*permanent_id, permanent.c_str(), 32);
}
if (update_id) {
*update_id = g_new(char, 32);
memcpy(*update_id, update.c_str(), 32);
}
retval = TRUE;
}
return retval;
}
/**
* poppler_document_get_n_pages:
* @document: A #PopplerDocument
*
* Returns the number of pages in a loaded document.
*
* Return value: Number of pages
**/
int poppler_document_get_n_pages(PopplerDocument *document)
{
g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), 0);
return document->doc->getNumPages();
}
/**
* poppler_document_get_page:
* @document: A #PopplerDocument
* @index: a page index
*
* Returns the #PopplerPage indexed at @index. This object is owned by the
* caller.
*
* Return value: (transfer full) : The #PopplerPage at @index
**/
PopplerPage *poppler_document_get_page(PopplerDocument *document, int index)
{
Page *page;
g_return_val_if_fail(0 <= index && index < poppler_document_get_n_pages(document), NULL);
page = document->doc->getPage(index + 1);
if (!page) {
return nullptr;
}
return _poppler_page_new(document, page, index);
}
/**
* poppler_document_get_page_by_label:
* @document: A #PopplerDocument
* @label: a page label
*
* Returns the #PopplerPage reference by @label. This object is owned by the
* caller. @label is a human-readable string representation of the page number,
* and can be document specific. Typically, it is a value such as "iii" or "3".
*
* By default, "1" refers to the first page.
*
* Return value: (transfer full) :The #PopplerPage referenced by @label
**/
PopplerPage *poppler_document_get_page_by_label(PopplerDocument *document, const char *label)
{
Catalog *catalog;
GooString label_g(label);
int index;
catalog = document->doc->getCatalog();
if (!catalog->labelToIndex(&label_g, &index)) {
return nullptr;
}
return poppler_document_get_page(document, index);
}
/**
* poppler_document_get_n_attachments:
* @document: A #PopplerDocument
*
* Returns the number of attachments in a loaded document.
* See also poppler_document_get_attachments()
*
* Return value: Number of attachments
*
* Since: 0.18
*/
guint poppler_document_get_n_attachments(PopplerDocument *document)
{
Catalog *catalog;
g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), 0);
catalog = document->doc->getCatalog();
return catalog && catalog->isOk() ? catalog->numEmbeddedFiles() : 0;
}
/**
* poppler_document_has_attachments:
* @document: A #PopplerDocument
*
* Returns %TRUE of @document has any attachments.
*
* Return value: %TRUE, if @document has attachments.
**/
gboolean poppler_document_has_attachments(PopplerDocument *document)
{
g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), FALSE);
return (poppler_document_get_n_attachments(document) != 0);
}
/**
* poppler_document_get_attachments:
* @document: A #PopplerDocument
*
* Returns a #GList containing #PopplerAttachment<!-- -->s. These attachments
* are unowned, and must be unreffed, and the list must be freed with
* g_list_free().
*
* Return value: (element-type PopplerAttachment) (transfer full): a list of available attachments.
**/
GList *poppler_document_get_attachments(PopplerDocument *document)
{
Catalog *catalog;
int n_files, i;
GList *retval = nullptr;
g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), NULL);
catalog = document->doc->getCatalog();
if (catalog == nullptr || !catalog->isOk()) {
return nullptr;
}
n_files = catalog->numEmbeddedFiles();
for (i = 0; i < n_files; i++) {
PopplerAttachment *attachment;
const std::unique_ptr<FileSpec> emb_file = catalog->embeddedFile(i);
if (!emb_file->isOk() || !emb_file->getEmbeddedFile()->isOk()) {
continue;
}
attachment = _poppler_attachment_new(emb_file.get());
if (attachment != nullptr) {
retval = g_list_prepend(retval, attachment);
}
}
return g_list_reverse(retval);
}
/**
* poppler_named_dest_from_bytestring:
* @data: (array length=length): the bytestring data
* @length: the bytestring length
*
* Converts a bytestring into a zero-terminated string suitable to
* pass to poppler_document_find_dest().
*
* Note that the returned string has no defined encoding and is not
* suitable for display to the user.
*
* The returned data must be freed using g_free().
*
* Returns: (transfer full): the named dest
*
* Since: 0.73
*/
char *poppler_named_dest_from_bytestring(const guint8 *data, gsize length)
{
const guint8 *p, *pend;
char *dest, *q;
g_return_val_if_fail(length != 0 || data != nullptr, nullptr);
/* Each source byte needs maximally 2 destination chars (\\ or \0) */
q = dest = (gchar *)g_malloc(length * 2 + 1);
pend = data + length;
for (p = data; p < pend; ++p) {
switch (*p) {
case '\0':
*q++ = '\\';
*q++ = '0';
break;
case '\\':
*q++ = '\\';
*q++ = '\\';
break;
default:
*q++ = *p;
break;
}
}
*q = 0; /* zero terminate */
return dest;
}
/**
* poppler_named_dest_to_bytestring:
* @name: the named dest string
* @length: (out): a location to store the length of the returned bytestring
*
* Converts a named dest string (e.g. from #PopplerDest.named_dest) into a
* bytestring, inverting the transformation of
* poppler_named_dest_from_bytestring().
*
* Note that the returned data is not zero terminated and may also
* contains embedded NUL bytes.
*
* If @name is not a valid named dest string, returns %NULL.
*
* The returned data must be freed using g_free().
*
* Returns: (array length=length) (transfer full) (nullable): a new bytestring,
* or %NULL
*
* Since: 0.73
*/
guint8 *poppler_named_dest_to_bytestring(const char *name, gsize *length)
{
const char *p;
guint8 *data, *q;
gsize len;
g_return_val_if_fail(name != nullptr, nullptr);
g_return_val_if_fail(length != nullptr, nullptr);
len = strlen(name);
q = data = (guint8 *)g_malloc(len);
for (p = name; *p; ++p) {
if (*p == '\\') {
p++;
len--;
if (*p == '0') {
*q++ = '\0';
} else if (*p == '\\') {
*q++ = '\\';
} else {
goto invalid;
}
} else {
*q++ = *p;
}
}
*length = len;
return data;
invalid:
g_free(data);
*length = 0;
return nullptr;
}
/**
* poppler_document_find_dest:
* @document: A #PopplerDocument
* @link_name: a named destination
*
* Creates a #PopplerDest for the named destination @link_name in @document.
*
* Note that named destinations are bytestrings, not string. That means that
* unless @link_name was returned by a poppler function (e.g. is
* #PopplerDest.named_dest), it needs to be converted to string
* using poppler_named_dest_from_bytestring() before being passed to this
* function.
*
* The returned value must be freed with poppler_dest_free().
*
* Return value: (transfer full): a new #PopplerDest destination, or %NULL if
* @link_name is not a destination.
**/
PopplerDest *poppler_document_find_dest(PopplerDocument *document, const gchar *link_name)
{
g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), nullptr);
g_return_val_if_fail(link_name != nullptr, nullptr);
gsize len;
guint8 *data = poppler_named_dest_to_bytestring(link_name, &len);
if (data == nullptr) {
return nullptr;
}
GooString g_link_name((const char *)data, (int)len);
g_free(data);
std::unique_ptr<LinkDest> link_dest = document->doc->findDest(&g_link_name);
if (link_dest == nullptr) {
return nullptr;
}
PopplerDest *dest = _poppler_dest_new_goto(document, link_dest.get());
return dest;
}
static gint _poppler_dest_compare_keys(gconstpointer a, gconstpointer b, gpointer user_data)
{
return g_strcmp0(static_cast<const gchar *>(a), static_cast<const gchar *>(b));
}
static void _poppler_dest_destroy_value(gpointer value)
{
poppler_dest_free(static_cast<PopplerDest *>(value));
}
/**
* poppler_document_create_dests_tree:
* @document: A #PopplerDocument
*
* Creates a balanced binary tree of all named destinations in @document
*
* The tree key is strings in the form returned by
* poppler_named_dest_to_bytestring() which constains a destination name.
* The tree value is the #PopplerDest which contains a named destination.
* The return value must be freed with g_tree_destroy().
*
* Returns: (transfer full) (nullable): the #GTree, or %NULL
* Since: 0.78
**/
GTree *poppler_document_create_dests_tree(PopplerDocument *document)
{
GTree *tree;
Catalog *catalog;
PopplerDest *dest;
int i;
g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), nullptr);
catalog = document->doc->getCatalog();
if (catalog == nullptr) {
return nullptr;
}
tree = g_tree_new_full(_poppler_dest_compare_keys, nullptr, g_free, _poppler_dest_destroy_value);
// Iterate from name-dict
const int nDests = catalog->numDests();
for (i = 0; i < nDests; ++i) {
// The names of name-dict cannot contain \0,
// so we can use strlen().
auto name = catalog->getDestsName(i);
std::unique_ptr<LinkDest> link_dest = catalog->getDestsDest(i);
if (link_dest) {
gchar *key = poppler_named_dest_from_bytestring(reinterpret_cast<const guint8 *>(name), strlen(name));
dest = _poppler_dest_new_goto(document, link_dest.get());
g_tree_insert(tree, key, dest);
}
}
// Iterate form name-tree
const int nDestsNameTree = catalog->numDestNameTree();
for (i = 0; i < nDestsNameTree; ++i) {
auto name = catalog->getDestNameTreeName(i);
std::unique_ptr<LinkDest> link_dest = catalog->getDestNameTreeDest(i);
if (link_dest) {
gchar *key = poppler_named_dest_from_bytestring(reinterpret_cast<const guint8 *>(name->c_str()), name->getLength());
dest = _poppler_dest_new_goto(document, link_dest.get());
g_tree_insert(tree, key, dest);
}
}
return tree;
}
char *_poppler_goo_string_to_utf8(const GooString *s)
{
if (s == nullptr) {
return nullptr;
}
char *result;
if (hasUnicodeByteOrderMark(s->toStr())) {
result = g_convert(s->c_str() + 2, s->getLength() - 2, "UTF-8", "UTF-16BE", nullptr, nullptr, nullptr);
} else if (hasUnicodeByteOrderMarkLE(s->toStr())) {
result = g_convert(s->c_str() + 2, s->getLength() - 2, "UTF-8", "UTF-16LE", nullptr, nullptr, nullptr);
} else {
int len;
gunichar *ucs4_temp;
int i;
len = s->getLength();
ucs4_temp = g_new(gunichar, len + 1);
for (i = 0; i < len; ++i) {
ucs4_temp[i] = pdfDocEncoding[(unsigned char)s->getChar(i)];
}
ucs4_temp[i] = 0;
result = g_ucs4_to_utf8(ucs4_temp, -1, nullptr, nullptr, nullptr);
g_free(ucs4_temp);
}
return result;
}
static GooString *_poppler_goo_string_from_utf8(const gchar *src)
{
if (src == nullptr) {
return nullptr;
}
gsize outlen;
gchar *utf16 = g_convert(src, -1, "UTF-16BE", "UTF-8", nullptr, &outlen, nullptr);
if (utf16 == nullptr) {
return nullptr;
}
GooString *result = new GooString(utf16, outlen);
g_free(utf16);
if (!hasUnicodeByteOrderMark(result->toStr())) {
prependUnicodeByteOrderMark(result->toNonConstStr());
}
return result;
}
static PopplerPageLayout convert_page_layout(Catalog::PageLayout pageLayout)
{
switch (pageLayout) {
case Catalog::pageLayoutSinglePage:
return POPPLER_PAGE_LAYOUT_SINGLE_PAGE;
case Catalog::pageLayoutOneColumn:
return POPPLER_PAGE_LAYOUT_ONE_COLUMN;
case Catalog::pageLayoutTwoColumnLeft:
return POPPLER_PAGE_LAYOUT_TWO_COLUMN_LEFT;
case Catalog::pageLayoutTwoColumnRight:
return POPPLER_PAGE_LAYOUT_TWO_COLUMN_RIGHT;
case Catalog::pageLayoutTwoPageLeft:
return POPPLER_PAGE_LAYOUT_TWO_PAGE_LEFT;
case Catalog::pageLayoutTwoPageRight:
return POPPLER_PAGE_LAYOUT_TWO_PAGE_RIGHT;
case Catalog::pageLayoutNone:
default:
return POPPLER_PAGE_LAYOUT_UNSET;
}
}
static PopplerPageMode convert_page_mode(Catalog::PageMode pageMode)
{
switch (pageMode) {
case Catalog::pageModeOutlines:
return POPPLER_PAGE_MODE_USE_OUTLINES;
case Catalog::pageModeThumbs:
return POPPLER_PAGE_MODE_USE_THUMBS;
case Catalog::pageModeFullScreen:
return POPPLER_PAGE_MODE_FULL_SCREEN;
case Catalog::pageModeOC:
return POPPLER_PAGE_MODE_USE_OC;
case Catalog::pageModeAttach:
return POPPLER_PAGE_MODE_USE_ATTACHMENTS;
case Catalog::pageModeNone:
default:
return POPPLER_PAGE_MODE_UNSET;
}
}
static PopplerPDFSubtype convert_pdf_subtype(PDFSubtype pdfSubtype)
{
switch (pdfSubtype) {
case subtypePDFA:
return POPPLER_PDF_SUBTYPE_PDF_A;
case subtypePDFE:
return POPPLER_PDF_SUBTYPE_PDF_E;
case subtypePDFUA:
return POPPLER_PDF_SUBTYPE_PDF_UA;
case subtypePDFVT:
return POPPLER_PDF_SUBTYPE_PDF_VT;
case subtypePDFX:
return POPPLER_PDF_SUBTYPE_PDF_X;
case subtypeNone:
return POPPLER_PDF_SUBTYPE_NONE;
case subtypeNull:
default:
return POPPLER_PDF_SUBTYPE_UNSET;
}
}
static PopplerPDFPart convert_pdf_subtype_part(PDFSubtypePart pdfSubtypePart)
{
switch (pdfSubtypePart) {
case subtypePart1:
return POPPLER_PDF_SUBTYPE_PART_1;
case subtypePart2:
return POPPLER_PDF_SUBTYPE_PART_2;
case subtypePart3:
return POPPLER_PDF_SUBTYPE_PART_3;
case subtypePart4:
return POPPLER_PDF_SUBTYPE_PART_4;
case subtypePart5:
return POPPLER_PDF_SUBTYPE_PART_5;
case subtypePart6:
return POPPLER_PDF_SUBTYPE_PART_6;
case subtypePart7:
return POPPLER_PDF_SUBTYPE_PART_7;
case subtypePart8:
return POPPLER_PDF_SUBTYPE_PART_8;
case subtypePartNone:
return POPPLER_PDF_SUBTYPE_PART_NONE;
case subtypePartNull:
default:
return POPPLER_PDF_SUBTYPE_PART_UNSET;
}
}
static PopplerPDFConformance convert_pdf_subtype_conformance(PDFSubtypeConformance pdfSubtypeConf)
{
switch (pdfSubtypeConf) {
case subtypeConfA:
return POPPLER_PDF_SUBTYPE_CONF_A;
case subtypeConfB:
return POPPLER_PDF_SUBTYPE_CONF_B;
case subtypeConfG:
return POPPLER_PDF_SUBTYPE_CONF_G;
case subtypeConfN:
return POPPLER_PDF_SUBTYPE_CONF_N;
case subtypeConfP:
return POPPLER_PDF_SUBTYPE_CONF_P;
case subtypeConfPG:
return POPPLER_PDF_SUBTYPE_CONF_PG;
case subtypeConfU:
return POPPLER_PDF_SUBTYPE_CONF_U;
case subtypeConfNone:
return POPPLER_PDF_SUBTYPE_CONF_NONE;
case subtypeConfNull:
default:
return POPPLER_PDF_SUBTYPE_CONF_UNSET;
}
}
/**
* poppler_document_get_pdf_version_string:
* @document: A #PopplerDocument
*
* Returns the PDF version of @document as a string (e.g. PDF-1.6)
*
* Return value: a new allocated string containing the PDF version
* of @document, or %NULL
*
* Since: 0.16
**/
gchar *poppler_document_get_pdf_version_string(PopplerDocument *document)
{
gchar *retval;
g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), NULL);
retval = g_strndup("PDF-", 15); /* allocates 16 chars, pads with \0s */
g_ascii_formatd(retval + 4, 15 + 1 - 4, "%.2g", document->doc->getPDFMajorVersion() + document->doc->getPDFMinorVersion() / 10.0);
return retval;
}
/**
* poppler_document_get_pdf_version:
* @document: A #PopplerDocument
* @major_version: (out) (nullable): return location for the PDF major version number
* @minor_version: (out) (nullable): return location for the PDF minor version number
*
* Updates values referenced by @major_version & @minor_version with the
* major and minor PDF versions of @document.
*
* Since: 0.16
**/
void poppler_document_get_pdf_version(PopplerDocument *document, guint *major_version, guint *minor_version)
{
g_return_if_fail(POPPLER_IS_DOCUMENT(document));
if (major_version) {
*major_version = document->doc->getPDFMajorVersion();
}
if (minor_version) {
*minor_version = document->doc->getPDFMinorVersion();
}
}
/**
* poppler_document_get_title:
* @document: A #PopplerDocument
*
* Returns the document's title
*
* Return value: a new allocated string containing the title
* of @document, or %NULL
*
* Since: 0.16
**/
gchar *poppler_document_get_title(PopplerDocument *document)
{
g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), NULL);
const std::unique_ptr<GooString> goo_title = document->doc->getDocInfoTitle();
return _poppler_goo_string_to_utf8(goo_title.get());
}
/**
* poppler_document_set_title:
* @document: A #PopplerDocument
* @title: A new title
*
* Sets the document's title. If @title is %NULL, Title entry
* is removed from the document's Info dictionary.
*
* Since: 0.46
**/
void poppler_document_set_title(PopplerDocument *document, const gchar *title)
{
g_return_if_fail(POPPLER_IS_DOCUMENT(document));
GooString *goo_title;
if (!title) {
goo_title = nullptr;
} else {
goo_title = _poppler_goo_string_from_utf8(title);
if (!goo_title) {
return;
}
}
document->doc->setDocInfoTitle(goo_title);
}
/**
* poppler_document_get_author:
* @document: A #PopplerDocument
*
* Returns the author of the document
*
* Return value: a new allocated string containing the author
* of @document, or %NULL
*
* Since: 0.16
**/
gchar *poppler_document_get_author(PopplerDocument *document)
{
g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), NULL);
const std::unique_ptr<GooString> goo_author = document->doc->getDocInfoAuthor();
return _poppler_goo_string_to_utf8(goo_author.get());
}
/**
* poppler_document_set_author:
* @document: A #PopplerDocument
* @author: A new author
*
* Sets the document's author. If @author is %NULL, Author
* entry is removed from the document's Info dictionary.
*
* Since: 0.46
**/
void poppler_document_set_author(PopplerDocument *document, const gchar *author)
{
g_return_if_fail(POPPLER_IS_DOCUMENT(document));
GooString *goo_author;
if (!author) {
goo_author = nullptr;
} else {
goo_author = _poppler_goo_string_from_utf8(author);
if (!goo_author) {
return;
}
}
document->doc->setDocInfoAuthor(goo_author);
}
/**
* poppler_document_get_subject:
* @document: A #PopplerDocument
*
* Returns the subject of the document
*
* Return value: a new allocated string containing the subject
* of @document, or %NULL
*
* Since: 0.16
**/
gchar *poppler_document_get_subject(PopplerDocument *document)
{
g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), NULL);
const std::unique_ptr<GooString> goo_subject = document->doc->getDocInfoSubject();
return _poppler_goo_string_to_utf8(goo_subject.get());
}
/**
* poppler_document_set_subject:
* @document: A #PopplerDocument
* @subject: A new subject
*
* Sets the document's subject. If @subject is %NULL, Subject
* entry is removed from the document's Info dictionary.
*
* Since: 0.46
**/
void poppler_document_set_subject(PopplerDocument *document, const gchar *subject)
{
g_return_if_fail(POPPLER_IS_DOCUMENT(document));
GooString *goo_subject;
if (!subject) {
goo_subject = nullptr;
} else {
goo_subject = _poppler_goo_string_from_utf8(subject);
if (!goo_subject) {
return;
}
}
document->doc->setDocInfoSubject(goo_subject);
}
/**
* poppler_document_get_keywords:
* @document: A #PopplerDocument
*
* Returns the keywords associated to the document
*
* Return value: a new allocated string containing keywords associated
* to @document, or %NULL
*
* Since: 0.16
**/
gchar *poppler_document_get_keywords(PopplerDocument *document)
{
g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), NULL);
const std::unique_ptr<GooString> goo_keywords = document->doc->getDocInfoKeywords();
return _poppler_goo_string_to_utf8(goo_keywords.get());
}
/**
* poppler_document_set_keywords:
* @document: A #PopplerDocument
* @keywords: New keywords
*
* Sets the document's keywords. If @keywords is %NULL,
* Keywords entry is removed from the document's Info dictionary.
*
* Since: 0.46
**/
void poppler_document_set_keywords(PopplerDocument *document, const gchar *keywords)
{
g_return_if_fail(POPPLER_IS_DOCUMENT(document));
GooString *goo_keywords;
if (!keywords) {
goo_keywords = nullptr;
} else {
goo_keywords = _poppler_goo_string_from_utf8(keywords);
if (!goo_keywords) {
return;
}
}
document->doc->setDocInfoKeywords(goo_keywords);
}
/**
* poppler_document_get_creator:
* @document: A #PopplerDocument
*
* Returns the creator of the document. If the document was converted
* from another format, the creator is the name of the product
* that created the original document from which it was converted.
*
* Return value: a new allocated string containing the creator
* of @document, or %NULL
*
* Since: 0.16
**/
gchar *poppler_document_get_creator(PopplerDocument *document)
{
g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), NULL);
const std::unique_ptr<GooString> goo_creator = document->doc->getDocInfoCreator();
return _poppler_goo_string_to_utf8(goo_creator.get());
}
/**
* poppler_document_set_creator:
* @document: A #PopplerDocument
* @creator: A new creator
*
* Sets the document's creator. If @creator is %NULL, Creator
* entry is removed from the document's Info dictionary.
*
* Since: 0.46
**/
void poppler_document_set_creator(PopplerDocument *document, const gchar *creator)
{
g_return_if_fail(POPPLER_IS_DOCUMENT(document));
GooString *goo_creator;
if (!creator) {
goo_creator = nullptr;
} else {
goo_creator = _poppler_goo_string_from_utf8(creator);
if (!goo_creator) {
return;
}
}
document->doc->setDocInfoCreator(goo_creator);
}
/**
* poppler_document_get_producer:
* @document: A #PopplerDocument
*
* Returns the producer of the document. If the document was converted
* from another format, the producer is the name of the product
* that converted it to PDF
*
* Return value: a new allocated string containing the producer
* of @document, or %NULL
*
* Since: 0.16
**/
gchar *poppler_document_get_producer(PopplerDocument *document)
{
g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), NULL);
const std::unique_ptr<GooString> goo_producer = document->doc->getDocInfoProducer();
return _poppler_goo_string_to_utf8(goo_producer.get());
}
/**
* poppler_document_set_producer:
* @document: A #PopplerDocument
* @producer: A new producer
*
* Sets the document's producer. If @producer is %NULL,
* Producer entry is removed from the document's Info dictionary.
*
* Since: 0.46
**/
void poppler_document_set_producer(PopplerDocument *document, const gchar *producer)
{
g_return_if_fail(POPPLER_IS_DOCUMENT(document));
GooString *goo_producer;
if (!producer) {
goo_producer = nullptr;
} else {
goo_producer = _poppler_goo_string_from_utf8(producer);
if (!goo_producer) {
return;
}
}
document->doc->setDocInfoProducer(goo_producer);
}
/**
* poppler_document_get_creation_date:
* @document: A #PopplerDocument
*
* Returns the date the document was created as seconds since the Epoch
*
* Return value: the date the document was created, or -1
*
* Since: 0.16
**/
time_t poppler_document_get_creation_date(PopplerDocument *document)
{
g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), (time_t)-1);
const std::unique_ptr<GooString> str = document->doc->getDocInfoCreatDate();
if (!str) {
return (time_t)-1;
}
time_t date;
gboolean success = _poppler_convert_pdf_date_to_gtime(str.get(), &date);
return (success) ? date : (time_t)-1;
}
/**
* poppler_document_set_creation_date:
* @document: A #PopplerDocument
* @creation_date: A new creation date
*
* Sets the document's creation date. If @creation_date is -1, CreationDate
* entry is removed from the document's Info dictionary.
*
* Since: 0.46
**/
void poppler_document_set_creation_date(PopplerDocument *document, time_t creation_date)
{
g_return_if_fail(POPPLER_IS_DOCUMENT(document));
GooString *str = creation_date == (time_t)-1 ? nullptr : timeToDateString(&creation_date);
document->doc->setDocInfoCreatDate(str);
}
/**
* poppler_document_get_creation_date_time:
* @document: A #PopplerDocument
*
* Returns the date the document was created as a #GDateTime
*
* Returns: (nullable): the date the document was created, or %NULL
*
* Since: 20.09.0
**/
GDateTime *poppler_document_get_creation_date_time(PopplerDocument *document)
{
g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), nullptr);
std::unique_ptr<GooString> str { document->doc->getDocInfoCreatDate() };
if (!str) {
return nullptr;
}
return _poppler_convert_pdf_date_to_date_time(str.get());
}
/**
* poppler_document_set_creation_date_time:
* @document: A #PopplerDocument
* @creation_datetime: (nullable): A new creation #GDateTime
*
* Sets the document's creation date. If @creation_datetime is %NULL,
* CreationDate entry is removed from the document's Info dictionary.
*
* Since: 20.09.0
**/
void poppler_document_set_creation_date_time(PopplerDocument *document, GDateTime *creation_datetime)
{
g_return_if_fail(POPPLER_IS_DOCUMENT(document));
GooString *str = nullptr;
if (creation_datetime) {
str = _poppler_convert_date_time_to_pdf_date(creation_datetime);
}
document->doc->setDocInfoCreatDate(str);
}
/**
* poppler_document_get_modification_date:
* @document: A #PopplerDocument
*
* Returns the date the document was most recently modified as seconds since the Epoch
*
* Return value: the date the document was most recently modified, or -1
*
* Since: 0.16
**/
time_t poppler_document_get_modification_date(PopplerDocument *document)
{
g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), (time_t)-1);
const std::unique_ptr<GooString> str = document->doc->getDocInfoModDate();
if (!str) {
return (time_t)-1;
}
time_t date;
gboolean success = _poppler_convert_pdf_date_to_gtime(str.get(), &date);
return (success) ? date : (time_t)-1;
}
/**
* poppler_document_set_modification_date:
* @document: A #PopplerDocument
* @modification_date: A new modification date
*
* Sets the document's modification date. If @modification_date is -1, ModDate
* entry is removed from the document's Info dictionary.
*
* Since: 0.46
**/
void poppler_document_set_modification_date(PopplerDocument *document, time_t modification_date)
{
g_return_if_fail(POPPLER_IS_DOCUMENT(document));
GooString *str = modification_date == (time_t)-1 ? nullptr : timeToDateString(&modification_date);
document->doc->setDocInfoModDate(str);
}
/**
* poppler_document_get_modification_date_time:
* @document: A #PopplerDocument
*
* Returns the date the document was most recently modified as a #GDateTime
*
* Returns: (nullable): the date the document was modified, or %NULL
*
* Since: 20.09.0
**/
GDateTime *poppler_document_get_modification_date_time(PopplerDocument *document)
{
g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), nullptr);
std::unique_ptr<GooString> str { document->doc->getDocInfoModDate() };
if (!str) {
return nullptr;
}
return _poppler_convert_pdf_date_to_date_time(str.get());
}
/**
* poppler_document_set_modification_date_time:
* @document: A #PopplerDocument
* @modification_datetime: (nullable): A new modification #GDateTime
*
* Sets the document's modification date. If @modification_datetime is %NULL,
* ModDate entry is removed from the document's Info dictionary.
*
* Since: 20.09.0
**/
void poppler_document_set_modification_date_time(PopplerDocument *document, GDateTime *modification_datetime)
{
g_return_if_fail(POPPLER_IS_DOCUMENT(document));
GooString *str = nullptr;
if (modification_datetime) {
str = _poppler_convert_date_time_to_pdf_date(modification_datetime);
}
document->doc->setDocInfoModDate(str);
}
/**
* poppler_document_is_linearized:
* @document: A #PopplerDocument
*
* Returns whether @document is linearized or not. Linearization of PDF
* enables efficient incremental access of the PDF file in a network environment.
*
* Return value: %TRUE if @document is linearized, %FALSE otherwise
*
* Since: 0.16
**/
gboolean poppler_document_is_linearized(PopplerDocument *document)
{
g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), FALSE);
return document->doc->isLinearized();
}
/**
* poppler_document_get_n_signatures:
* @document: A #PopplerDocument
*
* Returns how many digital signatures @document contains.
* PDF digital signatures ensure that the content hash not been altered since last edit and
* that it was produced by someone the user can trust
*
* Return value: The number of signatures found in the document
*
* Since: 21.12.0
**/
gint poppler_document_get_n_signatures(const PopplerDocument *document)
{
g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), 0);
return document->doc->getSignatureFields().size();
}
/**
* poppler_document_get_signature_fields:
* @document: A #PopplerDocument
*
* Returns a #GList containing all signature #PopplerFormField<!-- -->s in the document.
*
* Return value: (element-type PopplerFormField) (transfer full): a list of all signature form fields.
*
* Since: 22.02.0
**/
GList *poppler_document_get_signature_fields(PopplerDocument *document)
{
std::vector<FormFieldSignature *> signature_fields;
FormWidget *widget;
GList *result = nullptr;
gsize i;
signature_fields = document->doc->getSignatureFields();
for (i = 0; i < signature_fields.size(); i++) {
widget = signature_fields[i]->getCreateWidget();
if (widget != nullptr) {
result = g_list_prepend(result, _poppler_form_field_new(document, widget));
}
}
return g_list_reverse(result);
}
/**
* poppler_document_get_page_layout:
* @document: A #PopplerDocument
*
* Returns the page layout that should be used when the document is opened
*
* Return value: a #PopplerPageLayout that should be used when the document is opened
*
* Since: 0.16
**/
PopplerPageLayout poppler_document_get_page_layout(PopplerDocument *document)
{
Catalog *catalog;
g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), POPPLER_PAGE_LAYOUT_UNSET);
catalog = document->doc->getCatalog();
if (catalog && catalog->isOk()) {
return convert_page_layout(catalog->getPageLayout());
}
return POPPLER_PAGE_LAYOUT_UNSET;
}
/**
* poppler_document_get_page_mode:
* @document: A #PopplerDocument
*
* Returns a #PopplerPageMode representing how the document should
* be initially displayed when opened.
*
* Return value: a #PopplerPageMode that should be used when document is opened
*
* Since: 0.16
**/
PopplerPageMode poppler_document_get_page_mode(PopplerDocument *document)
{
Catalog *catalog;
g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), POPPLER_PAGE_MODE_UNSET);
catalog = document->doc->getCatalog();
if (catalog && catalog->isOk()) {
return convert_page_mode(catalog->getPageMode());
}
return POPPLER_PAGE_MODE_UNSET;
}
/**
* poppler_document_get_print_scaling:
* @document: A #PopplerDocument
*
* Returns the print scaling value suggested by author of the document.
*
* Return value: a #PopplerPrintScaling that should be used when document is printed
*
* Since: 0.73
**/
PopplerPrintScaling poppler_document_get_print_scaling(PopplerDocument *document)
{
Catalog *catalog;
ViewerPreferences *preferences;
PopplerPrintScaling print_scaling = POPPLER_PRINT_SCALING_APP_DEFAULT;
g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), POPPLER_PRINT_SCALING_APP_DEFAULT);
catalog = document->doc->getCatalog();
if (catalog && catalog->isOk()) {
preferences = catalog->getViewerPreferences();
if (preferences) {
switch (preferences->getPrintScaling()) {
default:
case ViewerPreferences::PrintScaling::printScalingAppDefault:
print_scaling = POPPLER_PRINT_SCALING_APP_DEFAULT;
break;
case ViewerPreferences::PrintScaling::printScalingNone:
print_scaling = POPPLER_PRINT_SCALING_NONE;
break;
}
}
}
return print_scaling;
}
/**
* poppler_document_get_print_duplex:
* @document: A #PopplerDocument
*
* Returns the duplex mode value suggested for printing by author of the document.
* Value POPPLER_PRINT_DUPLEX_NONE means that the document does not specify this
* preference.
*
* Returns: a #PopplerPrintDuplex that should be used when document is printed
*
* Since: 0.80
**/
PopplerPrintDuplex poppler_document_get_print_duplex(PopplerDocument *document)
{
Catalog *catalog;
ViewerPreferences *preferences;
PopplerPrintDuplex duplex = POPPLER_PRINT_DUPLEX_NONE;
g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), POPPLER_PRINT_DUPLEX_NONE);
catalog = document->doc->getCatalog();
if (catalog && catalog->isOk()) {
preferences = catalog->getViewerPreferences();
if (preferences) {
switch (preferences->getDuplex()) {
default:
case ViewerPreferences::Duplex::duplexNone:
duplex = POPPLER_PRINT_DUPLEX_NONE;
break;
case ViewerPreferences::Duplex::duplexSimplex:
duplex = POPPLER_PRINT_DUPLEX_SIMPLEX;
break;
case ViewerPreferences::Duplex::duplexDuplexFlipShortEdge:
duplex = POPPLER_PRINT_DUPLEX_DUPLEX_FLIP_SHORT_EDGE;
break;
case ViewerPreferences::Duplex::duplexDuplexFlipLongEdge:
duplex = POPPLER_PRINT_DUPLEX_DUPLEX_FLIP_LONG_EDGE;
break;
}
}
}
return duplex;
}
/**
* poppler_document_get_print_n_copies:
* @document: A #PopplerDocument
*
* Returns the suggested number of copies to be printed.
* This preference should be applied only if returned value
* is greater than 1 since value 1 usually means that
* the document does not specify it.
*
* Returns: Number of copies
*
* Since: 0.80
**/
gint poppler_document_get_print_n_copies(PopplerDocument *document)
{
Catalog *catalog;
ViewerPreferences *preferences;
gint retval = 1;
g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), 1);
catalog = document->doc->getCatalog();
if (catalog && catalog->isOk()) {
preferences = catalog->getViewerPreferences();
if (preferences) {
retval = preferences->getNumCopies();
}
}
return retval;
}
/**
* poppler_document_get_print_page_ranges:
* @document: A #PopplerDocument
* @n_ranges: (out): return location for number of ranges
*
* Returns the suggested page ranges to print in the form of array
* of #PopplerPageRange<!-- -->s and number of ranges.
* %NULL pointer means that the document does not specify page ranges
* for printing.
*
* Returns: (array length=n_ranges) (transfer full): an array
* of #PopplerPageRange<!-- -->s or %NULL. Free the array when
* it is no longer needed.
*
* Since: 0.80
**/
PopplerPageRange *poppler_document_get_print_page_ranges(PopplerDocument *document, int *n_ranges)
{
Catalog *catalog;
ViewerPreferences *preferences;
std::vector<std::pair<int, int>> ranges;
PopplerPageRange *result = nullptr;
g_return_val_if_fail(n_ranges != nullptr, nullptr);
*n_ranges = 0;
g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), nullptr);
catalog = document->doc->getCatalog();
if (catalog && catalog->isOk()) {
preferences = catalog->getViewerPreferences();
if (preferences) {
ranges = preferences->getPrintPageRange();
*n_ranges = ranges.size();
result = g_new(PopplerPageRange, ranges.size());
for (size_t i = 0; i < ranges.size(); ++i) {
result[i].start_page = ranges[i].first;
result[i].end_page = ranges[i].second;
}
}
}
return result;
}
/**
* poppler_document_get_permissions:
* @document: A #PopplerDocument
*
* Returns the flags specifying which operations are permitted when the document is opened.
*
* Return value: a set of flags from #PopplerPermissions enumeration
*
* Since: 0.16
**/
PopplerPermissions poppler_document_get_permissions(PopplerDocument *document)
{
guint flag = 0;
g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), POPPLER_PERMISSIONS_FULL);
if (document->doc->okToPrint()) {
flag |= POPPLER_PERMISSIONS_OK_TO_PRINT;
}
if (document->doc->okToChange()) {
flag |= POPPLER_PERMISSIONS_OK_TO_MODIFY;
}
if (document->doc->okToCopy()) {
flag |= POPPLER_PERMISSIONS_OK_TO_COPY;
}
if (document->doc->okToAddNotes()) {
flag |= POPPLER_PERMISSIONS_OK_TO_ADD_NOTES;
}
if (document->doc->okToFillForm()) {
flag |= POPPLER_PERMISSIONS_OK_TO_FILL_FORM;
}
if (document->doc->okToAccessibility()) {
flag |= POPPLER_PERMISSIONS_OK_TO_EXTRACT_CONTENTS;
}
if (document->doc->okToAssemble()) {
flag |= POPPLER_PERMISSIONS_OK_TO_ASSEMBLE;
}
if (document->doc->okToPrintHighRes()) {
flag |= POPPLER_PERMISSIONS_OK_TO_PRINT_HIGH_RESOLUTION;
}
return (PopplerPermissions)flag;
}
/**
* poppler_document_get_pdf_subtype_string:
* @document: A #PopplerDocument
*
* Returns the PDF subtype version of @document as a string.
*
* Returns: (transfer full) (nullable): a newly allocated string containing
* the PDF subtype version of @document, or %NULL
*
* Since: 0.70
**/
gchar *poppler_document_get_pdf_subtype_string(PopplerDocument *document)
{
g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), NULL);
std::unique_ptr<GooString> infostring;
switch (document->doc->getPDFSubtype()) {
case subtypePDFA:
infostring = document->doc->getDocInfoStringEntry("GTS_PDFA1Version");
break;
case subtypePDFE:
infostring = document->doc->getDocInfoStringEntry("GTS_PDFEVersion");
break;
case subtypePDFUA:
infostring = document->doc->getDocInfoStringEntry("GTS_PDFUAVersion");
break;
case subtypePDFVT:
infostring = document->doc->getDocInfoStringEntry("GTS_PDFVTVersion");
break;
case subtypePDFX:
infostring = document->doc->getDocInfoStringEntry("GTS_PDFXVersion");
break;
case subtypeNone:
case subtypeNull:
default: {
/* nothing */
}
}
return _poppler_goo_string_to_utf8(infostring.get());
}
/**
* poppler_document_get_pdf_subtype:
* @document: A #PopplerDocument
*
* Returns the subtype of @document as a #PopplerPDFSubtype.
*
* Returns: the document's subtype
*
* Since: 0.70
**/
PopplerPDFSubtype poppler_document_get_pdf_subtype(PopplerDocument *document)
{
g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), POPPLER_PDF_SUBTYPE_NONE);
return convert_pdf_subtype(document->doc->getPDFSubtype());
}
/**
* poppler_document_get_pdf_part:
* @document: A #PopplerDocument
*
* Returns the part of the conforming standard that the @document adheres to
* as a #PopplerPDFSubtype.
*
* Returns: the document's subtype part
*
* Since: 0.70
**/
PopplerPDFPart poppler_document_get_pdf_part(PopplerDocument *document)
{
g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), POPPLER_PDF_SUBTYPE_PART_NONE);
return convert_pdf_subtype_part(document->doc->getPDFSubtypePart());
}
/**
* poppler_document_get_pdf_conformance:
* @document: A #PopplerDocument
*
* Returns the conformance level of the @document as #PopplerPDFConformance.
*
* Returns: the document's subtype conformance level
*
* Since: 0.70
**/
PopplerPDFConformance poppler_document_get_pdf_conformance(PopplerDocument *document)
{
g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), POPPLER_PDF_SUBTYPE_CONF_NONE);
return convert_pdf_subtype_conformance(document->doc->getPDFSubtypeConformance());
}
/**
* poppler_document_get_metadata:
* @document: A #PopplerDocument
*
* Returns the XML metadata string of the document
*
* Return value: a new allocated string containing the XML
* metadata, or %NULL
*
* Since: 0.16
**/
gchar *poppler_document_get_metadata(PopplerDocument *document)
{
Catalog *catalog;
gchar *retval = nullptr;
g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), NULL);
catalog = document->doc->getCatalog();
if (catalog && catalog->isOk()) {
std::unique_ptr<GooString> s = catalog->readMetadata();
if (s) {
retval = g_strdup(s->c_str());
}
}
return retval;
}
/**
* poppler_document_reset_form:
* @document: A #PopplerDocument
* @fields: (element-type utf8) (nullable): list of fields to reset
* @exclude_fields: whether to reset all fields except those in @fields
*
* Resets the form fields specified by fields if exclude_fields is FALSE.
* Resets all others if exclude_fields is TRUE.
* All form fields are reset regardless of the exclude_fields flag
* if fields is empty.
*
* Since: 0.90
**/
void poppler_document_reset_form(PopplerDocument *document, GList *fields, gboolean exclude_fields)
{
std::vector<std::string> list;
Catalog *catalog;
GList *iter;
Form *form;
g_return_if_fail(POPPLER_IS_DOCUMENT(document));
catalog = document->doc->getCatalog();
if (catalog && catalog->isOk()) {
form = catalog->getForm();
if (form) {
for (iter = fields; iter != nullptr; iter = iter->next) {
list.emplace_back((char *)iter->data);
}
form->reset(list, exclude_fields);
}
}
}
/**
* poppler_document_has_javascript:
* @document: A #PopplerDocument
*
* Returns whether @document has any javascript in it.
*
* Since: 0.90
**/
gboolean poppler_document_has_javascript(PopplerDocument *document)
{
g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), FALSE);
return document->doc->hasJavascript();
}
static void poppler_document_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
{
PopplerDocument *document = POPPLER_DOCUMENT(object);
guint version;
switch (prop_id) {
case PROP_TITLE:
g_value_take_string(value, poppler_document_get_title(document));
break;
case PROP_FORMAT:
g_value_take_string(value, poppler_document_get_pdf_version_string(document));
break;
case PROP_FORMAT_MAJOR:
poppler_document_get_pdf_version(document, &version, nullptr);
g_value_set_uint(value, version);
break;
case PROP_FORMAT_MINOR:
poppler_document_get_pdf_version(document, nullptr, &version);
g_value_set_uint(value, version);
break;
case PROP_AUTHOR:
g_value_take_string(value, poppler_document_get_author(document));
break;
case PROP_SUBJECT:
g_value_take_string(value, poppler_document_get_subject(document));
break;
case PROP_KEYWORDS:
g_value_take_string(value, poppler_document_get_keywords(document));
break;
case PROP_CREATOR:
g_value_take_string(value, poppler_document_get_creator(document));
break;
case PROP_PRODUCER:
g_value_take_string(value, poppler_document_get_producer(document));
break;
case PROP_CREATION_DATE:
g_value_set_int(value, poppler_document_get_creation_date(document));
break;
case PROP_CREATION_DATETIME:
g_value_take_boxed(value, poppler_document_get_creation_date_time(document));
break;
case PROP_MOD_DATE:
g_value_set_int(value, poppler_document_get_modification_date(document));
break;
case PROP_MOD_DATETIME:
g_value_take_boxed(value, poppler_document_get_modification_date_time(document));
break;
case PROP_LINEARIZED:
g_value_set_boolean(value, poppler_document_is_linearized(document));
break;
case PROP_PAGE_LAYOUT:
g_value_set_enum(value, poppler_document_get_page_layout(document));
break;
case PROP_PAGE_MODE:
g_value_set_enum(value, poppler_document_get_page_mode(document));
break;
case PROP_VIEWER_PREFERENCES:
/* FIXME: write... */
g_value_set_flags(value, POPPLER_VIEWER_PREFERENCES_UNSET);
break;
case PROP_PRINT_SCALING:
g_value_set_enum(value, poppler_document_get_print_scaling(document));
break;
case PROP_PRINT_DUPLEX:
g_value_set_enum(value, poppler_document_get_print_duplex(document));
break;
case PROP_PRINT_N_COPIES:
g_value_set_int(value, poppler_document_get_print_n_copies(document));
break;
case PROP_PERMISSIONS:
g_value_set_flags(value, poppler_document_get_permissions(document));
break;
case PROP_SUBTYPE:
g_value_set_enum(value, poppler_document_get_pdf_subtype(document));
break;
case PROP_SUBTYPE_STRING:
g_value_take_string(value, poppler_document_get_pdf_subtype_string(document));
break;
case PROP_SUBTYPE_PART:
g_value_set_enum(value, poppler_document_get_pdf_part(document));
break;
case PROP_SUBTYPE_CONF:
g_value_set_enum(value, poppler_document_get_pdf_conformance(document));
break;
case PROP_METADATA:
g_value_take_string(value, poppler_document_get_metadata(document));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
}
}
static void poppler_document_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
{
PopplerDocument *document = POPPLER_DOCUMENT(object);
switch (prop_id) {
case PROP_TITLE:
poppler_document_set_title(document, g_value_get_string(value));
break;
case PROP_AUTHOR:
poppler_document_set_author(document, g_value_get_string(value));
break;
case PROP_SUBJECT:
poppler_document_set_subject(document, g_value_get_string(value));
break;
case PROP_KEYWORDS:
poppler_document_set_keywords(document, g_value_get_string(value));
break;
case PROP_CREATOR:
poppler_document_set_creator(document, g_value_get_string(value));
break;
case PROP_PRODUCER:
poppler_document_set_producer(document, g_value_get_string(value));
break;
case PROP_CREATION_DATE:
poppler_document_set_creation_date(document, g_value_get_int(value));
break;
case PROP_CREATION_DATETIME:
poppler_document_set_creation_date_time(document, (GDateTime *)g_value_get_boxed(value));
break;
case PROP_MOD_DATE:
poppler_document_set_modification_date(document, g_value_get_int(value));
break;
case PROP_MOD_DATETIME:
poppler_document_set_modification_date_time(document, (GDateTime *)g_value_get_boxed(value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
}
}
static void poppler_document_class_init(PopplerDocumentClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
gobject_class->finalize = poppler_document_finalize;
gobject_class->get_property = poppler_document_get_property;
gobject_class->set_property = poppler_document_set_property;
/**
* PopplerDocument:title:
*
* The document's title or %NULL
*/
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));
/**
* PopplerDocument:format:
*
* The PDF version as string. See also poppler_document_get_pdf_version_string()
*/
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));
/**
* PopplerDocument:format-major:
*
* The PDF major version number. See also poppler_document_get_pdf_version()
*/
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));
/**
* PopplerDocument:format-minor:
*
* The PDF minor version number. See also poppler_document_get_pdf_version()
*/
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));
/**
* PopplerDocument:author:
*
* The author of the document
*/
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));
/**
* PopplerDocument:subject:
*
* The subject of the document
*/
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));
/**
* PopplerDocument:keywords:
*
* The keywords associated to the document
*/
g_object_class_install_property(G_OBJECT_CLASS(klass), PROP_KEYWORDS, g_param_spec_string("keywords", "Keywords", "Keywords", nullptr, G_PARAM_READWRITE));
/**
* PopplerDocument:creator:
*
* The creator of the document. See also poppler_document_get_creator()
*/
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));
/**
* PopplerDocument:producer:
*
* The producer of the document. See also poppler_document_get_producer()
*/
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));
/**
* PopplerDocument:creation-date:
*
* The date the document was created as seconds since the Epoch, or -1
*
* Deprecated: 20.09.0: This will overflow in 2038. Use creation-datetime
* instead.
*/
g_object_class_install_property(G_OBJECT_CLASS(klass), PROP_CREATION_DATE,
g_param_spec_int("creation-date", "Creation Date", "The date and time the document was created", -1, G_MAXINT, -1, (GParamFlags)(G_PARAM_READWRITE | G_PARAM_DEPRECATED)));
/**
* PopplerDocument:creation-datetime:
* The #GDateTime the document was created.
*
* Since: 20.09.0
*/
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));
/**
* PopplerDocument:mod-date:
*
* The date the document was most recently modified as seconds since the Epoch, or -1
*
* Deprecated: 20.09.0: This will overflow in 2038. Use mod-datetime instead.
*/
g_object_class_install_property(G_OBJECT_CLASS(klass), PROP_MOD_DATE,
g_param_spec_int("mod-date", "Modification Date", "The date and time the document was modified", -1, G_MAXINT, -1, (GParamFlags)(G_PARAM_READWRITE | G_PARAM_DEPRECATED)));
/**
* PopplerDocument:mod-datetime:
*
* The #GDateTime the document was most recently modified.
*
* Since: 20.09.0
*/
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));
/**
* PopplerDocument:linearized:
*
* Whether document is linearized. See also poppler_document_is_linearized()
*/
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));
/**
* PopplerDocument:page-layout:
*
* The page layout that should be used when the document is opened
*/
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));
/**
* PopplerDocument:page-mode:
*
* The mode that should be used when the document is opened
*/
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));
/**
* PopplerDocument:viewer-preferences:
*/
g_object_class_install_property(G_OBJECT_CLASS(klass), PROP_VIEWER_PREFERENCES,
g_param_spec_flags("viewer-preferences", "Viewer Preferences", "Viewer Preferences", POPPLER_TYPE_VIEWER_PREFERENCES, POPPLER_VIEWER_PREFERENCES_UNSET, G_PARAM_READABLE));
/**
* PopplerDocument:print-scaling:
*
* Since: 0.73
*/
g_object_class_install_property(
G_OBJECT_CLASS(klass), PROP_PRINT_SCALING,
g_param_spec_enum("print-scaling", "Print Scaling", "Print Scaling Viewer Preference", POPPLER_TYPE_PRINT_SCALING, POPPLER_PRINT_SCALING_APP_DEFAULT, (GParamFlags)(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
/**
* PopplerDocument:print-duplex:
*
* Since: 0.80
*/
g_object_class_install_property(G_OBJECT_CLASS(klass), PROP_PRINT_DUPLEX,
g_param_spec_enum("print-duplex", "Print Duplex", "Duplex Viewer Preference", POPPLER_TYPE_PRINT_DUPLEX, POPPLER_PRINT_DUPLEX_NONE, (GParamFlags)(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
/**
* PopplerDocument:print-n-copies:
*
* Suggested number of copies to be printed for this document
*
* Since: 0.80
*/
g_object_class_install_property(G_OBJECT_CLASS(klass), PROP_PRINT_N_COPIES,
g_param_spec_int("print-n-copies", "Number of Copies to Print", "Number of Copies Viewer Preference", 1, G_MAXINT, 1, (GParamFlags)(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
/**
* PopplerDocument:permissions:
*
* Flags specifying which operations are permitted when the document is opened
*/
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));
/**
* PopplerDocument:subtype:
*
* Document PDF subtype type
*/
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));
/**
* PopplerDocument:subtype-string:
*
* Document PDF subtype. See also poppler_document_get_pdf_subtype_string()
*/
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));
/**
* PopplerDocument:subtype-part:
*
* Document PDF subtype part
*/
g_object_class_install_property(G_OBJECT_CLASS(klass), PROP_SUBTYPE_PART,
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));
/**
* PopplerDocument:subtype-conformance:
*
* Document PDF subtype conformance
*/
g_object_class_install_property(G_OBJECT_CLASS(klass), PROP_SUBTYPE_CONF,
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));
/**
* PopplerDocument:metadata:
*
* Document metadata in XML format, or %NULL
*/
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));
}
static void poppler_document_init(PopplerDocument *document) { }
/* PopplerIndexIter: For determining the index of a tree */
struct _PopplerIndexIter
{
PopplerDocument *document;
const std::vector<OutlineItem *> *items;
int index;
};
G_DEFINE_BOXED_TYPE(PopplerIndexIter, poppler_index_iter, poppler_index_iter_copy, poppler_index_iter_free)
/**
* poppler_index_iter_copy:
* @iter: a #PopplerIndexIter
*
* Creates a new #PopplerIndexIter as a copy of @iter. This must be freed with
* poppler_index_iter_free().
*
* Return value: a new #PopplerIndexIter
**/
PopplerIndexIter *poppler_index_iter_copy(PopplerIndexIter *iter)
{
PopplerIndexIter *new_iter;
g_return_val_if_fail(iter != nullptr, NULL);
new_iter = g_slice_dup(PopplerIndexIter, iter);
new_iter->document = (PopplerDocument *)g_object_ref(new_iter->document);
return new_iter;
}
/**
* poppler_index_iter_new:
* @document: a #PopplerDocument
*
* Returns the root #PopplerIndexIter for @document, or %NULL. This must be
* freed with poppler_index_iter_free().
*
* Certain documents have an index associated with them. This index can be used
* to help the user navigate the document, and is similar to a table of
* contents. Each node in the index will contain a #PopplerAction that can be
* displayed to the user &mdash; typically a #POPPLER_ACTION_GOTO_DEST or a
* #POPPLER_ACTION_URI<!-- -->.
*
* Here is a simple example of some code that walks the full index:
*
* <informalexample><programlisting>
* static void
* walk_index (PopplerIndexIter *iter)
* {
* do
* {
* /<!-- -->* Get the action and do something with it *<!-- -->/
* PopplerIndexIter *child = poppler_index_iter_get_child (iter);
* if (child)
* walk_index (child);
* poppler_index_iter_free (child);
* }
* while (poppler_index_iter_next (iter));
* }
* ...
* {
* iter = poppler_index_iter_new (document);
* walk_index (iter);
* poppler_index_iter_free (iter);
* }
*</programlisting></informalexample>
*
* Return value: a new #PopplerIndexIter
**/
PopplerIndexIter *poppler_index_iter_new(PopplerDocument *document)
{
PopplerIndexIter *iter;
Outline *outline;
const std::vector<OutlineItem *> *items;
outline = document->doc->getOutline();
if (outline == nullptr) {
return nullptr;
}
items = outline->getItems();
if (items == nullptr) {
return nullptr;
}
iter = g_slice_new(PopplerIndexIter);
iter->document = (PopplerDocument *)g_object_ref(document);
iter->items = items;
iter->index = 0;
return iter;
}
/**
* poppler_index_iter_get_child:
* @parent: a #PopplerIndexIter
*
* Returns a newly created child of @parent, or %NULL if the iter has no child.
* See poppler_index_iter_new() for more information on this function.
*
* Return value: a new #PopplerIndexIter
**/
PopplerIndexIter *poppler_index_iter_get_child(PopplerIndexIter *parent)
{
PopplerIndexIter *child;
OutlineItem *item;
g_return_val_if_fail(parent != nullptr, NULL);
item = (*parent->items)[parent->index];
item->open();
if (!(item->hasKids() && item->getKids())) {
return nullptr;
}
child = g_slice_new0(PopplerIndexIter);
child->document = (PopplerDocument *)g_object_ref(parent->document);
child->items = item->getKids();
g_assert(child->items);
return child;
}
static gchar *unicode_to_char(const Unicode *unicode, int len)
{
const UnicodeMap *uMap = globalParams->getUtf8Map();
GooString gstr;
gchar buf[8]; /* 8 is enough for mapping an unicode char to a string */
int i, n;
for (i = 0; i < len; ++i) {
n = uMap->mapUnicode(unicode[i], buf, sizeof(buf));
gstr.append(buf, n);
}
return g_strdup(gstr.c_str());
}
/**
* poppler_index_iter_is_open:
* @iter: a #PopplerIndexIter
*
* Returns whether this node should be expanded by default to the user. The
* document can provide a hint as to how the document's index should be expanded
* initially.
*
* Return value: %TRUE, if the document wants @iter to be expanded
**/
gboolean poppler_index_iter_is_open(PopplerIndexIter *iter)
{
OutlineItem *item;
item = (*iter->items)[iter->index];
return item->isOpen();
}
/**
* poppler_index_iter_get_action:
* @iter: a #PopplerIndexIter
*
* Returns the #PopplerAction associated with @iter. It must be freed with
* poppler_action_free().
*
* Return value: a new #PopplerAction
**/
PopplerAction *poppler_index_iter_get_action(PopplerIndexIter *iter)
{
OutlineItem *item;
const LinkAction *link_action;
PopplerAction *action;
gchar *title;
g_return_val_if_fail(iter != nullptr, NULL);
item = (*iter->items)[iter->index];
link_action = item->getAction();
const std::vector<Unicode> &itemTitle = item->getTitle();
title = unicode_to_char(itemTitle.data(), itemTitle.size());
action = _poppler_action_new(iter->document, link_action, title);
g_free(title);
return action;
}
/**
* poppler_index_iter_next:
* @iter: a #PopplerIndexIter
*
* Sets @iter to point to the next action at the current level, if valid. See
* poppler_index_iter_new() for more information.
*
* Return value: %TRUE, if @iter was set to the next action
**/
gboolean poppler_index_iter_next(PopplerIndexIter *iter)
{
g_return_val_if_fail(iter != nullptr, FALSE);
iter->index++;
if (iter->index >= (int)iter->items->size()) {
return FALSE;
}
return TRUE;
}
/**
* poppler_index_iter_free:
* @iter: a #PopplerIndexIter
*
* Frees @iter.
**/
void poppler_index_iter_free(PopplerIndexIter *iter)
{
if (G_UNLIKELY(iter == nullptr)) {
return;
}
g_object_unref(iter->document);
g_slice_free(PopplerIndexIter, iter);
}
struct _PopplerFontsIter
{
std::vector<FontInfo *> items;
int index;
};
G_DEFINE_BOXED_TYPE(PopplerFontsIter, poppler_fonts_iter, poppler_fonts_iter_copy, poppler_fonts_iter_free)
/**
* poppler_fonts_iter_get_full_name:
* @iter: a #PopplerFontsIter
*
* Returns the full name of the font associated with @iter
*
* Returns: the font full name
*/
const char *poppler_fonts_iter_get_full_name(PopplerFontsIter *iter)
{
FontInfo *info;
info = iter->items[iter->index];
const std::optional<std::string> &name = info->getName();
if (name) {
return name->c_str();
} else {
return nullptr;
}
}
/**
* poppler_fonts_iter_get_name:
* @iter: a #PopplerFontsIter
*
* Returns the name of the font associated with @iter
*
* Returns: the font name
*/
const char *poppler_fonts_iter_get_name(PopplerFontsIter *iter)
{
FontInfo *info;
const char *name;
name = poppler_fonts_iter_get_full_name(iter);
info = iter->items[iter->index];
if (info->getSubset() && name) {
while (*name && *name != '+') {
name++;
}
if (*name) {
name++;
}
}
return name;
}
/**
* poppler_fonts_iter_get_substitute_name:
* @iter: a #PopplerFontsIter
*
* The name of the substitute font of the font associated with @iter or %NULL if
* the font is embedded
*
* Returns: the name of the substitute font or %NULL if font is embedded
*
* Since: 0.20
*/
const char *poppler_fonts_iter_get_substitute_name(PopplerFontsIter *iter)
{
FontInfo *info;
info = iter->items[iter->index];
const std::optional<std::string> &name = info->getSubstituteName();
if (name) {
return name->c_str();
} else {
return nullptr;
}
}
/**
* poppler_fonts_iter_get_file_name:
* @iter: a #PopplerFontsIter
*
* The filename of the font associated with @iter or %NULL if
* the font is embedded
*
* Returns: the filename of the font or %NULL if font is embedded
*/
const char *poppler_fonts_iter_get_file_name(PopplerFontsIter *iter)
{
FontInfo *info;
info = iter->items[iter->index];
const std::optional<std::string> &file = info->getFile();
if (file) {
return file->c_str();
} else {
return nullptr;
}
}
/**
* poppler_fonts_iter_get_font_type:
* @iter: a #PopplerFontsIter
*
* Returns the type of the font associated with @iter
*
* Returns: the font type
*/
PopplerFontType poppler_fonts_iter_get_font_type(PopplerFontsIter *iter)
{
FontInfo *info;
g_return_val_if_fail(iter != nullptr, POPPLER_FONT_TYPE_UNKNOWN);
info = iter->items[iter->index];
return (PopplerFontType)info->getType();
}
/**
* poppler_fonts_iter_get_encoding:
* @iter: a #PopplerFontsIter
*
* Returns the encoding of the font associated with @iter
*
* Returns: the font encoding
*
* Since: 0.20
*/
const char *poppler_fonts_iter_get_encoding(PopplerFontsIter *iter)
{
FontInfo *info;
info = iter->items[iter->index];
const std::string &encoding = info->getEncoding();
if (!encoding.empty()) {
return encoding.c_str();
} else {
return nullptr;
}
}
/**
* poppler_fonts_iter_is_embedded:
* @iter: a #PopplerFontsIter
*
* Returns whether the font associated with @iter is embedded in the document
*
* Returns: %TRUE if font is embedded, %FALSE otherwise
*/
gboolean poppler_fonts_iter_is_embedded(PopplerFontsIter *iter)
{
FontInfo *info;
info = iter->items[iter->index];
return info->getEmbedded();
}
/**
* poppler_fonts_iter_is_subset:
* @iter: a #PopplerFontsIter
*
* Returns whether the font associated with @iter is a subset of another font
*
* Returns: %TRUE if font is a subset, %FALSE otherwise
*/
gboolean poppler_fonts_iter_is_subset(PopplerFontsIter *iter)
{
FontInfo *info;
info = iter->items[iter->index];
return info->getSubset();
}
/**
* poppler_fonts_iter_next:
* @iter: a #PopplerFontsIter
*
* Sets @iter to point to the next font
*
* Returns: %TRUE, if @iter was set to the next font
**/
gboolean poppler_fonts_iter_next(PopplerFontsIter *iter)
{
g_return_val_if_fail(iter != nullptr, FALSE);
iter->index++;
if (iter->index >= (int)iter->items.size()) {
return FALSE;
}
return TRUE;
}
/**
* poppler_fonts_iter_copy:
* @iter: a #PopplerFontsIter to copy
*
* Creates a copy of @iter
*
* Returns: a new allocated copy of @iter
*/
PopplerFontsIter *poppler_fonts_iter_copy(PopplerFontsIter *iter)
{
PopplerFontsIter *new_iter;
g_return_val_if_fail(iter != nullptr, NULL);
new_iter = g_slice_dup(PopplerFontsIter, iter);
new_iter->items.resize(iter->items.size());
for (std::size_t i = 0; i < iter->items.size(); i++) {
FontInfo *info = iter->items[i];
new_iter->items[i] = new FontInfo(*info);
}
return new_iter;
}
/**
* poppler_fonts_iter_free:
* @iter: a #PopplerFontsIter
*
* Frees the given #PopplerFontsIter
*/
void poppler_fonts_iter_free(PopplerFontsIter *iter)
{
if (G_UNLIKELY(iter == nullptr)) {
return;
}
for (auto entry : iter->items) {
delete entry;
}
iter->items.~vector<FontInfo *>();
g_slice_free(PopplerFontsIter, iter);
}
static PopplerFontsIter *poppler_fonts_iter_new(std::vector<FontInfo *> &&items)
{
PopplerFontsIter *iter;
iter = g_slice_new(PopplerFontsIter);
new ((void *)&iter->items) std::vector<FontInfo *>(std::move(items));
iter->index = 0;
return iter;
}
typedef struct _PopplerFontInfoClass PopplerFontInfoClass;
struct _PopplerFontInfoClass
{
GObjectClass parent_class;
};
G_DEFINE_TYPE(PopplerFontInfo, poppler_font_info, G_TYPE_OBJECT)
static void poppler_font_info_finalize(GObject *object);
static void poppler_font_info_class_init(PopplerFontInfoClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
gobject_class->finalize = poppler_font_info_finalize;
}
static void poppler_font_info_init(PopplerFontInfo *font_info)
{
font_info->document = nullptr;
font_info->scanner = nullptr;
}
static void poppler_font_info_finalize(GObject *object)
{
PopplerFontInfo *font_info = POPPLER_FONT_INFO(object);
delete font_info->scanner;
g_object_unref(font_info->document);
G_OBJECT_CLASS(poppler_font_info_parent_class)->finalize(object);
}
/**
* poppler_font_info_new:
* @document: a #PopplerDocument
*
* Creates a new #PopplerFontInfo object
*
* Returns: a new #PopplerFontInfo instance
*/
PopplerFontInfo *poppler_font_info_new(PopplerDocument *document)
{
PopplerFontInfo *font_info;
g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), NULL);
font_info = (PopplerFontInfo *)g_object_new(POPPLER_TYPE_FONT_INFO, nullptr);
font_info->document = (PopplerDocument *)g_object_ref(document);
font_info->scanner = new FontInfoScanner(document->doc);
return font_info;
}
/**
* poppler_font_info_scan:
* @font_info: a #PopplerFontInfo
* @n_pages: number of pages to scan
* @iter: (out): return location for a #PopplerFontsIter
*
* Scans the document associated with @font_info for fonts. At most
* @n_pages will be scanned starting from the current iterator. @iter will
* point to the first font scanned.
*
* Here is a simple example of code to scan fonts in a document
*
* <informalexample><programlisting>
* font_info = poppler_font_info_new (document);
* while (poppler_font_info_scan (font_info, 20, &fonts_iter)) {
* if (!fonts_iter)
* continue; /<!-- -->* No fonts found in these 20 pages *<!-- -->/
* do {
* /<!-- -->* Do something with font iter *<!-- -->/
* g_print ("Font Name: %s\n", poppler_fonts_iter_get_name (fonts_iter));
* } while (poppler_fonts_iter_next (fonts_iter));
* poppler_fonts_iter_free (fonts_iter);
* }
* </programlisting></informalexample>
*
* Returns: %TRUE, if there are more fonts left to scan
*/
gboolean poppler_font_info_scan(PopplerFontInfo *font_info, int n_pages, PopplerFontsIter **iter)
{
g_return_val_if_fail(iter != nullptr, FALSE);
std::vector<FontInfo *> items = font_info->scanner->scan(n_pages);
if (items.empty()) {
*iter = nullptr;
return FALSE;
} else {
*iter = poppler_fonts_iter_new(std::move(items));
}
return TRUE;
}
/* For backward compatibility */
void poppler_font_info_free(PopplerFontInfo *font_info)
{
g_return_if_fail(font_info != nullptr);
g_object_unref(font_info);
}
/* Optional content (layers) */
static Layer *layer_new(OptionalContentGroup *oc)
{
Layer *layer;
layer = g_slice_new0(Layer);
layer->oc = oc;
return layer;
}
static void layer_free(Layer *layer)
{
if (G_UNLIKELY(!layer)) {
return;
}
if (layer->kids) {
g_list_free_full(layer->kids, (GDestroyNotify)layer_free);
}
if (layer->label) {
g_free(layer->label);
}
g_slice_free(Layer, layer);
}
static GList *get_optional_content_rbgroups(OCGs *ocg)
{
Array *rb;
GList *groups = nullptr;
rb = ocg->getRBGroupsArray();
if (rb) {
int i, j;
for (i = 0; i < rb->getLength(); ++i) {
Array *rb_array;
GList *group = nullptr;
Object obj = rb->get(i);
if (!obj.isArray()) {
continue;
}
rb_array = obj.getArray();
for (j = 0; j < rb_array->getLength(); ++j) {
OptionalContentGroup *oc;
const Object &ref = rb_array->getNF(j);
if (!ref.isRef()) {
continue;
}
oc = ocg->findOcgByRef(ref.getRef());
group = g_list_prepend(group, oc);
}
groups = g_list_prepend(groups, group);
}
}
return groups;
}
GList *_poppler_document_get_layer_rbgroup(PopplerDocument *document, Layer *layer)
{
GList *l;
for (l = document->layers_rbgroups; l && l->data; l = g_list_next(l)) {
GList *group = (GList *)l->data;
if (g_list_find(group, layer->oc)) {
return group;
}
}
return nullptr;
}
static GList *get_optional_content_items_sorted(OCGs *ocg, Layer *parent, Array *order)
{
GList *items = nullptr;
Layer *last_item = parent;
int i;
for (i = 0; i < order->getLength(); ++i) {
Object orderItem = order->get(i);
if (orderItem.isDict()) {
const Object &ref = order->getNF(i);
if (ref.isRef()) {
OptionalContentGroup *oc = ocg->findOcgByRef(ref.getRef());
Layer *layer = layer_new(oc);
items = g_list_prepend(items, layer);
last_item = layer;
}
} else if (orderItem.isArray() && orderItem.arrayGetLength() > 0) {
if (!last_item) {
last_item = layer_new(nullptr);
items = g_list_prepend(items, last_item);
}
last_item->kids = get_optional_content_items_sorted(ocg, last_item, orderItem.getArray());
last_item = nullptr;
} else if (orderItem.isString()) {
last_item->label = _poppler_goo_string_to_utf8(orderItem.getString());
}
}
return g_list_reverse(items);
}
static GList *get_optional_content_items(OCGs *ocg)
{
Array *order;
GList *items = nullptr;
order = ocg->getOrderArray();
if (order) {
items = get_optional_content_items_sorted(ocg, nullptr, order);
} else {
const auto &ocgs = ocg->getOCGs();
for (const auto &oc : ocgs) {
Layer *layer = layer_new(oc.second.get());
items = g_list_prepend(items, layer);
}
items = g_list_reverse(items);
}
return items;
}
GList *_poppler_document_get_layers(PopplerDocument *document)
{
if (!document->layers) {
Catalog *catalog = document->doc->getCatalog();
OCGs *ocg = catalog->getOptContentConfig();
if (!ocg) {
return nullptr;
}
document->layers = get_optional_content_items(ocg);
document->layers_rbgroups = get_optional_content_rbgroups(ocg);
}
return document->layers;
}
static void poppler_document_layers_free(PopplerDocument *document)
{
if (G_UNLIKELY(!document->layers)) {
return;
}
g_list_free_full(document->layers, (GDestroyNotify)layer_free);
g_list_free_full(document->layers_rbgroups, (GDestroyNotify)g_list_free);
document->layers = nullptr;
document->layers_rbgroups = nullptr;
}
/* PopplerLayersIter */
struct _PopplerLayersIter
{
PopplerDocument *document;
GList *items;
int index;
};
G_DEFINE_BOXED_TYPE(PopplerLayersIter, poppler_layers_iter, poppler_layers_iter_copy, poppler_layers_iter_free)
/**
* poppler_layers_iter_copy:
* @iter: a #PopplerLayersIter
*
* Creates a new #PopplerLayersIter as a copy of @iter. This must be freed with
* poppler_layers_iter_free().
*
* Return value: a new #PopplerLayersIter
*
* Since 0.12
**/
PopplerLayersIter *poppler_layers_iter_copy(PopplerLayersIter *iter)
{
PopplerLayersIter *new_iter;
g_return_val_if_fail(iter != nullptr, NULL);
new_iter = g_slice_dup(PopplerLayersIter, iter);
new_iter->document = (PopplerDocument *)g_object_ref(new_iter->document);
return new_iter;
}
/**
* poppler_layers_iter_free:
* @iter: a #PopplerLayersIter
*
* Frees @iter.
*
* Since: 0.12
**/
void poppler_layers_iter_free(PopplerLayersIter *iter)
{
if (G_UNLIKELY(iter == nullptr)) {
return;
}
g_object_unref(iter->document);
g_slice_free(PopplerLayersIter, iter);
}
/**
* poppler_layers_iter_new:
* @document: a #PopplerDocument
*
* Since: 0.12
**/
PopplerLayersIter *poppler_layers_iter_new(PopplerDocument *document)
{
PopplerLayersIter *iter;
GList *items;
items = _poppler_document_get_layers(document);
if (!items) {
return nullptr;
}
iter = g_slice_new0(PopplerLayersIter);
iter->document = (PopplerDocument *)g_object_ref(document);
iter->items = items;
return iter;
}
/**
* poppler_layers_iter_get_child:
* @parent: a #PopplerLayersIter
*
* Returns a newly created child of @parent, or %NULL if the iter has no child.
* See poppler_layers_iter_new() for more information on this function.
*
* Return value: a new #PopplerLayersIter, or %NULL
*
* Since: 0.12
**/
PopplerLayersIter *poppler_layers_iter_get_child(PopplerLayersIter *parent)
{
PopplerLayersIter *child;
Layer *layer;
g_return_val_if_fail(parent != nullptr, NULL);
layer = (Layer *)g_list_nth_data(parent->items, parent->index);
if (!layer || !layer->kids) {
return nullptr;
}
child = g_slice_new0(PopplerLayersIter);
child->document = (PopplerDocument *)g_object_ref(parent->document);
child->items = layer->kids;
g_assert(child->items);
return child;
}
/**
* poppler_layers_iter_get_title:
* @iter: a #PopplerLayersIter
*
* Returns the title associated with @iter. It must be freed with
* g_free().
*
* Return value: a new string containing the @iter's title or %NULL if @iter doesn't have a title.
* The returned string should be freed with g_free() when no longer needed.
*
* Since: 0.12
**/
gchar *poppler_layers_iter_get_title(PopplerLayersIter *iter)
{
Layer *layer;
g_return_val_if_fail(iter != nullptr, NULL);
layer = (Layer *)g_list_nth_data(iter->items, iter->index);
return layer->label ? g_strdup(layer->label) : nullptr;
}
/**
* poppler_layers_iter_get_layer:
* @iter: a #PopplerLayersIter
*
* Returns the #PopplerLayer associated with @iter.
*
* Return value: (transfer full): a new #PopplerLayer, or %NULL if
* there isn't any layer associated with @iter
*
* Since: 0.12
**/
PopplerLayer *poppler_layers_iter_get_layer(PopplerLayersIter *iter)
{
Layer *layer;
PopplerLayer *poppler_layer = nullptr;
g_return_val_if_fail(iter != nullptr, NULL);
layer = (Layer *)g_list_nth_data(iter->items, iter->index);
if (layer->oc) {
GList *rb_group = nullptr;
rb_group = _poppler_document_get_layer_rbgroup(iter->document, layer);
poppler_layer = _poppler_layer_new(iter->document, layer, rb_group);
}
return poppler_layer;
}
/**
* poppler_layers_iter_next:
* @iter: a #PopplerLayersIter
*
* Sets @iter to point to the next action at the current level, if valid. See
* poppler_layers_iter_new() for more information.
*
* Return value: %TRUE, if @iter was set to the next action
*
* Since: 0.12
**/
gboolean poppler_layers_iter_next(PopplerLayersIter *iter)
{
g_return_val_if_fail(iter != nullptr, FALSE);
iter->index++;
if (iter->index >= (gint)g_list_length(iter->items)) {
return FALSE;
}
return TRUE;
}
typedef struct _PopplerPSFileClass PopplerPSFileClass;
struct _PopplerPSFileClass
{
GObjectClass parent_class;
};
G_DEFINE_TYPE(PopplerPSFile, poppler_ps_file, G_TYPE_OBJECT)
static void poppler_ps_file_finalize(GObject *object);
static void poppler_ps_file_class_init(PopplerPSFileClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
gobject_class->finalize = poppler_ps_file_finalize;
}
static void poppler_ps_file_init(PopplerPSFile *ps_file)
{
ps_file->out = nullptr;
ps_file->fd = -1;
ps_file->filename = nullptr;
ps_file->paper_width = -1;
ps_file->paper_height = -1;
ps_file->duplex = FALSE;
}
static void poppler_ps_file_finalize(GObject *object)
{
PopplerPSFile *ps_file = POPPLER_PS_FILE(object);
delete ps_file->out;
g_object_unref(ps_file->document);
g_free(ps_file->filename);
#ifndef G_OS_WIN32
if (ps_file->fd != -1) {
close(ps_file->fd);
}
#endif /* !G_OS_WIN32 */
G_OBJECT_CLASS(poppler_ps_file_parent_class)->finalize(object);
}
/**
* poppler_ps_file_new:
* @document: a #PopplerDocument
* @filename: the path of the output filename
* @first_page: the first page to print
* @n_pages: the number of pages to print
*
* Create a new postscript file to render to
*
* Return value: (transfer full): a PopplerPSFile
**/
PopplerPSFile *poppler_ps_file_new(PopplerDocument *document, const char *filename, int first_page, int n_pages)
{
PopplerPSFile *ps_file;
g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), NULL);
g_return_val_if_fail(filename != nullptr, NULL);
g_return_val_if_fail(n_pages > 0, NULL);
ps_file = (PopplerPSFile *)g_object_new(POPPLER_TYPE_PS_FILE, nullptr);
ps_file->document = (PopplerDocument *)g_object_ref(document);
ps_file->filename = g_strdup(filename);
ps_file->first_page = first_page + 1;
ps_file->last_page = first_page + 1 + n_pages - 1;
return ps_file;
}
#ifndef G_OS_WIN32
/**
* poppler_ps_file_new_fd:
* @document: a #PopplerDocument
* @fd: a valid file descriptor open for writing
* @first_page: the first page to print
* @n_pages: the number of pages to print
*
* Create a new postscript file to render to.
* Note that this function takes ownership of @fd; you must not operate on it
* again, nor close it.
*
* Return value: (transfer full): a #PopplerPSFile
*
* Since: 21.12.0
**/
PopplerPSFile *poppler_ps_file_new_fd(PopplerDocument *document, int fd, int first_page, int n_pages)
{
PopplerPSFile *ps_file;
g_return_val_if_fail(POPPLER_IS_DOCUMENT(document), nullptr);
g_return_val_if_fail(fd != -1, nullptr);
g_return_val_if_fail(n_pages > 0, nullptr);
ps_file = (PopplerPSFile *)g_object_new(POPPLER_TYPE_PS_FILE, nullptr);
ps_file->document = (PopplerDocument *)g_object_ref(document);
ps_file->fd = fd;
ps_file->first_page = first_page + 1;
ps_file->last_page = first_page + 1 + n_pages - 1;
return ps_file;
}
#endif /* !G_OS_WIN32 */
/**
* poppler_ps_file_set_paper_size:
* @ps_file: a PopplerPSFile which was not yet printed to.
* @width: the paper width in 1/72 inch
* @height: the paper height in 1/72 inch
*
* Set the output paper size. These values will end up in the
* DocumentMedia, the BoundingBox DSC comments and other places in the
* generated PostScript.
*
**/
void poppler_ps_file_set_paper_size(PopplerPSFile *ps_file, double width, double height)
{
g_return_if_fail(ps_file->out == nullptr);
ps_file->paper_width = width;
ps_file->paper_height = height;
}
/**
* poppler_ps_file_set_duplex:
* @ps_file: a PopplerPSFile which was not yet printed to
* @duplex: whether to force duplex printing (on printers which support this)
*
* Enable or disable Duplex printing.
*
**/
void poppler_ps_file_set_duplex(PopplerPSFile *ps_file, gboolean duplex)
{
g_return_if_fail(ps_file->out == nullptr);
ps_file->duplex = duplex;
}
/**
* poppler_ps_file_free:
* @ps_file: a PopplerPSFile
*
* Frees @ps_file
*
**/
void poppler_ps_file_free(PopplerPSFile *ps_file)
{
g_return_if_fail(ps_file != nullptr);
g_object_unref(ps_file);
}
/**
* poppler_document_get_form_field:
* @document: a #PopplerDocument
* @id: an id of a #PopplerFormField
*
* Returns the #PopplerFormField for the given @id. It must be freed with
* g_object_unref()
*
* Return value: (transfer full): a new #PopplerFormField or %NULL if
* not found
**/
PopplerFormField *poppler_document_get_form_field(PopplerDocument *document, gint id)
{
Page *page;
unsigned pageNum;
unsigned fieldNum;
FormWidget *field;
FormWidget::decodeID(id, &pageNum, &fieldNum);
page = document->doc->getPage(pageNum);
if (!page) {
return nullptr;
}
const std::unique_ptr<FormPageWidgets> widgets = page->getFormWidgets();
if (!widgets) {
return nullptr;
}
field = widgets->getWidget(fieldNum);
if (field) {
return _poppler_form_field_new(document, field);
}
return nullptr;
}
gboolean _poppler_convert_pdf_date_to_gtime(const GooString *date, time_t *gdate)
{
gchar *date_string;
gboolean retval;
if (hasUnicodeByteOrderMark(date->toStr())) {
date_string = g_convert(date->c_str() + 2, date->getLength() - 2, "UTF-8", "UTF-16BE", nullptr, nullptr, nullptr);
} else {
date_string = g_strndup(date->c_str(), date->getLength());
}
retval = poppler_date_parse(date_string, gdate);
g_free(date_string);
return retval;
}
/**
* _poppler_convert_pdf_date_to_date_time:
* @date: a PDF date
*
* Converts the PDF date in @date to a #GDateTime.
*
* Returns: The converted date, or %NULL on error.
**/
GDateTime *_poppler_convert_pdf_date_to_date_time(const GooString *date)
{
GDateTime *date_time = nullptr;
GTimeZone *time_zone = nullptr;
int year, mon, day, hour, min, sec, tzHours, tzMins;
char tz;
if (parseDateString(date, &year, &mon, &day, &hour, &min, &sec, &tz, &tzHours, &tzMins)) {
if (tz == '+' || tz == '-') {
gchar *identifier;
identifier = g_strdup_printf("%c%02u:%02u", tz, tzHours, tzMins);
#if GLIB_CHECK_VERSION(2, 68, 0)
time_zone = g_time_zone_new_identifier(identifier);
if (!time_zone) {
g_debug("Failed to create time zone for identifier \"%s\"", identifier);
time_zone = g_time_zone_new_utc();
}
#else
time_zone = g_time_zone_new(identifier);
#endif
g_free(identifier);
} else if (tz == '\0' || tz == 'Z') {
time_zone = g_time_zone_new_utc();
} else {
g_warning("unexpected tz val '%c'", tz);
time_zone = g_time_zone_new_utc();
}
date_time = g_date_time_new(time_zone, year, mon, day, hour, min, sec);
g_time_zone_unref(time_zone);
}
return date_time;
}
/**
* _poppler_convert_date_time_to_pdf_date:
* @datetime: a #GDateTime
*
* Converts a #GDateTime to a PDF date.
*
* Returns: The converted date
**/
GooString *_poppler_convert_date_time_to_pdf_date(GDateTime *datetime)
{
int offset_min;
gchar *date_str;
std::unique_ptr<GooString> out_str;
offset_min = g_date_time_get_utc_offset(datetime) / 1000000 / 60;
date_str = g_date_time_format(datetime, "D:%Y%m%d%H%M%S");
if (offset_min == 0) {
out_str = GooString::format("{0:s}Z", date_str);
} else {
char tz = offset_min > 0 ? '+' : '-';
out_str = GooString::format("{0:s}{1:c}{2:02d}'{3:02d}'", date_str, tz, offset_min / 60, offset_min % 60);
}
g_free(date_str);
return out_str.release();
}
static void _poppler_sign_document_thread(GTask *task, PopplerDocument *document, const PopplerSigningData *signing_data, GCancellable *cancellable)
{
const PopplerCertificateInfo *certificate_info;
const char *signing_data_signature_text;
const PopplerColor *font_color;
const PopplerColor *border_color;
const PopplerColor *background_color;
gboolean ret;
g_return_if_fail(POPPLER_IS_DOCUMENT(document));
g_return_if_fail(signing_data != nullptr);
signing_data_signature_text = poppler_signing_data_get_signature_text(signing_data);
if (signing_data_signature_text == nullptr) {
g_task_return_new_error(task, POPPLER_ERROR, POPPLER_ERROR_SIGNING, "No signature given");
return;
}
certificate_info = poppler_signing_data_get_certificate_info(signing_data);
if (certificate_info == nullptr) {
g_task_return_new_error(task, POPPLER_ERROR, POPPLER_ERROR_SIGNING, "Invalid certificate information provided for signing");
return;
}
PopplerPage *page = poppler_document_get_page(document, poppler_signing_data_get_page(signing_data));
if (page == nullptr) {
g_task_return_new_error(task, POPPLER_ERROR, POPPLER_ERROR_SIGNING, "Invalid page number selected for signing");
return;
}
font_color = poppler_signing_data_get_font_color(signing_data);
border_color = poppler_signing_data_get_border_color(signing_data);
background_color = poppler_signing_data_get_background_color(signing_data);
std::unique_ptr<GooString> signature_text = std::make_unique<GooString>(utf8ToUtf16WithBom(signing_data_signature_text));
std::unique_ptr<GooString> signature_text_left = std::make_unique<GooString>(utf8ToUtf16WithBom(poppler_signing_data_get_signature_text_left(signing_data)));
const auto field_partial_name = new GooString(poppler_signing_data_get_field_partial_name(signing_data), strlen(poppler_signing_data_get_field_partial_name(signing_data)));
const auto owner_pwd = std::optional<GooString>(poppler_signing_data_get_document_owner_password(signing_data));
const auto user_pwd = std::optional<GooString>(poppler_signing_data_get_document_user_password(signing_data));
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);
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);
const PopplerRectangle *rect = poppler_signing_data_get_signature_rectangle(signing_data);
ret = document->doc->sign(poppler_signing_data_get_destination_filename(signing_data), poppler_certificate_info_get_id((PopplerCertificateInfo *)certificate_info),
poppler_signing_data_get_password(signing_data) ? poppler_signing_data_get_password(signing_data) : "", field_partial_name, poppler_signing_data_get_page(signing_data) + 1,
PDFRectangle(rect->x1, rect->y1, rect->x2, rect->y2), *signature_text, *signature_text_left, poppler_signing_data_get_font_size(signing_data), poppler_signing_data_get_left_font_size(signing_data),
std::make_unique<AnnotColor>(font_color->red, font_color->green, font_color->blue), poppler_signing_data_get_border_width(signing_data),
std::make_unique<AnnotColor>(border_color->red, border_color->green, border_color->blue), std::make_unique<AnnotColor>(background_color->red, background_color->green, background_color->blue), reason.get(),
location.get(), poppler_signing_data_get_image_path(signing_data) ? poppler_signing_data_get_image_path(signing_data) : "", owner_pwd, user_pwd);
g_task_return_boolean(task, ret);
}
/**
* poppler_document_sign:
* @document: a #PopplerDocument
* @signing_data: a #PopplerSigningData
* @cancellable: a #GCancellable
* @callback: a #GAsyncReadyCallback
* @user_data: user data used by callback function
*
* Sign #document using #signing_data.
*
* Since: 23.07.0
**/
void poppler_document_sign(PopplerDocument *document, const PopplerSigningData *signing_data, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data)
{
GTask *task;
g_return_if_fail(POPPLER_IS_DOCUMENT(document));
g_return_if_fail(signing_data != nullptr);
task = g_task_new(document, cancellable, callback, user_data);
g_task_set_task_data(task, (void *)signing_data, nullptr);
g_task_run_in_thread(task, (GTaskThreadFunc)_poppler_sign_document_thread);
g_object_unref(task);
}
/**
* poppler_document_sign_finish:
* @document: a #PopplerDocument
* @result: a #GAsyncResult
* @error: a #GError
*
* Finish poppler_sign_document and get return status or error.
*
* Returns: %TRUE on successful signing a document, otherwise %FALSE and error is set.
*
* Since: 23.07.0
**/
gboolean poppler_document_sign_finish(PopplerDocument *document, GAsyncResult *result, GError **error)
{
g_return_val_if_fail(g_task_is_valid(result, document), FALSE);
return g_task_propagate_boolean(G_TASK(result), error);
}