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.
27 #include <string.h> /* strlen */
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"
46 #include "win32print.h"
47 #define pclose(file) win32_printer_close (file)
50 /* keep track of print options between prints */
51 typedef struct _dia_print_options
{
55 static dia_print_options last_print_options
=
61 count_objs(DiaObject
*obj
, DiaRenderer
*renderer
, int active_layer
, guint
*nobjs
)
67 print_page(DiagramData
*data
, DiaRenderer
*diarend
, Rectangle
*bounds
)
69 DiaPsRenderer
*rend
= DIA_PS_RENDERER(diarend
);
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
);
87 /* output a page number comment */
88 fprintf(rend
->file
, "%%%%Page: %d %d\n", rend
->pagenum
, 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
) );
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");
139 fprintf(rend
->file
, "showpage\n\n");
145 paginate_psprint(Diagram
*dia
, FILE *file
)
149 gfloat width
, height
;
150 gfloat x
, y
, initx
, inity
;
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
);
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)
180 for (x
= initx
; x
< extents
->right
; x
+= width
) {
181 Rectangle page_bounds
;
183 if ((extents
->right
- x
) < 1e-6)
186 page_bounds
.left
= x
;
187 page_bounds
.right
= x
+ width
;
189 page_bounds
.bottom
= y
+ height
;
191 nobjs
+= print_page(dia
->data
,rend
, &page_bounds
);
195 g_object_unref(rend
);
200 change_entry_state(GtkToggleButton
*radio
, GtkWidget
*entry
)
202 gtk_widget_set_sensitive(entry
, gtk_toggle_button_get_active(radio
));
206 ok_pressed(GtkButton
*button
, gboolean
*flag
)
212 static gboolean sigpipe_received
= FALSE
;
214 pipe_handler(int signum
)
216 sigpipe_received
= TRUE
;
220 diagram_print_destroy(GtkWidget
*widget
)
224 if ((dia
= gtk_object_get_user_data(GTK_OBJECT(widget
))) != NULL
) {
226 gtk_object_set_user_data(GTK_OBJECT(widget
), NULL
);
233 diagram_print_ps(Diagram
*dia
)
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
;
246 /* all the signal stuff below doesn't compile on win32, but it isn't
247 * needed anymore because the pipe handling - which never worked on win32
248 * anyway - is replace by "native" postscript printing now ...
253 /* create the dialog */
254 dialog
= gtk_dialog_new();
255 /* the dialog has it's own reference to the diagram */
257 gtk_object_set_user_data(GTK_OBJECT(dialog
), dia
);
258 g_signal_connect(GTK_OBJECT(dialog
), "destroy",
259 G_CALLBACK(diagram_print_destroy
), NULL
);
260 g_signal_connect(GTK_OBJECT(dialog
), "delete_event",
261 G_CALLBACK(gtk_main_quit
), NULL
);
262 g_signal_connect(GTK_OBJECT(dialog
), "delete_event",
263 G_CALLBACK(gtk_true
), NULL
);
264 vbox
= GTK_DIALOG(dialog
)->vbox
;
266 frame
= gtk_frame_new(_("Select Printer"));
267 gtk_container_set_border_width(GTK_CONTAINER(frame
), 5);
268 gtk_box_pack_start(GTK_BOX(vbox
), frame
, TRUE
, TRUE
, 0);
269 gtk_widget_show(frame
);
271 table
= gtk_table_new(2, 2, FALSE
);
272 gtk_container_set_border_width(GTK_CONTAINER(table
), 5);
273 gtk_table_set_row_spacings(GTK_TABLE(table
), 5);
274 gtk_table_set_col_spacings(GTK_TABLE(table
), 5);
275 gtk_container_add(GTK_CONTAINER(frame
), table
);
276 gtk_widget_show(table
);
278 iscmd
= gtk_radio_button_new_with_label(NULL
, _("Printer"));
279 gtk_table_attach(GTK_TABLE(table
), iscmd
, 0,1, 0,1,
280 GTK_FILL
, GTK_FILL
|GTK_EXPAND
, 0, 0);
281 gtk_widget_show(iscmd
);
283 cmd
= gtk_entry_new();
284 gtk_table_attach(GTK_TABLE(table
), cmd
, 1,2, 0,1,
285 GTK_FILL
|GTK_EXPAND
, GTK_FILL
|GTK_EXPAND
, 0, 0);
286 gtk_widget_show(cmd
);
288 g_signal_connect(GTK_OBJECT(iscmd
), "toggled",
289 G_CALLBACK(change_entry_state
), cmd
);
291 isofile
= gtk_radio_button_new_with_label(GTK_RADIO_BUTTON(iscmd
)->group
,
293 gtk_table_attach(GTK_TABLE(table
), isofile
, 0,1, 1,2,
294 GTK_FILL
, GTK_FILL
|GTK_EXPAND
, 0, 0);
295 gtk_widget_show(isofile
);
297 ofile
= gtk_entry_new();
298 gtk_widget_set_sensitive(ofile
, FALSE
);
299 gtk_table_attach(GTK_TABLE(table
), ofile
, 1,2, 1,2,
300 GTK_FILL
|GTK_EXPAND
, GTK_FILL
|GTK_EXPAND
, 0, 0);
301 gtk_widget_show(ofile
);
302 g_signal_connect(GTK_OBJECT(isofile
), "toggled",
303 G_CALLBACK(change_entry_state
), ofile
);
305 box
= GTK_DIALOG(dialog
)->action_area
;
307 button
= gtk_button_new_with_label(_("OK"));
308 g_signal_connect(GTK_OBJECT(button
), "clicked",
309 G_CALLBACK(ok_pressed
), &cont
);
310 GTK_WIDGET_SET_FLAGS(button
, GTK_CAN_DEFAULT
);
311 gtk_box_pack_start(GTK_BOX(box
), button
, TRUE
, TRUE
, 0);
312 gtk_widget_grab_default(button
);
313 gtk_widget_show(button
);
315 button
= gtk_button_new_with_label(_("Cancel"));
316 g_signal_connect(GTK_OBJECT(button
), "clicked",
317 G_CALLBACK(gtk_main_quit
), NULL
);
318 GTK_WIDGET_SET_FLAGS(button
, GTK_CAN_DEFAULT
);
319 gtk_box_pack_start(GTK_BOX(box
), button
, TRUE
, TRUE
, 0);
320 gtk_widget_show(button
);
322 /* Set default or old dialog values: */
324 gtk_entry_set_text(GTK_ENTRY(cmd
), win32_printer_default ());
327 const gchar
*printer
= g_getenv("PRINTER");
330 printcmd
= g_strdup_printf("lpr -P%s", printer
);
332 printcmd
= g_strdup("lpr");
335 gtk_entry_set_text(GTK_ENTRY(cmd
), printcmd
);
340 persistence_register_string_entry("printer-command", cmd
);
341 printcmd
= g_strdup(gtk_entry_get_text(GTK_ENTRY(cmd
)));
342 orig_command
= printcmd
;
344 /* Work out diagram filename and use this as default .ps file */
345 char *filename
= g_path_get_basename(dia
->filename
);
346 char *printer_filename
= g_malloc(strlen(filename
) + 4);
347 printer_filename
= strcpy(printer_filename
, filename
);
348 char *dot
= strrchr(printer_filename
, '.');
349 if ((dot
!= NULL
) && (strcmp(dot
, ".dia") == 0))
351 printer_filename
= strcat(printer_filename
, ".ps");
352 gtk_entry_set_text(GTK_ENTRY(ofile
), printer_filename
);
353 g_free(printer_filename
);
354 orig_file
= g_strdup(gtk_entry_get_text(GTK_ENTRY(ofile
)));
356 /* Scaling is already set at creation. */
357 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(iscmd
), last_print_options
.printer
);
358 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(isofile
), !last_print_options
.printer
);
360 gtk_widget_show(dialog
);
361 gboolean done
= FALSE
;
362 gboolean write_file
= TRUE
; /* Used in prompt to overwrite existing file */
369 gtk_widget_destroy(dialog
);
374 persistence_change_string_entry("printer-command", orig_command
, cmd
);
375 gtk_widget_destroy(dialog
);
376 g_free(orig_command
);
381 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(iscmd
))) {
382 printcmd
= g_strdup(gtk_entry_get_text(GTK_ENTRY(cmd
)));
384 file
= win32_printer_open (printcmd
);
386 file
= popen(printcmd
, "w");
390 const gchar
*filename
= gtk_entry_get_text(GTK_ENTRY(ofile
));
393 if (stat(filename
, &statbuf
) == 0) { /* Output file exists */
394 GtkWidget
*confirm_overwrite_dialog
= NULL
;
396 char *utf8filename
= NULL
;
398 if (!g_utf8_validate(filename
, -1, NULL
)) {
399 utf8filename
= g_filename_to_utf8(filename
, -1, NULL
, NULL
, NULL
);
401 if (utf8filename
== NULL
) {
402 message_warning(_("Some characters in the filename are neither "
403 "UTF-8\nnor your local encoding.\nSome things will break."));
407 if (utf8filename
== NULL
) utf8filename
= g_strdup(filename
);
408 g_snprintf(buffer
, 300,
409 _("The file '%s' already exists.\n"
410 "Do you want to overwrite it?"), utf8filename
);
411 g_free(utf8filename
);
412 confirm_overwrite_dialog
= gtk_message_dialog_new(GTK_WINDOW (dialog
),
413 GTK_DIALOG_MODAL
, GTK_MESSAGE_QUESTION
,
416 gtk_window_set_title(GTK_WINDOW (confirm_overwrite_dialog
),
417 _("File already exists"));
418 gtk_dialog_set_default_response (GTK_DIALOG (confirm_overwrite_dialog
),
421 if (gtk_dialog_run(GTK_DIALOG(confirm_overwrite_dialog
))
422 != GTK_RESPONSE_YES
) {
427 gtk_widget_destroy(confirm_overwrite_dialog
);
431 if (!g_path_is_absolute(filename
)) {
435 dirname
= g_path_get_dirname(dia
->filename
);
436 full_filename
= g_build_filename(dirname
, filename
, NULL
);
437 file
= fopen(full_filename
, "w");
438 g_free(full_filename
);
441 file
= fopen(filename
, "w");
448 /* Store dialog values */
449 last_print_options
.printer
= gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(iscmd
));
454 message_warning(_("Could not run command '%s': %s"), printcmd
, strerror(errno
));
457 message_warning(_("Could not open '%s' for writing: %s"),
458 gtk_entry_get_text(GTK_ENTRY(ofile
)), strerror(errno
));
464 g_free(orig_command
);
467 /* set up a SIGPIPE handler to catch IO errors, rather than segfaulting */
468 sigpipe_received
= FALSE
;
469 old_action
= signal(SIGPIPE
, pipe_handler
);
472 paginate_psprint(dia
, file
);
473 gtk_widget_destroy(dialog
);
475 int exitval
= pclose(file
);
477 message_error(_("Printing error: command '%s' returned %d\n"),
484 /* restore original behaviour */
485 signal(SIGPIPE
, old_action
);
487 if (sigpipe_received
)
488 message_error(_("Printing error: command '%s' caused sigpipe."),
491 if (is_pipe
) g_free(printcmd
);