if not modules then modules = { } end modules ["extralibs"] = { version = "2.3a", comment = "companion to luaotfload.lua", author = "Hans Hagen, Philipp Gesang", copyright = "PRAGMA ADE / ConTeXt Development Team", license = "GPL v.2.0", } -- extralibs: set up an emulation layer to load additional Context -- libraries --===================================================================-- --- PREPARE --===================================================================-- local getmetatable = getmetatable local require = require local select = select local setmetatable = setmetatable local tonumber = tonumber local texattribute = tex.attribute local new_node = node.new local copy_node = node.copy local otffeatures = fonts.constructors.newfeatures "otf" ----------------------------------------------------------------------- --- namespace ----------------------------------------------------------------------- --- The “typesetters” namespace isn’t bad at all; there is no need --- to remove it after loading. 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 kern_callback = "typesetters.kerncharacters" typesetters.kernfont = typesetters.kernfont or { } local kernfont = typesetters.kernfont ----------------------------------------------------------------------- --- node-ini ----------------------------------------------------------------------- nodes = nodes or { } --- should be present with luaotfload local bothways = function (t) return table.swapped (t, t) end local kerncodes = bothways({ [0] = "fontkern", [1] = "userkern", [2] = "accentkern", }) kerncodes.kerning = kerncodes.fontkern --- idiosyncrasy nodes.kerncodes = kerncodes nodes.skipcodes = bothways({ [ 0] = "userskip", [ 1] = "lineskip", [ 2] = "baselineskip", [ 3] = "parskip", [ 4] = "abovedisplayskip", [ 5] = "belowdisplayskip", [ 6] = "abovedisplayshortskip", [ 7] = "belowdisplayshortskip", [ 8] = "leftskip", [ 9] = "rightskip", [ 10] = "topskip", [ 11] = "splittopskip", [ 12] = "tabskip", [ 13] = "spaceskip", [ 14] = "xspaceskip", [ 15] = "parfillskip", [ 16] = "thinmuskip", [ 17] = "medmuskip", [ 18] = "thickmuskip", [100] = "leaders", [101] = "cleaders", [102] = "xleaders", [103] = "gleaders", }) ----------------------------------------------------------------------- --- node-res ----------------------------------------------------------------------- nodes.pool = nodes.pool or { } local pool = nodes.pool local kern = new_node("kern", nodes.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.gluespec = function (width, stretch, shrink, stretch_order, shrink_order) 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 return s 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 markdata = fonthashes.marks 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 if not markdata then markdata = { } setmetatableindex(markdata, function(t, k) if k == true then return marks[currentfont()] else local resources = { } if identifiers[k] then resources = identifiers[k].resources or { } end local marks = resources.marks or { } t[k] = marks return marks end end) fonthashes.marks = markdata end --- next stems from the multilingual interface interfaces = interfaces or { } interfaces.variables = interfaces.variables or { } interfaces.variables.max = "max" ----------------------------------------------------------------------- --- attr-ini ----------------------------------------------------------------------- attributes = attributes or { } --- to be removed with cleanup local hidden = { a_kerns = luatexbase.new_attribute("typo-krn:a_kerns", true), a_fontkern = luatexbase.new_attribute("typo-krn:a_fontkern", true), } attributes.private = attributes.private or function (attr_name) local res = hidden[attr_name] if not res then res = luatexbase.new_attribute(attr_name) end return res end if luatexbase.get_unset_value then attributes.unsetvalue = luatexbase.get_unset_value() else -- old luatexbase attributes.unsetvalue = (luatexbase.luatexversion < 37) and -1 or -2147483647 end ----------------------------------------------------------------------- --- luat-sto ----------------------------------------------------------------------- --- Storage is so ridiculously well designed in Context it’s a pity --- we can’t just force every package author to use it. storage = storage or { } storage.register = storage.register or function (...) local t = { ... } --- sorry return t end ----------------------------------------------------------------------- --- node-fin ----------------------------------------------------------------------- local plugin_store = { } local installattributehandler = function (plugin) --- Context has some load() magic here. plugin_store[plugin.name] = plugin.processor end nodes.installattributehandler = installattributehandler ----------------------------------------------------------------------- --- node-tsk ----------------------------------------------------------------------- nodes.tasks = nodes.tasks or { } nodes.tasks.enableaction = function () end ----------------------------------------------------------------------- --- core-ctx ----------------------------------------------------------------------- commands = commands or { } --===================================================================-- --- LOAD --===================================================================-- --- we should be ready at this moment to insert the libraries require "luaotfload-typo-krn" --- typesetters.kerns require "luaotfload-letterspace" --- typesetters.kernfont --===================================================================-- --- CLEAN --===================================================================-- --- interface ----------------------------------------------------------------------- local factors = kerns.factors local mapping = kerns.mapping local unsetvalue = attributes.unset_value local process_kerns = plugin_store.kern --- kern_callback : normal --- · callback: process_kerns --- · enabler: enablecharacterkerning --- · disabler: disablecharacterkerning --- · interface: kerns.set --- kernfont_callback : fontwise --- · callback: kernfont.handler --- · 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 --- we use the same callbacks as a node processor in Context --- unit -> bool local enablecharacterkerning = function ( ) return add_processor(function (head) return process_kerns("kerns", hidden.a_kerns, head) end, "typesetters.kerncharacters", "pre_linebreak_filter", "hpack_filter" ) end --- unit -> bool local disablecharacterkerning = function ( ) return remove_processor "typesetters.kerncharacters" end kerns.enablecharacterkerning = enablecharacterkerning kerns.disablecharacterkerning = disablecharacterkerning --- now for the simplistic variant --- unit -> bool local enablefontkerning = function ( ) return add_processor( kernfont.handler , "typesetters.kernfont" , "pre_linebreak_filter" , "hpack_filter") end --- unit -> bool local disablefontkerning = function ( ) return remove_processor "typesetters.kernfont" end --- fontwise kerning uses a font property for passing along the --- letterspacing factor 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 = "letterspace", --"kerncharacters", description = "letterspace", --"kerncharacters", initializers = { base = initializefontkerning, node = initializefontkerning, } } kerns.set = nil local characterkerning_enabled = false kerns.set = function (factor) if factor ~= "max" then factor = tonumber(factor) or 0 end if factor == "max" or factor ~= 0 then if not characterkerning_enabled then enablecharacterkerning() characterkerning_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[hidden.a_kerns] = factor return factor end ----------------------------------------------------------------------- --- options ----------------------------------------------------------------------- kerns .keepligature = false --- supposed to be of type function kerns .keeptogether = false --- supposed to be of type function kernfont.keepligature = false --- supposed to be of type function kernfont.keeptogether = false --- supposed to be of type function ----------------------------------------------------------------------- --- erase fake Context layer ----------------------------------------------------------------------- attributes = nil --commands = nil --- used in lualibs storage = nil --- not to confuse with utilities.storage nodes.tasks = nil collectgarbage"collect" --[[example-- \input luaotfload.sty \def\setcharacterkerning#1{% #1 factor : float \directlua{typesetters.kerns.set(0.618)}% } %directlua{typesetters.kerns.enablecharacterkerning()} \font\iwona = "name:Iwona:mode=node" at 42pt \font\lmregular = "name:Latin Modern Roman:mode=node" at 42pt {\iwona foo {\setcharacterkerning{0.618}% bar} baz} {\lmregular foo {\setcharacterkerning{0.125}ff fi ffi fl Th} baz} {\lmregular \directlua{ %% I’m not exactly sure how those work typesetters.kerns.keepligature = function (start) print("[liga]", start) return true end typesetters.kerns.keeptogether = function (start) print("[keeptogether]", start) return true end}% foo {\setcharacterkerning{0.125}ff fi ffi fl Th} baz} \bye --example]]-- -- vim:ts=2:sw=2:expandtab