[Tex/LaTex] Avoid just nearly filled last lines

luatexparagraphsspacingtypography

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?

Best Answer

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.