diff options
author | Philipp Gesang <phg42.2a@gmail.com> | 2013-04-29 23:12:38 +0200 |
---|---|---|
committer | Philipp Gesang <phg42.2a@gmail.com> | 2013-04-29 23:12:38 +0200 |
commit | ebdd60e14123b44f2e4f74b108ff34fadf480467 (patch) | |
tree | c9528e5979e5077e86c71ca31caab77c1f53ca82 | |
parent | 1d871e6152c1acfa01db6fd90ee809274b9f8751 (diff) | |
parent | 572c0e2de196d2d9551cd205b7f0c69800fb7516 (diff) | |
download | luaotfload-ebdd60e14123b44f2e4f74b108ff34fadf480467.tar.gz |
merge from phi-gamma/master
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | NEWS | 24 | ||||
-rw-r--r-- | filegraph.dot | 2 | ||||
-rw-r--r-- | luaotfload-database.lua | 254 | ||||
-rw-r--r-- | luaotfload-features.lua | 395 | ||||
-rw-r--r-- | luaotfload-merged.lua | 45 | ||||
-rw-r--r-- | luaotfload-override.lua | 10 | ||||
-rwxr-xr-x | luaotfload-tool.lua (renamed from fontdbutil.lua) | 31 | ||||
-rw-r--r-- | luaotfload.dtx | 406 | ||||
-rw-r--r-- | tests/pln-request-4-slashed.tex | 12 | ||||
-rw-r--r-- | tests/pln-request-5-cached.tex | 18 | ||||
-rw-r--r-- | tests/pln-subfont-1.tex | 12 | ||||
-rw-r--r-- | tests/pln-tfm.tex | 8 |
13 files changed, 901 insertions, 318 deletions
@@ -4,7 +4,7 @@ NAME = luaotfload DOC = $(NAME).pdf DTX = $(NAME).dtx OTFL = $(wildcard otfl-*.lua) otfl-blacklist.cnf font-age.lua -SCRIPT = fontdbutil +SCRIPT = luaotfload-tool.lua GLYPHSCRIPT = mkglyphlist GLYPHSOURCE = glyphlist.txt @@ -6,10 +6,26 @@ Change History backward-incompatible changes in the font structure (fontspec and unicode-math must be updated) * Synchronisation with ConTeXt is now easier and can be done by just - updating otfl-fonts-merged.lua (available in ConTeXt) - * Improve documentation - * renaming mkluatexfontdb into fontdbutil, with more search functionalities - * blacklisting font lingoes.ttf + updating luaotfload-merged.lua (<= luatex-fonts-merged.lua from + the LuaTeX-Plain format) + * Improved and extended documentation + * Blacklisting font lingoes.ttf while removing several TrueType collections + from the blacklist + * Font filenames are stored in the database (file: lookups more efficient) + * Optional caching of name: lookups + * Increased fidelity of XeTeX emulation + * Renaming mkluatexfontdb into luaotfload-tool (the old behavior is kept if + the script is symlinked/ renamed mkluatexfontdb) + * Added options to luaotfload-tool for simple database queries + * Logging facilities have been rewritten and allow for more granularity + * All files have been renamed, abandoning cryptic acronyms; also + the new prefix is »luaotfload« (<= »otfl«) + * The Adobe Glyph List (font-age.lua) is now built via script (mkglyphlist) + * Hans adapted the font loader to several of our requests (attribute + allocation, custom merged package name, non-ascii names in font cache + etc.) + * There is now a central, non-personal dev repo on github: + https://github.com/lualatex/luaotfload 2013/04/25, luaotfload v1.29: * Reverting the unified resolver, as the database was rebuilt too often diff --git a/filegraph.dot b/filegraph.dot index a0eadec..d69937b 100644 --- a/filegraph.dot +++ b/filegraph.dot @@ -62,7 +62,7 @@ strict digraph luaotfload_files { //looks weird with circo ... * main files * ································································· */ - fontdbutil [label = "fontdbutil\nmkluatexfontdb.lua", + fontdbutil [label = "luaotfload-util\nmkluatexfontdb.lua", shape = rect, width = "3.2cm", height = "1.2cm", diff --git a/luaotfload-database.lua b/luaotfload-database.lua index 3085b63..037ca07 100644 --- a/luaotfload-database.lua +++ b/luaotfload-database.lua @@ -239,6 +239,8 @@ local load_lua_file = function (path) end --- define locals in scope +local crude_file_lookup +local crude_file_lookup_verbose local find_closest local flush_cache local font_fullinfo @@ -246,6 +248,7 @@ local load_names local read_fonts_conf local reload_db local resolve +local resolve_cached local save_names local scan_external_dir local update_names @@ -298,9 +301,13 @@ do end end ---- chain: barenames -> [fullnames ->] basenames -> findfile -local crude_file_lookup_verbose = function (data, filename) - local mappings = data.mappings +local type1_formats = { "tfm", "ofm", } + +--- string -> (string * bool | int) +crude_file_lookup_verbose = function (filename) + if not names.data then names.data = load_names() end + local data = names.data + local mappings = data.mappings local found --- look up in db first ... @@ -323,29 +330,26 @@ local crude_file_lookup_verbose = function (data, filename) if found and mappings[found] then found = mappings[found].filename report("info", 0, "db", - "crude file lookup: req=%s; hit=bare; ret=%s", + "crude file lookup: req=%s; hit=base; ret=%s", filename, found[1]) return found end - --- now look for tfm et al.; will be superseded by proper - --- format lookup - found = resolvers.findfile(filename, "tfm") - if found then - report("info", 0, "db", - "crude file lookup: req=tfm; hit=bare; ret=%s", found) - return { found, false } - end - found = resolvers.findfile(filename, "ofm") - if found then - report("info", 0, "db", - "crude file lookup: req=ofm; hit=bare; ret=%s", found) - return { found, false } + --- ofm and tfm + for i=1, #type1_formats do + local format = type1_formats[i] + if resolvers.findfile(filename, format) then + return { file.addsuffix(filename, format), false }, format + end end - return false + return { filename, false }, nil end -local crude_file_lookup = function (data, filename) +--- string -> (string * bool | int) +crude_file_lookup = function (filename) + if not names.data then names.data = load_names() end + local data = names.data + local mappings = data.mappings local found = data.barenames[filename] -- or data.fullnames[filename] or data.basenames[filename] @@ -353,11 +357,13 @@ local crude_file_lookup = function (data, filename) found = data.mappings[found] if found then return found.filename end end - found = resolvers.findfile(filename, "tfm") - if found then return { found, false } end - found = resolvers.findfile(filename, "ofm") - if found then return { found, false } end - return false + for i=1, #type1_formats do + local format = type1_formats[i] + if resolvers.findfile(filename, format) then + return { file.addsuffix(filename, format), false }, format + end + end + return { filename, false }, nil end --[[doc-- @@ -377,28 +383,12 @@ TODO: × 3) make caching optional (via the config table) for debugging × 4) make names_update() cache aware (nil if “force”) × 5) add logging - × 6) add cache control to fontdbutil + × 6) add cache control to luaotfload-tool × 7) incr db version 8) wishlist: save cache only at the end of a run 9) ??? n) PROFIT!!! ---doc]]-- - ---- the resolver is called after the font request is parsed ---- this is where we insert the cache -local normal_resolver = fonts.definers.resolve -local dummy_resolver = function (specification) - --- this ensures that the db is always loaded - --- before a lookup occurs - if not names.data then names.data = load_names() end - --inspect(specification) - local resolved = normal_resolver(specification) - --inspect(resolved) - return resolved -end - ---[[doc-- The name lookup requires both the “name” and some other keys, so we’ll concatenate them. The spec is modified in place (ugh), so we’ll have to catalogue what @@ -431,34 +421,30 @@ We’ll just cache a deep copy of the entire spec as it leaves the resolver, lest we want to worry if we caught all the details. --doc]]-- ---- spec -> spec -local cached_resolver = function (specification) +--- 'a -> 'a -> table -> (string * int|boolean * boolean) +resolve_cached = function (_, _, specification) if not names.data then names.data = load_names() end local request_cache = names.data.request_cache local request = specification.specification - report("info", 4, "cache", - "looking for “%s” in cache ...", + report("log", 4, "cache", "looking for “%s” in cache ...", request) + local found = names.data.request_cache[request] + + --- case 1) cache positive ---------------------------------------- if found then --- replay fields from cache hit report("info", 4, "cache", "found!") - for i=1, #cache_fields do - local f = cache_fields[i] - if found[f] then specification[f] = found[f] end - end - return specification + return found[1], found[2], true end - report("info", 4, "cache", "not cached; resolving") + report("log", 4, "cache", "not cached; resolving") + --- case 2) cache negative ---------------------------------------- --- first we resolve normally ... - local resolved_spec = normal_resolver(specification) - --- ... then we add the fields to the cache - local entry = { } - for i=1, #cache_fields do - local f = cache_fields[i] - entry[f] = resolved_spec[f] - end - report("info", 4, "cache", "new entry: %s", request) + local filename, subfont, success = resolve(nil, nil, specification) + if not success then return filename, subfont, false end + --- ... then we add the fields to the cache ... ... + local entry = { filename, subfont } + report("log", 4, "cache", "new entry: %s", request) names.data.request_cache[request] = entry --- obviously, the updated cache needs to be stored. @@ -466,20 +452,13 @@ local cached_resolver = function (specification) --- whenever the cache is updated. --- TODO this should trigger a save only once the --- document is compiled (finish_pdffile callback?) - report("info", 5, "cache", "saving updated cache") + --- TODO we should speed up writing by separating + --- the cache from the db + report("log", 5, "cache", "saving updated cache") save_names() - return resolved_spec + return filename, subfont, true end -local resolvers = { - dummy = dummy_resolver, - normal = normal_resolver, - cached = cached_resolver, -} - -fonts.definers.resolve = resolvers[config.luaotfload.resolver] ---fonts.definers.resolve = resolvers.cached - --[[doc-- Luatex-fonts, the font-loader package luaotfload imports, comes with @@ -522,12 +501,6 @@ resolve = function (_,_,specification) -- the 1st two parameters are used by Con if not fonts_loaded then names.data = load_names() end local data = names.data - if specification.lookup == "file" then - local found = crude_file_lookup(data, specification.name) - --local found = crude_file_lookup_verbose(data, specification.name) - if found then return found[1], found[2], true end - end - local name = sanitize_string(specification.name) local style = sanitize_string(specification.style) or "regular" @@ -569,9 +542,7 @@ resolve = function (_,_,specification) -- the 1st two parameters are used by Con local found = { } local synonym_set = style_synonyms.set - for _,face in next, data.mappings do - --- TODO we really should store those in dedicated - --- .sanitized field + for _, face in next, data.mappings do local family, subfamily, fullname, psname, fontname, pfullname local facenames = face.sanitized @@ -648,12 +619,14 @@ resolve = function (_,_,specification) -- the 1st two parameters are used by Con end if #found == 1 then - if kpselookup(found[1].filename[1]) then + --- “found” is really synonymous with “registered in the db”. + local filename = found[1].filename[1] + if lfsisfile(filename) or kpselookup(filename) then report("log", 0, "resolve", "font family='%s', subfamily='%s' found: %s", - name, style, found[1].filename[1] + name, style, filename ) - return found[1].filename[1], found[1].filename[2], true + return filename, found[1].filename[2], true end elseif #found > 1 then -- we found matching font(s) but not in the requested optical @@ -669,22 +642,25 @@ resolve = function (_,_,specification) -- the 1st two parameters are used by Con least = difference end end - if kpselookup(closest.filename[1]) then + local filename = closest.filename[1] + if lfsisfile(filename) or kpselookup(filename) then report("log", 0, "resolve", "font family='%s', subfamily='%s' found: %s", - name, style, closest.filename[1] + name, style, filename ) - return closest.filename[1], closest.filename[2], true + return filename, closest.filename[2], true end elseif found.fallback then - return found.fallback.filename[1], found.fallback.filename[2], true + return found.fallback.filename[1], + found.fallback.filename[2], + true end --- no font found so far if not fonts_reloaded then --- last straw: try reloading the database return reload_db( - "unresoled font name: “" .. name .. "”", + "unresolved font name: “" .. name .. "”", resolve, nil, nil, specification ) end @@ -741,9 +717,7 @@ find_closest = function (name, limit) local name = sanitize_string(name) limit = limit or fuzzy_limit - if not fonts_loaded then - names.data = load_names() - end + if not fonts_loaded then names.data = load_names() end local data = names.data @@ -823,7 +797,7 @@ The data inside an Opentype font file can be quite heterogeneous. Thus in order to get the relevant information, parts of the original table as returned by the font file reader need to be relocated. --doc]]-- -font_fullinfo = function (filename, subfont, texmf) +font_fullinfo = function (filename, subfont) local tfmdata = { } local rawfont = fontloader.open(filename, subfont) if not rawfont then @@ -870,10 +844,7 @@ font_fullinfo = function (filename, subfont, texmf) tfmdata.fontname = metadata.fontname tfmdata.fullname = metadata.fullname tfmdata.familyname = metadata.familyname - tfmdata.filename = { - texmf and filebasename(filename) or filename, - subfont - } + tfmdata.filename = { filename, subfont } -- always store full path tfmdata.weight = metadata.pfminfo.weight tfmdata.width = metadata.pfminfo.width tfmdata.slant = metadata.italicangle @@ -887,8 +858,10 @@ font_fullinfo = function (filename, subfont, texmf) end --- we return true if the fond is new or re-indexed ---- string -> dbobj -> dbobj -> bool -> bool -local load_font = function (fullname, fontnames, newfontnames, texmf) +--- string -> dbobj -> dbobj -> bool +local load_font = function (fullname, fontnames, newfontnames) + if not fullname then return false end + local newmappings = newfontnames.mappings local newstatus = newfontnames.status @@ -905,47 +878,41 @@ local load_font = function (fullname, fontnames, newfontnames, texmf) local basename = filebasename(fullname) local barename = filenameonly(fullname) - --- entryname is apparently the identifier a font is - --- loaded by; it is different for files in the texmf - --- (due to kpse? idk.) - --- entryname = texmf : true -> basename | false -> fullname - local entryname = texmf and basename or fullname - - if not fullname then return false end + local entryname = basename - if names.blacklist[fullname] - or names.blacklist[basename] + if names.blacklist[fullname] or names.blacklist[basename] then report("log", 2, "db", "ignoring blacklisted font “%s”", fullname) return false end + local timestamp, db_timestamp - db_timestamp = status[entryname] - and status[entryname].timestamp + db_timestamp = status[fullname] + and status[fullname].timestamp timestamp = lfs.attributes(fullname, "modification") - local index_status = newstatus[entryname] - or (not texmf and newstatus[basename]) - local teststat = newstatus[entryname] + local index_status = newstatus[fullname] --- index_status: nil | false | table if index_status and index_status.timestamp == timestamp then -- already indexed this run return false end - newstatus[entryname] = newstatus[entryname] or { } - newstatus[entryname].timestamp = timestamp - newstatus[entryname].index = newstatus[entryname].index or { } + newstatus[fullname] = newstatus[fullname] or { } + newstatus[fullname].timestamp = timestamp + newstatus[fullname].index = newstatus[fullname].index or { } + --- this test compares the modification data registered + --- in the database with the current one if db_timestamp == timestamp - and not newstatus[entryname].index[1] then - for _,v in next, status[entryname].index do - local index = #newstatus[entryname].index + and not newstatus[fullname].index[1] then + for _,v in next, status[fullname].index do + local index = #newstatus[fullname].index local fullinfo = mappings[v] local location = #newmappings + 1 newmappings[location] = fullinfo --- keep - newstatus[entryname].index[index+1] = location --- is this actually used anywhere? + newstatus[fullname].index[index+1] = location --- is this actually used anywhere? -- newfullnames[fullname] = location newbasenames[basename] = location newbarenames[barename] = location @@ -958,34 +925,34 @@ local load_font = function (fullname, fontnames, newfontnames, texmf) if info then if type(info) == "table" and #info > 1 then --- ttc for n_font = 1, #info do - local fullinfo = font_fullinfo(fullname, n_font-1, texmf) + local fullinfo = font_fullinfo(fullname, n_font-1) if not fullinfo then return false end local location = #newmappings+1 - local index = newstatus[entryname].index[n_font] + local index = newstatus[fullname].index[n_font] if not index then index = location end newmappings[index] = fullinfo -- newfullnames[fullname] = location newbasenames[basename] = location newbarenames[barename] = location - newstatus[entryname].index[n_font] = index + newstatus[fullname].index[n_font] = index end else - local fullinfo = font_fullinfo(fullname, false, texmf) + local fullinfo = font_fullinfo(fullname, false) if not fullinfo then return false end local location = #newmappings+1 - local index = newstatus[entryname].index[1] + local index = newstatus[fullname].index[1] if not index then index = location end newmappings[index] = fullinfo -- newfullnames[fullname] = location newbasenames[basename] = location newbarenames[barename] = location - newstatus[entryname].index[1] = index + newstatus[fullname].index[1] = index end else --- missing info @@ -1096,13 +1063,13 @@ for key, value in next, font_extensions do end --- string -> dbobj -> dbobj -> bool -> (int * int) -local scan_dir = function (dirname, fontnames, newfontnames, texmf) +local scan_dir = function (dirname, fontnames, newfontnames) --[[ This function scans a directory and populates the list of fonts with all the fonts it finds. - dirname is the name of the directory to scan - names is the font database to fill -> no such term!!! - - texmf is a boolean saying if we are scanning a texmf directory + - texmf used to be a boolean saying if we are scanning a texmf directory ]] local n_scanned, n_new = 0, 0 --- total of fonts collected report("log", 2, "db", "scanning", "%s", dirname) @@ -1118,7 +1085,7 @@ local scan_dir = function (dirname, fontnames, newfontnames, texmf) local fullname = found[j] fullname = path_normalize(fullname) report("log", 2, "db", "loading font “%s”", fullname) - local new = load_font(fullname, fontnames, newfontnames, texmf) + local new = load_font(fullname, fontnames, newfontnames) if new then n_new = n_new + 1 end end end @@ -1142,7 +1109,7 @@ local function scan_texmf_fonts(fontnames, newfontnames) fontdirs = fontdirs .. stringgsub(kpseexpand_path("$TTFONTS"), "^%.", "") if not stringis_empty(fontdirs) then for _,d in next, filesplitpath(fontdirs) do - local found, new = scan_dir(d, fontnames, newfontnames, true) + local found, new = scan_dir(d, fontnames, newfontnames) n_scanned = n_scanned + found n_new = n_new + new end @@ -1409,7 +1376,7 @@ local function scan_os_fonts(fontnames, newfontnames) report("info", 2, "db", "Scanning OS fonts...") report("info", 3, "db", "Searching in static system directories...") for _,d in next, get_os_dirs() do - local found, new = scan_dir(d, fontnames, newfontnames, false) + local found, new = scan_dir(d, fontnames, newfontnames) n_scanned = n_scanned + found n_new = n_new + new end @@ -1442,9 +1409,9 @@ update_names = function (fontnames, force) fontnames = load_names() end if fontnames.version ~= names.version then - fontnames = fontnames_init(true) report("log", 1, "db", "No font names database or old " .. "one found; generating new one") + fontnames = fontnames_init(true) end end local newfontnames = fontnames_init(true) @@ -1508,15 +1475,24 @@ scan_external_dir = function (dir) end --- export functionality to the namespace “fonts.names” -names.flush_cache = flush_cache -names.load = load_names -names.save = save_names -names.scan = scan_external_dir -names.update = update_names - -names.resolve = resolve --- replace the resolver from luatex-fonts -names.resolvespec = resolve -names.find_closest = find_closest +names.flush_cache = flush_cache +names.load = load_names +names.save = save_names +names.scan = scan_external_dir +names.update = update_names +names.crude_file_lookup = crude_file_lookup +names.crude_file_lookup_verbose = crude_file_lookup_verbose + +--- replace the resolver from luatex-fonts +if config.luaotfload.resolver == "cached" then + report("info", 0, "cache", "caching of name: lookups active") + names.resolve = resolve_cached + names.resolvespec = resolve_cached +else + names.resolve = resolve + names.resolvespec = resolve +end +names.find_closest = find_closest -- for testing purpose names.read_fonts_conf = read_fonts_conf diff --git a/luaotfload-features.lua b/luaotfload-features.lua index 78f8256..494d02d 100644 --- a/luaotfload-features.lua +++ b/luaotfload-features.lua @@ -26,7 +26,7 @@ function fonts.definers.getspecification(str) return "", str, "", ":", str end -local feature_list = { } +local old_feature_list = { } local report = logs.names_report @@ -64,20 +64,23 @@ local isstyle = function (request) for _,v in next, request do local stylename = supported[v] if stylename then - feature_list.style = stylename + old_feature_list.style = stylename elseif stringfind(v, "^s=") then --- after all, we want everything after the second byte ... local val = stringsub(v, 3) - feature_list.optsize = val + old_feature_list.optsize = val elseif stylename == false then report("log", 0, - "load font", "unsupported font option: %s", v) + "load", "unsupported font option: %s", v) elseif not stringis_empty(v) then - feature_list.style = stringgsub(v, "[^%a%d]", "") + old_feature_list.style = stringgsub(v, "[^%a%d]", "") end end end +--- TODO an option to dump the default features for a script would make +--- a nice addition to luaotfload-tool + local defaults = { dflt = { "ccmp", "locl", "rlig", "liga", "clig", @@ -131,17 +134,22 @@ defaults.tibt = defaults.khmr defaults.lao = defaults.thai +--[[doc-- +Which features are active by default depends on the script requested. +--doc]]-- + --- (string, string) dict -> (string, string) dict local set_default_features = function (speclist) + speclist = speclist or { } local script = speclist.script or "dflt" - report("log", 0, "load font", + report("log", 0, "load", "auto-selecting default features for script: %s", script) local requested = defaults[script] if not requested then - report("log", 0, "load font", + report("log", 0, "load", "no defaults for script “%s”, falling back to “dflt”", script) requested = defaults.dflt @@ -149,9 +157,7 @@ local set_default_features = function (speclist) for i=1, #requested do local feat = requested[i] - if speclist[feat] ~= false then - speclist[feat] = true - end + if speclist[feat] ~= false then speclist[feat] = true end end for feat, state in next, global_defaults do @@ -163,14 +169,15 @@ local set_default_features = function (speclist) return speclist end -local function issome () feature_list.lookup = 'name' end -local function isfile () feature_list.lookup = 'file' end -local function isname () feature_list.lookup = 'name' end -local function thename(s) feature_list.name = s end -local function issub (v) feature_list.sub = v end -local function istrue (s) feature_list[s] = true end -local function isfalse(s) feature_list[s] = false end -local function iskey (k,v) feature_list[k] = v end +-- --[==[obsolete-- +local function issome () old_feature_list.lookup = 'name' end +local function isfile () old_feature_list.lookup = 'file' end +local function isname () old_feature_list.lookup = 'name' end +local function thename(s) old_feature_list.name = s end +local function issub (v) old_feature_list.sub = v end +local function istrue (s) old_feature_list[s] = true end +local function isfalse(s) old_feature_list[s] = false end +local function iskey (k,v) old_feature_list[k] = v end local P, S, R, C = lpeg.P, lpeg.S, lpeg.R, lpeg.C @@ -180,7 +187,8 @@ local spaces = P(" ")^0 local namespec = (1-S("/:("))^1 local filespec = (R("az", "AZ") * P(":"))^-1 * (1-S(":("))^1 local stylespec = spaces * P("/") * (((1-P(":"))^0)/isstyle) * spaces -local filename = (P("file:")/isfile * (filespec/thename)) + (P("[") * P(true)/isname * (((1-P("]"))^0)/thename) * P("]")) +local filename = (P("file:")/isfile * (filespec/thename)) + + (P("[") * P(true)/isname * (((1-P("]"))^0)/thename) * P("]")) local fontname = (P("name:")/isname * (namespec/thename)) + P(true)/issome * (namespec/thename) local sometext = (R("az","AZ","09") + S("+-.,"))^1 local truevalue = P("+") * spaces * (sometext/istrue) @@ -190,67 +198,318 @@ local somevalue = sometext/istrue local subvalue = P("(") * (C(P(1-S("()"))^1)/issub) * P(")") -- for Kim local option = spaces * (keyvalue + falsevalue + truevalue + somevalue) * spaces local options = P(":") * spaces * (P(";")^0 * option)^0 -local pattern = (filename + fontname) * subvalue^0 * stylespec^0 * options^0 - -local function colonized(specification) -- xetex mode - feature_list = { } - lpeg.match(pattern,specification.specification) - feature_list = set_default_features(feature_list) - if feature_list.style then - specification.style = feature_list.style - feature_list.style = nil +local oldsyntax = (filename + fontname) * subvalue^0 * stylespec^0 * options^0 + +--- to be annihilated +local function old_behavior (specification) -- xetex mode + old_feature_list = { } + lpeg.match(oldsyntax,specification.specification) + old_feature_list = set_default_features(old_feature_list) + if old_feature_list.style then + specification.style = old_feature_list.style + old_feature_list.style = nil end - if feature_list.optsize then - specification.optsize = feature_list.optsize - feature_list.optsize = nil + if old_feature_list.optsize then + specification.optsize = old_feature_list.optsize + old_feature_list.optsize = nil end - if feature_list.name then - if resolvers.findfile(feature_list.name, "tfm") then - feature_list.lookup = "file" - feature_list.name = file.addsuffix(feature_list.name, "tfm") - elseif resolvers.findfile(feature_list.name, "ofm") then - feature_list.lookup = "file" - feature_list.name = file.addsuffix(feature_list.name, "ofm") + if old_feature_list.name then + if resolvers.findfile(old_feature_list.name, "tfm") then + old_feature_list.lookup = "file" + old_feature_list.name = file.addsuffix(old_feature_list.name, "tfm") + elseif resolvers.findfile(old_feature_list.name, "ofm") then + old_feature_list.lookup = "file" + old_feature_list.name = file.addsuffix(old_feature_list.name, "ofm") end - specification.name = feature_list.name - feature_list.name = nil + specification.name = old_feature_list.name + old_feature_list.name = nil end - if feature_list.lookup then - specification.lookup = feature_list.lookup - feature_list.lookup = nil + --- this test overwrites valid file: requests for xetex bracket + --- syntax + if old_feature_list.lookup then + specification.lookup = old_feature_list.lookup + old_feature_list.lookup = nil end - if feature_list.sub then - specification.sub = feature_list.sub - feature_list.sub = nil + if old_feature_list.sub then + specification.sub = old_feature_list.sub + old_feature_list.sub = nil end - if not feature_list.mode then + if not old_feature_list.mode then -- if no mode is set, use our default - feature_list.mode = fonts.mode + old_feature_list.mode = fonts.mode + end + specification.features.normal = fonts.handlers.otf.features.normalize(old_feature_list) + return specification +end + +--fonts.definers.registersplit(":",old_behavior,"cryptic") +--fonts.definers.registersplit("", old_behavior,"more cryptic") -- catches \font\text=[names] +--obsolete]==]-- + +----------------------------------------------------------------------- +--- request syntax parser 2.2 +----------------------------------------------------------------------- +--- the luaotfload font request syntax (see manual) +--- has a canonical form: +--- +--- \font<csname>=<prefix>:<identifier>:<features> +--- +--- where +--- <csname> is the control sequence that activates the font +--- <prefix> is either “file” or “name”, determining the lookup +--- <identifer> is either a file name (no path) or a font +--- name, depending on the lookup +--- <features> is a list of switches or options, separated by +--- semicolons or commas; a switch is of the form “+” foo +--- or “-” foo, options are of the form lhs “=” rhs +--- +--- however, to ensure backward compatibility we also have +--- support for Xetex-style requests. +--- +--- for the Xetex emulation see: +--- · The XeTeX Reference Guide by Will Robertson, 2011 +--- · The XeTeX Companion by Michel Goosens, 2010 +--- · About XeTeX by Jonathan Kew, 2005 +--- +--- +--- caueat emptor. +--- the request is parsed into one of **four** different +--- lookup categories: the regular ones, file and name, +--- as well as the Xetex compatibility ones, path and anon. +--- (maybe a better choice of identifier would be “ambig”.) +--- +--- according to my reconstruction, the correct chaining +--- of the lookups for each category is as follows: +--- +--- | File -> ( db/filename lookup; +--- db/basename lookup; +--- kpse.find_file() ) +--- | Name -> ( names.resolve() ) +--- | Path -> ( db/filename lookup; +--- db/basename lookup; +--- kpse.find_file(); +--- fullpath lookup ) +--- | Anon -> ( names.resolve(); (* most general *) +--- db/filename lookup; +--- db/basename lookup; +--- kpse.find_file(); +--- fullpath lookup ) +--- +--- the database should be generated only if the chain has +--- been completed, and then only once. +--- +--- caching of successful lookups is essential. we need +--- an additional subtable "cached" in the database. it +--- should be nil’able by issuing luaotfload-tool --flush or +--- something. if a cache miss is followed by a successful +--- lookup, then it will be counted as new addition to the +--- cache. we also need a config option to ignore caching. +--- +--- also everything has to be finished by tomorrow at noon. +--- +----------------------------------------------------------------------- + + +local stringlower = string.lower + +local toboolean = function (s) + if s == "true" then return true end + if s == "false" then return false end +--if s == "yes" then return true end --- Context style +--if s == "no" then return false end + return s +end + +local lpegmatch = lpeg.match +local P, S, R = lpeg.P, lpeg.S, lpeg.R +local C, Cc, Cf, Cg, Cs, Ct + = lpeg.C, lpeg.Cc, lpeg.Cf, lpeg.Cg, lpeg.Cs, lpeg.Ct + +--- terminals and low-level classes ----------------------------------- +--- note we could use the predefined ones from lpeg.patterns +local dot = P"." +local colon = P":" +local featuresep = S",;" +local slash = P"/" +local equals = P"=" +local lbrk, rbrk = P"[", P"]" + +local spacing = S" \t\v" +local ws = spacing^0 + +local digit = R"09" +local alpha = R("az", "AZ") +local anum = alpha + digit +local decimal = digit^1 * (dot * digit^0)^-1 + +--- modifiers --------------------------------------------------------- +--[[doc-- + The slash notation: called “modifiers” (Kew) or “font options” + (Robertson, Goosens) + we only support the shorthands for italic / bold / bold italic + shapes, the rest is ignored. +--doc]]-- +local style_modifier = (P"BI" + P"IB" + P"bi" + P"ib" + S"biBI") + / stringlower +local other_modifier = P"S=" * decimal --- optical size; unsupported + + P"AAT" + P"aat" --- apple stuff; unsupported + + P"ICU" + P"icu" --- not applicable + + P"GR" + P"gr" --- sil stuff; unsupported +local garbage_modifier = ((1 - colon - slash)^0 * Cc(false)) +local modifier = slash * (other_modifier --> ignore + + Cs(style_modifier) --> collect + + garbage_modifier) --> warn +local modifier_list = Cg(Ct(modifier^0), "modifiers") + +--- lookups ----------------------------------------------------------- +local fontname = C((1-S"/:(")^1) --- like luatex-fonts +local prefixed = P"name:" * ws * Cg(fontname, "name") + + P"file:" * ws * Cg(fontname, "file") +local unprefixed = Cg(fontname, "anon") +local path_lookup = lbrk * Cg(C((1-rbrk)^1), "path") * rbrk + +--- features ---------------------------------------------------------- +local field = (anum + S"+-.")^1 --- sic! +--- assignments are “lhs=rhs” +--- switches are “+key” | “-key” +local assignment = C(field) * ws * equals * ws * (field / toboolean) +local switch = P"+" * ws * C(field) * Cc(true) + + P"-" * ws * C(field) * Cc(false) + + C(field) * Cc(true) -- catch crap +local feature_expr = ws * Cg(assignment + switch) * ws +local feature_list = Cf(Ct"" + * feature_expr + * (featuresep * feature_expr)^0 + , rawset) + * featuresep^-1 + +--- other ------------------------------------------------------------- +--- This rule is present in the original parser. It sets the “sub” +--- field of the specification which allows addressing a specific +--- font inside a TTC container. Neither in Luatex-Fonts nor in +--- Luaotfload is this documented, so we might as well silently drop +--- it. However, as backward compatibility is one of our prime goals we +--- just insert it here and leave it undocumented until someone cares +--- to ask. (Note: afair subfonts are numbered, but this rule matches a +--- string; I won’t mess with it though until someone reports a +--- problem.) +--- local subvalue = P("(") * (C(P(1-S("()"))^1)/issub) * P(")") -- for Kim +--- Who’s Kim? +--- Note to self: subfonts apparently start at index 0. Tested with +--- Cambria.ttc that includes “Cambria Math” at 0 and “Cambria” at 1. +--- Other values cause luatex to segfault. +local subfont = P"(" * Cg((1 - S"()")^1, "sub") * P")" +--- top-level rules --------------------------------------------------- +--- \font\foo=<specification>:<features> +local features = Cg(feature_list, "features") +local specification = (prefixed + unprefixed) + * subfont^-1 + * modifier_list^-1 +local font_request = Ct(path_lookup * (colon^-1 * features)^-1 + + specification * (colon * features)^-1) + +-- lpeg.print(font_request) +--- new parser: 632 rules +--- old parser: 230 rules + +local import_values = { + --- That’s what the 1.x parser did, not quite as graciously, + --- with an array of branch expressions. + -- "style", "optsize",--> from slashed notation; handled otherwise + "lookup", "sub" --[[‽]], "mode", +} + +local lookup_types = { "anon", "file", "name", "path" } + +local select_lookup = function (request) + for i=1, #lookup_types do + local lookup = lookup_types[i] + local value = request[lookup] + if value then + return lookup, value + end + end +end + +local supported = { + b = "bold", + i = "italic", + bi = "bolditalic", + aat = false, + icu = false, + gr = false, +} + +local handle_slashed = function (modifiers) + local style, optsize + for i=1, #modifiers do + local mod = modifiers[i] + if supported[mod] then + style = supported[mod] + --elseif stringfind(v, "^s=") then + elseif stringsub(v, 1, 2) == "s=" then + local val = stringsub(v, 3) + optsize = val + elseif stylename == false then + report("log", 0, + "load", "unsupported font option: %s", v) + elseif not stringis_empty(v) then + style = stringgsub(v, "[^%a%d]", "") + end end - specification.features.normal = fonts.handlers.otf.features.normalize(feature_list) + return style, optsize +end + +--- spec -> spec +local handle_request = function (specification) + local request = lpegmatch(font_request, + specification.specification) + local lookup, name = select_lookup(request) + request.features = set_default_features(request.features) + + if name then + specification.name = name + specification.lookup = lookup or specification.lookup + end + + if request.modifiers then + local style, optsize = handle_slashed(request.modifiers) + specification.style, specification.optsize = style, optsize + end + + for n=1, #import_values do + local feat = import_values[n] + local newvalue = request.features[feat] + if newvalue then + specification[feat] = request.features[feat] + request.features[feat] = nil + end + end + + --- The next line sets the “rand” feature to “random”; I haven’t + --- investigated it any further (luatex-fonts-ext), so it will + --- just stay here. + specification.features.normal + = fonts.handlers.otf.features.normalize(request.features) return specification end -fonts.definers.registersplit(":",colonized,"cryptic") -fonts.definers.registersplit("", colonized,"more cryptic") -- catches \font\text=[names] - ---- TODO below section is literally the same in luatex-fonts-def ---- why is it here? ---function fonts.definers.applypostprocessors(tfmdata) --- local postprocessors = tfmdata.postprocessors --- if postprocessors then --- for i=1,#postprocessors do --- local extrahash = postprocessors[i](tfmdata) -- after scaling etc --- if type(extrahash) == "string" and extrahash ~= "" then --- -- e.g. a reencoding needs this --- extrahash = string.gsub(lower(extrahash),"[^a-z]","-") --- tfmdata.properties.fullname = format("%s-%s",tfmdata.properties.fullname,extrahash) --- end --- end --- end --- return tfmdata ---end +local compare_requests = function (spec) + local old = old_behavior(spec) + local new = handle_request(spec) + return new +end + +--fonts.definers.registersplit(":", compare_requests, "cryptic") +--fonts.definers.registersplit("", compare_requests, "more cryptic") -- catches \font\text=[names] + +fonts.definers.registersplit(":", handle_request, "cryptic") +fonts.definers.registersplit("", handle_request, "more cryptic") -- catches \font\text=[names] + +--fonts.definers.registersplit(":",old_behavior,"cryptic") +--fonts.definers.registersplit("", old_behavior,"more cryptic") -- catches \font\text=[names] + ---[[ end included font-ltx.lua ]] --[[doc-- diff --git a/luaotfload-merged.lua b/luaotfload-merged.lua index 0cf2ce4..bf22bac 100644 --- a/luaotfload-merged.lua +++ b/luaotfload-merged.lua @@ -1,6 +1,6 @@ -- merged file : luatex-fonts-merged.lua -- parent file : luatex-fonts.lua --- merge date : 04/26/13 15:13:48 +-- merge date : 04/29/13 20:30:03 do -- begin closure to overcome local limits and interference @@ -3039,7 +3039,6 @@ function caches.getreadablepaths(category,subcategory) end local function makefullname(path,name) if path and path~="" then - name="temp-"..name return file.addsuffix(file.join(path,name),"lua"),file.addsuffix(file.join(path,name),usingjit and "lub" or "luc") end end @@ -3093,7 +3092,7 @@ function caches.compile(data,luaname,lucname) d=table.serialize(data,true) end if d and d~="" then - local f=io.open(lucname,'w') + local f=io.open(lucname,'wb') if f then local s=loadstring(d) if s then @@ -3218,7 +3217,7 @@ function containers.content(container,name) return container.storage[name] end function containers.cleanname(name) - return (gsub(lower(name),"[^%w%d]+","-")) + return (gsub(lower(name),"[^%w\128-\255]+","-")) end end -- closure @@ -4829,22 +4828,35 @@ end local fonts=fonts fonts.names=fonts.names or {} fonts.names.version=1.001 -fonts.names.basename="luatex-fonts-names.lua" +fonts.names.basename="luatex-fonts-names" fonts.names.new_to_old={} fonts.names.old_to_new={} +fonts.names.cache=containers.define("fonts","data",fonts.names.version,true) local data,loaded=nil,false local fileformats={ "lua","tex","other text files" } +function fonts.names.reportmissingbase() + texio.write("<missing font database, run: mtxrun --script fonts --reload --simple>") + fonts.names.reportmissingbase=nil +end +function fonts.names.reportmissingname() + texio.write("<unknown font in database, run: mtxrun --script fonts --reload --simple>") + fonts.names.reportmissingname=nil +end function fonts.names.resolve(name,sub) if not loaded then local basename=fonts.names.basename if basename and basename~="" then - for i=1,#fileformats do - local format=fileformats[i] - local foundname=resolvers.findfile(basename,format) or "" - if foundname~="" then - data=dofile(foundname) - texio.write("<font database loaded: ",foundname,">") - break + data=containers.read(fonts.names.cache,basename) + if not data then + basename=file.addsuffix(basename,"lua") + for i=1,#fileformats do + local format=fileformats[i] + local foundname=resolvers.findfile(basename,format) or "" + if foundname~="" then + data=dofile(foundname) + texio.write("<font database loaded: ",foundname,">") + break + end end end end @@ -4860,9 +4872,12 @@ function fonts.names.resolve(name,sub) else return filename,false end - else + elseif fonts.names.reportmissingname then + fonts.names.reportmissingname() return name,false end + elseif fonts.names.reportmissingbase then + fonts.names.reportmissingbase() end end fonts.names.resolvespec=fonts.names.resolve @@ -7713,7 +7728,7 @@ function injections.handler(head,where,keep) if rlmode and rlmode>=0 then n.xoffset=p.xoffset-p.width+d[1] else - n.xoffset=p.xoffset-d[1]-x + n.xoffset=p.xoffset-d[1]-x end end else @@ -10054,6 +10069,8 @@ for s=1,#datasets do if ok then success=true break + elseif not start then + break end end else diff --git a/luaotfload-override.lua b/luaotfload-override.lua index 7ee9730..2b9ef8b 100644 --- a/luaotfload-override.lua +++ b/luaotfload-override.lua @@ -81,10 +81,12 @@ logs.names_report = function (mode, lvl, ...) end end ---- This will vanish as soon as Hans fixes it in data-con ---- https://github.com/lualatex/luaotfload/issues/17 +if containers then + --- This will vanish as soon as Hans fixes it in data-con + --- https://github.com/lualatex/luaotfload/issues/17 -containers.cleanname = function (name) - return (string.gsub(string.lower(name),"[^%w%d\128\255]+","-")) + containers.cleanname = function (name) + return (string.gsub(string.lower(name),"[^%w%d\128\255]+","-")) + end end -- vim:tw=71:sw=4:ts=4:expandtab diff --git a/fontdbutil.lua b/luaotfload-tool.lua index 1aa99d0..bb935cf 100755 --- a/fontdbutil.lua +++ b/luaotfload-tool.lua @@ -28,14 +28,14 @@ string.quoted = string.quoted or function (str) return string.format("%q",str) end -dofile(loader_path) +require(loader_path) --[[doc-- Depending on how the script is called we change its behavior. For backwards compatibility, moving or symlinking the script to a file name starting with \fileent{mkluatexfontdb} will cause it to trigger a database update on every run. -Running as \fileent{fontdbutil} -- the new name -- will do this upon +Running as \fileent{luaotfload-tool} -- the new name -- will do this upon request only. There are two naming conventions followed here: firstly that of @@ -61,10 +61,9 @@ do -- we don’t have file.basename and the likes yet, so inline parser ftw local extension = dot * (1 - slash - dot)^1 local p_basename = path^-1 * C(thename) * extension^-1 * P(-1) - -- if stringfind(stringlower(arg[0]), "^fontdbutil") then local self = lpegmatch(p_basename, stringlower(arg[0])) - if self == "fontdbutil" then - config.luaotfload.self = "fontdbutil" + if self == "luaotfload-tool" then + config.luaotfload.self = "luaotfload-tool" else config.luaotfload.self = "mkluatexfontdb" end @@ -101,7 +100,7 @@ local db_src_out = names.path.dir.."/"..names.path.basename local db_bin_out = file.replacesuffix(db_src_out, "luc") local help_messages = { - fontdbutil = [[ + ["luaotfload-tool"] = [[ Usage: %s [OPTION]... @@ -119,7 +118,7 @@ This tool is part of the luaotfload package. Valid options are: -V --version print version and exit -h --help print this message - --alias=<name> force behavior of “fontdbutil” or legacy + --alias=<name> force behavior of “luaotfload-tool” or legacy “mkluatexfontdb” ------------------------------------------------------------------------------- DATABASE @@ -155,7 +154,7 @@ Valid options: -vvv print all steps of directory searching -V --version print version and exit -h --help print this message - --alias=<name> force behavior of “fontdbutil” or legacy + --alias=<name> force behavior of “luaotfload-tool” or legacy “mkluatexfontdb” The font database will be saved to @@ -167,7 +166,7 @@ The font database will be saved to local help_msg = function ( ) local template = help_messages[config.luaotfload.self] - or help.messages.fontdbutil + or help_messages["luaotfload-tool"] texiowrite_nl(stringformat(template, config.luaotfload.self, db_src_out, db_bin_out)) end @@ -276,14 +275,20 @@ actions.query = function (job) optsize = 0, } - local foundname, _whatever, success = + local foundname, subfont, success = fonts.names.resolve(nil, nil, tmpspec) if success then logs.names_report(false, 1, "resolve", "Font “%s” found!", query) - logs.names_report(false, 1, - "resolve", "Resolved file name “%s”", foundname) + if subfont then + logs.names_report(false, 1, "resolve", + "Resolved file name “%s”, subfont nr. “%s”", + foundname, subfont) + else + logs.names_report(false, 1, + "resolve", "Resolved file name “%s”", foundname) + end if job.show_info then show_font_info(foundname) end @@ -388,9 +393,7 @@ local process_cmdline = function ( ) -- unit -> jobspec if config.luaotfload.self == "mkluatexfontdb" then action_pending["generate"] = true - print(result.log_level) result.log_level = math.max(2, result.log_level) - print(result.log_level) end return result end diff --git a/luaotfload.dtx b/luaotfload.dtx index 18e01d8..5b454bc 100644 --- a/luaotfload.dtx +++ b/luaotfload.dtx @@ -130,6 +130,8 @@ and the derived files \setsansfont[Ligatures=TeX,Scale=MatchLowercase]{Iwona Medium} %setmathfont{XITS Math} +\usepackage{hologo} + \newcommand\TEX {\TeX\xspace} \newcommand\LUA {Lua\xspace} \newcommand\PDFTEX {pdf\TeX\xspace} @@ -160,6 +162,8 @@ and the derived files \renewcommand\partname{Part}%% gets rid of the stupid “file” heading +\usepackage{syntax}%% bnf for font request syntax + \VerbatimFootnotes \begin{document} \DocInput{luaotfload.dtx}% @@ -244,56 +248,177 @@ and the derived files % % \section{Loading Fonts} % -% \identifier{luaotfload} supports an extended font loading syntax: +% \identifier{luaotfload} supports an extended font request syntax: % -% \begin{center} +% \begin{quote} % |\font\foo={|% % \meta{prefix}|:|% % \meta{font name}|:|% % \meta{font features}|}|% % \meta{\TEX font features} -% \end{center} +% \end{quote} % % \noindent % The curly brackets are optional and escape the spaces in the enclosed -% font name (alternatively, double quotes serve the same purpose). -% The individual parts of the syntax are: -% -% \paragraph{Prefix} -% -% The \meta{prefix} is either |file:| or |name:|. -% It determines whether the font loader should interpret the request as a -% file name or font name, respectively, which again influences how it -% will attempt to locate the font. -% The prefix can be omitted, in which case |name:| is assumed. -% -%% \iffalse%% how am i supposed to friggin comment stuff in a dtx??? -%% TODO -%% it would appear that the next paragraph is incorrect; I get -%% name: lookups regardless unless the font file is actually -%% in CWD -%% \fi -%% For compatibility with \XETEX, surrounding the \meta{font name} with -%% square brackets is synonymous to using the |file:| prefix. +% font name. +% Alternatively, double quotes serve the same purpose. +% A selection of individual parts of the syntax are discussed below; +% for a more formal description see figure \ref{font-syntax}. % +% \begin{figure}[b] +% \setlength\grammarparsep{12pt plus 2pt minus 2pt} +% \setlength\grammarindent{5cm} +% \begingroup +% \small +% \begin{grammar} +% <definition> ::= `\\font', {\sc csname}, `=', <font request>, [ <size> ] ; +% +% <size> ::= `at', {\sc dimension} ; +% +% <font request> ::= `"', <unquoted font request> `"' +% \alt `{', <unquoted font request> `}' +% \alt <unquoted font request> ; +% +% <unquoted font request> ::= <specification>, [`:', <feature list> ] +% \alt `[', <path lookup> `]', [ [`:'], <feature list> ] ; +% +% <specification> ::= <prefixed spec>, [ <subfont no> ], \{ <modifier> \} +% \alt <anon lookup>, \{ <modifier> \} ; +% +% <prefixed spec> ::= `file:', <file lookup> +% \alt `name:', <name lookup> ; +% +% <file lookup> ::= \{ <name character> \} ; +% +% <name lookup> ::= \{ <name character> \} ; +% +% <anon lookup> ::= {\sc tfmname} | <name lookup> ; +% +% <path lookup> ::= \{ {\sc all_characters} - `]' \} ; +% +% <modifier> ::= `/', (`i' | `b' | `bi' | `ib') ; +% +% <subfont no> ::= `(', \{ {\sc digit} \}, `)' ; +% +% <feature list> ::= <feature expr>, \{ `;', <feature expr> \} ; +% +% <feature expr> ::= {\sc feature_id}, `=', {\sc feature_value} +% \alt <feature switch>, {\sc feature_id} ; +% +% <feature switch> ::= `+' | `-' ; +% +% <name character> ::= {\sc all_characters} - ( `(' | `/' | `:' ) ; +% \end{grammar} +% \endgroup +% \caption{Font request syntax. +% Braces or double quotes around the +% \emphasis{specification} rule will +% preserve whitespace in file names. +% In addition to the font style modifiers +% (\emphasis{slash-notation}) given above, there +% are others that are recognized but will be silently +% ignored: {\ttfamily aat}, +% {\ttfamily icu}, and +% {\ttfamily gr}. +% The special terminals are: +% {\sc feature\textunderscore id} for a valid font +% feature name and +% {\sc feature\textunderscore value} for the corresponding +% value. +% {\sc tfmname} is the name of a \abbrev{tfm} file. +% {\sc digit} again refers to bytes 48--57, and +% {\sc all\textunderscore characters} to all byte values. +% {\sc csname} and {\sc dimension} are the \TEX concepts.} +% \label{font-syntax} +% \end{figure} +% +% \subsection{Prefix -- the \identifier{luaotfload}{ }Way} +% +% In \identifier{luaotfload}, the canonical syntax for font requests +% requires a \emphasis{prefix}: +% \begin{quote} +% |\font\fontname=|\meta{prefix}|:|\meta{fontname}\dots +% \end{quote} +% where \meta{prefix} is either \verb|file:| or \verb|name:|. +% It determines whether the font loader should interpret the request as +% a \emphasis{file name} or +% \emphasis{font name}, respectively, +% which again influences how it will attempt to locate the font. +% Examples for font names are +% “Latin Modern Italic”, +% “GFS Bodoni Rg”, and +% “PT Serif Caption” +% -- they are the human readable identifiers +% usually listed in drop-down menus and the like. % In order for fonts installed both in system locations and in your % \fileent{texmf} to be accessible by font name, \identifier{luaotfload} must % first collect the metadata included in the files. -% Please refer to section ~\ref{sec:fontdb} below for instructions on how to +% Please refer to section~\ref{sec:fontdb} below for instructions on how to % create the database. % -% \paragraph{Font name} +% File names are whatever your file system allows them to be, except +% that that they may not contain the characters +% \verb|(|, +% \verb|:|, and +% \verb|/|. +% As obvious from the last exception, the \verb|file:| lookup will +% not process paths to the font location -- only those +% files found when generating the database are addressable this way. +% Continue below in the \XETEX section if you need to load your fonts +% by path. +% The file names corresponding to the example font names above are +% \fileent{lmroman12-italic.otf}, +% \fileent{GFSBodoni.otf}, and +% \fileent{PTZ56F.ttf}. +% +% \subsection{\hologo{XeTeX} Compatibility Layer} +% +% In addition to the regular prefixed requests, \identifier{luaotfload} +% accepts loading fonts the \XETEX way. +% There are again two modes: bracketed and unbracketed. +% A bracketed request looks as follows. % -% The \meta{font name} can be either a font filename or actual font -% name based on the \meta{prefix} as mentioned above. +% \begin{quote} +% |\font\fontname=[|\meta{path to file}|]| +% \end{quote} +% +% \noindent +% Inside the square brackets, every character except for a closing +% bracket is permitted, allowing for specifying paths to a font file. +% Naturally, path-less file names are equally valid and processed the +% same way as an ordinary \verb|file:| lookup. % -% A filename request may optionally include the absolute path to the font file, -% allowing for fonts outside the standard locations to be loaded as well. -% If no path is specified, then \identifier{kpathsea} is used to locate the -% font (which will typically be in the \fileent{texmf} tree or the -% current directory). +% \begin{quote} +% |\font\fontname=|\meta{font name} \dots +% \end{quote} % -% \subparagraph{Examples for loading by file name} +% Unbracketed (or, for lack of a better word: \emphasis{anonymous}) +% font requests resemble the conventional \TEX syntax. +% However, they have a broader spectrum of possible interpretations: +% before anything else, \identifier{luaotfload} attempts to load a +% traditional \TEX Font Metric (\abbrev{tfm} or \abbrev{ofm}). +% If this fails, it performs a \verb|name:| lookup, which itself will +% fall back to a \verb|file:| lookup if no database entry matches +% \meta{font name}. +% +% Furthermore, \identifier{luaotfload} supports the slashed (shorthand) +% font style notation from \XETEX. +% +% \begin{quote} +% |\font\fontname=|\meta{font name}|/|\meta{modifier}\dots +% \end{quote} +% +% \noindent +% Currently, four style modifiers are supported: +% \verb|i| for italic shape, +% \verb|b| for bold weight, +% \verb|bi| or \verb|ib| for the combination of both. +% Other “slashed” modifiers are too specific to the \XETEX engine and +% have no meaning in \LUATEX. +% +% \subsection{Examples} +% +% \subsubsection{Loading by File Name} % % For example, conventional \abbrev{type1} font can be loaded with a \verb|file:| % request like so: @@ -305,8 +430,11 @@ and the derived files % \end{quote} % % The \OpenType version of Janusz Nowacki’s font \emphasis{Antykwa -% Półtawskiego} (in \TEX Live) in its condensed variant can be loaded as -% follows: +% Półtawskiego}\footnote{% +% \url{http://jmn.pl/antykwa-poltawskiego/}, also available in +% in \TEX Live. +% } +% in its condensed variant can be loaded as follows: % % \begin{quote} % \begin{verbatim} @@ -324,7 +452,7 @@ and the derived files % \end{verbatim} % \end{quote} % -% \subparagraph{Examples for loading by font name} +% \subsubsection{Loading by Font Name} % % The \verb|name:| lookup does not depend on cryptic filenames: % @@ -360,9 +488,62 @@ and the derived files % \end{verbatim} % \end{quote} % -% \paragraph{Font features} +% \subsubsection{Modifiers} +% +% If the entire \emphasis{Iwona} family\footnote{% +% \url{http://jmn.pl/kurier-i-iwona/}, +% also in \TEX Live. +% } +% is installed in some location accessible by \identifier{luaotfload}, +% the regular shape can be loaded as follows: +% +% \begin{quote} +% \begin{verbatim} +% \font\iwona=Iwona at 20pt +% \end{verbatim} +% \end{quote} +% +% \noindent +% To load the most common of the other styles, the slash notation can +% be employed as shorthand: % -% \meta{font features} is semicolon-separated list of feature +% \begin{quote} +% \begin{verbatim} +% \font\iwonaitalic =Iwona/i at 20pt +% \font\iwonabold =Iwona/b at 20pt +% \font\iwonabolditalic=Iwona/bi at 20pt +% \end{verbatim} +% \end{quote} +% +% \noindent +% which is equivalent to these full names: +% +% \begin{quote} +% \begin{verbatim} +% \font\iwonaitalic ="Iwona Italic" at 20pt +% \font\iwonabold ="Iwona Bold" at 20pt +% \font\iwonabolditalic="Iwona BoldItalic" at 20pt +% \end{verbatim} +% \end{quote} +% +% \section{Font features} +% +% \emphasis{Font features} are the second to last component in the +% general scheme for font requests: +% +% \begin{quote} +% |\font\foo={|% +% \meta{prefix}|:|% +% \meta{font name}|:|% +% \meta{font features}|}|% +% \meta{\TEX font features} +% \end{quote} +% +% \noindent +% If style modifiers are present (\XETEX style), they must precede +% \meta{font features}. +% +% The element \meta{font features} is a semicolon-separated list of feature % tags\footnote{% % Cf. \url{http://www.microsoft.com/typography/otspec/featurelist.htm}. % } @@ -587,8 +768,8 @@ and the derived files % This is particularly noticeable if it occurs during a typesetting run. % In any case, subsequent updates to the database will be quite fast. % -% \subsection[fontdbutil / mkluatexfontdb.lua]% -% {\fileent{fontdbutil} / +% \subsection[luaotfload-tool / mkluatexfontdb.lua]% +% {\fileent{luaotfload-tool} / % \fileent{mkluatexfontdb.lua}\footnote{% % The script may be named just \fileent{mkluatexfontdb} in your % distribution. @@ -597,7 +778,7 @@ and the derived files % It can still be desirable at times to do some of these steps % manually, and without having to compile a document. % To this end, \identifier{luaotfload} comes with the utility -% \fileent{fontdbutil} that offers an interface to the database +% \fileent{luaotfload-tool} that offers an interface to the database % functionality. % Being a \LUA script, there are two ways to run it: % either make it executable (\verb|chmod +x| on unixoid systems) or @@ -612,15 +793,15 @@ and the derived files % \emphasis{Note}: % On \abbrev{MS} \identifier{Windows} systems, the script can be run % either by calling the wrapper application -% \fileent{fontdbutil.exe} or as -% \verb|texlua.exe fontdbutil|. +% \fileent{luaotfload-tool.exe} or as +% \verb|texlua.exe luaotfload-tool.lua|. % } % Invoked with the argument \verb|--update| it will perform a database % update, scanning for fonts not indexed. % % \begin{quote} % \begin{verbatim} -% fontdbutil --update +% luaotfload-tool --update % \end{verbatim} % \end{quote} % @@ -629,11 +810,11 @@ and the derived files % % \begin{quote} % \begin{verbatim} -% fontdbutil --update --force +% luaotfload-tool --update --force % \end{verbatim} % \end{quote} % -% For sake of backwards compatibility, \fileent{fontdbutil} may be +% For sake of backwards compatibility, \fileent{luaotfload-tool} may be % renamed or symlinked to \fileent{mkluatexfontdb}. % Whenever it is run under this name, it will update the database % first, mimicking the behavior of earlier versions of @@ -681,7 +862,7 @@ and the derived files % % \subsection{Querying from Outside} % -% \fileent{fontdbutil} also provides rudimentary means of +% \fileent{luaotfload-tool} also provides rudimentary means of % accessing the information collected in the font database. % If the option \verb|--find=|\emphasis{name} is given, the script will % try and search the fonts indexed by \identifier{luaotfload} for a @@ -690,7 +871,7 @@ and the derived files % % \begin{quote} % \begin{verbatim} -% fontdbutil --find="Iwona Regular" +% luaotfload-tool --find="Iwona Regular" % \end{verbatim} % \end{quote} % @@ -707,7 +888,7 @@ and the derived files % % \begin{quote} % \begin{verbatim} -% fontdbutil -F --find="Iwona Bright" +% luaotfload-tool -F --find="Iwona Bright" % \end{verbatim} % \end{quote} % @@ -718,7 +899,7 @@ and the derived files % using the \verb|-i| option (\verb|--info|). % \begin{quote} % \begin{verbatim} -% fontdbutil -F --find="Iwona Light Italic" +% luaotfload-tool -F --find="Iwona Light Italic" % \end{verbatim} % \end{quote} % \noindent @@ -727,7 +908,7 @@ and the derived files % In \TEX Live: \fileent{texmf-dist/doc/luatex/base/luatexref-t.pdf}. % } % -% \verb|fontdbutil --help| will list the available command line +% \verb|luaotfload-tool --help| will list the available command line % switches, including some not discussed in detail here. % % \subsection{Blacklisting Fonts} @@ -736,7 +917,7 @@ and the derived files % Some fonts are problematic in general, or just in \LUATEX. % If you find that compiling your document takes far too long or eats % away all your system’s memory, you can track down the culprit by -% running \verb|fontdbutil -v| to increase verbosity. +% running \verb|luaotfload-tool -v| to increase verbosity. % Take a note of the \emphasis{filename} of the font that database % creation fails with and append it to the file % \fileent{luaotfload-blacklist.cnf}. @@ -945,7 +1126,7 @@ and the derived files % verbosity levels and redirecting log output to \fileent{stdout}: % % \begin{verbatim} -% fontdbutil -fuvvv --log=stdout +% luaotfload-tool -fuvvv --log=stdout % \end{verbatim} % % If this fails, the font last printed to the terminal is likely to be @@ -998,12 +1179,13 @@ local luaotfload = luaotfload config = config or { } config.luaotfload = config.luaotfload or { } -luaotfload.prefer_merge = config.luaotfload.prefer_merge or true +config.luaotfload.resolver = config.luaotfload.resolver or "normal" +--luaotfload.prefer_merge = config.luaotfload.prefer_merge or true luaotfload.module = { name = "luaotfload", version = 2.2, - date = "2013/04/15", + date = "2013/04/29", description = "OpenType layout system.", author = "Elie Roux & Hans Hagen", copyright = "Elie Roux", @@ -1014,11 +1196,12 @@ local luatexbase = luatexbase local type, next = type, next local setmetatable = setmetatable +local find_file = kpse.find_file +local lfsisfile = lfs.isfile local stringfind = string.find -local stringsub = string.sub -local stringmatch = string.match local stringformat = string.format -local find_file = kpse.find_file +local stringmatch = string.match +local stringsub = string.sub local add_to_callback, create_callback = luatexbase.add_to_callback, luatexbase.create_callback @@ -1328,7 +1511,7 @@ add_to_callback("find_vf_file", loadmodule"lib-dir.lua" --- required by luaofload-database.lua loadmodule"override.lua" --- “luat-ovr” -logs.set_loglevel(0) +logs.set_loglevel(config.luaotfload.loglevel or 2) % \end{macrocode} % \CONTEXT does not support ofm, these lines were added in order to make it @@ -1350,23 +1533,101 @@ loadmodule"database.lua" --- “font-nms” loadmodule"colors.lua" --- “font-clr” % \end{macrocode} -% This hack makes fonts called with file method found by fonts.names.resolve -% instead of just trying to find them with \identifier{kpse}. -% It is necessary in cases when font files are not reachable by -% \identifier{kpse} but present in the database, a quite common case -% under Linux. -% -% As of 2013-04-24 we have a workaround in the resolver that handles -% \verb|file:| lookups diverted this way. -% It requires some overhead due to additional extra data saved in the -% names database, and might vanish entirely once the font request syntax -% is understood. -% Until then it is considered a kludge, like the hack below. +% Relying on the \verb|name:| resolver for everything has been the source +% of permanent trouble with the database. +% With the introduction of the new syntax parser we now have enough +% granularity to distinguish between the \XETEX emulation layer and the +% genuine \verb|name:| and \verb|file:| lookups of \LUATEX-Fonts. +% Another benefit is that we can now easily plug in or replace new lookup +% behaviors if necessary. +% +% The name resolver remains untouched, but it calls +% \luafunction{fonts.names.resolve()} internally anyways (see +% \fileent{luaotfload-database.lua}). +% +% \begin{macrocode} + +local request_resolvers = fonts.definers.resolvers +local formats = fonts.formats +formats.ofm = "type1" + +% \end{macrocode} +% \identifier{luaotfload} promises easy access to system fonts. +% Without additional precautions, this cannot be achieved by +% \identifier{kpathsea} alone, because it searches only the +% \fileent{texmf} directories by default. +% Although it is possible for \identifier{kpathsea} to include extra +% paths by adding them to the \verb|OSFONTDIR| environment variable, +% this is still short of the goal »\emphasis{it just works!}«. +% When building the font database \identifier{luaotfload} scans +% system font directories anyways, so we already have all the +% information for looking sytem fonts. +% With the release version 2.2 the file names are indexed in the database +% as well and we are ready to resolve \verb|file:| lookups this way. +% Thus we no longer need to call the \identifier{kpathsea} library in +% most cases when looking up font files, only when generating the database. +% +% \begin{macrocode} +request_resolvers.file = function (specification) + --local found = fonts.names.crude_file_lookup(specification.name) + local found = fonts.names.crude_file_lookup_verbose(specification.name) + specification.name = found[1] + --if format then specification.forced = format end +end + +% \end{macrocode} +% We classify as \verb|anon:| those requests that have neither a +% prefix nor brackets. According to Khaled\footnote{% +% \url{https://github.com/phi-gamma/luaotfload/issues/4#issuecomment-17090553}. +% } +% they are the \XETEX equivalent of a \verb|name:| request, so we will be +% treating them as such. +% +% \begin{macrocode} + +--request_resolvers.anon = request_resolvers.name + +% \end{macrocode} +% There is one drawback, though. +% This syntax is also used for requesting fonts in \identifier{Type1} +% (\abbrev{tfm}, \abbrev{ofm}) format. +% These are essentially \verb|file:| lookups and must be caught before +% the \verb|name:| resolver kicks in, lest they cause the database to +% update. +% Even if we were to require the \verb|file:| prefix for all +% \identifier{Type1} requests, tests have shown that certain fonts still +% include further fonts (e.~g. \fileent{omlgcb.ofm} will ask for +% \fileent{omsecob.tfm}) \emphasis{using the old syntax}. +% For this reason, we introduce an extra check with an early return. % % \begin{macrocode} +local type1_formats = { "tfm", "ofm", } + +request_resolvers.anon = function (specification) + local name = specification.name + for i=1, #type1_formats do + local format = type1_formats[i] + if resolvers.findfile(name, format) then + specification.name = file.addsuffix(name, format) + return + end + end + request_resolvers.name(specification) +end -fonts.definers.resolvers.file = function (specification) - specification.name = fonts.names.resolve('', '', specification) +% \end{macrocode} +% Prior to version 2.2, \identifier{luaotfload} did not distinguish +% \verb|file:| and \verb|path:| lookups, causing complications with the +% resolver. +% Now we test if the requested name is an absolute path in the file +% system, otherwise we fall back to the \verb|file:| lookup. +% +% \begin{macrocode} +request_resolvers.path = function (specification) + local exists, _ = lfsisfile(specification.name) + if not exists then -- resort to file: lookup + request_resolvers.file(specification) + end end % \end{macrocode} @@ -1442,7 +1703,6 @@ local patch_defined_font = function (specification, size, id) if type(tfmdata) == "table" then call_callback("luaotfload.patch_font", tfmdata) end - -- inspect(table.keys(tfmdata)) return tfmdata end diff --git a/tests/pln-request-4-slashed.tex b/tests/pln-request-4-slashed.tex new file mode 100644 index 0000000..5e7d99e --- /dev/null +++ b/tests/pln-request-4-slashed.tex @@ -0,0 +1,12 @@ +\ifdefined\directlua\input luaotfload.sty\fi +\font\iwona =iwona at 20pt +\font\iwonabold =iwona/b at 20pt +\font\iwonaitalic =iwona/i at 20pt +\font\iwonabolditalic =iwona/bi at 20pt + +\def\test{foo bar baz \endgraf} +{\iwona \test} +{\iwonabold \test} +{\iwonaitalic \test} +{\iwonabolditalic \test} +\bye diff --git a/tests/pln-request-5-cached.tex b/tests/pln-request-5-cached.tex new file mode 100644 index 0000000..8ba4a5e --- /dev/null +++ b/tests/pln-request-5-cached.tex @@ -0,0 +1,18 @@ +\ifdefined\directlua + \directlua{config = config or { luaotfload = { } } + config.luaotfload.resolver = "cached" + config.luaotfload.loglevel = 5 } + \input luaotfload.sty +\fi + +\font\iwona =name:iwona at 20pt +\font\iwonabold =name:iwona/b at 20pt +\font\iwonaitalic =name:iwona/i at 20pt +\font\iwonabolditalic =name:iwona/bi at 20pt + +\def\test{foo bar baz \endgraf} +{\iwona \test} +{\iwonabold \test} +{\iwonaitalic \test} +{\iwonabolditalic \test} +\bye diff --git a/tests/pln-subfont-1.tex b/tests/pln-subfont-1.tex new file mode 100644 index 0000000..fb8e1e7 --- /dev/null +++ b/tests/pln-subfont-1.tex @@ -0,0 +1,12 @@ +\ifdefined\directlua\input luaotfload.sty\fi +%% This requires the Cambria fonts from MS. +\directlua{ + inspect(fontloader.info"cambria.ttc") +} +%% Here we load both subfonts in the collection +%% with the not-quite documented subfont syntax. +\font\subfontone="file:cambria.ttc(0)" at 42pt +\font\subfonttwo="file:cambria.ttc(1)" at 42pt +\subfontone foo bar baz \endgraf +\subfonttwo foo bar baz \endgraf +\bye diff --git a/tests/pln-tfm.tex b/tests/pln-tfm.tex new file mode 100644 index 0000000..26fa738 --- /dev/null +++ b/tests/pln-tfm.tex @@ -0,0 +1,8 @@ +\ifdefined\directlua\input luaotfload.sty \fi +%% TFM’s can be loaded with a file: request ... +\font\antykwatorunska="file:rm-anttr" +%% or with an anonymous request, like in þe olde TeX: +\font\antykwatorunskabcap=ec-anttbcap +\antykwatorunska foo bar +\antykwatorunskabcap baz xyzzy +\bye |