Mercurial > hgrepos > Python2 > PyMuPDF
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 } |
