如果我用非零值框住某个内容\parskip
,然后使用 将其拆分\vsplit
,则生成的拆分框将具有所需的准确高度。这通常比我想要的要多,因此我会立即\unvbox
拆分框并重新拆分\vbox
以删除底部多余的空白。这种方法通常有效,除非您有非零值\parskip
并且拆分点位于段落之间。考虑 MWE:
\parskip=7pt
\setbox0=\vbox{\input knuth\par}
\splittopskip=0pt
\setbox1=\vsplit 0 to 2\baselineskip
\setbox1=\vbox{\unvbox1}
\ifdim\dp1<\dp\strutbox
\dp1=\dp\strutbox
\fi
\box1
\box0
\bye
正如您在输出中看到的,\parskip
前两段之间缺少 ,因为那是进行拆分的地方。
有没有办法检测分割框的最后一项是否是\parskip
?也欢迎使用 LuaTeX 解决方案。
编辑:我意识到如果中断发生在该点,则\vsplit
立即丢弃\parskip
。这个简单的例子很容易说明这一点:
\parskip=7pt
\setbox0=\vbox{\input knuth\par}
\setbox1=\vsplit 0 to 2\baselineskip
\vbox{\unvbox1 \unvbox0}
\bye
答案1
如果你确实希望框具有精确的高度,LuaTeX 支持
\setbox1=\vsplit 0 upto 2\baselineskip
作为内置替代方案
\setbox1=\vsplit 0 to 2\baselineskip
\setbox1=\vbox{\unvbox1}
正如您已经看到的,这并没有真正解决问题,因为 parskip 已被丢弃\vsplit
。但 LuaTeX 还允许保存和检查从 Lua 拆分中丢弃的元素,因此我们可以重新插入 parskip:
\directlua{
function my_split()
% Scan the parameters
local result_box = token.scan_int()
token.scan_keyword'='
local original_box = token.scan_int()
token.scan_keyword'upto'
local height = token.scan_dimen()
% Temporarly set tex.savingvdiscards
local savediscards = tex.savingvdiscards
tex.savingvdiscards = 1
% Do the actual split
local b = tex.splitbox(original_box, height, 'additional')
tex.savingvdiscards = savediscards
% Analyze tex.lists.split_discards_head to find the parskip
local current = tex.lists.split_discards_head
if current then current.prev = nil end
while current do
if current.id == 12 and current.subtype == 3 then
local this = current
tex.lists.split_discards_head, current = node.remove(tex.lists.split_discards_head, current)
b.head = node.insert_after(b.head, nil, this)
b.height = b.height + this.width
else
current = current.next
end
end
% Save the result
tex.box[result_box] = b
end
}
\protected\def\setparsplit{\relax\directlua{my_split()}}
这\setparsplit
可以用作
\directlua{
function my_split()
% Scan the parameters
local result_box = token.scan_int()
token.scan_keyword'='
local original_box = token.scan_int()
token.scan_keyword'upto'
local height = token.scan_dimen()
% Temporarly set tex.savingvdiscards
local savediscards = tex.savingvdiscards
tex.savingvdiscards = 1
% Do the actual split
local b = tex.splitbox(original_box, height, 'additional')
tex.savingvdiscards = savediscards
% Analyze tex.lists.split_discards_head to find the parskip
local current = tex.lists.split_discards_head
if current then current.prev = nil end
while current do
if current.id == 12 and current.subtype == 3 then
local this = current
tex.lists.split_discards_head, current = node.remove(tex.lists.split_discards_head, current)
b.head = node.insert_after(b.head, nil, this)
b.height = b.height + this.width
else
current = current.next
end
end
% Save the result
tex.box[result_box] = b
end
}
\protected\def\setparsplit{\relax\directlua{my_split()}}
\parskip=7pt
\setbox0=\vbox{\input knuth\par}
\splittopskip=0pt
\setparsplit1=0 upto 2\baselineskip
\ifdim\dp1<\dp\strutbox
\dp1=\dp\strutbox
\fi
\box1
\box0
\bye
答案2
首先,您的示例包含不精确的尺寸测量。您设置\splittopskip
为 0pt,因此 box1 的结果包括两条线,其高度为第一条线加上一个基线跳跃。但是您说\vsplit to2\baselineskip
,所以其余两个基线跳跃会在未满的 box1 内的这两条线下方创建一个空白空间。如果您使用,\setbox1=\vbox{\unvbox1}
那么这个空白空间将被删除。
此外,下一个 box0 以其可变高度的线开始,因此即使你修正了 box1 的深度,也无法在我们的示例中保持第二行和第三行之间的统一基线距离。因此,这些问题的解决方案是
- 保持
\splittopskip
价值 10pt - 使用技巧
\penalty0
作为第一个元素和\vsplit0 to0pt
第一个拆分操作:它在第一行添加 topskip,因此第一个 baselineskip 正好距离顶部 10pt。 - 使用更精确计算的分割点作为 1 topskip 加 (n-1) baselineskips。
该代码不能解决您所提出的问题,但可以解决我提到的问题:
\parskip=7pt
\setbox0=\vbox{\penalty0 \input knuth\par}
\splittopskip=10pt
\setbox1=\vsplit0 to0pt
\dimen0=\splittopskip \advance\dimen0 by 1\baselineskip
\setbox1=\vsplit 0 to \dimen0
\setbox1=\vbox{\unvbox1}
\ifdim\dp1<\dp\strutbox
\dp1=\dp\strutbox
\fi
\box1
\box0
\bye
现在,我们可以解决您的问题了。您可以\par
在本地重新定义何时将数据读入 box0,并检查最后的惩罚是否为 10001。如果这是真的,那么您可以添加\vskip\parskip
:
\parskip=7pt
\def\redefpar{\def\par{\endgraf \penalty10001 \penalty0 }}
\setbox0=\vbox{\penalty0 \redefpar \input knuth\par}
\splittopskip=10pt
\setbox1=\vsplit0 to0pt
\dimen0=\splittopskip \advance\dimen0 by 1\baselineskip
\setbox1=\vsplit 0 to \dimen0
\setbox1=\vbox{\unvbox1
\ifnum\lastpenalty=10001 \penalty0 \vskip\parskip\fi}
\ifdim\dp1<\dp\strutbox
\dp1=\dp\strutbox
\fi
\box1
\box0
\bye
答案3
这很大程度上是基于 @wipet 的回答。我对此进行了一些实验,以下是我的版本,可以更好地重建垂直间距。
(pdftex
默认情况下具有 e-TeX 扩展,我在这里(不必要地)使用过一次)
\parskip=7pt
\def\redefpar{\def\par{\endgraf \penalty10001 \penalty0 }}
\setbox0=\vbox{\penalty0 \redefpar \input knuth\par}
\splittopskip=10pt
\setbox1=\vsplit0 to0pt
\dimen0=\splittopskip \advance\dimen0 by 1\baselineskip
\setbox1=\vsplit 0 to \dimen0
\dimen0\dp1
\setbox1=\vbox{\unvbox1
\ifnum\lastpenalty=10001 \penalty0 \vskip\parskip\fi
}
\box1
\nointerlineskip
\kern-\dimen0
\vskip\dimexpr\baselineskip-\splittopskip\relax
\box0
\tracingoutput 1 %\showboxbreadth \maxdimen \showboxdepth \maxdimen
\bye
顺便说一下,我正在使用这个knuth.tex
文件
Thus, I came to the conclusion that the designer of a new system must not only
be the implementer and first large--scale user; the designer should also write
the first user manual.
The separation of any of these four components would have hurt \TeX\
significantly. If I had not participated fully in all these activities,
literally hundreds of improvements would never have been made, because I would
never have thought of them or perceived why they were important.
But a system cannot be successful if it is too strongly influenced by a single
person. Once the initial design is complete and fairly robust, the real test
begins as people with many different viewpoints undertake their own
experiments.
我从 ConTeXT 的 PDF 文档中复制了源代码,它以小字体显示。我不知道原始来源在哪里可以找到。