diff options
Diffstat (limited to 'luaotfload-database.lua')
-rw-r--r-- | luaotfload-database.lua | 254 |
1 files changed, 115 insertions, 139 deletions
diff --git a/luaotfload-database.lua b/luaotfload-database.lua index 3085b63..037ca07 100644 --- a/luaotfload-database.lua +++ b/luaotfload-database.lua @@ -239,6 +239,8 @@ local load_lua_file = function (path) end --- define locals in scope +local crude_file_lookup +local crude_file_lookup_verbose local find_closest local flush_cache local font_fullinfo @@ -246,6 +248,7 @@ local load_names local read_fonts_conf local reload_db local resolve +local resolve_cached local save_names local scan_external_dir local update_names @@ -298,9 +301,13 @@ do end end ---- chain: barenames -> [fullnames ->] basenames -> findfile -local crude_file_lookup_verbose = function (data, filename) - local mappings = data.mappings +local type1_formats = { "tfm", "ofm", } + +--- string -> (string * bool | int) +crude_file_lookup_verbose = function (filename) + if not names.data then names.data = load_names() end + local data = names.data + local mappings = data.mappings local found --- look up in db first ... @@ -323,29 +330,26 @@ local crude_file_lookup_verbose = function (data, filename) if found and mappings[found] then found = mappings[found].filename report("info", 0, "db", - "crude file lookup: req=%s; hit=bare; ret=%s", + "crude file lookup: req=%s; hit=base; ret=%s", filename, found[1]) return found end - --- now look for tfm et al.; will be superseded by proper - --- format lookup - found = resolvers.findfile(filename, "tfm") - if found then - report("info", 0, "db", - "crude file lookup: req=tfm; hit=bare; ret=%s", found) - return { found, false } - end - found = resolvers.findfile(filename, "ofm") - if found then - report("info", 0, "db", - "crude file lookup: req=ofm; hit=bare; ret=%s", found) - return { found, false } + --- ofm and tfm + for i=1, #type1_formats do + local format = type1_formats[i] + if resolvers.findfile(filename, format) then + return { file.addsuffix(filename, format), false }, format + end end - return false + return { filename, false }, nil end -local crude_file_lookup = function (data, filename) +--- string -> (string * bool | int) +crude_file_lookup = function (filename) + if not names.data then names.data = load_names() end + local data = names.data + local mappings = data.mappings local found = data.barenames[filename] -- or data.fullnames[filename] or data.basenames[filename] @@ -353,11 +357,13 @@ local crude_file_lookup = function (data, filename) found = data.mappings[found] if found then return found.filename end end - found = resolvers.findfile(filename, "tfm") - if found then return { found, false } end - found = resolvers.findfile(filename, "ofm") - if found then return { found, false } end - return false + for i=1, #type1_formats do + local format = type1_formats[i] + if resolvers.findfile(filename, format) then + return { file.addsuffix(filename, format), false }, format + end + end + return { filename, false }, nil end --[[doc-- @@ -377,28 +383,12 @@ TODO: × 3) make caching optional (via the config table) for debugging × 4) make names_update() cache aware (nil if “force”) × 5) add logging - × 6) add cache control to fontdbutil + × 6) add cache control to luaotfload-tool × 7) incr db version 8) wishlist: save cache only at the end of a run 9) ??? n) PROFIT!!! ---doc]]-- - ---- the resolver is called after the font request is parsed ---- this is where we insert the cache -local normal_resolver = fonts.definers.resolve -local dummy_resolver = function (specification) - --- this ensures that the db is always loaded - --- before a lookup occurs - if not names.data then names.data = load_names() end - --inspect(specification) - local resolved = normal_resolver(specification) - --inspect(resolved) - return resolved -end - ---[[doc-- The name lookup requires both the “name” and some other keys, so we’ll concatenate them. The spec is modified in place (ugh), so we’ll have to catalogue what @@ -431,34 +421,30 @@ We’ll just cache a deep copy of the entire spec as it leaves the resolver, lest we want to worry if we caught all the details. --doc]]-- ---- spec -> spec -local cached_resolver = function (specification) +--- 'a -> 'a -> table -> (string * int|boolean * boolean) +resolve_cached = function (_, _, specification) if not names.data then names.data = load_names() end local request_cache = names.data.request_cache local request = specification.specification - report("info", 4, "cache", - "looking for “%s” in cache ...", + report("log", 4, "cache", "looking for “%s” in cache ...", request) + local found = names.data.request_cache[request] + + --- case 1) cache positive ---------------------------------------- if found then --- replay fields from cache hit report("info", 4, "cache", "found!") - for i=1, #cache_fields do - local f = cache_fields[i] - if found[f] then specification[f] = found[f] end - end - return specification + return found[1], found[2], true end - report("info", 4, "cache", "not cached; resolving") + report("log", 4, "cache", "not cached; resolving") + --- case 2) cache negative ---------------------------------------- --- first we resolve normally ... - local resolved_spec = normal_resolver(specification) - --- ... then we add the fields to the cache - local entry = { } - for i=1, #cache_fields do - local f = cache_fields[i] - entry[f] = resolved_spec[f] - end - report("info", 4, "cache", "new entry: %s", request) + local filename, subfont, success = resolve(nil, nil, specification) + if not success then return filename, subfont, false end + --- ... then we add the fields to the cache ... ... + local entry = { filename, subfont } + report("log", 4, "cache", "new entry: %s", request) names.data.request_cache[request] = entry --- obviously, the updated cache needs to be stored. @@ -466,20 +452,13 @@ local cached_resolver = function (specification) --- whenever the cache is updated. --- TODO this should trigger a save only once the --- document is compiled (finish_pdffile callback?) - report("info", 5, "cache", "saving updated cache") + --- TODO we should speed up writing by separating + --- the cache from the db + report("log", 5, "cache", "saving updated cache") save_names() - return resolved_spec + return filename, subfont, true end -local resolvers = { - dummy = dummy_resolver, - normal = normal_resolver, - cached = cached_resolver, -} - -fonts.definers.resolve = resolvers[config.luaotfload.resolver] ---fonts.definers.resolve = resolvers.cached - --[[doc-- Luatex-fonts, the font-loader package luaotfload imports, comes with @@ -522,12 +501,6 @@ resolve = function (_,_,specification) -- the 1st two parameters are used by Con if not fonts_loaded then names.data = load_names() end local data = names.data - if specification.lookup == "file" then - local found = crude_file_lookup(data, specification.name) - --local found = crude_file_lookup_verbose(data, specification.name) - if found then return found[1], found[2], true end - end - local name = sanitize_string(specification.name) local style = sanitize_string(specification.style) or "regular" @@ -569,9 +542,7 @@ resolve = function (_,_,specification) -- the 1st two parameters are used by Con local found = { } local synonym_set = style_synonyms.set - for _,face in next, data.mappings do - --- TODO we really should store those in dedicated - --- .sanitized field + for _, face in next, data.mappings do local family, subfamily, fullname, psname, fontname, pfullname local facenames = face.sanitized @@ -648,12 +619,14 @@ resolve = function (_,_,specification) -- the 1st two parameters are used by Con end if #found == 1 then - if kpselookup(found[1].filename[1]) then + --- “found” is really synonymous with “registered in the db”. + local filename = found[1].filename[1] + if lfsisfile(filename) or kpselookup(filename) then report("log", 0, "resolve", "font family='%s', subfamily='%s' found: %s", - name, style, found[1].filename[1] + name, style, filename ) - return found[1].filename[1], found[1].filename[2], true + return filename, found[1].filename[2], true end elseif #found > 1 then -- we found matching font(s) but not in the requested optical @@ -669,22 +642,25 @@ resolve = function (_,_,specification) -- the 1st two parameters are used by Con least = difference end end - if kpselookup(closest.filename[1]) then + local filename = closest.filename[1] + if lfsisfile(filename) or kpselookup(filename) then report("log", 0, "resolve", "font family='%s', subfamily='%s' found: %s", - name, style, closest.filename[1] + name, style, filename ) - return closest.filename[1], closest.filename[2], true + return filename, closest.filename[2], true end elseif found.fallback then - return found.fallback.filename[1], found.fallback.filename[2], true + return found.fallback.filename[1], + found.fallback.filename[2], + true end --- no font found so far if not fonts_reloaded then --- last straw: try reloading the database return reload_db( - "unresoled font name: “" .. name .. "”", + "unresolved font name: “" .. name .. "”", resolve, nil, nil, specification ) end @@ -741,9 +717,7 @@ find_closest = function (name, limit) local name = sanitize_string(name) limit = limit or fuzzy_limit - if not fonts_loaded then - names.data = load_names() - end + if not fonts_loaded then names.data = load_names() end local data = names.data @@ -823,7 +797,7 @@ The data inside an Opentype font file can be quite heterogeneous. Thus in order to get the relevant information, parts of the original table as returned by the font file reader need to be relocated. --doc]]-- -font_fullinfo = function (filename, subfont, texmf) +font_fullinfo = function (filename, subfont) local tfmdata = { } local rawfont = fontloader.open(filename, subfont) if not rawfont then @@ -870,10 +844,7 @@ font_fullinfo = function (filename, subfont, texmf) tfmdata.fontname = metadata.fontname tfmdata.fullname = metadata.fullname tfmdata.familyname = metadata.familyname - tfmdata.filename = { - texmf and filebasename(filename) or filename, - subfont - } + tfmdata.filename = { filename, subfont } -- always store full path tfmdata.weight = metadata.pfminfo.weight tfmdata.width = metadata.pfminfo.width tfmdata.slant = metadata.italicangle @@ -887,8 +858,10 @@ font_fullinfo = function (filename, subfont, texmf) end --- we return true if the fond is new or re-indexed ---- string -> dbobj -> dbobj -> bool -> bool -local load_font = function (fullname, fontnames, newfontnames, texmf) +--- string -> dbobj -> dbobj -> bool +local load_font = function (fullname, fontnames, newfontnames) + if not fullname then return false end + local newmappings = newfontnames.mappings local newstatus = newfontnames.status @@ -905,47 +878,41 @@ local load_font = function (fullname, fontnames, newfontnames, texmf) local basename = filebasename(fullname) local barename = filenameonly(fullname) - --- entryname is apparently the identifier a font is - --- loaded by; it is different for files in the texmf - --- (due to kpse? idk.) - --- entryname = texmf : true -> basename | false -> fullname - local entryname = texmf and basename or fullname - - if not fullname then return false end + local entryname = basename - if names.blacklist[fullname] - or names.blacklist[basename] + if names.blacklist[fullname] or names.blacklist[basename] then report("log", 2, "db", "ignoring blacklisted font “%s”", fullname) return false end + local timestamp, db_timestamp - db_timestamp = status[entryname] - and status[entryname].timestamp + db_timestamp = status[fullname] + and status[fullname].timestamp timestamp = lfs.attributes(fullname, "modification") - local index_status = newstatus[entryname] - or (not texmf and newstatus[basename]) - local teststat = newstatus[entryname] + local index_status = newstatus[fullname] --- index_status: nil | false | table if index_status and index_status.timestamp == timestamp then -- already indexed this run return false end - newstatus[entryname] = newstatus[entryname] or { } - newstatus[entryname].timestamp = timestamp - newstatus[entryname].index = newstatus[entryname].index or { } + newstatus[fullname] = newstatus[fullname] or { } + newstatus[fullname].timestamp = timestamp + newstatus[fullname].index = newstatus[fullname].index or { } + --- this test compares the modification data registered + --- in the database with the current one if db_timestamp == timestamp - and not newstatus[entryname].index[1] then - for _,v in next, status[entryname].index do - local index = #newstatus[entryname].index + and not newstatus[fullname].index[1] then + for _,v in next, status[fullname].index do + local index = #newstatus[fullname].index local fullinfo = mappings[v] local location = #newmappings + 1 newmappings[location] = fullinfo --- keep - newstatus[entryname].index[index+1] = location --- is this actually used anywhere? + newstatus[fullname].index[index+1] = location --- is this actually used anywhere? -- newfullnames[fullname] = location newbasenames[basename] = location newbarenames[barename] = location @@ -958,34 +925,34 @@ local load_font = function (fullname, fontnames, newfontnames, texmf) if info then if type(info) == "table" and #info > 1 then --- ttc for n_font = 1, #info do - local fullinfo = font_fullinfo(fullname, n_font-1, texmf) + local fullinfo = font_fullinfo(fullname, n_font-1) if not fullinfo then return false end local location = #newmappings+1 - local index = newstatus[entryname].index[n_font] + local index = newstatus[fullname].index[n_font] if not index then index = location end newmappings[index] = fullinfo -- newfullnames[fullname] = location newbasenames[basename] = location newbarenames[barename] = location - newstatus[entryname].index[n_font] = index + newstatus[fullname].index[n_font] = index end else - local fullinfo = font_fullinfo(fullname, false, texmf) + local fullinfo = font_fullinfo(fullname, false) if not fullinfo then return false end local location = #newmappings+1 - local index = newstatus[entryname].index[1] + local index = newstatus[fullname].index[1] if not index then index = location end newmappings[index] = fullinfo -- newfullnames[fullname] = location newbasenames[basename] = location newbarenames[barename] = location - newstatus[entryname].index[1] = index + newstatus[fullname].index[1] = index end else --- missing info @@ -1096,13 +1063,13 @@ for key, value in next, font_extensions do end --- string -> dbobj -> dbobj -> bool -> (int * int) -local scan_dir = function (dirname, fontnames, newfontnames, texmf) +local scan_dir = function (dirname, fontnames, newfontnames) --[[ This function scans a directory and populates the list of fonts with all the fonts it finds. - dirname is the name of the directory to scan - names is the font database to fill -> no such term!!! - - texmf is a boolean saying if we are scanning a texmf directory + - texmf used to be a boolean saying if we are scanning a texmf directory ]] local n_scanned, n_new = 0, 0 --- total of fonts collected report("log", 2, "db", "scanning", "%s", dirname) @@ -1118,7 +1085,7 @@ local scan_dir = function (dirname, fontnames, newfontnames, texmf) local fullname = found[j] fullname = path_normalize(fullname) report("log", 2, "db", "loading font “%s”", fullname) - local new = load_font(fullname, fontnames, newfontnames, texmf) + local new = load_font(fullname, fontnames, newfontnames) if new then n_new = n_new + 1 end end end @@ -1142,7 +1109,7 @@ local function scan_texmf_fonts(fontnames, newfontnames) fontdirs = fontdirs .. stringgsub(kpseexpand_path("$TTFONTS"), "^%.", "") if not stringis_empty(fontdirs) then for _,d in next, filesplitpath(fontdirs) do - local found, new = scan_dir(d, fontnames, newfontnames, true) + local found, new = scan_dir(d, fontnames, newfontnames) n_scanned = n_scanned + found n_new = n_new + new end @@ -1409,7 +1376,7 @@ local function scan_os_fonts(fontnames, newfontnames) report("info", 2, "db", "Scanning OS fonts...") report("info", 3, "db", "Searching in static system directories...") for _,d in next, get_os_dirs() do - local found, new = scan_dir(d, fontnames, newfontnames, false) + local found, new = scan_dir(d, fontnames, newfontnames) n_scanned = n_scanned + found n_new = n_new + new end @@ -1442,9 +1409,9 @@ update_names = function (fontnames, force) fontnames = load_names() end if fontnames.version ~= names.version then - fontnames = fontnames_init(true) report("log", 1, "db", "No font names database or old " .. "one found; generating new one") + fontnames = fontnames_init(true) end end local newfontnames = fontnames_init(true) @@ -1508,15 +1475,24 @@ scan_external_dir = function (dir) end --- export functionality to the namespace “fonts.names” -names.flush_cache = flush_cache -names.load = load_names -names.save = save_names -names.scan = scan_external_dir -names.update = update_names - -names.resolve = resolve --- replace the resolver from luatex-fonts -names.resolvespec = resolve -names.find_closest = find_closest +names.flush_cache = flush_cache +names.load = load_names +names.save = save_names +names.scan = scan_external_dir +names.update = update_names +names.crude_file_lookup = crude_file_lookup +names.crude_file_lookup_verbose = crude_file_lookup_verbose + +--- replace the resolver from luatex-fonts +if config.luaotfload.resolver == "cached" then + report("info", 0, "cache", "caching of name: lookups active") + names.resolve = resolve_cached + names.resolvespec = resolve_cached +else + names.resolve = resolve + names.resolvespec = resolve +end +names.find_closest = find_closest -- for testing purpose names.read_fonts_conf = read_fonts_conf |