summaryrefslogtreecommitdiff
path: root/tex/context/base/node-fin.lua
diff options
context:
space:
mode:
Diffstat (limited to 'tex/context/base/node-fin.lua')
-rw-r--r--tex/context/base/node-fin.lua2444
1 files changed, 1222 insertions, 1222 deletions
diff --git a/tex/context/base/node-fin.lua b/tex/context/base/node-fin.lua
index 2e62ebcb5..e95725d29 100644
--- a/tex/context/base/node-fin.lua
+++ b/tex/context/base/node-fin.lua
@@ -1,1222 +1,1222 @@
-if not modules then modules = { } end modules ['node-fin'] = {
- version = 1.001,
- comment = "companion to node-fin.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files",
-}
-
--- this module is being reconstructed
--- local functions, only slightly slower
-
-local next, type, format = next, type, string.format
-
-local attributes, nodes, node = attributes, nodes, node
-
-local copy_node = node.copy
-local find_tail = node.slide
-
-local nodecodes = nodes.nodecodes
-local whatcodes = nodes.whatcodes
-
-local glyph_code = nodecodes.glyph
-local disc_code = nodecodes.disc
-local glue_code = nodecodes.glue
-local rule_code = nodecodes.rule
-local whatsit_code = nodecodes.whatsit
-local hlist_code = nodecodes.hlist
-local vlist_code = nodecodes.vlist
-
-local pdfliteral_code = whatcodes.pdfliteral
-
-local states = attributes.states
-local numbers = attributes.numbers
-local a_trigger = attributes.private('trigger')
-local triggering = false
-
-local starttiming = statistics.starttiming
-local stoptiming = statistics.stoptiming
-local loadstripped = utilities.lua.loadstripped
-local unsetvalue = attributes.unsetvalue
-
--- these two will be like trackers
-
-function states.enabletriggering()
- triggering = true
-end
-function states.disabletriggering()
- triggering = false
-end
-
--- the following code is no longer needed due to the new backend
--- but we keep it around for a while as an example
---
--- states.collected = states.collected or { }
---
--- storage.register("states/collected", states.collected, "states.collected")
---
--- local collected = states.collected
---
--- function states.collect(str)
--- collected[#collected+1] = str
--- end
---
--- function states.flush()
--- if #collected > 0 then
--- for i=1,#collected do
--- context(collected[i]) -- we're in context mode anyway
--- end
--- collected = { }
--- states.collected = collected
--- end
--- end
---
--- function states.check()
--- logs.report("states",concat(collected,"\n"))
--- end
-
--- we used to do the main processor loop here and call processor for each node
--- but eventually this was too much a slow down (1 sec on 23 for 120 pages mk)
--- so that we moved looping to the processor itself; this may lead to a bit of
--- duplicate code once that we have more state handlers
-
--- local function process_attribute(head,plugin) -- head,attribute,enabled,initializer,resolver,processor,finalizer
--- local namespace = plugin.namespace
--- if namespace.enabled ~= false then -- this test will go away
--- starttiming(attributes) -- in principle we could delegate this to the main caller
--- local done, used, ok = false, nil, false
--- local attribute = namespace.attribute or numbers[plugin.name] -- todo: plugin.attribute
--- local processor = plugin.processor
--- if processor then
--- local initializer = plugin.initializer
--- local resolver = plugin.resolver
--- local inheritance = (resolver and resolver()) or nil -- -0x7FFFFFFF -- we can best use nil and skip !
--- if initializer then
--- initializer(namespace,attribute,head)
--- end
--- head, ok = processor(namespace,attribute,head,inheritance)
--- if ok then
--- local finalizer = plugin.finalizer
--- if finalizer then
--- head, ok, used = finalizer(namespace,attribute,head)
--- if used then
--- local flusher = plugin.flusher
--- if flusher then
--- head = flusher(namespace,attribute,head,used)
--- end
--- end
--- end
--- done = true
--- end
--- end
--- stoptiming(attributes)
--- return head, done
--- else
--- return head, false
--- end
--- end
---
--- function nodes.installattributehandler(plugin) -- we need to avoid this nested function
--- return function(head)
--- return process_attribute(head,plugin)
--- end
--- end
-
--- An experiment: lean and mean functions. It is not really faster but
--- with upcoming functionality it might make a difference, e.g. features
--- like 'casing' and 'italics' can be called a lot so there it makes sense.
-
-nodes.plugindata = nil
-
-local template = [[
-local plugin = nodes.plugindata
-local starttiming = statistics.starttiming
-local stoptiming = statistics.stoptiming
-local namespace = plugin.namespace
-local attribute = namespace.attribute or attributes.numbers[plugin.name]
-local processor = plugin.processor
-local initializer = plugin.initializer
-local resolver = plugin.resolver
-local finalizer = plugin.finalizer
-local flusher = plugin.flusher
-if not processor then
- return function(head)
- return head, false
- end
-elseif initializer or finalizer or resolver then
- return function(head)
- starttiming(attributes)
- local done, used, ok = false, nil, false
- local inheritance = (resolver and resolver()) or nil -- -0x7FFFFFFF -- we can best use nil and skip !
- if initializer then
- initializer(namespace,attribute,head)
- end
- head, ok = processor(namespace,attribute,head,inheritance)
- if ok then
- if finalizer then
- head, ok, used = finalizer(namespace,attribute,head)
- if used and flusher then
- head = flusher(namespace,attribute,head,used)
- end
- end
- done = true
- end
- stoptiming(attributes)
- return head, done
- end
-else
- return function(head)
- starttiming(attributes)
- local head, done = processor(namespace,attribute,head)
- stoptiming(attributes)
- return head, done
- end
-end
-nodes.plugindata = nil
-]]
-
-function nodes.installattributehandler(plugin)
- nodes.plugindata = plugin
- return loadstripped(template)()
-end
-
--- the injectors
-
-local insert_node_before = node.insert_before
-local insert_node_after = node.insert_after
-
-local nsdata, nsnone, nslistwise, nsforced, nsselector, nstrigger
-local current, current_selector, done = 0, 0, false -- nb, stack has a local current !
-local nsbegin, nsend
-
-function states.initialize(namespace,attribute,head)
- nsdata = namespace.data
- nsnone = namespace.none
- nsforced = namespace.forced
- nsselector = namespace.selector
- nslistwise = namespace.listwise
- nstrigger = triggering and namespace.triggering and a_trigger
- current = 0
- current_selector = 0
- done = false -- todo: done cleanup
- nsstep = namespace.resolve_step
- if nsstep then
- nsbegin = namespace.resolve_begin
- nsend = namespace.resolve_end
- nspush = namespace.push
- nspop = namespace.pop
- end
-end
-
-function states.finalize(namespace,attribute,head) -- is this one ok?
- if current > 0 and nsnone then
- local id = head.id
- if id == hlist_code or id == vlist_code then
- local list = head.list
- if list then
- head.list = insert_node_before(list,list,copy_node(nsnone))
- end
- else
- head = insert_node_before(head,head,copy_node(nsnone))
- end
- return head, true, true
- end
- return head, false, false
-end
-
--- disc nodes can be ignored
--- we need to deal with literals too (reset as well as oval)
--- if id == glyph_code or (id == whatsit_code and stack.subtype == pdfliteral_code) or (id == rule_code and stack.width ~= 0) or (id == glue_code and stack.leader) then
-
--- local function process(namespace,attribute,head,inheritance,default) -- one attribute
--- local stack, done = head, false
--- while stack do
--- local id = stack.id
--- if id == glyph_code or (id == rule_code and stack.width ~= 0) or (id == glue_code and stack.leader) then -- or disc_code
--- local c = stack[attribute]
--- if c then
--- if default and c == inheritance then
--- if current ~= default then
--- head = insert_node_before(head,stack,copy_node(nsdata[default]))
--- current = default
--- done = true
--- end
--- elseif current ~= c then
--- head = insert_node_before(head,stack,copy_node(nsdata[c]))
--- current = c
--- done = true
--- end
--- -- here ? compare selective
--- if id == glue_code then --leader
--- -- same as *list
--- local content = stack.leader
--- if content then
--- local savedcurrent = current
--- local ci = content.id
--- if ci == hlist_code or ci == vlist_code then
--- -- else we reset inside a box unneeded, okay, the downside is
--- -- that we trigger color in each repeated box, so there is room
--- -- for improvement here
--- current = 0
--- end
--- local ok = false
--- if nstrigger and stack[nstrigger] then
--- local outer = stack[attribute]
--- if outer ~= inheritance then
--- stack.leader, ok = process(namespace,attribute,content,inheritance,outer)
--- else
--- stack.leader, ok = process(namespace,attribute,content,inheritance,default)
--- end
--- else
--- stack.leader, ok = process(namespace,attribute,content,inheritance,default)
--- end
--- current = savedcurrent
--- done = done or ok
--- end
--- end
--- elseif default and inheritance then
--- if current ~= default then
--- head = insert_node_before(head,stack,copy_node(nsdata[default]))
--- current = default
--- done = true
--- end
--- elseif current > 0 then
--- head = insert_node_before(head,stack,copy_node(nsnone))
--- current = 0
--- done = true
--- end
--- elseif id == hlist_code or id == vlist_code then
--- local content = stack.list
--- if content then
--- local ok = false
--- if nstrigger and stack[nstrigger] then
--- local outer = stack[attribute]
--- if outer ~= inheritance then
--- stack.list, ok = process(namespace,attribute,content,inheritance,outer)
--- else
--- stack.list, ok = process(namespace,attribute,content,inheritance,default)
--- end
--- else
--- stack.list, ok = process(namespace,attribute,content,inheritance,default)
--- end
--- done = done or ok
--- end
--- end
--- stack = stack.next
--- end
--- return head, done
--- end
-
--- local function process(namespace,attribute,head,inheritance,default) -- one attribute
--- local stack, done = head, false
-
--- local function check()
--- local c = stack[attribute]
--- if c then
--- if default and c == inheritance then
--- if current ~= default then
--- head = insert_node_before(head,stack,copy_node(nsdata[default]))
--- current = default
--- done = true
--- end
--- elseif current ~= c then
--- head = insert_node_before(head,stack,copy_node(nsdata[c]))
--- current = c
--- done = true
--- end
--- elseif default and inheritance then
--- if current ~= default then
--- head = insert_node_before(head,stack,copy_node(nsdata[default]))
--- current = default
--- done = true
--- end
--- elseif current > 0 then
--- head = insert_node_before(head,stack,copy_node(nsnone))
--- current = 0
--- done = true
--- end
--- return c
--- end
-
--- local function nested(content)
--- if nstrigger and stack[nstrigger] then
--- local outer = stack[attribute]
--- if outer ~= inheritance then
--- return process(namespace,attribute,content,inheritance,outer)
--- else
--- return process(namespace,attribute,content,inheritance,default)
--- end
--- else
--- return process(namespace,attribute,content,inheritance,default)
--- end
--- end
-
--- while stack do
--- local id = stack.id
--- if id == glyph_code then
--- check()
--- elseif id == glue_code then
--- local content = stack.leader
--- if content and check() then
--- local savedcurrent = current
--- local ci = content.id
--- if ci == hlist_code or ci == vlist_code then
--- -- else we reset inside a box unneeded, okay, the downside is
--- -- that we trigger color in each repeated box, so there is room
--- -- for improvement here
--- current = 0
--- end
-
--- local ok = false
--- stack.leader, ok = nested(content)
--- done = done or ok
-
--- current = savedcurrent
--- end
--- elseif id == hlist_code or id == vlist_code then
--- local content = stack.list
--- if content then
-
--- local ok = false
--- stack.list, ok = nested(content)
--- done = done or ok
-
--- end
--- elseif id == rule_code then
--- if stack.width ~= 0 then
--- check()
--- end
--- end
--- stack = stack.next
--- end
--- return head, done
--- end
-
--- local function process(namespace,attribute,head,inheritance,default) -- one attribute
--- local stack, done = head, false
--- while stack do
--- local id = stack.id
--- if id == glyph_code then
--- -- begin of check
--- local c = stack[attribute]
--- if c then
--- if default and c == inheritance then
--- if current ~= default then
--- head = insert_node_before(head,stack,copy_node(nsdata[default]))
--- current = default
--- done = true
--- end
--- elseif current ~= c then
--- head = insert_node_before(head,stack,copy_node(nsdata[c]))
--- current = c
--- done = true
--- end
--- elseif default and inheritance then
--- if current ~= default then
--- head = insert_node_before(head,stack,copy_node(nsdata[default]))
--- current = default
--- done = true
--- end
--- elseif current > 0 then
--- head = insert_node_before(head,stack,copy_node(nsnone))
--- current = 0
--- done = true
--- end
--- -- end of check
--- elseif id == glue_code then
--- local content = stack.leader
--- if content then
--- -- begin of check
--- local c = stack[attribute]
--- if c then
--- if default and c == inheritance then
--- if current ~= default then
--- head = insert_node_before(head,stack,copy_node(nsdata[default]))
--- current = default
--- done = true
--- end
--- elseif current ~= c then
--- head = insert_node_before(head,stack,copy_node(nsdata[c]))
--- current = c
--- done = true
--- end
--- -- begin special to this check
--- local savedcurrent = current
--- local ci = content.id
--- if ci == hlist_code or ci == vlist_code then
--- -- else we reset inside a box unneeded, okay, the downside is
--- -- that we trigger color in each repeated box, so there is room
--- -- for improvement here
--- current = 0
--- end
--- -- begin nested --
--- local ok = false
--- if nstrigger and stack[nstrigger] then
--- local outer = stack[attribute]
--- if outer ~= inheritance then
--- stack.leader, ok = process(namespace,attribute,content,inheritance,outer)
--- else
--- stack.leader, ok = process(namespace,attribute,content,inheritance,default)
--- end
--- else
--- stack.leader, ok = process(namespace,attribute,content,inheritance,default)
--- end
--- -- end nested --
--- done = done or ok
--- current = savedcurrent
--- -- end special to this check
--- elseif default and inheritance then
--- if current ~= default then
--- head = insert_node_before(head,stack,copy_node(nsdata[default]))
--- current = default
--- done = true
--- end
--- elseif current > 0 then
--- head = insert_node_before(head,stack,copy_node(nsnone))
--- current = 0
--- done = true
--- end
--- -- end of check
--- end
--- elseif id == hlist_code or id == vlist_code then
--- local content = stack.list
--- if content then
--- -- begin nested --
--- local ok
--- if nstrigger and stack[nstrigger] then
--- local outer = stack[attribute]
--- if outer ~= inheritance then
--- stack.list, ok = process(namespace,attribute,content,inheritance,outer)
--- else
--- stack.list, ok = process(namespace,attribute,content,inheritance,default)
--- end
--- else
--- stack.list, ok = process(namespace,attribute,content,inheritance,default)
--- end
--- -- end nested --
--- done = done or ok
--- end
--- elseif id == rule_code then
--- if stack.width ~= 0 then
--- -- begin of check
--- local c = stack[attribute]
--- if c then
--- if default and c == inheritance then
--- if current ~= default then
--- head = insert_node_before(head,stack,copy_node(nsdata[default]))
--- current = default
--- done = true
--- end
--- elseif current ~= c then
--- head = insert_node_before(head,stack,copy_node(nsdata[c]))
--- current = c
--- done = true
--- end
--- elseif default and inheritance then
--- if current ~= default then
--- head = insert_node_before(head,stack,copy_node(nsdata[default]))
--- current = default
--- done = true
--- end
--- elseif current > 0 then
--- head = insert_node_before(head,stack,copy_node(nsnone))
--- current = 0
--- done = true
--- end
--- -- end of check
--- end
--- end
--- stack = stack.next
--- end
--- return head, done
--- end
-
-local function process(namespace,attribute,head,inheritance,default) -- one attribute
- local stack = head
- local done = false
- local check = false
- local leader = nil
- while stack do
- local id = stack.id
- if id == glyph_code then
- check = true
- elseif id == glue_code then
- leader = stack.leader
- if leader then
- check = true
- end
- elseif id == hlist_code or id == vlist_code then
- local content = stack.list
- if content then
- -- begin nested --
- local ok
- if nstrigger and stack[nstrigger] then
- local outer = stack[attribute]
- if outer ~= inheritance then
- stack.list, ok = process(namespace,attribute,content,inheritance,outer)
- else
- stack.list, ok = process(namespace,attribute,content,inheritance,default)
- end
- else
- stack.list, ok = process(namespace,attribute,content,inheritance,default)
- end
- -- end nested --
- done = done or ok
- end
- elseif id == rule_code then
- check = stack.width ~= 0
- end
- -- much faster this way than using a check() and nested() function
- if check then
- local c = stack[attribute]
- if c then
- if default and c == inheritance then
- if current ~= default then
- head = insert_node_before(head,stack,copy_node(nsdata[default]))
- current = default
- done = true
- end
- elseif current ~= c then
- head = insert_node_before(head,stack,copy_node(nsdata[c]))
- current = c
- done = true
- end
- if leader then
- local savedcurrent = current
- local ci = leader.id
- if ci == hlist_code or ci == vlist_code then
- -- else we reset inside a box unneeded, okay, the downside is
- -- that we trigger color in each repeated box, so there is room
- -- for improvement here
- current = 0
- end
- -- begin nested --
- local ok = false
- if nstrigger and stack[nstrigger] then
- local outer = stack[attribute]
- if outer ~= inheritance then
- stack.leader, ok = process(namespace,attribute,leader,inheritance,outer)
- else
- stack.leader, ok = process(namespace,attribute,leader,inheritance,default)
- end
- else
- stack.leader, ok = process(namespace,attribute,leader,inheritance,default)
- end
- -- end nested --
- done = done or ok
- current = savedcurrent
- leader = false
- end
- elseif default and inheritance then
- if current ~= default then
- head = insert_node_before(head,stack,copy_node(nsdata[default]))
- current = default
- done = true
- end
- elseif current > 0 then
- head = insert_node_before(head,stack,copy_node(nsnone))
- current = 0
- done = true
- end
- check = false
- end
- stack = stack.next
- end
- return head, done
-end
-
-states.process = process
-
--- we can force a selector, e.g. document wide color spaces, saves a little
--- watch out, we need to check both the selector state (like colorspace) and
--- the main state (like color), otherwise we get into troubles when a selector
--- state changes while the main state stays the same (like two glyphs following
--- each other with the same color but different color spaces e.g. \showcolor)
-
--- local function selective(namespace,attribute,head,inheritance,default) -- two attributes
--- local stack, done = head, false
--- while stack do
--- local id = stack.id
--- -- we need to deal with literals too (reset as well as oval)
--- -- if id == glyph_code or (id == whatsit_code and stack.subtype == pdfliteral_code) or (id == rule_code and stack.width ~= 0) or (id == glue_code and stack.leader) then -- or disc_code
--- if id == glyph_code -- or id == disc_code
--- or (id == rule_code and stack.width ~= 0) or (id == glue_code and stack.leader) then -- or disc_code
--- local c = stack[attribute]
--- if c then
--- if default and c == inheritance then
--- if current ~= default then
--- local data = nsdata[default]
--- head = insert_node_before(head,stack,copy_node(data[nsforced or stack[nsselector] or nsselector]))
--- current = default
--- done = true
--- end
--- else
--- local s = stack[nsselector]
--- if current ~= c or current_selector ~= s then
--- local data = nsdata[c]
--- head = insert_node_before(head,stack,copy_node(data[nsforced or stack[nsselector] or nsselector]))
--- current = c
--- current_selector = s
--- done = true
--- end
--- end
--- elseif default and inheritance then
--- if current ~= default then
--- local data = nsdata[default]
--- head = insert_node_before(head,stack,copy_node(data[nsforced or stack[nsselector] or nsselector]))
--- current = default
--- done = true
--- end
--- elseif current > 0 then
--- head = insert_node_before(head,stack,copy_node(nsnone))
--- current, current_selector, done = 0, 0, true
--- end
--- if id == glue_code then -- leader
--- -- same as *list
--- local content = stack.leader
--- if content then
--- local savedcurrent = current
--- local ci = content.id
--- if ci == hlist_code or ci == vlist_code then
--- -- else we reset inside a box unneeded, okay, the downside is
--- -- that we trigger color in each repeated box, so there is room
--- -- for improvement here
--- current = 0
--- end
--- local ok = false
--- if nstrigger and stack[nstrigger] then
--- local outer = stack[attribute]
--- if outer ~= inheritance then
--- stack.leader, ok = selective(namespace,attribute,content,inheritance,outer)
--- else
--- stack.leader, ok = selective(namespace,attribute,content,inheritance,default)
--- end
--- else
--- stack.leader, ok = selective(namespace,attribute,content,inheritance,default)
--- end
--- current = savedcurrent
--- done = done or ok
--- end
--- end
--- elseif id == hlist_code or id == vlist_code then
--- local content = stack.list
--- if content then
--- local ok = false
--- if nstrigger and stack[nstrigger] then
--- local outer = stack[attribute]
--- if outer ~= inheritance then
--- stack.list, ok = selective(namespace,attribute,content,inheritance,outer)
--- else
--- stack.list, ok = selective(namespace,attribute,content,inheritance,default)
--- end
--- else
--- stack.list, ok = selective(namespace,attribute,content,inheritance,default)
--- end
--- done = done or ok
--- end
--- end
--- stack = stack.next
--- end
--- return head, done
--- end
-
--- local function selective(namespace,attribute,head,inheritance,default) -- two attributes
--- local stack, done = head, false
-
--- local function check()
--- local c = stack[attribute]
--- if c then
--- if default and c == inheritance then
--- if current ~= default then
--- local data = nsdata[default]
--- head = insert_node_before(head,stack,copy_node(data[nsforced or stack[nsselector] or nsselector]))
--- current = default
--- done = true
--- end
--- else
--- local s = stack[nsselector]
--- if current ~= c or current_selector ~= s then
--- local data = nsdata[c]
--- head = insert_node_before(head,stack,copy_node(data[nsforced or stack[nsselector] or nsselector]))
--- current = c
--- current_selector = s
--- done = true
--- end
--- end
--- elseif default and inheritance then
--- if current ~= default then
--- local data = nsdata[default]
--- head = insert_node_before(head,stack,copy_node(data[nsforced or stack[nsselector] or nsselector]))
--- current = default
--- done = true
--- end
--- elseif current > 0 then
--- head = insert_node_before(head,stack,copy_node(nsnone))
--- current, current_selector, done = 0, 0, true
--- end
--- return c
--- end
-
--- local function nested(content)
--- if nstrigger and stack[nstrigger] then
--- local outer = stack[attribute]
--- if outer ~= inheritance then
--- return selective(namespace,attribute,content,inheritance,outer)
--- else
--- return selective(namespace,attribute,content,inheritance,default)
--- end
--- else
--- return selective(namespace,attribute,content,inheritance,default)
--- end
--- end
-
--- while stack do
--- local id = stack.id
--- if id == glyph_code then
--- check()
--- elseif id == glue_code then
--- local content = stack.leader
--- if content and check() then
--- -- local savedcurrent = current
--- -- local ci = content.id
--- -- if ci == hlist_code or ci == vlist_code then
--- -- -- else we reset inside a box unneeded, okay, the downside is
--- -- -- that we trigger color in each repeated box, so there is room
--- -- -- for improvement here
--- -- current = 0
--- -- end
-
--- local ok = false
--- stack.leader, ok = nested(content)
--- done = done or ok
-
--- -- current = savedcurrent
--- end
--- elseif id == hlist_code or id == vlist_code then
--- local content = stack.list
--- if content then
-
--- local ok = false
--- stack.list, ok = nested(content)
--- done = done or ok
-
--- end
--- elseif id == rule_code then
--- if stack.width ~= 0 then
--- check()
--- end
--- end
--- stack = stack.next
--- end
--- return head, done
--- end
-
-local function selective(namespace,attribute,head,inheritance,default) -- two attributes
- local stack = head
- local done = false
- local check = false
- local leader = nil
- while stack do
- local id = stack.id
- if id == glyph_code then
- check = true
- elseif id == glue_code then
- leader = stack.leader
- if leader then
- check = true
- end
- elseif id == hlist_code or id == vlist_code then
- local content = stack.list
- if content then
- local ok = false
- -- begin nested
- if nstrigger and stack[nstrigger] then
- local outer = stack[attribute]
- if outer ~= inheritance then
- stack.list, ok = selective(namespace,attribute,content,inheritance,outer)
- else
- stack.list, ok = selective(namespace,attribute,content,inheritance,default)
- end
- else
- stack.list, ok = selective(namespace,attribute,content,inheritance,default)
- end
- -- end nested
- done = done or ok
- end
- elseif id == rule_code then
- check = stack.width ~= 0
- end
-
- if check then
- local c = stack[attribute]
- if c then
- if default and c == inheritance then
- if current ~= default then
- local data = nsdata[default]
- head = insert_node_before(head,stack,copy_node(data[nsforced or stack[nsselector] or nsselector]))
- current = default
- done = true
- end
- else
- local s = stack[nsselector]
- if current ~= c or current_selector ~= s then
- local data = nsdata[c]
- head = insert_node_before(head,stack,copy_node(data[nsforced or stack[nsselector] or nsselector]))
- current = c
- current_selector = s
- done = true
- end
- end
- if leader then
- local ok = false
- -- begin nested
- if nstrigger and stack[nstrigger] then
- local outer = stack[attribute]
- if outer ~= inheritance then
- stack.leader, ok = selective(namespace,attribute,leader,inheritance,outer)
- else
- stack.leader, ok = selective(namespace,attribute,leader,inheritance,default)
- end
- else
- stack.leader, ok = selective(namespace,attribute,leader,inheritance,default)
- end
- -- end nested
- done = done or ok
- leader = false
- end
- elseif default and inheritance then
- if current ~= default then
- local data = nsdata[default]
- head = insert_node_before(head,stack,copy_node(data[nsforced or stack[nsselector] or nsselector]))
- current = default
- done = true
- end
- elseif current > 0 then
- head = insert_node_before(head,stack,copy_node(nsnone))
- current, current_selector, done = 0, 0, true
- end
- check = false
- end
-
- stack = stack.next
- end
- return head, done
-end
-
-states.selective = selective
-
--- Ideally the next one should be merged with the previous but keeping it separate is
--- safer. We deal with two situations: efficient boxwise (layoutareas) and mixed layers
--- (as used in the stepper). In the stepper we cannot use the box branch as it involves
--- paragraph lines and then gets mixed up. A messy business (esp since we want to be
--- efficient).
---
--- Todo: make a better stacker. Keep track (in attribute) about nesting level. Not
--- entirely trivial and a generic solution is nicer (compares to the exporter).
-
--- local function stacked(namespace,attribute,head,default) -- no triggering, no inheritance, but list-wise
--- local stack, done = head, false
--- local current, depth = default or 0, 0
---
--- local function check()
--- local a = stack[attribute]
--- if a then
--- if current ~= a then
--- head = insert_node_before(head,stack,copy_node(nsdata[a]))
--- depth = depth + 1
--- current, done = a, true
--- end
--- elseif default > 0 then
--- --
--- elseif current > 0 then
--- head = insert_node_before(head,stack,copy_node(nsnone))
--- depth = depth - 1
--- current, done = 0, true
--- end
--- return a
--- end
---
--- while stack do
--- local id = stack.id
--- if id == glyph_code then
--- check()
--- elseif id == glue_code then
--- local content = stack.leader
--- if content and check() then
--- local ok = false
--- stack.leader, ok = stacked(namespace,attribute,content,current)
--- done = done or ok
--- end
--- elseif id == hlist_code or id == vlist_code then
--- local content = stack.list
--- if content then
--- -- the problem is that broken lines gets the attribute which can be a later one
--- if nslistwise then
--- local a = stack[attribute]
--- if a and current ~= a and nslistwise[a] then -- viewerlayer / needs checking, see below
--- local p = current
--- current, done = a, true
--- head = insert_node_before(head,stack,copy_node(nsdata[a]))
--- stack.list = stacked(namespace,attribute,content,current)
--- head, stack = insert_node_after(head,stack,copy_node(nsnone))
--- current = p
--- else
--- local ok = false
--- stack.list, ok = stacked(namespace,attribute,content,current)
--- done = done or ok
--- end
--- else
--- local ok = false
--- stack.list, ok = stacked(namespace,attribute,content,current)
--- done = done or ok
--- end
--- end
--- elseif id == rule_code then
--- if stack.width ~= 0 then
--- check()
--- end
--- end
--- stack = stack.next
--- end
--- while depth > 0 do
--- head = insert_node_after(head,stack,copy_node(nsnone))
--- depth = depth - 1
--- end
--- return head, done
--- end
-
-local function stacked(namespace,attribute,head,default) -- no triggering, no inheritance, but list-wise
- local stack = head
- local done = false
- local current = default or 0
- local depth = 0
- local check = false
- local leader = false
- while stack do
- local id = stack.id
- if id == glyph_code then
- check = true
- elseif id == glue_code then
- leader = stack.leader
- if leader then
- check = true
- end
- elseif id == hlist_code or id == vlist_code then
- local content = stack.list
- if content then
- -- the problem is that broken lines gets the attribute which can be a later one
- if nslistwise then
- local a = stack[attribute]
- if a and current ~= a and nslistwise[a] then -- viewerlayer / needs checking, see below
- local p = current
- current, done = a, true
- head = insert_node_before(head,stack,copy_node(nsdata[a]))
- stack.list = stacked(namespace,attribute,content,current)
- head, stack = insert_node_after(head,stack,copy_node(nsnone))
- current = p
- else
- local ok = false
- stack.list, ok = stacked(namespace,attribute,content,current)
- done = done or ok
- end
- else
- local ok = false
- stack.list, ok = stacked(namespace,attribute,content,current)
- done = done or ok
- end
- end
- elseif id == rule_code then
- check = stack.width ~= 0
- end
-
- if check then
- local a = stack[attribute]
- if a then
- if current ~= a then
- head = insert_node_before(head,stack,copy_node(nsdata[a]))
- depth = depth + 1
- current, done = a, true
- end
- if leader then
- local ok = false
- stack.leader, ok = stacked(namespace,attribute,content,current)
- done = done or ok
- leader = false
- end
- elseif default > 0 then
- --
- elseif current > 0 then
- head = insert_node_before(head,stack,copy_node(nsnone))
- depth = depth - 1
- current, done = 0, true
- end
- check = false
- end
-
- stack = stack.next
- end
- while depth > 0 do
- head = insert_node_after(head,stack,copy_node(nsnone))
- depth = depth - 1
- end
- return head, done
-end
-
-states.stacked = stacked
-
--- experimental
-
--- local function stacker(namespace,attribute,head,default) -- no triggering, no inheritance, but list-wise
--- nsbegin()
--- local current, previous, done, okay = head, head, false, false
--- local attrib = default or unsetvalue
---
--- local function check()
--- local a = current[attribute] or unsetvalue
--- if a ~= attrib then
--- local n = nsstep(a)
--- if n then
--- -- !!!! TEST CODE !!!!
--- -- head = insert_node_before(head,current,copy_node(nsdata[tonumber(n)])) -- a
--- head = insert_node_before(head,current,n) -- a
--- end
--- attrib, done, okay = a, true, true
--- end
--- return a
--- end
---
--- while current do
--- local id = current.id
--- if id == glyph_code then
--- check()
--- elseif id == glue_code then
--- local content = current.leader
--- if content and check() then
--- -- tricky as a leader has to be a list so we cannot inject before
--- local _, ok = stacker(namespace,attribute,content,attrib)
--- done = done or ok
--- end
--- elseif id == hlist_code or id == vlist_code then
--- local content = current.list
--- if not content then
--- -- skip
--- elseif nslistwise then
--- local a = current[attribute]
--- if a and attrib ~= a and nslistwise[a] then -- viewerlayer
--- done = true
--- head = insert_node_before(head,current,copy_node(nsdata[a]))
--- current.list = stacker(namespace,attribute,content,a)
--- head, current = insert_node_after(head,current,copy_node(nsnone))
--- else
--- local ok = false
--- current.list, ok = stacker(namespace,attribute,content,attrib)
--- done = done or ok
--- end
--- else
--- local ok = false
--- current.list, ok = stacker(namespace,attribute,content,default)
--- done = done or ok
--- end
--- elseif id == rule_code then
--- if current.width ~= 0 then
--- check()
--- end
--- end
--- previous = current
--- current = current.next
--- end
--- if okay then
--- local n = nsend()
--- if n then
--- -- !!!! TEST CODE !!!!
--- -- head = insert_node_after(head,previous,copy_node(nsdata[tostring(n)]))
--- head = insert_node_after(head,previous,n)
--- end
--- end
--- return head, done
--- end
-
-local function stacker(namespace,attribute,head,default) -- no triggering, no inheritance, but list-wise
- nsbegin()
- local current = head
- local previous = head
- local done = false
- local okay = false
- local attrib = default or unsetvalue
- local check = false
- local leader = false
- while current do
- local id = current.id
- if id == glyph_code then
- check = true
- elseif id == glue_code then
- leader = current.leader
- if leader then
- check = true
- end
- elseif id == hlist_code or id == vlist_code then
- local content = current.list
- if not content then
- -- skip
- elseif nslistwise then
- local a = current[attribute]
- if a and attrib ~= a and nslistwise[a] then -- viewerlayer
- done = true
- head = insert_node_before(head,current,copy_node(nsdata[a]))
- current.list = stacker(namespace,attribute,content,a)
- head, current = insert_node_after(head,current,copy_node(nsnone))
- else
- local ok = false
- current.list, ok = stacker(namespace,attribute,content,attrib)
- done = done or ok
- end
- else
- local ok = false
- current.list, ok = stacker(namespace,attribute,content,default)
- done = done or ok
- end
- elseif id == rule_code then
- check = current.width ~= 0
- end
-
- if check then
- local a = current[attribute] or unsetvalue
- if a ~= attrib then
- local n = nsstep(a)
- if n then
- -- !!!! TEST CODE !!!!
- -- head = insert_node_before(head,current,copy_node(nsdata[tonumber(n)])) -- a
- head = insert_node_before(head,current,n) -- a
- end
- attrib, done, okay = a, true, true
- if leader then
- -- tricky as a leader has to be a list so we cannot inject before
- local _, ok = stacker(namespace,attribute,leader,attrib)
- done = done or ok
- leader = false
- end
- end
- check = false
- end
-
- previous = current
- current = current.next
- end
- if okay then
- local n = nsend()
- if n then
- -- !!!! TEST CODE !!!!
- -- head = insert_node_after(head,previous,copy_node(nsdata[tostring(n)]))
- head = insert_node_after(head,previous,n)
- end
- end
- return head, done
-end
-
-states.stacker = stacker
-
--- -- --
-
-statistics.register("attribute processing time", function()
- return statistics.elapsedseconds(attributes,"front- and backend")
-end)
+if not modules then modules = { } end modules ['node-fin'] = {
+ version = 1.001,
+ comment = "companion to node-fin.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files",
+}
+
+-- this module is being reconstructed
+-- local functions, only slightly slower
+
+local next, type, format = next, type, string.format
+
+local attributes, nodes, node = attributes, nodes, node
+
+local copy_node = node.copy
+local find_tail = node.slide
+
+local nodecodes = nodes.nodecodes
+local whatcodes = nodes.whatcodes
+
+local glyph_code = nodecodes.glyph
+local disc_code = nodecodes.disc
+local glue_code = nodecodes.glue
+local rule_code = nodecodes.rule
+local whatsit_code = nodecodes.whatsit
+local hlist_code = nodecodes.hlist
+local vlist_code = nodecodes.vlist
+
+local pdfliteral_code = whatcodes.pdfliteral
+
+local states = attributes.states
+local numbers = attributes.numbers
+local a_trigger = attributes.private('trigger')
+local triggering = false
+
+local starttiming = statistics.starttiming
+local stoptiming = statistics.stoptiming
+local loadstripped = utilities.lua.loadstripped
+local unsetvalue = attributes.unsetvalue
+
+-- these two will be like trackers
+
+function states.enabletriggering()
+ triggering = true
+end
+function states.disabletriggering()
+ triggering = false
+end
+
+-- the following code is no longer needed due to the new backend
+-- but we keep it around for a while as an example
+--
+-- states.collected = states.collected or { }
+--
+-- storage.register("states/collected", states.collected, "states.collected")
+--
+-- local collected = states.collected
+--
+-- function states.collect(str)
+-- collected[#collected+1] = str
+-- end
+--
+-- function states.flush()
+-- if #collected > 0 then
+-- for i=1,#collected do
+-- context(collected[i]) -- we're in context mode anyway
+-- end
+-- collected = { }
+-- states.collected = collected
+-- end
+-- end
+--
+-- function states.check()
+-- logs.report("states",concat(collected,"\n"))
+-- end
+
+-- we used to do the main processor loop here and call processor for each node
+-- but eventually this was too much a slow down (1 sec on 23 for 120 pages mk)
+-- so that we moved looping to the processor itself; this may lead to a bit of
+-- duplicate code once that we have more state handlers
+
+-- local function process_attribute(head,plugin) -- head,attribute,enabled,initializer,resolver,processor,finalizer
+-- local namespace = plugin.namespace
+-- if namespace.enabled ~= false then -- this test will go away
+-- starttiming(attributes) -- in principle we could delegate this to the main caller
+-- local done, used, ok = false, nil, false
+-- local attribute = namespace.attribute or numbers[plugin.name] -- todo: plugin.attribute
+-- local processor = plugin.processor
+-- if processor then
+-- local initializer = plugin.initializer
+-- local resolver = plugin.resolver
+-- local inheritance = (resolver and resolver()) or nil -- -0x7FFFFFFF -- we can best use nil and skip !
+-- if initializer then
+-- initializer(namespace,attribute,head)
+-- end
+-- head, ok = processor(namespace,attribute,head,inheritance)
+-- if ok then
+-- local finalizer = plugin.finalizer
+-- if finalizer then
+-- head, ok, used = finalizer(namespace,attribute,head)
+-- if used then
+-- local flusher = plugin.flusher
+-- if flusher then
+-- head = flusher(namespace,attribute,head,used)
+-- end
+-- end
+-- end
+-- done = true
+-- end
+-- end
+-- stoptiming(attributes)
+-- return head, done
+-- else
+-- return head, false
+-- end
+-- end
+--
+-- function nodes.installattributehandler(plugin) -- we need to avoid this nested function
+-- return function(head)
+-- return process_attribute(head,plugin)
+-- end
+-- end
+
+-- An experiment: lean and mean functions. It is not really faster but
+-- with upcoming functionality it might make a difference, e.g. features
+-- like 'casing' and 'italics' can be called a lot so there it makes sense.
+
+nodes.plugindata = nil
+
+local template = [[
+local plugin = nodes.plugindata
+local starttiming = statistics.starttiming
+local stoptiming = statistics.stoptiming
+local namespace = plugin.namespace
+local attribute = namespace.attribute or attributes.numbers[plugin.name]
+local processor = plugin.processor
+local initializer = plugin.initializer
+local resolver = plugin.resolver
+local finalizer = plugin.finalizer
+local flusher = plugin.flusher
+if not processor then
+ return function(head)
+ return head, false
+ end
+elseif initializer or finalizer or resolver then
+ return function(head)
+ starttiming(attributes)
+ local done, used, ok = false, nil, false
+ local inheritance = (resolver and resolver()) or nil -- -0x7FFFFFFF -- we can best use nil and skip !
+ if initializer then
+ initializer(namespace,attribute,head)
+ end
+ head, ok = processor(namespace,attribute,head,inheritance)
+ if ok then
+ if finalizer then
+ head, ok, used = finalizer(namespace,attribute,head)
+ if used and flusher then
+ head = flusher(namespace,attribute,head,used)
+ end
+ end
+ done = true
+ end
+ stoptiming(attributes)
+ return head, done
+ end
+else
+ return function(head)
+ starttiming(attributes)
+ local head, done = processor(namespace,attribute,head)
+ stoptiming(attributes)
+ return head, done
+ end
+end
+nodes.plugindata = nil
+]]
+
+function nodes.installattributehandler(plugin)
+ nodes.plugindata = plugin
+ return loadstripped(template)()
+end
+
+-- the injectors
+
+local insert_node_before = node.insert_before
+local insert_node_after = node.insert_after
+
+local nsdata, nsnone, nslistwise, nsforced, nsselector, nstrigger
+local current, current_selector, done = 0, 0, false -- nb, stack has a local current !
+local nsbegin, nsend
+
+function states.initialize(namespace,attribute,head)
+ nsdata = namespace.data
+ nsnone = namespace.none
+ nsforced = namespace.forced
+ nsselector = namespace.selector
+ nslistwise = namespace.listwise
+ nstrigger = triggering and namespace.triggering and a_trigger
+ current = 0
+ current_selector = 0
+ done = false -- todo: done cleanup
+ nsstep = namespace.resolve_step
+ if nsstep then
+ nsbegin = namespace.resolve_begin
+ nsend = namespace.resolve_end
+ nspush = namespace.push
+ nspop = namespace.pop
+ end
+end
+
+function states.finalize(namespace,attribute,head) -- is this one ok?
+ if current > 0 and nsnone then
+ local id = head.id
+ if id == hlist_code or id == vlist_code then
+ local list = head.list
+ if list then
+ head.list = insert_node_before(list,list,copy_node(nsnone))
+ end
+ else
+ head = insert_node_before(head,head,copy_node(nsnone))
+ end
+ return head, true, true
+ end
+ return head, false, false
+end
+
+-- disc nodes can be ignored
+-- we need to deal with literals too (reset as well as oval)
+-- if id == glyph_code or (id == whatsit_code and stack.subtype == pdfliteral_code) or (id == rule_code and stack.width ~= 0) or (id == glue_code and stack.leader) then
+
+-- local function process(namespace,attribute,head,inheritance,default) -- one attribute
+-- local stack, done = head, false
+-- while stack do
+-- local id = stack.id
+-- if id == glyph_code or (id == rule_code and stack.width ~= 0) or (id == glue_code and stack.leader) then -- or disc_code
+-- local c = stack[attribute]
+-- if c then
+-- if default and c == inheritance then
+-- if current ~= default then
+-- head = insert_node_before(head,stack,copy_node(nsdata[default]))
+-- current = default
+-- done = true
+-- end
+-- elseif current ~= c then
+-- head = insert_node_before(head,stack,copy_node(nsdata[c]))
+-- current = c
+-- done = true
+-- end
+-- -- here ? compare selective
+-- if id == glue_code then --leader
+-- -- same as *list
+-- local content = stack.leader
+-- if content then
+-- local savedcurrent = current
+-- local ci = content.id
+-- if ci == hlist_code or ci == vlist_code then
+-- -- else we reset inside a box unneeded, okay, the downside is
+-- -- that we trigger color in each repeated box, so there is room
+-- -- for improvement here
+-- current = 0
+-- end
+-- local ok = false
+-- if nstrigger and stack[nstrigger] then
+-- local outer = stack[attribute]
+-- if outer ~= inheritance then
+-- stack.leader, ok = process(namespace,attribute,content,inheritance,outer)
+-- else
+-- stack.leader, ok = process(namespace,attribute,content,inheritance,default)
+-- end
+-- else
+-- stack.leader, ok = process(namespace,attribute,content,inheritance,default)
+-- end
+-- current = savedcurrent
+-- done = done or ok
+-- end
+-- end
+-- elseif default and inheritance then
+-- if current ~= default then
+-- head = insert_node_before(head,stack,copy_node(nsdata[default]))
+-- current = default
+-- done = true
+-- end
+-- elseif current > 0 then
+-- head = insert_node_before(head,stack,copy_node(nsnone))
+-- current = 0
+-- done = true
+-- end
+-- elseif id == hlist_code or id == vlist_code then
+-- local content = stack.list
+-- if content then
+-- local ok = false
+-- if nstrigger and stack[nstrigger] then
+-- local outer = stack[attribute]
+-- if outer ~= inheritance then
+-- stack.list, ok = process(namespace,attribute,content,inheritance,outer)
+-- else
+-- stack.list, ok = process(namespace,attribute,content,inheritance,default)
+-- end
+-- else
+-- stack.list, ok = process(namespace,attribute,content,inheritance,default)
+-- end
+-- done = done or ok
+-- end
+-- end
+-- stack = stack.next
+-- end
+-- return head, done
+-- end
+
+-- local function process(namespace,attribute,head,inheritance,default) -- one attribute
+-- local stack, done = head, false
+
+-- local function check()
+-- local c = stack[attribute]
+-- if c then
+-- if default and c == inheritance then
+-- if current ~= default then
+-- head = insert_node_before(head,stack,copy_node(nsdata[default]))
+-- current = default
+-- done = true
+-- end
+-- elseif current ~= c then
+-- head = insert_node_before(head,stack,copy_node(nsdata[c]))
+-- current = c
+-- done = true
+-- end
+-- elseif default and inheritance then
+-- if current ~= default then
+-- head = insert_node_before(head,stack,copy_node(nsdata[default]))
+-- current = default
+-- done = true
+-- end
+-- elseif current > 0 then
+-- head = insert_node_before(head,stack,copy_node(nsnone))
+-- current = 0
+-- done = true
+-- end
+-- return c
+-- end
+
+-- local function nested(content)
+-- if nstrigger and stack[nstrigger] then
+-- local outer = stack[attribute]
+-- if outer ~= inheritance then
+-- return process(namespace,attribute,content,inheritance,outer)
+-- else
+-- return process(namespace,attribute,content,inheritance,default)
+-- end
+-- else
+-- return process(namespace,attribute,content,inheritance,default)
+-- end
+-- end
+
+-- while stack do
+-- local id = stack.id
+-- if id == glyph_code then
+-- check()
+-- elseif id == glue_code then
+-- local content = stack.leader
+-- if content and check() then
+-- local savedcurrent = current
+-- local ci = content.id
+-- if ci == hlist_code or ci == vlist_code then
+-- -- else we reset inside a box unneeded, okay, the downside is
+-- -- that we trigger color in each repeated box, so there is room
+-- -- for improvement here
+-- current = 0
+-- end
+
+-- local ok = false
+-- stack.leader, ok = nested(content)
+-- done = done or ok
+
+-- current = savedcurrent
+-- end
+-- elseif id == hlist_code or id == vlist_code then
+-- local content = stack.list
+-- if content then
+
+-- local ok = false
+-- stack.list, ok = nested(content)
+-- done = done or ok
+
+-- end
+-- elseif id == rule_code then
+-- if stack.width ~= 0 then
+-- check()
+-- end
+-- end
+-- stack = stack.next
+-- end
+-- return head, done
+-- end
+
+-- local function process(namespace,attribute,head,inheritance,default) -- one attribute
+-- local stack, done = head, false
+-- while stack do
+-- local id = stack.id
+-- if id == glyph_code then
+-- -- begin of check
+-- local c = stack[attribute]
+-- if c then
+-- if default and c == inheritance then
+-- if current ~= default then
+-- head = insert_node_before(head,stack,copy_node(nsdata[default]))
+-- current = default
+-- done = true
+-- end
+-- elseif current ~= c then
+-- head = insert_node_before(head,stack,copy_node(nsdata[c]))
+-- current = c
+-- done = true
+-- end
+-- elseif default and inheritance then
+-- if current ~= default then
+-- head = insert_node_before(head,stack,copy_node(nsdata[default]))
+-- current = default
+-- done = true
+-- end
+-- elseif current > 0 then
+-- head = insert_node_before(head,stack,copy_node(nsnone))
+-- current = 0
+-- done = true
+-- end
+-- -- end of check
+-- elseif id == glue_code then
+-- local content = stack.leader
+-- if content then
+-- -- begin of check
+-- local c = stack[attribute]
+-- if c then
+-- if default and c == inheritance then
+-- if current ~= default then
+-- head = insert_node_before(head,stack,copy_node(nsdata[default]))
+-- current = default
+-- done = true
+-- end
+-- elseif current ~= c then
+-- head = insert_node_before(head,stack,copy_node(nsdata[c]))
+-- current = c
+-- done = true
+-- end
+-- -- begin special to this check
+-- local savedcurrent = current
+-- local ci = content.id
+-- if ci == hlist_code or ci == vlist_code then
+-- -- else we reset inside a box unneeded, okay, the downside is
+-- -- that we trigger color in each repeated box, so there is room
+-- -- for improvement here
+-- current = 0
+-- end
+-- -- begin nested --
+-- local ok = false
+-- if nstrigger and stack[nstrigger] then
+-- local outer = stack[attribute]
+-- if outer ~= inheritance then
+-- stack.leader, ok = process(namespace,attribute,content,inheritance,outer)
+-- else
+-- stack.leader, ok = process(namespace,attribute,content,inheritance,default)
+-- end
+-- else
+-- stack.leader, ok = process(namespace,attribute,content,inheritance,default)
+-- end
+-- -- end nested --
+-- done = done or ok
+-- current = savedcurrent
+-- -- end special to this check
+-- elseif default and inheritance then
+-- if current ~= default then
+-- head = insert_node_before(head,stack,copy_node(nsdata[default]))
+-- current = default
+-- done = true
+-- end
+-- elseif current > 0 then
+-- head = insert_node_before(head,stack,copy_node(nsnone))
+-- current = 0
+-- done = true
+-- end
+-- -- end of check
+-- end
+-- elseif id == hlist_code or id == vlist_code then
+-- local content = stack.list
+-- if content then
+-- -- begin nested --
+-- local ok
+-- if nstrigger and stack[nstrigger] then
+-- local outer = stack[attribute]
+-- if outer ~= inheritance then
+-- stack.list, ok = process(namespace,attribute,content,inheritance,outer)
+-- else
+-- stack.list, ok = process(namespace,attribute,content,inheritance,default)
+-- end
+-- else
+-- stack.list, ok = process(namespace,attribute,content,inheritance,default)
+-- end
+-- -- end nested --
+-- done = done or ok
+-- end
+-- elseif id == rule_code then
+-- if stack.width ~= 0 then
+-- -- begin of check
+-- local c = stack[attribute]
+-- if c then
+-- if default and c == inheritance then
+-- if current ~= default then
+-- head = insert_node_before(head,stack,copy_node(nsdata[default]))
+-- current = default
+-- done = true
+-- end
+-- elseif current ~= c then
+-- head = insert_node_before(head,stack,copy_node(nsdata[c]))
+-- current = c
+-- done = true
+-- end
+-- elseif default and inheritance then
+-- if current ~= default then
+-- head = insert_node_before(head,stack,copy_node(nsdata[default]))
+-- current = default
+-- done = true
+-- end
+-- elseif current > 0 then
+-- head = insert_node_before(head,stack,copy_node(nsnone))
+-- current = 0
+-- done = true
+-- end
+-- -- end of check
+-- end
+-- end
+-- stack = stack.next
+-- end
+-- return head, done
+-- end
+
+local function process(namespace,attribute,head,inheritance,default) -- one attribute
+ local stack = head
+ local done = false
+ local check = false
+ local leader = nil
+ while stack do
+ local id = stack.id
+ if id == glyph_code then
+ check = true
+ elseif id == glue_code then
+ leader = stack.leader
+ if leader then
+ check = true
+ end
+ elseif id == hlist_code or id == vlist_code then
+ local content = stack.list
+ if content then
+ -- begin nested --
+ local ok
+ if nstrigger and stack[nstrigger] then
+ local outer = stack[attribute]
+ if outer ~= inheritance then
+ stack.list, ok = process(namespace,attribute,content,inheritance,outer)
+ else
+ stack.list, ok = process(namespace,attribute,content,inheritance,default)
+ end
+ else
+ stack.list, ok = process(namespace,attribute,content,inheritance,default)
+ end
+ -- end nested --
+ done = done or ok
+ end
+ elseif id == rule_code then
+ check = stack.width ~= 0
+ end
+ -- much faster this way than using a check() and nested() function
+ if check then
+ local c = stack[attribute]
+ if c then
+ if default and c == inheritance then
+ if current ~= default then
+ head = insert_node_before(head,stack,copy_node(nsdata[default]))
+ current = default
+ done = true
+ end
+ elseif current ~= c then
+ head = insert_node_before(head,stack,copy_node(nsdata[c]))
+ current = c
+ done = true
+ end
+ if leader then
+ local savedcurrent = current
+ local ci = leader.id
+ if ci == hlist_code or ci == vlist_code then
+ -- else we reset inside a box unneeded, okay, the downside is
+ -- that we trigger color in each repeated box, so there is room
+ -- for improvement here
+ current = 0
+ end
+ -- begin nested --
+ local ok = false
+ if nstrigger and stack[nstrigger] then
+ local outer = stack[attribute]
+ if outer ~= inheritance then
+ stack.leader, ok = process(namespace,attribute,leader,inheritance,outer)
+ else
+ stack.leader, ok = process(namespace,attribute,leader,inheritance,default)
+ end
+ else
+ stack.leader, ok = process(namespace,attribute,leader,inheritance,default)
+ end
+ -- end nested --
+ done = done or ok
+ current = savedcurrent
+ leader = false
+ end
+ elseif default and inheritance then
+ if current ~= default then
+ head = insert_node_before(head,stack,copy_node(nsdata[default]))
+ current = default
+ done = true
+ end
+ elseif current > 0 then
+ head = insert_node_before(head,stack,copy_node(nsnone))
+ current = 0
+ done = true
+ end
+ check = false
+ end
+ stack = stack.next
+ end
+ return head, done
+end
+
+states.process = process
+
+-- we can force a selector, e.g. document wide color spaces, saves a little
+-- watch out, we need to check both the selector state (like colorspace) and
+-- the main state (like color), otherwise we get into troubles when a selector
+-- state changes while the main state stays the same (like two glyphs following
+-- each other with the same color but different color spaces e.g. \showcolor)
+
+-- local function selective(namespace,attribute,head,inheritance,default) -- two attributes
+-- local stack, done = head, false
+-- while stack do
+-- local id = stack.id
+-- -- we need to deal with literals too (reset as well as oval)
+-- -- if id == glyph_code or (id == whatsit_code and stack.subtype == pdfliteral_code) or (id == rule_code and stack.width ~= 0) or (id == glue_code and stack.leader) then -- or disc_code
+-- if id == glyph_code -- or id == disc_code
+-- or (id == rule_code and stack.width ~= 0) or (id == glue_code and stack.leader) then -- or disc_code
+-- local c = stack[attribute]
+-- if c then
+-- if default and c == inheritance then
+-- if current ~= default then
+-- local data = nsdata[default]
+-- head = insert_node_before(head,stack,copy_node(data[nsforced or stack[nsselector] or nsselector]))
+-- current = default
+-- done = true
+-- end
+-- else
+-- local s = stack[nsselector]
+-- if current ~= c or current_selector ~= s then
+-- local data = nsdata[c]
+-- head = insert_node_before(head,stack,copy_node(data[nsforced or stack[nsselector] or nsselector]))
+-- current = c
+-- current_selector = s
+-- done = true
+-- end
+-- end
+-- elseif default and inheritance then
+-- if current ~= default then
+-- local data = nsdata[default]
+-- head = insert_node_before(head,stack,copy_node(data[nsforced or stack[nsselector] or nsselector]))
+-- current = default
+-- done = true
+-- end
+-- elseif current > 0 then
+-- head = insert_node_before(head,stack,copy_node(nsnone))
+-- current, current_selector, done = 0, 0, true
+-- end
+-- if id == glue_code then -- leader
+-- -- same as *list
+-- local content = stack.leader
+-- if content then
+-- local savedcurrent = current
+-- local ci = content.id
+-- if ci == hlist_code or ci == vlist_code then
+-- -- else we reset inside a box unneeded, okay, the downside is
+-- -- that we trigger color in each repeated box, so there is room
+-- -- for improvement here
+-- current = 0
+-- end
+-- local ok = false
+-- if nstrigger and stack[nstrigger] then
+-- local outer = stack[attribute]
+-- if outer ~= inheritance then
+-- stack.leader, ok = selective(namespace,attribute,content,inheritance,outer)
+-- else
+-- stack.leader, ok = selective(namespace,attribute,content,inheritance,default)
+-- end
+-- else
+-- stack.leader, ok = selective(namespace,attribute,content,inheritance,default)
+-- end
+-- current = savedcurrent
+-- done = done or ok
+-- end
+-- end
+-- elseif id == hlist_code or id == vlist_code then
+-- local content = stack.list
+-- if content then
+-- local ok = false
+-- if nstrigger and stack[nstrigger] then
+-- local outer = stack[attribute]
+-- if outer ~= inheritance then
+-- stack.list, ok = selective(namespace,attribute,content,inheritance,outer)
+-- else
+-- stack.list, ok = selective(namespace,attribute,content,inheritance,default)
+-- end
+-- else
+-- stack.list, ok = selective(namespace,attribute,content,inheritance,default)
+-- end
+-- done = done or ok
+-- end
+-- end
+-- stack = stack.next
+-- end
+-- return head, done
+-- end
+
+-- local function selective(namespace,attribute,head,inheritance,default) -- two attributes
+-- local stack, done = head, false
+
+-- local function check()
+-- local c = stack[attribute]
+-- if c then
+-- if default and c == inheritance then
+-- if current ~= default then
+-- local data = nsdata[default]
+-- head = insert_node_before(head,stack,copy_node(data[nsforced or stack[nsselector] or nsselector]))
+-- current = default
+-- done = true
+-- end
+-- else
+-- local s = stack[nsselector]
+-- if current ~= c or current_selector ~= s then
+-- local data = nsdata[c]
+-- head = insert_node_before(head,stack,copy_node(data[nsforced or stack[nsselector] or nsselector]))
+-- current = c
+-- current_selector = s
+-- done = true
+-- end
+-- end
+-- elseif default and inheritance then
+-- if current ~= default then
+-- local data = nsdata[default]
+-- head = insert_node_before(head,stack,copy_node(data[nsforced or stack[nsselector] or nsselector]))
+-- current = default
+-- done = true
+-- end
+-- elseif current > 0 then
+-- head = insert_node_before(head,stack,copy_node(nsnone))
+-- current, current_selector, done = 0, 0, true
+-- end
+-- return c
+-- end
+
+-- local function nested(content)
+-- if nstrigger and stack[nstrigger] then
+-- local outer = stack[attribute]
+-- if outer ~= inheritance then
+-- return selective(namespace,attribute,content,inheritance,outer)
+-- else
+-- return selective(namespace,attribute,content,inheritance,default)
+-- end
+-- else
+-- return selective(namespace,attribute,content,inheritance,default)
+-- end
+-- end
+
+-- while stack do
+-- local id = stack.id
+-- if id == glyph_code then
+-- check()
+-- elseif id == glue_code then
+-- local content = stack.leader
+-- if content and check() then
+-- -- local savedcurrent = current
+-- -- local ci = content.id
+-- -- if ci == hlist_code or ci == vlist_code then
+-- -- -- else we reset inside a box unneeded, okay, the downside is
+-- -- -- that we trigger color in each repeated box, so there is room
+-- -- -- for improvement here
+-- -- current = 0
+-- -- end
+
+-- local ok = false
+-- stack.leader, ok = nested(content)
+-- done = done or ok
+
+-- -- current = savedcurrent
+-- end
+-- elseif id == hlist_code or id == vlist_code then
+-- local content = stack.list
+-- if content then
+
+-- local ok = false
+-- stack.list, ok = nested(content)
+-- done = done or ok
+
+-- end
+-- elseif id == rule_code then
+-- if stack.width ~= 0 then
+-- check()
+-- end
+-- end
+-- stack = stack.next
+-- end
+-- return head, done
+-- end
+
+local function selective(namespace,attribute,head,inheritance,default) -- two attributes
+ local stack = head
+ local done = false
+ local check = false
+ local leader = nil
+ while stack do
+ local id = stack.id
+ if id == glyph_code then
+ check = true
+ elseif id == glue_code then
+ leader = stack.leader
+ if leader then
+ check = true
+ end
+ elseif id == hlist_code or id == vlist_code then
+ local content = stack.list
+ if content then
+ local ok = false
+ -- begin nested
+ if nstrigger and stack[nstrigger] then
+ local outer = stack[attribute]
+ if outer ~= inheritance then
+ stack.list, ok = selective(namespace,attribute,content,inheritance,outer)
+ else
+ stack.list, ok = selective(namespace,attribute,content,inheritance,default)
+ end
+ else
+ stack.list, ok = selective(namespace,attribute,content,inheritance,default)
+ end
+ -- end nested
+ done = done or ok
+ end
+ elseif id == rule_code then
+ check = stack.width ~= 0
+ end
+
+ if check then
+ local c = stack[attribute]
+ if c then
+ if default and c == inheritance then
+ if current ~= default then
+ local data = nsdata[default]
+ head = insert_node_before(head,stack,copy_node(data[nsforced or stack[nsselector] or nsselector]))
+ current = default
+ done = true
+ end
+ else
+ local s = stack[nsselector]
+ if current ~= c or current_selector ~= s then
+ local data = nsdata[c]
+ head = insert_node_before(head,stack,copy_node(data[nsforced or stack[nsselector] or nsselector]))
+ current = c
+ current_selector = s
+ done = true
+ end
+ end
+ if leader then
+ local ok = false
+ -- begin nested
+ if nstrigger and stack[nstrigger] then
+ local outer = stack[attribute]
+ if outer ~= inheritance then
+ stack.leader, ok = selective(namespace,attribute,leader,inheritance,outer)
+ else
+ stack.leader, ok = selective(namespace,attribute,leader,inheritance,default)
+ end
+ else
+ stack.leader, ok = selective(namespace,attribute,leader,inheritance,default)
+ end
+ -- end nested
+ done = done or ok
+ leader = false
+ end
+ elseif default and inheritance then
+ if current ~= default then
+ local data = nsdata[default]
+ head = insert_node_before(head,stack,copy_node(data[nsforced or stack[nsselector] or nsselector]))
+ current = default
+ done = true
+ end
+ elseif current > 0 then
+ head = insert_node_before(head,stack,copy_node(nsnone))
+ current, current_selector, done = 0, 0, true
+ end
+ check = false
+ end
+
+ stack = stack.next
+ end
+ return head, done
+end
+
+states.selective = selective
+
+-- Ideally the next one should be merged with the previous but keeping it separate is
+-- safer. We deal with two situations: efficient boxwise (layoutareas) and mixed layers
+-- (as used in the stepper). In the stepper we cannot use the box branch as it involves
+-- paragraph lines and then gets mixed up. A messy business (esp since we want to be
+-- efficient).
+--
+-- Todo: make a better stacker. Keep track (in attribute) about nesting level. Not
+-- entirely trivial and a generic solution is nicer (compares to the exporter).
+
+-- local function stacked(namespace,attribute,head,default) -- no triggering, no inheritance, but list-wise
+-- local stack, done = head, false
+-- local current, depth = default or 0, 0
+--
+-- local function check()
+-- local a = stack[attribute]
+-- if a then
+-- if current ~= a then
+-- head = insert_node_before(head,stack,copy_node(nsdata[a]))
+-- depth = depth + 1
+-- current, done = a, true
+-- end
+-- elseif default > 0 then
+-- --
+-- elseif current > 0 then
+-- head = insert_node_before(head,stack,copy_node(nsnone))
+-- depth = depth - 1
+-- current, done = 0, true
+-- end
+-- return a
+-- end
+--
+-- while stack do
+-- local id = stack.id
+-- if id == glyph_code then
+-- check()
+-- elseif id == glue_code then
+-- local content = stack.leader
+-- if content and check() then
+-- local ok = false
+-- stack.leader, ok = stacked(namespace,attribute,content,current)
+-- done = done or ok
+-- end
+-- elseif id == hlist_code or id == vlist_code then
+-- local content = stack.list
+-- if content then
+-- -- the problem is that broken lines gets the attribute which can be a later one
+-- if nslistwise then
+-- local a = stack[attribute]
+-- if a and current ~= a and nslistwise[a] then -- viewerlayer / needs checking, see below
+-- local p = current
+-- current, done = a, true
+-- head = insert_node_before(head,stack,copy_node(nsdata[a]))
+-- stack.list = stacked(namespace,attribute,content,current)
+-- head, stack = insert_node_after(head,stack,copy_node(nsnone))
+-- current = p
+-- else
+-- local ok = false
+-- stack.list, ok = stacked(namespace,attribute,content,current)
+-- done = done or ok
+-- end
+-- else
+-- local ok = false
+-- stack.list, ok = stacked(namespace,attribute,content,current)
+-- done = done or ok
+-- end
+-- end
+-- elseif id == rule_code then
+-- if stack.width ~= 0 then
+-- check()
+-- end
+-- end
+-- stack = stack.next
+-- end
+-- while depth > 0 do
+-- head = insert_node_after(head,stack,copy_node(nsnone))
+-- depth = depth - 1
+-- end
+-- return head, done
+-- end
+
+local function stacked(namespace,attribute,head,default) -- no triggering, no inheritance, but list-wise
+ local stack = head
+ local done = false
+ local current = default or 0
+ local depth = 0
+ local check = false
+ local leader = false
+ while stack do
+ local id = stack.id
+ if id == glyph_code then
+ check = true
+ elseif id == glue_code then
+ leader = stack.leader
+ if leader then
+ check = true
+ end
+ elseif id == hlist_code or id == vlist_code then
+ local content = stack.list
+ if content then
+ -- the problem is that broken lines gets the attribute which can be a later one
+ if nslistwise then
+ local a = stack[attribute]
+ if a and current ~= a and nslistwise[a] then -- viewerlayer / needs checking, see below
+ local p = current
+ current, done = a, true
+ head = insert_node_before(head,stack,copy_node(nsdata[a]))
+ stack.list = stacked(namespace,attribute,content,current)
+ head, stack = insert_node_after(head,stack,copy_node(nsnone))
+ current = p
+ else
+ local ok = false
+ stack.list, ok = stacked(namespace,attribute,content,current)
+ done = done or ok
+ end
+ else
+ local ok = false
+ stack.list, ok = stacked(namespace,attribute,content,current)
+ done = done or ok
+ end
+ end
+ elseif id == rule_code then
+ check = stack.width ~= 0
+ end
+
+ if check then
+ local a = stack[attribute]
+ if a then
+ if current ~= a then
+ head = insert_node_before(head,stack,copy_node(nsdata[a]))
+ depth = depth + 1
+ current, done = a, true
+ end
+ if leader then
+ local ok = false
+ stack.leader, ok = stacked(namespace,attribute,content,current)
+ done = done or ok
+ leader = false
+ end
+ elseif default > 0 then
+ --
+ elseif current > 0 then
+ head = insert_node_before(head,stack,copy_node(nsnone))
+ depth = depth - 1
+ current, done = 0, true
+ end
+ check = false
+ end
+
+ stack = stack.next
+ end
+ while depth > 0 do
+ head = insert_node_after(head,stack,copy_node(nsnone))
+ depth = depth - 1
+ end
+ return head, done
+end
+
+states.stacked = stacked
+
+-- experimental
+
+-- local function stacker(namespace,attribute,head,default) -- no triggering, no inheritance, but list-wise
+-- nsbegin()
+-- local current, previous, done, okay = head, head, false, false
+-- local attrib = default or unsetvalue
+--
+-- local function check()
+-- local a = current[attribute] or unsetvalue
+-- if a ~= attrib then
+-- local n = nsstep(a)
+-- if n then
+-- -- !!!! TEST CODE !!!!
+-- -- head = insert_node_before(head,current,copy_node(nsdata[tonumber(n)])) -- a
+-- head = insert_node_before(head,current,n) -- a
+-- end
+-- attrib, done, okay = a, true, true
+-- end
+-- return a
+-- end
+--
+-- while current do
+-- local id = current.id
+-- if id == glyph_code then
+-- check()
+-- elseif id == glue_code then
+-- local content = current.leader
+-- if content and check() then
+-- -- tricky as a leader has to be a list so we cannot inject before
+-- local _, ok = stacker(namespace,attribute,content,attrib)
+-- done = done or ok
+-- end
+-- elseif id == hlist_code or id == vlist_code then
+-- local content = current.list
+-- if not content then
+-- -- skip
+-- elseif nslistwise then
+-- local a = current[attribute]
+-- if a and attrib ~= a and nslistwise[a] then -- viewerlayer
+-- done = true
+-- head = insert_node_before(head,current,copy_node(nsdata[a]))
+-- current.list = stacker(namespace,attribute,content,a)
+-- head, current = insert_node_after(head,current,copy_node(nsnone))
+-- else
+-- local ok = false
+-- current.list, ok = stacker(namespace,attribute,content,attrib)
+-- done = done or ok
+-- end
+-- else
+-- local ok = false
+-- current.list, ok = stacker(namespace,attribute,content,default)
+-- done = done or ok
+-- end
+-- elseif id == rule_code then
+-- if current.width ~= 0 then
+-- check()
+-- end
+-- end
+-- previous = current
+-- current = current.next
+-- end
+-- if okay then
+-- local n = nsend()
+-- if n then
+-- -- !!!! TEST CODE !!!!
+-- -- head = insert_node_after(head,previous,copy_node(nsdata[tostring(n)]))
+-- head = insert_node_after(head,previous,n)
+-- end
+-- end
+-- return head, done
+-- end
+
+local function stacker(namespace,attribute,head,default) -- no triggering, no inheritance, but list-wise
+ nsbegin()
+ local current = head
+ local previous = head
+ local done = false
+ local okay = false
+ local attrib = default or unsetvalue
+ local check = false
+ local leader = false
+ while current do
+ local id = current.id
+ if id == glyph_code then
+ check = true
+ elseif id == glue_code then
+ leader = current.leader
+ if leader then
+ check = true
+ end
+ elseif id == hlist_code or id == vlist_code then
+ local content = current.list
+ if not content then
+ -- skip
+ elseif nslistwise then
+ local a = current[attribute]
+ if a and attrib ~= a and nslistwise[a] then -- viewerlayer
+ done = true
+ head = insert_node_before(head,current,copy_node(nsdata[a]))
+ current.list = stacker(namespace,attribute,content,a)
+ head, current = insert_node_after(head,current,copy_node(nsnone))
+ else
+ local ok = false
+ current.list, ok = stacker(namespace,attribute,content,attrib)
+ done = done or ok
+ end
+ else
+ local ok = false
+ current.list, ok = stacker(namespace,attribute,content,default)
+ done = done or ok
+ end
+ elseif id == rule_code then
+ check = current.width ~= 0
+ end
+
+ if check then
+ local a = current[attribute] or unsetvalue
+ if a ~= attrib then
+ local n = nsstep(a)
+ if n then
+ -- !!!! TEST CODE !!!!
+ -- head = insert_node_before(head,current,copy_node(nsdata[tonumber(n)])) -- a
+ head = insert_node_before(head,current,n) -- a
+ end
+ attrib, done, okay = a, true, true
+ if leader then
+ -- tricky as a leader has to be a list so we cannot inject before
+ local _, ok = stacker(namespace,attribute,leader,attrib)
+ done = done or ok
+ leader = false
+ end
+ end
+ check = false
+ end
+
+ previous = current
+ current = current.next
+ end
+ if okay then
+ local n = nsend()
+ if n then
+ -- !!!! TEST CODE !!!!
+ -- head = insert_node_after(head,previous,copy_node(nsdata[tostring(n)]))
+ head = insert_node_after(head,previous,n)
+ end
+ end
+ return head, done
+end
+
+states.stacker = stacker
+
+-- -- --
+
+statistics.register("attribute processing time", function()
+ return statistics.elapsedseconds(attributes,"front- and backend")
+end)