/* * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client * Copyright (C) 1999-2006 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" #if GTK_CHECK_VERSION(2, 10, 0) #define SPACING 2.0 typedef struct { MsgInfo *msginfo; gint n_pages; gchar *hdr_data; 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; gdouble line_h; gint lines_per_page; gboolean all_headers; } PrintData; 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); } 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); g_print("size = %d (%g)\n", size, (gdouble)size / PANGO_SCALE); pango_font_description_free(desc); desc = gtkut_get_default_font_desc(); 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, body_h; PangoLayout *layout; PangoFontDescription *desc; gint layout_h; gint lines_per_page, lines_left; gint body_lines = 0; gint n_pages = 1; PageInfo *pinfo; gint i; FILE *fp; 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); 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 * PANGO_SCALE); 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); line_h = (gdouble)layout_h / PANGO_SCALE + SPACING; print_data->line_h = line_h; lines_per_page = height / line_h; print_data->lines_per_page = lines_per_page; body_h = height - hdr_h; if (body_h < 0) body_h = 0.0; lines_left = body_h / line_h; g_print("layout_h = %d, line_h = %g, lines_per_page = %d\n", layout_h, line_h, lines_per_page); g_print("hdr_h = %g, body_h = %g, lines_left = %d\n", hdr_h, body_h, lines_left); if ((fp = procmime_get_first_text_content(mpinfo->msginfo, NULL)) == NULL) { 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; g_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; g_print("n_pages = %d\n", n_pages); return n_pages; } static void begin_print(GtkPrintOperation *operation, GtkPrintContext *context, gpointer data) { PrintData *print_data = data; GSList *cur; MsgInfo *msginfo; 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; gint layout_h; PangoFontDescription *desc; gchar buf[BUFFSIZE]; gint count = 0; 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); #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 * PANGO_SCALE); if (pinfo->page_nr_per_msg == 0) { 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); 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; gint y0, y1; gdouble x, y; if (fgets(buf, sizeof(buf), mpinfo->fp) == NULL) break; strretchomp(buf); 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); } g_print("count = %d\n", count); desc = gtkut_get_default_font_desc(); 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); pango_cairo_show_layout(cr, layout); g_object_unref(layout); } gint printing_print_messages_gtk(GSList *mlist, gboolean all_headers) { static GtkPrintSettings *settings = NULL; GtkPrintOperation *op; GtkPrintOperationResult res; PrintData *print_data; GSList *cur; gint i; g_return_val_if_fail(mlist != NULL, -1); debug_print("printing start\n"); 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->line_h = 0.0; print_data->all_headers = all_headers; op = gtk_print_operation_new(); gtk_print_operation_set_unit(op, GTK_UNIT_POINTS); 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) gtk_print_operation_set_print_settings(op, settings); 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) { g_print("save settings\n"); if (settings) g_object_unref(settings); settings = g_object_ref (gtk_print_operation_get_print_settings(op)); } 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); } g_free(print_data); debug_print("printing finished\n"); return 0; } #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, 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 (check_command_line(prefs_common.print_cmd) < 0) return -1; procmsg_print_message_part(msginfo, partinfo, prefs_common.print_cmd, FALSE); return 0; }