diff options
Diffstat (limited to 'tex/context/base/typo-krn.lua')
-rw-r--r-- | tex/context/base/typo-krn.lua | 586 |
1 files changed, 397 insertions, 189 deletions
diff --git a/tex/context/base/typo-krn.lua b/tex/context/base/typo-krn.lua index 56f58bb73..46a977cfd 100644 --- a/tex/context/base/typo-krn.lua +++ b/tex/context/base/typo-krn.lua @@ -11,23 +11,38 @@ 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 find_node_tail = node.tail or node.slide -local free_node = node.free -local free_nodelist = node.flush_list -local copy_node = node.copy -local copy_nodelist = node.copy_list -local insert_node_before = node.insert_before -local insert_node_after = node.insert_after -local end_of_math = node.end_of_math +local tasks = nodes.tasks +local nuts = nodes.nuts +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 insert_node_before = nuts.insert_before +local insert_node_after = nuts.insert_after +local end_of_math = nuts.end_of_math + +local getfield = nuts.getfield +local setfield = nuts.setfield +local getnext = nuts.getnext +local getprev = nuts.getprev +local getid = nuts.getid +local getattr = nuts.getattr +local setattr = nuts.setattr +local getfont = nuts.getfont +local getsubtype = nuts.getsubtype +local getchar = nuts.getchar local texsetattribute = tex.setattribute local unsetvalue = attributes.unsetvalue -local nodepool = nodes.pool -local tasks = nodes.tasks - local new_gluespec = nodepool.gluespec local new_kern = nodepool.kern local new_glue = nodepool.glue @@ -36,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 @@ -45,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 @@ -107,10 +128,10 @@ kerns.keeptogether = false -- just for fun (todo: control setting with key/value -- blue : keep by goodie function kerns.keepligature(n) -- might become default - local f = n.font - local a = n[0] or 0 + local f = getfont(n) + local a = getattr(n,0) or 0 if trace_ligatures then - local c = n.char + local c = getchar(n) local d = fontdescriptions[f][c].name if a > 0 and contextsetups[a].keepligatures == v_auto then report("font %!font:name!, glyph %a, slot %X -> ligature %s, by %s feature %a",f,d,c,"kept","dynamic","keepligatures") @@ -169,9 +190,9 @@ end local function kern_injector(fillup,kern) if fillup then local g = new_glue(kern) - local s = g.spec - s.stretch = kern - s.stretch_order = 1 + local s = getfield(g,"spec") + setfield(s,"stretch",kern) + setfield(s,"stretch_order",1) return g else return new_kern(kern) @@ -181,212 +202,398 @@ end local function spec_injector(fillup,width,stretch,shrink) if fillup then local s = new_gluespec(width,2*stretch,2*shrink) - s.stretch_order = 1 + setfield(s,"stretch_order",1) return s else return new_gluespec(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 do_process(head,force) -- todo: glue so that we can fully stretch - local start, done, lastfont = head, false, nil +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 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 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 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 - start[a_kerns] = unsetvalue + setattr(start,a_kerns,0) -- unsetvalue) local krn = mapping[attr] if krn == v_max then - krn = .25 + krn = .25 fillup = true else fillup = false end - if krn and krn ~= 0 then - local id = start.id - if id == glyph_code then - lastfont = start.font - local c = start.components - if not c then - -- fine - elseif keepligature and keepligature(start) then - -- keep 'm - else - c = do_process(c,attr) + 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 p, n = s.prev, s.next - local tail = find_node_tail(c) + local t = find_node_tail(c) + local p = getprev(s) + local n = getnext(s) if p then - p.next = c - c.prev = p + setfield(p,"next",c) + setfield(c,"prev",p) else head = c end if n then - n.prev = tail + setfield(n,"prev",t) + setfield(t,"next",n) end - tail.next = n start = c - s.components = nil - -- we now leak nodes ! - -- free_node(s) - done = true + setfield(s,"components",nil) + free_node(s) + c = getfield(start,"components") end - local prev = start.prev - if not prev then - -- skip - elseif markdata[lastfont][start.char] then - -- skip - else - local pid = prev.id - if not pid then - -- nothing - elseif pid == kern_code then - if prev.subtype == kerning_code or prev[a_fontkern] then - if keeptogether and prev.prev.id == glyph_code and keeptogether(prev.prev,start) then -- we could also pass start - -- keep 'm - else - -- not yet ok, as injected kerns can be overlays (from node-inj.lua) - prev.subtype = userkern_code - prev.kern = prev.kern + quaddata[lastfont]*krn -- here - done = true - end - end - elseif pid == glyph_code then - if prev.font == lastfont then - local prevchar, lastchar = prev.char, start.char - 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, nxt = disc.prev, disc.next - if disc.subtype == discretionary_code then - -- maybe we should forget about this variant as there is no glue - -- possible - local pre, post, replace = disc.pre, disc.post, disc.replace - if pre and prv then -- must pair with start.prev - -- this one happens in most cases - local before = copy_node(prv) - pre.prev = before - before.next = pre - before.prev = nil - pre = do_process(before,attr) - pre = pre.next - pre.prev = nil - 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) - tail.next = after - after.prev = tail - after.next = nil - post = do_process(post,attr) - tail.next = nil - 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) - replace.prev = before - before.next = replace - before.prev = nil - tail.next = after - after.prev = tail - after.next = nil - replace = do_process(before,attr) - replace = replace.next - replace.prev = nil - after.prev.next = nil - disc.replace = replace - free_node(after) - free_node(before) - elseif prv and prv.id == glyph_code and prv.font == lastfont then - local prevchar, lastchar = prv.char, start.char - local kerns = chardata[lastfont][prevchar].kerns - local kern = kerns and kerns[lastchar] or 0 - krn = kern + quaddata[lastfont]*krn -- here - disc.replace = kern_injector(false,krn) -- only kerns permitted, no glue - else - krn = quaddata[lastfont]*krn -- here - 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 prv.id == glyph_code and prv.font == lastfont then - local prevchar, lastchar = prv.char, start.char - local kerns = chardata[lastfont][prevchar].kerns - local kern = kerns and kerns[lastchar] or 0 - krn = kern + quaddata[lastfont]*krn -- here - else - krn = quaddata[lastfont]*krn -- here - 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 = start.subtype - if subtype == userskip_code or subtype == xspaceskip_code or subtype == spaceskip_code then - local s = start.spec - local w = s.width - if w > 0 then - local width, stretch, shrink = w+gluefactor*w*krn, s.stretch, s.shrink - 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 start.subtype == kerning_code then -- handle with glyphs - -- local sk = start.kern - -- if sk > 0 then - -- start.kern = sk*krn - -- done = true - -- end - -- end - elseif lastfont and (id == hlist_code or id == vlist_code) then -- todo: lookahead - local p = start.prev - if p and p.id ~= 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 = start.next - if n and n.id ~= 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 - start = start.next + 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 @@ -413,10 +620,11 @@ function kerns.set(factor) return factor end -function kerns.handler(head) - return do_process(head) -- no direct map, because else fourth argument is tail == true -end - -- interface -commands.setcharacterkerning = kerns.set +interfaces.implement { + name = "setcharacterkerning", + actions = kerns.set, + arguments = "string" +} + |