1 /*
2  * This file is part of YAD.
3  *
4  * YAD is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * YAD is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with YAD. If not, see <http://www.gnu.org/licenses/>.
16  *
17  * Copyright (C) 2008-2019, Victor Ananjevsky <ananasik@gmail.com>
18  */
19 
20 #include <sys/types.h>
21 #include <unistd.h>
22 
23 #include <gtk/gtkunixprint.h>
24 
25 #include "yad.h"
26 
27 #define HEADER_HEIGHT (10*72/25.4)
28 #define HEADER_GAP (3*72/25.4)
29 #define HEADER_FONT "Sans 11"
30 
31 #define FONTNAME "Monospace"
32 #define FONTSIZE 11.0
33 
34 static gchar **text;
35 static gint nlines, npages;
36 
37 static PangoFontDescription *fdesc = NULL;
38 
39 static void
draw_header(GtkPrintContext * cnt,gint pn,gint pc)40 draw_header (GtkPrintContext * cnt, gint pn, gint pc)
41 {
42   cairo_t *cr;
43   PangoFontDescription *desc;
44   PangoLayout *layout;
45   gint pw, tw, th;
46   gchar *page;
47 
48   cr = gtk_print_context_get_cairo_context (cnt);
49   pw = gtk_print_context_get_width (cnt);
50 
51   layout = gtk_print_context_create_pango_layout (cnt);
52 
53   desc = pango_font_description_from_string (HEADER_FONT);
54   pango_layout_set_font_description (layout, desc);
55   pango_font_description_free (desc);
56 
57   pango_layout_set_text (layout, options.common_data.uri, -1);
58   pango_layout_get_pixel_size (layout, &tw, &th);
59   if (tw > pw)
60     {
61       pango_layout_set_width (layout, pw);
62       pango_layout_set_ellipsize (layout, PANGO_ELLIPSIZE_START);
63       pango_layout_get_pixel_size (layout, &tw, &th);
64     }
65 
66   cairo_move_to (cr, (pw - tw) / 2, (HEADER_HEIGHT - th) / 2);
67   pango_cairo_show_layout (cr, layout);
68 
69   page = g_strdup_printf ("%d/%d", pn, pc);
70   pango_layout_set_text (layout, page, -1);
71   g_free (page);
72 
73   pango_layout_set_width (layout, -1);
74   pango_layout_get_pixel_size (layout, &tw, &th);
75   cairo_move_to (cr, pw - tw - 4, (HEADER_HEIGHT - th) / 2);
76   pango_cairo_show_layout (cr, layout);
77 
78   g_object_unref (layout);
79 
80   cairo_move_to (cr, 0.0, HEADER_HEIGHT);
81   cairo_line_to (cr, pw, HEADER_HEIGHT);
82 
83   cairo_set_source_rgb (cr, 0, 0, 0);
84   cairo_set_line_width (cr, 1);
85   cairo_stroke (cr);
86 }
87 
88 static void
begin_print_text(GtkPrintOperation * op,GtkPrintContext * cnt,gpointer data)89 begin_print_text (GtkPrintOperation * op, GtkPrintContext * cnt, gpointer data)
90 {
91   gchar *buf;
92   gint i = 0;
93   gdouble ph;
94 
95   /* load file */
96   g_file_get_contents (options.common_data.uri, &buf, NULL, NULL);
97   text = g_strsplit (buf, "\n", 0);
98   g_free (buf);
99 
100   while (text[i] != NULL)
101     i++;
102 
103   ph = gtk_print_context_get_height (cnt);
104   if (options.print_data.headers)
105     ph -= HEADER_HEIGHT + HEADER_GAP;
106 
107   nlines = ph / FONTSIZE;
108   npages = i / nlines + 1;
109   gtk_print_operation_set_n_pages (op, npages);
110 
111   /* set font */
112   if (options.common_data.font)
113     fdesc = pango_font_description_from_string (options.common_data.font);
114   else
115     {
116       fdesc = pango_font_description_from_string (FONTNAME);
117       pango_font_description_set_size (fdesc, FONTSIZE * PANGO_SCALE);
118     }
119 }
120 
121 static void
draw_page_text(GtkPrintOperation * op,GtkPrintContext * cnt,gint page,gpointer data)122 draw_page_text (GtkPrintOperation * op, GtkPrintContext * cnt, gint page, gpointer data)
123 {
124   cairo_t *cr;
125   PangoLayout *layout;
126   gint i, line;
127 
128   cr = gtk_print_context_get_cairo_context (cnt);
129 
130   /* create header */
131   if (options.print_data.headers)
132     draw_header (cnt, page + 1, npages);
133 
134   /* add text */
135   layout = gtk_print_context_create_pango_layout (cnt);
136   pango_layout_set_font_description (layout, fdesc);
137 
138   cairo_move_to (cr, 0, HEADER_HEIGHT + HEADER_GAP);
139 
140   line = page * nlines;
141   for (i = 0; i < nlines; i++)
142     {
143       if (text[line + i] == NULL)
144         break;
145       pango_layout_set_text (layout, text[line + i], -1);
146       pango_cairo_show_layout (cr, layout);
147       cairo_rel_move_to (cr, 0, FONTSIZE);
148     }
149 
150   g_object_unref (layout);
151 }
152 
153 static void
draw_page_image(GtkPrintOperation * op,GtkPrintContext * cnt,gint page,gpointer data)154 draw_page_image (GtkPrintOperation * op, GtkPrintContext * cnt, gint page, gpointer data)
155 {
156   cairo_t *cr;
157   GdkPixbuf *pb, *spb;
158   guint iw, ih;
159   gdouble pw, ph;
160   gdouble factor;
161 
162   cr = gtk_print_context_get_cairo_context (cnt);
163 
164   pw = gtk_print_context_get_width (cnt);
165   ph = gtk_print_context_get_height (cnt);
166   if (options.print_data.headers)
167     ph -= HEADER_HEIGHT + HEADER_GAP;
168 
169   /* create header */
170   if (options.print_data.headers)
171     draw_header (cnt, 1, 1);
172 
173   /* scale image to page size */
174   pb = gdk_pixbuf_new_from_file (options.common_data.uri, NULL);
175   iw = gdk_pixbuf_get_width (pb);
176   ih = gdk_pixbuf_get_height (pb);
177 
178   if (pw < iw || ph < ih)
179     {
180       factor = MIN (pw / iw, ph / ih);
181       factor = (factor > 1.0) ? 1.0 : factor;
182       spb = gdk_pixbuf_scale_simple (pb, iw * factor, ih * factor, GDK_INTERP_HYPER);
183     }
184   else
185     spb = g_object_ref (pb);
186   g_object_unref (pb);
187 
188   /* add image to surface */
189   gdk_cairo_set_source_pixbuf (cr, spb, 0.0, HEADER_HEIGHT + HEADER_GAP);
190   cairo_paint (cr);
191   g_object_unref (spb);
192 }
193 
194 static void
raw_print_done(GtkPrintJob * job,gint * ret,GError * err)195 raw_print_done (GtkPrintJob * job, gint * ret, GError * err)
196 {
197   if (err)
198     {
199       g_printerr (_("Printing failed: %s\n"), err->message);
200       *ret = 1;
201     }
202   yad_exit (options.data.def_resp);
203 }
204 
205 static void
size_allocate_cb(GtkWidget * w,GtkAllocation * al,gpointer data)206 size_allocate_cb (GtkWidget * w, GtkAllocation * al, gpointer data)
207 {
208   gtk_widget_set_size_request (w, al->width, -1);
209 }
210 
211 gint
yad_print_run(void)212 yad_print_run (void)
213 {
214   GtkWidget *dlg;
215   GtkWidget *box, *img, *lbl;
216   gchar *uri, *fn, *job_name = NULL;
217   GtkPrintCapabilities pcap;
218   GtkPrintOperationAction act = GTK_PRINT_OPERATION_ACTION_PRINT;
219   GtkPrintSettings *print_settings = NULL;
220   GtkPageSetup *page_setup = NULL;
221   gint resp, ret = 0;
222   GError *err = NULL;
223 
224   /* check if file is exists */
225   if (options.common_data.uri && options.common_data.uri[0])
226     {
227       if (!g_file_test (options.common_data.uri, G_FILE_TEST_EXISTS))
228         {
229           g_printerr (_("File %s not found.\n"), options.common_data.uri);
230           return 1;
231         }
232     }
233   else
234     {
235       g_printerr (_("Filename is not specified.\n"));
236       return 1;
237     }
238 
239   /* load previously saved print settings */
240   fn = g_build_filename (g_get_user_config_dir (), "yad", "print.conf", NULL);
241   if (g_file_test (fn, G_FILE_TEST_EXISTS))
242     {
243       print_settings = gtk_print_settings_new_from_file (fn, NULL);
244       page_setup = gtk_page_setup_new_from_file (fn, NULL);
245     }
246   g_free (fn);
247 
248   if (!print_settings)
249     print_settings = gtk_print_settings_new ();
250   if (!page_setup)
251     page_setup = gtk_page_setup_new ();
252 
253   /* create print dialog */
254   dlg = gtk_print_unix_dialog_new (options.data.dialog_title, NULL);
255   gtk_window_set_type_hint (GTK_WINDOW (dlg), GDK_WINDOW_TYPE_HINT_NORMAL);
256   gtk_print_unix_dialog_set_embed_page_setup (GTK_PRINT_UNIX_DIALOG (dlg), TRUE);
257   pcap = GTK_PRINT_CAPABILITY_PAGE_SET | GTK_PRINT_CAPABILITY_COPIES |
258     GTK_PRINT_CAPABILITY_COLLATE | GTK_PRINT_CAPABILITY_REVERSE |
259     GTK_PRINT_CAPABILITY_NUMBER_UP | GTK_PRINT_CAPABILITY_NUMBER_UP_LAYOUT;
260   if (options.common_data.preview && options.print_data.type != YAD_PRINT_RAW)
261     pcap |= GTK_PRINT_CAPABILITY_PREVIEW;
262   gtk_print_unix_dialog_set_manual_capabilities (GTK_PRINT_UNIX_DIALOG (dlg), pcap);
263 
264   uri = g_build_filename (g_get_current_dir (), "yad.pdf", NULL);
265   gtk_print_settings_set (print_settings, "output-uri", g_filename_to_uri (uri, NULL, NULL));
266   g_free (uri);
267 
268   gtk_print_unix_dialog_set_settings (GTK_PRINT_UNIX_DIALOG (dlg), print_settings);
269   gtk_print_unix_dialog_set_page_setup (GTK_PRINT_UNIX_DIALOG (dlg), page_setup);
270 
271   /* set window behavior */
272   gtk_widget_set_name (dlg, "yad-dialog-window");
273   if (options.data.sticky)
274     gtk_window_stick (GTK_WINDOW (dlg));
275   gtk_window_set_resizable (GTK_WINDOW (dlg), !options.data.fixed);
276   gtk_window_set_keep_above (GTK_WINDOW (dlg), options.data.ontop);
277   gtk_window_set_decorated (GTK_WINDOW (dlg), !options.data.undecorated);
278   gtk_window_set_skip_taskbar_hint (GTK_WINDOW (dlg), options.data.skip_taskbar);
279   gtk_window_set_skip_pager_hint (GTK_WINDOW (dlg), options.data.skip_taskbar);
280 
281   /* set window size and position */
282   gtk_window_set_default_size (GTK_WINDOW (dlg), options.data.width, options.data.height);
283   if (options.data.center)
284     gtk_window_set_position (GTK_WINDOW (dlg), GTK_WIN_POS_CENTER);
285   else if (options.data.mouse)
286     gtk_window_set_position (GTK_WINDOW (dlg), GTK_WIN_POS_MOUSE);
287 
288   /* create yad's top box */
289   if (options.data.dialog_text || options.data.dialog_image)
290     {
291       box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
292 
293       if (options.data.dialog_image)
294         {
295           GdkPixbuf *pb = NULL;
296 
297           pb = get_pixbuf (options.data.dialog_image, YAD_BIG_ICON, FALSE);
298           img = gtk_image_new_from_pixbuf (pb);
299           if (pb)
300             g_object_unref (pb);
301 
302           gtk_widget_set_name (img, "yad-dialog-image");
303           gtk_box_pack_start (GTK_BOX (box), img, FALSE, FALSE, 2);
304         }
305       if (options.data.dialog_text)
306         {
307           gchar *buf = g_strcompress (options.data.dialog_text);
308 
309           lbl = gtk_label_new (NULL);
310           if (!options.data.no_markup)
311             gtk_label_set_markup (GTK_LABEL (lbl), buf);
312           else
313             gtk_label_set_text (GTK_LABEL (lbl), buf);
314           gtk_widget_set_name (lbl, "yad-dialog-label");
315           gtk_label_set_selectable (GTK_LABEL (lbl), options.data.selectable_labels);
316           gtk_label_set_xalign (GTK_LABEL (lbl), options.data.text_align);
317 
318           if (options.data.geometry || options.data.width != -1)
319             gtk_label_set_line_wrap (GTK_LABEL (lbl), TRUE);
320           gtk_box_pack_start (GTK_BOX (box), lbl, TRUE, TRUE, 2);
321           g_signal_connect (G_OBJECT (lbl), "size-allocate", G_CALLBACK (size_allocate_cb), NULL);
322           g_free (buf);
323         }
324 
325       /* add tob box to dialog */
326       gtk_widget_show_all (box);
327       gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dlg))), box, TRUE, TRUE, 5);
328       gtk_box_reorder_child (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dlg))), box, 0);
329     }
330 
331   do
332     {
333       resp = gtk_dialog_run (GTK_DIALOG (dlg));
334       switch (resp)
335         {
336         case GTK_RESPONSE_APPLY:   /* ask for preview */
337           act = GTK_PRINT_OPERATION_ACTION_PREVIEW;
338         case GTK_RESPONSE_OK:      /* run print */
339           print_settings = gtk_print_unix_dialog_get_settings (GTK_PRINT_UNIX_DIALOG (dlg));
340           page_setup = gtk_print_unix_dialog_get_page_setup (GTK_PRINT_UNIX_DIALOG (dlg));
341           job_name = g_strdup_printf ("yad-%s-%d", g_path_get_basename (options.common_data.uri), getpid ());
342           if (options.print_data.type != YAD_PRINT_RAW)
343             {
344               /* print text or image */
345               GtkPrintOperation *op = gtk_print_operation_new ();
346               gtk_print_operation_set_unit (op, GTK_UNIT_POINTS);
347               gtk_print_operation_set_print_settings (op, print_settings);
348               gtk_print_operation_set_default_page_setup (op, page_setup);
349               gtk_print_operation_set_job_name (op, job_name);
350 
351               switch (options.print_data.type)
352                 {
353                 case YAD_PRINT_TEXT:
354                   g_signal_connect (G_OBJECT (op), "begin-print", G_CALLBACK (begin_print_text), NULL);
355                   g_signal_connect (G_OBJECT (op), "draw-page", G_CALLBACK (draw_page_text), NULL);
356                   break;
357                 case YAD_PRINT_IMAGE:
358                   gtk_print_operation_set_n_pages (op, 1);
359                   g_signal_connect (G_OBJECT (op), "draw-page", G_CALLBACK (draw_page_image), NULL);
360                   break;
361                 default:;
362                 }
363 
364               if (gtk_print_operation_run (op, act, NULL, &err) == GTK_PRINT_OPERATION_RESULT_ERROR)
365                 {
366                   g_printerr (_("Printing failed: %s\n"), err->message);
367                   ret = 1;
368                 }
369             }
370           else
371             {
372               /* print raw ps or pdf data */
373               GtkPrinter *prnt;
374               GtkPrintJob *job;
375 
376               prnt = gtk_print_unix_dialog_get_selected_printer (GTK_PRINT_UNIX_DIALOG (dlg));
377 
378               if (g_str_has_suffix (options.common_data.uri, ".ps"))
379                 {
380                   if (!gtk_printer_accepts_ps (prnt))
381                     {
382                       g_printerr (_("Printer doesn't support ps format.\n"));
383                       ret = 1;
384                     }
385                 }
386               else if (g_str_has_suffix (options.common_data.uri, ".pdf"))
387                 {
388                   if (!gtk_printer_accepts_pdf (prnt))
389                     {
390                       g_printerr (_("Printer doesn't support pdf format.\n"));
391                       ret = 1;
392                     }
393                 }
394               else
395                 {
396                   g_printerr (_("This file type is not supported for raw printing.\n"));
397                   ret = 1;
398                 }
399               if (ret == 1)
400                 break;
401 
402               job = gtk_print_job_new (job_name, prnt, print_settings, page_setup);
403               if (gtk_print_job_set_source_file (job, options.common_data.uri, &err))
404                 {
405                   gtk_print_job_send (job, (GtkPrintJobCompleteFunc) raw_print_done, &ret, NULL);
406                   gtk_main ();
407                 }
408               else
409                 {
410                   g_printerr (_("Load source file failed: %s\n"), err->message);
411                   ret = 1;
412                 }
413             }
414           break;
415         default:
416           ret = 1;
417           break;
418         }
419     }
420   while (resp == GTK_RESPONSE_APPLY);
421 
422   gtk_widget_destroy (dlg);
423 
424   /* save print settings */
425   fn = g_build_filename (g_get_user_config_dir (), "yad", NULL);
426   g_mkdir_with_parents (fn, 0700);
427   g_free (fn);
428 
429   fn = g_build_filename (g_get_user_config_dir (), "yad", "print.conf", NULL);
430   gtk_print_settings_to_file (print_settings, fn, NULL);
431   gtk_page_setup_to_file (page_setup, fn, NULL);
432   g_free (fn);
433 
434   return ret;
435 }
436