summaryrefslogtreecommitdiff
path: root/tex/context/base/font-inj.lua
diff options
context:
space:
mode:
authorContext Git Mirror Bot <phg42.2a@gmail.com>2014-12-03 19:15:05 +0100
committerContext Git Mirror Bot <phg42.2a@gmail.com>2014-12-03 19:15:05 +0100
commit606f30879286f37c0cf0ef42bc225e835d0d596d (patch)
tree969577a4f86a2d1e9c085bda80812ebb45c074cb /tex/context/base/font-inj.lua
parent8fa06810ed0fdc14d3c45eefff9a41800a84150b (diff)
downloadcontext-606f30879286f37c0cf0ef42bc225e835d0d596d.tar.gz
2014-12-03 18:28:00
Diffstat (limited to 'tex/context/base/font-inj.lua')
-rw-r--r--tex/context/base/font-inj.lua1269
1 files changed, 1269 insertions, 0 deletions
diff --git a/tex/context/base/font-inj.lua b/tex/context/base/font-inj.lua
new file mode 100644
index 000000000..4687e1ea7
--- /dev/null
+++ b/tex/context/base/font-inj.lua
@@ -0,0 +1,1269 @@
+if not modules then modules = { } end modules ['font-inj'] = {
+ version = 1.001,
+ comment = "companion to font-lib.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files",
+}
+
+-- This property based variant is not faster but looks nicer than the attribute one. We
+-- need to use rawget (which is apbout 4 times slower than a direct access but we cannot
+-- get/set that one for our purpose!
+
+if not nodes.properties then return end
+
+local next, rawget = next, rawget
+local utfchar = utf.char
+
+local trace_injections = false trackers.register("fonts.injections", function(v) trace_injections = v end)
+
+local report_injections = logs.reporter("fonts","injections")
+
+report_injections("using experimental injector")
+
+local attributes, nodes, node = attributes, nodes, node
+
+fonts = fonts
+local fontdata = fonts.hashes.identifiers
+
+nodes.injections = nodes.injections or { }
+local injections = nodes.injections
+
+local nodecodes = nodes.nodecodes
+local glyph_code = nodecodes.glyph
+local disc_code = nodecodes.disc
+local kern_code = nodecodes.kern
+
+local nuts = nodes.nuts
+local nodepool = nuts.pool
+
+local newkern = nodepool.kern
+
+local tonode = nuts.tonode
+local tonut = nuts.tonut
+
+local getfield = nuts.getfield
+local setfield = nuts.setfield
+local getnext = nuts.getnext
+local getprev = nuts.getprev
+local getid = nuts.getid
+local getfont = nuts.getfont
+local getsubtype = nuts.getsubtype
+local getchar = nuts.getchar
+
+local traverse_id = nuts.traverse_id
+local traverse = nuts.traverse
+local insert_node_before = nuts.insert_before
+local insert_node_after = nuts.insert_after
+local find_tail = nuts.tail
+
+local properties = nodes.properties.data
+
+function injections.installnewkern(nk)
+ newkern = nk or newkern
+end
+
+local nofregisteredkerns = 0
+local nofregisteredpairs = 0
+local nofregisteredmarks = 0
+local nofregisteredcursives = 0
+----- markanchors = { } -- one base can have more marks
+
+function injections.reset(n)
+ local p = rawget(properties,start)
+ if p and p.injections then
+ -- todo: decrement counters? tricky as we then need to change the nof* to not increment
+ -- when we change a property
+ p.injections = nil -- should we keep the liga index?
+ end
+end
+
+function injections.setligaindex(n,index)
+ local p = rawget(properties,n)
+ if p then
+ p = p.injections
+ end
+ if p then
+ p.ligaindex = index
+ else
+ properties[n] = {
+ injections = {
+ ligaindex = index
+ }
+ }
+ end
+end
+
+function injections.getligaindex(n,default)
+ local p = rawget(properties,n)
+ if p then
+ p = p.injections
+ if p then
+ return p.ligaindex or default
+ end
+ end
+ return default
+end
+
+function injections.setcursive(start,nxt,factor,rlmode,exit,entry,tfmstart,tfmnext) -- hm: nuts or nodes
+ local dx = factor*(exit[1]-entry[1])
+ local dy = -factor*(exit[2]-entry[2])
+ local ws, wn = tfmstart.width, tfmnext.width
+ nofregisteredcursives = nofregisteredcursives + 1
+ if rlmode < 0 then
+ dx = -(dx + wn)
+ else
+ dx = dx - ws
+ end
+ --
+ local p = rawget(properties,start)
+ if p then
+ p = p.injections
+ end
+ if p then
+ p.cursiveanchor = true
+ else
+ properties[start] = {
+ injections = {
+ cursiveanchor = true,
+ },
+ }
+ end
+ local p = rawget(properties,nxt)
+ if p then
+ p = p.injections
+ end
+ if p then
+ p.cursivex = dx
+ p.cursivey = dy
+ else
+ properties[nxt] = {
+ injections = {
+ cursivex = dx,
+ cursivey = dy,
+ },
+ }
+ end
+ return dx, dy, nofregisteredcursives
+end
+
+function injections.setpair(current,factor,rlmode,r2lflag,spec,injection) -- r2lflag & tfmchr not used
+ local x, y, w, h = factor*spec[1], factor*spec[2], factor*spec[3], factor*spec[4]
+ if x ~= 0 or w ~= 0 or y ~= 0 or h ~= 0 then -- okay?
+ local yoffset = y - h
+ local leftkern = x
+ local rightkern = w - x
+ if leftkern ~= 0 or rightkern ~= 0 or yoffset ~= 0 then
+ nofregisteredpairs = nofregisteredpairs + 1
+ if rlmode and rlmode < 0 then
+ leftkern, rightkern = rightkern, leftkern
+ end
+ local p = rawget(properties,current)
+ if p then
+ p = p.injections
+ end
+ if p then
+ if leftkern ~= 0 or rightkern ~= 0 then
+ p.leftkern = (p.leftkern or 0) + leftkern
+ p.rightkern = (p.rightkern or 0) + rightkern
+ end
+ if yoffset ~= 0 then
+ p.yoffset = (p.yoffset or 0) + yoffset
+ end
+ elseif leftkern ~= 0 or rightkern ~= 0 then
+ properties[current] = {
+ injections = {
+ leftkern = leftkern,
+ rightkern = rightkern,
+ yoffset = yoffset,
+ },
+ }
+ else
+ properties[current] = {
+ injections = {
+ yoffset = yoffset,
+ },
+ }
+ end
+ return x, y, w, h, nofregisteredpairs
+ end
+ end
+ return x, y, w, h -- no bound
+end
+
+-- this needs checking for rl < 0 but it is unlikely that a r2l script
+-- uses kernclasses between glyphs so we're probably safe (KE has a
+-- problematic font where marks interfere with rl < 0 in the previous
+-- case)
+
+function injections.setkern(current,factor,rlmode,x,injection)
+ local dx = factor * x
+ if dx ~= 0 then
+ nofregisteredkerns = nofregisteredkerns + 1
+ local p = rawget(properties,current)
+ if not injection then
+ injection = "injections"
+ end
+ if p then
+ p = p[injection]
+ end
+ if p then
+ p.leftkern = dx + (p.leftkern or 0)
+ else
+ properties[current] = {
+ [injection] = {
+ leftkern = dx,
+ },
+ }
+ end
+ return dx, nofregisteredkerns
+ else
+ return 0, 0
+ end
+end
+
+function injections.setmark(start,base,factor,rlmode,ba,ma,tfmbase) -- ba=baseanchor, ma=markanchor
+ local dx, dy = factor*(ba[1]-ma[1]), factor*(ba[2]-ma[2])
+ nofregisteredmarks = nofregisteredmarks + 1
+ -- markanchors[nofregisteredmarks] = base
+ if rlmode >= 0 then
+ dx = tfmbase.width - dx -- see later commented ox
+ end
+ local p = rawget(properties,start)
+ if p then
+ p = p.injections
+ end
+ if p then
+ p.markx = dx
+ p.marky = dy
+ p.markdir = rlmode or 0
+ p.markbase = nofregisteredmarks
+ p.markbasenode = base
+ else
+ properties[start] = {
+ injections = {
+ markx = dx,
+ marky = dy,
+ markdir = rlmode or 0,
+ markbase = nofregisteredmarks,
+ markbasenode = base,
+ },
+ }
+ end
+ return dx, dy, nofregisteredmarks
+end
+
+local function dir(n)
+ return (n and n<0 and "r-to-l") or (n and n>0 and "l-to-r") or "unset"
+end
+
+local function trace(head)
+ report_injections("begin run: %s kerns, %s pairs, %s marks and %s cursives registered",
+ nofregisteredkerns,nofregisteredpairs,nofregisteredmarks,nofregisteredcursives)
+ for n in traverse_id(glyph_code,head) do
+ if getsubtype(n) < 256 then
+ local char = getchar(n)
+ report_injections("font %s, char %U, glyph %c",getfont(n),char,char)
+ local p = rawget(properties,n)
+ if p then
+ p = p.injections
+ end
+ if p then
+ local leftkern = p.leftkern or 0
+ local rightkern = p.rightkern or 0
+ local yoffset = p.yoffset or 0
+ local markx = p.markx or 0
+ local marky = p.marky or 0
+ local markdir = p.markdir or 0
+ local markbase = p.markbase or 0 -- will be markbasenode
+ local cursivex = p.cursivex or 0
+ local cursivey = p.cursivey or 0
+ local ligaindex = p.ligaindex or 0
+ if rightkern ~= 0 or yoffset ~= 0 then
+ report_injections(" pair: lx %p, rx %p, dy %p",leftkern,rightkern,yoffset)
+ elseif leftkern ~= 0 then
+ report_injections(" kern: dx %p",leftkern)
+ end
+ if markx ~= 0 or marky ~= 0 or markbase ~= 0 then
+ report_injections(" mark: dx %p, dy %p, dir %s, base %s",markx,marky,markdir,markbase ~= 0 and "yes" or "no")
+ end
+ if cursivex ~= 0 or cursivey ~= 0 then
+ report_injections(" curs: dx %p, dy %p",cursivex,cursivey)
+ end
+ if ligaindex ~= 0 then
+ report_injections(" liga: index %i",ligaindex)
+ end
+ end
+ end
+ end
+ report_injections("end run")
+end
+
+local function show_result(head)
+ local current = head
+ local skipping = false
+ while current do
+ local id = getid(current)
+ if id == glyph_code then
+ report_injections("char: %C, width %p, xoffset %p, yoffset %p",
+ getchar(current),getfield(current,"width"),getfield(current,"xoffset"),getfield(current,"yoffset"))
+ skipping = false
+ elseif id == kern_code then
+ report_injections("kern: %p",getfield(current,"kern"))
+ skipping = false
+ elseif not skipping then
+ report_injections()
+ skipping = true
+ end
+ current = getnext(current)
+ end
+end
+
+-- function injections.handler(head,where,keep) -- optimize for n=1
+-- if nofregisteredmarks > 0 or nofregisteredcursives > 0 then
+-- head = tonut(head)
+-- if trace_injections then
+-- trace(head)
+-- end
+-- -- in the future variant we will not copy items but refs to tables
+-- local glyphs, nofglyphs = { }, 0
+-- local marks, nofmarks = { }, 0
+-- -- maybe merge loops
+-- if nofregisteredpairs > 0 then
+-- local nf, tm = nil, nil
+-- for n in traverse_id(glyph_code,head) do -- only needed for relevant fonts
+-- if getsubtype(n) < 256 then
+-- local pn = rawget(properties,n)
+-- if pn then
+-- pn = pn.injections
+-- end
+-- local f = getfont(n)
+-- if f ~= nf then
+-- nf = f
+-- tm = fontdata[nf].resources.marks -- other hash in ctx
+-- end
+-- if tm and tm[getchar(n)] then
+-- nofmarks = nofmarks + 1
+-- marks[nofmarks] = n
+-- else
+-- nofglyphs = nofglyphs + 1
+-- glyphs[nofglyphs] = n
+-- end
+-- -- yoffsets can influence curs steps
+-- if pn then
+-- local yoffset = pn.yoffset
+-- if yoffset and yoffset ~= 0 then
+-- setfield(n,"yoffset",yoffset)
+-- end
+-- end
+-- end
+-- end
+-- else
+-- local nf, tm = nil, nil
+-- for n in traverse_id(glyph_code,head) do
+-- if getsubtype(n) < 256 then
+-- -- local pn = rawget(properties,n)
+-- -- if pn then
+-- -- pn = pn.injections
+-- -- end
+-- local f = getfont(n)
+-- if f ~= nf then
+-- nf = f
+-- tm = fontdata[nf].resources.marks -- other hash in ctx
+-- end
+-- if tm and tm[getchar(n)] then
+-- nofmarks = nofmarks + 1
+-- marks[nofmarks] = n
+-- else
+-- nofglyphs = nofglyphs + 1
+-- glyphs[nofglyphs] = n
+-- end
+-- end
+-- end
+-- end
+-- if nofglyphs > 0 then
+-- if nofregisteredcursives > 0 then
+-- local cursiveanchor, lastanchor = nil, nil
+-- local minc, maxc, last = 0, 0, nil
+-- for i=1,nofglyphs do
+-- local n = glyphs[i]
+-- local pn = rawget(properties,n)
+-- if pn then
+-- pn = pn.injections
+-- end
+-- if pn then
+-- local cursivex = pn.cursivex
+-- if cursivex then
+-- if cursiveanchor then
+-- if cursivex ~= 0 then
+-- pn.leftkern = (pn.leftkern or 0) + cursivex
+-- end
+-- if lastanchor then
+-- if maxc == 0 then
+-- minc = lastanchor
+-- end
+-- maxc = lastanchor
+-- properties[cursiveanchor].cursivedy = pn.cursivey
+-- end
+-- last = n
+-- else
+-- maxc = 0
+-- end
+-- elseif maxc > 0 then
+-- local ny = getfield(n,"yoffset")
+-- for i=maxc,minc,-1 do
+-- local ti = glyphs[i]
+-- ny = ny + properties[ti].cursivedy
+-- setfield(ti,"yoffset",ny) -- why not add ?
+-- end
+-- maxc = 0
+-- end
+-- if pn.cursiveanchor then
+-- cursiveanchor = n
+-- lastanchor = i
+-- else
+-- cursiveanchor = nil
+-- lastanchor = nil
+-- if maxc > 0 then
+-- local ny = getfield(n,"yoffset")
+-- for i=maxc,minc,-1 do
+-- local ti = glyphs[i]
+-- ny = ny + properties[ti].cursivedy
+-- setfield(ti,"yoffset",ny) -- why not add ?
+-- end
+-- maxc = 0
+-- end
+-- end
+-- elseif maxc > 0 then
+-- local ny = getfield(n,"yoffset")
+-- for i=maxc,minc,-1 do
+-- local ti = glyphs[i]
+-- ny = ny + properties[ti].cursivedy
+-- setfield(ti,"yoffset",getfield(ti,"yoffset") + ny) -- ?
+-- end
+-- maxc = 0
+-- cursiveanchor = nil
+-- lastanchor = nil
+-- end
+-- -- if maxc > 0 and not cursiveanchor then
+-- -- local ny = getfield(n,"yoffset")
+-- -- for i=maxc,minc,-1 do
+-- -- local ti = glyphs[i]
+-- -- ny = ny + properties[ti].cursivedy
+-- -- setfield(ti,"yoffset",ny) -- why not add ?
+-- -- end
+-- -- maxc = 0
+-- -- end
+-- end
+-- if last and maxc > 0 then
+-- local ny = getfield(last,"yoffset")
+-- for i=maxc,minc,-1 do
+-- local ti = glyphs[i]
+-- ny = ny + properties[ti].cursivedy
+-- setfield(ti,"yoffset",ny) -- why not add ?
+-- end
+-- end
+-- end
+-- if nofregisteredmarks > 0 then
+-- for i=1,nofmarks do
+-- local n = marks[i]
+-- local pn = rawget(properties,n)
+-- if pn then
+-- pn = pn.injections
+-- end
+-- if pn then
+-- -- local markbase = pn.markbase
+-- -- if markbase then
+-- -- local p = markanchors[markbase]
+-- local p = pn.markbasenode
+-- if p then
+-- local px = getfield(p,"xoffset")
+-- local ox = 0
+-- local pp = rawget(properties,p)
+-- local rightkern = pp and pp.rightkern
+-- if rightkern then -- x and w ~= 0
+-- if pn.markdir < 0 then
+-- -- kern(w-x) glyph(p) kern(x) mark(n)
+-- ox = px - pn.markx - rightkern
+-- -- report_injections("r2l case 1: %p",ox)
+-- else
+-- -- kern(x) glyph(p) kern(w-x) mark(n)
+-- -- ox = px - getfield(p,"width") + pn.markx - pp.leftkern
+-- ox = px - pn.markx - pp.leftkern
+-- -- report_injections("l2r case 1: %p",ox)
+-- end
+-- else
+-- -- we need to deal with fonts that have marks with width
+-- -- if pn.markdir < 0 then
+-- -- ox = px - pn.markx
+-- -- -- report_injections("r2l case 3: %p",ox)
+-- -- else
+-- -- -- ox = px - getfield(p,"width") + pn.markx
+-- ox = px - pn.markx
+-- -- report_injections("l2r case 3: %p",ox)
+-- -- end
+-- local wn = getfield(n,"width") -- in arial marks have widths
+-- if wn ~= 0 then
+-- -- bad: we should center
+-- -- insert_node_before(head,n,newkern(-wn/2))
+-- -- insert_node_after(head,n,newkern(-wn/2))
+-- pn.leftkern = -wn/2
+-- pn.rightkern = -wn/2
+-- -- wx[n] = { 0, -wn/2, 0, -wn }
+-- end
+-- -- so far
+-- end
+-- setfield(n,"xoffset",ox)
+-- --
+-- local py = getfield(p,"yoffset")
+-- local oy = 0
+-- if marks[p] then
+-- oy = py + pn.marky
+-- else
+-- oy = getfield(n,"yoffset") + py + pn.marky
+-- end
+-- setfield(n,"yoffset",oy)
+-- else
+-- -- normally this can't happen (only when in trace mode which is a special case anyway)
+-- -- report_injections("missing mark anchor %i",pn.markbase or 0)
+-- end
+-- -- end
+-- end
+-- end
+-- end
+-- for i=1,#glyphs do
+-- local n = glyphs[i]
+-- local pn = rawget(properties,n)
+-- if pn then
+-- pn = pn.injections
+-- end
+-- if pn then
+-- local leftkern = pn.leftkern
+-- if leftkern ~= 0 then
+-- insert_node_before(head,n,newkern(leftkern)) -- type 0/2
+-- end
+-- local rightkern = pn.rightkern
+-- if rightkern ~= 0 then
+-- insert_node_after(head,n,newkern(rightkern)) -- type 0/2
+-- end
+-- end
+-- end
+-- end
+-- if not keep then
+-- nofregisteredkerns = 0
+-- nofregisteredpairs = 0
+-- nofregisteredmarks = 0
+-- nofregisteredcursives = 0
+-- end
+-- return tonode(head), true
+-- elseif nofregisteredpairs > 0 then
+-- head = tonut(head)
+-- if trace_injections then
+-- trace(head)
+-- end
+-- for n in traverse_id(glyph_code,head) do
+-- if getsubtype(n) < 256 then
+-- local pn = rawget(properties,n)
+-- if pn then
+-- pn = pn.injections
+-- end
+-- if pn then
+-- local yoffset = pn.yoffset
+-- if yoffset and yoffset ~= 0 then
+-- setfield(n,"yoffset",yoffset)
+-- end
+-- local leftkern = pn.leftkern
+-- if leftkern and leftkern ~= 0 then
+-- insert_node_before(head,n,newkern(leftkern))
+-- end
+-- local rightkern = pn.rightkern
+-- if rightkern and rightkern ~= 0 then
+-- insert_node_after(head,n,newkern(rightkern))
+-- else
+-- -- simple (e.g. kernclass kerns)
+-- end
+-- end
+-- end
+-- end
+-- if not keep then
+-- nofregisteredpairs = 0
+-- nofregisteredkerns = 0
+-- end
+-- return tonode(head), true
+-- elseif nofregisteredkerns > 0 then
+-- head = tonut(head)
+-- if trace_injections then
+-- trace(head)
+-- end
+-- for n in traverse_id(glyph_code,head) do
+-- if getsubtype(n) < 256 then
+-- local pn = rawget(properties,n)
+-- if pn then
+-- pn = pn.injections
+-- end
+-- if pn then
+-- local leftkern = pn.leftkern
+-- if leftkern ~= 0 then
+-- insert_node_before(head,n,newkern(leftkern))
+-- end
+-- end
+-- end
+-- end
+-- if not keep then
+-- nofregisteredkerns = 0
+-- end
+-- return tonode(head), true
+-- else
+-- return head, false
+-- end
+-- end
+
+-------- todo: store pre/post/replace state, see todo
+
+-- in the future variant we will not copy items but refs to tables
+
+-- we could also check for marks here but maybe not all are registered (needs checking)
+
+local function collect_glyphs_1(head)
+ local glyphs, nofglyphs = { }, 0
+ local marks, nofmarks = { }, 0
+ local nf, tm = nil, nil
+ for n in traverse_id(glyph_code,head) do -- only needed for relevant fonts
+ if getsubtype(n) < 256 then
+ local pn = rawget(properties,n)
+ if pn then
+ pn = pn.injections
+ end
+ local f = getfont(n)
+ if f ~= nf then
+ nf = f
+ tm = fontdata[nf].resources.marks -- other hash in ctx
+ end
+ if tm and tm[getchar(n)] then
+ nofmarks = nofmarks + 1
+ marks[nofmarks] = n
+ else
+ nofglyphs = nofglyphs + 1
+ glyphs[nofglyphs] = n
+ end
+ -- yoffsets can influence curs steps
+ if pn then
+ local yoffset = pn.yoffset
+ if yoffset and yoffset ~= 0 then
+ setfield(n,"yoffset",yoffset)
+ end
+ end
+ end
+ end
+ return glyphs, nofglyphs, marks, nofmarks
+end
+
+-- local function run(n)
+-- local pn = rawget(properties,n)
+-- if pn then
+-- pn = pn.injections
+-- end
+-- local f = getfont(n)
+-- if f ~= nf then
+-- nf = f
+-- tm = fontdata[nf].resources.marks -- other hash in ctx
+-- end
+-- if tm and tm[getchar(n)] then -- also in disc?
+-- nofmarks = nofmarks + 1
+-- marks[nofmarks] = n
+-- else
+-- nofglyphs = nofglyphs + 1
+-- glyphs[nofglyphs] = n
+-- end
+-- -- yoffsets can influence curs steps
+-- if pn then
+-- local yoffset = pn.yoffset
+-- if yoffset and yoffset ~= 0 then
+-- setfield(n,"yoffset",yoffset)
+-- end
+-- end
+-- end
+-- local n = head
+-- while n do
+-- if getsubtype(n) < 256 then
+-- local id = getid(n)
+-- if id == glyph_code then
+-- run(n)
+-- elseif id == disc_code then
+-- local d = getfield(n,"pre") if d then for n in traverse(d) do run(d) end end
+-- local d = getfield(n,"post") if d then for n in traverse(d) do run(d) end end
+-- local d = getfield(n,"replace") if d then for n in traverse(d) do run(d) end end
+-- end
+-- end
+-- n = getnext(n)
+-- end
+
+local function collect_glyphs_2(head)
+ local glyphs, nofglyphs = { }, 0
+ local marks, nofmarks = { }, 0
+ local nf, tm = nil, nil
+ for n in traverse_id(glyph_code,head) do
+ if getsubtype(n) < 256 then
+ local f = getfont(n)
+ if f ~= nf then
+ nf = f
+ tm = fontdata[nf].resources.marks -- other hash in ctx
+ end
+ if tm and tm[getchar(n)] then
+ nofmarks = nofmarks + 1
+ marks[nofmarks] = n
+ else
+ nofglyphs = nofglyphs + 1
+ glyphs[nofglyphs] = n
+ end
+ end
+ end
+ return glyphs, nofglyphs, marks, nofmarks
+end
+
+-- local function run(n)
+-- local f = getfont(n)
+-- if f ~= nf then
+-- nf = f
+-- tm = fontdata[nf].resources.marks -- other hash in ctx
+-- end
+-- if tm and tm[getchar(n)] then
+-- nofmarks = nofmarks + 1
+-- marks[nofmarks] = n
+-- else
+-- nofglyphs = nofglyphs + 1
+-- glyphs[nofglyphs] = n
+-- end
+-- end
+-- --
+-- local n = head
+-- while n do
+-- if getsubtype(n) < 256 then
+-- local id = getid(n)
+-- if id == glyph_code then
+-- run(n)
+-- elseif id == disc_code then
+-- local d = getfield(n,"pre") if d then for n in traverse(d) do run(d) end end
+-- local d = getfield(n,"post") if d then for n in traverse(d) do run(d) end end
+-- local d = getfield(n,"replace") if d then for n in traverse(d) do run(d) end end
+-- end
+-- end
+-- n = getnext(n)
+-- end
+
+local function inject_marks(marks,nofmarks)
+ for i=1,nofmarks do
+ local n = marks[i]
+ local pn = rawget(properties,n)
+ if pn then
+ pn = pn.injections
+ end
+ if pn then
+ -- local markbase = pn.markbase
+ -- if markbase then
+ -- local p = markanchors[markbase]
+ local p = pn.markbasenode
+ if p then
+ local px = getfield(p,"xoffset")
+ local ox = 0
+ local pp = rawget(properties,p)
+ local rightkern = pp and pp.rightkern
+ if rightkern then -- x and w ~= 0
+ if pn.markdir < 0 then
+ -- kern(w-x) glyph(p) kern(x) mark(n)
+ ox = px - pn.markx - rightkern
+ -- report_injections("r2l case 1: %p",ox)
+ else
+ -- kern(x) glyph(p) kern(w-x) mark(n)
+ -- ox = px - getfield(p,"width") + pn.markx - pp.leftkern
+ ox = px - pn.markx - pp.leftkern
+ -- report_injections("l2r case 1: %p",ox)
+ end
+ else
+ -- we need to deal with fonts that have marks with width
+ -- if pn.markdir < 0 then
+ -- ox = px - pn.markx
+ -- -- report_injections("r2l case 3: %p",ox)
+ -- else
+ -- -- ox = px - getfield(p,"width") + pn.markx
+ ox = px - pn.markx
+ -- report_injections("l2r case 3: %p",ox)
+ -- end
+ local wn = getfield(n,"width") -- in arial marks have widths
+ if wn ~= 0 then
+ -- bad: we should center
+ -- insert_node_before(head,n,newkern(-wn/2))
+ -- insert_node_after(head,n,newkern(-wn/2))
+ pn.leftkern = -wn/2
+ pn.rightkern = -wn/2
+ -- wx[n] = { 0, -wn/2, 0, -wn }
+ end
+ -- so far
+ end
+ setfield(n,"xoffset",ox)
+ --
+ local py = getfield(p,"yoffset")
+ local oy = 0
+ if marks[p] then
+ oy = py + pn.marky
+ else
+ oy = getfield(n,"yoffset") + py + pn.marky
+ end
+ setfield(n,"yoffset",oy)
+ else
+ -- normally this can't happen (only when in trace mode which is a special case anyway)
+ -- report_injections("missing mark anchor %i",pn.markbase or 0)
+ end
+ -- end
+ end
+ end
+end
+
+local function inject_cursives(glyphs,nofglyphs)
+ local cursiveanchor, lastanchor = nil, nil
+ local minc, maxc, last = 0, 0, nil
+ for i=1,nofglyphs do
+ local n = glyphs[i]
+ local pn = rawget(properties,n)
+ if pn then
+ pn = pn.injections
+ end
+ if pn then
+ local cursivex = pn.cursivex
+ if cursivex then
+ if cursiveanchor then
+ if cursivex ~= 0 then
+ pn.leftkern = (pn.leftkern or 0) + cursivex
+ end
+ if lastanchor then
+ if maxc == 0 then
+ minc = lastanchor
+ end
+ maxc = lastanchor
+ properties[cursiveanchor].cursivedy = pn.cursivey
+ end
+ last = n
+ else
+ maxc = 0
+ end
+ elseif maxc > 0 then
+ local ny = getfield(n,"yoffset")
+ for i=maxc,minc,-1 do
+ local ti = glyphs[i]
+ ny = ny + properties[ti].cursivedy
+ setfield(ti,"yoffset",ny) -- why not add ?
+ end
+ maxc = 0
+ end
+ if pn.cursiveanchor then
+ cursiveanchor = n
+ lastanchor = i
+ else
+ cursiveanchor = nil
+ lastanchor = nil
+ if maxc > 0 then
+ local ny = getfield(n,"yoffset")
+ for i=maxc,minc,-1 do
+ local ti = glyphs[i]
+ ny = ny + properties[ti].cursivedy
+ setfield(ti,"yoffset",ny) -- why not add ?
+ end
+ maxc = 0
+ end
+ end
+ elseif maxc > 0 then
+ local ny = getfield(n,"yoffset")
+ for i=maxc,minc,-1 do
+ local ti = glyphs[i]
+ ny = ny + properties[ti].cursivedy
+ setfield(ti,"yoffset",getfield(ti,"yoffset") + ny) -- ?
+ end
+ maxc = 0
+ cursiveanchor = nil
+ lastanchor = nil
+ end
+ -- if maxc > 0 and not cursiveanchor then
+ -- local ny = getfield(n,"yoffset")
+ -- for i=maxc,minc,-1 do
+ -- local ti = glyphs[i]
+ -- ny = ny + properties[ti].cursivedy
+ -- setfield(ti,"yoffset",ny) -- why not add ?
+ -- end
+ -- maxc = 0
+ -- end
+ end
+ if last and maxc > 0 then
+ local ny = getfield(last,"yoffset")
+ for i=maxc,minc,-1 do
+ local ti = glyphs[i]
+ ny = ny + properties[ti].cursivedy
+ setfield(ti,"yoffset",ny) -- why not add ?
+ end
+ end
+end
+
+local function inject_kerns(glyphs,nofglyphs)
+ -- todo: pre/post/replace
+ for i=1,#glyphs do
+ local n = glyphs[i]
+ local pn = rawget(properties,n)
+ if pn then
+ pn = pn.injections
+ end
+ if pn then
+ local leftkern = pn.leftkern
+ if leftkern ~= 0 then
+ insert_node_before(head,n,newkern(leftkern)) -- type 0/2
+ end
+ local rightkern = pn.rightkern
+ if rightkern ~= 0 then
+ insert_node_after(head,n,newkern(rightkern)) -- type 0/2
+ end
+ end
+ end
+end
+
+local function inject_everything(head,where,keep)
+ head = tonut(head)
+ if trace_injections then
+ trace(head)
+ end
+ local glyphs, nofglyphs, marks, nofmarks
+ if nofregisteredpairs > 0 then
+ glyphs, nofglyphs, marks, nofmarks = collect_glyphs_1(head)
+ else
+ glyphs, nofglyphs, marks, nofmarks = collect_glyphs_2(head)
+ end
+ if nofglyphs > 0 then
+ if nofregisteredcursives > 0 then
+ inject_cursives(glyphs,nofglyphs)
+ end
+ if nofregisteredmarks > 0 then
+ inject_marks(marks,nofmarks)
+ end
+ inject_kerns(glyphs,nofglyphs)
+ end
+ if not keep then
+ nofregisteredkerns = 0
+ nofregisteredpairs = 0
+ nofregisteredmarks = 0
+ nofregisteredcursives = 0
+ end
+ return tonode(head), true
+end
+
+local function inject_kerns_only(head,where,keep)
+ head = tonut(head)
+ if trace_injections then
+ trace(head)
+ end
+ local n = head
+ local p = nil
+ while n do
+ local id = getid(n)
+ if id == glyph_code then
+ local pn = rawget(properties,n)
+ if pn then
+ if p then
+ local d = getfield(p,"post")
+ if d then
+ local pn = pn.postinjections
+ if pn then
+ local leftkern = pn.leftkern
+ if leftkern ~= 0 then
+ local t = find_tail(d)
+ insert_node_after(d,t,newkern(leftkern))
+ end
+ end
+ end
+ local d = getfield(p,"replace")
+ if d then
+ local pn = pn.replaceinjections
+ if pn then
+ local leftkern = pn.leftkern
+ if leftkern ~= 0 then
+ local t = find_tail(d)
+ insert_node_after(d,t,newkern(leftkern))
+ end
+ end
+ else
+ local pn = pn.injections
+ if pn then
+ local leftkern = pn.leftkern
+ if leftkern ~= 0 then
+ setfield(p,"replace",newkern(leftkern))
+ end
+ end
+ end
+ else
+ local pn = pn.injections
+ if pn then
+ local leftkern = pn.leftkern
+ if leftkern ~= 0 then
+ head = insert_node_before(head,n,newkern(leftkern))
+ end
+ end
+ end
+ end
+ p = nil
+ elseif id == disc_code then
+ local d = getfield(n,"pre")
+ if d then
+ local h = d
+ for n in traverse(d) do
+ local pn = rawget(properties,n)
+ if pn then
+ pn = pn.preinjections
+ end
+ if pn then
+ local leftkern = pn.leftkern
+ if leftkern ~= 0 then
+ h = insert_node_before(h,n,newkern(leftkern))
+ end
+ end
+ end
+ if h ~= d then
+ setfield(n,"pre",h)
+ end
+ end
+ local d = getfield(n,"post")
+ if d then
+ local h = d
+ for n in traverse(d) do
+ local pn = rawget(properties,n)
+ if pn then
+ pn = pn.postinjections
+ end
+ if pn then
+ local leftkern = pn.leftkern
+ if leftkern ~= 0 then
+ h = insert_node_before(h,n,newkern(leftkern))
+ end
+ end
+ end
+ if h ~= d then
+ setfield(n,"post",h)
+ end
+ end
+ local d = getfield(n,"replace")
+ if d then
+ local h = d
+ for n in traverse(d) do
+ local pn = rawget(properties,n)
+ if pn then
+ pn = pn.replaceinjections
+ end
+ if pn then
+ local leftkern = pn.leftkern
+ if leftkern ~= 0 then
+ h = insert_node_before(h,n,newkern(leftkern))
+ end
+ end
+ end
+ if h ~= d then
+ setfield(n,"replace",h)
+ end
+ end
+ p = n
+ else
+ p = nil
+ end
+ n = getnext(n)
+ end
+ --
+ if not keep then
+ nofregisteredkerns = 0
+ end
+ return tonode(head), true
+end
+
+local function inject_pairs_only(head,where,keep)
+ head = tonut(head)
+ if trace_injections then
+ trace(head)
+ end
+ --
+ local n = head
+ local p = nil
+ while n do
+ local id = getid(n)
+ if id == glyph_code then
+ local pn = rawget(properties,n)
+ if pn then
+ if p then
+ local d = getfield(p,"post")
+ if d then
+ local pn = pn.postinjections
+ if pn then
+ local leftkern = pn.leftkern
+ if leftkern ~= 0 then
+ local t = find_tail(d)
+ insert_node_after(d,t,newkern(leftkern))
+ end
+ -- local rightkern = pn.rightkern
+ -- if rightkern and rightkern ~= 0 then
+ -- insert_node_after(head,n,newkern(rightkern))
+ -- n = getnext(n) -- to be checked
+ -- end
+ end
+ end
+ local d = getfield(p,"replace")
+ if d then
+ local pn = pn.replaceinjections
+ if pn then
+ local leftkern = pn.leftkern
+ if leftkern ~= 0 then
+ local t = find_tail(d)
+ insert_node_after(d,t,newkern(leftkern))
+ end
+ -- local rightkern = pn.rightkern
+ -- if rightkern and rightkern ~= 0 then
+ -- insert_node_after(head,n,newkern(rightkern))
+ -- n = getnext(n) -- to be checked
+ -- end
+ end
+ else
+ local pn = pn.injections
+ if pn then
+ local leftkern = pn.leftkern
+ if leftkern ~= 0 then
+ setfield(p,"replace",newkern(leftkern))
+ end
+ -- local rightkern = pn.rightkern
+ -- if rightkern and rightkern ~= 0 then
+ -- insert_node_after(head,n,newkern(rightkern))
+ -- n = getnext(n) -- to be checked
+ -- end
+ end
+ end
+ else
+ -- this is the most common case
+ local pn = pn.injections
+ if pn then
+ local yoffset = pn.yoffset
+ if yoffset and yoffset ~= 0 then
+ setfield(n,"yoffset",yoffset)
+ end
+ local leftkern = pn.leftkern
+ if leftkern ~= 0 then
+ insert_node_before(head,n,newkern(leftkern))
+ end
+ local rightkern = pn.rightkern
+ if rightkern and rightkern ~= 0 then
+ insert_node_after(head,n,newkern(rightkern))
+ n = getnext(n) -- to be checked
+ end
+ end
+ end
+ end
+ p = nil
+ elseif id == disc_code then
+ local d = getfield(n,"pre")
+ if d then
+ local h = d
+ for n in traverse(d) do
+ local pn = rawget(properties,n)
+ if pn then
+ pn = pn.preinjections
+ end
+ if pn then
+ local yoffset = pn.yoffset
+ if yoffset and yoffset ~= 0 then
+ setfield(n,"yoffset",yoffset)
+ end
+ local leftkern = pn.leftkern
+ if leftkern ~= 0 then
+ h = insert_node_before(h,n,newkern(leftkern))
+ end
+ local rightkern = pn.rightkern
+ if rightkern and rightkern ~= 0 then
+ insert_node_after(head,n,newkern(rightkern))
+ n = getnext(n) -- to be checked
+ end
+ end
+ end
+ if h ~= d then
+ setfield(n,"pre",h)
+ end
+ end
+ local d = getfield(n,"post")
+ if d then
+ local h = d
+ for n in traverse(d) do
+ local pn = rawget(properties,n)
+ if pn then
+ pn = pn.postinjections
+ end
+ if pn then
+ local yoffset = pn.yoffset
+ if yoffset and yoffset ~= 0 then
+ setfield(n,"yoffset",yoffset)
+ end
+ local leftkern = pn.leftkern
+ if leftkern ~= 0 then
+ h = insert_node_before(h,n,newkern(leftkern))
+ end
+ local rightkern = pn.rightkern
+ if rightkern and rightkern ~= 0 then
+ insert_node_after(head,n,newkern(rightkern))
+ n = getnext(n) -- to be checked
+ end
+ end
+ end
+ if h ~= d then
+ setfield(n,"post",h)
+ end
+ end
+ local d = getfield(n,"replace")
+ if d then
+ local h = d
+ for n in traverse(d) do
+ local pn = rawget(properties,n)
+ if pn then
+ pn = pn.replaceinjections
+ end
+ if pn then
+ local yoffset = pn.yoffset
+ if yoffset and yoffset ~= 0 then
+ setfield(n,"yoffset",yoffset)
+ end
+ local leftkern = pn.leftkern
+ if leftkern ~= 0 then
+ h = insert_node_before(h,n,newkern(leftkern))
+ end
+ local rightkern = pn.rightkern
+ if rightkern and rightkern ~= 0 then
+ insert_node_after(head,n,newkern(rightkern))
+ n = getnext(n) -- to be checked
+ end
+ end
+ end
+ if h ~= d then
+ setfield(n,"replace",h)
+ end
+ end
+ p = n
+ else
+ p = nil
+ end
+ n = getnext(n)
+ end
+ --
+ if not keep then
+ nofregisteredpairs = 0
+ nofregisteredkerns = 0
+ end
+ return tonode(head), true
+end
+
+function injections.handler(head,where,keep) -- optimize for n=1 ?
+ if nofregisteredmarks > 0 or nofregisteredcursives > 0 then
+ return inject_everything(head,where,keep)
+ elseif nofregisteredpairs > 0 then
+ return inject_pairs_only(head,where,keep)
+ elseif nofregisteredkerns > 0 then
+ return inject_kerns_only(head,where,keep)
+ else
+ return head, false
+ end
+end