/src/ghostpdl/pcl/pxl/pxpthr.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 | | /* pxpthr.c.c */ |
18 | | /* PCL XL passtrough mode */ |
19 | | #include "stdio_.h" /* std.h + NULL */ |
20 | | #include "gsdevice.h" |
21 | | #include "gstypes.h" |
22 | | #include "gspath.h" |
23 | | #include "gscoord.h" |
24 | | #include "gsfont.h" |
25 | | #include "gsstate.h" |
26 | | #include "gsicc_manage.h" |
27 | | #include "pcommand.h" |
28 | | #include "pgmand.h" |
29 | | #include "pcstate.h" |
30 | | #include "pcfont.h" |
31 | | #include "pcparse.h" |
32 | | #include "pctop.h" |
33 | | #include "pcpage.h" |
34 | | #include "pcdraw.h" |
35 | | #include "pxoper.h" |
36 | | #include "pxstate.h" |
37 | | #include "pxfont.h" |
38 | | #include "pxgstate.h" |
39 | | #include "pxpthr.h" |
40 | | #include "pxparse.h" |
41 | | #include "plfont.h" |
42 | | #include "pjtop.h" |
43 | | #include "pxptable.h" |
44 | | |
45 | | |
46 | | /* forward decl */ |
47 | | void pxpcl_release(px_state_t* pxs); |
48 | | |
49 | | void pxpcl_pagestatereset(px_state_t* pxs); |
50 | | |
51 | | /* NB: tests for this function are used to flag pxl snippet mode |
52 | | */ |
53 | | static int |
54 | | pcl_end_page_noop(pcl_state_t * pcs, int num_copies, int flush) |
55 | 0 | { |
56 | 0 | return pxPassThrough; |
57 | 0 | } |
58 | | |
59 | | /* set variables other than setting the page device that do not |
60 | | default to pcl reset values */ |
61 | | int |
62 | | pxPassthrough_pcl_state_nonpage_exceptions(px_state_t * pxs) |
63 | 5 | { |
64 | | /* xl cursor -> pcl cursor position */ |
65 | 5 | gs_point xlcp, pclcp, dp; |
66 | 5 | int code; |
67 | | |
68 | | /* make the pcl ctm active, after resets the hpgl/2 ctm is |
69 | | active. */ |
70 | 5 | code = pcl_set_graphics_state(pxs->pcs); |
71 | 5 | if (code < 0) { |
72 | 0 | return code; |
73 | 0 | } |
74 | | |
75 | | /* xl current point -> device point -> pcl current |
76 | | point. If anything fails we assume the current |
77 | | point is not valid and use the cap from the pcl |
78 | | state initialization - pcl's origin */ |
79 | 5 | if (gs_currentpoint(pxs->pgs, &xlcp) || |
80 | 3 | gs_transform(pxs->pgs, xlcp.x, xlcp.y, &dp) || |
81 | 3 | gs_itransform(pxs->pcs->pgs, dp.x, dp.y, &pclcp)) { |
82 | 2 | pxs->pcs->cap.x = 0; |
83 | 2 | pxs->pcs->cap.y = inch2coord(2.0 / 6.0); /* 1/6" off by 2x in resolution. */ |
84 | 2 | if (gs_debug_c('i')) |
85 | 2 | dmprintf2(pxs->memory, |
86 | 2 | "passthrough: changing cap NO currentpoint (%d, %d) \n", |
87 | 2 | pxs->pcs->cap.x, pxs->pcs->cap.y); |
88 | 3 | } else { |
89 | 3 | if (gs_debug_c('i')) |
90 | 3 | dmprintf8(pxs->memory, |
91 | 3 | "passthrough: changing cap from (%d,%d) (%d,%d) (%d, %d) (%d, %d) \n", |
92 | 3 | pxs->pcs->cap.x, pxs->pcs->cap.y, (coord) xlcp.x, |
93 | 3 | (coord) xlcp.y, (coord) dp.x, (coord) dp.y, |
94 | 3 | (coord) pclcp.x, (coord) pclcp.y); |
95 | 3 | pxs->pcs->cap.x = (coord) pclcp.x; |
96 | 3 | pxs->pcs->cap.y = (coord) pclcp.y; |
97 | 3 | } |
98 | 5 | if (pxs->pcs->underline_enabled) |
99 | 0 | pxs->pcs->underline_start = pxs->pcs->cap; |
100 | | |
101 | | |
102 | 5 | pxs->char_angle = pxs->pxgs->char_angle; |
103 | 5 | pxs->char_shear.x = pxs->pxgs->char_shear.x; |
104 | 5 | pxs->char_shear.y = pxs->pxgs->char_shear.y; |
105 | 5 | pxs->char_scale.x = pxs->pxgs->char_scale.x; |
106 | 5 | pxs->char_scale.y = pxs->pxgs->char_scale.y; |
107 | 5 | pxs->char_bold_value = pxs->pxgs->char_bold_value; |
108 | | |
109 | 5 | return 0; |
110 | 5 | } |
111 | | |
112 | | /* retrieve the current pcl state and initialize pcl */ |
113 | | static int |
114 | | pxPassthrough_init(px_state_t * pxs) |
115 | 5 | { |
116 | 5 | int code; |
117 | | |
118 | 5 | if (gs_debug_c('i')) |
119 | 5 | dmprintf(pxs->memory, "passthrough: initializing global pcl state\n"); |
120 | 5 | pxs->pcs = pcl_get_gstate(pxs->pcls); |
121 | | |
122 | 5 | if (pxs->have_page) { |
123 | 3 | if (gs_debug_c('i')) |
124 | 3 | dmprintf(pxs->memory, "passthrough: snippet mode\n"); |
125 | | /* disable an end page in pcl, also used to flag in snippet mode */ |
126 | 3 | pxs->pcs->end_page = pcl_end_page_noop; |
127 | 3 | } |
128 | | |
129 | | /* default to pcl5c */ |
130 | 5 | pxs->pcs->personality = 0; |
131 | | /* for now we do not support intepolation in XL passthrough mode. */ |
132 | 5 | pxs->pcs->interpolate = false; |
133 | | /* we don't see a nice way to support the following options with |
134 | | passthrough at this time (NB) */ |
135 | 5 | pxs->pcs->page_set_on_command_line = false; |
136 | 5 | pxs->pcs->res_set_on_command_line = false; |
137 | 5 | pxs->pcs->high_level_device = false; |
138 | 5 | pxs->pcs->scanconverter = GS_SCANCONVERTER_DEFAULT; |
139 | | |
140 | 5 | { |
141 | 5 | char buf[100]; |
142 | 5 | int ret; |
143 | 5 | stream_cursor_read r; |
144 | | |
145 | 5 | ret = |
146 | 5 | gs_snprintf(buf, sizeof(buf), |
147 | 5 | "@PJL SET PAPERLENGTH = %d\n@PJL SET PAPERWIDTH = %d\n", |
148 | 5 | (int)(pxs->media_dims.y * 10 + .5), |
149 | 5 | (int)(pxs->media_dims.x * 10 + .5)); |
150 | | |
151 | | /* There is no reason gs_snprintf should fail, but to shut coverity up... */ |
152 | 5 | if (ret > 0) { |
153 | 5 | stream_cursor_read_init(&r, (const byte *)buf, ret); |
154 | 5 | pjl_proc_process(pxs->pjls, &r); |
155 | 5 | } |
156 | 5 | } |
157 | | |
158 | 5 | pxs->pcs->xfm_state.paper_size = pcl_get_default_paper(pxs->pcs); |
159 | | |
160 | | /* initialize pcl and install xl's page device in pcl's state */ |
161 | 5 | pcl_init_state(pxs->pcs, pxs->memory); |
162 | 5 | code = gs_setdevice_no_erase(pxs->pcs->pgs, gs_currentdevice(pxs->pgs)); |
163 | 5 | if (code < 0) |
164 | 0 | return code; |
165 | | |
166 | | /* yet another reset with the new page device */ |
167 | 5 | pxs->pcs->xfm_state.paper_size = pcl_get_default_paper(pxs->pcs); |
168 | | |
169 | | /* set the parser state and initialize the pcl parser */ |
170 | 5 | pxs->pcl_parser_state.definitions = pxs->pcs->pcl_commands; |
171 | 5 | pxs->pcl_parser_state.hpgl_parser_state = &pxs->gl_parser_state; |
172 | 5 | pcl_process_init(&pxs->pcl_parser_state, pxs->pcs); |
173 | | /* default 600 to match XL allow PCL to override */ |
174 | 5 | pxs->pcs->uom_cp = 7200L / 600L; |
175 | 5 | return gs_setgray(pxs->pcs->pgs, 0); |
176 | 5 | } |
177 | | |
178 | | static int |
179 | | pxPassthrough_setpagestate(px_state_t * pxs) |
180 | 5 | { |
181 | 5 | int code = 0; |
182 | | |
183 | | /* by definition we are in "snippet mode" if pxl has dirtied |
184 | | the page */ |
185 | 5 | if (pxs->have_page) { |
186 | 3 | if (gs_debug_c('i')) |
187 | 3 | dmprintf(pxs->memory, "passthrough: snippet mode\n"); |
188 | | /* disable an end page in pcl, also used to flag in snippet mode */ |
189 | 3 | pxs->pcs->end_page = pcl_end_page_noop; |
190 | | /* set the page size and orientation. Really just sets |
191 | | the page tranformation does not feed a page (see noop |
192 | | above) */ |
193 | 3 | code = pcl_new_logical_page_for_passthrough(pxs->pcs, |
194 | 3 | (int)pxs->orientation, |
195 | 3 | &pxs->media_dims); |
196 | | |
197 | 3 | if (gs_debug_c('i')) |
198 | 3 | dmprintf2(pxs->memory, |
199 | 3 | "passthrough: snippet mode changing orientation from %d to %d\n", |
200 | 3 | pxs->pcs->xfm_state.lp_orient, (int)pxs->orientation); |
201 | | |
202 | 3 | } else { /* not snippet mode - full page mode */ |
203 | | /* pcl can feed the page and presumedely pcl commands will |
204 | | be used to set pcl's state. */ |
205 | 2 | pxs->pcs->end_page = pcl_end_page_top; |
206 | | /* clean the pcl page if it was marked by a previous snippet |
207 | | and set to full page mode. */ |
208 | 2 | pxs->pcs->page_marked = 0; |
209 | 2 | code = pcl_new_logical_page_for_passthrough(pxs->pcs, |
210 | 2 | (int)pxs->orientation, |
211 | 2 | &pxs->media_dims); |
212 | 2 | if (gs_debug_c('i')) |
213 | 2 | dmprintf(pxs->memory, "passthrough: full page mode\n"); |
214 | 2 | } |
215 | 5 | return code; |
216 | 5 | } |
217 | | |
218 | | const byte apxPassthrough[] = { 0, 0 }; |
219 | | |
220 | | int |
221 | | pxPassthrough(px_args_t * par, px_state_t * pxs) |
222 | 5 | { |
223 | 5 | stream_cursor_read r; |
224 | 5 | int code = 0; |
225 | 5 | uint used; |
226 | | |
227 | | /* apparently if there is no open data source we open one. By the |
228 | | spec this should already be open, in practice it is not. */ |
229 | 5 | if (!pxs->data_source_open) { |
230 | 0 | if (gs_debug_c('i')) |
231 | 0 | dmprintf(pxs->memory, |
232 | 0 | "passthrough: data source not open upon entry\n"); |
233 | 0 | pxs->data_source_open = true; |
234 | 0 | pxs->data_source_big_endian = true; |
235 | 0 | } |
236 | | |
237 | | /* source available is part of the equation to determine if this |
238 | | operator is being called for the first time */ |
239 | 5 | if (par->source.available == 0) { |
240 | 5 | if (par->source.phase == 0) { |
241 | 5 | if (gs_debug_c('i')) |
242 | 5 | dmprintf(pxs->memory, |
243 | 5 | "passthrough starting getting more data\n"); |
244 | | |
245 | 5 | if (!pxs->pcs) |
246 | 5 | pxPassthrough_init(pxs); |
247 | | |
248 | | /* this is the first passthrough on this page */ |
249 | 5 | if (pxs->pass_first) { |
250 | 5 | code = pxPassthrough_setpagestate(pxs); |
251 | 5 | if (code < 0) |
252 | 0 | return code; |
253 | 5 | code = pxPassthrough_pcl_state_nonpage_exceptions(pxs); |
254 | 5 | if (code < 0) |
255 | 0 | return code; |
256 | 5 | pxs->pass_first = false; |
257 | 5 | } else { |
258 | | /* there was a previous passthrough check if there were |
259 | | any intervening XL commands */ |
260 | 0 | if (pxs->this_pass_contiguous == false) { |
261 | 0 | code = pxPassthrough_pcl_state_nonpage_exceptions(pxs); |
262 | 0 | if (code < 0) |
263 | 0 | return code; |
264 | 0 | } |
265 | 0 | } |
266 | 5 | par->source.phase = 1; |
267 | 5 | } |
268 | 5 | return pxNeedData; |
269 | 5 | } |
270 | | |
271 | | /* set pcl data stream pointers to xl's and process this batch of data. */ |
272 | 0 | r.ptr = par->source.data - 1; |
273 | 0 | r.limit = par->source.data + par->source.available - 1; |
274 | 0 | code = pcl_process(&pxs->pcl_parser_state, pxs->pcs, &r); |
275 | | /* updata xl's parser position to reflect what pcl has consumed. */ |
276 | 0 | used = (r.ptr + 1 - par->source.data); |
277 | 0 | par->source.available -= used; |
278 | 0 | par->source.data = r.ptr + 1; |
279 | |
|
280 | 0 | if (code < 0) { |
281 | 0 | dmprintf1(pxs->memory, "passthrough: error return %d\n", code); |
282 | 0 | return code; |
283 | 0 | } |
284 | | /* always return need data and we exit at the top when the data is |
285 | | exhausted. */ |
286 | 0 | { |
287 | 0 | if (used > px_parser_data_left(par->parser)) { |
288 | 0 | dmprintf(pxs->memory, "error: read past end of stream\n"); |
289 | 0 | return -1; |
290 | 0 | } else if (used < px_parser_data_left(par->parser)) { |
291 | 0 | return pxNeedData; |
292 | 0 | } else { |
293 | | /* end of operator and data */ |
294 | 0 | return 0; |
295 | 0 | } |
296 | 0 | } |
297 | 0 | } |
298 | | |
299 | | void |
300 | | pxpcl_pagestatereset(px_state_t* pxs) |
301 | 7.24k | { |
302 | 7.24k | pxs->pass_first = true; |
303 | 7.24k | if (pxs->pcs) { |
304 | 15 | pxs->pcs->xfm_state.left_offset_cp = 0.0; |
305 | 15 | pxs->pcs->xfm_state.top_offset_cp = 0.0; |
306 | 15 | pxs->pcs->margins.top = 0; |
307 | 15 | pxs->pcs->margins.left = 0; |
308 | 15 | } |
309 | 7.24k | } |
310 | | |
311 | | void |
312 | | pxpcl_release(px_state_t * pxs) |
313 | 4.82k | { |
314 | 4.82k | if (pxs->pcs) { |
315 | 5 | if (gs_debug_c('i')) |
316 | 5 | dmprintf(pxs->pcs->memory, |
317 | 5 | "passthrough: releasing global pcl state\n"); |
318 | 5 | pcl_grestore(pxs->pcs); |
319 | 5 | gs_grestore_only(pxs->pcs->pgs); |
320 | 5 | gs_nulldevice(pxs->pcs->pgs); |
321 | 5 | pxs->pcs->end_page = pcl_end_page_top; /* pcl_end_page handling */ |
322 | 5 | pxpcl_pagestatereset(pxs); |
323 | 5 | pxs->pcs = NULL; |
324 | 5 | pxs->this_pass_contiguous = false; |
325 | 5 | pxs->pass_first = true; |
326 | 5 | pxs->char_angle = 0; |
327 | 5 | pxs->char_shear.x = 0; |
328 | 5 | pxs->char_shear.y = 0; |
329 | 5 | pxs->char_scale.x = 1.0; |
330 | 5 | pxs->char_scale.y = 1.0; |
331 | 5 | pxs->char_bold_value = 0.0; |
332 | 5 | } |
333 | 4.82k | } |
334 | | |
335 | | /* the pxl parser must give us this information */ |
336 | | void |
337 | | pxpcl_passthroughcontiguous(px_state_t * pxs, bool cont) |
338 | 38 | { |
339 | 38 | pxs->this_pass_contiguous = cont; |
340 | 38 | } |
341 | | |
342 | | /* copy state from pcl to pxl after a non-snippet passthrough |
343 | | */ |
344 | | void |
345 | | pxpcl_endpassthroughcontiguous(px_state_t * pxs) |
346 | 0 | { |
347 | 0 | if (pxs->pcs->end_page == pcl_end_page_top && |
348 | 0 | pxs->pcs->page_marked && |
349 | 0 | pxs->orientation != pxs->pcs->xfm_state.lp_orient) { |
350 | | |
351 | | /* end of pcl whole job; need to reflect pcl orientation changes */ |
352 | 0 | pxs->orientation = pxs->pcs->xfm_state.lp_orient; |
353 | 0 | pxBeginPageFromPassthrough(pxs); |
354 | 0 | } |
355 | |
|
356 | 0 | pxs->pxgs->char_angle = pxs->char_angle; |
357 | 0 | pxs->pxgs->char_shear.x = pxs->char_shear.x; |
358 | 0 | pxs->pxgs->char_shear.y = pxs->char_shear.y; |
359 | 0 | pxs->pxgs->char_scale.x = pxs->char_scale.x; |
360 | 0 | pxs->pxgs->char_scale.y = pxs->char_scale.y; |
361 | 0 | pxs->pxgs->char_bold_value = pxs->char_bold_value; |
362 | 0 | } |
363 | | int |
364 | | pxpcl_selectfont(px_args_t * par, px_state_t * pxs) |
365 | 0 | { |
366 | 0 | int code; |
367 | 0 | stream_cursor_read r; |
368 | 0 | const px_value_t *pstr = par->pv[3]; |
369 | 0 | const byte *str = (const byte *)pstr->value.array.data; |
370 | 0 | uint len = pstr->value.array.size; |
371 | 0 | px_gstate_t *pxgs = pxs->pxgs; |
372 | 0 | pcl_font_selection_t *pfp; |
373 | |
|
374 | 0 | if (!pxs->pcs) |
375 | 0 | pxPassthrough_init(pxs); |
376 | | |
377 | | /* this is the first passthrough on this page */ |
378 | 0 | if (pxs->pass_first) { |
379 | 0 | code = pxPassthrough_setpagestate(pxs); |
380 | 0 | if (code < 0) |
381 | 0 | return code; |
382 | 0 | code = pxPassthrough_pcl_state_nonpage_exceptions(pxs); |
383 | 0 | if (code < 0) |
384 | 0 | return code; |
385 | 0 | pxs->pass_first = false; |
386 | 0 | } else { |
387 | | /* there was a previous passthrough check if there were |
388 | | any intervening XL commands */ |
389 | 0 | if (pxs->this_pass_contiguous == false) { |
390 | 0 | code = pxPassthrough_pcl_state_nonpage_exceptions(pxs); |
391 | 0 | if (code < 0) |
392 | 0 | return code; |
393 | 0 | } |
394 | 0 | } |
395 | 0 | r.ptr = str - 1; |
396 | 0 | r.limit = str + len - 1; |
397 | |
|
398 | 0 | code = pcl_process(&pxs->pcl_parser_state, pxs->pcs, &r); |
399 | 0 | if (code < 0) |
400 | 0 | return code; |
401 | | |
402 | 0 | code = pcl_recompute_font(pxs->pcs, false); /* select font */ |
403 | 0 | if (code < 0) |
404 | 0 | return code; |
405 | | |
406 | 0 | code = gs_setfont(pxs->pgs, pxs->pcs->font->pfont); |
407 | 0 | if (code < 0) |
408 | 0 | return code; |
409 | | |
410 | 0 | pfp = &pxs->pcs->font_selection[pxs->pcs->font_selected]; |
411 | |
|
412 | 0 | { |
413 | 0 | #define CP_PER_INCH (7200.0) |
414 | 0 | #define CP_PER_MM (7200.0/25.4) |
415 | 0 | #define CP_PER_10ths_of_MM (CP_PER_MM/10.0) |
416 | |
|
417 | 0 | static const double centipoints_per_measure[4] = { |
418 | 0 | CP_PER_INCH, /* eInch */ |
419 | 0 | CP_PER_MM, /* eMillimeter */ |
420 | 0 | CP_PER_10ths_of_MM, /* eTenthsOfAMillimeter */ |
421 | 0 | 1 /* pxeMeasure_next, won't reach */ |
422 | 0 | }; |
423 | |
|
424 | 0 | gs_point sz; |
425 | |
|
426 | 0 | pcl_font_scale(pxs->pcs, &sz); |
427 | 0 | pxgs->char_size = sz.x / |
428 | 0 | centipoints_per_measure[pxs->measure] * pxs->units_per_measure.x; |
429 | 0 | } |
430 | 0 | pxgs->symbol_set = pfp->params.symbol_set; |
431 | |
|
432 | 0 | if (pcl_downloaded_and_bound(pxs->pcs->font)) { |
433 | 0 | pxgs->symbol_map = 0; |
434 | 0 | } else { |
435 | 0 | px_set_symbol_map(pxs, pxs->pcs->font->font_type == plft_16bit); |
436 | 0 | } |
437 | |
|
438 | 0 | { |
439 | 0 | pl_font_t *plf = pxs->pcs->font; |
440 | | |
441 | | /* unfortunately the storage identifier is inconsistent |
442 | | between PCL and PCL XL, NB we should use the pxfont.h |
443 | | enumerations pxfsInternal and pxfsDownloaded but there is a |
444 | | foolish type redefinition in that header that need to be |
445 | | fixed. */ |
446 | |
|
447 | 0 | if (plf->storage == 4) |
448 | 0 | plf->storage = 1; |
449 | 0 | else |
450 | 0 | plf->storage = 0; |
451 | |
|
452 | 0 | pxgs->base_font = (px_font_t *) plf; |
453 | 0 | } |
454 | 0 | pxgs->char_matrix_set = false; |
455 | 0 | return 0; |
456 | 0 | } |