From ef5ae8e0bc07620b23c60fcab6dbfcb0fe78a6e8 Mon Sep 17 00:00:00 2001 From: seleznevae Date: Sat, 23 Nov 2019 19:36:59 +0300 Subject: [PATCH 1/4] [A] Added simplified version of max width limitation for experiment --- lib/fort.c | 48 ++++++++++++++++------ lib/fort.h | 29 ++++++------- src/cell.c | 5 +++ src/fort.h | 25 +++++------ src/properties.c | 21 +++++----- src/properties.h | 1 + src/string_buffer.c | 20 ++++++++- tests/bb_tests/test_table_properties.c | 57 +++++++++++++++++++++++++- 8 files changed, 153 insertions(+), 53 deletions(-) diff --git a/lib/fort.c b/lib/fort.c index 9888e9e..1e1d383 100644 --- a/lib/fort.c +++ b/lib/fort.c @@ -1874,6 +1874,7 @@ struct f_cell_props { uint32_t properties_flags; unsigned int col_min_width; + unsigned int col_max_width; enum ft_text_alignment align; unsigned int cell_padding_top; unsigned int cell_padding_bottom; @@ -2341,7 +2342,13 @@ size_t hint_width_cell(const f_cell_t *cell, const f_context_t *context, enum f_ if (cell->str_buffer && cell->str_buffer->str.data) { result += buffer_text_visible_width(cell->str_buffer); } + /* todo: + * need to think about a case when MIN / MAX properties are set at the same + * time. + * + */ result = MAX(result, (size_t)get_cell_property_hierarchically(properties, row, column, FT_CPROP_MIN_WIDTH)); + result = MIN(result, (size_t)get_cell_property_hierarchically(properties, row, column, FT_CPROP_MAX_WIDTH)); if (geom == INTERN_REPR_GEOMETRY) { char cell_style_tag[TEXT_STYLE_TAG_MAX_SIZE]; @@ -4281,12 +4288,14 @@ static struct f_cell_props g_default_cell_properties = { FT_ANY_COLUMN, /* cell_col */ /* properties_flags */ - FT_CPROP_MIN_WIDTH | FT_CPROP_TEXT_ALIGN | FT_CPROP_TOP_PADDING + FT_CPROP_MIN_WIDTH | FT_CPROP_MAX_WIDTH + | FT_CPROP_TEXT_ALIGN | FT_CPROP_TOP_PADDING | FT_CPROP_BOTTOM_PADDING | FT_CPROP_LEFT_PADDING | FT_CPROP_RIGHT_PADDING | FT_CPROP_EMPTY_STR_HEIGHT | FT_CPROP_CONT_FG_COLOR | FT_CPROP_CELL_BG_COLOR | FT_CPROP_CONT_BG_COLOR | FT_CPROP_CELL_TEXT_STYLE | FT_CPROP_CONT_TEXT_STYLE, 0, /* col_min_width */ + UINT_MAX, /* col_max_width */ FT_ALIGNED_LEFT, /* align */ 0, /* cell_padding_top */ 0, /* cell_padding_bottom */ @@ -4311,6 +4320,8 @@ static int get_prop_value_if_exists_otherwise_default(const struct f_cell_props switch (property) { case FT_CPROP_MIN_WIDTH: return cell_opts->col_min_width; + case FT_CPROP_MAX_WIDTH: + return cell_opts->col_max_width; case FT_CPROP_TEXT_ALIGN: return cell_opts->align; case FT_CPROP_TOP_PADDING: @@ -4444,6 +4455,11 @@ static f_status set_cell_property_impl(f_cell_props_t *opt, uint32_t property, i if (PROP_IS_SET(property, FT_CPROP_MIN_WIDTH)) { CHECK_NOT_NEGATIVE(value); opt->col_min_width = value; + } else if (PROP_IS_SET(property, FT_CPROP_MAX_WIDTH)) { + if (opt->cell_row != FT_ANY_ROW) + goto fort_fail; + CHECK_NOT_NEGATIVE(value); + opt->col_max_width = value; } else if (PROP_IS_SET(property, FT_CPROP_TEXT_ALIGN)) { opt->align = (enum ft_text_alignment)value; } else if (PROP_IS_SET(property, FT_CPROP_TOP_PADDING)) { @@ -4500,16 +4516,6 @@ f_status set_cell_property(f_cell_prop_container_t *cont, size_t row, size_t col return FT_ERROR; return set_cell_property_impl(opt, property, value); - /* - PROP_SET(opt->propertiess, property); - if (PROP_IS_SET(property, FT_CPROP_MIN_WIDTH)) { - opt->col_min_width = value; - } else if (PROP_IS_SET(property, FT_CPROP_TEXT_ALIGN)) { - opt->align = value; - } - - return FT_SUCCESS; - */ } @@ -6458,6 +6464,12 @@ int buffer_printf(f_string_buffer_t *buffer, size_t buffer_row, f_conv_context_t f_table_properties_t *props = context->table_properties; size_t row = context->row; size_t column = context->column; + unsigned max_width_prop = (unsigned)get_cell_property_hierarchically(props, row, column, FT_CPROP_MAX_WIDTH); + size_t padding_left = get_cell_property_hierarchically(props, row, column, FT_CPROP_LEFT_PADDING); + size_t padding_right = get_cell_property_hierarchically(props, row, column, FT_CPROP_RIGHT_PADDING); + /* Max width includes paddings see comments in @hint_width_cell */ + size_t max_width = (max_width_prop == UINT_MAX) ? UINT_MAX : max_width_prop - padding_left - padding_right; + int explicitly_limited = vis_width == max_width; if (buffer == NULL || buffer->str.data == NULL || buffer_row >= buffer_text_visible_height(buffer)) { @@ -6465,7 +6477,9 @@ int buffer_printf(f_string_buffer_t *buffer, size_t buffer_row, f_conv_context_t } size_t content_width = buffer_text_visible_width(buffer); - if (vis_width < content_width) + if (explicitly_limited) + content_width = MIN(max_width, content_width); + if ((vis_width < content_width) && !explicitly_limited) return -1; size_t left = 0; @@ -6496,9 +6510,17 @@ int buffer_printf(f_string_buffer_t *buffer, size_t buffer_row, f_conv_context_t buffer_substring(buffer, buffer_row, &beg, &end, &str_it_width); if (beg == NULL || end == NULL) return -1; - if (str_it_width < 0 || content_width < (size_t)str_it_width) + if (str_it_width < 0 || ((content_width < (size_t)str_it_width) && !explicitly_limited)) return -1; + /* Take into account max width property */ + if (max_width_prop != UINT_MAX) { + /* note: Need to calculate visible width here !!!! */ + if ((size_t)((char *)end - (char *)beg) > max_width) { + end = (char *)beg + max_width; + str_it_width = max_width; + } + } size_t padding = content_width - (size_t)str_it_width; CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, left, FT_SPACE)); diff --git a/lib/fort.h b/lib/fort.h index a45a0e4..89d5adf 100644 --- a/lib/fort.h +++ b/lib/fort.h @@ -46,8 +46,8 @@ SOFTWARE. #define LIBFORT_MAJOR_VERSION 0 #define LIBFORT_MINOR_VERSION 3 -#define LIBFORT_REVISION 0 -#define LIBFORT_VERSION_STR "0.3.0" +#define LIBFORT_REVISION 1 +#define LIBFORT_VERSION_STR "0.3.1" /***************************************************************************** @@ -696,18 +696,19 @@ int ft_set_border_style(ft_table_t *table, const struct ft_border_style *style); * @{ */ #define FT_CPROP_MIN_WIDTH (0x01U << 0) /**< Minimum width */ -#define FT_CPROP_TEXT_ALIGN (0x01U << 1) /**< Text alignment */ -#define FT_CPROP_TOP_PADDING (0x01U << 2) /**< Top padding for cell content */ -#define FT_CPROP_BOTTOM_PADDING (0x01U << 3) /**< Bottom padding for cell content */ -#define FT_CPROP_LEFT_PADDING (0x01U << 4) /**< Left padding for cell content */ -#define FT_CPROP_RIGHT_PADDING (0x01U << 5) /**< Right padding for cell content */ -#define FT_CPROP_EMPTY_STR_HEIGHT (0x01U << 6) /**< Height of empty cell */ -#define FT_CPROP_ROW_TYPE (0x01U << 7) /**< Row type */ -#define FT_CPROP_CONT_FG_COLOR (0x01U << 8) /**< Cell content foreground text color */ -#define FT_CPROP_CELL_BG_COLOR (0x01U << 9) /**< Cell background color */ -#define FT_CPROP_CONT_BG_COLOR (0x01U << 10) /**< Cell content background color */ -#define FT_CPROP_CELL_TEXT_STYLE (0x01U << 11) /**< Cell text style */ -#define FT_CPROP_CONT_TEXT_STYLE (0x01U << 12) /**< Cell content text style */ +#define FT_CPROP_MAX_WIDTH (0x01U << 1) /**< Maximum width */ +#define FT_CPROP_TEXT_ALIGN (0x01U << 2) /**< Text alignment */ +#define FT_CPROP_TOP_PADDING (0x01U << 3) /**< Top padding for cell content */ +#define FT_CPROP_BOTTOM_PADDING (0x01U << 4) /**< Bottom padding for cell content */ +#define FT_CPROP_LEFT_PADDING (0x01U << 5) /**< Left padding for cell content */ +#define FT_CPROP_RIGHT_PADDING (0x01U << 6) /**< Right padding for cell content */ +#define FT_CPROP_EMPTY_STR_HEIGHT (0x01U << 7) /**< Height of empty cell */ +#define FT_CPROP_ROW_TYPE (0x01U << 8) /**< Row type */ +#define FT_CPROP_CONT_FG_COLOR (0x01U << 9) /**< Cell content foreground text color */ +#define FT_CPROP_CELL_BG_COLOR (0x01U << 10) /**< Cell background color */ +#define FT_CPROP_CONT_BG_COLOR (0x01U << 11) /**< Cell content background color */ +#define FT_CPROP_CELL_TEXT_STYLE (0x01U << 12) /**< Cell text style */ +#define FT_CPROP_CONT_TEXT_STYLE (0x01U << 13) /**< Cell content text style */ /** @} */ diff --git a/src/cell.c b/src/cell.c index 522199a..6f69b5e 100644 --- a/src/cell.c +++ b/src/cell.c @@ -85,7 +85,12 @@ size_t hint_width_cell(const f_cell_t *cell, const f_context_t *context, enum f_ if (cell->str_buffer && cell->str_buffer->str.data) { result += buffer_text_visible_width(cell->str_buffer); } + /* todo: + * need to think about a case when MIN / MAX properties are set at the same + * time. + */ result = MAX(result, (size_t)get_cell_property_hierarchically(properties, row, column, FT_CPROP_MIN_WIDTH)); + result = MIN(result, (size_t)get_cell_property_hierarchically(properties, row, column, FT_CPROP_MAX_WIDTH)); if (geom == INTERN_REPR_GEOMETRY) { char cell_style_tag[TEXT_STYLE_TAG_MAX_SIZE]; diff --git a/src/fort.h b/src/fort.h index a47ae64..89d5adf 100644 --- a/src/fort.h +++ b/src/fort.h @@ -696,18 +696,19 @@ int ft_set_border_style(ft_table_t *table, const struct ft_border_style *style); * @{ */ #define FT_CPROP_MIN_WIDTH (0x01U << 0) /**< Minimum width */ -#define FT_CPROP_TEXT_ALIGN (0x01U << 1) /**< Text alignment */ -#define FT_CPROP_TOP_PADDING (0x01U << 2) /**< Top padding for cell content */ -#define FT_CPROP_BOTTOM_PADDING (0x01U << 3) /**< Bottom padding for cell content */ -#define FT_CPROP_LEFT_PADDING (0x01U << 4) /**< Left padding for cell content */ -#define FT_CPROP_RIGHT_PADDING (0x01U << 5) /**< Right padding for cell content */ -#define FT_CPROP_EMPTY_STR_HEIGHT (0x01U << 6) /**< Height of empty cell */ -#define FT_CPROP_ROW_TYPE (0x01U << 7) /**< Row type */ -#define FT_CPROP_CONT_FG_COLOR (0x01U << 8) /**< Cell content foreground text color */ -#define FT_CPROP_CELL_BG_COLOR (0x01U << 9) /**< Cell background color */ -#define FT_CPROP_CONT_BG_COLOR (0x01U << 10) /**< Cell content background color */ -#define FT_CPROP_CELL_TEXT_STYLE (0x01U << 11) /**< Cell text style */ -#define FT_CPROP_CONT_TEXT_STYLE (0x01U << 12) /**< Cell content text style */ +#define FT_CPROP_MAX_WIDTH (0x01U << 1) /**< Maximum width */ +#define FT_CPROP_TEXT_ALIGN (0x01U << 2) /**< Text alignment */ +#define FT_CPROP_TOP_PADDING (0x01U << 3) /**< Top padding for cell content */ +#define FT_CPROP_BOTTOM_PADDING (0x01U << 4) /**< Bottom padding for cell content */ +#define FT_CPROP_LEFT_PADDING (0x01U << 5) /**< Left padding for cell content */ +#define FT_CPROP_RIGHT_PADDING (0x01U << 6) /**< Right padding for cell content */ +#define FT_CPROP_EMPTY_STR_HEIGHT (0x01U << 7) /**< Height of empty cell */ +#define FT_CPROP_ROW_TYPE (0x01U << 8) /**< Row type */ +#define FT_CPROP_CONT_FG_COLOR (0x01U << 9) /**< Cell content foreground text color */ +#define FT_CPROP_CELL_BG_COLOR (0x01U << 10) /**< Cell background color */ +#define FT_CPROP_CONT_BG_COLOR (0x01U << 11) /**< Cell content background color */ +#define FT_CPROP_CELL_TEXT_STYLE (0x01U << 12) /**< Cell text style */ +#define FT_CPROP_CONT_TEXT_STYLE (0x01U << 13) /**< Cell content text style */ /** @} */ diff --git a/src/properties.c b/src/properties.c index d0b3222..05d25d5 100644 --- a/src/properties.c +++ b/src/properties.c @@ -246,12 +246,14 @@ static struct f_cell_props g_default_cell_properties = { FT_ANY_COLUMN, /* cell_col */ /* properties_flags */ - FT_CPROP_MIN_WIDTH | FT_CPROP_TEXT_ALIGN | FT_CPROP_TOP_PADDING + FT_CPROP_MIN_WIDTH | FT_CPROP_MAX_WIDTH + | FT_CPROP_TEXT_ALIGN | FT_CPROP_TOP_PADDING | FT_CPROP_BOTTOM_PADDING | FT_CPROP_LEFT_PADDING | FT_CPROP_RIGHT_PADDING | FT_CPROP_EMPTY_STR_HEIGHT | FT_CPROP_CONT_FG_COLOR | FT_CPROP_CELL_BG_COLOR | FT_CPROP_CONT_BG_COLOR | FT_CPROP_CELL_TEXT_STYLE | FT_CPROP_CONT_TEXT_STYLE, 0, /* col_min_width */ + UINT_MAX, /* col_max_width */ FT_ALIGNED_LEFT, /* align */ 0, /* cell_padding_top */ 0, /* cell_padding_bottom */ @@ -276,6 +278,8 @@ static int get_prop_value_if_exists_otherwise_default(const struct f_cell_props switch (property) { case FT_CPROP_MIN_WIDTH: return cell_opts->col_min_width; + case FT_CPROP_MAX_WIDTH: + return cell_opts->col_max_width; case FT_CPROP_TEXT_ALIGN: return cell_opts->align; case FT_CPROP_TOP_PADDING: @@ -409,6 +413,11 @@ static f_status set_cell_property_impl(f_cell_props_t *opt, uint32_t property, i if (PROP_IS_SET(property, FT_CPROP_MIN_WIDTH)) { CHECK_NOT_NEGATIVE(value); opt->col_min_width = value; + } else if (PROP_IS_SET(property, FT_CPROP_MAX_WIDTH)) { + if (opt->cell_row != FT_ANY_ROW) + goto fort_fail; + CHECK_NOT_NEGATIVE(value); + opt->col_max_width = value; } else if (PROP_IS_SET(property, FT_CPROP_TEXT_ALIGN)) { opt->align = (enum ft_text_alignment)value; } else if (PROP_IS_SET(property, FT_CPROP_TOP_PADDING)) { @@ -465,16 +474,6 @@ f_status set_cell_property(f_cell_prop_container_t *cont, size_t row, size_t col return FT_ERROR; return set_cell_property_impl(opt, property, value); - /* - PROP_SET(opt->propertiess, property); - if (PROP_IS_SET(property, FT_CPROP_MIN_WIDTH)) { - opt->col_min_width = value; - } else if (PROP_IS_SET(property, FT_CPROP_TEXT_ALIGN)) { - opt->align = value; - } - - return FT_SUCCESS; - */ } diff --git a/src/properties.h b/src/properties.h index 48901c0..ba30caa 100644 --- a/src/properties.h +++ b/src/properties.h @@ -34,6 +34,7 @@ struct f_cell_props { uint32_t properties_flags; unsigned int col_min_width; + unsigned int col_max_width; enum ft_text_alignment align; unsigned int cell_padding_top; unsigned int cell_padding_bottom; diff --git a/src/string_buffer.c b/src/string_buffer.c index dfafbcc..80f6260 100644 --- a/src/string_buffer.c +++ b/src/string_buffer.c @@ -590,6 +590,12 @@ int buffer_printf(f_string_buffer_t *buffer, size_t buffer_row, f_conv_context_t f_table_properties_t *props = context->table_properties; size_t row = context->row; size_t column = context->column; + unsigned max_width_prop = (unsigned)get_cell_property_hierarchically(props, row, column, FT_CPROP_MAX_WIDTH); + size_t padding_left = get_cell_property_hierarchically(props, row, column, FT_CPROP_LEFT_PADDING); + size_t padding_right = get_cell_property_hierarchically(props, row, column, FT_CPROP_RIGHT_PADDING); + /* Max width includes paddings see comments in @hint_width_cell */ + size_t max_width = (max_width_prop == UINT_MAX) ? UINT_MAX : max_width_prop - padding_left - padding_right; + int explicitly_limited = vis_width == max_width; if (buffer == NULL || buffer->str.data == NULL || buffer_row >= buffer_text_visible_height(buffer)) { @@ -597,7 +603,9 @@ int buffer_printf(f_string_buffer_t *buffer, size_t buffer_row, f_conv_context_t } size_t content_width = buffer_text_visible_width(buffer); - if (vis_width < content_width) + if (explicitly_limited) + content_width = MIN(max_width, content_width); + if ((vis_width < content_width) && !explicitly_limited) return -1; size_t left = 0; @@ -628,9 +636,17 @@ int buffer_printf(f_string_buffer_t *buffer, size_t buffer_row, f_conv_context_t buffer_substring(buffer, buffer_row, &beg, &end, &str_it_width); if (beg == NULL || end == NULL) return -1; - if (str_it_width < 0 || content_width < (size_t)str_it_width) + if (str_it_width < 0 || ((content_width < (size_t)str_it_width) && !explicitly_limited)) return -1; + /* Take into account max width property */ + if (max_width_prop != UINT_MAX) { + /* note: Need to calculate visible width here !!!! */ + if ((size_t)((char *)end - (char *)beg) > max_width) { + end = (char *)beg + max_width; + str_it_width = max_width; + } + } size_t padding = content_width - (size_t)str_it_width; CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, left, FT_SPACE)); diff --git a/tests/bb_tests/test_table_properties.c b/tests/bb_tests/test_table_properties.c index b3defae..12fcb7f 100644 --- a/tests/bb_tests/test_table_properties.c +++ b/tests/bb_tests/test_table_properties.c @@ -485,7 +485,7 @@ void test_table_cell_properties(void) - WHEN("Set table width and column alignment") { + WHEN("Set table min width and column alignment") { set_test_properties_as_default(); @@ -553,6 +553,61 @@ void test_table_cell_properties(void) ft_destroy_table(table); } + WHEN("Set table max width for a particular cell is impossible") { + table = ft_create_table(); + int status = ft_set_cell_prop(table, 0, 0, FT_CPROP_MAX_WIDTH, 5); + assert_true(status == FT_EINVAL); + ft_destroy_table(table); + } + + WHEN("Set table max width 1") { + set_test_properties_as_default(); + table = ft_create_table(); + ft_write_ln(table, "123456789"); + + int status = FT_SUCCESS; + status |= ft_set_cell_prop(table, FT_ANY_ROW, 0, FT_CPROP_MAX_WIDTH, 5); + assert_true(status == FT_SUCCESS); + + const char *table_str = ft_to_string(table); + assert_true(table_str != NULL); + const char *table_str_etalon = + "+-----+\n" + "| |\n" + "| 123 |\n" + "| |\n" + "+-----+\n"; + assert_str_equal(table_str, table_str_etalon); + ft_destroy_table(table); + } + + WHEN("Set table max width 2") { + set_test_properties_as_default(); + table = ft_create_table(); + ft_write_ln(table, "123456789", "123456789"); + ft_write_ln(table, "123456789", "123456789"); + + int status = FT_SUCCESS; + status |= ft_set_cell_prop(table, FT_ANY_ROW, 0, FT_CPROP_MAX_WIDTH, 5); + status |= ft_set_cell_prop(table, FT_ANY_ROW, 1, FT_CPROP_MAX_WIDTH, 7); + assert_true(status == FT_SUCCESS); + + const char *table_str = ft_to_string(table); + assert_true(table_str != NULL); + const char *table_str_etalon = + "+-----+-------+\n" + "| | |\n" + "| 123 | 12345 |\n" + "| | |\n" + "+-----+-------+\n" + "| | |\n" + "| 123 | 12345 |\n" + "| | |\n" + "+-----+-------+\n"; + assert_str_equal(table_str, table_str_etalon); + ft_destroy_table(table); + } + WHEN("Multiline cell") { set_test_properties_as_default(); From 25605fc2dab3d75870da4a2d01943e3ac6637fbf Mon Sep 17 00:00:00 2001 From: seleznevae Date: Mon, 2 Dec 2019 23:13:38 +0300 Subject: [PATCH 2/4] [R] Refactoring --- examples/1-simple_table.c | 3 +-- lib/fort.c | 36 +++++++++++++++++------------------- src/properties.c | 2 +- src/properties.h | 1 + src/string_buffer.c | 32 +++++++++++++++----------------- 5 files changed, 35 insertions(+), 39 deletions(-) diff --git a/examples/1-simple_table.c b/examples/1-simple_table.c index 7643f4c..9d4dba8 100644 --- a/examples/1-simple_table.c +++ b/examples/1-simple_table.c @@ -8,8 +8,7 @@ int main(void) ft_table_t *table = ft_create_table(); /* Setup header */ -// ft_set_cell_prop(table, 0, FT_ANY_COLUMN, FT_CPROP_ROW_TYPE, FT_ROW_HEADER); - ft_set_cell_prop(table, FT_ANY_ROW, 0, FT_CPROP_ROW_TYPE, FT_ROW_HEADER); + ft_set_cell_prop(table, 0, FT_ANY_COLUMN, FT_CPROP_ROW_TYPE, FT_ROW_HEADER); ft_write_ln(table, "N", "Driver", "Time", "Avg Speed"); ft_write_ln(table, "1", "Ricciardo", "1:25.945", "222.128"); diff --git a/lib/fort.c b/lib/fort.c index 6ccbd56..7e6bdfe 100644 --- a/lib/fort.c +++ b/lib/fort.c @@ -1853,6 +1853,7 @@ void buffer_set_u8strwid_func(int (*u8strwid)(const void *beg, const void *end, #define PROP_UNSET(ft_props, property) ((ft_props) &= ~((uint32_t)(property))) #define TEXT_STYLE_TAG_MAX_SIZE (64 * 2) +#define FT_MAX_WIDTH_UNLIMITED INT_MAX FT_INTERNAL void get_style_tag_for_cell(const f_table_properties_t *props, @@ -2348,7 +2349,6 @@ size_t hint_width_cell(const f_cell_t *cell, const f_context_t *context, enum f_ /* todo: * need to think about a case when MIN / MAX properties are set at the same * time. - * */ result = MAX(result, (size_t)get_cell_property_hierarchically(properties, row, column, FT_CPROP_MIN_WIDTH)); result = MIN(result, (size_t)get_cell_property_hierarchically(properties, row, column, FT_CPROP_MAX_WIDTH)); @@ -4297,7 +4297,7 @@ static struct f_cell_props g_default_cell_properties = { | FT_CPROP_CONT_BG_COLOR | FT_CPROP_CELL_TEXT_STYLE | FT_CPROP_CONT_TEXT_STYLE, 0, /* col_min_width */ - UINT_MAX, /* col_max_width */ + FT_MAX_WIDTH_UNLIMITED, /* col_max_width */ FT_ALIGNED_LEFT, /* align */ 0, /* cell_padding_top */ 0, /* cell_padding_bottom */ @@ -6410,13 +6410,19 @@ size_t buffer_text_visible_width(const f_string_buffer_t *buffer) static void -buffer_substring(const f_string_buffer_t *buffer, size_t buffer_row, const void **begin, const void **end, ptrdiff_t *str_it_width) +buffer_substring(const f_string_buffer_t *buffer, size_t vis_width, size_t buffer_row, const void **begin, const void **end, ptrdiff_t *str_it_width) { switch (buffer->type) { case CHAR_BUF: str_n_substring(buffer->str.cstr, '\n', buffer_row, (const char **)begin, (const char **)end); - if ((*(const char **)begin) && (*(const char **)end)) + if ((*(const char **)begin) && (*(const char **)end)) { *str_it_width = str_iter_width(*(const char **)begin, *(const char **)end); + + if (*str_it_width > vis_width) { + *str_it_width = vis_width; + *end = *begin + vis_width; + } + } break; #ifdef FT_HAVE_WCHAR case W_CHAR_BUF: @@ -6474,8 +6480,9 @@ int buffer_printf(f_string_buffer_t *buffer, size_t buffer_row, f_conv_context_t size_t padding_left = get_cell_property_hierarchically(props, row, column, FT_CPROP_LEFT_PADDING); size_t padding_right = get_cell_property_hierarchically(props, row, column, FT_CPROP_RIGHT_PADDING); /* Max width includes paddings see comments in @hint_width_cell */ - size_t max_width = (max_width_prop == UINT_MAX) ? UINT_MAX : max_width_prop - padding_left - padding_right; - int explicitly_limited = vis_width == max_width; + size_t max_width = (max_width_prop >= FT_MAX_WIDTH_UNLIMITED) ? FT_MAX_WIDTH_UNLIMITED : max_width_prop - padding_left - padding_right; + if (max_width < vis_width) + return -1; if (buffer == NULL || buffer->str.data == NULL || buffer_row >= buffer_text_visible_height(buffer)) { @@ -6483,9 +6490,8 @@ int buffer_printf(f_string_buffer_t *buffer, size_t buffer_row, f_conv_context_t } size_t content_width = buffer_text_visible_width(buffer); - if (explicitly_limited) - content_width = MIN(max_width, content_width); - if ((vis_width < content_width) && !explicitly_limited) + content_width = MIN(max_width, content_width); + if (vis_width < content_width) return -1; size_t left = 0; @@ -6513,20 +6519,12 @@ int buffer_printf(f_string_buffer_t *buffer, size_t buffer_row, f_conv_context_t ptrdiff_t str_it_width = 0; const void *beg = NULL; const void *end = NULL; - buffer_substring(buffer, buffer_row, &beg, &end, &str_it_width); + buffer_substring(buffer, content_width, buffer_row, &beg, &end, &str_it_width); if (beg == NULL || end == NULL) return -1; - if (str_it_width < 0 || ((content_width < (size_t)str_it_width) && !explicitly_limited)) + if (str_it_width < 0 || content_width < (size_t)str_it_width) return -1; - /* Take into account max width property */ - if (max_width_prop != UINT_MAX) { - /* note: Need to calculate visible width here !!!! */ - if ((size_t)((char *)end - (char *)beg) > max_width) { - end = (char *)beg + max_width; - str_it_width = max_width; - } - } size_t padding = content_width - (size_t)str_it_width; CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, left, FT_SPACE)); diff --git a/src/properties.c b/src/properties.c index 05d25d5..529e85d 100644 --- a/src/properties.c +++ b/src/properties.c @@ -253,7 +253,7 @@ static struct f_cell_props g_default_cell_properties = { | FT_CPROP_CONT_BG_COLOR | FT_CPROP_CELL_TEXT_STYLE | FT_CPROP_CONT_TEXT_STYLE, 0, /* col_min_width */ - UINT_MAX, /* col_max_width */ + FT_MAX_WIDTH_UNLIMITED, /* col_max_width */ FT_ALIGNED_LEFT, /* align */ 0, /* cell_padding_top */ 0, /* cell_padding_bottom */ diff --git a/src/properties.h b/src/properties.h index ba30caa..f07c45b 100644 --- a/src/properties.h +++ b/src/properties.h @@ -10,6 +10,7 @@ #define PROP_UNSET(ft_props, property) ((ft_props) &= ~((uint32_t)(property))) #define TEXT_STYLE_TAG_MAX_SIZE (64 * 2) +#define FT_MAX_WIDTH_UNLIMITED INT_MAX FT_INTERNAL void get_style_tag_for_cell(const f_table_properties_t *props, diff --git a/src/string_buffer.c b/src/string_buffer.c index 80f6260..9981869 100644 --- a/src/string_buffer.c +++ b/src/string_buffer.c @@ -530,13 +530,19 @@ size_t buffer_text_visible_width(const f_string_buffer_t *buffer) static void -buffer_substring(const f_string_buffer_t *buffer, size_t buffer_row, const void **begin, const void **end, ptrdiff_t *str_it_width) +buffer_substring(const f_string_buffer_t *buffer, size_t vis_width, size_t buffer_row, const void **begin, const void **end, ptrdiff_t *str_it_width) { switch (buffer->type) { case CHAR_BUF: str_n_substring(buffer->str.cstr, '\n', buffer_row, (const char **)begin, (const char **)end); - if ((*(const char **)begin) && (*(const char **)end)) + if ((*(const char **)begin) && (*(const char **)end)) { *str_it_width = str_iter_width(*(const char **)begin, *(const char **)end); + + if (*str_it_width > vis_width) { + *str_it_width = vis_width; + *end = *begin + vis_width; + } + } break; #ifdef FT_HAVE_WCHAR case W_CHAR_BUF: @@ -594,8 +600,9 @@ int buffer_printf(f_string_buffer_t *buffer, size_t buffer_row, f_conv_context_t size_t padding_left = get_cell_property_hierarchically(props, row, column, FT_CPROP_LEFT_PADDING); size_t padding_right = get_cell_property_hierarchically(props, row, column, FT_CPROP_RIGHT_PADDING); /* Max width includes paddings see comments in @hint_width_cell */ - size_t max_width = (max_width_prop == UINT_MAX) ? UINT_MAX : max_width_prop - padding_left - padding_right; - int explicitly_limited = vis_width == max_width; + size_t max_width = (max_width_prop >= FT_MAX_WIDTH_UNLIMITED) ? FT_MAX_WIDTH_UNLIMITED : max_width_prop - padding_left - padding_right; + if (max_width < vis_width) + return -1; if (buffer == NULL || buffer->str.data == NULL || buffer_row >= buffer_text_visible_height(buffer)) { @@ -603,9 +610,8 @@ int buffer_printf(f_string_buffer_t *buffer, size_t buffer_row, f_conv_context_t } size_t content_width = buffer_text_visible_width(buffer); - if (explicitly_limited) - content_width = MIN(max_width, content_width); - if ((vis_width < content_width) && !explicitly_limited) + content_width = MIN(max_width, content_width); + if (vis_width < content_width) return -1; size_t left = 0; @@ -633,20 +639,12 @@ int buffer_printf(f_string_buffer_t *buffer, size_t buffer_row, f_conv_context_t ptrdiff_t str_it_width = 0; const void *beg = NULL; const void *end = NULL; - buffer_substring(buffer, buffer_row, &beg, &end, &str_it_width); + buffer_substring(buffer, content_width, buffer_row, &beg, &end, &str_it_width); if (beg == NULL || end == NULL) return -1; - if (str_it_width < 0 || ((content_width < (size_t)str_it_width) && !explicitly_limited)) + if (str_it_width < 0 || content_width < (size_t)str_it_width) return -1; - /* Take into account max width property */ - if (max_width_prop != UINT_MAX) { - /* note: Need to calculate visible width here !!!! */ - if ((size_t)((char *)end - (char *)beg) > max_width) { - end = (char *)beg + max_width; - str_it_width = max_width; - } - } size_t padding = content_width - (size_t)str_it_width; CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, left, FT_SPACE)); From 668c0b4a57abc28c7174af264d65cfebd5357fb9 Mon Sep 17 00:00:00 2001 From: seleznevae Date: Tue, 3 Dec 2019 22:33:33 +0300 Subject: [PATCH 3/4] [F] Fix compilation warning --- lib/fort.c | 4 ++-- src/string_buffer.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/fort.c b/lib/fort.c index 238b5ac..eed5298 100644 --- a/lib/fort.c +++ b/lib/fort.c @@ -6418,9 +6418,9 @@ buffer_substring(const f_string_buffer_t *buffer, size_t vis_width, size_t buffe if ((*(const char **)begin) && (*(const char **)end)) { *str_it_width = str_iter_width(*(const char **)begin, *(const char **)end); - if (*str_it_width > vis_width) { + if (*str_it_width > (ptrdiff_t)vis_width) { *str_it_width = vis_width; - *end = *begin + vis_width; + *end = (char *)*begin + vis_width; } } break; diff --git a/src/string_buffer.c b/src/string_buffer.c index 9981869..00724ed 100644 --- a/src/string_buffer.c +++ b/src/string_buffer.c @@ -538,9 +538,9 @@ buffer_substring(const f_string_buffer_t *buffer, size_t vis_width, size_t buffe if ((*(const char **)begin) && (*(const char **)end)) { *str_it_width = str_iter_width(*(const char **)begin, *(const char **)end); - if (*str_it_width > vis_width) { + if (*str_it_width > (ptrdiff_t)vis_width) { *str_it_width = vis_width; - *end = *begin + vis_width; + *end = (char *)*begin + vis_width; } } break; From e4f29e4594f728b97967acc89f15ebfec8a6143a Mon Sep 17 00:00:00 2001 From: seleznevae Date: Thu, 12 Dec 2019 23:29:45 +0300 Subject: [PATCH 4/4] [A] Added implementation of max_width property for utf-8 and wchar_t --- lib/fort.c | 52 +++++++++++- src/string_buffer.c | 16 +++- src/utf8.h | 36 +++++++++ tests/bb_tests/test_table_properties.c | 106 +++++++++++++++++++++++++ 4 files changed, 206 insertions(+), 4 deletions(-) diff --git a/lib/fort.c b/lib/fort.c index eed5298..3c7ecee 100644 --- a/lib/fort.c +++ b/lib/fort.c @@ -486,6 +486,12 @@ utf8_nonnull utf8_pure utf8_weak size_t utf8len(const void *str); // Visible width of utf8string. utf8_nonnull utf8_pure utf8_weak size_t utf8width(const void *str); +// Forward pointer by `vis_w` visible codepoints +// (or less if end of string is encountered). Sets `width` to visible width of +// string between `str` and returned value. +utf8_nonnull utf8_pure utf8_weak const void *utf8forw(const void *str, + size_t vis_w, size_t *width); + // Visible width of codepoint. utf8_nonnull utf8_pure utf8_weak int utf8cwidth(utf8_int32_t c); @@ -909,6 +915,36 @@ size_t utf8width(const void *str) return length; } +const void *utf8forw(const void *str, size_t vis_w, size_t *width) +{ + size_t length = 0; + utf8_int32_t c = 0; + /* We need to store previous value of str + * because some codepoints may occupy more than + * one symbol and during iteration we may exceed vis_w. + */ + const void *prev_str = str; + size_t old_length = 0; + + str = utf8codepoint(str, &c); + while (c != 0) { + old_length = length; + length += utf8cwidth(c); + if (length > vis_w) + break; + prev_str = str; + str = utf8codepoint(str, &c); + } + + if (length > vis_w) { + *width = old_length; + return prev_str; + } else { + *width = length; + return str; + } +} + int utf8ncasecmp(const void *src1, const void *src2, size_t n) { utf8_int32_t src1_cp, src2_cp, src1_orig_cp, src2_orig_cp; @@ -6427,15 +6463,27 @@ buffer_substring(const f_string_buffer_t *buffer, size_t vis_width, size_t buffe #ifdef FT_HAVE_WCHAR case W_CHAR_BUF: wstr_n_substring(buffer->str.wstr, L'\n', buffer_row, (const wchar_t **)begin, (const wchar_t **)end); - if ((*(const wchar_t **)begin) && (*(const wchar_t **)end)) + if ((*(const wchar_t **)begin) && (*(const wchar_t **)end)) { *str_it_width = wcs_iter_width(*(const wchar_t **)begin, *(const wchar_t **)end); + + if (*str_it_width > (ptrdiff_t)vis_width) { + *str_it_width = vis_width; + *end = (wchar_t *)*begin + vis_width; + } + } break; #endif /* FT_HAVE_WCHAR */ #ifdef FT_HAVE_UTF8 case UTF8_BUF: utf8_n_substring(buffer->str.u8str, '\n', buffer_row, begin, end); - if ((*(const char **)begin) && (*(const char **)end)) + if ((*(const char **)begin) && (*(const char **)end)) { *str_it_width = utf8_width(*begin, *end); + + if (*str_it_width > (ptrdiff_t)vis_width) { + *end = utf8forw((const void *)*begin, vis_width, &vis_width); + *str_it_width = vis_width; + } + } break; #endif /* FT_HAVE_UTF8 */ default: diff --git a/src/string_buffer.c b/src/string_buffer.c index 00724ed..9e49bda 100644 --- a/src/string_buffer.c +++ b/src/string_buffer.c @@ -547,15 +547,27 @@ buffer_substring(const f_string_buffer_t *buffer, size_t vis_width, size_t buffe #ifdef FT_HAVE_WCHAR case W_CHAR_BUF: wstr_n_substring(buffer->str.wstr, L'\n', buffer_row, (const wchar_t **)begin, (const wchar_t **)end); - if ((*(const wchar_t **)begin) && (*(const wchar_t **)end)) + if ((*(const wchar_t **)begin) && (*(const wchar_t **)end)) { *str_it_width = wcs_iter_width(*(const wchar_t **)begin, *(const wchar_t **)end); + + if (*str_it_width > (ptrdiff_t)vis_width) { + *str_it_width = vis_width; + *end = (wchar_t *)*begin + vis_width; + } + } break; #endif /* FT_HAVE_WCHAR */ #ifdef FT_HAVE_UTF8 case UTF8_BUF: utf8_n_substring(buffer->str.u8str, '\n', buffer_row, begin, end); - if ((*(const char **)begin) && (*(const char **)end)) + if ((*(const char **)begin) && (*(const char **)end)) { *str_it_width = utf8_width(*begin, *end); + + if (*str_it_width > (ptrdiff_t)vis_width) { + *end = utf8forw((const void *)*begin, vis_width, &vis_width); + *str_it_width = vis_width; + } + } break; #endif /* FT_HAVE_UTF8 */ default: diff --git a/src/utf8.h b/src/utf8.h index b1ec66e..f76d5ee 100644 --- a/src/utf8.h +++ b/src/utf8.h @@ -121,6 +121,12 @@ utf8_nonnull utf8_pure utf8_weak size_t utf8len(const void *str); // Visible width of utf8string. utf8_nonnull utf8_pure utf8_weak size_t utf8width(const void *str); +// Forward pointer by `vis_w` visible codepoints +// (or less if end of string is encountered). Sets `width` to visible width of +// string between `str` and returned value. +utf8_nonnull utf8_pure utf8_weak const void *utf8forw(const void *str, + size_t vis_w, size_t *width); + // Visible width of codepoint. utf8_nonnull utf8_pure utf8_weak int utf8cwidth(utf8_int32_t c); @@ -544,6 +550,36 @@ size_t utf8width(const void *str) return length; } +const void *utf8forw(const void *str, size_t vis_w, size_t *width) +{ + size_t length = 0; + utf8_int32_t c = 0; + /* We need to store previous value of str + * because some codepoints may occupy more than + * one symbol and during iteration we may exceed vis_w. + */ + const void *prev_str = str; + size_t old_length = 0; + + str = utf8codepoint(str, &c); + while (c != 0) { + old_length = length; + length += utf8cwidth(c); + if (length > vis_w) + break; + prev_str = str; + str = utf8codepoint(str, &c); + } + + if (length > vis_w) { + *width = old_length; + return prev_str; + } else { + *width = length; + return str; + } +} + int utf8ncasecmp(const void *src1, const void *src2, size_t n) { utf8_int32_t src1_cp, src2_cp, src1_orig_cp, src2_orig_cp; diff --git a/tests/bb_tests/test_table_properties.c b/tests/bb_tests/test_table_properties.c index 12fcb7f..e227af6 100644 --- a/tests/bb_tests/test_table_properties.c +++ b/tests/bb_tests/test_table_properties.c @@ -581,6 +581,53 @@ void test_table_cell_properties(void) ft_destroy_table(table); } +#ifdef FT_HAVE_WCHAR + WHEN("Set table max width 1") { + set_test_properties_as_default(); + table = ft_create_table(); + ft_wwrite_ln(table, L"123456789"); + + int status = FT_SUCCESS; + status |= ft_set_cell_prop(table, FT_ANY_ROW, 0, FT_CPROP_MAX_WIDTH, 5); + assert_true(status == FT_SUCCESS); + + const wchar_t *table_str = ft_to_wstring(table); + assert_true(table_str != NULL); + const wchar_t *table_str_etalon = + L"+-----+\n" + L"| |\n" + L"| 123 |\n" + L"| |\n" + L"+-----+\n"; + assert_wcs_equal(table_str, table_str_etalon); + ft_destroy_table(table); + } +#endif /* ifdef FT_HAVE_WCHAR */ + +#ifdef FT_HAVE_UTF8 + WHEN("Set table max width 1") { + set_test_properties_as_default(); + table = ft_create_table(); + ft_u8write_ln(table, "視野無限廣窗外有"); + + int status = FT_SUCCESS; + status |= ft_set_cell_prop(table, FT_ANY_ROW, 0, FT_CPROP_MAX_WIDTH, 5); + assert_true(status == FT_SUCCESS); + + const char *table_str = ft_to_u8string(table); + assert_true(table_str != NULL); + const char *table_str_etalon = + "+-----+\n" + "| |\n" + "| 視 |\n" + "| |\n" + "+-----+\n"; + assert_str_equal(table_str, table_str_etalon); + ft_destroy_table(table); + } +#endif /* ifdef FT_HAVE_UTF8 */ + + WHEN("Set table max width 2") { set_test_properties_as_default(); table = ft_create_table(); @@ -608,6 +655,65 @@ void test_table_cell_properties(void) ft_destroy_table(table); } +#ifdef FT_HAVE_WCHAR + WHEN("Set table max width 2") { + set_test_properties_as_default(); + table = ft_create_table(); + ft_wwrite_ln(table, L"123456789", L"123456789"); + ft_wwrite_ln(table, L"123456789", L"123456789"); + + int status = FT_SUCCESS; + status |= ft_set_cell_prop(table, FT_ANY_ROW, 0, FT_CPROP_MAX_WIDTH, 5); + status |= ft_set_cell_prop(table, FT_ANY_ROW, 1, FT_CPROP_MAX_WIDTH, 7); + assert_true(status == FT_SUCCESS); + + const wchar_t *table_str = ft_to_wstring(table); + assert_true(table_str != NULL); + const wchar_t *table_str_etalon = + L"+-----+-------+\n" + L"| | |\n" + L"| 123 | 12345 |\n" + L"| | |\n" + L"+-----+-------+\n" + L"| | |\n" + L"| 123 | 12345 |\n" + L"| | |\n" + L"+-----+-------+\n"; + assert_wcs_equal(table_str, table_str_etalon); + ft_destroy_table(table); + } +#endif /* ifdef FT_HAVE_WCHAR */ + +#ifdef FT_HAVE_UTF8 + WHEN("Set table max width 2") { + set_test_properties_as_default(); + table = ft_create_table(); + ft_u8write_ln(table, "視野無限廣窗外有", "視野無限廣窗外有"); + + int status = FT_SUCCESS; + /* We test 2 different cases: + * 1 - trailing wide character doesn't fit into odd max width and therefore + * library have to add a space at the end + * 2 - wide character fits into the even max width + */ + status |= ft_set_cell_prop(table, FT_ANY_ROW, 0, FT_CPROP_MAX_WIDTH, 5); + status |= ft_set_cell_prop(table, FT_ANY_ROW, 1, FT_CPROP_MAX_WIDTH, 6); + assert_true(status == FT_SUCCESS); + + const char *table_str = ft_to_u8string(table); + assert_true(table_str != NULL); + const char *table_str_etalon = + "+-----+------+\n" + "| | |\n" + "| 視 | 視野 |\n" + "| | |\n" + "+-----+------+\n"; + assert_str_equal(table_str, table_str_etalon); + ft_destroy_table(table); + } +#endif /* ifdef FT_HAVE_UTF8 */ + + WHEN("Multiline cell") { set_test_properties_as_default();