diff options
Diffstat (limited to 'luaotfload-letterspace.lua')
-rw-r--r-- | luaotfload-letterspace.lua | 544 |
1 files changed, 0 insertions, 544 deletions
diff --git a/luaotfload-letterspace.lua b/luaotfload-letterspace.lua deleted file mode 100644 index 20f29f5..0000000 --- a/luaotfload-letterspace.lua +++ /dev/null @@ -1,544 +0,0 @@ -if not modules then modules = { } end modules ['letterspace'] = { - version = "2.5", - comment = "companion to luaotfload-main.lua", - author = "Hans Hagen, PRAGMA-ADE, Hasselt NL; adapted by Philipp Gesang", - copyright = "PRAGMA ADE / ConTeXt Development Team", - license = "see context related readme files" -} - -local log = luaotfload.log -local report = log.report - -local getmetatable = getmetatable -local require = require -local setmetatable = setmetatable -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 - -local new_kern = nodepool.kern -local new_glue = nodepool.glue - -local nodecodes = nodes.nodecodes - -local glyph_code = nodecodes.glyph -local kern_code = nodecodes.kern -local disc_code = nodecodes.disc -local math_code = nodecodes.math - -local fonthashes = fonts.hashes -local chardata = fonthashes.characters -local quaddata = fonthashes.quads -local otffeatures = fonts.constructors.newfeatures "otf" - ---[[doc-- - - Since the letterspacing method was derived initially from Context’s - typo-krn.lua we keep the sub-namespace “letterspace” inside the - “luaotfload” table. - ---doc]]-- - -luaotfload.letterspace = luaotfload.letterspace or { } -local letterspace = luaotfload.letterspace - -letterspace.keepligature = false -letterspace.keeptogether = false - ----=================================================================--- ---- preliminary definitions ----=================================================================--- --- We set up a layer emulating some Context internals that are needed --- for the letterspacing callback. ------------------------------------------------------------------------ ---- node-ini ------------------------------------------------------------------------ - -local bothways = function (t) return table.swapped (t, t) end -local kerncodes = bothways { [0] = "fontkern" - , [1] = "userkern" - , [2] = "accentkern" - } - -kerncodes.kerning = kerncodes.fontkern --- idiosyncrasy -local kerning_code = kerncodes.kerning -local userkern_code = kerncodes.userkern - - ------------------------------------------------------------------------ ---- 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 -end - -pool.glue = function (width, stretch, shrink, - stretch_order, shrink_order) - local n = new_node"glue" - if not width then - -- 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 - else - -- shared - n.spec = copy_node(width) - end - return n -end - ------------------------------------------------------------------------ ---- font-hsh ------------------------------------------------------------------------ ---- some initialization resembling font-hsh -local fonthashes = fonts.hashes -local identifiers = fonthashes.identifiers --- was: fontdata -local chardata = fonthashes.characters -local quaddata = fonthashes.quads -local parameters = fonthashes.parameters - ---- ('a, 'a) hash -> (('a, 'a) hash -> 'a -> 'a) -> ('a, 'a) hash -local setmetatableindex = function (t, f) - local mt = getmetatable(t) - if mt then - mt.__index = f - else - setmetatable(t, { __index = f }) - end - return t -end - -if not parameters then - parameters = { } - setmetatableindex(parameters, function(t, k) - if k == true then - return parameters[currentfont()] - else - local parameters = identifiers[k].parameters - t[k] = parameters - return parameters - end - end) - --fonthashes.parameters = parameters -end - -if not chardata then - chardata = { } - setmetatableindex(chardata, function(t, k) - if k == true then - return chardata[currentfont()] - else - local tfmdata = identifiers[k] - if not tfmdata then --- unsafe - tfmdata = font.fonts[k] - end - if tfmdata then - local characters = tfmdata.characters - t[k] = characters - return characters - end - end - end) - fonthashes.characters = chardata -end - -if not quaddata then - quaddata = { } - setmetatableindex(quaddata, function(t, k) - if k == true then - return quads[currentfont()] - else - local parameters = parameters[k] - local quad = parameters and parameters.quad or 0 - t[k] = quad - return quad - end - end) - --fonthashes.quads = quaddata -end - ----=================================================================--- ---- character kerning functionality ----=================================================================--- - -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 - return g - else - return new_kern(kern) - end -end - ---[[doc-- - - Caveat lector. - This is an adaptation of the Context character kerning mechanism - that emulates XeTeX-style fontwise letterspacing. Note that in its - present state it is far inferior to the original, which is - attribute-based and ignores font-boundaries. Nevertheless, due to - popular demand the following callback has been added. - ---doc]]-- - -local kernfactors = { } --- fontid -> factor - -local kerncharacters -kerncharacters = function (head) - local start, done = head, false - local lastfont = nil - local keepligature = letterspace.keepligature --- function - local keeptogether = letterspace.keeptogether --- function - local fillup = false - - local identifiers = fonthashes.identifiers - local kernfactors = kernfactors - - local firstkern = true - - while start do - local id = start.id - if id == glyph_code then - - --- 1) look up kern factor (slow, but cached rudimentarily) - local krn - local fontid = start.font - do - krn = kernfactors[fontid] - if not krn then - local tfmdata = identifiers[fontid] - if not tfmdata then -- unsafe - tfmdata = font.fonts[fontid] - end - if tfmdata then - fontproperties = tfmdata.properties - if fontproperties then - krn = fontproperties.kerncharacters - end - end - kernfactors[fontid] = krn - end - if not krn or krn == 0 then - firstkern = true - goto nextnode - elseif firstkern then - firstkern = false - if (id ~= disc_code) and (not start.components) then - --- not a ligature, skip node - goto nextnode - end - end - end - - if krn == "max" then - krn = .25 - fillup = true - else - fillup = false - end - - lastfont = fontid - - --- 2) resolve ligatures - local c = start.components - - if c then - if keepligature and keepligature(start) then - -- keep 'm - else - --- c = kerncharacters (c) --> taken care of after replacing - 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 -- kern ligature - - --- 3) apply the extra kerning - local prev = start.prev - if prev then - local pid = prev.id - - if not pid then - -- nothing - - 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 - then - if keeptogether and prev.prev.id == glyph_code and keeptogether(prev.prev,start) then - -- keep - else - 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 - elseif identifiers[lastfont] then - 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 = kerncharacters (before) - 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 = kerncharacters (post) - 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 = kerncharacters (before) - replace = replace.next - replace.prev = nil - after.prev.next = nil - 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 - 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 - - ::nextnode:: - if start then - start = start.next - end - end - return head, done -end - ----=================================================================--- ---- integration ----=================================================================--- - ---- · callback: kerncharacters ---- · enabler: enablefontkerning ---- · disabler: disablefontkerning - ---- callback wrappers - ---- (node_t -> node_t) -> string -> string list -> bool -local registered_as = { } --- procname -> callbacks -local add_processor = function (processor, name, ...) - local callbacks = { ... } - for i=1, #callbacks do - luatexbase.add_to_callback(callbacks[i], processor, name) - end - registered_as[name] = callbacks --- for removal - return true -end - ---- string -> bool -local remove_processor = function (name) - local callbacks = registered_as[name] - if callbacks then - for i=1, #callbacks do - luatexbase.remove_from_callback(callbacks[i], name) - end - return true - end - return false --> unregistered -end - ---- now for the simplistic variant ---- unit -> bool -local enablefontkerning = function ( ) - return add_processor( kerncharacters - , "luaotfload.letterspace" - , "pre_linebreak_filter" - , "hpack_filter") -end - ---- unit -> bool -local disablefontkerning = function ( ) - return remove_processor "luaotfload.letterspace" -end - ---[[doc-- - - Fontwise kerning is enabled via the “kernfactor” option at font - definition time. Unlike the Context implementation which relies on - Luatex attributes, it uses a font property for passing along the - letterspacing factor of a node. - - The callback is activated the first time a letterspaced font is - requested and stays active until the end of the run. Since the font - is a property of individual glyphs, every glyph in the entire - document must be checked for the kern property. This is quite - inefficient compared to Context’s attribute based approach, but Xetex - compatibility reduces our options significantly. - ---doc]]-- - - -local fontkerning_enabled = false --- callback state - ---- fontobj -> float -> unit -local initializefontkerning = function (tfmdata, factor) - if factor ~= "max" then - factor = tonumber (factor) or 0 - end - if factor == "max" or factor ~= 0 then - local fontproperties = tfmdata.properties - if fontproperties then - --- hopefully this field stays unused otherwise - fontproperties.kerncharacters = factor - end - if not fontkerning_enabled then - fontkerning_enabled = enablefontkerning () - end - end -end - ---- like the font colorization, fontwise kerning is hooked into the ---- feature mechanism - -otffeatures.register { - name = "kernfactor", - description = "kernfactor", - initializers = { - base = initializefontkerning, - node = initializefontkerning, - } -} - ---[[doc-- - - The “letterspace” feature is essentially identical with the above - “kernfactor” method, but scales the factor to percentages to match - Xetex’s behavior. (See the Xetex reference, page 5, section 1.2.2.) - - Since Xetex doesn’t appear to have a (documented) “max” keyword, we - assume all input values are numeric. - ---doc]]-- - -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)) - return - end - return initializefontkerning (tfmdata, factor * 0.01) -end - -otffeatures.register { - name = "letterspace", - description = "letterspace", - initializers = { - base = initializecompatfontkerning, - node = initializecompatfontkerning, - } -} - ---[[example-- - -See https://bitbucket.org/phg/lua-la-tex-tests/src/tip/pln-letterspace-8-compare.tex -for an example. - ---example]]-- - ---- vim:sw=2:ts=2:expandtab:tw=71 - |