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