/**
* parse text string into a float
*
+ * \param[in] text string to parse
+ * \param[in,out] textend On input is the end of the string in \a text. Output
+ * is set to indicate the character after those used to
+ * parse the number.
+ * \param[out] value The resulting value from the parse
+ * \return svgtiny_OK and the value updated else svgtiny_SVG_ERROR and value
+ * left unchanged. The textend is always updated to indicate the last
+ * character parsed.
+ *
* A number is started by 0 (started by sign) or more spaces (0x20), tabs (0x09),
* carridge returns (0xD) and newlines (0xA) followed by a decimal number.
* A number is defined as https://www.w3.org/TR/css-syntax-3/#typedef-number-token
* the input correctly.
*/
static svgtiny_code
-svgtiny_parse_number(const char *text,
- size_t textlen,
- const char **textend,
- float *value)
+parse_number(const char *text, const char **textend, float *value)
{
- enum b10sign {
- SPOSITIVE,
- SNEGATIVE,
- };
- const char *dataend;
const char *cur; /* text cursor */
enum {
STATE_WHITESPACE, /* processing whitespace */
STATE_SIGNEXPONENT, /* processing exponent part */
STATE_EXPONENT, /* processing exponent part have seen sign */
} state = STATE_WHITESPACE;
+ enum b10sign {
+ SPOSITIVE,
+ SNEGATIVE,
+ };
enum b10sign sign = SPOSITIVE; /* sign of number being constructed */
unsigned int significand = 0; /* significand of number being constructed */
int exponent = 0; /* exponent of the significand (distinct from exponent part) */
unsigned int exp_value = 0; /* value of the exponent part */
unsigned int digit_count = 0; /* has an actual digit been seen */
- dataend = text + textlen;
- for (cur=text; cur < dataend ; cur++) {
+ for (cur = text; cur < (*textend); cur++) {
switch (state) {
case STATE_WHITESPACE:
switch (*cur) {
return svgtiny_SVG_ERROR;
}
- if (digit_count==0) {
+ if (digit_count == 0) {
/* number had no digits (only +-.) which is a syntax error */
return svgtiny_SVG_ERROR;
}
}
-/**
- * parse text points into path points
- *
- * \param data Source text to parse
- * \param datalen Length of source text
- * \param pointv output vector of path elements.
- * \param pointc on input has number of path elements in pointv on exit has
- * the number of elements placed in the output vector.
- * \return svgtiny_OK on success else error code.
- *
- * parses a poly[line|gon] points text into a series of path elements.
- * The syntax is defined in https://www.w3.org/TR/SVG11/shapes.html#PointsBNF or
- * https://svgwg.org/svg2-draft/shapes.html#DataTypePoints
- *
- * This is a series of numbers separated by 0 (started by sign)
- * or more tabs (0x9), spaces (0x20), carrige returns (0xD) and newlines (0xA)
- * there may also be a comma in the separating whitespace after the preamble
- * A number is defined as https://www.w3.org/TR/css-syntax-3/#typedef-number-token
- *
- */
-svgtiny_code
-svgtiny_parse_poly_points(const char *text,
- size_t textlen,
- float *pointv,
- unsigned int *pointc)
-{
- const char *textend = text + textlen;
- const char *numberend = NULL;
- const char *cursor = text; /* text cursor */
- int even = 0; /* is the current point even */
- float point = 0; /* the odd point of the coordinate pair */
- float oddpoint = 0;
- svgtiny_code err;
-
- *pointc = 0;
-
- while (cursor < textend) {
- err = svgtiny_parse_number(cursor,
- textend - cursor,
- &numberend,
- &point);
- if (err != svgtiny_OK) {
- break;
- }
- cursor = numberend;
-
- if (even) {
- even = 0;
- pointv[(*pointc)++] = svgtiny_PATH_LINE;
- pointv[(*pointc)++] = oddpoint;
- pointv[(*pointc)++] = point;
- } else {
- even = 1;
- oddpoint=point;
- }
-
- /* advance cursor past whitespace (or comma) */
- advance_comma_whitespace(&cursor, textend);
- }
-
- return svgtiny_OK;
-}
-
-
-/**
- * Parse a length as a number of pixels.
- */
-svgtiny_code
-svgtiny_parse_length(const char *text,
- size_t textlen,
- int viewport_size,
- float *length)
-{
- svgtiny_code err;
- float number;
- const char *unit = NULL;
- int unitlen;
- float font_size = 20; /*css_len2px(&state.style.font_size.value.length, 0);*/
-
- err = svgtiny_parse_number(text, textlen, &unit, &number);
- if (err != svgtiny_OK) {
- unitlen = -1;
- } else {
- unitlen = (text + textlen) - unit;
- }
-
- /* discount whitespace on the end of the unit */
- while(unitlen > 0) {
- if ((unit[unitlen - 1] != 0x20) &&
- (unit[unitlen - 1] != 0x09) &&
- (unit[unitlen - 1] != 0x0A) &&
- (unit[unitlen - 1] != 0x0D)) {
- break;
- }
- unitlen--;
- }
-
- /* decode the unit */
- *length = 0;
- switch (unitlen) {
- case 0:
- /* no unit, assume pixels */
- *length = number;
- break;
- case 1:
- if (unit[0] == '%') {
- /* percentage of viewport */
- *length = number / 100.0 * viewport_size;
- }
- break;
-
- case 2:
- if (unit[0] == 'e' && unit[1] == 'm') {
- *length = number * font_size;
- } else if (unit[0] == 'e' && unit[1] == 'x') {
- *length = number / 2.0 * font_size;
- } else if (unit[0] == 'p' && unit[1] == 'x') {
- *length = number;
- } else if (unit[0] == 'p' && unit[1] == 't') {
- *length = number * 1.25;
- } else if (unit[0] == 'p' && unit[1] == 'c') {
- *length = number * 15.0;
- } else if (unit[0] == 'm' && unit[1] == 'm') {
- *length = number * 3.543307;
- } else if (unit[0] == 'c' && unit[1] == 'm') {
- *length = number * 35.43307;
- } else if (unit[0] == 'i' && unit[1] == 'n') {
- *length = number * 90;
- }
- break;
-
- default:
- /* unknown unit */
- break;
- }
-
- return svgtiny_OK;
-}
-
-
enum transform_type {
TRANSFORM_UNK,
TRANSFORM_MATRIX,
TRANSFORM_SKEWY,
};
+
static inline svgtiny_code
apply_transform(enum transform_type transform,
int paramc,
param_max = *paramc;
for(param_idx = 0; param_idx < param_max; param_idx++) {
- err = svgtiny_parse_number(*cursor,
- textend - (*cursor),
- &tokend,
- ¶mv[param_idx]);
+ tokend = textend;
+ err = parse_number(*cursor, &tokend, ¶mv[param_idx]);
if (err != svgtiny_OK) {
/* failed to parse number */
return err;
/**
- * Parse and apply a transform attribute.
- *
- * https://www.w3.org/TR/SVG11/coords.html#TransformAttribute
- *
- * parse transforms into transform matrix
- * | a c e |
- * | b d f |
- * | 0 0 1 |
- *
- * transforms to parse are:
- *
- * matrix(a b c d e f)
- * | a c e |
- * | b d f |
- * | 0 0 1 |
- *
- * translate(e f)
- * | 1 0 e |
- * | 0 1 f |
- * | 0 0 1 |
- *
- * translate(e)
- * | 1 0 e |
- * | 0 1 0 |
- * | 0 0 1 |
- *
- * scale(a d)
- * | a 0 0 |
- * | 0 d 0 |
- * | 0 0 1 |
- *
- * scale(a)
- * | a 0 0 |
- * | 0 1 0 |
- * | 0 0 1 |
- *
- * rotate(ang x y)
- * | cos(ang) -sin(ang) (-x * cos(ang) + y * sin(ang) + x) |
- * | sin(ang) cos(ang) (-x * sin(ang) - y * cos(ang) + y) |
- * | 0 0 1 |
- *
- * rotate(ang)
- * | cos(ang) -sin(ang) 0 |
- * | sin(ang) cos(ang) 0 |
- * | 0 0 1 |
- *
- * skewX(ang)
- * | 1 tan(ang) 0 |
- * | 0 1 0 |
- * | 0 0 1 |
- *
- * skewY(ang)
- * | 1 0 0 |
- * | tan(ang) 1 0 |
- * | 0 0 1 |
- *
- *
+ * convert ascii hex digit to an integer
*/
-svgtiny_code
-svgtiny_parse_transform(const char *text,
- size_t textlen,
- struct svgtiny_transformation_matrix *tm)
+static inline unsigned int hexd_to_int(const char *digit)
{
- const char *cursor = text; /* text cursor */
- const char *textend = text + textlen;
- enum transform_type transform = TRANSFORM_UNK;
- /* mapping of maimum number of parameters for each transform */
- const int param_max[]={0,6,2,2,3,1,1};
- const char *paramend;
- int paramc;
- float paramv[6];
- svgtiny_code err;
+ unsigned int value = *digit;
- /* advance cursor past optional whitespace */
- advance_whitespace(&cursor, textend);
+ if ((*digit >= 0x30 /* 0 */) && (*digit <= 0x39 /* 9 */)) {
+ value -= 0x30;
+ } else if ((*digit >= 0x41 /* A */) && (*digit <= 0x5A /* Z */) ) {
+ value -= 0x37;
+ } else if (((*digit >= 0x61 /* a */) && (*digit <= 0x7A /* z */))) {
+ value -= 0x57;
+ }
+ return value;
+}
- /* zero or more transform followed by whitespace or comma */
- while (cursor < textend) {
- err = parse_transform_function(&cursor, textend, &transform);
- if (err != svgtiny_OK) {
- /* invalid transform */
- goto transform_parse_complete;
- }
- /* advance cursor past optional whitespace */
- advance_whitespace(&cursor, textend);
-
- /* open parentheses */
- if (*cursor != 0x28 /* ( */) {
- /* invalid syntax */
- goto transform_parse_complete;
- }
- cursor++;
-
- paramc=param_max[transform];
- err = parse_transform_parameters(&cursor, textend, ¶mc, paramv);
- if (err != svgtiny_OK) {
- /* invalid parameters */
- goto transform_parse_complete;
- }
- paramend = cursor;
-
- /* have transform type and at least one parameter */
-
- /* apply transform */
- err = apply_transform(transform, paramc, paramv, tm);
- if (err != svgtiny_OK) {
- /* transform failed */
- goto transform_parse_complete;
- }
-
- /* advance cursor past whitespace (or comma) */
- advance_comma_whitespace(&cursor, textend);
- if (cursor == paramend) {
- /* no comma or whitespace between transforms */
- goto transform_parse_complete;
- }
- }
-
-transform_parse_complete:
- return svgtiny_OK;
-}
-
-
-/**
- * convert ascii hex digit to an integer
- */
-static inline unsigned int hexd_to_int(const char *digit)
-{
- unsigned int value = *digit;
-
- if ((*digit >= 0x30 /* 0 */) && (*digit <= 0x39 /* 9 */)) {
- value -= 0x30;
- } else if ((*digit >= 0x41 /* A */) && (*digit <= 0x5A /* Z */) ) {
- value -= 0x37;
- } else if (((*digit >= 0x61 /* a */) && (*digit <= 0x7A /* z */))) {
- value -= 0x57;
- }
- return value;
-}
-
-
-/**
- * convert two ascii hex digits to an integer
- */
-static inline unsigned int hexdd_to_int(const char *digits)
-{
- return (hexd_to_int(digits) << 4) | hexd_to_int(digits + 1);
-}
+/**
+ * convert two ascii hex digits to an integer
+ */
+static inline unsigned int hexdd_to_int(const char *digits)
+{
+ return (hexd_to_int(digits) << 4) | hexd_to_int(digits + 1);
+}
/**
/* only function currently supported is rgb */
return svgtiny_SVG_ERROR;
}
- cursor+=4;
+ cursor += 4;
for (idx = 0; idx < 4; idx++) {
- res = svgtiny_parse_number(cursor, textend - cursor, &argend, &argf[idx]);
+ argend = textend;
+ res = parse_number(cursor, &argend, &argf[idx]);
if (res != svgtiny_OK) {
break;
}
}
+/**
+ * parse text points into path points
+ *
+ * \param data Source text to parse
+ * \param datalen Length of source text
+ * \param pointv output vector of path elements.
+ * \param pointc on input has number of path elements in pointv on exit has
+ * the number of elements placed in the output vector.
+ * \return svgtiny_OK on success else error code.
+ *
+ * parses a poly[line|gon] points text into a series of path elements.
+ * The syntax is defined in https://www.w3.org/TR/SVG11/shapes.html#PointsBNF or
+ * https://svgwg.org/svg2-draft/shapes.html#DataTypePoints
+ *
+ * This is a series of numbers separated by 0 (started by sign)
+ * or more tabs (0x9), spaces (0x20), carrige returns (0xD) and newlines (0xA)
+ * there may also be a comma in the separating whitespace after the preamble
+ * A number is defined as https://www.w3.org/TR/css-syntax-3/#typedef-number-token
+ *
+ */
+svgtiny_code
+svgtiny_parse_poly_points(const char *text,
+ size_t textlen,
+ float *pointv,
+ unsigned int *pointc)
+{
+ const char *textend = text + textlen;
+ const char *numberend = NULL;
+ const char *cursor = text; /* text cursor */
+ int even = 0; /* is the current point even */
+ float point = 0; /* the odd point of the coordinate pair */
+ float oddpoint = 0;
+ svgtiny_code err;
+
+ *pointc = 0;
+
+ while (cursor < textend) {
+ numberend=textend;
+ err = parse_number(cursor, &numberend, &point);
+ if (err != svgtiny_OK) {
+ break;
+ }
+ cursor = numberend;
+
+ if (even) {
+ even = 0;
+ pointv[(*pointc)++] = svgtiny_PATH_LINE;
+ pointv[(*pointc)++] = oddpoint;
+ pointv[(*pointc)++] = point;
+ } else {
+ even = 1;
+ oddpoint=point;
+ }
+
+ /* advance cursor past whitespace (or comma) */
+ advance_comma_whitespace(&cursor, textend);
+ }
+
+ return svgtiny_OK;
+}
+
+
+/**
+ * Parse a length as a number of pixels.
+ */
+svgtiny_code
+svgtiny_parse_length(const char *text,
+ size_t textlen,
+ int viewport_size,
+ float *length)
+{
+ svgtiny_code err;
+ float number;
+ const char *unit;
+ int unitlen;
+ float font_size = 20; /*css_len2px(&state.style.font_size.value.length, 0);*/
+
+ unit = text + textlen;
+ err = parse_number(text, &unit, &number);
+ if (err != svgtiny_OK) {
+ unitlen = -1;
+ } else {
+ unitlen = (text + textlen) - unit;
+ }
+
+ /* discount whitespace on the end of the unit */
+ while(unitlen > 0) {
+ if ((unit[unitlen - 1] != 0x20) &&
+ (unit[unitlen - 1] != 0x09) &&
+ (unit[unitlen - 1] != 0x0A) &&
+ (unit[unitlen - 1] != 0x0D)) {
+ break;
+ }
+ unitlen--;
+ }
+
+ /* decode the unit */
+ *length = 0;
+ switch (unitlen) {
+ case 0:
+ /* no unit, assume pixels */
+ *length = number;
+ break;
+ case 1:
+ if (unit[0] == '%') {
+ /* percentage of viewport */
+ *length = number / 100.0 * viewport_size;
+ }
+ break;
+
+ case 2:
+ if (unit[0] == 'e' && unit[1] == 'm') {
+ *length = number * font_size;
+ } else if (unit[0] == 'e' && unit[1] == 'x') {
+ *length = number / 2.0 * font_size;
+ } else if (unit[0] == 'p' && unit[1] == 'x') {
+ *length = number;
+ } else if (unit[0] == 'p' && unit[1] == 't') {
+ *length = number * 1.25;
+ } else if (unit[0] == 'p' && unit[1] == 'c') {
+ *length = number * 15.0;
+ } else if (unit[0] == 'm' && unit[1] == 'm') {
+ *length = number * 3.543307;
+ } else if (unit[0] == 'c' && unit[1] == 'm') {
+ *length = number * 35.43307;
+ } else if (unit[0] == 'i' && unit[1] == 'n') {
+ *length = number * 90;
+ }
+ break;
+
+ default:
+ /* unknown unit */
+ break;
+ }
+
+ return svgtiny_OK;
+}
+
+
+/**
+ * Parse and apply a transform attribute.
+ *
+ * https://www.w3.org/TR/SVG11/coords.html#TransformAttribute
+ *
+ * parse transforms into transform matrix
+ * | a c e |
+ * | b d f |
+ * | 0 0 1 |
+ *
+ * transforms to parse are:
+ *
+ * matrix(a b c d e f)
+ * | a c e |
+ * | b d f |
+ * | 0 0 1 |
+ *
+ * translate(e f)
+ * | 1 0 e |
+ * | 0 1 f |
+ * | 0 0 1 |
+ *
+ * translate(e)
+ * | 1 0 e |
+ * | 0 1 0 |
+ * | 0 0 1 |
+ *
+ * scale(a d)
+ * | a 0 0 |
+ * | 0 d 0 |
+ * | 0 0 1 |
+ *
+ * scale(a)
+ * | a 0 0 |
+ * | 0 1 0 |
+ * | 0 0 1 |
+ *
+ * rotate(ang x y)
+ * | cos(ang) -sin(ang) (-x * cos(ang) + y * sin(ang) + x) |
+ * | sin(ang) cos(ang) (-x * sin(ang) - y * cos(ang) + y) |
+ * | 0 0 1 |
+ *
+ * rotate(ang)
+ * | cos(ang) -sin(ang) 0 |
+ * | sin(ang) cos(ang) 0 |
+ * | 0 0 1 |
+ *
+ * skewX(ang)
+ * | 1 tan(ang) 0 |
+ * | 0 1 0 |
+ * | 0 0 1 |
+ *
+ * skewY(ang)
+ * | 1 0 0 |
+ * | tan(ang) 1 0 |
+ * | 0 0 1 |
+ *
+ *
+ */
+svgtiny_code
+svgtiny_parse_transform(const char *text,
+ size_t textlen,
+ struct svgtiny_transformation_matrix *tm)
+{
+ const char *cursor = text; /* text cursor */
+ const char *textend = text + textlen;
+ enum transform_type transform = TRANSFORM_UNK;
+ /* mapping of maimum number of parameters for each transform */
+ const int param_max[]={0,6,2,2,3,1,1};
+ const char *paramend;
+ int paramc;
+ float paramv[6];
+ svgtiny_code err;
+
+ /* advance cursor past optional whitespace */
+ advance_whitespace(&cursor, textend);
+
+ /* zero or more transform followed by whitespace or comma */
+ while (cursor < textend) {
+ err = parse_transform_function(&cursor, textend, &transform);
+ if (err != svgtiny_OK) {
+ /* invalid transform */
+ goto transform_parse_complete;
+ }
+
+ /* advance cursor past optional whitespace */
+ advance_whitespace(&cursor, textend);
+
+ /* open parentheses */
+ if (*cursor != 0x28 /* ( */) {
+ /* invalid syntax */
+ goto transform_parse_complete;
+ }
+ cursor++;
+
+ paramc=param_max[transform];
+ err = parse_transform_parameters(&cursor, textend, ¶mc, paramv);
+ if (err != svgtiny_OK) {
+ /* invalid parameters */
+ goto transform_parse_complete;
+ }
+ paramend = cursor;
+
+ /* have transform type and at least one parameter */
+
+ /* apply transform */
+ err = apply_transform(transform, paramc, paramv, tm);
+ if (err != svgtiny_OK) {
+ /* transform failed */
+ goto transform_parse_complete;
+ }
+
+ /* advance cursor past whitespace (or comma) */
+ advance_comma_whitespace(&cursor, textend);
+ if (cursor == paramend) {
+ /* no comma or whitespace between transforms */
+ goto transform_parse_complete;
+ }
+ }
+
+transform_parse_complete:
+ return svgtiny_OK;
+}
+
+
/**
* Parse a paint.
*