From 1f0b5d5b87b61a37bebd393c0d67cce0b4ccdcb9 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sun, 29 Aug 2021 17:26:52 -0400 Subject: Add horizontal displacement Apply horizontal displacements for superscripts and subscripts that are provided in font metrics. This noticably improves the placement of superscripts in italics. Currently, we only apply these displacements in post-processing, and ignore the width changes during line-breaking. This could be improved by moving the baseline handling into the line-breaking proper. --- pango/pango-glyph-item.c | 4 ++ pango/pango-glyph-item.h | 10 ++++- pango/pango-layout-private.h | 5 ++- pango/pango-layout.c | 94 ++++++++++++++++++++++++++++---------------- pango/pango-renderer.c | 4 +- 5 files changed, 78 insertions(+), 39 deletions(-) diff --git a/pango/pango-glyph-item.c b/pango/pango-glyph-item.c index c64bfa13..7eb1737d 100644 --- a/pango/pango-glyph-item.c +++ b/pango/pango-glyph-item.c @@ -130,6 +130,8 @@ pango_glyph_item_split (PangoGlyphItem *orig, pango_glyph_string_set_size (orig->glyphs, orig->glyphs->num_glyphs - num_glyphs); new->y_offset = orig->y_offset; + new->start_x_offset = orig->start_x_offset; + new->end_x_offset = -orig->start_x_offset; return new; } @@ -157,6 +159,8 @@ pango_glyph_item_copy (PangoGlyphItem *orig) result->item = pango_item_copy (orig->item); result->glyphs = pango_glyph_string_copy (orig->glyphs); result->y_offset = orig->y_offset; + result->start_x_offset = orig->start_x_offset; + result->end_x_offset = orig->end_x_offset; return result; } diff --git a/pango/pango-glyph-item.h b/pango/pango-glyph-item.h index baea69fc..fd8951d2 100644 --- a/pango/pango-glyph-item.h +++ b/pango/pango-glyph-item.h @@ -33,8 +33,12 @@ G_BEGIN_DECLS * PangoGlyphItem: * @item: corresponding `PangoItem` * @glyphs: corresponding `PangoGlyphString` - * @baseline: shift of the baseline, relative to the - * containing lines baseline. Positive values shift upwards + * @y_offset: shift of the baseline, relative to the baseline + * of the containing line. Positive values shift upwards + * @start_x_offset: horizontal displacement to apply before the + * glyph item. Positive values shift right + * @end_x_offset: horizontal displacement to apply after th + * glyph item. Positive values shift right * * A `PangoGlyphItem` is a pair of a `PangoItem` and the glyphs * resulting from shaping the items text. @@ -50,6 +54,8 @@ struct _PangoGlyphItem PangoItem *item; PangoGlyphString *glyphs; int y_offset; + int start_x_offset; + int end_x_offset; }; #define PANGO_TYPE_GLYPH_ITEM (pango_glyph_item_get_type ()) diff --git a/pango/pango-layout-private.h b/pango/pango-layout-private.h index 1805e730..b9f9b137 100644 --- a/pango/pango-layout-private.h +++ b/pango/pango-layout-private.h @@ -112,11 +112,12 @@ struct _PangoLayoutIter Extents *line_extents; int line_index; - /* X position of the current run */ + /* Position of the current run */ int run_x; - /* Width of the current run */ + /* Width and end offset of the current run */ int run_width; + int end_x_offset; /* this run is left-to-right */ gboolean ltr; diff --git a/pango/pango-layout.c b/pango/pango-layout.c index 6c1e2e5e..098e3a3e 100644 --- a/pango/pango-layout.c +++ b/pango/pango-layout.c @@ -6154,18 +6154,23 @@ justify_words (PangoLayoutLine *line, typedef struct { PangoAttribute *attr; - int shift; + int x_offset; + int y_offset; } BaselineItem; static void -collect_shifts (ParaBreakState *state, - PangoItem *item, - PangoItem *prev, - int *start_shift, - int *end_shift) -{ - *start_shift = 0; - *end_shift = 0; +collect_baseline_shift (ParaBreakState *state, + PangoItem *item, + PangoItem *prev, + int *start_x_offset, + int *start_y_offset, + int *end_x_offset, + int *end_y_offset) +{ + *start_x_offset = 0; + *start_y_offset = 0; + *end_x_offset = 0; + *end_y_offset = 0; for (GSList *l = item->analysis.extra_attrs; l; l = l->next) { @@ -6175,8 +6180,8 @@ collect_shifts (ParaBreakState *state, { int value = ((PangoAttrInt *)attr)->value; - *start_shift += value; - *end_shift -= value; + *start_y_offset += value; + *end_y_offset -= value; } else if (attr->klass->type == PANGO_ATTR_BASELINE_SHIFT) { @@ -6187,51 +6192,60 @@ collect_shifts (ParaBreakState *state, entry = g_new0 (BaselineItem, 1); entry->attr = attr; + state->baseline_shifts = g_list_prepend (state->baseline_shifts, entry); value = ((PangoAttrInt *)attr)->value; if (value > 1024 || value < -1024) { - entry->shift = value; + entry->y_offset = value; + /* FIXME: compute an x_offset from value to italic angle */ } else { - int superscript_shift = 0; - int subscript_shift = 0; - hb_font_t *hb_font; + int superscript_x_offset = 0; + int superscript_y_offset = 0; + int subscript_x_offset = 0; + int subscript_y_offset = 0; if (prev) { - hb_font = pango_font_get_hb_font (prev->analysis.font); - hb_ot_metrics_get_position (hb_font, HB_OT_METRICS_TAG_SUPERSCRIPT_EM_Y_OFFSET, &superscript_shift); - hb_ot_metrics_get_position (hb_font, HB_OT_METRICS_TAG_SUBSCRIPT_EM_Y_OFFSET, &subscript_shift); + hb_font_t *hb_font = pango_font_get_hb_font (prev->analysis.font); + hb_ot_metrics_get_position (hb_font, HB_OT_METRICS_TAG_SUPERSCRIPT_EM_Y_OFFSET, &superscript_y_offset); + hb_ot_metrics_get_position (hb_font, HB_OT_METRICS_TAG_SUPERSCRIPT_EM_X_OFFSET, &superscript_x_offset); + hb_ot_metrics_get_position (hb_font, HB_OT_METRICS_TAG_SUBSCRIPT_EM_Y_OFFSET, &subscript_y_offset); + hb_ot_metrics_get_position (hb_font, HB_OT_METRICS_TAG_SUBSCRIPT_EM_X_OFFSET, &subscript_x_offset); } - if (superscript_shift == 0) - superscript_shift = 5000; - if (subscript_shift == 0) - subscript_shift = 5000; + if (superscript_y_offset == 0) + superscript_y_offset = 5000; + if (subscript_y_offset == 0) + subscript_y_offset = 5000; switch (value) { case PANGO_BASELINE_SHIFT_NONE: - entry->shift = 0; + entry->x_offset = 0; + entry->y_offset = 0; break; case PANGO_BASELINE_SHIFT_SUPERSCRIPT: - entry->shift = superscript_shift; + entry->x_offset = superscript_x_offset; + entry->y_offset = superscript_y_offset; break; case PANGO_BASELINE_SHIFT_SUBSCRIPT: - entry->shift = -subscript_shift; + entry->x_offset = subscript_x_offset; + entry->y_offset = -subscript_y_offset; break; default: g_assert_not_reached (); } } - *start_shift += entry->shift; - state->baseline_shifts = g_list_prepend (state->baseline_shifts, entry); + *start_x_offset += entry->x_offset; + *start_y_offset += entry->y_offset; } + if (attr->end_index == item->offset + item->length) { BaselineItem *entry = state->baseline_shifts->data; @@ -6239,7 +6253,10 @@ collect_shifts (ParaBreakState *state, if (attr->start_index == entry->attr->start_index && attr->end_index == entry->attr->end_index && ((PangoAttrInt *)attr)->value == ((PangoAttrInt *)entry->attr)->value) - *end_shift -= entry->shift; + { + *end_x_offset -= entry->x_offset; + *end_y_offset -= entry->y_offset; + } else g_warning ("Baseline attributes mismatch\n"); @@ -6251,7 +6268,7 @@ collect_shifts (ParaBreakState *state, } static void -apply_baseline_shifts (PangoLayoutLine *line, +apply_baseline_shift (PangoLayoutLine *line, ParaBreakState *state) { int y_offset = 0; @@ -6261,13 +6278,16 @@ apply_baseline_shifts (PangoLayoutLine *line, { PangoLayoutRun *run = l->data; PangoItem *item = run->item; + int start_x_offset, end_x_offset; int start_y_offset, end_y_offset; - collect_shifts (state, item, prev, &start_y_offset, &end_y_offset); + collect_baseline_shift (state, item, prev, &start_x_offset, &start_y_offset, &end_x_offset, &end_y_offset); y_offset += start_y_offset; run->y_offset = y_offset; + run->start_x_offset = start_x_offset; + run->end_x_offset = end_x_offset; y_offset += end_y_offset; @@ -6295,7 +6315,7 @@ pango_layout_line_postprocess (PangoLayoutLine *line, */ line->runs = g_slist_reverse (line->runs); - apply_baseline_shifts (line, state); + apply_baseline_shift (line, state); /* Ellipsize the line if necessary */ @@ -6543,16 +6563,22 @@ update_run (PangoLayoutIter *iter, if (iter->run_list_link == iter->line->runs) iter->run_x = line_ext->logical_rect.x; else - iter->run_x += iter->run_width; + { + iter->run_x += iter->end_x_offset + iter->run_width; + if (iter->run) + iter->run_x += iter->run->start_x_offset; + } if (iter->run) { iter->run_width = pango_glyph_string_get_width (iter->run->glyphs); + iter->end_x_offset = iter->run->end_x_offset; } else { /* The empty run at the end of a line */ iter->run_width = 0; + iter->end_x_offset = 0; } if (iter->run) @@ -7256,7 +7282,7 @@ pango_layout_iter_get_cluster_extents (PangoLayoutIter *iter, if (ink_rect) { - ink_rect->x += iter->cluster_x; + ink_rect->x += iter->cluster_x + iter->run->start_x_offset; ink_rect->y -= iter->run->y_offset; offset_y (iter, &ink_rect->y); } @@ -7264,7 +7290,7 @@ pango_layout_iter_get_cluster_extents (PangoLayoutIter *iter, if (logical_rect) { g_assert (logical_rect->width == iter->cluster_width); - logical_rect->x += iter->cluster_x; + logical_rect->x += iter->cluster_x + iter->run->start_x_offset; logical_rect->y -= iter->run->y_offset; offset_y (iter, &logical_rect->y); } diff --git a/pango/pango-renderer.c b/pango/pango-renderer.c index 231ebe7d..d47ba13a 100644 --- a/pango/pango-renderer.c +++ b/pango/pango-renderer.c @@ -580,7 +580,6 @@ pango_renderer_draw_layout_line (PangoRenderer *renderer, gboolean got_overall = FALSE; PangoRectangle overall_rect; const char *text; - int y_off; g_return_if_fail (PANGO_IS_RENDERER_FAST (renderer)); @@ -612,6 +611,7 @@ pango_renderer_draw_layout_line (PangoRenderer *renderer, PangoAttrShape *shape_attr; PangoRectangle ink_rect, *ink = NULL; PangoRectangle logical_rect, *logical = NULL; + int y_off; if (run->item->analysis.flags & PANGO_ANALYSIS_FLAG_CENTERED_BASELINE) logical = &logical_rect; @@ -651,6 +651,7 @@ pango_renderer_draw_layout_line (PangoRenderer *renderer, state.logical_rect_end = x + x_off + glyph_string_width; + x_off += run->start_x_offset; y_off = run->y_offset; if (run->item->analysis.flags & PANGO_ANALYSIS_FLAG_CENTERED_BASELINE) @@ -730,6 +731,7 @@ pango_renderer_draw_layout_line (PangoRenderer *renderer, draw_strikethrough (renderer, &state); x_off += glyph_string_width; + x_off += run->end_x_offset; } /* Finish off any remaining underlines -- cgit v1.2.1