Mercurial > hgrepos > Python2 > PyMuPDF
comparison mupdf-source/source/xps/xps-path.c @ 3:2c135c81b16c
MERGE: upstream PyMuPDF 1.26.4 with MuPDF 1.26.7
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Mon, 15 Sep 2025 11:44:09 +0200 |
| parents | b50eed0cc0ef |
| children |
comparison
equal
deleted
inserted
replaced
| 0:6015a75abc2d | 3:2c135c81b16c |
|---|---|
| 1 // Copyright (C) 2004-2024 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 "xps-imp.h" | |
| 25 | |
| 26 #include <math.h> | |
| 27 #include <string.h> | |
| 28 #include <stdlib.h> | |
| 29 | |
| 30 static char * | |
| 31 xps_parse_float_array(fz_context *ctx, xps_document *doc, char *s, int num, int *obtained, float *x) | |
| 32 { | |
| 33 int k = 0; | |
| 34 | |
| 35 if (s == NULL || *s == 0) | |
| 36 { | |
| 37 if (obtained) | |
| 38 *obtained = k; | |
| 39 return NULL; | |
| 40 } | |
| 41 | |
| 42 while (*s) | |
| 43 { | |
| 44 while (*s == 0x0d || *s == '\t' || *s == ' ' || *s == 0x0a) | |
| 45 s++; | |
| 46 x[k] = fz_strtof(s, &s); | |
| 47 while (*s == 0x0d || *s == '\t' || *s == ' ' || *s == 0x0a) | |
| 48 s++; | |
| 49 if (*s == ',') | |
| 50 s++; | |
| 51 if (++k == num) | |
| 52 break; | |
| 53 } | |
| 54 if (obtained) | |
| 55 *obtained = k; | |
| 56 return s; | |
| 57 } | |
| 58 | |
| 59 char * | |
| 60 xps_parse_point(fz_context *ctx, xps_document *doc, char *s_in, float *x, float *y) | |
| 61 { | |
| 62 char *s_out = s_in; | |
| 63 float xy[2]; | |
| 64 int obtained = 0; | |
| 65 | |
| 66 s_out = xps_parse_float_array(ctx, doc, s_out, 2, &obtained, &xy[0]); | |
| 67 if (obtained >= 2) | |
| 68 { | |
| 69 *x = xy[0]; | |
| 70 *y = xy[1]; | |
| 71 } | |
| 72 return s_out; | |
| 73 } | |
| 74 | |
| 75 /* Draw an arc segment transformed by the matrix, we approximate with straight | |
| 76 * line segments. We cannot use the fz_arc function because they only draw | |
| 77 * circular arcs, we need to transform the line to make them elliptical but | |
| 78 * without transforming the line width. | |
| 79 * | |
| 80 * We are guaranteed that on entry the point is at the point that would be | |
| 81 * calculated by th0, and on exit, a point is generated for us at th0. | |
| 82 */ | |
| 83 static void | |
| 84 xps_draw_arc_segment(fz_context *ctx, xps_document *doc, fz_path *path, fz_matrix mtx, float th0, float th1, int iscw) | |
| 85 { | |
| 86 float t, d; | |
| 87 fz_point p; | |
| 88 | |
| 89 while (th1 < th0) | |
| 90 th1 += FZ_PI * 2; | |
| 91 | |
| 92 d = FZ_PI / 180; /* 1-degree precision */ | |
| 93 | |
| 94 if (iscw) | |
| 95 { | |
| 96 for (t = th0 + d; t < th1 - d/2; t += d) | |
| 97 { | |
| 98 p = fz_transform_point_xy(cosf(t), sinf(t), mtx); | |
| 99 fz_lineto(ctx, path, p.x, p.y); | |
| 100 } | |
| 101 } | |
| 102 else | |
| 103 { | |
| 104 th0 += FZ_PI * 2; | |
| 105 for (t = th0 - d; t > th1 + d/2; t -= d) | |
| 106 { | |
| 107 p = fz_transform_point_xy(cosf(t), sinf(t), mtx); | |
| 108 fz_lineto(ctx, path, p.x, p.y); | |
| 109 } | |
| 110 } | |
| 111 } | |
| 112 | |
| 113 /* Given two vectors find the angle between them. */ | |
| 114 static float | |
| 115 angle_between(fz_point u, fz_point v) | |
| 116 { | |
| 117 float det = u.x * v.y - u.y * v.x; | |
| 118 float sign = (det < 0 ? -1 : 1); | |
| 119 float magu = u.x * u.x + u.y * u.y; | |
| 120 float magv = v.x * v.x + v.y * v.y; | |
| 121 float udotv = u.x * v.x + u.y * v.y; | |
| 122 float t = udotv / (magu * magv); | |
| 123 /* guard against rounding errors when near |1| (where acos will return NaN) */ | |
| 124 if (t < -1) t = -1; | |
| 125 if (t > 1) t = 1; | |
| 126 return sign * acosf(t); | |
| 127 } | |
| 128 | |
| 129 /* | |
| 130 Some explanation of the parameters here is warranted. See: | |
| 131 | |
| 132 http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes | |
| 133 | |
| 134 Add an arc segment to path, that describes a section of an elliptical | |
| 135 arc from the current point of path to (point_x,point_y), such that: | |
| 136 | |
| 137 The arc segment is taken from an elliptical arc of semi major radius | |
| 138 size_x, semi minor radius size_y, where the semi major axis of the | |
| 139 ellipse is rotated by rotation_angle. | |
| 140 | |
| 141 If is_large_arc, then the arc segment is selected to be > 180 degrees. | |
| 142 | |
| 143 If is_clockwise, then the arc sweeps clockwise. | |
| 144 */ | |
| 145 static void | |
| 146 xps_draw_arc(fz_context *ctx, xps_document *doc, fz_path *path, | |
| 147 float size_x, float size_y, float rotation_angle, | |
| 148 int is_large_arc, int is_clockwise, | |
| 149 float point_x, float point_y) | |
| 150 { | |
| 151 fz_matrix rotmat, revmat; | |
| 152 fz_matrix mtx; | |
| 153 fz_point pt; | |
| 154 float rx, ry; | |
| 155 float x1, y1, x2, y2; | |
| 156 float x1t, y1t; | |
| 157 float cxt, cyt, cx, cy; | |
| 158 float t1, t2, t3; | |
| 159 float sign; | |
| 160 float th1, dth; | |
| 161 | |
| 162 pt = fz_currentpoint(ctx, path); | |
| 163 x1 = pt.x; | |
| 164 y1 = pt.y; | |
| 165 x2 = point_x; | |
| 166 y2 = point_y; | |
| 167 rx = size_x; | |
| 168 ry = size_y; | |
| 169 | |
| 170 if (is_clockwise != is_large_arc) | |
| 171 sign = 1; | |
| 172 else | |
| 173 sign = -1; | |
| 174 | |
| 175 rotmat = fz_rotate(rotation_angle); | |
| 176 revmat = fz_rotate(-rotation_angle); | |
| 177 | |
| 178 /* http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes */ | |
| 179 /* Conversion from endpoint to center parameterization */ | |
| 180 | |
| 181 /* F.6.6.1 -- ensure radii are positive and non-zero */ | |
| 182 rx = fabsf(rx); | |
| 183 ry = fabsf(ry); | |
| 184 if (rx < 0.001f || ry < 0.001f || (x1 == x2 && y1 == y2)) | |
| 185 { | |
| 186 fz_lineto(ctx, path, x2, y2); | |
| 187 return; | |
| 188 } | |
| 189 | |
| 190 /* F.6.5.1 */ | |
| 191 pt.x = (x1 - x2) / 2; | |
| 192 pt.y = (y1 - y2) / 2; | |
| 193 pt = fz_transform_vector(pt, revmat); | |
| 194 x1t = pt.x; | |
| 195 y1t = pt.y; | |
| 196 | |
| 197 /* F.6.6.2 -- ensure radii are large enough */ | |
| 198 t1 = (x1t * x1t) / (rx * rx) + (y1t * y1t) / (ry * ry); | |
| 199 if (t1 > 1) | |
| 200 { | |
| 201 rx = rx * sqrtf(t1); | |
| 202 ry = ry * sqrtf(t1); | |
| 203 } | |
| 204 | |
| 205 /* F.6.5.2 */ | |
| 206 t1 = (rx * rx * ry * ry) - (rx * rx * y1t * y1t) - (ry * ry * x1t * x1t); | |
| 207 t2 = (rx * rx * y1t * y1t) + (ry * ry * x1t * x1t); | |
| 208 t3 = t1 / t2; | |
| 209 /* guard against rounding errors; sqrt of negative numbers is bad for your health */ | |
| 210 if (t3 < 0) t3 = 0; | |
| 211 t3 = sqrtf(t3); | |
| 212 | |
| 213 cxt = sign * t3 * (rx * y1t) / ry; | |
| 214 cyt = sign * t3 * -(ry * x1t) / rx; | |
| 215 | |
| 216 /* F.6.5.3 */ | |
| 217 pt.x = cxt; | |
| 218 pt.y = cyt; | |
| 219 pt = fz_transform_vector(pt, rotmat); | |
| 220 cx = pt.x + (x1 + x2) / 2; | |
| 221 cy = pt.y + (y1 + y2) / 2; | |
| 222 | |
| 223 /* F.6.5.4 */ | |
| 224 { | |
| 225 fz_point coord1, coord2, coord3, coord4; | |
| 226 coord1.x = 1; | |
| 227 coord1.y = 0; | |
| 228 coord2.x = (x1t - cxt) / rx; | |
| 229 coord2.y = (y1t - cyt) / ry; | |
| 230 coord3.x = (x1t - cxt) / rx; | |
| 231 coord3.y = (y1t - cyt) / ry; | |
| 232 coord4.x = (-x1t - cxt) / rx; | |
| 233 coord4.y = (-y1t - cyt) / ry; | |
| 234 th1 = angle_between(coord1, coord2); | |
| 235 dth = angle_between(coord3, coord4); | |
| 236 if (dth < 0 && !is_clockwise) | |
| 237 dth += ((FZ_PI / 180) * 360); | |
| 238 if (dth > 0 && is_clockwise) | |
| 239 dth -= ((FZ_PI / 180) * 360); | |
| 240 } | |
| 241 | |
| 242 mtx = fz_pre_scale(fz_pre_rotate(fz_translate(cx, cy), rotation_angle), rx, ry); | |
| 243 xps_draw_arc_segment(ctx, doc, path, mtx, th1, th1 + dth, is_clockwise); | |
| 244 | |
| 245 fz_lineto(ctx, path, point_x, point_y); | |
| 246 } | |
| 247 | |
| 248 fz_path * | |
| 249 xps_parse_abbreviated_geometry(fz_context *ctx, xps_document *doc, char *geom, int *fill_rule) | |
| 250 { | |
| 251 fz_path *path; | |
| 252 char **args = NULL; | |
| 253 char **pargs; | |
| 254 char *s = geom; | |
| 255 fz_point pt; | |
| 256 int i, n; | |
| 257 int cmd, old; | |
| 258 float x1, y1, x2, y2, x3, y3; | |
| 259 float smooth_x, smooth_y; /* saved cubic bezier control point for smooth curves */ | |
| 260 int reset_smooth; | |
| 261 | |
| 262 fz_var(args); | |
| 263 | |
| 264 path = fz_new_path(ctx); | |
| 265 | |
| 266 fz_try(ctx) | |
| 267 { | |
| 268 args = fz_malloc_array(ctx, strlen(geom) + 1, char*); | |
| 269 pargs = args; | |
| 270 | |
| 271 while (*s) | |
| 272 { | |
| 273 if ((*s >= 'A' && *s <= 'Z') || (*s >= 'a' && *s <= 'z')) | |
| 274 { | |
| 275 *pargs++ = s++; | |
| 276 } | |
| 277 else if ((*s >= '0' && *s <= '9') || *s == '.' || *s == '+' || *s == '-' || *s == 'e' || *s == 'E') | |
| 278 { | |
| 279 *pargs++ = s; | |
| 280 while ((*s >= '0' && *s <= '9') || *s == '.' || *s == '+' || *s == '-' || *s == 'e' || *s == 'E') | |
| 281 s ++; | |
| 282 } | |
| 283 else | |
| 284 { | |
| 285 s++; | |
| 286 } | |
| 287 } | |
| 288 | |
| 289 *pargs = s; | |
| 290 | |
| 291 n = pargs - args; | |
| 292 i = 0; | |
| 293 | |
| 294 old = 0; | |
| 295 | |
| 296 reset_smooth = 1; | |
| 297 smooth_x = 0; | |
| 298 smooth_y = 0; | |
| 299 | |
| 300 while (i < n) | |
| 301 { | |
| 302 cmd = args[i][0]; | |
| 303 if (cmd == '+' || cmd == '.' || cmd == '-' || (cmd >= '0' && cmd <= '9')) | |
| 304 cmd = old; /* it's a number, repeat old command */ | |
| 305 else | |
| 306 i ++; | |
| 307 | |
| 308 if (reset_smooth) | |
| 309 { | |
| 310 smooth_x = 0; | |
| 311 smooth_y = 0; | |
| 312 } | |
| 313 | |
| 314 reset_smooth = 1; | |
| 315 | |
| 316 switch (cmd) | |
| 317 { | |
| 318 case 'F': | |
| 319 if (i >= n) break; | |
| 320 *fill_rule = atoi(args[i]); | |
| 321 i ++; | |
| 322 break; | |
| 323 | |
| 324 case 'M': | |
| 325 if (i + 1 >= n) break; | |
| 326 fz_moveto(ctx, path, fz_atof(args[i]), fz_atof(args[i+1])); | |
| 327 i += 2; | |
| 328 break; | |
| 329 case 'm': | |
| 330 if (i + 1 >= n) break; | |
| 331 pt = fz_currentpoint(ctx, path); | |
| 332 fz_moveto(ctx, path, pt.x + fz_atof(args[i]), pt.y + fz_atof(args[i+1])); | |
| 333 i += 2; | |
| 334 break; | |
| 335 | |
| 336 case 'L': | |
| 337 if (i + 1 >= n) break; | |
| 338 fz_lineto(ctx, path, fz_atof(args[i]), fz_atof(args[i+1])); | |
| 339 i += 2; | |
| 340 break; | |
| 341 case 'l': | |
| 342 if (i + 1 >= n) break; | |
| 343 pt = fz_currentpoint(ctx, path); | |
| 344 fz_lineto(ctx, path, pt.x + fz_atof(args[i]), pt.y + fz_atof(args[i+1])); | |
| 345 i += 2; | |
| 346 break; | |
| 347 | |
| 348 case 'H': | |
| 349 if (i >= n) break; | |
| 350 pt = fz_currentpoint(ctx, path); | |
| 351 fz_lineto(ctx, path, fz_atof(args[i]), pt.y); | |
| 352 i += 1; | |
| 353 break; | |
| 354 case 'h': | |
| 355 if (i >= n) break; | |
| 356 pt = fz_currentpoint(ctx, path); | |
| 357 fz_lineto(ctx, path, pt.x + fz_atof(args[i]), pt.y); | |
| 358 i += 1; | |
| 359 break; | |
| 360 | |
| 361 case 'V': | |
| 362 if (i >= n) break; | |
| 363 pt = fz_currentpoint(ctx, path); | |
| 364 fz_lineto(ctx, path, pt.x, fz_atof(args[i])); | |
| 365 i += 1; | |
| 366 break; | |
| 367 case 'v': | |
| 368 if (i >= n) break; | |
| 369 pt = fz_currentpoint(ctx, path); | |
| 370 fz_lineto(ctx, path, pt.x, pt.y + fz_atof(args[i])); | |
| 371 i += 1; | |
| 372 break; | |
| 373 | |
| 374 case 'C': | |
| 375 if (i + 5 >= n) break; | |
| 376 x1 = fz_atof(args[i+0]); | |
| 377 y1 = fz_atof(args[i+1]); | |
| 378 x2 = fz_atof(args[i+2]); | |
| 379 y2 = fz_atof(args[i+3]); | |
| 380 x3 = fz_atof(args[i+4]); | |
| 381 y3 = fz_atof(args[i+5]); | |
| 382 fz_curveto(ctx, path, x1, y1, x2, y2, x3, y3); | |
| 383 i += 6; | |
| 384 reset_smooth = 0; | |
| 385 smooth_x = x3 - x2; | |
| 386 smooth_y = y3 - y2; | |
| 387 break; | |
| 388 | |
| 389 case 'c': | |
| 390 if (i + 5 >= n) break; | |
| 391 pt = fz_currentpoint(ctx, path); | |
| 392 x1 = fz_atof(args[i+0]) + pt.x; | |
| 393 y1 = fz_atof(args[i+1]) + pt.y; | |
| 394 x2 = fz_atof(args[i+2]) + pt.x; | |
| 395 y2 = fz_atof(args[i+3]) + pt.y; | |
| 396 x3 = fz_atof(args[i+4]) + pt.x; | |
| 397 y3 = fz_atof(args[i+5]) + pt.y; | |
| 398 fz_curveto(ctx, path, x1, y1, x2, y2, x3, y3); | |
| 399 i += 6; | |
| 400 reset_smooth = 0; | |
| 401 smooth_x = x3 - x2; | |
| 402 smooth_y = y3 - y2; | |
| 403 break; | |
| 404 | |
| 405 case 'S': | |
| 406 if (i + 3 >= n) break; | |
| 407 pt = fz_currentpoint(ctx, path); | |
| 408 x1 = fz_atof(args[i+0]); | |
| 409 y1 = fz_atof(args[i+1]); | |
| 410 x2 = fz_atof(args[i+2]); | |
| 411 y2 = fz_atof(args[i+3]); | |
| 412 fz_curveto(ctx, path, pt.x + smooth_x, pt.y + smooth_y, x1, y1, x2, y2); | |
| 413 i += 4; | |
| 414 reset_smooth = 0; | |
| 415 smooth_x = x2 - x1; | |
| 416 smooth_y = y2 - y1; | |
| 417 break; | |
| 418 | |
| 419 case 's': | |
| 420 if (i + 3 >= n) break; | |
| 421 pt = fz_currentpoint(ctx, path); | |
| 422 x1 = fz_atof(args[i+0]) + pt.x; | |
| 423 y1 = fz_atof(args[i+1]) + pt.y; | |
| 424 x2 = fz_atof(args[i+2]) + pt.x; | |
| 425 y2 = fz_atof(args[i+3]) + pt.y; | |
| 426 fz_curveto(ctx, path, pt.x + smooth_x, pt.y + smooth_y, x1, y1, x2, y2); | |
| 427 i += 4; | |
| 428 reset_smooth = 0; | |
| 429 smooth_x = x2 - x1; | |
| 430 smooth_y = y2 - y1; | |
| 431 break; | |
| 432 | |
| 433 case 'Q': | |
| 434 if (i + 3 >= n) break; | |
| 435 x1 = fz_atof(args[i+0]); | |
| 436 y1 = fz_atof(args[i+1]); | |
| 437 x2 = fz_atof(args[i+2]); | |
| 438 y2 = fz_atof(args[i+3]); | |
| 439 fz_quadto(ctx, path, x1, y1, x2, y2); | |
| 440 i += 4; | |
| 441 break; | |
| 442 case 'q': | |
| 443 if (i + 3 >= n) break; | |
| 444 pt = fz_currentpoint(ctx, path); | |
| 445 x1 = fz_atof(args[i+0]) + pt.x; | |
| 446 y1 = fz_atof(args[i+1]) + pt.y; | |
| 447 x2 = fz_atof(args[i+2]) + pt.x; | |
| 448 y2 = fz_atof(args[i+3]) + pt.y; | |
| 449 fz_quadto(ctx, path, x1, y1, x2, y2); | |
| 450 i += 4; | |
| 451 break; | |
| 452 | |
| 453 case 'A': | |
| 454 if (i + 6 >= n) break; | |
| 455 xps_draw_arc(ctx, doc, path, | |
| 456 fz_atof(args[i+0]), fz_atof(args[i+1]), fz_atof(args[i+2]), | |
| 457 atoi(args[i+3]), atoi(args[i+4]), | |
| 458 fz_atof(args[i+5]), fz_atof(args[i+6])); | |
| 459 i += 7; | |
| 460 break; | |
| 461 case 'a': | |
| 462 if (i + 6 >= n) break; | |
| 463 pt = fz_currentpoint(ctx, path); | |
| 464 xps_draw_arc(ctx, doc, path, | |
| 465 fz_atof(args[i+0]), fz_atof(args[i+1]), fz_atof(args[i+2]), | |
| 466 atoi(args[i+3]), atoi(args[i+4]), | |
| 467 fz_atof(args[i+5]) + pt.x, fz_atof(args[i+6]) + pt.y); | |
| 468 i += 7; | |
| 469 break; | |
| 470 | |
| 471 case 'Z': | |
| 472 case 'z': | |
| 473 fz_closepath(ctx, path); | |
| 474 break; | |
| 475 | |
| 476 default: | |
| 477 fz_warn(ctx, "ignoring invalid command '%c'", cmd); | |
| 478 if (old == cmd) /* avoid infinite loop */ | |
| 479 i++; | |
| 480 break; | |
| 481 } | |
| 482 | |
| 483 old = cmd; | |
| 484 } | |
| 485 } | |
| 486 fz_always(ctx) | |
| 487 fz_free(ctx, args); | |
| 488 fz_catch(ctx) | |
| 489 { | |
| 490 fz_drop_path(ctx, path); | |
| 491 fz_rethrow(ctx); | |
| 492 } | |
| 493 | |
| 494 return path; | |
| 495 } | |
| 496 | |
| 497 static void | |
| 498 xps_parse_arc_segment(fz_context *ctx, xps_document *doc, fz_path *path, fz_xml *root, int stroking, int *skipped_stroke) | |
| 499 { | |
| 500 /* ArcSegment pretty much follows the SVG algorithm for converting an | |
| 501 * arc in endpoint representation to an arc in centerpoint | |
| 502 * representation. Once in centerpoint it can be given to the | |
| 503 * graphics library in the form of a postscript arc. */ | |
| 504 | |
| 505 float rotation_angle; | |
| 506 int is_large_arc, is_clockwise; | |
| 507 float point_x, point_y; | |
| 508 float size_x, size_y; | |
| 509 int is_stroked; | |
| 510 | |
| 511 char *point_att = fz_xml_att(root, "Point"); | |
| 512 char *size_att = fz_xml_att(root, "Size"); | |
| 513 char *rotation_angle_att = fz_xml_att(root, "RotationAngle"); | |
| 514 char *is_large_arc_att = fz_xml_att(root, "IsLargeArc"); | |
| 515 char *sweep_direction_att = fz_xml_att(root, "SweepDirection"); | |
| 516 char *is_stroked_att = fz_xml_att(root, "IsStroked"); | |
| 517 | |
| 518 if (!point_att || !size_att || !rotation_angle_att || !is_large_arc_att || !sweep_direction_att) | |
| 519 { | |
| 520 fz_warn(ctx, "ArcSegment element is missing attributes"); | |
| 521 return; | |
| 522 } | |
| 523 | |
| 524 is_stroked = 1; | |
| 525 if (is_stroked_att && !strcmp(is_stroked_att, "false")) | |
| 526 is_stroked = 0; | |
| 527 if (!is_stroked) | |
| 528 *skipped_stroke = 1; | |
| 529 | |
| 530 point_x = point_y = 0; | |
| 531 size_x = size_y = 0; | |
| 532 | |
| 533 xps_parse_point(ctx, doc, point_att, &point_x, &point_y); | |
| 534 xps_parse_point(ctx, doc, size_att, &size_x, &size_y); | |
| 535 rotation_angle = fz_atof(rotation_angle_att); | |
| 536 is_large_arc = !strcmp(is_large_arc_att, "true"); | |
| 537 is_clockwise = !strcmp(sweep_direction_att, "Clockwise"); | |
| 538 | |
| 539 if (stroking && !is_stroked) | |
| 540 { | |
| 541 fz_moveto(ctx, path, point_x, point_y); | |
| 542 return; | |
| 543 } | |
| 544 | |
| 545 xps_draw_arc(ctx, doc, path, size_x, size_y, rotation_angle, is_large_arc, is_clockwise, point_x, point_y); | |
| 546 } | |
| 547 | |
| 548 static void | |
| 549 xps_parse_poly_quadratic_bezier_segment(fz_context *ctx, xps_document *doc, fz_path *path, fz_xml *root, int stroking, int *skipped_stroke) | |
| 550 { | |
| 551 char *points_att = fz_xml_att(root, "Points"); | |
| 552 char *is_stroked_att = fz_xml_att(root, "IsStroked"); | |
| 553 float x[2], y[2]; | |
| 554 int is_stroked; | |
| 555 fz_point pt; | |
| 556 char *s; | |
| 557 int n; | |
| 558 | |
| 559 if (!points_att) | |
| 560 { | |
| 561 fz_warn(ctx, "PolyQuadraticBezierSegment element has no points"); | |
| 562 return; | |
| 563 } | |
| 564 | |
| 565 is_stroked = 1; | |
| 566 if (is_stroked_att && !strcmp(is_stroked_att, "false")) | |
| 567 is_stroked = 0; | |
| 568 if (!is_stroked) | |
| 569 *skipped_stroke = 1; | |
| 570 | |
| 571 s = points_att; | |
| 572 n = 0; | |
| 573 while (*s != 0) | |
| 574 { | |
| 575 while (*s == ' ') s++; | |
| 576 x[n] = y[n] = 0; | |
| 577 s = xps_parse_point(ctx, doc, s, &x[n], &y[n]); | |
| 578 n ++; | |
| 579 if (n == 2) | |
| 580 { | |
| 581 if (stroking && !is_stroked) | |
| 582 { | |
| 583 fz_moveto(ctx, path, x[1], y[1]); | |
| 584 } | |
| 585 else | |
| 586 { | |
| 587 pt = fz_currentpoint(ctx, path); | |
| 588 fz_curveto(ctx, path, | |
| 589 (pt.x + 2 * x[0]) / 3, (pt.y + 2 * y[0]) / 3, | |
| 590 (x[1] + 2 * x[0]) / 3, (y[1] + 2 * y[0]) / 3, | |
| 591 x[1], y[1]); | |
| 592 } | |
| 593 n = 0; | |
| 594 } | |
| 595 } | |
| 596 } | |
| 597 | |
| 598 static void | |
| 599 xps_parse_poly_bezier_segment(fz_context *ctx, xps_document *doc, fz_path *path, fz_xml *root, int stroking, int *skipped_stroke) | |
| 600 { | |
| 601 char *points_att = fz_xml_att(root, "Points"); | |
| 602 char *is_stroked_att = fz_xml_att(root, "IsStroked"); | |
| 603 float x[3], y[3]; | |
| 604 int is_stroked; | |
| 605 char *s; | |
| 606 int n; | |
| 607 | |
| 608 if (!points_att) | |
| 609 { | |
| 610 fz_warn(ctx, "PolyBezierSegment element has no points"); | |
| 611 return; | |
| 612 } | |
| 613 | |
| 614 is_stroked = 1; | |
| 615 if (is_stroked_att && !strcmp(is_stroked_att, "false")) | |
| 616 is_stroked = 0; | |
| 617 if (!is_stroked) | |
| 618 *skipped_stroke = 1; | |
| 619 | |
| 620 s = points_att; | |
| 621 n = 0; | |
| 622 while (*s != 0) | |
| 623 { | |
| 624 while (*s == ' ') s++; | |
| 625 x[n] = y[n] = 0; | |
| 626 s = xps_parse_point(ctx, doc, s, &x[n], &y[n]); | |
| 627 n ++; | |
| 628 if (n == 3) | |
| 629 { | |
| 630 if (stroking && !is_stroked) | |
| 631 fz_moveto(ctx, path, x[2], y[2]); | |
| 632 else | |
| 633 fz_curveto(ctx, path, x[0], y[0], x[1], y[1], x[2], y[2]); | |
| 634 n = 0; | |
| 635 } | |
| 636 } | |
| 637 } | |
| 638 | |
| 639 static void | |
| 640 xps_parse_poly_line_segment(fz_context *ctx, xps_document *doc, fz_path *path, fz_xml *root, int stroking, int *skipped_stroke) | |
| 641 { | |
| 642 char *points_att = fz_xml_att(root, "Points"); | |
| 643 char *is_stroked_att = fz_xml_att(root, "IsStroked"); | |
| 644 int is_stroked; | |
| 645 float x, y; | |
| 646 char *s; | |
| 647 | |
| 648 if (!points_att) | |
| 649 { | |
| 650 fz_warn(ctx, "PolyLineSegment element has no points"); | |
| 651 return; | |
| 652 } | |
| 653 | |
| 654 is_stroked = 1; | |
| 655 if (is_stroked_att && !strcmp(is_stroked_att, "false")) | |
| 656 is_stroked = 0; | |
| 657 if (!is_stroked) | |
| 658 *skipped_stroke = 1; | |
| 659 | |
| 660 s = points_att; | |
| 661 while (*s != 0) | |
| 662 { | |
| 663 while (*s == ' ') s++; | |
| 664 x = y = 0; | |
| 665 s = xps_parse_point(ctx, doc, s, &x, &y); | |
| 666 if (stroking && !is_stroked) | |
| 667 fz_moveto(ctx, path, x, y); | |
| 668 else | |
| 669 fz_lineto(ctx, path, x, y); | |
| 670 } | |
| 671 } | |
| 672 | |
| 673 static void | |
| 674 xps_parse_path_figure(fz_context *ctx, xps_document *doc, fz_path *path, fz_xml *root, int stroking) | |
| 675 { | |
| 676 fz_xml *node; | |
| 677 | |
| 678 char *is_closed_att; | |
| 679 char *start_point_att; | |
| 680 char *is_filled_att; | |
| 681 | |
| 682 int is_closed = 0; | |
| 683 int is_filled = 1; | |
| 684 float start_x = 0; | |
| 685 float start_y = 0; | |
| 686 | |
| 687 int skipped_stroke = 0; | |
| 688 | |
| 689 is_closed_att = fz_xml_att(root, "IsClosed"); | |
| 690 start_point_att = fz_xml_att(root, "StartPoint"); | |
| 691 is_filled_att = fz_xml_att(root, "IsFilled"); | |
| 692 | |
| 693 if (is_closed_att) | |
| 694 is_closed = !strcmp(is_closed_att, "true"); | |
| 695 if (is_filled_att) | |
| 696 is_filled = !strcmp(is_filled_att, "true"); | |
| 697 if (start_point_att) | |
| 698 xps_parse_point(ctx, doc, start_point_att, &start_x, &start_y); | |
| 699 | |
| 700 if (!stroking && !is_filled) /* not filled, when filling */ | |
| 701 return; | |
| 702 | |
| 703 fz_moveto(ctx, path, start_x, start_y); | |
| 704 | |
| 705 for (node = fz_xml_down(root); node; node = fz_xml_next(node)) | |
| 706 { | |
| 707 if (fz_xml_is_tag(node, "ArcSegment")) | |
| 708 xps_parse_arc_segment(ctx, doc, path, node, stroking, &skipped_stroke); | |
| 709 if (fz_xml_is_tag(node, "PolyBezierSegment")) | |
| 710 xps_parse_poly_bezier_segment(ctx, doc, path, node, stroking, &skipped_stroke); | |
| 711 if (fz_xml_is_tag(node, "PolyLineSegment")) | |
| 712 xps_parse_poly_line_segment(ctx, doc, path, node, stroking, &skipped_stroke); | |
| 713 if (fz_xml_is_tag(node, "PolyQuadraticBezierSegment")) | |
| 714 xps_parse_poly_quadratic_bezier_segment(ctx, doc, path, node, stroking, &skipped_stroke); | |
| 715 } | |
| 716 | |
| 717 if (is_closed) | |
| 718 { | |
| 719 if (stroking && skipped_stroke) | |
| 720 fz_lineto(ctx, path, start_x, start_y); /* we've skipped using fz_moveto... */ | |
| 721 else | |
| 722 fz_closepath(ctx, path); /* no skipped segments, safe to closepath properly */ | |
| 723 } | |
| 724 } | |
| 725 | |
| 726 fz_path * | |
| 727 xps_parse_path_geometry(fz_context *ctx, xps_document *doc, xps_resource *dict, fz_xml *root, int stroking, int *fill_rule) | |
| 728 { | |
| 729 fz_xml *node; | |
| 730 | |
| 731 char *figures_att; | |
| 732 char *fill_rule_att; | |
| 733 char *transform_att; | |
| 734 | |
| 735 fz_xml *transform_tag = NULL; | |
| 736 fz_xml *figures_tag = NULL; /* only used by resource */ | |
| 737 | |
| 738 fz_matrix transform; | |
| 739 fz_path *path; | |
| 740 | |
| 741 figures_att = fz_xml_att(root, "Figures"); | |
| 742 fill_rule_att = fz_xml_att(root, "FillRule"); | |
| 743 transform_att = fz_xml_att(root, "Transform"); | |
| 744 | |
| 745 for (node = fz_xml_down(root); node; node = fz_xml_next(node)) | |
| 746 { | |
| 747 if (fz_xml_is_tag(node, "PathGeometry.Transform")) | |
| 748 transform_tag = fz_xml_down(node); | |
| 749 } | |
| 750 | |
| 751 xps_resolve_resource_reference(ctx, doc, dict, &transform_att, &transform_tag, NULL); | |
| 752 xps_resolve_resource_reference(ctx, doc, dict, &figures_att, &figures_tag, NULL); | |
| 753 | |
| 754 if (fill_rule_att) | |
| 755 { | |
| 756 if (!strcmp(fill_rule_att, "NonZero")) | |
| 757 *fill_rule = 1; | |
| 758 if (!strcmp(fill_rule_att, "EvenOdd")) | |
| 759 *fill_rule = 0; | |
| 760 } | |
| 761 | |
| 762 transform = xps_parse_transform(ctx, doc, transform_att, transform_tag, fz_identity); | |
| 763 | |
| 764 if (figures_att) | |
| 765 path = xps_parse_abbreviated_geometry(ctx, doc, figures_att, fill_rule); | |
| 766 else | |
| 767 path = fz_new_path(ctx); | |
| 768 | |
| 769 fz_try(ctx) | |
| 770 { | |
| 771 if (figures_tag) | |
| 772 xps_parse_path_figure(ctx, doc, path, figures_tag, stroking); | |
| 773 | |
| 774 for (node = fz_xml_down(root); node; node = fz_xml_next(node)) | |
| 775 { | |
| 776 if (fz_xml_is_tag(node, "PathFigure")) | |
| 777 xps_parse_path_figure(ctx, doc, path, node, stroking); | |
| 778 } | |
| 779 | |
| 780 if (transform_att || transform_tag) | |
| 781 fz_transform_path(ctx, path, transform); | |
| 782 } | |
| 783 fz_catch(ctx) | |
| 784 { | |
| 785 fz_drop_path(ctx, path); | |
| 786 fz_rethrow(ctx); | |
| 787 } | |
| 788 | |
| 789 return path; | |
| 790 } | |
| 791 | |
| 792 static int | |
| 793 xps_parse_line_cap(char *attr) | |
| 794 { | |
| 795 if (attr) | |
| 796 { | |
| 797 if (!strcmp(attr, "Flat")) return 0; | |
| 798 if (!strcmp(attr, "Round")) return 1; | |
| 799 if (!strcmp(attr, "Square")) return 2; | |
| 800 if (!strcmp(attr, "Triangle")) return 3; | |
| 801 } | |
| 802 return 0; | |
| 803 } | |
| 804 | |
| 805 void | |
| 806 xps_clip(fz_context *ctx, xps_document *doc, fz_matrix ctm, xps_resource *dict, char *clip_att, fz_xml *clip_tag) | |
| 807 { | |
| 808 fz_device *dev = doc->dev; | |
| 809 fz_path *path; | |
| 810 int fill_rule = 0; | |
| 811 | |
| 812 if (clip_att) | |
| 813 path = xps_parse_abbreviated_geometry(ctx, doc, clip_att, &fill_rule); | |
| 814 else if (clip_tag) | |
| 815 path = xps_parse_path_geometry(ctx, doc, dict, clip_tag, 0, &fill_rule); | |
| 816 else | |
| 817 path = fz_new_path(ctx); | |
| 818 fz_try(ctx) | |
| 819 fz_clip_path(ctx, dev, path, fill_rule == 0, ctm, fz_infinite_rect); | |
| 820 fz_always(ctx) | |
| 821 fz_drop_path(ctx, path); | |
| 822 fz_catch(ctx) | |
| 823 fz_rethrow(ctx); | |
| 824 } | |
| 825 | |
| 826 void | |
| 827 xps_parse_path(fz_context *ctx, xps_document *doc, fz_matrix ctm, char *base_uri, xps_resource *dict, fz_xml *root) | |
| 828 { | |
| 829 fz_device *dev = doc->dev; | |
| 830 | |
| 831 fz_xml *node; | |
| 832 | |
| 833 char *fill_uri; | |
| 834 char *stroke_uri; | |
| 835 char *opacity_mask_uri; | |
| 836 | |
| 837 char *transform_att; | |
| 838 char *clip_att; | |
| 839 char *data_att; | |
| 840 char *fill_att; | |
| 841 char *stroke_att; | |
| 842 char *opacity_att; | |
| 843 char *opacity_mask_att; | |
| 844 | |
| 845 fz_xml *transform_tag = NULL; | |
| 846 fz_xml *clip_tag = NULL; | |
| 847 fz_xml *data_tag = NULL; | |
| 848 fz_xml *fill_tag = NULL; | |
| 849 fz_xml *stroke_tag = NULL; | |
| 850 fz_xml *opacity_mask_tag = NULL; | |
| 851 | |
| 852 char *fill_opacity_att = NULL; | |
| 853 char *stroke_opacity_att = NULL; | |
| 854 | |
| 855 char *stroke_dash_array_att; | |
| 856 char *stroke_dash_cap_att; | |
| 857 char *stroke_dash_offset_att; | |
| 858 char *stroke_end_line_cap_att; | |
| 859 char *stroke_start_line_cap_att; | |
| 860 char *stroke_line_join_att; | |
| 861 char *stroke_miter_limit_att; | |
| 862 char *stroke_thickness_att; | |
| 863 | |
| 864 fz_stroke_state *stroke = NULL; | |
| 865 float samples[FZ_MAX_COLORS]; | |
| 866 fz_colorspace *colorspace; | |
| 867 fz_path *path = NULL; | |
| 868 fz_path *stroke_path = NULL; | |
| 869 fz_rect area; | |
| 870 int fill_rule; | |
| 871 int dash_len = 0; | |
| 872 | |
| 873 /* | |
| 874 * Extract attributes and extended attributes. | |
| 875 */ | |
| 876 | |
| 877 transform_att = fz_xml_att(root, "RenderTransform"); | |
| 878 clip_att = fz_xml_att(root, "Clip"); | |
| 879 data_att = fz_xml_att(root, "Data"); | |
| 880 fill_att = fz_xml_att(root, "Fill"); | |
| 881 stroke_att = fz_xml_att(root, "Stroke"); | |
| 882 opacity_att = fz_xml_att(root, "Opacity"); | |
| 883 opacity_mask_att = fz_xml_att(root, "OpacityMask"); | |
| 884 | |
| 885 stroke_dash_array_att = fz_xml_att(root, "StrokeDashArray"); | |
| 886 stroke_dash_cap_att = fz_xml_att(root, "StrokeDashCap"); | |
| 887 stroke_dash_offset_att = fz_xml_att(root, "StrokeDashOffset"); | |
| 888 stroke_end_line_cap_att = fz_xml_att(root, "StrokeEndLineCap"); | |
| 889 stroke_start_line_cap_att = fz_xml_att(root, "StrokeStartLineCap"); | |
| 890 stroke_line_join_att = fz_xml_att(root, "StrokeLineJoin"); | |
| 891 stroke_miter_limit_att = fz_xml_att(root, "StrokeMiterLimit"); | |
| 892 stroke_thickness_att = fz_xml_att(root, "StrokeThickness"); | |
| 893 | |
| 894 for (node = fz_xml_down(root); node; node = fz_xml_next(node)) | |
| 895 { | |
| 896 if (fz_xml_is_tag(node, "Path.RenderTransform")) | |
| 897 transform_tag = fz_xml_down(node); | |
| 898 if (fz_xml_is_tag(node, "Path.OpacityMask")) | |
| 899 opacity_mask_tag = fz_xml_down(node); | |
| 900 if (fz_xml_is_tag(node, "Path.Clip")) | |
| 901 clip_tag = fz_xml_down(node); | |
| 902 if (fz_xml_is_tag(node, "Path.Fill")) | |
| 903 fill_tag = fz_xml_down(node); | |
| 904 if (fz_xml_is_tag(node, "Path.Stroke")) | |
| 905 stroke_tag = fz_xml_down(node); | |
| 906 if (fz_xml_is_tag(node, "Path.Data")) | |
| 907 data_tag = fz_xml_down(node); | |
| 908 } | |
| 909 | |
| 910 fill_uri = base_uri; | |
| 911 stroke_uri = base_uri; | |
| 912 opacity_mask_uri = base_uri; | |
| 913 | |
| 914 xps_resolve_resource_reference(ctx, doc, dict, &data_att, &data_tag, NULL); | |
| 915 xps_resolve_resource_reference(ctx, doc, dict, &clip_att, &clip_tag, NULL); | |
| 916 xps_resolve_resource_reference(ctx, doc, dict, &transform_att, &transform_tag, NULL); | |
| 917 xps_resolve_resource_reference(ctx, doc, dict, &fill_att, &fill_tag, &fill_uri); | |
| 918 xps_resolve_resource_reference(ctx, doc, dict, &stroke_att, &stroke_tag, &stroke_uri); | |
| 919 xps_resolve_resource_reference(ctx, doc, dict, &opacity_mask_att, &opacity_mask_tag, &opacity_mask_uri); | |
| 920 | |
| 921 /* | |
| 922 * Act on the information we have gathered: | |
| 923 */ | |
| 924 | |
| 925 if (!data_att && !data_tag) | |
| 926 return; | |
| 927 | |
| 928 if (fz_xml_is_tag(fill_tag, "SolidColorBrush")) | |
| 929 { | |
| 930 fill_opacity_att = fz_xml_att(fill_tag, "Opacity"); | |
| 931 fill_att = fz_xml_att(fill_tag, "Color"); | |
| 932 fill_tag = NULL; | |
| 933 } | |
| 934 | |
| 935 if (fz_xml_is_tag(stroke_tag, "SolidColorBrush")) | |
| 936 { | |
| 937 stroke_opacity_att = fz_xml_att(stroke_tag, "Opacity"); | |
| 938 stroke_att = fz_xml_att(stroke_tag, "Color"); | |
| 939 stroke_tag = NULL; | |
| 940 } | |
| 941 | |
| 942 if (stroke_att || stroke_tag) | |
| 943 { | |
| 944 if (stroke_dash_array_att) | |
| 945 { | |
| 946 char *s = stroke_dash_array_att; | |
| 947 | |
| 948 while (*s) | |
| 949 { | |
| 950 while (*s == ' ') | |
| 951 s++; | |
| 952 if (*s) /* needed in case of a space before the last quote */ | |
| 953 dash_len++; | |
| 954 | |
| 955 while (*s && *s != ' ') | |
| 956 s++; | |
| 957 } | |
| 958 } | |
| 959 stroke = fz_new_stroke_state_with_dash_len(ctx, dash_len); | |
| 960 stroke->start_cap = xps_parse_line_cap(stroke_start_line_cap_att); | |
| 961 stroke->dash_cap = xps_parse_line_cap(stroke_dash_cap_att); | |
| 962 stroke->end_cap = xps_parse_line_cap(stroke_end_line_cap_att); | |
| 963 | |
| 964 stroke->linejoin = FZ_LINEJOIN_MITER_XPS; | |
| 965 if (stroke_line_join_att) | |
| 966 { | |
| 967 if (!strcmp(stroke_line_join_att, "Miter")) stroke->linejoin = FZ_LINEJOIN_MITER_XPS; | |
| 968 if (!strcmp(stroke_line_join_att, "Round")) stroke->linejoin = FZ_LINEJOIN_ROUND; | |
| 969 if (!strcmp(stroke_line_join_att, "Bevel")) stroke->linejoin = FZ_LINEJOIN_BEVEL; | |
| 970 } | |
| 971 | |
| 972 stroke->miterlimit = 10; | |
| 973 if (stroke_miter_limit_att) | |
| 974 stroke->miterlimit = fz_atof(stroke_miter_limit_att); | |
| 975 | |
| 976 stroke->linewidth = 1; | |
| 977 if (stroke_thickness_att) | |
| 978 stroke->linewidth = fz_atof(stroke_thickness_att); | |
| 979 | |
| 980 stroke->dash_phase = 0; | |
| 981 stroke->dash_len = 0; | |
| 982 if (stroke_dash_array_att) | |
| 983 { | |
| 984 char *s = stroke_dash_array_att; | |
| 985 | |
| 986 if (stroke_dash_offset_att) | |
| 987 stroke->dash_phase = fz_atof(stroke_dash_offset_att) * stroke->linewidth; | |
| 988 | |
| 989 while (*s) | |
| 990 { | |
| 991 while (*s == ' ') | |
| 992 s++; | |
| 993 if (*s) /* needed in case of a space before the last quote */ | |
| 994 stroke->dash_list[stroke->dash_len++] = fz_atof(s) * stroke->linewidth; | |
| 995 while (*s && *s != ' ') | |
| 996 s++; | |
| 997 } | |
| 998 if (dash_len > 0) | |
| 999 { | |
| 1000 /* fz_stroke_path doesn't draw non-empty paths with phase length zero */ | |
| 1001 float phase_len = 0; | |
| 1002 int i; | |
| 1003 for (i = 0; i < dash_len; i++) | |
| 1004 phase_len += stroke->dash_list[i]; | |
| 1005 if (phase_len == 0) | |
| 1006 dash_len = 0; | |
| 1007 } | |
| 1008 stroke->dash_len = dash_len; | |
| 1009 } | |
| 1010 } | |
| 1011 | |
| 1012 ctm = xps_parse_transform(ctx, doc, transform_att, transform_tag, ctm); | |
| 1013 | |
| 1014 if (clip_att || clip_tag) | |
| 1015 xps_clip(ctx, doc, ctm, dict, clip_att, clip_tag); | |
| 1016 | |
| 1017 fz_try(ctx) | |
| 1018 { | |
| 1019 fill_rule = 0; | |
| 1020 if (data_att) | |
| 1021 path = xps_parse_abbreviated_geometry(ctx, doc, data_att, &fill_rule); | |
| 1022 else if (data_tag) | |
| 1023 { | |
| 1024 path = xps_parse_path_geometry(ctx, doc, dict, data_tag, 0, &fill_rule); | |
| 1025 // /home/sebras/src/jxr/fts_06xx.xps | |
| 1026 if (stroke_att || stroke_tag) | |
| 1027 stroke_path = xps_parse_path_geometry(ctx, doc, dict, data_tag, 1, &fill_rule); | |
| 1028 } | |
| 1029 if (!stroke_path) | |
| 1030 stroke_path = path; | |
| 1031 | |
| 1032 if (stroke_att || stroke_tag) | |
| 1033 { | |
| 1034 area = fz_bound_path(ctx, stroke_path, stroke, ctm); | |
| 1035 if (stroke_path != path && (fill_att || fill_tag)) { | |
| 1036 fz_rect bounds = fz_bound_path(ctx, path, NULL, ctm); | |
| 1037 area = fz_union_rect(area, bounds); | |
| 1038 } | |
| 1039 } | |
| 1040 else | |
| 1041 area = fz_bound_path(ctx, path, NULL, ctm); | |
| 1042 | |
| 1043 xps_begin_opacity(ctx, doc, ctm, area, opacity_mask_uri, dict, opacity_att, opacity_mask_tag); | |
| 1044 | |
| 1045 if (fill_att) | |
| 1046 { | |
| 1047 xps_parse_color(ctx, doc, base_uri, fill_att, &colorspace, samples); | |
| 1048 if (fill_opacity_att) | |
| 1049 samples[0] *= fz_atof(fill_opacity_att); | |
| 1050 xps_set_color(ctx, doc, colorspace, samples); | |
| 1051 fz_fill_path(ctx, dev, path, fill_rule == 0, ctm, | |
| 1052 doc->colorspace, doc->color, doc->alpha, fz_default_color_params); | |
| 1053 } | |
| 1054 | |
| 1055 if (fill_tag) | |
| 1056 { | |
| 1057 fz_clip_path(ctx, dev, path, fill_rule == 0, ctm, area); | |
| 1058 xps_parse_brush(ctx, doc, ctm, area, fill_uri, dict, fill_tag); | |
| 1059 fz_pop_clip(ctx, dev); | |
| 1060 } | |
| 1061 | |
| 1062 if (stroke_att) | |
| 1063 { | |
| 1064 xps_parse_color(ctx, doc, base_uri, stroke_att, &colorspace, samples); | |
| 1065 if (stroke_opacity_att) | |
| 1066 samples[0] *= fz_atof(stroke_opacity_att); | |
| 1067 xps_set_color(ctx, doc, colorspace, samples); | |
| 1068 fz_stroke_path(ctx, dev, stroke_path, stroke, ctm, | |
| 1069 doc->colorspace, doc->color, doc->alpha, fz_default_color_params); | |
| 1070 } | |
| 1071 | |
| 1072 if (stroke_tag) | |
| 1073 { | |
| 1074 fz_clip_stroke_path(ctx, dev, stroke_path, stroke, ctm, area); | |
| 1075 xps_parse_brush(ctx, doc, ctm, area, stroke_uri, dict, stroke_tag); | |
| 1076 fz_pop_clip(ctx, dev); | |
| 1077 } | |
| 1078 | |
| 1079 xps_end_opacity(ctx, doc, opacity_mask_uri, dict, opacity_att, opacity_mask_tag); | |
| 1080 } | |
| 1081 fz_always(ctx) | |
| 1082 { | |
| 1083 if (stroke_path != path) | |
| 1084 fz_drop_path(ctx, stroke_path); | |
| 1085 fz_drop_path(ctx, path); | |
| 1086 fz_drop_stroke_state(ctx, stroke); | |
| 1087 } | |
| 1088 fz_catch(ctx) | |
| 1089 fz_rethrow(ctx); | |
| 1090 | |
| 1091 if (clip_att || clip_tag) | |
| 1092 fz_pop_clip(ctx, dev); | |
| 1093 } |
