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