% 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