summaryrefslogtreecommitdiff
path: root/macros/luatex/generic/luaotfload/luaotfload-auxiliary.lua
diff options
context:
space:
mode:
Diffstat (limited to 'macros/luatex/generic/luaotfload/luaotfload-auxiliary.lua')
-rw-r--r--macros/luatex/generic/luaotfload/luaotfload-auxiliary.lua851
1 files changed, 851 insertions, 0 deletions
diff --git a/macros/luatex/generic/luaotfload/luaotfload-auxiliary.lua b/macros/luatex/generic/luaotfload/luaotfload-auxiliary.lua
new file mode 100644
index 0000000000..3e3283b5ff
--- /dev/null
+++ b/macros/luatex/generic/luaotfload/luaotfload-auxiliary.lua
@@ -0,0 +1,851 @@
+-----------------------------------------------------------------------
+-- FILE: luaotfload-auxiliary.lua
+-- DESCRIPTION: part of luaotfload
+-- AUTHOR: Khaled Hosny, Élie Roux, Philipp Gesang
+-----------------------------------------------------------------------
+
+local ProvidesLuaModule = {
+ name = "luaotfload-auxiliary",
+ version = "2.991", --TAGVERSION
+ date = "2019-08-11", --TAGDATE
+ description = "luaotfload submodule / auxiliary functions",
+ license = "GPL v2.0"
+}
+
+if luatexbase and luatexbase.provides_module then
+ luatexbase.provides_module (ProvidesLuaModule)
+end
+
+
+luaotfload = luaotfload or { }
+local log = luaotfload.log
+local logreport = log.report
+local fonthashes = fonts.hashes
+local encodings = fonts.encodings
+local identifiers = fonthashes.identifiers
+local fontnames = fonts.names
+
+local fontid = font.id
+local texsprint = tex.sprint
+
+local dofile = dofile
+local getmetatable = getmetatable
+local setmetatable = setmetatable
+local utfcodepoint = utf8.codepoint
+local stringlower = string.lower
+local stringformat = string.format
+local stringgsub = string.gsub
+local stringbyte = string.byte
+local stringfind = string.find
+local tablecopy = table.copy
+
+local aux = { }
+local luaotfload_callbacks = { }
+
+-----------------------------------------------------------------------
+--- font patches
+-----------------------------------------------------------------------
+
+--- https://github.com/khaledhosny/luaotfload/issues/54
+
+local function rewrite_fontname(tfmdata, specification)
+ local format = tfmdata.format or tfmdata.properties.format
+ if stringfind (specification, " ") then
+ tfmdata.name = stringformat ("%q", specification)
+ else
+ --- other specs should parse just fine
+ tfmdata.name = specification
+ end
+end
+
+local rewriting = false
+
+function aux.start_rewrite_fontname()
+ if rewriting == false then
+ luatexbase.add_to_callback (
+ "luaotfload.patch_font",
+ rewrite_fontname,
+ "luaotfload.rewrite_fontname")
+ rewriting = true
+ logreport ("log", 1, "aux",
+ "start rewriting tfmdata.name field")
+ end
+end
+
+function aux.stop_rewrite_fontname()
+ if rewriting == true then
+ luatexbase.remove_from_callback
+ ("luaotfload.patch_font", "luaotfload.rewrite_fontname")
+ rewriting = false
+ logreport ("log", 1, "aux",
+ "stop rewriting tfmdata.name field")
+ end
+end
+
+
+--[[doc--
+This sets two dimensions apparently relied upon by the unicode-math
+package.
+--doc]]--
+
+local function set_sscale_dimens(fontdata)
+ local resources = fontdata.resources if not resources then return end
+ local mathconstants = resources.MathConstants if not mathconstants then return end
+ local parameters = fontdata.parameters if not parameters then return end
+ --- the default values below are complete crap
+ parameters [10] = mathconstants.ScriptPercentScaleDown or 70
+ parameters [11] = mathconstants.ScriptScriptPercentScaleDown or 50
+end
+
+luaotfload_callbacks [#luaotfload_callbacks + 1] = {
+ "patch_font", set_sscale_dimens, "set_sscale_dimens",
+}
+
+local default_units = 1000
+
+--- fontobj -> int
+local function lookup_units(fontdata)
+ local units = fontdata.units
+ if units and units > 0 then return units end
+ local shared = fontdata.shared if not shared then return default_units end
+ local rawdata = shared.rawdata if not rawdata then return default_units end
+ local metadata = rawdata.metadata if not metadata then return default_units end
+ local capheight = metadata.capheight if not capheight then return default_units end
+ local units = metadata.units or fontdata.units
+ if not units or units == 0 then
+ return default_units
+ end
+ return units
+end
+
+--[[doc--
+This callback corrects some values of the Cambria font.
+--doc]]--
+--- fontobj -> unit
+local function patch_cambria_domh(fontdata)
+ local mathconstants = fontdata.MathConstants
+ if mathconstants and fontdata.psname == "CambriaMath" then
+ --- my test Cambria has 2048
+ local units = fontdata.units or lookup_units(fontdata)
+ local sz = fontdata.parameters.size or fontdata.size
+ local mh = 2800 / units * sz
+ if mathconstants.DisplayOperatorMinHeight < mh then
+ mathconstants.DisplayOperatorMinHeight = mh
+ end
+ end
+end
+
+luaotfload_callbacks [#luaotfload_callbacks + 1] = {
+ "patch_font", patch_cambria_domh, "patch_cambria_domh",
+}
+
+
+--[[doc--
+
+ Add missing field to fonts that lack it. Addresses issue
+ https://github.com/lualatex/luaotfload/issues/253
+
+ This is considered a hack, especially since importing the
+ unicode-math package fixes the problem quite nicely.
+
+--doc]]--
+
+--- fontobj -> unit
+local function fixup_fontdata(data)
+ local t = type (data)
+ --- Some OT fonts like Libertine R lack the resources table, causing
+ --- the fontloader to nil-index.
+ if t == "table" then
+ if data and not data.resources then data.resources = { } end
+ end
+end
+
+luaotfload_callbacks [#luaotfload_callbacks + 1] = {
+ "patch_font_unsafe", fixup_fontdata, "fixup_fontdata",
+}
+
+
+--[[doc--
+
+Comment from fontspec:
+
+ “Here we patch fonts tfm table to emulate \XeTeX's \cs{fontdimen8},
+ which stores the caps-height of the font. (Cf.\ \cs{fontdimen5} which
+ stores the x-height.)
+
+ Falls back to measuring the glyph if the font doesn't contain the
+ necessary information.
+ This needs to be extended for fonts that don't contain an `X'.”
+
+--doc]]--
+
+local capheight_reference_chars = { "X", "M", "Ж", "ξ", }
+local capheight_reference_codepoints do
+ capheight_reference_codepoints = { }
+ for i = 1, #capheight_reference_chars do
+ local chr = capheight_reference_chars [i]
+ capheight_reference_codepoints [i] = utfcodepoint (chr)
+ end
+end
+
+local function determine_capheight(fontdata)
+ local parameters = fontdata.parameters if not parameters then return false end
+ local characters = fontdata.characters if not characters then return false end
+ --- Pretty simplistic but it does return *some* value for most fonts;
+ --- we could also refine the approach to return some kind of average
+ --- of all capital letters or a user-provided subset.
+ for i = 1, #capheight_reference_codepoints do
+ local refcp = capheight_reference_codepoints [i]
+ local refchar = characters [refcp]
+ if refchar then
+ logreport ("both", 4, "aux",
+ "picked height of character ‘%s’ (U+%d) as \\fontdimen8 \z
+ candidate",
+ capheight_reference_chars [i], refcp)
+ return refchar.height
+ end
+ end
+ return false
+end
+
+local function query_ascender(fontdata)
+ local parameters = fontdata.parameters if not parameters then return false end
+ local ascender = parameters.ascender
+ if ascender then
+ return ascender --- pre-scaled
+ end
+
+ local shared = fontdata.shared if not shared then return false end
+ local rawdata = shared.rawdata if not rawdata then return false end
+ local metadata = rawdata.metadata if not metadata then return false end
+ ascender = metadata.ascender if not ascender then return false end
+ local size = parameters.size if not size then return false end
+ local units = lookup_units (fontdata)
+ if not units or units == 0 then return false end
+ return ascender * size / units --- scaled
+end
+
+local function query_capheight(fontdata)
+ local parameters = fontdata.parameters if not parameters then return false end
+ local shared = fontdata.shared if not shared then return false end
+ local rawdata = shared.rawdata if not rawdata then return false end
+ local metadata = rawdata.metadata if not metadata then return false end
+ local capheight = metadata.capheight if not capheight then return false end
+ local size = parameters.size if not size then return false end
+ local units = lookup_units (fontdata)
+ if not units or units == 0 then return false end
+ return capheight * size / units
+end
+
+local function query_fontdimen8(fontdata)
+ local parameters = fontdata.parameters if not parameters then return false end
+ local fontdimen8 = parameters [8]
+ if fontdimen8 then return fontdimen8 end
+ return false
+end
+
+local function caphtfmt(ref, ht)
+ if not ht then return "<none>" end
+ if not ref then return tostring (ht) end
+ return stringformat ("%s(δ=%s)", ht, ht - ref)
+end
+
+local function set_capheight(fontdata)
+ if not fontdata then
+ logreport ("both", 0, "aux",
+ "error: set_capheight() received garbage")
+ return
+ end
+ local capheight_dimen8 = query_fontdimen8 (fontdata)
+ local capheight_alleged = query_capheight (fontdata)
+ local capheight_ascender = query_ascender (fontdata)
+ local capheight_measured = determine_capheight (fontdata)
+ logreport ("term", 4, "aux",
+ "capht: param[8]=%s advertised=%s ascender=%s measured=%s",
+ tostring (capheight_dimen8),
+ caphtfmt (capheight_dimen8, capheight_alleged),
+ caphtfmt (capheight_dimen8, capheight_ascender),
+ caphtfmt (capheight_dimen8, capheight_measured))
+ if capheight_dimen8 then --- nothing to do
+ return
+ end
+
+ local capheight = capheight_alleged or capheight_ascender or capheight_measured
+ if capheight then
+ fontdata.parameters [8] = capheight
+ end
+end
+
+luaotfload_callbacks [#luaotfload_callbacks + 1] = {
+ "patch_font", set_capheight, "set_capheight",
+}
+
+-- Of course there are also fonts with no sensible x-height, so let's add a
+-- fallback there:
+local function set_xheight(tfmdata)
+ local parameters = tfmdata.parameters
+ if not parameters then return end
+ if not (parameters.x_height or parameters[5] or 0) == 0 then return end
+ if tfmdata.characters and tfmdata.characters[120] then
+ parameters.x_height = tfmdata.characters[120].height
+ else
+ parameters.x_height = (parameters.ascender or 0)/2
+ end
+end
+luaotfload_callbacks [#luaotfload_callbacks + 1] = {
+ "patch_font", set_xheight, "set_xheight",
+}
+-----------------------------------------------------------------------
+--- glyphs and characters
+-----------------------------------------------------------------------
+
+--- int -> int -> bool
+function aux.font_has_glyph(font_id, codepoint)
+ local fontdata = fonts.hashes.identifiers[font_id]
+ if fontdata then
+ if fontdata.characters[codepoint] ~= nil then return true end
+ end
+ return false
+end
+
+--- undocumented
+
+local function raw_slot_of_name(font_id, glyphname)
+ local fontdata = font.fonts[font_id]
+ if fontdata.type == "virtual" then --- get base font for glyph idx
+ local codepoint = encodings.agl.unicodes[glyphname]
+ local glyph = fontdata.characters[codepoint]
+ if fontdata.characters[codepoint] then
+ return codepoint
+ end
+ end
+ return false
+end
+
+--[[doc--
+
+ This one is approximately “name_to_slot” from the microtype package;
+ note that it is all about Adobe Glyph names and glyph slots in the
+ font. The names and values may diverge from actual Unicode.
+
+ http://www.adobe.com/devnet/opentype/archives/glyph.html
+
+ The “unsafe” switch triggers a fallback lookup in the raw fonts
+ table. As some of the information is stored as references, this may
+ have unpredictable side-effects.
+
+--doc]]--
+
+--- int -> string -> bool -> (int | false)
+function aux.slot_of_name(font_id, glyphname, unsafe)
+ if not font_id or type (font_id) ~= "number"
+ or not glyphname or type (glyphname) ~= "string"
+ then
+ logreport ("both", 0, "aux",
+ "invalid parameters to slot_of_name (%s, %s)",
+ tostring (font_id), tostring (glyphname))
+ return false
+ end
+
+ local tfmdata = identifiers [font_id]
+ if not tfmdata then return raw_slot_of_name (font_id, glyphname) end
+ local resources = tfmdata.resources if not resources then return false end
+ local unicodes = resources.unicodes if not unicodes then return false end
+
+ local unicode = unicodes [glyphname]
+ if unicode then
+ if type (unicode) == "number" then
+ return unicode
+ else
+ return unicode [1] --- for multiple components
+ end
+ end
+ return false
+end
+
+--[[doc--
+
+ Inverse of above; not authoritative as to my knowledge the official
+ inverse of the AGL is the AGLFN. Maybe this whole issue should be
+ dealt with in a separate package that loads char-def.lua and thereby
+ solves the problem for the next couple decades.
+
+ http://partners.adobe.com/public/developer/en/opentype/aglfn13.txt
+
+--doc]]--
+
+local indices
+
+--- int -> (string | false)
+function aux.name_of_slot(codepoint)
+ if not codepoint or type (codepoint) ~= "number" then
+ logreport ("both", 0, "aux",
+ "invalid parameters to name_of_slot (%s)",
+ tostring (codepoint))
+ return false
+ end
+
+ if not indices then --- this will load the glyph list
+ local unicodes = encodings.agl.unicodes
+ if not unicodes or not next (unicodes)then
+ logreport ("both", 0, "aux",
+ "name_of_slot: failed to load the AGL.")
+ end
+ indices = table.swapped (unicodes)
+ end
+
+ local glyphname = indices [codepoint]
+ if glyphname then
+ return glyphname
+ end
+ return false
+end
+
+-----------------------------------------------------------------------
+--- features / scripts / languages
+-----------------------------------------------------------------------
+--- lots of arrowcode ahead
+
+local function get_features(tfmdata)
+ local resources = tfmdata.resources if not resources then return false end
+ local features = resources.features if not features then return false end
+ return features
+end
+
+--[[doc--
+This function, modeled after “check_script()” from fontspec, returns
+true if in the given font, the script “asked_script” is accounted for in at
+least one feature.
+--doc]]--
+
+--- int -> string -> bool
+function aux.provides_script(font_id, asked_script)
+ if not font_id or type (font_id) ~= "number"
+ or not asked_script or type (asked_script) ~= "string"
+ then
+ logreport ("both", 0, "aux",
+ "invalid parameters to provides_script(%s, %s)",
+ tostring (font_id), tostring (asked_script))
+ return false
+ end
+ asked_script = stringlower(asked_script)
+ if font_id and font_id > 0 then
+ local tfmdata = identifiers[font_id]
+ if not tfmdata then return false end
+ local features = get_features (tfmdata)
+ if features == false then
+ logreport ("log", 1, "aux", "font no %d lacks a features table", font_id)
+ return false
+ end
+ for method, featuredata in next, features do
+ --- where method: "gpos" | "gsub"
+ for feature, data in next, featuredata do
+ if data[asked_script] then
+ logreport ("log", 1, "aux",
+ "font no %d (%s) defines feature %s for script %s",
+ font_id, fontname, feature, asked_script)
+ return true
+ end
+ end
+ end
+ logreport ("log", 0, "aux",
+ "font no %d (%s) defines no feature for script %s",
+ font_id, fontname, asked_script)
+ end
+ logreport ("log", 0, "aux", "no font with id %d", font_id)
+ return false
+end
+
+--[[doc--
+This function, modeled after “check_language()” from fontspec, returns
+true if in the given font, the language with tage “asked_language” is
+accounted for in the script with tag “asked_script” in at least one
+feature.
+--doc]]--
+
+--- int -> string -> string -> bool
+function aux.provides_language(font_id, asked_script, asked_language)
+ if not font_id or type (font_id) ~= "number"
+ or not asked_script or type (asked_script) ~= "string"
+ or not asked_language or type (asked_language) ~= "string"
+ then
+ logreport ("both", 0, "aux",
+ "invalid parameters to provides_language(%s, %s, %s)",
+ tostring (font_id),
+ tostring (asked_script),
+ tostring (asked_language))
+ return false
+ end
+ asked_script = stringlower(asked_script)
+ asked_language = stringlower(asked_language)
+ if font_id and font_id > 0 then
+ local tfmdata = identifiers[font_id]
+ if not tfmdata then return false end
+ local features = get_features (tfmdata)
+ if features == false then
+ logreport ("log", 1, "aux", "font no %d lacks a features table", font_id)
+ return false
+ end
+ for method, featuredata in next, features do
+ --- where method: "gpos" | "gsub"
+ for feature, data in next, featuredata do
+ local scriptdata = data[asked_script]
+ if scriptdata and scriptdata[asked_language] then
+ logreport ("log", 1, "aux",
+ "font no %d (%s) defines feature %s "
+ .. "for script %s with language %s",
+ font_id, fontname, feature,
+ asked_script, asked_language)
+ return true
+ end
+ end
+ end
+ logreport ("log", 0, "aux",
+ "font no %d (%s) defines no feature "
+ .. "for script %s with language %s",
+ font_id, fontname, asked_script, asked_language)
+ end
+ logreport ("log", 0, "aux", "no font with id %d", font_id)
+ return false
+end
+
+--[[doc--
+We strip the syntax elements from feature definitions (shouldn’t
+actually be there in the first place, but who cares ...)
+--doc]]--
+
+local lpeg = require"lpeg"
+local C, P, S = lpeg.C, lpeg.P, lpeg.S
+local lpegmatch = lpeg.match
+
+local sign = S"+-"
+local rhs = P"=" * P(1)^0 * P(-1)
+local strip_garbage = sign^-1 * C((1 - rhs)^1)
+
+--s = "+foo" --> foo
+--ss = "-bar" --> bar
+--sss = "baz" --> baz
+--t = "foo=bar" --> foo
+--tt = "+bar=baz" --> bar
+--ttt = "-baz=true" --> baz
+--
+--print(lpeg.match(strip_garbage, s))
+--print(lpeg.match(strip_garbage, ss))
+--print(lpeg.match(strip_garbage, sss))
+--print(lpeg.match(strip_garbage, t))
+--print(lpeg.match(strip_garbage, tt))
+--print(lpeg.match(strip_garbage, ttt))
+
+--[[doc--
+This function, modeled after “check_feature()” from fontspec, returns
+true if in the given font, the language with tag “asked_language” is
+accounted for in the script with tag “asked_script” in feature
+“asked_feature”.
+--doc]]--
+
+--- int -> string -> string -> string -> bool
+function aux.provides_feature(font_id, asked_script,
+ asked_language, asked_feature)
+ if not font_id or type (font_id) ~= "number"
+ or not asked_script or type (asked_script) ~= "string"
+ or not asked_language or type (asked_language) ~= "string"
+ or not asked_feature or type (asked_feature) ~= "string"
+ then
+ logreport ("both", 0, "aux",
+ "invalid parameters to provides_feature(%s, %s, %s, %s)",
+ tostring (font_id), tostring (asked_script),
+ tostring (asked_language), tostring (asked_feature))
+ return false
+ end
+ asked_script = stringlower(asked_script)
+ asked_language = stringlower(asked_language)
+ asked_feature = lpegmatch(strip_garbage, asked_feature)
+
+ if font_id > 0 then
+ local tfmdata = identifiers[font_id]
+ if not tfmdata then return false end
+ local features = get_features (tfmdata)
+ if features == false then
+ logreport ("log", 1, "aux", "font no %d lacks a features table", font_id)
+ return false
+ end
+ for method, featuredata in next, features do
+ --- where method: "gpos" | "gsub"
+ local feature = featuredata[asked_feature]
+ if feature then
+ local scriptdata = feature[asked_script]
+ if scriptdata and scriptdata[asked_language] then
+ logreport ("log", 1, "aux",
+ "font no %d (%s) defines feature %s "
+ .. "for script %s with language %s",
+ font_id, fontname, asked_feature,
+ asked_script, asked_language)
+ return true
+ end
+ end
+ end
+ logreport ("log", 0, "aux",
+ "font no %d (%s) does not define feature %s for script %s with language %s",
+ font_id, fontname, asked_feature, asked_script, asked_language)
+ end
+ logreport ("log", 0, "aux", "no font with id %d", font_id)
+ return false
+end
+
+-----------------------------------------------------------------------
+--- font dimensions
+-----------------------------------------------------------------------
+
+--- int -> string -> int
+local function get_math_dimension(font_id, dimenname)
+ if type(font_id) == "string" then
+ font_id = fontid(font_id) --- safeguard
+ end
+ local fontdata = identifiers[font_id]
+ local mathdata = fontdata.mathparameters
+ if mathdata then
+ return mathdata[dimenname] or 0
+ end
+ return 0
+end
+
+aux.get_math_dimension = get_math_dimension
+
+--- int -> string -> unit
+function aux.sprint_math_dimension(font_id, dimenname)
+ if type(font_id) == "string" then
+ font_id = fontid(font_id)
+ end
+ local dim = get_math_dimension(font_id, dimenname)
+ texsprint(luatexbase.catcodetables["latex-package"], dim, "sp")
+end
+
+-----------------------------------------------------------------------
+--- extra database functions
+-----------------------------------------------------------------------
+
+--[====[-- TODO -> port this to new db model
+
+--- local directories -------------------------------------------------
+
+--- migrated from luaotfload-database.lua
+--- https://github.com/lualatex/luaotfload/pull/61#issuecomment-17776975
+
+--- string -> (int * int)
+local scan_external_dir = function (dir)
+ local old_names, new_names = names.data()
+ if not old_names then
+ old_names = load_names()
+ end
+ new_names = tablecopy(old_names)
+ local n_scanned, n_new = scan_dir(dir, old_names, new_names)
+ --- FIXME
+ --- This doesn’t seem right. If a db update is triggered after this
+ --- point, then the added fonts will be saved along with it --
+ --- which is not as “temporarily” as it should be. (This should be
+ --- addressed during a refactoring of names_resolve().)
+ names.data = new_names
+ return n_scanned, n_new
+end
+
+aux.scan_external_dir = scan_external_dir
+
+--]====]--
+
+function aux.scan_external_dir()
+ print "ERROR: scan_external_dir() is not implemented"
+end
+
+--- db queries --------------------------------------------------------
+
+--- https://github.com/lualatex/luaotfload/issues/74
+--- string -> (string * int)
+local function resolve_fontname(name)
+ local foundname, subfont = luaotfload.resolvers.name {
+ name = name,
+ specification = "name:" .. name,
+ }
+ if foundname then
+ return foundname, subfont
+ end
+ return false, false
+end
+
+aux.resolve_fontname = resolve_fontname
+
+--- string list -> (string * int)
+function aux.resolve_fontlist(names)
+ for n = 1, #names do
+ local foundname, subfont = resolve_fontname(this)
+ if foundname then
+ return foundname, subfont
+ end
+ end
+ return false, false
+end
+
+--- index access ------------------------------------------------------
+
+--- Based on a discussion on the Luatex mailing list:
+--- http://tug.org/pipermail/luatex/2014-June/004881.html
+
+--[[doc--
+
+ aux.read_font_index -- Read the names index from the canonical
+ location and return its contents. This does not affect the behavior
+ of Luaotfload: The returned table is independent of what the font
+ resolvers use internally. Access is raw: each call to the function
+ will result in the entire table being re-read from disk.
+
+--doc]]--
+
+local load_names = fontnames.load
+local access_font_index = fontnames.access_font_index
+
+function aux.read_font_index()
+ return load_names (true) or { }
+end
+
+--[[doc--
+
+ aux.font_index -- Access Luaotfload’s internal database. If the
+ database hasn’t been loaded yet this will cause it to be loaded, with
+ all the possible side-effects like for instance creating the index
+ file if it doesn’t exist, reading all font files, &c.
+
+--doc]]--
+
+function aux.font_index() return access_font_index () end
+
+
+--- loaded fonts ------------------------------------------------------
+
+--- just a proof of concept
+
+--- fontobj -> string list -> (string list) list
+local function get_font_data (tfmdata, keys)
+ local acc = {}
+ for n = 1, #keys do
+ acc[n] = tfmdata[keys[n]] or false
+ end
+ return acc
+end
+
+--[[doc--
+
+ The next one operates on the fonts.hashes.identifiers table.
+ It returns a list containing tuples of font ids and the
+ contents of the fields specified in the first argument.
+ Font table entries that were created indirectly -- e.g. by
+ \letterspacefont or during font expansion -- will not be
+ listed.
+
+--doc]]--
+
+local default_keys = { "fullname" }
+
+--- string list -> (int * string list) list
+function aux.get_loaded_fonts (keys)
+ keys = keys or default_keys
+ local acc = {}
+ for id, tfmdata in pairs(identifiers) do
+ local data = get_font_data(tfmdata, keys)
+ acc[#acc+1] = { id, data }
+ end
+ return acc
+end
+
+--- Raw access to the font.* namespace is unsafe so no documentation on
+--- this one.
+function aux.get_raw_fonts ()
+ local res = { }
+ for i, v in font.each() do
+ if v.filename then
+ res[#res+1] = { i, v }
+ end
+ end
+ return res
+end
+
+-----------------------------------------------------------------------
+--- font parameters
+-----------------------------------------------------------------------
+--- analogy of font-hsh
+
+fonthashes.parameters = fonthashes.parameters or { }
+fonthashes.quads = fonthashes.quads or { }
+
+local parameters = fonthashes.parameters or { }
+local quads = fonthashes.quads or { }
+
+setmetatable(parameters, { __index = function (t, font_id)
+ local tfmdata = identifiers[font_id]
+ if not tfmdata then --- unsafe; avoid
+ tfmdata = font.fonts[font_id]
+ end
+ if tfmdata and type(tfmdata) == "table" then
+ local fontparameters = tfmdata.parameters
+ t[font_id] = fontparameters
+ return fontparameters
+ end
+ return nil
+end})
+
+--[[doc--
+
+ Note that the reason as to why we prefer functions over table indices
+ is that functions are much safer against unintended manipulation.
+ This justifies the overhead they cost.
+
+--doc]]--
+
+--- int -> (number | false)
+function aux.get_quad(font_id)
+ local quad = quads[font_id]
+ if quad then
+ return quad
+ end
+ local fontparameters = parameters[font_id]
+ if fontparameters then
+ local quad = fontparameters.quad or 0
+ quads[font_id] = quad
+ return quad
+ end
+ return false
+end
+
+-----------------------------------------------------------------------
+--- initialization
+-----------------------------------------------------------------------
+
+local function inject_callbacks (lst)
+ if not lst and next (lst) then return false end
+
+ local inject = function (def)
+ local cb, fn, id = unpack (def)
+ cb = tostring (cb)
+ id = tostring (id)
+ if not cb or not fn or not id or not type (fn) == "function" then
+ logreport ("both", 0, "aux", "Invalid callback requested (%q, %s, %q).",
+ cb, tostring (fn), id)
+ return false
+ end
+ cb = stringformat ("luaotfload.%s", cb)
+ id = stringformat ("luaotfload.aux.%s", id)
+ logreport ("log", 5, "aux", "Installing callback %q->%q.", cb, id)
+ luatexbase.add_to_callback (cb, fn, id)
+ return true
+ end
+
+ local ret = true
+ for i = 1, #lst do ret = inject (lst [i]) end
+ return ret
+end
+
+return function ()
+ luaotfload.aux = aux
+ return inject_callbacks (luaotfload_callbacks)
+end
+
+-- vim:tw=79:sw=2:ts=8:et
+