The purpose of this question is to understand PostScript a bit better from a programming perspective. The goal described below is just an example used for illustration.
In the PostScript language, I can define a procedure to set the current graphics color as follows:
/cRED { 1 0 0 setrgbcolor } def % define a procedure to set the color to red.
I was wondering if there was a way to define a procedure that would define other color procedures. Assume that such a procedure called cdef
were defined. I could use it as follows:
/cRED 1 0 0 cdef
This should have the same effect as the previous definition of cRED.
The problem is that I can't seem to figure out how to "capture" the literal value of items on the stack in a procedure passed to def
.
I tried the following:
/cdef { /B exch def /G exch def /R exch def { R G B setrgbcolor } bind def } def
/cRED 1 0 0 cdef
/cGRN 0 1 0 cdef
/cBLU 0 0 1 cdef
My expectation was that by using bind
the values of R
G
and B
would be captured literally. I.e. I was expecting the code above to be equivalent to this:
/cRED { 1 0 0 setrgbcolor } def
/cGRN { 0 1 0 setrgbcolor } def
/cBLU { 0 0 1 setrgbcolor } def
Unfortunately the actual result is that cRED
cGRN
and cBLU
all set the color to blue. This is because cRED
cGRN
and cBLU
are still dependent on the R
G
and B
objects (which are globals). And so the color is blue for all because cBLU
defined last, setting R
G
and B
. Apparently bind
didn't work the way I expected.
Is there a way to define cdef
to achieve this? The crux of the issue is being able to pop a value off the stack and store it literally using def
. E.g. something like this pseudo-code:
/cdef { { $ $ $ setrgbcolor } bind def } def
Where $
woudl be replaced with the literal value of the item at the top of the stack when cdef is being evaluated. So /cCYN 0 1 1 cdef
is evaluated as /cCYN { 0 1 1 setrgbcolor } bind def
Is there some operator that fulfills the purpose of the $
as described above? The operators pop
, =
, and index
are close, but don't seem to work.
In addition, the use of Immediately Evaluated Names (e.g. //name
) seems promising, but they seem to be evaluated even before cdef
is executed.
Thanks
Do it by "rolling" the parameters in place instead of assigning them
/cdef { [ 4 1 roll /setrgbcolor load ] cvx bind def } def
In that way, when the ] cvx bind def
inside is executed it finds on the operand stack
/YourNameForTheProcedure
[ (i.e. mark)
your three parameters (the mark has been rolled below them)
the setrgbcolor operator (or procedure?)
Then the closing ]
will make an array out of the three numbers and the setrgbcolor, that will be made into a procedure by cvx
.
Note: Then You have to pass the r,g,b parameters in the correct order:
/CR 1 0 0 cdef
/CG 0 1 0 cdef
/CB 0 0 1 cdef