diff options
author | Matthias Clasen <mclasen@redhat.com> | 2021-11-06 23:43:35 -0400 |
---|---|---|
committer | Matthias Clasen <mclasen@redhat.com> | 2021-11-07 09:59:07 -0500 |
commit | 0e5bd15d24d31eede8d6f664f5e15b2a508a6169 (patch) | |
tree | 7d077d796bfa99f48148b83d9032d1481c39f84d /pango/itemize.c | |
parent | 346d92cbf4714320bd4b68ab772052cd998d5f03 (diff) | |
download | pango-0e5bd15d24d31eede8d6f664f5e15b2a508a6169.tar.gz |
itemize: Implement emulated Small Caps
When we detect that Small Caps are requested, but not
available via OpenType font features, emulate Small Caps
by splitting the item into lowercase and uppercase runs
and add text transform and font scale attributes to the
lowercase runs to get the effect of Small Caps.
Still to do: resolve conflicts with preexisting text
transform attributes.
Diffstat (limited to 'pango/itemize.c')
-rw-r--r-- | pango/itemize.c | 182 |
1 files changed, 181 insertions, 1 deletions
diff --git a/pango/itemize.c b/pango/itemize.c index 059f24e9..6f99e2bd 100644 --- a/pango/itemize.c +++ b/pango/itemize.c @@ -1193,9 +1193,187 @@ apply_font_scale (PangoContext *context, } /* }}} */ +/* {{{ Handling Small Caps */ + +static gboolean +feature_is_requested (hb_feature_t *features, + guint num_features, + hb_tag_t tag) +{ + for (guint i = 0; i < num_features; i++) + { + if (features[i].tag == tag) + return features[i].value != 0; + } + + return FALSE; +} + + +static gboolean +feature_is_supported (hb_font_t *font, + const PangoAnalysis *analysis, + hb_tag_t table, + hb_tag_t feature) +{ + hb_face_t *face = hb_font_get_face (font); + hb_script_t script; + hb_language_t language; + guint script_count = HB_OT_MAX_TAGS_PER_SCRIPT; + hb_tag_t script_tags[HB_OT_MAX_TAGS_PER_SCRIPT]; + hb_tag_t chosen_script; + guint language_count = HB_OT_MAX_TAGS_PER_LANGUAGE; + hb_tag_t language_tags[HB_OT_MAX_TAGS_PER_LANGUAGE]; + guint script_index, language_index; + guint feature_index; + + script = g_unicode_script_to_iso15924 (analysis->script); + language = hb_language_from_string (pango_language_to_string (analysis->language), -1); + + hb_ot_tags_from_script_and_language (script, language, + &script_count, script_tags, + &language_count, language_tags); + hb_ot_layout_table_select_script (face, table, + script_count, script_tags, + &script_index, + &chosen_script); + hb_ot_layout_script_select_language (face, table, + script_index, + language_count, language_tags, + &language_index); + + return hb_ot_layout_language_find_feature (face, table, + script_index, language_index, + feature, + &feature_index); +} + +static gboolean +small_caps_is_requested (PangoItem *item) +{ + hb_feature_t features[32]; + unsigned int num_features = 0; + + pango_analysis_collect_features (&item->analysis, + features, G_N_ELEMENTS (features), + &num_features); + + return feature_is_requested (features, num_features, HB_TAG ('s', 'm', 'c', 'p')); +} + +static gboolean +small_caps_is_supported (PangoItem *item) +{ + hb_font_t *font = pango_font_get_hb_font (item->analysis.font); + + return feature_is_supported (font, &item->analysis, HB_OT_TAG_GSUB, HB_TAG ('s', 'm', 'c', 'p')); +} + +static void +split_item_for_small_caps (const char *text, + GList *list_item) +{ + PangoItem *item = list_item->data; + const char *start, *end; + const char *p, *p0; + gunichar wc; + + start = text + item->offset; + end = start + item->length; + + char *s = g_strndup (start, end - start); + g_free (s); + + p = start; + while (p < end) + { + p0 = p; + wc = g_utf8_get_char (p); + while (p < end && (g_unichar_islower (wc) || consider_as_space (wc))) + { + p = g_utf8_next_char (p); + wc = g_utf8_get_char (p); + } + + if (p0 < p) + { + PangoItem *new_item; + PangoAttribute *attr; + + /* p0 .. p is a lowercase segment */ + if (p < end) + { + new_item = pango_item_split (item, p - p0, g_utf8_strlen (p, p - p0)); + list_item->data = new_item; + list_item = g_list_insert_before (list_item, list_item->next, item); + list_item = list_item->next; + } + else + { + new_item = item; + } + + attr = pango_attr_text_transform_new (PANGO_TEXT_TRANSFORM_UPPERCASE); + attr->start_index = new_item->offset; + attr->end_index = new_item->offset + new_item->length; + new_item->analysis.extra_attrs = g_slist_prepend (new_item->analysis.extra_attrs, attr); + + attr = pango_attr_font_scale_new (PANGO_FONT_SCALE_SMALL_CAPS); + attr->start_index = new_item->offset; + attr->end_index = new_item->offset + new_item->length; + new_item->analysis.extra_attrs = g_slist_prepend (new_item->analysis.extra_attrs, attr); + } + + p0 = p; + wc = g_utf8_get_char (p); + while (p < end && (!g_unichar_islower (wc) || consider_as_space (wc))) + { + p = g_utf8_next_char (p); + wc = g_utf8_get_char (p); + } + + if (p0 < p && p < end) + { + PangoItem *new_item; + + /* p0 .. p is a uppercase segment */ + new_item = pango_item_split (item, p - p0, g_utf8_strlen (p, p - p0)); + list_item->data = new_item; + list_item = g_list_insert_before (list_item, list_item->next, item); + list_item = list_item->next; + } + } +} + +static void +handle_small_caps_for_item (const char *text, + GList *l) +{ + PangoItem *item = l->data; + + if (small_caps_is_requested (item) && + !small_caps_is_supported (item)) + split_item_for_small_caps (text, l); +} + +static void +handle_small_caps (const char *text, + GList *items) +{ + GList *next; + + for (GList *l = items; l; l = next) + { + next = l->next; + handle_small_caps_for_item (text, l); + } +} + +/* }}} */ static GList * post_process_items (PangoContext *context, + const char *text, GList *items) { items = g_list_reverse (items); @@ -1211,6 +1389,8 @@ post_process_items (PangoContext *context, } } + handle_small_caps (text, items); + /* apply font-scale */ apply_font_scale (context, items); @@ -1245,7 +1425,7 @@ pango_itemize_with_font (PangoContext *context, itemize_state_finish (&state); - return post_process_items (context, state.result); + return post_process_items (context, text, state.result); } /** |