summaryrefslogtreecommitdiff
path: root/font-viewer
diff options
context:
space:
mode:
authorPerberos <[email protected]>2011-12-01 21:51:44 -0300
committerPerberos <[email protected]>2011-12-01 21:51:44 -0300
commit0b0e6bc987da4fd88a7854ebb12bde705e92c428 (patch)
tree47d329edd31c67eaa36b2147780e37e197e901b5 /font-viewer
downloadmate-control-center-0b0e6bc987da4fd88a7854ebb12bde705e92c428.tar.bz2
mate-control-center-0b0e6bc987da4fd88a7854ebb12bde705e92c428.tar.xz
moving from https://github.com/perberos/mate-desktop-environment
Diffstat (limited to 'font-viewer')
-rw-r--r--font-viewer/Makefile.am48
-rw-r--r--font-viewer/font-thumbnailer.c399
-rw-r--r--font-viewer/font-view.c608
-rw-r--r--font-viewer/fontilus.schemas.in109
-rw-r--r--font-viewer/ftstream-vfs.c154
-rw-r--r--font-viewer/mate-font-viewer.desktop.in.in14
-rw-r--r--font-viewer/totem-resources.c123
-rw-r--r--font-viewer/totem-resources.h33
8 files changed, 1488 insertions, 0 deletions
diff --git a/font-viewer/Makefile.am b/font-viewer/Makefile.am
new file mode 100644
index 00000000..9fd87eac
--- /dev/null
+++ b/font-viewer/Makefile.am
@@ -0,0 +1,48 @@
+
+INCLUDES = $(FONT_VIEWER_CFLAGS) $(MATECC_CAPPLETS_CFLAGS) -DDIRECTORY_DIR=\"$(directorydir)\" \
+ -DMATELOCALEDIR=\"$(datadir)/locale\"
+
+bin_PROGRAMS = mate-thumbnail-font mate-font-viewer
+
+mate_thumbnail_font_LDADD = $(MATECC_CAPPLETS_LIBS) $(FONT_VIEWER_LIBS)
+mate_thumbnail_font_SOURCES = ftstream-vfs.c font-thumbnailer.c totem-resources.c totem-resources.h
+
+mate_font_viewer_LDADD = $(MATECC_CAPPLETS_LIBS) $(FONT_VIEWER_LIBS)
+mate_font_viewer_SOURCES = ftstream-vfs.c font-view.c
+
+schemasdir = $(MATECONF_SCHEMA_FILE_DIR)
+schemas_DATA = fontilus.schemas
+
+desktopdir = $(datadir)/applications
+desktop_in_files = mate-font-viewer.desktop.in
+desktop_DATA = $(desktop_in_files:.desktop.in=.desktop)
+
+install-data-local: install-mateconf-schemas install-desktop-database
+
+if MATECONF_SCHEMAS_INSTALL
+install-mateconf-schemas:
+ if test -z "$(DESTDIR)"; then \
+ MATECONF_CONFIG_SOURCE=$(MATECONF_SCHEMA_CONFIG_SOURCE) $(MATECONFTOOL) --makefile-install-rule fontilus.schemas; \
+ fi
+else
+install-mateconf-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 \
+ mate-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 00000000..da9f42a9
--- /dev/null
+++ b/font-viewer/font-thumbnailer.c
@@ -0,0 +1,399 @@
+/* -*- mode: C; c-basic-offset: 4 -*-
+ * fontilus - a collection of font utilities for MATE
+ * Copyright (C) 2002-2003 James Henstridge <[email protected]>
+ *
+ * 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 "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
+
+FT_Error FT_New_Face_From_URI(FT_Library library,
+ const gchar *uri,
+ FT_Long face_index,
+ FT_Face *aface);
+
+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, 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;
+ gchar *uri;
+ 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;
+ 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, MATELOCALEDIR);
+ 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);
+ g_option_context_free (context);
+ if (!retval) {
+ g_printerr (_("Error parsing arguments: %s\n"), gerror->message);
+ g_error_free (gerror);
+ return 1;
+ }
+
+ if (!arguments || g_strv_length (arguments) != 2) {
+ /* FIXME: once glib bug 336089 is fixed, use print_help here instead! */
+ g_printerr("usage: %s [--text TEXT] [--size SIZE] FONT-FILE OUTPUT-FILE\n", argv[0]);
+ goto out;
+ }
+
+ 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;
+
+ /* Not sure this can really happen... */
+ 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, 8,
+ font_size*3*thumbstr_len/2, font_size*1.5);
+ 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);
+ 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 {
+ 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 finalise 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 00000000..f9fd94bc
--- /dev/null
+++ b/font-viewer/font-view.c
@@ -0,0 +1,608 @@
+/* -*- mode: C; c-basic-offset: 4 -*-
+ * fontilus - a collection of font utilities for MATE
+ * Copyright (C) 2002-2003 James Henstridge <[email protected]>
+ *
+ * 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 <ft2build.h>
+#include FT_FREETYPE_H
+#include FT_TYPE1_TABLES_H
+#include FT_SFNT_NAMES_H
+#include FT_TRUETYPE_IDS_H
+#include <X11/Xlib.h>
+#include <X11/Xft/Xft.h>
+
+#include <gio/gio.h>
+#include <gtk/gtk.h>
+#include <gdk/gdkx.h>
+#include <glib/gi18n.h>
+
+FT_Error FT_New_Face_From_URI(FT_Library library,
+ const gchar *uri,
+ FT_Long face_index,
+ FT_Face *aface);
+
+static const gchar lowercase_text[] = "abcdefghijklmnopqrstuvwxyz";
+static const gchar uppercase_text[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+static const gchar punctuation_text[] = "0123456789.:,;(*!?')";
+
+static inline XftFont *
+get_font(Display *xdisplay, FT_Face face, gint size, FcCharSet *charset)
+{
+ FcPattern *pattern;
+ XftFont *font;
+ int screen = DefaultScreen (xdisplay);
+
+ pattern = FcPatternBuild(NULL,
+ FC_FT_FACE, FcTypeFTFace, face,
+ FC_PIXEL_SIZE, FcTypeDouble, (double)size,
+ NULL);
+
+ if (charset)
+ FcPatternAddCharSet (pattern, "charset", charset);
+
+ FcConfigSubstitute (NULL, pattern, FcMatchPattern);
+ XftDefaultSubstitute (xdisplay, screen, pattern);
+
+ font = XftFontOpenPattern(xdisplay, pattern);
+
+ return font;
+}
+
+static inline void
+draw_string(Display *xdisplay, XftDraw *draw, XftFont *font, XftColor *colour,
+ const gchar *text, gint *pos_y)
+{
+ XGlyphInfo extents;
+ gint len = strlen(text);
+
+ XftTextExtentsUtf8(xdisplay, font, (guchar *)text, len, &extents);
+ XftDrawStringUtf8(draw, colour, font, 4, *pos_y + extents.y, (guchar *)text, len);
+ *pos_y += extents.height + 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 gboolean expose_event(GtkWidget *widget, GdkEventExpose *event, GdkPixmap *pixmap);
+
+static GdkPixmap *
+create_text_pixmap(GtkWidget *drawing_area, FT_Face face)
+{
+ gint i, pixmap_width, pixmap_height, pos_y, textlen;
+ GdkPixmap *pixmap = NULL;
+ const gchar *text;
+ Display *xdisplay;
+ Drawable xdrawable;
+ Visual *xvisual;
+ Colormap xcolormap;
+ XftDraw *draw;
+ XftColor colour;
+ XGlyphInfo extents;
+ XftFont *font;
+ gint *sizes = NULL, n_sizes, alpha_size;
+ FcCharSet *charset = NULL;
+ GdkWindow *window = gtk_widget_get_window (drawing_area);
+
+ text = pango_language_get_sample_string(NULL);
+ if (! check_font_contain_text (face, text))
+ {
+ pango_language_get_sample_string (pango_language_from_string ("en_US"));
+ }
+
+ textlen = strlen(text);
+
+ /* create the XftDraw */
+ xdisplay = GDK_PIXMAP_XDISPLAY(window);
+
+ #if GTK_CHECK_VERSION(3, 0, 0)
+ xvisual = GDK_VISUAL_XVISUAL(gdk_window_get_visual(window));
+ #else
+ xvisual = GDK_VISUAL_XVISUAL(gdk_drawable_get_visual(window));
+ #endif
+
+ xcolormap = GDK_COLORMAP_XCOLORMAP(gdk_drawable_get_colormap(window));
+ XftColorAllocName(xdisplay, xvisual, xcolormap, "black", &colour);
+
+ /* 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;
+ }
+ }
+
+ /* calculate size of pixmap to use (with 4 pixels padding) ... */
+ pixmap_width = 8;
+ pixmap_height = 8;
+
+ font = get_font(xdisplay, face, alpha_size, charset);
+ charset = FcCharSetCopy (font->charset);
+ XftTextExtentsUtf8(xdisplay, font,
+ (guchar *)lowercase_text, strlen(lowercase_text), &extents);
+ pixmap_height += extents.height + 4;
+ pixmap_width = MAX(pixmap_width, 8 + extents.width);
+ XftTextExtentsUtf8(xdisplay, font,
+ (guchar *)uppercase_text, strlen(uppercase_text), &extents);
+ pixmap_height += extents.height + 4;
+ pixmap_width = MAX(pixmap_width, 8 + extents.width);
+ XftTextExtentsUtf8(xdisplay, font,
+ (guchar *)punctuation_text, strlen(punctuation_text), &extents);
+ pixmap_height += extents.height + 4;
+ pixmap_width = MAX(pixmap_width, 8 + extents.width);
+ XftFontClose(xdisplay, font);
+
+ pixmap_height += 8;
+
+ for (i = 0; i < n_sizes; i++) {
+ font = get_font(xdisplay, face, sizes[i], charset);
+ if (!font) continue;
+ XftTextExtentsUtf8(xdisplay, font, (guchar *)text, textlen, &extents);
+ pixmap_height += extents.height + 4;
+ pixmap_width = MAX(pixmap_width, 8 + extents.width);
+ XftFontClose(xdisplay, font);
+ }
+
+ /* create pixmap */
+ gtk_widget_set_size_request(drawing_area, pixmap_width, pixmap_height);
+ pixmap = gdk_pixmap_new(window,
+ pixmap_width, pixmap_height, -1);
+ if (!pixmap)
+ goto end;
+ gdk_draw_rectangle(pixmap, gtk_widget_get_style(drawing_area)->white_gc,
+ TRUE, 0, 0, pixmap_width, pixmap_height);
+
+ xdrawable = GDK_DRAWABLE_XID(pixmap);
+ draw = XftDrawCreate(xdisplay, xdrawable, xvisual, xcolormap);
+
+ /* draw text */
+ pos_y = 4;
+ font = get_font(xdisplay, face, alpha_size, charset);
+ draw_string(xdisplay, draw, font, &colour, lowercase_text, &pos_y);
+ draw_string(xdisplay, draw, font, &colour, uppercase_text, &pos_y);
+ draw_string(xdisplay, draw, font, &colour, punctuation_text, &pos_y);
+ XftFontClose(xdisplay, font);
+
+ pos_y += 8;
+ for (i = 0; i < n_sizes; i++) {
+ font = get_font(xdisplay, face, sizes[i], charset);
+ if (!font) continue;
+ draw_string(xdisplay, draw, font, &colour, text, &pos_y);
+ XftFontClose(xdisplay, font);
+ }
+
+ g_signal_connect(drawing_area, "expose-event", G_CALLBACK(expose_event),
+ pixmap);
+
+ end:
+ g_free(sizes);
+ FcCharSetDestroy (charset);
+ return pixmap;
+}
+
+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) {
+ 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 gboolean
+expose_event(GtkWidget *widget, GdkEventExpose *event, GdkPixmap *pixmap)
+{
+ gdk_draw_drawable(gtk_widget_get_window (widget),
+ gtk_widget_get_style (widget)->fg_gc[gtk_widget_get_state (widget)],
+ pixmap,
+ event->area.x, event->area.y,
+ event->area.x, event->area.y,
+ event->area.width, event->area.height);
+ return 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)
+ 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;
+ GdkPixmap *pixmap;
+ GdkColor white = { 0, 0xffff, 0xffff, 0xffff };
+ GtkWidget *button, *align;
+
+ bindtextdomain(GETTEXT_PACKAGE, MATELOCALEDIR);
+ 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;
+ }
+
+ if (!XftInitFtLibrary()) {
+ g_printerr("could not initialise freetype library\n");
+ 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", create_text_pixmap, 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 00000000..2e303465
--- /dev/null
+++ b/font-viewer/fontilus.schemas.in
@@ -0,0 +1,109 @@
+<?xml version="1.0"?>
+<mateconfschemafile>
+ <schemalist>
+ <schema>
+ <key>/schemas/desktop/mate/thumbnailers/application@x-font-ttf/command</key>
+ <applyto>/desktop/mate/thumbnailers/application@x-font-ttf/command</applyto>
+ <type>string</type>
+ <default>mate-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/mate/thumbnailers/application@x-font-ttf/enable</key>
+ <applyto>/desktop/mate/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/mate/thumbnailers/application@x-font-type1/command</key>
+ <applyto>/desktop/mate/thumbnailers/application@x-font-type1/command</applyto>
+ <type>string</type>
+ <default>mate-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/mate/thumbnailers/application@x-font-type1/enable</key>
+ <applyto>/desktop/mate/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/mate/thumbnailers/application@x-font-pcf/command</key>
+ <applyto>/desktop/mate/thumbnailers/application@x-font-pcf/command</applyto>
+ <type>string</type>
+ <default>mate-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/mate/thumbnailers/application@x-font-pcf/enable</key>
+ <applyto>/desktop/mate/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/mate/thumbnailers/application@x-font-otf/command</key>
+ <applyto>/desktop/mate/thumbnailers/application@x-font-otf/command</applyto>
+ <type>string</type>
+ <default>mate-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/mate/thumbnailers/application@x-font-otf/enable</key>
+ <applyto>/desktop/mate/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>
+</mateconfschemafile>
diff --git a/font-viewer/ftstream-vfs.c b/font-viewer/ftstream-vfs.c
new file mode 100644
index 00000000..8bfcf397
--- /dev/null
+++ b/font-viewer/ftstream-vfs.c
@@ -0,0 +1,154 @@
+/* -*- mode: C; c-basic-offset: 4 -*-
+ * fontilus - a collection of font utilities for MATE
+ * Copyright (C) 2002-2003 James Henstridge <[email protected]>
+ *
+ * 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 <stdlib.h>
+#include <ft2build.h>
+#include FT_FREETYPE_H
+
+#include <gio/gio.h>
+
+static unsigned long
+vfs_stream_read(FT_Stream stream,
+ unsigned long offset,
+ unsigned char *buffer,
+ unsigned long count)
+{
+ GFileInputStream *handle = (GFileInputStream *)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 = (GFileInputStream *)stream->descriptor.pointer;
+
+ if (! handle)
+ return;
+
+ 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;
+ GError *error = NULL;
+ GFileInfo *info;
+ GFileInputStream *handle;
+
+ if (!stream)
+ return FT_Err_Invalid_Stream_Handle;
+
+ file = g_file_new_for_uri (uri);
+
+ handle = g_file_read (file, NULL, &error);
+ if (! handle) {
+ g_message ("%s", error->message);
+ g_object_unref (file);
+
+ g_error_free (error);
+ return FT_Err_Cannot_Open_Resource;
+ }
+
+ info = g_file_query_info (file, G_FILE_ATTRIBUTE_STANDARD_SIZE,
+ G_FILE_QUERY_INFO_NONE, NULL, &error);
+ g_object_unref (file);
+
+ if (! info) {
+ g_warning ("%s", error->message);
+
+ g_error_free (error);
+ 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;
+
+ if ((stream = calloc(1, sizeof(*stream))) == NULL)
+ return FT_Err_Out_Of_Memory;
+
+ error = vfs_stream_open(stream, uri);
+ if (error != FT_Err_Ok) {
+ free(stream);
+ return error;
+ }
+
+ /* freetype-2.1.3 accidentally broke compatibility. */
+#if defined(FT_OPEN_STREAM) && !defined(ft_open_stream)
+# define ft_open_stream FT_OPEN_STREAM
+#endif
+ 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) 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/mate-font-viewer.desktop.in.in b/font-viewer/mate-font-viewer.desktop.in.in
new file mode 100644
index 00000000..9c78f678
--- /dev/null
+++ b/font-viewer/mate-font-viewer.desktop.in.in
@@ -0,0 +1,14 @@
+[Desktop Entry]
+_Name=Font Viewer
+_Comment=Preview fonts
+Icon=preferences-desktop-font
+Exec=mate-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-MATE-Bugzilla-Bugzilla=MATE
+X-MATE-Bugzilla-Product=mate-control-center
+X-MATE-Bugzilla-Component=font properties
+X-MATE-Bugzilla-Version=@VERSION@
diff --git a/font-viewer/totem-resources.c b/font-viewer/totem-resources.c
new file mode 100644
index 00000000..3d107737
--- /dev/null
+++ b/font-viewer/totem-resources.c
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2007 Bastien Nocera <[email protected]>
+ *
+ * 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 00000000..421a03ca
--- /dev/null
+++ b/font-viewer/totem-resources.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2007 Bastien Nocera <[email protected]>
+ *
+ * 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);
+