/* poppler-document.cc: glib wrapper for poppler * Copyright (C) 2005, Red Hat, Inc. * * Copyright (C) 2016 Jakub Alba * Copyright (C) 2018-2019 Marek Kasik * Copyright (C) 2019 Masamichi Hosoda * Copyright (C) 2019, Oliver Sander * * 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 #ifndef __GI_SCANNER__ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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 #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 }; 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 (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->doc = newDoc; document->output_dev = new CairoOutputDev (); document->output_dev->startDoc(document->doc); return document; } static GooString * poppler_password_to_latin1 (const gchar *password) { gchar *password_latin; GooString *password_g; if (!password) return nullptr; password_latin = g_convert(password, -1, "ISO-8859-1", "UTF-8", nullptr, nullptr, nullptr); password_g = new 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; GooString *password_g; char *filename; if (!globalParams) { globalParams = std::make_unique(); } filename = g_filename_from_uri (uri, nullptr, error); if (!filename) return nullptr; 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 NULL; length = MultiByteToWideChar(CP_UTF8, 0, filename, -1, filenameW, length); newDoc = new PDFDoc(filenameW, length, password_g, password_g); delete [] filenameW; #else GooString *filename_g; filename_g = new GooString (filename); newDoc = new PDFDoc(filename_g, password_g, password_g); #endif g_free (filename); delete password_g; return _poppler_document_new_from_pdfdoc (newDoc, error); } /** * poppler_document_new_from_data: * @data: (array length=length) (element-type guint8): the pdf data * @length: the length of #data * @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. * * Note that @data must remain valid for as long as the returned document exists. * Prefer using poppler_document_new_from_bytes(). * * Return value: A newly created #PopplerDocument, or %NULL **/ PopplerDocument * poppler_document_new_from_data (char *data, int length, const char *password, GError **error) { PDFDoc *newDoc; MemStream *str; GooString *password_g; if (!globalParams) { globalParams = std::make_unique(); } // create stream str = new MemStream(data, 0, length, Object(objNull)); password_g = poppler_password_to_latin1(password); newDoc = new PDFDoc(str, password_g, password_g); delete password_g; return _poppler_document_new_from_pdfdoc (newDoc, error); } class BytesStream : public MemStream { std::unique_ptr m_bytes; public: BytesStream(GBytes *bytes, Object &&dictA) : MemStream(static_cast(g_bytes_get_data(bytes, nullptr)), 0, g_bytes_get_size(bytes), std::move(dictA)), m_bytes{g_bytes_ref(bytes), &g_bytes_unref} { } }; /** * 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; GooString *password_g; g_return_val_if_fail(bytes != nullptr, nullptr); g_return_val_if_fail(error == nullptr || *error == nullptr, nullptr); if (!globalParams) { globalParams = std::make_unique(); } // create stream str = new BytesStream(bytes, Object(objNull)); password_g = poppler_password_to_latin1(password); newDoc = new PDFDoc(str, password_g, password_g); delete password_g; return _poppler_document_new_from_pdfdoc (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; GooString *password_g; g_return_val_if_fail(G_IS_INPUT_STREAM(stream), NULL); g_return_val_if_fail(length == (goffset)-1 || length > 0, NULL); if (!globalParams) { globalParams = std::make_unique(); } 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), new GooString()); str = new CachedFileStream(cachedFile, 0, false, cachedFile->getLength(), Object(objNull)); } password_g = poppler_password_to_latin1(password); newDoc = new PDFDoc(str, password_g, password_g); delete password_g; return _poppler_document_new_from_pdfdoc (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; } 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 = new GooString (filename); int err_code; g_free (filename); err_code = document->doc->saveAs (fname); retval = handle_save_error (err_code, error); delete fname; } 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 = new GooString (filename); int err_code; g_free (filename); err_code = document->doc->saveWithoutChangesAs (fname); retval = handle_save_error (err_code, error); delete fname; } return retval; } static void poppler_document_finalize (GObject *object) { PopplerDocument *document = POPPLER_DOCUMENT (object); poppler_document_layers_free (document); delete document->output_dev; delete document->doc; 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 = (gchar *)g_memdup (permanent.c_str(), 32); if (update_id) *update_id = (gchar *)g_memdup (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 #PopplerAttachments. 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; FileSpec *emb_file; emb_file = catalog->embeddedFile (i); if (!emb_file->isOk () || !emb_file->getEmbeddedFile()->isOk()) { delete emb_file; continue; } attachment = _poppler_attachment_new (emb_file); delete emb_file; 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); LinkDest *link_dest = document->doc->findDest (&g_link_name); if (link_dest == nullptr) return nullptr; PopplerDest *dest = _poppler_dest_new_goto (document, link_dest); delete link_dest; return dest; } static gint _poppler_dest_compare_keys (gconstpointer a, gconstpointer b, gpointer user_data) { return g_strcmp0 (static_cast(a), static_cast(b)); } static void _poppler_dest_destroy_value (gpointer value) { poppler_dest_free (static_cast(value)); } /** * poppler_document_create_dests_tree: * @document: A #PopplerDocument * * Creates named destinations balanced binary tree in @document * * The tree key is strings in the form returned by * poppler_named_dest_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; LinkDest *link_dest; PopplerDest *dest; int i; gchar *key; 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); key = poppler_named_dest_from_bytestring (reinterpret_cast (name), strlen (name)); link_dest = catalog->getDestsDest (i); if (link_dest) { dest = _poppler_dest_new_goto (document, link_dest); delete link_dest; 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); key = poppler_named_dest_from_bytestring (reinterpret_cast (name->c_str ()), name->getLength ()); link_dest = catalog->getDestNameTreeDest (i); if (link_dest) { dest = _poppler_dest_new_goto (document, link_dest); delete link_dest; 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 (s->hasUnicodeMarker()) { result = g_convert (s->c_str () + 2, s->getLength () - 2, "UTF-8", "UTF-16BE", nullptr, nullptr, nullptr); } else if (s->hasUnicodeMarkerLE()) { 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 (!result->hasUnicodeMarker()) { result->prependUnicodeMarker(); } 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) (allow-none): return location for the PDF major version number * @minor_version: (out) (allow-none): return location for the PDF minor version number * * Returns: the major and minor PDF version numbers * * 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); GooString *goo_title = document->doc->getDocInfoTitle(); gchar *utf8 = _poppler_goo_string_to_utf8(goo_title); delete goo_title; return utf8; } /** * 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); GooString *goo_author = document->doc->getDocInfoAuthor(); gchar *utf8 = _poppler_goo_string_to_utf8(goo_author); delete goo_author; return utf8; } /** * 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); GooString *goo_subject = document->doc->getDocInfoSubject(); gchar *utf8 = _poppler_goo_string_to_utf8(goo_subject); delete goo_subject; return utf8; } /** * 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); GooString *goo_keywords = document->doc->getDocInfoKeywords(); gchar *utf8 = _poppler_goo_string_to_utf8(goo_keywords); delete goo_keywords; return utf8; } /** * 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); GooString *goo_creator = document->doc->getDocInfoCreator(); gchar *utf8 = _poppler_goo_string_to_utf8(goo_creator); delete goo_creator; return utf8; } /** * 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); GooString *goo_producer = document->doc->getDocInfoProducer(); gchar *utf8 = _poppler_goo_string_to_utf8(goo_producer); delete goo_producer; return utf8; } /** * 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); GooString *str = document->doc->getDocInfoCreatDate(); if (str == nullptr) { return (time_t)-1; } time_t date; gboolean success = _poppler_convert_pdf_date_to_gtime (str, &date); delete str; 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_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); GooString *str = document->doc->getDocInfoModDate(); if (str == nullptr) { return (time_t)-1; } time_t date; gboolean success = _poppler_convert_pdf_date_to_gtime (str, &date); delete str; 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_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_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 #PopplerPageRanges 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 #PopplerPageRanges 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> 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); 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: infostring = nullptr; } gchar *utf8 = _poppler_goo_string_to_utf8 (infostring); delete infostring; return utf8; } /** * 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 ()) { GooString *s = catalog->readMetadata (); if (s != nullptr) { retval = g_strdup (s->c_str()); delete s; } } return retval; } 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_MOD_DATE: g_value_set_int (value, poppler_document_get_modification_date (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_MOD_DATE: poppler_document_set_modification_date (document, g_value_get_int (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 */ 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, G_PARAM_READWRITE)); /** * PopplerDocument:mod-date: * * The date the document was most recently modified as seconds since the Epoch, or -1 */ 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, 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 *items; int index; }; POPPLER_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 — typically a #POPPLER_ACTION_GOTO_DEST or a * #POPPLER_ACTION_URI. * * Here is a simple example of some code that walks the full index: * * * 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); * } * * * Return value: a new #PopplerIndexIter **/ PopplerIndexIter * poppler_index_iter_new (PopplerDocument *document) { PopplerIndexIter *iter; Outline *outline; const std::vector *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) { static UnicodeMap *uMap = nullptr; if (uMap == nullptr) { GooString *enc = new GooString("UTF-8"); uMap = globalParams->getUnicodeMap(enc); uMap->incRefCnt (); delete enc; } 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 (); title = unicode_to_char (item->getTitle(), item->getTitleLength ()); 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 items; int index; }; POPPLER_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) { const GooString *name; FontInfo *info; info = iter->items[iter->index]; name = info->getName(); if (name != nullptr) { return info->getName()->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) { const GooString *name; FontInfo *info; info = iter->items[iter->index]; name = info->getSubstituteName(); if (name != nullptr) { 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) { const GooString *file; FontInfo *info; info = iter->items[iter->index]; file = info->getFile(); if (file != nullptr) { 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) { const GooString *encoding; FontInfo *info; info = iter->items[iter->index]; encoding = info->getEncoding(); if (encoding != nullptr) { 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(); g_slice_free (PopplerFontsIter, iter); } static PopplerFontsIter * poppler_fonts_iter_new (std::vector &&items) { PopplerFontsIter *iter; iter = g_slice_new (PopplerFontsIter); new ((void*)&iter->items) std::vector(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 * * * 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); * } * * * 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 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; }; POPPLER_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->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); 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: 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; } /** * 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; FormPageWidgets *widgets; FormWidget *field; FormWidget::decodeID (id, &pageNum, &fieldNum); page = document->doc->getPage (pageNum); if (!page) return nullptr; 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 (date->hasUnicodeMarker()) { 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; }