Commit 2d765f0e authored by Christian Persch's avatar Christian Persch Committed by Germán Poo-Caamaño

previewer: Allow passing the document and print settings as FDs

parent 57755b81
Pipeline #40406 failed with stages
in 5 minutes and 36 seconds
......@@ -21,6 +21,8 @@
#include <config.h>
#include <fcntl.h>
#if GTKUNIXPRINT_ENABLED
#include <gtk/gtkunixprint.h>
#endif
......@@ -34,6 +36,7 @@
struct _EvPreviewerWindow {
GtkApplicationWindow base_instance;
EvJob *job;
EvDocumentModel *model;
EvDocument *document;
......@@ -48,6 +51,7 @@ struct _EvPreviewerWindow {
#endif
gchar *print_job_title;
gchar *source_file;
int source_fd;
};
struct _EvPreviewerWindowClass {
......@@ -56,7 +60,7 @@ struct _EvPreviewerWindowClass {
enum {
PROP_0,
PROP_MODEL
PROP_JOB
};
#define MIN_SCALE 0.05409
......@@ -73,7 +77,6 @@ get_screen_dpi (EvPreviewerWindow *window)
return ev_document_misc_get_screen_dpi (screen);
}
#if GTKUNIXPRINT_ENABLED
static void
ev_previewer_window_error_dialog_run (EvPreviewerWindow *window,
GError *error)
......@@ -91,7 +94,6 @@ ev_previewer_window_error_dialog_run (EvPreviewerWindow *window,
gtk_dialog_run (GTK_DIALOG (dialog));
gtk_widget_destroy (dialog);
}
#endif
static void
ev_previewer_window_close (GSimpleAction *action,
......@@ -195,15 +197,28 @@ static void
ev_previewer_window_do_print (EvPreviewerWindow *window)
{
GtkPrintJob *job;
gboolean rv = FALSE;
GError *error = NULL;
job = gtk_print_job_new (window->print_job_title ?
window->print_job_title :
(window->source_file ? window->source_file : _("Evince")),
window->source_file,
window->printer,
window->print_settings,
window->print_page_setup);
if (gtk_print_job_set_source_file (job, window->source_file, &error)) {
#if GTK_CHECK_VERSION (3, 22, 0)
if (window->source_fd != -1)
rv = gtk_print_job_set_source_fd (job, window->source_fd, &error);
else
#endif
if (window->source_file != NULL)
rv = gtk_print_job_set_source_file (job, window->source_file, &error);
else
g_set_error_literal (&error, GTK_PRINT_ERROR, GTK_PRINT_ERROR_GENERAL,
"Neither file nor FD to print.");
if (rv) {
gtk_print_job_send (job,
(GtkPrintJobCompleteFunc)ev_previewer_window_print_finished,
window, NULL);
......@@ -322,15 +337,26 @@ view_sizing_mode_changed (EvDocumentModel *model,
}
static void
ev_previewer_window_set_document (EvPreviewerWindow *window,
GParamSpec *pspec,
EvDocumentModel *model)
load_job_finished_cb (EvJob *job,
EvPreviewerWindow *window)
{
EvDocument *document = ev_document_model_get_document (model);
g_assert (job == window->job);
if (ev_job_is_failed (job)) {
ev_previewer_window_error_dialog_run (window, job->error);
g_object_unref (window->job);
window->job = NULL;
return;
}
window->document = g_object_ref (job->document);
window->document = g_object_ref (document);
g_object_unref (window->job);
window->job = NULL;
g_signal_connect (model, "notify::sizing-mode",
ev_document_model_set_document (window->model, window->document);
g_signal_connect (window->model, "notify::sizing-mode",
G_CALLBACK (view_sizing_mode_changed),
window);
}
......@@ -340,6 +366,11 @@ ev_previewer_window_dispose (GObject *object)
{
EvPreviewerWindow *window = EV_PREVIEWER_WINDOW (object);
if (window->job) {
g_object_unref (window->job);
window->job = NULL;
}
if (window->model) {
g_object_unref (window->model);
window->model = NULL;
......@@ -376,13 +407,18 @@ ev_previewer_window_dispose (GObject *object)
g_free (window->source_file);
window->source_file = NULL;
}
if (window->source_fd != -1) {
close (window->source_fd);
window->source_fd = -1;
}
G_OBJECT_CLASS (ev_previewer_window_parent_class)->dispose (object);
}
static void
ev_previewer_window_init (EvPreviewerWindow *window)
{
window->source_fd = -1;
gtk_window_set_default_size (GTK_WINDOW (window), 600, 600);
g_action_map_add_action_entries (G_ACTION_MAP (window),
......@@ -390,23 +426,6 @@ ev_previewer_window_init (EvPreviewerWindow *window)
window);
}
static void
ev_previewer_window_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
EvPreviewerWindow *window = EV_PREVIEWER_WINDOW (object);
switch (prop_id) {
case PROP_MODEL:
window->model = g_value_dup_object (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static gboolean
_gtk_css_provider_load_from_resource (GtkCssProvider *provider,
const char *resource_path,
......@@ -428,31 +447,24 @@ _gtk_css_provider_load_from_resource (GtkCssProvider *provider,
return retval;
}
static GObject *
ev_previewer_window_constructor (GType type,
guint n_construct_properties,
GObjectConstructParam *construct_params)
static void
ev_previewer_window_constructed (GObject *object)
{
GObject *object;
EvPreviewerWindow *window;
EvPreviewerWindow *window = EV_PREVIEWER_WINDOW (object);
GtkWidget *vbox;
GtkWidget *swindow;
GError *error = NULL;
gdouble dpi;
GtkCssProvider *css_provider;
object = G_OBJECT_CLASS (ev_previewer_window_parent_class)->constructor (type,
n_construct_properties,
construct_params);
window = EV_PREVIEWER_WINDOW (object);
object = G_OBJECT_CLASS (ev_previewer_window_parent_class)->constructed (object);
window->model = ev_document_model_new ();
dpi = get_screen_dpi (window);
ev_document_model_set_min_scale (window->model, MIN_SCALE * dpi / 72.0);
ev_document_model_set_max_scale (window->model, MAX_SCALE * dpi / 72.0);
ev_document_model_set_sizing_mode (window->model, EV_SIZING_AUTOMATIC);
g_signal_connect_swapped (window->model, "notify::document",
G_CALLBACK (ev_previewer_window_set_document),
window);
css_provider = gtk_css_provider_new ();
_gtk_css_provider_load_from_resource (css_provider,
......@@ -498,8 +510,6 @@ ev_previewer_window_constructor (GType type,
gtk_container_add (GTK_CONTAINER (window), vbox);
gtk_widget_show (vbox);
return object;
}
......@@ -508,95 +518,160 @@ ev_previewer_window_class_init (EvPreviewerWindowClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
gobject_class->constructor = ev_previewer_window_constructor;
gobject_class->set_property = ev_previewer_window_set_property;
gobject_class->constructed = ev_previewer_window_constructed;
gobject_class->dispose = ev_previewer_window_dispose;
g_object_class_install_property (gobject_class,
PROP_MODEL,
g_param_spec_object ("model",
"Model",
"The document model",
EV_TYPE_DOCUMENT_MODEL,
G_PARAM_WRITABLE |
G_PARAM_CONSTRUCT_ONLY));
}
/* Public methods */
EvPreviewerWindow *
ev_previewer_window_new (EvDocumentModel *model)
ev_previewer_window_new (void)
{
return g_object_new (EV_TYPE_PREVIEWER_WINDOW,
"application", g_application_get_default (),
"model", model,
NULL);
}
void
ev_previewer_window_set_print_settings (EvPreviewerWindow *window,
const gchar *print_settings)
ev_previewer_window_set_job (EvPreviewerWindow *window,
const gchar *print_settings)
{
g_return_if_fail (EV_IS_PREVIEWER_WINDOW (window));
g_return_if_fail (EV_IS_JOB (job));
g_clear_object (&window->job);
window->job = g_object_ref (job);
g_signal_connect_object (window->job, "finished",
G_CALLBACK (load_job_finished_cb),
window, 0);
ev_job_scheduler_push_job (window->job, EV_JOB_PRIORITY_NONE);
}
static gboolean
ev_previewer_window_set_print_settings_take_file (EvPreviewerWindow *window,
GMappedFile *file,
GError **error)
{
GBytes *bytes;
GKeyFile *key_file;
GtkPrintSettings *psettings;
GtkPageSetup *psetup;
char *job_name;
gboolean rv;
if (window->print_settings)
g_object_unref (window->print_settings);
if (window->print_page_setup)
g_object_unref (window->print_page_setup);
if (window->print_job_title)
g_free (window->print_job_title);
g_free (window->print_job_title);
bytes = g_mapped_file_get_bytes (file);
key_file = g_key_file_new ();
rv = g_key_file_load_from_bytes (key_file, bytes, G_KEY_FILE_NONE, error);
g_bytes_unref (bytes);
g_mapped_file_unref (file);
if (!rv) {
window->print_settings = gtk_print_settings_new ();
window->print_page_setup = gtk_page_setup_new ();
window->print_job_title = g_strdup (_("Evince"));
return FALSE;
}
psettings = gtk_print_settings_new_from_key_file (key_file,
"Print Settings",
NULL);
window->print_settings = psettings ? psettings : gtk_print_settings_new ();
psetup = gtk_page_setup_new_from_key_file (key_file,
"Page Setup",
NULL);
window->print_page_setup = psetup ? psetup : gtk_page_setup_new ();
job_name = g_key_file_get_string (key_file,
"Print Job", "title",
NULL);
if (job_name) {
window->print_job_title = job_name;
gtk_window_set_title (GTK_WINDOW (window), job_name);
} else {
window->print_job_title = g_strdup (_("Evince"));
}
if (print_settings && g_file_test (print_settings, G_FILE_TEST_IS_REGULAR)) {
GKeyFile *key_file;
GError *error = NULL;
key_file = g_key_file_new ();
g_key_file_load_from_file (key_file,
print_settings,
G_KEY_FILE_KEEP_COMMENTS |
G_KEY_FILE_KEEP_TRANSLATIONS,
&error);
if (!error) {
GtkPrintSettings *psettings;
GtkPageSetup *psetup;
gchar *job_name;
psettings = gtk_print_settings_new_from_key_file (key_file,
"Print Settings",
NULL);
window->print_settings = psettings ? psettings : gtk_print_settings_new ();
psetup = gtk_page_setup_new_from_key_file (key_file,
"Page Setup",
NULL);
window->print_page_setup = psetup ? psetup : gtk_page_setup_new ();
job_name = g_key_file_get_string (key_file,
"Print Job", "title",
NULL);
if (job_name) {
window->print_job_title = job_name;
gtk_window_set_title (GTK_WINDOW (window), job_name);
}
} else {
window->print_settings = gtk_print_settings_new ();
window->print_page_setup = gtk_page_setup_new ();
g_error_free (error);
}
g_key_file_free (key_file);
} else {
window->print_settings = gtk_print_settings_new ();
window->print_page_setup = gtk_page_setup_new ();
}
g_key_file_free (key_file);
return TRUE;
}
gboolean
ev_previewer_window_set_print_settings (EvPreviewerWindow *window,
const gchar *print_settings,
GError **error)
{
GMappedFile *file;
g_return_val_if_fail (EV_IS_PREVIEWER_WINDOW (window), FALSE);
g_return_val_if_fail (print_settings != NULL, FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
file = g_mapped_file_new (print_settings, FALSE, error);
if (file == NULL)
return FALSE;
return ev_previewer_window_set_print_settings_take_file (window, file, error);
}
gboolean
ev_previewer_window_set_print_settings_fd (EvPreviewerWindow *window,
int fd,
GError **error)
{
GMappedFile *file;
g_return_val_if_fail (EV_IS_PREVIEWER_WINDOW (window), FALSE);
g_return_val_if_fail (fd != -1, FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
file = g_mapped_file_new_from_fd (fd, FALSE, error);
if (file == NULL)
return FALSE;
return ev_previewer_window_set_print_settings_take_file (window, file, error);
}
void
ev_previewer_window_set_source_file (EvPreviewerWindow *window,
const gchar *source_file)
{
if (window->source_file)
g_free (window->source_file);
g_return_if_fail (EV_IS_PREVIEWER_WINDOW (window));
g_free (window->source_file);
window->source_file = g_strdup (source_file);
}
void
ev_previewer_window_set_source_fd (EvPreviewerWindow *window,
int fd)
{
g_return_if_fail (EV_IS_PREVIEWER_WINDOW (window));
if (window->source_fd != -1)
close (window->source_fd);
window->source_fd = fcntl (fd, F_DUPFD_CLOEXEC, 3);
}
void
ev_previewer_window_take_source_fd (EvPreviewerWindow *window,
int fd /* transfer full */)
{
g_return_if_fail (EV_IS_PREVIEWER_WINDOW (window));
if (window->source_fd != -1)
close (window->source_fd);
window->source_fd = fd;
}
EvDocumentModel *
ev_previewer_window_get_document_model (EvPreviewerWindow *window)
{
......
......@@ -39,14 +39,24 @@ typedef struct _EvPreviewerWindow EvPreviewerWindow;
typedef struct _EvPreviewerWindowClass EvPreviewerWindowClass;
GType ev_previewer_window_get_type (void) G_GNUC_CONST;
EvPreviewerWindow *ev_previewer_window_new (EvDocumentModel *model);
EvPreviewerWindow *ev_previewer_window_new (void);
EvDocumentModel *ev_previewer_window_get_document_model (EvPreviewerWindow *window);
void ev_previewer_window_set_print_settings (EvPreviewerWindow *window,
const gchar *print_settings);
void ev_previewer_window_set_source_file (EvPreviewerWindow *window,
const gchar *source_file);
void ev_previewer_window_set_job (EvPreviewerWindow *window,
EvJob *job);
gboolean ev_previewer_window_set_print_settings (EvPreviewerWindow *window,
const gchar *print_settings,
GError **error);
gboolean ev_previewer_window_set_print_settings_fd (EvPreviewerWindow *window,
int fd,
GError **error);
void ev_previewer_window_set_source_file (EvPreviewerWindow *window,
const gchar *source_file);
void ev_previewer_window_set_source_fd (EvPreviewerWindow *window,
int fd);
void ev_previewer_window_take_source_fd (EvPreviewerWindow *window,
int fd);
G_END_DECLS
......
......@@ -2,7 +2,7 @@
* this file is part of evince, a gnome document viewer
*
* Copyright (C) 2009 Carlos Garcia Campos <carlosgc@gnome.org>
* Copyright © 2012 Christian Persch
* Copyright © 2012, 2018 Christian Persch
*
* Evince is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
......@@ -21,6 +21,12 @@
#include "config.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <gtk/gtk.h>
#include <glib/gi18n.h>
#include <evince-document.h>
......@@ -38,12 +44,24 @@
#endif
static gboolean unlink_temp_file = FALSE;
static gchar *print_settings = NULL;
static int input_fd = -1;
static char *input_file = NULL;
static char *input_mime_type = NULL;
static int print_settings_fd = -1;
static gchar *print_settings_file = NULL;
static EvPreviewerWindow *window = NULL;
static const GOptionEntry goption_options[] = {
{ "unlink-tempfile", 'u', 0, G_OPTION_ARG_NONE, &unlink_temp_file, N_("Delete the temporary file"), NULL },
{ "print-settings", 'p', 0, G_OPTION_ARG_FILENAME, &print_settings, N_("File specifying print settings"), N_("FILE") },
{ "unlink-tempfile", 'u', 0, G_OPTION_ARG_NONE, &unlink_temp_file,
N_("Delete the temporary file"), NULL },
{ "print-settings", 'p', 0, G_OPTION_ARG_FILENAME, &print_settings_file,
N_("File specifying print settings"), N_("FILE") },
{ "fd", 0, 0, G_OPTION_ARG_INT, &input_fd,
N_("File descriptor of input file"), N_("FD") },
{ "mime-type", 0, 0, G_OPTION_ARG_STRING, &input_mime_type,
N_("MIME type of input file"), N_("TYPE") },
{ "print-settings-fd", 0, 0, G_OPTION_ARG_INT, &print_settings_fd,
N_("File descriptor of print settings file"), N_("FD") },
{ NULL }
};
......@@ -63,35 +81,6 @@ ev_previewer_unlink_tempfile (const gchar *filename)
g_object_unref (tempdir);
}
static void
ev_previewer_load_job_finished (EvJob *job,
EvDocumentModel *model)
{
if (ev_job_is_failed (job)) {
g_object_unref (job);
return;
}
ev_document_model_set_document (model, job->document);
g_object_unref (job);
}
static void
ev_previewer_load_document (GFile *file,
EvDocumentModel *model)
{
EvJob *job;
gchar *uri;
uri = g_file_get_uri (file);
job = ev_job_load_new (uri);
g_signal_connect (job, "finished",
G_CALLBACK (ev_previewer_load_job_finished),
model);
ev_job_scheduler_push_job (job, EV_JOB_PRIORITY_NONE);
g_free (uri);
}
static void
activate_cb (GApplication *application,
gpointer user_data)
......@@ -102,35 +91,140 @@ activate_cb (GApplication *application,
}
static void
open_cb (GApplication *application,
GFile **files,
gint n_files,
const gchar *hint,
gpointer user_data)
startup_cb (GApplication *application,
gpointer data)
{
EvDocumentModel *model;
GFile *file;
char *path;
EvJob *job;
GError *ps_error = NULL;
gboolean ps_ok = TRUE;
g_assert (input_fd != -1 || input_file != NULL);
if (n_files != 1) {
g_application_quit (application);
return;
window = ev_previewer_window_new ();
if (print_settings_fd != -1) {
ps_ok = ev_previewer_window_set_print_settings_fd (EV_PREVIEWER_WINDOW (window), print_settings_fd, &ps_error);
close (print_settings_fd);
print_settings_fd = -1;
} else if (print_settings_file != NULL) {
ps_ok = ev_previewer_window_set_print_settings (EV_PREVIEWER_WINDOW (window), print_settings_file, &ps_error);
g_free (print_settings_file);
print_settings_file = NULL;
}
if (!ps_ok) {
g_printerr ("Failed to load print settings: %s\n", ps_error->message);
g_error_free (ps_error);
}
file = files[0];
if (input_fd != -1) {
ev_previewer_window_set_source_fd (EV_PREVIEWER_WINDOW (window), input_fd);
job = ev_job_load_fd_new_take (input_fd, input_mime_type,
EV_DOCUMENT_LOAD_FLAG_NO_CACHE);
input_fd = -1;
} else {
GFile *file;
char *uri;
char *path
file = g_file_new_for_commandline_arg (input_file);
uri = g_file_get_uri (file);
path = g_file_get_path (file);
ev_previewer_window_set_source_file (EV_PREVIEWER_WINDOW (window), path);
job = ev_job_load_new (uri);
g_free (uri);
g_free (path);
g_object_unref (file);
g_free (input_file);
input_file = NULL;
}
model = ev_document_model_new ();
ev_previewer_load_document (file, model);
ev_previewer_window_set_job (window, job);
g_object_unref (job);
window = ev_previewer_window_new (model);
g_object_unref (model);
/* Window will be presented by 'activate' signal */
}
ev_previewer_window_set_print_settings (EV_PREVIEWER_WINDOW (window), print_settings);
path = g_file_get_path (file);
ev_previewer_window_set_source_file (EV_PREVIEWER_WINDOW (window), path);
g_free (path);
static gboolean
check_arguments (int argc,
char** argv,
GError **error)
{
if (input_fd != -1) {
struct stat statbuf;
int flags;
if (fstat (input_fd, &statbuf) == -1 ||
(flags = fcntl (input_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));
return FALSE;
}
if (!S_ISREG (statbuf.st_mode)) {
g_set_error_literal (error, G_FILE_ERROR, G_FILE_ERROR_BADF,
"Not a regular file.");
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.");
return FALSE;
}
if (argc > 1) {
g_set_error_literal (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
"Too many arguments");
return FALSE;
}
if (input_mime_type == NULL) {
g_set_error_literal (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
"Must specify --mime-type");
return FALSE;
}
if (unlink_temp_file) {
g_set_error_literal (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
"Must not specify --unlink-tempfile");
return FALSE;
}
} else {
char *path;
if (argc != 2) {
g_set_error_literal (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
"Need exactly one argument");
return FALSE;
}
if (input_mime_type != NULL) {
g_set_error_literal (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
"Must not specify --mime-type");
return FALSE;
}
path = g_filename_from_uri (argv[1], NULL, NULL);
if (!g_file_test (argv[1], G_FILE_TEST_IS_REGULAR) && !g_file_test (path, G_FILE_TEST_IS_REGULAR)) {
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_NOENT,
"File \"%s\" does not exist or is not a regular file\n", argv[1]);
g_free (path);
return FALSE;
}
g_free (path);
input_file = g_strdup (argv[1]);
}
gtk_window_present (GTK_WINDOW (window));
return TRUE;
}
gint
......@@ -139,7 +233,6 @@ main (gint argc, gchar **argv)
GtkApplication *application;
GOptionContext *context;
GError *error = NULL;
gchar *path;
int status = 1;
const gchar *action_accels[] = {
......@@ -185,13 +278,16 @@ main (gint argc, gchar **argv)
textdomain (GETTEXT_PACKAGE);
#endif
g_set_prgname ("evince-previewer");
context = g_option_context_new (_("GNOME Document Previewer"));
g_option_context_set_translation_domain (context, GETTEXT_PACKAGE);
g_option_context_add_main_entries (context, goption_options, GETTEXT_PACKAGE);
g_option_context_add_group (context, gtk_get_option_group (TRUE));
if (!g_option_context_parse (context, &argc, &argv, &error)) {
if (!g_option_context_parse (context, &argc, &argv, &error) ||