我有一些文本出现在宏中,如下所示:
\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