Flynn, Peter. “Leveraging markup to process narrative recipes.” Presented at Balisage: The Markup Conference 2024, Washington, DC, July 29 - August 2, 2024. In Proceedings of Balisage: The Markup Conference 2024. Balisage Series on Markup Technologies, vol. 29 (2024). https://doi.org/10.4242/BalisageVol29.Flynn01.
Balisage: The Markup Conference 2024 July 29 - August 2, 2024
Balisage Paper: Leveraging markup to process narrative recipes
Peter Flynn
Peter Flynn managed the Academic and Collaborative
Technologies Group in IT Services at University College
Cork, Ireland until his retirement in 2018. He trained at
the London College of Printing and did his MA in
computerized planning systems at Central London Polytechnic
(now the University of Westminster). He worked in the UK for
the Printing and Publishing Industry Training Board as a DP
Manager and for United Information Services of Kansas as IT
consultant before joining UCC as Project Manager for
academic and research computing. In 1990 he installed
Ireland’s first Web server and concentrated on academic and
research publishing support. He has been Secretary of the
TeX Users Group, Deputy Director for Ireland of EARN, and a
member both of the IETF Working Group on HTML and of the W3C
XML SIG; and he has published books on HTML, SGML/XML, and LaTeX. Peter
also runs the markup and typesetting consultancy Silmaril,
and is editor of the XML FAQ as well as an irregular
contributor to conferences and journals in electronic
publishing, markup, and Humanities computing, and has been a
regular speaker and session chair at the XML Summer School
in Oxford. He completed a PhD in 2015 on User
Interfaces to Structured Documents with the Human
Factors Research Group in Applied Psychology in UCC. He
maintains a fairly random semi-technical blog at https://blogs.silmaril.ie/peter.
In two earlier papers I described the implementation of
‘℞’
(pronounced /ˈɹɛs.ɪ.pē/ ‘reSEEpay’), an
XML/XSLT system for checking and reproducing structured
cookery recipes. Since then, work has been ongoing:
a) to refine the categories used in the metadata; b) to improve the implementation in CSS; and c) to extend the markup to narrative recipes encoded
in other schemas. This paper describes the third of these, with
a working implementation in TEI P5. The categorization task is
out of scope for markup conferences, and is best discussed
over a good dinner (the CSS is in scope but barely addressed
here).
When is a narrative not a story? When it’s a recipe!
Publishing recipes with distinct sections enumerating the
ingredients and preparation steps is a 19th century invention.
Historically, recipes were just narrative prose. Can we
leverage markup and style to present narrative recipes in a
modern style?
Note: Acknowledgements
My thanks go again to all my friends and colleagues in
kitchens, laboratories, and libraries, especially on BlueSky and
Discord, for their help and suggestions, with a special mention
to Beatrix Färber, Editor of the CELT Project and culinary historian
Regina Sexton of the Department of
Adult Continuing Education, UCC
Structure and narrative
Modern recipes, in print as on the web, contain two major
components: the List of Ingredients and the Method. These were
discussed extensively in the earlier papers on this topic
[Flynn 2020][Flynn 2021]. They exist in one
form or another in every layout, design, schema, and handwritten
note for modern recipes, and a recipe would be considered
virtually useless without both of them.
Figure 1: Simple recipes with list of ingredients and method in the
modern style
<ol>
<li>Mix ingredients</li>
<li>Boil to 235°F</li>
<li>Stir to cool</li>
<li>Pour into dish</li>
</ol>
Mix ingredients
Boil to 235°F
Stir to cool
Pour into dish
Recipe for fudge used as a markup example in
Formatting Information [Flynn 2024], with MS for a family recipe.
Before the expansion of the cookery book from niche
publication to mass market during the second half of the 19th
century, recipes were narrative [Scully 2002][Nonymous 2018]. The Roman cookery
writer Apicius used a narrative form (from what we can judge),
and this became the convention for later writers. The arrival of
printing just made reproduction easier, so Marx Rumpolt, Hannah
Glasse, and Marie-Antonin Carême all followed the tradition of
writing in narrative form [Figure 2].
The early cookery authors like Rumpolt were writing for
professional cooks with large kitchens and kitchen staff in
wealthy houses: the cooks were assumed to be able to read, or
have access to someone who could, and who knew enough to be able
to decide on materials and quantities from experience. Like the
modern Répertoire de la Cuisine for
French cooking [Gringoire 1914], recipes
were terse because that was all that was needed.
Hannah Glasse was an exception: she explicitly
addressed the non-professional cook, and it is worth quoting her
intent:
If I have not wrote in the high polite ſtyle, I
hope ſhall be forgiven; for my intention is to inſtruct the
lower ſort and therefore muſt treat them in their own way.
For example: when I bid them lard a fowl, if I ſhould bid
them lard with large lardoons, they would not know what I
meant; but when I ſay they muſt lard with little pieces of
bacon, they know what I mean. So, in many other things in
Cookery, the great cooks have ſuch a high way of expreſſing
themſelves, that the poor girls are at a loſs to know what
they mean:[…][Glasse 1753]
This all changed rapidly after the middle of the 19th
century: Eliza Acton (1845) is usually credited with inventing
the separation of Ingredients from Method, and her lead was
followed by others including Isabella Beeton (1861) in England
and Fanny Farmer (1896) in the USA; although Mrs Beeton
notoriously gave ingredients but no quantities.
The ℞ software was written with the modern structured recipe
in mind, but an encounter with a German colleague with a copy of
Rumpolt’s recipes from the 16th century resulted in a decision
to try the same techniques with narrative recipes.
Implementation
The recipe marked No.5 in [Figure 2] from
the book by Rumpolt [Rumpolt 1581] was
chosen because it seemed to have the qualities needed to test
the system: a narrative for a simple dish, containing at least
one mention of all the ingredients, in order, with details of
what to do with each. No quantities are given apart from the
bread, but they can be deduced.
The original was scanned and a transcription composed (OCR
was attempted with tesseract but was
not successful). After proofing, the XML encoding was applied in
TEI P5 according to the tei_all.dtd to
avoid the need for generating a custom schema at this stage
[Figure 3] ([section “Results and conclusions”]).
Figure 3: Recipe encoded in TEI (before ℞ markup)
<!DOCTYPE list SYSTEM "/dtds/tei/p5/tei_all.dtd">
<list>
<label>Gebratene Leber.</label>
<item>
<list rend="Method">
<item>Nim̄ die Leber</item>
<item>vnnd quell ſie in heiſſem Waſſer</item>
<item>buꜩ ſie fein ſauber auß</item>
<item>vnd ſteck ſie an ein holꜩern Spieß</item>
<item>ſampt dem Ma‧gen</item>
<item>leg ſie auff ein Roßt</item>
<item>vnd brat ſie geſchwindt hinweg.</item>
</list>
<list rend="Serving">
<item>Wenn du ſie wilt anrichten</item>
<item>ſo nim̄ gebehte Schnitten drey oder vier
in die Schüſſel</item>
<item>geuß darein ein gute Hennenbrüh</item>
<item>mit Petterſilgen Wurꜩel
geſotten.</item>
</list>
<list rend="Comment">
<item>Und wenn du haſt angerichtet</item>
<item>ſo leg ſie auff die Brüh</item>
<item>ſo iſt es gut vnd zierlich.</item>
</list>
</item>
</list>
Grilled Liver.
Take the liver
and rinse it in hot water
cleaning it well out
and stick it on a wooden skewer
along with the stomach¹
lay it on the griddle
and quickly grill it off.
When you are ready to dress it
lay 3–4 slices of toasted bread on the plate
pour over a good chicken stock
steeped with root-parsley.
And when you have everything prepared
lay it [the liver] on the stock
so that it is good and dainty.
¹ Caul?
The categorization and measurement metadata provided by ℞ is
carried exclusively in attributes. This means that they can be
applied to any schema or DTD in a modification layer or shim
without the need to rewrite any of the element type structure.
It does, however, mean that one otherwise unused element type
valid in mixed content is available for hijacking to carry the
ingredient metadata, and possibly another to carry the method
metadata, although in the case of narrative recipes, the whole
text is the method. Alternatively, if no
unused element types are available, some additional attribute
could be used to flag the instances when ingredient or recipe
metadata is being carried on an element type in existing
use.
In the case of TEI, for the purposes of testing, the
material element type was chosen for the
ingredients. The attributes defined in ℞ were added as a shim to
the tei_all.dtd using the same enumerated attribute
token list files of categories used for the development system
[Flynn 2020] and the recipe was completed as
a TEI P5 document with a TEI Header [Figure 4].
Figure 4: Recipe encoded in TEI with ingredients marked in the
material element type
<!DOCTYPE list SYSTEM "recipe-tei.dtd">
<list>
<label serves="4" n="5">Gebratene Leber.</label>
<item>
<list rend="Method">
<item>Nim̄ die <material quantity="500" unit="g" meat="veal"
part="liver" xml:id="liver">Leber</material></item>
<item>vnnd quell ſie in <material quantity="1" unit="ℓ"
basic="water" treatment="hand-hot"
xml:id="water">heiſſem Waſſer</material></item>
<item>buꜩ ſie fein ſauber auß</item>
<item>vnd ſteck ſie an ein holꜩern Spieß</item>
<item>ſampt dem <material quantity="1" meat="veal" part="stomach"
xml:id="magen">Ma‧gen</material></item>
<item>leg ſie auff ein Roßt</item>
<item>vnd brat ſie geſchwindt hinweg.</item>
</list>
<list rend="Serving">
<item>Wenn du ſie wilt anrichten</item>
<item>ſo nim̄ <material quantity="3–4" unit="slice" quality="white"
basic="bread" treatment="warmed" xml:id="bread">gebehte
Schnitten drey oder vier</material> in die Schüſſel</item>
<item>geuß darein ein gute <material quantity="150" unit="ml"
meat="chicken" part="stock"
xml:id="stock">Hennenbrüh</material></item>
<item>mit <material quantity="25" unit="g"
vegetable="root-parsley" treatment="grated and cooked in
the stock" xml:id="parsley">Petterſilgen Wurꜩel</material>
geſotten.</item>
</list>
<list rend="Comment">
<item>Und wenn du haſt angerichtet</item>
<item>ſo leg ſie auff die Brüh</item>
<item>ſo iſt es gut vnd zierlich.</item>
</list>
</item>
</list>
The TEI Header has been omitted from this fragment
for space reasons.
<!ENTITY % tei SYSTEM "/dtds/tei/p5/tei_all.dtd">
<!ATTLIST material
quantity CDATA #IMPLIED
unit (%units-list;OMIT) #IMPLIED
unit-weight CDATA #IMPLIED
container (%package-list;OMIT) #IMPLIED
size (%sizes-list;OMIT) #IMPLIED
colour CDATA #IMPLIED
quality CDATA #IMPLIED
nature (%nature-list;OMIT) #IMPLIED
meat (%meat-list;OMIT) #IMPLIED
seafood (%seafood-list;OMIT) #IMPLIED
part (%part-list;OMIT) #IMPLIED
dairy (%dairy-list;OMIT) #IMPLIED
fruit (%fruit-list;OMIT) #IMPLIED
alcohol (%alcohol-list;OMIT) #IMPLIED
herb (%herb-list;OMIT) #IMPLIED
vegetable (%vegetable-list;OMIT) #IMPLIED
bean (%beans-list;OMIT) #IMPLIED
nut (%nuts-list;OMIT) #IMPLIED
seed (%seeds-list;OMIT) #IMPLIED
pasta (%pasta-list;OMIT) #IMPLIED
spice (%spice-list;OMIT) #IMPLIED
basic (%basic-list;OMIT) #IMPLIED
sprinkles (%sprinkles-list;OMIT) #IMPLIED
form (%form-list;OMIT) #IMPLIED
prep CDATA #IMPLIED
treatment CDATA #IMPLIED
note (1|2|3) #IMPLIED
comment CDATA #IMPLIED
symbol CDATA #IMPLIED
alt CDATA #IMPLIED
calories CDATA #IMPLIED
status (optional|required) #IMPLIED>
%tei;
The units, weights, and all ingredient
categorizations for ℞ are held in token list
files so that users can make local changes.
The resulting TEI document (with some site-associated
decoration) was enabled in the author’s recipe site and
transformed to the same HTML format as all the other recipes by
adapting the existing XSLT to handle the TEI element types. This
consisted largely of adding the TEI element type names as unions
to the existing XPaths in the match attributes of
the relevant XSL-templates. The resulting web page is online at
https://xml.silmaril.ie/recipes/grilled-liver.html
and the source (which serves as the print version, using CSS as in
[Flynn 2021]) is at https://xml.silmaril.ie/recipes/grilled-liver.xml [Figure 5].
Results and conclusions
This part of the ℞ development has been another learning
curve:
Encoding
The process of encoding was relatively
straightforward, much of which is due to the accessibility
and adaptability of the TEI, its documentation, and the
copious provision of element types. There is always an
element of chance in selecting element types for this kind
of temporary ‘abuse’, in that
someone, somewhere, will be depending absolutely on the
unmodified existence of the very one that you have chosen
to carry your metadata. In my experience, however, it is a
rare TEI document that, from the many modules available,
uses all 32,768 element types available in mixed
content.
Implementation
The adaptation of the existing web site XSLT to
process a TEI document instead of the homebrew recipe
schema used for development was also unproblematic, as
both follow the traditional structured document concepts
of heading—metadata—sectioning found in other systems
[Flynn 2017].
Alternative approaches
The TEI Consortium provides ODD [TEI Consortium 2004], a language for modifying the TEI
schema (or indeed any schema), so it could virtually be
rewritten to create a specialist vocabulary for recipes.
This would probably only make sense for a project encoding
large numbers of recipes such as the entire Rumpolt book,
and it would perhaps then be a possible scenario for
commercial publication. The author is interested to hear
of TEI encodings of recipes as part of other
ptojects.
Still to do
Readers may have noticed that the formal list of
ingredients is missing from the print version of the test
recipe in [Figure 5] (R). The
fully-formatted page in [Figure 5]
(L) is generated by XSLT, so performing a collation of the
ingredients to populate the list programmatically is
possible. But the print view is (by design) raw XML with
CSS. Performing the CSS collation has been possible for
recipes using the regular DTD, as they occur inline to the
normal progression of events. Doing it out-of-line for a
narrative recipe needs further work.
The current experiment was a proof-of-concept, and has been
mainly successful. If anyone has recipes encoded in, for
example, DocBook or JATS, I would be interested to hear from
them.