Commit ae8297b7 authored by Matthias Clasen's avatar Matthias Clasen
Browse files

tabs: Add a decimal point

This is useful for PANGO_TAB_DECIMAL.

Implement this in PangoLayout, in the
serializer, and update tests.
parent a6ed4ff8
......@@ -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;
......
......@@ -82,6 +82,7 @@
#include "pango-glyph-item.h"
#include <string.h>
#include <math.h>
#include <locale.h>
#include <hb-ot.h>
......@@ -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)
{
......
......@@ -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;
}
......@@ -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)
......
......@@ -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);
}
}
......
......@@ -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
}
]
},
......
......@@ -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
}
]
},
......
......@@ -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"
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment