summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthias Clasen <mclasen@redhat.com>2021-11-06 23:53:51 -0400
committerMatthias Clasen <mclasen@redhat.com>2021-11-07 09:59:34 -0500
commit0c25f62407f0a73274e745bb115a553c999618fc (patch)
treef89ba35219cc6d38b0d847ce9cb9612c86e2c585
parent0eec5fab2af80e43c9141ffc21c523be6415f0a8 (diff)
downloadpango-0c25f62407f0a73274e745bb115a553c999618fc.tar.gz
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.
-rw-r--r--pango/itemize.c197
1 files changed, 130 insertions, 67 deletions
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);