Commits

Philipp Gesang committed 18776d3

include typo-krn.lua

Comments (0)

Files changed (1)

+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