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 <link> 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 <h1> 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.

