/src/ghostpdl/contrib/pcl3/eprn/gdeveprn.c
Line | Count | Source (jump to first uncovered line) |
1 | | /****************************************************************************** |
2 | | File: $Id: gdeveprn.c,v 1.25 2001/04/30 05:15:51 Martin Rel $ |
3 | | Contents: Implementation of the abstract ghostscript device 'eprn': |
4 | | general functions and page layout |
5 | | Author: Martin Lottermoser, Greifswaldstrasse 28, 38124 Braunschweig, |
6 | | Germany. E-mail: Martin.Lottermoser@t-online.de. |
7 | | |
8 | | ******************************************************************************* |
9 | | * * |
10 | | * Copyright (C) 2000, 2001 by Martin Lottermoser * |
11 | | * All rights reserved * |
12 | | * * |
13 | | ******************************************************************************* |
14 | | |
15 | | Preprocessor variables: |
16 | | |
17 | | EPRN_NO_PAGECOUNTFILE |
18 | | Define this if you do not want to use eprn's pagecount-file feature. |
19 | | You very likely must define this on Microsoft Windows. |
20 | | |
21 | | EPRN_TRACE |
22 | | Define this to enable tracing. Only useful for development. |
23 | | |
24 | | EPRN_USE_GSTATE (integer) |
25 | | Define this to be non-zero if the graphics state should be accessed |
26 | | directly instead of via the interpreter context state. Newer ghostscript |
27 | | versions require the latter path. The default is zero unless |
28 | | GS_REVISION is defined and less than 600. |
29 | | |
30 | | GS_REVISION (integer) |
31 | | If defined, this must be the ghostscript version number, e.g., 601 for |
32 | | ghostscript 6.01. |
33 | | |
34 | | ******************************************************************************/ |
35 | | |
36 | | /*****************************************************************************/ |
37 | | |
38 | | #ifndef _XOPEN_SOURCE |
39 | | #define _XOPEN_SOURCE 500 |
40 | | #endif |
41 | | |
42 | | /* Preprocessor symbol with version-dependent default */ |
43 | | #ifndef EPRN_USE_GSTATE |
44 | | #if !defined(GS_REVISION) || GS_REVISION >= 600 |
45 | | #define EPRN_USE_GSTATE 0 |
46 | | #else |
47 | | #define EPRN_USE_GSTATE 1 |
48 | | #endif |
49 | | #endif /* !EPRN_USE_GSTATE */ |
50 | | |
51 | | /*****************************************************************************/ |
52 | | |
53 | | /* Special Aladdin header, must be included before <sys/types.h> on some |
54 | | platforms (e.g., FreeBSD). */ |
55 | | #include "std.h" |
56 | | |
57 | | /* Standard headers */ |
58 | | #include <assert.h> |
59 | | #include <math.h> |
60 | | #include <string.h> |
61 | | #include <stdio.h> |
62 | | #include <stdlib.h> |
63 | | #ifdef EPRN_TRACE |
64 | | #include <time.h> |
65 | | #endif /* EPRN_TRACE */ |
66 | | |
67 | | /* Ghostscript headers. With the exception of gdebug.h, these files are only |
68 | | needed to compile eprn_forget_defaultmatrix() which needs the prototypes |
69 | | for gs_setdefaultmatrix() (in gscoord.h) and gs_main_instance_default() |
70 | | (in imain.h). Unfortunately and in disregard of good SE practice, |
71 | | ghostscript's header files are not self-contained. Therefore, if this file |
72 | | does not compile because of undefined symbols, just add include directives |
73 | | until it does. */ |
74 | | #include "gserrors.h" |
75 | | #include "iref.h" /* needed by icstate.h */ |
76 | | #include "gsmemraw.h" /* needed by icstate.h */ |
77 | | #include "gsmemory.h" /* needed by icstate.h */ |
78 | | #include "gstypes.h" /* needed by gsstate.h */ |
79 | | #include "gsstate.h" /* needed by icstate.h */ |
80 | | #include "icstate.h" /* for struct gs_context_state_s */ |
81 | | #if !defined(GS_REVISION) || GS_REVISION >= 700 |
82 | | #include "iapi.h" /* needed by iminst.h */ |
83 | | #endif /* GS_REVISION */ |
84 | | #include "iminst.h" /* for struct gs_main_instance_s */ |
85 | | #include "imain.h" /* for gs_main_instance_default() */ |
86 | | #include "gscoord.h" /* for gs_setdefaultmatrix() */ |
87 | | #if EPRN_USE_GSTATE |
88 | | #include "igstate.h" |
89 | | #endif /* EPRN_USE_GSTATE */ |
90 | | #ifdef EPRN_TRACE |
91 | | #include "gdebug.h" |
92 | | #endif /* EPRN_TRACE */ |
93 | | #include "gxstdio.h" |
94 | | |
95 | | /* Special headers for this device */ |
96 | | #ifndef EPRN_NO_PAGECOUNTFILE |
97 | | #include "pagecount.h" |
98 | | #endif /* EPRN_NO_PAGECOUNTFILE */ |
99 | | #include "gdeveprn.h" |
100 | | |
101 | | /*****************************************************************************/ |
102 | | |
103 | | /* Prefix for error messages */ |
104 | | #define ERRPREF "? eprn: " |
105 | | |
106 | | |
107 | | /****************************************************************************** |
108 | | |
109 | | Function: eprn_get_initial_matrix |
110 | | |
111 | | This function returns the initial matrix for the device. |
112 | | |
113 | | The result is based on the following parameters: |
114 | | - eprn: default_orientation, down_shift, right_shift, soft_tumble |
115 | | - HWResolution, MediaSize, ShowpageCount |
116 | | |
117 | | ******************************************************************************/ |
118 | | |
119 | | void eprn_get_initial_matrix(gx_device *device, gs_matrix *mptr) |
120 | 0 | { |
121 | 0 | eprn_Device *dev = (eprn_Device *)device; |
122 | 0 | float |
123 | | /* The following two arrays are oriented w.r.t. pixmap device space, i.e., |
124 | | the index 0 refers to the x coordinate (horizontal) and the index 1 to |
125 | | the y coordinate (vertical) in pixmap device space. */ |
126 | 0 | extension[2], /* media extension in pixels */ |
127 | 0 | pixels_per_bp[2]; /* resolution */ |
128 | 0 | int |
129 | 0 | j, |
130 | 0 | quarters; |
131 | |
|
132 | | #ifdef EPRN_TRACE |
133 | | if_debug0(EPRN_TRACE_CHAR, "! eprn_get_initial_matrix()...\n"); |
134 | | #endif |
135 | | |
136 | | /* We need 'default_orientation' and also the margins. */ |
137 | 0 | if (dev->eprn.code == ms_none) { |
138 | | #ifdef EPRN_TRACE |
139 | | if_debug0(EPRN_TRACE_CHAR, |
140 | | "! eprn_get_initial_matrix(): code is still ms_none.\n"); |
141 | | #endif |
142 | 0 | if (eprn_set_page_layout(dev) != 0) |
143 | 0 | eprintf(" Processing can't be stopped at this point although this error " |
144 | 0 | "occurred.\n"); |
145 | | /* The current function has a signature without the ability to signal |
146 | | an error condition. */ |
147 | 0 | } |
148 | |
|
149 | 0 | quarters = dev->eprn.default_orientation + |
150 | 0 | (dev->MediaSize[0] <= dev->MediaSize[1]? 0: 1); |
151 | | /* Number of quarter-circle rotations by +90 degrees necessary to obtain |
152 | | default user space starting with the y axis upwards in pixmap device |
153 | | space. |
154 | | It's not documented, but 'MediaSize' is the requested "PageSize" page |
155 | | device parameter value and hence is to be interpreted in default (not |
156 | | default default!) user space. The condition above therefore tests |
157 | | whether landscape orientation has been requested. |
158 | | */ |
159 | | |
160 | | /* Soft tumble option: rotate default user space by 180 degrees on every |
161 | | second page */ |
162 | 0 | if (dev->eprn.soft_tumble && dev->ShowpageCount % 2 != 0) quarters += 2; |
163 | | |
164 | | /* Prepare auxiliary data */ |
165 | 0 | for (j = 0; j < 2; j++) pixels_per_bp[j] = dev->HWResolution[j]/BP_PER_IN; |
166 | | /* 'HWResolution[]' contains the standard PostScript page device parameter |
167 | | 'HWResolution' which is defined in pixels per inch with respect to |
168 | | device space. */ |
169 | 0 | if (quarters % 2 == 0) { |
170 | | /* Default user space and pixmap device space agree in what is "horizontal" |
171 | | and what is "vertical". */ |
172 | 0 | extension[0] = dev->MediaSize[0]; |
173 | 0 | extension[1] = dev->MediaSize[1]; |
174 | 0 | } |
175 | 0 | else { |
176 | 0 | extension[0] = dev->MediaSize[1]; |
177 | 0 | extension[1] = dev->MediaSize[0]; |
178 | 0 | } |
179 | | /* Convert from bp to pixels: */ |
180 | 0 | for (j = 0; j < 2; j++) extension[j] *= pixels_per_bp[j]; |
181 | | /* Note that we are using the user-specified extension of the sheet, not the |
182 | | "official" one we could obtain in most cases from 'size'. */ |
183 | |
|
184 | 0 | switch (quarters % 4) { |
185 | 0 | case 0: |
186 | | /* The y axis of default user space points upwards in pixmap device space. |
187 | | The CTM is uniquely characterized by the following mappings from |
188 | | default user space to pixmap device space: |
189 | | (0, 0) -> (0, height in pixels) |
190 | | (width in bp, 0) -> (width in pixels, height in pixels) |
191 | | (0, height in bp) -> (0, 0) |
192 | | 'width' and 'height' refer to the sheet's extension as seen from pixmap |
193 | | device space, i.e., width in pixels == extension[0] and |
194 | | height in pixels == extension[1]. |
195 | | |
196 | | From the PLR we find that the CTM is a PostScript matrix |
197 | | [a b c d tx ty] used for mapping user space coordinates (x, y) to |
198 | | device space coordinates (x', y') as follows: |
199 | | x' = a*x + c*y + tx |
200 | | y' = b*x + d*y + ty |
201 | | Ghostscript's matrix type 'gs_matrix' writes its structure components |
202 | | 'xx' etc. in storage layout order into a PostScript matrix (see |
203 | | write_matrix() in iutil.c), hence we obtain by comparison with |
204 | | gsmatrix.h the PostScript matrix [ xx xy yx yy tx ty ]. |
205 | | The correspondence can also be seen by comparison of the equations |
206 | | above with the code in gs_point_transform() in gsmatrix.c. |
207 | | It would, however, still be reassuring to have a corresponding |
208 | | statement in ghostscript's documentation. |
209 | | */ |
210 | 0 | gx_default_get_initial_matrix(device, mptr); |
211 | | /* Of course, I could also set this directly: |
212 | | mptr->xx = pixels_per_bp[0]; |
213 | | mptr->xy = 0; |
214 | | mptr->yx = 0; |
215 | | mptr->yy = -pixels_per_bp[1]; |
216 | | mptr->tx = 0; |
217 | | mptr->ty = extension[1]; |
218 | | Doing it in this way is, however, more stable against dramatic changes |
219 | | in ghostscript. |
220 | | */ |
221 | 0 | break; |
222 | 0 | case 1: |
223 | | /* The y axis of default user space points to the left in pixmap device |
224 | | space. The CTM is uniquely characterized by the following mappings from |
225 | | default user space to pixmap device space: |
226 | | (0, 0) -> (width in pixels, height in pixels) |
227 | | (height in bp, 0) -> (width in pixels, 0) |
228 | | (0, width in bp) -> (0, height in pixels) |
229 | | */ |
230 | 0 | mptr->xx = 0; |
231 | 0 | mptr->xy = -pixels_per_bp[1]; |
232 | 0 | mptr->yx = -pixels_per_bp[0]; |
233 | 0 | mptr->yy = 0; |
234 | 0 | mptr->tx = extension[0]; |
235 | 0 | mptr->ty = extension[1]; |
236 | 0 | break; |
237 | 0 | case 2: |
238 | | /* The y axis of default user space points downwards in pixmap device |
239 | | space. The CTM is uniquely characterized by the following mappings from |
240 | | default user space to pixmap device space: |
241 | | (0, 0) -> (width in pixels, 0) |
242 | | (width in bp, 0) -> (0, 0) |
243 | | (0, height in bp) -> (width in pixels, height in pixels) |
244 | | */ |
245 | 0 | mptr->xx = -pixels_per_bp[0]; |
246 | 0 | mptr->xy = 0; |
247 | 0 | mptr->yx = 0; |
248 | 0 | mptr->yy = pixels_per_bp[1]; |
249 | 0 | mptr->tx = extension[0]; |
250 | 0 | mptr->ty = 0; |
251 | 0 | break; |
252 | 0 | case 3: |
253 | | /* The y axis of default user space points to the right in pixmap device |
254 | | space. The CTM is uniquely characterized by the following mappings from |
255 | | default user space to pixmap device space: |
256 | | (0, 0) -> (0, 0) |
257 | | (height in bp, 0) -> (0, height in pixels) |
258 | | (0, width in bp) -> (width in pixels, 0) |
259 | | */ |
260 | 0 | mptr->xx = 0; |
261 | 0 | mptr->xy = pixels_per_bp[1]; |
262 | 0 | mptr->yx = pixels_per_bp[0]; |
263 | 0 | mptr->yy = 0; |
264 | 0 | mptr->tx = 0; |
265 | 0 | mptr->ty = 0; |
266 | 0 | break; |
267 | 0 | } |
268 | | |
269 | | /* Finally, shift the device space origin to the top-left corner of the |
270 | | printable area. I am deliberately not using the corresponding shift |
271 | | feature in gx_device_set_margins() because it achieves its effect by |
272 | | using the 'Margins' array which should remain at the user's disposal for |
273 | | correcting misadjustments. In addition, gx_device_set_margins() will not |
274 | | work correctly for quarters % 4 != 0 anyway. |
275 | | */ |
276 | 0 | { |
277 | 0 | gs_matrix translation; |
278 | | |
279 | | /* Translation of pixmap device space origin by top and left margins in |
280 | | pixmap device space */ |
281 | 0 | gs_make_translation( |
282 | 0 | -dev->eprn.right_shift*pixels_per_bp[0], |
283 | 0 | -dev->eprn.down_shift *pixels_per_bp[1], |
284 | 0 | &translation); |
285 | | |
286 | | /* Multiply the initial matrix from the right with the translation matrix, |
287 | | i.e., in going from user to device space the translation will be applied |
288 | | last. */ |
289 | 0 | gs_matrix_multiply(mptr, &translation, mptr); |
290 | 0 | } |
291 | |
|
292 | | #ifdef EPRN_TRACE |
293 | | if_debug6(EPRN_TRACE_CHAR, " Returning [%g %g %g %g %g %g].\n", |
294 | | mptr->xx, mptr->xy, mptr->yx, mptr->yy, mptr->tx, mptr->ty); |
295 | | #endif |
296 | 0 | return; |
297 | 0 | } |
298 | | |
299 | | /****************************************************************************** |
300 | | |
301 | | Function: print_flags |
302 | | |
303 | | Print a textual description of 'flags' to the error stream. |
304 | | |
305 | | ******************************************************************************/ |
306 | | |
307 | | static void print_flags(ms_MediaCode flags, const ms_Flag *user_flags) |
308 | 0 | { |
309 | | /* Non-standard flags first */ |
310 | 0 | if (user_flags != NULL) { |
311 | 0 | while (user_flags->code != ms_none) { |
312 | 0 | if (user_flags->code & flags) { |
313 | 0 | eprintf1("%s", user_flags->name); |
314 | 0 | flags &= ~user_flags->code; |
315 | 0 | } |
316 | 0 | user_flags++; |
317 | 0 | } |
318 | 0 | } |
319 | | |
320 | | /* Standard substrings */ |
321 | 0 | if (flags & MS_SMALL_FLAG) eprintf(MS_SMALL_STRING); |
322 | 0 | if (flags & MS_BIG_FLAG ) eprintf(MS_BIG_STRING); |
323 | 0 | if (flags & MS_EXTRA_FLAG) eprintf(MS_EXTRA_STRING); |
324 | 0 | flags &= ~(MS_SMALL_FLAG | MS_BIG_FLAG | MS_EXTRA_FLAG); |
325 | | |
326 | | /* Completeness check */ |
327 | 0 | if (flags & ~MS_TRANSVERSE_FLAG) |
328 | 0 | eprintf1("0x%04X", (unsigned int)(flags & ~MS_TRANSVERSE_FLAG)); |
329 | | |
330 | | /* Standard qualifier */ |
331 | 0 | if (flags & MS_TRANSVERSE_FLAG) eprintf("." MS_TRANSVERSE_STRING); |
332 | |
|
333 | 0 | return; |
334 | 0 | } |
335 | | |
336 | | /****************************************************************************** |
337 | | |
338 | | Function: eprn_flag_mismatch |
339 | | |
340 | | This routine is called if the media size can be supported for some |
341 | | combination of flags but not for that combination which has been requested. |
342 | | The parameter 'no_match' indicates whether these flags are supported at all |
343 | | for any of the supported sizes or not. |
344 | | |
345 | | If the derived device has set a flag mismatch error reporting function, the |
346 | | call will be passed to that function. Otherwise a general error message is |
347 | | written through the graphics library's eprintf(). |
348 | | |
349 | | ******************************************************************************/ |
350 | | |
351 | | static void eprn_flag_mismatch(const struct s_eprn_Device *eprn, |
352 | | bool no_match) |
353 | 0 | { |
354 | 0 | if (eprn->fmr != NULL) (*eprn->fmr)(eprn, no_match); |
355 | 0 | else { |
356 | 0 | const char *epref = eprn->CUPS_messages? CUPS_ERRPREF: ""; |
357 | |
|
358 | 0 | eprintf2("%s" ERRPREF "The %s does not support ", |
359 | 0 | epref, eprn->cap->name); |
360 | 0 | if (eprn->desired_flags == 0) eprintf("an empty set of media flags"); |
361 | 0 | else { |
362 | 0 | eprintf("the \""); |
363 | 0 | print_flags(eprn->desired_flags, eprn->flag_desc); |
364 | 0 | eprintf("\" flag(s)"); |
365 | 0 | } |
366 | 0 | eprintf1("\n%s (ignoring presence or absence of \"", epref); |
367 | 0 | { |
368 | 0 | ms_MediaCode optional = MS_TRANSVERSE_FLAG; |
369 | 0 | if (eprn->optional_flags != NULL) { |
370 | 0 | const ms_MediaCode *of = eprn->optional_flags; |
371 | 0 | while (*of != ms_none) optional |= *of++; |
372 | 0 | } |
373 | 0 | print_flags(optional, eprn->flag_desc); |
374 | 0 | } |
375 | 0 | eprintf("\") for "); |
376 | 0 | if (no_match) eprintf("any"); else eprintf("this"); |
377 | 0 | eprintf(" page size.\n"); |
378 | 0 | } |
379 | |
|
380 | 0 | return; |
381 | 0 | } |
382 | | |
383 | | /****************************************************************************** |
384 | | |
385 | | Function: better_flag_match |
386 | | |
387 | | This function returns true iff the flags in 'new_code' match the requested |
388 | | flags (strictly) better than those in 'old_code'. |
389 | | |
390 | | ******************************************************************************/ |
391 | | |
392 | | static bool better_flag_match(ms_MediaCode desired, |
393 | | const ms_MediaCode *optional, ms_MediaCode old_code, ms_MediaCode new_code) |
394 | 0 | { |
395 | 0 | ms_MediaCode |
396 | 0 | old_diff, /* difference between old flags and desired flags */ |
397 | 0 | new_diff; /* difference between new flags and desired flags */ |
398 | | |
399 | | /* Ignore the size information */ |
400 | 0 | old_code = ms_flags(old_code); |
401 | 0 | new_code = ms_flags(new_code); |
402 | | |
403 | | /* Determine differences to desired flags */ |
404 | 0 | old_diff = old_code ^ desired; |
405 | 0 | new_diff = new_code ^ desired; |
406 | | |
407 | | /* Check for exact matches */ |
408 | 0 | if (old_diff == 0) return false; |
409 | 0 | if (new_diff == 0) return true; |
410 | | |
411 | | /* Is the difference at most MS_TRANSVERSE_FLAG? */ |
412 | 0 | old_diff = old_diff & ~MS_TRANSVERSE_FLAG; |
413 | 0 | new_diff = new_diff & ~MS_TRANSVERSE_FLAG; |
414 | 0 | if (old_diff == 0) return false; |
415 | 0 | if (new_diff == 0) return true; |
416 | | |
417 | | /* Loop over the remaining optional flags */ |
418 | 0 | if (optional != NULL) { |
419 | 0 | const ms_MediaCode *opt = optional; |
420 | |
|
421 | 0 | while (*opt != ms_none) { |
422 | 0 | old_diff = old_diff & ~*opt; |
423 | 0 | new_diff = new_diff & ~*opt; |
424 | 0 | if (old_diff == 0) { |
425 | 0 | if (new_diff != 0) return false; |
426 | | /* At this point both are matches at the same level of optional flags. |
427 | | Now look for the last preceding flag in which they differ. */ |
428 | 0 | { |
429 | 0 | ms_MediaCode diff = ms_flags(old_code ^ new_code); |
430 | 0 | while (optional < opt && (diff & *opt) == 0) opt--; |
431 | 0 | if ((diff & *opt) == 0) { |
432 | 0 | if ((diff & MS_TRANSVERSE_FLAG) == 0) return false; |
433 | | /* old and new differ in MS_TRANSVERSE_FLAG */ |
434 | 0 | return (new_code & MS_TRANSVERSE_FLAG) == |
435 | 0 | (desired & MS_TRANSVERSE_FLAG); |
436 | 0 | } |
437 | 0 | return (new_code & *opt) == (desired & *opt); |
438 | 0 | } |
439 | 0 | } |
440 | 0 | if (new_diff == 0) return true; |
441 | 0 | opt++; |
442 | 0 | } |
443 | 0 | } |
444 | | |
445 | 0 | return false; /* Both codes are mismatches at this point */ |
446 | 0 | } |
447 | | |
448 | | /****************************************************************************** |
449 | | |
450 | | Function: flag_match |
451 | | |
452 | | This function returns true iff 'code' is an acceptable match for the flag |
453 | | request. |
454 | | |
455 | | ******************************************************************************/ |
456 | | |
457 | | static bool flag_match(ms_MediaCode desired, |
458 | | const ms_MediaCode *optional, ms_MediaCode code) |
459 | 0 | { |
460 | 0 | code = (ms_flags(code) ^ desired) & ~MS_TRANSVERSE_FLAG; |
461 | 0 | if (code == 0) return true; |
462 | | |
463 | 0 | if (optional == NULL) return false; |
464 | | |
465 | 0 | while (*optional != ms_none && code != 0) { |
466 | 0 | code = code & ~*optional; |
467 | 0 | optional++; |
468 | 0 | } |
469 | |
|
470 | 0 | return code == 0; |
471 | 0 | } |
472 | | |
473 | | /****************************************************************************** |
474 | | |
475 | | Function: eprn_set_page_layout |
476 | | |
477 | | This function determines media size, sheet orientation in pixmap device space, |
478 | | the orientation of default user space, and the imageable area. It should be |
479 | | called whenever the page device parameters "PageSize" and "LeadingEdge", |
480 | | the media flags, or the page descriptions have been changed. |
481 | | |
482 | | The function returns zero on success and a non-zero value otherwise. |
483 | | In the latter case, an error message has been issued. This can only |
484 | | occur if the media size is not supported with the flags requested. |
485 | | |
486 | | On success, the following variables in the device structure are consistent: |
487 | | width, height, MediaSize[], HWMargins[], eprn.code, eprn.default_orientation, |
488 | | eprn.right_shift, eprn.down_shift. |
489 | | |
490 | | ******************************************************************************/ |
491 | | |
492 | | int eprn_set_page_layout(eprn_Device *dev) |
493 | 0 | { |
494 | 0 | bool |
495 | 0 | no_match = true, |
496 | | /* Are the requested flags supported for some size? */ |
497 | 0 | landscape = dev->MediaSize[0] > dev->MediaSize[1]; |
498 | | /* It's not documented, but 'MediaSize' is the requested "PageSize" page |
499 | | device parameter value and hence is to be interpreted in default (not |
500 | | default default!) user space. */ |
501 | 0 | const char *epref = dev->eprn.CUPS_messages? CUPS_ERRPREF: ""; |
502 | 0 | const eprn_CustomPageDescription |
503 | 0 | *best_cmatch = NULL; /* best custom page size match */ |
504 | 0 | eprn_Eprn |
505 | 0 | *eprn = &dev->eprn; |
506 | 0 | const eprn_PageDescription |
507 | 0 | *best_cdmatch = NULL, /* best custom page size match in discrete list*/ |
508 | 0 | *best_dmatch = NULL, /* best discrete match */ |
509 | 0 | *pd; /* loop variable */ |
510 | 0 | float |
511 | | /* Page width and height in bp with w <= h (in a moment): */ |
512 | 0 | w = dev->MediaSize[0], |
513 | 0 | h = dev->MediaSize[1], |
514 | | /* pixmap device space margins in bp (canonical order): */ |
515 | 0 | margins[4]; |
516 | 0 | int |
517 | 0 | quarters; |
518 | 0 | ms_MediaCode |
519 | 0 | desired = eprn->desired_flags; |
520 | |
|
521 | | #ifdef EPRN_TRACE |
522 | | if_debug3(EPRN_TRACE_CHAR, |
523 | | "! eprn_set_page_layout(): PageSize = [%.0f %.0f], " |
524 | | "desired_flags = 0x%04X.\n", |
525 | | dev->MediaSize[0], dev->MediaSize[1], (unsigned int)desired); |
526 | | #endif |
527 | | |
528 | | /* Ensure w <= h */ |
529 | 0 | if (w > h) { |
530 | 0 | float temp; |
531 | 0 | temp = w; w = h; h = temp; |
532 | | /* This has effectively split 'MediaSize[]' into 'w', 'h' and 'landscape'. |
533 | | */ |
534 | 0 | } |
535 | | |
536 | | /* Initialization of primary return value */ |
537 | 0 | eprn->code = ms_none; |
538 | | |
539 | | /* Put the LeadingEdge value into the desired flag pattern if it's set */ |
540 | 0 | if (eprn->leading_edge_set) { |
541 | 0 | if (eprn->default_orientation % 2 == 0) /* true on short edge first */ |
542 | 0 | desired &= ~MS_TRANSVERSE_FLAG; |
543 | 0 | else |
544 | 0 | desired |= MS_TRANSVERSE_FLAG; |
545 | 0 | } |
546 | | |
547 | | /* Find best match in discrete sizes */ |
548 | 0 | if (eprn->media_overrides == NULL) pd = eprn->cap->sizes; |
549 | 0 | else pd = eprn->media_overrides; |
550 | 0 | while (pd->code != ms_none) { |
551 | 0 | const ms_SizeDescription *ms = ms_find_size_from_code(pd->code); |
552 | 0 | if (ms->dimen[0] > 0.0 /* ignore variable sizes */ && |
553 | 0 | fabs(w - ms->dimen[0]) <= 5.0 && |
554 | 0 | fabs(h - ms->dimen[1]) <= 5.0) { |
555 | | /* The size does match at 5 bp tolerance. This value has been chosen |
556 | | arbitrarily to be equal to PostScript's PageSize matching tolerance |
557 | | during media selection. The tolerance should really be that at which |
558 | | the printer in question distinguishes between sizes or smaller than |
559 | | that in order to at least prevent printing on unsupported sizes. |
560 | | */ |
561 | 0 | if (best_dmatch == NULL || |
562 | 0 | better_flag_match(desired, eprn->optional_flags, best_dmatch->code, |
563 | 0 | pd->code)) |
564 | 0 | best_dmatch = pd; |
565 | 0 | if (flag_match(desired, eprn->optional_flags, pd->code)) |
566 | 0 | no_match = false; |
567 | 0 | } |
568 | 0 | pd++; |
569 | 0 | } |
570 | | |
571 | | /* Next find the best match among the custom size descriptions */ |
572 | 0 | if (eprn->cap->custom != NULL) { |
573 | 0 | const eprn_CustomPageDescription *cp = eprn->cap->custom; |
574 | | |
575 | | /* First check whether the size is in the supported range */ |
576 | 0 | while (cp->width_max > 0.0) { |
577 | 0 | if (cp->width_min <= w && w <= cp->width_max && |
578 | 0 | cp->height_min <= h && h <= cp->height_max) { |
579 | | /* The size does match. */ |
580 | 0 | if (best_cmatch == NULL || |
581 | 0 | better_flag_match(desired, eprn->optional_flags, best_cmatch->code, |
582 | 0 | cp->code)) |
583 | 0 | best_cmatch = cp; |
584 | 0 | if (eprn->media_overrides == NULL && |
585 | 0 | flag_match(desired, eprn->optional_flags, cp->code)) |
586 | 0 | no_match = false; |
587 | 0 | } |
588 | 0 | cp++; |
589 | 0 | } |
590 | | |
591 | | /* If we have read a media configuration file, the flags to be matched |
592 | | must be sought in 'media_overrides'. */ |
593 | 0 | if (best_cmatch != NULL && eprn->media_overrides != NULL) { |
594 | 0 | for (pd = eprn->media_overrides; pd->code != ms_none; pd++) { |
595 | 0 | if (ms_without_flags(pd->code) == ms_CustomPageSize) { |
596 | 0 | if (best_cdmatch == NULL || |
597 | 0 | better_flag_match(desired, eprn->optional_flags, |
598 | 0 | best_cdmatch->code, pd->code)) |
599 | 0 | best_cdmatch = pd; |
600 | 0 | if (flag_match(desired, eprn->optional_flags, pd->code)) |
601 | 0 | no_match = false; |
602 | 0 | } |
603 | 0 | } |
604 | 0 | } |
605 | 0 | } |
606 | | |
607 | | /* Now the 'best_*match' variables indicate for each of the categories of |
608 | | page descriptions to which extent the size is supported at all (non-NULL |
609 | | value) and what the best flag match in the category is. Here we now check |
610 | | for NULL values, i.e., size matches. */ |
611 | 0 | if (best_dmatch == NULL) { |
612 | | /* No discrete match */ |
613 | 0 | if (best_cmatch == NULL) { |
614 | | /* No match at all. */ |
615 | 0 | eprintf3("%s" ERRPREF |
616 | 0 | "This document requests a page size of %.0f x %.0f bp.\n", |
617 | 0 | epref, dev->MediaSize[0], dev->MediaSize[1]); |
618 | 0 | if (eprn->cap->custom == NULL) { |
619 | | /* The printer does not support custom page sizes */ |
620 | 0 | if (eprn->media_overrides != NULL) |
621 | 0 | eprintf1( |
622 | 0 | "%s The media configuration file does not contain an entry for " |
623 | 0 | " this size.\n", epref); |
624 | 0 | else |
625 | 0 | eprintf2("%s This size is not supported by the %s.\n", |
626 | 0 | epref, eprn->cap->name); |
627 | 0 | } |
628 | 0 | else |
629 | 0 | eprintf3( |
630 | 0 | "%s This size is not supported as a discrete size and it exceeds " |
631 | 0 | "the\n" |
632 | 0 | "%s custom page size limits for the %s.\n", |
633 | 0 | epref, epref, eprn->cap->name); |
634 | 0 | return -1; |
635 | 0 | } |
636 | 0 | if (eprn->media_overrides != NULL && best_cdmatch == NULL) { |
637 | 0 | eprintf6("%s" ERRPREF |
638 | 0 | "This document requests a page size of %.0f x %.0f bp\n" |
639 | 0 | "%s but there is no entry for this size in the " |
640 | 0 | "media configuration file\n" |
641 | 0 | "%s %s.\n", |
642 | 0 | epref, dev->MediaSize[0], dev->MediaSize[1], epref, epref, |
643 | 0 | eprn->media_file); |
644 | 0 | return -1; |
645 | 0 | } |
646 | 0 | } |
647 | | /* Now we have: best_dmatch != NULL || best_cmatch != NULL && |
648 | | (eprn->media_overrides == NULL || best_cdmatch != NULL). */ |
649 | | |
650 | | /* Find a flag match among the size matches found so far */ |
651 | 0 | { |
652 | 0 | ms_MediaCode custom_code = ms_none; |
653 | | /* best custom page size match (either from cmatch or dcmatch) */ |
654 | 0 | if (best_cmatch != NULL && |
655 | 0 | (eprn->media_overrides == NULL || best_cdmatch != NULL)) |
656 | 0 | custom_code = (eprn->media_overrides == NULL? |
657 | 0 | best_cmatch->code: best_cdmatch->code); |
658 | |
|
659 | 0 | if (best_dmatch == NULL || |
660 | 0 | (best_cmatch != NULL && |
661 | 0 | better_flag_match(desired, eprn->optional_flags, best_dmatch->code, |
662 | 0 | custom_code))) { |
663 | 0 | if (flag_match(desired, eprn->optional_flags, custom_code)) { |
664 | 0 | if (eprn->media_overrides == NULL) { |
665 | 0 | eprn->code = best_cmatch->code; |
666 | 0 | margins[0] = best_cmatch->left; |
667 | 0 | margins[1] = best_cmatch->bottom; |
668 | 0 | margins[2] = best_cmatch->right; |
669 | 0 | margins[3] = best_cmatch->top; |
670 | 0 | } |
671 | 0 | else { |
672 | 0 | eprn->code = best_cdmatch->code; |
673 | 0 | margins[0] = best_cdmatch->left; |
674 | 0 | margins[1] = best_cdmatch->bottom; |
675 | 0 | margins[2] = best_cdmatch->right; |
676 | 0 | margins[3] = best_cdmatch->top; |
677 | 0 | } |
678 | 0 | } |
679 | 0 | } |
680 | 0 | else { |
681 | 0 | if (flag_match(desired, eprn->optional_flags, best_dmatch->code)) { |
682 | 0 | eprn->code = best_dmatch->code; |
683 | 0 | margins[0] = best_dmatch->left; |
684 | 0 | margins[1] = best_dmatch->bottom; |
685 | 0 | margins[2] = best_dmatch->right; |
686 | 0 | margins[3] = best_dmatch->top; |
687 | 0 | } |
688 | 0 | } |
689 | 0 | } |
690 | | /* If we've found a match, 'code' is no longer 'ms_none'. */ |
691 | 0 | if (eprn->code == ms_none) { |
692 | 0 | eprn_flag_mismatch(eprn, no_match); |
693 | 0 | return -1; |
694 | 0 | } |
695 | | |
696 | | /* Adapt the orientation of default default user space if not prescribed */ |
697 | 0 | if (!eprn->leading_edge_set) { |
698 | 0 | if (eprn->code & MS_TRANSVERSE_FLAG) eprn->default_orientation = 3; |
699 | | /* This leads to 0 if landscape orientation is requested. */ |
700 | 0 | else eprn->default_orientation = 0; |
701 | 0 | } |
702 | | |
703 | | /* |
704 | | Now 'eprn->default_orientation % 2' describes the sheet's orientation in |
705 | | pixmap device space. If this does not agree with the width and height |
706 | | values in the device instance, we'll have to adapt them. |
707 | | This is only necessary if there is a significant difference between width |
708 | | and height. |
709 | | */ |
710 | 0 | if (fabs(w - h) > 1 /* arbitrary */ && |
711 | 0 | (eprn->default_orientation % 2 == 0) != |
712 | 0 | (dev->width/dev->HWResolution[0] <= dev->height/dev->HWResolution[1])) { |
713 | 0 | bool reallocate = false; |
714 | |
|
715 | | #ifdef EPRN_TRACE |
716 | | if_debug0(EPRN_TRACE_CHAR, |
717 | | "! eprn_set_page_layout(): width-height change is necessary.\n"); |
718 | | #endif |
719 | | |
720 | | /* Free old storage if the device is open */ |
721 | 0 | if (dev->is_open) { |
722 | | #ifdef EPRN_TRACE |
723 | | if_debug0(EPRN_TRACE_CHAR, "! eprn_set_page_layout(): Device is open.\n"); |
724 | | #endif |
725 | 0 | reallocate = true; |
726 | | /* One could try and call the allocation/reallocation routines of the |
727 | | prn device directly, but they are not available in older ghostscript |
728 | | versions and this method is safer anyway because it relies on a |
729 | | documented API. */ |
730 | 0 | gdev_prn_close((gx_device *)dev); /* ignore the result */ |
731 | 0 | } |
732 | | |
733 | | /* Now set width and height via gx_device_set_media_size(). This function |
734 | | sets 'MediaSize[]', 'width', and 'height' based on the assumption that |
735 | | default user space has a y axis which is vertical in pixmap device |
736 | | space. This may be wrong and we have to fix it. Because fixing |
737 | | 'MediaSize[]' is simpler, gx_device_set_media_size() is called such |
738 | | that it gives the correct values for 'width' and 'height'. */ |
739 | 0 | if (eprn->default_orientation % 2 == 0) { |
740 | | /* portrait orientation of the sheet in pixmap device space */ |
741 | 0 | gx_device_set_media_size((gx_device *)dev, w, h); |
742 | 0 | if (landscape) { |
743 | 0 | dev->MediaSize[0] = h; |
744 | 0 | dev->MediaSize[1] = w; |
745 | 0 | } |
746 | 0 | } |
747 | 0 | else { |
748 | | /* landscape orientation in pixmap device space (transverse) */ |
749 | 0 | gx_device_set_media_size((gx_device *)dev, h, w); |
750 | 0 | if (!landscape) { |
751 | 0 | dev->MediaSize[0] = w; |
752 | 0 | dev->MediaSize[1] = h; |
753 | 0 | } |
754 | 0 | } |
755 | | |
756 | | /* If the device is/was open, reallocate storage */ |
757 | 0 | if (reallocate) { |
758 | 0 | int rc; |
759 | |
|
760 | 0 | rc = gdev_prn_open((gx_device *)dev); |
761 | 0 | if (rc < 0) { |
762 | 0 | eprintf2("%s" ERRPREF |
763 | 0 | "Failure of gdev_prn_open(), code is %d.\n", |
764 | 0 | epref, rc); |
765 | 0 | return rc; |
766 | 0 | } |
767 | 0 | } |
768 | 0 | } |
769 | | |
770 | | /* Increase the bottom margin for coloured modes except if it is exactly |
771 | | zero */ |
772 | 0 | if (eprn->colour_model != eprn_DeviceGray && margins[1] != 0.0) |
773 | 0 | margins[1] += eprn->cap->bottom_increment; |
774 | | |
775 | | /* Number of +90-degree rotations needed for default user space: */ |
776 | 0 | quarters = eprn->default_orientation; |
777 | 0 | if (landscape) quarters = (quarters + 1)%4; |
778 | | |
779 | | /* Store the top and left margins in the device structure for use by |
780 | | eprn_get_initial_matrix() and set the margins of the printable area if |
781 | | we may. |
782 | | gx_device_set_margins() (see gsdevice.c) copies the margins[] array to |
783 | | HWMargins[] which is presumably to be interpreted in default user space |
784 | | (see gs_initclip() in gspath.c), and if its second argument is true it |
785 | | also modifies the offset variable Margins[]. The first property means |
786 | | that gx_device_set_margins() can only be used if default user space and |
787 | | pixmap device space have the same "up" direction, and the second |
788 | | appropriates a parameter which is intended for the user. |
789 | | */ |
790 | 0 | if (eprn->keep_margins) { |
791 | 0 | eprn->down_shift = dev->HWMargins[3 - quarters]; |
792 | 0 | eprn->right_shift = dev->HWMargins[(4 - quarters)%4]; |
793 | 0 | } |
794 | 0 | else { |
795 | 0 | int j; |
796 | |
|
797 | 0 | eprn->down_shift = margins[3]; |
798 | 0 | eprn->right_shift = margins[0]; |
799 | |
|
800 | 0 | if (quarters != 0) { |
801 | | /* The "canonical margin order" for ghostscript is left, bottom, right, |
802 | | top. Hence for, e.g., a +90-degree rotation ('quarters' is 1) of |
803 | | default user space with respect to pixmap device space the left |
804 | | margin (index 0) in default user space is actually the bottom margin |
805 | | (index 1) in pixmap device space, the bottom margin is the right one, |
806 | | etc. |
807 | | */ |
808 | 0 | for (j = 0; j < 4; j++) dev->HWMargins[j] = margins[(j+quarters)%4]; |
809 | | /* 'HWMargins[]' is in bp (see gxdevcli.h) */ |
810 | 0 | } |
811 | 0 | else { |
812 | | /* Convert to inches */ |
813 | 0 | for (j = 0; j < 4; j++) margins[j] /= BP_PER_IN; |
814 | |
|
815 | 0 | gx_device_set_margins((gx_device *)dev, margins, false); |
816 | | /* Of course, I could set HWMargins[] directly also in this case. This |
817 | | way is however less prone to break on possible future incompatible |
818 | | changes to ghostscript and it covers the most frequent case (portrait |
819 | | and short edge first). */ |
820 | 0 | } |
821 | 0 | } |
822 | |
|
823 | 0 | return 0; |
824 | 0 | } |
825 | | |
826 | | /****************************************************************************** |
827 | | |
828 | | Function: eprn_init_device |
829 | | |
830 | | This function sets 'cap' to 'desc' and all device parameters which are |
831 | | modified through the put_params routines to default values. The resolution is |
832 | | left at its old value (and don't ask me why or I'll start to whimper). If the |
833 | | device is open when this function is called the device will be closed |
834 | | afterwards. |
835 | | |
836 | | 'desc' may not be NULL. |
837 | | |
838 | | ******************************************************************************/ |
839 | | |
840 | | int eprn_init_device(eprn_Device *dev, const eprn_PrinterDescription *desc) |
841 | 0 | { |
842 | 0 | eprn_Eprn *eprn = &dev->eprn; |
843 | 0 | int j; |
844 | 0 | float hres, vres; |
845 | 0 | int code; |
846 | |
|
847 | 0 | if (dev->is_open) gs_closedevice((gx_device *)dev); |
848 | |
|
849 | 0 | assert(desc != NULL); |
850 | 0 | eprn->cap = desc; |
851 | 0 | eprn_set_media_data(dev, NULL, 0); |
852 | | |
853 | | /* The media flags are retained because they have not been prescribed by the |
854 | | user directly in contact with eprn but are completely under the control |
855 | | of the derived device. */ |
856 | |
|
857 | 0 | eprn->code = ms_none; |
858 | 0 | eprn->leading_edge_set = false; |
859 | 0 | eprn->right_shift = 0; |
860 | 0 | eprn->down_shift = 0; |
861 | 0 | eprn->keep_margins = false; |
862 | 0 | eprn->soft_tumble = false; |
863 | 0 | for (j = 0; j < 4; j++) dev->HWMargins[j] = 0; |
864 | | |
865 | | /* Set to default colour state, ignoring request failures */ |
866 | 0 | eprn->colour_model = eprn_DeviceGray; |
867 | 0 | eprn->black_levels = 2; |
868 | 0 | eprn->non_black_levels = 0; |
869 | 0 | eprn->intensity_rendering = eprn_IR_halftones; |
870 | 0 | hres = dev->HWResolution[0]; |
871 | 0 | vres = dev->HWResolution[1]; |
872 | 0 | code = eprn_check_colour_info(desc->colour_info, &eprn->colour_model, |
873 | 0 | &hres, &vres, &eprn->black_levels, &eprn->non_black_levels); |
874 | 0 | if (code) { |
875 | 0 | return code; |
876 | 0 | } |
877 | 0 | if (eprn->pagecount_file != NULL) { |
878 | 0 | gs_free(dev->memory->non_gc_memory, eprn->pagecount_file, strlen(eprn->pagecount_file) + 1, |
879 | 0 | sizeof(char), "eprn_init_device"); |
880 | 0 | eprn->pagecount_file = NULL; |
881 | 0 | } |
882 | |
|
883 | 0 | eprn->media_position_set = false; |
884 | |
|
885 | 0 | return 0; |
886 | 0 | } |
887 | | |
888 | | /****************************************************************************** |
889 | | |
890 | | Function: eprn_set_media_flags |
891 | | |
892 | | ******************************************************************************/ |
893 | | |
894 | | void eprn_set_media_flags(eprn_Device *dev, ms_MediaCode desired, |
895 | | const ms_MediaCode *optional) |
896 | 0 | { |
897 | 0 | dev->eprn.code = ms_none; |
898 | |
|
899 | 0 | dev->eprn.desired_flags = desired; |
900 | 0 | dev->eprn.optional_flags = optional; |
901 | |
|
902 | 0 | return; |
903 | 0 | } |
904 | | |
905 | | /****************************************************************************** |
906 | | |
907 | | Function: eprn_open_device |
908 | | |
909 | | This function "opens" the device. According to Drivers.htm, the 'open_device' |
910 | | functions are called before any output is sent to the device, and they must |
911 | | ensure that the device instance is valid, possibly by doing suitable |
912 | | initialization. |
913 | | |
914 | | This particular implementation also checks whether the requested page size |
915 | | is supported by the printer. This discovery must, unfortunately, be |
916 | | delayed until the moment this function is called. Note that this also implies |
917 | | that various eprn parameters depending on the page size (e.g., 'eprn.code') |
918 | | can be relied upon to have valid values only after the device has been |
919 | | successfully opened. The same applies to rendering parameters. |
920 | | |
921 | | This function also opens the parts defined by base classes. |
922 | | |
923 | | The function returns zero on success and a ghostscript error value otherwise. |
924 | | |
925 | | ******************************************************************************/ |
926 | | |
927 | | int eprn_open_device(gx_device *device) |
928 | 0 | { |
929 | 0 | eprn_Eprn *eprn = &((eprn_Device *)device)->eprn; |
930 | 0 | const char *epref = eprn->CUPS_messages? CUPS_ERRPREF: ""; |
931 | 0 | int rc; |
932 | |
|
933 | | #ifdef EPRN_TRACE |
934 | | if_debug0(EPRN_TRACE_CHAR, "! eprn_open_device()...\n"); |
935 | | #endif |
936 | | |
937 | | /* Checks on page size and determination of derived values */ |
938 | 0 | if (eprn_set_page_layout((eprn_Device *)device) != 0) |
939 | 0 | return_error(gs_error_rangecheck); |
940 | | |
941 | | /* Check the rendering parameters */ |
942 | 0 | if (eprn_check_colour_info(eprn->cap->colour_info, &eprn->colour_model, |
943 | 0 | &device->HWResolution[0], &device->HWResolution[1], |
944 | 0 | &eprn->black_levels, &eprn->non_black_levels) != 0) { |
945 | 0 | gs_param_string str; |
946 | |
|
947 | 0 | eprintf1("%s" ERRPREF "The requested combination of colour model (", |
948 | 0 | epref); |
949 | 0 | str.size = 0; |
950 | 0 | if (eprn_get_string(eprn->colour_model, eprn_colour_model_list, &str) != 0) |
951 | 0 | assert(0); /* Bug. No harm on NDEBUG because I've just set the size. */ |
952 | 0 | errwrite(device->memory, (const char *)str.data, str.size * sizeof(str.data[0])); |
953 | 0 | eprintf7("),\n" |
954 | 0 | "%s resolution (%gx%g ppi) and intensity levels (%d, %d) is\n" |
955 | 0 | "%s not supported by the %s.\n", |
956 | 0 | epref, device->HWResolution[0], device->HWResolution[1], |
957 | 0 | eprn->black_levels, eprn->non_black_levels, epref, eprn->cap->name); |
958 | 0 | return_error(gs_error_rangecheck); |
959 | 0 | } |
960 | | |
961 | | /* Initialization for colour rendering */ |
962 | 0 | if (device->color_info.num_components == 4) { |
963 | | /* Native colour space is 'DeviceCMYK' */ |
964 | 0 | set_dev_proc(device, map_rgb_color, NULL); |
965 | |
|
966 | 0 | if (eprn->intensity_rendering == eprn_IR_FloydSteinberg) |
967 | 0 | set_dev_proc(device, map_cmyk_color, &eprn_map_cmyk_color_max); |
968 | 0 | else if (device->color_info.max_gray > 1 || device->color_info.max_color > 1) |
969 | 0 | set_dev_proc(device, map_cmyk_color, &eprn_map_cmyk_color_flex); |
970 | 0 | else |
971 | 0 | set_dev_proc(device, map_cmyk_color, &eprn_map_cmyk_color); |
972 | |
|
973 | 0 | if (eprn->intensity_rendering == eprn_IR_FloydSteinberg) |
974 | 0 | set_dev_proc(device, map_rgb_color, &eprn_map_rgb_color_for_CMY_or_K_max); |
975 | 0 | else if (device->color_info.max_gray > 1 || device->color_info.max_color > 1) |
976 | 0 | set_dev_proc(device, map_rgb_color, &eprn_map_rgb_color_for_CMY_or_K_flex); |
977 | 0 | else |
978 | 0 | set_dev_proc(device, map_rgb_color, &eprn_map_rgb_color_for_CMY_or_K); |
979 | |
|
980 | 0 | } |
981 | 0 | else { |
982 | 0 | set_dev_proc(device, map_cmyk_color, NULL); |
983 | |
|
984 | 0 | if (eprn->colour_model == eprn_DeviceRGB) { |
985 | 0 | if (eprn->intensity_rendering == eprn_IR_FloydSteinberg) |
986 | 0 | set_dev_proc(device, map_rgb_color, &eprn_map_rgb_color_for_RGB_max); |
987 | 0 | else if (device->color_info.max_color > 1) |
988 | 0 | set_dev_proc(device, map_rgb_color, &eprn_map_rgb_color_for_RGB_flex); |
989 | 0 | else |
990 | 0 | set_dev_proc(device, map_rgb_color, &eprn_map_rgb_color_for_RGB); |
991 | 0 | } else { |
992 | 0 | if (eprn->intensity_rendering == eprn_IR_FloydSteinberg) |
993 | 0 | set_dev_proc(device, map_rgb_color, &eprn_map_rgb_color_for_CMY_or_K_max); |
994 | 0 | else if (device->color_info.max_gray > 1 || device->color_info.max_color > 1) |
995 | 0 | set_dev_proc(device, map_rgb_color, &eprn_map_rgb_color_for_CMY_or_K_flex); |
996 | 0 | else |
997 | 0 | set_dev_proc(device, map_rgb_color, &eprn_map_rgb_color_for_CMY_or_K); |
998 | 0 | } |
999 | 0 | } |
1000 | 0 | eprn->output_planes = eprn_bits_for_levels(eprn->black_levels) + |
1001 | 0 | 3 * eprn_bits_for_levels(eprn->non_black_levels); |
1002 | |
|
1003 | 0 | #if !defined(GS_REVISION) || GS_REVISION >= 600 |
1004 | | /* According to my understanding, the following call should be superfluous |
1005 | | (because the colour mapping functions may not be called while the device |
1006 | | is closed) and I am also not aware of any situation where it does make a |
1007 | | difference. It shouldn't do any harm, though, and I feel safer with it :-) |
1008 | | */ |
1009 | 0 | gx_device_decache_colors(device); |
1010 | 0 | #endif |
1011 | |
|
1012 | 0 | #ifndef EPRN_NO_PAGECOUNTFILE |
1013 | | /* Read the page count value */ |
1014 | 0 | if (eprn->pagecount_file != NULL) { |
1015 | 0 | unsigned long count; |
1016 | 0 | if (pcf_getcount(device->memory, eprn->pagecount_file, &count) == 0) |
1017 | 0 | device->PageCount = count; |
1018 | | /* unsigned to signed. The C standard permits |
1019 | | an implementation to generate an overflow indication if the value is |
1020 | | too large. I consider this to mean that the type of 'PageCount' is |
1021 | | inappropriate :-). Note that eprn does not use 'PageCount' for |
1022 | | updating the file. */ |
1023 | 0 | else { |
1024 | | /* pcf_getcount() has issued an error message. */ |
1025 | 0 | eprintf( |
1026 | 0 | " No further attempts will be made to access the page count file.\n"); |
1027 | 0 | gs_free(device->memory->non_gc_memory, eprn->pagecount_file, strlen(eprn->pagecount_file) + 1, |
1028 | 0 | sizeof(char), "eprn_open_device"); |
1029 | 0 | eprn->pagecount_file = NULL; |
1030 | 0 | } |
1031 | 0 | } |
1032 | 0 | #endif /* !EPRN_NO_PAGECOUNTFILE */ |
1033 | | |
1034 | | /* Open the "prn" device part */ |
1035 | 0 | if ((rc = gdev_prn_open(device)) != 0) return rc; |
1036 | | |
1037 | | /* if device has been subclassed (FirstPage/LastPage device) then make sure we use |
1038 | | * the subclassed device. |
1039 | | */ |
1040 | 0 | while (device->child) |
1041 | 0 | device = device->child; |
1042 | 0 | eprn = &((eprn_Device *)device)->eprn; |
1043 | | |
1044 | | /* Just in case a previous open call failed in a derived device (note that |
1045 | | 'octets_per_line' is still the same as then): */ |
1046 | 0 | if (eprn->scan_line.str != NULL) |
1047 | 0 | gs_free(device->memory->non_gc_memory, eprn->scan_line.str, eprn->octets_per_line, sizeof(eprn_Octet), |
1048 | 0 | "eprn_open_device"); |
1049 | 0 | if (eprn->next_scan_line.str != NULL) { |
1050 | 0 | gs_free(device->memory->non_gc_memory, eprn->next_scan_line.str, eprn->octets_per_line, sizeof(eprn_Octet), |
1051 | 0 | "eprn_open_device"); |
1052 | 0 | eprn->next_scan_line.str = NULL; |
1053 | 0 | } |
1054 | | |
1055 | | /* Calls which might depend on prn having been initialized */ |
1056 | 0 | eprn->octets_per_line = gdev_prn_raster((gx_device_printer *)device); |
1057 | 0 | eprn->scan_line.str = (eprn_Octet *) gs_malloc(device->memory->non_gc_memory, eprn->octets_per_line, |
1058 | 0 | sizeof(eprn_Octet), "eprn_open_device"); |
1059 | 0 | if (eprn->intensity_rendering == eprn_IR_FloydSteinberg) { |
1060 | 0 | eprn->next_scan_line.str = (eprn_Octet *) gs_malloc(device->memory->non_gc_memory, eprn->octets_per_line, |
1061 | 0 | sizeof(eprn_Octet), "eprn_open_device"); |
1062 | 0 | if (eprn->next_scan_line.str == NULL && eprn->scan_line.str != NULL) { |
1063 | 0 | gs_free(device->memory->non_gc_memory, eprn->scan_line.str, eprn->octets_per_line, sizeof(eprn_Octet), |
1064 | 0 | "eprn_open_device"); |
1065 | 0 | eprn->scan_line.str = NULL; |
1066 | 0 | } |
1067 | 0 | } |
1068 | 0 | if (eprn->scan_line.str == NULL) { |
1069 | 0 | eprintf1("%s" ERRPREF |
1070 | 0 | "Memory allocation failure from gs_malloc() in eprn_open_device().\n", |
1071 | 0 | epref); |
1072 | 0 | return_error(gs_error_VMerror); |
1073 | 0 | } |
1074 | | |
1075 | 0 | return rc; |
1076 | 0 | } |
1077 | | |
1078 | | /****************************************************************************** |
1079 | | |
1080 | | Function: eprn_close_device |
1081 | | |
1082 | | ******************************************************************************/ |
1083 | | |
1084 | | int eprn_close_device(gx_device *device) |
1085 | 0 | { |
1086 | 0 | eprn_Eprn *eprn = &((eprn_Device *)device)->eprn; |
1087 | |
|
1088 | | #ifdef EPRN_TRACE |
1089 | | if_debug0(EPRN_TRACE_CHAR, "! eprn_close_device()...\n"); |
1090 | | #endif |
1091 | |
|
1092 | 0 | if (eprn->scan_line.str != NULL) { |
1093 | 0 | gs_free(device->memory->non_gc_memory, eprn->scan_line.str, eprn->octets_per_line, sizeof(eprn_Octet), |
1094 | 0 | "eprn_close_device"); |
1095 | 0 | eprn->scan_line.str = NULL; |
1096 | 0 | } |
1097 | 0 | if (eprn->next_scan_line.str != NULL) { |
1098 | 0 | gs_free(device->memory->non_gc_memory, eprn->next_scan_line.str, eprn->octets_per_line, sizeof(eprn_Octet), |
1099 | 0 | "eprn_close_device"); |
1100 | 0 | eprn->next_scan_line.str = NULL; |
1101 | 0 | } |
1102 | |
|
1103 | 0 | return gdev_prn_close(device); |
1104 | 0 | } |
1105 | | |
1106 | | /****************************************************************************** |
1107 | | |
1108 | | Function: eprn_forget_defaultmatrix |
1109 | | |
1110 | | This function tells the ghostscript kernel to forget the default matrix, |
1111 | | i.e., to consult the get_initial_matrix device procedure the next time the |
1112 | | default CTM is needed. |
1113 | | |
1114 | | ******************************************************************************/ |
1115 | | |
1116 | | static void eprn_forget_defaultmatrix(gx_device *device) |
1117 | 0 | { |
1118 | 0 | eprn_Eprn *eprn = &((eprn_Device *)device)->eprn; |
1119 | |
|
1120 | 0 | gs_setdefaultmatrix((gs_gstate *)eprn->pgs, NULL); |
1121 | |
|
1122 | 0 | return; |
1123 | 0 | } |
1124 | | |
1125 | | /****************************************************************************** |
1126 | | |
1127 | | Function: eprn_output_page |
1128 | | |
1129 | | This function is a wrapper for gdev_prn_output_page() in order to catch the |
1130 | | number of pages printed and to initialize the eprn_get_planes() API. |
1131 | | |
1132 | | ******************************************************************************/ |
1133 | | |
1134 | | int eprn_output_page(gx_device *dev, int num_copies, int flush) |
1135 | 0 | { |
1136 | 0 | eprn_Eprn *eprn = &((eprn_Device *)dev)->eprn; |
1137 | 0 | int rc; |
1138 | |
|
1139 | | #ifdef EPRN_TRACE |
1140 | | clock_t start_time = clock(); |
1141 | | if_debug0(EPRN_TRACE_CHAR, "! eprn_output_page()...\n"); |
1142 | | #endif |
1143 | | |
1144 | | /* Initialize eprn_get_planes() data */ |
1145 | 0 | eprn->next_y = 0; |
1146 | 0 | if (eprn->intensity_rendering == eprn_IR_FloydSteinberg) { |
1147 | | /* Fetch the first line and store it in 'next_scan_line'. */ |
1148 | 0 | if (eprn_fetch_scan_line((eprn_Device *)dev, &eprn->next_scan_line) == 0) |
1149 | 0 | eprn->next_y++; |
1150 | 0 | } |
1151 | | |
1152 | | /* Ship out */ |
1153 | 0 | rc = gdev_prn_output_page(dev, num_copies, flush); |
1154 | | |
1155 | | /* CUPS page accounting message. The CUPS documentation is not perfectly |
1156 | | clear on whether one should generate this message before printing a page |
1157 | | or after printing has been successful. The rasterto* filters generate it |
1158 | | before sending the page, but as the scheduler uses these messages for |
1159 | | accounting, this seems unfair. |
1160 | | */ |
1161 | 0 | if (rc == 0 && eprn->CUPS_accounting) |
1162 | 0 | eprintf2("PAGE: %ld %d\n", dev->ShowpageCount, num_copies); |
1163 | | /* The arguments are the number of the page, starting at 1, and the number |
1164 | | of copies of that page. */ |
1165 | |
|
1166 | 0 | #ifndef EPRN_NO_PAGECOUNTFILE |
1167 | | /* On success, record the number of pages printed */ |
1168 | 0 | if (rc == 0 && eprn->pagecount_file != NULL) { |
1169 | 0 | assert(num_copies > 0); /* because of signed/unsigned */ |
1170 | 0 | if (pcf_inccount(dev->memory, eprn->pagecount_file, num_copies) != 0) { |
1171 | | /* pcf_inccount() has issued an error message. */ |
1172 | 0 | eprintf( |
1173 | 0 | " No further attempts will be made to access the page count file.\n"); |
1174 | 0 | gs_free(dev->memory->non_gc_memory, eprn->pagecount_file, strlen(eprn->pagecount_file) + 1, |
1175 | 0 | sizeof(char), "eprn_output_page"); |
1176 | 0 | eprn->pagecount_file = NULL; |
1177 | 0 | } |
1178 | 0 | } |
1179 | 0 | #endif /* !EPRN_NO_PAGECOUNTFILE */ |
1180 | | |
1181 | | /* If soft tumble has been demanded, ensure the get_initial_matrix procedure |
1182 | | is consulted for the next page */ |
1183 | 0 | if (eprn->soft_tumble) eprn_forget_defaultmatrix(dev); |
1184 | |
|
1185 | | #ifdef EPRN_TRACE |
1186 | | if_debug1(EPRN_TRACE_CHAR, "! eprn_output_page() terminates after %f s.\n", |
1187 | | ((float)(clock() - start_time))/CLOCKS_PER_SEC); |
1188 | | #endif |
1189 | |
|
1190 | 0 | return rc; |
1191 | 0 | } |