Search code examples
postscript

In PostScript, define a procedure to define other procedures


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


Solution

  • 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