18

Old school typesetters never allowed the last line of a paragraph to be just nearly filled. Either it would be clearly shorter than the other lines or it would be stretched to align with the right margin. (I assume the standard value where they decided to stretch the line was the depth of the indentation.)

If you have the time the same result can be achieved by checking all pages and manually adding \parfillskip=0pt to all offending paragraphs.

For example, this document:

\documentclass{article}
\usepackage[paperwidth=3in,paperheight=3in]{geometry}
\begin{document}
\thispagestyle{empty}
asdfjk adsf af dsasdf f dasf fdsa fds afsd fdsaf asdfsdafdsa asdf dsaf asdf sadf
asdfjk adsf af dsasdf f dasf fdsa fds afsd fdsaf asdfsdafdsa asdf dsaf asdf sadf
sasdfjk adsf af dsasdf f dasf fdsa fds afsd fdsaf asdfsdafdsa asdf dsaf asdf sadf
asdfjk adsf af dsasdf f dasf fdsa fds afsd fdsaf asdfsdafdsa asdf dsaf asdf sadf
asdfjk adsf af dsasdf f dasf fdsa fds afsd fdsaf asdfsdafdsa asdf dsaf asdfxx 
\end{document}

renders to a paragraph with an almost-full last line:

Almost-full Paragraph

I've earlier been told that it would most probably be impossible to get pdfLaTeX to do this automatically.

If this is true -- could it be done with LuaTeX?

  • 5
    I............don't............think...........it's...........a..........good............idea. – percusse Jan 17 '12 at 20:51
  • 1
    @percusse Note that I only want to stretch these last lines by 1em (the typical indentation) which shouldn't be more -- I don't have the actual figures handy -- than also the other lines in the paragraph will be stretched. – Florian Jan 17 '12 at 21:00
  • 1
    Sorry if that was slightly rude. What I meant is that the same old school typesetters also (AFAIK) start handling the issue by considering the whole paragraph via eyeballing the pleasing cheats here and there. Probably that is the reason why there needs to be some sort of a human hand touching the issue. – percusse Jan 17 '12 at 21:04
  • 1
    Perhaps related: Ensure minimal length of last line – cmhughes Jan 17 '12 at 21:11
  • 1
    @percusse No offense taken! You are definitely right in the matter: Visual control is needed, but it would anyway be nice not to have too many things to correct manually. And if anybody could come up with a solution, that would take the rest of the paragraph into account, that would clearly be better than only stretching the last line! – Florian Jan 17 '12 at 21:21
3

This can be done in LuaTeX using the pre_linebreak_filter callback: Before the "normal" linebreaking, do a "trial" linebreak and measure the effective parfill. If it is below the current value of \parindent, set the parfill for this paragraph to zero. Additionally, it can sometimes happen that \parfillskip=0pt fails. In this case this will stretch the last line without affecting the remaining paragraph, leading to a potentially very stretched line. If you prefer to just accept a nearly filled line in this case, just delete the part surrounded by

luatexbase.add_to_callback("post_linebreak_filter", function(head)
  ...
end, "drop_short_parfill")
\documentclass{article}
\usepackage[paperwidth=3in,paperheight=3in]{geometry}
\usepackage{luacode}
\begin{luacode*}
  local glue = node.id'glue'
  luatexbase.add_to_callback("pre_linebreak_filter", function(head)
    local copy = node.copy_list(head)
    local parfill = node.tail(copy)
    if not parfill.id == glue or not parfill.subtype == 15 then
      texio.write_nl'Unable to find the parfill. Disabling special handler.'
      node.flush_list(copy)
      return true
    end
    local lines, infos = tex.linebreak(copy)
    local last = node.tail(lines)
    if tex.parindent > node.effective_glue(parfill, last) then
      node.flush_list(lines)
      copy = node.copy_list(head)
      parfill = node.tail(copy)
      node.setglue(parfill)
      lines, infos = tex.linebreak(copy)
      local t = node.tail(lines)
      if t.width == node.rangedimensions(t, t.head) then
        parfill = node.tail(head)
        node.setglue(parfill)
      end
    end
    node.flush_list(lines)
    return true
  end, "drop_short_parfill")
  luatexbase.add_to_callback("post_linebreak_filter", function(head)
    local t = node.tail(head)
    local tt = node.slide(t.head)
    while tt.id ~= glue or tt.subtype ~= 15 do
      tt = tt.prev
    end
    if tex.parindent > node.effective_glue(tt, t) then
      node.setglue(tt)
      local n = node.hpack(t.head, t.width, "exactly")
      head = node.insert_before(head, t, n)
      n.next, t.head = nil, nil
      node.free(t)
    end
    return head
  end, "drop_short_parfill")
\end{luacode*}
\begin{document}
\thispagestyle{empty}
asdfjk adsf af dsasdf f dasf fdsa fds afsd fdsaf asdfsdafdsa asdf dsaf asdf sadf
asdfjk adsf af dsasdf f dasf fdsa fds afsd fdsaf asdfsdafdsa asdf dsaf asdf sadf
sasdfjk adsf af dsasdf f dasf fdsa fds afsd fdsaf asdfsdafdsa asdf dsaf asdf sadf
asdfjk adsf af dsasdf f dasf fdsa fds afsd fdsaf asdfsdafdsa asdf dsaf asdf sadf
asdfjk adsf af dsasdf f dasf fdsa fds afsd fdsaf asdfsdafdsa asdf dsaf asdf

asdfjk adsf af dsasdf f dasf fdsa fds afsd fdsaf asdfsdafdsa asdf dsaf asdf sadf
asdfjk adsf af dsasdf f dasf fdsa fds afsd fdsaf asdfsdafdsa asdf dsaf asdf sadf
sasdfjk adsf af dsasdf f dasf fdsa fds afsd fdsaf asdfsdafdsa asdf dsaf asdf sadf
asdfjk adsf af dsasdf f dasf fdsa fds afsd fdsaf asdfsdafdsa asdf dsaf asdf sadf
asdfjk adsf af dsasdf f dasf fdsa fds afsd fdsaf asdfsdafdsa asdf dsaf asdfx

asdfjk adsf af dsasdf f dasf fdsa fds afsd fdsaf asdfsdafdsa asdf dsaf asdf sadf
asdfjk adsf af dsasdf f dasf fdsa fds afsd fdsaf asdfsdafdsa asdf dsaf asdf sadf
sasdfjk adsf af dsasdf f dasf fdsa fds afsd fdsaf asdfsdafdsa asdf dsaf asdf sadf
asdfjk adsf af dsasdf f dasf fdsa fds afsd fdsaf asdfsdafdsa asdf dsaf asdf sadf
asdfjk adsf af dsasdf f dasf fdsa fds afsd fdsaf asdfsdafdsa asdf dsaf asdfxx
\end{document}

In the example, the first paragraph does not need any change. The third automatically gets \parfillskip=0pt and for the second paragraph, only the last line is stretched.

enter image description here

In case you wonder why the result of the "trial" linebreak is always discarded instead of being reused as the actual linebreak, especially if no change is necessary: That would be implemented through linebreak_filter, which is broken in current LuaTeX versions.

| improve this answer | |
  • Great! Though the other two answers are very interesting indeed, this was more the kind of solution I was looking for. Just one question: When I run your code on TL 18/ LuaTeX 1.07.0 with the last "x" removed I get a "as-dfx" hyphenation and an extra line. I assume that this would also sometimes occur with real texts. Would it be possible to completely rule out the adding of extra lines, so that I could add this to an existing document without risking new line- and pagebreaks? – Florian Jun 27 '19 at 12:26
  • @Florian The question what to do in this case. It happens if TeX decides that there is way with badness below 10000 to align the paragraph, so what should be done then? You could fall back to default linebreaking, but then there are some paragraphs with small parfill again. You could also use an "emergency mode" where always only the last line is stretched to fill the entire line, disregarding the badness. But if TeX decides that this is not an option, it normally has a reason... – Marcel Krüger Jun 27 '19 at 13:05
  • @Florian I added this to the answer. – Marcel Krüger Jun 27 '19 at 14:35
  • This is just perfect for me personally. Thanks a lot! For others (eight members have starred this question) who might prefer the reasoning behind your first answer I'd suggest you also leave the other code in your answer. Imo this would make the answer even more comprehensive. – Florian Jun 28 '19 at 10:22
  • @Florian I think if you do not want this version, you probably prefer the version without post_linebreak_filter instead of the original one. So in that ease it is easy to modify. – Marcel Krüger Jun 28 '19 at 10:23
14

Adding a \parfillskip that has a minimum length and infinite stretchability seems like it would do it.

For example, the below sets parfillskip such that the end of the paragraph must have AT LEAST whitespace equal to the minimum of 2 x characters:

\documentclass{article}
\usepackage[paperwidth=3in,paperheight=3in]{geometry}
\begin{document}
\thispagestyle{empty}
%% Measure the width of 'x' in this font
%% and set \parfillskip equal to two times
%% that width.
\setbox0=\hbox{x} \parfillskip=2\wd0 plus1fil 

asdfjk adsf af dsasdf f dasf fdsa fds afsd fdsaf asdfsdafdsa asdf dsaf asdf sadf
asdfjk adsf af dsasdf f dasf fdsa fds afsd fdsaf asdfsdafdsa asdf dsaf asdf sadf
sasdfjk adsf af dsasdf f dasf fdsa fds afsd fdsaf asdfsdafdsa asdf dsaf asdf sadf
asdfjk adsf af dsasdf f dasf fdsa fds afsd fdsaf asdfsdafdsa asdf dsaf asdf sadf
asdfjk adsf af dsasdf f dasf fdsa fds afsd fdsaf asdfsdafdsa asdf dsaf asdfxx 
\end{document}

Commenting out the \parfillskip=2\wd0 plus1fil line gives:

Without <code>parfillskip</code>

But it shows this when parfillskip is set:

With <code>parfillskip</code>

| improve this answer | |
  • 1
    ex is a vertical measure, I think you meant em? – morbusg Jan 17 '12 at 22:01
  • Good catch - I updated it so parfillskip is set based on the width of the x character. – Justin Bailey Jan 17 '12 at 22:24
  • This indeed solves the problem, but I'd prefer a solution that doesn't add extra lines... – Florian Jan 17 '12 at 22:41
  • Was this question ever answered? I am interested whether any automatic method listed above worked in the original aim of automatically flushing right any end of paragraph line followed by at least so much whitespace in the line. – user34902 Aug 11 '13 at 14:31
14

I'm not sure I'm serious here, but if you are able (and or willing) to retypeset your paragaphs by collecting them first then there is one feature of TeX that would allow you to measure this and act upon it: TeX knows about the length of the last line of a paragraph (or partial paragraph to be precise) if that is followed by a math display. In that case the length of the line above the display is known within the formula as \predisplaysize. Thus you could

  • collect the paragraph material in a macro, say
  • trial typeset it and check out how close its last line is to the full line
  • depending on the result retypeset the paragraph in earnest using a suitable value of \parfillskip, e.g., set it to 0pt or set it to 2em plus 1fil or whatever.

Here is the code to check it out:

\newdimen\mydim
\def\checkit{%
  \mydim\hsize
  \advance\mydim by -\predisplaysize
  \advance\mydim by 2em
  \typeout{There is \the\mydim\space  space available on the last line}%
}


asdfjk adsf af dsasdf f dasf fdsa fds afsd fdsaf asdfsdafdsa asdf dsaf asdf sadf
asdfjk adsf af dsasdf f dasf fdsa fds afsd fdsaf asdfsdafdsa asdf dsaf asdfxx 
$$\checkit$$

The 2em are substracted because the \predisplaysize is the length of the last line + 2em. For your example that gives us:

There is 4.98819pt space available on the last line

There are some special conditions: if the last line is not typeset at its natural width then the size given as \maxdimen, e.g., if \parfillskipwas set to 0pt, and if there was no previous line then it is set to -\maxdimen but both cases could be controlled in your situation.

| improve this answer | |

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.