summaryrefslogtreecommitdiff
path: root/font-viewer
diff options
context:
space:
mode:
Diffstat (limited to 'font-viewer')
-rw-r--r--font-viewer/Makefile.am8
-rw-r--r--font-viewer/sushi-font-loader.c207
-rw-r--r--font-viewer/sushi-font-loader.h50
-rw-r--r--font-viewer/sushi-font-widget.c648
-rw-r--r--font-viewer/sushi-font-widget.h69
5 files changed, 980 insertions, 2 deletions
diff --git a/font-viewer/Makefile.am b/font-viewer/Makefile.am
index 5dbf458c..53bd887b 100644
--- a/font-viewer/Makefile.am
+++ b/font-viewer/Makefile.am
@@ -4,11 +4,15 @@ AM_CPPFLAGS = $(FONT_VIEWER_CFLAGS) $(MATECC_CAPPLETS_CFLAGS) -DDIRECTORY_DIR=\"
bin_PROGRAMS = mate-thumbnail-font mate-font-viewer
+font_loader_SOURCES = \
+ sushi-font-loader.h \
+ sushi-font-loader.c
+
mate_thumbnail_font_LDADD = $(MATECC_CAPPLETS_LIBS) $(FONT_VIEWER_LIBS)
-mate_thumbnail_font_SOURCES = ftstream-vfs.c ftstream-vfs.h font-thumbnailer.c totem-resources.c totem-resources.h
+mate_thumbnail_font_SOURCES = ftstream-vfs.c ftstream-vfs.h $(font_loader_SOURCES) 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 ftstream-vfs.h font-view.c
+mate_font_viewer_SOURCES = ftstream-vfs.c ftstream-vfs.h $(font_loader_SOURCES) sushi-font-widget.h sushi-font-widget.c font-view.c
thumbnailerdir = $(datadir)/thumbnailers
thumbnailer_DATA = mate-font-viewer.thumbnailer
diff --git a/font-viewer/sushi-font-loader.c b/font-viewer/sushi-font-loader.c
new file mode 100644
index 00000000..9ac319fd
--- /dev/null
+++ b/font-viewer/sushi-font-loader.c
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2011 Red Hat, Inc.
+ *
+ * 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.
+ *
+ * The Sushi project hereby grant permission for non-gpl compatible GStreamer
+ * plugins to be used and distributed together with GStreamer and Sushi. This
+ * permission is above and beyond the permissions granted by the GPL license
+ * Sushi is covered by.
+ *
+ * Authors: Cosimo Cecchi <[email protected]>
+ *
+ */
+
+#include "sushi-font-loader.h"
+
+#include <stdlib.h>
+#include <ft2build.h>
+#include FT_FREETYPE_H
+
+#include <gio/gio.h>
+
+typedef struct {
+ FT_Library library;
+ FT_Long face_index;
+ GFile *file;
+ GSimpleAsyncResult *result;
+
+ gchar *face_contents;
+ gsize face_length;
+} FontLoadJob;
+
+static FontLoadJob *
+font_load_job_new (FT_Library library,
+ const gchar *uri,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ FontLoadJob *job = g_slice_new0 (FontLoadJob);
+
+ job->library = library;
+ job->face_index = 0;
+ job->file = g_file_new_for_uri (uri);
+ job->result = g_simple_async_result_new
+ (NULL, callback, user_data,
+ sushi_new_ft_face_from_uri_async);
+
+ g_simple_async_result_set_op_res_gpointer (job->result, job, NULL);
+
+ return job;
+}
+
+static void
+font_load_job_free (FontLoadJob *job)
+{
+ g_clear_object (&job->result);
+ g_clear_object (&job->file);
+
+ g_slice_free (FontLoadJob, job);
+}
+
+static FT_Face
+create_face_from_contents (FontLoadJob *job,
+ gchar **contents,
+ GError **error)
+{
+ FT_Error ft_error;
+ FT_Face retval;
+
+ ft_error = FT_New_Memory_Face (job->library,
+ (const FT_Byte *) job->face_contents,
+ (FT_Long) job->face_length,
+ job->face_index,
+ &retval);
+
+ if (ft_error != 0) {
+ g_set_error_literal (error, G_IO_ERROR, 0,
+ "Unable to read the font face file");
+ retval = NULL;
+ g_free (job->face_contents);
+ } else {
+ *contents = job->face_contents;
+ }
+
+ return retval;
+}
+
+static gboolean
+font_load_job_callback (gpointer user_data)
+{
+ FontLoadJob *job = user_data;
+
+ g_simple_async_result_complete (job->result);
+ font_load_job_free (job);
+
+ return FALSE;
+}
+
+static void
+font_load_job_do_load (FontLoadJob *job,
+ GError **error)
+{
+ gchar *contents;
+ gsize length;
+
+ g_file_load_contents (job->file, NULL,
+ &contents, &length, NULL, error);
+
+ if ((error != NULL) && (*error == NULL)) {
+ job->face_contents = contents;
+ job->face_length = length;
+ }
+}
+
+static gboolean
+font_load_job (GIOSchedulerJob *sched_job,
+ GCancellable *cancellable,
+ gpointer user_data)
+{
+ FontLoadJob *job = user_data;
+ GError *error = NULL;
+
+ font_load_job_do_load (job, &error);
+
+ if (error != NULL)
+ g_simple_async_result_take_error (job->result, error);
+
+ g_io_scheduler_job_send_to_mainloop_async (sched_job,
+ font_load_job_callback,
+ job, NULL);
+
+ return FALSE;
+}
+
+/**
+ * sushi_new_ft_face_from_uri: (skip)
+ *
+ */
+FT_Face
+sushi_new_ft_face_from_uri (FT_Library library,
+ const gchar *uri,
+ gchar **contents,
+ GError **error)
+{
+ FontLoadJob *job = NULL;
+
+ job = font_load_job_new (library, uri, NULL, NULL);
+ font_load_job_do_load (job, error);
+
+ if ((error != NULL) && (*error != NULL)) {
+ g_object_unref (job);
+ return NULL;
+ }
+
+ return create_face_from_contents (job, contents, error);
+}
+
+/**
+ * sushi_new_ft_face_from_uri_async: (skip)
+ *
+ */
+void
+sushi_new_ft_face_from_uri_async (FT_Library library,
+ const gchar *uri,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ FontLoadJob *job = font_load_job_new (library, uri, callback, user_data);
+ g_io_scheduler_push_job (font_load_job,
+ job, NULL,
+ G_PRIORITY_DEFAULT,
+ NULL);
+}
+
+/**
+ * sushi_new_ft_face_from_uri_finish: (skip)
+ *
+ */
+FT_Face
+sushi_new_ft_face_from_uri_finish (GAsyncResult *result,
+ gchar **contents,
+ GError **error)
+{
+ FontLoadJob *job;
+
+ if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result),
+ error))
+ return NULL;
+
+ job = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (result));
+
+ return create_face_from_contents (job, contents, error);
+}
+
diff --git a/font-viewer/sushi-font-loader.h b/font-viewer/sushi-font-loader.h
new file mode 100644
index 00000000..aa63b9b1
--- /dev/null
+++ b/font-viewer/sushi-font-loader.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2011 Red Hat, Inc.
+ *
+ * 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.
+ *
+ * The Sushi project hereby grant permission for non-gpl compatible GStreamer
+ * plugins to be used and distributed together with GStreamer and Sushi. This
+ * permission is above and beyond the permissions granted by the GPL license
+ * Sushi is covered by.
+ *
+ * Authors: Cosimo Cecchi <[email protected]>
+ *
+ */
+
+#ifndef __SUSHI_FONT_LOADER_H__
+#define __SUSHI_FONT_LOADER_H__
+
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include <gio/gio.h>
+
+FT_Face sushi_new_ft_face_from_uri (FT_Library library,
+ const gchar *uri,
+ gchar **contents,
+ GError **error);
+
+void sushi_new_ft_face_from_uri_async (FT_Library library,
+ const gchar *uri,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+
+FT_Face sushi_new_ft_face_from_uri_finish (GAsyncResult *result,
+ gchar **contents,
+ GError **error);
+
+#endif /* __SUSHI_FONT_LOADER_H__ */
+
diff --git a/font-viewer/sushi-font-widget.c b/font-viewer/sushi-font-widget.c
new file mode 100644
index 00000000..09a4c194
--- /dev/null
+++ b/font-viewer/sushi-font-widget.c
@@ -0,0 +1,648 @@
+/*
+ * Copyright (C) 2011 Red Hat, Inc.
+ *
+ * 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.
+ *
+ * The Sushi project hereby grant permission for non-gpl compatible GStreamer
+ * plugins to be used and distributed together with GStreamer and Sushi. This
+ * permission is above and beyond the permissions granted by the GPL license
+ * Sushi is covered by.
+ *
+ * Authors: Cosimo Cecchi <[email protected]>
+ *
+ */
+
+#include "sushi-font-widget.h"
+#include "sushi-font-loader.h"
+
+#include <math.h>
+
+enum {
+ PROP_URI = 1,
+ NUM_PROPERTIES
+};
+
+enum {
+ LOADED,
+ NUM_SIGNALS
+};
+
+struct _SushiFontWidgetPrivate {
+ gchar *uri;
+
+ FT_Library library;
+ FT_Face face;
+ gchar *face_contents;
+
+ const gchar *lowercase_text;
+ const gchar *uppercase_text;
+ const gchar *punctuation_text;
+
+ gchar *sample_string;
+
+ gchar *font_name;
+ gboolean font_supports_title;
+};
+
+static GParamSpec *properties[NUM_PROPERTIES] = { NULL, };
+static guint signals[NUM_SIGNALS] = { 0, };
+
+G_DEFINE_TYPE (SushiFontWidget, sushi_font_widget, GTK_TYPE_DRAWING_AREA);
+
+#define SURFACE_SIZE 4
+#define SECTION_SPACING 16
+
+#define TITLE_SIZE 6
+
+static const gchar lowercase_text_stock[] = "abcdefghijklmnopqrstuvwxyz";
+static const gchar uppercase_text_stock[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+static const gchar punctuation_text_stock[] = "0123456789.:,;(*!?')";
+
+/* adapted from gnome-utils:font-viewer/font-view.c
+ *
+ * Copyright (C) 2002-2003 James Henstridge <[email protected]>
+ * Copyright (C) 2010 Cosimo Cecchi <[email protected]>
+ *
+ * License: GPLv2+
+ */
+static void
+draw_string (cairo_t *cr,
+ GtkBorder padding,
+ const gchar *text,
+ gint *pos_y)
+{
+ cairo_font_extents_t font_extents;
+ cairo_text_extents_t extents;
+
+ cairo_font_extents (cr, &font_extents);
+ cairo_text_extents (cr, text, &extents);
+
+ if (pos_y != NULL)
+ *pos_y += font_extents.ascent + font_extents.descent +
+ extents.y_advance + padding.top;
+
+ cairo_move_to (cr, padding.left, *pos_y);
+ cairo_show_text (cr, text);
+
+ *pos_y += padding.bottom;
+}
+
+static gboolean
+check_font_contain_text (FT_Face face,
+ const gchar *text)
+{
+ gunichar *string;
+ glong len, idx, map;
+ FT_CharMap charmap;
+ gboolean retval;
+
+ string = g_utf8_to_ucs4_fast (text, -1, &len);
+
+ for (map = 0; map < face->num_charmaps; map++) {
+ charmap = face->charmaps[map];
+ FT_Set_Charmap (face, charmap);
+
+ retval = TRUE;
+
+ for (idx = 0; idx < len; idx++) {
+ gunichar c = string[idx];
+
+ if (!FT_Get_Char_Index (face, c)) {
+ retval = FALSE;
+ break;
+ }
+ }
+
+ if (retval)
+ break;
+ }
+
+ g_free (string);
+
+ return retval;
+}
+
+static gchar *
+build_charlist_for_face (FT_Face face)
+{
+ GString *string;
+ gulong c;
+ guint glyph;
+
+ string = g_string_new (NULL);
+
+ /* exclude normal ASCII characters here */
+ c = 255;
+ glyph = 0;
+
+ do {
+ c = FT_Get_Next_Char (face, c, &glyph);
+ g_string_append_unichar (string, (gunichar) c);
+ } while (glyph != 0);
+
+ return g_string_free (string, FALSE);
+}
+
+static gchar *
+random_string_from_available_chars (FT_Face face,
+ gint n_chars)
+{
+ gchar *chars;
+ gint idx, rand, total_chars;
+ GString *retval;
+ gchar *ptr, *end;
+
+ idx = 0;
+ chars = build_charlist_for_face (face);
+
+ retval = g_string_new (NULL);
+ total_chars = g_utf8_strlen (chars, -1);
+
+ while (idx < n_chars) {
+ rand = g_random_int_range (0, total_chars);
+
+ ptr = g_utf8_offset_to_pointer (chars, rand);
+ end = g_utf8_find_next_char (ptr, NULL);
+
+ g_string_append_len (retval, ptr, end - ptr);
+ idx++;
+ }
+
+ return g_string_free (retval, FALSE);
+}
+
+static void
+build_strings_for_face (SushiFontWidget *self)
+{
+ const gchar *sample_string;
+
+ /* if we don't have lowercase/uppercase/punctuation text in the face,
+ * we omit it directly, and render a random text below.
+ */
+ if (check_font_contain_text (self->priv->face, lowercase_text_stock))
+ self->priv->lowercase_text = lowercase_text_stock;
+ else
+ self->priv->lowercase_text = NULL;
+
+ if (check_font_contain_text (self->priv->face, uppercase_text_stock))
+ self->priv->uppercase_text = uppercase_text_stock;
+ else
+ self->priv->uppercase_text = NULL;
+
+ if (check_font_contain_text (self->priv->face, punctuation_text_stock))
+ self->priv->punctuation_text = punctuation_text_stock;
+ else
+ self->priv->punctuation_text = NULL;
+
+ sample_string = pango_language_get_sample_string (NULL);
+
+ if (check_font_contain_text (self->priv->face, sample_string))
+ self->priv->sample_string = g_strdup (sample_string);
+ else
+ self->priv->sample_string = random_string_from_available_chars (self->priv->face, 36);
+
+ self->priv->font_name =
+ g_strconcat (self->priv->face->family_name, " ",
+ self->priv->face->style_name, NULL);
+ self->priv->font_supports_title =
+ check_font_contain_text (self->priv->face, self->priv->font_name);
+}
+
+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 = 14;
+ 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;
+ sizes[8] = 96;
+ sizes[9] = 120;
+ sizes[10] = 144;
+ sizes[11] = 168;
+ sizes[12] = 192;
+ sizes[13] = 216;
+ *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
+sushi_font_widget_size_request (GtkWidget *drawing_area,
+ gint *width,
+ gint *height,
+ gint *min_height)
+{
+ SushiFontWidget *self = SUSHI_FONT_WIDGET (drawing_area);
+ SushiFontWidgetPrivate *priv = self->priv;
+ gint i, pixmap_width, pixmap_height;
+ cairo_text_extents_t extents;
+ cairo_font_extents_t font_extents;
+ cairo_font_face_t *font;
+ gint *sizes = NULL, n_sizes, alpha_size;
+ cairo_t *cr;
+ cairo_surface_t *surface;
+ FT_Face face = priv->face;
+ GtkStyleContext *context;
+ GtkStateFlags state;
+ GtkBorder padding;
+
+ if (face == NULL) {
+ if (width != NULL)
+ *width = 1;
+ if (height != NULL)
+ *height = 1;
+ if (min_height != NULL)
+ *min_height = 1;
+
+ return;
+ }
+
+ surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+ SURFACE_SIZE, SURFACE_SIZE);
+ cr = cairo_create (surface);
+ context = gtk_widget_get_style_context (drawing_area);
+ state = gtk_style_context_get_state (context);
+ gtk_style_context_get_padding (context, state, &padding);
+
+ sizes = build_sizes_table (face, &n_sizes, &alpha_size);
+
+ /* calculate size of pixmap to use */
+ pixmap_width = padding.left + padding.right;
+ pixmap_height = 0;
+
+ font = cairo_ft_font_face_create_for_ft_face (face, 0);
+ cairo_set_font_face (cr, font);
+ cairo_font_face_destroy (font);
+
+ if (self->priv->font_supports_title) {
+ cairo_set_font_size (cr, sizes[TITLE_SIZE]);
+ cairo_font_extents (cr, &font_extents);
+ cairo_text_extents (cr, self->priv->font_name, &extents);
+ pixmap_height += font_extents.ascent + font_extents.descent +
+ extents.y_advance + padding.top + padding.bottom;
+ pixmap_width = MAX (pixmap_width, extents.width + padding.left + padding.right);
+ }
+
+ pixmap_height += SECTION_SPACING / 2;
+ cairo_set_font_size (cr, alpha_size);
+ cairo_font_extents (cr, &font_extents);
+
+ if (self->priv->lowercase_text != NULL) {
+ cairo_text_extents (cr, self->priv->lowercase_text, &extents);
+ pixmap_height += font_extents.ascent + font_extents.descent +
+ extents.y_advance + padding.top + padding.bottom;
+ pixmap_width = MAX (pixmap_width, extents.width + padding.left + padding.right);
+ }
+
+ if (self->priv->uppercase_text != NULL) {
+ cairo_text_extents (cr, self->priv->uppercase_text, &extents);
+ pixmap_height += font_extents.ascent + font_extents.descent +
+ extents.y_advance + padding.top + padding.bottom;
+ pixmap_width = MAX (pixmap_width, extents.width + padding.left + padding.right);
+ }
+
+ if (self->priv->punctuation_text != NULL) {
+ cairo_text_extents (cr, self->priv->punctuation_text, &extents);
+ pixmap_height += font_extents.ascent + font_extents.descent +
+ extents.y_advance + padding.top + padding.bottom;
+ pixmap_width = MAX (pixmap_width, extents.width + padding.left + padding.right);
+ }
+
+ pixmap_height += SECTION_SPACING;
+
+ for (i = 0; i < n_sizes; i++) {
+ cairo_set_font_size (cr, sizes[i]);
+ cairo_font_extents (cr, &font_extents);
+ cairo_text_extents (cr, self->priv->sample_string, &extents);
+ pixmap_height += font_extents.ascent + font_extents.descent +
+ extents.y_advance + padding.top + padding.bottom;
+ pixmap_width = MAX (pixmap_width, extents.width + padding.left + padding.right);
+
+ if ((i == 7) && (min_height != NULL))
+ *min_height = pixmap_height;
+ }
+
+ pixmap_height += padding.bottom + SECTION_SPACING;
+
+ if (width != NULL)
+ *width = pixmap_width;
+
+ if (height != NULL)
+ *height = pixmap_height;
+
+ cairo_destroy (cr);
+ cairo_surface_destroy (surface);
+ g_free (sizes);
+}
+
+static void
+sushi_font_widget_get_preferred_width (GtkWidget *drawing_area,
+ gint *minimum_width,
+ gint *natural_width)
+{
+ gint width;
+
+ sushi_font_widget_size_request (drawing_area, &width, NULL, NULL);
+
+ *minimum_width = *natural_width = width;
+}
+
+static void
+sushi_font_widget_get_preferred_height (GtkWidget *drawing_area,
+ gint *minimum_height,
+ gint *natural_height)
+{
+ gint height, min_height;
+
+ sushi_font_widget_size_request (drawing_area, NULL, &height, &min_height);
+
+ *minimum_height = min_height;
+ *natural_height = height;
+}
+
+static gboolean
+sushi_font_widget_draw (GtkWidget *drawing_area,
+ cairo_t *cr)
+{
+ SushiFontWidget *self = SUSHI_FONT_WIDGET (drawing_area);
+ SushiFontWidgetPrivate *priv = self->priv;
+ gint *sizes = NULL, n_sizes, alpha_size, pos_y = 0, i;
+ cairo_font_face_t *font;
+ FT_Face face = priv->face;
+ GtkStyleContext *context;
+ GdkRGBA color;
+ GtkBorder padding;
+ GtkStateFlags state;
+ gint allocated_height;
+
+ if (face == NULL)
+ goto end;
+
+ context = gtk_widget_get_style_context (drawing_area);
+ state = gtk_style_context_get_state (context);
+ gtk_style_context_get_color (context, state, &color);
+ gtk_style_context_get_padding (context, state, &padding);
+
+ gdk_cairo_set_source_rgba (cr, &color);
+
+ 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_face_destroy (font);
+
+ allocated_height = gtk_widget_get_allocated_height (drawing_area);
+
+ /* draw text */
+
+ if (self->priv->font_supports_title) {
+ cairo_set_font_size (cr, sizes[TITLE_SIZE]);
+ draw_string (cr, padding, self->priv->font_name, &pos_y);
+ }
+
+ if (pos_y > allocated_height)
+ goto end;
+
+ pos_y += SECTION_SPACING / 2;
+ cairo_set_font_size (cr, alpha_size);
+
+ if (self->priv->lowercase_text != NULL)
+ draw_string (cr, padding, self->priv->lowercase_text, &pos_y);
+ if (pos_y > allocated_height)
+ goto end;
+
+ if (self->priv->uppercase_text != NULL)
+ draw_string (cr, padding, self->priv->uppercase_text, &pos_y);
+ if (pos_y > allocated_height)
+ goto end;
+
+ if (self->priv->punctuation_text != NULL)
+ draw_string (cr, padding, self->priv->punctuation_text, &pos_y);
+ if (pos_y > allocated_height)
+ goto end;
+
+ pos_y += SECTION_SPACING;
+
+ for (i = 0; i < n_sizes; i++) {
+ cairo_set_font_size (cr, sizes[i]);
+ draw_string (cr, padding, self->priv->sample_string, &pos_y);
+ if (pos_y > allocated_height)
+ break;
+ }
+
+ end:
+ g_free (sizes);
+
+ return FALSE;
+}
+
+static void
+font_face_async_ready_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ SushiFontWidget *self = user_data;
+ GError *error = NULL;
+
+ self->priv->face =
+ sushi_new_ft_face_from_uri_finish (result,
+ &self->priv->face_contents,
+ &error);
+
+ if (error != NULL) {
+ /* FIXME: need to signal the error */
+ g_print ("Can't load the font face: %s\n", error->message);
+ g_error_free (error);
+
+ return;
+ }
+
+ build_strings_for_face (self);
+
+ gtk_widget_queue_resize (GTK_WIDGET (self));
+ g_signal_emit (self, signals[LOADED], 0);
+}
+
+static void
+load_font_face (SushiFontWidget *self)
+{
+ sushi_new_ft_face_from_uri_async (self->priv->library,
+ self->priv->uri,
+ font_face_async_ready_cb,
+ self);
+}
+
+static void
+sushi_font_widget_set_uri (SushiFontWidget *self,
+ const gchar *uri)
+{
+ g_free (self->priv->uri);
+ self->priv->uri = g_strdup (uri);
+
+ load_font_face (self);
+}
+
+static void
+sushi_font_widget_init (SushiFontWidget *self)
+{
+ FT_Error err;
+
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, SUSHI_TYPE_FONT_WIDGET,
+ SushiFontWidgetPrivate);
+
+ self->priv->face = NULL;
+ err = FT_Init_FreeType (&self->priv->library);
+
+ if (err != FT_Err_Ok)
+ g_error ("Unable to initialize FreeType");
+}
+
+static void
+sushi_font_widget_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ SushiFontWidget *self = SUSHI_FONT_WIDGET (object);
+
+ switch (prop_id) {
+ case PROP_URI:
+ g_value_set_string (value, self->priv->uri);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+sushi_font_widget_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ SushiFontWidget *self = SUSHI_FONT_WIDGET (object);
+
+ switch (prop_id) {
+ case PROP_URI:
+ sushi_font_widget_set_uri (self, g_value_get_string (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+sushi_font_widget_finalize (GObject *object)
+{
+ SushiFontWidget *self = SUSHI_FONT_WIDGET (object);
+
+ g_free (self->priv->uri);
+
+ if (self->priv->face != NULL) {
+ FT_Done_Face (self->priv->face);
+ self->priv->face = NULL;
+ }
+
+ g_free (self->priv->font_name);
+ g_free (self->priv->sample_string);
+ g_free (self->priv->face_contents);
+
+ if (self->priv->library != NULL) {
+ FT_Done_FreeType (self->priv->library);
+ self->priv->library = NULL;
+ }
+
+ G_OBJECT_CLASS (sushi_font_widget_parent_class)->finalize (object);
+}
+
+static void
+sushi_font_widget_class_init (SushiFontWidgetClass *klass)
+{
+ GObjectClass *oclass = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *wclass = GTK_WIDGET_CLASS (klass);
+
+ oclass->finalize = sushi_font_widget_finalize;
+ oclass->set_property = sushi_font_widget_set_property;
+ oclass->get_property = sushi_font_widget_get_property;
+
+ wclass->draw = sushi_font_widget_draw;
+ wclass->get_preferred_width = sushi_font_widget_get_preferred_width;
+ wclass->get_preferred_height = sushi_font_widget_get_preferred_height;
+
+ properties[PROP_URI] =
+ g_param_spec_string ("uri",
+ "Uri", "Uri",
+ NULL, G_PARAM_READWRITE);
+
+ signals[LOADED] =
+ g_signal_new ("loaded",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_FIRST,
+ 0, NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ g_object_class_install_properties (oclass, NUM_PROPERTIES, properties);
+ g_type_class_add_private (klass, sizeof (SushiFontWidgetPrivate));
+}
+
+SushiFontWidget *
+sushi_font_widget_new (const gchar *uri)
+{
+ return g_object_new (SUSHI_TYPE_FONT_WIDGET,
+ "uri", uri,
+ NULL);
+}
+
+/**
+ * sushi_font_widget_get_ft_face: (skip)
+ *
+ */
+FT_Face
+sushi_font_widget_get_ft_face (SushiFontWidget *self)
+{
+ return self->priv->face;
+}
+
diff --git a/font-viewer/sushi-font-widget.h b/font-viewer/sushi-font-widget.h
new file mode 100644
index 00000000..d50b734f
--- /dev/null
+++ b/font-viewer/sushi-font-widget.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2011 Red Hat, Inc.
+ *
+ * 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.
+ *
+ * The Sushi project hereby grant permission for non-gpl compatible GStreamer
+ * plugins to be used and distributed together with GStreamer and Sushi. This
+ * permission is above and beyond the permissions granted by the GPL license
+ * Sushi is covered by.
+ *
+ * Authors: Cosimo Cecchi <[email protected]>
+ *
+ */
+
+#ifndef __SUSHI_FONT_WIDGET_H__
+#define __SUSHI_FONT_WIDGET_H__
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+#include <cairo/cairo-ft.h>
+
+G_BEGIN_DECLS
+
+#define SUSHI_TYPE_FONT_WIDGET (sushi_font_widget_get_type ())
+#define SUSHI_FONT_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SUSHI_TYPE_FONT_WIDGET, SushiFontWidget))
+#define SUSHI_IS_FONT_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SUSHI_TYPE_FONT_WIDGET))
+#define SUSHI_FONT_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SUSHI_TYPE_FONT_WIDGET, SushiFontWidgetClass))
+#define SUSHI_IS_FONT_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SUSHI_TYPE_FONT_WIDGET))
+#define SUSHI_FONT_WIDGET_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SUSHI_TYPE_FONT_WIDGET, SushiFontWidgetClass))
+
+typedef struct _SushiFontWidget SushiFontWidget;
+typedef struct _SushiFontWidgetPrivate SushiFontWidgetPrivate;
+typedef struct _SushiFontWidgetClass SushiFontWidgetClass;
+
+struct _SushiFontWidget
+{
+ GtkDrawingArea parent_instance;
+
+ SushiFontWidgetPrivate *priv;
+};
+
+struct _SushiFontWidgetClass
+{
+ GtkDrawingAreaClass parent_class;
+};
+
+GType sushi_font_widget_get_type (void) G_GNUC_CONST;
+
+SushiFontWidget *sushi_font_widget_new (const gchar *uri);
+
+FT_Face sushi_font_widget_get_ft_face (SushiFontWidget *self);
+
+G_END_DECLS
+
+#endif /* __SUSHI_FONT_WIDGET_H__ */
+