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
39typedef 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
47struct _GtkPrintBackendLprClass
48{
49 GtkPrintBackendClass parent_class;
50};
51
52struct _GtkPrintBackendLpr
53{
54 GtkPrintBackend parent_instance;
55};
56
57static GObjectClass *backend_parent_class;
58
59static void lpr_printer_get_settings_from_options (GtkPrinter *printer,
60 GtkPrinterOptionSet *options,
61 GtkPrintSettings *settings);
62static GtkPrinterOptionSet *lpr_printer_get_options (GtkPrinter *printer,
63 GtkPrintSettings *settings,
64 GtkPageSetup *page_setup,
65 GtkPrintCapabilities capabilities);
66static void lpr_printer_prepare_for_print (GtkPrinter *printer,
67 GtkPrintJob *print_job,
68 GtkPrintSettings *settings,
69 GtkPageSetup *page_setup);
70static cairo_surface_t * lpr_printer_create_cairo_surface (GtkPrinter *printer,
71 GtkPrintSettings *settings,
72 double width,
73 double height,
74 GIOChannel *cache_io);
75static 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
82G_DEFINE_DYNAMIC_TYPE (GtkPrintBackendLpr, gtk_print_backend_lpr, GTK_TYPE_PRINT_BACKEND)
83
84void
85g_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
97void
98g_io_module_unload (GIOModule *module)
99{
100}
101
102char **
103g_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 **/
122GtkPrintBackend *
123gtk_print_backend_lpr_new (void)
124{
125 return g_object_new (GTK_TYPE_PRINT_BACKEND_LPR, NULL);
126}
127
128static void
129gtk_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
142static void
143gtk_print_backend_lpr_class_finalize (GtkPrintBackendLprClass *class)
144{
145}
146
147static 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
184static cairo_surface_t *
185lpr_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
202typedef struct {
203 GtkPrintBackend *backend;
204 GtkPrintJobCompleteFunc callback;
205 GtkPrintJob *job;
206 gpointer user_data;
207 GDestroyNotify dnotify;
208
209 GIOChannel *in;
210} _PrintStreamData;
211
212static void
213lpr_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
238static gboolean
239lpr_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
295static void
296gtk_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
371static void
372gtk_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
393static GtkPrinterOptionSet *
394lpr_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
425static void
426lpr_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
445static void
446lpr_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

source code of gtk/modules/printbackends/gtkprintbackendlpr.c