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 /* Simple high level forms */
18 #include "ghost.h"
19 #include "oper.h"
20 #include "gxdevice.h"
21 #include "ialloc.h"
22 #include "idict.h"
23 #include "idparam.h"
24 #include "igstate.h"
25 #include "gxdevsop.h"
26 #include "gscoord.h"
27 #include "gsform1.h"
28 #include "gspath.h"
29 #include "gxpath.h"
30 #include "gzstate.h"
31 #include "store.h"
32 
33 /* support for high level formss */
34 
35 /* [CTM before Form Matrix applied] <<Form dictionary>> .beginform -
36  */
zbeginform(i_ctx_t * i_ctx_p)37 static int zbeginform(i_ctx_t *i_ctx_p)
38 {
39     os_ptr op = osp;
40     gx_device *cdev = gs_currentdevice_inline(igs);
41     int code;
42     float BBox[4], Matrix[6];
43     gs_form_template_t tmplate;
44     gs_point ll, ur;
45     gs_fixed_rect box;
46 
47     check_type(*op, t_dictionary);
48     check_dict_read(*op);
49 
50     code = read_matrix(imemory, op - 1, &tmplate.CTM);
51     if (code < 0)
52         return code;
53 
54     code = dict_floats_param(imemory, op, "BBox", 4, BBox, NULL);
55     if (code < 0)
56         return code;
57     if (code == 0)
58        return_error(gs_error_undefined);
59     tmplate.FormID = -1;
60     tmplate.BBox.p.x = BBox[0];
61     tmplate.BBox.p.y = BBox[1];
62     tmplate.BBox.q.x = BBox[2];
63     tmplate.BBox.q.y = BBox[3];
64 
65     code = dict_floats_param(imemory, op, "Matrix", 6, Matrix, NULL);
66     if (code < 0)
67         return code;
68     if (code == 0)
69        return_error(gs_error_undefined);
70 
71     tmplate.form_matrix.xx = Matrix[0];
72     tmplate.form_matrix.xy = Matrix[1];
73     tmplate.form_matrix.yx = Matrix[2];
74     tmplate.form_matrix.yy = Matrix[3];
75     tmplate.form_matrix.tx = Matrix[4];
76     tmplate.form_matrix.ty = Matrix[5];
77 
78     tmplate.pcpath = igs->clip_path;
79 
80     tmplate.pgs = igs;
81     code = dev_proc(cdev, dev_spec_op)(cdev, gxdso_form_begin,
82                             &tmplate, 0);
83 
84     /* return value > 0 means the device sent us back a matrix
85      * and wants the CTM set to that.
86      */
87     if (code > 0)
88     {
89         gs_setmatrix(igs, &tmplate.CTM);
90         gs_distance_transform(tmplate.BBox.p.x, tmplate.BBox.p.y, &tmplate.CTM, &ll);
91         gs_distance_transform(tmplate.BBox.q.x, tmplate.BBox.q.y, &tmplate.CTM, &ur);
92 
93         /* A form can legitimately have negative co-ordinates in paths
94          * because it can be translated. But we always clip paths to the
95          * page which (clearly) can't have negative co-ordinates. NB this
96          * wouldn't be a problem if we didn't reset the CTM, but that would
97          * break the form capture.
98          * So here we temporarily set the clip to permit negative values,
99          * fortunately this works.....
100          */
101         /* We choose to permit negative values of the same magnitude as the
102          * positive ones.
103          */
104 
105         box.p.x = float2fixed(ll.x);
106         box.p.y = float2fixed(ll.y);
107         box.q.x = float2fixed(ur.x);
108         box.q.y = float2fixed(ur.y);
109 
110         if (box.p.x < 0) {
111             if(box.p.x * -1 > box.q.x)
112                 box.q.x = box.p.x * -1;
113         } else {
114             if (fabs(ur.x) > fabs(ll.x))
115                 box.p.x = box.q.x * -1;
116             else {
117                 box.p.x = float2fixed(ll.x * -1);
118                 box.q.x = float2fixed(ll.x);
119             }
120         }
121         if (box.p.y < 0) {
122             if(box.p.y * -1 > box.q.y)
123                 box.q.y = box.p.y * -1;
124         } else {
125             if (fabs(ur.y) > fabs(ll.y))
126                 box.p.y = box.q.y * -1;
127             else {
128                 box.p.y = float2fixed(ll.y * -1);
129                 box.q.y = float2fixed(ll.y);
130             }
131         }
132         /* This gets undone when we grestore after the form is executed */
133         code = gx_clip_to_rectangle(igs, &box);
134     }
135 
136     pop(2);
137     return code;
138 }
139 
140 /* - .endform -
141  */
zendform(i_ctx_t * i_ctx_p)142 static int zendform(i_ctx_t *i_ctx_p)
143 {
144     gx_device *cdev = gs_currentdevice_inline(igs);
145     int code;
146 
147     code = dev_proc(cdev, dev_spec_op)(cdev, gxdso_form_end,
148                             0, 0);
149     return code;
150 }
151 
152 /*
153  * - .get_form_id <int>
154  */
zget_form_id(i_ctx_t * i_ctx_p)155 static int zget_form_id(i_ctx_t *i_ctx_p)
156 {
157     os_ptr op = osp;
158     gx_device *cdev = gs_currentdevice_inline(igs);
159     int code, ID;
160 
161     code = dev_proc(cdev, dev_spec_op)(cdev, gxdso_get_form_ID,
162                             &ID, sizeof(int));
163 
164     if (code < 0){
165         ID = -1;
166         code = 0;
167     }
168 
169     push(1);
170     make_int(op, ID);
171     return code;
172 }
173 
174 /*
175  * [matrix] <dict> <int> .repeatform -
176  */
zrepeatform(i_ctx_t * i_ctx_p)177 static int zrepeatform(i_ctx_t *i_ctx_p)
178 {
179     os_ptr op = osp;
180     gx_device *cdev = gs_currentdevice_inline(igs);
181     int code;
182     gs_form_template_t tmplate;
183     float BBox[4], Matrix[6];
184 
185     check_type(*op, t_integer);
186 
187     code = read_matrix(imemory, op - 2, &tmplate.CTM);
188     if (code < 0)
189         return code;
190 
191     check_type(op[- 1], t_dictionary);
192     check_dict_read(*(op - 1));
193 
194     code = dict_floats_param(imemory, op - 1, "BBox", 4, BBox, NULL);
195     if (code < 0)
196         return code;
197     if (code == 0)
198        return_error(gs_error_undefined);
199 
200     tmplate.BBox.p.x = BBox[0];
201     tmplate.BBox.p.y = BBox[1];
202 
203     code = dict_floats_param(imemory, op - 1, "Matrix", 6, Matrix, NULL);
204     if (code < 0)
205         return code;
206     if (code == 0)
207        return_error(gs_error_undefined);
208 
209     tmplate.form_matrix.xx = Matrix[0];
210     tmplate.form_matrix.xy = Matrix[1];
211     tmplate.form_matrix.yx = Matrix[2];
212     tmplate.form_matrix.yy = Matrix[3];
213     tmplate.form_matrix.tx = Matrix[4];
214     tmplate.form_matrix.ty = Matrix[5];
215 
216     tmplate.pcpath = igs->clip_path;
217 
218     tmplate.FormID = op->value.intval;
219 
220     code = dev_proc(cdev, dev_spec_op)(cdev, gxdso_repeat_form,
221                             &tmplate, sizeof(gs_form_template_t));
222 
223     pop(3);
224     return code;
225 }
226 
227 const op_def zform_op_defs[] =
228 {
229     {"0.beginform", zbeginform},
230     {"0.endform", zendform},
231     {"0.get_form_id", zget_form_id},
232     {"3.repeatform", zrepeatform},
233 op_def_end(0)
234 };
235