New CPDB print backend for GTK Print Dialog
authortinytrebuchet <tinytrebuchet@protonmail.com>
Thu, 9 Feb 2023 18:17:02 +0000 (23:47 +0530)
committertinytrebuchet <tinytrebuchet@protonmail.com>
Fri, 10 Feb 2023 19:43:48 +0000 (01:13 +0530)
The Common Print Dialog Backends (CPDB) concept has GUI-toolkit-independent
backends for each print technology (CUPS, Print to File, cloud printing
services, ...) and each print dialog (GTK, Qt, Chromium, ...) is supposed
to use this backend, so that changes in print technologies can be centrally
and quickly covered by changing the backends and everything new gets available
in all print dialogs.

This commit provides a GTK print dialog backend to add support for the CPDB
concept. It communicates with all installed CPDB backends and so gives support
for all these print technologies to the GTK print dialog.

To make use of CPDB the GTK print dialog is supposed to be installed with this
backend and the 'Print To File' backend, and not any others to prevent printer
duplication.

meson_options.txt
modules/printbackends/gtkprintbackendcpdb.c [new file with mode: 0644]
modules/printbackends/gtkprintbackendcpdb.h [new file with mode: 0644]
modules/printbackends/gtkprintbackendcups.c
modules/printbackends/gtkprintbackendutils.c [new file with mode: 0644]
modules/printbackends/gtkprintbackendutils.h [new file with mode: 0644]
modules/printbackends/gtkprintercpdb.c [new file with mode: 0644]
modules/printbackends/gtkprintercpdb.h [new file with mode: 0644]
modules/printbackends/meson.build

index 5fd41258c3349f80655034753768dae1d2df5b1f..9f608e9e35fcadd3f2cfddd626477cb68c72a9d4 100644 (file)
@@ -41,6 +41,11 @@ option('media-gstreamer',
 
 # Print backends
 
+option('print-cpdb',
+       type: 'feature',
+       value: 'disabled',
+       description : 'Build the cpdb print backend')
+
 option('print-cups',
        type: 'feature',
        value: 'auto',
diff --git a/modules/printbackends/gtkprintbackendcpdb.c b/modules/printbackends/gtkprintbackendcpdb.c
new file mode 100644 (file)
index 0000000..77498cc
--- /dev/null
@@ -0,0 +1,1692 @@
+/* GTK - The GIMP Toolkit
+ * gtkprintbackendcpdb.h: Default implementation of GtkPrintBackend
+ * for the Common Print Dialog Backends (CPDB)
+ * Copyright (C) 2022, 2023 TinyTrebuchet <tinytrebuchet@protonmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include <config.h>
+#include <cpdb/frontend.h>
+
+#include <gtk/gtkprivate.h>
+#include "gtkprintbackendcpdb.h"
+#include "gtkprintbackendutils.h"
+
+#include <cairo.h>
+#include <cairo-pdf.h>
+
+#include <glib/gi18n-lib.h>
+#include <gtkprintercpdb.h>
+
+#define GTK_PRINT_BACKEND_CPDB_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_PRINT_BACKEND_CPDB, GtkPrintBackendCpdbClass))
+#define GTK_IS_PRINT_BACKEND_CPDB_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_PRINT_BACKEND_CPDB))
+#define GTK_PRINT_BACKEND_CPDB_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_PRINT_BACKEND_CPDB, GtkPrintBackendCpdbClass))
+
+/*
+ * Multiplier for converting points to mm
+ */
+#define points_multiplier 2.83464567
+
+#define _CPDB_MAX_CHUNK_SIZE 8192
+
+
+
+static void gtk_print_backend_cpdb_finalize                   (GObject                    *object);
+
+static void cpdb_request_printer_list                         (GtkPrintBackend            *backend);
+
+static void cpdb_printer_request_details                      (GtkPrinter                 *printer);
+
+static void cpdb_acquire_details_cb                           (cpdb_printer_obj_t         *cpdb_printer_obj,
+                                                               int                         success,
+                                                               gpointer                    user_data);
+
+static GtkPrintCapabilities cpdb_printer_get_capabilities     (GtkPrinter                 *printer);
+
+static GtkPrinterOptionSet *cpdb_printer_get_options          (GtkPrinter                 *printer,
+                                                               GtkPrintSettings           *settings,
+                                                               GtkPageSetup               *page_setup,
+                                                               GtkPrintCapabilities        capabilities);
+
+static GtkPageSetup *cpdb_get_gtk_page_setup                  (cpdb_printer_obj_t         *printer_obj,
+                                                               const char                 *media);
+static GList *cpdb_printer_list_papers                        (GtkPrinter                 *printer);
+static GtkPageSetup *cpdb_printer_get_default_page_size       (GtkPrinter                 *printer);
+
+static gboolean cpdb_printer_get_hard_margins                 (GtkPrinter                 *printer,
+                                                               double                     *top,
+                                                               double                     *bottom,
+                                                               double                     *left,
+                                                               double                     *right);
+static gboolean cpdb_printer_get_hard_margins_for_paper_size  (GtkPrinter                 *printer,
+                                                               GtkPaperSize               *paper_size,
+                                                               double                     *top,
+                                                               double                     *bottom,
+                                                               double                     *left,
+                                                               double                     *right);
+
+static void cpdb_printer_get_settings_from_options            (GtkPrinter                 *printer,
+                                                               GtkPrinterOptionSet        *options,
+                                                               GtkPrintSettings           *settings);
+
+static void cpdb_printer_prepare_for_print                    (GtkPrinter                 *printer,
+                                                               GtkPrintJob                *print_job,
+                                                               GtkPrintSettings           *settings,
+                                                               GtkPageSetup               *page_setup);
+
+static void cpdb_print_cb                                     (GtkPrintBackendCpdb        *cpdb_backend,
+                                                               GError                     *error,
+                                                               gpointer                    user_data);
+
+static gboolean cpdb_write                                    (GIOChannel                 *source,
+                                                               GIOCondition                con,
+                                                               gpointer                    user_data);
+
+static void cpdb_print_stream                                 (GtkPrintBackend            *backend,
+                                                               GtkPrintJob                *job,
+                                                               GIOChannel                 *data_io,
+                                                               GtkPrintJobCompleteFunc     callback,
+                                                               gpointer                    user_data,
+                                                               GDestroyNotify              dnotify);
+
+static void gtk_printer_cpdb_configure_page_setup             (GtkPrinter                 *printer,
+                                                               GtkPageSetup               *page_setup,
+                                                               GtkPrintSettings           *settings);
+
+static void gtk_printer_cpdb_configure_settings                      (const char                 *key,
+                                                               const char                 *value,
+                                                               gpointer                    user_data);
+
+static cairo_surface_t *cpdb_printer_create_cairo_surface     (GtkPrinter                 *printer,
+                                                               GtkPrintSettings           *settings,
+                                                               double                      width,
+                                                               double                      height,
+                                                               GIOChannel                 *cache_io);
+
+static void cpdb_fill_gtk_option                              (GtkPrinterOption           *gtk_option,
+                                                               cpdb_option_t              *cpdb_option,
+                                                               cpdb_printer_obj_t         *printer_obj);
+
+static void printer_updates_callback                          (cpdb_frontend_obj_t        *frontend_obj,
+                                                               cpdb_printer_obj_t         *printer_obj,
+                                                               cpdb_printer_update_t       change);
+
+static void add_option_to_settings                            (GtkPrinterOption           *option,
+                                                               gpointer                    user_data);
+
+static void cpdb_printer_add_hash_table                       (gpointer                    key,
+                                                               gpointer                    value,
+                                                               gpointer                    user_data);
+
+static void cpdb_add_gtk_printer                              (GtkPrintBackend            *backend,
+                                                               cpdb_printer_obj_t         *printer_obj);
+
+static void cpdb_remove_gtk_printer                           (GtkPrintBackend            *backend,
+                                                               cpdb_printer_obj_t         *printer_obj);
+
+static void set_state_message                                 (GtkPrinter                 *printer);
+
+static char *cpdb_option_translation                          (cpdb_printer_obj_t         *printer_obj,
+                                                               const char                 *option_name);
+static char *cpdb_choice_translation                          (cpdb_printer_obj_t         *printer_obj,
+                                                               const char                 *option_name,
+                                                               const char                 *choice_name);
+
+static void initialize                                        (void);
+static gchar *get_gtk_group                                   (cpdb_printer_obj_t         *printer_obj,
+                                                               const char                 *group_name);
+
+
+/*
+ * List of locales for text translation
+ */
+static const gchar* const* locales = NULL;
+
+/*
+ * Mark the options which have already been displayed in other tabs,
+ * and exclude them from the "Advanced" tab, while getting printer options
+ */
+static GHashTable *already_used_options = NULL;
+
+
+struct _GtkPrintBackendCpdbClass
+{
+  GtkPrintBackendClass parent_class;
+};
+
+struct _GtkPrintBackendCpdb
+{
+  GtkPrintBackend parent_instance;
+  cpdb_frontend_obj_t *frontend_obj;
+};
+
+typedef struct {
+  GtkPrintBackend *backend;
+  GtkPrintJobCompleteFunc callback;
+  GtkPrintJob *job;
+  char *path;
+  GIOStream *target_io_stream;
+  gpointer user_data;
+  GDestroyNotify dnotify;
+} _PrintStreamData;
+
+
+G_DEFINE_DYNAMIC_TYPE (GtkPrintBackendCpdb, gtk_print_backend_cpdb, GTK_TYPE_PRINT_BACKEND)
+
+/*
+ * GtkPrintBackend object for currently opened print dialog
+ */
+static GtkPrintBackend *gtk_print_backend = NULL;
+
+
+G_MODULE_EXPORT
+void
+g_io_module_load (GIOModule *module)
+{
+  g_type_module_use (G_TYPE_MODULE (module));
+
+  gtk_print_backend_cpdb_register_type (G_TYPE_MODULE (module));
+  gtk_printer_cpdb_register_type (G_TYPE_MODULE (module));
+
+  g_io_extension_point_implement (GTK_PRINT_BACKEND_EXTENSION_POINT_NAME,
+                                  GTK_TYPE_PRINT_BACKEND_CPDB,
+                                  "cpdb",
+                                  10);
+}
+
+G_MODULE_EXPORT
+void
+g_io_module_unload (GIOModule *module)
+{
+}
+
+G_MODULE_EXPORT
+char **
+g_io_module_query (void)
+{
+  char *eps[] = {
+    (char *)GTK_PRINT_BACKEND_EXTENSION_POINT_NAME,
+    NULL
+  };
+
+  return g_strdupv (eps);
+}
+
+/*
+ * GtkPrintBackendCpdb
+ */
+
+/*
+ * gtk_print_backend_cpdb_new:
+ *
+ * Creates a new #GtkPrintBackendCpdb object. #GtkPrintBackendCpdb
+ * implements the #GtkPrintBackend interface with direct access to
+ * the filesystem using Unix/Linux API calls
+ *
+ * Returns: the new #GtkPrintBackendCpdb object
+ */
+GtkPrintBackend *
+gtk_print_backend_cpdb_new (void)
+{
+  GTK_DEBUG (PRINTING, "CPDB Backend: Creating a new CPDB print backend object");
+
+  return g_object_new (GTK_TYPE_PRINT_BACKEND_CPDB, NULL);
+}
+
+
+static void
+gtk_print_backend_cpdb_class_init (GtkPrintBackendCpdbClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  GtkPrintBackendClass *backend_class = GTK_PRINT_BACKEND_CLASS (klass);
+
+  gobject_class->finalize = gtk_print_backend_cpdb_finalize;
+
+  backend_class->request_printer_list = cpdb_request_printer_list;
+  backend_class->printer_request_details = cpdb_printer_request_details;
+  backend_class->printer_get_capabilities = cpdb_printer_get_capabilities;
+  backend_class->printer_get_options = cpdb_printer_get_options;
+  backend_class->printer_list_papers = cpdb_printer_list_papers;
+  backend_class->printer_get_default_page_size = cpdb_printer_get_default_page_size;
+  backend_class->printer_get_hard_margins = cpdb_printer_get_hard_margins;
+  backend_class->printer_get_hard_margins_for_paper_size = cpdb_printer_get_hard_margins_for_paper_size;
+  backend_class->printer_get_settings_from_options = cpdb_printer_get_settings_from_options;
+  backend_class->printer_prepare_for_print = cpdb_printer_prepare_for_print;
+  backend_class->printer_create_cairo_surface = cpdb_printer_create_cairo_surface;
+  backend_class->print_stream = cpdb_print_stream;
+}
+
+static void
+gtk_print_backend_cpdb_class_finalize (GtkPrintBackendCpdbClass *class)
+{
+}
+
+static void
+gtk_print_backend_cpdb_init (GtkPrintBackendCpdb *backend_cpdb)
+{
+  char *tmp, *instance_name;
+
+  initialize ();
+
+  /*
+   * Initialize the FrontendObj with a random instance name to
+   * prevent conflicts with print dialogs opened from other programs
+   */
+  tmp = random_string (4);
+  instance_name = g_strdup_printf ("Gtk_%s", tmp);
+  GTK_DEBUG (PRINTING, "Creating frontendObj for CPDB backend: %s", instance_name);
+  backend_cpdb->frontend_obj = cpdbGetNewFrontendObj (instance_name,
+                                                      (cpdb_printer_callback) printer_updates_callback);
+  cpdbIgnoreLastSavedSettings (backend_cpdb->frontend_obj);
+  gtk_print_backend = GTK_PRINT_BACKEND (backend_cpdb);
+  g_free (tmp);
+  g_free (instance_name);
+}
+
+static void
+gtk_print_backend_cpdb_finalize (GObject *object)
+{
+  GtkPrintBackendCpdb *backend_cpdb;
+  GObjectClass *backend_parent_class;
+
+  GTK_DEBUG (PRINTING, "Finalizing CPDB backend object");
+
+  backend_cpdb = GTK_PRINT_BACKEND_CPDB (object);
+  backend_parent_class = G_OBJECT_CLASS (gtk_print_backend_cpdb_parent_class);
+  cpdbDeleteFrontendObj (backend_cpdb->frontend_obj);
+  gtk_print_backend = NULL;
+
+  backend_parent_class->finalize (object);
+}
+
+/*
+ * This function is responsible for displaying the printer
+ * list obtained from CPDB backend on the print dialog.
+ */
+static void
+cpdb_request_printer_list (GtkPrintBackend *backend)
+{
+  GtkPrintBackendCpdb *backend_cpdb = GTK_PRINT_BACKEND_CPDB (backend);
+
+  cpdbConnectToDBus (backend_cpdb->frontend_obj);
+  g_hash_table_foreach (backend_cpdb->frontend_obj->printer,
+                        cpdb_printer_add_hash_table,
+                        backend);
+  gtk_print_backend_set_list_done (backend);
+}
+
+/*
+ * This function is responsible for making a printer acquire all
+ * the details and supported options list, which need not be queried
+ * until the user clicks on the printer in the printer list in the dialog.
+ * The print dialog runs this function asynchronously and displays
+ * "Getting printer attributes..." as the printer status meanwhile.
+ * This function is needed as some printers (like temporary CUPS queue)
+ * can take a couple of seconds to materialize and acquire all the details,
+ * and it's important the dialog doesn't block during that time.
+ */
+static void
+cpdb_printer_request_details (GtkPrinter *printer)
+{
+  GtkPrinterCpdb *printer_cpdb = GTK_PRINTER_CPDB (printer);
+  cpdb_printer_obj_t *printer_obj = gtk_printer_cpdb_get_printer_obj (printer_cpdb);
+
+  cpdbAcquireDetails (printer_obj, cpdb_acquire_details_cb, printer);
+}
+
+static void
+cpdb_acquire_details_cb (cpdb_printer_obj_t   *printer_obj,
+                         int                  success,
+                         gpointer             user_data)
+{
+  GtkPrinter *printer = GTK_PRINTER (user_data);
+  GtkPrintBackend *backend = gtk_printer_get_backend (printer);
+  gboolean accepting_jobs, paused, status_changed;
+
+  if (success == 0)
+    {
+      GTK_DEBUG (PRINTING, "Error acquiring printer details");
+      g_signal_emit_by_name (printer, "details-acquired", FALSE);
+      return;
+    }
+
+  accepting_jobs = cpdbIsAcceptingJobs (printer_obj);
+  paused = g_strcmp0 (cpdbGetState (printer_obj), "stopped") == 0;
+  status_changed = paused ^ gtk_printer_is_paused (printer);
+
+  gtk_printer_set_is_accepting_jobs (printer, accepting_jobs);
+  gtk_printer_set_is_paused (printer, paused);
+  set_state_message (printer);
+
+  gtk_printer_set_has_details (printer, TRUE);
+  g_signal_emit_by_name (printer, "details-acquired", TRUE);
+
+  if (status_changed)
+    g_signal_emit_by_name (backend, "printer-status-changed", printer);
+}
+
+
+/*
+ * This function is responsible for specifying which features the
+ * print dialog should offer for the given printer.
+ */
+static GtkPrintCapabilities
+cpdb_printer_get_capabilities (GtkPrinter *printer)
+{
+  GtkPrintCapabilities capabilities = 0;
+  cpdb_option_t *cpdb_option;
+  GtkPrinterCpdb *printer_cpdb = GTK_PRINTER_CPDB (printer);
+  cpdb_printer_obj_t *printer_obj = gtk_printer_cpdb_get_printer_obj (printer_cpdb);
+
+  cpdb_option = cpdbGetOption (printer_obj, CPDB_OPTION_PAGE_SET);
+  if (cpdb_option != NULL && cpdb_option->num_supported >= 3)
+    capabilities |= GTK_PRINT_CAPABILITY_PAGE_SET;
+
+  cpdb_option = cpdbGetOption (printer_obj, CPDB_OPTION_COPIES);
+  if (cpdb_option != NULL &&
+      g_strcmp0 (cpdb_option->supported_values[0], "1") != 0 &&
+      g_strcmp0 (cpdb_option->supported_values[0], "1-1") != 0)
+    capabilities |= GTK_PRINT_CAPABILITY_COPIES;
+
+  cpdb_option = cpdbGetOption (printer_obj, CPDB_OPTION_COLLATE);
+  if (cpdb_option != NULL && cpdb_option->num_supported > 1)
+    capabilities |= GTK_PRINT_CAPABILITY_COLLATE;
+
+  cpdb_option = cpdbGetOption (printer_obj, CPDB_OPTION_PAGE_DELIVERY);
+  if (cpdb_option != NULL && cpdb_option->num_supported > 1)
+    capabilities |= GTK_PRINT_CAPABILITY_REVERSE;
+
+  cpdb_option = cpdbGetOption (printer_obj, CPDB_OPTION_PRINT_SCALING);
+  if (cpdb_option != NULL && cpdb_option->num_supported > 1)
+    capabilities |= GTK_PRINT_CAPABILITY_SCALE;
+
+  cpdb_option = cpdbGetOption (printer_obj, CPDB_OPTION_NUMBER_UP);
+  if (cpdb_option != NULL && cpdb_option->num_supported > 1)
+    capabilities |= GTK_PRINT_CAPABILITY_NUMBER_UP;
+
+  cpdb_option = cpdbGetOption (printer_obj, CPDB_OPTION_NUMBER_UP_LAYOUT);
+  if (cpdb_option != NULL && cpdb_option->num_supported > 1)
+    capabilities |= GTK_PRINT_CAPABILITY_NUMBER_UP_LAYOUT;
+
+  return capabilities;
+}
+
+
+/*
+ * This function is responsible for getting all the options
+ * that the printer supports and display them into the
+ * GUI template of GTK+ print dialog box.
+ * The backend should obtain whatever options it supports,
+ * from either its print server or PPDs.
+ */
+static GtkPrinterOptionSet *
+cpdb_printer_get_options (GtkPrinter            *printer,
+                          GtkPrintSettings      *settings,
+                          GtkPageSetup          *page_setup,
+                          GtkPrintCapabilities  capabilities)
+{
+  char *option_name;
+  char *display_name;
+  gpointer key, value;
+  GHashTableIter iter;
+  GtkPrinterCpdb *printer_cpdb;
+  cpdb_printer_obj_t *printer_obj;
+  cpdb_option_t *cpdb_option;
+  GtkPrinterOption *gtk_option;
+  GtkPrinterOptionSet *gtk_option_set;
+
+  gtk_option_set = gtk_printer_option_set_new ();
+  printer_cpdb = GTK_PRINTER_CPDB (printer);
+  printer_obj = gtk_printer_cpdb_get_printer_obj (printer_cpdb);
+
+  /** Page-Setup **/
+  cpdb_option = cpdbGetOption (printer_obj, CPDB_OPTION_NUMBER_UP);
+  if ((capabilities & GTK_PRINT_CAPABILITY_NUMBER_UP) && cpdb_option != NULL)
+    {
+      display_name = cpdb_option_translation (printer_obj,
+                                              cpdb_option->option_name);
+      gtk_option = gtk_printer_option_new ("gtk-n-up",
+                                           display_name,
+                                           GTK_PRINTER_OPTION_TYPE_PICKONE);
+      cpdb_fill_gtk_option (gtk_option, cpdb_option, printer_obj);
+      gtk_printer_option_set_add (gtk_option_set, gtk_option);
+      g_object_unref (gtk_option);
+    }
+
+  cpdb_option = cpdbGetOption (printer_obj, CPDB_OPTION_NUMBER_UP_LAYOUT);
+  if ((capabilities & GTK_PRINT_CAPABILITY_NUMBER_UP_LAYOUT) && cpdb_option != NULL)
+    {
+      display_name = cpdb_option_translation (printer_obj,
+                                              cpdb_option->option_name);
+      gtk_option = gtk_printer_option_new ("gtk-n-up-layout",
+                                           display_name,
+                                           GTK_PRINTER_OPTION_TYPE_PICKONE);
+      cpdb_fill_gtk_option (gtk_option, cpdb_option, printer_obj);
+      gtk_printer_option_set_add (gtk_option_set, gtk_option);
+      g_object_unref (gtk_option);
+    }
+
+  cpdb_option = cpdbGetOption (printer_obj, CPDB_OPTION_SIDES);
+  if (cpdb_option != NULL && cpdb_option->num_supported > 1)
+    {
+      display_name = cpdb_option_translation (printer_obj,
+                                              cpdb_option->option_name);
+      gtk_option = gtk_printer_option_new ("gtk-duplex",
+                                           display_name,
+                                           GTK_PRINTER_OPTION_TYPE_PICKONE);
+      cpdb_fill_gtk_option (gtk_option, cpdb_option, printer_obj);
+      gtk_printer_option_set_add (gtk_option_set, gtk_option);
+      g_object_unref (gtk_option);
+    }
+
+  cpdb_option = cpdbGetOption (printer_obj, CPDB_OPTION_MEDIA_SOURCE);
+  if (cpdb_option != NULL && cpdb_option->num_supported > 1)
+    {
+      display_name = cpdb_option_translation (printer_obj,
+                                              cpdb_option->option_name);
+      gtk_option = gtk_printer_option_new ("gtk-paper-source",
+                                           display_name,
+                                           GTK_PRINTER_OPTION_TYPE_PICKONE);
+      cpdb_fill_gtk_option (gtk_option, cpdb_option, printer_obj);
+      gtk_printer_option_set_add (gtk_option_set, gtk_option);
+      g_object_unref (gtk_option);
+    }
+
+  cpdb_option = cpdbGetOption (printer_obj, CPDB_OPTION_MEDIA_TYPE);
+  if (cpdb_option != NULL && cpdb_option->num_supported > 1)
+    {
+      display_name = cpdb_option_translation (printer_obj,
+                                              cpdb_option->option_name);
+      gtk_option = gtk_printer_option_new ("gtk-paper-type",
+                                           display_name,
+                                           GTK_PRINTER_OPTION_TYPE_PICKONE);
+      cpdb_fill_gtk_option (gtk_option, cpdb_option, printer_obj);
+      gtk_printer_option_set_add (gtk_option_set, gtk_option);
+      g_object_unref (gtk_option);
+    }
+
+  cpdb_option = cpdbGetOption (printer_obj, CPDB_OPTION_OUTPUT_BIN);
+  if (cpdb_option != NULL && cpdb_option->num_supported > 1)
+    {
+      display_name = cpdb_option_translation (printer_obj,
+                                              cpdb_option->option_name);
+      gtk_option = gtk_printer_option_new ("gtk-output-tray",
+                                           display_name,
+                                           GTK_PRINTER_OPTION_TYPE_PICKONE);
+      cpdb_fill_gtk_option (gtk_option, cpdb_option, printer_obj);
+      gtk_printer_option_set_add (gtk_option_set, gtk_option);
+      g_object_unref (gtk_option);
+    }
+
+
+  /** Jobs **/
+  cpdb_option = cpdbGetOption (printer_obj, CPDB_OPTION_JOB_PRIORITY);
+  if (cpdb_option != NULL)
+    {
+      /* job-priority is represented as a number from 1-100 */
+      const char *prio[] = {"100", "80", "50", "30"};
+      const char *prio_display[] = {N_("Urgent"), N_("High"), N_("Medium"), N_("Low")};
+
+      for (int i = 0; i < G_N_ELEMENTS(prio_display); i++)
+        prio_display[i] = _(prio_display[i]);
+      display_name = cpdb_option_translation (printer_obj,
+                                              cpdb_option->option_name);
+      gtk_option = gtk_printer_option_new ("gtk-job-prio",
+                                           display_name,
+                                           GTK_PRINTER_OPTION_TYPE_PICKONE);
+      gtk_printer_option_choices_from_array (gtk_option,
+                                             G_N_ELEMENTS (prio),
+                                             prio, prio_display);
+      gtk_printer_option_set (gtk_option, "50");
+      gtk_printer_option_set_add (gtk_option_set, gtk_option);
+      g_object_unref (gtk_option);
+    }
+
+  cpdb_option = cpdbGetOption (printer_obj, CPDB_OPTION_JOB_SHEETS);
+  if (cpdb_option != NULL && cpdb_option->num_supported > 1)
+    {
+      gtk_option = gtk_printer_option_new ("gtk-cover-before",
+                                           C_("printer option", "Before"),
+                                           GTK_PRINTER_OPTION_TYPE_PICKONE);
+      cpdb_fill_gtk_option (gtk_option, cpdb_option, printer_obj);
+      gtk_printer_option_set_add (gtk_option_set, gtk_option);
+      g_object_unref (gtk_option);
+
+      gtk_option = gtk_printer_option_new ("gtk-cover-after",
+                                           C_("printer option", "After"),
+                                           GTK_PRINTER_OPTION_TYPE_PICKONE);
+
+      cpdb_fill_gtk_option (gtk_option, cpdb_option, printer_obj);
+      gtk_printer_option_set_add (gtk_option_set, gtk_option);
+      g_object_unref (gtk_option);
+    }
+
+  cpdb_option = cpdbGetOption (printer_obj, CPDB_OPTION_BILLING_INFO);
+  if (cpdb_option != NULL)
+    {
+      display_name = cpdb_option_translation (printer_obj,
+                                              cpdb_option->option_name);
+      gtk_option = gtk_printer_option_new ("gtk-billing-info",
+                                          display_name,
+                                          GTK_PRINTER_OPTION_TYPE_STRING);
+      gtk_printer_option_set (gtk_option, "");
+      gtk_printer_option_set_add (gtk_option_set, gtk_option);
+      g_object_unref (gtk_option);
+    }
+
+  const char *print_at[] = {"now", "at", "on-hold"};
+  gtk_option = gtk_printer_option_new ("gtk-print-time",
+                                       _("Print at"),
+                                       GTK_PRINTER_OPTION_TYPE_PICKONE);
+  gtk_printer_option_choices_from_array (gtk_option,
+                                         G_N_ELEMENTS (print_at),
+                                         print_at, print_at);
+  gtk_printer_option_set (gtk_option, "now");
+  gtk_printer_option_set_add (gtk_option_set, gtk_option);
+  g_object_unref (gtk_option);
+
+  gtk_option = gtk_printer_option_new ("gtk-print-time-text",
+                                       _("Print at time"),
+                                       GTK_PRINTER_OPTION_TYPE_STRING);
+  gtk_printer_option_set (gtk_option, "");
+  gtk_printer_option_set_add (gtk_option_set, gtk_option);
+  g_object_unref (gtk_option);
+
+  /** Other options **/
+  g_hash_table_iter_init (&iter, printer_obj->options->table);
+  while (g_hash_table_iter_next (&iter, &key, &value))
+    {
+      option_name = (char *) key;
+      if (g_hash_table_contains (already_used_options, option_name))
+          continue;
+
+      cpdb_option = (cpdb_option_t *) value;
+      if (cpdb_option->num_supported <= 1)
+        continue;
+
+      display_name = cpdb_option_translation (printer_obj,
+                                              cpdb_option->option_name);
+      gtk_option = gtk_printer_option_new (cpdb_option->option_name,
+                                           display_name,
+                                           GTK_PRINTER_OPTION_TYPE_PICKONE);
+      cpdb_fill_gtk_option (gtk_option, cpdb_option, printer_obj);
+      gtk_option->group = get_gtk_group (printer_obj,
+                                         cpdb_option->group_name);
+      gtk_printer_option_set_add (gtk_option_set, gtk_option);
+      g_object_unref (gtk_option);
+    }
+
+  /*
+   * Check if borderles printing is supported
+   */
+  gboolean borderless = TRUE;
+  const char *attrs[] = {CPDB_OPTION_MARGIN_TOP, CPDB_OPTION_MARGIN_BOTTOM,
+                         CPDB_OPTION_MARGIN_LEFT, CPDB_OPTION_MARGIN_RIGHT};
+  for (int i = 0; i < 4; i++)
+    {
+      cpdb_option = cpdbGetOption (printer_obj, (gchar *) attrs[i]);
+      gboolean found = FALSE;
+
+      if (cpdb_option != NULL)
+        {
+          for (int j = 0; j < cpdb_option->num_supported; j++)
+            {
+              if (g_strcmp0 (cpdb_option->supported_values[j], "0") == 0)
+                {
+                  found = TRUE;
+                  break;
+                }
+            }
+        }
+
+      if (!found)
+        {
+          borderless = FALSE;
+          break;
+        }
+    }
+
+  if (borderless)
+    {
+      gtk_option = gtk_printer_option_new ("borderless",
+                                           C_("print option", "Borderless"),
+                                           GTK_PRINTER_OPTION_TYPE_BOOLEAN);
+      gtk_option->group = get_gtk_group (printer_obj, CPDB_GROUP_MEDIA);
+      gtk_printer_option_set_add (gtk_option_set, gtk_option);
+      g_object_unref (gtk_option);
+    }
+
+  return gtk_option_set;
+}
+
+/*
+ * Helper function to create GtkPageSetup from given "media" size
+ */
+static GtkPageSetup *
+cpdb_get_gtk_page_setup (cpdb_printer_obj_t *printer_obj,
+                         const char *media)
+{
+  char *display_name;
+  int width, height, num_margins, ok;
+  cpdb_margin_t *margins;
+  GtkPaperSize *paper_size;
+  GtkPageSetup *page_setup;
+
+  page_setup = gtk_page_setup_new ();
+  display_name = cpdb_choice_translation (printer_obj,
+                                          CPDB_OPTION_MEDIA,
+                                          media);
+  ok = cpdbGetMediaSize (printer_obj,
+                         media,
+                         &width,
+                         &height);
+  if (ok == 1)
+    {
+      paper_size = gtk_paper_size_new_custom (media,
+                                              display_name,
+                                              width / 100.0,
+                                              height / 100.0,
+                                              GTK_UNIT_MM);
+      gtk_page_setup_set_paper_size (page_setup, paper_size);
+      gtk_paper_size_free (paper_size);
+    }
+  num_margins = cpdbGetMediaMargins (printer_obj,
+                                     media,
+                                     &margins);
+  if (num_margins > 0)
+    {
+      gtk_page_setup_set_left_margin   (page_setup,
+                                        margins[0].left / 100.0,
+                                        GTK_UNIT_MM);
+      gtk_page_setup_set_right_margin  (page_setup,
+                                        margins[0].right / 100.0,
+                                        GTK_UNIT_MM);
+      gtk_page_setup_set_top_margin    (page_setup,
+                                        margins[0].top / 100.0,
+                                        GTK_UNIT_MM);
+      gtk_page_setup_set_bottom_margin (page_setup,
+                                        margins[0].bottom / 100.0,
+                                        GTK_UNIT_MM);
+    }
+  return page_setup;
+}
+
+/*
+ * This function is responsible for listing all the page sizes supported by a printer
+ */
+static GList *
+cpdb_printer_list_papers (GtkPrinter *printer)
+{
+  cpdb_option_t *media;
+  GList *result = NULL;
+  GtkPageSetup *page_setup;
+
+  GtkPrinterCpdb *printer_cpdb = GTK_PRINTER_CPDB (printer);
+  cpdb_printer_obj_t *printer_obj = gtk_printer_cpdb_get_printer_obj (printer_cpdb);
+
+  media = cpdbGetOption (printer_obj, CPDB_OPTION_MEDIA);
+  if (media == NULL)
+    return NULL;
+
+  for (int i = 0; i < media->num_supported; i++)
+    {
+      if (g_str_has_prefix (media->supported_values[i], "custom_min") ||
+          g_str_has_prefix (media->supported_values[i], "custom_max"))
+        continue;
+
+      page_setup = cpdb_get_gtk_page_setup (printer_obj,
+                                            media->supported_values[i]);
+      result = g_list_prepend (result, page_setup);
+    }
+  result = g_list_reverse (result);
+  return result;
+}
+
+/*
+ * This function is responsible for getting the default page size for a printer
+ */
+static GtkPageSetup *
+cpdb_printer_get_default_page_size (GtkPrinter *printer)
+{
+  char *default_media;
+  GtkPageSetup *page_setup = NULL;
+
+  GtkPrinterCpdb *printer_cpdb = GTK_PRINTER_CPDB (printer);
+  cpdb_printer_obj_t *printer_obj = gtk_printer_cpdb_get_printer_obj (printer_cpdb);
+
+  default_media = cpdbGetDefault (printer_obj, CPDB_OPTION_MEDIA);
+  if (default_media != NULL)
+    {
+      page_setup = cpdb_get_gtk_page_setup (printer_obj, default_media);
+    }
+
+  return page_setup;
+}
+
+/*
+ * This function is responsible for getting the
+ * default page size margins supported by a printer
+ */
+static gboolean
+cpdb_printer_get_hard_margins (GtkPrinter   *printer,
+                               double       *top,
+                               double       *bottom,
+                               double       *left,
+                               double       *right)
+{
+  char *left_margin, *right_margin, *top_margin, *bottom_margin;
+
+  GtkPrinterCpdb *printer_cpdb = GTK_PRINTER_CPDB (printer);
+  cpdb_printer_obj_t *printer_obj = gtk_printer_cpdb_get_printer_obj (printer_cpdb);
+
+  top_margin = cpdbGetDefault (printer_obj, CPDB_OPTION_MARGIN_TOP);
+  left_margin = cpdbGetDefault (printer_obj, CPDB_OPTION_MARGIN_LEFT);
+  right_margin = cpdbGetDefault (printer_obj, CPDB_OPTION_MARGIN_RIGHT);
+  bottom_margin = cpdbGetDefault (printer_obj, CPDB_OPTION_MARGIN_BOTTOM);
+
+  if (top_margin != NULL &&
+      left_margin != NULL &&
+      right_margin != NULL &&
+      bottom_margin != NULL)
+    {
+      *top = g_ascii_strtod (top_margin, NULL) * points_multiplier / 100.0;
+      *left = g_ascii_strtod (left_margin, NULL) * points_multiplier / 100.0;
+      *right = g_ascii_strtod (right_margin, NULL) * points_multiplier / 100.0;
+      *bottom = g_ascii_strtod (bottom_margin, NULL) * points_multiplier / 100.0;
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
+/*
+ * This function is responsible for getting the
+ * default page size margins for give paper size
+ */
+static gboolean
+cpdb_printer_get_hard_margins_for_paper_size (GtkPrinter      *printer,
+                                              GtkPaperSize    *paper_size,
+                                              double          *top,
+                                              double          *bottom,
+                                              double          *left,
+                                              double          *right)
+{
+  int num_margins;
+  const char *media;
+  cpdb_margin_t *margins;
+
+  GtkPrinterCpdb *printer_cpdb = GTK_PRINTER_CPDB (printer);
+  cpdb_printer_obj_t *printer_obj = gtk_printer_cpdb_get_printer_obj (printer_cpdb);
+
+  media = gtk_paper_size_get_name (paper_size);
+  if (media != NULL)
+    {
+      num_margins = cpdbGetMediaMargins (printer_obj, media, &margins);
+      if (num_margins > 0)
+        {
+          *top = margins[0].top * points_multiplier / 100.0;
+          *left = margins[0].left * points_multiplier / 100.0;
+          *right = margins[0].right * points_multiplier / 100.0;
+          *bottom = margins[0].bottom * points_multiplier / 100.0;
+
+          return TRUE;
+        }
+    }
+
+  return FALSE;
+}
+
+/*
+ * @user_data: GtkPrintSettings
+ */
+static void
+add_option_to_settings (GtkPrinterOption    *option,
+                        gpointer            user_data)
+{
+  GtkPrintSettings *settings = (GtkPrintSettings *) user_data;
+  gtk_print_settings_set (settings, option->name, option->value);
+}
+
+/*
+ * This method is invoked when the print button on the print dialog is pressed.
+ * It's responsible for gathering all the settings from the print dialog that the
+ * user may have selected before pressing the print button.
+ */
+static void
+cpdb_printer_get_settings_from_options (GtkPrinter            *printer,
+                                        GtkPrinterOptionSet   *options,
+                                        GtkPrintSettings      *settings)
+{
+  char *value;
+  GtkPrinterOption *option, *cover_before, *cover_after;
+
+  option = gtk_printer_option_set_lookup (options, "gtk-n-up");
+  if (option != NULL)
+    {
+      gtk_print_settings_set (settings,
+                              CPDB_OPTION_NUMBER_UP,
+                              option->value);
+    }
+
+  option = gtk_printer_option_set_lookup (options, "gtk-n-up-layout");
+  if (option != NULL)
+    {
+      gtk_print_settings_set (settings,
+                              CPDB_OPTION_NUMBER_UP_LAYOUT,
+                              option->value);
+    }
+
+  option = gtk_printer_option_set_lookup (options, "gtk-duplex");
+  if (option != NULL)
+    {
+      gtk_print_settings_set (settings,
+                              CPDB_OPTION_SIDES,
+                              option->value);
+    }
+
+  option = gtk_printer_option_set_lookup (options, "gtk-paper-source");
+  if (option != NULL)
+    {
+      gtk_print_settings_set (settings,
+                              CPDB_OPTION_MEDIA_SOURCE,
+                              option->value);
+    }
+
+  option = gtk_printer_option_set_lookup (options, "gtk-paper-type");
+  if (option != NULL)
+    {
+      gtk_print_settings_set (settings,
+                              CPDB_OPTION_MEDIA_TYPE,
+                              option->value);
+    }
+
+  option = gtk_printer_option_set_lookup (options, "gtk-output-tray");
+  if (option != NULL)
+    {
+      gtk_print_settings_set (settings,
+                              CPDB_OPTION_OUTPUT_BIN,
+                              option->value);
+    }
+
+  option = gtk_printer_option_set_lookup (options, "gtk-job-prio");
+  if (option != NULL)
+    {
+      gtk_print_settings_set (settings,
+                              CPDB_OPTION_JOB_PRIORITY,
+                              option->value);
+    }
+
+  cover_before = gtk_printer_option_set_lookup (options, "gtk-cover-before");
+  cover_after = gtk_printer_option_set_lookup (options, "gtk-cover-after");
+  if (cover_before != NULL && cover_after != NULL)
+    {
+      value = g_strdup_printf ("%s,%s",
+                               cover_before->value,
+                               cover_after->value);
+      gtk_print_settings_set (settings,
+                              CPDB_OPTION_JOB_SHEETS,
+                              value);
+      g_free (value);
+    }
+
+  option = gtk_printer_option_set_lookup (options, "gtk-billing-info");
+  if (option != NULL)
+    {
+      gtk_print_settings_set (settings,
+                              CPDB_OPTION_BILLING_INFO,
+                              option->value);
+    }
+
+  char *print_at = NULL;
+  option = gtk_printer_option_set_lookup (options, "gtk-print-time");
+  if (option != NULL)
+    print_at = g_strdup (option->value);
+
+  char *print_at_time = NULL;
+  option = gtk_printer_option_set_lookup (options, "gtk-print-time-text");
+  if (option != NULL)
+    print_at_time = g_strdup (option->value);
+
+  if (print_at != NULL && print_at_time != NULL)
+    {
+      if (g_strcmp0 (print_at, "at") == 0)
+        {
+          char *utc_time = NULL;
+
+          utc_time = localtime_to_utctime (print_at_time);
+          if (utc_time != NULL)
+            {
+              gtk_print_settings_set (settings,
+                                      CPDB_OPTION_JOB_HOLD_UNTIL,
+                                      utc_time);
+              g_free (utc_time);
+            }
+          else
+            gtk_print_settings_set (settings,
+                                    CPDB_OPTION_JOB_HOLD_UNTIL,
+                                    print_at_time);
+        }
+      else if (g_strcmp0 (print_at, "on-hold") == 0)
+        gtk_print_settings_set (settings,
+                                CPDB_OPTION_JOB_HOLD_UNTIL,
+                                CPDB_JOB_HOLD_INDEFINITE);
+    }
+  if (print_at != NULL)
+    g_free (print_at);
+  if (print_at_time != NULL)
+    g_free (print_at_time);
+
+  gtk_printer_option_set_foreach (options, add_option_to_settings, settings);
+}
+
+static cairo_status_t
+_cairo_write (void                  *closure,
+              const unsigned char   *data,
+              unsigned int          length)
+{
+  GIOChannel *io = (GIOChannel *)closure;
+  gsize written;
+  GError *error;
+
+  error = NULL;
+
+  GTK_DEBUG (PRINTING, "CPDB Backend: Writing %i byte chunk to temp file", length);
+
+  while (length > 0)
+    {
+      g_io_channel_write_chars (io, (const char *)data, length, &written, &error);
+
+      if (error != NULL)
+        {
+          GTK_DEBUG (PRINTING, "CPDB Backend: Error writing to temp file, %s", error->message);
+
+          g_error_free (error);
+          return CAIRO_STATUS_WRITE_ERROR;
+        }
+
+      GTK_DEBUG (PRINTING, "CPDB Backend: Wrote %" G_GSIZE_FORMAT " bytes to temp file\n", written);
+
+      data += written;
+      length -= written;
+    }
+
+  return CAIRO_STATUS_SUCCESS;
+}
+
+
+/*
+ * called after prepare_for_print ()
+ */
+static cairo_surface_t *
+cpdb_printer_create_cairo_surface  (GtkPrinter          *printer,
+                                    GtkPrintSettings    *settings,
+                                    double              width,
+                                    double              height,
+                                    GIOChannel          *cache_io)
+{
+  cairo_surface_t *surface;
+
+  surface = cairo_pdf_surface_create_for_stream (_cairo_write, cache_io, width, height);
+
+  cairo_surface_set_fallback_resolution  (surface,
+                                          2.0 * gtk_print_settings_get_printer_lpi (settings),
+                                          2.0 * gtk_print_settings_get_printer_lpi (settings));
+
+  return surface;
+}
+
+static void
+gtk_printer_cpdb_configure_settings (const char     *key,
+                                     const char     *value,
+                                     gpointer       user_data)
+{
+  const char *option_name, *option_value;
+  GtkPrinterCpdb *printer_cpdb = GTK_PRINTER_CPDB (user_data);
+  cpdb_printer_obj_t *printer_obj = gtk_printer_cpdb_get_printer_obj (printer_cpdb);
+
+  option_name = key;
+  option_value = value;
+  if (g_str_has_prefix (option_name, "gtk") ||
+      g_strcmp0 (option_value, "") == 0)
+    {
+      return;
+    }
+
+  cpdbAddSettingToPrinter (printer_obj, option_name, option_value);
+}
+
+static void
+gtk_printer_cpdb_configure_page_setup (GtkPrinter         *printer,
+                                       GtkPageSetup       *page_setup,
+                                       GtkPrintSettings   *settings)
+{
+  char *value, *orientation, *default_orientation;
+  const char *borderless;
+  double width, height, left, top, right, bottom;
+  GtkPageOrientation page_orientation;
+
+  GtkPrinterCpdb *printer_cpdb = GTK_PRINTER_CPDB (printer);
+  cpdb_printer_obj_t *printer_obj = gtk_printer_cpdb_get_printer_obj (printer_cpdb);
+
+  width = gtk_page_setup_get_paper_width (page_setup, GTK_UNIT_MM) * 100.0;
+  height = gtk_page_setup_get_paper_height (page_setup, GTK_UNIT_MM) * 100.0;
+  left = gtk_page_setup_get_left_margin (page_setup, GTK_UNIT_MM) * 100.0;
+  right = gtk_page_setup_get_right_margin (page_setup, GTK_UNIT_MM) * 100.0;
+  top = gtk_page_setup_get_top_margin (page_setup, GTK_UNIT_MM) * 100.0;
+  bottom = gtk_page_setup_get_bottom_margin (page_setup, GTK_UNIT_MM) * 100.0;
+
+  borderless = gtk_print_settings_get (settings, "borderless");
+  if (g_ascii_strcasecmp (borderless, "True") == 0)
+    left = right = top = bottom = 0;
+
+  value = g_strdup_printf ("{media-size={x-dimension=%.0f y-dimension=%.0f} "
+                            "media-bottom-margin=%.0f "
+                            "media-left-margin=%.0f "
+                            "media-right-margin=%.0f "
+                            "media-top-margin=%.0f}",
+                            width, height, bottom, left, right, top);
+  gtk_print_settings_set (settings, CPDB_OPTION_MEDIA_COL, value);
+  g_free (value);
+
+  page_orientation = gtk_page_setup_get_orientation (page_setup);
+  default_orientation = cpdbGetDefault (printer_obj, CPDB_OPTION_ORIENTATION);
+
+  switch (page_orientation)
+    {
+    case GTK_PAGE_ORIENTATION_PORTRAIT:
+      orientation = g_strdup_printf (CPDB_ORIENTATION_PORTRAIT);
+      break;
+
+    case GTK_PAGE_ORIENTATION_LANDSCAPE:
+      orientation = g_strdup_printf (CPDB_ORIENTATION_LANDSCAPE);
+      break;
+
+    case GTK_PAGE_ORIENTATION_REVERSE_LANDSCAPE:
+      orientation = g_strdup_printf (CPDB_ORIENTATION_RLANDSCAPE);
+      break;
+
+    case GTK_PAGE_ORIENTATION_REVERSE_PORTRAIT:
+      orientation = g_strdup_printf (CPDB_ORIENTATION_RPORTRAIT);
+      break;
+
+    default:
+      orientation = default_orientation;
+    }
+
+  gtk_print_settings_set (settings, CPDB_OPTION_ORIENTATION, orientation);
+  g_free (orientation);
+}
+
+static void
+cpdb_printer_prepare_for_print (GtkPrinter          *printer,
+                                GtkPrintJob         *print_job,
+                                GtkPrintSettings    *settings,
+                                GtkPageSetup        *page_setup)
+{
+  int n_ranges;
+  double scale;
+
+  GtkPrintPages pages;
+  GtkPageRange *ranges;
+  GtkPageSet page_set;
+  GtkPrintCapabilities capabilities;
+
+  capabilities = cpdb_printer_get_capabilities (printer);
+
+  pages = gtk_print_settings_get_print_pages (settings);
+  gtk_print_job_set_pages (print_job, pages);
+  gtk_print_settings_unset (settings, GTK_PRINT_SETTINGS_PRINT_PAGES);
+
+  if (pages == GTK_PRINT_PAGES_RANGES)
+    ranges = gtk_print_settings_get_page_ranges (settings, &n_ranges);
+  else
+    {
+      ranges = NULL;
+      n_ranges = 0;
+    }
+  gtk_print_job_set_page_ranges (print_job, ranges, n_ranges);
+  gtk_print_settings_unset (settings, GTK_PRINT_SETTINGS_PAGE_RANGES);
+
+  scale = gtk_print_settings_get_scale (settings);
+  if (scale != 100.0)
+    gtk_print_job_set_scale (print_job, scale / 100.0);
+  gtk_print_settings_unset (settings, GTK_PRINT_SETTINGS_SCALE);
+
+  if (capabilities & GTK_PRINT_CAPABILITY_COLLATE)
+    {
+      if (gtk_print_settings_get_collate (settings))
+        gtk_print_settings_set (settings,
+                                CPDB_OPTION_COLLATE,
+                                CPDB_COLLATE_ENABLED);
+    }
+  gtk_print_job_set_collate (print_job, FALSE);
+  gtk_print_settings_unset (settings, GTK_PRINT_SETTINGS_COLLATE);
+
+  if (capabilities & GTK_PRINT_CAPABILITY_REVERSE)
+    {
+      if (gtk_print_settings_get_reverse (settings))
+        gtk_print_settings_set (settings,
+                                CPDB_OPTION_PAGE_DELIVERY,
+                                CPDB_PAGE_DELIVERY_REVERSE);
+    }
+  gtk_print_job_set_reverse (print_job, FALSE);
+  gtk_print_settings_unset (settings, GTK_PRINT_SETTINGS_REVERSE);
+
+  if (capabilities & GTK_PRINT_CAPABILITY_COPIES)
+    {
+      int copies = gtk_print_settings_get_n_copies (settings);
+      if (copies > 1)
+        {
+          char *value = g_strdup_printf ("%d", copies);
+          gtk_print_settings_set (settings, CPDB_OPTION_COPIES, value);
+          g_free (value);
+        }
+    }
+  gtk_print_job_set_num_copies (print_job, 1);
+  gtk_print_settings_unset (settings, GTK_PRINT_SETTINGS_N_COPIES);
+
+  page_set = gtk_print_settings_get_page_set (settings);
+  switch (page_set)
+    {
+    case GTK_PAGE_SET_EVEN :
+      gtk_print_settings_set (settings,
+                              CPDB_OPTION_PAGE_SET,
+                              CPDB_PAGE_SET_EVEN);
+      break;
+
+    case GTK_PAGE_SET_ODD :
+      gtk_print_settings_set (settings,
+                              CPDB_OPTION_PAGE_SET,
+                              CPDB_PAGE_SET_ODD);
+      break;
+
+    case GTK_PAGE_SET_ALL :
+    default :
+      gtk_print_settings_set (settings,
+                              CPDB_OPTION_PAGE_SET,
+                              CPDB_PAGE_SET_ALL);
+    }
+  gtk_print_job_set_page_set (print_job, GTK_PAGE_SET_ALL);
+  gtk_print_settings_unset (settings, GTK_PRINT_SETTINGS_PAGE_SET);
+
+  gtk_print_settings_unset (settings, "printer");
+
+  gtk_printer_cpdb_configure_page_setup (printer, page_setup, settings);
+  gtk_print_settings_unset (settings, "borderless");
+
+  gtk_print_settings_foreach (settings,
+                              gtk_printer_cpdb_configure_settings,
+                              printer);
+}
+
+
+static void
+cpdb_print_cb  (GtkPrintBackendCpdb   *backend_cpdb,
+                GError                *error,
+                gpointer              user_data)
+{;
+  GtkPrinterCpdb *printer_cpdb;
+  cpdb_printer_obj_t *printer_obj;
+  _PrintStreamData *ps = (_PrintStreamData *) user_data;
+
+  if (ps->target_io_stream != NULL)
+    g_io_stream_close (G_IO_STREAM (ps->target_io_stream), NULL, NULL);
+
+  if (ps->callback)
+    ps->callback (ps->job, ps->user_data, error);
+
+  if (ps->dnotify)
+    ps->dnotify (ps->user_data);
+
+  gtk_print_job_set_status (ps->job,
+                            error ? GTK_PRINT_STATUS_FINISHED_ABORTED
+                                  : GTK_PRINT_STATUS_FINISHED);
+
+  if (!error)
+    {
+      printer_cpdb = GTK_PRINTER_CPDB (gtk_print_job_get_printer (ps->job));
+      printer_obj = gtk_printer_cpdb_get_printer_obj (printer_cpdb);
+
+      GTK_DEBUG (PRINTING, "CPDB Backend: Sending file to CPDB for printing - %s\n", ps->path);
+
+      cpdbPrintFile (printer_obj, ps->path);
+
+      g_free (ps->path);
+    }
+
+  if (ps->job)
+    g_object_unref (ps->job);
+
+  g_free (ps);
+}
+
+static gboolean
+cpdb_write (GIOChannel      *source,
+            GIOCondition    con,
+            gpointer        user_data)
+{
+  char buf[_CPDB_MAX_CHUNK_SIZE];
+  gsize bytes_read;
+  GError *error;
+  GIOStatus status;
+  GOutputStream *out_stream;
+  _PrintStreamData *ps = (_PrintStreamData *) user_data;
+
+  error = NULL;
+
+  status = g_io_channel_read_chars (source,
+                                    buf,
+                                    _CPDB_MAX_CHUNK_SIZE,
+                                    &bytes_read,
+                                    &error);
+
+  if (status != G_IO_STATUS_ERROR)
+    {
+      gsize bytes_written;
+
+      out_stream = g_io_stream_get_output_stream (ps->target_io_stream);
+      g_output_stream_write_all (out_stream,
+                                buf,
+                                bytes_read,
+                                &bytes_written,
+                                NULL,
+                                &error);
+    }
+
+  if (error != NULL || status == G_IO_STATUS_EOF)
+    {
+      cpdb_print_cb (GTK_PRINT_BACKEND_CPDB (ps->backend),
+                     error,
+                     user_data);
+
+      if (error != NULL)
+        {
+          GTK_DEBUG (PRINTING, "CPDB Backend: Error writing to file - %s\n", error->message);
+
+          g_error_free (error);
+        }
+
+      return FALSE;
+    }
+
+  GTK_DEBUG (PRINTING, "CPDB Backend: Writing %" G_GSIZE_FORMAT " byte chunk to cpdb pipe\n", bytes_read);
+
+  return TRUE;
+}
+
+
+
+static void
+cpdb_print_stream  (GtkPrintBackend           *backend,
+                    GtkPrintJob               *job,
+                    GIOChannel                *data_io,
+                    GtkPrintJobCompleteFunc   callback,
+                    gpointer                  user_data,
+                    GDestroyNotify            dnotify)
+{
+  GError *error;
+  GFile *file;
+  _PrintStreamData *ps;
+  GFileIOStream *iostream;
+
+  ps = g_new0 (_PrintStreamData, 1);
+  ps->callback = callback;
+  ps->user_data = user_data;
+  ps->dnotify = dnotify;
+  ps->job = g_object_ref (job);
+  ps->backend = backend;
+
+  error = NULL;
+  file = g_file_new_tmp (NULL, &iostream, &error);
+  if (file == NULL)
+    goto error;
+
+  ps->path = g_file_get_path (file);
+  ps->target_io_stream = G_IO_STREAM (iostream);
+
+  g_object_unref (file);
+
+error:
+  if (error != NULL)
+    {
+      GTK_DEBUG (PRINTING, "Error: %s\n", error->message);
+
+      cpdb_print_cb (GTK_PRINT_BACKEND_CPDB (backend), error, ps);
+
+      g_error_free (error);
+      return;
+    }
+
+  g_io_add_watch (data_io,
+                  G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_HUP,
+                  (GIOFunc) cpdb_write,
+                  ps);
+
+}
+
+/*
+ * Reflect changes in printers to the GTK print dialog.
+ */
+static void
+printer_updates_callback (cpdb_frontend_obj_t     *frontend_obj,
+                          cpdb_printer_obj_t      *printer_obj,
+                          cpdb_printer_update_t    change)
+{
+  GtkPrinter *printer;
+
+  if (gtk_print_backend == NULL)
+    return;
+  
+  switch (change)
+    {
+    case CPDB_CHANGE_PRINTER_ADDED:
+      cpdb_add_gtk_printer (gtk_print_backend, printer_obj);
+      break;
+
+    case CPDB_CHANGE_PRINTER_REMOVED:
+      cpdb_remove_gtk_printer (gtk_print_backend, printer_obj);
+      cpdbDeletePrinterObj (printer_obj);
+      break;
+
+    case CPDB_CHANGE_PRINTER_STATE_CHANGED:
+      printer = gtk_print_backend_find_printer (gtk_print_backend, printer_obj->name);
+      set_state_message (printer);
+      g_signal_emit_by_name (gtk_print_backend, "printer-status-changed", printer);
+      break;
+    
+    default:
+      break;
+    }
+}
+
+ /*
+  * @key: printer name
+  * @value: cpdb_printer_obj_t
+  * @user_data: GtkPrintBackend
+  */
+static void
+cpdb_printer_add_hash_table (gpointer key,
+                             gpointer value,
+                             gpointer user_data)
+{
+  cpdb_add_gtk_printer (user_data, value);
+}
+
+/*
+ * Adds given printer to given GtkPrintBackend
+ */
+static void
+cpdb_add_gtk_printer (GtkPrintBackend     *backend,
+                      cpdb_printer_obj_t  *printer_obj)
+{
+  GtkPrinter *printer;
+  GtkPrinterCpdb *printer_cpdb;
+  cpdb_printer_obj_t *default_printer;
+
+  GtkPrintBackendCpdb *backend_cpdb = GTK_PRINT_BACKEND_CPDB (backend);
+
+  /*
+   * Ignore printers from FILE backend, 
+   * since we are using "Print To File" GTK print backend.
+   */
+  if (g_strcmp0 (printer_obj->backend_name, "FILE") == 0)
+    return;
+  
+  printer_cpdb = g_object_new (GTK_TYPE_PRINTER_CPDB,
+                               "name", printer_obj->name,
+                               "backend", backend,
+                               NULL);
+  gtk_printer_cpdb_set_printer_obj (printer_cpdb, printer_obj);
+
+  printer = GTK_PRINTER (printer_cpdb);
+  gtk_printer_set_icon_name (printer, "printer");
+  gtk_printer_set_location (printer, printer_obj->location);
+  gtk_printer_set_description (printer, printer_obj->info);
+  gtk_printer_set_accepts_pdf (printer, TRUE);
+  gtk_printer_set_accepts_ps (printer, TRUE);
+  gtk_printer_set_is_active (printer, TRUE);
+  gtk_printer_set_has_details (printer, FALSE);
+
+  default_printer = cpdbGetDefaultPrinter (backend_cpdb->frontend_obj);
+  if (default_printer == printer_obj)
+    gtk_printer_set_is_default (printer, TRUE);
+
+  /*
+   * If printer state is not available, wait till cpdbAcquireDetails ()
+   * is called when the printer is clicked on in the print dialog
+   */
+  if (g_strcmp0 (printer_obj->state, "NA") == 0)
+    {
+      gtk_printer_set_is_accepting_jobs (printer, TRUE);
+      gtk_printer_set_is_paused (printer, FALSE);
+      gtk_printer_set_state_message (printer, "");
+    }
+  else
+    {
+      gtk_printer_set_is_accepting_jobs (printer,
+                                         cpdbIsAcceptingJobs (printer_obj));
+      gtk_printer_set_is_paused (printer,
+                                 g_strcmp0 (cpdbGetState (printer_obj),
+                                            CPDB_STATE_STOPPED) == 0);
+      set_state_message (printer);
+    }
+
+  gtk_print_backend_add_printer (backend, printer);
+  if (gtk_print_backend_printer_list_is_done (backend))
+    {
+      g_signal_emit_by_name (backend, "printer-added", printer);
+      g_signal_emit_by_name (backend, "printer-list-changed");
+    }
+  g_object_unref (printer);
+}
+
+/*
+ * Removes given printer from given GtkPrintBackend
+ */
+static void
+cpdb_remove_gtk_printer (GtkPrintBackend      *backend,
+                         cpdb_printer_obj_t   *printer_obj)
+{
+  GtkPrinter *printer = gtk_print_backend_find_printer (backend, printer_obj->name);
+
+  gtk_print_backend_remove_printer (backend, printer);
+  g_signal_emit_by_name (backend, "printer-removed", printer);
+  g_signal_emit_by_name (backend, "printer-list-changed");
+}
+
+/*
+ * Sets printer status
+ */
+static void
+set_state_message (GtkPrinter *printer)
+{
+  gboolean stopped, accepting_jobs;
+  GtkPrinterCpdb *printer_cpdb = GTK_PRINTER_CPDB (printer);
+  cpdb_printer_obj_t *printer_obj = gtk_printer_cpdb_get_printer_obj (printer_cpdb);
+
+  stopped = g_strcmp0 (cpdbGetState (printer_obj), CPDB_STATE_STOPPED) == 0;
+  accepting_jobs = cpdbIsAcceptingJobs (printer_obj);
+
+  if (stopped && !accepting_jobs)
+    /* Translators: this is a printer status. */
+    gtk_printer_set_state_message (printer, _("Paused; Rejecting Jobs"));
+  else if (stopped && accepting_jobs)
+    /* Translators: this is a printer status. */
+    gtk_printer_set_state_message (printer, _("Paused"));
+  else if (!accepting_jobs)
+    /* Translators: this is a printer status. */
+    gtk_printer_set_state_message (printer, _("Rejecting Jobs"));
+  else
+    gtk_printer_set_state_message (printer, "");
+}
+
+/*
+ * Fills option choices & sets default in gtk_option from cpdb_option
+ */
+static void
+cpdb_fill_gtk_option (GtkPrinterOption    *gtk_option,
+                      cpdb_option_t       *cpdb_option,
+                      cpdb_printer_obj_t  *printer_obj)
+{
+  char *display_val;
+  char **default_val;
+
+  gtk_printer_option_allocate_choices (gtk_option, cpdb_option->num_supported);
+  for (int i = 0; i < cpdb_option->num_supported; i++)
+    {
+      gtk_option->choices[i] = g_strdup (cpdb_option->supported_values[i]);
+      display_val = cpdb_choice_translation (printer_obj,
+                                             cpdb_option->option_name,
+                                             cpdb_option->supported_values[i]);
+      gtk_option->choices_display[i] = g_strdup (display_val);
+    }
+
+  if (g_strcmp0 (cpdb_option->default_value, "NA") != 0)
+    {
+      if (g_strcmp0 (cpdb_option->option_name, CPDB_OPTION_JOB_SHEETS) == 0)
+        {
+          default_val = g_strsplit (cpdb_option->default_value, ",", 2);
+          if (g_strcmp0 (gtk_option->name, "gtk-cover-before") == 0)
+            gtk_printer_option_set (gtk_option, default_val[0]);
+          else if (g_strcmp0 (gtk_option->name, "gtk-cover-after") == 0)
+            gtk_printer_option_set (gtk_option, default_val[1]);
+        }
+      else
+        {
+          gtk_printer_option_set (gtk_option, g_strdup (cpdb_option->default_value));
+        }
+     }
+}
+
+/*
+ * Wrapper for getting translation of an option name
+ */
+static char *
+cpdb_option_translation (cpdb_printer_obj_t *printer_obj,
+                         const char *option_name)
+{
+  int i;
+  char *translation = NULL;
+
+  for (i = 0; locales[i] != NULL; i++)
+    {
+      translation = cpdbGetOptionTranslation (printer_obj,
+                                              option_name,
+                                              locales[i]);
+      if (translation != NULL)
+        break;
+    }
+  return translation;
+}
+
+/*
+ * Wrapper for getting translation of a choice name
+ */
+static char *
+cpdb_choice_translation (cpdb_printer_obj_t *printer_obj,
+                         const char *option_name,
+                         const char *choice_name)
+{
+  int i;
+  char *translation = NULL;
+
+  for (i = 0; locales[i] != NULL; i++)
+    {
+      translation = cpdbGetChoiceTranslation (printer_obj,
+                                              option_name,
+                                              choice_name,
+                                              locales[i]);
+      if (translation != NULL)
+        break;
+    }
+  return translation;
+}
+
+/*
+ * Wrapper for getting translation of a group name
+ */
+static char *
+cpdb_group_translation (cpdb_printer_obj_t *printer_obj,
+                        const char *group_name)
+{
+  int i;
+  char *translation = NULL;
+
+  for (i = 0; locales[i] != NULL; i++)
+    {
+      translation = cpdbGetGroupTranslation (printer_obj,
+                                             group_name,
+                                             locales[i]);
+      if (translation != NULL)
+        break;
+    }
+  return translation;
+}
+
+/*
+ * Convert CPDB groups explicitly supported by GTK print dialog
+ */
+static gchar *
+get_gtk_group (cpdb_printer_obj_t *printer_obj,
+               const char *group_name)
+{
+  if (g_strcmp0 (group_name, CPDB_GROUP_COLOR) == 0)
+    return g_strdup ("ColorPage");
+  if (g_strcmp0 (group_name, CPDB_GROUP_QUALITY) == 0)
+    return g_strdup ("ImageQualityPage");
+  if (g_strcmp0 (group_name, CPDB_GROUP_FINISHINGS) == 0)
+    return g_strdup ("FinishingPage");
+  return cpdb_group_translation (printer_obj, group_name);
+}
+
+/*
+ * Do initial setup
+ */
+static void
+initialize (void)
+{
+  cpdbInit ();
+
+  if (locales == NULL)
+    locales = g_get_language_names ();
+
+  if (already_used_options == NULL)
+   {
+      already_used_options = g_hash_table_new (g_str_hash, g_str_equal);
+      g_hash_table_add (already_used_options, (gpointer) CPDB_OPTION_COPIES);
+      g_hash_table_add (already_used_options, (gpointer) CPDB_OPTION_PAGE_RANGES);
+      g_hash_table_add (already_used_options, (gpointer) CPDB_OPTION_ORIENTATION);
+      g_hash_table_add (already_used_options, (gpointer) CPDB_OPTION_PAGE_DELIVERY);
+      g_hash_table_add (already_used_options, (gpointer) CPDB_OPTION_COLLATE);
+      g_hash_table_add (already_used_options, (gpointer) CPDB_OPTION_NUMBER_UP);
+      g_hash_table_add (already_used_options, (gpointer) CPDB_OPTION_NUMBER_UP_LAYOUT);
+      g_hash_table_add (already_used_options, (gpointer) CPDB_OPTION_PAGE_SET);
+      g_hash_table_add (already_used_options, (gpointer) CPDB_OPTION_MEDIA);
+      g_hash_table_add (already_used_options, (gpointer) CPDB_OPTION_MARGIN_TOP);
+      g_hash_table_add (already_used_options, (gpointer) CPDB_OPTION_MARGIN_BOTTOM);
+      g_hash_table_add (already_used_options, (gpointer) CPDB_OPTION_MARGIN_LEFT);
+      g_hash_table_add (already_used_options, (gpointer) CPDB_OPTION_MARGIN_RIGHT);
+      g_hash_table_add (already_used_options, (gpointer) CPDB_OPTION_SIDES);
+      g_hash_table_add (already_used_options, (gpointer) CPDB_OPTION_MEDIA_SOURCE);
+      g_hash_table_add (already_used_options, (gpointer) CPDB_OPTION_MEDIA_TYPE);
+      g_hash_table_add (already_used_options, (gpointer) CPDB_OPTION_OUTPUT_BIN);
+      g_hash_table_add (already_used_options, (gpointer) CPDB_OPTION_JOB_PRIORITY);
+      g_hash_table_add (already_used_options, (gpointer) CPDB_OPTION_JOB_SHEETS);
+      g_hash_table_add (already_used_options, (gpointer) CPDB_OPTION_JOB_HOLD_UNTIL);
+      g_hash_table_add (already_used_options, (gpointer) CPDB_OPTION_BILLING_INFO);
+      g_hash_table_add (already_used_options, (gpointer) "borderless");
+   }
+}
diff --git a/modules/printbackends/gtkprintbackendcpdb.h b/modules/printbackends/gtkprintbackendcpdb.h
new file mode 100644 (file)
index 0000000..6324e76
--- /dev/null
@@ -0,0 +1,43 @@
+/* GTK - The GIMP Toolkit
+ * gtkprintbackendcpdb.h: Default implementation of GtkPrintBackend
+ * for the Common Print Dialog Backends (CPDB)
+ * Copyright (C) 2022, 2023 TinyTrebuchet <tinytrebuchet@protonmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef __GTK_PRINT_BACKEND_CPDB_H__
+#define __GTK_PRINT_BACKEND_CPDB_H__
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+#include "gtkprintbackendprivate.h"
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_PRINT_BACKEND_CPDB             (gtk_print_backend_cpdb_get_type ())
+#define GTK_PRINT_BACKEND_CPDB(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_PRINT_BACKEND_CPDB, GtkPrintBackendCpdb))
+#define GTK_IS_PRINT_BACKEND_CPDB(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_PRINT_BACKEND_CPDB))
+
+typedef struct _GtkPrintBackendCpdbClass GtkPrintBackendCpdbClass;
+typedef struct _GtkPrintBackendCpdb      GtkPrintBackendCpdb;
+
+GtkPrintBackend  *gtk_print_backend_cpdb_new        (void);
+GType             gtk_print_backend_cpdb_get_type   (void) G_GNUC_CONST;
+
+G_END_DECLS
+
+#endif /* __GTK_PRINT_BACKEND_CPDB_H__ */
+
index 878e073741115427b6081554dc000e06a9c04802..b88995b89dd24a33551a8fca030b7e3c15c384b2 100644 (file)
@@ -55,6 +55,7 @@
 
 #include "gtkcupsutils.h"
 #include "gtkcupssecretsutils.h"
+#include "gtkprintbackendutils.h"
 
 #include <gtkprintutils.h>
 #include "gtkprivate.h"
@@ -6483,87 +6484,6 @@ foreach_option_get_settings (GtkPrinterOption *option,
     gtk_print_settings_set (settings, option->name, value);
 }
 
-static gboolean
-supports_am_pm (void)
-{
-  struct tm tmp_tm = { 0 };
-  char   time[8];
-  int    length;
-
-  length = strftime (time, sizeof (time), "%p", &tmp_tm);
-
-  return length != 0;
-}
-
-/* Converts local time to UTC time. Local time has to be in one of these
- * formats:  HH:MM:SS, HH:MM, HH:MM:SS {am, pm}, HH:MM {am, pm}, HH {am, pm},
- * {am, pm} HH:MM:SS, {am, pm} HH:MM, {am, pm} HH.
- * Returns a newly allocated string holding UTC time in HH:MM:SS format
- * or NULL.
- */
-static char *
-localtime_to_utctime (const char *local_time)
-{
-  const char *formats_0[] = {" %I : %M : %S %p ", " %p %I : %M : %S ",
-                             " %H : %M : %S ",
-                             " %I : %M %p ", " %p %I : %M ",
-                             " %H : %M ",
-                             " %I %p ", " %p %I "};
-  const char *formats_1[] = {" %H : %M : %S ", " %H : %M "};
-  const char *end = NULL;
-  struct tm  *actual_local_time;
-  struct tm  *actual_utc_time;
-  struct tm   local_print_time;
-  struct tm   utc_print_time;
-  struct tm   diff_time;
-  char       *utc_time = NULL;
-  int         i, n;
-
-  if (local_time == NULL || local_time[0] == '\0')
-    return NULL;
-
-  n = supports_am_pm () ? G_N_ELEMENTS (formats_0) : G_N_ELEMENTS (formats_1);
-
-  for (i = 0; i < n; i++)
-    {
-      local_print_time.tm_hour = 0;
-      local_print_time.tm_min  = 0;
-      local_print_time.tm_sec  = 0;
-
-      if (supports_am_pm ())
-        end = strptime (local_time, formats_0[i], &local_print_time);
-      else
-        end = strptime (local_time, formats_1[i], &local_print_time);
-
-      if (end != NULL && end[0] == '\0')
-        break;
-    }
-
-  if (end != NULL && end[0] == '\0')
-    {
-      time_t rawtime;
-      time (&rawtime);
-
-      actual_utc_time = g_memdup2 (gmtime (&rawtime), sizeof (struct tm));
-      actual_local_time = g_memdup2 (localtime (&rawtime), sizeof (struct tm));
-
-      diff_time.tm_hour = actual_utc_time->tm_hour - actual_local_time->tm_hour;
-      diff_time.tm_min  = actual_utc_time->tm_min  - actual_local_time->tm_min;
-      diff_time.tm_sec  = actual_utc_time->tm_sec  - actual_local_time->tm_sec;
-
-      utc_print_time.tm_hour = ((local_print_time.tm_hour + diff_time.tm_hour) + 24) % 24;
-      utc_print_time.tm_min  = ((local_print_time.tm_min  + diff_time.tm_min)  + 60) % 60;
-      utc_print_time.tm_sec  = ((local_print_time.tm_sec  + diff_time.tm_sec)  + 60) % 60;
-
-      utc_time = g_strdup_printf ("%02d:%02d:%02d",
-                                  utc_print_time.tm_hour,
-                                  utc_print_time.tm_min,
-                                  utc_print_time.tm_sec);
-    }
-
-  return utc_time;
-}
-
 static void
 cups_printer_get_settings_from_options (GtkPrinter          *printer,
                                        GtkPrinterOptionSet *options,
diff --git a/modules/printbackends/gtkprintbackendutils.c b/modules/printbackends/gtkprintbackendutils.c
new file mode 100644 (file)
index 0000000..b1c8023
--- /dev/null
@@ -0,0 +1,128 @@
+/* GTK - The GIMP Toolkit
+ * gtkprintbackendcups.h: Default implementation of GtkPrintBackend
+ * for the Common Unix Print System (CUPS)
+ * Copyright (C) 2006, 2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include <glib.h>
+#include <sys/random.h>
+
+#include "gtkprintbackendutils.h"
+
+/* Converts local time to UTC time. Local time has to be in one of these
+ * formats:  HH:MM:SS, HH:MM, HH:MM:SS {am, pm}, HH:MM {am, pm}, HH {am, pm},
+ * {am, pm} HH:MM:SS, {am, pm} HH:MM, {am, pm} HH.
+ * Returns a newly allocated string holding UTC time in HH:MM:SS format
+ * or NULL.
+ */
+char *
+localtime_to_utctime (const char *local_time)
+{
+  const char *formats_0[] = {" %I : %M : %S %p ", " %p %I : %M : %S ",
+                             " %H : %M : %S ",
+                             " %I : %M %p ", " %p %I : %M ",
+                             " %H : %M ",
+                             " %I %p ", " %p %I "};
+  const char *formats_1[] = {" %H : %M : %S ", " %H : %M "};
+  const char *end = NULL;
+  struct tm  *actual_local_time;
+  struct tm  *actual_utc_time;
+  struct tm   local_print_time;
+  struct tm   utc_print_time;
+  struct tm   diff_time;
+  char       *utc_time = NULL;
+  int         i, n;
+
+  if (local_time == NULL || local_time[0] == '\0')
+    return NULL;
+
+  n = supports_am_pm () ? G_N_ELEMENTS (formats_0) : G_N_ELEMENTS (formats_1);
+
+  for (i = 0; i < n; i++)
+    {
+      local_print_time.tm_hour = 0;
+      local_print_time.tm_min  = 0;
+      local_print_time.tm_sec  = 0;
+
+      if (supports_am_pm ())
+        end = strptime (local_time, formats_0[i], &local_print_time);
+      else
+        end = strptime (local_time, formats_1[i], &local_print_time);
+
+      if (end != NULL && end[0] == '\0')
+        break;
+    }
+
+  if (end != NULL && end[0] == '\0')
+    {
+      time_t rawtime;
+      time (&rawtime);
+
+      actual_utc_time = g_memdup2 (gmtime (&rawtime), sizeof (struct tm));
+      actual_local_time = g_memdup2 (localtime (&rawtime), sizeof (struct tm));
+
+      diff_time.tm_hour = actual_utc_time->tm_hour - actual_local_time->tm_hour;
+      diff_time.tm_min  = actual_utc_time->tm_min  - actual_local_time->tm_min;
+      diff_time.tm_sec  = actual_utc_time->tm_sec  - actual_local_time->tm_sec;
+
+      utc_print_time.tm_hour = ((local_print_time.tm_hour + diff_time.tm_hour) + 24) % 24;
+      utc_print_time.tm_min  = ((local_print_time.tm_min  + diff_time.tm_min)  + 60) % 60;
+      utc_print_time.tm_sec  = ((local_print_time.tm_sec  + diff_time.tm_sec)  + 60) % 60;
+
+      utc_time = g_strdup_printf ("%02d:%02d:%02d",
+                                  utc_print_time.tm_hour,
+                                  utc_print_time.tm_min,
+                                  utc_print_time.tm_sec);
+    }
+
+  return utc_time;
+}
+
+gboolean
+supports_am_pm (void)
+{
+  struct tm tmp_tm = { 0 };
+  char   time[8];
+  int    length;
+
+  length = strftime (time, sizeof (time), "%p", &tmp_tm);
+
+  return length != 0;
+}
+
+/*
+ * Generate a random string of "n" length
+ */
+char *
+random_string (int n)
+{
+  const char charset[] =  "abcdefghijklmnopqrstuvwxyz"
+                          "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+                          "0123456789";
+
+  char *str = g_new0 (char, n+1);
+  getrandom (str, n, 0);
+  for (int i=0; i<n; i++)
+    {
+      int rand = str[i] + 128;
+      int idx = rand % ((int) sizeof charset);
+      str[i] = charset[idx];
+    }
+  str[n] = '\0';
+
+  return str;
+}
\ No newline at end of file
diff --git a/modules/printbackends/gtkprintbackendutils.h b/modules/printbackends/gtkprintbackendutils.h
new file mode 100644 (file)
index 0000000..8c8a4f8
--- /dev/null
@@ -0,0 +1,27 @@
+/* GTK - The GIMP Toolkit
+ * gtkprintbackendcups.h: Default implementation of GtkPrintBackend
+ * for the Common Unix Print System (CUPS)
+ * Copyright (C) 2006, 2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GTK_PRINT_BACKEND_UTILS_H__
+#define __GTK_PRINT_BACKEND_UTILS_H__
+
+char *random_string                          (int size);
+char *localtime_to_utctime                   (const char *local_time);
+gboolean supports_am_pm                      (void);
+
+#endif /* __GTK_PRINT_BACKEND_UTILS_H__ */
\ No newline at end of file
diff --git a/modules/printbackends/gtkprintercpdb.c b/modules/printbackends/gtkprintercpdb.c
new file mode 100644 (file)
index 0000000..aaf13cb
--- /dev/null
@@ -0,0 +1,137 @@
+/* GtkPrinterCpdb
+ * Copyright (C) 2022, 2023 TinyTrebuchet <tinytrebuchet@protonmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <gtkprintercpdb.h>
+
+enum {
+  PROP_0,
+  PROP_PRINTER_OBJ
+};
+
+static void gtk_printer_cpdb_init       (GtkPrinterCpdb      *printer);
+static void gtk_printer_cpdb_class_init (GtkPrinterCpdbClass *class);
+
+static GType gtk_printer_cpdb_type = 0;
+
+static void gtk_printer_cpdb_set_property (GObject      *object,
+                                           guint         prop_id,
+                                           const GValue *value,
+                                           GParamSpec   *pspec);
+static void gtk_printer_cpdb_get_property (GObject      *object,
+                                           guint         prop_id,
+                                           GValue       *value,
+                                           GParamSpec   *pspec);
+
+void 
+gtk_printer_cpdb_register_type (GTypeModule *module)
+{
+  const GTypeInfo object_info =
+  {
+    sizeof (GtkPrinterCpdbClass),
+    (GBaseInitFunc) NULL,
+    (GBaseFinalizeFunc) NULL,
+    (GClassInitFunc) gtk_printer_cpdb_class_init,
+    NULL,           /* class_finalize */
+    NULL,           /* class_data */
+    sizeof (GtkPrinterCpdb),
+    0,              /* n_preallocs */
+    (GInstanceInitFunc) gtk_printer_cpdb_init,
+  };
+
+ gtk_printer_cpdb_type = g_type_module_register_type (module,
+                                                      GTK_TYPE_PRINTER,
+                                                      "GtkPrinterCpdb",
+                                                      &object_info, 0);
+}
+
+GType
+gtk_printer_cpdb_get_type (void)
+{
+  return gtk_printer_cpdb_type;
+}
+
+static void
+gtk_printer_cpdb_class_init (GtkPrinterCpdbClass *klass)
+{
+
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->set_property = gtk_printer_cpdb_set_property;
+  object_class->get_property = gtk_printer_cpdb_get_property;
+
+  g_object_class_install_property (G_OBJECT_CLASS (klass),
+                                   PROP_PRINTER_OBJ,
+                                   g_param_spec_pointer ("printer-obj", 
+                                                         NULL, 
+                                                         NULL,
+                                                         G_PARAM_READWRITE));
+}
+
+static void
+gtk_printer_cpdb_init (GtkPrinterCpdb *self)
+{
+}
+
+static void
+gtk_printer_cpdb_set_property (GObject         *object,
+                               guint            prop_id,
+                               const GValue    *value,
+                               GParamSpec      *pspec)
+{
+  GtkPrinterCpdb *printer_cpdb = GTK_PRINTER_CPDB (object);
+  switch (prop_id)
+    {
+    case PROP_PRINTER_OBJ:
+      printer_cpdb->printer_obj = g_value_get_pointer (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+gtk_printer_cpdb_get_property (GObject    *object,
+                               guint       prop_id,
+                               GValue     *value,
+                               GParamSpec *pspec)
+{
+  GtkPrinterCpdb *printer_cpdb = GTK_PRINTER_CPDB (object);
+  switch (prop_id)
+    {
+    case PROP_PRINTER_OBJ:
+      g_value_set_pointer (value, printer_cpdb->printer_obj);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+cpdb_printer_obj_t *
+gtk_printer_cpdb_get_printer_obj (GtkPrinterCpdb *cpdb_printer)
+{
+  return cpdb_printer->printer_obj;
+}
+
+void
+gtk_printer_cpdb_set_printer_obj (GtkPrinterCpdb *cpdb_printer,
+                                  cpdb_printer_obj_t *printer_obj)
+{
+  cpdb_printer->printer_obj = printer_obj;
+}
diff --git a/modules/printbackends/gtkprintercpdb.h b/modules/printbackends/gtkprintercpdb.h
new file mode 100644 (file)
index 0000000..bca0eeb
--- /dev/null
@@ -0,0 +1,46 @@
+/* GtkPrinterCpdb
+ * Copyright (C) 2022, 2023 TinyTrebuchet <tinytrebuchet@protonmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef __GTK_PRINTER_CPDB_H__
+#define __GTK_PRINTER_CPDB_H__
+
+#include <glib-object.h>
+#include <cpdb/cpdb-frontend.h>
+#include <gtk/gtkprinterprivate.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_PRINTER_CPDB           (gtk_printer_cpdb_get_type ())
+G_DECLARE_FINAL_TYPE                    (GtkPrinterCpdb, gtk_printer_cpdb, GTK, PRINTER_CPDB, GtkPrinter)
+
+struct _GtkPrinterCpdb
+{
+  GtkPrinter parent_instance;
+  cpdb_printer_obj_t *printer_obj;
+};
+
+void                  gtk_printer_cpdb_register_type          (GTypeModule          *module);
+
+cpdb_printer_obj_t   *gtk_printer_cpdb_get_printer_obj        (GtkPrinterCpdb       *cpdb_printer);
+
+void                  gtk_printer_cpdb_set_printer_obj        (GtkPrinterCpdb       *cpdb_printer,
+                                                               cpdb_printer_obj_t   *printer_obj);
+
+G_END_DECLS
+
+#endif /* __GTK_PRINTER_CPDB_H__ */
\ No newline at end of file
index 6e0188f62c67815e7e613318172de7e8cb82097d..de4effa520088f164f053d25f0b867f5adc94b3f 100644 (file)
@@ -10,8 +10,27 @@ printbackends_args = [
   '-DGTK_PRINT_BACKEND_ENABLE_UNSUPPORTED',
 ] + common_cflags
 
+cpdb_dep = dependency('cpdb-frontend', version : '>=1.0', required: get_option('print-cpdb'))
 cups_dep = dependency('cups', version : '>=2.0', required: get_option('print-cups'))
-if cups_dep.found()
+
+# Use cpdb backend if present and enabled.
+# If not, use cups if present.
+# If not, use lpr.
+
+if get_option('print-cpdb').enabled() and cpdb_dep.found()
+  print_backends += 'cpdb'
+  shared_module('printbackend-cpdb',
+    sources: [
+      'gtkprintbackendcpdb.c',
+      'gtkprintercpdb.c',
+      'gtkprintbackendutils.c',
+    ],
+    c_args: printbackends_args,
+    dependencies: [libgtk_dep, cpdb_dep],
+    install_dir: printbackends_install_dir,
+    install: true,
+  )
+elif cups_dep.found()
   print_backends += 'cups'
   shared_module('printbackend-cups',
     sources: [
@@ -19,6 +38,7 @@ if cups_dep.found()
       'gtkprintercups.c',
       'gtkcupsutils.c',
       'gtkcupssecretsutils.c',
+      'gtkprintbackendutils.c',
     ],
     c_args: printbackends_args,
     dependencies: [libgtk_dep, cups_dep, colord_dep],