[sushi] font: render preview glyphs for non-latin fonts too
- From: Cosimo Cecchi <cosimoc src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [sushi] font: render preview glyphs for non-latin fonts too
- Date: Wed, 11 May 2011 21:56:37 +0000 (UTC)
commit e7560f206e3b179e8ead5a5a6151855086dba640
Author: Cosimo Cecchi <cosimoc gnome org>
Date: Wed May 11 17:53:20 2011 -0400
font: render preview glyphs for non-latin fonts too
We try to be smart here, and if we find out the font doesn't have glyphs
to represent the characters we want to preview, we build a map of all
the glyphs in the font, choose a random set and display those.
The initial lowercase/uppercase/punctuation alphabets are omitted entirely
in case there's no glyphs available to render them.
src/libsushi/sushi-font-widget.c | 227 +++++++++++++++++++++++++++++---------
1 files changed, 173 insertions(+), 54 deletions(-)
---
diff --git a/src/libsushi/sushi-font-widget.c b/src/libsushi/sushi-font-widget.c
index 7ac2fd9..fcffbc0 100644
--- a/src/libsushi/sushi-font-widget.c
+++ b/src/libsushi/sushi-font-widget.c
@@ -19,6 +19,15 @@ struct _SushiFontWidgetPrivate {
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, };
@@ -28,9 +37,9 @@ G_DEFINE_TYPE (SushiFontWidget, sushi_font_widget, GTK_TYPE_DRAWING_AREA);
#define SECTION_SPACING 16
-static const gchar lowercase_text[] = "abcdefghijklmnopqrstuvwxyz";
-static const gchar uppercase_text[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
-static const gchar punctuation_text[] = "0123456789.:,;(*!?')";
+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 */
static void
@@ -54,32 +63,121 @@ draw_string (cairo_t *cr,
}
static gboolean
-check_font_contain_text (FT_Face face, const gchar *text)
+check_font_contain_text (FT_Face face,
+ const gchar *text)
{
- while (text && *text) {
- gunichar wc = g_utf8_get_char (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;
- if (!FT_Get_Char_Index (face, wc))
- return FALSE;
+ for (idx = 0; idx < len; idx++) {
+ gunichar c = string[idx];
- text = g_utf8_next_char (text);
+ if (!FT_Get_Char_Index (face, c)) {
+ retval = FALSE;
+ break;
+ }
+ }
+
+ if (retval)
+ break;
}
- return TRUE;
+ g_free (string);
+
+ return retval;
}
-static const gchar *
-get_sample_string (FT_Face face)
+static GString *
+build_charlist_for_face (FT_Face face)
{
- const gchar *text;
+ GString *string;
+ gunichar 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, c);
+ } while (glyph != 0);
- text = pango_language_get_sample_string (NULL);
+ return string;
+}
+
+static gchar *
+random_string_from_available_chars (FT_Face face,
+ gint n_chars)
+{
+ GString *chars;
+ gint idx, rand;
+ gchar *retval;
+
+ idx = 0;
+ chars = build_charlist_for_face (face);
- if (!check_font_contain_text (face, text)) {
- text = pango_language_get_sample_string (pango_language_from_string ("en_US"));
+ retval = g_malloc (sizeof (char) * (n_chars + 1));
+ retval[n_chars] = '\0';
+
+ while (idx < n_chars) {
+ rand = g_random_int_range (0, chars->len);
+ retval[idx] = chars->str[idx];
+ idx++;
}
- return text;
+ g_string_free (chars, TRUE);
+
+ return retval;
+}
+
+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 *
@@ -126,10 +224,9 @@ sushi_font_widget_size_request (GtkWidget *drawing_area,
gint *width,
gint *height)
{
- SushiFontWidgetPrivate *priv = SUSHI_FONT_WIDGET (drawing_area)->priv;
+ SushiFontWidget *self = SUSHI_FONT_WIDGET (drawing_area);
+ SushiFontWidgetPrivate *priv = self->priv;
gint i, pixmap_width, pixmap_height;
- const gchar *text;
- gchar *font_name;
cairo_text_extents_t extents;
cairo_font_face_t *font;
gint *sizes = NULL, n_sizes, alpha_size;
@@ -153,7 +250,6 @@ sushi_font_widget_size_request (GtkWidget *drawing_area,
state = gtk_style_context_get_state (context);
gtk_style_context_get_padding (context, state, &padding);
- text = get_sample_string (face);
sizes = build_sizes_table (face, &n_sizes, &alpha_size);
/* calculate size of pixmap to use */
@@ -161,39 +257,47 @@ sushi_font_widget_size_request (GtkWidget *drawing_area,
pixmap_height = 0;
font = cairo_ft_font_face_create_for_ft_face (face, 0);
- cairo_set_font_face (cr, font);
- cairo_set_font_size (cr, alpha_size + 6);
- cairo_font_face_destroy (font);
- font_name = g_strconcat (face->family_name, " ",
- face->style_name, NULL);
+ if (self->priv->font_supports_title)
+ cairo_set_font_face (cr, font);
- cairo_text_extents (cr, font_name, &extents);
+ cairo_set_font_size (cr, alpha_size + 6);
+ cairo_text_extents (cr, self->priv->font_name, &extents);
pixmap_height += extents.height + extents.y_advance + padding.top + padding.bottom;
pixmap_width = MAX (pixmap_width, extents.width + padding.left + padding.right);
- g_free (font_name);
+ if (!self->priv->font_supports_title)
+ cairo_set_font_face (cr, font);
+
+ cairo_font_face_destroy (font);
pixmap_height += SECTION_SPACING / 2;
cairo_set_font_size (cr, alpha_size);
- cairo_text_extents (cr, lowercase_text, &extents);
- pixmap_height += extents.height + extents.y_advance + padding.top + padding.bottom;
- pixmap_width = MAX (pixmap_width, extents.width + padding.left + padding.right);
- cairo_text_extents (cr, uppercase_text, &extents);
- pixmap_height += extents.height + extents.y_advance + padding.top + padding.bottom;
- pixmap_width = MAX (pixmap_width, extents.width + padding.left + padding.right);
+ if (self->priv->lowercase_text != NULL) {
+ cairo_text_extents (cr, self->priv->lowercase_text, &extents);
+ pixmap_height += extents.height + extents.y_advance + padding.top + padding.bottom;
+ pixmap_width = MAX (pixmap_width, extents.width + padding.left + padding.right);
+ }
- cairo_text_extents (cr, punctuation_text, &extents);
- pixmap_height += extents.height + 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 += extents.height + 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 += extents.height + 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_text_extents (cr, text, &extents);
+ cairo_text_extents (cr, self->priv->sample_string, &extents);
pixmap_height += extents.height + extents.y_advance + padding.top + padding.bottom;
pixmap_width = MAX (pixmap_width, extents.width + padding.left + padding.right);
}
@@ -238,7 +342,8 @@ static gboolean
sushi_font_widget_draw (GtkWidget *drawing_area,
cairo_t *cr)
{
- SushiFontWidgetPrivate *priv = SUSHI_FONT_WIDGET (drawing_area)->priv;
+ SushiFontWidget *self = SUSHI_FONT_WIDGET (drawing_area);
+ SushiFontWidgetPrivate *priv = self->priv;
gint *sizes = NULL, n_sizes, alpha_size, pos_y = 0, i;
const gchar *text;
cairo_font_face_t *font;
@@ -264,29 +369,37 @@ sushi_font_widget_draw (GtkWidget *drawing_area,
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);
- font_name = g_strconcat (face->family_name, " ",
- face->style_name, NULL);
+ if (self->priv->font_supports_title)
+ cairo_set_font_face (cr, font);
/* draw text */
cairo_set_font_size (cr, alpha_size + 6);
- draw_string (cr, padding, font_name, &pos_y);
+ draw_string (cr, padding, self->priv->font_name, &pos_y);
+
+ if (!self->priv->font_supports_title)
+ cairo_set_font_face (cr, font);
+
+ cairo_font_face_destroy (font);
pos_y += SECTION_SPACING / 2;
cairo_set_font_size (cr, alpha_size);
- draw_string (cr, padding, lowercase_text, &pos_y);
- draw_string (cr, padding, uppercase_text, &pos_y);
- draw_string (cr, padding, punctuation_text, &pos_y);
+
+ if (self->priv->lowercase_text != NULL)
+ draw_string (cr, padding, self->priv->lowercase_text, &pos_y);
+
+ if (self->priv->uppercase_text != NULL)
+ draw_string (cr, padding, self->priv->uppercase_text, &pos_y);
+
+ if (self->priv->punctuation_text != NULL)
+ draw_string (cr, padding, self->priv->punctuation_text, &pos_y);
pos_y += SECTION_SPACING;
- text = get_sample_string (face);
for (i = 0; i < n_sizes; i++) {
cairo_set_font_size (cr, sizes[i]);
- draw_string (cr, padding, text, &pos_y);
+ draw_string (cr, padding, self->priv->sample_string, &pos_y);
}
end:
@@ -297,17 +410,19 @@ sushi_font_widget_draw (GtkWidget *drawing_area,
static void
font_face_async_ready_cb (GObject *object,
- GAsyncResult *res,
+ GAsyncResult *result,
gpointer user_data)
{
SushiFontWidget *self = user_data;
- FT_Face font_face;
+ FT_Face face;
gchar *contents = NULL;
GError *error = NULL;
+ gint i, res;
- self->priv->face = sushi_new_ft_face_from_uri_finish (res,
- &self->priv->face_contents,
- &error);
+ face = 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 */
@@ -315,6 +430,8 @@ font_face_async_ready_cb (GObject *object,
return;
}
+ build_strings_for_face (self);
+
gtk_widget_queue_resize (GTK_WIDGET (self));
g_signal_emit (self, signals[LOADED], 0);
}
@@ -394,6 +511,8 @@ sushi_font_widget_finalize (GObject *object)
self->priv->face = NULL;
}
+ g_free (self->priv->font_name);
+ g_free (self->priv->sample_string);
g_free (self->priv->face_contents);
G_OBJECT_CLASS (sushi_font_widget_parent_class)->finalize (object);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]