避免几乎填满最后几行

避免几乎填满最后几行

老派排字员从来不会允许段落的最后一行几乎填充。要么它会明显比其他线条短,要么它会被拉伸以与右边距对齐。(我认为他们决定拉伸线条的标准值是缩进的深度。)

如果您有时间,可以通过检查所有页面并手动添加\parfillskip=0pt所有违规段落来获得相同的结果。

例如本文档:

\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}

渲染为最后一行几乎完整的段落:

几乎满的段落

我之前曾被告知,pdfLaTeX 自动执行此操作很可能是不可能的。

如果这是真的——可以用 LuaTeX 来实现吗?

答案1

这可以在 LuaTeX 中使用回调来完成pre_linebreak_filter:在“正常”换行之前,进行“试验性”换行并测量有效填充。如果它低于当前的值\parindent,则将此段落的填充设置为零。此外,有时可能会发生\parfillskip=0pt失败的情况。在这种情况下,这将拉伸最后一行而不影响其余段落,从而导致行可能非常拉伸。如果您在这种情况下只想接受几乎填充的行,只需删除被包围的部分即可

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}

在本例中,第一段无需任何更改。第三段会自动拉伸\parfillskip=0pt,而第二段仅拉伸最后一行。

在此处输入图片描述

如果你想知道为什么“试验”换行的结果总是被丢弃而不是被重新用作实际的换行符,特别是如果不需要进行任何更改:这将通过 来实现linebreak_filter,但这在当前的 LuaTeX 版本中已被破坏。

答案2

添加\parfillskip具有最小长度和无限可拉伸性的似乎可以做到这一点。

例如,以下设置parfillskip使得段落末尾必须至少有等于最少 2 个x字符的空格:

\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}

注释掉该\parfillskip=2\wd0 plus1fil行会得到:

没有 <code>parfillskip</code>

parfillskip但是设置后会显示以下内容:

使用 <code>parfillskip</code>

答案3

我不确定我是否认真,但如果你能够(或愿意)通过先收集段落来重新排版它们,那么 TeX 的一个功能可以让你测量并采取行动:如果段落(或准确地说是部分段落)的最后一行后面跟着数学显示,TeX 就会知道它的长度。在这种情况下,显示上方的行的长度在公式中为\predisplaysize。因此你可以

  • 在宏中收集段落材料,比如
  • 试排版一下,看看最后一行和整行有多接近
  • 根据结果​​,使用合适的值认真重新排版段落\parfillskip,例如,将其设置为0pt或将其设置为2em plus 1fil或其他。

以下是检查代码:

\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$$

减去是2em因为\predisplaysize是最后一行的长度 + 2em。对于您的示例,我们得到:

There is 4.98819pt space available on the last line

有一些特殊情况:如果最后一行不是按照其自然宽度排版,那么给出的大小\maxdimen,例如,如果\parfillskip设置为 0pt,并且如果没有前一行,那么它将设置为,-\maxdimen但这两种情况都可以根据您的情况进行控制。

相关内容