| Main index | Placemat: main guide | About author |
Julian D. A. Wiseman
Contents: Varying parameters by page: summary; Varying parameters by page: visible parameters; 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.
When there are multipe pages of sheets to hold glasses (or, less frequently, for the tasting notes), it might be wanted to vary some parameters by page. To facilitate this CodeForGlassSheets is an array of items of code, of the same length as GlassesOnSheets. This code is invoked at the start of rendering each of the sheets described by GlassesOnSheets.
The simplest sensible use of this is embedded in the default parameters:
/CodeForGlassSheets GlassesOnSheets length 2 eq
{ [ {/FillTextAngle /LowerRight def} {/FillTextAngle /LowerLeft def} ] }
{ [ GlassesOnSheets length {{}} repeat ] }
ifelse def
That is, if the number of glass pages is exactly two then FillTextAngle is /LowerRight on the first page, and /LowerLeft on the second, so that everything appears to circle around the lower corner where the two sheets touch. And if if the number of glass pages is not two, then CodeForGlassSheets is an appropriate-length array of empty code: [ {} … {} ]. There is also a similar parameter CodeForTastingNoteSheets, with the same length as GlassesOnTastingNotePages, always defaulting to [ {} … {} ].
CodeForGlassSheets and CodeForTastingNoteSheets can also be used to vary headers, fonts, and almost any other parameter except Names, GlassesOnSheets, CodeForGlassSheets itself, and the GlassesOnTastingNotePages and CodeForTastingNoteSheets.
The code in CodeForGlassSheets may also draw directly on the page (and would do so before anything else is drawn), which might be used to add a corporate logo.
But this feature need not be over-used. If two sheets are to be a vastly different style, why not make them completely separately? The two possible reasons why not are:
If neither of these applies, consider splitting the job.
Code within CodeForGlassSheets is executed several times. As it might change the page size and hence the radius, it must be executed early. As it might change OverlapSubtitlesOnTitles, it must be executed before calculating the font sizes. And as it might make marks upon the page, it must be executed late.
So on some executions of the code, various internal parameters will not yet have been created. On others, they will. For CodeForGlassSheets, for the last execution, the one in which marks may be made, the boolean variable MayMarkPage will be true, at all earlier times it being false. When MayMarkPage is true also existing will be:
| 0 1 MakePathConnectingGlasses 0 2 MakePathConnectingGlasses |
1 0 MakePathConnectingGlasses 2 0 MakePathConnectingGlasses |
![]() |
![]() |
RenderingTastingNotesPages, having value false;
ThisPageDecanterLabels, usually false but for the decanter labels true;
SheetNum, an integer being the item of GlassesOnSheets currently being rendered;
GlassPositions, being a triple depth array. GlassPositions SheetNum get is an array, with one item for each glass on the page, each item being an array containing the x-y position of the center of the circle thus: [x y];
Radii, an array of reals with Radii SheetNum get being the radius on the sheet now being rendered;
MaxCircletextFontSizes, an array of reals with MaxCircletextFontSizes 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 MinCopiesCircletexts, or altered by code within Circlearrays). If CodeForGlassSheets writes any text, a sensible font and size in which to do it would be invoked by CircletextFont MaxCircletextFontSizes SheetNum get selectfont;
TitleFontSizes and similar variable SubtitleFontSizes, 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 SubtitleFontSizes and Subtitles;
BaseHeight, being a parameterless function returning the gap from the baseline to the top of BEGHKLNTVWXZ1fhijklt;
StringWidthRecursive, being a function that accepts one compound string (string, glyph, code, array of compound strings) and returns its width;
StringHeight, being a function that accepts one compound string and returns its top–bottom height;
ShowRecursive, doing the equivalent of show to a compound string;
CharPathRecursive, being a function taking a compound string and a boolean and doing the equivalent of charpath;
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.
Also available will be any of the parameters to the code, as described on this page or on the main guide, and perhaps as already altered by CodeForGlassSheets.
Hence the following example of CodeForGlassSheets, on two otherwise identical seven-glass five-row portrait pages, will add lines as on the example above-right.
/CodeForGlassSheets [
{
MayMarkPage ThisPageDecanterLabels not and
{
0 1 MakePathConnectingGlasses
0 2 MakePathConnectingGlasses
1 2 MakePathConnectingGlasses
6 setlinewidth 0.5 setgray 0 setlinecap stroke
} if
}
{
MayMarkPage ThisPageDecanterLabels not and
{
1 0 MakePathConnectingGlasses
2 0 MakePathConnectingGlasses
2 1 MakePathConnectingGlasses
6 setlinewidth 0.5 setgray 0 setlinecap stroke
} if
}
] def
When rendering tasting-note pages the following variables will be available to CodeForTastingNoteSheets:
RenderingTastingNotesPages, having value true;
TastingSheetNum, an integer being the item of GlassesOnTastingNotePages currently being rendered;
Functions BaseHeight, StringWidthRecursive, StringHeight, ShowRecursive and CharPathRecursive, as before;
Any of the parameters to the code, as described on this page or on the main guide, and perhaps as already altered by CodeForTastingNoteSheets.
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 (not that within CodeForGlassSheets and CodeForTastingNoteSheets) 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. Code should not alter the current path by anything other than rmoveto; nor leave altered 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 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) {CurrentFontSize -0.09 mul 0 rmoveto} (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) {CurrentFontSize -0.08 mul 0 rmoveto} (W)]
] def(The amount of the kerning is controlled by the “-0.08”: the optimal amout 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) {CurrentFontSize -0.08 mul 0 rmoveto} (W) ] def
/Grahams [(Graham) {CurrentFontSize -0.04 mul 0 rmoveto} /quoteright {CurrentFontSize -0.04 mul 0 rmoveto} (s)] def
/OffleyBoaVista [(Of) /fl (ey Boa Vista)] def
/RebelloValente [(Robertson) {CurrentFontSize -0.04 mul 0 rmoveto} /quoteright {CurrentFontSize -0.09 mul 0 rmoveto} (s Rebello V) {CurrentFontSize -0.09 mul 0 rmoveto} (alente)] def
/SmithWoodhouse [(Smith W) {CurrentFontSize -0.09 mul 0 rmoveto} (oodhouse) ] def
/Taylor [(T) {CurrentFontSize -0.09 mul 0 rmoveto} (aylor)] def
/Vargellas [(V) {CurrentFontSize -0.09 mul 0 rmoveto} (argellas) ] def
/Vesuvio [(V) {CurrentFontSize -0.09 mul 0 rmoveto} (esuvio) ] def
/Warre [(W) {CurrentFontSize -0.09 mul 0 rmoveto} (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&rdquo, in the new size, sum to the height of the “o&rdquo 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&rdquo). 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:
[
(St)
{
/Left /Bottom BaseHeight /Height 90 7 3 false Star eofill
pop pop pop moveto pop pop
/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 can be code that refers to NameNum. Particularly if FillTexts are used, this can substantially increase distil 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 distil time. If parameters do vary with NameNum, it is important that RenderPagesSeparately be deffed to true.
It might be that one wishes to have slightly different content on the tasting-note pages than on the glasses pages. The variable RenderingTastingNotesPages is always set to true or false, according to which type of page is being rendered. So
{RenderingTastingNotesPages {[/quoteright (63)]} {(1963)} ifelse}
causes to be used ’63 on the tasting-note page, but 1963 on the glasses page.
Julian D. A. Wiseman
| Main index | Top | About author |