Skip to main content
Skip table of contents

The need for shadowop

This page applies to Harlequin v13.1r0 and later; both Harlequin Core and Harlequin MultiRIP.

Simple redefinition is the basis of separator programs such as Adobe Separator and Aldus PrePrint. Consider trying to produce a cyan separation. If the setcmykcolor operator is redefined to set the gray value to the desired cyan component intensity (by subtracting the intensity from one) and discard the other operands, only the cyan part of the color is produced:

            /setcmykcolor {
              pop pop pop 1 exch sub setgray
            } bind def

If we treat all the relevant operators in analogous ways, we have produced a separation.

Where one system has control over the PostScript it is generating, this is a satisfactory way of working; however, where a fragment of PostScript redefines an operator outside a job to change its behavior inside, there are a number of pitfalls (in principle, stand-alone separator programs could cause jobs to fail because of this):

  • An operator cannot be redefined more than once to do something and then call the original, otherwise an infinite recursive loop will result. For example if we have an original redefinition:

/setgray { (executing setgray) == setgray } bind def

the bind operator ensures that the setgray within the procedure is the operator, not a name to look up. However, if we then add the following, perhaps unaware of the first redefinition:

/setgray { (operand:) = dup == setgray } bind def

then the setgray here is not bound by the bind operator, because when the name is looked up by the bind operator it finds the earlier redefinition, which is a procedure, not an operator. Then when setgray is run it prints the messages and then finds the name setgray, which it looks up in the dictionary stack and resolves as the second redefinition, repeats the messages and looks up setgray again, and so on. This results in an infinitely repeated series of messages.

  • An operator is not the same as a procedure. For most purposes this does not matter. If the interpreter simply encounters the name setgray, the procedure or operator to which it refers will be executed, and providing the number of operands consumed is correct, the program will behave as expected. A contrived and rare case where it would not behave as expected is in testing the type, like this, where the if procedure is not executed if setgray resolves to a procedure (an executable array):

/setgray load type /operatortype eq {...} if

A more realistic example which goes wrong because of the same distinction, is in using the // construct, or loading the operator:

/proc [ ... //setgray ... /setgray load ... ] cvx def

If setgray resolves to an operator, executing proc will cause setgray to be executed twice; but if it resolves to a procedure, the procedure will simply be pushed on the stack and left there each time an extra exec is required to execute it.

  • Sometimes an application will explicitly look up the operator in systemdict, bypassing any redefinition:

/setgray { (operand) = dup == //systemdict /setgray get exec } bind def

JavaScript errors detected

Please note, these errors can depend on your browser setup.

If this problem persists, please contact our support.