3

The goal of this code is to arrange three pieces of text on a line such that if any of the three parts are too long, then the center text is lowered by \baselineskip:

\documentclass{article}

\usepackage[showframe,papersize={5.5in,8.5in}]{geometry}
\newlength{\testwd}

\newcommand{\entry}[4][]{%
    \setbox1=\hbox{\strut\bfseries#2}%
    \setbox2=\hbox{\strut\itshape#3}%
    \setbox3=\hbox{\strut#4}%
    \setlength{\testwd}{\ifdim\wd1>\wd3 \wd1\else\wd3\fi}% Which is longer, L or R? Use it.
    \noindent%
        \if###1##\else\llap{#1\thinspace}\fi
        \hbox to 0pt{\box1\hss}%
        \hfill
        %% If the longer side (L or R) is longer than the half the remaining area around
        %% the center text, drop the center text.
        \ifdim\dimexpr \testwd - (\linewidth - \wd2)/2\relax>0pt\lower\baselineskip\fi
        \hbox{\box2}%
        \hfill
        \hbox to 0pt{\hss\box3}%
    \par
}

\begin{document}

\entry{Left side text}{Center Text}{Right text}

\entry{Left side text that is rather long}{Center Text}{Right side text}

\entry{Left side text}{Center Text}{Right side text that is rather long}

\end{document}

This produces:

program text example

Out of curiosity, I wonder if there is a way to do this without explicitly setting \testwd? In other words, can the calculation of \testwd be incorporated into the decision about whether or not to lower the center text? Or, is there a more efficient way of doing this altogether? (There is How to make text aligned left/center/right in the same line?, but this does not accomplish the same thing in that long lines overwrite each other, particularly the center text. All bets are off, of course, if the left and right texts overwrite each other. In this case that does not happen and so is not an issue.) This is useful in concert programs for example.

  • I altered the title of the post to be less cryptic. – sgmoye Jan 20 at 18:32
  • Setting \leftskip and \rightskip large enough and adding \null\allowbreak to the beginning (of the center text) might squeeze the center text to the next line (which would also be too small). A negative \hangindent might do it. In any case, it would be far more complex than a simple test. – John Kormylo Jan 20 at 18:56
  • 1
    If you just want to avoid creating a new length, use \dimen0. BTW, since your are already using box registers 1-3, you should put everything inside a group. – John Kormylo Jan 20 at 19:05
  • @JohnKormylo Yes, the \leftskip...\rightskip possibility makes my head ache. About the \dimen0 and grouping -- quite right. Thanks. – sgmoye Jan 20 at 19:21
  • 1
    you should use boxes 0,2,4 not 1,2,3; don't use odd numbered scratch registers fro local assignment. – David Carlisle Jan 20 at 22:51
2

The following measures the length of each text component uses that length to condition on placing the centre text inside a \makecell[t] construction with a blank first line or as-is.

enter image description here

\documentclass{article}

\usepackage[showframe,papersize={5.5in,8.5in}]{geometry}

\usepackage{makecell}

\newlength{\LeftItem}
\newlength{\CentreItem}
\newlength{\RightItem}

\newcommand{\entry}[3]{%
  \par
  \settowidth{\LeftItem}{\bfseries #1}%
  \settowidth{\CentreItem}{\itshape #2}%
  \settowidth{\RightItem}{#3}%
  \noindent
  \makebox[0pt][l]{\bfseries #1}\hfill
  \makebox[0pt]{%
    \ifdim\LeftItem>.5\dimexpr\linewidth-\CentreItem\relax
      \makecell[t]{\strut \\ \itshape #2}%
    \else
      \ifdim\RightItem>.5\dimexpr\linewidth-\CentreItem\relax
        \makecell[t]{\strut \\ \itshape #2}%
      \else
        \itshape #2%
      \fi
    \fi
  }\hfill
  \makebox[0pt][r]{#3}%
  \par
}

\begin{document}

\entry{Left side text}{Center Text}{Right text}

\entry{Left side text that is rather long}{Center Text}{Right side text}

\entry{Left side text}{Center Text}{Right side text that is rather long}

\end{document}

The code will make sure the contents fits the \linewidth, and the length of the elements are measured exactly. This might lead to constructions where everything is still set on the same line, but very close to one another. So, it might be feasible to add a little buffer through something like:

\newcommand{\entry}[3]{%
  % ...
  \settowidth{\CentreItem}{\itshape ~#2~}% Add space on either side of centre item
  % ...
}
0

Update

Here is the revised code that takes into account suggestions from @DavidCarlisle and @JohnKormylo. I've added spacing between the longest left/right and center texts; also used \rlap and \llap instead of rolling my own. The output is precisely the same.

\documentclass{article}

\usepackage[showframe,papersize={5.5in,8.5in}]{geometry}

%% #1: optional marginal character; #2: left text; #3: center text; #4: right text
\newcommand{\entry}[4][]{%
    \begingroup
        \setbox0=\hbox{\strut\bfseries#2}%
        \setbox2=\hbox{\strut\itshape#3}%
        \setbox4=\hbox{\strut#4}%
        %% Which is longer, L or R? Use it:
        \setlength{\dimen0}{\ifdim\wd0>\wd4 \wd0\else\wd4\fi}%
        \noindent%
            \if###1##\else\llap{#1\thinspace}\fi %% Optional marginal character
            \rlap{\box0}%
            \hfill
            %% If space between longest L/R entry and center text
            %% is less than 0.5em, lower the center text by \baselineskip:
            \ifdim\dimexpr \dimen0 - (\linewidth - \wd2 - 0.5em)/2\relax>0pt
                \lower\baselineskip
            \fi
            \hbox{\box2}%
            \hfill
            \llap{\box4}%
        \par
    \endgroup
}

\begin{document}

\entry{Left side text}{Center Text}{Right text}

\entry{Left side text that is rather long}{Center Text}{Right side text}

\entry{Left side text}{Center Text}{Right side text that is rather long}

\end{document}

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.