1
2
3\startcomponent mkdirtytricks
4
5\environment mkenvironment
6
7\chapter {Dirty tricks}
8
9If you ever laid your hands on the \TeX book, the words \quote {dirty tricks} will
10forever be associated with an appendix of that book. There is no doubt that you need
11to know a bit of the internals of \TEX\ in order to master this kind of trickyness.
12
13In this chaper I will show a few dirty \LUATEX\ tricks. It also gives an impression
14of what kind of discussions Taco and I had when discussing what kind of support should
15be build in the interface.
16
17\subject{afterlua}
18
19When we look at \LUA\ from the \TEX\ end, we can do things like:
20
21\startbuffer
22\def\test#1{
23 \setbox0=\hbox{\directlua0{tex.sprint(math.pi*#1)}}
24 pi: \the\wd0\space\the\ht0\space\the\dp0\par
25}
26\stopbuffer
27
28\typebuffer \blank \getbuffer \blank
29
30But what if we are at the \LUA\ end and want to let \TEX\ handle things? Imagine
31the following call:
32
33\startbuffer
34\setbox0\hbox{} \dimen0=0pt \ctxlua {
35 tex.sprint("\string\\setbox0=\string\\hbox{123}")
36 tex.sprint("\string\\the\string\\wd0")
37}
38\stopbuffer
39
40\typebuffer
41
42This gives: \ignorespaces \getbuffer. This may give you the impression that \TEX\
43kicks in immediately, but the following example demonstrates otherwise:
44
45\startbuffer
46\setbox0\hbox{} \dimen0=0pt \ctxlua {
47 tex.sprint("\string\\setbox0=\string\\hbox{123}")
48 tex.dimen[0] = tex.box[0].width
49 tex.sprint("\string\\the\string\\dimen0")
50}
51\stopbuffer
52
53\typebuffer
54
55This gives: \getbuffer. When still in \LUA, we never get to see the width
56of the box.
57
58A way out of this is the following rather straightforward approach:
59
60\starttyping
61function test(n)
62 function followup()
63 tex.sprint(tex.box[0].width)
64 end
65 tex.sprint("\\setbox0=\\hbox{123}\\directlua 0 {followup()}")
66end
67\stoptyping
68
69We can provide a more convenient solution for this:
70
71\starttyping
72afterlua = { } could also be done with closures
73
74function theafterlua(...)
75 for , fun in ipairs(afterlua) do
76 fun(...)
77 end
78 afterlua = { }
79end
80
81function afterlua(f)
82 afterlua[#afterlua1] = f
83end
84
85function theafterlua(...)
86 tex.sprint("\\directlua 0 {theafterlua("
87 .. table.concat({...},,) .. ")}")
88end
89\stoptyping
90
91If you look closely, you will see that we can (optionally) pass arguments
92to the function \type {theafterlua}. Usage now becomes:
93
94\starttyping
95function test(n)
96 afterlua(function(...)
97 tex.sprint(string.format("pi:
98 end)
99 afterlua(function(wd,ht,dp)
100 tex.sprint(string.format("ip:
101 end)
102 tex.sprint(string.format("\\setbox0=\\hbox{
103 local box0 = tex.box[0]
104 theafterlua(box0.width,box0.height,box0.depth)
105end
106\stoptyping
107
108The last call may confuse you but since it does a print to \TEX, it is
109in fact a delayed action. A cleaner implementation is the following:
110
111\starttyping
112local delayed = { }
113
114local function flushdelayed(...)
115 delayed = { }
116 for i=1, #t do
117 t[i](...)
118 end
119end
120
121function lua.delay(f)
122 delayed[#delayed1] = f
123end
124
125function lua.flush(...)
126 tex.sprint("\\directlua{flushdelayed(" ..
127 table.concat({...},,) .. ")}")
128end
129\stoptyping
130
131Usage is similar:
132
133\starttyping
134function test(n)
135 lua.delay(function(...)
136 tex.sprint(string.format("pi:
137 end)
138 tex.sprint(string.format("\\setbox0=\\hbox{
139 local box0 = tex.box[0]
140 lua.flush(box0.width,box0.height,box0.depth)
141end
142\stoptyping
143
144\stopcomponent
145 |