summaryrefslogtreecommitdiff
path: root/tex/context/base/node-ppt.lua
diff options
context:
space:
mode:
Diffstat (limited to 'tex/context/base/node-ppt.lua')
-rw-r--r--tex/context/base/node-ppt.lua476
1 files changed, 476 insertions, 0 deletions
diff --git a/tex/context/base/node-ppt.lua b/tex/context/base/node-ppt.lua
new file mode 100644
index 000000000..c8cba8566
--- /dev/null
+++ b/tex/context/base/node-ppt.lua
@@ -0,0 +1,476 @@
+if not modules then modules = { } end modules ['node-ppt'] = {
+ version = 1.001,
+ comment = "companion to node-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- This is all very exeperimental and likely to change.
+
+local next, type, unpack, load = next, type, table.unpack, load
+
+local serialize = table.serialize
+local formatters = string.formatters
+
+local report = logs.reporter("properties")
+local report_setting = logs.reporter("properties","setting")
+local trace_setting = false trackers.register("properties.setting", function(v) trace_setting = v end)
+
+-- report("using experimental properties")
+
+local nuts = nodes.nuts
+local tonut = nuts.tonut
+local tonode = nuts.tonode
+local getid = nuts.getid
+local getnext = nuts.getnext
+local getprev = nuts.getprev
+local getsubtype = nuts.getsubtype
+local getfield = nuts.getfield
+local setfield = nuts.setfield
+local getlist = nuts.getlist
+local flushnode = nuts.flush
+local removenode = nuts.remove
+local traverse = nuts.traverse
+local traverse_id = nuts.traverse_id
+
+local nodecodes = nodes.nodecodes
+local whatsitcodes = nodes.whatsitcodes
+
+local whatsit_code = nodecodes.whatsit
+local hlist_code = nodecodes.hlist
+local vlist_code = nodecodes.vlist
+local userdefined_code = whatsitcodes.userdefined
+local localpar_code = whatsitcodes.localpar
+
+local nodepool = nodes.pool
+local new_usernumber = nodepool.usernumber
+
+local nutpool = nuts.pool
+local nut_usernumber = nutpool.usernumber
+
+local variables = interfaces.variables
+local v_before = variables.before
+local v_after = variables.after
+local v_here = variables.here
+
+local cache = { }
+local nofslots = 0
+local property_id = nodepool.userids["property"]
+
+local properties = nodes.properties if not properties then return end -- temp
+local propertydata = properties.data
+
+local starttiming = statistics.starttiming
+local stoptiming = statistics.stoptiming
+
+if not propertydata then
+ return
+end
+
+-- management
+
+local function register(where,data,...)
+ if not data then
+ data = where
+ where = v_after
+ end
+ if data then
+ local data = { where, data, ... }
+ nofslots = nofslots + 1
+ if nofslots > 1 then
+ cache[nofslots] = data
+ else
+ -- report("restarting attacher")
+ cache = { data } -- also forces collection
+ end
+ return new_usernumber(property_id,nofslots)
+ end
+end
+
+local writenode = node.write
+local flushnode = context.flushnode
+
+function commands.deferredproperty(...)
+-- context(register(...))
+ flushnode(register(...))
+end
+
+
+function commands.immediateproperty(...)
+ writenode(register(...))
+end
+
+commands.attachproperty = commands.deferredproperty
+
+local actions = { } properties.actions = actions
+
+table.setmetatableindex(actions,function(t,k)
+ report("unknown property action %a",k)
+ local v = function() end
+ return v
+end)
+
+local f_delayed = formatters["return function(target,head,where,propdata,parent) %s end"]
+local f_immediate = formatters["return function(target,head,where,propdata) %s end"]
+
+local nofdelayed = 0 -- better is to keep track of it per page ... we can have deleted nodes with properties
+
+function actions.delayed(target,head,where,propdata,code,...) -- this one is used at the tex end
+-- local kind = type(code)
+-- if kind == "string" then
+-- code, err = load(f_delayed(code))
+-- if code then
+-- code = code()
+-- end
+-- elseif kind ~= "function" then
+-- code = nil
+-- end
+ if code then
+ local delayed = propdata.delayed
+ if delayed then
+ delayed[#delayed+1] = { where, code, ... }
+ else
+ propdata.delayed = { { where, code, ... } }
+ nofdelayed = nofdelayed + 1
+ end
+ end
+end
+
+function actions.fdelayed(target,head,where,propdata,code,...) -- this one is used at the tex end
+-- local kind = type(code)
+-- if kind == "string" then
+-- code, err = load(f_delayed(code))
+-- if code then
+-- code = code()
+-- end
+-- elseif kind ~= "function" then
+-- code = nil
+-- end
+ if code then
+ local delayed = propdata.delayed
+ if delayed then
+ delayed[#delayed+1] = { false, code, ... }
+ else
+ propdata.delayed = { { false, code, ... } }
+ nofdelayed = nofdelayed + 1
+ end
+ end
+end
+
+function actions.immediate(target,head,where,propdata,code,...) -- this one is used at the tex end
+ local kind = type(code)
+ if kind == "string" then
+ local f = f_immediate(code)
+ local okay, err = load(f)
+ if okay then
+ local h = okay()(target,head,where,propdata,...)
+ if h and h ~= head then
+ return h
+ end
+ end
+ elseif kind == "function" then
+ local h = code()(target,head,where,propdata,...)
+ if h and h ~= head then
+ return h
+ end
+ end
+end
+
+-- another experiment (a table or function closure are equally efficient); a function
+-- is easier when we want to experiment with different (compatible) implementations
+
+-- function nodes.nuts.pool.deferredfunction(...)
+-- nofdelayed = nofdelayed + 1
+-- local n = nut_usernumber(property_id,0)
+-- propertydata[n] = { deferred = { ... } }
+-- return n
+-- end
+
+-- function nodes.nuts.pool.deferredfunction(f)
+-- nofdelayed = nofdelayed + 1
+-- local n = nut_usernumber(property_id,0)
+-- propertydata[n] = { deferred = f }
+-- return n
+-- end
+
+-- maybe actions will get parent too
+
+local function delayed(head,parent) -- direct based
+ for target in traverse(head) do
+ local p = propertydata[target]
+ if p then
+ -- local deferred = p.deferred -- kind of late lua (but too soon as we have no access to pdf.h/v)
+ -- if deferred then
+ -- -- if #deferred > 0 then
+ -- -- deferred[1](unpack(deferred,2))
+ -- -- else
+ -- -- deferred[1]()
+ -- -- end
+ -- deferred()
+ -- p.deferred = false
+ -- if nofdelayed == 1 then
+ -- nofdelayed = 0
+ -- return head
+ -- else
+ -- nofdelayed = nofdelayed - 1
+ -- end
+ -- else
+ local delayed = p.delayed
+ if delayed then
+ for i=1,#delayed do
+ local d = delayed[i]
+ local code = d[2]
+ local kind = type(code)
+ if kind == "string" then
+ code, err = load(f_delayed(code))
+ if code then
+ code = code()
+ end
+ end
+ local where = d[1]
+ if where then
+ local h = code(target,where,head,p,parent,unpack(d,3)) -- target where propdata head parent
+ if h and h ~= head then
+ head = h
+ end
+ else
+ code(unpack(d,3))
+ end
+ end
+ p.delayed = nil
+ if nofdelayed == 1 then
+ nofdelayed = 0
+ return head
+ else
+ nofdelayed = nofdelayed - 1
+ end
+ end
+ -- end
+ end
+ local id = getid(target)
+ if id == hlist_code or id == vlist_code then
+ local list = getlist(target)
+ if list then
+ local done = delayed(list,parent)
+ if done then
+ setfield(target,"list",done)
+ end
+ if nofdelayed == 0 then
+ return head
+ end
+ end
+ else
+ -- maybe also some more lists? but we will only use this for some
+ -- special cases .. who knows
+ end
+ end
+ return head
+end
+
+function properties.delayed(head) --
+ if nofdelayed > 0 then
+ -- if next(propertydata) then
+ starttiming(properties)
+ head = delayed(tonut(head))
+ stoptiming(properties)
+ return tonode(head), true -- done in shipout anyway
+ -- else
+ -- delayed = 0
+ -- end
+ end
+ return head, false
+end
+
+-- more explicit ones too
+
+local anchored = {
+ [v_before] = function(n)
+ while n do
+ n = getprev(n)
+ if getid(n) == whatsit_code and getsubtype(n) == user_code and getfield(n,"user_id") == property_id then
+ -- continue
+ else
+ return n
+ end
+ end
+ end,
+ [v_after] = function(n)
+ while n do
+ n = getnext(n)
+ if getid(n) == whatsit_code then
+ local subtype = getsubtype(n)
+ if (subtype == userdefined_code and getfield(n,"user_id") == property_id) then
+ -- continue
+ elseif subtype == localpar_code then
+ -- continue .. can't happen anyway as we cannot write
+ else
+ return n
+ end
+ else
+ return n
+ end
+ end
+ end,
+ [v_here] = function(n)
+ -- todo
+ end,
+}
+
+table.setmetatableindex(anchored,function(t,k)
+ v = anchored[v_after]
+ t[k] = v
+ return v
+end)
+
+function properties.attach(head)
+
+ if nofslots <= 0 then
+ return head, false
+ end
+
+ local done = false
+ local last = nil
+ local head = tonut(head)
+
+ starttiming(properties)
+
+ for source in traverse_id(whatsit_code,head) do
+ if getsubtype(source) == userdefined_code then
+ if last then
+ removenode(head,last,true)
+ last = nil
+ end
+ if getfield(source,"user_id") == property_id then
+ local slot = getfield(source,"value")
+ local data = cache[slot]
+ if data then
+ cache[slot] = nil
+ local where = data[1]
+ local target = anchored[where](source)
+ if target then
+ local first = data[2]
+ local method = type(first)
+ local p_target = propertydata[target]
+ local p_source = propertydata[source]
+ if p_target then
+ if p_source then
+ for k, v in next, p_source do
+ p_target[k] = v
+ end
+ end
+ if method == "table" then
+ for k, v in next, first do
+ p_target[k] = v
+ end
+ elseif method == "function" then
+ first(target,head,where,p_target,unpack(data,3))
+ elseif method == "string" then
+ actions[first](target,head,where,p_target,unpack(data,3))
+ end
+ elseif p_source then
+ if method == "table" then
+ propertydata[target] = p_source
+ for k, v in next, first do
+ p_source[k] = v
+ end
+ elseif method == "function" then
+ propertydata[target] = p_source
+ first(target,head,where,p_source,unpack(data,3))
+ elseif method == "string" then
+ propertydata[target] = p_source
+ actions[first](target,head,where,p_source,unpack(data,3))
+ end
+ else
+ if method == "table" then
+ propertydata[target] = first
+ elseif method == "function" then
+ local t = { }
+ propertydata[target] = t
+ first(target,head,where,t,unpack(data,3))
+ elseif method == "string" then
+ local t = { }
+ propertydata[target] = t
+ actions[first](target,head,where,t,unpack(data,3))
+ end
+ end
+ if trace_setting then
+ report_setting("node %i, id %s, data %s",
+ target,nodecodes[getid(target)],serialize(propertydata[target],false))
+ end
+ end
+ if nofslots == 1 then
+ nofslots = 0
+ last = source
+ break
+ else
+ nofslots = nofslots - 1
+ end
+ end
+ last = source
+ end
+ end
+ end
+
+ if last then
+ removenode(head,last,true)
+ end
+
+ stoptiming(properties)
+
+ return head, done
+
+end
+
+local tasks = nodes.tasks
+
+-- maybe better hard coded in-place
+
+-- tasks.prependaction("processors","before","nodes.properties.attach")
+-- tasks.appendaction("shipouts","normalizers","nodes.properties.delayed")
+
+statistics.register("properties processing time", function()
+ return statistics.elapsedseconds(properties)
+end)
+
+-- only for development
+
+-- local function show(head,level,report)
+-- for target in traverse(head) do
+-- local p = propertydata[target]
+-- if p then
+-- report("level %i, node %i, id %s, data %s",
+-- level,target,nodecodes[getid(target)],serialize(propertydata[target],false))
+-- end
+-- local id = getid(target)
+-- if id == hlist_code or id == vlist_code then
+-- local list = getlist(target)
+-- if list then
+-- show(list,level+1,report)
+-- end
+-- else
+-- -- maybe more lists
+-- end
+-- end
+-- return head, false
+-- end
+--
+-- local report_shipout = logs.reporter("properties","shipout")
+-- local report_processors = logs.reporter("properties","processors")
+--
+-- function properties.showshipout (head) return tonode(show(tonut(head),1,report_shipout )), true end
+-- function properties.showprocessors(head) return tonode(show(tonut(head),1,report_processors)), true end
+--
+-- tasks.prependaction("shipouts","before","nodes.properties.showshipout")
+-- tasks.disableaction("shipouts","nodes.properties.showshipout")
+--
+-- trackers.register("properties.shipout",function(v)
+-- tasks.setaction("shipouts","nodes.properties.showshipout",v)
+-- end)
+--
+-- tasks.appendaction ("processors","after","nodes.properties.showprocessors")
+-- tasks.disableaction("processors","nodes.properties.showprocessors")
+--
+-- trackers.register("properties.processors",function(v)
+-- tasks.setaction("processors","nodes.properties.showprocessors",v)
+-- end)