summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Mark Bell <jmb@netsurf-browser.org>2011-12-04 21:06:24 (GMT)
committer John Mark Bell <jmb@netsurf-browser.org>2011-12-04 21:06:24 (GMT)
commitd5a183e14d42560632a6aa270aede226215bb3d3 (patch)
tree31d7c6692a8267237ff3caa34fa5958e280ead24
parentd153cb125a6a69e08a49c93f9774f86705aa9898 (diff)
downloadlibcss-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
-rw-r--r--Makefile1
-rw-r--r--include/libcss/font_face.h79
-rw-r--r--include/libcss/libcss.h1
-rw-r--r--include/libcss/select.h21
-rw-r--r--include/libcss/types.h4
-rw-r--r--src/parse/Makefile2
-rw-r--r--src/parse/font_face.c413
-rw-r--r--src/parse/font_face.h22
-rw-r--r--src/parse/language.c42
-rw-r--r--src/parse/properties/utils.c45
-rw-r--r--src/parse/properties/utils.h14
-rw-r--r--src/parse/propstrings.c10
-rw-r--r--src/parse/propstrings.h5
-rw-r--r--src/select/Makefile2
-rw-r--r--src/select/font_face.c251
-rw-r--r--src/select/font_face.h53
-rw-r--r--src/select/select.c329
-rw-r--r--src/stylesheet.c26
-rw-r--r--src/stylesheet.h2
-rw-r--r--test/data/css/INDEX1
-rw-r--r--test/data/css/fontface.css142
-rw-r--r--test/dump.h164
22 files changed, 1577 insertions, 52 deletions
diff --git a/Makefile b/Makefile
index 4b91c68..a6a7f2e 100644
--- a/Makefile
+++ b/Makefile
@@ -46,6 +46,7 @@ I := /include/libcss
INSTALL_ITEMS := $(INSTALL_ITEMS) $(I):include/libcss/computed.h
INSTALL_ITEMS := $(INSTALL_ITEMS) $(I):include/libcss/errors.h
+INSTALL_ITEMS := $(INSTALL_ITEMS) $(I):include/libcss/font_face.h
INSTALL_ITEMS := $(INSTALL_ITEMS) $(I):include/libcss/fpmath.h
INSTALL_ITEMS := $(INSTALL_ITEMS) $(I):include/libcss/functypes.h
INSTALL_ITEMS := $(INSTALL_ITEMS) $(I):include/libcss/hint.h
diff --git a/include/libcss/font_face.h b/include/libcss/font_face.h
new file mode 100644
index 0000000..6b18433
--- a/dev/null
+++ b/include/libcss/font_face.h
@@ -0,0 +1,79 @@
+/*
+ * 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 libcss_font_face_h_
+#define libcss_font_face_h_
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <libwapcaplet/libwapcaplet.h>
+
+#include <libcss/errors.h>
+#include <libcss/functypes.h>
+#include <libcss/properties.h>
+#include <libcss/types.h>
+
+typedef enum css_font_face_format {
+ CSS_FONT_FACE_FORMAT_UNSPECIFIED = 0x00,
+
+ CSS_FONT_FACE_FORMAT_WOFF = 0x01,
+ /* WOFF (Web Open Font Format); .woff */
+ CSS_FONT_FACE_FORMAT_OPENTYPE = 0x02,
+ /* TrueType or OpenType; .ttf, .otf */
+ CSS_FONT_FACE_FORMAT_EMBEDDED_OPENTYPE = 0x04,
+ /* Embedded OpenType; .eot */
+ CSS_FONT_FACE_FORMAT_SVG = 0x08,
+ /* SVG Font; .svg, .svgz */
+
+ CSS_FONT_FACE_FORMAT_UNKNOWN = 0x10,
+ /* Format specified, but not recognised */
+
+ /* We don't define CSS_FONT_FACE_SRC_FORMAT_TRUETYPE as might be
+ * expected, because the CSS3 specification
+ * (http://www.w3.org/TR/css3-fonts/, §4.3) says:
+ * "Given the overlap in common usage between TrueType and
+ * OpenType, the format hints "truetype" and "opentype" must be
+ * considered as synonymous"
+ * so we compute a hint of 'truetype' to css_font_face_format_opentype.
+ */
+} css_font_face_format;
+
+typedef enum css_font_face_location_type {
+ CSS_FONT_FACE_LOCATION_TYPE_UNSPECIFIED = 0,
+ CSS_FONT_FACE_LOCATION_TYPE_LOCAL = 1,
+ CSS_FONT_FACE_LOCATION_TYPE_URI = 2,
+} css_font_face_location_type;
+
+
+css_error css_font_face_get_font_family(
+ const css_font_face *font_face,
+ lwc_string **font_family);
+
+css_error css_font_face_count_srcs(const css_font_face *font_face,
+ uint32_t *count);
+css_error css_font_face_get_src(const css_font_face *font_face, uint32_t index,
+ const css_font_face_src **src);
+
+css_error css_font_face_src_get_location(const css_font_face_src *src,
+ lwc_string **location);
+
+css_font_face_location_type css_font_face_src_location_type(
+ const css_font_face_src *src);
+css_font_face_format css_font_face_src_format(const css_font_face_src *src);
+
+uint8_t css_font_face_font_style(const css_font_face *font_face);
+uint8_t css_font_face_font_weight(const css_font_face *font_face);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/libcss/libcss.h b/include/libcss/libcss.h
index 551177c..89e83b5 100644
--- a/include/libcss/libcss.h
+++ b/include/libcss/libcss.h
@@ -22,6 +22,7 @@ extern "C"
#include <libcss/properties.h>
#include <libcss/select.h>
#include <libcss/stylesheet.h>
+#include <libcss/font_face.h>
#ifdef __cplusplus
}
diff --git a/include/libcss/select.h b/include/libcss/select.h
index b6f9475..4a95752 100644
--- a/include/libcss/select.h
+++ b/include/libcss/select.h
@@ -130,6 +130,20 @@ typedef struct css_select_handler {
css_hint *size);
} css_select_handler;
+/**
+ * Font face selection result set
+ */
+typedef struct css_select_font_faces_results {
+ css_allocator_fn alloc;
+ void *pw;
+
+ /**
+ * Array of pointers to computed font faces.
+ */
+ css_font_face **font_faces;
+ uint32_t n_font_faces;
+} css_select_font_faces_results;
+
css_error css_select_ctx_create(css_allocator_fn alloc, void *pw,
css_select_ctx **result);
css_error css_select_ctx_destroy(css_select_ctx *ctx);
@@ -151,8 +165,13 @@ css_error css_select_style(css_select_ctx *ctx, void *node,
uint64_t media, const css_stylesheet *inline_style,
css_select_handler *handler, void *pw,
css_select_results **result);
+css_error css_select_results_destroy(css_select_results *results);
-css_error css_select_results_destroy(css_select_results *results);
+css_error css_select_font_faces(css_select_ctx *ctx,
+ uint64_t media, lwc_string *font_family,
+ css_select_font_faces_results **result);
+css_error css_select_font_faces_results_destroy(
+ css_select_font_faces_results *results);
#ifdef __cplusplus
}
diff --git a/include/libcss/types.h b/include/libcss/types.h
index c8b9cf9..ffaf13c 100644
--- a/include/libcss/types.h
+++ b/include/libcss/types.h
@@ -127,6 +127,10 @@ typedef struct css_select_ctx css_select_ctx;
typedef struct css_computed_style css_computed_style;
+typedef struct css_font_face css_font_face;
+
+typedef struct css_font_face_src css_font_face_src;
+
#ifdef __cplusplus
}
#endif
diff --git a/src/parse/Makefile b/src/parse/Makefile
index a7548a8..3d1df42 100644
--- a/src/parse/Makefile
+++ b/src/parse/Makefile
@@ -1,4 +1,4 @@
# Sources
-DIR_SOURCES := parse.c language.c important.c propstrings.c
+DIR_SOURCES := parse.c language.c important.c propstrings.c font_face.c
include build/makefiles/Makefile.subdir
diff --git a/src/parse/font_face.c b/src/parse/font_face.c
new file mode 100644
index 0000000..14a715d
--- a/dev/null
+++ b/src/parse/font_face.c
@@ -0,0 +1,413 @@
+/*
+ * 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 "parse/font_face.h"
+
+#include <assert.h>
+#include <string.h>
+
+#include "parse/propstrings.h"
+#include "parse/properties/utils.h"
+#include "select/font_face.h"
+
+static bool font_rule_font_family_reserved(css_language *c,
+ const css_token *ident)
+{
+ bool match;
+
+ return (lwc_string_caseless_isequal(ident->idata, c->strings[SERIF],
+ &match) == lwc_error_ok && match) ||
+ (lwc_string_caseless_isequal(ident->idata,
+ c->strings[SANS_SERIF], &match) == lwc_error_ok &&
+ match) ||
+ (lwc_string_caseless_isequal(ident->idata, c->strings[CURSIVE],
+ &match) == lwc_error_ok && match) ||
+ (lwc_string_caseless_isequal(ident->idata, c->strings[FANTASY],
+ &match) == lwc_error_ok && match) ||
+ (lwc_string_caseless_isequal(ident->idata,
+ c->strings[MONOSPACE], &match) == lwc_error_ok &&
+ match) ||
+ (lwc_string_caseless_isequal(ident->idata, c->strings[INHERIT],
+ &match) == lwc_error_ok && match) ||
+ (lwc_string_caseless_isequal(ident->idata, c->strings[INITIAL],
+ &match) == lwc_error_ok && match) ||
+ (lwc_string_caseless_isequal(ident->idata, c->strings[DEFAULT],
+ &match) == lwc_error_ok && match);
+}
+
+static css_error font_face_parse_font_family(css_language *c,
+ const parserutils_vector *vector, int *ctx,
+ css_font_face *font_face)
+{
+ css_error error;
+ lwc_string *string;
+
+ error = css__ident_list_or_string_to_string(c, vector, ctx,
+ font_rule_font_family_reserved, &string);
+ if (error != CSS_OK)
+ return error;
+
+ css__font_face_set_font_family(font_face, string);
+
+ lwc_string_unref(string);
+
+ return CSS_OK;
+}
+
+static css_error font_face_src_parse_format(css_language *c,
+ const parserutils_vector *vector, int *ctx,
+ css_font_face_format *format)
+{
+ bool match;
+ const css_token *token;
+
+ *format = CSS_FONT_FACE_FORMAT_UNSPECIFIED;
+
+ /* 'format(' STRING [ ',' STRING ]* ')'
+ *
+ * 'format(' already consumed
+ */
+
+ do {
+ consumeWhitespace(vector, ctx);
+
+ token = parserutils_vector_iterate(vector, ctx);
+ if (token == NULL || token->type != CSS_TOKEN_STRING)
+ return CSS_INVALID;
+
+ if (lwc_string_isequal(token->idata,
+ c->strings[WOFF], &match) == lwc_error_ok &&
+ match) {
+ *format |= CSS_FONT_FACE_FORMAT_WOFF;
+ } else if ((lwc_string_isequal(token->idata,
+ c->strings[TRUETYPE], &match) == lwc_error_ok &&
+ match) ||
+ (lwc_string_isequal(token->idata,
+ c->strings[OPENTYPE], &match) == lwc_error_ok &&
+ match)) {
+ *format |= CSS_FONT_FACE_FORMAT_OPENTYPE;
+ } else if (lwc_string_isequal(token->idata,
+ c->strings[EMBEDDED_OPENTYPE],
+ &match) == lwc_error_ok && match) {
+ *format |= CSS_FONT_FACE_FORMAT_EMBEDDED_OPENTYPE;
+ } else if (lwc_string_isequal(token->idata,
+ c->strings[SVG], &match) == lwc_error_ok &&
+ match) {
+ *format |= CSS_FONT_FACE_FORMAT_SVG;
+ } else {
+ /* The spec gives a list of possible strings, which
+ * hints that unknown strings should be parse errors,
+ * but it also talks about "unknown font formats",
+ * so we treat any string we don't know not as a parse
+ * error, but as indicating an "unknown font format".
+ */
+ *format |= CSS_FONT_FACE_FORMAT_UNKNOWN;
+ }
+
+ consumeWhitespace(vector, ctx);
+ token = parserutils_vector_iterate(vector, ctx);
+ } while (token != NULL && tokenIsChar(token, ','));
+
+ if (token == NULL || tokenIsChar(token, ')') == false)
+ return CSS_INVALID;
+
+ return CSS_OK;
+}
+
+static css_error font_face_src_parse_spec_or_name(css_language *c,
+ const parserutils_vector *vector, int *ctx,
+ lwc_string **location,
+ css_font_face_location_type *location_type,
+ css_font_face_format *format)
+{
+ const css_token *token;
+ css_error error;
+ bool match;
+
+ /* spec-or-name ::= font-face-spec | font-face-name
+ * font-face-spec ::= URI [ 'format(' STRING [ ',' STRING ]* ')' ]?
+ * font-face-name ::= 'local(' ident-list-or-string ')'
+ * ident-list-or-string ::= IDENT IDENT* | STRING
+ */
+
+ consumeWhitespace(vector, ctx);
+
+ token = parserutils_vector_iterate(vector, ctx);
+ if (token == NULL)
+ return CSS_INVALID;
+
+ if (token->type == CSS_TOKEN_URI) {
+ error = c->sheet->resolve(c->sheet->resolve_pw,
+ c->sheet->url, token->idata,
+ location);
+ if (error != CSS_OK)
+ return error;
+
+ *location_type = CSS_FONT_FACE_LOCATION_TYPE_URI;
+
+ consumeWhitespace(vector, ctx);
+
+ token = parserutils_vector_peek(vector, *ctx);
+ if (token != NULL && token->type == CSS_TOKEN_FUNCTION &&
+ lwc_string_caseless_isequal(token->idata,
+ c->strings[FORMAT], &match) == lwc_error_ok &&
+ match) {
+ parserutils_vector_iterate(vector, ctx);
+
+ error = font_face_src_parse_format(c, vector, ctx,
+ format);
+ if (error != CSS_OK) {
+ lwc_string_unref(*location);
+ return error;
+ }
+ }
+ } else if (token->type == CSS_TOKEN_FUNCTION &&
+ lwc_string_caseless_isequal(token->idata,
+ c->strings[LOCAL],
+ &match) == lwc_error_ok && match) {
+ consumeWhitespace(vector, ctx);
+
+ error = css__ident_list_or_string_to_string(
+ c, vector, ctx, NULL, location);
+ if (error != CSS_OK)
+ return error;
+
+ consumeWhitespace(vector, ctx);
+
+ token = parserutils_vector_iterate(vector, ctx);
+ if (token == NULL || tokenIsChar(token, ')') == false) {
+ lwc_string_unref(*location);
+ return CSS_INVALID;
+ }
+
+ *location_type = CSS_FONT_FACE_LOCATION_TYPE_LOCAL;
+ } else {
+ return CSS_INVALID;
+ }
+
+ return CSS_OK;
+}
+
+static css_error font_face_parse_src(css_language *c,
+ const parserutils_vector *vector, int *ctx,
+ css_font_face *font_face)
+{
+ int orig_ctx = *ctx;
+ css_error error = CSS_OK;
+ const css_token *token;
+ css_font_face_src *srcs = NULL, *new_srcs = NULL;
+ uint32_t n_srcs = 0;
+
+ /* src ::= spec-or-name [ ',' spec-or-name ]*
+ * spec-or-name ::= font-face-spec | font-face-name
+ * font-face-spec ::= URI [ 'format(' STRING [ ',' STRING ]* ')' ]?
+ * font-face-name ::= 'local(' ident-list-or-string ')'
+ * ident-list-or-string ::= IDENT IDENT* | STRING
+ */
+
+ /* Create one css_font_face_src for each consecutive location and
+ * [potentially] type pair in the comma-separated list
+ */
+ do {
+ lwc_string *location;
+ css_font_face_location_type location_type =
+ CSS_FONT_FACE_LOCATION_TYPE_UNSPECIFIED;
+ css_font_face_format format =
+ CSS_FONT_FACE_FORMAT_UNSPECIFIED;
+
+ error = font_face_src_parse_spec_or_name(c, vector, ctx,
+ &location, &location_type, &format);
+ if (error != CSS_OK)
+ goto cleanup;
+
+ /* This will be inefficient if there are a lot of locations -
+ * probably not a problem in practice.
+ */
+ new_srcs = c->alloc(srcs,
+ (n_srcs + 1) * sizeof(css_font_face_src),
+ c->pw);
+ if (new_srcs == NULL) {
+ error = CSS_NOMEM;
+ goto cleanup;
+ }
+ srcs = new_srcs;
+
+ srcs[n_srcs].location = location;
+ srcs[n_srcs].bits[0] = format << 2 | location_type;
+
+ ++n_srcs;
+
+ consumeWhitespace(vector, ctx);
+ token = parserutils_vector_iterate(vector, ctx);
+ } while (token != NULL && tokenIsChar(token, ','));
+
+ error = css__font_face_set_srcs(font_face, srcs, n_srcs);
+
+cleanup:
+ if (error != CSS_OK) {
+ *ctx = orig_ctx;
+ if (srcs != NULL)
+ c->alloc(srcs, 0, c->pw);
+ }
+
+ return error;
+}
+
+static css_error font_face_parse_font_style(css_language *c,
+ const parserutils_vector *vector, int *ctx,
+ css_font_face *font_face)
+{
+ int orig_ctx = *ctx;
+ css_error error = CSS_OK;
+ const css_token *token;
+ enum css_font_style_e style = 0;
+ bool match;
+
+ /* IDENT(normal, italic, oblique) */
+
+ token = parserutils_vector_iterate(vector, ctx);
+ if ((token == NULL) || ((token->type != CSS_TOKEN_IDENT))) {
+ *ctx = orig_ctx;
+ return CSS_INVALID;
+ }
+
+ if ((lwc_string_caseless_isequal(token->idata,
+ c->strings[NORMAL], &match) == lwc_error_ok && match)) {
+ style = CSS_FONT_STYLE_NORMAL;
+ } else if ((lwc_string_caseless_isequal(token->idata,
+ c->strings[ITALIC], &match) == lwc_error_ok && match)) {
+ style = CSS_FONT_STYLE_ITALIC;
+ } else if ((lwc_string_caseless_isequal(token->idata,
+ c->strings[OBLIQUE], &match) == lwc_error_ok &&
+ match)) {
+ style = CSS_FONT_STYLE_OBLIQUE;
+ } else {
+ error = CSS_INVALID;
+ }
+
+ if (error == CSS_OK) {
+ font_face->bits[0] = (font_face->bits[0] & 0xfc) | style;
+ } else {
+ *ctx = orig_ctx;
+ }
+
+ return error;
+}
+
+static css_error font_face_parse_font_weight(css_language *c,
+ const parserutils_vector *vector, int *ctx,
+ css_font_face *font_face)
+{
+ int orig_ctx = *ctx;
+ css_error error = CSS_OK;
+ const css_token *token;
+ enum css_font_weight_e weight = 0;
+ bool match;
+
+ /* NUMBER (100, 200, 300, 400, 500, 600, 700, 800, 900) |
+ * IDENT (normal, bold) */
+ token = parserutils_vector_iterate(vector, ctx);
+ if (token == NULL || (token->type != CSS_TOKEN_IDENT &&
+ token->type != CSS_TOKEN_NUMBER)) {
+ *ctx = orig_ctx;
+ return CSS_INVALID;
+ }
+
+ if (token->type == CSS_TOKEN_NUMBER) {
+ size_t consumed = 0;
+ css_fixed num = css__number_from_lwc_string(token->idata,
+ true, &consumed);
+ /* Invalid if there are trailing characters */
+ if (consumed != lwc_string_length(token->idata)) {
+ *ctx = orig_ctx;
+ return CSS_INVALID;
+ }
+
+ switch (FIXTOINT(num)) {
+ case 100: weight = CSS_FONT_WEIGHT_100; break;
+ case 200: weight = CSS_FONT_WEIGHT_200; break;
+ case 300: weight = CSS_FONT_WEIGHT_300; break;
+ case 400: weight = CSS_FONT_WEIGHT_400; break;
+ case 500: weight = CSS_FONT_WEIGHT_500; break;
+ case 600: weight = CSS_FONT_WEIGHT_600; break;
+ case 700: weight = CSS_FONT_WEIGHT_700; break;
+ case 800: weight = CSS_FONT_WEIGHT_800; break;
+ case 900: weight = CSS_FONT_WEIGHT_900; break;
+ default: error = CSS_INVALID;
+ }
+ } else if ((lwc_string_caseless_isequal(token->idata,
+ c->strings[NORMAL], &match) == lwc_error_ok && match)) {
+ weight = CSS_FONT_WEIGHT_NORMAL;
+ } else if ((lwc_string_caseless_isequal(token->idata,
+ c->strings[BOLD], &match) == lwc_error_ok && match)) {
+ weight = CSS_FONT_WEIGHT_BOLD;
+ } else {
+ error = CSS_INVALID;
+ }
+
+ if (error == CSS_OK) {
+ font_face->bits[0] = (font_face->bits[0] & 0xc3) |
+ (weight << 2);
+ } else {
+ *ctx = orig_ctx;
+ }
+
+ return error;
+}
+
+/**
+ * Parse a descriptor in an @font-face rule
+ *
+ * \param c Parsing context
+ * \param descriptor Token for this descriptor
+ * \param vector Vector of tokens to process
+ * \param ctx Pointer to vector iteration context
+ * \param rule Rule to process descriptor into
+ * \return CSS_OK on success,
+ * CSS_BADPARM on bad parameters,
+ * CSS_INVALID on invalid syntax,
+ * CSS_NOMEM on memory exhaustion
+ */
+css_error css__parse_font_descriptor(css_language *c,
+ const css_token *descriptor, const parserutils_vector *vector,
+ int *ctx, css_rule_font_face *rule)
+{
+ css_font_face *font_face = rule->font_face;
+ css_error error;
+ bool match;
+
+ if (font_face == NULL) {
+ error = css__font_face_create(c->sheet->alloc,
+ c->sheet->pw, &font_face);
+ if (error != CSS_OK) {
+ return error;
+ }
+
+ rule->font_face = font_face;
+ }
+
+ if (lwc_string_caseless_isequal(descriptor->idata,
+ c->strings[FONT_FAMILY], &match) == lwc_error_ok &&
+ match) {
+ return font_face_parse_font_family(c, vector, ctx, font_face);
+ } else if (lwc_string_caseless_isequal(descriptor->idata,
+ c->strings[SRC], &match) == lwc_error_ok && match) {
+ return font_face_parse_src(c, vector, ctx, font_face);
+ } else if (lwc_string_caseless_isequal(descriptor->idata,
+ c->strings[FONT_STYLE], &match) == lwc_error_ok &&
+ match) {
+ return font_face_parse_font_style(c, vector, ctx, font_face);
+ } else if (lwc_string_caseless_isequal(descriptor->idata,
+ c->strings[FONT_WEIGHT], &match) == lwc_error_ok &&
+ match) {
+ return font_face_parse_font_weight(c, vector, ctx, font_face);
+ }
+
+ return CSS_INVALID;
+}
+
diff --git a/src/parse/font_face.h b/src/parse/font_face.h
new file mode 100644
index 0000000..435380e
--- a/dev/null
+++ b/src/parse/font_face.h
@@ -0,0 +1,22 @@
+/*
+ * 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_parse_font_face_h_
+#define css_parse_font_face_h_
+
+#include <parserutils/utils/vector.h>
+
+#include "stylesheet.h"
+#include "lex/lex.h"
+#include "parse/language.h"
+
+css_error css__parse_font_descriptor(css_language *c,
+ const css_token *descriptor, const parserutils_vector *vector,
+ int *ctx, struct css_rule_font_face *rule);
+
+#endif
diff --git a/src/parse/language.c b/src/parse/language.c
index 0436c22..a5fce0e 100644
--- a/src/parse/language.c
+++ b/src/parse/language.c
@@ -12,6 +12,7 @@
#include "stylesheet.h"
#include "lex/lex.h"
+#include "parse/font_face.h"
#include "parse/important.h"
#include "parse/language.h"
#include "parse/parse.h"
@@ -558,6 +559,26 @@ css_error handleStartAtRule(css_language *c, const parserutils_vector *vector)
* so no need to destroy it */
c->state = HAD_RULE;
+ } else if (lwc_string_caseless_isequal(atkeyword->idata,
+ c->strings[FONT_FACE], &match) == lwc_error_ok &&
+ match) {
+ error = css__stylesheet_rule_create(c->sheet,
+ CSS_RULE_FONT_FACE, &rule);
+ if (error != CSS_OK)
+ return error;
+
+ consumeWhitespace(vector, &ctx);
+
+ error = css__stylesheet_add_rule(c->sheet, rule, NULL);
+ if (error != CSS_OK) {
+ css__stylesheet_rule_destroy(c->sheet, rule);
+ return error;
+ }
+
+ /* Rule is now owned by the sheet,
+ * so no need to destroy it */
+
+ c->state = HAD_RULE;
} else if (lwc_string_caseless_isequal(atkeyword->idata, c->strings[PAGE],
&match) == lwc_error_ok && match) {
const css_token *token;
@@ -693,9 +714,9 @@ css_error handleBlockContent(css_language *c, const parserutils_vector *vector)
context_entry *entry;
css_rule *rule;
- /* In CSS 2.1, block content comprises either declarations (if the
- * current block is associated with @page or a selector), or rulesets
- * (if the current block is associated with @media). */
+ /* Block content comprises either declarations (if the current block is
+ * associated with @page, @font-face or a selector), or rulesets (if the
+ * current block is associated with @media). */
entry = parserutils_stack_get_current(c->context);
if (entry == NULL || entry->data == NULL)
@@ -704,7 +725,8 @@ css_error handleBlockContent(css_language *c, const parserutils_vector *vector)
rule = entry->data;
if (rule == NULL || (rule->type != CSS_RULE_SELECTOR &&
rule->type != CSS_RULE_PAGE &&
- rule->type != CSS_RULE_MEDIA))
+ rule->type != CSS_RULE_MEDIA &&
+ rule->type != CSS_RULE_FONT_FACE))
return CSS_INVALID;
if (rule->type == CSS_RULE_MEDIA) {
@@ -729,6 +751,7 @@ css_error handleDeclaration(css_language *c, const parserutils_vector *vector)
/* Locations where declarations are permitted:
*
* + In @page
+ * + In @font-face
* + In ruleset
*/
entry = parserutils_stack_get_current(c->context);
@@ -737,7 +760,8 @@ css_error handleDeclaration(css_language *c, const parserutils_vector *vector)
rule = entry->data;
if (rule == NULL || (rule->type != CSS_RULE_SELECTOR &&
- rule->type != CSS_RULE_PAGE))
+ rule->type != CSS_RULE_PAGE &&
+ rule->type != CSS_RULE_FONT_FACE))
return CSS_INVALID;
/* Strip any leading whitespace (can happen if in nested block) */
@@ -759,7 +783,13 @@ css_error handleDeclaration(css_language *c, const parserutils_vector *vector)
consumeWhitespace(vector, &ctx);
- error = parseProperty(c, ident, vector, &ctx, rule);
+ if (rule->type == CSS_RULE_FONT_FACE) {
+ css_rule_font_face * ff_rule = (css_rule_font_face *) rule;
+ error = css__parse_font_descriptor(
+ c, ident, vector, &ctx, ff_rule);
+ } else {
+ error = parseProperty(c, ident, vector, &ctx, rule);
+ }
if (error != CSS_OK)
return error;
diff --git a/src/parse/properties/utils.c b/src/parse/properties/utils.c
index bf7e212..4eca6fc 100644
--- a/src/parse/properties/utils.c
+++ b/src/parse/properties/utils.c
@@ -1044,6 +1044,45 @@ css_error css__parse_unit_keyword(const char *ptr, size_t len, css_unit *unit)
}
/**
+ * Create a string from a list of IDENT/S tokens if the next token is IDENT
+ * or references the next token's string if it is a STRING
+ *
+ * \param c Parsing context
+ * \param vector Vector containing tokens
+ * \param ctx Vector iteration context
+ * \param reserved Callback to determine if an identifier is reserved
+ * \param result Pointer to location to receive resulting string
+ * \return CSS_OK on success, appropriate error otherwise.
+ *
+ * Post condition: \a *ctx is updated with the next token to process
+ * If the input is invalid, then \a *ctx remains unchanged.
+ *
+ * The resulting string's reference is passed to the caller
+ */
+css_error css__ident_list_or_string_to_string(css_language *c,
+ const parserutils_vector *vector, int *ctx,
+ bool (*reserved)(css_language *c, const css_token *ident),
+ lwc_string **result)
+{
+ const css_token *token;
+
+ token = parserutils_vector_peek(vector, *ctx);
+ if (token == NULL)
+ return CSS_INVALID;
+
+ if (token->type == CSS_TOKEN_STRING) {
+ token = parserutils_vector_iterate(vector, ctx);
+ *result = lwc_string_ref(token->idata);
+ return CSS_OK;
+ } else if(token->type == CSS_TOKEN_IDENT) {
+ return css__ident_list_to_string(c, vector, ctx, reserved,
+ result);
+ }
+
+ return CSS_INVALID;
+}
+
+/**
* Create a string from a list of IDENT/S tokens
*
* \param c Parsing context
@@ -1058,7 +1097,7 @@ css_error css__parse_unit_keyword(const char *ptr, size_t len, css_unit *unit)
*
* The resulting string's reference is passed to the caller
*/
-static css_error ident_list_to_string(css_language *c,
+css_error css__ident_list_to_string(css_language *c,
const parserutils_vector *vector, int *ctx,
bool (*reserved)(css_language *c, const css_token *ident),
lwc_string **result)
@@ -1084,7 +1123,7 @@ static css_error ident_list_to_string(css_language *c,
token->type == CSS_TOKEN_S)) {
if (token->type == CSS_TOKEN_IDENT) {
/* IDENT -- if reserved, reject style */
- if (reserved(c, token)) {
+ if (reserved != NULL && reserved(c, token)) {
error = CSS_INVALID;
goto cleanup;
}
@@ -1175,7 +1214,7 @@ css_error css__comma_list_to_style(css_language *c,
*ctx = prev_ctx;
- error = ident_list_to_string(c, vector, ctx,
+ error = css__ident_list_to_string(c, vector, ctx,
reserved, &str);
if (error != CSS_OK)
goto cleanup;
diff --git a/src/parse/properties/utils.h b/src/parse/properties/utils.h
index ab045bd..be903ee 100644
--- a/src/parse/properties/utils.h
+++ b/src/parse/properties/utils.h
@@ -181,10 +181,22 @@ css_error css__parse_unit_specifier(css_language *c,
css_error css__parse_unit_keyword(const char *ptr, size_t len,
css_unit *unit);
+css_error css__ident_list_or_string_to_string(css_language *c,
+ const parserutils_vector *vector, int *ctx,
+ bool (*reserved)(css_language *c, const css_token *ident),
+ lwc_string **result);
+
+css_error css__ident_list_to_string(css_language *c,
+ const parserutils_vector *vector, int *ctx,
+ bool (*reserved)(css_language *c, const css_token *ident),
+ lwc_string **result);
+
css_error css__comma_list_to_style(css_language *c,
const parserutils_vector *vector, int *ctx,
bool (*reserved)(css_language *c, const css_token *ident),
- css_code_t (*get_value)(css_language *c, const css_token *token, bool first),
+ css_code_t (*get_value)(css_language *c,
+ const css_token *token,
+ bool first),
css_style *result);
#endif
diff --git a/src/parse/propstrings.c b/src/parse/propstrings.c
index bf1fcd0..dea8816 100644
--- a/src/parse/propstrings.c
+++ b/src/parse/propstrings.c
@@ -30,6 +30,7 @@ const stringmap_entry stringmap[LAST_KNOWN] = {
{ "import", SLEN("import") },
{ "media", SLEN("media") },
{ "namespace", SLEN("namespace") },
+ { "font-face", SLEN("font-face") },
{ "page", SLEN("page") },
{ "aural", SLEN("aural") },
@@ -372,6 +373,15 @@ const stringmap_entry stringmap[LAST_KNOWN] = {
{ "currentColor", SLEN("currentColor") },
{ "odd", SLEN("odd") },
{ "even", SLEN("even") },
+ { "src", SLEN("src") },
+ { "local", SLEN("local") },
+ { "initial", SLEN("initial") },
+ { "format", SLEN("format") },
+ { "woff", SLEN("woff") },
+ { "truetype", SLEN("truetype") },
+ { "opentype", SLEN("opentype") },
+ { "embedded-opentype", SLEN("embedded-opentype") },
+ { "svg", SLEN("svg") },
{ "aliceblue", SLEN("aliceblue") },
{ "antiquewhite", SLEN("antiquewhite") },
diff --git a/src/parse/propstrings.h b/src/parse/propstrings.h
index 7c7b693..16affcd 100644
--- a/src/parse/propstrings.h
+++ b/src/parse/propstrings.h
@@ -15,7 +15,7 @@ enum {
UNIVERSAL,
/* At-rules */
- CHARSET, LIBCSS_IMPORT, MEDIA, NAMESPACE, PAGE,
+ CHARSET, LIBCSS_IMPORT, MEDIA, NAMESPACE, FONT_FACE, PAGE,
/* Media types */
AURAL, BRAILLE, EMBOSSED, HANDHELD, PRINT, PROJECTION,
@@ -88,7 +88,8 @@ enum {
W_RESIZE, LIBCSS_TEXT, WAIT, HELP, PROGRESS, SERIF, SANS_SERIF, CURSIVE,
FANTASY, MONOSPACE, MALE, FEMALE, CHILD, MIX, UNDERLINE, OVERLINE,
LINE_THROUGH, BLINK, RGB, RGBA, HSL, HSLA, LIBCSS_LEFT, LIBCSS_CENTER,
- LIBCSS_RIGHT, CURRENTCOLOR, ODD, EVEN,
+ LIBCSS_RIGHT, CURRENTCOLOR, ODD, EVEN, SRC, LOCAL, INITIAL,
+ FORMAT, WOFF, TRUETYPE, OPENTYPE, EMBEDDED_OPENTYPE, SVG,
/* Named colours */
FIRST_COLOUR,
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
--- a/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
--- a/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;
diff --git a/src/stylesheet.c b/src/stylesheet.c
index f8dedac..e37a00a 100644
--- a/src/stylesheet.c
+++ b/src/stylesheet.c
@@ -15,6 +15,7 @@
#include "utils/parserutilserror.h"
#include "utils/utils.h"
#include "select/dispatch.h"
+#include "select/font_face.h"
static css_error _add_selectors(css_stylesheet *sheet, css_rule *rule);
static css_error _remove_selectors(css_stylesheet *sheet, css_rule *rule);
@@ -261,7 +262,7 @@ css_error css_stylesheet_create(const css_stylesheet_params *params,
*/
css_error css_stylesheet_destroy(css_stylesheet *sheet)
{
- uint32_t string_index;
+ uint32_t index;
css_rule *r, *s;
if (sheet == NULL)
@@ -296,17 +297,17 @@ css_error css_stylesheet_destroy(css_stylesheet *sheet)
css__stylesheet_style_destroy(sheet->cached_style);
/* destroy string vector */
- for (string_index = 0;
- string_index < sheet->string_vector_c;
- string_index++) {
- lwc_string_unref(sheet->string_vector[string_index]);
+ for (index = 0;
+ index < sheet->string_vector_c;
+ index++) {
+ lwc_string_unref(sheet->string_vector[index]);
}
if (sheet->string_vector != NULL)
sheet->alloc(sheet->string_vector, 0, sheet->pw);
css__propstrings_unref();
-
+
sheet->alloc(sheet, 0, sheet->pw);
return CSS_OK;
@@ -625,7 +626,7 @@ css_error css_stylesheet_size(css_stylesheet *sheet, size_t *size)
bytes += hash_size;
}
-
+
*size = bytes;
return CSS_OK;
@@ -1194,10 +1195,10 @@ css_error css__stylesheet_rule_destroy(css_stylesheet *sheet, css_rule *rule)
break;
case CSS_RULE_FONT_FACE:
{
- css_rule_font_face *font_face = (css_rule_font_face *) rule;
+ css_rule_font_face *font_face_r = (css_rule_font_face *) rule;
- if (font_face->style != NULL)
- css__stylesheet_style_destroy(font_face->style);
+ if (font_face_r->font_face != NULL)
+ css__font_face_destroy(font_face_r->font_face);
}
break;
case CSS_RULE_PAGE:
@@ -1705,8 +1706,8 @@ size_t _rule_size(const css_rule *r)
bytes += sizeof(css_rule_font_face);
- if (rf->style != NULL)
- bytes += (rf->style->used * sizeof(css_code_t));
+ if (rf->font_face != NULL)
+ bytes += sizeof(css_font_face);
} else if (r->type == CSS_RULE_PAGE) {
const css_rule_page *rp = (const css_rule_page *) r;
const css_selector *s = rp->selector;
@@ -1731,4 +1732,3 @@ size_t _rule_size(const css_rule *r)
return bytes;
}
-
diff --git a/src/stylesheet.h b/src/stylesheet.h
index 0f6deed..d48d0a9 100644
--- a/src/stylesheet.h
+++ b/src/stylesheet.h
@@ -141,7 +141,7 @@ typedef struct css_rule_media {
typedef struct css_rule_font_face {
css_rule base;
- css_style *style;
+ css_font_face *font_face;
} css_rule_font_face;
typedef struct css_rule_page {
diff --git a/test/data/css/INDEX b/test/data/css/INDEX
index e918e16..42c66a8 100644
--- a/test/data/css/INDEX
+++ b/test/data/css/INDEX
@@ -7,3 +7,4 @@ allzengarden.css All CSS Zen Garden stylesheets concatenated
blocks.css Basic blocks and at-rule syntax
malformed.css Malformed declarations from the CSS 2.1 spec
badcomment.css Comment inside { ... } lacks starting /*
+fontface.css Various @font-face rules \ No newline at end of file
diff --git a/test/data/css/fontface.css b/test/data/css/fontface.css
new file mode 100644
index 0000000..bf58457
--- a/dev/null
+++ b/test/data/css/fontface.css
@@ -0,0 +1,142 @@
+@font-face {
+ invalid-descriptor: name;
+ src: invalid-src;
+}
+
+/* To use a downloadable font called Gentium */
+@font-face {
+ font-family: Gentium;
+ src: url(http://example.com/fonts/Gentium.ttf);
+}
+
+p { font-family: Gentium, serif; }
+
+/* load WOFF font if possible, otherwise use OpenType font */
+@font-face {
+ font-family: bodytext;
+ src: url(ideal-sans-serif.woff) format("woff"),
+ url(basic-sans-serif.ttf) format("opentype");
+}
+
+/* regular face of Gentium */
+@font-face {
+ font-family: MyGentium;
+ src: local(Gentium), /* use locally available Gentium */
+ url(Gentium.ttf); /* otherwise, download it */
+}
+
+/* bold face of Gentium */
+@font-face {
+ font-family: MyGentium;
+ src: local(Gentium Bold), /* full font name */
+ local(Gentium-Bold), /* Postscript name */
+ url(GentiumBold.ttf); /* otherwise, download it */
+ font-weight: bold;
+}
+
+/* Use a local font or reference an SVG font in another document: */
+@font-face {
+ font-family: Headline;
+ src: local(Futura-Medium),
+ url(fonts.svg#MyGeometricModern) format("svg");
+}
+
+/* Create an alias for local Japanese fonts on different platforms: */
+@font-face {
+ font-family: jpgothic;
+ src: local(HiraKakuPro-W3), local(Meiryo), local(IPAPGothic);
+}
+
+/* Reference a font face that cannot be matched within a larger family: */
+@font-face {
+ font-family: Hoefler Text Ornaments;
+ /* has the same font properties as Hoefler Text Regular */
+ src: local(HoeflerText-Ornaments);
+}
+
+/* Since localized fullnames should never match, a document with the header style rules below would always render using the default serif font, regardless whether a particular system locale parameter is set to Finnish or not: */
+@font-face {
+ font-family: SectionHeader;
+ src: local("Arial Lihavoitu"); /* Finnish fullname for Arial Bold, matching should fail */
+ font-weight: bold;
+}
+
+h2 { font-family: SectionHeader, serif; }
+
+/* A conformant user agent should never load the font ‘gentium.eot’ in the example below, since it is included in the first definition of the ‘src’ descriptor which is overridden by the second definition in the same @font-face rule: */
+@font-face {
+ font-family: MainText;
+ src: url(gentium.eot); /* for compatibility with older non-conformant user agents */
+ src: local("Gentium"), url(gentium.ttf); /* Overrides src definition */
+}
+
+/* The BBC provides news services in a wide variety of languages, many that are not well supported across all platforms. Using an @font-face rule, the BBC could provide a font for any of these languages, as it already does via a manual font download. */
+@font-face {
+ font-family: BBCBengali;
+ src: url(fonts/BBCBengali.ttf) format("opentype");
+ unicode-range: U+00-FF, U+980-9FF;
+}
+
+/* Technical documents often require a wide range of symbols. The STIX Fonts project is one project aimed at providing fonts to support a wide range of technical typesetting in a standardized way. The example below shows the use of a font that provides glyphs for many of the mathematical and technical symbol ranges within Unicode: */
+@font-face {
+ font-family: STIXGeneral;
+ src: local(STIXGeneral), url(/stixfonts/STIXGeneral.otf);
+ unicode-range: U+000-49F, U+2000-27FF, U+2900-2BFF, U+1D400-1D7FF;
+}
+
+
+
+@font-face {
+ font-family: JapaneseWithGentium;
+ src: local(MSMincho);
+ /* no range specified, defaults to entire range */
+}
+
+@font-face {
+ font-family: JapaneseWithGentium;
+ src: url(../fonts/Gentium.ttf);
+ unicode-range: U+0-2FF;
+}
+
+/* Consider a family constructed to optimize bandwidth by separating out Latin, Japanese and other characters into different font files: */
+/* fallback font - size: 4.5MB */
+@font-face {
+ font-family: DroidSans;
+ src: url(DroidSansFallback.ttf);
+ /* no range specified, defaults to entire range */
+}
+
+/* Japanese glyphs - size: 1.2MB */
+@font-face {
+ font-family: DroidSans;
+ src: url(DroidSansJapanese.ttf);
+ unicode-range: U+3000-9FFF, U+ff??;
+}
+
+/* Latin, Greek, Cyrillic along with some
+ punctuation and symbols - size: 190KB */
+@font-face {
+ font-family: DroidSans;
+ src: url(DroidSans.ttf);
+ unicode-range: U+000-5FF, U+1e00-1fff, U+2000-2300;
+}
+
+/* The first src: should be used because the second is invalid. */
+@font-face {
+ font-family: BadSrc;
+ src: url(http://example.com/fonts/Gentium.ttf);
+ src: fhgwgads;
+ font-style: oblique;
+}
+
+/* The third src: should be used because the second is invalid, but the thirsd is valid, overriding the first. */
+@font-face {
+ font-family: ThirdSrc;
+ src: url(http://example.com/fonts/Gentium.ttf);
+ src: fhgwgads;
+ font-style: italic;
+ src: local(gentium);
+}
+
+
+
diff --git a/test/dump.h b/test/dump.h
index 7a6d343..8817edf 100644
--- a/test/dump.h
+++ b/test/dump.h
@@ -8,6 +8,7 @@
#include "stylesheet.h"
#include "bytecode/bytecode.h"
#include "bytecode/opcodes.h"
+#include "select/font_face.h"
#include "testutils.h"
@@ -18,11 +19,14 @@ static void dump_rule_charset(css_rule_charset *s, char **buf, size_t *buflen);
static void dump_rule_import(css_rule_import *s, char **buf, size_t *buflen);
static void dump_rule_media(css_rule_media *s, char **buf, size_t *buflen);
static void dump_rule_page(css_rule_page *s, char **buf, size_t *buflen);
+static void dump_rule_font_face(css_rule_font_face *s,
+ char **buf, size_t *buflen);
static void dump_selector_list(css_selector *list, char **ptr);
static void dump_selector(css_selector *selector, char **ptr);
static void dump_selector_detail(css_selector_detail *detail, char **ptr);
static void dump_bytecode(css_style *style, char **ptr, uint32_t depth);
static void dump_string(lwc_string *string, char **ptr);
+static void dump_font_face(css_font_face *font_face, char**ptr);
void dump_sheet(css_stylesheet *sheet, char *buf, size_t *buflen)
{
@@ -50,6 +54,10 @@ void dump_sheet(css_stylesheet *sheet, char *buf, size_t *buflen)
dump_rule_page((css_rule_page *) rule,
&buf, buflen);
break;
+ case CSS_RULE_FONT_FACE:
+ dump_rule_font_face((css_rule_font_face *) rule,
+ &buf, buflen);
+ break;
default:
{
int written = sprintf(buf, "Unhandled rule type %d\n",
@@ -157,6 +165,22 @@ void dump_rule_page(css_rule_page *s, char **buf, size_t *buflen)
*buf = ptr;
}
+void dump_rule_font_face(css_rule_font_face *s, char **buf, size_t *buflen)
+{
+ char *ptr = *buf;
+
+ ptr += sprintf(ptr, "| @font-face ");
+
+ if (s->font_face != NULL) {
+ dump_font_face(s->font_face, &ptr);
+ }
+
+ *ptr++ = '\n';
+
+ *buflen -= ptr - *buf;
+ *buf = ptr;
+}
+
void dump_selector_list(css_selector *list, char **ptr)
{
if (list->combinator != NULL) {
@@ -833,7 +857,8 @@ void dump_bytecode(css_style *style, char **ptr, uint32_t depth)
break;
case BACKGROUND_IMAGE_URI:
{
- uint32_t snum = *((uint32_t *) bytecode); lwc_string *he;
+ uint32_t snum = *((uint32_t *) bytecode);
+ lwc_string *he;
css__stylesheet_string_get(style->sheet,
snum,
&he);
@@ -2017,9 +2042,7 @@ void dump_bytecode(css_style *style, char **ptr, uint32_t depth)
(int) lwc_string_length(he),
lwc_string_data(he));
- he =
- *((lwc_string **)
- bytecode);
+ css__stylesheet_string_get(style->sheet, snum, &he);
ADVANCE(sizeof(he));
*ptr += sprintf(*ptr, " '%.*s' ",
(int) lwc_string_length(he),
@@ -2365,4 +2388,137 @@ void dump_string(lwc_string *string, char **ptr)
lwc_string_data(string));
}
+void dump_font_face(css_font_face *font_face, char **ptr)
+{
+ uint8_t style, weight;
+
+ if (font_face->font_family != NULL) {
+ *(*ptr)++ = '\n';
+ *ptr += sprintf(*ptr, "| font-family: %.*s",
+ (int) lwc_string_length(font_face->font_family),
+ lwc_string_data(font_face->font_family));
+ }
+
+ *ptr += sprintf(*ptr, "\n| font-style: ");
+ style = css_font_face_font_style(font_face);
+ switch (style) {
+ case CSS_FONT_STYLE_INHERIT:
+ *ptr += sprintf(*ptr, "unspecified");
+ break;
+ case CSS_FONT_STYLE_NORMAL:
+ *ptr += sprintf(*ptr, "normal");
+ break;
+ case CSS_FONT_STYLE_ITALIC:
+ *ptr += sprintf(*ptr, "italic");
+ break;
+ case CSS_FONT_STYLE_OBLIQUE:
+ *ptr += sprintf(*ptr, "oblique");
+ break;
+ }
+
+ *ptr += sprintf(*ptr, "\n| font-weight: ");
+ weight = css_font_face_font_weight(font_face);
+ switch (weight) {
+ case CSS_FONT_WEIGHT_INHERIT:
+ *ptr += sprintf(*ptr, "unspecified");
+ break;
+ case CSS_FONT_WEIGHT_NORMAL:
+ *ptr += sprintf(*ptr, "normal");
+ break;
+ case CSS_FONT_WEIGHT_BOLD:
+ *ptr += sprintf(*ptr, "bold");
+ break;
+ case CSS_FONT_WEIGHT_100:
+ *ptr += sprintf(*ptr, "100");
+ break;
+ case CSS_FONT_WEIGHT_200:
+ *ptr += sprintf(*ptr, "200");
+ break;
+ case CSS_FONT_WEIGHT_300:
+ *ptr += sprintf(*ptr, "300");
+ break;
+ case CSS_FONT_WEIGHT_400:
+ *ptr += sprintf(*ptr, "400");
+ break;
+ case CSS_FONT_WEIGHT_500:
+ *ptr += sprintf(*ptr, "500");
+ break;
+ case CSS_FONT_WEIGHT_600:
+ *ptr += sprintf(*ptr, "600");
+ break;
+ case CSS_FONT_WEIGHT_700:
+ *ptr += sprintf(*ptr, "700");
+ break;
+ case CSS_FONT_WEIGHT_800:
+ *ptr += sprintf(*ptr, "800");
+ break;
+ case CSS_FONT_WEIGHT_900:
+ *ptr += sprintf(*ptr, "900");
+ break;
+ default:
+ *ptr += sprintf(*ptr, "Unhandled weight %d\n", (int)weight);
+ break;
+ }
+
+
+ if (font_face->srcs != NULL) {
+ uint32_t i;
+ css_font_face_src *srcs = font_face->srcs;
+ for (i = 0; i < font_face->n_srcs; ++i) {
+ *ptr += sprintf(*ptr, "\n| src: ");
+
+ css_font_face_format format =
+ css_font_face_src_format(&srcs[i]);
+
+ *ptr += sprintf(*ptr, "\n| format: ");
+
+ switch (format) {
+ case CSS_FONT_FACE_FORMAT_UNSPECIFIED:
+ *ptr += sprintf(*ptr, "unspecified");
+ break;
+ case CSS_FONT_FACE_FORMAT_WOFF:
+ *ptr += sprintf(*ptr, "WOFF");
+ break;
+ case CSS_FONT_FACE_FORMAT_OPENTYPE:
+ *ptr += sprintf(*ptr, "OTF");
+ break;
+ case CSS_FONT_FACE_FORMAT_EMBEDDED_OPENTYPE:
+ *ptr += sprintf(*ptr, "EOTF");
+ break;
+ case CSS_FONT_FACE_FORMAT_SVG:
+ *ptr += sprintf(*ptr, "SVG");
+ break;
+ case CSS_FONT_FACE_FORMAT_UNKNOWN:
+ *ptr += sprintf(*ptr, "unknown");
+ break;
+ default:
+ *ptr += sprintf(*ptr, "UNEXPECTED");
+ break;
+ }
+
+ if (srcs[i].location != NULL) {
+ *ptr += sprintf(*ptr, "\n| location: ");
+
+ switch (css_font_face_src_location_type(
+ &srcs[i])) {
+ case CSS_FONT_FACE_LOCATION_TYPE_LOCAL:
+ *ptr += sprintf(*ptr, "local");
+ break;
+ case CSS_FONT_FACE_LOCATION_TYPE_URI:
+ *ptr += sprintf(*ptr, "url");
+ break;
+ default:
+ *ptr += sprintf(*ptr, "UNKNOWN");
+ break;
+ }
+
+ *ptr += sprintf(*ptr, "(%.*s)",
+ (int) lwc_string_length(
+ srcs[i].location),
+ lwc_string_data(srcs[i].location));
+ }
+ }
+ }
+}
+
#endif