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.lua223
1 files changed, 223 insertions, 0 deletions
diff --git a/tex/context/base/typo-krn.lua b/tex/context/base/typo-krn.lua
new file mode 100644
index 000000000..746773e0a
--- /dev/null
+++ b/tex/context/base/typo-krn.lua
@@ -0,0 +1,223 @@
+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 utf = unicode.utf8
+
+local next, type = next, type
+local utfchar = utf.char
+
+local has_attribute = node.has_attribute
+local unset_attribute = node.unset_attribute
+local find_node_tail = node.tail or node.slide
+local free_node = node.free
+local copy_node = node.copy
+local insert_node_before = node.insert_before
+local insert_node_after = node.insert_after
+local make_glue_spec = nodes.glue_spec
+local make_kern_node = nodes.kern
+
+local glyph = node.id("glyph")
+local kern = node.id("kern")
+local disc = node.id('disc')
+local glue = node.id('glue')
+local hlist = node.id('hlist')
+local vlist = node.id('vlist')
+
+local fontdata = fonts.identifiers
+local chardata = fonts.characters
+local quaddata = fonts.quads
+
+kerns = kerns or { }
+kerns.mapping = kerns.mapping or { }
+kerns.factors = kerns.factors or { }
+kerns.attribute = attributes.private("kern")
+
+storage.register("kerns/mapping", kerns.mapping, "kerns.mapping")
+storage.register("kerns/factors", kerns.factors, "kerns.factors")
+
+local mapping = kerns.mapping
+local factors = kerns.factors
+
+function kerns.setspacing(factor)
+ local a = factors[factor]
+ if not a then
+ a = #mapping + 1
+ factors[factors], mapping[a] = a, factor
+ end
+ tex.attribute[kerns.attribute] = a
+ return a
+end
+
+-- 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 function process(namespace,attribute,head,force)
+ local scale = tex.scale -- will go
+ local start, done, lastfont = head, false, nil
+ while start do
+ -- faster to test for attr first
+ local attr = force or has_attribute(start,attribute)
+ if attr and attr > 0 then
+ unset_attribute(start,attribute)
+ local krn = mapping[attr]
+ if krn and krn ~= 0 then
+ local id = start.id
+ if id == glyph then
+ lastfont = start.font
+ local c = start.components
+ if c then
+ local s = start
+ local tail = find_node_tail(c)
+ if s.prev then
+ s.prev.next = c
+ c.prev = s.prev
+ else
+ head = c
+ end
+ if s.next then
+ s.next.prev = tail
+ end
+ tail.next = s.next
+ start = c
+ start.attr = s.attr
+ s.attr = nil
+ s.components = nil
+ free_node(s)
+ done = true
+ end
+ local prev = start.prev
+ if prev then
+ local pid = prev.id
+ if not pid then
+ -- nothing
+ elseif pid == kern and prev.subtype == 0 then
+ prev.subtype = 1
+ prev.kern = prev.kern + scale(quaddata[lastfont],krn)
+ done = true
+ elseif pid == glyph then
+ if prev.font == lastfont then
+ local prevchar, lastchar = prev.char, start.char
+ local kerns = chardata[lastfont][prevchar].kerns
+ local kern = kerns and kerns[lastchar] or 0
+ krn = scale(kern+quaddata[lastfont],krn)
+ else
+ krn = scale(quaddata[lastfont],krn)
+ end
+ insert_node_before(head,start,make_kern_node(krn))
+ done = true
+ elseif pid == disc 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 = 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 = 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 = 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 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 = scale(kern+quaddata[lastfont],krn)
+ else
+ krn = scale(quaddata[lastfont],krn)
+ end
+ disc.replace = make_kern_node(krn)
+ end
+ end
+ end
+ elseif id == glue and start.subtype == 0 then
+ local s = start.spec
+ local w = s.width
+ if w > 0 then
+ local width, stretch, shrink = w+2*scale(w,krn), s.stretch, s.shrink
+ start.spec = make_glue_spec(width,scale(stretch,width/w),scale(shrink,width/w))
+ -- local width, stretch, shrink = w+2*w*krn, s.stretch, s.shrink
+ -- start.spec = make_glue_spec(width,stretch*width/w,shrink*width/w))
+ done = true
+ end
+ elseif false and id == kern and start.subtype == 0 then -- handle with glyphs
+ local sk = start.kern
+ if sk > 0 then
+ -- start.kern = scale(sk,krn)
+ start.kern = sk*krn
+ done = true
+ end
+ elseif lastfont and (id == hlist or id == vlist) then -- todo: lookahead
+ if start.prev then
+ insert_node_before(head,start,make_kern_node(scale(quaddata[lastfont],krn)))
+ done = true
+ end
+ if start.next then
+ insert_node_after(head,start,make_kern_node(scale(quaddata[lastfont],krn)))
+ done = true
+ end
+ end
+ end
+ end
+ if start then
+ start = start.next
+ end
+ end
+ return head, done
+end
+
+kerns.process = function(namespace,attribute,head)
+ return process(namespace,attribute,head) -- no direct map, because else fourth argument is tail == true
+end
+
+lists.handle_kerning = nodes.install_attribute_handler {
+ name = "kern",
+ namespace = kerns,
+ processor = kerns.process,
+}
+
+function kerns.enable()
+ tasks.enableaction("processors","lists.handle_kerning")
+end