9

\strut seems to be a powerful command to get the interlinear spacing correct; particularly, when working with text in \parboxs or minipages. But it's also nice in contexts in which LaTeX is setting the text in an unexpected place.

Here's a MWE which illustrates two situations in which \strut is the quick and easy method to get the interlinear or textual alignment correct.

\documentclass{article}
\usepackage[width=7.5in]{geometry}
\usepackage{lipsum}
\pagestyle{empty}
\begin{document}

\noindent
\begin{minipage}[t]{3in}
  \hspace*{\fill}Naive Approach\hspace*{\fill}

  \vspace{1cm}

  \noindent
  \begin{tabular}{ll}
    first  & short text                                                         \\
    second & \parbox[t]{2.25in}{By putting a long line here I get bad spacing.} \\
    third  & short text                                                         \\
    fourth & spacing                                                            \\
    fifth  & short text                                                         \\
  \end{tabular}

  \vspace{1cm}

  \leavevmode\vadjust{
    \noindent
    \raisebox{\dimexpr0.5\totalheight\relax}[0pt][0pt]{\makebox[0pt][r]{Hello}}
  }%
  \lipsum[1]

  \vspace{1cm}

  On this side of things, the placement of \verb-Hello- just isn't easily
  gotten correct whether I substitute 

  \vspace{0.5cm}

  \centering
  \verb|\dimexpr0.5\totalheight\relax| 

  by
  \verb|\depth| or just plain \verb|0pt|.

  \vspace{0.5cm}

  \textbf{Note:} if I replace \verb-Hello- by \verb-Why- and then raise
  the box by \verb-\depth- then things appear to be visually aligned correctly.

\end{minipage}\hspace*{\fill}%
\begin{minipage}[t]{3in}
  \hspace*{\fill}Better spacing with \verb=\strut=\hspace*{\fill}

  \vspace{1cm}

  \noindent
  \begin{tabular}{ll}
    first  & short text                                                               \\
    second & \parbox[t]{2.25in}{By putting a long line here I get bad spacing.\strut} \\ 
    third  & short text                                                               \\
    fourth & spacing                                                                  \\
    fifth  & short text                                                               \\
  \end{tabular}

  \vspace{1cm}

  \leavevmode\strut\vadjust{
    \noindent
    \raisebox{\dp\strutbox}[0pt][0pt]{\makebox[0pt][r]{Hello}}
  }%
  \lipsum[1]


\end{minipage}

\end{document}

enter image description here

I've seen the answers posted at What's the difference between \strut, \mathstrut and \vphantom? and Spacing between items containing a parbox. They're helpful in understanding when to use \strut. I've looked at how \strut is defined. But I'm still in the dark about how \strut actually accomplishes what it does.

Could someone take the time to explain how it is that using \strut achieves the correct effect? Or point me in the right direction?

NOTE I've also seen this explanation but other than restating the definition, I'm still unclear about why it's working in the situations I've provided above.

UPDATE

After considering the example @Werner provided using \fboxs to show the depth created by \strut, I decided I should try something similar to illustrate my situation involving \vstrut. Unfortunately, it wasn't as simple as just placing my example into an \fbox. Basically the content disappears. But I prevailed. In the following MWE I test out three different scenarios with three cases each.

\documentclass{article}

\newcommand{\aestrut}{\raisebox{0pt}[0pt][0pt]{\rule[\dimexpr-0.3\baselineskip\relax]{0.4pt}{\dimexpr\baselineskip\relax}}}
\newcommand{\visibleStrutAndBaseline}{\aestrut\rule[-0.4pt]{1cm}{0.4pt} Followed by text}
\newcommand{\aehline}{\rule[-0.4pt]{1cm}{0.4pt}}

\newcommand{\aecontent}[1]{\aehline\hspace{\dimexpr-1cm+0.25em}#1}

\setlength{\parindent}{0pt}
\pagestyle{empty}
\begin{document}

  \visibleStrutAndBaseline --- using No strut
  {\leavevmode\vadjust{\raisebox{0pt}[0pt][0pt]{\aecontent{Text}}}}

  \vspace{0.25cm}

  \visibleStrutAndBaseline --- using Strut but not raised
  {\leavevmode\strut\vadjust{\raisebox{0pt}[0pt][0pt]{\aecontent{Text}}}}

  \vspace{0.25cm}

  \visibleStrutAndBaseline --- using Strut and raised by depth of strut
  {\leavevmode\strut\vadjust{\raisebox{\dp\strutbox}[0pt][0pt]{\aecontent{Text}}}}


  %%--------------------------------------------------------------------------------

  \vspace{1cm}

  \visibleStrutAndBaseline --- using No strut
  {\leavevmode\vadjust{\raisebox{0pt}[0pt][0pt]{\aecontent{Ziggy}}}}

  \vspace{0.25cm}

  \visibleStrutAndBaseline --- using Strut but not raised
  {\leavevmode\strut\vadjust{\raisebox{0pt}[0pt][0pt]{\aecontent{Ziggy}}}}

  \vspace{0.25cm}

  \visibleStrutAndBaseline --- using Strut and raised by depth of strut
  {\leavevmode\strut\vadjust{\raisebox{\dp\strutbox}[0pt][0pt]{\aecontent{Ziggy}}}}

  %%--------------------------------------------------------------------------------

  \vspace{1cm}

  \visibleStrutAndBaseline\rule[-2ex]{2pt}{3ex} --- using No strut
  {\leavevmode\vadjust{\raisebox{0pt}[0pt][0pt]{\aecontent{Deep}}}}

  \vspace{0.25cm}

  \visibleStrutAndBaseline\rule[-2ex]{2pt}{3ex} --- using Strut but not raised
  {\leavevmode\strut\vadjust{\raisebox{0pt}[0pt][0pt]{\aecontent{Deep}}}}

  \vspace{0.25cm}

  \visibleStrutAndBaseline\rule[-2ex]{2pt}{3ex} --- using Strut and raised by depth of strut
  {\leavevmode\strut\vadjust{\raisebox{\dp\strutbox}[0pt][0pt]{\aecontent{Deep}}}}

\end{document}

The scenarios involve the depth of the main line of text and the depth of the text in the \vadjust. So, in scenario one the depth of the \vadjust content is zero. In scenario two there is a nontrivial depth for this content. In the third scenario I've created a deep depth.

For each scenario there are three cases: The case one is to consider what happens when \strut is not used. Case two is to use \strut but not raise the contents of the \vadjust. And the third case illustrates raising the content by the depth of \strut.

In all scenarios and cases I use a visible strut to mimic \strut, which I've set to zero height and depth so as not to inadvertently disturb the vertical spacing.

enter image description here

This example helped me see two things: first was where exactly \vadjust is placing the text: Sure enough, others have been saying it's placed between the lines. And that is certainly true. Per correction by @DavidCarlisle : it seems to be placed so that the TOP of the \vadjust's content is aligned with the BOTTOM (not the baseline) of the box for the line from within which \vadjust was called. The second things I was able to see was that, as long as the main text does not have any boxes with wild depths, then \strut will be sufficiently deep to affect the correct depth to get the desired alignment.

  • I greatly appreciate the answers I'm getting, but I don't think I've properly formed the question, or I'm just totally not getting something. At any rate, I'm working on an update to the question that will better clarify where my confusion is coming from. – A.Ellett Jun 28 '13 at 19:54
  • I think my real confusion revolves around how \leavevmode, \vadjust and \strut are all interacting. – A.Ellett Jun 28 '13 at 19:56
6

From the comments on the on the other answers, it seems like you want to concentrate on

 \leavevmode\strut\vadjust{%
    \noindent
    \raisebox{\dp\strutbox}[0pt][0pt]{\makebox[0pt][r]{Hello}}
  }%

The first \leavevmode isn't really needed as \strut is defined to get into horizontal mode, however it does no harm and makes it clearer that the code needs horizontal mode. Then comes the \strut this is important and we'll come back to that. Then \vadjust: the content of \vadjust is evaluated immediately (like \vbox) but the resulting vertical material is not boxed but inserted into the vertical list that will be later constructed from the lines in the paragraph.

The intention of the code is to make hello aligned to the baseline of the previous box, but the vadjust material is inserted directly into the list not forced to baseline spacing so to align to the baseline of the previous box you need to back up by the depth of the previous box. the "previous box" has not yet been constructed, it consists of whatever text ends on the line that has the vadjust node If that line had arbitrary text its depth would not be known. However as \strut was added and this had bigger depth than any letter or other normal text it is reasonable to assume the depth of the line is the depth of strutbox.

So \raisebox{\dp\strutbox} gets you back to the baseline of the previous line.

This will fail if you put some deep inline math or an inline tabular in the line, but if you need to deal with that you need a more complicated two pass measuring scheme.

  • So it seems that I was on the right track in my update. Thank you for this explanation. – A.Ellett Jun 28 '13 at 21:20
  • @A.Ellett Oh I'd missed that update yes but not this: But, it seems to be placed so that the baseline of the \vadjust's content is aligned with the BOTTOM (not the baseline) The top of the vadjust material is placed after the bottom of the previous line, Here you are smashing the height of the insert with the 0pt makebox arguments so you get baseline but try \vadjust{\hbox{hello}} – David Carlisle Jun 28 '13 at 21:29
  • Argh! How right. I forgot that I was smashing things. Thank you for pointing that out. – A.Ellett Jun 28 '13 at 21:30
5

The main issue here deals with the bounding boxes associated with the contents. Some elements have different depths than others, most notably from text without descenders. For example, Hello has no depth, while spacing has some. The idea behind \strut is to ensure that \strut Hello and \strut spacing (for example) cover the same ground vertically.

A quick example of this is visible when you view this MWE:

enter image description here

\documentclass{article}
\newcommand{\ddd}[1]{\fbox{\strut#1}\,\fbox{#1}}% Show the difference in bounding boxes
\setlength{\fboxsep}{-\fboxrule}% For this example
\begin{document}

Letters: \ddd{a}\ \ddd{b}\ \ddd{p} \ddd{Q}

\bigskip

Words: \ddd{Hello}\ \ddd{spacing}

\end{document}

Is can be seen, the bounding boxes of these letters/words are vertically equivalent to one another, even though they each may have different descenders/ascenders. More specifically, \strut ensures at least a 70% \baselineskip in terms of height, and 30% \baselineskip in depth in the prevailing font. From \strutbox's definition (used by \strut) in the LaTeX kernel as part of \set@fontsize:

\setbox\strutbox\hbox{%
  \vrule\@height.7\baselineskip
        \@depth.3\baselineskip
        \@width\z@}%

So, specific to your case, you can see that the \strut inside the \parbox increases the depth of the box, and therefore the skip from one line to the next (see the difference in the bounding boxes related to spacing above).

The second comparison to Hello is somewhat bizarre in your particular case. In the first you raise it based on height+depth (or \totalheight) while in the second you raise it based on depth only (\dp). Since Hello has height, the first raise is non-zero, while it is zero in the second, since Hello has zero depth.

Also see, as reference, How to keep a constant \baselineskip when using minipages (or \parboxes)?

  • I think I should have left out the example of the \parbox. Though I included it because I couldn't figure out how to get the solution at the link you provided to work in a tabular environment. – A.Ellett Jun 28 '13 at 19:22
  • I think my real confusion arises from the second example with \vadjust where the depth of \strut seems to me like it should be insufficient to raise the box to properly align with the remaining text. – A.Ellett Jun 28 '13 at 19:25
  • @A.Ellett: Instead of \vadjust{\raisebox..\makebox..Hello} you can just use \llap{Hello} The output is the same. – Werner Jun 28 '13 at 19:38
  • Not quite. I'm actually interested in placing text in the margin. Compare the effects in this example: Here is some text {\leavevmode\vadjust{\raisebox{0pt}{\makebox[0pt][r]{Hello}}}} is not the same as {\leavevmode\llap{Hello}} – A.Ellett Jun 28 '13 at 19:46
  • @A.Ellett: Placing text in the margin is sufficiently obtained via a single \rlap. I'm not sure why you need \vadjust. – Werner Jun 28 '13 at 20:06
5

First of all, what does \strut do? It simply produces an invisible rule whose height is 70% and the depth 30% of the current baselineskip.

A \parbox[t] makes a box as high as its first line; all the successive lines contribute to the depth. Thus, when a \parbox[t] is found, between the line in which it is and the next one, the \lineskip glue will be inserted (excluding the trivial case of a one line \parbox[t]). A fix for getting uniform spacing is thus to end the \parbox[t] with \strut and adding one in the next line. This will ensure (except in the case where lines are too high or deep) that the baselines are \baselineskip apart (using the fact that the \lineskip glue is usually zero).

This is shown in your first example, with the \parbox nested in a tabular. There's no need to add a \strut in the following row, because rows in a tabular are kept apart from each other exactly by struts.

The second example is more contorted. The material in \vadjust should be vertical material and not horizontal material, which causes however horizontal mode to start. The material is added between lines and, since it contains a box, it will contribute its height and depth (but no interline glue). You can check this by saying \vadjust{Hello} instead. So it doesn't provide a good illustration of \strut usage.

  • I'm a bit confused by your statement that "there's no need to add a strut in the following row". It seems I needed it where it was placed. Could you clarify what you meant? – A.Ellett Jun 28 '13 at 21:10
  • @A.Ellett You don't need a strut in the row third&short text, because one is already provided by tabular. – egreg Jun 28 '13 at 21:21
0

\strut makes an invisible vbox such, that it fills space between the baselines. Hence (in typical cases) the spacing is uniform.

  • I agree with you about the typical case. But, where "Hello" is jutting out into the margin doesn't seem very typical to me. And in this particular scenario it's much more a mystery to me why \strut is working than the other. – A.Ellett Jun 28 '13 at 18:55
  • @A.Ellett Because "Hello" has zero depth. Hence \strutboxadds an additional depth and the lines are aligned. – Przemysław Scherwentke Jun 28 '13 at 19:02
  • But without a strut and without raising the box, "Hello" would be set between the lines. As I understand it \strut would add invisible height and depth. That might lead me to think that the correct value to pass to \raisebox would be something like \dimexpr\dp\strutbox+\ht\strutbox\relax. But that would be wrong. Given that \raisebox{\depth}[0pt][0pt]{Why} works and since "Hello" has zero depth, I would then naively think that \raisebox{0pt}.... should do the trick, which it doesn't. – A.Ellett Jun 28 '13 at 19:17

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.