diff options
author | Vincent Sanders <vince@kyllikki.org> | 2025-02-27 22:56:53 +0000 |
---|---|---|
committer | Vincent Sanders <vince@kyllikki.org> | 2025-03-02 11:13:35 +0000 |
commit | fb56aa214c775995fa9ee7e06c6860997e4cb5b7 (patch) | |
tree | d2af10b34e2e41504c4c6d42a7c87c2a8316508b | |
parent | 4609648b9e0f812d5b765a6dbaf92576e06475ec (diff) | |
download | netsurf-fb56aa214c775995fa9ee7e06c6860997e4cb5b7.tar.gz netsurf-fb56aa214c775995fa9ee7e06c6860997e4cb5b7.tar.bz2 |
Improve GTK layout functions to split correctly
-rw-r--r-- | frontends/gtk/layout_pango.c | 168 |
1 files changed, 126 insertions, 42 deletions
diff --git a/frontends/gtk/layout_pango.c b/frontends/gtk/layout_pango.c index a750e081e..89b192059 100644 --- a/frontends/gtk/layout_pango.c +++ b/frontends/gtk/layout_pango.c @@ -69,6 +69,7 @@ nsfont_width(const plot_font_style_t *fstyle, int *width) { PangoFontDescription *desc; + PangoRectangle pos; if (length == 0) { *width = 0; @@ -83,7 +84,8 @@ nsfont_width(const plot_font_style_t *fstyle, pango_layout_set_text(nsfont_pango_layout, string, length); - pango_layout_get_pixel_size(nsfont_pango_layout, width, 0); + pango_layout_index_to_pos(nsfont_pango_layout, length, &pos); + *width = PANGO_PIXELS(pos.x); NSLOG(netsurf, DEEPDEBUG, "fstyle: %p string:\"%.*s\", length: %" PRIsizet ", width: %dpx", @@ -93,6 +95,62 @@ nsfont_width(const plot_font_style_t *fstyle, return NSERROR_OK; } +/** + * Find the position in a string where an x coordinate falls. + * + * \param[in] layout A pango layout with font set + * \param[in] string UTF-8 string to measure + * \param[in] length length of string, in bytes + * \param[in] x coordinate to search for + * \param[out] string_idx updated to offset in string of actual_x, [0..length] + * \param[out] actual_x updated to x coordinate of character closest to x or full length if string_idx is 0 + * \return NSERROR_OK and string_idx and actual_x updated or appropriate error code on faliure + */ +static nserror +layout_position(PangoLayout *layout, + const char *string, + size_t length, + int x, + size_t *string_idx, + int *actual_x) +{ + int index; + PangoRectangle pos; + + /* deal with empty string */ + if (length == 0) { + *string_idx = 0; + *actual_x = 0; + return NSERROR_OK; + } + + x--; /* split x coordinate is exclusive */ + + pango_layout_set_text(layout, string, length); + + if (x <= 0) { + /* deal with negative or zero available width */ + index = 0; + } else { + /* compute index into string */ + if (pango_layout_xy_to_index(layout, + x * PANGO_SCALE, + 0, &index, 0) == FALSE) { + /* whole string fits */ + index = length; + } + } + + *string_idx = index; + /* if the split is at index 0 return the whole string length */ + if (index == 0) { + index = length; + } + pango_layout_index_to_pos(layout, index, &pos); + *actual_x = PANGO_PIXELS(pos.x); + + return NSERROR_OK; +} /** * Find the position in a string where an x coordinate falls. @@ -114,9 +172,8 @@ nsfont_position_in_string(const plot_font_style_t *fstyle, size_t *char_offset, int *actual_x) { - int index; PangoFontDescription *desc; - PangoRectangle pos; + nserror res; nsfont_pango_check(); @@ -124,24 +181,18 @@ nsfont_position_in_string(const plot_font_style_t *fstyle, pango_layout_set_font_description(nsfont_pango_layout, desc); pango_font_description_free(desc); - pango_layout_set_text(nsfont_pango_layout, string, length); - - if (pango_layout_xy_to_index(nsfont_pango_layout, - x * PANGO_SCALE, - 0, &index, 0) == FALSE) { - index = length; - } - - pango_layout_index_to_pos(nsfont_pango_layout, index, &pos); - - *char_offset = index; - *actual_x = PANGO_PIXELS(pos.x); + res = layout_position(nsfont_pango_layout, + string, + length, + x, + char_offset, + actual_x); NSLOG(netsurf, DEEPDEBUG, "fstyle: %p string:\"%.*s\", length: %" PRIsizet ", " "search_x: %dpx, offset: %" PRIsizet ", actual_x: %dpx", fstyle, (int)length, string, length, x, *char_offset, *actual_x); - return NSERROR_OK; + return res; } @@ -161,9 +212,9 @@ nsfont_position_in_string(const plot_font_style_t *fstyle, * \note char_offset of 0 must never be returned. * * Returns: - * char_offset giving split point closest to x, where actual_x <= x + * char_offset giving split point closest to x, where actual_x < x * else - * char_offset giving split point closest to x, where actual_x > x + * char_offset giving split point closest to x, where actual_x >= x * * Returning char_offset == length means no split possible */ @@ -172,14 +223,17 @@ nsfont_split(const plot_font_style_t *fstyle, const char *string, size_t length, int x, - size_t *char_offset, + size_t *string_idx, int *actual_x) { - int index = length; - PangoFontDescription *desc; + nserror res; PangoContext *context; PangoLayout *layout; - PangoLayoutLine *line; + PangoFontDescription *desc; + size_t split_len; + int split_x; + size_t str_len; + PangoRectangle pos; context = gdk_pango_context_get(); layout = pango_layout_new(context); @@ -188,37 +242,67 @@ nsfont_split(const plot_font_style_t *fstyle, pango_layout_set_font_description(layout, desc); pango_font_description_free(desc); - pango_layout_set_text(layout, string, length); + res = layout_position(layout, + string, + length, + x, + &split_len, + &split_x); + if (res != NSERROR_OK) { + goto split_done; + } - /* Limit width of layout to the available width */ - pango_layout_set_width(layout, x * PANGO_SCALE); + /* deal with being unable to split */ + if ((split_len < 1) || (split_len >= length)) { + *string_idx = length; + *actual_x = split_x; + goto split_done; + } - /* Request word wrapping */ - pango_layout_set_wrap(layout, PANGO_WRAP_WORD); + /* if string broke on boundary do not attempt to adjust */ + if (string[split_len] == ' ') { + *string_idx = split_len; + *actual_x = split_x; + goto split_done; + } - /* Prevent pango treating linebreak characters as line breaks */ - pango_layout_set_single_paragraph_mode(layout, TRUE); + /* attempt to break string */ + str_len = split_len; - /* Obtain the second line of the layout (if there is one) */ - line = pango_layout_get_line_readonly(layout, 1); - if (line != NULL) { - /* Pango split the text. The line's start_index indicates the - * start of the character after the line break. */ - index = line->start_index; + /* walk backwards through string looking for space to break on */ + while ((string[str_len] != ' ') && + (str_len > 0)) { + str_len--; + } + + /* walk forwards through string looking for space if back failed */ + if (str_len == 0) { + str_len = split_len; + while ((str_len < length) && + (string[str_len] != ' ')) { + str_len++; + } + } + /* include breaking character in match */ + if ((str_len < length) && (string[str_len] == ' ')) { + str_len++; } + *string_idx = str_len; + + pango_layout_index_to_pos(layout, str_len, &pos); + *actual_x = PANGO_PIXELS(pos.x); + +split_done: g_object_unref(layout); g_object_unref(context); - *char_offset = index; - /* Obtain the pixel offset of the split character */ - nsfont_width(fstyle, string, index, actual_x); - NSLOG(netsurf, DEEPDEBUG, - "fstyle: %p string:\"%.*s\", length: %" PRIsizet ", " + "fstyle: %p string:\"%.*s\" / \"%.*s\", length: %" PRIsizet ", " "split_x: %dpx, offset: %" PRIsizet ", actual_x: %dpx", - fstyle, (int)length, string, length, x, *char_offset, *actual_x); - return NSERROR_OK; + fstyle, (int)(*string_idx), string, (int)(length - *string_idx), + string+*string_idx, length, x, *string_idx, *actual_x); + return res; } |