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
|
% Modified \output-routine for the use with PyX
% this file makes changes of \hsize and \vsizes possible after
% every pagebreak
%
% Copyright (C) 2004 Michael Schindler <m-schindler@sourceforge.net>
%
% This file is part of PyX (http://pyx.sourceforge.net/).
%
% PyX is free software; you can redistribute it and/or modify
% it under the terms of the GNU General Public License as published by
% the Free Software Foundation; either version 2 of the License, or
% (at your option) any later version.
%
% PyX is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
% GNU General Public License for more details.
%
% You should have received a copy of the GNU General Public License
% along with PyX; if not, write to the Free Software
% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
%
%
\newtoks\lovsizes
\newtoks\lohsizes
\newtoks\parnos
\newtoks\parshapes
%
% Please note that all four tokenlists have to end with {\relax}
% This is needed for correct shortening
%
%%%%%%%%%% TeX's part %%%%%%%%%%
\newif\ifverbose \verbosefalse
\newcount\tempcnt
\newcount\parno \parno=1
\newcount\myprevgraf \myprevgraf=0
\newcount\showprevgraf \showprevgraf=0
\newcount\outputtype \outputtype=0
\newcount\razor
\newcount\leastcost \leastcost=10000000
\newcount\futurecost
%
% Tools for splitting and merging tokenlists after their first element {{{
% did I learn too much lisp or what?
\def\cdr#1{\expandafter\precdr\expandafter#1\the#1@}
\def\numcdr#1{\expandafter\prenumcdr\expandafter#1\the#1@}
\def\numconcat#1#2{\edef\foo{{\the#1}\the#2}\global#2=\expandafter{\foo}}
%
\def\precdr#1#2#3@{\ifx#2\relax\relax\global#1={\relax}\else#2\global#1={#3}\fi}
\def\prenumcdr#1#2#3@{\ifx\relax#2 0\global#1={\relax}\else#2\global#1={#3}\fi}
% }}}
%
% The output routine {{{
% Documentation {{{
% 1. Question: Who calls the pagebuilder and \output?
% Answer: Only \par and $$ (as far as I have read the TeXbook)
%
% 2. Question: So, all together: What is to be done?
% Answer: We have to simulate the work of the linebuilder that makes
% paragraphs and math into lines in order to find out, what value \prevgraf
% might have at a pagepreak. Then, we change the \hsize and \vsize at the
% pagepreak and inform PyX about the value of \prevgraf.
%
% 3. Question: When is \prevgraf advanced?
% Answer: At every line by 1 and at every display math by 3
%
% 4. Question: How can we hook into the pagebuilding mechanism to find out
% about lines and displays?
% Answer: We get into the pagebuilder that splits vertical lists into pages
% if we enforce a penalty of less than -10000. There are three built-in
% hooks: \interlinepenalty
% \predisplaypenalty
% \postdisplaypenalty
% These are set to values less than -10000 by wrapping \par and $$, the
% ones who call the pagebuilder and, therefore, \output
%
% Unfortunately, we get only "breaks", but we are interested in lines. At
% each list, we have one break less than lines. So, we have to add another
% call of \output at the end of a paragraph. This is done by an explicit
% \penalty
% A display math succeeding some text also calls the pagebuilder. We have
% to add another line in such situations. This is done in the output
% routine while processing \predisplaypenalty.
%
% 5. Question: Why are there so many \ifnum s in the output routine?
% Answer: As explained avove, we have to do different things at different
% positions of our text. \interlinepenalty and the \penalty for paragraph
% endings have to add a line. \postdisplaypenalty has to add the display
% (advances \prevgraf by 3), while \predisplaypenalty may still reside on
% the previous page, while the display math has moved to the next one.
% Therefore, it does not contribute to the \prevgraf. Nevertheless, if
% there was some text before the display math in the same paragraph, we
% still have to add the last line of text!
%
% This is the reason for the whole \outputtype business:
% We have to show a \prevgraf value different to \myprevgraf at a situation
% where an \interlinepenalty is followed by a \predisplaypenalty which is
% followed by a pagebreak. The display is on the next page.
% The same can -- of course -- also occur with a \predisplaypenalty
% following a \penalty caused by \par
%
% 6. Question: What the hell does \leastcost do?
% Answer: This is a tricky point. We hooked into the pagebuilding mechanism
% by changing penalties. In this case, TeX thinks, there is a very good
% pagebreak and calls \output. But \output removes the strongly negative
% penalty we added. Now, TeX has to reconsider the pagebreak and may come
% to the result that some of the lines that have appeared as perfect
% pagebreaks are not so perfect...
% Therefore, before we re-inject the whole vertical list and remove the
% negative penalty by saying
% \unvbox255 \advance\outputpenalty by XXXXXX \penalty\outputpenalty
% we have to know in advance, what the pagebuilder will think of the
% pagebreak in the next run. See the TeXbook, p. 111 for the correct
% formula.
%
% This is the reason for \showprevgraf. We have two counters:
% - \myprevgraf counts real lines and displays. This keeps us up-to-date
% with the paragraph all the time
% - \showprevgraf only counts what will be visible on the current page. It
% will be reset to \myprevgraf after the pagebreak.
%
% 7. Question: What do you do to \deadcycles?
% Answer: \deadcycles is advanced every time an \output routine has
% re-inserted \box255 into the recent contributions. We have to do this for
% every line and display math. Therefore, we are restricted to
% \maxdeadcycles lines or displays :-(
% To avoid this, we cheat the counting of deadcycles and re-advance
% \deadcycles by -1. Please, be careful with this hack!
% }}}
\def\setshowprevgraf#1{%
% \futurecost will be the cost in the next step (TeXBook, p.111)
\futurecost=#1
\advance\futurecost by\outputpenalty
% redo Knuth's formula for pagebreaking: futurecost (is the penalty), badness and insertpenalties
\ifnum\insertpenalties<10000
\ifnum\futurecost<-9999
\else\ifnum\futurecost<10000
\ifnum\badness<10000
\advance\futurecost by\badness
\else\ifnum\badness=10000
\futurecost=100000
\else
\futurecost=10000000 % infinity
\fi\fi
\else
\futurecost=10000 % this case is not in Knuth's formula !??
\fi\fi
\else
\futurecost=10000000 % infinity
\fi
% track the leastcost up to now and
% set showprevgraf to the current value of myprevgraf
\ifnum\futurecost>\leastcost\else
\global\leastcost=\futurecost
\global\showprevgraf=\myprevgraf
\fi
% show some debugging info
\inform
}
\def\inform{\ifverbose\immediate\write16{%
\space myprevgraf=\the\myprevgraf
\space showprevgraf=\the\showprevgraf
\space futurecost=\the\futurecost
\space leastcost=\the\leastcost}\fi
}
% at the end of all input we will need \showprevgraf to be \myprevgraf
% \supereject is called by \bye
% and explicitly by the textboxes() method
\edef\savesupereject{\supereject}
\def\supereject{\global\showprevgraf=\myprevgraf\savesupereject}
% save whatever someone said to be the output routine
% \plainoutput uses \makeheadline and \makefootline
\newtoks\saveoutput
\def\makeheadline{}
\def\makefootline{}
\saveoutput=\expandafter{\the\output}
\def\linemarker{\PyXMarker{start@\the\parno@\the\myprevgraf}}
\output={%
\tempcnt=\deadcycles \advance\tempcnt by-1 \deadcycles=\tempcnt%
\razor=-50000
\ifnum\outputpenalty>\razor
%%%%%%%%%% End of the page %%%%%%%%%%
\immediate\write16{PyXVariableBox:page=\the\pageno,par=\the\parno,prevgraf=\the\showprevgraf:}%
% reset showprevgraf
\global\showprevgraf=\myprevgraf
\tempcnt=\deadcycles \advance\tempcnt by 1 \deadcycles=\tempcnt
% set the outputtype
% \global\outputtype=0 % this has no outputtype!! Otherwise, the outputtype of the last page is lost
% e.g. <textline> <pagebreak> <textline> <display> makes
% <\interlinepenalty (1)> <pagebreak (!!! not 0!!!)> <\predisplaypenalty (should be 5, not 2)>
% after the page has been shipped out, we need a new leastcost
\global\leastcost=10000000
% do whatever someone told to be the output routine
\the\saveoutput
% and change the \hsize and \vsize
\cdr\lovsizes \cdr\lohsizes
\else\advance\razor by -100000 \ifnum\outputpenalty>\razor
%%%%%%%%%% InterLinePenalty: -100000 %%%%%%%%%%
\ifverbose\immediate\write16{******** InterLinePenalty ********}\fi
\global\advance\myprevgraf by 1
% this will be the cost in the next page break finding:
\setshowprevgraf{100000}
% set the outputtype
\global\outputtype=1
% and re-inject the whole page with the original penalty
\unvbox255
\linemarker
% \setbox0=\lastbox%
% \setbox0=\hbox to \the\wd0{\PyXMarker{start@\the\parno@\the\myprevgraf}%
% \unhbox0%
% \PyXMarker{end@\the\parno@\the\myprevgraf}}%
% \nointerlineskip\box0%
\advance\outputpenalty by 100000 \penalty\outputpenalty
\else\advance\razor by -100000 \ifnum\outputpenalty>\razor
%%%%%%%%%% PreDisplayPenalty: -200000 %%%%%%%%%%
\ifverbose\immediate\write16{******** PreDisplayPenalty ********}\fi
\ifnum\outputtype=1
\global\advance\myprevgraf by 1 % this is for the last preceding text line
\global\outputtype=5
% this will be the cost in the next page break finding:
\setshowprevgraf{200000}
\else\ifnum\outputtype=4
\global\advance\myprevgraf by 1 % this is for the first line of the current par
% which has no interlinepenalty
\global\outputtype=2
% this will be the cost in the next page break finding:
\setshowprevgraf{200000}
\else
\global\outputtype=2
\def\linemarker{}
\fi\fi
% and re-inject the whole page with the original penalty
\unvbox255
\linemarker
\advance\outputpenalty by 200000 \penalty\outputpenalty
\else\advance\razor by -100000 \ifnum\outputpenalty>\razor
%%%%%%%%%% PostDisplayPenalty: -300000 %%%%%%%%%%
\ifverbose\immediate\write16{******** PostDisplayPenalty ********}\fi
\global\advance\myprevgraf by 3 % all prevgraf for the display comes here, not in PreDisplayPenalty
% this will be the cost in the next page break finding:
\setshowprevgraf{300000}
% set the outputtype
\global\outputtype=3
% and re-inject the whole page with the original penalty
\unvbox255
\linemarker
\advance\outputpenalty by 300000 \penalty\outputpenalty
\else\advance\razor by -100000 \ifnum\outputpenalty>\razor
%%%%%%%%%% end of the paragraph: \penalty-400000 %%%%%%%%%%
\ifverbose\immediate\write16{******** Penalty ********}\fi
\global\advance\myprevgraf by 1
% this will be the cost in the next page break finding:
\setshowprevgraf{400000}
\global\outputtype=4
% and re-inject the whole page with the original penalty
\unvbox255
\linemarker
\advance\outputpenalty by 400000 \penalty\outputpenalty
\else
%%%%%%%%%% some stuff unknown to us %%%%%%%%%%
\immediate\write16{******** VEEEEEEERY negative value of outputpenalty: ERROR? ********}%
\unvbox255
\tempcnt=\deadcycles\advance\tempcnt by 1 \deadcycles=\tempcnt
\fi\fi\fi\fi\fi}
% }}}
%
% We have to reset certain things at the beginning of {{{
% every paragraph: Hook into it with \everypar
\newtoks\saveeverypar
\saveeverypar=\expandafter{\the\everypar}
\everypar={%
\global\advance\parno by 1
\global\myprevgraf=0
\global\showprevgraf=0
% \global\leastcost=10000000
% check if the following paragraph will need a \parshape
% if yes, say the first token of \parshapes
% if no, restore the parno number into \parnos.
% This has to come in \everypar to avoid content after the last \par
\tempcnt=\numcdr\parnos
\ifnum\tempcnt=\parno \cdr\parshapes
\else \numconcat\tempcnt\parnos
\fi
%PAR--\the\parno--%
\the\saveeverypar}
% }}}
%
% \par calls the pagebuilder and \output {{{
% We wrap the \par primitive
% - set \interlinepenalty first and reset it afterwards
% - call \output another time for the last line of the paragraph
\let\savepar\par
\def\par{%
\ifvmode\savepar\else\ifinner\savepar\else
% \global\leastcost=10000000
\advance\interlinepenalty by -100000
\vadjust{\penalty-400000}%
\savepar
\advance\interlinepenalty by 100000
\fi\fi}
% }}}
%
% $$ also calls the pagebuilder and \output {{{
% We wrap these characters with newly defined commands
% \display and \enddisplay
% XXX Can $ be redefined to achieve the same result?
% - $$ (begin) calls the pagebuilder for the previous text
% we have to do similar things as in \par
% - $$ (end) calls the pagebuilder for the display math
% we have to adjust \predisplaypenalty and \postdisplaypenalty
% XXX Has \interdisplaylinepenalty any effect on \prevgraf ?
\def\display{%
% \global\leastcost=10000000
\global\advance\interlinepenalty by -100000
$$%
\global\advance\interlinepenalty by 100000\relax}
\def\enddisplay{%
% \global\leastcost=10000000
\global\advance\predisplaypenalty by -200000
\global\advance\postdisplaypenalty by -300000
$$%
\global\advance\predisplaypenalty by 200000
\global\advance\postdisplaypenalty by 300000\relax}
% }}}
%
% vim:fdm=marker
|