1#include "mupdf/fitz.h"
2#include "svg-imp.h"
3
4#include <string.h>
5#include <math.h>
6
7int svg_is_whitespace_or_comma(int c)
8{
9 return (c == 0x20) || (c == 0x9) || (c == 0xD) || (c == 0xA) || (c == ',');
10}
11
12int svg_is_whitespace(int c)
13{
14 return (c == 0x20) || (c == 0x9) || (c == 0xD) || (c == 0xA);
15}
16
17int svg_is_alpha(int c)
18{
19 return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z');
20}
21
22int svg_is_digit(int c)
23{
24 return (c >= '0' && c <= '9') ||
25 (c == 'e') || (c == 'E') ||
26 (c == '+') || (c == '-') || (c == '.');
27}
28
29const char *
30svg_lex_number(float *fp, const char *ss)
31{
32 const char *s = ss;
33 if (*s == '-')
34 ++s;
35 while (*s >= '0' && *s <= '9')
36 ++s;
37 if (*s == '.') {
38 ++s;
39 while (*s >= '0' && *s <= '9')
40 ++s;
41 }
42 if (*s == 'e' || *s == 'E') {
43 ++s;
44 if (*s == '+' || *s == '-')
45 ++s;
46 while (*s >= '0' && *s <= '9')
47 ++s;
48 }
49 *fp = fz_atof(ss);
50 return s;
51}
52
53float
54svg_parse_number(const char *str, float min, float max, float inherit)
55{
56 float x;
57 if (!strcmp(str, "inherit"))
58 return inherit;
59 x = fz_atof(str);
60 if (x < min) return min;
61 if (x > max) return max;
62 return x;
63}
64
65/* Return length/coordinate in points */
66float
67svg_parse_length(const char *str, float percent, float font_size)
68{
69 char *end;
70 float val;
71
72 val = fz_strtof(str, &end);
73 if (end == str)
74 return 0; /* failed */
75
76 if (!strcmp(end, "px")) return val;
77
78 if (!strcmp(end, "pt")) return val * 1.0f;
79 if (!strcmp(end, "pc")) return val * 12.0f;
80 if (!strcmp(end, "mm")) return val * 2.83464567f;
81 if (!strcmp(end, "cm")) return val * 28.3464567f;
82 if (!strcmp(end, "in")) return val * 72.0f;
83
84 if (!strcmp(end, "em")) return val * font_size;
85 if (!strcmp(end, "ex")) return val * font_size * 0.5f;
86
87 if (!strcmp(end, "%"))
88 return val * percent * 0.01f;
89
90 if (end[0] == 0)
91 return val;
92
93 return 0;
94}
95
96/* Return angle in degrees */
97float
98svg_parse_angle(const char *str)
99{
100 char *end;
101 float val;
102
103 val = fz_strtof(str, &end);
104 if (end == str)
105 return 0; /* failed */
106
107 if (!strcmp(end, "deg"))
108 return val;
109
110 if (!strcmp(end, "grad"))
111 return val * 0.9f;
112
113 if (!strcmp(end, "rad"))
114 return val * FZ_RADIAN;
115
116 return val;
117}
118
119/* Coordinate transformations */
120fz_matrix
121svg_parse_transform(fz_context *ctx, svg_document *doc, const char *str, fz_matrix transform)
122{
123 char keyword[20];
124 int keywordlen;
125 float args[6];
126 int nargs;
127
128 nargs = 0;
129 keywordlen = 0;
130
131 while (*str)
132 {
133 while (svg_is_whitespace_or_comma(*str))
134 str ++;
135 if (*str == 0)
136 break;
137
138 /*
139 * Parse keyword and opening parenthesis.
140 */
141
142 keywordlen = 0;
143 while (svg_is_alpha(*str) && keywordlen < sizeof(keyword) - 1)
144 keyword[keywordlen++] = *str++;
145 keyword[keywordlen] = 0;
146
147 if (keywordlen == 0)
148 fz_throw(ctx, FZ_ERROR_SYNTAX, "expected keyword in transform attribute");
149
150 while (svg_is_whitespace(*str))
151 str ++;
152
153 if (*str != '(')
154 fz_throw(ctx, FZ_ERROR_SYNTAX, "expected opening parenthesis in transform attribute");
155 str ++;
156
157 /*
158 * Parse list of numbers until closing parenthesis
159 */
160
161 nargs = 0;
162 while (*str && *str != ')' && nargs < 6)
163 {
164 while (svg_is_whitespace_or_comma(*str))
165 str ++;
166 if (svg_is_digit(*str))
167 str = svg_lex_number(&args[nargs++], str);
168 else
169 break;
170 }
171
172 if (*str != ')')
173 fz_throw(ctx, FZ_ERROR_SYNTAX, "expected closing parenthesis in transform attribute");
174 str ++;
175
176 /*
177 * Execute the transform.
178 */
179
180 if (!strcmp(keyword, "matrix"))
181 {
182 if (nargs != 6)
183 fz_throw(ctx, FZ_ERROR_SYNTAX, "wrong number of arguments to matrix(): %d", nargs);
184 transform = fz_concat(fz_make_matrix(args[0], args[1], args[2], args[3], args[4], args[5]), transform);
185 }
186
187 else if (!strcmp(keyword, "translate"))
188 {
189 if (nargs != 2)
190 fz_throw(ctx, FZ_ERROR_SYNTAX, "wrong number of arguments to translate(): %d", nargs);
191 transform = fz_concat(fz_translate(args[0], args[1]), transform);
192 }
193
194 else if (!strcmp(keyword, "scale"))
195 {
196 if (nargs == 1)
197 transform = fz_concat(fz_scale(args[0], args[0]), transform);
198 else if (nargs == 2)
199 transform = fz_concat(fz_scale(args[0], args[1]), transform);
200 else
201 fz_throw(ctx, FZ_ERROR_SYNTAX, "wrong number of arguments to scale(): %d", nargs);
202 }
203
204 else if (!strcmp(keyword, "rotate"))
205 {
206 if (nargs != 1)
207 fz_throw(ctx, FZ_ERROR_SYNTAX, "wrong number of arguments to rotate(): %d", nargs);
208 transform = fz_concat(fz_rotate(args[0]), transform);
209 }
210
211 else if (!strcmp(keyword, "skewX"))
212 {
213 if (nargs != 1)
214 fz_throw(ctx, FZ_ERROR_SYNTAX, "wrong number of arguments to skewX(): %d", nargs);
215 transform = fz_concat(fz_make_matrix(1, 0, tanf(args[0] * FZ_DEGREE), 1, 0, 0), transform);
216 }
217
218 else if (!strcmp(keyword, "skewY"))
219 {
220 if (nargs != 1)
221 fz_throw(ctx, FZ_ERROR_SYNTAX, "wrong number of arguments to skewY(): %d", nargs);
222 transform = fz_concat(fz_make_matrix(1, tanf(args[0] * FZ_DEGREE), 0, 1, 0, 0), transform);
223 }
224
225 else
226 {
227 fz_throw(ctx, FZ_ERROR_SYNTAX, "unknown transform function: %s", keyword);
228 }
229 }
230
231 return transform;
232}
233