summaryrefslogtreecommitdiff
path: root/src/parse/font_face.c
diff options
context:
space:
mode:
authorJohn Mark Bell <jmb@netsurf-browser.org>2011-12-04 21:06:24 +0000
committerJohn Mark Bell <jmb@netsurf-browser.org>2011-12-04 21:06:24 +0000
commitd5a183e14d42560632a6aa270aede226215bb3d3 (patch)
tree31d7c6692a8267237ff3caa34fa5958e280ead24 /src/parse/font_face.c
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
Diffstat (limited to 'src/parse/font_face.c')
-rw-r--r--src/parse/font_face.c413
1 files changed, 413 insertions, 0 deletions
diff --git a/src/parse/font_face.c b/src/parse/font_face.c
new file mode 100644
index 0000000..14a715d
--- /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;
+}
+