From ebeca18635a0d7f658b0f6eb2a0af186c78365bb Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Wed, 3 Jan 2018 14:23:33 -0500 Subject: [PATCH] gtk-demo: Update font explorer example Revise the UI (no more double checkboxes), add font variations, update font features from the dialog, allow tweaking ranges. --- demos/gtk-demo/demo.gresource.xml | 2 + demos/gtk-demo/font-features.ui | 1559 ++------------------------- demos/gtk-demo/font_features.c | 1648 ++++++++++++++++++++++++++--- demos/gtk-demo/fontplane.c | 315 ++++++ demos/gtk-demo/fontplane.h | 65 ++ demos/gtk-demo/language-names.c | 233 ++++ demos/gtk-demo/language-names.h | 13 + demos/gtk-demo/meson.build | 2 +- demos/gtk-demo/script-names.c | 184 ++++ demos/gtk-demo/script-names.h | 13 + 10 files changed, 2380 insertions(+), 1654 deletions(-) create mode 100644 demos/gtk-demo/fontplane.c create mode 100644 demos/gtk-demo/fontplane.h create mode 100644 demos/gtk-demo/language-names.c create mode 100644 demos/gtk-demo/language-names.h create mode 100644 demos/gtk-demo/script-names.c create mode 100644 demos/gtk-demo/script-names.h diff --git a/demos/gtk-demo/demo.gresource.xml b/demos/gtk-demo/demo.gresource.xml index 4539b40499..2d69cdad78 100644 --- a/demos/gtk-demo/demo.gresource.xml +++ b/demos/gtk-demo/demo.gresource.xml @@ -163,6 +163,7 @@ flowbox.c foreigndrawing.c font_features.c + fontplane.c gestures.c glarea.c headerbar.c @@ -224,6 +225,7 @@ font-features.ui + fontplane.c spinbutton.ui diff --git a/demos/gtk-demo/font-features.ui b/demos/gtk-demo/font-features.ui index aae2615558..3a88e769f7 100644 --- a/demos/gtk-demo/font-features.ui +++ b/demos/gtk-demo/font-features.ui @@ -9,18 +9,18 @@ 1 1 - Font Features + Font Explorer 1 - 1 - 1 + 1 + 1 Reset 1 - view-refresh-symbolic + view-refresh-symbolic @@ -33,8 +33,8 @@ 1 - 1 - never + 1 + never 1 @@ -47,73 +47,40 @@ 1 - 1 - 1 + 1 + 1 Sans 12 - - - 1 - 1 - - - - - 0 - - - - - 1 - 1 - - - 1 - 10 - 10 - 10 - vertical - - - 1 - - - 1 - object-select-symbolic - - - - - 1 - 1 - 1 - baseline - - - - - - Enabled - 1 - 1 - 1 - baseline - - - - - - - - 1 - Kerning + 0 + Font Features + + + + + + + + vertical + + + 1 + 10 + + + + + 0 + + + + @@ -121,1418 +88,21 @@ 1 - 1 - - - 1 - 10 - 10 - 10 - vertical - - - 1 - - - 1 - object-select-symbolic - - - - - 1 - 1 - 1 - baseline - - - - - - Common Ligatures - 1 - 1 - 1 - baseline - - - - - - - - - 1 - - - 1 - object-select-symbolic - - - - - 1 - 1 - 1 - baseline - - - - - - Discretionary Ligatures - 1 - 1 - 1 - baseline - - - - - - - - - 1 - - - 1 - object-select-symbolic - - - - - 1 - 1 - 1 - baseline - - - - - - Historical Ligatures - 1 - 1 - 1 - baseline - - - - - - - - - 1 - - - 1 - object-select-symbolic - - - - - 1 - 1 - 1 - baseline - - - - - - Contextual Ligatures - 1 - 1 - 1 - baseline - - - - - - - - - + 1 - Ligatures + Font Variations + 0 + + + - - - - - 1 - 1 - + 1 - 10 - 10 - 10 - vertical - - - 1 - - - 1 - object-select-symbolic - - - - - 1 - 1 - 1 - baseline - - - - - - Small Caps - 1 - 1 - 1 - - baseline - - - - - - - - 1 - - - 1 - object-select-symbolic - - - - - 1 - 1 - 1 - baseline - - - - - - Small Caps from Caps - 1 - 1 - 1 - baseline - - - - - - - - - 1 - - - 1 - object-select-symbolic - - - - - 1 - 1 - 1 - baseline - - - - - - Petite Caps - 1 - 1 - 1 - baseline - - - - - - - - - 1 - - - 1 - object-select-symbolic - - - - - 1 - 1 - 1 - baseline - - - - - - Caps to Petite Caps - 1 - 1 - 1 - baseline - - - - - - - - - 1 - - - 1 - object-select-symbolic - - - - - 1 - 1 - 1 - baseline - - - - - - Unicase - 1 - 1 - 1 - baseline - - - - - - - - - 1 - - - 1 - object-select-symbolic - - - - - 1 - 1 - 1 - baseline - - - - - - Capital Spacing - 1 - 1 - 1 - baseline - - - - - - - - - 1 - - - 1 - object-select-symbolic - - - - - 1 - 1 - 1 - baseline - - - - - - Case-sensitive Forms - 1 - 1 - 1 - baseline - - - - - - - - - - - 1 - Letter Case - - - - - - - 1 - 1 - - - 1 - 10 - 10 - 10 - vertical - - - 1 - - - 1 - 0 - object-select-symbolic - - - - - Default - 1 - 1 - 1 - 1 - baseline - - - - - - - - 1 - - - 1 - object-select-symbolic - - - - - Lining - 1 - 1 - 1 - 1 - baseline - numcasedefault - - - - - - - - 1 - - - 1 - object-select-symbolic - - - - - Old-Style - 1 - 1 - 1 - baseline - 1 - numcasedefault - - - - - - - - - - 1 - Number Case - - - - - - - 1 - 1 - - - 1 - 10 - 10 - 10 - vertical - - - 1 - - - 1 - 0 - object-select-symbolic - - - - - Default - 1 - 1 - 1 - 1 - baseline - - - - - - - - 1 - - - 1 - object-select-symbolic - - - - - Proportional - 1 - 1 - 1 - 1 - numspacedefault - baseline - - - - - - - - 1 - - - 1 - object-select-symbolic - - - - - Tabular - 1 - 1 - 1 - 1 - numspacedefault - baseline - - - - - - - - - - 1 - Number Spacing - - - - - - - 1 - 1 - - - 1 - 10 - 10 - 10 - vertical - - - 1 - - - 1 - 0 - object-select-symbolic - - - - - Off - 1 - 1 - 1 - 1 - baseline - - - - - - - - 1 - - - 1 - object-select-symbolic - - - - - Normal - 1 - 1 - 1 - 1 - fractiondefault - baseline - - - - - - - - 1 - - - 1 - object-select-symbolic - - - - - Alternate - 1 - 1 - 1 - 1 - fractiondefault - baseline - - - - - - - - - - 1 - Fractions - - - - - - - 1 - 1 - - - 1 - 10 - 10 - 10 - vertical - - - 1 - - - 1 - object-select-symbolic - - - - - 1 - 1 - 1 - baseline - - - - - - Slashed Zero - 1 - 1 - 1 - baseline - - - - - - - - - 1 - - - 1 - object-select-symbolic - - - - - 1 - 1 - 1 - baseline - - - - - - Alternative Annotations - 1 - 1 - 1 - baseline - - - - - - - - - 1 - - - 1 - object-select-symbolic - - - - - 1 - 1 - 1 - baseline - - - - - - Scientific Inferiors - 1 - 1 - 1 - baseline - - - - - - - - - - - 1 - Numeric Extras - - - - - - - 1 - 1 - - - 1 - 10 - 10 - 10 - vertical - - - 1 - - - 1 - object-select-symbolic - - - - - 1 - 1 - 1 - baseline - - - - - - Swash Glyphs - 1 - 1 - 1 - baseline - - - - - - - - - 1 - - - 1 - object-select-symbolic - - - - - 1 - 1 - 1 - baseline - - - - - - Contextual Swash - 1 - 1 - 1 - baseline - - - - - - - - - 1 - - - 1 - object-select-symbolic - - - - - 1 - 1 - 1 - baseline - - - - - - Localized Forms - 1 - 1 - 1 - baseline - - - - - - - - - 1 - - - 1 - object-select-symbolic - - - - - 1 - 1 - 1 - baseline - - - - - - Contextual Alternatives - 1 - 1 - 1 - baseline - - - - - - - - - 1 - - - 1 - object-select-symbolic - - - - - 1 - 1 - 1 - baseline - - - - - - Historical Alternatives - 1 - 1 - 1 - baseline - - - - - - - - - 1 - - - 1 - object-select-symbolic - - - - - 1 - 1 - 1 - baseline - - - - - - Stylistic Alternatives - 1 - 1 - 1 - baseline - - - - - - - - - 1 - - - 1 - object-select-symbolic - - - - - 1 - 1 - 1 - baseline - - - - - - Titling Alternatives - 1 - 1 - 1 - baseline - - - - - - - - - 1 - - - 1 - object-select-symbolic - - - - - 1 - 1 - 1 - baseline - - - - - - Randomize - 1 - 1 - 1 - baseline - - - - - - - - - 1 - - - 1 - object-select-symbolic - - - - - 1 - 1 - 1 - baseline - - - - - - Subscript - 1 - 1 - 1 - baseline - - - - - - - - - 1 - - - 1 - object-select-symbolic - - - - - 1 - 1 - 1 - baseline - - - - - - Superscript - 1 - 1 - 1 - baseline - - - - - - - - - - - 1 - Character Alternatives - - - - - - - 1 - 1 - - - 1 - 10 - 10 - 10 - vertical - - - 1 - - - 1 - object-select-symbolic - - - - - 1 - 1 - 1 - baseline - - - - - - Initial Forms - 1 - 1 - 1 - baseline - - - - - - - - - 1 - - - 1 - object-select-symbolic - - - - - 1 - 1 - 1 - baseline - - - - - - Medial Forms - 1 - 1 - 1 - baseline - - - - - - - - - 1 - - - 1 - object-select-symbolic - - - - - 1 - 1 - 1 - baseline - - - - - - Final Forms - 1 - 1 - 1 - baseline - - - - - - - - - 1 - - - 1 - object-select-symbolic - - - - - 1 - 1 - 1 - baseline - - - - - - Isolated Forms - 1 - 1 - 1 - baseline - - - - - - - - - - - 1 - Positional Alternatives - - - - - - - 1 - 1 - - - 1 - 10 - 10 - 10 - vertical - - - 1 - - - 1 - object-select-symbolic - - - - - 1 - 1 - 1 - baseline - - - - - - Set 1 - 1 - 1 - 1 - baseline - - - - - - - - - 1 - - - 1 - object-select-symbolic - - - - - 1 - 1 - 1 - baseline - - - - - - Set 2 - 1 - 1 - 1 - baseline - - - - - - - - - 1 - - - 1 - object-select-symbolic - - - - - 1 - 1 - 1 - baseline - - - - - - Set 3 - 1 - 1 - 1 - baseline - - - - - - - - - 1 - - - 1 - object-select-symbolic - - - - - 1 - 1 - 1 - baseline - - - - - - Set 4 - 1 - 1 - 1 - baseline - - - - - - - - - 1 - - - 1 - object-select-symbolic - - - - - 1 - 1 - 1 - baseline - - - - - - Set 5 - 1 - 1 - 1 - baseline - - - - - - - - - - - 1 - Alternative Stylistic Sets + 10 + 10 @@ -1550,13 +120,13 @@ 1 vertical + 1 + 1 + 20 + 20 1 - 20 - 20 - 20 - 20 1 @@ -1564,7 +134,7 @@ 0 0 start - + 1 label @@ -1579,7 +149,7 @@ Τάχιστη αλώπηξ βαφής ψημένη γη, δρασκελίζει υπέρ νωθρού κυνός - + start 50 @@ -1592,19 +162,46 @@ 1 - 20 - 20 - 20 + 1 0 end 50 50 1 1 + + + + + + horizontal + 10 + + + 1 + 1 + 0 + end + 50 + 50 + 1 + + + + + + document-edit-symbolic + end + end + + + - 1 - 1 diff --git a/demos/gtk-demo/font_features.c b/demos/gtk-demo/font_features.c index b75508f405..a2df188a6a 100644 --- a/demos/gtk-demo/font_features.c +++ b/demos/gtk-demo/font_features.c @@ -1,4 +1,4 @@ -/* Pango/Font Features +/* Pango/Font Explorer * * This example demonstrates support for OpenType font features with * Pango attributes. The attributes can be used manually or via Pango @@ -6,6 +6,9 @@ * * 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 @@ -13,72 +16,351 @@ #include #include #include +#include +#include +#include +#include + +#include "open-type-layout.h" +#include "fontplane.h" +#include "script-names.h" +#include "language-names.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 *numcasedefault; -static GtkWidget *numspacedefault; -static GtkWidget *fractiondefault; static GtkWidget *stack; static GtkWidget *entry; +static GtkWidget *variations_heading; +static GtkWidget *variations_grid; +static GtkWidget *instance_combo; +static GtkWidget *edit_toggle; -#define num_features 40 +typedef struct { + unsigned int tag; + const char *name; + GtkWidget *icon; + GtkWidget *dflt; + GtkWidget *feat; +} FeatureItem; -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 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; - char *font_desc; - char *font_settings; const char *text; gboolean has_feature; - int i; - hb_tag_t lang_tag; - GtkTreeModel *model; GtkTreeIter iter; - const char *lang; + 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_entry_get_text (GTK_ENTRY (entry)); - font_desc = gtk_font_chooser_get_font (GTK_FONT_CHOOSER (font)); + 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 (i = 0; i < num_features; i++) + for (l = feature_items; l; l = l->next) { - if (!gtk_widget_is_sensitive (toggle[i])) + FeatureItem *item = l->data; + + if (!gtk_widget_is_sensitive (item->feat)) continue; - if (GTK_IS_RADIO_BUTTON (toggle[i])) + if (GTK_IS_RADIO_BUTTON (item->feat)) { - if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (toggle[i]))) + 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, gtk_buildable_get_name (GTK_BUILDABLE (toggle[i]))); + g_string_append (s, item->name); g_string_append (s, " 1"); has_feature = TRUE; } } - else + 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, gtk_buildable_get_name (GTK_BUILDABLE (toggle[i]))); - if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (toggle[i]))) + 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"); @@ -86,35 +368,58 @@ update_display (void) } } - font_settings = g_string_free (s, FALSE); - - gtk_label_set_text (GTK_LABEL (settings), font_settings); - + 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 = hb_language_to_string (hb_ot_tag_to_language (lang_tag)); + lang = pango_language_from_string (hb_language_to_string (hb_ot_tag_to_language (lang_tag))); } else lang = NULL; - s = g_string_new (""); - g_string_append_printf (s, "%s", text); + ensure_range (start, end, desc, features, lang); - gtk_label_set_markup (GTK_LABEL (label), s->str); + attrs = pango_attr_list_new (); - g_string_free (s, TRUE); + 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); - g_free (font_settings); + pango_font_description_free (desc); + g_free (features); + pango_attr_list_unref (attrs); } static PangoFont * @@ -122,61 +427,13 @@ 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); + return pango_context_load_font (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; @@ -201,17 +458,45 @@ tag_pair_equal (gconstpointer a, gconstpointer 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, l; + gint i, j, k; FT_Face ft_face; PangoFont *pango_font; 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)); + active = hb_ot_tag_from_language (hb_language_from_string (lang, -1)); + g_free (lang); store = gtk_list_store_new (4, G_TYPE_STRING, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT); @@ -224,7 +509,7 @@ update_script_combo (void) 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")); + g_hash_table_add (tags, pair); if (hb_font) { @@ -244,13 +529,6 @@ update_script_combo (void) 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++) { @@ -273,84 +551,70 @@ update_script_combo (void) 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; - } - } - } + GtkTreeIter iter; if (pair->lang_tag == HB_OT_TAG_DEFAULT_LANGUAGE) - langname = "Default"; + langname = NC_("Language", "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++) + langname = get_language_name_for_tag (pair->lang_tag); + if (!langname) { - if (pair->lang_tag == language_names[l].tag) - { - langname = language_names[l].name; - break; - } + hb_tag_to_string (pair->lang_tag, langbuf); + langbuf[4] = 0; + langname = langbuf; } } - name = g_strdup_printf ("%s - %s", scriptname, langname); - - gtk_list_store_insert_with_values (store, NULL, -1, - 0, name, + gtk_list_store_insert_with_values (store, &iter, -1, + 0, langname, 1, pair->script_index, 2, pair->lang_index, 3, pair->lang_tag, -1); - - g_free (name); + 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)); - gtk_combo_box_set_active (GTK_COMBO_BOX (script_lang), 0); + 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, k; + gint i, j; GtkTreeModel *model; GtkTreeIter iter; guint script_index, lang_index; PangoFont *pango_font; FT_Face ft_face; hb_font_t *hb_font; + GList *l; - for (i = 0; i < num_features; i++) - gtk_widget_set_opacity (icon[i], 0); + 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 */ @@ -371,6 +635,7 @@ update_features (void) { 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); @@ -389,14 +654,64 @@ update_features (void) for (j = 0; j < count; j++) { - for (k = 0; k < num_features; k++) +#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) { - if (hb_tag_from_string (feature_names[k], -1) == features[j]) - gtk_widget_set_opacity (icon[k], 0.5); + 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); } @@ -404,10 +719,885 @@ update_features (void) g_object_unref (pango_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_entry_set_text (GTK_ENTRY (entry), str); + g_free (str); + + update_display (); +} + +static void +entry_activated (GtkEntry *entry, + GtkAdjustment *adjustment) +{ + gdouble value; + gchar *err = NULL; + + value = g_strtod (gtk_entry_get_text (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_entry_set_width_chars (GTK_ENTRY (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; + PangoFont *pango_font; + 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); + + pango_font = get_pango_font (); + ft_face = pango_fc_font_lock_face (PANGO_FC_FONT (pango_font)), + + 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); + } + + pango_fc_font_unlock_face (PANGO_FC_FONT (pango_font)); + g_object_unref (pango_font); +} + static void font_changed (void) { update_script_combo (); + update_features (); + update_font_variations (); } static void @@ -420,17 +1610,26 @@ script_changed (void) static void reset_features (void) { - int i; + GList *l; - 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 < num_features; i++) + 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) { - if (!GTK_IS_RADIO_BUTTON (toggle[i])) + FeatureItem *item = l->data; + + if (GTK_IS_RADIO_BUTTON (item->feat)) { - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle[i]), FALSE); - gtk_widget_set_sensitive (toggle[i], FALSE); + 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); } } } @@ -442,6 +1641,7 @@ switch_to_entry (void) { text = g_strdup (gtk_entry_get_text (GTK_ENTRY (entry))); gtk_stack_set_visible_child_name (GTK_STACK (stack), "entry"); + gtk_widget_grab_focus (entry); } static void @@ -453,6 +1653,21 @@ switch_to_label (void) 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 (GtkEntry *entry, GdkEventKey *event) { @@ -463,7 +1678,7 @@ entry_key_press (GtkEntry *entry, GdkEventKey *event) if (keyval == GDK_KEY_Escape) { gtk_entry_set_text (GTK_ENTRY (entry), text); - switch_to_label (); + stop_edit (); return GDK_EVENT_STOP; } @@ -478,7 +1693,7 @@ do_font_features (GtkWidget *do_widget) if (!window) { GtkBuilder *builder; - int i; + GtkWidget *feature_list; builder = gtk_builder_new_from_resource ("/font_features/font-features.ui"); @@ -486,32 +1701,121 @@ do_font_features (GtkWidget *do_widget) 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, "stop_edit", G_CALLBACK (stop_edit)); + gtk_builder_add_callback_symbol (builder, "toggle_edit", G_CALLBACK (toggle_edit)); gtk_builder_add_callback_symbol (builder, "entry_key_press", G_CALLBACK (entry_key_press)); 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")); - 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")); + edit_toggle = GTK_WIDGET (gtk_builder_get_object (builder, "edit_toggle")); - for (i = 0; i < num_features; i++) - { - char *iname; + 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); - 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); - } + 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 (); diff --git a/demos/gtk-demo/fontplane.c b/demos/gtk-demo/fontplane.c new file mode 100644 index 0000000000..0e7e2d0b7d --- /dev/null +++ b/demos/gtk-demo/fontplane.c @@ -0,0 +1,315 @@ +/* GTK - The GIMP Toolkit + * Copyright (C) 2012 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + */ + +#include "config.h" + +#include "fontplane.h" + +#include "gtk.h" + +enum { + PROP_0, + PROP_WEIGHT_ADJUSTMENT, + PROP_WIDTH_ADJUSTMENT +}; + +G_DEFINE_TYPE (GtkFontPlane, gtk_font_plane, GTK_TYPE_WIDGET) + +static double +adjustment_get_normalized_value (GtkAdjustment *adj) +{ + return (gtk_adjustment_get_value (adj) - gtk_adjustment_get_lower (adj)) / + (gtk_adjustment_get_upper (adj) - gtk_adjustment_get_lower (adj)); +} + +static void +val_to_xy (GtkFontPlane *plane, + gint *x, + gint *y) +{ + gdouble u, v; + gint width, height; + + width = gtk_widget_get_allocated_width (GTK_WIDGET (plane)); + height = gtk_widget_get_allocated_height (GTK_WIDGET (plane)); + + u = adjustment_get_normalized_value (plane->width_adj); + v = adjustment_get_normalized_value (plane->weight_adj); + + *x = CLAMP (width * u, 0, width - 1); + *y = CLAMP (height * (1 - v), 0, height - 1); +} + +static void +plane_snapshot (GtkWidget *widget, + GtkSnapshot *snapshot) +{ + GtkFontPlane *plane = GTK_FONT_PLANE (widget); + gint x, y; + gint width, height; + cairo_t *cr; + + val_to_xy (plane, &x, &y); + width = gtk_widget_get_allocated_width (widget); + height = gtk_widget_get_allocated_height (widget); + + cr = gtk_snapshot_append_cairo (snapshot, + &GRAPHENE_RECT_INIT (0, 0, width, height), + "FontPlane"); + + cairo_set_source_rgb (cr, 0, 0, 0); + cairo_rectangle (cr, 0, 0, width, height); + cairo_paint (cr); + + cairo_move_to (cr, 0, y + 0.5); + cairo_line_to (cr, width, y + 0.5); + + cairo_move_to (cr, x + 0.5, 0); + cairo_line_to (cr, x + 0.5, height); + + if (gtk_widget_has_visible_focus (widget)) + { + cairo_set_line_width (cr, 3.0); + cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 0.6); + cairo_stroke_preserve (cr); + + cairo_set_line_width (cr, 1.0); + cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 0.8); + cairo_stroke (cr); + } + else + { + cairo_set_line_width (cr, 1.0); + cairo_set_source_rgba (cr, 0.8, 0.8, 0.8, 0.8); + cairo_stroke (cr); + } + + cairo_destroy (cr); +} + +static void +set_cross_cursor (GtkWidget *widget, + gboolean enabled) +{ + if (enabled) + gtk_widget_set_cursor_from_name (widget, "crosshair"); + else + gtk_widget_set_cursor (widget, NULL); +} + +static void +adj_changed (GtkFontPlane *plane) +{ + gtk_widget_queue_draw (GTK_WIDGET (plane)); +} + +static void +adjustment_set_normalized_value (GtkAdjustment *adj, + double val) +{ + gtk_adjustment_set_value (adj, + gtk_adjustment_get_lower (adj) + + val * (gtk_adjustment_get_upper (adj) - gtk_adjustment_get_lower (adj))); +} + +static void +update_value (GtkFontPlane *plane, + gint x, + gint y) +{ + GtkWidget *widget = GTK_WIDGET (plane); + gdouble u, v; + + u = CLAMP (x * (1.0 / gtk_widget_get_allocated_width (widget)), 0, 1); + v = CLAMP (1 - y * (1.0 / gtk_widget_get_allocated_height (widget)), 0, 1); + + adjustment_set_normalized_value (plane->width_adj, u); + adjustment_set_normalized_value (plane->weight_adj, v); + + gtk_widget_queue_draw (widget); +} + +static void +hold_action (GtkGestureLongPress *gesture, + gdouble x, + gdouble y, + GtkFontPlane *plane) +{ + gboolean handled; + + g_signal_emit_by_name (plane, "popup-menu", &handled); +} + +static void +plane_drag_gesture_begin (GtkGestureDrag *gesture, + gdouble start_x, + gdouble start_y, + GtkFontPlane *plane) +{ + guint button; + + button = gtk_gesture_single_get_current_button (GTK_GESTURE_SINGLE (gesture)); + + if (button == GDK_BUTTON_SECONDARY) + { + gboolean handled; + + g_signal_emit_by_name (plane, "popup-menu", &handled); + } + + if (button != GDK_BUTTON_PRIMARY) + { + gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_DENIED); + return; + } + + set_cross_cursor (GTK_WIDGET (plane), TRUE); + update_value (plane, start_x, start_y); + gtk_widget_grab_focus (GTK_WIDGET (plane)); + gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED); +} + +static void +plane_drag_gesture_update (GtkGestureDrag *gesture, + gdouble offset_x, + gdouble offset_y, + GtkFontPlane *plane) +{ + gdouble start_x, start_y; + + gtk_gesture_drag_get_start_point (GTK_GESTURE_DRAG (gesture), + &start_x, &start_y); + update_value (plane, start_x + offset_x, start_y + offset_y); +} + +static void +plane_drag_gesture_end (GtkGestureDrag *gesture, + gdouble offset_x, + gdouble offset_y, + GtkFontPlane *plane) +{ + set_cross_cursor (GTK_WIDGET (plane), FALSE); +} + +static void +gtk_font_plane_init (GtkFontPlane *plane) +{ + gtk_widget_set_has_window (GTK_WIDGET (plane), FALSE); + gtk_widget_set_can_focus (GTK_WIDGET (plane), TRUE); + + plane->drag_gesture = gtk_gesture_drag_new (GTK_WIDGET (plane)); + g_signal_connect (plane->drag_gesture, "drag-begin", + G_CALLBACK (plane_drag_gesture_begin), plane); + g_signal_connect (plane->drag_gesture, "drag-update", + G_CALLBACK (plane_drag_gesture_update), plane); + g_signal_connect (plane->drag_gesture, "drag-end", + G_CALLBACK (plane_drag_gesture_end), plane); + gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (plane->drag_gesture), 0); + + plane->long_press_gesture = gtk_gesture_long_press_new (GTK_WIDGET (plane)); + g_signal_connect (plane->long_press_gesture, "pressed", + G_CALLBACK (hold_action), plane); + gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (plane->long_press_gesture), + TRUE); +} + +static void +plane_finalize (GObject *object) +{ + GtkFontPlane *plane = GTK_FONT_PLANE (object); + + g_clear_object (&plane->weight_adj); + g_clear_object (&plane->width_adj); + + g_clear_object (&plane->drag_gesture); + g_clear_object (&plane->long_press_gesture); + + G_OBJECT_CLASS (gtk_font_plane_parent_class)->finalize (object); +} + +static void +plane_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GtkFontPlane *plane = GTK_FONT_PLANE (object); + GtkAdjustment *adjustment; + + switch (prop_id) + { + case PROP_WEIGHT_ADJUSTMENT: + adjustment = GTK_ADJUSTMENT (g_value_get_object (value)); + if (adjustment) + { + plane->weight_adj = g_object_ref_sink (adjustment); + g_signal_connect_swapped (adjustment, "value-changed", G_CALLBACK (adj_changed), plane); + } + break; + case PROP_WIDTH_ADJUSTMENT: + adjustment = GTK_ADJUSTMENT (g_value_get_object (value)); + if (adjustment) + { + plane->width_adj = g_object_ref_sink (adjustment); + g_signal_connect_swapped (adjustment, "value-changed", G_CALLBACK (adj_changed), plane); + } + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gtk_font_plane_class_init (GtkFontPlaneClass *class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (class); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class); + + object_class->finalize = plane_finalize; + object_class->set_property = plane_set_property; + + widget_class->snapshot = plane_snapshot; + + g_object_class_install_property (object_class, + PROP_WEIGHT_ADJUSTMENT, + g_param_spec_object ("weight-adjustment", + NULL, + NULL, + GTK_TYPE_ADJUSTMENT, + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property (object_class, + PROP_WIDTH_ADJUSTMENT, + g_param_spec_object ("width-adjustment", + NULL, + NULL, + GTK_TYPE_ADJUSTMENT, + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY)); +} + +GtkWidget * +gtk_font_plane_new (GtkAdjustment *weight_adj, + GtkAdjustment *width_adj) +{ + return g_object_new (GTK_TYPE_FONT_PLANE, + "weight-adjustment", weight_adj, + "width-adjustment", width_adj, + NULL); +} diff --git a/demos/gtk-demo/fontplane.h b/demos/gtk-demo/fontplane.h new file mode 100644 index 0000000000..e9983a1194 --- /dev/null +++ b/demos/gtk-demo/fontplane.h @@ -0,0 +1,65 @@ +/* GTK - The GIMP Toolkit + * Copyright (C) 2012 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + */ + +#ifndef __GTK_FONT_PLANE_H__ +#define __GTK_FONT_PLANE_H__ + +#include + +G_BEGIN_DECLS + +#define GTK_TYPE_FONT_PLANE (gtk_font_plane_get_type ()) +#define GTK_FONT_PLANE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_FONT_PLANE, GtkFontPlane)) +#define GTK_FONT_PLANE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_FONT_PLANE, GtkFontPlaneClass)) +#define GTK_IS_FONT_PLANE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_COLOR_PLANE)) +#define GTK_IS_FONT_PLANE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_COLOR_PLANE)) +#define GTK_FONT_PLANE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_FONT_PLANE, GtkFontPlaneClass)) + + +typedef struct _GtkFontPlane GtkFontPlane; +typedef struct _GtkFontPlaneClass GtkFontPlaneClass; + +struct _GtkFontPlane +{ + GtkWidget parent_instance; + + GtkAdjustment *weight_adj; + GtkAdjustment *width_adj; + + GtkGesture *drag_gesture; + GtkGesture *long_press_gesture; +}; + +struct _GtkFontPlaneClass +{ + GtkWidgetClass parent_class; + + /* Padding for future expansion */ + void (*_gtk_reserved1) (void); + void (*_gtk_reserved2) (void); + void (*_gtk_reserved3) (void); + void (*_gtk_reserved4) (void); +}; + + +GType gtk_font_plane_get_type (void) G_GNUC_CONST; +GtkWidget * gtk_font_plane_new (GtkAdjustment *width_adj, + GtkAdjustment *weight_adj); + +G_END_DECLS + +#endif /* __GTK_FONT_PLANE_H__ */ diff --git a/demos/gtk-demo/language-names.c b/demos/gtk-demo/language-names.c new file mode 100644 index 0000000000..2d433cff06 --- /dev/null +++ b/demos/gtk-demo/language-names.c @@ -0,0 +1,233 @@ +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "language-names.h" + +#define ISO_CODES_PREFIX "/usr" +#define ISO_CODES_DATADIR ISO_CODES_PREFIX "/share/xml/iso-codes" +#define ISO_CODES_LOCALESDIR ISO_CODES_PREFIX "/share/locale" + +static GHashTable *language_map; + +static char * +get_first_item_in_semicolon_list (const char *list) +{ + char **items; + char *item; + + items = g_strsplit (list, "; ", 2); + + item = g_strdup (items[0]); + g_strfreev (items); + + return item; +} + +static char * +capitalize_utf8_string (const char *str) +{ + char first[8] = { 0 }; + + if (!str) + return NULL; + + g_unichar_to_utf8 (g_unichar_totitle (g_utf8_get_char (str)), first); + + return g_strconcat (first, g_utf8_offset_to_pointer (str, 1), NULL); +} + +static char * +get_display_name (const char *language) +{ + const char *translated; + char *tmp; + char *name; + + translated = dgettext ("iso_639", language); + + tmp = get_first_item_in_semicolon_list (translated); + name = capitalize_utf8_string (tmp); + g_free (tmp); + + return name; +} + +static void +languages_parse_start_tag (GMarkupParseContext *ctx, + const char *element_name, + const char **attr_names, + const char **attr_values, + gpointer user_data, + GError **error) +{ + const char *ccode_longB; + const char *ccode_longT; + const char *ccode; + const char *ccode_id; + const char *lang_name; + char *display_name; + + if (!(g_str_equal (element_name, "iso_639_entry") || + g_str_equal (element_name, "iso_639_3_entry")) || + attr_names == NULL || + attr_values == NULL) + return; + + ccode = NULL; + ccode_longB = NULL; + ccode_longT = NULL; + ccode_id = NULL; + lang_name = NULL; + + while (*attr_names && *attr_values) + { + if (g_str_equal (*attr_names, "iso_639_1_code")) + { + if (**attr_values) + { + if (strlen (*attr_values) != 2) + return; + ccode = *attr_values; + } + } + else if (g_str_equal (*attr_names, "iso_639_2B_code")) + { + if (**attr_values) + { + if (strlen (*attr_values) != 3) + return; + ccode_longB = *attr_values; + } + } + else if (g_str_equal (*attr_names, "iso_639_2T_code")) + { + if (**attr_values) + { + if (strlen (*attr_values) != 3) + return; + ccode_longT = *attr_values; + } + } + else if (g_str_equal (*attr_names, "id")) + { + if (**attr_values) + { + if (strlen (*attr_values) != 2 && + strlen (*attr_values) != 3) + return; + ccode_id = *attr_values; + } + } + else if (g_str_equal (*attr_names, "name")) + { + lang_name = *attr_values; + } + + ++attr_names; + ++attr_values; + } + + if (lang_name == NULL) + return; + + display_name = get_display_name (lang_name); + + if (ccode != NULL) + g_hash_table_insert (language_map, + pango_language_from_string (ccode), + g_strdup (display_name)); + + if (ccode_longB != NULL) + g_hash_table_insert (language_map, + pango_language_from_string (ccode_longB), + g_strdup (display_name)); + + if (ccode_longT != NULL) + g_hash_table_insert (language_map, + pango_language_from_string (ccode_longT), + g_strdup (display_name)); + + if (ccode_id != NULL) + g_hash_table_insert (language_map, + pango_language_from_string (ccode_id), + g_strdup (display_name)); + + g_free (display_name); +} + +static void +languages_variant_init (const char *variant) +{ + gboolean res; + gsize buf_len; + g_autofree char *buf = NULL; + g_autofree char *filename = NULL; + g_autoptr (GError) error = NULL; + + bindtextdomain (variant, ISO_CODES_LOCALESDIR); + bind_textdomain_codeset (variant, "UTF-8"); + + error = NULL; + filename = g_strconcat (ISO_CODES_DATADIR, "/", variant, ".xml", NULL); + res = g_file_get_contents (filename, &buf, &buf_len, &error); + if (res) + { + g_autoptr (GMarkupParseContext) ctx = NULL; + GMarkupParser parser = { languages_parse_start_tag, NULL, NULL, NULL, NULL }; + + ctx = g_markup_parse_context_new (&parser, 0, NULL, NULL); + + error = NULL; + res = g_markup_parse_context_parse (ctx, buf, buf_len, &error); + + if (!res) + g_warning ("Failed to parse '%s': %s\n", filename, error->message); + } + else + g_warning ("Failed to load '%s': %s\n", filename, error->message); +} + +static void +languages_init (void) +{ + if (language_map) + return; + + language_map = g_hash_table_new_full (NULL, NULL, NULL, g_free); + languages_variant_init ("iso_639"); + languages_variant_init ("iso_639_3"); +} + +const char * +get_language_name (PangoLanguage *language) +{ + languages_init (); + + return (const char *) g_hash_table_lookup (language_map, language); +} + +const char * +get_language_name_for_tag (guint32 tag) +{ + hb_language_t lang; + const char *s; + + lang = hb_ot_tag_to_language (tag); + s = hb_language_to_string (lang); + + return get_language_name (pango_language_from_string (s)); +} diff --git a/demos/gtk-demo/language-names.h b/demos/gtk-demo/language-names.h new file mode 100644 index 0000000000..562f7ae2f8 --- /dev/null +++ b/demos/gtk-demo/language-names.h @@ -0,0 +1,13 @@ +#ifndef LANGUAGE_NAMES_H +#define LANGUAGE_NAMES_H + +#include + +G_BEGIN_DECLS + +const char * get_language_name (PangoLanguage *language); +const char * get_language_name_for_tag (guint32 tag); + +G_END_DECLS + +#endif diff --git a/demos/gtk-demo/meson.build b/demos/gtk-demo/meson.build index 0d5744762d..f4c3613215 100644 --- a/demos/gtk-demo/meson.build +++ b/demos/gtk-demo/meson.build @@ -94,7 +94,7 @@ gtkdemo_resources = gnome.compile_resources('gtkdemo_resources', source_dir: '.') executable('gtk4-demo', - 'main.c', 'gtkfishbowl.c', demos, demos_h, gtkdemo_resources, + 'main.c', 'gtkfishbowl.c', 'fontplane.c', 'script-names.c', 'language-names.c', demos, demos_h, gtkdemo_resources, c_args: gtkdemo_args, dependencies: gtkdemo_deps, include_directories: confinc, diff --git a/demos/gtk-demo/script-names.c b/demos/gtk-demo/script-names.c new file mode 100644 index 0000000000..5049c34d0b --- /dev/null +++ b/demos/gtk-demo/script-names.c @@ -0,0 +1,184 @@ +#include "config.h" +#include +#include +#include + +#include "script-names.h" + +static struct { + GUnicodeScript script; + hb_script_t hb_script; + const char *name; +} scripts[] = +{ + { G_UNICODE_SCRIPT_COMMON, HB_SCRIPT_COMMON, NULL }, + { G_UNICODE_SCRIPT_INHERITED, HB_SCRIPT_INHERITED, NULL }, + { G_UNICODE_SCRIPT_ARABIC, HB_SCRIPT_ARABIC, NC_("Script", "Arabic") }, + { G_UNICODE_SCRIPT_ARMENIAN, HB_SCRIPT_ARMENIAN, NC_("Script", "Armenian") }, + { G_UNICODE_SCRIPT_BENGALI, HB_SCRIPT_BENGALI, NC_("Script", "Bengali") }, + { G_UNICODE_SCRIPT_BOPOMOFO, HB_SCRIPT_BOPOMOFO, NC_("Script", "Bopomofo") }, + { G_UNICODE_SCRIPT_CHEROKEE, HB_SCRIPT_CHEROKEE, NC_("Script", "Cherokee") }, + { G_UNICODE_SCRIPT_COPTIC, HB_SCRIPT_COPTIC, NC_("Script", "Coptic") }, + { G_UNICODE_SCRIPT_CYRILLIC, HB_SCRIPT_CYRILLIC, NC_("Script", "Cyrillic") }, + { G_UNICODE_SCRIPT_DESERET, HB_SCRIPT_DESERET, NC_("Script", "Deseret") }, + { G_UNICODE_SCRIPT_DEVANAGARI, HB_SCRIPT_DEVANAGARI, NC_("Script", "Devanagari") }, + { G_UNICODE_SCRIPT_ETHIOPIC, HB_SCRIPT_ETHIOPIC, NC_("Script", "Ethiopic") }, + { G_UNICODE_SCRIPT_GEORGIAN, HB_SCRIPT_GEORGIAN, NC_("Script", "Georgian") }, + { G_UNICODE_SCRIPT_GOTHIC, HB_SCRIPT_GOTHIC, NC_("Script", "Gothic") }, + { G_UNICODE_SCRIPT_GREEK, HB_SCRIPT_GREEK, NC_("Script", "Greek") }, + { G_UNICODE_SCRIPT_GUJARATI, HB_SCRIPT_GUJARATI, NC_("Script", "Gujarati") }, + { G_UNICODE_SCRIPT_GURMUKHI, HB_SCRIPT_GURMUKHI, NC_("Script", "Gurmukhi") }, + { G_UNICODE_SCRIPT_HAN, HB_SCRIPT_HAN, NC_("Script", "Han") }, + { G_UNICODE_SCRIPT_HANGUL, HB_SCRIPT_HANGUL, NC_("Script", "Hangul") }, + { G_UNICODE_SCRIPT_HEBREW, HB_SCRIPT_HEBREW, NC_("Script", "Hebrew") }, + { G_UNICODE_SCRIPT_HIRAGANA, HB_SCRIPT_HIRAGANA, NC_("Script", "Hiragana") }, + { G_UNICODE_SCRIPT_KANNADA, HB_SCRIPT_KANNADA, NC_("Script", "Kannada") }, + { G_UNICODE_SCRIPT_KATAKANA, HB_SCRIPT_KATAKANA, NC_("Script", "Katakana") }, + { G_UNICODE_SCRIPT_KHMER, HB_SCRIPT_KHMER, NC_("Script", "Khmer") }, + { G_UNICODE_SCRIPT_LAO, HB_SCRIPT_LAO, NC_("Script", "Lao") }, + { G_UNICODE_SCRIPT_LATIN, HB_SCRIPT_LATIN, NC_("Script", "Latin") }, + { G_UNICODE_SCRIPT_MALAYALAM, HB_SCRIPT_MALAYALAM, NC_("Script", "Malayalam") }, + { G_UNICODE_SCRIPT_MONGOLIAN, HB_SCRIPT_MONGOLIAN, NC_("Script", "Mongolian") }, + { G_UNICODE_SCRIPT_MYANMAR, HB_SCRIPT_MYANMAR, NC_("Script", "Myanmar") }, + { G_UNICODE_SCRIPT_OGHAM, HB_SCRIPT_OGHAM, NC_("Script", "Ogham") }, + { G_UNICODE_SCRIPT_OLD_ITALIC, HB_SCRIPT_OLD_ITALIC, NC_("Script", "Old Italic") }, + { G_UNICODE_SCRIPT_ORIYA, HB_SCRIPT_ORIYA, NC_("Script", "Oriya") }, + { G_UNICODE_SCRIPT_RUNIC, HB_SCRIPT_RUNIC, NC_("Script", "Runic") }, + { G_UNICODE_SCRIPT_SINHALA, HB_SCRIPT_SINHALA, NC_("Script", "Sinhala") }, + { G_UNICODE_SCRIPT_SYRIAC, HB_SCRIPT_SYRIAC, NC_("Script", "Syriac") }, + { G_UNICODE_SCRIPT_TAMIL, HB_SCRIPT_TAMIL, NC_("Script", "Tamil") }, + { G_UNICODE_SCRIPT_TELUGU, HB_SCRIPT_TELUGU, NC_("Script", "Telugu") }, + { G_UNICODE_SCRIPT_THAANA, HB_SCRIPT_THAANA, NC_("Script", "Thaana") }, + { G_UNICODE_SCRIPT_THAI, HB_SCRIPT_THAI, NC_("Script", "Thai") }, + { G_UNICODE_SCRIPT_TIBETAN, HB_SCRIPT_TIBETAN, NC_("Script", "Tibetan") }, + { G_UNICODE_SCRIPT_CANADIAN_ABORIGINAL, HB_SCRIPT_CANADIAN_ABORIGINAL, NC_("Script", "Canadian Aboriginal") }, + { G_UNICODE_SCRIPT_YI, HB_SCRIPT_YI, NC_("Script", "Yi") }, + { G_UNICODE_SCRIPT_TAGALOG, HB_SCRIPT_TAGALOG, NC_("Script", "Tagalog") }, + { G_UNICODE_SCRIPT_HANUNOO, HB_SCRIPT_HANUNOO, NC_("Script", "Hanunoo") }, + { G_UNICODE_SCRIPT_BUHID, HB_SCRIPT_BUHID, NC_("Script", "Buhid") }, + { G_UNICODE_SCRIPT_TAGBANWA, HB_SCRIPT_TAGBANWA, NC_("Script", "Tagbanwa") }, + { G_UNICODE_SCRIPT_BRAILLE, HB_SCRIPT_BRAILLE, NC_("Script", "Braille") }, + { G_UNICODE_SCRIPT_CYPRIOT, HB_SCRIPT_CYPRIOT, NC_("Script", "Cypriot") }, + { G_UNICODE_SCRIPT_LIMBU, HB_SCRIPT_LIMBU, NC_("Script", "Limbu") }, + { G_UNICODE_SCRIPT_OSMANYA, HB_SCRIPT_OSMANYA, NC_("Script", "Osmanya") }, + { G_UNICODE_SCRIPT_SHAVIAN, HB_SCRIPT_SHAVIAN, NC_("Script", "Shavian") }, + { G_UNICODE_SCRIPT_LINEAR_B, HB_SCRIPT_LINEAR_B, NC_("Script", "Linear B") }, + { G_UNICODE_SCRIPT_TAI_LE, HB_SCRIPT_TAI_LE, NC_("Script", "Tai Le") }, + { G_UNICODE_SCRIPT_UGARITIC, HB_SCRIPT_UGARITIC, NC_("Script", "Ugaritic") }, + { G_UNICODE_SCRIPT_NEW_TAI_LUE, HB_SCRIPT_NEW_TAI_LUE, NC_("Script", "New Tai Lue") }, + { G_UNICODE_SCRIPT_BUGINESE, HB_SCRIPT_BUGINESE, NC_("Script", "Buginese") }, + { G_UNICODE_SCRIPT_GLAGOLITIC, HB_SCRIPT_GLAGOLITIC, NC_("Script", "Glagolitic") }, + { G_UNICODE_SCRIPT_TIFINAGH, HB_SCRIPT_TIFINAGH, NC_("Script", "Tifinagh") }, + { G_UNICODE_SCRIPT_SYLOTI_NAGRI, HB_SCRIPT_SYLOTI_NAGRI, NC_("Script", "Syloti Nagri") }, + { G_UNICODE_SCRIPT_OLD_PERSIAN, HB_SCRIPT_OLD_PERSIAN, NC_("Script", "Old Persian") }, + { G_UNICODE_SCRIPT_KHAROSHTHI, HB_SCRIPT_KHAROSHTHI, NC_("Script", "Kharoshthi") }, + { G_UNICODE_SCRIPT_UNKNOWN, HB_SCRIPT_UNKNOWN, NC_("Script", "Unknown") }, + { G_UNICODE_SCRIPT_BALINESE, HB_SCRIPT_BALINESE, NC_("Script", "Balinese") }, + { G_UNICODE_SCRIPT_CUNEIFORM, HB_SCRIPT_CUNEIFORM, NC_("Script", "Cuneiform") }, + { G_UNICODE_SCRIPT_PHOENICIAN, HB_SCRIPT_PHOENICIAN, NC_("Script", "Phoenician") }, + { G_UNICODE_SCRIPT_PHAGS_PA, HB_SCRIPT_PHAGS_PA, NC_("Script", "Phags-pa") }, + { G_UNICODE_SCRIPT_NKO, HB_SCRIPT_NKO, NC_("Script", "N'Ko") }, + { G_UNICODE_SCRIPT_KAYAH_LI, HB_SCRIPT_KAYAH_LI, NC_("Script", "Kayah Li") }, + { G_UNICODE_SCRIPT_LEPCHA, HB_SCRIPT_LEPCHA, NC_("Script", "Lepcha") }, + { G_UNICODE_SCRIPT_REJANG, HB_SCRIPT_REJANG, NC_("Script", "Rejang") }, + { G_UNICODE_SCRIPT_SUNDANESE, HB_SCRIPT_SUNDANESE, NC_("Script", "Sundanese") }, + { G_UNICODE_SCRIPT_SAURASHTRA, HB_SCRIPT_SAURASHTRA, NC_("Script", "Saurashtra") }, + { G_UNICODE_SCRIPT_CHAM, HB_SCRIPT_CHAM, NC_("Script", "Cham") }, + { G_UNICODE_SCRIPT_OL_CHIKI, HB_SCRIPT_OL_CHIKI, NC_("Script", "Ol Chiki") }, + { G_UNICODE_SCRIPT_VAI, HB_SCRIPT_VAI, NC_("Script", "Vai") }, + { G_UNICODE_SCRIPT_CARIAN, HB_SCRIPT_CARIAN, NC_("Script", "Carian") }, + { G_UNICODE_SCRIPT_LYCIAN, HB_SCRIPT_LYCIAN, NC_("Script", "Lycian") }, + { G_UNICODE_SCRIPT_LYDIAN, HB_SCRIPT_LYDIAN, NC_("Script", "Lydian") }, + { G_UNICODE_SCRIPT_AVESTAN, HB_SCRIPT_AVESTAN, NC_("Script", "Avestan") }, + { G_UNICODE_SCRIPT_BAMUM, HB_SCRIPT_BAMUM, NC_("Script", "Bamum") }, + { G_UNICODE_SCRIPT_EGYPTIAN_HIEROGLYPHS, HB_SCRIPT_EGYPTIAN_HIEROGLYPHS, NC_("Script", "Egyptian Hieroglpyhs") }, + { G_UNICODE_SCRIPT_IMPERIAL_ARAMAIC, HB_SCRIPT_IMPERIAL_ARAMAIC, NC_("Script", "Imperial Aramaic") }, + { G_UNICODE_SCRIPT_INSCRIPTIONAL_PAHLAVI, HB_SCRIPT_INSCRIPTIONAL_PAHLAVI, NC_("Script", "Inscriptional Pahlavi") }, + { G_UNICODE_SCRIPT_INSCRIPTIONAL_PARTHIAN, HB_SCRIPT_INSCRIPTIONAL_PARTHIAN, NC_("Script", "Inscriptional Parthian") }, + { G_UNICODE_SCRIPT_JAVANESE, HB_SCRIPT_JAVANESE, NC_("Script", "Javanese") }, + { G_UNICODE_SCRIPT_KAITHI, HB_SCRIPT_KAITHI, NC_("Script", "Kaithi") }, + { G_UNICODE_SCRIPT_LISU, HB_SCRIPT_LISU, NC_("Script", "Lisu") }, + { G_UNICODE_SCRIPT_MEETEI_MAYEK, HB_SCRIPT_MEETEI_MAYEK, NC_("Script", "Meetei Mayek") }, + { G_UNICODE_SCRIPT_OLD_SOUTH_ARABIAN, HB_SCRIPT_OLD_SOUTH_ARABIAN, NC_("Script", "Old South Arabian") }, + { G_UNICODE_SCRIPT_OLD_TURKIC, HB_SCRIPT_OLD_TURKIC, NC_("Script", "Old Turkic") }, + { G_UNICODE_SCRIPT_SAMARITAN, HB_SCRIPT_SAMARITAN, NC_("Script", "Samaritan") }, + { G_UNICODE_SCRIPT_TAI_THAM, HB_SCRIPT_TAI_THAM, NC_("Script", "Tai Tham") }, + { G_UNICODE_SCRIPT_TAI_VIET, HB_SCRIPT_TAI_VIET, NC_("Script", "Tai Viet") }, + { G_UNICODE_SCRIPT_BATAK, HB_SCRIPT_BATAK, NC_("Script", "Batak") }, + { G_UNICODE_SCRIPT_BRAHMI, HB_SCRIPT_BRAHMI, NC_("Script", "Brahmi") }, + { G_UNICODE_SCRIPT_MANDAIC, HB_SCRIPT_MANDAIC, NC_("Script", "Mandaic") }, + { G_UNICODE_SCRIPT_CHAKMA, HB_SCRIPT_CHAKMA, NC_("Script", "Chakma") }, + { G_UNICODE_SCRIPT_MEROITIC_CURSIVE, HB_SCRIPT_MEROITIC_CURSIVE, NC_("Script", "Meroitic Cursive") }, + { G_UNICODE_SCRIPT_MEROITIC_HIEROGLYPHS, HB_SCRIPT_MEROITIC_HIEROGLYPHS, NC_("Script", "Meroitic Hieroglyphs") }, + { G_UNICODE_SCRIPT_MIAO, HB_SCRIPT_MIAO, NC_("Script", "Miao") }, + { G_UNICODE_SCRIPT_SHARADA, HB_SCRIPT_SHARADA, NC_("Script", "Sharada") }, + { G_UNICODE_SCRIPT_SORA_SOMPENG, HB_SCRIPT_SORA_SOMPENG, NC_("Script", "Sora Sompeng") }, + { G_UNICODE_SCRIPT_TAKRI, HB_SCRIPT_TAKRI, NC_("Script", "Takri") }, + { G_UNICODE_SCRIPT_BASSA_VAH, HB_SCRIPT_BASSA_VAH, NC_("Script", "Bassa") }, + { G_UNICODE_SCRIPT_CAUCASIAN_ALBANIAN, HB_SCRIPT_CAUCASIAN_ALBANIAN, NC_("Script", "Caucasian Albanian") }, + { G_UNICODE_SCRIPT_DUPLOYAN, HB_SCRIPT_DUPLOYAN, NC_("Script", "Duployan") }, + { G_UNICODE_SCRIPT_ELBASAN, HB_SCRIPT_ELBASAN, NC_("Script", "Elbasan") }, + { G_UNICODE_SCRIPT_GRANTHA, HB_SCRIPT_GRANTHA, NC_("Script", "Grantha") }, + { G_UNICODE_SCRIPT_KHOJKI, HB_SCRIPT_KHOJKI, NC_("Script", "Kjohki") }, + { G_UNICODE_SCRIPT_KHUDAWADI, HB_SCRIPT_KHUDAWADI, NC_("Script", "Khudawadi, Sindhi") }, + { G_UNICODE_SCRIPT_LINEAR_A, HB_SCRIPT_LINEAR_A, NC_("Script", "Linear A") }, + { G_UNICODE_SCRIPT_MAHAJANI, HB_SCRIPT_MAHAJANI, NC_("Script", "Mahajani") }, + { G_UNICODE_SCRIPT_MANICHAEAN, HB_SCRIPT_MANICHAEAN, NC_("Script", "Manichaean") }, + { G_UNICODE_SCRIPT_MENDE_KIKAKUI, HB_SCRIPT_MENDE_KIKAKUI, NC_("Script", "Mende Kikakui") }, + { G_UNICODE_SCRIPT_MODI, HB_SCRIPT_MODI, NC_("Script", "Modi") }, + { G_UNICODE_SCRIPT_MRO, HB_SCRIPT_MRO, NC_("Script", "Mro") }, + { G_UNICODE_SCRIPT_NABATAEAN, HB_SCRIPT_NABATAEAN, NC_("Script", "Nabataean") }, + { G_UNICODE_SCRIPT_OLD_NORTH_ARABIAN, HB_SCRIPT_OLD_NORTH_ARABIAN, NC_("Script", "Old North Arabian") }, + { G_UNICODE_SCRIPT_OLD_PERMIC, HB_SCRIPT_OLD_PERMIC, NC_("Script", "Old Permic") }, + { G_UNICODE_SCRIPT_PAHAWH_HMONG, HB_SCRIPT_PAHAWH_HMONG, NC_("Script", "Pahawh Hmong") }, + { G_UNICODE_SCRIPT_PALMYRENE, HB_SCRIPT_PALMYRENE, NC_("Script", "Palmyrene") }, + { G_UNICODE_SCRIPT_PAU_CIN_HAU, HB_SCRIPT_PAU_CIN_HAU, NC_("Script", "Pau Cin Hau") }, + { G_UNICODE_SCRIPT_PSALTER_PAHLAVI, HB_SCRIPT_PSALTER_PAHLAVI, NC_("Script", "Psalter Pahlavi") }, + { G_UNICODE_SCRIPT_SIDDHAM, HB_SCRIPT_SIDDHAM, NC_("Script", "Siddham") }, + { G_UNICODE_SCRIPT_TIRHUTA, HB_SCRIPT_TIRHUTA, NC_("Script", "Tirhuta") }, + { G_UNICODE_SCRIPT_WARANG_CITI, HB_SCRIPT_WARANG_CITI, NC_("Script", "Warang Citi") }, + { G_UNICODE_SCRIPT_AHOM, HB_SCRIPT_AHOM, NC_("Script", "Ahom") }, + { G_UNICODE_SCRIPT_ANATOLIAN_HIEROGLYPHS, HB_SCRIPT_ANATOLIAN_HIEROGLYPHS, NC_("Script", "Anatolian Hieroglyphs") }, + { G_UNICODE_SCRIPT_HATRAN, HB_SCRIPT_HATRAN, NC_("Script", "Hatran") }, + { G_UNICODE_SCRIPT_MULTANI, HB_SCRIPT_MULTANI, NC_("Script", "Multani") }, + { G_UNICODE_SCRIPT_OLD_HUNGARIAN, HB_SCRIPT_OLD_HUNGARIAN, NC_("Script", "Old Hungarian") }, + { G_UNICODE_SCRIPT_SIGNWRITING, HB_SCRIPT_SIGNWRITING, NC_("Script", "Signwriting") }, + { G_UNICODE_SCRIPT_ADLAM, HB_SCRIPT_ADLAM, NC_("Script", "Adlam") }, + { G_UNICODE_SCRIPT_BHAIKSUKI, HB_SCRIPT_BHAIKSUKI, NC_("Script", "Bhaiksuki") }, + { G_UNICODE_SCRIPT_MARCHEN, HB_SCRIPT_MARCHEN, NC_("Script", "Marchen") }, + { G_UNICODE_SCRIPT_NEWA, HB_SCRIPT_NEWA, NC_("Script", "Newa") }, + { G_UNICODE_SCRIPT_OSAGE, HB_SCRIPT_OSAGE, NC_("Script", "Osage") }, + { G_UNICODE_SCRIPT_TANGUT, HB_SCRIPT_TANGUT, NC_("Script", "Tangut") }, + { G_UNICODE_SCRIPT_MASARAM_GONDI, HB_SCRIPT_INVALID, NC_("Script", "Masaram Gondi") }, + { G_UNICODE_SCRIPT_NUSHU, HB_SCRIPT_INVALID, NC_("Script", "Nushu") }, + { G_UNICODE_SCRIPT_SOYOMBO, HB_SCRIPT_INVALID, NC_("Script", "Soyombo") }, + { G_UNICODE_SCRIPT_ZANABAZAR_SQUARE, HB_SCRIPT_INVALID, NC_("Script", "Zanabazar Square") }, +}; + +const char * +get_script_name (GUnicodeScript script) +{ + int i; + + for (i = 0; i < G_N_ELEMENTS (scripts); i++) + { + if (scripts[i].script == script) + return g_dpgettext2 (GETTEXT_PACKAGE, "Script", scripts[i].name); + } + + return NULL; +} + +const char * +get_script_name_for_tag (guint32 tag) +{ + int i; + + for (i = 0; i < G_N_ELEMENTS (scripts); i++) + { + if (scripts[i].hb_script == hb_script_from_iso15924_tag (tag)) + return g_dpgettext2 (GETTEXT_PACKAGE, "Script", scripts[i].name); + } + + return NULL; +} diff --git a/demos/gtk-demo/script-names.h b/demos/gtk-demo/script-names.h new file mode 100644 index 0000000000..3036c05b56 --- /dev/null +++ b/demos/gtk-demo/script-names.h @@ -0,0 +1,13 @@ +#ifndef SCRIPT_NAMES_H +#define SCRIPT_NAMES_H + +#include + +G_BEGIN_DECLS + +const char * get_script_name (GUnicodeScript script); +const char * get_script_name_for_tag (guint32 tag); + +G_END_DECLS + +#endif