talons

Fork of Claws Mail https://www.claws-mail
Log | Files | Refs | README | LICENSE

commit f5233ebb7beef2ef76779a5cb86b1577c58d2e44
parent e0d9f8411a6a7026bc6284e9b91e6a2a5a0f8dc2
Author: Michael Rasmussen <mir@datanom.net>
Date:   Wed,  7 Nov 2018 03:20:11 +0100

Implement image handling

Signed-off-by: Michael Rasmussen <mir@datanom.net>

Diffstat:
Msrc/plugins/litehtml_viewer/Makefile.am | 6++++--
Msrc/plugins/litehtml_viewer/TODO | 3+++
Msrc/plugins/litehtml_viewer/lh_viewer.c | 1+
Msrc/plugins/litehtml_viewer/lh_widget.cpp | 129++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Msrc/plugins/litehtml_viewer/lh_widget.h | 6++++++
5 files changed, 142 insertions(+), 3 deletions(-)

diff --git a/src/plugins/litehtml_viewer/Makefile.am b/src/plugins/litehtml_viewer/Makefile.am @@ -53,14 +53,16 @@ litehtml_viewer_la_LDFLAGS = \ -avoid-version -module \ $(GTK_LIBS) \ $(FONTCONFIG_LIBS) \ - $(CAIRO_LIBS) + $(CAIRO_LIBS) \ + $(CURL_LIBS) litehtml_viewer_la_CPPFLAGS = \ $(IFLAGS) \ $(GLIB_CFLAGS) \ $(GTK_CFLAGS) \ $(FONTCONFIG_CFLAGS) \ - $(CAIRO_CFLAGS) + $(CAIRO_CFLAGS) \ + $(CURL_FLAGS) .PHONY: test diff --git a/src/plugins/litehtml_viewer/TODO b/src/plugins/litehtml_viewer/TODO @@ -2,3 +2,6 @@ - Add support for printing - Add support for links (open in default browser) +Images: +https://developer.gnome.org/gio/unstable/GMemoryInputStream.html#g-memory-input-stream-new-from-data +http://darcs.cielonegro.org/gtktwitter/gtktwitter.c diff --git a/src/plugins/litehtml_viewer/lh_viewer.c b/src/plugins/litehtml_viewer/lh_viewer.c @@ -23,6 +23,7 @@ #endif #include <codeconv.h> +#include "common/utils.h" #include "lh_viewer.h" static gchar *content_types[] = { "text/html", NULL }; diff --git a/src/plugins/litehtml_viewer/lh_widget.cpp b/src/plugins/litehtml_viewer/lh_widget.cpp @@ -22,6 +22,12 @@ #include "claws-features.h" #endif +#include <glib.h> +#include <glib/gstdio.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <curl/curl.h> #include "lh_widget.h" #include "lh_widget_wrapped.h" @@ -29,10 +35,19 @@ char master_css[] = { #include "css.inc" }; +/** + * curl callback + */ +static char* response_mime = NULL; /* response content-type. ex: "text/html" */ +static char* response_data = NULL; /* response data from server. */ +static size_t response_size = 0; /* response size of data */ + static gboolean expose_event_cb(GtkWidget *widget, GdkEvent *event, gpointer user_data); static void size_allocate_cb(GtkWidget *widget, GdkRectangle *allocation, gpointer user_data); +static size_t handle_returned_data(char* ptr, size_t size, size_t nmemb, void* stream); +static size_t handle_returned_header(void* ptr, size_t size, size_t nmemb, void* stream); lh_widget::lh_widget() { @@ -61,6 +76,7 @@ lh_widget::lh_widget() m_html = NULL; m_rendered_width = 0; m_context.load_master_stylesheet(master_css); + stream = NULL; } lh_widget::~lh_widget() @@ -70,6 +86,10 @@ lh_widget::~lh_widget() g_object_unref(m_scrolled_window); m_scrolled_window = NULL; m_html = NULL; + if (stream) { + g_input_stream_close(stream, NULL, NULL); + stream = NULL; + } } GtkWidget *lh_widget::get_widget() const @@ -124,7 +144,34 @@ void lh_widget::get_client_rect(litehtml::position& client) const GdkPixbuf *lh_widget::get_image(const litehtml::tchar_t* url, bool redraw_on_ready) { - return NULL; + GError *error = NULL; + GdkPixbuf *pixbuf = NULL; + + g_log(NULL, G_LOG_LEVEL_MESSAGE, "Loading... %s", url); + + GInputStream *image = load_url(url, &error); + if (error) { + g_log(NULL, G_LOG_LEVEL_MESSAGE, "Error: %s", error->message); + g_error_free(error); + return NULL; + } + + GdkPixbufLoader* loader = gdk_pixbuf_loader_new(); + if (gdk_pixbuf_loader_write(loader, (const guchar*)response_data, response_size, &error)) { + pixbuf = gdk_pixbuf_loader_get_pixbuf(loader); + } else { + g_log(NULL, G_LOG_LEVEL_ERROR, "lh_widget::get_image: Could not create pixbuf"); + } + gdk_pixbuf_loader_close(loader, NULL); + + /* cleanup callback data */ + if (response_mime) g_free(response_mime); + if (response_data) g_free(response_data); + response_data = NULL; + response_mime = NULL; + response_size = 0; + + return pixbuf; } void lh_widget::open_html(const gchar *contents) @@ -235,6 +282,58 @@ void lh_widget::clear() m_rendered_width = 0; } +GInputStream *lh_widget::load_url(const gchar *url, GError **error) +{ + GError* _error = NULL; + CURL* curl = NULL; + CURLcode res = CURLE_OK; + gsize len; + gchar* content; + + /* initialize callback data */ + response_mime = NULL; + response_data = NULL; + response_size = 0; + if (stream) { + g_input_stream_close(stream, NULL, &_error); + if (_error) { + if (error) *error = _error; + return NULL; + } + } + + stream = NULL; + + if (!strncmp(url, "file:///", 8) || g_file_test(url, G_FILE_TEST_EXISTS)) { + gchar* newurl = g_filename_from_uri(url, NULL, NULL); + if (g_file_get_contents(newurl ? newurl : url, &content, &len, &_error)) { + stream = g_memory_input_stream_new_from_data(content, len, g_free); + } else { + g_log(NULL, G_LOG_LEVEL_MESSAGE, "%s", _error->message); + } + g_free(newurl); + } else { + curl = curl_easy_init(); + if (!curl) return NULL; + curl_easy_setopt(curl, CURLOPT_URL, url); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, handle_returned_data); + curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, handle_returned_header); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); + curl_easy_setopt(curl, CURLOPT_TIMEOUT, HTTP_GET_TIMEOUT); + curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L); + res = curl_easy_perform(curl); + curl_easy_cleanup(curl); + if (res == CURLE_OK) { + stream = g_memory_input_stream_new_from_data(content, response_size, g_free); + } else + _error = g_error_new_literal(G_FILE_ERROR, res, curl_easy_strerror(res)); + } + + if (error && _error) *error = _error; + + return stream; +} + static gboolean expose_event_cb(GtkWidget *widget, GdkEvent *event, gpointer user_data) { @@ -255,6 +354,34 @@ static void size_allocate_cb(GtkWidget *widget, GdkRectangle *allocation, w->redraw(); } +static size_t handle_returned_data(char* ptr, size_t size, size_t nmemb, void* stream) { + if (!response_data) + response_data = (char*)malloc(size*nmemb); + else + response_data = (char*)realloc(response_data, response_size+size*nmemb); + if (response_data) { + memcpy(response_data+response_size, ptr, size*nmemb); + response_size += size*nmemb; + } + return size*nmemb; +} + +static size_t handle_returned_header(void* ptr, size_t size, size_t nmemb, void* stream) { + char* header = NULL; + + header = (char*) malloc(size*nmemb + 1); + memcpy(header, ptr, size*nmemb); + header[size*nmemb] = 0; + if (strncmp(header, "Content-Type: ", 14) == 0) { + char* stop = header + 14; + stop = strpbrk(header + 14, "\r\n;"); + if (stop) *stop = 0; + response_mime = strdup(header + 14); + } + free(header); + return size*nmemb; +} + /////////////////////////////////////////////////////////// extern "C" { diff --git a/src/plugins/litehtml_viewer/lh_widget.h b/src/plugins/litehtml_viewer/lh_widget.h @@ -1,8 +1,11 @@ #include <gtk/gtk.h> #include <glib.h> +#include <gio/gio.h> #include "container_linux.h" +#define HTTP_GET_TIMEOUT 60L + class lh_widget : public container_linux { public: @@ -28,7 +31,9 @@ class lh_widget : public container_linux private: void paint_white(); + GInputStream *load_url(const gchar *url, GError **error); + GInputStream *stream; litehtml::document::ptr m_html; gint m_rendered_width; GtkWidget *m_drawing_area; @@ -36,4 +41,5 @@ class lh_widget : public container_linux GtkWidget *m_viewport; litehtml::context m_context; gint m_height; + };