From ae8297b785eacc61890b6042c0f89af6f65b3bb3 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Mon, 29 Nov 2021 00:09:50 -0500 Subject: tabs: Add a decimal point This is useful for PANGO_TAB_DECIMAL. Implement this in PangoLayout, in the serializer, and update tests. --- pango/pango-layout-private.h | 1 + pango/pango-layout.c | 48 +++++++++++++++++----- pango/pango-tabs.c | 94 +++++++++++++++++++++++++++++++++++++------ pango/pango-tabs.h | 7 ++++ pango/serializer.c | 10 ++++- tests/layouts/valid-12.layout | 15 ++++--- tests/layouts/valid-13.layout | 15 ++++--- tests/testserialize.c | 11 +++-- 8 files changed, 165 insertions(+), 36 deletions(-) diff --git a/pango/pango-layout-private.h b/pango/pango-layout-private.h index b9f9b137..74c36126 100644 --- a/pango/pango-layout-private.h +++ b/pango/pango-layout-private.h @@ -74,6 +74,7 @@ struct _PangoLayout PangoRectangle logical_rect; PangoRectangle ink_rect; int tab_width; /* Cached width of a tab. -1 == not yet calculated */ + gunichar decimal; int copy_end; diff --git a/pango/pango-layout.c b/pango/pango-layout.c index 743f5d09..473d1785 100644 --- a/pango/pango-layout.c +++ b/pango/pango-layout.c @@ -82,6 +82,7 @@ #include "pango-glyph-item.h" #include #include +#include #include @@ -225,6 +226,7 @@ pango_layout_init (PangoLayout *layout) layout->line_count = 0; layout->tab_width = -1; + layout->decimal = 0; layout->unknown_glyphs_count = -1; layout->wrap = PANGO_WRAP_WORD; @@ -3373,6 +3375,7 @@ get_tab_pos (PangoLayoutLine *line, int index, int *tab_pos, PangoTabAlign *alignment, + gunichar *decimal, gboolean *is_default) { PangoLayout *layout = line->layout; @@ -3407,6 +3410,8 @@ get_tab_pos (PangoLayoutLine *line, if (in_pixels) *tab_pos *= PANGO_SCALE; + + *decimal = pango_tab_array_get_decimal_point (layout->tabs, index); } else if (n_tabs > 0) { @@ -3416,6 +3421,7 @@ get_tab_pos (PangoLayoutLine *line, int tab_width; pango_tab_array_get_tab (layout->tabs, n_tabs - 1, alignment, &last_pos); + *decimal = pango_tab_array_get_decimal_point (layout->tabs, n_tabs - 1); if (n_tabs > 1) pango_tab_array_get_tab (layout->tabs, n_tabs - 2, NULL, &next_to_last_pos); @@ -3439,6 +3445,8 @@ get_tab_pos (PangoLayoutLine *line, { /* No tab array set, so use default tab width */ *tab_pos = layout->tab_width * index; + *alignment = PANGO_TAB_LEFT; + *decimal = 0; } *tab_pos -= offset; @@ -3486,7 +3494,15 @@ static void break_state_set_last_tab (ParaBreakState *state, PangoGlyphString *glyphs, int width, int tab_pos, - PangoTabAlign tab_align); + PangoTabAlign tab_align, + gunichar tab_decimal); + +static void +ensure_decimal (PangoLayout *layout) +{ + if (layout->decimal == 0) + layout->decimal = g_utf8_get_char (localeconv ()->decimal_point); +} static void shape_tab (PangoLayoutLine *line, @@ -3498,6 +3514,7 @@ shape_tab (PangoLayoutLine *line, int current_width; int tab_pos; PangoTabAlign tab_align; + gunichar tab_decimal; current_width = line_width (line); @@ -3522,7 +3539,7 @@ shape_tab (PangoLayoutLine *line, { gboolean is_default; - get_tab_pos (line, i, &tab_pos, &tab_align, &is_default); + get_tab_pos (line, i, &tab_pos, &tab_align, &tab_decimal, &is_default); /* Make sure there is at least a space-width of space between * tab-aligned text and the text before it. However, only do @@ -3533,13 +3550,17 @@ shape_tab (PangoLayoutLine *line, if (tab_pos >= current_width + (is_default ? space_width : 1)) { glyphs->glyphs[0].geometry.width = tab_pos - current_width; - g_debug ("shape_tab: tab %d, align %d, width %d\n", - i, tab_align, glyphs->glyphs[0].geometry.width); break; } } - break_state_set_last_tab (state, glyphs, current_width, tab_pos, tab_align); + if (tab_decimal == 0) + { + ensure_decimal (line->layout); + tab_decimal = line->layout->decimal; + } + + break_state_set_last_tab (state, glyphs, current_width, tab_pos, tab_align, tab_decimal); } static inline gboolean @@ -3628,6 +3649,7 @@ struct _ParaBreakState int last_tab_width; int last_tab_pos; PangoTabAlign last_tab_align; + gunichar last_tab_decimal; }; static void @@ -3635,13 +3657,15 @@ break_state_set_last_tab (ParaBreakState *state, PangoGlyphString *glyphs, int width, int tab_pos, - PangoTabAlign tab_align) + PangoTabAlign tab_align, + gunichar tab_decimal) { state->last_tab = glyphs; state->last_tab_width = width; state->last_tab_pos = tab_pos; state->last_tab_align = tab_align; + state->last_tab_decimal = tab_decimal; } static gboolean @@ -3652,6 +3676,7 @@ static void get_decimal_prefix_width (PangoItem *item, PangoGlyphString *glyphs, const char *text, + gunichar decimal, int *width, gboolean *found) { @@ -3669,7 +3694,7 @@ get_decimal_prefix_width (PangoItem *item, for (i = 0, p = text + item->offset; i < item->num_chars; i++, p = g_utf8_next_char (p)) { - if (g_utf8_get_char (p) == '.') + if (g_utf8_get_char (p) == decimal) { *width += log_widths[i] / 2; *found = TRUE; @@ -3749,7 +3774,7 @@ shape_run (PangoLayoutLine *line, int width; gboolean found; - get_decimal_prefix_width (item, glyphs, layout->text, &width, &found); + get_decimal_prefix_width (item, glyphs, layout->text, state->last_tab_decimal, &width, &found); w -= width; } @@ -3808,7 +3833,7 @@ insert_run (PangoLayoutLine *line, int width; gboolean found; - get_decimal_prefix_width (run->item, run->glyphs, line->layout->text, &width, &found); + get_decimal_prefix_width (run->item, run->glyphs, line->layout->text, state->last_tab_decimal, &width, &found); state->last_tab_width += width; if (found) @@ -3922,6 +3947,11 @@ compute_log_widths (PangoLayout *layout, pango_glyph_item_get_logical_widths (&glyph_item, layout->text, state->log_widths); } +/* If last_tab is set, we've added a tab and remaining_width has been updated to + * account for its origin width, which is last_tab_pos - last_tab_width. shape_run + * updates the tab width, so we need to consider the delta when comparing + * against remaining_width. + */ static int tab_width_change (ParaBreakState *state) { diff --git a/pango/pango-tabs.c b/pango/pango-tabs.c index 47ac95c3..318f305b 100644 --- a/pango/pango-tabs.c +++ b/pango/pango-tabs.c @@ -28,12 +28,9 @@ typedef struct _PangoTab PangoTab; struct _PangoTab { - gint location; /* Offset in pixels of this tab stop - * from the left margin of the text. - */ - PangoTabAlign alignment; /* Where the tab stop appears relative - * to the text. - */ + int location; + PangoTabAlign alignment; + gunichar decimal_point; }; /** @@ -42,7 +39,8 @@ struct _PangoTab * A `PangoTabArray` contains an array of tab stops. * * `PangoTabArray` can be used to set tab stops in a `PangoLayout`. - * Each tab stop has an alignment and a position. + * Each tab stop has an alignment, a position, and optionally + * a character to use as decimal point. */ struct _PangoTabArray { @@ -59,6 +57,7 @@ init_tabs (PangoTabArray *array, gint start, gint end) { array->tabs[start].location = 0; array->tabs[start].alignment = PANGO_TAB_LEFT; + array->tabs[start].decimal_point = 0; ++start; } } @@ -141,6 +140,7 @@ pango_tab_array_new_with_positions (gint size, array->tabs[0].alignment = first_alignment; array->tabs[0].location = first_position; + array->tabs[0].decimal_point = 0; if (size == 1) return array; @@ -155,6 +155,7 @@ pango_tab_array_new_with_positions (gint size, array->tabs[i].alignment = align; array->tabs[i].location = pos; + array->tabs[i].decimal_point = 0; ++i; } @@ -266,9 +267,6 @@ pango_tab_array_resize (PangoTabArray *tab_array, * @location: tab location in Pango units * * Sets the alignment and location of a tab stop. - * - * @alignment must always be %PANGO_TAB_LEFT in the current - * implementation. */ void pango_tab_array_set_tab (PangoTabArray *tab_array, @@ -409,6 +407,9 @@ pango_tab_array_to_string (PangoTabArray *tab_array) g_string_append_printf (s, "%d", tab_array->tabs[i].location); if (tab_array->positions_in_pixels) g_string_append (s, "px"); + + if (tab_array->tabs[i].decimal_point != 0) + g_string_append_printf (s, ":%d", tab_array->tabs[i].decimal_point); } return g_string_free (s, FALSE); @@ -483,10 +484,9 @@ pango_tab_array_from_string (const char *text) pos = g_ascii_strtoll (p, &endp, 10); if (pos < 0 || (pixels && *endp != 'p') || - (!pixels && !g_ascii_isspace (*endp) && *endp != '\0')) goto fail; + (!pixels && !g_ascii_isspace (*endp) && *endp != ':' && *endp != '\0')) goto fail; pango_tab_array_set_tab (array, i, align, pos); - i++; p = (const char *)endp; if (pixels) @@ -494,7 +494,23 @@ pango_tab_array_from_string (const char *text) if (p[0] != 'p' || p[1] != 'x') goto fail; p += 2; } + + if (p[0] == ':') + { + gunichar ch; + + p++; + ch = g_ascii_strtoll (p, &endp, 10); + if (!g_ascii_isspace (*endp) && *endp != '\0') goto fail; + + pango_tab_array_set_decimal_point (array, i, ch); + + p = (const char *)endp; + } + p = skip_whitespace (p); + + i++; } goto success; @@ -506,3 +522,57 @@ fail: success: return array; } + +/** + * pango_tab_array_set_decimal_point: + * @tab_array: a `PangoTabArray` + * @tab_index: the index of a tab stop + * @decimal_point: the decimal point to use + * + * Sets the decimal point to use. + * + * This is only relevant for %PANGO_TAB_DECIMAL. + * + * By default, Pango uses the decimal point according + * to the current locale. + * + * Since: 1.50 + */ +void +pango_tab_array_set_decimal_point (PangoTabArray *tab_array, + int tab_index, + gunichar decimal_point) +{ + g_return_if_fail (tab_array != NULL); + g_return_if_fail (tab_index >= 0); + + if (tab_index >= tab_array->size) + pango_tab_array_resize (tab_array, tab_index + 1); + + tab_array->tabs[tab_index].decimal_point = decimal_point; +} + +/** + * pango_tab_array_get_decimal_point: + * @tab_array: a `PangoTabArray` + * @tab_index: the index of a tab stop + * + * Gets the decimal point to use. + * + * This is only relevant for %PANGO_TAB_DECIMAL. + * + * The default value of 0 means that Pango will use the + * decimal point according to the current locale. + * + * Since: 1.50 + */ +gunichar +pango_tab_array_get_decimal_point (PangoTabArray *tab_array, + int tab_index) +{ + g_return_val_if_fail (tab_array != NULL, 0); + g_return_val_if_fail (tab_index < tab_array->size, 0); + g_return_val_if_fail (tab_index >= 0, 0); + + return tab_array->tabs[tab_index].decimal_point; +} diff --git a/pango/pango-tabs.h b/pango/pango-tabs.h index 81ac1550..0792a36a 100644 --- a/pango/pango-tabs.h +++ b/pango/pango-tabs.h @@ -98,6 +98,13 @@ char * pango_tab_array_to_string (PangoTabArray *tab_array); PANGO_AVAILABLE_IN_1_50 PangoTabArray * pango_tab_array_from_string (const char *text); +PANGO_AVAILABLE_IN_1_50 +void pango_tab_array_set_decimal_point (PangoTabArray *tab_array, + int tab_index, + gunichar decimal_point); +PANGO_AVAILABLE_IN_1_50 +gunichar pango_tab_array_get_decimal_point (PangoTabArray *tab_array, + int tab_index); G_DEFINE_AUTOPTR_CLEANUP_FUNC(PangoTabArray, pango_tab_array_free) diff --git a/pango/serializer.c b/pango/serializer.c index 372fbd8b..fe0fc25c 100644 --- a/pango/serializer.c +++ b/pango/serializer.c @@ -226,6 +226,8 @@ add_tab_array (JsonBuilder *builder, json_builder_add_int_value (builder, pos); json_builder_set_member_name (builder, "alignment"); add_enum_value (builder, PANGO_TYPE_TAB_ALIGN, align, FALSE); + json_builder_set_member_name (builder, "decimal-point"); + json_builder_add_int_value (builder, pango_tab_array_get_decimal_point (tabs, i)); json_builder_end_object (builder); } json_builder_end_array (builder); @@ -1139,7 +1141,8 @@ json_to_tab_array (JsonReader *reader, for (int i = 0; i < json_reader_count_elements (reader); i++) { int pos; - PangoTabAlign align; + PangoTabAlign align = PANGO_TAB_LEFT; + gunichar ch = 0; json_reader_read_element (reader, i); if (json_reader_is_object (reader)) @@ -1156,14 +1159,17 @@ json_to_tab_array (JsonReader *reader, if (align == -1) goto fail; json_reader_end_member (reader); + json_reader_read_member (reader, "decimal-point"); + ch = json_reader_get_int_value (reader); + json_reader_end_member (reader); } else { pos = json_reader_get_int_value (reader); - align = PANGO_TAB_LEFT; } pango_tab_array_set_tab (tabs, i, align, pos); + pango_tab_array_set_decimal_point (tabs, i, ch); json_reader_end_element (reader); } } diff --git a/tests/layouts/valid-12.layout b/tests/layouts/valid-12.layout index bbe9a576..247b374e 100644 --- a/tests/layouts/valid-12.layout +++ b/tests/layouts/valid-12.layout @@ -23,23 +23,28 @@ "positions" : [ { "position" : 0, - "alignment" : "left" + "alignment" : "left", + "decimal-point" : 0 }, { "position" : 50, - "alignment" : "left" + "alignment" : "left", + "decimal-point" : 0 }, { "position" : 100, - "alignment" : "left" + "alignment" : "left", + "decimal-point" : 0 }, { "position" : 150, - "alignment" : "left" + "alignment" : "left", + "decimal-point" : 0 }, { "position" : 200, - "alignment" : "left" + "alignment" : "left", + "decimal-point" : 0 } ] }, diff --git a/tests/layouts/valid-13.layout b/tests/layouts/valid-13.layout index bc20a3c1..b557a278 100644 --- a/tests/layouts/valid-13.layout +++ b/tests/layouts/valid-13.layout @@ -23,23 +23,28 @@ "positions" : [ { "position" : 0, - "alignment" : "left" + "alignment" : "left", + "decimal-point" : 0 }, { "position" : 50, - "alignment" : "left" + "alignment" : "left", + "decimal-point" : 0 }, { "position" : 100, - "alignment" : "left" + "alignment" : "left", + "decimal-point" : 0 }, { "position" : 150, - "alignment" : "left" + "alignment" : "left", + "decimal-point" : 0 }, { "position" : 200, - "alignment" : "left" + "alignment" : "left", + "decimal-point" : 0 } ] }, diff --git a/tests/testserialize.c b/tests/testserialize.c index 325338f1..78424eff 100644 --- a/tests/testserialize.c +++ b/tests/testserialize.c @@ -87,6 +87,7 @@ test_serialize_tab_array (void) " 0 10 ", "20 10", "left:10px right:20px center:30px decimal:40px", + "decimal:10240:94", "" }; const char *roundtripped[] = { @@ -95,6 +96,7 @@ test_serialize_tab_array (void) "0 10", "20 10", "10px right:20px center:30px decimal:40px", + "decimal:10240:94", "" }; const char *invalid[] = { @@ -235,15 +237,18 @@ test_serialize_layout_valid (void) " \"positions\" : [\n" " {\n" " \"position\" : 0,\n" - " \"alignment\" : \"left\"\n" + " \"alignment\" : \"left\",\n" + " \"decimal-point\" : 0\n" " },\n" " {\n" " \"position\" : 50,\n" - " \"alignment\" : \"center\"\n" + " \"alignment\" : \"center\",\n" + " \"decimal-point\" : 0\n" " },\n" " {\n" " \"position\" : 100,\n" - " \"alignment\" : \"right\"\n" + " \"alignment\" : \"decimal\",\n" + " \"decimal-point\" : 94\n" " }\n" " ]\n" " },\n" -- cgit v1.2.1