summaryrefslogtreecommitdiff
path: root/tex/context/base/typo-krn.lua
diff options
context:
space:
mode:
Diffstat (limited to 'tex/context/base/typo-krn.lua')
-rw-r--r--tex/context/base/typo-krn.lua529
1 files changed, 353 insertions, 176 deletions
diff --git a/tex/context/base/typo-krn.lua b/tex/context/base/typo-krn.lua
index 5729c72c0..1616577dc 100644
--- a/tex/context/base/typo-krn.lua
+++ b/tex/context/base/typo-krn.lua
@@ -11,7 +11,8 @@ if not modules then modules = { } end modules ['typo-krn'] = {
local next, type, tonumber = next, type, tonumber
local utfchar = utf.char
-local nodes, node, fonts = nodes, node, fonts
+local nodes = nodes
+local fonts = fonts
local tasks = nodes.tasks
local nuts = nodes.nuts
@@ -20,11 +21,10 @@ local nodepool = nuts.pool
local tonode = nuts.tonode
local tonut = nuts.tonut
+-- check what is used
+
local find_node_tail = nuts.tail
local free_node = nuts.free
-local free_nodelist = nuts.flush_list
-local copy_node = nuts.copy
-local copy_nodelist = nuts.copy_list
local insert_node_before = nuts.insert_before
local insert_node_after = nuts.insert_after
local end_of_math = nuts.end_of_math
@@ -51,6 +51,7 @@ local nodecodes = nodes.nodecodes
local kerncodes = nodes.kerncodes
local skipcodes = nodes.skipcodes
local disccodes = nodes.disccodes
+local listcodes = nodes.listcodes
local glyph_code = nodecodes.glyph
local kern_code = nodecodes.kern
@@ -60,7 +61,12 @@ local hlist_code = nodecodes.hlist
local vlist_code = nodecodes.vlist
local math_code = nodecodes.math
+local box_list_code = listcodes.box
+local user_list_code = listcodes.unknown
+
local discretionary_code = disccodes.discretionary
+local automatic_code = disccodes.automatic
+
local kerning_code = kerncodes.kerning
local userkern_code = kerncodes.userkern
local userskip_code = skipcodes.userskip
@@ -203,20 +209,178 @@ local function spec_injector(fillup,width,stretch,shrink)
end
end
--- needs checking ... base mode / node mode -- also use insert_before/after etc
+-- a simple list injector, no components and such .. just disable ligatures in
+-- kern mode .. maybe not even hyphenate ... anyway, the next one is for simple
+-- sublists .. beware: we can have char -1
+
+local function inject_begin(boundary,prev,keeptogether,krn,ok) -- prev is a glyph
+ local id = getid(boundary)
+ if id == kern_code then
+ if getsubtype(boundary) == kerning_code or getattr(boundary,a_fontkern) then
+ local inject = true
+ if keeptogether then
+ local next = getnext(boundary)
+ if not next or (getid(next) == glyph_code and keeptogether(prev,next)) then
+ inject = false
+ end
+ end
+ if inject then
+ -- not yet ok, as injected kerns can be overlays (from node-inj.lua)
+ setfield(boundary,"subtype",userkern_code)
+ setfield(boundary,"kern",getfield(boundary,"kern") + quaddata[getfont(prev)]*krn)
+ return boundary, true
+ end
+ end
+ elseif id == glyph_code then
+ if keeptogether and keeptogether(boundary,prev) then
+ -- keep 'm
+ else
+ local charone = getchar(prev)
+ if charone > 0 then
+ local font = getfont(boundary)
+ local chartwo = getchar(boundary)
+ local kerns = chardata[font][charone].kerns
+ local kern = new_kern((kerns and kerns[chartwo] or 0) + quaddata[font]*krn)
+ setfield(boundary,"prev",kern)
+ setfield(kern,"next",boundary)
+ return kern, true
+ end
+ end
+ end
+ return boundary, ok
+end
+
+local function inject_end(boundary,next,keeptogether,krn,ok)
+ local tail = find_node_tail(boundary)
+ local id = getid(tail)
+ if id == kern_code then
+ if getsubtype(tail) == kerning_code or getattr(tail,a_fontkern) then
+ local inject = true
+ if keeptogether then
+ local prev = getprev(tail)
+ if getid(prev) == glyph_code and keeptogether(prev,two) then
+ inject = false
+ end
+ end
+ if inject then
+ -- not yet ok, as injected kerns can be overlays (from node-inj.lua)
+ setfield(tail,"subtype",userkern_code)
+ setfield(tail,"kern",getfield(tail,"kern") + quaddata[getfont(next)]*krn)
+ return boundary, true
+ end
+ end
+ elseif id == glyph_code then
+ if keeptogether and keeptogether(tail,two) then
+ -- keep 'm
+ else
+ local charone = getchar(tail)
+ if charone > 0 then
+ local font = getfont(tail)
+ local chartwo = getchar(next)
+ local kerns = chardata[font][charone].kerns
+ local kern = (kerns and kerns[chartwo] or 0) + quaddata[font]*krn
+ insert_node_after(boundary,tail,new_kern(kern))
+ return boundary, true
+ end
+ end
+ end
+ return boundary, ok
+end
+
+local function process_list(head,keeptogether,krn,font,okay)
+ local start = head
+ local prev = nil
+ local pid = nil
+ local kern = 0
+ local mark = font and markdata[font]
+ while start do
+ local id = getid(start)
+ if id == glyph_code then
+ if not font then
+ font = getfont(start)
+ mark = markdata[font]
+ kern = quaddata[font]*krn
+ end
+ if prev then
+ local char = getchar(start)
+ if mark[char] then
+ -- skip
+ elseif pid == kern_code then
+ if getsubtype(prev) == kerning_code or getattr(prev,a_fontkern) then
+ local inject = true
+ if keeptogether then
+ local prevprev = getprev(prev)
+ if getid(prevprev) == glyph_code and keeptogether(prevprev,start) then
+ inject = false
+ end
+ end
+ if inject then
+ -- not yet ok, as injected kerns can be overlays (from node-inj.lua)
+ setfield(prev,"subtype",userkern_code)
+ setfield(prev,"kern",getfield(prev,"kern") + kern)
+ okay = true
+ end
+ end
+ elseif pid == glyph_code then
+ if keeptogether and keeptogether(prev,start) then
+ -- keep 'm
+ else
+ local prevchar = getchar(prev)
+ local kerns = chardata[font][prevchar].kerns
+ -- if kerns then
+ -- print("it happens indeed, basemode kerns not yet injected")
+ -- end
+ insert_node_before(head,start,new_kern((kerns and kerns[char] or 0) + kern))
+ okay = true
+ end
+ end
+ end
+ end
+ if start then
+ prev = start
+ pid = id
+ start = getnext(start)
+ end
+ end
+ return head, okay, prev
+end
-local function do_process(head,force) -- todo: glue so that we can fully stretch
+local function closest_bound(b,get)
+ b = get(b)
+ if b and getid(b) ~= glue_code then
+ while b do
+ if not getattr(b,a_kerns) then
+ break
+ elseif getid(b) == glyph_code then
+ return b, getfont(b)
+ else
+ b = get(b)
+ end
+ end
+ end
+end
+
+function kerns.handler(head)
+ local head = tonut(head)
local start = head
local done = false
local lastfont = nil
local keepligature = kerns.keepligature
local keeptogether = kerns.keeptogether
local fillup = false
+ local bound = false
+ local prev = nil
+ local previd = nil
+ local prevchar = nil
+ local prevfont = nil
+ local prevmark = nil
while start do
- -- faster to test for attr first
- local attr = force or getattr(start,a_kerns)
+ -- fontkerns don't get the attribute but they always sit between glyphs so
+ -- are always valid bound .. disc nodes also somtimes don't get them
+ local id = getid(start)
+ local attr = getattr(start,a_kerns)
if attr and attr > 0 then
- setattr(start,a_kerns,unsetvalue)
+ setattr(start,a_kerns,0) -- unsetvalue)
local krn = mapping[attr]
if krn == v_max then
krn = .25
@@ -224,21 +388,21 @@ local function do_process(head,force) -- todo: glue so that we can fully stretch
else
fillup = false
end
- if krn and krn ~= 0 then
- local id = getid(start)
- if id == glyph_code then -- we could use the subtype ligature
- lastfont = getfont(start)
- local c = getfield(start,"components")
- if not c then
- -- fine
- elseif keepligature and keepligature(start) then
- -- keep 'm
- else
- c = do_process(c,attr)
- local s = start
- local p = getprev(s)
- local n = getnext(s)
- local tail = find_node_tail(c)
+ if not krn or krn == 0 then
+ bound = false
+ elseif id == glyph_code then -- we could use the subtype ligature
+ local c = getfield(start,"components")
+ if not c then
+ -- fine
+ elseif keepligature and keepligature(start) then
+ -- keep 'm
+ c = nil
+ else
+ while c do
+ local s = start
+ local t = find_node_tail(c)
+ local p = getprev(s)
+ local n = getnext(s)
if p then
setfield(p,"next",c)
setfield(c,"prev",p)
@@ -246,172 +410,190 @@ local function do_process(head,force) -- todo: glue so that we can fully stretch
head = c
end
if n then
- setfield(n,"prev",tail)
+ setfield(n,"prev",t)
+ setfield(t,"next",n)
end
- setfield(tail,"next",n)
start = c
setfield(s,"components",nil)
- -- we now leak nodes !
- -- free_node(s)
- done = true
+ free_node(s)
+ c = getfield(start,"components")
end
- local prev = getprev(start)
- if not prev then
- -- skip
- elseif markdata[lastfont][getchar(start)] then
- -- skip
- else
- local pid = getid(prev)
- if not pid then
- -- nothing
- elseif pid == kern_code then
- if getsubtype(prev) == kerning_code or getattr(prev,a_fontkern) then
- if keeptogether and getid(getprev(prev)) == glyph_code and keeptogether(getprev(prev),start) then -- we could also pass start
- -- keep 'm
- else
- -- not yet ok, as injected kerns can be overlays (from node-inj.lua)
- setfield(prev,"subtype",userkern_code)
- setfield(prev,"kern",getfield(prev,"kern") + quaddata[lastfont]*krn) -- here
- done = true
- end
- end
- elseif pid == glyph_code then
- if getfont(prev) == lastfont then
- local prevchar, lastchar = getchar(prev), getchar(start)
- if keeptogether and keeptogether(prev,start) then
- -- keep 'm
- else
- local kerns = chardata[lastfont][prevchar].kerns
- local kern = kerns and kerns[lastchar] or 0
- krn = kern + quaddata[lastfont]*krn -- here
- insert_node_before(head,start,kern_injector(fillup,krn))
- done = true
- end
- else
- krn = quaddata[lastfont]*krn -- here
- insert_node_before(head,start,kern_injector(fillup,krn))
- done = true
- end
- elseif pid == disc_code then
- -- a bit too complicated, we can best not copy and just calculate
- -- but we could have multiple glyphs involved so ...
- local disc = prev -- disc
- local prv = getprev(disc)
- local nxt = getnext(disc)
- if getsubtype(disc) == discretionary_code then
- -- maybe we should forget about this variant as there is no glue
- -- possible .. hardly used so a copy doesn't hurt much
- local pre = getfield(disc,"pre")
- local post = getfield(disc,"post")
- local replace = getfield(disc,"replace")
- if pre and prv then -- must pair with getprev(start)
- local before = copy_node(prv)
- setfield(pre,"prev",before)
- setfield(before,"next",pre)
- setfield(before,"prev",nil)
- pre = do_process(before,attr)
- pre = getnext(pre)
- setfield(pre,"prev",nil)
- setfield(disc,"pre",pre)
- free_node(before)
- end
- if post and nxt then -- must pair with start
- local after = copy_node(nxt)
- local tail = find_node_tail(post)
- setfield(tail,"next",after)
- setfield(after,"prev",tail)
- setfield(after,"next",nil)
- post = do_process(post,attr)
- setfield(tail,"next",nil)
- setfield(disc,"post",post)
- free_node(after)
- end
- if replace and prv and nxt then -- must pair with start and start.prev
- local before = copy_node(prv)
- local after = copy_node(nxt)
- local tail = find_node_tail(replace)
- setfield(replace,"prev",before)
- setfield(before,"next",replace)
- setfield(before,"prev",nil)
- setfield(tail,"next",after)
- setfield(after,"prev",tail)
- setfield(after,"next",nil)
- replace = do_process(before,attr)
- replace = getnext(replace)
- setfield(replace,"prev",nil)
- setfield(getfield(after,"prev"),"next",nil)
- setfield(disc,"replace",replace)
- free_node(after)
- free_node(before)
- elseif prv and getid(prv) == glyph_code and getfont(prv) == lastfont then
- local prevchar = getchar(prv)
- local lastchar = getchar(start)
- local kerns = chardata[lastfont][prevchar].kerns
- local kern = kerns and kerns[lastchar] or 0
- krn = kern + quaddata[lastfont]*krn -- here
- setfield(disc,"replace",kern_injector(false,krn)) -- only kerns permitted, no glue
- else
- krn = quaddata[lastfont]*krn -- here
- setfield(disc,"replace",kern_injector(false,krn)) -- only kerns permitted, no glue
- end
- else
- -- this one happens in most cases: automatic (-), explicit (\-), regular (patterns)
- if prv and getid(prv) == glyph_code and getfont(prv) == lastfont then
- -- the normal case
- local prevchar = getchar(prv)
- local lastchar = getchar(start)
- local kerns = chardata[lastfont][prevchar].kerns
- local kern = kerns and kerns[lastchar] or 0
- krn = kern + quaddata[lastfont]*krn
- else
- krn = quaddata[lastfont]*krn
- end
- insert_node_before(head,start,kern_injector(fillup,krn))
+ end
+ local char = getchar(start)
+ local font = getfont(start)
+ local mark = markdata[font]
+ if not bound then
+ -- yet
+ elseif mark[char] then
+ -- skip
+ elseif previd == kern_code then
+ if getsubtype(prev) == kerning_code or getattr(prev,a_fontkern) then
+ local inject = true
+ if keeptogether then
+ if previd == glyph_code and keeptogether(prev,start) then
+ inject = false
end
end
+ if inject then
+ -- not yet ok, as injected kerns can be overlays (from node-inj.lua)
+ setfield(prev,"subtype",userkern_code)
+ setfield(prev,"kern",getfield(prev,"kern") + quaddata[font]*krn)
+ done = true
+ end
end
- elseif id == glue_code then
- local subtype = getsubtype(start)
- if subtype == userskip_code or subtype == xspaceskip_code or subtype == spaceskip_code then
- local s = getfield(start,"spec")
- local w = getfield(s,"width")
- if w > 0 then
- local width = w+gluefactor*w*krn
- local stretch = getfield(s,"stretch")
- local shrink = getfield(s,"shrink")
- setfield(start,"spec",spec_injector(fillup,width,stretch*width/w,shrink*width/w))
+ elseif previd == glyph_code then
+ if prevfont == font then
+ if keeptogether and keeptogether(prev,start) then
+ -- keep 'm
+ else
+ local kerns = chardata[font][prevchar].kerns
+ local kern = (kerns and kerns[char] or 0) + quaddata[font]*krn
+ insert_node_before(head,start,kern_injector(fillup,kern))
done = true
end
+ else
+ insert_node_before(head,start,kern_injector(fillup,quaddata[font]*krn))
+ done = true
+ end
+ end
+ prev = start
+ prevchar = char
+ prevfont = font
+ prevmark = mark
+ previd = id
+ bound = true
+ elseif id == disc_code then
+ local prev, next, pglyph, nglyph -- delayed till needed
+ local subtype = getsubtype(start)
+ if subtype == automatic_code then
+ -- this is kind of special, as we have already injected the
+ -- previous kern
+ local prev = getprev(start)
+ local pglyph = prev and getid(prev) == glyph_code
+ languages.expand(start,pglyph and prev)
+ -- we can have a different start now
+ elseif subtype ~= discretionary_code then
+ prev = getprev(start)
+ pglyph = prev and getid(prev) == glyph_code
+ languages.expand(start,pglyph and prev)
+ end
+ local pre = getfield(start,"pre")
+ local post = getfield(start,"post")
+ local replace = getfield(start,"replace")
+ -- we really need to reasign the fields as luatex keeps track of
+ -- the tail in a temp preceding head .. kind of messy so we might
+ -- want to come up with a better solution some day like a real
+ -- pretail etc fields in a disc node
+ --
+ -- maybe i'll merge the now split functions
+ if pre then
+ local okay = false
+ if not prev then
+ prev = prev or getprev(start)
+ pglyph = prev and getid(prev) == glyph_code
+ end
+ if pglyph then
+ pre, okay = inject_begin(pre,prev,keeptogether,krn,okay)
+ end
+ pre, okay = process_list(pre,keeptogether,krn,false,okay)
+ if okay then
+ setfield(start,"pre",pre)
+ done = true
+ end
+ end
+ if post then
+ local okay = false
+ if not next then
+ next = getnext(start)
+ nglyph = next and getid(next) == glyph_code
+ end
+ if nglyph then
+ post, okay = inject_end(post,next,keeptogether,krn,okay)
end
- elseif id == kern_code then
- -- if getsubtype(start) == kerning_code then -- handle with glyphs
- -- local sk = getfield(start,"kern")
- -- if sk > 0 then
- -- setfield(start,"kern",sk*krn)
- -- done = true
- -- end
- -- end
- elseif lastfont and (id == hlist_code or id == vlist_code) then -- todo: lookahead
- local p = getprev(start)
- if p and getid(p) ~= glue_code then
- insert_node_before(head,start,kern_injector(fillup,quaddata[lastfont]*krn))
+ post, okay = process_list(post,keeptogether,krn,false,okay)
+ if okay then
+ setfield(start,"post",post)
done = true
end
- local n = getnext(start)
- if n and getid(n) ~= glue_code then
- insert_node_after(head,start,kern_injector(fillup,quaddata[lastfont]*krn))
+ end
+ if replace then
+ local okay = false
+ if not prev then
+ prev = prev or getprev(start)
+ pglyph = prev and getid(prev) == glyph_code
+ end
+ if pglyph then
+ replace, okay = inject_begin(replace,prev,keeptogether,krn,okay)
+ end
+ if not next then
+ next = getnext(start)
+ nglyph = next and getid(next) == glyph_code
+ end
+ if nglyph then
+ replace, okay = inject_end(replace,next,keeptogether,krn,okay)
+ end
+ replace, okay = process_list(replace,keeptogether,krn,false,okay)
+ if okay then
+ setfield(start,"replace",replace)
done = true
end
- elseif id == math_code then
- start = end_of_math(start)
+ elseif prevfont then
+ setfield(start,"replace",new_kern(quaddata[prevfont]*krn))
+ done = true
end
+ bound = false
+ elseif id == kern_code then
+ bound = getsubtype(start) == kerning_code or getattr(start,a_fontkern)
+ prev = start
+ previd = id
+ elseif id == glue_code then
+ local subtype = getsubtype(start)
+ if subtype == userskip_code or subtype == xspaceskip_code or subtype == spaceskip_code then
+ local s = getfield(start,"spec")
+ local w = getfield(s,"width")
+ if w > 0 then
+ local width = w+gluefactor*w*krn
+ local stretch = getfield(s,"stretch")
+ local shrink = getfield(s,"shrink")
+ setfield(start,"spec",spec_injector(fillup,width,stretch*width/w,shrink*width/w))
+ done = true
+ end
+ end
+ bound = false
+ elseif id == hlist_code or id == vlist_code then
+ local subtype = getsubtype(start)
+ if subtype == user_list_code or subtype == box_list_code then
+ -- special case
+ local b, f = closest_bound(start,getprev)
+ if b then
+ insert_node_before(head,start,kern_injector(fillup,quaddata[f]*krn))
+ done = true
+ end
+ local b, f = closest_bound(start,getnext)
+ if b then
+ insert_node_after(head,start,kern_injector(fillup,quaddata[f]*krn))
+ done = true
+ end
+ end
+ bound = false
+ elseif id == math_code then
+ start = end_of_math(start)
+ bound = false
end
- end
- if start then
+ if start then
+ start = getnext(start)
+ end
+ elseif id == kern_code then
+ bound = getsubtype(start) == kerning_code or getattr(start,a_fontkern)
+ prev = start
+ previd = id
+ start = getnext(start)
+ else
+ bound = false
start = getnext(start)
end
end
- return head, done
+ return tonode(head), done
end
local enabled = false
@@ -438,11 +620,6 @@ function kerns.set(factor)
return factor
end
-function kerns.handler(head)
- local head, done = do_process(tonut(head)) -- no direct map, because else fourth argument is tail == true
- return tonode(head), done
-end
-
-- interface
commands.setcharacterkerning = kerns.set