1 | /* GTK - The GIMP Toolkit |
2 | * gtkprintbackendlpr.c: LPR implementation of GtkPrintBackend |
3 | * for printing to lpr |
4 | * Copyright (C) 2006, 2007 Red Hat, Inc. |
5 | * |
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. |
10 | * |
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. |
15 | * |
16 | * You should have received a copy of the GNU Lesser General Public |
17 | * License along with this library. If not, see <http://www.gnu.org/licenses/>. |
18 | */ |
19 | |
20 | #include "config.h" |
21 | #include <unistd.h> |
22 | #include <sys/types.h> |
23 | #include <sys/stat.h> |
24 | #include <fcntl.h> |
25 | #include <stdlib.h> |
26 | #include <string.h> |
27 | |
28 | #include <errno.h> |
29 | #include <cairo.h> |
30 | #include <cairo-ps.h> |
31 | |
32 | #include <glib/gi18n-lib.h> |
33 | |
34 | #include <gtk/gtk.h> |
35 | #include "gtkprinterprivate.h" |
36 | |
37 | #include "gtkprintbackendlpr.h" |
38 | |
39 | typedef struct _GtkPrintBackendLprClass GtkPrintBackendLprClass; |
40 | |
41 | #define GTK_PRINT_BACKEND_LPR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_PRINT_BACKEND_LPR, GtkPrintBackendLprClass)) |
42 | #define GTK_IS_PRINT_BACKEND_LPR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_PRINT_BACKEND_LPR)) |
43 | #define GTK_PRINT_BACKEND_LPR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_PRINT_BACKEND_LPR, GtkPrintBackendLprClass)) |
44 | |
45 | #define _LPR_MAX_CHUNK_SIZE 8192 |
46 | |
47 | struct _GtkPrintBackendLprClass |
48 | { |
49 | GtkPrintBackendClass parent_class; |
50 | }; |
51 | |
52 | struct _GtkPrintBackendLpr |
53 | { |
54 | GtkPrintBackend parent_instance; |
55 | }; |
56 | |
57 | static GObjectClass *backend_parent_class; |
58 | |
59 | static void lpr_printer_get_settings_from_options (GtkPrinter *printer, |
60 | GtkPrinterOptionSet *options, |
61 | GtkPrintSettings *settings); |
62 | static GtkPrinterOptionSet *lpr_printer_get_options (GtkPrinter *printer, |
63 | GtkPrintSettings *settings, |
64 | GtkPageSetup *page_setup, |
65 | GtkPrintCapabilities capabilities); |
66 | static void lpr_printer_prepare_for_print (GtkPrinter *printer, |
67 | GtkPrintJob *print_job, |
68 | GtkPrintSettings *settings, |
69 | GtkPageSetup *page_setup); |
70 | static cairo_surface_t * lpr_printer_create_cairo_surface (GtkPrinter *printer, |
71 | GtkPrintSettings *settings, |
72 | double width, |
73 | double height, |
74 | GIOChannel *cache_io); |
75 | static void gtk_print_backend_lpr_print_stream (GtkPrintBackend *print_backend, |
76 | GtkPrintJob *job, |
77 | GIOChannel *data_io, |
78 | GtkPrintJobCompleteFunc callback, |
79 | gpointer user_data, |
80 | GDestroyNotify dnotify); |
81 | |
82 | G_DEFINE_DYNAMIC_TYPE (GtkPrintBackendLpr, gtk_print_backend_lpr, GTK_TYPE_PRINT_BACKEND) |
83 | |
84 | void |
85 | g_io_module_load (GIOModule *module) |
86 | { |
87 | g_type_module_use (G_TYPE_MODULE (module)); |
88 | |
89 | gtk_print_backend_lpr_register_type (G_TYPE_MODULE (module)); |
90 | |
91 | g_io_extension_point_implement (GTK_PRINT_BACKEND_EXTENSION_POINT_NAME, |
92 | GTK_TYPE_PRINT_BACKEND_LPR, |
93 | extension_name: "lpr" , |
94 | priority: 10); |
95 | } |
96 | |
97 | void |
98 | g_io_module_unload (GIOModule *module) |
99 | { |
100 | } |
101 | |
102 | char ** |
103 | g_io_module_query (void) |
104 | { |
105 | char *eps[] = { |
106 | (char *)GTK_PRINT_BACKEND_EXTENSION_POINT_NAME, |
107 | NULL |
108 | }; |
109 | |
110 | return g_strdupv (str_array: eps); |
111 | } |
112 | |
113 | /** |
114 | * gtk_print_backend_lpr_new: |
115 | * |
116 | * Creates a new #GtkPrintBackendLpr object. #GtkPrintBackendLpr |
117 | * implements the #GtkPrintBackend interface with direct access to |
118 | * the filesystem using Unix/Linux API calls |
119 | * |
120 | * Returns: the new #GtkPrintBackendLpr object |
121 | **/ |
122 | GtkPrintBackend * |
123 | gtk_print_backend_lpr_new (void) |
124 | { |
125 | return g_object_new (GTK_TYPE_PRINT_BACKEND_LPR, NULL); |
126 | } |
127 | |
128 | static void |
129 | gtk_print_backend_lpr_class_init (GtkPrintBackendLprClass *class) |
130 | { |
131 | GtkPrintBackendClass *backend_class = GTK_PRINT_BACKEND_CLASS (class); |
132 | |
133 | backend_parent_class = g_type_class_peek_parent (g_class: class); |
134 | |
135 | backend_class->print_stream = gtk_print_backend_lpr_print_stream; |
136 | backend_class->printer_create_cairo_surface = lpr_printer_create_cairo_surface; |
137 | backend_class->printer_get_options = lpr_printer_get_options; |
138 | backend_class->printer_get_settings_from_options = lpr_printer_get_settings_from_options; |
139 | backend_class->printer_prepare_for_print = lpr_printer_prepare_for_print; |
140 | } |
141 | |
142 | static void |
143 | gtk_print_backend_lpr_class_finalize (GtkPrintBackendLprClass *class) |
144 | { |
145 | } |
146 | |
147 | static cairo_status_t |
148 | _cairo_write (void *closure, |
149 | const unsigned char *data, |
150 | unsigned int length) |
151 | { |
152 | GIOChannel *io = (GIOChannel *)closure; |
153 | gsize written; |
154 | GError *error; |
155 | |
156 | error = NULL; |
157 | |
158 | GTK_NOTE (PRINTING, |
159 | g_print ("LPR Backend: Writing %i byte chunk to temp file\n" , length)); |
160 | |
161 | while (length > 0) |
162 | { |
163 | g_io_channel_write_chars (channel: io, buf: (const char *)data, count: length, bytes_written: &written, error: &error); |
164 | |
165 | if (error != NULL) |
166 | { |
167 | GTK_NOTE (PRINTING, |
168 | g_print ("LPR Backend: Error writing to temp file, %s\n" , error->message)); |
169 | |
170 | g_error_free (error); |
171 | return CAIRO_STATUS_WRITE_ERROR; |
172 | } |
173 | |
174 | GTK_NOTE (PRINTING, |
175 | g_print ("LPR Backend: Wrote %" G_GSIZE_FORMAT " bytes to temp file\n" , written)); |
176 | |
177 | data += written; |
178 | length -= written; |
179 | } |
180 | |
181 | return CAIRO_STATUS_SUCCESS; |
182 | } |
183 | |
184 | static cairo_surface_t * |
185 | lpr_printer_create_cairo_surface (GtkPrinter *printer, |
186 | GtkPrintSettings *settings, |
187 | double width, |
188 | double height, |
189 | GIOChannel *cache_io) |
190 | { |
191 | cairo_surface_t *surface; |
192 | |
193 | surface = cairo_ps_surface_create_for_stream (write_func: _cairo_write, closure: cache_io, width_in_points: width, height_in_points: height); |
194 | |
195 | cairo_surface_set_fallback_resolution (surface, |
196 | x_pixels_per_inch: 2.0 * gtk_print_settings_get_printer_lpi (settings), |
197 | y_pixels_per_inch: 2.0 * gtk_print_settings_get_printer_lpi (settings)); |
198 | |
199 | return surface; |
200 | } |
201 | |
202 | typedef struct { |
203 | GtkPrintBackend *backend; |
204 | GtkPrintJobCompleteFunc callback; |
205 | GtkPrintJob *job; |
206 | gpointer user_data; |
207 | GDestroyNotify dnotify; |
208 | |
209 | GIOChannel *in; |
210 | } _PrintStreamData; |
211 | |
212 | static void |
213 | lpr_print_cb (GtkPrintBackendLpr *print_backend, |
214 | GError *error, |
215 | gpointer user_data) |
216 | { |
217 | _PrintStreamData *ps = (_PrintStreamData *) user_data; |
218 | |
219 | if (ps->in != NULL) |
220 | g_io_channel_unref (channel: ps->in); |
221 | |
222 | if (ps->callback) |
223 | ps->callback (ps->job, ps->user_data, error); |
224 | |
225 | if (ps->dnotify) |
226 | ps->dnotify (ps->user_data); |
227 | |
228 | gtk_print_job_set_status (job: ps->job, |
229 | status: error ? GTK_PRINT_STATUS_FINISHED_ABORTED |
230 | : GTK_PRINT_STATUS_FINISHED); |
231 | |
232 | if (ps->job) |
233 | g_object_unref (object: ps->job); |
234 | |
235 | g_free (mem: ps); |
236 | } |
237 | |
238 | static gboolean |
239 | lpr_write (GIOChannel *source, |
240 | GIOCondition con, |
241 | gpointer user_data) |
242 | { |
243 | char buf[_LPR_MAX_CHUNK_SIZE]; |
244 | gsize bytes_read; |
245 | GError *error; |
246 | GIOStatus status; |
247 | _PrintStreamData *ps = (_PrintStreamData *) user_data; |
248 | |
249 | error = NULL; |
250 | |
251 | status = |
252 | g_io_channel_read_chars (channel: source, |
253 | buf, |
254 | _LPR_MAX_CHUNK_SIZE, |
255 | bytes_read: &bytes_read, |
256 | error: &error); |
257 | |
258 | if (status != G_IO_STATUS_ERROR) |
259 | { |
260 | gsize bytes_written; |
261 | |
262 | g_io_channel_write_chars (channel: ps->in, |
263 | buf, |
264 | count: bytes_read, |
265 | bytes_written: &bytes_written, |
266 | error: &error); |
267 | } |
268 | |
269 | if (error != NULL || status == G_IO_STATUS_EOF) |
270 | { |
271 | lpr_print_cb (GTK_PRINT_BACKEND_LPR (ps->backend), |
272 | error, user_data); |
273 | |
274 | |
275 | if (error != NULL) |
276 | { |
277 | GTK_NOTE (PRINTING, |
278 | g_print ("LPR Backend: %s\n" , error->message)); |
279 | |
280 | g_error_free (error); |
281 | } |
282 | |
283 | return FALSE; |
284 | } |
285 | |
286 | GTK_NOTE (PRINTING, |
287 | g_print ("LPR Backend: Writing %" G_GSIZE_FORMAT " byte chunk to lpr pipe\n" , bytes_read)); |
288 | |
289 | |
290 | return TRUE; |
291 | } |
292 | |
293 | #define LPR_COMMAND "lpr" |
294 | |
295 | static void |
296 | gtk_print_backend_lpr_print_stream (GtkPrintBackend *print_backend, |
297 | GtkPrintJob *job, |
298 | GIOChannel *data_io, |
299 | GtkPrintJobCompleteFunc callback, |
300 | gpointer user_data, |
301 | GDestroyNotify dnotify) |
302 | { |
303 | GError *print_error = NULL; |
304 | _PrintStreamData *ps; |
305 | GtkPrintSettings *settings; |
306 | int argc; |
307 | int in_fd; |
308 | char **argv = NULL; |
309 | const char *cmd_line; |
310 | |
311 | settings = gtk_print_job_get_settings (job); |
312 | |
313 | cmd_line = gtk_print_settings_get (settings, key: "lpr-commandline" ); |
314 | if (cmd_line == NULL) |
315 | cmd_line = LPR_COMMAND; |
316 | |
317 | ps = g_new0 (_PrintStreamData, 1); |
318 | ps->callback = callback; |
319 | ps->user_data = user_data; |
320 | ps->dnotify = dnotify; |
321 | ps->job = g_object_ref (job); |
322 | ps->in = NULL; |
323 | |
324 | /* spawn lpr with pipes and pipe ps file to lpr */ |
325 | if (!g_shell_parse_argv (command_line: cmd_line, argcp: &argc, argvp: &argv, error: &print_error)) |
326 | goto out; |
327 | |
328 | if (!g_spawn_async_with_pipes (NULL, |
329 | argv, |
330 | NULL, |
331 | flags: G_SPAWN_SEARCH_PATH, |
332 | NULL, |
333 | NULL, |
334 | NULL, |
335 | standard_input: &in_fd, |
336 | NULL, |
337 | NULL, |
338 | error: &print_error)) |
339 | goto out; |
340 | |
341 | ps->in = g_io_channel_unix_new (fd: in_fd); |
342 | |
343 | g_io_channel_set_encoding (channel: ps->in, NULL, error: &print_error); |
344 | if (print_error != NULL) |
345 | goto out; |
346 | |
347 | g_io_channel_set_close_on_unref (channel: ps->in, TRUE); |
348 | |
349 | g_io_add_watch (channel: data_io, |
350 | condition: G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_HUP, |
351 | func: (GIOFunc) lpr_write, |
352 | user_data: ps); |
353 | |
354 | out: |
355 | if (argv != NULL) |
356 | g_strfreev (str_array: argv); |
357 | |
358 | if (print_error != NULL) |
359 | { |
360 | lpr_print_cb (GTK_PRINT_BACKEND_LPR (print_backend), error: print_error, user_data: ps); |
361 | g_error_free (error: print_error); |
362 | |
363 | if (ps->in != NULL) |
364 | g_io_channel_unref (channel: ps->in); |
365 | if (ps->job) |
366 | g_object_unref (object: ps->job); |
367 | g_free (mem: ps); |
368 | } |
369 | } |
370 | |
371 | static void |
372 | gtk_print_backend_lpr_init (GtkPrintBackendLpr *backend) |
373 | { |
374 | GtkPrinter *printer; |
375 | |
376 | printer = g_object_new (GTK_TYPE_PRINTER, |
377 | first_property_name: "name" , _("Print to LPR" ), |
378 | "backend" , backend, |
379 | "is-virtual" , FALSE, |
380 | "accepts-pdf" , TRUE, |
381 | "accepts-ps" , TRUE, |
382 | NULL); |
383 | gtk_printer_set_has_details (printer, TRUE); |
384 | gtk_printer_set_icon_name (printer, icon: "printer" ); |
385 | gtk_printer_set_is_active (printer, TRUE); |
386 | gtk_printer_set_is_default (printer, TRUE); |
387 | |
388 | gtk_print_backend_add_printer (GTK_PRINT_BACKEND (backend), printer); |
389 | g_object_unref (object: printer); |
390 | gtk_print_backend_set_list_done (GTK_PRINT_BACKEND (backend)); |
391 | } |
392 | |
393 | static GtkPrinterOptionSet * |
394 | lpr_printer_get_options (GtkPrinter *printer, |
395 | GtkPrintSettings *settings, |
396 | GtkPageSetup *page_setup, |
397 | GtkPrintCapabilities capabilities) |
398 | { |
399 | GtkPrinterOptionSet *set; |
400 | GtkPrinterOption *option; |
401 | const char *command; |
402 | const char *n_up[] = {"1" , "2" , "4" , "6" , "9" , "16" }; |
403 | |
404 | set = gtk_printer_option_set_new (); |
405 | |
406 | option = gtk_printer_option_new (name: "gtk-n-up" , _("Pages Per Sheet" ), type: GTK_PRINTER_OPTION_TYPE_PICKONE); |
407 | gtk_printer_option_choices_from_array (option, G_N_ELEMENTS (n_up), choices: n_up, choices_display: n_up); |
408 | gtk_printer_option_set (option, value: "1" ); |
409 | gtk_printer_option_set_add (set, option); |
410 | g_object_unref (object: option); |
411 | |
412 | option = gtk_printer_option_new (name: "gtk-main-page-custom-input" , _("Command Line" ), type: GTK_PRINTER_OPTION_TYPE_STRING); |
413 | gtk_printer_option_set_activates_default (option, TRUE); |
414 | option->group = g_strdup (str: "GtkPrintDialogExtension" ); |
415 | if (settings != NULL && |
416 | (command = gtk_print_settings_get (settings, key: "lpr-commandline" ))!= NULL) |
417 | gtk_printer_option_set (option, value: command); |
418 | else |
419 | gtk_printer_option_set (option, LPR_COMMAND); |
420 | gtk_printer_option_set_add (set, option); |
421 | |
422 | return set; |
423 | } |
424 | |
425 | static void |
426 | lpr_printer_get_settings_from_options (GtkPrinter *printer, |
427 | GtkPrinterOptionSet *options, |
428 | GtkPrintSettings *settings) |
429 | { |
430 | GtkPrinterOption *option; |
431 | |
432 | option = gtk_printer_option_set_lookup (set: options, name: "gtk-main-page-custom-input" ); |
433 | if (option) |
434 | gtk_print_settings_set (settings, key: "lpr-commandline" , value: option->value); |
435 | |
436 | option = gtk_printer_option_set_lookup (set: options, name: "gtk-n-up" ); |
437 | if (option) |
438 | gtk_print_settings_set (settings, GTK_PRINT_SETTINGS_NUMBER_UP, value: option->value); |
439 | |
440 | option = gtk_printer_option_set_lookup (set: options, name: "gtk-n-up-layout" ); |
441 | if (option) |
442 | gtk_print_settings_set (settings, GTK_PRINT_SETTINGS_NUMBER_UP_LAYOUT, value: option->value); |
443 | } |
444 | |
445 | static void |
446 | lpr_printer_prepare_for_print (GtkPrinter *printer, |
447 | GtkPrintJob *print_job, |
448 | GtkPrintSettings *settings, |
449 | GtkPageSetup *page_setup) |
450 | { |
451 | double scale; |
452 | GtkPrintPages pages; |
453 | GtkPageRange *ranges; |
454 | int n_ranges; |
455 | |
456 | pages = gtk_print_settings_get_print_pages (settings); |
457 | gtk_print_job_set_pages (job: print_job, pages); |
458 | |
459 | if (pages == GTK_PRINT_PAGES_RANGES) |
460 | ranges = gtk_print_settings_get_page_ranges (settings, num_ranges: &n_ranges); |
461 | else |
462 | { |
463 | ranges = NULL; |
464 | n_ranges = 0; |
465 | } |
466 | |
467 | gtk_print_job_set_page_ranges (job: print_job, ranges, n_ranges); |
468 | gtk_print_job_set_collate (job: print_job, collate: gtk_print_settings_get_collate (settings)); |
469 | gtk_print_job_set_reverse (job: print_job, reverse: gtk_print_settings_get_reverse (settings)); |
470 | gtk_print_job_set_num_copies (job: print_job, num_copies: gtk_print_settings_get_n_copies (settings)); |
471 | gtk_print_job_set_n_up (job: print_job, n_up: gtk_print_settings_get_number_up (settings)); |
472 | gtk_print_job_set_n_up_layout (job: print_job, layout: gtk_print_settings_get_number_up_layout (settings)); |
473 | |
474 | scale = gtk_print_settings_get_scale (settings); |
475 | if (scale != 100.0) |
476 | gtk_print_job_set_scale (job: print_job, scale: scale / 100.0); |
477 | |
478 | gtk_print_job_set_page_set (job: print_job, page_set: gtk_print_settings_get_page_set (settings)); |
479 | gtk_print_job_set_rotate (job: print_job, TRUE); |
480 | } |
481 | |