diff options
Diffstat (limited to 'tex/context/base/typo-krn.lua')
-rw-r--r-- | tex/context/base/typo-krn.lua | 670 |
1 files changed, 335 insertions, 335 deletions
diff --git a/tex/context/base/typo-krn.lua b/tex/context/base/typo-krn.lua index fb28d3b2d..eac876262 100644 --- a/tex/context/base/typo-krn.lua +++ b/tex/context/base/typo-krn.lua @@ -1,335 +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 +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
|