gtkprintbackendlpr.c 13.6 KB
Newer Older
Cody Russell's avatar
Cody Russell committed
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
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
 *
 * 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, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

22
#include "config.h"
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>

#include <errno.h>
#include <cairo.h>
#include <cairo-ps.h>

#include <glib/gi18n-lib.h>

36
#include <gtk/gtk.h>
37
#include "gtkprinter-private.h"
38

39
40
#include "gtkprintbackendlpr.h"

41
42
43
44
45
46
47
48
49
50
51
52
typedef struct _GtkPrintBackendLprClass GtkPrintBackendLprClass;

#define GTK_PRINT_BACKEND_LPR_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_PRINT_BACKEND_LPR, GtkPrintBackendLprClass))
#define GTK_IS_PRINT_BACKEND_LPR_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_PRINT_BACKEND_LPR))
#define GTK_PRINT_BACKEND_LPR_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_PRINT_BACKEND_LPR, GtkPrintBackendLprClass))

#define _LPR_MAX_CHUNK_SIZE 8192

static GType print_backend_lpr_type = 0;

struct _GtkPrintBackendLprClass
{
Alexander Larsson's avatar
Alexander Larsson committed
53
  GtkPrintBackendClass parent_class;
54
55
56
57
};

struct _GtkPrintBackendLpr
{
Alexander Larsson's avatar
Alexander Larsson committed
58
  GtkPrintBackend parent_instance;
59
60
61
62
63
64
65
66
67
68
69
};

static GObjectClass *backend_parent_class;

static void                 gtk_print_backend_lpr_class_init      (GtkPrintBackendLprClass *class);
static void                 gtk_print_backend_lpr_init            (GtkPrintBackendLpr      *impl);
static void                 lpr_printer_get_settings_from_options (GtkPrinter              *printer,
								   GtkPrinterOptionSet     *options,
								   GtkPrintSettings        *settings);
static GtkPrinterOptionSet *lpr_printer_get_options               (GtkPrinter              *printer,
								   GtkPrintSettings        *settings,
70
71
								   GtkPageSetup            *page_setup,
								   GtkPrintCapabilities     capabilities);
72
73
74
75
static void                 lpr_printer_prepare_for_print         (GtkPrinter              *printer,
								   GtkPrintJob             *print_job,
								   GtkPrintSettings        *settings,
								   GtkPageSetup            *page_setup);
Alexander Larsson's avatar
Alexander Larsson committed
76
static cairo_surface_t *    lpr_printer_create_cairo_surface      (GtkPrinter              *printer,
77
								   GtkPrintSettings        *settings,
Alexander Larsson's avatar
Alexander Larsson committed
78
79
								   gdouble                  width,
								   gdouble                  height,
80
								   GIOChannel              *cache_io);
Alexander Larsson's avatar
Alexander Larsson committed
81
82
static void                 gtk_print_backend_lpr_print_stream    (GtkPrintBackend         *print_backend,
								   GtkPrintJob             *job,
83
								   GIOChannel              *data_io,
Alexander Larsson's avatar
Alexander Larsson committed
84
85
								   GtkPrintJobCompleteFunc  callback,
								   gpointer                 user_data,
86
								   GDestroyNotify           dnotify);
87
88
89
90

static void
gtk_print_backend_lpr_register_type (GTypeModule *module)
{
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
  static const GTypeInfo print_backend_lpr_info =
  {
    sizeof (GtkPrintBackendLprClass),
    NULL,		/* base_init */
    NULL,		/* base_finalize */
    (GClassInitFunc) gtk_print_backend_lpr_class_init,
    NULL,		/* class_finalize */
    NULL,		/* class_data */
    sizeof (GtkPrintBackendLpr),
    0,		/* n_preallocs */
    (GInstanceInitFunc) gtk_print_backend_lpr_init,
  };

  print_backend_lpr_type = g_type_module_register_type (module,
                                                        GTK_TYPE_PRINT_BACKEND,
                                                        "GtkPrintBackendLpr",
                                                        &print_backend_lpr_info, 0);
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
}

G_MODULE_EXPORT void 
pb_module_init (GTypeModule *module)
{
  gtk_print_backend_lpr_register_type (module);
}

G_MODULE_EXPORT void 
pb_module_exit (void)
{

}
  
G_MODULE_EXPORT GtkPrintBackend * 
pb_module_create (void)
{
  return gtk_print_backend_lpr_new ();
}

/*
 * GtkPrintBackendLpr
 */
GType
gtk_print_backend_lpr_get_type (void)
{
  return print_backend_lpr_type;
}

/**
 * gtk_print_backend_lpr_new:
 *
 * Creates a new #GtkPrintBackendLpr object. #GtkPrintBackendLpr
 * implements the #GtkPrintBackend interface with direct access to
 * the filesystem using Unix/Linux API calls
 *
 * Return value: the new #GtkPrintBackendLpr object
 **/
GtkPrintBackend *
gtk_print_backend_lpr_new (void)
{
  return g_object_new (GTK_TYPE_PRINT_BACKEND_LPR, NULL);
}

static void
gtk_print_backend_lpr_class_init (GtkPrintBackendLprClass *class)
{
Alexander Larsson's avatar
Alexander Larsson committed
155
156
  GtkPrintBackendClass *backend_class = GTK_PRINT_BACKEND_CLASS (class);
  
157
158
  backend_parent_class = g_type_class_peek_parent (class);

Alexander Larsson's avatar
Alexander Larsson committed
159
160
161
162
163
  backend_class->print_stream = gtk_print_backend_lpr_print_stream;
  backend_class->printer_create_cairo_surface = lpr_printer_create_cairo_surface;
  backend_class->printer_get_options = lpr_printer_get_options;
  backend_class->printer_get_settings_from_options = lpr_printer_get_settings_from_options;
  backend_class->printer_prepare_for_print = lpr_printer_prepare_for_print;
164
165
166
}

static cairo_status_t
167
_cairo_write (void                *closure,
168
169
170
              const unsigned char *data,
              unsigned int         length)
{
171
172
173
174
175
176
177
178
179
  GIOChannel *io = (GIOChannel *)closure;
  gsize written;
  GError *error;

  error = NULL;

  GTK_NOTE (PRINTING,
            g_print ("LPR Backend: Writting %i byte chunk to temp file\n", length));

180
181
  while (length > 0) 
    {
182
      g_io_channel_write_chars (io, data, length, &written, &error);
183

184
      if (error != NULL)
185
	{
186
187
188
189
	  GTK_NOTE (PRINTING,
                     g_print ("LPR Backend: Error writting to temp file, %s\n", error->message));

          g_error_free (error);
190
191
192
	  return CAIRO_STATUS_WRITE_ERROR;
	}    

193
194
195
      GTK_NOTE (PRINTING,
                g_print ("LPR Backend: Wrote %i bytes to temp file\n", written));

196
197
198
199
200
      data += written;
      length -= written;
    }

  return CAIRO_STATUS_SUCCESS;
201
202
203
}

static cairo_surface_t *
204
205
206
207
lpr_printer_create_cairo_surface (GtkPrinter       *printer,
				  GtkPrintSettings *settings,
				  gdouble           width, 
				  gdouble           height,
208
				  GIOChannel       *cache_io)
209
210
211
{
  cairo_surface_t *surface;
  
212
  surface = cairo_ps_surface_create_for_stream (_cairo_write, cache_io, width, height);
213
214

  /* TODO: DPI from settings object? */
215
  cairo_surface_set_fallback_resolution (surface, 300, 300);
216
217
218
219
220
221
222
223
224
225
226

  return surface;
}

typedef struct {
  GtkPrintBackend *backend;
  GtkPrintJobCompleteFunc callback;
  GtkPrintJob *job;
  gpointer user_data;
  GDestroyNotify dnotify;

227
  GIOChannel *in;
228
229
230
231
} _PrintStreamData;

static void
lpr_print_cb (GtkPrintBackendLpr *print_backend,
232
233
              GError             *error,
              gpointer            user_data)
234
235
236
{
  _PrintStreamData *ps = (_PrintStreamData *) user_data;

237
238
  if (ps->in != NULL) 
    g_io_channel_unref (ps->in);
239
240
241
242
243
244
245

  if (ps->callback)
    ps->callback (ps->job, ps->user_data, error);

  if (ps->dnotify)
    ps->dnotify (ps->user_data);

Matthias Clasen's avatar
Matthias Clasen committed
246
247
248
  gtk_print_job_set_status (ps->job, 
			    error ? GTK_PRINT_STATUS_FINISHED_ABORTED 
			          : GTK_PRINT_STATUS_FINISHED);
249
250
251
252
253
254
255
256

  if (ps->job)
    g_object_unref (ps->job);
  
  g_free (ps);
}

static gboolean
257
258
259
lpr_write (GIOChannel   *source,
           GIOCondition  con,
           gpointer      user_data)
260
261
262
263
{
  gchar buf[_LPR_MAX_CHUNK_SIZE];
  gsize bytes_read;
  GError *error;
264
  GIOStatus status;
265
266
267
268
  _PrintStreamData *ps = (_PrintStreamData *) user_data;

  error = NULL;

269
270
271
272
273
274
  status = 
    g_io_channel_read_chars (source,
                             buf,
                             _LPR_MAX_CHUNK_SIZE,
                             &bytes_read,
                             &error);
275

276
  if (status != G_IO_STATUS_ERROR)
277
    {
278
279
280
281
282
283
284
      gsize bytes_written;

      g_io_channel_write_chars (ps->in, 
                                buf, 
				bytes_read, 
				&bytes_written, 
				&error);
285
286
    }

287
  if (error != NULL || status == G_IO_STATUS_EOF)
288
    {
Matthias Clasen's avatar
Matthias Clasen committed
289
290
291
      lpr_print_cb (GTK_PRINT_BACKEND_LPR (ps->backend), 
		    error, user_data);

292

293
294
295
296
297
298
299
300
      if (error != NULL)
        {
          GTK_NOTE (PRINTING,
                    g_print ("LPR Backend: %s\n", error->message));

          g_error_free (error);
        } 

301
302
303
      return FALSE;
    }

304
305
306
307
  GTK_NOTE (PRINTING,
            g_print ("LPR Backend: Writting %i byte chunk to lpr pipe\n", bytes_read));


308
309
310
311
312
313
  return TRUE;
}

#define LPR_COMMAND "lpr"

static void
314
315
gtk_print_backend_lpr_print_stream (GtkPrintBackend        *print_backend,
				    GtkPrintJob            *job,
316
				    GIOChannel             *data_io,
317
				    GtkPrintJobCompleteFunc callback,
318
				    gpointer                user_data,
319
				    GDestroyNotify          dnotify)
320
{
Matthias Clasen's avatar
Matthias Clasen committed
321
  GError *print_error = NULL;
322
323
324
325
  GtkPrinter *printer;
  _PrintStreamData *ps;
  GtkPrintSettings *settings;
  gint argc;  
326
327
328
329
  gint in_fd;
  gchar **argv = NULL;
  const char *cmd_line;
  
330
331
332
333
334
335
336
337
338
339
340
341
  printer = gtk_print_job_get_printer (job);
  settings = gtk_print_job_get_settings (job);

  cmd_line = gtk_print_settings_get (settings, "lpr-commandline");
  if (cmd_line == NULL)
    cmd_line = LPR_COMMAND;
  
  ps = g_new0 (_PrintStreamData, 1);
  ps->callback = callback;
  ps->user_data = user_data;
  ps->dnotify = dnotify;
  ps->job = g_object_ref (job);
342
  ps->in = NULL;
343
344

 /* spawn lpr with pipes and pipe ps file to lpr */
Matthias Clasen's avatar
Matthias Clasen committed
345
  if (!g_shell_parse_argv (cmd_line, &argc, &argv, &print_error))
346
    goto out; 
347
348
349
350
351
352
353
354

  if (!g_spawn_async_with_pipes (NULL,
                                 argv,
                                 NULL,
                                 G_SPAWN_SEARCH_PATH,
                                 NULL,
                                 NULL,
                                 NULL,
355
356
357
                                 &in_fd,
                                 NULL,
                                 NULL,
Matthias Clasen's avatar
Matthias Clasen committed
358
                                 &print_error))
359
360
      goto out;

361
362
363
364
365
366
367
368
369
  ps->in = g_io_channel_unix_new (in_fd);

  g_io_channel_set_encoding (ps->in, NULL, &print_error);
  if (print_error != NULL)
    {
      if (ps->in != NULL)
        g_io_channel_unref (ps->in);
      
      goto out;
370
371
    }

Matthias Clasen's avatar
Matthias Clasen committed
372
373
  g_io_channel_set_close_on_unref (ps->in, TRUE);

374
  g_io_add_watch (data_io, 
375
376
377
378
379
                  G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_HUP,
                  (GIOFunc) lpr_write,
                  ps);

 out:
380
381
382
383
384
385
386
387
388
  if (argv != NULL)
    g_strfreev (argv);

  if (print_error != NULL)
    {
      lpr_print_cb (GTK_PRINT_BACKEND_LPR (print_backend),
		    print_error, ps);
      g_error_free (print_error);
    }
389
390
391
}

static void
Alexander Larsson's avatar
Alexander Larsson committed
392
gtk_print_backend_lpr_init (GtkPrintBackendLpr *backend)
393
394
395
396
{
  GtkPrinter *printer;

  printer = gtk_printer_new (_("Print to LPR"),
Alexander Larsson's avatar
Alexander Larsson committed
397
			     GTK_PRINT_BACKEND (backend),
398
399
			     TRUE); 
  gtk_printer_set_has_details (printer, TRUE);
400
  gtk_printer_set_icon_name (printer, "gtk-print");
401
  gtk_printer_set_is_active (printer, TRUE);
Alexander Larsson's avatar
Alexander Larsson committed
402
  gtk_printer_set_is_default (printer, TRUE);
403

Alexander Larsson's avatar
Alexander Larsson committed
404
405
  gtk_print_backend_add_printer (GTK_PRINT_BACKEND (backend), printer);
  g_object_unref (printer);
406
  gtk_print_backend_set_list_done (GTK_PRINT_BACKEND (backend));
407
408
409
}

static GtkPrinterOptionSet *
410
411
412
413
lpr_printer_get_options (GtkPrinter           *printer,
			 GtkPrintSettings     *settings,
			 GtkPageSetup         *page_setup,
			 GtkPrintCapabilities  capabilities)
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
{
  GtkPrinterOptionSet *set;
  GtkPrinterOption *option;
  const char *command;
  char *n_up[] = {"1", "2", "4", "6", "9", "16" };

  set = gtk_printer_option_set_new ();

  option = gtk_printer_option_new ("gtk-n-up", _("Pages Per Sheet"), GTK_PRINTER_OPTION_TYPE_PICKONE);
  gtk_printer_option_choices_from_array (option, G_N_ELEMENTS (n_up),
					 n_up, n_up);
  gtk_printer_option_set (option, "1");
  gtk_printer_option_set_add (set, option);
  g_object_unref (option);

  option = gtk_printer_option_new ("gtk-main-page-custom-input", _("Command Line"), GTK_PRINTER_OPTION_TYPE_STRING);
430
  option->group = g_strdup ("GtkPrintDialogExtension");
431
432
433
434
435
436
437
438
439
440
441
  if (settings != NULL &&
      (command = gtk_print_settings_get (settings, "lpr-commandline"))!= NULL)
    gtk_printer_option_set (option, command);
  else
    gtk_printer_option_set (option, LPR_COMMAND);
  gtk_printer_option_set_add (set, option);
    
  return set;
}

static void
442
lpr_printer_get_settings_from_options (GtkPrinter          *printer,
443
				       GtkPrinterOptionSet *options,
444
				       GtkPrintSettings    *settings)
445
446
447
448
449
450
451
452
{
  GtkPrinterOption *option;

  option = gtk_printer_option_set_lookup (options, "gtk-main-page-custom-input");
  gtk_print_settings_set (settings, "lpr-commandline", option->value);
}

static void
453
454
lpr_printer_prepare_for_print (GtkPrinter       *printer,
			       GtkPrintJob      *print_job,
455
			       GtkPrintSettings *settings,
456
			       GtkPageSetup     *page_setup)
457
458
459
460
461
462
463
464
465
466
467
468
469
470
{
  double scale;

  print_job->print_pages = gtk_print_settings_get_print_pages (settings);
  print_job->page_ranges = NULL;
  print_job->num_page_ranges = 0;
  
  if (print_job->print_pages == GTK_PRINT_PAGES_RANGES)
    print_job->page_ranges =
      gtk_print_settings_get_page_ranges (settings,
					  &print_job->num_page_ranges);
  
  print_job->collate = gtk_print_settings_get_collate (settings);
  print_job->reverse = gtk_print_settings_get_reverse (settings);
Alexander Larsson's avatar
Alexander Larsson committed
471
  print_job->num_copies = gtk_print_settings_get_n_copies (settings);
472
473
474
475
476
477
478
479

  scale = gtk_print_settings_get_scale (settings);
  if (scale != 100.0)
    print_job->scale = scale/100.0;

  print_job->page_set = gtk_print_settings_get_page_set (settings);
  print_job->rotate_to_orientation = TRUE;
}