[sushi] font: render preview glyphs for non-latin fonts too



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]