1 /* Copyright (C) 2001-2019 Artifex Software, Inc.
2    All Rights Reserved.
3 
4    This software is provided AS-IS with no warranty, either express or
5    implied.
6 
7    This software is distributed under license and may not be copied,
8    modified or distributed except as expressly authorized under the terms
9    of the license contained in the file LICENSE in this distribution.
10 
11    Refer to licensing information at http://www.artifex.com or contact
12    Artifex Software, Inc.,  1305 Grant Avenue - Suite 200, Novato,
13    CA 94945, U.S.A., +1(415)492-9861, for further information.
14 */
15 
16 
17 /* Path tracing procedures for Ghostscript library */
18 #include "math_.h"
19 #include "gx.h"
20 #include "gserrors.h"
21 #include "gspath.h"		/* for gs_path_enum_alloc prototype */
22 #include "gsstruct.h"
23 #include "gxfixed.h"
24 #include "gxarith.h"
25 #include "gzpath.h"
26 
27 /* Define the enumeration structure. */
28 public_st_path_enum();
29 
30 /* Read the current point of a path. */
31 int
gx_path_current_point(const gx_path * ppath,gs_fixed_point * ppt)32 gx_path_current_point(const gx_path * ppath, gs_fixed_point * ppt)
33 {
34     if (!path_position_valid(ppath))
35         return_error(gs_error_nocurrentpoint);
36     /* Copying the coordinates individually */
37     /* is much faster on a PC, and almost as fast on other machines.... */
38     ppt->x = ppath->position.x, ppt->y = ppath->position.y;
39     return 0;
40 }
41 
42 /* Read the start point of the current subpath. */
43 int
gx_path_subpath_start_point(const gx_path * ppath,gs_fixed_point * ppt)44 gx_path_subpath_start_point(const gx_path * ppath, gs_fixed_point * ppt)
45 {
46     const subpath *psub = ppath->current_subpath;
47 
48     if (!psub)
49         return_error(gs_error_nocurrentpoint);
50     *ppt = psub->pt;
51     return 0;
52 }
53 
54 /* Read the bounding box of a path. */
55 /* Note that if the last element of the path is a moveto, */
56 /* the bounding box does not include this point, */
57 /* unless this is the only element of the path. */
58 int
gx_path_bbox(gx_path * ppath,gs_fixed_rect * pbox)59 gx_path_bbox(gx_path * ppath, gs_fixed_rect * pbox)
60 {
61     if (ppath == NULL) {
62         return_error(gs_error_unknownerror) ;
63     }
64     if (ppath->bbox_accurate) {
65         /* The bounding box was set by setbbox. */
66         *pbox = ppath->bbox;
67         return 0;
68     }
69     if (ppath->first_subpath == 0) {
70         /* The path is empty, use the current point if any. */
71         int code = gx_path_current_point(ppath, &pbox->p);
72 
73         if (code < 0) {
74             /*
75              * Don't return garbage, in case the caller doesn't
76              * check the return code.
77              */
78             pbox->p.x = pbox->p.y = 0;
79         }
80         pbox->q = pbox->p;
81         return code;
82     }
83     /* The stored bounding box may not be up to date. */
84     /* Correct it now if necessary. */
85     if (ppath->box_last == ppath->current_subpath->last) {
86         /* Box is up to date */
87         *pbox = ppath->bbox;
88     } else {
89         fixed px, py, qx, qy;
90         const segment *pseg = ppath->box_last;
91 
92         if (pseg == 0) {	/* box is uninitialized */
93             pseg = (const segment *)ppath->first_subpath;
94             px = qx = pseg->pt.x;
95             py = qy = pseg->pt.y;
96         } else {
97             px = ppath->bbox.p.x, py = ppath->bbox.p.y;
98             qx = ppath->bbox.q.x, qy = ppath->bbox.q.y;
99         }
100 
101 /* Macro for adjusting the bounding box when adding a point */
102 #define ADJUST_BBOX(pt)\
103   if ((pt).x < px) px = (pt).x;\
104   else if ((pt).x > qx) qx = (pt).x;\
105   if ((pt).y < py) py = (pt).y;\
106   else if ((pt).y > qy) qy = (pt).y
107 
108         while ((pseg = pseg->next) != 0) {
109             switch (pseg->type) {
110                 case s_curve:
111                     ADJUST_BBOX(((const curve_segment *)pseg)->p1);
112                     ADJUST_BBOX(((const curve_segment *)pseg)->p2);
113                     /* falls through */
114                 default:
115                     ADJUST_BBOX(pseg->pt);
116             }
117         }
118 #undef ADJUST_BBOX
119 
120 #define STORE_BBOX(b)\
121   (b).p.x = px, (b).p.y = py, (b).q.x = qx, (b).q.y = qy;
122         STORE_BBOX(*pbox);
123         STORE_BBOX(ppath->bbox);
124 #undef STORE_BBOX
125 
126         ppath->box_last = ppath->current_subpath->last;
127     }
128     return 0;
129 }
130 
131 /* A variation of gs_path_bbox, to be used by the patbbox operator */
132 int
gx_path_bbox_set(gx_path * ppath,gs_fixed_rect * pbox)133 gx_path_bbox_set(gx_path * ppath, gs_fixed_rect * pbox)
134 {
135     if (ppath->bbox_set) {
136         /* The bounding box was set by setbbox. */
137         *pbox = ppath->bbox;
138         return 0;
139     } else
140         return gx_path_bbox(ppath, pbox);
141 }
142 
143 /* Test if a path has any curves. */
144 #undef gx_path_has_curves
145 bool
gx_path_has_curves(const gx_path * ppath)146 gx_path_has_curves(const gx_path * ppath)
147 {
148     return gx_path_has_curves_inline(ppath);
149 }
150 #define gx_path_has_curves(ppath)\
151   gx_path_has_curves_inline(ppath)
152 
153 /* Test if a path has no segments. */
154 #undef gx_path_is_void
155 bool
gx_path_is_void(const gx_path * ppath)156 gx_path_is_void(const gx_path * ppath)
157 {
158     return gx_path_is_void_inline(ppath);
159 }
160 #define gx_path_is_void(ppath)\
161   gx_path_is_void_inline(ppath)
162 
163 /* Test if a path has no elements at all. */
164 bool
gx_path_is_null(const gx_path * ppath)165 gx_path_is_null(const gx_path * ppath)
166 {
167     return gx_path_is_null_inline(ppath);
168 }
169 
170 /*
171  * Test if a subpath is a rectangle; if so, return its bounding box
172  * and the start of the next subpath.
173  * Note that this must recognize:
174  *      ordinary closed rectangles (M, L, L, L, C);
175  *      open rectangles (M, L, L, L);
176  *      rectangles closed with lineto (Mo, L, L, L, Lo);
177  *      rectangles closed with *both* lineto and closepath (bad PostScript,
178  *        but unfortunately not rare) (Mo, L, L, L, Lo, C).
179  */
180 gx_path_rectangular_type
gx_subpath_is_rectangular(const subpath * pseg0,gs_fixed_rect * pbox,const subpath ** ppnext)181 gx_subpath_is_rectangular(const subpath * pseg0, gs_fixed_rect * pbox,
182                           const subpath ** ppnext)
183 {
184     const segment *pseg1, *pseg2, *pseg3, *pseg4;
185     gx_path_rectangular_type type = prt_none;
186     fixed x0 = pseg0->pt.x, y0 = pseg0->pt.y;
187     fixed x1, y1, x2, y2, x3, y3;
188 
189     pseg1 = (const segment *)pseg0;
190     do {
191         pseg1 = pseg1->next;
192         if (pseg1 == NULL)
193             return prt_none;
194         x1 = pseg1->pt.x;
195         y1 = pseg1->pt.y;
196         if (pseg1->type == s_curve) {
197             if (gx_curve_is_really_point(x0, y0, pseg1))
198                 continue; /* Ignore this one and try again */
199             if (gx_curve_is_really_line(x0, y0, pseg1))
200                 break; /* That'll do! */
201             return prt_none;
202         } else if (pseg1->type != s_line && pseg1->type != s_gap)
203             return prt_none;
204     } while (x1 == x0 && y1 == y0);
205 
206     pseg2 = pseg1;
207     do {
208         pseg2 = pseg2->next;
209         if (pseg2 == NULL)
210             return prt_none;
211         x2 = pseg2->pt.x;
212         y2 = pseg2->pt.y;
213         if (pseg2->type == s_curve) {
214             if (gx_curve_is_really_point(x1, y1, pseg2))
215                 continue; /* Ignore this one and try again */
216             if (gx_curve_is_really_line(x1, y1, pseg2))
217                 break; /* That'll do! */
218             return prt_none;
219         } else if (pseg2->type != s_line && pseg2->type != s_gap)
220             return prt_none;
221     } while (x2 == x1 && y2 == y1);
222 
223     pseg3 = pseg2;
224     do {
225         pseg3 = pseg3->next;
226         if (pseg3 == NULL)
227             return prt_none;
228         x3 = pseg3->pt.x;
229         y3 = pseg3->pt.y;
230         if (pseg3->type == s_curve) {
231             if (gx_curve_is_really_point(x2, y2, pseg3))
232                 continue; /* Ignore this one and try again */
233             if (gx_curve_is_really_line(x2, y2, pseg3))
234                 break; /* That'll do! */
235             return prt_none;
236         } else if (pseg3->type != s_line && pseg3->type != s_gap)
237             return prt_none;
238     } while (x3 == x2 && y3 == y2);
239 
240     pseg4 = pseg3;
241     do {
242         pseg4 = pseg4->next;
243         if (pseg4 == NULL || pseg4->type == s_start) {
244             type = prt_open; /* M, L, L, L */
245             goto type_known;
246         }
247         if (pseg4->type == s_curve) {
248             if (gx_curve_is_really_point(x3, y3, pseg4))
249                 continue; /* Ignore this one and try again */
250             if (gx_curve_is_really_line(x3, y3, pseg4))
251                 break; /* That'll do! */
252             return prt_none;
253         } else if (pseg4->type == s_line_close) {
254             type = prt_closed; /* M, L, L, L, C */
255             goto type_known;
256         }
257     } while (pseg4->pt.x == x3 && pseg4->pt.y == y3);
258 
259     if (pseg4->pt.x != pseg0->pt.x || pseg4->pt.y != pseg0->pt.y)
260         return prt_none;
261     else if (pseg4->next == NULL || pseg4->next->type == s_start)
262         type = prt_fake_closed;	/* Mo, L, L, L, L, Mo */
263     else
264         return prt_none;
265 
266 type_known:
267 
268     if ((x0 == x1 && y1 == y2 && x2 == x3 && y3 == y0) ||
269         (x0 == x3 && y3 == y2 && x2 == x1 && y1 == y0)) {
270         /* Path is a rectangle.  Return the bounding box. */
271         if (x0 < x2)
272             pbox->p.x = x0, pbox->q.x = x2;
273         else
274             pbox->p.x = x2, pbox->q.x = x0;
275         if (y0 < y2)
276             pbox->p.y = y0, pbox->q.y = y2;
277         else
278             pbox->p.y = y2, pbox->q.y = y0;
279         while (pseg4 != 0 && pseg4->type != s_start)
280             pseg4 = pseg4->next;
281         *ppnext = (const subpath *)pseg4;
282         return type;
283     }
284     return prt_none;
285 }
286 /* Test if an entire path to be filled is a rectangle. */
287 gx_path_rectangular_type
gx_path_is_rectangular(const gx_path * ppath,gs_fixed_rect * pbox)288 gx_path_is_rectangular(const gx_path * ppath, gs_fixed_rect * pbox)
289 {
290     const subpath *pnext;
291 
292     return
293         (gx_path_subpath_count(ppath) == 1 ?
294          gx_subpath_is_rectangular(ppath->first_subpath, pbox, &pnext) :
295          prt_none);
296 }
297 
298 /* Translate an already-constructed path (in device space). */
299 /* Don't bother to update the cbox. */
300 int
gx_path_translate(gx_path * ppath,fixed dx,fixed dy)301 gx_path_translate(gx_path * ppath, fixed dx, fixed dy)
302 {
303     segment *pseg;
304 
305 #define update_xy(pt)\
306   pt.x += dx, pt.y += dy
307     if (ppath->box_last != 0) {
308         update_xy(ppath->bbox.p);
309         update_xy(ppath->bbox.q);
310     }
311     if (path_position_valid(ppath))
312         update_xy(ppath->position);
313     for (pseg = (segment *) (ppath->first_subpath); pseg != 0;
314          pseg = pseg->next
315         )
316         switch (pseg->type) {
317             case s_curve:
318 #define pcseg ((curve_segment *)pseg)
319                 update_xy(pcseg->p1);
320                 update_xy(pcseg->p2);
321 #undef pcseg
322                 /* fall through */
323             default:
324                 update_xy(pseg->pt);
325         }
326 #undef update_xy
327     return 0;
328 }
329 
330 /* Scale an existing path by a power of 2 (positive or negative).
331  * Currently the path drawing routines can't handle values
332  * close to the edge of the representable space.
333  * Also see clamp_point() in gspath.c .
334  */
335 void
gx_point_scale_exp2(gs_fixed_point * pt,int sx,int sy)336 gx_point_scale_exp2(gs_fixed_point * pt, int sx, int sy)
337 {
338     int v;
339 
340     if (sx > 0) {
341         v = (max_int - int2fixed(1000)) >> sx; /* arbitrary */
342         if (pt->x > v)
343             pt->x = v;
344         else if (pt->x < -v)
345             pt->x = -v;
346         pt->x <<= sx;
347     } else
348         pt->x >>= -sx;
349 
350     if (sy > 0) {
351         v = (max_int - int2fixed(1000)) >> sy;
352         if (pt->y > v)
353             pt->y = v;
354         else if (pt->y < -v)
355             pt->y = -v;
356         pt->y <<= sy;
357     } else
358         pt->y >>= -sy;
359 }
360 void
gx_rect_scale_exp2(gs_fixed_rect * pr,int sx,int sy)361 gx_rect_scale_exp2(gs_fixed_rect * pr, int sx, int sy)
362 {
363     gx_point_scale_exp2(&pr->p, sx, sy);
364     gx_point_scale_exp2(&pr->q, sx, sy);
365 }
366 int
gx_path_scale_exp2_shared(gx_path * ppath,int log2_scale_x,int log2_scale_y,bool segments_shared)367 gx_path_scale_exp2_shared(gx_path * ppath, int log2_scale_x, int log2_scale_y,
368                           bool segments_shared)
369 {
370     segment *pseg;
371 
372     gx_rect_scale_exp2(&ppath->bbox, log2_scale_x, log2_scale_y);
373 #define SCALE_XY(pt) gx_point_scale_exp2(&pt, log2_scale_x, log2_scale_y)
374     SCALE_XY(ppath->position);
375     if (!segments_shared) {
376         for (pseg = (segment *) (ppath->first_subpath); pseg != 0;
377              pseg = pseg->next
378              )
379             switch (pseg->type) {
380             case s_curve:
381                 SCALE_XY(((curve_segment *)pseg)->p1);
382                 SCALE_XY(((curve_segment *)pseg)->p2);
383                 /* fall through */
384             default:
385                 SCALE_XY(pseg->pt);
386             }
387     }
388 #undef SCALE_XY
389     return 0;
390 }
391 
392 /*
393  * Reverse a path.  We know ppath != ppath_old.
394  * NOTE: in releases 5.01 and earlier, the implicit line added by closepath
395  * became the first segment of the reversed path.  Starting in release
396  * 5.02, the code follows the Adobe implementation (and LanguageLevel 3
397  * specification), in which this line becomes the *last* segment of the
398  * reversed path.  This can produce some quite unintuitive results.
399  *
400  * The order of the subpaths is unspecified in the PLRM, but the CPSI
401  * reverses the subpaths, and the CET (11-05 p6, test 3) tests for it.
402  */
403 int
gx_path_copy_reversed(const gx_path * ppath_old,gx_path * ppath)404 gx_path_copy_reversed(const gx_path * ppath_old, gx_path * ppath)
405 {
406     const subpath *psub = ppath_old->current_subpath;
407 
408 #ifdef DEBUG
409     if (gs_debug_c('P'))
410         gx_dump_path(ppath_old, "before reversepath");
411 #endif
412  nsp:
413     if (psub) {
414         const segment *prev = psub->last;
415         const segment *pseg;
416         segment_notes notes =
417             (prev == (const segment *)psub ? sn_none :
418              psub->next->notes);
419         segment_notes prev_notes;
420         int code;
421 
422         if (!psub->is_closed) {
423             code = gx_path_add_point(ppath, prev->pt.x, prev->pt.y);
424             if (code < 0)
425                 return code;
426         }
427         /*
428          * The do ... while structure of this loop is artificial,
429          * designed solely to keep compilers from complaining about
430          * 'statement not reached' or 'end-of-loop code not reached'.
431          * The normal exit from this loop is the goto statement in
432          * the s_start arm of the switch.
433          */
434         do {
435             pseg = prev;
436             prev_notes = notes;
437             prev = pseg->prev;
438             notes = pseg->notes;
439             prev_notes = (prev_notes & sn_not_first) |
440                 (notes & ~sn_not_first);
441             switch (pseg->type) {
442                 case s_start:
443                     /* Finished subpath */
444                     if (psub->is_closed) {
445                         code =
446                             gx_path_close_subpath_notes(ppath, prev_notes);
447                         if (code < 0)
448                             return code;
449                     }
450                     do {
451                         psub = (const subpath *)psub->prev;
452                     } while (psub && psub->type != s_start);
453                     goto nsp;
454                 case s_curve:
455                     {
456                         const curve_segment *pc =
457                         (const curve_segment *)pseg;
458 
459                         code = gx_path_add_curve_notes(ppath,
460                                                        pc->p2.x, pc->p2.y,
461                                                        pc->p1.x, pc->p1.y,
462                                         prev->pt.x, prev->pt.y, prev_notes);
463                         break;
464                     }
465                 case s_line:
466                     code = gx_path_add_line_notes(ppath,
467                                         prev->pt.x, prev->pt.y, prev_notes);
468                     break;
469                 case s_gap:
470                     code = gx_path_add_gap_notes(ppath,
471                                         prev->pt.x, prev->pt.y, prev_notes);
472                     break;
473                 case s_line_close:
474                     /* Skip the closing line. */
475                     code = gx_path_add_point(ppath, prev->pt.x,
476                                              prev->pt.y);
477                     break;
478                 default:	/* not possible */
479                     return_error(gs_error_Fatal);
480             }
481         } while (code >= 0);
482         return code;		/* only reached if code < 0 */
483     }
484 #undef sn_not_end
485     /*
486      * In the Adobe implementations, reversepath discards a trailing
487      * moveto unless the path consists only of a moveto.  We reproduce
488      * this behavior here, even though we consider it a bug.
489      */
490     if (ppath_old->first_subpath == 0 &&
491         path_last_is_moveto(ppath_old)
492         ) {
493         int code = gx_path_add_point(ppath, ppath_old->position.x,
494                                      ppath_old->position.y);
495 
496         if (code < 0)
497             return code;
498     }
499 #ifdef DEBUG
500     if (gs_debug_c('P'))
501         gx_dump_path(ppath, "after reversepath");
502 #endif
503     return 0;
504 }
505 
506 int
gx_path_append_reversed(const gx_path * ppath_old,gx_path * ppath)507 gx_path_append_reversed(const gx_path * ppath_old, gx_path * ppath)
508 {
509     const subpath *psub = ppath_old->current_subpath;
510 
511 #ifdef DEBUG
512     if (gs_debug_c('P'))
513         gx_dump_path(ppath_old, "before reversepath");
514 #endif
515  nsp:
516     if (psub) {
517         const segment *prev = psub->last;
518         const segment *pseg;
519         segment_notes notes =
520             (prev == (const segment *)psub ? sn_none :
521              psub->next->notes);
522         segment_notes prev_notes;
523         int code;
524 
525         if (!psub->is_closed) {
526             code = gx_path_add_line(ppath, prev->pt.x, prev->pt.y);
527             if (code < 0)
528                 return code;
529         }
530         /*
531          * The do ... while structure of this loop is artificial,
532          * designed solely to keep compilers from complaining about
533          * 'statement not reached' or 'end-of-loop code not reached'.
534          * The normal exit from this loop is the goto statement in
535          * the s_start arm of the switch.
536          */
537         do {
538             pseg = prev;
539             prev_notes = notes;
540             prev = pseg->prev;
541             notes = pseg->notes;
542             prev_notes = (prev_notes & sn_not_first) |
543                 (notes & ~sn_not_first);
544             switch (pseg->type) {
545                 case s_start:
546                     /* Finished subpath */
547                     if (psub->is_closed) {
548                         code =
549                             gx_path_close_subpath_notes(ppath, prev_notes);
550                         if (code < 0)
551                             return code;
552                     }
553                     do {
554                         psub = (const subpath *)psub->prev;
555                     } while (psub && psub->type != s_start);
556                     goto nsp;
557                 case s_curve:
558                     {
559                         const curve_segment *pc =
560                         (const curve_segment *)pseg;
561 
562                         code = gx_path_add_curve_notes(ppath,
563                                                        pc->p2.x, pc->p2.y,
564                                                        pc->p1.x, pc->p1.y,
565                                         prev->pt.x, prev->pt.y, prev_notes);
566                         break;
567                     }
568                 case s_line:
569                     code = gx_path_add_line_notes(ppath,
570                                         prev->pt.x, prev->pt.y, prev_notes);
571                     break;
572                 case s_gap:
573                     code = gx_path_add_gap_notes(ppath,
574                                         prev->pt.x, prev->pt.y, prev_notes);
575                     break;
576                 case s_line_close:
577                     /* Skip the closing line. */
578                     code = gx_path_add_point(ppath, prev->pt.x,
579                                              prev->pt.y);
580                     break;
581                 default:	/* not possible */
582                     return_error(gs_error_Fatal);
583             }
584         } while (code >= 0);
585         return code;		/* only reached if code < 0 */
586     }
587 #undef sn_not_end
588     /*
589      * In the Adobe implementations, reversepath discards a trailing
590      * moveto unless the path consists only of a moveto.  We reproduce
591      * this behavior here, even though we consider it a bug.
592      */
593     if (ppath_old->first_subpath == 0 &&
594         path_last_is_moveto(ppath_old)
595         ) {
596         int code = gx_path_add_point(ppath, ppath_old->position.x,
597                                      ppath_old->position.y);
598 
599         if (code < 0)
600             return code;
601     }
602 #ifdef DEBUG
603     if (gs_debug_c('P'))
604         gx_dump_path(ppath, "after reversepath");
605 #endif
606     return 0;
607 }
608 
609 /* ------ Path enumeration ------ */
610 
611 /* Allocate a path enumerator. */
612 gs_path_enum *
gs_path_enum_alloc(gs_memory_t * mem,client_name_t cname)613 gs_path_enum_alloc(gs_memory_t * mem, client_name_t cname)
614 {
615     return gs_alloc_struct(mem, gs_path_enum, &st_path_enum, cname);
616 }
617 
618 /* Start enumerating a path. */
619 int
gx_path_enum_init(gs_path_enum * penum,const gx_path * ppath)620 gx_path_enum_init(gs_path_enum * penum, const gx_path * ppath)
621 {
622     penum->memory = 0;		/* path not copied */
623     penum->path = ppath;
624     penum->copied_path = 0;	/* not copied */
625     penum->pseg = (const segment *)ppath->first_subpath;
626     penum->moveto_done = false;
627     penum->notes = sn_none;
628     return 0;
629 }
630 
631 /* Enumerate the next element of a path. */
632 /* If the path is finished, return 0; */
633 /* otherwise, return the element type. */
634 int
gx_path_enum_next(gs_path_enum * penum,gs_fixed_point ppts[3])635 gx_path_enum_next(gs_path_enum * penum, gs_fixed_point ppts[3])
636 {
637     const segment *pseg = penum->pseg;
638 
639     if (pseg == 0) {		/* We've enumerated all the segments, but there might be */
640         /* a trailing moveto. */
641         const gx_path *ppath = penum->path;
642 
643         if (path_last_is_moveto(ppath) && !penum->moveto_done) {	/* Handle a trailing moveto */
644             penum->moveto_done = true;
645             penum->notes = sn_none;
646             ppts[0] = ppath->position;
647             return gs_pe_moveto;
648         }
649         return 0;
650     }
651     penum->pseg = pseg->next;
652     penum->notes = pseg->notes;
653     switch (pseg->type) {
654         case s_start:
655             ppts[0] = pseg->pt;
656             return gs_pe_moveto;
657         case s_line:
658             ppts[0] = pseg->pt;
659             return gs_pe_lineto;
660         case s_gap:
661             ppts[0] = pseg->pt;
662             return gs_pe_gapto;
663         case s_line_close:
664             ppts[0] = pseg->pt;
665             return gs_pe_closepath;
666         case s_curve:
667 #define pcseg ((const curve_segment *)pseg)
668             ppts[0] = pcseg->p1;
669             ppts[1] = pcseg->p2;
670             ppts[2] = pseg->pt;
671             return gs_pe_curveto;
672 #undef pcseg
673         default:
674             lprintf1("bad type %x in gx_path_enum_next!\n", pseg->type);
675             return_error(gs_error_Fatal);
676     }
677 }
678 
679 /* Return the notes from the last-enumerated segment. */
680 segment_notes
gx_path_enum_notes(const gs_path_enum * penum)681 gx_path_enum_notes(const gs_path_enum * penum)
682 {
683     return penum->notes;
684 }
685 
686 /* Back up 1 element in the path being enumerated. */
687 /* Return true if successful, false if we are at the beginning of the path. */
688 /* This implementation allows backing up multiple times, */
689 /* but no client currently relies on this. */
690 bool
gx_path_enum_backup(gs_path_enum * penum)691 gx_path_enum_backup(gs_path_enum * penum)
692 {
693     const segment *pseg = penum->pseg;
694 
695     if (pseg != 0) {
696         if ((pseg = pseg->prev) == 0)
697             return false;
698         penum->pseg = pseg;
699         return true;
700     }
701     /* We're at the end of the path.  Check to see whether */
702     /* we need to back up over a trailing moveto. */
703     {
704         const gx_path *ppath = penum->path;
705 
706         if (path_last_is_moveto(ppath) && penum->moveto_done) {		/* Back up over the trailing moveto. */
707             penum->moveto_done = false;
708             return true;
709         } {
710             const subpath *psub = ppath->current_subpath;
711 
712             if (psub == 0)	/* empty path */
713                 return false;
714             /* Back up to the last segment of the last subpath. */
715             penum->pseg = psub->last;
716             return true;
717         }
718     }
719 }
720