How to place a line break between every character in ConTeXt?

I have some text appearing in a macro, like this:

\printascolumn{This is some text.}


How can I place a line break between each letter or printed symbol, such that it appears like this?

T
h
i
s

i
s

s
o
m
e

t
e
x
t
.

-
Search for \handletokens –  Aditya Oct 27 '12 at 17:07

ConTeXt provides a macro \handletokens which passes each token to a command. You can use \handletokens to print text vertically (or along a path, as shown in the MetaFun manual). In the example below, I also horizontally align the letters, which gives a nicer output than the default left aligned letters.

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

\define[1]\printascolumn

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


which gives

-
+1 for using the right interface and aligning the letters:-) –  David Carlisle Oct 27 '12 at 20:38

Context may have something packaged, this just uses TeX primitives.

\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

-
Interestingly, this is the exact implementation of \handletokens in ConTeXt (except for different variable names, and a slightly different interface). –  Aditya Oct 27 '12 at 20:33

While the approach with \handletokens has its merits, it operates on token level and will fail if you have, say, an hbox in the argument. (Try \printascolumn{foo \hbox{bar} baz} ...) This is true for math mode as well.

Luckily we can do the same on node level, avoiding all the expansion issues and whatnot. The only prerequisite is that we must know which nodes to insert a break after. For our purpose it will suffice if we handle nodes of type glyph (“characters”), hlist and vlist (boxes). We specify their corresponding ids (type int) in the table breakafter using the convenient table nodes.nodecodes that is created with every Context run.

Then we create an action, which is just the Context jargon for LuaTeX callbacks (more or less, anyways ...). This action is required to be accessible from _G (locals won’t work because of the implementation), so we assign them the namespace document. We add it to the list processors which in many respects works like the pre_linebreak_filter: receive the head of a node list, manipulate the list, return a list. Once registered this action can be toggled on and off through the functions nodes.enableaction() and nodes.disableaction().

Inside the action we walk the paragraph node list. Once we encounter a node whose type is set in the breakafter table, we append a -10k penalty and a glue. This triggers a carriage return. Also, we must handle non-empty hboxes separately.[*] It is necessary to omit anything until the first glyph node. (For some obscure reason we can’t rely on the function node.first_glyph here, because it will return nil even though there are character nodes inside the box.)

One caveat: this solution as it is works on entire paragraphs only. To restrict it to some part of the paragraph list should be possible but would complicate things quite a bit.

[*] Hint: in Context the “processors” action is not totally equivalent to the pre_linebreak_filter: namely because it is applied to the hpack_filter callback as well. Under other circumstances this does mess up the iteration order quite thoroughly but here it can be considered a benefit for we don’t need to handle node lists recursively.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%                          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 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)
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
end

document.glyph_breaking = glyph_breaking

--- 6. register the glyph breaking as an action and activate it.

commands.start_as_column = function ( )
end
commands.stop_as_column = function ( )
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

-