1 /* GTK - The GIMP Toolkit
2  * gtkprintbackendpdf.c: Test implementation of GtkPrintBackend
3  * for printing to a test
4  * Copyright (C) 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, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21 
22 #include "config.h"
23 
24 #include <unistd.h>
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <fcntl.h>
28 #include <stdlib.h>
29 #include <string.h>
30 
31 #include <errno.h>
32 #include <cairo.h>
33 #include <cairo-pdf.h>
34 #include <cairo-ps.h>
35 
36 #include <glib/gi18n-lib.h>
37 
38 #include <gtk/gtkprintbackend.h>
39 #include <gtk/gtkunixprint.h>
40 #include <gtk/gtkprinter-private.h>
41 
42 #include "gtkprintbackendtest.h"
43 
44 
45 typedef struct _GtkPrintBackendTestClass GtkPrintBackendTestClass;
46 
47 #define GTK_PRINT_BACKEND_TEST_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_PRINT_BACKEND_TEST, GtkPrintBackendTestClass))
48 #define GTK_IS_PRINT_BACKEND_TEST_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_PRINT_BACKEND_TEST))
49 #define GTK_PRINT_BACKENDTEST_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_PRINT_BACKEND_TEST, GtkPrintBackendTestClass))
50 
51 #define _STREAM_MAX_CHUNK_SIZE 8192
52 
53 static GType print_backend_test_type = 0;
54 
55 struct _GtkPrintBackendTestClass
56 {
57   GtkPrintBackendClass parent_class;
58 };
59 
60 struct _GtkPrintBackendTest
61 {
62   GtkPrintBackend parent_instance;
63 };
64 
65 typedef enum
66 {
67   FORMAT_PDF,
68   FORMAT_PS,
69   N_FORMATS
70 } OutputFormat;
71 
72 static const gchar* formats[N_FORMATS] =
73 {
74   "pdf",
75   "ps"
76 };
77 
78 static GObjectClass *backend_parent_class;
79 
80 static void                 gtk_print_backend_test_class_init      (GtkPrintBackendTestClass *class);
81 static void                 gtk_print_backend_test_init            (GtkPrintBackendTest      *impl);
82 static void                 test_printer_get_settings_from_options (GtkPrinter              *printer,
83 								    GtkPrinterOptionSet     *options,
84 								    GtkPrintSettings        *settings);
85 static GtkPrinterOptionSet *test_printer_get_options               (GtkPrinter              *printer,
86 								    GtkPrintSettings        *settings,
87 								    GtkPageSetup            *page_setup,
88 								    GtkPrintCapabilities     capabilities);
89 static void                 test_printer_prepare_for_print         (GtkPrinter              *printer,
90 								    GtkPrintJob             *print_job,
91 								    GtkPrintSettings        *settings,
92 								    GtkPageSetup            *page_setup);
93 static void                 gtk_print_backend_test_print_stream    (GtkPrintBackend         *print_backend,
94 								    GtkPrintJob             *job,
95 								    GIOChannel              *data_io,
96 								    GtkPrintJobCompleteFunc  callback,
97 								    gpointer                 user_data,
98 								    GDestroyNotify           dnotify);
99 static cairo_surface_t *    test_printer_create_cairo_surface      (GtkPrinter              *printer,
100 								    GtkPrintSettings        *settings,
101 								    gdouble                  width,
102 								    gdouble                  height,
103 								    GIOChannel              *cache_io);
104 
105 static void                 test_printer_request_details           (GtkPrinter              *printer);
106 
107 static void
gtk_print_backend_test_register_type(GTypeModule * module)108 gtk_print_backend_test_register_type (GTypeModule *module)
109 {
110   const GTypeInfo print_backend_test_info =
111   {
112     sizeof (GtkPrintBackendTestClass),
113     NULL,		/* base_init */
114     NULL,		/* base_finalize */
115     (GClassInitFunc) gtk_print_backend_test_class_init,
116     NULL,		/* class_finalize */
117     NULL,		/* class_data */
118     sizeof (GtkPrintBackendTest),
119     0,		/* n_preallocs */
120     (GInstanceInitFunc) gtk_print_backend_test_init,
121   };
122 
123   print_backend_test_type = g_type_module_register_type (module,
124                                                          GTK_TYPE_PRINT_BACKEND,
125                                                          "GtkPrintBackendTest",
126                                                          &print_backend_test_info, 0);
127 }
128 
129 G_MODULE_EXPORT void
pb_module_init(GTypeModule * module)130 pb_module_init (GTypeModule *module)
131 {
132   gtk_print_backend_test_register_type (module);
133 }
134 
135 G_MODULE_EXPORT void
pb_module_exit(void)136 pb_module_exit (void)
137 {
138 
139 }
140 
141 G_MODULE_EXPORT GtkPrintBackend *
pb_module_create(void)142 pb_module_create (void)
143 {
144   return gtk_print_backend_test_new ();
145 }
146 
147 /*
148  * GtkPrintBackendTest
149  */
150 GType
gtk_print_backend_test_get_type(void)151 gtk_print_backend_test_get_type (void)
152 {
153   return print_backend_test_type;
154 }
155 
156 /**
157  * gtk_print_backend_test_new:
158  *
159  * Creates a new #GtkPrintBackendTest object. #GtkPrintBackendTest
160  * implements the #GtkPrintBackend interface with direct access to
161  * the testsystem using Unix/Linux API calls
162  *
163  * Return value: the new #GtkPrintBackendTest object
164  **/
165 GtkPrintBackend *
gtk_print_backend_test_new(void)166 gtk_print_backend_test_new (void)
167 {
168   return g_object_new (GTK_TYPE_PRINT_BACKEND_TEST, NULL);
169 }
170 
171 static void
gtk_print_backend_test_class_init(GtkPrintBackendTestClass * class)172 gtk_print_backend_test_class_init (GtkPrintBackendTestClass *class)
173 {
174   GtkPrintBackendClass *backend_class = GTK_PRINT_BACKEND_CLASS (class);
175 
176   backend_parent_class = g_type_class_peek_parent (class);
177 
178   backend_class->print_stream = gtk_print_backend_test_print_stream;
179   backend_class->printer_create_cairo_surface = test_printer_create_cairo_surface;
180   backend_class->printer_get_options = test_printer_get_options;
181   backend_class->printer_get_settings_from_options = test_printer_get_settings_from_options;
182   backend_class->printer_prepare_for_print = test_printer_prepare_for_print;
183   backend_class->printer_request_details = test_printer_request_details;
184 }
185 
186 /* return N_FORMATS if no explicit format in the settings */
187 static OutputFormat
format_from_settings(GtkPrintSettings * settings)188 format_from_settings (GtkPrintSettings *settings)
189 {
190   const gchar *value;
191   gint i;
192 
193   if (settings == NULL)
194     return N_FORMATS;
195 
196   value = gtk_print_settings_get (settings, GTK_PRINT_SETTINGS_OUTPUT_FILE_FORMAT);
197   if (value == NULL)
198     return N_FORMATS;
199 
200   for (i = 0; i < N_FORMATS; ++i)
201     if (strcmp (value, formats[i]) == 0)
202       break;
203 
204   g_assert (i < N_FORMATS);
205 
206   return (OutputFormat) i;
207 }
208 
209 static gchar *
output_test_from_settings(GtkPrintSettings * settings,const gchar * default_format)210 output_test_from_settings (GtkPrintSettings *settings,
211 			   const gchar      *default_format)
212 {
213   gchar *uri = NULL;
214 
215   if (settings)
216     uri = g_strdup (gtk_print_settings_get (settings, GTK_PRINT_SETTINGS_OUTPUT_URI));
217 
218   if (uri == NULL)
219     {
220       const gchar *extension;
221       gchar *name, *locale_name, *path;
222 
223       if (default_format)
224         extension = default_format;
225       else
226         {
227           OutputFormat format;
228 
229           format = format_from_settings (settings);
230           extension = format == FORMAT_PS ? "ps" : "pdf";
231         }
232 
233       /* default filename used for print-to-test */
234       name = g_strdup_printf (_("test-output.%s"), extension);
235       locale_name = g_filename_from_utf8 (name, -1, NULL, NULL, NULL);
236       g_free (name);
237 
238       if (locale_name != NULL)
239         {
240 	  gchar *current_dir = g_get_current_dir ();
241           path = g_build_filename (current_dir, locale_name, NULL);
242           g_free (locale_name);
243 
244           uri = g_filename_to_uri (path, NULL, NULL);
245           g_free (path);
246 	  g_free (current_dir);
247 	}
248     }
249 
250   return uri;
251 }
252 
253 static cairo_status_t
_cairo_write(void * closure,const unsigned char * data,unsigned int length)254 _cairo_write (void                *closure,
255               const unsigned char *data,
256               unsigned int         length)
257 {
258   GIOChannel *io = (GIOChannel *)closure;
259   gsize written;
260   GError *error;
261 
262   error = NULL;
263 
264   GTK_NOTE (PRINTING,
265             g_print ("TEST Backend: Writing %i byte chunk to temp test\n", length));
266 
267   while (length > 0)
268     {
269       g_io_channel_write_chars (io, (const gchar *) data, length, &written, &error);
270 
271       if (error != NULL)
272 	{
273 	  GTK_NOTE (PRINTING,
274                      g_print ("TEST Backend: Error writing to temp test, %s\n", error->message));
275 
276           g_error_free (error);
277 	  return CAIRO_STATUS_WRITE_ERROR;
278 	}
279 
280       GTK_NOTE (PRINTING,
281                 g_print ("TEST Backend: Wrote %i bytes to temp test\n", (int)written));
282 
283       data += written;
284       length -= written;
285     }
286 
287   return CAIRO_STATUS_SUCCESS;
288 }
289 
290 
291 static cairo_surface_t *
test_printer_create_cairo_surface(GtkPrinter * printer,GtkPrintSettings * settings,gdouble width,gdouble height,GIOChannel * cache_io)292 test_printer_create_cairo_surface (GtkPrinter       *printer,
293 				   GtkPrintSettings *settings,
294 				   gdouble           width,
295 				   gdouble           height,
296 				   GIOChannel       *cache_io)
297 {
298   cairo_surface_t *surface;
299   OutputFormat format;
300 
301   format = format_from_settings (settings);
302 
303   if (format == FORMAT_PS)
304     surface = cairo_ps_surface_create_for_stream (_cairo_write, cache_io, width, height);
305   else
306     surface = cairo_pdf_surface_create_for_stream (_cairo_write, cache_io, width, height);
307 
308   cairo_surface_set_fallback_resolution (surface,
309                                          2.0 * gtk_print_settings_get_printer_lpi (settings),
310                                          2.0 * gtk_print_settings_get_printer_lpi (settings));
311 
312   return surface;
313 }
314 
315 typedef struct {
316   GtkPrintBackend *backend;
317   GtkPrintJobCompleteFunc callback;
318   GtkPrintJob *job;
319   GIOChannel *target_io;
320   gpointer user_data;
321   GDestroyNotify dnotify;
322 } _PrintStreamData;
323 
324 static void
test_print_cb(GtkPrintBackendTest * print_backend,GError * error,gpointer user_data)325 test_print_cb (GtkPrintBackendTest *print_backend,
326                GError              *error,
327                gpointer            user_data)
328 {
329   _PrintStreamData *ps = (_PrintStreamData *) user_data;
330 
331   if (ps->target_io != NULL)
332     g_io_channel_unref (ps->target_io);
333 
334   if (ps->callback)
335     ps->callback (ps->job, ps->user_data, error);
336 
337   if (ps->dnotify)
338     ps->dnotify (ps->user_data);
339 
340   gtk_print_job_set_status (ps->job,
341 			    (error != NULL)?GTK_PRINT_STATUS_FINISHED_ABORTED:GTK_PRINT_STATUS_FINISHED);
342 
343   if (ps->job)
344     g_object_unref (ps->job);
345 
346   g_free (ps);
347 }
348 
349 static gboolean
test_write(GIOChannel * source,GIOCondition con,gpointer user_data)350 test_write (GIOChannel   *source,
351             GIOCondition  con,
352             gpointer      user_data)
353 {
354   gchar buf[_STREAM_MAX_CHUNK_SIZE];
355   gsize bytes_read;
356   GError *error;
357   GIOStatus read_status;
358   _PrintStreamData *ps = (_PrintStreamData *) user_data;
359 
360   error = NULL;
361 
362   read_status =
363     g_io_channel_read_chars (source,
364                              buf,
365                              _STREAM_MAX_CHUNK_SIZE,
366                              &bytes_read,
367                              &error);
368 
369   if (read_status != G_IO_STATUS_ERROR)
370     {
371       gsize bytes_written;
372 
373       g_io_channel_write_chars (ps->target_io,
374                                 buf,
375 				bytes_read,
376 				&bytes_written,
377 				&error);
378     }
379 
380   if (error != NULL || read_status == G_IO_STATUS_EOF)
381     {
382       test_print_cb (GTK_PRINT_BACKEND_TEST (ps->backend), error, user_data);
383 
384       if (error != NULL)
385         {
386           GTK_NOTE (PRINTING,
387                     g_print ("TEST Backend: %s\n", error->message));
388 
389           g_error_free (error);
390         }
391 
392       return FALSE;
393     }
394 
395   GTK_NOTE (PRINTING,
396             g_print ("TEST Backend: Writing %i byte chunk to target test\n", (int)bytes_read));
397 
398   return TRUE;
399 }
400 
401 static void
gtk_print_backend_test_print_stream(GtkPrintBackend * print_backend,GtkPrintJob * job,GIOChannel * data_io,GtkPrintJobCompleteFunc callback,gpointer user_data,GDestroyNotify dnotify)402 gtk_print_backend_test_print_stream (GtkPrintBackend        *print_backend,
403 				     GtkPrintJob            *job,
404 				     GIOChannel             *data_io,
405 				     GtkPrintJobCompleteFunc callback,
406 				     gpointer                user_data,
407 				     GDestroyNotify          dnotify)
408 {
409   GError *internal_error = NULL;
410   GtkPrinter *printer;
411   _PrintStreamData *ps;
412   GtkPrintSettings *settings;
413   gchar *uri, *testname;
414 
415   printer = gtk_print_job_get_printer (job);
416   settings = gtk_print_job_get_settings (job);
417 
418   ps = g_new0 (_PrintStreamData, 1);
419   ps->callback = callback;
420   ps->user_data = user_data;
421   ps->dnotify = dnotify;
422   ps->job = g_object_ref (job);
423   ps->backend = print_backend;
424 
425   internal_error = NULL;
426   uri = output_test_from_settings (settings, NULL);
427   testname = g_filename_from_uri (uri, NULL, &internal_error);
428   g_free (uri);
429 
430   if (testname == NULL)
431     goto error;
432 
433   ps->target_io = g_io_channel_new_file (testname, "w", &internal_error);
434 
435   g_free (testname);
436 
437   if (internal_error == NULL)
438     g_io_channel_set_encoding (ps->target_io, NULL, &internal_error);
439 
440 error:
441   if (internal_error != NULL)
442     {
443       test_print_cb (GTK_PRINT_BACKEND_TEST (print_backend),
444                     internal_error, ps);
445 
446       g_error_free (internal_error);
447       return;
448     }
449 
450   g_io_add_watch (data_io,
451                   G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_HUP,
452                   (GIOFunc) test_write,
453                   ps);
454 }
455 
456 static void
gtk_print_backend_test_init(GtkPrintBackendTest * backend)457 gtk_print_backend_test_init (GtkPrintBackendTest *backend)
458 {
459   GtkPrinter *printer;
460   int i;
461 
462   /* make 100 of these printers */
463   for (i = 0; i < 100; i++)
464     {
465       char *name;
466 
467       name = g_strdup_printf ("%s %i", _("Print to Test Printer"), i);
468       printer = g_object_new (GTK_TYPE_PRINTER,
469 			      "name", name,
470 			      "backend", backend,
471 			      "is-virtual", FALSE, /* treat printer like a real one*/
472 			      NULL);
473       g_free (name);
474 
475       g_message ("TEST Backend: Adding printer %d\n", i);
476 
477       gtk_printer_set_has_details (printer, FALSE);
478       gtk_printer_set_icon_name (printer, "gtk-delete"); /* use a delete icon just for fun */
479       gtk_printer_set_is_active (printer, TRUE);
480 
481       gtk_print_backend_add_printer (GTK_PRINT_BACKEND (backend), printer);
482       g_object_unref (printer);
483     }
484 
485   gtk_print_backend_set_list_done (GTK_PRINT_BACKEND (backend));
486 }
487 
488 static GtkPrinterOptionSet *
test_printer_get_options(GtkPrinter * printer,GtkPrintSettings * settings,GtkPageSetup * page_setup,GtkPrintCapabilities capabilities)489 test_printer_get_options (GtkPrinter           *printer,
490 			  GtkPrintSettings     *settings,
491 			  GtkPageSetup         *page_setup,
492 			  GtkPrintCapabilities  capabilities)
493 {
494   GtkPrinterOptionSet *set;
495   GtkPrinterOption *option;
496   const gchar *n_up[] = { "1" };
497   OutputFormat format;
498 
499   format = format_from_settings (settings);
500 
501   set = gtk_printer_option_set_new ();
502 
503   option = gtk_printer_option_new ("gtk-n-up", _("Pages per _sheet:"), GTK_PRINTER_OPTION_TYPE_PICKONE);
504   gtk_printer_option_choices_from_array (option, G_N_ELEMENTS (n_up),
505 					 (char **) n_up, (char **) n_up /* FIXME i18n (localised digits)! */);
506   gtk_printer_option_set (option, "1");
507   gtk_printer_option_set_add (set, option);
508   g_object_unref (option);
509 
510   return set;
511 }
512 
513 static void
test_printer_get_settings_from_options(GtkPrinter * printer,GtkPrinterOptionSet * options,GtkPrintSettings * settings)514 test_printer_get_settings_from_options (GtkPrinter          *printer,
515 					GtkPrinterOptionSet *options,
516 					GtkPrintSettings    *settings)
517 {
518 }
519 
520 static void
test_printer_prepare_for_print(GtkPrinter * printer,GtkPrintJob * print_job,GtkPrintSettings * settings,GtkPageSetup * page_setup)521 test_printer_prepare_for_print (GtkPrinter       *printer,
522 				GtkPrintJob      *print_job,
523 				GtkPrintSettings *settings,
524 				GtkPageSetup     *page_setup)
525 {
526   gdouble scale;
527 
528   print_job->print_pages = gtk_print_settings_get_print_pages (settings);
529   print_job->page_ranges = NULL;
530   print_job->num_page_ranges = 0;
531 
532   if (print_job->print_pages == GTK_PRINT_PAGES_RANGES)
533     print_job->page_ranges =
534       gtk_print_settings_get_page_ranges (settings,
535 					  &print_job->num_page_ranges);
536 
537   print_job->collate = gtk_print_settings_get_collate (settings);
538   print_job->reverse = gtk_print_settings_get_reverse (settings);
539   print_job->num_copies = gtk_print_settings_get_n_copies (settings);
540 
541   scale = gtk_print_settings_get_scale (settings);
542   if (scale != 100.0)
543     print_job->scale = scale/100.0;
544 
545   print_job->page_set = gtk_print_settings_get_page_set (settings);
546   print_job->rotate_to_orientation = TRUE;
547 }
548 
549 static gboolean
test_printer_details_aquired_cb(GtkPrinter * printer)550 test_printer_details_aquired_cb (GtkPrinter *printer)
551 {
552   gboolean success;
553   gint weight;
554 
555   /* weight towards success */
556   weight = g_random_int_range (0, 100);
557 
558   success = FALSE;
559   if (weight < 75)
560     success = TRUE;
561 
562   g_message ("success %i", success);
563   gtk_printer_set_has_details (printer, success);
564   g_signal_emit_by_name (printer, "details-acquired", success);
565 
566   return FALSE;
567 }
568 
569 static void
test_printer_request_details(GtkPrinter * printer)570 test_printer_request_details (GtkPrinter *printer)
571 {
572   gint weight;
573   gint time;
574   /* set the timer to succeed or fail at a random time interval */
575   /* weight towards the shorter end */
576   weight = g_random_int_range (0, 100);
577   if (weight < 50)
578     time = g_random_int_range (0, 2);
579   else if (weight < 75)
580     time = g_random_int_range (1, 5);
581   else
582     time = g_random_int_range (1, 10);
583 
584   g_message ("Gathering details in %i seconds", time);
585 
586   if (time == 0)
587     time = 10;
588   else
589     time *= 1000;
590 
591   g_timeout_add (time, (GSourceFunc) test_printer_details_aquired_cb, printer);
592 }
593 
594 
595