diff options
-rw-r--r-- | config.h.meson | 4 | ||||
-rw-r--r-- | gtk/gtkfontchooserwidget.c | 549 | ||||
-rw-r--r-- | gtk/language-names.c | 233 | ||||
-rw-r--r-- | gtk/language-names.h | 13 | ||||
-rw-r--r-- | gtk/meson.build | 6 | ||||
-rw-r--r-- | gtk/open-type-layout.h | 155 | ||||
-rw-r--r-- | gtk/script-names.c | 184 | ||||
-rw-r--r-- | gtk/script-names.h | 13 | ||||
-rw-r--r-- | gtk/ui/gtkfontchooserwidget.ui | 1 | ||||
-rw-r--r-- | meson.build | 3 |
10 files changed, 1160 insertions, 1 deletions
diff --git a/config.h.meson b/config.h.meson index 651360abd2..73836bb9ee 100644 --- a/config.h.meson +++ b/config.h.meson @@ -289,3 +289,7 @@ #mesondefine GTK_LIBDIR #mesondefine GTK_PRINT_BACKENDS + +#mesondefine HAVE_HARFBUZZ + +#mesondefine HAVE_PANGOFT diff --git a/gtk/gtkfontchooserwidget.c b/gtk/gtkfontchooserwidget.c index 7e2381fea1..ad1cbe7037 100644 --- a/gtk/gtkfontchooserwidget.c +++ b/gtk/gtkfontchooserwidget.c @@ -51,6 +51,22 @@ #include "gtkwidget.h" #include "gtksettings.h" #include "gtkdialog.h" +#include "gtkradiobutton.h" +#include "gtkcombobox.h" +#include "gtkgesturemultipress.h" + +#if defined(HAVE_HARFBUZZ) && defined(HAVE_PANGOFT) +#include <pango/pangofc-font.h> +#include <hb.h> +#include <hb-ot.h> +#include <hb-ft.h> +#include <freetype/freetype.h> +#include <freetype/ftmm.h> +#include "language-names.h" +#include "script-names.h" +#endif + +#include "open-type-layout.h" /** * SECTION:gtkfontchooserwidget @@ -103,9 +119,14 @@ struct _GtkFontChooserWidgetPrivate GtkWidget *size_slider; GtkWidget *size_slider2; + GtkWidget *feature_box; + GtkWidget *feature_language_combo; + PangoFontMap *font_map; PangoFontDescription *font_desc; + char *font_features; + PangoLanguage *font_language; GtkTreeIter font_iter; /* invalid if font not available or pointer into model (not filter_model) to the row containing font */ GtkFontFilterFunc filter_func; @@ -116,6 +137,8 @@ struct _GtkFontChooserWidgetPrivate GtkFontChooserLevel level; + GList *feature_items; + GAction *tweak_action; }; @@ -168,6 +191,7 @@ static void gtk_font_chooser_widget_set_show_preview_entry (GtkFontChooserWi static void gtk_font_chooser_widget_set_cell_size (GtkFontChooserWidget *fontchooser); static void gtk_font_chooser_widget_load_fonts (GtkFontChooserWidget *fontchooser, gboolean force); +static void gtk_font_chooser_widget_populate_features (GtkFontChooserWidget *fontchooser); static gboolean visible_func (GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data); @@ -181,6 +205,8 @@ static void gtk_font_chooser_widget_set_level (GtkFontChooserWidg static GtkFontChooserLevel gtk_font_chooser_widget_get_level (GtkFontChooserWidget *fontchooser); static void selection_changed (GtkTreeSelection *selection, GtkFontChooserWidget *fontchooser); +static void update_font_features (GtkFontChooserWidget *fontchooser); +static void update_language (GtkFontChooserWidget *fontchooser); static void gtk_font_chooser_widget_iface_init (GtkFontChooserIface *iface); @@ -563,8 +589,12 @@ gtk_font_chooser_widget_update_preview_attributes (GtkFontChooserWidget *fontcho /* Prevent font fallback */ pango_attr_list_insert (attrs, pango_attr_fallback_new (FALSE)); - /* Force current font */ + /* Force current font and features */ pango_attr_list_insert (attrs, pango_attr_font_desc_new (priv->font_desc)); + if (priv->font_features) + pango_attr_list_insert (attrs, pango_attr_font_features_new (priv->font_features)); + if (priv->font_language) + pango_attr_list_insert (attrs, pango_attr_language_new (priv->font_language)); gtk_entry_set_attributes (GTK_ENTRY (priv->preview), attrs); @@ -698,6 +728,8 @@ gtk_font_chooser_widget_class_init (GtkFontChooserWidgetClass *klass) gtk_widget_class_bind_template_child_private (widget_class, GtkFontChooserWidget, stack); gtk_widget_class_bind_template_child_private (widget_class, GtkFontChooserWidget, grid); gtk_widget_class_bind_template_child_private (widget_class, GtkFontChooserWidget, font_name_label); + gtk_widget_class_bind_template_child_private (widget_class, GtkFontChooserWidget, feature_box); + gtk_widget_class_bind_template_child_private (widget_class, GtkFontChooserWidget, feature_language_combo); gtk_widget_class_bind_template_callback (widget_class, text_changed_cb); gtk_widget_class_bind_template_callback (widget_class, stop_search_cb); @@ -709,6 +741,7 @@ gtk_font_chooser_widget_class_init (GtkFontChooserWidgetClass *klass) gtk_widget_class_bind_template_callback (widget_class, size_change_cb); gtk_widget_class_bind_template_callback (widget_class, output_cb); gtk_widget_class_bind_template_callback (widget_class, selection_changed); + gtk_widget_class_bind_template_callback (widget_class, update_language); gtk_widget_class_set_css_name (widget_class, I_("fontchooser")); } @@ -778,6 +811,7 @@ gtk_font_chooser_widget_init (GtkFontChooserWidget *fontchooser) /* Load data and set initial style-dependent parameters */ gtk_font_chooser_widget_load_fonts (fontchooser, TRUE); + gtk_font_chooser_widget_populate_features (fontchooser); gtk_font_chooser_widget_set_cell_size (fontchooser); gtk_font_chooser_widget_take_font_desc (fontchooser, NULL); } @@ -1085,6 +1119,10 @@ gtk_font_chooser_widget_finalize (GObject *object) g_object_unref (priv->tweak_action); + g_list_free_full (priv->feature_items, g_free); + + g_free (priv->font_features); + G_OBJECT_CLASS (gtk_font_chooser_widget_parent_class)->finalize (object); } @@ -1355,6 +1393,511 @@ gtk_font_chooser_widget_ensure_selection (GtkFontChooserWidget *fontchooser) } } +#if defined(HAVE_HARFBUZZ) && defined(HAVE_PANGOFT) + +static void +add_script (GtkListStore *store, + guint script_index, + hb_tag_t script, + guint lang_index, + hb_tag_t lang) +{ + char langbuf[5]; + const char *langname; + + if (lang == HB_OT_TAG_DEFAULT_LANGUAGE) + langname = C_("Language", "Default"); + else + { + langname = get_language_name_for_tag (lang); + if (!langname) + { + hb_tag_to_string (lang, langbuf); + langbuf[4] = 0; + langname = langbuf; + } + } + + gtk_list_store_insert_with_values (store, NULL, -1, + 0, langname, + 1, script_index, + 2, lang_index, + 3, lang, + -1); +} + +static int +feature_language_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; +} + +typedef struct { + hb_tag_t script_tag; + hb_tag_t lang_tag; + unsigned int script_index; + unsigned int lang_index; +} TagPair; + +static guint +tag_pair_hash (gconstpointer data) +{ + const TagPair *pair = data; + + return pair->script_tag + pair->lang_tag; +} + +static gboolean +tag_pair_equal (gconstpointer a, gconstpointer b) +{ + const TagPair *pair_a = a; + const TagPair *pair_b = b; + + return pair_a->script_tag == pair_b->script_tag && pair_a->lang_tag == pair_b->lang_tag; +} + +static void +update_language_combo (GtkFontChooserWidget *fontchooser, + hb_face_t *hb_face) +{ + GtkFontChooserWidgetPrivate *priv = fontchooser->priv; + GtkListStore *store; + gint i, j, k; + hb_tag_t scripts[80]; + unsigned int n_scripts; + unsigned int count; + hb_tag_t table[2] = { HB_OT_TAG_GSUB, HB_OT_TAG_GPOS }; + GHashTable *tags; + GHashTableIter iter; + TagPair *pair; + + tags = g_hash_table_new_full (tag_pair_hash, tag_pair_equal, g_free, NULL); + + pair = g_new (TagPair, 1); + pair->script_tag = HB_OT_TAG_DEFAULT_SCRIPT; + pair->lang_tag = HB_OT_TAG_DEFAULT_LANGUAGE; + g_hash_table_add (tags, pair); + + n_scripts = 0; + for (i = 0; i < 2; i++) + { + count = G_N_ELEMENTS (scripts); + hb_ot_layout_table_get_script_tags (hb_face, table[i], n_scripts, &count, scripts); + n_scripts += count; + } + + for (j = 0; j < n_scripts; j++) + { + hb_tag_t languages[80]; + unsigned int n_languages; + + n_languages = 0; + for (i = 0; i < 2; i++) + { + count = G_N_ELEMENTS (languages); + hb_ot_layout_script_get_language_tags (hb_face, table[i], j, n_languages, &count, languages); + n_languages += count; + } + + for (k = 0; k < n_languages; k++) + { + pair = g_new (TagPair, 1); + pair->script_tag = scripts[j]; + pair->lang_tag = languages[k]; + pair->script_index = j; + pair->lang_index = k; + g_hash_table_add (tags, pair); + } + } + + store = gtk_list_store_new (4, G_TYPE_STRING, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT); + + g_hash_table_iter_init (&iter, tags); + while (g_hash_table_iter_next (&iter, (gpointer *)&pair, NULL)) + add_script (store, pair->script_index, pair->script_tag, pair->lang_index, pair->lang_tag); + + g_hash_table_unref (tags); + + gtk_tree_sortable_set_default_sort_func (GTK_TREE_SORTABLE (store), + feature_language_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 (priv->feature_language_combo), GTK_TREE_MODEL (store)); + gtk_combo_box_set_active (GTK_COMBO_BOX (priv->feature_language_combo), 0); +} + +typedef struct { + hb_tag_t tag; + const char *name; + GtkWidget *feat; +} FeatureItem; + +static const char * +get_feature_display_name (hb_tag_t 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 +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); + } + + g_signal_handlers_unblock_by_func (feat, feat_clicked, NULL); +} + +static void +feat_pressed (GtkGesture *gesture, + int n_press, + double x, + double y, + GtkWidget *feat) +{ + gboolean inconsistent; + + inconsistent = gtk_check_button_get_inconsistent (GTK_CHECK_BUTTON (feat)); + set_inconsistent (GTK_CHECK_BUTTON (feat), !inconsistent); +} + +static void +add_check_group (GtkFontChooserWidget *fontchooser, + const char *title, + const char **tags) +{ + GtkFontChooserWidgetPrivate *priv = fontchooser->priv; + 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++) + { + hb_tag_t tag; + GtkWidget *feat; + FeatureItem *item; + GtkGesture *gesture; + + 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_swapped (feat, "notify::active", G_CALLBACK (update_font_features), fontchooser); + g_signal_connect_swapped (feat, "notify::inconsistent", G_CALLBACK (update_font_features), fontchooser); + g_signal_connect (feat, "clicked", G_CALLBACK (feat_clicked), NULL); + + gesture = gtk_gesture_multi_press_new (feat); + g_object_set_data_full (G_OBJECT (feat), "press", gesture, g_object_unref); + + gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (gesture), GDK_BUTTON_SECONDARY); + g_signal_connect (gesture, "pressed", G_CALLBACK (feat_pressed), feat); + + gtk_container_add (GTK_CONTAINER (group), feat); + + item = g_new (FeatureItem, 1); + item->name = tags[i]; + item->tag = tag; + item->feat = feat; + + priv->feature_items = g_list_prepend (priv->feature_items, item); + } + + gtk_container_add (GTK_CONTAINER (priv->feature_box), group); +} + +static void +add_radio_group (GtkFontChooserWidget *fontchooser, + const char *title, + const char **tags) +{ + GtkFontChooserWidgetPrivate *priv = fontchooser->priv; + 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++) + { + hb_tag_t 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_swapped (feat, "notify::active", G_CALLBACK (update_font_features), fontchooser); + 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->feat = feat; + + priv->feature_items = g_list_prepend (priv->feature_items, item); + } + + gtk_container_add (GTK_CONTAINER (priv->feature_box), group); +} + +static void +gtk_font_chooser_widget_populate_features (GtkFontChooserWidget *fontchooser) +{ + const char *ligatures[] = { "liga", "dlig", "hlig", "clig", NULL }; + const char *letter_case[] = { "smcp", "c2sc", "pcap", "c2pc", "unic", "cpsp", "case", NULL }; + const char *number_case[] = { "xxxx", "lnum", "onum", NULL }; + const char *number_spacing[] = { "xxxx", "pnum", "tnum", NULL }; + const char *number_formatting[] = { "zero", "nalt", NULL }; + const char *char_variants[] = { "swsh", "cswh", "calt", "falt", "hist", "salt", "jalt", "titl", "rand", NULL }; + + add_check_group (fontchooser, _("Ligatures"), ligatures); + add_check_group (fontchooser, _("Letter Case"), letter_case); + add_radio_group (fontchooser, _("Number Case"), number_case); + add_radio_group (fontchooser, _("Number Spacing"), number_spacing); + add_check_group (fontchooser, _("Number Formatting"), number_formatting); + add_check_group (fontchooser, _("Character Variants"), char_variants); + + update_font_features (fontchooser); +} + +static void +gtk_font_chooser_widget_update_font_features (GtkFontChooserWidget *fontchooser) +{ + GtkFontChooserWidgetPrivate *priv = fontchooser->priv; + PangoFont *pango_font; + FT_Face ft_face; + hb_font_t *hb_font; + hb_tag_t script_tag = FT_MAKE_TAG('l','a','t','n'); + hb_tag_t lang_tag = FT_MAKE_TAG('D','E','U',' '); + guint script_index = 0; + guint lang_index = 0; + int i, j; + GList *l; + + gtk_widget_hide (priv->feature_language_combo); + + for (l = priv->feature_items; l; l = l->next) + { + FeatureItem *item = l->data; + gtk_widget_hide (item->feat); + gtk_widget_hide (gtk_widget_get_parent (item->feat)); + } + + pango_font = pango_context_load_font (gtk_widget_get_pango_context (GTK_WIDGET (fontchooser)), + priv->font_desc); + ft_face = pango_fc_font_lock_face (PANGO_FC_FONT (pango_font)), + hb_font = hb_ft_font_create (ft_face, NULL); + + if (hb_font) + { + hb_tag_t table[2] = { HB_OT_TAG_GSUB, HB_OT_TAG_GPOS }; + hb_face_t *hb_face; + hb_tag_t features[80]; + unsigned int count; + unsigned int n_features; + + hb_face = hb_font_get_face (hb_font); + + update_language_combo (fontchooser, hb_face); + + n_features = 0; + for (i = 0; i < 2; i++) + { + hb_ot_layout_table_find_script (hb_face, table[i], script_tag, &script_index); + hb_ot_layout_script_find_language (hb_face, table[i], script_index, lang_tag, &lang_index); + count = G_N_ELEMENTS (features); + hb_ot_layout_language_get_feature_tags (hb_face, + table[i], + script_index, + lang_index, + n_features, + &count, + features); + n_features += count; + } + + for (j = 0; j < n_features; j++) + { + for (l = priv->feature_items; l; l = l->next) + { + FeatureItem *item = l->data; + if (item->tag != features[j]) + continue; + + gtk_widget_show (item->feat); + gtk_widget_show (gtk_widget_get_parent (item->feat)); + gtk_widget_show (priv->feature_language_combo); + + 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); + } + } + } + + hb_face_destroy (hb_face); + } + + pango_fc_font_unlock_face (PANGO_FC_FONT (pango_font)); + g_object_unref (pango_font); +} + +static void +update_font_features (GtkFontChooserWidget *fontchooser) +{ + GtkFontChooserWidgetPrivate *priv = fontchooser->priv; + GString *s; + GList *l; + + s = g_string_new ("\"kern\" 1, \"curs\" 1, \"lfbd\" 1, \"rfbd\" 1, \"mark\" 1, \"mkmk\" 1, \"mset\" 1, \"ccmp\" 1, \"rlig\" 1, \"rclt\" 1, \"rvrn\" 1"); + + for (l = priv->feature_items; l; l = l->next) + { + FeatureItem *item = l->data; + + if (!gtk_widget_is_sensitive (item->feat)) + continue; + + if (GTK_IS_RADIO_BUTTON (item->feat)) + { + if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (item->feat)) && + strcmp (item->name, "xxxx") != 0) + { + g_string_append (s, ", \""); + g_string_append (s, item->name); + g_string_append (s, "\" 1"); + } + } + else if (GTK_IS_CHECK_BUTTON (item->feat)) + { + if (gtk_check_button_get_inconsistent (GTK_CHECK_BUTTON (item->feat))) + continue; + + g_string_append (s, ", \""); + g_string_append (s, item->name); + if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (item->feat))) + g_string_append (s, "\" 1"); + else + g_string_append (s, " 0"); + } + } + + if (g_strcmp0 (priv->font_features, s->str) != 0) + { + g_free (priv->font_features); + priv->font_features = g_string_free (s, FALSE); + } + else + g_string_free (s, TRUE); + + gtk_font_chooser_widget_update_preview_attributes (fontchooser); +} + +static void +update_language (GtkFontChooserWidget *fontchooser) +{ + GtkFontChooserWidgetPrivate *priv = fontchooser->priv; + GtkTreeIter iter; + + if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (priv->feature_language_combo), &iter)) + { + GtkTreeModel *model; + PangoLanguage *lang; + hb_tag_t lang_tag; + + model = gtk_combo_box_get_model (GTK_COMBO_BOX (priv->feature_language_combo)); + gtk_tree_model_get (model, &iter, + 3, &lang_tag, + -1); + lang = pango_language_from_string (hb_language_to_string (hb_ot_tag_to_language (lang_tag))); + if (priv->font_language != lang) + { + priv->font_language = lang; + } + } + + gtk_font_chooser_widget_update_preview_attributes (fontchooser); +} + +#endif + static void gtk_font_chooser_widget_merge_font_desc (GtkFontChooserWidget *fontchooser, const PangoFontDescription *font_desc, @@ -1397,6 +1940,10 @@ gtk_font_chooser_widget_merge_font_desc (GtkFontChooserWidget *fontchooser } gtk_font_chooser_widget_update_marks (fontchooser); + +#if defined(HAVE_HARFBUZZ) && defined(HAVE_PANGOFT) + gtk_font_chooser_widget_update_font_features (fontchooser); +#endif } gtk_font_chooser_widget_update_preview_attributes (fontchooser); diff --git a/gtk/language-names.c b/gtk/language-names.c new file mode 100644 index 0000000000..2d433cff06 --- /dev/null +++ b/gtk/language-names.c @@ -0,0 +1,233 @@ +#include "config.h" + +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <dirent.h> +#include <locale.h> +#include <langinfo.h> +#include <sys/stat.h> + +#include <glib.h> +#include <glib/gi18n.h> +#include <glib/gstdio.h> +#include <hb-ot.h> + +#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/gtk/language-names.h b/gtk/language-names.h new file mode 100644 index 0000000000..562f7ae2f8 --- /dev/null +++ b/gtk/language-names.h @@ -0,0 +1,13 @@ +#ifndef LANGUAGE_NAMES_H +#define LANGUAGE_NAMES_H + +#include <pango/pango.h> + +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/gtk/meson.build b/gtk/meson.build index d759ae6665..f8d9a966f1 100644 --- a/gtk/meson.build +++ b/gtk/meson.build @@ -5,6 +5,8 @@ subdir('inspector') gtk_public_sources = files([ 'fallback-c89.c', 'fnmatch.c', + 'language-names.c', + 'script-names.c', 'gdkpixbufutils.c', 'gtkaboutdialog.c', 'gtkaccelgroup.c', @@ -824,6 +826,10 @@ gtk_deps = [ graphene_dep, ] +if harfbuzz_dep.found() and pangoft_dep.found() + gtk_deps += [ harfbuzz_dep, ] +endif + if x11_enabled x11_data_prefix = dependency('x11').get_pkgconfig_variable('prefix') diff --git a/gtk/open-type-layout.h b/gtk/open-type-layout.h new file mode 100644 index 0000000000..22521f3818 --- /dev/null +++ b/gtk/open-type-layout.h @@ -0,0 +1,155 @@ +/* Registered OpenType layout tags, see + * https://www.microsoft.com/typography/otspec/featuretags.htm + */ + +typedef struct { + unsigned int tag; + const char *name; +} NamedTag; + +#define MAKE_TAG(a,b,c,d) (unsigned int)(((a) << 24) | ((b) << 16) | ((c) << 8) | (d)) + +static NamedTag open_type_layout_features[] = { + { MAKE_TAG('a','a','l','t'), NC_("OpenType layout", "Access All Alternates") }, + { MAKE_TAG('a','b','v','f'), NC_("OpenType layout", "Above-base Forms") }, + { MAKE_TAG('a','b','v','m'), NC_("OpenType layout", "Above-base Mark Positioning") }, + { MAKE_TAG('a','b','v','s'), NC_("OpenType layout", "Above-base Substitutions") }, + { MAKE_TAG('a','f','r','c'), NC_("OpenType layout", "Alternative Fractions") }, + { MAKE_TAG('a','k','h','n'), NC_("OpenType layout", "Akhands") }, + { MAKE_TAG('b','l','w','f'), NC_("OpenType layout", "Below-base Forms") }, + { MAKE_TAG('b','l','w','m'), NC_("OpenType layout", "Below-base Mark Positioning") }, + { MAKE_TAG('b','l','w','s'), NC_("OpenType layout", "Below-base Substitutions") }, + { MAKE_TAG('c','a','l','t'), NC_("OpenType layout", "Contextual Alternates") }, + { MAKE_TAG('c','a','s','e'), NC_("OpenType layout", "Case-Sensitive Forms") }, + { MAKE_TAG('c','c','m','p'), NC_("OpenType layout", "Glyph Composition / Decomposition") }, + { MAKE_TAG('c','f','a','r'), NC_("OpenType layout", "Conjunct Form After Ro") }, + { MAKE_TAG('c','j','c','t'), NC_("OpenType layout", "Conjunct Forms") }, + { MAKE_TAG('c','l','i','g'), NC_("OpenType layout", "Contextual Ligatures") }, + { MAKE_TAG('c','p','c','t'), NC_("OpenType layout", "Centered CJK Punctuation") }, + { MAKE_TAG('c','p','s','p'), NC_("OpenType layout", "Capital Spacing") }, + { MAKE_TAG('c','s','w','h'), NC_("OpenType layout", "Contextual Swash") }, + { MAKE_TAG('c','u','r','s'), NC_("OpenType layout", "Cursive Positioning") }, + { MAKE_TAG('c','2','p','c'), NC_("OpenType layout", "Petite Capitals From Capitals") }, + { MAKE_TAG('c','2','s','c'), NC_("OpenType layout", "Small Capitals From Capitals") }, + { MAKE_TAG('d','i','s','t'), NC_("OpenType layout", "Distances") }, + { MAKE_TAG('d','l','i','g'), NC_("OpenType layout", "Discretionary Ligatures") }, + { MAKE_TAG('d','n','o','m'), NC_("OpenType layout", "Denominators") }, + { MAKE_TAG('d','t','l','s'), NC_("OpenType layout", "Dotless Forms") }, + { MAKE_TAG('e','x','p','t'), NC_("OpenType layout", "Expert Forms") }, + { MAKE_TAG('f','a','l','t'), NC_("OpenType layout", "Final Glyph on Line Alternates") }, + { MAKE_TAG('f','i','n','2'), NC_("OpenType layout", "Terminal Forms #2") }, + { MAKE_TAG('f','i','n','3'), NC_("OpenType layout", "Terminal Forms #3") }, + { MAKE_TAG('f','i','n','a'), NC_("OpenType layout", "Terminal Forms") }, + { MAKE_TAG('f','l','a','c'), NC_("OpenType layout", "Flattened accent forms") }, + { MAKE_TAG('f','r','a','c'), NC_("OpenType layout", "Fractions") }, + { MAKE_TAG('f','w','i','d'), NC_("OpenType layout", "Full Widths") }, + { MAKE_TAG('h','a','l','f'), NC_("OpenType layout", "Half Forms") }, + { MAKE_TAG('h','a','l','n'), NC_("OpenType layout", "Halant Forms") }, + { MAKE_TAG('h','a','l','t'), NC_("OpenType layout", "Alternate Half Widths") }, + { MAKE_TAG('h','i','s','t'), NC_("OpenType layout", "Historical Forms") }, + { MAKE_TAG('h','k','n','a'), NC_("OpenType layout", "Horizontal Kana Alternates") }, + { MAKE_TAG('h','l','i','g'), NC_("OpenType layout", "Historical Ligatures") }, + { MAKE_TAG('h','n','g','l'), NC_("OpenType layout", "Hangul") }, + { MAKE_TAG('h','o','j','o'), NC_("OpenType layout", "Hojo Kanji Forms") }, + { MAKE_TAG('h','w','i','d'), NC_("OpenType layout", "Half Widths") }, + { MAKE_TAG('i','n','i','t'), NC_("OpenType layout", "Initial Forms") }, + { MAKE_TAG('i','s','o','l'), NC_("OpenType layout", "Isolated Forms") }, + { MAKE_TAG('i','t','a','l'), NC_("OpenType layout", "Italics") }, + { MAKE_TAG('j','a','l','t'), NC_("OpenType layout", "Justification Alternates") }, + { MAKE_TAG('j','p','7','8'), NC_("OpenType layout", "JIS78 Forms") }, + { MAKE_TAG('j','p','8','3'), NC_("OpenType layout", "JIS83 Forms") }, + { MAKE_TAG('j','p','9','0'), NC_("OpenType layout", "JIS90 Forms") }, + { MAKE_TAG('j','p','0','4'), NC_("OpenType layout", "JIS2004 Forms") }, + { MAKE_TAG('k','e','r','n'), NC_("OpenType layout", "Kerning") }, + { MAKE_TAG('l','f','b','d'), NC_("OpenType layout", "Left Bounds") }, + { MAKE_TAG('l','i','g','a'), NC_("OpenType layout", "Standard Ligatures") }, + { MAKE_TAG('l','j','m','o'), NC_("OpenType layout", "Leading Jamo Forms") }, + { MAKE_TAG('l','n','u','m'), NC_("OpenType layout", "Lining Figures") }, + { MAKE_TAG('l','o','c','l'), NC_("OpenType layout", "Localized Forms") }, + { MAKE_TAG('l','t','r','a'), NC_("OpenType layout", "Left-to-right alternates") }, + { MAKE_TAG('l','t','r','m'), NC_("OpenType layout", "Left-to-right mirrored forms") }, + { MAKE_TAG('m','a','r','k'), NC_("OpenType layout", "Mark Positioning") }, + { MAKE_TAG('m','e','d','2'), NC_("OpenType layout", "Medial Forms #2") }, + { MAKE_TAG('m','e','d','i'), NC_("OpenType layout", "Medial Forms") }, + { MAKE_TAG('m','g','r','k'), NC_("OpenType layout", "Mathematical Greek") }, + { MAKE_TAG('m','k','m','k'), NC_("OpenType layout", "Mark to Mark Positioning") }, + { MAKE_TAG('m','s','e','t'), NC_("OpenType layout", "Mark Positioning via Substitution") }, + { MAKE_TAG('n','a','l','t'), NC_("OpenType layout", "Alternate Annotation Forms") }, + { MAKE_TAG('n','l','c','k'), NC_("OpenType layout", "NLC Kanji Forms") }, + { MAKE_TAG('n','u','k','t'), NC_("OpenType layout", "Nukta Forms") }, + { MAKE_TAG('n','u','m','r'), NC_("OpenType layout", "Numerators") }, + { MAKE_TAG('o','n','u','m'), NC_("OpenType layout", "Oldstyle Figures") }, + { MAKE_TAG('o','p','b','d'), NC_("OpenType layout", "Optical Bounds") }, + { MAKE_TAG('o','r','d','n'), NC_("OpenType layout", "Ordinals") }, + { MAKE_TAG('o','r','n','m'), NC_("OpenType layout", "Ornaments") }, + { MAKE_TAG('p','a','l','t'), NC_("OpenType layout", "Proportional Alternate Widths") }, + { MAKE_TAG('p','c','a','p'), NC_("OpenType layout", "Petite Capitals") }, + { MAKE_TAG('p','k','n','a'), NC_("OpenType layout", "Proportional Kana") }, + { MAKE_TAG('p','n','u','m'), NC_("OpenType layout", "Proportional Figures") }, + { MAKE_TAG('p','r','e','f'), NC_("OpenType layout", "Pre-Base Forms") }, + { MAKE_TAG('p','r','e','s'), NC_("OpenType layout", "Pre-base Substitutions") }, + { MAKE_TAG('p','s','t','f'), NC_("OpenType layout", "Post-base Forms") }, + { MAKE_TAG('p','s','t','s'), NC_("OpenType layout", "Post-base Substitutions") }, + { MAKE_TAG('p','w','i','d'), NC_("OpenType layout", "Proportional Widths") }, + { MAKE_TAG('q','w','i','d'), NC_("OpenType layout", "Quarter Widths") }, + { MAKE_TAG('r','a','n','d'), NC_("OpenType layout", "Randomize") }, + { MAKE_TAG('r','c','l','t'), NC_("OpenType layout", "Required Contextual Alternates") }, + { MAKE_TAG('r','k','r','f'), NC_("OpenType layout", "Rakar Forms") }, + { MAKE_TAG('r','l','i','g'), NC_("OpenType layout", "Required Ligatures") }, + { MAKE_TAG('r','p','h','f'), NC_("OpenType layout", "Reph Forms") }, + { MAKE_TAG('r','t','b','d'), NC_("OpenType layout", "Right Bounds") }, + { MAKE_TAG('r','t','l','a'), NC_("OpenType layout", "Right-to-left alternates") }, + { MAKE_TAG('r','t','l','m'), NC_("OpenType layout", "Right-to-left mirrored forms") }, + { MAKE_TAG('r','u','b','y'), NC_("OpenType layout", "Ruby Notation Forms") }, + { MAKE_TAG('r','v','r','n'), NC_("OpenType layout", "Required Variation Alternates") }, + { MAKE_TAG('s','a','l','t'), NC_("OpenType layout", "Stylistic Alternates") }, + { MAKE_TAG('s','i','n','f'), NC_("OpenType layout", "Scientific Inferiors") }, + { MAKE_TAG('s','i','z','e'), NC_("OpenType layout", "Optical size") }, + { MAKE_TAG('s','m','c','p'), NC_("OpenType layout", "Small Capitals") }, + { MAKE_TAG('s','m','p','l'), NC_("OpenType layout", "Simplified Forms") }, + { MAKE_TAG('s','s','0','1'), NC_("OpenType layout", "Stylistic Set 1") }, + { MAKE_TAG('s','s','0','2'), NC_("OpenType layout", "Stylistic Set 2") }, + { MAKE_TAG('s','s','0','3'), NC_("OpenType layout", "Stylistic Set 3") }, + { MAKE_TAG('s','s','0','4'), NC_("OpenType layout", "Stylistic Set 4") }, + { MAKE_TAG('s','s','0','5'), NC_("OpenType layout", "Stylistic Set 5") }, + { MAKE_TAG('s','s','0','6'), NC_("OpenType layout", "Stylistic Set 6") }, + { MAKE_TAG('s','s','0','7'), NC_("OpenType layout", "Stylistic Set 7") }, + { MAKE_TAG('s','s','0','8'), NC_("OpenType layout", "Stylistic Set 8") }, + { MAKE_TAG('s','s','0','9'), NC_("OpenType layout", "Stylistic Set 9") }, + { MAKE_TAG('s','s','1','0'), NC_("OpenType layout", "Stylistic Set 10") }, + { MAKE_TAG('s','s','1','1'), NC_("OpenType layout", "Stylistic Set 11") }, + { MAKE_TAG('s','s','1','2'), NC_("OpenType layout", "Stylistic Set 12") }, + { MAKE_TAG('s','s','1','3'), NC_("OpenType layout", "Stylistic Set 13") }, + { MAKE_TAG('s','s','1','4'), NC_("OpenType layout", "Stylistic Set 14") }, + { MAKE_TAG('s','s','1','5'), NC_("OpenType layout", "Stylistic Set 15") }, + { MAKE_TAG('s','s','1','6'), NC_("OpenType layout", "Stylistic Set 16") }, + { MAKE_TAG('s','s','1','7'), NC_("OpenType layout", "Stylistic Set 17") }, + { MAKE_TAG('s','s','1','8'), NC_("OpenType layout", "Stylistic Set 18") }, + { MAKE_TAG('s','s','1','9'), NC_("OpenType layout", "Stylistic Set 19") }, + { MAKE_TAG('s','s','2','0'), NC_("OpenType layout", "Stylistic Set 20") }, + { MAKE_TAG('s','s','t','y'), NC_("OpenType layout", "Math script style alternates") }, + { MAKE_TAG('s','t','c','h'), NC_("OpenType layout", "Stretching Glyph Decomposition") }, + { MAKE_TAG('s','u','b','s'), NC_("OpenType layout", "Subscript") }, + { MAKE_TAG('s','u','p','s'), NC_("OpenType layout", "Superscript") }, + { MAKE_TAG('s','w','s','h'), NC_("OpenType layout", "Swash") }, + { MAKE_TAG('t','i','t','l'), NC_("OpenType layout", "Titling") }, + { MAKE_TAG('t','j','m','o'), NC_("OpenType layout", "Trailing Jamo Forms") }, + { MAKE_TAG('t','n','a','m'), NC_("OpenType layout", "Traditional Name Forms") }, + { MAKE_TAG('t','n','u','m'), NC_("OpenType layout", "Tabular Figures") }, + { MAKE_TAG('t','r','a','d'), NC_("OpenType layout", "Traditional Forms") }, + { MAKE_TAG('t','w','i','d'), NC_("OpenType layout", "Third Widths") }, + { MAKE_TAG('u','n','i','c'), NC_("OpenType layout", "Unicase") }, + { MAKE_TAG('v','a','l','t'), NC_("OpenType layout", "Alternate Vertical Metrics") }, + { MAKE_TAG('v','a','t','u'), NC_("OpenType layout", "Vattu Variants") }, + { MAKE_TAG('v','e','r','t'), NC_("OpenType layout", "Vertical Writing") }, + { MAKE_TAG('v','h','a','l'), NC_("OpenType layout", "Alternate Vertical Half Metrics") }, + { MAKE_TAG('v','j','m','o'), NC_("OpenType layout", "Vowel Jamo Forms") }, + { MAKE_TAG('v','k','n','a'), NC_("OpenType layout", "Vertical Kana Alternates") }, + { MAKE_TAG('v','k','r','n'), NC_("OpenType layout", "Vertical Kerning") }, + { MAKE_TAG('v','p','a','l'), NC_("OpenType layout", "Proportional Alternate Vertical Metrics") }, + { MAKE_TAG('v','r','t','2'), NC_("OpenType layout", "Vertical Alternates and Rotation") }, + { MAKE_TAG('v','r','t','r'), NC_("OpenType layout", "Vertical Alternates for Rotation") }, + { MAKE_TAG('z','e','r','o'), NC_("OpenType layout", "Slashed Zero") }, +}; + +#undef MAKE_TAG diff --git a/gtk/script-names.c b/gtk/script-names.c new file mode 100644 index 0000000000..5049c34d0b --- /dev/null +++ b/gtk/script-names.c @@ -0,0 +1,184 @@ +#include "config.h" +#include <glib.h> +#include <glib/gi18n-lib.h> +#include <hb-ot.h> + +#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/gtk/script-names.h b/gtk/script-names.h new file mode 100644 index 0000000000..3036c05b56 --- /dev/null +++ b/gtk/script-names.h @@ -0,0 +1,13 @@ +#ifndef SCRIPT_NAMES_H +#define SCRIPT_NAMES_H + +#include <glib.h> + +G_BEGIN_DECLS + +const char * get_script_name (GUnicodeScript script); +const char * get_script_name_for_tag (guint32 tag); + +G_END_DECLS + +#endif diff --git a/gtk/ui/gtkfontchooserwidget.ui b/gtk/ui/gtkfontchooserwidget.ui index f2df859d4a..d17fc2f0f5 100644 --- a/gtk/ui/gtkfontchooserwidget.ui +++ b/gtk/ui/gtkfontchooserwidget.ui @@ -282,6 +282,7 @@ <object class="GtkComboBox" id="feature_language_combo"> <property name="halign">start</property> <property name="margin-top">10</property> + <signal name="changed" handler="update_language" swapped="yes"/> <child> <object class="GtkCellRendererText"/> <attributes> diff --git a/meson.build b/meson.build index 1288caa8e0..29d041599e 100644 --- a/meson.build +++ b/meson.build @@ -346,6 +346,9 @@ else platform_gio_dep = giounix_dep endif +cdata.set('HAVE_HARFBUZZ', harfbuzz_dep.found()) +cdata.set('HAVE_PANGOFT', pangoft_dep.found()) + backend_immodules = [] pc_gdk_extra_libs = [] |