| Main index | Placemat: main guide | About author |
Julian D. A. Wiseman
Contents: Variables that parameters may inspect; Code inside compound strings: summary; Code inside compound strings: examples.
Various of the parameters of the placemat are intended to hold code, or can accept code as well as simpler types. Herein find explanation and examples.
It is assumed that the reader is familiar with the simpler parameters of the code to make the glasses placemat, described at www.jdawiseman.com/papers/placemat/placemat.html.
Many parameters may be set to code, and this code may access internal variables. E.g., /OutlineTitles {WithinPage 2 mod 0 eq} def will make alternate circles outlined and not.
Several variables may well be available.
At the page level:
ThisPageDecanterLabels, being defined only on glasses pages: false for place settings, true for the decanter labels. Equalling NameNum Names length ge.
ThisName, being ThisPageDecanterLabels {DecanterLabelsName} {Names NameNum get} ifelse, in which DecanterLabelsName is (Decanter labels).
TypeOfPagesBeingRendered, being /Glasses if this is a glasses page; /TastingNotes if this is a tasting-note page; /PlaceName if a place-name page; /PrePour if a pre-pour sheet; /StickyLabels if a sticky-label page; /VoteRecorder if a vote-recorder page; /DecantingNotes if a decanting-notes page; and /Multiple if doing calculations applicable to multiple types of page.
On glasses sheets, SheetNum, an integer being the item of GlassesOnSheets currently being rendered. This also exists on pre-pour and sticky-label pages.
On tasting-note pages, TastingSheetNum, an integer being the item of GlassesOnTastingNotePages currently being rendered.
On place-name pages, PlaceNameSheetNum, an integer ≥0 and ≤PlaceNamesNumSheets–1, being the number of the place-name sheet currently being rendered.
Also only on place-name pages, SideFacingNamedPerson, a boolean which indicates which side of the place-name is being rendered. If accessing SideFacingNamedPerson then VariesByNameAnythingElse should be true whenever TypeOfPagesBeingRendered is /PlaceName (satisfied by VariesByNameAnythingElse being deffed to either {TypeOfPagesBeingRendered /PlaceName eq} or true).
On pre-pour pages, PrePourSheetNum, an integer ≥0 and ≤PrePourNumCopies–1, being the number of the pre-pour sheet currently being rendered.
On sticky-label pages, StickyLabelCopyNum, an integer ≥0 and ≤StickyLabelsNumCopies–1, being the number of the sticky label currently being rendered.
On vote-recorder pages, VoteRecorderTopTextNum. Also VoteRecorderSheetNum, less than the length of GlassesClusteredOnVoteRecorders.
On decanting-notes pages, DecantingNotesCopyNum, an integer ≥0 and ≤DecantingNotesNumCopies–1, being the number of the copy of the page. Also DecantingNotesSheetNum, less than the length of GlassesClusteredOnDecantingNotes.
PageWidth and PageHeight, from which it might be necessary to subtract some of the current used margins: MgnL, MgnR, MgnT, MgnB.
Radii, an array of reals holding the radii of the different glasses sheets. Also RadiiCirclearrayBaseline and RadiiCirclearrayInside, holding the distance from centre of the baseline and top of the Circlearrays.
CircletextMaxFontSizes, an array of reals with CircletextMaxFontSizes SheetNum get being the usual font size in which the Circlearrays are rendered (though the font size of any particular circle text might have been shrunk by CircletextsMinCopies, or altered by code within Circlearrays).
GlassPositions, a triple-depth array holding the positions of the glasses. GlassPositions SheetNum get WithinPage get is an array, [x y], where x and y are the position of the centre of the glass placement. GlassPositions is accessed by the function HalfDistanceBetweenCentresProportionRadius, which takes two integers, values of WithinPage, and returns, for the current value of SheetNum, ½ the distance between those circle centres divided by the radius. This function might well be used in FlightSeparationLines.
TitleFontSizes and similar variables AbovetitleFontSizes, BelowtitleFontSizes, and OvertitleFontSizes, being nested arrays. TitleFontSizes SheetNum get is an array of the same length as GlassesOnSheets SheetNum get, containing the sizes of the font at the start of rendering the Titles in those positions. Likewise for the others.
At the individual glass level (and thus available to glass-level settings such as OverlapSubtitlesOnTitles) there will also be:
WithinPage, being number of the glass on this page, thus running from zero to one less than the number of glasses on page SheetNum or TastingSheetNum. Hence TitleFontSizes SheetNum get WithinPage get is the font size of the title of the current circle.
WithinTitles, being number of item in the array Titles (and hence also of Subtitles, Circlearrays, and FillTexts).
Special cases:
If FlightSeparationPaintSeparately then FlightSeparationPaintCode may read FlightSeparationLineNum.
/Angle-WithTitles-to-HSB { GlassesOnSheets SheetNum get execU length dup dup 2 div CoprimeNear exch div mul exch 360 div add dup floor cvi sub 1 0.8 } bind def % /Angle-WithTitles-to-HSB /RaysStrokeCode { ThisName (Julian) eq {0.75 setgray stroke} % some think that colour in the placemats makes it harder to see the wine { 0.36 setlinewidth WithinPage0 WithinPage1 eq { RaysAngle0 WithinTitles0 Angle-WithTitles-to-HSB sethsbcolor stroke } { % Help received at groups.google.com/group/comp.lang.postscript/browse_thread/thread/888d4167a7eaadb2 << /Function << /BitsPerSample 8 /DataSource RaysAngle0 WithinTitles0 Angle-WithTitles-to-HSB gsave sethsbcolor currentrgbcolor grestore RaysAngle1 WithinTitles1 Angle-WithTitles-to-HSB gsave sethsbcolor currentrgbcolor grestore (123456) 5 -1 0 {exch dup 4 2 roll exch 255 mul 0.5 add floor cvi put} for /FunctionType 0 /Domain [0 1] /Range [0 1 0 1 0 1] /Order 1 /Size [2] /N 1 >> % Function dictionary /Coords [ X0 Y0 X1 Y1 ] /ShadingType 2 /ColorSpace /DeviceRGB /Extend [true dup] >> % Shading dictionary strokepath clipsave clip shfill cliprestore newpath } ifelse % WithinPage0 WithinPage1 eq } ifelse % ThisName ... } def % /RaysStrokeCode
The page entitled PostScript Routines lists some of the functions defined in the code, and these may be used by parameters. Noteworthy are BaseHeight, StringHeight, StringWidthRecursive, ShowRecursive, and CharPathRecursive. Also available will be any of the parameters to the code, as described on this page or on the main guide.
The code PaintBackgroundCode may draw directly on the page, and does so before anything else is drawn. Its brother, PaintForegroundCode, is executed after the other elements of the page have been painted. Either can be used to add a corporate logo. It is expected that either piece of code, if non-empty, would inspect TypeOfPagesBeingRendered, and then ThisPageDecanterLabels and perhaps SheetNum.
Available for use by PaintBackgroundCode and PaintForegroundCode is MakePathConnectingGlasses, a routine taking two integers, and making a path between points Radii SheetNum get away from those glass positions. The integers must be ≥0 but < the length of GlassesOnSheets SheetNum get. If the two items have similar x or y, the path drawn will be vertical or horizontal. Otherwise the line will emerge from the first position horizontally, and enter the second vertically.
Hence the following example of PaintBackgroundCode, on two otherwise identical seven-glass five-row portrait pages, will add lines as on the example above-right.
/PaintBackgroundCode { TypeOfPagesBeingRendered /Glasses eq { ThisPageDecanterLabels not { 0 1 SheetNum 1 eq {exch} if MakePathConnectingGlasses 0 2 SheetNum 1 eq {exch} if MakePathConnectingGlasses 1 2 SheetNum 1 eq {exch} if MakePathConnectingGlasses 6 setlinewidth 0.5 setgray 0 setlinecap stroke } if % ThisPageDecanterLabels not } if % TypeOfPagesBeingRendered /Glasses eq } def % /PaintBackgroundCode
In the compound ‘strings’ of arrays ([…]), it is permitted to include PostScript code as well as strings ((…)) and glyphs. It is intended that such code do only the following:
When user code within compound strings is being executed the current dictionary is UserScratchDict, which persists between executions of such user code. UserScratchDict will therefore hold any variables deffed. To avoid name clashes users should not store anything.
Code may assume that there is a currentpoint and currentfont, and this should still be true after execution. Any changes made by the code to the current path should be made by rmoveto, rlineto, and rcurveto; code should not alter the dictionary stack; nor alter anything that was on the stack at the start of code execution; nor leave changed the currentmatrix; nor change any of the many code variables.
User-entered code has the same access permissions as the program. Hence wilful or careless code can cause arbitrary confusion. Use with care, or not at all.


Some pairs of letters look better if nudged closer together. This is particularly true either side of a “W”, “V”, and even more if either of the neighbouring characters is an “A”. Compare the two versions of “Smith Woodhouse” (shown magnified 3×): the first not kerned; but in the second the “W” and “o” have been brought closer together with code thus:
[(Smith W) {-0.09 Kern} (oodhouse)]
The code in the parameter acts as if laying out the characters in an ordinary straight line. But when this is rendered as a part of one of the Circlearrays, the program keeps track of changes to the currentpoint, and converts horizontal changes to angular ones.
The author’s own initials, “JDAW”, also benefit from kerning, the two variations of these names being generated by:
/Names [ (JDAW) [(JDA) {-0.08 Kern} (W)] ] def
(The amount of the kerning is controlled by the “-0.08”: the optimal amount differs for different pairs of letters and and different typefaces.)
As these placemats are used by the author for port tastings, the same names keep recurring. Hence it makes sense to define well-kerned versions of these, most easily done by putting before the parameters the following definitions.
/JDAW [ (JDA) {-0.13 Kern} (W)] def /Grahams [(Graham) {-0.04 Kern} /quoteright {-0.04 Kern} (s)] def /OffleyBoaVista [(Of) /fl (ey Boa Vista)] def /RebelloValente [(Robertson) {-0.04 Kern} /quoteright {-0.09 Kern} (s Rebello V) {-0.092 Kern} (alente)] def /SmithWoodhouse [(Smith W) {-0.075 Kern} (oodhouse)] def /Taylor [(T) {-0.092 Kern} (aylor)] def /Vargellas [(V) {-0.092 Kern} (argellas)] def /Vesuvio [(V) {-0.10 Kern} (esuvio)] def /Warre [(W) {-0.065 Kern} (arre)] def

A vertical shift of the current point can also be useful, for example to to render the likes of “H2O” or “CH3CH2OH”. These are most elegantly done with repositioned /twosuperior and /threesuperior glyphs (“²”, “³”):
[(H) {0 CurrentFontSize -0.5 mul rmoveto} /twosuperior {0 CurrentFontSize 0.5 mul rmoveto} (O)]
[(CH) {0 CurrentFontSize -0.5 mul rmoveto} /threesuperior {0 CurrentFontSize 0.5 mul rmoveto} (CH) {0 CurrentFontSize -0.5 mul rmoveto} /twosuperior {0 CurrentFontSize 0.5 mul rmoveto} (OH)]
However sometimes a superscript is needed, in the sense of an ordinary character shown small and raised. For simplicity there are pre-written commands {SuperscriptOn} and {SuperscriptOff}. These are typically used in the header, as in the example in the right (though can of course be used in the Circlearrays).
/HeaderLeftText [ (Saturday 9) {SuperscriptOn} (th) {SuperscriptOff} ( February 2008, Boston) ] def

Likewise {SubscriptOn} and {SubscriptOff} can be used to render “H2O” and “CH3CH2OH”, as in the two following items of Circlearrays.
[ [(H) {SubscriptOn} (2) {SubscriptOff} (O)] ] [ [(CH) {SubscriptOn} (3) {SubscriptOff} (CH) {SubscriptOn} (2) {SubscriptOff} (OH)] ]

Changing the typeface mid-string is permitted, as in this hypothetical item of Circlearrays:
[ [{/TimesNewRomanPSMT CurrentFontSize selectfont} (Times)] [{/Helvetica CurrentFontSize selectfont} (Helvetica)] ]

Berry Brothers’ name contains an abbreviation, shown as a small “s” above a dot. The code below starts by saving the current font size in BrosOriginalFontSize. Then it calculates the height of the “s” (using the handy StringHeight function), of the “o”, and the dot, from which it can deduce the font size such that the dot, a gap half height of the dot, and the “s”, in the new size, sum to the height of the “o” in the old, and also deduce the vertical offset of the “s” (BrosVerticalOffset). Then the code changes the font size, and rmovetos up (it would also rmoveto right if the dot were wider than the “s”). Shows the “s”. Moves back down, and horizontally such that the s and the dot will have aligned centres, and shows the dot. Moves forwards to the end of the “s” (since the dot is narrower), and finally reverts the font size back to BrosOriginalFontSize, before showing the remainder of string (“ & Rudd Selection”).
[ (Berry Bro) { /BrosOriginalFontSize CurrentFontSize def /BrosHeight-s (s) StringHeight def /BrosHeight-dot (.) StringHeight def /BrosHeight-o (o) StringHeight def /BrosVerticalOffset BrosHeight-o BrosHeight-s 1.5 div BrosHeight-dot div 1 add div def CurrentFontName BrosOriginalFontSize BrosHeight-o mul BrosHeight-dot 1.5 mul BrosHeight-s add div selectfont /BrosWidth-s (s) StringWidthRecursive def /BrosWidth-dot (.) StringWidthRecursive def BrosWidth-dot BrosWidth-s gt {BrosWidth-dot BrosWidth-s sub 2 div} {0} ifelse BrosVerticalOffset rmoveto } (s) {BrosWidth-s BrosWidth-dot add -2 div BrosVerticalOffset neg rmoveto} (.) { BrosWidth-s BrosWidth-dot gt {BrosWidth-s BrosWidth-dot sub 2 div 0 rmoveto} if CurrentFontName BrosOriginalFontSize selectfont } ( & Rudd Selection) ]

It is permitted to include code that renders a shape. The following (in the example on the right used in Titles, Subtitles, Circlearrays and FillTexts) calls a routine, Star, that takes parameters:
leaving on the stack a dictionary containing useful values (LeftX CenterX RightX BottomY CenterY TopY Radius InnerRadius AntiClockwise).
[ (St) { /Left /Bottom BaseHeight /Height 90 7 3 false false Star fill begin RightX BottomY moveto end /EffectiveNumCharacters 1 def } (r) ]
(The /EffectiveNumCharacters 1 def tells the code how many characters is this shape, and is used by SameSizeTitlesIfAllOf. Of course the shape rendering code must finish by leaving the currentpoint at a suitable place.)
Users with competence in PostScript, or willing to acquire a little of same, will find it possible to do much or all of the text formatting that might be wanted.
Normally the glass descriptions will be the same for each person’s setting. But Titles, Subtitles, Circlearrays and FillTexts may be code that refers to NameNum. Particularly if FillTexts are used, this can substantially increase distill time, file size, and perhaps print time. By default the program assumes that these parameters are constant over users: they are rendered once (using execform), thus reducing file size and distill time. If parameters do vary with NameNum, it is important to def to true whichever is appropriate of VariesByNameTitlesAboveBelowOverOrnamentsDecanterLabels, VariesByNameCirclearrays, and VariesByNameAnythingElse. (But don’t do so idly: they can cause large increases in the file size.)
Julian D. A. Wiseman
| Main index | Top | About author |