From b5d3ad90e39a58967aa75aa08acd623c440aa97b Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Fri, 27 Aug 2021 23:54:59 -0400 Subject: Try harder to disect clusters We need to take into account whether the characters we are counting are actually cursor positions, else we are bound to look for more carets than the font has available, sometimes. This change would benefit from passing log attrs into pango_glyph_string_index_to_x. --- pango/glyphstring.c | 49 ++++++++++++++++++++++++++++++++++--------------- 1 file changed, 34 insertions(+), 15 deletions(-) diff --git a/pango/glyphstring.c b/pango/glyphstring.c index 30d68220..11eeffbf 100644 --- a/pango/glyphstring.c +++ b/pango/glyphstring.c @@ -404,6 +404,9 @@ pango_glyph_string_index_to_x (PangoGlyphString *glyphs, const char *p; + int attrs_len; + PangoLogAttr *attrs = NULL; + g_return_if_fail (glyphs != NULL); g_return_if_fail (length >= 0); g_return_if_fail (length == 0 || text != NULL); @@ -495,10 +498,20 @@ pango_glyph_string_index_to_x (PangoGlyphString *glyphs, /* Calculate offset of character within cluster */ - for (p = text + start_index; + if (g_utf8_next_char (text + start_index) != text + end_index) + { + /* FIXME: should allow log attrs to be passed in */ + attrs_len = g_utf8_strlen (text, length) + 1; + attrs = g_new0 (PangoLogAttr, attrs_len + 1); + pango_default_break (text, length, analysis, attrs, attrs_len); + } + + for (p = text + start_index, i = g_utf8_pointer_to_offset (text, text + start_index); p < text + end_index; - p = g_utf8_next_char (p)) + p = g_utf8_next_char (p), i++) { + if (!attrs[i].is_cursor_position) + continue; if (p < text + index) cluster_offset++; cluster_chars++; @@ -526,6 +539,7 @@ pango_glyph_string_index_to_x (PangoGlyphString *glyphs, hb_position_t caret; unsigned int caret_count = 1; int glyph_pos; + int num_carets; hb_font = pango_font_get_hb_font (analysis->font); @@ -544,7 +558,7 @@ pango_glyph_string_index_to_x (PangoGlyphString *glyphs, { if (glyph_pos != -1) { - /* multiple non-mark glyphs in cluster, giving up */ + g_print ("multiple non-mark glyphs in cluster, giving up\n"); goto fallback; } glyph_pos = i; @@ -552,24 +566,29 @@ pango_glyph_string_index_to_x (PangoGlyphString *glyphs, } if (glyph_pos == -1) { - /* no non-mark glyph in a multi-glyph cluster, giving up */ + g_print ("no non-mark glyph in a multi-glyph cluster, giving up\n"); goto fallback; } } - hb_ot_layout_get_ligature_carets (hb_font, - (analysis->level % 2) ? HB_DIRECTION_RTL : HB_DIRECTION_LTR, - glyphs->glyphs[glyph_pos].glyph, - cluster_offset - 1, &caret_count, &caret); - if (caret_count > 0) + num_carets = hb_ot_layout_get_ligature_carets (hb_font, + (analysis->level % 2) ? HB_DIRECTION_RTL : HB_DIRECTION_LTR, + glyphs->glyphs[glyph_pos].glyph, + cluster_offset - 1, &caret_count, &caret); + if (caret_count == 0) { - if (analysis->level % 2) /* Right to left */ - *x_pos = end_xpos + caret; - else - *x_pos = start_xpos + caret; - *x_pos += glyphs->glyphs[glyph_pos].geometry.x_offset; - return; + g_print ("no ligature caret information found for glyph %d, caret %d / %d. font has %d ligature carets\n", glyphs->glyphs[glyph_pos].glyph, cluster_offset - 1, cluster_chars, num_carets); + goto fallback; } + + g_print ("using ligature caret information for glyph %d, caret %d / %d. font has %d ligature carets\n", glyphs->glyphs[glyph_pos].glyph, cluster_offset - 1, cluster_chars, num_carets); + + if (analysis->level % 2) /* Right to left */ + *x_pos = end_xpos + caret; + else + *x_pos = start_xpos + caret; + *x_pos += glyphs->glyphs[glyph_pos].geometry.x_offset; + return; } fallback: -- cgit v1.2.1 From ea2c84eb7357031af60d37cb5378a254abe37ccc Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sat, 28 Aug 2021 09:29:53 -0400 Subject: Add pango_glyph_string_index_to_x_full This variant of pango_glyph_string_index_to_x takes log attrs so it does not have to compute them when it needs them to determine grapheme boundaries. --- pango/glyphstring.c | 85 ++++++++++++++++++++++++++++++++++++++--------------- pango/pango-glyph.h | 11 +++++++ 2 files changed, 72 insertions(+), 24 deletions(-) diff --git a/pango/glyphstring.c b/pango/glyphstring.c index 11eeffbf..282de61d 100644 --- a/pango/glyphstring.c +++ b/pango/glyphstring.c @@ -378,7 +378,6 @@ pango_glyph_string_get_logical_widths (PangoGlyphString *glyphs, * * Glyph positions * - */ void pango_glyph_string_index_to_x (PangoGlyphString *glyphs, @@ -388,6 +387,46 @@ pango_glyph_string_index_to_x (PangoGlyphString *glyphs, int index, gboolean trailing, int *x_pos) +{ + pango_glyph_string_index_to_x_full (glyphs, + text, length, + analysis, + NULL, + index, trailing, + x_pos); +} + +/** + * pango_glyph_string_index_to_x_full: + * @glyphs: the glyphs return from [func@shape] + * @text: the text for the run + * @length: the number of bytes (not characters) in @text. + * @analysis: the analysis information return from [func@itemize] + * @attrs: (nullable): `PangoLogAttr` array for @text + * @index_: the byte index within @text + * @trailing: whether we should compute the result for the beginning (%FALSE) + * or end (%TRUE) of the character. + * @x_pos: (out): location to store result + * + * Converts from character position to x position. + * + * This variant of [method@Pango.GlyphString.index_to_x] additionally + * accepts a `PangoLogAttr` array. The grapheme boundary information + * in it can be used to disambiguate positioning inside some complex + * clusters. [method@Pango.GlyphString.index_to_x] will compute this + * information if it needs it. + * + * Since: 1.50 + */ +void +pango_glyph_string_index_to_x_full (PangoGlyphString *glyphs, + const char *text, + int length, + PangoAnalysis *analysis, + PangoLogAttr *attrs, + int index, + gboolean trailing, + int *x_pos) { int i; int start_xpos = 0; @@ -404,9 +443,6 @@ pango_glyph_string_index_to_x (PangoGlyphString *glyphs, const char *p; - int attrs_len; - PangoLogAttr *attrs = NULL; - g_return_if_fail (glyphs != NULL); g_return_if_fail (length >= 0); g_return_if_fail (length == 0 || text != NULL); @@ -496,12 +532,15 @@ pango_glyph_string_index_to_x (PangoGlyphString *glyphs, end_xpos = (analysis->level % 2) ? 0 : width; } - /* Calculate offset of character within cluster */ - - if (g_utf8_next_char (text + start_index) != text + end_index) + /* Calculate offset of character within cluster. + * To come up with accurate answers here, we need to know grapheme + * boundaries. + */ + if (attrs == NULL && + g_utf8_next_char (text + start_index) != text + end_index) { - /* FIXME: should allow log attrs to be passed in */ - attrs_len = g_utf8_strlen (text, length) + 1; + int attrs_len = g_utf8_strlen (text, length) + 1; + attrs = g_new0 (PangoLogAttr, attrs_len + 1); pango_default_break (text, length, analysis, attrs, attrs_len); } @@ -510,10 +549,13 @@ pango_glyph_string_index_to_x (PangoGlyphString *glyphs, p < text + end_index; p = g_utf8_next_char (p), i++) { - if (!attrs[i].is_cursor_position) + if (attrs && !attrs[i].is_cursor_position) continue; + if (p < text + index) - cluster_offset++; + { + cluster_offset++; + } cluster_chars++; } @@ -521,17 +563,14 @@ pango_glyph_string_index_to_x (PangoGlyphString *glyphs, cluster_offset += 1; if (G_UNLIKELY (!cluster_chars)) /* pedantic */ - { + { *x_pos = start_xpos; return; } - /* Try to get a ligature caret position for the glyph - * from the font. - * - * If start_glyph_pos != end_glyph_pos, we are dealing - * with an m-n situation, where LigatureCaretList is - * not going to help. Just give up and do the simple thing. + /* Try to get a ligature caret position for the glyph from the font. + * This only makes sense if the cluster contains a single spacing + * glyph, so we need to check that all but one of them are marks. */ if (cluster_offset > 0 && cluster_offset < cluster_chars) { @@ -558,7 +597,7 @@ pango_glyph_string_index_to_x (PangoGlyphString *glyphs, { if (glyph_pos != -1) { - g_print ("multiple non-mark glyphs in cluster, giving up\n"); + /* multiple non-mark glyphs in cluster, giving up */ goto fallback; } glyph_pos = i; @@ -566,7 +605,7 @@ pango_glyph_string_index_to_x (PangoGlyphString *glyphs, } if (glyph_pos == -1) { - g_print ("no non-mark glyph in a multi-glyph cluster, giving up\n"); + /* no non-mark glyph in a multi-glyph cluster, giving up */ goto fallback; } } @@ -575,14 +614,12 @@ pango_glyph_string_index_to_x (PangoGlyphString *glyphs, (analysis->level % 2) ? HB_DIRECTION_RTL : HB_DIRECTION_LTR, glyphs->glyphs[glyph_pos].glyph, cluster_offset - 1, &caret_count, &caret); - if (caret_count == 0) + if (caret_count == 0 || num_carets == 0) { - g_print ("no ligature caret information found for glyph %d, caret %d / %d. font has %d ligature carets\n", glyphs->glyphs[glyph_pos].glyph, cluster_offset - 1, cluster_chars, num_carets); + /* no ligature caret information found for this glyph */ goto fallback; } - g_print ("using ligature caret information for glyph %d, caret %d / %d. font has %d ligature carets\n", glyphs->glyphs[glyph_pos].glyph, cluster_offset - 1, cluster_chars, num_carets); - if (analysis->level % 2) /* Right to left */ *x_pos = end_xpos + caret; else diff --git a/pango/pango-glyph.h b/pango/pango-glyph.h index 2094fa53..cd29b77a 100644 --- a/pango/pango-glyph.h +++ b/pango/pango-glyph.h @@ -24,6 +24,7 @@ #include #include +#include G_BEGIN_DECLS @@ -198,6 +199,16 @@ void pango_glyph_string_x_to_index (PangoGlyphStrin int *index_, int *trailing); +PANGO_AVAILABLE_IN_1_50 +void pango_glyph_string_index_to_x_full (PangoGlyphString *glyphs, + const char *text, + int length, + PangoAnalysis *analysis, + PangoLogAttr *attrs, + int index_, + gboolean trailing, + int *x_pos); + /* Shaping */ /** -- cgit v1.2.1 From 1c7a2ea2686af4affe0824ee57b38ea9cdec195d Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sat, 28 Aug 2021 12:28:02 -0400 Subject: Don't compute log attrs on demand If we don't have them, just do the fallback. --- pango/glyphstring.c | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/pango/glyphstring.c b/pango/glyphstring.c index 282de61d..ea9a6398 100644 --- a/pango/glyphstring.c +++ b/pango/glyphstring.c @@ -413,8 +413,7 @@ pango_glyph_string_index_to_x (PangoGlyphString *glyphs, * This variant of [method@Pango.GlyphString.index_to_x] additionally * accepts a `PangoLogAttr` array. The grapheme boundary information * in it can be used to disambiguate positioning inside some complex - * clusters. [method@Pango.GlyphString.index_to_x] will compute this - * information if it needs it. + * clusters. * * Since: 1.50 */ @@ -536,16 +535,7 @@ pango_glyph_string_index_to_x_full (PangoGlyphString *glyphs, * To come up with accurate answers here, we need to know grapheme * boundaries. */ - if (attrs == NULL && - g_utf8_next_char (text + start_index) != text + end_index) - { - int attrs_len = g_utf8_strlen (text, length) + 1; - - attrs = g_new0 (PangoLogAttr, attrs_len + 1); - pango_default_break (text, length, analysis, attrs, attrs_len); - } - - for (p = text + start_index, i = g_utf8_pointer_to_offset (text, text + start_index); + for (p = text + start_index, i = attrs ? g_utf8_pointer_to_offset (text, text + start_index) : 0; p < text + end_index; p = g_utf8_next_char (p), i++) { @@ -553,9 +543,7 @@ pango_glyph_string_index_to_x_full (PangoGlyphString *glyphs, continue; if (p < text + index) - { - cluster_offset++; - } + cluster_offset++; cluster_chars++; } -- cgit v1.2.1 From c9cfd2653a64066cd3bd24300423f2268749ef64 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sat, 28 Aug 2021 13:32:24 -0400 Subject: layout: Use pango_glyph_string_index_to_x_full --- pango/pango-layout.c | 53 ++++++++++++++++++++++++++++++++++------------------ 1 file changed, 35 insertions(+), 18 deletions(-) diff --git a/pango/pango-layout.c b/pango/pango-layout.c index 86c67020..d3d93e60 100644 --- a/pango/pango-layout.c +++ b/pango/pango-layout.c @@ -1788,6 +1788,8 @@ pango_layout_line_index_to_x (PangoLayoutLine *line, if (run->item->offset <= index && run->item->offset + run->item->length > index) { int offset = g_utf8_pointer_to_offset (layout->text, layout->text + index); + int attr_offset; + if (trailing) { while (index < line->start_index + line->length && @@ -1806,14 +1808,20 @@ pango_layout_line_index_to_x (PangoLayoutLine *line, offset--; index = g_utf8_prev_char (layout->text + index) - layout->text; } - } - pango_glyph_string_index_to_x (run->glyphs, - layout->text + run->item->offset, - run->item->length, - &run->item->analysis, - index - run->item->offset, trailing, x_pos); + /* Note: we simply assert here, since our items are all internally + * created. If that ever changes, we need to add a fallback here. + */ + g_assert (run->item->analysis.flags & PANGO_ANALYSIS_FLAG_HAS_CHAR_OFFSET); + attr_offset = ((PangoItemPrivate *)run->item)->char_offset; + + pango_glyph_string_index_to_x_full (run->glyphs, + layout->text + run->item->offset, + run->item->length, + &run->item->analysis, + layout->log_attrs + attr_offset, + index - run->item->offset, trailing, x_pos); if (x_pos) *x_pos += width; @@ -5039,6 +5047,7 @@ pango_layout_line_get_x_ranges (PangoLayoutLine *line, int run_start_index = MAX (start_index, run->item->offset); int run_end_index = MIN (end_index, run->item->offset + run->item->length); int run_start_x, run_end_x; + int attr_offset; g_assert (run_end_index > 0); @@ -5046,18 +5055,26 @@ pango_layout_line_get_x_ranges (PangoLayoutLine *line, run_end_index = g_utf8_prev_char (line->layout->text + run_end_index) - line->layout->text; - pango_glyph_string_index_to_x (run->glyphs, - line->layout->text + run->item->offset, - run->item->length, - &run->item->analysis, - run_start_index - run->item->offset, FALSE, - &run_start_x); - pango_glyph_string_index_to_x (run->glyphs, - line->layout->text + run->item->offset, - run->item->length, - &run->item->analysis, - run_end_index - run->item->offset, TRUE, - &run_end_x); + /* Note: we simply assert here, since our items are all internally + * created. If that ever changes, we need to add a fallback here. + */ + g_assert (run->item->analysis.flags & PANGO_ANALYSIS_FLAG_HAS_CHAR_OFFSET); + attr_offset = ((PangoItemPrivate *)run->item)->char_offset; + + pango_glyph_string_index_to_x_full (run->glyphs, + line->layout->text + run->item->offset, + run->item->length, + &run->item->analysis, + line->layout->log_attrs + attr_offset, + run_start_index - run->item->offset, FALSE, + &run_start_x); + pango_glyph_string_index_to_x_full (run->glyphs, + line->layout->text + run->item->offset, + run->item->length, + &run->item->analysis, + line->layout->log_attrs + attr_offset, + run_end_index - run->item->offset, TRUE, + &run_end_x); (*ranges)[2*range_count] = x_offset + accumulated_width + MIN (run_start_x, run_end_x); (*ranges)[2*range_count + 1] = x_offset + accumulated_width + MAX (run_start_x, run_end_x); -- cgit v1.2.1 From 6c84e672cb0e7bcc9a74f9ca0462c073a2ee2c19 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sat, 28 Aug 2021 13:41:37 -0400 Subject: pango-view: Use pango_glyph_string_index_to_x_full --- utils/viewer-pangocairo.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/utils/viewer-pangocairo.c b/utils/viewer-pangocairo.c index 98591e5a..c94de9e1 100644 --- a/utils/viewer-pangocairo.c +++ b/utils/viewer-pangocairo.c @@ -534,13 +534,14 @@ render_callback (PangoLayout *layout, { if (attrs[offset + i].is_cursor_position) { - pango_glyph_string_index_to_x (run->glyphs, - text + run->item->offset, - run->item->length, - &run->item->analysis, - p - start, - trailing, - &x); + pango_glyph_string_index_to_x_full (run->glyphs, + text + run->item->offset, + run->item->length, + &run->item->analysis, + (PangoLogAttr *)attrs + offset, + p - start, + trailing, + &x); x += rect.x; cairo_set_source_rgba (cr, 1.0, 0.0, 1.0, 0.5); -- cgit v1.2.1