Main index Other Papers index About author

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 www.jdawiseman.com. 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 January 2012, 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.

There are separate pages describing: the choosing of the page size; the obscure parameters; and how to use code in parameters. Also useful might be /TimesNewRomanPS-BoldMT: accented characters and their PostScript glyphs, which includes diacritics as well as accents. Further, PostScript programmers are invited to peruse the list of re-usable routines.

Introduction

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 are also rendered immediately below.

Example of an 8-glass port tasting placemat Example of tasting-notes sheet

Making Your First Placemat: Advice for Beginners

A code editor, a good text editor, is a boon, and it should be one that understands PostScript. On a Mac Alpha X is excellent, and Aquamacs Emacs is suitable for Emacs experts. On a PC the specification of NotePad++ looks encouraging, and user feedback has been enthusiastic. 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.

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”. As a test convert the unedited unchanged PostScript to PDF, perhaps by using PS2PDF.com.

Make a copy of the original PostScript. Give it a name which will make sense in aeons to come: perhaps YYYYMMDD.ps, or YYYYMMDD_TastingName.ps. Look at the ¶ of examples, both for file names, and for a sense of which of the many features might be wanted. 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.

If decanter labels are wanted, set DecanterLabelsNumCopies to 1, or perhaps 2.

By default, glasses are spread sensibly over one or several sheets. But if necessary change GlassesOnSheets to something like [   [ 0 1 2 3 ]   [ 4 5 6 7 ]   ]. (Obviously, this precise example assumes exactly 8 glasses on two pages each of four glasses.)

Now test some of the more exuberant features described in the rest of this manual. 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. 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 “First draft of the placemats”. Allow some time for comments and corrections and changes. Update if appropriate (and post “Updated draft of the placemats”).

Perhaps send to the author a copy of the PS and PDF for comment. 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.

Construct the decanter labels:

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

Enjoy the tasting.

 As further 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. 25 July 2012, Olympic years at the Portuguese Embassy17 Feb 2012, horizontal of 198031 Jan 2012, blind or not3 Jan 2012, small horizontal of 197715 Dec 2011, sticky labels at the Bell30 Nov 2011, the diet of the gods8 Nov 2011, ‘Port with Star Quality’11 Oct 2011, semi-blind 4×419 September 2011, ragbag of good port19 Aug 2011, emergency drinking, blind8 Aug 2011, mostly blind, port from the ’90s, and the annexe in Paris13 May 2011, Sandeman4 Apr 2011, Fonseca versus Guimaraens24 Feb 2011, Köln, Colheitas10 Dec 2010, Köln, 1955 horizontal29 Dec 2010, blind 1→1221 June 2010, horizontal of 198217 Apr 2010, Warre in Sussex16 Apr 2010, exercises ahead of Warre in Sussex1 Feb 2010, bring a bottle5 Jan 2010, 1985 or 197013 Nov 2009, bring a bottle2 Oct 2009, Warre21 July 2009, eighties night27 June 2009, at Quinta St Luiz26 June 2009, decent drinking;  and, undated, from the code’s default parameters. Also see complete list of archiving links.

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; the author has successfully used PS2PDF.com (fonts that can be used if distilling at PS2PDF.com). 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 (eight in the example above), and the program copes with any sensible number of glasses.

In the example provided, Titles is defined thus:

/Titles [
	(S70)
	(G70)
	(G63)
	(S77)
	(G77)
	(G85)
	(S85)
	(G85)
] 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 these titles is short, ideally ≤3 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 [
	()
	()
	()
	()
	()
	(Single)
	()
	(Magnum)
] 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 [
	[ (Sandeman) (1970) ]
	[ (Graham) (1970) ]
	[ (Graham) (1963) ]
	[ (Sandeman) (1977) ]
	[ (Graham) (1977) ]
	[ (Graham) (1985) (Single) ]
	[ (Sandeman) (1985) ]
	[ (Graham) (1985) (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 [
	(JDAW)
	[(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; etc…. (See /TimesNewRomanPS-BoldMT: accented characters and their PostScript glyphs.)

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

Currencies, fractions and arithmetic: £ /sterling; € /Euro; ¥ /yen (also used for the Renminbi); ₩ /won; ₪ /sheqel; ¢ /cent; ½ /onehalf; ¼ /onequarter; ¾ /threequarters; ⅛ /oneeighth; ⅜ /threeeighths; ⅝ /fiveeighths; ⅞ /seveneighths; ⅓ /onethird; ⅔ /twothirds; ≈ /approxequal; ≥ /greaterequal; ≤ /lessequal; ≠ /notequal; × /multiply; ÷ /divide.

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.

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 Names makes the silly but didactic “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 www.jdawiseman.com/​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}”.

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. Observe that Titles is short (“T”), and hence can be shown very large. Also 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”.

Other Page Types

Much of this data is used to drive many other types of pages, including Tasting-note pages, Vote Recorder and Decanting Notes. But these lack the space to hold all the data that can fit in a glass-sized circle. Each has room for two sets of text analogous to the four title-type arrays, and one analogous to the Circlearrays. These are stored in arrays as follows.

On glasses pages→
Page type↓
TitlesAbovetitles
Belowtitles
Overtitles
Circlearrays
Tasting NotesTitlesTastingNotesSubtitlesTastingNotesCirclearraysTastingNotes
Vote RecorderTitlesVoteRecorderSubtitlesVoteRecorderCirclearraysVoteRecorder
Decanting NotesTitlesDecantingNotesSubtitlesDecantingNotesCirclearraysDecantingNotes
Cork DisplayTitlesCorkDisplaySubtitlesCorkDisplayCirclearraysCorkDisplay

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. But all these arrays must be the same length.

/TitlesTastingNotes [ (1927) (1935) (1945) (1955) (1963) ] def
/Titles [ TitlesTastingNotes {2 2 getinterval} forall ] def

Indeed, it is quite usual to generate variations automatically. For a vertical tasting, elegance is enhanced by having two-digit years in the Titles, but four-digit years on the tasting-note sheets. The two-digit array can be produced from the four-digit as on the right.

Other parameters

Fonts

◊ The fonts in which the various text pieces are rendered are contained in the parameters TitlesFont, AbovetitlesFont, BelowtitlesFont, OvertitlesFont, FillTextFont, CircletextFont, and NamesFont. But PostScript uses font names that are not always obvious. Help is at hand: distilling fonts_illustrated.ps 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 fonts_illustrated.ps.

There is also, for the tasting-note and other sheets, SubtitlesFont.

The program was designed for left-to-right alphabets, and only Roman fonts have been tested. 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

◊ Two parameters control the page size and the page orientation:

/PaperType /A4 def  % /A4 /A3 /B4 /USL /USLegal /USL2 [SmallerPts LargerPts]
/Orientation /Portrait def  % /Landscape /Portrait

In PostScript distances, lengths, widths, etc, are always denominated in points, where 72 pt = 1″ = 1 inch = 25.4 mm, and hence 1 pt = 127360 mm = 0.3527 mm, and 1 mm ≈ 2.83 pt.

In the first line allowed values include /A4 (210mm×297mm, 8.28″×11.70″), /USL (8½″×11″), /A3 (297mm×420mm, 11.70″×16.55″), /USLegal (8½″×14″), /USL2 (11″×17″), or a custom page size expressed in points as an array of length two: [d1 d2] with 0 < d1 ≤ d2. For convenience of copy-pasting, allowed values appear in the comment at the end of the line—after the “%”. Also allowed are sizes from /A9 (don’t ask why but do shrink the margins) to /A0, and from /B9 to /B0. In the second line allowed values are /Landscape and /Portrait.

Also see the separate manual page Glasses placemat: choosing a paper size.

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. The margin equals the parameters MarginL (left), MarginR (right), MarginT (top), and MarginB (bottom), each defaulting to 30.

Decorating the Titles

Colour Schemes

By default, the Titles are black if the relevant item of Overtitles is empty, and grey otherwise. By default the Abovetitles and Belowtitles are always black; and Overtitles is black if the relevant item of Titles isn’t. These colour schemes are controlled by the parameters ColourSchemeTitles, ColourSchemeAbovetitles, ColourSchemeBelowtitles, 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.

Example of CrossHatchingTitles

CrossHatching

◊ 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, or /Right; and y coordinate CrossHatchingCentreY with possible values /Name, /Bottom, /Middle, or /Top. 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. The author believes that CrossHatching… looks quite stylish when every item of Overtitles is empty, and hence the text in black and the lines defaulting to white.

There are analogous booleans CrossHatchingAbovetitles, CrossHatchingBelowtitles, and CrossHatchingOvertitles; and strokeing code CrossHatchingAbovetitlesStrokeCode, CrossHatchingBelowtitlesStrokeCode, and CrossHatchingOvertitlesStrokeCode.

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. 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.

OutlineTitles

◊ 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)/2 = the golden ratio) and OutlineTitlesMultiplierBlack (defaulting to unity). Subject to there being not more than OutlineTitlesMaxNum black ripples, the number of black ripples is chosen such that the total width of the ripples is at least OutlineTitlesTargetTotalDistanceProportionRadius radii. This parameter can be relevant if a title is something like “L”, as the top-right of the circle can then be more than 1 radius from the title.

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 OutlineTitlesSuppressForDecanterLabels suppresses 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

InlineTitles

◊ A more architectural form of decoration is activated by the booleans InlineTitles, InlineAbovetitles, InlineBelowtitles and InlineOvertitles. 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.

It would be difficult for the code to compute correctly how many contours are needed. So instead the code makes an estimate, which will typically be a great overestimate, and then caps that estimate at InlineTitlesMaxNumberContours/InlineAboveBelowOverMaxNumberContours. Setting these carefully reduces file size and complexity, which can help some printers.

InlineTitles (and variations) do not mix well with other decoration: the text can be illegibly cluttered. Self-restraint is beautiful.

Example of titles filled with text

FillTexts

◊ The booleans FillTitles, FillAbovetitles, FillBelowtitles and FillOvertitles 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 FillTextPlacename, and the angle is FillTextAnglePlacename.

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 (e.g. via PS2PDF.com), 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.) 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 PlaceNamesNumCopies to 0.

Page-level settings

Example of use of Rays

Rays

To connect circles with pseudo-electrical field lines, as in the example on the right, set Rays to true. Then from each glass setting there emanates RaysLinesPerGlass lines, the first being offset from vertical by RaysAngleOffset°. It is strongly recommended that RaysLinesPerGlass be an integer multiple of 4. Rays can be straight lines, or, as in the example, curves, this being controlled by the boolean RaysUseCurves. The lines or curves can be truncated at the margin, or at the edge of the paper: RaysLinesToPaperEdge. If this is true, it is even more important to ensure that printing is done at 100% with no scaling. Lines shorter than RaysCollidingCircleMinLength that collide with a circle (rather than meeting one of its rays) are suppressed. 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, then A could not connect to C. But these lines can, layout dependent, connect almost 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.)

Imperfections in the algorithm can cause crossovers or missed intersections, either of which can be aesthetically detrimental. Typically the problem can be fixed by tweaking RaysLinesPerGlass and RaysAngleOffset: for several PermittedPackingStyles vertical and horizontal lines seem to cause particular problems. There is a further ‘feature’ in the interaction of rays and SideBySideGlassesTastingNotes. For right handers, the rays rather dramatically spill over into the tasting-note section. For left handers, less dramatically, they don’t.

Example of watercounts

H2O

◊ Particularly at a tasting of a sweet fortified wine, drinking water is important. If WaterCounts is true, then there are 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.

The number of these boxes is usually at least WaterCountTarget, each of size WaterCountSize and separated by gaps of width WaterCountGap. These 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, WaterCountsOverrideShowEverySheet overrides the complexity in the next few sentences, putting watercounts on every sheet. But if WaterCountsOverrideShowEverySheet is false, the presence or absence is controlled, for the right handers, by the array of booleans WaterCountShowRight; and for the lefties by WaterCountShowLeft. Nota Bene: WaterCountShowRight and WaterCountShowLeft must be the same length as GlassesOnSheets. The default values of WaterCountShowRight and WaterCountShowLeft refer to PageOrderingNonDecanterLabelGlasses, which is implicitly assumed to specify sessions. Naturally enough, WaterCountShowRight defaults to true for the last page in each ‘session’, and WaterCountShowLeft for the first.

Headers and footers, and external links

◊ It is possible to define a header to title a tasting. Headers have texts HeaderLeftText, HeaderCenterText, and HeaderRightText, which are rendered left-aligned to the page, centered and right-aligned. These are shown in font HeaderFont, at size HeaderFontSize, with the baseline of the characters a distance HeaderBaselineFromPageTop from the top of the page (which should be inside the top margin). Likewise FooterLeftText, FooterCenterText, and FooterRightText are shown in FooterFont at size FooterFontSize, with the top of the footer a distance FooterTopFromPageBottom from page bottom (which should be inside the bottom margin). Using all of six of these text positions would be very cluttered. Also, to have different headers on glass sheets and tasting-note sheets set the text parameters to suitable code: {TypeOfPagesBeingRendered /TastingNotes eq {(Tasting-note header)} {(Glass-sheet header)} ifelse}.

Usually elegance is enhanced by having left and right headers of approximately equal length.

It might be that in the future somebody finds, somewhere on the web, a copy of the placemat PDF. There should be a route home, human- and machine-readable. This appears in two separate places, and it is more natural to start with the non-footer place. A PDF document can have an ‘outline’, usually shown in a sidebar, that summarises the document structure and provides internal navigation. The placemat software creates this automatically. At the end of the outline are relevant external links. The user can create these with the array ExternalLinks, which is an array of even length of strings (not arrays, nor compound strings, just plain simple strings): URL0, Descriptor0, URL1, Descriptor1, etc …. These URLs must start “http://”.

Links or other text can also appear in the header or footers of ‘back-of-house pages’, contained in parameters such as FooterCenterText-Backstage, the default value of which is computed from ExternalLinks. There are like parameters FooterLeftText-Backstage, FooterRightText-Backstage, HeaderCenterText-Backstage, HeaderLeftText-Backstage, HeaderRightText-Backstage, which substitute for the non--Backstage parameters on the pre-pour pages, decanting-note and vote-recorder pages.

Layout

There are several types of layout for the glasses, and which may be used is controlled by the array PermittedPackingStyles, which should contain the permitted styles in order of preference. The code chooses the style from those in PermittedPackingStyles, and chooses the parameters for that style, to maximise the permitted circle radius (whilst ignoring MaxRadius), draws being resolved by order. Below are some examples of the packing styles.

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

5 glasses, /PseudoHexagonal on /A4 /Portrait
/PseudoHexagonal on /A4, /Portrait
8 glasses, /PseudoHexagonal on /A4 /Portrait
/PseudoHexagonal on /A4, /Portrait
13 glasses, /PseudoHexagonal on /A3 /Landscape
/PseudoHexagonal on /A3, /Landscape
16 glasses, /PseudoHexagonal on /A3 /Landscape
/PseudoHexagonal on /A3, /Landscape
5 glasses, /PseudoHexagonal on /USL=8½″×11″ /Portrait
/PseudoHexagonal on /USL, /Portrait
9 glasses, /PseudoHexagonal on /USLegal=8½″×14″ /Landscape
/PseudoHexagonal on /USLegal, /Landscape

/SquareGrid: circles lie in a square grid, in which the distance to vertical neighbours equals that to horizontal neighbours.

5 glasses, /SquareGrid on /A4 /Portrait
/SquareGrid on /A4, /Portrait
15 glasses, /SquareGrid on /A3 /Landscape
/SquareGrid on /A3, /Landscape

/RectangularDislocation or /RectangularDislocationV: circles lie in a rectangular grid, filling the x and y space, so vertical neighbours not necessarily being the same distance away as horizontal neighbours. There can also be a horizontal “dislocation”, above and below which there is regular grid, but the meeting of which is offset by half a circle. For /RectangularDislocation, the upper block has one column fewer than the lower block. For /RectangularDislocationV this is reversed, the lower block being narrower.

4 glasses, /RectangularDislocation on /A4 /Portrait
/RectangularDislocation on /A4, /Portrait
5 glasses, /RectangularDislocation on /A4 /Portrait
/RectangularDislocation on /A4, /Portrait
6 glasses, /RectangularDislocation on /A4 /Portrait
/RectangularDislocation on /A4, /Portrait
10 glasses, /RectangularDislocation on /A3 /Landscape
/RectangularDislocation on /A3, /Landscape
11 glasses, /RectangularDislocation on /A3 /Landscape
/RectangularDislocation on /A3, /Landscape
5 glasses, /RectangularDislocation on /USL=8½″×11″ /Portrait
/RectangularDislocation on /USL, /Portrait

/Gaia or /GaiaElegant: inspired by a simpler design seen by the author in Vila Nova de Gaia, and leaving room for a substantial corporate message in the centre of the page—particularly useful to those skilled in Adobe Illustrator.) /Gaia looks less handsome in portrait pages: the default /GaiaElegant applies only when the gap between the vertical columns of circles is at least 1½ circles across, and adjacent glasses are equally spaced. If /GaiaElegant, and there isn’t an elegant /Gaia, treated as /PseudoHexagonal.

6 glasses, /Gaia on /A4 /Landscape
/Gaia on /A4, /Landscape
6 glasses, /Gaia on /USL=8½″×11″ /Landscape
/Gaia on /USL, /Landscape

/Irregular, /IrregularMirror, /IrregularPortrait, /IrregularLandscape, /IrregularMirrorPortrait or /IrregularMirrorLandscape: each of these is a special pattern for exactly five glasses. The …Mirror variants are a mirror image of the non-Mirror variants; the …Landscape variants are ignored unless Orientation is /Landscape; and likewise the …Portrait variants are ignored unless Orientation is /Portrait.

5 glasses, /Irregular on /A4 /Landscape
/Irregular on /A4, /Landscape
5 glasses, /IrregularMirror on /A4 /Landscape
/IrregularMirror on /A4, /Landscape

/TwoRowsOrTwoColumns: like /PseudoHexagonal, but with either the rows or the columns closer, and therefore of maximum number 2. Relevant when the number of glasses is very small or the non-margin page extremely non-square. If unusable, ignored.

2 glasses, /TwoRowsOrTwoColumns on /A4 /Landscape
/TwoRowsOrTwoColumns on /A4, /Landscape

The default value of PermittedPackingStyles tests each of /RectangularDislocation, /PseudoHexagonal, /SquareGrid, /TwoRowsOrTwoColumns, /IrregularLandscape, and, redundantly, /GaiaElegant. It then chooses whichever gives the largest radius. Obviously if an elegant /Gaia is wanted one should delete all but /GaiaElegant.

Further control of the layout is possible with RowsMinNum and RowsMaxNum, as the number of rows of glasses is restricted to lie between these two. (It is assumed that 1 ≤ RowsMinNum ≤ RowsMaxNum and RowsMinNum ≤ the number of glasses. If not so, the limits are adjusted sensibly.) RowsMinNum and RowsMaxNum are ignored for the /TwoRowsOrTwoColumns, /Irregular and /IrregularMirror packing styles.

Additionally, PermittedPackingStyles may contain an array. Such an array must contain only sub-arrays 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. E.g., the following was made with the array [ [0 2] [2 2] [4 2] [6 2] [0 1] [3 1] [6 1] [0 0] [3 0] [6 0] ].

[ [0 2] [2 2] [4 2] [6 2] [0 1] [3 1] [6 1] [0 0] [3 0] [6 0] ]

As a convenience, there is also the parameter PackingDirectionVertical which can be set to /TopToBottom or /BottomToTop, and the parameter PackingDirectionHorizontal with possible values /LeftToRight or /RightToLeft. The same effect can be made, less easily, with GlassesOnSheets. PackingDirectionVertical is typically changed to from the default value to /BottomToTop when a tasting is in flights, starting with the front row; but the tasting-note sheets run in the opposite direction, top-to-bottom.

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 PageOrderingNonDecanterLabelGlasses, is shrunk to the smallest of that session’s radii. Alternatively, ShrinkRadii may be an array of integers of the same length as GlassesOnSheets, pages being grouped if they have matching values of the array (so the default value of ShrinkRadii, “/ToSmallestSamePageOrdering”, is equivalent to “{PageOrderingNonDecanterLabelGlasses}”).

The names appear at the top if NamesAtTop is true, and at the bottom if it is false. The names are rendered in the font NamesFont, at a size equal to the size of the Circlearrays (or rather, at the size at which the the Circlearrays would appear if CircletextsMinCopies were ignored), 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.

The making of decanter labels is controlled by the integer DecanterLabelsNumCopies. If this is ≥1, then the sizes of the Titles etc are reduced so that they fit within a business card of size DecanterLabelMaxSmallerDimension × DecanterLabelMaxLargerDimension, as described in the section Type sizes. The decanter label pages include instructions along the lines of “Cut; paste to the back of a business card; allow to dry; punch holes; hang on decanter; fill decanter; wait; drink.” 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.

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.

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. Technical aside: whilst these tasting-note pages are being rendered the program sets TypeOfPagesBeingRendered to /TastingNotes. Many parameters may be set to code that checks this flag.

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.

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 PlaceNamesNumCopies parameter, being an integer containing the number of such pages for each person. The pages are formatted to match the Titles: example on right. Generally PlaceNamesNumCopies is deffed to 0 or 1, though might be larger for tastings over multiple sessions.

Pages require three folds. First, fold in half; then fold and press hard along the faint dashed lines (so 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 PlaceNameSheetNum is the number of the place-name page. So 0 ≤ PlaceNameSheetNum < PlaceNamesNumCopies.

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 = ½″). The Names are set in font PlaceNamesFont (defaulting to TitlesFont), 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 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.

Example of a pre-pour sheet

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. Typically this necessitates many trays holding fourteen glasses, and, to avoid confusion, these trays should be labelled: a sheet of paper on each tray. These are on paper type PrePourPaperType defaulting to TastingNotesPaperType, with orientation PrePourOrientation defaulting to /Landscape.

The number of copies of these pre-pour sheets is PrePourNumCopies, by default 0. If pre-pour sheets are wanted this should be set to a higher value: usually /PrePourNumCopies 1 def is sufficient. If trays are too small to hold 14 glasses then 2 might be needed. Alternatively, if glasses are to be transferred from one place to another, 2 or even 3 might be required.

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.

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

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.)

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.

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: these might be changes for Kernning or different languages.

Also ∃ obvious parameters VoteRecorderPaperType and VoteRecorderOrientation; and VoteRecorderTitlesFontSizeMax and VoteRecorderSubtitleFontSizeProportionTitles.

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 is analogous to VoteRecorderSubtitleFontSizeProportionTitles.

Also ∃ obvious parameters DecantingNotesPaperType and DecantingNotesOrientation, and DecantingNotesTitlesFontSizeMax and DecantingNotesSubtitleFontSizeProportionTitles.

Cork Display

Bottles having been decanted, corks should be put on display. This is activated by /CorkDisplay true 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.

Sticky labels

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

So go back in time, to 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, to avoid the potential confusion. 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. Specifying these largely 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 Names.

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 and /Portrait. If StickyLabelsOrientationAutomatic then StickyLabelsOrientation is ignored, 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 NonDecanterLabelGlassesNumCopies.

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 WaterCounts 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 WaterCountShowRight and WaterCountShowLeft. These parameters are described in the section on WaterCounts.

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 ≤ 8, GlassesOnSheets is [ [0 1 2 … n–1] ], so putting all the glasses on a single page. If n > 8, then there are ⌈n÷6⌉ pages, the glasses being distributed as evenly as possible, so each page holding ≤ 6.

The parameter GlassesOnTastingNotePages works the same way, controlling the tasting-note pages. By default it is set to GlassesOnSheets, which is sensible with a PaperType of /A4 or /USL. Having more than six-ish wines per /A4 or /USL page of tasting notes can leave too little space for writing, so, if the glasses are on /A3 or /USL2, the default value of GlassesOnTastingNotePages will most likely need to be over-ridden.

Type sizes

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

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

  2. Second, these 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 ration constraint is applied: 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.

  3. 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.

  4. 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. 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 match, the arrays are considered jointly. Typically, arrays in the same or similar fonts should be considered together; those in very different fonts should not.

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 for that circle such that it does. The number of copies is also bounded above by CircletextsMaxCopies; and the minimum separation (measured in space-widths) between the items of the sub-arrays of Circlearrays is CircletextsMinNumSpacesBetween.

BackgroundTextsGlasses

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. The strings should be short: one or two characters is recommended, and are shown in font BackgroundTextsGlassesFont.

The text is shown in the centre of the page, 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.

The boolean parameter BackgroundTextsTastingNotes also shows these on the tasting-note pages. For each such page, if every glass on the page appears on a glasses page with the same BackgroundTextsGlassesTexts, then that item is shown on the page. For pre-pour pages, which are naturally associated with a glasses page, the boolean BackgroundTextsPrePour causes the BackgroundTextsGlassesTexts to appear.

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.

Examples can be found at www.jdawiseman.com/papers/placemat/flightseparations.pdf, the header showing 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.

Page order

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

Array nameLengthDefault
entries
Default explained
PageOrderingNonDecanterLabelGlassessame as GlassesOnSheets0 Sorted into order by the algorithm: 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, cork display and pre-pour pages.
PageOrderingTastingNotePagessame as GlassesOnTastingNotePages0
PageOrderingVoteRecordersame as GlassesClusteredOnVoteRecorders0
PageOrderingDecantingNotessame as GlassesClusteredOnDecantingNotes0
PageOrderingCorkDisplaysame as GlassesClusteredOnCorkDisplay0
PageOrderingPrePourPagessame as GlassesOnSheets0
PageOrderingPlaceNamesPlaceNamesNumCopies100Separated from 0s 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).

For a tasting over multiple sessions it can be helpful to have a page order more complicated than the default. For example, imagine a tasting split over afternoon and evening sessions, each session having three glasses pages and two tasting-note pages per person (example of such a tasting, inspiring but pre-dating these page-order controls). Managing such a tasting will be enough work and spare on the day: simplifying the task of table setting, thus reducing the likelihood of error, is worthwhile. One might set:

/PageOrderingNonDecanterLabelGlasses [ 10 10 10  20 20 20 ] def
/PageOrderingTastingNotePages [ 10 10  20 20 ] def
/PageOrderingVoteRecorder  [ 10 ] def
/PageOrderingDecantingNotes  [ 10 20 ] def
/PageOrderingCorkDisplay  [ 10 20 ] def
/PageOrderingPrePourPages [ 10 10 10  20 20 20 ] def
/PageOrderingPlaceNames [ 1 2 ] def
/PageOrderingDecanterLabels [ 0 0 0  0 0 0 ] def
/PageOrderingStickyLabels [ 65535 65535 65535  65535 65535 65535 ] def

First out of the printer are the decanter labels, which can be cut and pasted to business cards in advance (integer: 0). Next are place-name pages for the two sessions, collated, to be folded before the day (1 and 2). Next are the glasses and tasting-note pages for the afternoon, with each person’s five pages together (10), followed by the vote recorder for the day (10), and the decanting-notes and pre-pour sheets for that session (10). Then the glasses and tasting-note pages for the evening, with each person’s five pages together (20) followed by decanting-notes, cork-display and pre-pour sheets (20). Finally the bottle labels at the end (65535) for ease of paper-changing in the printer.

The values in PageOrderingNonDecanterLabelGlasses are used by ShrinkRadii as they are presumed to define sessions. The values in PageOrderingNonDecanterLabelGlasses are also used to determine the default values of WaterCountShowRight and WaterCountShowLeft.

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 MirrorPagesNonDecanterLabelGlasses, MirrorPagesTastingNotePages, MirrorPagesVoteRecorder, MirrorPagesDecantingNotes, 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, PlaceNamesNumCopies, PrePourNumCopies, DecantingNotesNumCopies. Analogously, there are parameters NonDecanterLabelGlassesNumCopies and TastingNotePagesNumCopies, both defaulting to 1. For the vote-recorder sheets the parameter is a boolean, VoteRecorders.

TastingNotePagesNumCopies 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 /NonDecanterLabelGlassesNumCopies {SheetNum 4 ge {0} {1} ifelse} def. Also, the values of GlassesOnTastingNotePages and GlassesClusteredOnDecantingNotes might need changing.

Code within parameters

It is possible to vary the parameters by sheet or by glass, as described at www.jdawiseman.com/papers/placemat/placemat-code.html. The same page also describes how to add code inside compound strings, and gives examples of why this might be done.

Page typeTypeOfPagesBeingRendered
(ThisPageDecanterLabels)
URL ending of first
page of this type
Glasses/Glasses
false
…#Glasses_0
Tasting notes/TastingNotes…#TastingNotes_0
Vote recorders/VoteRecorder…#VoteRecorder_0
Decanting notes/DecantingNotes…#DecantingNotes_0
Cork display/CorkDisplay…#CorkDisplay_0
Pre-pour pages/PrePour…#PrePour_0
Place names/PlaceName…#PlaceName_0
Decanter labels/Glasses
true
…#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.

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 www.jdawiseman.com. 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.

Comment

Avatar of jdaw1
Avatar of jdaw1

The author welcomes comment on this program, which may be by email or in the appropriate threads on The Port Forum, For The Love Of Port, CellarTracker, or WineBerserkers.com. The author’s most-often used avatars are to the sides of this paragraph, and there is also an svg gravatar, each of these being a stylised placemat.

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 (www.jdawiseman.com/papers/​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—what should I wear, and should I bring cheese?

Julian D. A. Wiseman


Recent changes

November 2011

October 2011

September 2011

August 2011

July 2011

June 2011

May 2011


Main index Top About author