comparison mupdf-source/source/svg/svg-parse.c @ 2:b50eed0cc0ef upstream

ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4. The directory name has changed: no version number in the expanded directory now.
author Franz Glasner <fzglas.hg@dom66.de>
date Mon, 15 Sep 2025 11:43:07 +0200
parents
children
comparison
equal deleted inserted replaced
1:1d09e1dec1d9 2:b50eed0cc0ef
1 // Copyright (C) 2004-2021 Artifex Software, Inc.
2 //
3 // This file is part of MuPDF.
4 //
5 // MuPDF is free software: you can redistribute it and/or modify it under the
6 // terms of the GNU Affero General Public License as published by the Free
7 // Software Foundation, either version 3 of the License, or (at your option)
8 // any later version.
9 //
10 // MuPDF is distributed in the hope that it will be useful, but WITHOUT ANY
11 // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12 // FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
13 // details.
14 //
15 // You should have received a copy of the GNU Affero General Public License
16 // along with MuPDF. If not, see <https://www.gnu.org/licenses/agpl-3.0.en.html>
17 //
18 // Alternative licensing terms are available from the licensor.
19 // For commercial licensing, see <https://www.artifex.com/> or contact
20 // Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco,
21 // CA 94129, USA, for further information.
22
23 #include "mupdf/fitz.h"
24 #include "svg-imp.h"
25
26 #include <string.h>
27 #include <math.h>
28
29 int svg_is_whitespace_or_comma(int c)
30 {
31 return (c == 0x20) || (c == 0x9) || (c == 0xD) || (c == 0xA) || (c == ',');
32 }
33
34 int svg_is_whitespace(int c)
35 {
36 return (c == 0x20) || (c == 0x9) || (c == 0xD) || (c == 0xA);
37 }
38
39 int svg_is_alpha(int c)
40 {
41 return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z');
42 }
43
44 int svg_is_digit(int c)
45 {
46 return (c >= '0' && c <= '9') ||
47 (c == 'e') || (c == 'E') ||
48 (c == '+') || (c == '-') || (c == '.');
49 }
50
51 const char *
52 svg_lex_number(float *fp, const char *ss)
53 {
54 const char *s = ss;
55 if (*s == '+' || *s == '-')
56 ++s;
57 while (*s >= '0' && *s <= '9')
58 ++s;
59 if (*s == '.') {
60 ++s;
61 while (*s >= '0' && *s <= '9')
62 ++s;
63 }
64 if (*s == 'e' || *s == 'E') {
65 ++s;
66 if (*s == '+' || *s == '-')
67 ++s;
68 while (*s >= '0' && *s <= '9')
69 ++s;
70 }
71 *fp = fz_atof(ss);
72 return s;
73 }
74
75 float
76 svg_parse_number(const char *str, float min, float max, float inherit)
77 {
78 float x;
79 if (!strcmp(str, "inherit"))
80 return inherit;
81 x = fz_atof(str);
82 if (x < min) return min;
83 if (x > max) return max;
84 return x;
85 }
86
87 float
88 svg_parse_length(const char *str, float percent, float font_size)
89 {
90 char *end;
91 float val;
92
93 val = fz_strtof(str, &end);
94 if (end == str)
95 return 0; /* failed */
96
97 if (!strcmp(end, "px")) return val;
98
99 if (!strcmp(end, "pt")) return val * 1.0f;
100 if (!strcmp(end, "pc")) return val * 12.0f;
101 if (!strcmp(end, "mm")) return val * 2.83464567f;
102 if (!strcmp(end, "cm")) return val * 28.3464567f;
103 if (!strcmp(end, "in")) return val * 72.0f;
104
105 if (!strcmp(end, "em")) return val * font_size;
106 if (!strcmp(end, "ex")) return val * font_size * 0.5f;
107
108 if (!strcmp(end, "%"))
109 return val * percent * 0.01f;
110
111 if (end[0] == 0)
112 return val;
113
114 return 0;
115 }
116
117 /* Return angle in degrees */
118 float
119 svg_parse_angle(const char *str)
120 {
121 char *end;
122 float val;
123
124 val = fz_strtof(str, &end);
125 if (end == str)
126 return 0; /* failed */
127
128 if (!strcmp(end, "deg"))
129 return val;
130
131 if (!strcmp(end, "grad"))
132 return val * 0.9f;
133
134 if (!strcmp(end, "rad"))
135 return val * FZ_RADIAN;
136
137 return val;
138 }
139
140 /* Coordinate transformations */
141 fz_matrix
142 svg_parse_transform(fz_context *ctx, svg_document *doc, const char *str, fz_matrix transform)
143 {
144 char keyword[20];
145 int keywordlen;
146 float args[6];
147 int nargs;
148
149 nargs = 0;
150 keywordlen = 0;
151
152 while (*str)
153 {
154 while (svg_is_whitespace_or_comma(*str))
155 str ++;
156 if (*str == 0)
157 break;
158
159 /*
160 * Parse keyword and opening parenthesis.
161 */
162
163 keywordlen = 0;
164 while (svg_is_alpha(*str) && keywordlen < (int)sizeof(keyword) - 1)
165 keyword[keywordlen++] = *str++;
166 keyword[keywordlen] = 0;
167
168 if (keywordlen == 0)
169 fz_throw(ctx, FZ_ERROR_SYNTAX, "expected keyword in transform attribute");
170
171 while (svg_is_whitespace(*str))
172 str ++;
173
174 if (*str != '(')
175 fz_throw(ctx, FZ_ERROR_SYNTAX, "expected opening parenthesis in transform attribute");
176 str ++;
177
178 /*
179 * Parse list of numbers until closing parenthesis
180 */
181
182 nargs = 0;
183 while (*str && *str != ')' && nargs < 6)
184 {
185 while (svg_is_whitespace_or_comma(*str))
186 str ++;
187 if (svg_is_digit(*str))
188 str = svg_lex_number(&args[nargs++], str);
189 else
190 break;
191 }
192
193 if (*str != ')')
194 fz_throw(ctx, FZ_ERROR_SYNTAX, "expected closing parenthesis in transform attribute");
195 str ++;
196
197 /*
198 * Execute the transform.
199 */
200
201 if (!strcmp(keyword, "matrix"))
202 {
203 if (nargs != 6)
204 fz_throw(ctx, FZ_ERROR_SYNTAX, "wrong number of arguments to matrix(): %d", nargs);
205 transform = fz_concat(fz_make_matrix(args[0], args[1], args[2], args[3], args[4], args[5]), transform);
206 }
207
208 else if (!strcmp(keyword, "translate"))
209 {
210 if (nargs == 1)
211 transform = fz_concat(fz_translate(args[0], 0), transform);
212 else if (nargs == 2)
213 transform = fz_concat(fz_translate(args[0], args[1]), transform);
214 else
215 fz_throw(ctx, FZ_ERROR_SYNTAX, "wrong number of arguments to translate(): %d", nargs);
216 }
217
218 else if (!strcmp(keyword, "scale"))
219 {
220 if (nargs == 1)
221 transform = fz_concat(fz_scale(args[0], args[0]), transform);
222 else if (nargs == 2)
223 transform = fz_concat(fz_scale(args[0], args[1]), transform);
224 else
225 fz_throw(ctx, FZ_ERROR_SYNTAX, "wrong number of arguments to scale(): %d", nargs);
226 }
227
228 else if (!strcmp(keyword, "rotate"))
229 {
230 if (nargs == 1)
231 transform = fz_concat(fz_rotate(args[0]), transform);
232 else if (nargs == 3)
233 {
234 transform = fz_concat(fz_translate(args[1], args[2]), transform);
235 transform = fz_concat(fz_rotate(args[0]), transform);
236 transform = fz_concat(fz_translate(-args[1], -args[2]), transform);
237 }
238 else
239 fz_throw(ctx, FZ_ERROR_SYNTAX, "wrong number of arguments to rotate(): %d", nargs);
240 }
241
242 else if (!strcmp(keyword, "skewX"))
243 {
244 if (nargs != 1)
245 fz_throw(ctx, FZ_ERROR_SYNTAX, "wrong number of arguments to skewX(): %d", nargs);
246 transform = fz_concat(fz_make_matrix(1, 0, tanf(args[0] * FZ_DEGREE), 1, 0, 0), transform);
247 }
248
249 else if (!strcmp(keyword, "skewY"))
250 {
251 if (nargs != 1)
252 fz_throw(ctx, FZ_ERROR_SYNTAX, "wrong number of arguments to skewY(): %d", nargs);
253 transform = fz_concat(fz_make_matrix(1, tanf(args[0] * FZ_DEGREE), 0, 1, 0, 0), transform);
254 }
255
256 else
257 {
258 fz_throw(ctx, FZ_ERROR_SYNTAX, "unknown transform function: %s", keyword);
259 }
260 }
261
262 return transform;
263 }
264
265 float
266 svg_parse_number_from_style(fz_context *ctx, svg_document *doc, const char *style, const char *att, float number)
267 {
268 if (style)
269 {
270 char *end, *p = strstr(style, att);
271 if (p)
272 {
273 size_t n = strlen(att);
274 if (p[n] == ':')
275 {
276 p += n + 1;
277 while (*p && svg_is_whitespace(*p))
278 ++p;
279 number = fz_strtof(p, &end);
280 if (end[0] == 'i' && end[1] == 'n') return number * 72;
281 if (end[0] == 'c' && end[1] == 'm') return number * 7200 / 254;
282 if (end[0] == 'm' && end[1] == 'm') return number * 720 / 254;
283 if (end[0] == 'p' && end[1] == 'c') return number * 12;
284 }
285 }
286 }
287 return number;
288 }
289
290 int
291 svg_parse_enum_from_style(fz_context *ctx, svg_document *doc, const char *style, const char *att,
292 int ecount, const char *etable[], int value)
293 {
294 char buf[100], *end, *p;
295 int i;
296 if (style)
297 {
298 p = strstr(style, att);
299 if (p)
300 {
301 size_t n = strlen(att);
302 if (p[n] == ':')
303 {
304 p += n + 1;
305 while (*p && svg_is_whitespace(*p))
306 ++p;
307 fz_strlcpy(buf, p, sizeof buf);
308 end = strchr(buf, ';');
309 if (end)
310 *end = 0;
311 for (i = 0; i < ecount; ++i)
312 if (!strcmp(etable[i], buf))
313 return i;
314 }
315 }
316 }
317 return value;
318 }
319
320 char *
321 svg_parse_string_from_style(fz_context *ctx, svg_document *doc, const char *style, const char *att,
322 char *buf, int buf_size, const char *value)
323 {
324 char *end, *p, quote;
325 if (style)
326 {
327 p = strstr(style, att);
328 if (p)
329 {
330 size_t n = strlen(att);
331 if (p[n] == ':')
332 {
333 p += n + 1;
334 while (*p && svg_is_whitespace(*p))
335 ++p;
336 quote = *p;
337 if (quote == '\'' || quote == '"')
338 {
339 fz_strlcpy(buf, p+1, buf_size);
340 end = strchr(buf, quote);
341 }
342 else
343 {
344 fz_strlcpy(buf, p, buf_size);
345 end = strchr(buf, ';');
346 }
347 if (end)
348 *end = 0;
349 return buf;
350 }
351 }
352 }
353 fz_strlcpy(buf, value, buf_size);
354 return buf;
355 }