summaryrefslogtreecommitdiff
path: root/tex/context/base/node-ref.lua
diff options
context:
space:
mode:
Diffstat (limited to 'tex/context/base/node-ref.lua')
-rw-r--r--tex/context/base/node-ref.lua1170
1 files changed, 585 insertions, 585 deletions
diff --git a/tex/context/base/node-ref.lua b/tex/context/base/node-ref.lua
index 09e066434..cd46cd2dd 100644
--- a/tex/context/base/node-ref.lua
+++ b/tex/context/base/node-ref.lua
@@ -1,585 +1,585 @@
-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 format = string.format
-
-local allocate, mark = utilities.storage.allocate, utilities.storage.mark
-
-local cleanupreferences, cleanupdestinations = false, true
-
-local attributes, nodes, node = attributes, nodes, node
-
-local nodeinjections = backends.nodeinjections
-local codeinjections = backends.codeinjections
-
-local transparencies = attributes.transparencies
-local colors = attributes.colors
-local references = structures.references
-local tasks = nodes.tasks
-
-local hpack_list = node.hpack
-local list_dimensions = node.dimensions
-
--- current.glue_set current.glue_sign
-
-local trace_backend = false trackers.register("nodes.backend", function(v) trace_backend = v end)
-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 report_reference = logs.reporter("backend","references")
-local report_destination = logs.reporter("backend","destinations")
-local report_area = logs.reporter("backend","areas")
-
-local nodecodes = nodes.nodecodes
-local skipcodes = nodes.skipcodes
-local whatcodes = nodes.whatcodes
-local listcodes = nodes.listcodes
-
-local hlist_code = nodecodes.hlist
-local vlist_code = nodecodes.vlist
-local glue_code = nodecodes.glue
-local whatsit_code = nodecodes.whatsit
-
-local leftskip_code = skipcodes.leftskip
-local rightskip_code = skipcodes.rightskip
-local parfillskip_code = skipcodes.parfillskip
-
-local localpar_code = whatcodes.localpar
-local dir_code = whatcodes.dir
-
-local line_code = listcodes.line
-
-local nodepool = nodes.pool
-
-local new_kern = nodepool.kern
-
-local traverse = node.traverse
-local find_node_tail = node.tail or node.slide
-local tosequence = nodes.tosequence
-
--- local function dimensions(parent,start,stop)
--- stop = stop and stop.next
--- if parent then
--- if stop then
--- return list_dimensions(parent.glue_set,parent.glue_sign,parent.glue_order,start,stop)
--- else
--- return list_dimensions(parent.glue_set,parent.glue_sign,parent.glue_order,start)
--- end
--- else
--- if stop then
--- return list_dimensions(start,stop)
--- else
--- return list_dimensions(start)
--- end
--- end
--- end
---
--- -- more compact
-
-local function dimensions(parent,start,stop)
- if parent then
- return list_dimensions(parent.glue_set,parent.glue_sign,parent.glue_order,start,stop and stop.next)
- else
- return list_dimensions(start,stop and stop.next)
- end
-end
-
--- is pardir important at all?
-
-local function inject_range(head,first,last,reference,make,stack,parent,pardir,txtdir)
- local width, height, depth = 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 head == first then
- if trace_backend then
- report_area("head: %04i %s %s %s => w=%p, h=%p, d=%p, c=%s",reference,pardir or "---",txtdir or "----",tosequence(first,last,true),width,height,depth,resolved)
- end
- result.next = first
- first.prev = result
- return result, last
- else
- if trace_backend then
- report_area("middle: %04i %s %s => w=%p, h=%p, d=%p, c=%s",reference,pardir or "---",txtdir or "----",tosequence(first,last,true),width,height,depth,resolved)
- end
- local prev = first.prev
- if prev then
- result.next = first
- result.prev = prev
- prev.next = result
- first.prev = result
- else
- result.next = first
- first.prev = result
- end
- if first == head.next then
- head.next = result -- hm, weird
- end
- return head, last
- end
- else
- return head, last
- end
-end
-
-local function inject_list(id,current,reference,make,stack,pardir,txtdir)
- local width, height, depth, correction = current.width, current.height, current.depth, 0
- local moveright = false
- local first = current.list
- 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 last.id == glue_code and last.subtype == rightskip_code then
- local prev = last.prev
- moveright = first.id == glue_code and first.subtype == leftskip_code
- if prev and prev.id == glue_code and prev.subtype == parfillskip_code then
- width = dimensions(current,first,prev.prev) -- maybe not current as we already take care of it
- else
- if moveright and first.writable then
- width = width - first.spec.stretch*current.glue_set * current.glue_sign
- end
- if last.writable then
- width = width - last.spec.stretch*current.glue_set * current.glue_sign
- end
- 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_backend then
- report_area("box: %04i %s %s: w=%p, h=%p, d=%p, c=%s",reference,pardir or "---",txtdir or "----",width,height,depth,resolved)
- end
- if not first then
- current.list = result
- elseif moveright then -- brr no prevs done
- -- result after first
- local n = first.next
- result.next = n
- first.next = result
- result.prev = first
- if n then n.prev = result end
- else
- -- first after result
- result.next = first
- first.prev = result
- current.list = result
- end
- end
-end
-
--- skip is somewhat messy
-
-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 = current.id
- if id == hlist_code or id == vlist_code then
- local r = current[attribute]
- -- somehow reference is true so the following fails (second one not done) in
- -- test \goto{test}[page(2)] test \gotobox{test}[page(2)]
- -- so let's wait till this fails again
- -- if not reference and r and (not skip or r > skip) then -- > or ~=
- if r and (not skip or r > skip) then -- > or ~=
- inject_list(id,current,r,make,stack,pardir,txtdir)
- end
- if r then
- done[r] = (done[r] or 0) + 1
- end
- local list = current.list
- if list then
- local _
- current.list, _, pardir, txtdir = inject_areas(list,attribute,make,stack,done,r or skip or 0,current,pardir,txtdir)
- end
- if r then
- done[r] = done[r] - 1
- end
- elseif id == whatsit_code then
- local subtype = current.subtype
- if subtype == localpar_code then
- pardir = current.dir
- elseif subtype == dir_code then
- txtdir = current.dir
- end
- elseif id == glue_code and current.subtype == leftskip_code then -- any glue at the left?
- --
- else
- local r = 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 current.subtype == 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 = current.next
- 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 = current.id
- if id == hlist_code or id == vlist_code then
- local r = current[attribute]
- if r and not done[r] then
- done[r] = true
- inject_list(id,current,r,make,stack,pardir,txtdir)
- end
- local list = current.list
- if list then
- current.list = inject_area(list,attribute,make,stack,done,current,pardir,txtdir)
- end
- elseif id == whatsit_code then
- local subtype = current.subtype
- if subtype == localpar_code then
- pardir = current.dir
- elseif subtype == dir_code then
- txtdir = current.dir
- end
- else
- local r = 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 = current.next
- end
- end
- return head, true
-end
-
--- tracing
-
-local nodepool = nodes.pool
-
-local new_rule = nodepool.rule
-local new_kern = nodepool.kern
-
-local set_attribute = node.set_attribute
-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 colorize(width,height,depth,n,reference,what)
- 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)
- rule[a_colormodel] = 1 -- gray color model
- rule[a_color] = u_color
- rule[a_transparency] = u_transparency
- if width < 0 then
- local kern = new_kern(width)
- rule.width = -width
- kern.next = rule
- rule.prev = kern
- return kern
- else
- return rule
- end
-end
-
-local nodepool = nodes.pool
-
-local new_kern = nodepool.kern
-
-local texattribute = tex.attribute
-local texcount = tex.count
-
--- references:
-
-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) }
- -- texattribute[attribute] = topofstack -- todo -> at tex end
- texcount.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)
- 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]
- if ht then
- if height < ht then height = ht end
- if depth < dp then depth = dp end
- end
- local annot = nodeinjections.reference(width,height,depth,set)
- if annot then
- nofreferences = nofreferences + 1
- local result, current
- if trace_references then
- local step = 65536
- result = hpack_list(colorize(width,height-step,depth-step,2,reference,"reference")) -- step subtracted so that we can see seperate links
- result.width = 0
- current = result
- end
- if current then
- current.next = annot
- else
- result = annot
- end
- references.registerpage(n)
- result = hpack_list(result,0)
- result.width, result.height, result.depth = 0, 0, 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
-
--- 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]
- if ht then
- if height < ht then height = ht end
- if depth < dp then depth = dp end
- end
- local result, current
- if trace_destinations then
- local step = 0
- if width == 0 then
- step = 4*65536
- width, height, depth = 5*step, 5*step, 0
- end
- for n=1,#name do
- local rule = hpack_list(colorize(width,height,depth,3,reference,"destination"))
- rule.width = 0
- if not result then
- result, current = rule, rule
- else
- current.next = rule
- rule.prev = current
- current = rule
- end
- width, height = width - step, height - step
- end
- end
- nofdestinations = nofdestinations + 1
- for n=1,#name do
- local annot = nodeinjections.destination(width,height,depth,name[n],view)
- if not result then
- result = annot
- else
- current.next = annot
- annot.prev = current
- end
- current = find_node_tail(annot)
- end
- if result then
- -- some internal error
- result = hpack_list(result,0)
- result.width, result.height, result.depth = 0, 0, 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
-
--- will move
-
-function references.mark(reference,h,d,view)
- return setdestination(tex.currentgrouplevel,h,d,reference,view)
-end
-
-function references.inject(prefix,reference,h,d,highlight,newwindow,layer) -- 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
- -- check
- set.highlight, set.newwindow,set.layer = highlight, newwindow, layer
- setreference(h,d,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
-
-commands.injectreference = references.inject
-commands.injectcurrentreference = references.injectcurrentset
-
---
-
-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 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")
-end
+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 format = string.format
+
+local allocate, mark = utilities.storage.allocate, utilities.storage.mark
+
+local cleanupreferences, cleanupdestinations = false, true
+
+local attributes, nodes, node = attributes, nodes, node
+
+local nodeinjections = backends.nodeinjections
+local codeinjections = backends.codeinjections
+
+local transparencies = attributes.transparencies
+local colors = attributes.colors
+local references = structures.references
+local tasks = nodes.tasks
+
+local hpack_list = node.hpack
+local list_dimensions = node.dimensions
+
+-- current.glue_set current.glue_sign
+
+local trace_backend = false trackers.register("nodes.backend", function(v) trace_backend = v end)
+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 report_reference = logs.reporter("backend","references")
+local report_destination = logs.reporter("backend","destinations")
+local report_area = logs.reporter("backend","areas")
+
+local nodecodes = nodes.nodecodes
+local skipcodes = nodes.skipcodes
+local whatcodes = nodes.whatcodes
+local listcodes = nodes.listcodes
+
+local hlist_code = nodecodes.hlist
+local vlist_code = nodecodes.vlist
+local glue_code = nodecodes.glue
+local whatsit_code = nodecodes.whatsit
+
+local leftskip_code = skipcodes.leftskip
+local rightskip_code = skipcodes.rightskip
+local parfillskip_code = skipcodes.parfillskip
+
+local localpar_code = whatcodes.localpar
+local dir_code = whatcodes.dir
+
+local line_code = listcodes.line
+
+local nodepool = nodes.pool
+
+local new_kern = nodepool.kern
+
+local traverse = node.traverse
+local find_node_tail = node.tail or node.slide
+local tosequence = nodes.tosequence
+
+-- local function dimensions(parent,start,stop)
+-- stop = stop and stop.next
+-- if parent then
+-- if stop then
+-- return list_dimensions(parent.glue_set,parent.glue_sign,parent.glue_order,start,stop)
+-- else
+-- return list_dimensions(parent.glue_set,parent.glue_sign,parent.glue_order,start)
+-- end
+-- else
+-- if stop then
+-- return list_dimensions(start,stop)
+-- else
+-- return list_dimensions(start)
+-- end
+-- end
+-- end
+--
+-- -- more compact
+
+local function dimensions(parent,start,stop)
+ if parent then
+ return list_dimensions(parent.glue_set,parent.glue_sign,parent.glue_order,start,stop and stop.next)
+ else
+ return list_dimensions(start,stop and stop.next)
+ end
+end
+
+-- is pardir important at all?
+
+local function inject_range(head,first,last,reference,make,stack,parent,pardir,txtdir)
+ local width, height, depth = 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 head == first then
+ if trace_backend then
+ report_area("head: %04i %s %s %s => w=%p, h=%p, d=%p, c=%s",reference,pardir or "---",txtdir or "----",tosequence(first,last,true),width,height,depth,resolved)
+ end
+ result.next = first
+ first.prev = result
+ return result, last
+ else
+ if trace_backend then
+ report_area("middle: %04i %s %s => w=%p, h=%p, d=%p, c=%s",reference,pardir or "---",txtdir or "----",tosequence(first,last,true),width,height,depth,resolved)
+ end
+ local prev = first.prev
+ if prev then
+ result.next = first
+ result.prev = prev
+ prev.next = result
+ first.prev = result
+ else
+ result.next = first
+ first.prev = result
+ end
+ if first == head.next then
+ head.next = result -- hm, weird
+ end
+ return head, last
+ end
+ else
+ return head, last
+ end
+end
+
+local function inject_list(id,current,reference,make,stack,pardir,txtdir)
+ local width, height, depth, correction = current.width, current.height, current.depth, 0
+ local moveright = false
+ local first = current.list
+ 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 last.id == glue_code and last.subtype == rightskip_code then
+ local prev = last.prev
+ moveright = first.id == glue_code and first.subtype == leftskip_code
+ if prev and prev.id == glue_code and prev.subtype == parfillskip_code then
+ width = dimensions(current,first,prev.prev) -- maybe not current as we already take care of it
+ else
+ if moveright and first.writable then
+ width = width - first.spec.stretch*current.glue_set * current.glue_sign
+ end
+ if last.writable then
+ width = width - last.spec.stretch*current.glue_set * current.glue_sign
+ end
+ 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_backend then
+ report_area("box: %04i %s %s: w=%p, h=%p, d=%p, c=%s",reference,pardir or "---",txtdir or "----",width,height,depth,resolved)
+ end
+ if not first then
+ current.list = result
+ elseif moveright then -- brr no prevs done
+ -- result after first
+ local n = first.next
+ result.next = n
+ first.next = result
+ result.prev = first
+ if n then n.prev = result end
+ else
+ -- first after result
+ result.next = first
+ first.prev = result
+ current.list = result
+ end
+ end
+end
+
+-- skip is somewhat messy
+
+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 = current.id
+ if id == hlist_code or id == vlist_code then
+ local r = current[attribute]
+ -- somehow reference is true so the following fails (second one not done) in
+ -- test \goto{test}[page(2)] test \gotobox{test}[page(2)]
+ -- so let's wait till this fails again
+ -- if not reference and r and (not skip or r > skip) then -- > or ~=
+ if r and (not skip or r > skip) then -- > or ~=
+ inject_list(id,current,r,make,stack,pardir,txtdir)
+ end
+ if r then
+ done[r] = (done[r] or 0) + 1
+ end
+ local list = current.list
+ if list then
+ local _
+ current.list, _, pardir, txtdir = inject_areas(list,attribute,make,stack,done,r or skip or 0,current,pardir,txtdir)
+ end
+ if r then
+ done[r] = done[r] - 1
+ end
+ elseif id == whatsit_code then
+ local subtype = current.subtype
+ if subtype == localpar_code then
+ pardir = current.dir
+ elseif subtype == dir_code then
+ txtdir = current.dir
+ end
+ elseif id == glue_code and current.subtype == leftskip_code then -- any glue at the left?
+ --
+ else
+ local r = 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 current.subtype == 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 = current.next
+ 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 = current.id
+ if id == hlist_code or id == vlist_code then
+ local r = current[attribute]
+ if r and not done[r] then
+ done[r] = true
+ inject_list(id,current,r,make,stack,pardir,txtdir)
+ end
+ local list = current.list
+ if list then
+ current.list = inject_area(list,attribute,make,stack,done,current,pardir,txtdir)
+ end
+ elseif id == whatsit_code then
+ local subtype = current.subtype
+ if subtype == localpar_code then
+ pardir = current.dir
+ elseif subtype == dir_code then
+ txtdir = current.dir
+ end
+ else
+ local r = 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 = current.next
+ end
+ end
+ return head, true
+end
+
+-- tracing
+
+local nodepool = nodes.pool
+
+local new_rule = nodepool.rule
+local new_kern = nodepool.kern
+
+local set_attribute = node.set_attribute
+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 colorize(width,height,depth,n,reference,what)
+ 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)
+ rule[a_colormodel] = 1 -- gray color model
+ rule[a_color] = u_color
+ rule[a_transparency] = u_transparency
+ if width < 0 then
+ local kern = new_kern(width)
+ rule.width = -width
+ kern.next = rule
+ rule.prev = kern
+ return kern
+ else
+ return rule
+ end
+end
+
+local nodepool = nodes.pool
+
+local new_kern = nodepool.kern
+
+local texattribute = tex.attribute
+local texcount = tex.count
+
+-- references:
+
+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) }
+ -- texattribute[attribute] = topofstack -- todo -> at tex end
+ texcount.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)
+ 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]
+ if ht then
+ if height < ht then height = ht end
+ if depth < dp then depth = dp end
+ end
+ local annot = nodeinjections.reference(width,height,depth,set)
+ if annot then
+ nofreferences = nofreferences + 1
+ local result, current
+ if trace_references then
+ local step = 65536
+ result = hpack_list(colorize(width,height-step,depth-step,2,reference,"reference")) -- step subtracted so that we can see seperate links
+ result.width = 0
+ current = result
+ end
+ if current then
+ current.next = annot
+ else
+ result = annot
+ end
+ references.registerpage(n)
+ result = hpack_list(result,0)
+ result.width, result.height, result.depth = 0, 0, 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
+
+-- 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]
+ if ht then
+ if height < ht then height = ht end
+ if depth < dp then depth = dp end
+ end
+ local result, current
+ if trace_destinations then
+ local step = 0
+ if width == 0 then
+ step = 4*65536
+ width, height, depth = 5*step, 5*step, 0
+ end
+ for n=1,#name do
+ local rule = hpack_list(colorize(width,height,depth,3,reference,"destination"))
+ rule.width = 0
+ if not result then
+ result, current = rule, rule
+ else
+ current.next = rule
+ rule.prev = current
+ current = rule
+ end
+ width, height = width - step, height - step
+ end
+ end
+ nofdestinations = nofdestinations + 1
+ for n=1,#name do
+ local annot = nodeinjections.destination(width,height,depth,name[n],view)
+ if not result then
+ result = annot
+ else
+ current.next = annot
+ annot.prev = current
+ end
+ current = find_node_tail(annot)
+ end
+ if result then
+ -- some internal error
+ result = hpack_list(result,0)
+ result.width, result.height, result.depth = 0, 0, 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
+
+-- will move
+
+function references.mark(reference,h,d,view)
+ return setdestination(tex.currentgrouplevel,h,d,reference,view)
+end
+
+function references.inject(prefix,reference,h,d,highlight,newwindow,layer) -- 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
+ -- check
+ set.highlight, set.newwindow,set.layer = highlight, newwindow, layer
+ setreference(h,d,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
+
+commands.injectreference = references.inject
+commands.injectcurrentreference = references.injectcurrentset
+
+--
+
+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 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")
+end