Files
gtk/demos/gtk-demo/font_features.c
Chun-wei Fan 22543210ba demos/font_features.c: Support PangoWin32 Font as well
Also support PangoWin32 fonts for the font features demo, which will
enable running on Windows even if PangoFT support is not enabled or is
unused.

Check for PangoWin32 as well in configure to see whether the font
features demo will be built.  If either PangoFT2 or PangoWin32 is found
along with HarfBuzz, build the font features demo.
2019-04-17 17:09:31 +08:00

1888 lines
73 KiB
C

/* Pango/Font Explorer
*
* 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.
*
* If the selected font supports OpenType font variations, then the
* axes are also offered for customization.
*/
#include "config.h"
#include <gtk/gtk.h>
#include <hb.h>
#include <hb-ot.h>
#include <hb-ft.h>
#include <freetype/ftmm.h>
#include <freetype/ftsnames.h>
#include <freetype/ttnameid.h>
#include <glib/gi18n.h>
#include "open-type-layout.h"
#include "fontplane.h"
#ifdef G_OS_WIN32
extern GHashTable *
gtk_font_chooser_widget_get_win32_locales (void);
#else
# include "script-names.h"
# include "language-names.h"
#endif
#include "gtkpangofontutilsprivate.h"
#define MAKE_TAG(a,b,c,d) (unsigned int)(((a) << 24) | ((b) << 16) | ((c) << 8) | (d))
static GtkWidget *label;
static GtkWidget *settings;
static GtkWidget *description;
static GtkWidget *font;
static GtkWidget *script_lang;
static GtkWidget *resetbutton;
static GtkWidget *stack;
static GtkWidget *entry;
static GtkWidget *variations_heading;
static GtkWidget *variations_grid;
static GtkWidget *instance_combo;
static GtkWidget *edit_toggle;
typedef struct {
unsigned int tag;
const char *name;
GtkWidget *icon;
GtkWidget *dflt;
GtkWidget *feat;
} FeatureItem;
static GList *feature_items;
typedef struct {
unsigned int start;
unsigned int end;
PangoFontDescription *desc;
char *features;
PangoLanguage *language;
} Range;
static GList *ranges;
static void add_font_variations (GString *s);
static void
free_range (gpointer data)
{
Range *range = data;
if (range->desc)
pango_font_description_free (range->desc);
g_free (range->features);
g_free (range);
}
static int
compare_range (gconstpointer a, gconstpointer b)
{
const Range *ra = a;
const Range *rb = b;
if (ra->start < rb->start)
return -1;
else if (ra->start > rb->start)
return 1;
else if (ra->end < rb->end)
return 1;
else if (ra->end > rb->end)
return -1;
return 0;
}
static void
ensure_range (unsigned int start,
unsigned int end,
PangoFontDescription *desc,
const char *features,
PangoLanguage *language)
{
GList *l;
Range *range;
for (l = ranges; l; l = l->next)
{
Range *r = l->data;
if (r->start == start && r->end == end)
{
range = r;
goto set;
}
}
range = g_new0 (Range, 1);
range->start = start;
range->end = end;
ranges = g_list_insert_sorted (ranges, range, compare_range);
set:
if (range->desc)
pango_font_description_free (range->desc);
if (desc)
range->desc = pango_font_description_copy (desc);
g_free (range->features);
range->features = g_strdup (features);
range->language = language;
}
static const char *
get_feature_display_name (unsigned int tag)
{
int i;
for (i = 0; i < G_N_ELEMENTS (open_type_layout_features); i++)
{
if (tag == open_type_layout_features[i].tag)
return g_dpgettext2 (NULL, "OpenType layout", open_type_layout_features[i].name);
}
return NULL;
}
static void update_display (void);
static void
set_inconsistent (GtkCheckButton *button,
gboolean inconsistent)
{
gtk_check_button_set_inconsistent (GTK_CHECK_BUTTON (button), inconsistent);
gtk_widget_set_opacity (gtk_widget_get_first_child (GTK_WIDGET (button)), inconsistent ? 0.0 : 1.0);
}
static void
feat_clicked (GtkWidget *feat,
gpointer data)
{
g_signal_handlers_block_by_func (feat, feat_clicked, NULL);
if (gtk_check_button_get_inconsistent (GTK_CHECK_BUTTON (feat)))
{
set_inconsistent (GTK_CHECK_BUTTON (feat), FALSE);
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (feat), TRUE);
}
else if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (feat)))
{
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (feat), FALSE);
}
else
{
set_inconsistent (GTK_CHECK_BUTTON (feat), TRUE);
}
g_signal_handlers_unblock_by_func (feat, feat_clicked, NULL);
}
static void
add_check_group (GtkWidget *box,
const char *title,
const char **tags)
{
GtkWidget *label;
GtkWidget *group;
PangoAttrList *attrs;
int i;
group = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
gtk_widget_set_halign (group, GTK_ALIGN_START);
label = gtk_label_new (title);
gtk_label_set_xalign (GTK_LABEL (label), 0.0);
gtk_widget_set_halign (label, GTK_ALIGN_START);
g_object_set (label, "margin-top", 10, "margin-bottom", 10, NULL);
attrs = pango_attr_list_new ();
pango_attr_list_insert (attrs, pango_attr_weight_new (PANGO_WEIGHT_BOLD));
gtk_label_set_attributes (GTK_LABEL (label), attrs);
pango_attr_list_unref (attrs);
gtk_container_add (GTK_CONTAINER (group), label);
for (i = 0; tags[i]; i++)
{
unsigned int tag;
GtkWidget *feat;
FeatureItem *item;
tag = hb_tag_from_string (tags[i], -1);
feat = gtk_check_button_new_with_label (get_feature_display_name (tag));
set_inconsistent (GTK_CHECK_BUTTON (feat), TRUE);
g_signal_connect (feat, "notify::active", G_CALLBACK (update_display), NULL);
g_signal_connect (feat, "notify::inconsistent", G_CALLBACK (update_display), NULL);
g_signal_connect (feat, "clicked", G_CALLBACK (feat_clicked), NULL);
gtk_container_add (GTK_CONTAINER (group), feat);
item = g_new (FeatureItem, 1);
item->name = tags[i];
item->tag = tag;
item->icon = NULL;
item->dflt = NULL;
item->feat = feat;
feature_items = g_list_prepend (feature_items, item);
}
gtk_container_add (GTK_CONTAINER (box), group);
}
static void
add_radio_group (GtkWidget *box,
const char *title,
const char **tags)
{
GtkWidget *label;
GtkWidget *group;
int i;
GtkWidget *group_button = NULL;
PangoAttrList *attrs;
group = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
gtk_widget_set_halign (group, GTK_ALIGN_START);
label = gtk_label_new (title);
gtk_label_set_xalign (GTK_LABEL (label), 0.0);
gtk_widget_set_halign (label, GTK_ALIGN_START);
g_object_set (label, "margin-top", 10, "margin-bottom", 10, NULL);
attrs = pango_attr_list_new ();
pango_attr_list_insert (attrs, pango_attr_weight_new (PANGO_WEIGHT_BOLD));
gtk_label_set_attributes (GTK_LABEL (label), attrs);
pango_attr_list_unref (attrs);
gtk_container_add (GTK_CONTAINER (group), label);
for (i = 0; tags[i]; i++)
{
unsigned int tag;
GtkWidget *feat;
FeatureItem *item;
const char *name;
tag = hb_tag_from_string (tags[i], -1);
name = get_feature_display_name (tag);
feat = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON (group_button),
name ? name : _("Default"));
if (group_button == NULL)
group_button = feat;
g_signal_connect (feat, "notify::active", G_CALLBACK (update_display), NULL);
g_object_set_data (G_OBJECT (feat), "default", group_button);
gtk_container_add (GTK_CONTAINER (group), feat);
item = g_new (FeatureItem, 1);
item->name = tags[i];
item->tag = tag;
item->icon = NULL;
item->dflt = NULL;
item->feat = feat;
feature_items = g_list_prepend (feature_items, item);
}
gtk_container_add (GTK_CONTAINER (box), group);
}
static void
update_display (void)
{
GString *s;
const char *text;
gboolean has_feature;
GtkTreeIter iter;
GtkTreeModel *model;
PangoFontDescription *desc;
GList *l;
PangoAttrList *attrs;
PangoAttribute *attr;
gint ins, bound;
guint start, end;
PangoLanguage *lang;
char *font_desc;
char *features;
text = gtk_editable_get_text (GTK_EDITABLE (entry));
if (gtk_label_get_selection_bounds (GTK_LABEL (label), &ins, &bound))
{
start = g_utf8_offset_to_pointer (text, ins) - text;
end = g_utf8_offset_to_pointer (text, bound) - text;
}
else
{
start = PANGO_ATTR_INDEX_FROM_TEXT_BEGINNING;
end = PANGO_ATTR_INDEX_TO_TEXT_END;
}
desc = gtk_font_chooser_get_font_desc (GTK_FONT_CHOOSER (font));
s = g_string_new ("");
add_font_variations (s);
if (s->len > 0)
{
pango_font_description_set_variations (desc, s->str);
g_string_free (s, TRUE);
}
font_desc = pango_font_description_to_string (desc);
s = g_string_new ("");
has_feature = FALSE;
for (l = feature_items; l; l = l->next)
{
FeatureItem *item = l->data;
if (!gtk_widget_is_sensitive (item->feat))
continue;
if (GTK_IS_RADIO_BUTTON (item->feat))
{
if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (item->feat)) &&
strcmp (item->name, "xxxx") != 0)
{
if (has_feature)
g_string_append (s, ", ");
g_string_append (s, item->name);
g_string_append (s, " 1");
has_feature = TRUE;
}
}
else if (GTK_IS_CHECK_BUTTON (item->feat))
{
if (gtk_check_button_get_inconsistent (GTK_CHECK_BUTTON (item->feat)))
continue;
if (has_feature)
g_string_append (s, ", ");
g_string_append (s, item->name);
if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (item->feat)))
g_string_append (s, " 1");
else
g_string_append (s, " 0");
has_feature = TRUE;
}
}
features = g_string_free (s, FALSE);
if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (script_lang), &iter))
{
hb_tag_t lang_tag;
model = gtk_combo_box_get_model (GTK_COMBO_BOX (script_lang));
gtk_tree_model_get (model, &iter,
3, &lang_tag,
-1);
lang = pango_language_from_string (hb_language_to_string (hb_ot_tag_to_language (lang_tag)));
}
else
lang = NULL;
ensure_range (start, end, desc, features, lang);
attrs = pango_attr_list_new ();
for (l = ranges; l; l = l->next)
{
Range *range = l->data;
attr = pango_attr_font_desc_new (range->desc);
attr->start_index = range->start;
attr->end_index = range->end;
pango_attr_list_insert (attrs, attr);
attr = pango_attr_font_features_new (range->features);
attr->start_index = range->start;
attr->end_index = range->end;
pango_attr_list_insert (attrs, attr);
if (range->language)
{
attr = pango_attr_language_new (range->language);
attr->start_index = range->start;
attr->end_index = range->end;
pango_attr_list_insert (attrs, attr);
}
}
gtk_label_set_text (GTK_LABEL (description), font_desc);
gtk_label_set_text (GTK_LABEL (settings), features);
gtk_label_set_text (GTK_LABEL (label), text);
gtk_label_set_attributes (GTK_LABEL (label), attrs);
g_free (font_desc);
pango_font_description_free (desc);
g_free (features);
pango_attr_list_unref (attrs);
}
typedef struct
{
PangoContext *context;
PangoFontMap *map;
PangoFont *font;
gpointer *ft_extra_items;
} demo_pango_items;
static demo_pango_items items;
static void
get_pango_font (void)
{
PangoFontDescription *desc;
PangoContext *context;
desc = gtk_font_chooser_get_font_desc (GTK_FONT_CHOOSER (font));
context = gtk_widget_get_pango_context (font);
items.map = pango_context_get_font_map (context);
items.font = pango_font_map_load_font (items.map, context, desc);
items.ft_extra_items = _gtk_pango_font_init_extra_ft_items (items.font);
return pango_context_load_font (context, desc);
}
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 int
script_sort_func (GtkTreeModel *model,
GtkTreeIter *a,
GtkTreeIter *b,
gpointer user_data)
{
char *sa, *sb;
int ret;
gtk_tree_model_get (model, a, 0, &sa, -1);
gtk_tree_model_get (model, b, 0, &sb, -1);
ret = strcmp (sa, sb);
g_free (sa);
g_free (sb);
return ret;
}
static void
update_script_combo (void)
{
GtkListStore *store;
hb_font_t *hb_font;
gint i, j, k;
FT_Face ft_face;
GHashTable *tags;
GHashTableIter iter;
TagPair *pair;
char *lang;
hb_tag_t active;
GtkTreeIter active_iter;
gboolean have_active = FALSE;
lang = gtk_font_chooser_get_language (GTK_FONT_CHOOSER (font));
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
active = hb_ot_tag_from_language (hb_language_from_string (lang, -1));
G_GNUC_END_IGNORE_DEPRECATIONS
g_free (lang);
store = gtk_list_store_new (4, G_TYPE_STRING, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT);
get_pango_font ();
ft_face = _gtk_pango_font_get_ft_face (items.font,
items.map,
items.ft_extra_items);
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_add (tags, pair);
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);
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);
}
_gtk_pango_font_release_ft_face (items.font, items.ft_extra_items);
if (_gtk_pango_font_release_ft_items (items.ft_extra_items))
g_free (items.ft_extra_items);
g_object_unref (items.font);
g_hash_table_iter_init (&iter, tags);
while (g_hash_table_iter_next (&iter, (gpointer *)&pair, NULL))
{
const char *langname;
char langbuf[5];
GtkTreeIter iter;
if (pair->lang_tag == HB_OT_TAG_DEFAULT_LANGUAGE)
langname = NC_("Language", "Default");
else
{
#ifdef G_OS_WIN32
GHashTable *ht_win32_languages = NULL;
hb_language_t lang;
ht_win32_languages = _gtk_font_chooser_widget_get_win32_locales ();
lang = hb_ot_tag_to_language (pair->lang_tag);
langname = g_hash_table_lookup (ht_win32_languages, lang);
#else
langname = get_language_name_for_tag (pair->lang_tag);
#endif
if (!langname)
{
hb_tag_to_string (pair->lang_tag, langbuf);
langbuf[4] = 0;
langname = langbuf;
}
}
gtk_list_store_insert_with_values (store, &iter, -1,
0, langname,
1, pair->script_index,
2, pair->lang_index,
3, pair->lang_tag,
-1);
if (pair->lang_tag == active)
{
have_active = TRUE;
active_iter = iter;
}
}
g_hash_table_destroy (tags);
gtk_tree_sortable_set_default_sort_func (GTK_TREE_SORTABLE (store),
script_sort_func, NULL, NULL);
gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (store),
GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID,
GTK_SORT_ASCENDING);
gtk_combo_box_set_model (GTK_COMBO_BOX (script_lang), GTK_TREE_MODEL (store));
if (have_active)
gtk_combo_box_set_active_iter (GTK_COMBO_BOX (script_lang), &active_iter);
else
gtk_combo_box_set_active_iter (GTK_COMBO_BOX (script_lang), 0);
}
static void
update_features (void)
{
gint i, j;
GtkTreeModel *model;
GtkTreeIter iter;
guint script_index, lang_index;
FT_Face ft_face;
hb_font_t *hb_font;
GList *l;
for (l = feature_items; l; l = l->next)
{
FeatureItem *item = l->data;
gtk_widget_hide (item->feat);
gtk_widget_hide (gtk_widget_get_parent (item->feat));
if (strcmp (item->name, "xxxx") == 0)
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (item->feat), TRUE);
}
/* 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);
get_pango_font ();
ft_face = _gtk_pango_font_get_ft_face (items.font, items.map, items.ft_extra_items);
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;
char *feat;
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++)
{
#if 0
char buf[5];
hb_tag_to_string (features[j], buf);
buf[4] = 0;
g_print ("%s present in %s\n", buf, i == 0 ? "GSUB" : "GPOS");
#endif
for (l = feature_items; l; l = l->next)
{
FeatureItem *item = l->data;
if (item->tag == features[j])
{
gtk_widget_show (item->feat);
gtk_widget_show (gtk_widget_get_parent (item->feat));
if (GTK_IS_RADIO_BUTTON (item->feat))
{
GtkWidget *def = GTK_WIDGET (g_object_get_data (G_OBJECT (item->feat), "default"));
gtk_widget_show (def);
}
else if (GTK_IS_CHECK_BUTTON (item->feat))
{
set_inconsistent (GTK_CHECK_BUTTON (item->feat), TRUE);
}
}
}
}
}
feat = gtk_font_chooser_get_font_features (GTK_FONT_CHOOSER (font));
if (feat)
{
for (l = feature_items; l; l = l->next)
{
FeatureItem *item = l->data;
char buf[5];
char *p;
hb_tag_to_string (item->tag, buf);
buf[4] = 0;
p = strstr (feat, buf);
if (p)
{
if (GTK_IS_RADIO_BUTTON (item->feat))
{
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (item->feat), p[6] == '1');
}
else if (GTK_IS_CHECK_BUTTON (item->feat))
{
set_inconsistent (GTK_CHECK_BUTTON (item->feat), FALSE);
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (item->feat), p[6] == '1');
}
}
}
g_free (feat);
}
hb_face_destroy (hb_face);
}
_gtk_pango_font_release_ft_face (items.font, items.ft_extra_items);
if (_gtk_pango_font_release_ft_items (items.ft_extra_items))
g_free (items.ft_extra_items);
g_object_unref (items.font);
}
#define FixedToFloat(f) (((float)(f))/65536.0)
static void
adjustment_changed (GtkAdjustment *adjustment,
GtkEntry *entry)
{
char *str;
str = g_strdup_printf ("%g", gtk_adjustment_get_value (adjustment));
gtk_editable_set_text (GTK_EDITABLE (entry), str);
g_free (str);
update_display ();
}
static void
entry_activated (GtkEntry *entry,
GtkAdjustment *adjustment)
{
gdouble value;
gchar *err = NULL;
value = g_strtod (gtk_editable_get_text (GTK_EDITABLE (entry)), &err);
if (err != NULL)
gtk_adjustment_set_value (adjustment, value);
}
static void unset_instance (GtkAdjustment *adjustment);
typedef struct {
guint32 tag;
GtkAdjustment *adjustment;
} Axis;
static GHashTable *axes;
static void
add_font_variations (GString *s)
{
GHashTableIter iter;
Axis *axis;
char buf[G_ASCII_DTOSTR_BUF_SIZE];
char *sep = "";
g_hash_table_iter_init (&iter, axes);
while (g_hash_table_iter_next (&iter, (gpointer *)NULL, (gpointer *)&axis))
{
char tag[5];
double value;
hb_tag_to_string (axis->tag, tag);
tag[4] = '\0';
value = gtk_adjustment_get_value (axis->adjustment);
g_string_append_printf (s, "%s%s=%s", sep, tag, g_ascii_dtostr (buf, sizeof (buf), value));
sep = ",";
}
}
static guint
axes_hash (gconstpointer v)
{
const Axis *p = v;
return p->tag;
}
static gboolean
axes_equal (gconstpointer v1, gconstpointer v2)
{
const Axis *p1 = v1;
const Axis *p2 = v2;
return p1->tag == p2->tag;
}
static void
add_axis (FT_Var_Axis *ax, FT_Fixed value, int i)
{
GtkWidget *axis_label;
GtkWidget *axis_entry;
GtkWidget *axis_scale;
GtkAdjustment *adjustment;
Axis *axis;
axis_label = gtk_label_new (ax->name);
gtk_widget_set_halign (axis_label, GTK_ALIGN_START);
gtk_widget_set_valign (axis_label, GTK_ALIGN_BASELINE);
gtk_grid_attach (GTK_GRID (variations_grid), axis_label, 0, i, 1, 1);
adjustment = gtk_adjustment_new ((double)FixedToFloat(value),
(double)FixedToFloat(ax->minimum),
(double)FixedToFloat(ax->maximum),
1.0, 10.0, 0.0);
axis_scale = gtk_scale_new (GTK_ORIENTATION_HORIZONTAL, adjustment);
gtk_scale_add_mark (GTK_SCALE (axis_scale), (double)FixedToFloat(ax->def), GTK_POS_TOP, NULL);
gtk_widget_set_valign (axis_scale, GTK_ALIGN_BASELINE);
gtk_widget_set_hexpand (axis_scale, TRUE);
gtk_widget_set_size_request (axis_scale, 100, -1);
gtk_scale_set_draw_value (GTK_SCALE (axis_scale), FALSE);
gtk_grid_attach (GTK_GRID (variations_grid), axis_scale, 1, i, 1, 1);
axis_entry = gtk_entry_new ();
gtk_widget_set_valign (axis_entry, GTK_ALIGN_BASELINE);
gtk_editable_set_width_chars (GTK_EDITABLE (axis_entry), 4);
gtk_grid_attach (GTK_GRID (variations_grid), axis_entry, 2, i, 1, 1);
axis = g_new (Axis, 1);
axis->tag = ax->tag;
axis->adjustment = adjustment;
g_hash_table_add (axes, axis);
adjustment_changed (adjustment, GTK_ENTRY (axis_entry));
g_signal_connect (adjustment, "value-changed", G_CALLBACK (adjustment_changed), axis_entry);
g_signal_connect (adjustment, "value-changed", G_CALLBACK (unset_instance), NULL);
g_signal_connect (axis_entry, "activate", G_CALLBACK (entry_activated), adjustment);
}
typedef struct {
char *name;
int n_axes;
guint32 *axes;
float *coords;
} Instance;
static guint
instance_hash (gconstpointer v)
{
const Instance *p = v;
return g_str_hash (p->name);
}
static gboolean
instance_equal (gconstpointer v1, gconstpointer v2)
{
const Instance *p1 = v1;
const Instance *p2 = v2;
return g_str_equal (p1->name, p2->name);
}
static void
free_instance (gpointer data)
{
Instance *instance = data;
g_free (instance->name);
g_free (instance->axes);
g_free (instance->coords);
g_free (instance);
}
static GHashTable *instances;
typedef struct {
const FT_UShort platform_id;
const FT_UShort encoding_id;
const char fromcode[12];
} FtEncoding;
#define TT_ENCODING_DONT_CARE 0xffff
static const FtEncoding ftEncoding[] = {
{ TT_PLATFORM_APPLE_UNICODE, TT_ENCODING_DONT_CARE, "UTF-16BE" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_ID_ROMAN, "MACINTOSH" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_ID_JAPANESE, "SJIS" },
{ TT_PLATFORM_MICROSOFT, TT_MS_ID_SYMBOL_CS, "UTF-16BE" },
{ TT_PLATFORM_MICROSOFT, TT_MS_ID_UNICODE_CS, "UTF-16BE" },
{ TT_PLATFORM_MICROSOFT, TT_MS_ID_SJIS, "SJIS-WIN" },
{ TT_PLATFORM_MICROSOFT, TT_MS_ID_GB2312, "GB2312" },
{ TT_PLATFORM_MICROSOFT, TT_MS_ID_BIG_5, "BIG-5" },
{ TT_PLATFORM_MICROSOFT, TT_MS_ID_WANSUNG, "Wansung" },
{ TT_PLATFORM_MICROSOFT, TT_MS_ID_JOHAB, "Johab" },
{ TT_PLATFORM_MICROSOFT, TT_MS_ID_UCS_4, "UTF-16BE" },
{ TT_PLATFORM_ISO, TT_ISO_ID_7BIT_ASCII, "ASCII" },
{ TT_PLATFORM_ISO, TT_ISO_ID_10646, "UTF-16BE" },
{ TT_PLATFORM_ISO, TT_ISO_ID_8859_1, "ISO-8859-1" },
};
typedef struct {
const FT_UShort platform_id;
const FT_UShort language_id;
const char lang[8];
} FtLanguage;
#define TT_LANGUAGE_DONT_CARE 0xffff
static const FtLanguage ftLanguage[] = {
{ TT_PLATFORM_APPLE_UNICODE, TT_LANGUAGE_DONT_CARE, "" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_ENGLISH, "en" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_FRENCH, "fr" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_GERMAN, "de" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_ITALIAN, "it" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_DUTCH, "nl" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_SWEDISH, "sv" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_SPANISH, "es" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_DANISH, "da" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_PORTUGUESE, "pt" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_NORWEGIAN, "no" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_HEBREW, "he" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_JAPANESE, "ja" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_ARABIC, "ar" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_FINNISH, "fi" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_GREEK, "el" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_ICELANDIC, "is" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_MALTESE, "mt" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_TURKISH, "tr" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_CROATIAN, "hr" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_CHINESE_TRADITIONAL, "zh-tw" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_URDU, "ur" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_HINDI, "hi" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_THAI, "th" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_KOREAN, "ko" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_LITHUANIAN, "lt" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_POLISH, "pl" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_HUNGARIAN, "hu" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_ESTONIAN, "et" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_LETTISH, "lv" },
/* { TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_SAAMISK, ??? */
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_FAEROESE, "fo" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_FARSI, "fa" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_RUSSIAN, "ru" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_CHINESE_SIMPLIFIED, "zh-cn" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_FLEMISH, "nl" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_IRISH, "ga" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_ALBANIAN, "sq" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_ROMANIAN, "ro" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_CZECH, "cs" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_SLOVAK, "sk" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_SLOVENIAN, "sl" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_YIDDISH, "yi" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_SERBIAN, "sr" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_MACEDONIAN, "mk" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_BULGARIAN, "bg" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_UKRAINIAN, "uk" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_BYELORUSSIAN, "be" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_UZBEK, "uz" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_KAZAKH, "kk" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_AZERBAIJANI, "az" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_AZERBAIJANI_CYRILLIC_SCRIPT, "az" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_AZERBAIJANI_ARABIC_SCRIPT, "ar" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_ARMENIAN, "hy" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_GEORGIAN, "ka" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_MOLDAVIAN, "mo" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_KIRGHIZ, "ky" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_TAJIKI, "tg" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_TURKMEN, "tk" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_MONGOLIAN, "mo" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_MONGOLIAN_MONGOLIAN_SCRIPT,"mo" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_MONGOLIAN_CYRILLIC_SCRIPT, "mo" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_PASHTO, "ps" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_KURDISH, "ku" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_KASHMIRI, "ks" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_SINDHI, "sd" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_TIBETAN, "bo" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_NEPALI, "ne" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_SANSKRIT, "sa" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_MARATHI, "mr" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_BENGALI, "bn" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_ASSAMESE, "as" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_GUJARATI, "gu" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_PUNJABI, "pa" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_ORIYA, "or" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_MALAYALAM, "ml" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_KANNADA, "kn" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_TAMIL, "ta" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_TELUGU, "te" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_SINHALESE, "si" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_BURMESE, "my" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_KHMER, "km" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_LAO, "lo" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_VIETNAMESE, "vi" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_INDONESIAN, "id" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_TAGALOG, "tl" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_MALAY_ROMAN_SCRIPT, "ms" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_MALAY_ARABIC_SCRIPT, "ms" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_AMHARIC, "am" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_TIGRINYA, "ti" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_GALLA, "om" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_SOMALI, "so" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_SWAHILI, "sw" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_RUANDA, "rw" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_RUNDI, "rn" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_CHEWA, "ny" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_MALAGASY, "mg" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_ESPERANTO, "eo" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_WELSH, "cy" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_BASQUE, "eu" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_CATALAN, "ca" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_LATIN, "la" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_QUECHUA, "qu" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_GUARANI, "gn" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_AYMARA, "ay" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_TATAR, "tt" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_UIGHUR, "ug" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_DZONGKHA, "dz" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_JAVANESE, "jw" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_SUNDANESE, "su" },
#if 0 /* these seem to be errors that have been dropped */
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_SCOTTISH_GAELIC },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_IRISH_GAELIC },
#endif
/* The following codes are new as of 2000-03-10 */
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_GALICIAN, "gl" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_AFRIKAANS, "af" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_BRETON, "br" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_INUKTITUT, "iu" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_SCOTTISH_GAELIC, "gd" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_MANX_GAELIC, "gv" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_IRISH_GAELIC, "ga" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_TONGAN, "to" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_GREEK_POLYTONIC, "el" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_GREELANDIC, "ik" },
{ TT_PLATFORM_MACINTOSH, TT_MAC_LANGID_AZERBAIJANI_ROMAN_SCRIPT,"az" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_ARABIC_SAUDI_ARABIA, "ar" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_ARABIC_IRAQ, "ar" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_ARABIC_EGYPT, "ar" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_ARABIC_LIBYA, "ar" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_ARABIC_ALGERIA, "ar" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_ARABIC_MOROCCO, "ar" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_ARABIC_TUNISIA, "ar" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_ARABIC_OMAN, "ar" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_ARABIC_YEMEN, "ar" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_ARABIC_SYRIA, "ar" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_ARABIC_JORDAN, "ar" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_ARABIC_LEBANON, "ar" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_ARABIC_KUWAIT, "ar" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_ARABIC_UAE, "ar" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_ARABIC_BAHRAIN, "ar" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_ARABIC_QATAR, "ar" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_BULGARIAN_BULGARIA, "bg" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_CATALAN_SPAIN, "ca" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_CHINESE_TAIWAN, "zh-tw" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_CHINESE_PRC, "zh-cn" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_CHINESE_HONG_KONG, "zh-hk" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_CHINESE_SINGAPORE, "zh-sg" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_CHINESE_MACAU, "zh-mo" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_CZECH_CZECH_REPUBLIC, "cs" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_DANISH_DENMARK, "da" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_GERMAN_GERMANY, "de" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_GERMAN_SWITZERLAND, "de" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_GERMAN_AUSTRIA, "de" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_GERMAN_LUXEMBOURG, "de" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_GERMAN_LIECHTENSTEI, "de" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_GREEK_GREECE, "el" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_ENGLISH_UNITED_STATES, "en" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_ENGLISH_UNITED_KINGDOM, "en" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_ENGLISH_AUSTRALIA, "en" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_ENGLISH_CANADA, "en" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_ENGLISH_NEW_ZEALAND, "en" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_ENGLISH_IRELAND, "en" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_ENGLISH_SOUTH_AFRICA, "en" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_ENGLISH_JAMAICA, "en" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_ENGLISH_CARIBBEAN, "en" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_ENGLISH_BELIZE, "en" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_ENGLISH_TRINIDAD, "en" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_ENGLISH_ZIMBABWE, "en" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_ENGLISH_PHILIPPINES, "en" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_SPANISH_SPAIN_TRADITIONAL_SORT,"es" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_SPANISH_MEXICO, "es" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_SPANISH_SPAIN_INTERNATIONAL_SORT,"es" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_SPANISH_GUATEMALA, "es" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_SPANISH_COSTA_RICA, "es" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_SPANISH_PANAMA, "es" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_SPANISH_DOMINICAN_REPUBLIC,"es" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_SPANISH_VENEZUELA, "es" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_SPANISH_COLOMBIA, "es" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_SPANISH_PERU, "es" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_SPANISH_ARGENTINA, "es" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_SPANISH_ECUADOR, "es" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_SPANISH_CHILE, "es" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_SPANISH_URUGUAY, "es" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_SPANISH_PARAGUAY, "es" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_SPANISH_BOLIVIA, "es" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_SPANISH_EL_SALVADOR, "es" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_SPANISH_HONDURAS, "es" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_SPANISH_NICARAGUA, "es" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_SPANISH_PUERTO_RICO, "es" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_FINNISH_FINLAND, "fi" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_FRENCH_FRANCE, "fr" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_FRENCH_BELGIUM, "fr" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_FRENCH_CANADA, "fr" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_FRENCH_SWITZERLAND, "fr" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_FRENCH_LUXEMBOURG, "fr" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_FRENCH_MONACO, "fr" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_HEBREW_ISRAEL, "he" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_HUNGARIAN_HUNGARY, "hu" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_ICELANDIC_ICELAND, "is" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_ITALIAN_ITALY, "it" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_ITALIAN_SWITZERLAND, "it" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_JAPANESE_JAPAN, "ja" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_KOREAN_EXTENDED_WANSUNG_KOREA,"ko" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_KOREAN_JOHAB_KOREA, "ko" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_DUTCH_NETHERLANDS, "nl" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_DUTCH_BELGIUM, "nl" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_NORWEGIAN_NORWAY_BOKMAL, "no" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_NORWEGIAN_NORWAY_NYNORSK, "nn" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_POLISH_POLAND, "pl" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_PORTUGUESE_BRAZIL, "pt" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_PORTUGUESE_PORTUGAL, "pt" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_RHAETO_ROMANIC_SWITZERLAND,"rm" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_ROMANIAN_ROMANIA, "ro" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_MOLDAVIAN_MOLDAVIA, "mo" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_RUSSIAN_RUSSIA, "ru" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_RUSSIAN_MOLDAVIA, "ru" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_CROATIAN_CROATIA, "hr" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_SERBIAN_SERBIA_LATIN, "sr" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_SERBIAN_SERBIA_CYRILLIC, "sr" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_SLOVAK_SLOVAKIA, "sk" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_ALBANIAN_ALBANIA, "sq" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_SWEDISH_SWEDEN, "sv" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_SWEDISH_FINLAND, "sv" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_THAI_THAILAND, "th" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_TURKISH_TURKEY, "tr" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_URDU_PAKISTAN, "ur" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_INDONESIAN_INDONESIA, "id" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_UKRAINIAN_UKRAINE, "uk" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_BELARUSIAN_BELARUS, "be" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_SLOVENE_SLOVENIA, "sl" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_ESTONIAN_ESTONIA, "et" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_LATVIAN_LATVIA, "lv" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_LITHUANIAN_LITHUANIA, "lt" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_CLASSIC_LITHUANIAN_LITHUANIA,"lt" },
#ifdef TT_MS_LANGID_MAORI_NEW_ZELAND
/* this seems to be an error that have been dropped */
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_MAORI_NEW_ZEALAND, "mi" },
#endif
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_FARSI_IRAN, "fa" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_VIETNAMESE_VIET_NAM, "vi" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_ARMENIAN_ARMENIA, "hy" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_AZERI_AZERBAIJAN_LATIN, "az" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_AZERI_AZERBAIJAN_CYRILLIC, "az" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_BASQUE_SPAIN, "eu" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_SORBIAN_GERMANY, "wen" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_MACEDONIAN_MACEDONIA, "mk" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_SUTU_SOUTH_AFRICA, "st" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_TSONGA_SOUTH_AFRICA, "ts" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_TSWANA_SOUTH_AFRICA, "tn" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_VENDA_SOUTH_AFRICA, "ven" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_XHOSA_SOUTH_AFRICA, "xh" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_ZULU_SOUTH_AFRICA, "zu" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_AFRIKAANS_SOUTH_AFRICA, "af" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_GEORGIAN_GEORGIA, "ka" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_FAEROESE_FAEROE_ISLANDS, "fo" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_HINDI_INDIA, "hi" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_MALTESE_MALTA, "mt" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_SAAMI_LAPONIA, "se" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_SCOTTISH_GAELIC_UNITED_KINGDOM,"gd" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_IRISH_GAELIC_IRELAND, "ga" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_MALAY_MALAYSIA, "ms" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_MALAY_BRUNEI_DARUSSALAM, "ms" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_KAZAK_KAZAKSTAN, "kk" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_SWAHILI_KENYA, "sw" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_UZBEK_UZBEKISTAN_LATIN, "uz" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_UZBEK_UZBEKISTAN_CYRILLIC, "uz" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_TATAR_TATARSTAN, "tt" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_BENGALI_INDIA, "bn" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_PUNJABI_INDIA, "pa" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_GUJARATI_INDIA, "gu" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_ORIYA_INDIA, "or" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_TAMIL_INDIA, "ta" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_TELUGU_INDIA, "te" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_KANNADA_INDIA, "kn" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_MALAYALAM_INDIA, "ml" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_ASSAMESE_INDIA, "as" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_MARATHI_INDIA, "mr" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_SANSKRIT_INDIA, "sa" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_KONKANI_INDIA, "kok" },
/* new as of 2001-01-01 */
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_ARABIC_GENERAL, "ar" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_CHINESE_GENERAL, "zh" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_ENGLISH_GENERAL, "en" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_FRENCH_WEST_INDIES, "fr" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_FRENCH_REUNION, "fr" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_FRENCH_CONGO, "fr" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_FRENCH_SENEGAL, "fr" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_FRENCH_CAMEROON, "fr" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_FRENCH_COTE_D_IVOIRE, "fr" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_FRENCH_MALI, "fr" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_BOSNIAN_BOSNIA_HERZEGOVINA,"bs" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_URDU_INDIA, "ur" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_TAJIK_TAJIKISTAN, "tg" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_YIDDISH_GERMANY, "yi" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_KIRGHIZ_KIRGHIZSTAN, "ky" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_TURKMEN_TURKMENISTAN, "tk" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_MONGOLIAN_MONGOLIA, "mn" },
/* the following seems to be inconsistent;
here is the current "official" way: */
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_TIBETAN_BHUTAN, "bo" },
/* and here is what is used by Passport SDK */
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_TIBETAN_CHINA, "bo" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_DZONGHKA_BHUTAN, "dz" },
/* end of inconsistency */
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_WELSH_WALES, "cy" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_KHMER_CAMBODIA, "km" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_LAO_LAOS, "lo" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_BURMESE_MYANMAR, "my" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_GALICIAN_SPAIN, "gl" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_MANIPURI_INDIA, "mni" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_SINDHI_INDIA, "sd" },
/* the following one is only encountered in Microsoft RTF specification */
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_KASHMIRI_PAKISTAN, "ks" },
/* the following one is not in the Passport list, looks like an omission */
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_KASHMIRI_INDIA, "ks" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_NEPALI_NEPAL, "ne" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_NEPALI_INDIA, "ne" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_FRISIAN_NETHERLANDS, "fy" },
/* new as of 2001-03-01 (from Office Xp) */
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_ENGLISH_HONG_KONG, "en" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_ENGLISH_INDIA, "en" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_ENGLISH_MALAYSIA, "en" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_ENGLISH_SINGAPORE, "en" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_SYRIAC_SYRIA, "syr" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_SINHALESE_SRI_LANKA, "si" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_CHEROKEE_UNITED_STATES, "chr" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_INUKTITUT_CANADA, "iu" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_AMHARIC_ETHIOPIA, "am" },
#if 0
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_TAMAZIGHT_MOROCCO },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_TAMAZIGHT_MOROCCO_LATIN },
#endif
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_PASHTO_AFGHANISTAN, "ps" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_FILIPINO_PHILIPPINES, "phi" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_DHIVEHI_MALDIVES, "div" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_OROMO_ETHIOPIA, "om" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_TIGRIGNA_ETHIOPIA, "ti" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_TIGRIGNA_ERYTHREA, "ti" },
/* New additions from Windows Xp/Passport SDK 2001-11-10. */
/* don't ask what this one means... It is commented out currently. */
#if 0
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_GREEK_GREECE2 },
#endif
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_SPANISH_UNITED_STATES, "es" },
/* The following two IDs blatantly violate MS specs by using a */
/* sublanguage >,. */
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_SPANISH_LATIN_AMERICA, "es" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_FRENCH_NORTH_AFRICA, "fr" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_FRENCH_MOROCCO, "fr" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_FRENCH_HAITI, "fr" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_BENGALI_BANGLADESH, "bn" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_PUNJABI_ARABIC_PAKISTAN, "ar" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_MONGOLIAN_MONGOLIA_MONGOLIAN,"mn" },
#if 0
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_EDO_NIGERIA },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_FULFULDE_NIGERIA },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_IBIBIO_NIGERIA },
#endif
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_HAUSA_NIGERIA, "ha" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_YORUBA_NIGERIA, "yo" },
/* language codes from, to, are (still) unknown. */
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_IGBO_NIGERIA, "ibo" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_KANURI_NIGERIA, "kau" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_GUARANI_PARAGUAY, "gn" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_HAWAIIAN_UNITED_STATES, "haw" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_LATIN, "la" },
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_SOMALI_SOMALIA, "so" },
#if 0
/* Note: Yi does not have a (proper) ISO 639-2 code, since it is mostly */
/* not written (but OTOH the peculiar writing system is worth */
/* studying). */
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_YI_CHINA },
#endif
{ TT_PLATFORM_MICROSOFT, TT_MS_LANGID_PAPIAMENTU_NETHERLANDS_ANTILLES,"pap" },
};
static const char *
FcSfntNameLanguage (FT_SfntName *sname)
{
int i;
FT_UShort platform_id = sname->platform_id;
FT_UShort language_id = sname->language_id;
for (i = 0; i < G_N_ELEMENTS (ftLanguage); i++)
if (ftLanguage[i].platform_id == platform_id &&
(ftLanguage[i].language_id == TT_LANGUAGE_DONT_CARE ||
ftLanguage[i].language_id == language_id))
{
if (ftLanguage[i].lang[0] == '\0')
return NULL;
else
return ftLanguage[i].lang;
}
return NULL;
}
static char *
FcSfntNameTranscode (FT_SfntName *name)
{
int i;
const char *fromcode;
for (i = 0; i < G_N_ELEMENTS (ftEncoding); i++)
if (ftEncoding[i].platform_id == name->platform_id &&
(ftEncoding[i].encoding_id == TT_ENCODING_DONT_CARE ||
ftEncoding[i].encoding_id == name->encoding_id))
break;
if (i == G_N_ELEMENTS (ftEncoding))
return NULL;
fromcode = ftEncoding[i].fromcode;
return g_convert ((const char *)name->string, name->string_len, "UTF-8", fromcode, NULL, NULL, NULL);
}
static char *
get_sfnt_name (FT_Face ft_face,
guint nameid)
{
guint count;
guint i, j;
const char * const *langs = g_get_language_names ();
char *res = NULL;
guint pos = G_MAXUINT;
count = FT_Get_Sfnt_Name_Count (ft_face);
for (i = 0; i < count; i++)
{
FT_SfntName name;
const char *lang;
if (FT_Get_Sfnt_Name (ft_face, i, &name) != 0)
continue;
if (name.name_id != nameid)
continue;
lang = FcSfntNameLanguage (&name);
for (j = 0; j < pos && langs[j]; j++)
{
if (strcmp (lang, langs[j]) == 0)
{
pos = j;
g_free (res);
res = FcSfntNameTranscode (&name);
}
}
if (pos == 0)
break;
}
return res;
}
static gboolean
is_valid_subfamily_id (guint id)
{
return id == 2 || id == 17 || (255 < id && id < 32768);
}
static void
add_instance (FT_Face ft_face,
FT_MM_Var *ft_mm_var,
FT_Var_Named_Style *ns,
GtkWidget *combo,
int pos)
{
Instance *instance;
int i;
instance = g_new0 (Instance, 1);
if (is_valid_subfamily_id (ns->strid))
instance->name = get_sfnt_name (ft_face, ns->strid);
if (!instance->name)
instance->name = g_strdup_printf ("Instance %d", pos);
g_hash_table_add (instances, instance);
gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo), instance->name);
instance->n_axes = ft_mm_var->num_axis;
instance->axes = g_new (guint32, ft_mm_var->num_axis);
instance->coords = g_new (float, ft_mm_var->num_axis);
for (i = 0; i < ft_mm_var->num_axis; i++)
{
instance->axes[i] = ft_mm_var->axis[i].tag;
instance->coords[i] = FixedToFloat(ns->coords[i]);
}
}
static void
unset_instance (GtkAdjustment *adjustment)
{
if (instance_combo)
gtk_combo_box_set_active (GTK_COMBO_BOX (instance_combo), 0);
}
static void
instance_changed (GtkComboBox *combo)
{
char *text;
Instance *instance;
Instance ikey;
int i;
text = gtk_combo_box_text_get_active_text (GTK_COMBO_BOX_TEXT (combo));
if (text[0] == '\0')
goto out;
ikey.name = text;
instance = g_hash_table_lookup (instances, &ikey);
if (!instance)
{
g_print ("did not find instance %s\n", text);
goto out;
}
for (i = 0; i < instance->n_axes; i++)
{
Axis *axis;
Axis akey;
guint32 tag;
gdouble value;
tag = instance->axes[i];
value = instance->coords[i];
akey.tag = tag;
axis = g_hash_table_lookup (axes, &akey);
if (axis)
{
g_signal_handlers_block_by_func (axis->adjustment, unset_instance, NULL);
gtk_adjustment_set_value (axis->adjustment, value);
g_signal_handlers_unblock_by_func (axis->adjustment, unset_instance, NULL);
}
}
out:
g_free (text);
}
static gboolean
matches_instance (FT_Var_Named_Style *instance,
FT_Fixed *coords,
FT_UInt num_coords)
{
FT_UInt i;
for (i = 0; i < num_coords; i++)
if (coords[i] != instance->coords[i])
return FALSE;
return TRUE;
}
static void
add_font_plane (int i)
{
GtkWidget *plane;
Axis *weight_axis;
Axis *width_axis;
Axis key;
key.tag = MAKE_TAG('w','g','h','t');
weight_axis = g_hash_table_lookup (axes, &key);
key.tag = MAKE_TAG('w','d','t','h');
width_axis = g_hash_table_lookup (axes, &key);
if (weight_axis && width_axis)
{
plane = gtk_font_plane_new (weight_axis->adjustment,
width_axis->adjustment);
gtk_widget_set_size_request (plane, 300, 300);
gtk_widget_set_halign (plane, GTK_ALIGN_CENTER);
gtk_grid_attach (GTK_GRID (variations_grid), plane, 0, i, 3, 1);
}
}
static void
update_font_variations (void)
{
GtkWidget *child, *next;
FT_Face ft_face;
FT_MM_Var *ft_mm_var;
FT_Error ret;
child = gtk_widget_get_first_child (variations_grid);
while (child != NULL)
{
next = gtk_widget_get_next_sibling (child);
gtk_widget_destroy (child);
child = next;
}
instance_combo = NULL;
g_hash_table_remove_all (axes);
g_hash_table_remove_all (instances);
get_pango_font ();
ft_face = _gtk_pango_font_get_ft_face (items.font,
items.map,
items.ft_extra_items);
ret = FT_Get_MM_Var (ft_face, &ft_mm_var);
if (ret == 0)
{
unsigned int i;
FT_Fixed *coords;
coords = g_new (FT_Fixed, ft_mm_var->num_axis);
ret = FT_Get_Var_Design_Coordinates (ft_face, ft_mm_var->num_axis, coords);
if (ft_mm_var->num_namedstyles > 0)
{
GtkWidget *label;
GtkWidget *combo;
label = gtk_label_new ("Instance");
gtk_label_set_xalign (GTK_LABEL (label), 0);
gtk_widget_set_halign (label, GTK_ALIGN_START);
gtk_widget_set_valign (label, GTK_ALIGN_BASELINE);
gtk_grid_attach (GTK_GRID (variations_grid), label, 0, -1, 2, 1);
combo = gtk_combo_box_text_new ();
gtk_widget_set_valign (combo, GTK_ALIGN_BASELINE);
g_signal_connect (combo, "changed", G_CALLBACK (instance_changed), NULL);
gtk_grid_attach (GTK_GRID (variations_grid), combo, 1, -1, 2, 1);
gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo), "");
for (i = 0; i < ft_mm_var->num_namedstyles; i++)
add_instance (ft_face, ft_mm_var, &ft_mm_var->namedstyle[i], combo, i);
for (i = 0; i < ft_mm_var->num_namedstyles; i++)
{
if (matches_instance (&ft_mm_var->namedstyle[i], coords, ft_mm_var->num_axis))
{
gtk_combo_box_set_active (GTK_COMBO_BOX (combo), i + 1);
break;
}
}
instance_combo = combo;
}
if (ret == 0)
{
for (i = 0; i < ft_mm_var->num_axis; i++)
add_axis (&ft_mm_var->axis[i], coords[i], i);
add_font_plane (ft_mm_var->num_axis);
}
g_free (coords);
free (ft_mm_var);
}
_gtk_pango_font_release_ft_face (items.font, items.ft_extra_items);
if (_gtk_pango_font_release_ft_items (items.ft_extra_items))
g_free (items.ft_extra_items);
g_object_unref (items.font);
}
static void
font_changed (void)
{
update_script_combo ();
update_features ();
update_font_variations ();
}
static void
script_changed (void)
{
update_features ();
update_display ();
}
static void
reset_features (void)
{
GList *l;
gtk_label_select_region (GTK_LABEL (label), 0, 0);
g_list_free_full (ranges, free_range);
ranges = NULL;
for (l = feature_items; l; l = l->next)
{
FeatureItem *item = l->data;
if (GTK_IS_RADIO_BUTTON (item->feat))
{
if (strcmp (item->name, "xxxx") == 0)
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (item->feat), TRUE);
}
else if (GTK_IS_CHECK_BUTTON (item->feat))
{
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (item->feat), FALSE);
set_inconsistent (GTK_CHECK_BUTTON (item->feat), TRUE);
}
}
}
static char *text;
static void
switch_to_entry (void)
{
text = g_strdup (gtk_editable_get_text (GTK_EDITABLE (entry)));
gtk_stack_set_visible_child_name (GTK_STACK (stack), "entry");
gtk_widget_grab_focus (entry);
}
static void
switch_to_label (void)
{
g_free (text);
text = NULL;
gtk_stack_set_visible_child_name (GTK_STACK (stack), "label");
update_display ();
}
static void
toggle_edit (void)
{
if (strcmp (gtk_stack_get_visible_child_name (GTK_STACK (stack)), "label") == 0)
switch_to_entry ();
else
switch_to_label ();
}
static void
stop_edit (void)
{
gtk_button_clicked (GTK_BUTTON (edit_toggle));
}
static gboolean
entry_key_press (GtkEventController *controller,
guint keyval,
guint keycode,
GdkModifierType modifiers,
GtkEntry *entry)
{
if (keyval == GDK_KEY_Escape)
{
gtk_editable_set_text (GTK_EDITABLE (entry), text);
stop_edit ();
return GDK_EVENT_STOP;
}
return GDK_EVENT_PROPAGATE;
}
GtkWidget *
do_font_features (GtkWidget *do_widget)
{
static GtkWidget *window = NULL;
if (!window)
{
GtkBuilder *builder;
GtkWidget *feature_list;
GtkEventController *controller;
builder = gtk_builder_new_from_resource ("/font_features/font-features.ui");
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, "stop_edit", G_CALLBACK (stop_edit));
gtk_builder_add_callback_symbol (builder, "toggle_edit", G_CALLBACK (toggle_edit));
gtk_builder_connect_signals (builder, NULL);
window = GTK_WIDGET (gtk_builder_get_object (builder, "window"));
feature_list = GTK_WIDGET (gtk_builder_get_object (builder, "feature_list"));
label = GTK_WIDGET (gtk_builder_get_object (builder, "label"));
settings = GTK_WIDGET (gtk_builder_get_object (builder, "settings"));
description = GTK_WIDGET (gtk_builder_get_object (builder, "description"));
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"));
stack = GTK_WIDGET (gtk_builder_get_object (builder, "stack"));
entry = GTK_WIDGET (gtk_builder_get_object (builder, "entry"));
edit_toggle = GTK_WIDGET (gtk_builder_get_object (builder, "edit_toggle"));
controller = gtk_event_controller_key_new ();
g_object_set_data_full (G_OBJECT (entry), "controller", controller, g_object_unref);
g_signal_connect (controller, "key-pressed", G_CALLBACK (entry_key_press), entry);
gtk_widget_add_controller (entry, controller);
add_check_group (feature_list, _("Kerning"), (const char *[]){ "kern", NULL });
add_check_group (feature_list, _("Ligatures"), (const char *[]){ "liga",
"dlig",
"hlig",
"clig",
"rlig", NULL });
add_check_group (feature_list, _("Letter Case"), (const char *[]){ "smcp",
"c2sc",
"pcap",
"c2pc",
"unic",
"cpsp",
"case",NULL });
add_radio_group (feature_list, _("Number Case"), (const char *[]){ "xxxx",
"lnum",
"onum", NULL });
add_radio_group (feature_list, _("Number Spacing"), (const char *[]){ "xxxx",
"pnum",
"tnum", NULL });
add_radio_group (feature_list, _("Fractions"), (const char *[]){ "xxxx",
"frac",
"afrc", NULL });
add_check_group (feature_list, _("Numeric Extras"), (const char *[]){ "zero",
"nalt",
"sinf", NULL });
add_check_group (feature_list, _("Character Alternatives"), (const char *[]){ "swsh",
"cswh",
"locl",
"calt",
"falt",
"hist",
"salt",
"jalt",
"titl",
"rand",
"subs",
"sups",
"ordn",
"ltra",
"ltrm",
"rtla",
"rtlm",
"rclt", NULL });
add_check_group (feature_list, _("Positional Alternatives"), (const char *[]){ "init",
"medi",
"med2",
"fina",
"fin2",
"fin3",
"isol", NULL });
add_check_group (feature_list, _("Width Variants"), (const char *[]){ "fwid",
"hwid",
"halt",
"pwid",
"palt",
"twid",
"qwid", NULL });
add_check_group (feature_list, _("Alternative Stylistic Sets"), (const char *[]){ "ss00",
"ss01",
"ss02",
"ss03",
"ss04",
"ss05",
"ss06",
"ss07",
"ss08",
"ss09",
"ss10",
"ss11",
"ss12",
"ss13",
"ss14",
"ss15",
"ss16",
"ss17",
"ss18",
"ss19",
"ss20", NULL });
add_check_group (feature_list, _("Mathematical"), (const char *[]){ "dtls",
"flac",
"mgrk",
"ssty", NULL });
add_check_group (feature_list, _("Optical Bounds"), (const char *[]){ "opbd",
"lfbd",
"rtbd", NULL });
feature_items = g_list_reverse (feature_items);
variations_heading = GTK_WIDGET (gtk_builder_get_object (builder, "variations_heading"));
variations_grid = GTK_WIDGET (gtk_builder_get_object (builder, "variations_grid"));
if (instances == NULL)
instances = g_hash_table_new_full (instance_hash, instance_equal, NULL, free_instance);
else
g_hash_table_remove_all (instances);
if (axes == NULL)
axes = g_hash_table_new_full (axes_hash, axes_equal, NULL, g_free);
else
g_hash_table_remove_all (axes);
font_changed ();
g_signal_connect (window, "destroy",
G_CALLBACK (gtk_widget_destroyed), &window);
g_object_unref (builder);
}
if (!gtk_widget_get_visible (window))
gtk_window_present (GTK_WINDOW (window));
else
gtk_widget_destroy (window);
return window;
}