From 1487697cb3d69c8eb86b50b441f4c44fe0a0ff12 Mon Sep 17 00:00:00 2001 From: Richard Wilson Date: Sat, 15 Jan 2005 22:11:53 +0000 Subject: [project @ 2005-01-15 22:11:53 by rjw] Background work for CSS counters (CSS 2.1/12.4) svn path=/import/netsurf/; revision=1449 --- css/css.h | 7 +- makefile | 2 +- render/list.c | 444 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ render/list.h | 39 ++++++ 4 files changed, 490 insertions(+), 2 deletions(-) create mode 100644 render/list.c create mode 100644 render/list.h diff --git a/css/css.h b/css/css.h index 6ac08ded3..cc5e9bbd6 100644 --- a/css/css.h +++ b/css/css.h @@ -112,7 +112,12 @@ typedef enum { CSS_VERTICAL_ALIGN_PERCENT, CSS_VERTICAL_ALIGN_NOT_SET } css_vertical_align_type; - + +struct css_counter { + const char *name; + css_list_style_type style; + const char *separator; /** NULL for counter() */ +}; /** Representation of a complete CSS 2 style. */ struct css_style { diff --git a/makefile b/makefile index a2260a557..991d83c97 100644 --- a/makefile +++ b/makefile @@ -20,7 +20,7 @@ OBJECTS_COMMON = content.o fetch.o fetchcache.o # content/ OBJECTS_COMMON += css.o css_enum.o parser.o ruleset.o scanner.o # css/ OBJECTS_COMMON += box.o form.o html.o html_redraw.o layout.o \ - textplain.o # render/ + list.o textplain.o # render/ OBJECTS_COMMON += messages.o pool.o translit.o url.o utils.o # utils/ OBJECTS_COMMON += imagemap.o loginlist.o options.o tree.o # desktop/ diff --git a/render/list.c b/render/list.c new file mode 100644 index 000000000..fced6416c --- /dev/null +++ b/render/list.c @@ -0,0 +1,444 @@ +/* + * This file is part of NetSurf, http://netsurf.sourceforge.net/ + * Licensed under the GNU General Public License, + * http://www.opensource.org/licenses/gpl-license + * Copyright 2005 Richard Wilson + */ + +/** \file + * HTML lists (implementation). + */ +#include +#include +#include +#include +#include "netsurf/css/css.h" +#include "netsurf/render/list.h" +#include "netsurf/utils/log.h" + +static struct list_counter *list_counter_pool = NULL; +static char list_counter_workspace[16]; + +static const char *list_counter_roman[] = { "I", "IV", "V", "IX", + "X", "XL", "L", "XC", + "C", "CD", "D", "CM", + "M"}; +static const int list_counter_decimal[] = { 1, 4, 5, 9, + 10, 40, 50, 90, + 100, 400, 500, 900, + 1000}; +#define ROMAN_DECIMAL_CONVERSIONS (sizeof(list_counter_decimal) \ + / sizeof(list_counter_decimal[0])) + + +static struct list_counter *render_list_find_counter(const char *name); +static char *render_list_encode_counter(struct list_counter_state *state, + css_list_style_type style); +static char *render_list_encode_roman(int value); + +static void render_list_counter_output(const char *name); + + +/** + * Finds a counter from the current pool, or adds a new one. + * + * \param name the name of the counter to find + * \return the counter, or NULL if it couldn't be found/created. + */ +static struct list_counter *render_list_find_counter(const char *name) { + struct list_counter *counter; + + assert(name); + /* find a current counter */ + for (counter = list_counter_pool; counter; counter = counter->next) + if (!strcmp(name, counter->name)) + return counter; + + /* create a new counter */ + counter = calloc(1, sizeof(struct list_counter)); + if (!counter) { + LOG(("No memory for calloc()")); + return NULL; + } + counter->name = malloc(strlen(name) + 1); + if (!counter->name) { + LOG(("No memory for malloc()")); + return NULL; + } + strcpy(counter->name, name); + counter->next = list_counter_pool; + list_counter_pool = counter; + return counter; +} + + +/** + * Removes all counters from the current pool. + */ +void render_list_destroy_counters(void) { + struct list_counter *counter = list_counter_pool; + struct list_counter *next_counter; + struct list_counter_state *state; + struct list_counter_state *next_state; + + while (counter) { + next_counter = counter->next; + free(counter->name); + state = counter->first; + free(counter); + counter = next_counter; + while (state) { + next_state = state->next; + free(state); + state = next_state; + } + } + list_counter_pool = NULL; +} + + +/** + * Resets a counter in accordance with counter-reset (CSS 2.1/12.4). + * + * \param name the name of the counter to reset + * \param value the value to reset the counter to + * \return true on success, false on failure. + */ +bool render_list_counter_reset(const char *name, int value) { + struct list_counter *counter; + struct list_counter_state *state; + struct list_counter_state *link; + + assert(name); + counter = render_list_find_counter(name); + if (!counter) + return false; + state = calloc(1, sizeof(struct list_counter_state)); + if (!state) { + LOG(("No memory for calloc()")); + return false; + } + state->count = value; + state->parent = counter->state; + counter->state = state; + if (!counter->first) { + counter->first = state; + } else { + for (link = counter->first; link->next; link = link->next); + link->next = state; + } +/* render_list_counter_output(name); +*/ return true; +} + + +/** + * Increments a counter in accordance with counter-increment (CSS 2.1/12.4). + * + * \param name the name of the counter to reset + * \param value the value to increment the counter by + * \return true on success, false on failure. + */ +bool render_list_counter_increment(const char *name, int value) { + struct list_counter *counter; + + assert(name); + counter = render_list_find_counter(name); + if (!counter) + return false; + /* if no counter-reset used, it is assumed the counter has been reset + * to 0 by the root element. */ + if (!counter->state) { + if (counter->first) { + counter->state = counter->first; + counter->state->count = 0; + } else { + render_list_counter_reset(name, 0); + } + } + if (counter->state) + counter->state->count += value; +/* render_list_counter_output(name); +*/ return (counter->state); +} + + +/** + * Ends the scope of a counter. + * + * \param name the name of the counter to end the scope for + * \return true on success, false on failure. + */ +bool render_list_counter_end_scope(const char *name) { + struct list_counter *counter; + + assert(name); + counter = render_list_find_counter(name); + if ((!counter) || (!counter->state)) + return false; + counter->state = counter->state->parent; +/* render_list_counter_output(name); +*/ return true; +} + + +/** + * Returns a textual representation of a counter for counter() or counters() + * (CSS 2.1/12.2). + * + * \param css_counter the counter to convert + * \return a textual representation of the counter, or NULL on failure + */ +char *render_list_counter(struct css_counter *css_counter) { + struct list_counter *counter; + struct list_counter_state *state; + char *compound = NULL; + char *merge, *extend; + + assert(css_counter); + counter = render_list_find_counter(css_counter->name); + if (!counter) { + LOG(("Failed to find/create counter for conversion")); + return NULL; + } + + /* handle counter() first */ + if (!css_counter->separator) + return render_list_encode_counter(counter->state, + css_counter->style); + + /* loop through all states for counters() */ + for (state = counter->first; state; state = state->next) { + merge = render_list_encode_counter(state, + css_counter->style); + if (!merge) { + free(compound); + return NULL; + } + if (!compound) { + compound = merge; + } else { + extend = realloc(compound, strlen(compound) + + strlen(merge) + 1); + if (!extend) { + LOG(("No memory for realloc()")); + free(compound); + free(merge); + return NULL; + } + compound = extend; + strcat(compound, merge); + } + if (state->next) { + merge = realloc(compound, strlen(compound) + + strlen(css_counter->separator) + 1); + if (!merge) { + LOG(("No memory for realloc()")); + free(compound); + return NULL; + } + compound = merge; + strcat(compound, css_counter->separator); + } + } + return compound; +} + + +/** + * Returns a textual representation of a counter state in a specified style. + * + * \param state the counter state to represent + * \param style the counter style to use + * \return a textual representation of the counter state, or NULL on failure + */ +static char *render_list_encode_counter(struct list_counter_state *state, + css_list_style_type style) { + char *result = NULL; + int i; + + /* no counter state means that the counter is currently out of scope */ + if (!state) { + result = malloc(1); + if (!result) + return NULL; + result[0] = '\0'; + return result; + } + + /* perform the relevant encoding to upper case where applicable */ + switch (style) { + case CSS_LIST_STYLE_TYPE_LOWER_ALPHA: + case CSS_LIST_STYLE_TYPE_UPPER_ALPHA: + if (state->count <= 0) + result = calloc(1, 1); + else + result = malloc(2); + if (!result) + return NULL; + if (state->count > 0) { + result[0] = 'A' + (state->count - 1) % 26; + result[1] = '\0'; + } + break; + case CSS_LIST_STYLE_TYPE_DISC: + case CSS_LIST_STYLE_TYPE_CIRCLE: + case CSS_LIST_STYLE_TYPE_SQUARE: + result = malloc(2); + if (!result) + return NULL; + result[0] = '?'; + result[1] = '\0'; + break; + case CSS_LIST_STYLE_TYPE_LOWER_ROMAN: + case CSS_LIST_STYLE_TYPE_UPPER_ROMAN: + result = render_list_encode_roman(state->count); + if (!result) + return NULL; + break; + case CSS_LIST_STYLE_TYPE_DECIMAL: + snprintf(list_counter_workspace, + sizeof list_counter_workspace, + "%i", state->count); + result = malloc(strlen(list_counter_workspace) + 1); + if (!result) + return NULL; + strcpy(result, list_counter_workspace); + break; + case CSS_LIST_STYLE_TYPE_NONE: + result = malloc(1); + if (!result) + return NULL; + result[0] = '\0'; + break; + case CSS_LIST_STYLE_TYPE_INHERIT: + case CSS_LIST_STYLE_TYPE_UNKNOWN: + case CSS_LIST_STYLE_TYPE_NOT_SET: + assert(0); + break; + } + + /* perform case conversion */ + if ((style == CSS_LIST_STYLE_TYPE_LOWER_ALPHA) || + (style == CSS_LIST_STYLE_TYPE_LOWER_ROMAN)) + for (i = 0; result[i]; i++) + result[i] = tolower(result[i]); + + return result; +} + + +/** + * Encodes a value in roman numerals. + * For values that cannot be represented (ie <=0) an empty string is returned. + * + * \param value the value to represent + * \return a string containing the representation, or NULL on failure + */ +static char *render_list_encode_roman(int value) { + int i, overflow, p = 0; + char temp[10]; + char *result; + + /* zero and below is returned as an empty string and not erred as + * if it is counters() will fail to complete other scopes. */ + if (value <= 0) { + result = malloc(1); + if (!result) + return NULL; + result[0] = '\0'; + return result; + } + + /* we only calculate 1->999 and then add a M for each thousand */ + overflow = value / 1000; + value = value % 1000; + + /* work backwards through the array */ + for (i = ROMAN_DECIMAL_CONVERSIONS - 1; i >= 0; i--) { + while (value >= list_counter_decimal[0]) { + if (value - list_counter_decimal[i] < + list_counter_decimal[0] - 1) + break; + value -= list_counter_decimal[i]; + temp[p++] = list_counter_roman[i][0]; + if (i & 0x1) + temp[p++] = list_counter_roman[i][1]; + } + } + temp[p] = '\0'; + + /* create a copy for the caller including thousands */ + result = malloc(p + overflow + 1); + if (!result) + return NULL; + for (i = 0; i < overflow; i++) + result[i] = 'M'; + strcpy(&result[overflow], temp); + return result; +} + + + + + + + + +void render_list_test(void) { + /* example given in CSS2.1/12.4.1 */ + render_list_counter_reset("item", 0); + render_list_counter_increment("item", 1); + render_list_counter_increment("item", 1); + render_list_counter_reset("item", 0); + render_list_counter_increment("item", 1); + render_list_counter_increment("item", 1); + render_list_counter_increment("item", 1); + render_list_counter_reset("item", 0); + render_list_counter_increment("item", 1); + render_list_counter_end_scope("item"); + render_list_counter_reset("item", 0); + render_list_counter_increment("item", 1); + render_list_counter_end_scope("item"); + render_list_counter_increment("item", 1); + render_list_counter_end_scope("item"); + render_list_counter_increment("item", 1); + render_list_counter_increment("item", 1); + render_list_counter_end_scope("item"); + render_list_counter_reset("item", 0); + render_list_counter_increment("item", 1); + render_list_counter_increment("item", 1); + render_list_counter_end_scope("item"); +} + +static void render_list_counter_output(const char *name) { + struct list_counter *counter; + char *result; + struct css_counter css_counter; + + assert(name); + counter = render_list_find_counter(name); + if (!counter) { + fprintf(stderr, "Unable to create counter '%s'\n", name); + return; + } + + css_counter.name = name; + css_counter.style = CSS_LIST_STYLE_TYPE_LOWER_ALPHA; + css_counter.separator = NULL; + result = render_list_counter(&css_counter); + if (!result) { + fprintf(stderr, "Failed to output counter('%s')\n", name); + } else { + fprintf(stderr, "counter('%s') is '%s'\n", name, result); + free(result); + } + css_counter.separator = "."; + result = render_list_counter(&css_counter); + if (!result) { + fprintf(stderr, "Failed to output counters('%s', '.')\n", name); + } else { + fprintf(stderr, "counters('%s', '.') is '%s'\n", name, result); + free(result); + } +} diff --git a/render/list.h b/render/list.h new file mode 100644 index 000000000..f168bd043 --- /dev/null +++ b/render/list.h @@ -0,0 +1,39 @@ +/* + * This file is part of NetSurf, http://netsurf.sourceforge.net/ + * Licensed under the GNU General Public License, + * http://www.opensource.org/licenses/gpl-license + * Copyright 2005 Richard Wilson + */ + +/** \file + * HTML lists (interface). + */ + +#ifndef _NETSURF_RENDER_LIST_H_ +#define _NETSURF_RENDER_LIST_H_ + +#include + +struct list_counter { + char *name; /** Counter name */ + struct list_counter_state *first; /** First counter state */ + struct list_counter_state *state; /** Current counter state */ + struct list_counter *next; /** Next counter */ +}; + +struct list_counter_state { + int count; /** Current count */ + struct list_counter_state *parent; /** Parent counter, or NULL */ + struct list_counter_state *next; /** Next counter, or NULL */ +}; + + +void render_list_destroy_counters(void); +bool render_list_counter_reset(const char *name, int value); +bool render_list_counter_increment(const char *name, int value); +bool render_list_counter_end_scope(const char *name); +char *render_list_counter(struct css_counter *css_counter); + +void render_list_test(void); + +#endif -- cgit v1.2.3