diff options
Diffstat (limited to 'Master/texmf-dist/tex/context/base/mkiv/node-rul.lua')
-rw-r--r-- | Master/texmf-dist/tex/context/base/mkiv/node-rul.lua | 665 |
1 files changed, 391 insertions, 274 deletions
diff --git a/Master/texmf-dist/tex/context/base/mkiv/node-rul.lua b/Master/texmf-dist/tex/context/base/mkiv/node-rul.lua index 63991813ead..4ec651d3b80 100644 --- a/Master/texmf-dist/tex/context/base/mkiv/node-rul.lua +++ b/Master/texmf-dist/tex/context/base/mkiv/node-rul.lua @@ -11,263 +11,113 @@ if not modules then modules = { } end modules ['node-rul'] = { -- -- todo: make robust for layers ... order matters -local attributes, nodes, node = attributes, nodes, node - -local nuts = nodes.nuts -local tonode = nuts.tonode -local tonut = nuts.tonut - -local getfield = nuts.getfield -local setfield = nuts.setfield -local setnext = nuts.setnext -local setprev = nuts.setprev -local setlink = nuts.setlink -local getnext = nuts.getnext -local getprev = nuts.getprev -local getid = nuts.getid -local getattr = nuts.getattr -local setattr = nuts.setattr -local getfont = nuts.getfont -local getsubtype = nuts.getsubtype -local getlist = nuts.getlist -local setlist = nuts.setlist - -local nodecodes = nodes.nodecodes -local tasks = nodes.tasks - -local properties = nodes.properties -local attribs = node.current_attr - -local glyph_code = nodecodes.glyph -local disc_code = nodecodes.disc -local rule_code = nodecodes.rule -local boundary_code = nodecodes.boundary -local dir_code = nodecodes.dir - -function nodes.striprange(first,last) -- todo: dir - if first and last then -- just to be sure - if first == last then - return first, last - end - while first and first ~= last do - local id = getid(first) - if id == glyph_code or id == disc_code or id == dir_code or id == boundary_code then -- or id == rule_code - break - else - first = getnext(first) - end - end - if not first then - return nil, nil - elseif first == last then - return first, last - end - while last and last ~= first do - local id = getid(last) - if id == glyph_code or id == disc_code or id == dir_code or id == boundary_code then -- or id == rule_code - break - else - local prev = getprev(last) -- luatex < 0.70 has italic correction kern not prev'd - if prev then - last = prev - else - break - end - end - end - if not last then - return nil, nil - end - end - return first, last -end - --- todo: order and maybe other dimensions - -local floor = math.floor - -local trace_ruled = false trackers.register("nodes.rules", function(v) trace_ruled = v end) -local report_ruled = logs.reporter("nodes","rules") - -local n_tostring = nodes.idstostring -local n_tosequence = nodes.tosequence - -local a_ruled = attributes.private('ruled') -local a_color = attributes.private('color') -local a_transparency = attributes.private('transparency') -local a_colorspace = attributes.private('colormodel') - -local insert_node_before = nuts.insert_before +-- todo: collect successive bit and pieces and combine them +-- +-- path s ; s := shaped(p) ; % p[] has rectangles +-- fill s withcolor .5white ; +-- draw boundingbox s withcolor yellow; + +local attributes = attributes +local nodes = nodes +local properties = nodes.properties + +local enableaction = nodes.tasks.enableaction + +local nuts = nodes.nuts +local tonode = nuts.tonode +local tonut = nuts.tonut + +local getfield = nuts.getfield +local setfield = nuts.setfield +local setnext = nuts.setnext +local setprev = nuts.setprev +local setlink = nuts.setlink +local getnext = nuts.getnext +local getprev = nuts.getprev +local getid = nuts.getid +local getattr = nuts.getattr +local setattr = nuts.setattr +local getfont = nuts.getfont +local getsubtype = nuts.getsubtype +local getlist = nuts.getlist +local setwhd = nuts.setwhd +local setdir = nuts.setdir +local setattrlist = nuts.setattrlist +local setshift = nuts.setshift +local getwidth = nuts.getwidth +local setwidth = nuts.setwidth + +local flushlist = nuts.flush_list +local effective_glue = nuts.effective_glue local insert_node_after = nuts.insert_after -local list_dimensions = nuts.dimensions +local insert_node_before = nuts.insert_before +local find_tail = nuts.tail +local setglue = nuts.setglue +local traverse_id = nuts.traverse_id +local list_dimensions = nuts.rangedimensions local hpack_nodes = nuts.hpack - -local striprange = nodes.striprange - -local fontdata = fonts.hashes.identifiers -local variables = interfaces.variables -local dimenfactor = fonts.helpers.dimenfactor -local splitdimen = number.splitdimen - -local v_yes = variables.yes -local v_all = variables.all -local v_foreground = variables.foreground +local current_attr = nuts.current_attr local nodecodes = nodes.nodecodes -local skipcodes = nodes.skipcodes +local rulecodes = nodes.rulecodes +local gluecodes = nodes.gluecodes +local listcodes = nodes.listcodes local kerncodes = nodes.kerncodes local glyph_code = nodecodes.glyph -local disc_code = nodecodes.disc +local localpar_code = nodecodes.localpar +local dir_code = nodecodes.dir local glue_code = nodecodes.glue -local penalty_code = nodecodes.penalty -local kern_code = nodecodes.kern local hlist_code = nodecodes.hlist -local vlist_code = nodecodes.vlist -local rule_code = nodecodes.rule -local boundary_code = nodecodes.boundary -local dir_code = nodecodes.dir -local userskip_code = skipcodes.userskip -local spaceskip_code = skipcodes.spaceskip -local xspaceskip_code = skipcodes.xspaceskip -local leader_code = skipcodes.leaders +local indent_code = listcodes.indent +local line_code = listcodes.line -local kerning_code = kerncodes.kern +local leftskip_code = gluecodes.leftskip +local rightskip_code = gluecodes.rightskip +local parfillskip_code = gluecodes.parfillskip local nodepool = nuts.pool local new_rule = nodepool.rule local new_userrule = nodepool.userrule local new_kern = nodepool.kern -local new_glue = nodepool.glue --- we can use this one elsewhere too --- --- todo: functions: word, sentence --- --- glyph rule unset whatsit glue margin_kern kern math disc +local n_tostring = nodes.idstostring +local n_tosequence = nodes.tosequence -local checkdir = true +local variables = interfaces.variables +local implement = interfaces.implement + +local privateattributes = attributes.private + +local a_ruled = privateattributes('ruled') +local a_runningtext = privateattributes('runningtext') +local a_color = privateattributes('color') +local a_transparency = privateattributes('transparency') +local a_colormodel = privateattributes('colormodel') +local a_linefiller = privateattributes("linefiller") +local a_viewerlayer = privateattributes("viewerlayer") + +local v_both = variables.both +local v_left = variables.left +local v_right = variables.right +local v_local = variables["local"] +local v_yes = variables.yes +local v_foreground = variables.foreground --- we assume {glyphruns} and no funny extra kerning, ok, maybe we need --- a dummy character as start and end; anyway we only collect glyphs --- --- this one needs to take layers into account (i.e. we need a list of --- critical attributes) - --- omkeren class en level -> scheelt functie call in analyze - --- todo: switching inside math - --- handlers - -local function processwords(attribute,data,flush,head,parent) -- we have hlistdir and local dir - local n = head - if n then - local f, l, a, d, i, class - local continue, leaders, done, strip, level = false, false, false, true, -1 - while n do - local id = getid(n) - if id == glyph_code or id == rule_code then - local aa = getattr(n,attribute) - if aa then - if aa == a then - if not f then -- ? - f = n - end - l = n - else - -- possible extensions: when in same class then keep spanning - local newlevel, newclass = floor(aa/1000), aa%1000 - -- strip = not continue or level == 1 -- 0 - if f then - if class == newclass then -- and newlevel > level then - head, done = flush(head,f,l,d,level,parent,false), true - else - head, done = flush(head,f,l,d,level,parent,strip), true - end - end - f, l, a = n, n, aa - level, class = newlevel, newclass - d = data[class] - local c = d.continue - leaders = c == v_all - continue = leaders or c == v_yes - end - else - if f then - head, done = flush(head,f,l,d,level,parent,strip), true - end - f, l, a = nil, nil, nil - end - elseif id == disc_code or id == boundary_code then - if f then - l = n - end - elseif id == kern_code and getsubtype(n) == kerning_code then - if f then - l = n - end - elseif id == hlist_code or id == vlist_code then - if f then - head, done = flush(head,f,l,d,level,parent,strip), true - f, l, a = nil, nil, nil - end - local list = getlist(n) - if list then - setlist(n,(processwords(attribute,data,flush,list,n))) -- watch () - end --- elseif checkdir and id == dir_code then -- only changes in dir, we assume proper boundaries --- if f and a then --- l = n --- end - elseif id == dir_code then -- only changes in dir, we assume proper boundaries - if f then - l = n - end - elseif f then - if continue then - if id == penalty_code then - l = n - -- elseif id == kern_code then - -- l = n - elseif id == glue_code then - -- catch \underbar{a} \underbar{a} (subtype test is needed) - local subtype = getsubtype(n) - if getattr(n,attribute) and (subtype == userskip_code or subtype == spaceskip_code or subtype == xspaceskip_code or (leaders and subtype >= leader_code)) then - l = n - else - head, done = flush(head,f,l,d,level,parent,strip), true - f, l, a = nil, nil, nil - end - end - else - head, done = flush(head,f,l,d,level,parent,strip), true - f, l, a = nil, nil, nil - end - end - n = getnext(n) - end - if f then - head, done = flush(head,f,l,d,level,parent,strip), true - end - return head, true -- todo: done - else - return head, false - end -end +local fonthashes = fonts.hashes +local fontdata = fonthashes.identifiers +local fontresources = fonthashes.resources --- nodes.processwords = processwords +local dimenfactor = fonts.helpers.dimenfactor +local splitdimen = number.splitdimen +local setmetatableindex = table.setmetatableindex -nodes.processwords = function(attribute,data,flush,head,parent) -- we have hlistdir and local dir - head = tonut(head) - if parent then - parent = tonut(parent) - end - local head, done = processwords(attribute,data,flush,head,parent) - return tonode(head), done -end +-- + +local striprange = nodes.striprange +local processwords = nodes.processwords -- @@ -286,7 +136,7 @@ local function userrule(t,noattributes) if noattributes == false or noattributes == nil then -- avoid fuzzy ones else - setfield(r,"attr",attribs()) + setattrlist(r,current_attr()) end properties[r] = t return tonode(r) @@ -296,8 +146,31 @@ rules.userrule = userrule local ruleactions = { } rules.ruleactions = ruleactions -callback.register("process_rule",function(n,h,v) - local n = tonut(n) +local function mathradical(n,h,v) + ----- size = getfield(n,"index") + local font = getfield(n,"transform") + local actions = fontresources[font].mathruleactions + if actions then + local action = actions.radicalaction + if action then + action(n,h,v,font) + end + end +end + +local function mathrule(n,h,v) + ----- size = getfield(n,"index") + local font = getfield(n,"transform") + local actions = fontresources[font].mathruleactions + if actions then + local action = actions.hruleaction + if action then + action(n,h,v,font) + end + end +end + +local function useraction(n,h,v) local p = properties[n] if p then local i = p.type or "draw" @@ -306,19 +179,48 @@ callback.register("process_rule",function(n,h,v) a(p,h,v,i,n) end end -end) +end + +local subtypeactions = { + [rulecodes.user] = useraction, + [rulecodes.over] = mathrule, + [rulecodes.under] = mathrule, + [rulecodes.fraction] = mathrule, + [rulecodes.radical] = mathradical, +} + +callbacks.register( + "process_rule", + function(n,h,v) + local n = tonut(n) + local s = getsubtype(n) + local a = subtypeactions[s] + if a then + a(n,h,v) + end + end, + "handle additional user rule features" +) -- +local trace_ruled = false trackers.register("nodes.rules", function(v) trace_ruled = v end) +local report_ruled = logs.reporter("nodes","rules") + function rules.define(settings) data[#data+1] = settings context(#data) end -local a_viewerlayer = attributes.private("viewerlayer") - local function flush_ruled(head,f,l,d,level,parent,strip) -- not that fast but acceptable for this purpose - if getid(f) ~= glyph_code then + local font = nil + local id = getid(f) + if id == glyph_code then + font = getfont(f) + elseif id == hlist_code then + font = getattr(f,a_runningtext) + end + if not font then -- saveguard ... we need to deal with rules and so (math) return head end @@ -336,8 +238,9 @@ local function flush_ruled(head,f,l,d,level,parent,strip) -- not that fast but a if not f then return head end - local w, ht, dp = list_dimensions(getfield(parent,"glue_set"),getfield(parent,"glue_sign"),getfield(parent,"glue_order"),f,getnext(l)) + local w, ht, dp = list_dimensions(parent,f,getnext(l)) local method = d.method + local empty = d.empty == v_yes local offset = d.offset local continue = d.continue local dy = d.dy @@ -349,19 +252,19 @@ local function flush_ruled(head,f,l,d,level,parent,strip) -- not that fast but a local ma = d.ma local ca = d.ca local ta = d.ta - local colorspace = ma > 0 and ma or getattr(f,a_colorspace) or 1 + local colorspace = ma > 0 and ma or getattr(f,a_colormodel) or 1 local color = ca > 0 and ca or getattr(f,a_color) local transparency = ta > 0 and ta or getattr(f,a_transparency) local foreground = order == v_foreground local layer = getattr(f,a_viewerlayer) - local e = dimenfactor(unit,getfont(f)) -- what if no glyph node + local e = dimenfactor(unit,font) -- what if no glyph node local rt = tonumber(rulethickness) if rt then rulethickness = e * rulethickness / 2 else local n, u = splitdimen(rulethickness) if n and u then -- we need to intercept ex and em and % and ... - rulethickness = n * dimenfactor(u,fontdata[getfont(f)]) / 2 + rulethickness = n * dimenfactor(u,fontdata[font]) / 2 else rulethickness = 1/5 end @@ -381,21 +284,28 @@ local function flush_ruled(head,f,l,d,level,parent,strip) -- not that fast but a if layer then setattr(r,a_viewerlayer,layer) end - local k = new_kern(-w) - if foreground then - insert_node_after(head,l,k) - insert_node_after(head,k,r) - l = r - else + if empty then head = insert_node_before(head,f,r) - insert_node_after(head,r,k) + setlink(r,getnext(l)) + setprev(f) + setnext(l) + flushlist(f) + else + local k = new_kern(-w) + if foreground then + insert_node_after(head,l,k) + insert_node_after(head,k,r) + l = r + else + head = insert_node_before(head,f,r) + insert_node_after(head,r,k) + end end if trace_ruled then report_ruled("level %a, width %p, height %p, depth %p, nodes %a, text %a", level,w,ht,dp,n_tostring(f,l),n_tosequence(f,l,true)) end end - if mp and mp ~= "" then local r = userrule { width = w, @@ -417,7 +327,7 @@ local function flush_ruled(head,f,l,d,level,parent,strip) -- not that fast but a local dp = -(offset+(i-1)*dy)*e + rulethickness + m local r = new_rule(w,ht,dp) if color then - setattr(r,a_colorspace,colorspace) + setattr(r,a_colormodel,colorspace) setattr(r,a_color,color) end if transparency then @@ -431,17 +341,14 @@ end local process = nodes.processwords -rules.handler = function(head) return process(a_ruled,data,flush_ruled,head) end +rules.handler = function(head) + return process(a_ruled,data,flush_ruled,head) +end function rules.enable() - tasks.enableaction("shipouts","nodes.rules.handler") + enableaction("shipouts","nodes.rules.handler") end --- elsewhere: --- --- tasks.appendaction ("shipouts", "normalizers", "nodes.rules.handler") --- tasks.disableaction("shipouts", "nodes.rules.handler") -- only kick in when used - local trace_shifted = false trackers.register("nodes.shifting", function(v) trace_shifted = v end) local report_shifted = logs.reporter("nodes","shifting") @@ -468,8 +375,8 @@ local function flush_shifted(head,first,last,data,level,parent,strip) -- not tha local next = getnext(last) setprev(first) setnext(last) - local width, height, depth = list_dimensions(getfield(parent,"glue_set"),getfield(parent,"glue_sign"),getfield(parent,"glue_order"),first,next) - local list = hpack_nodes(first,width,"exactly") + local width, height, depth = list_dimensions(parent,first,next) + local list = hpack_nodes(first,width,"exactly") -- we can use a simple pack if first == head then head = list end @@ -480,9 +387,8 @@ local function flush_shifted(head,first,last,data,level,parent,strip) -- not tha setlink(list,next) end local raise = data.dy * dimenfactor(data.unit,fontdata[getfont(first)]) - setfield(list,"shift",raise) - setfield(list,"height",height) - setfield(list,"depth",depth) + setshift(list,raise) + setwhd(list,width,height,depth) if trace_shifted then report_shifted("width %p, nodes %a, text %a",width,n_tostring(first,last),n_tosequence(first,last,true)) end @@ -494,21 +400,204 @@ local process = nodes.processwords nodes.shifts.handler = function(head) return process(a_shifted,data,flush_shifted,head) end function nodes.shifts.enable() - tasks.enableaction("shipouts","nodes.shifts.handler") + enableaction("shipouts","nodes.shifts.handler") end --- linefillers (placeholder) +-- linefillers nodes.linefillers = nodes.linefillers or { } nodes.linefillers.data = nodes.linefillers.data or { } +storage.register("nodes/linefillers/data", nodes.linefillers.data, "nodes.linefillers.data") + +local data = nodes.linefillers.data + +function nodes.linefillers.define(settings) + data[#data+1] = settings + context(#data) +end + +local function linefiller(current,data,width,location) + local height = data.height + local depth = data.depth + local mp = data.mp + local ma = data.ma + local ca = data.ca + local ta = data.ta + if mp and mp ~= "" then + return tonut(userrule { + width = width, + height = height, + depth = depth, + type = "mp", + line = data.rulethickness, + data = mp, + ma = ma, + ca = ca, + ta = ta, + option = location, + direction = getdir(current), + }) + else + local linefiller = new_rule(width,height,depth) + if ca then + setattr(linefiller,a_colorspace,ma) + setattr(linefiller,a_color,ca) + end + if ta then + setattr(linefiller,a_transparency,ta) + end + return linefiller + end +end + +local function find_attr(head,attr) + while head do + local a = head[attr] + if a then + return a, head + end + head = getnext(head) + end +end + function nodes.linefillers.handler(head) - return head, false +-- local current = tonut(head) -- when we hook into the contributers + for current in traverse_id(hlist_code,tonut(head)) do + if getsubtype(current) == line_code then + local list = getlist(current) + if list then + -- why doesn't leftskip take the attributes + -- or list[linefiller] or maybe first match (maybe we need a fast helper for that) + local a = getattr(current,a_linefiller) + if a then + local class = a % 1000 + local data = data[class] + if data then + local location = data.location + local scope = data.scope + local distance = data.distance + local threshold = data.threshold + local leftlocal = false + local rightlocal = false + -- + if scope == v_right then + leftlocal = true + elseif scope == v_left then + rightlocal = true + elseif scope == v_local then + leftlocal = true + rightlocal = true + end + -- + if location == v_left or location == v_both then + local lskip = nil -- leftskip + local iskip = nil -- indentation + local head = list + while head do + local id = getid(head) + if id == glue_code then + if getsubtype(head) == leftskip_code then + lskip = head + else + break + end + elseif id == localpar_code or id == dir_code then + -- go on + elseif id == hlist_code then + if getsubtype(head) == indent_code then + iskip = head + end + break + else + break + end + head = getnext(head) + end + if head then + local indentation = iskip and getwidth(iskip) or 0 + local leftfixed = lskip and getwidth(lskip) or 0 + local lefttotal = lskip and effective_glue(lskip,current) or 0 + local width = lefttotal - (leftlocal and leftfixed or 0) + indentation - distance + if width > threshold then + if iskip then + setwidth(iskip,0) + end + if lskip then + setglue(lskip,leftlocal and getwidth(lskip) or nil) + if distance > 0 then + insert_node_after(list,lskip,new_kern(distance)) + end + insert_node_after(list,lskip,linefiller(current,data,width,"left")) + else + insert_node_before(list,head,linefiller(current,data,width,"left")) + if distance > 0 then + insert_node_before(list,head,new_kern(distance)) + end + end + end + end + end + -- + if location == v_right or location == v_both then + local pskip = nil -- parfillskip + local rskip = nil -- rightskip + local tail = find_tail(list) + while tail and getid(tail) == glue_code do + local subtype = getsubtype(tail) + if subtype == rightskip_code then + rskip = tail + elseif subtype == parfillskip_code then + pskip = tail + else + break + end + tail = getprev(tail) + end + if tail then + local rightfixed = rskip and getwidth(rskip) or 0 + local righttotal = rskip and effective_glue(rskip,current) or 0 + local parfixed = pskip and getwidth(pskip) or 0 + local partotal = pskip and effective_glue(pskip,current) or 0 + local width = righttotal - (rightlocal and rightfixed or 0) + partotal - distance + if width > threshold then + if pskip then + setglue(pskip) + end + if rskip then + setglue(rskip,rightlocal and getwidth(rskip) or nil) + if distance > 0 then + insert_node_before(list,rskip,new_kern(distance)) + end + insert_node_before(list,rskip,linefiller(current,data,width,"right")) + else + insert_node_after(list,tail,linefiller(current,data,width,"right")) + if distance > 0 then + insert_node_after(list,tail,new_kern(distance)) + end + end + end + end + end + end + end + end + end + end + return head end --- interface +local enable = false + +function nodes.linefillers.enable() + if not enable then + -- we could now nil it + enableaction("finalizers","nodes.linefillers.handler") + enable = true + end +end -local implement = interfaces.implement +-- interface implement { name = "definerule", @@ -520,13 +609,14 @@ implement { { "order" }, { "method", "integer" }, { "offset", "number" }, - { "rulethickness", "string" }, + { "rulethickness" }, { "dy", "number" }, { "max", "number" }, { "ma", "integer" }, { "ca", "integer" }, { "ta", "integer" }, - { "mp", "string" }, + { "mp" }, + { "empty" }, } } } @@ -555,3 +645,30 @@ implement { onlyonce = true, actions = nodes.shifts.enable } + +implement { + name = "definelinefiller", + actions = { nodes.linefillers.define, context }, + arguments = { + { + { "method", "integer" }, + { "location", "string" }, + { "scope", "string" }, + { "mp", "string" }, + { "ma", "integer" }, + { "ca", "integer" }, + { "ta", "integer" }, + { "depth", "dimension" }, + { "height", "dimension" }, + { "distance", "dimension" }, + { "threshold", "dimension" }, + { "rulethickness", "dimension" }, + } + } +} + +implement { + name = "enablelinefillers", + onlyonce = true, + actions = nodes.linefillers.enable +} |