summaryrefslogtreecommitdiff
path: root/doc/context/sources/general/manuals/cld/cld-somemoreexamples.tex
diff options
context:
space:
mode:
Diffstat (limited to 'doc/context/sources/general/manuals/cld/cld-somemoreexamples.tex')
-rw-r--r--doc/context/sources/general/manuals/cld/cld-somemoreexamples.tex753
1 files changed, 753 insertions, 0 deletions
diff --git a/doc/context/sources/general/manuals/cld/cld-somemoreexamples.tex b/doc/context/sources/general/manuals/cld/cld-somemoreexamples.tex
new file mode 100644
index 000000000..a282be4e9
--- /dev/null
+++ b/doc/context/sources/general/manuals/cld/cld-somemoreexamples.tex
@@ -0,0 +1,753 @@
+% language=uk
+
+\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
+
+\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
+ [colored]
+ [foregroundcolor=red,
+ foregroundstyle=\underbar,
+ offset=.1ex,
+ location=low]
+\stopbuffer
+
+\typebuffer \getbuffer
+
+\startbuffer
+\processisolatedwords {\input ward \relax} \colored
+\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.colored(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.colored(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.colored("%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