1 % Copyright (C) 1994, 2000 Aladdin Enterprises. All rights reserved.
3 % This software is provided AS-IS with no warranty, either express or
6 % This software is distributed under license and may not be copied,
7 % modified or distributed except as expressly authorized under the terms
8 % of the license contained in the file LICENSE in this distribution.
10 % For more information about licensing, please refer to
11 % http://www.ghostscript.com/licensing/. For information on
12 % commercial licensing, go to http://www.artifex.com/licensing/ or
13 % contact Artifex Software, Inc., 101 Lucas Valley Road #110,
14 % San Rafael, CA 94903, U.S.A., +1(415)492-9861.
16 % $Id: gs_setpd.ps,v 1.27 2005/06/09 19:47:18 ray Exp $
17 % The current implementation of setpagedevice has the following limitations:
18 % - It doesn't attempt to "interact with the user" for Policy = 2.
20 languagelevel 1 .setlanguagelevel
23 % ---------------- Redefinitions ---------------- %
25 % Redefine .beginpage and .endpage so that they call BeginPage and
26 % EndPage respectively if appropriate.
28 % We have to guard against the BeginPage procedure not popping its operand.
29 % This is really stupid, but the Genoa CET does it.
30 /.beginpage { % - .beginpage -
31 .currentshowpagecount {
32 .currentpagedevice pop
33 dup //null ne { /BeginPage .knownget } { pop //false } ifelse {
34 % Stack: ... pagecount proc
36 % Stack: ... ..???.. oldcount
37 count 1 add exch sub { pop } repeat
44 % Guard similarly against EndPage not popping its operand.
45 /.endpage { % <reason> .endpage <print_bool>
46 .currentshowpagecount {
47 1 index .currentpagedevice pop
48 dup //null ne { /EndPage .knownget } { pop //false } ifelse {
49 % Stack: ... reason pagecount reason proc
51 % Stack: ... ..???.. print oldcount
52 count 2 add exch sub { exch pop } repeat
61 % Define interpreter callouts for handling gstate-saving operators,
62 % to make sure that they create a page device dictionary for use by
63 % the corresponding gstate-restoring operator.
64 % We'd really like to avoid the cost of doing this, but we don't see how.
65 % The names %gsavepagedevice, %savepagedevice, %gstatepagedevice,
66 % %copygstatepagedevice, and %currentgstatepagedevice are known to the
69 (%gsavepagedevice) cvn
70 { currentpagedevice pop gsave
74 { currentpagedevice pop save
77 (%gstatepagedevice) cvn
78 { currentpagedevice pop gstate
81 (%copygstatepagedevice) cvn
82 { currentpagedevice pop copy
85 (%currentgstatepagedevice) cvn
86 { currentpagedevice pop currentgstate
89 % Define interpreter callouts for handling gstate-restoring operators
90 % when the current page device needs to be changed.
91 % The names %grestorepagedevice, %grestoreallpagedevice,
92 % %restorepagedevice, %restore1pagedevice, and %setgstatepagedevice
93 % are known to the interpreter.
96 { % Since setpagedevice doesn't create new device objects,
97 % we must (carefully) reinstall the old parameters in
99 .currentpagedevice pop //null currentdevice //null .trysetparams
100 dup type /booleantype eq
102 { % This should never happen!
103 SETPDDEBUG { (Error in .trysetparams!) = pstack flush } if
104 cleartomark pop pop pop
105 /.installpagedevice cvx /rangecheck signalerror
108 % A careful reading of the Red Book reveals that an erasepage
109 % should occur, but *not* an initgraphics.
113 /.uninstallpagedevice
114 { 2 .endpage { .currentnumcopies //false .outputpage } if
118 (%grestorepagedevice) cvn
119 { .uninstallpagedevice grestore .installpagedevice
122 (%grestoreallpagedevice) cvn
123 { .uninstallpagedevice grestore .installpagedevice grestoreall
126 (%restore1pagedevice) cvn
127 { .uninstallpagedevice grestore .installpagedevice restore
130 (%restorepagedevice) cvn
131 { .uninstallpagedevice restore .installpagedevice
134 (%setgstatepagedevice) cvn
135 { .uninstallpagedevice setgstate .installpagedevice
138 % Redefine .currentnumcopies so it consults the NumCopies device parameter.
141 .dicttomark readonly def
144 { currentdevice //.numcopiesdict .getdeviceparams
145 dup type /integertype eq
146 { exch pop exch pop }
147 { cleartomark #copies }
151 % Redefine .currentpagedevice and .setpagedevice so they convert between
152 % null and a fixed empty directionary.
153 /.nullpagedevice 0 dict readonly def
154 /.currentpagedevice {
155 //.currentpagedevice exch dup //null eq { pop //.nullpagedevice } if exch
158 dup //.nullpagedevice eq { pop //null } if //.setpagedevice
161 % ---------------- Auxiliary definitions ---------------- %
163 % Define the required attributes of all page devices, and their default values.
164 % We don't include attributes such as .MediaSize, which all devices
165 % are guaranteed to supply on their own.
166 /.defaultpolicies mark
170 dup /.LockSafetyParams known {
171 % Only possible error is invalidaccess
172 /setpagedevice .systemvar /invalidaccess signalerror
177 .dicttomark readonly def
178 % Note that the values of .requiredattrs are executed, not just fetched.
180 /PageDeviceName //null
181 /PageOffset [0 0] readonly
182 % We populate InputAttributes with all of the known page sizes
183 % followed by a dummy media type that handles pages of any size.
184 % This will create some duplicates, but that only slightly slows
185 % down the media selection (loop is in zmedia2.c).
187 % Some PostScript creators assume that slot 0 is the default media
188 % size and some can't handle a non-standard 4-element array which
189 % is a 'range' type page size (always put last).
191 % Real Devices that can only handle specific page sizes will override this.
194 % First put the device's default page size in slot 0
195 % This satifies those that have devices built with a4 as the default
196 0 mark /PageSize currentdevice /PageSize gsgetdeviceprop .dicttomark
197 statusdict /.pagetypenames get {
198 counttomark 1 sub 2 idiv exch mark exch /PageSize exch
199 % stack: mark --dict-- --dict-- ... key mark /PageSize pagetypename
200 % see note above about pagetype executable array contents.
201 load dup 0 get exch 1 get 2 array astore .dicttomark
203 % If NORANGEPAGESIZE is defined, (-dNORANGEPAGESIZE), then don't add
204 % the 'match any' PageSize entry
205 systemdict /NORANGEPAGESIZE known not {
206 % Add one last entry which is the 4 element range array (non-standard)
208 % PageSize with either dimension 0 will be detected in
209 % match_page_size, so we can allow it here
210 mark /PageSize [0 dup 16#7ffff dup] .dicttomark
216 mark 0 mark .dicttomark readonly .dicttomark
218 (%MediaDestination) 0
219 /Install {{.callinstall}} bind
220 /BeginPage {{.callbeginpage}} bind
221 /EndPage {{.callendpage}} bind
222 /Policies .defaultpolicies
223 /ImagingBBox //null % default value
224 /UseCIEColor /.getuseciecolor load
225 .dicttomark readonly def
227 % Define currentpagedevice so it creates the dictionary on demand if needed,
228 % adding all the required entries defined just above.
229 % We have to deal specially with entries that the driver may change
232 /.MediaSize dup % because it changes when PageSize is set
237 .dicttomark readonly def
238 /.makecurrentpagedevice { % - .makecurrentpagedevice <dict>
239 currentdevice //null .getdeviceparams
240 % Make the dictionary large enough to add defaulted entries.
241 counttomark 2 idiv .requiredattrs length add dict
242 counttomark 2 idiv { dup 4 2 roll put } repeat exch pop
243 % Add any missing required attributes.
244 % Make a writable and (if possible) local copy of any default
245 % dictionaries, to work around a bug in the output of WordPerfect,
246 % which assumes that these dictionaries are writable and local.
247 .currentglobal exch dup gcheck .setglobal
249 2 index 2 index known {
250 1 index /Policies eq {
251 % Merge policies from the device driver with defaults
252 2 index % <<>> /key value <<>>
253 3 2 roll get % <<>> value <<policies>>
255 2 index 2 index known {
266 exec 2 index 3 1 roll put
268 } forall exch .setglobal
274 pop .makecurrentpagedevice
276 % If any of the dynamic keys have changed,
277 % we must update the page device dictionary.
278 currentdevice //.dynamicppkeys .getdeviceparams .dicttomark {
279 % Stack: current key value
280 2 index 2 index .knownget { 1 index ne } { //true } ifelse
282 { % This is the first entry being updated.
283 % Copy the dictionary to make it writable.
285 currentglobal 1 index dup gcheck currentglobal and setglobal
298 % If the device is the distiller device, update distillerparams that
299 % may have been changed by setdistillerparams
300 currentdevice .devicename /pdfwrite eq {
301 currentdistillerparams {
302 % Stack: current key value
303 2 index 2 index .knownget { 1 index ne } { //true } ifelse
304 { 2 index 3 1 roll put } { pop pop } ifelse
307 % If the dictionary was global and is now local, copy
308 % any global subsidiary dictionaries to local VM. This
309 % too is to work around the Word Perfect bug (see above).
312 dup type /dicttype eq { dup gcheck } { //false } ifelse {
313 % Copy-on-write, see above.
315 3 -1 roll dup length dict .copydict
318 .copytree 2 index 3 1 roll put
324 % We would like to do a .setpagedevice so we don't keep
325 % re-creating the dictionary. Unfortunately, the effect
326 % of this is that if any dynamic key changes (PageCount
327 % in particular), we will do the equivalent of a
328 % setpagedevice at the next restore or grestore.
329 % Therefore, we make the dictionary read-only, but
330 % we don't store it away. I.e., NOT:
331 % dup wcheck { .setpagedevice .currentpagedevice pop } if
337 % Copy a dictionary recursively.
338 /.copytree { % <dict> .copytree <dict'>
339 dup length dict exch {
340 dup type /dicttype eq { .copytree } if 2 index 3 1 roll put
344 % The implementation of setpagedevice is quite complex. Currently,
345 % everything but the media matching algorithm is implemented here.
347 % By default, we only present the requested changes to the device,
348 % but there are some parameters that require special merging action.
349 % Define those parameters here, with the procedures that do the merging.
350 % The procedures are called as follows:
351 % <merged> <key> <new_value> -proc- <merged> <key> <new_value'>
357 { 3 copy pop .knownget
359 { pop dup length dict }
360 { dup length 2 index length add dict .copydict }
365 ifelse .copydict readonly
369 /OutputAttributes 1 index
371 { 3 copy pop .knownget
372 { dup length 2 index length add dict .copydict }
376 .dicttomark readonly def
378 % Define the keys used in input attribute matching.
380 /PageSize /MediaColor /MediaWeight /MediaType /InsertSheet /ManualFeed
381 % The following are documented in Adobe's supplement for v2017.
382 /LeadingEdge /MediaClass
384 % Define other keys used in media selection.
385 /.inputselectionkeys [
386 /MediaPosition /Orientation
389 % Define the keys used in output attribute matching.
394 % Define all the parameters that should always be copied to the merged
398 .mergespecial { pop } forall
399 .inputattrkeys aload pop
400 .inputselectionkeys aload pop
401 .outputattrkeys aload pop
404 % Define the parameters that should not be presented to the device.
405 % The procedures are called as follows:
406 % <merged> <key> <value> -proc-
407 % The procedure leaves all its operands on the stack and returns
408 % true iff the key/value pair should be presented to .putdeviceparams.
409 /.presentspecial mark
410 .dynamicppkeys { pop //false } forall
411 % We must ignore an explicit request for .MediaSize,
412 % because media matching always handles this.
415 /OutputDevice //false
416 /PageDeviceName //false
418 /PageSize //false % obsolete alias for .MediaSize
419 /InputAttributes //false
423 { { 2 index /InputAttributes .knownget { //null eq } { //true } ifelse } }
427 .inputselectionkeys { //false } forall
428 /OutputAttributes //false
430 { { 2 index /OutputAttributes .knownget { //null eq } { //true } ifelse } }
438 { % HACK: don't transmit the color map, because
439 % window systems can change the color map on their own
440 % incrementally. Someday we'll have a better
441 % solution for this....
444 /ViewerPreProcess //false
445 /ImagingBBox //false % This prevents the ImagingBBox value in the setpagedevice
446 % from affecting the device's ImagingBBox parameter, but
447 % does retain a 'shadow' copy at the PostScript level.
448 % This is done for Adobe compatibility since Adobe does
449 % render marks outside the ImagingBBox (and QuarkXpress
451 .dicttomark readonly def
453 % Define access to device defaults.
454 /.defaultdeviceparams
455 { finddevice //null .getdeviceparams
458 % Select media (input or output). The hard work is done in an operator:
459 % <pagedict> <attrdict> <policydict> <keys> .matchmedia <key> true
460 % <pagedict> <attrdict> <policydict> <keys> .matchmedia false
461 % <pagedict> null <policydict> <keys> .matchmedia null true
462 /.selectmedia % <orig> <request> <merged> <failed> <-- retained
463 % <attrdict> <policydict> <attrkeys> <mediakey>
465 { 5 index 5 -2 roll 4 index .matchmedia
466 % Stack: orig request merged failed attrkeys mediakey
468 { 4 index 3 1 roll put pop
470 { % Adobe's implementations have a "big hairy heuristic"
471 % to choose the set of keys to report as having failed the match.
472 % For the moment, we report any keys that are in the request
473 % and don't have the same value as in the original dictionary.
474 5 index 1 index .knownget
475 { 4 index 3 1 roll put }
476 { 3 index exch .undef }
478 { % Stack: <orig> <request> <merged> <failed> <attrkey>
479 3 index 1 index .knownget
480 { 5 index 2 index .knownget { ne } { pop //true } ifelse }
482 ifelse % Stack: ... <failed> <attrkey> <report>
483 { 2 copy /rangecheck put }
491 % Apply Policies to any unprocessed failed requests.
492 % As we process each request entry, we replace the error name
493 % in the <failed> dictionary with the policy value,
494 % and we replace the key in the <merged> dictionary with its prior value
495 % (or remove it if it had no prior value).
497 % These procedures are called with the following on the stack:
498 % <orig> <merged> <failed> <Policies> <key> <policy>
499 % They are expected to consume the top 2 operands.
500 % NOTE: we currently treat all values other than 0, 1, or 7 (for PageSize)
501 % the same as 0, i.e., we signal an error.
502 0 { % Set errorinfo and signal a configurationerror.
503 pop dup 4 index exch get 2 array astore
504 $error /errorinfo 3 -1 roll put
506 /setpagedevice load /configurationerror signalerror
508 1 { % Roll back the failed request to its previous status.
509 SETPDDEBUG { (Rolling back.) = pstack flush } if
510 3 index 2 index 3 -1 roll put
511 4 index 1 index .knownget
512 { 4 index 3 1 roll put }
513 { 3 index exch .undef }
516 7 { % For PageSize only, just impose the request.
518 { pop pop 1 index /PageSize 7 put }
519 { .policyprocs 0 get exec }
522 .dicttomark readonly def
523 /.applypolicies % <orig> <merged> <failed> .applypolicies
524 % <orig> <merged'> <failed'>
525 { 1 index /Policies get 1 index
526 { type /integertype eq
527 { pop % already processed
529 { 2 copy .knownget not { 1 index /PolicyNotFound get } if
530 % Stack: <orig> <merged> <failed> <Policies> <key>
532 .policyprocs 1 index .knownget not { .policyprocs 0 get } if exec
539 % Prepare to present parameters to the device, by spreading them onto the
540 % operand stack and removing any that shouldn't be presented.
541 /.prepareparams % <params> .prepareparams -mark- <key1> <value1> ...
543 { % Stack: -mark- key1 value1 ... merged key value
544 .presentspecial 2 index .knownget
545 { exec { 3 -1 roll } { pop pop } ifelse }
552 % Put device parameters without resetting currentpagedevice.
553 % (.putdeviceparams clears the current page device.)
554 /.putdeviceparamsonly % <device> <Policies|null> <require_all> -mark-
555 % <key1> <value1> ... .putdeviceparamsonly
556 % On success: <device> <eraseflag>
557 % On failure: <device> <Policies|null> <req_all> -mark-
558 % <key1> <error1> ...
560 { counttomark 4 add 1 roll .putdeviceparams
561 dup type /booleantype eq { 3 } { counttomark 5 add } ifelse -1 roll
564 { pop .putdeviceparams
569 % Try setting the device parameters from the merged request.
570 /.trysetparams % <merged> <(ignored)> <device> <Policies>
572 { //true 4 index .prepareparams
573 % Add the computed .MediaSize.
574 % Stack: merged (ignored) device Policies -true-
575 % -mark- key1 value1 ...
576 counttomark 5 add index .computemediasize
577 exch pop exch pop /.MediaSize exch
578 SETPDDEBUG { (Putting.) = pstack flush } if
580 SETPDDEBUG { (Result of putting.) = pstack flush } if
583 % Compute the media size and initial matrix from a merged request (after
585 /.computemediasize % <request> .computemediasize
586 % <request> <matrix> <[width height]>
587 { dup /PageSize get % requested page size
588 1 index /InputAttributes get
589 2 index (%MediaSource) get get /PageSize get % media size
591 2 index /Policies get
592 dup /PageSize .knownget
593 { exch pop } { /PolicyNotFound get } ifelse % PageSize policy,
595 3 index /Orientation .knownget not { //null } if
596 4 index /RollFedMedia .knownget not { //false } if
597 matrix .matchpagesize not {
598 % This is a "can't happen" condition!
599 /setpagedevice load /rangecheck signalerror
604 % ---------------- setpagedevice itself ---------------- %
607 { % We mustn't pop the argument until the very end,
608 % so that the pseudo-operator machinery can restore the stack
609 % if an error occurs.
610 mark 1 index currentpagedevice
612 % Check whether we are changing OutputDevice;
613 % also handle the case where the current device
614 % is not a page device.
615 % Stack: mark <request> <current>
616 SETPDDEBUG { (Checking.) = pstack flush } if
618 dup /OutputDevice .knownget
619 { % Current device is a page device.
620 2 index /OutputDevice .knownget
621 { % A specific OutputDevice was requested.
631 { % Current device is not a page device.
632 % Use the default device.
633 1 index /OutputDevice .knownget not { .defaultdevicename } if
639 { exch pop .defaultdeviceparams
640 % In case of duplicate keys, .dicttomark takes the entry
641 % lower on the stack, so we can just append the defaults here.
642 .requiredattrs { exec } forall .dicttomark
646 % Check whether a viewer wants to intervene.
647 % We must check both the request (which takes precedence)
648 % and the current dictionary.
649 % Stack: mark <request> <orig>
650 exch dup /ViewerPreProcess .knownget
652 { 1 index /ViewerPreProcess .knownget { exec } if }
655 % Construct a merged request from the actual request plus
656 % any keys that should always be propagated.
657 % Stack: mark <request> <orig>
658 SETPDDEBUG { (Merging.) = pstack flush } if
660 exch 1 index length 1 index length add dict
662 { % Stack: <orig> <request> <merged> <key>
663 3 index 1 index .knownget { 3 copy put pop } if pop
666 % Stack: <orig> <request> <merged>
668 { % stack: <orig> <request> <merged> <merged> <rkey> <rvalue>
669 .mergespecial 2 index .knownget { exec } if
673 % Hack: if FIXEDRESOLUTION is true, discard any attempt to
674 % change HWResolution.
675 FIXEDRESOLUTION { dup /HWResolution .undef } if
676 % Hack: if FIXEDMEDIA is true, discard any attempt to change
677 % PageSize or HWSize.
679 { dup /PageSize 4 index /PageSize get put
680 dup /HWSize 4 index /HWSize get put
682 % Hack: to work around some files that take a PageSize
683 % from InputAttributes and impose it, discard any attempt
684 % to set PageSize to a 4-element value.
685 % Stack: mark <orig> <request> <merged>
686 dup /PageSize .knownget {
688 dup /PageSize 4 index /PageSize get put
692 % Select input and output media.
693 % Stack: mark <orig> <request> <merged>
694 SETPDDEBUG { (Selecting.) = pstack flush } if
697 1 index /InputAttributes .knownget
698 { 2 index /Policies get
699 .inputattrkeys (%MediaSource) cvn .selectmedia
701 1 index /OutputAttributes .knownget
702 { 2 index /Policies get
703 .outputattrkeys (%MediaDestination) cvn .selectmedia
705 3 -1 roll 4 1 roll % temporarily swap orig & request
707 3 -1 roll 4 1 roll % swap back
709 % Construct the new device, and attempt to set its attributes.
710 % Stack: mark <orig> <request> <merged> <failed>
711 SETPDDEBUG { (Constructing.) = pstack flush } if
713 currentdevice .devicename 2 index /OutputDevice get eq
715 { 1 index /OutputDevice get finddevice }
717 %**************** We should copy the device here,
718 %**************** but since we can't close the old device,
719 %**************** we don't. This is WRONG.
720 %****************copydevice
721 2 index /Policies get
723 dup type /booleantype ne
724 { % The request failed.
725 % Stack: ... <orig> <request> <merged> <failed> <device>
726 % <Policies> true mark <name> <errorname> ...
727 SETPDDEBUG { (Recovering.) = pstack flush } if
728 counttomark 4 add index
729 counttomark 2 idiv { dup 4 -2 roll put } repeat
731 % Stack: mark ... <orig> <request> <merged> <failed> <device>
733 6 2 roll 3 -1 roll 4 1 roll
735 3 -1 roll 4 1 roll 6 -2 roll
736 .trysetparams % shouldn't fail!
737 dup type /booleantype ne
738 { 2 { counttomark 1 add 1 roll cleartomark } repeat
739 /setpagedevice load exch signalerror
745 % The attempt succeeded. Install the new device.
746 % Stack: mark ... <merged> <failed> <device> <eraseflag>
747 SETPDDEBUG { (Installing.) = pstack flush } if
750 { 1 //true .outputpage
751 (>>setpagedevice, press <return> to continue<<\n) .confirm
754 % .setdevice clears the current page device!
755 .currentpagedevice pop exch
759 % Implement UseCIEColor directly if this is a LL3 system.
760 % The color substitution feature is now implemented in
761 % the interpreter, and this is used as an optimization.
763 % NB: This shoud be the only use of the .setuseciecolor
766 % If UseCIEColor is transitioned to false, set some
767 % color space other than /DeviceGray, to insure that
768 % initgraphics will actually perform a setcolorspace
769 % operation (there is an optimization in setcolorspace
770 % that does nothing if the operand and current color
771 % spaces are the same, and UseCIEColor is false).
773 /.setuseciecolor where
775 pop 1 index /UseCIEColor .knownget
777 dup .setuseciecolor not
778 { /DeviceRGB setcolorspace }
785 % Merge the request into the current page device,
786 % unless we're changing the OutputDevice.
787 % Stack: mark ... <merged> <failed>
788 exch currentpagedevice dup length 2 index length add dict
789 % Stack: mark ... <failed> <merged> <current> <newdict>
790 2 index /OutputDevice .knownget {
791 2 index /OutputDevice .knownget not { //null } if eq
795 % Same OutputDevice, merge the dictionaries.
798 % Different OutputDevice, discard the old dictionary.
801 % Initialize the default matrix, taking media matching
803 .computemediasize pop initmatrix concat
804 dup /PageOffset .knownget
805 { % Translate by the given number of 1/72" units in device X/Y.
807 2 index /HWResolution get dup 1 get exch 0 get
808 4 -1 roll mul 72 div 3 1 roll mul 72 div
809 idtransform translate
812 % We must install the new page device dictionary
813 % before calling the Install procedure.
815 .setdefaulthalftone % Set the default screen before calling Install.
816 dup /Install .knownget {
817 { .execinstall } stopped { .postinstall stop } { .postinstall } ifelse
823 % We break out the code after calling the Install procedure into a
824 % separate procedure, since it is executed even if Install causes an error.
825 % By making .execinstall a separate operator procedure, we get the stacks
826 % restored if it fails.
828 /.execinstall { % <proc> .execinstall -
829 % Because the interpreter optimizes tail calls, we can't just let
830 % the body of this procedure be 'exec', because that would lose
831 % the stack protection that is the whole reason for having the
832 % procedure in the first place. We hack this by adding a couple
833 % of extra tokens to ensure that the operator procedure is still
834 % on the stack during the exec.
838 /.postinstall { % mark ... <failed> <merged> .postinstall -
839 matrix currentmatrix .setdefaultmatrix
840 % Erase and initialize the page.
842 currentoverprint //false setoverprint 1 setcolor
844 0 setcolor setoverprint
847 % Clean up, calling PolicyReport if needed.
848 % Stack: mark ... <failed> <merged>
849 SETPDDEBUG { (Finishing.) = pstack flush } if
852 { 1 index /Policies get /PolicyReport get
853 counttomark 1 add 2 roll cleartomark