spac-par.mkxl /size: 18 Kb    last modification: 2025-02-21 11:03
1%D \module
2%D   [       file=spac-par,
3%D        version=2009.10.16, % 1997.03.31, was core-spa.tex
4%D          title=\CONTEXT\ Spacing Macros,
5%D       subtitle=Paragraphs,
6%D         author=Hans Hagen,
7%D           date=\currentdate,
8%D      copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
9%C
10%C This module is part of the \CONTEXT\ macro||package and is
11%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
12%C details.
13
14\writestatus{loading}{ConTeXt Spacing Macros / Paragraphs}
15
16\registerctxluafile{spac-par}{autosuffix}
17
18\unprotect
19
20% TODO: \appendtoks \strut \wrapuppar{\strut}\to \everypar
21
22% this might move to syst-ini.mkxl
23
24\setnewconstant\frozenparagraphdefault\numexpr
25    \adjustfrozenparcode
26  + \brokenpenaltyfrozenparcode
27  + \clubpenaltyfrozenparcode
28  + \demeritsfrozenparcode
29  + \displaypenaltyfrozenparcode
30  + \emergencyfrozenparcode
31  + \exhyphenpenaltyfrozenparcode
32  + \fitnessclassesfrozenparcode
33  + \hangfrozenparcode
34  + \hsizefrozenparcode
35  + \hyphenationfrozenparcode
36  + \hyphenpenaltyfrozenparcode
37  + \indentfrozenparcode
38  + \lastlinefrozenparcode
39  + \linebreakchecksfrozenparcode
40  % \linefrozenparcode
41  + \linepenaltyfrozenparcode
42  + \loosenessfrozenparcode
43  % \mathpenaltyfrozenparcode
44  + \orphanpenaltyfrozenparcode
45  + \parfillfrozenparcode
46  + \parpassesfrozenparcode
47  + \protrudefrozenparcode
48  + \shapefrozenparcode
49  + \shapingpenaltyfrozenparcode
50  + \singlelinepenaltyfrozenparcode
51  + \skipfrozenparcode
52  + \stretchfrozenparcode
53  + \toddlerpenaltyfrozenparcode
54  + \tolerancefrozenparcode
55  + \twindemeritsfrozenparcode
56  + \widowpenaltyfrozenparcode
57\relax
58
59\setnewconstant\paragraphupdatecodes\numexpr
60    \tolerancefrozenparcode
61  + \loosenessfrozenparcode
62  + \linepenaltyfrozenparcode
63  + \widowpenaltyfrozenparcode
64  + \clubpenaltyfrozenparcode
65  + \brokenpenaltyfrozenparcode
66  + \demeritsfrozenparcode
67  + \orphanpenaltyfrozenparcode
68  + \toddlerpenaltyfrozenparcode
69  + \singlelinepenaltyfrozenparcode
70  + \hyphenpenaltyfrozenparcode
71  + \exhyphenpenaltyfrozenparcode
72  + \linebreakchecksfrozenparcode
73  + \twindemeritsfrozenparcode
74\relax
75
76\setnewconstant\paragraphpenaltycodes\numexpr
77    \linepenaltyfrozenparcode
78  + \widowpenaltyfrozenparcode
79  + \clubpenaltyfrozenparcode
80  + \brokenpenaltyfrozenparcode
81  + \shapingpenaltyfrozenparcode
82  + \orphanpenaltyfrozenparcode
83  + \toddlerpenaltyfrozenparcode
84  + \singlelinepenaltyfrozenparcode
85  + \hyphenpenaltyfrozenparcode
86  + \exhyphenpenaltyfrozenparcode
87  + \linebreakchecksfrozenparcode
88\relax
89
90\setnewconstant\paragraphdemeritcodes\numexpr
91    \demeritsfrozenparcode
92  + \twindemeritsfrozenparcode
93\relax
94
95\setnewconstant\paragraphshapecodes\numexpr
96    \hangfrozenparcode
97  + \skipfrozenparcode
98  + \parfillfrozenparcode
99  + \shapefrozenparcode
100\relax
101
102\setnewconstant\paragraphlinecodes\numexpr
103    \linefrozenparcode
104\relax
105
106\setnewconstant\paragraphhyphenationcodes\numexpr
107    \hyphenationfrozenparcode
108\relax
109
110\setnewconstant\paragraphpassescodes\numexpr
111    \parpassesfrozenparcode
112\relax
113
114\bitwiseflip\glyphoptions\checktoddlerglyphoptioncode
115\bitwiseflip\glyphoptions\checktwinglyphoptioncode
116
117% \linefrozenparcode % lineskip
118
119\appendtoks
120    \parpasses\zerocount
121  % \noexpansion
122  % \noprotrusion
123\to \everyforgetall
124
125\permanent\protected\def\freezeparagraphproperties {\snapshotpar\frozenparagraphdefault}
126\permanent\protected\def\defrostparagraphproperties{\snapshotpar\zerocount}
127
128\permanent\protected\def\updateparagraphproperties{\ifhmode\snapshotpar\paragraphupdatecodes \fi}
129\permanent\protected\def\updateparagraphpenalties {\ifhmode\snapshotpar\paragraphpenaltycodes\fi}
130\permanent\protected\def\updateparagraphdemerits  {\ifhmode\snapshotpar\paragraphdemeritcodes\fi}
131\permanent\protected\def\updateparagraphshapes    {\ifhmode\snapshotpar\paragraphshapecodes  \fi}
132\permanent\protected\def\updateparagraphlines     {\ifhmode\snapshotpar\paragraphlinecodes   \fi}
133\permanent\protected\def\updateparagraphpasses    {\ifhmode\snapshotpar\paragraphpassescodes \fi}
134
135% so far
136
137\let\spac_paragraph_freeze\relax
138
139\permanent\protected\def\setparagraphfreezing   {\enforced\let\spac_paragraph_freeze\freezeparagraphproperties}
140\permanent\protected\def\forgetparagraphfreezing{\enforced\let\spac_paragraph_freeze\relax}
141
142\installcorenamespace {bparwrap}
143\installcorenamespace {eparwrap}
144\installcorenamespace {parwrapbefore}
145\installcorenamespace {parwrapafter}
146\installcorenamespace {parwrapcount}
147
148\lettonothing\spac_paragraph_wrap
149
150\newinteger\c_spac_paragraph_group_level
151
152\protected\def\spac_paragraph_update
153  {\c_spac_paragraph_group_level\currentgrouplevel\relax
154   \ifcsname\??bparwrap\the\c_spac_paragraph_group_level\endcsname
155     \the\lastnamedcs
156     \relax
157     \dontleavehmode % just in case
158     \wrapuppar{\the\csname\??eparwrap\the\c_spac_paragraph_group_level\endcsname\relax}%
159   \fi}
160
161\permanent\protected\def\registerparwrapper       {\spac_register_par_wrapper\toksapp\tokspre}
162\permanent\protected\def\registerparwrapperreverse{\spac_register_par_wrapper\tokspre\toksapp}
163
164\protected\def\spac_paragraph_install
165  {\expandafter\newtoks\csname\??bparwrap\the\currentgrouplevel\endcsname
166   \expandafter\newtoks\csname\??eparwrap\the\currentgrouplevel\endcsname}
167
168\def\spac_paragraph_install_count#1%
169  {\expandafter\newinteger\csname\??parwrapcount#1\endcsname}
170
171\def\spac_paragraph_install_pair#1#2#3%
172  {\expandafter\newtoks\csname\??parwrapbefore#3\endcsname
173   \expandafter\newtoks\csname\??parwrapafter #3\endcsname
174   #1\csname\??bparwrap\the\currentgrouplevel\endcsname{\the\csname\??parwrapbefore#3\endcsname}%
175   #2\csname\??eparwrap\the\currentgrouplevel\endcsname{\the\csname\??parwrapafter #3\endcsname}}
176
177\def\spac_register_par_wrapper_yes#1#2#3#4#5%
178  {\ifcsname\??bparwrap\the\currentgrouplevel\endcsname \else
179     \spac_paragraph_install
180   \fi
181   \ifcsname\??parwrapcount#3\endcsname \else
182     \spac_paragraph_install_count{#3}%
183   \fi
184   \ifcsname\??parwrapbefore#3\endcsname \else
185     \spac_paragraph_install_pair#1#2{#3}%
186   \fi
187   #1\csname\??parwrapbefore#3\endcsname{\global\advanceby\csname\??parwrapcount#3\endcsname\plusone\relax % global, see (!)
188                                         \clf_setparwrapper{#3}#4}%
189   #2\csname\??parwrapafter #3\endcsname{#5}%
190   \clf_newparwrapper{#3}%
191   \let\spac_paragraph_wrap\spac_paragraph_update}
192
193\def\spac_register_par_wrapper_nop#1#2#3#4#5%
194  {\ifcsname\??parwrapcount#3\endcsname \else
195     \spac_paragraph_install_count{#3}%
196   \fi
197   \global\csname\??parwrapcount#3\endcsname\plusone % global, see (!)
198   #4\wrapuppar{#5}}
199
200\protected\def\spac_register_par_wrapper
201  {\ifhmode
202     \expandafter\spac_register_par_wrapper_nop
203   \else
204     \expandafter\spac_register_par_wrapper_yes
205   \fi}
206
207\permanent\protected\def\forgetparwrapper
208  {\csname\??bparwrap\the\currentgrouplevel\endcsname\emptytoks
209   \csname\??eparwrap\the\currentgrouplevel\endcsname\emptytoks}
210
211\permanent\protected\def\unregisterparwrapper#1%
212  {\global\csname\??parwrapcount#1\endcsname\zerocount % global, see (!)
213   \ifcsname\??parwrapbefore#1\endcsname
214     \lastnamedcs\emptytoks
215     \csname\??parwrapafter#1\endcsname\emptytoks
216   \fi}
217
218\permanent\def\directparwrapper#1#2%
219  {#1\wrapuppar{#2}}
220
221\permanent\protected\def\doifelseparwrapper#1%
222  {\unless\ifcsname\??parwrapcount#1\endcsname
223     \expandafter\secondoftwoarguments
224   \orelse\ifcase\lastnamedcs
225     \expandafter\secondoftwoarguments
226   \else
227     \expandafter\firstoftwoarguments
228   \fi}
229
230% \getparwrapper  % defined in lua
231% \lastparwrapper % defined in lua
232
233\permanent\protected\def\showparwrapperstate#1%
234  {\begingroup
235   \infofont ΒΆ#1\hilo
236     {\smallinfofont\getparwrapper {#1}}%
237     {\smallinfofont\lastparwrapper{#1}}%
238   \endgroup}
239
240%appendtoks\updateparwrapperindeed\to\everypar
241%appendtoks\spac_paragraph_wrap   \to\everypar
242%appendtoks\spac_paragraph_freeze \to\everypar
243
244% (!) testcase for global setting of count
245%
246% \starttext
247%     \hsize3cm
248%     Aaa\wordright{Aaa}\par
249%     \sc{Bbb\wordright{Bbb}}\par
250%     {\sc Ccc\wordright{Ccc}}\par
251%     \sc{Ddd}\wordright{\sc{Ddd}}\par
252% \stoptext
253
254\setparagraphfreezing
255
256\appendtoks\lettonothing\spac_paragraph_wrap\to\everyforgetall
257
258%D In due time, the code below will be upgraded using the above mechanisms.
259
260%D The dreadful sequence \type {\bgroup} \unknown\ \type {\carryoverpar} \unknown\
261%D \type {\egroup} is needed when for instance sidefloats are used in combination
262%D with something that starts with a group. This is because otherwise the
263%D indentation as set (by the output routine) inside the group are forgotten
264%D afterwards. (I must not forget its existence).
265
266\mutable\lettonothing\currentparagraphproperties % visible for tracing
267
268\permanent\def\carryoverpar#1% #1 can be \endgroup or \egroup or ... expandable !
269  {\normalexpanded
270     {\noexpand#1%
271      \hangindent\the\hangindent
272      \hangafter \the\hangafter
273      \parskip   \the\parskip
274      \leftskip  \the\leftskip
275      \rightskip \the\rightskip
276      \relax}}
277
278\permanent\protected\def\pushparagraphproperties
279  {\edef\currentparagraphproperties{\carryoverpar\relax}%
280   \pushmacro\currentparagraphproperties}
281
282\permanent\protected\def\popparagraphproperties
283  {\popmacro\currentparagraphproperties
284   \currentparagraphproperties}
285
286\permanent\protected\def\flushparagraphproperties
287  {\popmacro\currentparagraphproperties}
288
289%D Beware, changing this will break some code (like pos/backgrounds) but it has been
290%D changed anyway so let's see where things go wrong.
291
292\installcorenamespace{paragraphintro}
293
294\newtoks\t_spac_paragraphs_intro_first
295\newtoks\t_spac_paragraphs_intro_next
296\newtoks\t_spac_paragraphs_intro_each
297
298\newconditional\c_spac_paragraphs_intro_first
299\newconditional\c_spac_paragraphs_intro_next
300\newconditional\c_spac_paragraphs_intro_each
301
302\lettonothing\spac_paragraphs_flush_intro
303\lettonothing\spac_paragraphs_intro_step
304
305\permanent\protected\tolerant\def\setupparagraphintro[#1]#*[#2]%
306  {\def\spac_paragraphs_intro_step##1%
307     {\csname\??paragraphintro\ifcsname\??paragraphintro##1\endcsname##1\fi\endcsname{#2}}%
308   \processcommacommand[#1]\spac_paragraphs_intro_step}
309
310\letcsname\??paragraphintro\empty\endcsname\gobbleoneargument
311
312\defcsname\??paragraphintro\v!reset\endcsname#1%
313  {\global\c_spac_paragraphs_intro_first\conditionalfalse
314   \global\c_spac_paragraphs_intro_next\conditionalfalse
315   \global\c_spac_paragraphs_intro_each\conditionalfalse
316   \global\t_spac_paragraphs_intro_first\emptytoks
317   \global\t_spac_paragraphs_intro_next \emptytoks
318   \global\t_spac_paragraphs_intro_each \emptytoks
319   \glettonothing\spac_paragraphs_flush_intro}
320
321\defcsname\??paragraphintro\v!first\endcsname#1%
322  {\global\c_spac_paragraphs_intro_first\conditionaltrue
323   \gtoksapp\t_spac_paragraphs_intro_first{#1}%
324   \glet\spac_paragraphs_flush_intro\spac_paragraphs_flush_intro_indeed}
325
326\defcsname\??paragraphintro\v!next\endcsname#1%
327  {\global\c_spac_paragraphs_intro_next\conditionaltrue
328   \gtoksapp\t_spac_paragraphs_intro_next{#1}%
329   \glet\spac_paragraphs_flush_intro\spac_paragraphs_flush_intro_indeed}
330
331\defcsname\??paragraphintro\v!each\endcsname#1%
332  {\global\c_spac_paragraphs_intro_each\conditionaltrue
333   \gtoksapp\t_spac_paragraphs_intro_each{#1}%
334   \glet\spac_paragraphs_flush_intro\spac_paragraphs_flush_intro_indeed}
335
336%D We can say:
337%D
338%D \starttyping
339%D \setupparagraphintro[first][\index{Knuth}]
340%D \stoptyping
341%D
342%D Maybe more convenient is:
343%D
344%D \starttyping
345%D \flushatparagraph{\index{Zapf}}
346%D \stoptyping
347%D
348%D \starttyping
349%D \setupparagraphintro[first][\hbox to 3.5em{\tt FIRST \hss}]
350%D \setupparagraphintro[first][\hbox to 3.5em{\tt TSRIF \hss}]
351%D \setupparagraphintro[next] [\hbox to 3.5em{\tt NEXT  \hss}]
352%D \setupparagraphintro[next] [\hbox to 3.5em{\tt TXEN  \hss}]
353%D \setupparagraphintro[each] [\hbox to 3.0em{\tt EACH  \hss}]
354%D \setupparagraphintro[each] [\hbox to 3.0em{\tt HCEA  \hss}]
355%D
356%D some paragraph \par
357%D some paragraph \par
358%D some paragraph \par
359%D some paragraph \par
360%D
361%D \setupparagraphintro[first][\hbox to 3.5em{\tt FIRST \hss}]
362%D \setupparagraphintro[first][\hbox to 3.5em{\tt TSRIF \hss}]
363%D
364%D some paragraph \par
365%D some paragraph \par
366%D
367%D \setupparagraphintro[reset]
368%D
369%D some paragraph \par
370%D \stoptyping
371
372\permanent\protected\def\flushatparagraph#1%
373  {\global\c_spac_paragraphs_intro_first\plusone
374   \gtoksapp\t_spac_paragraphs_intro_first{#1}%
375   \glet\spac_paragraphs_flush_intro\spac_paragraphs_flush_intro_indeed}
376
377%D Here comes the flusher (we misuse the one level expansion of token registers to
378%D feed a nice stream into the paragraph.)
379
380\protected\def\spac_paragraphs_flush_intro_indeed % we make sure that the token lists expand directly
381  {\normalexpanded{%                              % after another so the first code is there twice
382     \ifconditional\c_spac_paragraphs_intro_each
383       \ifconditional\c_spac_paragraphs_intro_next
384         \glet\spac_paragraphs_flush_intro\spac_paragraphs_flush_intro_next
385       \else
386         \glet\spac_paragraphs_flush_intro\spac_paragraphs_flush_intro_each
387       \fi
388       \ifconditional\c_spac_paragraphs_intro_first
389         \global\c_spac_paragraphs_intro_first\conditionalfalse
390         \global\t_spac_paragraphs_intro_first\emptytoks
391         \expand\t_spac_paragraphs_intro_first
392       \fi
393       \expand\t_spac_paragraphs_intro_each
394     \else
395       \ifconditional\c_spac_paragraphs_intro_next
396         \glet\spac_paragraphs_flush_intro\spac_paragraphs_flush_intro_next
397       \fi
398       \ifconditional\c_spac_paragraphs_intro_first
399         \global\c_spac_paragraphs_intro_first\conditionalfalse
400         \global\t_spac_paragraphs_intro_first\emptytoks
401         \expand\t_spac_paragraphs_intro_first
402       \fi
403     \fi}}
404
405\protected\def\spac_paragraphs_flush_intro_next
406  {\normalexpanded{%
407     \global\c_spac_paragraphs_intro_next\conditionalfalse
408     \global\t_spac_paragraphs_intro_next\emptytoks
409     \ifconditional\c_spac_paragraphs_intro_each
410       \glet\spac_paragraphs_flush_intro\spac_paragraphs_flush_intro_each
411       \expand\t_spac_paragraphs_intro_next
412       \expand\t_spac_paragraphs_intro_each
413     \else
414       \glettonothing\spac_paragraphs_flush_intro
415       \expand\t_spac_paragraphs_intro_next
416     \fi}}
417
418\protected\def\spac_paragraphs_flush_intro_each
419  {\expand\t_spac_paragraphs_intro_each}
420
421%D \macros
422%D   {flushatnextpar}
423%D
424%D This macro collects data that will be flushed at the next paragraph. By using
425%D this macro you can avoid interfering nodes (writes, etc).
426
427\lettonothing\flushpostponednodedata % hook into everypar
428
429\newbox    \b_spac_postponed_data
430%newinteger\c_spac_postponed_data
431
432% \installcorenamespace {postponednodesstack}
433%
434% \initializeboxstack\??postponednodesstack
435%
436% \protected\def\pushpostponednodedata
437%   {\global\advanceby\c_spac_postponed_data\plusone
438%    \savebox\??postponednodesstack{\the\c_spac_postponed_data}{\box\b_spac_postponed_data}}
439%
440% \protected\def\poppostponednodedata
441%   {\global\setbox\b_spac_postponed_data\hbox{\foundbox\??postponednodesstack{\the\c_spac_postponed_data}}%
442%    \global\advanceby\c_spac_postponed_data\minusone
443%    \ifvoid\b_spac_postponed_data\else
444%      \enforced\glet\flushpostponednodedata\spac_postponed_data_flush
445%    \fi}
446
447\newtoks\everyflushatnextpar
448
449\permanent\protected\def\pushpostponednodedata
450  {\globalpushbox\b_spac_postponed_data}
451
452\permanent\protected\def\poppostponednodedata
453  {\globalpopbox\b_spac_postponed_data
454   \ifvoid\b_spac_postponed_data\else
455     \enforced\glet\flushpostponednodedata\spac_postponed_data_flush
456   \fi}
457
458\permanent\protected\def\flushatnextpar
459  {\begingroup
460   \expand\everyflushatnextpar
461   \enforced\glet\flushpostponednodedata\spac_postponed_data_flush
462   \dowithnextboxcs\spac_postponed_data_finish\hpack}
463
464% \def\spac_postponed_data_finish
465%   {\global\setbox\b_spac_postponed_data\hpack % to\zeropoint
466%      {\box\b_spac_postponed_data\box\nextbox}%
467%    \endgroup}
468%
469% This is better when used with protrusion which does not like too deeply nested
470% boxes:
471%
472% \def\spac_postponed_data_finish
473%   {\dontcomplain
474%    \global\setbox\b_spac_postponed_data\hpack to \zeropoint
475%      {\unhbox\b_spac_postponed_data\unhbox\nextbox}%
476%    \endgroup}
477%
478% We could do this if there is content with widths ... not that is should be used
479% that way, but this way we don't overflow:
480
481\def\spac_postponed_data_finish
482  {\dontcomplain
483   \global\setbox\b_spac_postponed_data\hpack % to \zeropoint
484     {\unhbox\b_spac_postponed_data
485      \scratchdimen\wd\nextbox
486      \unhbox\nextbox
487      \ifcase\scratchdimen\else\kern-\scratchdimen\fi}%
488   \endgroup}
489
490\def\spac_postponed_data_flush
491  {%\iftrialtypesetting \else
492     \ifvoid\b_spac_postponed_data\else
493       \hpack{\smashedbox\b_spac_postponed_data}% \box\b_spac_postponed_data
494     \fi
495     \enforced\glettonothing\flushpostponednodedata
496   }%\fi}
497
498\permanent\protected\def\doflushatpar % might be renamed
499  {\ifvmode
500     \expandafter\flushatnextpar
501   \else
502     \expandafter\firstofoneargument
503   \fi}
504
505%D Experiment:
506%D
507%D \starttyping
508%D \startcombination[4*1]
509%D     {\vtop{           \hsize #1 \setupalign[tolerant]                             \samplefile{ward}}} {tolerant}
510%D     {\vtop{\darkblue  \hsize #1 \setupalign[tolerant,stretch]                     \samplefile{ward}}} {tolerant,stretch}
511%D     {\vtop{\darkgreen \hsize #1 \setupalign[tolerant]        \optimize[flushleft] \samplefile{ward}}} {tolerant,fuzzy}
512%D     {\vtop{\darkred   \hsize #1 \setupalign[tolerant]        \optimize[normal]    \samplefile{ward}}} {tolerant,lessfuzzy}
513%D \stopcombination
514%D \stoptyping
515
516\installcorenamespace {optimize}
517
518\installcommandhandler \??optimize {optimize} \??optimize
519
520\setupoptimize
521  [\c!lines=\zerocount,
522   \c!left=\zeropoint,
523   \c!right=\zeropoint,
524   \c!distance=\zeropoint]
525
526\permanent\protected\def\optimize[#S#1]%
527  {\begingroup
528   \ifhastok={#1}%
529     \setupcurrentoptimize[#1]%
530   \else
531     \cdef\currentoptimize{#1}%
532   \fi
533   \scratchdimenone  {\optimizeparameter\c!left }%
534   \scratchdimentwo  {\optimizeparameter\c!right}%
535   \scratchcounterone{\optimizeparameter\c!lines}%
536   \scratchdistance  {\optimizeparameter\c!distance}%
537   \normalexpanded
538     {\endgroup
539      \looseness\the\scratchcounterone\relax
540      \emergencyextrastretch\the\scratchdistance\relax
541      \ifzerodim\scratchdimenone\else
542        \emergencyleftskip \zeropoint \s!plus \the\scratchdimenone\relax
543      \fi
544      \ifzerodim\scratchdimentwo\else
545        \emergencyrightskip\zeropoint \s!plus \the\scratchdimentwo\relax
546      \fi}%
547   \ignorespaces}
548
549\defineoptimize[+\v!line][\c!lines=\plusone]
550\defineoptimize[-\v!line][\c!lines=\minusone]
551
552\defineoptimize[\v!flushleft ][\c!right=2\bodyfontsize]
553\defineoptimize[\v!flushright][\c!left=2\bodyfontsize]
554\defineoptimize[\v!normal    ][\c!right=2\bodyfontsize,\c!distance=\bodyfontsize]
555
556\protect \endinput
557