Main index List of placemats About author

Valid HTML 4.01 Transitional

Glasses placemat: how to avoid confusion at a port tasting

Julian D. A. Wiseman

Contents: Publication history; example output (and in PDF); How to control the PostScript program; Other easy parameters; Non-easy parameters; Code within parameters; Errors: causation and avoidance; How to comment; Who to invite?; Recent changes.

Publication history: only at Usual disclaimer and copyright terms apply. Also see the main feature, the PostScript program itself.

This is the ‘documentation’, such as it is, for the latest version, dated May 2016, which prior precedent suggests will not be the last. The code is mostly debugged, but errors surely remain. It is provided as-is, without liability—it is free.

Also see the complete list of previous placemats, some with comments; the advice about choosing of the page size; the obscure parameters; how to use code in parameters; /TimesNewRomanPS-BoldMT’s accented characters and their PostScript glyphs, which includes diacritics as well as accents. Further, PostScript programmers are invited to peruse the infrequently updated list of re-usable routines.


So you and your lucky guests are to enjoy a port tasting. Of course, there will be many glasses, each person having one for each port. But it is important to avoid the what-is-in-this-glass confusion: each person will need a wine tasting placemat, clearly labelling each glass’s spot.

The author has devised a PostScript program to create such pages, and all the other paperwork required to manage such tastings, large or small. Glasses pages are set as in the PDF example, the first two pages of which (to hold the glasses, and on which to write tasting notes) are also rendered immediately below.

Example of an 5-glass port tasting placemat Example of tasting-note sheet

 Making Your First Placemat: Advice for Beginners

Don’t try to make your first placemats with little time to spare. Start a week in advance, to allow time for learning and improvements, and to avoid the other things that need doing just before a tasting. Yes, it can be done in a few minutes—but not your first placemats.

A code editor, a good text editor, is a boon, and it should be one that understands PostScript. The basic text editor that came pre-installed on your computer will make life harder than it needs to be. A code editor that understands PostScript will prevent many errors and save much labour. On a PC NotePad++ is good. On a Mac: Alpha X is excellent and free, though not perfectly coping with OS versions ≥10.9; Aquamacs Emacs is suitable for Emacs experts; Sublime Text 3 seems good, but making it understand PostScript requires a technical step.

Bookmark this manual, and download to your machine a copy of the PostScript code (update this copy every time: new versions fix bugs; add features; improve default settings). Store it in a directory named something like “placemats”. Make a copy of the original PostScript. Give it a name which will make sense in aeons to come: perhaps, or Perhaps see the files names in the list of old placemats.

You have saved a .ps file. This file is to be opened in two different ways: with the code editor; and with a program to convert it from .ps to .pdf. Let’s master the latter. On a Mac right-click the file, and ‘Open With’ Preview. It should convert to a PDF. On a PC, in your preferred web browser, open (or, with fewer fonts available,; ‘Select files…’; and ‘Convert’ it. You can now convert the PostScript to PDF.

Open the file with the code editor, perhaps by right-clicking and choosing from the ‘Open With’ applications (Alpha, Notepad++, whatever). For a sense of which of the many features might be wanted look at the example placemats, or the complete list of old placemats, some of which have helpful comments and advice. Then use the text editor to edit the copy of the PostScript, changing to values appropriate for your tasting the:

Convert to PDF, to test that all is well.

Alter as necessary DecanterLabelsNumCopies, PlaceNames, PrePourNumCopies, DecantingNotesNumCopies, CorkDisplayNumCopies, VoteRecorders, StickyLabelsNumCopies and StickyLabelsTypes.

Perhaps (and perhaps not) change the glass-decoration settings: set InlineTitles to false; activate others. Play and experiment. Convert to PDF after each significant change, and admire the results. Choose a small subset of the decorative features to go in the final placemats. Too many flamboyant features looks cluttered and horrible, so be parsimonious. Perhaps change the the fonts—a list of valid PostScript font names being most easily made by distilling Convert to PDF.

If the tasting is being arranged via ThePortForum or another wine bulletin board, upload the PDF to the web, and post in an appropriate thread a link labelled something like “Current draft of the placemats”. Allow some time for comments and corrections and changes. Update if appropriate.

Perhaps send to the author links to the .ps and .pdf. Time and convenience permitting, help might be forthcoming. Please send the URLs of the uploaded .ps and .pdf files, rather than filling email with large enclosures. Allow some days for a response.

The day before the tasting print the mats using a toner printer.

Neatly fold the place-names pages, and construct the decanter labels. Cut a neat rectangle around each; paste to the back of a business card (typically one from a former employer—keep them when changing jobs); allow the glue to dry; punch holes top-left and top-right; hang on the decanter, perhaps using a 30″ metal bead chain (filling an unlabelled decanter risks needless confusion, so hang before filling).

A beginner reads these instructions as a beginner. I wrote and use the code, so can’t read these instructions as a beginner. So I might have misjudged what needs explaining. If you get stuck, even if only briefly, please send me details, so that instructions can be made clearer. Thank you.

Enjoy the tasting.

 As examples, the following link to placemats from actual tastings, or draft placemats for future tastings. Most of these were made with earlier versions of the software; some required expert use of the program’s features.

9 September 2015, ‘E2>V’ Elegant unfussy placemats.
9 May 2015, 1945s BackgroundTextsGlassesTexts.
24 Dec 2014, Three tawnies SideBySideGlassesTastingNotes.
29 Oct 2014, Croft Rotate180AlternateNames.
21 Oct 2014, Ramos Pinto BackgroundTextsGlassesTexts.
22 Apr 2014, A Flight of 1966 Typical for a horizontal.
19 Dec 2013, sticky labels at the Bell Stickies replace glass sheets.
11 & 12 June 2013, AHB’s 1963 Quinquagenary Includes most of the different page types.
22 Mar 2013, Taylor vertical Formatting varying by ‘type’ of port.
25 July 2012, Olympic years at the Portuguese Embassy FlightSeparations.
11 Oct 2011, semi-blind 4×4 Complicated management of the blind and a non-blind names, which were used on different pages.
Recent, from the code’s default parameters

Also see the complete list of placemats.

For some of the ≥2012 placemats, the parameters used in the construction are quoted in a thread on ThePortForum, Parameters for the placemat software. Users wishing to replicate a feature might be helped by some of those examples.

 How to control the PostScript program

PostScript programs are in the form of a text file, which can be interpreted by Adobe Distiller, by Preview on the Mac OS X, by GNU Ghostscript, or by any PostScript printer (though sending raw PostScript to a printer might require some technical knowledge). Several websites offer free PS→PDF conversion, including and (with fewer fonts) (fonts available on ps2pdf and online2pdf). The text file can be edited in any text editor, though it is easier to use a text editor that has been configured to edit PostScript files—see comment in box.

After some opening comments (after a “%” character the remainder of a line is a comment), the program defines some arrays, starting with Titles, Belowtitles, and Circlearrays. Each of these arrays has the same length, typically equal to the number of glass-settings on the page (five in the example above), and the program copes with any sensible number of glasses.

In the example provided, Titles is defined thus:

/Titles [
] def

Observe that the array is delimited with square parentheses [], and that each of the strings is delimited with round parentheses (). White space outside strings is ignored. The page looks best if each of the Titles is short, ideally ≤4 characters.

(Throughout this manual PostScript is shown in monospace with a pale grey background. Further, the preferred code editor of the author, Alpha X, helpfully colours the different types of element. That colouring is echoed here: /names; operators; array and code parentheses [ ] { }; and % comments.)

Belowtitles is an array of the same length, though, in the example, most of the strings are empty:

/Belowtitles [
] def

If an auction house is hosting the tasting, Belowtitles might contain lot numbers: (123), or even [(123) /arrowright (129)] = “123→129” (see below).

And Circlearrays, which is an array of arrays, thus:

/Circlearrays [
	[ (1963) (Fonseca) ]
	[ (1970) (Fonseca) ]
	[ (1977) (Fonseca) ]
	[ (1985) (Fonseca) (Single) ]
	[ (1985) (Fonseca) (Magnum) ]
] def

Each of the sub-arrays of Circlearrays contains strings. The program calculates how many copies of these sub-arrays can be spaced around the circle, and then does so with equal spacing between the elements of the sub-arrays.

Each place settings is named (e.g., the author’s “JDAW” initials are on the page shown above), and these names are listed in yet another array, not necessarily of the same length as the previous three:

/Names [
	[(G) /uacute /edieresis (st ) /Ocircumflex /ntilde /egrave]
	(Guest Two)
] def

Including a blank name, (), is very strongly recommended: last-minute changes to the guest list do happen.

Accents and modifiers: Á /Aacute á /aacute  /Acircumflex â /acircumflex À /Agrave à /agrave Å /Aring å /aring à/Atilde ã /atilde Ä /Adieresis ä /adieresis Ç /Ccedilla ç /ccedilla É /Eacute é /eacute Ê /Ecircumflex ê /ecircumflex ø /oslash (See /TimesNewRomanPS-BoldMT: accented characters and their PostScript glyphs.)

Punctuation and ligatures: ‘ /quoteleft ’ /quoteright “ /quotedblleft ” /quotedblright … /ellipsis – /endash — /emdash ‹ /guilsinglleft › /guilsinglright ‚ /quotesinglbase „ /quotedblbase fi /fi fl /fl æ /ae Æ /AE œ /oe Œ /OE ß /germandbls

Currencies, fractions, arithmetic and containment: £ /sterling € /Euro ¥ /yen (also used for CNY) ₩ /won ₪ /sheqel ¢ /cent ½ /onehalf ¼ /onequarter ¾ /threequarters ⅛ /oneeighth ⅜ /threeeighths ⅝ /fiveeighths ⅞ /seveneighths ⅓ /onethird ⅔ /twothirds ≈ /approxequal ≥ /greaterequal ≤ /lessequal ≠ /notequal × /multiply ÷ /divide ∈ /element ∉ /notelement ∋ /suchthat ∌ /notcontains

Greeks: α /alpha β /beta γ /gamma δ /delta ε /epsilon ζ /zeta η /eta θ /theta ι /iota κ /kappa λ /lambda μ /mu ν /nu ξ /xi ο /omicron π /pi ρ /rho σ /sigma τ /tau υ /upsilon φ /phi χ /chi ψ /psi ω /omega

Other: • /bullet · /periodcentered ◊ /lozenge † /dagger ‡ /daggerdbl § /section ® /registered ™ /trademark ♠ /spade ♥ /heart ♦ /diamond ♣ /club → /arrowright ← /arrowleft ↑ /arrowup ↓ /arrowdown ↔ /arrowboth ⇒ /arrowdblright ⇐ /arrowdblleft ⇑ /arrowdblup ⇓ /arrowdbldown ⇔ /arrowdblboth

Any string parameter may be replaced by an array (to make a ‘compound string’) of strings and glyphs, each glyph beginning with a forward slash (“/”). As an example, the second item of the default value of Names makes the silly but instructive “Gúëst Ôñè”. Examples of glyphs include accented characters such as /atilde, and the likes of /daggerdbl, as well as ligatures such as that in [(Quinta do Bom) /fi (m)]. Most of the characters one might need are in the box on the right, or in /TimesNewRomanPS-BoldMT: accented characters and their PostScript glyphs. Others are listed in appendix E.5 of the PostScript Language Reference Manual, third edition; a still fuller list is at​papers/​trivia/​character-entities.html. (But most fonts do not contain all the glyphs listed, so check the output carefully.) Any string parameter may be replaced with an array of any of: strings; glyphs; other like arrays; and indeed pieces of PostScript code such as “{-0.06 Kern}”.

Non-compounded string
Non-compounded string

If using a compound string, do ensure that it has been compounded. In the code below and the pictures on the right the ‘wrong’ element of Circlearrays has four pieces (1963, Graham, and s); whereas the correct version has two pieces (1963 and Graham’s) the second of which is a compound string. This type of error has been seen more than once!

/Circlearrays [  % First item erroneous.
	[ (1963)  (Graham) /quoteright (s)  ]  % Wrong! Missing [...] compounding.
	[ (1963) [(Graham) /quoteright (s)] ]  % Correct.
] def
Titles, Abovetitles, Belowtitles, Overtitles, Circlearrays

 Abovetitles and Overtitles

As well as Belowtitles, there are similar arrays Abovetitles and Overtitles. The diagram on the right shows the usage of these arrays. It is best if items of Titles are short, ideally ≤ 4 characters, so that they can be shown very large. Separately, observe that the Titles and Overtitles have been ‘filled’ with text: see FillTexts below.

This profusion of places to put information should be used sparsely, and consistently. For example, in a vertical, the Titles might contain two-digit years and the Overtitles the name of the shipper/quinta/château. Most Abovetitles would be blank, only a few specifying a non-standard bottle size (“Double Magnum”). Most Belowtitles would be blank, only a few specifying the likes of “Cask Sample”.


In PostScript sizes, distances, lengths, widths (and, approximately, font sizes) are denominated in points, where 72 pt = 1″ = 1 inch = 25.4 mm, and hence 1 pt = 172″ = 0.01388″ = 127360 mm = 0.35277 mm, and 1 mm = 360127 pt ≈ 2.83 pt.

If using lots of measurements in millimetres, near the start of the file insert the line “/mm {360 mul 127 div} def”, which automatically converts the likes of “6 mm” (with a space) into Adobe points. Or “/inch {72 mul} def” if preferring inches, or “/apc {87.468025932 mul} def” for atto-parsecs.

Angles are always in degrees = ° and never in radians, so a quarter turn is 90°. PostScript’s arithmetic is only single precision, so angles should not have magnitude larger than a few thousand degrees. I.e., prefer -40° to 5000°.

 Other parameters


◊ The fonts in which the various text pieces are rendered are contained in the parameters NamesFont, CircletextFont, TitlesFont, AbovetitlesFont, BelowtitlesFont, OvertitlesFont, FillTextFont, SubtitlesFont (on non-glasses sheets), HeaderFont, FooterFont, and even the rarely-used BackgroundTextsFont.

But PostScript uses font names that are not always obvious. Help is at hand: distilling produces a PDF with a list of the available fonts, their PostScript names, and formatted example text. If distilling on a different machine or with a different program, a different set of fonts might be available: re-test by re-distilling

The program was designed for left-to-right script, and the alphabets successfully used have been Roman (mostly), Greek (occasionally), and Cyrillic (rarely). Users with knowledge of PostScript who are able to assist with testing in other scripts—including right-to-left—are invited to contact the the author, perhaps after reading this request for assistance rendering “泉十段”.

 PaperType and Orientation

◊ The page size is controlled by the parameter PaperType:

/PaperType /A4 def  % /A4 /A3 /USL /USLegal /USL2 [SmallerPts LargerPts]
NameMillimetresInchesAdobe Points
/A4210 × 297≈ 8.27 × 11.69≈ 595.3 × 841.9
/A3297 × 420≈ 11.69 × 16.54≈ 841.9 × 1190.6
/USL215.9 × 279.48½ × 11612 × 792
/USLegal215.9 × 355.68½ × 14612 × 1008
/USL2279.4 × 431.811 × 17792 × 1224

The parameter PaperType can be one of the values in the table on the right, or a custom page size expressed in points (see box above right) as an array of length two: [d1 d2] with 0 < d1 ≤ d2. For convenience of copy-pasting, some of the allowed values appear in the comment at the end of the line—after the “%”. Also allowed are sizes from /A10 (don’t ask why but do shrink the margins) to /A0, and from /B10 to /B0. For help on page sizes, and how many glasses can fit on which, see the separate manual page Glasses placemat: choosing a paper size.

The parameter Orientation can take the values /Portrait or /Landscape. Its complicated default chooses the former if the PaperType is either /A4 or /USL, otherwise the latter.

Around the edge of the page is an invisible margin, the circles lying inside the area at least the margin away from the edge of the page. Usually, the margin equals the parameters MarginL (left), MarginR (right), MarginT (top), and MarginB (bottom), each defaulting to 24 points = ⅓″ ≈ 8.5 mm (or, top|bottom, if there are headers|footers, 30 points = 512″ ≈ 10.6 mm). Outside this there can be an extra margin, on the glasses pages OuterGlassesMarginL, …R, …B, and …T. This adds to the ordinary margin, except that, if OuterGlassesCropMarks, then on any corner in which both the OuterGlassesMargin…s are positive, crop marks are painted. This can be useful for large numbers of glasses on cropped /A2.

 Decorating the Titles

 Colour Schemes

Items of Titles, Abovetitles, Belowtitles, and Overtitles can be black or two-tone grey. These colour schemes are controlled by the parameters ColourSchemeTitles, ColourSchemeAbovetitles, ColourSchemeBelowtitles, and ColourSchemeOvertitles, each of which can be set to (code evaluating to) /Black or /MidGrey. There is also the analogous parameter ColourSchemePlaceNames, used to control the colour of Place Names. By default all are /Black.

Example of CrossHatchingTitles


◊ The example on the right is filled with a check pattern. This check is activated by the boolean CrossHatchingTitles. The polar gridlines originate from a point with x coordinate CrossHatchingCentreX which can take values of /Name, /Left, /Center, /Right, or a number being points from the left edge of the page; and y coordinate CrossHatchingCentreY with possible values /Name, /Bottom, /Middle, /Top, or points from the bottom of the page. Lines are formatted and stroked by the code in CrossHatchingTitlesStrokeCode (the code must include the stroke, this requirement permitting the likes of “gsavestroke grestorestroke”). The number of straight lines is CrossHatchingNumRadialLines, defaulting to 180 so 2° apart. The radial gaps start at CrossHatchingRadialGapStart (this is the diameter of the inner-most circle), the gaps between the radii tending to CrossHatchingRadialGapAtInfinity with decay CrossHatchingRadialGapDecay.

There are analogous booleans CrossHatchingAbovetitles, CrossHatchingBelowtitles, CrossHatchingOvertitles, and CrossHatchingPlaceNames; and strokeing code CrossHatchingAbovetitlesStrokeCode, CrossHatchingBelowtitlesStrokeCode, and CrossHatchingOvertitlesStrokeCode (CrossHatchingPlaceNames uses the sub-parameters of CrossHatchingTitles).

CrossHatchingInside (with CrossHatchingInsideStrokeCode) controls whether (and how) the cross hatching is in the background of the inside of the circle, behind the various titles. DecanterLabelsShowCrossHatchingInside controls whether this is shown on decanter labels. Similarly, the boolean CrossHatchingOutside (with CrossHatchingOutsideStrokeCode and CrossHatchingOutsideToPaperEdge) controls whether and how it appears outside and so between the circles.

 Sometimes used with CrossHatchingInside and CrossHatchingOutside is CirclearraysFillBehind, which fills behind the annulus containing the Circlearrays by executing CirclearraysFillBehindCode.

Example of outlined titles

 Stars and flowers

◊ In the very cluttered diagram on the left, showing QvA08, the Titles and Belowtitles contain small random stars and flowers. This feature is activated by setting to true the boolean parameter ShapesInTitles, which has obvious variations ShapesInAbovetitles, ShapesInBelowtitles, ShapesInOvertitles, and ShapesInPlaceNames. The array ShapesToUse, defaulting to [/Flower /Star], controls which shapes are used.

The shape is filled with the code ShapesTitlesFill (or ShapesAbovetitlesFill, +ShapesBelowtitlesFill, ShapesOvertitlesFill), which, if the shapes are filled, should end with a fill. The shape is then stroked with ShapesTitlesStroke (or ShapesAbovetitlesStroke, ShapesBelowtitlesStroke, ShapesOvertitlesStroke) which should end with a stroke.

For the stars, the numbers of points and the step between points are chosen randomly from within ShapesStarsPointsAndStepsArray, the default [[5 2] [6 2] [7 2] [7 3] [8 3]] being small 5/2 star small 6/2 star small 7/2 star small 7/3 star small 8/3 star. A possible alternative value for ShapesStarsPointsAndStepsArray is [[3 1.318] [4 1.792] [5 2.278] [6 2.77]], being pointier and less polygonal: small sharp 3-pointed star small sharp 4-pointed star small sharp 5-pointed star small sharp 6-pointed star. (Mathematicians might be interested in an open question about such stars.) For the flowers the number of petals is chosen randomly from ShapesFlowersNumPetalsMin to ShapesFlowersNumPetalsMax, and, as a proportion of 360° divided by the number of petals, the angular width of each is chosen randomly from the range ShapesFlowersAngularWidthMin to ShapesFlowersAngularWidthMax.

The radius of each shapes’s enclosing circle is random in the range ShapesEnclosingCircleRadiusMin to ShapesEnclosingCircleRadiusMax. An approximation to the typical separation between the shapes is ShapesAverageSeparation; a parameter controlling how far stars are moved from a regular grid is ShapesAverageMaxTweakPlusMinus. The pattern generates some shapes that are not shown, that are clipped away. If ShapesPrintQuickerDistillSlower is true these are removed—which can reduce file size at the price of slowing distillation.


◊ The Titles can be outlined by setting /OutlineTitles true def, as in the (still very cluttered) example with flowers and stars. The innermost white and black outlines have width OutlineTitlesInnerWidthWhite and OutlineTitlesInnerWidthBlack, and these widths grow in successive outlines by factors of OutlineTitlesMultiplierWhite (defaulting to ½(1+√5) = the golden ratio ≈ 1.618) and OutlineTitlesMultiplierBlack (defaulting to unity). The number of black ripples is capped at OutlineTitlesMaxNum.

If OutlineTitlesAlsoAbovetitles is true then Abovetitles are also outlined; if false then Abovetitles sit plainly atop the lines (and likewise OutlineTitlesAlsoBelowtitles and OutlineTitlesAlsoOvertitles). Generally it helps if the decanter labels fit onto a rectangular business card, so the default value of DecanterLabelsShowOutlineTitles does not show the outlines on the decanter labels. OutlineTitles does not impose as high a burden on a printer as does FillTexts, but can still be difficult for some. Again, test your printer.

Example of inlined titles


◊ A more architectural form of decoration is activated by the booleans InlineTitles, InlineAbovetitles, InlineBelowtitles, InlineOvertitles and InlinePlaceNames. These are in some sense a reversal of OutlineTitles—the lines being on the inside. In the Titles the lines have widths of InlineTitlesBlackWidth and InlineTitlesWhiteWidth; with obvious variations InlineAbovetitlesBlackWidth, InlineTitlesBlackWidth, InlineAbovetitlesWhiteWidth, InlineBelowtitlesBlackWidth, InlineBelowtitlesWhiteWidth, InlineOvertitlesBlackWidth and InlineOvertitlesWhiteWidth. All default to 1. If the colour scheme is /MidGrey then “black” means the darker of the two shades, and “white” the lighter.

The code can make an estimate of how many contours are needed, and does so if InlineTitlesAttemptMinimiseNumContours (or InlineAboveBelowOverAttemptMinimiseNumContours or InlinePlaceNamesAttemptMinimiseNumContours) is true (discussion). This estimate is then capped at InlineTitlesMaxNumberContours or InlineAbovetitlesMaxNumberContours or InlineBelowtitlesMaxNumberContours or InlineOvertitlesMaxNumberContours. (InlinePlaceNames uses the sub-parameters of InlineTitles.) If the code’s estimate is too low, as might happen if the lines are very thin, then the booleans should be false and the upper bounds set carefully. The attempted estimate is computed using a horrible algorithm, ‘discussed’ at comp.lang.postscript. By default the upper bounds are 1, which is elegantly sparse.

If InlinePrefillWhite, all of the Titles/Abovetitles/Belowtitles/Overtitles are filled white before painting the ‘Inlines’, so are opaque. This is good if there is CrossHatchingInside, but bad with BackgroundTextsGlasses.


The example on the right is also at a jaunty angle (well, an angle that the author imagines might be jaunty). This is controlled by RotationTitlesAboveBelowOverCirclearray, which rotates the whole glass circle by that number of °, and is here set to 15.

Example of titles filled with text


◊ The booleans FillTitles, FillAbovetitles, FillBelowtitles, FillOvertitles, and FillPlaceNames control whether their respective elements are filled with multiple copies of some text, outlined. The filling texts are in the array FillTexts, which is of the same length as Titles, and each is outlined FillTextNumOutlines times. Depending on the values of ColourSchemeTitles etc, the colours are either black and white, or 40% grey and white. Each rendering of an item of FillTexts is suffixed with FillTextNumSpaces spaces.

The additional parameter FillTextAngle may be deffed to a number (30 being a good choice), this being the ° from horizontal at which the FillTexts are shown. Alternatively, FillTextAngle may be one of /LowerLeft, /LowerCenter, /LowerRight, /MiddleLeft, /MiddleCenter, /MiddleRight, /UpperLeft, /UpperCenter or /UpperRight such that the text will be 90° to a line drawn from the centre of the circle to that corner or edge or centre of the page. Or FillTextAngle may be /Name, making the angle perpendicular to a line drawn to the positioning of the Names.

The FillTexts are set in the font FillTextFont, at a size of at least FillTextMinFontSizeAbsolute, and of a size of at least FillTextMinFontSizeProportionLargestTitleAboveBelowOver × the largest their font sizes.

In the example on the right the relevant item of Titles is (S); of Overtitles is (Sandeman); of Circlearrays is [(Sandeman) (1985)]; of FillTexts is (1985), with FillTextNumSpaces being 2 and FillTextAngle being 30, and all the typefaces being /HelveticaNeue-CondensedBold. The default value of FillTextAngle is complicated, but natural. If GlassesOnSheets specifes a two-page layout, then to /LowerRight on the first sheet, and /LowerLeft on the second. If not a two-page layout, then if NamesAtTop is true to /LowerCenter, otherwise to /Name.

The formatting of the PlaceNames… generally matches that of the Titles, except that the filling text is FillTextPlaceNames, and the angle is FillTextAnglePlaceNames.

Use of FillTexts makes distillation slow, and produces files that can be too complicated for some printers. The balance between these two can be influenced by the boolean FillTextPrintQuickerDistillSlower. For example, in a draft placemat with FillTitles and PlaceNames, changing FillTextPrintQuickerDistillSlower from false to true decreased the file size from 1013k to 871k, but increased the distill time from 64 seconds to 40 minutes. If distilling the PostScript via the web the latter would definitely have timed out the browser. And it might be that, on some printers, the print-slowly option (false) won’t print at all, whereas the print-quickly option (true) will. But some printers can’t cope with either: the author’s Canon Pixma MP240 fails to print the FillTexts at all, the interior of the Titles rendering as plain white. (Printing this FillText test page to an MP240 results in output of which this scan is an extract.) But the author’s next printer, a Samsung ML-2955DW, did print successfully, but with insufficient resolution for the small grey text to look entirely satisfactory. So test your printer; do not assume that anything will work first time; and do not assume that all larger more expensive printers cope better.

It also appears that Adobe does not always distill or render perfectly these FillTexts. This problem can be fixed by opening the PostScript file with Preview on the Mac OS X, and then printing from Preview or saving the PDF file Preview makes and subsequently printing from Acrobat (technical demonstration of error).

But there is also a problem with Preview on the Mac OS X, which crashes if the paths are too complicated. The relevant constraint is on the complexity of the path of the item of FillTexts, plus the complexity of the path of the text that is filled (the item of Titles or of Names, as appropriate). As Names are typically longer than Titles, the problem can typically be remedied by setting PlaceNames to false.

 Page-level settings

The Names of people can appear at the top or bottom of each glasses sheet, or both or neither. Presence or absence is determined by two arrays of booleans, NamesShowTop and NamesShowBottom, each of the same length as GlassesOnSheets. The names are rendered in the font NamesFont, at a size equal to the size of the largest of the Circlearrays, subject to a minimum of NamesFontSizeMin and a maximum of NamesFontSizeMax.

If Rotate180AlternateNames is true then, for every other person, the glass and tasting-note pages are rotated 180°. At set-up time this slightly simplifies separation of pages by owner.

Example of use of Rays


To connect circles with pseudo-electrical field lines, as in the example on the left, set Rays to true. Then from each glass’s circle there emanates RaysLinesPerGlass lines, the first being offset from vertical by RaysAngleOffset°. It is recommended that RaysLinesPerGlass be an integer multiple of 4. The rays can be truncated at the margin, or at the edge of the paper, controlled by the boolean RaysToPaperEdge. If this is true, it is even more important to ensure that printing is done at 100% with no scaling. Curves shorter than RaysMinLengthCurves are suppressed, though the ‘length’ is approximate, and includes parts that are outside the usable area and hence clipped. The formatting of the stroke can be controlled within RaysStrokeCode.

The electrical-ness is very pseudo. With real electro-magnetic lines, each circle would have to be of a positive or negative charge. So if A connected to B, and B to C, that would constrain the extent to which A could connect to C. But these lines can, layout dependent, connect any combination of pairs of circles. If a physical interpretation is wanted—this being strongly dis-recommended—it would have to be chromatic. It is best to deem the whole thing ‘decorative’. (Comment from a real physicist would be welcome.)

Examples Rays, a few tangled

Imperfections in the algorithm (discussion thereof) can cause aesthetically detrimental crossovers, as in three of the frames in the example on the right (24, 44, 64), in which the grey number is the value of RaysLinesPerGlass. Typically the problem can be fixed by tweaking RaysLinesPerGlass or RaysAngleOffset.

Example of waterboxes


◊ Particularly at a tasting of a sweet fortified wine, drinking water is important. It is possible to add a set of little glass icons (see lower-right of figure on left) with which to record water consumption: check one for each glass of water downed. Even better, use a diagonal slash (I drank half a glass of water) for a half glass of water, and a cross (I drank a whole glass of water) for a whole.

This is activated by the parameter WaterBoxes, which takes one of the values: /Glasses (so the water boxes appear on the glasses pages); /TastingNotes (on the tasting-note pages, see discussion); /Both (on both); /None (none). The number of boxes is WaterBoxesNum, each of which is no bigger than WaterBoxesSizeMax on each side, the gap÷box ratio being WaterBoxesGapProportionSize.

On glasses pages boxes should appear on the lower-right of the page for right-handed drinkers, and lower-left for left-handers. Tasters’ handedness is set by LeftHanders, an array of the text of the name of known left-handers, whether or not at this tasting. If a name within Names is duplicated, and both are to be left-handed, that name must appear twice within LeftHanders.

If there are multiple pages of glasses (see GlassesOnSheets below), then on which of these pages should they appear? For simplicity, WaterBoxesOverrideShowEverySheet overrides the complexity in the next few sentences, putting water boxes on every glasses sheet. But if WaterBoxesOverrideShowEverySheet is false, the presence or absence is controlled, for the right handers, by the array of booleans WaterBoxesShowRight; and for the lefties by WaterBoxesShowLeft. Nota Bene: WaterBoxesShowRight and WaterBoxesShowLeft must be the same length as GlassesOnSheets. The default values of WaterBoxesShowRight and WaterBoxesShowLeft refer to PageOrderingGlasses, which is implicitly assumed to specify sessions. Naturally enough, WaterBoxesShowRight defaults to true for the last page in each ‘session’, and WaterBoxesShowLeft for the first. There is an analgous parameter WaterBoxesShowTN, used for water boxes on the tasting-note sheets. logo on a cufflink

The majority of the placemats created by the author are for tastings arranged via To control the placement of a ThePortForum icon (examples on page at top, cufflink bearing logo on right), the parameter ThePortForumIconPlacement is set to one of /None, /LowerLeft, /LowerRight, /UpperLeft, /UpperRight, /LowerNonWaterBox, /UpperNonWaterBox or /UpperNonWaterBox, the last three evaluating to left or right according to the side on which the WaterBoxes are placed. Alternatively, for multiple icons on the page, ThePortForumIconPlacement can be an array of any of the non-/None of these possibilities. Icons on the tasting-note pages are controlled by ThePortForumIconTastingNotePlacement, with permitted values /None, /LowerLeft, /LowerRight, /UpperLeft, /UpperRight, /LowerNonName, /UpperNonName and /UpperName. Whether or not the icons are shown in colour, small colour ThePortForum logo or small black-and-white ThePortForum logo, is set by the boolean ThePortForumIconColour.

Parent and child links

ParametersVersionDateTimeAdobeFormat holds the date and time on which the parameters were last updated, as a string in the Adobe date format (D:YYYYMMDDhhmm). This isn’t necessarily important, but appears in the log file and embedded in multiple places in the PDF. Users mindful of versioning might wish to update it. (Programmers creating a wizard to change the parameters, who want the wizard name and version date to be output to the log file, should define strings WizardLongName and WizardVersionDateTimeAdobeFormat.)

Technical note: for most packing styles, the radius is a root of a polynomial. The polynomials were devised over some years, mostly by hand. For mathematical readers they have been regenerated in Mathematica, and published as a Mathematica notebook and in PDF. Please report errors and improvements to the author.

For most packing styles the radius and positions are computed analytically, to within PostScript’s single precision. There are three exceptions. For /DiamondsPlus, two quartic equations are solved numerically. For /Temple, two octic equations are solved numerically. For /Arch, the computation is an interval-bisection loop containing an intricate glass-positioning iteration, believed to be correct to within 0.1 pt ≈ 0.035 mm.


The layout of the glasses is very flexible. The user specifies an array, PermittedPackingStyles, which is a list of packing styles, each style optionally having constraints (e.g., only if Orientation is /Landscape) and flags (e.g., /Mirror). One at a time each style from the list is taken, and each allowed variation is tested. Broadly, the one with the biggest radius is selected. Before describing details, let’s show some examples.

/Diamonds: circles in each row and column lie midway between those in neighbouring rows and columns.

/A4 (210mm×297mm)
4 glasses, /Diamonds on /A4 /Portrait
5 glasses, /Diamonds on /A4 /Portrait
/A3 (420mm×297mm)
13 glasses, /Diamonds on /A3 /Landscape
/USLegal (14″×8½″)
9 glasses, /Diamonds on /USLegal /Landscape
/USLegal (14″×8½″)9 glasses, /Diamonds /Mirror on /USLegal /Landscape
[ /Diamonds /Mirror ]

The first four of these specify the pattern with a simple a name, /Diamonds. Alternatively, one can specify a variant of the base style with an array, containing flag and constraints, such as [ /Diamonds /Mirror ]. In the array version, the first item of the array must be the base style.

/RectangularDislocation: The next four examples are rectangular, optionally with a horizontal ‘dislocation’ in the middle, that being, /Diamonds-style, circles lying between the circles in the adjacent row.

5 glasses, /RectangularDislocation on /A4 /Portrait
6 glasses, /RectangularDislocation on /A4 /Portrait
10 glasses, /RectangularDislocation on /A3 /Landscape
10 glasses, /RectangularDislocation /Mirror on /A3 /Landscape
[ /RectangularDislocation /Mirror ]

Again observe the /Mirror variant.

12 glasses, /SquareGrid on /A3 /Landscape

/SquareGrid: /SquareGrid can be similar to /RectangularDislocation. In the latter the glasses fill the page: observe the six-glass example above, in which the circles touch their vertical neighbours, but have a slight gap from the horizontal neighbour. In /SquareGrid there is no dislocation, and by default the vertical and horizontal distances are the same and equal to the diameter (example on right). /SquareGrid has two optional one-parameter flags: /HorizontalAlignment (which must have a value of one of /Left, /Right, /Centre, or /Justify); and /VerticalAlignment (one of /Top, /Bottom, /Middle, or /Justify). Obviously either /Justify can allow the horizontal and vertical distances to differ.

/DiamondsAndRectangular: For some numbers of glasses and aspect ratios, the best fit can come from a pattern that is a combination of /Diamonds and a rectangular pattern. The diamonds can be offset from the diamonds either vertically or horizontally.

Sometimes there are multiple ways to arrange the columns or rows: see the three examples of 14 glasses on A3. A sub-parameter can be set, /RectColsToLeftOrRowsBelow, which specifies the number of rectangular columns to the left of the diamonds, or the number of rectangular rows below the diamonds. The number is automatically pruned, so far-right can be specified with the likes of [ /DiamondsAndRectangular /RectColsToLeftOrRowsBelow 999 ]. By default the diamonds are put in the middle, if that is possible, otherwise at 0 ⇒ far left or bottom.

14 glasses, /DiamondsAndRectangular on /A3 /Landscape
14 glasses, /DiamondsAndRectangular on /A3 /Landscape
[ /DiamondsAndRectangular /RectColsToLeftOrRowsBelow 0 ]
14 glasses, /DiamondsAndRectangular on /A3 /Landscape
[ /DiamondsAndRectangular /RectColsToLeftOrRowsBelow 2 ]
/USL2 (17″×11″)
14 glasses, /DiamondsAndRectangular on /USL2 /Landscape
/USLegal (14″×8½″)
9 glasses, /DiamondsAndRectangular on /USLegal /Landscape

/RectangularAlternateNudge: In this third rectangular variant, alternate rows (or columns) are slightly nudged, better to fill the space.

12 glasses, /RectangularAlternateNudge on /A3 /Landscape

Thus /RectangularAlternateNudge is a small deviation away from greater symmetry, about which the the author is unenthusiastic. Because of this non-enthusiasm, by default, /RectangularAlternateNudge comes with a sub-parameter: [ /RectangularAlternateNudge /ImprovementPointsMin 2 ]. This imposes an additional requirement: choose this packing style only if it is an improvement on the previous best radius of ≥ 2 points ≈ 0.7 mm. The next four examples show six glasses on /USL = 8½″×11″, with margins of 24pt = ⅓″, using the packing styles /Diamonds (radius ≈ 117.9); /SquareGrid and /RectangularDislocation (radius = 124); and /RectangularAlternateNudge (radius ≈ 126.3). So the radius improves by ≈ 2.3 pt, and hence the diameter by ≈ 1.6 mm. But on /A4 the improvement in the radius is much less: only ≈ 0.19 pt ≈ 0.066 mm, which seems insufficient to justify the asymmetry’s aesthetic damage.

/USL (8½″×11″)
6 glasses, /Diamonds on /USL /Portrait
radius ≈ 117.9pt ≈ 1.64″
/USL (8½″×11″)
6 glasses, /SquareGrid on /USL /Portrait
radius = 124pt ≈ 1.72″
/USL (8½″×11″)
6 glasses, /RectangularDislocation on /USL /Portrait
radius = 124pt ≈ 1.72″
/USL (8½″×11″)
6 glasses, /RectangularAlternateNudge on /USL /Portrait
radius ≈ 126.3pt ≈ 1.75″

/RectangularAlternateSplitNudge: This is a second ‘nudged’ rectangular variant, with the non-nudged rows (or columns) centred (or middled), and the nudged rows (etc) split, part nudged left (…) and part nudged right (). This works particularly well for twelve glasses on /USL2 = 17″×11″. In the two 12-glass examples below the rows are nudged; in the two 10-glass examples the columns are nudged. Note that /RectangularAlternateSplitNudge does not maximise the radius (obviously non-split nudging gives a larger radius) so for it to be used most other styles have to be removed from PermittedPackingStyles, or otherwise suppressed.

/USL2 (17″×11″)
12 glasses, /RectangularAlternateSplitNudge on /USL2 /Landscape
/USL2 (17″×11″)
12 glasses, [ /RectangularAlternateSplitNudge /Mirror ] on /USL2 /Landscape
[ /RectangularAlternateSplitNudge /Mirror ]
/USL2 (17″×11″)
10 glasses, /RectangularAlternateSplitNudge on /USL2 /Landscape
/USL2 (17″×11″)
10 glasses, [ /RectangularAlternateSplitNudge /Mirror ] on /USL2 /Landscape
[ /RectangularAlternateSplitNudge /Mirror ]

Both /RectangularAlternateSplitNudge and /RectangularAlternateNudge admit use of flags /ProhibitVerticalNudging or /ProhibitHorizontalNudging. Obviously using both would be strange.

7 glasses, /DiamondsPlus on /A4 /Portrait

/DiamondsPlus is mostly three-row diamonds (or, if /Portrait, three-column), with two extras set between the three rows (columns). It is the best packing of seven or ten glasses on several paper sizes.

There is a generalisation of /DiamondsPlus over any odd number of rows (columns), not yet coded.

/Arch and /PostsAndLintel: /PostsAndLintel arranges the circles around the left, top and right edges of the page, optionally with some circles at the bottom centre. This design might be particularly appropriate if the central circles held the candidate blends of a vintage, with some of the components around the edge. /Arch is similar, with the circles on a half ellipse.

The next four examples are specified as follows:

8 glasses, /PostsAndLintel on /A3 /Landscape
[ /PostsAndLintel /CentralGlasses 1 ]
7 glasses, /PostsAndLintel /Mirror on /A3 /Landscape
[ /PostsAndLintel /CentralGlasses 1 /Mirror ]
8 glasses, /Arch on /A3 /Landscape
[ /Arch /CentralGlasses 1 ]
10 glasses, /Arch on /A3 /Landscape
[ /Arch /CentralGlasses 3 /Mirror ]

The maximum number of /CentralGlasses is 3.

/Bespoke5 and /Bespoke7: Ignored for more than five and seven glasses, respectively.

5 glasses, /Bespoke5 on /A3 /Landscape
5 glasses, /Bespoke5 on /A3 /Landscape
[ /Bespoke5 /Mirror ]
/USLegal (14″×8½″)
7 glasses, /Bespoke7 on /USLegal /Landscape
/USLegal (14″×8½″)
7 glasses, /Bespoke7 on /USLegal /Landscape
[ /Bespoke7 /Mirror ]

This looks less good in /Portrait, so the default specifications are of the form [ /Bespoke5 /OnlyIfOrientation /Landscape ]. Self-evidently, the /OnlyIfOrientation causes the pattern to be ignored if the orientation is wrong.

/Temple: An intricate pattern, that has the largest radius for 10 glasses on /USL, and for 13 glasses on either /USLegal or /USL2. The sub-parameter /TempleExtraColsToLeftOrRowsBelow is to /Temple as /RectColsToLeftOrRowsBelow is to /DiamondsAndRectangular (so is followed by one integer).

/USL (11″×8½″)
10 glasses, /Temple on /USL /Landscape
/USLegal (14″×8½″)
13 glasses, /Temple on /USLegal /Landscape
/USL2 (17″×11″)
13 glasses, /Temple on /USL2 /Landscape

There are also three simple designs, /TopRow, /MiddleRow, /BottomRow, each having everything in one row, with obvious vertical position. There are also /Sides, /LeftSide, and /RightSide, with the obvious meanings. For more than a few glasses these ‘one-dimensional’ designs are too cramped.

4 glasses, /TopRow on /A3 /Landscape
4 glasses, /MiddleRow on /A3 /Landscape
4 glasses, /BottomRow on /A3 /Landscape
6 glasses, /Sides on /A3 /Landscape
3 glasses, /LeftSide on /A4 /Portrait
3 glasses, /RightSide on /A4 /Portrait
[ /Array /Positions [0 2] [2 2 3 2] [4 2 3 2] [6 2] [0 1] [3 1] [6 1] [0 0] [3 0] [6 0] ] on /A3 /Landscape
[ /Array /Positions [0 2] [2 2 3 2] [4 2 3 2] [6 2] [0 1] [3 1] [6 1] [0 0] [3 0] [6 0] ]

Alternatively, PermittedPackingStyles may specify a layout with an array of coordinates. Such a specification is always an array, [ /Array/Positions … ].

After the /Positions marker is a list of points, typically of the form [x y], with y increasing up the page. The code then chooses the radius and separately scales the x and y directions such that things fit as snugly as possible, obviously subject to the other upper bounds on the radius. The glass ordering is that given in the array: there is no subsequent sorting or ordering.

There is a more complicated variant, in which some of the sub-arrays are of the form [x y xy]. The first two elements are used, as before, and fix the radius and the canvas. The circle at (x,y) is then moved in a straight line towards (x′,y′). A circle stops moving if it collides with another circle (a tangential touch not being a collision); if it collides with the edge of the page; or it arrives at (x′,y′).

As an example of unmoved and moved circles (inspired by a GC + SW + QH triple vertical in November 2011), the diagram on the right has two frames, the difference being the small italic numbers in [ /Array /Positions [0 2] [2 2 3 2] [4 2 3 2] [6 2] [0 1] [3 1] [6 1] [0 0] [3 0] [6 0] ]. For the author’s taste, the closer relationship of the two B0 circles means they should be touching, rather than equally spaced between A0 and C0.

There are other flags and constraints and sub-parameters, as follows.

If PermittedPackingStyles is empty, or contains only invalid/impossible layouts, then instead several of the regular layouts are tried.

The following five diagrams show, for the five main page sizes with margins of 24pt = ⅓″ ≈ 8.5mm , the pattern that allows the largest circle. In faint grey on each image is, for easy comparison to glass sizes, the diameter in inches and millimetres.

/A3 (420mm×297mm)
/A3 /Landscape, best arrangements
/USL2 (17″×11″)
/USL2 /Landscape, best arrangements
/USLegal (14″×8½″)
/USLegal /Portrait, best arrangements
/USL (8½″×11″)
/USL /Portrait, best arrangements
/A4 (210mm×297mm)
/A4 /Portrait, best arrangements

(Not all of these ‘best’ arrangements are quite the mathematical optimum—for some of which see

 Radius bounds

When there are few glasses on a page, the radius of the circles could become very large. Hence the radius of the circles is bounded above by MaxRadius. Further, if there are multiple pages of glass sheets, it can look neater if all the pages, or all the pages in the same session, have the same radius. This is controlled by ShrinkRadii, which can be /NotAtAll, so each page’s radius is determined independently; /ToSmallest, so all pages have the same radius; or /ToSmallestSamePageOrdering, so the radius of each page in the same session, presumed to be equivalent to having the same value of PageOrderingGlasses, is shrunk to the smallest of that session’s radii. Alternatively, ShrinkRadii may be an array of the same length as GlassesOnSheets, pages being grouped if their array values are equal (so the default value of ShrinkRadii, “/ToSmallestSamePageOrdering”, is equivalent to “{PageOrderingGlasses}”).

 Other Page Types

Tastings also need other types of pages, including Tasting-note pages and Vote Recorders. But some page types lack the space to hold all the data that can fit in a glass-sized circle, having room for only two sets of text analogous to the four title-type arrays, as well as one analogous to the Circlearrays. The tables shows the variables in which these are stored, and the variations of the Names-type arrays.

On Glasses pages →

Page type ↓

Tasting NotesNamesTastingNotesCirclearraysTastingNotesTitlesTastingNotesSubtitlesTastingNotes
Vote RecorderNamesVoteRecorderCirclearraysVoteRecorderTitlesVoteRecorderSubtitlesVoteRecorder
Decanting NotesCirclearraysDecantingNotesTitlesDecantingNotesSubtitlesDecantingNotes
Cork DisplayCirclearraysCorkDisplayTitlesCorkDisplaySubtitlesCorkDisplay
Place NamesNamesPlaceNames
Pre Pour CirclearraysPrePour (box)
Circlearrays (circle)

As on Glasses pages.

Indeed, what is re-painted is a copy of whatever is on the Glasses sheets. Hence these four page types require that the relevant Glasses sheets exist. But they can exist and not appear, by /GlassesNumCopies 0 def.

Neck TagsCirclearraysNeckTags
Sticky LabelsStickyLabelsByNameWhichReplaceCirclearrays
else none.
sometimes NamesStickyLabels
Decanter Labels

Names… arrays may
be of unequal length.

All the Circlesarrays… / Titles… / Abovetitles / Belowtitles / Overtitles / Subtitles… arrays must be the same length.

NamesPlaceNames is actually an array of Names-like arrays, so nested one deeper, so there can be two place-name pages, one saying “Julian W.” and the other saying “Roast beef”—discussed below.

/Circlearrays [
	[ (1927) (Niepoort) ]
	[ (1931) (Niepoort) ]
	[ (1945) (Niepoort) ]
	[ (1955) (Niepoort) ]
	[ (1963) (Niepoort) ]
] def
/TitlesTastingNotes [ Circlearrays {0 get} forall ] def
/Titles [ TitlesTastingNotes {2 2 getinterval} forall ] def

These arrays may be set to already defined arrays, such as the default “/SubtitlesTastingNotes Belowtitles def”. Or they may be set to custom values, differing from one page type to the next.

Indeed, it is quite usual to generate variations automatically. For a vertical tasting, elegance is enhanced by having in the Titles two-digit years (“63”), but on the tasting-note sheets four-digit years (“1963”). The example on the right generates much of this automatically, and so repays study. (And, of course, if you are opening a bottle of Niepoort 1927 or Niepoort 1945, invite me.)


 Tasting-note pages

◊ By default there are also tasting-note pages (controlled by GlassesOnTastingNotePages), these pages being of size TastingNotesPaperType and orientation TastingNotesOrientation. The tasting-note pages are lightly divided into columns, the number of columns and the column headings being determined by the array TastingNotesColumnHeadings, by default this being [ (Times) (Eye) (Nose) (Mouth) (Score) ] (the “Times” column being intended to hold the times of decanting and sampling). These column headings are rendered in the font NamesFont at size TastingNotesColumnHeadingsFontSize. The columns have widths proportionate to TastingNotesColumnRelativeWidths, an array of the same length as TastingNotesColumnHeadings. TastingNotesColumnStrokeCode formats and strokes the lines that separate the columns.

The elements of TitlesTastingNotes are shown at a size not exceeding TastingNotesTitlesFontSizeMax, and the elements of SubtitlesTastingNotes are shown at TastingNotesSubtitleFontSizeProportionTitles times the size of the TitlesTastingNotes. The vertical separation between the CirclearraysTastingNotes and TitlesTastingNotes, and between the TitlesTastingNotes and SubtitlesTastingNotes, is TastingNotesLineGap.

	(JDAW) 5 1 /Alternating
	(DRT) 5 1 /Upright
] def
TastingNotesStarsNameColsRowsArrangement, /Alternating
TastingNotesStarsNameColsRowsArrangement, /Sideways
TastingNotesStarsNameColsRowsArrangement, /Upright

 Optionally, there can be a row of stars: circle to score. This can nudge those not prone to scoring into doing so. But different people use different scoring systems, so the parameter TastingNotesStarsNameColsRowsArrangement determines things by name.

This is an array of length a multiple of 4. The first of each set of four is the text of the name of the person to whom this applies (before comparing the code removes non-text). The second is a number of columns; the third a number of rows. The fourth of each set of four is /Alternating or /Sideways or /Upright.

There is also the decorative boolean TastingNotesCirclesBehind, which repeats the elements from the glasses pages, faded by a factor of either TastingNotesCirclesBehindFadingFactorIfAnyGrey or TastingNotesCirclesBehindFadingFactorIfAllBlack, according to whether ColourSchemeTitles is ever /MidGrey. By default TastingNotesCirclesBehind is true if distilling with Distiller, false otherwise (because of an awkward technical interaction between Mac Preview and GhostScript). The circles are in a straight line, the x position at the top being TastingNotesCirclesBehindTopX (0 ⇒ left, 1 ⇒ right), and at the bottom is TastingNotesCirclesBehindBottomX. The complication of the default values causes, over multiple TN pages, some variation to the visual effect. The boolean TastingNotesCirclesBehindFitAndCentreInRow controls whether the circles fit between the horizontal lines made by the Circlearrays (true), or whether they are as large as possible (false).

There has been discussion of printing A6 booklets to hold the tasting-note pages, one wine per page. To aid this it is possible to replace the names with page numbers via TastingNotesReplaceNameWithPageNum, the page-number string being defined in TastingNotesPageNumCompoundString, and being shown in a font size reduced by a factor of TastingNotesPageNumFontSizeFactor.

Side-by-side, right-Handed
Side-by-side, left-Handed

 Side-by-side Glasses and TastingNotes

◊ If having a mini tasting of only three wines, one might wish to print everything on one sheet. For right handers the three glasses would be on the left of the tasting-notes; for lefties, glasses to the right—as in the images on the right. This small-tasting layout is enabled by setting the boolean constant SideBySideGlassesTastingNotes to true. Between the two sides of the paper is a gutter, an internal margin, of size SideBySideGlassesTastingNotesWidthGutter; the proportion of the page occupied by the glasses (from the edge of the paper to the middle of the gutter) being SideBySideGlassesTastingNotesProportionPageGlasses. The people are right-handed unless present in the array LeftHanders.

When SideBySideGlassesTastingNotes is true several of the tasting-note parameters are ignored, action being determined by the glasses equivalent. Ignored parameters include TastingNotesPaperType and TastingNotesOrientation; PageOrderingTastingNotePages; and MirrorPagesTastingNotePages.

Example of a place-name sheet

 Place Names

◊ At a large formal tasting it is appropriate to label each person’s place-setting, so everybody knows who everybody else is, even late in the tasting. Pages to facilitate this are produced using the boolean PlaceNames parameter.

The pages are formatted to match the Titles: example on the right.

There can be multiple copies of the place names, with different names, so NamesPlaceNames is an array of arrays of names, by default containing just one sub-array, Names. Why the multiple sets of names. Have one set with the names of the people, over—so hiding—the set with names of foods. When the food is about to arrive, switch them, so that waiters and waitresses see which plates go where. That done, switch back. This might be achieved with parameters resembling those on the left:

/Names [ (Alex) (Ben) (Chris) (Derek) ] def
/NamesPlaceNames [
	Names  % People
		(Steak & kidney pie)  % Alex
		(Filet, medium-rare)  % Ben
		(Roast beef, lots)  % Chris
		(Low-salt diet salad)  % Derek
	]  % Foods, and food-free foods
] def

There is also the parameter PlaceNamesShowNameAsFooter, an array of booleans the same length as NamesPlaceNames, controlling whether the relevant element of Names is to be shown as a footer. If the big writing says “Low-salt diet salad”, it can simplify set-up to have a very small “Derek” written on the page.

Pages require three folds. First, fold in half; then fold and press hard along the faint dashed lines, thereby preventing the corners curling. The side with the small-writing header and footer should face the named person; the side without should face away. Whilst these are being rendered TypeOfPagesBeingRendered is /PlaceName, and the variable PlaceNameSetNum is the number of the item of NamesPlaceNames.

These place-name pages are set on pages of size PlaceNamesPaperType (defaulting to TastingNotesPaperType), and orientation PlaceNamesOrientation (default /Landscape). The lines marking the anti-curling folds are a distance PlaceNamesFirstAndThirdFoldsFromEdge away from the top and bottom edges, defaulting to 36 = ½″ = 12.7mm, which should be changed to 0 if printing to stiff card. The Names are set in font PlaceNamesFont (defaulting to NamesFont, though sometimes TitlesFont would be more appropriate), at the largest size that fits subject to an upper bound of PlaceNamesMaxFontSizeAbsolute (defaulting to 192). The colour scheme used is ColourSchemePlaceNames, this functioning in the same manner as ColourSchemeTitles. Because these names are usually larger than the Titles, various details such as line-widths are set larger by a factor of PlaceNamesDetailsScalingFactor; but, messily, this parameter is used partly in the code, and partly in the default values of other parameters.

Caution: if used with FillTitles distillation is excruciatingly slow. Distillation over the web will almost certainly time out: use local conversion.

A pre-pour sheet

A pre-pour page from a Ramos Pinto vertical on 21 October 2014. The grey lines are the top-left of the “RP” BackgroundTextsGlassesTexts.

 Pre-Pour Sheets

◊ Tastings arranged on ThePortForum are for a maximum of fourteen people, in order to give a 5cl portion to each person (75cl bottle, less the angels’ share and less a little loss during decanting leaves only an edge more than 14×5cl = 70cl). At a tasting attended by eight people, decanters can circulate and people help themselves. But when there are fourteen people and so only 5cl each, portions need to be pre-poured. This could be a source of confusion: “What did you pour into these fourteen glasses?”, “Err, I think…”. Instead glasses are arranged on or close to a page that labels them. Then the pourer knows what to pour; and those moving the glasses to the table know what has been poured.

The number of copies of these pre-pour sheets is PrePourNumCopies, the default being 1 if the length of Names is ≥13, otherwise 0. In an awkward working space /PrePourNumCopies 2 def might be useful.

The ordering of the loops is controlled by PrePourCollate: if true then PrePourNumCopies is outside SheetNum and WithinPage/WithinTitles; if false then PrePourNumCopies is inside. In a typical vertical tastings, the oldest wines come first in the list, so as to be top-left of the sheet. But decanting is in the reverse order, youngest to oldest, so it is convenient to reverse the order of the pre-pour sheets. This is controlled by PrePourReverseOrder, which defaults to true. There are also obvious parameters PrePourPaperType and PrePourOrientation.

If RotationTitlesAboveBelowOverCirclearray ≠ 0, then small arrows appear on the pre-pour sheets indicating the correct orientation.

Example of a tasting in which over-lapping pages caused problems with the pre-pour sheets

This was constructed from two sheets of A3 card, both of which included the ’63 and ’83. The right sheet was guillotined (shown green), overlapped on the left, and fixed with double-sided sticky tape. The double-presence of the ’63 and ’83 caused them to have two pre-pour sheets, a problem later fixed by PrePourRemoveDuplicatesByWithinTitles. Also observe use of HeaderFooterCenterX.

For some layouts, it may be desirable for a specific bottle to appear on more than one glasses sheet (e.g. when using overlapping sheets). The consequent duplication of pre-pour sheets is removed by PrePourRemoveDuplicatesByWithinTitles being true, its default value. The guillotine-and-overlap trick also messes with the order of the pre-pour sheets, sometimes fixable with PrePourSortByWithinTitles.

To make pre-pour sheets for wines not appearing on a glasses sheet, see GlassesNumCopies.

 Points: The Vote Recorder

◊ At the end of a tasting there is typically a vote for the Wine Of The Night (WOTN), and for large tastings there might also be a vote after each flight or page. The vote is not usually secret, people being asked to declare 1st, 2nd, and 3rd places (scoring 3, 2, 1). To assist the ‘returning officer’ there is paper on which votes can be recorded. For WOTN it is the points that should be recorded, rather than the ranks, as doing so simplifies the addition of the totals. (There is also benefit in there being agreement as to what is recorded: if you record, but I retain the vote-recorder sheet and enter it into a description of the tasting, it is useful that “3” be unambiguously first place three points.)

Indeed, at tastings, the rules are slightly more liberal. Each voter has 6 points to assign, which may be assigned in any simple fractions, subject to no one wine receiving more than 3. So permitted scoring patterns include the likes of 3:2:1, and 3:3, and 2:2:2, and 3:1½:1½, and 2:1½:1:½:½:½, and others.

Vote recorder sheets can also be used to hold, in a blind tasting, guesstimates of which is what.

These sheets are enabled by setting VoteRecorders to true. There are then conceptually two levels of loops. Pages are specified, except the question in the top text. Then each of these pages can be re-used with multiple top texts.

The vote-recorder sheet contains the wines within GlassesClusteredOnVoteRecorders, which is a triple-depth array, so one deeper than GlassesOnTastingNotePages. The outermost array is one per vote-recorder page: voting on separate days, with different people or wines, or different total-column-row flags, should be recorded on separate sheets. Within this are ‘clusters’, intended to align with tasting-note pages. Within this are integers pointing to wines. The default value is empty if GlassesOnTastingNotePages is empty, otherwise [GlassesOnTastingNotePages], so the cluster divisions (some 25% grey backgrounding) on the vote recorder are page divisions between the tasting-note pages.

Example of VoteRecorderCrossedBox.

Of the same length as GlassesClusteredOnVoteRecorders is VoteRecorderTopTexts, a double-depth array. The inner arrays hold the one/multiple top-texts to be used with each item of GlassesClusteredOnVoteRecorders. There are also arrays of booleans VoteRecorderShowTotalRow and VoteRecorderShowTotalCol, which specify whether there is to be a total column or row. Generally a total column is always wanted; typically a total row is wanted only for blind-tasting “What is it?” top-texts. The (Total) strings are in VoteRecorderTotalColTitle and VoteRecorderTotalRowTitle: there might be changes for Kernning or different languages. For blind-tasting “What is it?” voting, typically the person who brought a bottle should not guess. Rectangles can be crossed out by setting VoteRecorderCrossedBox to code evaluating to a boolean, that code typically referring to WithinTitles and NameNum.

VoteRecorderNamesOrientation controls the orientation of the NamesVoteRecorder, with allowed values /Horizontal, /Vertical, or the default value of /Either.

Also ∃ obvious parameters VoteRecorderPaperType and VoteRecorderOrientation; and VoteRecorderTitlesFontSizeMax and VoteRecorderSubtitleFontSizeProportionTitles (a positive number or /Automatic).

 Decanting Notes

◊ Those decanting should record the time each bottle is decanted, as well as the condition of the cork and perhaps the qualities of the sediment. Pages to do same are controlled by GlassesClusteredOnDecantingNotes, which has the same structure as GlassesClusteredOnVoteRecorders. There are DecantingNotesNumCopies copies of each page specified by GlassesClusteredOnDecantingNotes. Each page is headed DecantingNotesTopText, the columns being headed DecantingNotesColumnHeadingTimes and DecantingNotesColumnHeadingNotes. Other parameters specify page size and orientation, name the page, the columns, and DecantingNotesSubtitleFontSizeProportionTitles (a positive number or /Automatic).

Also ∃ obvious parameters DecantingNotesPaperType and DecantingNotesOrientation, and DecantingNotesTitlesFontSizeMax.

 The Accounts

Example of top of Accounts page.

◊ Ideally, tawdry financial matters will have been handled in advance, in a spreadsheet, with payments having been prompt and efficient. Ideally. But sometimes matters need to be settled after the food bill has arrived, late in the evening after much drinking. To help organise this there can be an accounts page, activated by setting AccountsNumCopies to more than zero, which happens by default if Names is of length ≥ 8. Columns are organised into groups, with titles for the groups and for the individual columns. AccountsColumnGroupHeadings contains the groups’ headings. AccountsSubColumnHeadings is of the same length as AccountsColumnGroupHeadings, but AccountsSubColumnHeadings contains arrays of the sub-columns’ headings. The relative widths of the sub-columns are in AccountsColumnRelativeWidths, the length of which is the sum of the lengths of AccountsSubColumnHeadings’ elements. There is one row per NamesAccounts (by default equalling Names), and a few extra rows as in AccountsExtraRows. There are also the obvious parameters AccountsTopText, AccountsColumnHeadingsFontSize, AccountsPaperType, and AccountsOrientation. (Also see the discussion about the request for an accounts page.)

Neck tag from a Dow versus Graham double vertical, 21 April 2016

 Neck Tags

There are several way to label bottles. Decanter labels could be made—but that entails some advance faffing with glue and string. Sticky labels could be printed—if whoever is doing the printing has the required labels. Neck tags are the easiest to prepare, though too small for decanters.

Neck tags are enabled by /NeckTagsNumCopies 1 def. There are then lots of fiddly parameters, which should generally be left at their default values. NeckTagsMinWidth, NeckTagsMaxWidth, NeckTagsMinHeight, and NeckTagsMaxHeight constrain the size of each tag. The circle to be cut is of radius NeckTagsHoleRadius, defaulting to 15mm radius ⇒ 30mm diameter. From the top of that hole to the top of the whole tag is NeckTagsSpaceAboveHole pt; between hole and top of the Title/Abovetitle/etc is at least NeckTagsSpaceAboveTitlesEtc pt; and between bottom of the Title/Belowtitle/etc and the inside of the bordering text (an element of CirclearraysNeckTags) is at least NeckTagsSpaceBelowTitlesEtc pt.

If there are BackgroundTextsGlasses then whether those background texts appear in the neck tags is controlled by NeckTagsShowBackgroundTexts. Likewise for OutlineTitles, by NeckTagsShowOutlineTitles; and for CrossHatchingInside, by NeckTagsShowCrossHatchingInside. The tags are on paper NeckTagsPaperType.

Example of a cork-display page.

 Cork Display

Bottles having been decanted, corks should be put on display, for which pages are activated by /CorkDisplayNumCopies 1 def. There are CorkDisplayNumCopies of each, and also CorkDisplaySpaceForNumExtras blanks for extra bottles that might appear. Each rectangle is at least CorkDisplayMinWidth wide, and at least CorkDisplayMinHeight high; and the corks shown are in GlassesClusteredOnCorkDisplay, a triple-depth array resembling GlassesClusteredOnVoteRecorders. Pages are titled with CorkDisplayTopText, defaulting to “The Corks”. The paper type is CorkDisplayPaperType; the orientation being chosen automatically.

Decanters, labelled, at a tasting on 16 March 2007

 Decanter labels

The making of decanter labels is controlled by the integer DecanterLabelsNumCopies. Decanter labels are to fit within a rectangle of size DecanterLabelsMaxSmallerDimension × DecanterLabelsMaxLargerDimension (which should be the size of business cards less a small margin). Orientation, of labels not page, is controlled by DecanterLabelsOrientation, with allowed values /Landscape, /Portrait, and /Automatic. The decanter label pages include instructions along the lines of “Decanter labels: cut; paste to business cards; allow to dry; punch holes; hang on decanters; fill decanters; wait; drink.” (this text being in the parameter DecanterLabelsTopText). Two sets of decanter labels can be pasted to both sides of the business cards, sometimes affording those sitting ‘behind’ the labels a clue as to contents.

 Sticky labels

What is the purpose of the sticky labels that isn’t handled by the decanter labels?

At the start of decanting, some bottles will be clearly labelled. Others might have little more than a capsule, so after opening, could be entirely unlabelled. To prevent confusion there should be, printed onto sheets of sticky labels, bottle labels. Alternatively, in an informal tasting, with many more wines than glasses, it can be useful to stick to the foot of the glass (please, never the bowl, always the foot) a very small sticky label identifying the current contents and the glass’s owner. Specifying these requires describing the sheet of labels

Sticky labels appear if StickyLabelsNumCopies exceeds zero, and multiple copies are permitted. If the labels are for bottles, there is one per wine. If for glasses, there is one per wine per element of Names. StickyLabelsByNameWhichReplaceCirclearrays controls which; if there is one label per wine per person, then the element of Circlearrays is replaced with the element of NamesStickyLabels. When there are labels by name, it is convenient for cutting if each name’s labels are in a convenient rectangular block, enforced by StickyLabelsAvoidAcrossColumnsOrRows, and not spilling over page boundaries, enforced by StickyLabelsAvoidAcrossPages.

To allow both bottle and glass labels, the sticky labels can be of multiple types, as contained in the array StickyLabelsTypes. This does very little: the sticky label code is run with StickyLabelsTypeThis set to each element of this in turn. The other parameters might choose to access the current value of StickyLabelsTypeThis. With StickyLabelsTypeThis equalling 0 the default parameters fit Ryman product 0220013460 (containing 25 sheets, or equivalently to the 100-sheet pack 0220023460), having on each A4 sheet eight labels, each 99mm×68mm. With StickyLabelsTypeThis equalling 1 the default parameters fit Avery J8651, each sheet of which has 65 labels, each 38.1mm×21.2mm. Regular users of this software should inform the author if other sheets of labels should be included in the defaults.

If not using a default, the user must change the specification of the label layout. For all the following it is assumed that the page is portrait.

Then StickyLabelsOrientation is the desired orientation of the labels. That is not necessarily the same as the orientation of the page: if, with the page held portrait, labels are wider than they are high, it will be the opposite. Allowed values are /Landscape, /Portrait, and /Automatic. If /Automatic then the orientation being chosen automatically, the code choosing /Landscape unless switching to /Portrait would increase the linear size of the text by ≥√√2.

If one has a part-used sheet of labels, it can help to specify the where on the first page of sticky labels to start, as if StickyLabelsFirstPageStartPosition had already been done.

To make sticky labels for wines not appearing on a glasses sheet, see GlassesNumCopies.

There are also settings StickyLabelsReverseOrder, StickyLabelsRemoveDuplicatesByWithinTitles, StickyLabelsSortByWithinTitles, StickyLabelsShowBackgroundTexts, and StickyLabelsShowCirclearraysInCircle (as well as in the straight lines top and bottom). These are analogous to the similarly-named PrePour… settings.

 Non-easy parameters

Stylised glasses sheet, 1 of 2 Stylised glasses sheet, 2 of 2

 GlassesOnSheets and GlassesOnTastingNotePages

◊ Imagine that ten wines are to be tasted—too many for one sheet of /A4 or /USL. So instead the glasses could be split over two sheets, as shown on the right.

This is done using the parameter GlassesOnSheets, which is an array of arrays, each of the inner arrays listing the elements of Titles etc to be on that page. This two-sheet example happens with GlassesOnSheets set to [   [ 0 1 2 3 4 ]   [ 5 6 7 8 9 ]   ], which is the default with Titles of length ten. Observe that numbering starts at 0: the first wine (described by the first element of each of Titles, Subitles, Circlearrays, and FillTexts) is number 0; the tenth glass is therefore number 9.

In the picture the WaterBoxes are on the right side of the second page—as the author is right-handed. Participants are right-handed unless in LeftHanders, an array of the names of known left-handers. Which pages have water counts is controlled by WaterBoxesShowRight and WaterBoxesShowLeft. These parameters are described in the section on WaterBoxes.

GlassesOnSheets has a more general purpose. As a putative tasting is being arranged, there are candidate bottles, some of which will appear in the final version. Create entries in Titles, Subitles, Circlearrays, and FillTexts for all these wines. Then, when decisions have been taken, use GlassesOnSheets to select from them, and to determine which wines appear in which order on which sheets.

The default value of /GlassesOnSheets is complicated. Let n = the length of Titles. Then if n ≤ 6, GlassesOnSheets is [ [0 1 2 … n–1] ], so putting all the glasses on a single page. Otherwise there are ⌈n ÷ 6⌉ pages, the glasses being distributed as evenly as possible, so each page holding ≤ 6. By default, GlassesOnSheets is the minimum number of pages subject to there being at most GlassesOnSheetsMaxPerSheet per page. The default value of GlassesOnSheetsMaxPerSheet can be 6 (if PaperType is /A4 or /USL) or 9 (/USLegal) or 14 (/A3 or /USL2).

The parameter GlassesOnTastingNotePages works the same way, controlling the tasting-note pages. By default it is built analgously to GlassesOnSheets, except from GlassesOnSheetsMaxPerTNSheet.

 Type sizes

How big is the circle text? In points, not bigger than CircletextMaxFontSizeAbsolute; and as a proportion of the circles’ radius not bigger than CircletextMaxFontSizeProportionRadius. If, in a font size equal to the lesser of these two, a particular item in Circlearrays is too long to fit at least CircletextsMinCopies times, then the font size is reduced such that it does. If CircletextsSameFontSizeIfRadiiShrunkToBeSame, then across all pages compelled to have the same radius (see ShrinkRadii), the Circlearrays are compelled to have the same size. If not, the font size of Circlearrays can vary within and between pages. (Technical note: CircletextsSameFontSizeIfRadiiShrunkToBeSame should have the same boolean value over any such set of pages. Typically this is most easily achieved by having it depend on PageOrderingGlasses SheetNum get or on ShrinkRadii SheetNum get, rather than directly on SheetNum.)

Imagine that, at the font size chosen in the previous paragraph, some particular item of Circlearrays fits 3.999 times, and so would be shown 3 times, with white space occupying the ‘0.999’ remainder. Aesthetically it would be better if the font size were tweaked a mite smaller, this item then fitting 4+ε times and being shown 4 times. The boolean parameter CircletextsTweakSize activates this tweaking. And if the boolean CircletextMaxFontSizeMayBeSlightlyExceeded is true, then there can be an initial tweak upwards in size (not exceeding a factor of √√2 ≈ 1.189). But if the user needs precise control over the font size (but why?), then both should be changed to false.

If Rays is activated, then CircletextsTryToResonateWithRays can improve the look of a very short item of Circlearrays, such as [ /dagger ] = “†”. The number of copies is also bounded above by CircletextsMaxCopies; when this is invoked the the number of copies that would otherwise appear is on the top of the stack, so CircletextsMaxCopies could be code referring to it, such code advisedly starting with a “dup”. Relatedly, the minimum separation between the items of the sub-arrays of Circlearrays, measured in space-widths, is CircletextsMinNumSpacesBetween.

The type sizes of Titles and Abovetitles, Belowtitles and Overtitles are controlled by a multi-stage process.

First, the Titles are fitted, and various one-at-a-time constraints applied. These include:

TitleMaxHeightProportionInnerRadius is the maximum height, measured bottom to top, of the Titles, expressed as a proportion of the ‘inner radius’, that is, of the radius to the inside of the Circlearrays, adjusted by ExclusionAnnulusProportionInnerRadiusTitlesAboveBelow.

TitleMinHeightForAbovetitleProportionInnerRadius: constrains the upper bound of the title if the relevant element of Abovetitles is non-empty.

TitleMinHeightForBelowtitleProportionInnerRadius: constrains the lower bound of the title if the relevant element of Belowtitles is non-empty.

For these purposes, “the circle” is the inside of the Circlearrays, adjusted by ExclusionAnnulusProportionInnerRadiusTitlesAboveBelow (for the Titles, Abovetitles and Belowtitles) or by ExclusionAnnulusProportionInnerRadiusOvertitles (for just the Overtitles). These might behave weirdly if negative or ≥1, and default to zero which uses all available space.

Second, the font sizes of the Titles are considered together. It would look messy if there were many type sizes, only slightly different. If FontSizesTitlesNotSmallerIfTitlesNotLonger true, then sizes are so constrained. I.e., the font size of “Df” must be ≤ the font size of “W” which must = that of “I”. Then a constraint is applied to their ratio: for two titles, either they have the same font size, or the larger÷smaller ratio of their font sizes ≥ FontSizesRatioTitlesMin, which defaults to 2.

Except that one might not wish to apply this constraint over pages in different sessions: it would hardly matter whether yesterday’s sheets did or did not precisely match today’s. So there is an array FontSizesSetsGlassesPages of the same length as GlassesOnSheets, and pages with matching values in this array are considered together.

Third, Abovetitles, Belowtitles and Overtitles are fitted around the Titles. Then, if needed, these are lowered to the size of the matching element of Titles, times AbovetitleMaxFontSizeProportionTitles, BelowtitleMaxFontSizeProportionTitles or OvertitleMaxFontSizeProportionTitles.

Fourth, as with the Titles, fewer matching font sizes would look better.

○ If FontSizesAboveBelowOverNotSmallerIfTitlesNotLonger, then, in turn and separately for each of Abovetitles, Belowtitles and Overtitles, lengths are constrained according to the lengths of the elements of Titles.

○ It is possible to force items to be the shown at the same font size. FontSizesTitlesEquivalences is an array of the same length as Titles. If two items of FontSizesTitlesEquivalences are equal, then the relevant Titles are forced to the size of the smaller. Likewise FontSizesAbovetitlesEquivalences for the Abovetitles, and FontSizesBelowtitlesEquivalences for the Belowtitles, and FontSizesOvertitlesEquivalences for the Overtitles. (But be careful: FontSizesTitlesEquivalences and FontSizesAboveBelowOverNotSmallerIfTitlesNotLonger together can force everything to the same size: if a short title is forced to be the same size as a long title, then every other title longer than the short title would also be forced to the same size.)

○ Then the ratio of FontSizesRatioAboveBelowOverMin is used, and over the same FontSizesSetsGlassesPages sets of pages.

But should Abovetitles be considered with Overtitles? If their fonts are very different, perhaps not. So there is an array of length three, FontSizesSetsAboveBelowOver, the elements of which refer to ‘Above’, ‘Below’ and ‘Over’, in that order. If the elements of FontSizesSetsAboveBelowOver are equal, the arrays are considered jointly. Typically, arrays in the same or similar fonts should be considered together; those in very different fonts should not.


BackgroundTextsGlasses: some 1982s BackgroundTextsGlasses: some 1983s    BackgroundTextsGlasses: more 1982s BackgroundTextsGlasses: more 1983s

In some multi-page tastings, for each page all the wines have something in common. A typical example is a multi-horizontal, in which wines from several houses are tasted from two or few vintages. As everything on a page has a common feature, perhaps this common feature should be marked, faintly, as in the two-session four-page example on the right.

This is activated by setting BackgroundTextsGlasses to true. Then BackgroundTextsGlassesTexts is an array of the same length as GlassesOnSheets, containing the strings to be shown on glasses pages. The strings should be short: one or two characters is recommended, and are shown in font BackgroundTextsFont. Likewise BackgroundTextsTNsTexts is of the same length as GlassesOnTastingNotePages.

The text is aligned with BackgroundTextsAlignmentVertical (being one of /Middle, /Bottom, /Top) and BackgroundTextsAlignmentHorizontal (being one of /Centre, /Left, /Right).

The separate x and y sizes each as large as possible, subject to the following.

The bounding path of the text is constructed and then BackgroundTextsGlassesPaintCode is executed. The default clips then strokes, but the example above shows a plain fill.

BackgroundTexts… looks best with the ‘Squoosh’ at 1 (so not squooshed), but still touching all four margins. This precise fitting can be done by inserting into the compound string some {±x Kern}. The amount of extra kerning required is computed, and appears in the output log—typically the last page of the PDF.

The boolean parameter BackgroundTextsTastingNotes also shows these on the tasting-note pages. For pre-pour pages, which are naturally associated with a glasses page, the boolean BackgroundTextsPrePour causes the BackgroundTextsGlassesTexts to appear; and likewise BackgroundTextsDecanterLabels underneath each decanter label.

 But I’m not drinking that: GlassesCirclesFadingFactor and GlassesCrossedOut

Sometimes some people aren’t drinking some things. Perhaps the men are refraining from the Champagne to save more for their wives (e.g.); perhaps some people just don’t want some things. The placemats should indicate this, so that unwanted glasses are not wastefully filled. The parameter GlassesCirclesFadingFactor can be code that returns a real ≥0 and ≤1, in which 0 means entirely faded away and 1 is the black-is-black default. E.g., /GlassesCirclesFadingFactor {(Julian) Names NameNum get eq  WithinTitles 0 eq  and {0.125} {1} ifelse} def fades the first glass (glass 0) of Julian. It is also possible to cross out a glass, with the boolean parameter GlassesCrossedOut, which by default is {GlassesCirclesFadingFactor 0.5 le}.

 Separating flights within a page

[   [ [/Left 0 3 -0.5] [0 3] [/Top 3 0 +0.5] ]   [ [/Left 5 +1.0] [/Clockwise 5] [/Bottom 5 +1.0] ]   [ [/Bottom 3] /Curve [3 6] /Curve [3 1] /Curve [/Top 3] ]   [ [/Top 4] [1 4] [/Widdershins 4] [/Bottom 4] ]   [ /Closed [/Right] [/Bottom] [/Clockwise 7] [/Widdershins 4] [/Clockwise 2] ]   ]
[ /Closed [0 3] [3 1] /Curve [1 4] [4 2] [4 7] /Curve  [4 6] [/Widdershins 6] [6 3] /Curve [3 5] ]

◊ It is often natural to split a tasting into ‘flights’ of a few wines. This can structure a balance between quietly contemplating the differences between wines, and subsequent sociable discussion. The parameters FlightSeparations and FlightSeparationLines allow the page to be divided into zones. However FlightSeparationLines is fiddly, and if one wine is added or taken away, will need to be completely reworked. Hence it is recommended that division into flights be saved until late in the planning.

FlightSeparations is a boolean: true to activate the line-drawing code, false de-activated. The real work is done by FlightSeparationLines, being an array nested four deep. FlightSeparationLines is of the same length as GlassesOnSheets. Each item of FlightSeparationLines is an array, each of which holds line descriptions, as many as there are lines on that page. Each line description is an array, pieces of which can be as follows.

In the PDF of examples the header shows that page’s item of FlightSeparationLines (i.e., FlightSeparationLines is an array one deeper than the header). If using FlightSeparations it is strongly recommended that these PDF examples be examined and appropriate parts used as a starting draft. The first two pages of this file are shown on the right, the first having many example lines, to the gain of instruction and the loss of aesthetics.

The path can be stroked, clipped or filled by the code FlightSeparationPaintCode, which has a default stroke that is complicated but elegant. Use of fill is discouraged, as it impedes comparison of colours of the wines. If FlightSeparationPaintSeparately then FlightSeparationPaintCode is invoked after each line, the variable FlightSeparationLineNum being set to the appropriate integer ≥ 0. If not FlightSeparationPaintSeparately, then all the lines on the current page are drawn, after which is the single call of FlightSeparationPaintCode. The former is better if the format is not constant; the latter preferred if overlapping or crossing lines are to be double-stroked.

Some control over the radius of the arcs is sometimes possible. If any circles touch, the radius of the arcs equals that of the circles. If no circles touch (probably because of MaxRadius or ShrinkRadii) then FlightSeparationArcRadiusControl controls the radius. FlightSeparationArcRadiusControl should be both ≥0 and ≤1; the default being the maximum. If it is 0, the radius of the arcs is that of the circles. If 1, the radius is the half the distance between the closest circles. If between, between.

There is a bug in Adobe Distiller, such that if FlightSeparationPaintSeparately is false and there are multiple lines on one page (⇔ FlightSeparationLines SheetNum get length ≥ 2), the apparent ends of some lines are controlled by setlinejoin rather than setlinecap. It is a small aesthetic imperfection, but an aesthetic imperfection nonetheless. The problem can be avoided if no line begins where the previous one ended, achievable by reversing the direction of individual lines, or by re-ordering the set of lines.

The 1997 Horizontal on 16th June 2014 had complicated usage of FlightSeparationLines, as can be seen from the following explanatory diagram. Some text nudged to keep it on the page, most obviously that referring to the corners.)

Example of complicated usage of FlightSeparationLines.

 Page order

◊ The order of the pages is controlled by eight arrays of integers.

Array nameLengthDefault
Default explained
PageOrderingGlassessame as GlassesOnSheets1 Sorted into order by the algorithm: in the outermost loop, by paper size. Within that, for simplicity of table setting, starting with interleaved glasses sheets and tasting-note sheets; then the vote-recording page(s) to be placed with the returning officer’s tasting-note sheets; then decanting notes, accounts, cork display and pre-pour pages.
PageOrderingTastingNotePagessame as GlassesOnTastingNotePages1
PageOrderingVoteRecordersame as GlassesClusteredOnVoteRecorders1
PageOrderingDecantingNotessame as GlassesClusteredOnDecantingNotes1
PageOrderingAccountsarbitrarysame as PageOrderingVoteRecorder, duplicates removed
PageOrderingCorkDisplaysame as GlassesClusteredOnCorkDisplay1
PageOrderingNeckTagssame as GlassesOnSheets1
PageOrderingPrePourPagessame as GlassesOnSheets1
PageOrderingPlaceNamessame as NamesPlaceNames100Separated from 1s to facilitate pre-tasting folding.
PageOrderingDecanterLabelssame as GlassesOnSheets200Separated from above to facilitate pre-tasting gluing.
PageOrderingStickyLabelssame as GlassesOnSheets300Separated from above so there is a single switch of printer paper to the labels.

Draws are resolved in a manner that separates pages on different paper types, perhaps simplifing printer loading, as described by the following algorithm (in which “matching” means matching the page ordering and the paper type).

The values in PageOrderingGlasses are also used by ShrinkRadii as they are presumed to define sessions. The values in PageOrderingGlasses are also used to determine the default values of WaterBoxesShowRight and WaterBoxesShowLeft.

Example Table of contents

The sections can be lightly titled. The parameter PageOrderingSections is an array of even length, alternately items of the page orderings and compound strings, that adds extra rows to the PDF reader’s table of contents. The compound strings appear before the page orderings with which they are paired. On the right is an extract from a screen image, taken from a Mac’s PDF reader, Preview. The pinked regions (highlighted here for clarity but not in the actual PDF) were added with the following—do observe the separate addition of the empty lines. (Since this was made the software has become more assiduous about adding page numbers to the Table of Contents.) Alas Mac Preview, at least up to version 8.1 (877), ignores a little of the formating, as illustrated on the Apple Support Communities.

/PageOrderingSections [
	  0  ()
	  0  [/section ( Friday evening, Cantley House)]
	 10  ()
	 10  [/section ( Saturday afternoon, Walter Arms)]
	 20  ()
	 20  [/section ( Saturday evening, Cantley House)]
	200  ()
	200  [/section ( Multiple sessions)]
] def


Sometimes PDF pages are to be inserted after the PostScript, typically made in Word or Excel or another application. That mostly works, except that it would mess the page numbering in the Table of Contents. This can be repaired, and the inserted pages can have their own ToC entries. Start by /PagesToBeInserted true def. Then there are five arrays of the same length, one of which is an array of arrays. ToC entries appear before a page of one of the types in the sub-arrays of PagesToBeInsertedBeforeTypeOneOf, and before instance number of the item of PagesToBeInsertedBeforeInstances. (The double-depth array allows some flexibility by de facto merging several types; and instance 0 is the first such block of these types, 1 the second, etc.) If the element of PagesToBeInsertedDests is a name, so beginning with a “/”, there will be a ToC entry pointing to this ‘DEST’, titled with the element of PagesToBeInsertedDescriptions. And the number of pages being inserted is the element of PagesToBeInsertedNumPages, which allows the page numbering to be correct.

If changing the PDF with Mac Adobe Pro XI, the user could take the following steps. • Adobe Pro XI, show thumbnails icon Show the page thumbnails. • Drag from the finder to the gap between the appropriate pages the PDF to be inserted. • Right-click the inserted page, ‘Number Pages…’; Style=‘None’, Prefix=“Food order”. • Adobe Pro XI, show DESTinations icon Show the DESTinations. • Navigate to inserted page; Adobe Pro XI, new DESTinations icon click new DESTination icon; give it the correct name (such as “FoodOrder_0”, without the “/” prefix needed in the PostScript). • Save. Done.

 Page mirroring

◊ Generally one should print using toner not ink, as a single drop of wine or water causes ink to smudge. Alternatively, one could print the glasses mats onto acetate, and use the non-printed side. This requires mirroring the page, this being controlled by the eight arrays MirrorPagesGlasses, MirrorPagesTastingNotePages, MirrorPagesVoteRecorder, MirrorPagesDecantingNotes, MirrorPagesAccounts, MirrorPagesCorkDisplay, MirrorPagesPrePour, MirrorPagesPlaceNames, MirrorPagesDecanterLabels, and MirrorPagesStickyLabels. These are of the same length as the arrays PageOrdering…, though contain booleans. By default all values are false.

Use of page mirroring should be accompanied by use of PageOrdering…, to ensure that the acetates are consecutive.

 Numbers of copies

For most types of sheet, there is a parameter controlling how many copies appear. Already described are DecanterLabelsNumCopies, StickyLabelsNumCopies, PrePourNumCopies, DecantingNotesNumCopies, AccountsNumCopies. Analogously, there are parameters GlassesNumCopies and TastingNotePagesNumCopies, both defaulting to 1. For the vote-recorder sheets the parameter is a boolean, VoteRecorders.

GlassesNumCopies has a role that is not obvious. For both the pre-pour sheets and the sticky labels what appears there is a scaled version of that on the glasses sheets. So if a wine does not appear on the glasses sheets, it cannot appear on the pre-pours or sticky labels. But what if this is wanted? What if the pre-pour sheets should be known wines such as D63 F63 G63 T63, but the glasses sheets should be a blinded version of this such as α63 β63 γ63 δ63 (example of such a tasting)? Then construct additional glasses sheets to hold the material for the extra pre-pour or sticky-label sheets, but suppress their output with something like /GlassesNumCopies {SheetNum 4 ge {0} {1} ifelse} def. That done, the values of GlassesOnTastingNotePages and GlassesClusteredOnDecantingNotes might also need work.

 Suppressing pages

You have made the placemats for somebody else’s large tasting. But that somebody else wants to change a font; or doesn’t know what is a pre-pour sheet nor whether they are wanted. One could make several new versions, each of several megabytes and several hundred pages, and email those. But only a few pages are relevant. One could kill the other pages by specifying 0 copies or setting their presence to false, and by reducing the number of wines and people. What a faff, and because of the interactions between font sizes, doing so might change the shown pages. Instead suppress temporarily-unwanted page types with the array TestingSuppressPageTypes, which can contain any of /Glasses /TastingNotes /VoteRecorder /DecantingNotes /Accounts /CorkDisplay /NeckTags /PrePour /PlaceName /DecanterLabels /StickyLabels /DistillerLog. Also set TestingMaxNumPagesToShow to a small integer, which will suppress all subsequent pages.

 Placemat ©

Who owns the © of the output, of the placemats? The parameter CopyrightStatementPlacemats holds this, defaulting to “Copyright 2016 Julian D. A. Wiseman of”. Obviously, adjust to your needs, copying the form. By default others may use the output, as given in the human-readable text LicensingAgreementTextPlacemats, defaulting to “This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International Licence.” There is the related parameter LicensingAgreementLinkPlacemats, defaulting to ( Obviously LicensingAgreementTextPlacemats and LicensingAgreementLinkPlacemats should be consistent. It is hoped to embed these permissions, and perhaps additional parameters, in a machine-readable manner. Please could those with expertise in the use of pdfmark read the request for help, and then help.

 Code within parameters

Another manual page explains how and why to vary the parameters by sheet or by glass, and how and why to add code inside compound strings.

Page typeTypeOfPagesBeingRenderedURL ending of first
page of this type
Tasting notes/TastingNotes…#TastingNotes_0
Vote recorders/VoteRecorder…#VoteRecorder_0
Decanting notes/DecantingNotes…#DecantingNotes_0
Cork display/CorkDisplay…#CorkDisplay_0
Neck-tag pages/NeckTags…#NeckTags_0
Pre-pour pages/PrePour…#PrePour_0
Place names/PlaceName…#PlaceName_0
Decanter labels/DecanterLabels…#DecanterLabels_0
Sticky labels/StickyLabels…#StickyLabels_0

 Linking to specific pages within the PDF

So that it is possible to link to specific pages within the PDF, the PDF includes some ‘nameddest’s. However, the use of these is imperfectly reliable (see help received). It seems to work when opening a PDF within the browser, but if the PDF is read by a separate application, it seems not to work.

To link to the first of the pages of glasses the URL should end …#Glasses_0. For second page, …#Glasses_1; etc. Links for other types of pages are shown in the table on the right. The log file also contains a list of the “URL # tags” added to the PDF.

If GlassesDestForEachCircle is true, there are also # tags for individual glass circles, as detailed in the log. (But not all PDF viewers correctly zoom to these circles: see bugs in Apple Preview and Google Chrome.) For some actual placemats, changing GlassesDestForEachCircle from false to true increased the file size from ≈ 360k to ≈ 400k, ≈ 11%: not tiny. These ‘Dest’s are most useful for the placemat maker, speeding the checking of an individual circle–but there’s no need to check the same circle on multiples Names, because they are all the same. So GlassesDestForEachCircle’s default value is {NameNum 0 eq}, which makes them for only the first of the Names.

 Errors: causation and avoidance

This is a PostScript program, and its various parameters are entered directly in PostScript. The program does little error checking. If parameters are malformed (e.g., non-matching parentheses), or if the parameters have nonsense values, then the output might be inelegant, incomplete, or non-existent.

If it just won’t work, then start again with the code as published at Does that distill correctly? Then change the Titles etc, and set the Circlearrays to be something simple of the same length. Does that distill correctly? Then add any required complexity to the Circlearrays, and re-test, before setting the other parameters one at a time. If you believe yourself to be coping competently, and it still won’t work, contact the author, enclosing in the email a link to the PostScript file and the log file produced by the distiller, and saying which program you use for the ps→pdf conversion.

The program outputs to a log file. This can contain various ‘Error’s, ‘Warning’s, and ‘Advice’s, as well as values of variables and other diagnostics. Perusal of this data might help. If OutputLogToPage is still at its default value of true, this log file also appears at the end of the output. There is the analogous parameter OutputLogToLog, and LogThisExtra, a user-chosen string which is output to the log.


The author welcomes comment on this program, which may be by email or in the appropriate thread on The Port Forum (there also being a thread showing the used parameters from actual tastings, a thread on For The Love Of Port, and a thread on WineBerserkers).

 Who to invite?

Permission is given to make non-commercial use of this program as you see fit, and to distribute its output. But not to distribute the code: refer interested parties to the latest version via this page (​placemat/​placemat.html). Those with improvements to the code are invited to send them to the author.

It hardly needs saying that the finest way to thank me for this excellent product would be an invitation to your tasting! I’m particularly fond of Port and Madeira, and to a lesser extent of whisky and beer, but am willing to have my horizons broadened. Let me check my diary… yes, that evening is good—thank you very much indeed.

— Julian D. A. Wiseman

 Recent changes

April 2016

March 2016

February 2016

January 2016

December 2015

October 2015

Main index Top About author