16

I would like a vertical equivalent to \mbox{} that somehow takes a sequence of boxes and stacks them on top of each other.

I'd like the resulting width to be the width of the widest box, and I'd like it to have the following options:

  • either no vertical space between the boxes or standard inter-line spacing
  • left, right, or center alignment
  • the baseline of the resulting box goes through one of the following:

    • top: the baseline of the first box (like \vtop)
    • middle: in the middle of the box
    • bottom: the baseline of the last box (like \vbox)

For example:

surrounding text
\fbox{\stack[align=rt]{something}{something else}{foo}}
surrounding text

would produce:

                 +----------------+
surrounding text |      something | surrounding text
                 | something else |
                 |            foo |
                 +----------------+

Does something like this already exist? If not, does anyone have any pointers about how to make it?

Edit: I'm using LaTeX

6 Answers 6

13

The v0.8 of adjustbox from 2011/11/14 has now a \stackbox[<hor>][<vert>]{<content>} macro (and a matching stackbox environment and stack key). It accepts vertical and horizontal alignment and uses the varwidth package internally. To remove the usual skip between lines, you can use \baselineskip=0pt at the begin and \par at the end. I might add an option for this later.

\documentclass{article}

\usepackage{adjustbox}

\begin{document}

surrounding text
\fbox{\stackbox[r][t]{something\\something else\\foo}}
surrounding text

surrounding text
\fbox{\stackbox[l][b]{something\\something else\\foo}}
surrounding text

surrounding text
\adjustbox{stack=cc,fbox}{something\\something else\\foo}
surrounding text

% No interline skip:
surrounding text
\fbox{\stackbox[c][b]{\baselineskip=0pt something\\something else\\foo\par}}
surrounding text

\end{document}

Result

6
  • as Leo Liu already pointed out it is a simple tabular
    – user2478
    Nov 17, 2011 at 8:28
  • 5
    @Herbert: My first implementation try was using a tabular. However, like all other box macros of adjustbox the content is read as a real box to allow verbatim etc. This is actually quit difficult to do with an implicit tabular, so I went with varwidth. I also wanted to point out that there is a package which has a macro for this. Nov 17, 2011 at 8:33
  • Thank you! I like having something already written and debugged. :) Speaking of bugs, \offinterlineskip doesn't quite work with the macro variant (but the environment version works fine): \setlength{\fboxsep}{-\fboxrule}\stackbox[r][t]{\offinterlineskip\fbox{something}\\\fbox{somethingelse}\\\fbox{foo}} Nov 17, 2011 at 18:16
  • @RichardHansen: You need to add a \par at the very end to make it work. I might add it in the macro definition in the next package version. Nov 18, 2011 at 8:22
  • 1
    @Mohan: stackbox uses paragraph mode for the contents, which leads to the limitation to \linewidth. Dec 10, 2012 at 21:00
15

It is strange no one has mentioned tabular:

\documentclass{article}
\usepackage{array}
\begin{document}
before
\begin{tabular}[t]{|c|}\firsthline
foo\\bar\\baz\\\lasthline
\end{tabular}
after

before
\begin{tabular}[b]{|c|}\firsthline
foo\\bar\\baz\\\lasthline
\end{tabular}
after
\end{document}

enter image description here

You can easily define a new command for short.

And there are indeed quite a lot of tool packages for tabulars to help you to get what you want.

7
  • Is it possible to control inter-row spacing with tabular (so that each box is stacked on top of each other with no vertical gaps)? Nov 17, 2011 at 4:47
  • @RichardHansen: Yes, of course. The factor \arraystretch is for this. And you can use makecell package to modify the gape of the tabular lines.
    – Leo Liu
    Nov 17, 2011 at 4:49
  • Ah yes, \renewcommand{\arraystretch}{0} eliminated all vertical space between the rows. Excellent! Nov 17, 2011 at 5:10
  • What are the pros and cons of using tabular vs. the varwidth approach? Is one better than the other with respect to compatibility (with other LaTeX commands or packages)? Nov 17, 2011 at 6:21
  • @Richard: Well, you know a varwidth is a minipage with variable width, in fact origin \parbox or minipage is enough in most cases. A tabular is actually more complex and more powerful than a minipage box: alignment, spacing, frames, etc.
    – Leo Liu
    Nov 17, 2011 at 6:30
9

This is an old question but it has a very easy decomplexified answer, which I offer here for completeness. Use \valign from TeX.

Then it becomes a two liner:

\documentclass{article}
\begin{document}
Before text \lower 30pt\hbox{\valign{&\hbox to 1cm{\fbox{#}\hfil}\vfill\cr
one& two& three& four& five& six\cr}} after text.
\end{document}

TeX commands are available in (La)TeX, and these are the commands that LaTeX uses to build its own macros. I have used \fbox from LaTeX, but is as easy to define a boxit command using TeX. (Please also see the alternative solution by morbusg in the comments, using ooalign).

enter image description here

2
  • Maybe even Before text \oalign{one\cr two\cr three\cr four\cr} after text.?
    – morbusg
    Dec 10, 2011 at 13:14
  • 1
    @morbusg -- Thanks I forgot about this! Even simpler. I don't know why people don't use basic TeX more. Dec 10, 2011 at 13:17
7

Here is an option that uses the keyval package for setting the keys, the varwidth package for setting the text block, the setspace package for line height adjustment and xifthen for conditioning.

enter image description here

\documentclass{article}
\usepackage{setspace}% http://ctan.org/pkg/setspace
\usepackage{varwidth}% http://ctan.org/pkg/varwidth
\usepackage{keyval}% http://ctan.org/pkg/keyval
\usepackage{xifthen}% http://ctan.org/pkg/xifthen
\makeatletter
\newcommand{\stack}[2][,]{%
  \setkeys{stack}{#1}% Set keys
  \begin{varwidth}[\stack@valign]{\textwidth}\stack@spacing\stack@halign#2\end{varwidth}%
}
% ========= KEY DEFINITIONS =========
\define@key{stack}{halign}{% horizontal alignment
  \ifthenelse{\equal{#1}{r}}{\def\stack@halign{\raggedleft}}{% right-aligned
  \ifthenelse{\equal{#1}{c}}{\def\stack@halign{\centering}}{% centered
  \ifthenelse{\equal{#1}{l}}{\def\stack@halign{\raggedright}}{% left-aligned
  }}}% Otherwise, justified
}
\define@key{stack}{valign}{% vertical alignment
  \ifthenelse{\equal{#1}{m}}{\def\stack@valign{m}}{% middle-aligned
  \ifthenelse{\equal{#1}{b}}{\def\stack@valign{b}}{% bottom-aligned
    \def\stack@valign{t}}}% top-aligned
}
\define@key{stack}{spacing}{\def\stack@spacing{#1}}% spacing
\makeatother
% ========= KEY DEFAULTS =========
\setkeys{stack}{halign=l,valign=t,spacing=\setstretch{1}}%

\begin{document}
surrounding text
\fbox{\stack[valign=m]{something\par something else\par foo}}%
surrounding text

surrounding text
\fbox{\stack[valign=b,halign=l]{something\par something else\par foo}}%
surrounding text

surrounding text
\fbox{\stack[valign=t,halign=r]{something\par something else\par foo}}%
surrounding text

surrounding text
\fbox{\stack[valign=m,halign=c]{something\par something else\par foo}}%
surrounding text

surrounding text
\fbox{\stack[valign=t,spacing=\doublespacing]{something\par something else\par foo}}%
surrounding text

surrounding text
\fbox{\stack[valign=b,halign=c,spacing=\onehalfspacing]{something\par something else\par foo}}%
surrounding text
\end{document}

The following key-value pairs are defined:

  • valign: Adjusts the vertical alignment; possible options are top (default), middle and bottom. Anything else specified defaults to top.
  • halign: Adjusts the horizontal alignment; possible options are left or \raggedright (default), center or \centering, and right or \raggedleft. Anything else defaults to justified text.
  • spacing: Provides an additional formatting hook. With the setspace package loaded, one could use \doublespacing or \setstretch{2}, say. Turning off the inter-line skip is done using spacing=\offinterlineskip or spacing={\baselineskip=0pt}.

Note @barbarabeeton's comment regarding \offinterlineskip:

\offinterlineskip has the danger that if some lines contain no ascenders or no descenders, the baselines can become very irregular. Very ugly and undesirable. use \strut, or less intrusive, $\mathstrut$ (since it doesn't include leading), to even them out if you must use this option.

5
  • Another good answer! I have the same question: How would I turn off inter-line spacing? Thanks! Nov 17, 2011 at 4:21
  • I think I figured it out: spacing=\offinterlineskip seems to do it. Nov 17, 2011 at 4:31
  • 1
    \offinterlineskip has the danger that if some lines contain no ascenders or no descenders, the baselines can become very irregular. very ugly and undesirable. use \strut, or less intrusive, $\mathstrut$ (since it doesn't include leading), to even them out if you must use this option. Nov 17, 2011 at 15:50
  • Sometimes I intentionally want the baselines to be irregular. Not often, but often enough that I'd like it to be an option. Nov 17, 2011 at 21:20
  • @RichardHansen: Agreed, this is down to personal preference and the application of the provided solution (not always included in the original question), and therefore very subjective at times.
    – Werner
    Nov 17, 2011 at 21:22
6

Basic Solution:

You can use a node with tikz. Below I have defined a macro that accepts an optional alignment as either top, middle, or bottom alogn with 3 parameters for the various text fields. You can easily extend this to handle more.

\MyVBox[top|middle|bottom]{text1}{text2}{text3}

Using just these basic alignment positions you get:

enter image description here

Fancy Options:

But, since this is using \tikz you can also get fancier and adjust the alignment to center, left, add a fill, specify a color for the lines, use a dashed line, etc, etc:

enter image description here

Further Enhancements:

In this solution I resorted to magic numbers for yshift that had to be tweaked to get the top and middle alignment options to work properly. However if the vertical size of the bounding box changes by adjusting the inner sep or the line thickness, this will need to be tweaked. I attempted to use the use as bounding box option as well as adjust those parameters automatically but was not able to get it to work as I expected.

Code:

\documentclass[border=5pt]{standalone}
\usepackage{tikz}

\tikzset{top/.style={baseline={([yshift=-2.44ex]current bounding box.north)}}}
\tikzset{middle/.style={baseline={([yshift=-0.80ex]current bounding box.west)}}}
\tikzset{bottom/.style={baseline=(X.base)}}

\newcommand{\MyVBox}[4][middle, align=right]{\tikz [#1] \node [draw=black, align=right, #1] (X) {#2 \\ #3 \\ #4};}%
\begin{document}
surrounding text
    \MyVBox[top]{something}{something else}{foo}
surrounding text

surrounding text
    \MyVBox[middle]{something}{something else}{foo}
surrounding text

surrounding text
    \MyVBox[bottom]{something}{something else}{foo}
surrounding text

\bigskip\hrule\bigskip
surrounding text
    \MyVBox[middle,align=center,draw=blue,fill=yellow]{something}{longer something else}{foo}
surrounding text

surrounding text
    \MyVBox[middle,align=left,draw=red,fill=green, dashed]{something}{even longer something else}{foo}
surrounding text
\end{document}
3
  • Thank you for the answer! Do you know how I might turn off the inter-line space? Nov 17, 2011 at 4:13
  • 1
    It seems like tikz is powerful enough that it should be possible to specify which line's baseline the entire box's baseline should go through. I wonder how hard it would be to tweak the macro to make it possible to specify a line number to use as the baseline? For example, baseline=1 would be the same as giving the top option, baseline=2 would align to the second line's baseline, baseline=-1 would be the same as bottom, baseline=-2 would select the baseline of the second-to-last line as the box's baseline, etc. Nov 17, 2011 at 4:40
  • I believe the the inter-line spacing is the normal spacing between lines in a paragraph, so adjusting that should do the job. Yeah, I agree that tikz can probably do that, I just am not that much of an expert to be able to do it without much work. You can post a follow up question about that and am pretty sure you'll get a great solution. Nov 17, 2011 at 15:19
4

This is a ConTeXt solution:

\starttext
surrounding text
\inframed [align=left, location=top]
  {something\\something else\\foo}
surrounding text
\stoptext

the result looks like this: result

left, right, or center alignment

Use the align key: left, middle, or right.

The baseline position is controlled by the location key. Set it to either top, bottom, middle. The baseline distance can be controlled using something like

\inframed [
  align=left,
  location=top,
  top={\setupinterlinespace[18pt]}]
  {something\\something else\\foo}

You can remove the frame with frame=off. There are many more options to tweak look here or here for more information.

1
  • Sorry, I forgot to mention that I'm using LaTeX. Nov 17, 2011 at 4:31

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .