\vbox append: Insufficient line spacing between last line of first \vbox, and first line of second \vbox
Put \strut
at the end of the text in the first vbox, and at the beginning of the text in the second.
That solution works. My initial explanation was wrong because I misread \unvbox
as \box
:
The (incorrect) explanation is that each box is treated like a single huge letter when TeX stacks them in a "vertical list"; TeX does not look inside the boxes to see where the baselines of the interior text are, and makes no attempt to make them regularly spaced.
The real explanation for no baseline-skip is that's a feature of \unvbox
. But \strut
will still do the job.
I presume you are actually doing something that requires \vbox
... Probably \parbox
is better in the general case. If you are just trying to prevent page breaks, there are better solutions (including \samepage
, which works pretty well, as long as you know it only applies to lines in a paragraph).
There are fancier things possible, to rebox the box contents as \vtop
and \vbox
to measure the heights and depths of first and last lines, and then set \prevdepth
so TeX does the vertical spacing based on contents, even without struts.
As egreg explained in this answer:
At the start of a
\vbox
, the parameter\prevdepth
is set to-1000pt
and, when you do\unvbox
, this value inhibits the interline glue.
In your case, \prevdepth
is set to -1000pt
right after \setbox\myvboxtwo=\vbox{
, among others, and this causes the problem you mentioned when \myvboxtwo
is \unvbox
ed. If you save \myvboxone
's depth after it has been set and insert it as the
\prevdepth
value at the beginning of the assignment to \myvboxtwo
, the interline glue will be correct when \myvboxtwo
is \unvbox
ed after \myvboxone
.
Note: I switched your code to use LaTeX's \newsavebox
instead of TeX's \newbox
. AFAIK, it does more checks. Also, during your assignment to \myvboxthree
, TeX never switches to horizontal mode, therefore the \hsize
setting and the \endgraf
are useless.
\documentclass{article}
\usepackage[expansion=alltext,shrink=20,stretch=20]{microtype}
\usepackage{fontspec}
\usepackage{blindtext}
\setmainfont{Verdana}
\newcommand{\mytesttext}{\blindtext[1]}
\newdimen\mydim
\pagestyle{empty}
\newsavebox{\myvboxone}
\newsavebox{\myvboxtwo}
\newsavebox{\myvboxthree}
\begin{document}
\setbox\myvboxone=\vbox{{\hsize=\textwidth \mytesttext \endgraf}}
\mydim=\dp\myvboxone
\setbox\myvboxtwo=\vbox{
\prevdepth=\mydim
{\hsize=\textwidth \mytesttext \endgraf}
}
\setbox\myvboxthree=\vbox{
\unvbox\myvboxone
\unvbox\myvboxtwo
}
\box\myvboxthree
\end{document}
If we add a very deep box or rule in the last line of \myvboxone
, like this:
\setbox\myvboxone=\vbox{
{\hsize=\textwidth \mytesttext
\vrule width 0.4pt depth 20pt\endgraf}}
its depth is well taken into account by the above solution (note that I don't show again the start of the first box and the end of the second one: they are the same as above):
April 13 edit
Replying to this comment: if I use the following modified definition of \mytesttext
:
\newcommand{\mytesttext}{\blindtext[1]ee eeeeeee eee eee ee eee eee ee eee ee
eee ee eeeeeee eee eee ee eeen}
then zoomed output where the two \vbox
es join is as follows:
The interline looks fine to me.
When the vertical material is \unvbox
ed then the result does not respect baselineskip above and below such material. If we need to add something below such material then \lastbox
trick can be used. This removes the last box from \unvbox
ed material and returns it to the vertical list again, but the \prevdepth
is correctly set and next "normal" line will respect baselineskip settings. But you does not have next "normal" line, you have next \ubvbox
ed material. So new problem is here. This could be solved by \null
(i.e. \hbox{}
). The code should be:
\newbox\myvboxone
\setbox\myvboxone=\vbox{{\mytesttext \endgraf}}%
\newbox\myvboxtwo
\setbox\myvboxtwo=\vbox{{\null \mytesttext \endgraf}}%
\newbox\myvboxthree
\setbox\myvboxthree=\vbox
{{\unvbox\myvboxone \lastbox\kern-\prevdepth \unvbox\myvboxtwo}}%
\box\myvboxthree
\bye
Note that the \myvboxone
is the same as your. When it is \unvboxed
then \lastbox
trick is used followed by \kern-\prevdepth
. Now, we are exactly at the baseline of the last line. Next \unvboxed
material begins with \null
which is positioned to the baseline of last line of \myvboxone
. Next line has correct \baselineskip
because it was calulated when \myvboxtwo
was created.
Edit If you want to use \vboxes
from \vsplit
then the core trick is to set \splittopskip
to \baselineskip
, to insert first \penalty0
before the column of the text and to do first "dummy" \vsplit
in this \penalty0
. This inserts the correct lineskip above the first line in the column. Next \vsplits
inserts correct lineskip above the rest of the column too. So you are sure that the material from \vplit
behaves like as the \null
is here (but it is not here:). The connection of splitted parts back together needs only the \lastbox
trick now. Example:
\newbox\myvboxone
\newbox\myvboxtwo
\newbox\myvboxthree
\newbox\allcolumn
\setbox\myvboxone=\vbox{{\mytesttext \endgraf}}% begin of the text
\splittopskip=\baselineskip % << core trick is here + \penaty0
\setbox\allcolumn=\vbox{{\penalty0 \mytesttext \endgraf}}
\setbox0=\vsplit\allcolumn to0pt % this resets \allcolun, now it starts with
% correct skip above the first line.
\setbox\myvboxtwo=\vsplit\allcolumn to3\baselineskip % three lines from \allcolumn
\setbox\myvboxthree=\vbox % cat begin of the text with three lines from \allcolumn
{\unvbox\myvboxone \lastbox\kern-\prevdepth \unvbox\myvboxtwo}%
\bigskip test1:\medskip \copy\myvboxthree % testing of the result
\setbox\myvboxtwo=\vsplit\allcolumn to4\baselineskip % next 4 lines from \allcolumn
\setbox\myvboxthree=\vbox % cat previous text with next four lines
{\unvbox\myvboxthree \lastbox\kern-\prevdepth \unvbox\myvboxtwo}%
\bigskip test2:\medskip \box\myvboxthree % testing of the result
\bye