lowlevel-localboxes.tex /size: 12 Kb    last modification: 2024-01-16 10:21
1% language=us runpath=texruns:manuals/lowlevel
2
3% \unprotect \pushoverloadmode
4% \popoverloadmode \protect
5
6\environment lowlevel-style
7
8\startdocument
9  [title=localboxes,
10   color=middlered]
11
12\startsectionlevel[title=Introduction]
13
14The \LUATEX\ engine inherited a few features from other engines and adding local
15boxes to paragraphs is one of them. This concept comes from \OMEGA\ but over time
16it has been made a bit more robust, also by using native par nodes instead of
17whatsit nodes that are used to support \TEX's extensions. In another low level
18manual we discuss paragraph properties and these local boxes are also part of
19that so you might want to catch up on that. Local boxes are stored in an initial
20par node with an adequate subtype but users wont' notice this (unless they mess
21around in \LUA). The inline par nodes have a different subtype and are injected
22with the \typ {\localinterlinepenalty}, \typ {\localbrokenpenalty}, \typ
23{\localleftbox}, \typ {\localrightbox} and \LUAMETATEX\ specific \typ
24{\localmiddlebox} primitives. WHen these primitives are used in vertical mode
25they just set registers.
26
27The original (\OMEGA) idea was that local boxes are used for repetitive
28punctuation (like quotes) at the left and|/|or right end of the lines that make
29up a paragraph. That means that when these primitives inject nodes they actually
30introduce states so that a stretch of text can be marked.
31
32When this mechanism was cleaned up in \LUAMETATEX\ I decided to investigate if
33other usage made sense. After all, it is a feature that introduces some extra
34code and it then pays of to use it when possible. Among the extensions are a
35callback that is triggered when the left and right boxes get added and
36experiments with that showed some potential but in order to retain performance as
37well as limit extensive node memory usage (par nodes are large) a system of
38indices was added. All this will be illustrated below. Warning: the mechanism in
39\LUAMETATEX\ is not compatible with \LUATEX.
40
41{\em This is a preliminary, uncorrected manual.}
42
43\stopsectionlevel
44
45\startsectionlevel[title=The basics]
46
47This mechanism uses a mix of setting (pseudo horizontal) box registers that get
48associated with (positions in a) paragraph. When the lines resulting from
49breaking the list gets packaged into an horizontal (line) box, the local left and
50right boxes get prepended and appended to the textual part (inside the left,
51right and parfills kips and left or right hanging margins). When assigning the
52current local boxes to the paragraph node(s) references to the pseudo registers
53are used and the packaging actually copies them. This mix of referencing and
54copying is somewhat tricky but the engine does it best to hide this for the user.
55
56This mechanism is rather useless when not wrapped into some high level mechanism
57because by default setting these boxes wipes the existing value. In \LUAMETATEX\
58you can actually access the boxes so prepending and appending is possible but
59experiments showed that this could come with a huge performance hit when the
60lists are not cleaned up during a run. This is why we have introduced indices:
61when you assign local boxes using the index option that specific index will be
62replaced and therefore we have a more sparse solution. So, contrary to \LUATEX,
63in \LUAMETATEX\ the local box registers have a linked lists of local boxes tagged
64by index. Unless you manipulate in \LUA, this is hidden from the user. One can
65access the boxes from the \TEX\ the but there can be no confusion with \LUATEX\
66here because there we don't have access. This is why usage as in \LUATEX\ will
67also work in \LUAMETATEX.
68
69This mechanism obeys grouping as is demonstrated in the next three examples. The
70first example is:
71
72\startbuffer[example-1]
73\start
74    \dorecurse{10}{test #1.1 }
75    \localleftbox{\blackrule[width=2em,color=darkred] }
76    \dorecurse{20}{test #1.2 }
77    \removeunwantedspaces
78    \localrightbox{ \blackrule[width=3em,color=darkblue]}
79    \dorecurse{20}{test #1.3 }
80\stop
81    \dorecurse{20}{test #1.4 }
82    % par ends here
83\stopbuffer
84
85\typebuffer[example-1][option=TEX]
86
87The next example differs in a subtle way: watch the \type {keep} keyword,
88it makes the setting retain after the group ends.
89
90\startbuffer[example-2]
91\start
92    \start
93        \dorecurse{10}{test #1.1 }
94        \localleftbox keep {\blackrule[width=2em,color=darkred] }
95        \dorecurse{20}{test #1.2 }
96        \removeunwantedspaces
97        \localrightbox { \blackrule[width=3em,color=darkblue]}
98        \dorecurse{20}{test #1.3 }
99    \stop
100        \dorecurse{20}{test #1.4 }
101\stop
102% par ends here
103\stopbuffer
104
105\typebuffer[example-2][option=TEX]
106
107The third example has two times \type {keep}. This option is \LUAMETATEX\
108specific.
109
110\startbuffer[example-3]
111\start
112    \start
113        \dorecurse{10}{test #1.1 }
114        \localleftbox keep {\blackrule[width=2em,color=darkred] }
115        \dorecurse{20}{test #1.2 }
116        \removeunwantedspaces
117        \localrightbox keep { \blackrule[width=3em,color=darkblue]}
118        \dorecurse{20}{test #1.3 }
119    \stop
120        \dorecurse{20}{test #1.4 }
121\stop
122% par ends here
123\stopbuffer
124
125\typebuffer[example-3][option=TEX]
126
127\startplacefigure % [location=page]
128    \startcombination[nx=1,ny=3]
129        {\vbox{\hsize\textwidth\getbuffer[example-1]}} {\bf Example 1}
130        {\vbox{\hsize\textwidth\getbuffer[example-2]}} {\bf Example 2}
131        {\vbox{\hsize\textwidth\getbuffer[example-3]}} {\bf Example 3}
132    \stopcombination
133\stopplacefigure
134
135One (nasty) side effect is that when you set these boxes ungrouped they are
136applied to whatever follows, which is why resetting them is built in the relevant
137parts of \CONTEXT. The next examples are typeset grouped an demonstrate the use
138of indices:
139
140\startbuffer
141\dorecurse{20}{before #1 }
142\localleftbox{\bf \darkred L 1 }%
143\localleftbox{\bf \darkred L 2 }%
144\dorecurse{20}{after #1 }
145\stopbuffer
146
147\typebuffer[option=TEX] \start \getbuffer \par \stop
148
149Indices can be set for both sides:
150
151\startbuffer
152\dorecurse{5}{\localrightbox index #1{ \bf \darkgreen R #1}}%
153\dorecurse{20}{before #1 }
154\dorecurse{5}{\localleftbox index #1{\bf \darkred L #1 }}%
155\dorecurse{20}{after #1 }
156\stopbuffer
157
158\typebuffer[option=TEX] \start \getbuffer \par \stop
159
160We can instruct this mechanism to hook the local box into the main
161par node by using the \type {par} keyword. Keep in mind that these
162local boxes only come into play when the lines are broken, so till
163then changing them is possible.
164
165\startbuffer
166\dorecurse{3}{\localrightbox index #1{ \bf \darkgreen R #1}}%
167\dorecurse{20}{before #1 }
168\dorecurse{2}{\localleftbox par index #1{\bf \darkred L #1 }}%
169\dorecurse{20}{after #1 }
170\stopbuffer
171
172\typebuffer[option=TEX] \start \getbuffer \par \stop
173
174\stopsectionlevel
175
176\startsectionlevel[title=The interface]
177
178{\em The interface described here is experimental.}
179
180Because it is hard to foresee if this mechanism will be used at all the \CONTEXT\
181interface is somewhat low level: one can build functionality on top of it. In the
182previous section we saw examples of local boxes being part of the text but one
183reason for extending the interface was to see if we can also use this engine
184feature for efficiently placing marginal content.
185
186\startbuffer[definition]
187\definelocalboxes
188  [lefttext]
189  [location=lefttext,width=3em,color=darkblue]
190\definelocalboxes
191  [lefttextx]
192  [location=lefttext,width=3em,color=darkblue]
193
194\definelocalboxes
195  [righttext]
196  [location=righttext,width=3em,color=darkyellow]
197\definelocalboxes
198  [righttextx]
199  [location=righttext,width=3em,color=darkyellow]
200\stopbuffer
201
202\typebuffer[definition][option=TEX]
203
204\getbuffer[definition]
205
206The order of definition matters! Here the \type {x} variants have a larger index
207number. There can (currently) be at most 256 indices. The defined local boxes
208are triggered with \type {\localbox}:
209
210\startbuffer[example]
211\startnarrower
212\dorecurse{20}{before #1 }%
213\localbox[lefttext]{[L] }%
214\localbox[lefttextx]{[LL] }%
215\localbox[righttext]{ [RR]}%
216\localbox[righttextx]{ [R]}%
217\dorecurse{20}{ after #1}%
218\stopnarrower
219\stopbuffer
220
221\typebuffer[example][option=TEX]
222
223Watch how we obey the margins:
224
225\getbuffer[example]
226
227Here these local boxes have dimensions. The predefined margin variants are
228virtual. Here we set up the style and color:
229
230\startbuffer[definition]
231\setuplocalboxes
232  [leftmargin]
233  [style=\bs,
234   color=darkgreen]
235\setuplocalboxes
236  [rightmargin]
237  [style=\bs,
238   color=darkred]
239\stopbuffer
240
241\typebuffer[definition][option=TEX]
242
243\startbuffer[example]
244\dorecurse{2}{
245    \dorecurse{10}{some text #1.##1 }%
246    KEY#1.1%
247    \localmargintext[leftmargin]{L #1.1}%
248    \localmargintext[rightmargin]{R #1.1}%
249    \dorecurse{10}{some text #1.##1 }%
250    KEY#1.2%
251    \localmargintext[leftmargin]{L #1.2}%
252    \localmargintext[rightmargin]{R #1.2}%
253    \dorecurse{10}{some text #1.##1 }%
254    \blank
255}
256\stopbuffer
257
258\typebuffer[example][option=TEX]
259
260You can also use \type {leftedge} and \type {rightedge} but using them here would
261put them outside the page.
262
263{\getbuffer[definition,example]}
264
265In previous examples you can see that setting something at the left will lag behind
266so deep down we use another trick here: \type {\localmiddlebox}. When these boxes
267get placed a callback can be triggered and in \CONTEXT\ we use that to move these
268middle boxes to the margins.
269
270Next we implement line numbers. Watch out: this will not replace the existing
271mechanisms, it's just an alternative as we have alternative table mechanisms. We
272have a repertoire of helpers for constructing the result:
273
274\startbuffer[definition]
275\definelocalboxes
276  [linenumberleft]
277  [command=\LeftNumber,
278   location=middle,
279   distance=\leftmargindistance,
280   width=3em,
281   style=\bs,
282   color=darkred]
283
284\definelocalboxes
285  [linenumberright] % [linenumberleft]
286  [command=\RightNumber,
287   location=middle,
288   distance=\rightmargindistance,
289   width=3em,
290   style=\bf,
291   color=darkgreen]
292
293\definecounter[MyLineNumberL]
294\definecounter[MyLineNumberR]
295
296\setupcounter
297  [MyLineNumberL]
298  [numberconversion=characters]
299
300\setupcounter
301  [MyLineNumberR]
302  [numberconversion=romannumerals]
303
304\def\LineNumberL
305  {\incrementcounter[MyLineNumberL]%
306   \convertedcounter[MyLineNumberL]}
307
308\def\LineNumberR
309  {\incrementcounter[MyLineNumberR]%
310   \convertedcounter[MyLineNumberR]}
311
312\protected\def\LeftNumber
313  {\setbox\localboxcontentbox\hbox
314     to \localboxesparameter{width}
315     {(\LineNumberL\hss\strut)}%
316   \localmarginlefttext\zeropoint}
317
318\protected\def\RightNumber
319  {\setbox\localboxcontentbox\hbox
320     to \localboxesparameter{width}
321     {(\strut\hss\LineNumberR)}%
322   \localmarginrighttext\zeropoint}
323\stopbuffer
324
325\typebuffer[definition][option=TEX]
326
327\startbuffer[example]
328\localbox[linenumberleft]{}%
329\localbox[linenumberright]{}%
330\dorecurse{2}{
331    \samplefile{tufte}
332    \par
333}
334\resetlocalbox[linenumberleft]%
335\resetlocalbox[linenumberright]%
336\stopbuffer
337
338\typebuffer[example][option=TEX]
339
340We use our tufte example to illustrate the usage:
341
342\getbuffer[definition]
343
344{\getbuffer[example]}
345
346For convenience we support ranges like this (we've reset the line number counters
347here):
348
349\resetcounter[MyLineNumberL]
350\resetcounter[MyLineNumberR]
351
352\startbuffer[example]
353\startlocalboxrange[linenumberleft]%
354\startlocalboxrange[linenumberright]%
355\dorecurse{2}{
356    \samplefile{tufte}
357    \par
358}
359\stoplocalboxrange
360\stoplocalboxrange
361\stopbuffer
362
363\typebuffer[example][option=TEX]
364
365{\getbuffer[example]}
366
367\stopsectionlevel
368
369\startsectionlevel[title=The helpers]
370
371For the moment we have these helpers:
372
373\starttabulate[|l|;|]
374\NC \type {\localboxindex}            \NC integer   \NC \NR
375\NC \type {\localboxlinenumber}       \NC integer   \NC \NR
376\NC
377\NC \type {\localboxlinewidth}        \NC dimension \NC \NR
378\NC \type {\localboxlocalwidth}       \NC dimension \NC \NR
379\NC \type {\localboxprogress}         \NC dimension \NC \NR
380\NC \type {\localboxleftoffset}       \NC dimension \NC \NR
381\NC \type {\localboxrightoffset}      \NC dimension \NC \NR
382\NC
383\NC \type {\localboxleftskip}         \NC dimension \NC \NR
384\NC \type {\localboxrightskip}        \NC dimension \NC \NR
385\NC \type {\localboxlefthang}         \NC dimension \NC \NR
386\NC \type {\localboxrighthang}        \NC dimension \NC \NR
387\NC
388\NC \type {\localboxindent}           \NC dimension \NC \NR
389\NC \type {\localboxparfillleftskip}  \NC dimension \NC \NR
390\NC \type {\localboxparfillrightskip} \NC dimension \NC \NR
391\NC \type {\localboxovershoot}        \NC dimension \NC \NR
392\NC
393\stoptabulate
394
395The progress and offsets are accumulated values of the normalized indent, hangs,
396skips etc. The line number is the position in the paragraph. In the callback we
397set the box register \type {\localboxcontentbox} and use it after the command has
398been applied. In the line number example you can see how we set its final
399content, so these boxes are sort of dynamic. Normally in the middle case no
400content is passed and in the par builder a middle is not taken into account when
401calculating the line width.
402
403\stopsectionlevel
404
405\stopdocument
406
407
408%     implement { name = "localboxmarkonce",
409