From ee02b5441b8ed416b5d75ecb2597f6b2ca3ec7a1 Mon Sep 17 00:00:00 2001 From: Philipp Gesang Date: Fri, 17 May 2013 12:28:12 +0200 Subject: import ``typo-krn`` --- luaotfload-typo-krn.lua | 335 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 335 insertions(+) create mode 100644 luaotfload-typo-krn.lua diff --git a/luaotfload-typo-krn.lua b/luaotfload-typo-krn.lua new file mode 100644 index 0000000..fb28d3b --- /dev/null +++ b/luaotfload-typo-krn.lua @@ -0,0 +1,335 @@ +if not modules then modules = { } end modules ['typo-krn'] = { + version = 1.001, + comment = "companion to typo-krn.mkiv", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +local next, type, tonumber = next, type, tonumber +local utfchar = utf.char + +local nodes, node, fonts = nodes, node, 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 texattribute = tex.attribute +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 + +local nodecodes = nodes.nodecodes +local kerncodes = nodes.kerncodes +local skipcodes = nodes.skipcodes + +local glyph_code = nodecodes.glyph +local kern_code = nodecodes.kern +local disc_code = nodecodes.disc +local glue_code = nodecodes.glue +local hlist_code = nodecodes.hlist +local vlist_code = nodecodes.vlist +local math_code = nodecodes.math + +local kerning_code = kerncodes.kerning +local userkern_code = kerncodes.userkern +local userskip_code = skipcodes.userskip +local spaceskip_code = skipcodes.spaceskip +local xspaceskip_code = skipcodes.xspaceskip + +local fonthashes = fonts.hashes +local fontdata = fonthashes.identifiers +local chardata = fonthashes.characters +local quaddata = fonthashes.quads +local markdata = fonthashes.marks + +local v_max = interfaces.variables.max + +typesetters = typesetters or { } +local typesetters = typesetters + +typesetters.kerns = typesetters.kerns or { } +local kerns = typesetters.kerns + +kerns.mapping = kerns.mapping or { } +kerns.factors = kerns.factors or { } +local a_kerns = attributes.private("kern") +local a_fontkern = attributes.private('fontkern') +kerns.attribute = kerns.attribute + +storage.register("typesetters/kerns/mapping", kerns.mapping, "typesetters.kerns.mapping") +storage.register("typesetters/kerns/factors", kerns.factors, "typesetters.kerns.factors") + +local mapping = kerns.mapping +local factors = kerns.factors + +-- one must use liga=no and mode=base and kern=yes +-- use more helpers +-- make sure it runs after all others +-- there will be a width adaptor field in nodes so this will change +-- todo: interchar kerns / disc nodes / can be made faster + +local gluefactor = 4 -- assumes quad = .5 enspace + +kerns.keepligature = false -- just for fun (todo: control setting with key/value) +kerns.keeptogether = false -- just for fun (todo: control setting with key/value) + +-- can be optimized .. the prev thing .. but hardly worth the effort + +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 + return g + else + return new_kern(kern) + end +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 + return s + else + return new_gluespec(width,stretch,shrink) + end +end + +-- needs checking ... base mode / node mode + +local function do_process(namespace,attribute,head,force) -- todo: glue so that we can fully stretch + local start, done, lastfont = head, false, nil + local keepligature = kerns.keepligature + local keeptogether = kerns.keeptogether + local fillup = false + while start do + -- faster to test for attr first + local attr = force or start[attribute] + if attr and attr > 0 then + start[attribute] = unsetvalue + local krn = mapping[attr] + if krn == v_max then + 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 c then + if keepligature and keepligature(start) then + -- keep 'm + else + c = do_process(namespace,attribute,c,attr) + local s = start + local p, n = s.prev, s.next + local tail = find_node_tail(c) + if p then + p.next = c + c.prev = p + else + head = c + end + if n then + n.prev = tail + end + tail.next = n + start = c + s.components = nil + -- we now leak nodes ! + -- free_node(s) + done = true + end + 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 pre, post, replace = disc.pre, disc.post, disc.replace + local prv, nxt = disc.prev, disc.next + 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(namespace,attribute,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(namespace,attribute,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(namespace,attribute,before,attr) + replace = replace.next + replace.prev = nil + after.prev.next = nil + disc.replace = replace + free_node(after) + free_node(before) + else + 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 + disc.replace = kern_injector(false,krn) -- only kerns permitted, no glue + end + 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) + done = true + end + 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)) + 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)) + done = true + end + elseif id == math_code then + start = end_of_math(start) + end + end + end + if start then + start = start.next + end + end + return head, done +end + +local enabled = false + +function kerns.set(factor) + if factor ~= v_max then + factor = tonumber(factor) or 0 + end + if factor == v_max or factor ~= 0 then + if not enabled then + tasks.enableaction("processors","typesetters.kerns.handler") + enabled = true + end + local a = factors[factor] + if not a then + a = #mapping + 1 + factors[factors], mapping[a] = a, factor + end + factor = a + else + factor = unsetvalue + end + texattribute[a_kerns] = factor + return factor +end + +local function process(namespace,attribute,head) + return do_process(namespace,attribute,head) -- no direct map, because else fourth argument is tail == true +end + +kerns.handler = nodes.installattributehandler { + name = "kern", + namespace = kerns, + processor = process, +} + +-- interface + +commands.setcharacterkerning = kerns.set -- cgit v1.2.3