Spacing difference when using boxes











up vote
16
down vote

favorite
1












On the left (obtained from the MWE below), I do not use boxes for the title nor the body text and the title and the text appears to be in the correct spot.



However, when I attempt to use newsaveboxs (MWE with defUseBoxes{} uncommented) I obtain the results on the right.



enter image description hereenter image description here



Questions:




  • What are this additional skip's being inserted and how do I get both to exhibit identical behavior?

  • In my actual use case I can tweak the addition of the struts to get correct behavior, but that also seems to need a conditional based on if the body text is more than one line. My understanding is that I should only need a strut at the end of the body text, but that does not seem to work here.


Notes:




  • A vertical space defined by DesiredSkipAboveTitle is added before the title and a DesiredSkipBelowTitle is added after the title.

  • Need to run at least twice to get the tikz drawing in correct position.

  • The adjustbox package can be used to move the boxes into what I think is the correct position. This can be seen with both defUseBoxes{} and defUseAdjustBox{} uncommented. From this we see that the TitleBox needs a raise=-1.3pt option and the BodyBox requires a raise=-13.3pt with the align=t. I would like to know where these amounts come from.


Code:



defUseBoxes{}% Uncomment to use boxes
%defUseAdjustBox{}% Uncomment to use adjustbox (with added fudge factors)
%% --------------------
documentclass{article}
usepackage{tikz}
usepackage{tikzpagenodes}
usepackage[export]{adjustbox}

usepackage[paperwidth=7.0cm,showframe]{geometry}

newcommand*{DesiredSkipAboveTitle}{5pt}
newcommand*{DesiredSkipBelowTitle}{10pt}

newcommand*{NumberOfTitleLines}{2}
newcommand*{Title}{A Title that Takes Up Two Lines}
newsavebox{TitleBox}
newcommand*{SetupTitleBox}[2]{%
setbox#1vbox{%
bfseriescentering%
#2%
%strut%
par%
}%
}

newcommand*{BodyTextLarge}{%
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Integer id nisl porta, porttitor tellus sed, sollicitudin erat.
Aliquam tellus urna, feugiat et tortor quis, aliquam mattis orci.%
}%
newcommand*{BodyTextSmall}{%
Small Text.%
}%

newcommand{SelectedBodyText}{BodyTextLarge}
%newcommand{SelectedBodyText}{BodyTextSmall}


newsavebox{BodyBox}
newcommand{SetupBodyBox}[2]{%
setbox#1vbox{%
noindent#2%
%strut%
}%
}%

newlength{AdditionalSkip}
newcommand*{ShowTextGuideLines}[1]{%
begin{tikzpicture}[remember picture, overlay]
coordinate (X) at (current page text area.north west);

draw [draw=red, fill=yellow, fill opacity=0.2]
(X) circle (1pt);%% DEBUGGING: Ensure (X) is the correct spot.

setlength{AdditionalSkip}{DesiredSkipAboveTitle}%
foreach X in {1, ..., #1} {%
ifnumX>NumberOfTitleLines
%% After title: need to adjust AdditionalSkip for space after title
setlength{AdditionalSkip}{%
dimexprDesiredSkipAboveTitle+DesiredSkipBelowTitlerelax%
}%
fi
draw [thin, red] ([yshift=-Xbaselineskip-AdditionalSkip]X) -- ++ (hsize,0);
}%

tikzset{Node Style/.style={anchor=south, draw=red, inner sep=1pt}}
ifdefinedUseBoxes
node [Node Style, fill=yellow] at ([xshift=0.5hsize]X) {using boxes};
else
node [Node Style, fill=green] at ([xshift=0.5hsize]X) {Not using boxes};
fi
end{tikzpicture}%
}%


begin{document}
%% ---------------------------------------------------------- Set up the title and body
SetupTitleBox{TitleBox}{Title}%
SetupBodyBox{BodyBox}{SelectedBodyText}%
%% ---------------------------------------------------------- Title
hbox{}kern-topskip%% See https://tex.stackexchange.com/questions/7676/why-does-vspace0pt-add-vertical-space#comment12433_7681
vspace*{DesiredSkipAboveTitle}%
%%
ifdefinedUseBoxes
ifdefinedUseAdjustBox% Following tweaks seem to be close
noindentadjustbox{valign=t,raise=-1.3pt}{usebox{TitleBox}}%
vspace*{DesiredSkipBelowTitle}%
parnoindentadjustbox{valign=t,raise=-13.3pt}{usebox{BodyBox}}%
else
noindentusebox{TitleBox}%
vspace*{DesiredSkipBelowTitle}%
parnoindentusebox{BodyBox}%
fi
else
noindent{bfseriescenteringTitlepar}%
vspace*{DesiredSkipBelowTitle}%
parnoindentSelectedBodyText%
fi
noindentShowTextGuideLines{7}%
end{document}









share|improve this question

















This question has an open bounty worth +400
reputation from Peter Grill ending in 6 days.


One or more of the answers is exemplary and worthy of an additional bounty.


The detailed explanation was very helpful in understanding the issues.












  • 2




    you used vbox rather than vtop (not that either of them are really latex)
    – David Carlisle
    Dec 3 at 20:41










  • @DavidCarlisle: Using vtop instead of vbox seems to resolve the issue with the TitleBox, but not the BodyBox?
    – Peter Grill
    Dec 3 at 20:57








  • 2




    I didn't actually run the code, I'll look in a bit:-) but getting spacing around boxes right is tricky you need to control the first and last baseline but boxes only have one reference point, hence latex3 coffins, boxes with handles.
    – David Carlisle
    Dec 3 at 20:58















up vote
16
down vote

favorite
1












On the left (obtained from the MWE below), I do not use boxes for the title nor the body text and the title and the text appears to be in the correct spot.



However, when I attempt to use newsaveboxs (MWE with defUseBoxes{} uncommented) I obtain the results on the right.



enter image description hereenter image description here



Questions:




  • What are this additional skip's being inserted and how do I get both to exhibit identical behavior?

  • In my actual use case I can tweak the addition of the struts to get correct behavior, but that also seems to need a conditional based on if the body text is more than one line. My understanding is that I should only need a strut at the end of the body text, but that does not seem to work here.


Notes:




  • A vertical space defined by DesiredSkipAboveTitle is added before the title and a DesiredSkipBelowTitle is added after the title.

  • Need to run at least twice to get the tikz drawing in correct position.

  • The adjustbox package can be used to move the boxes into what I think is the correct position. This can be seen with both defUseBoxes{} and defUseAdjustBox{} uncommented. From this we see that the TitleBox needs a raise=-1.3pt option and the BodyBox requires a raise=-13.3pt with the align=t. I would like to know where these amounts come from.


Code:



defUseBoxes{}% Uncomment to use boxes
%defUseAdjustBox{}% Uncomment to use adjustbox (with added fudge factors)
%% --------------------
documentclass{article}
usepackage{tikz}
usepackage{tikzpagenodes}
usepackage[export]{adjustbox}

usepackage[paperwidth=7.0cm,showframe]{geometry}

newcommand*{DesiredSkipAboveTitle}{5pt}
newcommand*{DesiredSkipBelowTitle}{10pt}

newcommand*{NumberOfTitleLines}{2}
newcommand*{Title}{A Title that Takes Up Two Lines}
newsavebox{TitleBox}
newcommand*{SetupTitleBox}[2]{%
setbox#1vbox{%
bfseriescentering%
#2%
%strut%
par%
}%
}

newcommand*{BodyTextLarge}{%
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Integer id nisl porta, porttitor tellus sed, sollicitudin erat.
Aliquam tellus urna, feugiat et tortor quis, aliquam mattis orci.%
}%
newcommand*{BodyTextSmall}{%
Small Text.%
}%

newcommand{SelectedBodyText}{BodyTextLarge}
%newcommand{SelectedBodyText}{BodyTextSmall}


newsavebox{BodyBox}
newcommand{SetupBodyBox}[2]{%
setbox#1vbox{%
noindent#2%
%strut%
}%
}%

newlength{AdditionalSkip}
newcommand*{ShowTextGuideLines}[1]{%
begin{tikzpicture}[remember picture, overlay]
coordinate (X) at (current page text area.north west);

draw [draw=red, fill=yellow, fill opacity=0.2]
(X) circle (1pt);%% DEBUGGING: Ensure (X) is the correct spot.

setlength{AdditionalSkip}{DesiredSkipAboveTitle}%
foreach X in {1, ..., #1} {%
ifnumX>NumberOfTitleLines
%% After title: need to adjust AdditionalSkip for space after title
setlength{AdditionalSkip}{%
dimexprDesiredSkipAboveTitle+DesiredSkipBelowTitlerelax%
}%
fi
draw [thin, red] ([yshift=-Xbaselineskip-AdditionalSkip]X) -- ++ (hsize,0);
}%

tikzset{Node Style/.style={anchor=south, draw=red, inner sep=1pt}}
ifdefinedUseBoxes
node [Node Style, fill=yellow] at ([xshift=0.5hsize]X) {using boxes};
else
node [Node Style, fill=green] at ([xshift=0.5hsize]X) {Not using boxes};
fi
end{tikzpicture}%
}%


begin{document}
%% ---------------------------------------------------------- Set up the title and body
SetupTitleBox{TitleBox}{Title}%
SetupBodyBox{BodyBox}{SelectedBodyText}%
%% ---------------------------------------------------------- Title
hbox{}kern-topskip%% See https://tex.stackexchange.com/questions/7676/why-does-vspace0pt-add-vertical-space#comment12433_7681
vspace*{DesiredSkipAboveTitle}%
%%
ifdefinedUseBoxes
ifdefinedUseAdjustBox% Following tweaks seem to be close
noindentadjustbox{valign=t,raise=-1.3pt}{usebox{TitleBox}}%
vspace*{DesiredSkipBelowTitle}%
parnoindentadjustbox{valign=t,raise=-13.3pt}{usebox{BodyBox}}%
else
noindentusebox{TitleBox}%
vspace*{DesiredSkipBelowTitle}%
parnoindentusebox{BodyBox}%
fi
else
noindent{bfseriescenteringTitlepar}%
vspace*{DesiredSkipBelowTitle}%
parnoindentSelectedBodyText%
fi
noindentShowTextGuideLines{7}%
end{document}









share|improve this question

















This question has an open bounty worth +400
reputation from Peter Grill ending in 6 days.


One or more of the answers is exemplary and worthy of an additional bounty.


The detailed explanation was very helpful in understanding the issues.












  • 2




    you used vbox rather than vtop (not that either of them are really latex)
    – David Carlisle
    Dec 3 at 20:41










  • @DavidCarlisle: Using vtop instead of vbox seems to resolve the issue with the TitleBox, but not the BodyBox?
    – Peter Grill
    Dec 3 at 20:57








  • 2




    I didn't actually run the code, I'll look in a bit:-) but getting spacing around boxes right is tricky you need to control the first and last baseline but boxes only have one reference point, hence latex3 coffins, boxes with handles.
    – David Carlisle
    Dec 3 at 20:58













up vote
16
down vote

favorite
1









up vote
16
down vote

favorite
1






1





On the left (obtained from the MWE below), I do not use boxes for the title nor the body text and the title and the text appears to be in the correct spot.



However, when I attempt to use newsaveboxs (MWE with defUseBoxes{} uncommented) I obtain the results on the right.



enter image description hereenter image description here



Questions:




  • What are this additional skip's being inserted and how do I get both to exhibit identical behavior?

  • In my actual use case I can tweak the addition of the struts to get correct behavior, but that also seems to need a conditional based on if the body text is more than one line. My understanding is that I should only need a strut at the end of the body text, but that does not seem to work here.


Notes:




  • A vertical space defined by DesiredSkipAboveTitle is added before the title and a DesiredSkipBelowTitle is added after the title.

  • Need to run at least twice to get the tikz drawing in correct position.

  • The adjustbox package can be used to move the boxes into what I think is the correct position. This can be seen with both defUseBoxes{} and defUseAdjustBox{} uncommented. From this we see that the TitleBox needs a raise=-1.3pt option and the BodyBox requires a raise=-13.3pt with the align=t. I would like to know where these amounts come from.


Code:



defUseBoxes{}% Uncomment to use boxes
%defUseAdjustBox{}% Uncomment to use adjustbox (with added fudge factors)
%% --------------------
documentclass{article}
usepackage{tikz}
usepackage{tikzpagenodes}
usepackage[export]{adjustbox}

usepackage[paperwidth=7.0cm,showframe]{geometry}

newcommand*{DesiredSkipAboveTitle}{5pt}
newcommand*{DesiredSkipBelowTitle}{10pt}

newcommand*{NumberOfTitleLines}{2}
newcommand*{Title}{A Title that Takes Up Two Lines}
newsavebox{TitleBox}
newcommand*{SetupTitleBox}[2]{%
setbox#1vbox{%
bfseriescentering%
#2%
%strut%
par%
}%
}

newcommand*{BodyTextLarge}{%
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Integer id nisl porta, porttitor tellus sed, sollicitudin erat.
Aliquam tellus urna, feugiat et tortor quis, aliquam mattis orci.%
}%
newcommand*{BodyTextSmall}{%
Small Text.%
}%

newcommand{SelectedBodyText}{BodyTextLarge}
%newcommand{SelectedBodyText}{BodyTextSmall}


newsavebox{BodyBox}
newcommand{SetupBodyBox}[2]{%
setbox#1vbox{%
noindent#2%
%strut%
}%
}%

newlength{AdditionalSkip}
newcommand*{ShowTextGuideLines}[1]{%
begin{tikzpicture}[remember picture, overlay]
coordinate (X) at (current page text area.north west);

draw [draw=red, fill=yellow, fill opacity=0.2]
(X) circle (1pt);%% DEBUGGING: Ensure (X) is the correct spot.

setlength{AdditionalSkip}{DesiredSkipAboveTitle}%
foreach X in {1, ..., #1} {%
ifnumX>NumberOfTitleLines
%% After title: need to adjust AdditionalSkip for space after title
setlength{AdditionalSkip}{%
dimexprDesiredSkipAboveTitle+DesiredSkipBelowTitlerelax%
}%
fi
draw [thin, red] ([yshift=-Xbaselineskip-AdditionalSkip]X) -- ++ (hsize,0);
}%

tikzset{Node Style/.style={anchor=south, draw=red, inner sep=1pt}}
ifdefinedUseBoxes
node [Node Style, fill=yellow] at ([xshift=0.5hsize]X) {using boxes};
else
node [Node Style, fill=green] at ([xshift=0.5hsize]X) {Not using boxes};
fi
end{tikzpicture}%
}%


begin{document}
%% ---------------------------------------------------------- Set up the title and body
SetupTitleBox{TitleBox}{Title}%
SetupBodyBox{BodyBox}{SelectedBodyText}%
%% ---------------------------------------------------------- Title
hbox{}kern-topskip%% See https://tex.stackexchange.com/questions/7676/why-does-vspace0pt-add-vertical-space#comment12433_7681
vspace*{DesiredSkipAboveTitle}%
%%
ifdefinedUseBoxes
ifdefinedUseAdjustBox% Following tweaks seem to be close
noindentadjustbox{valign=t,raise=-1.3pt}{usebox{TitleBox}}%
vspace*{DesiredSkipBelowTitle}%
parnoindentadjustbox{valign=t,raise=-13.3pt}{usebox{BodyBox}}%
else
noindentusebox{TitleBox}%
vspace*{DesiredSkipBelowTitle}%
parnoindentusebox{BodyBox}%
fi
else
noindent{bfseriescenteringTitlepar}%
vspace*{DesiredSkipBelowTitle}%
parnoindentSelectedBodyText%
fi
noindentShowTextGuideLines{7}%
end{document}









share|improve this question















On the left (obtained from the MWE below), I do not use boxes for the title nor the body text and the title and the text appears to be in the correct spot.



However, when I attempt to use newsaveboxs (MWE with defUseBoxes{} uncommented) I obtain the results on the right.



enter image description hereenter image description here



Questions:




  • What are this additional skip's being inserted and how do I get both to exhibit identical behavior?

  • In my actual use case I can tweak the addition of the struts to get correct behavior, but that also seems to need a conditional based on if the body text is more than one line. My understanding is that I should only need a strut at the end of the body text, but that does not seem to work here.


Notes:




  • A vertical space defined by DesiredSkipAboveTitle is added before the title and a DesiredSkipBelowTitle is added after the title.

  • Need to run at least twice to get the tikz drawing in correct position.

  • The adjustbox package can be used to move the boxes into what I think is the correct position. This can be seen with both defUseBoxes{} and defUseAdjustBox{} uncommented. From this we see that the TitleBox needs a raise=-1.3pt option and the BodyBox requires a raise=-13.3pt with the align=t. I would like to know where these amounts come from.


Code:



defUseBoxes{}% Uncomment to use boxes
%defUseAdjustBox{}% Uncomment to use adjustbox (with added fudge factors)
%% --------------------
documentclass{article}
usepackage{tikz}
usepackage{tikzpagenodes}
usepackage[export]{adjustbox}

usepackage[paperwidth=7.0cm,showframe]{geometry}

newcommand*{DesiredSkipAboveTitle}{5pt}
newcommand*{DesiredSkipBelowTitle}{10pt}

newcommand*{NumberOfTitleLines}{2}
newcommand*{Title}{A Title that Takes Up Two Lines}
newsavebox{TitleBox}
newcommand*{SetupTitleBox}[2]{%
setbox#1vbox{%
bfseriescentering%
#2%
%strut%
par%
}%
}

newcommand*{BodyTextLarge}{%
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Integer id nisl porta, porttitor tellus sed, sollicitudin erat.
Aliquam tellus urna, feugiat et tortor quis, aliquam mattis orci.%
}%
newcommand*{BodyTextSmall}{%
Small Text.%
}%

newcommand{SelectedBodyText}{BodyTextLarge}
%newcommand{SelectedBodyText}{BodyTextSmall}


newsavebox{BodyBox}
newcommand{SetupBodyBox}[2]{%
setbox#1vbox{%
noindent#2%
%strut%
}%
}%

newlength{AdditionalSkip}
newcommand*{ShowTextGuideLines}[1]{%
begin{tikzpicture}[remember picture, overlay]
coordinate (X) at (current page text area.north west);

draw [draw=red, fill=yellow, fill opacity=0.2]
(X) circle (1pt);%% DEBUGGING: Ensure (X) is the correct spot.

setlength{AdditionalSkip}{DesiredSkipAboveTitle}%
foreach X in {1, ..., #1} {%
ifnumX>NumberOfTitleLines
%% After title: need to adjust AdditionalSkip for space after title
setlength{AdditionalSkip}{%
dimexprDesiredSkipAboveTitle+DesiredSkipBelowTitlerelax%
}%
fi
draw [thin, red] ([yshift=-Xbaselineskip-AdditionalSkip]X) -- ++ (hsize,0);
}%

tikzset{Node Style/.style={anchor=south, draw=red, inner sep=1pt}}
ifdefinedUseBoxes
node [Node Style, fill=yellow] at ([xshift=0.5hsize]X) {using boxes};
else
node [Node Style, fill=green] at ([xshift=0.5hsize]X) {Not using boxes};
fi
end{tikzpicture}%
}%


begin{document}
%% ---------------------------------------------------------- Set up the title and body
SetupTitleBox{TitleBox}{Title}%
SetupBodyBox{BodyBox}{SelectedBodyText}%
%% ---------------------------------------------------------- Title
hbox{}kern-topskip%% See https://tex.stackexchange.com/questions/7676/why-does-vspace0pt-add-vertical-space#comment12433_7681
vspace*{DesiredSkipAboveTitle}%
%%
ifdefinedUseBoxes
ifdefinedUseAdjustBox% Following tweaks seem to be close
noindentadjustbox{valign=t,raise=-1.3pt}{usebox{TitleBox}}%
vspace*{DesiredSkipBelowTitle}%
parnoindentadjustbox{valign=t,raise=-13.3pt}{usebox{BodyBox}}%
else
noindentusebox{TitleBox}%
vspace*{DesiredSkipBelowTitle}%
parnoindentusebox{BodyBox}%
fi
else
noindent{bfseriescenteringTitlepar}%
vspace*{DesiredSkipBelowTitle}%
parnoindentSelectedBodyText%
fi
noindentShowTextGuideLines{7}%
end{document}






spacing vertical-alignment boxes dimensions






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Dec 7 at 0:44

























asked Dec 3 at 20:05









Peter Grill

163k24432744




163k24432744






This question has an open bounty worth +400
reputation from Peter Grill ending in 6 days.


One or more of the answers is exemplary and worthy of an additional bounty.


The detailed explanation was very helpful in understanding the issues.








This question has an open bounty worth +400
reputation from Peter Grill ending in 6 days.


One or more of the answers is exemplary and worthy of an additional bounty.


The detailed explanation was very helpful in understanding the issues.










  • 2




    you used vbox rather than vtop (not that either of them are really latex)
    – David Carlisle
    Dec 3 at 20:41










  • @DavidCarlisle: Using vtop instead of vbox seems to resolve the issue with the TitleBox, but not the BodyBox?
    – Peter Grill
    Dec 3 at 20:57








  • 2




    I didn't actually run the code, I'll look in a bit:-) but getting spacing around boxes right is tricky you need to control the first and last baseline but boxes only have one reference point, hence latex3 coffins, boxes with handles.
    – David Carlisle
    Dec 3 at 20:58














  • 2




    you used vbox rather than vtop (not that either of them are really latex)
    – David Carlisle
    Dec 3 at 20:41










  • @DavidCarlisle: Using vtop instead of vbox seems to resolve the issue with the TitleBox, but not the BodyBox?
    – Peter Grill
    Dec 3 at 20:57








  • 2




    I didn't actually run the code, I'll look in a bit:-) but getting spacing around boxes right is tricky you need to control the first and last baseline but boxes only have one reference point, hence latex3 coffins, boxes with handles.
    – David Carlisle
    Dec 3 at 20:58








2




2




you used vbox rather than vtop (not that either of them are really latex)
– David Carlisle
Dec 3 at 20:41




you used vbox rather than vtop (not that either of them are really latex)
– David Carlisle
Dec 3 at 20:41












@DavidCarlisle: Using vtop instead of vbox seems to resolve the issue with the TitleBox, but not the BodyBox?
– Peter Grill
Dec 3 at 20:57






@DavidCarlisle: Using vtop instead of vbox seems to resolve the issue with the TitleBox, but not the BodyBox?
– Peter Grill
Dec 3 at 20:57






2




2




I didn't actually run the code, I'll look in a bit:-) but getting spacing around boxes right is tricky you need to control the first and last baseline but boxes only have one reference point, hence latex3 coffins, boxes with handles.
– David Carlisle
Dec 3 at 20:58




I didn't actually run the code, I'll look in a bit:-) but getting spacing around boxes right is tricky you need to control the first and last baseline but boxes only have one reference point, hence latex3 coffins, boxes with handles.
– David Carlisle
Dec 3 at 20:58










1 Answer
1






active

oldest

votes

















up vote
13
down vote



accepted
+200










I think it would probably be useful to understand how TeX normally inserts vertical space between lines/boxes.
This will make it clear why this spacing is different when you wrap your paragraphs in a vbox, as in your MWE.
A method to achieve identical spacing with and without boxes can be found at the bottom this answer.



About baselineskip and lineskip



TeX normally tries to insert an appropriate amount of space between lines so that subsequent baselines are separated by baselineskip.
If it fails to do so without causing the distance between the boxes containing these lines to fall below lineskiplimit it'll insert lineskip instead.
Since lineskiplimit=0pt by default, this happens precisely when the boxes would otherwise overlap.



Because baselineskip=12pt, and lineskiplimit=0pt by default for a 10pt document, this means that if the distance between consecutive baselines is 12pt if the sum of the depth of the top line and the height of the bottom line is less than 12pt. Otherwise, the (boxes containing these) lines will be separated by lineskip (which is 1pt by default).



Here's an illustration:



parskip=0pt parindent=0pt
An ordinary line of text
par
Another ordinary line of text

vspace{baselineskip}

rule[-5pt]{10pt}{15pt}This line has a depth of texttt{5pt}
par
rule[-5pt]{10pt}{15pt}This line has a height of texttt{10pt}


iillutration



Note that if you manually add additional skips, these are just added to whatever amount of space would otherwise be inserted:



parskip=0pt parindent=0pt
An ordinary line of text
parvspace{2mm}
Another ordinary line of text

vspace{baselineskip}

rule[-5pt]{10pt}{15pt}This line has a depth of texttt{5pt}
parvspace{2mm}
rule[-5pt]{10pt}{15pt}This line has a height of texttt{10pt}


with vspace



The same thing, but with vertical boxes



It is no different for your vboxes. A vbox containing multiple lines has just a single baseline, which coincides with the baseline of the bottom line of its contents (top line for a vtop).
If TeX can't separate these baselines by exactly baselineskip without the boxes overlapping, it will completely ignore baselineskip, put a vertical space of lineskip between these boxes and call it a day.



This is the result: (green lines indicate the baselines)



parskip=0pt parindent=0pt
vbox{The first vbox containing\two lines}
par
vbox{The first vbox containing\two lines}


vboxes



So the distance between the baseline of the second line of the first box and the baseline of the first line of the second box is equal to the depth of the former line plus the height of the latter line plus lineskip.
In this case I think that's 0pt + 6.94444pt + 1pt = 7.94444pt (the second number is how tall the letters h, d and b are).
Adding a strut or some character with descenders (gjpqy) to the first vbox would increase this distance by increasing the depth of the last line of this box.



The same thing happens between consecutive vtops:



parskip=0pt parindent=0pt
vtop{The first vtop containing\two lines}
par
vtop{The first vtop containing\two lines}


vtops



The only situation that likely matches your expected/desired outcome is the following:



parskip=0pt parindent=0pt
vbox{A vbox containing\two lines}
par
vtop{A vtop containing\two lines}


vbox and vtop## Heading ##



This happens because the baselines are close enough together that they can be separated by exactly baselineskip without the boxes overlapping.
The space above this vbox and below this vtop is again going to be "wrong", however.



The top of the page



Something similar happens at the top of the page.
TeX tries to insert a (positive) skip that wil separate the baseline of the first box on a page from the top of the text area by topskip. If it can't, it'll just place the box directly at the top.
The default value of topskip is equal to the font size (so 10pt for a 10pt document).



This is why it matters whether you put a vtop or a vbox at the top of your page.



documentclass{article}
usepackage[showframe]{geometry}
begin{document}
parskip=0pt parindent=0pt
vtop{A vtop containing\two lines}
clearpage
vbox{A vbox containing\two lines}
end{document}


top vtoptop vbox



In your MWE topskip isn't really relevant because you've inserted hbox{}kern-topskip. The hbox creates an empty box that will be separated from the top of the text area by the full topskip, and the subsequent kern undoes this skip.
It is thus as if there is an invisible paragraph at the top of your document that ends where the text area begins, so TeX tries to separate the first box (after this) from the top of the text area by baselineskip and insert lineskip if it can't, exactly as described above.







A possible solution



To get the correct spacing above a vertical box you should use vtop instead of vbox for the reasons outlined above.



To get the right amount of spacing below a vtop you can set its depth to zero and then insert a vertical skip just below the box equal to <n>baselineskip, where <n> is the number of lines in the box beyond the first. This skip can be calculated by taking the depth of the box and rounding down to a multiple baselineskip. You may want to set lineskiplimit=-maxdimen (inside the box, or globally) to ensure that all baselines inside the box are separated by exactly baselineskip.



The macro copyboxwithappropriatespace, which I define below (and which could use a better name), does almost precisely these two things. Instead of setting the depth to zero it sets it to the remainder of the division of the original depth by baselineskip (as suggested by jfbu in the comments).
This matters if the last line of your box is particularly deep or the first line of the next box is particularly high.



makeatletter                %% <-- make @ usable in command sequences
%% Inserts a box of depth 0 and insert an appropriate vspace:
newcommand*copyboxwithappropriatespace[1]{%
begingroup
@tempdima=dp#1relax %% <-- store the depth in @tempdima
count@=@tempdimarelax %% <-- convert to a number
dividecount@ by baselineskip %% <-- divide by baselineskip
dp#1=dimexpr@tempdima-count@baselineskip %% <-- set the depth to to the remainder
copy#1% %% <-- insert the box
dp#1=@tempdimarelax %% <-- reset its depth
vspace*{count@baselineskip}%
endgroup %% ^-- insert count@ baselineskips
}
makeatother %% <-- revert @


This command replaces copy (or usebox).



(If you want, you could define useboxwithappropriatespace by copying this definition, removing dp#1=@tempdimarelax and replacing copy by usebox in this definition.)



documentclass{article}

usepackage{showframe} %% <- show boundary of text area
usepackage{tikz} %% <- To draw the red brackets
usepackage{tikzpagenodes} %% <- to place them correctly

makeatletter %% <-- make @ usable in command sequences
%% Inserts a box of depth 0 and insert an appropriate vspace:
newcommand*copyboxwithappropriatespace[1]{%
begingroup
@tempdima=dp#1relax %% <-- store the depth in @tempdima
count@=@tempdimarelax %% <-- convert to a number
dividecount@ by baselineskip %% <-- divide by baselineskip
dp#1=dimexpr@tempdima-count@baselineskip %% <-- set the depth to to the remainder
copy#1% %% <-- insert the box
dp#1=@tempdimarelax %% <-- reset its depth
vspace*{count@baselineskip}%
endgroup %% ^-- insert count@ baselineskips
}
makeatother %% <-- revert @

parindent=0pt %% <- saves me from having to type noindent
parskip=0pt %% <- default value is "0pt plus 1pt"

begin{document}

newsavebox{TitleBox}
newsavebox{BodyBox}
setboxTitleBox=vtop{textbf{A title that takes\up more than\two lines}}
setboxBodyBox=vtop{Lorem\ipsum}

copyboxwithappropriatespaceTitleBox

copyboxwithappropriatespaceBodyBox

copyboxwithappropriatespaceBodyBox

copyboxwithappropriatespaceBodyBox

begin{tikzpicture}[remember picture, overlay,]
draw[red] (current page text area.north west) -- +(-2pt,0)
-- node[left]{texttt{detokenize{topskip}}} +(-2pt,-topskip) -- +(0,-topskip);
foreach X in {0, ..., 7} {
draw[red] ([yshift=-topskip-Xbaselineskip]current page text area.north west) -- +(-2pt,0)
-- node[left]{texttt{detokenize{baselineskip}}} +(-2pt,-baselineskip) -- +(0,-baselineskip);
}
end{tikzpicture}

clearpage

textbf{A title that takes\up more than\two lines}

Lorem\ipsum par Lorem\ipsum par Lorem\ipsum

end{document}


This produces two pages that look identical (apart from the fact that only the first has the red brackets) and which were constructed differently: one using vertical boxes and one using ordinary paragraphs.



output



Note that I've set parskip=0pt because the default value of parskip is not equal to 0pt but to 0pt plus 1pt.
This means that the amount of space between paragraphs can be stretched to up to 1pt to improve the page layout, which I'm guessing you don't want to happen.





In your MWE



The spacing in your MWE will be correct if you replace vbox by top and replace usebox by copyboxwithappropriatespace (and add the above definition to your preamble, of course):



defUseBoxes{}% Uncomment to use boxes
%% --------------------
documentclass{article}
usepackage{tikz}
usepackage{tikzpagenodes}

usepackage[paperwidth=7.0cm,showframe]{geometry}

newcommand*{DesiredSkipAboveTitle}{5pt}
newcommand*{DesiredSkipBelowTitle}{10pt}

newcommand*{NumberOfTitleLines}{2}
newcommand*{Title}{A Title that Takes Up Two Lines}
newsavebox{TitleBox}
newcommand*{SetupTitleBox}[2]{%
setbox#1vtop{%
bfseriescentering%
#2%
%strut%
par%
}%
}

newcommand*{BodyTextLarge}{%
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Integer id nisl porta, porttitor tellus sed, sollicitudin erat.
Aliquam tellus urna, feugiat et tortor quis, aliquam mattis orci.%
}%
newcommand*{BodyTextSmall}{%
Small Text.%
}%

newcommand{SelectedBodyText}{BodyTextLarge}
%newcommand{SelectedBodyText}{BodyTextSmall}


newsavebox{BodyBox}
newcommand{SetupBodyBox}[2]{%
setbox#1vtop{%
noindent#2%
%strut%
}%
}%

makeatletter %% <-- make @ usable in command sequences
%% Inserts a box of depth 0 and insert an appropriate vspace:
newcommand*copyboxwithappropriatespace[1]{%
begingroup
@tempdima=dp#1relax %% <-- store the depth in @tempdima
count@=@tempdimarelax %% <-- convert to a number
dividecount@ by baselineskip %% <-- divide by baselineskip
dp#1=dimexpr@tempdima-count@baselineskip %% <-- set the depth to to the remainder
copy#1% %% <-- insert the box
dp#1=@tempdimarelax %% <-- reset its depth
vspace*{count@baselineskip}%
endgroup %% ^-- insert count@ baselineskips
}
makeatother %% <-- revert @

newlength{AdditionalSkip}
newcommand*{ShowTextGuideLines}[1]{%
begin{tikzpicture}[remember picture, overlay]
coordinate (X) at (current page text area.north west);

draw [draw=red, fill=yellow, fill opacity=0.2]
(X) circle (1pt);%% DEBUGGING: Ensure (X) is the correct spot.

setlength{AdditionalSkip}{DesiredSkipAboveTitle}%
foreach X in {1, ..., #1} {%
ifnumX>NumberOfTitleLines
%% After title: need to adjust AdditionalSkip for space after title
setlength{AdditionalSkip}{%
dimexprDesiredSkipAboveTitle+DesiredSkipBelowTitlerelax%
}%
fi
draw [thin, red] ([yshift=-Xbaselineskip-AdditionalSkip]X) -- ++ (hsize,0);
}%

tikzset{Node Style/.style={anchor=south, draw=red, inner sep=1pt}}
ifdefinedUseBoxes
node [Node Style, fill=yellow] at ([xshift=0.5hsize]X) {using boxes};
else
node [Node Style, fill=green] at ([xshift=0.5hsize]X) {Not using boxes};
fi
end{tikzpicture}%
}%


begin{document}
%% ---------------------------------------------------------- Set up the title and body
SetupTitleBox{TitleBox}{Title}%
SetupBodyBox{BodyBox}{SelectedBodyText}%
%% ---------------------------------------------------------- Title
hbox{}kern-topskip%% See https://tex.stackexchange.com/questions/7676/why-does-vspace0pt-add-vertical-space#comment12433_7681
vspace*{DesiredSkipAboveTitle}%
%%
ifdefinedUseBoxes
noindentcopyboxwithappropriatespace{TitleBox}%
vspace*{DesiredSkipBelowTitle}%
parnoindentcopyboxwithappropriatespace{BodyBox}%
else
noindent{bfseriescenteringTitlepar}%
vspace*{DesiredSkipBelowTitle}%
parnoindentSelectedBodyText%
fi
noindentShowTextGuideLines{7}%
end{document}


enter image description here






share|improve this answer



















  • 1




    Thanks for the detailed explanation.
    – Peter Grill
    yesterday










  • You're welcome, and thank you for your generosity. The second bounty wasn't necessary.
    – Circumscribe
    yesterday










  • +1 but I believe there is an issue in copyboxwithappropriatespace: it should not "insert multiple of baselineskip closest to @tempdima" but "insert largest multiple of baselineskip less than @tempdima". And not set the dp of the inserted box to zero, but to the remainder of @tempdima (i.e. the actual depth of the vtop box) after removing "largest multiple of baselineskip". To obtain that largest integer you can do count@@tempdima then dividecount@ by baselineskip. Then count@ has this largest integer and it times baselineskip is to be removed.
    – jfbu
    22 hours ago












  • @jfbu: You're right. The definition of copyboxwithappropriatespace isn't perfect and was really only intended for this specific use case. If your boxes contain just normal lines of text, then it doesn't really matter whether you round down or to the nearest integer and this missed depth isn't very relevant, but in general it would be better to do precisely what you say. (It is annoying that dimexpr doesn't just round down btw…)
    – Circumscribe
    19 hours ago










  • I only just now thought of a better way to accomplish the same thing: you can create both a vbox and a vtop and then insert a vtop with the depth of the vbox and a vspace* equal to the difference between their depths! This would also work if the distance between the baselines inside the box varies, (My answer is getting a little long though…)
    – Circumscribe
    19 hours ago













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',
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
});


}
});














draft saved

draft discarded


















StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2ftex.stackexchange.com%2fquestions%2f463039%2fspacing-difference-when-using-boxes%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown

























1 Answer
1






active

oldest

votes








1 Answer
1






active

oldest

votes









active

oldest

votes






active

oldest

votes








up vote
13
down vote



accepted
+200










I think it would probably be useful to understand how TeX normally inserts vertical space between lines/boxes.
This will make it clear why this spacing is different when you wrap your paragraphs in a vbox, as in your MWE.
A method to achieve identical spacing with and without boxes can be found at the bottom this answer.



About baselineskip and lineskip



TeX normally tries to insert an appropriate amount of space between lines so that subsequent baselines are separated by baselineskip.
If it fails to do so without causing the distance between the boxes containing these lines to fall below lineskiplimit it'll insert lineskip instead.
Since lineskiplimit=0pt by default, this happens precisely when the boxes would otherwise overlap.



Because baselineskip=12pt, and lineskiplimit=0pt by default for a 10pt document, this means that if the distance between consecutive baselines is 12pt if the sum of the depth of the top line and the height of the bottom line is less than 12pt. Otherwise, the (boxes containing these) lines will be separated by lineskip (which is 1pt by default).



Here's an illustration:



parskip=0pt parindent=0pt
An ordinary line of text
par
Another ordinary line of text

vspace{baselineskip}

rule[-5pt]{10pt}{15pt}This line has a depth of texttt{5pt}
par
rule[-5pt]{10pt}{15pt}This line has a height of texttt{10pt}


iillutration



Note that if you manually add additional skips, these are just added to whatever amount of space would otherwise be inserted:



parskip=0pt parindent=0pt
An ordinary line of text
parvspace{2mm}
Another ordinary line of text

vspace{baselineskip}

rule[-5pt]{10pt}{15pt}This line has a depth of texttt{5pt}
parvspace{2mm}
rule[-5pt]{10pt}{15pt}This line has a height of texttt{10pt}


with vspace



The same thing, but with vertical boxes



It is no different for your vboxes. A vbox containing multiple lines has just a single baseline, which coincides with the baseline of the bottom line of its contents (top line for a vtop).
If TeX can't separate these baselines by exactly baselineskip without the boxes overlapping, it will completely ignore baselineskip, put a vertical space of lineskip between these boxes and call it a day.



This is the result: (green lines indicate the baselines)



parskip=0pt parindent=0pt
vbox{The first vbox containing\two lines}
par
vbox{The first vbox containing\two lines}


vboxes



So the distance between the baseline of the second line of the first box and the baseline of the first line of the second box is equal to the depth of the former line plus the height of the latter line plus lineskip.
In this case I think that's 0pt + 6.94444pt + 1pt = 7.94444pt (the second number is how tall the letters h, d and b are).
Adding a strut or some character with descenders (gjpqy) to the first vbox would increase this distance by increasing the depth of the last line of this box.



The same thing happens between consecutive vtops:



parskip=0pt parindent=0pt
vtop{The first vtop containing\two lines}
par
vtop{The first vtop containing\two lines}


vtops



The only situation that likely matches your expected/desired outcome is the following:



parskip=0pt parindent=0pt
vbox{A vbox containing\two lines}
par
vtop{A vtop containing\two lines}


vbox and vtop## Heading ##



This happens because the baselines are close enough together that they can be separated by exactly baselineskip without the boxes overlapping.
The space above this vbox and below this vtop is again going to be "wrong", however.



The top of the page



Something similar happens at the top of the page.
TeX tries to insert a (positive) skip that wil separate the baseline of the first box on a page from the top of the text area by topskip. If it can't, it'll just place the box directly at the top.
The default value of topskip is equal to the font size (so 10pt for a 10pt document).



This is why it matters whether you put a vtop or a vbox at the top of your page.



documentclass{article}
usepackage[showframe]{geometry}
begin{document}
parskip=0pt parindent=0pt
vtop{A vtop containing\two lines}
clearpage
vbox{A vbox containing\two lines}
end{document}


top vtoptop vbox



In your MWE topskip isn't really relevant because you've inserted hbox{}kern-topskip. The hbox creates an empty box that will be separated from the top of the text area by the full topskip, and the subsequent kern undoes this skip.
It is thus as if there is an invisible paragraph at the top of your document that ends where the text area begins, so TeX tries to separate the first box (after this) from the top of the text area by baselineskip and insert lineskip if it can't, exactly as described above.







A possible solution



To get the correct spacing above a vertical box you should use vtop instead of vbox for the reasons outlined above.



To get the right amount of spacing below a vtop you can set its depth to zero and then insert a vertical skip just below the box equal to <n>baselineskip, where <n> is the number of lines in the box beyond the first. This skip can be calculated by taking the depth of the box and rounding down to a multiple baselineskip. You may want to set lineskiplimit=-maxdimen (inside the box, or globally) to ensure that all baselines inside the box are separated by exactly baselineskip.



The macro copyboxwithappropriatespace, which I define below (and which could use a better name), does almost precisely these two things. Instead of setting the depth to zero it sets it to the remainder of the division of the original depth by baselineskip (as suggested by jfbu in the comments).
This matters if the last line of your box is particularly deep or the first line of the next box is particularly high.



makeatletter                %% <-- make @ usable in command sequences
%% Inserts a box of depth 0 and insert an appropriate vspace:
newcommand*copyboxwithappropriatespace[1]{%
begingroup
@tempdima=dp#1relax %% <-- store the depth in @tempdima
count@=@tempdimarelax %% <-- convert to a number
dividecount@ by baselineskip %% <-- divide by baselineskip
dp#1=dimexpr@tempdima-count@baselineskip %% <-- set the depth to to the remainder
copy#1% %% <-- insert the box
dp#1=@tempdimarelax %% <-- reset its depth
vspace*{count@baselineskip}%
endgroup %% ^-- insert count@ baselineskips
}
makeatother %% <-- revert @


This command replaces copy (or usebox).



(If you want, you could define useboxwithappropriatespace by copying this definition, removing dp#1=@tempdimarelax and replacing copy by usebox in this definition.)



documentclass{article}

usepackage{showframe} %% <- show boundary of text area
usepackage{tikz} %% <- To draw the red brackets
usepackage{tikzpagenodes} %% <- to place them correctly

makeatletter %% <-- make @ usable in command sequences
%% Inserts a box of depth 0 and insert an appropriate vspace:
newcommand*copyboxwithappropriatespace[1]{%
begingroup
@tempdima=dp#1relax %% <-- store the depth in @tempdima
count@=@tempdimarelax %% <-- convert to a number
dividecount@ by baselineskip %% <-- divide by baselineskip
dp#1=dimexpr@tempdima-count@baselineskip %% <-- set the depth to to the remainder
copy#1% %% <-- insert the box
dp#1=@tempdimarelax %% <-- reset its depth
vspace*{count@baselineskip}%
endgroup %% ^-- insert count@ baselineskips
}
makeatother %% <-- revert @

parindent=0pt %% <- saves me from having to type noindent
parskip=0pt %% <- default value is "0pt plus 1pt"

begin{document}

newsavebox{TitleBox}
newsavebox{BodyBox}
setboxTitleBox=vtop{textbf{A title that takes\up more than\two lines}}
setboxBodyBox=vtop{Lorem\ipsum}

copyboxwithappropriatespaceTitleBox

copyboxwithappropriatespaceBodyBox

copyboxwithappropriatespaceBodyBox

copyboxwithappropriatespaceBodyBox

begin{tikzpicture}[remember picture, overlay,]
draw[red] (current page text area.north west) -- +(-2pt,0)
-- node[left]{texttt{detokenize{topskip}}} +(-2pt,-topskip) -- +(0,-topskip);
foreach X in {0, ..., 7} {
draw[red] ([yshift=-topskip-Xbaselineskip]current page text area.north west) -- +(-2pt,0)
-- node[left]{texttt{detokenize{baselineskip}}} +(-2pt,-baselineskip) -- +(0,-baselineskip);
}
end{tikzpicture}

clearpage

textbf{A title that takes\up more than\two lines}

Lorem\ipsum par Lorem\ipsum par Lorem\ipsum

end{document}


This produces two pages that look identical (apart from the fact that only the first has the red brackets) and which were constructed differently: one using vertical boxes and one using ordinary paragraphs.



output



Note that I've set parskip=0pt because the default value of parskip is not equal to 0pt but to 0pt plus 1pt.
This means that the amount of space between paragraphs can be stretched to up to 1pt to improve the page layout, which I'm guessing you don't want to happen.





In your MWE



The spacing in your MWE will be correct if you replace vbox by top and replace usebox by copyboxwithappropriatespace (and add the above definition to your preamble, of course):



defUseBoxes{}% Uncomment to use boxes
%% --------------------
documentclass{article}
usepackage{tikz}
usepackage{tikzpagenodes}

usepackage[paperwidth=7.0cm,showframe]{geometry}

newcommand*{DesiredSkipAboveTitle}{5pt}
newcommand*{DesiredSkipBelowTitle}{10pt}

newcommand*{NumberOfTitleLines}{2}
newcommand*{Title}{A Title that Takes Up Two Lines}
newsavebox{TitleBox}
newcommand*{SetupTitleBox}[2]{%
setbox#1vtop{%
bfseriescentering%
#2%
%strut%
par%
}%
}

newcommand*{BodyTextLarge}{%
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Integer id nisl porta, porttitor tellus sed, sollicitudin erat.
Aliquam tellus urna, feugiat et tortor quis, aliquam mattis orci.%
}%
newcommand*{BodyTextSmall}{%
Small Text.%
}%

newcommand{SelectedBodyText}{BodyTextLarge}
%newcommand{SelectedBodyText}{BodyTextSmall}


newsavebox{BodyBox}
newcommand{SetupBodyBox}[2]{%
setbox#1vtop{%
noindent#2%
%strut%
}%
}%

makeatletter %% <-- make @ usable in command sequences
%% Inserts a box of depth 0 and insert an appropriate vspace:
newcommand*copyboxwithappropriatespace[1]{%
begingroup
@tempdima=dp#1relax %% <-- store the depth in @tempdima
count@=@tempdimarelax %% <-- convert to a number
dividecount@ by baselineskip %% <-- divide by baselineskip
dp#1=dimexpr@tempdima-count@baselineskip %% <-- set the depth to to the remainder
copy#1% %% <-- insert the box
dp#1=@tempdimarelax %% <-- reset its depth
vspace*{count@baselineskip}%
endgroup %% ^-- insert count@ baselineskips
}
makeatother %% <-- revert @

newlength{AdditionalSkip}
newcommand*{ShowTextGuideLines}[1]{%
begin{tikzpicture}[remember picture, overlay]
coordinate (X) at (current page text area.north west);

draw [draw=red, fill=yellow, fill opacity=0.2]
(X) circle (1pt);%% DEBUGGING: Ensure (X) is the correct spot.

setlength{AdditionalSkip}{DesiredSkipAboveTitle}%
foreach X in {1, ..., #1} {%
ifnumX>NumberOfTitleLines
%% After title: need to adjust AdditionalSkip for space after title
setlength{AdditionalSkip}{%
dimexprDesiredSkipAboveTitle+DesiredSkipBelowTitlerelax%
}%
fi
draw [thin, red] ([yshift=-Xbaselineskip-AdditionalSkip]X) -- ++ (hsize,0);
}%

tikzset{Node Style/.style={anchor=south, draw=red, inner sep=1pt}}
ifdefinedUseBoxes
node [Node Style, fill=yellow] at ([xshift=0.5hsize]X) {using boxes};
else
node [Node Style, fill=green] at ([xshift=0.5hsize]X) {Not using boxes};
fi
end{tikzpicture}%
}%


begin{document}
%% ---------------------------------------------------------- Set up the title and body
SetupTitleBox{TitleBox}{Title}%
SetupBodyBox{BodyBox}{SelectedBodyText}%
%% ---------------------------------------------------------- Title
hbox{}kern-topskip%% See https://tex.stackexchange.com/questions/7676/why-does-vspace0pt-add-vertical-space#comment12433_7681
vspace*{DesiredSkipAboveTitle}%
%%
ifdefinedUseBoxes
noindentcopyboxwithappropriatespace{TitleBox}%
vspace*{DesiredSkipBelowTitle}%
parnoindentcopyboxwithappropriatespace{BodyBox}%
else
noindent{bfseriescenteringTitlepar}%
vspace*{DesiredSkipBelowTitle}%
parnoindentSelectedBodyText%
fi
noindentShowTextGuideLines{7}%
end{document}


enter image description here






share|improve this answer



















  • 1




    Thanks for the detailed explanation.
    – Peter Grill
    yesterday










  • You're welcome, and thank you for your generosity. The second bounty wasn't necessary.
    – Circumscribe
    yesterday










  • +1 but I believe there is an issue in copyboxwithappropriatespace: it should not "insert multiple of baselineskip closest to @tempdima" but "insert largest multiple of baselineskip less than @tempdima". And not set the dp of the inserted box to zero, but to the remainder of @tempdima (i.e. the actual depth of the vtop box) after removing "largest multiple of baselineskip". To obtain that largest integer you can do count@@tempdima then dividecount@ by baselineskip. Then count@ has this largest integer and it times baselineskip is to be removed.
    – jfbu
    22 hours ago












  • @jfbu: You're right. The definition of copyboxwithappropriatespace isn't perfect and was really only intended for this specific use case. If your boxes contain just normal lines of text, then it doesn't really matter whether you round down or to the nearest integer and this missed depth isn't very relevant, but in general it would be better to do precisely what you say. (It is annoying that dimexpr doesn't just round down btw…)
    – Circumscribe
    19 hours ago










  • I only just now thought of a better way to accomplish the same thing: you can create both a vbox and a vtop and then insert a vtop with the depth of the vbox and a vspace* equal to the difference between their depths! This would also work if the distance between the baselines inside the box varies, (My answer is getting a little long though…)
    – Circumscribe
    19 hours ago

















up vote
13
down vote



accepted
+200










I think it would probably be useful to understand how TeX normally inserts vertical space between lines/boxes.
This will make it clear why this spacing is different when you wrap your paragraphs in a vbox, as in your MWE.
A method to achieve identical spacing with and without boxes can be found at the bottom this answer.



About baselineskip and lineskip



TeX normally tries to insert an appropriate amount of space between lines so that subsequent baselines are separated by baselineskip.
If it fails to do so without causing the distance between the boxes containing these lines to fall below lineskiplimit it'll insert lineskip instead.
Since lineskiplimit=0pt by default, this happens precisely when the boxes would otherwise overlap.



Because baselineskip=12pt, and lineskiplimit=0pt by default for a 10pt document, this means that if the distance between consecutive baselines is 12pt if the sum of the depth of the top line and the height of the bottom line is less than 12pt. Otherwise, the (boxes containing these) lines will be separated by lineskip (which is 1pt by default).



Here's an illustration:



parskip=0pt parindent=0pt
An ordinary line of text
par
Another ordinary line of text

vspace{baselineskip}

rule[-5pt]{10pt}{15pt}This line has a depth of texttt{5pt}
par
rule[-5pt]{10pt}{15pt}This line has a height of texttt{10pt}


iillutration



Note that if you manually add additional skips, these are just added to whatever amount of space would otherwise be inserted:



parskip=0pt parindent=0pt
An ordinary line of text
parvspace{2mm}
Another ordinary line of text

vspace{baselineskip}

rule[-5pt]{10pt}{15pt}This line has a depth of texttt{5pt}
parvspace{2mm}
rule[-5pt]{10pt}{15pt}This line has a height of texttt{10pt}


with vspace



The same thing, but with vertical boxes



It is no different for your vboxes. A vbox containing multiple lines has just a single baseline, which coincides with the baseline of the bottom line of its contents (top line for a vtop).
If TeX can't separate these baselines by exactly baselineskip without the boxes overlapping, it will completely ignore baselineskip, put a vertical space of lineskip between these boxes and call it a day.



This is the result: (green lines indicate the baselines)



parskip=0pt parindent=0pt
vbox{The first vbox containing\two lines}
par
vbox{The first vbox containing\two lines}


vboxes



So the distance between the baseline of the second line of the first box and the baseline of the first line of the second box is equal to the depth of the former line plus the height of the latter line plus lineskip.
In this case I think that's 0pt + 6.94444pt + 1pt = 7.94444pt (the second number is how tall the letters h, d and b are).
Adding a strut or some character with descenders (gjpqy) to the first vbox would increase this distance by increasing the depth of the last line of this box.



The same thing happens between consecutive vtops:



parskip=0pt parindent=0pt
vtop{The first vtop containing\two lines}
par
vtop{The first vtop containing\two lines}


vtops



The only situation that likely matches your expected/desired outcome is the following:



parskip=0pt parindent=0pt
vbox{A vbox containing\two lines}
par
vtop{A vtop containing\two lines}


vbox and vtop## Heading ##



This happens because the baselines are close enough together that they can be separated by exactly baselineskip without the boxes overlapping.
The space above this vbox and below this vtop is again going to be "wrong", however.



The top of the page



Something similar happens at the top of the page.
TeX tries to insert a (positive) skip that wil separate the baseline of the first box on a page from the top of the text area by topskip. If it can't, it'll just place the box directly at the top.
The default value of topskip is equal to the font size (so 10pt for a 10pt document).



This is why it matters whether you put a vtop or a vbox at the top of your page.



documentclass{article}
usepackage[showframe]{geometry}
begin{document}
parskip=0pt parindent=0pt
vtop{A vtop containing\two lines}
clearpage
vbox{A vbox containing\two lines}
end{document}


top vtoptop vbox



In your MWE topskip isn't really relevant because you've inserted hbox{}kern-topskip. The hbox creates an empty box that will be separated from the top of the text area by the full topskip, and the subsequent kern undoes this skip.
It is thus as if there is an invisible paragraph at the top of your document that ends where the text area begins, so TeX tries to separate the first box (after this) from the top of the text area by baselineskip and insert lineskip if it can't, exactly as described above.







A possible solution



To get the correct spacing above a vertical box you should use vtop instead of vbox for the reasons outlined above.



To get the right amount of spacing below a vtop you can set its depth to zero and then insert a vertical skip just below the box equal to <n>baselineskip, where <n> is the number of lines in the box beyond the first. This skip can be calculated by taking the depth of the box and rounding down to a multiple baselineskip. You may want to set lineskiplimit=-maxdimen (inside the box, or globally) to ensure that all baselines inside the box are separated by exactly baselineskip.



The macro copyboxwithappropriatespace, which I define below (and which could use a better name), does almost precisely these two things. Instead of setting the depth to zero it sets it to the remainder of the division of the original depth by baselineskip (as suggested by jfbu in the comments).
This matters if the last line of your box is particularly deep or the first line of the next box is particularly high.



makeatletter                %% <-- make @ usable in command sequences
%% Inserts a box of depth 0 and insert an appropriate vspace:
newcommand*copyboxwithappropriatespace[1]{%
begingroup
@tempdima=dp#1relax %% <-- store the depth in @tempdima
count@=@tempdimarelax %% <-- convert to a number
dividecount@ by baselineskip %% <-- divide by baselineskip
dp#1=dimexpr@tempdima-count@baselineskip %% <-- set the depth to to the remainder
copy#1% %% <-- insert the box
dp#1=@tempdimarelax %% <-- reset its depth
vspace*{count@baselineskip}%
endgroup %% ^-- insert count@ baselineskips
}
makeatother %% <-- revert @


This command replaces copy (or usebox).



(If you want, you could define useboxwithappropriatespace by copying this definition, removing dp#1=@tempdimarelax and replacing copy by usebox in this definition.)



documentclass{article}

usepackage{showframe} %% <- show boundary of text area
usepackage{tikz} %% <- To draw the red brackets
usepackage{tikzpagenodes} %% <- to place them correctly

makeatletter %% <-- make @ usable in command sequences
%% Inserts a box of depth 0 and insert an appropriate vspace:
newcommand*copyboxwithappropriatespace[1]{%
begingroup
@tempdima=dp#1relax %% <-- store the depth in @tempdima
count@=@tempdimarelax %% <-- convert to a number
dividecount@ by baselineskip %% <-- divide by baselineskip
dp#1=dimexpr@tempdima-count@baselineskip %% <-- set the depth to to the remainder
copy#1% %% <-- insert the box
dp#1=@tempdimarelax %% <-- reset its depth
vspace*{count@baselineskip}%
endgroup %% ^-- insert count@ baselineskips
}
makeatother %% <-- revert @

parindent=0pt %% <- saves me from having to type noindent
parskip=0pt %% <- default value is "0pt plus 1pt"

begin{document}

newsavebox{TitleBox}
newsavebox{BodyBox}
setboxTitleBox=vtop{textbf{A title that takes\up more than\two lines}}
setboxBodyBox=vtop{Lorem\ipsum}

copyboxwithappropriatespaceTitleBox

copyboxwithappropriatespaceBodyBox

copyboxwithappropriatespaceBodyBox

copyboxwithappropriatespaceBodyBox

begin{tikzpicture}[remember picture, overlay,]
draw[red] (current page text area.north west) -- +(-2pt,0)
-- node[left]{texttt{detokenize{topskip}}} +(-2pt,-topskip) -- +(0,-topskip);
foreach X in {0, ..., 7} {
draw[red] ([yshift=-topskip-Xbaselineskip]current page text area.north west) -- +(-2pt,0)
-- node[left]{texttt{detokenize{baselineskip}}} +(-2pt,-baselineskip) -- +(0,-baselineskip);
}
end{tikzpicture}

clearpage

textbf{A title that takes\up more than\two lines}

Lorem\ipsum par Lorem\ipsum par Lorem\ipsum

end{document}


This produces two pages that look identical (apart from the fact that only the first has the red brackets) and which were constructed differently: one using vertical boxes and one using ordinary paragraphs.



output



Note that I've set parskip=0pt because the default value of parskip is not equal to 0pt but to 0pt plus 1pt.
This means that the amount of space between paragraphs can be stretched to up to 1pt to improve the page layout, which I'm guessing you don't want to happen.





In your MWE



The spacing in your MWE will be correct if you replace vbox by top and replace usebox by copyboxwithappropriatespace (and add the above definition to your preamble, of course):



defUseBoxes{}% Uncomment to use boxes
%% --------------------
documentclass{article}
usepackage{tikz}
usepackage{tikzpagenodes}

usepackage[paperwidth=7.0cm,showframe]{geometry}

newcommand*{DesiredSkipAboveTitle}{5pt}
newcommand*{DesiredSkipBelowTitle}{10pt}

newcommand*{NumberOfTitleLines}{2}
newcommand*{Title}{A Title that Takes Up Two Lines}
newsavebox{TitleBox}
newcommand*{SetupTitleBox}[2]{%
setbox#1vtop{%
bfseriescentering%
#2%
%strut%
par%
}%
}

newcommand*{BodyTextLarge}{%
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Integer id nisl porta, porttitor tellus sed, sollicitudin erat.
Aliquam tellus urna, feugiat et tortor quis, aliquam mattis orci.%
}%
newcommand*{BodyTextSmall}{%
Small Text.%
}%

newcommand{SelectedBodyText}{BodyTextLarge}
%newcommand{SelectedBodyText}{BodyTextSmall}


newsavebox{BodyBox}
newcommand{SetupBodyBox}[2]{%
setbox#1vtop{%
noindent#2%
%strut%
}%
}%

makeatletter %% <-- make @ usable in command sequences
%% Inserts a box of depth 0 and insert an appropriate vspace:
newcommand*copyboxwithappropriatespace[1]{%
begingroup
@tempdima=dp#1relax %% <-- store the depth in @tempdima
count@=@tempdimarelax %% <-- convert to a number
dividecount@ by baselineskip %% <-- divide by baselineskip
dp#1=dimexpr@tempdima-count@baselineskip %% <-- set the depth to to the remainder
copy#1% %% <-- insert the box
dp#1=@tempdimarelax %% <-- reset its depth
vspace*{count@baselineskip}%
endgroup %% ^-- insert count@ baselineskips
}
makeatother %% <-- revert @

newlength{AdditionalSkip}
newcommand*{ShowTextGuideLines}[1]{%
begin{tikzpicture}[remember picture, overlay]
coordinate (X) at (current page text area.north west);

draw [draw=red, fill=yellow, fill opacity=0.2]
(X) circle (1pt);%% DEBUGGING: Ensure (X) is the correct spot.

setlength{AdditionalSkip}{DesiredSkipAboveTitle}%
foreach X in {1, ..., #1} {%
ifnumX>NumberOfTitleLines
%% After title: need to adjust AdditionalSkip for space after title
setlength{AdditionalSkip}{%
dimexprDesiredSkipAboveTitle+DesiredSkipBelowTitlerelax%
}%
fi
draw [thin, red] ([yshift=-Xbaselineskip-AdditionalSkip]X) -- ++ (hsize,0);
}%

tikzset{Node Style/.style={anchor=south, draw=red, inner sep=1pt}}
ifdefinedUseBoxes
node [Node Style, fill=yellow] at ([xshift=0.5hsize]X) {using boxes};
else
node [Node Style, fill=green] at ([xshift=0.5hsize]X) {Not using boxes};
fi
end{tikzpicture}%
}%


begin{document}
%% ---------------------------------------------------------- Set up the title and body
SetupTitleBox{TitleBox}{Title}%
SetupBodyBox{BodyBox}{SelectedBodyText}%
%% ---------------------------------------------------------- Title
hbox{}kern-topskip%% See https://tex.stackexchange.com/questions/7676/why-does-vspace0pt-add-vertical-space#comment12433_7681
vspace*{DesiredSkipAboveTitle}%
%%
ifdefinedUseBoxes
noindentcopyboxwithappropriatespace{TitleBox}%
vspace*{DesiredSkipBelowTitle}%
parnoindentcopyboxwithappropriatespace{BodyBox}%
else
noindent{bfseriescenteringTitlepar}%
vspace*{DesiredSkipBelowTitle}%
parnoindentSelectedBodyText%
fi
noindentShowTextGuideLines{7}%
end{document}


enter image description here






share|improve this answer



















  • 1




    Thanks for the detailed explanation.
    – Peter Grill
    yesterday










  • You're welcome, and thank you for your generosity. The second bounty wasn't necessary.
    – Circumscribe
    yesterday










  • +1 but I believe there is an issue in copyboxwithappropriatespace: it should not "insert multiple of baselineskip closest to @tempdima" but "insert largest multiple of baselineskip less than @tempdima". And not set the dp of the inserted box to zero, but to the remainder of @tempdima (i.e. the actual depth of the vtop box) after removing "largest multiple of baselineskip". To obtain that largest integer you can do count@@tempdima then dividecount@ by baselineskip. Then count@ has this largest integer and it times baselineskip is to be removed.
    – jfbu
    22 hours ago












  • @jfbu: You're right. The definition of copyboxwithappropriatespace isn't perfect and was really only intended for this specific use case. If your boxes contain just normal lines of text, then it doesn't really matter whether you round down or to the nearest integer and this missed depth isn't very relevant, but in general it would be better to do precisely what you say. (It is annoying that dimexpr doesn't just round down btw…)
    – Circumscribe
    19 hours ago










  • I only just now thought of a better way to accomplish the same thing: you can create both a vbox and a vtop and then insert a vtop with the depth of the vbox and a vspace* equal to the difference between their depths! This would also work if the distance between the baselines inside the box varies, (My answer is getting a little long though…)
    – Circumscribe
    19 hours ago















up vote
13
down vote



accepted
+200







up vote
13
down vote



accepted
+200




+200




I think it would probably be useful to understand how TeX normally inserts vertical space between lines/boxes.
This will make it clear why this spacing is different when you wrap your paragraphs in a vbox, as in your MWE.
A method to achieve identical spacing with and without boxes can be found at the bottom this answer.



About baselineskip and lineskip



TeX normally tries to insert an appropriate amount of space between lines so that subsequent baselines are separated by baselineskip.
If it fails to do so without causing the distance between the boxes containing these lines to fall below lineskiplimit it'll insert lineskip instead.
Since lineskiplimit=0pt by default, this happens precisely when the boxes would otherwise overlap.



Because baselineskip=12pt, and lineskiplimit=0pt by default for a 10pt document, this means that if the distance between consecutive baselines is 12pt if the sum of the depth of the top line and the height of the bottom line is less than 12pt. Otherwise, the (boxes containing these) lines will be separated by lineskip (which is 1pt by default).



Here's an illustration:



parskip=0pt parindent=0pt
An ordinary line of text
par
Another ordinary line of text

vspace{baselineskip}

rule[-5pt]{10pt}{15pt}This line has a depth of texttt{5pt}
par
rule[-5pt]{10pt}{15pt}This line has a height of texttt{10pt}


iillutration



Note that if you manually add additional skips, these are just added to whatever amount of space would otherwise be inserted:



parskip=0pt parindent=0pt
An ordinary line of text
parvspace{2mm}
Another ordinary line of text

vspace{baselineskip}

rule[-5pt]{10pt}{15pt}This line has a depth of texttt{5pt}
parvspace{2mm}
rule[-5pt]{10pt}{15pt}This line has a height of texttt{10pt}


with vspace



The same thing, but with vertical boxes



It is no different for your vboxes. A vbox containing multiple lines has just a single baseline, which coincides with the baseline of the bottom line of its contents (top line for a vtop).
If TeX can't separate these baselines by exactly baselineskip without the boxes overlapping, it will completely ignore baselineskip, put a vertical space of lineskip between these boxes and call it a day.



This is the result: (green lines indicate the baselines)



parskip=0pt parindent=0pt
vbox{The first vbox containing\two lines}
par
vbox{The first vbox containing\two lines}


vboxes



So the distance between the baseline of the second line of the first box and the baseline of the first line of the second box is equal to the depth of the former line plus the height of the latter line plus lineskip.
In this case I think that's 0pt + 6.94444pt + 1pt = 7.94444pt (the second number is how tall the letters h, d and b are).
Adding a strut or some character with descenders (gjpqy) to the first vbox would increase this distance by increasing the depth of the last line of this box.



The same thing happens between consecutive vtops:



parskip=0pt parindent=0pt
vtop{The first vtop containing\two lines}
par
vtop{The first vtop containing\two lines}


vtops



The only situation that likely matches your expected/desired outcome is the following:



parskip=0pt parindent=0pt
vbox{A vbox containing\two lines}
par
vtop{A vtop containing\two lines}


vbox and vtop## Heading ##



This happens because the baselines are close enough together that they can be separated by exactly baselineskip without the boxes overlapping.
The space above this vbox and below this vtop is again going to be "wrong", however.



The top of the page



Something similar happens at the top of the page.
TeX tries to insert a (positive) skip that wil separate the baseline of the first box on a page from the top of the text area by topskip. If it can't, it'll just place the box directly at the top.
The default value of topskip is equal to the font size (so 10pt for a 10pt document).



This is why it matters whether you put a vtop or a vbox at the top of your page.



documentclass{article}
usepackage[showframe]{geometry}
begin{document}
parskip=0pt parindent=0pt
vtop{A vtop containing\two lines}
clearpage
vbox{A vbox containing\two lines}
end{document}


top vtoptop vbox



In your MWE topskip isn't really relevant because you've inserted hbox{}kern-topskip. The hbox creates an empty box that will be separated from the top of the text area by the full topskip, and the subsequent kern undoes this skip.
It is thus as if there is an invisible paragraph at the top of your document that ends where the text area begins, so TeX tries to separate the first box (after this) from the top of the text area by baselineskip and insert lineskip if it can't, exactly as described above.







A possible solution



To get the correct spacing above a vertical box you should use vtop instead of vbox for the reasons outlined above.



To get the right amount of spacing below a vtop you can set its depth to zero and then insert a vertical skip just below the box equal to <n>baselineskip, where <n> is the number of lines in the box beyond the first. This skip can be calculated by taking the depth of the box and rounding down to a multiple baselineskip. You may want to set lineskiplimit=-maxdimen (inside the box, or globally) to ensure that all baselines inside the box are separated by exactly baselineskip.



The macro copyboxwithappropriatespace, which I define below (and which could use a better name), does almost precisely these two things. Instead of setting the depth to zero it sets it to the remainder of the division of the original depth by baselineskip (as suggested by jfbu in the comments).
This matters if the last line of your box is particularly deep or the first line of the next box is particularly high.



makeatletter                %% <-- make @ usable in command sequences
%% Inserts a box of depth 0 and insert an appropriate vspace:
newcommand*copyboxwithappropriatespace[1]{%
begingroup
@tempdima=dp#1relax %% <-- store the depth in @tempdima
count@=@tempdimarelax %% <-- convert to a number
dividecount@ by baselineskip %% <-- divide by baselineskip
dp#1=dimexpr@tempdima-count@baselineskip %% <-- set the depth to to the remainder
copy#1% %% <-- insert the box
dp#1=@tempdimarelax %% <-- reset its depth
vspace*{count@baselineskip}%
endgroup %% ^-- insert count@ baselineskips
}
makeatother %% <-- revert @


This command replaces copy (or usebox).



(If you want, you could define useboxwithappropriatespace by copying this definition, removing dp#1=@tempdimarelax and replacing copy by usebox in this definition.)



documentclass{article}

usepackage{showframe} %% <- show boundary of text area
usepackage{tikz} %% <- To draw the red brackets
usepackage{tikzpagenodes} %% <- to place them correctly

makeatletter %% <-- make @ usable in command sequences
%% Inserts a box of depth 0 and insert an appropriate vspace:
newcommand*copyboxwithappropriatespace[1]{%
begingroup
@tempdima=dp#1relax %% <-- store the depth in @tempdima
count@=@tempdimarelax %% <-- convert to a number
dividecount@ by baselineskip %% <-- divide by baselineskip
dp#1=dimexpr@tempdima-count@baselineskip %% <-- set the depth to to the remainder
copy#1% %% <-- insert the box
dp#1=@tempdimarelax %% <-- reset its depth
vspace*{count@baselineskip}%
endgroup %% ^-- insert count@ baselineskips
}
makeatother %% <-- revert @

parindent=0pt %% <- saves me from having to type noindent
parskip=0pt %% <- default value is "0pt plus 1pt"

begin{document}

newsavebox{TitleBox}
newsavebox{BodyBox}
setboxTitleBox=vtop{textbf{A title that takes\up more than\two lines}}
setboxBodyBox=vtop{Lorem\ipsum}

copyboxwithappropriatespaceTitleBox

copyboxwithappropriatespaceBodyBox

copyboxwithappropriatespaceBodyBox

copyboxwithappropriatespaceBodyBox

begin{tikzpicture}[remember picture, overlay,]
draw[red] (current page text area.north west) -- +(-2pt,0)
-- node[left]{texttt{detokenize{topskip}}} +(-2pt,-topskip) -- +(0,-topskip);
foreach X in {0, ..., 7} {
draw[red] ([yshift=-topskip-Xbaselineskip]current page text area.north west) -- +(-2pt,0)
-- node[left]{texttt{detokenize{baselineskip}}} +(-2pt,-baselineskip) -- +(0,-baselineskip);
}
end{tikzpicture}

clearpage

textbf{A title that takes\up more than\two lines}

Lorem\ipsum par Lorem\ipsum par Lorem\ipsum

end{document}


This produces two pages that look identical (apart from the fact that only the first has the red brackets) and which were constructed differently: one using vertical boxes and one using ordinary paragraphs.



output



Note that I've set parskip=0pt because the default value of parskip is not equal to 0pt but to 0pt plus 1pt.
This means that the amount of space between paragraphs can be stretched to up to 1pt to improve the page layout, which I'm guessing you don't want to happen.





In your MWE



The spacing in your MWE will be correct if you replace vbox by top and replace usebox by copyboxwithappropriatespace (and add the above definition to your preamble, of course):



defUseBoxes{}% Uncomment to use boxes
%% --------------------
documentclass{article}
usepackage{tikz}
usepackage{tikzpagenodes}

usepackage[paperwidth=7.0cm,showframe]{geometry}

newcommand*{DesiredSkipAboveTitle}{5pt}
newcommand*{DesiredSkipBelowTitle}{10pt}

newcommand*{NumberOfTitleLines}{2}
newcommand*{Title}{A Title that Takes Up Two Lines}
newsavebox{TitleBox}
newcommand*{SetupTitleBox}[2]{%
setbox#1vtop{%
bfseriescentering%
#2%
%strut%
par%
}%
}

newcommand*{BodyTextLarge}{%
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Integer id nisl porta, porttitor tellus sed, sollicitudin erat.
Aliquam tellus urna, feugiat et tortor quis, aliquam mattis orci.%
}%
newcommand*{BodyTextSmall}{%
Small Text.%
}%

newcommand{SelectedBodyText}{BodyTextLarge}
%newcommand{SelectedBodyText}{BodyTextSmall}


newsavebox{BodyBox}
newcommand{SetupBodyBox}[2]{%
setbox#1vtop{%
noindent#2%
%strut%
}%
}%

makeatletter %% <-- make @ usable in command sequences
%% Inserts a box of depth 0 and insert an appropriate vspace:
newcommand*copyboxwithappropriatespace[1]{%
begingroup
@tempdima=dp#1relax %% <-- store the depth in @tempdima
count@=@tempdimarelax %% <-- convert to a number
dividecount@ by baselineskip %% <-- divide by baselineskip
dp#1=dimexpr@tempdima-count@baselineskip %% <-- set the depth to to the remainder
copy#1% %% <-- insert the box
dp#1=@tempdimarelax %% <-- reset its depth
vspace*{count@baselineskip}%
endgroup %% ^-- insert count@ baselineskips
}
makeatother %% <-- revert @

newlength{AdditionalSkip}
newcommand*{ShowTextGuideLines}[1]{%
begin{tikzpicture}[remember picture, overlay]
coordinate (X) at (current page text area.north west);

draw [draw=red, fill=yellow, fill opacity=0.2]
(X) circle (1pt);%% DEBUGGING: Ensure (X) is the correct spot.

setlength{AdditionalSkip}{DesiredSkipAboveTitle}%
foreach X in {1, ..., #1} {%
ifnumX>NumberOfTitleLines
%% After title: need to adjust AdditionalSkip for space after title
setlength{AdditionalSkip}{%
dimexprDesiredSkipAboveTitle+DesiredSkipBelowTitlerelax%
}%
fi
draw [thin, red] ([yshift=-Xbaselineskip-AdditionalSkip]X) -- ++ (hsize,0);
}%

tikzset{Node Style/.style={anchor=south, draw=red, inner sep=1pt}}
ifdefinedUseBoxes
node [Node Style, fill=yellow] at ([xshift=0.5hsize]X) {using boxes};
else
node [Node Style, fill=green] at ([xshift=0.5hsize]X) {Not using boxes};
fi
end{tikzpicture}%
}%


begin{document}
%% ---------------------------------------------------------- Set up the title and body
SetupTitleBox{TitleBox}{Title}%
SetupBodyBox{BodyBox}{SelectedBodyText}%
%% ---------------------------------------------------------- Title
hbox{}kern-topskip%% See https://tex.stackexchange.com/questions/7676/why-does-vspace0pt-add-vertical-space#comment12433_7681
vspace*{DesiredSkipAboveTitle}%
%%
ifdefinedUseBoxes
noindentcopyboxwithappropriatespace{TitleBox}%
vspace*{DesiredSkipBelowTitle}%
parnoindentcopyboxwithappropriatespace{BodyBox}%
else
noindent{bfseriescenteringTitlepar}%
vspace*{DesiredSkipBelowTitle}%
parnoindentSelectedBodyText%
fi
noindentShowTextGuideLines{7}%
end{document}


enter image description here






share|improve this answer














I think it would probably be useful to understand how TeX normally inserts vertical space between lines/boxes.
This will make it clear why this spacing is different when you wrap your paragraphs in a vbox, as in your MWE.
A method to achieve identical spacing with and without boxes can be found at the bottom this answer.



About baselineskip and lineskip



TeX normally tries to insert an appropriate amount of space between lines so that subsequent baselines are separated by baselineskip.
If it fails to do so without causing the distance between the boxes containing these lines to fall below lineskiplimit it'll insert lineskip instead.
Since lineskiplimit=0pt by default, this happens precisely when the boxes would otherwise overlap.



Because baselineskip=12pt, and lineskiplimit=0pt by default for a 10pt document, this means that if the distance between consecutive baselines is 12pt if the sum of the depth of the top line and the height of the bottom line is less than 12pt. Otherwise, the (boxes containing these) lines will be separated by lineskip (which is 1pt by default).



Here's an illustration:



parskip=0pt parindent=0pt
An ordinary line of text
par
Another ordinary line of text

vspace{baselineskip}

rule[-5pt]{10pt}{15pt}This line has a depth of texttt{5pt}
par
rule[-5pt]{10pt}{15pt}This line has a height of texttt{10pt}


iillutration



Note that if you manually add additional skips, these are just added to whatever amount of space would otherwise be inserted:



parskip=0pt parindent=0pt
An ordinary line of text
parvspace{2mm}
Another ordinary line of text

vspace{baselineskip}

rule[-5pt]{10pt}{15pt}This line has a depth of texttt{5pt}
parvspace{2mm}
rule[-5pt]{10pt}{15pt}This line has a height of texttt{10pt}


with vspace



The same thing, but with vertical boxes



It is no different for your vboxes. A vbox containing multiple lines has just a single baseline, which coincides with the baseline of the bottom line of its contents (top line for a vtop).
If TeX can't separate these baselines by exactly baselineskip without the boxes overlapping, it will completely ignore baselineskip, put a vertical space of lineskip between these boxes and call it a day.



This is the result: (green lines indicate the baselines)



parskip=0pt parindent=0pt
vbox{The first vbox containing\two lines}
par
vbox{The first vbox containing\two lines}


vboxes



So the distance between the baseline of the second line of the first box and the baseline of the first line of the second box is equal to the depth of the former line plus the height of the latter line plus lineskip.
In this case I think that's 0pt + 6.94444pt + 1pt = 7.94444pt (the second number is how tall the letters h, d and b are).
Adding a strut or some character with descenders (gjpqy) to the first vbox would increase this distance by increasing the depth of the last line of this box.



The same thing happens between consecutive vtops:



parskip=0pt parindent=0pt
vtop{The first vtop containing\two lines}
par
vtop{The first vtop containing\two lines}


vtops



The only situation that likely matches your expected/desired outcome is the following:



parskip=0pt parindent=0pt
vbox{A vbox containing\two lines}
par
vtop{A vtop containing\two lines}


vbox and vtop## Heading ##



This happens because the baselines are close enough together that they can be separated by exactly baselineskip without the boxes overlapping.
The space above this vbox and below this vtop is again going to be "wrong", however.



The top of the page



Something similar happens at the top of the page.
TeX tries to insert a (positive) skip that wil separate the baseline of the first box on a page from the top of the text area by topskip. If it can't, it'll just place the box directly at the top.
The default value of topskip is equal to the font size (so 10pt for a 10pt document).



This is why it matters whether you put a vtop or a vbox at the top of your page.



documentclass{article}
usepackage[showframe]{geometry}
begin{document}
parskip=0pt parindent=0pt
vtop{A vtop containing\two lines}
clearpage
vbox{A vbox containing\two lines}
end{document}


top vtoptop vbox



In your MWE topskip isn't really relevant because you've inserted hbox{}kern-topskip. The hbox creates an empty box that will be separated from the top of the text area by the full topskip, and the subsequent kern undoes this skip.
It is thus as if there is an invisible paragraph at the top of your document that ends where the text area begins, so TeX tries to separate the first box (after this) from the top of the text area by baselineskip and insert lineskip if it can't, exactly as described above.







A possible solution



To get the correct spacing above a vertical box you should use vtop instead of vbox for the reasons outlined above.



To get the right amount of spacing below a vtop you can set its depth to zero and then insert a vertical skip just below the box equal to <n>baselineskip, where <n> is the number of lines in the box beyond the first. This skip can be calculated by taking the depth of the box and rounding down to a multiple baselineskip. You may want to set lineskiplimit=-maxdimen (inside the box, or globally) to ensure that all baselines inside the box are separated by exactly baselineskip.



The macro copyboxwithappropriatespace, which I define below (and which could use a better name), does almost precisely these two things. Instead of setting the depth to zero it sets it to the remainder of the division of the original depth by baselineskip (as suggested by jfbu in the comments).
This matters if the last line of your box is particularly deep or the first line of the next box is particularly high.



makeatletter                %% <-- make @ usable in command sequences
%% Inserts a box of depth 0 and insert an appropriate vspace:
newcommand*copyboxwithappropriatespace[1]{%
begingroup
@tempdima=dp#1relax %% <-- store the depth in @tempdima
count@=@tempdimarelax %% <-- convert to a number
dividecount@ by baselineskip %% <-- divide by baselineskip
dp#1=dimexpr@tempdima-count@baselineskip %% <-- set the depth to to the remainder
copy#1% %% <-- insert the box
dp#1=@tempdimarelax %% <-- reset its depth
vspace*{count@baselineskip}%
endgroup %% ^-- insert count@ baselineskips
}
makeatother %% <-- revert @


This command replaces copy (or usebox).



(If you want, you could define useboxwithappropriatespace by copying this definition, removing dp#1=@tempdimarelax and replacing copy by usebox in this definition.)



documentclass{article}

usepackage{showframe} %% <- show boundary of text area
usepackage{tikz} %% <- To draw the red brackets
usepackage{tikzpagenodes} %% <- to place them correctly

makeatletter %% <-- make @ usable in command sequences
%% Inserts a box of depth 0 and insert an appropriate vspace:
newcommand*copyboxwithappropriatespace[1]{%
begingroup
@tempdima=dp#1relax %% <-- store the depth in @tempdima
count@=@tempdimarelax %% <-- convert to a number
dividecount@ by baselineskip %% <-- divide by baselineskip
dp#1=dimexpr@tempdima-count@baselineskip %% <-- set the depth to to the remainder
copy#1% %% <-- insert the box
dp#1=@tempdimarelax %% <-- reset its depth
vspace*{count@baselineskip}%
endgroup %% ^-- insert count@ baselineskips
}
makeatother %% <-- revert @

parindent=0pt %% <- saves me from having to type noindent
parskip=0pt %% <- default value is "0pt plus 1pt"

begin{document}

newsavebox{TitleBox}
newsavebox{BodyBox}
setboxTitleBox=vtop{textbf{A title that takes\up more than\two lines}}
setboxBodyBox=vtop{Lorem\ipsum}

copyboxwithappropriatespaceTitleBox

copyboxwithappropriatespaceBodyBox

copyboxwithappropriatespaceBodyBox

copyboxwithappropriatespaceBodyBox

begin{tikzpicture}[remember picture, overlay,]
draw[red] (current page text area.north west) -- +(-2pt,0)
-- node[left]{texttt{detokenize{topskip}}} +(-2pt,-topskip) -- +(0,-topskip);
foreach X in {0, ..., 7} {
draw[red] ([yshift=-topskip-Xbaselineskip]current page text area.north west) -- +(-2pt,0)
-- node[left]{texttt{detokenize{baselineskip}}} +(-2pt,-baselineskip) -- +(0,-baselineskip);
}
end{tikzpicture}

clearpage

textbf{A title that takes\up more than\two lines}

Lorem\ipsum par Lorem\ipsum par Lorem\ipsum

end{document}


This produces two pages that look identical (apart from the fact that only the first has the red brackets) and which were constructed differently: one using vertical boxes and one using ordinary paragraphs.



output



Note that I've set parskip=0pt because the default value of parskip is not equal to 0pt but to 0pt plus 1pt.
This means that the amount of space between paragraphs can be stretched to up to 1pt to improve the page layout, which I'm guessing you don't want to happen.





In your MWE



The spacing in your MWE will be correct if you replace vbox by top and replace usebox by copyboxwithappropriatespace (and add the above definition to your preamble, of course):



defUseBoxes{}% Uncomment to use boxes
%% --------------------
documentclass{article}
usepackage{tikz}
usepackage{tikzpagenodes}

usepackage[paperwidth=7.0cm,showframe]{geometry}

newcommand*{DesiredSkipAboveTitle}{5pt}
newcommand*{DesiredSkipBelowTitle}{10pt}

newcommand*{NumberOfTitleLines}{2}
newcommand*{Title}{A Title that Takes Up Two Lines}
newsavebox{TitleBox}
newcommand*{SetupTitleBox}[2]{%
setbox#1vtop{%
bfseriescentering%
#2%
%strut%
par%
}%
}

newcommand*{BodyTextLarge}{%
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Integer id nisl porta, porttitor tellus sed, sollicitudin erat.
Aliquam tellus urna, feugiat et tortor quis, aliquam mattis orci.%
}%
newcommand*{BodyTextSmall}{%
Small Text.%
}%

newcommand{SelectedBodyText}{BodyTextLarge}
%newcommand{SelectedBodyText}{BodyTextSmall}


newsavebox{BodyBox}
newcommand{SetupBodyBox}[2]{%
setbox#1vtop{%
noindent#2%
%strut%
}%
}%

makeatletter %% <-- make @ usable in command sequences
%% Inserts a box of depth 0 and insert an appropriate vspace:
newcommand*copyboxwithappropriatespace[1]{%
begingroup
@tempdima=dp#1relax %% <-- store the depth in @tempdima
count@=@tempdimarelax %% <-- convert to a number
dividecount@ by baselineskip %% <-- divide by baselineskip
dp#1=dimexpr@tempdima-count@baselineskip %% <-- set the depth to to the remainder
copy#1% %% <-- insert the box
dp#1=@tempdimarelax %% <-- reset its depth
vspace*{count@baselineskip}%
endgroup %% ^-- insert count@ baselineskips
}
makeatother %% <-- revert @

newlength{AdditionalSkip}
newcommand*{ShowTextGuideLines}[1]{%
begin{tikzpicture}[remember picture, overlay]
coordinate (X) at (current page text area.north west);

draw [draw=red, fill=yellow, fill opacity=0.2]
(X) circle (1pt);%% DEBUGGING: Ensure (X) is the correct spot.

setlength{AdditionalSkip}{DesiredSkipAboveTitle}%
foreach X in {1, ..., #1} {%
ifnumX>NumberOfTitleLines
%% After title: need to adjust AdditionalSkip for space after title
setlength{AdditionalSkip}{%
dimexprDesiredSkipAboveTitle+DesiredSkipBelowTitlerelax%
}%
fi
draw [thin, red] ([yshift=-Xbaselineskip-AdditionalSkip]X) -- ++ (hsize,0);
}%

tikzset{Node Style/.style={anchor=south, draw=red, inner sep=1pt}}
ifdefinedUseBoxes
node [Node Style, fill=yellow] at ([xshift=0.5hsize]X) {using boxes};
else
node [Node Style, fill=green] at ([xshift=0.5hsize]X) {Not using boxes};
fi
end{tikzpicture}%
}%


begin{document}
%% ---------------------------------------------------------- Set up the title and body
SetupTitleBox{TitleBox}{Title}%
SetupBodyBox{BodyBox}{SelectedBodyText}%
%% ---------------------------------------------------------- Title
hbox{}kern-topskip%% See https://tex.stackexchange.com/questions/7676/why-does-vspace0pt-add-vertical-space#comment12433_7681
vspace*{DesiredSkipAboveTitle}%
%%
ifdefinedUseBoxes
noindentcopyboxwithappropriatespace{TitleBox}%
vspace*{DesiredSkipBelowTitle}%
parnoindentcopyboxwithappropriatespace{BodyBox}%
else
noindent{bfseriescenteringTitlepar}%
vspace*{DesiredSkipBelowTitle}%
parnoindentSelectedBodyText%
fi
noindentShowTextGuideLines{7}%
end{document}


enter image description here







share|improve this answer














share|improve this answer



share|improve this answer








edited 17 hours ago

























answered Dec 7 at 21:35









Circumscribe

3,8371429




3,8371429








  • 1




    Thanks for the detailed explanation.
    – Peter Grill
    yesterday










  • You're welcome, and thank you for your generosity. The second bounty wasn't necessary.
    – Circumscribe
    yesterday










  • +1 but I believe there is an issue in copyboxwithappropriatespace: it should not "insert multiple of baselineskip closest to @tempdima" but "insert largest multiple of baselineskip less than @tempdima". And not set the dp of the inserted box to zero, but to the remainder of @tempdima (i.e. the actual depth of the vtop box) after removing "largest multiple of baselineskip". To obtain that largest integer you can do count@@tempdima then dividecount@ by baselineskip. Then count@ has this largest integer and it times baselineskip is to be removed.
    – jfbu
    22 hours ago












  • @jfbu: You're right. The definition of copyboxwithappropriatespace isn't perfect and was really only intended for this specific use case. If your boxes contain just normal lines of text, then it doesn't really matter whether you round down or to the nearest integer and this missed depth isn't very relevant, but in general it would be better to do precisely what you say. (It is annoying that dimexpr doesn't just round down btw…)
    – Circumscribe
    19 hours ago










  • I only just now thought of a better way to accomplish the same thing: you can create both a vbox and a vtop and then insert a vtop with the depth of the vbox and a vspace* equal to the difference between their depths! This would also work if the distance between the baselines inside the box varies, (My answer is getting a little long though…)
    – Circumscribe
    19 hours ago
















  • 1




    Thanks for the detailed explanation.
    – Peter Grill
    yesterday










  • You're welcome, and thank you for your generosity. The second bounty wasn't necessary.
    – Circumscribe
    yesterday










  • +1 but I believe there is an issue in copyboxwithappropriatespace: it should not "insert multiple of baselineskip closest to @tempdima" but "insert largest multiple of baselineskip less than @tempdima". And not set the dp of the inserted box to zero, but to the remainder of @tempdima (i.e. the actual depth of the vtop box) after removing "largest multiple of baselineskip". To obtain that largest integer you can do count@@tempdima then dividecount@ by baselineskip. Then count@ has this largest integer and it times baselineskip is to be removed.
    – jfbu
    22 hours ago












  • @jfbu: You're right. The definition of copyboxwithappropriatespace isn't perfect and was really only intended for this specific use case. If your boxes contain just normal lines of text, then it doesn't really matter whether you round down or to the nearest integer and this missed depth isn't very relevant, but in general it would be better to do precisely what you say. (It is annoying that dimexpr doesn't just round down btw…)
    – Circumscribe
    19 hours ago










  • I only just now thought of a better way to accomplish the same thing: you can create both a vbox and a vtop and then insert a vtop with the depth of the vbox and a vspace* equal to the difference between their depths! This would also work if the distance between the baselines inside the box varies, (My answer is getting a little long though…)
    – Circumscribe
    19 hours ago










1




1




Thanks for the detailed explanation.
– Peter Grill
yesterday




Thanks for the detailed explanation.
– Peter Grill
yesterday












You're welcome, and thank you for your generosity. The second bounty wasn't necessary.
– Circumscribe
yesterday




You're welcome, and thank you for your generosity. The second bounty wasn't necessary.
– Circumscribe
yesterday












+1 but I believe there is an issue in copyboxwithappropriatespace: it should not "insert multiple of baselineskip closest to @tempdima" but "insert largest multiple of baselineskip less than @tempdima". And not set the dp of the inserted box to zero, but to the remainder of @tempdima (i.e. the actual depth of the vtop box) after removing "largest multiple of baselineskip". To obtain that largest integer you can do count@@tempdima then dividecount@ by baselineskip. Then count@ has this largest integer and it times baselineskip is to be removed.
– jfbu
22 hours ago






+1 but I believe there is an issue in copyboxwithappropriatespace: it should not "insert multiple of baselineskip closest to @tempdima" but "insert largest multiple of baselineskip less than @tempdima". And not set the dp of the inserted box to zero, but to the remainder of @tempdima (i.e. the actual depth of the vtop box) after removing "largest multiple of baselineskip". To obtain that largest integer you can do count@@tempdima then dividecount@ by baselineskip. Then count@ has this largest integer and it times baselineskip is to be removed.
– jfbu
22 hours ago














@jfbu: You're right. The definition of copyboxwithappropriatespace isn't perfect and was really only intended for this specific use case. If your boxes contain just normal lines of text, then it doesn't really matter whether you round down or to the nearest integer and this missed depth isn't very relevant, but in general it would be better to do precisely what you say. (It is annoying that dimexpr doesn't just round down btw…)
– Circumscribe
19 hours ago




@jfbu: You're right. The definition of copyboxwithappropriatespace isn't perfect and was really only intended for this specific use case. If your boxes contain just normal lines of text, then it doesn't really matter whether you round down or to the nearest integer and this missed depth isn't very relevant, but in general it would be better to do precisely what you say. (It is annoying that dimexpr doesn't just round down btw…)
– Circumscribe
19 hours ago












I only just now thought of a better way to accomplish the same thing: you can create both a vbox and a vtop and then insert a vtop with the depth of the vbox and a vspace* equal to the difference between their depths! This would also work if the distance between the baselines inside the box varies, (My answer is getting a little long though…)
– Circumscribe
19 hours ago






I only just now thought of a better way to accomplish the same thing: you can create both a vbox and a vtop and then insert a vtop with the depth of the vbox and a vspace* equal to the difference between their depths! This would also work if the distance between the baselines inside the box varies, (My answer is getting a little long though…)
– Circumscribe
19 hours ago




















draft saved

draft discarded




















































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.





Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


Please pay close attention to the following guidance:


  • 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.




draft saved


draft discarded














StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2ftex.stackexchange.com%2fquestions%2f463039%2fspacing-difference-when-using-boxes%23new-answer', 'question_page');
}
);

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







Popular posts from this blog

How to change which sound is reproduced for terminal bell?

Title Spacing in Bjornstrup Chapter, Removing Chapter Number From Contents

Can I use Tabulator js library in my java Spring + Thymeleaf project?