diff options
author | Karl Berry <karl@freefriends.org> | 2015-12-11 23:16:34 +0000 |
---|---|---|
committer | Karl Berry <karl@freefriends.org> | 2015-12-11 23:16:34 +0000 |
commit | 71945bfdf73e58a09c05155c6d49729528cac3f7 (patch) | |
tree | aa614024e79d15166a1aa500e223c748b1a66c83 /Master/texmf-dist/tex/luatex/luaotfload/luaotfload-letterspace.lua | |
parent | fa3dbdbd11bbc7da0c06e2871472419998e645c9 (diff) |
luaotfload (11dec15)
git-svn-id: svn://tug.org/texlive/trunk@39078 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist/tex/luatex/luaotfload/luaotfload-letterspace.lua')
-rw-r--r-- | Master/texmf-dist/tex/luatex/luaotfload/luaotfload-letterspace.lua | 299 |
1 files changed, 194 insertions, 105 deletions
diff --git a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-letterspace.lua b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-letterspace.lua index 20f29f53cb6..5fa25f9d343 100644 --- a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-letterspace.lua +++ b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-letterspace.lua @@ -6,8 +6,11 @@ if not modules then modules = { } end modules ['letterspace'] = { license = "see context related readme files" } +--- This code diverged quite a bit from its origin in Context. Please +--- do *not* report bugs on the Context list. + local log = luaotfload.log -local report = log.report +local logreport = log.report local getmetatable = getmetatable local require = require @@ -17,16 +20,28 @@ local tonumber = tonumber local next = next local nodes, node, fonts = nodes, node, fonts -local find_node_tail = node.tail or node.slide -local free_node = node.free -local copy_node = node.copy -local new_node = node.new -local insert_node_before = node.insert_before - -local nodepool = nodes.pool - +--- As of December 2014 the faster ``node.direct.*`` interface is +--- preferred. +local nodedirect = nodes.nuts +local getchar = nodedirect.getchar +local getfont = nodedirect.getfont +local getid = nodedirect.getid +local getnext = nodedirect.getnext +local getprev = nodedirect.getprev +local getfield = nodedirect.getfield +local setfield = nodedirect.setfield +local getsubtype = nodedirect.getsubtype +local find_node_tail = nodedirect.tail +local todirect = nodedirect.tonut +local tonode = nodedirect.tonode + +local insert_node_before = nodedirect.insert_before +local free_node = nodedirect.free +local copy_node = nodedirect.copy +local new_node = nodedirect.new + +local nodepool = nodedirect.pool local new_kern = nodepool.kern -local new_glue = nodepool.glue local nodecodes = nodes.nodecodes @@ -34,6 +49,7 @@ local glyph_code = nodecodes.glyph local kern_code = nodecodes.kern local disc_code = nodecodes.disc local math_code = nodecodes.math +local glue_code = nodecodes.glue local fonthashes = fonts.hashes local chardata = fonthashes.characters @@ -68,44 +84,51 @@ local kerncodes = bothways { [0] = "fontkern" , [1] = "userkern" , [2] = "accentkern" } +local skipcodes = bothways { [0] = "userskip" + , [13] = "spaceskip" + , [14] = "xspaceskip" + } -kerncodes.kerning = kerncodes.fontkern --- idiosyncrasy -local kerning_code = kerncodes.kerning -local userkern_code = kerncodes.userkern - +kerncodes.kerning = kerncodes.fontkern --- idiosyncrasy +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 ----------------------------------------------------------------------- --- node-res ----------------------------------------------------------------------- -nodes.pool = nodes.pool or { } -local pool = nodes.pool - -local kern = new_node ("kern", kerncodes.userkern) local glue_spec = new_node "glue_spec" -pool.kern = function (k) - local n = copy_node (kern) - n.kern = k - return n +local new_gluespec = function (width, + stretch, shrink, + stretch_order, shrink_order) + local spec = copy_node(glue_spec) + if width then setfield(spec, "width" , width ) end + if stretch then setfield(spec, "stretch" , stretch ) end + if shrink then setfield(spec, "shrink" , shrink ) end + if stretch_order then setfield(spec, "stretch_order", stretch_order) end + if shrink_order then setfield(spec, "shrink_order" , shrink_order ) end + return spec end -pool.glue = function (width, stretch, shrink, - stretch_order, shrink_order) - local n = new_node"glue" - if not width then +local new_glue = function (width, stretch, shrink, + stretch_order, shrink_order) + local n = new_node "glue" + if not width then return n end -- no spec - elseif width == false or tonumber(width) then - local s = copy_node(glue_spec) - if width then s.width = width end - if stretch then s.stretch = stretch end - if shrink then s.shrink = shrink end - if stretch_order then s.stretch_order = stretch_order end - if shrink_order then s.shrink_order = shrink_order end - n.spec = s + if width == false then + local width = tonumber(width) + if width then + setfield(n, "spec", + new_gluespec(width, stretch, shrink, + stretch_order, shrink_order)) + end else -- shared - n.spec = copy_node(width) + setfield(n, "spec", copy_node(width)) end return n end @@ -187,13 +210,28 @@ end local kern_injector = function (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) end + return new_kern(kern) +end + +local kernable_skip = function (n) + local st = getsubtype (n) + return st == userskip_code + or st == spaceskip_code + or st == xspaceskip_code +end + +local function spec_injector (fillup, width, stretch, shrink) + if fillup then + local spec = new_gluespec(width, 2 * stretch, 2 * shrink) + setfield(spec, "stretch_order", 1) + return spec + end + return new_gluespec(width,stretch,shrink) end --[[doc-- @@ -219,16 +257,14 @@ kerncharacters = function (head) local identifiers = fonthashes.identifiers local kernfactors = kernfactors - local firstkern = true while start do - local id = start.id + local id = getid(start) if id == glyph_code then - --- 1) look up kern factor (slow, but cached rudimentarily) local krn - local fontid = start.font + local fontid = getfont(start) do krn = kernfactors[fontid] if not krn then @@ -249,7 +285,7 @@ kerncharacters = function (head) goto nextnode elseif firstkern then firstkern = false - if (id ~= disc_code) and (not start.components) then + if (id ~= disc_code) and (not getfield(start, "components")) then --- not a ligature, skip node goto nextnode end @@ -266,7 +302,7 @@ kerncharacters = function (head) lastfont = fontid --- 2) resolve ligatures - local c = start.components + local c = getfield(start, "components") if c then if keepligature and keepligature(start) then @@ -274,20 +310,20 @@ kerncharacters = function (head) else --- c = kerncharacters (c) --> taken care of after replacing local s = start - local p, n = s.prev, s.next + local p, n = getprev(s), s.next local tail = find_node_tail(c) if p then - p.next = c - c.prev = p + setfield(p, "next", c) + p = getprev(c) else head = c end if n then - n.prev = tail + tail = getprev(n) end - tail.next = n + setnext(tail, "next", n) start = c - s.components = nil + setfield(s, "components", nil) -- we now leak nodes ! -- free_node(s) done = true @@ -295,33 +331,58 @@ kerncharacters = function (head) end -- kern ligature --- 3) apply the extra kerning - local prev = start.prev + local prev = getprev(start) if prev then - local pid = prev.id + local pid = getid(prev) if not pid then -- nothing + elseif pid == glue_code and kernable_skip(prev) then + local spec = getfield(prev, "spec") + local wd = getfield(spec, "width") + if wd > 0 then + --- formula taken from Context + --- existing_width extended by four times the + --- width times the font’s kernfactor + local newwd = wd + --[[two en to a quad]] 4 * wd * krn + local stretched = (getfield(spec,"stretch") * newwd) / wd + local shrunk = (getfield(spec,"shrink") * newwd) / wd + setfield(prev, "spec", + spec_injector(fillup, newwd, stretched, shrunk)) + done = true + end + elseif pid == kern_code then - if prev.subtype == kerning_code --- context does this by means of an - or prev.subtype == userkern_code --- attribute; we may need a test + local prev_subtype = getsubtype(prev) + if prev_subtype == kerning_code --- context does this by means of an + or prev_subtype == userkern_code --- attribute; we may need a test then - if keeptogether and prev.prev.id == glyph_code and keeptogether(prev.prev,start) then + + local pprev = getprev(prev) + local pprev_id = getid(pprev) + + if keeptogether + and pprev_id == glyph_code + and keeptogether(pprev, start) + then -- keep else - prev.subtype = userkern_code - prev.kern = prev.kern + quaddata[lastfont]*krn -- here + prev_subtype = userkern_code + local prev_kern = getfield(prev, "kern") + prev_kern = prev_kern + quaddata[lastfont] * krn 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 + if getfont(prev) == lastfont then + local prevchar = getchar(prev) + local lastchar = getchar(start) + if keeptogether and keeptogether(prev, start) then -- keep 'm elseif identifiers[lastfont] then - local kerns = chardata[lastfont][prevchar].kerns + local kerns = chardata[lastfont] and 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)) @@ -337,31 +398,34 @@ kerncharacters = function (head) -- 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 + local pre = getfield(disc, "pre") + local post = getfield(disc, "post") + local replace = getfield(disc, "replace") + local prv = getprev(disc) + local nxt = getnext(disc) 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 + setfield(pre, "prev", before) + setfield(before, "next", pre) + setfield(before, "prev", nil) pre = kerncharacters (before) - pre = pre.next - pre.prev = nil - disc.pre = pre + pre = getnext(pre) + setfield(pre, "prev", nil) + setfield(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 + setfield(tail, "next", after) + setfield(after, "prev", tail) + setfield(after, "next", nil) post = kerncharacters (post) - tail.next = nil - disc.post = post + setfield(tail, "next", nil) + setfield(disc, "post", post) free_node(after) end @@ -369,38 +433,42 @@ kerncharacters = function (head) 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 + setfield(replace, "prev", before) + setfield(before, "next", replace) + setfield(before, "prev", nil) + setfield(tail, "next", after) + setfield(after, "prev", tail) + setfield(after, "next", nil) replace = kerncharacters (before) - replace = replace.next - replace.prev = nil - after.prev.next = nil - disc.replace = replace + replace = getnext(replace) + setfield(replace, "prev", nil) + setfield(after, "prev.next", nil) + setfield(disc, "replace", replace) free_node(after) free_node(before) + elseif identifiers[lastfont] then - 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 + if prv + and getid(prv) == glyph_code + and getfont(prv) == lastfont + then + local prevchar = getchar(prv) + local lastchar = getchar(start) + local kerns = chardata[lastfont] and 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 - end + setfield(disc, "replace", kern_injector(false, krn)) + end --[[if replace and prv and nxt]] + end --[[if not pid]] + end --[[if prev]] + end --[[if id == glyph_code]] ::nextnode:: if start then - start = start.next + start = getnext(start) end end return head, done @@ -439,19 +507,40 @@ local remove_processor = function (name) return false --> unregistered end ---- now for the simplistic variant +--- When font kerning is requested, usually by defining a font with the +--- ``letterspace`` parameter, we inject a wrapper for the +--- ``kerncharacters()`` node processor in the relevant callbacks. This +--- wrapper initially converts the received head node into its “direct” +--- counterpart. Likewise, the callback result is converted back to an +--- ordinary node prior to returning. Internally, ``kerncharacters()`` +--- performs all node operations on direct nodes. + --- unit -> bool local enablefontkerning = function ( ) - return add_processor( kerncharacters + + local handler = function (hd) + local direct_hd = todirect (hd) + logreport ("term", 5, "letterspace", + "kerncharacters() invoked with node.direct interface \z + (``%s`` -> ``%s``)", tostring (hd), tostring (direct_hd)) + local direct_hd, _done = kerncharacters (direct_hd) + if not direct_hd then --- bad + logreport ("both", 0, "letterspace", + "kerncharacters() failed to return a valid new head") + end + return tonode (direct_hd) + end + + return add_processor( handler , "luaotfload.letterspace" , "pre_linebreak_filter" , "hpack_filter") end --- unit -> bool -local disablefontkerning = function ( ) - return remove_processor "luaotfload.letterspace" -end +---al disablefontkerning = function ( ) +---eturn remove_processor "luaotfload.letterspace" +--- --[[doc-- @@ -515,10 +604,10 @@ otffeatures.register { local initializecompatfontkerning = function (tfmdata, percentage) local factor = tonumber (percentage) if not factor then - report ("both", 0, "letterspace", - "Invalid argument to letterspace: %s (type %q), " .. - "was expecting percentage as Lua number instead.", - percentage, type (percentage)) + logreport ("both", 0, "letterspace", + "Invalid argument to letterspace: %s (type %q), " .. + "was expecting percentage as Lua number instead.", + percentage, type (percentage)) return end return initializefontkerning (tfmdata, factor * 0.01) |