[balsa] Printing of HTML message parts
- From: Peter Bloomfield <peterb src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [balsa] Printing of HTML message parts
- Date: Fri, 15 Feb 2019 22:49:42 +0000 (UTC)
commit b33ac92f890f0cbfb1844865b9ac493b1240545e
Author: Albrecht Dreß <albrecht dress arcor de>
Date: Fri Feb 15 17:17:49 2019 -0500
Printing of HTML message parts
* libbalsa/html.[ch]: implement function for rendering to a
Cairo surface; refactor for extracting common code
* src/balsa-print-object-html.[ch]: implement printing the HTML
Cairo surface (new module)
* src/Makefile.am, src/meson.build: add new module
* src/balsa-print-object.c: use the new module for HTML (falls
back to default if built w/o HTML support)
* src/balsa-print-object.h: add the “volatile” HTML printing
options to BalsaPrintSetup
* src/print-gtk.c: refactor print dialogue to use GtkGrid;
add new options; add function for selecting the proper part
from multipart/alternative
* src/balsa-print-object-html.[ch]: new files
Signed-off-by: Peter Bloomfield <PeterBloomfield bellsouth net>
ChangeLog | 18 ++++
libbalsa/html.c | 239 ++++++++++++++++++++++++++++++------------
libbalsa/html.h | 6 +-
src/Makefile.am | 2 +
src/balsa-print-object.c | 5 +-
src/balsa-print-object.h | 9 +-
src/meson.build | 2 +
src/print-gtk.c | 268 +++++++++++++++++++++++++++--------------------
8 files changed, 366 insertions(+), 183 deletions(-)
---
diff --git a/ChangeLog b/ChangeLog
index 509b4fbbb..70a21926e 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,21 @@
+2018-02-15 Albrecht Dreß <albrecht dress arcor de>
+
+ Printing of HTML message parts
+
+ * libbalsa/html.[ch]: implement function for rendering to a
+ Cairo surface; refactor for extracting common code
+ * src/balsa-print-object-html.[ch]: implement printing the HTML
+ Cairo surface (new module)
+ * src/Makefile.am, src/meson.build: add new module
+ * src/balsa-print-object.c: use the new module for HTML (falls
+ back to default if built w/o HTML support)
+ * src/balsa-print-object.h: add the “volatile” HTML printing
+ options to BalsaPrintSetup
+ * src/print-gtk.c: refactor print dialogue to use GtkGrid;
+ add new options; add function for selecting the proper part
+ from multipart/alternative
+ * src/balsa-print-object-html.[ch]: new files
+
2019-02-15 Peter Bloomfield <pbloomfield bellsouth net>
* src/mailbox-node.c (balsa_mailbox_node_dispose): now that we
diff --git a/libbalsa/html.c b/libbalsa/html.c
index f4a2f2ef6..ea50bc453 100644
--- a/libbalsa/html.c
+++ b/libbalsa/html.c
@@ -1,7 +1,7 @@
/* -*-mode:c; c-style:k&r; c-basic-offset:4; -*- */
/* Balsa E-Mail Client
*
- * Copyright (C) 1997-2016 Stuart Parmenter and others,
+ * Copyright (C) 1997-2019 Stuart Parmenter and others,
* See the file AUTHORS for a list.
*
* This program is free software; you can redistribute it and/or modify
@@ -42,6 +42,16 @@
#endif
#define G_LOG_DOMAIN "html"
+
+#define CID_REGEX "<[^>]*src\\s*=\\s*['\"]?\\s*cid:"
+#define SRC_REGEX "<[^>]*src\\s*=\\s*['\"]?\\s*[^c][^i][^d][^:]"
+
+/* approximate image resolution for printing */
+#define HTML_PRINT_DPI 200.0
+/* zoom level for printing */
+#define HTML_PRINT_ZOOM 2.0
+
+
/*
* lbh_get_body_content
*
@@ -150,6 +160,10 @@ typedef struct {
LibBalsaHtmlSearchCallback search_cb;
gpointer search_cb_data;
gchar * search_text;
+ /* stuff used for printing only */
+ gboolean webprocess_error;
+ cairo_surface_t *surface;
+ volatile gint screenshot_done;
} LibBalsaWebKitInfo;
#define LIBBALSA_HTML_INFO "libbalsa-webkit2-info"
@@ -190,6 +204,8 @@ lbh_get_body_content_utf8(LibBalsaMessageBody * body,
/*
* GDestroyNotify func
+ *
+ * Note: do *not* destroy the surface in this function!
*/
static void
lbh_webkit_info_free(LibBalsaWebKitInfo * info)
@@ -480,6 +496,7 @@ lbh_web_process_terminated_cb(WebKitWebView *web_view,
WebKitWebProcessTerminationReason reason,
gpointer user_data)
{
+ LibBalsaWebKitInfo *info = (LibBalsaWebKitInfo *) user_data;
const gchar *reason_str;
switch (reason) {
@@ -494,16 +511,19 @@ lbh_web_process_terminated_cb(WebKitWebView *web_view,
break;
}
g_warning("webkit process terminated abnormally: %s", reason_str);
+ info->webprocess_error = TRUE;
}
#else
/*
* Callback for the "web-process-crashed" signal
*/
static gboolean
-lbh_web_process_crashed_cb(WebKitWebView * web_view,
- gpointer data)
+lbh_web_process_crashed_cb(WebKitWebView *web_view,
+ gpointer data)
{
+ LibBalsaWebKitInfo *info = (LibBalsaWebKitInfo *) data;
g_debug("%s", __func__);
+ info->webprocess_error = TRUE;
return FALSE;
}
#endif
@@ -568,6 +588,147 @@ lbh_context_menu_cb(WebKitWebView * web_view,
return retval;
}
+static WebKitWebView *
+lbh_web_view_new(LibBalsaWebKitInfo *info,
+ gint width,
+ gint height,
+ gboolean auto_load_images)
+{
+ WebKitWebView *view;
+ WebKitSettings *settings;
+ static guint have_registered_cid = 0U;
+
+ view = WEBKIT_WEB_VIEW(webkit_web_view_new());
+ g_object_set_data_full(G_OBJECT(view), LIBBALSA_HTML_INFO, info, (GDestroyNotify) lbh_webkit_info_free);
+ gtk_widget_set_size_request(GTK_WIDGET(view), width, height);
+
+ settings = webkit_web_view_get_settings(view);
+ webkit_settings_set_enable_plugins(settings, FALSE);
+ webkit_settings_set_enable_javascript(settings, FALSE);
+ webkit_settings_set_enable_java(settings, FALSE);
+ webkit_settings_set_enable_hyperlink_auditing(settings, TRUE);
+ webkit_settings_set_auto_load_images(settings, auto_load_images);
+
+ if (g_atomic_int_or(&have_registered_cid, 1U) == 0U) {
+ WebKitWebContext *context;
+ /* Apparently, WebKitWebContext is static, and does not like to
+ * have the scheme registered many times (13? 15?), after which
+ * the web process crashes and does not get respawned.
+ * We register it once with the address of a static pointer to
+ * LibBalsaWebKitInfo. */
+
+ context = webkit_web_view_get_context(view);
+ webkit_web_context_register_uri_scheme(context, "cid", lbh_cid_cb, &info, NULL);
+ g_debug("%s_ registered “cid:” scheme", __func__);
+ }
+
+#if WEBKIT_CHECK_VERSION(2,20,0)
+ g_signal_connect(view, "web-process-terminated", G_CALLBACK(lbh_web_process_terminated_cb), info);
+#else
+ g_signal_connect(view, "web-process-crashed", G_CALLBACK(lbh_web_process_crashed_cb), info);
+#endif
+ g_signal_connect(view, "decide-policy", G_CALLBACK(lbh_decide_policy_cb), info);
+ g_signal_connect(view, "resource-load-started", G_CALLBACK(lbh_resource_load_started_cb), info);
+
+ return view;
+}
+
+
+static void
+dump_snapshot(GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ LibBalsaWebKitInfo *info = (LibBalsaWebKitInfo *) user_data;
+ WebKitWebView *webview = WEBKIT_WEB_VIEW(source_object);
+ GError *error = NULL;
+
+ info->surface = webkit_web_view_get_snapshot_finish(webview, res, &error);
+ if (info->surface != NULL) {
+ g_debug("%s: html snapshot done, surface %p", __func__, info->surface);
+ cairo_surface_reference(info->surface);
+ } else {
+ g_warning("%s: error taking html snapshot: %s", __func__, error->message);
+ g_clear_error(&error);
+ }
+ g_atomic_int_inc(&info->screenshot_done);
+}
+
+
+/** \brief Render a HMTL part into a Cairo surface
+ *
+ * \param body HTML message body part
+ * \param width rendering width in Cairo units (1/72")
+ * \param load_external_images whether external images referenced by the HTML shall be loaded
+ * \return a cairo surface on success, or NULL on error
+ */
+cairo_surface_t *
+libbalsa_html_print_bitmap(LibBalsaMessageBody *body,
+ gdouble width,
+ gboolean load_external_images)
+{
+ gint render_width;
+ gchar *text;
+ gboolean have_src_cid;
+ gboolean have_src_oth;
+ gssize len;
+ GtkWidget *offline_window;
+ WebKitWebView *view;
+ LibBalsaWebKitInfo *info;
+ cairo_surface_t *html_surface = NULL;
+
+ g_return_val_if_fail(body != NULL, NULL);
+ len = lbh_get_body_content_utf8(body, &text);
+ if (len < 0) {
+ return NULL;
+ }
+
+ have_src_cid = g_regex_match_simple(CID_REGEX, text, G_REGEX_CASELESS, 0);
+ have_src_oth = g_regex_match_simple(SRC_REGEX, text, G_REGEX_CASELESS, 0);
+
+ info = g_new0(LibBalsaWebKitInfo, 1);
+ offline_window = gtk_offscreen_window_new();
+ render_width = (gint) (width * HTML_PRINT_DPI / 72.0);
+ g_debug("%s: request Cairo width %g, render width %d", __func__, width, render_width);
+ gtk_window_set_default_size(GTK_WINDOW(offline_window), render_width, 200);
+ view = lbh_web_view_new(info, render_width, 200, load_external_images || (have_src_cid &&
!have_src_oth));
+ webkit_web_view_set_zoom_level(view, HTML_PRINT_ZOOM); /* heuristic setting, any way
to calculate it? */
+ gtk_container_add(GTK_CONTAINER(offline_window), GTK_WIDGET(view));
+ gtk_widget_show_all(offline_window);
+
+ webkit_web_view_load_html(view, text, NULL);
+ g_free(text);
+
+ /* wait until the page is loaded */
+ while (webkit_web_view_is_loading(view)) {
+ gtk_main_iteration_do(FALSE);
+ g_usleep(100);
+ }
+
+ /* get the snapshot of the rendered html */
+ if (info->webprocess_error) {
+ g_warning("%s: web process terminated abnormally, cannot take snapshot", __func__);
+ } else {
+ g_debug("%s: html loaded, taking snapshot", __func__);
+ webkit_web_view_get_snapshot(view, WEBKIT_SNAPSHOT_REGION_FULL_DOCUMENT,
WEBKIT_SNAPSHOT_OPTIONS_NONE, NULL, dump_snapshot,
+ info);
+ while (g_atomic_int_get(&info->screenshot_done) == 0) {
+ gtk_main_iteration_do(FALSE);
+ g_usleep(100);
+ }
+ g_debug("%s: snapshot done, size %dx%d", __func__, cairo_image_surface_get_width(info->surface),
+ cairo_image_surface_get_height(info->surface));
+ html_surface = info->surface;
+ }
+
+ /* destroy the offscreen window */
+ gtk_widget_destroy(offline_window);
+
+ /* return the surface */
+ return html_surface;
+}
+
+
/* Create a new WebKitWebView widget:
* body LibBalsaMessageBody that belongs to the
* LibBalsaMessage from which to extract any
@@ -575,7 +736,6 @@ lbh_context_menu_cb(WebKitWebView * web_view,
* hover_cb callback for link-hover signal;
* clicked_cb callback for the "link-clicked" signal;
*/
-
GtkWidget *
libbalsa_html_new(LibBalsaMessageBody * body,
LibBalsaHtmlCallback hover_cb,
@@ -583,16 +743,8 @@ libbalsa_html_new(LibBalsaMessageBody * body,
{
gchar *text;
gssize len;
- GtkWidget *widget;
GtkWidget *vbox;
- WebKitWebView *web_view;
- static LibBalsaWebKitInfo *info;
- static gboolean have_registered_cid = FALSE;
- WebKitSettings *settings;
- static const gchar cid_regex[] =
- "<[^>]*src\\s*=\\s*['\"]?\\s*cid:";
- static const gchar src_regex[] =
- "<[^>]*src\\s*=\\s*['\"]?\\s*[^c][^i][^d][^:]";
+ LibBalsaWebKitInfo *info;
gboolean have_src_cid;
gboolean have_src_oth;
@@ -600,67 +752,24 @@ libbalsa_html_new(LibBalsaMessageBody * body,
if (len < 0)
return NULL;
- info = g_new(LibBalsaWebKitInfo, 1);
+ info = g_new0(LibBalsaWebKitInfo, 1);
info->body = body;
info->hover_cb = hover_cb;
info->clicked_cb = clicked_cb;
- info->info_bar = NULL;
- info->uri = NULL;
- info->search_text = NULL;
-
- widget = webkit_web_view_new();
- /* WebkitWebView is uncontrollably scrollable, so if we don't set a
- * minimum size it may be just a few pixels high. */
- gtk_widget_set_size_request(widget, -1, 200);
-
- info->web_view = web_view = WEBKIT_WEB_VIEW(widget);
- g_object_set_data_full(G_OBJECT(web_view), LIBBALSA_HTML_INFO, info,
- (GDestroyNotify) lbh_webkit_info_free);
-
- if (!have_registered_cid) {
- WebKitWebContext *context;
- /* Apparently, WebKitWebContext is static, and does not like to
- * have the scheme registered many times (13? 15?), after which
- * the web process crashes and does not get respawned.
- * We register it once with the address of a static pointer to
- * LibBalsaWebKitInfo. */
-
- context = webkit_web_view_get_context(web_view);
- webkit_web_context_register_uri_scheme(context, "cid", lbh_cid_cb,
- &info, NULL);
- have_registered_cid = TRUE;
- g_debug("%s registered cid: scheme", __func__);
- }
- have_src_cid = g_regex_match_simple(cid_regex, text, G_REGEX_CASELESS, 0);
- have_src_oth = g_regex_match_simple(src_regex, text, G_REGEX_CASELESS, 0);
+ have_src_cid = g_regex_match_simple(CID_REGEX, text, G_REGEX_CASELESS, 0);
+ have_src_oth = g_regex_match_simple(SRC_REGEX, text, G_REGEX_CASELESS, 0);
- settings = webkit_web_view_get_settings(web_view);
- webkit_settings_set_enable_plugins(settings, FALSE);
- webkit_settings_set_enable_javascript(settings, FALSE);
- webkit_settings_set_enable_java(settings, FALSE);
- webkit_settings_set_enable_hyperlink_auditing(settings, TRUE);
- webkit_settings_set_auto_load_images(settings, have_src_cid && !have_src_oth);
+ info->web_view = lbh_web_view_new(info, -1, 200, have_src_cid && !have_src_oth);
- g_signal_connect(web_view, "mouse-target-changed",
+ g_signal_connect(info->web_view, "mouse-target-changed",
G_CALLBACK(lbh_mouse_target_changed_cb), info);
- g_signal_connect(web_view, "decide-policy",
- G_CALLBACK(lbh_decide_policy_cb), info);
- g_signal_connect(web_view, "resource-load-started",
- G_CALLBACK(lbh_resource_load_started_cb), info);
-#if WEBKIT_CHECK_VERSION(2,20,0)
- g_signal_connect(web_view, "web-process-terminated",
- G_CALLBACK(lbh_web_process_terminated_cb), info);
-#else
- g_signal_connect(web_view, "web-process-crashed",
- G_CALLBACK(lbh_web_process_crashed_cb), info);
-#endif
- g_signal_connect(web_view, "context-menu",
+ g_signal_connect(info->web_view, "context-menu",
G_CALLBACK(lbh_context_menu_cb), info);
vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
- g_object_set_data(G_OBJECT(vbox), "libbalsa-html-web-view", web_view);
- gtk_box_pack_end(GTK_BOX(vbox), widget, TRUE, TRUE, 0);
+ g_object_set_data(G_OBJECT(vbox), "libbalsa-html-web-view", info->web_view);
+ gtk_box_pack_end(GTK_BOX(vbox), GTK_WIDGET(info->web_view), TRUE, TRUE, 0);
/* Simple check for possible resource requests: */
if (have_src_oth) {
@@ -669,7 +778,7 @@ libbalsa_html_new(LibBalsaMessageBody * body,
g_debug("%s shows info_bar", __func__);
}
- webkit_web_view_load_html(web_view, text, NULL);
+ webkit_web_view_load_html(info->web_view, text, NULL);
g_free(text);
return vbox;
diff --git a/libbalsa/html.h b/libbalsa/html.h
index 2a067a18f..fc987bd91 100644
--- a/libbalsa/html.h
+++ b/libbalsa/html.h
@@ -1,7 +1,7 @@
/* -*-mode:c; c-style:k&r; c-basic-offset:4; -*- */
/* Balsa E-Mail Client
*
- * Copyright (C) 1997-2016 Stuart Parmenter and others,
+ * Copyright (C) 1997-2019 Stuart Parmenter and others,
* See the file AUTHORS for a list.
*
* This program is free software; you can redistribute it and/or modify
@@ -72,6 +72,10 @@ GtkWidget *libbalsa_html_get_view_widget(GtkWidget * widget);
gboolean libbalsa_html_can_print(GtkWidget * widget);
void libbalsa_html_print(GtkWidget * widget);
+cairo_surface_t *libbalsa_html_print_bitmap(LibBalsaMessageBody *body,
+ gdouble
width,
+ gboolean
load_external_images)
+ G_GNUC_WARN_UNUSED_RESULT;
# endif /* HAVE_HTML_WIDGET */
diff --git a/src/Makefile.am b/src/Makefile.am
index 34830f3ee..55d855b30 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -78,6 +78,8 @@ balsa_print_source = print-gtk.c \
balsa-print-object-default.h \
balsa-print-object-header.c \
balsa-print-object-header.h \
+ balsa-print-object-html.c \
+ balsa-print-object-html.h \
balsa-print-object-image.c \
balsa-print-object-image.h \
balsa-print-object-text.c \
diff --git a/src/balsa-print-object.c b/src/balsa-print-object.c
index 4ad4bb2d6..853496513 100644
--- a/src/balsa-print-object.c
+++ b/src/balsa-print-object.c
@@ -1,6 +1,6 @@
/* -*-mode:c; c-style:k&r; c-basic-offset:4; -*- */
/* Balsa E-Mail Client
- * Copyright (C) 1997-2016 Stuart Parmenter and others
+ * Copyright (C) 1997-2019 Stuart Parmenter and others
* Written by (C) Albrecht Dreß <albrecht dress arcor de> 2007
*
* This program is free software; you can redistribute it and/or modify
@@ -32,6 +32,7 @@
#include "balsa-print-object-header.h"
#include "balsa-print-object-image.h"
#include "balsa-print-object-text.h"
+#include "balsa-print-object-html.h"
/* object related functions */
@@ -138,7 +139,7 @@ balsa_print_objects_append_from_body(GList * list,
GList * (*handler)(GList *, GtkPrintContext *, LibBalsaMessageBody *,
BalsaPrintSetup *);
} pr_handlers[] = {
- { "text/html", -1, balsa_print_object_default },
+ { "text/html", -1, balsa_print_object_html },
{ "text/enriched", -1, balsa_print_object_default },
{ "text/richtext", -1, balsa_print_object_default },
{ "text/x-vcard", -1, balsa_print_object_text_vcard },
diff --git a/src/balsa-print-object.h b/src/balsa-print-object.h
index 4216287a1..4e5fc97e7 100644
--- a/src/balsa-print-object.h
+++ b/src/balsa-print-object.h
@@ -1,7 +1,7 @@
/* -*-mode:c; c-style:k&r; c-basic-offset:4; -*- */
/* Balsa E-Mail Client
- * Copyright (C) 1997-2016 Stuart Parmenter and others
- * Written by (C) Albrecht Dre� <albrecht dress arcor de> 2007
+ * Copyright (C) 1997-2019 Stuart Parmenter and others
+ * Written by (C) Albrecht Dreß <albrecht dress arcor de> 2007
*
* 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
@@ -45,6 +45,11 @@ typedef struct {
gint page_count;
guint curr_depth;
+
+ /* note: the following two fields are relevant only if HTML support is enabled;
+ * don't hide them for code simplicity even if HTML support is disabled */
+ gboolean print_alt_html; /* print text/html in multipart/alternative */
+ gboolean html_load_images; /* load external images when printing text/html */
} BalsaPrintSetup;
diff --git a/src/meson.build b/src/meson.build
index 2f41d5947..2623d86bb 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -80,6 +80,8 @@ balsa_print_source = [
'balsa-print-object-default.h',
'balsa-print-object-header.c',
'balsa-print-object-header.h',
+ 'balsa-print-object-html.c',
+ 'balsa-print-object-html.h',
'balsa-print-object-image.c',
'balsa-print-object-image.h',
'balsa-print-object-text.c',
diff --git a/src/print-gtk.c b/src/print-gtk.c
index 373ff32a1..6b1e36b77 100644
--- a/src/print-gtk.c
+++ b/src/print-gtk.c
@@ -1,6 +1,6 @@
/* -*-mode:c; c-style:k&r; c-basic-offset:4; -*- */
/* Balsa E-Mail Client
- * Copyright (C) 1997-2016 Stuart Parmenter and others,
+ * Copyright (C) 1997-2019 Stuart Parmenter and others,
* See the file AUTHORS for a list.
*
* This program is free software; you can redistribute it and/or modify
@@ -47,9 +47,13 @@ typedef struct {
GtkWidget *margin_bottom;
GtkWidget *margin_left;
GtkWidget *margin_right;
+#ifdef HAVE_HTML_WIDGET
+ GtkWidget *html_print;
+ GtkWidget *html_load_imgs;
+ BalsaPrintSetup *setup;
+#endif
} BalsaPrintPrefs;
-
typedef struct {
/* related message */
LibBalsaMessage *message;
@@ -111,6 +115,71 @@ print_header_footer(GtkPrintContext * context, cairo_t * cairo_ctx,
}
+/*
+ * Scan the parts of a multipart/alternative as to find the proper one for printing. According to RFC 2046,
Sect. 5.1.4, the first
+ * /should/ be the "plain" one, and the following the "fancier" ones. This is not guaranteed, though.
+ *
+ * Furthermore, we may have cases like
+ * +- multipart/alternative
+ * +- text/plain
+ * +- multipart/mixed
+ * +- text/html
+ * +- ...
+ */
+static LibBalsaMessageBody *
+find_alt_part(LibBalsaMessageBody *parts,
+ gboolean print_alt_html)
+{
+ LibBalsaMessageBody *use_part;
+
+ /* scan the parts */
+ for (use_part = parts; use_part != NULL; use_part = use_part->next) {
+ gchar *mime_type;
+
+ mime_type = libbalsa_message_body_get_mime_type(use_part);
+ if ((g_ascii_strncasecmp(mime_type, "multipart/", 10U) == 0) && (use_part->parts != NULL)) {
+ /* consider the first child of a multipart */
+ g_free(mime_type);
+ mime_type = libbalsa_message_body_get_mime_type(use_part->parts);
+ }
+
+ if (((g_ascii_strcasecmp(mime_type, "text/plain") == 0) && !print_alt_html) ||
+ ((g_ascii_strcasecmp(mime_type, "text/html") == 0) && print_alt_html)) {
+ g_free(mime_type);
+ return use_part;
+ }
+ g_free(mime_type);
+ }
+
+ /* nothing found, fall back to the first part in the chain */
+ return parts;
+}
+
+
+static GList *
+print_single_part(GList *bpo_list,
+ GtkPrintContext *context,
+ BalsaPrintSetup *psetup,
+ LibBalsaMessageBody *body,
+ gboolean no_first_sep,
+ gboolean add_signature)
+{
+ if (!no_first_sep) {
+ bpo_list = balsa_print_object_separator(bpo_list, psetup);
+ }
+ if (add_signature) {
+#ifdef HAVE_GPGME
+ if (body->was_encrypted) {
+ bpo_list = balsa_print_object_frame_begin(bpo_list, _("Signed and encrypted matter"),
psetup);
+ } else {
+ bpo_list = balsa_print_object_frame_begin(bpo_list, _("Signed matter"), psetup);
+ }
+#endif /* HAVE_GPGME */
+ }
+ return balsa_print_objects_append_from_body(bpo_list, context, body, psetup);
+}
+
+
/*
* scan the body list and prepare print data according to the content type
*/
@@ -118,11 +187,10 @@ static GList *
scan_body(GList *bpo_list, GtkPrintContext * context, BalsaPrintSetup * psetup,
LibBalsaMessageBody * body, gboolean no_first_sep)
{
+ gboolean add_signature = FALSE;
#ifdef HAVE_GPGME
- gboolean add_signature;
gboolean have_crypto_frame;
#endif /* HAVE_GPGME */
-
while (body) {
gchar *conttype;
@@ -158,29 +226,20 @@ scan_body(GList *bpo_list, GtkPrintContext * context, BalsaPrintSetup * psetup,
#endif /* HAVE_GPGME */
if (g_ascii_strncasecmp(conttype, "multipart/", 10)) {
- if (no_first_sep)
+ bpo_list = print_single_part(bpo_list, context, psetup, body, no_first_sep, add_signature);
no_first_sep = FALSE;
- else
- bpo_list = balsa_print_object_separator(bpo_list, psetup);
-#ifdef HAVE_GPGME
- if (add_signature) {
- if (body->was_encrypted)
- bpo_list = balsa_print_object_frame_begin(bpo_list,
- _("Signed and encrypted matter"),
- psetup);
- else
- bpo_list = balsa_print_object_frame_begin(bpo_list,
- _("Signed matter"),
- psetup);
- }
-#endif /* HAVE_GPGME */
- bpo_list = balsa_print_objects_append_from_body(bpo_list, context,
- body, psetup);
}
if (body->parts) {
- bpo_list = scan_body(bpo_list, context, psetup, body->parts, no_first_sep);
- no_first_sep = FALSE;
+ if (g_ascii_strcasecmp(conttype, "multipart/alternative") == 0) {
+ LibBalsaMessageBody *print_part;
+
+ print_part = find_alt_part(body->parts, psetup->print_alt_html);
+ bpo_list = print_single_part(bpo_list, context, psetup, print_part, no_first_sep,
add_signature);
+ } else {
+ bpo_list = scan_body(bpo_list, context, psetup, body->parts, no_first_sep);
+ }
+ no_first_sep = FALSE;
}
/* end the frame for an embedded message or encrypted stuff */
@@ -417,12 +476,12 @@ add_font_button(const gchar * text, const gchar * font, GtkGrid * grid,
gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT);
gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
gtk_widget_set_halign(label, GTK_ALIGN_START);
- gtk_grid_attach(grid, label, 0, row, 1, 1);
+ gtk_grid_attach(grid, label, 1, row, 1, 1);
font_button = gtk_font_button_new_with_font(font);
gtk_label_set_mnemonic_widget(GTK_LABEL(label), font_button);
gtk_widget_set_hexpand(font_button, TRUE);
- gtk_grid_attach(grid, font_button, 1, row, 1, 1);
+ gtk_grid_attach(grid, font_button, 2, row, 1, 1);
return font_button;
}
@@ -440,7 +499,7 @@ add_margin_spinbtn(const gchar * text, gdouble min, gdouble max, gdouble dflt,
gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT);
gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
gtk_widget_set_halign(label, GTK_ALIGN_START);
- gtk_grid_attach(grid, label, 0, row, 1, 1);
+ gtk_grid_attach(grid, label, 1, row, 1, 1);
if (get_default_user_units() == GTK_UNIT_INCH) {
unit = _("inch");
@@ -459,13 +518,13 @@ add_margin_spinbtn(const gchar * text, gdouble min, gdouble max, gdouble dflt,
}
gtk_spin_button_set_numeric(GTK_SPIN_BUTTON(spinbtn), TRUE);
gtk_label_set_mnemonic_widget(GTK_LABEL(label), spinbtn);
- gtk_grid_attach(grid, spinbtn, 1, row, 1, 1);
+ gtk_grid_attach(grid, spinbtn, 2, row, 1, 1);
label = gtk_label_new(unit);
gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT);
gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
gtk_widget_set_halign(label, GTK_ALIGN_START);
- gtk_grid_attach(grid, label, 2, row, 1, 1);
+ gtk_grid_attach(grid, label, 3, row, 1, 1);
return spinbtn;
}
@@ -482,115 +541,89 @@ check_margins(GtkAdjustment * adjustment, GtkAdjustment * other)
}
static GtkWidget *
-message_prefs_widget(GtkPrintOperation * operation,
- BalsaPrintPrefs * print_prefs)
+create_options_group(const gchar *label_str, GtkWidget *parent_grid, gint parent_col, gint parent_row, gint
parent_width)
{
- GtkWidget *page;
- GtkWidget *group;
- GtkWidget *label;
- GtkWidget *hbox;
- GtkWidget *vbox;
+ GtkWidget *group;
+ GtkWidget *label;
GtkWidget *grid;
- GtkPageSetup *pg_setup;
gchar *markup;
- gtk_print_operation_set_custom_tab_label(operation, _("Message"));
-
- page = gtk_box_new(GTK_ORIENTATION_VERTICAL, 18);
- gtk_container_set_border_width(GTK_CONTAINER(page), 12);
-
group = gtk_box_new(GTK_ORIENTATION_VERTICAL, 12);
- gtk_box_pack_start(GTK_BOX(page), group, FALSE, TRUE, 0);
+ gtk_grid_attach(GTK_GRID(parent_grid), group, parent_col, parent_row, parent_width, 1);
label = gtk_label_new(NULL);
- markup = g_strdup_printf("<b>%s</b>", _("Fonts"));
+ markup = g_strdup_printf("<b>%s</b>", label_str);
gtk_label_set_markup(GTK_LABEL(label), markup);
g_free(markup);
gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
gtk_widget_set_halign(label, GTK_ALIGN_START);
gtk_box_pack_start(GTK_BOX(group), label, FALSE, FALSE, 0);
- hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
- gtk_box_pack_start(GTK_BOX(group), hbox, TRUE, TRUE, 0);
- gtk_box_pack_start(GTK_BOX(hbox), gtk_label_new(" "),
- FALSE, FALSE, 0);
- vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 6);
- gtk_box_pack_start(GTK_BOX(hbox), vbox, TRUE, TRUE, 0);
-
- grid = gtk_grid_new();
- gtk_grid_set_row_spacing(GTK_GRID(grid), 6);
- gtk_grid_set_column_spacing(GTK_GRID(grid), 6);
-
- gtk_box_pack_start(GTK_BOX(vbox), grid, FALSE, TRUE, 0);
-
- print_prefs->header_font =
- add_font_button(_("_Header Font:"), balsa_app.print_header_font,
- GTK_GRID(grid), 0);
- print_prefs->body_font =
- add_font_button(_("B_ody Font:"), balsa_app.print_body_font,
- GTK_GRID(grid), 1);
- print_prefs->footer_font =
- add_font_button(_("_Footer Font:"), balsa_app.print_footer_font,
- GTK_GRID(grid), 2);
+ grid = gtk_grid_new();
+ gtk_box_pack_start(GTK_BOX(group), grid, FALSE, FALSE, 0);
+ gtk_grid_set_column_spacing(GTK_GRID(grid), 0);
+ gtk_grid_set_row_spacing(GTK_GRID(grid), 6);
+ gtk_grid_attach(GTK_GRID(grid), gtk_label_new(" "), 0, 0, 1, 1);
+ return grid;
+}
- group = gtk_box_new(GTK_ORIENTATION_VERTICAL, 12);
- gtk_box_pack_start(GTK_BOX(page), group, FALSE, TRUE, 0);
+static GtkWidget *
+message_prefs_widget(GtkPrintOperation * operation,
+ BalsaPrintPrefs * print_prefs)
+{
+ GtkWidget *page;
+ GtkWidget *grid;
+#ifdef HAVE_HTML_WIDGET
+ GtkWidget *dummy;
+#endif
+ GtkPageSetup *pg_setup;
- label = gtk_label_new(NULL);
- markup = g_strdup_printf("<b>%s</b>", _("Highlighting"));
- gtk_label_set_markup(GTK_LABEL(label), markup);
- g_free(markup);
- gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
- gtk_widget_set_halign(label, GTK_ALIGN_START);
- gtk_box_pack_start(GTK_BOX(group), label, FALSE, FALSE, 0);
+ gtk_print_operation_set_custom_tab_label(operation, _("Message"));
+
+ page = gtk_grid_new();
+ gtk_grid_set_column_spacing(GTK_GRID(page), 18);
+ gtk_grid_set_row_spacing(GTK_GRID(page), 18);
+ gtk_container_set_border_width(GTK_CONTAINER(page), 12);
+
+ /* fonts */
+ grid = create_options_group(_("Fonts"), page, 0, 0, 3);
- hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
- gtk_box_pack_start(GTK_BOX(group), hbox, TRUE, TRUE, 0);
- gtk_box_pack_start(GTK_BOX(hbox), gtk_label_new(" "),
- FALSE, FALSE, 0);
- vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 6);
- gtk_box_pack_start(GTK_BOX(hbox), vbox, TRUE, TRUE, 0);
+ print_prefs->header_font = add_font_button(_("_Header Font:"), balsa_app.print_header_font,
GTK_GRID(grid), 0);
+ print_prefs->body_font = add_font_button(_("B_ody Font:"), balsa_app.print_body_font, GTK_GRID(grid), 1);
+ print_prefs->footer_font = add_font_button(_("_Footer Font:"), balsa_app.print_footer_font,
GTK_GRID(grid), 2);
+
+ /* syntax highlighting */
+ grid = create_options_group(_("Highlighting"), page, 0, 1, 1);
print_prefs->highlight_cited =
gtk_check_button_new_with_mnemonic(_("Highlight _cited text"));
- gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON
- (print_prefs->highlight_cited),
- balsa_app.print_highlight_cited);
- gtk_box_pack_start(GTK_BOX(vbox), print_prefs->highlight_cited, FALSE,
- TRUE, 0);
-
- print_prefs->highlight_phrases =
- gtk_check_button_new_with_mnemonic(_
- ("Highlight _structured phrases"));
- gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON
- (print_prefs->highlight_phrases),
- balsa_app.print_highlight_phrases);
- gtk_box_pack_start(GTK_BOX(vbox), print_prefs->highlight_phrases,
- FALSE, TRUE, 0);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(print_prefs->highlight_cited),
balsa_app.print_highlight_cited);
+ gtk_grid_attach(GTK_GRID(grid), print_prefs->highlight_cited, 1, 0, 1, 1);
- group = gtk_box_new(GTK_ORIENTATION_VERTICAL, 12);
- gtk_box_pack_start(GTK_BOX(page), group, FALSE, TRUE, 0);
+ print_prefs->highlight_phrases = gtk_check_button_new_with_mnemonic(_("Highlight _structured phrases"));
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(print_prefs->highlight_phrases),
balsa_app.print_highlight_phrases);
+ gtk_grid_attach(GTK_GRID(grid), print_prefs->highlight_phrases, 1, 1, 1, 1);
- label = gtk_label_new(NULL);
- markup = g_strdup_printf("<b>%s</b>", _("Margins"));
- gtk_label_set_markup(GTK_LABEL(label), markup);
- g_free(markup);
- gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
- gtk_widget_set_halign(label, GTK_ALIGN_START);
- gtk_box_pack_start(GTK_BOX(group), label, FALSE, FALSE, 0);
+#ifdef HAVE_HTML_WIDGET
+ /* treatment of HTML messages and parts */
+ grid = create_options_group(_("Highlighting"), page, 1, 1, 1);
- hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
- gtk_box_pack_start(GTK_BOX(group), hbox, TRUE, TRUE, 0);
- gtk_box_pack_start(GTK_BOX(hbox), gtk_label_new(" "),
- FALSE, FALSE, 0);
- vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 6);
- gtk_box_pack_start(GTK_BOX(hbox), vbox, TRUE, TRUE, 0);
+ print_prefs->html_print = gtk_check_button_new_with_mnemonic(_("Prefer text/plain over HTML"));
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(print_prefs->html_print), balsa_app.display_alt_plain);
+ gtk_grid_attach(GTK_GRID(grid), print_prefs->html_print, 1, 0, 1, 1);
- grid = gtk_grid_new();
- gtk_grid_set_row_spacing(GTK_GRID(grid), 6);
- gtk_grid_set_column_spacing(GTK_GRID(grid), 6);
+ print_prefs->html_load_imgs = gtk_check_button_new_with_mnemonic(_("Download images from remote servers
(may be dangerous)"));
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(print_prefs->html_load_imgs), FALSE);
+ gtk_grid_attach(GTK_GRID(grid), print_prefs->html_load_imgs, 1, 1, 1, 1);
- gtk_box_pack_start(GTK_BOX(vbox), grid, FALSE, TRUE, 0);
+ /* phantom alignment */
+ dummy = gtk_label_new(" ");
+ gtk_widget_set_hexpand(dummy, TRUE);
+ gtk_grid_attach(GTK_GRID(grid), dummy, 2, 1, 1, 1);
+#endif
+
+ /* margins */
+ grid = create_options_group(_("Margins"), page, 0, 2, 2);
pg_setup = gtk_print_operation_get_default_page_setup(operation);
print_prefs->margin_top =
@@ -671,6 +704,12 @@ message_prefs_apply(GtkPrintOperation * operation, GtkWidget * widget,
balsa_app.margin_left /= 25.4;
balsa_app.margin_right /= 25.4;
}
+#ifdef HAVE_HTML_WIDGET
+ print_prefs->setup->print_alt_html =
+ !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(print_prefs->html_print));
+ print_prefs->setup->html_load_images =
+ gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(print_prefs->html_load_imgs));
+#endif
}
@@ -721,6 +760,9 @@ message_print(LibBalsaMessage * msg, GtkWindow * parent)
/* create a print context */
print_data = g_new0(BalsaPrintData, 1);
print_data->message = msg;
+#ifdef HAVE_HTML_WIDGET
+ print_prefs.setup = &print_data->setup;
+#endif
g_signal_connect(print, "begin_print", G_CALLBACK(begin_print), print_data);
g_signal_connect(print, "draw_page", G_CALLBACK(draw_page), print_data);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]