[luatex] Turning a string into a list of nodes.

Paul Isambert zappathustra at free.fr
Thu Nov 18 21:52:10 CET 2010

Hello all,

I've been trying to devise a Lua function which, when fed a string, returns a
list of nodes. The application I have in mind is line numbering, counting lines
in the post_linebreak_filter and adding an \hbox with the number to lines that
are a multiple of a given number. Everything should be done in Lua, and thus the
counter must be converted to a list of nodes. I.e. the following should be

mylist = string_tolist(tostring(linecounter))

And more generally (there might be other applications beside line numbering):

mylist = string_tolist("Blah blah.")

I've come up with the code below. It does exactly that (given it's fed ASCII,
for it's not supposed to be complete now), but I find it quite convoluted: it
analyzes the string and turns each character into a node. And discretionaries
can't be inserted.

And here come the questions: Have I missed a definitely simpler way to do what I
want? If not, any suggestion on the code below? Do you think I should
feature-request Taco for a hard-coded function, capitalizing on his upcoming
available time and bad conscience? :)


-- Turns a character into a node.
local function create_glyph (char, fnt)
  local glyph = font.getfont(fnt).characters[string.byte(char)]
  local glyph_node = node.new(37, 0)
  -- Could use glyph.name with PS fonts.
  glyph_node.char = string.byte(char)
  glyph_node.font = fnt
  glyph_node.lang = tex.language
  glyph_node.left, glyph_node.right = tex.lefthyphenmin, tex.righthyphenmin
  glyph_node.uchyph = tex.uchyph
  glyph_node.xoffset, glyph_node.yoffset = 0, 0
-- These produce an error message if one tries to set them.
--  glyph_node.width = glyph.width
--  glyph_node.height = glyph.height
--  glyph_node.depth = glyph.depth
  glyph_node.expansion_factor = glyph.expansion_factor
  return glyph_node

-- Creates a skip node.
local function create_skip (fnt)
  local skip = node.new(10, 0)
  local spec = node.new(47)
  spec.width = font.getfont(fnt).parameters.space
  spec.stretch = font.getfont(fnt).parameters.space_stretch
  spec.shrink = font.getfont(fnt).parameters.space_shrink
  spec.stretch_order = 0
  spec.shrink_order = 0
  skip.spec = spec
  return skip

-- Returns the first character of a string and
-- the rest of the string.
local function string_pop(str)
  	local first_char
  	local new_string = string.gsub(str, "(.)",
  	  function (match)
  	    first_char = match
  	    return ""
  	  end, 1)
  	return first_char, new_string

function string_tolist (str, fnt)
  -- The font is optional, the current one is used if none is given.
  local Font = fnt or font.current()
  local head, prev
  local char, str = string_pop(str)
  while char do
    local glyph
    if char == " " then
      glyph = create_skip(Font)
      glyph = create_glyph(char, Font)
    glyph.prev = prev
    if prev then
      prev.next = glyph
      head = glyph
    prev = glyph
    char, str = string_pop(str)
  -- How can one insert discretionaries in the list?
  for item in node.traverse(head) do
  for item in node.traverse(head) do
    if item.next then
      -- If the second argument isn't there, LuaTeX crashes.
      node.kerning(item, item.next)
  return head

More information about the luatex mailing list