diff options
author | Philipp Gesang <phg42.2a@gmail.com> | 2013-07-03 10:18:36 -0700 |
---|---|---|
committer | Philipp Gesang <phg42.2a@gmail.com> | 2013-07-03 10:18:36 -0700 |
commit | 47138047e4b2f7500c627034f83c6343d61bf257 (patch) | |
tree | 4bef4a3010c9de1275623c5f649fedf6844535fa | |
parent | d83870c2e97cbae23a1d011f0fd0e2994a893e3b (diff) | |
parent | 4adcf7591ed8104b0b846d500a5d30d4ff32512e (diff) | |
download | luaotfload-47138047e4b2f7500c627034f83c6343d61bf257.tar.gz |
Merge pull request #104 from phi-gamma/master
update to version 2.3
-rw-r--r-- | NEWS | 6 | ||||
-rw-r--r-- | filegraph.dot | 3 | ||||
-rw-r--r-- | luaotfload-basics-gen.lua | 4 | ||||
-rw-r--r-- | luaotfload-database.lua | 493 | ||||
-rw-r--r-- | luaotfload-features.lua | 2 | ||||
-rw-r--r-- | luaotfload-lib-dir.lua | 470 | ||||
-rw-r--r-- | luaotfload-merged.lua | 6 | ||||
-rw-r--r-- | luaotfload-override.lua | 82 | ||||
-rwxr-xr-x | luaotfload-tool.lua | 496 | ||||
-rw-r--r-- | luaotfload-tool.rst | 24 | ||||
-rw-r--r-- | luaotfload.dtx | 59 |
11 files changed, 924 insertions, 721 deletions
@@ -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 |