1
/* -*- mode: C; c-basic-offset: 4 -*-
2
* fontilus - a collection of font utilities for GNOME
3
* Copyright (C) 2002-2003 James Henstridge <james@daa.com.au>
5
* This program is free software; you can redistribute it and/or modify
6
* it under the terms of the GNU General Public License as published by
7
* the Free Software Foundation; either version 2 of the License, or
8
* (at your option) any later version.
10
* This program is distributed in the hope that it will be useful,
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
* GNU General Public License for more details.
15
* You should have received a copy of the GNU General Public License
16
* along with this program; if not, write to the Free Software
17
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25
#include FT_FREETYPE_H
26
#include FT_TYPE1_TABLES_H
27
#include FT_SFNT_NAMES_H
28
#include FT_TRUETYPE_IDS_H
30
#include <X11/Xft/Xft.h>
35
#include <glib/gi18n.h>
37
FT_Error FT_New_Face_From_URI(FT_Library library,
42
static const gchar lowercase_text[] = "abcdefghijklmnopqrstuvwxyz";
43
static const gchar uppercase_text[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
44
static const gchar punctuation_text[] = "0123456789.:,;(*!?')";
46
static inline XftFont *
47
get_font(Display *xdisplay, FT_Face face, gint size, FcCharSet *charset)
51
int screen = DefaultScreen (xdisplay);
53
pattern = FcPatternBuild(NULL,
54
FC_FT_FACE, FcTypeFTFace, face,
55
FC_PIXEL_SIZE, FcTypeDouble, (double)size,
59
FcPatternAddCharSet (pattern, "charset", charset);
61
FcConfigSubstitute (NULL, pattern, FcMatchPattern);
62
XftDefaultSubstitute (xdisplay, screen, pattern);
64
font = XftFontOpenPattern(xdisplay, pattern);
70
draw_string(Display *xdisplay, XftDraw *draw, XftFont *font, XftColor *colour,
71
const gchar *text, gint *pos_y)
74
gint len = strlen(text);
76
XftTextExtentsUtf8(xdisplay, font, (guchar *)text, len, &extents);
77
XftDrawStringUtf8(draw, colour, font, 4, *pos_y + extents.y, (guchar *)text, len);
78
*pos_y += extents.height + 4;
82
check_font_contain_text (FT_Face face, const gchar *text)
86
gunichar wc = g_utf8_get_char (text);
87
if (!FT_Get_Char_Index (face, wc))
90
text = g_utf8_next_char (text);
97
static gboolean expose_event(GtkWidget *widget, GdkEventExpose *event, GdkPixmap *pixmap);
100
create_text_pixmap(GtkWidget *drawing_area, FT_Face face)
102
gint i, pixmap_width, pixmap_height, pos_y, textlen;
103
GdkPixmap *pixmap = NULL;
113
gint *sizes = NULL, n_sizes, alpha_size;
114
FcCharSet *charset = NULL;
115
GdkWindow *window = gtk_widget_get_window (drawing_area);
117
text = pango_language_get_sample_string(NULL);
118
if (! check_font_contain_text (face, text))
120
pango_language_get_sample_string (pango_language_from_string ("en_US"));
123
textlen = strlen(text);
125
/* create the XftDraw */
126
xdisplay = GDK_PIXMAP_XDISPLAY(window);
127
xvisual = GDK_VISUAL_XVISUAL(gdk_drawable_get_visual(window));
128
xcolormap = GDK_COLORMAP_XCOLORMAP(gdk_drawable_get_colormap(window));
129
XftColorAllocName(xdisplay, xvisual, xcolormap, "black", &colour);
131
/* work out what sizes to render */
132
if (FT_IS_SCALABLE(face)) {
134
sizes = g_new(gint, n_sizes);
145
/* use fixed sizes */
146
n_sizes = face->num_fixed_sizes;
147
sizes = g_new(gint, n_sizes);
149
for (i = 0; i < face->num_fixed_sizes; i++) {
150
sizes[i] = face->available_sizes[i].height;
152
/* work out which font size to render */
153
if (face->available_sizes[i].height <= 24)
154
alpha_size = face->available_sizes[i].height;
158
/* calculate size of pixmap to use (with 4 pixels padding) ... */
162
font = get_font(xdisplay, face, alpha_size, charset);
163
charset = FcCharSetCopy (font->charset);
164
XftTextExtentsUtf8(xdisplay, font,
165
(guchar *)lowercase_text, strlen(lowercase_text), &extents);
166
pixmap_height += extents.height + 4;
167
pixmap_width = MAX(pixmap_width, 8 + extents.width);
168
XftTextExtentsUtf8(xdisplay, font,
169
(guchar *)uppercase_text, strlen(uppercase_text), &extents);
170
pixmap_height += extents.height + 4;
171
pixmap_width = MAX(pixmap_width, 8 + extents.width);
172
XftTextExtentsUtf8(xdisplay, font,
173
(guchar *)punctuation_text, strlen(punctuation_text), &extents);
174
pixmap_height += extents.height + 4;
175
pixmap_width = MAX(pixmap_width, 8 + extents.width);
176
XftFontClose(xdisplay, font);
180
for (i = 0; i < n_sizes; i++) {
181
font = get_font(xdisplay, face, sizes[i], charset);
183
XftTextExtentsUtf8(xdisplay, font, (guchar *)text, textlen, &extents);
184
pixmap_height += extents.height + 4;
185
pixmap_width = MAX(pixmap_width, 8 + extents.width);
186
XftFontClose(xdisplay, font);
190
gtk_widget_set_size_request(drawing_area, pixmap_width, pixmap_height);
191
pixmap = gdk_pixmap_new(window,
192
pixmap_width, pixmap_height, -1);
195
gdk_draw_rectangle(pixmap, gtk_widget_get_style(drawing_area)->white_gc,
196
TRUE, 0, 0, pixmap_width, pixmap_height);
198
xdrawable = GDK_DRAWABLE_XID(pixmap);
199
draw = XftDrawCreate(xdisplay, xdrawable, xvisual, xcolormap);
203
font = get_font(xdisplay, face, alpha_size, charset);
204
draw_string(xdisplay, draw, font, &colour, lowercase_text, &pos_y);
205
draw_string(xdisplay, draw, font, &colour, uppercase_text, &pos_y);
206
draw_string(xdisplay, draw, font, &colour, punctuation_text, &pos_y);
207
XftFontClose(xdisplay, font);
210
for (i = 0; i < n_sizes; i++) {
211
font = get_font(xdisplay, face, sizes[i], charset);
213
draw_string(xdisplay, draw, font, &colour, text, &pos_y);
214
XftFontClose(xdisplay, font);
217
g_signal_connect(drawing_area, "expose-event", G_CALLBACK(expose_event),
222
FcCharSetDestroy (charset);
227
add_row(GtkWidget *table, gint *row_p,
228
const gchar *name, const gchar *value, gboolean multiline,
234
bold_name = g_strconcat("<b>", name, "</b>", NULL);
235
name_w = gtk_label_new(bold_name);
237
gtk_misc_set_alignment(GTK_MISC(name_w), 0.0, 0.0);
238
gtk_label_set_use_markup(GTK_LABEL(name_w), TRUE);
240
gtk_table_attach(GTK_TABLE(table), name_w, 0, 1, *row_p, *row_p + 1,
241
GTK_FILL, GTK_FILL, 0, 0);
244
GtkWidget *label, *viewport;
245
GtkScrolledWindow *swin;
248
label = gtk_label_new (value);
249
gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
250
gtk_label_set_selectable (GTK_LABEL (label), TRUE);
251
gtk_widget_set_size_request (label, 200, -1);
252
gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.0);
255
swin = GTK_SCROLLED_WINDOW(gtk_scrolled_window_new(NULL, NULL));
256
gtk_scrolled_window_set_policy(swin,
257
GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
259
viewport = gtk_viewport_new (gtk_scrolled_window_get_hadjustment (swin),
260
gtk_scrolled_window_get_vadjustment (swin));
261
gtk_viewport_set_shadow_type (GTK_VIEWPORT (viewport), GTK_SHADOW_NONE);
263
gtk_container_add (GTK_CONTAINER(swin), viewport);
266
flags = GTK_FILL|GTK_EXPAND;
269
gtk_table_attach(GTK_TABLE(table), GTK_WIDGET(swin), 0, 2, *row_p, *row_p + 1,
270
GTK_FILL|GTK_EXPAND, flags, 0, 0);
272
gtk_container_add (GTK_CONTAINER (viewport), label);
274
GtkWidget *label = gtk_label_new(value);
275
gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
276
gtk_label_set_selectable(GTK_LABEL(label), TRUE);
277
gtk_table_attach(GTK_TABLE(table), label, 1, 2, *row_p, *row_p + 1,
278
GTK_FILL|GTK_EXPAND, GTK_FILL, 0, 0);
286
add_face_info(GtkWidget *table, gint *row_p, const gchar *uri, FT_Face face)
291
PS_FontInfoRec ps_info;
293
add_row(table, row_p, _("Name:"), face->family_name, FALSE, FALSE);
295
if (face->style_name)
296
add_row(table, row_p, _("Style:"), face->style_name, FALSE, FALSE);
298
file = g_file_new_for_uri (uri);
300
info = g_file_query_info (file,
301
G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE ","
302
G_FILE_ATTRIBUTE_STANDARD_SIZE,
303
G_FILE_QUERY_INFO_NONE,
305
g_object_unref (file);
308
s = g_content_type_get_description (g_file_info_get_content_type (info));
309
add_row (table, row_p, _("Type:"), s, FALSE, FALSE);
312
s = g_format_size_for_display (g_file_info_get_size (info));
313
add_row (table, row_p, _("Size:"), s, FALSE, FALSE);
316
g_object_unref (info);
319
if (FT_IS_SFNT(face)) {
321
gchar *version = NULL, *copyright = NULL, *description = NULL;
323
len = FT_Get_Sfnt_Name_Count(face);
324
for (i = 0; i < len; i++) {
327
if (FT_Get_Sfnt_Name(face, i, &sname) != 0)
330
/* only handle the unicode names for US langid */
331
if (!(sname.platform_id == TT_PLATFORM_MICROSOFT &&
332
sname.encoding_id == TT_MS_ID_UNICODE_CS &&
333
sname.language_id == TT_MS_LANGID_ENGLISH_UNITED_STATES))
336
switch (sname.name_id) {
337
case TT_NAME_ID_COPYRIGHT:
339
copyright = g_convert((gchar *)sname.string, sname.string_len,
340
"UTF-8", "UTF-16BE", NULL, NULL, NULL);
342
case TT_NAME_ID_VERSION_STRING:
344
version = g_convert((gchar *)sname.string, sname.string_len,
345
"UTF-8", "UTF-16BE", NULL, NULL, NULL);
347
case TT_NAME_ID_DESCRIPTION:
349
description = g_convert((gchar *)sname.string, sname.string_len,
350
"UTF-8", "UTF-16BE", NULL, NULL, NULL);
357
add_row(table, row_p, _("Version:"), version, FALSE, FALSE);
361
add_row(table, row_p, _("Copyright:"), copyright, TRUE, TRUE);
365
add_row(table, row_p, _("Description:"), description, TRUE, TRUE);
368
} else if (FT_Get_PS_Font_Info(face, &ps_info) == 0) {
369
if (ps_info.version && g_utf8_validate(ps_info.version, -1, NULL))
370
add_row(table, row_p, _("Version:"), ps_info.version, FALSE, FALSE);
371
if (ps_info.notice && g_utf8_validate(ps_info.notice, -1, NULL))
372
add_row(table, row_p, _("Copyright:"), ps_info.notice, TRUE, FALSE);
377
expose_event(GtkWidget *widget, GdkEventExpose *event, GdkPixmap *pixmap)
379
gdk_draw_drawable(gtk_widget_get_window (widget),
380
gtk_widget_get_style (widget)->fg_gc[gtk_widget_get_state (widget)],
382
event->area.x, event->area.y,
383
event->area.x, event->area.y,
384
event->area.width, event->area.height);
389
set_icon(GtkWindow *window, const gchar *uri)
395
GtkIconTheme *icon_theme;
396
const gchar *icon_name = NULL, *content_type;
398
screen = gtk_widget_get_screen (GTK_WIDGET (window));
399
icon_theme = gtk_icon_theme_get_for_screen (screen);
401
file = g_file_new_for_uri (uri);
403
info = g_file_query_info (file, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
404
G_FILE_QUERY_INFO_NONE, NULL, NULL);
405
g_object_unref (file);
410
content_type = g_file_info_get_content_type (info);
411
icon = g_content_type_get_icon (content_type);
413
if (G_IS_THEMED_ICON (icon)) {
414
const gchar * const *names = NULL;
416
names = g_themed_icon_get_names (G_THEMED_ICON (icon));
419
for (i = 0; names[i]; i++) {
420
if (gtk_icon_theme_has_icon (icon_theme, names[i])) {
421
icon_name = names[i];
429
gtk_window_set_icon_name (window, icon_name);
432
g_object_unref (icon);
436
font_install_finished_cb (GObject *source_object,
442
g_file_copy_finish (G_FILE (source_object), res, &err);
445
gtk_button_set_label (GTK_BUTTON (data), _("Installed"));
448
gtk_button_set_label (GTK_BUTTON (data), _("Install Failed"));
449
g_debug ("Install failed: %s", err->message);
452
gtk_widget_set_sensitive (GTK_WIDGET (data), FALSE);
456
install_button_clicked_cb (GtkButton *button,
457
const gchar *font_file)
460
gchar *dest_path, *dest_filename;
464
/* first check if ~/.fonts exists */
465
dest_path = g_build_filename (g_get_home_dir (), ".fonts", NULL);
466
if (!g_file_test (dest_path, G_FILE_TEST_EXISTS)) {
467
GFile *f = g_file_new_for_path (dest_path);
468
g_file_make_directory_with_parents (f, NULL, &err);
471
/* TODO: show error dialog */
472
g_warning ("Could not create fonts directory: %s", err->message);
480
/* create destination filename */
481
src = g_file_new_for_uri (font_file);
483
dest_filename = g_file_get_basename (src);
484
dest_path = g_build_filename (g_get_home_dir (), ".fonts", dest_filename, NULL);
485
g_free (dest_filename);
487
dest = g_file_new_for_path (dest_path);
489
/* TODO: show error dialog if file exists */
490
g_file_copy_async (src, dest, G_FILE_COPY_NONE, 0, NULL, NULL, NULL,
491
font_install_finished_cb, button);
493
g_object_unref (src);
494
g_object_unref (dest);
499
main(int argc, char **argv)
505
gchar *font_file, *title;
507
GtkWidget *window, *hbox, *table, *swin, *drawing_area;
509
GdkColor white = { 0, 0xffff, 0xffff, 0xffff };
510
GtkWidget *button, *align;
512
bindtextdomain(GETTEXT_PACKAGE, GNOMELOCALEDIR);
513
bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
514
textdomain(GETTEXT_PACKAGE);
516
gtk_init(&argc, &argv);
519
g_printerr(_("usage: %s fontfile\n"), argv[0]);
523
if (!XftInitFtLibrary()) {
524
g_printerr("could not initialise freetype library\n");
528
error = FT_Init_FreeType(&library);
530
g_printerr("could not initialise freetype\n");
534
file = g_file_new_for_commandline_arg (argv[1]);
535
font_file = g_file_get_uri (file);
536
g_object_unref (file);
539
g_printerr("could not parse argument into a URI\n");
543
error = FT_New_Face_From_URI(library, font_file, 0, &face);
545
g_printerr("could not load face '%s'\n", font_file);
549
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
550
title = g_strconcat(face->family_name,
551
face->style_name ? ", " : "",
552
face->style_name, NULL);
553
gtk_window_set_title(GTK_WINDOW(window), title);
554
set_icon(GTK_WINDOW(window), font_file);
556
gtk_window_set_resizable(GTK_WINDOW(window), TRUE);
558
hbox = gtk_hbox_new(FALSE, 0);
559
gtk_container_add(GTK_CONTAINER(window), hbox);
561
swin = gtk_scrolled_window_new(NULL, NULL);
562
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(swin),
563
GTK_POLICY_AUTOMATIC, GTK_POLICY_NEVER);
564
gtk_box_pack_start(GTK_BOX(hbox), swin, TRUE, TRUE, 0);
566
drawing_area = gtk_drawing_area_new();
567
gtk_widget_modify_bg(drawing_area, GTK_STATE_NORMAL, &white);
568
gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(swin),
570
g_signal_connect (drawing_area, "realize", create_text_pixmap, face);
572
/* set the minimum size on the scrolled window to prevent
573
* unnecessary scrolling */
574
gtk_widget_set_size_request(swin, 500, -1);
576
g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
578
table = gtk_table_new(1, 2, FALSE);
579
gtk_container_set_border_width(GTK_CONTAINER(table), 5);
580
gtk_box_pack_start(GTK_BOX(hbox), table, FALSE, TRUE, 0);
583
add_face_info(table, &row, font_file, face);
585
/* add install button */
586
align = gtk_alignment_new (1.0, 0.5, 0.0, 0.0);
587
gtk_table_attach (GTK_TABLE (table), align, 0, 2, row, row + 1,
588
GTK_FILL|GTK_EXPAND, GTK_FILL, 0, 0);
590
button = gtk_button_new_with_mnemonic (_("I_nstall Font"));
591
g_signal_connect (button, "clicked",
592
G_CALLBACK (install_button_clicked_cb), font_file);
593
gtk_container_add (GTK_CONTAINER (align), button);
596
gtk_table_set_col_spacings(GTK_TABLE(table), 8);
597
gtk_table_set_row_spacings(GTK_TABLE(table), 2);
598
gtk_widget_show_all(window);