summaryrefslogtreecommitdiff
diff options
authorVincent Sanders <vince@kyllikki.org>2025-02-27 22:56:53 +0000
committerVincent Sanders <vince@kyllikki.org>2025-03-02 11:13:35 +0000
commitfb56aa214c775995fa9ee7e06c6860997e4cb5b7 (patch)
treed2af10b34e2e41504c4c6d42a7c87c2a8316508b
parent4609648b9e0f812d5b765a6dbaf92576e06475ec (diff)
downloadnetsurf-fb56aa214c775995fa9ee7e06c6860997e4cb5b7.tar.gz
netsurf-fb56aa214c775995fa9ee7e06c6860997e4cb5b7.tar.bz2
Improve GTK layout functions to split correctly
-rw-r--r--frontends/gtk/layout_pango.c168
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;
}
close