[evolution/wip/webkit-composer: 806/966] Add support for inline (CID) images
- From: Tomas Popela <tpopela src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [evolution/wip/webkit-composer: 806/966] Add support for inline (CID) images
- Date: Wed, 23 Apr 2014 11:01:12 +0000 (UTC)
commit e628172b597bb2c4152a38ce73af27fce30b0085
Author: Tomas Popela <tpopela redhat com>
Date: Thu Feb 6 10:07:04 2014 +0100
Add support for inline (CID) images
With this change all the inline images inserted into composer
(emoticons as well) are upon their insertion encoded to base64 and the
outcome is set to src attribute of the HTML image element. When sending
the message for all these images the CamelMimeParts are created and
attached to it. When saving message as draft no changes are made. When
opening the message as new (and the message containes CID images) the
content from all the CamelMimeParts with images is encoded again to base64 and
saved. When the message is loaded the CID sources are replaced with
base64 data.The core functionality was moved from EMsgComposer to EEditorWidget
because right now it is too tight with DOM of the composer's web view.
composer/e-composer-private.c | 9 +-
composer/e-msg-composer.c | 137 +--------------
composer/e-msg-composer.h | 2 -
e-util/e-editor-selection.c | 86 ++++++---
e-util/e-editor-widget.c | 409 ++++++++++++++++++++++++++++++++++++++---
e-util/e-editor-widget.h | 7 +
6 files changed, 454 insertions(+), 196 deletions(-)
---
diff --git a/composer/e-composer-private.c b/composer/e-composer-private.c
index e563a2a..81dc1d3 100644
--- a/composer/e-composer-private.c
+++ b/composer/e-composer-private.c
@@ -381,8 +381,10 @@ e_composer_private_dispose (EMsgComposer *composer)
composer->priv->composer_actions = NULL;
}
- g_clear_object (&composer->priv->gallery_icon_view);
- g_clear_object (&composer->priv->gallery_scrolled_window);
+ if (composer->priv->gallery_scrolled_window != NULL) {
+ g_object_unref (composer->priv->gallery_scrolled_window);
+ composer->priv->gallery_scrolled_window = NULL;
+ }
if (composer->priv->redirect != NULL) {
g_object_unref (composer->priv->redirect);
@@ -406,9 +408,6 @@ e_composer_private_finalize (EMsgComposer *composer)
g_free (composer->priv->charset);
g_free (composer->priv->mime_type);
g_free (composer->priv->mime_body);
-
- g_hash_table_destroy (composer->priv->inline_images);
- g_hash_table_destroy (composer->priv->inline_images_by_url);
}
gchar *
diff --git a/composer/e-msg-composer.c b/composer/e-msg-composer.c
index 97844e3..c63a044 100644
--- a/composer/e-msg-composer.c
+++ b/composer/e-msg-composer.c
@@ -1339,11 +1339,12 @@ composer_build_message (EMsgComposer *composer,
gboolean pre_encode;
EEditor *editor;
EEditorWidget *editor_widget;
-
- clear_current_images (composer);
+ GList *inline_images;
editor = e_msg_composer_get_editor (composer);
editor_widget = e_editor_get_editor_widget (editor);
+ inline_images = e_editor_widget_get_parts_for_inline_images (editor_widget);
+
data = g_byte_array_new ();
text = e_editor_widget_get_text_html (editor_widget);
length = strlen (text);
@@ -2348,39 +2349,6 @@ msg_composer_command_after (EMsgComposer *composer,
gtkhtml_editor_run_command (editor, "text-default-color");
gtkhtml_editor_run_command (editor, "italic-off");
}
-
-static gchar *
-msg_composer_image_uri (EMsgComposer *composer,
- const gchar *uri)
-{
- GHashTable *hash_table;
- CamelMimePart *part;
- const gchar *cid;
-
- hash_table = composer->priv->inline_images_by_url;
- part = g_hash_table_lookup (hash_table, uri);
-
- if (part == NULL && g_str_has_prefix (uri, "file:"))
- part = e_msg_composer_add_inline_image_from_file (
- composer, uri + 5);
-
- if (part == NULL && g_str_has_prefix (uri, "cid:")) {
- hash_table = composer->priv->inline_images;
- part = g_hash_table_lookup (hash_table, uri);
- }
-
- if (part == NULL)
- return NULL;
-
- composer->priv->current_images =
- g_list_prepend (composer->priv->current_images, part);
-
- cid = camel_mime_part_get_content_id (part);
- if (cid == NULL)
- return NULL;
-
- return g_strconcat ("cid:", cid, NULL);
-}
#endif /* WEBKIT-COMPOSER */
static gboolean
@@ -4514,105 +4482,6 @@ e_msg_composer_attach (EMsgComposer *composer,
g_object_unref (attachment);
}
-/**
- * e_msg_composer_add_inline_image_from_file:
- * @composer: a composer object
- * @filename: the name of the file containing the image
- *
- * This reads in the image in @filename and adds it to @composer
- * as an inline image, to be wrapped in a multipart/related.
- *
- * Returns: the newly-created CamelMimePart (which must be reffed
- * if the caller wants to keep its own reference), or %NULL on error.
- **/
-CamelMimePart *
-e_msg_composer_add_inline_image_from_file (EMsgComposer *composer,
- const gchar *filename)
-{
- gchar *mime_type, *cid, *url, *name, *dec_file_name;
- CamelStream *stream;
- CamelDataWrapper *wrapper;
- CamelMimePart *part;
- EMsgComposerPrivate *p = composer->priv;
-
- dec_file_name = g_strdup (filename);
- camel_url_decode (dec_file_name);
-
- if (!g_file_test (dec_file_name, G_FILE_TEST_IS_REGULAR))
- return NULL;
-
- stream = camel_stream_fs_new_with_name (
- dec_file_name, O_RDONLY, 0, NULL);
- if (!stream)
- return NULL;
-
- wrapper = camel_data_wrapper_new ();
- camel_data_wrapper_construct_from_stream_sync (
- wrapper, stream, NULL, NULL);
- g_object_unref (CAMEL_OBJECT (stream));
-
- mime_type = e_util_guess_mime_type (dec_file_name, TRUE);
- if (mime_type == NULL)
- mime_type = g_strdup ("application/octet-stream");
- camel_data_wrapper_set_mime_type (wrapper, mime_type);
- g_free (mime_type);
-
- part = camel_mime_part_new ();
- camel_medium_set_content (CAMEL_MEDIUM (part), wrapper);
- g_object_unref (wrapper);
-
- cid = camel_header_msgid_generate ();
- camel_mime_part_set_content_id (part, cid);
- name = g_path_get_basename (dec_file_name);
- camel_mime_part_set_filename (part, name);
- g_free (name);
- camel_mime_part_set_encoding (part, CAMEL_TRANSFER_ENCODING_BASE64);
-
- url = g_strdup_printf ("file:%s", dec_file_name);
- g_hash_table_insert (p->inline_images_by_url, url, part);
-
- url = g_strdup_printf ("cid:%s", cid);
- g_hash_table_insert (p->inline_images, url, part);
- g_free (cid);
-
- g_free (dec_file_name);
-
- return part;
-}
-
-/**
- * e_msg_composer_add_inline_image_from_mime_part:
- * @composer: a composer object
- * @part: a CamelMimePart containing image data
- *
- * This adds the mime part @part to @composer as an inline image, to
- * be wrapped in a multipart/related.
- **/
-void
-e_msg_composer_add_inline_image_from_mime_part (EMsgComposer *composer,
- CamelMimePart *part)
-{
- gchar *url;
- const gchar *location, *cid;
- EMsgComposerPrivate *p = composer->priv;
-
- cid = camel_mime_part_get_content_id (part);
- if (!cid) {
- camel_mime_part_set_content_id (part, NULL);
- cid = camel_mime_part_get_content_id (part);
- }
-
- url = g_strdup_printf ("cid:%s", cid);
- g_hash_table_insert (p->inline_images, url, part);
- g_object_ref (part);
-
- location = camel_mime_part_get_content_location (part);
- if (location != NULL)
- g_hash_table_insert (
- p->inline_images_by_url,
- g_strdup (location), part);
-}
-
static void
composer_get_message_ready (EMsgComposer *composer,
GAsyncResult *result,
diff --git a/composer/e-msg-composer.h b/composer/e-msg-composer.h
index e4cd4e1..35864d5 100644
--- a/composer/e-msg-composer.h
+++ b/composer/e-msg-composer.h
@@ -192,8 +192,6 @@ void e_save_spell_languages (const GList *spell_languages);
void e_msg_composer_is_from_new_message
(EMsgComposer *composer,
gboolean is_from_new_message);
-
-
G_END_DECLS
#endif /* E_MSG_COMPOSER_H */
diff --git a/e-util/e-editor-selection.c b/e-util/e-editor-selection.c
index 123dc8f..8231133 100644
--- a/e-util/e-editor-selection.c
+++ b/e-util/e-editor-selection.c
@@ -3020,6 +3020,7 @@ struct _LoadContext {
goffset total_num_bytes;
gssize bytes_read;
const gchar *content_type;
+ const gchar *filename;
gchar buffer[4096];
};
@@ -3059,44 +3060,32 @@ image_load_context_free (LoadContext *load_context)
}
static void
-image_load_finish (LoadContext *load_context)
+insert_base64_image (EEditorSelection *selection,
+ const gchar *base64_content,
+ const gchar *filename)
{
- EEditorSelection *selection;
EEditorWidget *editor_widget;
WebKitDOMDocument *document;
- WebKitDOMElement *element;
- WebKitDOMElement *caret_position;
- GMemoryOutputStream *output_stream;
- gchar *base64_encoded;
- gchar *mime_type;
- gchar *output;
- gsize size;
- gpointer data;
+ WebKitDOMElement *element, *caret_position;
- output_stream = G_MEMORY_OUTPUT_STREAM (load_context->output_stream);
-
- selection = load_context->selection;
+ e_editor_selection_save_caret_position (selection);
editor_widget = e_editor_selection_ref_editor_widget (selection);
g_return_if_fail (editor_widget != NULL);
document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (editor_widget));
- g_object_unref (editor_widget);
-
- mime_type = g_content_type_get_mime_type (load_context->content_type);
- data = g_memory_output_stream_get_data (output_stream);
- size = g_memory_output_stream_get_data_size (output_stream);
-
- base64_encoded = g_base64_encode ((const guchar *) data, size);
- output = g_strconcat ("data:", mime_type, ";base64,", base64_encoded, NULL);
-
- e_editor_selection_save_caret_position (selection);
+ g_object_unref (editor_widget);
element = webkit_dom_document_create_element (document, "img", NULL);
webkit_dom_html_image_element_set_src (
WEBKIT_DOM_HTML_IMAGE_ELEMENT (element),
- output);
+ base64_content);
+ webkit_dom_element_set_attribute (
+ WEBKIT_DOM_ELEMENT (element), "data-inline", "", NULL);
+ webkit_dom_element_set_attribute (
+ WEBKIT_DOM_ELEMENT (element), "data-name",
+ filename ? filename : "", NULL);
caret_position = webkit_dom_document_get_element_by_id (
document, "-x-evo-caret-position");
@@ -3109,6 +3098,30 @@ image_load_finish (LoadContext *load_context)
e_editor_selection_restore_caret_position (selection);
+}
+
+static void
+image_load_finish (LoadContext *load_context)
+{
+ EEditorSelection *selection;
+ GMemoryOutputStream *output_stream;
+ gchar *base64_encoded, *mime_type, *output;
+ gsize size;
+ gpointer data;
+
+ output_stream = G_MEMORY_OUTPUT_STREAM (load_context->output_stream);
+
+ selection = load_context->selection;
+
+ mime_type = g_content_type_get_mime_type (load_context->content_type);
+
+ data = g_memory_output_stream_get_data (output_stream);
+ size = g_memory_output_stream_get_data_size (output_stream);
+
+ base64_encoded = g_base64_encode ((const guchar *) data, size);
+ output = g_strconcat ("data:", mime_type, ";base64,", base64_encoded, NULL);
+ insert_base64_image (selection, output, load_context->filename);
+
g_free (base64_encoded);
g_free (output);
g_free (mime_type);
@@ -3119,7 +3132,7 @@ image_load_finish (LoadContext *load_context)
static void
image_load_write_cb (GOutputStream *output_stream,
GAsyncResult *result,
- LoadContext *load_context)
+ LoadContext *load_context)
{
GInputStream *input_stream;
gssize bytes_written;
@@ -3242,6 +3255,7 @@ image_load_query_info_cb (GFile *file,
load_context->content_type = g_file_info_get_content_type (file_info);
load_context->total_num_bytes = g_file_info_get_size (file_info);
+ load_context->filename = g_file_info_get_name (file_info);
g_file_read_async (
file, G_PRIORITY_DEFAULT,
@@ -3301,8 +3315,26 @@ e_editor_selection_insert_image (EEditorSelection *selection,
g_return_if_fail (E_IS_EDITOR_SELECTION (selection));
g_return_if_fail (image_uri != NULL);
- if (is_in_html_mode (selection))
- image_load_and_insert_async (selection, image_uri);
+ if (is_in_html_mode (selection)) {
+ if (strstr (image_uri, ";base64,")) {
+ if (g_str_has_prefix (image_uri, "data:"))
+ insert_base64_image (selection, image_uri, "");
+ if (strstr (image_uri, ";data")) {
+ const gchar *base64_data = strstr (image_uri, ";") + 1;
+ gchar *filename;
+ glong filename_length;
+
+ filename_length =
+ g_utf8_strlen (image_uri, -1) -
+ g_utf8_strlen (base64_data, -1) - 1;
+ filename = g_strndup (image_uri, filename_length);
+
+ insert_base64_image (selection, base64_data, filename);
+ g_free (filename);
+ }
+ } else
+ image_load_and_insert_async (selection, image_uri);
+ }
}
/**
diff --git a/e-util/e-editor-widget.c b/e-util/e-editor-widget.c
index 8f03957..712bd78 100644
--- a/e-util/e-editor-widget.c
+++ b/e-util/e-editor-widget.c
@@ -71,6 +71,8 @@ struct _EEditorWidgetPrivate {
WebKitDOMElement *element_under_mouse;
+ GHashTable *inline_images;
+
GSettings *font_settings;
GSettings *aliasing_settings;
@@ -338,6 +340,47 @@ body_input_event_cb (WebKitDOMElement *element,
}
}
+static void
+change_cid_images_src_to_base64 (EEditorWidget *widget)
+{
+ gint ii, length;
+ WebKitDOMDocument *document;
+ WebKitDOMNodeList *list;
+
+ document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (widget));
+
+ list = webkit_dom_document_query_selector_all (document, "img[src^=\"cid:\"]", NULL);
+ length = webkit_dom_node_list_get_length (list);
+ for (ii = 0; ii < length; ii++) {
+ WebKitDOMNode *node = webkit_dom_node_list_item (list, ii);
+ gchar *cid_src;
+ const gchar *base64_src;
+
+ cid_src = webkit_dom_html_image_element_get_src (
+ WEBKIT_DOM_HTML_IMAGE_ELEMENT (node));
+
+ if ((base64_src = g_hash_table_lookup (widget->priv->inline_images, cid_src)) != NULL) {
+ const gchar *base64_data = strstr (base64_src, ";") + 1;
+ gchar *name;
+ glong name_length;
+
+ name_length =
+ g_utf8_strlen (base64_src, -1) -
+ g_utf8_strlen (base64_data, -1) - 1;
+ name = g_strndup (base64_src, name_length);
+
+ webkit_dom_element_set_attribute (
+ WEBKIT_DOM_ELEMENT (node), "data-inline", "", NULL);
+ webkit_dom_element_set_attribute (
+ WEBKIT_DOM_ELEMENT (node), "data-name", name, NULL);
+ webkit_dom_html_image_element_set_src (
+ WEBKIT_DOM_HTML_IMAGE_ELEMENT (node),
+ base64_data);
+ g_free (name);
+ }
+ }
+ g_hash_table_remove_all (widget->priv->inline_images);
+}
static void
editor_widget_load_status_changed (EEditorWidget *widget)
@@ -360,6 +403,10 @@ editor_widget_load_status_changed (EEditorWidget *widget)
FALSE,
widget);
+ if (widget->priv->html_mode) {
+ change_cid_images_src_to_base64 (widget);
+ }
+
/* Dispatch queued operations */
while (widget->priv->postreload_operations &&
!g_queue_is_empty (widget->priv->postreload_operations)) {
@@ -671,59 +718,171 @@ editor_widget_check_magic_links (EEditorWidget *widget,
g_free (node_text);
}
-void
-e_editor_widget_insert_smiley (EEditorWidget *widget,
- EEmoticon *emoticon)
+typedef struct _LoadContext LoadContext;
+
+struct _LoadContext {
+ EEditorWidget *widget;
+ gchar *content_type;
+ gchar *name;
+ EEmoticon *emoticon;
+};
+
+static LoadContext *
+emoticon_load_context_new (EEditorWidget *widget,
+ EEmoticon *emoticon)
{
- gchar *filename_uri, *html, *node_text;
+ LoadContext *load_context;
+
+ load_context = g_slice_new0 (LoadContext);
+ load_context->widget = widget;
+ load_context->emoticon = emoticon;
+
+ return load_context;
+}
+
+static void
+emoticon_load_context_free (LoadContext *load_context)
+{
+ g_free (load_context->content_type);
+ g_free (load_context->name);
+ g_slice_free (LoadContext, load_context);
+}
+
+static void
+emoticon_read_async_cb (GFile *file,
+ GAsyncResult *result,
+ LoadContext *load_context)
+{
+ EEditorWidget *widget = load_context->widget;
+ EEmoticon *emoticon = load_context->emoticon;
+ GError *error = NULL;
+ gchar *html, *node_text, *mime_type;
+ gchar *base64_encoded, *output, *data;
+ const gchar *emoticon_start;
+ GFileInputStream *input_stream;
+ GOutputStream *output_stream;
+ gssize size;
WebKitDOMDocument *document;
- WebKitDOMElement *span;
+ WebKitDOMElement *span, *caret_position;
WebKitDOMNode *node;
WebKitDOMNode *parent;
WebKitDOMRange *range;
+ input_stream = g_file_read_finish (file, result, &error);
+ g_return_if_fail (!error && input_stream);
+
+ output_stream = g_memory_output_stream_new (NULL, 0, g_realloc, g_free);
+
+ size = g_output_stream_splice (
+ output_stream, G_INPUT_STREAM (input_stream),
+ G_OUTPUT_STREAM_SPLICE_NONE, NULL, &error);
+
+ if (error || (size == -1))
+ goto out;
+
+ e_editor_selection_save_caret_position (
+ e_editor_widget_get_selection (widget));
+
+ mime_type = g_content_type_get_mime_type (load_context->content_type);
document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (widget));
range = editor_widget_get_dom_range (widget);
node = webkit_dom_range_get_end_container (range, NULL);
node_text = webkit_dom_text_get_whole_text (WEBKIT_DOM_TEXT (node));
-
- filename_uri = e_emoticon_get_uri (emoticon);
parent = webkit_dom_node_get_parent_node (node);
span = webkit_dom_document_create_element (document, "SPAN", NULL);
+
+ data = g_memory_output_stream_get_data (G_MEMORY_OUTPUT_STREAM (output_stream));
+
+ base64_encoded = g_base64_encode ((const guchar *) data, size);
+ output = g_strconcat ("data:", mime_type, ";base64,", base64_encoded, NULL);
+
/* Insert span with image representation and another one with text
* represetation and hide/show them dependant on active composer mode */
/* ​ == UNICODE_ZERO_WIDTH_SPACE */
html = g_strdup_printf (
- "<span class\"-x-evo-smiley-wrapper\"><img src=\"%s\" alt=\"%s\" "
- "x-evo-smiley=\"%s\" class=\"-x-evo-smiley-img\"/><span "
- "class=\"-x-evo-smiley-text\" style=\"display: none;\">%s</span>"
- "</span>​",
- filename_uri, emoticon ? emoticon->text_face : "",
- emoticon->icon_name, emoticon ? emoticon->text_face : "");
-
- span = WEBKIT_DOM_ELEMENT (webkit_dom_node_append_child (
- parent, WEBKIT_DOM_NODE (span), NULL));
+ "<span class=\"-x-evo-smiley-wrapper\"><img src=\"%s\" alt=\"%s\" "
+ "x-evo-smiley=\"%s\" class=\"-x-evo-smiley-img\" data-inline "
+ "data-name=\"%s\"/><span class=\"-x-evo-smiley-text\" "
+ "style=\"display: none;\">%s</span></span>​",
+ output, emoticon ? emoticon->text_face : "", emoticon->icon_name,
+ load_context->name, emoticon ? emoticon->text_face : "");
+
+ caret_position = webkit_dom_document_get_element_by_id (
+ document, "-x-evo-caret-position");
+ span = WEBKIT_DOM_ELEMENT (webkit_dom_node_insert_before (
+ parent,
+ WEBKIT_DOM_NODE (span),
+ WEBKIT_DOM_NODE (caret_position),
+ NULL));
webkit_dom_html_element_set_outer_html (
WEBKIT_DOM_HTML_ELEMENT (span), html, NULL);
- webkit_dom_node_append_child (
- parent,
- e_editor_selection_get_caret_position_node (document),
- NULL);
-
- webkit_dom_character_data_delete_data (
- WEBKIT_DOM_CHARACTER_DATA (node),
- g_utf8_strlen (node_text, -1) - strlen (emoticon->text_face),
- strlen (emoticon->text_face),
- NULL);
+ emoticon_start = g_utf8_strrchr (
+ node_text, -1, g_utf8_get_char (emoticon->text_face));
+ if (emoticon_start) {
+ webkit_dom_character_data_delete_data (
+ WEBKIT_DOM_CHARACTER_DATA (node),
+ g_utf8_strlen (node_text, -1) - strlen (emoticon_start),
+ strlen (emoticon->text_face),
+ NULL);
+ }
e_editor_selection_restore_caret_position (
e_editor_widget_get_selection (widget));
g_free (html);
- g_free (filename_uri);
g_free (node_text);
+ g_free (base64_encoded);
+ g_free (output);
+ g_free (mime_type);
+ g_object_unref (output_stream);
+ out:
+ emoticon_load_context_free (load_context);
+}
+
+static void
+emoticon_query_info_async_cb (GFile *file,
+ GAsyncResult *result,
+ LoadContext *load_context)
+{
+ GError *error = NULL;
+ GFileInfo *info;
+
+ info = g_file_query_info_finish (file, result, &error);
+ g_return_if_fail (!error && info);
+
+ load_context->content_type = g_strdup (g_file_info_get_content_type (info));
+ load_context->name = g_strdup (g_file_info_get_name (info));
+
+ g_file_read_async (
+ file, G_PRIORITY_DEFAULT, NULL,
+ (GAsyncReadyCallback) emoticon_read_async_cb, load_context);
+
+ g_object_unref (info);
+}
+
+void
+e_editor_widget_insert_smiley (EEditorWidget *widget,
+ EEmoticon *emoticon)
+{
+ GFile *file;
+ gchar *filename_uri;
+ LoadContext *load_context;
+
+ filename_uri = e_emoticon_get_uri (emoticon);
+ g_return_if_fail (filename_uri != NULL);
+
+ load_context = emoticon_load_context_new (widget, emoticon);
+
+ file = g_file_new_for_uri (filename_uri);
+ g_file_query_info_async (
+ file, "standard::*", G_FILE_QUERY_INFO_NONE,
+ G_PRIORITY_DEFAULT, NULL,
+ (GAsyncReadyCallback) emoticon_query_info_async_cb, load_context);
+
+ g_free (filename_uri);
+ g_object_unref (file);
}
static void
@@ -999,11 +1158,26 @@ editor_widget_dispose (GObject *object)
priv->font_settings = NULL;
}
+ g_hash_table_remove_all (priv->inline_images);
+
/* Chain up to parent's dispose() method. */
G_OBJECT_CLASS (e_editor_widget_parent_class)->dispose (object);
}
static void
+editor_widget_finalize (GObject *object)
+{
+ EEditorWidgetPrivate *priv;
+
+ priv = E_EDITOR_WIDGET_GET_PRIVATE (object);
+
+ g_hash_table_destroy (priv->inline_images);
+
+ /* Chain up to parent's finalize() method. */
+ G_OBJECT_CLASS (e_editor_widget_parent_class)->finalize (object);
+}
+
+static void
editor_widget_constructed (GObject *object)
{
e_extensible_load_extensions (E_EXTENSIBLE (object));
@@ -1320,6 +1494,7 @@ e_editor_widget_class_init (EEditorWidgetClass *class)
object_class->get_property = editor_widget_get_property;
object_class->set_property = editor_widget_set_property;
object_class->dispose = editor_widget_dispose;
+ object_class->finalize = editor_widget_finalize;
object_class->constructed = editor_widget_constructed;
widget_class = GTK_WIDGET_CLASS (class);
@@ -1555,6 +1730,7 @@ e_editor_widget_init (EEditorWidget *editor)
"enable-plugins", FALSE,
"enable-scripts", FALSE,
"enable-spell-checking", TRUE,
+ "respect-image-orientation", TRUE,
NULL);
webkit_web_view_set_settings (WEBKIT_WEB_VIEW (editor), settings);
@@ -1608,6 +1784,11 @@ e_editor_widget_init (EEditorWidget *editor)
editor->priv->aliasing_settings = g_settings;
}
+ editor->priv->inline_images = g_hash_table_new_full (
+ g_str_hash, g_str_equal,
+ (GDestroyNotify) g_free,
+ (GDestroyNotify) g_free);
+
e_editor_widget_update_fonts (editor);
/* Make WebKit think we are displaying a local file, so that it
@@ -2242,7 +2423,7 @@ e_editor_widget_dequote_plain_text (EEditorWidget *widget)
document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (widget));
list = webkit_dom_document_query_selector_all (
- document, "blockquote.-x-evo-plaintext-quoted", NULL);
+ document, "blockquote.-x-evo-plaintext-quoted", NULL);
length = webkit_dom_node_list_get_length (list);
for (ii = 0; ii < length; ii++) {
WebKitDOMNodeList *gt_list;
@@ -3421,3 +3602,175 @@ e_editor_widget_check_magic_links (EEditorWidget *widget,
editor_widget_check_magic_links (widget, range, include_space, NULL);
}
+static CamelMimePart *
+e_editor_widget_add_inline_image_from_element (EEditorWidget *widget,
+ WebKitDOMElement *element)
+{
+ CamelStream *stream;
+ CamelDataWrapper *wrapper;
+ CamelMimePart *part = NULL;
+ gsize decoded_size;
+ gssize size;
+ gchar *mime_type = NULL;
+ gchar *element_src, *cid, *name;
+ const gchar *base64_encoded_data;
+ guchar *base64_decoded_data;
+
+ if (!WEBKIT_DOM_IS_HTML_IMAGE_ELEMENT (element))
+ return NULL;
+
+ element_src = webkit_dom_html_image_element_get_src (
+ WEBKIT_DOM_HTML_IMAGE_ELEMENT (element));
+
+ base64_encoded_data = strstr (element_src, ";base64,");
+ if (!base64_encoded_data)
+ goto out;
+
+ mime_type = g_strndup (
+ element_src + 5,
+ base64_encoded_data - (strstr (element_src, "data:") + 5));
+
+ /* Move to actual data */
+ base64_encoded_data += 8;
+
+ base64_decoded_data = g_base64_decode (base64_encoded_data, &decoded_size);
+
+ stream = camel_stream_mem_new ();
+ size = camel_stream_write (
+ stream, (gchar *) base64_decoded_data, decoded_size, NULL, NULL);
+
+ if (size == -1)
+ goto out;
+
+ wrapper = camel_data_wrapper_new ();
+ camel_data_wrapper_construct_from_stream_sync (
+ wrapper, stream, NULL, NULL);
+ g_object_unref (CAMEL_OBJECT (stream));
+
+ camel_data_wrapper_set_mime_type (wrapper, mime_type);
+
+ part = camel_mime_part_new ();
+ camel_medium_set_content (CAMEL_MEDIUM (part), wrapper);
+ g_object_unref (wrapper);
+
+ cid = camel_header_msgid_generate ();
+ camel_mime_part_set_content_id (part, cid);
+ name = webkit_dom_element_get_attribute (element, "data-name");
+ camel_mime_part_set_filename (part, name);
+ g_free (name);
+ camel_mime_part_set_encoding (part, CAMEL_TRANSFER_ENCODING_BASE64);
+out:
+ g_free (mime_type);
+ g_free (element_src);
+ g_free (base64_decoded_data);
+
+ return part;
+}
+
+GList *
+e_editor_widget_get_parts_for_inline_images (EEditorWidget *widget)
+{
+ GHashTable *added;
+ GList *parts = NULL;
+ gint length, ii;
+ WebKitDOMDocument *document;
+ WebKitDOMNodeList *list;
+
+ document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (widget));
+ list = webkit_dom_document_query_selector_all (document, "img[data-inline]", NULL);
+
+ length = webkit_dom_node_list_get_length (list);
+ if (length == 0)
+ return parts;
+
+ added = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, NULL);
+ for (ii = 0; ii < length; ii++) {
+ CamelMimePart *part;
+ WebKitDOMNode *node;
+ gchar *src;
+
+ node = webkit_dom_node_list_item (list, ii);
+ src = webkit_dom_html_image_element_get_src (
+ WEBKIT_DOM_HTML_IMAGE_ELEMENT (node));
+
+ if (!g_hash_table_lookup (added, src)) {
+ part = e_editor_widget_add_inline_image_from_element (
+ widget, WEBKIT_DOM_ELEMENT (node));
+ parts = g_list_append (parts, part);
+ g_hash_table_insert (
+ added, src, (gpointer) camel_mime_part_get_content_id (part));
+ }
+ g_free (src);
+ }
+
+ for (ii = 0; ii < length; ii++) {
+ WebKitDOMNode *node;
+ gchar *src;
+ const gchar *id;
+
+ node = webkit_dom_node_list_item (list, ii);
+ src = webkit_dom_html_image_element_get_src (
+ WEBKIT_DOM_HTML_IMAGE_ELEMENT (node));
+
+ if ((id = g_hash_table_lookup (added, src)) != NULL) {
+ gchar *cid = g_strdup_printf ("cid:%s", id);
+ webkit_dom_html_image_element_set_src (
+ WEBKIT_DOM_HTML_IMAGE_ELEMENT (node), cid);
+ g_free (cid);
+ }
+ g_free (src);
+ }
+ g_hash_table_destroy (added);
+
+ return parts;
+}
+
+/**
+ * e_editor_widget_add_inline_image_from_mime_part:
+ * @composer: a composer object
+ * @part: a CamelMimePart containing image data
+ *
+ * This adds the mime part @part to @composer as an inline image.
+ **/
+void
+e_editor_widget_add_inline_image_from_mime_part (EEditorWidget *widget,
+ CamelMimePart *part)
+{
+ CamelDataWrapper *dw;
+ CamelStream *stream;
+ GByteArray *byte_array;
+ gchar *src, *base64_encoded, *mime_type, *cid_src;
+ const gchar *cid, *name;
+
+ stream = camel_stream_mem_new ();
+ dw = camel_medium_get_content (CAMEL_MEDIUM (part));
+ g_return_if_fail (dw);
+
+ mime_type = camel_data_wrapper_get_mime_type (dw);
+ camel_data_wrapper_decode_to_stream_sync (dw, stream, NULL, NULL);
+ camel_stream_close (stream, NULL, NULL);
+
+ byte_array = camel_stream_mem_get_byte_array (CAMEL_STREAM_MEM (stream));
+
+ if (!byte_array->data)
+ return;
+
+ base64_encoded = g_base64_encode ((const guchar *) byte_array->data, byte_array->len);
+
+ name = camel_mime_part_get_filename (part);
+ /* Insert file name before new src */
+ src = g_strconcat (name, ";data:", mime_type, ";base64,", base64_encoded, NULL);
+
+ cid = camel_mime_part_get_content_id (part);
+ if (!cid) {
+ camel_mime_part_set_content_id (part, NULL);
+ cid = camel_mime_part_get_content_id (part);
+ }
+ cid_src = g_strdup_printf ("cid:%s", cid);
+
+ g_hash_table_insert (widget->priv->inline_images, cid_src, src);
+
+ g_free (base64_encoded);
+ g_free (mime_type);
+ g_object_unref (stream);
+}
diff --git a/e-util/e-editor-widget.h b/e-util/e-editor-widget.h
index 412f199..2b1a603 100644
--- a/e-util/e-editor-widget.h
+++ b/e-util/e-editor-widget.h
@@ -27,6 +27,8 @@
#include <webkit/webkit.h>
+#include <camel/camel.h>
+
#include <e-util/e-editor-selection.h>
#include <e-util/e-emoticon.h>
#include <e-util/e-spell-checker.h>
@@ -125,6 +127,11 @@ void e_editor_widget_dequote_plain_text
(EEditorWidget *widget);
void e_editor_widget_force_spellcheck
(EEditorWidget *widget);
+void e_editor_widget_add_inline_image_from_mime_part
+ (EEditorWidget *widget,
+ CamelMimePart *part);
+GList * e_editor_widget_get_parts_for_inline_images
+ (EEditorWidget *widget);
G_END_DECLS
#endif /* E_EDITOR_WIDGET_H */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]