From 0b3cbf57875fd692e4ba0b336fefa4bee1ed00dc Mon Sep 17 00:00:00 2001 From: Simeon Simeonov Date: Mon, 26 Feb 2018 11:23:00 +0100 Subject: Initial commit for sylpheed 3.7.0 --- src/printing.c | 747 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 747 insertions(+) create mode 100644 src/printing.c (limited to 'src/printing.c') diff --git a/src/printing.c b/src/printing.c new file mode 100644 index 0000000..ca0f55f --- /dev/null +++ b/src/printing.c @@ -0,0 +1,747 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2017 Hiroyuki Yamamoto + * + * 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 "config.h" +#endif + +#include "defs.h" +#include "printing.h" + +#include +#include +#if GTK_CHECK_VERSION(2, 10, 0) +#include +#endif + +#include + +#include "mainwindow.h" +#include "procmsg.h" +#include "procheader.h" +#include "prefs_common.h" +#include "alertpanel.h" +#include "utils.h" + +#if GTK_CHECK_VERSION(2, 10, 0) + +#define SPACING 2.0 + +typedef struct +{ + MsgInfo *msginfo; + gint n_pages; + gchar *hdr_data; + gchar *msg_text_file; + FILE *fp; +} MsgPrintInfo; + +typedef struct +{ + gint page_nr_per_msg; + MsgPrintInfo *mpinfo; + glong pos; + gint lines; +} PageInfo; + +typedef struct +{ + GSList *mlist; + gint n_msgs; + MsgPrintInfo *msgs; + GPtrArray *pages; + gint n_pages; + + MimeInfo *partinfo; + + gdouble line_h; + gint lines_per_page; + + gboolean all_headers; +} PrintData; + +static GtkPrintSettings *settings = NULL; +static GtkPageSetup *page_setup = NULL; + +static gint get_header_data(MsgPrintInfo *mpinfo, PrintData *print_data) +{ + MsgInfo *msginfo; + FILE *fp; + GPtrArray *headers; + GString *str; + gint i; + + msginfo = mpinfo->msginfo; + + if ((fp = procmsg_open_message(msginfo)) == NULL) + return -1; + + if (print_data->all_headers) + headers = procheader_get_header_array_asis(fp, NULL); + else + headers = procheader_get_header_array_for_display(fp, NULL); + + fclose(fp); + + str = g_string_new(NULL); + + for (i = 0; i < headers->len; i++) { + Header *hdr; + gchar *text; + const gchar *body; + + hdr = g_ptr_array_index(headers, i); + + if (!g_ascii_strcasecmp(hdr->name, "Subject")) + body = msginfo->subject; + else if (!g_ascii_strcasecmp(hdr->name, "From")) + body = msginfo->from; + else if (!g_ascii_strcasecmp(hdr->name, "To")) + body = msginfo->to; + else if (!g_ascii_strcasecmp(hdr->name, "Cc")) { + unfold_line(hdr->body); + body = hdr->body; + while (g_ascii_isspace(*body)) + body++; + } else { + body = hdr->body; + while (g_ascii_isspace(*body)) + body++; + } + + if (body && *body != '\0') + text = g_markup_printf_escaped("%s: %s\n", + hdr->name, body); + else + text = g_markup_printf_escaped("%s: (none)\n", + hdr->name); + g_string_append(str, text); + g_free(text); + } + + /* dummy character for correct separation */ + g_string_append_c(str, ' '); + + mpinfo->hdr_data = g_string_free(str, FALSE); + procheader_header_array_destroy(headers); + + return 0; +} + +static gint layout_set_headers(PangoLayout *layout, MsgPrintInfo *mpinfo, + PrintData *print_data) +{ + PangoFontDescription *desc; + gint size; + + g_return_val_if_fail(mpinfo->hdr_data != NULL, -1); + + desc = pango_font_description_from_string(prefs_common_get()->textfont); + size = pango_font_description_get_size(desc); + pango_font_description_free(desc); + desc = gtkut_get_default_font_desc(); + debug_print("layout_set_headers: using font %s (style %d, %g pt)\n", pango_font_description_get_family(desc), pango_font_description_get_style(desc), (gdouble)size / PANGO_SCALE); + pango_font_description_set_size(desc, size); + pango_layout_set_font_description(layout, desc); + pango_font_description_free(desc); + + pango_layout_set_markup(layout, mpinfo->hdr_data, -1); + + return 0; +} + +static gint message_count_page(MsgPrintInfo *mpinfo, GtkPrintContext *context, + PrintData *print_data) +{ + cairo_t *cr; + gdouble width, height, line_h, hdr_h = 0.0, body_h; + gdouble dpi_x, dpi_y; + PangoLayout *layout; + PangoFontDescription *desc; + gint layout_h; + gint lines_per_page, lines_left; + gint n_pages = 1; + PageInfo *pinfo; + gint i; + FILE *fp = NULL; + gchar buf[BUFFSIZE]; + glong pos = 0; + + cr = gtk_print_context_get_cairo_context(context); + width = gtk_print_context_get_width(context); + height = gtk_print_context_get_height(context); + dpi_x = gtk_print_context_get_dpi_x(context); + dpi_y = gtk_print_context_get_dpi_y(context); + + layout = gtk_print_context_create_pango_layout(context); + pango_layout_set_width(layout, width * PANGO_SCALE); + pango_layout_set_wrap(layout, PANGO_WRAP_WORD_CHAR); + pango_layout_set_spacing(layout, (SPACING / 72 * dpi_y) * PANGO_SCALE); + + if (!print_data->partinfo) { + if (get_header_data(mpinfo, print_data) < 0) { + g_object_unref(layout); + return 0; + } + layout_set_headers(layout, mpinfo, print_data); + + pango_layout_get_size(layout, NULL, &layout_h); + hdr_h = (gdouble)layout_h / PANGO_SCALE; + } + + pango_layout_set_attributes(layout, NULL); + desc = pango_font_description_from_string(prefs_common_get()->textfont); + pango_layout_set_font_description(layout, desc); + pango_font_description_free(desc); + + pango_layout_set_text(layout, "Test", -1); + pango_layout_get_size(layout, NULL, &layout_h); + if (layout_h <= 0) { + g_warning("invalid layout_h (%d) ! falling back to default height (%d)\n", layout_h, 12 * PANGO_SCALE); + layout_h = 12 * PANGO_SCALE; + } + line_h = (gdouble)layout_h / PANGO_SCALE + (SPACING / 72 * dpi_y); + print_data->line_h = line_h; + lines_per_page = (height - line_h) / line_h; + print_data->lines_per_page = lines_per_page; + body_h = height - hdr_h - line_h; + if (body_h < 0) + body_h = 0.0; + lines_left = body_h / line_h; + + debug_print("width = %g, height = %g\n", width, height); + debug_print("dpi_x = %g, dpi_y = %g\n", dpi_x, dpi_y); + debug_print("layout_h = %d, line_h = %g, lines_per_page = %d\n", layout_h, line_h, lines_per_page); + debug_print("hdr_h = %g, body_h = %g, lines_left = %d\n", hdr_h, body_h, lines_left); + + if (print_data->partinfo) { + FILE *msgfp; + + if ((msgfp = procmsg_open_message(mpinfo->msginfo)) == NULL) + return -1; + fp = procmime_get_text_content(print_data->partinfo, msgfp, + NULL); + fclose(msgfp); + } else { + mpinfo->msg_text_file = get_tmp_file(); + if (procmsg_save_message_as_text(mpinfo->msginfo, mpinfo->msg_text_file, NULL, print_data->all_headers) == 0) { + if ((fp = g_fopen(mpinfo->msg_text_file, "rb")) != NULL) { + while (fgets(buf, sizeof(buf), fp) != NULL) + if (buf[0] == '\r' || buf[0] == '\n') + break; + } + } + } + if (!fp) { + g_warning("Can't get text part\n"); + return -1; + } + + i = 0; + pos = ftell(fp); + pinfo = g_new(PageInfo, 1); + pinfo->page_nr_per_msg = 0; + pinfo->mpinfo = mpinfo; + pinfo->pos = pos; + pinfo->lines = lines_left; + g_ptr_array_add(print_data->pages, pinfo); + + while (fgets(buf, sizeof(buf), fp) != NULL) { + gint lines; + gint line_offset = 0; + + strretchomp(buf); + pango_layout_set_text(layout, buf, -1); + lines = pango_layout_get_line_count(layout); + + while (lines_left < lines) { + PangoLayoutLine *line; + + debug_print("page increment: %d: lines_left = %d, lines = %d\n", i, lines_left, lines); + line_offset += lines_left; + line = pango_layout_get_line(layout, line_offset); + lines -= lines_left; + lines_left = lines_per_page; + + pinfo = g_new(PageInfo, 1); + pinfo->page_nr_per_msg = ++i; + pinfo->mpinfo = mpinfo; + pinfo->pos = pos + line->start_index; + pinfo->lines = lines_left; + g_ptr_array_add(print_data->pages, pinfo); + } + + lines_left -= lines; + pos = ftell(fp); + } + + rewind(fp); + mpinfo->fp = fp; + + g_object_unref(layout); + + n_pages = i + 1; + debug_print("n_pages = %d\n", n_pages); + + return n_pages; +} + +static void begin_print(GtkPrintOperation *operation, GtkPrintContext *context, + gpointer data) +{ + PrintData *print_data = data; + gint n_pages = 0; + gint i; + + debug_print("begin_print\n"); + + for (i = 0; i < print_data->n_msgs; i++) { + n_pages += message_count_page(&print_data->msgs[i], context, + print_data); + } + + print_data->n_pages = n_pages; + gtk_print_operation_set_n_pages(operation, n_pages); +} + +static void draw_page(GtkPrintOperation *operation, GtkPrintContext *context, + gint page_nr, gpointer data) +{ + PrintData *print_data = data; + PageInfo *pinfo; + MsgPrintInfo *mpinfo; + MsgInfo *msginfo; + cairo_t *cr; + PangoLayout *layout; + gdouble width, height; + gdouble dpi; + gint layout_h; + PangoFontDescription *desc; + gint font_size; + gchar buf[BUFFSIZE]; + gint count = 0; + + if (page_nr >= print_data->n_pages) + return; + + pinfo = g_ptr_array_index(print_data->pages, page_nr); + mpinfo = pinfo->mpinfo; + msginfo = mpinfo->msginfo; + + debug_print("draw_page: %d (%d), pos = %ld\n", page_nr, pinfo->page_nr_per_msg, pinfo->pos); + debug_print("msg pages = %d\n", mpinfo->n_pages); + + cr = gtk_print_context_get_cairo_context(context); + width = gtk_print_context_get_width(context); + height = gtk_print_context_get_height(context); + dpi = gtk_print_context_get_dpi_y(context); + +#if 0 + cairo_set_source_rgb(cr, 0.8, 0.8, 0.8); + cairo_rectangle(cr, 0, 0, width, font_size * 5); + cairo_fill(cr); +#endif + + cairo_set_source_rgb(cr, 0, 0, 0); + cairo_move_to(cr, 0, 0); + + layout = gtk_print_context_create_pango_layout(context); + pango_layout_set_width(layout, width * PANGO_SCALE); + pango_layout_set_wrap(layout, PANGO_WRAP_WORD_CHAR); + pango_layout_set_spacing(layout, (SPACING / 72 * dpi) * PANGO_SCALE); + + if (pinfo->page_nr_per_msg == 0 && mpinfo->hdr_data) { + if (layout_set_headers(layout, mpinfo, print_data) < 0) { + g_object_unref(layout); + return; + } + + pango_cairo_show_layout(cr, layout); + pango_layout_get_size(layout, NULL, &layout_h); + cairo_rel_move_to(cr, 0, (double)layout_h / PANGO_SCALE); + } + + pango_layout_set_attributes(layout, NULL); + desc = pango_font_description_from_string(prefs_common_get()->textfont); + font_size = pango_font_description_get_size(desc); + debug_print("draw_page: using font %s (style %d, %g pt)\n", pango_font_description_get_family(desc), pango_font_description_get_style(desc), (gdouble)font_size / PANGO_SCALE); + pango_font_description_set_size(desc, font_size); + pango_layout_set_font_description(layout, desc); + pango_font_description_free(desc); + + if (fseek(mpinfo->fp, pinfo->pos, SEEK_SET) < 0) { + FILE_OP_ERROR("draw_page", "fseek"); + g_object_unref(layout); + return; + } + + while (count < pinfo->lines) { + gint lines; + gint i; + PangoLayoutIter *iter; + PangoLayoutLine *layout_line; + gint baseline; + gdouble x, y; + + if (fgets(buf, sizeof(buf), mpinfo->fp) == NULL) + break; + strretchomp(buf); + if (buf[0] == '\0') { + /* dummy character for correct empty line height */ + buf[0] = ' '; + buf[1] = '\0'; + } + + pango_layout_set_text(layout, buf, -1); + lines = pango_layout_get_line_count(layout); + iter = pango_layout_get_iter(layout); + cairo_get_current_point(cr, &x, &y); + + for (i = 0; i < lines; i++) { + layout_line = pango_layout_iter_get_line(iter); + baseline = pango_layout_iter_get_baseline(iter); + cairo_move_to(cr, 0, + y + (gdouble)baseline / PANGO_SCALE); + pango_cairo_show_layout_line(cr, layout_line); + count++; + if (count >= pinfo->lines && i + 1 < lines) + break; + pango_layout_iter_next_line(iter); + } + + pango_layout_iter_free(iter); + + pango_layout_get_size(layout, NULL, &layout_h); + cairo_move_to(cr, 0, + y + (gdouble)layout_h / PANGO_SCALE + (SPACING / 72 * dpi)); + } + debug_print("count = %d\n", count); + + desc = gtkut_get_default_font_desc(); + pango_font_description_set_size(desc, font_size); + pango_layout_set_font_description(layout, desc); + pango_font_description_free(desc); + g_snprintf(buf, sizeof(buf), "- %d -", pinfo->page_nr_per_msg + 1); + pango_layout_set_text(layout, buf, -1); + pango_layout_set_alignment(layout, PANGO_ALIGN_CENTER); + cairo_move_to(cr, 0, height - (gdouble)font_size / PANGO_SCALE / 72 * dpi); + pango_cairo_show_layout(cr, layout); + + g_object_unref(layout); +} + +static gboolean printing_load_settings(void) +{ +#if GTK_CHECK_VERSION(2, 12, 0) + gchar *file; + GtkPrintSettings *new_settings; + + debug_print("printing_load_settings: load settings\n"); + + file = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, "printingrc", NULL); + if (!is_file_exist(file)) { + debug_print("printing_load_settings: %s not exist\n", file); + g_free(file); + return FALSE; + } + + if ((new_settings = gtk_print_settings_new_from_file(file, NULL)) == NULL) { + g_warning("printing_load_settings: gtk_print_settings_new_from_file: %s: error", file); + g_free(file); + return FALSE; + } + g_free(file); + + if (settings) + g_object_unref(settings); + settings = new_settings; + + return TRUE; +#else + return FALSE; +#endif /* GTK_CHECK_VERSION(2, 12, 0) */ +} + +static gboolean printing_save_settings(void) +{ +#if GTK_CHECK_VERSION(2, 12, 0) + gchar *file; + gboolean ret = TRUE; + + g_return_val_if_fail(settings != NULL, FALSE); + + debug_print("printing_save_settings: save settings\n"); + + file = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, "printingrc", NULL); + if (gtk_print_settings_to_file(settings, file, NULL) != TRUE) { + g_warning("printing_save_settings: gtk_print_settings_to_file: %s: error", file); + ret = FALSE; + } + g_free(file); + + return ret; +#else + return FALSE; +#endif /* GTK_CHECK_VERSION(2, 12, 0) */ +} + +static gboolean printing_load_page_setup(void) +{ +#if GTK_CHECK_VERSION(2, 12, 0) + gchar *file; + GtkPageSetup *new_setup; + + debug_print("printing_load_page_setup: load page setup\n"); + + file = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, "pagesetuprc", NULL); + if (!is_file_exist(file)) { + debug_print("printing_load_page_setup: %s not exist\n", file); + g_free(file); + return FALSE; + } + + if ((new_setup = gtk_page_setup_new_from_file(file, NULL)) == NULL) { + g_warning("printing_load_page_setup: gtk_page_setup_new_from_file: %s: error", file); + g_free(file); + return FALSE; + } + g_free(file); + + if (page_setup) + g_object_unref(page_setup); + page_setup = new_setup; + + return TRUE; +#else + return FALSE; +#endif /* GTK_CHECK_VERSION(2, 12, 0) */ +} + +static gboolean printing_save_page_setup(void) +{ +#if GTK_CHECK_VERSION(2, 12, 0) + gchar *file; + gboolean ret = TRUE; + + g_return_val_if_fail(page_setup != NULL, FALSE); + + debug_print("printing_save_page_setup: save page setup\n"); + + file = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, "pagesetuprc", NULL); + if (gtk_page_setup_to_file(page_setup, file, NULL) != TRUE) { + g_warning("printing_save_page_setup: gtk_page_setup_to_file: %s: error", file); + ret = FALSE; + } + g_free(file); + + return ret; +#else + return FALSE; +#endif /* GTK_CHECK_VERSION(2, 12, 0) */ +} + +gint printing_print_messages_gtk(GSList *mlist, MimeInfo *partinfo, + gboolean all_headers) +{ + GtkPrintOperation *op; + GtkPrintOperationResult res; + PrintData *print_data; + GSList *cur; + gchar *prev_dir; + gint i; + + g_return_val_if_fail(mlist != NULL, -1); + + debug_print("printing start\n"); + + prev_dir = g_get_current_dir(); + change_dir(get_document_dir()); + + print_data = g_new0(PrintData, 1); + print_data->mlist = mlist; + print_data->n_msgs = g_slist_length(mlist); + print_data->msgs = g_new0(MsgPrintInfo, print_data->n_msgs); + for (i = 0, cur = mlist; cur != NULL; i++, cur = cur->next) { + print_data->msgs[i].msginfo = (MsgInfo *)cur->data; + } + print_data->pages = g_ptr_array_new(); + print_data->n_pages = 0; + print_data->partinfo = partinfo; + print_data->line_h = 0.0; + print_data->all_headers = all_headers; + + op = gtk_print_operation_new(); + + /* GTK_UNIT_POINTS introduces text corruption bug, so use pixel unit */ + gtk_print_operation_set_unit(op, GTK_UNIT_PIXEL); + + g_signal_connect(op, "begin-print", G_CALLBACK(begin_print), + print_data); + g_signal_connect(op, "draw-page", G_CALLBACK(draw_page), print_data); + + if (!settings) + printing_load_settings(); + if (settings) + gtk_print_operation_set_print_settings(op, settings); + if (!page_setup) + printing_load_page_setup(); + if (page_setup) + gtk_print_operation_set_default_page_setup(op, page_setup); + + res = gtk_print_operation_run + (op, GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG, + GTK_WINDOW(main_window_get()->window), NULL); + + if (res == GTK_PRINT_OPERATION_RESULT_APPLY) { + if (settings) + g_object_unref(settings); + settings = g_object_ref(gtk_print_operation_get_print_settings(op)); + printing_save_settings(); + } + + g_object_unref(op); + for (i = 0; i < print_data->pages->len; i++) { + PageInfo *pinfo; + pinfo = g_ptr_array_index(print_data->pages, i); + g_free(pinfo); + } + g_ptr_array_free(print_data->pages, TRUE); + for (i = 0; i < print_data->n_msgs; i++) { + g_free(print_data->msgs[i].hdr_data); + if (print_data->msgs[i].fp) + fclose(print_data->msgs[i].fp); + if (print_data->msgs[i].msg_text_file) { + g_unlink(print_data->msgs[i].msg_text_file); + g_free(print_data->msgs[i].msg_text_file); + } + } + g_free(print_data); + + change_dir(prev_dir); + g_free(prev_dir); + + debug_print("printing finished\n"); + + return 0; +} + +void printing_page_setup_gtk(void) +{ + GtkPageSetup *new_page_setup; + + if (!settings) + printing_load_settings(); + if (!settings) + settings = gtk_print_settings_new(); + if (!page_setup) + printing_load_page_setup(); + + new_page_setup = gtk_print_run_page_setup_dialog + (GTK_WINDOW(main_window_get()->window), page_setup, settings); + + if (page_setup) + g_object_unref(page_setup); + page_setup = new_page_setup; + + printing_save_page_setup(); +} + +#endif /* GTK_CHECK_VERSION(2, 10, 0) */ + +static gint check_command_line(const gchar *cmdline) +{ + gchar *msg; + + msg = g_strconcat + (_("The message will be printed with the following command:"), + "\n\n", cmdline ? cmdline : _("(Default print command)"), + NULL); + if (alertpanel(_("Print"), msg, GTK_STOCK_OK, GTK_STOCK_CANCEL, NULL) + != G_ALERTDEFAULT) { + g_free(msg); + return -2; + } + g_free(msg); + + if (cmdline && str_find_format_times(cmdline, 's') != 1) { + alertpanel_error(_("Print command line is invalid:\n`%s'"), + cmdline); + return -1; + } + + return 0; +} + +gint printing_print_messages_with_command(GSList *mlist, gboolean all_headers, + const gchar *cmdline) +{ + MsgInfo *msginfo; + GSList *cur; + + g_return_val_if_fail(mlist != NULL, -1); + + if (check_command_line(cmdline) < 0) + return -1; + + for (cur = mlist; cur != NULL; cur = cur->next) { + msginfo = (MsgInfo *)cur->data; + if (msginfo) + procmsg_print_message(msginfo, cmdline, all_headers); + } + + return 0; +} + +gint printing_print_messages(GSList *mlist, gboolean all_headers) +{ +#if GTK_CHECK_VERSION(2, 10, 0) + if (!prefs_common.use_print_cmd) + return printing_print_messages_gtk(mlist, NULL, all_headers); + else +#endif /* GTK_CHECK_VERSION(2, 10, 0) */ + return printing_print_messages_with_command + (mlist, all_headers, prefs_common.print_cmd); +} + +gint printing_print_message(MsgInfo *msginfo, gboolean all_headers) +{ + GSList mlist; + + mlist.data = msginfo; + mlist.next = NULL; + return printing_print_messages(&mlist, all_headers); +} + +gint printing_print_message_part(MsgInfo *msginfo, MimeInfo *partinfo) +{ +#if GTK_CHECK_VERSION(2, 10, 0) + if (!prefs_common.use_print_cmd) { + GSList mlist; + + mlist.data = msginfo; + mlist.next = NULL; + return printing_print_messages_gtk(&mlist, partinfo, FALSE); + } +#endif /* GTK_CHECK_VERSION(2, 10, 0) */ + if (check_command_line(prefs_common.print_cmd) < 0) + return -1; + procmsg_print_message_part(msginfo, partinfo, prefs_common.print_cmd, + FALSE); + return 0; +} -- cgit v1.3