summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorElie Roux <elie.roux@telecom-bretagne.eu>2013-05-03 01:31:57 -0700
committerElie Roux <elie.roux@telecom-bretagne.eu>2013-05-03 01:31:57 -0700
commit3e3dcf4b43fb6bf14bba1bd4024b418124822e93 (patch)
treeb70a6b21034ce00c7b741329f4b5b50a29ef9e26
parent49cc81c4edff6e3c813a93964923bdb35f07e95c (diff)
parent225c63363a259867bad8848e9e9880e43cbf4ad5 (diff)
downloadluaotfload-3e3dcf4b43fb6bf14bba1bd4024b418124822e93.tar.gz
Merge pull request #14 from lualatex/master
update with lualatex master
-rw-r--r--Makefile2
-rw-r--r--NEWS37
-rw-r--r--filegraph.dot2
-rw-r--r--luaotfload-auxiliary.lua406
-rw-r--r--luaotfload-database.lua485
-rw-r--r--luaotfload-features.lua395
-rw-r--r--luaotfload-merged.lua45
-rw-r--r--luaotfload-override.lua10
-rwxr-xr-xluaotfload-tool.lua (renamed from fontdbutil.lua)31
-rw-r--r--luaotfload.dtx505
-rwxr-xr-xmkglyphlist6
-rw-r--r--tests/font_patch.tex6
-rw-r--r--tests/pln-aux-1.tex49
-rw-r--r--tests/pln-aux-2.tex102
-rw-r--r--tests/pln-aux-3.tex39
-rw-r--r--tests/pln-aux-4.tex41
-rw-r--r--tests/pln-request-4-slashed.tex12
-rw-r--r--tests/pln-request-5-cached.tex18
-rw-r--r--tests/pln-subfont-1.tex12
-rw-r--r--tests/pln-tfm.tex10
20 files changed, 1720 insertions, 493 deletions
diff --git a/Makefile b/Makefile
index 4821619..bdc2d35 100644
--- a/Makefile
+++ b/Makefile
@@ -4,7 +4,7 @@ NAME = luaotfload
DOC = $(NAME).pdf
DTX = $(NAME).dtx
OTFL = $(wildcard otfl-*.lua) otfl-blacklist.cnf font-age.lua
-SCRIPT = fontdbutil
+SCRIPT = luaotfload-tool.lua
GLYPHSCRIPT = mkglyphlist
GLYPHSOURCE = glyphlist.txt
diff --git a/NEWS b/NEWS
index d2808f5..3858d8f 100644
--- a/NEWS
+++ b/NEWS
@@ -6,10 +6,28 @@ Change History
backward-incompatible changes in the font structure (fontspec and
unicode-math must be updated)
* Synchronisation with ConTeXt is now easier and can be done by just
- updating otfl-fonts-merged.lua (available in ConTeXt)
- * Improve documentation
- * renaming mkluatexfontdb into fontdbutil, with more search functionalities
- * blacklisting font lingoes.ttf
+ updating luaotfload-merged.lua (<= luatex-fonts-merged.lua from
+ the LuaTeX-Plain format)
+ * Improved and extended documentation
+ * Font filenames are stored in the database (file: lookups more efficient)
+ * Optional caching of name: lookups
+ * Increased fidelity of XeTeX emulation
+ * Renaming mkluatexfontdb into luaotfload-tool (the old behavior is kept if
+ the script is symlinked/ renamed mkluatexfontdb)
+ * Added options to luaotfload-tool for simple database queries
+ * Logging facilities have been rewritten and allow for more granularity
+ * All files have been renamed, abandoning cryptic acronyms; also
+ the new prefix is »luaotfload« (<= »otfl«)
+ * The Adobe Glyph List (font-age.lua) is now built via script (mkglyphlist)
+ * Hans adapted the font loader to several of our requests (attribute
+ allocation, custom merged package name etc.)
+ * There is now a central, non-personal dev repo on github:
+ https://github.com/lualatex/luaotfload
+
+2013/04/27, luaotfload v1.3:
+ * blacklisting lingoes.ttf (segfaults)
+ * unblacklisting ttc fonts (warning: may break LuaTeX < 0.65)
+ * fixing font cache file name with non-ascii characters
2013/04/25, luaotfload v1.29:
* Reverting the unified resolver, as the database was rebuilt too often
@@ -19,13 +37,20 @@ Change History
* Fix a bug that made fontconfig files not parsed when OSFONTDIR is set
2013/04/11, luaotfload v1.28:
- * Adapting to LuaTeX 0.75, keeping backward-compatibility
+ * Adapting to LuaTeX 0.75
* Fix small documentation issues in mkluatexfontdb
- * Fix possibility of infite loop with fontconfig config files references
+ * Fix possible infite loop with fontconfig config files references
* Adding semibold synonym for bold
* file:xxx syntax now uses the same search function as name: which
make more fonts recognized
+2012/05/28, luaotfload v1.27:
+ * Fix "endless loop in charlist" with some OpenType math fonts
+
+2012/03/27, luaotfload v1.26:
+ * Enable setting italic correction values by default
+ * Fix finding demibold italic fonts
+
2011/04/21, luaotfload v1.25:
* Fix bug loading *.dfont fonts
* Misc. documentation fixes
diff --git a/filegraph.dot b/filegraph.dot
index a0eadec..d69937b 100644
--- a/filegraph.dot
+++ b/filegraph.dot
@@ -62,7 +62,7 @@ strict digraph luaotfload_files { //looks weird with circo ...
* main files
* ································································· */
- fontdbutil [label = "fontdbutil\nmkluatexfontdb.lua",
+ fontdbutil [label = "luaotfload-util\nmkluatexfontdb.lua",
shape = rect,
width = "3.2cm",
height = "1.2cm",
diff --git a/luaotfload-auxiliary.lua b/luaotfload-auxiliary.lua
new file mode 100644
index 0000000..2cf1e00
--- /dev/null
+++ b/luaotfload-auxiliary.lua
@@ -0,0 +1,406 @@
+#!/usr/bin/env texlua
+-----------------------------------------------------------------------
+-- FILE: luaotfload-auxiliary.lua
+-- DESCRIPTION: part of luaotfload
+-- REQUIREMENTS: luaotfload 2.2
+-- AUTHOR: Khaled Hosny, Élie Roux, Philipp Gesang
+-- VERSION: 1.0
+-- CREATED: 2013-05-01 14:40:50+0200
+-----------------------------------------------------------------------
+--
+
+--- this file addresses issue #24
+--- https://github.com/lualatex/luaotfload/issues/24#
+
+luaotfload = luaotfload or {}
+luaotfload.aux = luaotfload.aux or { }
+
+config = config or { }
+config.luaotfload = config.luaotfload or { }
+
+local aux = luaotfload.aux
+local log = luaotfload.log
+local identifiers = fonts.hashes.identifiers
+
+local fontid = font.id
+local texsprint = tex.sprint
+
+local utf8 = unicode.utf8
+local stringlower = string.lower
+local stringformat = string.format
+local stringgsub = string.gsub
+local stringbyte = string.byte
+
+-----------------------------------------------------------------------
+--- font patches
+-----------------------------------------------------------------------
+
+--[[doc--
+This sets two dimensions apparently relied upon by the unicode-math
+package.
+--doc]]--
+
+local set_sscale_dimens = function (fontdata)
+ local mathconstants = fontdata.MathConstants
+ local parameters = fontdata.parameters
+ if mathconstants then
+ parameters[10] = mathconstants.ScriptPercentScaleDown or 70
+ parameters[11] = mathconstants.ScriptScriptPercentScaleDown or 50
+ end
+ return fontdata
+end
+
+luatexbase.add_to_callback(
+ "luaotfload.patch_font",
+ set_sscale_dimens,
+ "luaotfload.aux.set_sscale_dimens")
+
+--- fontobj -> int
+local lookup_units = function (fontdata)
+ local metadata = fontdata.shared and fontdata.shared.rawdata.metadata
+ if metadata and metadata.units_per_em then
+ return metadata.units_per_em
+ elseif fontdata.parameters and fontdata.parameters.units then
+ return fontdata.parameters.units
+ elseif fontdata.units then --- v1.x
+ return fontdata.units
+ end
+ return 1000
+end
+
+--[[doc--
+This callback corrects some values of the Cambria font.
+--doc]]--
+local patch_cambria_domh = function (fontdata)
+ local mathconstants = fontdata.MathConstants
+ if mathconstants and fontdata.psname == "CambriaMath" then
+ --- my test Cambria has 2048
+ local units_per_em = fontdata.units_per_em or lookup_units(fontdata)
+ local sz = fontdata.parameters.size or fontdata.size
+ local mh = 2800 / units_per_em * sz
+ if mathconstants.DisplayOperatorMinHeight < mh then
+ mathconstants.DisplayOperatorMinHeight = mh
+ end
+ end
+end
+
+luatexbase.add_to_callback(
+ "luaotfload.patch_font",
+ patch_cambria_domh,
+ "luaotfload.aux.patch_cambria_domh")
+
+--[[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 set_capheight = function (fontdata)
+ local shared = fontdata.shared
+ local parameters = fontdata.parameters
+ local capheight
+ if shared then
+ local units_per_em = parameters.units
+ local size = parameters.size
+ local os2_capheight = shared.rawdata.metadata.pfminfo.os2_capheight
+
+ if os2_capheight > 0 then
+ capheight = os2_capheight / units_per_em * size
+ else
+ local X8 = stringbyte"X"
+ if fontdata.characters[X8] then
+ capheight = fontdata.characters[X8].height
+ else
+ capheight = parameters.ascender / units_per_em * size
+ end
+ end
+ else
+ local X8 = stringbyte"X"
+ if fontdata.characters[X8] then
+ capheight = fontdata.characters[X8].height
+ end
+ end
+ if capheight then
+ --- is this legit? afaics there’s nothing else on the
+ --- array part of that table
+ fontdata.parameters[8] = capheight
+ end
+end
+
+luatexbase.add_to_callback(
+ "luaotfload.patch_font",
+ set_capheight,
+ "luaotfload.aux.set_capheight")
+
+-----------------------------------------------------------------------
+--- glyphs
+-----------------------------------------------------------------------
+
+--- int -> int -> bool
+local font_has_glyph = function (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
+
+aux.font_has_glyph = font_has_glyph
+
+--- int -> bool
+local current_font_has_glyph = function (codepoint)
+ return font_has_glyph (font.current(), codepoint)
+end
+
+aux.current_font_has_glyph = current_font_has_glyph
+
+local do_if_glyph_else = function (chr, positive, negative)
+ local codepoint = tonumber(chr)
+ if not codepoint then codepoint = utf8.byte(chr) end
+ if current_font_has_glyph(codepoint) then
+ tex.sprint(positive)
+ else
+ tex.sprint(negative)
+ end
+end
+
+aux.do_if_glyph_else = do_if_glyph_else
+
+--[[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
+
+--doc]]--
+
+--- string -> (int | false)
+local slot_of_name = function (glyphname)
+ local fontdata = identifiers[font.current()]
+ if fontdata then
+ local unicode = fontdata.resources.unicodes[glyphname]
+ if unicode and type(unicode) == "number" then
+ return unicode
+ else
+ return unicode[1] --- for multiple components
+ end
+ end
+ return false
+end
+
+aux.slot_of_name = slot_of_name
+
+--[[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)
+local name_of_slot = function (codepoint)
+ if not indices then --- this will load the glyph list
+ local unicodes = fonts.encodings.agl.unicodes
+ indices = table.swapped(unicodes)
+ end
+ local glyphname = indices[codepoint]
+ if glyphname then
+ return glyphname
+ end
+ return false
+end
+
+aux.name_of_slot = name_of_slot
+
+-----------------------------------------------------------------------
+--- features / scripts / languages
+-----------------------------------------------------------------------
+--- lots of arrowcode ahead
+
+--[[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
+local provides_script = function (font_id, asked_script)
+ asked_script = stringlower(asked_script)
+ if font_id and font_id > 0 then
+ local fontdata = identifiers[font_id].shared.rawdata
+ if fontdata then
+ local fontname = fontdata.metadata.fontname
+ local features = fontdata.resources.features
+ for method, featuredata in next, features do
+ --- where method: "gpos" | "gsub"
+ for feature, data in next, featuredata do
+ if data[asked_script] then
+ log(stringformat(
+ "font no %d (%s) defines feature %s for script %s",
+ font_id, fontname, feature, asked_script))
+ return true
+ end
+ end
+ end
+ log(stringformat(
+ "font no %d (%s) defines no feature for script %s",
+ font_id, fontname, asked_script))
+ end
+ end
+ log(stringformat("no font with id %d", font_id))
+ return false
+end
+
+aux.provides_script = provides_script
+
+--[[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
+local provides_language = function (font_id, asked_script, asked_language)
+ asked_script = stringlower(asked_script)
+ asked_language = stringlower(asked_language)
+ if font_id and font_id > 0 then
+ local fontdata = identifiers[font_id].shared.rawdata
+ if fontdata then
+ local fontname = fontdata.metadata.fontname
+ local features = fontdata.resources.features
+ 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
+ log(stringformat("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
+ log(stringformat(
+ "font no %d (%s) defines no feature for script %s with language %s",
+ font_id, fontname, asked_script, asked_language))
+ end
+ end
+ log(stringformat("no font with id %d", font_id))
+ return false
+end
+
+aux.provides_language = provides_language
+
+--[[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
+local provides_feature = function (font_id, asked_script,
+ asked_language, asked_feature)
+ asked_script = stringlower(asked_script)
+ asked_language = stringlower(asked_language)
+ asked_feature = lpegmatch(strip_garbage, asked_feature)
+
+ if font_id and font_id > 0 then
+ local fontdata = identifiers[font_id].shared.rawdata
+ if fontdata then
+ local features = fontdata.resources.features
+ local fontname = fontdata.metadata.fontname
+ 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
+ log(stringformat("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
+ log(stringformat(
+ "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
+ end
+ log(stringformat("no font with id %d", font_id))
+ return false
+end
+
+aux.provides_feature = provides_feature
+
+-----------------------------------------------------------------------
+--- font dimensions
+-----------------------------------------------------------------------
+
+--- string -> string -> int
+local get_math_dimension = function (csname, dimenname)
+ local fontdata = identifiers[fontid(csname)]
+ local mathdata = fontdata.mathparameters
+ if mathdata then return mathdata[dimenname] or 0 end
+ return 0
+end
+
+aux.get_math_dimension = get_math_dimension
+
+--- string -> string -> unit
+local sprint_math_dimension = function (csname, dimenname)
+ local dim = get_math_dimension(csname, dimenname)
+ texsprint(luatexbase.catcodetables["latex-package"], dim)
+ texsprint(luatexbase.catcodetables["latex-package"], "sp")
+end
+
+aux.sprint_math_dimension = sprint_math_dimension
+
+-- vim:tw=71:sw=2:ts=2:expandtab
diff --git a/luaotfload-database.lua b/luaotfload-database.lua
index 3085b63..aaba55a 100644
--- a/luaotfload-database.lua
+++ b/luaotfload-database.lua
@@ -71,17 +71,25 @@ fonts.definers = fonts.definers or { }
local names = fonts.names
-names.version = 2.202
-names.data = nil
+names.version = 2.203
+names.data = nil --- contains the loaded database
+names.lookups = nil --- contains the lookup cache
names.path = {
- basename = "luaotfload-names.lua",
- dir = "",
- path = "",
+ dir = "", --- db and cache directory
+ basename = "luaotfload-names.lua", --- db file name
+ path = "", --- full path to db file
+ lookup_basename = "luaotfload-lookup-cache.lua", --- cache file name
+ lookup_path = "", --- cache full path
}
-config = config or { }
-config.luaotfload = config.luaotfload or { }
-config.luaotfload.resolver = config.luaotfload.resolver or "normal"
+config = config or { }
+config.luaotfload = config.luaotfload or { }
+config.luaotfload.resolver = config.luaotfload.resolver or "normal"
+if config.luaotfload.update_live ~= false then
+ --- this option allows for disabling updates
+ --- during a TeX run
+ config.luaotfload.update_live = true
+end
-- We use the cache.* of ConTeXt (see luat-basics-gen), we can
-- use it safely (all checks and directory creations are already done). It
@@ -92,8 +100,9 @@ if caches then
if not writable_path then
error("Impossible to find a suitable writeable cache...")
end
- names.path.dir = writable_path
- names.path.path = filejoin(writable_path, names.path.basename)
+ names.path.dir = writable_path
+ names.path.path = filejoin(writable_path, names.path.basename)
+ names.path.lookup_path = filejoin(writable_path, names.path.lookup_basename)
else --- running as script, inject some dummies
caches = { }
logs = { report = function () end }
@@ -183,12 +192,6 @@ mtx-fonts has in names.tma:
--doc]]--
local fontnames_init = function (keep_cache) --- returns dbobj
- local request_cache
- if keep_cache and names.data and names.data.request_cache then
- request_cache = names.data.request_cache
- else
- request_cache = { }
- end
return {
mappings = { },
status = { },
@@ -201,7 +204,6 @@ local fontnames_init = function (keep_cache) --- returns dbobj
basenames = { },
-- fullnames = { },
version = names.version,
- request_cache = request_cache,
}
end
@@ -239,14 +241,19 @@ local load_lua_file = function (path)
end
--- define locals in scope
+local crude_file_lookup
+local crude_file_lookup_verbose
local find_closest
local flush_cache
local font_fullinfo
local load_names
+local load_lookups
local read_fonts_conf
local reload_db
local resolve
+local resolve_cached
local save_names
+local save_lookups
local scan_external_dir
local update_names
@@ -275,6 +282,20 @@ load_names = function ( )
return data
end
+--- unit -> dbobj
+load_lookups = function ( )
+ local foundname, data = load_lua_file(names.path.lookup_path)
+ if data then
+ report("both", 1, "cache",
+ "Lookup cache loaded (%s)", foundname)
+ else
+ report("both", 1, "cache",
+ "No lookup cache, creating empty.")
+ data = { }
+ end
+ return data
+end
+
local fuzzy_limit = 1 --- display closest only
local style_synonyms = { set = { } }
@@ -298,9 +319,13 @@ do
end
end
---- chain: barenames -> [fullnames ->] basenames -> findfile
-local crude_file_lookup_verbose = function (data, filename)
- local mappings = data.mappings
+local type1_formats = { "tfm", "ofm", }
+
+--- string -> (string * bool | int)
+crude_file_lookup_verbose = function (filename)
+ if not names.data then names.data = load_names() end
+ local data = names.data
+ local mappings = data.mappings
local found
--- look up in db first ...
@@ -312,52 +337,43 @@ local crude_file_lookup_verbose = function (data, filename)
filename, found[1])
return found
end
--- found = data.fullnames[filename]
--- if found and mappings[found] then
--- found = mappings[found].filename[1]
--- "crude file lookup: req=%s; hit=bare; ret=%s",
--- filename, found[1])
--- return found
--- end
found = data.basenames[filename]
if found and mappings[found] then
found = mappings[found].filename
report("info", 0, "db",
- "crude file lookup: req=%s; hit=bare; ret=%s",
+ "crude file lookup: req=%s; hit=base; ret=%s",
filename, found[1])
return found
end
- --- now look for tfm et al.; will be superseded by proper
- --- format lookup
- found = resolvers.findfile(filename, "tfm")
- if found then
- report("info", 0, "db",
- "crude file lookup: req=tfm; hit=bare; ret=%s", found)
- return { found, false }
- end
- found = resolvers.findfile(filename, "ofm")
- if found then
- report("info", 0, "db",
- "crude file lookup: req=ofm; hit=bare; ret=%s", found)
- return { found, false }
+ --- ofm and tfm
+ for i=1, #type1_formats do
+ local format = type1_formats[i]
+ if resolvers.findfile(filename, format) then
+ return { file.addsuffix(filename, format), false }, format
+ end
end
- return false
+ return { filename, false }, nil
end
-local crude_file_lookup = function (data, filename)
+--- string -> (string * bool | int)
+crude_file_lookup = function (filename)
+ if not names.data then names.data = load_names() end
+ local data = names.data
+ local mappings = data.mappings
local found = data.barenames[filename]
--- or data.fullnames[filename]
or data.basenames[filename]
if found then
found = data.mappings[found]
if found then return found.filename end
end
- found = resolvers.findfile(filename, "tfm")
- if found then return { found, false } end
- found = resolvers.findfile(filename, "ofm")
- if found then return { found, false } end
- return false
+ for i=1, #type1_formats do
+ local format = type1_formats[i]
+ if resolvers.findfile(filename, format) then
+ return { file.addsuffix(filename, format), false }, format
+ end
+ end
+ return { filename, false }, nil
end
--[[doc--
@@ -377,28 +393,12 @@ TODO:
× 3) make caching optional (via the config table) for debugging
× 4) make names_update() cache aware (nil if “force”)
× 5) add logging
- × 6) add cache control to fontdbutil
+ × 6) add cache control to luaotfload-tool
× 7) incr db version
8) wishlist: save cache only at the end of a run
9) ???
n) PROFIT!!!
---doc]]--
-
---- the resolver is called after the font request is parsed
---- this is where we insert the cache
-local normal_resolver = fonts.definers.resolve
-local dummy_resolver = function (specification)
- --- this ensures that the db is always loaded
- --- before a lookup occurs
- if not names.data then names.data = load_names() end
- --inspect(specification)
- local resolved = normal_resolver(specification)
- --inspect(resolved)
- return resolved
-end
-
---[[doc--
The name lookup requires both the “name” and some other
keys, so we’ll concatenate them.
The spec is modified in place (ugh), so we’ll have to catalogue what
@@ -431,62 +431,73 @@ We’ll just cache a deep copy of the entire spec as it leaves the
resolver, lest we want to worry if we caught all the details.
--doc]]--
---- spec -> spec
-local cached_resolver = function (specification)
- if not names.data then names.data = load_names() end
- local request_cache = names.data.request_cache
+--- 'a -> 'a -> table -> (string * int|boolean * boolean)
+resolve_cached = function (_, _, specification)
+ --if not names.data then names.data = load_names() end
+ if not names.lookups then names.lookups = load_lookups() end
local request = specification.specification
- report("info", 4, "cache",
- "looking for “%s” in cache ...",
+ report("both", 4, "cache", "looking for “%s” in cache ...",
request)
- local found = names.data.request_cache[request]
+
+ local found = names.lookups[request]
+
+ --- case 1) cache positive ----------------------------------------
if found then --- replay fields from cache hit
report("info", 4, "cache", "found!")
- for i=1, #cache_fields do
- local f = cache_fields[i]
- if found[f] then specification[f] = found[f] end
- end
- return specification
+ return found[1], found[2], true
end
- report("info", 4, "cache", "not cached; resolving")
+ report("both", 4, "cache", "not cached; resolving")
+ --- case 2) cache negative ----------------------------------------
--- first we resolve normally ...
- local resolved_spec = normal_resolver(specification)
- --- ... then we add the fields to the cache
- local entry = { }
- for i=1, #cache_fields do
- local f = cache_fields[i]
- entry[f] = resolved_spec[f]
- end
- report("info", 4, "cache", "new entry: %s", request)
- names.data.request_cache[request] = entry
+ local filename, subfont, success = resolve(nil, nil, specification)
+ if not success then return filename, subfont, false end
+ --- ... then we add the fields to the cache ... ...
+ local entry = { filename, subfont }
+ report("both", 4, "cache", "new entry: %s", request)
+ names.lookups[request] = entry
--- obviously, the updated cache needs to be stored.
--- for the moment, we write the entire db to disk
--- whenever the cache is updated.
--- TODO this should trigger a save only once the
--- document is compiled (finish_pdffile callback?)
- report("info", 5, "cache", "saving updated cache")
- save_names()
- return resolved_spec
+ --- TODO we should speed up writing by separating
+ --- the cache from the db
+ report("both", 5, "cache", "saving updated cache")
+ save_lookups()
+ return filename, subfont, true
end
-local resolvers = {
- dummy = dummy_resolver,
- normal = normal_resolver,
- cached = cached_resolver,
-}
-
-fonts.definers.resolve = resolvers[config.luaotfload.resolver]
---fonts.definers.resolve = resolvers.cached
+--- this used to be inlined; with the lookup cache we don’t
+--- have to be parsimonious wrt function calls anymore
+--- “found” is the match accumulator
+local add_to_match = function (
+ found, optsize, dsnsize, size,
+ minsize, maxsize, face)
+ local continue = true
+ if optsize then
+ if dsnsize == size or (size > minsize and size <= maxsize) then
+ found[1] = face
+ continue = false ---> break
+ else
+ found[#found+1] = face
+ end
+ else
+ found[1] = face
+ continue = false ---> break
+ end
+ return found, continue
+end
--[[doc--
Luatex-fonts, the font-loader package luaotfload imports, comes with
basic file location facilities (see luatex-fonts-syn.lua).
-However, the builtin functionality is too limited to be of more than
-basic use, which is why we supply our own resolver that accesses the
-font database created by the mkluatexfontdb script.
+However, not only does the builtin functionality rely on Context’s font
+name database, it is also too limited to be of more than basic use.
+For this reason, luaotfload supplies its own resolvers that accesses
+the font database created by the luaotfload-tool script.
--doc]]--
@@ -522,12 +533,6 @@ resolve = function (_,_,specification) -- the 1st two parameters are used by Con
if not fonts_loaded then names.data = load_names() end
local data = names.data
- if specification.lookup == "file" then
- local found = crude_file_lookup(data, specification.name)
- --local found = crude_file_lookup_verbose(data, specification.name)
- if found then return found[1], found[2], true end
- end
-
local name = sanitize_string(specification.name)
local style = sanitize_string(specification.style) or "regular"
@@ -569,9 +574,7 @@ resolve = function (_,_,specification) -- the 1st two parameters are used by Con
local found = { }
local synonym_set = style_synonyms.set
- for _,face in next, data.mappings do
- --- TODO we really should store those in dedicated
- --- .sanitized field
+ for _, face in next, data.mappings do
local family, subfamily, fullname, psname, fontname, pfullname
local facenames = face.sanitized
@@ -595,65 +598,56 @@ resolve = function (_,_,specification) -- the 1st two parameters are used by Con
if name == family then
if subfamily == style then
- if optsize then
- if dsnsize == size
- or (size > minsize and size <= maxsize) then
- found[1] = face
- break
- else
- found[#found+1] = face
- end
- else
- found[1] = face
- break
- end
+ local continue
+ found, continue = add_to_match(
+ found, optsize, dsnsize, size,
+ minsize, maxsize, face)
+ if continue == false then break end
elseif synonym_set[style] and
synonym_set[style][subfamily]
then
- if optsize then
- if dsnsize == size
- or (size > minsize and size <= maxsize) then
- found[1] = face
- break
- else
- found[#found+1] = face
- end
- else
- found[1] = face
- break
- end
+ local continue
+ found, continue = add_to_match(
+ found, optsize, dsnsize, size,
+ minsize, maxsize, face)
+ if continue == false then break end
elseif subfamily == "regular" or
- synonym_set.regular[subfamily] then
+ synonym_set.regular[subfamily] then
found.fallback = face
+ elseif name == fullname
+ or name == pfullname
+ or name == fontname
+ or name == psname
+ then
+ local continue
+ found, continue = add_to_match(
+ found, optsize, dsnsize, size,
+ minsize, maxsize, face)
+ if continue == false then break end
end
- end
-
- if name == fullname
- or name == pfullname
- or name == fontname
- or name == psname then
- if optsize then
- if dsnsize == size
- or (size > minsize and size <= maxsize) then
- found[1] = face
- break
- else
- found[#found+1] = face
- end
- else
- found[1] = face
- break
+ else
+ if name == fullname
+ or name == pfullname
+ or name == fontname
+ or name == psname then
+ local continue
+ found, continue = add_to_match(
+ found, optsize, dsnsize, size,
+ minsize, maxsize, face)
+ if continue == false then break end
end
end
end
if #found == 1 then
- if kpselookup(found[1].filename[1]) then
+ --- “found” is really synonymous with “registered in the db”.
+ local filename = found[1].filename[1]
+ if lfsisfile(filename) or kpselookup(filename) then
report("log", 0, "resolve",
"font family='%s', subfamily='%s' found: %s",
- name, style, found[1].filename[1]
+ name, style, filename
)
- return found[1].filename[1], found[1].filename[2], true
+ return filename, found[1].filename[2], true
end
elseif #found > 1 then
-- we found matching font(s) but not in the requested optical
@@ -669,22 +663,25 @@ resolve = function (_,_,specification) -- the 1st two parameters are used by Con
least = difference
end
end
- if kpselookup(closest.filename[1]) then
+ local filename = closest.filename[1]
+ if lfsisfile(filename) or kpselookup(filename) then
report("log", 0, "resolve",
"font family='%s', subfamily='%s' found: %s",
- name, style, closest.filename[1]
+ name, style, filename
)
- return closest.filename[1], closest.filename[2], true
+ return filename, closest.filename[2], true
end
elseif found.fallback then
- return found.fallback.filename[1], found.fallback.filename[2], true
+ return found.fallback.filename[1],
+ found.fallback.filename[2],
+ true
end
--- no font found so far
if not fonts_reloaded then
--- last straw: try reloading the database
return reload_db(
- "unresoled font name: “" .. name .. "”",
+ "unresolved font name: “" .. name .. "”",
resolve, nil, nil, specification
)
end
@@ -701,9 +698,9 @@ end --- resolve()
--- string -> ('a -> 'a) -> 'a list -> 'a
reload_db = function (why, caller, ...)
- report("log", 1, "db", "reload initiated; reason: “%s”", why)
+ report("both", 1, "db", "reload initiated; reason: “%s”", why)
names.data = update_names()
- save_names(names.data)
+ save_names()
fonts_reloaded = true
return caller(...)
end
@@ -741,9 +738,7 @@ find_closest = function (name, limit)
local name = sanitize_string(name)
limit = limit or fuzzy_limit
- if not fonts_loaded then
- names.data = load_names()
- end
+ if not fonts_loaded then names.data = load_names() end
local data = names.data
@@ -823,7 +818,7 @@ The data inside an Opentype font file can be quite heterogeneous.
Thus in order to get the relevant information, parts of the original
table as returned by the font file reader need to be relocated.
--doc]]--
-font_fullinfo = function (filename, subfont, texmf)
+font_fullinfo = function (filename, subfont)
local tfmdata = { }
local rawfont = fontloader.open(filename, subfont)
if not rawfont then
@@ -870,10 +865,7 @@ font_fullinfo = function (filename, subfont, texmf)
tfmdata.fontname = metadata.fontname
tfmdata.fullname = metadata.fullname
tfmdata.familyname = metadata.familyname
- tfmdata.filename = {
- texmf and filebasename(filename) or filename,
- subfont
- }
+ tfmdata.filename = { filename, subfont } -- always store full path
tfmdata.weight = metadata.pfminfo.weight
tfmdata.width = metadata.pfminfo.width
tfmdata.slant = metadata.italicangle
@@ -887,8 +879,10 @@ font_fullinfo = function (filename, subfont, texmf)
end
--- we return true if the fond is new or re-indexed
---- string -> dbobj -> dbobj -> bool -> bool
-local load_font = function (fullname, fontnames, newfontnames, texmf)
+--- string -> dbobj -> dbobj -> bool
+local load_font = function (fullname, fontnames, newfontnames)
+ if not fullname then return false end
+
local newmappings = newfontnames.mappings
local newstatus = newfontnames.status
@@ -905,47 +899,41 @@ local load_font = function (fullname, fontnames, newfontnames, texmf)
local basename = filebasename(fullname)
local barename = filenameonly(fullname)
- --- entryname is apparently the identifier a font is
- --- loaded by; it is different for files in the texmf
- --- (due to kpse? idk.)
- --- entryname = texmf : true -> basename | false -> fullname
- local entryname = texmf and basename or fullname
+ local entryname = basename
- if not fullname then return false end
-
- if names.blacklist[fullname]
- or names.blacklist[basename]
+ if names.blacklist[fullname] or names.blacklist[basename]
then
report("log", 2, "db",
"ignoring blacklisted font “%s”", fullname)
return false
end
+
local timestamp, db_timestamp
- db_timestamp = status[entryname]
- and status[entryname].timestamp
+ db_timestamp = status[fullname]
+ and status[fullname].timestamp
timestamp = lfs.attributes(fullname, "modification")
- local index_status = newstatus[entryname]
- or (not texmf and newstatus[basename])
- local teststat = newstatus[entryname]
+ local index_status = newstatus[fullname]
--- index_status: nil | false | table
if index_status and index_status.timestamp == timestamp then
-- already indexed this run
return false
end
- newstatus[entryname] = newstatus[entryname] or { }
- newstatus[entryname].timestamp = timestamp
- newstatus[entryname].index = newstatus[entryname].index or { }
+ newstatus[fullname] = newstatus[fullname] or { }
+ newstatus[fullname].timestamp = timestamp
+ newstatus[fullname].index = newstatus[fullname].index or { }
+ --- this test compares the modification data registered
+ --- in the database with the current one
if db_timestamp == timestamp
- and not newstatus[entryname].index[1] then
- for _,v in next, status[entryname].index do
- local index = #newstatus[entryname].index
+ and not newstatus[fullname].index[1] then
+ for _,v in next, status[fullname].index do
+ local index = #newstatus[fullname].index
local fullinfo = mappings[v]
local location = #newmappings + 1
newmappings[location] = fullinfo --- keep
- newstatus[entryname].index[index+1] = location --- is this actually used anywhere?
+ newstatus[fullname].index[index+1] = location --- is this actually used anywhere?
-- newfullnames[fullname] = location
newbasenames[basename] = location
newbarenames[barename] = location
@@ -958,34 +946,34 @@ local load_font = function (fullname, fontnames, newfontnames, texmf)
if info then
if type(info) == "table" and #info > 1 then --- ttc
for n_font = 1, #info do
- local fullinfo = font_fullinfo(fullname, n_font-1, texmf)
+ local fullinfo = font_fullinfo(fullname, n_font-1)
if not fullinfo then
return false
end
local location = #newmappings+1
- local index = newstatus[entryname].index[n_font]
+ local index = newstatus[fullname].index[n_font]
if not index then index = location end
newmappings[index] = fullinfo
-- newfullnames[fullname] = location
newbasenames[basename] = location
newbarenames[barename] = location
- newstatus[entryname].index[n_font] = index
+ newstatus[fullname].index[n_font] = index
end
else
- local fullinfo = font_fullinfo(fullname, false, texmf)
+ local fullinfo = font_fullinfo(fullname, false)
if not fullinfo then
return false
end
local location = #newmappings+1
- local index = newstatus[entryname].index[1]
+ local index = newstatus[fullname].index[1]
if not index then index = location end
newmappings[index] = fullinfo
-- newfullnames[fullname] = location
newbasenames[basename] = location
newbarenames[barename] = location
- newstatus[entryname].index[1] = index
+ newstatus[fullname].index[1] = index
end
else --- missing info
@@ -1096,13 +1084,13 @@ for key, value in next, font_extensions do
end
--- string -> dbobj -> dbobj -> bool -> (int * int)
-local scan_dir = function (dirname, fontnames, newfontnames, texmf)
+local scan_dir = function (dirname, fontnames, newfontnames)
--[[
This function scans a directory and populates the list of fonts
with all the fonts it finds.
- dirname is the name of the directory to scan
- names is the font database to fill -> no such term!!!
- - texmf is a boolean saying if we are scanning a texmf directory
+ - texmf used to be a boolean saying if we are scanning a texmf directory
]]
local n_scanned, n_new = 0, 0 --- total of fonts collected
report("log", 2, "db", "scanning", "%s", dirname)
@@ -1118,7 +1106,7 @@ local scan_dir = function (dirname, fontnames, newfontnames, texmf)
local fullname = found[j]
fullname = path_normalize(fullname)
report("log", 2, "db", "loading font “%s”", fullname)
- local new = load_font(fullname, fontnames, newfontnames, texmf)
+ local new = load_font(fullname, fontnames, newfontnames)
if new then n_new = n_new + 1 end
end
end
@@ -1142,7 +1130,7 @@ local function scan_texmf_fonts(fontnames, newfontnames)
fontdirs = fontdirs .. stringgsub(kpseexpand_path("$TTFONTS"), "^%.", "")
if not stringis_empty(fontdirs) then
for _,d in next, filesplitpath(fontdirs) do
- local found, new = scan_dir(d, fontnames, newfontnames, true)
+ local found, new = scan_dir(d, fontnames, newfontnames)
n_scanned = n_scanned + found
n_new = n_new + new
end
@@ -1409,7 +1397,7 @@ local function scan_os_fonts(fontnames, newfontnames)
report("info", 2, "db", "Scanning OS fonts...")
report("info", 3, "db", "Searching in static system directories...")
for _,d in next, get_os_dirs() do
- local found, new = scan_dir(d, fontnames, newfontnames, false)
+ local found, new = scan_dir(d, fontnames, newfontnames)
n_scanned = n_scanned + found
n_new = n_new + new
end
@@ -1417,14 +1405,20 @@ local function scan_os_fonts(fontnames, newfontnames)
end
flush_cache = function ()
- if not names.data then names.data = load_names() end
- names.data.request_cache = { }
+ if not names.lookups then names.lookups = load_lookups() end
+ names.lookups = { }
collectgarbage"collect"
- return true, names.data
+ return true, names.lookups
end
--- dbobj -> bool -> dbobj
update_names = function (fontnames, force)
+ if config.luaotfload.update_live == false then
+ report("info", 2, "db",
+ "skipping database update")
+ --- skip all db updates
+ return fontnames
+ end
local starttime = os.gettimeofday()
local n_scanned, n_new = 0, 0
--[[
@@ -1432,7 +1426,7 @@ update_names = function (fontnames, force)
- “newfontnames” is the final table to return
- force is whether we rebuild it from scratch or not
]]
- report("info", 2, "db", "Updating the font names database"
+ report("both", 2, "db", "Updating the font names database"
.. (force and " forcefully" or ""))
if force then
@@ -1442,9 +1436,9 @@ update_names = function (fontnames, force)
fontnames = load_names()
end
if fontnames.version ~= names.version then
+ report("both", 1, "db", "No font names database or old "
+ .. "one found; generating new one")
fontnames = fontnames_init(true)
- report("log", 1, "db", "No font names database or old "
- .. "one found; generating new one")
end
end
local newfontnames = fontnames_init(true)
@@ -1470,13 +1464,47 @@ update_names = function (fontnames, force)
return newfontnames
end
+--- unit -> string
+local ensure_names_path = function ( )
+ local path = names.path.dir
+ if not lfsisdir(path) then
+ dirmkdirs(path)
+ end
+ return path
+end
+
+--- The lookup cache is an experimental feature of version 2.2;
+--- instead of incorporating it into the database it gets its own
+--- file. As we update it after every single addition this saves us
+--- quite some time.
+
+--- unit -> string
+save_lookups = function ( )
+ ---- this is boilerplate and should be refactored into something
+ ---- usable by both the db and the cache writers
+ local lookups = names.lookups
+ local path = ensure_names_path()
+ if fileiswritable(path) then
+ local luaname, lucname = make_name(names.path.lookup_path)
+ if luaname then
+ tabletofile(luaname, lookups, true)
+ if lucname and type(caches.compile) == "function" then
+ os.remove(lucname)
+ caches.compile(lookups, luaname, lucname)
+ report("info", 1, "cache", "Lookup cache saved")
+ return names.path.lookup_path
+ end
+ end
+ end
+ report("info", 0, "cache", "Could not write lookup cache")
+ return nil
+end
+
+--- save_names() is usually called without the argument
--- dbobj -> unit
save_names = function (fontnames)
if not fontnames then fontnames = names.data end
- local path = names.path.dir
- if not lfs.isdir(path) then
- dirmkdirs(path)
- end
+ local path = ensure_names_path()
if fileiswritable(path) then
local luaname, lucname = make_name(names.path.path)
if luaname then
@@ -1508,15 +1536,24 @@ scan_external_dir = function (dir)
end
--- export functionality to the namespace “fonts.names”
-names.flush_cache = flush_cache
-names.load = load_names
-names.save = save_names
-names.scan = scan_external_dir
-names.update = update_names
-
-names.resolve = resolve --- replace the resolver from luatex-fonts
-names.resolvespec = resolve
-names.find_closest = find_closest
+names.flush_cache = flush_cache
+names.load = load_names
+names.save = save_names
+names.scan = scan_external_dir
+names.update = update_names
+names.crude_file_lookup = crude_file_lookup
+names.crude_file_lookup_verbose = crude_file_lookup_verbose
+
+--- replace the resolver from luatex-fonts
+if config.luaotfload.resolver == "cached" then
+ report("info", 0, "cache", "caching of name: lookups active")
+ names.resolve = resolve_cached
+ names.resolvespec = resolve_cached
+else
+ names.resolve = resolve
+ names.resolvespec = resolve
+end
+names.find_closest = find_closest
-- for testing purpose
names.read_fonts_conf = read_fonts_conf
diff --git a/luaotfload-features.lua b/luaotfload-features.lua
index 78f8256..494d02d 100644
--- a/luaotfload-features.lua
+++ b/luaotfload-features.lua
@@ -26,7 +26,7 @@ function fonts.definers.getspecification(str)
return "", str, "", ":", str
end
-local feature_list = { }
+local old_feature_list = { }
local report = logs.names_report
@@ -64,20 +64,23 @@ local isstyle = function (request)
for _,v in next, request do
local stylename = supported[v]
if stylename then
- feature_list.style = stylename
+ old_feature_list.style = stylename
elseif stringfind(v, "^s=") then
--- after all, we want everything after the second byte ...
local val = stringsub(v, 3)
- feature_list.optsize = val
+ old_feature_list.optsize = val
elseif stylename == false then
report("log", 0,
- "load font", "unsupported font option: %s", v)
+ "load", "unsupported font option: %s", v)
elseif not stringis_empty(v) then
- feature_list.style = stringgsub(v, "[^%a%d]", "")
+ old_feature_list.style = stringgsub(v, "[^%a%d]", "")
end
end
end
+--- TODO an option to dump the default features for a script would make
+--- a nice addition to luaotfload-tool
+
local defaults = {
dflt = {
"ccmp", "locl", "rlig", "liga", "clig",
@@ -131,17 +134,22 @@ defaults.tibt = defaults.khmr
defaults.lao = defaults.thai
+--[[doc--
+Which features are active by default depends on the script requested.
+--doc]]--
+
--- (string, string) dict -> (string, string) dict
local set_default_features = function (speclist)
+ speclist = speclist or { }
local script = speclist.script or "dflt"
- report("log", 0, "load font",
+ report("log", 0, "load",
"auto-selecting default features for script: %s",
script)
local requested = defaults[script]
if not requested then
- report("log", 0, "load font",
+ report("log", 0, "load",
"no defaults for script “%s”, falling back to “dflt”",
script)
requested = defaults.dflt
@@ -149,9 +157,7 @@ local set_default_features = function (speclist)
for i=1, #requested do
local feat = requested[i]
- if speclist[feat] ~= false then
- speclist[feat] = true
- end
+ if speclist[feat] ~= false then speclist[feat] = true end
end
for feat, state in next, global_defaults do
@@ -163,14 +169,15 @@ local set_default_features = function (speclist)
return speclist
end
-local function issome () feature_list.lookup = 'name' end
-local function isfile () feature_list.lookup = 'file' end
-local function isname () feature_list.lookup = 'name' end
-local function thename(s) feature_list.name = s end
-local function issub (v) feature_list.sub = v end
-local function istrue (s) feature_list[s] = true end
-local function isfalse(s) feature_list[s] = false end
-local function iskey (k,v) feature_list[k] = v end
+-- --[==[obsolete--
+local function issome () old_feature_list.lookup = 'name' end
+local function isfile () old_feature_list.lookup = 'file' end
+local function isname () old_feature_list.lookup = 'name' end
+local function thename(s) old_feature_list.name = s end
+local function issub (v) old_feature_list.sub = v end
+local function istrue (s) old_feature_list[s] = true end
+local function isfalse(s) old_feature_list[s] = false end
+local function iskey (k,v) old_feature_list[k] = v end
local P, S, R, C = lpeg.P, lpeg.S, lpeg.R, lpeg.C
@@ -180,7 +187,8 @@ local spaces = P(" ")^0
local namespec = (1-S("/:("))^1
local filespec = (R("az", "AZ") * P(":"))^-1 * (1-S(":("))^1
local stylespec = spaces * P("/") * (((1-P(":"))^0)/isstyle) * spaces
-local filename = (P("file:")/isfile * (filespec/thename)) + (P("[") * P(true)/isname * (((1-P("]"))^0)/thename) * P("]"))
+local filename = (P("file:")/isfile * (filespec/thename))
+ + (P("[") * P(true)/isname * (((1-P("]"))^0)/thename) * P("]"))
local fontname = (P("name:")/isname * (namespec/thename)) + P(true)/issome * (namespec/thename)
local sometext = (R("az","AZ","09") + S("+-.,"))^1
local truevalue = P("+") * spaces * (sometext/istrue)
@@ -190,67 +198,318 @@ local somevalue = sometext/istrue
local subvalue = P("(") * (C(P(1-S("()"))^1)/issub) * P(")") -- for Kim
local option = spaces * (keyvalue + falsevalue + truevalue + somevalue) * spaces
local options = P(":") * spaces * (P(";")^0 * option)^0
-local pattern = (filename + fontname) * subvalue^0 * stylespec^0 * options^0
-
-local function colonized(specification) -- xetex mode
- feature_list = { }
- lpeg.match(pattern,specification.specification)
- feature_list = set_default_features(feature_list)
- if feature_list.style then
- specification.style = feature_list.style
- feature_list.style = nil
+local oldsyntax = (filename + fontname) * subvalue^0 * stylespec^0 * options^0
+
+--- to be annihilated
+local function old_behavior (specification) -- xetex mode
+ old_feature_list = { }
+ lpeg.match(oldsyntax,specification.specification)
+ old_feature_list = set_default_features(old_feature_list)
+ if old_feature_list.style then
+ specification.style = old_feature_list.style
+ old_feature_list.style = nil
end
- if feature_list.optsize then
- specification.optsize = feature_list.optsize
- feature_list.optsize = nil
+ if old_feature_list.optsize then
+ specification.optsize = old_feature_list.optsize
+ old_feature_list.optsize = nil
end
- if feature_list.name then
- if resolvers.findfile(feature_list.name, "tfm") then
- feature_list.lookup = "file"
- feature_list.name = file.addsuffix(feature_list.name, "tfm")
- elseif resolvers.findfile(feature_list.name, "ofm") then
- feature_list.lookup = "file"
- feature_list.name = file.addsuffix(feature_list.name, "ofm")
+ if old_feature_list.name then
+ if resolvers.findfile(old_feature_list.name, "tfm") then
+ old_feature_list.lookup = "file"
+ old_feature_list.name = file.addsuffix(old_feature_list.name, "tfm")
+ elseif resolvers.findfile(old_feature_list.name, "ofm") then
+ old_feature_list.lookup = "file"
+ old_feature_list.name = file.addsuffix(old_feature_list.name, "ofm")
end
- specification.name = feature_list.name
- feature_list.name = nil
+ specification.name = old_feature_list.name
+ old_feature_list.name = nil
end
- if feature_list.lookup then
- specification.lookup = feature_list.lookup
- feature_list.lookup = nil
+ --- this test overwrites valid file: requests for xetex bracket
+ --- syntax
+ if old_feature_list.lookup then
+ specification.lookup = old_feature_list.lookup
+ old_feature_list.lookup = nil
end
- if feature_list.sub then
- specification.sub = feature_list.sub
- feature_list.sub = nil
+ if old_feature_list.sub then
+ specification.sub = old_feature_list.sub
+ old_feature_list.sub = nil
end
- if not feature_list.mode then
+ if not old_feature_list.mode then
-- if no mode is set, use our default
- feature_list.mode = fonts.mode
+ old_feature_list.mode = fonts.mode
+ end
+ specification.features.normal = fonts.handlers.otf.features.normalize(old_feature_list)
+ return specification
+end
+
+--fonts.definers.registersplit(":",old_behavior,"cryptic")
+--fonts.definers.registersplit("", old_behavior,"more cryptic") -- catches \font\text=[names]
+--obsolete]==]--
+
+-----------------------------------------------------------------------
+--- request syntax parser 2.2
+-----------------------------------------------------------------------
+--- the luaotfload font request syntax (see manual)
+--- has a canonical form:
+---
+--- \font<csname>=<prefix>:<identifier>:<features>
+---
+--- where
+--- <csname> is the control sequence that activates the font
+--- <prefix> is either “file” or “name”, determining the lookup
+--- <identifer> is either a file name (no path) or a font
+--- name, depending on the lookup
+--- <features> is a list of switches or options, separated by
+--- semicolons or commas; a switch is of the form “+” foo
+--- or “-” foo, options are of the form lhs “=” rhs
+---
+--- however, to ensure backward compatibility we also have
+--- support for Xetex-style requests.
+---
+--- for the Xetex emulation see:
+--- · The XeTeX Reference Guide by Will Robertson, 2011
+--- · The XeTeX Companion by Michel Goosens, 2010
+--- · About XeTeX by Jonathan Kew, 2005
+---
+---
+--- caueat emptor.
+--- the request is parsed into one of **four** different
+--- lookup categories: the regular ones, file and name,
+--- as well as the Xetex compatibility ones, path and anon.
+--- (maybe a better choice of identifier would be “ambig”.)
+---
+--- according to my reconstruction, the correct chaining
+--- of the lookups for each category is as follows:
+---
+--- | File -> ( db/filename lookup;
+--- db/basename lookup;
+--- kpse.find_file() )
+--- | Name -> ( names.resolve() )
+--- | Path -> ( db/filename lookup;
+--- db/basename lookup;
+--- kpse.find_file();
+--- fullpath lookup )
+--- | Anon -> ( names.resolve(); (* most general *)
+--- db/filename lookup;
+--- db/basename lookup;
+--- kpse.find_file();
+--- fullpath lookup )
+---
+--- the database should be generated only if the chain has
+--- been completed, and then only once.
+---
+--- caching of successful lookups is essential. we need
+--- an additional subtable "cached" in the database. it
+--- should be nil’able by issuing luaotfload-tool --flush or
+--- something. if a cache miss is followed by a successful
+--- lookup, then it will be counted as new addition to the
+--- cache. we also need a config option to ignore caching.
+---
+--- also everything has to be finished by tomorrow at noon.
+---
+-----------------------------------------------------------------------
+
+
+local stringlower = string.lower
+
+local toboolean = function (s)
+ if s == "true" then return true end
+ if s == "false" then return false end
+--if s == "yes" then return true end --- Context style
+--if s == "no" then return false end
+ return s
+end
+
+local lpegmatch = lpeg.match
+local P, S, R = lpeg.P, lpeg.S, lpeg.R
+local C, Cc, Cf, Cg, Cs, Ct
+ = lpeg.C, lpeg.Cc, lpeg.Cf, lpeg.Cg, lpeg.Cs, lpeg.Ct
+
+--- terminals and low-level classes -----------------------------------
+--- note we could use the predefined ones from lpeg.patterns
+local dot = P"."
+local colon = P":"
+local featuresep = S",;"
+local slash = P"/"
+local equals = P"="
+local lbrk, rbrk = P"[", P"]"
+
+local spacing = S" \t\v"
+local ws = spacing^0
+
+local digit = R"09"
+local alpha = R("az", "AZ")
+local anum = alpha + digit
+local decimal = digit^1 * (dot * digit^0)^-1
+
+--- modifiers ---------------------------------------------------------
+--[[doc--
+ The slash notation: called “modifiers” (Kew) or “font options”
+ (Robertson, Goosens)
+ we only support the shorthands for italic / bold / bold italic
+ shapes, the rest is ignored.
+--doc]]--
+local style_modifier = (P"BI" + P"IB" + P"bi" + P"ib" + S"biBI")
+ / stringlower
+local other_modifier = P"S=" * decimal --- optical size; unsupported
+ + P"AAT" + P"aat" --- apple stuff; unsupported
+ + P"ICU" + P"icu" --- not applicable
+ + P"GR" + P"gr" --- sil stuff; unsupported
+local garbage_modifier = ((1 - colon - slash)^0 * Cc(false))
+local modifier = slash * (other_modifier --> ignore
+ + Cs(style_modifier) --> collect
+ + garbage_modifier) --> warn
+local modifier_list = Cg(Ct(modifier^0), "modifiers")
+
+--- lookups -----------------------------------------------------------
+local fontname = C((1-S"/:(")^1) --- like luatex-fonts
+local prefixed = P"name:" * ws * Cg(fontname, "name")
+ + P"file:" * ws * Cg(fontname, "file")
+local unprefixed = Cg(fontname, "anon")
+local path_lookup = lbrk * Cg(C((1-rbrk)^1), "path") * rbrk
+
+--- features ----------------------------------------------------------
+local field = (anum + S"+-.")^1 --- sic!
+--- assignments are “lhs=rhs”
+--- switches are “+key” | “-key”
+local assignment = C(field) * ws * equals * ws * (field / toboolean)
+local switch = P"+" * ws * C(field) * Cc(true)
+ + P"-" * ws * C(field) * Cc(false)
+ + C(field) * Cc(true) -- catch crap
+local feature_expr = ws * Cg(assignment + switch) * ws
+local feature_list = Cf(Ct""
+ * feature_expr
+ * (featuresep * feature_expr)^0
+ , rawset)
+ * featuresep^-1
+
+--- other -------------------------------------------------------------
+--- This rule is present in the original parser. It sets the “sub”
+--- field of the specification which allows addressing a specific
+--- font inside a TTC container. Neither in Luatex-Fonts nor in
+--- Luaotfload is this documented, so we might as well silently drop
+--- it. However, as backward compatibility is one of our prime goals we
+--- just insert it here and leave it undocumented until someone cares
+--- to ask. (Note: afair subfonts are numbered, but this rule matches a
+--- string; I won’t mess with it though until someone reports a
+--- problem.)
+--- local subvalue = P("(") * (C(P(1-S("()"))^1)/issub) * P(")") -- for Kim
+--- Who’s Kim?
+--- Note to self: subfonts apparently start at index 0. Tested with
+--- Cambria.ttc that includes “Cambria Math” at 0 and “Cambria” at 1.
+--- Other values cause luatex to segfault.
+local subfont = P"(" * Cg((1 - S"()")^1, "sub") * P")"
+--- top-level rules ---------------------------------------------------
+--- \font\foo=<specification>:<features>
+local features = Cg(feature_list, "features")
+local specification = (prefixed + unprefixed)
+ * subfont^-1
+ * modifier_list^-1
+local font_request = Ct(path_lookup * (colon^-1 * features)^-1
+ + specification * (colon * features)^-1)
+
+-- lpeg.print(font_request)
+--- new parser: 632 rules
+--- old parser: 230 rules
+
+local import_values = {
+ --- That’s what the 1.x parser did, not quite as graciously,
+ --- with an array of branch expressions.
+ -- "style", "optsize",--> from slashed notation; handled otherwise
+ "lookup", "sub" --[[‽]], "mode",
+}
+
+local lookup_types = { "anon", "file", "name", "path" }
+
+local select_lookup = function (request)
+ for i=1, #lookup_types do
+ local lookup = lookup_types[i]
+ local value = request[lookup]
+ if value then
+ return lookup, value
+ end
+ end
+end
+
+local supported = {
+ b = "bold",
+ i = "italic",
+ bi = "bolditalic",
+ aat = false,
+ icu = false,
+ gr = false,
+}
+
+local handle_slashed = function (modifiers)
+ local style, optsize
+ for i=1, #modifiers do
+ local mod = modifiers[i]
+ if supported[mod] then
+ style = supported[mod]
+ --elseif stringfind(v, "^s=") then
+ elseif stringsub(v, 1, 2) == "s=" then
+ local val = stringsub(v, 3)
+ optsize = val
+ elseif stylename == false then
+ report("log", 0,
+ "load", "unsupported font option: %s", v)
+ elseif not stringis_empty(v) then
+ style = stringgsub(v, "[^%a%d]", "")
+ end
end
- specification.features.normal = fonts.handlers.otf.features.normalize(feature_list)
+ return style, optsize
+end
+
+--- spec -> spec
+local handle_request = function (specification)
+ local request = lpegmatch(font_request,
+ specification.specification)
+ local lookup, name = select_lookup(request)
+ request.features = set_default_features(request.features)
+
+ if name then
+ specification.name = name
+ specification.lookup = lookup or specification.lookup
+ end
+
+ if request.modifiers then
+ local style, optsize = handle_slashed(request.modifiers)
+ specification.style, specification.optsize = style, optsize
+ end
+
+ for n=1, #import_values do
+ local feat = import_values[n]
+ local newvalue = request.features[feat]
+ if newvalue then
+ specification[feat] = request.features[feat]
+ request.features[feat] = nil
+ end
+ end
+
+ --- The next line sets the “rand” feature to “random”; I haven’t
+ --- investigated it any further (luatex-fonts-ext), so it will
+ --- just stay here.
+ specification.features.normal
+ = fonts.handlers.otf.features.normalize(request.features)
return specification
end
-fonts.definers.registersplit(":",colonized,"cryptic")
-fonts.definers.registersplit("", colonized,"more cryptic") -- catches \font\text=[names]
-
---- TODO below section is literally the same in luatex-fonts-def
---- why is it here?
---function fonts.definers.applypostprocessors(tfmdata)
--- local postprocessors = tfmdata.postprocessors
--- if postprocessors then
--- for i=1,#postprocessors do
--- local extrahash = postprocessors[i](tfmdata) -- after scaling etc
--- if type(extrahash) == "string" and extrahash ~= "" then
--- -- e.g. a reencoding needs this
--- extrahash = string.gsub(lower(extrahash),"[^a-z]","-")
--- tfmdata.properties.fullname = format("%s-%s",tfmdata.properties.fullname,extrahash)
--- end
--- end
--- end
--- return tfmdata
---end
+local compare_requests = function (spec)
+ local old = old_behavior(spec)
+ local new = handle_request(spec)
+ return new
+end
+
+--fonts.definers.registersplit(":", compare_requests, "cryptic")
+--fonts.definers.registersplit("", compare_requests, "more cryptic") -- catches \font\text=[names]
+
+fonts.definers.registersplit(":", handle_request, "cryptic")
+fonts.definers.registersplit("", handle_request, "more cryptic") -- catches \font\text=[names]
+
+--fonts.definers.registersplit(":",old_behavior,"cryptic")
+--fonts.definers.registersplit("", old_behavior,"more cryptic") -- catches \font\text=[names]
+
---[[ end included font-ltx.lua ]]
--[[doc--
diff --git a/luaotfload-merged.lua b/luaotfload-merged.lua
index 0cf2ce4..bf22bac 100644
--- a/luaotfload-merged.lua
+++ b/luaotfload-merged.lua
@@ -1,6 +1,6 @@
-- merged file : luatex-fonts-merged.lua
-- parent file : luatex-fonts.lua
--- merge date : 04/26/13 15:13:48
+-- merge date : 04/29/13 20:30:03
do -- begin closure to overcome local limits and interference
@@ -3039,7 +3039,6 @@ function caches.getreadablepaths(category,subcategory)
end
local function makefullname(path,name)
if path and path~="" then
- name="temp-"..name
return file.addsuffix(file.join(path,name),"lua"),file.addsuffix(file.join(path,name),usingjit and "lub" or "luc")
end
end
@@ -3093,7 +3092,7 @@ function caches.compile(data,luaname,lucname)
d=table.serialize(data,true)
end
if d and d~="" then
- local f=io.open(lucname,'w')
+ local f=io.open(lucname,'wb')
if f then
local s=loadstring(d)
if s then
@@ -3218,7 +3217,7 @@ function containers.content(container,name)
return container.storage[name]
end
function containers.cleanname(name)
- return (gsub(lower(name),"[^%w%d]+","-"))
+ return (gsub(lower(name),"[^%w\128-\255]+","-"))
end
end -- closure
@@ -4829,22 +4828,35 @@ end
local fonts=fonts
fonts.names=fonts.names or {}
fonts.names.version=1.001
-fonts.names.basename="luatex-fonts-names.lua"
+fonts.names.basename="luatex-fonts-names"
fonts.names.new_to_old={}
fonts.names.old_to_new={}
+fonts.names.cache=containers.define("fonts","data",fonts.names.version,true)
local data,loaded=nil,false
local fileformats={ "lua","tex","other text files" }
+function fonts.names.reportmissingbase()
+ texio.write("<missing font database, run: mtxrun --script fonts --reload --simple>")
+ fonts.names.reportmissingbase=nil
+end
+function fonts.names.reportmissingname()
+ texio.write("<unknown font in database, run: mtxrun --script fonts --reload --simple>")
+ fonts.names.reportmissingname=nil
+end
function fonts.names.resolve(name,sub)
if not loaded then
local basename=fonts.names.basename
if basename and basename~="" then
- for i=1,#fileformats do
- local format=fileformats[i]
- local foundname=resolvers.findfile(basename,format) or ""
- if foundname~="" then
- data=dofile(foundname)
- texio.write("<font database loaded: ",foundname,">")
- break
+ data=containers.read(fonts.names.cache,basename)
+ if not data then
+ basename=file.addsuffix(basename,"lua")
+ for i=1,#fileformats do
+ local format=fileformats[i]
+ local foundname=resolvers.findfile(basename,format) or ""
+ if foundname~="" then
+ data=dofile(foundname)
+ texio.write("<font database loaded: ",foundname,">")
+ break
+ end
end
end
end
@@ -4860,9 +4872,12 @@ function fonts.names.resolve(name,sub)
else
return filename,false
end
- else
+ elseif fonts.names.reportmissingname then
+ fonts.names.reportmissingname()
return name,false
end
+ elseif fonts.names.reportmissingbase then
+ fonts.names.reportmissingbase()
end
end
fonts.names.resolvespec=fonts.names.resolve
@@ -7713,7 +7728,7 @@ function injections.handler(head,where,keep)
if rlmode and rlmode>=0 then
n.xoffset=p.xoffset-p.width+d[1]
else
- n.xoffset=p.xoffset-d[1]-x
+ n.xoffset=p.xoffset-d[1]-x
end
end
else
@@ -10054,6 +10069,8 @@ for s=1,#datasets do
if ok then
success=true
break
+ elseif not start then
+ break
end
end
else
diff --git a/luaotfload-override.lua b/luaotfload-override.lua
index 7ee9730..2b9ef8b 100644
--- a/luaotfload-override.lua
+++ b/luaotfload-override.lua
@@ -81,10 +81,12 @@ logs.names_report = function (mode, lvl, ...)
end
end
---- This will vanish as soon as Hans fixes it in data-con
---- https://github.com/lualatex/luaotfload/issues/17
+if containers then
+ --- This will vanish as soon as Hans fixes it in data-con
+ --- https://github.com/lualatex/luaotfload/issues/17
-containers.cleanname = function (name)
- return (string.gsub(string.lower(name),"[^%w%d\128\255]+","-"))
+ containers.cleanname = function (name)
+ return (string.gsub(string.lower(name),"[^%w%d\128\255]+","-"))
+ end
end
-- vim:tw=71:sw=4:ts=4:expandtab
diff --git a/fontdbutil.lua b/luaotfload-tool.lua
index 1aa99d0..bb935cf 100755
--- a/fontdbutil.lua
+++ b/luaotfload-tool.lua
@@ -28,14 +28,14 @@ string.quoted = string.quoted or function (str)
return string.format("%q",str)
end
-dofile(loader_path)
+require(loader_path)
--[[doc--
Depending on how the script is called we change its behavior.
For backwards compatibility, moving or symlinking the script to a
file name starting with \fileent{mkluatexfontdb} will cause it to
trigger a database update on every run.
-Running as \fileent{fontdbutil} -- the new name -- will do this upon
+Running as \fileent{luaotfload-tool} -- the new name -- will do this upon
request only.
There are two naming conventions followed here: firstly that of
@@ -61,10 +61,9 @@ do -- we don’t have file.basename and the likes yet, so inline parser ftw
local extension = dot * (1 - slash - dot)^1
local p_basename = path^-1 * C(thename) * extension^-1 * P(-1)
- -- if stringfind(stringlower(arg[0]), "^fontdbutil") then
local self = lpegmatch(p_basename, stringlower(arg[0]))
- if self == "fontdbutil" then
- config.luaotfload.self = "fontdbutil"
+ if self == "luaotfload-tool" then
+ config.luaotfload.self = "luaotfload-tool"
else
config.luaotfload.self = "mkluatexfontdb"
end
@@ -101,7 +100,7 @@ local db_src_out = names.path.dir.."/"..names.path.basename
local db_bin_out = file.replacesuffix(db_src_out, "luc")
local help_messages = {
- fontdbutil = [[
+ ["luaotfload-tool"] = [[
Usage: %s [OPTION]...
@@ -119,7 +118,7 @@ This tool is part of the luaotfload package. Valid options are:
-V --version print version and exit
-h --help print this message
- --alias=<name> force behavior of “fontdbutil” or legacy
+ --alias=<name> force behavior of “luaotfload-tool” or legacy
“mkluatexfontdb”
-------------------------------------------------------------------------------
DATABASE
@@ -155,7 +154,7 @@ Valid options:
-vvv print all steps of directory searching
-V --version print version and exit
-h --help print this message
- --alias=<name> force behavior of “fontdbutil” or legacy
+ --alias=<name> force behavior of “luaotfload-tool” or legacy
“mkluatexfontdb”
The font database will be saved to
@@ -167,7 +166,7 @@ The font database will be saved to
local help_msg = function ( )
local template = help_messages[config.luaotfload.self]
- or help.messages.fontdbutil
+ or help_messages["luaotfload-tool"]
texiowrite_nl(stringformat(template, config.luaotfload.self, db_src_out, db_bin_out))
end
@@ -276,14 +275,20 @@ actions.query = function (job)
optsize = 0,
}
- local foundname, _whatever, success =
+ local foundname, subfont, success =
fonts.names.resolve(nil, nil, tmpspec)
if success then
logs.names_report(false, 1,
"resolve", "Font “%s” found!", query)
- logs.names_report(false, 1,
- "resolve", "Resolved file name “%s”", foundname)
+ if subfont then
+ logs.names_report(false, 1, "resolve",
+ "Resolved file name “%s”, subfont nr. “%s”",
+ foundname, subfont)
+ else
+ logs.names_report(false, 1,
+ "resolve", "Resolved file name “%s”", foundname)
+ end
if job.show_info then
show_font_info(foundname)
end
@@ -388,9 +393,7 @@ local process_cmdline = function ( ) -- unit -> jobspec
if config.luaotfload.self == "mkluatexfontdb" then
action_pending["generate"] = true
- print(result.log_level)
result.log_level = math.max(2, result.log_level)
- print(result.log_level)
end
return result
end
diff --git a/luaotfload.dtx b/luaotfload.dtx
index 18e01d8..fd71485 100644
--- a/luaotfload.dtx
+++ b/luaotfload.dtx
@@ -130,6 +130,8 @@ and the derived files
\setsansfont[Ligatures=TeX,Scale=MatchLowercase]{Iwona Medium}
%setmathfont{XITS Math}
+\usepackage{hologo}
+
\newcommand\TEX {\TeX\xspace}
\newcommand\LUA {Lua\xspace}
\newcommand\PDFTEX {pdf\TeX\xspace}
@@ -160,6 +162,8 @@ and the derived files
\renewcommand\partname{Part}%% gets rid of the stupid “file” heading
+\usepackage{syntax}%% bnf for font request syntax
+
\VerbatimFootnotes
\begin{document}
\DocInput{luaotfload.dtx}%
@@ -244,56 +248,177 @@ and the derived files
%
% \section{Loading Fonts}
%
-% \identifier{luaotfload} supports an extended font loading syntax:
+% \identifier{luaotfload} supports an extended font request syntax:
%
-% \begin{center}
+% \begin{quote}
% |\font\foo={|%
% \meta{prefix}|:|%
% \meta{font name}|:|%
% \meta{font features}|}|%
% \meta{\TEX font features}
-% \end{center}
+% \end{quote}
%
% \noindent
% The curly brackets are optional and escape the spaces in the enclosed
-% font name (alternatively, double quotes serve the same purpose).
-% The individual parts of the syntax are:
-%
-% \paragraph{Prefix}
-%
-% The \meta{prefix} is either |file:| or |name:|.
-% It determines whether the font loader should interpret the request as a
-% file name or font name, respectively, which again influences how it
-% will attempt to locate the font.
-% The prefix can be omitted, in which case |name:| is assumed.
-%
-%% \iffalse%% how am i supposed to friggin comment stuff in a dtx???
-%% TODO
-%% it would appear that the next paragraph is incorrect; I get
-%% name: lookups regardless unless the font file is actually
-%% in CWD
-%% \fi
-%% For compatibility with \XETEX, surrounding the \meta{font name} with
-%% square brackets is synonymous to using the |file:| prefix.
+% font name.
+% Alternatively, double quotes serve the same purpose.
+% A selection of individual parts of the syntax are discussed below;
+% for a more formal description see figure \ref{font-syntax}.
+%
+% \begin{figure}[b]
+% \setlength\grammarparsep{12pt plus 2pt minus 2pt}
+% \setlength\grammarindent{5cm}
+% \begingroup
+% \small
+% \begin{grammar}
+% <definition> ::= `\\font', {\sc csname}, `=', <font request>, [ <size> ] ;
+%
+% <size> ::= `at', {\sc dimension} ;
+%
+% <font request> ::= `"', <unquoted font request> `"'
+% \alt `{', <unquoted font request> `}'
+% \alt <unquoted font request> ;
+%
+% <unquoted font request> ::= <specification>, [`:', <feature list> ]
+% \alt `[', <path lookup> `]', [ [`:'], <feature list> ] ;
+%
+% <specification> ::= <prefixed spec>, [ <subfont no> ], \{ <modifier> \}
+% \alt <anon lookup>, \{ <modifier> \} ;
+%
+% <prefixed spec> ::= `file:', <file lookup>
+% \alt `name:', <name lookup> ;
+%
+% <file lookup> ::= \{ <name character> \} ;
+%
+% <name lookup> ::= \{ <name character> \} ;
+%
+% <anon lookup> ::= {\sc tfmname} | <name lookup> ;
+%
+% <path lookup> ::= \{ {\sc all_characters} - `]' \} ;
+%
+% <modifier> ::= `/', (`I' | `B' | `BI' | `IB') ;
+%
+% <subfont no> ::= `(', \{ {\sc digit} \}, `)' ;
+%
+% <feature list> ::= <feature expr>, \{ `;', <feature expr> \} ;
+%
+% <feature expr> ::= {\sc feature_id}, `=', {\sc feature_value}
+% \alt <feature switch>, {\sc feature_id} ;
+%
+% <feature switch> ::= `+' | `-' ;
+%
+% <name character> ::= {\sc all_characters} - ( `(' | `/' | `:' ) ;
+% \end{grammar}
+% \endgroup
+% \caption{Font request syntax.
+% Braces or double quotes around the
+% \emphasis{specification} rule will
+% preserve whitespace in file names.
+% In addition to the font style modifiers
+% (\emphasis{slash-notation}) given above, there
+% are others that are recognized but will be silently
+% ignored: {\ttfamily aat},
+% {\ttfamily icu}, and
+% {\ttfamily gr}.
+% The special terminals are:
+% {\sc feature\textunderscore id} for a valid font
+% feature name and
+% {\sc feature\textunderscore value} for the corresponding
+% value.
+% {\sc tfmname} is the name of a \abbrev{tfm} file.
+% {\sc digit} again refers to bytes 48--57, and
+% {\sc all\textunderscore characters} to all byte values.
+% {\sc csname} and {\sc dimension} are the \TEX concepts.}
+% \label{font-syntax}
+% \end{figure}
%
+% \subsection{Prefix -- the \identifier{luaotfload}{ }Way}
+%
+% In \identifier{luaotfload}, the canonical syntax for font requests
+% requires a \emphasis{prefix}:
+% \begin{quote}
+% |\font\fontname=|\meta{prefix}|:|\meta{fontname}\dots
+% \end{quote}
+% where \meta{prefix} is either \verb|file:| or \verb|name:|.
+% It determines whether the font loader should interpret the request as
+% a \emphasis{file name} or
+% \emphasis{font name}, respectively,
+% which again influences how it will attempt to locate the font.
+% Examples for font names are
+% “Latin Modern Italic”,
+% “GFS Bodoni Rg”, and
+% “PT Serif Caption”
+% -- they are the human readable identifiers
+% usually listed in drop-down menus and the like.
% In order for fonts installed both in system locations and in your
% \fileent{texmf} to be accessible by font name, \identifier{luaotfload} must
% first collect the metadata included in the files.
-% Please refer to section ~\ref{sec:fontdb} below for instructions on how to
+% Please refer to section~\ref{sec:fontdb} below for instructions on how to
% create the database.
%
-% \paragraph{Font name}
+% File names are whatever your file system allows them to be, except
+% that that they may not contain the characters
+% \verb|(|,
+% \verb|:|, and
+% \verb|/|.
+% As is obvious from the last exception, the \verb|file:| lookup will
+% not process paths to the font location -- only those
+% files found when generating the database are addressable this way.
+% Continue below in the \XETEX section if you need to load your fonts
+% by path.
+% The file names corresponding to the example font names above are
+% \fileent{lmroman12-italic.otf},
+% \fileent{GFSBodoni.otf}, and
+% \fileent{PTZ56F.ttf}.
+%
+% \subsection{\hologo{XeTeX} Compatibility Layer}
+%
+% In addition to the regular prefixed requests, \identifier{luaotfload}
+% accepts loading fonts the \XETEX way.
+% There are again two modes: bracketed and unbracketed.
+% A bracketed request looks as follows.
%
-% The \meta{font name} can be either a font filename or actual font
-% name based on the \meta{prefix} as mentioned above.
+% \begin{quote}
+% |\font\fontname=[|\meta{path to file}|]|
+% \end{quote}
%
-% A filename request may optionally include the absolute path to the font file,
-% allowing for fonts outside the standard locations to be loaded as well.
-% If no path is specified, then \identifier{kpathsea} is used to locate the
-% font (which will typically be in the \fileent{texmf} tree or the
-% current directory).
+% \noindent
+% Inside the square brackets, every character except for a closing
+% bracket is permitted, allowing for specifying paths to a font file.
+% Naturally, path-less file names are equally valid and processed the
+% same way as an ordinary \verb|file:| lookup.
+%
+% \begin{quote}
+% |\font\fontname=|\meta{font name} \dots
+% \end{quote}
%
-% \subparagraph{Examples for loading by file name}
+% Unbracketed (or, for lack of a better word: \emphasis{anonymous})
+% font requests resemble the conventional \TEX syntax.
+% However, they have a broader spectrum of possible interpretations:
+% before anything else, \identifier{luaotfload} attempts to load a
+% traditional \TEX Font Metric (\abbrev{tfm} or \abbrev{ofm}).
+% If this fails, it performs a \verb|name:| lookup, which itself will
+% fall back to a \verb|file:| lookup if no database entry matches
+% \meta{font name}.
+%
+% Furthermore, \identifier{luaotfload} supports the slashed (shorthand)
+% font style notation from \XETEX.
+%
+% \begin{quote}
+% |\font\fontname=|\meta{font name}|/|\meta{modifier}\dots
+% \end{quote}
+%
+% \noindent
+% Currently, four style modifiers are supported:
+% \verb|I| for italic shape,
+% \verb|B| for bold weight,
+% \verb|BI| or \verb|IB| for the combination of both.
+% Other “slashed” modifiers are too specific to the \XETEX engine and
+% have no meaning in \LUATEX.
+%
+% \subsection{Examples}
+%
+% \subsubsection{Loading by File Name}
%
% For example, conventional \abbrev{type1} font can be loaded with a \verb|file:|
% request like so:
@@ -305,8 +430,11 @@ and the derived files
% \end{quote}
%
% The \OpenType version of Janusz Nowacki’s font \emphasis{Antykwa
-% Półtawskiego} (in \TEX Live) in its condensed variant can be loaded as
-% follows:
+% Półtawskiego}\footnote{%
+% \url{http://jmn.pl/antykwa-poltawskiego/}, also available in
+% in \TEX Live.
+% }
+% in its condensed variant can be loaded as follows:
%
% \begin{quote}
% \begin{verbatim}
@@ -324,7 +452,7 @@ and the derived files
% \end{verbatim}
% \end{quote}
%
-% \subparagraph{Examples for loading by font name}
+% \subsubsection{Loading by Font Name}
%
% The \verb|name:| lookup does not depend on cryptic filenames:
%
@@ -342,6 +470,7 @@ and the derived files
% \end{verbatim}
% \end{quote}
%
+% \noindent
% Which fits nicely with the whole set:
%
% \begin{quote}
@@ -360,9 +489,62 @@ and the derived files
% \end{verbatim}
% \end{quote}
%
-% \paragraph{Font features}
+% \subsubsection{Modifiers}
+%
+% If the entire \emphasis{Iwona} family\footnote{%
+% \url{http://jmn.pl/kurier-i-iwona/},
+% also in \TEX Live.
+% }
+% is installed in some location accessible by \identifier{luaotfload},
+% the regular shape can be loaded as follows:
+%
+% \begin{quote}
+% \begin{verbatim}
+% \font\iwona=Iwona at 20pt
+% \end{verbatim}
+% \end{quote}
+%
+% \noindent
+% To load the most common of the other styles, the slash notation can
+% be employed as shorthand:
+%
+% \begin{quote}
+% \begin{verbatim}
+% \font\iwonaitalic =Iwona/I at 20pt
+% \font\iwonabold =Iwona/B at 20pt
+% \font\iwonabolditalic=Iwona/BI at 20pt
+% \end{verbatim}
+% \end{quote}
+%
+% \noindent
+% which is equivalent to these full names:
+%
+% \begin{quote}
+% \begin{verbatim}
+% \font\iwonaitalic ="Iwona Italic" at 20pt
+% \font\iwonabold ="Iwona Bold" at 20pt
+% \font\iwonabolditalic="Iwona BoldItalic" at 20pt
+% \end{verbatim}
+% \end{quote}
+%
+% \section{Font features}
+%
+% \emphasis{Font features} are the second to last component in the
+% general scheme for font requests:
+%
+% \begin{quote}
+% |\font\foo={|%
+% \meta{prefix}|:|%
+% \meta{font name}|:|%
+% \meta{font features}|}|%
+% \meta{\TEX font features}
+% \end{quote}
+%
+% \noindent
+% If style modifiers are present (\XETEX style), they must precede
+% \meta{font features}.
%
-% \meta{font features} is semicolon-separated list of feature
+% The element \meta{font features} is a semicolon-separated list of feature
% tags\footnote{%
% Cf. \url{http://www.microsoft.com/typography/otspec/featurelist.htm}.
% }
@@ -587,8 +769,8 @@ and the derived files
% This is particularly noticeable if it occurs during a typesetting run.
% In any case, subsequent updates to the database will be quite fast.
%
-% \subsection[fontdbutil / mkluatexfontdb.lua]%
-% {\fileent{fontdbutil} /
+% \subsection[luaotfload-tool / mkluatexfontdb.lua]%
+% {\fileent{luaotfload-tool} /
% \fileent{mkluatexfontdb.lua}\footnote{%
% The script may be named just \fileent{mkluatexfontdb} in your
% distribution.
@@ -597,7 +779,7 @@ and the derived files
% It can still be desirable at times to do some of these steps
% manually, and without having to compile a document.
% To this end, \identifier{luaotfload} comes with the utility
-% \fileent{fontdbutil} that offers an interface to the database
+% \fileent{luaotfload-tool} that offers an interface to the database
% functionality.
% Being a \LUA script, there are two ways to run it:
% either make it executable (\verb|chmod +x| on unixoid systems) or
@@ -612,15 +794,15 @@ and the derived files
% \emphasis{Note}:
% On \abbrev{MS} \identifier{Windows} systems, the script can be run
% either by calling the wrapper application
-% \fileent{fontdbutil.exe} or as
-% \verb|texlua.exe fontdbutil|.
+% \fileent{luaotfload-tool.exe} or as
+% \verb|texlua.exe luaotfload-tool.lua|.
% }
% Invoked with the argument \verb|--update| it will perform a database
% update, scanning for fonts not indexed.
%
% \begin{quote}
% \begin{verbatim}
-% fontdbutil --update
+% luaotfload-tool --update
% \end{verbatim}
% \end{quote}
%
@@ -629,11 +811,11 @@ and the derived files
%
% \begin{quote}
% \begin{verbatim}
-% fontdbutil --update --force
+% luaotfload-tool --update --force
% \end{verbatim}
% \end{quote}
%
-% For sake of backwards compatibility, \fileent{fontdbutil} may be
+% For sake of backwards compatibility, \fileent{luaotfload-tool} may be
% renamed or symlinked to \fileent{mkluatexfontdb}.
% Whenever it is run under this name, it will update the database
% first, mimicking the behavior of earlier versions of
@@ -653,7 +835,7 @@ and the derived files
% The complete list is is given in table \ref{table-searchpaths}.
% Other paths can be specified by setting the environment variable
% \verb+OSFONTDIR+.
-% If it is non-empty, then search will be limited to the included
+% If it is non-empty, then search will be extended to the included
% directories.
%
% \begin{table}[t]
@@ -681,7 +863,7 @@ and the derived files
%
% \subsection{Querying from Outside}
%
-% \fileent{fontdbutil} also provides rudimentary means of
+% \fileent{luaotfload-tool} also provides rudimentary means of
% accessing the information collected in the font database.
% If the option \verb|--find=|\emphasis{name} is given, the script will
% try and search the fonts indexed by \identifier{luaotfload} for a
@@ -690,7 +872,7 @@ and the derived files
%
% \begin{quote}
% \begin{verbatim}
-% fontdbutil --find="Iwona Regular"
+% luaotfload-tool --find="Iwona Regular"
% \end{verbatim}
% \end{quote}
%
@@ -707,7 +889,7 @@ and the derived files
%
% \begin{quote}
% \begin{verbatim}
-% fontdbutil -F --find="Iwona Bright"
+% luaotfload-tool -F --find="Iwona Bright"
% \end{verbatim}
% \end{quote}
%
@@ -718,7 +900,7 @@ and the derived files
% using the \verb|-i| option (\verb|--info|).
% \begin{quote}
% \begin{verbatim}
-% fontdbutil -F --find="Iwona Light Italic"
+% luaotfload-tool -i --find="Iwona Light Italic"
% \end{verbatim}
% \end{quote}
% \noindent
@@ -727,7 +909,7 @@ and the derived files
% In \TEX Live: \fileent{texmf-dist/doc/luatex/base/luatexref-t.pdf}.
% }
%
-% \verb|fontdbutil --help| will list the available command line
+% \verb|luaotfload-tool --help| will list the available command line
% switches, including some not discussed in detail here.
%
% \subsection{Blacklisting Fonts}
@@ -736,7 +918,7 @@ and the derived files
% Some fonts are problematic in general, or just in \LUATEX.
% If you find that compiling your document takes far too long or eats
% away all your system’s memory, you can track down the culprit by
-% running \verb|fontdbutil -v| to increase verbosity.
+% running \verb|luaotfload-tool -v| to increase verbosity.
% Take a note of the \emphasis{filename} of the font that database
% creation fails with and append it to the file
% \fileent{luaotfload-blacklist.cnf}.
@@ -945,7 +1127,7 @@ and the derived files
% verbosity levels and redirecting log output to \fileent{stdout}:
%
% \begin{verbatim}
-% fontdbutil -fuvvv --log=stdout
+% luaotfload-tool -fuvvv --log=stdout
% \end{verbatim}
%
% If this fails, the font last printed to the terminal is likely to be
@@ -998,12 +1180,14 @@ local luaotfload = luaotfload
config = config or { }
config.luaotfload = config.luaotfload or { }
-luaotfload.prefer_merge = config.luaotfload.prefer_merge or true
+config.luaotfload.resolver = config.luaotfload.resolver or "normal"
+config.luaotfload.definer = config.luaotfload.definer or "patch"
+--luaotfload.prefer_merge = config.luaotfload.prefer_merge or true
luaotfload.module = {
name = "luaotfload",
version = 2.2,
- date = "2013/04/15",
+ date = "2013/04/29",
description = "OpenType layout system.",
author = "Elie Roux & Hans Hagen",
copyright = "Elie Roux",
@@ -1014,11 +1198,12 @@ local luatexbase = luatexbase
local type, next = type, next
local setmetatable = setmetatable
+local find_file = kpse.find_file
+local lfsisfile = lfs.isfile
local stringfind = string.find
-local stringsub = string.sub
-local stringmatch = string.match
local stringformat = string.format
-local find_file = kpse.find_file
+local stringmatch = string.match
+local stringsub = string.sub
local add_to_callback, create_callback =
luatexbase.add_to_callback, luatexbase.create_callback
@@ -1041,6 +1226,11 @@ luaotfload.font_definer = "patch" --- | “generic” | “old”
local error, warning, info, log =
luatexbase.provides_module(luaotfload.module)
+luaotfload.error = error
+luaotfload.warning = warning
+luaotfload.info = info
+luaotfload.log = log
+
% \end{macrocode}
% We set the minimum version requirement for \LUATEX to v0.76,
% because the font loader requires recent features like direct
@@ -1328,7 +1518,7 @@ add_to_callback("find_vf_file",
loadmodule"lib-dir.lua" --- required by luaofload-database.lua
loadmodule"override.lua" --- “luat-ovr”
-logs.set_loglevel(0)
+logs.set_loglevel(config.luaotfload.loglevel or 2)
% \end{macrocode}
% \CONTEXT does not support ofm, these lines were added in order to make it
@@ -1350,84 +1540,113 @@ loadmodule"database.lua" --- “font-nms”
loadmodule"colors.lua" --- “font-clr”
% \end{macrocode}
-% This hack makes fonts called with file method found by fonts.names.resolve
-% instead of just trying to find them with \identifier{kpse}.
-% It is necessary in cases when font files are not reachable by
-% \identifier{kpse} but present in the database, a quite common case
-% under Linux.
-%
-% As of 2013-04-24 we have a workaround in the resolver that handles
-% \verb|file:| lookups diverted this way.
-% It requires some overhead due to additional extra data saved in the
-% names database, and might vanish entirely once the font request syntax
-% is understood.
-% Until then it is considered a kludge, like the hack below.
+% Relying on the \verb|name:| resolver for everything has been the source
+% of permanent trouble with the database.
+% With the introduction of the new syntax parser we now have enough
+% granularity to distinguish between the \XETEX emulation layer and the
+% genuine \verb|name:| and \verb|file:| lookups of \LUATEX-Fonts.
+% Another benefit is that we can now easily plug in or replace new lookup
+% behaviors if necessary.
+% The name resolver remains untouched, but it calls
+% \luafunction{fonts.names.resolve()} internally anyways (see
+% \fileent{luaotfload-database.lua}).
%
% \begin{macrocode}
-fonts.definers.resolvers.file = function (specification)
- specification.name = fonts.names.resolve('', '', specification)
+local request_resolvers = fonts.definers.resolvers
+local formats = fonts.formats
+formats.ofm = "type1"
+
+% \end{macrocode}
+% \identifier{luaotfload} promises easy access to system fonts.
+% Without additional precautions, this cannot be achieved by
+% \identifier{kpathsea} alone, because it searches only the
+% \fileent{texmf} directories by default.
+% Although it is possible for \identifier{kpathsea} to include extra
+% paths by adding them to the \verb|OSFONTDIR| environment variable,
+% this is still short of the goal »\emphasis{it just works!}«.
+% When building the font database \identifier{luaotfload} scans
+% system font directories anyways, so we already have all the
+% information for looking sytem fonts.
+% With the release version 2.2 the file names are indexed in the database
+% as well and we are ready to resolve \verb|file:| lookups this way.
+% Thus we no longer need to call the \identifier{kpathsea} library in
+% most cases when looking up font files, only when generating the database.
+%
+% \begin{macrocode}
+request_resolvers.file = function (specification)
+ --local found = fonts.names.crude_file_lookup(specification.name)
+ local found = fonts.names.crude_file_lookup_verbose(specification.name)
+ specification.name = found[1]
+ --if format then specification.forced = format end
end
% \end{macrocode}
-% We create a callback for patching fonts on the fly, to be used by other
-% packages.
-% It initially contains the empty function that we are going to override
-% below.
+% We classify as \verb|anon:| those requests that have neither a
+% prefix nor brackets. According to Khaled\footnote{%
+% \url{https://github.com/phi-gamma/luaotfload/issues/4#issuecomment-17090553}.
+% }
+% they are the \XETEX equivalent of a \verb|name:| request, so we will be
+% treating them as such.
%
% \begin{macrocode}
-create_callback("luaotfload.patch_font", "simple", dummy_function)
+--request_resolvers.anon = request_resolvers.name
% \end{macrocode}
-% This is a wrapper for the imported font loader.
-% As of 2013, everything it does appear to be redundand, so we won’t use
-% it unless somebody points out a cogent reason.
-% Nevertheless, it has been adapted to work with the current structure of
-% font data objects and will stay here for reference / until breakage is
-% reported.
-% \emphasis{TODO}
-% This one also enables patching fonts.
-% The current fontloader apparently comes with a dedicated mechanism for
-% that already: enhancers.
-% How those work remains to be figured out.
+% There is one drawback, though.
+% This syntax is also used for requesting fonts in \identifier{Type1}
+% (\abbrev{tfm}, \abbrev{ofm}) format.
+% These are essentially \verb|file:| lookups and must be caught before
+% the \verb|name:| resolver kicks in, lest they cause the database to
+% update.
+% Even if we were to require the \verb|file:| prefix for all
+% \identifier{Type1} requests, tests have shown that certain fonts still
+% include further fonts (e.~g. \fileent{omlgcb.ofm} will ask for
+% \fileent{omsecob.tfm}) \emphasis{using the old syntax}.
+% For this reason, we introduce an extra check with an early return.
%
% \begin{macrocode}
-local define_font_wrapper = function (...)
- --- we use “tfmdata” (not “fontdata”) for consistency with the
- --- font loader
- local tfmdata = fonts.definers.read(...)
- if type(tfmdata) == "table" and tfmdata.shared then
- local metadata = tfmdata.shared.rawdata.metadata
- local mathdata = metadata.math --- do all fonts have this field?
- if mathdata then
- local mathconstants = { } --- why new hash, not modify in place?
- local units_per_em = metadata.units_per_em
- local size = tfmdata.size
- for k,v in next, mathdata do
- --- afaics this is alread taken care of by
- --- definers.read
- if stringfind(k, "Percent") then
- -- keep percent values as is
- --print(k,v)
- mathconstants[k] = v
- else
- mathconstants[k] = v / units_per_em * size
- end
- end
- --- for \overwithdelims
- --- done by definers.read as well
- mathconstants.FractionDelimiterSize = 1.01 * size
- --- fontloader has 2.4 × size
- mathconstants.FractionDelimiterDisplayStyleSize = 2.39 * size
- tfmdata.MathConstants = mathconstants
+local type1_formats = { "tfm", "ofm", }
+
+request_resolvers.anon = function (specification)
+ local name = specification.name
+ for i=1, #type1_formats do
+ local format = type1_formats[i]
+ if resolvers.findfile(name, format) then
+ specification.name = file.addsuffix(name, format)
+ return
end
- call_callback("luaotfload.patch_font", tfmdata)
end
- return tfmdata
+ request_resolvers.name(specification)
+end
+
+% \end{macrocode}
+% Prior to version 2.2, \identifier{luaotfload} did not distinguish
+% \verb|file:| and \verb|path:| lookups, causing complications with the
+% resolver.
+% Now we test if the requested name is an absolute path in the file
+% system, otherwise we fall back to the \verb|file:| lookup.
+%
+% \begin{macrocode}
+request_resolvers.path = function (specification)
+ local exists, _ = lfsisfile(specification.name)
+ if not exists then -- resort to file: lookup
+ request_resolvers.file(specification)
+ end
end
% \end{macrocode}
+% We create a callback for patching fonts on the fly, to be used by other
+% packages.
+% It initially contains the empty function that we are going to override
+% below.
+%
+% \begin{macrocode}
+
+create_callback("luaotfload.patch_font", "simple", dummy_function)
+
+% \end{macrocode}
% \subsection{\CONTEXT override}
% We provide a simplified version of the original font definition
% callback.
@@ -1442,7 +1661,6 @@ local patch_defined_font = function (specification, size, id)
if type(tfmdata) == "table" then
call_callback("luaotfload.patch_font", tfmdata)
end
- -- inspect(table.keys(tfmdata))
return tfmdata
end
@@ -1455,59 +1673,26 @@ reset_callback("define_font")
%
% \begin{macrocode}
-if luaotfload.font_definer == "old" then
- add_to_callback("define_font",
- define_font_wrapper,
- "luaotfload.define_font",
- 1)
-elseif luaotfload.font_definer == "generic" then
+local font_definer = config.luaotfload.definer
+
+if font_definer == "generic" then
add_to_callback("define_font",
fonts.definers.read,
"luaotfload.define_font",
1)
-elseif luaotfload.font_definer == "patch" then
+elseif font_definer == "patch" then
add_to_callback("define_font",
patch_defined_font,
"luaotfload.define_font",
1)
end
---[[todo--
---- The manual promises coercion of the file: lookup if
---- the asked name is enclosed in brackets.
---- A couple things make me doubt that this is the case:
----
---- 1) there doesn’t appear to be code for these cases
---- 2) the brackets remain part of the file name
---- 3) we still get calls to names.resolve which
---- ignores the “lookup” field of the spec it gets
----
---- For this reason here is some code that a) coerces
---- file: lookups in these cases and b) strips the brackets
---- from the file name. As we *still* get name: lookups even
---- though this code is active I’ll just leave it here
---- for reference, ineffective as it is.
-do
- local getspecification, makespecification =
- fonts.definers.getspecification, fonts.definers.makespecification
-
- local analyze = function (specification, size)
- local lookup, name, sub, method, detail = getspecification(specification or "")
- local filename = stringmatch(name, "^%[(.*)%]$")
- if filename then
- lookup = "file" --> coerce file:
- name = filename --> remove brackets
- end
- return makespecification(specification, lookup, name, sub, method, detail, size)
- end
- fonts.definers.analyze = analyze
-end
---]]--
-
-loadmodule"features.lua" --- contains what was “font-ltx” and “font-otc”
+loadmodule"features.lua" --- contains what was “font-ltx” and “font-otc”
+loadmodule"auxiliary.lua" --- additionaly high-level functionality (new)
-- vim:tw=71:sw=4:ts=4:expandtab
+
% \end{macrocode}
%
% \iffalse
diff --git a/mkglyphlist b/mkglyphlist
index 9ee1528..d73a608 100755
--- a/mkglyphlist
+++ b/mkglyphlist
@@ -8,6 +8,12 @@
-- VERSION: 1.0
-- CREATED: 04/23/2013 12:42:17 PM CEST
-----------------------------------------------------------------------
+-- interesting thread on the Context list:
+-- http://www.ntg.nl/pipermail/ntg-context/2008/029057.html
+-----------------------------------------------------------------------
+
+
+-----------------------------------------------------------------------
-- config
-----------------------------------------------------------------------
local glyphfile = "./glyphlist.txt"
diff --git a/tests/font_patch.tex b/tests/font_patch.tex
index d41ff48..d3bba07 100644
--- a/tests/font_patch.tex
+++ b/tests/font_patch.tex
@@ -8,7 +8,11 @@
mc.DisplayOperatorMinHeight = 2800 / em * sz
end
end
- luatexbase.add_to_callback("luaotfload.patch_font", patch, "cambria.domh")
+ %% part of luaotfload-auxiliary
+ %luatexbase.add_to_callback(
+ %"luaotfload.patch_font",
+ %patch,
+ %"luaotfload.aux.patch_cambria_domh")
}
\font\4={name:Cambria Math:mode=base;script=math} at 10pt
diff --git a/tests/pln-aux-1.tex b/tests/pln-aux-1.tex
new file mode 100644
index 0000000..a504850
--- /dev/null
+++ b/tests/pln-aux-1.tex
@@ -0,0 +1,49 @@
+\input luaotfload.sty
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% usage for glyph tests
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\baselineskip=17.28pt
+
+\font\iwonaregular=name:iwona at 14.4pt
+\font\lmromanten=file:lmroman10-regular.otf at 14.4pt
+\font\cmuregular=file:cmunrm.otf at 14.4pt
+
+%% wrap tests in macros (could move to style file)
+\def\doifglyphelse#1#2#3{%
+ \directlua{
+ luaotfload.aux.do_if_glyph_else([[#1]], [[#2]], [[#3]])
+ }%
+}
+
+\def\doifglyph#1#2{\doifglyphelse{#1}{#2}{}}
+
+%% no otf font loaded yet, so both fail:
+first:
+\doifglyphelse{a}{true}{false}
+\doifglyph {a}{yep}
+
+%% load lm and try repeat:
+\lmromanten
+second:
+\doifglyphelse{a}{true}{false}
+\doifglyph {a}{yep}
+
+%% let’s test some more free fonts
+\def\checkglyphset{%
+ \doifglyphelse ö{ö}{nope}
+ \doifglyphelse п{п}{nope}
+ \doifglyphelse α{α}{nope}
+ \doifglyphelse Æ{Æ}{nope}
+ \doifglyphelse ą{ą}{nope}
+ \doifglyphelse ř{ř}{nope}
+ \doifglyphelse ˝{˝}{nope}
+ \doifglyphelse ѩ{ѩ}{nope}
+ \endgraf
+}
+
+\iwonaregular \checkglyphset
+\lmromanten \checkglyphset
+\cmuregular \checkglyphset
+
+\bye
diff --git a/tests/pln-aux-2.tex b/tests/pln-aux-2.tex
new file mode 100644
index 0000000..62192a5
--- /dev/null
+++ b/tests/pln-aux-2.tex
@@ -0,0 +1,102 @@
+\input luaotfload.sty
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% script, features, and language
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\font\minionregular=file:MinionPro_Regular.otf at 9pt
+\font\biolinum=file:LinBiolinum_R.otf at 9pt
+\font\cmuregular=file:cmunrm.otf at 9pt
+
+%% (1) luaotfload.aux.provides_script(font_id, script)
+%% #1 defined font; #2 OT script tag
+\def\providesscript[#1][#2]{%
+ \bgroup#1%
+ let’s see whether \detokenize{#1} has script #2:
+ \directlua{
+ local aux = luaotfload.aux
+ local succ = aux.provides_script(font.current(), [[#2]])
+ if succ then tex.sprint"true" else tex.sprint"false" end
+ }%
+ \egroup
+ \endgraf%
+}
+
+\providesscript [\minionregular][latn]%% Latin
+\providesscript [\biolinum][latn]
+\providesscript [\cmuregular][latn]
+\providesscript [\minionregular][cyrl]%% Cyrillic
+\providesscript [\biolinum][cyrl]
+\providesscript [\cmuregular][cyrl]
+\providesscript [\minionregular][tibt]%% Tibetan
+\providesscript [\biolinum][tibt]
+\providesscript [\cmuregular][tibt]
+
+\hrule % --------------------------------------------------------------
+
+%% (2) luaotfload.aux.provides_language(font_id, script, language)
+%% #1 defined font; #2 OT script tag; #3 OT language tag
+\def\provideslanguage[#1][#2][#3]{%
+ \bgroup#1%
+ let’s see whether \detokenize{#1} supports language #3 for script #2:
+ \directlua{
+ local aux = luaotfload.aux
+ local succ = aux.provides_language(font.current(), [[#2]], [[#3]])
+ if succ then tex.sprint"true" else tex.sprint"false" end
+ }%
+ \egroup
+ \endgraf%
+}
+
+\provideslanguage [\minionregular][latn][nld]%% Latin/Dutch
+\provideslanguage [\biolinum][latn][nld]
+\provideslanguage [\cmuregular][latn][nld]
+\provideslanguage [\minionregular][latn][deu]%% Latin/German
+\provideslanguage [\biolinum][latn][deu]
+\provideslanguage [\cmuregular][latn][deu]
+\provideslanguage [\minionregular][cyrl][rus]%% Cyrillic/Russian
+\provideslanguage [\biolinum][cyrl][rus]
+\provideslanguage [\cmuregular][cyrl][rus]
+\provideslanguage [\minionregular][cyrl][klm]%% Cyrillic/Kalmyk
+\provideslanguage [\biolinum][cyrl][klm]
+\provideslanguage [\cmuregular][cyrl][klm]
+\provideslanguage [\minionregular][cyrl][srb]%% Cyrillic/Serbian
+\provideslanguage [\biolinum][cyrl][srb]
+\provideslanguage [\cmuregular][cyrl][srb]
+\provideslanguage [\minionregular][tibt][tib]%% Tibetan
+\provideslanguage [\biolinum][tibt][tib]
+\provideslanguage [\cmuregular][tibt][tib]
+
+\hrule % --------------------------------------------------------------
+
+%% (3) luaotfload.aux.provides_feature(
+%% font_id, script, language, feature)
+%% #1 defined font; #2 OT script tag;
+%% #3 OT language tag; #4 OT feature
+\def\providesfeature[#1][#2][#3][#4]{%this is getting ridiculous
+ \bgroup#1%
+ let’s see whether \detokenize{#1} supports feature #4 for the
+ combination of script #2 with language #3:
+ \directlua{
+ local aux = luaotfload.aux
+ local succ = aux.provides_feature(
+ font.current(), [[#2]], [[#3]], [[#4]])
+ if succ then tex.sprint"true" else tex.sprint"false" end
+ }%
+ \egroup
+ \endgraf%
+}
+
+\providesfeature [\minionregular][latn][nld][liga]%% Latin/Dutch
+\providesfeature [\biolinum][latn][nld][liga]
+\providesfeature [\cmuregular][latn][nld][liga]
+\providesfeature [\minionregular][latn][deu][liga]%% Latin/German
+\providesfeature [\biolinum][latn][deu][liga]
+\providesfeature [\cmuregular][latn][deu][liga]
+\providesfeature [\minionregular][cyrl][srb][liga]%% Cyrillic/Serbian
+\providesfeature [\biolinum][cyrl][srb][liga]
+\providesfeature [\cmuregular][cyrl][srb][liga]
+\providesfeature [\minionregular][tibt][tib][liga]%% Tibetan
+\providesfeature [\biolinum][tibt][tib][liga]
+\providesfeature [\cmuregular][tibt][tib][liga]
+
+\bye
diff --git a/tests/pln-aux-3.tex b/tests/pln-aux-3.tex
new file mode 100644
index 0000000..12a80cf
--- /dev/null
+++ b/tests/pln-aux-3.tex
@@ -0,0 +1,39 @@
+\input luaotfload.sty
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% math dimension getter
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\font\xitsmath=file:xits-math.otf
+\font\cambriamath=file:cambria.ttc(1)
+
+\font\main=file:Iwona-Regular.otf at 12pt\main
+
+\directlua{
+ local aux = luaotfload.aux
+ local test_a = function (fontname, dimension)
+ tex.sprint(
+ "(", fontname, " (", dimension, " ",
+ aux.get_math_dimension(fontname, dimension),
+ [[))\endgraf ]])
+ end
+
+ local test_b = function (fontname, dimension)
+ aux.sprint_math_dimension(fontname, dimension)
+ tex.print[[\endgraf ]]
+ end
+
+ test_a("xitsmath", "AxisHeight")
+ test_a("xitsmath", "RadicalVerticalGap")
+ test_a("cambriamath", "StackTopShiftUp")
+ test_a("cambriamath", "FractionNumeratorGapMin")
+
+ test_b("xitsmath", "AxisHeight")
+ test_b("xitsmath", "RadicalVerticalGap")
+ test_b("cambriamath", "StackTopShiftUp")
+ test_b("cambriamath", "FractionNumeratorGapMin")
+}
+
+foo bar baz
+
+\bye
diff --git a/tests/pln-aux-4.tex b/tests/pln-aux-4.tex
new file mode 100644
index 0000000..80ffc0b
--- /dev/null
+++ b/tests/pln-aux-4.tex
@@ -0,0 +1,41 @@
+\input luaotfload.sty
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% unicode character mappings
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\font\ptserifregular = file:PTF55F.ttf \ptserifregular
+
+%% here we map the function luaotfload.aux.name_of_slot
+%% on a short text, printing a list of letters, their
+%% code points and names (as specified in the Adobe
+%% Glyph List).
+
+\directlua{
+ local aux = luaotfload.aux
+ local cbk = function (str)
+ if string.match(str, "^EOF") then
+ luatexbase.remove_from_callback("process_input_buffer", "weird")
+ return [[the end!]]
+ end
+ local res = { }
+ for chr in string.utfcharacters(str) do
+ local val = unicode.utf8.byte(chr)
+ local line = chr .. " <> " .. tostring(val)
+ line = line .. " <> " .. (aux.name_of_slot(val) or "")
+ res[\string#res+1] = line
+ end
+ return table.concat(res, [[\endgraf]])
+ end
+
+ luatexbase.add_to_callback("process_input_buffer", cbk, "weird")
+}
+
+Я узнал что у меня
+Есть огромная семья
+И тропинка и лесок
+В поле каждый колосок
+
+EOF
+
+\bye
diff --git a/tests/pln-request-4-slashed.tex b/tests/pln-request-4-slashed.tex
new file mode 100644
index 0000000..5e7d99e
--- /dev/null
+++ b/tests/pln-request-4-slashed.tex
@@ -0,0 +1,12 @@
+\ifdefined\directlua\input luaotfload.sty\fi
+\font\iwona =iwona at 20pt
+\font\iwonabold =iwona/b at 20pt
+\font\iwonaitalic =iwona/i at 20pt
+\font\iwonabolditalic =iwona/bi at 20pt
+
+\def\test{foo bar baz \endgraf}
+{\iwona \test}
+{\iwonabold \test}
+{\iwonaitalic \test}
+{\iwonabolditalic \test}
+\bye
diff --git a/tests/pln-request-5-cached.tex b/tests/pln-request-5-cached.tex
new file mode 100644
index 0000000..8ba4a5e
--- /dev/null
+++ b/tests/pln-request-5-cached.tex
@@ -0,0 +1,18 @@
+\ifdefined\directlua
+ \directlua{config = config or { luaotfload = { } }
+ config.luaotfload.resolver = "cached"
+ config.luaotfload.loglevel = 5 }
+ \input luaotfload.sty
+\fi
+
+\font\iwona =name:iwona at 20pt
+\font\iwonabold =name:iwona/b at 20pt
+\font\iwonaitalic =name:iwona/i at 20pt
+\font\iwonabolditalic =name:iwona/bi at 20pt
+
+\def\test{foo bar baz \endgraf}
+{\iwona \test}
+{\iwonabold \test}
+{\iwonaitalic \test}
+{\iwonabolditalic \test}
+\bye
diff --git a/tests/pln-subfont-1.tex b/tests/pln-subfont-1.tex
new file mode 100644
index 0000000..fb8e1e7
--- /dev/null
+++ b/tests/pln-subfont-1.tex
@@ -0,0 +1,12 @@
+\ifdefined\directlua\input luaotfload.sty\fi
+%% This requires the Cambria fonts from MS.
+\directlua{
+ inspect(fontloader.info"cambria.ttc")
+}
+%% Here we load both subfonts in the collection
+%% with the not-quite documented subfont syntax.
+\font\subfontone="file:cambria.ttc(0)" at 42pt
+\font\subfonttwo="file:cambria.ttc(1)" at 42pt
+\subfontone foo bar baz \endgraf
+\subfonttwo foo bar baz \endgraf
+\bye
diff --git a/tests/pln-tfm.tex b/tests/pln-tfm.tex
new file mode 100644
index 0000000..16ae41a
--- /dev/null
+++ b/tests/pln-tfm.tex
@@ -0,0 +1,10 @@
+\ifdefined\directlua\input luaotfload.sty \fi
+%% TFM’s can be loaded with a file: request ...
+\font\antykwatorunska="file:rm-anttr"
+%% or with an anonymous request, like in þe olde TeX:
+\font\antykwatorunskabcap=ec-anttbcap
+\font\lmromanten={file:ec-lmr10} at 10pt
+\antykwatorunska foo bar
+\antykwatorunskabcap baz xyzzy
+\lmromanten Donde, está, la biblioteca. Me llamo T-Bone La araña discoteca.
+\bye