summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-database.lua
diff options
context:
space:
mode:
Diffstat (limited to 'Master/texmf-dist/tex/luatex/luaotfload/luaotfload-database.lua')
-rw-r--r--Master/texmf-dist/tex/luatex/luaotfload/luaotfload-database.lua579
1 files changed, 272 insertions, 307 deletions
diff --git a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-database.lua b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-database.lua
index b1d264f9b78..8f0ef41fc73 100644
--- a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-database.lua
+++ b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-database.lua
@@ -5,8 +5,8 @@
do -- block to avoid to many local variables error
assert(luaotfload_module, "This is a part of luaotfload and should not be loaded independently") {
name = "luaotfload-database",
- version = "3.18", --TAGVERSION
- date = "2021-05-21", --TAGDATE
+ version = "3.19", --TAGVERSION
+ date = "2022-02-24", --TAGDATE
description = "luaotfload submodule / database",
license = "GPL v2.0",
author = "Khaled Hosny, Elie Roux, Philipp Gesang, Marcel Krüger",
@@ -142,8 +142,7 @@ do
end
local otfhandler = fonts.handlers.otf or { }
-local gzipload = gzip.load
-local gzipsave = gzip.save
+local gzipopen = gzip.open
local iolines = io.lines
local ioopen = io.open
local kpseexpand_path = kpse.expand_path
@@ -159,6 +158,7 @@ local mathmin = math.min
local osgetenv = os.getenv
local osgettimeofday = os.gettimeofday
local osremove = os.remove
+local dump = string.dump
local stringfind = string.find
local stringformat = string.format
local stringgmatch = string.gmatch
@@ -486,42 +486,47 @@ local function initialize_namedata (formats, created)
}
end
+-- A helper to load a file which might be gziped
+local function load_maybe_gzip (path, binary)
+ local gzippath = path .. '.gz'
+ local f = ioopen (gzippath, 'rb')
+ if f then
+ path = gzippath
+ local result = f:read'*a'
+ result = gzip.decompress(result, 31)
+ f:close()
+ return path, result
+ else
+ f, msg = ioopen (path, binary and 'rb' or 'r')
+ if f then
+ local result = f:read'*a'
+ f:close()
+ return path, result
+ end
+ end
+
+ return nil, msg
+end
+
--- When loading a lua file we try its binary complement first, which
--- is assumed to be located at an identical path, carrying the suffix
--- .luc.
---- string -> (string * table)
-local function load_lua_file (path)
- local foundname = filereplacesuffix (path, "luc")
- local code = nil
-
- local fh = ioopen (foundname, "rb") -- try bin first
- if fh then
- local chunk = fh:read"*all"
- fh:close()
- code = load (chunk, "b")
- end
-
- if not code then --- fall back to text file
- foundname = filereplacesuffix (path, "lua")
- fh = ioopen(foundname, "rb")
- if fh then
- local chunk = fh:read"*all"
- fh:close()
- code = load (chunk, "t")
- end
- end
-
- if not code then --- probe gzipped file
- foundname = filereplacesuffix (path, "lua.gz")
- local chunk = gzipload (foundname)
- if chunk then
- code = load (chunk, "t")
+--- string -> string -> (string * table)
+local function load_lua_file (path_lua, path_luc)
+ local foundname, chunk = load_maybe_gzip (path_luc, true)
+ if foundname then
+ chunk = assert (load (chunk, 'b'))
+ else
+ foundname, chunk = load_maybe_gzip (path_lua, false)
+ if foundname then
+ chunk = assert (load (chunk, 't'))
+ else
+ return nil
end
end
- if not code then return nil, nil end
- return foundname, code ()
+ return foundname, chunk()
end
--- define locals in scope
@@ -543,7 +548,8 @@ local fuzzy_limit = 1 --- display closest only
--- bool? -> -> bool? -> dbobj option
local function load_names (dry_run, no_rebuild)
local starttime = osgettimeofday ()
- local foundname, data = load_lua_file (config.luaotfload.paths.index_path_lua)
+ local foundname, data = load_lua_file (config.luaotfload.paths.index_path_lua,
+ config.luaotfload.paths.index_path_luc)
if data then
logreport ("log", 0, "db",
@@ -624,7 +630,8 @@ end
--- unit -> unit
local function load_lookups ( )
- local foundname, data = load_lua_file(config.luaotfload.paths.lookup_path_lua)
+ local foundname, data = load_lua_file(config.luaotfload.paths.lookup_path_lua,
+ config.luaotfload.paths.lookup_path_luc)
if data then
logreport ("log", 0, "cache", "Lookup cache loaded from %s.", foundname)
logreport ("term", 3, "cache",
@@ -638,11 +645,12 @@ local function load_lookups ( )
end
local regular_synonym = {
- book = true,
- normal = true,
- plain = true,
- regular = true,
- roman = true,
+ book = 0,
+ normal = 0,
+ plain = 0,
+ regular = 0,
+ roman = 0,
+ medium = 1000, -- Some font authors haven't got the memo that medium is not regular
}
local style_synonym = {
@@ -1577,8 +1585,6 @@ local function organize_namedata (rawinfo,
or nametable.family
or rawinfo.familyname
or info.familyname
--- local default_modifier = nametable.typographicsubfamily
--- or nametable.subfamily
local fontnames = {
--- see
--- https://developer.apple.com/fonts/TTRefMan/RM06/Chap6name.html
@@ -1642,7 +1648,7 @@ local function split_fontname (fontname)
end
end
-local function organize_styledata (metadata, rawinfo, info)
+local function organize_styledata (metadata, rawinfo)
local pfminfo = metadata.pfminfo
local names = rawinfo.names
return {
@@ -1686,8 +1692,7 @@ local function ot_fullinfo (filename,
basename,
info)
local style = organize_styledata (metadata,
- rawinfo,
- info)
+ rawinfo)
local res = {
file = { base = basename,
full = filename,
@@ -1929,7 +1934,7 @@ local function read_font_names (fullname,
if insert_fullinfo (fullname, basename, n_font,
loader, format, location,
targetmappings, targetentrystatus,
- info)
+ info[n_font])
then
success = true
end
@@ -2368,7 +2373,7 @@ local function collect_font_filenames_texmf ()
fontdirs = fontdirs .. path_separator .. expanded_path "type1 fonts"
fontdirs = fontdirs .. path_separator .. expanded_path "afm"
- fontdirs = filesplitpath (fontdirs) or { }
+ fontdirs = filesplitpath (fontdirs) or { }
local tasks = filter_out_pwd (fontdirs)
logreport ("both", 3, "db",
@@ -2725,95 +2730,51 @@ local function generate_filedata (mappings)
return files
end
-local bold_spectrum_low = 501 --- 500 is medium, 900 heavy/black
-local normal_weight = 400
-local bold_weight = 700
-local normal_width = 5
-
-local pick_style
-local pick_fallback_style
-local check_regular
-
-do
- function pick_style (typographicsubfamily, subfamily)
- return style_synonym [typographicsubfamily or subfamily or ""]
- end
-
- function pick_fallback_style (italicangle, pfmweight, width)
- --[[--
- More aggressive, but only to determine bold faces.
- Note: Before you make this test more inclusive, ensure
- no fonts are matched in the bold synonym spectrum over
- a literally “bold[italic]” one. In the past, heuristics
- been tried but ultimately caused unwanted modifiers
- polluting the lookup table. What doesn’t work is, e. g.
- treating weights > 500 as bold or allowing synonyms like
- “heavy”, “black”.
- --]]--
- if width == normal_width then
- if pfmweight == bold_weight then
- --- bold spectrum matches
- if italicangle == 0 then
- return "b"
- end
- return "bi"
- elseif pfmweight == normal_weight then
- if italicangle ~= 0 then
- return "i"
- end
- end
- return tostring(pfmweight) .. (italicangle == 0 and "" or "i")
- end
- return false
- end
-
- --- we use only exact matches here since there are constructs
- --- like “regularitalic” (Cabin, Bodoni Old Fashion)
-
- function check_regular (typographicsubfamily,
- subfamily,
- italicangle,
- weight,
- width,
- pfmweight)
- local plausible_weight = false
- --[[--
- This filters out undesirable candidates that specify their
- typographicsubfamily or subfamily as “regular” but are actually of
- “semibold” or other weight—another drawback of the
- oversimplifying classification into only three styles (r, i,
- b, bi).
- --]]--
- if italicangle == 0 then
- if pfmweight == 400 then
- --[[--
- Some fonts like Dejavu advertise an undistinguished
- regular and a “condensed” version with the same
- weight whilst also providing the style info in the
- typographic subfamily instead of the subfamily (i. e.
- the converse of what Adobe’s doing). The only way to
- weed out the undesired pseudo-regular shape is to
- peek at its advertised width (4 vs. 5).
- --]]--
- if width then
- plausible_weight = width == normal_width
- else
- plausible_weight = true
- end
- elseif weight and regular_synonym [weight] then
- plausible_weight = true
- end
- end
-
- if plausible_weight then
- if subfamily then
- if regular_synonym [subfamily] then return "r" end
- elseif typographicsubfamily then
- if regular_synonym [typographicsubfamily] then return "r" end
- end
- end
- return false
- end
+local function regular_score(entry)
+ return 10000 * (entry.italicangle or 0)^2 -- We really don't want italic fonts here (italic font have an angle around 10)
+ + .01 * ((entry.pfmweight or 400) - 400)^2 -- weights are normally multiples of 100, so they are still quite large after .01
+ + ((entry.width or 5) - 5)^2
+ + (regular_synonym[entry.subfamily or entry.typographicsubfamily] or 1000000)
+ + (entry.pfmweight > 500 and 1000 or 0)
+end
+local function italic_score(entry, regular_entry)
+ local regularangle = regular_entry.italicangle or 0
+ local angle = entry.italicangle or 0
+ if (angle == 0 or angle == regularangle)
+ and (entry.subfamily == regular_entry.subfamily or style_synonym[entry.subfamily] ~= 'i') then
+ return -- This font is not italic in any way
+ end
+ return .1 * (angle - regularangle - 10)^2 -- Should there ever be multiple levels of italicness...
+ + 0.1 * ((entry.pfmweight or 400) - (regular_entry.pfmweight or 400))^2 -- weights are normally multiples of 100, so they are still quite large after .01
+ + ((entry.width or 5) - regular_entry.width)^2
+ + (style_synonym[entry.subfamily or entry.typographicsubfamily] == 'i' and 0 or 1000000)
+end
+local function bold_score(entry, regular_entry)
+ local regularweight = regular_entry.pfmweight or 400
+ local weight = entry.pfmweight
+ if weight < regularweight + 100
+ and (entry.subfamily == regular_entry.subfamily or style_synonym[entry.subfamily] ~= 'b') then
+ return -- This font is not bold in any way
+ end
+ return 10000 * (entry.italicangle or 0)^2 -- We really don't want italic fonts here (italic font have an angle around 10)
+ + .01 * ((entry.pfmweight or 400) - (regularweight + 200))^2 -- weights are normally multiples of 100, so they are still quite large after .01
+ + ((entry.width or 5) - regular_entry.width)^2
+ + (style_synonym[entry.subfamily or entry.typographicsubfamily] == 'b' and 0 or 1000000)
+ + (entry.pfmweight > 500 and 0 or 10000)
+end
+local function bolditalic_score(entry, bold_entry, italic_entry)
+ local italicangle = italic_entry.italicangle or 0
+ local angle = entry.italicangle or 0
+ local boldweight = bold_entry.pfmweight or 400
+ local weight = entry.pfmweight or 400
+ if (angle == 0 or weight < boldweight)
+ and (entry.subfamily == bold_entry.subfamily or entry.subfamily == italic_entry.subfamily or style_synonym[entry.subfamily] ~= 'bi') then
+ return -- This font is not italic in any way
+ end
+ return 100 * (angle - italicangle)^2
+ + (weight - boldweight)^2
+ + ((entry.width or 5) - bold_entry.width)^2
+ + (style_synonym[entry.subfamily or entry.typographicsubfamily] == 'bi' and 0 or 1000000)
end
local function pull_values (entry)
@@ -2854,7 +2815,7 @@ local function pull_values (entry)
end
end
-local function add_family (name, subtable, modifier, entry)
+local function add_family (name, subtable, entry)
if not name then --- probably borked font
return
end
@@ -2866,22 +2827,9 @@ local function add_family (name, subtable, modifier, entry)
familytable [#familytable + 1] = {
index = entry.index,
- modifier = modifier,
}
end
-local function add_lastresort_regular (name, subtable, entry)
- if not name then --- probably borked font
- return
- end
- local familytable = subtable [name]
- if not familytable then
- familytable = { }
- subtable [name] = familytable
- end
- familytable.fallback = entry.index
-end
-
local function get_subtable (families, entry)
local location = entry.location
local format = entry.format
@@ -2919,27 +2867,7 @@ local function collect_families (mappings)
local width = entry.width
local pfmweight = entry.pfmweight
local italicangle = entry.italicangle
- local modifier = pick_style (typographicsubfamily, subfamily)
-
- if not modifier then --- regular, exact only
- modifier = check_regular (typographicsubfamily,
- subfamily,
- italicangle,
- weight,
- width,
- pfmweight)
- end
-
- if not modifier then
- modifier = pick_fallback_style (italicangle, pfmweight, width)
- end
-
- if modifier then
- add_family (familyname, subtable, modifier, entry)
- end
- if modifier ~= 'r' and regular_synonym[typographicsubfamily or subfamily or ''] then
- add_lastresort_regular (familyname, subtable, entry)
- end
+ add_family (familyname, subtable, entry)
end
collectgarbage "collect"
@@ -2983,99 +2911,135 @@ end
local style_categories = { "r", "b", "i", "bi" }
local bold_categories = { "b", "bi" }
+local huge = math.huge
local function group_modifiers (mappings, families)
logreport ("info", 2, "db", "Analyzing shapes, weights, and styles.")
for location, location_data in next, families do
for format, format_data in next, location_data do
for familyname, collected in next, format_data do
- local styledata = { } --- will replace the “collected” table
- local lastresort_regular = collected.fallback
- collected.fallback = nil
- --- First, fill in the ordinary style data that
- --- fits neatly into the four relevant modifier
- --- categories.
- for _, modifier in next, style_categories do
- local entries
- for key, info in next, collected do
- if info.modifier == modifier then
- if not entries then
- entries = { }
- end
- local index = info.index
- local entry = mappings [index]
- local size = entry.size
+ local best_score = huge
+ local best_match
+ for i=1,#collected do
+ local v = collected[i]
+ local entry = mappings[v.index]
+ local score = regular_score(entry)
+ if score <= best_score then
+ v.prev = best_score == score and best_match or nil
+ best_score = score
+ best_match = v
+ end
+ end
+ local regular = {}
+ repeat
+ local index = best_match.index
+ local entry = mappings[index]
+ local size = entry.size
+ if size then
+ regular [#regular + 1] = {
+ size [1],
+ size [2],
+ size [3],
+ index,
+ }
+ else
+ regular.default = index
+ end
+ best_match = best_match.prev
+ until not best_match
+ local regular_entry = mappings[regular.default or regular[1][4]]
+ local best_match_i, best_match_b
+ local best_score_i, best_score_b = 10000000000, 10000000000
+ for i=1,#collected do
+ local v = collected[i]
+ local entry = mappings[v.index]
+ local score_i = italic_score(entry, regular_entry)
+ local score_b = bold_score(entry, regular_entry)
+ if score_i and score_i <= best_score_i then
+ v.prev = best_score_i == score_i and best_match_i or nil
+ best_score_i = score_i
+ best_match_i = v
+ end
+ if score_b and score_b <= best_score_b then
+ v.prev = best_score_b == score_b and best_match_b or nil
+ best_score_b = score_b
+ best_match_b = v
+ end
+ end
+ local italic, bold
+ if best_match_i then
+ italic = {}
+ repeat
+ local index = best_match_i.index
+ local entry = mappings[index]
+ local size = entry.size
+ if size then
+ italic [#italic + 1] = {
+ size [1],
+ size [2],
+ size [3],
+ index,
+ }
+ else
+ italic.default = index
+ end
+ best_match_i = best_match_i.prev
+ until not best_match_i
+ end
+ if best_match_b then
+ bold = {}
+ repeat
+ local index = best_match_b.index
+ local entry = mappings[index]
+ local size = entry.size
+ if size then
+ bold [#bold + 1] = {
+ size [1],
+ size [2],
+ size [3],
+ index,
+ }
+ else
+ bold.default = index
+ end
+ best_match_b = best_match_b.prev
+ until not best_match_b
+ end
+ local bolditalic
+ if bold and italic then
+ best_score = 1000000000000
+ local bold_entry = mappings[bold.default or bold[1][4]]
+ local italic_entry = mappings[italic.default or italic[1][4]]
+ for i=1,#collected do
+ local v = collected[i]
+ local entry = mappings[v.index]
+ local score = bolditalic_score(entry, bold_entry, italic_entry)
+ if score and score <= best_score then
+ v.prev = best_score == score and best_match or nil
+ best_score = score
+ best_match = v
+ end
+ end
+ if best_match then
+ bolditalic = {}
+ repeat
+ local index = best_match.index
+ local entry = mappings[index]
+ local size = entry.size
if size then
- entries [#entries + 1] = {
+ bolditalic [#bolditalic + 1] = {
size [1],
size [2],
size [3],
index,
}
else
- entries.default = index
+ bolditalic.default = index
end
- collected [key] = nil
- end
- styledata [modifier] = entries
+ best_match = best_match.prev
+ until not best_match
end
end
- if not styledata.r and lastresort_regular then
- styledata.r = {default = lastresort_regular}
- end
-
- --- At this point the family set may still lack
- --- entries for bold or bold italic. We will fill
- --- those in using the modifier with the numeric
- --- weight that is closest to bold (700).
- if next (collected) then --- there are uncategorized entries
- for _, modifier in next, bold_categories do
- if not styledata [modifier] then
- local closest
- local minimum = 2^51
- for key, info in next, collected do
- local info_modifier = tonumber (info.modifier) and "b" or "bi"
- if modifier == info_modifier then
- local index = info.index
- local entry = mappings [index]
- local weight = entry.pfmweight
- local diff = weight < 700 and 700 - weight or weight - 700
- if weight > 500 and diff < minimum then
- minimum = diff
- closest = weight
- end
- end
- end
- if closest then
- --- We know there is a substitute face for the modifier.
- --- Now we scan the list again to extract the size data
- --- in case the shape is available at multiple sizes.
- local entries = { }
- for key, info in next, collected do
- local info_modifier = tonumber (info.modifier) and "b" or "bi"
- if modifier == info_modifier then
- local index = info.index
- local entry = mappings [index]
- local size = entry.size
- if entry.pfmweight == closest then
- if size then
- entries [#entries + 1] = {
- size [1],
- size [2],
- size [3],
- index,
- }
- else
- entries.default = index
- end
- end
- end
- end
- styledata [modifier] = entries
- end
- end
- end
- end
- format_data [familyname] = styledata
+ format_data [familyname] = { r = regular, b = bold, i = italic, bi = bolditalic }
end
end
end
@@ -3514,28 +3478,62 @@ function update_names (currentnames, force, dry_run)
return targetnames
end
+local function compress_dummy(data) return data end
+--- string -> string -> (string * table)
+local function save_lua_table (data, path_lua, path_luc, compress)
+ if compress then
+ osremove(path_lua)
+ osremove(path_luc)
+ path_lua, path_luc = path_lua .. '.gz', path_luc .. '.gz'
+ else
+ osremove(path_lua .. '.gz')
+ osremove(path_luc .. '.gz')
+ end
+ local file_lua, msg = ioopen(path_lua, compress and 'wb' or 'w')
+ if not file_lua then
+ logreport ("info", 0, "cache", "Failed to write %q: %s", path_lua, msg)
+ end
+ local file_luc file_luc, msg = ioopen(path_luc, 'wb')
+ if not file_luc then
+ logreport ("info", 0, "cache", "Failed to write %q: %s", path_luc, msg)
+ end
+ if not (file_lua or file_luc) then
+ return
+ end
+ -- If we can only write one of the files, try to remove the other
+ -- one to avoid them beiing inconsistent. This will probably fail,
+ -- but in some situations we might be allowed to delete a file we
+ -- can't write to.
+ if not file_lua then
+ osremove(path_lua)
+ elseif not file_luc then
+ osremove(path_luc)
+ end
+ local serialized = tableserialize (data, true)
+ if file_lua then
+ file_lua:write(compress and gzip.compress(serialized, 31) or serialized)
+ file_lua:close()
+ end
+ if file_luc then
+ local compiled = dump(assert(load(serialized, 't')), true)
+ file_luc:write(compress and gzip.compress(compiled, 31) or compiled)
+ file_luc:close()
+ end
+ -- Even if we could write one file but not the other one it's still an
+ -- error since reloading is then unreliable.
+ return file_lua and path_lua or nil, file_luc and path_luc or nil
+end
+
--- unit -> bool
function save_lookups ( )
local paths = config.luaotfload.paths
local luaname, lucname = paths.lookup_path_lua, paths.lookup_path_luc
- if fileiswritable (luaname) and fileiswritable (lucname) then
- tabletofile (luaname, lookup_cache, true)
- osremove (lucname)
- caches.compile (lookup_cache, luaname, lucname)
- --- double check ...
- if lfsisfile (luaname) and lfsisfile (lucname) then
- logreport ("both", 3, "cache", "Lookup cache saved.")
- return true
- end
- logreport ("info", 0, "cache", "Could not compile lookup cache.")
- return false
- end
- logreport ("info", 0, "cache", "Lookup cache file not writable.")
- if not fileiswritable (luaname) then
- logreport ("info", 0, "cache", "Failed to write %s.", luaname)
- end
- if not fileiswritable (lucname) then
- logreport ("info", 0, "cache", "Failed to write %s.", lucname)
+ luaname, lucname = save_lua_table(lookup_cache, luaname, lucname)
+ if luaname and lucname then
+ logreport ("both", 3, "cache", "Lookup cache saved.")
+ return true
+ else
+ logreport ("info", 0, "cache", "Lookup cache file not writable.")
end
return false
end
@@ -3553,45 +3551,15 @@ function save_names (currentnames)
end
local paths = config.luaotfload.paths
local luaname, lucname = paths.index_path_lua, paths.index_path_luc
- if fileiswritable (luaname) and fileiswritable (lucname) then
- osremove (lucname)
- local gzname = luaname .. ".gz"
- if config.luaotfload.db.compress then
- local serialized = tableserialize (currentnames, true)
- gzipsave (gzname, serialized)
- caches.compile (currentnames, "", lucname)
- else
- tabletofile (luaname, currentnames, true)
- caches.compile (currentnames, luaname, lucname)
- end
+ local compress = config.luaotfload.db.compress
+ luaname, lucname = save_lua_table(currentnames, luaname, lucname, compress)
+ if luaname and lucname then
logreport ("info", 2, "db", "Font index saved at ...")
- local success = false
- if lfsisfile (luaname) then
- logreport ("info", 2, "db", "Text: " .. luaname)
- success = true
- end
- if lfsisfile (gzname) then
- logreport ("info", 2, "db", "Gzip: " .. gzname)
- success = true
- end
- if lfsisfile (lucname) then
- logreport ("info", 2, "db", "Byte: " .. lucname)
- success = true
- end
- if success then
- return true
- else
- logreport ("info", 0, "db", "Could not compile font index.")
- return false
- end
+ logreport ("info", 2, "db", "Text: " .. luaname)
+ logreport ("info", 2, "db", "Byte: " .. lucname)
+ return true
end
logreport ("info", 0, "db", "Index file not writable")
- if not fileiswritable (luaname) then
- logreport ("info", 0, "db", "Failed to write %s.", luaname)
- end
- if not fileiswritable (lucname) then
- logreport ("info", 0, "db", "Failed to write %s.", lucname)
- end
return false
end
@@ -3829,10 +3797,7 @@ return function ()
fonts.definers = fonts.definers or { resolvers = { } }
names.blacklist = blacklist
- -- MK Changed to rebuild with case insensitive fallback.
- -- Negative version to indicate generation by modified code.
- names.version = -2 --- decrease monotonically
- -- /MK
+ names.version = 6 --- increase monotonically
names.data = nil --- contains the loaded database
names.lookups = nil --- contains the lookup cache