summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--NEWS6
-rw-r--r--filegraph.dot3
-rw-r--r--luaotfload-basics-gen.lua4
-rw-r--r--luaotfload-database.lua493
-rw-r--r--luaotfload-features.lua2
-rw-r--r--luaotfload-lib-dir.lua470
-rw-r--r--luaotfload-merged.lua6
-rw-r--r--luaotfload-override.lua82
-rwxr-xr-xluaotfload-tool.lua496
-rw-r--r--luaotfload-tool.rst24
-rw-r--r--luaotfload.dtx59
11 files changed, 924 insertions, 721 deletions
diff --git a/NEWS b/NEWS
index b504b9b..8615d90 100644
--- a/NEWS
+++ b/NEWS
@@ -1,7 +1,7 @@
Change History
--------------
-2013/xx/xx, luaotfload v2.3:
+2013/07/03, luaotfload v2.3:
* New experimental lookups: ``kpse`` (kpathsea), ``my`` (callback)
* Precedence of texmf over system fonts can be requested
using the ``--prefer-texmf`` option of luaotfload-tool
@@ -9,6 +9,10 @@ Change History
* Rewrite the output of ``\fontname`` via ``patch_font`` callback
* Allow blacklisting directories
* Drop compatibility layer
+ * Remove dependency on luaotfload-lib-dir (references to dir.glob() and
+ dir.mkdirs())
+ * luaotfload-tool now displays extensive information about a font if given
+ the argument --inspect
2013/05/20, luaotfload v2.2:
* There is now a central, non-personal dev repo on github:
diff --git a/filegraph.dot b/filegraph.dot
index 8db137c..c8a07d3 100644
--- a/filegraph.dot
+++ b/filegraph.dot
@@ -139,10 +139,9 @@ strict digraph luaotfload_files { //looks weird with circo ...
label = <
<table cellborder="0" bgcolor="#FFFFFFAA">
<th> <td colspan="2"> <font point-size="12" face="Iwona Italic">Luaotfload Libraries</font> </td> </th>
- <tr> <td>luaotfload-lib-dir.lua</td> <td>luaotfload-features.lua</td> </tr>
+ <tr> <td>luaotfload-auxiliary.lua</td> <td>luaotfload-features.lua</td> </tr>
<tr> <td>luaotfload-override.lua</td> <td>luaotfload-loaders.lua</td> </tr>
<tr> <td>luaotfload-database.lua</td> <td>luaotfload-color.lua</td> </tr>
- <tr> <td>luaotfload-auxiliary.lua</td> </tr>
</table>
>,
]
diff --git a/luaotfload-basics-gen.lua b/luaotfload-basics-gen.lua
index 4a46fbb..a5da2fd 100644
--- a/luaotfload-basics-gen.lua
+++ b/luaotfload-basics-gen.lua
@@ -151,13 +151,13 @@ do
-- quite like tex live or so
- if cachepaths == "" then
+ if cachepaths == "$TEXMFCACHE" then
cachepaths = kpse.expand_var('$TEXMFVAR') or ""
end
-- this also happened to be used
- if cachepaths == "" then
+ if cachepaths == "$TEXMFVAR" then
cachepaths = kpse.expand_var('$VARTEXMF') or ""
end
diff --git a/luaotfload-database.lua b/luaotfload-database.lua
index 484decf..c0aadaf 100644
--- a/luaotfload-database.lua
+++ b/luaotfload-database.lua
@@ -16,8 +16,8 @@ local lpeg = require "lpeg"
local P, R, S, lpegmatch
= lpeg.P, lpeg.R, lpeg.S, lpeg.match
-local C, Cc, Cf, Cg, Ct
- = lpeg.C, lpeg.Cc, lpeg.Cf, lpeg.Cg, lpeg.Ct
+local C, Cc, Cf, Cg, Cs, Ct
+ = lpeg.C, lpeg.Cc, lpeg.Cf, lpeg.Cg, lpeg.Cs, lpeg.Ct
--- Luatex builtins
local load = load
@@ -33,12 +33,11 @@ local iolines = io.lines
local ioopen = io.open
local kpseexpand_path = kpse.expand_path
local kpseexpand_var = kpse.expand_var
-local kpselookup = kpse.lookup
local kpsefind_file = kpse.find_file
+local kpselookup = kpse.lookup
local kpsereadable_file = kpse.readable_file
-local lfsisdir = lfs.isdir
-local lfsisfile = lfs.isfile
local lfsattributes = lfs.attributes
+local lfsdir = lfs.dir
local mathabs = math.abs
local mathmin = math.min
local stringfind = string.find
@@ -49,29 +48,30 @@ local stringlower = string.lower
local stringsub = string.sub
local stringupper = string.upper
local tableconcat = table.concat
-local tablecopy = table.copy
local tablesort = table.sort
-local tabletofile = table.tofile
local texiowrite_nl = texio.write_nl
local utf8gsub = unicode.utf8.gsub
local utf8lower = unicode.utf8.lower
--- these come from Lualibs/Context
-local dirglob = dir.glob
-local dirmkdirs = dir.mkdirs
local filebasename = file.basename
-local filenameonly = file.nameonly
-local filedirname = file.dirname
local filecollapsepath = file.collapsepath or file.collapse_path
+local filedirname = file.dirname
local fileextname = file.extname
local fileiswritable = file.iswritable
local filejoin = file.join
+local filenameonly = file.nameonly
local filereplacesuffix = file.replacesuffix
local filesplitpath = file.splitpath or file.split_path
+local lfsisdir = lfs.isdir
+local lfsisfile = lfs.isfile
+local lfsmkdirs = lfs.mkdirs
local stringis_empty = string.is_empty
local stringsplit = string.split
local stringstrip = string.strip
local tableappend = table.append
+local tablecopy = table.copy
+local tabletofile = table.tofile
local tabletohash = table.tohash
--- the font loader namespace is “fonts”, same as in Context
@@ -83,32 +83,33 @@ fonts.definers = fonts.definers or { }
local names = fonts.names
+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
+
names.version = 2.207
names.data = nil --- contains the loaded database
names.lookups = nil --- contains the lookup cache
names.path = {
dir = "", --- db and cache directory
- basename = "luaotfload-names.lua", --- db file name
+ basename = config.luaotfload.names_file
+ or "luaotfload-names.lua",
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"
-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
-- uses TEXMFCACHE or TEXMFVAR as starting points.
local writable_path
if caches then
- writable_path = caches.getwritablepath("names","")
+ writable_path = caches.getwritablepath "names"
if not writable_path then
luaotfload.error("Impossible to find a suitable writeable cache...")
end
@@ -136,6 +137,47 @@ local sanitize_string = function (str)
return nil
end
+local find_files_indeed
+find_files_indeed = function (acc, dirs, filter)
+ if not next (dirs) then --- done
+ return acc
+ end
+
+ local dir = dirs[#dirs]
+ dirs[#dirs] = nil
+
+ local newdirs, newfiles = { }, { }
+ for ent in lfsdir (dir) do
+ if ent ~= "." and ent ~= ".." then
+ local fullpath = dir .. "/" .. ent
+ if filter (fullpath) == true then
+ if lfsisdir (fullpath) then
+ newdirs[#newdirs+1] = fullpath
+ elseif lfsisfile (fullpath) then
+ newfiles[#newfiles+1] = fullpath
+ end
+ end
+ end
+ end
+ return find_files_indeed (tableappend (acc, newfiles),
+ tableappend (dirs, newdirs),
+ filter)
+end
+
+local dummyfilter = function () return true end
+
+--- the optional filter function receives the full path of a file
+--- system entity. a filter applies if the first argument it returns is
+--- true.
+
+--- string -> function? -> string list
+local find_files = function (root, filter)
+ if lfsisdir (root) then
+ return find_files_indeed ({}, { root }, filter or dummyfilter)
+ end
+end
+
+
--[[doc--
This is a sketch of the luaotfload db:
@@ -295,6 +337,17 @@ load_names = function (dry_run)
"Font names database loaded", "%s", foundname)
report("info", 3, "db", "Loading took %0.f ms",
1000*(os.gettimeofday()-starttime))
+
+ local db_version, nms_version = data.version, names.version
+ if db_version ~= nms_version then
+ report("log", 0, "db",
+ [[Version mismatch; expected %4.3f, got %4.3f]],
+ nms_version, db_version)
+ if not fonts_reloaded then
+ report("log", 0, "db", [[force rebuild]])
+ return update_names({ }, true, false)
+ end
+ end
else
report("both", 0, "db",
[[Font names database not found, generating new one.]])
@@ -326,22 +379,51 @@ end
local style_synonyms = { set = { } }
do
+ local combine = function (ta, tb)
+ local result = { }
+ for i=1, #ta do
+ for j=1, #tb do
+ result[#result+1] = ta[i] .. tb[j]
+ end
+ end
+ return result
+ end
+
--- read this: http://blogs.adobe.com/typblography/2008/05/indesign_font_conflicts.html
--- tl;dr: font style synonyms are unreliable.
- style_synonyms.list = {
+ ---
+ --- Context matches font names against lists of known identifiers
+ --- for weight, style, width, and variant, so that including
+ --- the family name there are five dimensions for choosing a
+ --- match. The sad thing is, while this is a decent heuristic it
+ --- makes no sense to imitate it in luaotfload because the user
+ --- interface must fit into the much more limited Xetex scheme that
+ --- distinguishes between merely four style categories (variants):
+ --- “regular”, “italic”, “bold”, and “bolditalic”. As a result,
+ --- some of the styles are lumped together although they can differ
+ --- significantly (like “medium” and “bold”).
+
+ --- Xetex (XeTeXFontMgr.cpp) appears to recognize only “plain”,
+ --- “normal”, and “roman” as synonyms for “regular”.
+ local list = {
regular = { "normal", "roman",
- "plain", "book", },
+ "plain", "book",
+ "light", "extralight",
+ "ultralight", },
bold = { "demi", "demibold",
"semibold", "boldregular",
- "medium" },
+ "medium", "mediumbold",
+ "ultrabold", "extrabold",
+ "heavy", "black",
+ "bold", },
italic = { "regularitalic", "normalitalic",
- "oblique", "slanted", },
- bolditalic = { "boldoblique", "boldslanted",
- "demiitalic", "demioblique",
- "demislanted", "demibolditalic",
- "semibolditalic", },
+ "oblique", "slanted",
+ "italic", },
}
+ list.bolditalic = combine(list.bold, list.italic)
+ style_synonyms.list = list
+
for category, synonyms in next, style_synonyms.list do
style_synonyms.set[category] = tabletohash(synonyms, true)
end
@@ -358,12 +440,12 @@ local verbose_lookup = function (data, kind, filename)
found = data.full[found]
if found == nil then --> texmf
report("info", 0, "db",
- "crude file lookup: req=%s; hit=%s => kpse",
+ "Crude file lookup: req=%s; hit=%s => kpse",
filename, kind)
found = dummy_findfile(filename)
else
report("info", 0, "db",
- "crude file lookup: req=%s; hit=%s; ret=%s",
+ "Crude file lookup: req=%s; hit=%s; ret=%s",
filename, kind, found)
end
return found
@@ -538,23 +620,23 @@ end
resolve_cached = function (_, _, specification)
if not names.lookups then names.lookups = load_lookups() end
local request = hash_request(specification)
- report("both", 4, "cache", "looking for “%s” in cache ...",
+ report("both", 4, "cache", "Looking for “%s” in cache ...",
request)
local found = names.lookups[request]
--- case 1) cache positive ----------------------------------------
if found then --- replay fields from cache hit
- report("info", 4, "cache", "found!")
+ report("info", 4, "cache", "Found!")
local basename = found[1]
--- check the presence of the file in case it’s been removed
local success = verify_font_file(basename)
if success == true then
return basename, found[2], true
end
- report("both", 4, "cache", "cached file not found; resolving again")
+ report("both", 4, "cache", "Cached file not found; resolving again")
else
- report("both", 4, "cache", "not cached; resolving")
+ report("both", 4, "cache", "Not cached; resolving")
end
--- case 2) cache negative ----------------------------------------
@@ -563,16 +645,16 @@ resolve_cached = function (_, _, 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)
+ report("both", 4, "cache", "New entry: %s", request)
names.lookups[request] = entry
--- obviously, the updated cache needs to be stored.
--- TODO this should trigger a save only once the
--- document is compiled (finish_pdffile callback?)
- report("both", 5, "cache", "saving updated cache")
+ report("both", 5, "cache", "Saving updated cache")
local success = save_lookups()
if not success then --- sad, but not critical
- report("both", 0, "cache", "could not write to cache")
+ report("both", 0, "cache", "Could not write to cache")
end
return filename, subfont, true
end
@@ -580,9 +662,16 @@ end
--- 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 add_to_match = function (found, size, face)
+
+ local optsize, dsnsize, maxsize, minsize
+ if #face.size > 0 then
+ optsize = face.size
+ dsnsize = optsize[1] and optsize[1] / 10
+ -- can be nil
+ maxsize = optsize[2] and optsize[2] / 10 or dsnsize
+ minsize = optsize[3] and optsize[3] / 10 or dsnsize
+ end
local continue = true
if optsize then
if dsnsize == size or (size > minsize and size <= maxsize) then
@@ -637,18 +726,18 @@ the font database created by the luaotfload-tool script.
--- values.
---
-resolve = function (_,_,specification) -- the 1st two parameters are used by ConTeXt
+resolve = function (_, _, specification) -- the 1st two parameters are used by ConTeXt
if not fonts_loaded then names.data = load_names() end
local data = names.data
local name = sanitize_string(specification.name)
local style = sanitize_string(specification.style) or "regular"
- local size
+ local askedsize
if specification.optsize then
- size = tonumber(specification.optsize)
+ askedsize = tonumber(specification.optsize)
elseif specification.size then
- size = specification.size / 65536
+ askedsize = specification.size / 65536
end
if type(data) ~= "table" then
@@ -665,18 +754,6 @@ resolve = function (_,_,specification) -- the 1st two parameters are used by Con
return specification.name, false, false
end
- local db_version, nms_version = data.version, names.version
- if db_version ~= nms_version then
- report("log", 0, "db",
- [[version mismatch; expected %4.3f, got %4.3f]],
- nms_version, db_version)
- if not fonts_reloaded then
- return reload_db("version mismatch",
- resolve, nil, nil, specification)
- end
- return specification.name, false, false
- end
-
if not data.mappings then
if not fonts_reloaded then
return reload_db("invalid database; missing font mapping",
@@ -685,7 +762,8 @@ resolve = function (_,_,specification) -- the 1st two parameters are used by Con
return specification.name, false, false
end
- local found = { } --> collect results
+ local exact = { } --> collect exact style matches
+ local synonymous = { } --> collect matching style synonyms
local fallback --> e.g. non-matching style (fontspec is anal about this)
local candidates = { } --> secondary results, incomplete matches
@@ -708,47 +786,31 @@ resolve = function (_,_,specification) -- the 1st two parameters are used by Con
fontname = fontname or sanitize_string(face.fontname)
pfullname = pfullname or sanitize_string(face.fullname)
- local optsize, dsnsize, maxsize, minsize
- if #face.size > 0 then
- optsize = face.size
- dsnsize = optsize[1] and optsize[1] / 10
- -- can be nil
- maxsize = optsize[2] and optsize[2] / 10 or dsnsize
- minsize = optsize[3] and optsize[3] / 10 or dsnsize
- end
-
if name == family
or name == metafamily
then
- if style == prefmodifiers
- or style == subfamily
- or synonym_set[style] and
- (synonym_set[style][prefmodifiers] or
- synonym_set[style][subfamily])
- then
+ if style == prefmodifiers then
local continue
- found, continue = add_to_match(
- found, optsize, dsnsize, size,
- minsize, maxsize, face)
+ exact, continue = add_to_match(exact, askedsize, face)
if continue == false then break end
-
- elseif prefmodifiers == "regular"
- or subfamily == "regular"
- --- TODO this match should be performed when building the db
- or synonym_set.regular[prefmodifiers]
- or synonym_set.regular[subfamily]
- then
- fallback = face
+ elseif style == subfamily then
+ exact = add_to_match(exact, askedsize, 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
+ synonymous, continue = add_to_match(synonymous, askedsize, face)
+ elseif synonym_set[style] and
+ (synonym_set[style][prefmodifiers] or
+ synonym_set[style][subfamily])
+ or synonym_set.regular[prefmodifiers]
+ or synonym_set.regular[subfamily]
+ then
+ synonymous = add_to_match(synonymous, askedsize, face)
+ elseif prefmodifiers == "regular"
+ or subfamily == "regular" then
+ fallback = face
else --- mark as last straw but continue
candidates[#candidates+1] = face
end
@@ -758,14 +820,19 @@ resolve = function (_,_,specification) -- the 1st two parameters are used by Con
or name == fontname
or name == psname then
local continue
- found, continue = add_to_match(
- found, optsize, dsnsize, size,
- minsize, maxsize, face)
+ exact, continue = add_to_match(exact, askedsize, face)
if continue == false then break end
end
end
end
+ local found
+ if next(exact) then
+ found = exact
+ else
+ found = synonymous
+ end
+
--- this is a monster
if #found == 1 then
--- “found” is really synonymous with “registered in the db”.
@@ -774,7 +841,7 @@ resolve = function (_,_,specification) -- the 1st two parameters are used by Con
= get_font_file(data.filenames.full, entry)
if success == true then
report("log", 0, "resolve",
- "font family='%s', subfamily='%s' found: %s",
+ "Font family='%s', subfamily='%s' found: %s",
name, style, filename
)
return filename, subfont, true
@@ -787,7 +854,7 @@ resolve = function (_,_,specification) -- the 1st two parameters are used by Con
local least = math.huge -- initial value is infinity
for i,face in next, found do
local dsnsize = face.size[1]/10
- local difference = mathabs(dsnsize-size)
+ local difference = mathabs(dsnsize - askedsize)
if difference < least then
closest = face
least = difference
@@ -797,7 +864,7 @@ resolve = function (_,_,specification) -- the 1st two parameters are used by Con
= get_font_file(data.filenames.full, closest)
if success == true then
report("log", 0, "resolve",
- "font family='%s', subfamily='%s' found: %s",
+ "Font family='%s', subfamily='%s' found: %s",
name, style, filename
)
return filename, subfont, true
@@ -807,11 +874,11 @@ resolve = function (_,_,specification) -- the 1st two parameters are used by Con
= get_font_file(data.filenames.full, fallback)
if success == true then
report("log", 0, "resolve",
- "no exact match for request %s; using fallback",
+ "No exact match for request %s; using fallback",
specification.specification
)
report("log", 0, "resolve",
- "font family='%s', subfamily='%s' found: %s",
+ "Font family='%s', subfamily='%s' found: %s",
name, style, filename
)
return filename, subfont, true
@@ -823,7 +890,7 @@ resolve = function (_,_,specification) -- the 1st two parameters are used by Con
= get_font_file(data.filenames.full, entry)
if success == true then
report("log", 0, "resolve",
- "font family='%s', subfamily='%s' found: %s",
+ "Font family='%s', subfamily='%s' found: %s",
name, style, filename
)
return filename, subfont, true
@@ -861,7 +928,7 @@ end
--- string -> ('a -> 'a) -> 'a list -> 'a
reload_db = function (why, caller, ...)
- report("both", 1, "db", "reload initiated; reason: “%s”", why)
+ report("both", 1, "db", "Reload initiated; reason: “%s”", why)
names.data = update_names(names.data, false, false)
local success = save_names()
if success then
@@ -957,13 +1024,13 @@ find_closest = function (name, limit)
tablesort(distances)
limit = mathmin(n_distances, limit)
report(false, 1, "query",
- "displaying %d distance levels", limit)
+ "Displaying %d distance levels", limit)
for i = 1, limit do
local dist = distances[i]
local namelst = by_distance[dist]
report(false, 0, "query",
- "distance from “" .. name .. "”: " .. dist
+ "Distance from “" .. name .. "”: " .. dist
.. "\n " .. tableconcat(namelst, "\n ")
)
end
@@ -991,7 +1058,7 @@ font_fullinfo = function (filename, subfont, texmf, basename)
local tfmdata = { }
local rawfont = fontloaderopen(filename, subfont)
if not rawfont then
- report("log", 1, "error", "failed to open %s", filename)
+ report("log", 1, "error", "Failed to open %s", filename)
return
end
local metadata = fontloader.to_table(rawfont)
@@ -1029,7 +1096,7 @@ font_fullinfo = function (filename, subfont, texmf, basename)
end
else
-- no names table, propably a broken font
- report("log", 1, "db", "broken font rejected", "%s", basefile)
+ report("log", 1, "db", "Broken font rejected", "%s", basefile)
return
end
tfmdata.fontname = metadata.fontname
@@ -1081,7 +1148,7 @@ local load_font = function (fullname, fontnames, newfontnames, texmf)
if names.blacklist[fullname] or names.blacklist[basename]
then
report("log", 2, "db",
- "ignoring blacklisted font “%s”", fullname)
+ "Ignoring blacklisted font “%s”", fullname)
return false
end
@@ -1112,7 +1179,7 @@ local load_font = function (fullname, fontnames, newfontnames, texmf)
newmappings[location] = fullinfo --- keep
newentrystatus.index[index+1] = location --- is this actually used anywhere?
end
- report("log", 2, "db", "font “%s” already indexed", basename)
+ report("log", 2, "db", "Font “%s” already indexed", basename)
return false
end
@@ -1145,7 +1212,7 @@ local load_font = function (fullname, fontnames, newfontnames, texmf)
end
else --- missing info
- report("log", 1, "db", "failed to load “%s”", basename)
+ report("log", 1, "db", "Failed to load “%s”", basename)
return false
end
return true
@@ -1170,8 +1237,8 @@ do
return path
end
--[[doc--
- Cygwin used to be treated different from windows and dos. This
- special treatment was removed with a patch submitted by Ken Brown.
+ The special treatment for cygwin was removed with a patch submitted
+ by Ken Brown.
Reference: http://cygwin.com/ml/cygwin/2013-05/msg00006.html
--doc]]--
@@ -1226,7 +1293,7 @@ local create_blacklist = function (blacklist, whitelist)
local result = { }
local dirs = { }
- report("info", 2, "db", "blacklisting “%d” files and directories",
+ report("info", 2, "db", "Blacklisting “%d” files and directories",
#blacklist)
for i=1, #blacklist do
local entry = blacklist[i]
@@ -1237,7 +1304,7 @@ local create_blacklist = function (blacklist, whitelist)
end
end
- report("info", 2, "db", "whitelisting “%d” files", #whitelist)
+ report("info", 2, "db", "Whitelisting “%d” files", #whitelist)
for i=1, #whitelist do
result[whitelist[i]] = nil
end
@@ -1265,12 +1332,12 @@ end
--- unit -> unit
read_blacklist = function ()
local files = {
- kpselookup("luaotfload-blacklist.cnf", {all=true, format="tex"})
+ kpselookup ("luaotfload-blacklist.cnf",
+ {all=true, format="tex"})
}
local blacklist = { }
local whitelist = { }
- --- TODO lpegify
if files and type(files) == "table" then
for _,v in next, files do
for line in iolines(v) do
@@ -1286,7 +1353,7 @@ read_blacklist = function ()
line = stringsub(line, 1, cmt - 1)
end
line = stringstrip(line)
- report("log", 2, "db", "blacklisted file “%s”", line)
+ report("log", 2, "db", "Blacklisted file “%s”", line)
blacklist[#blacklist+1] = line
end
end
@@ -1295,10 +1362,59 @@ read_blacklist = function ()
names.blacklist = create_blacklist(blacklist, whitelist)
end
-local font_extensions = { "otf", "ttf", "ttc", "dfont" }
-local font_extensions_set = {}
-for key, value in next, font_extensions do
- font_extensions_set[value] = true
+local font_extensions = { "otf", "ttf", "ttc", "dfont" }
+local font_extensions_set = tabletohash (font_extensions)
+local p_font_extensions
+do
+ local extns
+ --tablesort (font_extensions) --- safeguard
+ for i=#font_extensions, 1, -1 do
+ local e = font_extensions[i]
+ if not extns then
+ extns = P(e)
+ else
+ extns = extns + P(e)
+ end
+ end
+ extns = extns * P(-1)
+ p_font_extensions = (1 - extns)^1 * extns
+end
+
+local process_dir_tree
+process_dir_tree = function (acc, dirs)
+ if not next (dirs) then --- done
+ return acc
+ end
+
+ local dir = dirs[#dirs]
+ dirs[#dirs] = nil
+
+ local newdirs, newfiles = { }, { }
+ local blacklist = names.blacklist
+ for ent in lfsdir (dir) do
+ --- filter right away
+ if ent ~= "." and ent ~= ".." and not blacklist[ent] then
+ local fullpath = dir .. "/" .. ent
+ if lfsisdir (fullpath)
+ and not lpegmatch (p_blacklist, fullpath)
+ then
+ newdirs[#newdirs+1] = fullpath
+ elseif lfsisfile (fullpath) then
+ if lpegmatch (p_font_extensions, stringlower(ent)) then
+ newfiles[#newfiles+1] = fullpath
+ end
+ end
+ end
+ end
+ return process_dir_tree (tableappend (acc, newfiles),
+ tableappend (dirs, newdirs))
+end
+
+--- string -> string list
+local find_font_files = function (root)
+ if lfsisdir (root) then
+ return process_dir_tree ({}, { root })
+ end
end
--[[doc--
@@ -1314,40 +1430,45 @@ end
--doc]]--
--- string -> dbobj -> dbobj -> bool -> bool -> (int * int)
-local scan_dir = function (dirname, fontnames, newfontnames, dry_run, texmf)
- if lpegmatch(p_blacklist, dirname) then
+local scan_dir = function (dirname, fontnames, newfontnames,
+ dry_run, texmf)
+ if lpegmatch (p_blacklist, dirname) then
+ report ("both", 3, "db",
+ "Skipping blacklisted directory %s", dirname)
--- ignore
return 0, 0
end
-
- local n_scanned, n_new = 0, 0 --- total of fonts collected
- report("both", 3, "db", "scanning directory %s", dirname)
- for _,i in next, font_extensions do
- for _,ext in next, { i, stringupper(i) } do
- local found = dirglob(stringformat("%s/**.%s$", dirname, ext))
- local n_found = #found
- --- note that glob fails silently on broken symlinks, which
- --- happens sometimes in TeX Live.
- report("both", 4, "db", "%s '%s' fonts found", n_found, ext)
- n_scanned = n_scanned + n_found
- for j=1, n_found do
- local fullname = found[j]
- fullname = path_normalize(fullname)
- local new
- if dry_run == true then
- report("both", 1, "db", "would have been loading “%s”", fullname)
- else
- report("both", 4, "db", "loading font “%s”", fullname)
- local new = load_font(fullname, fontnames, newfontnames, texmf)
- if new == true then
- n_new = n_new + 1
- end
- end
+ local found = find_font_files (dirname)
+ if not found then
+ report ("both", 3, "db",
+ "No such directory: “%s”; skipping.", dirname)
+ return 0, 0
+ end
+ report ("both", 3, "db", "Scanning directory %s", dirname)
+
+ local n_new = 0 --- total of fonts collected
+ local n_found = #found
+ report ("both", 4, "db", "%d font files detected", n_found)
+ for j=1, n_found do
+ local fullname = found[j]
+ fullname = path_normalize(fullname)
+ local new
+ if dry_run == true then
+ report ("both", 1, "db",
+ "Would have been loading “%s”", fullname)
+ else
+ report ("both", 4, "db",
+ "Loading font “%s”", fullname)
+ local new = load_font (fullname, fontnames,
+ newfontnames, texmf)
+ if new == true then
+ n_new = n_new + 1
end
end
end
- report("both", 4, "db", "%d fonts found in '%s'", n_scanned, dirname)
- return n_scanned, n_new
+
+ report("both", 4, "db", "%d fonts found in '%s'", n_found, dirname)
+ return n_found, n_new
end
--- dbobj -> dbobj -> bool? -> (int * int)
@@ -1366,7 +1487,6 @@ local scan_texmf_fonts = function (fontnames, newfontnames, dry_run)
fontdirs = fontdirs .. stringgsub(kpseexpand_path("$TTFONTS"), "^%.", "")
if not stringis_empty(fontdirs) then
for _,d in next, filesplitpath(fontdirs) do
- report("info", 4, "db", "Entering directory %s", d)
local found, new = scan_dir(d, fontnames, newfontnames, dry_run, true)
n_scanned = n_scanned + found
n_new = n_new + new
@@ -1471,7 +1591,7 @@ do --- closure for read_fonts_conf()
local fonts_conf_scanner = function (path)
local fh = ioopen(path, "r")
if not fh then
- report("both", 3, "db", "cannot open fontconfig file %s", path)
+ report("both", 3, "db", "Cannot open fontconfig file %s", path)
return
end
local raw = fh:read"*all"
@@ -1479,12 +1599,22 @@ do --- closure for read_fonts_conf()
local confdata = lpegmatch(p_cheapxml, raw)
if not confdata then
- report("both", 3, "db", "cannot scan fontconfig file %s", path)
+ report("both", 3, "db", "Cannot scan fontconfig file %s", path)
return
end
return confdata
end
+ local p_conf = P".conf" * P(-1)
+ local p_filter = (1 - p_conf)^1 * p_conf
+
+ local conf_filter = function (path)
+ if lpegmatch (p_filter, path) then
+ return true
+ end
+ return false
+ end
+
--[[doc--
read_fonts_conf_indeed() is called with six arguments; the
latter three are tables that represent the state and are
@@ -1545,7 +1675,7 @@ do --- closure for read_fonts_conf()
path, home, xdg_home,
acc, done, dirs_done)
elseif lfsisdir(path) then --- arrow code ahead
- local config_files = dirglob(filejoin(path, "*.conf"))
+ local config_files = find_files (path, conf_filter)
for _, filename in next, config_files do
if not done[filename] then
acc = read_fonts_conf_indeed(
@@ -1645,7 +1775,7 @@ end
--- dbobj -> dbobj
local gen_fast_lookups = function (fontnames)
- report("both", 2, "db", "creating filename map")
+ report("both", 2, "db", "Creating filename map")
local mappings = fontnames.mappings
local nmappings = #mappings
--- this is needlessly complicated due to texmf priorization
@@ -1685,7 +1815,7 @@ local gen_fast_lookups = function (fontnames)
local known = filenames.base[base] or filenames.bare[bare]
if known then --- known
report("both", 3, "db",
- "font file “%s” already indexed (%d)",
+ "Font file “%s” already indexed (%d)",
base, idx)
report("both", 3, "db", "> old location: %s",
(filenames.full[known] or "texmf"))
@@ -1704,7 +1834,7 @@ local gen_fast_lookups = function (fontnames)
end
if config.luaotfload.prioritize == "texmf" then
- report("both", 2, "db", "preferring texmf fonts")
+ report("both", 2, "db", "Preferring texmf fonts")
addmap(sys)
addmap(texmf)
else --- sys
@@ -1783,7 +1913,7 @@ end
local ensure_names_path = function ( )
local path = names.path.dir
if not lfsisdir(path) then
- dirmkdirs(path)
+ lfsmkdirs(path)
end
return path
end
@@ -1829,6 +1959,8 @@ save_names = function (fontnames)
os.remove(lucname)
caches.compile(fontnames, luaname, lucname)
report("info", 1, "db", "Font names database saved")
+ report("info", 3, "db", "Text: " .. luaname)
+ report("info", 3, "db", "Byte: " .. lucname)
return true
end
end
@@ -1875,7 +2007,7 @@ local purge_from_cache = function (category, path, list, all)
local checkname = file.replacesuffix(
filename, "lua", "luc")
if lfs.isfile(checkname) then
- report("info", 5, "cache", "removing %s", filename)
+ report("info", 5, "cache", "Removing %s", filename)
os.remove(filename)
n = n + 1
end
@@ -1883,15 +2015,17 @@ local purge_from_cache = function (category, path, list, all)
end
end
end
- report("info", 2, "cache", "removed lua files : %i", n)
+ report("info", 2, "cache", "Removed lua files : %i", n)
return true
end
+
--- string -> string list -> int -> string list -> string list -> string list ->
--- (string list * string list * string list * string list)
local collect_cache collect_cache = function (path, all, n, luanames,
lucnames, rest)
if not all then
- local all = dirglob(path .. "/**/*")
+ local all = find_files (path)
+
local luanames, lucnames, rest = { }, { }, { }
return collect_cache(nil, all, 1, luanames, lucnames, rest)
end
@@ -1911,9 +2045,34 @@ local collect_cache collect_cache = function (path, all, n, luanames,
return luanames, lucnames, rest, all
end
+local getwritablecachepath = function ( )
+ --- fonts.handlers.otf doesn’t exist outside a Luatex run,
+ --- so we have to improvise
+ local writable = caches.getwritablepath
+ (config.luaotfload.cache_dir)
+ if writable then
+ return writable
+ end
+end
+
+local getreadablecachepaths = function ( )
+ local readables = caches.getreadablepaths
+ (config.luaotfload.cache_dir)
+ local result = { }
+ if readables then
+ for i=1, #readables do
+ local readable = readables[i]
+ if lfsisdir (readable) then
+ result[#result+1] = readable
+ end
+ end
+ end
+ return result
+end
+
--- unit -> unit
local purge_cache = function ( )
- local writable_path = caches.getwritablepath()
+ local writable_path = getwritablecachepath ()
local luanames, lucnames, rest = collect_cache(writable_path)
if logs.get_loglevel() > 1 then
print_cache("writable path", writable_path, luanames, lucnames, rest)
@@ -1924,7 +2083,7 @@ end
--- unit -> unit
local erase_cache = function ( )
- local writable_path = caches.getwritablepath()
+ local writable_path = getwritablecachepath ()
local luanames, lucnames, rest, all = collect_cache(writable_path)
if logs.get_loglevel() > 1 then
print_cache("writable path", writable_path, luanames, lucnames, rest)
@@ -1939,19 +2098,20 @@ end
--- unit -> unit
local show_cache = function ( )
- local readable_paths = caches.getreadablepaths()
- local writable_path = caches.getwritablepath()
+ local readable_paths = getreadablecachepaths ()
+ local writable_path = getwritablecachepath ()
local luanames, lucnames, rest = collect_cache(writable_path)
- separator()
- print_cache("writable path", writable_path, luanames, lucnames, rest)
+ separator ()
+ print_cache ("writable path", writable_path,
+ luanames, lucnames, rest)
texiowrite_nl""
for i=1,#readable_paths do
local readable_path = readable_paths[i]
if readable_path ~= writable_path then
- local luanames, lucnames = collect_cache(readable_path)
- print_cache("readable path",
- readable_path,luanames,lucnames,rest)
+ local luanames, lucnames = collect_cache (readable_path)
+ print_cache ("readable path",
+ readable_path, luanames, lucnames, rest)
end
end
separator()
@@ -1971,6 +2131,7 @@ names.update = update_names
names.crude_file_lookup = crude_file_lookup
names.crude_file_lookup_verbose = crude_file_lookup_verbose
names.read_blacklist = read_blacklist
+names.sanitize_string = sanitize_string
names.getfilename = resolve_fullpath
--- font cache
diff --git a/luaotfload-features.lua b/luaotfload-features.lua
index 81d1437..690a33c 100644
--- a/luaotfload-features.lua
+++ b/luaotfload-features.lua
@@ -1032,7 +1032,7 @@ local feature_expr = ws * Cg(assignment + switch) * ws
local option = feature_expr
local feature_list = Cf(Ct""
* option
- * (featuresep * option)^0
+ * (featuresep * option^-1)^0
, rawset)
* featuresep^-1
diff --git a/luaotfload-lib-dir.lua b/luaotfload-lib-dir.lua
deleted file mode 100644
index 3d0576e..0000000
--- a/luaotfload-lib-dir.lua
+++ /dev/null
@@ -1,470 +0,0 @@
-if not modules then modules = { } end modules ['l-dir'] = {
- version = 1.001,
- comment = "companion to luat-lib.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
--- dir.expandname will be merged with cleanpath and collapsepath
-
-local type, select = type, select
-local find, gmatch, match, gsub = string.find, string.gmatch, string.match, string.gsub
-local concat, insert, remove, unpack = table.concat, table.insert, table.remove, table.unpack
-local lpegmatch = lpeg.match
-
-local P, S, R, C, Cc, Cs, Ct, Cv, V = lpeg.P, lpeg.S, lpeg.R, lpeg.C, lpeg.Cc, lpeg.Cs, lpeg.Ct, lpeg.Cv, lpeg.V
-
-dir = dir or { }
-local dir = dir
-local lfs = lfs
-
-local attributes = lfs.attributes
-local walkdir = lfs.dir
-local isdir = lfs.isdir
-local isfile = lfs.isfile
-local currentdir = lfs.currentdir
-local chdir = lfs.chdir
-
--- in case we load outside luatex
-
-if not isdir then
- function isdir(name)
- local a = attributes(name)
- return a and a.mode == "directory"
- end
- lfs.isdir = isdir
-end
-
-if not isfile then
- function isfile(name)
- local a = attributes(name)
- return a and a.mode == "file"
- end
- lfs.isfile = isfile
-end
-
--- handy
-
-function dir.current()
- return (gsub(currentdir(),"\\","/"))
-end
-
--- optimizing for no find (*) does not save time
-
---~ local function globpattern(path,patt,recurse,action) -- fails in recent luatex due to some change in lfs
---~ local ok, scanner
---~ if path == "/" then
---~ ok, scanner = xpcall(function() return walkdir(path..".") end, function() end) -- kepler safe
---~ else
---~ ok, scanner = xpcall(function() return walkdir(path) end, function() end) -- kepler safe
---~ end
---~ if ok and type(scanner) == "function" then
---~ if not find(path,"/$") then path = path .. '/' end
---~ for name in scanner do
---~ local full = path .. name
---~ local mode = attributes(full,'mode')
---~ if mode == 'file' then
---~ if find(full,patt) then
---~ action(full)
---~ end
---~ elseif recurse and (mode == "directory") and (name ~= '.') and (name ~= "..") then
---~ globpattern(full,patt,recurse,action)
---~ end
---~ end
---~ end
---~ end
-
-local lfsisdir = isdir
-
-local function isdir(path)
- path = gsub(path,"[/\\]+$","")
- return lfsisdir(path)
-end
-
-lfs.isdir = isdir
-
-local function globpattern(path,patt,recurse,action)
- if path == "/" then
- path = path .. "."
- elseif not find(path,"/$") then
- path = path .. '/'
- end
- if isdir(path) then -- lfs.isdir does not like trailing /
- for name in walkdir(path) do -- lfs.dir accepts trailing /
- local full = path .. name
- local mode = attributes(full,'mode')
- if mode == 'file' then
- if find(full,patt) then
- action(full)
- end
- elseif recurse and (mode == "directory") and (name ~= '.') and (name ~= "..") then
- globpattern(full,patt,recurse,action)
- end
- end
- end
-end
-
-dir.globpattern = globpattern
-
-local function collectpattern(path,patt,recurse,result)
- local ok, scanner
- result = result or { }
- if path == "/" then
- ok, scanner, first = xpcall(function() return walkdir(path..".") end, function() end) -- kepler safe
- else
- ok, scanner, first = xpcall(function() return walkdir(path) end, function() end) -- kepler safe
- end
- if ok and type(scanner) == "function" then
- if not find(path,"/$") then path = path .. '/' end
- for name in scanner, first do
- local full = path .. name
- local attr = attributes(full)
- local mode = attr.mode
- if mode == 'file' then
- if find(full,patt) then
- result[name] = attr
- end
- elseif recurse and (mode == "directory") and (name ~= '.') and (name ~= "..") then
- attr.list = collectpattern(full,patt,recurse)
- result[name] = attr
- end
- end
- end
- return result
-end
-
-dir.collectpattern = collectpattern
-
-local pattern = Ct {
- [1] = (C(P(".") + P("/")^1) + C(R("az","AZ") * P(":") * P("/")^0) + Cc("./")) * V(2) * V(3),
- [2] = C(((1-S("*?/"))^0 * P("/"))^0),
- [3] = C(P(1)^0)
-}
-
-local filter = Cs ( (
- P("**") / ".*" +
- P("*") / "[^/]*" +
- P("?") / "[^/]" +
- P(".") / "%%." +
- P("+") / "%%+" +
- P("-") / "%%-" +
- P(1)
-)^0 )
-
-local function glob(str,t)
- if type(t) == "function" then
- if type(str) == "table" then
- for s=1,#str do
- glob(str[s],t)
- end
- elseif isfile(str) then
- t(str)
- else
- local split = lpegmatch(pattern,str) -- we could use the file splitter
- if split then
- local root, path, base = split[1], split[2], split[3]
- local recurse = find(base,"%*%*")
- local start = root .. path
- local result = lpegmatch(filter,start .. base)
- globpattern(start,result,recurse,t)
- end
- end
- else
- if type(str) == "table" then
- local t = t or { }
- for s=1,#str do
- glob(str[s],t)
- end
- return t
- elseif isfile(str) then
- if t then
- t[#t+1] = str
- return t
- else
- return { str }
- end
- else
- local split = lpegmatch(pattern,str) -- we could use the file splitter
- if split then
- local t = t or { }
- local action = action or function(name) t[#t+1] = name end
- local root, path, base = split[1], split[2], split[3]
- local recurse = find(base,"%*%*")
- local start = root .. path
- local result = lpegmatch(filter,start .. base)
- globpattern(start,result,recurse,action)
- return t
- else
- return { }
- end
- end
- end
-end
-
-dir.glob = glob
-
---~ list = dir.glob("**/*.tif")
---~ list = dir.glob("/**/*.tif")
---~ list = dir.glob("./**/*.tif")
---~ list = dir.glob("oeps/**/*.tif")
---~ list = dir.glob("/oeps/**/*.tif")
-
-local function globfiles(path,recurse,func,files) -- func == pattern or function
- if type(func) == "string" then
- local s = func
- func = function(name) return find(name,s) end
- end
- files = files or { }
- local noffiles = #files
- for name in walkdir(path) do
- if find(name,"^%.") then
- --- skip
- else
- local mode = attributes(name,'mode')
- if mode == "directory" then
- if recurse then
- globfiles(path .. "/" .. name,recurse,func,files)
- end
- elseif mode == "file" then
- if not func or func(name) then
- noffiles = noffiles + 1
- files[noffiles] = path .. "/" .. name
- end
- end
- end
- end
- return files
-end
-
-dir.globfiles = globfiles
-
--- t = dir.glob("c:/data/develop/context/sources/**/????-*.tex")
--- t = dir.glob("c:/data/develop/tex/texmf/**/*.tex")
--- t = dir.glob("c:/data/develop/context/texmf/**/*.tex")
--- t = dir.glob("f:/minimal/tex/**/*")
--- print(dir.ls("f:/minimal/tex/**/*"))
--- print(dir.ls("*.tex"))
-
-function dir.ls(pattern)
- return concat(glob(pattern),"\n")
-end
-
---~ mkdirs("temp")
---~ mkdirs("a/b/c")
---~ mkdirs(".","/a/b/c")
---~ mkdirs("a","b","c")
-
-local make_indeed = true -- false
-
-local onwindows = os.type == "windows" or find(os.getenv("PATH"),";")
-
-if onwindows then
-
- function dir.mkdirs(...)
- local str, pth = "", ""
- for i=1,select("#",...) do
- local s = select(i,...)
- if s == "" then
- -- skip
- elseif str == "" then
- str = s
- else
- str = str .. "/" .. s
- end
- end
- local first, middle, last
- local drive = false
- first, middle, last = match(str,"^(//)(//*)(.*)$")
- if first then
- -- empty network path == local path
- else
- first, last = match(str,"^(//)/*(.-)$")
- if first then
- middle, last = match(str,"([^/]+)/+(.-)$")
- if middle then
- pth = "//" .. middle
- else
- pth = "//" .. last
- last = ""
- end
- else
- first, middle, last = match(str,"^([a-zA-Z]:)(/*)(.-)$")
- if first then
- pth, drive = first .. middle, true
- else
- middle, last = match(str,"^(/*)(.-)$")
- if not middle then
- last = str
- end
- end
- end
- end
- for s in gmatch(last,"[^/]+") do
- if pth == "" then
- pth = s
- elseif drive then
- pth, drive = pth .. s, false
- else
- pth = pth .. "/" .. s
- end
- if make_indeed and not isdir(pth) then
- lfs.mkdir(pth)
- end
- end
- return pth, (isdir(pth) == true)
- end
-
- --~ print(dir.mkdirs("","","a","c"))
- --~ print(dir.mkdirs("a"))
- --~ print(dir.mkdirs("a:"))
- --~ print(dir.mkdirs("a:/b/c"))
- --~ print(dir.mkdirs("a:b/c"))
- --~ print(dir.mkdirs("a:/bbb/c"))
- --~ print(dir.mkdirs("/a/b/c"))
- --~ print(dir.mkdirs("/aaa/b/c"))
- --~ print(dir.mkdirs("//a/b/c"))
- --~ print(dir.mkdirs("///a/b/c"))
- --~ print(dir.mkdirs("a/bbb//ccc/"))
-
-else
-
- function dir.mkdirs(...)
- local str, pth = "", ""
- for i=1,select("#",...) do
- local s = select(i,...)
- if s and s ~= "" then -- we catch nil and false
- if str ~= "" then
- str = str .. "/" .. s
- else
- str = s
- end
- end
- end
- str = gsub(str,"/+","/")
- if find(str,"^/") then
- pth = "/"
- for s in gmatch(str,"[^/]+") do
- local first = (pth == "/")
- if first then
- pth = pth .. s
- else
- pth = pth .. "/" .. s
- end
- if make_indeed and not first and not isdir(pth) then
- lfs.mkdir(pth)
- end
- end
- else
- pth = "."
- for s in gmatch(str,"[^/]+") do
- pth = pth .. "/" .. s
- if make_indeed and not isdir(pth) then
- lfs.mkdir(pth)
- end
- end
- end
- return pth, (isdir(pth) == true)
- end
-
- --~ print(dir.mkdirs("","","a","c"))
- --~ print(dir.mkdirs("a"))
- --~ print(dir.mkdirs("/a/b/c"))
- --~ print(dir.mkdirs("/aaa/b/c"))
- --~ print(dir.mkdirs("//a/b/c"))
- --~ print(dir.mkdirs("///a/b/c"))
- --~ print(dir.mkdirs("a/bbb//ccc/"))
-
-end
-
-dir.makedirs = dir.mkdirs
-
--- we can only define it here as it uses dir.current
-
-if onwindows then
-
- function dir.expandname(str) -- will be merged with cleanpath and collapsepath
- local first, nothing, last = match(str,"^(//)(//*)(.*)$")
- if first then
- first = dir.current() .. "/" -- dir.current sanitizes
- end
- if not first then
- first, last = match(str,"^(//)/*(.*)$")
- end
- if not first then
- first, last = match(str,"^([a-zA-Z]:)(.*)$")
- if first and not find(last,"^/") then
- local d = currentdir()
- if chdir(first) then
- first = dir.current()
- end
- chdir(d)
- end
- end
- if not first then
- first, last = dir.current(), str
- end
- last = gsub(last,"//","/")
- last = gsub(last,"/%./","/")
- last = gsub(last,"^/*","")
- first = gsub(first,"/*$","")
- if last == "" or last == "." then
- return first
- else
- return first .. "/" .. last
- end
- end
-
-else
-
- function dir.expandname(str) -- will be merged with cleanpath and collapsepath
- if not find(str,"^/") then
- str = currentdir() .. "/" .. str
- end
- str = gsub(str,"//","/")
- str = gsub(str,"/%./","/")
- str = gsub(str,"(.)/%.$","%1")
- return str
- end
-
-end
-
-file.expandname = dir.expandname -- for convenience
-
-local stack = { }
-
-function dir.push(newdir)
- insert(stack,currentdir())
- if newdir and newdir ~= "" then
- chdir(newdir)
- end
-end
-
-function dir.pop()
- local d = remove(stack)
- if d then
- chdir(d)
- end
- return d
-end
-
-local function found(...) -- can have nil entries
- for i=1,select("#",...) do
- local path = select(i,...)
- local kind = type(path)
- if kind == "string" then
- if isdir(path) then
- return path
- end
- elseif kind == "table" then
- -- here we asume no holes, i.e. an indexed table
- local path = found(unpack(path))
- if path then
- return path
- end
- end
- end
- -- return nil -- if we want print("crappath") to show something
-end
-
-dir.found = found
diff --git a/luaotfload-merged.lua b/luaotfload-merged.lua
index 81e3fec..52c199a 100644
--- a/luaotfload-merged.lua
+++ b/luaotfload-merged.lua
@@ -3044,10 +3044,10 @@ if not caches.namespace or caches.namespace=="" or caches.namespace=="context" t
end
do
local cachepaths=kpse.expand_var('$TEXMFCACHE') or ""
- if cachepaths=="" then
+ if cachepaths=="" or cachepaths == "$TEXMFCACHE" then
cachepaths=kpse.expand_var('$TEXMFVAR') or ""
end
- if cachepaths=="" then
+ if cachepaths=="" or cachepaths == "$TEXMFVAR" then
cachepaths=kpse.expand_var('$VARTEXMF') or ""
end
if cachepaths=="" then
@@ -3143,7 +3143,7 @@ function caches.savedata(path,name,data)
local luaname,lucname=makefullname(path,name)
if luaname then
texio.write(string.format("(save: %s)",luaname))
- table.tofile(luaname,data,true,{ reduce=true })
+ table.tofile(luaname,data,true,{ reduce=false })
if lucname and type(caches.compile)=="function" then
os.remove(lucname)
texio.write(string.format("(save: %s)",lucname))
diff --git a/luaotfload-override.lua b/luaotfload-override.lua
index caf3627..39cc172 100644
--- a/luaotfload-override.lua
+++ b/luaotfload-override.lua
@@ -15,17 +15,23 @@ because we lack a user interface to toggle per-subsystem tracing.
local module_name = "luaotfload"
+local ioopen = io.open
+local iowrite = io.write
+local lfsisdir = lfs.isdir
+local lfsisfile = lfs.isfile
+local md5sumhexa = md5.sumhexa
+local osdate = os.date
+local ostime = os.time
local select = select
local stringformat = string.format
+local stringsub = string.sub
local tableconcat = table.concat
+local texio_write_nl = texio.write_nl
local texiowrite_nl = texio.write_nl
+local texio_write = texio.write
local texiowrite = texio.write
local type = type
-local texio_write_nl = texio.write_nl
-local texio_write = texio.write
-local iowrite = io.write
-
--[[doc--
We recreate the verbosity levels previously implemented in font-nms:
@@ -62,9 +68,67 @@ logs.getloglevel = get_loglevel
logs.get_loglevel = get_loglevel
logs.get_log_level = get_loglevel
-local set_logout = function (s)
+local writeln --- scope so we can change it
+
+local log_msg = [[
+logging output redirected to %s
+to monitor the progress run "tail -f %s" in another terminal
+]]
+
+local tmppath = os.getenv "TMPDIR" or "/tmp"
+
+local choose_logfile = function ( )
+ if lfsisdir (tmppath) then
+ local fname
+ repeat --- ensure that file of that name doesn’t exist
+ fname = tmppath .. "/luaotfload-log-"
+ .. stringsub (md5sumhexa (ostime ()), 1, 8)
+ until not lfsisfile (fname)
+ iowrite (stringformat (log_msg, fname, fname))
+ return ioopen (fname, "w")
+ end
+ --- missing /tmp
+ return false
+end
+
+local set_logout = function (s, finalizers)
if s == "stdout" then
logout = "term"
+ elseif s == "file" then --- inject custom logger
+ local chan = choose_logfile ()
+ chan:write (stringformat ("logging initiated at %s",
+ osdate ("%F %T", ostime ())))
+ local writefile = function (...)
+ if select ("#", ...) == 2 then
+ chan:write (select (2, ...))
+ else
+ chan:write (select (1, ...))
+ end
+ end
+ local writefile_nl= function (...)
+ chan:write "\n"
+ if select ("#", ...) == 2 then
+ chan:write (select (2, ...))
+ else
+ chan:write (select (1, ...))
+ end
+ end
+
+ local writeln_orig = writeln
+
+ texiowrite = writefile
+ texiowrite_nl = writefile_nl
+ writeln = writefile_nl
+
+ finalizers[#finalizers+1] = function ()
+ chan:write (stringformat ("\nlogging finished at %s\n",
+ osdate ("%F %T", ostime ())))
+ chan:close ()
+ texiowrite = texio.write
+ texiowrite_nl = texio.write_nl
+ writeln = writeln_orig
+ end
+ return finalizers
--else --- remains “log”
end
end
@@ -92,7 +156,6 @@ end
io.stdout:setvbuf "no"
io.stderr:setvbuf "no"
-local writeln
if tex and (tex.jobname or tex.formatname) then
--- TeX
writeln = texiowrite_nl
@@ -106,7 +169,10 @@ end
stdout = function (category, ...)
local res = { module_name, "|", category, ":" }
- if select("#", ...) == 1 then
+ local nargs = select("#", ...)
+ if nargs == 0 then
+ writeln (tableconcat ({...}))
+ elseif nargs == 1 then
res[#res+1] = select(1, ...) -- around 30% faster than unpack()
else
res[#res+1] = stringformat(...)
@@ -152,7 +218,7 @@ local names_report = function (mode, lvl, ...)
if loglevel >= lvl then
if mode == "log" then
log (...)
- elseif mode == "both" then
+ elseif mode == "both" and log ~= "stdout" then
log (...)
stdout (...)
else
diff --git a/luaotfload-tool.lua b/luaotfload-tool.lua
index f1302a7..a353b37 100755
--- a/luaotfload-tool.lua
+++ b/luaotfload-tool.lua
@@ -46,11 +46,12 @@ end
local stringexplode = string.explode
local stringformat = string.format
local stringlower = string.lower
+local stringrep = string.rep
local tableconcat = table.concat
local texiowrite_nl = texio.write_nl
+local texiowrite = texio.write
-
-local C, Ct, P = lpeg.C, lpeg.Ct, lpeg.P
+local C, Ct, P, S = lpeg.C, lpeg.Ct, lpeg.P, lpeg.S
local lpegmatch = lpeg.match
local loader_file = "luatexbase.loader.lua"
@@ -79,9 +80,13 @@ After support for querying the database was added, the latter appeared
to be the more appropriate.
--doc]]--
-config = config or { }
-local config = config
-config.luaotfload = config.luaotfload or { }
+config = config or { }
+local config = config
+config.luaotfload = config.luaotfload or { }
+config.luaotfload.names_dir = config.luaotfload.names_dir or "names"
+config.luaotfload.cache_dir = config.luaotfload.cache_dir or "fonts"
+config.luaotfload.names_file = config.luaotfload.names_file
+ or "luaotfload-names.lua"
do -- we don’t have file.basename and the likes yet, so inline parser ftw
local slash = P"/"
@@ -108,19 +113,6 @@ config.lualibs.load_extended = false
require "lualibs"
---- prepare directories: the cache function in Luatex-Fonts
---- checks for writable directory only on startup, so everything
---- has to be laid out before we load basics-gen
-
-local cachepath = kpse.expand_var "$TEXMFVAR"
-if not lfs.isdir(cachepath) then
- dir.mkdirs(cachepath)
- if not lfs.isdir(cachepath) then
- texiowrite_nl(stringformat(
- "ERROR could not create directory %s", cachepath))
- end
-end
-
--[[doc--
\fileent{luatex-basics-gen.lua} calls functions from the
\luafunction{texio.*} library; too much for our taste.
@@ -141,8 +133,13 @@ require"alt_getopt"
local version = "2.3" -- same version number as luaotfload
local names = fonts.names
-local db_src_out = names.path.dir.."/"..names.path.basename
-local db_bin_out = file.replacesuffix(db_src_out, "luc")
+local sanitize_string = names.sanitize_string
+
+--local db_src_out = names.path.dir.."/"..names.path.basename
+local names_plain = file.join
+ (caches.getwritablepath (config.luaotfload.names_dir),
+ config.luaotfload.names_file)
+local names_bin = file.replacesuffix (names_plain, "luc")
local help_messages = {
["luaotfload-tool"] = [[
@@ -181,7 +178,11 @@ This tool is part of the luaotfload package. Valid options are:
-F --fuzzy look for approximate matches if --find fails
--limit=n limit display of fuzzy matches to <n>
(default: n = 1)
- -i --info display font metadata
+
+ -i --info display basic font metadata
+ -I --inspect display detailed font metadata
+ -w --warnings display warnings generated by the
+ fontloader library
--list=<criterion> output list of entries by field <criterion>
--list=<criterion>:<value> restrict to entries with <criterion>=<value>
@@ -198,6 +199,9 @@ The font database will be saved to
--cache=<directive> operate on font cache, where <directive> is
“show”, “purge”, or “erase”
+The font cache will be written to
+ %s
+
]],
mkluatexfontdb = [[
@@ -226,7 +230,12 @@ The font database will be saved to
local help_msg = function ( )
local template = help_messages[config.luaotfload.self]
or help_messages["luaotfload-tool"]
- texiowrite_nl(stringformat(template, config.luaotfload.self, db_src_out, db_bin_out))
+ texiowrite_nl(stringformat(template,
+ config.luaotfload.self,
+ names_plain,
+ names_bin,
+ caches.getwritablepath
+ (config.luaotfload.cache_dir)))
end
local version_msg = function ( )
@@ -235,34 +244,386 @@ local version_msg = function ( )
config.luaotfload.self, version, names.version))
end
+
+--- makeshift formatting
+
+local head_adornchars = {
+ [1] = "*", [2] = "=", [3] = "~", [4] = "-", [5] = "·",
+}
+
+local textwidth = 80
+local wd_leftcolumn = math.floor(textwidth * .25)
+local key_fmt = stringformat([[%%%ds]], wd_leftcolumn)
+local val_fmt = [[%s]]
+local fieldseparator = ":"
+local info_fmt = key_fmt .. fieldseparator .. " " .. val_fmt
+
+local currentdepth = 0
+local counterstack = { } -- counters per level
+local counterformat = "%d"
+
+local format_counter = function (stack)
+ local acc = { }
+ for lvl=1, #stack do
+ acc[#acc+1] = stringformat(counterformat, stack[lvl])
+ end
+ return tableconcat(acc, ".")
+end
+
+local print_heading = function (title, level)
+ local structuredata
+ if currentdepth == level then -- top is current
+ counterstack[#counterstack] = counterstack[#counterstack] + 1
+ elseif currentdepth < level then -- push new
+ counterstack[#counterstack+1] = 1
+ else -- pop
+ local diff = currentdepth - level
+ while diff > 0 do
+ counterstack[#counterstack] = nil
+ diff = diff - 1
+ end
+ counterstack[#counterstack] = counterstack[#counterstack] + 1
+ end
+ currentdepth = level
+
+ texiowrite_nl ""
+ if not level or level > #head_adornchars then
+ level = #head_adornchars
+ end
+ local adornchar = head_adornchars[level]
+
+ local counter = format_counter(counterstack)
+
+ local s = adornchar .. adornchar .. " "
+ .. counter .. " "
+ .. title .. " "
+ texiowrite_nl (s .. stringrep(adornchar, textwidth-utf.len(s)))
+end
+
local show_info_items = function (fontinfo)
- local items = table.sortedkeys(fontinfo)
+ local items = table.sortedkeys(fontinfo)
+ print_heading(fontinfo.fullname, 1)
+ texiowrite_nl ""
for n = 1, #items do
local item = items[n]
texiowrite_nl(stringformat(
- [[ %11s: %s]], item, fontinfo[item]))
+ info_fmt, item, fontinfo[item]))
+ end
+ texiowrite_nl ""
+end
+
+local p_eol = S"\n\r"^1
+local p_space = S" \t\v"^0
+local p_line = p_space * C((1 - p_eol)^1)^-1
+local p_lines = Ct(p_line * (p_eol^1 * p_line^-1)^0)
+
+local show_fontloader_warnings = function (ws)
+ local nws = #ws
+ print_heading(stringformat(
+ [[the fontloader emitted %d warnings]],
+ nws), 2)
+ texiowrite_nl ""
+ for i=1, nws do
+ local w = ws[i]
+ texiowrite_nl (stringformat("%d:", i))
+ local lines = lpegmatch(p_lines, w)
+ for i=1, #lines do
+ local line = lines[i]
+ texiowrite_nl(" · " .. line)
+ end
+ texiowrite_nl ""
+ end
+end
+
+local p_spacechar = S" \n\r\t\v"
+local p_wordchar = (1 - p_spacechar)
+local p_whitespace = p_spacechar^1
+local p_word = C(p_wordchar^1)
+local p_words = Ct(p_word * (p_whitespace * p_word)^0)
+
+--- string -> int -> string list
+local reflow = function (text, width)
+ local words
+ if type(text) == "string" then
+ words = lpegmatch(p_words, text)
+ if #words < 2 then
+ return { text }
+ end
+ else
+ words = text
+ if #words < 2 then
+ return words
+ end
end
+
+ local space = " "
+ local utflen = utf.len
+ local reflowed = { }
+
+ local first = words[1]
+ local linelen = #first
+ local line = { first }
+
+ for i=2, #words do
+ local word = words[i]
+ local lword = utflen(word)
+ linelen = linelen + lword + 1
+ if linelen > width then
+ reflowed[#reflowed+1] = tableconcat(line)
+ linelen = #word
+ line = { word }
+ else
+ line[#line+1] = space
+ line[#line+1] = word
+ end
+ end
+ reflowed[#reflowed+1] = tableconcat(line)
+ return reflowed
end
-local show_font_info = function (filename)
- local fullname = resolvers.findfile(filename)
+--- string -> 'a -> string list
+local print_field = function (key, val)
+ val = tostring(val)
+ local lhs = stringformat(key_fmt, key) .. fieldseparator .. " "
+ local wd_lhs = #lhs
+ local lines = reflow(val, textwidth - wd_lhs)
+
+ texiowrite_nl(lhs)
+ texiowrite(lines[1])
+ if #lines > 1 then
+ local indent = stringrep(" ", wd_lhs)
+ for i=2, #lines do
+ texiowrite_nl(indent)
+ texiowrite (lines[i])
+ end
+ end
+end
+
+local display_names = function (names)
+ print_heading("Font Metadata", 2)
+ for i=1, #names do
+ local lang, namedata = names[i].lang, names[i].names
+ print_heading(stringformat("Language: %s ", i, lang), 3)
+ texiowrite_nl ""
+ if namedata then
+ for field, value in next, namedata do
+ print_field(field, value)
+ end
+ end
+ end
+end
+
+--- see luafflib.c
+local general_fields = {
+ --- second: l -> literal | n -> length | d -> date
+ { "fullname", "l", "font name" },
+ { "version", "l", "font version" },
+ { "creationtime", "d", "creation time" },
+ { "modificationtime", "d", "modification time" },
+ { "subfonts", "n", "number of subfonts" },
+ { "glyphcnt", "l", "number of glyphs" },
+ { "weight", "l", "weight indicator" },
+ { "design_size", "l", "design size" },
+ { "design_range_bottom", "l", "design size min" },
+ { "design_range_top", "l", "design size max" },
+ { "fontstyle_id", "l", "font style id" },
+ { "fontstyle_name", "l", "font style name" },
+ { "strokewidth", "l", "stroke width" },
+ { "units_per_em", "l", "units per em" },
+ { "ascent", "l", "ascender height" },
+ { "descent", "l", "descender height" },
+ { "comments", "l", "comments" },
+ { "os2_version", "l", "os2 version" },
+ { "sfd_version", "l", "sfd version" },
+}
+
+local display_general = function (fullinfo)
+ texiowrite_nl ""
+ print_heading("General Information", 2)
+ texiowrite_nl ""
+ for i=1, #general_fields do
+ local field = general_fields[i]
+ local key, mode, desc = unpack(field)
+ local val
+ if mode == "l" then
+ val = fullinfo[key]
+ elseif mode == "n" then
+ local v = fullinfo[key]
+ if v then
+ val = #fullinfo[key]
+ end
+ elseif mode == "d" then
+ val = os.date("%F %T", fullinfo[key])
+ end
+ if not val then
+ val = "<none>"
+ end
+ print_field(desc, val)
+ end
+end
+
+local print_features = function (features)
+ for tag, data in next, features do
+ print_heading(tag, 4)
+ for script, languages in next, data do
+ local field = stringformat(key_fmt, script).. fieldseparator .. " "
+ local wd_field = #field
+ --inspect(languages.list)
+ local lines = reflow(languages.list, textwidth - wd_field)
+ local indent = stringrep(" ", wd_field)
+ texiowrite_nl(field)
+ texiowrite(lines[1])
+ if #lines > 1 then
+ for i=1, #lines do
+ texiowrite_nl(indent .. lines[i])
+ end
+ end
+ end
+ end
+end
+
+local extract_feature_info = function (set)
+ local collected = { }
+ for i=1, #set do
+ local features = set[i].features
+ if features then
+ for j=1, #features do
+ local feature = features[j]
+ local scripts = feature.scripts
+ local tagname = stringlower(feature.tag)
+ local entry = collected[tagname] or { }
+
+ for k=1, #scripts do
+ local script = scripts[k]
+ local scriptname = stringlower(script.script)
+ local c_script = entry[scriptname] or {
+ list = { },
+ set = { },
+ }
+ local list, set = c_script.list, c_script.set
+
+ for l=1, #script.langs do
+ local langname = stringlower(script.langs[l])
+ if not set[langname] then
+ list[#list+1] = langname
+ set[langname] = true
+ end
+ end
+ entry[scriptname] = c_script
+ end
+ collected[tagname] = entry
+ end
+ end
+ end
+ return collected
+end
+
+local display_feature_set = function (set)
+ local collected = extract_feature_info(set)
+ print_features(collected)
+end
+
+local display_features = function (gsub, gpos)
+ texiowrite_nl ""
+ print_heading("Features", 2)
+ print_heading("GSUB Features", 3)
+ display_feature_set(gsub)
+ print_heading("GPOS Features", 3)
+ display_feature_set(gpos)
+end
+
+local show_full_info = function (path, subfont, warnings)
+ local rawinfo, warn = fontloader.open(path, subfont)
+ if warnings then
+ show_fontloader_warnings(warn)
+ end
+ if not rawinfo then
+ texiowrite_nl(stringformat([[cannot open font %s]], path))
+ return
+ end
+ local fontdata = { }
+ local fullinfo = fontloader.to_table(rawinfo)
+ local fields = fontloader.fields(rawinfo)
+ fontloader.close(rawinfo)
+ display_names(fullinfo.names)
+ display_general(fullinfo)
+ display_features(fullinfo.gsub, fullinfo.gpos)
+end
+
+--- Subfonts returned by fontloader.info() do not correspond
+--- to the actual indices required by fontloader.open(), so
+--- we try and locate the correct one by matching the request
+--- against the full name.
+
+local subfont_by_name
+subfont_by_name = function (lst, askedname, n)
+ if not n then
+ return subfont_by_name (lst, askedname, 1)
+ end
+
+ local font = lst[n]
+ if font then
+ if sanitize_string(font.fullname) == askedname then
+ return font
+ end
+ return subfont_by_name (lst, askedname, n+1)
+ end
+ return false
+end
+
+--[[doc--
+The font info knows two levels of detail:
+
+ a) basic information returned by fontloader.info(); and
+ b) detailed information that is a subset of the font table
+ returned by fontloader.open().
+--doc]]--
+
+local show_font_info = function (basename, askedname, detail, warnings)
+ local filenames = names.data.filenames
+ local index = filenames.base[basename]
+ local fullname = filenames.full[index]
+ askedname = sanitize_string(askedname)
+ if not fullname then -- texmf
+ fullname = resolvers.findfile(basename)
+ end
if fullname then
- local fontinfo = fontloader.info(fullname)
- local nfonts = #fontinfo
+ local shortinfo = fontloader.info(fullname)
+ local nfonts = #shortinfo
if nfonts > 0 then -- true type collection
- logs.names_report(true, 1, "resolve",
- [[%s is a font collection]], filename)
- for n = 1, nfonts do
+ local subfont
+ if askedname then
+ logs.names_report(true, 1, "resolve",
+ [[%s is part of the font collection %s]],
+ askedname, basename)
+ subfont = subfont_by_name(shortinfo, askedname)
+ end
+ if subfont then
+ show_info_items(subfont)
+ if detail == true then
+ show_full_info(fullname, subfont, warnings)
+ end
+ else -- list all subfonts
logs.names_report(true, 1, "resolve",
- [[showing info for font no. %d]], n)
- show_info_items(fontinfo[n])
+ [[%s is a font collection]], basename)
+ for subfont = 1, nfonts do
+ logs.names_report(true, 1, "resolve",
+ [[Showing info for font no. %d]], n)
+ show_info_items(shortinfo[subfont])
+ if detail == true then
+ show_full_info(fullname, subfont, warnings)
+ end
+ end
end
else
- show_info_items(fontinfo)
+ show_info_items(shortinfo)
+ if detail == true then
+ show_full_info(fullname, subfont, warnings)
+ end
end
else
logs.names_report(true, 1, "resolve",
- "font %s not found", filename)
+ "Font %s not found", filename)
end
end
@@ -286,8 +647,8 @@ local actions = { } --- (jobspec -> (bool * bool)) list
actions.loglevel = function (job)
logs.set_loglevel(job.log_level)
logs.names_report("info", 3, "util",
- "setting log level", "%d", job.log_level)
- logs.names_report("log", 0, "util", "lua=%s", _VERSION)
+ "Setting log level", "%d", job.log_level)
+ logs.names_report("log", 0, "util", "Lua=%s", _VERSION)
return true, true
end
@@ -374,11 +735,11 @@ actions.query = function (job)
"Resolved file name “%s”, subfont nr. “%s”",
foundname, subfont)
else
- logs.names_report(false, 0,
- "resolve", "Resolved file name “%s”", foundname)
+ logs.names_report(false, 0, "resolve",
+ "Resolved file name “%s”", foundname)
end
if job.show_info then
- show_font_info(foundname)
+ show_font_info(foundname, query, job.full_info, job.warnings)
end
else
logs.names_report(false, 0,
@@ -469,7 +830,7 @@ actions.list = function (job)
local nmappings = #mappings
if criterion == "*" then
- logs.names_report(false, 1, "list", "all %d entries", nmappings)
+ logs.names_report(false, 1, "list", "All %d entries", nmappings)
for i=1, nmappings do
local entry = mappings[i]
local fields = get_fields(entry, asked_fields)
@@ -484,12 +845,12 @@ actions.list = function (job)
criterion = criterion[1]
asked_fields = set_primary_field(asked_fields, criterion)
- logs.names_report(false, 1, "list", "by %s", criterion)
+ logs.names_report(false, 1, "list", "By %s", criterion)
--- firstly, build a list of fonts to operate on
local targets = { }
if asked_value then --- only those whose value matches
- logs.names_report(false, 2, "list", "restricting to value %s", asked_value)
+ logs.names_report(false, 2, "list", "Restricting to value %s", asked_value)
for i=1, nmappings do
local entry = mappings[i]
if entry[criterion]
@@ -540,6 +901,22 @@ actions.list = function (job)
return true, true
end
+--- stuff to be carried out prior to exit
+
+local finalizers = { }
+
+--- returns false if at least one of the actions failed, mainly
+--- for closing io channels
+local finalize = function ()
+ local success = true
+ for _, fun in next, finalizers do
+ if type (fun) == "function" then
+ if fun () == false then success = false end
+ end
+ end
+ return success
+end
+
--[[--
Command-line processing.
mkluatexfontdb.lua relies on the script alt_getopt to process argv and
@@ -553,6 +930,8 @@ alt_getopt.
local process_cmdline = function ( ) -- unit -> jobspec
local result = { -- jobspec
force_reload = nil,
+ full_info = false,
+ warnings = false,
criterion = "",
query = "",
log_level = 0, --- 2 is approx. the old behavior
@@ -569,6 +948,7 @@ local process_cmdline = function ( ) -- unit -> jobspec
fuzzy = "F",
help = "h",
info = "i",
+ inspect = "I",
limit = 1,
list = 1,
log = 1,
@@ -578,9 +958,10 @@ local process_cmdline = function ( ) -- unit -> jobspec
update = "u",
verbose = 1 ,
version = "V",
+ warnings = "w",
}
- local short_options = "bDfFilpquvVh"
+ local short_options = "bDfFiIlpquvVhw"
local options, _, optarg =
alt_getopt.get_ordered_opts (arg, short_options, long_options)
@@ -608,12 +989,18 @@ local process_cmdline = function ( ) -- unit -> jobspec
elseif v == "verbose" then
local lvl = optarg[n]
if lvl then
- result.log_level = tonumber(lvl)
+ lvl = tonumber(lvl)
+ result.log_level = lvl
+ if lvl > 2 then
+ result.warnings = true
+ end
end
+ elseif v == "w" then
+ result.warnings = true
elseif v == "log" then
local str = optarg[n]
if str then
- logs.set_logout(str)
+ finalizers = logs.set_logout(str, finalizers)
end
elseif v == "find" then
action_pending["query"] = true
@@ -627,6 +1014,9 @@ local process_cmdline = function ( ) -- unit -> jobspec
end
elseif v == "i" then
result.show_info = true
+ elseif v == "I" then
+ result.show_info = true
+ result.full_info = true
elseif v == "alias" then
config.luaotfload.self = optarg[n]
elseif v == "l" then
@@ -667,7 +1057,7 @@ local main = function ( ) -- unit -> int
local actionname = action_sequence[i]
local exit = false
if action_pending[actionname] then
- logs.names_report("log", 3, "util", "preparing for task",
+ logs.names_report("log", 3, "util", "Preparing for task",
"%s", actionname)
local action = actions[actionname]
@@ -675,22 +1065,26 @@ local main = function ( ) -- unit -> int
if not success then
logs.names_report(false, 0, "util",
- "could not finish task", "%s", actionname)
+ "Could not finish task", "%s", actionname)
retval = -1
exit = true
elseif not continue then
logs.names_report(false, 3, "util",
- "task completed, exiting", "%s", actionname)
+ "Task completed, exiting", "%s", actionname)
exit = true
else
logs.names_report(false, 3, "util",
- "task completed successfully", "%s", actionname)
+ "Task completed successfully", "%s", actionname)
end
end
if exit then break end
end
- texiowrite_nl""
+ if finalize () == false then
+ retval = -1
+ end
+
+ --texiowrite_nl""
return retval
end
diff --git a/luaotfload-tool.rst b/luaotfload-tool.rst
index 9b03b37..31a1010 100644
--- a/luaotfload-tool.rst
+++ b/luaotfload-tool.rst
@@ -15,11 +15,11 @@
SYNOPSIS
=======================================================================
-**luaotfload** [ -bDcfFipquvVh ]
+**luaotfload** [ -bDcfFiIpquvVwh ]
**luaotfload** --update [ --force ] [ --quiet ] [ --verbose ] [ --prefer-texmf ] [ --dry-run ]
-**luaotfload** --find=FONTNAME [ --fuzzy ] [ --info ]
+**luaotfload** --find=FONTNAME [ --fuzzy ] [ --info ] [ --inspect ]
**luaotfload** --flush-lookups
@@ -71,8 +71,18 @@ query mode
--fuzzy, -F Show approximate matches to the file name if
the lookup was unsuccessful (requires
``--find``).
+
--info, -i Display basic information to a resolved font
file (requires ``--find``).
+--inspect, -I Display detailed information by loading the
+ font and analyzing the font table; very slow!
+ For the meaning of the returned fields see
+ the LuaTeX documentation.
+ (requires ``--find``).
+--warnings, -w Print the warnings generated by the fontloader
+ library (assumes ``-I``). Automatically enabled
+ if the verbosity level exceeds 2.
+
--show-blacklist, -b Show blacklisted files (not directories).
--list=CRITERION Show entries, where *CRITERION* is one of the
following:
@@ -106,8 +116,14 @@ miscellaneous
--verbose=N, -v Set verbosity level to *n* or the number of
repetitions of ``-v``.
--quiet No verbose output (log level set to zero).
---log=stdout Redirect log output to terminal (for database
- troubleshooting).
+--log=CHANNEL Redirect log output (for database
+ troubleshooting), where *CHANNEL* can be
+
+ 1) ``stdout`` -> all output will be
+ dumped to the terminal; or
+ 2) ``file`` -> write to a file to the temporary
+ directory (the name will be chosen
+ automatically (**experimental!**).
--version, -V Show version number and exit.
--help, -h Show help message and exit.
diff --git a/luaotfload.dtx b/luaotfload.dtx
index 70edf8c..b8bee75 100644
--- a/luaotfload.dtx
+++ b/luaotfload.dtx
@@ -40,7 +40,7 @@
\input docstrip.tex
\Msg{************************************************************************}
\Msg{* Installation}
-\Msg{* Package: luaotfload v2.2 OpenType layout system}
+\Msg{* Package: luaotfload v2.3 OpenType layout system}
\Msg{************************************************************************}
\keepsilent
@@ -111,7 +111,7 @@ and the derived files
%<*driver>
\NeedsTeXFormat{LaTeX2e}
\ProvidesFile{luaotfload.drv}%
- [2013/05/23 v2.2d OpenType layout system]%
+ [2013/05/23 v2.3d OpenType layout system]%
\documentclass{ltxdoc}
\usepackage{metalogo,multicol,mdwlist,fancyvrb,xspace}
\usepackage[x11names]{xcolor}
@@ -231,7 +231,7 @@ and the derived files
% \GetFileInfo{luaotfload.drv}
%
% \title{The \identifier{luaotfload} package}
-% \date{2013/05/23 v2.2d}
+% \date{2013/05/23 v2.3d}
% \author{Elie Roux · Khaled Hosny · Philipp Gesang\\
% Home: \url{https://github.com/lualatex/luaotfload}\\
% Support: \email{lualatex-dev@tug.org}}
@@ -400,7 +400,23 @@ and the derived files
% \begin{quote}
% |\font\fontname=|\meta{prefix}|:|\meta{fontname}\dots
% \end{quote}
-% where \meta{prefix} is either \verb|file:| or \verb|name:|.
+% where \meta{prefix} is either \verb|file:| or \verb|name:|.\footnote{%
+% The development version also knows two further prefixes,
+% \verb|kpse:| and \verb|my:|.
+% A \verb|kpse| lookup is restricted to files that can be found by
+% \identifier{kpathsea} and
+% will not attempt to locate system fonts.
+% This behavior can be of value when an extra degree of encapsulation is
+% needed, for instance when supplying a customized tex distribution.
+%
+% The \verb|my| lookup takes this a step further: it lets you define
+% a custom resolver function and hook it into the \luafunction{resolve_font}
+% callback.
+% This ensures full control over how a file is located.
+% For a working example see the
+% \href{https://bitbucket.org/phg/lua-la-tex-tests/src/5f6a535d/pln-lookup-callback-1.tex}
+% {test repo}.
+% }
% It determines whether the font loader should interpret the request as
% a \emphasis{file name} or
% \emphasis{font name}, respectively,
@@ -410,7 +426,26 @@ and the derived files
% “GFS Bodoni Rg”, and
% “PT Serif Caption”
% -- they are the human readable identifiers
-% usually listed in drop-down menus and the like.
+% usually listed in drop-down menus and the like.\footnote{%
+% Font names may appear like a great choice at first because they
+% offer seemingly more intuitive identifiers in comparison to arguably
+% cryptic file names:
+% “PT Sans Bold” is a lot more descriptive than \fileent{PTS75F.ttf}.
+% On the other hand, font names are quite arbitrary and there is no
+% universal method to determine their meaning.
+% While \identifier{luaotfload} provides fairly sophisticated heuristic
+% to figure out a matching font style, weight, and optical size, it
+% cannot be relied upon to work satisfactorily for all font files.
+% For an in-depth analysis of the situation and how broken font names
+% are, please refer to
+% \href{http://www.ntg.nl/pipermail/ntg-context/2013/073889.html}
+% {this post}
+% by Hans Hagen, the author of the font loader.
+% If in doubt, use filenames.
+% \fileent{luaotfload-tool} can perform the matching for you with the
+% option \verb|--find=<name>|, and you can use the file name it returns
+% in your font definition.
+% }
% 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.
@@ -1159,9 +1194,6 @@ and the derived files
% \ouritem {luaotfload-features.lua} font feature handling;
% incorporates some of the code from
% \fileent{font-otc} from \CONTEXT;
-% \ouritem {luaotfload-lib-dir.lua} \fileent{l-dir} from \CONTEXT;
-% contains functionality required
-% by \fileent{luaotfload-font-nms.lua}.
% \ouritem {luaotfload-override.lua} overrides the \CONTEXT logging
% functionality.
% \ouritem {luaotfload-loaders.lua} registers the \OpenType
@@ -1428,8 +1460,8 @@ and the derived files
%<*lua>
% \fi
% \begin{macrocode}
-luaotfload = luaotfload or {}
-local luaotfload = luaotfload
+luaotfload = luaotfload or {}
+local luaotfload = luaotfload
config = config or { }
config.luaotfload = config.luaotfload or { }
@@ -1440,7 +1472,9 @@ config.luaotfload.compatibility = config.luaotfload.compatibility or false
config.luaotfload.loglevel = config.luaotfload.loglevel or 1
config.luaotfload.color_callback = config.luaotfload.color_callback or "pre_linebreak_filter"
config.luaotfload.prioritize = config.luaotfload.prioritize or "sys"
---luaotfload.prefer_merge = config.luaotfload.prefer_merge or true
+config.luaotfload.names_dir = config.luaotfload.names_dir or "names"
+config.luaotfload.cache_dir = config.luaotfload.cache_dir or "fonts"
+config.luaotfload.names_file = config.luaotfload.names_file or "luaotfload-names.lua"
luaotfload.module = {
name = "luaotfload",
@@ -1770,7 +1804,6 @@ add_to_callback("hpack_filter",
add_to_callback("find_vf_file",
find_vf_file, "luaotfload.find_vf_file")
-loadmodule"lib-dir.lua" --- required by luaofload-database.lua
loadmodule"override.lua" --- “luat-ovr”
logs.set_loglevel(config.luaotfload.loglevel)
@@ -2036,7 +2069,7 @@ loadmodule"auxiliary.lua" --- additionaly high-level functionality (new)
\else
\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{luaotfload}%
- [2013/05/23 v2.2d OpenType layout system]
+ [2013/05/23 v2.3d OpenType layout system]
\RequirePackage{luatexbase}
\fi
\ifnum\luatexversion<76