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.c399
1 files changed, 399 insertions, 0 deletions
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;
+}