如何仅显示段落的某些行?

如何仅显示段落的某些行?

通过阅读 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}

可以修改代码以仅输出指定的行。需要注意的是,拆除是从最底下的一行开始的。

相关内容