summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/tex/context/base/mkiv/node-ref.lua
diff options
context:
space:
mode:
Diffstat (limited to 'Master/texmf-dist/tex/context/base/mkiv/node-ref.lua')
-rw-r--r--Master/texmf-dist/tex/context/base/mkiv/node-ref.lua941
1 files changed, 941 insertions, 0 deletions
diff --git a/Master/texmf-dist/tex/context/base/mkiv/node-ref.lua b/Master/texmf-dist/tex/context/base/mkiv/node-ref.lua
new file mode 100644
index 00000000000..9f2d0918c0f
--- /dev/null
+++ b/Master/texmf-dist/tex/context/base/mkiv/node-ref.lua
@@ -0,0 +1,941 @@
+if not modules then modules = { } end modules ['node-ref'] = {
+ version = 1.001,
+ comment = "companion to node-ref.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- We supported pdf right from the start and in mkii this has resulted in
+-- extensive control over the links. Nowadays pdftex provides a lot more
+-- control over margins but as mkii supports multiple backends we stuck to
+-- our own mechanisms. In mkiv again we implement our own handling. Eventually
+-- we will even disable the pdf primitives.
+
+-- helper, will end up in luatex
+
+-- is grouplevel still used?
+
+local concat = table.concat
+
+local attributes, nodes, node = attributes, nodes, node
+
+local allocate = utilities.storage.allocate, utilities.storage.mark
+local mark = utilities.storage.allocate, utilities.storage.mark
+
+local nodeinjections = backends.nodeinjections
+local codeinjections = backends.codeinjections
+
+local cleanupreferences = false
+local cleanupdestinations = true
+
+local transparencies = attributes.transparencies
+local colors = attributes.colors
+local references = structures.references
+local tasks = nodes.tasks
+
+local trace_references = false trackers.register("nodes.references", function(v) trace_references = v end)
+local trace_destinations = false trackers.register("nodes.destinations", function(v) trace_destinations = v end)
+local trace_areas = false trackers.register("nodes.areas", function(v) trace_areas = v end)
+local show_references = false trackers.register("nodes.references.show", function(v) show_references = tonumber(v) or (v and 2.25 or false) end)
+local show_destinations = false trackers.register("nodes.destinations.show", function(v) show_destinations = tonumber(v) or (v and 2.00 or false) end)
+
+local report_reference = logs.reporter("backend","references")
+local report_destination = logs.reporter("backend","destinations")
+local report_area = logs.reporter("backend","areas")
+
+local nuts = nodes.nuts
+local nodepool = nuts.pool
+
+local tonode = nuts.tonode
+local tonut = nuts.tonut
+
+local getfield = nuts.getfield
+local setfield = nuts.setfield
+local setlink = nuts.setlink
+local setnext = nuts.setnext
+local setprev = nuts.setprev
+local getnext = nuts.getnext
+local getprev = nuts.getprev
+local getid = nuts.getid
+local getlist = nuts.getlist
+local setlist = nuts.setlist
+local getattr = nuts.getattr
+local setattr = nuts.setattr
+local getsubtype = nuts.getsubtype
+
+local hpack_list = nuts.hpack
+local vpack_list = nuts.vpack
+local list_dimensions = nuts.dimensions
+local traverse = nuts.traverse
+local find_node_tail = nuts.tail
+
+local nodecodes = nodes.nodecodes
+local skipcodes = nodes.skipcodes
+local listcodes = nodes.listcodes
+
+local hlist_code = nodecodes.hlist
+local vlist_code = nodecodes.vlist
+local glue_code = nodecodes.glue
+local glyph_code = nodecodes.glyph
+local rule_code = nodecodes.rule
+local dir_code = nodecodes.dir
+local localpar_code = nodecodes.localpar
+
+local leftskip_code = skipcodes.leftskip
+local rightskip_code = skipcodes.rightskip
+local parfillskip_code = skipcodes.parfillskip
+
+local line_code = listcodes.line
+
+local new_rule = nodepool.rule
+local new_kern = nodepool.kern
+
+local free_node = nuts.free
+
+local tosequence = nodes.tosequence
+
+local implement = interfaces.implement
+
+-- Normally a (destination) area is a box or a simple stretch if nodes but when it is
+-- a paragraph we have a problem: we cannot calculate the height well. This happens
+-- with footnotes or content broken across a page.
+
+local function hlist_dimensions(start,stop,parent)
+ local last = stop and getnext(stop)
+ if parent then
+ return list_dimensions(getfield(parent,"glue_set"),getfield(parent,"glue_sign"),getfield(parent,"glue_order"),start,last)
+ else
+ return list_dimensions(start,last)
+ end
+end
+
+local function vlist_dimensions(start,stop) -- also needs the stretch and so
+ local temp
+ if stop then
+ temp = getnext(stop)
+ setnext(stop,nil)
+ end
+ local v = vpack_list(start)
+ local w = getfield(v,"width")
+ local h = getfield(v,"height")
+ local d = getfield(v,"depth")
+ setlist(v) -- not needed
+ free_node(v)
+ if temp then
+ setnext(stop,temp)
+ end
+ return w, h, d
+end
+
+local function dimensions(parent,start,stop) -- in principle we could move some to the caller
+ local id = getid(start)
+ if start == stop then
+ if id == hlist_code or id == vlist_code or id == glyph_code or id == rule_code then
+ if trace_areas then
+ report_area("dimensions taken of %a",nodecodes[id])
+ end
+ -- hm, parent can be zero
+ local width = getfield(start,"width")
+ local height = getfield(parent,"height")
+ local depth = getfield(parent,"depth")
+ if height == 0 and depth == 0 then
+ height = getfield(start,"height")
+ depth = getfield(start,"depth")
+ end
+ return width, height, depth
+ else
+ if trace_areas then
+ report_area("dimensions calculated of %a",nodecodes[id])
+ end
+ return hlist_dimensions(start,stop) -- one node only so simple
+ end
+ end
+ local last = stop and getnext(stop)
+ if parent then
+ -- todo: if no prev and no next and parent
+ -- todo: we need a a list_dimensions for a vlist
+ if getid(parent) == vlist_code then
+ if false then
+ local l = getlist(parent)
+ local c = l
+ local ok = false
+ while c do
+ if c == start then
+ ok = true
+ end
+ if ok and getid(c) == hlist_code then
+ break
+ else
+ c = getnext(c)
+ end
+ end
+ if ok and c then
+ if trace_areas then
+ report_area("dimensions taken of first line in vlist")
+ end
+ return getfield(c,"width"), getfield(c,"height"), getfield(c,"depth"), c
+ else
+ if trace_areas then
+ report_area("dimensions taken of vlist (probably wrong)")
+ end
+ return hlist_dimensions(start,stop,parent)
+ end
+ else
+ --
+ -- we can as well calculate here because we only have kerns and glue
+ --
+ local first = nil
+ local last = nil
+ local current = start
+ local noflines = 0
+ while current do
+ local id = getid(current)
+ if id == hlist_code or id == vlist_code or id == rule_code then
+ if noflines == 0 then
+ first = current
+ noflines = 1
+ else
+ noflines = noflines + 1
+ end
+ last = current
+ end
+ if current == stop then
+ break
+ else
+ current = getnext(current)
+ end
+ end
+ if noflines > 1 then
+ if trace_areas then
+ report_area("dimensions taken of vlist")
+ end
+ local w, h, d = vlist_dimensions(first,last,parent)
+ local ht = getfield(first,"height")
+ return w, ht, d + h - ht, first
+ else
+ -- return hlist_dimensions(start,stop,parent)
+ if first then
+ if trace_areas then
+ report_area("dimensions taken of first line in vlist")
+ end
+ return getfield(first,"width"), getfield(first,"height"), getfield(first,"depth"), first
+ else
+ if trace_areas then
+ report_area("dimensions taken of vlist (probably wrong)")
+ end
+ return hlist_dimensions(start,stop,parent)
+ end
+ end
+ end
+ else
+ if trace_areas then
+ report_area("dimensions taken of range starting with %a using parent",nodecodes[id])
+ end
+ return hlist_dimensions(start,stop,parent)
+ end
+ else
+ if trace_areas then
+ report_area("dimensions taken of range starting with %a",nodecodes[id])
+ end
+ return hlist_dimensions(start,stop)
+ end
+end
+
+-- is pardir important at all?
+
+local function inject_range(head,first,last,reference,make,stack,parent,pardir,txtdir)
+ local width, height, depth, line = dimensions(parent,first,last)
+ if txtdir == "+TRT" or (txtdir == "===" and pardir == "TRT") then -- KH: textdir == "===" test added
+ width = - width
+ end
+ local result, resolved = make(width,height,depth,reference)
+ if result and resolved then
+ if line then
+ -- special case, we only treat the first line in a vlist
+ local l = getlist(line)
+ if trace_areas then
+ report_area("%s: %i : %s %s %s => w=%p, h=%p, d=%p, c=%S","line",
+ reference,pardir or "---",txtdir or "---",
+ tosequence(l,nil,true),width,height,depth,resolved)
+ end
+ setlist(line,result)
+ setlink(result,l)
+ return head, last
+ else
+ if head == first then
+ if trace_areas then
+ report_area("%s: %i : %s %s %s => w=%p, h=%p, d=%p, c=%S","head",
+ reference,pardir or "---",txtdir or "---",
+ tosequence(first,last,true),width,height,depth,resolved)
+ end
+ setlink(result,first)
+ return result, last
+ else
+ if trace_areas then
+ report_area("%s: %i : %s %s %s => w=%p, h=%p, d=%p, c=%S","middle",
+ reference,pardir or "---",txtdir or "---",
+ tosequence(first,last,true),width,height,depth,resolved)
+ end
+ local prev = getprev(first)
+ if prev then
+ setlink(prev,result)
+ end
+ setlink(result,first)
+ return head, last
+ end
+ end
+ else
+ return head, last
+ end
+end
+
+local function inject_list(id,current,reference,make,stack,pardir,txtdir)
+ local width = getfield(current,"width")
+ local height = getfield(current,"height")
+ local depth = getfield(current,"depth")
+ local correction = 0
+ local moveright = false
+ local first = getlist(current)
+ if id == hlist_code then -- box_code line_code
+ -- can be either an explicit hbox or a line and there is no way
+ -- to recognize this; anyway only if ht/dp (then inline)
+ local sr = stack[reference]
+ if first then
+ if sr and sr[2] then
+ local last = find_node_tail(first)
+ if getid(last) == glue_code and getsubtype(last) == rightskip_code then
+ local prev = getprev(last)
+ moveright = getid(first) == glue_code and getsubtype(first) == leftskip_code
+ if prev and getid(prev) == glue_code and getsubtype(prev) == parfillskip_code then
+ width = dimensions(current,first,getprev(prev)) -- maybe not current as we already take care of it
+ else
+ if moveright then
+ width = width - getfield(first,"stretch") * getfield(current,"glue_set") * getfield(current,"glue_sign")
+ end
+ width = width - getfield(last,"stretch") * getfield(current,"glue_set") * getfield(current,"glue_sign")
+ end
+ end
+ else
+ -- also weird
+ end
+ else
+ -- ok
+ end
+ correction = width
+ else
+ correction = height + depth
+ height, depth = depth, height -- ugly hack, needed because pdftex backend does something funny
+ end
+ if pardir == "TRT" then
+ width = - width
+ end
+ local result, resolved = make(width,height,depth,reference)
+ -- todo: only when width is ok
+ if result and resolved then
+ if trace_areas then
+ report_area("%s: %04i %s %s %s: w=%p, h=%p, d=%p, c=%S","box",
+ reference,pardir or "---",txtdir or "----","[]",width,height,depth,resolved)
+ end
+ if not first then
+ setlist(current,result)
+ elseif moveright then -- brr no prevs done
+ -- result after first
+ local n = getnext(first)
+ setnext(result,n)
+ setlink(first,first)
+ if n then
+ setprev(n,result)
+ end
+ else
+ -- first after result
+ setlink(result,first)
+ setlist(current,result)
+ end
+ end
+end
+
+-- skip is somewhat messy
+
+-- todo: when line we can look at the next line
+
+-- see dimensions: this is tricky with split off boxes like inserts
+-- where we can end up with a first and last spanning lines so maybe
+-- we need to do vlists differently
+
+local function inject_areas(head,attribute,make,stack,done,skip,parent,pardir,txtdir) -- main
+ if head then
+ local current, first, last, firstdir, reference = head, nil, nil, nil, nil
+ pardir = pardir or "==="
+ txtdir = txtdir or "==="
+ while current do
+ local id = getid(current)
+ if id == hlist_code or id == vlist_code then
+ local r = getattr(current,attribute)
+ -- test \goto{test}[page(2)] test \gotobox{test}[page(2)]
+ -- test \goto{\TeX}[page(2)] test \gotobox{\hbox {x} \hbox {x}}[page(2)]
+ -- if r and (not skip or r >) skip then -- maybe no > test
+ -- inject_list(id,current,r,make,stack,pardir,txtdir)
+ -- end
+ if r then
+ if not reference then
+ reference, first, last, firstdir = r, current, current, txtdir
+ elseif r == reference then
+ -- same link
+ last = current
+ elseif (done[reference] or 0) == 0 then
+ if not skip or r > skip then -- maybe no > test
+ head, current = inject_range(head,first,last,reference,make,stack,parent,pardir,firstdir)
+ reference, first, last, firstdir = nil, nil, nil, nil
+ end
+ else
+ reference, first, last, firstdir = r, current, current, txtdir
+ end
+ done[r] = (done[r] or 0) + 1
+ end
+ local list = getlist(current)
+ if list then
+ local h, ok
+ h, ok , pardir, txtdir = inject_areas(list,attribute,make,stack,done,r or skip or 0,current,pardir,txtdir)
+ if h ~= current then
+ setlist(current,h)
+ end
+ end
+ if r then
+ done[r] = done[r] - 1
+ end
+ elseif id == dir_code then
+ txtdir = getfield(current,"dir")
+ elseif id == localpar_code then
+ pardir = getfield(current,"dir")
+ elseif id == glue_code and getsubtype(current) == leftskip_code then -- any glue at the left?
+ --
+ else
+ local r = getattr(current,attribute)
+ if not r then
+ -- just go on, can be kerns
+ elseif not reference then
+ reference, first, last, firstdir = r, current, current, txtdir
+ elseif r == reference then
+ last = current
+ elseif (done[reference] or 0) == 0 then -- or id == glue_code and getsubtype(current) == right_skip_code
+ if not skip or r > skip then -- maybe no > test
+ head, current = inject_range(head,first,last,reference,make,stack,parent,pardir,firstdir)
+ reference, first, last, firstdir = nil, nil, nil, nil
+ end
+ else
+ reference, first, last, firstdir = r, current, current, txtdir
+ end
+ end
+ current = getnext(current)
+ end
+ if reference and (done[reference] or 0) == 0 then
+ head = inject_range(head,first,last,reference,make,stack,parent,pardir,firstdir)
+ end
+ end
+ return head, true, pardir, txtdir
+end
+
+local function inject_area(head,attribute,make,stack,done,parent,pardir,txtdir) -- singular !
+ if head then
+ pardir = pardir or "==="
+ txtdir = txtdir or "==="
+ local current = head
+ while current do
+ local id = getid(current)
+ if id == hlist_code or id == vlist_code then
+ local r = getattr(current,attribute)
+ if r and not done[r] then
+ done[r] = true
+ inject_list(id,current,r,make,stack,pardir,txtdir)
+ end
+ local list = getlist(current)
+ if list then
+ local h = inject_area(list,attribute,make,stack,done,current,pardir,txtdir)
+ if h ~= current then
+ setlist(current,h)
+ end
+ end
+ elseif id == dir_code then
+ txtdir = getfield(current,"dir")
+ elseif id == localpar_code then
+ pardir = getfield(current,"dir")
+ else
+ local r = getattr(current,attribute)
+ if r and not done[r] then
+ done[r] = true
+ head, current = inject_range(head,current,current,r,make,stack,parent,pardir,txtdir)
+ end
+ end
+ current = getnext(current)
+ end
+ end
+ return head, true
+end
+
+-- tracing: todo: use predefined colors
+
+local register_color = colors.register
+
+local a_color = attributes.private('color')
+local a_colormodel = attributes.private('colormodel')
+local a_transparency = attributes.private('transparency')
+local u_transparency = nil
+local u_colors = { }
+local force_gray = true
+
+local function addstring(what,str,shift) --todo make a pluggable helper (in font-ctx)
+ if str then
+ local typesetters = nuts.typesetters
+ if typesetters then
+ local hashes = fonts.hashes
+ local infofont = fonts.infofont()
+ local emwidth = hashes.emwidths [infofont]
+ local exheight = hashes.exheights[infofont]
+ if what == "reference" then
+ str = str .. " "
+ shift = - (shift or 2.25) * exheight
+ else
+ str = str .. " "
+ shift = (shift or 2) * exheight
+ end
+ local text = typesetters.tohpack(str,infofont)
+ local rule = new_rule(emwidth/5,4*exheight,3*exheight)
+ setfield(text,"shift",shift)
+ return hpack_list(nuts.linked(text,rule))
+ end
+ end
+end
+
+local function colorize(width,height,depth,n,reference,what,sr,offset)
+ if force_gray then n = 0 end
+ u_transparency = u_transparency or transparencies.register(nil,2,.65)
+ local ucolor = u_colors[n]
+ if not ucolor then
+ if n == 1 then
+ u_color = register_color(nil,'rgb',.75,0,0)
+ elseif n == 2 then
+ u_color = register_color(nil,'rgb',0,.75,0)
+ elseif n == 3 then
+ u_color = register_color(nil,'rgb',0,0,.75)
+ else
+ n = 0
+ u_color = register_color(nil,'gray',.5)
+ end
+ u_colors[n] = u_color
+ end
+ if width == 0 then
+ -- probably a strut as placeholder
+ report_area("%s %s has no %s dimensions, width %p, height %p, depth %p",what,reference,"horizontal",width,height,depth)
+ width = 65536
+ end
+ if height + depth <= 0 then
+ report_area("%s %s has no %s dimensions, width %p, height %p, depth %p",what,reference,"vertical",width,height,depth)
+ height = 65536/2
+ depth = height
+ end
+ local rule = new_rule(width,height,depth) -- todo: use tracer rule
+ setattr(rule,a_colormodel,1) -- gray color model
+ setattr(rule,a_color,u_color)
+ setattr(rule,a_transparency,u_transparency)
+ if width < 0 then
+ local kern = new_kern(width)
+ setfield(rule,"width",-width)
+ setnext(kern,rule)
+ setprev(rule,kern)
+ return kern
+ else
+
+if sr and sr ~= "" then
+ local text = addstring(what,sr,shift)
+ if text then
+ local kern = new_kern(-getfield(text,"width"))
+ setlink(kern,text)
+ setlink(text,rule)
+ return kern
+ end
+end
+
+ return rule
+ end
+end
+
+local function justadd(what,sr,shift)
+ if sr and sr ~= "" then
+ local text = addstring(what,sr,shift)
+ if text then
+ local kern = new_kern(-getfield(text,"width"))
+ setlink(kern,text)
+ setlink(text,rule)
+ return kern
+ end
+ end
+end
+
+-- references:
+
+local texsetattribute = tex.setattribute
+local texsetcount = tex.setcount
+
+local stack = { }
+local done = { }
+local attribute = attributes.private('reference')
+local nofreferences = 0
+local topofstack = 0
+
+nodes.references = {
+ attribute = attribute,
+ stack = stack,
+ done = done,
+}
+
+-- todo: get rid of n (n is just a number, can be used for tracing, obsolete)
+
+local function setreference(h,d,r)
+ topofstack = topofstack + 1
+ -- the preroll permits us to determine samepage (but delayed also has some advantages)
+ -- so some part of the backend work is already done here
+ stack[topofstack] = { r, h, d, codeinjections.prerollreference(r) }
+ -- texsetattribute(attribute,topofstack) -- todo -> at tex end
+ texsetcount("lastreferenceattribute",topofstack)
+end
+
+function references.get(n) -- not public so functionality can change
+ local sn = stack[n]
+ return sn and sn[1]
+end
+
+local function makereference(width,height,depth,reference) -- height and depth are of parent
+ local sr = stack[reference]
+ if sr then
+ if trace_references then
+ report_reference("resolving attribute %a",reference)
+ end
+ local resolved, ht, dp, set, n = sr[1], sr[2], sr[3], sr[4], sr[5]
+ -- logs.report("temp","child: ht=%p dp=%p, parent: ht=%p dp=%p",ht,dp,height,depth)
+ if ht then
+ if height < ht then height = ht end
+ if depth < dp then depth = dp end
+ end
+ -- logs.report("temp","used: ht=%p dp=%p",height,depth)
+ local annot = nodeinjections.reference(width,height,depth,set)
+ if annot then
+ annot = tonut(annot) -- todo
+ nofreferences = nofreferences + 1
+ local result, current, texts
+ if show_references then
+ local d = sr[1]
+ if d then
+ local r = d.reference
+ local p = d.prefix
+ if r then
+ if p then
+ texts = p .. "|" .. r
+ else
+ texts = r
+ end
+ else
+ -- t[#t+1] = d.internal or "?"
+ end
+ end
+ end
+ if trace_references then
+ local step = 65536
+ result = hpack_list(colorize(width,height-step,depth-step,2,reference,"reference",texts,show_references)) -- step subtracted so that we can see seperate links
+ setfield(result,"width",0)
+ current = result
+ elseif texts then
+ texts = justadd("reference",texts,show_references)
+ if texts then
+ result = hpack_list(texts)
+ setfield(result,"width",0)
+ current = result
+ end
+ end
+ if current then
+ setlink(current,annot)
+ else
+ result = annot
+ end
+ references.registerpage(n)
+ result = hpack_list(result,0)
+ setfield(result,"width",0)
+ setfield(result,"height",0)
+ setfield(result,"depth",0)
+ if cleanupreferences then stack[reference] = nil end
+ return result, resolved
+ elseif trace_references then
+ report_reference("unable to resolve annotation %a",reference)
+ end
+ elseif trace_references then
+ report_reference("unable to resolve attribute %a",reference)
+ end
+end
+
+-- function nodes.references.handler(head)
+-- if topofstack > 0 then
+-- return inject_areas(head,attribute,makereference,stack,done)
+-- else
+-- return head, false
+-- end
+-- end
+
+function nodes.references.handler(head)
+ if topofstack > 0 then
+ head = tonut(head)
+ local head, done = inject_areas(head,attribute,makereference,stack,done)
+ return tonode(head), done
+ else
+ return head, false
+ end
+end
+
+-- destinations (we can clean up once set, unless tagging!)
+
+local stack = { }
+local done = { }
+local attribute = attributes.private('destination')
+local nofdestinations = 0
+local topofstack = 0
+
+nodes.destinations = {
+ attribute = attribute,
+ stack = stack,
+ done = done,
+}
+
+local function setdestination(n,h,d,name,view) -- n = grouplevel, name == table
+ topofstack = topofstack + 1
+ stack[topofstack] = { n, h, d, name, view }
+ return topofstack
+end
+
+local function makedestination(width,height,depth,reference)
+ local sr = stack[reference]
+ if sr then
+ if trace_destinations then
+ report_destination("resolving attribute %a",reference)
+ end
+ local resolved, ht, dp, name, view = sr[1], sr[2], sr[3], sr[4], sr[5] -- sr[4] will change to just internal
+ if ht then
+ if height < ht then height = ht end
+ if depth < dp then depth = dp end
+ end
+ local result, current, texts
+ if show_destinations then
+ if name and #name > 0 then
+ local t = { }
+ for i=1,#name do
+ local s = name[i]
+ if type(s) == "number" then
+ local d = references.internals[s]
+ if d then
+ d = d.references
+ local r = d.reference
+ local p = d.usedprefix
+ if r then
+ if p then
+ t[#t+1] = p .. "|" .. r
+ else
+ t[#t+1] = r
+ end
+ else
+ -- t[#t+1] = d.internal or "?"
+ end
+ end
+ else
+ -- in fact we have a prefix:name here
+ end
+ end
+ if #t > 0 then
+ texts = concat(t," & ")
+ end
+ end
+ end
+ if trace_destinations then
+ local step = 0
+ if width == 0 then
+ step = 4*65536
+ width, height, depth = 5*step, 5*step, 0
+ end
+ local rule = hpack_list(colorize(width,height,depth,3,reference,"destination",texts,show_destinations))
+ setfield(rule,"width",0)
+ if not result then
+ result, current = rule, rule
+ else
+ setlink(current,rule)
+ current = rule
+ end
+ width, height = width - step, height - step
+ elseif texts then
+ texts = justadd("destination",texts,show_destinations)
+ if texts then
+ result = hpack_list(texts)
+ if result then
+ setfield(result,"width",0)
+ current = result
+ end
+ end
+ end
+ nofdestinations = nofdestinations + 1
+ local annot = nodeinjections.destination(width,height,depth,name,view)
+ if annot then
+ annot = tonut(annot) -- obsolete soon
+ if result then
+ setlink(current,annot)
+ else
+ result = annot
+ end
+ current = find_node_tail(annot)
+ end
+ if result then
+ -- some internal error
+ result = hpack_list(result,0)
+ setfield(result,"width",0)
+ setfield(result,"height",0)
+ setfield(result,"depth",0)
+ end
+ if cleanupdestinations then stack[reference] = nil end
+ return result, resolved
+ elseif trace_destinations then
+ report_destination("unable to resolve attribute %a",reference)
+ end
+end
+
+-- function nodes.destinations.handler(head)
+-- if topofstack > 0 then
+-- return inject_area(head,attribute,makedestination,stack,done) -- singular
+-- else
+-- return head, false
+-- end
+-- end
+
+function nodes.destinations.handler(head)
+ if topofstack > 0 then
+ head = tonut(head)
+ local head, done = inject_areas(head,attribute,makedestination,stack,done)
+ return tonode(head), done
+ else
+ return head, false
+ end
+end
+
+
+-- will move
+
+function references.mark(reference,h,d,view)
+ return setdestination(tex.currentgrouplevel,h,d,reference,view)
+end
+
+function references.inject(prefix,reference,specification) -- todo: use currentreference is possible
+ local set, bug = references.identify(prefix,reference)
+ if bug or #set == 0 then
+ -- unknown ref, just don't set it and issue an error
+ else
+ set.highlight = specification.highlight
+ set.newwindow = specification.newwindow
+ set.layer = specification.layer
+ setreference(specification.height,specification.depth,set) -- sets attribute / todo: for set[*].error
+ end
+end
+
+-- function references.injectinternal(internal,specification)
+-- references.inject("","internal("..internal..")",specification)
+-- if bug or #set == 0 then
+-- -- unknown ref, just don't set it and issue an error
+-- else
+-- -- nil prefix when ""
+-- -- check
+-- set.highlight = specification.highlight
+-- set.newwindow = specification.newwindow
+-- set.layer = specification.layer
+-- setreference(specification.height,specification.depth,set) -- sets attribute / todo: for set[*].error
+-- end
+-- end
+
+function references.injectcurrentset(h,d) -- used inside doifelse
+ local currentset = references.currentset
+ if currentset then
+ setreference(h,d,currentset) -- sets attribute / todo: for set[*].error
+ end
+end
+
+implement {
+ name = "injectreference",
+ actions = references.inject,
+ arguments = {
+ "string",
+ "string",
+ {
+ { "highlight", "boolean" },
+ { "newwindow", "boolean" },
+ { "layer" },
+ { "height", "dimen" },
+ { "depth", "dimen" },
+ { "view" },
+ }
+ }
+}
+
+-- implement {
+-- name = "injectinternalreference",
+-- actions = references.injectinternal,
+-- arguments = {
+-- "integer",
+-- {
+-- { "highlight", "boolean" },
+-- { "newwindow", "boolean" },
+-- { "layer" },
+-- { "height", "dimen" },
+-- { "depth", "dimen" },
+-- { "view" },
+-- }
+-- }
+-- }
+
+implement {
+ name = "injectcurrentreference",
+ actions = references.injectcurrentset,
+}
+
+implement {
+ name = "injectcurrentreferencehtdp",
+ actions = references.injectcurrentset,
+ arguments = { "dimen", "dimen" },
+}
+
+--
+
+local function checkboth(open,close)
+ if open and open ~= "" then
+ local set, bug = references.identify("",open)
+ open = not bug and #set > 0 and set
+ end
+ if close and close ~= "" then
+ local set, bug = references.identify("",close)
+ close = not bug and #set > 0 and set
+ end
+ return open, close
+end
+
+-- end temp hack
+
+statistics.register("interactive elements", function()
+ if nofreferences > 0 or nofdestinations > 0 then
+ return string.format("%s references, %s destinations",nofreferences,nofdestinations)
+ else
+ return nil
+ end
+end)
+
+function references.enableinteraction()
+ tasks.enableaction("shipouts","nodes.references.handler")
+ tasks.enableaction("shipouts","nodes.destinations.handler")
+ function references.enableinteraction() end
+end
+
+implement {
+ name = "enableinteraction",
+ actions = references.enableinteraction,
+ onlyonce = true
+}