2006-12-05 David Lodge <dave@cirt.net>
[dia.git] / app / paginate_psprint.c
blobbc238fe82ec4332b6cc1d8573cf1751666843970
1 /* Dia -- an diagram creation/manipulation program
2 * Copyright (C) 1998, 1999 Alexander Larsson
4 * paginate_psprint.[ch] -- pagination code for the postscript backend
5 * Copyright (C) 1999 James Henstridge
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.
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.
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.
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
26 #include <stdio.h>
27 #include <string.h> /* strlen */
28 #include <signal.h>
29 #include <errno.h>
30 #include <sys/stat.h>
32 #include "intl.h"
33 #include "message.h"
34 #include "diagram.h"
35 #include "diagramdata.h"
36 #include "render_eps.h"
37 #include "diapsrenderer.h"
38 #include "paginate_psprint.h"
39 #include "diapagelayout.h"
40 #include "persistence.h"
42 #include <gtk/gtk.h>
44 #ifdef G_OS_WIN32
45 #include <io.h>
46 #include "win32print.h"
47 #define pclose(file) win32_printer_close (file)
48 #endif
50 /* keep track of print options between prints */
51 typedef struct _dia_print_options {
52 int printer;
53 } dia_print_options;
55 static dia_print_options last_print_options =
60 static void
61 count_objs(DiaObject *obj, DiaRenderer *renderer, int active_layer, guint *nobjs)
63 (*nobjs)++;
66 static guint
67 print_page(DiagramData *data, DiaRenderer *diarend, Rectangle *bounds)
69 DiaPsRenderer *rend = DIA_PS_RENDERER(diarend);
70 guint nobjs = 0;
71 gfloat tmargin = data->paper.tmargin, bmargin = data->paper.bmargin;
72 gfloat lmargin = data->paper.lmargin;
73 gfloat scale = data->paper.scaling;
74 gchar d1_buf[G_ASCII_DTOSTR_BUF_SIZE];
75 gchar d2_buf[G_ASCII_DTOSTR_BUF_SIZE];
77 rend->paper = data->paper.name;
78 rend->is_portrait = data->paper.is_portrait;
80 /* count the number of objects in this region */
81 data_render(data, diarend, bounds,
82 (ObjectRenderer) count_objs, &nobjs);
84 if (nobjs == 0)
85 return nobjs;
87 /* output a page number comment */
88 fprintf(rend->file, "%%%%Page: %d %d\n", rend->pagenum, rend->pagenum);
89 rend->pagenum++;
91 /* save print context */
92 fprintf(rend->file, "gs\n");
94 /* transform coordinate system */
95 if (data->paper.is_portrait) {
96 fprintf(rend->file, "%s %s scale\n",
97 g_ascii_formatd(d1_buf, sizeof(d1_buf), "%f", 28.346457*scale),
98 g_ascii_formatd(d2_buf, sizeof(d2_buf), "%f", -28.346457*scale) );
99 fprintf(rend->file, "%s %s translate\n",
100 g_ascii_formatd(d1_buf, sizeof(d1_buf), "%f", lmargin/scale - bounds->left),
101 g_ascii_formatd(d2_buf, sizeof(d2_buf), "%f", -bmargin/scale - bounds->bottom) );
102 } else {
103 fprintf(rend->file, "90 rotate\n");
104 fprintf(rend->file, "%s %s scale\n",
105 g_ascii_formatd(d1_buf, sizeof(d1_buf), "%f", 28.346457*scale),
106 g_ascii_formatd(d2_buf, sizeof(d2_buf), "%f", -28.346457*scale) );
107 fprintf(rend->file, "%s %s translate\n",
108 g_ascii_formatd(d1_buf, sizeof(d1_buf), "%f", lmargin/scale - bounds->left),
109 g_ascii_formatd(d2_buf, sizeof(d2_buf), "%f", tmargin/scale - bounds->top) );
112 /* set up clip mask */
113 fprintf(rend->file, "n %s %s m ",
114 g_ascii_formatd(d1_buf, sizeof(d1_buf), "%f", bounds->left),
115 g_ascii_formatd(d2_buf, sizeof(d2_buf), "%f", bounds->top) );
116 fprintf(rend->file, "%s %s l ",
117 g_ascii_formatd(d1_buf, sizeof(d1_buf), "%f", bounds->right),
118 g_ascii_formatd(d2_buf, sizeof(d2_buf), "%f", bounds->top) );
119 fprintf(rend->file, "%s %s l ",
120 g_ascii_formatd(d1_buf, sizeof(d1_buf), "%f", bounds->right),
121 g_ascii_formatd(d2_buf, sizeof(d2_buf), "%f", bounds->bottom) );
122 fprintf(rend->file, "%s %s l ",
123 g_ascii_formatd(d1_buf, sizeof(d1_buf), "%f", bounds->left),
124 g_ascii_formatd(d2_buf, sizeof(d2_buf), "%f", bounds->bottom) );
125 fprintf(rend->file, "%s %s l ",
126 g_ascii_formatd(d1_buf, sizeof(d1_buf), "%f", bounds->left),
127 g_ascii_formatd(d2_buf, sizeof(d2_buf), "%f", bounds->top) );
128 /* Tip from Dov Grobgeld: Clip does not destroy the path, so we should
129 do a newpath afterwards */
130 fprintf(rend->file, "clip n\n");
132 /* render the region */
133 data_render(data, diarend, bounds, NULL, NULL);
135 /* restore print context */
136 fprintf(rend->file, "gr\n");
138 /* print the page */
139 fprintf(rend->file, "showpage\n\n");
141 return nobjs;
144 void
145 paginate_psprint(Diagram *dia, FILE *file)
147 DiaRenderer *rend;
148 Rectangle *extents;
149 gfloat width, height;
150 gfloat x, y, initx, inity;
151 guint nobjs = 0;
153 rend = new_psprint_renderer(dia, file);
155 #ifdef DIA_PS_RENDERER_DUAL_PASS
156 /* Prepare the prolog (with fonts etc) */
157 data_render(dia->data, DIA_RENDERER(rend), NULL, NULL, NULL);
158 eps_renderer_prolog_done(rend);
159 #endif
161 /* the usable area of the page */
162 width = dia->data->paper.width;
163 height = dia->data->paper.height;
165 /* get extents, and make them multiples of width / height */
166 extents = &dia->data->extents;
167 initx = extents->left;
168 inity = extents->top;
169 /* make page boundaries align with origin */
170 if (!dia->data->paper.fitto) {
171 initx = floor(initx / width) * width;
172 inity = floor(inity / height) * height;
175 /* iterate through all the pages in the diagram */
176 for (y = inity; y < extents->bottom; y += height) {
177 /* ensure we are not producing pages for epsilon */
178 if ((extents->bottom - y) < 1e-6)
179 break;
180 for (x = initx; x < extents->right; x += width) {
181 Rectangle page_bounds;
183 if ((extents->right - x) < 1e-6)
184 break;
186 page_bounds.left = x;
187 page_bounds.right = x + width;
188 page_bounds.top = y;
189 page_bounds.bottom = y + height;
191 nobjs += print_page(dia->data,rend, &page_bounds);
195 g_object_unref(rend);
199 static void
200 change_entry_state(GtkToggleButton *radio, GtkWidget *entry)
202 gtk_widget_set_sensitive(entry, gtk_toggle_button_get_active(radio));
205 static void
206 ok_pressed(GtkButton *button, gboolean *flag)
208 *flag = TRUE;
209 gtk_main_quit();
212 static gboolean sigpipe_received = FALSE;
213 static void
214 pipe_handler(int signum)
216 sigpipe_received = TRUE;
219 static gboolean
220 diagram_print_destroy(GtkWidget *widget)
222 Diagram *dia;
224 if ((dia = gtk_object_get_user_data(GTK_OBJECT(widget))) != NULL) {
225 g_object_unref(dia);
226 gtk_object_set_user_data(GTK_OBJECT(widget), NULL);
229 return FALSE;
232 void
233 diagram_print_ps(Diagram *dia)
235 GtkWidget *dialog;
236 GtkWidget *vbox, *frame, *table, *box, *button;
237 GtkWidget *iscmd, *isofile;
238 GtkWidget *cmd, *ofile;
239 gboolean cont = FALSE;
240 gchar *printcmd = NULL;
241 gchar *orig_command, *orig_file;
242 gchar *filename = NULL;
243 gchar *printer_filename = NULL;
244 gchar *dot = NULL;
245 gboolean done = FALSE;
246 gboolean write_file = TRUE; /* Used in prompt to overwrite existing file */
248 FILE *file;
249 gboolean is_pipe;
250 #ifndef G_OS_WIN32
251 /* all the signal stuff below doesn't compile on win32, but it isn't
252 * needed anymore because the pipe handling - which never worked on win32
253 * anyway - is replace by "native" postscript printing now ...
255 void *old_action;
256 #endif
258 /* create the dialog */
259 dialog = gtk_dialog_new();
260 /* the dialog has it's own reference to the diagram */
261 g_object_ref(dia);
262 gtk_object_set_user_data(GTK_OBJECT(dialog), dia);
263 g_signal_connect(GTK_OBJECT(dialog), "destroy",
264 G_CALLBACK(diagram_print_destroy), NULL);
265 g_signal_connect(GTK_OBJECT(dialog), "delete_event",
266 G_CALLBACK(gtk_main_quit), NULL);
267 g_signal_connect(GTK_OBJECT(dialog), "delete_event",
268 G_CALLBACK(gtk_true), NULL);
269 vbox = GTK_DIALOG(dialog)->vbox;
271 frame = gtk_frame_new(_("Select Printer"));
272 gtk_container_set_border_width(GTK_CONTAINER(frame), 5);
273 gtk_box_pack_start(GTK_BOX(vbox), frame, TRUE, TRUE, 0);
274 gtk_widget_show(frame);
276 table = gtk_table_new(2, 2, FALSE);
277 gtk_container_set_border_width(GTK_CONTAINER(table), 5);
278 gtk_table_set_row_spacings(GTK_TABLE(table), 5);
279 gtk_table_set_col_spacings(GTK_TABLE(table), 5);
280 gtk_container_add(GTK_CONTAINER(frame), table);
281 gtk_widget_show(table);
283 iscmd = gtk_radio_button_new_with_label(NULL, _("Printer"));
284 gtk_table_attach(GTK_TABLE(table), iscmd, 0,1, 0,1,
285 GTK_FILL, GTK_FILL|GTK_EXPAND, 0, 0);
286 gtk_widget_show(iscmd);
288 cmd = gtk_entry_new();
289 gtk_table_attach(GTK_TABLE(table), cmd, 1,2, 0,1,
290 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 0, 0);
291 gtk_widget_show(cmd);
293 g_signal_connect(GTK_OBJECT(iscmd), "toggled",
294 G_CALLBACK(change_entry_state), cmd);
296 isofile = gtk_radio_button_new_with_label(GTK_RADIO_BUTTON(iscmd)->group,
297 _("File"));
298 gtk_table_attach(GTK_TABLE(table), isofile, 0,1, 1,2,
299 GTK_FILL, GTK_FILL|GTK_EXPAND, 0, 0);
300 gtk_widget_show(isofile);
302 ofile = gtk_entry_new();
303 gtk_widget_set_sensitive(ofile, FALSE);
304 gtk_table_attach(GTK_TABLE(table), ofile, 1,2, 1,2,
305 GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 0, 0);
306 gtk_widget_show(ofile);
307 g_signal_connect(GTK_OBJECT(isofile), "toggled",
308 G_CALLBACK(change_entry_state), ofile);
310 box = GTK_DIALOG(dialog)->action_area;
312 button = gtk_button_new_with_label(_("OK"));
313 g_signal_connect(GTK_OBJECT(button), "clicked",
314 G_CALLBACK(ok_pressed), &cont);
315 GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
316 gtk_box_pack_start(GTK_BOX(box), button, TRUE, TRUE, 0);
317 gtk_widget_grab_default(button);
318 gtk_widget_show(button);
320 button = gtk_button_new_with_label(_("Cancel"));
321 g_signal_connect(GTK_OBJECT(button), "clicked",
322 G_CALLBACK(gtk_main_quit), NULL);
323 GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
324 gtk_box_pack_start(GTK_BOX(box), button, TRUE, TRUE, 0);
325 gtk_widget_show(button);
327 /* Set default or old dialog values: */
328 #ifdef G_OS_WIN32
329 gtk_entry_set_text(GTK_ENTRY(cmd), win32_printer_default ());
330 #else
332 const gchar *printer = g_getenv("PRINTER");
334 if (printer) {
335 printcmd = g_strdup_printf("lpr -P%s", printer);
336 } else {
337 printcmd = g_strdup("lpr");
340 gtk_entry_set_text(GTK_ENTRY(cmd), printcmd);
341 g_free(printcmd);
342 printcmd = NULL;
344 #endif
345 persistence_register_string_entry("printer-command", cmd);
346 printcmd = g_strdup(gtk_entry_get_text(GTK_ENTRY(cmd)));
347 orig_command = printcmd;
349 /* Work out diagram filename and use this as default .ps file */
350 filename = g_path_get_basename(dia->filename);
351 printer_filename = g_malloc(strlen(filename) + 4);
352 printer_filename = strcpy(printer_filename, filename);
353 dot = strrchr(printer_filename, '.');
354 if ((dot != NULL) && (strcmp(dot, ".dia") == 0))
355 *dot = '\0';
356 printer_filename = strcat(printer_filename, ".ps");
357 gtk_entry_set_text(GTK_ENTRY(ofile), printer_filename);
358 g_free(printer_filename);
359 orig_file = g_strdup(gtk_entry_get_text(GTK_ENTRY(ofile)));
361 /* Scaling is already set at creation. */
362 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(iscmd), last_print_options.printer);
363 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(isofile), !last_print_options.printer);
365 gtk_widget_show(dialog);
367 do {
368 cont = FALSE;
369 write_file = TRUE;
370 gtk_main();
372 if(!dia) {
373 gtk_widget_destroy(dialog);
374 return;
377 if (!cont) {
378 persistence_change_string_entry("printer-command", orig_command, cmd);
379 gtk_widget_destroy(dialog);
380 g_free(orig_command);
381 g_free(orig_file);
382 return;
385 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(iscmd))) {
386 printcmd = g_strdup(gtk_entry_get_text(GTK_ENTRY(cmd)));
387 #ifdef G_OS_WIN32
388 file = win32_printer_open (printcmd);
389 #else
390 file = popen(printcmd, "w");
391 #endif
392 is_pipe = TRUE;
393 } else {
394 const gchar *filename = gtk_entry_get_text(GTK_ENTRY(ofile));
395 struct stat statbuf;
397 if (stat(filename, &statbuf) == 0) { /* Output file exists */
398 GtkWidget *confirm_overwrite_dialog = NULL;
399 char buffer[300];
400 char *utf8filename = NULL;
402 if (!g_utf8_validate(filename, -1, NULL)) {
403 utf8filename = g_filename_to_utf8(filename, -1, NULL, NULL, NULL);
405 if (utf8filename == NULL) {
406 message_warning(_("Some characters in the filename are neither "
407 "UTF-8\nnor your local encoding.\nSome things will break."));
411 if (utf8filename == NULL) utf8filename = g_strdup(filename);
412 g_snprintf(buffer, 300,
413 _("The file '%s' already exists.\n"
414 "Do you want to overwrite it?"), utf8filename);
415 g_free(utf8filename);
416 confirm_overwrite_dialog = gtk_message_dialog_new(GTK_WINDOW (dialog),
417 GTK_DIALOG_MODAL, GTK_MESSAGE_QUESTION,
418 GTK_BUTTONS_YES_NO,
419 buffer);
420 gtk_window_set_title(GTK_WINDOW (confirm_overwrite_dialog),
421 _("File already exists"));
422 gtk_dialog_set_default_response (GTK_DIALOG (confirm_overwrite_dialog),
423 GTK_RESPONSE_NO);
425 if (gtk_dialog_run(GTK_DIALOG(confirm_overwrite_dialog))
426 != GTK_RESPONSE_YES) {
427 write_file = FALSE;
428 file = 0;
431 gtk_widget_destroy(confirm_overwrite_dialog);
434 if (write_file) {
435 if (!g_path_is_absolute(filename)) {
436 char *dirname;
437 char *full_filename;
439 dirname = g_path_get_dirname(dia->filename);
440 full_filename = g_build_filename(dirname, filename, NULL);
441 file = fopen(full_filename, "w");
442 g_free(full_filename);
443 g_free(dirname);
444 } else {
445 file = fopen(filename, "w");
449 is_pipe = FALSE;
452 /* Store dialog values */
453 last_print_options.printer = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(iscmd));
455 if (write_file) {
456 if (!file) {
457 if (is_pipe) {
458 message_warning(_("Could not run command '%s': %s"), printcmd, strerror(errno));
459 g_free(printcmd);
460 } else
461 message_warning(_("Could not open '%s' for writing: %s"),
462 gtk_entry_get_text(GTK_ENTRY(ofile)), strerror(errno));
463 } else
464 done = TRUE;
466 } while(!done);
468 g_free(orig_command);
469 g_free(orig_file);
470 #ifndef G_OS_WIN32
471 /* set up a SIGPIPE handler to catch IO errors, rather than segfaulting */
472 sigpipe_received = FALSE;
473 old_action = signal(SIGPIPE, pipe_handler);
474 #endif
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);
484 } else
485 fclose(file);
487 #ifndef G_OS_WIN32
488 /* restore original behaviour */
489 signal(SIGPIPE, old_action);
490 #endif
491 if (sigpipe_received)
492 message_error(_("Printing error: command '%s' caused sigpipe."),
493 printcmd);
495 if (is_pipe) g_free(printcmd);