Current File : //usr/share/texlive/texmf-dist/tex/luatex/luaotfload/fontloader-font-otj.lua |
if not modules then modules = { } end modules ['font-otj'] = {
version = 1.001,
optimize = true,
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 about 4 times slower than a direct access but we cannot
-- get/set that one for our purpose! This version does a bit more with discretionaries
-- (and Kai has tested it with his collection of weird fonts.)
-- There is some duplicate code here (especially in the the pre/post/replace branches) but
-- we go for speed. We could store a list of glyph and mark nodes when registering but it's
-- cleaner to have an identification pass here. Also, I need to keep tracing in mind so
-- being too clever here is dangerous.
-- As we have a rawget on properties we don't need one on injections.
-- The use_advance code was just a test and is meant for testing and manuals. There is no
-- performance (or whatever) gain and using kerns is somewhat cleaner (at least for now).
-- An alternative is to have a list per base of all marks and then do a run over the node
-- list that resolves the accumulated l/r/x/y and then do an inject pass.
-- if needed we can flag a kern node as immutable
-- The thing with these positioning options is that it is not clear what Uniscribe does with
-- the 2rl flag and we keep oscillating a between experiments.
if not nodes.properties then return end
local next, rawget, tonumber = next, rawget, tonumber
local fastcopy = table.fastcopy
local registertracker = trackers.register
local registerdirective = directives.register
local trace_injections = false registertracker("fonts.injections", function(v) trace_injections = v end)
local trace_marks = false registertracker("fonts.injections.marks", function(v) trace_marks = v end)
local trace_cursive = false registertracker("fonts.injections.cursive", function(v) trace_cursive = v end)
local trace_spaces = false registertracker("fonts.injections.spaces", function(v) trace_spaces = v end)
-- local fix_cursive_marks = false
--
-- registerdirective("fonts.injections.fixcursivemarks", function(v)
-- fix_cursive_marks = v
-- end)
local report_injections = logs.reporter("fonts","injections")
local report_spaces = logs.reporter("fonts","spaces")
local attributes, nodes, node = attributes, nodes, node
fonts = fonts
local hashes = fonts.hashes
local fontdata = hashes.identifiers
local fontmarks = hashes.marks
----- parameters = fonts.hashes.parameters -- not in generic
----- resources = fonts.hashes.resources -- not in generic
nodes.injections = nodes.injections or { }
local injections = nodes.injections
local tracers = nodes.tracers
local setcolor = tracers and tracers.colors.set
local resetcolor = tracers and tracers.colors.reset
local nodecodes = nodes.nodecodes
local glyph_code = nodecodes.glyph
local disc_code = nodecodes.disc
local kern_code = nodecodes.kern
local glue_code = nodecodes.glue
local nuts = nodes.nuts
local nodepool = nuts.pool
local tonode = nuts.tonode
local tonut = nuts.tonut
local setfield = nuts.setfield
local getnext = nuts.getnext
local getprev = nuts.getprev
local getid = nuts.getid
local getfont = nuts.getfont
local getchar = nuts.getchar
local getoffsets = nuts.getoffsets
local getboth = nuts.getboth
local getdisc = nuts.getdisc
local setdisc = nuts.setdisc
local setoffsets = nuts.setoffsets
local ischar = nuts.ischar
local getkern = nuts.getkern
local setkern = nuts.setkern
local setlink = nuts.setlink
local setwidth = nuts.setwidth
local getwidth = nuts.getwidth
----- traverse_id = nuts.traverse_id
----- traverse_char = nuts.traverse_char
local nextchar = nuts.traversers.char
local nextglue = nuts.traversers.glue
local insert_node_before = nuts.insert_before
local insert_node_after = nuts.insert_after
local properties = nodes.properties.data
local fontkern = nuts.pool and nuts.pool.fontkern -- context
local italickern = nuts.pool and nuts.pool.italickern -- context
local useitalickerns = false -- context only
directives.register("fonts.injections.useitalics", function(v)
if v then
report_injections("using italics for space kerns (tracing only)")
end
useitalickerns = v
end)
if not fontkern then -- generic
local thekern = nuts.new("kern",0) -- fontkern
local setkern = nuts.setkern
local copy_node = nuts.copy_node
fontkern = function(k)
local n = copy_node(thekern)
setkern(n,k)
return n
end
end
if not italickern then -- generic
local thekern = nuts.new("kern",3) -- italiccorrection
local setkern = nuts.setkern
local copy_node = nuts.copy_node
italickern = function(k)
local n = copy_node(thekern)
setkern(n,k)
return n
end
end
function injections.installnewkern() end -- obsolete
local nofregisteredkerns = 0
local nofregisteredpositions = 0
local nofregisteredmarks = 0
local nofregisteredcursives = 0
local keepregisteredcounts = false
function injections.keepcounts()
keepregisteredcounts = true
end
function injections.resetcounts()
nofregisteredkerns = 0
nofregisteredpositions = 0
nofregisteredmarks = 0
nofregisteredcursives = 0
keepregisteredcounts = false
end
-- We need to make sure that a possible metatable will not kick in unexpectedly.
function injections.reset(n)
local p = rawget(properties,n)
if p then
p.injections = false -- { } -- nil should work too as we use rawget
else
properties[n] = false -- { injections = { } } -- nil should work too as we use rawget
end
end
function injections.copy(target,source)
local sp = rawget(properties,source)
if sp then
local tp = rawget(properties,target)
local si = sp.injections
if si then
si = fastcopy(si)
if tp then
tp.injections = si
else
properties[target] = {
injections = si,
}
end
elseif tp then
tp.injections = false -- { }
else
properties[target] = { injections = { } }
end
else
local tp = rawget(properties,target)
if tp then
tp.injections = false -- { }
else
properties[target] = false -- { injections = { } }
end
end
end
function injections.setligaindex(n,index) -- todo: don't set when 0
local p = rawget(properties,n)
if p then
local i = p.injections
if i then
i.ligaindex = index
else
p.injections = {
ligaindex = index
}
end
else
properties[n] = {
injections = {
ligaindex = index
}
}
end
end
function injections.getligaindex(n,default)
local p = rawget(properties,n)
if p then
local i = p.injections
if i then
return i.ligaindex or default
end
end
return default
end
function injections.setcursive(start,nxt,factor,rlmode,exit,entry,tfmstart,tfmnext,r2lflag)
-- The standard says something about the r2lflag related to the first in a series:
--
-- When this bit is set, the last glyph in a given sequence to which the cursive
-- attachment lookup is applied, will be positioned on the baseline.
--
-- But it looks like we don't need to consider it.
local dx = factor*(exit[1]-entry[1])
local dy = -factor*(exit[2]-entry[2])
local ws = tfmstart.width
local wn = tfmnext.width
nofregisteredcursives = nofregisteredcursives + 1
if rlmode < 0 then
dx = -(dx + wn)
else
dx = dx - ws
end
if dx == 0 then
-- get rid of funny -0
dx = 0
end
--
local p = rawget(properties,start)
if p then
local i = p.injections
if i then
i.cursiveanchor = true
else
p.injections = {
cursiveanchor = true,
}
end
else
properties[start] = {
injections = {
cursiveanchor = true,
},
}
end
local p = rawget(properties,nxt)
if p then
local i = p.injections
if i then
i.cursivex = dx
i.cursivey = dy
else
p.injections = {
cursivex = dx,
cursivey = dy,
}
end
else
properties[nxt] = {
injections = {
cursivex = dx,
cursivey = dy,
},
}
end
return dx, dy, nofregisteredcursives
end
-- kind: 0=single 1=first of pair, 2=second of pair
function injections.setposition(kind,current,factor,rlmode,spec,injection)
local x = factor * (spec[1] or 0)
local y = factor * (spec[2] or 0)
local w = factor * (spec[3] or 0)
local h = factor * (spec[4] or 0)
if x ~= 0 or w ~= 0 or y ~= 0 or h ~= 0 then -- okay?
local yoffset = y - h
local leftkern = x -- both kerns are set in a pair kern compared
local rightkern = w - x -- to normal kerns where we set only leftkern
if leftkern ~= 0 or rightkern ~= 0 or yoffset ~= 0 then
nofregisteredpositions = nofregisteredpositions + 1
if rlmode and rlmode < 0 then
leftkern, rightkern = rightkern, leftkern
end
if not injection then
injection = "injections"
end
local p = rawget(properties,current)
if p then
local i = p[injection]
if i then
if leftkern ~= 0 then
i.leftkern = (i.leftkern or 0) + leftkern
end
if rightkern ~= 0 then
i.rightkern = (i.rightkern or 0) + rightkern
end
if yoffset ~= 0 then
i.yoffset = (i.yoffset or 0) + yoffset
end
elseif leftkern ~= 0 or rightkern ~= 0 then
p[injection] = {
leftkern = leftkern,
rightkern = rightkern,
yoffset = yoffset,
}
else
p[injection] = {
yoffset = yoffset,
}
end
elseif leftkern ~= 0 or rightkern ~= 0 then
properties[current] = {
[injection] = {
leftkern = leftkern,
rightkern = rightkern,
yoffset = yoffset,
},
}
else
properties[current] = {
[injection] = {
yoffset = yoffset,
},
}
end
return x, y, w, h, nofregisteredpositions
end
end
return x, y, w, h -- no bound
end
-- The next one is used for simple kerns coming from a truetype kern table. The r2l
-- variant variant needs checking but it is unlikely that a r2l script uses thsi
-- feature.
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
local i = p[injection]
if i then
i.leftkern = dx + (i.leftkern or 0)
else
p[injection] = {
leftkern = dx,
}
end
else
properties[current] = {
[injection] = {
leftkern = dx,
},
}
end
return dx, nofregisteredkerns
else
return 0, 0
end
end
-- This one is an optimization of pairs where we have only a "w" entry. This one is
-- potentially different from the previous one wrt r2l. It needs checking. The
-- optimization relates to smaller tma files.
function injections.setmove(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 rlmode and rlmode < 0 then
-- we need to swap with a single so then we also need to to it here
-- as move is just a simple single
if p then
local i = p[injection]
if i then
i.rightkern = dx + (i.rightkern or 0)
else
p[injection] = {
rightkern = dx,
}
end
else
properties[current] = {
[injection] = {
rightkern = dx,
},
}
end
else
if p then
local i = p[injection]
if i then
i.leftkern = dx + (i.leftkern or 0)
else
p[injection] = {
leftkern = dx,
}
end
else
properties[current] = {
[injection] = {
leftkern = dx,
},
}
end
end
return dx, nofregisteredkerns
else
return 0, 0
end
end
function injections.setmark(start,base,factor,rlmode,ba,ma,tfmbase,mkmk,checkmark) -- ba=baseanchor, ma=markanchor
local dx = factor*(ba[1]-ma[1])
local dy = factor*(ba[2]-ma[2])
nofregisteredmarks = nofregisteredmarks + 1
if rlmode >= 0 then
dx = tfmbase.width - dx -- see later commented ox
end
local p = rawget(properties,start)
-- hm, dejavu serif does a sloppy mark2mark before mark2base
if p then
local i = p.injections
if i then
if i.markmark then
-- out of order mkmk: yes or no or option
else
-- if dx ~= 0 then
-- i.markx = dx
-- end
-- if y ~= 0 then
-- i.marky = dy
-- end
-- if rlmode then
-- i.markdir = rlmode
-- end
i.markx = dx
i.marky = dy
i.markdir = rlmode or 0
i.markbase = nofregisteredmarks
i.markbasenode = base
i.markmark = mkmk
i.checkmark = checkmark
end
else
p.injections = {
markx = dx,
marky = dy,
markdir = rlmode or 0,
markbase = nofregisteredmarks,
markbasenode = base,
markmark = mkmk,
checkmark = checkmark,
}
end
else
properties[start] = {
injections = {
markx = dx,
marky = dy,
markdir = rlmode or 0,
markbase = nofregisteredmarks,
markbasenode = base,
markmark = mkmk,
checkmark = checkmark,
},
}
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 showchar(n,nested)
local char = getchar(n)
report_injections("%wfont %s, char %U, glyph %c",nested and 2 or 0,getfont(n),char,char)
end
local function show(n,what,nested,symbol)
if n then
local p = rawget(properties,n)
if p then
local i = p[what]
if i then
local leftkern = i.leftkern or 0
local rightkern = i.rightkern or 0
local yoffset = i.yoffset or 0
local markx = i.markx or 0
local marky = i.marky or 0
local markdir = i.markdir or 0
local markbase = i.markbase or 0
local cursivex = i.cursivex or 0
local cursivey = i.cursivey or 0
local ligaindex = i.ligaindex or 0
local cursbase = i.cursiveanchor
local margin = nested and 4 or 2
--
if rightkern ~= 0 or yoffset ~= 0 then
report_injections("%w%s pair: lx %p, rx %p, dy %p",margin,symbol,leftkern,rightkern,yoffset)
elseif leftkern ~= 0 then
report_injections("%w%s kern: dx %p",margin,symbol,leftkern)
end
if markx ~= 0 or marky ~= 0 or markbase ~= 0 then
report_injections("%w%s mark: dx %p, dy %p, dir %s, base %s",margin,symbol,markx,marky,markdir,markbase ~= 0 and "yes" or "no")
end
if cursivex ~= 0 or cursivey ~= 0 then
if cursbase then
report_injections("%w%s curs: base dx %p, dy %p",margin,symbol,cursivex,cursivey)
else
report_injections("%w%s curs: dx %p, dy %p",margin,symbol,cursivex,cursivey)
end
elseif cursbase then
report_injections("%w%s curs: base",margin,symbol)
end
if ligaindex ~= 0 then
report_injections("%w%s liga: index %i",margin,symbol,ligaindex)
end
end
end
end
end
local function showsub(n,what,where)
report_injections("begin subrun: %s",where)
for n in nextchar, n do
showchar(n,where)
show(n,what,where," ")
end
report_injections("end subrun")
end
local function trace(head,where)
report_injections()
report_injections("begin run %s: %s kerns, %s positions, %s marks and %s cursives registered",
where or "",nofregisteredkerns,nofregisteredpositions,nofregisteredmarks,nofregisteredcursives)
local n = head
while n do
local id = getid(n)
if id == glyph_code then
showchar(n)
show(n,"injections",false," ")
show(n,"preinjections",false,"<")
show(n,"postinjections",false,">")
show(n,"replaceinjections",false,"=")
show(n,"emptyinjections",false,"*")
elseif id == disc_code then
local pre, post, replace = getdisc(n)
if pre then
showsub(pre,"preinjections","pre")
end
if post then
showsub(post,"postinjections","post")
end
if replace then
showsub(replace,"replaceinjections","replace")
end
show(n,"emptyinjections",false,"*")
end
n = getnext(n)
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
local w = getwidth(current)
local x, y = getoffsets(current)
report_injections("char: %C, width %p, xoffset %p, yoffset %p",getchar(current),w,x,y)
skipping = false
elseif id == kern_code then
report_injections("kern: %p",getkern(current))
skipping = false
elseif not skipping then
report_injections()
skipping = true
end
current = getnext(current)
end
report_injections()
end
-- G +D-pre G
-- D-post+
-- +D-replace+
--
-- G +D-pre +D-pre
-- D-post +D-post
-- +D-replace +D-replace
local function inject_kerns_only(head,where)
if trace_injections then
trace(head,"kerns")
end
local current = head
local prev = nil
local next = nil
local prevdisc = nil
-- local prevglyph = nil
local pre = nil -- saves a lookup
local post = nil -- saves a lookup
local replace = nil -- saves a lookup
local pretail = nil -- saves a lookup
local posttail = nil -- saves a lookup
local replacetail = nil -- saves a lookup
while current do
local next = getnext(current)
local char, id = ischar(current)
if char then
local p = rawget(properties,current)
if p then
local i = p.injections
if i then
-- left|glyph|right
local leftkern = i.leftkern
if leftkern and leftkern ~= 0 then
if prev and getid(prev) == glue_code then
if useitalickerns then
head = insert_node_before(head,current,italickern(leftkern))
else
setwidth(prev, getwidth(prev) + leftkern)
end
else
head = insert_node_before(head,current,fontkern(leftkern))
end
end
end
if prevdisc then
local done = false
if post then
local i = p.postinjections
if i then
local leftkern = i.leftkern
if leftkern and leftkern ~= 0 then
setlink(posttail,fontkern(leftkern))
done = true
end
end
end
if replace then
local i = p.replaceinjections
if i then
local leftkern = i.leftkern
if leftkern and leftkern ~= 0 then
setlink(replacetail,fontkern(leftkern))
done = true
end
end
else
local i = p.emptyinjections
if i then
-- glyph|disc|glyph (special case)
local leftkern = i.leftkern
if leftkern and leftkern ~= 0 then
replace = fontkern(leftkern)
done = true
end
end
end
if done then
setdisc(prevdisc,pre,post,replace)
end
end
end
prevdisc = nil
-- prevglyph = current
elseif char == false then
-- other font
prevdisc = nil
-- prevglyph = current
elseif id == disc_code then
pre, post, replace, pretail, posttail, replacetail = getdisc(current,true)
local done = false
if pre then
-- left|pre glyphs|right
for n in nextchar, pre do
local p = rawget(properties,n)
if p then
local i = p.injections or p.preinjections
if i then
local leftkern = i.leftkern
if leftkern and leftkern ~= 0 then
pre = insert_node_before(pre,n,fontkern(leftkern))
done = true
end
end
end
end
end
if post then
-- left|post glyphs|right
for n in nextchar, post do
local p = rawget(properties,n)
if p then
local i = p.injections or p.postinjections
if i then
local leftkern = i.leftkern
if leftkern and leftkern ~= 0 then
post = insert_node_before(post,n,fontkern(leftkern))
done = true
end
end
end
end
end
if replace then
-- left|replace glyphs|right
for n in nextchar, replace do
local p = rawget(properties,n)
if p then
local i = p.injections or p.replaceinjections
if i then
local leftkern = i.leftkern
if leftkern and leftkern ~= 0 then
replace = insert_node_before(replace,n,fontkern(leftkern))
done = true
end
end
end
end
end
if done then
setdisc(current,pre,post,replace)
end
-- prevglyph = nil
prevdisc = current
else
-- prevglyph = nil
prevdisc = nil
end
prev = current
current = next
end
--
if keepregisteredcounts then
keepregisteredcounts = false
else
nofregisteredkerns = 0
end
if trace_injections then
show_result(head)
end
return head
end
local function inject_positions_only(head,where)
if trace_injections then
trace(head,"positions")
end
local current = head
local prev = nil
local next = nil
local prevdisc = nil
local prevglyph = nil
local pre = nil -- saves a lookup
local post = nil -- saves a lookup
local replace = nil -- saves a lookup
local pretail = nil -- saves a lookup
local posttail = nil -- saves a lookup
local replacetail = nil -- saves a lookup
while current do
local next = getnext(current)
local char, id = ischar(current)
if char then
local p = rawget(properties,current)
if p then
local i = p.injections
if i then
-- left|glyph|right
local yoffset = i.yoffset
if yoffset and yoffset ~= 0 then
setoffsets(current,false,yoffset)
end
local leftkern = i.leftkern
local rightkern = i.rightkern
if leftkern and leftkern ~= 0 then
if rightkern and leftkern == -rightkern then
setoffsets(current,leftkern,false)
rightkern = 0
elseif prev and getid(prev) == glue_code then
if useitalickerns then
head = insert_node_before(head,current,italickern(leftkern))
else
setwidth(prev, getwidth(prev) + leftkern)
end
else
head = insert_node_before(head,current,fontkern(leftkern))
end
end
if rightkern and rightkern ~= 0 then
if next and getid(next) == glue_code then
if useitalickerns then
insert_node_after(head,current,italickern(rightkern))
else
setwidth(next, getwidth(next) + rightkern)
end
else
insert_node_after(head,current,fontkern(rightkern))
end
end
else
local i = p.emptyinjections
if i then
-- glyph|disc|glyph (special case)
local rightkern = i.rightkern
if rightkern and rightkern ~= 0 then
if next and getid(next) == disc_code then
if replace then
-- error, we expect an empty one
else
-- KE setfield(next,"replace",fontkern(rightkern)) -- maybe also leftkern
replace = fontkern(rightkern) -- maybe also leftkern
done = true --KE
end
end
end
end
end
if prevdisc then
local done = false
if post then
local i = p.postinjections
if i then
local leftkern = i.leftkern
if leftkern and leftkern ~= 0 then
setlink(posttail,fontkern(leftkern))
done = true
end
end
end
if replace then
local i = p.replaceinjections
if i then
local leftkern = i.leftkern
if leftkern and leftkern ~= 0 then
setlink(replacetail,fontkern(leftkern))
done = true
end
end
else
local i = p.emptyinjections
if i then
-- new .. okay?
local leftkern = i.leftkern
if leftkern and leftkern ~= 0 then
replace = fontkern(leftkern)
done = true
end
end
end
if done then
setdisc(prevdisc,pre,post,replace)
end
end
end
prevdisc = nil
prevglyph = current
elseif char == false then
prevdisc = nil
prevglyph = current
elseif id == disc_code then
pre, post, replace, pretail, posttail, replacetail = getdisc(current,true)
local done = false
if pre then
-- left|pre glyphs|right
for n in nextchar, pre do
local p = rawget(properties,n)
if p then
local i = p.injections or p.preinjections
if i then
local yoffset = i.yoffset
if yoffset and yoffset ~= 0 then
setoffsets(n,false,yoffset)
end
local leftkern = i.leftkern
if leftkern and leftkern ~= 0 then
pre = insert_node_before(pre,n,fontkern(leftkern))
done = true
end
local rightkern = i.rightkern
if rightkern and rightkern ~= 0 then
insert_node_after(pre,n,fontkern(rightkern))
done = true
end
end
end
end
end
if post then
-- left|post glyphs|right
for n in nextchar, post do
local p = rawget(properties,n)
if p then
local i = p.injections or p.postinjections
if i then
local yoffset = i.yoffset
if yoffset and yoffset ~= 0 then
setoffsets(n,false,yoffset)
end
local leftkern = i.leftkern
if leftkern and leftkern ~= 0 then
post = insert_node_before(post,n,fontkern(leftkern))
done = true
end
local rightkern = i.rightkern
if rightkern and rightkern ~= 0 then
insert_node_after(post,n,fontkern(rightkern))
done = true
end
end
end
end
end
if replace then
-- left|replace glyphs|right
for n in nextchar, replace do
local p = rawget(properties,n)
if p then
local i = p.injections or p.replaceinjections
if i then
local yoffset = i.yoffset
if yoffset and yoffset ~= 0 then
setoffsets(n,false,yoffset)
end
local leftkern = i.leftkern
if leftkern and leftkern ~= 0 then
replace = insert_node_before(replace,n,fontkern(leftkern))
done = true
end
local rightkern = i.rightkern
if rightkern and rightkern ~= 0 then
insert_node_after(replace,n,fontkern(rightkern))
done = true
end
end
end
end
end
if prevglyph then
if pre then
local p = rawget(properties,prevglyph)
if p then
local i = p.preinjections
if i then
-- glyph|pre glyphs
local rightkern = i.rightkern
if rightkern and rightkern ~= 0 then
pre = insert_node_before(pre,pre,fontkern(rightkern))
done = true
end
end
end
end
if replace then
local p = rawget(properties,prevglyph)
if p then
local i = p.replaceinjections
if i then
-- glyph|replace glyphs
local rightkern = i.rightkern
if rightkern and rightkern ~= 0 then
replace = insert_node_before(replace,replace,fontkern(rightkern))
done = true
end
end
end
end
end
if done then
setdisc(current,pre,post,replace)
end
prevglyph = nil
prevdisc = current
else
prevglyph = nil
prevdisc = nil
end
prev = current
current = next
end
--
if keepregisteredcounts then
keepregisteredcounts = false
else
nofregisteredpositions = 0
end
if trace_injections then
show_result(head)
end
return head
end
local function showoffset(n,flag)
local x, y = getoffsets(n)
if x ~= 0 or y ~= 0 then
setcolor(n,"darkgray")
end
end
local function inject_everything(head,where)
if trace_injections then
trace(head,"everything")
end
local hascursives = nofregisteredcursives > 0
local hasmarks = nofregisteredmarks > 0
--
local current = head
local last = nil
local prev = nil
local next = nil
local prevdisc = nil
local prevglyph = nil
local pre = nil -- saves a lookup
local post = nil -- saves a lookup
local replace = nil -- saves a lookup
local pretail = nil -- saves a lookup
local posttail = nil -- saves a lookup
local replacetail = nil -- saves a lookup
--
local cursiveanchor = nil
local minc = 0
local maxc = 0
local glyphs = { }
local marks = { }
local nofmarks = 0
--
-- local applyfix = hascursives and fix_cursive_marks
--
-- move out
--
local function processmark(p,n,pn) -- p = basenode
local px, py = getoffsets(p)
local nx, ny = getoffsets(n)
local ox = 0
local rightkern = nil
local pp = rawget(properties,p)
if pp then
pp = pp.injections
if pp then
rightkern = pp.rightkern
end
end
local markdir = pn.markdir
if rightkern then -- x and w ~= 0
ox = px - (pn.markx or 0) - rightkern
if markdir and markdir < 0 then
-- kern(w-x) glyph(p) kern(x) mark(n)
if not pn.markmark then
ox = ox + (pn.leftkern or 0)
end
else
-- kern(x) glyph(p) kern(w-x) mark(n)
--
-- According to Kai we don't need to handle leftkern here but I'm
-- pretty sure I've run into a case where it was needed so maybe
-- some day we need something more clever here.
--
-- maybe we need to apply both then
--
if false then
-- a mark with kerning (maybe husayni needs it )
local leftkern = pp.leftkern
if leftkern then
ox = ox - leftkern
end
end
end
else
ox = px - (pn.markx or 0)
if markdir and markdir < 0 then
if not pn.markmark then
local leftkern = pn.leftkern
if leftkern then
ox = ox + leftkern -- husayni needs it
end
end
end
if pn.checkmark then
local wn = getwidth(n) -- in arial marks have widths
if wn and wn ~= 0 then
wn = wn/2
if trace_injections then
report_injections("correcting non zero width mark %C",getchar(n))
end
-- -- bad: we should center
--
-- pn.leftkern = -wn
-- pn.rightkern = -wn
--
-- -- we're too late anyway as kerns are already injected so we do it the
-- -- ugly way (no checking if the previous is already a kern) .. maybe we
-- -- should fix the font instead
--
-- todo: head and check for prev / next kern
--
insert_node_before(n,n,fontkern(-wn))
insert_node_after(n,n,fontkern(-wn))
end
end
end
local oy = ny + py + (pn.marky or 0)
if not pn.markmark then
local yoffset = pn.yoffset
if yoffset then
oy = oy + yoffset -- husayni needs it
end
end
setoffsets(n,ox,oy)
if trace_marks then
showoffset(n,true)
end
end
-- begin of temp fix --
-- local base = nil -- bah, some arabic fonts have no mark anchoring
-- end of temp fix --
while current do
local next = getnext(current)
local char, id = ischar(current)
if char then
local p = rawget(properties,current)
-- begin of temp fix --
-- if applyfix then
-- if not p then
-- local m = fontmarks[getfont(current)]
-- if m and m[char] then
-- if base then
-- p = { injections = { markbasenode = base } }
-- nofmarks = nofmarks + 1
-- marks[nofmarks] = current
-- properties[current] = p
-- hasmarks = true
-- end
-- else
-- base = current
-- end
-- end
-- end
-- end of temp fix
if p then
local i = p.injections
-- begin of temp fix --
-- if applyfix then
-- if not i then
-- local m = fontmarks[getfont(current)]
-- if m and m[char] then
-- if base then
-- i = { markbasenode = base }
-- nofmarks = nofmarks + 1
-- marks[nofmarks] = current
-- p.injections = i
-- hasmarks = true
-- end
-- else
-- base = current
-- end
-- end
-- end
-- end of temp fix --
if i then
local pm = i.markbasenode
-- begin of temp fix --
-- if applyfix then
-- if not pm then
-- local m = fontmarks[getfont(current)]
-- if m and m[char] then
-- if base then
-- pm = base
-- i.markbasenode = pm
-- hasmarks = true
-- end
-- else
-- base = current
-- end
-- else
-- base = current
-- end
-- end
-- end of temp fix --
if pm then
nofmarks = nofmarks + 1
marks[nofmarks] = current
else
local yoffset = i.yoffset
if yoffset and yoffset ~= 0 then
setoffsets(current,false,yoffset)
end
if hascursives then
local cursivex = i.cursivex
if cursivex then
if cursiveanchor then
if cursivex ~= 0 then
i.leftkern = (i.leftkern or 0) + cursivex
end
if maxc == 0 then
minc = 1
maxc = 1
glyphs[1] = cursiveanchor
else
maxc = maxc + 1
glyphs[maxc] = cursiveanchor
end
properties[cursiveanchor].cursivedy = i.cursivey -- cursiveprops
last = current
else
maxc = 0
end
elseif maxc > 0 then
local nx, ny = getoffsets(current)
for i=maxc,minc,-1 do
local ti = glyphs[i]
ny = ny + properties[ti].cursivedy
setoffsets(ti,false,ny) -- why not add ?
if trace_cursive then
showoffset(ti)
end
end
maxc = 0
cursiveanchor = nil
end
if i.cursiveanchor then
cursiveanchor = current -- no need for both now
else
if maxc > 0 then
local nx, ny = getoffsets(current)
for i=maxc,minc,-1 do
local ti = glyphs[i]
ny = ny + properties[ti].cursivedy
setoffsets(ti,false,ny) -- why not add ?
if trace_cursive then
showoffset(ti)
end
end
maxc = 0
end
cursiveanchor = nil
end
end
-- left|glyph|right
local leftkern = i.leftkern
local rightkern = i.rightkern
if leftkern and leftkern ~= 0 then
if rightkern and leftkern == -rightkern then
setoffsets(current,leftkern,false)
rightkern = 0
elseif prev and getid(prev) == glue_code then
if useitalickerns then
head = insert_node_before(head,current,italickern(leftkern))
else
setwidth(prev, getwidth(prev) + leftkern)
end
else
head = insert_node_before(head,current,fontkern(leftkern))
end
end
if rightkern and rightkern ~= 0 then
if next and getid(next) == glue_code then
if useitalickerns then
insert_node_after(head,current,italickern(rightkern))
else
setwidth(next, getwidth(next) + rightkern)
end
else
insert_node_after(head,current,fontkern(rightkern))
end
end
end
else
local i = p.emptyinjections
if i then
-- glyph|disc|glyph (special case)
local rightkern = i.rightkern
if rightkern and rightkern ~= 0 then
if next and getid(next) == disc_code then
if replace then
-- error, we expect an empty one
else
replace = fontkern(rightkern)
done = true
end
end
end
end
end
if prevdisc then
if p then
local done = false
if post then
local i = p.postinjections
if i then
local leftkern = i.leftkern
if leftkern and leftkern ~= 0 then
setlink(posttail,fontkern(leftkern))
done = true
end
end
end
if replace then
local i = p.replaceinjections
if i then
local leftkern = i.leftkern
if leftkern and leftkern ~= 0 then
setlink(replacetail,fontkern(leftkern))
done = true
end
end
else
local i = p.emptyinjections
if i then
local leftkern = i.leftkern
if leftkern and leftkern ~= 0 then
replace = fontkern(leftkern)
done = true
end
end
end
if done then
setdisc(prevdisc,pre,post,replace)
end
end
end
else
-- cursive
if hascursives and maxc > 0 then
local nx, ny = getoffsets(current)
for i=maxc,minc,-1 do
local ti = glyphs[i]
ny = ny + properties[ti].cursivedy
local xi, yi = getoffsets(ti)
setoffsets(ti,xi,yi + ny) -- can be mark, we could use properties
end
maxc = 0
cursiveanchor = nil
end
end
prevdisc = nil
prevglyph = current
elseif char == false then
-- base = nil
prevdisc = nil
prevglyph = current
elseif id == disc_code then
-- base = nil
pre, post, replace, pretail, posttail, replacetail = getdisc(current,true)
local done = false
if pre then
-- left|pre glyphs|right
for n in nextchar, pre do
local p = rawget(properties,n)
if p then
local i = p.injections or p.preinjections
if i then
local yoffset = i.yoffset
if yoffset and yoffset ~= 0 then
setoffsets(n,false,yoffset)
end
local leftkern = i.leftkern
if leftkern and leftkern ~= 0 then
pre = insert_node_before(pre,n,fontkern(leftkern))
done = true
end
local rightkern = i.rightkern
if rightkern and rightkern ~= 0 then
insert_node_after(pre,n,fontkern(rightkern))
done = true
end
if hasmarks then
local pm = i.markbasenode
if pm then
processmark(pm,n,i)
end
end
end
end
end
end
if post then
-- left|post glyphs|right
for n in nextchar, post do
local p = rawget(properties,n)
if p then
local i = p.injections or p.postinjections
if i then
local yoffset = i.yoffset
if yoffset and yoffset ~= 0 then
setoffsets(n,false,yoffset)
end
local leftkern = i.leftkern
if leftkern and leftkern ~= 0 then
post = insert_node_before(post,n,fontkern(leftkern))
done = true
end
local rightkern = i.rightkern
if rightkern and rightkern ~= 0 then
insert_node_after(post,n,fontkern(rightkern))
done = true
end
if hasmarks then
local pm = i.markbasenode
if pm then
processmark(pm,n,i)
end
end
end
end
end
end
if replace then
-- left|replace glyphs|right
for n in nextchar, replace do
local p = rawget(properties,n)
if p then
local i = p.injections or p.replaceinjections
if i then
local yoffset = i.yoffset
if yoffset and yoffset ~= 0 then
setoffsets(n,false,yoffset)
end
local leftkern = i.leftkern
if leftkern and leftkern ~= 0 then
replace = insert_node_before(replace,n,fontkern(leftkern))
done = true
end
local rightkern = i.rightkern
if rightkern and rightkern ~= 0 then
insert_node_after(replace,n,fontkern(rightkern))
done = true
end
if hasmarks then
local pm = i.markbasenode
if pm then
processmark(pm,n,i)
end
end
end
end
end
end
if prevglyph then
if pre then
local p = rawget(properties,prevglyph)
if p then
local i = p.preinjections
if i then
-- glyph|pre glyphs
local rightkern = i.rightkern
if rightkern and rightkern ~= 0 then
pre = insert_node_before(pre,pre,fontkern(rightkern))
done = true
end
end
end
end
if replace then
local p = rawget(properties,prevglyph)
if p then
local i = p.replaceinjections
if i then
-- glyph|replace glyphs
local rightkern = i.rightkern
if rightkern and rightkern ~= 0 then
replace = insert_node_before(replace,replace,fontkern(rightkern))
done = true
end
end
end
end
end
if done then
setdisc(current,pre,post,replace)
end
prevglyph = nil
prevdisc = current
else
-- base = nil
prevglyph = nil
prevdisc = nil
end
prev = current
current = next
end
-- cursive
if hascursives and maxc > 0 then
local nx, ny = getoffsets(last)
for i=maxc,minc,-1 do
local ti = glyphs[i]
ny = ny + properties[ti].cursivedy
setoffsets(ti,false,ny) -- why not add ?
if trace_cursive then
showoffset(ti)
end
end
end
--
if nofmarks > 0 then
for i=1,nofmarks do
local m = marks[i]
local p = rawget(properties,m)
local i = p.injections
local b = i.markbasenode
processmark(b,m,i)
end
elseif hasmarks then
-- sometyhing bad happened
end
--
if keepregisteredcounts then
keepregisteredcounts = false
else
nofregisteredkerns = 0
nofregisteredpositions = 0
nofregisteredmarks = 0
nofregisteredcursives = 0
end
if trace_injections then
show_result(head)
end
return head
end
-- space triggers
local triggers = false
function nodes.injections.setspacekerns(font,sequence)
if triggers then
triggers[font] = sequence
else
triggers = { [font] = sequence }
end
end
local getthreshold
if context then
local threshold = 1 -- todo: add a few methods for context
local parameters = fonts.hashes.parameters
directives.register("otf.threshold", function(v) threshold = tonumber(v) or 1 end)
getthreshold = function(font)
local p = parameters[font]
local f = p.factor
local s = p.spacing
local t = threshold * (s and s.width or p.space or 0) - 2
return t > 0 and t or 0, f
end
else
injections.threshold = 0
getthreshold = function(font)
local p = fontdata[font].parameters
local f = p.factor
local s = p.spacing
local t = injections.threshold * (s and s.width or p.space or 0) - 2
return t > 0 and t or 0, f
end
end
injections.getthreshold = getthreshold
function injections.isspace(n,threshold,id)
if (id or getid(n)) == glue_code then
local w = getwidth(n)
if threshold and w > threshold then -- was >=
return 32
end
end
end
-- We have a plugin so that Kai can use the next in plain. Such a plugin is rather application
-- specific.
--
-- local getboth = nodes.direct.getboth
-- local getid = nodes.direct.getid
-- local getprev = nodes.direct.getprev
-- local getnext = nodes.direct.getnext
--
-- local whatsit_code = nodes.nodecodes.whatsit
-- local glyph_code = nodes.nodecodes.glyph
--
-- local function getspaceboth(n) -- fragile: what it prev/next has no width field
-- local prev, next = getboth(n)
-- while prev and (getid(prev) == whatsit_code or (getwidth(prev) == 0 and getid(prev) ~= glyph_code)) do
-- prev = getprev(prev)
-- end
-- while next and (getid(next) == whatsit_code or (getwidth(next) == 0 and getid(next) ~= glyph_code)) do
-- next = getnext(next)
-- end
-- end
--
-- injections.installgetspaceboth(getspaceboth)
local getspaceboth = getboth
function injections.installgetspaceboth(gb)
getspaceboth = gb or getboth
end
local function injectspaces(head)
if not triggers then
return head
end
local lastfont = nil
local spacekerns = nil
local leftkerns = nil
local rightkerns = nil
local factor = 0
local threshold = 0
local leftkern = false
local rightkern = false
local function updatefont(font,trig)
leftkerns = trig.left
rightkerns = trig.right
lastfont = font
threshold,
factor = getthreshold(font)
end
for n in nextglue, head do
local prev, next = getspaceboth(n)
local prevchar = prev and ischar(prev)
local nextchar = next and ischar(next)
if nextchar then
local font = getfont(next)
local trig = triggers[font]
if trig then
if lastfont ~= font then
updatefont(font,trig)
end
if rightkerns then
rightkern = rightkerns[nextchar]
end
end
end
if prevchar then
local font = getfont(prev)
local trig = triggers[font]
if trig then
if lastfont ~= font then
updatefont(font,trig)
end
if leftkerns then
leftkern = leftkerns[prevchar]
end
end
end
if leftkern then
local old = getwidth(n)
if old > threshold then
if rightkern then
if useitalickerns then
local lnew = leftkern * factor
local rnew = rightkern * factor
if trace_spaces then
report_spaces("%C [%p + %p + %p] %C",prevchar,lnew,old,rnew,nextchar)
end
head = insert_node_before(head,n,italickern(lnew))
insert_node_after(head,n,italickern(rnew))
else
local new = old + (leftkern + rightkern) * factor
if trace_spaces then
report_spaces("%C [%p -> %p] %C",prevchar,old,new,nextchar)
end
setwidth(n,new)
end
rightkern = false
else
if useitalickerns then
local new = leftkern * factor
if trace_spaces then
report_spaces("%C [%p + %p]",prevchar,old,new)
end
insert_node_after(head,n,italickern(new)) -- tricky with traverse but ok
else
local new = old + leftkern * factor
if trace_spaces then
report_spaces("%C [%p -> %p]",prevchar,old,new)
end
setwidth(n,new)
end
end
end
leftkern = false
elseif rightkern then
local old = getwidth(n)
if old > threshold then
if useitalickerns then
local new = rightkern * factor
if trace_spaces then
report_spaces("[%p + %p] %C",old,new,nextchar)
end
insert_node_after(head,n,italickern(new))
else
local new = old + rightkern * factor
if trace_spaces then
report_spaces("[%p -> %p] %C",old,new,nextchar)
end
setwidth(n,new)
end
else
-- message
end
rightkern = false
end
end
triggers = false
return head
end
--
function injections.handler(head,where)
if triggers then
head = injectspaces(head)
end
-- todo: marks only run too
if nofregisteredmarks > 0 or nofregisteredcursives > 0 then
if trace_injections then
report_injections("injection variant %a","everything")
end
return inject_everything(head,where)
elseif nofregisteredpositions > 0 then
if trace_injections then
report_injections("injection variant %a","positions")
end
return inject_positions_only(head,where)
elseif nofregisteredkerns > 0 then
if trace_injections then
report_injections("injection variant %a","kerns")
end
return inject_kerns_only(head,where)
else
return head
end
end