summaryrefslogtreecommitdiff
path: root/tex/context/base/node-ini.lua
diff options
context:
space:
mode:
Diffstat (limited to 'tex/context/base/node-ini.lua')
-rw-r--r--tex/context/base/node-ini.lua931
1 files changed, 352 insertions, 579 deletions
diff --git a/tex/context/base/node-ini.lua b/tex/context/base/node-ini.lua
index af30b3940..d2cfc17e0 100644
--- a/tex/context/base/node-ini.lua
+++ b/tex/context/base/node-ini.lua
@@ -8,14 +8,63 @@ if not modules then modules = { } end modules ['node-ini'] = {
--[[ldx--
<p>Access to nodes is what gives <l n='luatex'/> its power. Here we
-implement a few helper functions.</p>
+implement a few helper functions. These functions are rather optimized.</p>
--ldx]]--
-nodes = nodes or { }
-nodes.trace = false
+nodes = nodes or { }
+nodes.trace = false
+nodes.ignore = nodes.ignore or false
-- handy helpers
+if node.protect_glyphs then
+
+ nodes.protect_glyphs = node.protect_glyphs
+ nodes.unprotect_glyphs = node.unprotect_glyphs
+
+else do
+
+ -- initial value subtype : X000 0001 = 1 = 0x01 = char
+ --
+ -- expected before linebreak : X000 0000 = 0 = 0x00 = glyph
+ -- X000 0010 = 2 = 0x02 = ligature
+ -- X000 0100 = 4 = 0x04 = ghost
+ -- X000 1010 = 10 = 0x0A = leftboundary lig
+ -- X001 0010 = 18 = 0x12 = rightboundary lig
+ -- X001 1010 = 26 = 0x1A = both boundaries lig
+ -- X000 1100 = 12 = 0x1C = leftghost
+ -- X001 0100 = 20 = 0x14 = rightghost
+
+
+ local glyph = node.id('glyph')
+ local traverse_id = node.traverse_id
+
+ function nodes.protect_glyphs(head)
+ local done = false
+ for g in traverse_id(glyph,head) do
+ local s = g.subtype
+ if s == 1 then
+ done, g.subtype = true, 256
+ elseif s <= 256 then
+ done, g.subtype = true, 256 + s
+ end
+ end
+ return done
+ end
+
+ function nodes.unprotect_glyphs(head)
+ local done = false
+ for g in traverse_id(glyph,head) do
+ local s = g.subtype
+ if s > 256 then
+ done, g.subtype = true, s - 256
+ end
+ end
+ return done
+ end
+
+end end
+
do
local remove, free = node.remove, node.free
@@ -76,44 +125,18 @@ function nodes.report(t,done)
end
end
---~ function nodes.count(stack)
---~ if stack then
---~ local n = 0
---~ for _, node in pairs(stack) do
---~ if node then
---~ local kind = node[1]
---~ if kind == 'hlist' or kind == 'vlist' then
---~ local content = node[8]
---~ if type(content) == "table" then
---~ n = n + 1 + nodes.count(content) -- self counts too
---~ else
---~ n = n + 1
---~ end
---~ elseif kind == 'inline' then
---~ n = n + nodes.count(node[4]) -- self does not count
---~ else
---~ n = n + 1
---~ end
---~ end
---~ end
---~ return n
---~ else
---~ return 0
---~ end
---~ end
-
do
local hlist, vlist = node.id('hlist'), node.id('vlist')
- function nodes.count(stack)
+ local function count(stack,flat)
local n = 0
while stack do
local id = stack.id
- if id == hlist or id == vlist then
+ if not flat and id == hlist or id == vlist then
local list = stack.list
if list then
- n = n + 1 + nodes.count(list) -- self counts too
+ n = n + 1 + count(list) -- self counts too
else
n = n + 1
end
@@ -125,6 +148,8 @@ do
return n
end
+ nodes.count = count
+
end
--[[ldx--
@@ -147,95 +172,14 @@ original table is used.</p>
<p>Insertion is handled (at least in <l n='context'/> as follows. When
we need to insert a node at a certain position, we change the node at
that position by a dummy node, tagged <type>inline</type> which itself
-contains the original node and one or more new nodes. Before we pass
+has_attribute the original node and one or more new nodes. Before we pass
back the list we collapse the list. Of course collapsing could be built
into the <l n='tex'/> engine, but this is a not so natural extension.</p>
<p>When we collapse (something that we only do when really needed), we
-also ignore the empty nodes.</p>
+also ignore the empty nodes. [This is obsolete!]</p>
--ldx]]--
---~ function nodes.inline(...)
---~ return { 'inline', 0, nil, { ... } }
---~ end
-
---~ do
-
---~ function collapse(stack,existing_t)
---~ if stack then
---~ local t = existing_t or { }
---~ for _, node in pairs(stack) do
---~ if node then
---~ -- if node[3] then node[3][1] = nil end -- remove status bit
---~ local kind = node[1]
---~ if kind == 'inline' then
---~ collapse(node[4],t)
---~ elseif kind == 'hlist' or kind == 'vlist' then
---~ local content = node[8]
---~ if type(content) == "table" then
---~ node[8] = collapse(content)
---~ end
---~ t[#t+1] = node
---~ else
---~ t[#t+1] = node
---~ end
---~ else
---~ -- deleted node
---~ end
---~ end
---~ return t
---~ else
---~ return stack
---~ end
---~ end
-
---~ nodes.collapse = collapse
-
---~ end
-
---[[ldx--
-<p>The following function implements a generic node processor. A
-generic processer is not that much needed, because we often need
-to act differently for horizontal or vertical lists. For instance
-counting nodes needs a different method (ok, we could add a second
-handle for catching them but it would become messy then).</p>
---ldx]]--
-
---~ function nodes.each(stack,handle)
---~ if stack then
---~ local i = 1
---~ while true do
---~ local node = stack[i]
---~ if node then
---~ local kind = node[1]
---~ if kind == 'hlist' or kind == 'vlist' then
---~ local content = node[8]
---~ if type(content) == "table" then
---~ nodes.each(content,handle)
---~ end
---~ elseif kind == 'inline' then
---~ nodes.each(node[4],handle)
---~ else
---~ stack[i] = handle(kind,node)
---~ end
---~ end
---~ i = i + 1
---~ if i > #stack then
---~ break
---~ end
---~ end
---~ end
---~ end
-
---~ function nodes.remove(stack,id,subid) -- "whatsit", 6
---~ nodes.each(stack, function(kind,node)
---~ if kind == id and node[2] == subid then
---~ return false
---~ else
---~ return node
---~ end
---~ end)
---~ end
--[[ldx--
<p>Serializing nodes can be handy for tracing. Also, saving and
@@ -298,64 +242,100 @@ if not fonts.tfm.id then fonts.tfm.id = { } end
do
- local glyph, hlist, vlist = node.id('glyph'), node.id('hlist'), node.id('vlist')
- local pushmarks = false
+ local glyph = node.id('glyph')
+ local has_attribute = node.has_attribute
+ local traverse_id = node.traverse_id
- function nodes.process_glyphs(head)
+ local pairs = pairs
+
+ local starttiming, stoptiming = input.starttiming, input.stoptiming
+
+ function nodes.process_characters(head)
if status.output_active then -- not ok, we need a generic blocker, pagebody ! / attr tex.attibutes
- -- 25% calls
- return true
- elseif not head then
- -- 25% calls
- return true
- elseif not head.next and (head.id == hlist or head.id == vlist) then
- return head
+ return head, false -- true
else
-- either next or not, but definitely no already processed list
- input.start_timing(nodes)
- local usedfonts, found, fontdata, done = { }, false, fonts.tfm.id, false
- for n in node.traverse_id(glyph,head) do
- local font = n.font
- if not usedfonts[font] then
- local shared = fontdata[font].shared
- if shared and shared.processors then
- usedfonts[font], found = shared.processors, true
+ starttiming(nodes)
+ local usedfonts, attrfonts, done = { }, { }, false
+ -- todo: should be independent of otf
+ local set_dynamics, font_ids = fonts.otf.set_dynamics, fonts.tfm.id -- todo: font-var.lua so that we can global this one
+ local a, u, prevfont, prevattr = 0, 0, nil, 0
+ for n in traverse_id(glyph,head) do
+ local font, attr = n.font, has_attribute(n,0) -- zero attribute is reserved for fonts, preset to 0 is faster (first match)
+ if attr and attr > 0 then
+ if font ~= prevfont or attr ~= prevattr then
+ local used = attrfonts[font]
+ if not used then
+ used = { }
+ attrfonts[font] = used
+ end
+ if not used[attr] then
+ local d = set_dynamics(font_ids[font],attr) -- todo, script, language -> n.language also axis
+ if d then
+ used[attr] = d
+ a = a + 1
+ end
+ end
+ prevfont, prevattr = font, attr
+ end
+ elseif font ~= prevfont then
+ prevfont, prevattr = font, 0
+ local used = usedfonts[font]
+ if not used then
+ local data = font_ids[font]
+ if data then
+ local shared = data.shared -- we need to check shared, only when same features
+ if shared then
+ local processors = shared.processors
+ if processors and #processors > 0 then
+ usedfonts[font] = processors
+ u = u + 1
+ end
+ end
+ else
+ -- probably nullfont
+ end
end
- end
- end
- if found then
- local tail = head
- if head.next then
- tail = node.slide(head)
else
- head.prev = nil
+ prevattr = attr
end
+ end
+ -- we could combine these and just make the attribute nil
+ if u > 0 then
for font, processors in pairs(usedfonts) do
- if pushmarks then
- local h, d = fonts.pushmarks(head,font)
- head, done = head or h, done or d
- end
- for _, processor in ipairs(processors) do
- local h, d = processor(head,font)
- head, done = head or h, done or d
+ local n = #processors
+ if n == 1 then
+ local h, d = processors[1](head,font,false)
+ head, done = h or head, done or d
+ else
+ for i=1,#processors do
+ local h, d = processors[i](head,font,false)
+ head, done = h or head, done or d
+ end
end
- if pushmarks then
- local h, d = fonts.popmarks(head,font)
- head, done = head or h, done or d
+ end
+ end
+ if a > 0 then -- we need to get rid of a loop here
+ for font, dynamics in pairs(attrfonts) do
+ for attribute, processors in pairs(dynamics) do -- attr can switch in between
+ local n = #processors
+ if n == 1 then
+ local h, d = processors[1](head,font,attribute)
+ head, done = h or head, done or d
+ else
+ for i=1,n do
+ local h, d = processors[i](head,font,attribute)
+ head, done = h or head, done or d
+ end
+ end
end
end
end
- input.stop_timing(nodes)
+ stoptiming(nodes)
if nodes.trace then
nodes.report(head,done)
end
- if done then
- return head -- something changed
- elseif head then
- return true -- nothing changed
- else
- return false -- delete list
- end
+ return head, true
end
end
@@ -366,9 +346,9 @@ end
do
- local contains, set, attribute = node.has_attribute, node.set_attribute, tex.attribute
+ local has_attribute, set, attribute = node.has_attribute, node.set_attribute, tex.attribute
- function nodes.inherit_attributes(n)
+ function nodes.inherit_attributes(n) -- still ok ?
if n then
local i = 1
while true do
@@ -376,7 +356,7 @@ do
if a < 0 then
break
else
- local ai = contains(n,i)
+ local ai = has_attribute(n,i)
if not ai then
set(n,i,a)
end
@@ -400,54 +380,132 @@ function nodes.length(head)
end
end
-nodes.processors.actions = nodes.processors.actions or { }
+--~ nodes.processors.actions = nodes.processors.actions or { }
-function nodes.processors.action(head)
- if head then
- node.slide(head)
- local actions, done = nodes.processors.actions, false
- for i=1,#actions do
- local action = actions[i]
- if action then
- local h, ok = action(head)
- if ok then
- head = h
- end
- done = done or ok
- end
- end
- if done then
- return head
- else
- return true
- end
- else
- return head
- end
-end
+--~ function nodes.processors.action(head)
+--~ if head then
+--~ node.slide(head)
+--~ local done = false
+--~ local actions = nodes.processors.actions
+--~ for i=1,#actions do
+--~ local h, ok = actions[i](head)
+--~ if ok then
+--~ head, done = h, true
+--~ end
+--~ end
+--~ if done then
+--~ return head
+--~ else
+--~ return true
+--~ end
+--~ else
+--~ return head
+--~ end
+--~ end
lists = lists or { }
lists.plugins = lists.plugins or { }
-function nodes.process_lists(head)
- return nodes.process_attributes(head,lists.plugins)
-end
-
chars = chars or { }
chars.plugins = chars.plugins or { }
-function nodes.process_chars(head)
- return nodes.process_attributes(head,chars.plugins)
+--~ words = words or { }
+--~ words.plugins = words.plugins or { }
+
+callbacks.trace = false
+
+do
+
+ kernel = kernel or { }
+
+ local starttiming, stoptiming = input.starttiming, input.stoptiming
+ local hyphenate, ligaturing, kerning = lang.hyphenate, node.ligaturing, node.kerning
+
+ function kernel.hyphenation(head,tail) -- lang.hyphenate returns done
+ starttiming(kernel)
+ local done = hyphenate(head,tail)
+ stoptiming(kernel)
+ return head, tail, done
+ end
+ function kernel.ligaturing(head,tail) -- node.ligaturing returns head,tail,done
+ starttiming(kernel)
+ local head, tail, done = ligaturing(head,tail)
+ stoptiming(kernel)
+ return head, tail, done
+ end
+ function kernel.kerning(head,tail) -- node.kerning returns head,tail,done
+ starttiming(kernel)
+ local head, tail, done = kerning(head,tail)
+ stoptiming(kernel)
+ return head, tail, done
+ end
+
end
-nodes.processors.actions = { -- for the moment here, will change
- nodes.process_chars, -- attribute driven
- nodes.process_glyphs, -- font driven
- nodes.process_lists, -- attribute driven
-}
+callback.register('hyphenate' , function(head,tail) return tail end)
+callback.register('ligaturing', function(head,tail) return tail end)
+callback.register('kerning' , function(head,tail) return tail end)
-callback.register('pre_linebreak_filter', nodes.processors.action)
-callback.register('hpack_filter', nodes.processors.action)
+-- used to be loop, this is faster, called often; todo: shift up tail or even better,
+-- handle tail everywhere; for the moment we're safe
+
+do
+
+ local charplugins, listplugins = chars.plugins, lists.plugins
+
+ nodes.processors.actions = function(head,tail) -- removed: if head ... end
+ local ok, done = false, false
+ head, ok = nodes.process_attributes(head,charplugins) ; done = done or ok -- attribute driven
+ head, tail, ok = kernel.hyphenation (head,tail) ; done = done or ok -- language driven
+ head, ok = languages.words.check (head,tail) ; done = done or ok -- language driven
+ head, ok = nodes.process_characters(head) ; done = done or ok -- font driven
+ ok = nodes.protect_glyphs (head) ; done = done or ok -- turn chars into glyphs
+ head, tail, ok = kernel.ligaturing (head,tail) ; done = done or ok -- normal ligaturing routine / needed for base mode
+ head, tail, ok = kernel.kerning (head,tail) ; done = done or ok -- normal kerning routine / needed for base mode
+ head, ok = nodes.process_attributes(head,listplugins) ; done = done or ok -- attribute driven
+ return head, done
+ end
+
+end
+
+do
+
+ local actions = nodes.processors.actions
+ local first_character = node.first_character
+ local slide = node.slide
+
+ local function tracer(what,state,head,groupcode,glyphcount)
+ texio.write_nl(string.format("%s %s: group: %s, nodes: %s",
+ (state and "Y") or "N", what, groupcode or "?", nodes.count(head,true)))
+ end
+
+ function nodes.processors.pre_linebreak_filter(head,groupcode) -- todo: tail
+ local first, found = first_character(head)
+ if found then
+ if callbacks.trace then tracer("pre_linebreak",true,head,groupcode) end
+ local head, done = actions(head,slide(head))
+ return (done and head) or true
+ else
+ if callbacks.trace then tracer("pre_linebreak",false,head,groupcode) end
+ return true
+ end
+ end
+
+ function nodes.processors.hpack_filter(head,groupcode) -- todo: tail
+ local first, found = first_character(head)
+ if found then
+ if callbacks.trace then tracer("hpack",true,head,groupcode) end
+ local head, done = actions(head,slide(head))
+ return (done and head) or true
+ end
+ if callbacks.trace then tracer("hpack",false,head,groupcode) end
+ return true
+ end
+
+end
+
+callback.register('pre_linebreak_filter', nodes.processors.pre_linebreak_filter)
+callback.register('hpack_filter' , nodes.processors.hpack_filter)
do
@@ -462,16 +520,40 @@ do
-- flat: don't use next, but indexes
-- verbose: also add type
+ -- can be sped up
+
+ function nodes.astable(n,sparse)
+ local f, t = node.fields(n.id,n.subtype), { }
+ for i=1,#f do
+ local v = f[i]
+ local d = n[v]
+ if d then
+ if v == "ref_count" or v == "id" then
+ -- skip
+ elseif expand[v] then -- or: type(n[v]) ~= "string" or type(n[v]) ~= "number" or type(n[v]) ~= "table"
+ t[v] = "pointer to list"
+ elseif sparse then
+ if (type(d) == "number" and d ~= 0) or (type(d) == "string" and d ~= "") then
+ t[v] = d
+ end
+ else
+ t[v] = d
+ end
+ end
+ end
+ t.type = node.type(n.id)
+ return t
+ end
function nodes.totable(n,flat,verbose)
- local function totable(n,verbose)
+ local function totable(n)
local f = node.fields(n.id,n.subtype)
local tt = { }
for _,v in ipairs(f) do
if n[v] then
if v == "ref_count" then
-- skip
- elseif expand[v] then -- or: type(n[v]) ~= "string" or type(n[v]) ~= "number"
+ elseif expand[v] then -- or: type(n[v]) ~= "string" or type(n[v]) ~= "number" or type(n[v]) ~= "table"
tt[v] = nodes.totable(n[v],flat,verbose)
else
tt[v] = n[v]
@@ -487,12 +569,12 @@ do
if flat then
local t = { }
while n do
- t[#t+1] = totable(n,verbose)
+ t[#t+1] = totable(n)
n = n.next
end
return t
else
- local t = totable(n,verbose)
+ local t = totable(n)
if n.next then
t.next = nodes.totable(n.next,flat,verbose)
end
@@ -504,11 +586,7 @@ do
end
local function key(k)
- if type(k) == "number" then
- return "["..k.."]"
- else
- return k
- end
+ return ((type(k) == "number") and "["..k.."]") or k
end
local function serialize(root,name,handle,depth,m)
@@ -518,13 +596,14 @@ do
handle(("%s%s={"):format(depth,key(name)))
else
depth = ""
- if type(name) == "string" then
+ local tname = type(name)
+ if tname == "string" then
if name == "return" then
handle("return {")
else
handle(name .. "={")
end
- elseif type(name) == "number" then
+ elseif tname == "number"then
handle("[" .. name .. "]={")
else
handle("t={")
@@ -533,7 +612,7 @@ do
if root then
local fld
if root.id then
- fld = node.fields(root.id,root.subtype)
+ fld = node.fields(root.id,root.subtype) -- we can cache these (todo)
else
fld = table.sortedkeys(root)
end
@@ -541,13 +620,23 @@ do
handle(("%s %s=%q,"):format(depth,'type',root['type']))
end
for _,k in ipairs(fld) do
- if k then
+ if k == "ref_count" then
+ -- skip
+ elseif k then
local v = root[k]
local t = type(v)
if t == "number" then
+if v == 0 then
+ -- skip
+else
handle(("%s %s=%s,"):format(depth,key(k),v))
+end
elseif t == "string" then
+if v == "" then
+ -- skip
+else
handle(("%s %s=%q,"):format(depth,key(k),v))
+end
elseif v then -- userdata or table
serialize(v,k,handle,depth,m+1)
end
@@ -585,9 +674,22 @@ do
tex.print("\\stoptyping")
end
+ function nodes.check_for_leaks(sparse)
+ local l = { }
+ local q = node.usedlist()
+ for p in node.traverse(q) do
+ local s = table.serialize(nodes.astable(p,sparse),node.type(p.id))
+ l[s] = (l[s] or 0) + 1
+ end
+ node.flush_list(q)
+ for k, v in pairs(l) do
+ texio.write_nl(string.format("%s * %s", v, k))
+ end
+ end
+
end
-if not node.list_has_attribute then
+if not node.list_has_attribute then -- no longer needed
function node.list_has_attribute(list,attribute)
if list and attribute then
@@ -609,377 +711,48 @@ function nodes.pack_list(head)
return t
end
--- helpers
-
do
- local kern_node = node.new("kern",1)
- local penalty_node = node.new("penalty")
- local glue_node = node.new("glue")
- local glue_spec_node = node.new("glue_spec")
+ local glue, whatsit, hlist = node.id("glue"), node.id("whatsit"), node.id("hlist")
- function nodes.penalty(p)
- local n = node.copy(penalty_node)
- n.penalty = p
- return n
- end
- function nodes.kern(k)
- local n = node.copy(kern_node)
- n.kern = k
- return n
- end
- function nodes.glue(width,stretch,shrink)
- local n = node.copy(glue_node)
- local s = node.copy(glue_spec_node)
- s.width, s.stretch, s.shrink = width, stretch, shrink
- n.spec = s
- return n
+ function nodes.leftskip(n)
+ while n do
+ local id = n.id
+ if id == glue then
+ if n.subtype == 8 then -- 7 in c/web source
+ return (n.spec and n.spec.width) or 0
+ else
+ return 0
+ end
+ elseif id == whatsit then
+ n = n.next
+ elseif id == hlist then
+ return n.width
+ else
+ break
+ end
+ end
+ return 0
end
- function nodes.glue_spec(width,stretch,shrink)
- local s = node.copy(glue_spec_node)
- s.width, s.stretch, s.shrink = width, stretch, shrink
- return s
+ function nodes.rightskip(n)
+ if n then
+ n = node.slide(n)
+ while n do
+ local id = n.id
+ if id == glue then
+ if n.subtype == 9 then -- 8 in the c/web source
+ return (n.spec and n.spec.width) or 0
+ else
+ return 0
+ end
+ elseif id == whatsit then
+ n = n.prev
+ else
+ break
+ end
+ end
+ end
+ return false
end
end
-
--- old code
-
---~ function nodes.do_process_glyphs(stack)
---~ if not stack or #stack == 0 then
---~ return false
---~ elseif #stack == 1 then
---~ local node = stack[1]
---~ if node then
---~ local kind = node[1]
---~ if kind == 'glyph' then
---~ local tfmdata = fonts.tfm.id[node[5]] -- we can use fonts.tfm.processor_id
---~ if tfmdata and tfmdata.shared and tfmdata.shared.processors then
---~ for _, func in pairs(tfmdata.shared.processors) do -- per font
---~ func(stack,1,node)
---~ end
---~ end
---~ elseif kind == 'hlist' or kind == "vlist" then
---~ local done = nodes.do_process_glyphs(node[8])
---~ end
---~ return true
---~ else
---~ return false
---~ end
---~ else
---~ local font_ids = { }
---~ local done = false
---~ for _, v in pairs(stack) do
---~ if v then
---~ if v[1] == 'glyph' then
---~ local font_id = v[5]
---~ local tfmdata = fonts.tfm.id[font_id] -- we can use fonts.tfm.processor_id
---~ if tfmdata and tfmdata.shared and tfmdata.shared.processors then
---~ font_ids[font_id] = tfmdata.shared.processors
---~ end
---~ end
---~ end
---~ end
---~ if done then
---~ return false
---~ else
---~ -- todo: generic loop before
---~ for font_id, _ in pairs(font_ids) do
---~ for _, func in pairs(font_ids[font_id]) do -- per font
---~ local i = 1
---~ while true do
---~ local node = stack[i]
---~ if node and node[1] == 'glyph' and node[5] == font_id then
---~ i = func(stack,i,node)
---~ end
---~ if i < #stack then
---~ i = i + 1
---~ else
---~ break
---~ end
---~ end
---~ end
---~ end
---~ for i=1, #stack do
---~ local node = stack[i]
---~ if node then
---~ if node[1] == 'hlist' or node[1] == "vlist" then
---~ nodes.do_process_glyphs(node[8])
---~ end
---~ end
---~ end
---~ return true
---~ end
---~ end
---~ end
-
---~ function nodes.do_process_glyphs(stack)
---~ local function process_list(node)
---~ local done = false
---~ if node and node[1] == 'hlist' or node[1] == "vlist" then
---~ local attributes = node[3]
---~ if attributes then
---~ if not attributes[1] then
---~ nodes.do_process_glyphs(node[8])
---~ attributes[1] = 1
---~ done = true
---~ end
---~ else
---~ nodes.do_process_glyphs(node[8])
---~ node[3] = { 1 }
---~ done = true
---~ end
---~ end
---~ return done
---~ end
---~ if not stack or #stack == 0 then
---~ return false
---~ elseif #stack == 1 then
---~ return process_list(stack[1])
---~ else
---~ local font_ids, found = { }, false
---~ for _, node in ipairs(stack) do
---~ if node and node[1] == 'glyph' then
---~ local font_id = node[5]
---~ local tfmdata = fonts.tfm.id[font_id] -- we can use fonts.tfm.processor_id
---~ if tfmdata and tfmdata.shared and tfmdata.shared.processors then
---~ font_ids[font_id], found = tfmdata.shared.processors, true
---~ end
---~ end
---~ end
---~ if not found then
---~ return false
---~ else
---~ -- we need func to report a 'done'
---~ local done = false
---~ for font_id, font_func in pairs(font_ids) do
---~ for _, func in pairs(font_func) do -- per font
---~ local i = 1
---~ while true do
---~ local node = stack[i]
---~ if node and node[1] == 'glyph' and node[5] == font_id then
---~ i = func(stack,i,node)
---~ done = true
---~ end
---~ if i < #stack then
---~ i = i + 1
---~ else
---~ break
---~ end
---~ end
---~ end
---~ end
---~ for _, node in ipairs(stack) do
---~ if node then
---~ done = done or process_list(node)
---~ end
---~ end
---~ return done
---~ end
---~ end
---~ end
-
---~ function nodes.process_glyphs(t,...)
---~ input.start_timing(nodes)
---~ local done = nodes.do_process_glyphs(t)
---~ if done then
---~ t = nodes.collapse(t)
---~ end
---~ input.stop_timing(nodes)
---~ nodes.report(t,done)
---~ if done then
---~ return t
---~ else
---~ return true
---~ end
---~ end
-
---~ function nodes.do_process_glyphs(stack)
---~ local function process_list(node)
---~ local done = false
---~ if node and node[1] == 'hist' or node[1] == "vlist" then
---~ local attributes = node[3]
---~ if attributes then
---~ if attributes[1] then
---~ else
---~ local content = node[8]
---~ if type(content) == "table" then
---~ nodes.do_process_glyphs(content)
---~ end
---~ attributes[1] = 1
---~ done = true
---~ end
---~ else
---~ nodes.do_process_glyphs(node[8])
---~ node[3] = { 1 }
---~ done = true
---~ end
---~ end
---~ return done
---~ end
---~ if not stack or #stack == 0 then
---~ return false
---~ elseif #stack == 1 then
---~ return process_list(stack[1])
---~ else
---~ local font_ids, found = { }, false
---~ for _, node in ipairs(stack) do
---~ if node and node[1] == 'glyph' then
---~ local font_id = node[5]
---~ local tfmdata = fonts.tfm.id[font_id] -- we can use fonts.tfm.processor_id
---~ if tfmdata and tfmdata.shared and tfmdata.shared.processors then
---~ font_ids[font_id], found = tfmdata.shared.processors, true
---~ end
---~ end
---~ end
---~ if not found then
---~ return false
---~ else
---~ -- we need func to report a 'done'
---~ local done = false
---~ for font_id, font_func in pairs(font_ids) do
---~ for _, func in pairs(font_func) do -- per font
---~ local i = 1
---~ while true do
---~ local node = stack[i]
---~ if node and node[1] == 'glyph' and node[5] == font_id then
---~ i = func(stack,i,node)
---~ done = true
---~ end
---~ if i < #stack then
---~ i = i + 1
---~ else
---~ break
---~ end
---~ end
---~ end
---~ end
---~ for _, node in ipairs(stack) do
---~ if node then
---~ done = done or process_list(node)
---~ end
---~ end
---~ return done
---~ end
---~ end
---~ end
-
---~ function nodes.process_glyphs(t,...)
---~ if status.output_active then
---~ return true
---~ else
---~ input.start_timing(nodes)
---~ local done = nodes.do_process_glyphs(t)
---~ if done then
---~ t = nodes.collapse(t)
---~ end
---~ input.stop_timing(nodes)
---~ nodes.report(t,done)
---~ if done then
---~ return t
---~ else
---~ return true
---~ end
---~ end
---~ end
-
---~ do
-
---~ local function do_process_glyphs(stack)
---~ if not stack or #stack == 0 then
---~ return false
---~ elseif #stack == 1 and stack[1][1] ~= 'glyph' then
---~ return false
---~ else
---~ local font_ids, found = { }, false
---~ local fti = fonts.tfm.id
---~ for _, node in ipairs(stack) do
---~ if node and node[1] == 'glyph' then
---~ local font_id = node[5]
---~ local tfmdata = fti[font_id] -- we can use fonts.tfm.processor_id
---~ if tfmdata and tfmdata.shared and tfmdata.shared.processors then
---~ font_ids[font_id], found = tfmdata.shared.processors, true
---~ end
---~ end
---~ end
---~ if not found then
---~ return false
---~ else
---~ -- we need func to report a 'done'
---~ local done = false
---~ for font_id, font_func in pairs(font_ids) do
---~ for _, func in pairs(font_func) do -- per font
---~ local i = 1
---~ while true do
---~ local node = stack[i]
---~ if node and node[1] == 'glyph' and node[5] == font_id then
---~ i = func(stack,i,node)
---~ done = true
---~ end
---~ if i < #stack then
---~ i = i + 1
---~ else
---~ break
---~ end
---~ end
---~ end
---~ end
---~ for _, node in ipairs(stack) do
---~ if node then
---~ done = done or process_list(node)
---~ end
---~ end
---~ return done
---~ end
---~ end
---~ end
-
---~ local function do_collapse_glyphs(stack,existing_t)
---~ if stack then
---~ local t = existing_t or { }
---~ for _, node in pairs(stack) do
---~ if node then
---~ if node[3] then node[3][1] = nil end -- remove status bit / 1 sec faster on 15 sec
---~ if node[1] == 'inline' then
---~ local nodes = node[4]
---~ if #nodes == 1 then
---~ t[#t+1] = nodes[1]
---~ else
---~ do_collapse_glyphs(nodes,t)
---~ end
---~ else
---~ t[#t+1] = node
---~ end
---~ else
---~ -- deleted node
---~ end
---~ end
---~ return t
---~ else
---~ return stack
---~ end
---~ end
-
---~ function nodes.process_glyphs(t,...)
---~ --~ print(...)
---~ if status.output_active then -- not ok, we need a generic blocker, pagebody ! / attr tex.attibutes
---~ return true
---~ else
---~ input.start_timing(nodes)
---~ local done = do_process_glyphs(t)
---~ if done then
---~ t = do_collapse_glyphs(t)
---~ end
---~ input.stop_timing(nodes)
---~ nodes.report(t,done)
---~ if done then
---~ --~ texio.write_nl("RETURNING PROCESSED LIST")
---~ return t
---~ else
---~ --~ texio.write_nl("RETURNING SIGNAL")
---~ return true
---~ end
---~ end
---~ end
-
---~ end