Implement image handling
authorMichael Rasmussen <mir@datanom.net>
Wed, 7 Nov 2018 02:20:11 +0000 (03:20 +0100)
committerAndrej Kacian <ticho@claws-mail.org>
Tue, 12 Feb 2019 18:38:09 +0000 (19:38 +0100)
Signed-off-by: Michael Rasmussen <mir@datanom.net>
src/plugins/litehtml_viewer/Makefile.am
src/plugins/litehtml_viewer/TODO
src/plugins/litehtml_viewer/lh_viewer.c
src/plugins/litehtml_viewer/lh_widget.cpp
src/plugins/litehtml_viewer/lh_widget.h

index 5e4e732..15cb479 100644 (file)
@@ -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
 
index 912078f..56e3846 100644 (file)
@@ -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
index 73c1b69..0b3cf53 100644 (file)
@@ -23,6 +23,7 @@
 #endif
 
 #include <codeconv.h>
+#include "common/utils.h"
 #include "lh_viewer.h"
 
 static gchar *content_types[] = { "text/html", NULL };
index a700b63..5c90005 100644 (file)
 #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" {
 
index 4cdf8ce..b17b626 100644 (file)
@@ -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;
+
 };