8

Is there a way to define a function \horizontallengthof returning the length of a piece of text in internal units? I would like to be able to use it anywhere where something of that sort is required, for example like p{\horizontallengthof{this particular piece of text}} (in a table specification) or \hspace{\horizontallengthof{this particular piece of text}}.


I am hesitant to post a particular use case, since I am interested in a generic solution instead of workarounds for particular situations. But here is one (with calc's \widthof standing in for \horizontallengthof):

\documentclass{article}
\usepackage{fixltx2e}
\usepackage{calc}

\begin{document}

\begin{tabular}{p{0em}@{\hspace{1.0em}\quad}l@{\qquad}l}
  \(\bullet\) & \(x = y\) & \(z = w\) \\
  \(\bullet\) & \(a = b\) & \(c = d\) \\
\end{tabular}

\begin{tabular}{p{\widthof{\(\bullet\)}}@{\hspace{1.0em}\quad}l@{\qquad}l}
  \(\bullet\) & \(x = y\) & \(z = w\) \\
  \(\bullet\) & \(a = b\) & \(c = d\) \\
\end{tabular}

\begin{tabular}{p{\widthof{\(\bullet\)}}@{\hspace{1.0em-\widthof{\(\bullet\)}}\quad}l@{\qquad}l}
  \(\bullet\) & \(x = y\) & \(z = w\) \\
  \(\bullet\) & \(a = b\) & \(c = d\) \\
\end{tabular}

\end{document}

With the suggestion to add fixltx2e, this compiles properly, but I'd like the second table to have the same appearance (spacing-wise) as the first one, while giving me no Overfull \hbox warnings. Table 3 is a failed attempt at achieving this.

One reason why I didn't initially post this is that I didn't want any potential workarounds to this distract from a general (and imho needed) solution.

Moriambar
  • 11,466
  • 2
    \usepackage{calc} and then \hspace{\widthof{text}} – egreg Feb 21 '13 at 11:08
  • 2
    Hmm, if I try \documentclass{article} \usepackage{calc} \begin{document} a\hspace{\widthof{some text}}b \end{document}, I get a ! Missing number, treated as zero. error. – Lover of Structure Feb 21 '13 at 11:25
  • Uh! I've been too confident in calc. :( However, \widthof can be used in the argument of \parbox and for p-columns. – egreg Feb 21 '13 at 11:32
  • @egreg I think your confidence in calc is correct, but LaTeX let you down – David Carlisle Feb 21 '13 at 11:42
  • @egreg Okay, and if I want to be able to use \widthof for something like \(\bullet\)? See my added minimal example. – Lover of Structure Feb 21 '13 at 11:43
  • 1
    fragile command, moving argument add \usepackage{fixltx2e} and then \( is robust (or you could just use $ which is robust anyway) – David Carlisle Feb 21 '13 at 11:52
  • @egreg I could make a separate question for that (maybe I should?) but see my edited (and final) motivating example. – Lover of Structure Feb 21 '13 at 12:22
  • using widthof in the array preamble is massively inefficient you will redo the box and measure every row and then all you are doing is making a box as big as its known content. Why not simply use @{(\bullet)} and insert a bullet every row. Or use l. – David Carlisle Feb 21 '13 at 12:31
  • l will of course be wider than p{0pt} as l makes a column as wide as the bullet but p{0pt} makes a zero width column with the bullet over-printing the following column. – David Carlisle Feb 21 '13 at 12:33
  • 1
    \begin{tabular}{l@{\qquad}l@{\qquad}l} \rlap{\(\bullet\)} & \(x = y\) & \(z = w\) \ \rlap{\(\bullet\)} & \(a = b\) & \(c = d\) \\end{tabular} – egreg Feb 21 '13 at 12:34
  • Btw, with egreg's \Hspace, the following gives the desired result and no overfull \hboxes: \begin{tabular}{p{\widthof{\(\bullet\)}}@{\Hspace{1.0em-\widthof{\(\bullet\)}}\quad}l@{\qquad}l}. – Lover of Structure Feb 21 '13 at 12:35
  • @DavidCarlisle Oh, I wasn't quite aware that it was overprinting the second column. I see, I see. The reason why I came up with this particular example in the first place was because (if I remember correctly) I was unhappy with the (imho huge) added vertical space everywhere when you deal with bulleted lists. So I simply made it a table and put it inside memoir's \begin{Spacing}{1} \noindent ... \end{Spacing}. I hope my motivation makes sense: this is really about emulating ordinary itemize, whose vertical spacing I'm very unhappy with. – Lover of Structure Feb 21 '13 at 12:41
  • @egreg (and David): Anything else I need to know about package loading order for calc? The documentation doesn't specify that it needs to be before or after certain other packages. – Lover of Structure Feb 21 '13 at 12:43
  • 1
    It shouldn't clash with any package, it is part of the core (but you don't need it for this anyway:-) – David Carlisle Feb 21 '13 at 12:47
  • arrggggggggggggg!!!!!!!!!!!!!!! every single space in a latex list is a user settable parameter, if you don't like them, change them. " I hope my motivation makes sense: " that would be a no then:-) – David Carlisle Feb 21 '13 at 12:49
  • @DavidCarlisle By the way, using memoir necessitates $\bullet$ instead of \(\bullet\), even with fixltx2e; I have no clue why. This might be something to pass on to those in charge (if you're not one of them). – Lover of Structure Feb 21 '13 at 12:56
  • @DavidCarlisle About the "argh" comment :-) You might be lucky: I might actually have remembered incorrectly. Sorry about that :-) Actually the thing was that I was trying to emulate a bulleted list (itemize) with horizontal alignment (see that there are two equations in each row). So that really did necessitate a tabular; I didn't want to fiddle with tabs and itemize to get that result. I did in fact manage to create a \newenvironment{mytightlist} (with most parameters set to 0pt), which addresses my vertical spacing preference. My previous comment about what motivated me was wrong. – Lover of Structure Feb 21 '13 at 12:59
  • Btw the reason for the particular \hspace{1.0em}\quad in my example was to have it match up with earlier lists. (That's why I didn't first want to post this example, because it'd too confusing to explain, apparently it's confusing to myself ...) – Lover of Structure Feb 21 '13 at 13:10

4 Answers4

13

Hmm arguably this is a calc and/or LaTeX bug.

using calc and \widthof would work in most of the places you mention (as in LaTeX2e such places explicitly changed to use \setlength internally so that they would work with calc. However \hspace appears to have escaped that conversion, the following patch fixes that.....

\documentclass{article}

\usepackage{calc} 

\makeatletter
\def\@hspace#1{\begingroup\setlength\dimen@{#1}\hskip\dimen@\endgroup}
\makeatother

\begin{document}

 a\hspace{\widthof{some text}}b 

\end{document}

The above answers the original question, but the later edits have clarified that this is not needed at all. \widthof is a relatively expensive operation and doing every row just to make space to insert the same text you measured is really just torturing your computer for no real gain. The formulation in the second table matches the version you gave in the first without the over full boxes and without measuring anything.

enter image description here

\documentclass{article}
\usepackage{fixltx2e}
\usepackage{calc}

\begin{document}

\begin{tabular}{p{0em}@{\hspace{1.0em}\quad}l@{\qquad}l}
  \(\bullet\) & \(x = y\) & \(z = w\) \\
  \(\bullet\) & \(a = b\) & \(c = d\) \\
\end{tabular}

\begin{tabular}{@{\hspace\tabcolsep\rlap{$\bullet$}\hspace{1.0em}\quad}l@{\qquad}l}
   \(x = y\) & \(z = w\) \\
   \(a = b\) & \(c = d\) \\
\end{tabular}

\end{document}
David Carlisle
  • 757,742
7

calc patches some of the main LaTeX constructs so that an extended syntax is accepted. However, it doesn't do the same for \hspace.

The command \widthof can be used in \setlength and in the argument to \parbox, minipage and also in the p-column specifier.

You can get a version of \hspace that works with \widthof, but I wouldn't use it as a replacement.

\documentclass{article}
\usepackage{xparse,calc}
\newlength\Hspacelen
\NewDocumentCommand{\Hspace}{sm}
 {\setlength\Hspacelen{#2}%
  \IfBooleanTF{#1}
    {\hspace*{\Hspacelen}}
    {\hspace{\Hspacelen}}%
 }

\makeatletter
%\def\Hspace#1#{\@Hspace{#1}}
%\def\@Hspace#1#2{\setlength\@tempdima{#2}\hspace#1{\@tempdima}}
\makeatother

\begin{document}
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

a\Hspace{\widthof{text}}b

\Hspace*{\widthof{text}}ab
\end{document}

The commented version is a faster alternative.

However, since you probably don't want a space that disappears at page breaks, the faster method is to say

\leavevmode\hphantom{text}

Don't forget \leavevmode at the start of a paragraph; it's irrelevant if \hphantom is in the middle of a paragraph.

egreg
  • 1,121,712
  • +1 I think you two found the "right" answer jointly (see comment thread to original question above). This was a very close call. You ultimately have more details on the actual question. It won't hurt for people to upvote yours equally. – Lover of Structure Mar 02 '13 at 00:58
3

Does \wd works ?

E.g:

\newsavebox{\mymeasure} % only 1 time, in the preamble

Then:

\sbox{\mymeasure}{this particular piece of text}
X\hspace{\wd\mymeasure}X

So, to have just 1 command, in your preamble:

\newsavebox{\mymeasure}
\newcommand{\measure}[1]{\sbox{\mymeasure}{#1}\wd\mymeasure}

Usage:

X\hspace{\measure{this particular piece of texte}}X

  • No :-( Here is minimal example code: \documentclass{article} \begin{document} X\hspace{\wd\mbox{some text}}X \end{document} – Lover of Structure Feb 21 '13 at 10:54
  • Ok, \wd seems to operate on box only; I have modified my answer – Lionel MANSUY Feb 21 '13 at 10:59
  • Thanks for your efforts! Now if I try \documentclass{article} \newsavebox{\mymeasure} \newcommand{\measure}[1]{\sbox{\mymeasure}{#1}\wd\mymeasure} \begin{document} a\hspace{\measure{some text}}b \end{document}, that gives me a "Missing number, treated as zero." error. – Lover of Structure Feb 21 '13 at 11:16
  • 2
    @LoverofStructure Of course it doesn't work this way. Box assignment \sbox cannot be in expandable context, and TeX tries to expand everything when it is looking for dimensions. You would have to use the first option suggested by Lionel: place \sbox\mymeasure{some text} before the \hspace, and then use \hspace{\wd\mymeasure} – yo' Feb 21 '13 at 12:22
  • @tohecz Is there a way to put the saving of the box inside the macro? – Lionel MANSUY Feb 21 '13 at 12:25
  • 1
    No, \hspace{#1}, as is, internally uses \kern#1. The macro \kern expands what follows as long as needed to get <number><unit>, expanding all \skip and \dimen (lengths) and \ht, \wd, \dp too (box measurements), then it continues looking for plus or minus part of the skip. Since \sbox is not expandable, \kern complains that it has not found the skip value yet and hit a non-expandable barrier. – yo' Feb 21 '13 at 12:41
3

This is how I do it.

\documentclass{article}
\begin{document}
\newcommand\findwidth[1]{\setbox0\hbox{#1}\the\wd0}
This text is how long? \findwidth{This text is how long?}
\par\rule{\wd0}{1ex}
\end{document}
  • 1
    LaTeX has a standard \settowidth command for saving the width of some known text in advance, The trick calc plays is to allow this to be used inline rather than boxing the text first. – David Carlisle Feb 21 '13 at 12:36