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