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