1 /* This is dvipdfmx, an eXtended version of dvipdfm by Mark A. Wicks.
2
3 Copyright (C) 2002-2015 by Jin-Hwan Cho and Shunsaku Hirata,
4 the dvipdfmx project team.
5
6 Copyright (C) 1998, 1999 by Mark A. Wicks <mwicks@kettering.edu>
7
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
21 */
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <math.h>
28
29 #include "system.h"
30 #include "error.h"
31 #include "mem.h"
32 #include "mfileio.h"
33 #include "dpxutil.h"
34 #include "numbers.h"
35
36 #include "pdfdoc.h"
37 #include "pdfdev.h"
38 #include "pdfcolor.h"
39
40 #include "pdfdraw.h"
41
42
43 /*
44 * Numbers are rounding to 0-5 fractional digits
45 * in output routine.
46 */
47 #define detM(M) ((M).a * (M).d - (M).b * (M).c)
48 #define detP(M) ((M)->a * (M)->d - (M)->b * (M)->c)
49
50
51 static /* __inline__ */ int
inversematrix(pdf_tmatrix * W,const pdf_tmatrix * M)52 inversematrix (pdf_tmatrix *W, const pdf_tmatrix *M)
53 {
54 double det;
55
56 det = detP(M);
57 if (fabs(det) < 1.e-8) {
58 WARN("Inverting matrix with zero determinant...");
59 return -1; /* result is undefined. */
60 }
61
62 W->a = (M->d) / det; W->b = -(M->b) / det;
63 W->c = -(M->c) / det; W->d = (M->a) / det;
64 W->e = (M->c) * (M->f) - (M->d) * (M->e);
65 W->f = (M->b) * (M->e) - (M->a) * (M->f);
66
67 return 0;
68 }
69
70 /* pdf_coord as vector */
71 #define vecprd(v,w) ((v).x * (w).x + (v).y * (w).y)
72 #define vecrot(v,w) ((v).x * (w).y - (v).y * (w).x)
73 #define dsign(v) (((v) >= 0.0) ? 1.0 : -1.0)
74 /* acos => [0, pi] */
75 #define vecang(v,w) ( \
76 dsign(vecrot((v),(w))) * \
77 acos(vecprd((v),(w)) / sqrt(vecprd((v),(v)) * vecprd((w),(w)))) \
78 )
79
80 static /* __inline__ */ int
pdf_coord__equal(const pdf_coord * p1,const pdf_coord * p2)81 pdf_coord__equal (const pdf_coord *p1, const pdf_coord *p2)
82 {
83 if (fabs(p1->x - p2->x) < 1.e-7 &&
84 fabs(p1->y - p2->y) < 1.e-7)
85 return 1;
86 return 0;
87 }
88 #define COORD_EQUAL(p,q) pdf_coord__equal((p),(q))
89
90 #if 0
91 static int
92 pdf_coord__sort_compar_X (const void *pp1, const void *pp2)
93 {
94 pdf_coord *p1 = (pdf_coord *)pp1;
95 pdf_coord *p2 = (pdf_coord *)pp2;
96
97 if (pdf_coord__equal(p1, p2))
98 return 0;
99 else
100 return (int) dsign(p1->x - p2->x);
101
102 return 1;
103 }
104
105 static int
106 pdf_coord__sort_compar_Y (const void *pp1, const void *pp2)
107 {
108 pdf_coord *p1 = (pdf_coord *)pp1;
109 pdf_coord *p2 = (pdf_coord *)pp2;
110
111 if (pdf_coord__equal(p1, p2))
112 return 0;
113 else
114 return (int) dsign(p1->y - p2->y);
115
116 return 1;
117 }
118 #endif
119
120
121 static /* __inline__ */ int
pdf_coord__transform(pdf_coord * p,const pdf_tmatrix * M)122 pdf_coord__transform (pdf_coord *p, const pdf_tmatrix *M)
123 {
124 double x, y;
125
126 x = p->x; y = p->y;
127 p->x = x * M->a + y * M->c + M->e;
128 p->y = x * M->b + y * M->d + M->f;
129
130 return 0;
131 }
132
133 #if 0
134 static /* __inline__ */ int
135 pdf_coord__itransform (pdf_coord *p, const pdf_tmatrix *M)
136 {
137 pdf_tmatrix W;
138 double x, y;
139 int error;
140
141 error = inversematrix(&W, M);
142 if (error)
143 return error;
144
145 x = p->x; y = p->y;
146 p->x = x * W.a + y * W.c + W.e;
147 p->y = x * W.b + y * W.d + W.f;
148
149 return 0;
150 }
151 #endif
152
153 static /* __inline__ */ int
pdf_coord__dtransform(pdf_coord * p,const pdf_tmatrix * M)154 pdf_coord__dtransform (pdf_coord *p, const pdf_tmatrix *M)
155 {
156 double x, y;
157
158 x = p->x; y = p->y;
159 p->x = x * M->a + y * M->c;
160 p->y = x * M->b + y * M->d;
161
162 return 0;
163 }
164
165 static /* __inline__ */ int
pdf_coord__idtransform(pdf_coord * p,const pdf_tmatrix * M)166 pdf_coord__idtransform (pdf_coord *p, const pdf_tmatrix *M)
167 {
168 pdf_tmatrix W;
169 double x, y;
170 int error;
171
172 error = inversematrix(&W, M);
173 if (error)
174 return error;
175
176 x = p->x; y = p->y;
177 p->x = x * W.a + y * W.c;
178 p->y = x * W.b + y * W.d;
179
180 return 0;
181 }
182
183
184 /* Modify M itself */
185 void
pdf_invertmatrix(pdf_tmatrix * M)186 pdf_invertmatrix (pdf_tmatrix *M)
187 {
188 pdf_tmatrix W;
189 double det;
190
191 ASSERT(M);
192
193 det = detP(M);
194 if (fabs(det) < 1.e-8) {
195 WARN("Inverting matrix with zero determinant...");
196 W.a = 1.0; W.c = 0.0;
197 W.b = 0.0; W.d = 1.0;
198 W.e = 0.0; W.f = 0.0;
199 } else {
200 W.a = (M->d) / det; W.b = -(M->b) / det;
201 W.c = -(M->c) / det; W.d = (M->a) / det;
202 W.e = (M->c) * (M->f) - (M->d) * (M->e);
203 W.f = (M->b) * (M->e) - (M->a) * (M->f);
204 W.e /= det; W.f /= det;
205 }
206
207 pdf_copymatrix(M, &W);
208
209 return;
210 }
211
212
213 typedef struct pa_elem_
214 {
215 int type;
216 pdf_coord p[3];
217 } pa_elem;
218
219 /* each subpath delimitted by moveto */
220 struct pdf_path_
221 {
222 int num_paths;
223 int max_paths;
224 pa_elem *path;
225 };
226
227 static const struct {
228 char opchr; /* PDF operator char */
229 int n_pts; /* number of *points* */
230 const char *strkey;
231 } petypes[] = {
232 #define PE_TYPE__INVALID -1
233 #define PE_TYPE__MOVETO 0
234 {'m', 1, "moveto" },
235 #define PE_TYPE__LINETO 1
236 {'l', 1, "lineto" },
237 #define PE_TYPE__CURVETO 2
238 {'c', 3, "curveto" },
239 /* no PS correspondence for v and y */
240 #define PE_TYPE__CURVETO_V 3
241 {'v', 2, "vcurveto"}, /* current point replicated */
242 #define PE_TYPE__CURVETO_Y 4
243 {'y', 2, "ycurveto"}, /* last point replicated */
244 #define PE_TYPE__CLOSEPATH 5
245 {'h', 0, "closepath"},
246 #define PE_TYPE__TERMINATE 6
247 {' ', 0, NULL}
248 };
249
250 #define PE_VALID(p) ((p) && \
251 (p)->type > PE_TYPE__INVALID && (p)->type < PE_TYPE__TERMINATE)
252 #define PE_N_PTS(p) (PE_VALID((p)) ? petypes[(p)->type].n_pts : 0)
253 #define PE_OPCHR(p) (PE_VALID((p)) ? petypes[(p)->type].opchr : ' ')
254
255 #define PA_LENGTH(pa) ((pa)->num_paths)
256
257 #define GS_FLAG_CURRENTPOINT_SET (1 << 0)
258
259
260 #define FORMAT_BUFF_LEN 1024
261 static char fmt_buf[FORMAT_BUFF_LEN];
262
263 static void
init_a_path(pdf_path * p)264 init_a_path (pdf_path *p)
265 {
266 ASSERT(p);
267
268 p->num_paths = 0;
269 p->max_paths = 0;
270 p->path = NULL;
271
272 return;
273 }
274
275 static void
pdf_path__clearpath(pdf_path * p)276 pdf_path__clearpath (pdf_path *p)
277 {
278 ASSERT(p);
279
280 p->num_paths = 0;
281
282 return;
283 }
284
285 static int
pdf_path__growpath(pdf_path * p,int max_pe)286 pdf_path__growpath (pdf_path *p, int max_pe)
287 {
288 if (max_pe < p->max_paths)
289 return 0;
290
291 p->max_paths = MAX(p->max_paths + 8, max_pe);
292 p->path = RENEW(p->path, p->max_paths, pa_elem);
293
294 return 0;
295 }
296
297 static void
clear_a_path(pdf_path * p)298 clear_a_path (pdf_path *p)
299 {
300 ASSERT(p);
301
302 if (p->path)
303 RELEASE(p->path);
304 p->path = NULL;
305 p->num_paths = 0;
306 p->max_paths = 0;
307
308 return;
309 }
310
311 static int
pdf_path__copypath(pdf_path * p1,const pdf_path * p0)312 pdf_path__copypath (pdf_path *p1, const pdf_path *p0)
313 {
314 pa_elem *pe0, *pe1;
315 int i;
316
317 pdf_path__growpath(p1, PA_LENGTH(p0));
318 for (i = 0; i < PA_LENGTH(p0); i++) {
319 pe1 = &(p1->path[i]);
320 pe0 = &(p0->path[i]);
321 /* FIXME */
322 pe1->type = pe0->type;
323 pe1->p[0].x = pe0->p[0].x;
324 pe1->p[0].y = pe0->p[0].y;
325 pe1->p[1].x = pe0->p[1].x;
326 pe1->p[1].y = pe0->p[1].y;
327 pe1->p[2].x = pe0->p[2].x;
328 pe1->p[2].y = pe0->p[2].y;
329 }
330 p1->num_paths = PA_LENGTH(p0);
331
332 return 0;
333 }
334
335
336 /* start new subpath */
337 static int
pdf_path__moveto(pdf_path * pa,pdf_coord * cp,const pdf_coord * p0)338 pdf_path__moveto (pdf_path *pa,
339 pdf_coord *cp,
340 const pdf_coord *p0)
341 {
342 pa_elem *pe;
343
344 pdf_path__growpath(pa, PA_LENGTH(pa) + 1);
345 if (PA_LENGTH(pa) > 0) {
346 pe = &pa->path[pa->num_paths-1];
347 if (pe->type == PE_TYPE__MOVETO) {
348 pe->p[0].x = cp->x = p0->x;
349 pe->p[0].y = cp->y = p0->y;
350 return 0;
351 }
352 }
353 pe = &pa->path[pa->num_paths++];
354 pe->type = PE_TYPE__MOVETO;
355 pe->p[0].x = cp->x = p0->x;
356 pe->p[0].y = cp->y = p0->y;
357
358 return 0;
359 }
360
361 /* Do 'compression' of path while adding new path elements.
362 * Sequantial moveto command will be replaced with a
363 * single moveto. If cp is not equal to the last point in pa,
364 * then moveto is inserted (starting new subpath).
365 * FIXME:
366 * 'moveto' must be used to enforce starting new path.
367 * This affects how 'closepath' is treated.
368 */
369 static pa_elem *
pdf_path__next_pe(pdf_path * pa,const pdf_coord * cp)370 pdf_path__next_pe (pdf_path *pa, const pdf_coord *cp)
371 {
372 pa_elem *pe;
373
374 pdf_path__growpath(pa, PA_LENGTH(pa) + 2);
375 if (PA_LENGTH(pa) == 0) {
376 pe = &pa->path[pa->num_paths++];
377 pe->type = PE_TYPE__MOVETO;
378 pe->p[0].x = cp->x;
379 pe->p[0].y = cp->y;
380
381 return &pa->path[pa->num_paths++];
382 }
383
384 pe = &pa->path[pa->num_paths-1];
385 switch (pe->type) {
386 case PE_TYPE__MOVETO:
387 pe->p[0].x = cp->x;
388 pe->p[0].y = cp->y;
389 break;
390 case PE_TYPE__LINETO:
391 if (!COORD_EQUAL(&pe->p[0], cp)) {
392 pe = &pa->path[pa->num_paths++];
393 pe->type = PE_TYPE__MOVETO;
394 pe->p[0].x = cp->x;
395 pe->p[0].y = cp->y;
396 }
397 break;
398 case PE_TYPE__CURVETO:
399 if (!COORD_EQUAL(&pe->p[2], cp)) {
400 pe = &pa->path[pa->num_paths++];
401 pe->type = PE_TYPE__MOVETO;
402 pe->p[0].x = cp->x;
403 pe->p[0].y = cp->y;
404 }
405 break;
406 case PE_TYPE__CURVETO_Y:
407 case PE_TYPE__CURVETO_V:
408 if (!COORD_EQUAL(&pe->p[1], cp)) {
409 pe = &pa->path[pa->num_paths++];
410 pe->type = PE_TYPE__MOVETO;
411 pe->p[0].x = cp->x;
412 pe->p[0].y = cp->y;
413 }
414 break;
415 case PE_TYPE__CLOSEPATH:
416 pe = &pa->path[pa->num_paths++];
417 pe->type = PE_TYPE__MOVETO;
418 pe->p[0].x = cp->x;
419 pe->p[0].y = cp->y;
420 break;
421 }
422
423 return &pa->path[pa->num_paths++];
424 }
425
426 static int
pdf_path__transform(pdf_path * pa,const pdf_tmatrix * M)427 pdf_path__transform (pdf_path *pa, const pdf_tmatrix *M)
428 {
429 pa_elem *pe;
430 int n = 0, i;
431
432 ASSERT(pa && M);
433
434 for (i = 0; i < PA_LENGTH(pa); i++) {
435 pe = &(pa->path[i]);
436 n = PE_N_PTS(pe);
437 while (n-- > 0)
438 pdf_coord__transform(&(pe->p[n]), M);
439 }
440
441 return 0;
442 }
443
444
445 /* Path Construction */
446 static int
pdf_path__lineto(pdf_path * pa,pdf_coord * cp,const pdf_coord * p0)447 pdf_path__lineto (pdf_path *pa,
448 pdf_coord *cp,
449 const pdf_coord *p0)
450 {
451 pa_elem *pe;
452
453 pe = pdf_path__next_pe(pa, cp);
454 pe->type = PE_TYPE__LINETO;
455 pe->p[0].x = cp->x = p0->x;
456 pe->p[0].y = cp->y = p0->y;
457
458 return 0;
459 }
460
461 static int
pdf_path__curveto(pdf_path * pa,pdf_coord * cp,const pdf_coord * p0,const pdf_coord * p1,const pdf_coord * p2)462 pdf_path__curveto (pdf_path *pa,
463 pdf_coord *cp,
464 const pdf_coord *p0,
465 const pdf_coord *p1,
466 const pdf_coord *p2
467 )
468 {
469 pa_elem *pe;
470
471 pe = pdf_path__next_pe(pa, cp);
472 if (COORD_EQUAL(cp, p0)) {
473 pe->type = PE_TYPE__CURVETO_V;
474 pe->p[0].x = p1->x;
475 pe->p[0].y = p1->y;
476 pe->p[1].x = cp->x = p2->x;
477 pe->p[1].y = cp->y = p2->y;
478 } else if (COORD_EQUAL(p1, p2)) {
479 pe->type = PE_TYPE__CURVETO_Y;
480 pe->p[0].x = p0->x;
481 pe->p[0].y = p0->y;
482 pe->p[1].x = cp->x = p1->x;
483 pe->p[1].y = cp->y = p1->y;
484 } else {
485 pe->type = PE_TYPE__CURVETO;
486 pe->p[0].x = p0->x;
487 pe->p[0].y = p0->y;
488 pe->p[1].x = p1->x;
489 pe->p[1].y = p1->y;
490 pe->p[2].x = cp->x = p2->x;
491 pe->p[2].y = cp->y = p2->y;
492 }
493
494 return 0;
495 }
496
497 #if 0
498 #define QB_TWO_THIRD (2.0/3.0)
499 #define QB_ONE_THIRD (1.0/3.0)
500
501 static int
502 pdf_path__curveto_QB (pdf_path *pa,
503 pdf_coord *cp,
504 const pdf_coord *p0,
505 const pdf_coord *p1
506 )
507 {
508 pdf_coord q0, q1;
509
510 q0.x = cp->x + QB_TWO_THIRD * (p0->x - cp->x);
511 q0.y = cp->y + QB_TWO_THIRD * (p0->y - cp->y);
512 q1.x = p0->x + QB_ONE_THIRD * (p1->x - p0->x);
513 q1.y = p0->y + QB_ONE_THIRD * (p1->y - p0->y);
514 /* q2 == p1 */
515
516 return pdf_path__curveto(pa, cp, &q0, &q1, p1);
517 }
518 #endif
519
520
521 /* This isn't specified as cp to somewhere. */
522 static int
pdf_path__elliptarc(pdf_path * pa,pdf_coord * cp,const pdf_coord * ca,double r_x,double r_y,double xar,double a_0,double a_1,int a_d)523 pdf_path__elliptarc (pdf_path *pa,
524 pdf_coord *cp,
525 const pdf_coord *ca, /* ellipsis center */
526 double r_x, /* x radius */
527 double r_y, /* y radius */
528 double xar, /* x-axis-rotation (deg!) */
529 double a_0, /* start angle */
530 double a_1, /* stop angle */
531 int a_d /* arc orientation */
532 )
533 {
534 double b, b_x, b_y;
535 double d_a, q;
536 pdf_coord p0, p1, p2, p3;
537 pdf_coord e0, e1;
538 pdf_tmatrix T;
539 int n_c; /* number of segments */
540 int i, error = 0;
541
542 if (fabs(r_x) < 1.e-8 ||
543 fabs(r_y) < 1.e-8)
544 return -1;
545
546 if (a_d < 0) {
547 for ( ; a_1 > a_0; a_1 -= 360.0);
548 } else {
549 for ( ; a_1 < a_0; a_0 -= 360.0);
550 }
551
552 d_a = a_1 - a_0;
553 for (n_c = 1; fabs(d_a) > 90.0 * n_c; n_c++);
554 d_a /= n_c;
555 if (fabs(d_a) < 1.e-8)
556 return -1;
557
558 a_0 *= M_PI / 180.0;
559 a_1 *= M_PI / 180.0;
560 d_a *= M_PI / 180.0;
561 xar *= M_PI / 180.0;
562 T.a = cos(xar); T.c = -sin(xar);
563 T.b = -T.c ; T.d = T.a;
564 T.e = 0.0 ; T.f = 0.0;
565
566 /* A parameter that controls cb-curve (off-curve) points */
567 b = 4.0 * (1.0 - cos(.5 * d_a)) / (3.0 * sin(.5 * d_a));
568 b_x = r_x * b;
569 b_y = r_y * b;
570
571 p0.x = r_x * cos(a_0);
572 p0.y = r_y * sin(a_0);
573 pdf_coord__transform(&p0, &T);
574 p0.x += ca->x; p0.y += ca->y;
575 if (PA_LENGTH(pa) == 0) {
576 pdf_path__moveto(pa, cp, &p0);
577 } else if (!COORD_EQUAL(cp, &p0)) {
578 pdf_path__lineto(pa, cp, &p0); /* add line seg */
579 }
580 for (i = 0; !error && i < n_c; i++) {
581 q = a_0 + i * d_a;
582 e0.x = cos(q); e0.y = sin(q);
583 e1.x = cos(q + d_a); e1.y = sin(q + d_a);
584
585 /* Condition for tangent vector requirs
586 * d1 = p1 - p0 = f ( sin a, -cos a)
587 * d2 = p2 - p3 = g ( sin b, -cos b)
588 * and from symmetry
589 * g^2 = f^2
590 */
591 p0.x = r_x * e0.x; /* s.p. */
592 p0.y = r_y * e0.y;
593 p3.x = r_x * e1.x; /* e.p. */
594 p3.y = r_y * e1.y;
595
596 p1.x = -b_x * e0.y;
597 p1.y = b_y * e0.x;
598 p2.x = b_x * e1.y;
599 p2.y = -b_y * e1.x;
600
601 pdf_coord__transform(&p0, &T);
602 pdf_coord__transform(&p1, &T);
603 pdf_coord__transform(&p2, &T);
604 pdf_coord__transform(&p3, &T);
605 p0.x += ca->x; p0.y += ca->y;
606 p3.x += ca->x; p3.y += ca->y;
607 p1.x += p0.x ; p1.y += p0.y ;
608 p2.x += p3.x ; p2.y += p3.y ;
609
610 error = pdf_path__curveto(pa, &p0, &p1, &p2, &p3);
611 cp->x = p3.x; cp->y = p3.y;
612 }
613
614 return error;
615 }
616
617 static int
pdf_path__closepath(pdf_path * pa,pdf_coord * cp)618 pdf_path__closepath (pdf_path *pa, pdf_coord *cp /* no arg */)
619 {
620 pa_elem *pe = NULL;
621 int i;
622
623 /* search for start point of the last subpath */
624 for (i = PA_LENGTH(pa) - 1; i >= 0; i--) {
625 pe = &pa->path[i];
626 if (pe->type == PE_TYPE__MOVETO)
627 break;
628 }
629
630 if (!pe || i < 0)
631 return -1; /* No path or no start point(!) */
632
633 cp->x = pe->p[0].x;
634 cp->y = pe->p[0].y;
635
636 pdf_path__growpath(pa, PA_LENGTH(pa) + 1);
637
638 /* NOTE:
639 * Manually closed path without closepath is not
640 * affected by linejoin. A path with coincidental
641 * starting and ending point is not the same as
642 * 'closed' path.
643 */
644 pe = &pa->path[pa->num_paths++];
645 pe->type = PE_TYPE__CLOSEPATH;
646
647 return 0;
648 }
649
650 /*
651 * x y width height re
652 *
653 * is equivalent to
654 *
655 * x y m
656 * (x + width) y l
657 * (x + width) (y + height) l
658 * x (y + height) l
659 * h
660 */
661 /* Just for quick test */
662 static /* __inline__ */ int
pdf_path__isarect(pdf_path * pa,int f_ir)663 pdf_path__isarect (pdf_path *pa,
664 int f_ir /* fill-rule is ignorable */
665 )
666 {
667 pa_elem *pe0, *pe1, *pe2, *pe3, *pe4;
668
669 if (PA_LENGTH(pa) == 5) {
670 pe0 = &(pa->path[0]);
671 pe1 = &(pa->path[1]);
672 pe2 = &(pa->path[2]);
673 pe3 = &(pa->path[3]);
674 pe4 = &(pa->path[4]);
675 if (pe0->type == PE_TYPE__MOVETO &&
676 pe1->type == PE_TYPE__LINETO &&
677 pe2->type == PE_TYPE__LINETO &&
678 pe3->type == PE_TYPE__LINETO &&
679 pe4->type == PE_TYPE__CLOSEPATH) {
680 if (pe1->p[0].y - pe0->p[0].y == 0 &&
681 pe2->p[0].x - pe1->p[0].x == 0 &&
682 pe3->p[0].y - pe2->p[0].y == 0) {
683 if (pe1->p[0].x - pe0->p[0].x
684 == pe2->p[0].x - pe3->p[0].x) {
685 return 1;
686 }
687 /* Winding number is different but ignore it here. */
688 } else if (f_ir &&
689 pe1->p[0].x - pe0->p[0].x == 0 &&
690 pe2->p[0].y - pe1->p[0].y == 0 &&
691 pe3->p[0].x - pe2->p[0].x == 0) {
692 if (pe1->p[0].y - pe0->p[0].y
693 == pe2->p[0].y - pe3->p[0].y) {
694 return 1;
695 }
696 }
697 }
698 }
699
700 return 0;
701 }
702
703 /* Path Painting */
704 /* F is obsoleted */
705 #define PT_OP_VALID(c) ( \
706 (c) == 'f' || (c) == 'F' || \
707 (c) == 's' || (c) == 'S' || \
708 (c) == 'b' || (c) == 'B' || \
709 (c) == 'W' || (c) == ' ' \
710 )
711
712 static /* __inline__ */ int
INVERTIBLE_MATRIX(const pdf_tmatrix * M)713 INVERTIBLE_MATRIX (const pdf_tmatrix *M)
714 {
715 if (fabs(detP(M)) < 1.e-8) {
716 WARN("Transformation matrix not invertible.");
717 WARN("--- M = [%g %g %g %g %g %g]",
718 M->a, M->b, M->c, M->d, M->e, M->f);
719 return -1;
720 }
721 return 0;
722 }
723
724 /* rectfill, rectstroke, rectclip, recteoclip
725 *
726 * Draw isolated rectangle without actually doing
727 * gsave/grestore operation.
728 *
729 * TODO:
730 * linestyle, fill-opacity, stroke-opacity,....
731 * As this routine draw a single graphics object
732 * each time, there should be options for specifying
733 * various drawing styles, which might inherite
734 * current graphcs state parameter.
735 */
736 static int
pdf_dev__rectshape(const pdf_rect * r,const pdf_tmatrix * M,char opchr)737 pdf_dev__rectshape (const pdf_rect *r,
738 const pdf_tmatrix *M,
739 char opchr
740 )
741 {
742 char *buf = fmt_buf;
743 int len = 0;
744 int isclip = 0;
745 pdf_coord p;
746 double wd, ht;
747
748 ASSERT(r && PT_OP_VALID(opchr));
749
750 isclip = (opchr == 'W' || opchr == ' ') ? 1 : 0;
751
752 /* disallow matrix for clipping.
753 * q ... clip Q does nothing and
754 * n M cm ... clip n alter CTM.
755 */
756 if (M && (isclip ||
757 !INVERTIBLE_MATRIX(M)))
758 return -1;
759
760 graphics_mode();
761
762 buf[len++] = ' ';
763 if (!isclip) {
764 buf[len++] = 'q';
765 if (M) {
766 buf[len++] = ' ';
767 len += pdf_sprint_matrix(buf + len, M);
768 buf[len++] = ' ';
769 buf[len++] = 'c'; buf[len++] = 'm';
770 }
771 buf[len++] = ' ';
772 }
773 buf[len++] = 'n';
774
775 p.x = r->llx; p.y = r->lly;
776 wd = r->urx - r->llx;
777 ht = r->ury - r->lly;
778 buf[len++] = ' ';
779 len += pdf_sprint_coord (buf + len, &p);
780 buf[len++] = ' ';
781 len += pdf_sprint_length(buf + len, wd);
782 buf[len++] = ' ';
783 len += pdf_sprint_length(buf + len, ht);
784 buf[len++] = ' ';
785 buf[len++] = 'r'; buf[len++] = 'e';
786
787 if (opchr != ' ') {
788 buf[len++] = ' ';
789 buf[len++] = opchr;
790
791 buf[len++] = ' ';
792 buf[len++] = isclip ? 'n' : 'Q';
793 }
794
795 pdf_doc_add_page_content(buf, len); /* op: q cm n re Q */
796
797 return 0;
798 }
799
800 static int path_added = 0;
801
802 /* FIXME */
803 static int
pdf_dev__flushpath(pdf_path * pa,char opchr,int rule,int ignore_rule)804 pdf_dev__flushpath (pdf_path *pa,
805 char opchr,
806 int rule,
807 int ignore_rule)
808 {
809 pa_elem *pe, *pe1;
810 char *b = fmt_buf;
811 long b_len = FORMAT_BUFF_LEN;
812 pdf_rect r; /* FIXME */
813 pdf_coord *pt;
814 int n_pts, n_seg;
815 int len = 0;
816 int isclip = 0;
817 int isrect, i, j;
818
819 ASSERT(pa && PT_OP_VALID(opchr));
820
821 isclip = (opchr == 'W') ? 1 : 0;
822
823 if (PA_LENGTH(pa) <= 0 && path_added == 0)
824 return 0;
825
826 path_added = 0;
827 graphics_mode();
828 isrect = pdf_path__isarect(pa, ignore_rule);
829 if (isrect) {
830 pe = &(pa->path[0]);
831 pe1 = &(pa->path[2]);
832
833 r.llx = pe->p[0].x;
834 r.lly = pe->p[0].y;
835 r.urx = pe1->p[0].x - pe->p[0].x; /* width... */
836 r.ury = pe1->p[0].y - pe->p[0].y; /* height... */
837
838 b[len++] = ' ';
839 len += pdf_sprint_rect(b + len, &r);
840 b[len++] = ' ';
841 b[len++] = 'r';
842 b[len++] = 'e';
843 pdf_doc_add_page_content(b, len); /* op: re */
844 len = 0;
845 } else {
846 n_seg = PA_LENGTH(pa);
847 for (i = 0, len = 0, pe = &pa->path[0];
848 i < n_seg; pe++, i++) {
849 n_pts = PE_N_PTS(pe);
850 for (j = 0, pt = &pe->p[0];
851 j < n_pts; j++, pt++) {
852 b[len++] = ' ';
853 len += pdf_sprint_coord(b + len, pt);
854 }
855 b[len++] = ' ';
856 b[len++] = PE_OPCHR(pe);
857 if (len + 128 > b_len) {
858 pdf_doc_add_page_content(b, len); /* op: m l c v y h */
859 len = 0;
860 }
861 }
862 if (len > 0) {
863 pdf_doc_add_page_content(b, len); /* op: m l c v y h */
864 len = 0;
865 }
866 }
867
868 b[len++] = ' ';
869 b[len++] = opchr;
870 if (rule == PDF_FILL_RULE_EVENODD)
871 b[len++] = '*';
872 if (isclip) {
873 b[len++] = ' '; b[len++] = 'n';
874 }
875
876 pdf_doc_add_page_content(b, len); /* op: f F s S b B W f* F* s* S* b* B* W* */
877
878 return 0;
879 }
880
881
882 /* Graphics State */
883 typedef struct pdf_gstate_
884 {
885 pdf_coord cp;
886
887 pdf_tmatrix matrix; /* cm, - */
888
889 pdf_color strokecolor;
890 pdf_color fillcolor;
891 /* colorspace here */
892
893 struct {
894 int num_dash;
895 double pattern[PDF_DASH_SIZE_MAX];
896 double offset;
897 } linedash; /* d, D */
898
899 double linewidth; /* w, LW */
900
901 int linecap; /* J, LC */
902 int linejoin; /* j, LJ */
903 double miterlimit; /* M, ML */
904
905 int flatness; /* i, FL, 0 to 100 (0 for use device-default) */
906
907 /* internal */
908 pdf_path path;
909 long flags;
910 /* bookkeeping the origin of the last transform applied */
911 pdf_coord pt_fixee;
912 } pdf_gstate;
913
914
915 typedef struct m_stack_elem
916 {
917 void *data;
918 struct m_stack_elem *prev;
919 } m_stack_elem;
920
921 typedef struct m_stack
922 {
923 int size;
924 m_stack_elem *top;
925 m_stack_elem *bottom;
926 } m_stack;
927
928 static void
m_stack_init(m_stack * stack)929 m_stack_init (m_stack *stack)
930 {
931 ASSERT(stack);
932
933 stack->size = 0;
934 stack->top = NULL;
935 stack->bottom = NULL;
936
937 return;
938 }
939
940 static void
m_stack_push(m_stack * stack,void * data)941 m_stack_push (m_stack *stack, void *data)
942 {
943 m_stack_elem *elem;
944
945 ASSERT(stack);
946
947 elem = NEW(1, m_stack_elem);
948 elem->prev = stack->top;
949 elem->data = data;
950
951 stack->top = elem;
952 if (stack->size == 0)
953 stack->bottom = elem;
954
955 stack->size++;
956
957 return;
958 }
959
960 static void *
m_stack_pop(m_stack * stack)961 m_stack_pop (m_stack *stack)
962 {
963 m_stack_elem *elem;
964 void *data;
965
966 ASSERT(stack);
967
968 if (stack->size == 0)
969 return NULL;
970
971 data = stack->top->data;
972 elem = stack->top;
973 stack->top = elem->prev;
974 if (stack->size == 1)
975 stack->bottom = NULL;
976 RELEASE(elem);
977
978 stack->size--;
979
980 return data;
981 }
982
983 static void *
m_stack_top(m_stack * stack)984 m_stack_top (m_stack *stack)
985 {
986 void *data;
987
988 ASSERT(stack);
989
990 if (stack->size == 0)
991 return NULL;
992
993 data = stack->top->data;
994
995 return data;
996 }
997
998 #define m_stack_depth(s) ((s)->size)
999
1000 static m_stack gs_stack;
1001
1002 static void
init_a_gstate(pdf_gstate * gs)1003 init_a_gstate (pdf_gstate *gs)
1004 {
1005 gs->cp.x = 0.0;
1006 gs->cp.y = 0.0;
1007
1008 pdf_setmatrix(&gs->matrix, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
1009
1010 pdf_color_black(&gs->strokecolor);
1011 pdf_color_black(&gs->fillcolor);
1012
1013 gs->linedash.num_dash = 0;
1014 gs->linedash.offset = 0;
1015 gs->linecap = 0;
1016 gs->linejoin = 0;
1017 gs->linewidth = 1.0;
1018 gs->miterlimit = 10.0;
1019
1020 gs->flatness = 1; /* default to 1 in PDF */
1021
1022 /* Internal variables */
1023 gs->flags = 0;
1024 init_a_path(&gs->path);
1025 gs->pt_fixee.x = 0;
1026 gs->pt_fixee.y = 0;
1027
1028 return;
1029 }
1030
1031 static void
clear_a_gstate(pdf_gstate * gs)1032 clear_a_gstate (pdf_gstate *gs)
1033 {
1034 clear_a_path(&gs->path);
1035 memset(gs, 0, sizeof(pdf_gstate));
1036
1037 return;
1038 }
1039
1040 static void
copy_a_gstate(pdf_gstate * gs1,pdf_gstate * gs2)1041 copy_a_gstate (pdf_gstate *gs1, pdf_gstate *gs2)
1042 {
1043 int i;
1044
1045 ASSERT(gs1 && gs2);
1046
1047 gs1->cp.x = gs2->cp.x;
1048 gs1->cp.y = gs2->cp.y;
1049
1050 pdf_copymatrix(&gs1->matrix, &gs2->matrix);
1051
1052 /* TODO:
1053 * Path should be linked list and gsave only
1054 * record starting point within path rather than
1055 * copying whole path.
1056 */
1057 pdf_path__copypath(&gs1->path, &gs2->path);
1058
1059 gs1->linedash.num_dash = gs2->linedash.num_dash;
1060 for (i = 0; i < gs2->linedash.num_dash; i++) {
1061 gs1->linedash.pattern[i] = gs2->linedash.pattern[i];
1062 }
1063 gs1->linedash.offset = gs2->linedash.offset;
1064
1065 gs1->linecap = gs2->linecap;
1066 gs1->linejoin = gs2->linejoin;
1067 gs1->linewidth = gs2->linewidth;
1068 gs1->miterlimit = gs2->miterlimit;
1069 gs1->flatness = gs2->flatness;
1070
1071 pdf_color_copycolor(&gs1->fillcolor , &gs2->fillcolor);
1072 pdf_color_copycolor(&gs1->strokecolor, &gs2->strokecolor);
1073 gs1->pt_fixee.x = gs2->pt_fixee.x;
1074 gs1->pt_fixee.y = gs2->pt_fixee.y;
1075
1076 return;
1077 }
1078
1079 void
pdf_dev_init_gstates(void)1080 pdf_dev_init_gstates (void)
1081 {
1082 pdf_gstate *gs;
1083
1084 m_stack_init(&gs_stack);
1085
1086 gs = NEW(1, pdf_gstate);
1087 init_a_gstate(gs);
1088
1089 m_stack_push(&gs_stack, gs); /* Initial state */
1090
1091 return;
1092 }
1093
1094 void
pdf_dev_clear_gstates(void)1095 pdf_dev_clear_gstates (void)
1096 {
1097 pdf_gstate *gs;
1098
1099 if (m_stack_depth(&gs_stack) > 1) /* at least 1 elem. */
1100 WARN("GS stack depth is not zero at the end of the document.");
1101
1102 while ((gs = m_stack_pop(&gs_stack)) != NULL) {
1103 clear_a_gstate(gs);
1104 RELEASE(gs);
1105 }
1106 return;
1107 }
1108
1109 int
pdf_dev_gsave(void)1110 pdf_dev_gsave (void)
1111 {
1112 pdf_gstate *gs0, *gs1;
1113
1114 gs0 = m_stack_top(&gs_stack);
1115 gs1 = NEW(1, pdf_gstate);
1116 init_a_gstate(gs1);
1117 copy_a_gstate(gs1, gs0);
1118 m_stack_push(&gs_stack, gs1);
1119
1120 pdf_doc_add_page_content(" q", 2); /* op: q */
1121
1122 return 0;
1123 }
1124
1125 int
pdf_dev_grestore(void)1126 pdf_dev_grestore (void)
1127 {
1128 pdf_gstate *gs;
1129
1130 if (m_stack_depth(&gs_stack) <= 1) { /* Initial state at bottom */
1131 WARN("Too many grestores.");
1132 return -1;
1133 }
1134
1135 gs = m_stack_pop(&gs_stack);
1136 clear_a_gstate(gs);
1137 RELEASE(gs);
1138
1139 pdf_doc_add_page_content(" Q", 2); /* op: Q */
1140
1141 pdf_dev_reset_fonts(0);
1142
1143 return 0;
1144 }
1145
1146
1147 int
pdf_dev_push_gstate(void)1148 pdf_dev_push_gstate (void)
1149 {
1150 m_stack *gss = &gs_stack;
1151 pdf_gstate *gs0;
1152
1153 gs0 = NEW(1, pdf_gstate);
1154
1155 init_a_gstate(gs0);
1156
1157 m_stack_push(gss, gs0);
1158
1159 return 0;
1160 }
1161
1162
1163 int
pdf_dev_pop_gstate(void)1164 pdf_dev_pop_gstate (void)
1165 {
1166 m_stack *gss = &gs_stack;
1167 pdf_gstate *gs;
1168
1169 if (m_stack_depth(gss) <= 1) { /* Initial state at bottom */
1170 WARN("Too many grestores.");
1171 return -1;
1172 }
1173
1174 gs = m_stack_pop(gss);
1175 clear_a_gstate(gs);
1176 RELEASE(gs);
1177
1178 return 0;
1179 }
1180
1181
1182 int
pdf_dev_current_depth(void)1183 pdf_dev_current_depth (void)
1184 {
1185 return (m_stack_depth(&gs_stack) - 1); /* 0 means initial state */
1186 }
1187
1188 void
pdf_dev_grestore_to(int depth)1189 pdf_dev_grestore_to (int depth)
1190 {
1191 m_stack *gss = &gs_stack;
1192 pdf_gstate *gs;
1193
1194 ASSERT(depth >= 0);
1195
1196 if (m_stack_depth(gss) > depth + 1) {
1197 WARN("Closing pending transformations at end of page/XObject.");
1198 }
1199
1200 while (m_stack_depth(gss) > depth + 1) {
1201 pdf_doc_add_page_content(" Q", 2); /* op: Q */
1202 gs = m_stack_pop(gss);
1203 clear_a_gstate(gs);
1204 RELEASE(gs);
1205 }
1206 pdf_dev_reset_fonts(0);
1207
1208 return;
1209 }
1210
1211 int
pdf_dev_currentpoint(pdf_coord * p)1212 pdf_dev_currentpoint (pdf_coord *p)
1213 {
1214 m_stack *gss = &gs_stack;
1215 pdf_gstate *gs = m_stack_top(gss);
1216 pdf_coord *cpt = &gs->cp;
1217
1218 ASSERT(p);
1219
1220 p->x = cpt->x; p->y = cpt->y;
1221
1222 return 0;
1223 }
1224
1225 int
pdf_dev_currentmatrix(pdf_tmatrix * M)1226 pdf_dev_currentmatrix (pdf_tmatrix *M)
1227 {
1228 m_stack *gss = &gs_stack;
1229 pdf_gstate *gs = m_stack_top(gss);
1230 pdf_tmatrix *CTM = &gs->matrix;
1231
1232 ASSERT(M);
1233
1234 pdf_copymatrix(M, CTM);
1235
1236 return 0;
1237 }
1238
1239 #if 0
1240 int
1241 pdf_dev_currentcolor (pdf_color *color, int is_fill)
1242 {
1243 m_stack *gss = &gs_stack;
1244 pdf_gstate *gs = m_stack_top(gss);
1245 pdf_color *fcl = &gs->fillcolor;
1246 pdf_color *scl = &gs->strokecolor;
1247
1248 ASSERT(color);
1249
1250 pdf_color_copycolor(color, is_fill ? fcl : scl);
1251
1252 return 0;
1253 }
1254 #endif /* 0 */
1255
1256 /*
1257 * mask == 0 means stroking color, mask == 0x20 nonstroking color
1258 *
1259 * force == 1 means that operators will be generated even if
1260 * the color is the same as the current graphics state color
1261 */
1262 void
pdf_dev_set_color(const pdf_color * color,char mask,int force)1263 pdf_dev_set_color (const pdf_color *color, char mask, int force)
1264 {
1265 int len;
1266
1267 pdf_gstate *gs = m_stack_top(&gs_stack);
1268 pdf_color *current = mask ? &gs->fillcolor : &gs->strokecolor;
1269
1270 ASSERT(pdf_color_is_valid(color));
1271
1272 if (!(pdf_dev_get_param(PDF_DEV_PARAM_COLORMODE) &&
1273 (force || pdf_color_compare(color, current))))
1274 /* If "color" is already the current color, then do nothing
1275 * unless a color operator is forced
1276 */
1277 return;
1278
1279 graphics_mode();
1280 len = pdf_color_to_string(color, fmt_buf, mask);
1281 fmt_buf[len++] = ' ';
1282 switch (pdf_color_type(color)) {
1283 case PDF_COLORSPACE_TYPE_RGB:
1284 fmt_buf[len++] = 'R' | mask;
1285 fmt_buf[len++] = 'G' | mask;
1286 break;
1287 case PDF_COLORSPACE_TYPE_CMYK:
1288 fmt_buf[len++] = 'K' | mask;
1289 break;
1290 case PDF_COLORSPACE_TYPE_GRAY:
1291 fmt_buf[len++] = 'G' | mask;
1292 break;
1293 default: /* already verified the given color */
1294 break;
1295 }
1296 pdf_doc_add_page_content(fmt_buf, len); /* op: RG K G rg k g etc. */
1297
1298 pdf_color_copycolor(current, color);
1299 }
1300
1301 int
pdf_dev_concat(const pdf_tmatrix * M)1302 pdf_dev_concat (const pdf_tmatrix *M)
1303 {
1304 m_stack *gss = &gs_stack;
1305 pdf_gstate *gs = m_stack_top(gss);
1306 pdf_path *cpa = &gs->path;
1307 pdf_coord *cpt = &gs->cp;
1308 pdf_tmatrix *CTM = &gs->matrix;
1309 pdf_tmatrix W = {0, 0, 0, 0, 0, 0}; /* Init to avoid compiler warning */
1310 char *buf = fmt_buf;
1311 int len = 0;
1312
1313 ASSERT(M);
1314
1315 /* Adobe Reader erases page content if there are
1316 * non invertible transformation.
1317 */
1318 if (fabs(detP(M)) < 1.0e-8) {
1319 WARN("Transformation matrix not invertible.");
1320 WARN("--- M = [%g %g %g %g %g %g]",
1321 M->a, M->b, M->c, M->d, M->e, M->f);
1322 return -1;
1323 }
1324
1325 if (fabs(M->a - 1.0) > 1.e-8 || fabs(M->b) > 1.e-8
1326 || fabs(M->c) > 1.e-8 || fabs(M->d - 1.0) > 1.e-8
1327 || fabs(M->e) > 1.e-8 || fabs(M->f) > 1.e-8) {
1328 buf[len++] = ' ';
1329 len += pdf_sprint_matrix(buf + len, M);
1330 buf[len++] = ' ';
1331 buf[len++] = 'c';
1332 buf[len++] = 'm';
1333 pdf_doc_add_page_content(buf, len); /* op: cm */
1334
1335 pdf_concatmatrix(CTM, M);
1336 }
1337 inversematrix(&W, M);
1338
1339 pdf_path__transform (cpa, &W);
1340 pdf_coord__transform(cpt, &W);
1341
1342 return 0;
1343 }
1344
1345 /*
1346 * num w LW linewidth (g.t. 0)
1347 * int J LC linecap
1348 * int j LJ linejoin
1349 * num M ML miter limit (g.t. 0)
1350 * array num d D line dash
1351 * int ri RI renderint intnet
1352 * int i FL flatness tolerance (0-100)
1353 * name gs -- name: res. name of ExtGState dict.
1354 */
1355 int
pdf_dev_setmiterlimit(double mlimit)1356 pdf_dev_setmiterlimit (double mlimit)
1357 {
1358 m_stack *gss = &gs_stack;
1359 pdf_gstate *gs = m_stack_top(gss);
1360 int len = 0;
1361 char *buf = fmt_buf;
1362
1363 if (gs->miterlimit != mlimit) {
1364 buf[len++] = ' ';
1365 len += pdf_sprint_length(buf + len, mlimit);
1366 buf[len++] = ' ';
1367 buf[len++] = 'M';
1368 pdf_doc_add_page_content(buf, len); /* op: M */
1369 gs->miterlimit = mlimit;
1370 }
1371
1372 return 0;
1373 }
1374
1375 int
pdf_dev_setlinecap(int capstyle)1376 pdf_dev_setlinecap (int capstyle)
1377 {
1378 m_stack *gss = &gs_stack;
1379 pdf_gstate *gs = m_stack_top(gss);
1380 int len = 0;
1381 char *buf = fmt_buf;
1382
1383 if (gs->linecap != capstyle) {
1384 len = sprintf(buf, " %d J", capstyle);
1385 pdf_doc_add_page_content(buf, len); /* op: J */
1386 gs->linecap = capstyle;
1387 }
1388
1389 return 0;
1390 }
1391
1392 int
pdf_dev_setlinejoin(int joinstyle)1393 pdf_dev_setlinejoin (int joinstyle)
1394 {
1395 m_stack *gss = &gs_stack;
1396 pdf_gstate *gs = m_stack_top(gss);
1397 int len = 0;
1398 char *buf = fmt_buf;
1399
1400 if (gs->linejoin != joinstyle) {
1401 len = sprintf(buf, " %d j", joinstyle);
1402 pdf_doc_add_page_content(buf, len); /* op: j */
1403 gs->linejoin = joinstyle;
1404 }
1405
1406 return 0;
1407 }
1408
1409 int
pdf_dev_setlinewidth(double width)1410 pdf_dev_setlinewidth (double width)
1411 {
1412 m_stack *gss = &gs_stack;
1413 pdf_gstate *gs = m_stack_top(gss);
1414 int len = 0;
1415 char *buf = fmt_buf;
1416
1417 if (gs->linewidth != width) {
1418 buf[len++] = ' ';
1419 len += pdf_sprint_length(buf + len, width);
1420 buf[len++] = ' ';
1421 buf[len++] = 'w';
1422 pdf_doc_add_page_content(buf, len); /* op: w */
1423 gs->linewidth = width;
1424 }
1425
1426 return 0;
1427 }
1428
1429 int
pdf_dev_setdash(int count,double * pattern,double offset)1430 pdf_dev_setdash (int count, double *pattern, double offset)
1431 {
1432 m_stack *gss = &gs_stack;
1433 pdf_gstate *gs = m_stack_top(gss);
1434 int len = 0;
1435 char *buf = fmt_buf;
1436 int i;
1437
1438 gs->linedash.num_dash = count;
1439 gs->linedash.offset = offset;
1440 pdf_doc_add_page_content(" [", 2); /* op: */
1441 for (i = 0; i < count; i++) {
1442 buf[0] = ' ';
1443 len = pdf_sprint_length (buf + 1, pattern[i]);
1444 pdf_doc_add_page_content(buf, len + 1); /* op: */
1445 gs->linedash.pattern[i] = pattern[i];
1446 }
1447 pdf_doc_add_page_content("] ", 2); /* op: */
1448 len = pdf_sprint_length (buf, offset);
1449 pdf_doc_add_page_content(buf, len); /* op: */
1450 pdf_doc_add_page_content(" d", 2); /* op: d */
1451
1452 return 0;
1453 }
1454
1455 #if 0
1456 int
1457 pdf_dev_setflat (int flatness)
1458 {
1459 m_stack *gss = &gs_stack;
1460 pdf_gstate *gs = m_stack_top(gss);
1461 int len = 0;
1462 char *buf = fmt_buf;
1463
1464 if (flatness < 0 || flatness > 100)
1465 return -1;
1466
1467 if (gs->flatness != flatness) {
1468 gs->flatness = flatness;
1469 len = sprintf(buf, " %d i", flatness);
1470 pdf_doc_add_page_content(buf, len); /* op: i */
1471 }
1472
1473 return 0;
1474 }
1475 #endif
1476
1477 /* ZSYUEDVEDEOF */
1478 int
pdf_dev_clip(void)1479 pdf_dev_clip (void)
1480 {
1481 m_stack *gss = &gs_stack;
1482 pdf_gstate *gs = m_stack_top(gss);
1483 pdf_path *cpa = &gs->path;
1484
1485 return pdf_dev__flushpath(cpa, 'W', PDF_FILL_RULE_NONZERO, 0);
1486 }
1487
1488 int
pdf_dev_eoclip(void)1489 pdf_dev_eoclip (void)
1490 {
1491 m_stack *gss = &gs_stack;
1492 pdf_gstate *gs = m_stack_top(gss);
1493 pdf_path *cpa = &gs->path;
1494
1495 return pdf_dev__flushpath(cpa, 'W', PDF_FILL_RULE_EVENODD, 0);
1496 }
1497
1498 int
pdf_dev_flushpath(char p_op,int fill_rule)1499 pdf_dev_flushpath (char p_op, int fill_rule)
1500 {
1501 m_stack *gss = &gs_stack;
1502 pdf_gstate *gs = m_stack_top(gss);
1503 pdf_path *cpa = &gs->path;
1504 int error = 0;
1505
1506 /* last arg 'ignore_rule' is only for single object
1507 * that can be converted to a rect where fill rule
1508 * is inessential.
1509 */
1510 error = pdf_dev__flushpath(cpa, p_op, fill_rule, 1);
1511 pdf_path__clearpath(cpa);
1512
1513 gs->flags &= ~GS_FLAG_CURRENTPOINT_SET;
1514
1515 return error;
1516 }
1517
1518 int
pdf_dev_newpath(void)1519 pdf_dev_newpath (void)
1520 {
1521 m_stack *gss = &gs_stack;
1522 pdf_gstate *gs = m_stack_top(gss);
1523 pdf_path *p = &gs->path;
1524
1525 if (PA_LENGTH(p) > 0) {
1526 pdf_path__clearpath (p);
1527 }
1528 /* The following is required for "newpath" operator in mpost.c. */
1529 pdf_doc_add_page_content(" n", 2); /* op: n */
1530
1531 return 0;
1532 }
1533
1534 int
pdf_dev_moveto(double x,double y)1535 pdf_dev_moveto (double x, double y)
1536 {
1537 m_stack *gss = &gs_stack;
1538 pdf_gstate *gs = m_stack_top(gss);
1539 pdf_path *cpa = &gs->path;
1540 pdf_coord *cpt = &gs->cp;
1541 pdf_coord p;
1542
1543 p.x = x; p.y = y;
1544 return pdf_path__moveto(cpa, cpt, &p); /* cpt updated */
1545 }
1546
1547 int
pdf_dev_rmoveto(double x,double y)1548 pdf_dev_rmoveto (double x, double y)
1549 {
1550 m_stack *gss = &gs_stack;
1551 pdf_gstate *gs = m_stack_top(gss);
1552 pdf_path *cpa = &gs->path;
1553 pdf_coord *cpt = &gs->cp;
1554 pdf_coord p;
1555
1556 p.x = cpt->x + x;
1557 p.y = cpt->y + y;
1558 return pdf_path__moveto(cpa, cpt, &p); /* cpt updated */
1559 }
1560
1561 int
pdf_dev_lineto(double x,double y)1562 pdf_dev_lineto (double x, double y)
1563 {
1564 m_stack *gss = &gs_stack;
1565 pdf_gstate *gs = m_stack_top(gss);
1566 pdf_path *cpa = &gs->path;
1567 pdf_coord *cpt = &gs->cp;
1568 pdf_coord p0;
1569
1570 p0.x = x; p0.y = y;
1571
1572 return pdf_path__lineto(cpa, cpt, &p0);
1573 }
1574
1575 int
pdf_dev_rlineto(double x,double y)1576 pdf_dev_rlineto (double x, double y)
1577 {
1578 m_stack *gss = &gs_stack;
1579 pdf_gstate *gs = m_stack_top(gss);
1580 pdf_path *cpa = &gs->path;
1581 pdf_coord *cpt = &gs->cp;
1582 pdf_coord p0;
1583
1584 p0.x = x + cpt->x; p0.y = y + cpt->y;
1585
1586 return pdf_path__lineto(cpa, cpt, &p0);
1587 }
1588
1589 int
pdf_dev_curveto(double x0,double y0,double x1,double y1,double x2,double y2)1590 pdf_dev_curveto (double x0, double y0,
1591 double x1, double y1,
1592 double x2, double y2)
1593 {
1594 m_stack *gss = &gs_stack;
1595 pdf_gstate *gs = m_stack_top(gss);
1596 pdf_path *cpa = &gs->path;
1597 pdf_coord *cpt = &gs->cp;
1598 pdf_coord p0, p1, p2;
1599
1600 p0.x = x0; p0.y = y0;
1601 p1.x = x1; p1.y = y1;
1602 p2.x = x2; p2.y = y2;
1603
1604 return pdf_path__curveto(cpa, cpt, &p0, &p1, &p2);
1605 }
1606
1607 int
pdf_dev_vcurveto(double x0,double y0,double x1,double y1)1608 pdf_dev_vcurveto (double x0, double y0,
1609 double x1, double y1)
1610 {
1611 m_stack *gss = &gs_stack;
1612 pdf_gstate *gs = m_stack_top(gss);
1613 pdf_path *cpa = &gs->path;
1614 pdf_coord *cpt = &gs->cp;
1615 pdf_coord p0, p1;
1616
1617 p0.x = x0; p0.y = y0;
1618 p1.x = x1; p1.y = y1;
1619
1620 return pdf_path__curveto(cpa, cpt, cpt, &p0, &p1);
1621 }
1622
1623 int
pdf_dev_ycurveto(double x0,double y0,double x1,double y1)1624 pdf_dev_ycurveto (double x0, double y0,
1625 double x1, double y1)
1626 {
1627 m_stack *gss = &gs_stack;
1628 pdf_gstate *gs = m_stack_top(gss);
1629 pdf_path *cpa = &gs->path;
1630 pdf_coord *cpt = &gs->cp;
1631 pdf_coord p0, p1;
1632
1633 p0.x = x0; p0.y = y0;
1634 p1.x = x1; p1.y = y1;
1635
1636 return pdf_path__curveto(cpa, cpt, &p0, &p1, &p1);
1637 }
1638
1639 int
pdf_dev_rcurveto(double x0,double y0,double x1,double y1,double x2,double y2)1640 pdf_dev_rcurveto (double x0, double y0,
1641 double x1, double y1,
1642 double x2, double y2)
1643 {
1644 m_stack *gss = &gs_stack;
1645 pdf_gstate *gs = m_stack_top(gss);
1646 pdf_path *cpa = &gs->path;
1647 pdf_coord *cpt = &gs->cp;
1648 pdf_coord p0, p1, p2;
1649
1650 p0.x = x0 + cpt->x; p0.y = y0 + cpt->y;
1651 p1.x = x1 + cpt->x; p1.y = y1 + cpt->y;
1652 p2.x = x2 + cpt->x; p2.y = y2 + cpt->y;
1653
1654 return pdf_path__curveto(cpa, cpt, &p0, &p1, &p2);
1655 }
1656
1657
1658 int
pdf_dev_closepath(void)1659 pdf_dev_closepath (void)
1660 {
1661 m_stack *gss = &gs_stack;
1662 pdf_gstate *gs = m_stack_top(gss);
1663 pdf_coord *cpt = &gs->cp;
1664 pdf_path *cpa = &gs->path;
1665
1666 return pdf_path__closepath(cpa, cpt);
1667 }
1668
1669
1670 void
pdf_dev_dtransform(pdf_coord * p,const pdf_tmatrix * M)1671 pdf_dev_dtransform (pdf_coord *p, const pdf_tmatrix *M)
1672 {
1673 m_stack *gss = &gs_stack;
1674 pdf_gstate *gs = m_stack_top(gss);
1675 pdf_tmatrix *CTM = &gs->matrix;
1676
1677 ASSERT(p);
1678
1679 pdf_coord__dtransform(p, (M ? M : CTM));
1680
1681 return;
1682 }
1683
1684 void
pdf_dev_idtransform(pdf_coord * p,const pdf_tmatrix * M)1685 pdf_dev_idtransform (pdf_coord *p, const pdf_tmatrix *M)
1686 {
1687 m_stack *gss = &gs_stack;
1688 pdf_gstate *gs = m_stack_top(gss);
1689 pdf_tmatrix *CTM = &gs->matrix;
1690
1691 ASSERT(p);
1692
1693 pdf_coord__idtransform(p, (M ? M : CTM));
1694
1695 return;
1696 }
1697
1698 void
pdf_dev_transform(pdf_coord * p,const pdf_tmatrix * M)1699 pdf_dev_transform (pdf_coord *p, const pdf_tmatrix *M)
1700 {
1701 m_stack *gss = &gs_stack;
1702 pdf_gstate *gs = m_stack_top(gss);
1703 pdf_tmatrix *CTM = &gs->matrix;
1704
1705 ASSERT(p);
1706
1707 pdf_coord__transform(p, (M ? M : CTM));
1708
1709 return;
1710 }
1711
1712 #if 0
1713 void
1714 pdf_dev_itransform (pdf_coord *p, const pdf_tmatrix *M)
1715 {
1716 m_stack *gss = &gs_stack;
1717 pdf_gstate *gs = m_stack_top(gss);
1718 pdf_tmatrix *CTM = &gs->matrix;
1719
1720 ASSERT(p);
1721
1722 pdf_coord__itransform(p, (M ? M : CTM));
1723
1724 return;
1725 }
1726 #endif
1727
1728 int
pdf_dev_arc(double c_x,double c_y,double r,double a_0,double a_1)1729 pdf_dev_arc (double c_x , double c_y, double r,
1730 double a_0 , double a_1)
1731 {
1732 m_stack *gss = &gs_stack;
1733 pdf_gstate *gs = m_stack_top(gss);
1734 pdf_path *cpa = &gs->path;
1735 pdf_coord *cpt = &gs->cp;
1736 pdf_coord c;
1737
1738 c.x = c_x; c.y = c_y;
1739
1740 return pdf_path__elliptarc(cpa, cpt, &c, r, r, 0.0, a_0, a_1, +1);
1741 }
1742
1743 /* *negative* arc */
1744 int
pdf_dev_arcn(double c_x,double c_y,double r,double a_0,double a_1)1745 pdf_dev_arcn (double c_x , double c_y, double r,
1746 double a_0 , double a_1)
1747 {
1748 m_stack *gss = &gs_stack;
1749 pdf_gstate *gs = m_stack_top(gss);
1750 pdf_path *cpa = &gs->path;
1751 pdf_coord *cpt = &gs->cp;
1752 pdf_coord c;
1753
1754 c.x = c_x; c.y = c_y;
1755
1756 return pdf_path__elliptarc(cpa, cpt, &c, r, r, 0.0, a_0, a_1, -1);
1757 }
1758
1759 int
pdf_dev_arcx(double c_x,double c_y,double r_x,double r_y,double a_0,double a_1,int a_d,double xar)1760 pdf_dev_arcx (double c_x , double c_y,
1761 double r_x , double r_y,
1762 double a_0 , double a_1,
1763 int a_d ,
1764 double xar)
1765 {
1766 m_stack *gss = &gs_stack;
1767 pdf_gstate *gs = m_stack_top(gss);
1768 pdf_path *cpa = &gs->path;
1769 pdf_coord *cpt = &gs->cp;
1770 pdf_coord c;
1771
1772 c.x = c_x; c.y = c_y;
1773
1774 return pdf_path__elliptarc(cpa, cpt, &c, r_x, r_y, xar, a_0, a_1, a_d);
1775 }
1776
1777 /* Required by Tpic */
1778 int
pdf_dev_bspline(double x0,double y0,double x1,double y1,double x2,double y2)1779 pdf_dev_bspline (double x0, double y0,
1780 double x1, double y1, double x2, double y2)
1781 {
1782 m_stack *gss = &gs_stack;
1783 pdf_gstate *gs = m_stack_top(gss);
1784 pdf_path *cpa = &gs->path;
1785 pdf_coord *cpt = &gs->cp;
1786 pdf_coord p1, p2, p3;
1787
1788 p1.x = x0 + 2.0 * (x1 - x0) / 3.0;
1789 p1.y = y0 + 2.0 * (y1 - y0) / 3.0;
1790 p2.x = x1 + (x2 - x1) / 3.0;
1791 p2.y = y1 + (y2 - y1) / 3.0;
1792 p3.x = x2;
1793 p3.y = y2;
1794
1795 return pdf_path__curveto(cpa, cpt, &p1, &p2, &p3);
1796 }
1797
1798 #if 0
1799 int
1800 pdf_dev_rectstroke (double x, double y,
1801 double w, double h,
1802 const pdf_tmatrix *M /* optional */
1803 )
1804 {
1805 pdf_rect r;
1806
1807 r.llx = x;
1808 r.lly = y;
1809 r.urx = x + w;
1810 r.ury = y + h;
1811
1812 return pdf_dev__rectshape(&r, M, 'S');
1813 }
1814 #endif
1815
1816 int
pdf_dev_rectfill(double x,double y,double w,double h)1817 pdf_dev_rectfill (double x, double y,
1818 double w, double h)
1819 {
1820 pdf_rect r;
1821
1822 r.llx = x;
1823 r.lly = y;
1824 r.urx = x + w;
1825 r.ury = y + h;
1826
1827 return pdf_dev__rectshape(&r, NULL, 'f');
1828 }
1829
1830 int
pdf_dev_rectclip(double x,double y,double w,double h)1831 pdf_dev_rectclip (double x, double y,
1832 double w, double h)
1833 {
1834 pdf_rect r;
1835
1836 r.llx = x;
1837 r.lly = y;
1838 r.urx = x + w;
1839 r.ury = y + h;
1840
1841 return pdf_dev__rectshape(&r, NULL, 'W');
1842 }
1843
1844 int
pdf_dev_rectadd(double x,double y,double w,double h)1845 pdf_dev_rectadd (double x, double y,
1846 double w, double h)
1847 {
1848 pdf_rect r;
1849
1850 r.llx = x;
1851 r.lly = y;
1852 r.urx = x + w;
1853 r.ury = y + h;
1854 path_added = 1;
1855
1856 return pdf_dev__rectshape(&r, NULL, ' ');
1857 }
1858
1859 void
pdf_dev_set_fixed_point(double x,double y)1860 pdf_dev_set_fixed_point (double x, double y)
1861 {
1862 m_stack *gss = &gs_stack;
1863 pdf_gstate *gs = m_stack_top(gss);
1864 gs->pt_fixee.x = x;
1865 gs->pt_fixee.y = y;
1866 }
1867
1868 void
pdf_dev_get_fixed_point(pdf_coord * p)1869 pdf_dev_get_fixed_point (pdf_coord *p)
1870 {
1871 m_stack *gss = &gs_stack;
1872 pdf_gstate *gs = m_stack_top(gss);
1873 p->x = gs->pt_fixee.x;
1874 p->y = gs->pt_fixee.y;
1875 }
1876