summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPhilipp Gesang <phg42.2a@gmail.com>2013-04-29 15:02:52 +0200
committerPhilipp Gesang <phg42.2a@gmail.com>2013-04-29 15:02:52 +0200
commit71f44e48c73c13d23431274f5886f9480a60fc24 (patch)
tree8939726a4ab60d86c2461f74827940e8fb0e3e27
parent9c16f2f73eed3946020b0197d9fe97034c4f9f24 (diff)
parente79f34d859d48be485589f19fc6905afa5872a53 (diff)
downloadluaotfload-71f44e48c73c13d23431274f5886f9480a60fc24.tar.gz
merge newsyntax branch into master
-rwxr-xr-xfontdbutil.lua14
-rw-r--r--luaotfload-database.lua185
-rw-r--r--luaotfload-features.lua395
-rw-r--r--luaotfload.dtx373
-rw-r--r--tests/pln-request-4-slashed.tex12
-rw-r--r--tests/pln-request-5-cached.tex18
-rw-r--r--tests/pln-subfont-1.tex12
-rw-r--r--tests/pln-tfm.tex8
8 files changed, 787 insertions, 230 deletions
diff --git a/fontdbutil.lua b/fontdbutil.lua
index 1aa99d0..470d282 100755
--- a/fontdbutil.lua
+++ b/fontdbutil.lua
@@ -28,7 +28,7 @@ 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.
@@ -276,14 +276,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
diff --git a/luaotfload-database.lua b/luaotfload-database.lua
index 3085b63..d4613ba 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--
@@ -383,22 +389,6 @@ TODO:
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"
@@ -570,8 +543,6 @@ 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
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
@@ -870,10 +844,10 @@ 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
- }
+ if texmf then
+ filename = filebasename(filename)
+ end
+ tfmdata.filename = { filename, subfont }
tfmdata.weight = metadata.pfminfo.weight
tfmdata.width = metadata.pfminfo.width
tfmdata.slant = metadata.italicangle
@@ -1508,15 +1482,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..6cbfdf4 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 fontdbutil --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.dtx b/luaotfload.dtx
index 18e01d8..6392c64 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}\space 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}.
% }
@@ -1003,7 +1184,7 @@ 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 +1195,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 +1510,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 +1532,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 +1702,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