Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

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