1
/* Copyright (C) 2001-2006 Artifex Software, Inc.
4
This software is provided AS-IS with no warranty, either express or
7
This software is distributed under license and may not be copied, modified
8
or distributed except as expressly authorized under the terms of that
9
license. Refer to licensing information at http://www.artifex.com/
10
or contact Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134,
11
San Rafael, CA 94903, U.S.A., +1(415)492-9861, for further information.
14
/* $Id: zupath.c 9043 2008-08-28 22:48:19Z giles $ */
15
/* Operators related to user paths */
34
#include "gzpath.h" /* for saving path */
35
#include "gzstate.h" /* for accessing path */
38
extern const gx_device gs_hit_device;
39
extern const int gs_hit_detected;
42
* CPSI mode affects two algorithms in this file:
43
* - CPSI allows ucache to appear anywhere in user paths, even though the
44
* PLRM says ucache must appear (if at all) at the beginning
46
* - After appending an empty user path, in CPSI the current point is
47
* defined, even though the PLRM strongly implies this is incorrect
49
* The 'upath_compat' Boolean controls this behavior.
52
extern bool CPSI_mode;
54
/* Forward references */
55
static int upath_append(os_ptr, i_ctx_t *, bool);
56
static int upath_stroke(i_ctx_t *, gs_matrix *, bool);
58
/* ---------------- Insideness testing ---------------- */
60
/* Forward references */
61
static int in_test(i_ctx_t *, int (*)(gs_state *));
62
static int in_path(os_ptr, i_ctx_t *, gx_device *);
63
static int in_path_result(i_ctx_t *, int, int);
64
static int in_utest(i_ctx_t *, int (*)(gs_state *));
65
static int in_upath(i_ctx_t *, gx_device *);
66
static int in_upath_result(i_ctx_t *, int, int);
68
/* <x> <y> ineofill <bool> */
69
/* <userpath> ineofill <bool> */
71
zineofill(i_ctx_t *i_ctx_p)
73
return in_test(i_ctx_p, gs_eofill);
76
/* <x> <y> infill <bool> */
77
/* <userpath> infill <bool> */
79
zinfill(i_ctx_t *i_ctx_p)
81
return in_test(i_ctx_p, gs_fill);
84
/* <x> <y> instroke <bool> */
85
/* <userpath> instroke <bool> */
87
zinstroke(i_ctx_t *i_ctx_p)
89
return in_test(i_ctx_p, gs_stroke);
92
/* <x> <y> <userpath> inueofill <bool> */
93
/* <userpath1> <userpath2> inueofill <bool> */
95
zinueofill(i_ctx_t *i_ctx_p)
97
return in_utest(i_ctx_p, gs_eofill);
100
/* <x> <y> <userpath> inufill <bool> */
101
/* <userpath1> <userpath2> inufill <bool> */
103
zinufill(i_ctx_t *i_ctx_p)
105
return in_utest(i_ctx_p, gs_fill);
108
/* <x> <y> <userpath> inustroke <bool> */
109
/* <x> <y> <userpath> <matrix> inustroke <bool> */
110
/* <userpath1> <userpath2> inustroke <bool> */
111
/* <userpath1> <userpath2> <matrix> inustroke <bool> */
113
zinustroke(i_ctx_t *i_ctx_p)
114
{ /* This is different because of the optional matrix operand. */
116
int code = gs_gsave(igs);
123
if ((spop = upath_stroke(i_ctx_p, &mat, false)) < 0) {
127
if ((npop = in_path(op - spop, i_ctx_p, &hdev)) < 0) {
131
if (npop > 1) /* matrix was supplied */
132
code = gs_concat(igs, &mat);
134
code = gs_stroke(igs);
135
return in_upath_result(i_ctx_p, npop + spop, code);
138
/* ------ Internal routines ------ */
140
/* Do the work of the non-user-path insideness operators. */
142
in_test(i_ctx_t *i_ctx_p, int (*paintproc)(gs_state *))
146
int npop = in_path(op, i_ctx_p, &hdev);
151
code = (*paintproc)(igs);
152
return in_path_result(i_ctx_p, npop, code);
155
/* Set up a clipping path and device for insideness testing. */
157
in_path(os_ptr oppath, i_ctx_t *i_ctx_p, gx_device * phdev)
159
int code = gs_gsave(igs);
165
code = num_params(oppath, 2, uxy);
166
if (code >= 0) { /* Aperture is a single pixel. */
170
gs_transform(igs, uxy[0], uxy[1], &dxy);
171
fr.p.x = fixed_floor(float2fixed(dxy.x));
172
fr.p.y = fixed_floor(float2fixed(dxy.y));
173
fr.q.x = fr.p.x + fixed_1;
174
fr.q.y = fr.p.y + fixed_1;
175
code = gx_clip_to_rectangle(igs, &fr);
177
} else if (code == e_stackunderflow) {
178
/* If 0 elements, definitely a stackunderflow; otherwise, */
179
/* only 1 number, also a stackunderflow. */
181
} else { /* Aperture is a user path. */
182
/* We have to set the clipping path without disturbing */
183
/* the current path. */
184
gx_path *ipath = igs->path;
187
gx_path_init_local(&save, imemory);
188
gx_path_assign_preserve(&save, ipath);
190
code = upath_append(oppath, i_ctx_p, false);
192
code = gx_clip_to_path(igs);
193
gx_path_assign_free(igs->path, &save);
200
/* Install the hit detection device. */
201
gx_set_device_color_1(igs);
202
gx_device_init((gx_device *) phdev, (const gx_device *)&gs_hit_device,
204
phdev->width = phdev->height = max_int;
205
gx_device_fill_in_procs(phdev);
206
gx_set_device_only(igs, phdev);
210
/* Finish an insideness test. */
212
in_path_result(i_ctx_t *i_ctx_p, int npop, int code)
217
gs_grestore(igs); /* matches gsave in in_path */
218
if (code == gs_hit_detected)
220
else if (code == 0) /* completed painting without a hit */
227
make_bool(op, result);
232
/* Do the work of the user-path insideness operators. */
234
in_utest(i_ctx_t *i_ctx_p, int (*paintproc)(gs_state *))
237
int npop = in_upath(i_ctx_p, &hdev);
242
code = (*paintproc)(igs);
243
return in_upath_result(i_ctx_p, npop, code);
246
/* Set up a clipping path and device for insideness testing */
247
/* with a user path. */
249
in_upath(i_ctx_t *i_ctx_p, gx_device * phdev)
252
int code = gs_gsave(igs);
257
if ((code = upath_append(op, i_ctx_p, false)) < 0 ||
258
(code = npop = in_path(op - 1, i_ctx_p, phdev)) < 0
266
/* Finish an insideness test with a user path. */
268
in_upath_result(i_ctx_t *i_ctx_p, int npop, int code)
270
gs_grestore(igs); /* matches gsave in in_upath */
271
return in_path_result(i_ctx_p, npop, code);
274
/* ---------------- User paths ---------------- */
276
/* User path operator codes */
278
upath_op_setbbox = 0,
280
upath_op_rmoveto = 2,
282
upath_op_rlineto = 4,
283
upath_op_curveto = 5,
284
upath_op_rcurveto = 6,
288
upath_op_closepath = 10,
292
/* User path interpretation states */
294
UPS_INITIAL = 1, /* (no ops yet) */
295
UPS_UCACHE = 2, /* ucache */
296
UPS_SETBBOX = 4, /* [ucache] setbbox */
297
UPS_PATH = 8 /* (within path) */
300
typedef struct up_data_s {
305
#define UP_DATA_PATH(n) {n, UPS_SETBBOX | UPS_PATH, UPS_PATH}
307
#define UPATH_MAX_OP 11
308
#define UPATH_REPEAT 32
309
static const up_data_t up_data[UPATH_MAX_OP + 1] = {
310
{4, UPS_INITIAL | UPS_UCACHE, UPS_SETBBOX}, /* setbbox */
321
{0, UPS_INITIAL, UPS_UCACHE} /* ucache */
324
/* Declare operator procedures not declared in opextern.h. */
325
int zsetbbox(i_ctx_t *);
326
static int zucache(i_ctx_t *);
329
static const op_proc_t up_ops[UPATH_MAX_OP + 1] = {
330
zsetbbox, zmoveto, zrmoveto, zlineto, zrlineto,
331
zcurveto, zrcurveto, zarc, zarcn, zarct,
337
zucache(i_ctx_t *i_ctx_p)
339
/* A no-op for now. */
343
/* <userpath> uappend - */
345
zuappend(i_ctx_t *i_ctx_p)
348
int code = gs_gsave(igs);
352
if ((code = upath_append(op, i_ctx_p, false)) >= 0)
353
code = gs_upmergepath(igs);
361
/* <userpath> ueofill - */
363
zueofill(i_ctx_t *i_ctx_p)
366
int code = gs_gsave(igs);
370
if ((code = upath_append(op, i_ctx_p, CPSI_mode)) >= 0)
371
code = gs_eofill(igs);
379
/* <userpath> ufill - */
381
zufill(i_ctx_t *i_ctx_p)
384
int code = gs_gsave(igs);
388
if ((code = upath_append(op, i_ctx_p, CPSI_mode)) >= 0)
397
/* <userpath> ustroke - */
398
/* <userpath> <matrix> ustroke - */
400
zustroke(i_ctx_t *i_ctx_p)
402
int code = gs_gsave(igs);
407
if ((code = npop = upath_stroke(i_ctx_p, NULL, CPSI_mode)) >= 0)
408
code = gs_stroke(igs);
416
/* <userpath> ustrokepath - */
417
/* <userpath> <matrix> ustrokepath - */
419
zustrokepath(i_ctx_t *i_ctx_p)
422
gs_matrix saved_matrix;
423
int npop, code = gs_currentmatrix(igs, &saved_matrix);
427
/* Save and reset the path. */
428
gx_path_init_local(&save, imemory);
429
gx_path_assign_preserve(&save, igs->path);
430
if ((code = npop = upath_stroke(i_ctx_p, NULL, false)) < 0 ||
431
(code = gs_strokepath(igs)) < 0
433
gx_path_assign_free(igs->path, &save);
437
* If a matrix was specified then restore the previous matrix.
440
if ((code = gs_setmatrix(igs, &saved_matrix)) < 0) {
441
gx_path_assign_free(igs->path, &save);
445
gx_path_free(&save, "ustrokepath");
450
/* <with_ucache> upath <userpath> */
451
/* We do all the work in a procedure that is also used to construct */
452
/* the UnpaintedPath user path for ImageType 2 images. */
453
int make_upath(i_ctx_t *i_ctx_p, ref *rupath, gs_state *pgs, gx_path *ppath,
456
zupath(i_ctx_t *i_ctx_p)
460
check_type(*op, t_boolean);
461
return make_upath(i_ctx_p, op, igs, igs->path, op->value.boolval);
464
/* Compute the path length for user path purposes. */
466
path_length_for_upath(const gx_path *ppath)
470
gs_fixed_point pts[3];
472
gx_path_enum_init(&penum, ppath);
473
while ((op = gx_path_enum_next(&penum, pts)) != 0) {
482
case gs_pe_closepath:
486
return_error(e_unregistered);
493
make_upath(i_ctx_t *i_ctx_p, ref *rupath, gs_state *pgs, gx_path *ppath,
496
int size = (with_ucache ? 6 : 5);
504
/* Compute the bounding box. */
505
if ((code = gs_upathbbox(pgs, &bbox, true)) < 0) {
507
* Note: Adobe throws 'nocurrentpoint' error, but the PLRM does
508
* not list this as a possible error from 'upath', so if we are
509
* not in CPSI compatibility mode, we set a reasonable default
512
if (code != e_nocurrentpoint || CPSI_mode)
514
bbox.p.x = bbox.p.y = bbox.q.x = bbox.q.y = 0;
517
code = path_length_for_upath(ppath);
522
return_error(e_limitcheck);
524
code = ialloc_ref_array(rupath, a_all | a_executable, size,
528
/* Construct the path. */
529
next = rupath->value.refs;
531
if ((code = name_enter_string(pgs->memory, "ucache", next)) < 0)
533
r_set_attrs(next, a_executable | l_new);
536
make_real_new(next, bbox.p.x);
537
make_real_new(next + 1, bbox.p.y);
538
make_real_new(next + 2, bbox.q.x);
539
make_real_new(next + 3, bbox.q.y);
541
if ((code = name_enter_string(pgs->memory, "setbbox", next)) < 0)
543
r_set_attrs(next, a_executable | l_new);
548
/* Patch the path in the gstate to set up the enumerator. */
549
gx_path *save_path = pgs->path;
552
gs_path_enum_copy_init(&penum, pgs, false);
553
pgs->path = save_path;
554
while ((op = gs_path_enum_next(&penum, pts)) != 0) {
563
ml:make_real_new(next, pts[0].x);
564
make_real_new(next + 1, pts[0].y);
569
make_real_new(next, pts[0].x);
570
make_real_new(next + 1, pts[0].y);
571
make_real_new(next + 2, pts[1].x);
572
make_real_new(next + 3, pts[1].y);
573
make_real_new(next + 4, pts[2].x);
574
make_real_new(next + 5, pts[2].y);
577
case gs_pe_closepath:
581
return_error(e_unregistered);
583
if ((code = name_enter_string(pgs->memory, opstr, next)) < 0)
585
r_set_attrs(next, a_executable);
594
zgetpath(i_ctx_t *i_ctx_p)
597
int i, code, path_size, leaf_count;
598
ref *main_ref, *operators[5];
601
path_size = code = path_length_for_upath(igs->path);
604
leaf_count = (path_size + max_array_size - 1) / max_array_size;
605
code = ialloc_ref_array(op, a_all, leaf_count, "zgetpath_master");
611
if (dict_find_string(systemdict, "moveto", &operators[1]) <= 0 ||
612
dict_find_string(systemdict, "lineto", &operators[2]) <= 0 ||
613
dict_find_string(systemdict, "curveto", &operators[3]) <= 0 ||
614
dict_find_string(systemdict, "closepath", &operators[4]) <= 0)
615
return_error(e_undefined);
617
main_ref = op->value.refs;
618
for (i = 0; i < leaf_count; i++) {
619
int leaf_size = ( i == leaf_count - 1) ? path_size - i * max_array_size : max_array_size;
620
code = ialloc_ref_array(&main_ref[i], a_all | a_executable, leaf_size, "zgetpath_leaf");
628
static const int oper_count[5] = { 0, 2, 2, 6, 0 };
630
const double *fts[6];
639
main_ref = op->value.refs;
640
gs_path_enum_copy_init(&penum, igs, false);
641
pe = gs_path_enum_next(&penum, pts);
644
for (i = 0; i < leaf_count; i++) {
645
int leaf_size = ( i == leaf_count - 1) ? path_size - i * max_array_size : max_array_size;
646
ref *leaf_ref = main_ref[i].value.refs;
648
for (j = 0; j < leaf_size; j++) {
649
if (k < oper_count[pe])
650
make_real_new(&leaf_ref[j], (float)*fts[k++]);
653
ref_assign(&leaf_ref[j], operators[pe]);
654
pe = gs_path_enum_next(&penum, pts);
658
return_error(e_unregistered);
667
/* ------ Internal routines ------ */
669
/* Append a user path to the current path. */
671
upath_append_aux(os_ptr oppath, i_ctx_t *i_ctx_p, int *pnargs, bool upath_compat)
673
upath_state ups = UPS_INITIAL;
676
if (r_has_type(oppath, t__invalid))
677
return_error(e_stackunderflow);
678
if (!r_is_array(oppath))
679
return_error(e_typecheck);
682
/****** ROUND tx AND ty ******/
684
if ( r_size(oppath) == 2 &&
685
array_get(imemory, oppath, 1, &opcodes) >= 0 &&
686
r_has_type(&opcodes, t_string)
687
) { /* 1st element is operands, 2nd is operators */
694
array_get(imemory, oppath, 0, &operands);
695
code = num_array_format(&operands);
700
opp = opcodes.value.bytes;
701
ocount = r_size(&opcodes);
705
if (opx > UPATH_REPEAT)
706
repcount = opx - UPATH_REPEAT;
707
else if (opx > UPATH_MAX_OP)
708
return_error(e_rangecheck);
709
else { /* operator */
710
const up_data_t data = up_data[opx];
712
*pnargs = 0; /* in case of error */
713
if (upath_compat && opx == upath_op_ucache) {
714
/* CPSI does not complain about incorrect ucache
715
placement, even though PLRM3 says it's illegal. */
716
ups = ups > UPS_UCACHE ? ups : data.state_after;
718
if (!(ups & data.states_before))
719
return_error(e_typecheck);
720
ups = data.state_after;
724
byte opargs = data.num_args;
728
(*pnargs)++; /* in case of error */
729
code = num_array_get(imemory, &operands, format, i++, op);
732
r_set_type_attrs(op, t_integer, 0);
735
r_set_type_attrs(op, t_real, 0);
738
return_error(e_typecheck);
741
code = (*up_ops[opx])(i_ctx_p);
749
} else { /* Ordinary executable array. */
750
const ref *arp = oppath;
751
uint ocount = r_size(oppath);
757
for (; index < ocount; index++) {
764
array_get(imemory, arp, index, &rup);
765
switch (r_type(&rup)) {
773
if (!r_has_attr(&rup, a_executable) ||
774
dict_find(systemdict, &rup, &defp) <= 0 ||
775
r_btype(defp) != t_operator)
776
return_error(e_typecheck); /* all errors = typecheck */
780
xop:if (!r_has_attr(defp, a_executable))
781
return_error(e_typecheck);
782
oproc = real_opproc(defp);
783
for (opx = 0; opx <= UPATH_MAX_OP; opx++)
784
if (oproc == up_ops[opx])
786
if (opx > UPATH_MAX_OP)
787
return_error(e_typecheck);
789
if (argcount != data.num_args)
790
return_error(e_typecheck);
791
if (upath_compat && opx == upath_op_ucache) {
792
/* CPSI does not complain about incorrect ucache
793
placement, even though PLRM3 says it's illegal. */
794
ups = ups > UPS_UCACHE ? ups : data.state_after;
796
if (!(ups & data.states_before))
797
return_error(e_typecheck);
798
ups = data.state_after;
800
code = (*up_ops[opx])(i_ctx_p);
802
if (code == e_nocurrentpoint)
803
return_error(e_rangecheck); /* CET 11-22 */
809
return_error(e_typecheck);
814
return_error(e_typecheck); /* leftover args */
817
if (ups < UPS_SETBBOX)
818
return_error(e_typecheck); /* no setbbox */
819
if (ups == UPS_SETBBOX && upath_compat) {
821
* In CPSI compatibility mode, an empty path with a setbbox also
822
* does a moveto (but only if the path is empty). Since setbbox
823
* was the last operator, its operands are still on the o-stack.
826
return zmoveto(i_ctx_p);
831
upath_append(os_ptr oppath, i_ctx_t *i_ctx_p, bool upath_compat)
834
int code = upath_append_aux(oppath, i_ctx_p, &nargs, upath_compat);
837
/* Pop args on error, to match Adobe interpreters. */
844
/* Append a user path to the current path, and then apply or return */
845
/* a transformation if one is supplied. */
847
upath_stroke(i_ctx_t *i_ctx_p, gs_matrix *pmat, bool upath_compat)
853
if ((code = read_matrix(imemory, op, &mat)) >= 0) {
854
if ((code = upath_append(op - 1, i_ctx_p, upath_compat)) >= 0) {
858
code = gs_concat(igs, &mat);
862
if ((code = upath_append(op, i_ctx_p, upath_compat)) >= 0)
864
gs_make_identity(pmat);
867
return (code < 0 ? code : npop);
870
/* ---------------- Initialization procedure ---------------- */
872
const op_def zupath_l2_op_defs[] =
874
op_def_begin_level2(),
875
/* Insideness testing */
876
{"1ineofill", zineofill},
877
{"1infill", zinfill},
878
{"1instroke", zinstroke},
879
{"2inueofill", zinueofill},
880
{"2inufill", zinufill},
881
{"2inustroke", zinustroke},
883
{"1uappend", zuappend},
884
{"0ucache", zucache},
885
{"1ueofill", zueofill},
888
{"1ustroke", zustroke},
889
{"1ustrokepath", zustrokepath},
890
/* Path access for PDF */
891
{"0.getpath", zgetpath},