summaryrefslogtreecommitdiff
path: root/doc/context/sources/general/manuals/evenmore/evenmore-paragraphs.tex
blob: c8958a48752d46462a7bda0c91d442f64903bc08 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
% language=us runpath=texruns:manuals/evenmore

% End of July 2020 I decided to look into some of the backlog items and paragraphs
% are on them, as are inserts and so. The usual musical time stamp is buying the
% excellent Prince Live at the Aladdin Las Vegas DVD which I ran between the
% moments that I had enough of coding. Some tracks, like Family Name, fit perfectly
% in mid 2020.

% https://www.youtube.com/watch?v=f8FAJXPBdOg

\environment evenmore-style

\startcomponent evenmore-paragraphs

\enableexperiments[paragraphs.freeze]

\startchapter[title=Paragraphs]

{\em This is mostly a wrapup of some developments, and definitely not a tutorial.
What is described here is experimental and successive version of \CONTEXT\ \LMTX\
will explore their potential. As long a users stay away from the low level
primitives we can try to guarantee consistent behavior (and catch side effect or
deal with known issues).}

\startsection[title=Freezing]

A well known property of paragraphs is that when the moment is there to split
into lines, the current state of variables drives it. There are a lot of quite
some variables involved. The most significant one is the \type {\hsize}. Take
this:

\startbuffer
\bgroup
    {\bf Ward:}
    \hsize .25\textwidth
    {\bf Ward:} \samplefile{ward}
    \hsize .75\textwidth % last value
\egroup
\stopbuffer

\typebuffer

This gives:

{\forgetparagraphfreezing \getbuffer}

But wait, why do we get the full text width here? The reason is that by the time
the paragraph ends, after the \type {\egroup}, the \type {\hsize} is set back to
what it was before the group started.

\startbuffer
\bgroup
    \hsize .25\textwidth
    {\bf Ward:} \samplefile{ward}
    \hsize .75\textwidth % last value
    \par
\egroup
\stopbuffer

\typebuffer

This gives:

{\forgetparagraphfreezing \getbuffer}

The last \type {\hsize} specified is used. That is not really a problem, but the
fact that we need to explicitly end a paragraph before the group ends actually
is, and in a moment we will see an example where that matters a lot. First a
general solution to this problem is discussed. In the next example, we do group,
but inside that group we take a snapshot of the \type {\hsize}:

\startbuffer
\bgroup
    \hsize .80\textwidth
    \dontleavehmode
    \snapshotpar "000001
    {\bf Ward:} \samplefile{ward}
\egroup
\stopbuffer

\typebuffer

This time we get:

{\forgetparagraphfreezing \getbuffer}

The magic number used in the snapshot relates to the \type {\hsize}.

\startcolumns[n=2]
\starttabulate[|T||]
\NC 0x\uchexnumbers{\hsizefrozenparcode        } \NC hsize \NC \NR
\NC 0x\uchexnumbers{\skipfrozenparcode         } \NC leftskip rightskip \NC \NR
\NC 0x\uchexnumbers{\hangfrozenparcode         } \NC hangindent hangafter \NC \NR
\NC 0x\uchexnumbers{\indentfrozenparcode       } \NC parindent \NC \NR
\NC 0x\uchexnumbers{\parfillfrozenparcode      } \NC parfillskip parfillleftskip \NC \NR
\NC 0x\uchexnumbers{\adjustfrozenparcode       } \NC adjustspacing adjustspacingstep adjustspacingshrink adjustspacingstretch \NC \NR
\NC 0x\uchexnumbers{\protrudefrozenparcode     } \NC protrudechars \NC \NR
\NC 0x\uchexnumbers{\tolerancefrozenparcode    } \NC pretolerance tolerance \NC \NR
\NC 0x\uchexnumbers{\stretchfrozenparcode      } \NC emergencystretch \NC \NR
\NC 0x\uchexnumbers{\loosenessfrozenparcode    } \NC looseness \NC \NR
\NC 0x\uchexnumbers{\lastlinefrozenparcode     } \NC lastlinefit \NC \NR
\NC 0x\uchexnumbers{\linepenaltyfrozenparcode  } \NC linepenalty interlinepenalty interlinepenalties \NC \NR
\NC 0x\uchexnumbers{\clubpenaltyfrozenparcode  } \NC clubpenalty clubpenalties \NC \NR
\NC 0x\uchexnumbers{\widowpenaltyfrozenparcode } \NC widowpenalty widowpenalties displaywidowpenalty displaywidowpenalties \NC \NR
\NC 0x\uchexnumbers{\brokenpenaltyfrozenparcode} \NC brokenpenalty \NC \NR
\NC 0x\uchexnumbers{\demeritsfrozenparcode     } \NC adjdemerits doublehyphendemerits finalhyphendemerits \NC \NR
\NC 0x\uchexnumbers{\shapefrozenparcode        } \NC parshape \NC \NR
\NC 0x\uchexnumbers{\linefrozenparcode         } \NC baselineskip lineskip lineskiplimit \NC \NR
\NC                                              \NC \NC \NR
\NC 0xFFFFFFF                                    \BC all of them \NC \NR
\stoptabulate
\stopcolumns

In practice you will set them all on one go, so:

\starttyping
\snapshotpar "FFFFFFF
\stoptyping

How often do we need such a feature? Actually more often than one thinks,
especially when we have an unpredictable situation. For instance, when you
typeset from an \XML\ source you often don't know what you get, and you can have
cases that end up like this:

\startbuffer
\placefigure[left,none]{}{} {Ward: \bf  \dorecurse{3}{\samplefile{ward}} } \par
\placefigure[left,none]{}{} {\bf Ward:} \dorecurse{3}{\samplefile{ward}}   \par
\stopbuffer

\typebuffer

This might render as:

{\forgetparagraphfreezing \getbuffer} \forgetsidefloats % needed due to interference

The placement of such a figure is hooked into \type {\everypar} and uses hanging
indentation. Like \type {\hsize}, \type {\hangafter} and \type {\hangindent} can
be forgotten before the paragraph ends. In \MKII\ and \MKIV\ the recommended
solution is to always start a paragraph explicitly, with a strut, forced
indentation of preferably:

\startbuffer
\dontleavehmode {Ward: \bf  \dorecurse{3}{\samplefile{ward} } } \par
\dontleavehmode {\bf Ward:} \dorecurse{3}{\samplefile{ward} }   \par
\stopbuffer

\typebuffer

In an \XML\ mapping we can hide it but in a regular \TEX\ source this is not
pretty. With little effort we can do the snapping automatically, so that we get:

\placefigure[left,none]{}{} {Ward: \bf  \dorecurse{3}{\samplefile{ward} } } \par
\placefigure[left,none]{}{} {\bf Ward:} \dorecurse{3}{\samplefile{ward} }   \par

and this is what \CONTEXT\ \LMTX\ will do once we're sure that the snapshot
feature behaves well and has no side effects. There is of course some overhead
involved in taking snapshots, keeping track of the values and accessing them
later, but it is rewarding.

In addition to the numeric \type {\snapshotpar} primitive there is also another
way to take s snapshot. As with the numeric variant, it only takes a snapshot when
in horizontal mode: there has to be a so called local par node at the head of the
current list. The next code shows some how to play with some of what \CONTEXT\
offers:

\starttyping
\setuplayout[alternative=doublesided]

\starttext

\startbuffer
    \dorecurse{8}{
        \interlinepenalties 1 \maxcard
        CASE 1: \samplefile{tufte} \par
    } \page

    \dorecurse{8}{
        CASE 2: \samplefile{tufte}
        \interlinepenalties 1 \maxcard \par
    } \page

    \dorecurse{8}{
        CASE 3: \samplefile{tufte}
        \interlinepenalties 1 \maxcard \freezeparagraphproperties \par
    } \page

    \dorecurse{8}{
        CASE 4: \samplefile{tufte}
        \frozen \interlinepenalties 1 \maxcard \par
    } \page

    \dorecurse{8}{
        CASE 5: \samplefile{tufte}
        \frozen \interlinepenalty \maxcard \par
    } \page
\stopbuffer

\typebuffer \page \getbuffer

\stoptext
\stoptyping

When you process this you will notice that the \type {\frozen} prefix also
snapshots the parameter that gets set. Now, there is a pitfall here: some for
these settings are persistent, i.e. they are not reset after a paragraph has been
typeset. For instance, \type {\tolerance} is a general setting, but \type
{\hangindent} is a one shot setting: it's value gets reset after the paragraph
has been dealt with.

Here is another test one can run to see what happens:

\starttyping
\dontleavehmode
\defrostparagraphproperties
\writestatus{state}{after  start            \uchexnumbers{\the\snapshotpar}}%
\writestatus{state}{before set   hangindent \uchexnumbers{\the\snapshotpar}}%
\frozen\hangindent10pt
\writestatus{state}{after  set   hangindent \uchexnumbers{\the\snapshotpar}}%
\writestatus{state}{before set   looseness  \uchexnumbers{\the\snapshotpar}}%
\frozen\looseness 1
\writestatus{state}{after  set   looseness  \uchexnumbers{\the\snapshotpar}}%
\writestatus{state}{before set   hangafter  \uchexnumbers{\the\snapshotpar}}%
\frozen\hangafter 2
\writestatus{state}{after  set   hangafter  \uchexnumbers{\the\snapshotpar}}%
\begingroup
\writestatus{state}{before set   rightskip  \uchexnumbers{\the\snapshotpar}}%
\frozen\rightskip2cm
\writestatus{state}{after  set   rightskip  \uchexnumbers{\the\snapshotpar}}%
\endgroup
\writestatus{state}{before reset hangindent \uchexnumbers{\the\snapshotpar}}%
\snapshotpar-\frozenhangindentcode
\writestatus{state}{after  reset hangindent \uchexnumbers{\the\snapshotpar}}%
\writestatus{state}{before reset hangafter  \uchexnumbers{\the\snapshotpar}}%
\snapshotpar-\frozenhangaftercode
\writestatus{state}{after  reset hangafter  \uchexnumbers{\the\snapshotpar}}%
... content ...
\stoptyping

You can group an assignment and then take a snapshot. That way the change doesn't
affect following paragraphs, unless of course to did a global assignment. In
\CONTEXT\ we have a bunch of constants that can be used instead of the hard to
remember bit positions. The \type {\frozen} prefix can also be used with for
instance a \type {\advance} operation. Of course it only has effect for those
(internal) parameters that relate to a paragraph.

Keep in mind that what is show here will evolve: in \CONTEXT\ \LMTX\ we will
snapshot by default and the core macros are aware of this fact. Although the way
\CONTEXT\ is set up makes it relatively easy to make this paradigm shift users
should anyway be aware of this change when they do their own low level tweaking,
but in that case they probably already are aware of possible interferences.

\stopsection

\startsection[title=Wrapping up]

Another new (low level) feature is wrapping up a paragraph. Traditional \TEX\
comes with the powerful \type {\everypar} and in \LUAMETATEX\ we now have \type
{\wrapuppar}. This primitive collects tokens that will be expanded just before
the paragraph ends. Here is an example:

\startbuffer
\dontleavehmode
\wrapuppar{\hfill {\bf ONE}}%
\wrapuppar{\crlf\strut\hfill {\bf TWO}\hfill\strut}%
\wrapuppar{\crlf\strut {\bf THREE}\hfill\strut {\bf FOUR}}%
\samplefile{ward}

\samplefile{ward}
\stopbuffer

\typebuffer

We can only wrapup when we are in a paragraph although one can of course use the
\type {\wrapuppar} command inside an \type {\everypar} if needed.

\getbuffer

An more useful example is the following. We leave it to the reader to check it
out:

\starttyping
\dorecurse{10}{
    \bgroup
        \advance\hsize by -#1cm\relax
        \dontleavehmode
        \wrapuppar{\strut\nobreak\hfill\nobreak QED}%
        \samplefile{ward}
    \egroup
    \par
}
\stoptyping

\stopsection

\startsection[title=Insertions]

The concept of inserts is kind of complicated. They are nodes in a list that make
separate streams. An application of inserts are footnotes. In the text flow a
symbol is typeset (like a raised number) and the note itself becomes an insert.
When a paragraph is broken into lines, these inserts end up in to be boxed line,
but when the line is actually wrapped in a box, these inserts are collected and
injected after the line. The page builder will then take their dimensions into
account when it comes to breaking pages. Depending on how strict the rules are
the inserts will end up on the same page, move, of be broken into lines.

This all works well as long as the inserts are not burried into boxes: they then
are invisible to the mechanism described before. Take the following example:

\starttyping
\dontleavehmode
l\hbox{h\footnote{h1} test}
l\hbox{h\footnote{h2}} test
l\hbox{h\footnote{h3}} test
l\footnote{l4} test
l\footnote{l5} test
l\hbox{h\footnote{h6} test}
l\hbox{\hbox{\hbox{h\footnote{h7} test}}}
l\footnote{l8} test
\par
\stoptyping

% \starttabulate
% \NC test \NC test \footnote{before} \samplefile{tufte} \footnote{after}\NC \NR
% ...
% \NC test \NC test \footnote{before} \samplefile{tufte} \footnote{after}\NC \NR
% \stoptabulate

In the engines used with \MKII\ only a few footnotes actually show up. In \MKIV\
the situation is slightly better because there we use some trickery to migrate
these notes to the outer level. But even there it's not perfect because the order
changes. We can actually fix that (and do so) but it comes at a performance penalty
so this is why in \MKIV\ dealing with this is optional.

\start
    \def\Hi#1{\high{\tx#1}}
    \dontleavehmode
    \hbox{lh\H1 test lh\H2 test lh\H3 test l\H4 test l\H5 test lh\H6 test lh\H7 test l\H8 test}

    \starttabulate[|||||||||||]
    \NC \MKII \NC \PDFTEX\ & \XETEX         \NC \Hi4 l4 \NC \Hi5 l5 \NC \Hi8 l8 \NC         \NC         \NC         \NC         \NC         \NC \NR
    \NC \MKIV \footnote{In older versions.} \NC \LUATEX             \NC \Hi1 h1 \NC \Hi2 h2 \NC \Hi3 h3 \NC \Hi6 h6 \NC \Hi7 h7 \NC \Hi4 l4 \NC \Hi5 l5 \NC \Hi8 l8 \NC \NR
    \NC \LMTX \NC \LUAMETATEX               \NC \Hi1 h1 \NC \Hi2 h2 \NC \Hi3 h3 \NC \Hi4 l4 \NC \Hi5 l5 \NC \Hi6 h6 \NC \Hi7 h7 \NC \Hi8 l8 \NC \NR
    \stoptabulate
\stop

However, when you look at the results of \LMTX\ you will notice that the
situation is better. This is because we have some code to \LUAMETATEX\ that can
better deal with some cases. Combined with some \LUA\ magic (as in \MKIV) we get
the right order at hardly any runtime overhead. It must be noted that the
original \TEX\ engine for good reason works as it does because there the actual
typesetting (read: resolving glyphs, hyphenation, applying ligatures and kerns,
etc.) is interwoven with the main scanning|/|expanding loops in order to be
efficient on the machines of those times. In \LUATEX\ we have these stages
separated but the code dealing with inserts is the same, if only because we have
to be compatible. In \LUAMETATEX\ we have again a bit simpler code because we use
the fact that lists are double linked, which also makes it possible to add some
magic code dealing with nested inserts without obscuring the code. It comes of
course at a bit performance hit and the nodes related to lists also because
larger but they are already much larger than in other engines, so we don't care
too much about that. It is anyway a mechanism that need to be enabled explicitly.

\stopsection

\stopchapter

\stopcomponent

% \starttext

%     \def\TestA  {\registerparwrapper       {A}  {[\ignorespaces}{\removeunwantedspaces]\showparwrapperstate{A}}}
%     \def\TestB#1{\registerparwrapper       {B#1}{(\ignorespaces}{\removeunwantedspaces)\showparwrapperstate{B#1}}}
%     \def\TestC  {\registerparwrapper       {C}  {<\ignorespaces}{\removeunwantedspaces>\showparwrapperstate{C}\forgetparwrapper}}
%     \def\TestR  {\registerparwrapperreverse{R}  {<\ignorespaces}{\removeunwantedspaces>\showparwrapperstate{R}}}

%     \start
%         \TestA
%         \dorecurse{3}{1.#1 before \ruledvbox{\hsize2em\raggedcenter\TestB1 !\par} after\par} \blank
%         \dorecurse{3}{2.#1 before \ruledvbox{\hsize3em\raggedcenter        !\par} after\par} \blank
%         \dorecurse{3}{3.#1 before \ruledvbox{\hsize4em\raggedcenter\TestB2 !}     after\par} \blank
%         \forgetparwrapper
%         \dorecurse{3}{4.#1 before \ruledvbox{\hsize5em\raggedcenter\TestB3 !}     after\par} \blank
%         \TestC
%         \dorecurse{3}{5.#1 before \ruledvbox{\hsize2em\raggedcenter\TestA   !}     after\par} \blank
%     \stop


%     \start
%         \TestA
%         \dorecurse{3}{6.#1  before after\par} \blank
%         \TestB4
%         \dorecurse{3}{7.#1 before after\par} \blank
%         \TestB5
%         \TestR
%         \dorecurse{3}{8.#1 before after\par} \blank
%     \stop

% \stoptext