/src/ghostpdl/pcl/pxl/pxgstate.c
Line | Count | Source |
1 | | /* Copyright (C) 2001-2024 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 | | /* pxgstate.c */ |
18 | | /* PCL XL graphics state operators */ |
19 | | |
20 | | #include "math_.h" |
21 | | #include "memory_.h" |
22 | | #include "stdio_.h" /* std.h + NULL */ |
23 | | #include "gstypes.h" |
24 | | #include "gsmemory.h" |
25 | | #include "gsstruct.h" |
26 | | #include "pxoper.h" |
27 | | #include "pxstate.h" |
28 | | #include "pxparse.h" |
29 | | #include "gdebug.h" |
30 | | #include "gsstate.h" |
31 | | #include "gscoord.h" |
32 | | #include "gxcspace.h" /* must precede gscolor2.h */ |
33 | | #include "gscie.h" |
34 | | #include "gsicc.h" |
35 | | #include "gsicc_manage.h" |
36 | | #include "gsimage.h" |
37 | | #include "gspath.h" |
38 | | #include "gspath2.h" |
39 | | #include "gsrop.h" |
40 | | #include "gxpath.h" |
41 | | #include "gzstate.h" |
42 | | #include "gscolor2.h" |
43 | | #include "pxptable.h" |
44 | | |
45 | | /* |
46 | | * There is an apparent bug in the LJ5 and LJ6MP firmware that causes |
47 | | * SetClipIntersect with even/odd mode and exterior region to behave the |
48 | | * same as SetClipReplace. To emulate this bug, uncomment the following |
49 | | * #define. |
50 | | */ |
51 | | #define CLIP_INTERSECT_EXTERIOR_REPLACES |
52 | | /* |
53 | | * H-P printers apparently have a maximum dash pattern length of 20. |
54 | | * Our library doesn't impose a limit, but we may as well emulate the |
55 | | * H-P limit here for error checking and Genoa test compatibility. |
56 | | */ |
57 | 0 | #define MAX_DASH_ELEMENTS 20 |
58 | | |
59 | | /* Imported operators */ |
60 | | px_operator_proc(pxRectanglePath); |
61 | | /* Forward references */ |
62 | | px_operator_proc(pxSetClipIntersect); |
63 | | |
64 | | /* Imported color space types */ |
65 | | extern const gs_color_space_type gs_color_space_type_Indexed; /* gscolor2.c */ |
66 | | |
67 | | extern const gs_color_space_type gs_color_space_type_Pattern; /* gspcolor.c */ |
68 | | |
69 | | /* Define 'client procedures' for copying the PCL XL state. */ |
70 | | /* We export the reference count adjustment procedures for the sake of */ |
71 | | /* the state setting code in pxink.c. */ |
72 | | void |
73 | | px_paint_rc_adjust(px_paint_t * ppt, int delta, gs_memory_t * mem) |
74 | 3.45M | { |
75 | 3.45M | if (ppt->type == pxpPattern) { |
76 | | /* |
77 | | * There is no public API for adjusting the reference count of a |
78 | | * gs_client_color, and even the private API requires having a |
79 | | * color space available. We'll need to fix this properly |
80 | | * sooner or later, but for the moment, fake it. |
81 | | */ |
82 | 53.1k | gs_color_space cspace; |
83 | | |
84 | | /* |
85 | | * Even though this is a colored Pattern, and hence does have a |
86 | | * base space, we set has_base_space to false to prevent the |
87 | | * adjust_color_count procedure from trying to call the |
88 | | * adjustment procedures for the base space. |
89 | | */ |
90 | 53.1k | cspace.type = &gs_color_space_type_Pattern; |
91 | 53.1k | cspace.params.pattern.has_base_space = false; |
92 | 53.1k | (*cspace.type->adjust_color_count) (&ppt->value.pattern.color, |
93 | 53.1k | &cspace, delta); |
94 | 53.1k | rc_adjust_only(ppt->value.pattern.pattern, delta, |
95 | 53.1k | "px_paint_rc_adjust"); |
96 | 53.1k | } |
97 | 3.45M | } |
98 | | void |
99 | | px_gstate_rc_adjust(px_gstate_t * pxgs, int delta, gs_memory_t * mem) |
100 | 1.71M | { |
101 | 1.71M | px_paint_rc_adjust(&pxgs->pen, delta, mem); |
102 | 1.71M | px_paint_rc_adjust(&pxgs->brush, delta, mem); |
103 | 1.71M | } |
104 | | static void * |
105 | | px_gstate_client_alloc(gs_memory_t * mem) |
106 | 350k | { |
107 | 350k | px_gstate_t *pxgs = (px_gstate_t *) gs_alloc_bytes(mem, |
108 | 350k | sizeof(px_gstate_t), |
109 | 350k | "px_gstate_alloc"); |
110 | | |
111 | 350k | if (pxgs == 0) |
112 | 0 | return 0; |
113 | | /* Initialize reference-counted pointers and other pointers */ |
114 | | /* needed to establish invariants. */ |
115 | 350k | pxgs->memory = mem; |
116 | 350k | pxgs->halftone.thresholds.data = 0; |
117 | 350k | pxgs->halftone.thresholds.size = 0; |
118 | 350k | pxgs->dither_matrix.data = 0; |
119 | 350k | pxgs->dither_matrix.size = 0; |
120 | 350k | pxgs->brush.type = pxpNull; |
121 | 350k | pxgs->pen.type = pxpNull; |
122 | 350k | px_dict_init(&pxgs->temp_pattern_dict, mem, px_free_pattern); |
123 | 350k | return pxgs; |
124 | 350k | } |
125 | | |
126 | | static int |
127 | | px_gstate_client_copy_for(void *to, void *from, gs_gstate_copy_reason_t reason) |
128 | 684k | { |
129 | 4.10M | #define pxfrom ((px_gstate_t *)from) |
130 | 4.10M | #define pxto ((px_gstate_t *)to) |
131 | 684k | px_gstate_rc_adjust(pxfrom, 1, pxfrom->memory); |
132 | 684k | px_gstate_rc_adjust(pxto, -1, pxto->memory); |
133 | | /* |
134 | | * In the context of the PCL XL code, this routine may be called for |
135 | | * gsave, grestore, or gstate (copying the gstate for makepattern |
136 | | * or Pattern rendering). See gxstate.h for details of the 'from' |
137 | | * and 'to' arguments for each of these cases. |
138 | | * |
139 | | * We have some structures that belong to the individual gstates for |
140 | | * storage management purposes. Currently these are: |
141 | | * dither_matrix, temp_pattern_dict |
142 | | * px_gstate_client_alloc initializes them to an empty state. |
143 | | * For gsave and gstate, the new current gstate or copied gstate |
144 | | * respectively should have the empty structure. For grestore, |
145 | | * we want to swap the structures, because we will then free the |
146 | | * saved gstate (and release the structure that was in the current |
147 | | * gstate before the grestore). |
148 | | * |
149 | | * halftone.thresholds is a different special case. We need to |
150 | | * copy it for gsave and gstate, and free it on grestore. |
151 | | */ |
152 | 684k | { |
153 | 684k | gs_string tmat; |
154 | 684k | gs_string thtt; |
155 | 684k | pl_dict_t tdict; |
156 | 684k | gs_string *phtt; |
157 | | |
158 | 684k | tmat = pxto->dither_matrix; |
159 | 684k | thtt = pxto->halftone.thresholds; |
160 | 684k | tdict = pxto->temp_pattern_dict; |
161 | 684k | *pxto = *pxfrom; |
162 | 684k | switch (reason) { |
163 | 168 | case copy_for_gstate: |
164 | | /* Just put back the (empty) 'to' structures. */ |
165 | 168 | pxto->dither_matrix = tmat; |
166 | 168 | pxto->temp_pattern_dict = tdict; |
167 | 168 | phtt = &pxto->halftone.thresholds; |
168 | 168 | goto copy; |
169 | 342k | case copy_for_gsave: |
170 | | /* Swap the structures, but set the parent of the new */ |
171 | | /* (empty) dictionary to the old one. */ |
172 | 342k | pxfrom->dither_matrix = tmat; |
173 | 342k | pxfrom->temp_pattern_dict = tdict; |
174 | 342k | pl_dict_set_parent(&pxfrom->temp_pattern_dict, |
175 | 342k | &pxto->temp_pattern_dict); |
176 | 342k | phtt = &pxfrom->halftone.thresholds; |
177 | 342k | copy:if (phtt->data) { |
178 | 0 | byte *str = gs_alloc_string(pxfrom->memory, phtt->size, |
179 | 0 | "px_gstate_client_copy(thresholds)"); |
180 | |
|
181 | 0 | if (str == 0) |
182 | 0 | return_error(errorInsufficientMemory); |
183 | 0 | memcpy(str, phtt->data, phtt->size); |
184 | 0 | phtt->data = str; |
185 | 0 | } |
186 | 342k | break; |
187 | 342k | default: /* copy_for_grestore */ |
188 | | /* Swap the structures. */ |
189 | 342k | pxfrom->dither_matrix = tmat; |
190 | 342k | pxfrom->halftone.thresholds = thtt; |
191 | 342k | pxfrom->temp_pattern_dict = tdict; |
192 | 684k | } |
193 | 684k | } |
194 | 684k | return 0; |
195 | 684k | #undef pxfrom |
196 | 684k | #undef pxto |
197 | 684k | } |
198 | | static void |
199 | | px_gstate_client_free(void *old, gs_memory_t * mem, gs_gstate *pgs) |
200 | 350k | { |
201 | 350k | px_gstate_t *pxgs = old; |
202 | | |
203 | 350k | px_dict_release(&pxgs->temp_pattern_dict); |
204 | 350k | if (pxgs->halftone.thresholds.data) |
205 | 0 | gs_free_string(mem, (byte *) pxgs->halftone.thresholds.data, |
206 | 350k | pxgs->halftone.thresholds.size, |
207 | 350k | "px_gstate_free(halftone.thresholds)"); |
208 | 350k | if (pxgs->dither_matrix.data) |
209 | 0 | gs_free_string(mem, (byte *) pxgs->dither_matrix.data, |
210 | 350k | pxgs->dither_matrix.size, |
211 | 350k | "px_gstate_free(dither_matrix)"); |
212 | 350k | px_gstate_rc_adjust(old, -1, mem); |
213 | 350k | gs_free_object(mem, old, "px_gstate_free"); |
214 | 350k | } |
215 | | |
216 | | static const gs_gstate_client_procs px_gstate_procs = { |
217 | | px_gstate_client_alloc, |
218 | | 0, /* copy -- superseded by copy_for */ |
219 | | px_gstate_client_free, |
220 | | px_gstate_client_copy_for |
221 | | }; |
222 | | |
223 | | /* ---------------- Initialization ---------------- */ |
224 | | |
225 | | /* Allocate a px_gstate_t. */ |
226 | | px_gstate_t * |
227 | | px_gstate_alloc(gs_memory_t * mem) |
228 | 8.09k | { |
229 | 8.09k | px_gstate_t *pxgs = px_gstate_client_alloc(mem); |
230 | | |
231 | 8.09k | if (pxgs == 0) |
232 | 0 | return 0; |
233 | 8.09k | pxgs->stack_depth = 0; |
234 | 8.09k | px_gstate_init(pxgs, NULL); |
235 | 8.09k | return pxgs; |
236 | 8.09k | } |
237 | | |
238 | | /* Initialize a px_gstate_t. */ |
239 | | void |
240 | | px_gstate_init(px_gstate_t * pxgs, gs_gstate * pgs) |
241 | 24.2k | { |
242 | 24.2k | pxgs->halftone.method = eDeviceBest; |
243 | 24.2k | pxgs->halftone.set = false; |
244 | 24.2k | pxgs->halftone.origin.x = pxgs->halftone.origin.y = 0; |
245 | | /* halftone.thresholds was initialized at alloc time */ |
246 | 24.2k | px_gstate_reset(pxgs); |
247 | 24.2k | if (pgs) |
248 | 8.09k | gs_gstate_set_client(pgs, pxgs, &px_gstate_procs, true); |
249 | 24.2k | } |
250 | | |
251 | | /* Initialize the graphics state for a page. */ |
252 | | /* Note that this takes a px_state_t, not a px_gstate_t. */ |
253 | | int |
254 | | px_initgraphics(px_state_t * pxs) |
255 | 6.34k | { |
256 | 6.34k | gs_gstate *pgs = pxs->pgs; |
257 | 6.34k | int code; |
258 | | |
259 | 6.34k | px_gstate_reset(pxs->pxgs); |
260 | 6.34k | code = gs_initgraphics(pgs); |
261 | 6.34k | if (code < 0) return code; |
262 | | |
263 | 6.34k | gs_setfilladjust(pgs, 0.5, 0.5); |
264 | | |
265 | 6.34k | { |
266 | 6.34k | gs_point inch; |
267 | 6.34k | float dpi; |
268 | | |
269 | 6.34k | gs_dtransform(pgs, 72.0, 0.0, &inch); |
270 | 6.34k | dpi = fabs(inch.x) + fabs(inch.y); |
271 | | |
272 | | /* Stroke adjustment leads to anomalies at high resolutions. */ |
273 | 6.34k | if (dpi >= 150) |
274 | 0 | gs_setstrokeadjust(pgs, false); |
275 | | |
276 | | /* We need the H-P interpretation of zero-length lines */ |
277 | | /* and of using bevel joins for the segments of flattened curves. */ |
278 | 6.34k | code = gs_setdotlength(pgs, 72.0 / 300, true); |
279 | 6.34k | if (code < 0) return code; |
280 | 6.34k | } |
281 | | /* we always clamp coordinates hp does not seem to report |
282 | | limit checks in paths */ |
283 | 6.34k | gs_setlimitclamp(pgs, true); |
284 | 6.34k | return 0; |
285 | 6.34k | } |
286 | | |
287 | | /* Reset a px_gstate_t, initially or at the beginning of a page. */ |
288 | | void |
289 | | px_gstate_reset(px_gstate_t * pxgs) |
290 | 30.6k | { |
291 | 30.6k | pxgs->brush.type = pxpGray; |
292 | 30.6k | pxgs->brush.value.gray = 0; |
293 | 30.6k | pxgs->char_angle = 0; |
294 | 30.6k | pxgs->char_bold_value = 0; |
295 | 30.6k | pxgs->char_scale.x = pxgs->char_scale.y = 1; |
296 | 30.6k | pxgs->char_shear.x = pxgs->char_shear.y = 0; |
297 | | /* The order of transforms is arbitrary, */ |
298 | | /* because the transforms are all no-ops. */ |
299 | 30.6k | pxgs->char_transforms[0] = pxct_rotate; |
300 | 30.6k | pxgs->char_transforms[1] = pxct_shear; |
301 | 30.6k | pxgs->char_transforms[2] = pxct_scale; |
302 | 30.6k | pxgs->char_sub_mode = eNoSubstitution; |
303 | 30.6k | pxgs->clip_mode = eNonZeroWinding; |
304 | 30.6k | pxgs->color_space = eRGB; |
305 | 30.6k | pxgs->palette.size = 0; |
306 | 30.6k | pxgs->palette.data = 0; |
307 | 30.6k | pxgs->palette_is_shared = false; |
308 | 30.6k | pxgs->base_font = 0; |
309 | | /* halftone_method was set above. */ |
310 | 30.6k | pxgs->fill_mode = eNonZeroWinding; |
311 | 30.6k | pxgs->dashed = false; |
312 | 30.6k | pxgs->pen.type = pxpGray; |
313 | 30.6k | pxgs->pen.value.gray = 0; |
314 | | /* temp_pattern_dict was set at allocation time. */ |
315 | 30.6k | gs_make_identity(&pxgs->text_ctm); |
316 | 30.6k | pxgs->char_matrix_set = false; |
317 | 30.6k | pxgs->symbol_map = 0; |
318 | 30.6k | pxgs->writing_mode = eHorizontal; |
319 | 30.6k | } |
320 | | |
321 | | /* initial clip region note, we don't use gs_initclip() because we |
322 | | give special handling for the XL 1/6" border */ |
323 | | int |
324 | | px_initclip(px_state_t * pxs) |
325 | 2.05k | { |
326 | 2.05k | return gs_initclip(pxs->pgs); |
327 | 2.05k | } |
328 | | |
329 | | static bool |
330 | | px_is_currentcolor_pattern(const gs_gstate * pgs) |
331 | 0 | { |
332 | 0 | return (gs_color_space_num_components(gs_currentcolorspace(pgs)) < 1); |
333 | 0 | } |
334 | | |
335 | | /* Set up the color space information for a bitmap image or pattern. */ |
336 | | int |
337 | | px_image_color_space(gs_image_t * pim, |
338 | | const px_bitmap_params_t * params, |
339 | | const gs_string * palette, const gs_gstate * pgs) |
340 | 0 | { |
341 | |
|
342 | 0 | int depth = params->depth; |
343 | 0 | gs_color_space *pbase_pcs = NULL; |
344 | 0 | gs_color_space *pcs = NULL; |
345 | 0 | bool cie_space = false; |
346 | 0 | int code = 0; |
347 | |
|
348 | 0 | switch (params->color_space) { |
349 | 0 | case eGray: |
350 | 0 | pbase_pcs = gs_cspace_new_DeviceGray(pgs->memory); |
351 | 0 | if (pbase_pcs == NULL) |
352 | 0 | return_error(errorInsufficientMemory); |
353 | 0 | pbase_pcs->cmm_icc_profile_data = pgs->icc_manager->default_gray; |
354 | 0 | pbase_pcs->type = &gs_color_space_type_ICC; |
355 | 0 | rc_increment(pbase_pcs->cmm_icc_profile_data); |
356 | 0 | break; |
357 | 0 | case eRGB: |
358 | 0 | pbase_pcs = gs_cspace_new_DeviceRGB(pgs->memory); |
359 | 0 | if (pbase_pcs == NULL) |
360 | 0 | return_error(errorInsufficientMemory); |
361 | 0 | pbase_pcs->cmm_icc_profile_data = pgs->icc_manager->default_rgb; |
362 | 0 | pbase_pcs->type = &gs_color_space_type_ICC; |
363 | 0 | rc_increment(pbase_pcs->cmm_icc_profile_data); |
364 | 0 | break; |
365 | 0 | case eSRGB: |
366 | 0 | cie_space = true; |
367 | 0 | pbase_pcs = gs_cspace_new_DeviceRGB(pgs->memory); |
368 | 0 | if (pbase_pcs == NULL) |
369 | 0 | return_error(errorInsufficientMemory); |
370 | 0 | pbase_pcs->cmm_icc_profile_data = pgs->icc_manager->default_rgb; |
371 | 0 | pbase_pcs->type = &gs_color_space_type_ICC; |
372 | 0 | rc_increment(pbase_pcs->cmm_icc_profile_data); |
373 | 0 | break; |
374 | 0 | default: |
375 | 0 | return_error(errorIllegalAttributeValue); |
376 | 0 | } |
377 | | |
378 | 0 | if (params->indexed) { |
379 | 0 | pcs = gs_cspace_alloc(pgs->memory, &gs_color_space_type_Indexed); |
380 | 0 | if (pcs == NULL) { |
381 | | /* free the base space also */ |
382 | 0 | rc_decrement(pbase_pcs, "px_image_color_space"); |
383 | 0 | return_error(errorInsufficientMemory); |
384 | 0 | } |
385 | 0 | pcs->base_space = pbase_pcs; |
386 | 0 | pcs->params.indexed.hival = (1 << depth) - 1; |
387 | 0 | pcs->params.indexed.lookup.table.size = palette->size; |
388 | 0 | { |
389 | 0 | uint n = palette->size; |
390 | 0 | byte *p = gs_alloc_string(pgs->memory, n, |
391 | 0 | "px_image_color_space(palette)"); |
392 | |
|
393 | 0 | if (p == 0) { |
394 | 0 | rc_decrement(pbase_pcs, "px_image_color_space"); |
395 | 0 | return_error(errorInsufficientMemory); |
396 | |
|
397 | 0 | } |
398 | 0 | memcpy(p, palette->data, n); |
399 | 0 | pcs->params.indexed.lookup.table.data = p; |
400 | 0 | } |
401 | 0 | pcs->params.indexed.use_proc = 0; |
402 | 0 | } else { |
403 | 0 | pcs = pbase_pcs; |
404 | 0 | } |
405 | 0 | gs_image_t_init(pim, pcs); |
406 | 0 | pim->ColorSpace = pcs; |
407 | 0 | pim->BitsPerComponent = depth; |
408 | 0 | if (params->indexed) |
409 | 0 | pim->Decode[1] = (float)((1 << depth) - 1); |
410 | | /* NB - this needs investigation */ |
411 | 0 | if (cie_space && !px_is_currentcolor_pattern(pgs)) { |
412 | 0 | code = gs_setrgbcolor((gs_gstate *) pgs, 0.0, 0.0, 0.0); |
413 | 0 | } |
414 | 0 | return code; |
415 | 0 | } |
416 | | |
417 | | /* Check the setting of the clipping region. */ |
418 | | #define check_clip_region(par, pxs)\ |
419 | 4.23k | if ( par->pv[0]->value.i == eExterior && pxs->pxgs->clip_mode != eEvenOdd )\ |
420 | 4.23k | return_error(errorClipModeMismatch) |
421 | | |
422 | | /* Record the most recent character transformation. */ |
423 | | static void |
424 | | add_char_transform(px_gstate_t * pxgs, px_char_transform_t trans) |
425 | 17 | { /* Promote this transformation to the head of the list. */ |
426 | 17 | if (pxgs->char_transforms[2] == trans) |
427 | 2 | pxgs->char_transforms[2] = pxgs->char_transforms[1], |
428 | 2 | pxgs->char_transforms[1] = pxgs->char_transforms[0]; |
429 | 15 | else if (pxgs->char_transforms[1] == trans) |
430 | 0 | pxgs->char_transforms[1] = pxgs->char_transforms[0]; |
431 | 17 | pxgs->char_transforms[0] = trans; |
432 | 17 | pxgs->char_matrix_set = false; |
433 | 17 | } |
434 | | |
435 | | /* ---------------- Operators ---------------- */ |
436 | | |
437 | | const byte apxPopGS[] = { 0, 0 }; |
438 | | int |
439 | | pxPopGS(px_args_t * par, px_state_t * pxs) |
440 | 7.34k | { |
441 | 7.34k | gs_gstate *pgs = pxs->pgs; |
442 | 7.34k | px_gstate_t *pxgs = pxs->pxgs; |
443 | 7.34k | int code; |
444 | | |
445 | | /* |
446 | | * Even though the H-P documentation says that a PopGS with an |
447 | | * empty stack is illegal, the implementations apparently simply |
448 | | * do nothing in this case. |
449 | | */ |
450 | 7.34k | if (pxgs->stack_depth == 0) |
451 | 1 | return 0; |
452 | 7.34k | if (pxgs->palette.data && !pxgs->palette_is_shared) { |
453 | 0 | gs_free_string(pxs->memory, (byte *) pxgs->palette.data, |
454 | 0 | pxgs->palette.size, "pxPopGS(palette)"); |
455 | 0 | pxgs->palette.data = 0; |
456 | 0 | } |
457 | 7.34k | px_purge_pattern_cache(pxs, eTempPattern); |
458 | 7.34k | code = gs_grestore(pgs); |
459 | 7.34k | pxs->pxgs = gs_gstate_client_data(pgs); |
460 | 7.34k | return code; |
461 | 7.34k | } |
462 | | |
463 | | const byte apxPushGS[] = { 0, 0 }; |
464 | | int |
465 | | pxPushGS(px_args_t * par, px_state_t * pxs) |
466 | 1.85k | { |
467 | 1.85k | gs_gstate *pgs = pxs->pgs; |
468 | 1.85k | int code = gs_gsave(pgs); |
469 | 1.85k | px_gstate_t *pxgs; |
470 | | |
471 | 1.85k | if (code < 0) |
472 | 0 | return code; |
473 | 1.85k | pxgs = pxs->pxgs = gs_gstate_client_data(pgs); |
474 | 1.85k | if (pxgs->palette.data) |
475 | 0 | pxgs->palette_is_shared = true; |
476 | 1.85k | ++(pxgs->stack_depth); |
477 | 1.85k | return code; |
478 | 1.85k | } |
479 | | |
480 | | /* To restore the interpreters to default state we assemble the |
481 | | following pxl commands and execute them as a stream. |
482 | | |
483 | | ubyte 2 ColorSpace |
484 | | SetColorSpace |
485 | | |
486 | | ubyte_array [ uint16 3 |
487 | | 0000 00 00 00 ... |
488 | | ] RGBColor |
489 | | SetBrushSource |
490 | | |
491 | | uint16 0 CharAngle |
492 | | SetCharAngle |
493 | | |
494 | | SetPageDefaultCTM |
495 | | ubyte_array [ uint16 3 |
496 | | 0000 00 00 00 ... |
497 | | |
498 | | ] RGBColor |
499 | | SetPenSource |
500 | | |
501 | | ubyte 1 PenWidth |
502 | | SetPenWidth |
503 | | |
504 | | ubyte 0 TxMode |
505 | | SetSourceTxMode |
506 | | |
507 | | ubyte 0 TxMode |
508 | | SetPatternTxMode |
509 | | |
510 | | ubyte 252 ROP3 |
511 | | SetROP |
512 | | |
513 | | ubyte 0 LineCapStyle |
514 | | SetLineCap |
515 | | |
516 | | ubyte 0 SolidLine |
517 | | SetLineDash |
518 | | |
519 | | ubyte 0 LineJoinStyle |
520 | | SetLineJoin |
521 | | |
522 | | uint16 10 MiterLength |
523 | | SetMiterLimit |
524 | | |
525 | | real32_xy 1.000000 1.000000 CharScale |
526 | | SetCharScale |
527 | | |
528 | | real32_xy 0.000000 0.000000 CharShear |
529 | | SetCharShear |
530 | | |
531 | | ubyte 0 DeviceMatrix |
532 | | SetHalftoneMethod |
533 | | |
534 | | ubyte 0 ClipMode |
535 | | SetClipMode |
536 | | |
537 | | SetClipToPage |
538 | | |
539 | | NewPath |
540 | | |
541 | | ubyte 0 FillMode |
542 | | SetFillMode |
543 | | |
544 | | */ |
545 | | |
546 | | /* assembled (little endian) stream */ |
547 | | static const byte pxSetDefaultGSstr[] = { |
548 | | 192, 2, 248, 3, 106, 200, 193, 3, 0, 0, 0, 0, 248, 11, 99, 193, |
549 | | 0, 0, 248, 161, 100, 116, 200, 193, 3, 0, 0, 0, 0, 248, 11, 121, 192, 1, |
550 | | 248, 75, 122, 192, |
551 | | 0, 248, 45, 124, 192, 0, 248, 45, 120, 192, 252, 248, 44, 123, 192, 0, |
552 | | 248, 71, 113, 192, |
553 | | 0, 248, 78, 112, 192, 0, 248, 72, 114, 193, 10, 0, 248, 73, 115, 213, 0, |
554 | | 0, 128, 63, 0, 0, 128, 63, |
555 | | 248, 164, 101, 213, 0, 0, 0, 0, 0, 0, 0, 0, 248, 165, 102, 192, 0, 248, |
556 | | 33, 109, 192, 0, 248, |
557 | | 84, 127, 105, 133, 192, 0, 248, 70, 110 |
558 | | }; |
559 | | |
560 | | const byte apxSetDefaultGS[] = { 0, 0 }; |
561 | | int |
562 | | pxSetDefaultGS(px_args_t * par, px_state_t * pxs) |
563 | 1 | { |
564 | 1 | px_parser_state_t *pst = par->parser; |
565 | 1 | px_parser_state_t st; |
566 | 1 | stream_cursor_read r; |
567 | 1 | int code; |
568 | | |
569 | 1 | st.memory = pxs->memory; |
570 | 1 | px_process_init(&st, false /* big_endian */ ); |
571 | | |
572 | 1 | st.macro_state = pst->macro_state | ptsExecStream; |
573 | | |
574 | 1 | stream_cursor_read_init(&r, pxSetDefaultGSstr, sizeof(pxSetDefaultGSstr)); |
575 | | /* we don't expect this to fail */ |
576 | 1 | code = px_process(&st, pxs, &r); |
577 | | |
578 | 1 | pst->macro_state = st.macro_state & ~ptsExecStream; |
579 | 1 | return code; |
580 | 1 | } |
581 | | |
582 | | const byte apxSetClipReplace[] = { |
583 | | pxaClipRegion, 0, 0 |
584 | | }; |
585 | | int |
586 | | pxSetClipReplace(px_args_t * par, px_state_t * pxs) |
587 | 1.66k | { |
588 | 1.66k | int code; |
589 | | |
590 | 1.66k | check_clip_region(par, pxs); |
591 | 1.66k | if ((code = px_initclip(pxs)) < 0) |
592 | 0 | return code; |
593 | 1.66k | return pxSetClipIntersect(par, pxs); |
594 | 1.66k | } |
595 | | |
596 | | const byte apxSetCharAngle[] = { |
597 | | pxaCharAngle, 0, 0 |
598 | | }; |
599 | | int |
600 | | pxSetCharAngle(px_args_t * par, px_state_t * pxs) |
601 | 3.31k | { |
602 | 3.31k | real angle = real_value(par->pv[0], 0); |
603 | 3.31k | px_gstate_t *pxgs = pxs->pxgs; |
604 | | |
605 | 3.31k | if (angle != pxgs->char_angle || pxgs->char_transforms[0] != pxct_rotate) { |
606 | 15 | pxgs->char_angle = angle; |
607 | 15 | add_char_transform(pxgs, pxct_rotate); |
608 | 15 | } |
609 | 3.31k | return 0; |
610 | 3.31k | } |
611 | | |
612 | | /* confusion in the 3.0 spec - this argument identifier WritingMode |
613 | | 173 is now being used by SelectPCLFont */ |
614 | | const byte apxSetCharAttributes[] = { |
615 | | pxaWritingMode, 0, 0 |
616 | | }; |
617 | | int |
618 | | pxSetCharAttributes(px_args_t * par, px_state_t * pxs) |
619 | 0 | { |
620 | 0 | pxeWritingMode_t arg = par->pv[0]->value.i; |
621 | 0 | pxs->pxgs->writing_mode = arg; |
622 | 0 | return 0; |
623 | 0 | } |
624 | | |
625 | | const byte apxSetCharScale[] = { |
626 | | pxaCharScale, 0, 0 |
627 | | }; |
628 | | int |
629 | | pxSetCharScale(px_args_t * par, px_state_t * pxs) |
630 | 1 | { |
631 | 1 | real x_scale = real_value(par->pv[0], 0); |
632 | 1 | real y_scale = real_value(par->pv[0], 1); |
633 | 1 | px_gstate_t *pxgs = pxs->pxgs; |
634 | | |
635 | 1 | if (x_scale != pxgs->char_scale.x || y_scale != pxgs->char_scale.y || |
636 | 1 | pxgs->char_transforms[0] != pxct_scale) { |
637 | 1 | pxgs->char_scale.x = x_scale; |
638 | 1 | pxgs->char_scale.y = y_scale; |
639 | 1 | add_char_transform(pxgs, pxct_scale); |
640 | 1 | } |
641 | 1 | return 0; |
642 | 1 | } |
643 | | |
644 | | const byte apxSetCharShear[] = { |
645 | | pxaCharShear, 0, 0 |
646 | | }; |
647 | | |
648 | | /* experiments indicate the character shearing operands are |
649 | | clamped after range checking though it is not documented in |
650 | | the HP manual. */ |
651 | | |
652 | 2 | #define SHEAR_LIMIT 16383.0 |
653 | | |
654 | | int |
655 | | pxSetCharShear(px_args_t * par, px_state_t * pxs) |
656 | 1 | { |
657 | 1 | real x_shear = real_value(par->pv[0], 0); |
658 | 1 | real y_shear = real_value(par->pv[0], 1); |
659 | 1 | px_gstate_t *pxgs = pxs->pxgs; |
660 | | |
661 | 1 | x_shear = x_shear > SHEAR_LIMIT ? SHEAR_LIMIT : x_shear; |
662 | 1 | y_shear = y_shear > SHEAR_LIMIT ? SHEAR_LIMIT : y_shear; |
663 | | |
664 | 1 | if (x_shear != pxgs->char_shear.x || y_shear != pxgs->char_shear.y || |
665 | 1 | pxgs->char_transforms[0] != pxct_shear) { |
666 | 1 | pxgs->char_shear.x = x_shear; |
667 | 1 | pxgs->char_shear.y = y_shear; |
668 | 1 | add_char_transform(pxgs, pxct_shear); |
669 | 1 | } |
670 | 1 | return 0; |
671 | 1 | } |
672 | | |
673 | | const byte apxSetClipIntersect[] = { |
674 | | pxaClipRegion, 0, 0 |
675 | | }; |
676 | | int |
677 | | pxSetClipIntersect(px_args_t * par, px_state_t * pxs) |
678 | 1.66k | { |
679 | 1.66k | gs_gstate *pgs = pxs->pgs; |
680 | 1.66k | pxeClipRegion_t clip_region = par->pv[0]->value.i; |
681 | 1.66k | int code; |
682 | | |
683 | 1.66k | check_clip_region(par, pxs); |
684 | | |
685 | 1.66k | if (clip_region == eExterior) { |
686 | | /* |
687 | | * We know clip_mode is eEvenOdd, so we can complement the |
688 | | * region defined by the current path by just adding a |
689 | | * rectangle that encloses the entire page. |
690 | | */ |
691 | 0 | gs_rect bbox; |
692 | |
|
693 | 0 | code = gs_gsave(pgs); |
694 | 0 | if (code < 0) |
695 | 0 | return code; |
696 | 0 | px_initclip(pxs); |
697 | 0 | if ((code = gs_clippath(pgs)) < 0 || |
698 | 0 | (code = gs_pathbbox(pgs, &bbox)) < 0) |
699 | 0 | DO_NOTHING; |
700 | 0 | gs_grestore(pgs); |
701 | 0 | if (code < 0 || (code = gs_rectappend(pgs, &bbox, 1)) < 0) |
702 | 0 | return code; |
703 | 0 | #ifdef CLIP_INTERSECT_EXTERIOR_REPLACES |
704 | 0 | px_initclip(pxs); |
705 | 0 | #endif |
706 | 0 | } |
707 | 1.66k | code = (pxs->pxgs->clip_mode == eEvenOdd ? gs_eoclip(pgs) : gs_clip(pgs)); |
708 | 1.66k | if (code < 0) |
709 | 0 | return code; |
710 | | |
711 | 1.66k | return gs_newpath(pgs); |
712 | 1.66k | } |
713 | | |
714 | | const byte apxSetClipRectangle[] = { |
715 | | pxaClipRegion, pxaBoundingBox, 0, 0 |
716 | | }; |
717 | | int |
718 | | pxSetClipRectangle(px_args_t * par, px_state_t * pxs) |
719 | 914 | { |
720 | 914 | px_args_t args; |
721 | 914 | gs_gstate *pgs = pxs->pgs; |
722 | 914 | int code; |
723 | | |
724 | 914 | check_clip_region(par, pxs); |
725 | 914 | gs_newpath(pgs); |
726 | 914 | args.pv[0] = par->pv[1]; |
727 | 914 | if ((code = pxRectanglePath(&args, pxs)) < 0) |
728 | 0 | return code; |
729 | 914 | return pxSetClipReplace(par, pxs); |
730 | 914 | } |
731 | | |
732 | | const byte apxSetClipToPage[] = { 0, 0 }; |
733 | | int |
734 | | pxSetClipToPage(px_args_t * par, px_state_t * pxs) |
735 | 224 | { |
736 | 224 | gs_newpath(pxs->pgs); |
737 | 224 | return px_initclip(pxs); |
738 | 224 | } |
739 | | |
740 | | const byte apxSetCursor[] = { |
741 | | pxaPoint, 0, 0 |
742 | | }; |
743 | | |
744 | | int |
745 | | pxSetCursor(px_args_t * par, px_state_t * pxs) |
746 | 12.8k | { |
747 | 12.8k | return gs_moveto(pxs->pgs, real_value(par->pv[0], 0), |
748 | 12.8k | real_value(par->pv[0], 1)); |
749 | 12.8k | } |
750 | | |
751 | | const byte apxSetCursorRel[] = { |
752 | | pxaPoint, 0, 0 |
753 | | }; |
754 | | |
755 | | int |
756 | | pxSetCursorRel(px_args_t * par, px_state_t * pxs) |
757 | 0 | { |
758 | 0 | return gs_rmoveto(pxs->pgs, real_value(par->pv[0], 0), |
759 | 0 | real_value(par->pv[0], 1));; |
760 | 0 | } |
761 | | |
762 | | /* SetHalftoneMethod is in pxink.c */ |
763 | | const byte apxSetFillMode[] = { |
764 | | pxaFillMode, 0, 0 |
765 | | }; |
766 | | |
767 | | int |
768 | | pxSetFillMode(px_args_t * par, px_state_t * pxs) |
769 | 374 | { |
770 | 374 | pxs->pxgs->fill_mode = par->pv[0]->value.i; |
771 | 374 | return 0; |
772 | 374 | } |
773 | | |
774 | | /* SetFont is in pxfont.c */ |
775 | | const byte apxSetLineDash[] = { |
776 | | 0, pxaLineDashStyle, pxaDashOffset, pxaSolidLine, 0 |
777 | | }; |
778 | | |
779 | | int |
780 | | pxSetLineDash(px_args_t * par, px_state_t * pxs) |
781 | 3 | { |
782 | 3 | px_gstate_t *pxgs = pxs->pxgs; |
783 | 3 | gs_gstate *pgs = pxs->pgs; |
784 | | |
785 | 3 | if (par->pv[0]) { |
786 | 0 | float pattern[MAX_DASH_ELEMENTS * 2]; |
787 | 0 | uint size = par->pv[0]->value.array.size; |
788 | 0 | real offset = (par->pv[1] ? real_value(par->pv[1], 0) : 0.0); |
789 | 0 | int code; |
790 | |
|
791 | 0 | if (par->pv[2]) |
792 | 0 | return_error(errorIllegalAttributeCombination); |
793 | 0 | if (size > MAX_DASH_ELEMENTS || size == 0) |
794 | 0 | return_error(errorIllegalArraySize); |
795 | | |
796 | | /* |
797 | | * The H-P documentation gives no clue about what a negative |
798 | | * dash pattern element is supposed to do. The H-P printers |
799 | | * apparently interpret it as drawing a line backwards in the |
800 | | * current direction (which may extend outside the original |
801 | | * subpath) with the caps inside the line instead of outside; a |
802 | | * dash pattern with a negative total length crashes the LJ 6MP |
803 | | * firmware so badly the printer has to be power cycled! We |
804 | | * take a different approach here: we compensate for negative |
805 | | * elements by propagating them to adjacent positive ones. This |
806 | | * doesn't produce quite the same output as the H-P printers do, |
807 | | * but this is such an obscure feature that we don't think it's |
808 | | * worth the trouble to emulate exactly. |
809 | | */ |
810 | 0 | { |
811 | 0 | uint orig_size = size; |
812 | 0 | int i; |
813 | | |
814 | | /* Acquire the pattern, duplicating it if the length is odd. */ |
815 | 0 | if (size & 1) |
816 | 0 | size <<= 1; |
817 | 0 | for (i = 0; i < size; ++i) |
818 | 0 | pattern[i] = real_elt(par->pv[0], i % orig_size); |
819 | | /* Get rid of negative draws. */ |
820 | 0 | if (pattern[0] < 0) |
821 | 0 | offset -= pattern[0], |
822 | 0 | pattern[size - 1] += pattern[0], |
823 | 0 | pattern[1] += pattern[0], pattern[0] = -pattern[0]; |
824 | 0 | for (i = 2; i < size; i += 2) |
825 | 0 | if (pattern[i] < 0) |
826 | 0 | pattern[i - 1] += pattern[i], |
827 | 0 | pattern[i + 1] += pattern[i], |
828 | 0 | pattern[i] = -pattern[i]; |
829 | | /* |
830 | | * Now propagate negative skips iteratively. Since each step |
831 | | * decreases either the remaining total of negative skips or |
832 | | * the total number of pattern elements, the process is |
833 | | * guaranteed to terminate. |
834 | | */ |
835 | 0 | elim:for (i = 0; i < size; i += 2) { |
836 | 0 | float draw = pattern[i], skip = pattern[i + 1]; |
837 | 0 | int inext, iprev; |
838 | 0 | float next, prev; |
839 | |
|
840 | 0 | if (skip > 0) |
841 | 0 | continue; |
842 | 0 | if (size == 2) { /* => i == 0 */ |
843 | 0 | if ((pattern[0] = draw + skip) <= 0) |
844 | 0 | pattern[0] = -pattern[0]; |
845 | 0 | pattern[1] = 0; |
846 | 0 | break; |
847 | 0 | } |
848 | 0 | inext = (i == size - 2 ? 0 : i + 2); |
849 | 0 | next = pattern[inext]; |
850 | | /* |
851 | | * Consider the sequence D, -S, E, where D and E are draws |
852 | | * and -S is a negative skip. If S <= D, replace the 3 |
853 | | * elements with D - S + E. |
854 | | */ |
855 | 0 | if (draw + skip >= 0) { |
856 | 0 | next += draw + skip; |
857 | 0 | goto shrink; |
858 | 0 | } |
859 | | /* |
860 | | * Otherwise, let T be the skip preceding D. Replace T |
861 | | * with T + D - S. If S > E, replace D, -S, E with E, S - |
862 | | * (D + E), D; otherwise, replace D, -S, E with E. In |
863 | | * both cases, net progress has occurred. |
864 | | */ |
865 | 0 | iprev = (i == 0 ? size - 1 : i - 1); |
866 | 0 | prev = pattern[iprev]; |
867 | 0 | pattern[iprev] = prev + draw + skip; |
868 | 0 | if (-skip > next) { |
869 | 0 | pattern[i] = next; |
870 | 0 | pattern[i + 1] = -(skip + draw + next); |
871 | 0 | pattern[i + 2] = draw; |
872 | 0 | goto elim; |
873 | 0 | } |
874 | 0 | shrink:if (inext == 0) { |
875 | 0 | offset += next - pattern[0]; |
876 | 0 | pattern[0] = next; |
877 | 0 | } else { |
878 | 0 | pattern[i] = next; |
879 | 0 | memmove(&pattern[i + 1], &pattern[i + 3], |
880 | 0 | (size - (i + 3)) * sizeof(pattern[0])); |
881 | 0 | } |
882 | 0 | size -= 2; |
883 | 0 | goto elim; |
884 | 0 | } |
885 | 0 | } |
886 | 0 | code = gs_setdash(pgs, pattern, size, offset); |
887 | 0 | if (code < 0) |
888 | 0 | return code; |
889 | | /* patterns with 0 total skip length are treated as solid |
890 | | line pattern on the LJ6 */ |
891 | 0 | { |
892 | 0 | bool skips_have_length = false; |
893 | 0 | int i; |
894 | |
|
895 | 0 | for (i = 0; i < size; i += 2) |
896 | 0 | if (pattern[i + 1] != 0) { |
897 | 0 | skips_have_length = true; |
898 | 0 | break; |
899 | 0 | } |
900 | 0 | if (skips_have_length == false) { |
901 | 0 | pxgs->dashed = false; |
902 | 0 | return gs_setdash(pgs, NULL, 0, 0.0); |
903 | 0 | } |
904 | 0 | pxgs->dashed = (size != 0); |
905 | 0 | } |
906 | 0 | gs_currentmatrix(pgs, &pxgs->dash_matrix); |
907 | 0 | return 0; |
908 | 3 | } else if (par->pv[2]) { |
909 | 1 | if (par->pv[1]) |
910 | 0 | return_error(errorIllegalAttributeCombination); |
911 | 1 | pxgs->dashed = false; |
912 | 1 | return gs_setdash(pgs, NULL, 0, 0.0); |
913 | 1 | } else |
914 | 2 | return_error(errorMissingAttribute); |
915 | 3 | } |
916 | | |
917 | | const byte apxSetLineCap[] = { |
918 | | pxaLineCapStyle, 0, 0 |
919 | | }; |
920 | | int |
921 | | pxSetLineCap(px_args_t * par, px_state_t * pxs) |
922 | 689 | { |
923 | 689 | static const gs_line_cap cap_map[] = pxeLineCap_to_library; |
924 | | |
925 | 689 | return gs_setlinecap(pxs->pgs, cap_map[par->pv[0]->value.i]); |
926 | 689 | } |
927 | | |
928 | | const byte apxBeginUserDefinedLineCap[] = { 0, 0 }; |
929 | | int |
930 | | pxBeginUserDefinedLineCap(px_args_t * par, px_state_t * pxs) |
931 | 2 | { |
932 | 2 | dmprintf(pxs->memory, "undocumented\n"); |
933 | 2 | return 0; |
934 | 2 | } |
935 | | |
936 | | const byte apxEndUserDefinedLineCap[] = { 0, 0 }; |
937 | | int |
938 | | pxEndUserDefinedLineCap(px_args_t * par, px_state_t * pxs) |
939 | 4 | { |
940 | 4 | dmprintf(pxs->memory, "undocumented\n"); |
941 | 4 | return 0; |
942 | 4 | } |
943 | | |
944 | | const byte apxSetLineJoin[] = { |
945 | | pxaLineJoinStyle, 0, 0 |
946 | | }; |
947 | | |
948 | | int |
949 | | pxSetLineJoin(px_args_t * par, px_state_t * pxs) |
950 | 151 | { |
951 | 151 | static const gs_line_join join_map[] = pxeLineJoin_to_library; |
952 | | |
953 | 151 | return gs_setlinejoin(pxs->pgs, join_map[par->pv[0]->value.i]); |
954 | 151 | } |
955 | | |
956 | | const byte apxSetMiterLimit[] = { |
957 | | pxaMiterLength, 0, 0 |
958 | | }; |
959 | | |
960 | | int |
961 | | pxSetMiterLimit(px_args_t * par, px_state_t * pxs) |
962 | 1 | { |
963 | 1 | float limit = real_value(par->pv[0], 0); |
964 | | |
965 | 1 | if (limit == 0) { /* |
966 | | * H-P printers interpret this to mean use the default value |
967 | | * of 10, even though nothing in the documentation says or |
968 | | * implies this. |
969 | | */ |
970 | 0 | limit = 10; |
971 | 1 | } else { /* PCL XL, but not the library, allows limit values <1. */ |
972 | 1 | if (limit < 1) |
973 | 0 | limit = 1; |
974 | 1 | } |
975 | 1 | return gs_setmiterlimit(pxs->pgs, limit); |
976 | 1 | } |
977 | | |
978 | | const byte apxSetPageDefaultCTM[] = { 0, 0 }; |
979 | | int |
980 | | pxSetPageDefaultCTM(px_args_t * par, px_state_t * pxs) |
981 | 322 | { |
982 | 322 | gs_make_identity(&pxs->pxgs->text_ctm); |
983 | 322 | return gs_setmatrix(pxs->pgs, &pxs->initial_matrix); |
984 | 322 | } |
985 | | |
986 | | const byte apxSetPageOrigin[] = { |
987 | | pxaPageOrigin, 0, 0 |
988 | | }; |
989 | | int |
990 | | pxSetPageOrigin(px_args_t * par, px_state_t * pxs) |
991 | 912 | { |
992 | 912 | return gs_translate(pxs->pgs, real_value(par->pv[0], 0), |
993 | 912 | real_value(par->pv[0], 1)); |
994 | 912 | } |
995 | | |
996 | | const byte apxSetPageRotation[] = { |
997 | | pxaPageAngle, 0, 0 |
998 | | }; |
999 | | int |
1000 | | pxSetPageRotation(px_args_t * par, px_state_t * pxs) |
1001 | 221 | { /* Since the Y coordinate of user space is inverted, */ |
1002 | | /* we must negate rotation angles. */ |
1003 | 221 | real angle = -real_value(par->pv[0], 0); |
1004 | 221 | int code = gs_rotate(pxs->pgs, angle); |
1005 | | |
1006 | 221 | if (code < 0) |
1007 | 0 | return code; |
1008 | | /* Post-multiply the text CTM by the rotation matrix. */ |
1009 | 221 | { |
1010 | 221 | gs_matrix rmat; |
1011 | 221 | px_gstate_t *pxgs = pxs->pxgs; |
1012 | | |
1013 | 221 | gs_make_rotation(angle, &rmat); |
1014 | 221 | gs_matrix_multiply(&pxgs->text_ctm, &rmat, &pxgs->text_ctm); |
1015 | 221 | } |
1016 | 221 | return 0; |
1017 | 221 | } |
1018 | | |
1019 | | const byte apxSetPageScale[] = { |
1020 | | 0, pxaPageScale, pxaMeasure, pxaUnitsPerMeasure, 0 |
1021 | | }; |
1022 | | |
1023 | | int |
1024 | | pxSetPageScale(px_args_t * par, px_state_t * pxs) |
1025 | 227 | { |
1026 | 227 | int code; |
1027 | 227 | real sx = 1; |
1028 | 227 | real sy = 1; |
1029 | 227 | static const real units_conversion_table[3][3] = { |
1030 | 227 | {1, 25.4f, 254}, /* in -> in, mill, 1/10 mill */ |
1031 | 227 | {0.0394f, 1, 10}, /* mill -> in, mill, 1/10 mill */ |
1032 | 227 | {0.00394f, .1f, 1} /* 1/10 mill -> in, mill, 1/10 mill */ |
1033 | 227 | }; |
1034 | | |
1035 | | /* measuure and units of measure. Actually session user units |
1036 | | divided by new user unit, bizarre. */ |
1037 | 227 | if (par->pv[1] && par->pv[2]) { |
1038 | | /* new user measure */ |
1039 | 0 | real nux = real_value(par->pv[2], 0); |
1040 | 0 | real nuy = real_value(par->pv[2], 1); |
1041 | |
|
1042 | 0 | if (nux != 0 && nuy != 0) { |
1043 | | /* new measure */ |
1044 | 0 | pxeMeasure_t mt = par->pv[1]->value.i; |
1045 | | /* convert to session units */ |
1046 | 0 | real factor = units_conversion_table[pxs->measure][mt]; |
1047 | 0 | real sux = nux * factor; |
1048 | 0 | real suy = nuy * factor; |
1049 | |
|
1050 | 0 | sx = pxs->units_per_measure.x / sux; |
1051 | 0 | sy = pxs->units_per_measure.y / suy; |
1052 | | /* check for overflow. NB we should do a better job here */ |
1053 | 0 | if (fabs(sx) > 1000.0) { |
1054 | 0 | dmprintf2(pxs->memory, |
1055 | 0 | "warning probable overflow avoided for scaling factors %f %f\n", |
1056 | 0 | sx, sy); |
1057 | 0 | sx = sy = 1; |
1058 | 0 | } |
1059 | 0 | } |
1060 | 227 | } else if (par->pv[0]) { /* page scale */ |
1061 | 218 | sx = real_value(par->pv[0], 0); |
1062 | 218 | sy = real_value(par->pv[0], 1); |
1063 | 218 | } |
1064 | 227 | code = gs_scale(pxs->pgs, sx, sy); |
1065 | 227 | if (code < 0) |
1066 | 0 | return code; |
1067 | | /* Post-multiply the text CTM by the scale matrix. */ |
1068 | 227 | { |
1069 | 227 | gs_matrix smat; |
1070 | 227 | px_gstate_t *pxgs = pxs->pxgs; |
1071 | | |
1072 | 227 | gs_make_scaling(sx, sy, &smat); |
1073 | 227 | gs_matrix_multiply(&pxgs->text_ctm, &smat, &pxgs->text_ctm); |
1074 | 227 | } |
1075 | 227 | return 0; |
1076 | 227 | } |
1077 | | |
1078 | | const byte apxSetPaintTxMode[] = { |
1079 | | pxaTxMode, 0, 0 |
1080 | | }; |
1081 | | int |
1082 | | pxSetPaintTxMode(px_args_t * par, px_state_t * pxs) |
1083 | 207 | { |
1084 | 207 | gs_settexturetransparent(pxs->pgs, par->pv[0]->value.i == eTransparent); |
1085 | 207 | return 0; |
1086 | 207 | } |
1087 | | |
1088 | | const byte apxSetPenWidth[] = { |
1089 | | pxaPenWidth, 0, 0 |
1090 | | }; |
1091 | | int |
1092 | | pxSetPenWidth(px_args_t * par, px_state_t * pxs) |
1093 | 1 | { |
1094 | 1 | return gs_setlinewidth(pxs->pgs, real_value(par->pv[0], 0)); |
1095 | 1 | } |
1096 | | |
1097 | | const byte apxSetROP[] = { |
1098 | | pxaROP3, 0, 0 |
1099 | | }; |
1100 | | int |
1101 | | pxSetROP(px_args_t * par, px_state_t * pxs) |
1102 | 357 | { |
1103 | | /* 252 here is a magic number. It is the default state of RasterOPs which appears to be |
1104 | | * fully opaque. Since it's the default, and that seems to work, permit it to be set. |
1105 | | */ |
1106 | 357 | if (pxs->high_level_device && !pxs->supports_rasterops && par->pv[0]->value.i != 252) { |
1107 | 0 | errprintf(pxs->memory, "Unsupported use of RasterOP %d detected. Output may not be correct.\n", par->pv[0]->value.i); |
1108 | 0 | return 0; |
1109 | 0 | } |
1110 | 357 | gs_setrasterop(pxs->pgs, (gs_rop3_t) (par->pv[0]->value.i)); |
1111 | 357 | return 0; |
1112 | 357 | } |
1113 | | |
1114 | | const byte apxSetSourceTxMode[] = { |
1115 | | pxaTxMode, 0, 0 |
1116 | | }; |
1117 | | int |
1118 | | pxSetSourceTxMode(px_args_t * par, px_state_t * pxs) |
1119 | 207 | { |
1120 | 207 | gs_setsourcetransparent(pxs->pgs, par->pv[0]->value.i == eTransparent); |
1121 | 207 | return 0; |
1122 | 207 | } |
1123 | | |
1124 | | const byte apxSetCharBoldValue[] = { |
1125 | | pxaCharBoldValue, 0, 0 |
1126 | | }; |
1127 | | int |
1128 | | pxSetCharBoldValue(px_args_t * par, px_state_t * pxs) |
1129 | 0 | { |
1130 | 0 | float arg = real_value(par->pv[0], 0); |
1131 | |
|
1132 | 0 | pxs->pxgs->char_bold_value = arg; |
1133 | 0 | return 0; |
1134 | 0 | } |
1135 | | |
1136 | | const byte apxSetClipMode[] = { |
1137 | | pxaClipMode, 0, 0 |
1138 | | }; |
1139 | | int |
1140 | | pxSetClipMode(px_args_t * par, px_state_t * pxs) |
1141 | 459 | { |
1142 | 459 | pxs->pxgs->clip_mode = par->pv[0]->value.i; |
1143 | 459 | return 0; |
1144 | 459 | } |
1145 | | |
1146 | | const byte apxSetPathToClip[] = { 0, 0 }; |
1147 | | int |
1148 | | pxSetPathToClip(px_args_t * par, px_state_t * pxs) |
1149 | 1 | { |
1150 | 1 | return gs_clippath(pxs->pgs); |
1151 | 1 | } |
1152 | | |
1153 | | const byte apxSetCharSubMode[] = { |
1154 | | pxaCharSubModeArray, 0, 0 |
1155 | | }; |
1156 | | int |
1157 | | pxSetCharSubMode(px_args_t * par, px_state_t * pxs) |
1158 | 0 | { |
1159 | | /* |
1160 | | * It isn't clear from the documentation why the attribute is an |
1161 | | * array rather than just a Boolean, but we have to assume there |
1162 | | * is some reason for this. |
1163 | | */ |
1164 | 0 | const px_value_t *psubs = par->pv[0]; |
1165 | 0 | pxeCharSubModeArray_t arg; |
1166 | |
|
1167 | 0 | if (psubs->value.array.size != 1 || |
1168 | 0 | psubs->value.array.data[0] >= pxeCharSubModeArray_next) |
1169 | 0 | return_error(errorIllegalAttributeValue); |
1170 | 0 | arg = psubs->value.array.data[0]; |
1171 | 0 | if (pxs->pxgs->char_sub_mode != arg) { |
1172 | 0 | pxs->pxgs->char_sub_mode = arg; |
1173 | 0 | px_purge_character_cache(pxs); |
1174 | 0 | } |
1175 | 0 | return 0; |
1176 | 0 | } |