Why is an empty line added when insert is used?Whatsits: when are they used in practice?Make tocloft insert empty page after the ToCHow remove empty line between two subsections?Why does TeX attempt to insert 'missing' tokens?why I can't use footnote(insert) in nested boxes?Insert empty pages until certain page numberscrartcl / section / itemize: Why empty page?Why is the bye command not used by LaTeX?Empty last line before page breaks using mdframedHorizontal space problem when the float environment isn't used
How to determine the optimal threshold to achieve the highest accuracy
How could a medieval fortress manage large groups of migrants and travelers?
Why did Steve Rogers choose this character in Endgame?
Kepler space telescope planets detection
Why don't commercial aircraft adopt a slightly more seaplane-like design to allow safer ditching in case of emergency?
License validity of unreleased project
How fast does a character need to move to be effectively invisible?
Do dragons smell of lilacs?
Finding the package which provides a given command
Is it rude to refer to janitors as 'floor people'?
What happens when I team swap while I have Pokemon inside a gym?
Why is Katakana not pronounced Katagana?
Can you perfectly wrap a cube with this blocky shape?
Unix chat server making communication between terminals possible
How to delete certain lists from a nested list?
Why do candidates not quit if they no longer have a realistic chance to win in the 2020 US presidents election
What details should I consider before agreeing for part of my salary to be 'retained' by employer?
How to ask my office to remove the pride decorations without appearing anti-LGBTQ?
Animal Shelter Management C++
What is the meaning of [[:space:]] in bash?
Is there any conditions on a finite abelian group so that it cannot be class group of any number field?
When does Fisher's "go get more data" approach make sense?
Can I remove the doors before installing a sliding patio doors frame?
Does the Intel 8085 CPU use real memory addresses?
Why is an empty line added when insert is used?
Whatsits: when are they used in practice?Make tocloft insert empty page after the ToCHow remove empty line between two subsections?Why does TeX attempt to insert 'missing' tokens?why I can't use footnote(insert) in nested boxes?Insert empty pages until certain page numberscrartcl / section / itemize: Why empty page?Why is the bye command not used by LaTeX?Empty last line before page breaks using mdframedHorizontal space problem when the float environment isn't used
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty margin-bottom:0;
Consider the following example:
tracingonline=1
tracingoutput=1
gdefmakefootline gdefmakeheadline
inserttopinsvbox tovsizehrule height vsize width 300pt
end
The log file contains this:
Completed box being shipped out [1]
vbox(643.20255+0.0)x300.0
.vbox(643.20255+0.0)x300.0
..vbox(643.20255+0.0)x300.0
...rule(643.20255+0.0)x300.0
Completed box being shipped out [2]
vbox(643.20255+0.0)x469.75499
.vbox(643.20255+0.0)x469.75499, glue set 633.20255fill
..glue(topskip) 10.0
..hbox(0.0+0.0)x469.75499
..glue 0.0 plus 1.0fill
It is not clear where the elements on page 2 come from (topskip
, line
and vfill
). This behavior is not documented in chapter 15 of The TeXbook.
floats page-breaking tex-core plain-tex
add a comment |
Consider the following example:
tracingonline=1
tracingoutput=1
gdefmakefootline gdefmakeheadline
inserttopinsvbox tovsizehrule height vsize width 300pt
end
The log file contains this:
Completed box being shipped out [1]
vbox(643.20255+0.0)x300.0
.vbox(643.20255+0.0)x300.0
..vbox(643.20255+0.0)x300.0
...rule(643.20255+0.0)x300.0
Completed box being shipped out [2]
vbox(643.20255+0.0)x469.75499
.vbox(643.20255+0.0)x469.75499, glue set 633.20255fill
..glue(topskip) 10.0
..hbox(0.0+0.0)x469.75499
..glue 0.0 plus 1.0fill
It is not clear where the elements on page 2 come from (topskip
, line
and vfill
). This behavior is not documented in chapter 15 of The TeXbook.
floats page-breaking tex-core plain-tex
add a comment |
Consider the following example:
tracingonline=1
tracingoutput=1
gdefmakefootline gdefmakeheadline
inserttopinsvbox tovsizehrule height vsize width 300pt
end
The log file contains this:
Completed box being shipped out [1]
vbox(643.20255+0.0)x300.0
.vbox(643.20255+0.0)x300.0
..vbox(643.20255+0.0)x300.0
...rule(643.20255+0.0)x300.0
Completed box being shipped out [2]
vbox(643.20255+0.0)x469.75499
.vbox(643.20255+0.0)x469.75499, glue set 633.20255fill
..glue(topskip) 10.0
..hbox(0.0+0.0)x469.75499
..glue 0.0 plus 1.0fill
It is not clear where the elements on page 2 come from (topskip
, line
and vfill
). This behavior is not documented in chapter 15 of The TeXbook.
floats page-breaking tex-core plain-tex
Consider the following example:
tracingonline=1
tracingoutput=1
gdefmakefootline gdefmakeheadline
inserttopinsvbox tovsizehrule height vsize width 300pt
end
The log file contains this:
Completed box being shipped out [1]
vbox(643.20255+0.0)x300.0
.vbox(643.20255+0.0)x300.0
..vbox(643.20255+0.0)x300.0
...rule(643.20255+0.0)x300.0
Completed box being shipped out [2]
vbox(643.20255+0.0)x469.75499
.vbox(643.20255+0.0)x469.75499, glue set 633.20255fill
..glue(topskip) 10.0
..hbox(0.0+0.0)x469.75499
..glue 0.0 plus 1.0fill
It is not clear where the elements on page 2 come from (topskip
, line
and vfill
). This behavior is not documented in chapter 15 of The TeXbook.
floats page-breaking tex-core plain-tex
floats page-breaking tex-core plain-tex
edited Jul 8 at 14:52
Peter Mortensen
5563 silver badges7 bronze badges
5563 silver badges7 bronze badges
asked Jul 8 at 5:39
Igor LiferenkoIgor Liferenko
2,5568 silver badges30 bronze badges
2,5568 silver badges30 bronze badges
add a comment |
add a comment |
2 Answers
2
active
oldest
votes
I'll try to explain this using quotes from the TeXbook.
Summary
When the main vertical list isn't empty at the point where the end
token is digested, TeX inserts the equivalent of linevfillpenalty-'10000000000
into the main vertical list, exercises the page builder and prepares to read end
again (TeXbook pp. 264 and 283). In your example, this line
brings the first box to the main vertical list, because the insertion item is not a box item. As a consequence, TeX prepends topskip
glue to this box, which is 10pt
here. This topskip
glue is a valid breakpoint because it is immediately preceded by the insertion item; the associated cost is zero. The next legal breakpoint is the vfill
glue following the hbox
that corresponds to the aforementioned line
. The cost associated with this vfill
glue is infinite because the associated penalty is zero and the page would be overfull should a break occur at the vfill
item (i.e., b = ∞; this is because the insertion plus the topskip
glue plus the empty hbox
are 10 points too high in total to fit on the page). Therefore TeX breaks a new page at the best breakpoint seen so far, which is the topskip
glue. Thus, page 1 only contains material from the insertion (the output
routine wraps this material inside two vbox
es). Page 2 contains a new topskip
glue item, the empty hbox
and the vfill
glue item.
Analysis
As said above, if the main vertical list isn't empty at the point where the end
token is digested, TeX inserts the equivalent of linevfillpenalty-'10000000000
into the main vertical list, exercises the page builder and prepares to read end
again. In your example, this brings 10 points of topskip
glue followed by an empty hbox to hsize
onto the current page (among others). In this particular case, the topskip
glue item is a valid breakpoint1 and TeX immediately computes the associated cost (remember that it is exercising the page builder as part of the special end
processing); it finds that this cost is zero, because the insertion is exactly vsize
high. Thus, the conditions for starting a new page are not fulfilled yet (TeXbook p. 112, § 2).
Therefore, TeX continues with the implicitly-added items. After the empty hbox
comes the vfill
glue: it moves these two items from “recent contributions” to the “current page” list. The hbox
is not a legal breakpoint, but the vfill
item is, since it is immediately preceded by a non-discardable item (the hbox
). TeX computes the cost c associated with this second legal breakpoint and finds c = ∞, because there is 10pt
material in excess on the current page, up to and excluding the breakpoint (the page goal was decreased by vsize
and became 0pt
when the insertion item was moved to the “current page,” and the page total went from 0pt
to 10pt
with the topskip
glue and remained unchanged after the empty hbox
). According to the rules given on p. 112 of the TeXbook, this causes TeX to decide that it is time to break a new page at the best remembered breakpoint, which is the one at the topskip
glue (associated cost: 0).
This glue item from topskip
, the empty hbox
and the vfill
glue item are thus all put back at the top of the “recent contributions” list, followed by the penalty-'10000000000
item that has been waiting there all the time since end
inserted it (cf. long paragraph p. 125). Since holdinginserts
is 0
by default, the insertion item is then removed from the “current page,” its contents is appended to boxtopins
with no interline glue, the items remaining on the “current page” (none here!) are put together in box255
, and finally the output
routine is invoked and ships out page 1.
Then TeX starts page 2. The first thing it does is to exercise the page builder, since an output
routine has just ended (cf. p. 122, § 2, item (e), reproduced in footnote 3). So, TeX tries to move items from “recent contributions” to the “current page.” The glue item from topskip
that was put back at the top of the “recent contributions” is discarded at this occasion (there is no box yet on the “current page”) and immediately replaced by a new topskip
glue item,2 since the hbox
item that follows it will be the first box on the current page. TeX moves this box to the current page, followed by the vfill
glue item and the penalty-'10000000000
. The vfill
glue is a legal breakpoint, but it doesn't fulfill the conditions given p. 112 for finishing the page (the associated values are p=0
and c=100000
). The penalty item that follows, on the other hand, does fulfill them (p ≤ −10000).
All remaining items except the breakpoint, namely the penalty-'10000000000
, thus went on page 2. This penalty item is changed into a penalty10000
and put back at the top of the “recent contributions” (TeXbook p. 125), but immediately discarded as the page builder is exercised (again due to § 2 of p. 122, item (e), as reproduced in footnote 3). Because of this, the main vertical list is finally empty and since the output
routine executed shipout
for page 2, deadcycles
is 0
when TeX sees end
for the second time. According to the TeXbook p. 283, this terminates the job.
Confirmation of the analysis with experiments
TeXbook p. 264:
When TeX sees an
end
command, it terminates the job only if the main vertical list has been entirely output and ifdeadcycles=0
. Otherwise it inserts the equivalent of
line vfill penalty-'10000000000
into the main vertical list, and prepares to read the
end
token again.
Notes:
This corresponds to the code posted by David Carlisle.
On page 283, Knuth adds the precision that the page builder is exercised after the aforementioned box/glue/penalty combination has been added to the main vertical list.
We can show that in your example, TeX sees the end
token before the main vertical list has been emptied. In order to do this, insert showlists
before end
in your example and you'll see:
### vertical mode entered at line 0
### current page:
insert253, natural size 643.20255; split(10.0,16383.99998); float cost 0
.vbox(643.20255+0.0)x300.0
..rule(643.20255+0.0)x300.0
total height 0.0
goal height 0.0
insert253 adds 643.20255
prevdepth ignored
./insert.tex:5: OK.
l.5 showlists
In contrast, if you insert nullbreakshowlists
before end
, you'll see:
### vertical mode entered at line 0
prevdepth 0.0
./insert.tex:5: OK.
l.5 nullbreakshowlists
That is an empty vertical list (displayed after page 1 and an underfull page 2 have been shipped out). So, getting back to your example: when TeX sees end
, it doesn't consider yet that the page is full (it would indeed be possible to “rewind” using a box followed by negative kerns or skips), so it doesn't call the output
routine yet. We can insert some more diagnostic tools to confirm the precisions I added here:
tracingonline=1
tracingoutput=1
tracingmacros=2
tracingpages=1
gdefmakefootline gdefmakeheadline
inserttopinsvbox tovsizehrule height vsize width 300pt
showlists
end
which prints:
%% goal height=643.20255, max depth=4.0
### vertical mode entered at line 0
### current page:
insert253, natural size 643.20255; split(10.0,16383.99998); float cost 0
.vbox(643.20255+0.0)x300.0
..rule(643.20255+0.0)x300.0
total height 0.0
goal height 0.0
insert253 adds 643.20255
prevdepth ignored
./insert.tex:7: OK.
l.7 showlists
% t=0.0 g=0.0 b=0 p=0 c=0#
% t=10.0 g=0.0 b=* p=0 c=*
output->plainoutput
plainoutput ->shipout vbox makeheadline pagebody makefootline advancepageno ifnum outputpenalty >-@MM else dosupereject fi
(...)
Completed box being shipped out [1]
vbox(643.20255+0.0)x300.0
.vbox(643.20255+0.0)x300.0
..vbox(643.20255+0.0)x300.0
...rule(643.20255+0.0)x300.0
advancepageno ->ifnum pageno <z@ global advance pageno m@ne else global advance pageno @ne fi
%% goal height=643.20255, max depth=4.0
% t=10.0 g=643.20255 b=10000 p=0 c=100000#
% t=10.0 plus 1.0fill g=643.20255 b=0 p=-1073741824 c=-1073741824#
output->plainoutput
plainoutput ->shipout vbox makeheadline pagebody makefootline advancepageno ifnum outputpenalty >-@MM else dosupereject fi
(...)
Completed box being shipped out [2]
vbox(643.20255+0.0)x469.75499
.vbox(643.20255+0.0)x469.75499, glue set 633.20255fill
..glue(topskip) 10.0
..hbox(0.0+0.0)x469.75499
..glue 0.0 plus 1.0fill
The %% goal height=643.20255, max depth=4.0
line is printed when
the first box or insertion enters the current page list
(TeXbook p. 113). This is your insert
, okay. Right after that, showlists
indeed shows this insert on the current page:
### vertical mode entered at line 0
### current page:
insert253, natural size 643.20255; split(10.0,16383.99998); float cost 0
.vbox(643.20255+0.0)x300.0
(...)
This is because, according to the TeXbook p. 281:
TeX also exercises the page builder (see below), after an
insert
has been appended in vertical mode.
So, the page builder has been exercised before TeX even read showlists
. The insert
has been moved right away from the “recent contributions” to the “current page.” But TeX doesn't consider yet that the page has to be finished, it won't invoke the output
routine yet! Indeed, remember what comes next in the log:
% t=0.0 g=0.0 b=0 p=0 c=0#
% t=10.0 g=0.0 b=* p=0 c=*
output->plainoutput
The first line with g=0
clearly shows that the insert
has been put on the current page, otherwise the page goal would still be 643.20255
points. Now, remember what is said in the TeXbook p. 114:
TeX inserts special glue just before the first box on each page. This special glue is equal to
topskip
, except that the natural space has been decreased by the height of the first box, or it has been set to zero in lieu of a negative value.
The % t=0.0 g=0.0 b=0 p=0 c=0#
line corresponds to the topskip
glue automatically inserted before the box equivalent to line
. It is a legal breakpoint because of the quote from footnote 1 below, and was printed when the page builder was exercised as part of the special processing of end
(otherwise, it would have been printed after the hbox
command had been digested by TeX, according to the TeXbook p. 282). However, the conjunction of p=0
and c=0
at the end of that line implies that TeX sees no reason to break a new page at the best breakpoint seen so far (marked with #
: first legal breakpoint here). Indeed, remember paragraph 2 of page 112:
If the resulting c is less than or equal to the smallest cost seen so far on the current page, TeX remembers the current breakpoint as the best so far. And if c = ∞ or if p ≤ −10000, TeX seizes the initiative and breaks the page at the best remembered breakpoint.
(the last “if” is most probably an “if and only if”). The topskip
breakpoint being a glue item, its associated p value is 0; since the insertion is exactly vsize
high, breaking at this topskip
item would cause a page badness equal to zero (b=0
), therefore the cost c associated with the first breakpoint is indeed c=0
according to the b + p + q expression from page 111 (insertpenalties
is zero after the insert
).
So, after putting the topskip
glue on the “current page” due to the special end
processing and computing the cost associated with this potential breakpoint, TeX is still waiting for more material before deciding that a page break has to happen. So, it takes the next items inserted in “recent contributions” by end
, and moves them one by one to the “current page”. The hbox
isn't a breakpoint, it is simply moved; the vfill
is also moved—there is no reason to discard it—and is a legal breakpoint, since it is preceded by a non-discardable item (the hbox
). The page builder computes its associated cost:
% t=10.0 g=0.0 b=* p=0 c=*
This line being printed for a glue item, the associated p value is 0
, thus p < 10000. Because of the topskip
glue item (10 points) and the empty hbox
, breaking at the vfill
glue item would cause an overfull page, hence b=*
and the computed cost c=*
. This decides TeX to break the page at the best remembered breakpoint (the topskip
) and finally invoke the output
routine for page 1:
output->plainoutput
plainoutput ->shipout vbox makeheadline pagebody makefootline advancepageno ifnum outputpenalty >-@MM else dosupereject fi
(...)
So, you get a second page containing the (new) topskip
glue item, an empty hbox
equivalent to line
and another glue item from vfill
(all wrapped in two vbox
es: one from plainoutput
and one from pagebody
).
You would like to do something to shipout
page 1 after the insertion, but before end
added the equivalent of line vfill penalty-'10000000000
, right? First idea: append a penalty after the insert
that forces page breaking, e.g., with break
(which is equivalent to penalty -10000
). Alas, this doesn't work because:
A
penalty
seen in vertical mode causes TeX to exercise the page builder (TeXbook p. 280), and therefore to try to move things from the “recent contributions” to the “current page”.This sentence from p. 112 of the TeXbook:
Whenever TeX is moving an item from the top of the “recent contributions” to the bottom of the “current page,” it discards a discardable item (glue, kern, or penalty) if the current page does not contain any boxes.
which is the case here (the insertion item on the “current page” isn't a box item).
Indeed, if we try:
tracingonline=1
gdefmakefootline gdefmakeheadline
inserttopinsvbox tovsizehrule height vsize width 300pt
break
showlists
end
we get to see:
### vertical mode entered at line 0
### current page:
insert253, natural size 643.20255; split(10.0,16383.99998); float cost 0
.vbox(643.20255+0.0)x300.0
..rule(643.20255+0.0)x300.0
total height 0.0
goal height 0.0
insert253 adds 643.20255
prevdepth ignored
./insert.tex:5: OK.
l.5 showlists
As before, the “current page” has the insertion at the point where showlists
is executed, but the penalty has been discarded, as could be guessed from the previous quote. Compare this with:
tracingonline=1
tracingpages=1
gdefmakefootline gdefmakeheadline
inserttopinsvbox tovsizehrule height vsize width 300pt
topskip=0ptnullpenalty100
showlists
end
which gives:
%% goal height=643.20255, max depth=4.0
% t=0.0 g=0.0 b=0 p=0 c=0#
% t=0.0 g=0.0 b=0 p=100 c=100
### vertical mode entered at line 0
### current page:
insert253, natural size 643.20255; split(10.0,16383.99998); float cost 0
.vbox(643.20255+0.0)x300.0
..rule(643.20255+0.0)x300.0
glue(topskip) 0.0
hbox(0.0+0.0)x0.0
penalty 100
total height 0.0
goal height 0.0
insert253 adds 643.20255
prevdepth 0.0
Because of the null
(an empty hbox
), the penalty wasn't discarded this time; it got onto the current page and was noted as being a legal breakpoint (t=0.0 g=0.0 b=0 p=100 c=100
), though not the best seen so far (that one has the trailing #
). And thanks to the topskip=0pt
, we got all this on page 1 (with a positive topskip
, we would have had c = ∞ for the penalty100
breakpoint, thus again a page break at the topskip
glue, then a second page). With topskip=0pt
, the insertion, the topskip
glue and the null
box all fit on the first page. Therefore, all we need to do in order to get only one page of output as you probably wanted, is to trigger a page break after the null
box. We can do this using a penalty, which won't be discarded this time since there is already a box on the “current page list.” Here we go:
tracingonline=1
tracingpages=1
gdefmakefootline gdefmakeheadline
inserttopinsvbox tovsizehrule height vsize width 300pt
topskip=0ptnullbreak
end
which gives the following output:
%% goal height=643.20255, max depth=4.0
% t=0.0 g=0.0 b=0 p=0 c=0#
% t=0.0 g=0.0 b=0 p=-10000 c=-10000#
[1] )
Output written on insert.pdf (1 page, 968 bytes).
Transcript written on insert.log.
TeX Output finished at Mon Jul 8 10:11:58
One page of output, broken at the penalty -10000
from our break
.
Footnotes
We are precisely in the case described page 114 of the TeXbook:
If insertions occur before the first box, the
topskip
glue before that box is considered to be a valid breakpoint; this is the only case in which a completed page might not contain a box.This can be verified with code such as
output=expandaftertheoutputglobaltopskip=6pt
inserted beforeend
and watching the TeX messages printed withtracingonline=1tracingpages=1tracingoutput=1relax
(idea from Igor).Paragraph 2 of the TeXbook p. 122 gives the conditions in which the page builder is exercised:
TeX is liable to invoke an output routine whenever it tries to move something from the list of recent contributions to the current page, because it might discover a page break with c = ∞ then. Here is a list of the times when that can happen: (a) At the beginning or end of a paragraph, provided that this paragraph is being contributed to the main vertical list. (b) At the beginning or end of a displayed equation within such a paragraph. (c) After completing an
halign
in vertical mode. (d) After contributing a box or penalty or insertion to the main vertical list. (e) After anoutput
routine has ended.
There is an error in your answer: "It is this precise topskip that decides TeX to finally call the output routine for page 1: % t=10.0 g=0.0 b=* p=0 c=* ...". In fact,topskip
decides here:% t=0.0 g=0.0 b=0 p=0 c=0#
.% t=10.0 g=0.0 b=* p=0 c=*
is decided byvfill
. See p.113.
– Igor Liferenko
Jul 9 at 5:41
I stand by my assertion: thetopskip
causes 10 points “two much material” on the page (t=10.0 g=0.0
); this explains thec=*
which causes TeX to say “Oh, it's time to break a new page, let's find the best breakpoint we've seen so far and rewind.” The#
marks the best breakpoint seen so far, and it is thetopskip
as show by thet=0.0
on the line ending with#
. AFAICT, thevfill
is not seen at all while processing page 1 of the original example.
– frougon
Jul 9 at 5:50
Please read this sentence on p.113: "However, the % lines are generated by the penalty or glue items that follow the hboxes, not by the boxes themselves." This means that the first%
can be caused only by glue (and this is confirmed by the fact thatshowlists
is between%%
and%
). And the glue is, of course,topskip
. It causes the first%
line. Then comes empty hbox, which stands 10pt lower. Next%
can be caused also only by glue. And that glue isvfill
.
– Igor Liferenko
Jul 9 at 5:57
(was too long to add to the previous comment) The breakpoint itself doesn't stay on the page that is about to be finished: TeX always breaks “right before the chosen breakpoint”; that is why the line ending with#
hast=0.0
instead oft=10.0
.
– frougon
Jul 9 at 5:58
BTW, it's possible to see that oldtopskip
is indeed discarded and newtopskip
is used by adding this beforeend
:topskip=5ptoutput=expandaftertheoutputglobaltopskip=6pt
– Igor Liferenko
Jul 9 at 6:05
|
show 25 more comments
It is tex-the-program's final endgame to flush out the insert:
tex.web has
@ We don't want to leave |main_control| immediately when a |stop| command
is sensed, because it may be necessary to invoke an .\output routine
several times before things really grind to a halt. (The output routine
might even say `.\gdef\end...', to prolong the life of the job.)
Therefore |its_all_over| is |true| only when the current page
and contribution list are empty, and when the last output was not a
``dead cycle.''
@<Declare act...@>=
function its_all_over:boolean; do this when .\end or .\dump occurs
label exit;
begin if privileged then
begin if (page_head=page_tail)and(head=tail)and(dead_cycles=0) then
begin its_all_over:=true; return;
end;
back_input; we will try to end again after ejecting residual material
tail_append(new_null_box);
width(tail):=hsize;
tail_append(new_glue(fill_glue));
tail_append(new_penalty(-@'10000000000));@/
build_page; append .\hbox to \hsize\vfill\penalty-'10000000000
^^^^^^^^^^^^^^^^^^^^^^
end;
its_all_over:=false;
exit:end;
This is mentioned on p.264 of TeXbook. I guess the meaning is that ifinsert
which occupies the whole page is used, there has to be some additional text, so second page is inevitable. Still, I'm not sure I completely understand how the explanation on p.264 applies to OP.
– Igor Liferenko
Jul 8 at 7:26
@IgorLiferenko in your case when it hitsend
the main vertical list is not empty (there is aninsert
node left) so the clause on page 262 means the the line is added andend
is called again
– David Carlisle
Jul 8 at 7:50
add a comment |
Your Answer
StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "85"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);
else
createEditor();
);
function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);
);
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2ftex.stackexchange.com%2fquestions%2f499060%2fwhy-is-an-empty-line-added-when-insert-is-used%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
2 Answers
2
active
oldest
votes
2 Answers
2
active
oldest
votes
active
oldest
votes
active
oldest
votes
I'll try to explain this using quotes from the TeXbook.
Summary
When the main vertical list isn't empty at the point where the end
token is digested, TeX inserts the equivalent of linevfillpenalty-'10000000000
into the main vertical list, exercises the page builder and prepares to read end
again (TeXbook pp. 264 and 283). In your example, this line
brings the first box to the main vertical list, because the insertion item is not a box item. As a consequence, TeX prepends topskip
glue to this box, which is 10pt
here. This topskip
glue is a valid breakpoint because it is immediately preceded by the insertion item; the associated cost is zero. The next legal breakpoint is the vfill
glue following the hbox
that corresponds to the aforementioned line
. The cost associated with this vfill
glue is infinite because the associated penalty is zero and the page would be overfull should a break occur at the vfill
item (i.e., b = ∞; this is because the insertion plus the topskip
glue plus the empty hbox
are 10 points too high in total to fit on the page). Therefore TeX breaks a new page at the best breakpoint seen so far, which is the topskip
glue. Thus, page 1 only contains material from the insertion (the output
routine wraps this material inside two vbox
es). Page 2 contains a new topskip
glue item, the empty hbox
and the vfill
glue item.
Analysis
As said above, if the main vertical list isn't empty at the point where the end
token is digested, TeX inserts the equivalent of linevfillpenalty-'10000000000
into the main vertical list, exercises the page builder and prepares to read end
again. In your example, this brings 10 points of topskip
glue followed by an empty hbox to hsize
onto the current page (among others). In this particular case, the topskip
glue item is a valid breakpoint1 and TeX immediately computes the associated cost (remember that it is exercising the page builder as part of the special end
processing); it finds that this cost is zero, because the insertion is exactly vsize
high. Thus, the conditions for starting a new page are not fulfilled yet (TeXbook p. 112, § 2).
Therefore, TeX continues with the implicitly-added items. After the empty hbox
comes the vfill
glue: it moves these two items from “recent contributions” to the “current page” list. The hbox
is not a legal breakpoint, but the vfill
item is, since it is immediately preceded by a non-discardable item (the hbox
). TeX computes the cost c associated with this second legal breakpoint and finds c = ∞, because there is 10pt
material in excess on the current page, up to and excluding the breakpoint (the page goal was decreased by vsize
and became 0pt
when the insertion item was moved to the “current page,” and the page total went from 0pt
to 10pt
with the topskip
glue and remained unchanged after the empty hbox
). According to the rules given on p. 112 of the TeXbook, this causes TeX to decide that it is time to break a new page at the best remembered breakpoint, which is the one at the topskip
glue (associated cost: 0).
This glue item from topskip
, the empty hbox
and the vfill
glue item are thus all put back at the top of the “recent contributions” list, followed by the penalty-'10000000000
item that has been waiting there all the time since end
inserted it (cf. long paragraph p. 125). Since holdinginserts
is 0
by default, the insertion item is then removed from the “current page,” its contents is appended to boxtopins
with no interline glue, the items remaining on the “current page” (none here!) are put together in box255
, and finally the output
routine is invoked and ships out page 1.
Then TeX starts page 2. The first thing it does is to exercise the page builder, since an output
routine has just ended (cf. p. 122, § 2, item (e), reproduced in footnote 3). So, TeX tries to move items from “recent contributions” to the “current page.” The glue item from topskip
that was put back at the top of the “recent contributions” is discarded at this occasion (there is no box yet on the “current page”) and immediately replaced by a new topskip
glue item,2 since the hbox
item that follows it will be the first box on the current page. TeX moves this box to the current page, followed by the vfill
glue item and the penalty-'10000000000
. The vfill
glue is a legal breakpoint, but it doesn't fulfill the conditions given p. 112 for finishing the page (the associated values are p=0
and c=100000
). The penalty item that follows, on the other hand, does fulfill them (p ≤ −10000).
All remaining items except the breakpoint, namely the penalty-'10000000000
, thus went on page 2. This penalty item is changed into a penalty10000
and put back at the top of the “recent contributions” (TeXbook p. 125), but immediately discarded as the page builder is exercised (again due to § 2 of p. 122, item (e), as reproduced in footnote 3). Because of this, the main vertical list is finally empty and since the output
routine executed shipout
for page 2, deadcycles
is 0
when TeX sees end
for the second time. According to the TeXbook p. 283, this terminates the job.
Confirmation of the analysis with experiments
TeXbook p. 264:
When TeX sees an
end
command, it terminates the job only if the main vertical list has been entirely output and ifdeadcycles=0
. Otherwise it inserts the equivalent of
line vfill penalty-'10000000000
into the main vertical list, and prepares to read the
end
token again.
Notes:
This corresponds to the code posted by David Carlisle.
On page 283, Knuth adds the precision that the page builder is exercised after the aforementioned box/glue/penalty combination has been added to the main vertical list.
We can show that in your example, TeX sees the end
token before the main vertical list has been emptied. In order to do this, insert showlists
before end
in your example and you'll see:
### vertical mode entered at line 0
### current page:
insert253, natural size 643.20255; split(10.0,16383.99998); float cost 0
.vbox(643.20255+0.0)x300.0
..rule(643.20255+0.0)x300.0
total height 0.0
goal height 0.0
insert253 adds 643.20255
prevdepth ignored
./insert.tex:5: OK.
l.5 showlists
In contrast, if you insert nullbreakshowlists
before end
, you'll see:
### vertical mode entered at line 0
prevdepth 0.0
./insert.tex:5: OK.
l.5 nullbreakshowlists
That is an empty vertical list (displayed after page 1 and an underfull page 2 have been shipped out). So, getting back to your example: when TeX sees end
, it doesn't consider yet that the page is full (it would indeed be possible to “rewind” using a box followed by negative kerns or skips), so it doesn't call the output
routine yet. We can insert some more diagnostic tools to confirm the precisions I added here:
tracingonline=1
tracingoutput=1
tracingmacros=2
tracingpages=1
gdefmakefootline gdefmakeheadline
inserttopinsvbox tovsizehrule height vsize width 300pt
showlists
end
which prints:
%% goal height=643.20255, max depth=4.0
### vertical mode entered at line 0
### current page:
insert253, natural size 643.20255; split(10.0,16383.99998); float cost 0
.vbox(643.20255+0.0)x300.0
..rule(643.20255+0.0)x300.0
total height 0.0
goal height 0.0
insert253 adds 643.20255
prevdepth ignored
./insert.tex:7: OK.
l.7 showlists
% t=0.0 g=0.0 b=0 p=0 c=0#
% t=10.0 g=0.0 b=* p=0 c=*
output->plainoutput
plainoutput ->shipout vbox makeheadline pagebody makefootline advancepageno ifnum outputpenalty >-@MM else dosupereject fi
(...)
Completed box being shipped out [1]
vbox(643.20255+0.0)x300.0
.vbox(643.20255+0.0)x300.0
..vbox(643.20255+0.0)x300.0
...rule(643.20255+0.0)x300.0
advancepageno ->ifnum pageno <z@ global advance pageno m@ne else global advance pageno @ne fi
%% goal height=643.20255, max depth=4.0
% t=10.0 g=643.20255 b=10000 p=0 c=100000#
% t=10.0 plus 1.0fill g=643.20255 b=0 p=-1073741824 c=-1073741824#
output->plainoutput
plainoutput ->shipout vbox makeheadline pagebody makefootline advancepageno ifnum outputpenalty >-@MM else dosupereject fi
(...)
Completed box being shipped out [2]
vbox(643.20255+0.0)x469.75499
.vbox(643.20255+0.0)x469.75499, glue set 633.20255fill
..glue(topskip) 10.0
..hbox(0.0+0.0)x469.75499
..glue 0.0 plus 1.0fill
The %% goal height=643.20255, max depth=4.0
line is printed when
the first box or insertion enters the current page list
(TeXbook p. 113). This is your insert
, okay. Right after that, showlists
indeed shows this insert on the current page:
### vertical mode entered at line 0
### current page:
insert253, natural size 643.20255; split(10.0,16383.99998); float cost 0
.vbox(643.20255+0.0)x300.0
(...)
This is because, according to the TeXbook p. 281:
TeX also exercises the page builder (see below), after an
insert
has been appended in vertical mode.
So, the page builder has been exercised before TeX even read showlists
. The insert
has been moved right away from the “recent contributions” to the “current page.” But TeX doesn't consider yet that the page has to be finished, it won't invoke the output
routine yet! Indeed, remember what comes next in the log:
% t=0.0 g=0.0 b=0 p=0 c=0#
% t=10.0 g=0.0 b=* p=0 c=*
output->plainoutput
The first line with g=0
clearly shows that the insert
has been put on the current page, otherwise the page goal would still be 643.20255
points. Now, remember what is said in the TeXbook p. 114:
TeX inserts special glue just before the first box on each page. This special glue is equal to
topskip
, except that the natural space has been decreased by the height of the first box, or it has been set to zero in lieu of a negative value.
The % t=0.0 g=0.0 b=0 p=0 c=0#
line corresponds to the topskip
glue automatically inserted before the box equivalent to line
. It is a legal breakpoint because of the quote from footnote 1 below, and was printed when the page builder was exercised as part of the special processing of end
(otherwise, it would have been printed after the hbox
command had been digested by TeX, according to the TeXbook p. 282). However, the conjunction of p=0
and c=0
at the end of that line implies that TeX sees no reason to break a new page at the best breakpoint seen so far (marked with #
: first legal breakpoint here). Indeed, remember paragraph 2 of page 112:
If the resulting c is less than or equal to the smallest cost seen so far on the current page, TeX remembers the current breakpoint as the best so far. And if c = ∞ or if p ≤ −10000, TeX seizes the initiative and breaks the page at the best remembered breakpoint.
(the last “if” is most probably an “if and only if”). The topskip
breakpoint being a glue item, its associated p value is 0; since the insertion is exactly vsize
high, breaking at this topskip
item would cause a page badness equal to zero (b=0
), therefore the cost c associated with the first breakpoint is indeed c=0
according to the b + p + q expression from page 111 (insertpenalties
is zero after the insert
).
So, after putting the topskip
glue on the “current page” due to the special end
processing and computing the cost associated with this potential breakpoint, TeX is still waiting for more material before deciding that a page break has to happen. So, it takes the next items inserted in “recent contributions” by end
, and moves them one by one to the “current page”. The hbox
isn't a breakpoint, it is simply moved; the vfill
is also moved—there is no reason to discard it—and is a legal breakpoint, since it is preceded by a non-discardable item (the hbox
). The page builder computes its associated cost:
% t=10.0 g=0.0 b=* p=0 c=*
This line being printed for a glue item, the associated p value is 0
, thus p < 10000. Because of the topskip
glue item (10 points) and the empty hbox
, breaking at the vfill
glue item would cause an overfull page, hence b=*
and the computed cost c=*
. This decides TeX to break the page at the best remembered breakpoint (the topskip
) and finally invoke the output
routine for page 1:
output->plainoutput
plainoutput ->shipout vbox makeheadline pagebody makefootline advancepageno ifnum outputpenalty >-@MM else dosupereject fi
(...)
So, you get a second page containing the (new) topskip
glue item, an empty hbox
equivalent to line
and another glue item from vfill
(all wrapped in two vbox
es: one from plainoutput
and one from pagebody
).
You would like to do something to shipout
page 1 after the insertion, but before end
added the equivalent of line vfill penalty-'10000000000
, right? First idea: append a penalty after the insert
that forces page breaking, e.g., with break
(which is equivalent to penalty -10000
). Alas, this doesn't work because:
A
penalty
seen in vertical mode causes TeX to exercise the page builder (TeXbook p. 280), and therefore to try to move things from the “recent contributions” to the “current page”.This sentence from p. 112 of the TeXbook:
Whenever TeX is moving an item from the top of the “recent contributions” to the bottom of the “current page,” it discards a discardable item (glue, kern, or penalty) if the current page does not contain any boxes.
which is the case here (the insertion item on the “current page” isn't a box item).
Indeed, if we try:
tracingonline=1
gdefmakefootline gdefmakeheadline
inserttopinsvbox tovsizehrule height vsize width 300pt
break
showlists
end
we get to see:
### vertical mode entered at line 0
### current page:
insert253, natural size 643.20255; split(10.0,16383.99998); float cost 0
.vbox(643.20255+0.0)x300.0
..rule(643.20255+0.0)x300.0
total height 0.0
goal height 0.0
insert253 adds 643.20255
prevdepth ignored
./insert.tex:5: OK.
l.5 showlists
As before, the “current page” has the insertion at the point where showlists
is executed, but the penalty has been discarded, as could be guessed from the previous quote. Compare this with:
tracingonline=1
tracingpages=1
gdefmakefootline gdefmakeheadline
inserttopinsvbox tovsizehrule height vsize width 300pt
topskip=0ptnullpenalty100
showlists
end
which gives:
%% goal height=643.20255, max depth=4.0
% t=0.0 g=0.0 b=0 p=0 c=0#
% t=0.0 g=0.0 b=0 p=100 c=100
### vertical mode entered at line 0
### current page:
insert253, natural size 643.20255; split(10.0,16383.99998); float cost 0
.vbox(643.20255+0.0)x300.0
..rule(643.20255+0.0)x300.0
glue(topskip) 0.0
hbox(0.0+0.0)x0.0
penalty 100
total height 0.0
goal height 0.0
insert253 adds 643.20255
prevdepth 0.0
Because of the null
(an empty hbox
), the penalty wasn't discarded this time; it got onto the current page and was noted as being a legal breakpoint (t=0.0 g=0.0 b=0 p=100 c=100
), though not the best seen so far (that one has the trailing #
). And thanks to the topskip=0pt
, we got all this on page 1 (with a positive topskip
, we would have had c = ∞ for the penalty100
breakpoint, thus again a page break at the topskip
glue, then a second page). With topskip=0pt
, the insertion, the topskip
glue and the null
box all fit on the first page. Therefore, all we need to do in order to get only one page of output as you probably wanted, is to trigger a page break after the null
box. We can do this using a penalty, which won't be discarded this time since there is already a box on the “current page list.” Here we go:
tracingonline=1
tracingpages=1
gdefmakefootline gdefmakeheadline
inserttopinsvbox tovsizehrule height vsize width 300pt
topskip=0ptnullbreak
end
which gives the following output:
%% goal height=643.20255, max depth=4.0
% t=0.0 g=0.0 b=0 p=0 c=0#
% t=0.0 g=0.0 b=0 p=-10000 c=-10000#
[1] )
Output written on insert.pdf (1 page, 968 bytes).
Transcript written on insert.log.
TeX Output finished at Mon Jul 8 10:11:58
One page of output, broken at the penalty -10000
from our break
.
Footnotes
We are precisely in the case described page 114 of the TeXbook:
If insertions occur before the first box, the
topskip
glue before that box is considered to be a valid breakpoint; this is the only case in which a completed page might not contain a box.This can be verified with code such as
output=expandaftertheoutputglobaltopskip=6pt
inserted beforeend
and watching the TeX messages printed withtracingonline=1tracingpages=1tracingoutput=1relax
(idea from Igor).Paragraph 2 of the TeXbook p. 122 gives the conditions in which the page builder is exercised:
TeX is liable to invoke an output routine whenever it tries to move something from the list of recent contributions to the current page, because it might discover a page break with c = ∞ then. Here is a list of the times when that can happen: (a) At the beginning or end of a paragraph, provided that this paragraph is being contributed to the main vertical list. (b) At the beginning or end of a displayed equation within such a paragraph. (c) After completing an
halign
in vertical mode. (d) After contributing a box or penalty or insertion to the main vertical list. (e) After anoutput
routine has ended.
There is an error in your answer: "It is this precise topskip that decides TeX to finally call the output routine for page 1: % t=10.0 g=0.0 b=* p=0 c=* ...". In fact,topskip
decides here:% t=0.0 g=0.0 b=0 p=0 c=0#
.% t=10.0 g=0.0 b=* p=0 c=*
is decided byvfill
. See p.113.
– Igor Liferenko
Jul 9 at 5:41
I stand by my assertion: thetopskip
causes 10 points “two much material” on the page (t=10.0 g=0.0
); this explains thec=*
which causes TeX to say “Oh, it's time to break a new page, let's find the best breakpoint we've seen so far and rewind.” The#
marks the best breakpoint seen so far, and it is thetopskip
as show by thet=0.0
on the line ending with#
. AFAICT, thevfill
is not seen at all while processing page 1 of the original example.
– frougon
Jul 9 at 5:50
Please read this sentence on p.113: "However, the % lines are generated by the penalty or glue items that follow the hboxes, not by the boxes themselves." This means that the first%
can be caused only by glue (and this is confirmed by the fact thatshowlists
is between%%
and%
). And the glue is, of course,topskip
. It causes the first%
line. Then comes empty hbox, which stands 10pt lower. Next%
can be caused also only by glue. And that glue isvfill
.
– Igor Liferenko
Jul 9 at 5:57
(was too long to add to the previous comment) The breakpoint itself doesn't stay on the page that is about to be finished: TeX always breaks “right before the chosen breakpoint”; that is why the line ending with#
hast=0.0
instead oft=10.0
.
– frougon
Jul 9 at 5:58
BTW, it's possible to see that oldtopskip
is indeed discarded and newtopskip
is used by adding this beforeend
:topskip=5ptoutput=expandaftertheoutputglobaltopskip=6pt
– Igor Liferenko
Jul 9 at 6:05
|
show 25 more comments
I'll try to explain this using quotes from the TeXbook.
Summary
When the main vertical list isn't empty at the point where the end
token is digested, TeX inserts the equivalent of linevfillpenalty-'10000000000
into the main vertical list, exercises the page builder and prepares to read end
again (TeXbook pp. 264 and 283). In your example, this line
brings the first box to the main vertical list, because the insertion item is not a box item. As a consequence, TeX prepends topskip
glue to this box, which is 10pt
here. This topskip
glue is a valid breakpoint because it is immediately preceded by the insertion item; the associated cost is zero. The next legal breakpoint is the vfill
glue following the hbox
that corresponds to the aforementioned line
. The cost associated with this vfill
glue is infinite because the associated penalty is zero and the page would be overfull should a break occur at the vfill
item (i.e., b = ∞; this is because the insertion plus the topskip
glue plus the empty hbox
are 10 points too high in total to fit on the page). Therefore TeX breaks a new page at the best breakpoint seen so far, which is the topskip
glue. Thus, page 1 only contains material from the insertion (the output
routine wraps this material inside two vbox
es). Page 2 contains a new topskip
glue item, the empty hbox
and the vfill
glue item.
Analysis
As said above, if the main vertical list isn't empty at the point where the end
token is digested, TeX inserts the equivalent of linevfillpenalty-'10000000000
into the main vertical list, exercises the page builder and prepares to read end
again. In your example, this brings 10 points of topskip
glue followed by an empty hbox to hsize
onto the current page (among others). In this particular case, the topskip
glue item is a valid breakpoint1 and TeX immediately computes the associated cost (remember that it is exercising the page builder as part of the special end
processing); it finds that this cost is zero, because the insertion is exactly vsize
high. Thus, the conditions for starting a new page are not fulfilled yet (TeXbook p. 112, § 2).
Therefore, TeX continues with the implicitly-added items. After the empty hbox
comes the vfill
glue: it moves these two items from “recent contributions” to the “current page” list. The hbox
is not a legal breakpoint, but the vfill
item is, since it is immediately preceded by a non-discardable item (the hbox
). TeX computes the cost c associated with this second legal breakpoint and finds c = ∞, because there is 10pt
material in excess on the current page, up to and excluding the breakpoint (the page goal was decreased by vsize
and became 0pt
when the insertion item was moved to the “current page,” and the page total went from 0pt
to 10pt
with the topskip
glue and remained unchanged after the empty hbox
). According to the rules given on p. 112 of the TeXbook, this causes TeX to decide that it is time to break a new page at the best remembered breakpoint, which is the one at the topskip
glue (associated cost: 0).
This glue item from topskip
, the empty hbox
and the vfill
glue item are thus all put back at the top of the “recent contributions” list, followed by the penalty-'10000000000
item that has been waiting there all the time since end
inserted it (cf. long paragraph p. 125). Since holdinginserts
is 0
by default, the insertion item is then removed from the “current page,” its contents is appended to boxtopins
with no interline glue, the items remaining on the “current page” (none here!) are put together in box255
, and finally the output
routine is invoked and ships out page 1.
Then TeX starts page 2. The first thing it does is to exercise the page builder, since an output
routine has just ended (cf. p. 122, § 2, item (e), reproduced in footnote 3). So, TeX tries to move items from “recent contributions” to the “current page.” The glue item from topskip
that was put back at the top of the “recent contributions” is discarded at this occasion (there is no box yet on the “current page”) and immediately replaced by a new topskip
glue item,2 since the hbox
item that follows it will be the first box on the current page. TeX moves this box to the current page, followed by the vfill
glue item and the penalty-'10000000000
. The vfill
glue is a legal breakpoint, but it doesn't fulfill the conditions given p. 112 for finishing the page (the associated values are p=0
and c=100000
). The penalty item that follows, on the other hand, does fulfill them (p ≤ −10000).
All remaining items except the breakpoint, namely the penalty-'10000000000
, thus went on page 2. This penalty item is changed into a penalty10000
and put back at the top of the “recent contributions” (TeXbook p. 125), but immediately discarded as the page builder is exercised (again due to § 2 of p. 122, item (e), as reproduced in footnote 3). Because of this, the main vertical list is finally empty and since the output
routine executed shipout
for page 2, deadcycles
is 0
when TeX sees end
for the second time. According to the TeXbook p. 283, this terminates the job.
Confirmation of the analysis with experiments
TeXbook p. 264:
When TeX sees an
end
command, it terminates the job only if the main vertical list has been entirely output and ifdeadcycles=0
. Otherwise it inserts the equivalent of
line vfill penalty-'10000000000
into the main vertical list, and prepares to read the
end
token again.
Notes:
This corresponds to the code posted by David Carlisle.
On page 283, Knuth adds the precision that the page builder is exercised after the aforementioned box/glue/penalty combination has been added to the main vertical list.
We can show that in your example, TeX sees the end
token before the main vertical list has been emptied. In order to do this, insert showlists
before end
in your example and you'll see:
### vertical mode entered at line 0
### current page:
insert253, natural size 643.20255; split(10.0,16383.99998); float cost 0
.vbox(643.20255+0.0)x300.0
..rule(643.20255+0.0)x300.0
total height 0.0
goal height 0.0
insert253 adds 643.20255
prevdepth ignored
./insert.tex:5: OK.
l.5 showlists
In contrast, if you insert nullbreakshowlists
before end
, you'll see:
### vertical mode entered at line 0
prevdepth 0.0
./insert.tex:5: OK.
l.5 nullbreakshowlists
That is an empty vertical list (displayed after page 1 and an underfull page 2 have been shipped out). So, getting back to your example: when TeX sees end
, it doesn't consider yet that the page is full (it would indeed be possible to “rewind” using a box followed by negative kerns or skips), so it doesn't call the output
routine yet. We can insert some more diagnostic tools to confirm the precisions I added here:
tracingonline=1
tracingoutput=1
tracingmacros=2
tracingpages=1
gdefmakefootline gdefmakeheadline
inserttopinsvbox tovsizehrule height vsize width 300pt
showlists
end
which prints:
%% goal height=643.20255, max depth=4.0
### vertical mode entered at line 0
### current page:
insert253, natural size 643.20255; split(10.0,16383.99998); float cost 0
.vbox(643.20255+0.0)x300.0
..rule(643.20255+0.0)x300.0
total height 0.0
goal height 0.0
insert253 adds 643.20255
prevdepth ignored
./insert.tex:7: OK.
l.7 showlists
% t=0.0 g=0.0 b=0 p=0 c=0#
% t=10.0 g=0.0 b=* p=0 c=*
output->plainoutput
plainoutput ->shipout vbox makeheadline pagebody makefootline advancepageno ifnum outputpenalty >-@MM else dosupereject fi
(...)
Completed box being shipped out [1]
vbox(643.20255+0.0)x300.0
.vbox(643.20255+0.0)x300.0
..vbox(643.20255+0.0)x300.0
...rule(643.20255+0.0)x300.0
advancepageno ->ifnum pageno <z@ global advance pageno m@ne else global advance pageno @ne fi
%% goal height=643.20255, max depth=4.0
% t=10.0 g=643.20255 b=10000 p=0 c=100000#
% t=10.0 plus 1.0fill g=643.20255 b=0 p=-1073741824 c=-1073741824#
output->plainoutput
plainoutput ->shipout vbox makeheadline pagebody makefootline advancepageno ifnum outputpenalty >-@MM else dosupereject fi
(...)
Completed box being shipped out [2]
vbox(643.20255+0.0)x469.75499
.vbox(643.20255+0.0)x469.75499, glue set 633.20255fill
..glue(topskip) 10.0
..hbox(0.0+0.0)x469.75499
..glue 0.0 plus 1.0fill
The %% goal height=643.20255, max depth=4.0
line is printed when
the first box or insertion enters the current page list
(TeXbook p. 113). This is your insert
, okay. Right after that, showlists
indeed shows this insert on the current page:
### vertical mode entered at line 0
### current page:
insert253, natural size 643.20255; split(10.0,16383.99998); float cost 0
.vbox(643.20255+0.0)x300.0
(...)
This is because, according to the TeXbook p. 281:
TeX also exercises the page builder (see below), after an
insert
has been appended in vertical mode.
So, the page builder has been exercised before TeX even read showlists
. The insert
has been moved right away from the “recent contributions” to the “current page.” But TeX doesn't consider yet that the page has to be finished, it won't invoke the output
routine yet! Indeed, remember what comes next in the log:
% t=0.0 g=0.0 b=0 p=0 c=0#
% t=10.0 g=0.0 b=* p=0 c=*
output->plainoutput
The first line with g=0
clearly shows that the insert
has been put on the current page, otherwise the page goal would still be 643.20255
points. Now, remember what is said in the TeXbook p. 114:
TeX inserts special glue just before the first box on each page. This special glue is equal to
topskip
, except that the natural space has been decreased by the height of the first box, or it has been set to zero in lieu of a negative value.
The % t=0.0 g=0.0 b=0 p=0 c=0#
line corresponds to the topskip
glue automatically inserted before the box equivalent to line
. It is a legal breakpoint because of the quote from footnote 1 below, and was printed when the page builder was exercised as part of the special processing of end
(otherwise, it would have been printed after the hbox
command had been digested by TeX, according to the TeXbook p. 282). However, the conjunction of p=0
and c=0
at the end of that line implies that TeX sees no reason to break a new page at the best breakpoint seen so far (marked with #
: first legal breakpoint here). Indeed, remember paragraph 2 of page 112:
If the resulting c is less than or equal to the smallest cost seen so far on the current page, TeX remembers the current breakpoint as the best so far. And if c = ∞ or if p ≤ −10000, TeX seizes the initiative and breaks the page at the best remembered breakpoint.
(the last “if” is most probably an “if and only if”). The topskip
breakpoint being a glue item, its associated p value is 0; since the insertion is exactly vsize
high, breaking at this topskip
item would cause a page badness equal to zero (b=0
), therefore the cost c associated with the first breakpoint is indeed c=0
according to the b + p + q expression from page 111 (insertpenalties
is zero after the insert
).
So, after putting the topskip
glue on the “current page” due to the special end
processing and computing the cost associated with this potential breakpoint, TeX is still waiting for more material before deciding that a page break has to happen. So, it takes the next items inserted in “recent contributions” by end
, and moves them one by one to the “current page”. The hbox
isn't a breakpoint, it is simply moved; the vfill
is also moved—there is no reason to discard it—and is a legal breakpoint, since it is preceded by a non-discardable item (the hbox
). The page builder computes its associated cost:
% t=10.0 g=0.0 b=* p=0 c=*
This line being printed for a glue item, the associated p value is 0
, thus p < 10000. Because of the topskip
glue item (10 points) and the empty hbox
, breaking at the vfill
glue item would cause an overfull page, hence b=*
and the computed cost c=*
. This decides TeX to break the page at the best remembered breakpoint (the topskip
) and finally invoke the output
routine for page 1:
output->plainoutput
plainoutput ->shipout vbox makeheadline pagebody makefootline advancepageno ifnum outputpenalty >-@MM else dosupereject fi
(...)
So, you get a second page containing the (new) topskip
glue item, an empty hbox
equivalent to line
and another glue item from vfill
(all wrapped in two vbox
es: one from plainoutput
and one from pagebody
).
You would like to do something to shipout
page 1 after the insertion, but before end
added the equivalent of line vfill penalty-'10000000000
, right? First idea: append a penalty after the insert
that forces page breaking, e.g., with break
(which is equivalent to penalty -10000
). Alas, this doesn't work because:
A
penalty
seen in vertical mode causes TeX to exercise the page builder (TeXbook p. 280), and therefore to try to move things from the “recent contributions” to the “current page”.This sentence from p. 112 of the TeXbook:
Whenever TeX is moving an item from the top of the “recent contributions” to the bottom of the “current page,” it discards a discardable item (glue, kern, or penalty) if the current page does not contain any boxes.
which is the case here (the insertion item on the “current page” isn't a box item).
Indeed, if we try:
tracingonline=1
gdefmakefootline gdefmakeheadline
inserttopinsvbox tovsizehrule height vsize width 300pt
break
showlists
end
we get to see:
### vertical mode entered at line 0
### current page:
insert253, natural size 643.20255; split(10.0,16383.99998); float cost 0
.vbox(643.20255+0.0)x300.0
..rule(643.20255+0.0)x300.0
total height 0.0
goal height 0.0
insert253 adds 643.20255
prevdepth ignored
./insert.tex:5: OK.
l.5 showlists
As before, the “current page” has the insertion at the point where showlists
is executed, but the penalty has been discarded, as could be guessed from the previous quote. Compare this with:
tracingonline=1
tracingpages=1
gdefmakefootline gdefmakeheadline
inserttopinsvbox tovsizehrule height vsize width 300pt
topskip=0ptnullpenalty100
showlists
end
which gives:
%% goal height=643.20255, max depth=4.0
% t=0.0 g=0.0 b=0 p=0 c=0#
% t=0.0 g=0.0 b=0 p=100 c=100
### vertical mode entered at line 0
### current page:
insert253, natural size 643.20255; split(10.0,16383.99998); float cost 0
.vbox(643.20255+0.0)x300.0
..rule(643.20255+0.0)x300.0
glue(topskip) 0.0
hbox(0.0+0.0)x0.0
penalty 100
total height 0.0
goal height 0.0
insert253 adds 643.20255
prevdepth 0.0
Because of the null
(an empty hbox
), the penalty wasn't discarded this time; it got onto the current page and was noted as being a legal breakpoint (t=0.0 g=0.0 b=0 p=100 c=100
), though not the best seen so far (that one has the trailing #
). And thanks to the topskip=0pt
, we got all this on page 1 (with a positive topskip
, we would have had c = ∞ for the penalty100
breakpoint, thus again a page break at the topskip
glue, then a second page). With topskip=0pt
, the insertion, the topskip
glue and the null
box all fit on the first page. Therefore, all we need to do in order to get only one page of output as you probably wanted, is to trigger a page break after the null
box. We can do this using a penalty, which won't be discarded this time since there is already a box on the “current page list.” Here we go:
tracingonline=1
tracingpages=1
gdefmakefootline gdefmakeheadline
inserttopinsvbox tovsizehrule height vsize width 300pt
topskip=0ptnullbreak
end
which gives the following output:
%% goal height=643.20255, max depth=4.0
% t=0.0 g=0.0 b=0 p=0 c=0#
% t=0.0 g=0.0 b=0 p=-10000 c=-10000#
[1] )
Output written on insert.pdf (1 page, 968 bytes).
Transcript written on insert.log.
TeX Output finished at Mon Jul 8 10:11:58
One page of output, broken at the penalty -10000
from our break
.
Footnotes
We are precisely in the case described page 114 of the TeXbook:
If insertions occur before the first box, the
topskip
glue before that box is considered to be a valid breakpoint; this is the only case in which a completed page might not contain a box.This can be verified with code such as
output=expandaftertheoutputglobaltopskip=6pt
inserted beforeend
and watching the TeX messages printed withtracingonline=1tracingpages=1tracingoutput=1relax
(idea from Igor).Paragraph 2 of the TeXbook p. 122 gives the conditions in which the page builder is exercised:
TeX is liable to invoke an output routine whenever it tries to move something from the list of recent contributions to the current page, because it might discover a page break with c = ∞ then. Here is a list of the times when that can happen: (a) At the beginning or end of a paragraph, provided that this paragraph is being contributed to the main vertical list. (b) At the beginning or end of a displayed equation within such a paragraph. (c) After completing an
halign
in vertical mode. (d) After contributing a box or penalty or insertion to the main vertical list. (e) After anoutput
routine has ended.
There is an error in your answer: "It is this precise topskip that decides TeX to finally call the output routine for page 1: % t=10.0 g=0.0 b=* p=0 c=* ...". In fact,topskip
decides here:% t=0.0 g=0.0 b=0 p=0 c=0#
.% t=10.0 g=0.0 b=* p=0 c=*
is decided byvfill
. See p.113.
– Igor Liferenko
Jul 9 at 5:41
I stand by my assertion: thetopskip
causes 10 points “two much material” on the page (t=10.0 g=0.0
); this explains thec=*
which causes TeX to say “Oh, it's time to break a new page, let's find the best breakpoint we've seen so far and rewind.” The#
marks the best breakpoint seen so far, and it is thetopskip
as show by thet=0.0
on the line ending with#
. AFAICT, thevfill
is not seen at all while processing page 1 of the original example.
– frougon
Jul 9 at 5:50
Please read this sentence on p.113: "However, the % lines are generated by the penalty or glue items that follow the hboxes, not by the boxes themselves." This means that the first%
can be caused only by glue (and this is confirmed by the fact thatshowlists
is between%%
and%
). And the glue is, of course,topskip
. It causes the first%
line. Then comes empty hbox, which stands 10pt lower. Next%
can be caused also only by glue. And that glue isvfill
.
– Igor Liferenko
Jul 9 at 5:57
(was too long to add to the previous comment) The breakpoint itself doesn't stay on the page that is about to be finished: TeX always breaks “right before the chosen breakpoint”; that is why the line ending with#
hast=0.0
instead oft=10.0
.
– frougon
Jul 9 at 5:58
BTW, it's possible to see that oldtopskip
is indeed discarded and newtopskip
is used by adding this beforeend
:topskip=5ptoutput=expandaftertheoutputglobaltopskip=6pt
– Igor Liferenko
Jul 9 at 6:05
|
show 25 more comments
I'll try to explain this using quotes from the TeXbook.
Summary
When the main vertical list isn't empty at the point where the end
token is digested, TeX inserts the equivalent of linevfillpenalty-'10000000000
into the main vertical list, exercises the page builder and prepares to read end
again (TeXbook pp. 264 and 283). In your example, this line
brings the first box to the main vertical list, because the insertion item is not a box item. As a consequence, TeX prepends topskip
glue to this box, which is 10pt
here. This topskip
glue is a valid breakpoint because it is immediately preceded by the insertion item; the associated cost is zero. The next legal breakpoint is the vfill
glue following the hbox
that corresponds to the aforementioned line
. The cost associated with this vfill
glue is infinite because the associated penalty is zero and the page would be overfull should a break occur at the vfill
item (i.e., b = ∞; this is because the insertion plus the topskip
glue plus the empty hbox
are 10 points too high in total to fit on the page). Therefore TeX breaks a new page at the best breakpoint seen so far, which is the topskip
glue. Thus, page 1 only contains material from the insertion (the output
routine wraps this material inside two vbox
es). Page 2 contains a new topskip
glue item, the empty hbox
and the vfill
glue item.
Analysis
As said above, if the main vertical list isn't empty at the point where the end
token is digested, TeX inserts the equivalent of linevfillpenalty-'10000000000
into the main vertical list, exercises the page builder and prepares to read end
again. In your example, this brings 10 points of topskip
glue followed by an empty hbox to hsize
onto the current page (among others). In this particular case, the topskip
glue item is a valid breakpoint1 and TeX immediately computes the associated cost (remember that it is exercising the page builder as part of the special end
processing); it finds that this cost is zero, because the insertion is exactly vsize
high. Thus, the conditions for starting a new page are not fulfilled yet (TeXbook p. 112, § 2).
Therefore, TeX continues with the implicitly-added items. After the empty hbox
comes the vfill
glue: it moves these two items from “recent contributions” to the “current page” list. The hbox
is not a legal breakpoint, but the vfill
item is, since it is immediately preceded by a non-discardable item (the hbox
). TeX computes the cost c associated with this second legal breakpoint and finds c = ∞, because there is 10pt
material in excess on the current page, up to and excluding the breakpoint (the page goal was decreased by vsize
and became 0pt
when the insertion item was moved to the “current page,” and the page total went from 0pt
to 10pt
with the topskip
glue and remained unchanged after the empty hbox
). According to the rules given on p. 112 of the TeXbook, this causes TeX to decide that it is time to break a new page at the best remembered breakpoint, which is the one at the topskip
glue (associated cost: 0).
This glue item from topskip
, the empty hbox
and the vfill
glue item are thus all put back at the top of the “recent contributions” list, followed by the penalty-'10000000000
item that has been waiting there all the time since end
inserted it (cf. long paragraph p. 125). Since holdinginserts
is 0
by default, the insertion item is then removed from the “current page,” its contents is appended to boxtopins
with no interline glue, the items remaining on the “current page” (none here!) are put together in box255
, and finally the output
routine is invoked and ships out page 1.
Then TeX starts page 2. The first thing it does is to exercise the page builder, since an output
routine has just ended (cf. p. 122, § 2, item (e), reproduced in footnote 3). So, TeX tries to move items from “recent contributions” to the “current page.” The glue item from topskip
that was put back at the top of the “recent contributions” is discarded at this occasion (there is no box yet on the “current page”) and immediately replaced by a new topskip
glue item,2 since the hbox
item that follows it will be the first box on the current page. TeX moves this box to the current page, followed by the vfill
glue item and the penalty-'10000000000
. The vfill
glue is a legal breakpoint, but it doesn't fulfill the conditions given p. 112 for finishing the page (the associated values are p=0
and c=100000
). The penalty item that follows, on the other hand, does fulfill them (p ≤ −10000).
All remaining items except the breakpoint, namely the penalty-'10000000000
, thus went on page 2. This penalty item is changed into a penalty10000
and put back at the top of the “recent contributions” (TeXbook p. 125), but immediately discarded as the page builder is exercised (again due to § 2 of p. 122, item (e), as reproduced in footnote 3). Because of this, the main vertical list is finally empty and since the output
routine executed shipout
for page 2, deadcycles
is 0
when TeX sees end
for the second time. According to the TeXbook p. 283, this terminates the job.
Confirmation of the analysis with experiments
TeXbook p. 264:
When TeX sees an
end
command, it terminates the job only if the main vertical list has been entirely output and ifdeadcycles=0
. Otherwise it inserts the equivalent of
line vfill penalty-'10000000000
into the main vertical list, and prepares to read the
end
token again.
Notes:
This corresponds to the code posted by David Carlisle.
On page 283, Knuth adds the precision that the page builder is exercised after the aforementioned box/glue/penalty combination has been added to the main vertical list.
We can show that in your example, TeX sees the end
token before the main vertical list has been emptied. In order to do this, insert showlists
before end
in your example and you'll see:
### vertical mode entered at line 0
### current page:
insert253, natural size 643.20255; split(10.0,16383.99998); float cost 0
.vbox(643.20255+0.0)x300.0
..rule(643.20255+0.0)x300.0
total height 0.0
goal height 0.0
insert253 adds 643.20255
prevdepth ignored
./insert.tex:5: OK.
l.5 showlists
In contrast, if you insert nullbreakshowlists
before end
, you'll see:
### vertical mode entered at line 0
prevdepth 0.0
./insert.tex:5: OK.
l.5 nullbreakshowlists
That is an empty vertical list (displayed after page 1 and an underfull page 2 have been shipped out). So, getting back to your example: when TeX sees end
, it doesn't consider yet that the page is full (it would indeed be possible to “rewind” using a box followed by negative kerns or skips), so it doesn't call the output
routine yet. We can insert some more diagnostic tools to confirm the precisions I added here:
tracingonline=1
tracingoutput=1
tracingmacros=2
tracingpages=1
gdefmakefootline gdefmakeheadline
inserttopinsvbox tovsizehrule height vsize width 300pt
showlists
end
which prints:
%% goal height=643.20255, max depth=4.0
### vertical mode entered at line 0
### current page:
insert253, natural size 643.20255; split(10.0,16383.99998); float cost 0
.vbox(643.20255+0.0)x300.0
..rule(643.20255+0.0)x300.0
total height 0.0
goal height 0.0
insert253 adds 643.20255
prevdepth ignored
./insert.tex:7: OK.
l.7 showlists
% t=0.0 g=0.0 b=0 p=0 c=0#
% t=10.0 g=0.0 b=* p=0 c=*
output->plainoutput
plainoutput ->shipout vbox makeheadline pagebody makefootline advancepageno ifnum outputpenalty >-@MM else dosupereject fi
(...)
Completed box being shipped out [1]
vbox(643.20255+0.0)x300.0
.vbox(643.20255+0.0)x300.0
..vbox(643.20255+0.0)x300.0
...rule(643.20255+0.0)x300.0
advancepageno ->ifnum pageno <z@ global advance pageno m@ne else global advance pageno @ne fi
%% goal height=643.20255, max depth=4.0
% t=10.0 g=643.20255 b=10000 p=0 c=100000#
% t=10.0 plus 1.0fill g=643.20255 b=0 p=-1073741824 c=-1073741824#
output->plainoutput
plainoutput ->shipout vbox makeheadline pagebody makefootline advancepageno ifnum outputpenalty >-@MM else dosupereject fi
(...)
Completed box being shipped out [2]
vbox(643.20255+0.0)x469.75499
.vbox(643.20255+0.0)x469.75499, glue set 633.20255fill
..glue(topskip) 10.0
..hbox(0.0+0.0)x469.75499
..glue 0.0 plus 1.0fill
The %% goal height=643.20255, max depth=4.0
line is printed when
the first box or insertion enters the current page list
(TeXbook p. 113). This is your insert
, okay. Right after that, showlists
indeed shows this insert on the current page:
### vertical mode entered at line 0
### current page:
insert253, natural size 643.20255; split(10.0,16383.99998); float cost 0
.vbox(643.20255+0.0)x300.0
(...)
This is because, according to the TeXbook p. 281:
TeX also exercises the page builder (see below), after an
insert
has been appended in vertical mode.
So, the page builder has been exercised before TeX even read showlists
. The insert
has been moved right away from the “recent contributions” to the “current page.” But TeX doesn't consider yet that the page has to be finished, it won't invoke the output
routine yet! Indeed, remember what comes next in the log:
% t=0.0 g=0.0 b=0 p=0 c=0#
% t=10.0 g=0.0 b=* p=0 c=*
output->plainoutput
The first line with g=0
clearly shows that the insert
has been put on the current page, otherwise the page goal would still be 643.20255
points. Now, remember what is said in the TeXbook p. 114:
TeX inserts special glue just before the first box on each page. This special glue is equal to
topskip
, except that the natural space has been decreased by the height of the first box, or it has been set to zero in lieu of a negative value.
The % t=0.0 g=0.0 b=0 p=0 c=0#
line corresponds to the topskip
glue automatically inserted before the box equivalent to line
. It is a legal breakpoint because of the quote from footnote 1 below, and was printed when the page builder was exercised as part of the special processing of end
(otherwise, it would have been printed after the hbox
command had been digested by TeX, according to the TeXbook p. 282). However, the conjunction of p=0
and c=0
at the end of that line implies that TeX sees no reason to break a new page at the best breakpoint seen so far (marked with #
: first legal breakpoint here). Indeed, remember paragraph 2 of page 112:
If the resulting c is less than or equal to the smallest cost seen so far on the current page, TeX remembers the current breakpoint as the best so far. And if c = ∞ or if p ≤ −10000, TeX seizes the initiative and breaks the page at the best remembered breakpoint.
(the last “if” is most probably an “if and only if”). The topskip
breakpoint being a glue item, its associated p value is 0; since the insertion is exactly vsize
high, breaking at this topskip
item would cause a page badness equal to zero (b=0
), therefore the cost c associated with the first breakpoint is indeed c=0
according to the b + p + q expression from page 111 (insertpenalties
is zero after the insert
).
So, after putting the topskip
glue on the “current page” due to the special end
processing and computing the cost associated with this potential breakpoint, TeX is still waiting for more material before deciding that a page break has to happen. So, it takes the next items inserted in “recent contributions” by end
, and moves them one by one to the “current page”. The hbox
isn't a breakpoint, it is simply moved; the vfill
is also moved—there is no reason to discard it—and is a legal breakpoint, since it is preceded by a non-discardable item (the hbox
). The page builder computes its associated cost:
% t=10.0 g=0.0 b=* p=0 c=*
This line being printed for a glue item, the associated p value is 0
, thus p < 10000. Because of the topskip
glue item (10 points) and the empty hbox
, breaking at the vfill
glue item would cause an overfull page, hence b=*
and the computed cost c=*
. This decides TeX to break the page at the best remembered breakpoint (the topskip
) and finally invoke the output
routine for page 1:
output->plainoutput
plainoutput ->shipout vbox makeheadline pagebody makefootline advancepageno ifnum outputpenalty >-@MM else dosupereject fi
(...)
So, you get a second page containing the (new) topskip
glue item, an empty hbox
equivalent to line
and another glue item from vfill
(all wrapped in two vbox
es: one from plainoutput
and one from pagebody
).
You would like to do something to shipout
page 1 after the insertion, but before end
added the equivalent of line vfill penalty-'10000000000
, right? First idea: append a penalty after the insert
that forces page breaking, e.g., with break
(which is equivalent to penalty -10000
). Alas, this doesn't work because:
A
penalty
seen in vertical mode causes TeX to exercise the page builder (TeXbook p. 280), and therefore to try to move things from the “recent contributions” to the “current page”.This sentence from p. 112 of the TeXbook:
Whenever TeX is moving an item from the top of the “recent contributions” to the bottom of the “current page,” it discards a discardable item (glue, kern, or penalty) if the current page does not contain any boxes.
which is the case here (the insertion item on the “current page” isn't a box item).
Indeed, if we try:
tracingonline=1
gdefmakefootline gdefmakeheadline
inserttopinsvbox tovsizehrule height vsize width 300pt
break
showlists
end
we get to see:
### vertical mode entered at line 0
### current page:
insert253, natural size 643.20255; split(10.0,16383.99998); float cost 0
.vbox(643.20255+0.0)x300.0
..rule(643.20255+0.0)x300.0
total height 0.0
goal height 0.0
insert253 adds 643.20255
prevdepth ignored
./insert.tex:5: OK.
l.5 showlists
As before, the “current page” has the insertion at the point where showlists
is executed, but the penalty has been discarded, as could be guessed from the previous quote. Compare this with:
tracingonline=1
tracingpages=1
gdefmakefootline gdefmakeheadline
inserttopinsvbox tovsizehrule height vsize width 300pt
topskip=0ptnullpenalty100
showlists
end
which gives:
%% goal height=643.20255, max depth=4.0
% t=0.0 g=0.0 b=0 p=0 c=0#
% t=0.0 g=0.0 b=0 p=100 c=100
### vertical mode entered at line 0
### current page:
insert253, natural size 643.20255; split(10.0,16383.99998); float cost 0
.vbox(643.20255+0.0)x300.0
..rule(643.20255+0.0)x300.0
glue(topskip) 0.0
hbox(0.0+0.0)x0.0
penalty 100
total height 0.0
goal height 0.0
insert253 adds 643.20255
prevdepth 0.0
Because of the null
(an empty hbox
), the penalty wasn't discarded this time; it got onto the current page and was noted as being a legal breakpoint (t=0.0 g=0.0 b=0 p=100 c=100
), though not the best seen so far (that one has the trailing #
). And thanks to the topskip=0pt
, we got all this on page 1 (with a positive topskip
, we would have had c = ∞ for the penalty100
breakpoint, thus again a page break at the topskip
glue, then a second page). With topskip=0pt
, the insertion, the topskip
glue and the null
box all fit on the first page. Therefore, all we need to do in order to get only one page of output as you probably wanted, is to trigger a page break after the null
box. We can do this using a penalty, which won't be discarded this time since there is already a box on the “current page list.” Here we go:
tracingonline=1
tracingpages=1
gdefmakefootline gdefmakeheadline
inserttopinsvbox tovsizehrule height vsize width 300pt
topskip=0ptnullbreak
end
which gives the following output:
%% goal height=643.20255, max depth=4.0
% t=0.0 g=0.0 b=0 p=0 c=0#
% t=0.0 g=0.0 b=0 p=-10000 c=-10000#
[1] )
Output written on insert.pdf (1 page, 968 bytes).
Transcript written on insert.log.
TeX Output finished at Mon Jul 8 10:11:58
One page of output, broken at the penalty -10000
from our break
.
Footnotes
We are precisely in the case described page 114 of the TeXbook:
If insertions occur before the first box, the
topskip
glue before that box is considered to be a valid breakpoint; this is the only case in which a completed page might not contain a box.This can be verified with code such as
output=expandaftertheoutputglobaltopskip=6pt
inserted beforeend
and watching the TeX messages printed withtracingonline=1tracingpages=1tracingoutput=1relax
(idea from Igor).Paragraph 2 of the TeXbook p. 122 gives the conditions in which the page builder is exercised:
TeX is liable to invoke an output routine whenever it tries to move something from the list of recent contributions to the current page, because it might discover a page break with c = ∞ then. Here is a list of the times when that can happen: (a) At the beginning or end of a paragraph, provided that this paragraph is being contributed to the main vertical list. (b) At the beginning or end of a displayed equation within such a paragraph. (c) After completing an
halign
in vertical mode. (d) After contributing a box or penalty or insertion to the main vertical list. (e) After anoutput
routine has ended.
I'll try to explain this using quotes from the TeXbook.
Summary
When the main vertical list isn't empty at the point where the end
token is digested, TeX inserts the equivalent of linevfillpenalty-'10000000000
into the main vertical list, exercises the page builder and prepares to read end
again (TeXbook pp. 264 and 283). In your example, this line
brings the first box to the main vertical list, because the insertion item is not a box item. As a consequence, TeX prepends topskip
glue to this box, which is 10pt
here. This topskip
glue is a valid breakpoint because it is immediately preceded by the insertion item; the associated cost is zero. The next legal breakpoint is the vfill
glue following the hbox
that corresponds to the aforementioned line
. The cost associated with this vfill
glue is infinite because the associated penalty is zero and the page would be overfull should a break occur at the vfill
item (i.e., b = ∞; this is because the insertion plus the topskip
glue plus the empty hbox
are 10 points too high in total to fit on the page). Therefore TeX breaks a new page at the best breakpoint seen so far, which is the topskip
glue. Thus, page 1 only contains material from the insertion (the output
routine wraps this material inside two vbox
es). Page 2 contains a new topskip
glue item, the empty hbox
and the vfill
glue item.
Analysis
As said above, if the main vertical list isn't empty at the point where the end
token is digested, TeX inserts the equivalent of linevfillpenalty-'10000000000
into the main vertical list, exercises the page builder and prepares to read end
again. In your example, this brings 10 points of topskip
glue followed by an empty hbox to hsize
onto the current page (among others). In this particular case, the topskip
glue item is a valid breakpoint1 and TeX immediately computes the associated cost (remember that it is exercising the page builder as part of the special end
processing); it finds that this cost is zero, because the insertion is exactly vsize
high. Thus, the conditions for starting a new page are not fulfilled yet (TeXbook p. 112, § 2).
Therefore, TeX continues with the implicitly-added items. After the empty hbox
comes the vfill
glue: it moves these two items from “recent contributions” to the “current page” list. The hbox
is not a legal breakpoint, but the vfill
item is, since it is immediately preceded by a non-discardable item (the hbox
). TeX computes the cost c associated with this second legal breakpoint and finds c = ∞, because there is 10pt
material in excess on the current page, up to and excluding the breakpoint (the page goal was decreased by vsize
and became 0pt
when the insertion item was moved to the “current page,” and the page total went from 0pt
to 10pt
with the topskip
glue and remained unchanged after the empty hbox
). According to the rules given on p. 112 of the TeXbook, this causes TeX to decide that it is time to break a new page at the best remembered breakpoint, which is the one at the topskip
glue (associated cost: 0).
This glue item from topskip
, the empty hbox
and the vfill
glue item are thus all put back at the top of the “recent contributions” list, followed by the penalty-'10000000000
item that has been waiting there all the time since end
inserted it (cf. long paragraph p. 125). Since holdinginserts
is 0
by default, the insertion item is then removed from the “current page,” its contents is appended to boxtopins
with no interline glue, the items remaining on the “current page” (none here!) are put together in box255
, and finally the output
routine is invoked and ships out page 1.
Then TeX starts page 2. The first thing it does is to exercise the page builder, since an output
routine has just ended (cf. p. 122, § 2, item (e), reproduced in footnote 3). So, TeX tries to move items from “recent contributions” to the “current page.” The glue item from topskip
that was put back at the top of the “recent contributions” is discarded at this occasion (there is no box yet on the “current page”) and immediately replaced by a new topskip
glue item,2 since the hbox
item that follows it will be the first box on the current page. TeX moves this box to the current page, followed by the vfill
glue item and the penalty-'10000000000
. The vfill
glue is a legal breakpoint, but it doesn't fulfill the conditions given p. 112 for finishing the page (the associated values are p=0
and c=100000
). The penalty item that follows, on the other hand, does fulfill them (p ≤ −10000).
All remaining items except the breakpoint, namely the penalty-'10000000000
, thus went on page 2. This penalty item is changed into a penalty10000
and put back at the top of the “recent contributions” (TeXbook p. 125), but immediately discarded as the page builder is exercised (again due to § 2 of p. 122, item (e), as reproduced in footnote 3). Because of this, the main vertical list is finally empty and since the output
routine executed shipout
for page 2, deadcycles
is 0
when TeX sees end
for the second time. According to the TeXbook p. 283, this terminates the job.
Confirmation of the analysis with experiments
TeXbook p. 264:
When TeX sees an
end
command, it terminates the job only if the main vertical list has been entirely output and ifdeadcycles=0
. Otherwise it inserts the equivalent of
line vfill penalty-'10000000000
into the main vertical list, and prepares to read the
end
token again.
Notes:
This corresponds to the code posted by David Carlisle.
On page 283, Knuth adds the precision that the page builder is exercised after the aforementioned box/glue/penalty combination has been added to the main vertical list.
We can show that in your example, TeX sees the end
token before the main vertical list has been emptied. In order to do this, insert showlists
before end
in your example and you'll see:
### vertical mode entered at line 0
### current page:
insert253, natural size 643.20255; split(10.0,16383.99998); float cost 0
.vbox(643.20255+0.0)x300.0
..rule(643.20255+0.0)x300.0
total height 0.0
goal height 0.0
insert253 adds 643.20255
prevdepth ignored
./insert.tex:5: OK.
l.5 showlists
In contrast, if you insert nullbreakshowlists
before end
, you'll see:
### vertical mode entered at line 0
prevdepth 0.0
./insert.tex:5: OK.
l.5 nullbreakshowlists
That is an empty vertical list (displayed after page 1 and an underfull page 2 have been shipped out). So, getting back to your example: when TeX sees end
, it doesn't consider yet that the page is full (it would indeed be possible to “rewind” using a box followed by negative kerns or skips), so it doesn't call the output
routine yet. We can insert some more diagnostic tools to confirm the precisions I added here:
tracingonline=1
tracingoutput=1
tracingmacros=2
tracingpages=1
gdefmakefootline gdefmakeheadline
inserttopinsvbox tovsizehrule height vsize width 300pt
showlists
end
which prints:
%% goal height=643.20255, max depth=4.0
### vertical mode entered at line 0
### current page:
insert253, natural size 643.20255; split(10.0,16383.99998); float cost 0
.vbox(643.20255+0.0)x300.0
..rule(643.20255+0.0)x300.0
total height 0.0
goal height 0.0
insert253 adds 643.20255
prevdepth ignored
./insert.tex:7: OK.
l.7 showlists
% t=0.0 g=0.0 b=0 p=0 c=0#
% t=10.0 g=0.0 b=* p=0 c=*
output->plainoutput
plainoutput ->shipout vbox makeheadline pagebody makefootline advancepageno ifnum outputpenalty >-@MM else dosupereject fi
(...)
Completed box being shipped out [1]
vbox(643.20255+0.0)x300.0
.vbox(643.20255+0.0)x300.0
..vbox(643.20255+0.0)x300.0
...rule(643.20255+0.0)x300.0
advancepageno ->ifnum pageno <z@ global advance pageno m@ne else global advance pageno @ne fi
%% goal height=643.20255, max depth=4.0
% t=10.0 g=643.20255 b=10000 p=0 c=100000#
% t=10.0 plus 1.0fill g=643.20255 b=0 p=-1073741824 c=-1073741824#
output->plainoutput
plainoutput ->shipout vbox makeheadline pagebody makefootline advancepageno ifnum outputpenalty >-@MM else dosupereject fi
(...)
Completed box being shipped out [2]
vbox(643.20255+0.0)x469.75499
.vbox(643.20255+0.0)x469.75499, glue set 633.20255fill
..glue(topskip) 10.0
..hbox(0.0+0.0)x469.75499
..glue 0.0 plus 1.0fill
The %% goal height=643.20255, max depth=4.0
line is printed when
the first box or insertion enters the current page list
(TeXbook p. 113). This is your insert
, okay. Right after that, showlists
indeed shows this insert on the current page:
### vertical mode entered at line 0
### current page:
insert253, natural size 643.20255; split(10.0,16383.99998); float cost 0
.vbox(643.20255+0.0)x300.0
(...)
This is because, according to the TeXbook p. 281:
TeX also exercises the page builder (see below), after an
insert
has been appended in vertical mode.
So, the page builder has been exercised before TeX even read showlists
. The insert
has been moved right away from the “recent contributions” to the “current page.” But TeX doesn't consider yet that the page has to be finished, it won't invoke the output
routine yet! Indeed, remember what comes next in the log:
% t=0.0 g=0.0 b=0 p=0 c=0#
% t=10.0 g=0.0 b=* p=0 c=*
output->plainoutput
The first line with g=0
clearly shows that the insert
has been put on the current page, otherwise the page goal would still be 643.20255
points. Now, remember what is said in the TeXbook p. 114:
TeX inserts special glue just before the first box on each page. This special glue is equal to
topskip
, except that the natural space has been decreased by the height of the first box, or it has been set to zero in lieu of a negative value.
The % t=0.0 g=0.0 b=0 p=0 c=0#
line corresponds to the topskip
glue automatically inserted before the box equivalent to line
. It is a legal breakpoint because of the quote from footnote 1 below, and was printed when the page builder was exercised as part of the special processing of end
(otherwise, it would have been printed after the hbox
command had been digested by TeX, according to the TeXbook p. 282). However, the conjunction of p=0
and c=0
at the end of that line implies that TeX sees no reason to break a new page at the best breakpoint seen so far (marked with #
: first legal breakpoint here). Indeed, remember paragraph 2 of page 112:
If the resulting c is less than or equal to the smallest cost seen so far on the current page, TeX remembers the current breakpoint as the best so far. And if c = ∞ or if p ≤ −10000, TeX seizes the initiative and breaks the page at the best remembered breakpoint.
(the last “if” is most probably an “if and only if”). The topskip
breakpoint being a glue item, its associated p value is 0; since the insertion is exactly vsize
high, breaking at this topskip
item would cause a page badness equal to zero (b=0
), therefore the cost c associated with the first breakpoint is indeed c=0
according to the b + p + q expression from page 111 (insertpenalties
is zero after the insert
).
So, after putting the topskip
glue on the “current page” due to the special end
processing and computing the cost associated with this potential breakpoint, TeX is still waiting for more material before deciding that a page break has to happen. So, it takes the next items inserted in “recent contributions” by end
, and moves them one by one to the “current page”. The hbox
isn't a breakpoint, it is simply moved; the vfill
is also moved—there is no reason to discard it—and is a legal breakpoint, since it is preceded by a non-discardable item (the hbox
). The page builder computes its associated cost:
% t=10.0 g=0.0 b=* p=0 c=*
This line being printed for a glue item, the associated p value is 0
, thus p < 10000. Because of the topskip
glue item (10 points) and the empty hbox
, breaking at the vfill
glue item would cause an overfull page, hence b=*
and the computed cost c=*
. This decides TeX to break the page at the best remembered breakpoint (the topskip
) and finally invoke the output
routine for page 1:
output->plainoutput
plainoutput ->shipout vbox makeheadline pagebody makefootline advancepageno ifnum outputpenalty >-@MM else dosupereject fi
(...)
So, you get a second page containing the (new) topskip
glue item, an empty hbox
equivalent to line
and another glue item from vfill
(all wrapped in two vbox
es: one from plainoutput
and one from pagebody
).
You would like to do something to shipout
page 1 after the insertion, but before end
added the equivalent of line vfill penalty-'10000000000
, right? First idea: append a penalty after the insert
that forces page breaking, e.g., with break
(which is equivalent to penalty -10000
). Alas, this doesn't work because:
A
penalty
seen in vertical mode causes TeX to exercise the page builder (TeXbook p. 280), and therefore to try to move things from the “recent contributions” to the “current page”.This sentence from p. 112 of the TeXbook:
Whenever TeX is moving an item from the top of the “recent contributions” to the bottom of the “current page,” it discards a discardable item (glue, kern, or penalty) if the current page does not contain any boxes.
which is the case here (the insertion item on the “current page” isn't a box item).
Indeed, if we try:
tracingonline=1
gdefmakefootline gdefmakeheadline
inserttopinsvbox tovsizehrule height vsize width 300pt
break
showlists
end
we get to see:
### vertical mode entered at line 0
### current page:
insert253, natural size 643.20255; split(10.0,16383.99998); float cost 0
.vbox(643.20255+0.0)x300.0
..rule(643.20255+0.0)x300.0
total height 0.0
goal height 0.0
insert253 adds 643.20255
prevdepth ignored
./insert.tex:5: OK.
l.5 showlists
As before, the “current page” has the insertion at the point where showlists
is executed, but the penalty has been discarded, as could be guessed from the previous quote. Compare this with:
tracingonline=1
tracingpages=1
gdefmakefootline gdefmakeheadline
inserttopinsvbox tovsizehrule height vsize width 300pt
topskip=0ptnullpenalty100
showlists
end
which gives:
%% goal height=643.20255, max depth=4.0
% t=0.0 g=0.0 b=0 p=0 c=0#
% t=0.0 g=0.0 b=0 p=100 c=100
### vertical mode entered at line 0
### current page:
insert253, natural size 643.20255; split(10.0,16383.99998); float cost 0
.vbox(643.20255+0.0)x300.0
..rule(643.20255+0.0)x300.0
glue(topskip) 0.0
hbox(0.0+0.0)x0.0
penalty 100
total height 0.0
goal height 0.0
insert253 adds 643.20255
prevdepth 0.0
Because of the null
(an empty hbox
), the penalty wasn't discarded this time; it got onto the current page and was noted as being a legal breakpoint (t=0.0 g=0.0 b=0 p=100 c=100
), though not the best seen so far (that one has the trailing #
). And thanks to the topskip=0pt
, we got all this on page 1 (with a positive topskip
, we would have had c = ∞ for the penalty100
breakpoint, thus again a page break at the topskip
glue, then a second page). With topskip=0pt
, the insertion, the topskip
glue and the null
box all fit on the first page. Therefore, all we need to do in order to get only one page of output as you probably wanted, is to trigger a page break after the null
box. We can do this using a penalty, which won't be discarded this time since there is already a box on the “current page list.” Here we go:
tracingonline=1
tracingpages=1
gdefmakefootline gdefmakeheadline
inserttopinsvbox tovsizehrule height vsize width 300pt
topskip=0ptnullbreak
end
which gives the following output:
%% goal height=643.20255, max depth=4.0
% t=0.0 g=0.0 b=0 p=0 c=0#
% t=0.0 g=0.0 b=0 p=-10000 c=-10000#
[1] )
Output written on insert.pdf (1 page, 968 bytes).
Transcript written on insert.log.
TeX Output finished at Mon Jul 8 10:11:58
One page of output, broken at the penalty -10000
from our break
.
Footnotes
We are precisely in the case described page 114 of the TeXbook:
If insertions occur before the first box, the
topskip
glue before that box is considered to be a valid breakpoint; this is the only case in which a completed page might not contain a box.This can be verified with code such as
output=expandaftertheoutputglobaltopskip=6pt
inserted beforeend
and watching the TeX messages printed withtracingonline=1tracingpages=1tracingoutput=1relax
(idea from Igor).Paragraph 2 of the TeXbook p. 122 gives the conditions in which the page builder is exercised:
TeX is liable to invoke an output routine whenever it tries to move something from the list of recent contributions to the current page, because it might discover a page break with c = ∞ then. Here is a list of the times when that can happen: (a) At the beginning or end of a paragraph, provided that this paragraph is being contributed to the main vertical list. (b) At the beginning or end of a displayed equation within such a paragraph. (c) After completing an
halign
in vertical mode. (d) After contributing a box or penalty or insertion to the main vertical list. (e) After anoutput
routine has ended.
edited Jul 10 at 7:30
answered Jul 8 at 8:47
frougonfrougon
4,9621 gold badge10 silver badges20 bronze badges
4,9621 gold badge10 silver badges20 bronze badges
There is an error in your answer: "It is this precise topskip that decides TeX to finally call the output routine for page 1: % t=10.0 g=0.0 b=* p=0 c=* ...". In fact,topskip
decides here:% t=0.0 g=0.0 b=0 p=0 c=0#
.% t=10.0 g=0.0 b=* p=0 c=*
is decided byvfill
. See p.113.
– Igor Liferenko
Jul 9 at 5:41
I stand by my assertion: thetopskip
causes 10 points “two much material” on the page (t=10.0 g=0.0
); this explains thec=*
which causes TeX to say “Oh, it's time to break a new page, let's find the best breakpoint we've seen so far and rewind.” The#
marks the best breakpoint seen so far, and it is thetopskip
as show by thet=0.0
on the line ending with#
. AFAICT, thevfill
is not seen at all while processing page 1 of the original example.
– frougon
Jul 9 at 5:50
Please read this sentence on p.113: "However, the % lines are generated by the penalty or glue items that follow the hboxes, not by the boxes themselves." This means that the first%
can be caused only by glue (and this is confirmed by the fact thatshowlists
is between%%
and%
). And the glue is, of course,topskip
. It causes the first%
line. Then comes empty hbox, which stands 10pt lower. Next%
can be caused also only by glue. And that glue isvfill
.
– Igor Liferenko
Jul 9 at 5:57
(was too long to add to the previous comment) The breakpoint itself doesn't stay on the page that is about to be finished: TeX always breaks “right before the chosen breakpoint”; that is why the line ending with#
hast=0.0
instead oft=10.0
.
– frougon
Jul 9 at 5:58
BTW, it's possible to see that oldtopskip
is indeed discarded and newtopskip
is used by adding this beforeend
:topskip=5ptoutput=expandaftertheoutputglobaltopskip=6pt
– Igor Liferenko
Jul 9 at 6:05
|
show 25 more comments
There is an error in your answer: "It is this precise topskip that decides TeX to finally call the output routine for page 1: % t=10.0 g=0.0 b=* p=0 c=* ...". In fact,topskip
decides here:% t=0.0 g=0.0 b=0 p=0 c=0#
.% t=10.0 g=0.0 b=* p=0 c=*
is decided byvfill
. See p.113.
– Igor Liferenko
Jul 9 at 5:41
I stand by my assertion: thetopskip
causes 10 points “two much material” on the page (t=10.0 g=0.0
); this explains thec=*
which causes TeX to say “Oh, it's time to break a new page, let's find the best breakpoint we've seen so far and rewind.” The#
marks the best breakpoint seen so far, and it is thetopskip
as show by thet=0.0
on the line ending with#
. AFAICT, thevfill
is not seen at all while processing page 1 of the original example.
– frougon
Jul 9 at 5:50
Please read this sentence on p.113: "However, the % lines are generated by the penalty or glue items that follow the hboxes, not by the boxes themselves." This means that the first%
can be caused only by glue (and this is confirmed by the fact thatshowlists
is between%%
and%
). And the glue is, of course,topskip
. It causes the first%
line. Then comes empty hbox, which stands 10pt lower. Next%
can be caused also only by glue. And that glue isvfill
.
– Igor Liferenko
Jul 9 at 5:57
(was too long to add to the previous comment) The breakpoint itself doesn't stay on the page that is about to be finished: TeX always breaks “right before the chosen breakpoint”; that is why the line ending with#
hast=0.0
instead oft=10.0
.
– frougon
Jul 9 at 5:58
BTW, it's possible to see that oldtopskip
is indeed discarded and newtopskip
is used by adding this beforeend
:topskip=5ptoutput=expandaftertheoutputglobaltopskip=6pt
– Igor Liferenko
Jul 9 at 6:05
There is an error in your answer: "It is this precise topskip that decides TeX to finally call the output routine for page 1: % t=10.0 g=0.0 b=* p=0 c=* ...". In fact,
topskip
decides here: % t=0.0 g=0.0 b=0 p=0 c=0#
. % t=10.0 g=0.0 b=* p=0 c=*
is decided by vfill
. See p.113.– Igor Liferenko
Jul 9 at 5:41
There is an error in your answer: "It is this precise topskip that decides TeX to finally call the output routine for page 1: % t=10.0 g=0.0 b=* p=0 c=* ...". In fact,
topskip
decides here: % t=0.0 g=0.0 b=0 p=0 c=0#
. % t=10.0 g=0.0 b=* p=0 c=*
is decided by vfill
. See p.113.– Igor Liferenko
Jul 9 at 5:41
I stand by my assertion: the
topskip
causes 10 points “two much material” on the page (t=10.0 g=0.0
); this explains the c=*
which causes TeX to say “Oh, it's time to break a new page, let's find the best breakpoint we've seen so far and rewind.” The #
marks the best breakpoint seen so far, and it is the topskip
as show by the t=0.0
on the line ending with #
. AFAICT, the vfill
is not seen at all while processing page 1 of the original example.– frougon
Jul 9 at 5:50
I stand by my assertion: the
topskip
causes 10 points “two much material” on the page (t=10.0 g=0.0
); this explains the c=*
which causes TeX to say “Oh, it's time to break a new page, let's find the best breakpoint we've seen so far and rewind.” The #
marks the best breakpoint seen so far, and it is the topskip
as show by the t=0.0
on the line ending with #
. AFAICT, the vfill
is not seen at all while processing page 1 of the original example.– frougon
Jul 9 at 5:50
Please read this sentence on p.113: "However, the % lines are generated by the penalty or glue items that follow the hboxes, not by the boxes themselves." This means that the first
%
can be caused only by glue (and this is confirmed by the fact that showlists
is between %%
and %
). And the glue is, of course, topskip
. It causes the first %
line. Then comes empty hbox, which stands 10pt lower. Next %
can be caused also only by glue. And that glue is vfill
.– Igor Liferenko
Jul 9 at 5:57
Please read this sentence on p.113: "However, the % lines are generated by the penalty or glue items that follow the hboxes, not by the boxes themselves." This means that the first
%
can be caused only by glue (and this is confirmed by the fact that showlists
is between %%
and %
). And the glue is, of course, topskip
. It causes the first %
line. Then comes empty hbox, which stands 10pt lower. Next %
can be caused also only by glue. And that glue is vfill
.– Igor Liferenko
Jul 9 at 5:57
(was too long to add to the previous comment) The breakpoint itself doesn't stay on the page that is about to be finished: TeX always breaks “right before the chosen breakpoint”; that is why the line ending with
#
has t=0.0
instead of t=10.0
.– frougon
Jul 9 at 5:58
(was too long to add to the previous comment) The breakpoint itself doesn't stay on the page that is about to be finished: TeX always breaks “right before the chosen breakpoint”; that is why the line ending with
#
has t=0.0
instead of t=10.0
.– frougon
Jul 9 at 5:58
BTW, it's possible to see that old
topskip
is indeed discarded and new topskip
is used by adding this before end
: topskip=5ptoutput=expandaftertheoutputglobaltopskip=6pt
– Igor Liferenko
Jul 9 at 6:05
BTW, it's possible to see that old
topskip
is indeed discarded and new topskip
is used by adding this before end
: topskip=5ptoutput=expandaftertheoutputglobaltopskip=6pt
– Igor Liferenko
Jul 9 at 6:05
|
show 25 more comments
It is tex-the-program's final endgame to flush out the insert:
tex.web has
@ We don't want to leave |main_control| immediately when a |stop| command
is sensed, because it may be necessary to invoke an .\output routine
several times before things really grind to a halt. (The output routine
might even say `.\gdef\end...', to prolong the life of the job.)
Therefore |its_all_over| is |true| only when the current page
and contribution list are empty, and when the last output was not a
``dead cycle.''
@<Declare act...@>=
function its_all_over:boolean; do this when .\end or .\dump occurs
label exit;
begin if privileged then
begin if (page_head=page_tail)and(head=tail)and(dead_cycles=0) then
begin its_all_over:=true; return;
end;
back_input; we will try to end again after ejecting residual material
tail_append(new_null_box);
width(tail):=hsize;
tail_append(new_glue(fill_glue));
tail_append(new_penalty(-@'10000000000));@/
build_page; append .\hbox to \hsize\vfill\penalty-'10000000000
^^^^^^^^^^^^^^^^^^^^^^
end;
its_all_over:=false;
exit:end;
This is mentioned on p.264 of TeXbook. I guess the meaning is that ifinsert
which occupies the whole page is used, there has to be some additional text, so second page is inevitable. Still, I'm not sure I completely understand how the explanation on p.264 applies to OP.
– Igor Liferenko
Jul 8 at 7:26
@IgorLiferenko in your case when it hitsend
the main vertical list is not empty (there is aninsert
node left) so the clause on page 262 means the the line is added andend
is called again
– David Carlisle
Jul 8 at 7:50
add a comment |
It is tex-the-program's final endgame to flush out the insert:
tex.web has
@ We don't want to leave |main_control| immediately when a |stop| command
is sensed, because it may be necessary to invoke an .\output routine
several times before things really grind to a halt. (The output routine
might even say `.\gdef\end...', to prolong the life of the job.)
Therefore |its_all_over| is |true| only when the current page
and contribution list are empty, and when the last output was not a
``dead cycle.''
@<Declare act...@>=
function its_all_over:boolean; do this when .\end or .\dump occurs
label exit;
begin if privileged then
begin if (page_head=page_tail)and(head=tail)and(dead_cycles=0) then
begin its_all_over:=true; return;
end;
back_input; we will try to end again after ejecting residual material
tail_append(new_null_box);
width(tail):=hsize;
tail_append(new_glue(fill_glue));
tail_append(new_penalty(-@'10000000000));@/
build_page; append .\hbox to \hsize\vfill\penalty-'10000000000
^^^^^^^^^^^^^^^^^^^^^^
end;
its_all_over:=false;
exit:end;
This is mentioned on p.264 of TeXbook. I guess the meaning is that ifinsert
which occupies the whole page is used, there has to be some additional text, so second page is inevitable. Still, I'm not sure I completely understand how the explanation on p.264 applies to OP.
– Igor Liferenko
Jul 8 at 7:26
@IgorLiferenko in your case when it hitsend
the main vertical list is not empty (there is aninsert
node left) so the clause on page 262 means the the line is added andend
is called again
– David Carlisle
Jul 8 at 7:50
add a comment |
It is tex-the-program's final endgame to flush out the insert:
tex.web has
@ We don't want to leave |main_control| immediately when a |stop| command
is sensed, because it may be necessary to invoke an .\output routine
several times before things really grind to a halt. (The output routine
might even say `.\gdef\end...', to prolong the life of the job.)
Therefore |its_all_over| is |true| only when the current page
and contribution list are empty, and when the last output was not a
``dead cycle.''
@<Declare act...@>=
function its_all_over:boolean; do this when .\end or .\dump occurs
label exit;
begin if privileged then
begin if (page_head=page_tail)and(head=tail)and(dead_cycles=0) then
begin its_all_over:=true; return;
end;
back_input; we will try to end again after ejecting residual material
tail_append(new_null_box);
width(tail):=hsize;
tail_append(new_glue(fill_glue));
tail_append(new_penalty(-@'10000000000));@/
build_page; append .\hbox to \hsize\vfill\penalty-'10000000000
^^^^^^^^^^^^^^^^^^^^^^
end;
its_all_over:=false;
exit:end;
It is tex-the-program's final endgame to flush out the insert:
tex.web has
@ We don't want to leave |main_control| immediately when a |stop| command
is sensed, because it may be necessary to invoke an .\output routine
several times before things really grind to a halt. (The output routine
might even say `.\gdef\end...', to prolong the life of the job.)
Therefore |its_all_over| is |true| only when the current page
and contribution list are empty, and when the last output was not a
``dead cycle.''
@<Declare act...@>=
function its_all_over:boolean; do this when .\end or .\dump occurs
label exit;
begin if privileged then
begin if (page_head=page_tail)and(head=tail)and(dead_cycles=0) then
begin its_all_over:=true; return;
end;
back_input; we will try to end again after ejecting residual material
tail_append(new_null_box);
width(tail):=hsize;
tail_append(new_glue(fill_glue));
tail_append(new_penalty(-@'10000000000));@/
build_page; append .\hbox to \hsize\vfill\penalty-'10000000000
^^^^^^^^^^^^^^^^^^^^^^
end;
its_all_over:=false;
exit:end;
answered Jul 8 at 7:07
David CarlisleDavid Carlisle
515k44 gold badges1168 silver badges1936 bronze badges
515k44 gold badges1168 silver badges1936 bronze badges
This is mentioned on p.264 of TeXbook. I guess the meaning is that ifinsert
which occupies the whole page is used, there has to be some additional text, so second page is inevitable. Still, I'm not sure I completely understand how the explanation on p.264 applies to OP.
– Igor Liferenko
Jul 8 at 7:26
@IgorLiferenko in your case when it hitsend
the main vertical list is not empty (there is aninsert
node left) so the clause on page 262 means the the line is added andend
is called again
– David Carlisle
Jul 8 at 7:50
add a comment |
This is mentioned on p.264 of TeXbook. I guess the meaning is that ifinsert
which occupies the whole page is used, there has to be some additional text, so second page is inevitable. Still, I'm not sure I completely understand how the explanation on p.264 applies to OP.
– Igor Liferenko
Jul 8 at 7:26
@IgorLiferenko in your case when it hitsend
the main vertical list is not empty (there is aninsert
node left) so the clause on page 262 means the the line is added andend
is called again
– David Carlisle
Jul 8 at 7:50
This is mentioned on p.264 of TeXbook. I guess the meaning is that if
insert
which occupies the whole page is used, there has to be some additional text, so second page is inevitable. Still, I'm not sure I completely understand how the explanation on p.264 applies to OP.– Igor Liferenko
Jul 8 at 7:26
This is mentioned on p.264 of TeXbook. I guess the meaning is that if
insert
which occupies the whole page is used, there has to be some additional text, so second page is inevitable. Still, I'm not sure I completely understand how the explanation on p.264 applies to OP.– Igor Liferenko
Jul 8 at 7:26
@IgorLiferenko in your case when it hits
end
the main vertical list is not empty (there is an insert
node left) so the clause on page 262 means the the line is added and end
is called again– David Carlisle
Jul 8 at 7:50
@IgorLiferenko in your case when it hits
end
the main vertical list is not empty (there is an insert
node left) so the clause on page 262 means the the line is added and end
is called again– David Carlisle
Jul 8 at 7:50
add a comment |
Thanks for contributing an answer to TeX - LaTeX Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2ftex.stackexchange.com%2fquestions%2f499060%2fwhy-is-an-empty-line-added-when-insert-is-used%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown