summaryrefslogtreecommitdiff
path: root/src/select
diff options
context:
space:
mode:
authorVincent Sanders <vince@kyllikki.org>2021-02-05 00:04:35 +0000
committerVincent Sanders <vince@kyllikki.org>2021-02-05 00:04:35 +0000
commit131bb27e3bf9d117ede308dd968814204f72e64c (patch)
tree40f5ab13d8abcc06804a509893d6ba029ad2795e /src/select
parente8238ac2dab2a9c8cd31e7f66504218cf90c4d23 (diff)
downloadlibcss-131bb27e3bf9d117ede308dd968814204f72e64c.tar.gz
libcss-131bb27e3bf9d117ede308dd968814204f72e64c.tar.bz2
add text formatting of values with list styles
Diffstat (limited to 'src/select')
-rw-r--r--src/select/Makefile2
-rw-r--r--src/select/format_list_style.c542
2 files changed, 543 insertions, 1 deletions
diff --git a/src/select/Makefile b/src/select/Makefile
index 025ad39..8b47673 100644
--- a/src/select/Makefile
+++ b/src/select/Makefile
@@ -2,6 +2,6 @@
select_generator:
python3 src/select/select_generator.py
-DIR_SOURCES := arena.c computed.c dispatch.c hash.c select.c font_face.c
+DIR_SOURCES := arena.c computed.c dispatch.c hash.c select.c font_face.c format_list_style.c
include $(NSBUILD)/Makefile.subdir
diff --git a/src/select/format_list_style.c b/src/select/format_list_style.c
new file mode 100644
index 0000000..8fff04a
--- /dev/null
+++ b/src/select/format_list_style.c
@@ -0,0 +1,542 @@
+/*
+ * This file is part of LibCSS
+ * Licensed under the MIT License,
+ * http://www.opensource.org/licenses/mit-license.php
+ * Copyright 2021 Vincent Sanders <vince@netsurf-browser.org>
+ */
+
+#include "select/propget.h"
+#include "utils/utils.h"
+
+#define SYMBOL_SIZE 5
+typedef char symbol_t[SYMBOL_SIZE];
+
+struct list_counter_style {
+ const char *name; /**< style name for debug purposes */
+ struct {
+ const int start; /**< first acceptable value for this style */
+ const int end; /**< last acceptable value for this style */
+ } range;
+ struct {
+ const unsigned int length;
+ const symbol_t value;
+ } pad;
+ const char *prefix;
+ const char *postfix;
+ const symbol_t *symbols; /**< array of symbols which represent this style */
+ const int *weights; /**< symbol weights for additive schemes */
+ const size_t items; /**< items in symbol and weight table */
+ size_t (*calc)(uint8_t *ares, const size_t alen, int value, const struct list_counter_style *cstyle); /**< function to calculate the system */
+};
+
+/**
+ * Copy a null-terminated UTF-8 string to buffer at offset, if there is space
+ *
+ * \param[in] buf The output buffer
+ * \param[in] buflen The length of \a buf
+ * \param[in] pos Current position in \a buf
+ * \param[in] str The string to copy into \a buf
+ * \return The number of bytes needed in the output buffer which may be
+ * larger than \a buflen but the buffer will not be overrun
+ */
+static inline size_t
+copy_string(char *buf, const size_t buflen, size_t pos, const char *str)
+{
+ size_t sidx = 0; /* current string index */
+
+ while (str[sidx] != '\0') {
+ if (pos < buflen) {
+ buf[pos] = str[sidx];
+ }
+ pos++;
+ sidx++;
+ }
+
+ return sidx;
+}
+
+/**
+ * Copy a UTF-8 symbol to buffer at offset, if there is space
+ *
+ * \param[in] buf The output buffer
+ * \param[in] buflen The length of \a buf
+ * \param[in] pos Current position in \a buf
+ * \param[in] symbol The symbol to copy into \a buf
+ * \return The number of bytes needed in the output buffer which may be
+ * larger than \a buflen but the buffer will not be overrun
+ */
+static inline size_t
+copy_symbol(char *buf, const size_t buflen, size_t pos, const symbol_t symbol)
+{
+ size_t sidx = 0; /* current symbol index */
+
+ while ((sidx < sizeof(symbol_t)) && (symbol[sidx] != '\0')) {
+ if (pos < buflen) {
+ buf[pos] = symbol[sidx];
+ }
+ pos++;
+ sidx++;
+ }
+
+ return sidx;
+}
+
+/**
+ * maps alphabet values to output values with a symbol table
+ *
+ * Takes a list of alphabet values and for each one outputs the
+ * compete symbol (in utf8) to an output buffer.
+ *
+ * \param buf The oputput buffer
+ * \param buflen the length of \a buf
+ * \param aval array of alphabet values
+ * \param alen The number of values in \a alen
+ * \param symtab The symbol table
+ * \param symtablen The number of symbols in \a symtab
+ * \return The number of bytes needed in the output buffer whichmay be
+ * larger than \a buflen but the buffer will not be overrun
+ */
+static size_t
+map_aval_to_symbols(char *buf, const size_t buflen,
+ const uint8_t *aval, const size_t alen,
+ const struct list_counter_style *cstyle)
+{
+ size_t oidx = 0;
+ size_t aidx; /* numeral index */
+ const symbol_t postfix = "."; /* default postfix string */
+
+ /* add padding if required */
+ if (alen < cstyle->pad.length) {
+ size_t pidx; /* padding index */
+ for (pidx=cstyle->pad.length - alen; pidx > 0; pidx--) {
+ oidx += copy_symbol(buf, buflen, oidx,
+ cstyle->pad.value);
+ }
+ }
+
+ /* map symbols */
+ for (aidx=0; aidx < alen; aidx++) {
+ oidx += copy_symbol(buf, buflen, oidx,
+ cstyle->symbols[aval[aidx]]);
+ }
+
+ /* postfix */
+ oidx += copy_string(buf, buflen, oidx,
+ (cstyle->postfix != NULL) ?
+ cstyle->postfix : postfix);
+
+ return oidx;
+}
+
+
+/**
+ * generate numeric symbol values
+ *
+ * fills array with numeric values that represent the input value
+ *
+ * \param ares Buffer to recive the converted values
+ * \param alen the length of \a ares buffer
+ * \param value The value to convert
+ * \param slen The number of symbols in the alphabet
+ * \return The length a complete conversion which may be larger than \a alen
+ */
+static size_t
+calc_numeric_system(uint8_t *ares,
+ const size_t alen,
+ int value,
+ const struct list_counter_style *cstyle)
+{
+ size_t idx = 0;
+ uint8_t *first;
+ uint8_t *last;
+
+ /* generate alphabet values in ascending order */
+ while (value > 0) {
+ if (idx < alen) {
+ ares[idx] = value % cstyle->items;
+ }
+ idx++;
+ value = value / cstyle->items;
+ }
+
+ /* put the values in decending order */
+ first = ares;
+ if (idx < alen) {
+ last = first + (idx - 1);
+ } else {
+ last = first + (alen - 1);
+ }
+ while (first < last) {
+ *first ^= *last;
+ *last ^= *first;
+ *first ^= *last;
+ first++;
+ last--;
+ }
+
+ return idx;
+}
+
+
+/**
+ * generate addative symbol values
+ *
+ * fills array with numeric values that represent the input value
+ *
+ * \param ares Buffer to recive the converted values
+ * \param alen the length of \a ares buffer
+ * \param value The value to convert
+ * \param wlen The number of weights
+ * \return The length a complete conversion which may be larger than \a alen
+ */
+static size_t
+calc_additive_system(uint8_t *ares,
+ const size_t alen,
+ int value,
+ const struct list_counter_style *cstyle)
+{
+ size_t widx; /* weight index */
+ size_t aidx = 0;
+ size_t idx;
+ size_t times; /* number of times a weight occours */
+
+ /* iterate over the available weights */
+ for (widx = 0; widx < cstyle->items;widx++) {
+ times = value / cstyle->weights[widx];
+ if (times > 0) {
+ for (idx=0;idx < times;idx++) {
+ if (aidx < alen) {
+ ares[aidx] = widx;
+ }
+ aidx++;
+ }
+
+ value -= times * cstyle->weights[widx];
+ }
+ }
+
+ return aidx;
+}
+
+
+/**
+ * generate alphabet symbol values for latin and greek labelling
+ *
+ * fills array with alphabet values suitable for the input value
+ *
+ * \param ares Buffer to recive the converted values
+ * \param alen the length of \a ares buffer
+ * \param value The value to convert
+ * \param slen The number of symbols in the alphabet
+ * \return The length a complete conversion which may be larger than \a alen
+ */
+static size_t
+calc_alphabet_system(uint8_t *ares,
+ const size_t alen,
+ int value,
+ const struct list_counter_style *cstyle)
+{
+ size_t idx = 0;
+ uint8_t *first;
+ uint8_t *last;
+
+ /* generate alphabet values in ascending order */
+ while (value > 0) {
+ --value;
+ if (idx < alen) {
+ ares[idx] = value % cstyle->items;
+ }
+ idx++;
+ value = value / cstyle->items;
+ }
+
+ /* put the values in decending order */
+ first = ares;
+ if (idx < alen) {
+ last = first + (idx - 1);
+ } else {
+ last = first + (alen - 1);
+ }
+ while (first < last) {
+ *first ^= *last;
+ *last ^= *first;
+ *first ^= *last;
+ first++;
+ last--;
+ }
+
+ return idx;
+}
+
+
+/**
+ * Roman numeral conversion
+ *
+ * \return The number of numerals that are nesesary for full output
+ */
+static size_t
+calc_roman_system(uint8_t *buf,
+ const size_t maxlen,
+ int value,
+ const struct list_counter_style *cstyle)
+{
+ const int S[] = { 0, 2, 4, 2, 4, 2, 4 };
+ const int D[] = { 1000, 500, 100, 50, 10, 5, 1 };
+ const size_t L = sizeof(D) / sizeof(int) - 1;
+ size_t k = 0; /* index into output buffer */
+ unsigned int i = 0; /* index into maps */
+ int r, r2;
+
+ UNUSED(cstyle);
+
+ assert(cstyle->items == 7);
+
+ while (value > 0) {
+ if (D[i] <= value) {
+ r = value / D[i];
+ value = value - (r * D[i]);
+ if (i < L) {
+ /* lookahead */
+ r2 = value / D[i+1];
+ }
+ if (i < L && r2 >= S[i+1]) {
+ /* will violate repeat boundary on next pass */
+ value = value - (r2 * D[i+1]);
+ if (k < maxlen) buf[k++] = i+1;
+ if (k < maxlen) buf[k++] = i-1;
+ } else if (S[i] && r >= S[i]) {
+ /* violated repeat boundary on this pass */
+ if (k < maxlen) buf[k++] = i;
+ if (k < maxlen) buf[k++] = i-1;
+ } else {
+ while (r-- > 0 && k < maxlen) {
+ buf[k++] = i;
+ }
+ }
+ }
+ i++;
+ }
+ if (k < maxlen) {
+ buf[k] = '\0';
+ }
+ return k;
+}
+
+
+/* tables for all the counter styles */
+
+
+static const symbol_t georgian_symbols[] = {
+ "ჵ",
+ "ჰ", "ჯ", "ჴ", "ხ", "ჭ", "წ", "ძ", "ც", "ჩ",
+ "შ", "ყ", "ღ", "ქ", "ფ", "ჳ", "ტ", "ს", "რ",
+ "ჟ", "პ", "ო", "ჲ", "ნ", "მ", "ლ", "კ", "ი",
+ "თ", "ჱ", "ზ", "ვ", "ე", "დ", "გ", "ბ", "ა",
+};
+static const int georgian_weights[] = {
+ 10000,
+ 9000, 8000, 7000, 6000, 5000, 4000, 3000, 2000, 1000,
+ 900, 800, 700, 600, 500, 400, 300, 200, 100,
+ 90, 80, 70, 60, 50, 40, 30, 20, 10,
+ 9, 8, 7, 6, 5, 4, 3, 2, 1
+};
+static struct list_counter_style lcs_georgian = {
+ .name="georgian",
+ .range.start = 1,
+ .range.end = 19999,
+ .symbols = georgian_symbols,
+ .weights = georgian_weights,
+ .items = (sizeof(georgian_symbols) / SYMBOL_SIZE),
+ .calc = calc_additive_system,
+};
+
+
+static const symbol_t armenian_symbols[] = {
+ "Ք", "Փ", "Ւ", "Ց", "Ր", "Տ", "Վ", "Ս", "Ռ",
+ "Ջ", "Պ", "Չ", "Ո", "Շ", "Ն", "Յ", "Մ", "Ճ",
+ "Ղ", "Ձ", "Հ", "Կ", "Ծ", "Խ", "Լ", "Ի", "Ժ",
+ "Թ", "Ը", "Է", "Զ", "Ե", "Դ", "Գ", "Բ", "Ա"
+};
+static const int armenian_weights[] = {
+ 9000, 8000, 7000, 6000, 5000, 4000, 3000, 2000, 1000,
+ 900, 800, 700, 600, 500, 400, 300, 200, 100,
+ 90, 80, 70, 60, 50, 40, 30, 20, 10,
+ 9, 8, 7, 6, 5, 4, 3, 2, 1
+};
+static struct list_counter_style lcs_armenian = {
+ .name = "armenian",
+ .range.start = 1,
+ .range.end = 9999,
+ .symbols = armenian_symbols,
+ .weights = armenian_weights,
+ .items = (sizeof(armenian_symbols) / SYMBOL_SIZE),
+ .calc = calc_additive_system,
+};
+
+
+static const symbol_t decimal_symbols[] = {
+ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"
+};
+static struct list_counter_style lcs_decimal = {
+ .name = "decimal",
+ .symbols = decimal_symbols,
+ .items = (sizeof(decimal_symbols) / SYMBOL_SIZE),
+ .calc = calc_numeric_system,
+};
+
+
+static struct list_counter_style lcs_decimal_leading_zero = {
+ .name = "decimal-leading-zero",
+ .pad.length = 2,
+ .pad.value = "0",
+ .symbols = decimal_symbols,
+ .items = (sizeof(decimal_symbols) / SYMBOL_SIZE),
+ .calc = calc_numeric_system,
+};
+
+
+static const symbol_t lower_greek_symbols[] = {
+ "α", "β", "γ", "δ", "ε", "ζ", "η", "θ", "ι", "κ",
+ "λ", "μ", "ν", "ξ", "ο", "π", "ρ", "σ", "τ", "υ",
+ "φ", "χ", "ψ", "ω"
+};
+static struct list_counter_style lcs_lower_greek = {
+ .name="lower-greek",
+ .symbols = lower_greek_symbols,
+ .items = (sizeof(lower_greek_symbols) / SYMBOL_SIZE),
+ .calc = calc_alphabet_system,
+};
+
+
+static const symbol_t upper_alpha_symbols[] = {
+ "A", "B", "C", "D", "E", "F", "G", "H", "I", "J",
+ "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T",
+ "U", "V", "W", "X", "Y", "Z"
+};
+static struct list_counter_style lcs_upper_alpha = {
+ .name="upper-alpha",
+ .symbols = upper_alpha_symbols,
+ .items = (sizeof(upper_alpha_symbols) / SYMBOL_SIZE),
+ .calc = calc_alphabet_system,
+};
+
+
+static const symbol_t lower_alpha_symbols[] = {
+ "a", "b", "c", "d", "e", "f", "g", "h", "i", "j",
+ "k", "l", "m", "n", "o", "p", "q", "r", "s", "t",
+ "u", "v", "w", "x", "y", "z"
+};
+static struct list_counter_style lcs_lower_alpha = {
+ .name="lower-alpha",
+ .symbols = lower_alpha_symbols,
+ .items = (sizeof(lower_alpha_symbols) / SYMBOL_SIZE),
+ .calc = calc_alphabet_system,
+};
+
+
+static const symbol_t upper_roman_symbols[] = {
+ "M", "D", "C", "L", "X", "V", "I"
+};
+static struct list_counter_style lcs_upper_roman = {
+ .name="upper-roman",
+ .symbols = upper_roman_symbols,
+ .items = (sizeof(upper_roman_symbols) / SYMBOL_SIZE),
+ .calc = calc_roman_system,
+};
+
+
+static const symbol_t lower_roman_symbols[] = {
+ "m", "d", "c", "l", "x", "v", "i"
+};
+static struct list_counter_style lcs_lower_roman = {
+ .name="lower-roman",
+ .symbols = lower_roman_symbols,
+ .items = (sizeof(lower_roman_symbols) / SYMBOL_SIZE),
+ .calc = calc_roman_system,
+};
+
+#if 0
+static const symbol_t lower_hexidecimal_symbols[] = {
+ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
+ "a", "b", "c", "d", "e", "f"
+};
+static struct list_counter_style lcs_lower_hexidecimal = {
+ .name="lower_hexidecimal",
+ .symbols = lower_hexidecimal_symbols,
+ .items = (sizeof(lower_hexidecimal_symbols) / SYMBOL_SIZE),
+ .calc = calc_numeric_system,
+};
+#endif
+
+
+/* exported interface defined in select.h */
+css_error css_computed_format_list_style(
+ const css_computed_style *style,
+ int value,
+ char *buffer,
+ size_t buffer_length,
+ size_t *format_length)
+{
+ uint8_t type = get_list_style_type(style);
+
+ size_t alen;
+ uint8_t aval[20];
+ struct list_counter_style *cstyle;
+
+ switch (type) {
+ case CSS_LIST_STYLE_TYPE_DECIMAL_LEADING_ZERO:
+ cstyle = &lcs_decimal_leading_zero;
+ break;
+
+ case CSS_LIST_STYLE_TYPE_LOWER_ROMAN:
+ cstyle = &lcs_lower_roman;
+ break;
+
+ case CSS_LIST_STYLE_TYPE_UPPER_ROMAN:
+ cstyle = &lcs_upper_roman;
+ break;
+
+ case CSS_LIST_STYLE_TYPE_LOWER_ALPHA:
+ case CSS_LIST_STYLE_TYPE_LOWER_LATIN:
+ cstyle = &lcs_lower_alpha;
+ break;
+
+ case CSS_LIST_STYLE_TYPE_UPPER_ALPHA:
+ case CSS_LIST_STYLE_TYPE_UPPER_LATIN:
+ cstyle = &lcs_upper_alpha;
+ break;
+
+ case CSS_LIST_STYLE_TYPE_LOWER_GREEK:
+ cstyle = &lcs_lower_greek;
+ break;
+
+ case CSS_LIST_STYLE_TYPE_ARMENIAN:
+ cstyle = &lcs_armenian;
+ break;
+
+ case CSS_LIST_STYLE_TYPE_GEORGIAN:
+ cstyle = &lcs_georgian;
+ break;
+
+ case CSS_LIST_STYLE_TYPE_DECIMAL:
+ default:
+ cstyle = &lcs_decimal;
+ break;
+ }
+
+ alen = cstyle->calc(aval, sizeof(aval), value, cstyle);
+
+ /* ensure it is possible to calculate with the selected system */
+ if ((alen == 0) || (alen >= sizeof(aval))) {
+ /* retry in decimal */
+ alen = lcs_decimal.calc(aval, sizeof(aval), value, &lcs_decimal);
+ if ((alen == 0) || (alen >= sizeof(aval))) {
+ /* failed in decimal, give up */
+ return 0;
+ }
+ }
+
+ *format_length = map_aval_to_symbols(buffer, buffer_length, aval, alen, cstyle);
+
+ return CSS_OK;
+}