通过阅读 Knuth 的TeX 书(章节14 TeX 如何将段落分成行),我理解整个段落都被读入然后通过“换行算法”以最佳方式进行“处理”(第 91 页):
排版系统的主要职责之一是将长序列的单词分成适当大小的单独行。... TeX 以一种有趣的方式选择断点,将每个段落作为一个整体考虑;段落的结束词实际上会影响第一行的外观。
因此,我假设它在对每个段落进行优化后,逐行刷新行。是否可以中断这种刷新行并仅输出段落的特定行?我猜,这几乎类似于跨页分段的方式。
例如,通过定义showlines
一个可以按以下方式使用的环境(比如说):
\begin{showlines}[lines=2]
One of typesetting system's chief duties is to take long sequences
of words and to break it up into individual lines of the appropriate size.
\TeX\ chooses breakpoints in an interesting way that considers each paragraph
in its entirety; the closing words of a paragraph can actually influence
the appearance of the first line.
\end{showlines}
并且 TeX 只会输出传统输出的前两行(可能会丢弃其余部分):
注意:这里需要注意的是,在进行换行优化后,即使在使用连字符的情况下,也要保留排版,因为连接应该与行的输出有关。当然,如果有其他方法可以做到这一点(例如,逐行进行可能不太理想的换行),那么这种方法也可以。
作为奖励,不仅允许展示第一n
行如上例所示(通过[lines=n]
),但允许使用如下符号显示段落中的任意行组合:[lines={1,4-5}]
或[lines={-2}]
最后两行。
答案1
缺少 Lua(La)TeX 解决方案。不再有:
\documentclass{article}
\usepackage{luatexbase}
\usepackage{luacode}
\begin{luacode}
twolines = function ( head )
local orig_head = head
local linecounter = 0
while head do
if head.id == 0 then
linecounter = linecounter + 1
end
if linecounter > 1 then
head.next = nil
return true
end
head = head.next
end
return true
end
luatexbase.add_to_callback("post_linebreak_filter",twolines,"twolines")
\end{luacode}
\begin{document}
\hsize 4in
A wonderful serenity has taken possession of my entire soul, like these sweet mornings of spring which I enjoy with my
whole heart. I am alone, and feel the charm of existence in this spot, which was created for the bliss of souls like mine. I am
so happy, my dear friend, so absorbed in the exquisite sense of mere tranquil existence, that I neglect my talents. I should be
incapable of drawing a single stroke at the present moment; and yet I feel that I never was a greater artist than now.
\end{document}
这添加了一个回调函数(twolines
),它post_linebreak_filter
计算在段落中找到的(节点 id 0)的数量hlists
,并删除hlist
段落中第二个节点之后的所有内容(a vlist
- 这就是orig_head
指向的 - 在此示例中不需要)。
缺点:
- 它适用于每个段落。为了避免这种情况,请仅在需要时激活回调。请参阅chickenize 包作者:Arno Trautmann。
- 它存在内存泄漏。第二个节点之后的节点
hlist
仍在结构中。需要调用node.free(...)
以从内存中删除节点。请参阅参考手册(第 4 章,节点库)。 - 它不适用于 iTeX [叮咚]。
编辑:这是一个完整的解决方案,包括环境:
\documentclass{article}
\usepackage{luatexbase}
\usepackage{luacode}
\begin{luacode}
twolines = function ( head )
local linecounter = 0
while head do
if head.id == 0 then
linecounter = linecounter + 1
end
if linecounter >= showlinescount then
local lastline=head
head = head.next
while head do
local head_next = head.next
node.free(head)
head = head_next
end
lastline.next = nil
return true
end
head = head.next
end
return true
end
\end{luacode}
\newenvironment{showlines}[1]
{\directlua{showlinescount = tonumber('#1')
luatexbase.add_to_callback("post_linebreak_filter",twolines,"twolines")}}
{\par
\directlua{luatexbase.remove_from_callback("post_linebreak_filter","twolines")}}
\begin{document}
\hsize 4in
\newcommand\sample{A wonderful serenity has taken possession of my entire soul, like these sweet mornings
of spring which I enjoy with my whole heart. I am alone, and feel the charm of existence in this spot,
which was created for the bliss of souls like mine. I am so happy, my dear friend, so absorbed in the exquisite
sense of mere tranquil existence, that I neglect my talents. I should be incapable of drawing a single
stroke at the present moment; and yet I feel that I never was a greater artist than now.}
\begin{showlines}{3}
\sample
\end{showlines}
\sample
\end{document}
这仍然无法与 iTeX [叮铃铃] 一起使用。但它不再有内存泄漏,并且它提供了一个showlines
环境。
答案2
当 TeX 将段落分成几行时,它会为每一行构建一个水平盒子。这堆水平盒子可以放在一个垂直盒子中(这样实际上就有了第一个水平盒子),然后使用该操作一次分析一个水平盒子\lastbox
。
下面的代码深受以下影响TeX 按主题分类,第 5.9.6 节。
\documentclass{article}
\usepackage{lipsum}
\newbox\linebox
\newcount\mycount
\def\processline{% Underline every second line
\global\advance\mycount by 1
\ifodd\mycount
\underline{\box\linebox}%
\else
\box\linebox
\fi}
\def\splitlines{%
\setbox\linebox\lastbox
\ifvoid\linebox
\noindent\ignorespaces
\else
\unskip\unpenalty
\begingroup\splitlines\space\endgroup
\processline
\fi}
\long\def\doit#1{\vbox{#1\par\splitlines}}
\begin{document}
\doit{\lipsum[1]}
\end{document}
您可以重新定义\processline
以满足您的需要。例如,它可能会根据条件丢弃行或\unhbox
进行进一步更改。按数字选择行现在只需遍历水平框并比较数字即可。可能需要两次剖析垂直框。
答案3
以下代码输出参数中指定的行数,前提是所有行具有相同的高度(更准确地说,基线是均匀分布的):
\documentclass[a4paper]{article}
\makeatletter
\newbox\wernerboxa
\newbox\wernerboxb
\newenvironment{showlines}[1]
{\vbadness=\@M\def\how@many{#1}\par
\setbox\wernerboxa=\vbox\bgroup}
{\egroup\setbox\wernerboxb=\vsplit\wernerboxa to\how@many\baselineskip
\vbox{\unvbox\wernerboxb}}
\makeatother
\usepackage{lipsum}
\begin{document}
\begin{showlines}{5}
\lipsum
\end{showlines}
\end{document}
递归地重复该\vsplit
操作可以解决显示指定行列表的问题。
让我们看看另一种可以应对一行中有更高物体的情况的方法。
如果段落中没有花哨的东西,只有纯文本,则修改我的答案会为段落的每一行添加下划线;实际上,规则是由宏添加的\add@rule
,因此可以对其进行修改,使其仅在满足某些条件时才绘制规则;最终的行数可在宏中找到\how@many
。这是自下而上的;添加检查某行是否必须加下划线不应该太复杂。
\documentclass[a4paper]{article}
\usepackage{lipsum}
\makeatletter
\newif\ifboxended
\newbox\wernerbox
\def\add@rule{\kern0.8\p@\hrule\kern-1.2\p@}
\newenvironment{werner}
{\par\setbox\wernerbox=\vbox\bgroup}
{\par\xdef\how@many{\the\prevgraf}\egroup
\message{The box has \how@many\space lines}%
\underlinevbox}
\def\underlinevbox{\begingroup\global\setbox\@ne=\box\voidb@x
\global\boxendedfalse
\setbox\z@=\copy\wernerbox\relax\dounderlinevbox}
\def\dounderlinevbox{%
\setbox\z@=\vbox{\unvbox\z@
\ifcase\lastnodetype
% char node (can't remove)
\or
% hlist node
\setbox\tw@=\lastbox
\def\next{\global\setbox\@ne=\vbox{\box\tw@\add@rule\unvbox\@ne}}%
\or
% vlist node
\setbox\tw@=\lastbox
\def\next{\global\setbox\@ne=\vbox{\box\tw@\add@rule\unvbox\@ne}}%
\or
% rule node (can't remove)
\or
% ins node (can't remove)
\or
% mark node (can't remove)
\or
% adjust node (can't remove)
\or
% ligature node (can't happen)
\or
% disc node (can't happen)
\or
% whatsit node (can't remove)
\or
% math node (can't remove)
\or
% glue node
\skip@=\lastskip\unskip
\def\next{\global\setbox\@ne=\vbox{\vskip\skip@\unvbox\@ne}}%
\or
% kern node
\dimen@=\lastkern\unkern
\def\next{\global\setbox\@ne=\vbox{\kern\dimen@\unvbox\@ne}}%
\or
% penalty node
\count@=\lastpenalty\unpenalty
\def\next{\global\setbox\@ne=\vbox{\penalty\count@\unvbox\@ne}}%
\or
% unset node (can't happen)
\or
% math mode node (can't remove)
\else
% empty list
\def\next{\global\boxendedtrue}
\fi
\next}
\ifboxended
\def\next{\unvbox\@ne\endgroup}
\else
\let\next\dounderlinevbox
\fi
\next}
\makeatother
\begin{document}
\begin{werner}
\lipsum[2]
\end{werner}
\end{document}
可以修改代码以仅输出指定的行。需要注意的是,拆除是从最底下的一行开始的。