| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118 |
- // Copyright (C) 2004-2025 Artifex Software, Inc.
- //
- // This file is part of MuPDF.
- //
- // MuPDF is free software: you can redistribute it and/or modify it under the
- // terms of the GNU Affero General Public License as published by the Free
- // Software Foundation, either version 3 of the License, or (at your option)
- // any later version.
- //
- // MuPDF is distributed in the hope that it will be useful, but WITHOUT ANY
- // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- // FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
- // details.
- //
- // You should have received a copy of the GNU Affero General Public License
- // along with MuPDF. If not, see <https://www.gnu.org/licenses/agpl-3.0.en.html>
- //
- // Alternative licensing terms are available from the licensor.
- // For commercial licensing, see <https://www.artifex.com/> or contact
- // Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco,
- // CA 94129, USA, for further information.
- #include "mupdf/fitz.h"
- #include "html-imp.h"
- #include <string.h>
- #include "css-properties.h"
- struct lexbuf
- {
- fz_context *ctx;
- fz_pool *pool;
- const unsigned char *start;
- const unsigned char *s;
- const char *file;
- int line;
- int lookahead;
- int c;
- int string_len;
- char string[1024];
- };
- static fz_css_value *parse_expr(struct lexbuf *buf);
- static fz_css_selector *parse_selector(struct lexbuf *buf);
- FZ_NORETURN static void fz_css_error(struct lexbuf *buf, const char *msg)
- {
- #define PRE_POST_SIZE 30
- unsigned char text[PRE_POST_SIZE * 2 + 4];
- unsigned char *d = text;
- const unsigned char *s = buf->start;
- int n;
- /* We want to make a helpful fragment for the error message.
- * We want err_pos to be the point at which we just tripped
- * the error. err_pos needs to be at least 1 byte behind
- * our read pointer, as we've read that char. */
- const unsigned char *err_pos = buf->s;
- n = 1;
- /* And if we're using lookahead, it's further behind. */
- if (buf->lookahead >= CSS_KEYWORD)
- n += buf->string_len;
- else if (buf->lookahead != EOF)
- n += 1;
- /* But it can't be before the start of the buffer */
- n = fz_mini(n, err_pos - buf->start);
- err_pos -= n;
- /* We're going to try to output:
- * <section prior to the error> ">" <the char that tripped> "<" <section after the error>
- */
- /* Is the section prior to the error too long? If so, truncate it with an ellipsis. */
- n = sizeof(text)-1;
- if (err_pos - s > n-PRE_POST_SIZE - 3)
- {
- *d++ = '.';
- *d++ = '.';
- *d++ = '.';
- n -= 3;
- s = err_pos - (n-PRE_POST_SIZE - 3);
- }
- /* Copy the prefix (if there is one) */
- if (err_pos > s)
- {
- n = err_pos - s;
- while (n)
- {
- unsigned char c = *s++;
- *d++ = (c < 32 || c > 127) ? ' ' : c;
- n--;
- }
- }
- /* Marker, char, end marker */
- *d++ = '>', n--;
- if (*err_pos)
- *d++ = *err_pos++, n--;
- *d++ = '<', n--;
- /* Postfix */
- n = (int)strlen((const char *)err_pos);
- if (n <= PRE_POST_SIZE)
- {
- while (n > 0)
- {
- unsigned char c = *err_pos++;
- *d++ = (c < 32 || c > 127) ? ' ' : c;
- n--;
- }
- }
- else
- {
- for (n = PRE_POST_SIZE-3; n > 0; n--)
- {
- unsigned char c = *err_pos++;
- *d++ = (c < 32 || c > 127) ? ' ' : c;
- }
- *d++ = '.';
- *d++ = '.';
- *d++ = '.';
- }
- *d = 0;
- fz_throw(buf->ctx, FZ_ERROR_SYNTAX, "css syntax error: %s (%s:%d) (%s)", msg, buf->file, buf->line, text);
- }
- fz_css *fz_new_css(fz_context *ctx)
- {
- fz_pool *pool = fz_new_pool(ctx);
- fz_css *css = NULL;
- fz_try(ctx)
- {
- css = fz_pool_alloc(ctx, pool, sizeof *css);
- css->pool = pool;
- css->rule = NULL;
- }
- fz_catch(ctx)
- {
- fz_drop_pool(ctx, pool);
- fz_rethrow(ctx);
- }
- return css;
- }
- void fz_drop_css(fz_context *ctx, fz_css *css)
- {
- if (css)
- fz_drop_pool(ctx, css->pool);
- }
- static fz_css_rule *fz_new_css_rule(fz_context *ctx, fz_pool *pool, fz_css_selector *selector, fz_css_property *declaration)
- {
- fz_css_rule *rule = fz_pool_alloc(ctx, pool, sizeof *rule);
- rule->selector = selector;
- rule->declaration = declaration;
- rule->next = NULL;
- return rule;
- }
- static fz_css_selector *fz_new_css_selector(fz_context *ctx, fz_pool *pool, const char *name)
- {
- fz_css_selector *sel = fz_pool_alloc(ctx, pool, sizeof *sel);
- sel->name = name ? fz_pool_strdup(ctx, pool, name) : NULL;
- sel->combine = 0;
- sel->cond = NULL;
- sel->left = NULL;
- sel->right = NULL;
- sel->next = NULL;
- return sel;
- }
- static fz_css_condition *fz_new_css_condition(fz_context *ctx, fz_pool *pool, int type, const char *key, const char *val)
- {
- fz_css_condition *cond = fz_pool_alloc(ctx, pool, sizeof *cond);
- cond->type = type;
- cond->key = key ? fz_pool_strdup(ctx, pool, key) : NULL;
- cond->val = val ? fz_pool_strdup(ctx, pool, val) : NULL;
- cond->next = NULL;
- return cond;
- }
- static fz_css_property *fz_new_css_property(fz_context *ctx, fz_pool *pool, const char *name, fz_css_value *value, int spec)
- {
- struct css_property_info *info = css_property_lookup(name, strlen(name));
- if (info)
- {
- fz_css_property *prop = fz_pool_alloc(ctx, pool, sizeof *prop);
- prop->name = info->key;
- prop->value = value;
- prop->spec = spec;
- prop->important = 0;
- prop->next = NULL;
- return prop;
- }
- return NULL;
- }
- static fz_css_value *fz_new_css_value_x(fz_context *ctx, fz_pool *pool, int type)
- {
- fz_css_value *val = fz_pool_alloc(ctx, pool, sizeof *val);
- val->type = type;
- val->data = NULL;
- val->args = NULL;
- val->next = NULL;
- return val;
- }
- static fz_css_value *fz_new_css_value(fz_context *ctx, fz_pool *pool, int type, const char *data)
- {
- fz_css_value *val = fz_pool_alloc(ctx, pool, sizeof *val);
- val->type = type;
- val->data = fz_pool_strdup(ctx, pool, data);
- val->args = NULL;
- val->next = NULL;
- return val;
- }
- static void css_lex_next(struct lexbuf *buf)
- {
- if (buf->c == 0)
- return;
- buf->s += fz_chartorune(&buf->c, (const char *)buf->s);
- if (buf->c == '\n')
- ++buf->line;
- buf->lookahead = EOF;
- }
- static void css_lex_init(fz_context *ctx, struct lexbuf *buf, fz_pool *pool, const char *s, const char *file)
- {
- buf->ctx = ctx;
- buf->pool = pool;
- buf->s = (const unsigned char *)s;
- buf->lookahead = EOF;
- buf->start = buf->s;
- buf->c = -1;
- buf->file = file;
- buf->line = 1;
- css_lex_next(buf);
- buf->string_len = 0;
- }
- static inline int iswhite(int c)
- {
- return c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == '\f';
- }
- static int isnmstart(int c)
- {
- return c == '\\' || c == '_' || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
- (c >= 128 && c <= UCS_MAX);
- }
- static int isnmchar(int c)
- {
- return c == '\\' || c == '_' || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
- (c >= '0' && c <= '9') || c == '-' || (c >= 128 && c <= UCS_MAX);
- }
- static void css_push_char(struct lexbuf *buf, int c)
- {
- char out[4];
- int n = fz_runetochar(out, c);
- if (buf->string_len + n >= (int)nelem(buf->string))
- fz_css_error(buf, "token too long");
- memcpy(buf->string + buf->string_len, out, n);
- buf->string_len += n;
- }
- static void css_push_zero(struct lexbuf *buf)
- {
- if (buf->string_len + 1 >= (int)nelem(buf->string))
- fz_css_error(buf, "token too long");
- buf->string[buf->string_len] = 0;
- buf->string_len += 1;
- }
- static int css_lex_accept(struct lexbuf *buf, int t)
- {
- if (buf->c == t)
- {
- css_lex_next(buf);
- return 1;
- }
- return 0;
- }
- static void css_lex_expect(struct lexbuf *buf, int t)
- {
- if (!css_lex_accept(buf, t))
- fz_css_error(buf, "unexpected character");
- }
- static int css_lex_number(struct lexbuf *buf)
- {
- while (buf->c >= '0' && buf->c <= '9')
- {
- css_push_char(buf, buf->c);
- css_lex_next(buf);
- }
- if (css_lex_accept(buf, '.'))
- {
- css_push_char(buf, '.');
- while (buf->c >= '0' && buf->c <= '9')
- {
- css_push_char(buf, buf->c);
- css_lex_next(buf);
- }
- }
- if (css_lex_accept(buf, '%'))
- {
- css_push_char(buf, '%');
- css_push_zero(buf);
- return CSS_PERCENT;
- }
- if (isnmstart(buf->c))
- {
- css_push_char(buf, buf->c);
- css_lex_next(buf);
- while (isnmchar(buf->c))
- {
- css_push_char(buf, buf->c);
- css_lex_next(buf);
- }
- css_push_zero(buf);
- return CSS_LENGTH;
- }
- css_push_zero(buf);
- return CSS_NUMBER;
- }
- static int css_lex_keyword(struct lexbuf *buf)
- {
- while (isnmchar(buf->c))
- {
- css_push_char(buf, buf->c);
- css_lex_next(buf);
- }
- css_push_zero(buf);
- return CSS_KEYWORD;
- }
- static int css_lex_hash(struct lexbuf *buf)
- {
- while (isnmchar(buf->c))
- {
- css_push_char(buf, buf->c);
- css_lex_next(buf);
- }
- css_push_zero(buf);
- return CSS_HASH;
- }
- static int css_lex_string(struct lexbuf *buf, int q)
- {
- while (buf->c && buf->c != q)
- {
- if (css_lex_accept(buf, '\\'))
- {
- if (css_lex_accept(buf, 'n'))
- css_push_char(buf, '\n');
- else if (css_lex_accept(buf, 'r'))
- css_push_char(buf, '\r');
- else if (css_lex_accept(buf, 'f'))
- css_push_char(buf, '\f');
- else if (css_lex_accept(buf, '\f'))
- /* line continuation */ ;
- else if (css_lex_accept(buf, '\n'))
- /* line continuation */ ;
- else if (css_lex_accept(buf, '\r'))
- css_lex_accept(buf, '\n');
- else
- {
- css_push_char(buf, buf->c);
- css_lex_next(buf);
- }
- }
- else
- {
- css_push_char(buf, buf->c);
- css_lex_next(buf);
- }
- }
- css_lex_expect(buf, q);
- css_push_zero(buf);
- return CSS_STRING;
- }
- static void css_lex_uri(struct lexbuf *buf)
- {
- while (buf->c && buf->c != ')' && !iswhite(buf->c))
- {
- if (css_lex_accept(buf, '\\'))
- {
- if (css_lex_accept(buf, 'n'))
- css_push_char(buf, '\n');
- else if (css_lex_accept(buf, 'r'))
- css_push_char(buf, '\r');
- else if (css_lex_accept(buf, 'f'))
- css_push_char(buf, '\f');
- else
- {
- css_push_char(buf, buf->c);
- css_lex_next(buf);
- }
- }
- else if (buf->c == '!' || buf->c == '#' || buf->c == '$' || buf->c == '%' || buf->c == '&' ||
- (buf->c >= '*' && buf->c <= '[') ||
- (buf->c >= ']' && buf->c <= '~') ||
- buf->c > 159)
- {
- css_push_char(buf, buf->c);
- css_lex_next(buf);
- }
- else
- fz_css_error(buf, "unexpected character in url");
- }
- css_push_zero(buf);
- }
- static int css_lex(struct lexbuf *buf)
- {
- int t;
- // TODO: keyword escape sequences
- buf->string_len = 0;
- restart:
- if (buf->c == 0)
- return EOF;
- if (iswhite(buf->c))
- {
- while (iswhite(buf->c))
- css_lex_next(buf);
- return ' ';
- }
- if (css_lex_accept(buf, '/'))
- {
- if (css_lex_accept(buf, '*'))
- {
- while (buf->c)
- {
- if (css_lex_accept(buf, '*'))
- {
- while (buf->c == '*')
- css_lex_next(buf);
- if (css_lex_accept(buf, '/'))
- goto restart;
- }
- css_lex_next(buf);
- }
- fz_css_error(buf, "unterminated comment");
- }
- return '/';
- }
- if (css_lex_accept(buf, '<'))
- {
- if (css_lex_accept(buf, '!'))
- {
- css_lex_expect(buf, '-');
- css_lex_expect(buf, '-');
- goto restart; /* ignore CDO */
- }
- return '<';
- }
- if (css_lex_accept(buf, '-'))
- {
- if (css_lex_accept(buf, '-'))
- {
- if (css_lex_accept(buf, '>'))
- goto restart; /* ignore CDC */
- }
- if (isnmstart(buf->c))
- {
- css_push_char(buf, '-');
- return css_lex_keyword(buf);
- }
- return '-';
- }
- if (css_lex_accept(buf, '.'))
- {
- if (buf->c >= '0' && buf->c <= '9')
- {
- css_push_char(buf, '.');
- return css_lex_number(buf);
- }
- return '.';
- }
- if (css_lex_accept(buf, '#'))
- {
- if (isnmchar(buf->c))
- return css_lex_hash(buf);
- return '#';
- }
- if (css_lex_accept(buf, '"'))
- return css_lex_string(buf, '"');
- if (css_lex_accept(buf, '\''))
- return css_lex_string(buf, '\'');
- if (buf->c >= '0' && buf->c <= '9')
- return css_lex_number(buf);
- if (css_lex_accept(buf, 'u'))
- {
- if (css_lex_accept(buf, 'r'))
- {
- if (css_lex_accept(buf, 'l'))
- {
- if (css_lex_accept(buf, '('))
- {
- while (iswhite(buf->c))
- css_lex_next(buf);
- if (css_lex_accept(buf, '"'))
- css_lex_string(buf, '"');
- else if (css_lex_accept(buf, '\''))
- css_lex_string(buf, '\'');
- else
- css_lex_uri(buf);
- while (iswhite(buf->c))
- css_lex_next(buf);
- css_lex_expect(buf, ')');
- return CSS_URI;
- }
- css_push_char(buf, 'u');
- css_push_char(buf, 'r');
- css_push_char(buf, 'l');
- return css_lex_keyword(buf);
- }
- css_push_char(buf, 'u');
- css_push_char(buf, 'r');
- return css_lex_keyword(buf);
- }
- css_push_char(buf, 'u');
- return css_lex_keyword(buf);
- }
- if (isnmstart(buf->c))
- {
- css_push_char(buf, buf->c);
- css_lex_next(buf);
- return css_lex_keyword(buf);
- }
- t = buf->c;
- css_lex_next(buf);
- return t;
- }
- static void next(struct lexbuf *buf)
- {
- buf->lookahead = css_lex(buf);
- }
- static int accept(struct lexbuf *buf, int t)
- {
- if (buf->lookahead == t)
- {
- next(buf);
- return 1;
- }
- return 0;
- }
- static void expect(struct lexbuf *buf, int t)
- {
- if (accept(buf, t))
- return;
- fz_css_error(buf, "unexpected token");
- }
- static void white(struct lexbuf *buf)
- {
- while (buf->lookahead == ' ')
- next(buf);
- }
- static int iscond(int t)
- {
- return t == ':' || t == '.' || t == '[' || t == CSS_HASH;
- }
- static fz_css_value *parse_term(struct lexbuf *buf)
- {
- fz_css_value *v;
- if (buf->lookahead == '+' || buf->lookahead == '-')
- {
- float sign = buf->lookahead == '-' ? -1 : 1;
- next(buf);
- if (buf->lookahead != CSS_NUMBER && buf->lookahead != CSS_LENGTH && buf->lookahead != CSS_PERCENT)
- fz_css_error(buf, "expected number");
- if (sign < 0)
- {
- v = fz_new_css_value_x(buf->ctx, buf->pool, buf->lookahead);
- v->data = fz_pool_alloc(buf->ctx, buf->pool, strlen(buf->string) + 2);
- v->data[0] = '-';
- strcpy(v->data + 1, buf->string);
- }
- else
- {
- v = fz_new_css_value(buf->ctx, buf->pool, buf->lookahead, buf->string);
- }
- next(buf);
- white(buf);
- return v;
- }
- if (buf->lookahead == CSS_KEYWORD)
- {
- v = fz_new_css_value(buf->ctx, buf->pool, CSS_KEYWORD, buf->string);
- next(buf);
- if (accept(buf, '('))
- {
- white(buf);
- v->type = '(';
- v->args = parse_expr(buf);
- expect(buf, ')');
- }
- white(buf);
- return v;
- }
- switch (buf->lookahead)
- {
- case CSS_HASH:
- case CSS_STRING:
- case CSS_URI:
- case CSS_NUMBER:
- case CSS_LENGTH:
- case CSS_PERCENT:
- v = fz_new_css_value(buf->ctx, buf->pool, buf->lookahead, buf->string);
- next(buf);
- white(buf);
- return v;
- }
- fz_css_error(buf, "expected value");
- }
- static fz_css_value *parse_expr(struct lexbuf *buf)
- {
- fz_css_value *head, *tail;
- head = tail = parse_term(buf);
- while (buf->lookahead != '}' && buf->lookahead != ';' && buf->lookahead != '!' &&
- buf->lookahead != ')' && buf->lookahead != EOF)
- {
- if (accept(buf, ','))
- {
- white(buf);
- if (buf->lookahead != ';')
- {
- tail = tail->next = fz_new_css_value(buf->ctx, buf->pool, ',', ",");
- tail = tail->next = parse_term(buf);
- }
- }
- else if (accept(buf, '/'))
- {
- white(buf);
- tail = tail->next = fz_new_css_value(buf->ctx, buf->pool, '/', "/");
- tail = tail->next = parse_term(buf);
- }
- else
- {
- tail = tail->next = parse_term(buf);
- }
- }
- return head;
- }
- static fz_css_property *parse_declaration(struct lexbuf *buf)
- {
- fz_css_property *p;
- if (buf->lookahead != CSS_KEYWORD)
- fz_css_error(buf, "expected keyword in property");
- p = fz_new_css_property(buf->ctx, buf->pool, buf->string, NULL, 0);
- next(buf);
- white(buf);
- expect(buf, ':');
- white(buf);
- if (p)
- p->value = parse_expr(buf);
- else
- (void) parse_expr(buf);
- /* !important */
- if (accept(buf, '!'))
- {
- white(buf);
- if (buf->lookahead != CSS_KEYWORD || strcmp(buf->string, "important"))
- fz_css_error(buf, "expected keyword 'important' after '!'");
- if (p)
- p->important = 1;
- next(buf);
- white(buf);
- }
- return p;
- }
- static fz_css_property *parse_declaration_list(struct lexbuf *buf)
- {
- fz_css_property *head, *tail = NULL, *p;
- white(buf);
- if (buf->lookahead == '}' || buf->lookahead == EOF)
- return NULL;
- p = parse_declaration(buf);
- if (p)
- tail = p;
- head = tail;
- while (accept(buf, ';'))
- {
- white(buf);
- if (buf->lookahead != '}' && buf->lookahead != ';' && buf->lookahead != EOF)
- {
- p = parse_declaration(buf);
- if (p)
- {
- if (!head)
- head = tail = p;
- else
- tail = tail->next = p;
- }
- }
- }
- return head;
- }
- static char *parse_attrib_value(struct lexbuf *buf)
- {
- char *s;
- if (buf->lookahead == CSS_KEYWORD || buf->lookahead == CSS_STRING)
- {
- s = fz_pool_strdup(buf->ctx, buf->pool, buf->string);
- next(buf);
- white(buf);
- return s;
- }
- fz_css_error(buf, "expected attribute value");
- }
- static fz_css_condition *parse_condition(struct lexbuf *buf)
- {
- fz_css_condition *c;
- if (accept(buf, ':'))
- {
- (void)accept(buf, ':'); /* swallow css3 :: syntax and pretend it's a normal pseudo-class */
- if (buf->lookahead != CSS_KEYWORD)
- fz_css_error(buf, "expected keyword after ':'");
- c = fz_new_css_condition(buf->ctx, buf->pool, ':', "pseudo", buf->string);
- next(buf);
- if (accept(buf, '('))
- {
- white(buf);
- if (accept(buf, CSS_KEYWORD))
- white(buf);
- expect(buf, ')');
- }
- return c;
- }
- if (accept(buf, '.'))
- {
- if (buf->lookahead != CSS_KEYWORD)
- fz_css_error(buf, "expected keyword after '.'");
- c = fz_new_css_condition(buf->ctx, buf->pool, '.', "class", buf->string);
- next(buf);
- return c;
- }
- if (accept(buf, '['))
- {
- white(buf);
- if (buf->lookahead != CSS_KEYWORD)
- fz_css_error(buf, "expected keyword after '['");
- c = fz_new_css_condition(buf->ctx, buf->pool, '[', buf->string, NULL);
- next(buf);
- white(buf);
- if (accept(buf, '='))
- {
- c->type = '=';
- c->val = parse_attrib_value(buf);
- }
- else if (accept(buf, '|'))
- {
- expect(buf, '=');
- c->type = '|';
- c->val = parse_attrib_value(buf);
- }
- else if (accept(buf, '~'))
- {
- expect(buf, '=');
- c->type = '~';
- c->val = parse_attrib_value(buf);
- }
- expect(buf, ']');
- return c;
- }
- if (buf->lookahead == CSS_HASH)
- {
- c = fz_new_css_condition(buf->ctx, buf->pool, '#', "id", buf->string);
- next(buf);
- return c;
- }
- fz_css_error(buf, "expected condition");
- }
- static fz_css_condition *parse_condition_list(struct lexbuf *buf)
- {
- fz_css_condition *head, *tail;
- head = tail = parse_condition(buf);
- while (iscond(buf->lookahead))
- {
- tail = tail->next = parse_condition(buf);
- }
- return head;
- }
- static fz_css_selector *parse_simple_selector(struct lexbuf *buf)
- {
- fz_css_selector *s;
- if (accept(buf, '*'))
- {
- s = fz_new_css_selector(buf->ctx, buf->pool, NULL);
- if (iscond(buf->lookahead))
- s->cond = parse_condition_list(buf);
- return s;
- }
- else if (buf->lookahead == CSS_KEYWORD)
- {
- s = fz_new_css_selector(buf->ctx, buf->pool, buf->string);
- next(buf);
- if (iscond(buf->lookahead))
- s->cond = parse_condition_list(buf);
- return s;
- }
- else if (iscond(buf->lookahead))
- {
- s = fz_new_css_selector(buf->ctx, buf->pool, NULL);
- s->cond = parse_condition_list(buf);
- return s;
- }
- fz_css_error(buf, "expected selector");
- }
- static fz_css_selector *parse_combinator(struct lexbuf *buf, int c, fz_css_selector *a)
- {
- fz_css_selector *sel, *b;
- white(buf);
- b = parse_simple_selector(buf);
- sel = fz_new_css_selector(buf->ctx, buf->pool, NULL);
- sel->combine = c;
- sel->left = a;
- sel->right = b;
- return sel;
- }
- static fz_css_selector *parse_selector(struct lexbuf *buf)
- {
- fz_css_selector *sel = parse_simple_selector(buf);
- for (;;)
- {
- if (accept(buf, ' '))
- {
- white(buf);
- if (accept(buf, '+'))
- sel = parse_combinator(buf, '+', sel);
- else if (accept(buf, '>'))
- sel = parse_combinator(buf, '>', sel);
- else if (buf->lookahead != ',' && buf->lookahead != '{' && buf->lookahead != EOF)
- sel = parse_combinator(buf, ' ', sel);
- else
- break;
- }
- else if (accept(buf, '+'))
- sel = parse_combinator(buf, '+', sel);
- else if (accept(buf, '>'))
- sel = parse_combinator(buf, '>', sel);
- else
- break;
- }
- return sel;
- }
- static fz_css_selector *parse_selector_list(struct lexbuf *buf)
- {
- fz_css_selector *head, *tail;
- head = tail = parse_selector(buf);
- while (accept(buf, ','))
- {
- white(buf);
- tail = tail->next = parse_selector(buf);
- }
- return head;
- }
- static fz_css_rule *parse_ruleset(struct lexbuf *buf)
- {
- fz_css_selector *s = NULL;
- fz_css_property *p = NULL;
- fz_try(buf->ctx)
- {
- s = parse_selector_list(buf);
- expect(buf, '{');
- p = parse_declaration_list(buf);
- expect(buf, '}');
- white(buf);
- }
- fz_catch(buf->ctx)
- {
- fz_rethrow_unless(buf->ctx, FZ_ERROR_SYNTAX);
- fz_report_error(buf->ctx);
- while (buf->lookahead != EOF)
- {
- if (accept(buf, '}'))
- {
- white(buf);
- break;
- }
- next(buf);
- }
- return NULL;
- }
- return fz_new_css_rule(buf->ctx, buf->pool, s, p);
- }
- static fz_css_rule *parse_at_page(struct lexbuf *buf)
- {
- fz_css_selector *s = NULL;
- fz_css_property *p = NULL;
- white(buf);
- if (accept(buf, ':'))
- {
- expect(buf, CSS_KEYWORD);
- white(buf);
- }
- expect(buf, '{');
- p = parse_declaration_list(buf);
- expect(buf, '}');
- white(buf);
- s = fz_new_css_selector(buf->ctx, buf->pool, "@page");
- return fz_new_css_rule(buf->ctx, buf->pool, s, p);
- }
- static fz_css_rule *parse_at_font_face(struct lexbuf *buf)
- {
- fz_css_selector *s = NULL;
- fz_css_property *p = NULL;
- white(buf);
- expect(buf, '{');
- p = parse_declaration_list(buf);
- expect(buf, '}');
- white(buf);
- s = fz_new_css_selector(buf->ctx, buf->pool, "@font-face");
- return fz_new_css_rule(buf->ctx, buf->pool, s, p);
- }
- static void parse_at_rule(struct lexbuf *buf)
- {
- expect(buf, CSS_KEYWORD);
- /* skip until '{' or ';' */
- while (buf->lookahead != EOF)
- {
- if (accept(buf, ';'))
- {
- white(buf);
- return;
- }
- if (accept(buf, '{'))
- {
- int depth = 1;
- while (buf->lookahead != EOF && depth > 0)
- {
- if (accept(buf, '{'))
- ++depth;
- else if (accept(buf, '}'))
- --depth;
- else
- next(buf);
- }
- white(buf);
- return;
- }
- next(buf);
- }
- }
- static fz_css_rule *parse_stylesheet(struct lexbuf *buf, fz_css_rule *chain)
- {
- fz_css_rule *rule, **nextp, *tail;
- tail = chain;
- if (tail)
- {
- while (tail->next)
- tail = tail->next;
- nextp = &tail->next;
- }
- else
- {
- nextp = &tail;
- }
- white(buf);
- while (buf->lookahead != EOF)
- {
- if (accept(buf, '@'))
- {
- if (buf->lookahead == CSS_KEYWORD && !strcmp(buf->string, "page"))
- {
- next(buf);
- rule = *nextp = parse_at_page(buf);
- nextp = &rule->next;
- }
- else if (buf->lookahead == CSS_KEYWORD && !strcmp(buf->string, "font-face"))
- {
- next(buf);
- rule = *nextp = parse_at_font_face(buf);
- nextp = &rule->next;
- }
- else
- {
- parse_at_rule(buf);
- }
- }
- else
- {
- fz_css_rule *x = parse_ruleset(buf);
- if (x)
- {
- rule = *nextp = x;
- nextp = &rule->next;
- }
- }
- white(buf);
- }
- return chain ? chain : tail;
- }
- const char *fz_css_property_name(int key)
- {
- const char *name = "unknown";
- size_t i;
- for (i = 0; i < nelem(css_property_list); ++i)
- if (*css_property_list[i].name && css_property_list[i].key == key)
- name = css_property_list[i].name;
- return name;
- }
- fz_css_property *fz_parse_css_properties(fz_context *ctx, fz_pool *pool, const char *source)
- {
- struct lexbuf buf;
- css_lex_init(ctx, &buf, pool, source, "<inline>");
- next(&buf);
- return parse_declaration_list(&buf);
- }
- void fz_parse_css(fz_context *ctx, fz_css *css, const char *source, const char *file)
- {
- struct lexbuf buf;
- css_lex_init(ctx, &buf, css->pool, source, file);
- next(&buf);
- css->rule = parse_stylesheet(&buf, css->rule);
- }
|