如何在 ConTeXt 中的每个字符之间添加换行符?

如何在 ConTeXt 中的每个字符之间添加换行符?

我有一些文本出现在宏中,如下所示:

\printascolumn{This is some text.}

我怎样才能在每个字母或印刷符号之间放置一个换行符,以使其看起来像这样?

T
h
i
s

i
s

s
o
m
e

t
e
x
t
.

答案1

ConTeXt 提供了一个宏\handletokens,它将每个标记传递给一个命令。您可以使用\handletokens它来垂直打印文本(或沿着路径打印,如 MetaFun 手册中所示)。在下面的示例中,我还水平对齐了字母,这比默认的左对齐字母输出效果更好。

\define[1]\AddLine{#1\crlf}

\define[1]\printascolumn
    {\framed[align=middle, frame=off]{\handletokens #1 \with\AddLine}}

\starttext
\printascolumn{This is some text}
\stoptext

这使

在此处输入图片描述

答案2

上下文可能有一些打包的东西,这只是使用 TeX 原语。

在此处输入图片描述

\def\printascolumn#1{%
  \printa#1\relax}

\def\printa{\futurelet\tmp\printb}

\def\printb{%
\ifx\tmp\relax\printe\fi
\hbox{\tmp}%
\ifx\tmp\spchar
   \expandafter\printc
\else
   \expandafter\printd
\fi
  }

\def\printe#1\fi#2\fi{\fi}

\def\tmp#1{%
\let\spchar= #1%
\def\printc#1{\printa}}
\tmp{ }

\def\printd#1{\printa}

\printascolumn{This is some text.}

\bye

答案3

虽然使用的方法\handletokens有其优点,但它在 token 级别进行操作,如果参数中有一个 hbox,它将失败。(尝试\printascolumn{foo \hbox{bar} baz}......)这对于数学模式也是如此。

幸运的是,我们可以在节点级别执行相同的操作,从而避免所有扩展问题和其他问题。唯一的先决条件是我们必须知道在哪些节点后插入中断。对于我们的目的,如果我们处理以下类型的节点就足够了 字形(“人物”),列表虚拟列表 (盒子)。我们使用每次运行 Context 时创建的breakafter便捷表在表中指定它们对应的 ID(类型为 int) 。nodes.nodecodes

然后我们创建一个行动,这只是 LuaTeX 回调的上下文术语(或多或少,无论如何......)。此操作需要从_G当地人将因实施而无法工作),因此我们为它们分配命名空间document。我们将其添加到列表中,processors该列表在很多方面都像 一样工作:接收节点列表的头部、操作列表、返回列表。注册后,可以通过函数和pre_linebreak_filter来打开和关闭此操作。nodes.enableaction()nodes.disableaction()

在操作中,我们遍历段落节点列表。一旦遇到表中设置了类型的节点breakafter ,我们就会附加 -10k 惩罚和粘合。这会触发回车符。此外,我们必须单独处理非空的水平框。[*] 有必要省略第一个字形节点之前的所有内容。(出于某些晦涩的原因,我们不能依赖这里的函数 ,因为 即使框内有字符节点,node.first_glyph它也会返回。)nil

需要注意的是:此解决方案仅适用于整个段落。将其限制在段落列表的某个部分应该是可能的,但会使事情变得相当复杂。

[*] 提示:在上下文中,“处理器”操作是 不完全相同 因为pre_linebreak_filter它是 hpack_filter也适用于回调。在其他情况下,这确实会彻底打乱迭代顺序,但在这里这可以被视为一种好处,因为我们不需要递归处理节点列表。

演示

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%                          implementation
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\startluacode
  --- 1. namespace, required for callback insertion
  document = document or { }

  --- 2. reserve some locals for convenience and a marginal performance
  ---    gain
  local copy_node       = node.copy
  local count_nodes     = node.count
  local traverse_nodes  = node.traverse
  local new_node        = node.new
  local find_tail       = node.tail

  local tasks     = nodes.tasks
  local nodecodes = nodes.nodecodes

  --- 3. prepare our breaks. a break consists of (1) a penalty
  ---    and (2) an infinite horizontal glue.
  local generic_fill                = new_node(nodecodes.glue)
  generic_fill.spec                 = new_node(nodecodes.gluespec)
  generic_fill.spec.width           = 0
  generic_fill.spec.stretch         = 2^16 --> 1pt
  generic_fill.spec.shrink          = 0
  generic_fill.spec.stretch_order   = 2
  local generic_break               = new_node(nodecodes.penalty)
  generic_break.penalty             = -10^4

  --- 4. which nodes do we intend to inserts breaks after?
  local breakafter = {
    [nodecodes.glyph] = true,
    [nodecodes.hlist] = true,
    [nodecodes.vlist] = true,
  }

  --- 5. the main “action” (Contextese for callback). nothing special:
  ---    we hop over the node list prior to linebreaking and ensure that
  ---    every node of type “glyph”, as long as it is not the last node
  ---    in the list, is followed by a break.
  local glyph_breaking glyph_breaking = function (head)
    for n in traverse_nodes(head) do
      local id, len = n.id, count_nodes(nodecodes.glyph, n)
      if id == nodecodes.hlist and n.list then -- unbox
        local hd = n.list
        while hd and hd.id ~= nodecodes.glyph do
          hd = hd.next
        end
        if hd then
          local tl = find_tail(hd)
          tl.next, hd.prev         = n.next, n.prev
          n.prev.next, n.next.prev = hd, tl
          n = tl
        end
      end
      if breakafter[id] then
        local nxt = n.next
        if nxt then
          local brk  = copy_node(generic_break)
          local fill = copy_node(generic_fill)
          n.next, brk.prev    = brk, n
          brk.next, fill.prev = fill, brk
          fill.next, nxt.prev = nxt, fill
        end
      end
    end
    return head
  end

  document.glyph_breaking = glyph_breaking

  --- 6. register the glyph breaking as an action and activate it.
  tasks.appendaction ("processors", "before", "document.glyph_breaking")
  tasks.disableaction("processors",           "document.glyph_breaking")

  commands.start_as_column = function ( )
    tasks.enableaction("processors", "document.glyph_breaking")
  end
  commands.stop_as_column = function ( )
    tasks.disableaction("processors", "document.glyph_breaking")
  end
\stopluacode

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%                              macros
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 7. user interface commands. these work on per-paragraph base only as
%%    the callback is only triggered before hyphenation.
\def\startascolumn{\par\bgroup\dontcomplain\ctxcommand{start_as_column()}}
\def\stopascolumn{\par\ctxcommand{stop_as_column()}\egroup}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%                              testing
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\starttext
  Normal text.
  \startascolumn
    foo \hbox{bar \framed[corner=00,align=right]{baz}} xyzzy
    \m{\pi(x)\sim\frac{x}{\ln x}.} %% <== looks funny ...
  \stopascolumn
  Normal text.

  % \startascolumn
  %   \input knuth
  % \stopascolumn
\stoptext

相关内容