diff --git a/gtk/gtkfontchooserwidget.c b/gtk/gtkfontchooserwidget.c index 44f2858c6d..dfa7b7b231 100644 --- a/gtk/gtkfontchooserwidget.c +++ b/gtk/gtkfontchooserwidget.c @@ -58,15 +58,22 @@ #include "gtkrevealer.h" #include "gtklistbox.h" #include "gtkpopover.h" +#include "gtkradiobutton.h" +#include "gtkcheckbutton.h" #if defined(HAVE_HARFBUZZ) && defined(HAVE_PANGOFT) #include +#include +#include +#include #include #include #include "language-names.h" #include "script-names.h" #endif +#include "open-type-layout.h" + /** * SECTION:gtkfontchooserwidget * @Short_description: A widget for selecting fonts @@ -134,6 +141,9 @@ struct _GtkFontChooserWidgetPrivate GtkWidget *font_grid; + GtkWidget *feature_box; + GtkWidget *feature_language_combo; + PangoFontMap *font_map; PangoFontDescription *font_desc; @@ -152,6 +162,8 @@ struct _GtkFontChooserWidgetPrivate gboolean updating_variations; GtkFontChooserLevel level; + + GList *feature_items; }; /* Keep in line with GtkTreeStore defined in gtkfontchooserwidget.ui */ @@ -204,6 +216,7 @@ static void gtk_font_chooser_widget_set_cell_size (GtkFontChooserWi static void gtk_font_chooser_widget_load_fonts (GtkFontChooserWidget *fontchooser, gboolean force); static void gtk_font_chooser_widget_populate_filters (GtkFontChooserWidget *fontchooser); +static void gtk_font_chooser_widget_populate_features (GtkFontChooserWidget *fontchooser); static gboolean visible_func (GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data); @@ -241,6 +254,8 @@ static void language_filter_activated (GtkFontChooserWidget *fontchooser); static void popover_notify (GObject *object, GParamSpec *pspec, GtkFontChooserWidget *fontchooser); +static void collect_font_features (GtkFontChooserWidget *fontchooser, + PangoAttrList *attrs); static void gtk_font_chooser_widget_iface_init (GtkFontChooserIface *iface); @@ -630,6 +645,8 @@ gtk_font_chooser_widget_update_preview_attributes (GtkFontChooserWidget *fontcho /* Force current font */ pango_attr_list_insert (attrs, pango_attr_font_desc_new (priv->font_desc)); + collect_font_features (fontchooser, attrs); + gtk_entry_set_attributes (GTK_ENTRY (priv->preview), attrs); pango_attr_list_unref (attrs); } @@ -740,6 +757,8 @@ gtk_font_chooser_widget_class_init (GtkFontChooserWidgetClass *klass) gtk_widget_class_bind_template_child_private (widget_class, GtkFontChooserWidget, size_slider); gtk_widget_class_bind_template_child_private (widget_class, GtkFontChooserWidget, grid); gtk_widget_class_bind_template_child_private (widget_class, GtkFontChooserWidget, font_grid); + 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_child_private (widget_class, GtkFontChooserWidget, filter_popover); gtk_widget_class_bind_template_child_private (widget_class, GtkFontChooserWidget, script_filter_revealer); @@ -873,6 +892,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_filters (fontchooser); + gtk_font_chooser_widget_populate_features (fontchooser); gtk_font_chooser_widget_set_cell_size (fontchooser); gtk_font_chooser_widget_take_font_desc (fontchooser, NULL); } @@ -1081,7 +1101,6 @@ script_filter_func (GtkListBoxRow *row, { GtkFontChooserWidget *fontchooser = data; GtkFontChooserWidgetPrivate *priv = fontchooser->priv; - PangoScript script; const char *term; if (priv->script_term == NULL) @@ -1755,6 +1774,8 @@ gtk_font_chooser_widget_finalize (GObject *object) g_free (priv->script_term); g_free (priv->language_term); + g_list_free_full (priv->feature_items, g_free); + G_OBJECT_CLASS (gtk_font_chooser_widget_parent_class)->finalize (object); } @@ -2134,6 +2155,499 @@ gtk_font_chooser_widget_update_font_variations (GtkFontChooserWidget *fontchoose pango_fc_font_unlock_face (PANGO_FC_FONT (pango_font)); g_object_unref (pango_font); } + +static void +add_script (GtkListStore *store, + guint script_index, + hb_tag_t script, + guint lang_index, + hb_tag_t lang) +{ + char scriptbuf[5]; + char langbuf[5]; + const char *scriptname; + const char *langname; + char *name; + + hb_tag_to_string (script, scriptbuf); + scriptbuf[4] = 0; + + hb_tag_to_string (lang, langbuf); + langbuf[4] = 0; + + if (script == HB_OT_TAG_DEFAULT_SCRIPT) + scriptname = "Default"; + else if (script == HB_TAG ('m','a','t','h')) + scriptname = "Math"; + else + scriptname = get_script_name_for_tag (script); + + if (!scriptname) + scriptname = scriptbuf; + + langname = get_language_name_for_tag (lang); + if (!langname) + langname = langbuf; + + if (lang == HB_OT_TAG_DEFAULT_LANGUAGE) + name = g_strdup_printf ("%s Script", scriptname); + else + name = g_strdup (langname); + + gtk_list_store_insert_with_values (store, NULL, -1, + 0, name, + 1, script_index, + 2, lang_index, + 3, lang, + -1); + g_free (name); +} + +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); + g_print ("found %d scripts in %s\n", count, i == 0 ? "GSUB" : "GPOS"); + 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); + g_print ("found %d languages for script %d in %s\n", count, j, i == 0 ? "GSUB" : "GPOS"); + n_languages += count; + } + + 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); + + 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); + } + 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 (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; + + 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 (gtk_font_chooser_widget_update_preview_attributes), fontchooser); + g_signal_connect_swapped (feat, "notify::inconsistent", G_CALLBACK (gtk_font_chooser_widget_update_preview_attributes), fontchooser); + 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->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 (gtk_font_chooser_widget_update_preview_attributes), 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); +} + +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; + + 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)); + + 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 +collect_font_features (GtkFontChooserWidget *fontchooser, + PangoAttrList *attrs) +{ + GtkFontChooserWidgetPrivate *priv = fontchooser->priv; + GString *s; + GList *l; + GtkTreeIter iter; + + 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"); + } + } + + pango_attr_list_insert (attrs, pango_attr_font_features_new (s->str)); + g_string_free (s, TRUE); + + if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (priv->feature_language_combo), &iter)) + { + GtkTreeModel *model; + const char *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 = hb_language_to_string (hb_ot_tag_to_language (lang_tag)); + pango_attr_list_insert (attrs, pango_attr_language_new (pango_language_from_string (lang))); + } +} + #endif static void @@ -2182,6 +2696,7 @@ gtk_font_chooser_widget_merge_font_desc (GtkFontChooserWidget *fontchooser gtk_font_chooser_widget_update_preview_attributes (fontchooser); #if defined(HAVE_HARFBUZZ) && defined(HAVE_PANGOFT) + gtk_font_chooser_widget_update_font_features (fontchooser); gtk_font_chooser_widget_update_font_variations (fontchooser); #endif diff --git a/gtk/meson.build b/gtk/meson.build index 0a95643f76..d04c35843f 100644 --- a/gtk/meson.build +++ b/gtk/meson.build @@ -827,6 +827,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/ui/gtkfontchooserwidget.ui b/gtk/ui/gtkfontchooserwidget.ui index 2182cc7843..34ea6ca8d6 100644 --- a/gtk/ui/gtkfontchooserwidget.ui +++ b/gtk/ui/gtkfontchooserwidget.ui @@ -214,6 +214,30 @@ 1 + + + Font Features + + + vertical + + + + + + 0 + + + + + + + + + 0 + 2 + +