summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPhilipp Gesang <phg42.2a@gmail.com>2013-04-29 23:12:38 +0200
committerPhilipp Gesang <phg42.2a@gmail.com>2013-04-29 23:12:38 +0200
commitebdd60e14123b44f2e4f74b108ff34fadf480467 (patch)
treec9528e5979e5077e86c71ca31caab77c1f53ca82
parent1d871e6152c1acfa01db6fd90ee809274b9f8751 (diff)
parent572c0e2de196d2d9551cd205b7f0c69800fb7516 (diff)
downloadluaotfload-ebdd60e14123b44f2e4f74b108ff34fadf480467.tar.gz
merge from phi-gamma/master
-rw-r--r--Makefile2
-rw-r--r--NEWS24
-rw-r--r--filegraph.dot2
-rw-r--r--luaotfload-database.lua254
-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.dtx406
-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.tex8
13 files changed, 901 insertions, 318 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..d1089d6 100644
--- a/NEWS
+++ b/NEWS
@@ -6,10 +6,26 @@ 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
+ * Blacklisting font lingoes.ttf while removing several TrueType collections
+ from the blacklist
+ * 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, non-ascii names in font cache
+ etc.)
+ * There is now a central, non-personal dev repo on github:
+ https://github.com/lualatex/luaotfload
2013/04/25, luaotfload v1.29:
* Reverting the unified resolver, as the database was rebuilt too often
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-database.lua b/luaotfload-database.lua
index 3085b63..037ca07 100644
--- a/luaotfload-database.lua
+++ b/luaotfload-database.lua
@@ -239,6 +239,8 @@ 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
@@ -246,6 +248,7 @@ local load_names
local read_fonts_conf
local reload_db
local resolve
+local resolve_cached
local save_names
local scan_external_dir
local update_names
@@ -298,9 +301,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 ...
@@ -323,29 +330,26 @@ local crude_file_lookup_verbose = function (data, 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]
@@ -353,11 +357,13 @@ local crude_file_lookup = function (data, filename)
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 +383,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,34 +421,30 @@ 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)
+--- 'a -> 'a -> table -> (string * int|boolean * boolean)
+resolve_cached = function (_, _, specification)
if not names.data then names.data = load_names() end
local request_cache = names.data.request_cache
local request = specification.specification
- report("info", 4, "cache",
- "looking for “%s” in cache ...",
+ report("log", 4, "cache", "looking for “%s” in cache ...",
request)
+
local found = names.data.request_cache[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("log", 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)
+ 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("log", 4, "cache", "new entry: %s", request)
names.data.request_cache[request] = entry
--- obviously, the updated cache needs to be stored.
@@ -466,20 +452,13 @@ local cached_resolver = function (specification)
--- 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")
+ --- TODO we should speed up writing by separating
+ --- the cache from the db
+ report("log", 5, "cache", "saving updated cache")
save_names()
- return resolved_spec
+ 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
-
--[[doc--
Luatex-fonts, the font-loader package luaotfload imports, comes with
@@ -522,12 +501,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 +542,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
@@ -648,12 +619,14 @@ resolve = function (_,_,specification) -- the 1st two parameters are used by Con
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 +642,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
@@ -741,9 +717,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 +797,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 +844,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 +858,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 +878,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
-
- if not fullname then return false end
+ local entryname = basename
- 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 +925,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 +1063,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 +1085,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 +1109,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 +1376,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
@@ -1442,9 +1409,9 @@ update_names = function (fontnames, force)
fontnames = load_names()
end
if fontnames.version ~= names.version then
- fontnames = fontnames_init(true)
report("log", 1, "db", "No font names database or old "
.. "one found; generating new one")
+ fontnames = fontnames_init(true)
end
end
local newfontnames = fontnames_init(true)
@@ -1508,15 +1475,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..5b454bc 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 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}
+%
+% \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.
%
-% 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).
+% \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:
%
@@ -360,9 +488,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:
%
-% \meta{font features} is semicolon-separated list of feature
+% \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}.
+%
+% 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 +768,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 +778,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 +793,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 +810,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
@@ -681,7 +862,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 +871,7 @@ and the derived files
%
% \begin{quote}
% \begin{verbatim}
-% fontdbutil --find="Iwona Regular"
+% luaotfload-tool --find="Iwona Regular"
% \end{verbatim}
% \end{quote}
%
@@ -707,7 +888,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 +899,7 @@ and the derived files
% using the \verb|-i| option (\verb|--info|).
% \begin{quote}
% \begin{verbatim}
-% fontdbutil -F --find="Iwona Light Italic"
+% luaotfload-tool -F --find="Iwona Light Italic"
% \end{verbatim}
% \end{quote}
% \noindent
@@ -727,7 +908,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 +917,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 +1126,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 +1179,13 @@ 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"
+--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 +1196,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
@@ -1328,7 +1511,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,23 +1533,101 @@ 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}
+
+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 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}
+
+--request_resolvers.anon = request_resolvers.name
+
+% \end{macrocode}
+% 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 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
+ end
+ request_resolvers.name(specification)
+end
-fonts.definers.resolvers.file = function (specification)
- specification.name = fonts.names.resolve('', '', specification)
+% \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}
@@ -1442,7 +1703,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
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..26fa738
--- /dev/null
+++ b/tests/pln-tfm.tex
@@ -0,0 +1,8 @@
+\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
+\antykwatorunska foo bar
+\antykwatorunskabcap baz xyzzy
+\bye