From fc4466b32ed330a956ac603b00fd145524cff49a Mon Sep 17 00:00:00 2001 From: Karl Berry Date: Fri, 22 Apr 2016 22:14:39 +0000 Subject: context (22apr16) git-svn-id: svn://tug.org/texlive/trunk@40691 c570f23f-e606-0410-a88d-b1316a301751 --- .../texmf-dist/tex/context/base/mkiv/node-fnt.lua | 514 +++++++++++++++++++++ 1 file changed, 514 insertions(+) create mode 100644 Master/texmf-dist/tex/context/base/mkiv/node-fnt.lua (limited to 'Master/texmf-dist/tex/context/base/mkiv/node-fnt.lua') diff --git a/Master/texmf-dist/tex/context/base/mkiv/node-fnt.lua b/Master/texmf-dist/tex/context/base/mkiv/node-fnt.lua new file mode 100644 index 00000000000..7f0d239979a --- /dev/null +++ b/Master/texmf-dist/tex/context/base/mkiv/node-fnt.lua @@ -0,0 +1,514 @@ +if not modules then modules = { } end modules ['node-fnt'] = { + version = 1.001, + comment = "companion to font-ini.mkiv", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files", +} + +if not context then os.exit() end -- generic function in node-dum + +local next, type = next, type +local concat, keys = table.concat, table.keys + +local nodes, node, fonts = nodes, node, fonts + +local trace_characters = false trackers.register("nodes.characters", function(v) trace_characters = v end) +local trace_fontrun = false trackers.register("nodes.fontrun", function(v) trace_fontrun = v end) +local trace_variants = false trackers.register("nodes.variants", function(v) trace_variants = v end) + +local force_discrun = true directives.register("nodes.discrun", function(v) force_discrun = v end) +local force_boundaryrun = true directives.register("nodes.boundaryrun", function(v) force_boundaryrun = v end) +local force_basepass = true directives.register("nodes.basepass", function(v) force_basepass = v end) + +local report_fonts = logs.reporter("fonts","processing") + +local fonthashes = fonts.hashes +local fontdata = fonthashes.identifiers +local fontvariants = fonthashes.variants +local fontmodes = fonthashes.modes + +local otf = fonts.handlers.otf + +local starttiming = statistics.starttiming +local stoptiming = statistics.stoptiming + +local nodecodes = nodes.nodecodes +local handlers = nodes.handlers + +local nuts = nodes.nuts +local tonut = nuts.tonut +local tonode = nuts.tonode + +local getattr = nuts.getattr +local getid = nuts.getid +local getfont = nuts.getfont +local getsubtype = nuts.getsubtype +local getchar = nuts.getchar +local getdisc = nuts.getdisc +local getnext = nuts.getnext +local getprev = nuts.getprev +local getboth = nuts.getboth +local getfield = nuts.getfield +----- getdisc = nuts.getdisc +local setchar = nuts.setchar +local setlink = nuts.setlink +local setfield = nuts.setfield +local setprev = nuts.setprev + +local isglyph = nuts.isglyph -- unchecked +local ischar = nuts.ischar -- checked + +local traverse_id = nuts.traverse_id +local traverse_char = nuts.traverse_char +local remove_node = nuts.remove +local protect_glyph = nuts.protect_glyph +local free_node = nuts.free + +local glyph_code = nodecodes.glyph +local disc_code = nodecodes.disc +local boundary_code = nodecodes.boundary +local word_boundary = nodes.boundarycodes.word + +local setmetatableindex = table.setmetatableindex + +-- some tests with using an array of dynamics[id] and processes[id] demonstrated +-- that there was nothing to gain (unless we also optimize other parts) +-- +-- maybe getting rid of the intermediate shared can save some time + +-- potential speedup: check for subtype < 256 so that we can remove that test +-- elsewhere, danger: injected nodes will not be dealt with but that does not +-- happen often; we could consider processing sublists but that might need more +-- checking later on; the current approach also permits variants + +local run = 0 + +local setfontdynamics = { } +local fontprocesses = { } + +-- setmetatableindex(setfontdynamics, function(t,font) +-- local tfmdata = fontdata[font] +-- local shared = tfmdata.shared +-- local v = shared and shared.dynamics and otf.setdynamics or false +-- t[font] = v +-- return v +-- end) + +setmetatableindex(setfontdynamics, function(t,font) + local tfmdata = fontdata[font] + local shared = tfmdata.shared + local f = shared and shared.dynamics and otf.setdynamics or false + if f then + local v = { } + t[font] = v + setmetatableindex(v,function(t,k) + local v = f(font,k) + t[k] = v + return v + end) + return v + else + t[font] = false + return false + end +end) + +setmetatableindex(fontprocesses, function(t,font) + local tfmdata = fontdata[font] + local shared = tfmdata.shared -- we need to check shared, only when same features + local processes = shared and shared.processes + if processes and #processes > 0 then + t[font] = processes + return processes + else + t[font] = false + return false + end +end) + +fonts.hashes.setdynamics = setfontdynamics +fonts.hashes.processes = fontprocesses + +-- if we forget about basemode we don't need to test too much here and we can consider running +-- over sub-ranges .. this involves a bit more initializations but who cares .. in that case we +-- also need to use the stop criterium (we already use head too) ... we cannot use traverse +-- then, so i'll test it on some local clone first ... the only pitfall is changed directions +-- inside a run which means that we need to keep track of this which in turn complicates matters +-- in a way i don't like + +-- we need to deal with the basemode fonts here and can only run over ranges as we +-- otherwise get luatex craches due to all kind of asserts in the disc/lig builder + +local ligaturing = nuts.ligaturing +local kerning = nuts.kerning + +local expanders + +function fonts.setdiscexpansion(v) + if v == nil or v == true then + expanders = languages and languages.expanders + elseif type(v) == "table" then + expanders = v + else + expanders = false + end +end + +function fonts.getdiscexpansion() + return expanders and true or false +end + +fonts.setdiscexpansion(true) + +function handlers.characters(head) + -- either next or not, but definitely no already processed list + starttiming(nodes) + + local usedfonts = { } + local attrfonts = { } + local basefonts = { } + local a, u, b = 0, 0, 0 + local basefont = nil + local prevfont = nil + local prevattr = 0 + local mode = nil + local done = false + local variants = nil + local redundant = nil + + if trace_fontrun then + run = run + 1 + report_fonts() + report_fonts("checking node list, run %s",run) + report_fonts() + local n = tonut(head) + while n do + local char, id = isglyph(n) + if char then + local font = getfont(n) + local attr = getattr(n,0) or 0 + report_fonts("font %03i, dynamic %03i, glyph %C",font,attr,char) + elseif id == disc_code then + report_fonts("[disc] %s",nodes.listtoutf(n,true,false,n)) + elseif id == boundary_code then + report_fonts("[boundary] %i:%i",getsubtype(n),getfield(n,"value")) + else + report_fonts("[%s]",nodecodes[id]) + end + n = getnext(n) + end + end + + local nuthead = tonut(head) + + for n in traverse_char(nuthead) do + local font = getfont(n) + local attr = getattr(n,0) or 0 -- zero attribute is reserved for fonts in context + if font ~= prevfont or attr ~= prevattr then + prevfont = font + prevattr = attr + mode = fontmodes[font] -- we can also avoid the attr check + variants = fontvariants[font] + if mode == "none" then + -- skip + protect_glyph(n) + else + if basefont then + basefont[2] = getprev(n) + end + if attr > 0 then + local used = attrfonts[font] + if not used then + used = { } + attrfonts[font] = used + end + if not used[attr] then + local fd = setfontdynamics[font] + if fd then + used[attr] = fd[attr] + a = a + 1 + elseif force_basepass then + b = b + 1 + basefont = { n, nil } + basefonts[b] = basefont + end + end + else + local used = usedfonts[font] + if not used then + local fp = fontprocesses[font] + if fp then + usedfonts[font] = fp + u = u + 1 + elseif force_basepass then + b = b + 1 + basefont = { n, nil } + basefonts[b] = basefont + end + end + end + end + end + if variants then + local char = getchar(n) + if char >= 0xFE00 and (char <= 0xFE0F or (char >= 0xE0100 and char <= 0xE01EF)) then + local hash = variants[char] + if hash then + local p = getprev(n) + if p then + local char = ischar(p) -- checked + local variant = hash[char] + if variant then + if trace_variants then + report_fonts("replacing %C by %C",char,variant) + end + setchar(p,variant) + if not redundant then + redundant = { n } + else + redundant[#redundant+1] = n + end + end + end + end + end + end + end + + if force_boundaryrun then + + -- we can inject wordboundaries and then let the hyphenator do its work + -- but we need to get rid of those nodes in order to build ligatures + -- and kern (a rather context thing) + + for b in traverse_id(boundary_code,nuthead) do + if getsubtype(b) == word_boundary then + if redundant then + redundant[#redundant+1] = b + else + redundant = { b } + end + end + end + + end + + if redundant then + for i=1,#redundant do + local r = redundant[i] + local p, n = getboth(r) + if r == nuthead then + nuthead = n + setprev(n) + else + setlink(p,n) + end + if b > 0 then + for i=1,b do + local bi = basefonts[i] + if r == bi[1] then + bi[1] = n + end + if r == bi[2] then + bi[2] = n + end + end + end + free_node(r) + end + end + + local e = 0 + + if force_discrun then + + -- basefont is not supported in disc only runs ... it would mean a lot of + -- ranges .. we could try to run basemode as a separate processor run but + -- not for now (we can consider it when the new node code is tested + + -- local prevfont = nil + -- local prevattr = 0 + + for d in traverse_id(disc_code,nuthead) do + -- we could use first_glyph, only doing replace is good enough + local _, _, r = getdisc(d) + if r then + for n in traverse_char(r) do + local font = getfont(n) + local attr = getattr(n,0) or 0 -- zero attribute is reserved for fonts in context + if font ~= prevfont or attr ~= prevattr then + if attr > 0 then + local used = attrfonts[font] + if not used then + used = { } + attrfonts[font] = used + end + if not used[attr] then + local fd = setfontdynamics[font] + if fd then + used[attr] = fd[attr] + a = a + 1 + end + end + else + local used = usedfonts[font] + if not used then + local fp = fontprocesses[font] + if fp then + usedfonts[font] = fp + u = u + 1 + end + end + end + prevfont = font + prevattr = attr + end + end + break + elseif expanders then + local subtype = getsubtype(d) + if subtype == discretionary_code then + -- already done when replace + else + expanders[subtype](d) + e = e + 1 + end + end + end + + end + + if trace_fontrun then + report_fonts() + report_fonts("statics : %s",u > 0 and concat(keys(usedfonts)," ") or "none") + report_fonts("dynamics: %s",a > 0 and concat(keys(attrfonts)," ") or "none") + report_fonts("built-in: %s",b > 0 and b or "none") + report_fonts("removed : %s",redundant and #redundant > 0 and #redundant or "none") + if expanders then + report_fonts("expanded: %s",e > 0 and e or "none") + end + report_fonts() + end + -- in context we always have at least 2 processors + if u == 0 then + -- skip + elseif u == 1 then + local font, processors = next(usedfonts) + for i=1,#processors do + local h, d = processors[i](head,font,0) + if d then + head = h or head + done = true + end + end + else + for font, processors in next, usedfonts do + for i=1,#processors do + local h, d = processors[i](head,font,0) + if d then + head = h or head + done = true + end + end + end + end + if a == 0 then + -- skip + elseif a == 1 then + local font, dynamics = next(attrfonts) + for attribute, processors in next, dynamics do -- attr can switch in between + for i=1,#processors do + local h, d = processors[i](head,font,attribute) + if d then + head = h or head + done = true + end + end + end + else + for font, dynamics in next, attrfonts do + for attribute, processors in next, dynamics do -- attr can switch in between + for i=1,#processors do + local h, d = processors[i](head,font,attribute) + if d then + head = h or head + done = true + end + end + end + end + end + if b == 0 then + -- skip + elseif b == 1 then + -- only one font + local range = basefonts[1] + local start = range[1] + local stop = range[2] + if (start or stop) and (start ~= stop) then + local front = nuthead == start + if stop then + start, stop = ligaturing(start,stop) + start, stop = kerning(start,stop) + elseif start then -- safeguard + start = ligaturing(start) + start = kerning(start) + end + if front then + head = tonode(start) + end + end + else + -- multiple fonts + for i=1,b do + local range = basefonts[i] + local start = range[1] + local stop = range[2] + if start then + local front = nuthead == start + local prev, next + if stop then + next = getnext(stop) + start, stop = ligaturing(start,stop) + start, stop = kerning(start,stop) + else + prev = getprev(start) + start = ligaturing(start) + start = kerning(start) + end + if prev then + setlink(prev,start) + end + if next then + setlink(stop,next) + end + if front and nuthead ~= start then + head = tonode(nuthead) + end + end + end + end + stoptiming(nodes) + if trace_characters then + nodes.report(head,done) + end + return head, true +end + +local d_protect_glyphs = nuts.protect_glyphs +local d_unprotect_glyphs = nuts.unprotect_glyphs + +handlers.protectglyphs = function(n) return d_protect_glyphs (tonut(n)) end +handlers.unprotectglyphs = function(n) return d_unprotect_glyphs(tonut(n)) end + +-- function handlers.protectglyphs(h) +-- local h = tonut(h) +-- for n in traverse_id(disc_code,h) do +-- local pre, post, replace = getdisc(n) +-- if pre then d_protect_glyphs(pre) end +-- if post then d_protect_glyphs(post) end +-- if replace then d_protect_glyphs(replace) end +-- end +-- return d_protect_glyphs(h) +-- end -- cgit v1.2.3