[pango/small-caps: 2/6] itemize: Implement emulated Small Caps
- From: Matthias Clasen <matthiasc src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [pango/small-caps: 2/6] itemize: Implement emulated Small Caps
- Date: Sun, 7 Nov 2021 14:22:44 +0000 (UTC)
commit aa4a6ad5a8a9745dca0b1e979a6b8a44dc01dc19
Author: Matthias Clasen <mclasen redhat com>
Date: Sat Nov 6 23:43:35 2021 -0400
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.
pango/itemize.c | 173 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 172 insertions(+), 1 deletion(-)
---
diff --git a/pango/itemize.c b/pango/itemize.c
index 539cdc66..b67077a0 100644
--- a/pango/itemize.c
+++ b/pango/itemize.c
@@ -1183,10 +1183,179 @@ 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;
+
+ 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;
+ while (p < end && g_unichar_islower (g_utf8_get_char (p)))
+ p = g_utf8_next_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;
+ while (p < end && !g_unichar_islower (g_utf8_get_char (p)))
+ p = g_utf8_next_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);
@@ -1202,6 +1371,8 @@ post_process_items (PangoContext *context,
}
}
+ handle_small_caps (text, items);
+
/* apply font-scale */
apply_font_scale (context, items);
@@ -1236,7 +1407,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);
}
/**
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]