Skip to content

global node

The node library contains functions that facilitate dealing with (lists of) nodes and their values. They allow you to create, alter, copy, delete, and insert LuaTEX node objects, the core objects within the typesetter.

LuaTEX nodes are represented in Lua as userdata with the metadata type luatex.node. The various parts within a node can be accessed using named fields.

Each node has at least the three fields next, id, and subtype:

  • The next field returns the userdata object for the next node in a linked list of nodes, or nil, if there is no next node.
  • The id indicates TEX’s ‘node type’. The field id has a numeric value for efficiency reasons, but some of the library functions also accept a string value instead of id.
  • The subtype is another number. It often gives further information about a node of a particular id, but it is most important when dealing with ‘whatsits’, because they are differentiated solely based on their subtype.

The other available fields depend on the id (and for ‘whatsits’, the subtype) of the node.

Support for unset (alignment) nodes is partial: they can be queried and modified from Lua code, but not created.

Nodes can be compared to each other, but: you are actually comparing indices into the node memory. This means that equality tests can only be trusted under very limited conditions. It will not work correctly in any situation where one of the two nodes has been freed and/or reallocated: in that case, there will be false positives.

At the moment, memory management of nodes should still be done explicitly by the user. Nodes are not ‘seen’ by the Lua garbage collector, so you have to call the node freeing functions yourself when you are no longer in need of a node (list). Nodes form linked lists without reference counting, so you have to be careful that when control returns back to LuaTEX itself, you have not deleted nodes that are still referenced from a next pointer elsewhere, and that you did not create nodes that are referenced more than once. Normally the setters and getters handle this for you.

There are statistics available with regards to the allocated node memory, which can be handy for tracing.

😱 Types incomplete or incorrect? 🙏 Please contribute!


methods


node.effective_glue


function node.effective_glue(
  glue: Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
},
  parent: (HlistNode|VlistNode),
  round: boolean?
) ->  (number|integer)?
@param round - When you pass true as third argument the value will be rounded.

Return the effective width of a glue node.

Reference:

node.uses_font


function node.uses_font(
  n: Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
},
  font: integer
) ->  boolean

Return true when a glyph or disc node references that font.

Reference:

node.is_node


function node.is_node(item: any) -> t (false|integer)

Return a number (the internal index of the node) if the argument is a userdata object of type node and false when no node is passed.

Reference:

node.types


function node.types() ->  table<integer,string>

Return a table that maps node id numbers to node type strings, providing an overview of the possible top-level id types.

Example:

assert.same(
  node.types(),
  {
    [0] = "hlist",
    [1] = "vlist",
    [2] = "rule",
    [3] = "ins",
    [4] = "mark",
    [5] = "adjust",
    [6] = "boundary",
    [7] = "disc",
    [8] = "whatsit",
    [9] = "local_par",
    [10] = "dir",
    [11] = "math",
    [12] = "glue",
    [13] = "kern",
    [14] = "penalty",
    [15] = "unset",
    [16] = "style",
    [17] = "choice",
    [18] = "noad",
    [19] = "radical",
    [20] = "fraction",
    [21] = "accent",
    [22] = "fence",
    [23] = "math_char",
    [24] = "sub_box",
    [25] = "sub_mlist",
    [26] = "math_text_char",
    [27] = "delim",
    [28] = "margin_kern",
    [29] = "glyph",
    [30] = "align_record",
    [31] = "pseudo_file",
    [32] = "pseudo_line",
    [33] = "page_insert",
    [34] = "split_insert",
    [35] = "expr_stack",
    [36] = "nested_list",
    [37] = "span",
    [38] = "attribute",
    [39] = "glue_spec",
    [40] = "attribute_list",
    [41] = "temp",
    [42] = "align_stack",
    [43] = "movement_stack",
    [44] = "if_stack",
    [45] = "unhyphenated",
    [46] = "hyphenated",
    [47] = "delta",
    [48] = "passive",
    [49] = "shape",
  }
)

Reference:

node.whatsits


function node.whatsits() ->  table<integer,string>

Provides a table of subtype mappings.

TeX’s ‘whatsits’ all have the same id. The various subtypes are defined by their subtype fields.

Example:

assert.same(node.whatsits(), {
  [0] = "open",
  [1] = "write",
  [2] = "close",
  [3] = "special",
  [4] = "late_special",
  [7] = "save_pos",
  [8] = "late_lua",
  [9] = "user_defined",
  [16] = "pdf_literal",
  [17] = "pdf_late_literal",
  [18] = "pdf_refobj",
  [19] = "pdf_annot",
  [20] = "pdf_start_link",
  [21] = "pdf_end_link",
  [22] = "pdf_dest",
  [23] = "pdf_action",
  [24] = "pdf_thread",
  [25] = "pdf_start_thread",
  [26] = "pdf_end_thread",
  [27] = "pdf_thread_data",
  [28] = "pdf_link_data",
  [29] = "pdf_colorstack",
  [30] = "pdf_setmatrix",
  [31] = "pdf_save",
  [32] = "pdf_restore",
  [33] = "pdf_link_state",
})

Reference:

node.id


function node.id(type: NodeTypeName) ->  NodeTypeId

Convert a single type name to its internal numeric representation.

Example:

local function equals(name, id)
  assert.equals(node.id(name), id)
end

equals("hlist", 0)
equals("vlist", 1)
equals("rule", 2)
equals("ins", 3)
equals("mark", 4)
equals("adjust", 5)
equals("boundary", 6)
equals("disc", 7)
equals("whatsit", 8)
equals("local_par", 9)
equals("dir", 10)
equals("math", 11)
equals("glue", 12)
equals("kern", 13)
equals("penalty", 14)
equals("unset", 15)
equals("style", 16)
equals("choice", 17)
equals("noad", 18)
equals("radical", 19)
equals("fraction", 20)
equals("accent", 21)
equals("fence", 22)
equals("math_char", 23)
equals("sub_box", 24)
equals("sub_mlist", 25)
equals("math_text_char", 26)
equals("delim", 27)
equals("margin_kern", 28)
equals("glyph", 29)
equals("align_record", 30)
equals("pseudo_file", 31)
equals("pseudo_line", 32)
equals("page_insert", 33)
equals("split_insert", 34)
equals("expr_stack", 35)
equals("nested_list", 36)
equals("span", 37)
equals("attribute", 38)
equals("glue_spec", 39)
equals("attribute_list", 40)
equals("temp", 41)
equals("align_stack", 42)
equals("movement_stack", 43)
equals("if_stack", 44)
equals("unhyphenated", 45)
equals("hyphenated", 46)
equals("delta", 47)
equals("passive", 48)
equals("shape", 49)
Reference:

node.type


function node.type(n: NodeTypeId) ->  (NodeTypeName|"node")?
@param n - The numeric node type id.

Convert an internal numeric node type representation to an external node type string.

If the argument is a number, then the type function converts an internal numeric representation to an external string representation. Otherwise, it will return the string node if the object represents a node, and nil otherwise.

assert.equals(node.type(29), "glyph")
assert.equals(node.type(node.id("glyph")), "glyph")
assert.is_nil(node.type("xxx"))

Reference:

node.subtype


function node.subtype(whatsit_type_name: WhatsitTypeName) -> whatsit_type_id WhatsitTypeId

Convert a single whatsit name to its internal numeric representation (subtype).

Example:

local function equals(name, id)
  assert.equals(node.subtype(name), id)
end

equals("open", 0)
equals("write", 1)
equals("close", 2)
equals("special", 3)
equals("late_special", 4)
equals("save_pos", 7)
equals("late_lua", 8)
equals("user_defined", 9)
equals("pdf_literal", 16)
equals("pdf_late_literal", 17)
equals("pdf_refobj", 18)
equals("pdf_annot", 19)
equals("pdf_start_link", 20)
equals("pdf_end_link", 21)
equals("pdf_dest", 22)
equals("pdf_action", 23)
equals("pdf_thread", 24)
equals("pdf_start_thread", 25)
equals("pdf_end_thread", 26)
equals("pdf_thread_data", 27)
equals("pdf_link_data", 28)
equals("pdf_colorstack", 29)
equals("pdf_setmatrix", 30)
equals("pdf_save", 31)
equals("pdf_restore", 32)
equals("pdf_link_state", 33)

Reference:

node.fields


function node.fields(
  id: NodeTypeId,
  subtype: number?
) ->  { [number]: string }

Return an array of valid field names for a particular type of node.


Example:

local types = {}
for type_id, type in pairs(node.types()) do
  if type_id ~= 8 then
    types[type] = {}
    for field_id, field in pairs(node.fields(type_id)) do
      types[type][field_id] = field
    end
  end
end

assert.same(types, {
  accent = {
    "id",
    "subtype",
    "attr",
    "nucleus",
    "sub",
    "sup",
    "accent",
    "bot_accent",
    "top_accent",
    "overlay_accent",
    "fraction",
    [-1] = "prev",
    [0] = "next",
  },
  adjust = {
    "id",
    "subtype",
    "attr",
    "head",
    [-1] = "prev",
    [0] = "next",
  },
  align_record = { "id", "subtype", [-1] = "prev", [0] = "next" },
  align_stack = { "id", "subtype", [-1] = "prev", [0] = "next" },
  attribute = { "id", "number", "value", [0] = "next" },
  attribute_list = { "id", [0] = "next" },
  boundary = {
    "id",
    "subtype",
    "attr",
    "value",
    [-1] = "prev",
    [0] = "next",
  },
  choice = {
    "id",
    "subtype",
    "attr",
    "display",
    "text",
    "script",
    "scriptscript",
    [-1] = "prev",
    [0] = "next",
  },
  delim = {
    "id",
    "subtype",
    "attr",
    "small_fam",
    "small_char",
    "large_fam",
    "large_char",
    [-1] = "prev",
    [0] = "next",
  },
  delta = { "id", "subtype", [-1] = "prev", [0] = "next" },
  dir = {
    "id",
    "subtype",
    "attr",
    "dir",
    "level",
    [-1] = "prev",
    [0] = "next",
  },
  disc = {
    "id",
    "subtype",
    "attr",
    "pre",
    "post",
    "replace",
    "penalty",
    [-1] = "prev",
    [0] = "next",
  },
  expr_stack = { "id", "subtype", [-1] = "prev", [0] = "next" },
  fence = {
    "id",
    "subtype",
    "attr",
    "delim",
    "italic",
    "height",
    "depth",
    "options",
    "class",
    [-1] = "prev",
    [0] = "next",
  },
  fraction = {
    "id",
    "subtype",
    "attr",
    "width",
    "num",
    "denom",
    "left",
    "right",
    "middle",
    "fam",
    "options",
    [-1] = "prev",
    [0] = "next",
  },
  glue = {
    "id",
    "subtype",
    "attr",
    "leader",
    "width",
    "stretch",
    "shrink",
    "stretch_order",
    "shrink_order",
    [-1] = "prev",
    [0] = "next",
  },
  glue_spec = {
    "id",
    "width",
    "stretch",
    "shrink",
    "stretch_order",
    "shrink_order",
    [0] = "next",
  },
  glyph = {
    "id",
    "subtype",
    "attr",
    "char",
    "font",
    "lang",
    "left",
    "right",
    "uchyph",
    "components",
    "xoffset",
    "yoffset",
    "width",
    "height",
    "depth",
    "expansion_factor",
    "data",
    [-1] = "prev",
    [0] = "next",
  },
  hlist = {
    "id",
    "subtype",
    "attr",
    "width",
    "depth",
    "height",
    "dir",
    "shift",
    "glue_order",
    "glue_sign",
    "glue_set",
    "head",
    [-1] = "prev",
    [0] = "next",
  },
  hyphenated = { "id", "subtype", [-1] = "prev", [0] = "next" },
  if_stack = { "id", "subtype", [-1] = "prev", [0] = "next" },
  ins = {
    "id",
    "subtype",
    "attr",
    "cost",
    "depth",
    "height",
    "spec",
    "head",
    [-1] = "prev",
    [0] = "next",
  },
  kern = {
    "id",
    "subtype",
    "attr",
    "kern",
    "expansion_factor",
    [-1] = "prev",
    [0] = "next",
  },
  local_par = {
    "id",
    "subtype",
    "attr",
    "pen_inter",
    "pen_broken",
    "dir",
    "box_left",
    "box_left_width",
    "box_right",
    "box_right_width",
    [-1] = "prev",
    [0] = "next",
  },
  margin_kern = {
    "id",
    "subtype",
    "attr",
    "width",
    "glyph",
    [-1] = "prev",
    [0] = "next",
  },
  mark = {
    "id",
    "subtype",
    "attr",
    "class",
    "mark",
    [-1] = "prev",
    [0] = "next",
  },
  math = {
    "id",
    "subtype",
    "attr",
    "surround",
    "width",
    "stretch",
    "shrink",
    "stretch_order",
    "shrink_order",
    [-1] = "prev",
    [0] = "next",
  },
  math_char = {
    "id",
    "subtype",
    "attr",
    "fam",
    "char",
    [-1] = "prev",
    [0] = "next",
  },
  math_text_char = {
    "id",
    "subtype",
    "attr",
    "fam",
    "char",
    [-1] = "prev",
    [0] = "next",
  },
  movement_stack = { "id", "subtype", [-1] = "prev", [0] = "next" },
  nested_list = { "id", "subtype", [-1] = "prev", [0] = "next" },
  noad = {
    "id",
    "subtype",
    "attr",
    "nucleus",
    "sub",
    "sup",
    [-1] = "prev",
    [0] = "next",
  },
  page_insert = {
    "id",
    "subtype",
    "height",
    "last_ins_ptr",
    "best_ins_ptr",
    "width",
    "stretch",
    "shrink",
    "stretch_order",
    "shrink_order",
    [-1] = "prev",
    [0] = "next",
  },
  passive = { "id", "subtype", [-1] = "prev", [0] = "next" },
  penalty = {
    "id",
    "subtype",
    "attr",
    "penalty",
    [-1] = "prev",
    [0] = "next",
  },
  pseudo_file = { "id", "subtype", [-1] = "prev", [0] = "next" },
  pseudo_line = { "id", "subtype", [-1] = "prev", [0] = "next" },
  radical = {
    "id",
    "subtype",
    "attr",
    "nucleus",
    "sub",
    "sup",
    "left",
    "degree",
    "width",
    "options",
    [-1] = "prev",
    [0] = "next",
  },
  rule = {
    "id",
    "subtype",
    "attr",
    "width",
    "depth",
    "height",
    "dir",
    "index",
    "left",
    "right",
    [-1] = "prev",
    [0] = "next",
  },
  shape = { "id", "subtype", [-1] = "prev", [0] = "next" },
  span = { "id", "subtype", [-1] = "prev", [0] = "next" },
  split_insert = {
    "id",
    "subtype",
    "height",
    "last_ins_ptr",
    "best_ins_ptr",
    "broken_ptr",
    "broken_ins",
    [-1] = "prev",
    [0] = "next",
  },
  style = {
    "id",
    "subtype",
    "attr",
    "style",
    [-1] = "prev",
    [0] = "next",
  },
  sub_box = {
    "id",
    "subtype",
    "attr",
    "head",
    [-1] = "prev",
    [0] = "next",
  },
  sub_mlist = {
    "id",
    "subtype",
    "attr",
    "head",
    [-1] = "prev",
    [0] = "next",
  },
  temp = { "id", "subtype", [-1] = "prev", [0] = "next" },
  unhyphenated = { "id", "subtype", [-1] = "prev", [0] = "next" },
  unset = {
    "id",
    "subtype",
    "attr",
    "width",
    "depth",
    "height",
    "dir",
    "shrink",
    "glue_order",
    "glue_sign",
    "stretch",
    "span",
    "head",
    [-1] = "prev",
    [0] = "next",
  },
  vlist = {
    "id",
    "subtype",
    "attr",
    "width",
    "depth",
    "height",
    "dir",
    "shift",
    "glue_order",
    "glue_sign",
    "glue_set",
    "head",
    [-1] = "prev",
    [0] = "next",
  },
})

If you want to get the valid fields for a “whatsit”, you have to supply the second argument also. In other cases, any given second argument will be silently ignored.

Reference:

node.has_field


function node.has_field(
  n: Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
},
  field: string
) -> t boolean

Return a boolean that is only true if n is actually a node, and it has the field.

Example:

local glyph = node.new("glyph")

for _, field in ipairs({
  "prev",
  "next",
  "id",
  "subtype",
  "attr",
  "char",
  "font",
  "lang",
  "left",
  "right",
  "uchyph",
  "components",
  "xoffset",
  "yoffset",
  "width",
  "height",
  "depth",
  "expansion_factor",
  "data",
}) do
  assert.is_true(node.has_field(glyph, field))
end

  assert.is_false(node.has_field(glyph, "xxx"))

Reference:

@see node.direct.has_field

node.new


function node.new(
  id: (integer|NodeTypeName),
  subtype: (integer|string)?
) ->  Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
}

Create a new node.

All its fields are initialized to either zero or nil except for id and subtype. Instead of numbers you can also use strings (names). If you create a new whatsit node the second argument is required. As with all node functions, this function creates a node at the TeX level.

Example:

for type_id, node_type in pairs(node.types()) do
  if node_type == "whatsit" then
    for subtype_id, subtype in pairs(node.whatsits()) do
      local n = node.new(node_type, subtype)
      assert.equals(n.id, type_id)
      assert.equals(n.subtype, subtype_id)
    end
  else
    local n = node.new(node_type)
    assert.equals(n.id, type_id)
  end
end

Reference:

@see node.direct.new

node.free


function node.free(n: Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
}) -> next Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
}

Free the TeX memory allocated for node n and return the next field of the freed node.

Be careful: no checks are done on whether this node is still pointed to from a register or some next field: it is up to you to make sure that the internal data structures remain correct.

Example:

local n1 = node.new("glyph")
local n2 = node.new("glyph")
n1.next = n2
assert.equals(node.free(n1), n2)

Reference:

node.flush_node


function node.flush_node(n: Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
})

Free the TeX memory allocated for the specified node and return nothing.

Reference:

node.flush_list


function node.flush_list(n: Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
})

Free the TeX memory allocated for a list of nodes.

Be careful: no checks are done on whether any of these nodes is still pointed to from a register or some next field: it is up to you to make sure that the internal data structures remain correct.

Reference:

node.copy


function node.copy(n: Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
}) -> m Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
}

Create a deep copy of node n, including all nested lists as in the case of a hlist or vlist node. Only the next field is not copied.

Reference:

node.copy_list


function node.copy_list(
  n: Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
},
  m: Node?
) -> m Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
}

Create a deep copy of the node list that starts at node n.

If m is also given, the copy stops just before node m.

Note that you cannot copy attribute lists this way. However, there is normally no need to copy attribute lists as when you do assignments to the attr field or make changes to specific attributes, the needed copying and freeing takes place automatically.

Reference:

node.prev


function node.prev(n: Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
}) -> m Node?

Return the node preceding the given node, or nil if there is no such node.

Reference:

node.next


function node.next(n: Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
}) -> m Node?

Return the node following the given node, or nil if there is no such node.

Reference:

node.current_attr


function node.current_attr() -> m Node?

Return the currently active list of attributes, if there is one.

The intended usage of current_attr is as follows:

local x1 = node.new("glyph")
x1.attr = node.current_attr()
local x2 = node.new("glyph")
x2.attr = node.current_attr()

or:

local x1 = node.new("glyph")
local x2 = node.new("glyph")
local ca = node.current_attr()
x1.attr = ca
x2.attr = ca

The attribute lists are ref counted and the assignment takes care of incrementing the refcount. You cannot expect the value ca to be valid any more when you assign attributes (using tex.setattribute) or when control has been passed back to TeX.

Note: this function is somewhat experimental, and it returns the actual attribute list, not a copy thereof. Therefore, changing any of the attributes in the list will change these values for all nodes that have the current attribute list assigned to them.

Reference:

node.hpack


function node.hpack(
  n: Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
},
  width: integer?,
  info: string?,
  dir: string?
)
 -> n Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
}
 -> badness integer

Create a new hlist by packaging the list that begins at node n into a horizontal box.

With only a single argument, this box is created using the natural width of its components. In the three argument form, info must be either additional or exactly, and width is the additional (\hbox spread) or exact (\hbox to) width to be used. The second return value is the badness of the generated box.

Caveat: there can be unexpected side-effects to this function, like updating some of the marks and \inserts. Also note that the content of h is the original node list n: if you call node.free(h) you will also free the node list itself, unless you explicitly set the list field to nil beforehand. And in a similar way, calling node.free(n) will invalidate h as well!

Reference:

node.vpack


function node.vpack(
  head: Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
},
  height: integer?,
  info: ("additional"|"exactly")?,
  dir: (DirectionSpecifier|DirectionSpecifierId)?
)
 -> new_head Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
}
 -> badness integer
@param height - The additional (\vbox spread) or exact (\vbox to) height to be used.

@param info - Must be either additional (\vbox spread) or exactly (\vbox to).

@return badness - The second return value is the badness of the generated box.

😱 Types incomplete or incorrect? 🙏 Please contribute!

Create a new vlist by packaging the list that begins at node head into a vertical box.

With only a single argument, this box is created using the natural height of its components.

Caveat: there can be unexpected side-effects to this function, like updating some of the marks and \inserts. Also note that the content of new_head is the original node list head: if you call node.free(new_head) you will also free the node list itself, unless you explicitly set the list field to nil beforehand. And in a similar way, calling node.free(head) will invalidate new_head as well!

Reference:

node.prepend_prevdepth


function node.prepend_prevdepth(
  n: Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
},
  prevdepth: integer
) -> new_prevdepth integer
@param n - vlist or hlist

Add the interlinespace to a line keeping the baselineskip and lineskip into account.

Reference:

node.dimensions


function node.dimensions(
  n: Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
},
  dir: DirectionSpecifier?
)
 -> width integer
 -> height integer
 -> depth integer

@return width - scaled points

@return height - scaled points

@return depth - scaled points

😱 Types incomplete or incorrect? 🙏 Please contribute!

Calculate the natural in-line dimensions of the end of the node list starting at node n.

The return values are scaled points.

You need to keep in mind that this is one of the few places in TeX where floats are used, which means that you can get small differences in rounding when you compare the width reported by hpack with dimensions.

Reference:

Source: luatex-nodes.tex#L1490-L1546 * Corresponding C source code: lnodelib.c#L2767-L2812

node.dimensions


function node.dimensions(
  n: Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
},
  t: Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
},
  dir: DirectionSpecifier?
)
 -> width integer
 -> height integer
 -> depth integer
@param t - terminating node

@return width - scaled points

@return height - scaled points

@return depth - scaled points

😱 Types incomplete or incorrect? 🙏 Please contribute!

Calculate the natural in-line dimensions of the node list starting at node n and terminating just before node t.

Reference:

Source: luatex-nodes.tex#L1490-L1546 * Corresponding C source code: lnodelib.c#L2767-L2812

node.dimensions


function node.dimensions(
  glue_set: integer,
  glue_sign: integer,
  glue_order: integer,
  n: Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
},
  dir: DirectionSpecifier?
)
 -> width integer
 -> height integer
 -> depth integer

@return width - scaled points

@return height - scaled points

@return depth - scaled points

😱 Types incomplete or incorrect? 🙏 Please contribute!

Calculates the natural in-line dimensions of the end of the node list starting at node n.

This is an alternative format that starts with glue parameters as the first three arguments.

This calling method takes glue settings into account and is especially useful for finding the actual width of a sublist of nodes that are already boxed, for example in code like this, which prints the width of the space in between the a and b as it would be if \box0 was used as-is:

\setbox0 = \hbox to 20pt {a b}

\directlua{print (node.dimensions(
    tex.box[0].glue_set,
    tex.box[0].glue_sign,
    tex.box[0].glue_order,
    tex.box[0].head.next,
    node.tail(tex.box[0].head)
)) }

Reference:

Source: luatex-nodes.tex#L1490-L1546 * Corresponding C source code: lnodelib.c#L2838-L2880

node.dimensions


function node.dimensions(
  glue_set: integer,
  glue_sign: integer,
  glue_order: integer,
  d: integer,
  t: integer,
  dir: DirectionSpecifier?
)
 -> width integer
 -> height integer
 -> depth integer
@param d - The index number of the node in the memory table for direct access.

@param t - terminating node

@return width - scaled points

@return height - scaled points

@return depth - scaled points

😱 Types incomplete or incorrect? 🙏 Please contribute!

Calculate the natural in-line dimensions of the node list starting at node n and terminating just before node t.

This is an alternative format that starts with glue parameters as the first three arguments.

Reference:

Source: luatex-nodes.tex#L1490-L1546 * Corresponding C source code: lnodelib.c#L2767-L2812

node.rangedimensions


function node.rangedimensions(
  parent: Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
},
  first: Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
},
  last: Node?
)
 -> width integer
 -> height integer
 -> depth integer

@return width - scaled points

@return height - scaled points

@return depth - scaled points

😱 Types incomplete or incorrect? 🙏 Please contribute!

Calculate the natural in-line dimensions of the node list parent starting at node first and terminating just before node last.

This functions saves a few lookups in comparison to node.dimensions() and can be more convenient in some cases.

Reference:

node.mlist_to_hlist


function node.mlist_to_hlist(
  n: Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
},
  display_type: string,
  penalties: boolean
) -> h Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
}

Run the internal mlist to hlist conversion, converting the math list in n into the horizontal list h.

The interface is exactly the same as for the callback mlist_to_hlist.

Reference:

node.tail


function node.tail(n: Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
}) -> m Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
}

Return the last node of the node list that starts at n.

Reference:

node.length


function node.length(
  n: Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
},
  m: Node?
) -> i integer

Return the number of nodes contained in the node list that starts at n.

If m is also supplied it stops at m instead of at the end of the list. The node m is not counted.

Reference:

node.count


function node.count(
  id: (integer|string),
  n: Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
},
  m: Node?
) -> i integer

Return the number of nodes contained in the node list that starts at n that have a matching id field.

If m is also supplied, counting stops at m instead of at the end of the list. The node m is not counted. This function also accept string id’s.

Reference:

node.is_char


function node.is_char(
  n: Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
},
  font: integer?
)
 ->  (boolean|integer)?
 ->  integer?

Signal if the glyph is already turned into a character reference or not by examining the subtype.

Example:

assert.equals(node.is_char(node.new("glyph")), 0)
assert.equals(node.is_char(node.new("hlist")), nil)

Reference:

node.is_glyph


function node.is_glyph(n: Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
})
 -> character (boolean|integer)
 -> font integer

Signal if the glyph is already turned into a character reference or not by examining the subtype.

Example:

local character, font = node.is_glyph(node.new("glyph"))
assert.equals(character, 0)
assert.equals(font, 0)
assert.is_false(node.is_glyph(node.new("hlist")))

Reference:

node.traverse


function node.traverse(n: Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
}) ->  fun() -> (Node,integer,integer)

Return a Lua iterator that loops over the node list that starts at n.

Example:

callback.register("post_linebreak_filter", function(head)
  for n, type, subtype in node.traverse(head.head) do
    assert.is_type(n, "userdata")
    assert.is_type(type, "number")
    assert.is_type(subtype, "number")
  end
  return head
end)

It should be clear from the definition of the function f that even though it is possible to add or remove nodes from the node list while traversing, you have to take great care to make sure all the next (and prev) pointers remain valid.

Reference:

node.traverse_id


function node.traverse_id(
  id: integer,
  n: Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
}
) ->  fun() -> (Node,integer)

Return an iterator that loops over all the nodes in the list that starts at n that have a matching id field.

Reference:

node.traverse_char


function node.traverse_char(n: Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
}) ->  fun() -> (Node,integer,integer)

Loop over the glyph nodes in a list.

Only nodes with a subtype less than 256 are seen.

Reference:

node.traverse_glyph


function node.traverse_glyph(n: Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
}) ->  fun() -> (Node,integer,integer)

Loop over a list and return the list and filter all glyphs.

Reference:

node.traverse_list


function node.traverse_list(n: Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
}) ->  fun() -> (Node,integer,integer,Node)

Loop over the hlist and vlist nodes in a list.

The four return values can save some time compared to fetching these fields.

Reference:

node.has_glyph


function node.has_glyph(n: Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
}) -> n Node?

Return the first glyph or disc node in the given list.

Reference:

node.end_of_math


function node.end_of_math(n: Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
}) -> t Node?

Look for and return the next math node following the start node n.

If the given node is a math end node this helper returns that node, else it follows the list and returns the next math endnote. If no such node is found nil is returned.

Reference:

node.remove


function node.remove(
  head: Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
},
  current: Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
}
)
 -> head Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
}
 -> current Node?
@param current - A node following the list head.

@return head - The new head

@return current - The node following the current in the calling argument.

😱 Types incomplete or incorrect? 🙏 Please contribute!

Remove the node current from the list following head.

It is your responsibility to make sure it is really part of that list. The return values are the new head and current nodes. The returned current is the node following the current in the calling argument, and is only passed back as a convenience (or nil, if there is no such node). The returned head is more important, because if the function is called with current equal to head, it will be changed.

Example:

local g1 = node.new("glyph")
local g2 = node.new("glyph")
local g3 = node.new("glyph")

g1.next = g2
g2.next = g3
assert.equals(g1.next, g2)

local head, current = node.remove(g1, g2)
assert.equals(g1.next, g3)
assert.equals(head, g1)
assert.equals(current, g3)

Reference:

node.insert_before


function node.insert_before(
  head: Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
},
  current: Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
},
  new: Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
}
)
 -> head Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
}
 -> new Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
}

Insert the node new before current into the list following head.

It is your responsibility to make sure that current is really part of that list. The return values are the (potentially mutated) head and the node new, set up to be part of the list (with correct next field). If head is initially nil, it will become new.

Reference:

node.insert_after


function node.insert_after(
  head: Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
},
  current: Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
},
  new: Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
}
)
 -> head Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
}
 -> new Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
}

Insert the node new after current into the list following head.

It is your responsibility to make sure that current is really part of that list. The return values are the head and the node new, set up to be part of the list (with correct next field). If head is initially nil, it will become new.

Reference:

node.first_glyph


function node.first_glyph(
  n: Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
},
  m: Node?
)
 -> n Node?
 -> success boolean

Return the first node in the list starting at n that is a glyph node with a subtype indicating it is a glyph, or nil.

If m is given, processing stops at (but including) that node, otherwise processing stops at the end of the list.

Reference:

node.ligaturing


function node.ligaturing(
  head: Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
},
  tail: Node?
)
 -> head Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
}
 -> tail Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
}
 -> success boolean

@return head - the new head

@return tail - the new tail (both head and tail can change into a new ligature)

Apply TeX-style ligaturing to the specified nodelist.

Reference:

node.kerning


function node.kerning(
  head: Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
},
  tail: Node?
)
 -> head Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
}
 -> tail Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
}
 -> success boolean

@return head - the new head (can be an inserted kern node, because special kernings with word boundaries are possible).

@return tail - the new tail (can be an inserted kern node, because special kernings with word boundaries are possible).

Apply TeX-style kerning to the specified node list.

Reference:

node.unprotect_glyph


function node.unprotect_glyph(n: Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
})

Convert from characters to glyphs during node processing by subtracting 256 from all glyph node subtypes.

Reference:

node.unprotect_glyphs


function node.unprotect_glyphs(
  head: Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
},
  tail: Node?
)

Convert from characters to glyphs during node processing by subtracting 256 from the glyph node subtype.

Reference:

node.protect_glyph


function node.protect_glyph(n: Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
})

Add 256 to the glyph node subtype except that if the value is 1, add only 255.

The special handling of 1 means that characters will become glyphs after subtraction of 256.

Reference:

node.protect_glyphs


function node.protect_glyphs(
  head: Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
},
  tail: Node?
)

Add 256 to all glyph node subtypes in the node list starting at head, except that if the value is 1, add only 255.

The special handling of 1 means that characters will become glyphs after subtraction of 256.

Reference:

node.last_node


function node.last_node() -> n Node?

Pop the last node from TeX's “current list”.

Reference:

node.write


function node.write(n: Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
})

Append a node list to TeX's “current list”.

The node list is not deep-copied! There is no error checking either! You mignt need to enforce horizontal mode in order for this to work as expected.

Reference:

node.protrusion_skippable


function node.protrusion_skippable(n: Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
}) -> skippable boolean

Return true if, for the purpose of line boundary discovery when character protrusion is active, this node can be skipped.

Reference:

node.setglue


function node.setglue(
  n: Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
},
  width: (integer|any),
  stretch: (integer|any),
  shrink: (integer|any),
  stretch_order: (integer|any),
  shrink_order: (integer|any)
)

Set the five properties of a glue node in one go.

Non-numeric values are equivalent to zero and reset a property.

When you pass values, only arguments that are numbers are assigned so

node.setglue(n,655360,false,65536)

will only adapt the width and shrink.

When a list node is passed, you set the glue, order and sign instead.

Reference:

node.getglue


function node.getglue(n: Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
})
 -> width_or_glue_set (integer|number)?
 -> stretch_or_glue_order integer?
 -> shrink_or_glue_sign integer?
 -> stretch_order integer?
 -> shrink_order integer?

@return width_or_glue_set - When a list node (vlist or hlist) is passed, than the glue set is returned.

@return stretch_or_glue_order - When a list node (vlist or hlist) is passed, than the glue order is returned.

@return shrink_or_glue_sign - When a list node (vlist or hlist) is passed, than the glue sign is returned.

Return 5 values or nothing when no glue is passed.

When the second argument is false, only the width is returned (this is consistent with tex.get).

Reference:

node.is_zero_glue


function node.is_zero_glue(n: Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
}) -> isglue boolean

Return true when the width, stretch and shrink properties are zero.

Reference:

node.has_attribute


function node.has_attribute(
  n: Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
},
  id: integer,
  value: integer?
) -> value integer?

Test if a node has the attribute with number id set.

If value is also supplied, also tests if the value matches value. It returns the value, or, if no match is found, nil.

Reference:

node.get_attribute


function node.get_attribute(
  n: Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
},
  id: integer
) -> value integer?

Test if a node has an attribute with number id set.

It returns the value, or, if no match is found, nil. If no id is given then the zero attributes is assumed.

Reference:

node.find_attribute


function node.find_attribute(
  n: Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
},
  id: integer
)
 -> value integer?
 -> n Node?

Find the first node that has attribute with number id set.

It returns the value and the node if there is a match and otherwise nothing.

Reference:

node.set_attribute


function node.set_attribute(
  n: Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
},
  id: integer,
  value: integer?
)

Set the attribute with number id to the value value.

Duplicate assignments are ignored.

Reference:

node.unset_attribute


function node.unset_attribute(
  n: Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
},
  id: integer,
  value: integer?
) -> value integer?

Unset the attribute with the number id.

If value is also supplied, it will only perform this operation if the value matches value. Missing attributes or attribute-value pairs are ignored.

If the attribute was actually deleted, returns its old value. Otherwise, returns nil.

Reference:

node.slide


function node.slide(n: Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
}) -> tail Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
}

Return the found tail node and make sure that the node lists is double linked.

After some callbacks automatic sliding takes place. This feature can be turned off with node.fix_node_lists(false) but you better make sure then that you don't mess up lists. In most cases TeX itself only uses next pointers but your other callbacks might expect proper prev pointers too. Future versions of LuaTeX can add more checking but this will not influence usage.

Reference:

node.check_discretionaries


function node.check_discretionaries(head: Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
})

Check all disc nodes in the node list.

When you fool around with disc nodes you need to be aware of the fact that they have a special internal data structure. As long as you reassign the fields when you have extended the lists it’s ok because then the tail pointers get updated, but when you add to list without reassigning you might end up in trouble when the linebreak routine kicks in.

Reference:

node.check_discretionary


function node.check_discretionary(n: Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
})

Check one disc node only and also check if the node is a disc node.

When you fool around with disc nodes you need to be aware of the fact that they have a special internal data structure. As long as you reassign the fields when you have extended the lists it’s ok because then the tail pointers get updated, but when you add to list without reassigning you might end up in trouble when the linebreak routine kicks in.

Reference:

node.flatten_discretionaries


function node.flatten_discretionaries(n: Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
})
 -> head Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
}
 -> count integer

Remove the discretionaries in the list and inject the replace field when set.

Reference:

node.family_font


function node.family_font(fam: integer) -> id integer
@param fam - family identifier

Return the font currently associated with the node.

You can normally also access the font with the normal font field or getter because it will resolve the family automatically for noads.

Reference:

node.getnext


function node.getnext(n: Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
}) -> next Node?

Return the next node of the current node.

Example:

local n1 = node.new("glyph")
local n2 = node.new("glyph")
n1.next = n2
assert.equals(node.getnext(n1), n2)

Reference:

@see node.getprev node.direct.getnext

node.getprev


function node.getprev(n: Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
}) -> prev Node?

Return the previous node of the current node.

Example:

local n1 = node.new("glyph")
local n2 = node.new("glyph")
n1.prev = n2
assert.equals(node.getprev(n1), n2)

Reference:

node.getboth


function node.getboth(n: Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
})
 -> prev Node?
 -> next Node?

Return the previous and next pointer of a node.

Example:

local n1 = node.new("glyph")
local n2 = node.new("glyph")
local n3 = node.new("glyph")

n1.next = n2
n2.next = n3
node.slide(n1) -- to set n2.prev

local prev, next = node.getboth(n2)
assert.equals(prev, n1)
assert.equals(next, n3)

Reference:

@see node.direct.setboth

node.getid


function node.getid(n: Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
}) -> id integer

Return the id (type) of a node.

Reference:

node.getsubtype


function node.getsubtype(n: Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
}) -> subtype integer

Return the subtype of a node.

Example:

local hlist = node.new("hlist")
hlist.subtype = 2

assert.equals(node.getsubtype(hlist), 2)

Reference:

node.getfont


function node.getfont(n: Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
}) -> font integer?

Return the font identifier of a glyph, math_char, math_text_char or delim node.

Example:

local n = node.new("glyph") --[[@as GlyphNode]]
n.font = 1
assert.equals(node.getfont(n), 29)

Reference:

node.getchar


function node.getchar(n: Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
}) -> char integer?

Return the character index (char) in the font of glyph, math_char, math_text_char or delim nodes.

Example:

local n = node.new("glyph") --[[@as GlyphNode]]
n.char = 1
assert.equals(node.getchar(n), 1)

Reference:

@see node.direct.getchar node.direct.setchar

node.getwhd


function node.getwhd(
  n: Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
},
  get_expansion_factor: boolean?
)
 -> width integer
 -> height integer
 -> depth integer
 -> ex integer?

@return ex - If the node is a glyph and get_ex is true

😱 Types incomplete or incorrect? 🙏 Please contribute!

Return the width, height and depth of a list, rule or (unexpanded) glyph as well as glue (its spec is looked at) and unset node.

Reference:

node.getdisc


function node.getdisc(
  n: Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
},
  get_tail: boolean?
)
 -> pre Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
}
 -> post Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
}
 -> replace Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
}
 -> pre_tail Node?
 -> post_tail Node?
 -> replace_tail Node?

Return the pre, post and replace fields and optionally when true is passed also the tail fields.

Reference:

node.getlist


function node.getlist(n: Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
}) -> list Node?

Get child node lists of parent hlist, vlist, sub_box, sub_mlist, ins, or adjust nodes.

Example:

local hlist = node.new("hlist")
local glyph1 = node.new("glyph")
local glyph2 = node.new("glyph")

hlist.head = glyph1
glyph1.next = glyph2

local list = node.getlist(hlist)
assert.equals(list, glyph1)
assert.equals(list.next, glyph2)

Reference:

node.getleader


function node.getleader(n: Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
}) -> leaders Node?

Get the leaders of glue nodes.

Reference:

node.setfield


function node.setfield(
  n: Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
},
  field: string,
  value: any
)

Set the value of a generic node field.

Example:

local n = node.new("glyph")
node.setfield(n, "char", 2)
assert.equals(node.getfield(n, "char"), 2)

Reference:

@see node.direct.setfield

node.getfield


function node.getfield(
  n: Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
},
  field: string
) ->  any?

Get the value of a generic node field.

Other field names are often shared so a specific getter makes no sense.

Example:

local n = node.new("glyph") --[[@as GlyphNode]]
n.char = 123
assert.equals(node.getfield(n, "char"), 123)

Reference:

node.setproperty


function node.setproperty(
  n: Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
},
  value: any
)

Assign values to the properties table.

Example:

local n = node.new("glyph")
node.setproperty(n, "Some value")
assert.equals(node.getproperty(n), "Some value")

Reference:

node.getproperty


function node.getproperty(node: Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
}) -> value any

Each node also can have a properties table and you can get properties using the getproperty function.

Example:

local n = node.new("glyph")
node.setproperty(n, "Some value")
assert.equals(node.getproperty(n), "Some value")

Reference:

node.set_properties_mode


function node.set_properties_mode(
  enable: boolean,
  use_metatable: boolean?
)

Enable managing properties in the node (de)allocator functions.

Managing properties in the node (de)allocator functions is disabled by default.

Reference:

node.flush_properties_table


function node.flush_properties_table()

Example:

local n = node.new("glyph")
node.setproperty(n, "Some properties")
assert.equals(node.get_properties_table()[n], "Some properties")

node.flush_properties_table()
assert.is_nil(node.get_properties_table()[n])

Reference:

😱 Types incomplete or incorrect? 🙏 Please contribute!

node.get_properties_table


function node.get_properties_table() ->  table<Node,any>

Example:

local glyph1 = node.new("glyph")
node.setproperty(glyph1, "Properties of glyph 1")

local glyph2 = node.new("glyph")
node.setproperty(glyph2, "Properties of glyph 2")

assert.equals(node.get_properties_table()[glyph1], "Properties of glyph 1")
assert.equals(node.get_properties_table()[glyph2], "Properties of glyph 2")

Reference:

node.fix_node_lists


function node.fix_node_lists()

Reference:

😱 Types incomplete or incorrect? 🙏 Please contribute!

node.hyphenating


function node.hyphenating(
  n: Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
},
  m: Node?
)

Reference:

node.make_extensible


function node.make_extensible(
  fnt: integer,
  chr: integer,
  size: integer,
  overlap: integer?,
  horizontal: boolean?,
  attlist: Node?
)

Reference:

node.subtypes


function node.subtypes(subtype: (string|integer)) ->  table<integer,string>

Example:

local subtypes = {}

for _, node_type in pairs(node.types()) do
  local subtype = node.subtypes(node_type)
  if subtype then
    subtypes[node_type] = subtype
  end
end

assert.same(subtypes, {
  accent = {
    "fixedtop",
    "fixedbottom",
    "fixedboth",
    [0] = "bothflexible",
  },
  adjust = { "pre", [0] = "normal" },
  boundary = { "user", "protrusion", "word", [0] = "cancel" },
  dir = { "cancel", [0] = "normal" },
  disc = {
    "explicit",
    "automatic",
    "regular",
    "first",
    "second",
    [0] = "discretionary",
  },
  fence = { "left", "middle", "right", "no", [0] = "unset" },
  glue = {
    "lineskip",
    "baselineskip",
    "parskip",
    "abovedisplayskip",
    "belowdisplayskip",
    "abovedisplayshortskip",
    "belowdisplayshortskip",
    "leftskip",
    "rightskip",
    "topskip",
    "splittopskip",
    "tabskip",
    "spaceskip",
    "xspaceskip",
    "parfillskip",
    "mathskip",
    "thinmuskip",
    "medmuskip",
    "thickmuskip",
    [0] = "userskip",
    [98] = "conditionalmathskip",
    [99] = "muglue",
    [100] = "leaders",
    [101] = "cleaders",
    [102] = "xleaders",
    [103] = "gleaders",
  },
  glyph = {
    "character",
    "ligature",
    [0] = "unset",
    [4] = "ghost",
    [8] = "left",
    [16] = "right",
  },
  hlist = {
    "line",
    "box",
    "indent",
    "alignment",
    "cell",
    "equation",
    "equationnumber",
    "math",
    "mathchar",
    "hextensible",
    "vextensible",
    "hdelimiter",
    "vdelimiter",
    "overdelimiter",
    "underdelimiter",
    "numerator",
    "denominator",
    "limits",
    "fraction",
    "nucleus",
    "sup",
    "sub",
    "degree",
    "scripts",
    "over",
    "under",
    "accent",
    "radical",
    [0] = "unknown",
  },
  kern = {
    "userkern",
    "accentkern",
    "italiccorrection",
    [0] = "fontkern",
  },
  math = { "endmath", [0] = "beginmath" },
  noad = {
    "opdisplaylimits",
    "oplimits",
    "opnolimits",
    "bin",
    "rel",
    "open",
    "close",
    "punct",
    "inner",
    "under",
    "over",
    "vcenter",
    [0] = "ord",
  },
  penalty = {
    "linebreakpenalty",
    "linepenalty",
    "wordpenalty",
    "finalpenalty",
    "noadpenalty",
    "beforedisplaypenalty",
    "afterdisplaypenalty",
    "equationnumberpenalty",
    [0] = "userpenalty",
  },
  radical = {
    "uradical",
    "uroot",
    "uunderdelimiter",
    "uoverdelimiter",
    "udelimiterunder",
    "udelimiterover",
    [0] = "radical",
  },
  rule = {
    "box",
    "image",
    "empty",
    "user",
    "over",
    "under",
    "fraction",
    "radical",
    "outline",
    [0] = "normal",
  },
  vlist = {
    "line",
    "box",
    "indent",
    "alignment",
    "cell",
    "equation",
    "equationnumber",
    "math",
    "mathchar",
    "hextensible",
    "vextensible",
    "hdelimiter",
    "vdelimiter",
    "overdelimiter",
    "underdelimiter",
    "numerator",
    "denominator",
    "limits",
    "fraction",
    "nucleus",
    "sup",
    "sub",
    "degree",
    "scripts",
    "over",
    "under",
    "accent",
    "radical",
    [0] = "unknown",
  },
})

Reference:

node.tostring


function node.tostring(n: Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
}) ->  string

@return - For example <node nil < 234 > nil : glyph 0>

😱 Types incomplete or incorrect? 🙏 Please contribute!

Example:

assert.equals(
  node.tostring(node.new("glyph")),
  "<node    nil <    234 >    nil : glyph 0>"
)

Reference:

@see node.direct.tostring

node.usedlist


function node.usedlist() -> n Node {
    next = Node?,
    prev = Node?,
    id = integer,
    subtype = integer,
    head = Node?,
    attr = Node,
}

Example:

assert.equals(
  tostring(node.usedlist()),
  "<node    nil <    234 >    239 : dir 0>"
)

Reference:

@see node.direct.usedlist

node.values


function node.values(type: ("dir"|"direction"|"glue"|"pdf_literal"|"pdf_action"|"pdf_window"|"color_stack"|"pagestate")) ->  string[]?

Report some used values.

Valid arguments are dir, direction, glue, pdf_literal, pdf_action, pdf_window and color_stack. Keep in mind that the setters normally expect a number, but this helper gives you a list of what numbers matter. For practical reason the pagestate values are also reported with this helper.

Example:

assert.same(
  node.values("dir"),
  { [0] = "TLT", [1] = "TRT", [2] = "LTL", [3] = "RTT" }
)
assert.same(
  node.values("direction"),
  { [0] = "TLT", [1] = "TRT", [2] = "LTL", [3] = "RTT" }
)
assert.same(
  node.values("glue"),
  { [0] = "normal", [1] = "fi", [2] = "fil", [3] = "fill", [4] = "filll" }
)
assert.same(
  node.values("pdf_literal"),
  {
    [0] = "origin",
    [1] = "page",
    [2] = "always",
    [3] = "raw",
    [4] = "text",
    [5] = "font",
    [6] = "special",
  }
)
assert.same(
  node.values("pdf_action"),
  { [0] = "page", [1] = "goto", [2] = "thread", [3] = "user" }
)
assert.same(
  node.values("pdf_window"),
  { [0] = "unset", [1] = "new", [2] = "nonew" }
)
assert.same(
  node.values("color_stack"),
  { [0] = "set", [1] = "push", [2] = "pop", [3] = "current" }
)
assert.same(
  node.values("pagestate"),
  { [0] = "empty", [1] = "box_there", [2] = "inserts_only" }
)

Reference:

fields


node.direct


node.direct: table

Deep down in TEX a node has a number which is a numeric entry in a memory table. In fact, this model, where TEX manages memory is real fast and one of the reasons why plugging in callbacks that operate on nodes is quite fast too. Each node gets a number that is in fact an index in the memory table and that number often is reported when you print node related information. You go from userdata nodes and there numeric references and back with:

<integer> d = node.todirect(<node> n)
<node> n = node.tonode(<integer> d)

The userdata model is rather robust as it is a virtual interface with some additional checking while the more direct access which uses the node numbers directly. However, even with userdata you can get into troubles when you free nodes that are no longer allocated or mess up lists. if you apply tostring to a node you see its internal (direct) number and id.

😱 Types incomplete or incorrect? 🙏 Please contribute!