[gnome-utils] font-viewer: import from gnome-control-center (#632205)



commit 78a49680d858c0d1cfe7926fb3befc0fac8299b9
Author: Cosimo Cecchi <cosimoc gnome org>
Date:   Wed Nov 3 10:28:34 2010 +0100

    font-viewer: import from gnome-control-center (#632205)
    
    Also, port it to GTK+3/cairo rendering, and cleanup the coding style to
    be more consistent.
    
    Thanks to Rodrigo Moya for the initial patch.

 Makefile.am                                 |    1 +
 configure.ac                                |    3 +
 font-viewer/Makefile.am                     |   70 ++++
 font-viewer/font-thumbnailer.c              |  436 ++++++++++++++++++++
 font-viewer/font-view.c                     |  584 +++++++++++++++++++++++++++
 font-viewer/fontilus.schemas.in             |  109 +++++
 font-viewer/ftstream-vfs.c                  |  152 +++++++
 font-viewer/gnome-font-viewer.desktop.in.in |   15 +
 font-viewer/totem-resources.c               |  123 ++++++
 font-viewer/totem-resources.h               |   33 ++
 10 files changed, 1526 insertions(+), 0 deletions(-)
---
diff --git a/Makefile.am b/Makefile.am
index 169c53a..c553ec3 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -25,6 +25,7 @@ SUBDIRS = \
 	po			\
 	libeggsmclient		\
 	baobab			\
+	font-viewer		\
 	gnome-dictionary	\
 	gnome-screenshot	\
 	gsearchtool		\
diff --git a/configure.ac b/configure.ac
index b5eda58..50ad0a5 100644
--- a/configure.ac
+++ b/configure.ac
@@ -398,6 +398,9 @@ AC_CONFIG_FILES([
         gnome-dictionary/src/Makefile
 
         gnome-screenshot/Makefile
+
+	font-viewer/Makefile
+	font-viewer/gnome-font-viewer.desktop.in
 ])
 
 AC_OUTPUT
diff --git a/font-viewer/Makefile.am b/font-viewer/Makefile.am
new file mode 100644
index 0000000..5757064
--- /dev/null
+++ b/font-viewer/Makefile.am
@@ -0,0 +1,70 @@
+
+INCLUDES =					\
+	$(GIO_CFLAGS)				\
+	$(GTK_CFLAGS)				\
+	-DDIRECTORY_DIR=\"$(directorydir)\" 	\
+	-DGNOMELOCALEDIR=\"$(datadir)/locale\"
+
+bin_PROGRAMS = \
+	gnome-thumbnail-font \
+	gnome-font-viewer
+
+ftstream_SOURCES = \
+	ftstream-vfs.h \
+	ftstream-vfs.c
+
+gnome_thumbnail_font_LDADD = \
+	$(GIO_LIBS) \
+	$(GTK_LIBS)
+
+gnome_thumbnail_font_SOURCES = \
+	$(ftstream_SOURCES) \
+	font-thumbnailer.c \
+	totem-resources.c \
+	totem-resources.h
+
+gnome_font_viewer_LDADD = \
+	$(XFT_LIBS) \
+	$(GIO_LIBS) \
+	$(GTK_LIBS)
+
+gnome_font_viewer_SOURCES = \
+	$(ftstream_SOURCES) \
+	font-view.c
+
+schemasdir = $(GCONF_SCHEMA_FILE_DIR)
+schemas_DATA = fontilus.schemas
+
+desktopdir = $(datadir)/applications
+desktop_in_files = gnome-font-viewer.desktop.in
+desktop_DATA = $(desktop_in_files:.desktop.in=.desktop)
+
+install-data-local: install-gconf-schemas install-desktop-database
+
+if GCONF_SCHEMAS_INSTALL
+install-gconf-schemas:
+	if test -z "$(DESTDIR)"; then \
+	  GCONF_CONFIG_SOURCE=$(GCONF_SCHEMA_CONFIG_SOURCE) $(GCONFTOOL) --makefile-install-rule fontilus.schemas; \
+	fi
+else
+install-gconf-schemas:
+endif
+
+install-desktop-database: install-desktopDATA
+	update-desktop-database $(DESTDIR)$(desktopdir)
+
+uninstall-local:
+	rm -f $(DESTDIR)$(desktopdir)/mimeinfo.cache
+
+ INTLTOOL_DESKTOP_RULE@
+ INTLTOOL_DIRECTORY_RULE@
+ INTLTOOL_SCHEMAS_RULE@
+
+CLEANFILES = $(desktop_in_files) $(desktop_DATA) \
+  $(schemas_DATA) $(directory_DATA)
+
+EXTRA_DIST = \
+  fontilus.schemas.in \
+  gnome-font-viewer.desktop.in.in
+
+-include $(top_srcdir)/git.mk
diff --git a/font-viewer/font-thumbnailer.c b/font-viewer/font-thumbnailer.c
new file mode 100644
index 0000000..470670c
--- /dev/null
+++ b/font-viewer/font-thumbnailer.c
@@ -0,0 +1,436 @@
+/* -*- mode: C; c-basic-offset: 4 -*- */
+
+/*
+ * font-thumbnailer: a thumbnailer for font files, using FreeType
+ *
+ * Copyright (C) 2002-2003  James Henstridge <james daa com au>
+ *
+ * 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 Free Software Foundation; either version 2 of the License, 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <stdio.h>
+#include <locale.h>
+#include <ft2build.h>
+#include FT_FREETYPE_H
+
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <gio/gio.h>
+#include <glib/gi18n.h>
+
+#include "ftstream-vfs.h"
+#include "totem-resources.h"
+
+static const gchar *
+get_ft_error (FT_Error error)
+{
+#undef __FTERRORS_H__
+#define FT_ERRORDEF(e,v,s) case e: return s;
+#define FT_ERROR_START_LIST
+#define FT_ERROR_END_LIST
+    switch (error) {
+#include FT_ERRORS_H
+    default:
+	return "unknown";
+    }
+}
+
+#define FONT_SIZE 64
+#define PAD_PIXELS 4
+
+static void
+draw_bitmap (GdkPixbuf *pixbuf,
+	     FT_Bitmap *bitmap,
+	     gint off_x,
+	     gint off_y)
+{
+    guchar *buffer;
+    gint p_width, p_height, p_rowstride;
+    gint i, j;
+
+    buffer = gdk_pixbuf_get_pixels (pixbuf);
+    p_width = gdk_pixbuf_get_width (pixbuf);
+    p_height = gdk_pixbuf_get_height (pixbuf);
+    p_rowstride = gdk_pixbuf_get_rowstride (pixbuf);
+
+    for (j = 0; j < bitmap->rows; j++) {
+	if (j + off_y < 0 || j + off_y >= p_height)
+	    continue;
+
+	for (i = 0; i < bitmap->width; i++) {
+	    guchar pixel;
+	    gint pos;
+
+	    if (i + off_x < 0 || i + off_x >= p_width)
+		continue;
+
+	    switch (bitmap->pixel_mode) {
+	    case ft_pixel_mode_mono:
+		pixel = bitmap->buffer[j * bitmap->pitch + i / 8];
+		pixel = 255 - ((pixel >> (7 - i % 8)) & 0x1) * 255;
+		break;
+	    case ft_pixel_mode_grays:
+		pixel = 255 - bitmap->buffer[j * bitmap->pitch + i];
+		break;
+	    default:
+		pixel = 255;
+	    }
+	    pos = (j + off_y) * p_rowstride + 3 * (i + off_x);
+	    buffer[pos] = pixel;
+	    buffer[pos + 1] = pixel;
+	    buffer[pos + 2] = pixel;
+	}
+    }
+}
+
+static void
+draw_char (GdkPixbuf *pixbuf,
+	   FT_Face face,
+	   FT_UInt glyph_index,
+	   gint *pen_x,
+	   gint *pen_y)
+{
+    FT_Error error;
+    FT_GlyphSlot slot;
+
+    slot = face->glyph;
+
+    error = FT_Load_Glyph (face, glyph_index, FT_LOAD_DEFAULT);
+    if (error) {
+	g_printerr ("Could not load glyph index '%ud': %s\n", glyph_index,
+		    get_ft_error (error));
+	return;
+    }
+
+    error = FT_Render_Glyph (slot, ft_render_mode_normal);
+    if (error) {
+	g_printerr("Could not render glyph index '%ud': %s\n", glyph_index,
+		   get_ft_error (error));
+	return;
+    }
+
+    draw_bitmap (pixbuf, &slot->bitmap,
+		 *pen_x + slot->bitmap_left,
+		 *pen_y - slot->bitmap_top);
+
+    *pen_x += slot->advance.x >> 6;
+}
+
+static void
+save_pixbuf (GdkPixbuf *pixbuf,
+	     const gchar *filename)
+{
+    guchar *buffer;
+    gint p_width, p_height, p_rowstride;
+    gint i, j;
+    gint trim_left, trim_right, trim_top, trim_bottom;
+    GdkPixbuf *subpixbuf;
+
+    buffer = gdk_pixbuf_get_pixels (pixbuf);
+    p_width = gdk_pixbuf_get_width (pixbuf);
+    p_height = gdk_pixbuf_get_height (pixbuf);
+    p_rowstride = gdk_pixbuf_get_rowstride (pixbuf);
+
+    for (i = 0; i < p_width; i++) {
+	gboolean seen_pixel = FALSE;
+
+	for (j = 0; j < p_height; j++) {
+	    gint offset = j * p_rowstride + 3 * i;
+
+	    seen_pixel = (buffer[offset] != 0xff ||
+			  buffer[offset + 1] != 0xff ||
+			  buffer[offset + 2] != 0xff);
+
+	    if (seen_pixel)
+		break;
+	}
+
+	if (seen_pixel)
+	    break;
+    }
+
+    trim_left = MIN (p_width, i);
+    trim_left = MAX (trim_left - PAD_PIXELS, 0);
+
+    for (i = p_width - 1; i >= trim_left; i--) {
+	gboolean seen_pixel = FALSE;
+
+	for (j = 0; j < p_height; j++) {
+	    gint offset = j * p_rowstride + 3 * i;
+
+	    seen_pixel = (buffer[offset] != 0xff ||
+			  buffer[offset + 1] != 0xff ||
+			  buffer[offset + 2] != 0xff);
+
+	    if (seen_pixel)
+		break;
+	}
+
+	if (seen_pixel)
+	    break;
+    }
+
+    trim_right = MAX (trim_left, i);
+    trim_right = MIN (trim_right + PAD_PIXELS, p_width - 1);
+
+    for (j = 0; j < p_height; j++) {
+	gboolean seen_pixel = FALSE;
+
+	for (i = 0; i < p_width; i++) {
+	    gint offset = j * p_rowstride + 3 * i;
+
+	    seen_pixel = (buffer[offset] != 0xff ||
+			  buffer[offset + 1] != 0xff ||
+			  buffer[offset + 2] != 0xff);
+
+	    if (seen_pixel)
+		break;
+	}
+
+	if (seen_pixel)
+	    break;
+    }
+
+    trim_top = MIN (p_height, j);
+    trim_top = MAX (trim_top - PAD_PIXELS, 0);
+
+    for (j = p_height - 1; j >= trim_top; j--) {
+	gboolean seen_pixel = FALSE;
+
+	for (i = 0; i < p_width; i++) {
+	    gint offset = j * p_rowstride + 3 * i;
+
+	    seen_pixel = (buffer[offset] != 0xff ||
+			  buffer[offset + 1] != 0xff ||
+			  buffer[offset + 2] != 0xff);
+
+	    if (seen_pixel)
+		break;
+	}
+
+	if (seen_pixel)
+	    break;
+    }
+
+    trim_bottom = MAX (trim_top, j);
+    trim_bottom = MIN (trim_bottom + PAD_PIXELS, p_height - 1);
+
+    subpixbuf = gdk_pixbuf_new_subpixbuf (pixbuf,
+					  trim_left, trim_top,
+					  trim_right - trim_left,
+					  trim_bottom - trim_top);
+
+    gdk_pixbuf_save (subpixbuf, filename,
+		     "png", NULL, NULL);
+    g_object_unref (subpixbuf);
+}
+
+int
+main (int argc,
+      char **argv)
+{
+    FT_Error error;
+    FT_Library library;
+    FT_Face face;
+    FT_UInt glyph_index1, glyph_index2;
+    GFile *file;
+    GdkPixbuf *pixbuf;
+    guchar *buffer;
+    gint i, len, pen_x, pen_y;
+    gunichar *thumbstr = NULL;
+    glong thumbstr_len = 2;
+    gint font_size = FONT_SIZE;
+    gchar *thumbstr_utf8 = NULL, *help, *uri;
+    gchar **arguments = NULL;
+    GOptionContext *context;
+    GError *gerror = NULL;
+    gboolean retval, default_thumbstr = TRUE;
+    gint rv = 1;
+
+    const GOptionEntry options[] = {
+	    { "text", 't', 0, G_OPTION_ARG_STRING, &thumbstr_utf8,
+	      N_("Text to thumbnail (default: Aa)"), N_("TEXT") },
+	    { "size", 's', 0, G_OPTION_ARG_INT, &font_size,
+	      N_("Font size (default: 64)"), N_("SIZE") },
+	    { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &arguments,
+	      NULL, N_("FONT-FILE OUTPUT-FILE") },
+	    { NULL }
+    };
+
+    bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR);
+    bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+    textdomain (GETTEXT_PACKAGE);
+
+    setlocale (LC_ALL, "");
+
+    g_type_init ();
+    g_thread_init (NULL);
+
+    context = g_option_context_new (NULL);
+    g_option_context_add_main_entries (context, options, GETTEXT_PACKAGE);
+
+    retval = g_option_context_parse (context, &argc, &argv, &gerror);
+    if (!retval) {
+	g_printerr ("Error parsing arguments: %s\n", gerror->message);
+
+	g_option_context_free  (context);
+	g_error_free (gerror);
+        return 1;
+    }
+
+    if (!arguments || g_strv_length (arguments) != 2) {
+	help = g_option_context_get_help (context, TRUE, NULL);
+	g_printerr ("%s", help);
+
+	g_option_context_free (context);
+	goto out;
+    }
+
+    g_option_context_free (context);
+
+    if (thumbstr_utf8 != NULL) {
+	/* build ucs4 version of string to thumbnail */
+	gerror = NULL;
+	thumbstr = g_utf8_to_ucs4 (thumbstr_utf8, strlen (thumbstr_utf8),
+				   NULL, &thumbstr_len, &gerror);
+	default_thumbstr = FALSE;
+
+	if (gerror != NULL) {
+	    g_printerr ("Failed to convert: %s\n", gerror->message);
+	    g_error_free (gerror);
+	    goto out;
+	}
+    }
+
+    error = FT_Init_FreeType (&library);
+    if (error) {
+	g_printerr("Could not initialise freetype: %s\n", get_ft_error (error));
+	goto out;
+    }
+
+    totem_resources_monitor_start (arguments[0], 30 * G_USEC_PER_SEC);
+
+    file = g_file_new_for_commandline_arg (arguments[0]);
+    uri = g_file_get_uri (file);
+    g_object_unref (file);
+
+    error = FT_New_Face_From_URI (library, uri, 0, &face);
+    if (error) {
+	g_printerr ("Could not load face '%s': %s\n", uri,
+		    get_ft_error (error));
+        g_free (uri);
+	goto out;
+    }
+
+    g_free (uri);
+
+    error = FT_Set_Pixel_Sizes (face, 0, font_size);
+    if (error) {
+	g_printerr ("Could not set pixel size: %s\n", get_ft_error (error));
+	goto out;
+    }
+
+    for (i = 0; i < face->num_charmaps; i++) {
+	if (face->charmaps[i]->encoding == ft_encoding_latin_1 ||
+	    face->charmaps[i]->encoding == ft_encoding_unicode ||
+	    face->charmaps[i]->encoding == ft_encoding_apple_roman) {
+	    error = FT_Set_Charmap (face, face->charmaps[i]);
+	    if (error) {
+		g_printerr("Could not set charmap: %s\n", get_ft_error(error));
+		goto out;
+	    }
+	    break;
+	}
+    }
+
+    pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
+			     FALSE, /* has-alpha */
+			     8, /* bits-per-sample */
+			     font_size * 3 * thumbstr_len / 2, /* width */
+			     font_size * 1.5); /* height */
+    if (!pixbuf) {
+	g_printerr ("Could not create pixbuf\n");
+	goto out;
+    }
+
+    buffer = gdk_pixbuf_get_pixels (pixbuf);
+    len = gdk_pixbuf_get_rowstride (pixbuf) * gdk_pixbuf_get_height (pixbuf);
+
+    /* fill the pixbuf buffer to make it white */
+    for (i = 0; i < len; i++)
+	buffer[i] = 255;
+
+    pen_x = font_size / 2;
+    pen_y = font_size;
+
+    if (default_thumbstr) {
+	glyph_index1 = FT_Get_Char_Index (face, 'A');
+	glyph_index2 = FT_Get_Char_Index (face, 'a');
+
+	/* if the glyphs for those letters don't exist, pick some other
+	* glyphs. */
+	if (glyph_index1 == 0)
+	    glyph_index1 = MIN (65, face->num_glyphs - 1);
+
+	if (glyph_index2 == 0)
+	    glyph_index2 = MIN (97, face->num_glyphs - 1);
+
+	draw_char (pixbuf, face, glyph_index1, &pen_x, &pen_y);
+	draw_char (pixbuf, face, glyph_index2, &pen_x, &pen_y);
+    } else {
+	const gunichar *p = thumbstr;
+	FT_Select_Charmap (face, FT_ENCODING_UNICODE);
+	i = 0;
+
+	while (i < thumbstr_len) {
+	    glyph_index1 = FT_Get_Char_Index (face, *p);
+	    draw_char (pixbuf, face, glyph_index1, &pen_x, &pen_y);
+	    i++;
+	    p++;
+	}
+    }
+
+    save_pixbuf (pixbuf, arguments[1]);
+    g_object_unref (pixbuf);
+
+    totem_resources_monitor_stop ();
+
+    /* freeing the face causes a crash I haven't tracked down yet */
+    error = FT_Done_Face (face);
+    if (error) {
+	g_printerr("Could not unload face: %s\n", get_ft_error (error));
+	goto out;
+    }
+
+    error = FT_Done_FreeType (library);
+    if (error) {
+	g_printerr ("Could not finalize freetype library: %s\n",
+		   get_ft_error (error));
+	goto out;
+    }
+
+    rv = 0; /* success */
+
+  out:
+
+    g_strfreev (arguments);
+    g_free (thumbstr);
+    g_free (thumbstr_utf8);
+
+    return rv;
+}
diff --git a/font-viewer/font-view.c b/font-viewer/font-view.c
new file mode 100644
index 0000000..ab05968
--- /dev/null
+++ b/font-viewer/font-view.c
@@ -0,0 +1,584 @@
+/* -*- mode: C; c-basic-offset: 4 -*- */
+
+/*
+ * font-view: a font viewer for GNOME
+ *
+ * Copyright (C) 2002-2003  James Henstridge <james daa com au>
+ * Copyright (C) 2010 Cosimo Cecchi <cosimoc gnome org>
+ *
+ * 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 Free Software Foundation; either version 2 of the License, 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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
+ */
+
+#include <config.h>
+
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include FT_TYPE1_TABLES_H
+#include FT_SFNT_NAMES_H
+#include FT_TRUETYPE_IDS_H
+#include <cairo/cairo-ft.h>
+#include <gio/gio.h>
+#include <gtk/gtk.h>
+#include <glib/gi18n.h>
+
+#include "ftstream-vfs.h"
+
+static const gchar lowercase_text[] = "abcdefghijklmnopqrstuvwxyz";
+static const gchar uppercase_text[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+static const gchar punctuation_text[] = "0123456789.:,;(*!?')";
+
+static void
+draw_string (cairo_t *cr,
+	     const gchar *text,
+	     gint *pos_y)
+{
+    GdkColor black = { 0, 0, 0, 0 };
+    cairo_text_extents_t extents;
+
+    gdk_cairo_set_source_color (cr, &black);
+
+    cairo_text_extents (cr, text, &extents);
+    cairo_move_to (cr, 4, *pos_y);
+    cairo_show_text (cr, text);
+    
+    *pos_y += extents.height + extents.y_advance + 4;
+}
+
+static gboolean
+check_font_contain_text (FT_Face face, const gchar *text)
+{
+    while (text && *text) {
+	    gunichar wc = g_utf8_get_char (text);
+
+	    if (!FT_Get_Char_Index (face, wc))
+		return FALSE;
+
+	    text = g_utf8_next_char (text);
+    }
+
+    return TRUE;
+}
+
+static const gchar *
+get_sample_string (FT_Face face)
+{
+    const gchar *text;
+
+    text = pango_language_get_sample_string (NULL);
+
+    if (!check_font_contain_text (face, text)) {
+	text = pango_language_get_sample_string (pango_language_from_string ("en_US"));
+    }
+
+    return text;
+}
+
+static gint *
+build_sizes_table (FT_Face face,
+		   gint *n_sizes,
+		   gint *alpha_size)
+{
+    gint *sizes = NULL;
+    gint i;
+
+    /* work out what sizes to render */
+    if (FT_IS_SCALABLE (face)) {
+	*n_sizes = 8;
+	sizes = g_new (gint, *n_sizes);
+	sizes[0] = 8;
+	sizes[1] = 10;
+	sizes[2] = 12;
+	sizes[3] = 18;
+	sizes[4] = 24;
+	sizes[5] = 36;
+	sizes[6] = 48;
+	sizes[7] = 72;
+	*alpha_size = 24;
+    } else {
+	/* use fixed sizes */
+	*n_sizes = face->num_fixed_sizes;
+	sizes = g_new (gint, *n_sizes);
+	*alpha_size = 0;
+
+	for (i = 0; i < face->num_fixed_sizes; i++) {
+	    sizes[i] = face->available_sizes[i].height;
+
+	    /* work out which font size to render */
+	    if (face->available_sizes[i].height <= 24)
+		*alpha_size = face->available_sizes[i].height;
+	}
+    }
+
+    return sizes;
+}
+
+static void
+realize_callback (GtkWidget *drawing_area,
+		  FT_Face face)
+{
+    gint i, pixmap_width, pixmap_height;
+    const gchar *text;
+    cairo_text_extents_t extents;
+    cairo_font_face_t *font;
+    gint *sizes = NULL, n_sizes, alpha_size;
+    cairo_t *cr;
+
+    cr = gdk_cairo_create (gtk_widget_get_window (drawing_area));
+
+    text = get_sample_string (face);
+    sizes = build_sizes_table (face, &n_sizes, &alpha_size);
+
+    /* calculate size of pixmap to use (with 4 pixels padding) ... */
+    pixmap_width = 8;
+    pixmap_height = 8;
+
+    font = cairo_ft_font_face_create_for_ft_face (face, 0);
+    cairo_set_font_face (cr, font);
+    cairo_set_font_size (cr, alpha_size);
+    cairo_font_face_destroy (font);
+
+    cairo_text_extents (cr, lowercase_text, &extents);
+    pixmap_height += extents.height + 4;
+    pixmap_width = MAX (pixmap_width, 8 + extents.width);
+
+    cairo_text_extents (cr, uppercase_text, &extents);
+    pixmap_height += extents.height + 4;
+    pixmap_width = MAX (pixmap_width, 8 + extents.width);
+
+    cairo_text_extents (cr, punctuation_text, &extents);
+    pixmap_height += extents.height + 4;
+    pixmap_width = MAX (pixmap_width, 8 + extents.width);
+
+    pixmap_height += 8;
+
+    for (i = 0; i < n_sizes; i++) {
+	cairo_set_font_size (cr, sizes[i]);
+	cairo_text_extents (cr, text, &extents);
+	pixmap_height += extents.height + 4;
+	pixmap_width = MAX (pixmap_width, 8 + extents.width);
+    }
+
+    gtk_widget_set_size_request (drawing_area, pixmap_width, pixmap_height);
+
+    cairo_destroy (cr);
+    g_free (sizes);
+}
+
+static void
+draw_callback (GtkWidget *drawing_area,
+	       cairo_t *cr,
+	       FT_Face face)
+{
+    cairo_font_extents_t font_extents;
+    gint *sizes = NULL, n_sizes, alpha_size, pos_y, i;
+    const gchar *text;
+    cairo_font_face_t *font;
+
+    text = get_sample_string (face);
+    sizes = build_sizes_table (face, &n_sizes, &alpha_size);
+
+    font = cairo_ft_font_face_create_for_ft_face (face, 0);
+    cairo_set_font_face (cr, font);
+    cairo_font_extents (cr, &font_extents);
+    cairo_font_face_destroy (font);
+
+    /* draw text */
+    pos_y = MAX (font_extents.height, 32) + 4;
+    cairo_set_font_size (cr, alpha_size);
+    draw_string (cr, lowercase_text, &pos_y);
+    draw_string (cr, uppercase_text, &pos_y);
+    draw_string (cr, punctuation_text, &pos_y);
+
+    pos_y += 8;
+    for (i = 0; i < n_sizes; i++) {
+	cairo_set_font_size (cr, sizes[i]);
+	draw_string (cr, text, &pos_y);
+    }
+
+ end:
+    g_free (sizes);
+}
+
+static void
+add_row (GtkWidget *table,
+	 gint *row_p,
+	 const gchar *name,
+	 const gchar *value,
+	 gboolean multiline,
+	 gboolean expand)
+{
+    gchar *bold_name;
+    GtkWidget *name_w;
+
+    bold_name = g_strconcat ("<b>", name, "</b>", NULL);
+    name_w = gtk_label_new (bold_name);
+    g_free (bold_name);
+    gtk_misc_set_alignment (GTK_MISC (name_w), 0.0, 0.0);
+    gtk_label_set_use_markup (GTK_LABEL (name_w), TRUE);
+
+    gtk_table_attach (GTK_TABLE(table), name_w, 0, 1, *row_p, *row_p + 1,
+		      GTK_FILL, GTK_FILL, 0, 0);
+
+    if (multiline) {
+	GtkWidget *label, *viewport;
+	GtkScrolledWindow *swin;
+        guint flags;
+
+        label = gtk_label_new (value);
+        gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
+        gtk_label_set_selectable (GTK_LABEL (label), TRUE);
+        gtk_widget_set_size_request (label, 200, -1);
+        gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.0);
+
+        swin = GTK_SCROLLED_WINDOW (gtk_scrolled_window_new (NULL, NULL));
+        gtk_scrolled_window_set_policy (swin,
+					GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
+
+        viewport = gtk_viewport_new (gtk_scrolled_window_get_hadjustment (swin),
+                                     gtk_scrolled_window_get_vadjustment (swin));
+        gtk_viewport_set_shadow_type (GTK_VIEWPORT (viewport), GTK_SHADOW_NONE);
+
+        gtk_container_add (GTK_CONTAINER (swin), viewport);
+        (*row_p)++;
+
+        if (expand)
+          flags = GTK_FILL | GTK_EXPAND;
+        else
+          flags = GTK_FILL;
+
+        gtk_table_attach (GTK_TABLE (table), GTK_WIDGET (swin),
+			  0, 2, *row_p, *row_p + 1,
+			  GTK_FILL | GTK_EXPAND, flags, 0, 0);
+
+        gtk_container_add (GTK_CONTAINER (viewport), label);
+    } else {
+        GtkWidget *label = gtk_label_new (value);
+        gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+	gtk_label_set_selectable (GTK_LABEL(label), TRUE);
+        gtk_table_attach (GTK_TABLE (table), label,
+			  1, 2, *row_p, *row_p + 1,
+			  GTK_FILL | GTK_EXPAND, GTK_FILL, 0, 0);
+    }
+
+    (*row_p)++;
+}
+
+static void
+add_face_info (GtkWidget *table,
+	       gint *row_p,
+	       const gchar *uri,
+	       FT_Face face)
+{
+    gchar *s;
+    GFile *file;
+    GFileInfo *info;
+    PS_FontInfoRec ps_info;
+
+    add_row (table, row_p, _("Name:"), face->family_name, FALSE, FALSE);
+
+    if (face->style_name)
+	add_row (table, row_p, _("Style:"), face->style_name, FALSE, FALSE);
+
+    file = g_file_new_for_uri (uri);
+    info = g_file_query_info (file,
+                              G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE ","
+                              G_FILE_ATTRIBUTE_STANDARD_SIZE,
+                              G_FILE_QUERY_INFO_NONE,
+                              NULL, NULL);
+    g_object_unref (file);
+
+    if (info != NULL) {
+        s = g_content_type_get_description (g_file_info_get_content_type (info));
+        add_row (table, row_p, _("Type:"), s, FALSE, FALSE);
+        g_free (s);
+
+        s = g_format_size_for_display (g_file_info_get_size (info));
+        add_row (table, row_p, _("Size:"), s, FALSE, FALSE);
+        g_free (s);
+
+        g_object_unref (info);
+    }
+
+    if (FT_IS_SFNT (face)) {
+	gint i, len;
+	gchar *version = NULL, *copyright = NULL, *description = NULL;
+
+	len = FT_Get_Sfnt_Name_Count (face);
+	for (i = 0; i < len; i++) {
+	    FT_SfntName sname;
+
+	    if (FT_Get_Sfnt_Name (face, i, &sname) != 0)
+		continue;
+
+	    /* only handle the unicode names for US langid */
+	    if (!(sname.platform_id == TT_PLATFORM_MICROSOFT &&
+		  sname.encoding_id == TT_MS_ID_UNICODE_CS &&
+		  sname.language_id == TT_MS_LANGID_ENGLISH_UNITED_STATES))
+		continue;
+
+	    switch (sname.name_id) {
+	    case TT_NAME_ID_COPYRIGHT:
+		g_free (copyright);
+		copyright = g_convert ((gchar *)sname.string, sname.string_len,
+				       "UTF-8", "UTF-16BE", NULL, NULL, NULL);
+		break;
+	    case TT_NAME_ID_VERSION_STRING:
+		g_free (version);
+		version = g_convert ((gchar *)sname.string, sname.string_len,
+				     "UTF-8", "UTF-16BE", NULL, NULL, NULL);
+		break;
+	    case TT_NAME_ID_DESCRIPTION:
+		g_free (description);
+		description = g_convert ((gchar *)sname.string, sname.string_len,
+					 "UTF-8", "UTF-16BE", NULL, NULL, NULL);
+		break;
+	    default:
+		break;
+	    }
+	}
+	if (version) {
+	    add_row (table, row_p, _("Version:"), version, FALSE, FALSE);
+	    g_free (version);
+	}
+	if (copyright) {
+	    add_row (table, row_p, _("Copyright:"), copyright, TRUE, TRUE);
+	    g_free (copyright);
+	}
+	if (description) {
+	    add_row (table, row_p, _("Description:"), description, TRUE, TRUE);
+	    g_free (description);
+	}
+    } else if (FT_Get_PS_Font_Info (face, &ps_info) == 0) {
+	if (ps_info.version && g_utf8_validate (ps_info.version, -1, NULL))
+	    add_row (table, row_p, _("Version:"), ps_info.version, FALSE, FALSE);
+	if (ps_info.notice && g_utf8_validate (ps_info.notice, -1, NULL))
+	    add_row (table, row_p, _("Copyright:"), ps_info.notice, TRUE, FALSE);
+    }
+}
+
+static void
+set_icon (GtkWindow *window,
+	  const gchar *uri)
+{
+    GFile *file;
+    GIcon *icon;
+    GFileInfo *info;
+    GdkScreen *screen;
+    GtkIconTheme *icon_theme;
+    const gchar *icon_name = NULL, *content_type;
+
+    screen = gtk_widget_get_screen (GTK_WIDGET (window));
+    icon_theme = gtk_icon_theme_get_for_screen (screen);
+
+    file = g_file_new_for_uri (uri);
+    info = g_file_query_info (file, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
+                              G_FILE_QUERY_INFO_NONE, NULL, NULL);
+    g_object_unref (file);
+
+    if (info == NULL)
+	return;
+
+    content_type = g_file_info_get_content_type (info);
+    icon = g_content_type_get_icon (content_type);
+
+    if (G_IS_THEMED_ICON (icon)) {
+       const gchar * const *names = NULL;
+
+       names = g_themed_icon_get_names (G_THEMED_ICON (icon));
+       if (names) {
+          gint i;
+          for (i = 0; names[i]; i++) {
+	      if (gtk_icon_theme_has_icon (icon_theme, names[i])) {
+		  icon_name = names[i];
+		  break;
+	      }
+          }
+       }
+    }
+
+    if (icon_name) {
+        gtk_window_set_icon_name (window, icon_name);
+    }
+
+    g_object_unref (icon);
+}
+
+static void
+font_install_finished_cb (GObject      *source_object,
+                          GAsyncResult *res,
+                          gpointer      data)
+{
+    GError *err = NULL;
+
+    g_file_copy_finish (G_FILE (source_object), res, &err);
+
+    if (!err) {
+        gtk_button_set_label (GTK_BUTTON (data), _("Installed"));
+    } else {
+        gtk_button_set_label (GTK_BUTTON (data), _("Install Failed"));
+        g_debug ("Install failed: %s", err->message);
+        g_error_free (err);
+    }
+
+    gtk_widget_set_sensitive (GTK_WIDGET (data), FALSE);
+}
+
+static void
+install_button_clicked_cb (GtkButton *button,
+                           const gchar *font_file)
+{
+    GFile *src, *dest;
+    gchar *dest_path, *dest_filename;
+    GError *err = NULL;
+
+    /* first check if ~/.fonts exists */
+    dest_path = g_build_filename (g_get_home_dir (), ".fonts", NULL);
+    if (!g_file_test (dest_path, G_FILE_TEST_EXISTS)) {
+        GFile *f = g_file_new_for_path (dest_path);
+        g_file_make_directory_with_parents (f, NULL, &err);
+        g_object_unref (f);
+        if (err) {
+            /* TODO: show error dialog */
+            g_warning ("Could not create fonts directory: %s", err->message);
+            g_error_free (err);
+            g_free (dest_path);
+            return;
+        }
+    }
+    g_free (dest_path);
+
+    /* create destination filename */
+    src = g_file_new_for_uri (font_file);
+
+    dest_filename = g_file_get_basename (src);
+    dest_path = g_build_filename (g_get_home_dir (), ".fonts", dest_filename,
+				  NULL);
+    g_free (dest_filename);
+
+    dest = g_file_new_for_path (dest_path);
+
+    /* TODO: show error dialog if file exists */
+    g_file_copy_async (src, dest, G_FILE_COPY_NONE, 0, NULL, NULL, NULL,
+                       font_install_finished_cb, button);
+
+    g_object_unref (src);
+    g_object_unref (dest);
+    g_free (dest_path);
+}
+
+int
+main (int argc,
+      char **argv)
+{
+    FT_Error error;
+    FT_Library library;
+    FT_Face face;
+    GFile *file;
+    gchar *font_file, *title;
+    gint row;
+    GtkWidget *window, *hbox, *table, *swin, *drawing_area;
+    GdkColor white = { 0, 0xffff, 0xffff, 0xffff };
+    GtkWidget *button, *align;
+
+    bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR);
+    bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+    textdomain (GETTEXT_PACKAGE);
+
+    gtk_init (&argc, &argv);
+
+    if (argc != 2) {
+	g_printerr (_("Usage: %s fontfile\n"), argv[0]);
+	return 1;
+    }
+
+    error = FT_Init_FreeType (&library);
+    if (error) {
+	g_printerr("Could not initialise freetype\n");
+	return 1;
+    }
+
+    file = g_file_new_for_commandline_arg (argv[1]);
+    font_file = g_file_get_uri (file);
+    g_object_unref (file);
+
+    if (!font_file) {
+	g_printerr("Could not parse argument into a URI\n");
+	return 1;
+    }
+
+    error = FT_New_Face_From_URI (library, font_file, 0, &face);
+    if (error) {
+	g_printerr("Could not load face '%s'\n", font_file);
+	return 1;
+    }
+
+    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+    title = g_strconcat (face->family_name,
+			 face->style_name ? ", " : "",
+			 face->style_name, NULL);
+    gtk_window_set_title (GTK_WINDOW (window), title);
+    set_icon (GTK_WINDOW (window), font_file);
+    g_free (title);
+    gtk_window_set_resizable (GTK_WINDOW (window), TRUE);
+
+    hbox = gtk_hbox_new (FALSE, 0);
+    gtk_container_add (GTK_CONTAINER (window), hbox);
+
+    swin = gtk_scrolled_window_new (NULL, NULL);
+    gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (swin),
+				    GTK_POLICY_AUTOMATIC, GTK_POLICY_NEVER);
+    gtk_box_pack_start (GTK_BOX (hbox), swin, TRUE, TRUE, 0);
+
+    drawing_area = gtk_drawing_area_new ();
+    gtk_widget_modify_bg (drawing_area, GTK_STATE_NORMAL, &white);
+    gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (swin),
+					   drawing_area);
+    g_signal_connect (drawing_area, "realize",
+		      G_CALLBACK (realize_callback), face);
+    g_signal_connect (drawing_area, "draw",
+		      G_CALLBACK (draw_callback), face);
+
+    /* set the minimum size on the scrolled window to prevent
+     * unnecessary scrolling */
+    gtk_widget_set_size_request (swin, 500, -1);
+
+    g_signal_connect (window, "destroy",
+		      G_CALLBACK (gtk_main_quit), NULL);
+
+    table = gtk_table_new (1, 2, FALSE);
+    gtk_container_set_border_width (GTK_CONTAINER (table), 5);
+    gtk_box_pack_start (GTK_BOX (hbox), table, FALSE, TRUE, 0);
+
+    row = 0;
+    add_face_info (table, &row, font_file, face);
+
+    /* add install button */
+    align = gtk_alignment_new (1.0, 0.5, 0.0, 0.0);
+    gtk_table_attach (GTK_TABLE (table), align, 0, 2, row, row + 1,
+                      GTK_FILL | GTK_EXPAND, GTK_FILL, 0, 0);
+
+    button = gtk_button_new_with_mnemonic (_("I_nstall Font"));
+    g_signal_connect (button, "clicked",
+                      G_CALLBACK (install_button_clicked_cb), font_file);
+    gtk_container_add (GTK_CONTAINER (align), button);
+
+    gtk_table_set_col_spacings (GTK_TABLE (table), 8);
+    gtk_table_set_row_spacings (GTK_TABLE (table), 2);
+    gtk_widget_show_all (window);
+
+    gtk_main ();
+
+    return 0;
+}
diff --git a/font-viewer/fontilus.schemas.in b/font-viewer/fontilus.schemas.in
new file mode 100644
index 0000000..b989367
--- /dev/null
+++ b/font-viewer/fontilus.schemas.in
@@ -0,0 +1,109 @@
+<?xml version="1.0"?>
+<gconfschemafile>
+  <schemalist>
+    <schema>
+      <key>/schemas/desktop/gnome/thumbnailers/application x-font-ttf/command</key>
+      <applyto>/desktop/gnome/thumbnailers/application x-font-ttf/command</applyto>
+      <type>string</type>
+      <default>gnome-thumbnail-font %u %o</default>
+      <locale name="C">
+        <short>Thumbnail command for TrueType fonts</short>
+        <long>
+          Set this key to the command used to create thumbnails for
+          TrueType fonts.
+        </long>
+      </locale>
+    </schema>
+    <schema>
+      <key>/schemas/desktop/gnome/thumbnailers/application x-font-ttf/enable</key>
+      <applyto>/desktop/gnome/thumbnailers/application x-font-ttf/enable</applyto>
+      <type>bool</type>
+      <default>true</default>
+      <locale name="C">
+        <short>Whether to thumbnail TrueType fonts</short>
+        <long>
+          If set to true, then TrueType fonts will be thumbnailed.
+        </long>
+      </locale>
+    </schema>
+
+    <schema>
+      <key>/schemas/desktop/gnome/thumbnailers/application x-font-type1/command</key>
+      <applyto>/desktop/gnome/thumbnailers/application x-font-type1/command</applyto>
+      <type>string</type>
+      <default>gnome-thumbnail-font %u %o</default>
+      <locale name="C">
+        <short>Thumbnail command for Type1 fonts</short>
+        <long>
+          Set this key to the command used to create thumbnails for
+          Type1 fonts.
+        </long>
+      </locale>
+    </schema>
+    <schema>
+      <key>/schemas/desktop/gnome/thumbnailers/application x-font-type1/enable</key>
+      <applyto>/desktop/gnome/thumbnailers/application x-font-type1/enable</applyto>
+      <type>bool</type>
+      <default>true</default>
+      <locale name="C">
+        <short>Whether to thumbnail Type1 fonts</short>
+        <long>
+          If set to true, then Type1 fonts will be thumbnailed.
+        </long>
+      </locale>
+    </schema>
+
+    <schema>
+      <key>/schemas/desktop/gnome/thumbnailers/application x-font-pcf/command</key>
+      <applyto>/desktop/gnome/thumbnailers/application x-font-pcf/command</applyto>
+      <type>string</type>
+      <default>gnome-thumbnail-font %u %o</default>
+      <locale name="C">
+        <short>Thumbnail command for PCF fonts</short>
+        <long>
+          Set this key to the command used to create thumbnails for
+          PCF fonts.
+        </long>
+      </locale>
+    </schema>
+    <schema>
+      <key>/schemas/desktop/gnome/thumbnailers/application x-font-pcf/enable</key>
+      <applyto>/desktop/gnome/thumbnailers/application x-font-pcf/enable</applyto>
+      <type>bool</type>
+      <default>true</default>
+      <locale name="C">
+        <short>Whether to thumbnail PCF fonts</short>
+        <long>
+          If set to true, then PCF fonts will be thumbnailed.
+        </long>
+      </locale>
+    </schema>
+
+    <schema>
+      <key>/schemas/desktop/gnome/thumbnailers/application x-font-otf/command</key>
+      <applyto>/desktop/gnome/thumbnailers/application x-font-otf/command</applyto>
+      <type>string</type>
+      <default>gnome-thumbnail-font %u %o</default>
+      <locale name="C">
+        <short>Thumbnail command for OpenType fonts</short>
+        <long>
+          Set this key to the command used to create thumbnails for
+          OpenType fonts.
+        </long>
+      </locale>
+    </schema>
+    <schema>
+      <key>/schemas/desktop/gnome/thumbnailers/application x-font-otf/enable</key>
+      <applyto>/desktop/gnome/thumbnailers/application x-font-otf/enable</applyto>
+      <type>bool</type>
+      <default>true</default>
+      <locale name="C">
+        <short>Whether to thumbnail OpenType fonts</short>
+        <long>
+          If set to true, then OpenType fonts will be thumbnailed.
+        </long>
+      </locale>
+    </schema>
+
+  </schemalist>
+</gconfschemafile>
diff --git a/font-viewer/ftstream-vfs.c b/font-viewer/ftstream-vfs.c
new file mode 100644
index 0000000..a433199
--- /dev/null
+++ b/font-viewer/ftstream-vfs.c
@@ -0,0 +1,152 @@
+/* -*- mode: C; c-basic-offset: 4 -*- */
+
+/*
+ * ftstream-vfs: a FreeType/GIO stream bridge
+ *
+ * Copyright (C) 2002-2003  James Henstridge <james daa com au>
+ *
+ * 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 Free Software Foundation; either version 2 of the License, 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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
+ */
+
+#include <config.h>
+
+#include <stdlib.h>
+#include <ft2build.h>
+#include FT_FREETYPE_H
+
+#include <gio/gio.h>
+
+#include "ftstream-vfs.h"
+
+static unsigned long
+vfs_stream_read (FT_Stream stream,
+		 unsigned long offset,
+		 unsigned char *buffer,
+		 unsigned long count)
+{
+    GFileInputStream *handle = stream->descriptor.pointer;
+    gssize bytes_read = 0;
+
+    if (!g_seekable_seek (G_SEEKABLE (handle), offset, G_SEEK_SET, NULL, NULL))
+        return 0;
+
+    if (count > 0) {
+        bytes_read = g_input_stream_read (G_INPUT_STREAM (handle), buffer,
+					  count, NULL, NULL);
+
+        if (bytes_read == -1)
+            return 0;
+    }
+
+    return bytes_read;
+}
+
+static void
+vfs_stream_close (FT_Stream stream)
+{
+    GFileInputStream *handle = stream->descriptor.pointer;
+
+    if (handle == NULL)
+        return;
+
+    /* this also closes the stream */
+    g_object_unref (handle);
+
+    stream->descriptor.pointer = NULL;
+    stream->size = 0;
+    stream->base = NULL;
+}
+
+static FT_Error
+vfs_stream_open (FT_Stream stream,
+		 const char *uri)
+{
+    GFile *file;
+    GFileInfo *info;
+    GFileInputStream *handle;
+
+    file = g_file_new_for_uri (uri);
+    handle = g_file_read (file, NULL, NULL);
+
+    if (handle == NULL) {
+	g_object_unref (file);
+        return FT_Err_Cannot_Open_Resource;
+    }
+
+    info = g_file_query_info (file,
+			      G_FILE_ATTRIBUTE_STANDARD_SIZE,
+                              G_FILE_QUERY_INFO_NONE, NULL,
+			      NULL);
+    g_object_unref (file);
+
+    if (info == NULL) {
+        return FT_Err_Cannot_Open_Resource;
+    }
+
+    stream->size = g_file_info_get_size (info);
+
+    g_object_unref (info);
+
+    stream->descriptor.pointer = handle;
+    stream->pathname.pointer = NULL;
+    stream->pos = 0;
+
+    stream->read = vfs_stream_read;
+    stream->close = vfs_stream_close;
+
+    return FT_Err_Ok;
+}
+
+/* load a typeface from a URI */
+FT_Error
+FT_New_Face_From_URI (FT_Library library,
+		      const gchar* uri,
+		      FT_Long face_index,
+		      FT_Face *aface)
+{
+    FT_Open_Args args;
+    FT_Stream stream;
+    FT_Error error;
+
+    stream = calloc (1, sizeof (*stream));
+
+    if (stream == NULL)
+	return FT_Err_Out_Of_Memory;
+
+    error = vfs_stream_open (stream, uri);
+
+    if (error != FT_Err_Ok) {
+	free (stream);
+	return error;
+    }
+
+    args.flags = FT_OPEN_STREAM;
+    args.stream = stream;
+
+    error = FT_Open_Face (library, &args, face_index, aface);
+
+    if (error != FT_Err_Ok) {
+	if (stream->close != NULL)
+	    stream->close(stream);
+
+	free (stream);
+	return error;
+    }
+
+    /* so that freetype will free the stream */
+    (*aface)->face_flags &= ~FT_FACE_FLAG_EXTERNAL_STREAM;
+
+    return error;
+}
diff --git a/font-viewer/gnome-font-viewer.desktop.in.in b/font-viewer/gnome-font-viewer.desktop.in.in
new file mode 100644
index 0000000..ddecaac
--- /dev/null
+++ b/font-viewer/gnome-font-viewer.desktop.in.in
@@ -0,0 +1,15 @@
+[Desktop Entry]
+_Name=Font Viewer
+_Comment=Preview fonts
+Icon=preferences-desktop-font
+Exec=gnome-font-viewer %u
+Terminal=false
+Type=Application
+StartupNotify=true
+NoDisplay=true
+MimeType=application/x-font-ttf;application/x-font-pcf;application/x-font-type1;application/x-font-otf;
+X-GNOME-Bugzilla-Bugzilla=GNOME
+X-GNOME-Bugzilla-Product=gnome-utils
+X-GNOME-Bugzilla-Component=font-viewer
+X-GNOME-Bugzilla-OtherBinaries=gnome-thumbnail-font
+X-GNOME-Bugzilla-Version= VERSION@
diff --git a/font-viewer/totem-resources.c b/font-viewer/totem-resources.c
new file mode 100644
index 0000000..f3831e7
--- /dev/null
+++ b/font-viewer/totem-resources.c
@@ -0,0 +1,123 @@
+/* 
+ * Copyright (C) 2007 Bastien Nocera <hadess hadess net>
+ *
+ * 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 Free Software Foundation; either version 2 of the License, 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA.
+ *
+ * The Totem project hereby grant permission for non-gpl compatible GStreamer
+ * plugins to be used and distributed together with GStreamer and Totem. This
+ * permission are above and beyond the permissions granted by the GPL license
+ * Totem is covered by.
+ *
+ * Monday 7th February 2005: Christian Schaller: Add exception clause.
+ * See license_change file for details.
+ *
+ */
+
+#include <config.h>
+
+#include <glib.h>
+#include <glib/gstdio.h>
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/resource.h>
+
+#include "totem-resources.h"
+
+#define MAX_HELPER_MEMORY (256 * 1024 * 1024)	/* 256 MB */
+#define MAX_HELPER_SECONDS (15)			/* 15 seconds */
+#define DEFAULT_SLEEP_TIME (30 * G_USEC_PER_SEC) /* 30 seconds */
+
+static guint sleep_time = DEFAULT_SLEEP_TIME;
+static gboolean finished = TRUE;
+
+static void
+set_resource_limits (const char *input)
+{
+	struct rlimit limit;
+	struct stat buf;
+	rlim_t max;
+
+	max = MAX_HELPER_MEMORY;
+
+	/* Set the maximum virtual size depending on the size
+	 * of the file to process, as we wouldn't be able to
+	 * mmap it otherwise */
+	if (input == NULL) {
+		max = MAX_HELPER_MEMORY;
+	} else if (g_stat (input, &buf) == 0) {
+		max = MAX_HELPER_MEMORY + buf.st_size;
+	} else if (g_str_has_prefix (input, "file://") != FALSE) {
+		char *file;
+		file = g_filename_from_uri (input, NULL, NULL);
+		if (file != NULL && g_stat (file, &buf) == 0)
+			max = MAX_HELPER_MEMORY + buf.st_size;
+		g_free (file);
+	}
+
+	limit.rlim_cur = max;
+	limit.rlim_max = max;
+
+	setrlimit (RLIMIT_DATA, &limit);
+
+	limit.rlim_cur = MAX_HELPER_SECONDS;
+	limit.rlim_max = MAX_HELPER_SECONDS;
+	setrlimit (RLIMIT_CPU, &limit);
+}
+
+G_GNUC_NORETURN static gpointer
+time_monitor (gpointer data)
+{
+	const char *app_name;
+
+	g_usleep (sleep_time);
+
+	if (finished != FALSE)
+		g_thread_exit (NULL);
+
+	app_name = g_get_application_name ();
+	if (app_name == NULL)
+		app_name = g_get_prgname ();
+	g_print ("%s couldn't process file: '%s'\n"
+		 "Reason: Took too much time to process.\n",
+		 app_name,
+		 (const char *) data);
+
+	exit (0);
+}
+
+void
+totem_resources_monitor_start (const char *input, gint wall_clock_time)
+{
+	set_resource_limits (input);
+
+	if (wall_clock_time < 0)
+		return;
+
+	if (wall_clock_time > 0)
+		sleep_time = wall_clock_time;
+
+	finished = FALSE;
+	g_thread_create (time_monitor, (gpointer) input, FALSE, NULL);
+}
+
+void
+totem_resources_monitor_stop (void)
+{
+	finished = TRUE;
+}
+
diff --git a/font-viewer/totem-resources.h b/font-viewer/totem-resources.h
new file mode 100644
index 0000000..421a03c
--- /dev/null
+++ b/font-viewer/totem-resources.h
@@ -0,0 +1,33 @@
+/* 
+ * Copyright (C) 2007 Bastien Nocera <hadess hadess net>
+ *
+ * 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 Free Software Foundation; either version 2 of the License, 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA.
+ *
+ * The Totem project hereby grant permission for non-gpl compatible GStreamer
+ * plugins to be used and distributed together with GStreamer and Totem. This
+ * permission are above and beyond the permissions granted by the GPL license
+ * Totem is covered by.
+ *
+ * Monday 7th February 2005: Christian Schaller: Add exception clause.
+ * See license_change file for details.
+ *
+ */
+
+#include <glib.h>
+
+void totem_resources_monitor_start	(const char *input,
+					 gint wall_clock_time);
+void totem_resources_monitor_stop	(void);
+



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]