/* Dia -- an diagram creation/manipulation program * Copyright (C) 1998, 1999 Alexander Larsson * * paginate_psprint.[ch] -- pagination code for the postscript backend * Copyright (C) 1999 James Henstridge * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifdef HAVE_CONFIG_H #include #endif /* so we get popen and sigaction even when compiling with -ansi */ #define _POSIX_C_SOURCE 2 #include #include /* strlen */ #include #include #include #include #include "intl.h" #include "message.h" #include "diagram.h" #include "diagramdata.h" #include "render_eps.h" #include "diapsrenderer.h" #include "paginate_psprint.h" #include "diapagelayout.h" #include "persistence.h" #include #ifdef G_OS_WIN32 #include #include "win32print.h" #define pclose(file) win32_printer_close (file) #endif /* keep track of print options between prints */ typedef struct _dia_print_options { int printer; } dia_print_options; static dia_print_options last_print_options = { 1 }; static void count_objs(DiaObject *obj, DiaRenderer *renderer, int active_layer, guint *nobjs) { (*nobjs)++; } static guint print_page(DiagramData *data, DiaRenderer *diarend, Rectangle *bounds) { DiaPsRenderer *rend = DIA_PS_RENDERER(diarend); guint nobjs = 0; gfloat tmargin = data->paper.tmargin, bmargin = data->paper.bmargin; gfloat lmargin = data->paper.lmargin; gfloat scale = data->paper.scaling; gchar d1_buf[G_ASCII_DTOSTR_BUF_SIZE]; gchar d2_buf[G_ASCII_DTOSTR_BUF_SIZE]; rend->paper = data->paper.name; rend->is_portrait = data->paper.is_portrait; /* count the number of objects in this region */ data_render(data, diarend, bounds, (ObjectRenderer) count_objs, &nobjs); if (nobjs == 0) return nobjs; /* output a page number comment */ fprintf(rend->file, "%%%%Page: %d %d\n", rend->pagenum, rend->pagenum); rend->pagenum++; /* save print context */ fprintf(rend->file, "gs\n"); /* transform coordinate system */ if (data->paper.is_portrait) { fprintf(rend->file, "%s %s scale\n", g_ascii_formatd(d1_buf, sizeof(d1_buf), "%f", 28.346457*scale), g_ascii_formatd(d2_buf, sizeof(d2_buf), "%f", -28.346457*scale) ); fprintf(rend->file, "%s %s translate\n", g_ascii_formatd(d1_buf, sizeof(d1_buf), "%f", lmargin/scale - bounds->left), g_ascii_formatd(d2_buf, sizeof(d2_buf), "%f", -bmargin/scale - bounds->bottom) ); } else { fprintf(rend->file, "90 rotate\n"); fprintf(rend->file, "%s %s scale\n", g_ascii_formatd(d1_buf, sizeof(d1_buf), "%f", 28.346457*scale), g_ascii_formatd(d2_buf, sizeof(d2_buf), "%f", -28.346457*scale) ); fprintf(rend->file, "%s %s translate\n", g_ascii_formatd(d1_buf, sizeof(d1_buf), "%f", lmargin/scale - bounds->left), g_ascii_formatd(d2_buf, sizeof(d2_buf), "%f", tmargin/scale - bounds->top) ); } /* set up clip mask */ fprintf(rend->file, "n %s %s m ", g_ascii_formatd(d1_buf, sizeof(d1_buf), "%f", bounds->left), g_ascii_formatd(d2_buf, sizeof(d2_buf), "%f", bounds->top) ); fprintf(rend->file, "%s %s l ", g_ascii_formatd(d1_buf, sizeof(d1_buf), "%f", bounds->right), g_ascii_formatd(d2_buf, sizeof(d2_buf), "%f", bounds->top) ); fprintf(rend->file, "%s %s l ", g_ascii_formatd(d1_buf, sizeof(d1_buf), "%f", bounds->right), g_ascii_formatd(d2_buf, sizeof(d2_buf), "%f", bounds->bottom) ); fprintf(rend->file, "%s %s l ", g_ascii_formatd(d1_buf, sizeof(d1_buf), "%f", bounds->left), g_ascii_formatd(d2_buf, sizeof(d2_buf), "%f", bounds->bottom) ); fprintf(rend->file, "%s %s l ", g_ascii_formatd(d1_buf, sizeof(d1_buf), "%f", bounds->left), g_ascii_formatd(d2_buf, sizeof(d2_buf), "%f", bounds->top) ); /* Tip from Dov Grobgeld: Clip does not destroy the path, so we should do a newpath afterwards */ fprintf(rend->file, "clip n\n"); /* render the region */ data_render(data, diarend, bounds, NULL, NULL); /* restore print context */ fprintf(rend->file, "gr\n"); /* print the page */ fprintf(rend->file, "showpage\n\n"); return nobjs; } void paginate_psprint(Diagram *dia, FILE *file) { DiaRenderer *rend; Rectangle *extents; gfloat width, height; gfloat x, y, initx, inity; guint nobjs = 0; rend = new_psprint_renderer(dia, file); #ifdef DIA_PS_RENDERER_DUAL_PASS /* Prepare the prolog (with fonts etc) */ data_render(dia->data, DIA_RENDERER(rend), NULL, NULL, NULL); eps_renderer_prolog_done(rend); #endif /* the usable area of the page */ width = dia->data->paper.width; height = dia->data->paper.height; /* get extents, and make them multiples of width / height */ extents = &dia->data->extents; initx = extents->left; inity = extents->top; /* make page boundaries align with origin */ if (!dia->data->paper.fitto) { initx = floor(initx / width) * width; inity = floor(inity / height) * height; } /* iterate through all the pages in the diagram */ for (y = inity; y < extents->bottom; y += height) { /* ensure we are not producing pages for epsilon */ if ((extents->bottom - y) < 1e-6) break; for (x = initx; x < extents->right; x += width) { Rectangle page_bounds; if ((extents->right - x) < 1e-6) break; page_bounds.left = x; page_bounds.right = x + width; page_bounds.top = y; page_bounds.bottom = y + height; nobjs += print_page(dia->data,rend, &page_bounds); } } g_object_unref(rend); } static void change_entry_state(GtkToggleButton *radio, GtkWidget *entry) { gtk_widget_set_sensitive(entry, gtk_toggle_button_get_active(radio)); } static void ok_pressed(GtkButton *button, gboolean *flag) { *flag = TRUE; gtk_main_quit(); } static gboolean sigpipe_received = FALSE; static void pipe_handler(int signum) { sigpipe_received = TRUE; } static gboolean diagram_print_destroy(GtkWidget *widget) { Diagram *dia; if ((dia = gtk_object_get_user_data(GTK_OBJECT(widget))) != NULL) { g_object_unref(dia); gtk_object_set_user_data(GTK_OBJECT(widget), NULL); } return FALSE; } void diagram_print_ps(Diagram *dia) { GtkWidget *dialog; GtkWidget *vbox, *frame, *table, *box, *button; GtkWidget *iscmd, *isofile; GtkWidget *cmd, *ofile; gboolean cont = FALSE; gchar *printcmd = NULL; gchar *orig_command, *orig_file; gchar *filename = NULL; gchar *printer_filename = NULL; gchar *dot = NULL; gboolean done = FALSE; gboolean write_file = TRUE; /* Used in prompt to overwrite existing file */ FILE *file; gboolean is_pipe; #ifndef G_OS_WIN32 /* all the signal stuff below doesn't compile on win32, but it isn't * needed anymore because the pipe handling - which never worked on win32 * anyway - is replace by "native" postscript printing now ... */ struct sigaction old_sigpipe_action, sigpipe_action; #endif /* create the dialog */ dialog = gtk_dialog_new(); /* the dialog has it's own reference to the diagram */ g_object_ref(dia); gtk_object_set_user_data(GTK_OBJECT(dialog), dia); g_signal_connect(GTK_OBJECT(dialog), "destroy", G_CALLBACK(diagram_print_destroy), NULL); g_signal_connect(GTK_OBJECT(dialog), "delete_event", G_CALLBACK(gtk_main_quit), NULL); g_signal_connect(GTK_OBJECT(dialog), "delete_event", G_CALLBACK(gtk_true), NULL); vbox = GTK_DIALOG(dialog)->vbox; frame = gtk_frame_new(_("Select Printer")); gtk_container_set_border_width(GTK_CONTAINER(frame), 5); gtk_box_pack_start(GTK_BOX(vbox), frame, TRUE, TRUE, 0); gtk_widget_show(frame); table = gtk_table_new(2, 2, FALSE); gtk_container_set_border_width(GTK_CONTAINER(table), 5); gtk_table_set_row_spacings(GTK_TABLE(table), 5); gtk_table_set_col_spacings(GTK_TABLE(table), 5); gtk_container_add(GTK_CONTAINER(frame), table); gtk_widget_show(table); iscmd = gtk_radio_button_new_with_label(NULL, _("Printer")); gtk_table_attach(GTK_TABLE(table), iscmd, 0,1, 0,1, GTK_FILL, GTK_FILL|GTK_EXPAND, 0, 0); gtk_widget_show(iscmd); cmd = gtk_entry_new(); gtk_table_attach(GTK_TABLE(table), cmd, 1,2, 0,1, GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 0, 0); gtk_widget_show(cmd); g_signal_connect(GTK_OBJECT(iscmd), "toggled", G_CALLBACK(change_entry_state), cmd); isofile = gtk_radio_button_new_with_label(GTK_RADIO_BUTTON(iscmd)->group, _("File")); gtk_table_attach(GTK_TABLE(table), isofile, 0,1, 1,2, GTK_FILL, GTK_FILL|GTK_EXPAND, 0, 0); gtk_widget_show(isofile); ofile = gtk_entry_new(); gtk_widget_set_sensitive(ofile, FALSE); gtk_table_attach(GTK_TABLE(table), ofile, 1,2, 1,2, GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 0, 0); gtk_widget_show(ofile); g_signal_connect(GTK_OBJECT(isofile), "toggled", G_CALLBACK(change_entry_state), ofile); box = GTK_DIALOG(dialog)->action_area; button = gtk_button_new_with_label(_("OK")); g_signal_connect(GTK_OBJECT(button), "clicked", G_CALLBACK(ok_pressed), &cont); GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT); gtk_box_pack_start(GTK_BOX(box), button, TRUE, TRUE, 0); gtk_widget_grab_default(button); gtk_widget_show(button); button = gtk_button_new_with_label(_("Cancel")); g_signal_connect(GTK_OBJECT(button), "clicked", G_CALLBACK(gtk_main_quit), NULL); GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT); gtk_box_pack_start(GTK_BOX(box), button, TRUE, TRUE, 0); gtk_widget_show(button); /* Set default or old dialog values: */ #ifdef G_OS_WIN32 gtk_entry_set_text(GTK_ENTRY(cmd), win32_printer_default ()); #else { const gchar *printer = g_getenv("PRINTER"); if (printer) { printcmd = g_strdup_printf("lpr -P%s", printer); } else { printcmd = g_strdup("lpr"); } gtk_entry_set_text(GTK_ENTRY(cmd), printcmd); g_free(printcmd); printcmd = NULL; } #endif persistence_register_string_entry("printer-command", cmd); printcmd = g_strdup(gtk_entry_get_text(GTK_ENTRY(cmd))); orig_command = printcmd; /* Work out diagram filename and use this as default .ps file */ filename = g_path_get_basename(dia->filename); printer_filename = g_malloc(strlen(filename) + 4); printer_filename = strcpy(printer_filename, filename); dot = strrchr(printer_filename, '.'); if ((dot != NULL) && (strcmp(dot, ".dia") == 0)) *dot = '\0'; printer_filename = strcat(printer_filename, ".ps"); gtk_entry_set_text(GTK_ENTRY(ofile), printer_filename); g_free(printer_filename); orig_file = g_strdup(gtk_entry_get_text(GTK_ENTRY(ofile))); /* Scaling is already set at creation. */ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(iscmd), last_print_options.printer); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(isofile), !last_print_options.printer); gtk_widget_show(dialog); do { cont = FALSE; write_file = TRUE; gtk_main(); if(!dia) { gtk_widget_destroy(dialog); return; } if (!cont) { persistence_change_string_entry("printer-command", orig_command, cmd); gtk_widget_destroy(dialog); g_free(orig_command); g_free(orig_file); return; } if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(iscmd))) { printcmd = g_strdup(gtk_entry_get_text(GTK_ENTRY(cmd))); #ifdef G_OS_WIN32 file = win32_printer_open (printcmd); #else file = popen(printcmd, "w"); #endif is_pipe = TRUE; } else { const gchar *filename = gtk_entry_get_text(GTK_ENTRY(ofile)); struct stat statbuf; if (g_stat(filename, &statbuf) == 0) { /* Output file exists */ GtkWidget *confirm_overwrite_dialog = NULL; char buffer[300]; char *utf8filename = NULL; if (!g_utf8_validate(filename, -1, NULL)) { utf8filename = g_filename_to_utf8(filename, -1, NULL, NULL, NULL); if (utf8filename == NULL) { message_warning(_("Some characters in the filename are neither " "UTF-8\nnor your local encoding.\nSome things will break.")); } } if (utf8filename == NULL) utf8filename = g_strdup(filename); g_snprintf(buffer, 300, _("The file '%s' already exists.\n" "Do you want to overwrite it?"), utf8filename); g_free(utf8filename); confirm_overwrite_dialog = gtk_message_dialog_new(GTK_WINDOW (dialog), GTK_DIALOG_MODAL, GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO, buffer); gtk_window_set_title(GTK_WINDOW (confirm_overwrite_dialog), _("File already exists")); gtk_dialog_set_default_response (GTK_DIALOG (confirm_overwrite_dialog), GTK_RESPONSE_NO); if (gtk_dialog_run(GTK_DIALOG(confirm_overwrite_dialog)) != GTK_RESPONSE_YES) { write_file = FALSE; file = 0; } gtk_widget_destroy(confirm_overwrite_dialog); } if (write_file) { if (!g_path_is_absolute(filename)) { char *dirname; char *full_filename; dirname = g_path_get_dirname(dia->filename); full_filename = g_build_filename(dirname, filename, NULL); file = g_fopen(full_filename, "w"); g_free(full_filename); g_free(dirname); } else { file = g_fopen(filename, "w"); } } is_pipe = FALSE; } /* Store dialog values */ last_print_options.printer = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(iscmd)); if (write_file) { if (!file) { if (is_pipe) { message_warning(_("Could not run command '%s': %s"), printcmd, strerror(errno)); g_free(printcmd); } else message_warning(_("Could not open '%s' for writing: %s"), gtk_entry_get_text(GTK_ENTRY(ofile)), strerror(errno)); } else done = TRUE; } } while(!done); g_free(orig_command); g_free(orig_file); #ifndef G_OS_WIN32 /* set up a SIGPIPE handler to catch IO errors, rather than segfaulting */ sigpipe_received = FALSE; memset(&sigpipe_action, 0, sizeof(struct sigaction)); sigpipe_action.sa_handler = pipe_handler; sigaction(SIGPIPE, &sigpipe_action, &old_sigpipe_action); #endif paginate_psprint(dia, file); gtk_widget_destroy(dialog); if (is_pipe) { int exitval = pclose(file); if (exitval != 0) { message_error(_("Printing error: command '%s' returned %d\n"), printcmd, exitval); } } else fclose(file); #ifndef G_OS_WIN32 /* restore original behaviour */ sigaction(SIGPIPE, &old_sigpipe_action, NULL); #endif if (sigpipe_received) message_error(_("Printing error: command '%s' caused sigpipe."), printcmd); if (is_pipe) g_free(printcmd); }