From ee4e7886a6fbd00e777019a38adb8d2ace1980e4 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sat, 6 Nov 2021 23:43:07 -0400 Subject: itemize: Handle PANGO_FONT_SCALE_SMALL_CAPS This is not used by anything yet. --- pango/itemize.c | 56 +++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 39 insertions(+), 17 deletions(-) (limited to 'pango/itemize.c') diff --git a/pango/itemize.c b/pango/itemize.c index bd339633..66bf0e8d 100644 --- a/pango/itemize.c +++ b/pango/itemize.c @@ -1024,6 +1024,8 @@ itemize_state_finish (ItemizeState *state) /* }}} */ /* {{{ Post-processing */ + /* {{{ Handling font scale */ + typedef struct { PangoAttribute *attr; double scale; @@ -1048,43 +1050,61 @@ collect_font_scale (PangoContext *context, if (attr->start_index == item->offset) { ScaleItem *entry; - hb_font_t *hb_font; int y_scale; hb_position_t y_size; + hb_position_t cap_height; + hb_position_t x_height; entry = g_new (ScaleItem, 1); entry->attr = attr; *stack = g_list_prepend (*stack, entry); - if (prev) - { - hb_font = pango_font_get_hb_font (prev->analysis.font); - hb_font_get_scale (hb_font, NULL, &y_scale); - } - else - hb_font = NULL; - switch (((PangoAttrInt *)attr)->value) { case PANGO_FONT_SCALE_NONE: break; case PANGO_FONT_SCALE_SUPERSCRIPT: - if (hb_font && - hb_ot_metrics_get_position (hb_font, + if (prev && + hb_ot_metrics_get_position (pango_font_get_hb_font (prev->analysis.font), HB_OT_METRICS_TAG_SUPERSCRIPT_EM_Y_SIZE, &y_size)) - entry->scale = y_size / (double) y_scale; + { + hb_font_get_scale (pango_font_get_hb_font (prev->analysis.font), NULL, &y_scale); + entry->scale = y_size / (double) y_scale; + } else - entry->scale = 1 / 1.2; + { + entry->scale = 1 / 1.2; + } break; case PANGO_FONT_SCALE_SUBSCRIPT: - if (hb_font && - hb_ot_metrics_get_position (hb_font, + if (prev && + hb_ot_metrics_get_position (pango_font_get_hb_font (prev->analysis.font), HB_OT_METRICS_TAG_SUBSCRIPT_EM_Y_SIZE, &y_size)) - entry->scale = y_size / (double) y_scale; + { + hb_font_get_scale (pango_font_get_hb_font (prev->analysis.font), NULL, &y_scale); + entry->scale = y_size / (double) y_scale; + } else - entry->scale = 1 / 1.2; + { + entry->scale = 1 / 1.2; + } + break; + case PANGO_FONT_SCALE_SMALL_CAPS: + if (hb_ot_metrics_get_position (pango_font_get_hb_font (item->analysis.font), + HB_OT_METRICS_TAG_CAP_HEIGHT, + &cap_height) && + hb_ot_metrics_get_position (pango_font_get_hb_font (item->analysis.font), + HB_OT_METRICS_TAG_X_HEIGHT, + &x_height)) + { + entry->scale = x_height / (double) cap_height; + } + else + { + entry->scale = 0.8; + } break; default: g_assert_not_reached (); @@ -1167,6 +1187,8 @@ apply_font_scale (PangoContext *context, } } +/* }}} */ + static GList * post_process_items (PangoContext *context, GList *items) -- cgit v1.2.1 From 346d92cbf4714320bd4b68ab772052cd998d5f03 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sun, 7 Nov 2021 09:53:18 -0500 Subject: itemize: Break out a helper Factor out the 'consider as space' part from the itemization code, so we can reuse it when breaking items into upper- and lowercase runs. --- pango/itemize.c | 59 +++++++++++++++++++++++++++++++-------------------------- 1 file changed, 32 insertions(+), 27 deletions(-) (limited to 'pango/itemize.c') diff --git a/pango/itemize.c b/pango/itemize.c index 66bf0e8d..059f24e9 100644 --- a/pango/itemize.c +++ b/pango/itemize.c @@ -902,6 +902,36 @@ itemize_state_update_for_new_run (ItemizeState *state) } } +/* We don't want space characters to affect font selection; in general, +* it's always wrong to select a font just to render a space. +* We assume that all fonts have the ASCII space, and for other space +* characters if they don't, HarfBuzz will compatibility-decompose them +* to ASCII space... +* See bugs #355987 and #701652. +* +* We don't want to change fonts just for variation selectors. +* See bug #781123. +* +* Finally, don't change fonts for line or paragraph separators. +* +* Note that we want spaces to use the 'better' font, comparing +* the font that is used before and after the space. This is handled +* in itemize_state_add_character(). +*/ +static gboolean +consider_as_space (gunichar wc) +{ + GUnicodeType type = g_unichar_type (wc); + return type == G_UNICODE_CONTROL || + type == G_UNICODE_FORMAT || + type == G_UNICODE_SURROGATE || + type == G_UNICODE_LINE_SEPARATOR || + type == G_UNICODE_PARAGRAPH_SEPARATOR || + (type == G_UNICODE_SPACE_SEPARATOR && wc != 0x1680u /* OGHAM SPACE MARK */) || + (wc >= 0xfe00u && wc <= 0xfe0fu) || + (wc >= 0xe0100u && wc <= 0xe01efu); +} + static void itemize_state_process_run (ItemizeState *state) { @@ -926,33 +956,8 @@ itemize_state_process_run (ItemizeState *state) gboolean is_forced_break = (wc == '\t' || wc == LINE_SEPARATOR); PangoFont *font; int font_position; - GUnicodeType type; - - /* We don't want space characters to affect font selection; in general, - * it's always wrong to select a font just to render a space. - * We assume that all fonts have the ASCII space, and for other space - * characters if they don't, HarfBuzz will compatibility-decompose them - * to ASCII space... - * See bugs #355987 and #701652. - * - * We don't want to change fonts just for variation selectors. - * See bug #781123. - * - * Finally, don't change fonts for line or paragraph separators. - * - * Note that we want spaces to use the 'better' font, comparing - * the font that is used before and after the space. This is handled - * in itemize_state_add_character(). - */ - type = g_unichar_type (wc); - if (G_UNLIKELY (type == G_UNICODE_CONTROL || - type == G_UNICODE_FORMAT || - type == G_UNICODE_SURROGATE || - type == G_UNICODE_LINE_SEPARATOR || - type == G_UNICODE_PARAGRAPH_SEPARATOR || - (type == G_UNICODE_SPACE_SEPARATOR && wc != 0x1680u /* OGHAM SPACE MARK */) || - (wc >= 0xfe00u && wc <= 0xfe0fu) || - (wc >= 0xe0100u && wc <= 0xe01efu))) + + if (consider_as_space (wc)) { font = NULL; font_position = 0xffff; -- cgit v1.2.1 From 0e5bd15d24d31eede8d6f664f5e15b2a508a6169 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sat, 6 Nov 2021 23:43:35 -0400 Subject: 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 | 182 +++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 181 insertions(+), 1 deletion(-) (limited to 'pango/itemize.c') diff --git a/pango/itemize.c b/pango/itemize.c index 059f24e9..6f99e2bd 100644 --- a/pango/itemize.c +++ b/pango/itemize.c @@ -1192,10 +1192,188 @@ 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); } /** -- cgit v1.2.1 From 0c25f62407f0a73274e745bb115a553c999618fc Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sat, 6 Nov 2021 23:53:51 -0400 Subject: itemize: Emulate other casing variants When we detect that one of the other casing variants is requested, but not available via OpenType font features, emulate it by splitting the item into lowercase and uppercase runs and apply a suitable font scale and text transform to the runs to get the desired effect. Still to do: resolve conflics with preexisting text transform attributes. --- pango/itemize.c | 197 +++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 130 insertions(+), 67 deletions(-) (limited to 'pango/itemize.c') diff --git a/pango/itemize.c b/pango/itemize.c index 6f99e2bd..0c00b9b7 100644 --- a/pango/itemize.c +++ b/pango/itemize.c @@ -1193,29 +1193,14 @@ apply_font_scale (PangoContext *context, } /* }}} */ -/* {{{ Handling Small Caps */ +/* {{{ Handling Casing variants */ 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) +all_features_supported (PangoItem *item, + hb_tag_t *features, + guint n_features) { + hb_font_t *font = pango_font_get_hb_font (item->analysis.font); hb_face_t *face = hb_font_get_face (font); hb_script_t script; hb_language_t language; @@ -1225,65 +1210,120 @@ feature_is_supported (hb_font_t *font, 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; + guint index; - script = g_unicode_script_to_iso15924 (analysis->script); - language = hb_language_from_string (pango_language_to_string (analysis->language), -1); + script = g_unicode_script_to_iso15924 (item->analysis.script); + language = hb_language_from_string (pango_language_to_string (item->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, + hb_ot_layout_table_select_script (face, HB_OT_TAG_GSUB, script_count, script_tags, &script_index, &chosen_script); - hb_ot_layout_script_select_language (face, table, + hb_ot_layout_script_select_language (face, HB_OT_TAG_GSUB, script_index, language_count, language_tags, &language_index); - return hb_ot_layout_language_find_feature (face, table, - script_index, language_index, - feature, - &feature_index); + for (int i = 0; i < n_features; i++) + { + if (!hb_ot_layout_language_find_feature (face, HB_OT_TAG_GSUB, + script_index, language_index, + features[i], + &index)) + return FALSE; + } + + return TRUE; } static gboolean -small_caps_is_requested (PangoItem *item) +variant_supported (PangoItem *item, + PangoVariant variant) { - hb_feature_t features[32]; - unsigned int num_features = 0; + hb_tag_t features[2]; + guint num_features = 0; - pango_analysis_collect_features (&item->analysis, - features, G_N_ELEMENTS (features), - &num_features); + switch (variant) + { + case PANGO_VARIANT_NORMAL: + case PANGO_VARIANT_TITLE_CAPS: + return TRUE; + case PANGO_VARIANT_SMALL_CAPS: + features[num_features++] = HB_TAG ('s', 'm', 'c', 'p'); + break; + case PANGO_VARIANT_ALL_SMALL_CAPS: + features[num_features++] = HB_TAG ('s', 'm', 'c', 'p'); + features[num_features++] = HB_TAG ('c', '2', 's', 'c'); + break; + case PANGO_VARIANT_PETITE_CAPS: + features[num_features++] = HB_TAG ('p', 'c', 'a', 'p'); + break; + case PANGO_VARIANT_ALL_PETITE_CAPS: + features[num_features++] = HB_TAG ('p', 'c', 'a', 'p'); + features[num_features++] = HB_TAG ('c', '2', 'p', 'c'); + break; + case PANGO_VARIANT_UNICASE: + features[num_features++] = HB_TAG ('u', 'n', 'i', 'c'); + break; + default: + g_assert_not_reached (); + } - return feature_is_requested (features, num_features, HB_TAG ('s', 'm', 'c', 'p')); + return all_features_supported (item, features, num_features); } -static gboolean -small_caps_is_supported (PangoItem *item) +static PangoVariant +get_font_variant (PangoItem *item) { - hb_font_t *font = pango_font_get_hb_font (item->analysis.font); + PangoFontDescription *desc; + PangoVariant variant; + + desc = pango_font_describe (item->analysis.font); + variant = pango_font_description_get_variant (desc); + pango_font_description_free (desc); - return feature_is_supported (font, &item->analysis, HB_OT_TAG_GSUB, HB_TAG ('s', 'm', 'c', 'p')); + return variant; } static void -split_item_for_small_caps (const char *text, - GList *list_item) +split_item_for_variant (const char *text, + GList *list_item, + PangoVariant variant) { PangoItem *item = list_item->data; const char *start, *end; const char *p, *p0; gunichar wc; + PangoTextTransform transform = PANGO_TEXT_TRANSFORM_NONE; + PangoFontScale lowercase_scale = PANGO_FONT_SCALE_NONE; + PangoFontScale uppercase_scale = PANGO_FONT_SCALE_NONE; + + switch (variant) + { + case PANGO_VARIANT_ALL_SMALL_CAPS: + case PANGO_VARIANT_ALL_PETITE_CAPS: + uppercase_scale = PANGO_FONT_SCALE_SMALL_CAPS; + G_GNUC_FALLTHROUGH; + case PANGO_VARIANT_SMALL_CAPS: + case PANGO_VARIANT_PETITE_CAPS: + transform = PANGO_TEXT_TRANSFORM_UPPERCASE; + lowercase_scale = PANGO_FONT_SCALE_SMALL_CAPS; + break; + case PANGO_VARIANT_UNICASE: + uppercase_scale = PANGO_FONT_SCALE_SMALL_CAPS; + break; + case PANGO_VARIANT_NORMAL: + case PANGO_VARIANT_TITLE_CAPS: + default: + g_assert_not_reached (); + } start = text + item->offset; end = start + item->length; - char *s = g_strndup (start, end - start); - g_free (s); - p = start; while (p < end) { @@ -1313,15 +1353,21 @@ split_item_for_small_caps (const char *text, 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); + if (transform != PANGO_TEXT_TRANSFORM_NONE) + { + attr = pango_attr_text_transform_new (transform); + 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); + if (lowercase_scale != PANGO_FONT_SCALE_NONE) + { + attr = pango_attr_font_scale_new (lowercase_scale); + 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; @@ -1332,40 +1378,57 @@ split_item_for_small_caps (const char *text, wc = g_utf8_get_char (p); } - if (p0 < p && p < end) + if (p0 < p) { PangoItem *new_item; + PangoAttribute *attr; /* 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; + 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; + } + + if (uppercase_scale != PANGO_FONT_SCALE_NONE) + { + attr = pango_attr_font_scale_new (uppercase_scale); + 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); + } } } } static void -handle_small_caps_for_item (const char *text, - GList *l) +handle_variants_for_item (const char *text, + GList *l) { PangoItem *item = l->data; + PangoVariant variant; - if (small_caps_is_requested (item) && - !small_caps_is_supported (item)) - split_item_for_small_caps (text, l); + variant = get_font_variant (item); + if (!variant_supported (item, variant)) + split_item_for_variant (text, l, variant); } static void -handle_small_caps (const char *text, - GList *items) +handle_variants (const char *text, + GList *items) { GList *next; for (GList *l = items; l; l = next) { next = l->next; - handle_small_caps_for_item (text, l); + handle_variants_for_item (text, l); } } @@ -1389,7 +1452,7 @@ post_process_items (PangoContext *context, } } - handle_small_caps (text, items); + handle_variants (text, items); /* apply font-scale */ apply_font_scale (context, items); -- cgit v1.2.1 From cb6a93fb8ac99b155cf73ed0decd1c8a1230caa3 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sun, 7 Nov 2021 10:56:03 -0500 Subject: Cosmetics: Add the right section Move private API out of the 'Public API' section. --- pango/itemize.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'pango/itemize.c') diff --git a/pango/itemize.c b/pango/itemize.c index 0c00b9b7..e39e6fe2 100644 --- a/pango/itemize.c +++ b/pango/itemize.c @@ -1461,7 +1461,7 @@ post_process_items (PangoContext *context, } /* }}} */ -/* {{{ Public API */ +/* {{{ Private API */ /* Like pango_itemize_with_base_dir, but takes a font description */ GList * @@ -1491,6 +1491,9 @@ pango_itemize_with_font (PangoContext *context, return post_process_items (context, text, state.result); } +/* }}} */ +/* {{{ Public API */ + /** * pango_itemize_with_base_dir: * @context: a structure holding information that affects -- cgit v1.2.1 From b9b92e79da0e55acf0c13968ca699d4dd78fc614 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sun, 7 Nov 2021 11:11:45 -0500 Subject: Reshuffle itemize API Split the post-processing off into a separate function that can take log attrs in addition. This will allow us to handle word starts when dealing with text transforms for emulated Small Caps. So far, this is all private API that is used from PangoLayout. Please file an issue if you want to access Small Caps emulation without PangoLayout. --- pango/itemize.c | 64 +++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 40 insertions(+), 24 deletions(-) (limited to 'pango/itemize.c') diff --git a/pango/itemize.c b/pango/itemize.c index e39e6fe2..b5f544a7 100644 --- a/pango/itemize.c +++ b/pango/itemize.c @@ -1435,26 +1435,30 @@ handle_variants (const char *text, /* }}} */ static GList * -post_process_items (PangoContext *context, - const char *text, - GList *items) +reorder_items (PangoContext *context, + GList *items) { + int char_offset = 0; + items = g_list_reverse (items); - /* Compute the char offset for each item */ - { - int char_offset = 0; - for (GList *l = items; l; l = l->next) - { - PangoItemPrivate *item = l->data; - item->char_offset = char_offset; - char_offset += item->num_chars; - } - } + /* Also cmpute the char offset for each item here */ + for (GList *l = items; l; l = l->next) + { + PangoItemPrivate *item = l->data; + item->char_offset = char_offset; + char_offset += item->num_chars; + } - handle_variants (text, items); + return items; +} - /* apply font-scale */ +static GList * +post_process_items (PangoContext *context, + const char *text, + GList *items) +{ + handle_variants (text, items); apply_font_scale (context, items); return items; @@ -1488,7 +1492,16 @@ pango_itemize_with_font (PangoContext *context, itemize_state_finish (&state); - return post_process_items (context, text, state.result); + return reorder_items (context, state.result); +} + +GList * +pango_itemize_post_process_items (PangoContext *context, + const char *text, + PangoLogAttr *log_attrs, + GList *items) +{ + return post_process_items (context, text, items); } /* }}} */ @@ -1527,15 +1540,19 @@ pango_itemize_with_base_dir (PangoContext *context, PangoAttrList *attrs, PangoAttrIterator *cached_iter) { + GList *items; + g_return_val_if_fail (context != NULL, NULL); g_return_val_if_fail (start_index >= 0, NULL); g_return_val_if_fail (length >= 0, NULL); g_return_val_if_fail (length == 0 || text != NULL, NULL); - return pango_itemize_with_font (context, base_dir, - text, start_index, length, - attrs, cached_iter, - NULL); + items = pango_itemize_with_font (context, base_dir, + text, start_index, length, + attrs, cached_iter, + NULL); + + return pango_itemize_post_process_items (context, text, NULL, items); } /** @@ -1579,10 +1596,9 @@ pango_itemize (PangoContext *context, g_return_val_if_fail (length >= 0, NULL); g_return_val_if_fail (length == 0 || text != NULL, NULL); - return pango_itemize_with_font (context, context->base_dir, - text, start_index, length, - attrs, cached_iter, - NULL); + return pango_itemize_with_base_dir (context, context->base_dir, + text, start_index, length, + attrs, cached_iter); } /* }}} */ -- cgit v1.2.1 From 1790dbf9da8a4249528833ead1af32d232861b64 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sun, 7 Nov 2021 11:40:47 -0500 Subject: itemize: Handle text transforms Take text transforms into account when emulating Small Caps. This requires us to have log attrs to determine word starts. --- pango/itemize.c | 87 ++++++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 70 insertions(+), 17 deletions(-) (limited to 'pango/itemize.c') diff --git a/pango/itemize.c b/pango/itemize.c index b5f544a7..82c737cc 100644 --- a/pango/itemize.c +++ b/pango/itemize.c @@ -1288,10 +1288,34 @@ get_font_variant (PangoItem *item) return variant; } +static PangoTextTransform +find_text_transform (const PangoAnalysis *analysis) +{ + GSList *l; + PangoTextTransform transform = PANGO_TEXT_TRANSFORM_NONE; + + for (l = analysis->extra_attrs; l; l = l->next) + { + PangoAttribute *attr = l->data; + + if (attr->klass->type == PANGO_ATTR_TEXT_TRANSFORM) + transform = (PangoTextTransform) ((PangoAttrInt*)attr)->value; + } + + return transform; +} + +/* Split list_item into upper- and lowercase runs, and + * add font scale and text transform attributes to make + * them be appear according to variant. The log_attrs are + * needed for taking text transforms into account when + * determining the case of characters int he run. + */ static void split_item_for_variant (const char *text, - GList *list_item, - PangoVariant variant) + PangoLogAttr *log_attrs, + PangoVariant variant, + GList *list_item) { PangoItem *item = list_item->data; const char *start, *end; @@ -1300,6 +1324,9 @@ split_item_for_variant (const char *text, PangoTextTransform transform = PANGO_TEXT_TRANSFORM_NONE; PangoFontScale lowercase_scale = PANGO_FONT_SCALE_NONE; PangoFontScale uppercase_scale = PANGO_FONT_SCALE_NONE; + PangoTextTransform item_transform; + gboolean is_word_start; + int offset; switch (variant) { @@ -1321,18 +1348,28 @@ split_item_for_variant (const char *text, g_assert_not_reached (); } + item_transform = find_text_transform (&item->analysis); + start = text + item->offset; end = start + item->length; + offset = ((PangoItemPrivate *)item)->char_offset; p = start; while (p < end) { p0 = p; wc = g_utf8_get_char (p); - while (p < end && (g_unichar_islower (wc) || consider_as_space (wc))) + is_word_start = log_attrs && log_attrs[offset].is_word_start; + while (p < end && (item_transform == PANGO_TEXT_TRANSFORM_LOWERCASE || + consider_as_space (wc) || + (g_unichar_islower (wc) && + !(item_transform == PANGO_TEXT_TRANSFORM_UPPERCASE || + (item_transform == PANGO_TEXT_TRANSFORM_CAPITALIZE && is_word_start))))) { p = g_utf8_next_char (p); wc = g_utf8_get_char (p); + offset++; + is_word_start = log_attrs && log_attrs[offset].is_word_start; } if (p0 < p) @@ -1358,7 +1395,7 @@ split_item_for_variant (const char *text, attr = pango_attr_text_transform_new (transform); 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); + new_item->analysis.extra_attrs = g_slist_append (new_item->analysis.extra_attrs, attr); } if (lowercase_scale != PANGO_FONT_SCALE_NONE) @@ -1366,16 +1403,22 @@ split_item_for_variant (const char *text, attr = pango_attr_font_scale_new (lowercase_scale); 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); + new_item->analysis.extra_attrs = g_slist_append (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))) + is_word_start = log_attrs && log_attrs[offset].is_word_start; + while (p < end && (item_transform == PANGO_TEXT_TRANSFORM_UPPERCASE || + consider_as_space (wc) || + !(item_transform == PANGO_TEXT_TRANSFORM_LOWERCASE || g_unichar_islower (wc)) || + (item_transform == PANGO_TEXT_TRANSFORM_CAPITALIZE && is_word_start))) { p = g_utf8_next_char (p); wc = g_utf8_get_char (p); + offset++; + is_word_start = log_attrs && log_attrs[offset].is_word_start; } if (p0 < p) @@ -1401,34 +1444,36 @@ split_item_for_variant (const char *text, attr = pango_attr_font_scale_new (uppercase_scale); 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); + new_item->analysis.extra_attrs = g_slist_append (new_item->analysis.extra_attrs, attr); } } } } static void -handle_variants_for_item (const char *text, - GList *l) +handle_variants_for_item (const char *text, + PangoLogAttr *log_attrs, + GList *l) { PangoItem *item = l->data; PangoVariant variant; variant = get_font_variant (item); if (!variant_supported (item, variant)) - split_item_for_variant (text, l, variant); + split_item_for_variant (text, log_attrs, variant, l); } static void -handle_variants (const char *text, - GList *items) +handle_variants (const char *text, + PangoLogAttr *log_attrs, + GList *items) { GList *next; for (GList *l = items; l; l = next) { next = l->next; - handle_variants_for_item (text, l); + handle_variants_for_item (text, log_attrs, l); } } @@ -1456,9 +1501,10 @@ reorder_items (PangoContext *context, static GList * post_process_items (PangoContext *context, const char *text, + PangoLogAttr *log_attrs, GList *items) { - handle_variants (text, items); + handle_variants (text, log_attrs, items); apply_font_scale (context, items); return items; @@ -1467,7 +1513,12 @@ post_process_items (PangoContext *context, /* }}} */ /* {{{ Private API */ -/* Like pango_itemize_with_base_dir, but takes a font description */ +/* Like pango_itemize_with_base_dir, but takes a font description. + * In contrast to pango_itemize_with_base_dir, this function does + * not call pango_itemize_post_process_items, so you need to do that + * separately, after applying attributes that affect segmentation and + * computing the log attrs. + */ GList * pango_itemize_with_font (PangoContext *context, PangoDirection base_dir, @@ -1495,13 +1546,15 @@ pango_itemize_with_font (PangoContext *context, return reorder_items (context, state.result); } +/* Apply post-processing steps that may require log attrs. + */ GList * pango_itemize_post_process_items (PangoContext *context, const char *text, PangoLogAttr *log_attrs, GList *items) { - return post_process_items (context, text, items); + return post_process_items (context, text, log_attrs, items); } /* }}} */ @@ -1601,6 +1654,6 @@ pango_itemize (PangoContext *context, attrs, cached_iter); } - /* }}} */ + /* }}} */ /* vim:set foldmethod=marker expandtab: */ -- cgit v1.2.1