diff options
author | John Mark Bell <jmb@netsurf-browser.org> | 2011-12-04 21:06:24 +0000 |
---|---|---|
committer | John Mark Bell <jmb@netsurf-browser.org> | 2011-12-04 21:06:24 +0000 |
commit | d5a183e14d42560632a6aa270aede226215bb3d3 (patch) | |
tree | 31d7c6692a8267237ff3caa34fa5958e280ead24 /src/select | |
parent | d153cb125a6a69e08a49c93f9774f86705aa9898 (diff) | |
download | libcss-d5a183e14d42560632a6aa270aede226215bb3d3.tar.gz libcss-d5a183e14d42560632a6aa270aede226215bb3d3.tar.bz2 |
@font-face support. Credit: James Montgomerie
Things missing: parser tests; the following descriptors: font-feature-settings, font-stretch, font-variant, unicode-range.
svn path=/trunk/libcss/; revision=13244
Diffstat (limited to 'src/select')
-rw-r--r-- | src/select/Makefile | 2 | ||||
-rw-r--r-- | src/select/font_face.c | 251 | ||||
-rw-r--r-- | src/select/font_face.h | 53 | ||||
-rw-r--r-- | src/select/select.c | 329 |
4 files changed, 615 insertions, 20 deletions
diff --git a/src/select/Makefile b/src/select/Makefile index db1c4e6..b99eab3 100644 --- a/src/select/Makefile +++ b/src/select/Makefile @@ -1,4 +1,4 @@ # Sources -DIR_SOURCES := computed.c dispatch.c hash.c select.c +DIR_SOURCES := computed.c dispatch.c hash.c select.c font_face.c include build/makefiles/Makefile.subdir diff --git a/src/select/font_face.c b/src/select/font_face.c new file mode 100644 index 0000000..ae24827 --- /dev/null +++ b/src/select/font_face.c @@ -0,0 +1,251 @@ +/* + * This file is part of LibCSS. + * Licensed under the MIT License, + * http://www.opensource.org/licenses/mit-license.php + * Copyright 2011 Things Made Out Of Other Things Ltd. + * Written by James Montgomerie <jamie@th.ingsmadeoutofotherthin.gs> + */ + +#include <string.h> + +#include "select/font_face.h" + +static void font_faces_srcs_destroy(css_font_face *font_face) +{ + uint32_t i; + css_font_face_src *srcs = font_face->srcs; + + for (i = 0; i < font_face->n_srcs; ++i) { + if (srcs[i].location != NULL) { + lwc_string_unref(srcs[i].location); + } + } + + font_face->alloc(srcs, 0, font_face->pw); + font_face->srcs = NULL; +} + +/** + * Create a font-face + * + * \param alloc Memory (de)allocation function + * \param pw Pointer to client-specific data + * \param result Pointer to location to receive result + * \return CSS_OK on success, + * CSS_NOMEM on memory exhaustion, + * CSS_BADPARM on bad parameters. + */ +css_error css__font_face_create(css_allocator_fn alloc, void *pw, + css_font_face **result) +{ + css_font_face *f; + + if (alloc == NULL || result == NULL) + return CSS_BADPARM; + + f = alloc(NULL, sizeof(css_font_face), pw); + if (f == NULL) + return CSS_NOMEM; + + memset(f, 0, sizeof(css_font_face)); + + f->alloc = alloc; + f->pw = pw; + + *result = f; + + return CSS_OK; +} + +/** + * Destroy a font-face + * + * \param font_face Font-face to destroy + * \return CSS_OK on success, appropriate error otherwise + */ +css_error css__font_face_destroy(css_font_face *font_face) +{ + if (font_face == NULL) + return CSS_BADPARM; + + if (font_face->font_family != NULL) + lwc_string_unref(font_face->font_family); + + if (font_face->srcs != NULL) + font_faces_srcs_destroy(font_face); + + font_face->alloc(font_face, 0, font_face->pw); + + return CSS_OK; +} + + +/** + * Set a font-face's font-family name + * + * \param font_face The font-face + * \param font_family Font-family name + * \param result Pointer to location to receive result + * \return CSS_OK on success, + * CSS_BADPARM on bad parameters. + */ +css_error css__font_face_set_font_family(css_font_face *font_face, + lwc_string *font_family) +{ + if (font_face == NULL || font_family == NULL) + return CSS_BADPARM; + + if (font_face->font_family != NULL) + lwc_string_unref(font_face->font_family); + + font_face->font_family = lwc_string_ref(font_family); + + return CSS_OK; +} + +/** + * Get a font-face's font-family name + * + * \param font_face The font-face + * \param result Pointer to location to receive result + * \return CSS_OK on success, + * CSS_BADPARM on bad parameters. + */ +css_error css_font_face_get_font_family(const css_font_face *font_face, + lwc_string **font_family) +{ + if (font_face == NULL || font_family == NULL) + return CSS_BADPARM; + + *font_family = font_face->font_family; + + return CSS_OK; +} + +/** + * Get the style of font for a font-face. + * + * \param src The font-face + * \return The style, as a css_font_style_e + */ +uint8_t css_font_face_font_style(const css_font_face *font_face) +{ + return font_face->bits[0] & 0x3; +} + +/** + * Get the weight of font for a font-face. + * + * \param src The font-face + * \return The style, as a css_font_weight_e + */ +uint8_t css_font_face_font_weight(const css_font_face *font_face) +{ + return (font_face->bits[0] >> 2) & 0xf; +} + +/** + * Get the number of potential src locations for a font-face + * + * \param font_face The font-face + * \param count Pointer to location to receive result + * \return CSS_OK on success, + * CSS_BADPARM on bad parameters. + */ +css_error css_font_face_count_srcs(const css_font_face *font_face, + uint32_t *count) +{ + if (font_face == NULL || count == NULL) + return CSS_BADPARM; + + *count = font_face->n_srcs; + return CSS_OK; +} + +/** + * Get a specific src location from a font-face + * + * \param font_face The font-face + * \param index The index for the wanted src. + * \param count Pointer to location to receive result + * \return CSS_OK on success, + * CSS_BADPARM on bad parameters. + */ +css_error css_font_face_get_src(const css_font_face *font_face, + uint32_t index, const css_font_face_src **src) +{ + if (font_face == NULL || src == NULL || index >= font_face->n_srcs) + return CSS_BADPARM; + + *src = &(font_face->srcs[index]); + + return CSS_OK; +} + +/** + * Get the location for a font-face src. + * + * \param font_face The font-face + * \param count Pointer to location to receive result + * \return CSS_OK on success, + * CSS_BADPARM on bad parameters. + * + * \note The type of location (local or URL) can be gathered from + * css_font_face_src_location_type, and the format of font (if specified) + * from css_font_face_src_format. + */ +css_error css_font_face_src_get_location(const css_font_face_src *src, + lwc_string **location) +{ + if (src == NULL || location == NULL) + return CSS_BADPARM; + + *location = src->location; + + return CSS_OK; +} + +/** + * Get the location type for a font-face src. + * + * \param src The font-face src + * \return The location type + */ +css_font_face_location_type css_font_face_src_location_type( + const css_font_face_src *src) +{ + return src->bits[0] & 0x3; +} + +/** + * Get the format of font for a font-face src. + * + * \param src The font-face src + * \return The format, if specified + */ +css_font_face_format css_font_face_src_format(const css_font_face_src *src) +{ + return (src->bits[0] >> 2) & 0x1f; +} + +/** + * Set a font-faces array of srcs. + * + * \param font_face The font-face + * \param srcs The array of css_font_face_srcs + * \param n_srcs The count of css_font_face_srcs in the array + * \return The format, if specified + */ +css_error css__font_face_set_srcs(css_font_face *font_face, + css_font_face_src *srcs, uint32_t n_srcs) +{ + if (font_face->srcs != NULL) + font_faces_srcs_destroy(font_face); + + font_face->srcs = srcs; + font_face->n_srcs = n_srcs; + + return CSS_OK; +} + + diff --git a/src/select/font_face.h b/src/select/font_face.h new file mode 100644 index 0000000..3572de8 --- /dev/null +++ b/src/select/font_face.h @@ -0,0 +1,53 @@ +/* + * This file is part of LibCSS. + * Licensed under the MIT License, + * http://www.opensource.org/licenses/mit-license.php + * Copyright 2011 Things Made Out Of Other Things Ltd. + * Written by James Montgomerie <jamie@th.ingsmadeoutofotherthin.gs> + */ + +#ifndef css_select_font_face_h_ +#define css_select_font_face_h_ + +#include <libcss/font_face.h> + + +struct css_font_face_src { + lwc_string *location; + /* + * Bit allocations: + * + * 76543210 + * 1 _fffffll format | location type + */ + uint8_t bits[1]; +}; + +struct css_font_face { + lwc_string *font_family; + css_font_face_src *srcs; + uint32_t n_srcs; + + /* + * Bit allocations: + * + * 76543210 + * 1 __wwwwss font-weight | font-style + */ + uint8_t bits[1]; + + css_allocator_fn alloc; + void *pw; +}; + +css_error css__font_face_create(css_allocator_fn alloc, void *pw, + css_font_face **result); +css_error css__font_face_destroy(css_font_face *font_face); + +css_error css__font_face_set_font_family(css_font_face *font_face, + lwc_string *font_family); + +css_error css__font_face_set_srcs(css_font_face *font_face, + css_font_face_src *srcs, uint32_t n_srcs); + +#endif diff --git a/src/select/select.c b/src/select/select.c index 7baac03..4243140 100644 --- a/src/select/select.c +++ b/src/select/select.c @@ -8,6 +8,8 @@ #include <assert.h> #include <string.h> +#include <libwapcaplet/libwapcaplet.h> + #include <libcss/select.h> #include "bytecode/bytecode.h" @@ -17,6 +19,7 @@ #include "select/dispatch.h" #include "select/hash.h" #include "select/propset.h" +#include "select/font_face.h" #include "select/select.h" #include "utils/parserutilserror.h" #include "utils/utils.h" @@ -74,6 +77,27 @@ struct css_select_ctx { lwc_string *after; }; +/** + * Container for selected font faces + */ +typedef struct css_select_font_faces_list { + const css_font_face **font_faces; + size_t count; +} css_select_font_faces_list; + +/** + * Font face selection state + */ +typedef struct css_select_font_faces_state { + lwc_string *font_family; + uint64_t media; + + css_select_font_faces_list ua_font_faces; + css_select_font_faces_list user_font_faces; + css_select_font_faces_list author_font_faces; +} css_select_font_faces_state; + + static css_error set_hint(css_select_state *state, uint32_t prop); static css_error set_initial(css_select_state *state, uint32_t prop, css_pseudo_element pseudo, @@ -104,6 +128,11 @@ static css_error match_detail(css_select_ctx *ctx, void *node, bool *match, css_pseudo_element *pseudo_element); static css_error cascade_style(const css_style *style, css_select_state *state); +static css_error select_font_faces_from_sheet(css_select_ctx *ctx, + const css_stylesheet *sheet, + css_origin origin, + css_select_font_faces_state *state); + #ifdef DEBUG_CHAIN_MATCHING static void dump_chain(const css_selector *selector); #endif @@ -558,6 +587,144 @@ css_error css_select_results_destroy(css_select_results *results) return CSS_OK; } +/** + * Search a selection context for defined font faces + * + * \param ctx Selection context + * \param media Currently active media types + * \param font_family Font family to search for + * \param result Pointer to location to receive result + * \return CSS_OK on success, appropriate error otherwise. + */ +css_error css_select_font_faces(css_select_ctx *ctx, + uint64_t media, lwc_string *font_family, + css_select_font_faces_results **result) +{ + uint32_t i; + css_error error; + css_select_font_faces_state state; + uint32_t n_font_faces; + + if (ctx == NULL || font_family == NULL || result == NULL) + return CSS_BADPARM; + + memset(&state, 0, sizeof(css_select_font_faces_state)); + state.font_family = font_family; + state.media = media; + + /* Iterate through the top-level stylesheets, selecting font-faces + * from those which apply to our current media requirements and + * are not disabled */ + for (i = 0; i < ctx->n_sheets; i++) { + const css_select_sheet s = ctx->sheets[i]; + + if ((s.media & media) != 0 && + s.sheet->disabled == false) { + error = select_font_faces_from_sheet(ctx, s.sheet, + s.origin, &state); + if (error != CSS_OK) + goto cleanup; + } + } + + n_font_faces = state.ua_font_faces.count + + state.user_font_faces.count + + state.author_font_faces.count; + + + if (n_font_faces > 0) { + /* We found some matching faces. Make a results structure with + * the font faces in priority order. */ + css_select_font_faces_results *results; + + results = ctx->alloc(NULL, + sizeof(css_select_font_faces_results), + ctx->pw); + if (results == NULL) { + error = CSS_NOMEM; + goto cleanup; + } + + results->alloc = ctx->alloc; + results->pw = ctx->pw; + + results->font_faces = ctx->alloc(NULL, + n_font_faces * sizeof(css_font_face *), + ctx->pw); + if (results->font_faces == NULL) { + ctx->alloc(results, 0, ctx->pw); + error = CSS_NOMEM; + goto cleanup; + } + + results->n_font_faces = n_font_faces; + + i = 0; + if (state.ua_font_faces.count != 0) { + memcpy(results->font_faces, + state.ua_font_faces.font_faces, + sizeof(css_font_face *) * + state.ua_font_faces.count); + + i += state.ua_font_faces.count; + } + + if (state.user_font_faces.count != 0) { + memcpy(results->font_faces + i, + state.user_font_faces.font_faces, + sizeof(css_font_face *) * + state.user_font_faces.count); + i += state.user_font_faces.count; + } + + if (state.author_font_faces.count != 0) { + memcpy(results->font_faces + i, + state.author_font_faces.font_faces, + sizeof(css_font_face *) * + state.author_font_faces.count); + } + + *result = results; + } + + error = CSS_OK; + +cleanup: + if (state.ua_font_faces.count != 0) + ctx->alloc(state.ua_font_faces.font_faces, 0, ctx->pw); + + if (state.user_font_faces.count != 0) + ctx->alloc(state.user_font_faces.font_faces, 0, ctx->pw); + + if (state.author_font_faces.count != 0) + ctx->alloc(state.author_font_faces.font_faces, 0, ctx->pw); + + return error; +} + +/** + * Destroy a font-face result set + * + * \param results Result set to destroy + * \return CSS_OK on success, appropriate error otherwise + */ +css_error css_select_font_faces_results_destroy( + css_select_font_faces_results *results) +{ + if (results == NULL) + return CSS_BADPARM; + + if (results->font_faces != NULL) { + /* Don't destroy the individual css_font_faces, they're owned + by their respective sheets */ + results->alloc(results->font_faces, 0, results->pw); + } + + results->alloc(results, 0, results->pw); + + return CSS_OK; +} + /****************************************************************************** * Selection engine internals below here * ******************************************************************************/ @@ -870,13 +1037,14 @@ css_error set_initial(css_select_state *state, return CSS_OK; } +#define IMPORT_STACK_SIZE 256 + css_error select_from_sheet(css_select_ctx *ctx, const css_stylesheet *sheet, css_origin origin, css_select_state *state) { const css_stylesheet *s = sheet; const css_rule *rule = s->rule_list; uint32_t sp = 0; -#define IMPORT_STACK_SIZE 256 const css_rule *import_stack[IMPORT_STACK_SIZE]; do { @@ -894,7 +1062,8 @@ css_error select_from_sheet(css_select_ctx *ctx, const css_stylesheet *sheet, if (import->sheet != NULL && (import->media & state->media) != 0) { /* It's applicable, so process it */ - assert(sp < IMPORT_STACK_SIZE - 1); + if (sp >= IMPORT_STACK_SIZE) + return CSS_NOMEM; import_stack[sp++] = rule; @@ -930,6 +1099,144 @@ css_error select_from_sheet(css_select_ctx *ctx, const css_stylesheet *sheet, return CSS_OK; } +static inline bool _rule_applies_to_media(const css_rule *rule, uint64_t media) +{ + bool applies = true; + const css_rule *ancestor = rule; + + while (ancestor != NULL) { + const css_rule_media *m = (const css_rule_media *) ancestor; + + if (ancestor->type == CSS_RULE_MEDIA && + (m->media & media) == 0) { + applies = false; + break; + } + + if (ancestor->ptype != CSS_RULE_PARENT_STYLESHEET) + ancestor = ancestor->parent; + else + ancestor = NULL; + } + + return applies; +} + +static css_error _select_font_face_from_rule(css_select_ctx *ctx, + const css_rule_font_face *rule, css_origin origin, + css_select_font_faces_state *state) +{ + if (_rule_applies_to_media((const css_rule *) rule, state->media)) { + bool correct_family = false; + + if (lwc_string_isequal( + rule->font_face->font_family, + state->font_family, + &correct_family) == lwc_error_ok && + correct_family) { + css_select_font_faces_list *faces = NULL; + const css_font_face **new_faces; + uint32_t index; + size_t new_size; + + switch (origin) { + case CSS_ORIGIN_UA: + faces = &state->ua_font_faces; + break; + case CSS_ORIGIN_USER: + faces = &state->user_font_faces; + break; + case CSS_ORIGIN_AUTHOR: + faces = &state->author_font_faces; + break; + } + + index = faces->count++; + new_size = faces->count * sizeof(css_font_face *); + + new_faces = ctx->alloc(faces->font_faces, + new_size, ctx->pw); + if (new_faces == NULL) { + faces->count = 0; + return CSS_NOMEM; + } + faces->font_faces = new_faces; + + faces->font_faces[index] = rule->font_face; + } + } + + return CSS_OK; +} + +static css_error select_font_faces_from_sheet(css_select_ctx *ctx, + const css_stylesheet *sheet, + css_origin origin, + css_select_font_faces_state *state) +{ + const css_stylesheet *s = sheet; + const css_rule *rule = s->rule_list; + uint32_t sp = 0; + const css_rule *import_stack[IMPORT_STACK_SIZE]; + + do { + /* Find first non-charset rule, if we're at the list head */ + if (rule == s->rule_list) { + while (rule != NULL && rule->type == CSS_RULE_CHARSET) + rule = rule->next; + } + + if (rule != NULL && rule->type == CSS_RULE_IMPORT) { + /* Current rule is an import */ + const css_rule_import *import = + (const css_rule_import *) rule; + + if (import->sheet != NULL && + (import->media & state->media) != 0) { + /* It's applicable, so process it */ + if (sp >= IMPORT_STACK_SIZE) + return CSS_NOMEM; + + import_stack[sp++] = rule; + + s = import->sheet; + rule = s->rule_list; + } else { + /* Not applicable; skip over it */ + rule = rule->next; + } + } else if (rule != NULL && rule->type == CSS_RULE_FONT_FACE) { + css_error error; + + error = _select_font_face_from_rule( + ctx, + (const css_rule_font_face *) rule, + origin, + state); + + if (error != CSS_OK) + return error; + + rule = rule->next; + } else if (rule == NULL) { + /* Find next sheet to process */ + if (sp > 0) { + sp--; + rule = import_stack[sp]->next; + s = import_stack[sp]->parent; + } else { + s = NULL; + } + } else { + rule = rule->next; + } + } while (s != NULL); + + return CSS_OK; +} + +#undef IMPORT_STACK_SIZE + static inline bool _selectors_pending(const css_selector **node, const css_selector **id, const css_selector ***classes, uint32_t n_classes, const css_selector **univ) @@ -1063,8 +1370,6 @@ css_error match_selectors_in_sheet(css_select_ctx *ctx, while (_selectors_pending(node_selectors, id_selectors, class_selectors, n_classes, univ_selectors)) { const css_selector *selector; - css_rule *rule, *parent; - bool process = true; /* Selectors must be matched in ascending order of specificity * and rule index. (c.f. css__outranks_existing()) @@ -1077,21 +1382,7 @@ css_error match_selectors_in_sheet(css_select_ctx *ctx, /* Ignore any selectors contained in rules which are a child * of an @media block that doesn't match the current media * requirements. */ - for (rule = selector->rule; rule != NULL; rule = parent) { - if (rule->type == CSS_RULE_MEDIA && - (((css_rule_media *) rule)->media & - state->media) == 0) { - process = false; - break; - } - - if (rule->ptype != CSS_RULE_PARENT_STYLESHEET) - parent = rule->parent; - else - parent = NULL; - } - - if (process) { + if (_rule_applies_to_media(selector->rule, state->media)) { error = match_selector_chain(ctx, selector, state); if (error != CSS_OK) goto cleanup; |