summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--filegraph.dot44
-rwxr-xr-xfontdbutil.lua34
-rw-r--r--luaotfload-basics-gen.lua48
-rw-r--r--luaotfload-database.lua862
-rw-r--r--luaotfload-features.lua74
-rw-r--r--luaotfload-merged.lua856
-rw-r--r--luaotfload-override.lua3
-rw-r--r--luaotfload.dtx75
-rw-r--r--tests/lookups.tex6
10 files changed, 1545 insertions, 459 deletions
diff --git a/.gitignore b/.gitignore
index 5c508a2..4160efd 100644
--- a/.gitignore
+++ b/.gitignore
@@ -31,3 +31,5 @@ tests/*.dvi
tests/*.ofm
tests/*.ovp
tests/*.ovf
+tests/*.sty
+tests/luaotfload*
diff --git a/filegraph.dot b/filegraph.dot
index f1283f0..a0eadec 100644
--- a/filegraph.dot
+++ b/filegraph.dot
@@ -27,6 +27,9 @@ strict digraph luaotfload_files { //looks weird with circo ...
/* ····································································
* file structure
* ································································· */
+ fontdbutil -> font_names [label="--update",
+ style=dashed]
+
luaotfload -> otfl_fonts_merged [label="merged"]
luaotfload -> merged_lua_libs [label="unmerged", style=solid]
luaotfload -> merged_luatex_fonts [label="unmerged", style=solid]
@@ -35,7 +38,6 @@ strict digraph luaotfload_files { //looks weird with circo ...
luaotfload -> luaotfload_libs
luaotfload -> otfl_blacklist_cnf
-
otfl_fonts_merged -> merged_lua_libs [label="merged",
style=dotted,
lhead=cluster_merged]
@@ -46,12 +48,36 @@ strict digraph luaotfload_files { //looks weird with circo ...
style=dotted,
lhead=cluster_merged]
+ merged_luatex_fonts -> font_age [label="luatex-fonts-enc.lua",
+ ltail=cluster_merged]
+
+ luaotfload_libs -> font_names [label="luaotfload-database.lua"]
+ mkglyphlist -> font_age [label="generates from glyphlist.txt",
+ style=dashed]
+
+ subgraph { rank = same; mkglyphlist; fontdbutil; luaotfload }
/* ····································································
* main files
* ································································· */
+ fontdbutil [label = "fontdbutil\nmkluatexfontdb.lua",
+ shape = rect,
+ width = "3.2cm",
+ height = "1.2cm",
+ color = "#01012222",
+ style = "filled,rounded",
+ penwidth=2]
+
+ mkglyphlist [label = "mkglyphlist",
+ shape = rect,
+ width = "3.2cm",
+ height = "1.2cm",
+ color = "#01012222",
+ style = "filled,rounded",
+ penwidth=2]
+
luaotfload [label = "luaotfload.lua",
shape = rect,
width = "3.2cm",
@@ -81,6 +107,22 @@ strict digraph luaotfload_files { //looks weird with circo ...
* ································································· */
+ font_age [style = "filled,dashed",
+ shape = rect,
+ width = "3.2cm",
+ fillcolor = "#01012222",
+ color = grey40,
+ style = "filled,dotted,rounded",
+ label = "font-age.lua"]
+
+ font_names [style = "filled,dashed",
+ shape = rect,
+ width = "3.2cm",
+ fillcolor = "#01012222",
+ color = grey40,
+ style = "filled,dotted,rounded",
+ label = "luaotfload-names.lua\nluaotfload-names.luc"]
+
otfl_blacklist_cnf [style = "filled,dashed",
shape = rect,
width = "3.2cm",
diff --git a/fontdbutil.lua b/fontdbutil.lua
index 31c7dfa..fed1840 100755
--- a/fontdbutil.lua
+++ b/fontdbutil.lua
@@ -1,11 +1,15 @@
#!/usr/bin/env texlua
---[[
+
+--[[doc--
This file was originally written by Elie Roux and Khaled Hosny and is under CC0
license (see http://creativecommons.org/publicdomain/zero/1.0/legalcode).
-This file is a wrapper for the luaotfload's font names module. It is part of the
-luaotfload bundle, please see the luaotfload documentation for more info.
---]]
+This file is a wrapper for the luaotfload font names module
+(luaotfload-database.lua). It is part of the luaotfload bundle, please
+see the luaotfload documentation for more info. Report bugs to
+\url{https://github.com/lualatex/luaotfload/issues}.
+
+--doc]]--
kpse.set_program_name"luatex"
@@ -14,18 +18,17 @@ local texiowrite_nl = texio.write_nl
local stringfind = string.find
local stringlower = string.lower
--- First we need to be able to load module (code copied from
--- luatexbase-loader.sty):
+
local loader_file = "luatexbase.loader.lua"
local loader_path = assert(kpse.find_file(loader_file, "lua"),
"File '"..loader_file.."' not found")
+
string.quoted = string.quoted or function (str)
return string.format("%q",str)
end
---texiowrite_nl("("..loader_path..")")
-dofile(loader_path) -- FIXME this pollutes stdout with filenames
+dofile(loader_path)
--[[doc--
Depending on how the script is called we change its behavior.
@@ -73,7 +76,20 @@ config.lualibs.prefer_merged = true
config.lualibs.load_extended = false
require"lualibs"
+
+--[[doc--
+\fileent{luatex-basics-gen.lua} calls functions from the
+\luafunction{texio.*} library; too much for our taste.
+We intercept them with dummies.
+--doc]]--
+
+local dummy_function = function ( ) end
+local backup_write, backup_write_nl = texio.write, texio.write_nl
+
+texio.write, texio.write_nl = dummy_function, dummy_function
require"luaotfload-basics-gen.lua"
+texio.write, texio.write_nl = backup_write, backup_write_nl
+
require"luaotfload-override.lua" --- this populates the logs.* namespace
require"luaotfload-database"
require"alt_getopt"
@@ -224,7 +240,7 @@ actions.generate = function (job)
local fontnames, savedname
fontnames = names.update(fontnames, job.force_reload)
logs.names_report("log", 0, "db",
- "fonts in the database", "%i", #fontnames.mappings)
+ "Fonts in the database: %i", #fontnames.mappings)
savedname = names.save(fontnames)
if savedname then --- FIXME have names.save return bool
return true, true
diff --git a/luaotfload-basics-gen.lua b/luaotfload-basics-gen.lua
index 727086e..61f3910 100644
--- a/luaotfload-basics-gen.lua
+++ b/luaotfload-basics-gen.lua
@@ -130,7 +130,9 @@ end
caches = { }
-local writable, readables = nil, { }
+local writable = nil
+local readables = { }
+local usingjit = jit
if not caches.namespace or caches.namespace == "" or caches.namespace == "context" then
caches.namespace = 'generic'
@@ -204,7 +206,7 @@ end
local function makefullname(path,name)
if path and path ~= "" then
name = "temp-" .. name -- clash prevention
- return file.addsuffix(file.join(path,name),"lua"), file.addsuffix(file.join(path,name),"luc")
+ return file.addsuffix(file.join(path,name),"lua"), file.addsuffix(file.join(path,name),usingjit and "lub" or "luc")
end
end
@@ -265,26 +267,36 @@ end
-- this) in which case one should limit the method to luac and enable support
-- for execution.
-caches.compilemethod = "both"
+-- function caches.compile(data,luaname,lucname)
+-- local d = io.loaddata(luaname)
+-- if not d or d == "" then
+-- d = table.serialize(data,true) -- slow
+-- end
+-- if d and d ~= "" then
+-- local f = io.open(lucname,'w')
+-- if f then
+-- local s = loadstring(d)
+-- if s then
+-- f:write(string.dump(s,true))
+-- end
+-- f:close()
+-- end
+-- end
+-- end
function caches.compile(data,luaname,lucname)
- local done = false
- if caches.compilemethod == "luac" or caches.compilemethod == "both" then
- done = os.spawn("texluac -o " .. string.quoted(lucname) .. " -s " .. string.quoted(luaname)) == 0
+ local d = io.loaddata(luaname)
+ if not d or d == "" then
+ d = table.serialize(data,true) -- slow
end
- if not done and (caches.compilemethod == "dump" or caches.compilemethod == "both") then
- local d = io.loaddata(luaname)
- if not d or d == "" then
- d = table.serialize(data,true) -- slow
- end
- if d and d ~= "" then
- local f = io.open(lucname,'w')
- if f then
- local s
- if _G["loadstring"] then s=loadstring(d) else s=load(d) end
- f:write(string.dump(s))
- f:close()
+ if d and d ~= "" then
+ local f = io.open(lucname,'w')
+ if f then
+ local s = loadstring(d)
+ if s then
+ f:write(string.dump(s,true))
end
+ f:close()
end
end
end
diff --git a/luaotfload-database.lua b/luaotfload-database.lua
index 19b04db..d731038 100644
--- a/luaotfload-database.lua
+++ b/luaotfload-database.lua
@@ -18,6 +18,7 @@ local pcall = pcall
local require = require
local tonumber = tonumber
+local fontloaderinfo = fontloader.info
local iolines = io.lines
local ioopen = io.open
local kpseexpand_path = kpse.expand_path
@@ -45,6 +46,8 @@ local utf8lower = unicode.utf8.lower
local dirglob = dir.glob
local dirmkdirs = dir.mkdirs
local filebasename = file.basename
+local filenameonly = file.nameonly
+local filedirname = file.dirname
local filecollapsepath = file.collapsepath or file.collapse_path
local fileextname = file.extname
local fileiswritable = file.iswritable
@@ -81,32 +84,6 @@ end
names.path.dir = writable_path
names.path.path = filejoin(writable_path, names.path.basename)
-
----- <FIXME>
----
---- these lines load some binary module called “lualatex-platform”
---- that doesn’t appear to build with Lua 5.2. I’m going ahead and
---- disable it for the time being until someone clarifies what it
---- is supposed to do and whether we should care to fix it.
----
---local success = pcall(require, "luatexbase.modutils")
---if success then
--- success = pcall(luatexbase.require_module,
--- "lualatex-platform", "2011/03/30")
--- print(success)
---end
-
---local get_installed_fonts
---if success then
--- get_installed_fonts = lualatex.platform.get_installed_fonts
---else
--- function get_installed_fonts()
--- end
---end
----- </FIXME>
-
-local get_installed_fonts = nil
-
--[[doc--
Auxiliary functions
--doc]]--
@@ -121,10 +98,46 @@ local sanitize_string = function (str)
return nil
end
+--[[doc--
+This is a sketch of the db:
+
+ type dbobj = {
+ mappings : fontentry list;
+ status : filestatus;
+ version : float;
+ }
+ and fontentry = {
+ familyname : string;
+ filename : (string * bool);
+ fontname : string;
+ fullname : string;
+ names : {
+ family : string;
+ fullname : string;
+ psname : string;
+ subfamily : string;
+ }
+ size : int list;
+ slant : int;
+ weight : int;
+ width : int;
+ }
+ and filestatus = (fullname, { index : int list; timestamp : int }) dict
+
+beware that this is a reconstruction and may be incomplete.
+
+--doc]]--
+
local fontnames_init = function ( )
return {
mappings = { },
status = { },
+ --- adding filename mapping increases the
+ --- size of the serialized db on my system
+ --- (5840 font files) by a factor of ...
+ barenames = { },--- incr. by 1.11
+ basenames = { },--- incr. by 1.22
+-- fullnames = { },--- incr. by 1.48
version = names.version,
}
end
@@ -180,12 +193,16 @@ local save_names
local scan_external_dir
local update_names
+--- unit -> dbobj
load_names = function ( )
+ local starttime = os.gettimeofday()
local foundname, data = load_lua_file(names.path.path)
if data then
report("info", 1, "db",
"Font names database loaded", "%s", foundname)
+ report("info", 1, "db", "Loading took %0.f ms",
+ 1000*(os.gettimeofday()-starttime))
else
report("info", 0, "db",
[[Font names database not found, generating new one.
@@ -204,10 +221,6 @@ do
regular = { "normal", "roman",
"plain", "book",
"medium", },
- --- TODO note from Élie Roux
- --- boldregular was for old versions of Linux Libertine, is it still useful?
- --- semibold is in new versions of Linux Libertine, but there is also a bold,
- --- not sure it's useful here...
bold = { "demi", "demibold",
"semibold", "boldregular",},
italic = { "regularitalic", "normalitalic",
@@ -227,6 +240,55 @@ end
local fonts_loaded = false
local fonts_reloaded = false
+local crude_file_lookup_verbose = function (data, filename)
+ local found = data.barenames[filename]
+ if found then
+ report("info", 0, "db",
+ "crude file lookup: req=%s; hit=bare; ret=%s",
+ filename, found[1])
+ return found
+ end
+-- found = data.fullnames[filename]
+-- if found then
+-- report("info", 0, "db",
+-- "crude file lookup: req=%s; hit=bare; ret=%s",
+-- filename, found[1])
+-- return found
+-- end
+ found = data.basenames[filename]
+ if found then
+ report("info", 0, "db",
+ "crude file lookup: req=%s; hit=bare; ret=%s",
+ filename, found[1])
+ return found
+ end
+ 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 }
+ end
+ return false
+end
+
+local crude_file_lookup = function (data, filename)
+ local found = data.barenames[filename]
+-- or data.fullnames[filename]
+ or data.basenames[filename]
+ if found then return found 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
+end
+
--[[doc--
Luatex-fonts, the font-loader package luaotfload imports, comes with
@@ -253,8 +315,10 @@ font database created by the mkluatexfontdb script.
--- · specification: string (== <lookup> ":" <name>)
--- · sub: string
---
---- the return value of “resolve” is the file name of the requested
---- font
+--- the first return value of “resolve” is the file name of the
+--- requested font (string)
+--- the second is of type bool or string and indicates the subfont of a
+--- ttc
---
--- 'a -> 'a -> table -> (string * string | bool * bool)
---
@@ -264,6 +328,18 @@ font database created by the mkluatexfontdb script.
---
---
resolve = function (_,_,specification) -- the 1st two parameters are used by ConTeXt
+ if not fonts_loaded then
+ names.data = load_names()
+ fonts_loaded = true
+ 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"
@@ -274,152 +350,162 @@ resolve = function (_,_,specification) -- the 1st two parameters are used by Con
size = specification.size / 65536
end
- if not fonts_loaded then
- names.data = load_names()
- fonts_loaded = true
+ if type(data) ~= "table" then
+ --- this catches a case where load_names() doesn’t
+ --- return a database object, which can happen only
+ --- in case there is valid Lua code in the database,
+ --- but it’s not a table, e.g. it contains an integer.
+ if not fonts_reloaded then
+ return reload_db("invalid database; not a table",
+ resolve, nil, nil, specification
+ )
+ end
+ --- unsucessfully reloaded; bail
+ return specification.name, false, false
end
- local data = names.data
- if type(data) == "table" then
- local db_version, nms_version = data.version, names.version
- if data.version ~= names.version then
- report("log", 0, "db",
- [[version mismatch; expected %4.3f, got %4.3f]],
- nms_version, db_version
- )
- return reload_db(resolve, nil, nil, specification)
+ local db_version, nms_version = data.version, names.version
+ if db_version ~= nms_version then
+ report("log", 0, "db",
+ [[version mismatch; expected %4.3f, got %4.3f]],
+ nms_version, db_version
+ )
+ return reload_db("version mismatch", resolve, nil, nil, specification)
+ end
+
+ if not data.mappings then
+ return reload_db("invalid database; missing font mapping",
+ resolve, nil, nil, specification
+ )
+ end
+
+ 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 = sanitize_string(face.names and face.names.family)
+ local subfamily = sanitize_string(face.names and face.names.subfamily)
+ local fullname = sanitize_string(face.names and face.names.fullname)
+ local psname = sanitize_string(face.names and face.names.psname)
+ local fontname = sanitize_string(face.fontname)
+ local pfullname = sanitize_string(face.fullname)
+ local optsize, dsnsize, maxsize, minsize
+ if #face.size > 0 then
+ optsize = face.size
+ dsnsize = optsize[1] and optsize[1] / 10
+ -- can be nil
+ maxsize = optsize[2] and optsize[2] / 10 or dsnsize
+ minsize = optsize[3] and optsize[3] / 10 or dsnsize
end
- if data.mappings then
- 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 = sanitize_string(face.names and face.names.family)
- local subfamily = sanitize_string(face.names and face.names.subfamily)
- local fullname = sanitize_string(face.names and face.names.fullname)
- local psname = sanitize_string(face.names and face.names.psname)
- local fontname = sanitize_string(face.fontname)
- local pfullname = sanitize_string(face.fullname)
- local optsize, dsnsize, maxsize, minsize
- if #face.size > 0 then
- optsize = face.size
- dsnsize = optsize[1] and optsize[1] / 10
- -- can be nil
- maxsize = optsize[2] and optsize[2] / 10 or dsnsize
- minsize = optsize[3] and optsize[3] / 10 or dsnsize
- end
- if name == family then
- if subfamily == style then
- if optsize then
- if dsnsize == size
- or (size > minsize and size <= maxsize) then
- found[1] = face
- break
- else
- found[#found+1] = face
- end
- else
- found[1] = face
- break
- end
- elseif synonym_set[style] and
- synonym_set[style][subfamily] then
- if optsize then
- if dsnsize == size
- or (size > minsize and size <= maxsize) then
- found[1] = face
- break
- else
- found[#found+1] = face
- end
- else
- found[1] = face
- break
- end
- elseif subfamily == "regular" or
- synonym_set.regular[subfamily] then
- found.fallback = face
+ if name == family then
+ if subfamily == style then
+ if optsize then
+ if dsnsize == size
+ or (size > minsize and size <= maxsize) then
+ found[1] = face
+ break
+ else
+ found[#found+1] = face
end
else
- if name == fullname
- or name == pfullname
- or name == fontname
- or name == psname then
- if optsize then
- if dsnsize == size
- or (size > minsize and size <= maxsize) then
- found[1] = face
- break
- else
- found[#found+1] = face
- end
- else
- found[1] = face
- break
- end
+ found[1] = face
+ break
+ end
+ elseif synonym_set[style] and
+ synonym_set[style][subfamily] then
+ if optsize then
+ if dsnsize == size
+ or (size > minsize and size <= maxsize) then
+ found[1] = face
+ break
+ else
+ found[#found+1] = face
end
+ else
+ found[1] = face
+ break
end
+ elseif subfamily == "regular" or
+ synonym_set.regular[subfamily] then
+ found.fallback = face
end
- if #found == 1 then
- if kpselookup(found[1].filename[1]) then
- report("log", 0, "resolve",
- "font family='%s', subfamily='%s' found: %s",
- name, style, found[1].filename[1]
- )
- return found[1].filename[1], found[1].filename[2], true
- end
- elseif #found > 1 then
- -- we found matching font(s) but not in the requested optical
- -- sizes, so we loop through the matches to find the one with
- -- least difference from the requested size.
- local closest
- local least = math.huge -- initial value is infinity
- for i,face in next, found do
- local dsnsize = face.size[1]/10
- local difference = mathabs(dsnsize-size)
- if difference < least then
- closest = face
- least = difference
+ else
+ if name == fullname
+ or name == pfullname
+ or name == fontname
+ or name == psname then
+ if optsize then
+ if dsnsize == size
+ or (size > minsize and size <= maxsize) then
+ found[1] = face
+ break
+ else
+ found[#found+1] = face
end
+ else
+ found[1] = face
+ break
end
- if kpselookup(closest.filename[1]) then
- report("log", 0, "resolve",
- "font family='%s', subfamily='%s' found: %s",
- name, style, closest.filename[1]
- )
- return closest.filename[1], closest.filename[2], true
- end
- elseif found.fallback then
- 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(resolve, nil, nil, specification)
- else
- --- else, fallback to requested name
- --- specification.name is empty with absolute paths, looks
- --- like a bug in the specification parser <TODO< is it still
- --- relevant? looks not...
- return specification.name, false, false
+ end
+ end
+
+ if #found == 1 then
+ if kpselookup(found[1].filename[1]) then
+ report("log", 0, "resolve",
+ "font family='%s', subfamily='%s' found: %s",
+ name, style, found[1].filename[1]
+ )
+ return found[1].filename[1], found[1].filename[2], true
+ end
+ elseif #found > 1 then
+ -- we found matching font(s) but not in the requested optical
+ -- sizes, so we loop through the matches to find the one with
+ -- least difference from the requested size.
+ local closest
+ local least = math.huge -- initial value is infinity
+ for i,face in next, found do
+ local dsnsize = face.size[1]/10
+ local difference = mathabs(dsnsize-size)
+ if difference < least then
+ closest = face
+ least = difference
end
end
- else --- no db or outdated; reload names and retry
- if not fonts_reloaded then
- return reload_db(resolve, nil, nil, specification)
- else --- unsucessfully reloaded; bail
- return specification.name, false, false
+ if kpselookup(closest.filename[1]) then
+ report("log", 0, "resolve",
+ "font family='%s', subfamily='%s' found: %s",
+ name, style, closest.filename[1]
+ )
+ return closest.filename[1], closest.filename[2], true
end
+ elseif found.fallback then
+ 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 .. "”",
+ resolve, nil, nil, specification
+ )
end
+
+ --- else, fallback to requested name
+ --- specification.name is empty with absolute paths, looks
+ --- like a bug in the specification parser <TODO< is it still
+ --- relevant? looks not...
+ return specification.name, false, false
end --- resolve()
--- when reload is triggered we update the database
--- and then re-run the caller with the arg list
---- ('a -> 'a) -> 'a list -> 'a
-reload_db = function (caller, ...)
- report("log", 1, "db", "reload initiated")
+--- string -> ('a -> 'a) -> 'a list -> 'a
+reload_db = function (why, caller, ...)
+ report("log", 1, "db", "reload initiated; reason: “%s”", why)
names.data = update_names()
save_names(names.data)
fonts_reloaded = true
@@ -466,67 +552,65 @@ find_closest = function (name, limit)
local data = names.data
- if type(data) == "table" then
- local by_distance = { } --- (int, string list) dict
- local distances = { } --- int list
- local cached = { } --- (string, int) dict
- local mappings = data.mappings
- local n_fonts = #mappings
-
- for n = 1, n_fonts do
- local current = mappings[n]
- local cnames = current.names
- --[[
- This is simplistic but surpisingly fast.
- Matching is performed against the “family” name
- of a db record. We then store its “fullname” at
- it edit distance.
- We should probably do some weighting over all the
- font name categories as well as whatever agrep
- does.
- --]]
- if cnames then
- local fullname, family = cnames.fullname, cnames.family
- family = sanitize_string(family)
-
- local dist = cached[family]--- maybe already calculated
- if not dist then
- dist = iterative_levenshtein(name, family)
- cached[family] = dist
- end
- local namelst = by_distance[dist]
- if not namelst then --- first entry
- namelst = { fullname }
- distances[#distances+1] = dist
- else --- append
- namelst[#namelst+1] = fullname
- end
- by_distance[dist] = namelst
+ if type(data) ~= "table" then
+ return reload_db("no database", find_closest, name)
+ end
+ local by_distance = { } --- (int, string list) dict
+ local distances = { } --- int list
+ local cached = { } --- (string, int) dict
+ local mappings = data.mappings
+ local n_fonts = #mappings
+
+ for n = 1, n_fonts do
+ local current = mappings[n]
+ local cnames = current.names
+ --[[
+ This is simplistic but surpisingly fast.
+ Matching is performed against the “family” name
+ of a db record. We then store its “fullname” at
+ it edit distance.
+ We should probably do some weighting over all the
+ font name categories as well as whatever agrep
+ does.
+ --]]
+ if cnames then
+ local fullname, family = cnames.fullname, cnames.family
+ family = sanitize_string(family)
+
+ local dist = cached[family]--- maybe already calculated
+ if not dist then
+ dist = iterative_levenshtein(name, family)
+ cached[family] = dist
end
- end
-
- --- print the matches according to their distance
- local n_distances = #distances
- if n_distances > 0 then --- got some data
- tablesort(distances)
- limit = mathmin(n_distances, limit)
- report(false, 1, "query",
- "displaying %d distance levels", limit)
-
- for i = 1, limit do
- local dist = distances[i]
- local namelst = by_distance[dist]
- report(false, 0, "query",
- "distance from “" .. name .. "”: " .. dist
- .. "\n " .. tableconcat(namelst, "\n ")
- )
+ local namelst = by_distance[dist]
+ if not namelst then --- first entry
+ namelst = { fullname }
+ distances[#distances+1] = dist
+ else --- append
+ namelst[#namelst+1] = fullname
end
+ by_distance[dist] = namelst
+ end
+ end
- return true
+ --- print the matches according to their distance
+ local n_distances = #distances
+ if n_distances > 0 then --- got some data
+ tablesort(distances)
+ limit = mathmin(n_distances, limit)
+ report(false, 1, "query",
+ "displaying %d distance levels", limit)
+
+ for i = 1, limit do
+ local dist = distances[i]
+ local namelst = by_distance[dist]
+ report(false, 0, "query",
+ "distance from “" .. name .. "”: " .. dist
+ .. "\n " .. tableconcat(namelst, "\n ")
+ )
end
- return false
- else --- need reload
- return reload_db(find_closest, name)
+
+ return true
end
return false
end --- find_closest()
@@ -595,107 +679,173 @@ font_fullinfo = function (filename, subfont, texmf)
return tfmdata
end
-local load_font = function (filename, fontnames, newfontnames, texmf)
- local newmappings = newfontnames.mappings
- local newstatus = newfontnames.status
- local mappings = fontnames.mappings
- local status = fontnames.status
- local basename = filebasename(filename)
- local basefile = texmf and basename or filename
- if filename then
- if names.blacklist[filename] or
- names.blacklist[basename] then
- report("log", 2, "db", "ignoring font", "%s", filename)
- return
- end
- local timestamp, db_timestamp
- db_timestamp = status[basefile] and status[basefile].timestamp
- timestamp = lfs.attributes(filename, "modification")
-
- local index_status = newstatus[basefile] or (not texmf and newstatus[basename])
- if index_status and index_status.timestamp == timestamp then
- -- already indexed this run
- return
- 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)
+ local newmappings = newfontnames.mappings
+ local newstatus = newfontnames.status
- newstatus[basefile] = newstatus[basefile] or { }
- newstatus[basefile].timestamp = timestamp
- newstatus[basefile].index = newstatus[basefile].index or { }
+-- local newfullnames = newfontnames.fullnames
+ local newbasenames = newfontnames.basenames
+ local newbarenames = newfontnames.barenames
- if db_timestamp == timestamp and not newstatus[basefile].index[1] then
- for _,v in next, status[basefile].index do
- local index = #newstatus[basefile].index
- newmappings[#newmappings+1] = mappings[v]
- newstatus[basefile].index[index+1] = #newmappings
- end
- report("log", 1, "db", "font already indexed", "%s", basefile)
- return
+ local mappings = fontnames.mappings
+ local status = fontnames.status
+-- local fullnames = fontnames.fullnames
+ local basenames = fontnames.basenames
+ local barenames = fontnames.barenames
+
+ 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
+
+ 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
+ timestamp = lfs.attributes(fullname, "modification")
+
+ local index_status = newstatus[entryname]
+ or (not texmf and newstatus[basename])
+ local teststat = newstatus[entryname]
+ --- 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 { }
+
+ if db_timestamp == timestamp
+ and not newstatus[entryname].index[1] then
+ for _,v in next, status[entryname].index do
+ local index = #newstatus[entryname].index
+ local fullinfo = mappings[v]
+ newmappings[#newmappings+1] = fullinfo --- keep
+ newstatus[entryname].index[index+1] = #newmappings
+-- newfullnames[fullname] = fullinfo.filename
+ newbasenames[basename] = fullinfo.filename
+ newbarenames[barename] = fullinfo.filename
end
- local info = fontloader.info(filename)
- if info then
- if type(info) == "table" and #info > 1 then
- for i in next, info do
- local fullinfo = font_fullinfo(filename, i-1, texmf)
- if not fullinfo then
- return
- end
- local index = newstatus[basefile].index[i]
- if newstatus[basefile].index[i] then
- index = newstatus[basefile].index[i]
- else
- index = #newmappings+1
- end
- newmappings[index] = fullinfo
- newstatus[basefile].index[i] = index
- end
- else
- local fullinfo = font_fullinfo(filename, false, texmf)
+ report("log", 2, "db", "font “%s” already indexed", entryname)
+ return false
+ end
+
+ local info = fontloaderinfo(fullname)
+ if info then
+ if type(info) == "table" and #info > 1 then --- ttc
+ for i in next, info do
+ local fullinfo = font_fullinfo(fullname, i-1, texmf)
if not fullinfo then
- return
+ return false
end
- local index
- if newstatus[basefile].index[1] then
- index = newstatus[basefile].index[1]
+ local index = newstatus[entryname].index[i]
+ if newstatus[entryname].index[i] then
+ index = newstatus[entryname].index[i]
else
index = #newmappings+1
end
- newmappings[index] = fullinfo
- newstatus[basefile].index[1] = index
+ newmappings[index] = fullinfo
+-- newfullnames[fullname] = fullinfo.filename
+ newbasenames[basename] = fullinfo.filename
+ newbarenames[barename] = fullinfo.filename
+ newstatus[entryname].index[i] = index
end
else
- report("log", 1, "db", "failed to load", "%s", basefile)
+ local fullinfo = font_fullinfo(fullname, false, texmf)
+ if not fullinfo then
+ return false
+ end
+ local index
+ if newstatus[entryname].index[1] then
+ index = newstatus[entryname].index[1]
+ else
+ index = #newmappings+1
+ end
+ newmappings[index] = fullinfo
+-- newfullnames[fullname] = { fullinfo.filename[1], fullinfo.filename[2] }
+ newbasenames[basename] = { fullinfo.filename[1], fullinfo.filename[2] }
+ newbarenames[barename] = { fullinfo.filename[1], fullinfo.filename[2] }
+ newstatus[entryname].index[1] = index
end
+
+ else --- missing info
+ report("log", 1, "db", "failed to load “%s”", entryname)
+ return false
end
+ return true
end
-local function path_normalize(path)
- --[[
- path normalization:
- - a\b\c -> a/b/c
- - a/../b -> b
- - /cygdrive/a/b -> a:/b
- - reading symlinks under non-Win32
- - using kpse.readable_file on Win32
- ]]
- if os.type == "windows" or os.type == "msdos" or os.name == "cygwin" then
- path = stringgsub(path, '\\', '/')
- path = stringlower(path)
- path = stringgsub(path, '^/cygdrive/(%a)/', '%1:/')
- end
- if os.type ~= "windows" and os.type ~= "msdos" then
- local dest = lfs.readlink(path)
- if dest then
- if kpsereadable_file(dest) then
- path = dest
- elseif kpsereadable_file(filejoin(file.dirname(path), dest)) then
- path = filejoin(file.dirname(path), dest)
- else
- -- broken symlink?
+local path_normalize
+do
+ --- os.type and os.name are constants so we
+ --- choose a normalization function in advance
+ --- instead of testing with every call
+ local os_type, os_name = os.type, os.name
+ local filecollapsepath = filecollapsepath
+ local lfsreadlink = lfs.readlink
+
+ --- windows and dos
+ if os_type == "windows" or os_type == "msdos" then
+ --- ms platfom specific stuff
+ path_normalize = function (path)
+ path = stringgsub(path, '\\', '/')
+ path = stringlower(path)
+ path = stringgsub(path, '^/cygdrive/(%a)/', '%1:/')
+ path = filecollapsepath(path)
+ return path
+ end
+
+ elseif os_name == "cygwin" then -- union of ms + unix
+ path_normalize = function (path)
+ path = stringgsub(path, '\\', '/')
+ path = stringlower(path)
+ path = stringgsub(path, '^/cygdrive/(%a)/', '%1:/')
+ local dest = lfsreadlink(path)
+ if dest then
+ if kpsereadable_file(dest) then
+ path = dest
+ elseif kpsereadable_file(filejoin(filedirname(path), dest)) then
+ path = filejoin(file.dirname(path), dest)
+ else
+ -- broken symlink?
+ end
end
+ path = filecollapsepath(path)
+ return path
+ end
+
+ else -- posix
+ path_normalize = function (path)
+ local dest = lfsreadlink(path)
+ if dest then
+ if kpsereadable_file(dest) then
+ path = dest
+ elseif kpsereadable_file(filejoin(filedirname(path), dest)) then
+ path = filejoin(file.dirname(path), dest)
+ else
+ -- broken symlink?
+ end
+ end
+ path = filecollapsepath(path)
+ return path
end
end
- path = filecollapsepath(path)
- return path
end
fonts.path_normalize = path_normalize
@@ -723,7 +873,7 @@ local function read_blacklist()
if stringsub(line, 1, 1) == "-" then
whitelist[stringsub(line, 2, -1)] = true
else
- report("log", 2, "db", "blacklisted file", "%s", line)
+ report("log", 2, "db", "blacklisted file “%s”", line)
blacklist[line] = true
end
end
@@ -741,69 +891,40 @@ for key, value in next, font_extensions do
font_extensions_set[value] = true
end
---local installed_fonts_scanned = false --- ugh
-
---- we already have scan_os_fonts don’t we?
-
---local function scan_installed_fonts(fontnames, newfontnames)
--- --- Try to query and add font list from operating system.
--- --- This uses the lualatex-platform module.
--- --- <phg>what for? why can’t we do this in Lua?</phg>
--- report("info", 0, "Scanning fonts known to operating system...")
--- local fonts = get_installed_fonts()
--- if fonts and #fonts > 0 then
--- installed_fonts_scanned = true
--- report("log", 2, "operating system fonts found", "%d", #fonts)
--- for key, value in next, fonts do
--- local file = value.path
--- if file then
--- local ext = fileextname(file)
--- if ext and font_extensions_set[ext] then
--- file = path_normalize(file)
--- report("log", 1, "loading font", "%s", file)
--- load_font(file, fontnames, newfontnames, false)
--- end
--- end
--- end
--- else
--- report("log", 2, "Could not retrieve list of installed fonts")
--- end
---end
-
-local function scan_dir(dirname, fontnames, newfontnames, texmf)
+--- string -> dbobj -> dbobj -> bool -> (int * int)
+local scan_dir = function (dirname, fontnames, newfontnames, texmf)
--[[
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
+ - names is the font database to fill -> no such term!!!
- texmf is a boolean saying if we are scanning a texmf directory
]]
- local list, found = { }, { }
- local nbfound = 0
+ local n_scanned, n_new = 0, 0 --- total of fonts collected
report("log", 2, "db", "scanning", "%s", dirname)
for _,i in next, font_extensions do
for _,ext in next, { i, stringupper(i) } do
- found = dirglob(stringformat("%s/**.%s$", dirname, ext))
- -- note that glob fails silently on broken symlinks, which happens
- -- sometimes in TeX Live.
- report("log", 2, "db",
- "fonts found", "%s '%s' fonts found", #found, ext)
- nbfound = nbfound + #found
- tableappend(list, found)
+ local found = dirglob(stringformat("%s/**.%s$", dirname, ext))
+ local n_found = #found
+ --- note that glob fails silently on broken symlinks, which
+ --- happens sometimes in TeX Live.
+ report("log", 2, "db", "%s '%s' fonts found", n_found, ext)
+ n_scanned = n_scanned + n_found
+ for j=1, n_found do
+ local fullname = found[j]
+ fullname = path_normalize(fullname)
+ report("log", 2, "db", "loading font “%s”", fullname)
+ local new = load_font(fullname, fontnames, newfontnames, texmf)
+ if new then n_new = n_new + 1 end
+ end
end
end
- report("log", 2, "db",
- "fonts found", "%d fonts found in '%s'", nbfound, dirname)
-
- for _,file in next, list do
- file = path_normalize(file)
- report("log", 1, "db",
- "loading font", "%s", file)
- load_font(file, fontnames, newfontnames, texmf)
- end
+ report("log", 2, "db", "%d fonts found in '%s'", n_scanned, dirname)
+ return n_scanned, n_new
end
local function scan_texmf_fonts(fontnames, newfontnames)
+ local n_scanned, n_new = 0, 0
--[[
This function scans all fonts in the texmf tree, through kpathsea
variables OPENTYPEFONTS and TTFONTS of texmf.cnf
@@ -817,9 +938,12 @@ 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
- scan_dir(d, fontnames, newfontnames, true)
+ local found, new = scan_dir(d, fontnames, newfontnames, true)
+ n_scanned = n_scanned + found
+ n_new = n_new + new
end
end
+ return n_scanned, n_new
end
--[[
@@ -851,7 +975,7 @@ read_fonts_conf = function (path, results, passed_paths)
passed_paths[#passed_paths+1] = path
passed_paths_set = tabletohash(passed_paths, true)
if not fh then
- report("log", 2, "db", "cannot open file", "%s", path)
+ report("log", 2, "db", "cannot open file %s", path)
return results
end
local incomments = false
@@ -942,8 +1066,6 @@ local function get_os_dirs()
else
local passed_paths = {}
local os_dirs = {}
- -- what about ~/config/fontconfig/fonts.conf etc?
- -- Answer: they should be included by the others, please report if it's not
for _,p in next, {"/usr/local/etc/fonts/fonts.conf", "/etc/fonts/fonts.conf"} do
if lfs.isfile(p) then
read_fonts_conf(p, os_dirs, passed_paths)
@@ -955,6 +1077,7 @@ local function get_os_dirs()
end
local function scan_os_fonts(fontnames, newfontnames)
+ local n_scanned, n_new = 0, 0
--[[
This function scans the OS fonts through
- fontcache for Unix (reads the fonts.conf file and scans the directories)
@@ -963,17 +1086,24 @@ local function scan_os_fonts(fontnames, newfontnames)
report("info", 1, "db", "Scanning OS fonts...")
report("info", 2, "db", "Searching in static system directories...")
for _,d in next, get_os_dirs() do
- scan_dir(d, fontnames, newfontnames, false)
+ local found, new = scan_dir(d, fontnames, newfontnames, false)
+ n_scanned = n_scanned + found
+ n_new = n_new + new
end
+ return n_scanned, n_new
end
+--- dbobj -> bool -> dbobj
update_names = function (fontnames, force)
+ local starttime = os.gettimeofday()
+ local n_scanned, n_new = 0, 0
--[[
The main function, scans everything
- - fontnames is the final table to return
+ - “newfontnames” is the final table to return
- force is whether we rebuild it from scratch or not
]]
- report("info", 1, "db", "Updating the font names database")
+ report("info", 1, "db", "Updating the font names database"
+ .. (force and " forcefully" or ""))
if force then
fontnames = fontnames_init()
@@ -991,16 +1121,29 @@ update_names = function (fontnames, force)
read_blacklist()
--installed_fonts_scanned = false
--scan_installed_fonts(fontnames, newfontnames) --- see fixme above
- scan_texmf_fonts(fontnames, newfontnames)
+ local scanned, new = scan_texmf_fonts(fontnames, newfontnames)
+ n_scanned = n_scanned + scanned
+ n_new = n_new + new
--if not installed_fonts_scanned
--and stringis_empty(kpseexpand_path("$OSFONTDIR"))
if stringis_empty(kpseexpand_path("$OSFONTDIR"))
then
- scan_os_fonts(fontnames, newfontnames)
+ local scanned, new = scan_os_fonts(fontnames, newfontnames)
+ n_scanned = n_scanned + scanned
+ n_new = n_new + new
end
+ --- stats:
+ --- before rewrite | after rewrite
+ --- partial: 804 ms | 701 ms
+ --- forced: 45384 ms | 44714 ms
+ report("info", 1, "db",
+ "Scanned %d font files; %d new entries.", n_scanned, n_new)
+ report("info", 1, "db",
+ "Rebuilt in %0.f ms", 1000*(os.gettimeofday()-starttime))
return newfontnames
end
+--- dbobj -> unit
save_names = function (fontnames)
local path = names.path.dir
if not lfs.isdir(path) then
@@ -1028,8 +1171,9 @@ scan_external_dir = function (dir)
fonts_loaded = true
end
new_names = tablecopy(old_names)
- scan_dir(dir, old_names, new_names)
+ local n_scanned, n_new = scan_dir(dir, old_names, new_names)
names.data = new_names
+ return n_scanned, n_new
end
--- export functionality to the namespace “fonts.names”
diff --git a/luaotfload-features.lua b/luaotfload-features.lua
index 0121ede..78f8256 100644
--- a/luaotfload-features.lua
+++ b/luaotfload-features.lua
@@ -37,6 +37,12 @@ local stringfind = string.find
local stringexplode = string.explode
local stringis_empty = string.is_empty
+--[[doc--
+Apparently, these “modifiers” are another measure of emulating \XETEX,
+cf. “About \XETEX”, by Jonathan Kew, 2005; and
+ “The \XETEX Reference Guide”, by Will Robertson, 2011.
+--doc]]--
+
local supported = {
b = "bold",
i = "italic",
@@ -105,6 +111,8 @@ local defaults = {
},
}
+local global_defaults = { mode = "node" }
+
defaults.beng = defaults.deva
defaults.guru = defaults.deva
defaults.gujr = defaults.deva
@@ -123,22 +131,36 @@ defaults.tibt = defaults.khmr
defaults.lao = defaults.thai
-local function set_default_features(script)
- local features
- local script = script or "dflt"
+--- (string, string) dict -> (string, string) dict
+local set_default_features = function (speclist)
+ local script = speclist.script or "dflt"
+
report("log", 0, "load font",
"auto-selecting default features for script: %s",
script)
- if defaults[script] then
- features = defaults[script]
- else
- features = defaults["dflt"]
+
+ local requested = defaults[script]
+ if not requested then
+ report("log", 0, "load font",
+ "no defaults for script “%s”, falling back to “dflt”",
+ script)
+ requested = defaults.dflt
end
- for _,v in next, features do
- if feature_list[v] ~= false then
- feature_list[v] = true
+
+ for i=1, #requested do
+ local feat = requested[i]
+ if speclist[feat] ~= false then
+ speclist[feat] = true
end
end
+
+ for feat, state in next, global_defaults do
+ --- This is primarily intended for setting node
+ --- mode unless “base” is requested, as stated
+ --- in the manual.
+ if not speclist[feat] then speclist[feat] = state end
+ end
+ return speclist
end
local function issome () feature_list.lookup = 'name' end
@@ -173,7 +195,7 @@ local pattern = (filename + fontname) * subvalue^0 * stylespec^0 * options^0
local function colonized(specification) -- xetex mode
feature_list = { }
lpeg.match(pattern,specification.specification)
- set_default_features(feature_list.script)
+ feature_list = set_default_features(feature_list)
if feature_list.style then
specification.style = feature_list.style
feature_list.style = nil
@@ -213,20 +235,22 @@ end
fonts.definers.registersplit(":",colonized,"cryptic")
fonts.definers.registersplit("", colonized,"more cryptic") -- catches \font\text=[names]
-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
+--- 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
---[[ end included font-ltx.lua ]]
--[[doc--
diff --git a/luaotfload-merged.lua b/luaotfload-merged.lua
index 314305a..da5e35a 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/20/13 13:33:53
+-- merge date : 04/24/13 13:39:43
do -- begin closure to overcome local limits and interference
@@ -2978,7 +2978,9 @@ function resolvers.unresolve(s)
return s
end
caches={}
-local writable,readables=nil,{}
+local writable=nil
+local readables={}
+local usingjit=jit
if not caches.namespace or caches.namespace=="" or caches.namespace=="context" then
caches.namespace='generic'
end
@@ -3038,7 +3040,7 @@ 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),"luc")
+ return file.addsuffix(file.join(path,name),"lua"),file.addsuffix(file.join(path,name),usingjit and "lub" or "luc")
end
end
function caches.is_writable(path,name)
@@ -3085,26 +3087,19 @@ function caches.savedata(path,name,data)
end
end
end
-caches.compilemethod="both"
function caches.compile(data,luaname,lucname)
- local done=false
- if caches.compilemethod=="luac" or caches.compilemethod=="both" then
- done=os.spawn("texluac -o "..string.quoted(lucname).." -s "..string.quoted(luaname))==0
+ local d=io.loaddata(luaname)
+ if not d or d=="" then
+ d=table.serialize(data,true)
end
- if not done and (caches.compilemethod=="dump" or caches.compilemethod=="both") then
- local d=io.loaddata(luaname)
- if not d or d=="" then
- d=table.serialize(data,true)
- end
- if d and d~="" then
- local f=io.open(lucname,'w')
- if f then
- local s=loadstring(d)
- if s then
- f:write(string.dump(s,true))
- end
- f:close()
+ if d and d~="" then
+ local f=io.open(lucname,'w')
+ if f then
+ local s=loadstring(d)
+ if s then
+ f:write(string.dump(s,true))
end
+ f:close()
end
end
end
@@ -6103,6 +6098,14 @@ actions["reorganize lookups"]=function(data,filename,raw)
local current=coverage.current
if current then
current=t_uncover(splitter,t_u_cache,current)
+ local lookups=rule.lookups
+ if lookups then
+ for i=1,#current do
+ if not lookups[i] then
+ lookups[i]=""
+ end
+ end
+ end
rule.current=t_hashed(current,t_h_cache)
end
local after=coverage.after
@@ -10313,6 +10316,819 @@ end -- closure
do -- begin closure to overcome local limits and interference
+if not modules then modules={} end modules ['font-otp']={
+ version=1.001,
+ comment="companion to font-otf.lua (packing)",
+ author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright="PRAGMA ADE / ConTeXt Development Team",
+ license="see context related readme files"
+}
+local next,type=next,type
+local sort,concat=table.sort,table.concat
+local sortedhash=table.sortedhash
+local trace_packing=false trackers.register("otf.packing",function(v) trace_packing=v end)
+local trace_loading=false trackers.register("otf.loading",function(v) trace_loading=v end)
+local report_otf=logs.reporter("fonts","otf loading")
+fonts=fonts or {}
+local handlers=fonts.handlers or {}
+fonts.handlers=handlers
+local otf=handlers.otf or {}
+handlers.otf=otf
+local enhancers=otf.enhancers or {}
+otf.enhancers=enhancers
+local glists=otf.glists or { "gsub","gpos" }
+otf.glists=glists
+local criterium=1
+local threshold=0
+local function tabstr_normal(t)
+ local s={}
+ local n=0
+ for k,v in next,t do
+ n=n+1
+ if type(v)=="table" then
+ s[n]=k..">"..tabstr_normal(v)
+ elseif v==true then
+ s[n]=k.."+"
+ elseif v then
+ s[n]=k.."="..v
+ else
+ s[n]=k.."-"
+ end
+ end
+ if n==0 then
+ return ""
+ elseif n==1 then
+ return s[1]
+ else
+ sort(s)
+ return concat(s,",")
+ end
+end
+local function tabstr_flat(t)
+ local s={}
+ local n=0
+ for k,v in next,t do
+ n=n+1
+ s[n]=k.."="..v
+ end
+ if n==0 then
+ return ""
+ elseif n==1 then
+ return s[1]
+ else
+ sort(s)
+ return concat(s,",")
+ end
+end
+local function tabstr_mixed(t)
+ local s={}
+ local n=#t
+ if n==0 then
+ return ""
+ elseif n==1 then
+ local k=t[1]
+ if k==true then
+ return "++"
+ elseif k==false then
+ return "--"
+ else
+ return tostring(k)
+ end
+ else
+ for i=1,n do
+ local k=t[i]
+ if k==true then
+ s[i]="++"
+ elseif k==false then
+ s[i]="--"
+ else
+ s[i]=k
+ end
+ end
+ return concat(s,",")
+ end
+end
+local function tabstr_boolean(t)
+ local s={}
+ local n=0
+ for k,v in next,t do
+ n=n+1
+ if v then
+ s[n]=k.."+"
+ else
+ s[n]=k.."-"
+ end
+ end
+ if n==0 then
+ return ""
+ elseif n==1 then
+ return s[1]
+ else
+ sort(s)
+ return concat(s,",")
+ end
+end
+local function packdata(data)
+ if data then
+ local h,t,c={},{},{}
+ local hh,tt,cc={},{},{}
+ local nt,ntt=0,0
+ local function pack_normal(v)
+ local tag=tabstr_normal(v)
+ local ht=h[tag]
+ if ht then
+ c[ht]=c[ht]+1
+ return ht
+ else
+ nt=nt+1
+ t[nt]=v
+ h[tag]=nt
+ c[nt]=1
+ return nt
+ end
+ end
+ local function pack_flat(v)
+ local tag=tabstr_flat(v)
+ local ht=h[tag]
+ if ht then
+ c[ht]=c[ht]+1
+ return ht
+ else
+ nt=nt+1
+ t[nt]=v
+ h[tag]=nt
+ c[nt]=1
+ return nt
+ end
+ end
+ local function pack_boolean(v)
+ local tag=tabstr_boolean(v)
+ local ht=h[tag]
+ if ht then
+ c[ht]=c[ht]+1
+ return ht
+ else
+ nt=nt+1
+ t[nt]=v
+ h[tag]=nt
+ c[nt]=1
+ return nt
+ end
+ end
+ local function pack_indexed(v)
+ local tag=concat(v," ")
+ local ht=h[tag]
+ if ht then
+ c[ht]=c[ht]+1
+ return ht
+ else
+ nt=nt+1
+ t[nt]=v
+ h[tag]=nt
+ c[nt]=1
+ return nt
+ end
+ end
+ local function pack_mixed(v)
+ local tag=tabstr_mixed(v)
+ local ht=h[tag]
+ if ht then
+ c[ht]=c[ht]+1
+ return ht
+ else
+ nt=nt+1
+ t[nt]=v
+ h[tag]=nt
+ c[nt]=1
+ return nt
+ end
+ end
+ local function pack_final(v)
+ if c[v]<=criterium then
+ return t[v]
+ else
+ local hv=hh[v]
+ if hv then
+ return hv
+ else
+ ntt=ntt+1
+ tt[ntt]=t[v]
+ hh[v]=ntt
+ cc[ntt]=c[v]
+ return ntt
+ end
+ end
+ end
+ local function success(stage,pass)
+ if nt==0 then
+ if trace_loading or trace_packing then
+ report_otf("pack quality: nothing to pack")
+ end
+ return false
+ elseif nt>=threshold then
+ local one,two,rest=0,0,0
+ if pass==1 then
+ for k,v in next,c do
+ if v==1 then
+ one=one+1
+ elseif v==2 then
+ two=two+1
+ else
+ rest=rest+1
+ end
+ end
+ else
+ for k,v in next,cc do
+ if v>20 then
+ rest=rest+1
+ elseif v>10 then
+ two=two+1
+ else
+ one=one+1
+ end
+ end
+ data.tables=tt
+ end
+ if trace_loading or trace_packing then
+ report_otf("pack quality: stage %s, pass %s, %s packed, 1-10:%s, 11-20:%s, rest:%s (criterium: %s)",stage,pass,one+two+rest,one,two,rest,criterium)
+ end
+ return true
+ else
+ if trace_loading or trace_packing then
+ report_otf("pack quality: stage %s, pass %s, %s packed, aborting pack (threshold: %s)",stage,pass,nt,threshold)
+ end
+ return false
+ end
+ end
+ local function packers(pass)
+ if pass==1 then
+ return pack_normal,pack_indexed,pack_flat,pack_boolean,pack_mixed
+ else
+ return pack_final,pack_final,pack_final,pack_final,pack_final
+ end
+ end
+ local resources=data.resources
+ local lookuptypes=resources.lookuptypes
+ for pass=1,2 do
+ if trace_packing then
+ report_otf("start packing: stage 1, pass %s",pass)
+ end
+ local pack_normal,pack_indexed,pack_flat,pack_boolean,pack_mixed=packers(pass)
+ for unicode,description in next,data.descriptions do
+ local boundingbox=description.boundingbox
+ if boundingbox then
+ description.boundingbox=pack_indexed(boundingbox)
+ end
+ local slookups=description.slookups
+ if slookups then
+ for tag,slookup in next,slookups do
+ local what=lookuptypes[tag]
+ if what=="pair" then
+ local t=slookup[2] if t then slookup[2]=pack_indexed(t) end
+ local t=slookup[3] if t then slookup[3]=pack_indexed(t) end
+ elseif what~="substitution" then
+ slookups[tag]=pack_indexed(slookup)
+ end
+ end
+ end
+ local mlookups=description.mlookups
+ if mlookups then
+ for tag,mlookup in next,mlookups do
+ local what=lookuptypes[tag]
+ if what=="pair" then
+ for i=1,#mlookup do
+ local lookup=mlookup[i]
+ local t=lookup[2] if t then lookup[2]=pack_indexed(t) end
+ local t=lookup[3] if t then lookup[3]=pack_indexed(t) end
+ end
+ elseif what~="substitution" then
+ for i=1,#mlookup do
+ mlookup[i]=pack_indexed(mlookup[i])
+ end
+ end
+ end
+ end
+ local kerns=description.kerns
+ if kerns then
+ for tag,kern in next,kerns do
+ kerns[tag]=pack_flat(kern)
+ end
+ end
+ local math=description.math
+ if math then
+ local kerns=math.kerns
+ if kerns then
+ for tag,kern in next,kerns do
+ kerns[tag]=pack_normal(kern)
+ end
+ end
+ end
+ local anchors=description.anchors
+ if anchors then
+ for what,anchor in next,anchors do
+ if what=="baselig" then
+ for _,a in next,anchor do
+ for k=1,#a do
+ a[k]=pack_indexed(a[k])
+ end
+ end
+ else
+ for k,v in next,anchor do
+ anchor[k]=pack_indexed(v)
+ end
+ end
+ end
+ end
+ end
+ local lookups=data.lookups
+ if lookups then
+ for _,lookup in next,lookups do
+ local rules=lookup.rules
+ if rules then
+ for i=1,#rules do
+ local rule=rules[i]
+ local r=rule.before if r then for i=1,#r do r[i]=pack_boolean(r[i]) end end
+ local r=rule.after if r then for i=1,#r do r[i]=pack_boolean(r[i]) end end
+ local r=rule.current if r then for i=1,#r do r[i]=pack_boolean(r[i]) end end
+ local r=rule.replacements if r then rule.replacements=pack_flat (r) end
+ local r=rule.lookups if r then rule.lookups=pack_indexed(r) end
+ end
+ end
+ end
+ end
+ local anchor_to_lookup=resources.anchor_to_lookup
+ if anchor_to_lookup then
+ for anchor,lookup in next,anchor_to_lookup do
+ anchor_to_lookup[anchor]=pack_normal(lookup)
+ end
+ end
+ local lookup_to_anchor=resources.lookup_to_anchor
+ if lookup_to_anchor then
+ for lookup,anchor in next,lookup_to_anchor do
+ lookup_to_anchor[lookup]=pack_normal(anchor)
+ end
+ end
+ local sequences=resources.sequences
+ if sequences then
+ for feature,sequence in next,sequences do
+ local flags=sequence.flags
+ if flags then
+ sequence.flags=pack_normal(flags)
+ end
+ local subtables=sequence.subtables
+ if subtables then
+ sequence.subtables=pack_normal(subtables)
+ end
+ local features=sequence.features
+ if features then
+ for script,feature in next,features do
+ features[script]=pack_normal(feature)
+ end
+ end
+ end
+ end
+ local lookups=resources.lookups
+ if lookups then
+ for name,lookup in next,lookups do
+ local flags=lookup.flags
+ if flags then
+ lookup.flags=pack_normal(flags)
+ end
+ local subtables=lookup.subtables
+ if subtables then
+ lookup.subtables=pack_normal(subtables)
+ end
+ end
+ end
+ local features=resources.features
+ if features then
+ for _,what in next,glists do
+ local list=features[what]
+ if list then
+ for feature,spec in next,list do
+ list[feature]=pack_normal(spec)
+ end
+ end
+ end
+ end
+ if not success(1,pass) then
+ return
+ end
+ end
+ if nt>0 then
+ for pass=1,2 do
+ if trace_packing then
+ report_otf("start packing: stage 2, pass %s",pass)
+ end
+ local pack_normal,pack_indexed,pack_flat,pack_boolean,pack_mixed=packers(pass)
+ for unicode,description in next,data.descriptions do
+ local kerns=description.kerns
+ if kerns then
+ description.kerns=pack_normal(kerns)
+ end
+ local math=description.math
+ if math then
+ local kerns=math.kerns
+ if kerns then
+ math.kerns=pack_normal(kerns)
+ end
+ end
+ local anchors=description.anchors
+ if anchors then
+ description.anchors=pack_normal(anchors)
+ end
+ local mlookups=description.mlookups
+ if mlookups then
+ for tag,mlookup in next,mlookups do
+ mlookups[tag]=pack_normal(mlookup)
+ end
+ end
+ end
+ local lookups=data.lookups
+ if lookups then
+ for _,lookup in next,lookups do
+ local rules=lookup.rules
+ if rules then
+ for i=1,#rules do
+ local rule=rules[i]
+ local r=rule.before if r then rule.before=pack_normal(r) end
+ local r=rule.after if r then rule.after=pack_normal(r) end
+ local r=rule.current if r then rule.current=pack_normal(r) end
+ end
+ end
+ end
+ end
+ local sequences=resources.sequences
+ if sequences then
+ for feature,sequence in next,sequences do
+ sequence.features=pack_normal(sequence.features)
+ end
+ end
+ if not success(2,pass) then
+ end
+ end
+ for pass=1,2 do
+ local pack_normal,pack_indexed,pack_flat,pack_boolean,pack_mixed=packers(pass)
+ for unicode,description in next,data.descriptions do
+ local slookups=description.slookups
+ if slookups then
+ description.slookups=pack_normal(slookups)
+ end
+ local mlookups=description.mlookups
+ if mlookups then
+ description.mlookups=pack_normal(mlookups)
+ end
+ end
+ end
+ end
+ end
+end
+local unpacked_mt={
+ __index=function(t,k)
+ t[k]=false
+ return k
+ end
+}
+local function unpackdata(data)
+ if data then
+ local tables=data.tables
+ if tables then
+ local resources=data.resources
+ local lookuptypes=resources.lookuptypes
+ local unpacked={}
+ setmetatable(unpacked,unpacked_mt)
+ for unicode,description in next,data.descriptions do
+ local tv=tables[description.boundingbox]
+ if tv then
+ description.boundingbox=tv
+ end
+ local slookups=description.slookups
+ if slookups then
+ local tv=tables[slookups]
+ if tv then
+ description.slookups=tv
+ slookups=unpacked[tv]
+ end
+ if slookups then
+ for tag,lookup in next,slookups do
+ local what=lookuptypes[tag]
+ if what=="pair" then
+ local tv=tables[lookup[2]]
+ if tv then
+ lookup[2]=tv
+ end
+ local tv=tables[lookup[3]]
+ if tv then
+ lookup[3]=tv
+ end
+ elseif what~="substitution" then
+ local tv=tables[lookup]
+ if tv then
+ slookups[tag]=tv
+ end
+ end
+ end
+ end
+ end
+ local mlookups=description.mlookups
+ if mlookups then
+ local tv=tables[mlookups]
+ if tv then
+ description.mlookups=tv
+ mlookups=unpacked[tv]
+ end
+ if mlookups then
+ for tag,list in next,mlookups do
+ local tv=tables[list]
+ if tv then
+ mlookups[tag]=tv
+ list=unpacked[tv]
+ end
+ if list then
+ local what=lookuptypes[tag]
+ if what=="pair" then
+ for i=1,#list do
+ local lookup=list[i]
+ local tv=tables[lookup[2]]
+ if tv then
+ lookup[2]=tv
+ end
+ local tv=tables[lookup[3]]
+ if tv then
+ lookup[3]=tv
+ end
+ end
+ elseif what~="substitution" then
+ for i=1,#list do
+ local tv=tables[list[i]]
+ if tv then
+ list[i]=tv
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+ local kerns=description.kerns
+ if kerns then
+ local tm=tables[kerns]
+ if tm then
+ description.kerns=tm
+ kerns=unpacked[tm]
+ end
+ if kerns then
+ for k,kern in next,kerns do
+ local tv=tables[kern]
+ if tv then
+ kerns[k]=tv
+ end
+ end
+ end
+ end
+ local math=description.math
+ if math then
+ local kerns=math.kerns
+ if kerns then
+ local tm=tables[kerns]
+ if tm then
+ math.kerns=tm
+ kerns=unpacked[tm]
+ end
+ if kerns then
+ for k,kern in next,kerns do
+ local tv=tables[kern]
+ if tv then
+ kerns[k]=tv
+ end
+ end
+ end
+ end
+ end
+ local anchors=description.anchors
+ if anchors then
+ local ta=tables[anchors]
+ if ta then
+ description.anchors=ta
+ anchors=unpacked[ta]
+ end
+ if anchors then
+ for tag,anchor in next,anchors do
+ if tag=="baselig" then
+ for _,list in next,anchor do
+ for i=1,#list do
+ local tv=tables[list[i]]
+ if tv then
+ list[i]=tv
+ end
+ end
+ end
+ else
+ for a,data in next,anchor do
+ local tv=tables[data]
+ if tv then
+ anchor[a]=tv
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+ local lookups=data.lookups
+ if lookups then
+ for _,lookup in next,lookups do
+ local rules=lookup.rules
+ if rules then
+ for i=1,#rules do
+ local rule=rules[i]
+ local before=rule.before
+ if before then
+ local tv=tables[before]
+ if tv then
+ rule.before=tv
+ before=unpacked[tv]
+ end
+ if before then
+ for i=1,#before do
+ local tv=tables[before[i]]
+ if tv then
+ before[i]=tv
+ end
+ end
+ end
+ end
+ local after=rule.after
+ if after then
+ local tv=tables[after]
+ if tv then
+ rule.after=tv
+ after=unpacked[tv]
+ end
+ if after then
+ for i=1,#after do
+ local tv=tables[after[i]]
+ if tv then
+ after[i]=tv
+ end
+ end
+ end
+ end
+ local current=rule.current
+ if current then
+ local tv=tables[current]
+ if tv then
+ rule.current=tv
+ current=unpacked[tv]
+ end
+ if current then
+ for i=1,#current do
+ local tv=tables[current[i]]
+ if tv then
+ current[i]=tv
+ end
+ end
+ end
+ end
+ local replacements=rule.replacements
+ if replacements then
+ local tv=tables[replacements]
+ if tv then
+ rule.replacements=tv
+ end
+ end
+ local fore=rule.fore
+ if fore then
+ local tv=tables[fore]
+ if tv then
+ rule.fore=tv
+ end
+ end
+ local back=rule.back
+ if back then
+ local tv=tables[back]
+ if tv then
+ rule.back=tv
+ end
+ end
+ local names=rule.names
+ if names then
+ local tv=tables[names]
+ if tv then
+ rule.names=tv
+ end
+ end
+ local lookups=rule.lookups
+ if lookups then
+ local tv=tables[lookups]
+ if tv then
+ rule.lookups=tv
+ end
+ end
+ end
+ end
+ end
+ end
+ local anchor_to_lookup=resources.anchor_to_lookup
+ if anchor_to_lookup then
+ for anchor,lookup in next,anchor_to_lookup do
+ local tv=tables[lookup]
+ if tv then
+ anchor_to_lookup[anchor]=tv
+ end
+ end
+ end
+ local lookup_to_anchor=resources.lookup_to_anchor
+ if lookup_to_anchor then
+ for lookup,anchor in next,lookup_to_anchor do
+ local tv=tables[anchor]
+ if tv then
+ lookup_to_anchor[lookup]=tv
+ end
+ end
+ end
+ local ls=resources.sequences
+ if ls then
+ for _,feature in next,ls do
+ local flags=feature.flags
+ if flags then
+ local tv=tables[flags]
+ if tv then
+ feature.flags=tv
+ end
+ end
+ local subtables=feature.subtables
+ if subtables then
+ local tv=tables[subtables]
+ if tv then
+ feature.subtables=tv
+ end
+ end
+ local features=feature.features
+ if features then
+ local tv=tables[features]
+ if tv then
+ feature.features=tv
+ features=unpacked[tv]
+ end
+ if features then
+ for script,data in next,features do
+ local tv=tables[data]
+ if tv then
+ features[script]=tv
+ end
+ end
+ end
+ end
+ end
+ end
+ local lookups=resources.lookups
+ if lookups then
+ for _,lookup in next,lookups do
+ local flags=lookup.flags
+ if flags then
+ local tv=tables[flags]
+ if tv then
+ lookup.flags=tv
+ end
+ end
+ local subtables=lookup.subtables
+ if subtables then
+ local tv=tables[subtables]
+ if tv then
+ lookup.subtables=tv
+ end
+ end
+ end
+ end
+ local features=resources.features
+ if features then
+ for _,what in next,glists do
+ local feature=features[what]
+ if feature then
+ for tag,spec in next,feature do
+ local tv=tables[spec]
+ if tv then
+ feature[tag]=tv
+ end
+ end
+ end
+ end
+ end
+ data.tables=nil
+ end
+ end
+end
+if otf.enhancers.register then
+ otf.enhancers.register("pack",packdata)
+ otf.enhancers.register("unpack",unpackdata)
+end
+otf.enhancers.unpack=unpackdata
+
+end -- closure
+
+do -- begin closure to overcome local limits and interference
+
if not modules then modules={} end modules ['luatex-fonts-lua']={
version=1.001,
comment="companion to luatex-*.tex",
diff --git a/luaotfload-override.lua b/luaotfload-override.lua
index 94f2376..4953edf 100644
--- a/luaotfload-override.lua
+++ b/luaotfload-override.lua
@@ -72,6 +72,9 @@ logs.names_report = function (mode, lvl, ...)
if loglevel > lvl then
if mode == "log" then
log (...)
+ elseif mode == "both" then
+ log (...)
+ stdout (...)
else
stdout (...)
end
diff --git a/luaotfload.dtx b/luaotfload.dtx
index dbc822f..18e01d8 100644
--- a/luaotfload.dtx
+++ b/luaotfload.dtx
@@ -870,6 +870,7 @@ and the derived files
% \incitem{font-otf.lua} \incitem{font-otb.lua}
% \incitem{node-inj.lua} \incitem{font-ota.lua}
% \incitem{font-otn.lua} \incitem{font-def.lua}
+% \incitem{font-otp.lua}
% \end{itemize}
% \end{multicols}
% \end{itemize}
@@ -907,21 +908,20 @@ and the derived files
% \normalitem{\fileent{#1}}%
% \space--\hskip1em
% }
-% \ouritem {luaotfload-font-otc.lua} \fileent{font-otc} from \CONTEXT;
-% font feature handling.
-% \ouritem {luaotfload-lib-dir.lua} \fileent{l-dir} from \CONTEXT;
-% contains functionality required
-% by \fileent{luaotfload-font-nms.lua}.
-% \ouritem {luaotfload-luat-ovr.lua} overrides the \CONTEXT logging
-% functionality.
-% \ouritem {luaotfload-font-pfb.lua} registers the \OpenType
-% font reader as handler for
-% Postscript fonts.
-% \ouritem {luaotfload-font-nms.lua} font database.
-% \ouritem {luaotfload-font-clr.lua} color handling.
-% \ouritem {luaotfload-font-ltx.lua} font feature handling.
-% \ouritem {luaotfload-features.lua} definitions of the \verb|anum| and
-% \verb|tlig| features.
+% \ouritem {luaotfload-features.lua} font feature handling;
+% incorporates some of the code from
+% \fileent{font-otc} from \CONTEXT;
+% \ouritem {luaotfload-lib-dir.lua} \fileent{l-dir} from \CONTEXT;
+% contains functionality required
+% by \fileent{luaotfload-font-nms.lua}.
+% \ouritem {luaotfload-override.lua} overrides the \CONTEXT logging
+% functionality.
+% \ouritem {luaotfload-loaders.lua} registers the \OpenType
+% font reader as handler for
+% Postscript fonts
+% (\abbrev{pfa}, \abbrev{pfb}).
+% \ouritem {luaotfload-database.lua} font names database.
+% \ouritem {luaotfload-colors.lua} color handling.
% \end{itemize}
%
% \begin{figure}[b]
@@ -936,6 +936,10 @@ and the derived files
% version of this package before reporting a bug, as
% \identifier{luaotfload} is under active development and still a
% moving target.
+% The development takes place on \identifier{github} at
+% \url{https://github.com/lualatex/luaotfload} where there is an issue
+% tracker for submitting bug reports, feature requests and the likes
+% requests and the likes.
%
% Errors during database generation can be traced by increasing
% verbosity levels and redirecting log output to \fileent{stdout}:
@@ -1038,13 +1042,19 @@ local error, warning, info, log =
luatexbase.provides_module(luaotfload.module)
% \end{macrocode}
-%
-% We set the minimum version requirement for \LUATEX to v0.74, as it was
-% the first version to include version 5.2 of the \LUA interpreter.
+% We set the minimum version requirement for \LUATEX to v0.76,
+% because the font loader requires recent features like direct
+% attribute indexing and \luafunction{node.end_of_math()} that aren’t
+% available in earlier versions.\footnote{%
+% See Taco’s announcement of v0.76:
+% \url{http://comments.gmane.org/gmane.comp.tex.luatex.user/4042}
+% and this commit by Hans that introduced those features.
+% \url{http://repo.or.cz/w/context.git/commitdiff/a51f6cf6ee087046a2ae5927ed4edff0a1acec1b}.
+% }
%
% \begin{macrocode}
-local luatex_version = 74
+local luatex_version = 76
if tex.luatexversion < luatex_version then
warning("LuaTeX v%.2f is old, v%.2f is recommended.",
@@ -1107,7 +1117,6 @@ end
% \fileent{luaotfload-merged.lua}.
% If this file cannot be found, the original libraries from \CONTEXT of
% which the merged code was composed are loaded instead.
-%
% The imported font loader will call \luafunction{callback.register} once
% while reading \fileent{font-def.lua}.
% This is unavoidable unless we modify the imported files, but harmless
@@ -1265,6 +1274,7 @@ else--- the loading sequence is known to change, so this might have to
loadmodule('node-inj.lua')
loadmodule('font-ota.lua')
loadmodule('font-otn.lua')
+ loadmodule('font-otp.lua')--- since 2013-04-23
loadmodule('luatex-fonts-lua.lua')
loadmodule('font-def.lua')
loadmodule('luatex-fonts-def.lua')
@@ -1318,6 +1328,8 @@ add_to_callback("find_vf_file",
loadmodule"lib-dir.lua" --- required by luaofload-database.lua
loadmodule"override.lua" --- “luat-ovr”
+logs.set_loglevel(0)
+
% \end{macrocode}
% \CONTEXT does not support ofm, these lines were added in order to make it
% work. However they do not seem necessary so they are commented for now.
@@ -1339,9 +1351,17 @@ 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 kpse. It is necessary in case
-% of fonts that are not accessible by kpse but present in the database, a
-% quite common case under Linux.
+% 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.
%
% \begin{macrocode}
@@ -1389,7 +1409,7 @@ local define_font_wrapper = function (...)
--- definers.read
if stringfind(k, "Percent") then
-- keep percent values as is
- print(k,v)
+ --print(k,v)
mathconstants[k] = v
else
mathconstants[k] = v / units_per_em * size
@@ -1415,8 +1435,10 @@ end
% \begin{macrocode}
local read_font_file = fonts.definers.read
-local patch_defined_font = function (...)
- local tfmdata = read_font_file(...)-- spec -> size -> id -> tmfdata
+
+--- spec -> size -> id -> tmfdata
+local patch_defined_font = function (specification, size, id)
+ local tfmdata = read_font_file(specification, size, id)
if type(tfmdata) == "table" then
call_callback("luaotfload.patch_font", tfmdata)
end
@@ -1486,7 +1508,6 @@ loadmodule"features.lua" --- contains what was “font-ltx” and “font-otc”
-- vim:tw=71:sw=4:ts=4:expandtab
-
% \end{macrocode}
%
% \iffalse
diff --git a/tests/lookups.tex b/tests/lookups.tex
index db26312..8b03d8e 100644
--- a/tests/lookups.tex
+++ b/tests/lookups.tex
@@ -5,10 +5,16 @@
\font\second=file:antpoltltsemiexpd-bolditalic.otf at 42pt
%% lookup font by name, with style in slash notation
\font\third={name:Antykwa torunska/I} at 42pt
+%% unspecified lookup; in definers.read:
+%% - first it falls back to “file”
+%% - empty “method” field triggers fallback to “name”
+%% - names.resolve -> kpse.lookup -> hit!
+\font\fourth=iwona at 42pt
{\first foo \endgraf}
{\second bar \endgraf}
{\third baz \endgraf}
+{\fourth xyzzy \endgraf}
\bye