summaryrefslogtreecommitdiff
path: root/source/svg/svg-parse.c
diff options
context:
space:
mode:
Diffstat (limited to 'source/svg/svg-parse.c')
-rw-r--r--source/svg/svg-parse.c276
1 files changed, 276 insertions, 0 deletions
diff --git a/source/svg/svg-parse.c b/source/svg/svg-parse.c
new file mode 100644
index 00000000..b8db376f
--- /dev/null
+++ b/source/svg/svg-parse.c
@@ -0,0 +1,276 @@
+#include "mupdf/svg.h"
+
+int svg_is_whitespace_or_comma(int c)
+{
+ return (c == 0x20) || (c == 0x9) || (c == 0xD) || (c == 0xA) || (c == ',');
+}
+
+int svg_is_whitespace(int c)
+{
+ return (c == 0x20) || (c == 0x9) || (c == 0xD) || (c == 0xA);
+}
+
+int svg_is_alpha(int c)
+{
+ return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z');
+}
+
+int svg_is_digit(int c)
+{
+ return (c >= '0' && c <= '9') ||
+ (c == 'e') || (c == 'E') ||
+ (c == '+') || (c == '-') || (c == '.');
+}
+
+const char *
+svg_lex_number(float *fp, const char *ss)
+{
+ const char *s = ss;
+ if (*s == '-')
+ ++s;
+ while (*s >= '0' && *s <= '9')
+ ++s;
+ if (*s == '.') {
+ ++s;
+ while (*s >= '0' && *s <= '9')
+ ++s;
+ }
+ if (*s == 'e' || *s == 'E') {
+ ++s;
+ if (*s == '+' || *s == '-')
+ ++s;
+ while (*s >= '0' && *s <= '9')
+ ++s;
+ }
+ *fp = atof(ss);
+ return s;
+}
+
+float
+svg_parse_number(const char *str, float min, float max, float inherit)
+{
+ float x;
+ if (!strcmp(str, "inherit"))
+ return inherit;
+ x = atof(str);
+ if (x < min) return min;
+ if (x > max) return max;
+ return x;
+}
+
+/* Return length/coordinate in points */
+float
+svg_parse_length(const char *str, float percent, float font_size)
+{
+ char *end;
+ float val;
+
+ val = (float)strtod(str, &end);
+ if (end == str)
+ return 0; /* failed */
+
+ if (!strcmp(end, "px")) return val;
+
+ if (!strcmp(end, "pt")) return val * 1.0;
+ if (!strcmp(end, "pc")) return val * 12.0;
+ if (!strcmp(end, "mm")) return val * 2.83464567;
+ if (!strcmp(end, "cm")) return val * 28.3464567;
+ if (!strcmp(end, "in")) return val * 72.0;
+
+ if (!strcmp(end, "em")) return val * font_size;
+ if (!strcmp(end, "ex")) return val * font_size * 0.5;
+
+ if (!strcmp(end, "%"))
+ return val * percent * 0.01;
+
+ if (end[0] == 0)
+ return val;
+
+ return 0;
+}
+
+/* Return angle in degrees */
+float
+svg_parse_angle(const char *str)
+{
+ char *end;
+ float val;
+
+ val = (float)strtod(str, &end);
+ if (end == str)
+ return 0; /* failed */
+
+ if (!strcmp(end, "deg"))
+ return val;
+
+ if (!strcmp(end, "grad"))
+ return val * 0.9;
+
+ if (!strcmp(end, "rad"))
+ return val * 57.2957795;
+
+ return val;
+}
+
+/* Coordinate transformations */
+void
+svg_parse_transform(fz_context *ctx, svg_document *doc, char *str, fz_matrix *transform)
+{
+ char keyword[20];
+ int keywordlen;
+ char number[20];
+ int numberlen;
+ float args[6];
+ int nargs;
+
+ nargs = 0;
+ keywordlen = 0;
+
+ while (*str)
+ {
+ keywordlen = 0;
+ nargs = 0;
+
+ /*
+ * Parse keyword and opening parenthesis.
+ */
+
+ while (svg_is_whitespace(*str))
+ str ++;
+
+ if (*str == 0)
+ break;
+
+ while (svg_is_alpha(*str) && keywordlen < sizeof(keyword) - 1)
+ keyword[keywordlen++] = *str++;
+ keyword[keywordlen] = 0;
+
+ if (keywordlen == 0)
+ fz_throw(ctx, FZ_ERROR_GENERIC, "syntax error in transform attribute - no keyword");
+
+ while (svg_is_whitespace(*str))
+ str ++;
+
+ if (*str != '(')
+ fz_throw(ctx, FZ_ERROR_GENERIC, "syntax error in transform attribute - no open paren");
+ str ++;
+
+ while (svg_is_whitespace(*str))
+ str ++;
+
+ /*
+ * Parse list of numbers until closing parenthesis
+ */
+
+ while (nargs < 6)
+ {
+ numberlen = 0;
+
+ while (svg_is_digit(*str) && numberlen < sizeof(number) - 1)
+ number[numberlen++] = *str++;
+ number[numberlen] = 0;
+
+ args[nargs++] = atof(number);
+
+ while (svg_is_whitespace_or_comma(*str))
+ str ++;
+
+ if (*str == ')')
+ {
+ str ++;
+ break;
+ }
+
+ if (*str == 0)
+ fz_throw(ctx, FZ_ERROR_GENERIC, "syntax error in transform attribute - no close paren");
+ }
+
+ while (svg_is_whitespace_or_comma(*str))
+ str ++;
+
+ /*
+ * Execute the transform.
+ */
+
+ if (!strcmp(keyword, "matrix"))
+ {
+ fz_matrix m;
+
+ if (nargs != 6)
+ fz_throw(ctx, FZ_ERROR_GENERIC, "wrong number of arguments to matrix(): %d", nargs);
+
+ m.a = args[0];
+ m.b = args[1];
+ m.c = args[2];
+ m.d = args[3];
+ m.e = args[4];
+ m.f = args[5];
+
+ fz_concat(transform, transform, &m);
+ }
+
+ else if (!strcmp(keyword, "translate"))
+ {
+ if (nargs != 2)
+ fz_throw(ctx, FZ_ERROR_GENERIC, "wrong number of arguments to translate(): %d", nargs);
+
+ fz_pre_translate(transform, args[0], args[1]);
+ }
+
+ else if (!strcmp(keyword, "scale"))
+ {
+ if (nargs == 1)
+ fz_pre_scale(transform, args[0], args[0]);
+ else if (nargs == 2)
+ fz_pre_scale(transform, args[0], args[1]);
+ else
+ fz_throw(ctx, FZ_ERROR_GENERIC, "wrong number of arguments to scale(): %d", nargs);
+ }
+
+ else if (!strcmp(keyword, "rotate"))
+ {
+ if (nargs != 1)
+ fz_throw(ctx, FZ_ERROR_GENERIC, "wrong number of arguments to rotate(): %d", nargs);
+ fz_pre_rotate(transform, args[0]);
+ }
+
+ else if (!strcmp(keyword, "skewX"))
+ {
+ fz_matrix m;
+
+ if (nargs != 1)
+ fz_throw(ctx, FZ_ERROR_GENERIC, "wrong number of arguments to skewX(): %d", nargs);
+
+ m.a = 1.0;
+ m.b = 0.0;
+ m.c = tan(args[0] * 0.0174532925);
+ m.d = 1.0;
+ m.e = 0.0;
+ m.f = 0.0;
+
+ fz_concat(transform, transform, &m);
+ }
+
+ else if (!strcmp(keyword, "skewY"))
+ {
+ fz_matrix m;
+
+ if (nargs != 1)
+ fz_throw(ctx, FZ_ERROR_GENERIC, "wrong number of arguments to skewY(): %d", nargs);
+
+ m.a = 1.0;
+ m.b = tan(args[0] * 0.0174532925);
+ m.c = 0.0;
+ m.d = 1.0;
+ m.e = 0.0;
+ m.f = 0.0;
+
+ fz_concat(transform, transform, &m);
+ }
+
+ else
+ {
+ fz_throw(ctx, FZ_ERROR_GENERIC, "unknown transform function: %s", keyword);
+ }
+ }
+}