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 */
34 #include "diagramdata.h"
35 #include "render_eps.h"
36 #include "diapsrenderer.h"
37 #include "paginate_psprint.h"
38 #include "diapagelayout.h"
39 #include "persistence.h"
45 #include "win32print.h"
46 #define pclose(file) win32_printer_close (file)
49 /* keep track of print options between prints */
50 typedef struct _dia_print_options
{
54 static dia_print_options last_print_options
=
60 count_objs(DiaObject
*obj
, DiaRenderer
*renderer
, int active_layer
, guint
*nobjs
)
66 print_page(DiagramData
*data
, DiaRenderer
*diarend
, Rectangle
*bounds
)
68 DiaPsRenderer
*rend
= DIA_PS_RENDERER(diarend
);
70 gfloat tmargin
= data
->paper
.tmargin
, bmargin
= data
->paper
.bmargin
;
71 gfloat lmargin
= data
->paper
.lmargin
;
72 gfloat scale
= data
->paper
.scaling
;
73 gchar d1_buf
[G_ASCII_DTOSTR_BUF_SIZE
];
74 gchar d2_buf
[G_ASCII_DTOSTR_BUF_SIZE
];
76 rend
->paper
= data
->paper
.name
;
77 rend
->is_portrait
= data
->paper
.is_portrait
;
79 /* count the number of objects in this region */
80 data_render(data
, diarend
, bounds
,
81 (ObjectRenderer
) count_objs
, &nobjs
);
86 /* output a page number comment */
87 fprintf(rend
->file
, "%%%%Page: %d %d\n", rend
->pagenum
, rend
->pagenum
);
90 /* save print context */
91 fprintf(rend
->file
, "gs\n");
93 /* transform coordinate system */
94 if (data
->paper
.is_portrait
) {
95 fprintf(rend
->file
, "%s %s scale\n",
96 g_ascii_formatd(d1_buf
, sizeof(d1_buf
), "%f", 28.346457*scale
),
97 g_ascii_formatd(d2_buf
, sizeof(d2_buf
), "%f", -28.346457*scale
) );
98 fprintf(rend
->file
, "%s %s translate\n",
99 g_ascii_formatd(d1_buf
, sizeof(d1_buf
), "%f", lmargin
/scale
- bounds
->left
),
100 g_ascii_formatd(d2_buf
, sizeof(d2_buf
), "%f", -bmargin
/scale
- bounds
->bottom
) );
102 fprintf(rend
->file
, "90 rotate\n");
103 fprintf(rend
->file
, "%s %s scale\n",
104 g_ascii_formatd(d1_buf
, sizeof(d1_buf
), "%f", 28.346457*scale
),
105 g_ascii_formatd(d2_buf
, sizeof(d2_buf
), "%f", -28.346457*scale
) );
106 fprintf(rend
->file
, "%s %s translate\n",
107 g_ascii_formatd(d1_buf
, sizeof(d1_buf
), "%f", lmargin
/scale
- bounds
->left
),
108 g_ascii_formatd(d2_buf
, sizeof(d2_buf
), "%f", tmargin
/scale
- bounds
->top
) );
111 /* set up clip mask */
112 fprintf(rend
->file
, "n %s %s m ",
113 g_ascii_formatd(d1_buf
, sizeof(d1_buf
), "%f", bounds
->left
),
114 g_ascii_formatd(d2_buf
, sizeof(d2_buf
), "%f", bounds
->top
) );
115 fprintf(rend
->file
, "%s %s l ",
116 g_ascii_formatd(d1_buf
, sizeof(d1_buf
), "%f", bounds
->right
),
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
->bottom
) );
121 fprintf(rend
->file
, "%s %s l ",
122 g_ascii_formatd(d1_buf
, sizeof(d1_buf
), "%f", bounds
->left
),
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
->top
) );
127 /* Tip from Dov Grobgeld: Clip does not destroy the path, so we should
128 do a newpath afterwards */
129 fprintf(rend
->file
, "clip n\n");
131 /* render the region */
132 data_render(data
, diarend
, bounds
, NULL
, NULL
);
134 /* restore print context */
135 fprintf(rend
->file
, "gr\n");
138 fprintf(rend
->file
, "showpage\n\n");
144 paginate_psprint(Diagram
*dia
, FILE *file
)
148 gfloat width
, height
;
149 gfloat x
, y
, initx
, inity
;
152 rend
= new_psprint_renderer(dia
, file
);
154 #ifdef DIA_PS_RENDERER_DUAL_PASS
155 /* Prepare the prolog (with fonts etc) */
156 data_render(dia
->data
, DIA_RENDERER(rend
), NULL
, NULL
, NULL
);
157 eps_renderer_prolog_done(rend
);
160 /* the usable area of the page */
161 width
= dia
->data
->paper
.width
;
162 height
= dia
->data
->paper
.height
;
164 /* get extents, and make them multiples of width / height */
165 extents
= &dia
->data
->extents
;
166 initx
= extents
->left
;
167 inity
= extents
->top
;
168 /* make page boundaries align with origin */
169 if (!dia
->data
->paper
.fitto
) {
170 initx
= floor(initx
/ width
) * width
;
171 inity
= floor(inity
/ height
) * height
;
174 /* iterate through all the pages in the diagram */
175 for (y
= inity
; y
< extents
->bottom
; y
+= height
)
176 for (x
= initx
; x
< extents
->right
; x
+= width
) {
177 Rectangle page_bounds
;
179 page_bounds
.left
= x
;
180 page_bounds
.right
= x
+ width
;
182 page_bounds
.bottom
= y
+ height
;
184 nobjs
+= print_page(dia
->data
,rend
, &page_bounds
);
187 g_object_unref(rend
);
192 change_entry_state(GtkToggleButton
*radio
, GtkWidget
*entry
)
194 gtk_widget_set_sensitive(entry
, gtk_toggle_button_get_active(radio
));
198 ok_pressed(GtkButton
*button
, gboolean
*flag
)
204 static gboolean sigpipe_received
= FALSE
;
206 pipe_handler(int signum
)
208 sigpipe_received
= TRUE
;
212 diagram_print_destroy(GtkWidget
*widget
)
216 if ((dia
= gtk_object_get_user_data(GTK_OBJECT(widget
))) != NULL
) {
218 gtk_object_set_user_data(GTK_OBJECT(widget
), NULL
);
225 diagram_print_ps(Diagram
*dia
)
228 GtkWidget
*vbox
, *frame
, *table
, *box
, *button
;
229 GtkWidget
*iscmd
, *isofile
;
230 GtkWidget
*cmd
, *ofile
;
231 gboolean cont
= FALSE
;
232 gchar
*printcmd
= NULL
;
233 gchar
*orig_command
, *orig_file
;
238 /* all the signal stuff below doesn't compile on win32, but it isn't
239 * needed anymore because the pipe handling - which never worked on win32
240 * anyway - is replace by "native" postscript printing now ...
245 /* create the dialog */
246 dialog
= gtk_dialog_new();
247 /* the dialog has it's own reference to the diagram */
249 gtk_object_set_user_data(GTK_OBJECT(dialog
), dia
);
250 g_signal_connect(GTK_OBJECT(dialog
), "destroy",
251 G_CALLBACK(diagram_print_destroy
), NULL
);
252 g_signal_connect(GTK_OBJECT(dialog
), "delete_event",
253 G_CALLBACK(gtk_main_quit
), NULL
);
254 g_signal_connect(GTK_OBJECT(dialog
), "delete_event",
255 G_CALLBACK(gtk_true
), NULL
);
256 vbox
= GTK_DIALOG(dialog
)->vbox
;
258 frame
= gtk_frame_new(_("Select Printer"));
259 gtk_container_set_border_width(GTK_CONTAINER(frame
), 5);
260 gtk_box_pack_start(GTK_BOX(vbox
), frame
, TRUE
, TRUE
, 0);
261 gtk_widget_show(frame
);
263 table
= gtk_table_new(2, 2, FALSE
);
264 gtk_container_set_border_width(GTK_CONTAINER(table
), 5);
265 gtk_table_set_row_spacings(GTK_TABLE(table
), 5);
266 gtk_table_set_col_spacings(GTK_TABLE(table
), 5);
267 gtk_container_add(GTK_CONTAINER(frame
), table
);
268 gtk_widget_show(table
);
270 iscmd
= gtk_radio_button_new_with_label(NULL
, _("Printer"));
271 gtk_table_attach(GTK_TABLE(table
), iscmd
, 0,1, 0,1,
272 GTK_FILL
, GTK_FILL
|GTK_EXPAND
, 0, 0);
273 gtk_widget_show(iscmd
);
275 cmd
= gtk_entry_new();
276 gtk_table_attach(GTK_TABLE(table
), cmd
, 1,2, 0,1,
277 GTK_FILL
|GTK_EXPAND
, GTK_FILL
|GTK_EXPAND
, 0, 0);
278 gtk_widget_show(cmd
);
280 g_signal_connect(GTK_OBJECT(iscmd
), "toggled",
281 G_CALLBACK(change_entry_state
), cmd
);
283 isofile
= gtk_radio_button_new_with_label(GTK_RADIO_BUTTON(iscmd
)->group
,
285 gtk_table_attach(GTK_TABLE(table
), isofile
, 0,1, 1,2,
286 GTK_FILL
, GTK_FILL
|GTK_EXPAND
, 0, 0);
287 gtk_widget_show(isofile
);
289 ofile
= gtk_entry_new();
290 gtk_widget_set_sensitive(ofile
, FALSE
);
291 gtk_table_attach(GTK_TABLE(table
), ofile
, 1,2, 1,2,
292 GTK_FILL
|GTK_EXPAND
, GTK_FILL
|GTK_EXPAND
, 0, 0);
293 gtk_widget_show(ofile
);
294 g_signal_connect(GTK_OBJECT(isofile
), "toggled",
295 G_CALLBACK(change_entry_state
), ofile
);
297 box
= GTK_DIALOG(dialog
)->action_area
;
299 button
= gtk_button_new_with_label(_("OK"));
300 g_signal_connect(GTK_OBJECT(button
), "clicked",
301 G_CALLBACK(ok_pressed
), &cont
);
302 GTK_WIDGET_SET_FLAGS(button
, GTK_CAN_DEFAULT
);
303 gtk_box_pack_start(GTK_BOX(box
), button
, TRUE
, TRUE
, 0);
304 gtk_widget_grab_default(button
);
305 gtk_widget_show(button
);
307 button
= gtk_button_new_with_label(_("Cancel"));
308 g_signal_connect(GTK_OBJECT(button
), "clicked",
309 G_CALLBACK(gtk_main_quit
), NULL
);
310 GTK_WIDGET_SET_FLAGS(button
, GTK_CAN_DEFAULT
);
311 gtk_box_pack_start(GTK_BOX(box
), button
, TRUE
, TRUE
, 0);
312 gtk_widget_show(button
);
314 /* Set default or old dialog values: */
316 gtk_entry_set_text(GTK_ENTRY(cmd
), win32_printer_default ());
319 const gchar
*printer
= g_getenv("PRINTER");
322 printcmd
= g_strdup_printf("lpr -P%s", printer
);
324 printcmd
= g_strdup("lpr");
327 gtk_entry_set_text(GTK_ENTRY(cmd
), printcmd
);
332 printf("Print dialog?\n");
333 persistence_register_string_entry("printer-command", cmd
);
334 printcmd
= g_strdup(gtk_entry_get_text(GTK_ENTRY(cmd
)));
335 orig_command
= printcmd
;
336 /* Ought to use filename+extension here */
337 gtk_entry_set_text(GTK_ENTRY(ofile
), "output.ps");
338 persistence_register_string_entry("printer-file", ofile
);
339 orig_file
= g_strdup(gtk_entry_get_text(GTK_ENTRY(ofile
)));
341 /* Scaling is already set at creation. */
342 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(iscmd
), last_print_options
.printer
);
343 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(isofile
), !last_print_options
.printer
);
345 gtk_widget_show(dialog
);
349 gtk_widget_destroy(dialog
);
354 persistence_change_string_entry("printer-command", orig_command
, cmd
);
355 persistence_change_string_entry("printer-file", orig_file
, ofile
);
356 gtk_widget_destroy(dialog
);
357 g_free(orig_command
);
362 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(iscmd
))) {
363 printcmd
= g_strdup(gtk_entry_get_text(GTK_ENTRY(cmd
)));
365 file
= win32_printer_open (printcmd
);
367 file
= popen(printcmd
, "w");
371 const gchar
*filename
= gtk_entry_get_text(GTK_ENTRY(ofile
));
372 if (!g_path_is_absolute(filename
)) {
376 dirname
= g_path_get_dirname(dia
->filename
);
377 full_filename
= g_build_filename(dirname
, filename
, NULL
);
378 file
= fopen(full_filename
, "w");
379 g_free(full_filename
);
382 file
= fopen(filename
, "w");
387 /* Store dialog values */
388 g_free(orig_command
);
390 last_print_options
.printer
= gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(iscmd
));
394 message_warning(_("Could not run command '%s': %s"), printcmd
, strerror(errno
));
397 message_warning(_("Could not open '%s' for writing: %s"),
398 gtk_entry_get_text(GTK_ENTRY(ofile
)), strerror(errno
));
399 gtk_widget_destroy(dialog
);
404 /* set up a SIGPIPE handler to catch IO errors, rather than segfaulting */
405 sigpipe_received
= FALSE
;
406 old_action
= signal(SIGPIPE
, pipe_handler
);
409 paginate_psprint(dia
, file
);
410 gtk_widget_destroy(dialog
);
412 int exitval
= pclose(file
);
414 message_error(_("Printing error: command '%s' returned %d\n"),
421 /* restore original behaviour */
422 signal(SIGPIPE
, old_action
);
424 if (sigpipe_received
)
425 message_error(_("Printing error: command '%s' caused sigpipe."),
428 if (is_pipe
) g_free(printcmd
);