From 3bd1f8e319cc5fc96c141dff052bcb41f525caed Mon Sep 17 00:00:00 2001 From: Christian Persch Date: Thu, 16 Aug 2018 23:02:58 +0200 Subject: [PATCH 1/3] gfile: Enable inclusion together with glib.h GDir class name conflicts with a glib type. Surround its definition with GFILE_NO_GDIR, so that source code in glib/ can define that before including gfile.h and not get the conflict. --- goo/gfile.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/goo/gfile.h b/goo/gfile.h index 4ce805e..0f02180 100644 --- a/goo/gfile.h +++ b/goo/gfile.h @@ -175,6 +175,8 @@ private: // GDir and GDirEntry //------------------------------------------------------------------------ +#ifndef GFILE_NO_GDIR + class GDirEntry { public: @@ -220,4 +222,6 @@ private: #endif }; +#endif /* !GFILE_NO_GDIR */ + #endif -- 2.9.4 From 76f531346c873d64b9f15485210c30fe39da9a92 Mon Sep 17 00:00:00 2001 From: Christian Persch Date: Thu, 16 Aug 2018 23:02:58 +0200 Subject: [PATCH 2/3] Add poppler_document_new_from_fd While it's already possible to create a PopplerDocument for STDIN by using fd://0 as the URI, it was not yet possible to create a PopplerDocument from a file descriptor. This adds poppler_document_new_from_fd(), which accepts a readable FD for a regular file, or for STDIN. Add a --fd option to test/gtk-test to test this. When used, gtk-test arguments are FD numbers instead of filenames or URIs. To test, use e.g. $ 3 +#include +#include +#include +#include #ifndef __GI_SCANNER__ +#define GFILE_NO_GDIR +#include #include #include #include @@ -36,6 +42,8 @@ #include #include #include +#include +#include #endif #include "poppler.h" @@ -357,6 +365,87 @@ poppler_document_new_from_gfile (GFile *file, return document; } +/** + * poppler_document_new_from_fd: + * @file: 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 ownership of @fd; you must not operate on it + * again, or close it. + * + * Returns: (transfer full): a new #PopplerDocument, or %NULL + * + * Since: 0.68 + */ +PopplerDocument * +poppler_document_new_from_fd (int fd, + const char *password, + GError **error) +{ + struct stat statbuf; + int flags; + BaseStream *stream; + PDFDoc *newDoc; + GooString *password_g; + + g_return_val_if_fail(fd != -1, NULL); + + 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 FALSE; + } + + if (fd != STDIN_FILENO && !S_ISREG(statbuf.st_mode)) { + g_set_error_literal(error, G_FILE_ERROR, G_FILE_ERROR_BADF, + "Not a regular file."); + close(fd); + return FALSE; + } + + switch (flags & O_ACCMODE) { + case O_RDONLY: + case O_RDWR: + break; + case O_WRONLY: + default: + g_set_error_literal(error, G_FILE_ERROR, G_FILE_ERROR_BADF, + "Not a readable file descriptor."); + close(fd); + return FALSE; + } + + if (!globalParams) { + globalParams = new GlobalParams(); + } + + if (fd == STDIN_FILENO) { + CachedFile *cachedFile = new CachedFile(new StdinCacheLoader(), nullptr); + stream = new CachedFileStream(cachedFile, 0, gFalse, cachedFile->getLength(), + Object(objNull)); + } else { + GooFile *file = GooFile::open(fd); + stream = new FileStream(file, 0, gFalse, file->size(), + Object(objNull)); + } + + password_g = poppler_password_to_latin1(password); + newDoc = new PDFDoc(stream, password_g, password_g); + delete password_g; + + return _poppler_document_new_from_pdfdoc (newDoc, error); +} + static gboolean handle_save_error (int err_code, GError **error) diff --git a/glib/poppler-document.h b/glib/poppler-document.h index a7fcea1..aaa13d9 100644 --- a/glib/poppler-document.h +++ b/glib/poppler-document.h @@ -184,6 +184,9 @@ PopplerDocument *poppler_document_new_from_gfile (GFile *fil const char *password, GCancellable *cancellable, GError **error); +PopplerDocument *poppler_document_new_from_fd (int fd, + const char *password, + GError **error); gboolean poppler_document_save (PopplerDocument *document, const char *uri, GError **error); diff --git a/glib/reference/poppler-docs.sgml b/glib/reference/poppler-docs.sgml index 5db818f..0b031dc 100644 --- a/glib/reference/poppler-docs.sgml +++ b/glib/reference/poppler-docs.sgml @@ -73,6 +73,10 @@ Index of new symbols in 0.46 + + Index of new symbols in 0.68 + + diff --git a/glib/reference/poppler-sections.txt b/glib/reference/poppler-sections.txt index af1bbba..8fe501c 100644 --- a/glib/reference/poppler-sections.txt +++ b/glib/reference/poppler-sections.txt @@ -128,6 +128,7 @@ poppler_document_new_from_file poppler_document_new_from_data poppler_document_new_from_stream poppler_document_new_from_gfile +poppler_document_new_from_fd poppler_document_save poppler_document_save_a_copy poppler_document_get_id diff --git a/goo/gfile.cc b/goo/gfile.cc index 381a0ce..4a7bcea 100644 --- a/goo/gfile.cc +++ b/goo/gfile.cc @@ -682,6 +682,10 @@ GooFile* GooFile::open(const wchar_t *fileName) { return handle == INVALID_HANDLE_VALUE ? nullptr : new GooFile(handle); } +GooFile* GooFile::open(int fd) { + return nullptr; +} + bool GooFile::modificationTimeChangedSinceOpen() const { struct _FILETIME lastModified; @@ -718,10 +722,15 @@ GooFile* GooFile::open(const GooString *fileName) { return fd < 0 ? nullptr : new GooFile(fd); } +GooFile* GooFile::open(int fd) { + return fd == -1 ? nullptr : new GooFile(fd); +} + GooFile::GooFile(int fdA) : fd(fdA) { struct stat statbuf; + fstat(fd, &statbuf); modifiedTimeOnOpen = mtim(statbuf); } diff --git a/goo/gfile.h b/goo/gfile.h index 0f02180..5a17773 100644 --- a/goo/gfile.h +++ b/goo/gfile.h @@ -146,6 +146,7 @@ public: Goffset size() const; static GooFile *open(const GooString *fileName); + static GooFile *open(int fd); #ifdef _WIN32 static GooFile *open(const wchar_t *fileName); @@ -154,7 +155,7 @@ public: // Asuming than on windows you can't change files that are already open bool modificationTimeChangedSinceOpen() const; - + private: GooFile(HANDLE handleA); HANDLE handle; @@ -163,7 +164,7 @@ private: ~GooFile() { close(fd); } bool modificationTimeChangedSinceOpen() const; - + private: GooFile(int fdA); int fd; diff --git a/test/gtk-test.cc b/test/gtk-test.cc index ec8a9eb..d970083 100644 --- a/test/gtk-test.cc +++ b/test/gtk-test.cc @@ -20,15 +20,18 @@ #include #include #include +#include static int page = 0; static gboolean cairo_output = FALSE; static gboolean splash_output = FALSE; +static gboolean args_are_fds = FALSE; static const char **file_arguments = nullptr; static const GOptionEntry options[] = { { "cairo", 'c', 0, G_OPTION_ARG_NONE, &cairo_output, "Cairo Output Device", nullptr}, { "splash", 's', 0, G_OPTION_ARG_NONE, &splash_output, "Splash Output Device", nullptr}, { "page", 'p', 0, G_OPTION_ARG_INT, &page, "Page number", "PAGE" }, + { "fd", 'f', 0, G_OPTION_ARG_NONE, &args_are_fds, "File descriptors", nullptr }, { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &file_arguments, nullptr, "PDF-FILES…" }, { } }; @@ -377,31 +380,50 @@ main (int argc, char *argv []) for (int i = 0; file_arguments[i]; i++) { View *view; - GFile *file; - PopplerDocument *doc; + GFile *file = nullptr; + PopplerDocument *doc = nullptr; GError *error = nullptr; + const char *arg; - file = g_file_new_for_commandline_arg (file_arguments[i]); - doc = poppler_document_new_from_gfile (file, nullptr, nullptr, &error); - if (!doc) { - gchar *uri; + arg = file_arguments[i]; + if (args_are_fds) { + char *end; + gint64 v; - uri = g_file_get_uri (file); - g_printerr ("Error opening document %s: %s\n", uri, error->message); - g_error_free (error); - g_free (uri); + errno = 0; + end = nullptr; + v = g_ascii_strtoll (arg, &end, 10); + if (errno || end == arg || v == -1 || v < G_MININT || v > G_MAXINT) { + g_set_error (&error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, + "Failed to parse \"%s\" as file descriptor number", arg); + } else { + doc = poppler_document_new_from_fd (int(v), nullptr, &error); + } + } else { + file = g_file_new_for_commandline_arg (arg); + doc = poppler_document_new_from_gfile (file, nullptr, nullptr, &error); + if (!doc) { + gchar *uri; + + uri = g_file_get_uri (file); + g_prefix_error (&error, "%s: ", uri); + g_free (uri); + } g_object_unref (file); - - continue; } - g_object_unref (file); - view = view_new (doc); - view_list = g_list_prepend (view_list, view); - view_set_page (view, CLAMP (page, 0, poppler_document_get_n_pages (doc) - 1)); + if (doc) { + view = view_new (doc); + view_list = g_list_prepend (view_list, view); + view_set_page (view, CLAMP (page, 0, poppler_document_get_n_pages (doc) - 1)); + } else { + g_printerr ("Error opening document: %s\n", error->message); + g_error_free (error); + } } - gtk_main (); + if (view_list != nullptr) + gtk_main (); delete globalParams; -- 2.9.4 From e80f295f152b444007bcd558dc829fec3e50d83c Mon Sep 17 00:00:00 2001 From: Christian Persch Date: Thu, 16 Aug 2018 23:02:58 +0200 Subject: [PATCH 3/3] glib: Add API to save to file descriptor --- glib/poppler-attachment.cc | 76 ++++++++++++++++++++++++++++++++++--- glib/poppler-attachment.h | 3 ++ glib/poppler-document.cc | 59 ++++++++++++++++++++++++++++ glib/poppler-document.h | 8 ++++ glib/poppler-media.cc | 74 +++++++++++++++++++++++++++++++++--- glib/poppler-media.h | 3 ++ glib/reference/poppler-sections.txt | 3 ++ 7 files changed, 214 insertions(+), 12 deletions(-) diff --git a/glib/poppler-attachment.cc b/glib/poppler-attachment.cc index c6502e9..545ea7a 100644 --- a/glib/poppler-attachment.cc +++ b/glib/poppler-attachment.cc @@ -138,11 +138,12 @@ save_helper (const gchar *buf, n = fwrite (buf, 1, count, f); if (n != count) { + int errsv = errno; g_set_error (error, G_FILE_ERROR, - g_file_error_from_errno (errno), + g_file_error_from_errno (errsv), _("Error writing to image file: %s"), - g_strerror (errno)); + g_strerror (errsv)); return FALSE; } @@ -170,18 +171,22 @@ poppler_attachment_save (PopplerAttachment *attachment, FILE *f; g_return_val_if_fail (POPPLER_IS_ATTACHMENT (attachment), FALSE); + g_return_val_if_fail (filename != nullptr, FALSE); + g_return_val_if_fail (error == nullptr || *error == nullptr, FALSE); + f = g_fopen (filename, "wb"); if (f == nullptr) { + int errsv = errno; gchar *display_name = g_filename_display_name (filename); g_set_error (error, G_FILE_ERROR, - g_file_error_from_errno (errno), + g_file_error_from_errno (errsv), _("Failed to open '%s' for writing: %s"), display_name, - g_strerror (errno)); + g_strerror (errsv)); g_free (display_name); return FALSE; } @@ -190,13 +195,14 @@ poppler_attachment_save (PopplerAttachment *attachment, if (fclose (f) < 0) { + int errsv = errno; gchar *display_name = g_filename_display_name (filename); g_set_error (error, G_FILE_ERROR, - g_file_error_from_errno (errno), + g_file_error_from_errno (errsv), _("Failed to close '%s', all data may not have been saved: %s"), display_name, - g_strerror (errno)); + g_strerror (errsv)); g_free (display_name); return FALSE; } @@ -204,6 +210,64 @@ poppler_attachment_save (PopplerAttachment *attachment, return result; } +/** + * poppler_attachment_save_to_fd: + * @attachment: A #PopplerAttachment. + * @fd: a valid file descriptor open for writing + * @error: (allow-none): return location for error, or %NULL. + * + * Saves @attachment to a file referred to by @fd. If @error is set, %FALSE + * will be returned. Possible errors include those in the #G_FILE_ERROR domain + * and whatever the save function generates. + * Note that this function ownership of @fd; you must not operate on it + * again, or close it. + * + * Return value: %TRUE, if the file successfully saved + * + * Since: 0.68 + **/ +gboolean +poppler_attachment_save_to_fd (PopplerAttachment *attachment, + int fd, + GError **error) +{ + gboolean result; + FILE *f; + + g_return_val_if_fail (POPPLER_IS_ATTACHMENT (attachment), FALSE); + g_return_val_if_fail (fd != -1, FALSE); + g_return_val_if_fail (error == nullptr || *error == nullptr, FALSE); + + f = fdopen (fd, "wb"); + if (f == 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; + } + + result = poppler_attachment_save_to_callback (attachment, save_helper, f, error); + + if (fclose (f) < 0) + { + int errsv = errno; + g_set_error (error, + G_FILE_ERROR, + g_file_error_from_errno (errsv), + _("Failed to close FD %d, all data may not have been saved: %s"), + fd, + g_strerror (errsv)); + return FALSE; + } + + return result; +} + #define BUF_SIZE 1024 /** diff --git a/glib/poppler-attachment.h b/glib/poppler-attachment.h index ff8c849..d4366c7 100644 --- a/glib/poppler-attachment.h +++ b/glib/poppler-attachment.h @@ -76,6 +76,9 @@ GType poppler_attachment_get_type (void) G_GNUC_CONST; gboolean poppler_attachment_save (PopplerAttachment *attachment, const char *filename, GError **error); +gboolean poppler_attachment_save_to_fd (PopplerAttachment *attachment, + int fd, + GError **error); gboolean poppler_attachment_save_to_callback (PopplerAttachment *attachment, PopplerAttachmentSaveFunc save_func, gpointer user_data, diff --git a/glib/poppler-document.cc b/glib/poppler-document.cc index d986452..3d797c5 100644 --- a/glib/poppler-document.cc +++ b/glib/poppler-document.cc @@ -549,6 +549,64 @@ poppler_document_save_a_copy (PopplerDocument *document, return retval; } +/** + * 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 ownership of @fd; you must not operate on it + * again, or 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: 0.68 + **/ +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); +} + static void poppler_document_finalize (GObject *object) { @@ -2890,6 +2948,7 @@ poppler_ps_file_new (PopplerDocument *document, const char *filename, return ps_file; } + /** * poppler_ps_file_set_paper_size: * @ps_file: a PopplerPSFile which was not yet printed to. diff --git a/glib/poppler-document.h b/glib/poppler-document.h index aaa13d9..237f8e1 100644 --- a/glib/poppler-document.h +++ b/glib/poppler-document.h @@ -193,6 +193,10 @@ gboolean poppler_document_save (PopplerDocument *doc gboolean poppler_document_save_a_copy (PopplerDocument *document, const char *uri, GError **error); +gboolean poppler_document_save_to_fd (PopplerDocument *document, + int fd, + gboolean include_changes, + GError **error); gboolean poppler_document_get_id (PopplerDocument *document, gchar **permanent_id, gchar **update_id); @@ -306,6 +310,10 @@ PopplerPSFile *poppler_ps_file_new (PopplerDocument *document, const char *filename, int first_page, int n_pages); +PopplerPSFile *poppler_ps_file_new_fd (PopplerDocument *document, + int fd, + int first_page, + int n_pages); void poppler_ps_file_set_paper_size (PopplerPSFile *ps_file, double width, double height); diff --git a/glib/poppler-media.cc b/glib/poppler-media.cc index b7afa9a..62f5f78 100644 --- a/glib/poppler-media.cc +++ b/glib/poppler-media.cc @@ -177,11 +177,12 @@ save_helper (const gchar *buf, n = fwrite (buf, 1, count, f); if (n != count) { + int errsv = errno; g_set_error (error, G_FILE_ERROR, - g_file_error_from_errno (errno), + g_file_error_from_errno (errsv), "Error writing to media file: %s", - g_strerror (errno)); + g_strerror (errsv)); return FALSE; } @@ -218,13 +219,14 @@ poppler_media_save (PopplerMedia *poppler_media, if (f == nullptr) { + int errsv = errno; gchar *display_name = g_filename_display_name (filename); g_set_error (error, G_FILE_ERROR, - g_file_error_from_errno (errno), + g_file_error_from_errno (errsv), "Failed to open '%s' for writing: %s", display_name, - g_strerror (errno)); + g_strerror (errsv)); g_free (display_name); return FALSE; } @@ -233,13 +235,14 @@ poppler_media_save (PopplerMedia *poppler_media, if (fclose (f) < 0) { + int errsv = errno; gchar *display_name = g_filename_display_name (filename); g_set_error (error, G_FILE_ERROR, - g_file_error_from_errno (errno), + g_file_error_from_errno (errsv), "Failed to close '%s', all data may not have been saved: %s", display_name, - g_strerror (errno)); + g_strerror (errsv)); g_free (display_name); return FALSE; } @@ -247,6 +250,65 @@ poppler_media_save (PopplerMedia *poppler_media, return result; } +/** + * poppler_media_save_to_fd: + * @poppler_media: a #PopplerMedia + * @fd: a valid file descriptor open for writing + * @error: (allow-none): return location for error, or %NULL. + * + * Saves embedded stream of @poppler_media to a file referred to by @fd. + * If @error is set, %FALSE will be returned. + * Possible errors include those in the #G_FILE_ERROR domain + * and whatever the save function generates. + * Note that this function ownership of @fd; you must not operate on it + * again, or close it. + * + * Return value: %TRUE, if the file successfully saved + * + * Since: 0.68 + */ +gboolean +poppler_media_save_to_fd (PopplerMedia *poppler_media, + int fd, + GError **error) +{ + gboolean result; + FILE *f; + + g_return_val_if_fail (POPPLER_IS_MEDIA (poppler_media), FALSE); + g_return_val_if_fail (poppler_media->stream.isStream(), FALSE); + + f = fdopen (fd, "wb"); + + if (f == 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; + } + + result = poppler_media_save_to_callback (poppler_media, save_helper, f, error); + + if (fclose (f) < 0) + { + int errsv = errno; + g_set_error (error, + G_FILE_ERROR, + g_file_error_from_errno (errsv), + "Failed to close FD %d, all data may not have been saved: %s", + fd, + g_strerror (errsv)); + return FALSE; + } + + return result; +} + #define BUF_SIZE 1024 /** diff --git a/glib/poppler-media.h b/glib/poppler-media.h index 292d386..b80b6d1 100644 --- a/glib/poppler-media.h +++ b/glib/poppler-media.h @@ -62,6 +62,9 @@ const gchar *poppler_media_get_mime_type (PopplerMedia *poppler_media) gboolean poppler_media_save (PopplerMedia *poppler_media, const char *filename, GError **error); +gboolean poppler_media_save_to_fd (PopplerMedia *poppler_media, + int fd, + GError **error); gboolean poppler_media_save_to_callback (PopplerMedia *poppler_media, PopplerMediaSaveFunc save_func, gpointer user_data, diff --git a/glib/reference/poppler-sections.txt b/glib/reference/poppler-sections.txt index 8fe501c..715a684 100644 --- a/glib/reference/poppler-sections.txt +++ b/glib/reference/poppler-sections.txt @@ -130,6 +130,7 @@ poppler_document_new_from_stream poppler_document_new_from_gfile poppler_document_new_from_fd poppler_document_save +poppler_document_save_to_fd poppler_document_save_a_copy poppler_document_get_id poppler_document_get_pdf_version_string @@ -280,6 +281,7 @@ poppler_action_movie_operation_get_type PopplerAttachment PopplerAttachmentSaveFunc poppler_attachment_save +poppler_attachment_save_to_fd poppler_attachment_save_to_callback @@ -541,6 +543,7 @@ poppler_media_get_filename poppler_media_get_mime_type poppler_media_is_embedded poppler_media_save +poppler_media_save_to_fd poppler_media_save_to_callback -- 2.9.4