summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/tex/context/base/node-ref.lua
diff options
context:
space:
mode:
Diffstat (limited to 'Master/texmf-dist/tex/context/base/node-ref.lua')
-rw-r--r--Master/texmf-dist/tex/context/base/node-ref.lua373
1 files changed, 307 insertions, 66 deletions
diff --git a/Master/texmf-dist/tex/context/base/node-ref.lua b/Master/texmf-dist/tex/context/base/node-ref.lua
index 78ef409bca5..9703cc184a9 100644
--- a/Master/texmf-dist/tex/context/base/node-ref.lua
+++ b/Master/texmf-dist/tex/context/base/node-ref.lua
@@ -16,6 +16,8 @@ if not modules then modules = { } end modules ['node-ref'] = {
-- is grouplevel still used?
+local concat = table.concat
+
local attributes, nodes, node = attributes, nodes, node
local allocate = utilities.storage.allocate, utilities.storage.mark
@@ -32,9 +34,11 @@ local colors = attributes.colors
local references = structures.references
local tasks = nodes.tasks
-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 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")
@@ -57,6 +61,7 @@ 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
@@ -69,6 +74,8 @@ 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 whatsit_code = nodecodes.whatsit
local leftskip_code = skipcodes.leftskip
@@ -83,68 +90,147 @@ 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 function dimensions(parent,start,stop)
--- stop = stop and getnext(stop)
--- if parent then
--- if stop then
--- return list_dimensions(getfield(parent,"glue_set"),getfield(parent,"glue_sign"),getfield(parent,"glue_order"),start,stop)
--- else
--- return list_dimensions(getfield(parent,"glue_set"),getfield(parent,"glue_sign",getfield(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 implement = interfaces.implement
+
+-- Normally a (destination) area is a box or a simple stretch if nodes but when it is
+-- a paragraph we hav ea problem: we cannot calculate the height well. This happens
+-- with footnotes or content broken across a page.
+
+local function vlist_dimensions(start,stop)
+ local temp
+ if stop then
+ temp = getnext(stop)
+ setfield(stop,"next",nil)
+ end
+ local v = vpack_list(start)
+ local w = getfield(v,"width")
+ local h = getfield(v,"height")
+ local d = getfield(v,"depth")
+ setfield(v,"list",nil)
+ free_node(v)
+ if temp then
+ setfield(stop,"next",temp)
+ end
+ return w, h, d
+end
-local function dimensions(parent,start,stop)
+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 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 -- or image
+ if trace_areas then
+ report_area("dimensions taken of %a",nodecodes[id])
+ end
+ return getfield(start,"width"), getfield(parent,"height"), getfield(parent,"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
- return list_dimensions(getfield(parent,"glue_set"),getfield(parent,"glue_sign"),getfield(parent,"glue_order"),start,stop and getnext(stop))
+ -- 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
+ 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
+ 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
- return list_dimensions(start,stop and getnext(stop))
+ 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 = dimensions(parent,first,last)
+ 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 head == first then
- if trace_backend then
- report_area("%s: %04i %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)
+ 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: %04i %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
- setfield(result,"next",first)
- setfield(first,"prev",result)
- return result, last
+ setfield(line,"list",result)
+ setfield(result,"next",l)
+ setfield(l,"prev",result)
+ return head, last
else
- if trace_backend then
- report_area("%s: %04i %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
- setfield(prev,"next",result)
- setfield(result,"prev",prev)
+ if head == first then
+ if trace_areas then
+ report_area("%s: %04i %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
+ setfield(result,"next",first)
+ setfield(first,"prev",result)
+ return result, last
+ else
+ if trace_areas then
+ report_area("%s: %04i %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
+ setfield(prev,"next",result)
+ setfield(result,"prev",prev)
+ end
+ setfield(result,"next",first)
+ setfield(first,"prev",result)
+ -- if first == getnext(head) then
+ -- setfield(head,"next",result) -- hm, weird
+ -- end
+ return head, last
end
- setfield(result,"next",first)
- setfield(first,"prev",result)
--- if first == getnext(head) then
--- setfield(head,"next",result) -- hm, weird
--- end
- return head, last
end
else
return head, last
@@ -152,9 +238,12 @@ local function inject_range(head,first,last,reference,make,stack,parent,pardir,t
end
local function inject_list(id,current,reference,make,stack,pardir,txtdir)
- local width, height, depth, correction = getfield(current,"width"), getfield(current,"height"), getfield(current,"depth"), 0
- local moveright = false
- local first = getlist(current)
+ 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)
@@ -193,7 +282,7 @@ local function inject_list(id,current,reference,make,stack,pardir,txtdir)
local result, resolved = make(width,height,depth,reference)
-- todo: only when width is ok
if result and resolved then
- if trace_backend 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
@@ -227,6 +316,10 @@ local function inject_areas(head,attribute,make,stack,done,skip,parent,pardir,tx
while current do
local id = getid(current)
if id == hlist_code or id == vlist_code then
+
+ -- see dimensions: this is tricky with split off boxes like inserts
+ -- where we can end up with a first and last spanning lines
+
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)]
@@ -330,7 +423,7 @@ local function inject_area(head,attribute,make,stack,done,parent,pardir,txtdir)
return head, true
end
--- tracing
+-- tracing: todo: use predefined colors
local register_color = colors.register
@@ -341,7 +434,33 @@ local u_transparency = nil
local u_colors = { }
local force_gray = true
-local function colorize(width,height,depth,n,reference,what)
+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.fast_hpack(str,infofont)
+ local rule = new_rule(emwidth/5,4*exheight,3*exheight)
+ local list = getlist(text)
+ setfield(text,"shift",shift)
+ return nuts.fasthpack(nuts.linked(text,rule))
+ -- local text = typesetters.fast_hpack(str,fonts.infofont())
+ -- return text
+ 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]
@@ -379,10 +498,37 @@ local function colorize(width,height,depth,n,reference,what)
setfield(rule,"prev",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"))
+ setfield(kern,"next",text)
+ setfield(text,"prev",kern)
+ setfield(text,"next",rule)
+ setfield(rule,"prev",text)
+ 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"))
+ setfield(kern,"next",text)
+ setfield(text,"prev",kern)
+ setfield(text,"next",rule)
+ setfield(rule,"prev",text)
+ return kern
+ end
+ end
+end
+
-- references:
local texsetattribute = tex.setattribute
@@ -423,23 +569,45 @@ local function makereference(width,height,depth,reference) -- height and depth a
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)
+ -- 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)
--- step = 0
+ -- logs.report("temp","used: ht=%p dp=%p",height,depth)
local annot = nodeinjections.reference(width,height,depth,set)
if annot then
-annot = tonut(annot)
+ annot = tonut(annot) -- todo
nofreferences = nofreferences + 1
- local result, current
+ 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")) -- step subtracted so that we can see seperate links
+ 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
setfield(current,"next",annot)
@@ -506,19 +674,49 @@ local function makedestination(width,height,depth,reference)
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]
+ 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
+ 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"))
+ 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
@@ -528,6 +726,15 @@ local function makedestination(width,height,depth,reference)
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)
@@ -580,14 +787,17 @@ 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
+function references.inject(prefix,reference,specification) -- todo: use currentreference is possible
+-- print(prefix,reference,h,d,highlight,newwindow,layer)
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
+ 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
@@ -598,8 +808,32 @@ function references.injectcurrentset(h,d) -- used inside doifelse
end
end
-commands.injectreference = references.inject
-commands.injectcurrentreference = references.injectcurrentset
+implement {
+ name = "injectreference",
+ actions = references.inject,
+ arguments = {
+ "string",
+ "string",
+ {
+ { "highlight", "boolean" },
+ { "newwindow", "boolean" },
+ { "layer" },
+ { "height", "dimen" },
+ { "depth", "dimen" },
+ }
+ }
+}
+
+implement {
+ name = "injectcurrentreference",
+ actions = references.injectcurrentset,
+}
+
+implement {
+ name = "injectcurrentreferencehtdp",
+ actions = references.injectcurrentset,
+ arguments = { "dimen", "dimen" },
+}
--
@@ -628,4 +862,11 @@ 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
+}