From fd51b46898ac9c880bb9552c3e301bfef7ac22bd Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sun, 29 Aug 2021 15:53:48 -0400 Subject: Implement font-dependent scaling Add a new font-scale attribute to indicate font size changes due to super- and subscript shifts, and handle it during item post-processing to find the right font sizes. --- pango/itemize.c | 181 +++++++++++++++++++++++++++++++++++++++++++---- pango/pango-attributes.c | 31 ++++++-- pango/pango-attributes.h | 12 +++- pango/pango-layout.c | 1 + pango/pango-markup.c | 12 ++++ tests/test-itemize.c | 1 + 6 files changed, 217 insertions(+), 21 deletions(-) diff --git a/pango/itemize.c b/pango/itemize.c index 11bc2513..29a1cdff 100644 --- a/pango/itemize.c +++ b/pango/itemize.c @@ -34,6 +34,7 @@ #include "pango-attributes-private.h" #include "pango-item-private.h" +#include /* {{{ Font cache */ @@ -1019,6 +1020,169 @@ itemize_state_finish (ItemizeState *state) if (state->base_font) g_object_unref (state->base_font); } + +/* }}} */ +/* {{{ Post-processing */ + +typedef struct { + PangoAttribute *attr; + double scale; +} ScaleItem; + +static gboolean +collect_font_scale (PangoContext *context, + GList **stack, + PangoItem *item, + PangoItem *prev, + double *scale) +{ + gboolean retval = FALSE; + GList *l; + + for (GSList *l = item->analysis.extra_attrs; l; l = l->next) + { + PangoAttribute *attr = l->data; + + if (attr->klass->type == PANGO_ATTR_FONT_SCALE) + { + if (attr->start_index == item->offset) + { + ScaleItem *entry; + hb_font_t *hb_font; + int y_scale; + hb_position_t y_size; + + entry = g_new (ScaleItem, 1); + entry->attr = attr; + *stack = g_list_prepend (*stack, entry); + + hb_font = pango_font_get_hb_font (prev->analysis.font); + hb_font_get_scale (hb_font, NULL, &y_scale); + + switch (((PangoAttrInt *)attr)->value) + { + case PANGO_FONT_SCALE_NONE: + break; + case PANGO_FONT_SCALE_SUPERSCRIPT: + if (hb_ot_metrics_get_position (hb_font, + HB_OT_METRICS_TAG_SUPERSCRIPT_EM_Y_SIZE, + &y_size)) + entry->scale = y_size / (double) y_scale; + else + entry->scale = 1 / 1.2; + break; + case PANGO_FONT_SCALE_SUBSCRIPT: + if (hb_ot_metrics_get_position (hb_font, + HB_OT_METRICS_TAG_SUBSCRIPT_EM_Y_SIZE, + &y_size)) + entry->scale = y_size / (double) y_scale; + else + entry->scale = 1 / 1.2; + break; + default: + g_assert_not_reached (); + } + } + } + } + + *scale = 1.0; + + for (l = *stack; l; l = l->next) + { + ScaleItem *entry = l->data; + *scale *= entry->scale; + retval = TRUE; + } + + l = *stack; + while (l) + { + ScaleItem *entry = l->data; + GList *next = l->next; + + if (entry->attr->end_index == item->offset + item->length) + { + *stack = g_list_delete_link (*stack, l); + g_free (entry); + } + + l = next; + } + + return retval; +} + +static void +apply_scale_to_item (PangoContext *context, + PangoItem *item, + double scale) +{ + PangoFontDescription *desc; + double size; + + desc = pango_font_describe (item->analysis.font); + size = scale * pango_font_description_get_size (desc); + + if (pango_font_description_get_size_is_absolute (desc)) + pango_font_description_set_absolute_size (desc, size); + else + pango_font_description_set_size (desc, size); + + g_object_unref (item->analysis.font); + item->analysis.font = pango_font_map_load_font (context->font_map, context, desc); + + pango_font_description_free (desc); +} + +static void +apply_font_scale (PangoContext *context, + GList *items) +{ + PangoItem *prev; + GList *stack = NULL; + + for (GList *l = items; l; l = l->next) + { + PangoItem *item = l->data; + double scale; + + if (collect_font_scale (context, &stack, item, prev, &scale)) + apply_scale_to_item (context, item, scale); + + prev = item; + } + + if (stack != NULL) + { + g_warning ("Leftover font scales"); + g_list_free_full (stack, g_free); + } +} + +static GList * +post_process_items (PangoContext *context, + GList *items) +{ + 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; + } + } + + /* apply font-scale */ + apply_font_scale (context, items); + + return items; +} + /* }}} */ /* {{{ Public API */ @@ -1034,8 +1198,6 @@ pango_itemize_with_font (PangoContext *context, const PangoFontDescription *desc) { ItemizeState state; - GList *items; - int char_offset; if (length == 0 || g_utf8_get_char (text + start_index) == '\0') return NULL; @@ -1049,18 +1211,7 @@ pango_itemize_with_font (PangoContext *context, itemize_state_finish (&state); - items = g_list_reverse (state.result); - - /* Compute the char offset for each item */ - 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; - } - - return items; + return post_process_items (context, state.result); } /** @@ -1154,6 +1305,6 @@ pango_itemize (PangoContext *context, NULL); } -/* }}} */ + /* }}} */ /* vim:set foldmethod=marker expandtab: */ diff --git a/pango/pango-attributes.c b/pango/pango-attributes.c index 8507c963..65af8f3b 100644 --- a/pango/pango-attributes.c +++ b/pango/pango-attributes.c @@ -947,6 +947,20 @@ pango_attr_baseline_shift_new (int rise) return pango_attr_int_new (&klass, (int)rise); } + +PangoAttribute * +pango_attr_font_scale_new (PangoFontScale scale) +{ + static const PangoAttrClass klass = { + PANGO_ATTR_FONT_SCALE, + pango_attr_int_copy, + pango_attr_int_destroy, + pango_attr_int_equal + }; + + return pango_attr_int_new (&klass, (int)scale); +} + /** * pango_attr_scale_new: * @scale_factor: factor to scale the font @@ -1558,6 +1572,7 @@ pango_attribute_as_int (PangoAttribute *attr) case PANGO_ATTR_WORD: case PANGO_ATTR_SENTENCE: case PANGO_ATTR_BASELINE_SHIFT: + case PANGO_ATTR_FONT_SCALE: return (PangoAttrInt *)attr; default: @@ -2847,10 +2862,14 @@ pango_attr_iterator_get_font (PangoAttrIterator *iterator, { gboolean found = FALSE; - /* Hack: special-case FONT_FEATURES. We don't want them to - * override each other, so we never merge them. This should - * be fixed when we implement attr-merging. */ - if (attr->klass->type != PANGO_ATTR_FONT_FEATURES) + /* Hack: special-case FONT_FEATURES, BASELINE_SHIFT and FONT_SCALE. + * We don't want these to accumulate, not override each other, + * so we never merge them. + * This needs to be handled more systematically. + */ + if (attr->klass->type != PANGO_ATTR_FONT_FEATURES && + attr->klass->type != PANGO_ATTR_BASELINE_SHIFT && + attr->klass->type != PANGO_ATTR_FONT_SCALE) { GSList *tmp_list = *extra_attrs; while (tmp_list) @@ -2917,7 +2936,9 @@ pango_attr_iterator_get_attrs (PangoAttrIterator *iterator) GSList *tmp_list2; gboolean found = FALSE; - if (attr->klass->type != PANGO_ATTR_FONT_DESC) + if (attr->klass->type != PANGO_ATTR_FONT_DESC && + attr->klass->type != PANGO_ATTR_BASELINE_SHIFT && + attr->klass->type != PANGO_ATTR_FONT_SCALE) for (tmp_list2 = attrs; tmp_list2; tmp_list2 = tmp_list2->next) { PangoAttribute *old_attr = tmp_list2->data; diff --git a/pango/pango-attributes.h b/pango/pango-attributes.h index 1c9df2c5..59183a60 100644 --- a/pango/pango-attributes.h +++ b/pango/pango-attributes.h @@ -79,7 +79,8 @@ typedef struct _PangoAttrFontFeatures PangoAttrFontFeatures; * @PANGO_ATTR_ABSOLUTE_LINE_HEIGHT: line height ([struct@Pango.AttrInt]). Since: 1.50 * @PANGO_ATTR_WORD: override segmentation to classify the range of the attribute as a single word ([struct@Pango.AttrInt]). Since 1.50 * @PANGO_ATTR_SENTENCE: override segmentation to classify the range of the attribute as a single sentence ([struct@Pango.AttrInt]). Since 1.50 - * @PANGO_ATTR_BASELINE_SHIFT: baseline displacement ([struct@Pango.AttrSize]). Since 1.50 + * @PANGO_ATTR_BASELINE_SHIFT: baseline displacement ([struct@Pango.AttrInt]). Since 1.50 + * @PANGO_ATTR_FONT_SCALE: font-relative size change ([struct@Pango.AttrInt]). Since 1.50 * * The `PangoAttrType` distinguishes between different types of attributes. * @@ -127,6 +128,7 @@ typedef enum PANGO_ATTR_WORD, /* PangoAttrInt */ PANGO_ATTR_SENTENCE, /* PangoAttrInt */ PANGO_ATTR_BASELINE_SHIFT, /* PangoAttrSize */ + PANGO_ATTR_FONT_SCALE, /* PangoAttrInt */ } PangoAttrType; /** @@ -245,6 +247,12 @@ typedef enum { PANGO_BASELINE_SHIFT_SUBSCRIPT, } PangoBaselineShift; +typedef enum { + PANGO_FONT_SCALE_NONE, + PANGO_FONT_SCALE_SUPERSCRIPT, + PANGO_FONT_SCALE_SUBSCRIPT, +} PangoFontScale; + /** * PANGO_ATTR_INDEX_FROM_TEXT_BEGINNING: * @@ -536,6 +544,8 @@ PANGO_AVAILABLE_IN_ALL PangoAttribute * pango_attr_rise_new (int rise); PANGO_AVAILABLE_IN_1_50 PangoAttribute * pango_attr_baseline_shift_new (int shift); +PANGO_AVAILABLE_IN_1_50 +PangoAttribute * pango_attr_font_scale_new (PangoFontScale scale); PANGO_AVAILABLE_IN_ALL PangoAttribute * pango_attr_scale_new (double scale_factor); PANGO_AVAILABLE_IN_1_4 diff --git a/pango/pango-layout.c b/pango/pango-layout.c index 58e3a91c..6c1e2e5e 100644 --- a/pango/pango-layout.c +++ b/pango/pango-layout.c @@ -4321,6 +4321,7 @@ affects_itemization (PangoAttribute *attr, case PANGO_ATTR_ABSOLUTE_SIZE: case PANGO_ATTR_GRAVITY: case PANGO_ATTR_GRAVITY_HINT: + case PANGO_ATTR_FONT_SCALE: /* These need to be constant across runs */ case PANGO_ATTR_LETTER_SPACING: case PANGO_ATTR_SHAPE: diff --git a/pango/pango-markup.c b/pango/pango-markup.c index 65396547..54c08c67 100644 --- a/pango/pango-markup.c +++ b/pango/pango-markup.c @@ -1232,6 +1232,7 @@ span_parse_func (MarkupData *md G_GNUC_UNUSED, const char *line_height = NULL; const char *text_transform = NULL; const char *segment = NULL; + const char *font_scale = NULL; g_markup_parse_context_get_position (context, &line_number, &char_number); @@ -1286,6 +1287,7 @@ span_parse_func (MarkupData *md G_GNUC_UNUSED, CHECK_ATTRIBUTE2(style, "font_style"); CHECK_ATTRIBUTE2(variant, "font_variant"); CHECK_ATTRIBUTE2(weight, "font_weight"); + CHECK_ATTRIBUTE(font_scale); CHECK_ATTRIBUTE (foreground); CHECK_ATTRIBUTE2(foreground, "fgcolor"); @@ -1699,6 +1701,16 @@ span_parse_func (MarkupData *md G_GNUC_UNUSED, } + if (G_UNLIKELY (font_scale)) + { + PangoFontScale scale; + + if (!span_parse_enum ("font_scale", font_scale, PANGO_TYPE_FONT_SCALE, (int*)(void*)&scale, line_number, error)) + goto error; + + add_attribute (tag, pango_attr_font_scale_new (scale)); + } + if (G_UNLIKELY (letter_spacing)) { gint n = 0; diff --git a/tests/test-itemize.c b/tests/test-itemize.c index 29f59210..e5775985 100644 --- a/tests/test-itemize.c +++ b/tests/test-itemize.c @@ -73,6 +73,7 @@ affects_itemization (PangoAttribute *attr, case PANGO_ATTR_ABSOLUTE_SIZE: case PANGO_ATTR_GRAVITY: case PANGO_ATTR_GRAVITY_HINT: + case PANGO_ATTR_FONT_SCALE: /* These are part of ItemProperties, so need to break runs */ case PANGO_ATTR_LETTER_SPACING: case PANGO_ATTR_SHAPE: -- cgit v1.2.1