1 /* Dia -- an diagram creation/manipulation program
2  * Copyright (C) 1998, 1999 Alexander Larsson
3  *
4  * paginate_psprint.[ch] -- pagination code for the postscript backend
5  * Copyright (C) 1999 James Henstridge
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20  */
21 
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25 
26 /* so we get popen and sigaction even when compiling with -ansi */
27 #define _POSIX_C_SOURCE 2
28 #include <stdio.h>
29 #include <string.h> /* strlen */
30 #include <signal.h>
31 #include <errno.h>
32 #include <sys/stat.h>
33 
34 #include <glib/gstdio.h>
35 
36 #include "intl.h"
37 #include "message.h"
38 #include "diagramdata.h"
39 #include "render_eps.h"
40 #include "diapsrenderer.h"
41 #include "paginate_psprint.h"
42 #include "persistence.h"
43 
44 #include <gtk/gtk.h>
45 
46 #ifdef G_OS_WIN32
47 #include <io.h>
48 #include "win32print.h"
49 #define pclose(file) win32_printer_close (file)
50 #endif
51 
52 /* keep track of print options between prints */
53 typedef struct _dia_print_options {
54   int printer;
55 } dia_print_options;
56 
57 static dia_print_options last_print_options =
58 {
59     1
60 };
61 
62 static void
count_objs(DiaObject * obj,DiaRenderer * renderer,int active_layer,guint * nobjs)63 count_objs(DiaObject *obj, DiaRenderer *renderer, int active_layer, guint *nobjs)
64 {
65   (*nobjs)++;
66 }
67 
68 static guint
print_page(DiagramData * data,DiaRenderer * diarend,Rectangle * bounds)69 print_page(DiagramData *data, DiaRenderer *diarend, Rectangle *bounds)
70 {
71   DiaPsRenderer *rend = DIA_PS_RENDERER(diarend);
72   guint nobjs = 0;
73   gfloat tmargin = data->paper.tmargin, bmargin = data->paper.bmargin;
74   gfloat lmargin = data->paper.lmargin;
75   gfloat scale = data->paper.scaling;
76   gchar d1_buf[G_ASCII_DTOSTR_BUF_SIZE];
77   gchar d2_buf[G_ASCII_DTOSTR_BUF_SIZE];
78 
79   rend->paper = data->paper.name;
80   rend->is_portrait = data->paper.is_portrait;
81 
82   /* count the number of objects in this region */
83   data_render(data, diarend, bounds,
84               (ObjectRenderer) count_objs, &nobjs);
85 
86   if (nobjs == 0)
87     return nobjs;
88 
89   /* output a page number comment */
90   fprintf(rend->file, "%%%%Page: %d %d\n", rend->pagenum, rend->pagenum);
91   rend->pagenum++;
92 
93   /* save print context */
94   fprintf(rend->file, "gs\n");
95 
96   /* transform coordinate system */
97   if (data->paper.is_portrait) {
98     fprintf(rend->file, "%s %s scale\n",
99 	    g_ascii_formatd(d1_buf, sizeof(d1_buf), "%f", 28.346457*scale),
100 	    g_ascii_formatd(d2_buf, sizeof(d2_buf), "%f", -28.346457*scale) );
101     fprintf(rend->file, "%s %s translate\n",
102 	    g_ascii_formatd(d1_buf, sizeof(d1_buf), "%f", lmargin/scale - bounds->left),
103 	    g_ascii_formatd(d2_buf, sizeof(d2_buf), "%f", -bmargin/scale - bounds->bottom) );
104   } else {
105     fprintf(rend->file, "90 rotate\n");
106     fprintf(rend->file, "%s %s scale\n",
107 	    g_ascii_formatd(d1_buf, sizeof(d1_buf), "%f", 28.346457*scale),
108 	    g_ascii_formatd(d2_buf, sizeof(d2_buf), "%f", -28.346457*scale) );
109     fprintf(rend->file, "%s %s translate\n",
110 	    g_ascii_formatd(d1_buf, sizeof(d1_buf), "%f", lmargin/scale - bounds->left),
111 	    g_ascii_formatd(d2_buf, sizeof(d2_buf), "%f", tmargin/scale - bounds->top) );
112   }
113 
114   /* set up clip mask */
115   fprintf(rend->file, "n %s %s m ",
116 	  g_ascii_formatd(d1_buf, sizeof(d1_buf), "%f", bounds->left),
117 	  g_ascii_formatd(d2_buf, sizeof(d2_buf), "%f", bounds->top) );
118   fprintf(rend->file, "%s %s l ",
119 	  g_ascii_formatd(d1_buf, sizeof(d1_buf), "%f", bounds->right),
120 	  g_ascii_formatd(d2_buf, sizeof(d2_buf), "%f", bounds->top) );
121   fprintf(rend->file, "%s %s l ",
122 	  g_ascii_formatd(d1_buf, sizeof(d1_buf), "%f", bounds->right),
123 	  g_ascii_formatd(d2_buf, sizeof(d2_buf), "%f", bounds->bottom) );
124   fprintf(rend->file, "%s %s l ",
125 	  g_ascii_formatd(d1_buf, sizeof(d1_buf), "%f", bounds->left),
126 	  g_ascii_formatd(d2_buf, sizeof(d2_buf), "%f", bounds->bottom) );
127   fprintf(rend->file, "%s %s l ",
128 	  g_ascii_formatd(d1_buf, sizeof(d1_buf), "%f", bounds->left),
129 	  g_ascii_formatd(d2_buf, sizeof(d2_buf), "%f", bounds->top) );
130   /* Tip from Dov Grobgeld: Clip does not destroy the path, so we should
131      do a newpath afterwards */
132   fprintf(rend->file, "clip n\n");
133 
134   /* render the region */
135   data_render(data, diarend, bounds, NULL, NULL);
136 
137   /* restore print context */
138   fprintf(rend->file, "gr\n");
139 
140   /* print the page */
141   fprintf(rend->file, "showpage\n\n");
142 
143   return nobjs;
144 }
145 
146 void
paginate_psprint(DiagramData * dia,FILE * file)147 paginate_psprint(DiagramData *dia, FILE *file)
148 {
149   DiaRenderer *rend;
150   Rectangle *extents;
151   gfloat width, height;
152   gfloat x, y, initx, inity;
153   guint nobjs = 0;
154 
155   rend = new_psprint_renderer(dia, file);
156 
157 #ifdef DIA_PS_RENDERER_DUAL_PASS
158   /* Prepare the prolog (with fonts etc) */
159   data_render(dia, DIA_RENDERER(rend), NULL, NULL, NULL);
160   eps_renderer_prolog_done(rend);
161 #endif
162 
163   /* the usable area of the page */
164   width = dia->paper.width;
165   height = dia->paper.height;
166 
167   /* get extents, and make them multiples of width / height */
168   extents = &dia->extents;
169   initx = extents->left;
170   inity = extents->top;
171   /* make page boundaries align with origin */
172   if (!dia->paper.fitto) {
173     initx = floor(initx / width)  * width;
174     inity = floor(inity / height) * height;
175   }
176 
177   /* iterate through all the pages in the diagram */
178   for (y = inity; y < extents->bottom; y += height) {
179     /* ensure we are not producing pages for epsilon */
180     if ((extents->bottom - y) < 1e-6)
181       break;
182     for (x = initx; x < extents->right; x += width) {
183       Rectangle page_bounds;
184 
185       if ((extents->right - x) < 1e-6)
186 	break;
187 
188       page_bounds.left = x;
189       page_bounds.right = x + width;
190       page_bounds.top = y;
191       page_bounds.bottom = y + height;
192 
193       nobjs += print_page(dia,rend, &page_bounds);
194     }
195   }
196 
197   g_object_unref(rend);
198 
199 }
200 
201 static void
change_entry_state(GtkToggleButton * radio,GtkWidget * entry)202 change_entry_state(GtkToggleButton *radio, GtkWidget *entry)
203 {
204   gtk_widget_set_sensitive(entry, gtk_toggle_button_get_active(radio));
205 }
206 
207 static void
ok_pressed(GtkButton * button,gboolean * flag)208 ok_pressed(GtkButton *button, gboolean *flag)
209 {
210   *flag = TRUE;
211   gtk_main_quit();
212 }
213 
214 static gboolean sigpipe_received = FALSE;
215 static void
pipe_handler(int signum)216 pipe_handler(int signum)
217 {
218   sigpipe_received = TRUE;
219 }
220 
221 static gboolean
diagram_print_destroy(GtkWidget * widget)222 diagram_print_destroy(GtkWidget *widget)
223 {
224   DiagramData *dia;
225 
226   if ((dia = g_object_get_data(G_OBJECT(widget), "diagram")) != NULL) {
227     g_object_unref(dia);
228     g_object_set_data(G_OBJECT(widget), "diagram", NULL);
229   }
230 
231   return FALSE;
232 }
233 
234 void
diagram_print_ps(DiagramData * dia,const gchar * original_filename)235 diagram_print_ps(DiagramData *dia, const gchar* original_filename)
236 {
237   GtkWidget *dialog;
238   GtkWidget *vbox, *frame, *table, *box, *button;
239   GtkWidget *iscmd, *isofile;
240   GtkWidget *cmd, *ofile;
241   gboolean cont = FALSE;
242   gchar *printcmd = NULL;
243   gchar *orig_command, *orig_file;
244   gchar *filename = NULL;
245   gchar *printer_filename = NULL;
246   gchar *dot = NULL;
247   gboolean done = FALSE;
248   gboolean write_file = TRUE;	/* Used in prompt to overwrite existing file */
249 
250   FILE *file;
251   gboolean is_pipe;
252 #ifndef G_OS_WIN32
253   /* all the signal stuff below doesn't compile on win32, but it isn't
254    * needed anymore because the pipe handling - which never worked on win32
255    * anyway - is replace by "native" postscript printing now ...
256    */
257   struct sigaction old_sigpipe_action, sigpipe_action;
258 #endif
259 
260   /* create the dialog */
261   dialog = gtk_dialog_new();
262   /* the dialog has it's own reference to the diagram */
263   g_object_ref(dia);
264   g_object_set_data(G_OBJECT(dialog), "diagram", dia);
265   g_signal_connect(GTK_OBJECT(dialog), "destroy",
266 		   G_CALLBACK(diagram_print_destroy), NULL);
267   g_signal_connect(GTK_OBJECT(dialog), "delete_event",
268 		   G_CALLBACK(gtk_main_quit), NULL);
269   g_signal_connect(GTK_OBJECT(dialog), "delete_event",
270 		   G_CALLBACK(gtk_true), NULL);
271   vbox = GTK_DIALOG(dialog)->vbox;
272 
273   frame = gtk_frame_new(_("Select Printer"));
274   gtk_container_set_border_width(GTK_CONTAINER(frame), 5);
275   gtk_box_pack_start(GTK_BOX(vbox), frame, TRUE, TRUE, 0);
276   gtk_widget_show(frame);
277 
278   table = gtk_table_new(2, 2, FALSE);
279   gtk_container_set_border_width(GTK_CONTAINER(table), 5);
280   gtk_table_set_row_spacings(GTK_TABLE(table), 5);
281   gtk_table_set_col_spacings(GTK_TABLE(table), 5);
282   gtk_container_add(GTK_CONTAINER(frame), table);
283   gtk_widget_show(table);
284 
285   iscmd = gtk_radio_button_new_with_label(NULL, _("Printer"));
286   gtk_table_attach(GTK_TABLE(table), iscmd, 0,1, 0,1,
287 		   GTK_FILL, GTK_FILL|GTK_EXPAND, 0, 0);
288   gtk_widget_show(iscmd);
289 
290   cmd = gtk_entry_new();
291   gtk_table_attach(GTK_TABLE(table), cmd, 1,2, 0,1,
292 		   GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 0, 0);
293   gtk_widget_show(cmd);
294 
295   g_signal_connect(GTK_OBJECT(iscmd), "toggled",
296 		   G_CALLBACK(change_entry_state), cmd);
297 
298   isofile = gtk_radio_button_new_with_label(GTK_RADIO_BUTTON(iscmd)->group,
299 					    _("File"));
300   gtk_table_attach(GTK_TABLE(table), isofile, 0,1, 1,2,
301 		   GTK_FILL, GTK_FILL|GTK_EXPAND, 0, 0);
302   gtk_widget_show(isofile);
303 
304   ofile = gtk_entry_new();
305   gtk_widget_set_sensitive(ofile, FALSE);
306   gtk_table_attach(GTK_TABLE(table), ofile, 1,2, 1,2,
307 		   GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 0, 0);
308   gtk_widget_show(ofile);
309   g_signal_connect(GTK_OBJECT(isofile), "toggled",
310 		   G_CALLBACK(change_entry_state), ofile);
311 
312   box = GTK_DIALOG(dialog)->action_area;
313 
314   button = gtk_button_new_with_label(_("OK"));
315   g_signal_connect(GTK_OBJECT(button), "clicked",
316 		   G_CALLBACK(ok_pressed), &cont);
317   GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
318   gtk_box_pack_start(GTK_BOX(box), button, TRUE, TRUE, 0);
319   gtk_widget_grab_default(button);
320   gtk_widget_show(button);
321 
322   button = gtk_button_new_with_label(_("Cancel"));
323   g_signal_connect(GTK_OBJECT(button), "clicked",
324 		   G_CALLBACK(gtk_main_quit), NULL);
325   GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
326   gtk_box_pack_start(GTK_BOX(box), button, TRUE, TRUE, 0);
327   gtk_widget_show(button);
328 
329   /* Set default or old dialog values: */
330 #ifdef G_OS_WIN32
331   gtk_entry_set_text(GTK_ENTRY(cmd), win32_printer_default ());
332 #else
333   {
334     const gchar *printer = g_getenv("PRINTER");
335 
336     if (printer) {
337       printcmd = g_strdup_printf("lpr -P%s", printer);
338     } else {
339       printcmd = g_strdup("lpr");
340     }
341 
342     gtk_entry_set_text(GTK_ENTRY(cmd), printcmd);
343     g_free(printcmd);
344     printcmd = NULL;
345   }
346 #endif
347   persistence_register_string_entry("printer-command", cmd);
348   printcmd = g_strdup(gtk_entry_get_text(GTK_ENTRY(cmd)));
349   orig_command = printcmd;
350 
351   /* Work out diagram filename and use this as default .ps file */
352   filename = g_path_get_basename(original_filename);
353 
354   printer_filename = g_malloc(strlen(filename) + 4);
355   printer_filename = strcpy(printer_filename, filename);
356   dot = strrchr(printer_filename, '.');
357   if ((dot != NULL) && (strcmp(dot, ".dia") == 0))
358     *dot = '\0';
359   printer_filename = strcat(printer_filename, ".ps");
360   gtk_entry_set_text(GTK_ENTRY(ofile), printer_filename);
361   g_free(printer_filename);
362   orig_file = g_strdup(gtk_entry_get_text(GTK_ENTRY(ofile)));
363 
364   /* Scaling is already set at creation. */
365   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(iscmd), last_print_options.printer);
366   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(isofile), !last_print_options.printer);
367 
368   gtk_widget_show(dialog);
369 
370   do {
371     cont = FALSE;
372     write_file = TRUE;
373     gtk_main();
374 
375     if(!dia) {
376       gtk_widget_destroy(dialog);
377       return;
378     }
379 
380     if (!cont) {
381       persistence_change_string_entry("printer-command", orig_command, cmd);
382       gtk_widget_destroy(dialog);
383       g_free(orig_command);
384       g_free(orig_file);
385       return;
386     }
387 
388     if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(iscmd))) {
389       printcmd = g_strdup(gtk_entry_get_text(GTK_ENTRY(cmd)));
390 #ifdef G_OS_WIN32
391       file = win32_printer_open (printcmd);
392 #else
393       file = popen(printcmd, "w");
394 #endif
395       is_pipe = TRUE;
396     } else {
397       const gchar *filename = gtk_entry_get_text(GTK_ENTRY(ofile));
398       struct stat statbuf;
399 
400       if (g_stat(filename, &statbuf) == 0) {	/* Output file exists */
401         GtkWidget *confirm_overwrite_dialog = NULL;
402         char *utf8filename = NULL;
403 
404         if (!g_utf8_validate(filename, -1, NULL)) {
405           utf8filename = g_filename_to_utf8(filename, -1, NULL, NULL, NULL);
406 
407           if (utf8filename == NULL) {
408             message_warning(_("Some characters in the filename are neither "
409 			    "UTF-8\nnor your local encoding.\nSome things will break."));
410           }
411         }
412 
413         if (utf8filename == NULL)
414 	  utf8filename = g_strdup(filename);
415         confirm_overwrite_dialog = gtk_message_dialog_new(GTK_WINDOW (dialog),
416                        GTK_DIALOG_MODAL, GTK_MESSAGE_QUESTION,
417                        GTK_BUTTONS_YES_NO,
418                        _("The file '%s' already exists.\n"
419                          "Do you want to overwrite it?"), utf8filename);
420         g_free(utf8filename);
421         gtk_window_set_title(GTK_WINDOW (confirm_overwrite_dialog),
422 	                   _("File already exists"));
423         gtk_dialog_set_default_response (GTK_DIALOG (confirm_overwrite_dialog),
424 	                   GTK_RESPONSE_NO);
425 
426         if (gtk_dialog_run(GTK_DIALOG(confirm_overwrite_dialog))
427 	                   != GTK_RESPONSE_YES) {
428           write_file = FALSE;
429 		  file = 0;
430         }
431 
432         gtk_widget_destroy(confirm_overwrite_dialog);
433       }
434 
435       if (write_file) {
436         if (!g_path_is_absolute(filename)) {
437           char *full_filename;
438 
439           full_filename = g_build_filename(g_get_home_dir(), filename, NULL);
440           file = g_fopen(full_filename, "w");
441           g_free(full_filename);
442         } else {
443           file = g_fopen(filename, "w");
444         }
445       }
446 
447       is_pipe = FALSE;
448     }
449 
450     /* Store dialog values */
451     last_print_options.printer = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(iscmd));
452 
453     if (write_file) {
454       if (!file) {
455         if (is_pipe) {
456           message_warning(_("Could not run command '%s': %s"), printcmd, strerror(errno));
457           g_free(printcmd);
458         } else
459           message_warning(_("Could not open '%s' for writing: %s"),
460 		      gtk_entry_get_text(GTK_ENTRY(ofile)), strerror(errno));
461       } else
462         done = TRUE;
463     }
464   } while(!done);
465 
466   g_free(orig_command);
467   g_free(orig_file);
468 #ifndef G_OS_WIN32
469   /* set up a SIGPIPE handler to catch IO errors, rather than segfaulting */
470   sigpipe_received = FALSE;
471   memset(&sigpipe_action, 0, sizeof(struct sigaction));
472   sigpipe_action.sa_handler = pipe_handler;
473   sigaction(SIGPIPE, &sigpipe_action, &old_sigpipe_action);
474 #endif
475 
476   paginate_psprint(dia, file);
477   gtk_widget_destroy(dialog);
478   if (is_pipe) {
479     int exitval = pclose(file);
480     if (exitval != 0) {
481       message_error(_("Printing error: command '%s' returned %d\n"),
482 		    printcmd, exitval);
483     }
484   } else
485     fclose(file);
486 
487 #ifndef G_OS_WIN32
488   /* restore original behaviour */
489   sigaction(SIGPIPE, &old_sigpipe_action, NULL);
490 #endif
491   if (sigpipe_received)
492     message_error(_("Printing error: command '%s' caused sigpipe."),
493 		  printcmd);
494 
495   if (is_pipe) g_free(printcmd);
496 }
497