From 81842e9a4d512fb573aff7d538a798130968c943 Mon Sep 17 00:00:00 2001 From: James Bursa Date: Thu, 16 Sep 2010 02:04:38 +0000 Subject: LibCSS API documentation, part 1, along with a simple example. svn path=/trunk/libcss/; revision=10783 --- docs/API | 281 +++++++++++++++++++++++++ examples/example1.c | 579 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 860 insertions(+) create mode 100644 docs/API create mode 100644 examples/example1.c diff --git a/docs/API b/docs/API new file mode 100644 index 0000000..a213871 --- /dev/null +++ b/docs/API @@ -0,0 +1,281 @@ +Using the LibCSS API +==================== + +This document explains how to use LibCSS. In addition to this document, please +see the examples and the headers (found in /usr/local/include/libcss or a +similar location). Experience with C is assumed. + +Using the library consists of the following general steps: + +1. Initialize the library. +2. Load one or more CSS files. +3. Use the Selection API to determine styles. +4. Use the computed styles. +5. Shut down the library. + +Please see example1.c for a demonstration of these steps. + + +Initialize the library +---------------------- + +The library is initialized using css_initialise(): + + css_initialise("Aliases", myrealloc, 0); + +The first argument is the pathname of an Aliases file, which maps character +encoding names to their canonical form. For an example, see test/data/Aliases. + +The 2nd argument is an allocation function. All allocations required by library +initialization will be made by calling this function. It takes the same +arguments as realloc(); a pointer and a size. If pointer is NULL a new block is +being requested. If size is 0 the block should be freed. Otherwise an existing +block is being resized. In many cases this function can simply call realloc(). + +The allocation function also takes a private data pointer, which is the third +argument to css_initialise(). This is not used by LibCSS but may be used to +communicate context to the allocation function. + +The allocation function pointer and private data pointer are arguments to many +LibCSS functions and work in the same way. + +css_initialise() returns a css_error value. It is CSS_OK if everything worked, +and an error code otherwise. The error codes are defined in libcss/errors.h. + +Many LibCSS functions return a css_error value. Checking the return value of +every call that does is advised, for example: + + css_error code; + code = css_initialise("../test/data/Aliases", myrealloc, 0); + if (code != CSS_OK) { + fprintf(stderr, "ERROR: css_initialise failed: %s\n", + css_error_to_string(code)); + exit(EXIT_FAILURE); + } + +LibCSS depends on LibWapcaplet. This must be initialized before LibCSS. For +example: + + lwc_code = lwc_initialise(myrealloc, NULL, 0); + if (lwc_code != lwc_error_ok) + ... + + +Load one or more CSS files +-------------------------- + +A stylesheet is represented by the opaque type css_stylesheet. To create one, +use css_stylesheet_create(), for example: + + css_stylesheet *sheet; + code = css_stylesheet_create(CSS_LEVEL_DEFAULT, "UTF-8", "", NULL, + false, false, myrealloc, 0, resolve_url, 0, &sheet); + if (code != CSS_OK) + ... + +The arguments are as follows: + + css_language_level level + Which version of CSS the stylesheet should be treated as. It currently has + no effect and is reserved for future use. The recommended value is + CSS_LEVEL_DEFAULT. + + const char *charset + The encoding of the stylesheet data, or NULL if LibCSS should attempt to + detect it. If the encoding is known, for example from the Content-Type + header or a file attribute, then it should be supplied here. + + const char *url + The URL that the stylesheet was retrieved from. LibCSS uses this along with + the resolve function (see below) to convert relative URLs in the stylesheet + (e.g. imports, background images) to absolute URLs. If the stylesheet has + no URL, use "". + + const char *title + This is intended for the stylesheet title (for example from the tag). + The title is not used by LibCSS but may be retrieved using + css_stylesheet_get_title(). May be NULL if there is no title. + + bool allow_quirks + + + bool inline_style + + + css_allocator_fn alloc + void *alloc_pw + + + css_url_resolution_fn resolve + void *resolve_pw + + + css_stylesheet **stylesheet + Updated with the newly created stylesheet object. + +Once the stylesheet has been created, CSS source data can be added to it. LibCSS +parses the data into internal structures. Only data in memory is supported; you +must handle reading from files or the network if required. Data is added using +css_stylesheet_append_data(), for example: + + code = css_stylesheet_append_data(sheet, data, length); + if (code != CSS_OK && code != CSS_NEEDDATA) + ... + +The second argument is a pointer to a buffer containing some CSS to be parsed, +with length in bytes given in the 3rd argument. + +This function may be called repeatedly with more data from the same stylesheet, +for example as data arrives over the network. + +The return value may be CSS_NEEDDATA instead of CSS_OK. This indicates that more +data may be expected. The two states can be treated identically. + +When all the data has been supplied, css_stylesheet_data_done() completes the +processing: + + code = css_stylesheet_data_done(sheet); + if (code != CSS_OK) + ... + +The stylesheet is now in memory and ready for further use. + + +Use the Selection API to determine styles +----------------------------------------- + +The Selection API is currently the only way to get information about styles from +stylesheets that have been loaded. It takes a document node as input and returns +the computed style that applies to that node. For example, it can be used to +answer the question "What style should this

element have?" + +CSS selectors can be complex and apply to certain arrangments of elements within +a document tree. Therefore LibCSS has to be able to navigate your document tree +and read attributes of it to determine if a style applies. It does this through +a series of functions that you supply. In this way LibCSS is independent of the +representation of the document. For example, with the style rule: + + table h2 { color: red; } + +when requesting the style for an h2 element node, LibCSS will search its +ancestors for a table element to determine if this style applies. + +The first step in using the Selection API is creating a selection context. This +is a list of the stylesheets to be used. A context is created using +css_select_ctx_create(): + + css_select_ctx *select_ctx; + code = css_select_ctx_create(myrealloc, 0, &select_ctx); + if (code != CSS_OK) + ... + +Stylesheets are added to the context using css_select_ctx_append_sheet(): + + code = css_select_ctx_append_sheet(select_ctx, sheet, CSS_ORIGIN_AUTHOR, + CSS_MEDIA_ALL); + if (code != CSS_OK) + ... + +When adding a stylesheet, the origin and media can be specified. These are used +in the computation of styles as defined in the CSS specification. + +Alternatively stylesheets may be added using css_select_ctx_insert_sheet(). + +After the context has been prepared, an empty computed style is created: + + css_computed_style *style; + code = css_computed_style_create(myrealloc, 0, &style); + if (code != CSS_OK) + ... + +The style is then determined for a document node using css_select_style(): + + code = css_select_style(select_ctx, element_node, 0, + CSS_MEDIA_SCREEN, NULL, style, + &select_handler, 0); + if (code != CSS_OK) + ... + +The arguments are as follows: + + css_select_ctx *ctx + The selection context, as described above. + + void *node + A pointer to the document node for which the style is required. This is a + void pointer and may therefore be of any desired type. LibCSS can not use it + directly; instead it gets information about it through the functions given + in the handler argument, described below. Usually this will be a node in a + document tree. + + uint32_t pseudo_element + + uint64_t media + The media that the style should apply to. The computed style will only + consider stylesheets or @media blocks that include this media. See the CSS + specification for more details. + + const css_stylesheet *inline_style + + css_computed_style *result + Updated to the computed style for the node. + + css_select_handler *handler + This is a table of functions that are used to get information from and to + navigate the document tree, in order to determine if a CSS selector matches + the document node. Further details are below. + + void *pw + A private data pointer that is passed to each of the handler functions. + +The types of the handler functions that need to be supplied and the definition +of css_select_handler are given in libcss/select.h. The functions all have the +following in common: + + * the first argument is the private data pointer that was the last argument to + css_select_style() + + * the second argument is the document node that is being queried is some way + + * the last one or two arguments are pointers that must be updated with the + required information + + * the return value is a css_error and should be CSS_OK if everything worked and + an error code otherwise + +For example, the node_name function, which determines the element name of a +node, could be this: + + css_error node_name(void *pw, void *n, lwc_string **name) + { + my_document_node *node = n; + *name = lwc_string_ref(node->name); + return CSS_OK; + } + +where my_document_node is your document tree node type (e.g. a struct of some +sort). + + +Use the computed styles +----------------------- + +After the style has been computed by css_select_style() the CSS properties can +finally be retrieved. This is done using the property accessor functions +declared in libcss/computed.h. + +Note that although struct css_computed_style is declared in computed.h, its +members must not be used directly. The accessors carry out various additional +work to read the properties correctly. + +For example, the css_computed_color() accessor retrieves the color property: + + uint8_t color_type; + css_color color_shade; + color_type = css_computed_color(style, &color_shade); + +In this case color_type can be CSS_COLOR_INHERIT or CSS_COLOR_COLOR. In the +latter case, color_shade contains the actual color in RRGGBBAA format. Together +these two variables encode the possible values for the property given by the +CSS specification. + diff --git a/examples/example1.c b/examples/example1.c new file mode 100644 index 0000000..8690e0a --- /dev/null +++ b/examples/example1.c @@ -0,0 +1,579 @@ +/* + * LibCSS - example1.c + * + * Compile this using a command such as: + * gcc -g -W -Wall -o example1 example1.c `pkg-config --cflags --libs libcss` + */ + +#include +#include +#include + +/* The entire API is available through this header. */ +#include + + +/* This macro is used to silence compiler warnings about unused function + * arguments. */ +#define UNUSED(x) ((x) = (x)) + + +/* Function declarations. */ +static void *myrealloc(void *ptr, size_t len, void *pw); +static css_error resolve_url(void *pw, + const char *base, lwc_string *rel, lwc_string **abs); +static void die(const char *text, css_error code); +static css_error node_name(void *pw, void *node, + lwc_string **name); +static css_error node_classes(void *pw, void *node, + lwc_string ***classes, uint32_t *n_classes); +static css_error node_id(void *pw, void *node, + lwc_string **id); +static css_error named_ancestor_node(void *pw, void *node, + lwc_string *name, + void **ancestor); +static css_error named_parent_node(void *pw, void *node, + lwc_string *name, + void **parent); +static css_error named_sibling_node(void *pw, void *node, + lwc_string *name, + void **sibling); +static css_error parent_node(void *pw, void *node, void **parent); +static css_error sibling_node(void *pw, void *node, void **sibling); +static css_error node_has_name(void *pw, void *node, + lwc_string *name, + bool *match); +static css_error node_has_class(void *pw, void *node, + lwc_string *name, + bool *match); +static css_error node_has_id(void *pw, void *node, + lwc_string *name, + bool *match); +static css_error node_has_attribute(void *pw, void *node, + lwc_string *name, + bool *match); +static css_error node_has_attribute_equal(void *pw, void *node, + lwc_string *name, + lwc_string *value, + bool *match); +static css_error node_has_attribute_dashmatch(void *pw, void *node, + lwc_string *name, + lwc_string *value, + bool *match); +static css_error node_has_attribute_includes(void *pw, void *node, + lwc_string *name, + lwc_string *value, + bool *match); +static css_error node_is_first_child(void *pw, void *node, bool *match); +static css_error node_is_link(void *pw, void *node, bool *match); +static css_error node_is_visited(void *pw, void *node, bool *match); +static css_error node_is_hover(void *pw, void *node, bool *match); +static css_error node_is_active(void *pw, void *node, bool *match); +static css_error node_is_focus(void *pw, void *node, bool *match); +static css_error node_is_lang(void *pw, void *node, + lwc_string *lang, bool *match); +static css_error node_presentational_hint(void *pw, void *node, + uint32_t property, css_hint *hint); +static css_error ua_default_for_property(void *pw, uint32_t property, + css_hint *hint); +static css_error compute_font_size(void *pw, const css_hint *parent, + css_hint *size); + +/* Table of function pointers for the LibCSS Select API. */ +static css_select_handler select_handler = { + node_name, + node_classes, + node_id, + named_ancestor_node, + named_parent_node, + named_sibling_node, + parent_node, + sibling_node, + node_has_name, + node_has_class, + node_has_id, + node_has_attribute, + node_has_attribute_equal, + node_has_attribute_dashmatch, + node_has_attribute_includes, + node_is_first_child, + node_is_link, + node_is_visited, + node_is_hover, + node_is_active, + node_is_focus, + node_is_lang, + node_presentational_hint, + ua_default_for_property, + compute_font_size +}; + + +int main(int argc, char **argv) +{ + lwc_error lwc_code; + css_error code; + css_stylesheet *sheet; + size_t size; + const char data[] = "h1 { color: red } " + "h4 { color: #321; } " + "h4, h5 { color: #123456; "; + css_select_ctx *select_ctx; + uint32_t count; + unsigned int hh; + + UNUSED(argc); + UNUSED(argv); + + /* initialise libwapcaplet (required by libcss) */ + lwc_code = lwc_initialise(myrealloc, NULL, 0); + if (lwc_code != lwc_error_ok) { + fprintf(stderr, "lwc_initialise: %i\n", lwc_code); + return 1; + } + + /* initialise libcss */ + code = css_initialise("../test/data/Aliases", myrealloc, 0); + if (code != CSS_OK) + die("css_initialise", code); + + + /* create a stylesheet */ + code = css_stylesheet_create(CSS_LEVEL_DEFAULT, "UTF-8", "", NULL, + false, false, myrealloc, 0, resolve_url, 0, &sheet); + if (code != CSS_OK) + die("css_stylesheet_create", code); + code = css_stylesheet_size(sheet, &size); + if (code != CSS_OK) + die("css_stylesheet_size", code); + printf("created stylesheet, size %zu\n", size); + + + /* parse some CSS source */ + code = css_stylesheet_append_data(sheet, (const uint8_t *) data, + sizeof data); + if (code != CSS_OK && code != CSS_NEEDDATA) + die("css_stylesheet_append_data", code); + code = css_stylesheet_data_done(sheet); + if (code != CSS_OK) + die("css_stylesheet_data_done", code); + code = css_stylesheet_size(sheet, &size); + if (code != CSS_OK) + die("css_stylesheet_size", code); + printf("appended data, size now %zu\n", size); + + + /* prepare a selection context containing the stylesheet */ + code = css_select_ctx_create(myrealloc, 0, &select_ctx); + if (code != CSS_OK) + die("css_select_ctx_create", code); + code = css_select_ctx_append_sheet(select_ctx, sheet, CSS_ORIGIN_AUTHOR, + CSS_MEDIA_ALL); + if (code != CSS_OK) + die("css_select_ctx_append_sheet", code); + code = css_select_ctx_count_sheets(select_ctx, &count); + if (code != CSS_OK) + die("css_select_ctx_count_sheets", code); + printf("created selection context with %i sheets\n", count); + + + /* select style for each of h1 to h6 */ + for (hh = 1; hh != 7; hh++) { + css_computed_style *style; + char element[20]; + lwc_string *element_name; + uint8_t color_type; + css_color color_shade; + + /* in this very simple example our "document tree" is just one + * node and is in fact a libwapcaplet string containing the + * element name */ + snprintf(element, sizeof element, "h%i", hh); + lwc_intern_string(element, strlen(element), &element_name); + + code = css_computed_style_create(myrealloc, 0, &style); + if (code != CSS_OK) + die("css_computed_style_create", code); + code = css_select_style(select_ctx, element_name, 0, + CSS_MEDIA_SCREEN, NULL, style, + &select_handler, 0); + if (code != CSS_OK) + die("css_select_style", code); + + color_type = css_computed_color(style, &color_shade); + if (color_type == CSS_COLOR_INHERIT) + printf("color of h%i is 'inherit'\n", hh); + else + printf("color of h%i is %x\n", hh, color_shade); + + code = css_computed_style_destroy(style); + if (code != CSS_OK) + die("css_computed_style_destroy", code); + } + + + /* free everything and shut down libcss */ + code = css_select_ctx_destroy(select_ctx); + if (code != CSS_OK) + die("css_select_ctx_destroy", code); + code = css_stylesheet_destroy(sheet); + if (code != CSS_OK) + die("css_stylesheet_destroy", code); + code = css_finalise(myrealloc, 0); + if (code != CSS_OK) + die("css_finalise", code); + + + return 0; +} + + +void *myrealloc(void *ptr, size_t len, void *pw) +{ + UNUSED(pw); + /*printf("myrealloc(%p, %zu)\n", ptr, len);*/ + + return realloc(ptr, len); +} + + +css_error resolve_url(void *pw, + const char *base, lwc_string *rel, lwc_string **abs) +{ + UNUSED(pw); + UNUSED(base); + + /* About as useless as possible */ + *abs = lwc_string_ref(rel); + + return CSS_OK; +} + + +void die(const char *text, css_error code) +{ + fprintf(stderr, "ERROR: %s: %i: %s\n", + text, code, css_error_to_string(code)); + exit(EXIT_FAILURE); +} + + + +/* Select handlers. Our "document tree" is actually just a single node, which is + * a libwapcaplet string containing the element name. Therefore all the + * functions below except those getting or testing the element name return empty + * data or false attributes. */ +css_error node_name(void *pw, void *n, lwc_string **name) +{ + lwc_string *node = n; + UNUSED(pw); + *name = lwc_string_ref(node); + return CSS_OK; +} + +css_error node_classes(void *pw, void *n, + lwc_string ***classes, uint32_t *n_classes) +{ + UNUSED(pw); + UNUSED(n); + *classes = NULL; + *n_classes = 0; + return CSS_OK; +} + +css_error node_id(void *pw, void *n, lwc_string **id) +{ + UNUSED(pw); + UNUSED(n); + *id = NULL; + return CSS_OK; +} + +css_error named_ancestor_node(void *pw, void *n, + lwc_string *name, + void **ancestor) +{ + UNUSED(pw); + UNUSED(n); + UNUSED(name); + *ancestor = NULL; + return CSS_OK; +} + +css_error named_parent_node(void *pw, void *n, + lwc_string *name, + void **parent) +{ + UNUSED(pw); + UNUSED(n); + UNUSED(name); + *parent = NULL; + return CSS_OK; +} + +css_error named_sibling_node(void *pw, void *n, + lwc_string *name, + void **sibling) +{ + UNUSED(pw); + UNUSED(n); + UNUSED(name); + *sibling = NULL; + return CSS_OK; +} + +css_error parent_node(void *pw, void *n, void **parent) +{ + UNUSED(pw); + UNUSED(n); + *parent = NULL; + return CSS_OK; +} + +css_error sibling_node(void *pw, void *n, void **sibling) +{ + UNUSED(pw); + UNUSED(n); + *sibling = NULL; + return CSS_OK; +} + +css_error node_has_name(void *pw, void *n, + lwc_string *name, + bool *match) +{ + lwc_string *node = n; + UNUSED(pw); + assert(lwc_string_caseless_isequal(node, name, match) == lwc_error_ok); + return CSS_OK; +} + +css_error node_has_class(void *pw, void *n, + lwc_string *name, + bool *match) +{ + UNUSED(pw); + UNUSED(n); + UNUSED(name); + *match = false; + return CSS_OK; +} + +css_error node_has_id(void *pw, void *n, + lwc_string *name, + bool *match) +{ + UNUSED(pw); + UNUSED(n); + UNUSED(name); + *match = false; + return CSS_OK; +} + +css_error node_has_attribute(void *pw, void *n, + lwc_string *name, + bool *match) +{ + UNUSED(pw); + UNUSED(n); + UNUSED(name); + *match = false; + return CSS_OK; +} + +css_error node_has_attribute_equal(void *pw, void *n, + lwc_string *name, + lwc_string *value, + bool *match) +{ + UNUSED(pw); + UNUSED(n); + UNUSED(name); + UNUSED(value); + *match = false; + return CSS_OK; +} + +css_error node_has_attribute_dashmatch(void *pw, void *n, + lwc_string *name, + lwc_string *value, + bool *match) +{ + UNUSED(pw); + UNUSED(n); + UNUSED(name); + UNUSED(value); + *match = false; + return CSS_OK; +} + +css_error node_has_attribute_includes(void *pw, void *n, + lwc_string *name, + lwc_string *value, + bool *match) +{ + UNUSED(pw); + UNUSED(n); + UNUSED(name); + UNUSED(value); + *match = false; + return CSS_OK; +} + +css_error node_is_first_child(void *pw, void *n, bool *match) +{ + UNUSED(pw); + UNUSED(n); + *match = false; + return CSS_OK; +} + +css_error node_is_link(void *pw, void *n, bool *match) +{ + UNUSED(pw); + UNUSED(n); + *match = false; + return CSS_OK; +} + +css_error node_is_visited(void *pw, void *n, bool *match) +{ + UNUSED(pw); + UNUSED(n); + *match = false; + return CSS_OK; +} + +css_error node_is_hover(void *pw, void *n, bool *match) +{ + UNUSED(pw); + UNUSED(n); + *match = false; + return CSS_OK; +} + +css_error node_is_active(void *pw, void *n, bool *match) +{ + UNUSED(pw); + UNUSED(n); + *match = false; + return CSS_OK; +} + +css_error node_is_focus(void *pw, void *n, bool *match) +{ + UNUSED(pw); + UNUSED(n); + *match = false; + return CSS_OK; +} + +css_error node_is_lang(void *pw, void *n, + lwc_string *lang, + bool *match) +{ + UNUSED(pw); + UNUSED(n); + UNUSED(lang); + *match = false; + return CSS_OK; +} + +css_error node_presentational_hint(void *pw, void *node, + uint32_t property, css_hint *hint) +{ + UNUSED(pw); + UNUSED(node); + UNUSED(property); + UNUSED(hint); + return CSS_PROPERTY_NOT_SET; +} + +css_error ua_default_for_property(void *pw, uint32_t property, css_hint *hint) +{ + UNUSED(pw); + + if (property == CSS_PROP_COLOR) { + hint->data.color = 0x00000000; + hint->status = CSS_COLOR_COLOR; + } else if (property == CSS_PROP_FONT_FAMILY) { + hint->data.strings = NULL; + hint->status = CSS_FONT_FAMILY_SANS_SERIF; + } else if (property == CSS_PROP_QUOTES) { + /* Not exactly useful :) */ + hint->data.strings = NULL; + hint->status = CSS_QUOTES_NONE; + } else if (property == CSS_PROP_VOICE_FAMILY) { + /** \todo Fix this when we have voice-family done */ + hint->data.strings = NULL; + hint->status = 0; + } else { + return CSS_INVALID; + } + + return CSS_OK; +} + +css_error compute_font_size(void *pw, const css_hint *parent, css_hint *size) +{ + static css_hint_length sizes[] = { + { FLTTOFIX(6.75), CSS_UNIT_PT }, + { FLTTOFIX(7.50), CSS_UNIT_PT }, + { FLTTOFIX(9.75), CSS_UNIT_PT }, + { FLTTOFIX(12.0), CSS_UNIT_PT }, + { FLTTOFIX(13.5), CSS_UNIT_PT }, + { FLTTOFIX(18.0), CSS_UNIT_PT }, + { FLTTOFIX(24.0), CSS_UNIT_PT } + }; + const css_hint_length *parent_size; + + UNUSED(pw); + + /* Grab parent size, defaulting to medium if none */ + if (parent == NULL) { + parent_size = &sizes[CSS_FONT_SIZE_MEDIUM - 1]; + } else { + assert(parent->status == CSS_FONT_SIZE_DIMENSION); + assert(parent->data.length.unit != CSS_UNIT_EM); + assert(parent->data.length.unit != CSS_UNIT_EX); + parent_size = &parent->data.length; + } + + assert(size->status != CSS_FONT_SIZE_INHERIT); + + if (size->status < CSS_FONT_SIZE_LARGER) { + /* Keyword -- simple */ + size->data.length = sizes[size->status - 1]; + } else if (size->status == CSS_FONT_SIZE_LARGER) { + /** \todo Step within table, if appropriate */ + size->data.length.value = + FMUL(parent_size->value, FLTTOFIX(1.2)); + size->data.length.unit = parent_size->unit; + } else if (size->status == CSS_FONT_SIZE_SMALLER) { + /** \todo Step within table, if appropriate */ + size->data.length.value = + FMUL(parent_size->value, FLTTOFIX(1.2)); + size->data.length.unit = parent_size->unit; + } else if (size->data.length.unit == CSS_UNIT_EM || + size->data.length.unit == CSS_UNIT_EX) { + size->data.length.value = + FMUL(size->data.length.value, parent_size->value); + + if (size->data.length.unit == CSS_UNIT_EX) { + size->data.length.value = FMUL(size->data.length.value, + FLTTOFIX(0.6)); + } + + size->data.length.unit = parent_size->unit; + } else if (size->data.length.unit == CSS_UNIT_PCT) { + size->data.length.value = FDIV(FMUL(size->data.length.value, + parent_size->value), FLTTOFIX(100)); + size->data.length.unit = parent_size->unit; + } + + size->status = CSS_FONT_SIZE_DIMENSION; + + return CSS_OK; +} + + -- cgit v1.2.3