1 /* GTK - The GIMP Toolkit
2 * gtkprintbackendcups.h: Default implementation of GtkPrintBackend
3 * for the Common Unix Print System (CUPS)
4 * Copyright (C) 2003, Red Hat, Inc.
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the
18 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 * Boston, MA 02111-1307, USA.
23 #include <sys/types.h>
28 #include <cups/cups.h>
29 #include <cups/language.h>
30 #include <cups/http.h>
34 #include <cairo-pdf.h>
37 #include <glib/gi18n-lib.h>
40 #include <gtk/gtkprintoperation.h>
41 #include <gtk/gtkprintsettings.h>
42 #include <gtk/gtkprintbackend.h>
43 #include <gtk/gtkprinter.h>
45 #include "gtkprintbackendcups.h"
46 #include "gtkprintercups.h"
48 #include "gtkcupsutils.h"
51 typedef struct _GtkPrintBackendCupsClass GtkPrintBackendCupsClass;
53 #define GTK_PRINT_BACKEND_CUPS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_PRINT_BACKEND_CUPS, GtkPrintBackendCupsClass))
54 #define GTK_IS_PRINT_BACKEND_CUPS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_PRINT_BACKEND_CUPS))
55 #define GTK_PRINT_BACKEND_CUPS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_PRINT_BACKEND_CUPS, GtkPrintBackendCupsClass))
57 #define _CUPS_MAX_ATTEMPTS 10
58 #define _CUPS_MAX_CHUNK_SIZE 8192
60 #define _CUPS_MAP_ATTR_INT(attr, v, a) {if (!g_ascii_strcasecmp (attr->name, (a))) v = attr->values[0].integer;}
61 #define _CUPS_MAP_ATTR_STR(attr, v, a) {if (!g_ascii_strcasecmp (attr->name, (a))) v = g_strdup (attr->values[0].string.text);}
63 static GType print_backend_cups_type = 0;
65 typedef void (* GtkPrintCupsResponseCallbackFunc) (GtkPrintBackend *print_backend,
66 GtkCupsResult *result,
77 } GtkPrintCupsDispatchState;
84 GtkCupsRequest *request;
86 GtkPrintBackendCups *backend;
88 } GtkPrintCupsDispatchWatch;
90 struct _GtkPrintBackendCupsClass
92 GObjectClass parent_class;
95 struct _GtkPrintBackendCups
97 GObject parent_instance;
101 char *default_printer;
103 guint list_printers_poll;
104 guint list_printers_pending : 1;
105 guint got_default_printer : 1;
108 static GObjectClass *backend_parent_class;
110 static void gtk_print_backend_cups_class_init (GtkPrintBackendCupsClass *class);
111 static void gtk_print_backend_cups_iface_init (GtkPrintBackendIface *iface);
112 static void gtk_print_backend_cups_init (GtkPrintBackendCups *impl);
113 static void gtk_print_backend_cups_finalize (GObject *object);
114 static GList * cups_get_printer_list (GtkPrintBackend *print_backend);
115 static void cups_request_execute (GtkPrintBackendCups *print_backend,
116 GtkCupsRequest *request,
117 GtkPrintCupsResponseCallbackFunc callback,
119 GDestroyNotify notify,
121 static void cups_printer_get_settings_from_options (GtkPrinter *printer,
122 GtkPrinterOptionSet *options,
123 GtkPrintSettings *settings);
124 static gboolean cups_printer_mark_conflicts (GtkPrinter *printer,
125 GtkPrinterOptionSet *options);
126 static GtkPrinterOptionSet *cups_printer_get_options (GtkPrinter *printer,
127 GtkPrintSettings *settings,
128 GtkPageSetup *page_setup);
129 static void cups_printer_prepare_for_print (GtkPrinter *printer,
130 GtkPrintJob *print_job,
131 GtkPrintSettings *settings,
132 GtkPageSetup *page_setup);
133 static GList * cups_printer_list_papers (GtkPrinter *printer);
134 static void cups_printer_request_details (GtkPrinter *printer);
135 static void cups_request_default_printer (GtkPrintBackendCups *print_backend);
136 static void cups_request_ppd (GtkPrinter *printer);
137 static void cups_printer_get_hard_margins (GtkPrinter *printer,
142 static void set_option_from_settings (GtkPrinterOption *option,
143 GtkPrintSettings *setting);
144 static void cups_begin_polling_info (GtkPrintBackendCups *print_backend,
147 static gboolean cups_job_info_poll_timeout (gpointer user_data);
150 gtk_print_backend_cups_register_type (GTypeModule *module)
152 if (!print_backend_cups_type)
154 static const GTypeInfo print_backend_cups_info =
156 sizeof (GtkPrintBackendCupsClass),
157 NULL, /* base_init */
158 NULL, /* base_finalize */
159 (GClassInitFunc) gtk_print_backend_cups_class_init,
160 NULL, /* class_finalize */
161 NULL, /* class_data */
162 sizeof (GtkPrintBackendCups),
164 (GInstanceInitFunc) gtk_print_backend_cups_init
167 static const GInterfaceInfo print_backend_info =
169 (GInterfaceInitFunc) gtk_print_backend_cups_iface_init, /* interface_init */
170 NULL, /* interface_finalize */
171 NULL /* interface_data */
174 print_backend_cups_type = g_type_module_register_type (module,
176 "GtkPrintBackendCups",
177 &print_backend_cups_info, 0);
178 g_type_module_add_interface (module,
179 print_backend_cups_type,
180 GTK_TYPE_PRINT_BACKEND,
181 &print_backend_info);
186 pb_module_init (GTypeModule *module)
188 gtk_print_backend_cups_register_type (module);
189 gtk_printer_cups_register_type (module);
193 pb_module_exit (void)
198 G_MODULE_EXPORT GtkPrintBackend *
199 pb_module_create (void)
201 return gtk_print_backend_cups_new ();
205 * GtkPrintBackendCups
208 gtk_print_backend_cups_get_type (void)
210 return print_backend_cups_type;
214 * gtk_print_backend_cups_new:
216 * Creates a new #GtkPrintBackendCups object. #GtkPrintBackendCups
217 * implements the #GtkPrintBackend interface with direct access to
218 * the filesystem using Unix/Linux API calls
220 * Return value: the new #GtkPrintBackendCups object
223 gtk_print_backend_cups_new (void)
225 return g_object_new (GTK_TYPE_PRINT_BACKEND_CUPS, NULL);
229 gtk_print_backend_cups_class_init (GtkPrintBackendCupsClass *class)
231 GObjectClass *gobject_class = G_OBJECT_CLASS (class);
233 backend_parent_class = g_type_class_peek_parent (class);
235 gobject_class->finalize = gtk_print_backend_cups_finalize;
238 static cairo_status_t
239 _cairo_write_to_cups (void *cache_fd_as_pointer,
240 const unsigned char *data,
243 cairo_status_t result;
245 cache_fd = GPOINTER_TO_INT (cache_fd_as_pointer);
247 result = CAIRO_STATUS_WRITE_ERROR;
249 /* write out the buffer */
250 if (write (cache_fd, data, length) != -1)
251 result = CAIRO_STATUS_SUCCESS;
257 static cairo_surface_t *
258 cups_printer_create_cairo_surface (GtkPrinter *printer,
263 cairo_surface_t *surface;
265 /* TODO: check if it is a ps or pdf printer */
267 surface = cairo_ps_surface_create_for_stream (_cairo_write_to_cups, GINT_TO_POINTER (cache_fd), width, height);
269 /* TODO: DPI from settings object? */
270 cairo_ps_surface_set_dpi (surface, 300, 300);
276 gtk_print_backend_cups_find_printer (GtkPrintBackend *print_backend,
277 const gchar *printer_name)
279 GtkPrintBackendCups *cups_print_backend;
281 cups_print_backend = GTK_PRINT_BACKEND_CUPS (print_backend);
283 return (GtkPrinter *) g_hash_table_lookup (cups_print_backend->printers,
288 GtkPrintJobCompleteFunc callback;
291 GDestroyNotify dnotify;
292 } CupsPrintStreamData;
295 cups_free_print_stream_data (CupsPrintStreamData *data)
298 data->dnotify (data->user_data);
299 g_object_unref (data->job);
304 cups_print_cb (GtkPrintBackendCups *print_backend,
305 GtkCupsResult *result,
308 GError *error = NULL;
309 CupsPrintStreamData *ps = user_data;
311 if (gtk_cups_result_is_error (result))
312 error = g_error_new_literal (gtk_print_error_quark (),
313 GTK_PRINT_ERROR_INTERNAL_ERROR,
314 gtk_cups_result_get_error_string (result));
317 ps->callback (ps->job, ps->user_data, error);
322 ipp_attribute_t *attr; /* IPP job-id attribute */
323 ipp_t *response = gtk_cups_result_get_response (result);
325 if ((attr = ippFindAttribute(response, "job-id", IPP_TAG_INTEGER)) != NULL)
326 job_id = attr->values[0].integer;
330 gtk_print_job_set_status (ps->job, GTK_PRINT_STATUS_FINISHED);
333 gtk_print_job_set_status (ps->job, GTK_PRINT_STATUS_PENDING);
334 cups_begin_polling_info (print_backend, ps->job, job_id);
338 gtk_print_job_set_status (ps->job, GTK_PRINT_STATUS_FINISHED_ABORTED);
342 g_error_free (error);
347 add_cups_options (const char *key,
351 GtkCupsRequest *request = user_data;
353 if (!g_str_has_prefix (key, "cups-"))
356 if (strcmp (value, "gtk-ignore-value") == 0)
359 key = key + strlen("cups-");
361 gtk_cups_request_encode_option (request, key, value);
365 gtk_print_backend_cups_print_stream (GtkPrintBackend *print_backend,
368 GtkPrintJobCompleteFunc callback,
370 GDestroyNotify dnotify)
373 GtkPrinterCups *cups_printer;
374 CupsPrintStreamData *ps;
375 GtkCupsRequest *request;
376 GtkPrintSettings *settings;
379 cups_printer = GTK_PRINTER_CUPS (gtk_print_job_get_printer (job));
380 settings = gtk_print_job_get_settings (job);
384 request = gtk_cups_request_new (NULL,
389 cups_printer->device_uri);
391 gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
392 NULL, cups_printer->printer_uri);
394 gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
397 title = gtk_print_job_get_title (job);
399 gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name", NULL,
402 gtk_print_settings_foreach (settings, add_cups_options, request);
404 ps = g_new0 (CupsPrintStreamData, 1);
405 ps->callback = callback;
406 ps->user_data = user_data;
407 ps->dnotify = dnotify;
408 ps->job = g_object_ref (job);
410 cups_request_execute (GTK_PRINT_BACKEND_CUPS (print_backend),
412 (GtkPrintCupsResponseCallbackFunc) cups_print_cb,
414 (GDestroyNotify)cups_free_print_stream_data,
420 gtk_print_backend_cups_iface_init (GtkPrintBackendIface *iface)
422 iface->get_printer_list = cups_get_printer_list;
423 iface->find_printer = gtk_print_backend_cups_find_printer;
424 iface->print_stream = gtk_print_backend_cups_print_stream;
425 iface->printer_request_details = cups_printer_request_details;
426 iface->printer_create_cairo_surface = cups_printer_create_cairo_surface;
427 iface->printer_get_options = cups_printer_get_options;
428 iface->printer_mark_conflicts = cups_printer_mark_conflicts;
429 iface->printer_get_settings_from_options = cups_printer_get_settings_from_options;
430 iface->printer_prepare_for_print = cups_printer_prepare_for_print;
431 iface->printer_list_papers = cups_printer_list_papers;
432 iface->printer_get_hard_margins = cups_printer_get_hard_margins;
436 gtk_print_backend_cups_init (GtkPrintBackendCups *backend_cups)
438 backend_cups->list_printers_poll = 0;
439 backend_cups->list_printers_pending = FALSE;
440 backend_cups->printers = g_hash_table_new_full (g_str_hash,
442 (GDestroyNotify) g_free,
443 (GDestroyNotify) g_object_unref);
445 cups_request_default_printer (backend_cups);
449 gtk_print_backend_cups_finalize (GObject *object)
451 GtkPrintBackendCups *backend_cups;
453 backend_cups = GTK_PRINT_BACKEND_CUPS (object);
455 if (backend_cups->list_printers_poll > 0)
456 g_source_remove (backend_cups->list_printers_poll);
458 if (backend_cups->printers)
459 g_hash_table_unref (backend_cups->printers);
461 g_free (backend_cups->default_printer);
462 backend_cups->default_printer = NULL;
464 backend_parent_class->finalize (object);
468 cups_dispatch_watch_check (GSource *source)
470 GtkPrintCupsDispatchWatch *dispatch;
471 GtkCupsPollState poll_state;
474 dispatch = (GtkPrintCupsDispatchWatch *) source;
476 poll_state = gtk_cups_request_get_poll_state (dispatch->request);
478 if (dispatch->data_poll == NULL &&
479 dispatch->request->http != NULL)
481 dispatch->data_poll = g_new0 (GPollFD, 1);
482 dispatch->data_poll->fd = dispatch->request->http->fd;
484 g_source_add_poll (source, dispatch->data_poll);
487 if (dispatch->data_poll != NULL && dispatch->request->http != NULL)
489 if (dispatch->data_poll->fd != dispatch->request->http->fd)
490 dispatch->data_poll->fd = dispatch->request->http->fd;
492 if (poll_state == GTK_CUPS_HTTP_READ)
493 dispatch->data_poll->events = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_PRI;
494 else if (poll_state == GTK_CUPS_HTTP_WRITE)
495 dispatch->data_poll->events = G_IO_OUT | G_IO_ERR;
497 dispatch->data_poll->events = 0;
500 if (poll_state != GTK_CUPS_HTTP_IDLE)
501 if (!(dispatch->data_poll->revents & dispatch->data_poll->events))
504 result = gtk_cups_request_read_write (dispatch->request);
505 if (result && dispatch->data_poll != NULL)
507 g_source_remove_poll (source, dispatch->data_poll);
508 g_free (dispatch->data_poll);
509 dispatch->data_poll = NULL;
516 cups_dispatch_watch_prepare (GSource *source,
519 GtkPrintCupsDispatchWatch *dispatch;
521 dispatch = (GtkPrintCupsDispatchWatch *) source;
526 return gtk_cups_request_read_write (dispatch->request);
530 cups_dispatch_watch_dispatch (GSource *source,
531 GSourceFunc callback,
534 GtkPrintCupsDispatchWatch *dispatch;
535 GtkPrintCupsResponseCallbackFunc ep_callback;
536 GtkCupsResult *result;
538 g_assert (callback != NULL);
540 ep_callback = (GtkPrintCupsResponseCallbackFunc) callback;
542 dispatch = (GtkPrintCupsDispatchWatch *) source;
544 result = gtk_cups_request_get_result (dispatch->request);
546 if (gtk_cups_result_is_error (result))
547 g_warning (gtk_cups_result_get_error_string (result));
549 ep_callback (GTK_PRINT_BACKEND (dispatch->backend), result, user_data);
551 g_source_unref (source);
556 cups_dispatch_watch_finalize (GSource *source)
558 GtkPrintCupsDispatchWatch *dispatch;
560 dispatch = (GtkPrintCupsDispatchWatch *) source;
562 gtk_cups_request_free (dispatch->request);
564 if (dispatch->backend)
566 g_object_unref (dispatch->backend);
567 dispatch->backend = NULL;
570 if (dispatch->data_poll != NULL)
571 g_free (dispatch->data_poll);
574 static GSourceFuncs _cups_dispatch_watch_funcs = {
575 cups_dispatch_watch_prepare,
576 cups_dispatch_watch_check,
577 cups_dispatch_watch_dispatch,
578 cups_dispatch_watch_finalize
583 cups_request_execute (GtkPrintBackendCups *print_backend,
584 GtkCupsRequest *request,
585 GtkPrintCupsResponseCallbackFunc callback,
587 GDestroyNotify notify,
590 GtkPrintCupsDispatchWatch *dispatch;
592 dispatch = (GtkPrintCupsDispatchWatch *) g_source_new (&_cups_dispatch_watch_funcs,
593 sizeof (GtkPrintCupsDispatchWatch));
595 dispatch->request = request;
596 dispatch->backend = g_object_ref (print_backend);
597 dispatch->data_poll = NULL;
599 g_source_set_callback ((GSource *) dispatch, (GSourceFunc) callback, user_data, notify);
601 g_source_attach ((GSource *) dispatch, NULL);
605 cups_request_printer_info_cb (GtkPrintBackendCups *print_backend,
606 GtkCupsResult *result,
609 ipp_attribute_t *attr;
612 GtkPrinterCups *cups_printer;
615 gchar *member_printer_uri;
621 char uri[HTTP_MAX_URI], /* Printer URI */
622 method[HTTP_MAX_URI], /* Method/scheme name */
623 username[HTTP_MAX_URI], /* Username:password */
624 hostname[HTTP_MAX_URI], /* Hostname */
625 resource[HTTP_MAX_URI]; /* Resource name */
626 int port; /* Port number */
627 gboolean status_changed;
629 g_assert (GTK_IS_PRINT_BACKEND_CUPS (print_backend));
632 member_printer_uri = NULL;
634 printer_name = (gchar *)user_data;
635 cups_printer = (GtkPrinterCups *) g_hash_table_lookup (print_backend->printers, printer_name);
640 printer = GTK_PRINTER (cups_printer);
642 if (gtk_cups_result_is_error (result))
644 if (gtk_printer_is_new (printer))
646 g_hash_table_remove (print_backend->printers,
651 return; /* TODO: mark as inactive printer */
654 response = gtk_cups_result_get_response (result);
656 /* TODO: determine printer type and use correct icon */
657 gtk_printer_set_icon_name (printer, "printer");
659 cups_printer->device_uri = g_strdup_printf ("/printers/%s", printer_name);
665 for (attr = response->attrs; attr != NULL; attr = attr->next)
670 _CUPS_MAP_ATTR_STR (attr, loc, "printer-location");
671 _CUPS_MAP_ATTR_STR (attr, desc, "printer-info");
672 _CUPS_MAP_ATTR_STR (attr, state_msg, "printer-state-message");
673 _CUPS_MAP_ATTR_STR (attr, printer_uri, "printer-uri-supported");
674 _CUPS_MAP_ATTR_STR (attr, member_printer_uri, "member-uris");
675 _CUPS_MAP_ATTR_INT (attr, cups_printer->state, "printer-state");
676 _CUPS_MAP_ATTR_INT (attr, job_count, "queued-job-count");
679 /* if we got a member_printer_uri then this printer is part of a class
680 so use member_printer_uri, else user printer_uri */
682 if (cups_printer->printer_uri)
683 g_free (cups_printer->printer_uri);
685 if (member_printer_uri)
687 g_free (printer_uri);
688 cups_printer->printer_uri = member_printer_uri;
691 cups_printer->printer_uri = printer_uri;
693 status_changed = gtk_printer_set_job_count (printer, job_count);
695 status_changed |= gtk_printer_set_location (printer, loc);
696 status_changed |= gtk_printer_set_description (printer, desc);
697 status_changed |= gtk_printer_set_state_message (printer, state_msg);
699 #if (CUPS_VERSION_MAJOR == 1 && CUPS_VERSION_MINOR >= 2) || CUPS_VERSION_MAJOR > 1
700 httpSeparateURI (HTTP_URI_CODING_ALL, cups_printer->printer_uri,
701 method, sizeof (method),
702 username, sizeof (username),
703 hostname, sizeof (hostname),
705 resource, sizeof (resource));
708 httpSeparate (cups_printer->printer_uri,
716 gethostname(uri, sizeof(uri));
718 if (strcasecmp(uri, hostname) == 0)
719 strcpy(hostname, "localhost");
721 if (cups_printer->hostname)
722 g_free (cups_printer->hostname);
724 cups_printer->hostname = g_strdup (hostname);
725 cups_printer->port = port;
728 g_signal_emit_by_name (GTK_PRINT_BACKEND (print_backend), "printer-status-changed", printer);
732 cups_request_printer_info (GtkPrintBackendCups *print_backend,
733 const gchar *printer_name)
736 GtkCupsRequest *request;
741 request = gtk_cups_request_new (NULL,
743 IPP_GET_PRINTER_ATTRIBUTES,
748 printer_uri = g_strdup_printf ("ipp://localhost/printers/%s",
750 gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_URI,
751 "printer-uri", NULL, printer_uri);
753 g_free (printer_uri);
755 cups_request_execute (print_backend,
757 (GtkPrintCupsResponseCallbackFunc) cups_request_printer_info_cb,
758 g_strdup (printer_name),
759 (GDestroyNotify) g_free,
766 GtkPrintBackendCups *print_backend;
773 job_object_died (gpointer user_data,
774 GObject *where_the_object_was)
776 CupsJobPollData *data = user_data;
781 cups_job_poll_data_free (CupsJobPollData *data)
784 g_object_weak_unref (G_OBJECT (data->job), job_object_died, data);
790 cups_request_job_info_cb (GtkPrintBackendCups *print_backend,
791 GtkCupsResult *result,
794 CupsJobPollData *data = user_data;
795 ipp_attribute_t *attr;
800 if (data->job == NULL)
802 cups_job_poll_data_free (data);
808 response = gtk_cups_result_get_response (result);
811 for (attr = response->attrs; attr != NULL; attr = attr->next)
816 _CUPS_MAP_ATTR_INT (attr, state, "job-state");
822 case IPP_JOB_PENDING:
824 case IPP_JOB_STOPPED:
825 gtk_print_job_set_status (data->job,
826 GTK_PRINT_STATUS_PENDING);
828 case IPP_JOB_PROCESSING:
829 gtk_print_job_set_status (data->job,
830 GTK_PRINT_STATUS_PRINTING);
833 case IPP_JOB_CANCELLED:
834 case IPP_JOB_ABORTED:
835 gtk_print_job_set_status (data->job,
836 GTK_PRINT_STATUS_FINISHED_ABORTED);
840 case IPP_JOB_COMPLETED:
841 gtk_print_job_set_status (data->job,
842 GTK_PRINT_STATUS_FINISHED);
847 if (!done && data->job != NULL)
851 if (data->counter < 5)
853 else if (data->counter < 10)
858 g_timeout_add (timeout, cups_job_info_poll_timeout, data);
861 cups_job_poll_data_free (data);
865 cups_request_job_info (CupsJobPollData *data)
868 GtkCupsRequest *request;
873 request = gtk_cups_request_new (NULL,
875 IPP_GET_JOB_ATTRIBUTES,
880 printer_uri = g_strdup_printf ("ipp://localhost/jobs/%d", data->job_id);
881 gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_URI,
882 "job-uri", NULL, printer_uri);
883 g_free (printer_uri);
885 cups_request_execute (data->print_backend,
887 (GtkPrintCupsResponseCallbackFunc) cups_request_job_info_cb,
894 cups_job_info_poll_timeout (gpointer user_data)
896 CupsJobPollData *data = user_data;
898 if (data->job == NULL)
899 cups_job_poll_data_free (data);
901 cups_request_job_info (data);
907 cups_begin_polling_info (GtkPrintBackendCups *print_backend,
911 CupsJobPollData *data;
913 data = g_new0 (CupsJobPollData, 1);
915 data->print_backend = print_backend;
917 data->job_id = job_id;
920 g_object_weak_ref (G_OBJECT (job), job_object_died, data);
922 cups_request_job_info (data);
926 printer_cmp (GtkPrinter *a, GtkPrinter *b)
928 const char *name_a, *name_b;
929 g_assert (GTK_IS_PRINTER (a) && GTK_IS_PRINTER (b));
931 name_a = gtk_printer_get_name (a);
932 name_b = gtk_printer_get_name (b);
933 if (name_a == NULL && name_b == NULL)
935 else if (name_a == NULL)
937 else if (name_b == NULL)
940 return g_ascii_strcasecmp (name_a, name_b);
944 printer_hash_to_sorted_active_list (const gchar *key,
950 printer = GTK_PRINTER (value);
952 if (gtk_printer_get_name (printer) == NULL)
955 if (!gtk_printer_is_active (printer))
958 *out_list = g_list_insert_sorted (*out_list, value, (GCompareFunc) printer_cmp);
962 printer_hash_to_sorted_active_name_list (const gchar *key,
968 printer = GTK_PRINTER (value);
971 if (gtk_printer_get_name (printer) == NULL)
974 if (!gtk_printer_is_active (printer))
977 if (gtk_printer_is_active (printer))
978 *out_list = g_list_insert_sorted (*out_list,
979 (char *)gtk_printer_get_name (printer),
984 mark_printer_inactive (const gchar *printer_name,
985 GtkPrintBackendCups *cups_backend)
988 GHashTable *printer_hash;
990 printer_hash = cups_backend->printers;
992 printer = (GtkPrinter *) g_hash_table_lookup (printer_hash,
998 gtk_printer_set_is_active (printer, FALSE);
1000 g_signal_emit_by_name (GTK_PRINT_BACKEND (cups_backend), "printer-removed", printer);
1004 cups_request_printer_list_cb (GtkPrintBackendCups *cups_backend,
1005 GtkCupsResult *result,
1008 ipp_attribute_t *attr;
1010 gboolean list_has_changed;
1011 GList *removed_printer_checklist;
1013 list_has_changed = FALSE;
1015 g_assert (GTK_IS_PRINT_BACKEND_CUPS (cups_backend));
1017 cups_backend->list_printers_pending = FALSE;
1019 if (gtk_cups_result_is_error (result))
1021 g_warning ("Error getting printer list: %s", gtk_cups_result_get_error_string (result));
1025 /* gether the names of the printers in the current queue
1026 so we may check to see if they were removed */
1027 removed_printer_checklist = NULL;
1028 if (cups_backend->printers != NULL)
1029 g_hash_table_foreach (cups_backend->printers,
1030 (GHFunc) printer_hash_to_sorted_active_name_list,
1031 &removed_printer_checklist);
1033 response = gtk_cups_result_get_response (result);
1035 attr = ippFindAttribute (response, "printer-name", IPP_TAG_NAME);
1039 GtkPrinterCups *cups_printer;
1040 GtkPrinter *printer;
1041 const gchar *printer_name;
1044 printer_name = attr->values[0].string.text;
1045 /* remove name from checklist if it was found */
1046 node = g_list_find_custom (removed_printer_checklist, printer_name, (GCompareFunc) g_ascii_strcasecmp);
1047 removed_printer_checklist = g_list_delete_link (removed_printer_checklist, node);
1049 cups_printer = (GtkPrinterCups *) g_hash_table_lookup (cups_backend->printers,
1051 printer = cups_printer ? GTK_PRINTER (cups_printer) : NULL;
1055 list_has_changed = TRUE;
1056 cups_printer = gtk_printer_cups_new (attr->values[0].string.text,
1057 GTK_PRINT_BACKEND (cups_backend));
1058 printer = GTK_PRINTER (cups_printer);
1060 if (cups_backend->default_printer != NULL &&
1061 strcmp (cups_backend->default_printer, gtk_printer_get_name (printer)) == 0)
1062 gtk_printer_set_is_default (printer, TRUE);
1064 g_hash_table_insert (cups_backend->printers,
1065 g_strdup (gtk_printer_get_name (printer)),
1069 if (!gtk_printer_is_active (printer))
1071 gtk_printer_set_is_active (printer, TRUE);
1072 gtk_printer_set_is_new (printer, TRUE);
1073 list_has_changed = TRUE;
1076 if (gtk_printer_is_new (printer))
1078 g_signal_emit_by_name (GTK_PRINT_BACKEND (cups_backend),
1082 gtk_printer_set_is_new (printer, FALSE);
1085 cups_request_printer_info (cups_backend, gtk_printer_get_name (printer));
1087 attr = ippFindNextAttribute (response,
1092 /* look at the removed printers checklist and mark any printer
1093 as inactive if it is in the list, emitting a printer_removed signal */
1095 if (removed_printer_checklist != NULL)
1097 g_list_foreach (removed_printer_checklist, (GFunc) mark_printer_inactive, cups_backend);
1098 g_list_free (removed_printer_checklist);
1099 list_has_changed = TRUE;
1102 if (list_has_changed)
1103 g_signal_emit_by_name (GTK_PRINT_BACKEND (cups_backend), "printer-list-changed");
1108 cups_request_printer_list (GtkPrintBackendCups *cups_backend)
1111 GtkCupsRequest *request;
1113 if (cups_backend->list_printers_pending ||
1114 !cups_backend->got_default_printer)
1117 cups_backend->list_printers_pending = TRUE;
1121 request = gtk_cups_request_new (NULL,
1128 cups_request_execute (cups_backend,
1130 (GtkPrintCupsResponseCallbackFunc) cups_request_printer_list_cb,
1140 cups_get_printer_list (GtkPrintBackend *print_backend)
1142 GtkPrintBackendCups *cups_backend;
1145 cups_backend = GTK_PRINT_BACKEND_CUPS (print_backend);
1148 if (cups_backend->printers != NULL)
1149 g_hash_table_foreach (cups_backend->printers,
1150 (GHFunc) printer_hash_to_sorted_active_list,
1153 if (cups_backend->list_printers_poll == 0)
1155 cups_request_printer_list (cups_backend);
1156 cups_backend->list_printers_poll = g_timeout_add (3000,
1157 (GSourceFunc) cups_request_printer_list,
1165 GtkPrinterCups *printer;
1167 gchar *ppd_filename;
1171 get_ppd_data_free (GetPPDData *data)
1173 close (data->ppd_fd);
1174 unlink (data->ppd_filename);
1175 g_free (data->ppd_filename);
1176 g_object_unref (data->printer);
1181 cups_request_ppd_cb (GtkPrintBackendCups *print_backend,
1182 GtkCupsResult *result,
1186 GtkPrinter *printer;
1188 printer = GTK_PRINTER (data->printer);
1189 GTK_PRINTER_CUPS (printer)->reading_ppd = FALSE;
1191 if (gtk_cups_result_is_error (result))
1193 g_signal_emit_by_name (printer, "details-acquired", printer, FALSE);
1197 response = gtk_cups_result_get_response (result);
1199 data->printer->ppd_file = ppdOpenFile (data->ppd_filename);
1200 gtk_printer_set_has_details (printer, TRUE);
1201 g_signal_emit_by_name (printer, "details-acquired", printer, TRUE);
1205 cups_request_ppd (GtkPrinter *printer)
1208 GtkPrintBackend *print_backend;
1209 GtkPrinterCups *cups_printer;
1210 GtkCupsRequest *request;
1215 cups_printer = GTK_PRINTER_CUPS (printer);
1219 http = httpConnectEncrypt(cups_printer->hostname,
1223 data = g_new0 (GetPPDData, 1);
1225 data->ppd_fd = g_file_open_tmp ("gtkprint_ppd_XXXXXX",
1226 &data->ppd_filename,
1231 g_warning ("%s", error->message);
1232 g_error_free (error);
1236 g_signal_emit_by_name (printer, "details-acquired", printer, FALSE);
1240 fchmod (data->ppd_fd, S_IRUSR | S_IWUSR);
1242 data->printer = g_object_ref (printer);
1244 resource = g_strdup_printf ("/printers/%s.ppd", gtk_printer_get_name (printer));
1245 request = gtk_cups_request_new (http,
1249 cups_printer->hostname,
1254 cups_printer->reading_ppd = TRUE;
1256 print_backend = gtk_printer_get_backend (printer);
1258 cups_request_execute (GTK_PRINT_BACKEND_CUPS (print_backend),
1260 (GtkPrintCupsResponseCallbackFunc) cups_request_ppd_cb,
1262 (GDestroyNotify)get_ppd_data_free,
1268 cups_request_default_printer_cb (GtkPrintBackendCups *print_backend,
1269 GtkCupsResult *result,
1273 ipp_attribute_t *attr;
1275 response = gtk_cups_result_get_response (result);
1277 if ((attr = ippFindAttribute(response, "printer-name", IPP_TAG_NAME)) != NULL)
1278 print_backend->default_printer = g_strdup (attr->values[0].string.text);
1280 print_backend->got_default_printer = TRUE;
1282 /* Make sure to kick off get_printers if we are polling it, as we could
1283 have blocked this reading the default printer */
1284 if (print_backend->list_printers_poll != 0)
1285 cups_request_printer_list (print_backend);
1289 cups_request_default_printer (GtkPrintBackendCups *print_backend)
1292 GtkCupsRequest *request;
1297 if ((str = getenv("LPDEST")) != NULL)
1299 print_backend->default_printer = g_strdup (str);
1300 print_backend->got_default_printer = TRUE;
1303 else if ((str = getenv("PRINTER")) != NULL &&
1304 strcmp(str, "lp") != 0)
1306 print_backend->default_printer = g_strdup (str);
1307 print_backend->got_default_printer = TRUE;
1311 request = gtk_cups_request_new (NULL,
1318 cups_request_execute (print_backend,
1320 (GtkPrintCupsResponseCallbackFunc) cups_request_default_printer_cb,
1321 g_object_ref (print_backend),
1328 cups_printer_request_details (GtkPrinter *printer)
1330 GtkPrinterCups *cups_printer;
1332 cups_printer = GTK_PRINTER_CUPS (printer);
1333 if (!cups_printer->reading_ppd &&
1334 gtk_printer_cups_get_ppd (cups_printer) == NULL)
1335 cups_request_ppd (printer);
1339 ppd_text_to_utf8 (ppd_file_t *ppd_file, const char *text)
1341 const char *encoding = NULL;
1344 if (g_ascii_strcasecmp (ppd_file->lang_encoding, "UTF-8") == 0)
1346 return g_strdup (text);
1348 else if (g_ascii_strcasecmp (ppd_file->lang_encoding, "ISOLatin1") == 0)
1350 encoding = "ISO-8859-1";
1352 else if (g_ascii_strcasecmp (ppd_file->lang_encoding, "ISOLatin2") == 0)
1354 encoding = "ISO-8859-2";
1356 else if (g_ascii_strcasecmp (ppd_file->lang_encoding, "ISOLatin5") == 0)
1358 encoding = "ISO-8859-5";
1360 else if (g_ascii_strcasecmp (ppd_file->lang_encoding, "JIS83-RKSJ") == 0)
1362 encoding = "SHIFT-JIS";
1364 else if (g_ascii_strcasecmp (ppd_file->lang_encoding, "MacStandard") == 0)
1366 encoding = "MACINTOSH";
1368 else if (g_ascii_strcasecmp (ppd_file->lang_encoding, "WindowsANSI") == 0)
1370 encoding = "WINDOWS-1252";
1374 /* Fallback, try iso-8859-1... */
1375 encoding = "ISO-8859-1";
1378 res = g_convert (text, -1, "UTF-8", encoding, NULL, NULL, NULL);
1382 g_warning ("unable to convert PPD text");
1383 res = g_strdup ("???");
1389 /* TODO: Add more translations for common settings here */
1391 static const struct {
1392 const char *keyword;
1393 const char *translation;
1394 } cups_option_translations[] = {
1395 { "Duplex", N_("Two Sided") },
1399 static const struct {
1400 const char *keyword;
1402 const char *translation;
1403 } cups_choice_translations[] = {
1404 { "Duplex", "None", N_("One Sided") },
1405 { "InputSlot", "Auto", N_("Auto Select") },
1406 { "InputSlot", "AutoSelect", N_("Auto Select") },
1407 { "InputSlot", "Default", N_("Printer Default") },
1408 { "InputSlot", "None", N_("Printer Default") },
1409 { "InputSlot", "PrinterDefault", N_("Printer Default") },
1410 { "InputSlot", "Unspecified", N_("Auto Select") },
1413 static const struct {
1414 const char *ppd_keyword;
1416 } option_names[] = {
1417 {"Duplex", "gtk-duplex" },
1418 {"MediaType", "gtk-paper-type"},
1419 {"InputSlot", "gtk-paper-source"},
1420 {"OutputBin", "gtk-output-tray"},
1423 /* keep sorted when changing */
1424 static const char *color_option_whitelist[] = {
1425 "BRColorEnhancement",
1431 "BlackSubstitution",
1439 "RPSBlackOverPrint",
1443 /* keep sorted when changing */
1444 static const char *color_group_whitelist[] = {
1451 "HPColorOptionsPanel",
1454 /* keep sorted when changing */
1455 static const char *image_quality_option_whitelist[] = {
1457 "BRHalfTonePattern",
1467 "HPGraphicsHalftone",
1482 /* keep sorted when changing */
1483 static const char *image_quality_group_whitelist[] = {
1490 /* keep sorted when changing */
1491 static const char * finishing_option_whitelist[] = {
1504 "StapleOrientation",
1510 /* keep sorted when changing */
1511 static const char *finishing_group_whitelist[] = {
1520 /* keep sorted when changing */
1521 static const char *cups_option_blacklist[] = {
1530 get_option_text (ppd_file_t *ppd_file, ppd_option_t *option)
1535 for (i = 0; i < G_N_ELEMENTS (cups_option_translations); i++)
1537 if (strcmp (cups_option_translations[i].keyword, option->keyword) == 0)
1538 return g_strdup (_(cups_option_translations[i].translation));
1541 utf8 = ppd_text_to_utf8 (ppd_file, option->text);
1543 /* Some ppd files have spaces in the text before the colon */
1550 get_choice_text (ppd_file_t *ppd_file, ppd_choice_t *choice)
1553 ppd_option_t *option = choice->option;
1554 const char *keyword = option->keyword;
1556 for (i = 0; i < G_N_ELEMENTS (cups_choice_translations); i++)
1558 if (strcmp (cups_choice_translations[i].keyword, keyword) == 0 &&
1559 strcmp (cups_choice_translations[i].choice, choice->choice) == 0)
1560 return g_strdup (_(cups_choice_translations[i].translation));
1562 return ppd_text_to_utf8 (ppd_file, choice->text);
1566 group_has_option (ppd_group_t *group, ppd_option_t *option)
1573 if (group->num_options > 0 &&
1574 option >= group->options && option < group->options + group->num_options)
1577 for (i = 0; i < group->num_subgroups; i++)
1579 if (group_has_option (&group->subgroups[i],option))
1586 set_option_off (GtkPrinterOption *option)
1588 /* Any of these will do, _set only applies the value
1589 * if its allowed of the option */
1590 gtk_printer_option_set (option, "False");
1591 gtk_printer_option_set (option, "Off");
1592 gtk_printer_option_set (option, "None");
1596 value_is_off (const char *value)
1598 return (strcasecmp (value, "None") == 0 ||
1599 strcasecmp (value, "Off") == 0 ||
1600 strcasecmp (value, "False") == 0);
1604 available_choices (ppd_file_t *ppd,
1605 ppd_option_t *option,
1606 ppd_choice_t ***available,
1607 gboolean keep_if_only_one_option)
1609 ppd_option_t *other_option;
1612 ppd_const_t *constraint;
1613 const char *choice, *other_choice;
1614 ppd_option_t *option1, *option2;
1615 ppd_group_t *installed_options;
1617 gboolean all_default;
1623 conflicts = g_new0 (char, option->num_choices);
1625 installed_options = NULL;
1626 for (i = 0; i < ppd->num_groups; i++)
1628 if (strcmp (ppd->groups[i].name, "InstallableOptions") == 0)
1630 installed_options = &ppd->groups[i];
1635 for (i = ppd->num_consts, constraint = ppd->consts; i > 0; i--, constraint++)
1637 option1 = ppdFindOption (ppd, constraint->option1);
1638 if (option1 == NULL)
1641 option2 = ppdFindOption (ppd, constraint->option2);
1642 if (option2 == NULL)
1645 if (option == option1)
1647 choice = constraint->choice1;
1648 other_option = option2;
1649 other_choice = constraint->choice2;
1651 else if (option == option2)
1653 choice = constraint->choice2;
1654 other_option = option1;
1655 other_choice = constraint->choice1;
1660 /* We only care of conflicts with installed_options and
1662 if (!group_has_option (installed_options, other_option) &&
1663 (strcmp (other_option->keyword, "PageSize") != 0))
1666 if (*other_choice == 0)
1668 /* Conflict only if the installed option is not off */
1669 if (value_is_off (other_option->defchoice))
1672 /* Conflict if the installed option has the specified default */
1673 else if (strcasecmp (other_choice, other_option->defchoice) != 0)
1678 /* Conflict with all non-off choices */
1679 for (j = 0; j < option->num_choices; j++)
1681 if (!value_is_off (option->choices[j].choice))
1687 for (j = 0; j < option->num_choices; j++)
1689 if (strcasecmp (option->choices[j].choice, choice) == 0)
1697 for (j = 0; j < option->num_choices; j++)
1701 else if (strcmp (option->choices[j].choice, option->defchoice) != 0)
1702 all_default = FALSE;
1705 if (all_default && !keep_if_only_one_option)
1708 if (num_conflicts == option->num_choices)
1712 /* Some ppds don't have a "use printer default" option for
1713 InputSlot. This means you always have to select a particular slot,
1714 and you can't auto-pick source based on the paper size. To support
1715 this we always add an auto option if there isn't one already. If
1716 the user chooses the generated option we don't send any InputSlot
1717 value when printing. The way we detect existing auto-cases is based
1718 on feedback from Michael Sweet of cups fame.
1721 if (strcmp (option->keyword, "InputSlot") == 0)
1723 gboolean found_auto = FALSE;
1724 for (j = 0; j < option->num_choices; j++)
1728 if (strcmp (option->choices[j].choice, "Auto") == 0 ||
1729 strcmp (option->choices[j].choice, "AutoSelect") == 0 ||
1730 strcmp (option->choices[j].choice, "Default") == 0 ||
1731 strcmp (option->choices[j].choice, "None") == 0 ||
1732 strcmp (option->choices[j].choice, "PrinterDefault") == 0 ||
1733 strcmp (option->choices[j].choice, "Unspecified") == 0 ||
1734 option->choices[j].code == NULL ||
1735 option->choices[j].code[0] == 0)
1750 *available = g_new (ppd_choice_t *, option->num_choices - num_conflicts + add_auto);
1753 for (j = 0; j < option->num_choices; j++)
1756 (*available)[i++] = &option->choices[j];
1760 (*available)[i++] = NULL;
1763 return option->num_choices - num_conflicts + add_auto;
1766 static GtkPrinterOption *
1767 create_pickone_option (ppd_file_t *ppd_file,
1768 ppd_option_t *ppd_option,
1769 const char *gtk_name)
1771 GtkPrinterOption *option;
1772 ppd_choice_t **available;
1777 g_assert (ppd_option->ui == PPD_UI_PICKONE);
1781 n_choices = available_choices (ppd_file, ppd_option, &available, g_str_has_prefix (gtk_name, "gtk-"));
1784 label = get_option_text (ppd_file, ppd_option);
1785 option = gtk_printer_option_new (gtk_name, label,
1786 GTK_PRINTER_OPTION_TYPE_PICKONE);
1789 gtk_printer_option_allocate_choices (option, n_choices);
1790 for (i = 0; i < n_choices; i++)
1792 if (available[i] == NULL)
1794 /* This was auto-added */
1795 option->choices[i] = g_strdup ("gtk-ignore-value");
1796 option->choices_display[i] = g_strdup (_("Printer Default"));
1800 option->choices[i] = g_strdup (available[i]->choice);
1801 option->choices_display[i] = get_choice_text (ppd_file, available[i]);
1804 gtk_printer_option_set (option, ppd_option->defchoice);
1806 #ifdef PRINT_IGNORED_OPTIONS
1808 g_warning ("Ignoring pickone %s\n", ppd_option->text);
1815 static GtkPrinterOption *
1816 create_boolean_option (ppd_file_t *ppd_file,
1817 ppd_option_t *ppd_option,
1818 const char *gtk_name)
1820 GtkPrinterOption *option;
1821 ppd_choice_t **available;
1825 g_assert (ppd_option->ui == PPD_UI_BOOLEAN);
1829 n_choices = available_choices (ppd_file, ppd_option, &available, g_str_has_prefix (gtk_name, "gtk-"));
1832 label = get_option_text (ppd_file, ppd_option);
1833 option = gtk_printer_option_new (gtk_name, label,
1834 GTK_PRINTER_OPTION_TYPE_BOOLEAN);
1837 gtk_printer_option_allocate_choices (option, 2);
1838 option->choices[0] = g_strdup ("True");
1839 option->choices_display[0] = g_strdup ("True");
1840 option->choices[1] = g_strdup ("True");
1841 option->choices_display[1] = g_strdup ("True");
1843 gtk_printer_option_set (option, ppd_option->defchoice);
1845 #ifdef PRINT_IGNORED_OPTIONS
1847 g_warning ("Ignoring boolean %s\n", ppd_option->text);
1855 get_option_name (const char *keyword)
1859 for (i = 0; i < G_N_ELEMENTS (option_names); i++)
1860 if (strcmp (option_names[i].ppd_keyword, keyword) == 0)
1861 return g_strdup (option_names[i].name);
1863 return g_strdup_printf ("cups-%s", keyword);
1867 strptr_cmp (const void *a, const void *b)
1869 char **aa = (char **)a;
1870 char **bb = (char **)b;
1871 return strcmp (*aa, *bb);
1876 string_in_table (char *str, const char *table[], int table_len)
1878 return bsearch (&str, table, table_len, sizeof (char *), (void *)strptr_cmp) != NULL;
1881 #define STRING_IN_TABLE(_str, _table) (string_in_table (_str, _table, G_N_ELEMENTS (_table)))
1884 handle_option (GtkPrinterOptionSet *set,
1885 ppd_file_t *ppd_file,
1886 ppd_option_t *ppd_option,
1887 ppd_group_t *toplevel_group,
1888 GtkPrintSettings *settings)
1890 GtkPrinterOption *option;
1893 if (STRING_IN_TABLE (ppd_option->keyword, cups_option_blacklist))
1896 name = get_option_name (ppd_option->keyword);
1899 if (ppd_option->ui == PPD_UI_PICKONE)
1901 option = create_pickone_option (ppd_file, ppd_option, name);
1903 else if (ppd_option->ui == PPD_UI_BOOLEAN)
1905 option = create_boolean_option (ppd_file, ppd_option, name);
1908 g_warning ("Ignored pickmany setting %s\n", ppd_option->text);
1913 if (STRING_IN_TABLE (toplevel_group->name,
1914 color_group_whitelist) ||
1915 STRING_IN_TABLE (ppd_option->keyword,
1916 color_option_whitelist))
1918 option->group = g_strdup ("ColorPage");
1920 else if (STRING_IN_TABLE (toplevel_group->name,
1921 image_quality_group_whitelist) ||
1922 STRING_IN_TABLE (ppd_option->keyword,
1923 image_quality_option_whitelist))
1925 option->group = g_strdup ("ImageQualityPage");
1927 else if (STRING_IN_TABLE (toplevel_group->name,
1928 finishing_group_whitelist) ||
1929 STRING_IN_TABLE (ppd_option->keyword,
1930 finishing_option_whitelist))
1932 option->group = g_strdup ("FinishingPage");
1936 option->group = g_strdup (toplevel_group->text);
1939 set_option_from_settings (option, settings);
1941 gtk_printer_option_set_add (set, option);
1948 handle_group (GtkPrinterOptionSet *set,
1949 ppd_file_t *ppd_file,
1951 ppd_group_t *toplevel_group,
1952 GtkPrintSettings *settings)
1956 /* Ignore installable options */
1957 if (strcmp (toplevel_group->name, "InstallableOptions") == 0)
1960 for (i = 0; i < group->num_options; i++)
1961 handle_option (set, ppd_file, &group->options[i], toplevel_group, settings);
1963 for (i = 0; i < group->num_subgroups; i++)
1964 handle_group (set, ppd_file, &group->subgroups[i], toplevel_group, settings);
1968 static GtkPrinterOptionSet *
1969 cups_printer_get_options (GtkPrinter *printer,
1970 GtkPrintSettings *settings,
1971 GtkPageSetup *page_setup)
1973 GtkPrinterOptionSet *set;
1974 GtkPrinterOption *option;
1975 ppd_file_t *ppd_file;
1977 char *print_at[] = { "now", "at", "on-hold" };
1978 char *n_up[] = {"1", "2", "4", "6", "9", "16" };
1979 char *prio[] = {"100", "80", "50", "30" };
1980 char *prio_display[] = {N_("Urgent"), N_("High"), N_("Medium"), N_("Low") };
1981 char *cover[] = {"none", "classified", "confidential", "secret", "standard", "topsecret", "unclassified" };
1982 char *cover_display[] = {N_("None"), N_("Classified"), N_("Confidential"), N_("Secret"), N_("Standard"), N_("Top Secret"), N_("Unclassified"),};
1985 set = gtk_printer_option_set_new ();
1987 /* Cups specific, non-ppd related settings */
1989 option = gtk_printer_option_new ("gtk-n-up", "Pages Per Sheet", GTK_PRINTER_OPTION_TYPE_PICKONE);
1990 gtk_printer_option_choices_from_array (option, G_N_ELEMENTS (n_up),
1992 gtk_printer_option_set (option, "1");
1993 set_option_from_settings (option, settings);
1994 gtk_printer_option_set_add (set, option);
1995 g_object_unref (option);
1997 for (i = 0; i < G_N_ELEMENTS(prio_display); i++)
1998 prio_display[i] = _(prio_display[i]);
2000 option = gtk_printer_option_new ("gtk-job-prio", "Job Priority", GTK_PRINTER_OPTION_TYPE_PICKONE);
2001 gtk_printer_option_choices_from_array (option, G_N_ELEMENTS (prio),
2002 prio, prio_display);
2003 gtk_printer_option_set (option, "50");
2004 set_option_from_settings (option, settings);
2005 gtk_printer_option_set_add (set, option);
2006 g_object_unref (option);
2008 option = gtk_printer_option_new ("gtk-billing-info", "Billing Info", GTK_PRINTER_OPTION_TYPE_STRING);
2009 gtk_printer_option_set (option, "");
2010 set_option_from_settings (option, settings);
2011 gtk_printer_option_set_add (set, option);
2012 g_object_unref (option);
2014 for (i = 0; i < G_N_ELEMENTS(cover_display); i++)
2015 cover_display[i] = _(cover_display[i]);
2017 option = gtk_printer_option_new ("gtk-cover-before", "Before", GTK_PRINTER_OPTION_TYPE_PICKONE);
2018 gtk_printer_option_choices_from_array (option, G_N_ELEMENTS (cover),
2019 cover, cover_display);
2020 gtk_printer_option_set (option, "none");
2021 set_option_from_settings (option, settings);
2022 gtk_printer_option_set_add (set, option);
2023 g_object_unref (option);
2025 option = gtk_printer_option_new ("gtk-cover-after", "After", GTK_PRINTER_OPTION_TYPE_PICKONE);
2026 gtk_printer_option_choices_from_array (option, G_N_ELEMENTS (cover),
2027 cover, cover_display);
2028 gtk_printer_option_set (option, "none");
2029 set_option_from_settings (option, settings);
2030 gtk_printer_option_set_add (set, option);
2031 g_object_unref (option);
2033 option = gtk_printer_option_new ("gtk-print-time", "Print at", GTK_PRINTER_OPTION_TYPE_PICKONE);
2034 gtk_printer_option_choices_from_array (option, G_N_ELEMENTS (print_at),
2035 print_at, print_at);
2036 gtk_printer_option_set (option, "now");
2037 set_option_from_settings (option, settings);
2038 gtk_printer_option_set_add (set, option);
2039 g_object_unref (option);
2041 option = gtk_printer_option_new ("gtk-print-time-text", "Print at time", GTK_PRINTER_OPTION_TYPE_STRING);
2042 gtk_printer_option_set (option, "");
2043 set_option_from_settings (option, settings);
2044 gtk_printer_option_set_add (set, option);
2045 g_object_unref (option);
2047 /* Printer (ppd) specific settings */
2048 ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
2051 GtkPaperSize *paper_size;
2052 ppd_option_t *option;
2054 ppdMarkDefaults (ppd_file);
2056 paper_size = gtk_page_setup_get_paper_size (page_setup);
2058 option = ppdFindOption(ppd_file, "PageSize");
2059 strncpy (option->defchoice, gtk_paper_size_get_ppd_name (paper_size),
2062 for (i = 0; i < ppd_file->num_groups; i++)
2063 handle_group (set, ppd_file, &ppd_file->groups[i], &ppd_file->groups[i], settings);
2071 mark_option_from_set (GtkPrinterOptionSet *set,
2072 ppd_file_t *ppd_file,
2073 ppd_option_t *ppd_option)
2075 GtkPrinterOption *option;
2076 char *name = get_option_name (ppd_option->keyword);
2078 option = gtk_printer_option_set_lookup (set, name);
2081 ppdMarkOption (ppd_file, ppd_option->keyword, option->value);
2088 mark_group_from_set (GtkPrinterOptionSet *set,
2089 ppd_file_t *ppd_file,
2094 for (i = 0; i < group->num_options; i++)
2095 mark_option_from_set (set, ppd_file, &group->options[i]);
2097 for (i = 0; i < group->num_subgroups; i++)
2098 mark_group_from_set (set, ppd_file, &group->subgroups[i]);
2102 set_conflicts_from_option (GtkPrinterOptionSet *set,
2103 ppd_file_t *ppd_file,
2104 ppd_option_t *ppd_option)
2106 GtkPrinterOption *option;
2108 if (ppd_option->conflicted)
2110 name = get_option_name (ppd_option->keyword);
2111 option = gtk_printer_option_set_lookup (set, name);
2114 gtk_printer_option_set_has_conflict (option, TRUE);
2116 g_warning ("conflict for option %s ignored", ppd_option->keyword);
2123 set_conflicts_from_group (GtkPrinterOptionSet *set,
2124 ppd_file_t *ppd_file,
2129 for (i = 0; i < group->num_options; i++)
2130 set_conflicts_from_option (set, ppd_file, &group->options[i]);
2132 for (i = 0; i < group->num_subgroups; i++)
2133 set_conflicts_from_group (set, ppd_file, &group->subgroups[i]);
2137 cups_printer_mark_conflicts (GtkPrinter *printer,
2138 GtkPrinterOptionSet *options)
2140 ppd_file_t *ppd_file;
2144 ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
2146 if (ppd_file == NULL)
2149 ppdMarkDefaults (ppd_file);
2151 for (i = 0; i < ppd_file->num_groups; i++)
2152 mark_group_from_set (options, ppd_file, &ppd_file->groups[i]);
2154 num_conflicts = ppdConflicts (ppd_file);
2156 if (num_conflicts > 0)
2158 for (i = 0; i < ppd_file->num_groups; i++)
2159 set_conflicts_from_group (options, ppd_file, &ppd_file->groups[i]);
2162 return num_conflicts > 0;
2166 GtkPrinter *printer;
2167 GtkPrinterOptionSet *options;
2168 GtkPrintSettings *settings;
2169 ppd_file_t *ppd_file;
2174 const char *standard;
2178 map_settings_to_option (GtkPrinterOption *option,
2179 const NameMapping table[],
2181 GtkPrintSettings *settings,
2182 const char *standard_name,
2183 const char *cups_name)
2187 const char *cups_value;
2188 const char *standard_value;
2190 /* If the cups-specific setting is set, always use that */
2192 name = g_strdup_printf ("cups-%s", cups_name);
2193 cups_value = gtk_print_settings_get (settings, name);
2196 if (cups_value != NULL) {
2197 gtk_printer_option_set (option, cups_value);
2201 /* Otherwise we try to convert from the general setting */
2202 standard_value = gtk_print_settings_get (settings, standard_name);
2203 if (standard_value == NULL)
2206 for (i = 0; i < n_elements; i++)
2208 if (table[i].cups == NULL && table[i].standard == NULL)
2210 gtk_printer_option_set (option, standard_value);
2213 else if (table[i].cups == NULL &&
2214 strcmp (table[i].standard, standard_value) == 0)
2216 set_option_off (option);
2219 else if (strcmp (table[i].standard, standard_value) == 0)
2221 gtk_printer_option_set (option, table[i].cups);
2228 map_option_to_settings (const char *value,
2229 const NameMapping table[],
2231 GtkPrintSettings *settings,
2232 const char *standard_name,
2233 const char *cups_name)
2238 for (i = 0; i < n_elements; i++)
2240 if (table[i].cups == NULL && table[i].standard == NULL)
2242 gtk_print_settings_set (settings,
2247 else if (table[i].cups == NULL && table[i].standard != NULL)
2249 if (value_is_off (value))
2251 gtk_print_settings_set (settings,
2257 else if (strcmp (table[i].cups, value) == 0)
2259 gtk_print_settings_set (settings,
2266 /* Always set the corresponding cups-specific setting */
2267 name = g_strdup_printf ("cups-%s", cups_name);
2268 gtk_print_settings_set (settings, name, value);
2273 static const NameMapping paper_source_map[] = {
2274 { "Lower", "lower"},
2275 { "Middle", "middle"},
2276 { "Upper", "upper"},
2278 { "Envelope", "envelope"},
2279 { "Cassette", "cassette"},
2280 { "LargeCapacity", "large-capacity"},
2281 { "AnySmallFormat", "small-format"},
2282 { "AnyLargeFormat", "large-format"},
2286 static const NameMapping output_tray_map[] = {
2287 { "Upper", "upper"},
2288 { "Lower", "lower"},
2293 static const NameMapping duplex_map[] = {
2294 { "DuplexTumble", "vertical" },
2295 { "DuplexNoTumble", "horizontal" },
2299 static const NameMapping output_mode_map[] = {
2300 { "Standard", "normal" },
2301 { "Normal", "normal" },
2302 { "Draft", "draft" },
2303 { "Fast", "draft" },
2306 static const NameMapping media_type_map[] = {
2307 { "Transparency", "transparency"},
2308 { "Standard", "stationery"},
2312 static const NameMapping all_map[] = {
2318 set_option_from_settings (GtkPrinterOption *option,
2319 GtkPrintSettings *settings)
2321 const char *cups_value;
2324 if (settings == NULL)
2327 if (strcmp (option->name, "gtk-paper-source") == 0)
2328 map_settings_to_option (option, paper_source_map, G_N_ELEMENTS (paper_source_map),
2329 settings, GTK_PRINT_SETTINGS_DEFAULT_SOURCE, "InputSlot");
2330 else if (strcmp (option->name, "gtk-output-tray") == 0)
2331 map_settings_to_option (option, output_tray_map, G_N_ELEMENTS (output_tray_map),
2332 settings, GTK_PRINT_SETTINGS_OUTPUT_BIN, "OutputBin");
2333 else if (strcmp (option->name, "gtk-duplex") == 0)
2334 map_settings_to_option (option, duplex_map, G_N_ELEMENTS (duplex_map),
2335 settings, GTK_PRINT_SETTINGS_DUPLEX, "Duplex");
2336 else if (strcmp (option->name, "cups-OutputMode") == 0)
2337 map_settings_to_option (option, output_mode_map, G_N_ELEMENTS (output_mode_map),
2338 settings, GTK_PRINT_SETTINGS_QUALITY, "OutputMode");
2339 else if (strcmp (option->name, "cups-Resolution") == 0)
2341 cups_value = gtk_print_settings_get (settings, option->name);
2343 gtk_printer_option_set (option, cups_value);
2346 int res = gtk_print_settings_get_resolution (settings);
2349 value = g_strdup_printf ("%ddpi", res);
2350 gtk_printer_option_set (option, value);
2355 else if (strcmp (option->name, "gtk-paper-type") == 0)
2356 map_settings_to_option (option, media_type_map, G_N_ELEMENTS (media_type_map),
2357 settings, GTK_PRINT_SETTINGS_MEDIA_TYPE, "MediaType");
2358 else if (strcmp (option->name, "gtk-n-up") == 0)
2360 map_settings_to_option (option, all_map, G_N_ELEMENTS (all_map),
2361 settings, GTK_PRINT_SETTINGS_NUMBER_UP, "number-up");
2363 else if (strcmp (option->name, "gtk-billing-info") == 0)
2365 cups_value = gtk_print_settings_get (settings, "cups-job-billing");
2367 gtk_printer_option_set (option, cups_value);
2369 else if (strcmp (option->name, "gtk-job-prio") == 0)
2371 cups_value = gtk_print_settings_get (settings, "cups-job-priority");
2373 gtk_printer_option_set (option, cups_value);
2375 else if (strcmp (option->name, "gtk-cover-before") == 0)
2377 cups_value = gtk_print_settings_get (settings, "cover-before");
2379 gtk_printer_option_set (option, cups_value);
2381 else if (strcmp (option->name, "gtk-cover-after") == 0)
2383 cups_value = gtk_print_settings_get (settings, "cover-after");
2385 gtk_printer_option_set (option, cups_value);
2387 else if (strcmp (option->name, "gtk-print-time") == 0)
2389 cups_value = gtk_print_settings_get (settings, "print-at");
2391 gtk_printer_option_set (option, cups_value);
2393 else if (strcmp (option->name, "gtk-print-time-text") == 0)
2395 cups_value = gtk_print_settings_get (settings, "print-at-time");
2397 gtk_printer_option_set (option, cups_value);
2399 else if (g_str_has_prefix (option->name, "cups-"))
2401 cups_value = gtk_print_settings_get (settings, option->name);
2403 gtk_printer_option_set (option, cups_value);
2408 foreach_option_get_settings (GtkPrinterOption *option,
2411 struct OptionData *data = user_data;
2412 GtkPrintSettings *settings = data->settings;
2415 value = option->value;
2417 if (strcmp (option->name, "gtk-paper-source") == 0)
2418 map_option_to_settings (value, paper_source_map, G_N_ELEMENTS (paper_source_map),
2419 settings, GTK_PRINT_SETTINGS_DEFAULT_SOURCE, "InputSlot");
2420 else if (strcmp (option->name, "gtk-output-tray") == 0)
2421 map_option_to_settings (value, output_tray_map, G_N_ELEMENTS (output_tray_map),
2422 settings, GTK_PRINT_SETTINGS_OUTPUT_BIN, "OutputBin");
2423 else if (strcmp (option->name, "gtk-duplex") == 0)
2424 map_option_to_settings (value, duplex_map, G_N_ELEMENTS (duplex_map),
2425 settings, GTK_PRINT_SETTINGS_DUPLEX, "Duplex");
2426 else if (strcmp (option->name, "cups-OutputMode") == 0)
2427 map_option_to_settings (value, output_mode_map, G_N_ELEMENTS (output_mode_map),
2428 settings, GTK_PRINT_SETTINGS_QUALITY, "OutputMode");
2429 else if (strcmp (option->name, "cups-Resolution") == 0)
2431 int res = atoi (value);
2432 /* TODO: What if resolution is on XXXxYYYdpi form? */
2434 gtk_print_settings_set_resolution (settings, res);
2435 gtk_print_settings_set (settings, option->name, value);
2437 else if (strcmp (option->name, "gtk-paper-type") == 0)
2438 map_option_to_settings (value, media_type_map, G_N_ELEMENTS (media_type_map),
2439 settings, GTK_PRINT_SETTINGS_MEDIA_TYPE, "MediaType");
2440 else if (strcmp (option->name, "gtk-n-up") == 0)
2441 map_option_to_settings (value, all_map, G_N_ELEMENTS (all_map),
2442 settings, GTK_PRINT_SETTINGS_NUMBER_UP, "number-up");
2443 else if (strcmp (option->name, "gtk-billing-info") == 0 && strlen (value) > 0)
2444 gtk_print_settings_set (settings, "cups-job-billing", value);
2445 else if (strcmp (option->name, "gtk-job-prio") == 0)
2446 gtk_print_settings_set (settings, "cups-job-priority", value);
2447 else if (strcmp (option->name, "gtk-cover-before") == 0)
2448 gtk_print_settings_set (settings, "cover-before", value);
2449 else if (strcmp (option->name, "gtk-cover-after") == 0)
2450 gtk_print_settings_set (settings, "cover-after", value);
2451 else if (strcmp (option->name, "gtk-print-time") == 0)
2452 gtk_print_settings_set (settings, "print-at", value);
2453 else if (strcmp (option->name, "gtk-print-time-text") == 0)
2454 gtk_print_settings_set (settings, "print-at-time", value);
2455 else if (g_str_has_prefix (option->name, "cups-"))
2456 gtk_print_settings_set (settings, option->name, value);
2460 cups_printer_get_settings_from_options (GtkPrinter *printer,
2461 GtkPrinterOptionSet *options,
2462 GtkPrintSettings *settings)
2464 struct OptionData data;
2465 const char *print_at, *print_at_time;
2467 data.printer = printer;
2468 data.options = options;
2469 data.settings = settings;
2470 data.ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
2472 if (data.ppd_file != NULL)
2474 GtkPrinterOption *cover_before, *cover_after;
2476 gtk_printer_option_set_foreach (options, foreach_option_get_settings, &data);
2478 cover_before = gtk_printer_option_set_lookup (options, "gtk-cover-before");
2479 cover_after = gtk_printer_option_set_lookup (options, "gtk-cover-after");
2480 if (cover_before && cover_after)
2482 char *value = g_strdup_printf ("%s,%s", cover_before->value, cover_after->value);
2483 gtk_print_settings_set (settings, "cups-job-sheets", value);
2487 print_at = gtk_print_settings_get (settings, "print-at");
2488 print_at_time = gtk_print_settings_get (settings, "print-at-time");
2489 if (strcmp (print_at, "at") == 0)
2490 gtk_print_settings_set (settings, "cups-job-hold-until", print_at_time);
2491 else if (strcmp (print_at, "on-hold") == 0)
2492 gtk_print_settings_set (settings, "cups-job-hold-until", "indefinite");
2497 cups_printer_prepare_for_print (GtkPrinter *printer,
2498 GtkPrintJob *print_job,
2499 GtkPrintSettings *settings,
2500 GtkPageSetup *page_setup)
2502 GtkPageSet page_set;
2503 GtkPaperSize *paper_size;
2504 const char *ppd_paper_name;
2507 print_job->print_pages = gtk_print_settings_get_print_pages (settings);
2508 print_job->page_ranges = NULL;
2509 print_job->num_page_ranges = 0;
2511 if (print_job->print_pages == GTK_PRINT_PAGES_RANGES)
2512 print_job->page_ranges =
2513 gtk_print_settings_get_page_ranges (settings,
2514 &print_job->num_page_ranges);
2516 if (gtk_print_settings_get_collate (settings))
2517 gtk_print_settings_set (settings, "cups-Collate", "True");
2518 print_job->collate = FALSE;
2520 if (gtk_print_settings_get_reverse (settings))
2521 gtk_print_settings_set (settings, "cups-OutputOrder", "Reverse");
2522 print_job->reverse = FALSE;
2524 if (gtk_print_settings_get_num_copies (settings) > 1)
2525 gtk_print_settings_set_int (settings, "cups-copies",
2526 gtk_print_settings_get_num_copies (settings));
2527 print_job->num_copies = 1;
2529 scale = gtk_print_settings_get_scale (settings);
2530 print_job->scale = 1.0;
2532 print_job->scale = scale/100.0;
2534 page_set = gtk_print_settings_get_page_set (settings);
2535 if (page_set == GTK_PAGE_SET_EVEN)
2536 gtk_print_settings_set (settings, "cups-page-set", "even");
2537 else if (page_set == GTK_PAGE_SET_ODD)
2538 gtk_print_settings_set (settings, "cups-page-set", "odd");
2539 print_job->page_set = GTK_PAGE_SET_ALL;
2541 paper_size = gtk_page_setup_get_paper_size (page_setup);
2542 ppd_paper_name = gtk_paper_size_get_ppd_name (paper_size);
2543 if (ppd_paper_name != NULL)
2544 gtk_print_settings_set (settings, "cups-PageSize", ppd_paper_name);
2547 char *custom_name = g_strdup_printf ("Custom.%2fx%.2f",
2548 gtk_paper_size_get_width (paper_size, GTK_UNIT_POINTS),
2549 gtk_paper_size_get_height (paper_size, GTK_UNIT_POINTS));
2550 gtk_print_settings_set (settings, "cups-PageSize", custom_name);
2551 g_free (custom_name);
2554 print_job->rotate_to_orientation = TRUE;
2558 cups_printer_list_papers (GtkPrinter *printer)
2560 ppd_file_t *ppd_file;
2563 GtkPageSetup *page_setup;
2564 GtkPaperSize *paper_size;
2565 ppd_option_t *option;
2566 ppd_choice_t *choice;
2570 ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
2571 if (ppd_file == NULL)
2576 for (i = 0; i < ppd_file->num_sizes; i++)
2578 size = &ppd_file->sizes[i];
2580 display_name = NULL;
2581 option = ppdFindOption(ppd_file, "PageSize");
2584 choice = ppdFindChoice(option, size->name);
2586 display_name = ppd_text_to_utf8 (ppd_file, choice->text);
2588 if (display_name == NULL)
2589 display_name = g_strdup (size->name);
2591 page_setup = gtk_page_setup_new ();
2592 paper_size = gtk_paper_size_new_from_ppd (size->name,
2596 gtk_page_setup_set_paper_size (page_setup, paper_size);
2597 gtk_paper_size_free (paper_size);
2599 gtk_page_setup_set_top_margin (page_setup, size->length - size->top, GTK_UNIT_POINTS);
2600 gtk_page_setup_set_bottom_margin (page_setup, size->bottom, GTK_UNIT_POINTS);
2601 gtk_page_setup_set_left_margin (page_setup, size->left, GTK_UNIT_POINTS);
2602 gtk_page_setup_set_right_margin (page_setup, size->width - size->right, GTK_UNIT_POINTS);
2604 g_free (display_name);
2606 l = g_list_prepend (l, page_setup);
2609 return g_list_reverse (l);
2613 cups_printer_get_hard_margins (GtkPrinter *printer,
2619 ppd_file_t *ppd_file;
2621 ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
2622 if (ppd_file == NULL)
2625 *left = ppd_file->custom_margins[0];
2626 *bottom = ppd_file->custom_margins[1];
2627 *right = ppd_file->custom_margins[2];
2628 *top = ppd_file->custom_margins[3];