Main index Placemat: main guide About author

Valid HTML 4.01 Transitional

PostScript Routines

Julian D. A. Wiseman



The author has written a program, in PostScript, to render placemats for glasses at a wine tasting. That program can be found at, and the documentation at and The code contains several routines that other PostScript programmers might wish to reuse, described here.

However, users are cautioned that this ‘manual’ tends to lag the code.

General-purpose routines


string1 string2 Concatenate string

Concatenate concatenates the two strings. An older version, available at, would work with strings or arrays, and did some error handling.


mark thing0 thing1 … ConcatenateToMark string

ConcatenateToMark concatenates the items, converting non-strings to strings using ToString.


- CurrentFontName name

CurrentFontName returns the name of the current font.


- CurrentFontSize num

CurrentFontSize returns the size of the current font, such that CurrentFontName CurrentFontSize selectfont has no effect.


int1 int2 GreatestCommonDivisor int

GreatestCommonDivisor returns the greatest common divisor of the two integers. There is a small speed gain if int1 ≥ int2. If it is not known which is larger, it is not worth testing.


any IsNumber bool

IsNumber returns true if the parameter is an integer or a real, otherwise false.


array code HeapSort -

The code is a function that takes two of the items in array from the stack, and returns a boolean. HeapSort sorts the array such that for elements i and i+1 the comparison code returns true. E.g., [2 0 4 3 1] dup {lt} HeapSort == outputs [0 1 2 3 4]. This HeapSort was originally converted from Numerical Recipes in C by J Plowes, and then generalised by this author.


[ c0 c1 … ] MustBeAbove MustBeBelow Tolerance PolynomialRoots [ in-range roots ]

Returns real solutions to 0 = ∑i≥0 xi × ci that satisfy MustBeAbove > x > MustBeBelow. Up to cubics solutions are exact to within PostScript’s single precision. Beyond cubics the x’s will be correct to within ±Tolerance, though will usually be better than that. Roots of odd multiplicity are reported once as single roots (unless x = 0); roots of even multiplicity are reported either once or not at all (again, unless x = 0). For high order polynomials it is not very fast, but is currently called for polynomials of order no greater than a quartic.

PolynomialRoots computes multiple roots, and calls PolynomialRoot which finds a single root given a range containing that root. PolynomialRoot uses a new algorithm, seemingly better than Brent’s Method. Assume that the Lower and Upper bounds on x are xL and xU, with the values of the polynomial (or in the more general case of any function) being fL and fU, these having product ≤ 0. Linear interpolation takes the next guess, x′, to be xL + (xUxL) × fL ÷ (fLfU). The new method (Wiseman’s Method?) changes this to xL + (xUxL) × Max[ 0.143, Min[ 0.857, [fL ÷ (fLfU) ]]. The optimal value for the constant is not known, but some simple experiments suggested that it is not far from one seventh (and one minus this). Broadly, this method does linear interpolation, except when that might be mis-behaving, when the interval size typically diminishes by a factor ≈ 7.


Routines, and one piece of code, that simplify debugging and improve the programming environment.


- CountGraphicsStack int

The code that creates CountGraphicsStack, and initialises it to zero, also redefines gsave (to increment CountGraphicsStack) and grestore (to decrement). Thereafter CountGraphicsStack plays a role analagous to that of countdictstack, checking that gsaves and grestores are balanced. Some help in writing CountGraphicsStack was received here.)


any ThingToDebugText string

ThingToDebugText creates a text representation of any, working well with code, names, numbers, booleans, dictionaries, or arrays of anything which which it works well.


- OutputLogToPage bool

OutputLogToPage is user-created boolean, controlling whether output to the log file is also sent to an extra page at the end of the PDF. Users whose only PostScript interpreter does not produce a log file, such as Mac Preview, should use this. The boolean is tested by code at the end of the PostScript program, code that calls a variety of routines and parameters, including SetPaperSize, and the array OutputLog created early in the program (also copy-paste this creation which also makes OutputToLog). Most functions described in this page call OutputToLog: include this code in your program.

Error reporting

The bulk of the PostScript program lies within a piece of code that is exec’d by a stopped command. If this stopped returns true a block of code then calls OutputToLog with much of the contents of the $error dictionary. This output is far more comprehensive than that output by default in Distiller or GhostScript. Anybody writing a large PostScript program is strongly advised to use this block of code, or something similar.



/Left|/Center|/Right /Top|/Middle|/Bottom num1 /Radius|/Diameter|/Height|/Width num2 int1 int2 DrawLinesInside AntiClockwise     Star     dict

Example stars

Star appends a star-shaped path to the currentpath.

A dictionary is left on the stack, it having values for LeftX (of the vertices), CenterX (of the enclosing circle), RightX, BottomY, CenterY (circle), TopY, Radius, InnerRadius, and the boolean AntiClockwise.

In the example on the right the columns have int1 from 5 to 10; and rows have num2 being 2, 3 and 4; DrawLinesInside being false.

Example analogue clock


/Left|/Center|/Right /Top|/Middle|/Bottom num1 num2 num3 num4 true AnalogueClock -

/Left|/Center|/Right /Top|/Middle|/Bottom num1 num2 false AnalogueClock -

AnalogueClock’s first two parameters, both names, have the same role as in Star; num1 is the width, num2 is the height. If the last parameter, the boolean, is true, then num3 is the hours (0 ≤ num3 ≤ 12), and num4 the minutes (0 ≤ num4 ≤ 60). If the last parameter is false no hands are shown.

In May 2011 this was removed from the main code, but can still be found at

Compound Objects

In many places the PostScript program accepts parameters in the form of a “compound object”. A compound object is any of the following.

There are a number of routines handling compound objects described below.

Note that the wide freedom allowed to code in a compound object causes some problems.


glyphname bool GlyphPath -

GlyphPath computes the path of a glyph, with the charpath-style boolean flag, destroying the currentpoint. Its design was based on suggestions made in this thread.


glyphname bool GlyphPathMoveto -

GlyphPathMoveto computes the path of a glyph, with the charpath-style boolean flag, moving the currentpoint as if after a show.


CompoundObject bool CharPathRecursive -

CharPathRecursive computes the path of a compound object, with the charpath-style boolean flag, destroying the currentpoint. This, and several other functions taking a CompoundObject, must work around two different bugs in distiller; one about forming paths with both charpath and lineto; the other about path construction differing after a call of nulldevice. CharPathRecursive calls GlyphPath, CharPathRec, CharPathRecMoveto and XcheckRecursive.


CompoundObject bool CharPathRecursiveMoveto -

CharPathRecursiveMoveto computes the path of a compound object, with the charpath-style boolean flag, moving the currentpoint as if after a show. Other than not destroying the currentpoint, CharPathRecursiveMoveto closely resembles CharPathRecursive. CharPathRecursiveMoveto calls GlyphPathMoveto, CharPathRecMoveto and XcheckRecursive.


CompoundObject StringHeight num

StringHeight computes the height of its parameter, that is, the difference between the top and bottom of its bounding box. Calls PathBBox.


CompoundObject StringMiddleOffset num

StringMiddleOffset computes the offset to the baseline required to centre the parameter vertically. Calls PathBBox.


CompoundObject StringTopOffset num

StringTopOffset computes the offset to the baseline required to align the top of the parameter vertically against the y value of the currentpoint. Calls PathBBox.


CompoundObject StringWidthRecursive num

StringWidthRecursive computes the width of the compound object. Unlike the operator stringwidth, it does not compute a vertical offset, as compound objects are always shown horizontally. Other than this no-y computation, StringWidthRecursive and stringwidth are analagous. Calls StringWidthRec.


CompoundObject ShowRecursive -

ShowRecursive is the compound-object version of show. The compound object is shown starting at the currentpoint, and in the current font, though either of these might be changed by code within the compound object.


CompoundObject LengthCompoundObject -

LengthCompoundObject computes the width of the parameter in characters, and is hence meant to be analagous to length. Before any code execution, EffectiveNumCharacters is set to zero. After the code execution EffectiveNumCharacters is added to the length. Hence code rendering a glyph or sequence of glyphs can set EffectiveNumCharacters to be the effective number of ‘characters’, whatever that might be wanted to mean.


CompoundObject XcheckRecursive bool

XcheckRecursive returns true if the parameter contains any code, however deeply within nested arrays, otherwise false.


CompoundObject DeGlyphRecursive string

DeGlyphRecursive approximates a compound object with a string. PDF pages can be named, but the name is passed to pdfmark as a string. But page names might contain, for example, accents. DeGlyphRecursive strips accents, and approximates other glyphs.

Paths and Output


- PathBBox llx lly urx ury

PathBBox is what the lower-case operator pathbbox should have been. From PLRM3 on pathbbox: “If the user coordinate system is rotated (other than by a multiple of 90 degrees) or skewed, the bounding box returned may be larger than expected.” This is not so for PathBBox which, further, does not need a preceding flattenpath. PathBBox works using pathforall, and for curve pieces solves the quadratic to compute precise turning points.

If the current path contains any line or curves, isolated moves are ignored. If the current path contains only isolated moves, the returned box includes all the moves. If the current path contains nothing, PathBBox invokes emptycurrentpath, which, if not defined by calling code, will cause distillation to terminate with an error.

PathBBox is about a hundred times slower than flattenpath pathbbox, and was discussed in this thread.


bool1 bool2 name1 name2 SetPaperSize -

The parameter name1 should be one of /A0 though to /A9, or /USL (for 8½″×11″ US Letter), /USL2 (for 11″×17″ US Ledger), /USLegal (for 8½″×14″ US Legal), or /A4_USL (the width of A4, the 11″ height of US Letter). The parameter name2 should be one of /Landscape or /Portrait. If bool1 then setpagedevice is called. If bool2 then the sizes are echoed to the log by calling OutputToLog. SetPaperSize thus sets the paper size. Calls Dimensions.

Example of titles filled with text


numx numy CompoundObject numSpaces numLineStep numAngle int code1 code2 RepeatClippedWithin -

RepeatClippedWithin paints repeated copies of a compound object within a path. On calling RepeatClippedWithin there is a current font. The current path is used as the clipping path, and remains the current path on exit. The CompoundObject is the object shown repeatedly. Horizontally, these are separated by numSpaces space widths. Lines are numLineStep apart. The compound object is shown at angle numAngle. The int parameter is the number of outlines shown. The innermost colour, that is the fill colour of the compound object, is set by code1, and the other by code2. The numx and numy values determine a ‘starting point’ for the filling. In the example on the right RepeatClippedWithin has been called twice: the fills line up because the numx and numy values were the same. (Help on insideness testing was received here.)


{TransformFunction} num TransformPath -

TransformPath replaces the current path with a transformed version. The parameter TransformFunction does most of the work, it taking four parameters x, y, dx, dy (the last two being a vector, or a direction pointing away from x,y), and converts them to x', y', dx', dy'. Thus the first parameter converts a position, and a direction from that position, to a new position and a new direction. The second parameter, a number, is a distance in the transformed space that can be deemed small (with 0.12, or one pixel per inch at 600dpi, being sensible). If TransformPath appears not to work, a likely cause is a failure by the calling routine to pass code that correctly computes dx', dy'. Calls TransformMoveto, TransformLinetoClosepath, TransformLineto, TransformClosepath and TransformCurveto.

In May 2011 this was removed from the main code, but can still be found at

Julian D. A. Wiseman

Main index Top About author