[gnome-web-photo/webkit: 1/4] Complete rewrite: it's now using webkit
- From: Vincent Untz <vuntz src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-web-photo/webkit: 1/4] Complete rewrite: it's now using webkit
- Date: Tue, 15 Feb 2011 07:55:03 +0000 (UTC)
commit 26435ee035a239bf2c56b5a74fb697b26659ff2e
Author: Vincent Untz <vuntz gnome org>
Date: Tue Feb 15 08:47:56 2011 +0100
Complete rewrite: it's now using webkit
Makefile.am | 3 +-
README | 3 +-
TODO | 7 -
autogen.sh | 2 +-
configure.ac | 103 ++---
data/Makefile.am | 16 +-
data/gnome-web-photo.thumbnailer | 4 +
data/prefs.js | 73 ---
data/style.css | 3 -
data/thumbnailer.schemas.in | 4 +-
m4/libxul.m4 | 566 ----------------------
src/Components.cpp | 191 --------
src/Components.h | 28 --
src/Embed.cpp | 163 -------
src/Embed.h | 65 ---
src/Listener.cpp | 134 ------
src/Listener.h | 55 ---
src/Makefile.am | 54 +--
src/Prefs.cpp | 182 -------
src/Prefs.h | 28 --
src/Printer.cpp | 199 --------
src/Printer.h | 55 ---
src/Writer.cpp | 712 ----------------------------
src/Writer.h | 135 ------
src/gnome-web-photo.c | 971 ++++++++++++++++++++++++++++++++++++++
src/main.cpp | 657 --------------------------
26 files changed, 1026 insertions(+), 3387 deletions(-)
---
diff --git a/Makefile.am b/Makefile.am
index a7c1432..d559a64 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -36,8 +36,7 @@ MAINTAINERCLEANFILES = \
DISTCHECK_CONFIGURE_FLAGS = \
--disable-silent-rules \
- --disable-schemas-install \
- --with-gecko=$(GECKO)
+ --disable-schemas-install
ChangeLog:
$(AM_V_GEN) if test -f $(top_srcdir)/.git/HEAD; then \
diff --git a/README b/README
index 62d101c..e0bcd19 100644
--- a/README
+++ b/README
@@ -3,6 +3,5 @@ files and thumbnails from HTML files and web pages.
Requirements:
* gtk+
-* libpng
-* mozilla or firefox trunk build from 2005-07-19 or later
+* webkitgtk
diff --git a/TODO b/TODO
index 05a3d77..3ad0669 100644
--- a/TODO
+++ b/TODO
@@ -1,14 +1,7 @@
-Important:
-* Port to gecko 1.9/XULrunner. Maybe look at mozilla's reftest helper (mozilla/layout/tools/reftest/) ?
-
Improvements:
-* Test on a big endian machine
* Improve thumbnail creation time
* Thumbnailing service? I.e. background process (which exits after some time without requests),
which the frontend tells which files/pages to thumbnail.
-* Find out why it crashes on https:// sites, and on http://slashdot.org/, if I disable the profile
* Fix the output on framed pages
-* Use <canvas> and drawWindow() instead of the low-level stuff (<canvas> can operate without a document,
- so we don't need to put it in an embed)
* Add memory size and CPU time limit (look at totem-video-thumbnailer/indexer)
* Implement printing to printer, using the gtk 2.10 print APIs
diff --git a/autogen.sh b/autogen.sh
index e0f3d1a..085ac9b 100755
--- a/autogen.sh
+++ b/autogen.sh
@@ -6,7 +6,7 @@ test -z "$srcdir" && srcdir=.
PKG_NAME="gnome-web-photo"
-(test -f $srcdir/src/main.cpp) || {
+(test -f $srcdir/src/gnome-web-photo.c) || {
echo -n "**Error**: Directory "\`$srcdir\'" does not look like the"
echo " top-level $PKG_NAME directory"
exit 1
diff --git a/configure.ac b/configure.ac
index 1cf37ab..ae38e43 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,5 +1,6 @@
# Copyright © 2000-2004 Marco Pesenti Gritti
# Copyright © 2003, 2004, 2005 Christian Persch
+# Copyright © 2011 Vincent Untz
#
# 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
@@ -31,9 +32,10 @@ m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])])
IT_PROG_INTLTOOL([0.35.0])
-GLIB_REQUIRED=2.6.0
-GTK_REQUIRED=2.6.3
-LIBXML_REQUIRED=2.6.12
+GLIB_REQUIRED=2.14.0
+GTK2_REQUIRED=2.20.0
+GTK3_REQUIRED=2.99.3
+WEBKIT_REQUIRED=1.1.23
AC_ENABLE_SHARED([yes])
AC_ENABLE_STATIC([no])
@@ -45,97 +47,64 @@ AC_ISC_POSIX
AC_PROG_LN_S
AC_PROG_CC
-AC_PROG_CXX
AM_PROG_CC_STDC
AC_HEADER_STDC
AC_C_BIGENDIAN
-# AC_PATH_PROG([GLIB_GENMARSHAL],[glib-genmarshal])
-# AC_PATH_PROG([GLIB_MKENUMS],[glib-mkenums])
-
GNOME_DEBUG_CHECK
GNOME_COMPILE_WARNINGS([maximum])
-GNOME_CXX_WARNINGS
MORE_WARN_FLAGS=
DEPRECATION_FLAGS=
-MOZILLA_WARN_CXXFLAGS="-Wno-ctor-dtor-privacy -Wno-non-virtual-dtor"
if test "x$enable_maintainer_mode" = "xyes"; then
AC_DEFINE([MAINTAINER_MODE],[1],[Define to enable 'maintainer-only' behaviour])
enable_debug=yes
DEPRECATION_FLAGS="-DG_DISABLE_DEPRECATED -DGDK_DISABLE_DEPRECATED -DGDK_PIXBUF_DISABLE_DEPRECATED -DGCONF_DISABLE_DEPRECATED -DGNOME_VFS_DISABLE_DEPRECATED -DBONOBO_UI_DISABLE_DEPRECATED -DBONOBO_DISABLE_DEPRECATED -DLIBGLADE_DISABLE_DEPRECATED -DPANGO_DISABLE_DEPRECATED -DGTK_DISABLE_DEPRECATED -DGNOME_DISABLE_DEPRECATED"
- MOZILLA_WARN_CXXFLAGS="-Wall -Wconversion -Wpointer-arith -Wcast-align -Woverloaded-virtual -Wsynth $MOZILLA_WARN_CXXFLAGS"
fi
-PKG_CHECK_MODULES([DEPENDENCY], [\
- glib-2.0 >= $GLIB_REQUIRED \
- gtk+-2.0 >= $GTK_REQUIRED \
- libxml-2.0 >= $LIBXML_REQUIRED \
- gconf-2.0 \
- libpng])
+AC_MSG_CHECKING([which gtk+ version to compile against])
+AC_ARG_WITH([gtk],
+ [AS_HELP_STRING([--with-gtk=2.0|3.0],[which gtk+ version to compile against (default: 3.0)])],
+ [case "$with_gtk" in
+ 2.0|3.0) ;;
+ *) AC_MSG_ERROR([invalid gtk version specified]) ;;
+ esac],
+ [with_gtk=3.0])
+AC_MSG_RESULT([$with_gtk])
+
+case "$with_gtk" in
+ 2.0)
+ DEPENDENCY_PC="glib-2.0 >= $GLIB_REQUIRED gtk+-2.0 >= $GTK2_REQUIRED webkit-1.0 >= $WEBKIT_REQUIRED gconf-2.0"
+ ;;
+ 3.0)
+ DEPENDENCY_PC="glib-2.0 >= $GLIB_REQUIRED gtk+-3.0 >= $GTK3_REQUIRED webkitgtk-3.0 >= $WEBKIT_REQUIRED"
+ AC_DEFINE([HAVE_GNOME3], 1,
+ [Use GNOME 3 technologies])
+ ;;
+esac
+AM_CONDITIONAL(HAVE_GNOME3, test "$with_gtk" == 3.0)
+
+PKG_CHECK_MODULES([DEPENDENCY], [$DEPENDENCY_PC])
AC_SUBST([DEPENDENCY_CFLAGS])
AC_SUBST([DEPENDENCY_LIBS])
-# ************
-# JPEG support
-# ************
-
-AC_MSG_CHECKING([whether JPEG support is requested])
-AC_ARG_ENABLE([jpeg],
- [AS_HELP_STRING([--disable-jpeg],[Disable JPEG support])],
- [enable_jpeg=$enableval],[enable_jpeg=yes])
-AC_MSG_RESULT([$enable_jpeg])
-
-# libjpeg sucksâ??it doesn't have a .pc file!
-have_jpeg=no
-if test "$enable_jpeg" != "no"; then
- AC_CHECK_LIB([jpeg],[jpeg_destroy_decompress],[have_jpeg=yes],[have_jpeg=no])
-fi
-if test "$enable_jpeg" != "no" -a "$have_jpeg" = "yes"; then
- AC_MSG_CHECKING([for jpeglib.h])
- AC_PREPROC_IFELSE([AC_LANG_SOURCE(
- [[#include <stdio.h>
- #undef PACKAGE
- #undef VERSION
- #undef HAVE_STDLIB_H
- #include <jpeglib.h>]])],
- [have_jpeg=yes],
- [have_jpeg=no])
- AC_MSG_RESULT([$have_jpeg])
-fi
-if test "$enable_jpeg" = "yes" -a "$have_jpeg" = "yes"; then
- JPEG_LIBS="-ljpeg"
- AC_SUBST([JPEG_LIBS])
- AC_DEFINE([ENABLE_JPEG],[1],[Define if JPEG support is enabled])
-elif test "$enable_jpeg" = "yes" -a "$have_jpeg" = "no"; then
- AC_MSG_ERROR([JPEG support requested but required jpeg library or headers not found])
-else
- AC_MSG_WARN([JPEG support disabled])
-fi
-
-AM_CONDITIONAL([ENABLE_JPEG],[test "$enable_jpeg" = "yes" -a "$have_jpeg" = "yes"])
-
# *****
# GConf
# *****
-AC_PATH_PROG([GCONFTOOL], [gconftool-2], [no])
+if test "$with_gtk" == 2.0; then
+ AC_PATH_PROG([GCONFTOOL], [gconftool-2], [no])
+
+ if test "x$GCONFTOOL" = xno; then
+ AC_MSG_ERROR([gconftool-2 not found])
+ fi
-if test "x$GCONFTOOL" = xno; then
- AC_MSG_ERROR([gconftool-2 not found])
fi
AM_GCONF_SOURCE_2
-# *********
-# Xulrunner
-# *********
-
-LIBXUL_INIT
-LIBXUL_DEFINES
-
# ******************
# Portability checks
# ******************
@@ -162,18 +131,14 @@ if test "x$enable_maintainer_mode" = "xyes"; then
AC_LANG_POP([C])
fi
-SUPPRESSION_CXXFLAGS="-Wconversion -Wpointer-arith -Wcast-align -Woverloaded-virtual -Wsynth -Wno-ctor-dtor-privacy -Wno-non-virtual-dtor"
-
# *****************
# Add warning flags
# *****************
AM_CPPFLAGS="$AM_CPPFLAGS $DEPRECATION_FLAGS"
AM_CFLAGS="$AM_CFLAGS $WARN_CFLAGS $MORE_WARN_FLAGS"
-AM_CXXFLAGS="$AM_CXXFLAGS $WARN_CXXFLAGS $SUPPRESSION_CXXFLAGS"
AC_SUBST([AM_CPPFLAGS])
AC_SUBST([AM_CFLAGS])
-AC_SUBST([AM_CXXFLAGS])
AC_SUBST([AM_LDFLAGS])
# ********************
diff --git a/data/Makefile.am b/data/Makefile.am
index 3af718d..3665bee 100644
--- a/data/Makefile.am
+++ b/data/Makefile.am
@@ -1,3 +1,4 @@
+if !HAVE_GNOME3
@INTLTOOL_SCHEMAS_RULE@
schemadir = $(GCONF_SCHEMA_FILE_DIR)
@@ -12,17 +13,16 @@ if GCONF_SCHEMAS_INSTALL
done \
fi
endif
+endif
-cssdir = $(pkgdatadir)
-css_DATA = style.css
-
-prefsdir = $(pkgdatadir)
-prefs_DATA = prefs.js
+if HAVE_GNOME3
+thumbnailerdir = $(datadir)/thumbnailers
+thumbnailer_DATA = gnome-web-photo.thumbnailer
+endif
EXTRA_DIST = \
- $(schema_in_files) \
- $(css_DATA) \
- $(prefs_DATA)
+ $(schema_in_files) \
+ $(thumbnailer_DATA)
CLEANFILES = $(schema_DATA)
DISTCLEANFILES = $(schema_DATA)
diff --git a/data/gnome-web-photo.thumbnailer b/data/gnome-web-photo.thumbnailer
new file mode 100644
index 0000000..732c463
--- /dev/null
+++ b/data/gnome-web-photo.thumbnailer
@@ -0,0 +1,4 @@
+[Thumbnailer Entry]
+TryExec=gnome-web-photo
+Exec=gnome-web-photo --mode=thumbnail --timeout=16 --thumbnail-size=%s %u %o
+MimeType=text/html;application/xhtml+xml;
diff --git a/data/thumbnailer.schemas.in b/data/thumbnailer.schemas.in
index 9db40c1..f3a3d65 100644
--- a/data/thumbnailer.schemas.in
+++ b/data/thumbnailer.schemas.in
@@ -16,7 +16,7 @@
<applyto>/desktop/gnome/thumbnailers/text html/command</applyto>
<owner>gnome-web-photo</owner>
<type>string</type>
- <default>gnome-web-photo --mode=thumbnail --timeout=16 --size=%s %u %o</default>
+ <default>gnome-web-photo --mode=thumbnail --timeout=16 --thumbnail-size=%s %u %o</default>
<locale name="C">
<short>The command to thumbnail HTML files</short>
<long>The command to thumbnail HTML files.</long>
@@ -38,7 +38,7 @@
<applyto>/desktop/gnome/thumbnailers/application xhtml@xml/command</applyto>
<owner>gnome-web-photo</owner>
<type>string</type>
- <default>gnome-web-photo --mode=thumbnail --timeout=16 --size=%s %u %o</default>
+ <default>gnome-web-photo --mode=thumbnail --timeout=16 --thumbnail-size=%s %u %o</default>
<locale name="C">
<short>The command to thumbnail XHTML files</short>
<long>The command to thumbnail XHTML files.</long>
diff --git a/src/Makefile.am b/src/Makefile.am
index 55e0f10..d310681 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,67 +1,21 @@
bin_PROGRAMS = gnome-web-photo
-gecko_include_subdirs = \
- . \
- content \
- docshell \
- dom \
- embed_base \
- gfx \
- gtkembedmoz \
- layout \
- locale \
- necko \
- pipnss \
- pref \
- uriloader \
- view \
- webbrwsr \
- widget \
- windowwatcher \
- xpcom
-
gnome_web_photo_SOURCES = \
- Components.cpp \
- Components.h \
- Embed.cpp \
- Embed.h \
- Listener.cpp \
- Listener.h \
- Prefs.cpp \
- Prefs.h \
- Printer.cpp \
- Printer.h \
- Writer.cpp \
- Writer.h \
- main.cpp
+ gnome-web-photo.c
gnome_web_photo_CPPFLAGS = \
$(DEPENDENCY_CFLAGS) \
- $(addprefix -I$(GECKO_INCLUDE_ROOT)/,$(gecko_include_subdirs)) \
- -DSHARE_DIR=\"$(pkgdatadir)\" \
-DLOCALEDIR=\"$(datadir)/locale\" \
- -DGECKO_HOME=\"$(GECKO_HOME)\" \
- -DGECKO_PREFIX=\"$(GECKO_PREFIX)\" \
- -DXPCOM_GLUE_USE_NSPR \
- -DXPCOM_GLUE \
$(AM_CPPFLAGS)
-gnome_web_photo_CXXFLAGS = \
- $(DEPENDENCY_CFLAGS) \
- $(LIBXUL_INCLUDES) \
- $(LIBXUL_CXXFLAGS) \
- -UGTK_DISABLE_DEPRECATED\
- $(AM_CXXFLAGS)
+gnome_web_photo_CFLAGS = \
+ $(AM_CFLAGS)
gnome_web_photo_LDFLAGS = \
- -R$(LIBXUL_LIBDIR) \
$(AM_LDFLAGS)
gnome_web_photo_LDADD = \
- $(DEPENDENCY_LIBS) \
- $(JPEG_LIBS) \
- $(LIBXUL_LIBS) \
- -lxpcomglue
+ $(DEPENDENCY_LIBS)
install-exec-hook: gnome-web-photo
cd $(DESTDIR)$(bindir) && \
diff --git a/src/gnome-web-photo.c b/src/gnome-web-photo.c
new file mode 100644
index 0000000..0a9d895
--- /dev/null
+++ b/src/gnome-web-photo.c
@@ -0,0 +1,971 @@
+/*
+ * Copyright (C) 2011 Novell, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1, 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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.
+ *
+ * Authors:
+ * Vincent Untz <vuntz gnome org>
+ *
+ * A small part of the code is based on the original xulrunner-based
+ * gnome-web-photo (src/main.cpp), which was released under LGPLv2.1+. The
+ * copyright of this code was:
+ * Copyright (C) 2005 Christian Persch
+ */
+
+/* TODO:
+ * - PHOTO_MODE doesn't work for very tall pages (like pgo, see comment before
+ * gtk_widget_set_size_request()).
+ * - Correctly define GETTEXT_PACKAGE (via config.h)
+ */
+
+/* Build with either:
+ * gcc -g -Wall -DHAVE_GNOME3 `pkg-config --cflags --libs gtk+-3.0 webkitgtk-3.0` gnome-web-photo.c -o gnome-web-photo
+ * or:
+ * gcc -g -Wall `pkg-config --cflags --libs gconf-2.0 gtk+-2.0 webkit-1.0` gnome-web-photo.c -o gnome-web-photo
+ */
+
+#include "config.h"
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+#include <webkit/webkit.h>
+
+#ifdef HAVE_GNOME3
+#include <gio/gio.h>
+#include <cairo/cairo-xlib.h>
+#else
+#include <gconf/gconf-client.h>
+#endif
+
+#ifdef HAVE_GNOME3
+#define GSETTINGS_DESKTOP_INTERFACE "org.gnome.desktop.interface"
+#define GSETTINGS_VARIABLE_FONT_KEY "document-font-name"
+#define GSETTINGS_MONOSPACE_FONT_KEY "monospace-font-name"
+#else
+#define GCONF_VARIABLE_FONT_KEY "/desktop/gnome/interface/document_font_name"
+#define GCONF_MONOSPACE_FONT_KEY "/desktop/gnome/interface/monospace_font_name"
+#define GCONF_EPHY_MINIMUM_FONT_SIZE "/apps/epiphany/web/minimum_font_size"
+#endif
+
+#define DEFAULT_VARIABLE_FONT "Sans 10"
+#define DEFAULT_MONOSPACE_FONT "Monospace 10"
+#define DEFAULT_MINIMUM_SIZE 7
+
+#define MIN_WIDTH 64
+#define MAX_WIDTH 2048
+#define DEFAULT_WIDTH 1024
+
+/* Value to keep in sync with the one from cairo:
+ * MAX_IMAGE_SIZE in cairo-image-surface.c */
+#define MAX_HEIGHT 32767
+
+#define DEFAULT_THUMBNAIL_SIZE 256
+
+typedef enum
+{
+ MODE_PHOTO,
+ MODE_THUMBNAIL,
+ MODE_PRINT,
+ MODE_LAST,
+ MODE_INVALID = MODE_LAST
+} PhotoMode;
+
+static struct {
+ const char *name;
+ PhotoMode mode;
+} modes[] = {
+ { "photo", MODE_PHOTO },
+ { "thumbnail", MODE_THUMBNAIL },
+ { "print", MODE_PRINT }
+};
+
+static PhotoMode parsed_mode = MODE_INVALID;
+
+typedef struct {
+ char *uri;
+ char *outfile;
+
+ PhotoMode mode;
+
+ int width;
+ int thumbnail_size;
+ gboolean print_background;
+
+ int timeout;
+ gboolean force;
+
+ gboolean error;
+
+ GtkWidget *window;
+ WebKitWebView *webview;
+ guint idle_id;
+ guint timeout_id;
+} PhotoData;
+
+
+/******************\
+ * Setup Web View *
+\******************/
+
+static gboolean
+_on_new_window (WebKitWebView *webview,
+ WebKitWebFrame *frame,
+ WebKitNetworkRequest *request,
+ WebKitWebNavigationAction *action,
+ WebKitWebPolicyDecision *decision)
+{
+ webkit_web_policy_decision_ignore (decision);
+ return TRUE;
+}
+
+static gboolean
+_on_print_requested (WebKitWebView *webview,
+ WebKitWebFrame *frame)
+{
+ return TRUE;
+}
+
+static gboolean
+_on_console_message (WebKitWebView *webview,
+ const gchar *message,
+ guint line_number,
+ const gchar *source_id)
+{
+ return TRUE;
+}
+
+static gboolean
+_on_script_alert (WebKitWebView *webview,
+ WebKitWebFrame *frame,
+ const gchar *message)
+{
+ return TRUE;
+}
+
+static gboolean
+_on_script_confirm (WebKitWebView *webview,
+ WebKitWebFrame *frame,
+ const gchar *message,
+ gboolean *confirmed)
+{
+ *confirmed = FALSE;
+ return TRUE;
+}
+
+static gboolean
+_on_script_prompt (WebKitWebView *webview,
+ WebKitWebFrame *frame,
+ const gchar *message,
+ const gchar *default_txt,
+ const gchar **text)
+{
+ *text = NULL;
+ return TRUE;
+}
+
+static gboolean
+_on_geolocation (WebKitWebView *webview,
+ WebKitWebFrame *frame,
+ WebKitGeolocationPolicyDecision *decision)
+{
+ webkit_geolocation_policy_deny (decision);
+ return TRUE;
+}
+
+static void
+_prepare_web_view (WebKitWebView *webview)
+{
+ g_signal_connect (webview, "new-window-policy-decision-requested",
+ G_CALLBACK (_on_new_window), NULL);
+ g_signal_connect (webview, "print-requested",
+ G_CALLBACK (_on_print_requested), NULL);
+ g_signal_connect (webview, "console-message",
+ G_CALLBACK (_on_console_message), NULL);
+ g_signal_connect (webview, "script-alert",
+ G_CALLBACK (_on_script_alert), NULL);
+ g_signal_connect (webview, "script-confirm",
+ G_CALLBACK (_on_script_confirm), NULL);
+ g_signal_connect (webview, "script-prompt",
+ G_CALLBACK (_on_script_prompt), NULL);
+ g_signal_connect (webview, "geolocation-policy-decision-requested",
+ G_CALLBACK (_on_geolocation), NULL);
+}
+
+
+/******************\
+ * Setup Settings *
+\******************/
+
+static void
+_parse_font (const char *font_descr,
+ const char *default_descr,
+ char **font_name,
+ int *font_size)
+{
+ PangoFontDescription* pango_descr;
+
+ if (!font_descr)
+ font_descr = default_descr;
+
+ pango_descr = pango_font_description_from_string (font_descr);
+
+ if (!pango_descr && default_descr) {
+ _parse_font (default_descr, NULL, font_name, font_size);
+ return;
+ }
+
+ *font_name = g_strdup (pango_font_description_get_family (pango_descr));
+ *font_size = pango_font_description_get_size (pango_descr);
+
+ if (pango_font_description_get_size_is_absolute (pango_descr) == FALSE)
+ *font_size /= PANGO_SCALE;
+
+ pango_font_description_free (pango_descr);
+}
+
+static void
+_prepare_web_settings (WebKitWebSettings *settings,
+ gboolean print_background)
+{
+#ifdef HAVE_GNOME3
+ GSettings *gsettings;
+#else
+ GConfClient *client;
+#endif
+ char *value;
+ char *font_name = NULL;
+ int font_size = 0;
+
+ /* Various settings */
+
+ g_object_set (G_OBJECT (settings),
+ /* printing settings */
+ "print-backgrounds", print_background,
+ /* don't save anything from this to the global history */
+ "enable-private-browsing", TRUE,
+ /* shouldn't be needed */
+ "enable-html5-database", FALSE,
+ "enable-html5-local-storage", FALSE,
+ /* no automatic popup or other similar behavior */
+ "javascript-can-open-windows-automatically", FALSE,
+ "auto-resize-window", FALSE,
+ /* ensure secure settings */
+ "javascript-can-access-clipboard", FALSE,
+ "enable-universal-access-from-file-uris", FALSE,
+ NULL);
+
+ /* Fetch fonts from user config */
+
+#ifdef HAVE_GNOME3
+ gsettings = g_settings_new (GSETTINGS_DESKTOP_INTERFACE);
+#else
+ client = gconf_client_get_default ();
+#endif
+
+#ifdef HAVE_GNOME3
+ value = g_settings_get_string (gsettings, GSETTINGS_VARIABLE_FONT_KEY);
+#else
+ value = gconf_client_get_string (client, GCONF_VARIABLE_FONT_KEY, NULL);
+#endif
+ _parse_font (value, DEFAULT_VARIABLE_FONT, &font_name, &font_size);
+ g_free (value);
+
+ g_object_set (G_OBJECT (settings),
+ "default-font-family", font_name,
+ "default-font-size", font_size,
+ "sans-serif-font-family", font_name,
+ NULL);
+ g_free (font_name);
+
+#ifdef HAVE_GNOME3
+ value = g_settings_get_string (gsettings, GSETTINGS_MONOSPACE_FONT_KEY);
+#else
+ value = gconf_client_get_string (client, GCONF_MONOSPACE_FONT_KEY, NULL);
+#endif
+ _parse_font (value, DEFAULT_MONOSPACE_FONT, &font_name, &font_size);
+ g_free (value);
+
+ g_object_set (G_OBJECT (settings),
+ "monospace-font-family", font_name,
+ "default-monospace-font-size", font_size,
+ NULL);
+ g_free (font_name);
+
+#ifdef HAVE_GNOME3
+ /* We can't assume the GSettings schemas for epiphany are installed */
+ font_size = DEFAULT_MINIMUM_SIZE;
+#else
+ font_size = gconf_client_get_int (client, GCONF_EPHY_MINIMUM_FONT_SIZE, NULL);
+#endif
+ if (font_size == 0)
+ font_size = DEFAULT_MINIMUM_SIZE;
+
+ g_object_set (G_OBJECT (settings),
+ "minimum-font-size", font_size,
+ NULL);
+
+#ifdef HAVE_GNOME3
+ g_object_unref (gsettings);
+#else
+ g_object_unref (client);
+#endif
+}
+
+
+/*****************\
+ * General Setup *
+\*****************/
+
+static void
+_prepare_webkit (WebKitWebView *webview,
+ WebKitWebSettings *settings,
+ gboolean print_background)
+{
+ SoupSession* session = webkit_get_default_session();
+
+ /* We don't want auth dialogs */
+ soup_session_remove_feature_by_type (session, WEBKIT_TYPE_SOUP_AUTH_DIALOG);
+
+ _prepare_web_view (webview);
+ _prepare_web_settings (settings, print_background);
+}
+
+
+/******************************************\
+ * GtkOffscreenWindow with limited height *
+\******************************************/
+
+/* This is a GtkOffscreenWindow with a maximum height.
+ * See comment above gtk_widget_set_size_request() call to understand why we
+ * need this. */
+
+typedef struct _PhotoOffscreenWindow PhotoOffscreenWindow;
+typedef struct _PhotoOffscreenWindowClass PhotoOffscreenWindowClass;
+
+struct _PhotoOffscreenWindow
+{
+ GtkOffscreenWindow parent_object;
+};
+
+struct _PhotoOffscreenWindowClass
+{
+ GtkOffscreenWindowClass parent_class;
+};
+
+GType photo_offscreen_window_get_type (void) G_GNUC_CONST;
+
+G_DEFINE_TYPE (PhotoOffscreenWindow, photo_offscreen_window, GTK_TYPE_OFFSCREEN_WINDOW)
+
+#ifdef HAVE_GNOME3
+static void
+photo_offscreen_window_get_preferred_height (GtkWidget *widget,
+ gint *minimum,
+ gint *natural)
+{
+ GTK_WIDGET_CLASS (photo_offscreen_window_parent_class)->get_preferred_height (widget, minimum, natural);
+
+ *minimum = MIN (*minimum, MAX_HEIGHT);
+ *natural = MIN (*natural, MAX_HEIGHT);
+}
+#else
+static void
+photo_offscreen_window_size_request (GtkWidget *widget,
+ GtkRequisition *requisition)
+{
+ GTK_WIDGET_CLASS (photo_offscreen_window_parent_class)->size_request (widget, requisition);
+
+ requisition->height = MIN (requisition->height, MAX_HEIGHT);
+}
+#endif
+
+static void
+photo_offscreen_window_class_init (PhotoOffscreenWindowClass *class)
+{
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
+
+#ifdef HAVE_GNOME3
+ widget_class->get_preferred_height = photo_offscreen_window_get_preferred_height;
+#else
+ widget_class->size_request = photo_offscreen_window_size_request;
+#endif
+}
+
+static void
+photo_offscreen_window_init (PhotoOffscreenWindow *window)
+{
+}
+
+static GtkWidget *
+photo_offscreen_window_new (void)
+{
+ return g_object_new (photo_offscreen_window_get_type (), NULL);
+}
+
+/************\
+ * Core *
+\************/
+
+static void
+_write_photo (PhotoData *data)
+{
+#ifdef HAVE_GNOME3
+ cairo_surface_t *surface;
+ cairo_status_t status;
+
+ surface = gtk_offscreen_window_get_surface (GTK_OFFSCREEN_WINDOW (data->window));
+ status = cairo_surface_write_to_png (surface, data->outfile);
+
+ switch (status) {
+ case CAIRO_STATUS_SUCCESS:
+ break;
+ default:
+ data->error = TRUE;
+ /* Translators: first %s is a URI */
+ g_printerr (_("Error while saving '%s': %s\n"),
+ data->uri, cairo_status_to_string (status));
+ break;
+ }
+#else
+ GdkPixbuf *pixbuf;
+ GError *error = NULL;
+
+ pixbuf = gtk_offscreen_window_get_pixbuf (GTK_OFFSCREEN_WINDOW (data->window));
+ gdk_pixbuf_save (pixbuf, data->outfile, "png", &error, NULL);
+ g_object_unref (pixbuf);
+
+ if (error) {
+ data->error = TRUE;
+ /* Translators: first %s is a URI */
+ g_printerr (_("Error while saving '%s': %s\n"),
+ data->uri, error->message);
+ g_error_free (error);
+ }
+#endif
+}
+
+static void
+_write_thumbnail (PhotoData *data)
+{
+#ifdef HAVE_GNOME3
+ cairo_surface_t *surface;
+ cairo_surface_t *thumb_surface;
+ cairo_t *cr;
+ cairo_status_t status;
+ int width;
+ int height;
+ int thumb_width;
+ int thumb_height;
+
+ surface = gtk_offscreen_window_get_surface (GTK_OFFSCREEN_WINDOW (data->window));
+ width = cairo_xlib_surface_get_width (surface);
+ height = cairo_xlib_surface_get_height (surface);
+
+ /* Too tall? It'll be a square */
+ if (height > width) {
+ height = width;
+ }
+
+ thumb_width = data->thumbnail_size;
+ thumb_height = (data->thumbnail_size * height) / (double) width;
+
+ thumb_surface = cairo_surface_create_similar (surface, CAIRO_CONTENT_COLOR,
+ thumb_width, thumb_height);
+
+ cr = cairo_create (thumb_surface);
+ cairo_scale (cr,
+ data->thumbnail_size / (double) width,
+ data->thumbnail_size / (double) width);
+ cairo_set_source_surface (cr, surface, 0, 0);
+ cairo_paint (cr);
+ cairo_destroy (cr);
+
+ status = cairo_surface_write_to_png (thumb_surface, data->outfile);
+
+ cairo_surface_destroy (thumb_surface);
+
+ switch (status) {
+ case CAIRO_STATUS_SUCCESS:
+ break;
+ default:
+ data->error = TRUE;
+ /* Translators: first %s is a URI */
+ g_printerr (_("Error while thumbnailing '%s': %s\n"),
+ data->uri, cairo_status_to_string (status));
+ break;
+ }
+#else
+ GdkPixbuf *pixbuf;
+ GdkPixbuf *thumb_pixbuf;
+ GError *error = NULL;
+ int width;
+ int height;
+ int thumb_width;
+ int thumb_height;
+
+ pixbuf = gtk_offscreen_window_get_pixbuf (GTK_OFFSCREEN_WINDOW (data->window));
+ width = gdk_pixbuf_get_width (pixbuf);
+ height = gdk_pixbuf_get_height (pixbuf);
+
+ /* Too tall? It'll be a square */
+ if (height > width) {
+ GdkPixbuf *subpixbuf;
+
+ subpixbuf = gdk_pixbuf_new_subpixbuf (pixbuf, 0, 0, width, width);
+ g_object_unref (pixbuf);
+
+ pixbuf = subpixbuf;
+ height = width;
+ }
+
+ thumb_width = data->thumbnail_size;
+ thumb_height = (data->thumbnail_size * height) / (double) width;
+
+ thumb_pixbuf = gdk_pixbuf_scale_simple (pixbuf,
+ thumb_width, thumb_height,
+ GDK_INTERP_BILINEAR);
+
+ gdk_pixbuf_save (thumb_pixbuf, data->outfile, "png", &error, NULL);
+
+ g_object_unref (pixbuf);
+ g_object_unref (thumb_pixbuf);
+
+ if (error) {
+ data->error = TRUE;
+ /* Translators: first %s is a URI */
+ g_printerr (_("Error while thumbnailing '%s': %s\n"),
+ data->uri, error->message);
+ g_error_free (error);
+ }
+#endif
+}
+
+static void
+_print_photo (PhotoData *data)
+{
+ GtkPrintOperation *operation;
+ WebKitWebFrame *main_frame;
+ GError *error = NULL;
+
+ operation = gtk_print_operation_new ();
+ gtk_print_operation_set_export_filename (operation, data->outfile);
+
+ main_frame = webkit_web_view_get_main_frame (data->webview);
+
+ webkit_web_frame_print_full (main_frame, operation,
+ GTK_PRINT_OPERATION_ACTION_EXPORT, &error);
+
+ if (error) {
+ data->error = TRUE;
+ /* Translators: first %s is a URI */
+ g_printerr (_("Error while printing '%s': %s\n"),
+ data->uri, error->message);
+ g_error_free (error);
+ }
+
+ g_object_unref (operation);
+}
+
+static void
+_do_action (PhotoData *data)
+{
+ switch (data->mode) {
+ case MODE_PHOTO:
+ _write_photo (data);
+ break;
+ case MODE_THUMBNAIL:
+ _write_thumbnail (data);
+ break;
+ case MODE_PRINT:
+ _print_photo (data);
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+}
+
+static gboolean
+_on_web_view_load_error (WebKitWebView *webview,
+ WebKitWebFrame *frame,
+ const char *uri,
+ GError *error,
+ PhotoData *data)
+{
+ /* Cancelling is explicitly done by us, so we don't do anything here */
+ if (error->domain == WEBKIT_NETWORK_ERROR &&
+ error->code == WEBKIT_NETWORK_ERROR_CANCELLED)
+ return TRUE;
+
+ data->error = TRUE;
+ /* Translators: first %s is a URI */
+ g_printerr (_("Error while loading '%s': %s\n"), uri, error->message);
+
+ gtk_main_quit ();
+
+ return TRUE;
+}
+
+static gboolean
+_web_view_loaded_idle (PhotoData *data)
+{
+ data->idle_id = 0;
+ if (data->timeout_id > 0) {
+ g_source_remove (data->timeout_id);
+ data->timeout_id = 0;
+ }
+
+ _do_action (data);
+
+ gtk_main_quit ();
+
+ return FALSE;
+}
+
+static void
+_on_web_view_load_status (WebKitWebView *webview,
+ GParamSpec *pspec,
+ PhotoData *data)
+{
+ switch (webkit_web_view_get_load_status (webview)) {
+ case WEBKIT_LOAD_FINISHED:
+ /* For local files, we finish the load so fast that the page is not even
+ * rendered. Going back to the idle loop fixes this. */
+ g_assert (data->idle_id == 0);
+ data->idle_id = g_idle_add ((GSourceFunc) _web_view_loaded_idle, data);
+ break;
+
+ case WEBKIT_LOAD_FAILED:
+ /* Ignore since we'll have the load-error event */
+ break;
+
+ default:
+ break;
+ }
+}
+
+static gboolean
+_on_timeout (PhotoData *data)
+{
+ data->timeout_id = 0;
+ if (data->idle_id > 0) {
+ g_source_remove (data->idle_id);
+ data->idle_id = 0;
+ }
+
+ if (data->force) {
+ switch (webkit_web_view_get_load_status (data->webview)) {
+ case WEBKIT_LOAD_FIRST_VISUALLY_NON_EMPTY_LAYOUT:
+ case WEBKIT_LOAD_FINISHED:
+ /* Translators: first %s is a URI */
+ g_printerr (_("Timed out while loading '%s'. Outputting current view...\n"), data->uri);
+ _do_action (data);
+ break;
+
+ default:
+ data->error = TRUE;
+ /* Translators: first %s is a URI */
+ g_printerr (_("Timed out while loading '%s'. Nothing to output...\n"), data->uri);
+ break;
+ }
+ } else {
+ data->error = TRUE;
+ /* Translators: first %s is a URI */
+ g_printerr (_("Timed out while loading '%s'.\n"), data->uri);
+ }
+
+ /* We have to do it after checking the load status */
+ webkit_web_view_stop_loading (data->webview);
+
+ gtk_main_quit ();
+
+ return FALSE;
+}
+
+static GtkWidget *
+_create_web_window (PhotoData *data)
+{
+ GtkWidget *window;
+ GtkWidget *webview;
+ WebKitWebSettings *settings;
+
+ window = photo_offscreen_window_new ();
+ data->window = window;
+
+ /* We don't specify a height: if we did so, we'd be forcing the height of the
+ * output, which is not desirable since the page could be less tall than
+ * that.
+ * But on the other hand, if the page is too tall (eg, planet.gnome.org), we
+ * can't handle it completely and we run out of memory before being able to
+ * use it. This is why we use PhotoOffscreenWindow, which limits the maximum
+ * height of the page.
+ * This means we won't have the whole page "displayed" in the offscreen
+ * window if it's too tall. This is not an issue for MODE_THUMBNAIL (we don't
+ * need the whole page), nor for MODE_PRINT (the print operation is not
+ * related to what is displayed). So it only affects MODE_PHOTO. But it's
+ * better than not getting anything anyway. */
+ gtk_widget_set_size_request (window, data->width, -1);
+
+ webview = webkit_web_view_new ();
+ data->webview = WEBKIT_WEB_VIEW (webview);
+
+ settings = webkit_web_settings_new ();
+
+ _prepare_webkit (data->webview, settings, data->print_background);
+
+ webkit_web_view_set_settings (data->webview, settings);
+
+ gtk_container_add (GTK_CONTAINER(window), webview);
+ gtk_widget_show_all (window);
+
+ g_signal_connect (webview, "load-error",
+ G_CALLBACK (_on_web_view_load_error), data);
+ g_signal_connect (webview, "notify::load-status",
+ G_CALLBACK (_on_web_view_load_status), data);
+
+ if (data->timeout > 0)
+ data->timeout_id = g_timeout_add_seconds (data->timeout,
+ (GSourceFunc) _on_timeout, data);
+
+ webkit_web_view_open (data->webview, data->uri);
+
+ return window;
+}
+
+
+/************************\
+ * Command-line options *
+\************************/
+
+static gboolean
+_parse_mode (const gchar *option_name,
+ const gchar *value,
+ gpointer data,
+ GError **error)
+{
+ g_assert (value != NULL);
+
+ guint i;
+ for (i = 0; i <= G_N_ELEMENTS (modes); i++) {
+ if (g_ascii_strcasecmp (value, modes[i].name) == 0) {
+ parsed_mode = modes[i].mode;
+ break;
+ }
+ }
+
+ if (i == MODE_INVALID) {
+ *error = g_error_new (G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
+ _("Unknown mode '%s'"), value);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+_print_synopsis (void)
+{
+ const char *name = g_get_prgname ();
+ g_assert (name != NULL);
+
+ g_print (_("Usage: %s [--mode=photo|thumbnail|print] [...]\n"), name);
+
+ switch (parsed_mode) {
+ case MODE_PHOTO:
+ g_print (_("Usage: %s [-t TIMEOUT] [--force] [-w WIDTH] [--file] URI|FILE OUTFILE\n"), name);
+ break;
+ case MODE_THUMBNAIL:
+ g_print (_("Usage: %s [-t TIMEOUT] [--force] [-w WIDTH] [-s THUMBNAILSIZE] [--file] URI|FILE OUTFILE\n"), name);
+ break;
+ case MODE_PRINT:
+ g_print (_("Usage: %s [-t TIMEOUT] [--force] [-w WIDTH] [--print-background] [--file] URI|FILE OUTFILE\n"), name);
+ break;
+ default:
+ break;
+ }
+}
+
+int
+main (int argc,
+ char **argv)
+{
+ GOptionContext *context;
+ GError *error = NULL;
+ char **arguments = NULL;
+ gboolean is_file = FALSE;
+ GtkWidget *window;
+
+ PhotoData data = { NULL, NULL,
+ MODE_INVALID,
+ /* thumbnail_size set to -1 to be able to check if option
+ * was passed or not */
+ DEFAULT_WIDTH, -1, FALSE,
+ 60, FALSE,
+ FALSE,
+ NULL, NULL, 0, 0 };
+
+ const GOptionEntry main_options[] =
+ {
+ { "mode", 'm', 0, G_OPTION_ARG_CALLBACK, (void*) _parse_mode,
+ N_("Operation mode [photo|thumbnail|print]"), NULL },
+ { "timeout", 't', 0, G_OPTION_ARG_INT, &data.timeout,
+ N_("Timeout in seconds, or 0 to disable timeout (default: 60)"),
+ /* Translators: T will appear in the help, as in: --timeout=T */
+ N_("T") },
+ { "force", 'f', 0, G_OPTION_ARG_NONE, &data.force,
+ N_("Force output when timeout expires, even if the page is not fully loaded"), NULL },
+ { "width", 'w', 0, G_OPTION_ARG_INT, &data.width,
+ N_("Desired width of the web page (default: 1024)"),
+ /* Translators: W will appear in the help, as in: --width=W */
+ N_("W") },
+ { "thumbnail-size", 's', 0, G_OPTION_ARG_INT, &data.thumbnail_size,
+ N_("Thumbnail size (default: 256)"),
+ /* Translators: S will appear in the help, as in: --thumbnail-size=S */
+ N_("S") },
+ { "print-background", 0, 0, G_OPTION_ARG_NONE, &data.print_background,
+ N_("Print background images and colours (default: false)"), NULL },
+ { "file", 0, 0, G_OPTION_ARG_NONE, &is_file,
+ N_("Argument is a file and not a URI"), NULL },
+ { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &arguments, "", NULL },
+ { NULL }
+ };
+
+#ifdef ENABLE_NLS
+ /* Initialize the i18n stuff */
+ bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
+ bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+ textdomain (GETTEXT_PACKAGE);
+#endif
+
+ /* Now parse the arguments */
+ context = g_option_context_new (NULL);
+ g_option_context_set_translation_domain (context, GETTEXT_PACKAGE);
+ g_option_context_add_main_entries (context, main_options, GETTEXT_PACKAGE);
+
+ /* This will initialize GTK+ too */
+ g_option_context_add_group (context, gtk_get_option_group (TRUE));
+
+ if (!g_option_context_parse (context, &argc, &argv, &error)) {
+ g_printerr ("%s\n", error->message);
+ g_error_free (error);
+ g_option_context_free (context);
+ _print_synopsis ();
+ return 1;
+ }
+ g_option_context_free (context);
+
+ data.mode = parsed_mode;
+
+ /* Mode not explicitly specified, derive from invocation filename */
+ if (data.mode == MODE_INVALID) {
+ const char *program = g_get_prgname ();
+ g_assert (program != NULL);
+
+ if (g_ascii_strcasecmp (program, "gnome-web-thumbnail") == 0) {
+ data.mode = MODE_THUMBNAIL;
+ } else if (g_ascii_strcasecmp (program, "gnome-web-print") == 0) {
+ data.mode = MODE_PRINT;
+ } else {
+ data.mode = MODE_PHOTO;
+ }
+ }
+
+ /* Renice when thumbnailing, since it is likely that we are called multiple
+ * times in sequence in order to thumbnail a bunch of HTML files. */
+ if (data.mode == MODE_THUMBNAIL) {
+ nice (5);
+ }
+
+ /* Check timeout */
+ if (data.timeout < 0) {
+ g_printerr (_("--timeout cannot be negative!\n"));
+ _print_synopsis ();
+ return 1;
+ }
+
+ /* Check thumbnail size */
+ if (data.mode == MODE_THUMBNAIL) {
+ if (data.thumbnail_size == -1)
+ data.thumbnail_size = DEFAULT_THUMBNAIL_SIZE;
+
+ if (data.thumbnail_size != 32 &&
+ data.thumbnail_size != 64 &&
+ data.thumbnail_size != 96 &&
+ data.thumbnail_size != 128 &&
+ data.thumbnail_size != 256) {
+ g_printerr (_("--size can only be 32, 64, 96, 128 or 256!\n"));
+ return 1;
+ }
+ } else if (data.thumbnail_size != -1) {
+ g_printerr (_("--size is only available in thumbnail mode!\n"));
+ _print_synopsis ();
+ return 1;
+ }
+
+ /* Check width */
+ if (data.width < MIN_WIDTH || data.width > MAX_WIDTH) {
+ g_printerr (_("--width out of bounds; must be between %d and %d!\n"), MIN_WIDTH, MAX_WIDTH);
+ return 1;
+ }
+
+ if (data.mode == MODE_THUMBNAIL && (data.width % 32) != 0) {
+ g_printerr (_("--width must be a multiple of 32 in thumbnail mode!\n"));
+ return 1;
+ }
+
+ /* Check --print-background */
+ if (data.mode != MODE_PRINT && data.print_background) {
+ g_printerr (_("--print-background is only available in print mode!\n"));
+ _print_synopsis ();
+ return 1;
+ }
+
+ /* Check uri/output */
+ if (!arguments || g_strv_length (arguments) < 2) {
+ g_printerr (_("Missing arguments!\n"));
+ _print_synopsis ();
+ return 1;
+ }
+
+ if (g_strv_length (arguments) > 2) {
+ g_printerr (_("Too many arguments!\n"));
+ _print_synopsis ();
+ return 1;
+ }
+
+ /* Replace filename with URI */
+ if (is_file) {
+ GFile *file;
+
+ file = g_file_new_for_commandline_arg (arguments[0]);
+
+ g_free (arguments[0]);
+ arguments[0] = g_file_get_uri (file);
+
+ g_object_unref (file);
+ }
+
+ /* Now let's do the work! */
+ data.uri = arguments[0];
+ data.outfile = arguments[1];
+
+ window = _create_web_window (&data);
+
+ gtk_main ();
+
+ gtk_widget_destroy (window);
+
+ return data.error ? 1 : 0;
+}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]