mirror of
https://gitlab.gnome.org/GNOME/gtk.git
synced 2024-11-13 04:10:13 +00:00
gtk-demo: Expand font features demo
Add more features to the list, allow selecting script/language from the set that is supported by the font, indicate which features are present in the font for the selected script/language, and expand the default specimen to cover latin, cyrillic and greek.
This commit is contained in:
parent
d5d6a050c5
commit
9de3b24c20
File diff suppressed because it is too large
Load Diff
@ -1,15 +1,23 @@
|
||||
/* Pango/Font Features
|
||||
*
|
||||
* This demonstrates support for OpenType font features with
|
||||
* Pango attributes. The attributes can be used manually or
|
||||
* via Pango markup.
|
||||
* This example demonstrates support for OpenType font features with
|
||||
* Pango attributes. The attributes can be used manually or via Pango
|
||||
* markup.
|
||||
*
|
||||
* It can also be used to explore available features in OpenType fonts
|
||||
* and their effect.
|
||||
*/
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
#include <pango/pangofc-font.h>
|
||||
#include <hb.h>
|
||||
#include <hb-ot.h>
|
||||
#include <hb-ft.h>
|
||||
|
||||
static GtkWidget *label;
|
||||
static GtkWidget *settings;
|
||||
static GtkWidget *font;
|
||||
static GtkWidget *script_lang;
|
||||
static GtkWidget *resetbutton;
|
||||
static GtkWidget *numcasedefault;
|
||||
static GtkWidget *numspacedefault;
|
||||
@ -17,10 +25,19 @@ static GtkWidget *fractiondefault;
|
||||
static GtkWidget *stack;
|
||||
static GtkWidget *entry;
|
||||
|
||||
static GtkWidget *toggle[24];
|
||||
#define num_features 40
|
||||
|
||||
static GtkWidget *toggle[num_features];
|
||||
static GtkWidget *icon[num_features];
|
||||
static const char *feature_names[num_features] = {
|
||||
"kern", "liga", "dlig", "hlig", "clig", "smcp", "c2sc", "pcap", "c2pc", "unic",
|
||||
"cpsp", "case", "lnum", "onum", "pnum", "tnum", "frac", "afrc", "zero", "nalt",
|
||||
"sinf", "swsh", "cswh", "locl", "calt", "hist", "salt", "titl", "rand", "subs",
|
||||
"sups", "init", "medi", "fina", "isol", "ss01", "ss02", "ss03", "ss04", "ss05"
|
||||
};
|
||||
|
||||
static void
|
||||
update (void)
|
||||
update_display (void)
|
||||
{
|
||||
GString *s;
|
||||
char *font_desc;
|
||||
@ -28,6 +45,10 @@ update (void)
|
||||
const char *text;
|
||||
gboolean has_feature;
|
||||
int i;
|
||||
hb_tag_t lang_tag;
|
||||
GtkTreeModel *model;
|
||||
GtkTreeIter iter;
|
||||
const char *lang;
|
||||
|
||||
text = gtk_entry_get_text (GTK_ENTRY (entry));
|
||||
|
||||
@ -36,7 +57,7 @@ update (void)
|
||||
s = g_string_new ("");
|
||||
|
||||
has_feature = FALSE;
|
||||
for (i = 0; i < 24; i++)
|
||||
for (i = 0; i < num_features; i++)
|
||||
{
|
||||
if (!gtk_widget_is_sensitive (toggle[i]))
|
||||
continue;
|
||||
@ -69,8 +90,24 @@ update (void)
|
||||
|
||||
gtk_label_set_text (GTK_LABEL (settings), font_settings);
|
||||
|
||||
|
||||
if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (script_lang), &iter))
|
||||
{
|
||||
model = gtk_combo_box_get_model (GTK_COMBO_BOX (script_lang));
|
||||
gtk_tree_model_get (model, &iter,
|
||||
3, &lang_tag,
|
||||
-1);
|
||||
|
||||
lang = hb_language_to_string (hb_ot_tag_to_language (lang_tag));
|
||||
}
|
||||
else
|
||||
lang = NULL;
|
||||
|
||||
s = g_string_new ("");
|
||||
g_string_append_printf (s, "<span font_desc='%s' font_features='%s'>%s</span>", font_desc, font_settings, text);
|
||||
g_string_append_printf (s, "<span font_desc='%s' font_features='%s'", font_desc, font_settings);
|
||||
if (lang)
|
||||
g_string_append_printf (s, " lang='%s'", lang);
|
||||
g_string_append_printf (s, ">%s</span>", text);
|
||||
|
||||
gtk_label_set_markup (GTK_LABEL (label), s->str);
|
||||
|
||||
@ -80,15 +117,315 @@ update (void)
|
||||
g_free (font_settings);
|
||||
}
|
||||
|
||||
static PangoFont *
|
||||
get_pango_font (void)
|
||||
{
|
||||
PangoFontDescription *desc;
|
||||
PangoContext *context;
|
||||
PangoFontMap *map;
|
||||
|
||||
desc = gtk_font_chooser_get_font_desc (GTK_FONT_CHOOSER (font));
|
||||
context = gtk_widget_get_pango_context (font);
|
||||
map = pango_context_get_font_map (context);
|
||||
|
||||
return pango_font_map_load_font (map, context, desc);
|
||||
}
|
||||
|
||||
static struct { const char *name; hb_script_t script; } script_names[] = {
|
||||
{ "Common", HB_SCRIPT_COMMON },
|
||||
{ "Inherited", HB_SCRIPT_INHERITED },
|
||||
{ "Unknown", HB_SCRIPT_UNKNOWN },
|
||||
{ "Arabic", HB_SCRIPT_ARABIC },
|
||||
{ "Armenian", HB_SCRIPT_ARMENIAN },
|
||||
{ "Bengali", HB_SCRIPT_BENGALI },
|
||||
{ "Cyrillic", HB_SCRIPT_CYRILLIC },
|
||||
{ "Devanagari", HB_SCRIPT_DEVANAGARI },
|
||||
{ "Georgian", HB_SCRIPT_GEORGIAN },
|
||||
{ "Greek", HB_SCRIPT_GREEK },
|
||||
{ "Gujarati", HB_SCRIPT_GUJARATI },
|
||||
{ "Gurmukhi", HB_SCRIPT_GURMUKHI },
|
||||
{ "Hangul", HB_SCRIPT_HANGUL },
|
||||
{ "Han", HB_SCRIPT_HAN },
|
||||
{ "Hebrew", HB_SCRIPT_HEBREW },
|
||||
{ "Hiragana", HB_SCRIPT_HIRAGANA },
|
||||
{ "Kannada", HB_SCRIPT_KANNADA },
|
||||
{ "Katakana", HB_SCRIPT_KATAKANA },
|
||||
{ "Lao", HB_SCRIPT_LAO },
|
||||
{ "Latin", HB_SCRIPT_LATIN },
|
||||
{ "Malayalam", HB_SCRIPT_MALAYALAM },
|
||||
{ "Oriya", HB_SCRIPT_ORIYA },
|
||||
{ "Tamil", HB_SCRIPT_TAMIL },
|
||||
{ "Telugu", HB_SCRIPT_TELUGU },
|
||||
{ "Thai", HB_SCRIPT_THAI },
|
||||
{ "Tibetan", HB_SCRIPT_TIBETAN },
|
||||
{ "Bopomofo", HB_SCRIPT_BOPOMOFO }
|
||||
/* FIXME: complete */
|
||||
};
|
||||
|
||||
static struct { const char *name; hb_tag_t tag; } language_names[] = {
|
||||
{ "Arabic", HB_TAG ('A','R','A',' ') },
|
||||
{ "Romanian", HB_TAG ('R','O','M',' ') },
|
||||
{ "Skolt Sami", HB_TAG ('S','K','S',' ') },
|
||||
{ "Northern Sami", HB_TAG ('N','S','M',' ') },
|
||||
{ "Kildin Sami", HB_TAG ('K','S','M',' ') },
|
||||
{ "Moldavian", HB_TAG ('M','O','L',' ') },
|
||||
{ "Turkish", HB_TAG ('T','R','K',' ') },
|
||||
{ "Azerbaijani", HB_TAG ('A','Z','E',' ') },
|
||||
{ "Crimean Tatar", HB_TAG ('C','R','T',' ') },
|
||||
{ "Serbian", HB_TAG ('S','R','B',' ') },
|
||||
{ "German", HB_TAG ('D','E','U',' ') }
|
||||
/* FIXME: complete */
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
hb_tag_t script_tag;
|
||||
hb_tag_t lang_tag;
|
||||
unsigned int script_index;
|
||||
unsigned int lang_index;
|
||||
} TagPair;
|
||||
|
||||
static guint
|
||||
tag_pair_hash (gconstpointer data)
|
||||
{
|
||||
const TagPair *pair = data;
|
||||
|
||||
return pair->script_tag + pair->lang_tag;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
tag_pair_equal (gconstpointer a, gconstpointer b)
|
||||
{
|
||||
const TagPair *pair_a = a;
|
||||
const TagPair *pair_b = b;
|
||||
|
||||
return pair_a->script_tag == pair_b->script_tag && pair_a->lang_tag == pair_b->lang_tag;
|
||||
}
|
||||
|
||||
static void
|
||||
reset (void)
|
||||
update_script_combo (void)
|
||||
{
|
||||
GtkListStore *store;
|
||||
hb_font_t *hb_font;
|
||||
gint i, j, k, l;
|
||||
FT_Face ft_face;
|
||||
PangoFont *pango_font;
|
||||
GHashTable *tags;
|
||||
GHashTableIter iter;
|
||||
TagPair *pair;
|
||||
|
||||
store = gtk_list_store_new (4, G_TYPE_STRING, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT);
|
||||
|
||||
pango_font = get_pango_font ();
|
||||
ft_face = pango_fc_font_lock_face (PANGO_FC_FONT (pango_font)),
|
||||
hb_font = hb_ft_font_create (ft_face, NULL);
|
||||
|
||||
tags = g_hash_table_new_full (tag_pair_hash, tag_pair_equal, g_free, NULL);
|
||||
|
||||
pair = g_new (TagPair, 1);
|
||||
pair->script_tag = HB_OT_TAG_DEFAULT_SCRIPT;
|
||||
pair->lang_tag = HB_OT_TAG_DEFAULT_LANGUAGE;
|
||||
g_hash_table_insert (tags, pair, g_strdup ("Default"));
|
||||
|
||||
if (hb_font)
|
||||
{
|
||||
hb_tag_t tables[2] = { HB_OT_TAG_GSUB, HB_OT_TAG_GPOS };
|
||||
hb_face_t *hb_face;
|
||||
|
||||
hb_face = hb_font_get_face (hb_font);
|
||||
|
||||
for (i= 0; i < 2; i++)
|
||||
{
|
||||
hb_tag_t scripts[80];
|
||||
unsigned int script_count = G_N_ELEMENTS (scripts);
|
||||
|
||||
hb_ot_layout_table_get_script_tags (hb_face, tables[i], 0, &script_count, scripts);
|
||||
for (j = 0; j < script_count; j++)
|
||||
{
|
||||
hb_tag_t languages[80];
|
||||
unsigned int language_count = G_N_ELEMENTS (languages);
|
||||
|
||||
pair = g_new (TagPair, 1);
|
||||
pair->script_tag = scripts[j];
|
||||
pair->lang_tag = HB_OT_TAG_DEFAULT_LANGUAGE;
|
||||
pair->script_index = j;
|
||||
pair->lang_index = HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX;
|
||||
g_hash_table_add (tags, pair);
|
||||
|
||||
hb_ot_layout_script_get_language_tags (hb_face, tables[i], j, 0, &language_count, languages);
|
||||
for (k = 0; k < language_count; k++)
|
||||
{
|
||||
pair = g_new (TagPair, 1);
|
||||
pair->script_tag = scripts[j];
|
||||
pair->lang_tag = languages[k];
|
||||
pair->script_index = j;
|
||||
pair->lang_index = k;
|
||||
g_hash_table_add (tags, pair);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
hb_face_destroy (hb_face);
|
||||
}
|
||||
|
||||
pango_fc_font_unlock_face (PANGO_FC_FONT (pango_font));
|
||||
g_object_unref (pango_font);
|
||||
|
||||
g_hash_table_iter_init (&iter, tags);
|
||||
while (g_hash_table_iter_next (&iter, (gpointer *)&pair, NULL))
|
||||
{
|
||||
const char *scriptname;
|
||||
char scriptbuf[5];
|
||||
const char *langname;
|
||||
char langbuf[5];
|
||||
char *name;
|
||||
|
||||
if (pair->script_tag == HB_OT_TAG_DEFAULT_SCRIPT)
|
||||
scriptname = "Default";
|
||||
else if (pair->script_tag == HB_TAG ('m','a','t','h'))
|
||||
scriptname = "Math";
|
||||
else
|
||||
{
|
||||
hb_script_t script;
|
||||
|
||||
hb_tag_to_string (pair->script_tag, scriptbuf);
|
||||
scriptbuf[4] = 0;
|
||||
scriptname = scriptbuf;
|
||||
|
||||
script = hb_script_from_iso15924_tag (pair->script_tag);
|
||||
for (k = 0; k < G_N_ELEMENTS (script_names); k++)
|
||||
{
|
||||
if (script == script_names[k].script)
|
||||
{
|
||||
scriptname = script_names[k].name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (pair->lang_tag == HB_OT_TAG_DEFAULT_LANGUAGE)
|
||||
langname = "Default";
|
||||
else
|
||||
{
|
||||
hb_tag_to_string (pair->lang_tag, langbuf);
|
||||
langbuf[4] = 0;
|
||||
langname = langbuf;
|
||||
|
||||
for (l = 0; l < G_N_ELEMENTS (language_names); l++)
|
||||
{
|
||||
if (pair->lang_tag == language_names[l].tag)
|
||||
{
|
||||
langname = language_names[l].name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
name = g_strdup_printf ("%s - %s", scriptname, langname);
|
||||
|
||||
gtk_list_store_insert_with_values (store, NULL, -1,
|
||||
0, name,
|
||||
1, pair->script_index,
|
||||
2, pair->lang_index,
|
||||
3, pair->lang_tag,
|
||||
-1);
|
||||
|
||||
g_free (name);
|
||||
}
|
||||
|
||||
g_hash_table_destroy (tags);
|
||||
|
||||
gtk_combo_box_set_model (GTK_COMBO_BOX (script_lang), GTK_TREE_MODEL (store));
|
||||
gtk_combo_box_set_active (GTK_COMBO_BOX (script_lang), 0);
|
||||
}
|
||||
|
||||
static void
|
||||
update_features (void)
|
||||
{
|
||||
gint i, j, k;
|
||||
GtkTreeModel *model;
|
||||
GtkTreeIter iter;
|
||||
guint script_index, lang_index;
|
||||
PangoFont *pango_font;
|
||||
FT_Face ft_face;
|
||||
hb_font_t *hb_font;
|
||||
|
||||
for (i = 0; i < num_features; i++)
|
||||
gtk_widget_set_opacity (icon[i], 0);
|
||||
|
||||
/* set feature presence checks from the font features */
|
||||
|
||||
if (!gtk_combo_box_get_active_iter (GTK_COMBO_BOX (script_lang), &iter))
|
||||
return;
|
||||
|
||||
model = gtk_combo_box_get_model (GTK_COMBO_BOX (script_lang));
|
||||
gtk_tree_model_get (model, &iter,
|
||||
1, &script_index,
|
||||
2, &lang_index,
|
||||
-1);
|
||||
|
||||
pango_font = get_pango_font ();
|
||||
ft_face = pango_fc_font_lock_face (PANGO_FC_FONT (pango_font)),
|
||||
hb_font = hb_ft_font_create (ft_face, NULL);
|
||||
|
||||
if (hb_font)
|
||||
{
|
||||
hb_tag_t tables[2] = { HB_OT_TAG_GSUB, HB_OT_TAG_GPOS };
|
||||
hb_face_t *hb_face;
|
||||
|
||||
hb_face = hb_font_get_face (hb_font);
|
||||
|
||||
for (i = 0; i < 2; i++)
|
||||
{
|
||||
hb_tag_t features[80];
|
||||
unsigned int count = G_N_ELEMENTS(features);
|
||||
|
||||
hb_ot_layout_language_get_feature_tags (hb_face,
|
||||
tables[i],
|
||||
script_index,
|
||||
lang_index,
|
||||
0,
|
||||
&count,
|
||||
features);
|
||||
|
||||
for (j = 0; j < count; j++)
|
||||
{
|
||||
for (k = 0; k < num_features; k++)
|
||||
{
|
||||
if (hb_tag_from_string (feature_names[k], -1) == features[j])
|
||||
gtk_widget_set_opacity (icon[k], 0.5);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
hb_face_destroy (hb_face);
|
||||
}
|
||||
|
||||
pango_fc_font_unlock_face (PANGO_FC_FONT (pango_font));
|
||||
g_object_unref (pango_font);
|
||||
}
|
||||
|
||||
static void
|
||||
font_changed (void)
|
||||
{
|
||||
update_script_combo ();
|
||||
}
|
||||
|
||||
static void
|
||||
script_changed (void)
|
||||
{
|
||||
update_features ();
|
||||
update_display ();
|
||||
}
|
||||
|
||||
static void
|
||||
reset_features (void)
|
||||
{
|
||||
int i;
|
||||
|
||||
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (numcasedefault), TRUE);
|
||||
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (numspacedefault), TRUE);
|
||||
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (fractiondefault), TRUE);
|
||||
for (i = 0; i < 24; i++)
|
||||
for (i = 0; i < num_features; i++)
|
||||
{
|
||||
if (!GTK_IS_RADIO_BUTTON (toggle[i]))
|
||||
{
|
||||
@ -113,7 +450,7 @@ switch_to_label (void)
|
||||
g_free (text);
|
||||
text = NULL;
|
||||
gtk_stack_set_visible_child_name (GTK_STACK (stack), "label");
|
||||
update ();
|
||||
update_display ();
|
||||
}
|
||||
|
||||
static gboolean
|
||||
@ -141,8 +478,10 @@ do_font_features (GtkWidget *do_widget)
|
||||
|
||||
builder = gtk_builder_new_from_resource ("/font_features/font-features.ui");
|
||||
|
||||
gtk_builder_add_callback_symbol (builder, "update", update);
|
||||
gtk_builder_add_callback_symbol (builder, "reset", reset);
|
||||
gtk_builder_add_callback_symbol (builder, "update_display", update_display);
|
||||
gtk_builder_add_callback_symbol (builder, "font_changed", font_changed);
|
||||
gtk_builder_add_callback_symbol (builder, "script_changed", script_changed);
|
||||
gtk_builder_add_callback_symbol (builder, "reset", reset_features);
|
||||
gtk_builder_add_callback_symbol (builder, "switch_to_entry", switch_to_entry);
|
||||
gtk_builder_add_callback_symbol (builder, "switch_to_label", switch_to_label);
|
||||
gtk_builder_add_callback_symbol (builder, "entry_key_press", G_CALLBACK (entry_key_press));
|
||||
@ -153,39 +492,24 @@ do_font_features (GtkWidget *do_widget)
|
||||
settings = GTK_WIDGET (gtk_builder_get_object (builder, "settings"));
|
||||
resetbutton = GTK_WIDGET (gtk_builder_get_object (builder, "reset"));
|
||||
font = GTK_WIDGET (gtk_builder_get_object (builder, "font"));
|
||||
script_lang = GTK_WIDGET (gtk_builder_get_object (builder, "script_lang"));
|
||||
numcasedefault = GTK_WIDGET (gtk_builder_get_object (builder, "numcasedefault"));
|
||||
numspacedefault = GTK_WIDGET (gtk_builder_get_object (builder, "numspacedefault"));
|
||||
fractiondefault = GTK_WIDGET (gtk_builder_get_object (builder, "fractiondefault"));
|
||||
stack = GTK_WIDGET (gtk_builder_get_object (builder, "stack"));
|
||||
entry = GTK_WIDGET (gtk_builder_get_object (builder, "entry"));
|
||||
|
||||
i = 0;
|
||||
toggle[i++] = GTK_WIDGET (gtk_builder_get_object (builder, "kern"));
|
||||
toggle[i++] = GTK_WIDGET (gtk_builder_get_object (builder, "liga"));
|
||||
toggle[i++] = GTK_WIDGET (gtk_builder_get_object (builder, "dlig"));
|
||||
toggle[i++] = GTK_WIDGET (gtk_builder_get_object (builder, "hlig"));
|
||||
toggle[i++] = GTK_WIDGET (gtk_builder_get_object (builder, "clig"));
|
||||
toggle[i++] = GTK_WIDGET (gtk_builder_get_object (builder, "smcp"));
|
||||
toggle[i++] = GTK_WIDGET (gtk_builder_get_object (builder, "c2sc"));
|
||||
toggle[i++] = GTK_WIDGET (gtk_builder_get_object (builder, "lnum"));
|
||||
toggle[i++] = GTK_WIDGET (gtk_builder_get_object (builder, "onum"));
|
||||
toggle[i++] = GTK_WIDGET (gtk_builder_get_object (builder, "pnum"));
|
||||
toggle[i++] = GTK_WIDGET (gtk_builder_get_object (builder, "tnum"));
|
||||
toggle[i++] = GTK_WIDGET (gtk_builder_get_object (builder, "frac"));
|
||||
toggle[i++] = GTK_WIDGET (gtk_builder_get_object (builder, "afrc"));
|
||||
toggle[i++] = GTK_WIDGET (gtk_builder_get_object (builder, "zero"));
|
||||
toggle[i++] = GTK_WIDGET (gtk_builder_get_object (builder, "nalt"));
|
||||
toggle[i++] = GTK_WIDGET (gtk_builder_get_object (builder, "swsh"));
|
||||
toggle[i++] = GTK_WIDGET (gtk_builder_get_object (builder, "calt"));
|
||||
toggle[i++] = GTK_WIDGET (gtk_builder_get_object (builder, "hist"));
|
||||
toggle[i++] = GTK_WIDGET (gtk_builder_get_object (builder, "salt"));
|
||||
toggle[i++] = GTK_WIDGET (gtk_builder_get_object (builder, "ss01"));
|
||||
toggle[i++] = GTK_WIDGET (gtk_builder_get_object (builder, "ss02"));
|
||||
toggle[i++] = GTK_WIDGET (gtk_builder_get_object (builder, "ss03"));
|
||||
toggle[i++] = GTK_WIDGET (gtk_builder_get_object (builder, "ss04"));
|
||||
toggle[i++] = GTK_WIDGET (gtk_builder_get_object (builder, "ss05"));
|
||||
for (i = 0; i < num_features; i++)
|
||||
{
|
||||
char *iname;
|
||||
|
||||
update ();
|
||||
toggle[i] = GTK_WIDGET (gtk_builder_get_object (builder, feature_names[i]));
|
||||
iname = g_strconcat (feature_names[i], "_pres", NULL);
|
||||
icon[i] = GTK_WIDGET (gtk_builder_get_object (builder, iname));
|
||||
g_free (iname);
|
||||
}
|
||||
|
||||
font_changed ();
|
||||
|
||||
g_signal_connect (window, "destroy",
|
||||
G_CALLBACK (gtk_widget_destroyed), &window);
|
||||
|
Loading…
Reference in New Issue
Block a user