% language=us runpath=texruns:manuals/cld \startcomponent cld-somemoreexamples \environment cld-environment \usemodule[morse] \startchapter[title=Some more examples] \startsection[title=Appetizer] Before we give some more examples, we will have a look at the way the title page is made. This way you get an idea what more is coming. \typefile {cld-mkiv-simple-titlepage.cld} This does not look that bad, does it? Of course in pure \TEX\ code it looks mostly the same but loops and calculations feel a bit more natural in \LUA\ then in \TEX. The result is shown in \in {figure} [fig:cover]. The actual cover page was derived from this. \startplacefigure[location=here,reference=fig:cover,title={The simplified cover page.}] \doiffileexistselse {cld-mkiv-simple-titlepage.pdf} { \externalfigure [cld-mkiv-simple-titlepage.pdf] [height=.5\textheight] } { \scale [height=.5\textheight] {\cldprocessfile{cld-mkiv-simple-titlepage.cld}} } \stopplacefigure \stopsection \startsection[title=A few examples] As it makes most sense to use the \LUA\ interface for generated text, here is another example with a loop: \startbuffer context.startitemize { "a", "packed", "two" } for i=1,10 do context.startitem() context("this is item %i",i) context.stopitem() end context.stopitemize() \stopbuffer \typebuffer \ctxluabuffer Just as you can mix \TEX\ with \XML\ and \METAPOST, you can define bits and pieces of a document in \LUA. Tables are good candidates: \startbuffer local one = { align = "middle", style = "type", } local two = { align = "middle", style = "type", background = "color", backgroundcolor = "darkblue", foregroundcolor = "white", } local random = math.random context.bTABLE { framecolor = "darkblue" } for i=1,10 do context.bTR() for i=1,20 do local r = random(99) context.bTD(r < 50 and one or two) context("%2i",r) context.eTD() end context.eTR() end context.eTABLE() \stopbuffer \typebuffer \placetable[top][tab:random]{A table generated by \LUA.}{\ctxluabuffer} Here we see a function call to \type {context} in the most indented line. The first argument is a format that makes sure that we get two digits and the random number is substituted into this format. The result is shown in \in{table}[tab:random]. The line correction is ignored when we use this table as a float, otherwise it assures proper vertical spacing around the table. Watch how we define the tables \type {one} and \type {two} beforehand. This saves 198 redundant table constructions. Not all code will look as simple as this. Consider the following: \starttyping context.placefigure( "caption", function() context.externalfigure( { "cow.pdf" } ) end ) \stoptyping Here we pass an argument wrapped in a function. If we would not do that, the external figure would end up wrong, as arguments to functions are evaluated before the function that gets them (we already showed some alternative approaches in previous chapters). A function argument is treated as special and in this case the external figure ends up right. Here is another example: \startbuffer context.placefigure("Two cows!",function() context.bTABLE() context.bTR() context.bTD() context.externalfigure( { "cow.pdf" }, { width = "3cm", height = "3cm" } ) context.eTD() context.bTD { align = "{lohi,middle}" } context("and") context.eTD() context.bTD() context.externalfigure( { "cow.pdf" }, { width = "4cm", height = "3cm" } ) context.eTD() context.eTR() context.eTABLE() end) \stopbuffer \typebuffer In this case the figure is not an argument so it gets flushed sequentially with the rest. \ctxluabuffer \stopsection \startsection[title=Styles] Say that you want to typeset a word in a bold font. You can do that this way: \starttyping context("This is ") context.bold("important") context("!") \stoptyping Now imagine that you want this important word to be in red too. As we have a nested command, we end up with a nested call: \starttyping context("This is ") context.bold(function() context.color( { "red" }, "important") end) context("!") \stoptyping or \starttyping context("This is ") context.bold(context.delayed.color( { "red" }, "important")) context("!") \stoptyping In that case it's good to know that there is a command that combines both features: \starttyping context("This is ") context.style( { style = "bold", color = "red" }, "important") context("!") \stoptyping But that is still not convenient when we have to do that often. So, you can wrap the style switch in a function. \starttyping local function mycommands.important(str) context.style( { style = "bold", color = "red" }, str ) end context("This is ") mycommands.important( "important") context(", and ") mycommands.important( "this") context(" too !") \stoptyping Or you can setup a named style: \starttyping context.setupstyle( { "important" }, { style = "bold", color = "red" } ) context("This is ") context.style( { "important" }, "important") context(", and ") context.style( { "important" }, "this") context(" too !") \stoptyping Or even define one: \starttyping context.definestyle( { "important" }, { style = "bold", color = "red" } ) context("This is ") context.important("important") context(", and ") context.important("this") context(" too !") \stoptyping This last solution is especially handy for more complex cases: \startbuffer context.definestyle( { "important" }, { style = "bold", color = "red" } ) context("This is ") context.startimportant() context.inframed("important") context.stopimportant() context(", and ") context.important("this") context(" too !") \stopbuffer \typebuffer \ctxluabuffer \stopsection \startsection[title=A complete example] One day my 6 year old niece Lorien was at the office and wanted to know what I was doing. As I knew she was practicing arithmetic at school I wrote a quick and dirty script to generate sheets with exercises. The most impressive part was that the answers were included. It was a rather braindead bit of \LUA, written in a few minutes, but the weeks after I ended up running it a few more times, for her and her friends, every time a bit more difficult and also using different arithmetic. It was that script that made me decide to extend the basic cld manual into this more extensive document. We generate three columns of exercises. Each exercise is a row in a table. The last argument to the function determines if answers are shown. \starttyping local random = math.random local function ForLorien(n,maxa,maxb,answers) context.startcolumns { n = 3 } context.starttabulate { "|r|c|r|c|r|" } for i=1,n do local sign = random(0,1) > 0.5 local a, b = random(1,maxa or 99), random(1,max or maxb or 99) if b > a and not sign then a, b = b, a end context.NC() context(a) context.NC() context.mathematics(sign and "+" or "-") context.NC() context(b) context.NC() context("=") context.NC() context(answers and (sign and a+b or a-b)) context.NC() context.NR() end context.stoptabulate() context.stopcolumns() context.page() end \stoptyping This is a typical example of where it's more convenient to write the code in \LUA\ that in \TEX's macro language. As a consequence setting up the page also happens in \LUA: \starttyping context.setupbodyfont { "palatino", "14pt" } context.setuplayout { backspace = "2cm", topspace = "2cm", header = "1cm", footer = "0cm", height = "middle", width = "middle", } \stoptyping This leave us to generate the document. There is a pitfall here: we need to use the same random number for the exercises and the answers, so we freeze and defrost it. Functions in the \type {commands} namespace implement functionality that is used at the \TEX\ end but better can be done in \LUA\ than in \TEX\ macro code. Of course these functions can also be used at the \LUA\ end. \starttyping context.starttext() local n = 120 commands.freezerandomseed() ForLorien(n,10,10) ForLorien(n,20,20) ForLorien(n,30,30) ForLorien(n,40,40) ForLorien(n,50,50) commands.defrostrandomseed() ForLorien(n,10,10,true) ForLorien(n,20,20,true) ForLorien(n,30,30,true) ForLorien(n,40,40,true) ForLorien(n,50,50,true) context.stoptext() \stoptyping \ctxlua{document.checkcldresource("cld-005.cld")} \placefigure [here] [fig:lorien] {Lorien's challenge.} {\startcombination {\externalfigure[cld-005.pdf][page=1,width=.45\textwidth,frame=on]} {exercises} {\externalfigure[cld-005.pdf][page=6,width=.45\textwidth,frame=on]} {answers} \stopcombination} A few pages of the result are shown in \in {figure} [fig:lorien]. In the \CONTEXT\ distribution a more advanced version can be found in \type {s-edu-01.cld} as I was also asked to generate multiplication and table exercises. In the process I had to make sure that there were no duplicates on a page as she complained that was not good. There a set of sheets is generated with: \starttyping moduledata.educational.arithematic.generate { name = "Bram Otten", fontsize = "12pt", columns = 2, run = { { method = "bin_add_and_subtract", maxa = 8, maxb = 8 }, { method = "bin_add_and_subtract", maxa = 16, maxb = 16 }, { method = "bin_add_and_subtract", maxa = 32, maxb = 32 }, { method = "bin_add_and_subtract", maxa = 64, maxb = 64 }, { method = "bin_add_and_subtract", maxa = 128, maxb = 128 }, }, } \stoptyping \stopsection \startsection[title=Interfacing] The fact that we can define functionality using \LUA\ code does not mean that we should abandon the \TEX\ interface. As an example of this we use a relatively simple module for typesetting morse code.\footnote {The real module is a bit larger and can format verbose morse.} First we create a proper namespace: \starttyping moduledata.morse = moduledata.morse or { } local morse = moduledata.morse \stoptyping We will use a few helpers and create shortcuts for them. The first helper loops over each \UTF\ character in a string. The other two helpers map a character onto an uppercase (because morse only deals with uppercase) or onto an similar shaped character (because morse only has a limited character set). \starttyping local utfcharacters = string.utfcharacters local ucchars, shchars = characters.ucchars, characters.shchars \stoptyping The morse codes are stored in a table. \starttyping local codes = { ["A"] = "·—", ["B"] = "—···", ["C"] = "—·—·", ["D"] = "—··", ["E"] = "·", ["F"] = "··—·", ["G"] = "——·", ["H"] = "····", ["I"] = "··", ["J"] = "·———", ["K"] = "—·—", ["L"] = "·—··", ["M"] = "——", ["N"] = "—·", ["O"] = "———", ["P"] = "·——·", ["Q"] = "——·—", ["R"] = "·—·", ["S"] = "···", ["T"] = "—", ["U"] = "··—", ["V"] = "···—", ["W"] = "·——", ["X"] = "—··—", ["Y"] = "—·——", ["Z"] = "——··", ["0"] = "—————", ["1"] = "·————", ["2"] = "··———", ["3"] = "···——", ["4"] = "····—", ["5"] = "·····", ["6"] = "—····", ["7"] = "——···", ["8"] = "———··", ["9"] = "————·", ["."] = "·—·—·—", [","] = "——··——", [":"] = "———···", [";"] = "—·—·—", ["?"] = "··——··", ["!"] = "—·—·——", ["-"] = "—····—", ["/"] = "—··—· ", ["("] = "—·——·", [")"] = "—·——·—", ["="] = "—···—", ["@"] = "·——·—·", ["'"] = "·————·", ['"'] = "·—··—·", ["À"] = "·——·—", ["Å"] = "·——·—", ["Ä"] = "·—·—", ["Æ"] = "·—·—", ["Ç"] = "—·—··", ["É"] = "··—··", ["È"] = "·—··—", ["Ñ"] = "——·——", ["Ö"] = "———·", ["Ø"] = "———·", ["Ü"] = "··——", ["ß"] = "··· ···", } morse.codes = codes \stoptyping As you can see, there are a few non \ASCII\ characters supported as well. There will never be full \UNICODE\ support simply because morse is sort of obsolete. Also, in order to support \UNICODE\ one could as well use the bits of \UTF\ characters, although \unknown\ memorizing the whole \UNICODE\ table is not much fun. We associate a metatable index function with this mapping. That way we can not only conveniently deal with the casing, but also provide a fallback based on the shape. Once found, we store the representation so that only one lookup is needed per character. \starttyping local function resolvemorse(t,k) if k then local u = ucchars[k] local v = rawget(t,u) or rawget(t,shchars[u]) or false t[k] = v return v else return false end end setmetatable(codes, { __index = resolvemorse }) \stoptyping Next comes some rendering code. As we can best do rendering at the \TEX\ end we just use macros. \starttyping local MorseBetweenWords = context.MorseBetweenWords local MorseBetweenCharacters = context.MorseBetweenCharacters local MorseLong = context.MorseLong local MorseShort = context.MorseShort local MorseSpace = context.MorseSpace local MorseUnknown = context.MorseUnknown \stoptyping The main function is not that complex. We need to keep track of spaces and newlines. We have a nested loop because a fallback to shape can result in multiple characters. \starttyping function morse.tomorse(str) local inmorse = false for s in utfcharacters(str) do local m = codes[s] if m then if inmorse then MorseBetweenWords() else inmorse = true end local done = false for m in utfcharacters(m) do if done then MorseBetweenCharacters() else done = true end if m == "·" then MorseShort() elseif m == "—" then MorseLong() elseif m == " " then MorseBetweenCharacters() end end inmorse = true elseif s == "\n" or s == " " then MorseSpace() inmorse = false else if inmorse then MorseBetweenWords() else inmorse = true end MorseUnknown(s) end end end \stoptyping We use this function in two additional functions. One typesets a file, the other a table of available codes. \starttyping function morse.filetomorse(name,verbose) morse.tomorse(resolvers.loadtexfile(name),verbose) end function morse.showtable() context.starttabulate { "|l|l|" } for k, v in table.sortedpairs(codes) do context.NC() context(k) context.NC() morse.tomorse(v,true) context.NC() context.NR() end context.stoptabulate() end \stoptyping We're done with the \LUA\ code that we can either put in an external file or put in the module file. The \TEX\ file has two parts. The typesetting macros that we use at the \LUA\ end are defined first. These can be overloaded. \starttyping \def\MorseShort {\dontleavehmode \vrule width \MorseWidth height \MorseHeight depth \zeropoint \relax} \def\MorseLong {\dontleavehmode \vrule width 3\dimexpr\MorseWidth height \MorseHeight depth \zeropoint \relax} \def\MorseBetweenCharacters {\kern\MorseWidth} \def\MorseBetweenWords {\hskip3\dimexpr\MorseWidth\relax} \def\MorseSpace {\hskip7\dimexpr\MorseWidth\relax} \def\MorseUnknown#1 {[\detokenize{#1}]} \stoptyping The dimensions are stored in macros as well. Of course we could provide a proper setup command, but it hardly makes sense. \starttyping \def\MorseWidth {0.4em} \def\MorseHeight{0.2em} \stoptyping Finally we have arrived at the macros that interface to the \LUA\ functions. \starttyping \def\MorseString#1{\ctxlua{moduledata.morse.tomorse(\!!bs#1\!!es)}} \def\MorseFile #1{\ctxlua{moduledata.morse.filetomorse("#1")}} \def\MorseTable {\ctxlua{moduledata.morse.showtable()}} \stoptyping \startbuffer \Morse{A more advanced solution would be to convert a node list. That way we can deal with weird input.} \stopbuffer A string is converted to morse with the first command. \typebuffer This shows up as: \startalignment[flushleft,tolerant]\getbuffer\stopalignment Reduction and uppercasing is demonstrated in the next example: \startbuffer \MorseString{ÀÁÂÃÄÅàáâãäå} \stopbuffer \typebuffer This gives: \startalignment[flushleft,tolerant]\getbuffer\stopalignment \stopsection \startsection[title=Using helpers] The next example shows a bit of \LPEG. On top of the standard functionality a few additional functions are provided. Let's start with a pure \TEX\ example: \startbuffer \defineframed [coloredframed] [foregroundcolor=red, foregroundstyle=\underbar, offset=.1ex, location=low] \stopbuffer \typebuffer \getbuffer \startbuffer \processisolatedwords {\input ward \relax} \coloredframed \stopbuffer \typebuffer \blank \getbuffer \blank Because this processor macro operates at the \TEX\ end it has some limitations. The content is collected in a very narrow box and from that a regular paragraph is constructed. It is for this reason that no color is applied: the snippets that end up in the box are already typeset. An alternative is to delegate the task to \LUA: \startbuffer \startluacode local function process(data) local words = lpeg.split(lpeg.patterns.spacer,data or "") for i=1,#words do if i == 1 then context.dontleavehmode() else context.space() end context.coloredframed(words[i]) end end process(io.loaddata(resolvers.findfile("ward.tex"))) \stopluacode \stopbuffer \typebuffer \blank \getbuffer \blank The function splits the loaded data into a table with individual words. We use a splitter that splits on spacing tokens. The special case for \type {i = 1} makes sure that we end up in horizontal mode (read: properly start a paragraph). This time we do get color because the typesetting is done directly. Here is an alternative implementation: \starttyping local done = false local function reset() done = false return true end local function apply(s) if done then context.space() else done = true context.dontleavehmode() end context.coloredframed(s) end local splitter = lpeg.P(reset) * lpeg.splitter(lpeg.patterns.spacer,apply) local function process(data) lpeg.match(splitter,data) end \stoptyping This version is more efficient as it does not create an intermediate table. The next one is comaprable: \starttyping local function apply(s) context.coloredframed("%s ",s) end local splitter lpeg.splitter(lpeg.patterns.spacer,apply) local function process(data) context.dontleavevmode() lpeg.match(splitter,data) context.removeunwantedspaces() end \stoptyping \stopsection \startsection[title=Formatters] Sometimes can save a bit of work by using formatters. By default, the \type {context} command, when called directly, applies a given formatter. But when called as table this feature is lost because then we want to process non|-|strings as well. The next example shows a way out: \startbuffer context("the current emwidth is %p",\number\emwidth) context.par() context.formatted("the current emwidth is %p",\number\emwidth) context.par() context.bold(string.formatters["the current emwidth is %p"](\number\emwidth)) context.par() context.formatted.bold("the current emwidth is %p",\number\emwidth) \stopbuffer The last one is the most interesting one here: in the subnamespace \type {formatted} (watch the \type {d}) a format specification with extra arguments is expected. \ctxluabuffer \stopsection \stopchapter \stopcomponent