Bug Summary

File:svgtiny_parse.c
Warning:line 358, column 14
Value stored to 'idend' during its initialization is never read

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name svgtiny_parse.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -pic-is-pie -mframe-pointer=none -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/var/lib/jenkins/workspace/scan-build-libsvgtiny -resource-dir /usr/lib/llvm-14/lib/clang/14.0.6 -D _BSD_SOURCE -D _DEFAULT_SOURCE -I /var/lib/jenkins/workspace/scan-build-libsvgtiny/include/ -I /var/lib/jenkins/workspace/scan-build-libsvgtiny/src -D _ALIGNED=__attribute__((aligned)) -D STMTEXPR=1 -D DEBUG -I /var/lib/jenkins/artifacts-x86_64-linux-gnu/include -internal-isystem /usr/lib/llvm-14/lib/clang/14.0.6/include -internal-isystem /usr/local/include -internal-isystem /usr/bin/../lib/gcc/x86_64-linux-gnu/12/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -Og -Wwrite-strings -Wno-error -std=c99 -fconst-strings -fdebug-compilation-dir=/var/lib/jenkins/workspace/scan-build-libsvgtiny -ferror-limit 19 -fgnuc-version=4.2.1 -analyzer-display-progress -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /var/lib/jenkins/workspace/scan-build-libsvgtiny/clangScanBuildReports/2024-12-10-004627-2209150-1 -x c src/svgtiny_parse.c
1/*
2 * This file is part of Libsvgtiny
3 * Licensed under the MIT License,
4 * http://opensource.org/licenses/mit-license.php
5 * Copyright 2024 Vincent Sanders <vince@netsurf-browser.org>
6 */
7
8#include <stddef.h>
9#include <math.h>
10#include <float.h>
11#include <string.h>
12
13
14#include "svgtiny.h"
15#include "svgtiny_internal.h"
16#include "svgtiny_parse.h"
17
18/* Source file generated by `gperf`. */
19#include "autogenerated_colors.c"
20
21/** xlink XML namespace https://www.w3.org/TR/xlink11/#att-method */
22#define XLINK_NS"http://www.w3.org/1999/xlink" "http://www.w3.org/1999/xlink"
23
24#define SIGNIFICAND_MAX100000000 100000000
25#define EXPONENT_MAX38 38
26#define EXPONENT_MIN-38 -38
27
28
29/**
30 * parse text string into a float
31 *
32 * \param[in] text string to parse
33 * \param[in,out] textend On input is the end of the string in \a text. Output
34 * is set to indicate the character after those used to
35 * parse the number.
36 * \param[out] value The resulting value from the parse
37 * \return svgtiny_OK and the value updated else svgtiny_SVG_ERROR and value
38 * left unchanged. The textend is always updated to indicate the last
39 * character parsed.
40 *
41 * A number is started by 0 (started by sign) or more spaces (0x20), tabs (0x09),
42 * carridge returns (0xD) and newlines (0xA) followed by a decimal number.
43 * A number is defined as https://www.w3.org/TR/css-syntax-3/#typedef-number-token
44 *
45 * This state machine parses number text into a sign, significand and exponent
46 * then builds a single precision float from those values.
47 *
48 * The significand stores the first nine decimal digits of the number (floats
49 * only have seven thus ensuring nothing is lost in conversion).
50 *
51 * The exponent is limited to 10^38 (again the float limit) and results in
52 * FLT_MAX being returned with a range error.
53 *
54 * An exponent below 10^-38 will result in emitting the smallest value possible
55 * FLT_MIN with a range error.
56 *
57 * This is not a strtof clone because it has an input length limit instead of
58 * needing null terminated input, is not locale dependent and only processes
59 * decimal numbers (not hex etc.). These limitations are necessary to process
60 * the input correctly.
61 */
62svgtiny_code
63svgtiny_parse_number(const char *text, const char **textend, float *value)
64{
65 const char *cur; /* text cursor */
66 enum {
67 STATE_WHITESPACE, /* processing whitespace */
68 STATE_NUMBER, /* processing whole number */
69 STATE_FRACT, /* processing fractional part */
70 STATE_SIGNEXPONENT, /* processing exponent part */
71 STATE_EXPONENT, /* processing exponent part have seen sign */
72 } state = STATE_WHITESPACE;
73 enum b10sign {
74 SPOSITIVE,
75 SNEGATIVE,
76 };
77 enum b10sign sign = SPOSITIVE; /* sign of number being constructed */
78 unsigned int significand = 0; /* significand of number being constructed */
79 int exponent = 0; /* exponent of the significand (distinct from exponent part) */
80 enum b10sign exp_sign = SPOSITIVE; /* sign of exponent part */
81 unsigned int exp_value = 0; /* value of the exponent part */
82 unsigned int digit_count = 0; /* has an actual digit been seen */
83
84
85 for (cur = text; cur < (*textend); cur++) {
86 switch (state) {
87 case STATE_WHITESPACE:
88 switch (*cur) {
89 case 0x9: case 0xA: case 0xD: case 0x20:
90 /* skip whitespace */
91 continue;
92
93 case '.':
94 /* new number with fraction part */
95 digit_count = 0;
96 state = STATE_FRACT;
97 continue;
98
99 case '-':
100 sign = SNEGATIVE;
101 digit_count = 0;
102 state = STATE_NUMBER;
103 continue;
104
105 case '+':
106 digit_count = 0;
107 state = STATE_NUMBER;
108 continue;
109
110 case '0': case '1': case '2': case '3': case '4':
111 case '5': case '6': case '7': case '8': case '9':
112 significand = (*cur - '0');
113 digit_count = 1;
114 state = STATE_NUMBER;
115 continue;
116
117 default:
118 /* anything else completes conversion */
119 goto svgtiny_parse_number_end;
120 }
121 break;
122
123 case STATE_NUMBER:
124 switch(*cur) {
125 case '.':
126 state = STATE_FRACT;
127 continue;
128
129 case '0': case '1': case '2': case '3': case '4':
130 case '5': case '6': case '7': case '8': case '9':
131 digit_count += 1;
132 if (significand < SIGNIFICAND_MAX100000000) {
133 /* still space to acumulate digits in the significand */
134 significand = (significand * 10) + (*cur - '0');
135 } else {
136 /* significand has accumulated all the
137 * digits it can so just extend the
138 * exponent */
139 exponent += 1;
140 }
141 continue;
142
143 case 'e':
144 case 'E':
145 if (digit_count == 0) {
146 /* number has no digits before exponent which is a syntax error */
147 goto svgtiny_parse_number_end;
148
149 }
150 state = STATE_SIGNEXPONENT;
151 continue;
152
153 default:
154 /* anything else completes conversion */
155 goto svgtiny_parse_number_end;
156 }
157
158 break;
159
160 case STATE_FRACT:
161 switch(*cur) {
162 case '0': case '1': case '2': case '3': case '4':
163 case '5': case '6': case '7': case '8': case '9':
164 digit_count += 1;
165 if (significand < SIGNIFICAND_MAX100000000) {
166 /* still space to acumulate digits in the significand */
167 significand = (significand * 10) + (*cur - '0');
168 exponent -= 1;
169 }
170
171 continue;
172
173 case 'e':
174 case 'E':
175 if (digit_count == 0) {
176 /* number has no digits before exponent which is a syntax error */
177 goto svgtiny_parse_number_end;
178
179 }
180 state = STATE_SIGNEXPONENT;
181 continue;
182
183 default:
184 /* anything else completes conversion */
185 goto svgtiny_parse_number_end;
186
187 }
188 break;
189
190 case STATE_SIGNEXPONENT:
191 switch(*cur) {
192 case '-':
193 exp_sign = SNEGATIVE;
194 state = STATE_EXPONENT;
195 continue;
196
197 case '+':
198 state = STATE_EXPONENT;
199 continue;
200
201 case '0': case '1': case '2': case '3': case '4':
202 case '5': case '6': case '7': case '8': case '9':
203 if (exp_value < 1000) {
204 /* still space to acumulate digits in the exponent value */
205 exp_value = (exp_value * 10) + (*cur - '0');
206 }
207 state = STATE_EXPONENT;
208 continue;
209
210 default:
211 /* anything else completes conversion */
212 goto svgtiny_parse_number_end;
213
214 }
215 break;
216
217 case STATE_EXPONENT:
218 switch(*cur) {
219 case '0': case '1': case '2': case '3': case '4':
220 case '5': case '6': case '7': case '8': case '9':
221 if (exp_value < 1000) {
222 /* still space to acumulate digits in the exponent value */
223 exp_value = (exp_value * 10) + (*cur - '0');
224 }
225
226 continue;
227
228 default:
229 /* anything else completes conversion */
230 goto svgtiny_parse_number_end;
231
232 }
233 break;
234 }
235 }
236
237svgtiny_parse_number_end:
238 *textend = cur;
239
240 if (state == STATE_WHITESPACE) {
241 /* no characters except whitespace */
242 return svgtiny_SVG_ERROR;
243 }
244
245 if (digit_count == 0) {
246 /* number had no digits (only +-.) which is a syntax error */
247 return svgtiny_SVG_ERROR;
248 }
249
250 /* deal with exponent value */
251 if (exp_sign == SNEGATIVE) {
252 exponent -= exp_value;
253 } else {
254 exponent += exp_value;
255 }
256
257 /* deal with number too large to represent */
258 if (exponent > EXPONENT_MAX38) {
259 if (sign == SPOSITIVE) {
260 *value = FLT_MAX3.40282347e+38F;
261 } else {
262 *value = -FLT_MAX3.40282347e+38F;
263 }
264 return svgtiny_OK;
265 /*return svgtiny_RANGE;*/
266 }
267
268 /* deal with number too small to represent */
269 if (exponent < EXPONENT_MIN-38) {
270 if (sign == SPOSITIVE) {
271 *value = FLT_MIN1.17549435e-38F;
272 } else {
273 *value = -FLT_MIN1.17549435e-38F;
274 }
275 return svgtiny_OK;
276 /*return svgtiny_RANGE;*/
277 }
278
279 if (sign == SPOSITIVE) {
280 *value = (float)significand * powf(10, exponent);
281 } else {
282 *value = -(float)significand * powf(10, exponent);
283 }
284
285 return svgtiny_OK;
286}
287
288
289/**
290 * advance across hexidecimal characters
291 *
292 * \param cursor current cursor
293 * \param textend end of buffer
294 */
295static inline void advance_hex(const char **cursor, const char *textend)
296{
297 while ((*cursor) < textend) {
298 if (((**cursor < 0x30 /* 0 */) || (**cursor > 0x39 /* 9 */)) &&
299 ((**cursor < 0x41 /* A */) || (**cursor > 0x46 /* F */)) &&
300 ((**cursor < 0x61 /* a */) || (**cursor > 0x66 /* f */))) {
301 break;
302 }
303 (*cursor)++;
304 }
305}
306
307
308/**
309 * advance over SVG id
310 *
311 * \param cursor current cursor
312 * \param textend end of buffer
313 *
314 * https://www.w3.org/TR/SVG2/struct.html#IDAttribute
315 */
316static inline void advance_id(const char **cursor, const char *textend)
317{
318 while((*cursor) < textend) {
319 if ((**cursor == 0x20) ||
320 (**cursor == 0x09) ||
321 (**cursor == 0x0A) ||
322 (**cursor == 0x0D) ||
323 (**cursor == ')')) {
324 break;
325 }
326 (*cursor)++;
327 }
328}
329
330static inline void advance_property_name(const char **cursor, const char *textend)
331{
332 while ((*cursor) < textend) {
333 if (((**cursor < 0x30 /* 0 */) || (**cursor > 0x39 /* 9 */)) &&
334 ((**cursor < 0x41 /* A */) || (**cursor > 0x5A /* Z */)) &&
335 ((**cursor < 0x61 /* a */) || (**cursor > 0x7A /* z */)) &&
336 (**cursor != '-') &&
337 (**cursor != '_')) {
338 break;
339 }
340 (*cursor)++;
341 }
342}
343
344
345/**
346 * parse text to obtain identifier from a url with a fragment
347 *
348 * This limits url links to identifiers within the document.
349 */
350static inline svgtiny_code
351parse_url_fragment(const char **text,
352 const char *textend,
353 const char **idout,
354 int *idlenout)
355{
356 const char *cursor = *text;
357 const char *idstart = cursor;
358 const char *idend = cursor;
Value stored to 'idend' during its initialization is never read
359
360 advance_whitespace(&cursor, textend);
361
362 if (*cursor != '#') {
363 /* not a fragment id */
364 return svgtiny_SVG_ERROR;
365 }
366 cursor++;
367
368 idstart = cursor;
369 advance_id(&cursor, textend);
370 idend = cursor;
371
372 if ((idend - idstart) == 0) {
373 /* no id */
374 return svgtiny_SVG_ERROR;
375 }
376
377 advance_whitespace(&cursor, textend);
378
379 /* url syntax is correct update output */
380 *text = cursor;
381 *idout = idstart;
382 *idlenout = idend - idstart;
383
384 return svgtiny_OK;
385}
386
387
388/**
389 * get an element in the document from a url
390 */
391static svgtiny_code
392element_from_url(const char **url,
393 size_t urllen,
394 struct svgtiny_parse_state *state,
395 dom_element **element)
396{
397 svgtiny_code res;
398 dom_exception exc;
399 const char *cursor = *url;
400 const char *urlend = *url + urllen;
401 const char *id;
402 int idlen = 0;
403 dom_string *id_str;
404
405 /**
406 * \todo deal with illegal references (circular etc)
407 * https://svgwg.org/svg2-draft/linking.html#TermInvalidReference
408 *
409 * \todo parsing the fragment out of the url only implements same
410 * document references from fragments and might be extended.
411 * https://svgwg.org/svg2-draft/linking.html#TermSameDocumentURL
412 */
413 res = parse_url_fragment(&cursor, urlend, &id, &idlen);
414 if (res != svgtiny_OK) {
415 return res;
416 }
417
418 exc = dom_string_create_interned((const uint8_t *)id, idlen, &id_str);
419 if (exc != DOM_NO_ERR) {
420 return svgtiny_LIBDOM_ERROR;
421 }
422
423 exc = dom_document_get_element_by_id(state->document, id_str, element)dom_document_get_element_by_id((dom_document *) (state->document
), (id_str), (struct dom_element **) (element))
;
424 dom_string_unref(id_str);
425 if (exc != DOM_NO_ERR) {
426 return svgtiny_LIBDOM_ERROR;
427 }
428
429 *url = cursor;
430 return svgtiny_OK;
431}
432
433
434enum transform_type {
435 TRANSFORM_UNK,
436 TRANSFORM_MATRIX,
437 TRANSFORM_TRANSLATE,
438 TRANSFORM_SCALE,
439 TRANSFORM_ROTATE,
440 TRANSFORM_SKEWX,
441 TRANSFORM_SKEWY,
442};
443
444
445static inline svgtiny_code
446apply_transform(enum transform_type transform,
447 int paramc,
448 float *paramv,
449 struct svgtiny_transformation_matrix *tm)
450{
451 /* initialise matrix to cartesian standard basis
452 * | 1 0 0 |
453 * | 0 1 0 |
454 * | 0 0 1 |
455 */
456 float a = 1, b = 0, c = 0, d = 1, e = 0, f = 0; /* parameter matrix */
457 float za,zb,zc,zd,ze,zf; /* temporary matrix */
458 float angle;
459
460 /* there must be at least one parameter */
461 if (paramc < 1) {
462 return svgtiny_SVG_ERROR;
463 }
464
465 switch (transform) {
466 case TRANSFORM_MATRIX:
467 if (paramc != 6) {
468 /* too few parameters */
469 return svgtiny_SVG_ERROR;
470 }
471 a=paramv[0];
472 b=paramv[1];
473 c=paramv[2];
474 d=paramv[3];
475 e=paramv[4];
476 f=paramv[5];
477 break;
478
479 case TRANSFORM_TRANSLATE:
480 e = paramv[0];
481 if (paramc == 2) {
482 f = paramv[1];
483 }
484 break;
485
486 case TRANSFORM_SCALE:
487 a = d = paramv[0];
488 if (paramc == 2) {
489 d = paramv[1];
490 }
491 break;
492
493 case TRANSFORM_ROTATE:
494 angle = paramv[0] / 180 * M_PI3.14159265358979323846;
495 a = cos(angle);
496 b = sin(angle);
497 c = -sin(angle);
498 d = cos(angle);
499
500 if (paramc == 3) {
501 e = -paramv[1] * cos(angle) +
502 paramv[2] * sin(angle) +
503 paramv[1];
504 f = -paramv[1] * sin(angle) -
505 paramv[2] * cos(angle) +
506 paramv[2];
507 } else if (paramc == 2) {
508 /* one or three paramters only*/
509 return svgtiny_SVG_ERROR;
510 }
511
512 break;
513
514 case TRANSFORM_SKEWX:
515 angle = paramv[0] / 180 * M_PI3.14159265358979323846;
516 c = tan(angle);
517 break;
518
519 case TRANSFORM_SKEWY:
520 angle = paramv[0] / 180 * M_PI3.14159265358979323846;
521 b = tan(angle);
522 break;
523
524 default:
525 /* unknown transform (not be possible to be here) */
526 return svgtiny_SVG_ERROR;
527 }
528
529 za = tm->a * a + tm->c * b;
530 zb = tm->b * a + tm->d * b;
531 zc = tm->a * c + tm->c * d;
532 zd = tm->b * c + tm->d * d;
533 ze = tm->a * e + tm->c * f + tm->e;
534 zf = tm->b * e + tm->d * f + tm->f;
535
536 tm->a = za;
537 tm->b = zb;
538 tm->c = zc;
539 tm->d = zd;
540 tm->e = ze;
541 tm->f = zf;
542
543 return svgtiny_OK;
544}
545
546
547/* determine transform function */
548static inline svgtiny_code
549parse_transform_function(const char **cursor,
550 const char *textend,
551 enum transform_type *transformout)
552{
553 const char *tokstart;
554 size_t toklen;
555 enum transform_type transform = TRANSFORM_UNK;
556
557 tokstart = *cursor;
558 while ((*cursor) < textend) {
559 if ((**cursor != 0x61 /* a */) &&
560 (**cursor != 0x65 /* e */) &&
561 (**cursor != 0x74 /* t */) &&
562 (**cursor != 0x73 /* s */) &&
563 (**cursor != 0x72 /* r */) &&
564 (**cursor != 0x6B /* k */) &&
565 (**cursor != 0x6C /* l */) &&
566 (**cursor != 0x77 /* w */) &&
567 (**cursor != 0x63 /* c */) &&
568 (**cursor != 0x69 /* i */) &&
569 (**cursor != 0x6D /* m */) &&
570 (**cursor != 0x6E /* n */) &&
571 (**cursor != 0x6F /* o */) &&
572 (**cursor != 0x78 /* x */) &&
573 (**cursor != 0x58 /* X */) &&
574 (**cursor != 0x59 /* Y */)) {
575 break;
576 }
577 (*cursor)++;
578 }
579 toklen = (*cursor) - tokstart;
580
581 if (toklen == 5) {
582 /* scale, skewX, skewY */
583 if (strncmp("scale", tokstart, 5) == 0) {
584 transform = TRANSFORM_SCALE;
585 } else if (strncmp("skewX", tokstart, 5) == 0) {
586 transform = TRANSFORM_SKEWX;
587 } else if (strncmp("skewY", tokstart, 5) == 0) {
588 transform = TRANSFORM_SKEWY;
589 }
590 } else if (toklen == 6) {
591 /* matrix, rotate */
592 if (strncmp("matrix", tokstart, 6) == 0) {
593 transform = TRANSFORM_MATRIX;
594 } else if (strncmp("rotate", tokstart, 6) == 0) {
595 transform = TRANSFORM_ROTATE;
596 }
597 } else if (toklen == 9) {
598 /* translate */
599 if (strncmp("translate", tokstart, 9) == 0) {
600 transform = TRANSFORM_TRANSLATE;
601 }
602 }
603 if (transform == TRANSFORM_UNK) {
604 /* invalid transform */
605 return svgtiny_SVG_ERROR;
606 }
607
608 *transformout = transform;
609 return svgtiny_OK;
610}
611
612
613/**
614 * parse transform function parameters
615 *
616 * \param cursor current cursor
617 * \param textend end of buffer
618 * \param paramc max number of permitted parameters on input and number found on output
619 * \param paramv vector of float point numbers to put result in must have space for paramc entries
620 * \return svgtiny_OK and paramc and paramv updated or svgtiny_SVG_ERROR on error
621 */
622static inline svgtiny_code
623parse_transform_parameters(const char **cursor,
624 const char *textend,
625 int *paramc,
626 float *paramv)
627{
628 int param_idx = 0;
629 int param_max;
630 const char *tokend;
631 svgtiny_code err;
632
633 param_max = *paramc;
634
635 for(param_idx = 0; param_idx < param_max; param_idx++) {
636 tokend = textend;
637 err = svgtiny_parse_number(*cursor, &tokend, &paramv[param_idx]);
638 if (err != svgtiny_OK) {
639 /* failed to parse number */
640 return err;
641 }
642 *cursor = tokend;
643
644 /* advance cursor past optional whitespace */
645 advance_whitespace(cursor, textend);
646
647 if (*cursor >= textend) {
648 /* parameter list without close parenteses */
649 return svgtiny_SVG_ERROR;
650 }
651
652 /* close parentheses ends parameters */
653 if (**cursor == 0x29 /* ) */) {
654 (*cursor)++;
655 *paramc = param_idx + 1;
656 return svgtiny_OK;
657 }
658
659 /* comma can be skipped */
660 if (**cursor == 0x2C /* , */) {
661 (*cursor)++;
662 if ((*cursor) >= textend) {
663 /* parameter list without close parenteses */
664 return svgtiny_SVG_ERROR;
665 }
666 }
667
668 if ((*cursor) == tokend) {
669 /* no comma or whitespace between parameters */
670 return svgtiny_SVG_ERROR;
671 }
672 }
673 /* too many parameters for transform given */
674 return svgtiny_SVG_ERROR;
675}
676
677
678/**
679 * convert ascii hex digit to an integer
680 */
681static inline unsigned int hexd_to_int(const char *digit)
682{
683 unsigned int value = *digit;
684
685 if ((*digit >= 0x30 /* 0 */) && (*digit <= 0x39 /* 9 */)) {
686 value -= 0x30;
687 } else if ((*digit >= 0x41 /* A */) && (*digit <= 0x46 /* F */) ) {
688 value -= 0x37;
689 } else if (((*digit >= 0x61 /* a */) && (*digit <= 0x66 /* f */))) {
690 value -= 0x57;
691 }
692 return value;
693}
694
695
696/**
697 * convert two ascii hex digits to an integer
698 */
699static inline unsigned int hexdd_to_int(const char *digits)
700{
701 return (hexd_to_int(digits) << 4) | hexd_to_int(digits + 1);
702}
703
704
705/**
706 * parse a hex colour
707 * https://www.w3.org/TR/css-color-4/#typedef-hex-color
708 */
709static inline svgtiny_code
710parse_hex_color(const char **cursor, const char *textend, svgtiny_colour *c)
711{
712 unsigned int r, g, b, a=0xff;
713 const char *tokstart;
714
715 /* hex-color */
716 if ((**cursor != '#') || ((textend - (*cursor)) < 4)) {
717 return svgtiny_SVG_ERROR;
718 }
719
720 (*cursor)++;
721 tokstart = *cursor;
722
723 advance_hex(cursor, textend);
724
725 switch ((*cursor) - tokstart) {
726 case 3:
727 r = hexd_to_int(tokstart);
728 g = hexd_to_int(tokstart + 1);
729 b = hexd_to_int(tokstart + 2);
730 r |= r << 4;
731 g |= g << 4;
732 b |= b << 4;
733 break;
734 case 4:
735 r = hexd_to_int(tokstart);
736 g = hexd_to_int(tokstart + 1);
737 b = hexd_to_int(tokstart + 2);
738 a = hexd_to_int(tokstart + 3);
739 r |= r << 4;
740 g |= g << 4;
741 b |= b << 4;
742 break;
743 case 6:
744 r = hexdd_to_int(tokstart);
745 g = hexdd_to_int(tokstart + 2);
746 b = hexdd_to_int(tokstart + 4);
747 *c = svgtiny_RGB(r, g, b)((r) << 16 | (g) << 8 | (b));
748 break;
749 case 8:
750 r = hexdd_to_int(tokstart);
751 g = hexdd_to_int(tokstart + 2);
752 b = hexdd_to_int(tokstart + 4);
753 a = hexdd_to_int(tokstart + 6);
754 break;
755 default:
756 /* unparsable hex color */
757 *cursor = tokstart - 1; /* put cursor back */
758 return svgtiny_SVG_ERROR;
759 }
760
761 /** \todo do something with the alpha */
762 UNUSED(a)((void) (a));
763
764 *c = svgtiny_RGB(r, g, b)((r) << 16 | (g) << 8 | (b));
765 return svgtiny_OK;
766}
767
768
769/**
770 * parse a color function
771 *
772 * https://www.w3.org/TR/css-color-5/#typedef-color-function
773 *
774 * The only actual supported color function is rgb
775 */
776static inline svgtiny_code
777parse_color_function(const char **cursorout,
778 const char *textend,
779 svgtiny_colour *c)
780{
781 const char *cursor = *cursorout;
782 const char *argend = cursor;
783 svgtiny_code res;
784 float argf[4];
785 int idx; /* argument index */
786
787 if ((textend - cursor) < 10) {
788 /* must be at least ten characters to be a valid function */
789 return svgtiny_SVG_ERROR;
790 }
791
792 if (((cursor[0] != 'r') && (cursor[0] != 'R')) ||
793 ((cursor[1] != 'g') && (cursor[1] != 'G')) ||
794 ((cursor[2] != 'b') && (cursor[2] != 'B')) ||
795 (cursor[3] != '('))
796 {
797 /* only function currently supported is rgb */
798 return svgtiny_SVG_ERROR;
799 }
800 cursor += 4;
801
802 for (idx = 0; idx < 4; idx++) {
803 argend = textend;
804 res = svgtiny_parse_number(cursor, &argend, &argf[idx]);
805 if (res != svgtiny_OK) {
806 break;
807 }
808 cursor = argend;
809 if (cursor >= textend) {
810 /* no more input */
811 break;
812 }
813 if (*cursor == '%') {
814 /* percentage */
815 argf[idx] = argf[idx] * 255 / 100;
816 cursor++;
817 }
818 /* value must be clamped */
819 if (argf[idx] < 0) {
820 argf[idx] = 0;
821 }
822 if (argf[idx] > 255) {
823 argf[idx] = 255;
824 }
825 /* advance cursor to next argument */
826 advance_whitespace(&cursor, textend);
827 if (cursor >= textend) {
828 /* no more input */
829 break;
830 }
831 if (*cursor == ')') {
832 /* close parenthesis, arguments are complete */
833 cursor++;
834 break;
835 }
836 if (*cursor == ',') {
837 /* skip optional comma */
838 cursor++;
839 }
840 }
841 if (idx < 2 || idx > 3) {
842 return svgtiny_SVG_ERROR;
843 }
844
845 *cursorout = cursor;
846 *c = svgtiny_RGB((unsigned int)argf[0],(((unsigned int)argf[0]) << 16 | ((unsigned int)argf[1]
) << 8 | ((unsigned int)argf[2]))
847 (unsigned int)argf[1],(((unsigned int)argf[0]) << 16 | ((unsigned int)argf[1]
) << 8 | ((unsigned int)argf[2]))
848 (unsigned int)argf[2])(((unsigned int)argf[0]) << 16 | ((unsigned int)argf[1]
) << 8 | ((unsigned int)argf[2]))
;
849 return svgtiny_OK;
850}
851
852
853/**
854 * parse a paint url
855 *
856 * /todo this does not cope with any url that is not a fragment which
857 * identifies a gradient paint server.
858 */
859static inline svgtiny_code
860parse_paint_url(const char **cursorout,
861 const char *textend,
862 struct svgtiny_parse_state_gradient *grad,
863 struct svgtiny_parse_state *state,
864 svgtiny_colour *c)
865{
866 const char *cursor = *cursorout;
867 svgtiny_code res;
868 dom_element *ref; /* referenced element */
869
870 if (grad == NULL((void*)0)) {
871 return svgtiny_SVG_ERROR;
872 }
873
874 if ((textend - cursor) < 6) {
875 /* must be at least six characters to be a url */
876 return svgtiny_SVG_ERROR;
877 }
878
879 if (((cursor[0] != 'u') && (cursor[0] != 'U')) ||
880 ((cursor[1] != 'r') && (cursor[1] != 'R')) ||
881 ((cursor[2] != 'l') && (cursor[2] != 'L')) ||
882 (cursor[3] != '('))
883 {
884 /* only function currently supported is url */
885 return svgtiny_SVG_ERROR;
886 }
887 cursor += 4;
888
889 res = element_from_url(&cursor, textend - cursor, state, &ref);
890 if (res != svgtiny_OK){
891 return res;
892 }
893 if (ref == NULL((void*)0)) {
894 /* unable to find referenced element */
895 return svgtiny_SVG_ERROR;
896 }
897
898 if ((cursor >= textend) || (*cursor != ')')) {
899 /* no close bracket on url */
900 dom_node_unref(ref)dom_node_unref((dom_node *) (ref));
901 return svgtiny_SVG_ERROR;
902 }
903 cursor++;
904
905 /* find and update gradient */
906 res = svgtiny_update_gradient(ref, state, grad);
907 if (res == svgtiny_OK) {
908 *c = svgtiny_LINEAR_GRADIENT0x2000000;
909 }
910 dom_node_unref(ref)dom_node_unref((dom_node *) (ref));
911
912 return res;
913}
914
915
916/**
917 * parse a none token
918 *
919 * \return svgtiny_OK if none found else svgtiny_SVG_ERROR if not
920 */
921svgtiny_code svgtiny_parse_none(const char *cursor, const char *textend)
922{
923 const char *noneend;
924 if ((textend - cursor) < 4) {
925 /* too short to be none */
926 return svgtiny_SVG_ERROR;
927 }
928 if (cursor[0] != 'n' ||
929 cursor[1] != 'o' ||
930 cursor[2] != 'n' ||
931 cursor[3] != 'e') {
932 /* keyword doesnt match */
933 return svgtiny_SVG_ERROR;
934 }
935 cursor += 4;
936 noneend = cursor;
937
938 advance_whitespace(&cursor, textend);
939 if ((noneend != textend) && (noneend == cursor)) {
940 /* trailing stuff that is not whitespace */
941 return svgtiny_SVG_ERROR;
942 }
943 return svgtiny_OK;
944}
945
946/**
947 * Parse a paint.
948 *
949 * https://www.w3.org/TR/SVG11/painting.html#SpecifyingPaint
950 * https://www.w3.org/TR/SVG2/painting.html#SpecifyingPaint
951 *
952 */
953static svgtiny_code
954svgtiny_parse_paint(const char *text,
955 size_t textlen,
956 struct svgtiny_parse_state_gradient *grad,
957 struct svgtiny_parse_state *state,
958 svgtiny_colour *c)
959{
960 const char *cursor = text; /* cursor */
961 const char *textend = text + textlen;
962 svgtiny_code res;
963
964 advance_whitespace(&cursor, textend);
965
966 res = svgtiny_parse_none(cursor, textend);
967 if (res == svgtiny_OK) {
968 *c = svgtiny_TRANSPARENT0x1000000;
969 return res;
970 }
971
972 /* attempt to parse element as a paint url */
973 res = parse_paint_url(&cursor, textend, grad, state, c);
974 if (res == svgtiny_OK) {
975 return res;
976 }
977
978 return svgtiny_parse_color(cursor, textend - cursor, c);
979}
980
981
982/**
983 * parse an offset
984 */
985static svgtiny_code
986svgtiny_parse_offset(const char *text, size_t textlen, float *offset)
987{
988 svgtiny_code err;
989 float number;
990 const char *numend;
991
992 numend = text + textlen;
993 err = svgtiny_parse_number(text, &numend, &number);
994 if (err != svgtiny_OK) {
995 return err;
996 }
997 if ((numend < (text + textlen)) && (*numend == '%')) {
998 number /= 100.0;
999 }
1000 /* ensure value between 0 and 1 */
1001 if (number < 0) {
1002 number = 0;
1003 }
1004 if (number > 1.0) {
1005 number = 1.0;
1006 }
1007 *offset = number;
1008 return svgtiny_OK;
1009}
1010
1011
1012/**
1013 * dispatch parse operation
1014 */
1015static inline svgtiny_code
1016dispatch_op(const char *value,
1017 size_t value_len,
1018 struct svgtiny_parse_state *state,
1019 struct svgtiny_parse_internal_operation *styleop)
1020{
1021 float parse_len;
1022 svgtiny_code res = svgtiny_OK;
1023
1024 switch (styleop->operation) {
1025 case SVGTIOP_NONE:
1026 res = svgtiny_SVG_ERROR;
1027 break;
1028
1029 case SVGTIOP_PAINT:
1030 res = svgtiny_parse_paint(value,
1031 value_len,
1032 styleop->param,
1033 state,
1034 styleop->value);
1035 break;
1036
1037 case SVGTIOP_COLOR:
1038 res = svgtiny_parse_color(value, value_len, styleop->value);
1039 break;
1040
1041 case SVGTIOP_LENGTH:
1042 res = svgtiny_parse_length(value,
1043 value_len,
1044 *((int *)styleop->param),
1045 styleop->value);
1046 break;
1047
1048 case SVGTIOP_INTLENGTH:
1049 res = svgtiny_parse_length(value,
1050 value_len,
1051 *((int *)styleop->param),
1052 &parse_len);
1053 *((int *)styleop->value) = parse_len;
1054 break;
1055
1056 case SVGTIOP_OFFSET:
1057 res = svgtiny_parse_offset(value, value_len, styleop->value);
1058 break;
1059 }
1060 return res;
1061}
1062
1063/**
1064 * parse a declaration in a style
1065 *
1066 * https://www.w3.org/TR/CSS21/syndata.html#declaration
1067 *
1068 * \param declaration The declaration without any preceeding space
1069 * \param end The end of the declaration string
1070 * \param state parse state to pass on
1071 * \param styleops The table of style operations to apply
1072 *
1073 * declaration is "<property name> : <property value>"
1074 */
1075static inline svgtiny_code
1076parse_declaration(const char *declaration,
1077 const char *end,
1078 struct svgtiny_parse_state *state,
1079 struct svgtiny_parse_internal_operation *styleops)
1080{
1081 const char *cursor = declaration; /* text cursor */
1082 size_t key_len; /* key length */
1083 struct svgtiny_parse_internal_operation *styleop;
1084
1085 /* declaration must be at least 3 characters long (ie "a:b") */
1086 if ((end - declaration) < 3) {
1087 return svgtiny_SVG_ERROR;
1088 }
1089
1090 /* find end of key */
1091 advance_property_name(&cursor, end);
1092
1093 if ((cursor - declaration) < 1) {
1094 /* no key */
1095 return svgtiny_SVG_ERROR;
1096 }
1097
1098 key_len = cursor - declaration;
1099
1100 advance_whitespace(&cursor, end);
1101
1102 if ((cursor >= end) || (*cursor != ':')) {
1103 /* no colon */
1104 return svgtiny_SVG_ERROR;
1105 }
1106 cursor++; /* advance over colon */
1107
1108 advance_whitespace(&cursor, end);
1109
1110 /* search style operations for a match */
1111 for (styleop = styleops; styleop->key != NULL((void*)0); styleop++) {
1112 if ((dom_string_byte_length(styleop->key) == key_len) &&
1113 (memcmp(declaration, dom_string_data(styleop->key), key_len) == 0)) {
1114 /* found the operation, stop iterating */
1115 return dispatch_op(cursor, end - cursor, state, styleop);
1116 }
1117 }
1118
1119 return svgtiny_OK;
1120}
1121
1122
1123/**
1124 * parse text points into path points
1125 *
1126 * \param data Source text to parse
1127 * \param datalen Length of source text
1128 * \param pointv output vector of path elements.
1129 * \param pointc on input has number of path elements in pointv on exit has
1130 * the number of elements placed in the output vector.
1131 * \return svgtiny_OK on success else error code.
1132 *
1133 * parses a poly[line|gon] points text into a series of path elements.
1134 * The syntax is defined in https://www.w3.org/TR/SVG11/shapes.html#PointsBNF or
1135 * https://svgwg.org/svg2-draft/shapes.html#DataTypePoints
1136 *
1137 * This is a series of numbers separated by 0 (started by sign)
1138 * or more tabs (0x9), spaces (0x20), carrige returns (0xD) and newlines (0xA)
1139 * there may also be a comma in the separating whitespace after the preamble
1140 * A number is defined as https://www.w3.org/TR/css-syntax-3/#typedef-number-token
1141 *
1142 */
1143svgtiny_code
1144svgtiny_parse_poly_points(const char *text,
1145 size_t textlen,
1146 float *pointv,
1147 unsigned int *pointc)
1148{
1149 const char *textend = text + textlen;
1150 const char *numberend = NULL((void*)0);
1151 const char *cursor = text; /* text cursor */
1152 int even = 0; /* is the current point even */
1153 float point = 0; /* the odd point of the coordinate pair */
1154 float oddpoint = 0;
1155 svgtiny_code err;
1156
1157 *pointc = 0;
1158
1159 while (cursor < textend) {
1160 numberend=textend;
1161 err = svgtiny_parse_number(cursor, &numberend, &point);
1162 if (err != svgtiny_OK) {
1163 break;
1164 }
1165 cursor = numberend;
1166
1167 if (even) {
1168 even = 0;
1169 pointv[(*pointc)++] = svgtiny_PATH_LINE;
1170 pointv[(*pointc)++] = oddpoint;
1171 pointv[(*pointc)++] = point;
1172 } else {
1173 even = 1;
1174 oddpoint=point;
1175 }
1176
1177 /* advance cursor past whitespace (or comma) */
1178 advance_comma_whitespace(&cursor, textend);
1179 }
1180
1181 return svgtiny_OK;
1182}
1183
1184
1185/**
1186 * Parse a length as a number of pixels.
1187 */
1188svgtiny_code
1189svgtiny_parse_length(const char *text,
1190 size_t textlen,
1191 int viewport_size,
1192 float *length)
1193{
1194 svgtiny_code err;
1195 float number;
1196 const char *unit;
1197 int unitlen;
1198 float font_size = 20; /*css_len2px(&state.style.font_size.value.length, 0);*/
1199
1200 unit = text + textlen;
1201 err = svgtiny_parse_number(text, &unit, &number);
1202 if (err != svgtiny_OK) {
1203 unitlen = -1;
1204 } else {
1205 unitlen = (text + textlen) - unit;
1206 }
1207
1208 /* discount whitespace on the end of the unit */
1209 while(unitlen > 0) {
1210 if ((unit[unitlen - 1] != 0x20) &&
1211 (unit[unitlen - 1] != 0x09) &&
1212 (unit[unitlen - 1] != 0x0A) &&
1213 (unit[unitlen - 1] != 0x0D)) {
1214 break;
1215 }
1216 unitlen--;
1217 }
1218
1219 /* decode the unit */
1220 *length = 0;
1221 switch (unitlen) {
1222 case 0:
1223 /* no unit, assume pixels */
1224 *length = number;
1225 break;
1226 case 1:
1227 if (unit[0] == '%') {
1228 /* percentage of viewport */
1229 *length = number / 100.0 * viewport_size;
1230 }
1231 break;
1232
1233 case 2:
1234 if (unit[0] == 'e' && unit[1] == 'm') {
1235 *length = number * font_size;
1236 } else if (unit[0] == 'e' && unit[1] == 'x') {
1237 *length = number / 2.0 * font_size;
1238 } else if (unit[0] == 'p' && unit[1] == 'x') {
1239 *length = number;
1240 } else if (unit[0] == 'p' && unit[1] == 't') {
1241 *length = number * 1.25;
1242 } else if (unit[0] == 'p' && unit[1] == 'c') {
1243 *length = number * 15.0;
1244 } else if (unit[0] == 'm' && unit[1] == 'm') {
1245 *length = number * 3.543307;
1246 } else if (unit[0] == 'c' && unit[1] == 'm') {
1247 *length = number * 35.43307;
1248 } else if (unit[0] == 'i' && unit[1] == 'n') {
1249 *length = number * 90;
1250 }
1251 break;
1252
1253 default:
1254 /* unknown unit */
1255 break;
1256 }
1257
1258 return svgtiny_OK;
1259}
1260
1261
1262/**
1263 * Parse and apply a transform attribute.
1264 *
1265 * https://www.w3.org/TR/SVG11/coords.html#TransformAttribute
1266 *
1267 * parse transforms into transform matrix
1268 * | a c e |
1269 * | b d f |
1270 * | 0 0 1 |
1271 *
1272 * transforms to parse are:
1273 *
1274 * matrix(a b c d e f)
1275 * | a c e |
1276 * | b d f |
1277 * | 0 0 1 |
1278 *
1279 * translate(e f)
1280 * | 1 0 e |
1281 * | 0 1 f |
1282 * | 0 0 1 |
1283 *
1284 * translate(e)
1285 * | 1 0 e |
1286 * | 0 1 0 |
1287 * | 0 0 1 |
1288 *
1289 * scale(a d)
1290 * | a 0 0 |
1291 * | 0 d 0 |
1292 * | 0 0 1 |
1293 *
1294 * scale(a)
1295 * | a 0 0 |
1296 * | 0 1 0 |
1297 * | 0 0 1 |
1298 *
1299 * rotate(ang x y)
1300 * | cos(ang) -sin(ang) (-x * cos(ang) + y * sin(ang) + x) |
1301 * | sin(ang) cos(ang) (-x * sin(ang) - y * cos(ang) + y) |
1302 * | 0 0 1 |
1303 *
1304 * rotate(ang)
1305 * | cos(ang) -sin(ang) 0 |
1306 * | sin(ang) cos(ang) 0 |
1307 * | 0 0 1 |
1308 *
1309 * skewX(ang)
1310 * | 1 tan(ang) 0 |
1311 * | 0 1 0 |
1312 * | 0 0 1 |
1313 *
1314 * skewY(ang)
1315 * | 1 0 0 |
1316 * | tan(ang) 1 0 |
1317 * | 0 0 1 |
1318 *
1319 *
1320 */
1321svgtiny_code
1322svgtiny_parse_transform(const char *text,
1323 size_t textlen,
1324 struct svgtiny_transformation_matrix *tm)
1325{
1326 const char *cursor = text; /* text cursor */
1327 const char *textend = text + textlen;
1328 enum transform_type transform = TRANSFORM_UNK;
1329 /* mapping of maimum number of parameters for each transform */
1330 const int param_max[]={0,6,2,2,3,1,1};
1331 const char *paramend;
1332 int paramc;
1333 float paramv[6];
1334 svgtiny_code err;
1335
1336 /* advance cursor past optional whitespace */
1337 advance_whitespace(&cursor, textend);
1338
1339 /* zero or more transform followed by whitespace or comma */
1340 while (cursor < textend) {
1341 err = parse_transform_function(&cursor, textend, &transform);
1342 if (err != svgtiny_OK) {
1343 /* invalid transform */
1344 goto transform_parse_complete;
1345 }
1346
1347 /* advance cursor past optional whitespace */
1348 advance_whitespace(&cursor, textend);
1349
1350 /* open parentheses */
1351 if (*cursor != 0x28 /* ( */) {
1352 /* invalid syntax */
1353 goto transform_parse_complete;
1354 }
1355 cursor++;
1356
1357 paramc=param_max[transform];
1358 err = parse_transform_parameters(&cursor, textend, &paramc, paramv);
1359 if (err != svgtiny_OK) {
1360 /* invalid parameters */
1361 goto transform_parse_complete;
1362 }
1363 paramend = cursor;
1364
1365 /* have transform type and at least one parameter */
1366
1367 /* apply transform */
1368 err = apply_transform(transform, paramc, paramv, tm);
1369 if (err != svgtiny_OK) {
1370 /* transform failed */
1371 goto transform_parse_complete;
1372 }
1373
1374 /* advance cursor past whitespace (or comma) */
1375 advance_comma_whitespace(&cursor, textend);
1376 if (cursor == paramend) {
1377 /* no comma or whitespace between transforms */
1378 goto transform_parse_complete;
1379 }
1380 }
1381
1382transform_parse_complete:
1383 return svgtiny_OK;
1384}
1385
1386
1387/**
1388 * Parse a color.
1389 *
1390 * https://www.w3.org/TR/SVG11/types.html#DataTypeColor
1391 * https://www.w3.org/TR/css-color-5/#typedef-color
1392 *
1393 * <color> = <color-base> | currentColor | <system-color>
1394 * <color-base> = <hex-color> | <color-function> | <named-color> | transparent
1395 * <color-function> = <rgb()> | <rgba()> | <hsl()> | <hsla()> | <hwb()> |
1396 * <lab()> | <lch()> | <oklab()> | <oklch()> | <color()>
1397 *
1398 * \todo this does not cope with currentColor or transparent and supports only
1399 * the rgb color function.
1400 */
1401svgtiny_code
1402svgtiny_parse_color(const char *text, size_t textlen, svgtiny_colour *c)
1403{
1404 const struct svgtiny_named_color *named_color;
1405 const char *cursor = text; /* cursor */
1406 const char *textend = text + textlen;
1407 svgtiny_code res;
1408
1409 advance_whitespace(&cursor, textend);
1410
1411 /* attempt to parse element as a hex color */
1412 res = parse_hex_color(&cursor, textend, c);
1413 if (res == svgtiny_OK) {
1414 return res;
1415 }
1416
1417 /* attempt to parse element as a color function */
1418 res = parse_color_function(&cursor, textend, c);
1419 if (res == svgtiny_OK) {
1420 return res;
1421 }
1422
1423 named_color = svgtiny_color_lookup(cursor, textend - cursor);
1424 if (named_color) {
1425 *c = named_color->color;
1426 return svgtiny_OK;
1427 }
1428
1429 /* did not parse as a color */
1430 *c = svgtiny_RGB(0, 0, 0)((0) << 16 | (0) << 8 | (0));
1431 return svgtiny_SVG_ERROR;
1432}
1433
1434/**
1435 * parse a viewbox attribute
1436 *
1437 * https://www.w3.org/TR/SVG11/coords.html#ViewBoxAttribute
1438 * https://www.w3.org/TR/SVG2/coords.html#ViewBoxAttribute
1439 *
1440 * <min-x>,? <min-y>,? <width>,? <height>
1441 */
1442svgtiny_code
1443svgtiny_parse_viewbox(const char *text,
1444 size_t textlen,
1445 float viewport_width,
1446 float viewport_height,
1447 struct svgtiny_transformation_matrix *tm)
1448{
1449 const char *cursor = text; /* text cursor */
1450 const char *textend = text + textlen;
1451 const char *paramend;
1452 float paramv[4];
1453 int paramidx = 0;
1454 svgtiny_code res;
1455
1456 /* advance cursor past optional whitespace */
1457 advance_whitespace(&cursor, textend);
1458
1459 for (paramidx = 0; paramidx < 3; paramidx++) {
1460 paramend = textend;
1461 res = svgtiny_parse_number(cursor, &paramend, &paramv[paramidx]);
1462 if (res != svgtiny_OK) {
1463 /* failed to parse number */
1464 return res;
1465 }
1466 cursor = paramend;
1467 advance_comma_whitespace(&cursor, textend);
1468 }
1469 paramend = textend;
1470 res = svgtiny_parse_number(cursor, &paramend, &paramv[paramidx]);
1471 if (res != svgtiny_OK) {
1472 /* failed to parse number */
1473 return res;
1474 }
1475 cursor = paramend;
1476 advance_whitespace(&cursor, textend);
1477
1478 if (cursor != textend) {
1479 /* syntax error */
1480 return svgtiny_SVG_ERROR;
1481 }
1482
1483 tm->a = (float)viewport_width / paramv[2];
1484 tm->d = (float)viewport_height / paramv[3];
1485 tm->e += -paramv[0] * tm->a;
1486 tm->f += -paramv[1] * tm->d;
1487
1488 return svgtiny_OK;
1489}
1490
1491
1492/**
1493 * parse an inline style
1494 */
1495svgtiny_code
1496svgtiny_parse_inline_style(dom_element *node,
1497 struct svgtiny_parse_state *state,
1498 struct svgtiny_parse_internal_operation *ops)
1499{
1500 const char *cursor; /* text cursor */
1501 const char *textend;
1502 const char *declaration_start;
1503 dom_string *attr;
1504 dom_exception exc;
1505
1506 /* style attribute */
1507 exc = dom_element_get_attribute(node, state->interned_style, &attr)dom_element_get_attribute( (dom_element *) (node), (state->
interned_style), (&attr))
;
1508 if (exc != DOM_NO_ERR) {
1509 return svgtiny_LIBDOM_ERROR;
1510 }
1511 if (attr == NULL((void*)0)) {
1512 /* no style attribute */
1513 return svgtiny_OK;
1514 }
1515 cursor = dom_string_data(attr);
1516 textend = cursor + dom_string_byte_length(attr);
1517
1518 while (cursor < textend) {
1519 advance_whitespace(&cursor, textend);
1520 declaration_start = cursor;
1521 while (cursor < textend) {
1522 if ((*cursor == ';') &&
1523 (*(cursor - 1) != '\\')) {
1524 break;
1525 }
1526 cursor++;
1527 }
1528 parse_declaration(declaration_start, cursor, state, ops);
1529 cursor++; /* skip semicolon */
1530 }
1531 dom_string_unref(attr);
1532 return svgtiny_OK;
1533}
1534
1535
1536/**
1537 * parse attributes controled by operation table
1538 */
1539svgtiny_code
1540svgtiny_parse_attributes(dom_element *node,
1541 struct svgtiny_parse_state *state,
1542 struct svgtiny_parse_internal_operation *styleops)
1543{
1544 struct svgtiny_parse_internal_operation *styleop;
1545 dom_string *attr;
1546 dom_exception exc;
1547
1548 for (styleop = styleops; styleop->key != NULL((void*)0); styleop++) {
1549 exc = dom_element_get_attribute(node, styleop->key, &attr)dom_element_get_attribute( (dom_element *) (node), (styleop->
key), (&attr))
;
1550 if (exc != DOM_NO_ERR) {
1551 return svgtiny_LIBDOM_ERROR;
1552 }
1553 if (attr != NULL((void*)0)) {
1554 dispatch_op(dom_string_data(attr),
1555 dom_string_byte_length(attr),
1556 state,
1557 styleop);
1558 dom_string_unref(attr);
1559 }
1560 }
1561 return svgtiny_OK;
1562}
1563
1564
1565/**
1566 * parses href attribute contents as a url fragment and finds matching element
1567 *
1568 * \param node The node on which to examine teh href attribute for a url
1569 * \param state The parse state
1570 * \param element result element pointer or NULL if no matching element
1571 * \return svgtiny_OK and element updated else error code
1572 */
1573svgtiny_code
1574svgtiny_parse_element_from_href(dom_element *node,
1575 struct svgtiny_parse_state *state,
1576 dom_element **element)
1577{
1578 dom_exception exc;
1579 dom_string *attr;
1580 svgtiny_code res;
1581 const char *url;
1582
1583 /* attempt to get href in default namespace */
1584 exc = dom_element_get_attribute(node, state->interned_href, &attr)dom_element_get_attribute( (dom_element *) (node), (state->
interned_href), (&attr))
;
1585 if (exc != DOM_NO_ERR) {
1586 return svgtiny_LIBDOM_ERROR;
1587 }
1588
1589 if (attr == NULL((void*)0)) {
1590 dom_string *xmlns_xlink;
1591 exc = dom_string_create_interned((const uint8_t *)XLINK_NS"http://www.w3.org/1999/xlink",
1592 strlen(XLINK_NS"http://www.w3.org/1999/xlink"),
1593 &xmlns_xlink);
1594 if (exc != DOM_NO_ERR || xmlns_xlink == NULL((void*)0)) {
1595 return svgtiny_LIBDOM_ERROR;
1596 }
1597
1598 /* attempt to get href attribute in xlink namespace */
1599 exc = dom_element_get_attribute_ns(node,dom_element_get_attribute_ns((dom_element *) (node), (xmlns_xlink
), (state->interned_href), (&attr))
1600 xmlns_xlink,dom_element_get_attribute_ns((dom_element *) (node), (xmlns_xlink
), (state->interned_href), (&attr))
1601 state->interned_href,dom_element_get_attribute_ns((dom_element *) (node), (xmlns_xlink
), (state->interned_href), (&attr))
1602 &attr)dom_element_get_attribute_ns((dom_element *) (node), (xmlns_xlink
), (state->interned_href), (&attr))
;
1603 dom_string_unref(xmlns_xlink);
1604 if (exc != DOM_NO_ERR) {
1605 return svgtiny_LIBDOM_ERROR;
1606 }
1607 if (attr == NULL((void*)0)) {
1608 /* no href attribute */
1609 *element = NULL((void*)0);
1610 return svgtiny_OK;
1611 }
1612 }
1613
1614 url = dom_string_data(attr);
1615 res = element_from_url(&url,
1616 dom_string_byte_length(attr),
1617 state,
1618 element);
1619
1620 dom_string_unref(attr);
1621 return res;
1622}