Two excellent answers have already been given, that explain in detail what happens and why: I would just like to add a few remarks, and corroborate the analysis with evidence coming from appropriate tracing commands.
I will use the following diagnostic program:
% !TEX TS-program = pdftex
\showboxbreadth = 1000
\showboxdepth = 10
\output={%
\tracingcommands = 0
\immediate\write -1 {Entering output routine,
\string\outputpenalty\space = \number\outputpenalty.}%
\showlists % output
\plainoutput
\immediate\write -1 {Exiting output routine.}%
}
\tracingpages = 1
\hrule
\vskip 21.9cm plus 1pt \relax % as it was in the question
one paragraph
\par
\hrule % \hrule is here only as a "visualizer", you can remove it
\tracingcommands = 1
\medskip
\showlists % #1
\par % let's make it explicit
\showlists % #2
\end
Note the use of \tracingpages = 1
: this will emit a tracing line at the exact moment at which TeX moves an item, which is a legal breakpoint, from the list of “recent contributions” to the “current page” and computes the cost of cutting off the page just above the item it is moving; this tracing line shows how this cost is computed.
I will now go through the transcript file it produces and add my remarks; you are invited to compile the above code, open the transcript file, and follow the forthcoming discussion looking at your copy of it.
This is pdfTeX, Version 3.14159265-2.6-1.40.16 (TeX Live 2015) (preloaded format=pdftex 2016.3.5) 20 APR 2016 11:35
entering extended mode
restricted \write18 enabled.
file:line:error style messages enabled.
%&-line parsing enabled.
**wipet.tex
(./wipet.tex
%% goal height=643.20255, max depth=4.0
% t=10.0 g=643.20255 b=10000 p=0 c=100000#
The last line is emitted when TeX is moving from the “contribution list” to the “current page” the glue coming from the “big” \vskip 29.1cm ...
command, which is a legal breakpoint: TeX is now reckoning of cutting off the page above this glue, that is, after the \topskip
glue and the top rule. This would of course yield a page of infinite badness, so a cost of c=100000#
is computed (the #
mark at the end tells that this is, notwithstanding all, the best breakpoint appeared thus far).
The following two lines come from our request of tracing down the execution of every primitive command (\tracingcommands = 1
):
{vertical mode: \vskip}
{\showlists}
Thus we see that the \medskip
command is executed, but, as @egreg and @wipet have already explained, this merely has the effect of appending a glue item to the list of “recent contributions”, without exercising the page builder. The tracing produced by the ensuing \showlists
commands (the one marked % #1
in the source code) confirms this:
### vertical mode entered at line 0
### current page:
\glue(\topskip) 9.6
\rule(0.4+0.0)x*
\glue 623.11517 plus 1.0
\glue(\parskip) 0.0 plus 1.0
\hbox(6.94444+1.94444)x469.75499, glue set 386.92151fil
.\hbox(0.0+0.0)x20.0
.\tenrm o
.\tenrm n
.\tenrm e
.\glue 3.33333 plus 1.66666 minus 1.11111
.\tenrm p
.\tenrm a
.\tenrm r
.\tenrm a
.\tenrm g
.\tenrm r
.\tenrm a
.\tenrm p
.\tenrm h
.\penalty 10000
.\glue(\parfillskip) 0.0 plus 1.0fil
.\glue(\rightskip) 0.0
total height 640.05962 plus 2.0
goal height 643.20255
### recent contributions:
\rule(0.4+0.0)x*
\glue 6.0 plus 2.0 minus 2.0
prevdepth ignored, prevgraf 1 line
./wipet.tex:21: OK.
l.21 \showlists
% #1
?
Note that no further line coming from \tracingpages
has been emitted yet, but nonetheless the “total height” of the page already includes the “large” \vskip 29.1cm ...
and the height, but not the depth, of the line of text. Don’t be confused by this: TeX is still remembering as “best break so far” the break above the large \vskip
, because no one of the items that currently follow on the page is a legal breakpoint (the \parskip
glue is not, since it is preceded by a discardable item). If a page break occurred now at that point, the ensuing items would be moved back to the “recent contributions” list.
Then the \par
command gets executed:
{\par}
This one does exercise the page builder, and indeed it is immediately followed by a line emitted by \tracingpages
:
% t=642.40405 plus 2.0 g=643.20255 b=6 p=0 c=6#
TeX has just moved the rule and the glue from the “recent contributions” to the current page (actually, what triggered the above line is the glue, which is a legal breakpoint), and it is now evaluating the cost of cutting off the page above the \medskip
. This cost is appealing, but TeX is still waiting to see if anything better is coming along (the badness isn’t infinite yet, neither is the penalty lower than or equal to -10000).
Next comes the second \showlists
command (that marked % #2
)
{\showlists}
whose output confirms that the \medskip
has been incorporated into the “current page”:
### vertical mode entered at line 0
### current page:
\glue(\topskip) 9.6
\rule(0.4+0.0)x*
\glue 623.11517 plus 1.0
\glue(\parskip) 0.0 plus 1.0
\hbox(6.94444+1.94444)x469.75499, glue set 386.92151fil
.\hbox(0.0+0.0)x20.0
.\tenrm o
.\tenrm n
.\tenrm e
.\glue 3.33333 plus 1.66666 minus 1.11111
.\tenrm p
.\tenrm a
.\tenrm r
.\tenrm a
.\tenrm g
.\tenrm r
.\tenrm a
.\tenrm p
.\tenrm h
.\penalty 10000
.\glue(\parfillskip) 0.0 plus 1.0fil
.\glue(\rightskip) 0.0
\rule(0.4+0.0)x*
\glue 6.0 plus 2.0 minus 2.0
total height 648.40405 plus 4.0 minus 2.0
goal height 643.20255
prevdepth ignored, prevgraf 1 line
./wipet.tex:23: OK.
l.23 \showlists
% #2
?
But again, recall that, although the “total height” of the “current page” now includes the “large” \vskip 29.1cm ...
, the line of text (both height and depth), the bottom rule, and the \medskip
glue, the “best remembered breakpoint” is still above the \medskip
(the one of the line that says t=642.40405 plus 2.0 ...
): as we’ll see, this will be the chosen breakpoint, and the \glue 6.0 plus 2.0 minus 2.0
will be returned to the “recent contributions”.
Well then, this is the state of affairs when the \end
command is digested:
{\end}
Since the main vertical has not been entirely output (cf. The TeXbook, p. 264, first paragraph), this commands inserts the equivalent of
\line{} \vfill \penalty-’10000000000
into the main vertical list—more precisely, into the “recent contributions”; both the box produced by \line{}
and the \penalty
exercise the page builder, causing the box, the infinite glue and the penalty themselves to be immediately forwarded to the “current page”; the glue is a legal breakpoint, so TeX computes the cost of cutting off the page above it, that is, just after the empty box, and emits the following tracing line (always from \tracingpages
):
% t=648.40405 plus 4.0 minus 2.0 g=643.20255 b=* p=0 c=*
This time the badness is infinite, so the output routine is invoked (also note that the line says p=0
, from which you can tell that the breakpoint being considered is indeed the glue, not the highly negative penalty; but the page is broken at the best remembered breakpoint, which, let us repeat it once again, is still above the \medskip
, at t=642.40405 plus 2.0
, with c=6
, the last computation marked with the #
sign). And actually, what immediately follows is the tracing produced by the commands we included in our custom output routine:
{internal vertical mode: \tracingcommands}
Entering output routine, \outputpenalty = 10000.
(note that \outputpenalty
has the expected value for a break “at glue”). Among these commands is the \showlists
marked with % output
:
### internal vertical mode entered at line 24 (\output routine)
prevdepth ignored
### vertical mode entered at line 0
### recent contributions:
\glue 6.0 plus 2.0 minus 2.0
\hbox(0.0+0.0)x469.75499
\glue 0.0 plus 1.0fill
\penalty -1073741824
prevdepth ignored, prevgraf 1 line
./wipet.tex:24: OK.
<output> ...= \number \outputpenalty .}\showlists
\plainoutput \immediate \w...
<to be read again>
\end
l.24 \end
?
Again, everything is as we expect: the internal vertical list being constructed by the output routine is, obviously, still empty, and the main vertical list contains the items that follow the chosen breakpoint, starting with the chosen brakpoint itself, which have been returned to the “recent contributions”.
The first page is therefore ejected…
[1{/usr/local/texlive/2015/texmf-var/fonts/map/pdftex/updmap/pdftex.map}]
…then the output routine terminates.
Exiting output routine.
When an output routine ends, the page builder is immediately exercised again. The current page is empty, in particular it does not contain any box, so the \glue 6.0 ...
is discarded. Then comes the empty box: since this is the first box contributed to the page, TeX inserts the \topskip
glue above it and emits the line
%% goal height=643.20255, max depth=4.0
Note that the \topskip
glue is not a legal breakpoint (because it is not preceded by a non-discardable item); indeed, the current page hasn’t got any legal breakpoint yet. The first one is the infinite glue, and when it moves it to the “current page” list, TeX emits the line
% t=10.0 g=643.20255 b=10000 p=0 c=100000#
This corresponds to the cost of breaking the page above the infinite glue. But the chosen breakpoint will be the next one, that TeX consider when it is moving the \penalty-’10000000000
from the “recent contributions” to the “current page”:
% t=10.0 plus 1.0fill g=643.20255 b=0 p=-1073741824 c=-1073741824#
(“above” the penalty, as it were). Here p≤-10000, so the output routine is immediately invoked:
{\tracingcommands}
Entering output routine, \outputpenalty = -1073741824.
(note the value of \outputpenalty
). The state of the lists is now as follows:
### internal vertical mode entered at line 24 (\output routine)
prevdepth ignored
### vertical mode entered at line 0
### recent contributions:
\penalty 10000
prevdepth ignored, prevgraf 1 line
./wipet.tex:24: OK.
<output> ...= \number \outputpenalty .}\showlists
\plainoutput \immediate \w...
<to be read again>
\end
l.24 \end
?
You can see, once again, that the chosen breakpoint has been returned to the “recent contributions”, but with the value of the penalty changed to 10000 (The TeXbook, p. 125). The output routine terminates, ejecting the second page…
[2]
Exiting output routine.
…and another turn on the roundabout begins: the page builder is exercised because an output routine has just ended, and the penalty item is discarded because the current page is empty. After this, the \end
commands is executed again (p. 264)…
{vertical mode: \end}
…but this time the main vertical list is empty, so a “happy ending” ensues:
)</usr/local/texlive/2015/texmf-dist/fonts/type1/public/amsfonts/cm/cmr10.pfb>
Output written on wipet.pdf (2 pages, 12562 bytes).
PDF statistics:
15 PDF objects out of 1000 (max. 8388607)
9 compressed objects within 1 object stream
0 named destinations out of 1000 (max. 500000)
1 words of extra memory for PDF output out of 10000 (max. 10000000)
(Deep breath.)
Maybe someone else will now volunteer to write a similar analysis for the other two scenarios…
\bye
rather than\end
. I can't help feeling that section 32.1.2 of TeX by Topic contains a clue, but my TeX knowledge simply isn't good enough to work this out!\bye
macro adds the\penalty-20000
to the main vertical list. So, there are more discardable items: glue plus penalty and then\end
decides that vertical list is empty. It is irrelevant what value of the penalty is. For example adding\penalty0
after\medskip
stops to generate the empty page too.