/src/ghostpdl/pcl/pxl/pxpaint.c
Line | Count | Source |
1 | | /* Copyright (C) 2001-2023 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., 39 Mesa Street, Suite 108A, San Francisco, |
13 | | CA 94129, USA, for further information. |
14 | | */ |
15 | | |
16 | | |
17 | | /* pxpaint.c */ |
18 | | /* PCL XL painting operators */ |
19 | | |
20 | | #include "math_.h" |
21 | | #include "stdio_.h" /* std.h + NULL */ |
22 | | #include "pxoper.h" |
23 | | #include "pxstate.h" |
24 | | #include "pxfont.h" /* for px_text */ |
25 | | #include "gsstate.h" |
26 | | #include "gscoord.h" |
27 | | #include "gspaint.h" |
28 | | #include "gspath.h" |
29 | | #include "gspath2.h" |
30 | | #include "gsrop.h" |
31 | | #include "gxfarith.h" |
32 | | #include "gxfixed.h" |
33 | | #include "gxgstate.h" |
34 | | #include "gxmatrix.h" |
35 | | #include "gxpath.h" |
36 | | #include "pxptable.h" |
37 | | #include "pxgstate.h" /* Prototype for px_high_level_pattern */ |
38 | | |
39 | | /* |
40 | | * The H-P documentation says we are supposed to draw rectangles |
41 | | * counter-clockwise on the page, which is clockwise in user space. |
42 | | * However, the LaserJet 6 (and probably the LJ 5 as well) draw rectangles |
43 | | * clockwise! To draw rectangles clockwise, uncomment the following |
44 | | * #define. |
45 | | * clj4550 and clj4600 draw counter-clockwise rectangles |
46 | | */ |
47 | | /*#define DRAW_RECTANGLES_CLOCKWISE*/ |
48 | | /* |
49 | | * The H-P printers do really weird things for arcs, chords, or pies where |
50 | | * the width and/or height of the bounding box is negative. To emulate |
51 | | * their behavior, uncomment the following #define. |
52 | | */ |
53 | | #define REFLECT_NEGATIVE_ARCS |
54 | | |
55 | | /* Forward references */ |
56 | | px_operator_proc(pxNewPath); |
57 | | |
58 | | /* ---------------- Utilities ---------------- */ |
59 | | |
60 | | /* Add lines to the path. line_proc is gs_lineto or gs_rlineto. */ |
61 | | /* Attributes: pxaEndPoint, pxaNumberOfPoints, pxaPointType. */ |
62 | | static int |
63 | | add_lines(px_args_t * par, px_state_t * pxs, |
64 | | int (*line_proc) (gs_gstate *, double, double)) |
65 | 14.6k | { |
66 | 14.6k | int code = 0; |
67 | | |
68 | 14.6k | if (par->pv[0]) { /* Single segment, specified as argument. */ |
69 | 5.95k | if (par->pv[1] || par->pv[2]) |
70 | 0 | return_error(errorIllegalAttributeCombination); |
71 | 5.95k | return (*line_proc) (pxs->pgs, real_value(par->pv[0], 0), |
72 | 5.95k | real_value(par->pv[0], 1)); |
73 | 5.95k | } |
74 | | /* Multiple segments, specified in source data. */ |
75 | 8.73k | if (!(par->pv[1] && par->pv[2])) |
76 | 2 | return_error(errorMissingAttribute); |
77 | 8.73k | { |
78 | 8.73k | int32_t num_points = par->pv[1]->value.i; |
79 | 8.73k | pxeDataType_t type = (pxeDataType_t) par->pv[2]->value.i; |
80 | 8.73k | int point_size = (type == eUByte || type == eSByte ? 2 : 4); |
81 | | |
82 | 176k | while (par->source.position < (ulong)num_points * point_size) { |
83 | 172k | const byte *dp = par->source.data; |
84 | 172k | int px, py; |
85 | | |
86 | 172k | if (par->source.available < point_size) { /* We don't even have one point's worth of source data. */ |
87 | 4.49k | return pxNeedData; |
88 | 4.49k | } |
89 | 167k | switch (type) { |
90 | 0 | case eUByte: |
91 | 0 | px = dp[0]; |
92 | 0 | py = dp[1]; |
93 | 0 | break; |
94 | 164k | case eSByte: |
95 | 164k | px = (int)(dp[0] ^ 0x80) - 0x80; |
96 | 164k | py = (int)(dp[1] ^ 0x80) - 0x80; |
97 | 164k | break; |
98 | 0 | case eUInt16: |
99 | 0 | px = uint16at(dp, pxs->data_source_big_endian); |
100 | 0 | py = uint16at(dp + 2, pxs->data_source_big_endian); |
101 | 0 | break; |
102 | 3.77k | case eSInt16: |
103 | 3.77k | px = sint16at(dp, pxs->data_source_big_endian); |
104 | 3.77k | py = sint16at(dp + 2, pxs->data_source_big_endian); |
105 | 3.77k | break; |
106 | 0 | default: /* can't happen, pacify compiler */ |
107 | 0 | return_error(errorIllegalAttributeValue); |
108 | 167k | } |
109 | 167k | code = (*line_proc) (pxs->pgs, (double) px, (double) py); |
110 | 167k | if (code < 0) |
111 | 0 | break; |
112 | 167k | par->source.position += point_size; |
113 | 167k | par->source.available -= point_size; |
114 | 167k | par->source.data += point_size; |
115 | 167k | } |
116 | 8.73k | } |
117 | 4.24k | return code; |
118 | 8.73k | } |
119 | | |
120 | | /* Add Bezier curves to the path. curve_proc is gs_curveto or gs_rcurveto. */ |
121 | | /* Attributes: pxaNumberOfPoints, pxaPointType, pxaControlPoint1, */ |
122 | | /* pxaControlPoint2, pxaEndPoint. */ |
123 | | static int |
124 | | add_curves(px_args_t * par, px_state_t * pxs, |
125 | | int (*curve_proc) (gs_gstate *, double, double, double, double, |
126 | | double, double)) |
127 | 14.7k | { |
128 | 14.7k | int code = 0; |
129 | | |
130 | 14.7k | if (par->pv[2] && par->pv[3] && par->pv[4]) { /* Single curve, specified as argument. */ |
131 | 0 | if (par->pv[0] || par->pv[1]) |
132 | 0 | return_error(errorIllegalAttributeCombination); |
133 | 0 | return (*curve_proc) (pxs->pgs, real_value(par->pv[2], 0), |
134 | 0 | real_value(par->pv[2], 1), |
135 | 0 | real_value(par->pv[3], 0), |
136 | 0 | real_value(par->pv[3], 1), |
137 | 0 | real_value(par->pv[4], 0), |
138 | 0 | real_value(par->pv[4], 1)); |
139 | 0 | } |
140 | | /* Multiple segments, specified in source data. */ |
141 | 14.7k | else if (par->pv[0] && par->pv[1]) { |
142 | 14.7k | if (par->pv[2] || par->pv[3] || par->pv[4]) |
143 | 1 | return_error(errorIllegalAttributeCombination); |
144 | 14.7k | } else |
145 | 4 | return_error(errorMissingAttribute); |
146 | 14.7k | { |
147 | 14.7k | int32_t num_points = par->pv[0]->value.i; |
148 | 14.7k | pxeDataType_t type = (pxeDataType_t) par->pv[1]->value.i; |
149 | 14.7k | int point_size = (type == eUByte || type == eSByte ? 2 : 4); |
150 | 14.7k | int segment_size = point_size * 3; |
151 | | |
152 | 14.7k | if (num_points % 3) |
153 | 1 | return_error(errorIllegalDataLength); |
154 | 42.4k | while (par->source.position < (ulong)num_points * point_size) { |
155 | 35.1k | const byte *dp = par->source.data; |
156 | 35.1k | int points[6]; |
157 | 35.1k | int i; |
158 | | |
159 | 35.1k | if (par->source.available < point_size * 3) { /* We don't even have one point's worth of source data. */ |
160 | 7.43k | return pxNeedData; |
161 | 7.43k | } |
162 | 27.6k | switch (type) { |
163 | 0 | case eUByte: |
164 | 0 | for (i = 0; i < 6; ++i) |
165 | 0 | points[i] = dp[i]; |
166 | 0 | break; |
167 | 4.10k | case eSByte: |
168 | 28.7k | for (i = 0; i < 6; ++i) |
169 | 24.6k | points[i] = (int)(dp[i] ^ 0x80) - 0x80; |
170 | 4.10k | break; |
171 | 0 | case eUInt16: |
172 | 0 | for (i = 0; i < 12; i += 2) |
173 | 0 | points[i >> 1] = |
174 | 0 | uint16at(dp + i, pxs->data_source_big_endian); |
175 | 0 | break; |
176 | 23.5k | case eSInt16: |
177 | 165k | for (i = 0; i < 12; i += 2) |
178 | 141k | points[i >> 1] |
179 | 141k | = sint16at(dp + i, pxs->data_source_big_endian); |
180 | 23.5k | break; |
181 | 0 | default: /* can't happen, pacify compiler */ |
182 | 0 | return_error(errorIllegalAttributeValue); |
183 | 27.6k | } |
184 | 27.6k | code = (*curve_proc) (pxs->pgs, |
185 | 27.6k | (double) points[0], (double) points[1], |
186 | 27.6k | (double) points[2], (double) points[3], |
187 | 27.6k | (double) points[4], (double) points[5]); |
188 | 27.6k | if (code < 0) |
189 | 1 | break; |
190 | 27.6k | par->source.position += segment_size; |
191 | 27.6k | par->source.available -= segment_size; |
192 | 27.6k | par->source.data += segment_size; |
193 | 27.6k | } |
194 | 14.7k | } |
195 | 7.29k | return code; |
196 | 14.7k | } |
197 | | |
198 | | /* |
199 | | * Set up all the parameters for an arc, chord, ellipse, or pie. If pp3 and |
200 | | * pp4 are NULL, we're filling the entire box. Store the upper left box |
201 | | * corner (for repositioning the cursor), the center, the radius, and the |
202 | | * two angles in *params, and return one of the following (or a negative |
203 | | * error code): |
204 | | */ |
205 | | typedef enum |
206 | | { |
207 | | /* |
208 | | * Arc box is degenerate (zero width and/or height). |
209 | | * Only origin and center have been set. |
210 | | */ |
211 | | arc_degenerate = 0, |
212 | | /* |
213 | | * Arc box is square. No CTM adjustment was required; save_ctm is |
214 | | * not set. |
215 | | */ |
216 | | arc_square, |
217 | | /* |
218 | | * Arc box is rectangular, CTM has been saved and adjusted. |
219 | | */ |
220 | | arc_rectangular |
221 | | } px_arc_type_t; |
222 | | |
223 | | typedef struct px_arc_params_s |
224 | | { |
225 | | gs_point origin; |
226 | | gs_point center; |
227 | | double radius; |
228 | | double ang3, ang4; |
229 | | gs_matrix save_ctm; |
230 | | bool reversed; |
231 | | } px_arc_params_t; |
232 | | |
233 | | static int /* px_arc_type_t or error code */ |
234 | | setup_arc(px_arc_params_t * params, const px_value_t * pbox, |
235 | | const px_value_t * pp3, const px_value_t * pp4, |
236 | | const px_state_t * pxs, bool ellipse) |
237 | 0 | { |
238 | 0 | real x1 = real_value(pbox, 0); |
239 | 0 | real y1 = real_value(pbox, 1); |
240 | 0 | real x2 = real_value(pbox, 2); |
241 | 0 | real y2 = real_value(pbox, 3); |
242 | 0 | real xc = (x1 + x2) * 0.5; |
243 | 0 | real yc = (y1 + y2) * 0.5; |
244 | 0 | real xr, yr; |
245 | 0 | bool rotated; |
246 | 0 | int code; |
247 | |
|
248 | 0 | #ifdef REFLECT_NEGATIVE_ARCS |
249 | 0 | rotated = x1 > x2; |
250 | 0 | params->reversed = rotated ^ (y1 > y2); |
251 | | #else |
252 | | rotated = false; |
253 | | params->reversed = false; |
254 | | #endif |
255 | 0 | if (x1 > x2) { |
256 | 0 | real temp = x1; |
257 | |
|
258 | 0 | x1 = x2; |
259 | 0 | x2 = temp; |
260 | 0 | } |
261 | 0 | if (y1 > y2) { |
262 | 0 | real temp = y1; |
263 | |
|
264 | 0 | y1 = y2; |
265 | 0 | y2 = temp; |
266 | 0 | } |
267 | 0 | params->origin.x = x1; |
268 | 0 | params->origin.y = y1; |
269 | 0 | xr = (x2 - x1) * 0.5; |
270 | 0 | yr = (y2 - y1) * 0.5; |
271 | | /* From what we can gather ellipses are degenerate if both |
272 | | width and height of the bounding box are 0. Other objects |
273 | | behave as expected. A 0 area bounding box is degenerate */ |
274 | 0 | if (ellipse) { |
275 | | /* The bounding box is degenerate, set what we can and exit. */ |
276 | 0 | if (xr == 0 && yr == 0) { |
277 | 0 | params->center.x = xc; |
278 | 0 | params->center.y = yc; |
279 | 0 | return arc_degenerate; |
280 | 0 | } |
281 | 0 | } else { |
282 | 0 | if (xr == 0 || yr == 0) { |
283 | 0 | params->center.x = xc; |
284 | 0 | params->center.y = yc; |
285 | 0 | return arc_degenerate; |
286 | 0 | } |
287 | 0 | } |
288 | | |
289 | 0 | if (pp3 && pp4) { |
290 | 0 | real dx3 = real_value(pp3, 0) - xc; |
291 | 0 | real dy3 = real_value(pp3, 1) - yc; |
292 | 0 | real dx4 = real_value(pp4, 0) - xc; |
293 | 0 | real dy4 = real_value(pp4, 1) - yc; |
294 | |
|
295 | 0 | if ((dx3 == 0 && dy3 == 0) || (dx4 == 0 && dy4 == 0)) |
296 | 0 | return_error(errorIllegalAttributeValue); |
297 | 0 | { |
298 | 0 | double ang3 = atan2((double)(dy3 * xr), |
299 | 0 | (double)(dx3 * yr)) * radians_to_degrees; |
300 | 0 | double ang4 = atan2((double)(dy4 * xr), |
301 | 0 | (double)(dx4 * yr)) * radians_to_degrees; |
302 | |
|
303 | 0 | if (rotated) |
304 | 0 | ang3 += 180, ang4 += 180; |
305 | 0 | params->ang3 = ang3; |
306 | 0 | params->ang4 = ang4; |
307 | 0 | } |
308 | 0 | } |
309 | 0 | params->radius = yr; |
310 | 0 | if (xr == yr) { |
311 | 0 | params->center.x = xc; |
312 | 0 | params->center.y = yc; |
313 | 0 | return arc_square; |
314 | 0 | } else { /* Must adjust the CTM. Move the origin to (xc,yc) */ |
315 | | /* for simplicity. */ |
316 | 0 | if ((code = gs_currentmatrix(pxs->pgs, ¶ms->save_ctm)) < 0 || |
317 | 0 | (code = gs_translate(pxs->pgs, xc, yc)) < 0 || |
318 | 0 | (code = gs_scale(pxs->pgs, xr, yr)) < 0) |
319 | 0 | return code; |
320 | 0 | params->center.x = 0; |
321 | 0 | params->center.y = 0; |
322 | 0 | params->radius = 1.0; |
323 | 0 | return arc_rectangular; |
324 | 0 | } |
325 | 0 | } |
326 | | |
327 | | /* per the nonsense in 5.7.3 (The ROP3 Operands) from the pxl |
328 | | reference manual the following rops are allowed for stroking. */ |
329 | | static bool |
330 | | pxl_allow_rop_for_stroke(gs_gstate * pgs) |
331 | 0 | { |
332 | 0 | gs_rop3_t rop = gs_currentrasterop(pgs); |
333 | |
|
334 | 0 | if (rop == 0 || rop == 160 || rop == 170 || rop == 240 || rop == 250 |
335 | 0 | || rop == 255) |
336 | 0 | return true; |
337 | 0 | return false; |
338 | 0 | } |
339 | | |
340 | | /* Paint (stroke and/or fill) the current path. */ |
341 | | static int |
342 | | paint_path(px_state_t * pxs) |
343 | 7.92k | { |
344 | 7.92k | gs_gstate *pgs = pxs->pgs; |
345 | 7.92k | gx_path *ppath = gx_current_path(pgs); |
346 | 7.92k | px_gstate_t *pxgs = pxs->pxgs; |
347 | 7.92k | bool will_stroke = pxgs->pen.type != pxpNull; |
348 | 7.92k | bool will_fill = pxgs->brush.type != pxpNull; |
349 | | |
350 | 7.92k | int code = 0; |
351 | | /* nothing to do. */ |
352 | 7.92k | if (!will_fill && !will_stroke) |
353 | 0 | return 0; |
354 | | |
355 | 7.92k | if (gx_path_is_void(ppath)) |
356 | 0 | return 0; |
357 | | |
358 | 7.92k | pxs->have_page = true; |
359 | | |
360 | 7.92k | if (will_fill) { |
361 | 7.92k | gx_path *stroke_path = NULL; |
362 | 7.92k | int (*fill_proc) (gs_gstate *) = |
363 | 7.92k | (pxgs->fill_mode == eEvenOdd ? gs_eofill : gs_fill); |
364 | | |
365 | 7.92k | if ((code = px_set_paint(&pxgs->brush, pxs)) < 0) |
366 | 0 | return code; |
367 | | |
368 | | /* if we are also going to stroke the path, store a copy. */ |
369 | 7.92k | if (will_stroke) { |
370 | 0 | stroke_path = gx_path_alloc_shared(ppath, pxs->memory, "paint_path(save_for_stroke)"); |
371 | 0 | if (stroke_path == NULL) |
372 | 0 | return_error(errorInsufficientMemory); |
373 | 0 | gx_path_assign_preserve(stroke_path, ppath); |
374 | 0 | } |
375 | | |
376 | | /* Make a reduced version of the path, and put that back. */ |
377 | 7.92k | code = gx_path_elide_1d(ppath); |
378 | 7.92k | if (code < 0) |
379 | 0 | return code; |
380 | | |
381 | | /* exit here if no stroke or the fill failed. */ |
382 | 7.92k | code = (*fill_proc) (pgs); |
383 | 7.92k | if (code < 0 || !will_stroke) { |
384 | 7.92k | if (stroke_path) |
385 | 0 | gx_path_free(stroke_path, "paint_path(error_with_fill)"); |
386 | 7.92k | return code; |
387 | 7.92k | } |
388 | | |
389 | | /* restore the path for the stroke, will_stroke and hence |
390 | | stroke_path must be set at this point. */ |
391 | 0 | gx_path_assign_free(ppath, stroke_path); |
392 | 0 | } |
393 | | |
394 | | /* |
395 | | * Per the description in the PCL XL reference documentation, |
396 | | * set a standard logical operation and transparency for stroking. |
397 | | * will_stroke is asserted true here. |
398 | | */ |
399 | 0 | { |
400 | 0 | gs_rop3_t save_rop = gs_currentrasterop(pgs); |
401 | 0 | bool save_transparent = gs_currenttexturetransparent(pgs); |
402 | 0 | bool need_restore_rop = false; |
403 | |
|
404 | 0 | if (pxl_allow_rop_for_stroke(pgs) == false) { |
405 | 0 | gs_setrasterop(pgs, rop3_T); |
406 | 0 | gs_settexturetransparent(pgs, false); |
407 | 0 | need_restore_rop = true; |
408 | 0 | } |
409 | 0 | code = px_set_paint(&pxgs->pen, pxs); |
410 | 0 | if (code < 0) |
411 | 0 | DO_NOTHING; |
412 | 0 | code = gs_stroke(pgs); |
413 | | /* Bit hacky. Normally we handle this up at the interpreter level, and for |
414 | | * fill (above) that's how it works. However, px_set_paint() will call |
415 | | * gs_setpattern, which means that the high level pattern we've saved will |
416 | | * not be the one we use here. If we simply returned remap_color, as might be |
417 | | * expected, we would throw an error in the interpreter, and even if we didn't, |
418 | | * when we came back we would do the fill again, which is wasteful. Instead we |
419 | | * will cater for the situation here by calling the high level pattern routine |
420 | | * to install the pattern, then do the stroke again. |
421 | | */ |
422 | 0 | if (code == gs_error_Remap_Color) { |
423 | 0 | code = px_high_level_pattern(pxs->pgs); |
424 | 0 | code = gs_stroke(pgs); |
425 | 0 | } |
426 | 0 | if (code < 0) |
427 | 0 | DO_NOTHING; |
428 | 0 | if (need_restore_rop) { |
429 | 0 | gs_setrasterop(pgs, save_rop); |
430 | 0 | gs_settexturetransparent(pgs, save_transparent); |
431 | 0 | } |
432 | 0 | } |
433 | 0 | return code; |
434 | 7.92k | } |
435 | | |
436 | | /* Paint a shape defined by a one-operator path. */ |
437 | | static int |
438 | | paint_shape(px_args_t * par, px_state_t * pxs, px_operator_proc((*path_op))) |
439 | 704 | { |
440 | | |
441 | 704 | int code; |
442 | 704 | gs_gstate *pgs = pxs->pgs; |
443 | 704 | gs_fixed_point fxp; |
444 | | |
445 | | /* build the path */ |
446 | 704 | if ((code = pxNewPath(par, pxs)) < 0 || |
447 | 704 | (code = (*path_op) (par, pxs)) < 0) |
448 | 0 | return code; |
449 | | |
450 | | /* save position and stroke and or fill the path */ |
451 | 704 | code = gx_path_current_point(gx_current_path(pxs->pgs), &fxp); |
452 | 704 | if (code < 0) |
453 | 0 | return code; |
454 | | |
455 | 704 | code = paint_path(pxs); |
456 | 704 | if (code < 0) |
457 | 0 | return code; |
458 | | |
459 | | /* restore the saved position, and open a new subpath */ |
460 | 704 | code = gx_path_add_point(gx_current_path(pxs->pgs), fxp.x, fxp.y); |
461 | 704 | if (code < 0) |
462 | 0 | return code; |
463 | | |
464 | 704 | return gx_setcurrentpoint_from_path(pgs, gx_current_path(pxs->pgs)); |
465 | 704 | } |
466 | | |
467 | | /* ---------------- Operators ---------------- */ |
468 | | |
469 | | const byte apxCloseSubPath[] = { 0, 0 }; |
470 | | int |
471 | | pxCloseSubPath(px_args_t * par, px_state_t * pxs) |
472 | 2.26k | { |
473 | 2.26k | return gs_closepath(pxs->pgs); |
474 | 2.26k | } |
475 | | |
476 | | const byte apxNewPath[] = { 0, 0 }; |
477 | | int |
478 | | pxNewPath(px_args_t * par, px_state_t * pxs) |
479 | 23.4k | { |
480 | 23.4k | return gs_newpath(pxs->pgs); |
481 | 23.4k | } |
482 | | |
483 | | /* Unlike painting single objects the PaintPath operator preserves the |
484 | | path */ |
485 | | const byte apxPaintPath[] = { 0, 0 }; |
486 | | int |
487 | | pxPaintPath(px_args_t * par, px_state_t * pxs) |
488 | 7.22k | { |
489 | 7.22k | gx_path *ppath = gx_current_path(pxs->pgs); |
490 | 7.22k | gx_path *save_path = |
491 | 7.22k | gx_path_alloc_shared(ppath, pxs->memory, "pxPaintPath"); |
492 | 7.22k | int code; |
493 | | |
494 | 7.22k | if (save_path == 0) |
495 | 0 | return_error(errorInsufficientMemory); |
496 | | |
497 | 7.22k | gx_path_assign_preserve(save_path, ppath); |
498 | 7.22k | code = paint_path(pxs); |
499 | 7.22k | gx_path_assign_free(ppath, save_path); |
500 | | |
501 | 7.22k | if (code >= 0) |
502 | 7.22k | code = gx_setcurrentpoint_from_path(pxs->pgs, ppath); |
503 | | |
504 | 7.22k | return code; |
505 | 7.22k | } |
506 | | |
507 | | const byte apxArcPath[] = { |
508 | | pxaBoundingBox, pxaStartPoint, pxaEndPoint, 0, |
509 | | pxaArcDirection, 0 |
510 | | }; |
511 | | int |
512 | | pxArcPath(px_args_t * par, px_state_t * pxs) |
513 | 0 | { /* |
514 | | * Note that "clockwise" in user space is counter-clockwise on |
515 | | * the page, because the Y coordinate is inverted. |
516 | | */ |
517 | 0 | bool clockwise = (par->pv[3] != 0 && par->pv[3]->value.i == eClockWise); |
518 | 0 | px_arc_params_t params; |
519 | 0 | int code = |
520 | 0 | setup_arc(¶ms, par->pv[0], par->pv[1], par->pv[2], pxs, false); |
521 | 0 | int rcode = code; |
522 | |
|
523 | 0 | if (code >= 0 && code != arc_degenerate) { |
524 | 0 | bool closed = params.ang3 == params.ang4; |
525 | |
|
526 | 0 | clockwise ^= params.reversed; |
527 | 0 | if (closed) { |
528 | 0 | if (clockwise) |
529 | 0 | params.ang4 += 360; |
530 | 0 | else |
531 | 0 | params.ang3 += 360; |
532 | 0 | } |
533 | 0 | code = gs_arc_add(pxs->pgs, !clockwise, params.center.x, |
534 | 0 | params.center.y, params.radius, params.ang3, |
535 | 0 | params.ang4, false); |
536 | 0 | if (code >= 0 && closed) { /* We have to close the path explicitly. */ |
537 | 0 | code = gs_closepath(pxs->pgs); |
538 | 0 | } |
539 | 0 | } |
540 | 0 | if (rcode == arc_rectangular) |
541 | 0 | gs_setmatrix(pxs->pgs, ¶ms.save_ctm); |
542 | 0 | return code; |
543 | 0 | } |
544 | | |
545 | | const byte apxBezierPath[] = { |
546 | | 0, pxaNumberOfPoints, pxaPointType, pxaControlPoint1, pxaControlPoint2, |
547 | | pxaEndPoint, 0 |
548 | | }; |
549 | | int |
550 | | pxBezierPath(px_args_t * par, px_state_t * pxs) |
551 | 10.5k | { |
552 | 10.5k | return add_curves(par, pxs, gs_curveto); |
553 | 10.5k | } |
554 | | |
555 | | const byte apxBezierRelPath[] = { |
556 | | 0, pxaNumberOfPoints, pxaPointType, pxaControlPoint1, pxaControlPoint2, |
557 | | pxaEndPoint, 0 |
558 | | }; |
559 | | int |
560 | | pxBezierRelPath(px_args_t * par, px_state_t * pxs) |
561 | 4.19k | { |
562 | 4.19k | return add_curves(par, pxs, gs_rcurveto); |
563 | 4.19k | } |
564 | | |
565 | | const byte apxChord[] = { |
566 | | pxaBoundingBox, pxaStartPoint, pxaEndPoint, 0, 0 |
567 | | }; |
568 | | px_operator_proc(pxChordPath); |
569 | | int |
570 | | pxChord(px_args_t * par, px_state_t * pxs) |
571 | 0 | { |
572 | 0 | return paint_shape(par, pxs, pxChordPath); |
573 | 0 | } |
574 | | |
575 | | const byte apxChordPath[] = { |
576 | | pxaBoundingBox, pxaStartPoint, pxaEndPoint, 0, 0 |
577 | | }; |
578 | | int |
579 | | pxChordPath(px_args_t * par, px_state_t * pxs) |
580 | 0 | { |
581 | 0 | px_arc_params_t params; |
582 | 0 | int code = |
583 | 0 | setup_arc(¶ms, par->pv[0], par->pv[1], par->pv[2], pxs, false); |
584 | 0 | int rcode = code; |
585 | | |
586 | | /* See ArcPath above for the meaning of "clockwise". */ |
587 | 0 | if (code >= 0 && code != arc_degenerate) { |
588 | 0 | if (params.ang3 == params.ang4) |
589 | 0 | params.ang3 += 360; |
590 | 0 | code = gs_arc_add(pxs->pgs, !params.reversed, |
591 | 0 | params.center.x, params.center.y, |
592 | 0 | params.radius, params.ang3, params.ang4, false); |
593 | 0 | if (code >= 0) |
594 | 0 | code = gs_closepath(pxs->pgs); |
595 | 0 | } |
596 | 0 | if (rcode == arc_rectangular) |
597 | 0 | gs_setmatrix(pxs->pgs, ¶ms.save_ctm); |
598 | 0 | if (code >= 0) |
599 | 0 | code = gs_moveto(pxs->pgs, params.origin.x, params.origin.y); |
600 | 0 | return code; |
601 | |
|
602 | 0 | } |
603 | | |
604 | | const byte apxEllipse[] = { |
605 | | pxaBoundingBox, 0, 0 |
606 | | }; |
607 | | px_operator_proc(pxEllipsePath); |
608 | | int |
609 | | pxEllipse(px_args_t * par, px_state_t * pxs) |
610 | 0 | { |
611 | 0 | return paint_shape(par, pxs, pxEllipsePath); |
612 | 0 | } |
613 | | |
614 | | const byte apxEllipsePath[] = { |
615 | | pxaBoundingBox, 0, 0 |
616 | | }; |
617 | | int |
618 | | pxEllipsePath(px_args_t * par, px_state_t * pxs) |
619 | 0 | { |
620 | 0 | px_arc_params_t params; |
621 | 0 | int code = setup_arc(¶ms, par->pv[0], NULL, NULL, pxs, true); |
622 | 0 | int rcode = code; |
623 | 0 | real a_start = 180.0; |
624 | 0 | real a_end = -180.0; |
625 | | |
626 | | /* swap start and end angles if counter clockwise ellipse */ |
627 | 0 | if (params.reversed) { |
628 | 0 | a_start = -180.0; |
629 | 0 | a_end = 180.0; |
630 | 0 | } |
631 | | /* See ArcPath above for the meaning of "clockwise". */ |
632 | 0 | if (code < 0 || code == arc_degenerate || |
633 | 0 | (code = gs_arc_add(pxs->pgs, !params.reversed, |
634 | 0 | params.center.x, params.center.y, |
635 | 0 | params.radius, a_start, a_end, false)) < 0 || |
636 | | /* We have to close the path explicitly. */ |
637 | 0 | (code = gs_closepath(pxs->pgs)) < 0) |
638 | 0 | DO_NOTHING; |
639 | 0 | if (rcode == arc_rectangular) |
640 | 0 | gs_setmatrix(pxs->pgs, ¶ms.save_ctm); |
641 | 0 | if (code >= 0) |
642 | 0 | code = gs_moveto(pxs->pgs, params.origin.x, params.origin.y); |
643 | 0 | return code; |
644 | 0 | } |
645 | | |
646 | | const byte apxLinePath[] = { |
647 | | 0, pxaEndPoint, pxaNumberOfPoints, pxaPointType, 0 |
648 | | }; |
649 | | int |
650 | | pxLinePath(px_args_t * par, px_state_t * pxs) |
651 | 7.49k | { |
652 | 7.49k | return add_lines(par, pxs, gs_lineto); |
653 | 7.49k | } |
654 | | |
655 | | const byte apxLineRelPath[] = { |
656 | | 0, pxaEndPoint, pxaNumberOfPoints, pxaPointType, 0 |
657 | | }; |
658 | | int |
659 | | pxLineRelPath(px_args_t * par, px_state_t * pxs) |
660 | 7.20k | { |
661 | 7.20k | return add_lines(par, pxs, gs_rlineto); |
662 | 7.20k | } |
663 | | |
664 | | const byte apxPie[] = { |
665 | | pxaBoundingBox, pxaStartPoint, pxaEndPoint, 0, 0 |
666 | | }; |
667 | | px_operator_proc(pxPiePath); |
668 | | int |
669 | | pxPie(px_args_t * par, px_state_t * pxs) |
670 | 0 | { |
671 | 0 | return paint_shape(par, pxs, pxPiePath); |
672 | 0 | } |
673 | | |
674 | | const byte apxPiePath[] = { |
675 | | pxaBoundingBox, pxaStartPoint, pxaEndPoint, 0, 0 |
676 | | }; |
677 | | int |
678 | | pxPiePath(px_args_t * par, px_state_t * pxs) |
679 | 0 | { |
680 | 0 | px_arc_params_t params; |
681 | 0 | int code = |
682 | 0 | setup_arc(¶ms, par->pv[0], par->pv[1], par->pv[2], pxs, false); |
683 | 0 | int rcode = code; |
684 | | |
685 | | /* See ArcPath above for the meaning of "clockwise". */ |
686 | 0 | if (code >= 0 && code != arc_degenerate) { |
687 | 0 | if (params.ang3 == params.ang4) |
688 | 0 | params.ang3 += 360; |
689 | 0 | code = gs_moveto(pxs->pgs, params.center.x, params.center.y); |
690 | 0 | if (code >= 0) { |
691 | 0 | code = gs_arc_add(pxs->pgs, !params.reversed, |
692 | 0 | params.center.x, params.center.y, |
693 | 0 | params.radius, params.ang3, params.ang4, true); |
694 | 0 | } |
695 | 0 | } |
696 | 0 | if (rcode == arc_rectangular) |
697 | 0 | gs_setmatrix(pxs->pgs, ¶ms.save_ctm); |
698 | 0 | if (code < 0 || rcode == arc_degenerate || |
699 | 0 | (code = gs_closepath(pxs->pgs)) < 0 || |
700 | 0 | (code = gs_moveto(pxs->pgs, params.origin.x, params.origin.y)) < 0) |
701 | 0 | DO_NOTHING; |
702 | 0 | return code; |
703 | 0 | } |
704 | | |
705 | | const byte apxRectangle[] = { |
706 | | pxaBoundingBox, 0, 0 |
707 | | }; |
708 | | px_operator_proc(pxRectanglePath); |
709 | | int |
710 | | pxRectangle(px_args_t * par, px_state_t * pxs) |
711 | 704 | { |
712 | 704 | return paint_shape(par, pxs, pxRectanglePath); |
713 | 704 | } |
714 | | |
715 | | const byte apxRectanglePath[] = { |
716 | | pxaBoundingBox, 0, 0 |
717 | | }; |
718 | | int |
719 | | pxRectanglePath(px_args_t * par, px_state_t * pxs) |
720 | 1.81k | { |
721 | 1.81k | double x1, y1, x2, y2; |
722 | 1.81k | gs_fixed_point p1; |
723 | 1.81k | gs_gstate *pgs = pxs->pgs; |
724 | 1.81k | gx_path *ppath = gx_current_path(pgs); |
725 | 1.81k | gs_fixed_point lines[3]; |
726 | | |
727 | 1.81k | #define p2 lines[1] |
728 | 7.27k | #define pctm (&((const gs_gstate *)pgs)->ctm) |
729 | 1.81k | int code; |
730 | | |
731 | 1.81k | set_box_value(x1, y1, x2, y2, par->pv[0]); |
732 | | /* |
733 | | * Rectangles are always drawn in a canonical order. |
734 | | */ |
735 | 1.81k | if (x1 > x2) { |
736 | 2 | double t = x1; |
737 | | |
738 | 2 | x1 = x2; |
739 | 2 | x2 = t; |
740 | 2 | } |
741 | 1.81k | if (y1 > y2) { |
742 | 0 | double t = y1; |
743 | |
|
744 | 0 | y1 = y2; |
745 | 0 | y2 = t; |
746 | 0 | } |
747 | 1.81k | if ((code = gs_point_transform2fixed(pctm, x1, y1, &p1)) < 0 || |
748 | 1.81k | (code = gs_point_transform2fixed(pctm, x2, y2, &p2)) < 0 || |
749 | 1.81k | (code = gs_moveto(pgs, x1, y1)) < 0) |
750 | 0 | return code; |
751 | | #ifdef DRAW_RECTANGLES_CLOCKWISE |
752 | | /* |
753 | | * DRAW_RECTANGLES_CLOCKWISE means clockwise on the page, which is |
754 | | * counter-clockwise in user space. |
755 | | */ |
756 | | if ((code = gs_point_transform2fixed(pctm, x2, y1, &lines[0])) < 0 || |
757 | | (code = gs_point_transform2fixed(pctm, x1, y2, &lines[2])) < 0) |
758 | | return code; |
759 | | #else |
760 | 1.81k | if ((code = gs_point_transform2fixed(pctm, x1, y2, &lines[0])) < 0 || |
761 | 1.81k | (code = gs_point_transform2fixed(pctm, x2, y1, &lines[2])) < 0) |
762 | 0 | return code; |
763 | 1.81k | #endif |
764 | 1.81k | if ((code = gx_path_add_lines(ppath, lines, 3)) < 0) |
765 | 0 | return code; |
766 | 1.81k | return gs_closepath(pgs); |
767 | 1.81k | #undef pctm |
768 | 1.81k | #undef p2 |
769 | 1.81k | } |
770 | | |
771 | | const byte apxRoundRectangle[] = { |
772 | | pxaBoundingBox, pxaEllipseDimension, 0, 0 |
773 | | }; |
774 | | px_operator_proc(pxRoundRectanglePath); |
775 | | int |
776 | | pxRoundRectangle(px_args_t * par, px_state_t * pxs) |
777 | 0 | { |
778 | 0 | return paint_shape(par, pxs, pxRoundRectanglePath); |
779 | 0 | } |
780 | | |
781 | | const byte apxRoundRectanglePath[] = { |
782 | | pxaBoundingBox, pxaEllipseDimension, 0, 0 |
783 | | }; |
784 | | int |
785 | | pxRoundRectanglePath(px_args_t * par, px_state_t * pxs) |
786 | 0 | { |
787 | 0 | double x1, y1, x2, y2; |
788 | 0 | real xr = real_value(par->pv[1], 0) * 0.5; |
789 | 0 | real yr = real_value(par->pv[1], 1) * 0.5; |
790 | 0 | real xd, yd; |
791 | 0 | gs_matrix save_ctm; |
792 | 0 | gs_gstate *pgs = pxs->pgs; |
793 | 0 | int code; |
794 | |
|
795 | 0 | set_box_value(x1, y1, x2, y2, par->pv[0]); |
796 | 0 | xd = x2 - x1; |
797 | 0 | yd = y2 - y1; |
798 | | /* |
799 | | * H-P printers give an error if the points are specified out |
800 | | * of order. |
801 | | */ |
802 | 0 | if (xd < 0 || yd < 0) |
803 | 0 | return_error(errorIllegalAttributeValue); |
804 | 0 | if (xr == 0 || yr == 0) |
805 | 0 | return pxRectanglePath(par, pxs); |
806 | 0 | gs_currentmatrix(pgs, &save_ctm); |
807 | 0 | gs_translate(pgs, x1, y1); |
808 | 0 | if (xr != yr) { /* Change coordinates so the arcs are circular. */ |
809 | 0 | double scale = xr / yr; |
810 | |
|
811 | 0 | if ((code = gs_scale(pgs, scale, 1.0)) < 0) |
812 | 0 | return code; |
813 | 0 | xd *= yr / xr; |
814 | 0 | } |
815 | 0 | #define r yr |
816 | | /* |
817 | | * Draw the rectangle counter-clockwise on the page, which is |
818 | | * clockwise in user space. (This may be reversed if the |
819 | | * coordinates are specified in a non-standard order.) |
820 | | */ |
821 | 0 | if ((code = gs_moveto(pgs, 0.0, r)) < 0 || |
822 | 0 | (code = gs_arcn(pgs, r, yd - r, r, 180.0, 90.0)) < 0 || |
823 | 0 | (code = gs_arcn(pgs, xd - r, yd - r, r, 90.0, 0.0)) < 0 || |
824 | 0 | (code = gs_arcn(pgs, xd - r, r, r, 0.0, 270.0)) < 0 || |
825 | 0 | (code = gs_arcn(pgs, r, r, r, 270.0, 180.0)) < 0 || |
826 | 0 | (code = gs_closepath(pgs)) < 0 || |
827 | 0 | (code = gs_moveto(pgs, 0.0, 0.0)) < 0) |
828 | 0 | return code; |
829 | 0 | #undef r |
830 | 0 | return gs_setmatrix(pgs, &save_ctm); |
831 | 0 | } |
832 | | |
833 | | const byte apxText[] = { |
834 | | pxaTextData, 0, pxaXSpacingData, pxaYSpacingData, 0 |
835 | | }; |
836 | | int |
837 | | pxText(px_args_t * par, px_state_t * pxs) |
838 | 3.16k | { |
839 | 3.16k | { |
840 | 3.16k | int code = px_set_paint(&pxs->pxgs->brush, pxs); |
841 | | |
842 | 3.16k | if (code < 0) |
843 | 0 | return code; |
844 | 3.16k | } |
845 | 3.16k | if (par->pv[0]->value.array.size != 0 && pxs->pxgs->brush.type != pxpNull) |
846 | 3.16k | pxs->have_page = true; |
847 | | |
848 | 3.16k | return px_text(par, pxs, false); |
849 | 3.16k | } |
850 | | |
851 | | const byte apxTextPath[] = { |
852 | | pxaTextData, 0, pxaXSpacingData, pxaYSpacingData, 0 |
853 | | }; |
854 | | int |
855 | | pxTextPath(px_args_t * par, px_state_t * pxs) |
856 | 0 | { |
857 | 0 | int code = px_set_paint(&pxs->pxgs->pen, pxs); |
858 | |
|
859 | 0 | if (code < 0) |
860 | 0 | return code; |
861 | | /* NB this should be refactored with pxText (immediately above) |
862 | | and it is not a good heuristic for detecting a marked page. */ |
863 | 0 | if (par->pv[0]->value.array.size != 0 && pxs->pxgs->pen.type != pxpNull) |
864 | 0 | pxs->have_page = true; |
865 | 0 | return px_text(par, pxs, true); |
866 | 0 | } |