fix bug 3524174, thanks to Jeff Higgins for the report, thanks to Michael J Gruber...
[PyX.git] / contrib / textboxes.tex
blobb9f57db78e7a25435a1d9527e2539164b797334a
1 % Modified \output-routine for the use with PyX
2 % this file makes changes of \hsize and \vsizes possible after
3 % every pagebreak
5 % Copyright (C) 2004 Michael Schindler <m-schindler@sourceforge.net>
7 % This file is part of PyX (http://pyx.sourceforge.net/).
9 % PyX is free software; you can redistribute it and/or modify
10 % it under the terms of the GNU General Public License as published by
11 % the Free Software Foundation; either version 2 of the License, or
12 % (at your option) any later version.
14 % PyX is distributed in the hope that it will be useful,
15 % but WITHOUT ANY WARRANTY; without even the implied warranty of
16 % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 % GNU General Public License for more details.
19 % You should have received a copy of the GNU General Public License
20 % along with PyX; if not, write to the Free Software
21 % Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 \newtoks\lovsizes
25 \newtoks\lohsizes
26 \newtoks\parnos
27 \newtoks\parshapes
29 % Please note that all four tokenlists have to end with {\relax}
30 % This is needed for correct shortening
32 %%%%%%%%%% TeX's part %%%%%%%%%%
33 \newif\ifverbose \verbosefalse
34 \newcount\tempcnt
35 \newcount\parno \parno=1
36 \newcount\myprevgraf \myprevgraf=0
37 \newcount\showprevgraf \showprevgraf=0
38 \newcount\outputtype \outputtype=0
39 \newcount\razor
40 \newcount\leastcost \leastcost=10000000
41 \newcount\futurecost
43 % Tools for splitting and merging tokenlists after their first element {{{
44 % did I learn too much lisp or what?
45 \def\cdr#1{\expandafter\precdr\expandafter#1\the#1@}
46 \def\numcdr#1{\expandafter\prenumcdr\expandafter#1\the#1@}
47 \def\numconcat#1#2{\edef\foo{{\the#1}\the#2}\global#2=\expandafter{\foo}}
49 \def\precdr#1#2#3@{\ifx#2\relax\relax\global#1={\relax}\else#2\global#1={#3}\fi}
50 \def\prenumcdr#1#2#3@{\ifx\relax#2 0\global#1={\relax}\else#2\global#1={#3}\fi}
51 % }}}
53 % The output routine {{{
54 % Documentation {{{
55 % 1. Question: Who calls the pagebuilder and \output?
56 % Answer: Only \par and $$ (as far as I have read the TeXbook)
58 % 2. Question: So, all together: What is to be done?
59 % Answer: We have to simulate the work of the linebuilder that makes
60 % paragraphs and math into lines in order to find out, what value \prevgraf
61 % might have at a pagepreak. Then, we change the \hsize and \vsize at the
62 % pagepreak and inform PyX about the value of \prevgraf.
64 % 3. Question: When is \prevgraf advanced?
65 % Answer: At every line by 1 and at every display math by 3
67 % 4. Question: How can we hook into the pagebuilding mechanism to find out
68 % about lines and displays?
69 % Answer: We get into the pagebuilder that splits vertical lists into pages
70 % if we enforce a penalty of less than -10000. There are three built-in
71 % hooks: \interlinepenalty
72 % \predisplaypenalty
73 % \postdisplaypenalty
74 % These are set to values less than -10000 by wrapping \par and $$, the
75 % ones who call the pagebuilder and, therefore, \output
77 % Unfortunately, we get only "breaks", but we are interested in lines. At
78 % each list, we have one break less than lines. So, we have to add another
79 % call of \output at the end of a paragraph. This is done by an explicit
80 % \penalty
81 % A display math succeeding some text also calls the pagebuilder. We have
82 % to add another line in such situations. This is done in the output
83 % routine while processing \predisplaypenalty.
85 % 5. Question: Why are there so many \ifnum s in the output routine?
86 % Answer: As explained avove, we have to do different things at different
87 % positions of our text. \interlinepenalty and the \penalty for paragraph
88 % endings have to add a line. \postdisplaypenalty has to add the display
89 % (advances \prevgraf by 3), while \predisplaypenalty may still reside on
90 % the previous page, while the display math has moved to the next one.
91 % Therefore, it does not contribute to the \prevgraf. Nevertheless, if
92 % there was some text before the display math in the same paragraph, we
93 % still have to add the last line of text!
95 % This is the reason for the whole \outputtype business:
96 % We have to show a \prevgraf value different to \myprevgraf at a situation
97 % where an \interlinepenalty is followed by a \predisplaypenalty which is
98 % followed by a pagebreak. The display is on the next page.
99 % The same can -- of course -- also occur with a \predisplaypenalty
100 % following a \penalty caused by \par
102 % 6. Question: What the hell does \leastcost do?
103 % Answer: This is a tricky point. We hooked into the pagebuilding mechanism
104 % by changing penalties. In this case, TeX thinks, there is a very good
105 % pagebreak and calls \output. But \output removes the strongly negative
106 % penalty we added. Now, TeX has to reconsider the pagebreak and may come
107 % to the result that some of the lines that have appeared as perfect
108 % pagebreaks are not so perfect...
109 % Therefore, before we re-inject the whole vertical list and remove the
110 % negative penalty by saying
111 % \unvbox255 \advance\outputpenalty by XXXXXX \penalty\outputpenalty
112 % we have to know in advance, what the pagebuilder will think of the
113 % pagebreak in the next run. See the TeXbook, p. 111 for the correct
114 % formula.
116 % This is the reason for \showprevgraf. We have two counters:
117 % - \myprevgraf counts real lines and displays. This keeps us up-to-date
118 % with the paragraph all the time
119 % - \showprevgraf only counts what will be visible on the current page. It
120 % will be reset to \myprevgraf after the pagebreak.
122 % 7. Question: What do you do to \deadcycles?
123 % Answer: \deadcycles is advanced every time an \output routine has
124 % re-inserted \box255 into the recent contributions. We have to do this for
125 % every line and display math. Therefore, we are restricted to
126 % \maxdeadcycles lines or displays :-(
127 % To avoid this, we cheat the counting of deadcycles and re-advance
128 % \deadcycles by -1. Please, be careful with this hack!
129 % }}}
130 \def\setshowprevgraf#1{%
131 % \futurecost will be the cost in the next step (TeXBook, p.111)
132 \futurecost=#1
133 \advance\futurecost by\outputpenalty
134 % redo Knuth's formula for pagebreaking: futurecost (is the penalty), badness and insertpenalties
135 \ifnum\insertpenalties<10000
136 \ifnum\futurecost<-9999
137 \else\ifnum\futurecost<10000
138 \ifnum\badness<10000
139 \advance\futurecost by\badness
140 \else\ifnum\badness=10000
141 \futurecost=100000
142 \else
143 \futurecost=10000000 % infinity
144 \fi\fi
145 \else
146 \futurecost=10000 % this case is not in Knuth's formula !??
147 \fi\fi
148 \else
149 \futurecost=10000000 % infinity
151 % track the leastcost up to now and
152 % set showprevgraf to the current value of myprevgraf
153 \ifnum\futurecost>\leastcost\else
154 \global\leastcost=\futurecost
155 \global\showprevgraf=\myprevgraf
157 % show some debugging info
158 \inform
160 \def\inform{\ifverbose\immediate\write16{%
161 \space myprevgraf=\the\myprevgraf
162 \space showprevgraf=\the\showprevgraf
163 \space futurecost=\the\futurecost
164 \space leastcost=\the\leastcost}\fi
166 % at the end of all input we will need \showprevgraf to be \myprevgraf
167 % \supereject is called by \bye
168 % and explicitly by the textboxes() method
169 \edef\savesupereject{\supereject}
170 \def\supereject{\global\showprevgraf=\myprevgraf\savesupereject}
171 % save whatever someone said to be the output routine
172 % \plainoutput uses \makeheadline and \makefootline
173 \newtoks\saveoutput
174 \def\makeheadline{}
175 \def\makefootline{}
176 \saveoutput=\expandafter{\the\output}
177 \def\linemarker{\PyXMarker{start@\the\parno@\the\myprevgraf}}
178 \output={%
179 \tempcnt=\deadcycles \advance\tempcnt by-1 \deadcycles=\tempcnt%
180 \razor=-50000
181 \ifnum\outputpenalty>\razor
182 %%%%%%%%%% End of the page %%%%%%%%%%
183 \immediate\write16{PyXVariableBox:page=\the\pageno,par=\the\parno,prevgraf=\the\showprevgraf:}%
184 % reset showprevgraf
185 \global\showprevgraf=\myprevgraf
186 \tempcnt=\deadcycles \advance\tempcnt by 1 \deadcycles=\tempcnt
187 % set the outputtype
188 % \global\outputtype=0 % this has no outputtype!! Otherwise, the outputtype of the last page is lost
189 % e.g. <textline> <pagebreak> <textline> <display> makes
190 % <\interlinepenalty (1)> <pagebreak (!!! not 0!!!)> <\predisplaypenalty (should be 5, not 2)>
191 % after the page has been shipped out, we need a new leastcost
192 \global\leastcost=10000000
193 % do whatever someone told to be the output routine
194 \the\saveoutput
195 % and change the \hsize and \vsize
196 \cdr\lovsizes \cdr\lohsizes
197 \else\advance\razor by -100000 \ifnum\outputpenalty>\razor
198 %%%%%%%%%% InterLinePenalty: -100000 %%%%%%%%%%
199 \ifverbose\immediate\write16{******** InterLinePenalty ********}\fi
200 \global\advance\myprevgraf by 1
201 % this will be the cost in the next page break finding:
202 \setshowprevgraf{100000}
203 % set the outputtype
204 \global\outputtype=1
205 % and re-inject the whole page with the original penalty
206 \unvbox255
207 \linemarker
208 % \setbox0=\lastbox%
209 % \setbox0=\hbox to \the\wd0{\PyXMarker{start@\the\parno@\the\myprevgraf}%
210 % \unhbox0%
211 % \PyXMarker{end@\the\parno@\the\myprevgraf}}%
212 % \nointerlineskip\box0%
213 \advance\outputpenalty by 100000 \penalty\outputpenalty
214 \else\advance\razor by -100000 \ifnum\outputpenalty>\razor
215 %%%%%%%%%% PreDisplayPenalty: -200000 %%%%%%%%%%
216 \ifverbose\immediate\write16{******** PreDisplayPenalty ********}\fi
217 \ifnum\outputtype=1
218 \global\advance\myprevgraf by 1 % this is for the last preceding text line
219 \global\outputtype=5
220 % this will be the cost in the next page break finding:
221 \setshowprevgraf{200000}
222 \else\ifnum\outputtype=4
223 \global\advance\myprevgraf by 1 % this is for the first line of the current par
224 % which has no interlinepenalty
225 \global\outputtype=2
226 % this will be the cost in the next page break finding:
227 \setshowprevgraf{200000}
228 \else
229 \global\outputtype=2
230 \def\linemarker{}
231 \fi\fi
232 % and re-inject the whole page with the original penalty
233 \unvbox255
234 \linemarker
235 \advance\outputpenalty by 200000 \penalty\outputpenalty
236 \else\advance\razor by -100000 \ifnum\outputpenalty>\razor
237 %%%%%%%%%% PostDisplayPenalty: -300000 %%%%%%%%%%
238 \ifverbose\immediate\write16{******** PostDisplayPenalty ********}\fi
239 \global\advance\myprevgraf by 3 % all prevgraf for the display comes here, not in PreDisplayPenalty
240 % this will be the cost in the next page break finding:
241 \setshowprevgraf{300000}
242 % set the outputtype
243 \global\outputtype=3
244 % and re-inject the whole page with the original penalty
245 \unvbox255
246 \linemarker
247 \advance\outputpenalty by 300000 \penalty\outputpenalty
248 \else\advance\razor by -100000 \ifnum\outputpenalty>\razor
249 %%%%%%%%%% end of the paragraph: \penalty-400000 %%%%%%%%%%
250 \ifverbose\immediate\write16{******** Penalty ********}\fi
251 \global\advance\myprevgraf by 1
252 % this will be the cost in the next page break finding:
253 \setshowprevgraf{400000}
254 \global\outputtype=4
255 % and re-inject the whole page with the original penalty
256 \unvbox255
257 \linemarker
258 \advance\outputpenalty by 400000 \penalty\outputpenalty
259 \else
260 %%%%%%%%%% some stuff unknown to us %%%%%%%%%%
261 \immediate\write16{******** VEEEEEEERY negative value of outputpenalty: ERROR? ********}%
262 \unvbox255
263 \tempcnt=\deadcycles\advance\tempcnt by 1 \deadcycles=\tempcnt
264 \fi\fi\fi\fi\fi}
265 % }}}
267 % We have to reset certain things at the beginning of {{{
268 % every paragraph: Hook into it with \everypar
269 \newtoks\saveeverypar
270 \saveeverypar=\expandafter{\the\everypar}
271 \everypar={%
272 \global\advance\parno by 1
273 \global\myprevgraf=0
274 \global\showprevgraf=0
275 % \global\leastcost=10000000
276 % check if the following paragraph will need a \parshape
277 % if yes, say the first token of \parshapes
278 % if no, restore the parno number into \parnos.
279 % This has to come in \everypar to avoid content after the last \par
280 \tempcnt=\numcdr\parnos
281 \ifnum\tempcnt=\parno \cdr\parshapes
282 \else \numconcat\tempcnt\parnos
284 %PAR--\the\parno--%
285 \the\saveeverypar}
286 % }}}
288 % \par calls the pagebuilder and \output {{{
289 % We wrap the \par primitive
290 % - set \interlinepenalty first and reset it afterwards
291 % - call \output another time for the last line of the paragraph
292 \let\savepar\par
293 \def\par{%
294 \ifvmode\savepar\else\ifinner\savepar\else
295 % \global\leastcost=10000000
296 \advance\interlinepenalty by -100000
297 \vadjust{\penalty-400000}%
298 \savepar
299 \advance\interlinepenalty by 100000
300 \fi\fi}
301 % }}}
303 % $$ also calls the pagebuilder and \output {{{
304 % We wrap these characters with newly defined commands
305 % \display and \enddisplay
306 % XXX Can $ be redefined to achieve the same result?
307 % - $$ (begin) calls the pagebuilder for the previous text
308 % we have to do similar things as in \par
309 % - $$ (end) calls the pagebuilder for the display math
310 % we have to adjust \predisplaypenalty and \postdisplaypenalty
311 % XXX Has \interdisplaylinepenalty any effect on \prevgraf ?
312 \def\display{%
313 % \global\leastcost=10000000
314 \global\advance\interlinepenalty by -100000
316 \global\advance\interlinepenalty by 100000\relax}
317 \def\enddisplay{%
318 % \global\leastcost=10000000
319 \global\advance\predisplaypenalty by -200000
320 \global\advance\postdisplaypenalty by -300000
322 \global\advance\predisplaypenalty by 200000
323 \global\advance\postdisplaypenalty by 300000\relax}
324 % }}}
326 % vim:fdm=marker