summaryrefslogtreecommitdiff
path: root/font-viewer/font-thumbnailer.c
diff options
context:
space:
mode:
Diffstat (limited to 'font-viewer/font-thumbnailer.c')
-rw-r--r--font-viewer/font-thumbnailer.c380
1 files changed, 136 insertions, 244 deletions
diff --git a/font-viewer/font-thumbnailer.c b/font-viewer/font-thumbnailer.c
index c7fe58bb..d2d8f759 100644
--- a/font-viewer/font-thumbnailer.c
+++ b/font-viewer/font-thumbnailer.c
@@ -1,9 +1,9 @@
/* -*- mode: C; c-basic-offset: 4 -*- */
-
/*
* font-thumbnailer: a thumbnailer for font files, using FreeType
*
* Copyright (C) 2002-2003 James Henstridge <[email protected]>
+ * Copyright (C) 2012 Cosimo Cecchi <[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
@@ -27,12 +27,16 @@
#include <stdio.h>
#include <locale.h>
#include <ft2build.h>
+#include <math.h>
#include FT_FREETYPE_H
+#include <gdk/gdk.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
#include <gio/gio.h>
#include <glib/gi18n.h>
+#include <cairo/cairo-ft.h>
+
#include "sushi-font-loader.h"
#include "totem-resources.h"
@@ -50,194 +54,110 @@ get_ft_error (FT_Error error)
}
}
-#define FONT_SIZE 64
-#define PAD_PIXELS 4
+#define THUMB_SIZE 128
+#define PADDING_VERTICAL 2
+#define PADDING_HORIZONTAL 4
-static void
-draw_bitmap (GdkPixbuf *pixbuf,
- FT_Bitmap *bitmap,
- gint off_x,
- gint off_y)
+static gboolean
+check_font_contain_text (FT_Face face,
+ const gchar *text)
{
- 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;
- }
- }
-}
+ gunichar *string;
+ glong len, idx, map;
+ FT_CharMap charmap;
+ gboolean retval;
-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;
+ string = g_utf8_to_ucs4_fast (text, -1, &len);
- slot = face->glyph;
+ for (map = 0; map < face->num_charmaps; map++) {
+ charmap = face->charmaps[map];
+ FT_Set_Charmap (face, charmap);
- 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;
- }
+ retval = TRUE;
- draw_bitmap (pixbuf, &slot->bitmap,
- *pen_x + slot->bitmap_left,
- *pen_y - slot->bitmap_top);
+ for (idx = 0; idx < len; idx++) {
+ gunichar c = string[idx];
- *pen_x += slot->advance.x >> 6;
-}
-
-static void
-save_pixbuf (GdkPixbuf *pixbuf,
- const gchar *filename)
-{
- guchar *buffer;
- gint p_width, p_height, p_rowstride;
- gint i, j;
- gint trim_left, trim_right, trim_top, trim_bottom;
- GdkPixbuf *subpixbuf;
-
- buffer = gdk_pixbuf_get_pixels (pixbuf);
- p_width = gdk_pixbuf_get_width (pixbuf);
- p_height = gdk_pixbuf_get_height (pixbuf);
- p_rowstride = gdk_pixbuf_get_rowstride (pixbuf);
-
- for (i = 0; i < p_width; i++) {
- gboolean seen_pixel = FALSE;
-
- for (j = 0; j < p_height; j++) {
- gint offset = j * p_rowstride + 3 * i;
-
- seen_pixel = (buffer[offset] != 0xff ||
- buffer[offset + 1] != 0xff ||
- buffer[offset + 2] != 0xff);
-
- if (seen_pixel)
- break;
- }
-
- if (seen_pixel)
- break;
+ if (!FT_Get_Char_Index (face, c)) {
+ retval = FALSE;
+ 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;
+ if (retval)
+ break;
+ }
- for (j = 0; j < p_height; j++) {
- gint offset = j * p_rowstride + 3 * i;
+ g_free (string);
- seen_pixel = (buffer[offset] != 0xff ||
- buffer[offset + 1] != 0xff ||
- buffer[offset + 2] != 0xff);
-
- if (seen_pixel)
- break;
- }
+ return retval;
+}
- if (seen_pixel)
- break;
- }
+static gchar *
+check_for_ascii_glyph_numbers (FT_Face face,
+ gboolean *found_ascii)
+{
+ GString *ascii_string, *string;
+ gulong c;
+ guint glyph, found = 0;
- trim_right = MAX (trim_left, i);
- trim_right = MIN (trim_right + PAD_PIXELS, p_width - 1);
+ string = g_string_new (NULL);
+ ascii_string = g_string_new (NULL);
+ *found_ascii = FALSE;
- for (j = 0; j < p_height; j++) {
- gboolean seen_pixel = FALSE;
+ c = FT_Get_First_Char (face, &glyph);
- for (i = 0; i < p_width; i++) {
- gint offset = j * p_rowstride + 3 * i;
+ do {
+ if (glyph == 65 || glyph == 97) {
+ g_string_append_unichar (ascii_string, (gunichar) c);
+ found++;
+ }
- seen_pixel = (buffer[offset] != 0xff ||
- buffer[offset + 1] != 0xff ||
- buffer[offset + 2] != 0xff);
+ if (found == 2)
+ break;
- if (seen_pixel)
- break;
- }
+ g_string_append_unichar (string, (gunichar) c);
+ c = FT_Get_Next_Char (face, c, &glyph);
+ } while (glyph != 0);
- if (seen_pixel)
- break;
+ if (found == 2) {
+ *found_ascii = TRUE;
+ g_string_free (string, TRUE);
+ return g_string_free (ascii_string, FALSE);
+ } else {
+ g_string_free (ascii_string, TRUE);
+ return g_string_free (string, FALSE);
}
+}
- trim_top = MIN (p_height, j);
- trim_top = MAX (trim_top - PAD_PIXELS, 0);
+static gchar *
+build_fallback_thumbstr (FT_Face face)
+{
+ gchar *chars;
+ gint idx, total_chars;
+ GString *retval;
+ gchar *ptr, *end;
+ gboolean found_ascii;
- for (j = p_height - 1; j >= trim_top; j--) {
- gboolean seen_pixel = FALSE;
+ chars = check_for_ascii_glyph_numbers (face, &found_ascii);
- for (i = 0; i < p_width; i++) {
- gint offset = j * p_rowstride + 3 * i;
+ if (found_ascii)
+ return chars;
- seen_pixel = (buffer[offset] != 0xff ||
- buffer[offset + 1] != 0xff ||
- buffer[offset + 2] != 0xff);
+ idx = 0;
+ retval = g_string_new (NULL);
+ total_chars = g_utf8_strlen (chars, -1);
- if (seen_pixel)
- break;
- }
+ while (idx < 2) {
+ total_chars = (gint) floor (total_chars / 2.0);
+ ptr = g_utf8_offset_to_pointer (chars, total_chars);
+ end = g_utf8_find_next_char (ptr, NULL);
- if (seen_pixel)
- break;
+ g_string_append_len (retval, ptr, end - ptr);
+ idx++;
}
- 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);
+ return g_string_free (retval, FALSE);
}
int
@@ -247,14 +167,8 @@ main (int argc,
FT_Error error;
FT_Library library;
FT_Face face;
- FT_UInt glyph_index1, glyph_index2;
GFile *file;
- GdkPixbuf *pixbuf;
- guchar *buffer;
- gint i, len, pen_x, pen_y;
- gunichar *thumbstr = NULL;
- glong thumbstr_len = 2;
- gint font_size = FONT_SIZE;
+ gint font_size, thumb_size = THUMB_SIZE;
gchar *thumbstr_utf8 = NULL, *help, *uri;
gchar **arguments = NULL;
GOptionContext *context;
@@ -262,12 +176,20 @@ main (int argc,
gchar *contents = NULL;
gboolean retval, default_thumbstr = TRUE;
gint rv = 1;
+ GdkRGBA white = { 1.0, 1.0, 1.0, 1.0 };
+ GdkRGBA black = { 0.0, 0.0, 0.0, 1.0 };
+ cairo_surface_t *surface;
+ cairo_t *cr;
+ cairo_text_extents_t text_extents;
+ cairo_font_face_t *font;
+ gchar *str;
+ gdouble scale, scale_x, scale_y;
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") },
+ { "size", 's', 0, G_OPTION_ARG_INT, &thumb_size,
+ N_("Thumbnail size (default: 128)"), N_("SIZE") },
{ G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &arguments,
NULL, N_("FONT-FILE OUTPUT-FILE") },
{ NULL }
@@ -279,6 +201,8 @@ main (int argc,
setlocale (LC_ALL, "");
+ g_type_init ();
+
context = g_option_context_new (NULL);
g_option_context_add_main_entries (context, options, GETTEXT_PACKAGE);
@@ -301,20 +225,9 @@ main (int argc,
g_option_context_free (context);
- if (thumbstr_utf8 != NULL) {
- /* build ucs4 version of string to thumbnail */
- gerror = NULL;
- thumbstr = g_utf8_to_ucs4 (thumbstr_utf8, strlen (thumbstr_utf8),
- NULL, &thumbstr_len, &gerror);
+ if (thumbstr_utf8 != NULL)
default_thumbstr = FALSE;
- if (gerror != NULL) {
- g_printerr ("Failed to convert: %s\n", gerror->message);
- g_error_free (gerror);
- goto out;
- }
- }
-
error = FT_Init_FreeType (&library);
if (error) {
g_printerr("Could not initialise freetype: %s\n", get_ft_error (error));
@@ -338,78 +251,57 @@ main (int argc,
g_free (uri);
- error = FT_Set_Pixel_Sizes (face, 0, font_size);
- if (error) {
- g_printerr ("Could not set pixel size: %s\n", get_ft_error (error));
- goto out;
- }
-
- for (i = 0; i < face->num_charmaps; i++) {
- if (face->charmaps[i]->encoding == ft_encoding_latin_1 ||
- face->charmaps[i]->encoding == ft_encoding_unicode ||
- face->charmaps[i]->encoding == ft_encoding_apple_roman) {
- error = FT_Set_Charmap (face, face->charmaps[i]);
- if (error) {
- g_printerr("Could not set charmap: %s\n", get_ft_error(error));
- goto out;
- }
- break;
- }
- }
-
- pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
- FALSE, /* has-alpha */
- 8, /* bits-per-sample */
- font_size * 3 * thumbstr_len / 2, /* width */
- font_size * 1.5); /* height */
- if (!pixbuf) {
- g_printerr ("Could not create pixbuf\n");
- goto out;
+ if (default_thumbstr) {
+ if (check_font_contain_text (face, "Aa"))
+ str = g_strdup ("Aa");
+ else
+ str = build_fallback_thumbstr (face);
+ } else {
+ str = thumbstr_utf8;
}
- buffer = gdk_pixbuf_get_pixels (pixbuf);
- len = gdk_pixbuf_get_rowstride (pixbuf) * gdk_pixbuf_get_height (pixbuf);
-
- /* fill the pixbuf buffer to make it white */
- for (i = 0; i < len; i++)
- buffer[i] = 255;
+ surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+ thumb_size, thumb_size);
+ cr = cairo_create (surface);
- pen_x = font_size / 2;
- pen_y = font_size;
+ gdk_cairo_set_source_rgba (cr, &white);
+ cairo_paint (cr);
- if (default_thumbstr) {
- glyph_index1 = FT_Get_Char_Index (face, 'A');
- glyph_index2 = FT_Get_Char_Index (face, 'a');
+ font = cairo_ft_font_face_create_for_ft_face (face, 0);
+ cairo_set_font_face (cr, font);
+ cairo_font_face_destroy (font);
- /* 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);
+ font_size = thumb_size - 2 * PADDING_VERTICAL;
+ cairo_set_font_size (cr, font_size);
+ cairo_text_extents (cr, str, &text_extents);
- if (glyph_index2 == 0)
- glyph_index2 = MIN (97, face->num_glyphs - 1);
+ if ((text_extents.width) > (thumb_size - 2 * PADDING_HORIZONTAL)) {
+ scale_x = (gdouble) (thumb_size - 2 * PADDING_HORIZONTAL) / (text_extents.width);
+ } else {
+ scale_x = 1.0;
+ }
- draw_char (pixbuf, face, glyph_index1, &pen_x, &pen_y);
- draw_char (pixbuf, face, glyph_index2, &pen_x, &pen_y);
+ if ((text_extents.height) > (thumb_size - 2 * PADDING_VERTICAL)) {
+ scale_y = (gdouble) (thumb_size - 2 * PADDING_VERTICAL) / (text_extents.height);
} else {
- const gunichar *p = thumbstr;
- FT_Select_Charmap (face, FT_ENCODING_UNICODE);
- i = 0;
-
- while (i < thumbstr_len) {
- glyph_index1 = FT_Get_Char_Index (face, *p);
- draw_char (pixbuf, face, glyph_index1, &pen_x, &pen_y);
- i++;
- p++;
- }
+ scale_y = 1.0;
}
- save_pixbuf (pixbuf, arguments[1]);
- g_object_unref (pixbuf);
+ scale = MIN (scale_x, scale_y);
+ cairo_scale (cr, scale, scale);
+ cairo_translate (cr,
+ PADDING_HORIZONTAL - text_extents.x_bearing + (thumb_size - scale * text_extents.width) / 2.0,
+ PADDING_VERTICAL - text_extents.y_bearing + (thumb_size - scale * text_extents.height) / 2.0);
+
+ gdk_cairo_set_source_rgba (cr, &black);
+ cairo_show_text (cr, str);
+ cairo_destroy (cr);
+
+ cairo_surface_write_to_png (surface, arguments[1]);
+ cairo_surface_destroy (surface);
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));
@@ -428,9 +320,9 @@ main (int argc,
out:
g_strfreev (arguments);
- g_free (thumbstr);
- g_free (thumbstr_utf8);
+ g_free (str);
g_free (contents);
return rv;
}
+