5

When typesetting paragraphs, I want to ensure their last line is neither overly short nor overly long. To that end, I'm looking for settings that produce either:

  • a fully justified, perfectly rectangular paragraph;
  • a justified paragraph whose last line is filled more than 20% and less than 80%.

In other words, no paragraph should have a last line that is filled for less than 20% or more than 80%—they should become fully justified instead. The effect could be achieved by manually adding \parfillskip 0pt to (only) those paragraphs, but this is exactly what I want to automate instead.

This document shows two good examples and two bad examples:

\documentclass[a4paper]{article}
\usepackage[utf8]{inputenc}

% Page setup
\usepackage[a4paper,margin=3cm]{geometry}

% Typography
\usepackage{newtxtext,newtxmath}
\usepackage{microtype}
\parindent 0pt
\parskip\baselineskip

\begin{document}

\textbf{I'm looking for settings
        that produce either a)~a~perfect rectangle
        or b)~a paragraph whose last line
        is filled more than 20\% and less than 80\%.}

\section*{Good examples}

\textbf{My perfect paragraph is a~rectangle:}

{
\parfillskip 0pt
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed rhoncus lorem eget ultricies bibendum. Duis luctus felis arcu, sit amet dapibus orci imperdiet id. Duis ullamcorper tortor eget leo fringilla, a lacinia nisl pulvinar. Etiam id facilisis augue. Sed convallis tempus ex, sed accumsan justo pulvinar vitae. Sed id sapien leo. Aliquam posuere ex lacus, ut posuere metus ullamcorper eu. Duis a imperdiet nibh. Donec tincidunt hendrerit nulla, et convallis metus imperdiet nec. Pellentesque massa enim, pharetra in pulvinar a, efficitur nec lorem. Cras mattis ex lorem, et euismod ligula rhoncus. Aenean ultricies quis velit non faucibus.

}

\textbf{However, this is not always achievable (and that's fine):}

{
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed rhoncus lorem eget ultricies bibendum. Duis luctus felis arcu, sit amet dapibus orci imperdiet id. Duis ullamcorper tortor eget leo fringilla, a lacinia nisl pulvinar. Etiam id facilisis augue. Sed convallis tempus ex, sed accumsan justo pulvinar vitae. Sed id sapien leo. Aliquam posuere ex lacus, ut posuere metus ullamcorper eu. Duis a imperdiet nibh. Donec tincidunt hendrerit nulla, et convallis metus imperdiet nec. Pellentesque massa enim, pharetra in pulvinar a, efficitur nec lorem. Cras mattis ex lorem, et euismod ligula rhoncus.

}

\section*{Bad examples}

\textbf{This last line is too short:}

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed rhoncus lorem eget ultricies bibendum. Duis luctus felis arcu, sit amet dapibus orci imperdiet id. Duis ullamcorper tortor eget leo fringilla, a lacinia nisl pulvinar. Etiam id facilisis augue. Sed convallis tempus ex, sed accumsan justo pulvinar vitae. Sed id sapien leo. Aliquam posuere ex lacus, ut posuere metus ullamcorper eu. Duis a imperdiet nibh. Donec tincidunt hendrerit nulla, et convallis metus imperdiet nec. Pellentesque massa enim, pharetra in pulvinar a, efficitur nec lorem.

\textbf{The above can be fixed with}
\verb!\parfillskip 0pt plus 0.80\textwidth!
\textbf{but that doesn't help for the case below.}

\textbf{This last line should have been justified (note the small gap at the end):}

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed rhoncus lorem eget ultricies bibendum. Duis luctus felis arcu, sit amet dapibus orci imperdiet id. Duis ullamcorper tortor eget leo fringilla, a lacinia nisl pulvinar. Etiam id facilisis augue. Sed convallis tempus ex, sed accumsan justo pulvinar vitae. Sed id sapien leo. Aliquam posuere ex lacus, ut posuere metus ullamcorper eu. Duis a imperdiet nibh. Donec tincidunt hendrerit nulla, et convallis metus imperdiet nec. Pellentesque massa enim, pharetra in pulvinar a, efficitur nec lorem. Cras mattis ex lorem, et euismod ligula rhoncus. Aenean ultricies quis velit non ut faucibus.

\textbf{The above can be fixed with}
\verb!\parfillskip 0pt!
\textbf{but that doesn't help for the case below.}

\end{document}

A solution should consist of adjustments in the preamble only. No special commands should be required at the beginning or end of any paragraph. I hope that, as in several related cases, it can be solved with a special bit of glue in \parfillskip, but I'm not sure whether this is possible.


Related questions, whose solutions I have tried but do not produce the intended output:

  • @DavidCarlisle I'd prefer a (La)TeX solution if there is one. – Ruben Verborgh Apr 14 '17 at 11:45
  • Not sure how to automate but, in an \mbox, place the last several words of the paragraph and a \hspace{.2\linewidth}, so that your 1st paragraph (in article class) would be When typesetting paragraphs, I want to ensure their last line is neither overly short nor overly long. To that end, I'm looking for settings \mbox{that produce either:\hspace{.2\linewidth}} – Steven B. Segletes Apr 14 '17 at 11:46
  • @DavidCarlisle A natural width of 20–80% is fine, so a line with a natural width of 21% is fine as well. – Ruben Verborgh Apr 14 '17 at 11:46
  • @StevenB.Segletes It's the automation that is difficult. Manually adding \parfilskip0pt to selected paragraphs would also work, but I want TeX to apply this automatically to paragraphs with small or long last liens. – Ruben Verborgh Apr 14 '17 at 11:48
  • 1
    @RubenVerborgh typically they do, unless the paragraphs are quite long, to give more flexibility to avoid the short lines, with paragraphs of say 1-3 lines avoiding excessive word space is likely not possible. – David Carlisle Apr 14 '17 at 12:10
3

This approach is semi-automatic, in that one must still enclose the paragraph in a \fixit macro.

REVISED APPROACH

Rather than dissect the paragraph word by word, as in the ORIGINAL APPROACH below, here, I just measure the length of the \hbox containing the paragraph material, and decide, based on the length, whether to employ a trailing \hspace{}\mbox and/or a \parfillskip0pt.

The macro provides an optional argument if tweaking is required. It represents the largest fractional line overrun that will be compressed away, currently set to .05 (5%). You will know it needs tweaking if a line that you thought would be compressed away gets expanded into a whole new widely spaced line. Setting the default value to .0 will eliminate any problems, but at the expense that some lines that could otherwise be compressed to eliminate trailing widows are instead expanded so that the widow takes up 20% of the subsequent line.

\documentclass{article}
\usepackage[margin=1.5in,top=0cm,bottom=0cm]{geometry}
\newcommand\fixit[2][.05]{%
  \setbox0=\hbox{\hspace{\parindent}#2}\fixithelp{#1}{#2}}
\newcommand\fixithelp[2]{%
  \wd0=\dimexpr\wd0-\linewidth\relax%
  \ifdim\wd0>0pt\relax%
    \fixithelp{#1}{#2}%
  \else%
    \wd0=\dimexpr\wd0+\linewidth\relax
    \ifdim\wd0>.9\linewidth\relax%
      {\parfillskip0pt\relax#2\par}%
    \else%
      \ifdim\wd0>.8\linewidth\relax%
        {\parfillskip0pt\relax#2\hspace{.2\linewidth}\par}%
      \else%
        \ifdim\wd0<#1\linewidth\relax%
          {\parfillskip0pt\relax#2\par}%
        \else%
          \ifdim\wd0<.2\linewidth\relax%
            {\parfillskip0pt\relax#2\hspace{.8\linewidth}\mbox{}\par}%
          \else%
            #2%
          \fi
        \fi
      \fi
    \fi
  \fi%
}
\sloppy
\parskip1ex
\begin{document}
\noindent\rule{.2\linewidth}{2pt}\hfill\rule{.2\linewidth}{2pt}

\fixit{%
yakity yack, paragraphs, I want to \textit{ensure their last line} is neither 
overly short nor overly long. To that end, I'm looking for settings that 
produce either:}

\fixit{%
yack, yakity yack, paragraphs, I want to \textit{ensure their last line} is neither 
overly short nor overly long. To that end, I'm looking for settings that 
produce either:}

\fixit{%
yakity yack, yakity yack, paragraphs, I want to \textit{ensure their last line} is neither 
overly short nor overly long. To that end, I'm looking for settings that 
produce either:}

\fixit{%
yack, yakity yack, yakity yack, paragraphs, I want to \textit{ensure their last line} is neither 
overly short nor overly long. To that end, I'm looking for settings that 
produce either:}

\fixit{%
yackity yack, yakity yack, yakity yack, paragraphs, I want to \textit{ensure their last line} is neither 
overly short nor overly long. To that end, I'm looking for settings that 
produce either:}

\fixit{%
yack, yackity yack, yakity yack, yakity yack, paragraphs, I want to \textit{ensure their last line} is neither 
overly short nor overly long. To that end, I'm looking for settings that 
produce either:}

\fixit{%
yackity yack, yackity yack, yakity yack, yakity yack, paragraphs, I want to \textit{ensure their last line} is neither 
overly short nor overly long. To that end, I'm looking for settings that 
produce either:}

\fixit{%
yack, yackity yack, yackity yack, yakity yack, yakity yack, paragraphs, I want to \textit{ensure their last line} is neither 
overly short nor overly long. To that end, I'm looking for settings that 
produce either:}

\fixit{%
yackity yack, yackity yack, yackity yack, yakity yack, yakity yack, paragraphs, I want to \textit{ensure their last line} is neither 
overly short nor overly long. To that end, I'm looking for settings that 
produce either:}

\fixit{%
yack, yackity yack, yackity yack, yackity yack, yakity yack, yakity yack, paragraphs, I want to \textit{ensure their last line} is neither 
overly short nor overly long. To that end, I'm looking for settings that 
produce either:}

\fixit{%
yackity yack, yackity yack, yackity yack, yackity yack, yakity yack, yakity yack, paragraphs, I want to \textit{ensure their last line} is neither 
overly short nor overly long. To that end, I'm looking for settings that 
produce either:}

\fixit{%
yack, yackity yack, yackity yack, yackity yack, yackity yack, yakity yack, yakity yack, paragraphs, I want to \textit{ensure their last line} is neither 
overly short nor overly long. To that end, I'm looking for settings that 
produce either:}

\fixit{%
yackity yack, yackity yack, yackity yack, yackity yack, yackity yack, yakity yack, yakity yack, paragraphs, I want to \textit{ensure their last line} is neither 
overly short nor overly long. To that end, I'm looking for settings that 
produce either:}

\fixit{%
yack, yackity yack, yackity yack, yackity yack, yackity yack, yackity yack, yakity yack, yakity yack, paragraphs, I want to \textit{ensure their last line} is neither 
overly short nor overly long. To that end, I'm looking for settings that 
produce either:}

\fixit{%
yackity yack, yackity yack, yackity yack, yackity yack, yackity yack, yackity yack, yakity yack, yakity yack, paragraphs, I want to \textit{ensure their last line} is neither 
overly short nor overly long. To that end, I'm looking for settings that 
produce either:}

\fixit{%
yack, yackity yack, yackity yack, yackity yack, yackity yack, yackity yack, yackity yack, yakity yack, yakity yack, paragraphs, I want to \textit{ensure their last line} is neither 
overly short nor overly long. To that end, I'm looking for settings that 
produce either:}

\fixit{%
yackity yack, yackity yack, yackity yack, yackity yack, yackity yack, yackity yack, yackity yack, yakity yack, yakity yack, paragraphs, I want to \textit{ensure their last line} is neither 
overly short nor overly long. To that end, I'm looking for settings that 
produce either:}

\end{document}

Margins=1.5in

enter image description here

Margins = 1.7in

enter image description here

Margins = 1.9in

enter image description here

ORIGINAL APPROACH

But what the macro does is regurgitate the paragraph word by word until it is down to the last 4 words. Then, it determines how many of those last words are required to exceed .2\linewidth and bundles them together inside an \mbox along with a \hspace{.2\linewidth}. If the last 3 words do not exceed .2\linewidth, then it uses all 4 in the \mbox.

For what the OP requests, \sloppy will almost assuredly be required, since we are talking about changing the text length by .4\linewidth in one fell swoop (which is almost always an undesired thing). So I added it.

One thing that will break the approach is if a previously opened group is ended within the last 4 words of the paragraph. That would apply, for example, when closing a long \textit near the end of a paragraph.

The other thing to note is that a perfectly rectangular paragraph will never be created with this approach, because of the added \hspace at the end of the paragraph.

\documentclass{article}
\newcommand\fixit[1]{\fixithelp#1 \cr\relax}
\def\fixithelp#1 #2 #3 #4 #5\relax{\ifx \cr#5\finishup#1 #2 #3 #4\relax%
  \else#1 \fixithelp#2 #3 #4 #5\relax\fi}
\def\finishup#1 #2 #3 #4\relax{ %
  \setbox0=\hbox{#4}%
  \ifdim\wd0>.2\linewidth\relax #1 #2 #3 \mbox{#4\hspace{.2\linewidth}}\else%
    \setbox0=\hbox{#3 #4}%
    \ifdim\wd0>.2\linewidth\relax #1 #2 \mbox{#3 #4\hspace{.2\linewidth}}\else%
      \setbox0=\hbox{#2 #3 #4}%
      \ifdim\wd0>.2\linewidth\relax #1 \mbox{#2 #3 #4\hspace{.2\linewidth}}\else%
        \mbox{#1 #2 #3 #4\hspace{.2\linewidth}}%
      \fi%
    \fi%
  \fi%
}
\sloppy
\parskip1em
\begin{document}
\fixit{typesetting 
paragraphs, I want to \textit{ensure their last line} is neither 
overly short nor overly long. To that end, I'm looking for settings that 
produce either:}

\fixit{%
paragraphs, I want to \textit{ensure their last line} is neither 
overly short nor overly long. To that end, I'm looking for settings that 
produce either:}

\fixit{%
paragraphs, I want to \textit{ensure their last line} is neither 
overly short nor overly long. To that end, I'm looking for settings that 
produce overlylongwords:}

\fixit{%
I want to \textit{ensure their last line} is neither 
overly short nor overly long. To that end, I'm looking for settings that 
produce overlylongwords:}

\fixit{%
typesetting paragraphs, I want to \textit{ensure their last line} is neither 
overly short nor overly long. To that end, I'm looking for settings that 
produce I I I I:}

\fixit{%
typesetting I want to \textit{ensure their last line} is neither 
overly short nor overly long. To that end, I'm looking for settings that 
produce I I I I:}
\end{document}

enter image description here

  • 1
    Thanks, definitely a good start! If there's somehow a way to do this without the \fixit, then this seems like a good approach. Out of curiosity, could you elaborate on why a solution is not possible / difficult with only glue settings (as the related problems linked to in the question do)? – Ruben Verborgh Apr 21 '17 at 7:58
  • @RubenVerborgh Thanks. Unfortunately, glue is not my specialty, and so I only knew how to attack the problem with a sledgehammer, and not a scalpel. – Steven B. Segletes Apr 21 '17 at 10:22

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.