summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/tex
diff options
context:
space:
mode:
authorKarl Berry <karl@freefriends.org>2014-01-01 22:57:02 +0000
committerKarl Berry <karl@freefriends.org>2014-01-01 22:57:02 +0000
commit0479a789f43c32b8fbc9d74bb82c615932a3c5b1 (patch)
treeb1c9d139f32df9676c239bdad84408cf55124729 /Master/texmf-dist/tex
parent999dd6fa37bd291385b20ccf85f980eccfd2e683 (diff)
luaotfload (1jan14)
git-svn-id: svn://tug.org/texlive/trunk@32538 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist/tex')
-rw-r--r--Master/texmf-dist/tex/luatex/luaotfload/luaotfload-auxiliary.lua141
-rw-r--r--Master/texmf-dist/tex/luatex/luaotfload/luaotfload-basics-gen.lua6
-rw-r--r--Master/texmf-dist/tex/luatex/luaotfload/luaotfload-blacklist.cnf2
-rw-r--r--Master/texmf-dist/tex/luatex/luaotfload/luaotfload-characters.lua36
-rw-r--r--Master/texmf-dist/tex/luatex/luaotfload/luaotfload-colors.lua41
-rw-r--r--Master/texmf-dist/tex/luatex/luaotfload/luaotfload-database.lua2998
-rw-r--r--Master/texmf-dist/tex/luatex/luaotfload/luaotfload-diagnostics.lua681
-rw-r--r--Master/texmf-dist/tex/luatex/luaotfload/luaotfload-extralibs.lua501
-rw-r--r--Master/texmf-dist/tex/luatex/luaotfload/luaotfload-features.lua58
-rw-r--r--Master/texmf-dist/tex/luatex/luaotfload/luaotfload-files.lua27
-rw-r--r--Master/texmf-dist/tex/luatex/luaotfload/luaotfload-fontloader.lua (renamed from Master/texmf-dist/tex/luatex/luaotfload/luaotfload-merged.lua)1913
-rw-r--r--Master/texmf-dist/tex/luatex/luaotfload/luaotfload-glyphlist.lua2
-rw-r--r--Master/texmf-dist/tex/luatex/luaotfload/luaotfload-letterspace.lua330
-rw-r--r--Master/texmf-dist/tex/luatex/luaotfload/luaotfload-loaders.lua33
-rw-r--r--Master/texmf-dist/tex/luatex/luaotfload/luaotfload-override.lua152
-rw-r--r--Master/texmf-dist/tex/luatex/luaotfload/luaotfload-status.lua39
-rw-r--r--Master/texmf-dist/tex/luatex/luaotfload/luaotfload-typo-krn.lua335
-rw-r--r--Master/texmf-dist/tex/luatex/luaotfload/luaotfload.lua70
-rw-r--r--Master/texmf-dist/tex/luatex/luaotfload/luaotfload.sty2
19 files changed, 5250 insertions, 2117 deletions
diff --git a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-auxiliary.lua b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-auxiliary.lua
index 0a3ace88037..d3de7313f07 100644
--- a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-auxiliary.lua
+++ b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-auxiliary.lua
@@ -2,9 +2,9 @@
-----------------------------------------------------------------------
-- FILE: luaotfload-auxiliary.lua
-- DESCRIPTION: part of luaotfload
--- REQUIREMENTS: luaotfload 2.3
+-- REQUIREMENTS: luaotfload 2.4
-- AUTHOR: Khaled Hosny, Élie Roux, Philipp Gesang
--- VERSION: 2.3a
+-- VERSION: 2.4
-- CREATED: 2013-05-01 14:40:50+0200
-----------------------------------------------------------------------
--
@@ -234,7 +234,7 @@ local set_capheight = function (fontdata)
local shared = fontdata.shared
local parameters = fontdata.parameters
local capheight
- if shared then
+ if shared and shared.rawdata.metadata.pfminfo then
local units_per_em = parameters.units
local size = parameters.size
local os2_capheight = shared.rawdata.metadata.pfminfo.os2_capheight
@@ -368,35 +368,124 @@ aux.name_of_slot = name_of_slot
resides. The file is huge (>3.7 MB as of 2013) and not part of the
isolated font loader. Nevertheless, we include a partial version
generated by the mkcharacters script that contains only the
- “direction” and “mirror” fields of each character defined.
+ a subset of the fields of each character defined.
+
+ Currently, these are (compare the mkcharacters script!)
+
+ · "direction"
+ · "mirror"
+ · "category"
+ · "textclass"
+
+ The directional information is required for packages like Simurgh [0]
+ to work correctly. In an early stage [1] it was necessary to load
+ further files from Context directly, including the full blown version
+ of char-def. Since we have no use for most of the so imported
+ functionality, the required parts have been isolated and are now
+ instated along with luaotfload-characters.lua. We can extend the set
+ of imported features easily should it not be enough.
+
+ [0] https://github.com/persian-tex/simurgh
+ [1] http://tex.stackexchange.com/a/132301/14066
--doc]]--
-characters = characters or { } --- should be created in basics-gen
-characters.data = { }
-local chardef = "luaotfload-characters"
+characters = characters or { } --- should be created in basics-gen
+characters.data = nil
+local chardef = "luaotfload-characters"
do
- local chardata
- local index = function (t, k)
+ local setmetatableindex = function (t, f)
+ local mt = getmetatable (t)
+ if mt then
+ mt.__index = f
+ else
+ setmetatable (t, { __index = f })
+ end
+ end
+
+ --- there are some special tables for each field that provide access
+ --- to fields of the character table by means of a metatable
+
+ local mkcharspecial = function (characters, tablename, field)
+
+ local chardata = characters.data
+
+ if chardata then
+ local newspecial = { }
+ characters [tablename] = newspecial --> e.g. “characters.data.mirrors”
+
+ local idx = function (t, char)
+ local c = chardata [char]
+ if c then
+ local m = c [field] --> e.g. “mirror”
+ if m then
+ t [char] = m
+ return m
+ end
+ end
+ newspecial [char] = false
+ return char
+ end
+
+ setmetatableindex (newspecial, idx)
+ end
+
+ end
+
+ local mkcategories = function (characters) -- different from the others
+
+ local chardata = characters.data
+
+ setmetatable (characters, { __index = function (t, char)
+ if char then
+ local c = chardata [char]
+ c = c.category or char
+ t [char] = c
+ return c
+ end
+ end})
+
+ end
+
+ local load_failed = false
+ local chardata --> characters.data; loaded on demand
+
+ local load_chardef = function ()
+
+ log ("Loading character metadata from %s.", chardef)
+ chardata = dofile (kpse.find_file (chardef, "lua"))
+
if chardata == nil then
- log("Loading character metadata from %s.", chardef)
- chardata = dofile(kpse.find_file(chardef, "lua"))
- if chardata == nil then
- warning("Could not load %s; continuing with empty character table.",
+ warning ("Could not load %s; continuing \z
+ with empty character table.",
chardef)
- chardata = { }
- end
+ chardata = { }
+ load_failed = true
end
- return chardata[k]
+
+ characters = { } --- nuke metatable
+ characters.data = chardata
+
+ --- institute some of the functionality from char-ini.lua
+
+ mkcharspecial (characters, "mirrors", "mirror")
+ mkcharspecial (characters, "directions", "direction")
+ mkcharspecial (characters, "textclasses", "textclass")
+ mkcategories (characters)
+
end
- local mt = getmetatable(characters.data)
- if mt then
- mt.__index = index
- else
- setmetatable(characters.data, { __index = index })
+ local charindex = function (t, k)
+ if chardata == nil and load_failed ~= true then
+ load_chardef ()
+ end
+
+ return characters [k]
end
+
+ setmetatableindex (characters, charindex)
+
end
-----------------------------------------------------------------------
@@ -588,6 +677,8 @@ aux.sprint_math_dimension = sprint_math_dimension
local namesresolve = fonts.names.resolve
local namesscan_dir = fonts.names.scan_dir
+--[====[-- TODO -> port this to new db model
+
--- local directories -------------------------------------------------
--- migrated from luaotfload-database.lua
@@ -595,7 +686,7 @@ local namesscan_dir = fonts.names.scan_dir
--- string -> (int * int)
local scan_external_dir = function (dir)
- local old_names, new_names = names.data
+ local old_names, new_names = names.data()
if not old_names then
old_names = load_names()
end
@@ -612,6 +703,12 @@ end
aux.scan_external_dir = scan_external_dir
+--]====]--
+
+aux.scan_external_dir = function ()
+ print "ERROR: scan_external_dir() is not implemented"
+end
+
--- db queries --------------------------------------------------------
--- https://github.com/lualatex/luaotfload/issues/74
diff --git a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-basics-gen.lua b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-basics-gen.lua
index 26c1edc729b..9cf5b93172a 100644
--- a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-basics-gen.lua
+++ b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-basics-gen.lua
@@ -89,6 +89,7 @@ local remapper = {
fea = "font feature files",
pfa = "type1 fonts", -- this is for Khaled, in ConTeXt we don't use this!
pfb = "type1 fonts", -- this is for Khaled, in ConTeXt we don't use this!
+ afm = "afm",
}
function resolvers.findfile(name,fileformat)
@@ -117,6 +118,11 @@ end
resolvers.findbinfile = resolvers.findfile
+function resolvers.loadbinfile(filename,filetype)
+ local data = io.loaddata(filename)
+ return true, data, #data
+end
+
function resolvers.resolve(s)
return s
end
diff --git a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-blacklist.cnf b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-blacklist.cnf
index 5f80e242c73..5c03dc298f2 100644
--- a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-blacklist.cnf
+++ b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-blacklist.cnf
@@ -8,3 +8,5 @@ spltfgbd.ttf
spltfgbi.ttf
spltfgit.ttf
spltfgrg.ttf
+% Buggy Max OS font, see https://github.com/lualatex/luaotfload/issues/139
+Skia.ttf
diff --git a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-characters.lua b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-characters.lua
index 036c69118ed..13f0482bb2d 100644
--- a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-characters.lua
+++ b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-characters.lua
@@ -163,11 +163,13 @@ return {
category="ps",
direction="on",
mirror=0x0029,
+ textclass="open",
},
{
category="pe",
direction="on",
mirror=0x0028,
+ textclass="close",
},
{
category="po",
@@ -245,6 +247,7 @@ return {
category="sm",
direction="on",
mirror=0x003E,
+ textclass="open",
},
{
category="sm",
@@ -254,6 +257,7 @@ return {
category="sm",
direction="on",
mirror=0x003C,
+ textclass="close",
},
{
category="po",
@@ -371,6 +375,7 @@ return {
category="ps",
direction="on",
mirror=0x005D,
+ textclass="open",
},
{
category="po",
@@ -380,6 +385,7 @@ return {
category="pe",
direction="on",
mirror=0x005B,
+ textclass="close",
},
{
category="sk",
@@ -501,6 +507,7 @@ return {
category="ps",
direction="on",
mirror=0x007D,
+ textclass="open",
},
{
category="sm",
@@ -510,6 +517,7 @@ return {
category="pe",
direction="on",
mirror=0x007B,
+ textclass="close",
},
{
category="sm",
@@ -695,6 +703,7 @@ return {
category="pi",
direction="on",
mirror=0x00BB,
+ textclass="open",
},
{
category="sm",
@@ -760,6 +769,7 @@ return {
category="pf",
direction="on",
mirror=0x00AB,
+ textclass="close",
},
{
category="no",
@@ -28315,11 +28325,13 @@ return {
category="pi",
direction="on",
mirror=0x203A,
+ textclass="open",
},
[0x203A]={
category="pf",
direction="on",
mirror=0x2039,
+ textclass="close",
},
[0x203B]={
category="po",
@@ -28563,11 +28575,13 @@ return {
category="ps",
direction="on",
mirror=0x207E,
+ textclass="open",
},
[0x207E]={
category="pe",
direction="on",
mirror=0x207D,
+ textclass="close",
},
[0x207F]={
category="ll",
@@ -28629,11 +28643,13 @@ return {
category="ps",
direction="on",
mirror=0x208E,
+ textclass="open",
},
[0x208E]={
category="pe",
direction="on",
mirror=0x208D,
+ textclass="close",
},
[0x2090]={
category="lm",
@@ -62939,31 +62955,37 @@ return {
category="ps",
direction="on",
mirror=0xFE5A,
+ textclass="open",
},
[0xFE5A]={
category="pe",
direction="on",
mirror=0xFE59,
+ textclass="close",
},
[0xFE5B]={
category="ps",
direction="on",
mirror=0xFE5C,
+ textclass="open",
},
[0xFE5C]={
category="pe",
direction="on",
mirror=0xFE5B,
+ textclass="close",
},
[0xFE5D]={
category="ps",
direction="on",
mirror=0xFE5E,
+ textclass="open",
},
[0xFE5E]={
category="pe",
direction="on",
mirror=0xFE5D,
+ textclass="close",
},
[0xFE5F]={
category="po",
@@ -62989,11 +63011,13 @@ return {
category="sm",
direction="on",
mirror=0xFE65,
+ textclass="open",
},
[0xFE65]={
category="sm",
direction="on",
mirror=0xFE64,
+ textclass="close",
},
[0xFE66]={
category="sm",
@@ -63611,11 +63635,13 @@ return {
category="ps",
direction="on",
mirror=0xFF09,
+ textclass="open",
},
[0xFF09]={
category="pe",
direction="on",
mirror=0xFF08,
+ textclass="close",
},
[0xFF0A]={
category="po",
@@ -63693,6 +63719,7 @@ return {
category="sm",
direction="on",
mirror=0xFF1E,
+ textclass="open",
},
[0xFF1D]={
category="sm",
@@ -63702,6 +63729,7 @@ return {
category="sm",
direction="on",
mirror=0xFF1C,
+ textclass="close",
},
[0xFF1F]={
category="po",
@@ -63819,6 +63847,7 @@ return {
category="ps",
direction="on",
mirror=0xFF3D,
+ textclass="open",
},
[0xFF3C]={
category="po",
@@ -63828,6 +63857,7 @@ return {
category="pe",
direction="on",
mirror=0xFF3B,
+ textclass="close",
},
[0xFF3E]={
category="sk",
@@ -63949,6 +63979,7 @@ return {
category="ps",
direction="on",
mirror=0xFF5D,
+ textclass="open",
},
[0xFF5C]={
category="sm",
@@ -63958,6 +63989,7 @@ return {
category="pe",
direction="on",
mirror=0xFF5B,
+ textclass="close",
},
[0xFF5E]={
category="sm",
@@ -63967,11 +63999,13 @@ return {
category="ps",
direction="on",
mirror=0xFF60,
+ textclass="open",
},
[0xFF60]={
category="pe",
direction="on",
mirror=0xFF5F,
+ textclass="close",
},
[0xFF61]={
category="po",
@@ -63981,11 +64015,13 @@ return {
category="ps",
direction="on",
mirror=0xFF63,
+ textclass="open",
},
[0xFF63]={
category="pe",
direction="on",
mirror=0xFF62,
+ textclass="close",
},
[0xFF64]={
category="po",
diff --git a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-colors.lua b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-colors.lua
index ac9291dae7b..b8ecb875c7d 100644
--- a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-colors.lua
+++ b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-colors.lua
@@ -1,5 +1,5 @@
if not modules then modules = { } end modules ['luaotfload-colors'] = {
- version = "2.3a",
+ version = "2.4",
comment = "companion to luaotfload.lua (font color)",
author = "Khaled Hosny, Elie Roux, Philipp Gesang",
copyright = "Luaotfload Development Team",
@@ -126,19 +126,12 @@ registerotffeature {
--- something is carried around in ``res``
--- for later use by color_handler() --- but what?
-local res --- <- state of what?
+local res = nil
--- float -> unit
local function pageresources(alpha)
- local res2
- if not res then
- res = "/TransGs1<</ca 1/CA 1>>"
- end
- res2 = stringformat("/TransGs%s<</ca %s/CA %s>>",
- alpha, alpha, alpha)
- res = stringformat("%s%s",
- res,
- stringfind(res, res2) and "" or res2)
+ res = res or {}
+ res[alpha] = true
end
--- we store results of below color handler as tuples of
@@ -280,17 +273,25 @@ end
--- node -> node
local color_handler = function (head)
- -- check if our page resources existed in the previous run
- -- and remove it to avoid duplicating it later
- if res then
- local r = "/ExtGState<<" .. res .. ">>"
- tex.pdfpageresources = stringgsub(tex.pdfpageresources, r, "")
- end
local new_head = node_colorize(head, nil, nil)
-- now append our page resources
- if res and stringfind(res, "%S") then -- test for non-empty string
- local r = "/ExtGState<<" .. res .. ">>"
- tex.pdfpageresources = tex.pdfpageresources..r
+ if res then
+ res["1"] = true
+ local tpr, t = tex.pdfpageresources, ""
+ for k in pairs(res) do
+ local str = stringformat("/TransGs%s<</ca %s/CA %s>>", k, k, k)
+ if not stringfind(tpr,str) then
+ t = t .. str
+ end
+ end
+ if t ~= "" then
+ if not stringfind(tpr,"/ExtGState<<.*>>") then
+ tpr = tpr.."/ExtGState<<>>"
+ end
+ tpr = stringgsub(tpr,"/ExtGState<<","%1"..t)
+ tex.pdfpageresources = tpr
+ end
+ res = nil -- reset res
end
return new_head
end
diff --git a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-database.lua b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-database.lua
index 1b75458088a..b47fc8b1f58 100644
--- a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-database.lua
+++ b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-database.lua
@@ -1,15 +1,40 @@
if not modules then modules = { } end modules ['luaotfload-database'] = {
- version = "2.3b",
+ version = "2.4",
comment = "companion to luaotfload.lua",
author = "Khaled Hosny, Elie Roux, Philipp Gesang",
copyright = "Luaotfload Development Team",
license = "GNU GPL v2"
}
---- TODO: if the specification is an absolute filename with a font not in the
---- database, add the font to the database and load it. There is a small
---- difficulty with the filenames of the TEXMF tree that are referenced as
---- relative paths...
+--[[doc--
+
+ Some statistics:
+
+ a) TL 2012, mkluatexfontdb --force
+ b) v2.4, luaotfload-tool --update --force
+ c) v2.4, luaotfload-tool --update --force --formats=+afm,pfa,pfb
+ d) Context, mtxrun --script fonts --reload --force
+
+ (Keep in mind that Context does index fewer fonts since it
+ considers only the contents of the minimals tree, not the
+ tex live one!)
+
+ time (m:s) peak VmSize (kB)
+ a 1:19 386 018
+ b 0:37 715 797
+ c 2:27 1 017 674
+ d 0:44 1 082 313
+
+ Most of the increase in memory consumption from version 1.x to 2.2+
+ can be attributed to the move from single-pass to a multi-pass
+ approach to building the index: Information is first gathered from
+ all reachable fonts and only afterwards processed, classified and
+ discarded. Also, there is a good deal of additional stuff kept in
+ the database now: two extra tables for file names and font families
+ have been added, making font lookups more efficient while improving
+ maintainability of the code.
+
+--doc]]--
local lpeg = require "lpeg"
@@ -20,93 +45,104 @@ local C, Cc, Cf, Cg, Cs, Ct
= lpeg.C, lpeg.Cc, lpeg.Cf, lpeg.Cg, lpeg.Cs, lpeg.Ct
--- Luatex builtins
-local load = load
-local next = next
-local pcall = pcall
-local require = require
-local tonumber = tonumber
-local unpack = table.unpack
-
-local fontloaderinfo = fontloader.info
-local fontloaderopen = fontloader.open
-local iolines = io.lines
-local ioopen = io.open
-local kpseexpand_path = kpse.expand_path
-local kpseexpand_var = kpse.expand_var
-local kpsefind_file = kpse.find_file
-local kpselookup = kpse.lookup
-local kpsereadable_file = kpse.readable_file
-local lfsattributes = lfs.attributes
-local lfschdir = lfs.chdir
-local lfscurrentdir = lfs.currentdir
-local lfsdir = lfs.dir
-local mathabs = math.abs
-local mathmin = math.min
-local osremove = os.remove
-local stringfind = string.find
-local stringformat = string.format
-local stringgmatch = string.gmatch
-local stringgsub = string.gsub
-local stringlower = string.lower
-local stringsub = string.sub
-local stringupper = string.upper
-local tableconcat = table.concat
-local tablesort = table.sort
-local texiowrite_nl = texio.write_nl
-local utf8gsub = unicode.utf8.gsub
-local utf8lower = unicode.utf8.lower
+local load = load
+local next = next
+local require = require
+local tonumber = tonumber
+local unpack = table.unpack
+
+local fontloaderinfo = fontloader.info
+local fontloaderclose = fontloader.close
+local fontloaderopen = fontloader.open
+----- fontloaderto_table = fontloader.to_table
+local gzipopen = gzip.open
+local iolines = io.lines
+local ioopen = io.open
+local iopopen = io.popen
+local kpseexpand_path = kpse.expand_path
+local kpseexpand_var = kpse.expand_var
+local kpsefind_file = kpse.find_file
+local kpselookup = kpse.lookup
+local kpsereadable_file = kpse.readable_file
+local lfsattributes = lfs.attributes
+local lfschdir = lfs.chdir
+local lfscurrentdir = lfs.currentdir
+local lfsdir = lfs.dir
+local mathabs = math.abs
+local mathmin = math.min
+local osgettimeofday = os.gettimeofday
+local osremove = os.remove
+local stringfind = string.find
+local stringformat = string.format
+local stringgmatch = string.gmatch
+local stringgsub = string.gsub
+local stringlower = string.lower
+local stringsub = string.sub
+local stringupper = string.upper
+local tableconcat = table.concat
+local tablesort = table.sort
+local utf8gsub = unicode.utf8.gsub
+local utf8lower = unicode.utf8.lower
+local utf8length = unicode.utf8.length
+local zlibcompress = zlib.compress
--- these come from Lualibs/Context
-local getwritablepath = caches.getwritablepath
-local filebasename = file.basename
-local filecollapsepath = file.collapsepath or file.collapse_path
-local filedirname = file.dirname
-local fileextname = file.extname
-local fileiswritable = file.iswritable
-local filejoin = file.join
-local filenameonly = file.nameonly
-local filereplacesuffix = file.replacesuffix
-local filesplitpath = file.splitpath or file.split_path
-local lfsisdir = lfs.isdir
-local lfsisfile = lfs.isfile
-local lfsmkdirs = lfs.mkdirs
-local stringis_empty = string.is_empty
-local stringsplit = string.split
-local stringstrip = string.strip
-local tableappend = table.append
-local tablecopy = table.copy
-local tabletofile = table.tofile
-local tabletohash = table.tohash
-
+local filebasename = file.basename
+local filecollapsepath = file.collapsepath or file.collapse_path
+local filedirname = file.dirname
+local fileextname = file.extname
+local fileiswritable = file.iswritable
+local filejoin = file.join
+local filenameonly = file.nameonly
+local filereplacesuffix = file.replacesuffix
+local filesplitpath = file.splitpath or file.split_path
+local filesuffix = file.suffix
+local getwritablepath = caches.getwritablepath
+local lfsisdir = lfs.isdir
+local lfsisfile = lfs.isfile
+local lfsmkdirs = lfs.mkdirs
+local lpegsplitat = lpeg.splitat
+local stringis_empty = string.is_empty
+local stringsplit = string.split
+local stringstrip = string.strip
+local tableappend = table.append
+local tablecopy = table.copy
+local tablefastcopy = table.fastcopy
+local tabletofile = table.tofile
+local tabletohash = table.tohash
+local tableserialize = table.serialize
+local runasscript = caches == nil
--- the font loader namespace is “fonts”, same as in Context
--- we need to put some fallbacks into place for when running
--- as a script
-fonts = fonts or { }
-fonts.names = fonts.names or { }
-fonts.definers = fonts.definers or { }
-
-local names = fonts.names
-
-config = config or { }
-config.luaotfload = config.luaotfload or { }
-config.luaotfload.resolver = config.luaotfload.resolver or "normal"
-if config.luaotfload.update_live ~= false then
- --- this option allows for disabling updates
- --- during a TeX run
- config.luaotfload.update_live = true
-end
-
-names.version = 2.207
-names.data = nil --- contains the loaded database
-names.lookups = nil --- contains the lookup cache
-
-names.path = { index = { }, lookups = { } }
-names.path.globals = {
- prefix = "", --- writable_path/names_dir
- names_dir = config.luaotfload.names_dir or "names",
- index_file = config.luaotfload.index_file
- or "luaotfload-names.lua",
- lookups_file = "luaotfload-lookup-cache.lua",
+fonts = fonts or { }
+fonts.names = fonts.names or { }
+fonts.definers = fonts.definers or { }
+
+local luaotfloadconfig = config.luaotfload --- always present
+luaotfloadconfig.resolver = luaotfloadconfig.resolver or "normal"
+luaotfloadconfig.formats = luaotfloadconfig.formats or "otf,ttf,ttc,dfont"
+luaotfloadconfig.strip = luaotfloadconfig.strip == true
+
+--- this option allows for disabling updates
+--- during a TeX run
+luaotfloadconfig.update_live = luaotfloadconfig.update_live ~= false
+luaotfloadconfig.compress = luaotfloadconfig.compress ~= false
+
+local names = fonts.names
+local name_index = nil --> upvalue for names.data
+local lookup_cache = nil --> for names.lookups
+names.version = 2.4
+names.data = nil --- contains the loaded database
+names.lookups = nil --- contains the lookup cache
+
+names.path = { index = { }, lookups = { } }
+names.path.globals = {
+ prefix = "", --- writable_path/names_dir
+ names_dir = luaotfloadconfig.names_dir or "names",
+ index_file = luaotfloadconfig.index_file
+ or "luaotfload-names.lua",
+ lookups_file = "luaotfload-lookup-cache.lua",
}
--- string -> (string * string)
@@ -115,7 +151,61 @@ local make_luanames = function (path)
filereplacesuffix(path, "luc")
end
-local report = logs.names_report
+local report = logs.names_report
+local report_status = logs.names_status
+local report_status_start = logs.names_status_start
+local report_status_stop = logs.names_status_stop
+
+--- The “termwidth” value is only considered when printing
+--- short status messages, e.g. when building the database
+--- online.
+if not luaotfloadconfig.termwidth then
+ local tw = 79
+ if not ( os.type == "windows" --- Assume broken terminal.
+ or os.getenv "TERM" == "dumb")
+ then
+ local p = iopopen "tput cols"
+ if p then
+ result = tonumber (p:read "*all")
+ p:close ()
+ if result then
+ tw = result
+ else
+ report ("log", 2, "db", "tput returned non-number.")
+ end
+ else
+ report ("log", 2, "db", "Shell escape disabled or tput executable missing.")
+ report ("log", 2, "db", "Assuming 79 cols terminal width.")
+ end
+ end
+ luaotfloadconfig.termwidth = tw
+end
+
+names.patterns = { }
+local patterns = names.patterns
+
+local trailingslashes = P"/"^1 * P(-1)
+local stripslashes = C((1 - trailingslashes)^0)
+patterns.stripslashes = stripslashes
+
+local comma = P","
+local noncomma = 1-comma
+local splitcomma = Ct((C(noncomma^1) + comma)^1)
+patterns.splitcomma = splitcomma
+
+local format_precedence = {
+ "otf", "ttc", "ttf",
+ "dfont", "afm", "pfb",
+ "pfa",
+}
+
+local location_precedence = {
+ "local", "system", "texmf",
+}
+
+local set_location_precedence = function (precedence)
+ location_precedence = precedence
+end
--[[doc--
We use the functions in the cache.* namespace that come with the
@@ -130,7 +220,7 @@ local report = logs.names_report
created by different user.
--doc]]--
-if caches then
+if not runasscript then
local globals = names.path.globals
local names_dir = globals.names_dir
@@ -139,16 +229,17 @@ if caches then
luaotfload.error
("Impossible to find a suitable writeable cache...")
else
+ prefix = lpegmatch (stripslashes, prefix)
report ("log", 0, "db",
- "root cache directory is " .. prefix)
+ "Root cache directory is %s.", prefix)
end
globals.prefix = prefix
- local lookups = names.path.lookups
+ local lookup_path = names.path.lookups
local index = names.path.index
local lookups_file = filejoin (prefix, globals.lookups_file)
local index_file = filejoin (prefix, globals.index_file)
- lookups.lua, lookups.luc = make_luanames (lookups_file)
+ lookup_path.lua, lookup_path.luc = make_luanames (lookups_file)
index.lua, index.luc = make_luanames (index_file)
else --- running as script, inject some dummies
caches = { }
@@ -160,14 +251,42 @@ end
Auxiliary functions
--doc]]--
+--- fontnames contain all kinds of garbage; as a precaution we
+--- lowercase and strip them of non alphanumerical characters
+
--- string -> string
-local sanitize_string = function (str)
+
+local invalidchars = "[^%a%d]"
+
+local sanitize_fontname = function (str)
if str ~= nil then
- return utf8gsub(utf8lower(str), "[^%a%d]", "")
+ str = utf8gsub (utf8lower (str), invalidchars, "")
+ return str
end
return nil
end
+local sanitize_fontnames = function (rawnames)
+ local result = { }
+ for category, namedata in next, rawnames do
+
+ if type (namedata) == "string" then
+ result [category] = utf8gsub (utf8lower (namedata),
+ invalidchars,
+ "")
+ else
+ local target = { }
+ for field, name in next, namedata do
+ target [field] = utf8gsub (utf8lower (name),
+ invalidchars,
+ "")
+ end
+ result [category] = target
+ end
+ end
+ return result
+end
+
local find_files_indeed
find_files_indeed = function (acc, dirs, filter)
if not next (dirs) then --- done
@@ -219,16 +338,44 @@ end
This is a sketch of the luaotfload db:
type dbobj = {
- mappings : fontentry list;
- status : filestatus;
- version : float;
- // new in v2.3; these supersede the basenames / barenames
- // hashes from v2.2
- filenames : filemap;
+ families : familytable;
+ files : filemap;
+ status : filestatus;
+ mappings : fontentry list;
+ meta : metadata;
+ names : namedata; // TODO: check for relevance after db is finalized
+ }
+ and familytable = {
+ local : (format, familyentry) hash; // specified with include dir
+ texmf : (format, familyentry) hash;
+ system : (format, familyentry) hash;
+ }
+ and familyentry = {
+ regular : sizes;
+ italic : sizes;
+ bold : sizes;
+ bolditalic : sizes;
+ }
+ and sizes = {
+ default : int; // points into mappings or names
+ optical : (int, int) list; // design size -> index entry
+ }
+ and metadata = {
+ formats : string list; // { "otf", "ttf", "ttc", "dfont" }
+ statistics : TODO;
+ version : float;
}
and filemap = {
- base : (string, int) hash; // basename -> idx
- bare : (string, int) hash; // barename -> idx
+ base : {
+ local : (string, int) hash; // basename -> idx
+ system : (string, int) hash;
+ texmf : (string, int) hash;
+ };
+ bare : {
+ local : (string, (string, int) hash) hash; // location -> (barename -> idx)
+ system : (string, (string, int) hash) hash;
+ texmf : (string, (string, int) hash) hash;
+ };
full : (int, string) hash; // idx -> full path
}
and fontentry = {
@@ -239,6 +386,7 @@ This is a sketch of the luaotfload db:
fullname : string; // <- metadata
sanitized : {
family : string;
+ fontstyle_name : string; // <- new in 2.4
fontname : string; // <- metadata
fullname : string; // <- namedata.names
metafamily : string;
@@ -250,13 +398,14 @@ This is a sketch of the luaotfload db:
size : int list;
slant : int;
subfont : int;
- texmf : bool;
+ location : local | system | texmf;
weight : int;
width : int;
- units_per_em : int; // mainly 1000, but also 2048 or 256
+ units_per_em : int; // mainly 1000, but also 2048 or 256
}
- and filestatus = (fullname,
- { index : int list; timestamp : int }) dict
+ and filestatus = (string, // fullname
+ { index : int list; // pointer into mappings
+ timestamp : int; }) dict
beware that this is a reconstruction and may be incomplete.
@@ -295,42 +444,95 @@ mtx-fonts has in names.tma:
--doc]]--
-local fontnames_init = function (keep_cache) --- returns dbobj
+local initialize_namedata = function (formats) --- returns dbobj
return {
- mappings = { },
- status = { },
--- filenames = { }, -- created later
- version = names.version,
+ --families = { },
+ status = { }, -- was: status; map abspath -> mapping
+ mappings = { }, -- TODO: check if still necessary after rewrite
+ names = { },
+-- files = { }, -- created later
+ meta = {
+ formats = formats,
+ statistics = { },
+ version = names.version,
+ },
}
end
+--[[doc--
+
+ Since Luaotfload does not depend on the lualibs anymore we
+ have to put our own small wrappers for the gzip library in
+ place.
+
+ load_gzipped -- Read and decompress and entire gzipped file.
+ Returns the uncompressed content as a string.
+
+--doc]]--
+
+local load_gzipped = function (filename)
+ local gh = gzipopen (filename,"rb")
+ if gh then
+ local data = gh:read "*all"
+ gh:close ()
+ return data
+ end
+end
+
+--[[doc--
+
+ save_gzipped -- Compress and write a string to file. The return
+ value is the number of bytes written. Zlib parameters are: best
+ compression and default strategy.
+
+--doc]]--
+
+local save_gzipped = function (filename, data)
+ local gh = gzipopen (filename, "wb9")
+ if gh then
+ gh:write (data)
+ local bytes = gh:seek ()
+ gh:close ()
+ return bytes
+ end
+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 load_lua_file = function (path)
- local foundname = filereplacesuffix(path, "luc")
+ local foundname = filereplacesuffix (path, "luc")
+ local code = nil
- local fh = ioopen(foundname, "rb") -- try bin first
+ local fh = ioopen (foundname, "rb") -- try bin first
if fh then
local chunk = fh:read"*all"
fh:close()
- code = load(chunk, "b")
+ code = load (chunk, "b")
end
if not code then --- fall back to text file
- foundname = filereplacesuffix(path, "lua")
+ foundname = filereplacesuffix (path, "lua")
fh = ioopen(foundname, "rb")
if fh then
local chunk = fh:read"*all"
fh:close()
- code = load(chunk, "t")
+ code = load (chunk, "t")
+ end
+ end
+
+ if not code then --- probe gzipped file
+ foundname = filereplacesuffix (path, "lua.gz")
+ local chunk = load_gzipped (foundname)
+ if chunk then
+ code = load (chunk, "t")
end
end
if not code then return nil, nil end
- return foundname, code()
+ return foundname, code ()
end
--- define locals in scope
@@ -338,127 +540,106 @@ local crude_file_lookup
local crude_file_lookup_verbose
local find_closest
local flush_lookup_cache
-local font_fullinfo
+local ot_fullinfo
+local t1_fullinfo
local load_names
local load_lookups
local read_blacklist
local read_fonts_conf
local reload_db
-local resolve
+local resolve_name
local resolve_cached
local resolve_fullpath
local save_names
local save_lookups
local update_names
+local get_font_filter
+local set_font_filter
--- state of the database
-local fonts_loaded = false
local fonts_reloaded = false
+local fonts_read = 0
--- limit output when approximate font matching (luaotfload-tool -F)
local fuzzy_limit = 1 --- display closest only
--- bool? -> dbobj
load_names = function (dry_run)
- local starttime = os.gettimeofday()
- local foundname, data = load_lua_file(names.path.index.lua)
+ local starttime = osgettimeofday ()
+ local foundname, data = load_lua_file (names.path.index.lua)
if data then
- report("both", 2, "db",
- "Font names database loaded", "%s", foundname)
- report("info", 3, "db", "Loading took %0.f ms",
- 1000*(os.gettimeofday()-starttime))
+ report ("both", 2, "db",
+ "Font names database loaded", "%s", foundname)
+ report ("info", 3, "db", "Loading took %0.f ms.",
+ 1000 * (osgettimeofday () - starttime))
- local db_version, nms_version = data.version, names.version
+ local db_version, nms_version = data.meta.version, names.version
if db_version ~= nms_version then
- report("both", 0, "db",
- [[Version mismatch; expected %4.3f, got %4.3f]],
- nms_version, db_version)
+ report ("both", 0, "db",
+ [[Version mismatch; expected %4.3f, got %4.3f.]],
+ nms_version, db_version)
if not fonts_reloaded then
- report("both", 0, "db", [[Force rebuild]])
- return update_names({ }, true, false)
+ report ("both", 0, "db", [[Force rebuild.]])
+ data = update_names ({ }, true, false)
+ if not data then
+ report ("both", 0, "db",
+ "Database creation unsuccessful.")
+ end
end
end
else
- report("both", 0, "db",
- [[Font names database not found, generating new one.]])
- report("both", 0, "db",
- [[This can take several minutes; please be patient.]])
- data = update_names(fontnames_init(false), nil, dry_run)
- local success = save_names(data)
- if not success then
- report("both", 0, "db", "Database creation unsuccessful.")
+ report ("both", 0, "db",
+ [[Font names database not found, generating new one.]])
+ report ("both", 0, "db",
+ [[This can take several minutes; please be patient.]])
+ data = update_names (initialize_namedata (get_font_filter ()),
+ nil, dry_run)
+ if not data then
+ report ("both", 0, "db", "Database creation unsuccessful.")
end
end
- fonts_loaded = true
return data
end
---- unit -> dbobj
+--- unit -> unit
load_lookups = function ( )
local foundname, data = load_lua_file(names.path.lookups.lua)
if data then
report("both", 3, "cache",
- "Lookup cache loaded (%s)", foundname)
+ "Lookup cache loaded from %s.", foundname)
else
report("both", 1, "cache",
"No lookup cache, creating empty.")
data = { }
end
- return data
+ lookup_cache = data
end
-local style_synonyms = { set = { } }
-do
- local combine = function (ta, tb)
- local result = { }
- for i=1, #ta do
- for j=1, #tb do
- result[#result+1] = ta[i] .. tb[j]
- end
- end
- return result
- end
-
- --- read this: http://blogs.adobe.com/typblography/2008/05/indesign_font_conflicts.html
- --- tl;dr: font style synonyms are unreliable.
- ---
- --- Context matches font names against lists of known identifiers
- --- for weight, style, width, and variant, so that including
- --- the family name there are five dimensions for choosing a
- --- match. The sad thing is, while this is a decent heuristic it
- --- makes no sense to imitate it in luaotfload because the user
- --- interface must fit into the much more limited Xetex scheme that
- --- distinguishes between merely four style categories (variants):
- --- “regular”, “italic”, “bold”, and “bolditalic”. As a result,
- --- some of the styles are lumped together although they can differ
- --- significantly (like “medium” and “bold”).
-
- --- Xetex (XeTeXFontMgr.cpp) appears to recognize only “plain”,
- --- “normal”, and “roman” as synonyms for “regular”.
- local list = {
- regular = { "normal", "roman",
- "plain", "book",
- "light", "extralight",
- "ultralight", },
- bold = { "demi", "demibold",
- "semibold", "boldregular",
- "medium", "mediumbold",
- "ultrabold", "extrabold",
- "heavy", "black",
- "bold", },
- italic = { "regularitalic", "normalitalic",
- "oblique", "slanted",
- "italic", },
- }
+local regular_synonym = {
+ book = "r",
+ normal = "r",
+ plain = "r",
+ regular = "r",
+ roman = "r",
+}
- list.bolditalic = combine(list.bold, list.italic)
- style_synonyms.list = list
+local italic_synonym = {
+ oblique = true,
+ slanted = true,
+ italic = true,
+}
- for category, synonyms in next, style_synonyms.list do
- style_synonyms.set[category] = tabletohash(synonyms, true)
- end
-end
+local style_category = {
+ regular = "r",
+ bold = "b",
+ bolditalic = "bi",
+ italic = "i",
+ r = "regular",
+ b = "bold",
+ bi = "bolditalic",
+ i = "italic",
+}
local type1_formats = { "tfm", "ofm", }
@@ -471,12 +652,12 @@ local verbose_lookup = function (data, kind, filename)
found = data.full[found]
if found == nil then --> texmf
report("info", 0, "db",
- "Crude file lookup: req=%s; hit=%s => kpse",
+ "Crude file lookup: req=%s; hit=%s => kpse.",
filename, kind)
found = dummy_findfile(filename)
else
report("info", 0, "db",
- "Crude file lookup: req=%s; hit=%s; ret=%s",
+ "Crude file lookup: req=%s; hit=%s; ret=%s.",
filename, kind, found)
end
return found
@@ -484,60 +665,88 @@ local verbose_lookup = function (data, kind, filename)
return false
end
---- string -> (string | string * string)
+--- string -> (string * string * bool)
crude_file_lookup_verbose = function (filename)
- if not names.data then names.data = load_names() end
- local data = names.data
- local mappings = data.mappings
- local filenames = data.filenames
+ if not name_index then name_index = load_names() end
+ local mappings = name_index.mappings
+ local files = name_index.files
local found
--- look up in db first ...
- found = verbose_lookup(filenames, "bare", filename)
+ found = verbose_lookup(files, "bare", filename)
if found then
- return found
+ return found, nil, true
end
- found = verbose_lookup(filenames, "base", filename)
+ found = verbose_lookup(files, "base", filename)
if found then
- return found
+ return found, nil, true
end
--- ofm and tfm, returns pair
for i=1, #type1_formats do
local format = type1_formats[i]
if resolvers.findfile(filename, format) then
- return file.addsuffix(filename, format), format
+ return file.addsuffix(filename, format), format, true
end
end
- return filename, nil
+ return filename, nil, false
end
---- string -> (string | string * string)
-crude_file_lookup = function (filename)
- if not names.data then names.data = load_names() end
- local data = names.data
- local mappings = data.mappings
- local filenames = data.filenames
+local lookup_filename = function (filename)
+ if not name_index then name_index = load_names () end
+ local files = name_index.files
+ local basedata = files.base
+ local baredata = files.bare
+ for i = 1, #location_precedence do
+ local location = location_precedence [i]
+ local basenames = basedata [location]
+ local barenames = baredata [location]
+ local idx
+ if basenames ~= nil then
+ idx = basenames [filename]
+ if idx then
+ goto done
+ end
+ end
+ if barenames ~= nil then
+ for j = 1, #format_precedence do
+ local format = format_precedence [j]
+ local filemap = barenames [format]
+ if filemap then
+ idx = barenames [format] [filename]
+ if idx then
+ break
+ end
+ end
+ end
+ end
+::done::
+ if idx then
+ return files.full [idx]
+ end
+ end
+end
- local found
+--- string -> (string * string * bool)
+crude_file_lookup = function (filename)
+ local found = lookup_filename (filename)
- found = filenames.base[filename]
- or filenames.bare[filename]
+ if not found then
+ found = dummy_findfile(filename)
+ end
if found then
- found = filenames.full[found]
- if found == nil then
- found = dummy_findfile(filename)
- end
- return found or filename
+ return found, nil, true
end
+
for i=1, #type1_formats do
local format = type1_formats[i]
if resolvers.findfile(filename, format) then
- return file.addsuffix(filename, format), format
+ return file.addsuffix(filename, format), format, true
end
end
- return filename, nil
+
+ return filename, nil, false
end
--[[doc--
@@ -545,15 +754,19 @@ Existence of the resolved file name is verified differently depending
on whether the index entry has a texmf flag set.
--doc]]--
-local get_font_file = function (fullnames, entry)
+local get_font_file = function (index)
+ local entry = name_index.mappings [index]
+ if not entry then
+ return false
+ end
local basename = entry.basename
- if entry.texmf == true then
+ if entry.location == "texmf" then
if kpselookup(basename) then
return true, basename, entry.subfont
end
- else
- local fullname = fullnames[entry.index]
- if lfsisfile(fullname) then
+ else --- system, local
+ local fullname = name_index.files.full [index]
+ if lfsisfile (fullname) then
return true, basename, entry.subfont
end
end
@@ -562,28 +775,18 @@ end
--[[doc--
We need to verify if the result of a cached lookup actually exists in
-the texmf or filesystem.
+the texmf or filesystem. Again, due to the schizoprenic nature of the
+font managment we have to check both the system path and the texmf.
--doc]]--
local verify_font_file = function (basename)
- if not names.data then names.data = load_names() end
- local filenames = names.data.filenames
- local idx = filenames.base[basename]
- if not idx then
- return false
- end
-
- --- firstly, check filesystem
- local fullname = filenames.full[idx]
- if fullname and lfsisfile(fullname) then
+ local path = resolve_fullpath (basename)
+ if path and lfsisfile(path) then
return true
end
-
- --- secondly, locate via kpathsea
if kpsefind_file(basename) then
return true
end
-
return false
end
@@ -618,8 +821,8 @@ Idk what the “spec” resolver is for.
optsize, size
spec: name, sub resolved, sub, name, forced
-* name: contains both the name resolver from luatex-fonts and resolve()
- below
+* name: contains both the name resolver from luatex-fonts and
+ resolve_name() below
From my reading of font-def.lua, what a resolver does is
basically rewrite the “name” field of the specification record
@@ -648,46 +851,48 @@ local hash_request = function (specification)
end
--- 'a -> 'a -> table -> (string * int|boolean * boolean)
-resolve_cached = function (_, _, specification)
- if not names.lookups then names.lookups = load_lookups() end
+resolve_cached = function (specification)
+ if not lookup_cache then load_lookups () end
local request = hash_request(specification)
report("both", 4, "cache", "Looking for %q in cache ...",
request)
- local found = names.lookups[request]
+ local found = lookup_cache [request]
--- case 1) cache positive ----------------------------------------
if found then --- replay fields from cache hit
report("info", 4, "cache", "Found!")
local basename = found[1]
--- check the presence of the file in case it’s been removed
- local success = verify_font_file(basename)
+ local success = verify_font_file (basename)
if success == true then
return basename, found[2], true
end
- report("both", 4, "cache", "Cached file not found; resolving again")
+ report("both", 4, "cache", "Cached file not found; resolving again.")
else
- report("both", 4, "cache", "Not cached; resolving")
+ report("both", 4, "cache", "Not cached; resolving.")
end
--- case 2) cache negative ----------------------------------------
--- first we resolve normally ...
- local filename, subfont, success = resolve(nil, nil, specification)
- if not success then return filename, subfont, false end
+ local filename, subfont = resolve_name (specification)
+ if not filename then
+ return nil, nil
+ end
--- ... then we add the fields to the cache ... ...
local entry = { filename, subfont }
- report("both", 4, "cache", "New entry: %s", request)
- names.lookups[request] = entry
+ report("both", 4, "cache", "New entry: %s.", request)
+ lookup_cache [request] = entry
--- obviously, the updated cache needs to be stored.
--- TODO this should trigger a save only once the
--- document is compiled (finish_pdffile callback?)
- report("both", 5, "cache", "Saving updated cache")
- local success = save_lookups()
+ report("both", 5, "cache", "Saving updated cache.")
+ local success = save_lookups ()
if not success then --- sad, but not critical
- report("both", 0, "cache", "Could not write to cache")
+ report("both", 0, "cache", "Error writing cache.")
end
- return filename, subfont, true
+ return filename, subfont
end
--- this used to be inlined; with the lookup cache we don’t
@@ -695,17 +900,19 @@ end
--- “found” is the match accumulator
local add_to_match = function (found, size, face)
- 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
local continue = true
- if optsize then
- if dsnsize == size or (size > minsize and size <= maxsize) then
+
+ local optsize = face.size
+
+ if optsize and next (optsize) then
+ local dsnsize, maxsize, minsize
+ dsnsize = optsize[1]
+ maxsize = optsize[2]
+ minsize = optsize[3]
+
+ if size ~= nil
+ and (dsnsize == size or (size > minsize and size <= maxsize))
+ then
found[1] = face
continue = false ---> break
else
@@ -715,244 +922,287 @@ local add_to_match = function (found, size, face)
found[1] = face
continue = false ---> break
end
+
return found, continue
end
+local choose_closest = function (distances)
+ local closest = 2^51
+ local match
+ for i = 1, #distances do
+ local d, index = unpack (distances [i])
+ if d < closest then
+ closest = d
+ match = index
+ end
+ end
+ return match
+end
+
--[[doc--
-Luatex-fonts, the font-loader package luaotfload imports, comes with
-basic file location facilities (see luatex-fonts-syn.lua).
-However, not only does the builtin functionality rely on Context’s font
-name database, it is also too limited to be of more than basic use.
-For this reason, luaotfload supplies its own resolvers that accesses
-the font database created by the luaotfload-tool script.
+ choose_size -- Pick a font face of appropriate size from the list
+ of family members with matching style. There are three categories:
+
+ 1. exact matches: if there is a face whose design size equals
+ the asked size, it is returned immediately and no further
+ candidates are inspected.
+
+ 2. range matches: of all faces in whose design range the
+ requested size falls the one whose center the requested
+ size is closest to is returned.
+
+ 3. out-of-range matches: of all other faces (i. e. whose range
+ is above or below the asked size) the one is chosen whose
+ boundary (upper or lower) is closest to the requested size.
+
+ 4. default matches: if no design size or a design size of zero
+ is requested, the face with the default size is returned.
--doc]]--
+--- int * int * int * int list -> int -> int
+local choose_size = function (sizes, askedsize)
+ local mappings = name_index.mappings
+ local match = sizes.default
+ local exact
+ local inrange = { } --- distance * index list
+ local norange = { } --- distance * index list
+ local fontname, subfont
+ if askedsize ~= 0 then
+ --- firstly, look for an exactly matching design size or
+ --- matching range
+ for i = 1, #sizes do
+ local dsnsize, high, low, index = unpack (sizes [i])
+ if dsnsize == askedsize then
+ --- exact match, this is what we were looking for
+ exact = index
+ goto skip
+ elseif askedsize < low then
+ --- below range, add to the norange table
+ local d = low - askedsize
+ norange [#norange + 1] = { d, index }
+ elseif askedsize > high then
+ --- beyond range, add to the norange table
+ local d = askedsize - high
+ norange [#norange + 1] = { d, index }
+ else
+ --- range match
+ local d = ((low + high) / 2) - askedsize
+ if d < 0 then
+ d = -d
+ end
+ inrange [#inrange + 1] = { d, index }
+ end
+ end
+ end
+::skip::
+ if exact then
+ match = exact
+ elseif #inrange > 0 then
+ match = choose_closest (inrange)
+ elseif #norange > 0 then
+ match = choose_closest (norange)
+ end
+ return match
+end
----
---- the request specification has the fields:
----
---- · features: table
---- · normal: set of { ccmp clig itlc kern liga locl mark mkmk rlig }
---- · ???
---- · forced: string
---- · lookup: "name"
---- · method: string
---- · name: string
---- · resolved: string
---- · size: int
---- · specification: string (== <lookup> ":" <name>)
---- · sub: string
----
---- 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)
----
---- note by phg: I added a third return value that indicates a
---- successful lookup as this cannot be inferred from the other
---- values.
----
-
-resolve = function (_, _, specification) -- the 1st two parameters are used by ConTeXt
- if not fonts_loaded then names.data = load_names() end
- local data = names.data
-
- local name = sanitize_string(specification.name)
- local style = sanitize_string(specification.style) or "regular"
-
- local askedsize
- if specification.optsize then
- askedsize = tonumber(specification.optsize)
- elseif specification.size then
- askedsize = specification.size / 65536
- end
-
- 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
- )
+--[[doc--
+
+ resolve_familyname -- Query the families table for an entry
+ matching the specification.
+ The parameters “name” and “style” are pre-sanitized.
+
+--doc]]--
+--- spec -> string -> string -> int -> string * int
+local resolve_familyname = function (specification, name, style, askedsize)
+ local families = name_index.families
+ local mappings = name_index.mappings
+ local candidates = nil
+ --- arrow code alert
+ for i = 1, #location_precedence do
+ local location = location_precedence [i]
+ local locgroup = families [location]
+ for j = 1, #format_precedence do
+ local format = format_precedence [j]
+ local fmtgroup = locgroup [format]
+ if fmtgroup then
+ local familygroup = fmtgroup [name]
+ if familygroup then
+ local stylegroup = familygroup [style]
+ if stylegroup then --- suitable match
+ candidates = stylegroup
+ goto done
+ end
+ end
+ end
end
- --- unsucessfully reloaded; bail
- return specification.name, false, false
end
+ if true then
+ return nil, nil
+ end
+::done::
+ index = choose_size (candidates, askedsize)
+ local success, resolved, subfont = get_font_file (index)
+ if not success then
+ return nil, nil
+ end
+ report ("info", 2, "db", "Match found: %s(%d).",
+ resolved, subfont or 0)
+ return resolved, subfont
+end
- if not data.mappings then
- if not fonts_reloaded then
- return reload_db("invalid database; missing font mapping",
- resolve, nil, nil, specification)
- end
- return specification.name, false, false
- end
-
- local exact = { } --> collect exact style matches
- local synonymous = { } --> collect matching style synonyms
- local fallback --> e.g. non-matching style (fontspec is anal about this)
- local candidates = { } --> secondary results, incomplete matches
-
- local synonym_set = style_synonyms.set
- for n, face in next, data.mappings do
- local family, subfamily, fullname, prefmodifiers
- local psname, fontname, pfullname, metafamily
-
- local facenames = face.sanitized
- if facenames then
- family = facenames.family
- subfamily = facenames.subfamily
- prefmodifiers = facenames.prefmodifiers or facenames.subfamily
- fullname = facenames.fullname
- psname = facenames.psname
- fontname = facenames.fontname
- pfullname = facenames.pfullname
- metafamily = facenames.metafamily
- end
- fontname = fontname or sanitize_string(face.fontname)
- pfullname = pfullname or sanitize_string(face.fullname)
-
- if name == family
- or name == metafamily
+local resolve_fontname = function (specification, name, style)
+ local mappings = name_index.mappings
+ local fallback = nil
+ local lastresort = nil
+ style = style_category [style]
+ for i = 1, #mappings do
+ local face = mappings [i]
+ local prefmodifiers = face.prefmodifiers
+ local subfamily = face.subfamily
+ if face.fontname == name
+ or face.fullname == name
+ or face.psname == name
then
- if style == prefmodifiers then
- local continue
- exact, continue = add_to_match(exact, askedsize, face)
- if continue == false then break end
- elseif style == subfamily then
- exact = add_to_match(exact, askedsize, face)
- elseif synonym_set[style] and synonym_set[style][prefmodifiers]
- or synonym_set.regular[prefmodifiers]
- then
- --- treat synonyms for prefmodifiers as first-class
- --- (needed to prioritize DejaVu Book over Condensed)
- exact = add_to_match(exact, askedsize, face)
- elseif name == fullname
- or name == pfullname
- or name == fontname
- or name == psname
+ return face.basename, face.subfont
+ elseif face.familyname == name then
+ if prefmodifiers == style
+ or subfamily == style
then
- synonymous = add_to_match(synonymous, askedsize, face)
- elseif synonym_set[style] and synonym_set[style][subfamily]
- or synonym_set.regular[subfamily]
- then
- synonymous = add_to_match(synonymous, askedsize, face)
- elseif prefmodifiers == "regular"
- or subfamily == "regular" then
fallback = face
- else --- mark as last straw but continue
- candidates[#candidates+1] = face
- end
- else
- if name == fullname
- or name == pfullname
- or name == fontname
- or name == psname then
- local continue
- exact, continue = add_to_match(exact, askedsize, face)
- if continue == false then break end
+ elseif regular_synonym [prefmodifiers]
+ or regular_synonym [subfamily]
+ then
+ lastresort = face
end
+ elseif face.metafamily == name
+ and (regular_synonym [prefmodifiers]
+ or regular_synonym [subfamily])
+ then
+ lastresort = face
end
end
-
- local found
- if next(exact) then
- found = exact
- else
- found = synonymous
+ if fallback then
+ return fallback.basename, fallback.subfont
+ end
+ if lastresort then
+ return lastresort.basename, lastresort.subfont
end
+ return nil, nil
+end
- --- this is a monster
- if #found == 1 then
- --- “found” is really synonymous with “registered in the db”.
- local entry = found[1]
- local success, filename, subfont
- = get_font_file(data.filenames.full, entry)
- if success == true then
- report("log", 0, "resolve",
- "Font family='%s', subfamily='%s' found: %s",
- name, style, filename
- )
- return filename, subfont, 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 - askedsize)
- if difference < least then
- closest = face
- least = difference
- end
- end
- local success, filename, subfont
- = get_font_file(data.filenames.full, closest)
- if success == true then
- report("log", 0, "resolve",
- "Font family='%s', subfamily='%s' found: %s",
- name, style, filename
- )
- return filename, subfont, true
- end
- elseif fallback then
- local success, filename, subfont
- = get_font_file(data.filenames.full, fallback)
- if success == true then
- report("log", 0, "resolve",
- "No exact match for request %s; using fallback",
- specification.specification
- )
- report("log", 0, "resolve",
- "Font family='%s', subfamily='%s' found: %s",
- name, style, filename
- )
- return filename, subfont, true
- end
- elseif next(candidates) then
- --- pick the first candidate encountered
- local entry = candidates[1]
- local success, filename, subfont
- = get_font_file(data.filenames.full, entry)
- if success == true then
- report("log", 0, "resolve",
- "Font family='%s', subfamily='%s' found: %s",
- name, style, filename
- )
- return filename, subfont, true
+--[[doc--
+
+ resolve_name -- Perform a name: lookup. This first queries the
+ font families table and, if there is no match for the spec, the
+ font names table.
+ The return value is a pair consisting of the file name and the
+ subfont index if appropriate..
+
+ the request specification has the fields:
+
+ · features: table
+ · normal: set of { ccmp clig itlc kern liga locl mark mkmk rlig }
+ · ???
+ · forced: string
+ · lookup: "name"
+ · method: string
+ · name: string
+ · resolved: string
+ · size: int
+ · specification: string (== <lookup> ":" <name>)
+ · sub: string
+
+ The “size” field deserves special attention: if its value is
+ negative, then it actually specifies a scalefactor of the
+ design size of the requested font. This happens e.g. if a font is
+ requested without an explicit “at size”. If the font is part of a
+ larger collection with different design sizes, this complicates
+ matters a bit: Normally, the resolver prefers fonts that have a
+ design size as close as possible to the requested size. If no
+ size specified, then the design size is implied. But which design
+ size should that be? Xetex appears to pick the “normal” (unmarked)
+ size: with Adobe fonts this would be the one that is neither
+ “caption” nor “subhead” nor “display” &c ... For fonts by Adobe this
+ seems to be the one that does not receive a “prefmodifiers” field.
+ (IOW Adobe uses the “prefmodifiers” field to encode the design size
+ in more or less human readable format.) However, this is not true
+ of LM and EB Garamond. As this matters only where there are
+ multiple design sizes to a given font/style combination, we put a
+ workaround in place that chooses that unmarked version.
+
+ The first return value of “resolve_name” is the file name of the
+ requested font (string). It can be passed to the fullname resolver
+ get_font_file().
+ The second value is either “false” or an integer indicating the
+ subfont index in a TTC.
+
+--doc]]--
+
+--- table -> string * (int | bool)
+resolve_name = function (specification)
+ local resolved, subfont
+ if not name_index then name_index = load_names () end
+ local name = sanitize_fontname (specification.name)
+ local style = sanitize_fontname (specification.style) or "r"
+ local askedsize = specification.optsize
+
+ if askedsize then
+ askedsize = tonumber (askedsize)
+ else
+ askedsize = specification.size
+ if askedsize and askedsize >= 0 then
+ askedsize = askedsize / 65536
+ else
+ askedsize = 0
end
end
- --- no font found so far
- if not fonts_reloaded then
- --- last straw: try reloading the database
- return reload_db(
- "unresolved font name: '" .. name .. "'",
- resolve, nil, nil, specification
- )
+ resolved, subfont = resolve_familyname (specification,
+ name,
+ style,
+ askedsize)
+ if not resolved then
+ resolved, subfont = resolve_fontname (specification,
+ name,
+ style)
+ end
+ if not resolved then
+ resolved = specification.name, false
end
- --- else, default to requested name
- return specification.name, false, false
-end --- resolve()
+ if not resolved then
+ if not fonts_reloaded then
+ return reload_db ("Font not found.",
+ resolve_name,
+ specification)
+ end
+ end
+ return resolved, subfont
+end
resolve_fullpath = function (fontname, ext) --- getfilename()
- if not fonts_loaded then
- names.data = load_names()
- end
- local filenames = names.data.filenames
- local idx = filenames.base[fontname]
- or filenames.bare[fontname]
- if idx then
- return filenames.full[idx]
+ if not name_index then name_index = load_names () end
+ local files = name_index.files
+ local basedata = files.base
+ local baredata = files.bare
+ for i = 1, #location_precedence do
+ local location = location_precedence [i]
+ local basenames = basedata [location]
+ local idx
+ if basenames ~= nil then
+ idx = basenames [fontname]
+ end
+ if ext then
+ local barenames = baredata [location] [ext]
+ if not idx and barenames ~= nil then
+ idx = barenames [fontname]
+ end
+ end
+ if idx then
+ return files.full [idx]
+ end
end
return ""
end
@@ -962,14 +1212,23 @@ end
--- string -> ('a -> 'a) -> 'a list -> 'a
reload_db = function (why, caller, ...)
- report("both", 1, "db", "Reload initiated; reason: %q", why)
- names.data = update_names(names.data, false, false)
- local success = save_names()
- if success then
+ local namedata = name_index
+ local formats = tableconcat (namedata.meta.formats, ",")
+
+ report ("both", 1, "db",
+ "Reload initiated (formats: %s); reason: %q.",
+ formats, why)
+
+ set_font_filter (formats)
+ namedata = update_names (namedata, false, false)
+
+ if namedata then
fonts_reloaded = true
- return caller(...)
+ name_index = namedata
+ return caller (...)
end
- report("both", 0, "db", "Database update unsuccessful.")
+
+ report ("both", 0, "db", "Database update unsuccessful.")
end
--- string -> string -> int
@@ -1002,54 +1261,50 @@ end
--- string -> int -> bool
find_closest = function (name, limit)
- local name = sanitize_string(name)
+ local name = sanitize_fontname (name)
limit = limit or fuzzy_limit
- if not fonts_loaded then names.data = load_names() end
-
- local data = names.data
-
- if type(data) ~= "table" then
+ if not name_index then name_index = load_names () end
+ if not name_index or type (name_index) ~= "table" then
if not fonts_reloaded then
return reload_db("no database", find_closest, name)
end
return false
end
+
local by_distance = { } --- (int, string list) dict
local distances = { } --- int list
local cached = { } --- (string, int) dict
- local mappings = data.mappings
+ local mappings = name_index.mappings
local n_fonts = #mappings
for n = 1, n_fonts do
local current = mappings[n]
- local cnames = current.sanitized
--[[
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.
+ Matching is performed against the “fullname” field
+ of a db record in preprocessed form. We then store the
+ raw “fullname” at its 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
+ local fullname = current.plainname
+ local sfullname = current.fullname
+ local dist = cached[sfullname]--- maybe already calculated
- 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 not dist then
+ dist = iterative_levenshtein(name, sfullname)
+ cached[sfullname] = 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
end
--- print the matches according to their distance
@@ -1058,15 +1313,15 @@ find_closest = function (name, limit)
tablesort(distances)
limit = mathmin(n_distances, limit)
report(false, 1, "query",
- "Displaying %d distance levels", limit)
+ "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 ")
- )
+ "Distance from \"%s\": %s\n "
+ .. tableconcat (namelst, "\n "),
+ name, dist)
end
return true
@@ -1074,182 +1329,478 @@ find_closest = function (name, limit)
return false
end --- find_closest()
-local sanitize_names = function (names)
- local res = { }
- for idx, name in next, names do
- res[idx] = sanitize_string(name)
- end
- return res
-end
-
--[[doc--
-The data inside an Opentype font file can be quite heterogeneous.
-Thus in order to get the relevant information, parts of the original
-table as returned by the font file reader need to be relocated.
+
+ load_font_file -- Safely open a font file. See
+ <http://www.ntg.nl/pipermail/ntg-context/2013/075885.html>
+ regarding the omission of ``fontloader.close()``.
+
--doc]]--
---- string -> int -> bool -> string -> fontentry
-font_fullinfo = function (filename, subfont, texmf, basename)
- local tfmdata = { }
- local rawfont = fontloaderopen(filename, subfont)
+
+local load_font_file = function (filename, subfont)
+ local rawfont, _msg = fontloaderopen (filename, subfont)
if not rawfont then
- report("log", 1, "error", "Failed to open %s", filename)
+ report ("log", 1, "db", "ERROR: failed to open %s.", filename)
return
end
- local metadata = fontloader.to_table(rawfont)
- fontloader.close(rawfont)
- collectgarbage("collect")
+ return rawfont
+end
+
+--- rawdata -> (int * int * int | bool)
+
+local get_size_info = function (metadata)
+ local design_size = metadata.design_size
+ local design_range_top = metadata.design_range_top
+ local design_range_bottom = metadata.design_range_bottom
+
+ local fallback_size = design_size ~= 0 and design_size
+ or design_range_bottom ~= 0 and design_range_bottom
+ or design_range_top ~= 0 and design_range_top
+
+ if fallback_size then
+ design_size = (design_size or fallback_size) / 10
+ design_range_top = (design_range_top or fallback_size) / 10
+ design_range_bottom = (design_range_bottom or fallback_size) / 10
+ return {
+ design_size, design_range_top, design_range_bottom,
+ }
+ end
+
+ return false
+end
+
+local get_english_names = function (names, basename)
+
+ local english_names
+
+ if names then
+ --inspect(names)
+ for _, raw_namedata in next, names do
+ if raw_namedata.lang == "English (US)" then
+ english_names = raw_namedata.names
+ end
+ end
+ else
+ -- no names table, probably a broken font
+ report("log", 1, "db",
+ "Broken font %s rejected due to missing names table.",
+ basename)
+ return nil
+ end
+
+ return english_names
+end
+
+local organize_namedata = function (metadata,
+ english_names,
+ basename,
+ info)
+ local fontnames = {
+ --- see
+ --- https://developer.apple.com/fonts/TTRefMan/RM06/Chap6name.html
+ --- http://www.microsoft.com/typography/OTSPEC/name.htm#NameIDs
+ english = {
+ --- where a “compatfull” field is given, the value of “fullname” is
+ --- either identical or differs by separating the style
+ --- with a hyphen and omitting spaces. (According to the
+ --- spec, “compatfull” is “Macintosh only”.)
+ --- Of the three “fullname” fields, this one appears to be the one
+ --- with the entire name given in a legible,
+ --- non-abbreviated fashion, for most fonts at any rate.
+ --- However, in some fonts (e.g. CMU) all three fields are
+ --- identical.
+ fullname = --[[ 18 ]] english_names.compatfull
+ or --[[ 4 ]] english_names.fullname,
+ --- we keep both the “preferred family” and the “family”
+ --- values around since both are valid but can turn out
+ --- quite differently, e.g. with Latin Modern:
+ --- preffamily: “Latin Modern Sans”,
+ --- family: “LM Sans 10”
+ preffamily = --[[ 16 ]] english_names.preffamilyname,
+ family = --[[ 1 ]] english_names.family,
+ prefmodifiers = --[[ 17 ]] english_names.prefmodifiers,
+ subfamily = --[[ 2 ]] english_names.subfamily,
+ psname = --[[ 6 ]] english_names.postscriptname,
+ },
+
+ metadata = {
+ fullname = metadata.fullname,
+ fontname = metadata.fontname,
+ familyname = metadata.familyname,
+ },
+
+ info = {
+ fullname = info.fullname,
+ familyname = info.familyname,
+ fontname = info.fontname,
+ },
+ }
+
-- see http://www.microsoft.com/typography/OTSPEC/features_pt.htm#size
if metadata.fontstyle_name then
+ --- not present in all fonts, often differs from the preferred
+ --- subfamily as well as subfamily fields, e.g. with
+ --- LMSans10-BoldOblique:
+ --- subfamily: “Bold Italic”
+ --- prefmodifiers: “10 Bold Oblique”
+ --- fontstyle_name: “Bold Oblique”
for _, name in next, metadata.fontstyle_name do
if name.lang == 1033 then --- I hate magic numbers
- tfmdata.fontstyle_name = name.name
+ fontnames.fontstyle_name = name.name
end
end
end
- if metadata.names then
- for _, namedata in next, metadata.names do
- if namedata.lang == "English (US)" then
- local names = {
- --- see
- --- https://developer.apple.com/fonts/TTRefMan/RM06/Chap6name.html
- fullname = namedata.names.compatfull
- or namedata.names.fullname,
- family = namedata.names.preffamilyname
- or namedata.names.family,
- prefmodifiers = namedata.names.prefmodifiers,
- subfamily = tfmdata.fontstyle_name
- or namedata.names.subfamily,
- psname = namedata.names.postscriptname,
- pfullname = metadata.fullname,
- fontname = metadata.fontname,
- metafamily = metadata.familyname,
- }
--- tfmdata.names = names
- tfmdata.sanitized = sanitize_names(names)
- end
- end
- else
- -- no names table, propably a broken font
- report("log", 1, "db", "Broken font rejected", "%s", basefile)
- return
+
+ return {
+ sanitized = sanitize_fontnames (fontnames),
+ fontname = metadata.fontname,
+ fullname = metadata.fullname,
+ familyname = metadata.familyname,
+ }
+
+end
+
+
+local dashsplitter = lpegsplitat "-"
+
+local split_fontname = function (fontname)
+ --- sometimes the style hides in the latter part of the
+ --- fontname, separated by a dash, e.g. “Iwona-Regular”,
+ --- “GFSSolomos-Regular”
+ local splitted = { lpegmatch (dashsplitter, fontname) }
+ if next (splitted) then
+ return sanitize_fontname (splitted [#splitted])
end
- tfmdata.fontname = metadata.fontname
- tfmdata.fullname = metadata.fullname
- tfmdata.familyname = metadata.familyname
- tfmdata.weight = metadata.pfminfo.weight
- tfmdata.width = metadata.pfminfo.width
- tfmdata.slant = metadata.italicangle
- --- this is for querying
- tfmdata.units_per_em = metadata.units_per_em
- tfmdata.version = metadata.version
- -- don't waste the space with zero values
- tfmdata.size = {
- metadata.design_size ~= 0 and metadata.design_size or nil,
- metadata.design_range_top ~= 0 and metadata.design_range_top or nil,
- metadata.design_range_bottom ~= 0 and metadata.design_range_bottom or nil,
+end
+
+local organize_styledata = function (fontname,
+ metadata,
+ english_names,
+ info)
+ local pfminfo = metadata.pfminfo
+ local names = metadata.names
+
+ return {
+ -- see http://www.microsoft.com/typography/OTSPEC/features_pt.htm#size
+ size = get_size_info (metadata),
+ weight = {
+ pfminfo.weight, -- integer (multiple of 100?)
+ sanitize_fontname (info.weight), -- style name
+ },
+ split = split_fontname (fontname),
+ width = pfminfo.width,
+ italicangle = metadata.italicangle,
+-- italicangle = {
+-- metadata.italicangle, -- float
+-- info.italicangle, -- truncated to integer point size?
+-- },
+ --- this is for querying, see www.ntg.nl/maps/40/07.pdf for details
+ units_per_em = metadata.units_per_em,
+ version = metadata.version,
}
+end
- --- file location data (used to be filename field)
- tfmdata.filename = filename --> sys
- tfmdata.basename = basename --> texmf
- tfmdata.texmf = texmf or false
- tfmdata.subfont = subfont
+--[[doc--
+The data inside an Opentype font file can be quite heterogeneous.
+Thus in order to get the relevant information, parts of the original
+table as returned by the font file reader need to be relocated.
+--doc]]--
+
+--- string -> int -> bool -> string -> fontentry
- return tfmdata
+ot_fullinfo = function (filename,
+ subfont,
+ location,
+ basename,
+ format,
+ info)
+
+ local metadata = load_font_file (filename, subfont)
+ if not metadata then
+ return nil
+ end
+
+ local english_names = get_english_names (metadata.names, basename)
+ local namedata = organize_namedata (metadata,
+ english_names,
+ basename,
+ info)
+ local style = organize_styledata (namedata.fontname,
+ metadata,
+ english_names,
+ info)
+
+ local res = {
+ file = { base = basename,
+ full = filename,
+ subfont = subfont,
+ location = location or "system" },
+ format = format,
+ names = namedata,
+ style = style,
+ version = metadata.version,
+ }
+ --- Closing the file manually is a tad faster and more memory
+ --- efficient than having it closed by the gc
+ fontloaderclose (metadata)
+ return res
end
---- we return true if the fond is new or re-indexed
---- string -> dbobj -> dbobj -> bool
-local load_font = function (fullname, fontnames, newfontnames, texmf)
- if not fullname then
+--[[doc--
+
+ Type1 font inspector. In comparison with OTF, PFB’s contain a good
+ deal less name fields which makes it tricky in some parts to find a
+ meaningful representation for the database.
+
+ Good read: http://www.adobe.com/devnet/font/pdfs/5004.AFM_Spec.pdf
+
+--doc]]--
+
+--- string -> int -> bool -> string -> fontentry
+
+t1_fullinfo = function (filename, _subfont, location, basename, format)
+ local sanitized
+ local metadata = load_font_file (filename)
+ local fontname = metadata.fontname
+ local fullname = metadata.fullname
+ local familyname = metadata.familyname
+ local italicangle = metadata.italicangle
+ local splitstyle = split_fontname (fontname)
+ local style = ""
+ local weight
+
+ sanitized = sanitize_fontnames ({
+ fontname = fontname,
+ psname = fullname,
+ pfullname = fullname,
+ metafamily = family,
+ familyname = familyname,
+ weight = metadata.weight, --- string identifier
+ prefmodifiers = style,
+ })
+
+ weight = sanitized.weight
+
+ if weight == "bold" then
+ style = weight
+ end
+
+ if italicangle ~= 0 then
+ style = style .. "italic"
+ end
+
+ return {
+ basename = basename,
+ fullpath = filename,
+ subfont = false,
+ location = location or "system",
+ format = format,
+ fullname = sanitized.fullname,
+ fontname = sanitized.fontname,
+ familyname = sanitized.familyname,
+ plainname = fullname,
+ psname = sanitized.fontname,
+ version = metadata.version,
+ size = false,
+ splitstyle = splitstyle,
+ fontstyle_name = style ~= "" and style or weight,
+ weight = { metadata.pfminfo.weight,
+ weight },
+ italicangle = italicangle,
+ }
+end
+
+local loaders = {
+ dfont = ot_fullinfo,
+ otf = ot_fullinfo,
+ ttc = ot_fullinfo,
+ ttf = ot_fullinfo,
+
+ pfb = t1_fullinfo,
+ pfa = t1_fullinfo,
+}
+
+--- not side-effect free!
+
+local compare_timestamps = function (fullname,
+ currentstatus,
+ currententrystatus,
+ currentmappings,
+ targetstatus,
+ targetentrystatus,
+ targetmappings)
+
+ local currenttimestamp = currententrystatus
+ and currententrystatus.timestamp
+ local targettimestamp = lfsattributes (fullname, "modification")
+
+ if targetentrystatus ~= nil
+ and targetentrystatus.timestamp == targettimestamp then
+ report ("log", 3, "db", "Font %q already read.", fullname)
return false
end
- local newmappings = newfontnames.mappings
- local newstatus = newfontnames.status --- by full path
+ targetentrystatus.timestamp = targettimestamp
+ targetentrystatus.index = targetentrystatus.index or { }
+
+ if currenttimestamp == targettimestamp
+ and not targetentrystatus.index [1]
+ then
+ --- copy old namedata into new
+
+ for _, currentindex in next, currententrystatus.index do
+
+ local targetindex = #targetentrystatus.index
+ local fullinfo = currentmappings [currentindex]
+ local location = #targetmappings + 1
+
+ targetmappings [location] = fullinfo
+ targetentrystatus.index [targetindex + 1] = location
+ end
+
+ report ("log", 3, "db", "Font %q already indexed.", fullname)
+
+ return false
+ end
+
+ return true
+end
+
+local insert_fullinfo = function (fullname,
+ basename,
+ n_font,
+ loader,
+ format,
+ location,
+ targetmappings,
+ targetentrystatus,
+ info)
+
+ local subfont
+ if n_font ~= false then
+ subfont = n_font - 1
+ else
+ subfont = false
+ n_font = 1
+ end
+
+ local fullinfo = loader (fullname, subfont,
+ location, basename,
+ format, info)
+
+ if not fullinfo then
+ return false
+ end
+
+ local index = targetentrystatus.index [n_font]
+
+ if not index then
+ index = #targetmappings + 1
+ end
+
+ targetmappings [index] = fullinfo
+ targetentrystatus.index [n_font] = index
+
+ return true
+end
+
- local mappings = fontnames.mappings
- local status = fontnames.status
- local basename = filebasename(fullname)
- local barename = filenameonly(fullname)
+--- we return true if the font is new or re-indexed
+--- string -> dbobj -> dbobj -> bool
+
+local read_font_names = function (fullname,
+ currentnames,
+ targetnames,
+ location)
+
+ local targetmappings = targetnames.mappings
+ local targetstatus = targetnames.status --- by full path
+ local targetentrystatus = targetstatus [fullname]
+
+ if targetentrystatus == nil then
+ targetentrystatus = { }
+ targetstatus [fullname] = targetentrystatus
+ end
- local entryname = fullname
- if texmf == true then
+ local currentmappings = currentnames.mappings
+ local currentstatus = currentnames.status
+ local currententrystatus = currentstatus [fullname]
+
+ local basename = filebasename (fullname)
+ local barename = filenameonly (fullname)
+ local entryname = fullname
+
+ if location == "texmf" then
entryname = basename
end
- if names.blacklist[fullname] or names.blacklist[basename]
- then
+ --- 1) skip if blacklisted
+
+ if names.blacklist[fullname] or names.blacklist[basename] then
report("log", 2, "db",
- "Ignoring blacklisted font %q", fullname)
+ "Ignoring blacklisted font %q.", fullname)
return false
end
- local new_timestamp, current_timestamp
- current_timestamp = status[fullname]
- and status[fullname].timestamp
- new_timestamp = lfsattributes(fullname, "modification")
+ --- 2) skip if known with same timestamp
- local newentrystatus = newstatus[fullname]
- --- newentrystatus: nil | false | table
- if newentrystatus and newentrystatus.timestamp == new_timestamp then
- -- already indexed this run
+ if not compare_timestamps (fullname,
+ currentstatus,
+ currententrystatus,
+ currentmappings,
+ targetstatus,
+ targetentrystatus,
+ targetmappings)
+ then
return false
end
- newstatus[fullname] = newentrystatus or { }
- local newentrystatus = newstatus[fullname]
- newentrystatus.timestamp = new_timestamp
- newentrystatus.index = newentrystatus.index or { }
+ --- 3) new font; choose a loader, abort if unknown
- if current_timestamp == new_timestamp
- and not newentrystatus.index[1]
- then
- for _, v in next, status[fullname].index do
- local index = #newentrystatus.index
- local fullinfo = mappings[v]
- local location = #newmappings + 1
- newmappings[location] = fullinfo --- keep
- newentrystatus.index[index+1] = location --- is this actually used anywhere?
- end
- report("log", 2, "db", "Font %q already indexed", basename)
+ local format = stringlower (filesuffix (basename))
+ local loader = loaders [format] --- ot_fullinfo, t1_fullinfo
+
+ if not loader then
+ report ("both", 0, "db",
+ "Unknown format: %q, skipping.", format)
return false
end
- local info = fontloaderinfo(fullname)
- if info then
- if type(info) == "table" and #info > 1 then --- ttc
- for n_font = 1, #info do
- local fullinfo = font_fullinfo(fullname, n_font-1, texmf, basename)
- if not fullinfo then
- return false
- end
- local location = #newmappings+1
- local index = newentrystatus.index[n_font]
- if not index then index = location end
+ --- 4) get basic info, abort if fontloader can’t read it
- newmappings[index] = fullinfo
- newentrystatus.index[n_font] = index
- end
- else
- local fullinfo = font_fullinfo(fullname, false, texmf, basename)
- if not fullinfo then
- return false
- end
- local location = #newmappings+1
- local index = newentrystatus.index[1]
- if not index then index = location end
+ local info = fontloaderinfo (fullname)
+
+ if not info then
+ report ("log", 1, "db",
+ "Failed to read basic information from %q", basename)
+ return false
+ end
- newmappings[index] = fullinfo
- newentrystatus.index[1] = index
+
+ --- 5) check for subfonts and process each of them
+
+ if type (info) == "table" and #info > 1 then --- ttc
+
+ local success = false --- true if at least one subfont got read
+
+ for n_font = 1, #info do
+ if insert_fullinfo (fullname, basename, n_font,
+ loader, format, location,
+ targetmappings, targetentrystatus,
+ info)
+ then
+ success = true
+ end
end
- else --- missing info
- report("log", 1, "db", "Failed to load %q", basename)
- return false
+ return success
end
- return true
+
+ return insert_fullinfo (fullname, basename, false,
+ loader, format, location,
+ targetmappings, targetentrystatus,
+ info)
end
local path_normalize
@@ -1327,7 +1878,7 @@ local create_blacklist = function (blacklist, whitelist)
local result = { }
local dirs = { }
- report("info", 2, "db", "Blacklisting %q files and directories",
+ report("info", 2, "db", "Blacklisting %d files and directories.",
#blacklist)
for i=1, #blacklist do
local entry = blacklist[i]
@@ -1338,7 +1889,7 @@ local create_blacklist = function (blacklist, whitelist)
end
end
- report("info", 2, "db", "Whitelisting %q files", #whitelist)
+ report("info", 2, "db", "Whitelisting %d files.", #whitelist)
for i=1, #whitelist do
result[whitelist[i]] = nil
end
@@ -1387,7 +1938,7 @@ read_blacklist = function ()
line = stringsub(line, 1, cmt - 1)
end
line = stringstrip(line)
- report("log", 2, "db", "Blacklisted file %q", line)
+ report("log", 2, "db", "Blacklisted file %q.", line)
blacklist[#blacklist+1] = line
end
end
@@ -1396,22 +1947,73 @@ read_blacklist = function ()
names.blacklist = create_blacklist(blacklist, whitelist)
end
-local font_extensions = { "otf", "ttf", "ttc", "dfont" }
-local font_extensions_set = tabletohash (font_extensions)
-local p_font_extensions
+local p_font_filter
+
do
- local extns
- --tablesort (font_extensions) --- safeguard
- for i=#font_extensions, 1, -1 do
- local e = font_extensions[i]
- if not extns then
- extns = P(e)
- else
- extns = extns + P(e)
+ local current_formats = { }
+
+ local extension_pattern = function (list)
+ local pat
+ for i=#list, 1, -1 do
+ local e = list[i]
+ if not pat then
+ pat = P(e)
+ else
+ pat = pat + P(e)
+ end
end
+ pat = pat * P(-1)
+ return (1 - pat)^1 * pat
end
- extns = extns * P(-1)
- p_font_extensions = (1 - extns)^1 * extns
+
+ --- small helper to adjust the font filter pattern (--formats
+ --- option)
+
+ set_font_filter = function (formats)
+
+ if not formats or type (formats) ~= "string" then
+ return
+ end
+
+ if stringsub (formats, 1, 1) == "+" then -- add
+ formats = lpegmatch (splitcomma, stringsub (formats, 2))
+ if formats then
+ current_formats = tableappend (current_formats, formats)
+ end
+ elseif stringsub (formats, 1, 1) == "-" then -- add
+ formats = lpegmatch (splitcomma, stringsub (formats, 2))
+ if formats then
+ local newformats = { }
+ for i = 1, #current_formats do
+ local fmt = current_formats[i]
+ local include = true
+ for j = 1, #formats do
+ if current_formats[i] == formats[j] then
+ include = false
+ goto skip
+ end
+ end
+ newformats[#newformats+1] = fmt
+ ::skip::
+ end
+ current_formats = newformats
+ end
+ else -- set
+ formats = lpegmatch (splitcomma, formats)
+ if formats then
+ current_formats = formats
+ end
+ end
+
+ p_font_filter = extension_pattern (current_formats)
+ end
+
+ get_font_filter = function (formats)
+ return tablefastcopy (current_formats)
+ end
+
+ --- initialize
+ set_font_filter (luaotfloadconfig.formats)
end
local process_dir_tree
@@ -1438,9 +2040,22 @@ process_dir_tree = function (acc, dirs)
then
dirs[#dirs+1] = fullpath
elseif lfsisfile (fullpath) then
- if lpegmatch (p_font_extensions, stringlower(ent)) then
- newfiles[#newfiles+1] = fullpath
+ ent = stringlower (ent)
+
+ if lpegmatch (p_font_filter, ent) then
+ if filesuffix (ent) == "afm" then
+ --- fontloader.open() will load the afm
+ --- iff both files are in the same directory
+ local pfbpath = filereplacesuffix
+ (fullpath, "pfb")
+ if lfsisfile (pfbpath) then
+ newfiles[#newfiles+1] = pfbpath
+ end
+ else
+ newfiles[#newfiles+1] = fullpath
+ end
end
+
end
end
end
@@ -1450,88 +2065,197 @@ process_dir_tree = function (acc, dirs)
return process_dir_tree (acc, dirs)
end
---- string -> string list
-local find_font_files = function (root)
+local process_dir = function (dir)
+ local pwd = lfscurrentdir ()
+ if lfschdir (dir) then
+ lfschdir (pwd)
+
+ local files = { }
+ local blacklist = names.blacklist
+ for ent in lfsdir (dir) do
+ if ent ~= "." and ent ~= ".." and not blacklist[ent] then
+ local fullpath = dir .. "/" .. ent
+ if lfsisfile (fullpath) then
+ ent = stringlower (ent)
+ if lpegmatch (p_font_filter, ent)
+ then
+ if filesuffix (ent) == "afm" then
+ --- fontloader.open() will load the afm
+ --- iff both files are in the same
+ --- directory
+ local pfbpath = filereplacesuffix
+ (fullpath, "pfb")
+ if lfsisfile (pfbpath) then
+ files[#files+1] = pfbpath
+ end
+ else
+ files[#files+1] = fullpath
+ end
+ end
+ end
+ end
+ end
+ return files
+ end
+ return { }
+end
+
+--- string -> bool -> string list
+local find_font_files = function (root, recurse)
if lfsisdir (root) then
- return process_dir_tree ({}, { root })
+ if recurse == true then
+ return process_dir_tree ({}, { root })
+ else --- kpathsea already delivered the necessary subdirs
+ return process_dir (root)
+ end
end
end
+--- truncate_string -- Cut the first part of a string to fit it
+--- into a given terminal width. The parameter “restrict” (int)
+--- indicates the number of characters already consumed on the
+--- line.
+local truncate_string = function (str, restrict)
+ local tw = luaotfloadconfig.termwidth
+ local wd = tw - restrict
+ local len = utf8length (str)
+ if wd - len < 0 then
+ --- combined length exceeds terminal,
+ str = ".." .. stringsub(str, len - wd + 2)
+ end
+ return str
+end
+
--[[doc--
scan_dir() scans a directory and populates the list of fonts
with all the fonts it finds.
- · dirname : name of the directory to scan
- · fontnames : current font db object
- · newnames : font db object to fill
- · dry_run : don’t touch anything
+ · dirname : name of the directory to scan
+ · currentnames : current font db object
+ · targetnames : font db object to fill
+ · dry_run : don’t touch anything
--doc]]--
--- string -> dbobj -> dbobj -> bool -> bool -> (int * int)
-local scan_dir = function (dirname, fontnames, newfontnames,
- dry_run, texmf)
+
+local scan_dir = function (dirname, currentnames, targetnames,
+ dry_run, location)
if lpegmatch (p_blacklist, dirname) then
- report ("both", 3, "db",
- "Skipping blacklisted directory %s", dirname)
+ report ("both", 4, "db",
+ "Skipping blacklisted directory %s.", dirname)
--- ignore
return 0, 0
end
- local found = find_font_files (dirname)
+ local found = find_font_files (dirname, location ~= "texmf")
if not found then
- report ("both", 3, "db",
+ report ("both", 4, "db",
"No such directory: %q; skipping.", dirname)
return 0, 0
end
- report ("both", 3, "db", "Scanning directory %s", dirname)
+ report ("both", 4, "db", "Scanning directory %s.", dirname)
+
+ local n_new = 0 --- total of fonts collected
+ local n_found = #found
+ local max_fonts = luaotfloadconfig.max_fonts
- local n_new = 0 --- total of fonts collected
- local n_found = #found
- report ("both", 4, "db", "%d font files detected", n_found)
+ report ("both", 4, "db", "%d font files detected.", n_found)
for j=1, n_found do
+ if max_fonts and fonts_read >= max_fonts then
+ break
+ end
+
local fullname = found[j]
fullname = path_normalize(fullname)
local new
+
if dry_run == true then
- report ("both", 1, "db",
- "Would have been loading %q", fullname)
+ local truncated = truncate_string (fullname, 43)
+ report ("log", 2, "db",
+ "Would have been loading %s.", fullname)
+ report_status ("term", "db",
+ "Would have been loading %s", truncated)
else
- report ("both", 4, "db",
- "Loading font %q", fullname)
- local new = load_font (fullname, fontnames,
- newfontnames, texmf)
+ local truncated = truncate_string (fullname, 32)
+ report ("log", 2, "db", "Loading font %s.", fullname)
+ report_status ("term", "db", "Loading font %s", truncated)
+ local new = read_font_names (fullname, currentnames,
+ targetnames, texmf)
if new == true then
+ fonts_read = fonts_read + 1
n_new = n_new + 1
end
end
end
-
- report("both", 4, "db", "%d fonts found in '%s'", n_found, dirname)
+ report ("both", 4, "db", "Done. %d fonts indexed in %q.",
+ n_found, dirname)
return n_found, n_new
end
+--- string list -> string list
+local filter_out_pwd = function (dirs)
+ local result = { }
+ local pwd = path_normalize (lpegmatch (stripslashes,
+ lfscurrentdir ()))
+ for i = 1, #dirs do
+ --- better safe than sorry
+ local dir = path_normalize (lpegmatch (stripslashes, dirs[i]))
+ if not (dir == "." or dir == pwd) then
+ result[#result+1] = dir
+ end
+ end
+ return result
+end
+
+local path_separator = ostype == "windows" and ";" or ":"
+
+--[[doc--
+ scan_texmf_fonts() scans all fonts in the texmf tree through the
+ kpathsea variables OPENTYPEFONTS and TTFONTS of texmf.cnf.
+ The current working directory comes as “.” (texlive) or absolute
+ path (miktex) and will always be filtered out.
+--doc]]--
+
--- dbobj -> dbobj -> bool? -> (int * int)
-local scan_texmf_fonts = function (fontnames, newfontnames, dry_run)
- 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
- ]]
- if stringis_empty(kpseexpand_path("$OSFONTDIR")) then
- report("info", 2, "db", "Scanning TEXMF fonts...")
+
+local scan_texmf_fonts = function (currentnames, targetnames, dry_run)
+
+ local n_scanned, n_new, fontdirs = 0, 0
+ local osfontdir = kpseexpand_path "$OSFONTDIR"
+
+ if stringis_empty (osfontdir) then
+ report ("info", 1, "db", "Scanning TEXMF fonts...")
else
- report("info", 2, "db", "Scanning TEXMF and OS fonts...")
+ report ("info", 1, "db", "Scanning TEXMF and OS fonts...")
+ if logs.get_loglevel () > 3 then
+ local osdirs = filesplitpath (osfontdir)
+ report ("info", 0, "db",
+ "$OSFONTDIR has %d entries:", #osdirs)
+ for i = 1, #osdirs do
+ report ("info", 0, "db", "[%d] %s", i, osdirs[i])
+ end
+ end
end
- local fontdirs = stringgsub(kpseexpand_path("$OPENTYPEFONTS"), "^%.", "")
- fontdirs = fontdirs .. stringgsub(kpseexpand_path("$TTFONTS"), "^%.", "")
- if not stringis_empty(fontdirs) then
- for _,d in next, filesplitpath(fontdirs) do
- local found, new = scan_dir(d, fontnames, newfontnames, dry_run, true)
+
+ fontdirs = kpseexpand_path "$OPENTYPEFONTS"
+ fontdirs = fontdirs .. path_separator .. kpseexpand_path "$TTFONTS"
+ fontdirs = fontdirs .. path_separator .. kpseexpand_path "$T1FONTS"
+
+ if not stringis_empty (fontdirs) then
+ local tasks = filter_out_pwd (filesplitpath (fontdirs))
+ report ("info", 3, "db",
+ "Initiating scan of %d directories.", #tasks)
+ report_status_start (2, 4)
+ for _, d in next, tasks do
+ local found, new = scan_dir (d, currentnames, targetnames,
+ dry_run, "texmf")
n_scanned = n_scanned + found
n_new = n_new + new
end
+ report_status_stop ("term", "db", "Scanned %d files, %d new.", n_scanned, n_new)
end
+
return n_scanned, n_new
end
@@ -1631,7 +2355,7 @@ do --- closure for read_fonts_conf()
local fonts_conf_scanner = function (path)
local fh = ioopen(path, "r")
if not fh then
- report("both", 3, "db", "Cannot open fontconfig file %s", path)
+ report("both", 3, "db", "Cannot open fontconfig file %s.", path)
return
end
local raw = fh:read"*all"
@@ -1639,7 +2363,7 @@ do --- closure for read_fonts_conf()
local confdata = lpegmatch(p_cheapxml, raw)
if not confdata then
- report("both", 3, "db", "Cannot scan fontconfig file %s", path)
+ report("both", 3, "db", "Cannot scan fontconfig file %s.", path)
return
end
return confdata
@@ -1690,6 +2414,10 @@ do --- closure for read_fonts_conf()
--- We exclude paths with texmf in them, as they should be
--- found anyway; also duplicates are ignored by checking
--- if they are elements of dirs_done.
+ ---
+ --- FIXME does this mean we cannot access paths from
+ --- distributions (e.g. Context minimals) installed
+ --- separately?
if not (stringfind(path, "texmf") or dirs_done[path]) then
acc[#acc+1] = path
dirs_done[path] = true
@@ -1786,226 +2514,782 @@ local function get_os_dirs ()
return {}
end
---- dbobj -> dbobj -> bool? -> (int * int)
-local scan_os_fonts = function (fontnames, newfontnames, dry_run)
- 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
+--[[doc--
+
+ scan_os_fonts() scans the OS fonts through
+ - fontconfig for Unix (reads the fonts.conf file[s] and scans the
directories)
- a static set of directories for Windows and MacOSX
- ]]
- report("info", 2, "db", "Scanning OS fonts...")
- report("info", 3, "db", "Searching in static system directories...")
- for _, d in next, get_os_dirs() do
- local found, new = scan_dir(d, fontnames, newfontnames, dry_run)
+
+ **NB**: If $OSFONTDIR is nonempty, as it appears to be by default
+ on Windows setups, the system fonts will have already been
+ processed while scanning the TEXMF. Thus, this function is
+ never called.
+
+--doc]]--
+
+--- dbobj -> dbobj -> bool? -> (int * int)
+local scan_os_fonts = function (currentnames,
+ targetnames,
+ dry_run)
+
+ local n_scanned, n_new = 0, 0
+ report ("info", 1, "db", "Scanning OS fonts...")
+ report ("info", 2, "db",
+ "Searching in static system directories...")
+
+ report_status_start (2, 4)
+ for _, d in next, get_os_dirs () do
+ local found, new = scan_dir (d, currentnames,
+ targetnames, dry_run)
n_scanned = n_scanned + found
n_new = n_new + new
end
+ report_status_stop ("term", "db", "Scanned %d files, %d new.", n_scanned, n_new)
+
return n_scanned, n_new
end
--- unit -> (bool, lookup_cache)
flush_lookup_cache = function ()
- if not names.lookups then names.lookups = load_lookups() end
- names.lookups = { }
+ lookup_cache = { }
collectgarbage "collect"
- return true, names.lookups
+ return true, lookup_cache
end
---- dbobj -> dbobj
-local gen_fast_lookups = function (fontnames)
- report("both", 2, "db", "Creating filename map")
- local mappings = fontnames.mappings
+
+--- fontentry list -> filemap
+
+local generate_filedata = function (mappings)
+
+ report ("both", 2, "db", "Creating filename map.")
+
local nmappings = #mappings
- --- this is needlessly complicated due to texmf priorization
- local filenames = {
- bare = { },
- base = { },
+
+ local files = {
+ bare = {
+ ["local"] = { },
+ system = { }, --- mapped to mapping format -> index in full
+ texmf = { }, --- mapped to mapping format -> “true”
+ },
+ base = {
+ ["local"] = { },
+ system = { }, --- mapped to index in “full”
+ texmf = { }, --- set; all values are “true”
+ },
full = { }, --- non-texmf
}
- local texmf, sys = { }, { } -- quintuple list
+ local base = files.base
+ local bare = files.bare
+ local full = files.full
+
+ local conflicts = {
+ basenames = 0,
+ barenames = 0,
+ }
- for idx = 1, nmappings do
- local entry = mappings[idx]
- local filename = entry.filename
- local basename = entry.basename
- local bare = filenameonly(filename)
- local subfont = entry.subfont
+ for index = 1, nmappings do
+ local entry = mappings [index]
+
+ local filedata = entry.file
+ local format
+ local location
+ local fullpath
+ local basename
+ local barename
+ local subfont
+
+ if filedata then --- new entry
+ format = entry.format --- otf, afm, ...
+ location = filedata.location --- texmf, system, ...
+ fullpath = filedata.full
+ basename = filedata.base
+ barename = filenameonly (fullpath)
+ subfont = filedata.subfont
+ else
+ format = entry.format --- otf, afm, ...
+ location = entry.location --- texmf, system, ...
+ fullpath = entry.fullpath
+ basename = entry.basename
+ barename = filenameonly (fullpath)
+ subfont = entry.subfont
+ end
- entry.index = idx
---- unfortunately, the sys/texmf schism prevents us from
---- doing away the full name, so we cannot avoid the
---- substantial duplication
--- entry.filename = nil
+ entry.index = index
- if entry.texmf == true then
- texmf[#texmf+1] = { idx, basename, bare, true, nil }
+ --- 1) add to basename table
+
+ local inbase = base [location] --- no format since the suffix is known
+
+ if inbase then
+ local present = inbase [basename]
+ if present then
+ report ("both", 4, "db",
+ "Conflicting basename: %q already indexed \z
+ in category %s, ignoring.",
+ barename, location)
+ conflicts.basenames = conflicts.basenames + 1
+
+ --- track conflicts per font
+ local conflictdata = entry.conflicts
+
+ if not conflictdata then
+ entry.conflicts = { basename = present }
+ else -- some conflicts already detected
+ conflictdata.basename = present
+ end
+
+ else
+ inbase [basename] = index
+ end
else
- sys[#sys+1] = { idx, basename, bare, false, filename }
+ inbase = { basename = index }
+ base [location] = inbase
end
+
+ --- 2) add to barename table
+
+ local inbare = bare [location] [format]
+
+ if inbare then
+ local present = inbare [barename]
+ if present then
+ report ("both", 4, "db",
+ "Conflicting barename: %q already indexed \z
+ in category %s/%s, ignoring.",
+ barename, location, format)
+ conflicts.barenames = conflicts.barenames + 1
+
+ --- track conflicts per font
+ local conflictdata = entry.conflicts
+
+ if not conflictdata then
+ entry.conflicts = { barename = present }
+ else -- some conflicts already detected
+ conflictdata.barename = present
+ end
+
+ else
+ inbare [barename] = index
+ end
+ else
+ inbare = { [barename] = index }
+ bare [location] [format] = inbare
+ end
+
+ --- 3) add to fullpath map
+
+ full [index] = fullpath
end
- local addmap = function (lst)
- --- this will overwrite existing entries
- for i=1, #lst do
- local idx, base, bare, intexmf, full = unpack(lst[i])
+ return files
+end
+
+local pick_style
+local check_regular
- local known = filenames.base[base] or filenames.bare[bare]
- if known then --- known
- report("both", 3, "db",
- "Font file %q already indexed (%d)",
- base, idx)
- report("both", 3, "db", "> old location: %s",
- (filenames.full[known] or "texmf"))
- report("both", 3, "db", "> new location: %s",
- (intexmf and "texmf" or full))
+do
+ local splitfontname = lpeg.splitat "-"
+
+ local choose_exact = function (field)
+ if italic_synonym [field] then
+ return "i"
+ elseif field == "bold" then
+ return "b"
+ elseif field == "bolditalic" or field == "boldoblique" then
+ return "bi"
+ end
+
+ return false
+ end
+
+ pick_style = function (fontstyle_name,
+ prefmodifiers,
+ subfamily,
+ splitstyle)
+ local style
+ if fontstyle_name then
+ style = choose_exact (fontstyle_name)
+ end
+ if not style then
+ if prefmodifiers then
+ style = choose_exact (prefmodifiers)
+ elseif subfamily then
+ style = choose_exact (subfamily)
+ end
+ end
+-- if not style and splitstyle then
+-- style = choose_exact (splitstyle)
+-- end
+ return style
+ end
+
+ --- we use only exact matches here since there are constructs
+ --- like “regularitalic” (Cabin, Bodoni Old Fashion)
+
+ check_regular = function (fontstyle_name,
+ prefmodifiers,
+ subfamily,
+ splitstyle)
+
+ if fontstyle_name then
+ return regular_synonym [fontstyle_name]
+ elseif prefmodifiers then
+ return regular_synonym [prefmodifiers]
+ elseif subfamily then
+ return regular_synonym [subfamily]
+ elseif splitstyle then
+ return regular_synonym [splitstyle]
+ end
+
+ return nil
+ end
+end
+
+local pull_values = function (entry)
+ local file = entry.file
+ local names = entry.names
+ local style = entry.style
+ local sanitized = names.sanitized
+ local english = sanitized.english
+ local info = sanitized.info
+ local metadata = sanitized.metadata
+
+ --- pull file info ...
+ entry.basename = file.base
+ entry.fullpath = file.full
+ entry.location = file.location
+ entry.subfont = file.subfont
+
+ --- pull name info ...
+ entry.psname = english.psname
+ entry.fontname = info.fontname
+ entry.fullname = english.fullname or info.fullname
+ entry.prefmodifiers = english.prefmodifiers
+ local metafamily = metadata.familyname
+ local familyname = english.preffamily or english.family
+ entry.familyname = familyname
+ if familyname ~= metafamily then
+ entry.metafamily = metadata.familyname
+ end
+ entry.fontstyle_name = sanitized.fontstyle_name
+ entry.plainname = names.fullname
+ entry.subfamily = english.subfamily
+
+ --- pull style info ...
+ entry.italicangle = style.italicangle
+ entry.size = style.size
+ entry.splitstyle = style.split
+ entry.weight = style.weight
+
+ if luaotfloadconfig.strip == true then
+ entry.file = nil
+ entry.names = nil
+ entry.style = nil
+ end
+end
+
+local add_family = function (name, subtable, modifier, entry)
+ local familytable = subtable [name]
+ if not familytable then
+ familytable = { }
+ subtable [name] = familytable
+ end
+
+ --- the style table is treated as an unordered list
+ local styletable = familytable [modifier]
+ if not styletable then
+ styletable = { }
+ familytable [modifier] = styletable
+ end
+
+ if not entry.prefmodifiers then --- default size for this style/family combo
+ styletable.default = entry.index
+ end
+
+ local size = entry.size --- dsnsize * high * low
+ if size then
+ styletable [#styletable + 1] = {
+ size [1],
+ size [2],
+ size [3],
+ entry.index,
+ }
+ else
+ styletable.default = entry.index
+ end
+end
+
+local collect_families = function (mappings)
+
+ report ("info", 2, "db", "Analyzing families, sizes, and styles.")
+
+ local families = {
+ ["local"] = { },
+ system = { },
+ texmf = { },
+ }
+
+ for i = 1, #mappings do
+
+ local entry = mappings [i]
+
+ if entry.file then
+ pull_values (entry)
+ end
+
+ local location = entry.location
+ local format = entry.format
+
+ local subtable = families [location] [format]
+ if not subtable then
+ subtable = { }
+ families [location] [format] = subtable
+ end
+
+ local familyname = entry.familyname
+ local metafamily = entry.metafamily
+ local fontstyle_name = entry.fontstyle_name
+ local prefmodifiers = entry.prefmodifiers
+ local subfamily = entry.subfamily
+
+ local weight = entry.weight
+ local italicangle = entry.italicangle
+ local splitstyle = entry.splitstyle
+
+ local modifier = pick_style (fontstyle_name,
+ prefmodifiers,
+ subfamily,
+ splitstyle)
+
+ if not modifier then --- regular, exact only
+ modifier = check_regular (fontstyle_name,
+ prefmodifiers,
+ subfamily,
+ splitstyle)
+ end
+
+ if modifier then
+ add_family (familyname, subtable, modifier, entry)
+ --- registering the metafamilies is unreliable within the
+ --- same table as identifiers might interfere with an
+ --- unmarked style that lacks a metafamily, e.g.
+ ---
+ --- iwona condensed regular ->
+ --- family: iwonacond
+ --- metafamily: iwona
+ --- iwona regular ->
+ --- family: iwona
+ --- metafamily: ø
+ ---
+ --- Both would be registered as under the same family,
+ --- i.e. “iwona”, and depending on the loading order
+ --- the query “name:iwona” can resolve to the condensed
+ --- version instead of the actual unmarked one. The only
+ --- way around this would be to introduce a separate
+ --- table for metafamilies and do fallback queries on it.
+ --- At the moment this is not pressing enough to justify
+ --- further increasing the index size, maybe if need
+ --- arises from the user side.
+-- if metafamily and metafamily ~= familyname then
+-- add_family (metafamily, subtable, modifier, entry)
+-- end
+ end
+ end
+
+ collectgarbage "collect"
+ return families
+end
+
+local cmp_sizes = function (a, b)
+ return a [1] < b [1]
+end
+
+local order_design_sizes = function (families)
+
+ report ("info", 2, "db", "Ordering design sizes.")
+
+ for location, data in next, families do
+ for format, data in next, data do
+ for familyname, data in next, data do
+ for style, data in next, data do
+ tablesort (data, cmp_sizes)
+ end
end
+ end
+ end
+
+ return families
+end
+
+local retrieve_namedata = function (currentnames,
+ targetnames,
+ dry_run,
+ n_rawnames,
+ n_newnames)
+
+ local rawnames, new = scan_texmf_fonts (currentnames,
+ targetnames,
+ dry_run)
+
+ n_rawnames = n_rawnames + rawnames
+ n_newnames = n_newnames + new
+
+ rawnames, new = scan_os_fonts (currentnames, targetnames, dry_run)
- filenames.bare[bare] = idx
- filenames.base[base] = idx
- if intexmf == true then
- filenames.full[idx] = nil
+ n_rawnames = n_rawnames + rawnames
+ n_newnames = n_newnames + new
+
+ return n_rawnames, n_newnames
+end
+
+
+--- dbobj -> stats
+
+local collect_statistics = function (mappings)
+ local sum_dsnsize, n_dsnsize = 0, 0
+
+ local fullname, family, families = { }, { }, { }
+ local subfamily, prefmodifiers, fontstyle_name = { }, { }, { }
+
+ local addtohash = function (hash, item)
+ if item then
+ local times = hash [item]
+ if times then
+ hash [item] = times + 1
+ else
+ hash [item] = 1
+ end
+ end
+ end
+
+ local appendtohash = function (hash, key, value)
+ if key and value then
+ local entry = hash [key]
+ if entry then
+ entry [#entry + 1] = value
else
- filenames.full[idx] = full
+ hash [key] = { value }
end
end
end
- if config.luaotfload.prioritize == "texmf" then
- report("both", 2, "db", "Preferring texmf fonts")
- addmap(sys)
- addmap(texmf)
- else --- sys
- addmap(texmf)
- addmap(sys)
+ local addtoset = function (hash, key, value)
+ if key and value then
+ local set = hash [key]
+ if set then
+ set [value] = true
+ else
+ hash [key] = { [value] = true }
+ end
+ end
end
- fontnames.filenames = filenames
- texmf, sys = nil, nil
- collectgarbage "collect"
- return fontnames
+ local setsize = function (set)
+ local n = 0
+ for _, _ in next, set do
+ n = n + 1
+ end
+ return n
+ end
+
+ local hashsum = function (hash)
+ local n = 0
+ for _, m in next, hash do
+ n = n + m
+ end
+ return n
+ end
+
+ for _, entry in next, mappings do
+ local style = entry.style
+ local names = entry.names.sanitized
+ local englishnames = names.english
+
+ addtohash (fullname, englishnames.fullname)
+ addtohash (family, englishnames.family)
+ addtohash (subfamily, englishnames.subfamily)
+ addtohash (prefmodifiers, englishnames.prefmodifiers)
+ addtohash (fontstyle_name, names.fontstyle_name)
+
+ addtoset (families, englishnames.family, englishnames.fullname)
+
+ local sizeinfo = entry.style.size
+ if sizeinfo then
+ sum_dsnsize = sum_dsnsize + sizeinfo [1]
+ n_dsnsize = n_dsnsize + 1
+ end
+ end
+
+ --inspect (families)
+
+ local n_fullname = setsize (fullname)
+ local n_family = setsize (family)
+
+ if logs.get_loglevel () > 1 then
+ local pprint_top = function (hash, n, set)
+
+ local freqs = { }
+ local items = { }
+
+ for item, value in next, hash do
+ if set then
+ freq = setsize (value)
+ else
+ freq = value
+ end
+ local ifreq = items [freq]
+ if ifreq then
+ ifreq [#ifreq + 1] = item
+ else
+ items [freq] = { item }
+ freqs [#freqs + 1] = freq
+ end
+ end
+
+ tablesort (freqs)
+
+ local from = #freqs
+ local to = from - (n - 1)
+ if to < 1 then
+ to = 1
+ end
+
+ for i = from, to, -1 do
+ local freq = freqs [i]
+ local itemlist = items [freq]
+
+ if type (itemlist) == "table" then
+ itemlist = tableconcat (itemlist, ", ")
+ end
+
+ report ("both", 0, "db",
+ " · %4d × %s.",
+ freq, itemlist)
+ end
+ end
+
+ report ("both", 0, "", "~~~~ font index statistics ~~~~")
+ report ("both", 0, "db",
+ " · Collected %d fonts (%d names) in %d families.",
+ #mappings, n_fullname, n_family)
+ pprint_top (families, 4, true)
+
+ report ("both", 0, "db",
+ " · %d different “subfamily” kinds.",
+ setsize (subfamily))
+ pprint_top (subfamily, 4)
+
+ report ("both", 0, "db",
+ " · %d different “prefmodifiers” kinds.",
+ setsize (prefmodifiers))
+ pprint_top (prefmodifiers, 4)
+
+ report ("both", 0, "db",
+ " · %d different “fontstyle_name” kinds.",
+ setsize (fontstyle_name))
+ pprint_top (fontstyle_name, 4)
+ end
+
+ local mean_dsnsize = 0
+ if n_dsnsize > 0 then
+ mean_dsnsize = sum_dsnsize / n_dsnsize
+ end
+
+ return {
+ mean_dsnsize = mean_dsnsize,
+ names = {
+ fullname = n_fullname,
+ families = n_family,
+ },
+-- style = {
+-- subfamily = subfamily,
+-- prefmodifiers = prefmodifiers,
+-- fontstyle_name = fontstyle_name,
+-- },
+ }
end
--- force: dictate rebuild from scratch
--- dry_dun: don’t write to the db, just scan dirs
--- dbobj? -> bool? -> bool? -> dbobj
-update_names = function (fontnames, force, dry_run)
- if config.luaotfload.update_live == false then
- report("info", 2, "db",
- "skipping database update")
+update_names = function (currentnames, force, dry_run)
+
+ local targetnames
+
+ if luaotfloadconfig.update_live == false then
+ report ("info", 2, "db",
+ "Skipping database update.")
--- skip all db updates
- return fontnames or names.data
+ return currentnames or name_index
end
- local starttime = os.gettimeofday()
- local n_scanned, n_new = 0, 0
+
+ local starttime = osgettimeofday ()
+ local n_rawnames, n_newnames = 0, 0
+
--[[
The main function, scans everything
- - “newfontnames” is the final table to return
+ - “targetnames” is the final table to return
- force is whether we rebuild it from scratch or not
]]
- report("both", 2, "db", "Updating the font names database"
- .. (force and " forcefully" or ""))
-
- if force then
- fontnames = fontnames_init(false)
+ report("both", 1, "db", "Updating the font names database"
+ .. (force and " forcefully." or "."))
+
+ --- pass 1 get raw data: read font files (normal case) or reuse
+ --- information present in index
+
+ if luaotfloadconfig.skip_read == true then
+ --- the difference to a “dry run” is that we don’t search
+ --- for font files entirely. we also ignore the “force”
+ --- parameter since it concerns only the font files.
+ report ("info", 2, "db",
+ "Ignoring font files, reusing old data.")
+ currentnames = load_names (false)
+ targetnames = currentnames
else
- if not fontnames then
- fontnames = load_names(dry_run)
- end
- if fontnames.version ~= names.version then
- report("both", 1, "db", "No font names database or old "
- .. "one found; generating new one")
- fontnames = fontnames_init(true)
+ if force then
+ currentnames = initialize_namedata (get_font_filter ())
+ else
+ if not currentnames then
+ currentnames = load_names (dry_run)
+ end
+ if currentnames.meta.version ~= names.version then
+ report ("both", 1, "db", "No font names database or old "
+ .. "one found; generating new one.")
+ currentnames = initialize_namedata (get_font_filter ())
+ end
end
- end
- local newfontnames = fontnames_init(true)
- read_blacklist()
- local scanned, new
- scanned, new = scan_texmf_fonts(fontnames, newfontnames, dry_run)
- n_scanned = n_scanned + scanned
- n_new = n_new + new
+ targetnames = initialize_namedata (get_font_filter ())
- scanned, new = scan_os_fonts(fontnames, newfontnames, dry_run)
- n_scanned = n_scanned + scanned
- n_new = n_new + new
+ read_blacklist ()
+
+ local n_raw, n_new= retrieve_namedata (currentnames,
+ targetnames,
+ dry_run,
+ n_rawnames,
+ n_newnames)
+ report ("info", 3, "db",
+ "Scanned %d font files; %d new entries.",
+ n_rawnames, n_newnames)
+ end
+
+ --- pass 2 (optional): collect some stats about the raw font info
+ if luaotfloadconfig.statistics == true then
+ targetnames.meta.statistics = collect_statistics
+ (targetnames.mappings)
+ end
--- we always generate the file lookup tables because
--- non-texmf entries are redirected there and the mapping
--- needs to be 100% consistent
- newfontnames = gen_fast_lookups(newfontnames)
- --- stats:
- --- before rewrite | after rewrite
- --- partial: 804 ms | 701 ms
- --- forced: 45384 ms | 44714 ms
- report("info", 3, "db",
- "Scanned %d font files; %d new entries.", n_scanned, n_new)
- report("info", 3, "db",
- "Rebuilt in %0.f ms", 1000*(os.gettimeofday()-starttime))
- return newfontnames
-end
+ --- pass 3: build filename table
+ targetnames.files = generate_filedata (targetnames.mappings)
+
+ --- pass 4: build family lookup table
+ targetnames.families = collect_families (targetnames.mappings)
+
+ --- pass 5: order design size tables
+ targetnames.families = order_design_sizes (targetnames.families)
---- The lookup cache is an experimental feature of version 2.2;
---- instead of incorporating it into the database it gets its own
---- file. As we update it after every single addition this saves us
---- quite some time.
+
+ report ("info", 3, "db",
+ "Rebuilt in %0.f ms.",
+ 1000 * (osgettimeofday () - starttime))
+ name_index = targetnames
+
+ if dry_run ~= true then
+
+ save_names ()
+
+ local success, _lookups = flush_lookup_cache ()
+ if success then
+ local success = save_lookups ()
+ if success then
+ logs.names_report ("info", 2, "cache",
+ "Lookup cache emptied.")
+ return targetnames
+ end
+ end
+ end
+ return targetnames
+end
--- unit -> bool
save_lookups = function ( )
- local lookups = names.lookups
local path = names.path.lookups
local luaname, lucname = path.lua, path.luc
if fileiswritable (luaname) and fileiswritable (lucname) then
- tabletofile (luaname, lookups, true)
+ tabletofile (luaname, lookup_cache, true)
osremove (lucname)
- caches.compile (lookups, luaname, lucname)
+ caches.compile (lookup_cache, luaname, lucname)
--- double check ...
if lfsisfile (luaname) and lfsisfile (lucname) then
- report ("both", 3, "cache", "Lookup cache saved")
+ report ("both", 3, "cache", "Lookup cache saved.")
return true
end
- report ("info", 0, "cache", "Could not compile lookup cache")
+ report ("info", 0, "cache", "Could not compile lookup cache.")
return false
end
- report ("info", 0, "cache", "Lookup cache file not writable")
+ report ("info", 0, "cache", "Lookup cache file not writable.")
if not fileiswritable (luaname) then
- report ("info", 0, "cache", "Failed to write %s", luaname)
+ report ("info", 0, "cache", "Failed to write %s.", luaname)
end
if not fileiswritable (lucname) then
- report ("info", 0, "cache", "Failed to write %s", lucname)
+ report ("info", 0, "cache", "Failed to write %s.", lucname)
end
return false
end
--- save_names() is usually called without the argument
--- dbobj? -> bool
-save_names = function (fontnames)
- if not fontnames then fontnames = names.data end
+save_names = function (currentnames)
+ if not currentnames then
+ currentnames = name_index
+ end
local path = names.path.index
local luaname, lucname = path.lua, path.luc
if fileiswritable (luaname) and fileiswritable (lucname) then
- tabletofile (luaname, fontnames, true)
osremove (lucname)
- caches.compile (fontnames, luaname, lucname)
- if lfsisfile (luaname) and lfsisfile (lucname) then
- report ("info", 1, "db", "Font index saved")
- report ("info", 3, "db", "Text: " .. luaname)
- report ("info", 3, "db", "Byte: " .. lucname)
+ local gzname = luaname .. ".gz"
+ if luaotfloadconfig.compress then
+ local serialized = tableserialize (currentnames, true)
+ save_gzipped (gzname, serialized)
+ caches.compile (currentnames, "", lucname)
+ else
+ tabletofile (luaname, currentnames, true)
+ caches.compile (currentnames, luaname, lucname)
+ end
+ report ("info", 2, "db", "Font index saved at ...")
+ local success = false
+ if lfsisfile (luaname) then
+ report ("info", 2, "db", "Text: " .. luaname)
+ success = true
+ end
+ if lfsisfile (gzname) then
+ report ("info", 2, "db", "Gzip: " .. gzname)
+ success = true
+ end
+ if lfsisfile (lucname) then
+ report ("info", 2, "db", "Byte: " .. lucname)
+ success = true
+ end
+ if success then
return true
+ else
+ report ("info", 0, "db", "Could not compile font index.")
+ return false
end
- report ("info", 0, "db", "Could not compile font index")
- return false
end
report ("info", 0, "db", "Index file not writable")
if not fileiswritable (luaname) then
- report ("info", 0, "db", "Failed to write %s", luaname)
+ report ("info", 0, "db", "Failed to write %s.", luaname)
end
if not fileiswritable (lucname) then
- report ("info", 0, "db", "Failed to write %s", lucname)
+ report ("info", 0, "db", "Failed to write %s.", lucname)
end
return false
end
@@ -2031,24 +3315,24 @@ end
--- string -> string -> string list -> bool -> bool
local purge_from_cache = function (category, path, list, all)
- report("info", 2, "cache", "Luaotfload cache: %s %s",
+ report("info", 1, "cache", "Luaotfload cache: %s %s",
(all and "erase" or "purge"), category)
- report("info", 2, "cache", "location: %s",path)
+ report("info", 1, "cache", "location: %s",path)
local n = 0
for i=1,#list do
local filename = list[i]
if stringfind(filename,"luatex%-cache") then -- safeguard
if all then
- report("info", 5, "cache", "removing %s", filename)
+ report("info", 5, "cache", "Removing %s.", filename)
osremove(filename)
n = n + 1
else
- local suffix = file.suffix(filename)
+ local suffix = filesuffix(filename)
if suffix == "lua" then
local checkname = file.replacesuffix(
filename, "lua", "luc")
- if lfs.isfile(checkname) then
- report("info", 5, "cache", "Removing %s", filename)
+ if lfsisfile(checkname) then
+ report("info", 5, "cache", "Removing %s.", filename)
osremove(filename)
n = n + 1
end
@@ -2056,7 +3340,7 @@ local purge_from_cache = function (category, path, list, all)
end
end
end
- report("info", 2, "cache", "Removed lua files : %i", n)
+ report("info", 1, "cache", "Removed lua files : %i", n)
return true
end
@@ -2073,7 +3357,7 @@ local collect_cache collect_cache = function (path, all, n, luanames,
local filename = all[n]
if filename then
- local suffix = file.suffix(filename)
+ local suffix = filesuffix(filename)
if suffix == "lua" then
luanames[#luanames+1] = filename
elseif suffix == "luc" then
@@ -2089,7 +3373,7 @@ end
local getwritablecachepath = function ( )
--- fonts.handlers.otf doesn’t exist outside a Luatex run,
--- so we have to improvise
- local writable = getwritablepath (config.luaotfload.cache_dir)
+ local writable = getwritablepath (luaotfloadconfig.cache_dir)
if writable then
return writable
end
@@ -2097,7 +3381,7 @@ end
local getreadablecachepaths = function ( )
local readables = caches.getreadablepaths
- (config.luaotfload.cache_dir)
+ (luaotfloadconfig.cache_dir)
local result = { }
if readables then
for i=1, #readables do
@@ -2145,7 +3429,7 @@ local show_cache = function ( )
separator ()
print_cache ("writable path", writable_path,
luanames, lucnames, rest)
- texiowrite_nl""
+ texio.write_nl""
for i=1,#readable_paths do
local readable_path = readable_paths[i]
if readable_path ~= writable_path then
@@ -2163,16 +3447,19 @@ end
-----------------------------------------------------------------------
names.scan_dir = scan_dir
+names.set_font_filter = set_font_filter
names.flush_lookup_cache = flush_lookup_cache
names.save_lookups = save_lookups
names.load = load_names
+names.data = function () return name_index end
names.save = save_names
names.update = update_names
names.crude_file_lookup = crude_file_lookup
names.crude_file_lookup_verbose = crude_file_lookup_verbose
names.read_blacklist = read_blacklist
-names.sanitize_string = sanitize_string
+names.sanitize_fontname = sanitize_fontname
names.getfilename = resolve_fullpath
+names.set_location_precedence = set_location_precedence
--- font cache
names.purge_cache = purge_cache
@@ -2180,15 +3467,16 @@ names.erase_cache = erase_cache
names.show_cache = show_cache
--- replace the resolver from luatex-fonts
-if config.luaotfload.resolver == "cached" then
- report("both", 2, "cache", "caching of name: lookups active")
- names.resolve = resolve_cached
- names.resolvespec = resolve_cached
+if luaotfloadconfig.resolver == "cached" then
+ report("both", 2, "cache", "Caching of name: lookups active.")
+ names.resolvespec = resolve_cached
+ names.resolve_name = resolve_cached
else
- names.resolve = resolve
- names.resolvespec = resolve
+ names.resolvespec = resolve_name
+ names.resolve_name = resolve_name
end
-names.find_closest = find_closest
+
+names.find_closest = find_closest
-- for testing purpose
names.read_fonts_conf = read_fonts_conf
diff --git a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-diagnostics.lua b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-diagnostics.lua
new file mode 100644
index 00000000000..68ed18c456b
--- /dev/null
+++ b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-diagnostics.lua
@@ -0,0 +1,681 @@
+#!/usr/bin/env texlua
+-----------------------------------------------------------------------
+-- FILE: luaotfload-diagnostics.lua
+-- DESCRIPTION: functionality accessible by the --diagnose option
+-- REQUIREMENTS: luaotfload-tool.lua
+-- AUTHOR: Philipp Gesang (Phg), <phg42.2a@gmail.com>
+-- VERSION: 2.4
+-- CREATED: 2013-07-28 10:01:18+0200
+-----------------------------------------------------------------------
+--
+local names = fonts.names
+local luatexstatus = status
+local status = config.luaotfload.status
+
+local kpse = require "kpse"
+local kpseexpand_path = kpse.expand_path
+local kpseexpand_var = kpse.expand_var
+local kpsefind_file = kpse.find_file
+
+local lfs = require "lfs"
+local lfsattributes = lfs.attributes
+local lfsisfile = lfs.isfile
+local lfsreadlink = lfs.readlink
+
+local md5 = require "md5"
+local md5sumhexa = md5.sumhexa
+
+local ioopen = io.open
+
+local osgetenv = os.getenv
+local osname = os.name
+local osremove = os.remove
+local ostype = os.type
+local stringformat = string.format
+local stringlower = string.lower
+local stringsub = string.sub
+
+local fileisreadable = file.isreadable
+local fileiswritable = file.iswritable
+local filesplitpath = file.splitpath
+local filesuffix = file.suffix
+local ioloaddata = io.loaddata
+local lua_of_json = utilities.json.tolua
+local tableconcat = table.concat
+local tablesortedkeys = table.sortedkeys
+local tabletohash = table.tohash
+
+local lpeg = require "lpeg"
+local C, Cg, Ct = lpeg.C, lpeg.Cg, lpeg.Ct
+local lpegmatch = lpeg.match
+
+local out = function (...)
+ logs.names_report (false, 0, "diagnose", ...)
+end
+
+local check_index = function (errcnt)
+
+ out "================= font names =================="
+ local namedata = names.data()
+
+ if not namedata then
+ namedata = names.load ()
+ end
+
+ local mappings = namedata.mappings
+
+ if not namedata and namedata.formats and namedata.version then
+ out "Database corrupt."
+ return errcnt + 1
+ end
+
+ out ("Database version: %.3f.", namedata.meta.version)
+ out ("Font formats indexed: %s.",
+ tableconcat (namedata.meta.formats, ", "))
+ out ("%d font files indexed.", #mappings)
+
+ local by_format = { }
+ for i = 1, #mappings do
+ local record = mappings[i]
+ local format = stringlower (filesuffix (record.filename))
+ local count = by_format[format]
+ if count then
+ by_format[format] = count + 1
+ else
+ by_format[format] = 1
+ end
+ end
+
+ local formats = tablesortedkeys (by_format)
+ for i = 1, #formats do
+ local format = formats[i]
+ out ("%20s: %5d", format, by_format[format])
+ end
+ return errcnt
+end
+
+local verify_files = function (errcnt, status)
+ out "================ verify files ================="
+ local hashes = status.hashes
+ local notes = status.notes
+ if not hashes or #hashes == 0 then
+ out ("FAILED: cannot read checksums from %s.", status_file)
+ return 1/0
+ elseif not notes then
+ out ("FAILED: cannot read commit metadata from %s.",
+ status_file)
+ return 1/0
+ end
+
+ out ("Luaotfload revision %s.", notes.revision)
+ out ("Committed by %s.", notes.committer)
+ out ("Timestamp %s.", notes.timestamp)
+
+ local nhashes = #hashes
+ out ("Testing %d files for integrity.", nhashes)
+ for i = 1, nhashes do
+ local fname, canonicalsum = unpack (hashes[i])
+ local location = kpsefind_file (fname)
+ or kpsefind_file (fname, "texmfscripts")
+ if not location then
+ errcnt = errcnt + 1
+ out ("FAILED: file %s missing.", fname)
+ else
+ out ("File: %s.", location)
+ local raw = ioloaddata (location)
+ if not raw then
+ errcnt = errcnt + 1
+ out ("FAILED: file %d not readable.", fname)
+ else
+ local sum = md5sumhexa (raw)
+ if sum ~= canonicalsum then
+ errcnt = errcnt + 1
+ out ("FAILED: checksum mismatch for file %s.",
+ fname)
+ out ("Expected %s.", canonicalsum)
+ out ("Got %s.", sum)
+ else
+ out ("Ok, %s passed.", fname)
+ end
+ end
+ end
+ end
+ return errcnt
+end
+
+local get_tentative_attributes = function (file)
+ if not lfsisfile (file) then
+ local chan = ioopen (file, "w")
+ if chan then
+ chan:close ()
+ local attributes = lfsattributes (file)
+ os.remove (file)
+ return attributes
+ end
+ end
+end
+
+local p_permissions = Ct(Cg(Ct(C(1) * C(1) * C(1)), "u")
+ * Cg(Ct(C(1) * C(1) * C(1)), "g")
+ * Cg(Ct(C(1) * C(1) * C(1)), "o"))
+
+local analyze_permissions = function (raw)
+ return lpegmatch (p_permissions, raw)
+end
+
+local stripslashes = names.patterns.stripslashes
+
+local get_permissions = function (t, location)
+ if stringsub (location, #location) == "/" then
+ --- strip trailing slashes (lfs idiosyncrasy on Win)
+ location = lpegmatch (stripslashes, location)
+ end
+ local attributes = lfsattributes (location)
+
+ if not attributes and t == "f" then
+ attributes = get_tentative_attributes (location)
+ if not attributes then
+ return false
+ end
+ end
+
+ local permissions
+
+ if fileisreadable (location) then
+ --- link handling appears to be unnecessary because
+ --- lfs.attributes() will return the information on
+ --- the link target.
+ if mode == "link" then --follow and repeat
+ location = lfsreadlink (location)
+ attributes = lfsattributes (location)
+ end
+ end
+
+ permissions = analyze_permissions (attributes.permissions)
+
+ return {
+ location = location,
+ mode = attributes.mode,
+ owner = attributes.uid, --- useless on windows
+ permissions = permissions,
+ attributes = attributes,
+ }
+end
+
+local check_conformance = function (spec, permissions, errcnt)
+ local uid = permissions.attributes.uid
+ local gid = permissions.attributes.gid
+ local raw = permissions.attributes.permissions
+
+ out ("Owner: %d, group %d, permissions %s.", uid, gid, raw)
+ if ostype == "unix" then
+ if uid == 0 or gid == 0 then
+ out "Owned by the superuser, permission conflict likely."
+ errcnt = errcnt + 1
+ end
+ end
+
+ local user = permissions.permissions.u
+ if spec.r == true then
+ if user[1] == "r" then
+ out "Readable: ok."
+ else
+ out "Not readable: permissions need fixing."
+ errcnt = errcnt + 1
+ end
+ end
+
+ if spec.w == true then
+ if user[2] == "w"
+ or fileiswritable (permissions.location) then
+ out "Writable: ok."
+ else
+ out "Not writable: permissions need fixing."
+ errcnt = errcnt + 1
+ end
+ end
+
+ return errcnt
+end
+
+local path = names.path
+
+local desired_permissions = {
+ { "d", {"r","w"}, function () return caches.getwritablepath () end },
+ { "d", {"r","w"}, path.globals.prefix },
+ { "f", {"r","w"}, path.index.lua .. ".gz" },
+ { "f", {"r","w"}, path.index.luc },
+ { "f", {"r","w"}, path.lookups.lua },
+ { "f", {"r","w"}, path.lookups.luc },
+}
+
+local check_permissions = function (errcnt)
+ out [[=============== file permissions ==============]]
+ for i = 1, #desired_permissions do
+ local t, spec, path = unpack (desired_permissions[i])
+ if type (path) == "function" then
+ path = path ()
+ end
+
+ spec = tabletohash (spec)
+
+ out ("Checking permissions of %s.", path)
+
+ local permissions = get_permissions (t, path)
+ if permissions then
+ --inspect (permissions)
+ errcnt = check_conformance (spec, permissions, errcnt)
+ else
+ errcnt = errcnt + 1
+ end
+ end
+ return errcnt
+end
+
+local check_upstream
+
+if kpsefind_file ("https.lua", "lua") == nil then
+ check_upstream = function (errcnt)
+ out [[============= upstream repository =============
+ WARNING: Cannot retrieve repository data.
+ Github API access requires the luasec library.
+ Grab it from <https://github.com/brunoos/luasec>
+ and retry.]]
+ return errcnt
+ end
+else
+--- github api stuff begin
+ local https = require "ssl.https"
+
+ local gh_api_root = [[https://api.github.com]]
+ local release_url = [[https://github.com/lualatex/luaotfload/releases]]
+ local luaotfload_repo = [[lualatex/luaotfload]]
+ local user_agent = [[lualatex/luaotfload integrity check]]
+ local shortbytes = 8
+
+ local gh_shortrevision = function (rev)
+ return stringsub (rev, 1, shortbytes)
+ end
+
+ local gh_encode_parameters = function (parameters)
+ local acc = {}
+ for field, value in next, parameters do
+ --- unsafe, non-urlencoded coz it’s all ascii chars
+ acc[#acc+1] = field .. "=" .. value
+ end
+ return "?" .. tableconcat (acc, "&")
+ end
+
+ local gh_make_url = function (components, parameters)
+ local url = tableconcat ({ gh_api_root,
+ unpack (components) },
+ "/")
+ if parameters then
+ url = url .. gh_encode_parameters (parameters)
+ end
+ return url
+ end
+
+ local alright = [[HTTP/1.1 200 OK]]
+
+ local gh_api_request = function (...)
+ local args = {...}
+ local nargs = #args
+ local final = args[nargs]
+ local request = {
+ url = "",
+ headers = { ["user-agent"] = user_agent },
+ }
+ if type (final) == "table" then
+ args[nargs] = nil
+ request = gh_make_url (args, final)
+ else
+ request = gh_make_url (args)
+ end
+
+ out ("Requesting <%s>.", request)
+ local response, code, headers, status
+ = https.request (request)
+ if status ~= alright then
+ out "Request failed!"
+ return false
+ end
+ return response
+ end
+
+ local gh_api_checklimit = function (headers)
+ local rawlimit = gh_api_request "rate_limit"
+ local limitdata = lua_of_json (rawlimit)
+ if not limitdata and limitdata.rate then
+ out "Cannot parse API rate limit."
+ return false
+ end
+ limitdata = limitdata.rate
+
+ local limit = tonumber (limitdata.limit)
+ local left = tonumber (limitdata.remaining)
+ local reset = tonumber (limitdata.reset)
+
+ out ("%d of %d Github API requests left.", left, limit)
+ if left == 0 then
+ out ("Cannot make any more API requests.")
+ if ostype == "unix" then
+ out ("Try again later at %s.", osdate ("%F %T", reset))
+ else --- windows doesn’t C99
+ out ("Try again later at %s.",
+ osdate ("%Y-%m-d %H:%M:%S", reset))
+ end
+ end
+ return true
+ end
+
+ local gh_tags = function ()
+ out "Fetching tags from repository, please stand by."
+ local rawtags = gh_api_request ("repos",
+ luaotfload_repo,
+ "tags")
+ local taglist = lua_of_json (rawtags)
+ if not taglist or #taglist == 0 then
+ out "Cannot parse response."
+ return false
+ end
+
+ local ntags = #taglist
+ out ("Repository contains %d tags.", ntags)
+ local _idx, latest = next (taglist)
+ out ("The most recent release is %s (revision %s).",
+ latest.name,
+ gh_shortrevision (latest.commit.sha))
+ return latest
+ end
+
+ local gh_compare = function (head, base)
+ if base == nil then
+ base = "HEAD"
+ end
+ out ("Fetching comparison between %s and %s, \z
+ please stand by.",
+ gh_shortrevision (head),
+ gh_shortrevision (base))
+ local comparison = base .. "..." .. head
+ local rawstatus = gh_api_request ("repos",
+ luaotfload_repo,
+ "compare",
+ comparison)
+ local status = lua_of_json (rawstatus)
+ if not status then
+ out "Cannot parse response for status request."
+ return false
+ end
+ return status
+ end
+
+ local gh_news = function (since)
+ local compared = gh_compare (since)
+ if not compared then
+ return false
+ end
+ local behind_by = compared.behind_by
+ local ahead_by = compared.ahead_by
+ local status = compared.status
+ out ("Comparison state: %s.", status)
+ if behind_by > 0 then
+ out ("Your Luaotfload is %d \z
+ revisions behind upstream.",
+ behind_by)
+ return behind_by
+ elseif status == "ahead" then
+ out "Since you are obviously from the future \z
+ I assume you already know the repository state."
+ else
+ out "Everything up to date. \z
+ Luaotfload is in sync with upstream."
+ end
+ return false
+ end
+
+ local gh_catchup = function (current, latest)
+ local compared = gh_compare (latest, current)
+ local ahead_by = tonumber (compared.ahead_by)
+ if ahead_by > 0 then
+ local permalink_url = compared.permalink_url
+ out ("Your Luaotfload is %d revisions \z
+ behind the most recent release.",
+ ahead_by)
+ out ("To view the commit log, visit <%s>.",
+ permalink_url)
+ out ("You can grab an up to date tarball at <%s>.",
+ release_url)
+ return true
+ else
+ out "There weren't any new releases in the meantime."
+ out "Luaotfload is up to date."
+ end
+ return false
+ end
+
+ check_upstream = function (current)
+ out "============= upstream repository ============="
+ local _succ = gh_api_checklimit ()
+ local behind = gh_news (current)
+ if behind then
+ local latest = gh_tags ()
+ local _behind = gh_catchup (current,
+ latest.commit.sha,
+ latest.name)
+ end
+ end
+
+ --- trivium: diff since the first revision as pushed by Élie
+ --- in 2009
+ --- local firstrevision = "c3ccb3ee07e0a67171c24960966ae974e0dd8e98"
+ --- check_upstream (firstrevision)
+end
+--- github api stuff end
+
+local print_envvar = function (var)
+ local val = osgetenv (var)
+ if val then
+ out ("%20s: %q", stringformat ("$%s", var), val)
+ return val
+ else
+ out ("%20s: <unset>", stringformat ("$%s", var))
+ end
+end
+
+local print_path = function (var)
+ local val = osgetenv (var)
+ if val then
+ local paths = filesplitpath (val)
+ if paths then
+ local npaths = #paths
+ if npaths == 1 then
+ out ("%20s: %q", stringformat ("$%s", var), val)
+ elseif npaths > 1 then
+ out ("%20s: <%d items>", stringformat ("$%s", var), npaths)
+ for i = 1, npaths do
+ out (" +: %q", paths[i])
+ end
+ else
+ out ("%20s: <empty>")
+ end
+ end
+ else
+ out ("%20s: <unset>", stringformat ("$%s", var))
+ end
+end
+
+local print_kpsevar = function (var)
+ var = "$" .. var
+ local val = kpseexpand_var (var)
+ if val and val ~= var then
+ out ("%20s: %q", var, val)
+ return val
+ else
+ out ("%20s: <empty or unset>", var)
+ end
+end
+
+local print_kpsepath = function (var)
+ var = "$" .. var
+ local val = kpseexpand_path (var)
+ if val and val ~= "" then
+ local paths = filesplitpath (val)
+ if paths then
+ local npaths = #paths
+ if npaths == 1 then
+ out ("%20s: %q", var, paths[1])
+ elseif npaths > 1 then
+ out ("%20s: <%d items>", var, npaths)
+ for i = 1, npaths do
+ out (" +: %q", paths[i])
+ end
+ else
+ out ("%20s: <empty>")
+ end
+ end
+ else
+ out ("%20s: <empty or unset>", var)
+ end
+end
+
+--- this test first if a variable is set and then expands the
+--- paths; this is necessitated by the fact that expand-path will
+--- return the empty string both if the variable is unset and if
+--- the directory does not exist
+
+local print_kpsepathvar = function (var)
+ local vvar = "$" .. var
+ local val = kpseexpand_var (vvar)
+ if val and val ~= vvar then
+ out ("%20s: %q", vvar, val)
+ print_kpsepath (var)
+ else
+ out ("%20s: <empty or unset>", var)
+ end
+end
+
+local check_environment = function (errcnt)
+ out "============ environment settings ============="
+ out ("system: %s/%s", ostype, osname)
+ if ostype == "unix" and io.popen then
+ local chan = io.popen ("uname -a", "r")
+ if chan then
+ out ("info: %s", chan:read "*all")
+ chan:close ()
+ end
+ end
+
+ out "1) *shell environment*"
+ print_envvar "SHELL"
+ print_path "PATH"
+ print_path "OSFONTDIR"
+ print_envvar "USER"
+ if ostype == "windows" then
+ print_envvar "WINDIR"
+ print_envvar "CD"
+ print_path "TEMP"
+ elseif ostype == "unix" then
+ print_envvar "HOME"
+ print_envvar "PWD"
+ print_path "TMPDIR"
+ end
+
+ out "2) *kpathsea*"
+ print_kpsepathvar "OPENTYPEFONTS"
+ print_kpsepathvar "TTFONTS"
+
+ print_kpsepathvar "TEXMFCACHE"
+ print_kpsepathvar "TEXMFVAR"
+
+ --- the expansion of these can be quite large; as they aren’t
+ --- usually essential to luaotfload, we won’t dump every single
+ --- path
+ print_kpsevar "LUAINPUTS"
+ print_kpsevar "CLUAINPUTS"
+
+ return errcnt
+end
+
+local anamneses = {
+ "environment",
+ "files",
+ "index",
+ "repository",
+ "permissions"
+}
+
+local splitcomma = names.patterns.splitcomma
+
+local diagnose = function (job)
+ local errcnt = 0
+ local asked = job.asked_diagnostics
+ if asked == "all" or asked == "thorough" then
+ asked = tabletohash (anamneses, true)
+ else
+ asked = lpegmatch (splitcomma, asked)
+ asked = tabletohash (asked, true)
+ end
+
+ if asked.index == true then
+ errcnt = check_index (errcnt)
+ asked.index = nil
+ end
+
+ if asked.environment == true then
+ errcnt = check_environment (errcnt)
+ asked.environment = nil
+ end
+
+ if asked.files == true then
+ errcnt = verify_files (errcnt, status)
+ asked.files = nil
+ end
+
+ if asked.permissions == true then
+ errcnt = check_permissions (errcnt)
+ asked.permissions = nil
+ end
+
+ if asked.repository == true then
+ check_upstream (status.notes.revision)
+ asked.repository = nil
+ end
+
+ local rest = next (asked)
+ if rest ~= nil then --> something unknown
+ out ("Unknown diagnostic %q.", rest)
+ end
+ if errcnt == 0 then --> success
+ out ("Everything appears to be in order, \z
+ you may sleep well.")
+ return true, false
+ end
+ out ( [[===============================================
+ WARNING
+ ===============================================
+
+ The diagnostic detected %d errors.
+
+ This version of luaotfload may have been
+ tampered with. Modified versions of the
+ luaotfload source are unsupported. Read the log
+ carefully and get a clean version from CTAN or
+ github:
+
+ × http://ctan.org/tex-archive/macros/luatex/generic/luaotfload
+ × https://github.com/lualatex/luaotfload/releases
+
+ If you are uncertain as to how to proceed, then
+ ask on the lualatex mailing list:
+
+ http://www.tug.org/mailman/listinfo/lualatex-dev
+
+ ===============================================
+]], errcnt)
+ return true, false
+end
+
+return diagnose
+
+-- vim:tw=71:sw=4:ts=4:expandtab
diff --git a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-extralibs.lua b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-extralibs.lua
deleted file mode 100644
index 3769e06741b..00000000000
--- a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-extralibs.lua
+++ /dev/null
@@ -1,501 +0,0 @@
-if not modules then modules = { } end modules ["extralibs"] = {
- version = "2.3a",
- comment = "companion to luaotfload.lua",
- author = "Hans Hagen, Philipp Gesang",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "GPL v.2.0",
-}
-
--- extralibs: set up an emulation layer to load additional Context
--- libraries
-
---===================================================================--
---- PREPARE
---===================================================================--
-
-local getmetatable = getmetatable
-local require = require
-local select = select
-local setmetatable = setmetatable
-local tonumber = tonumber
-
-local texattribute = tex.attribute
-
-local new_node = node.new
-local copy_node = node.copy
-local otffeatures = fonts.constructors.newfeatures "otf"
-
------------------------------------------------------------------------
---- namespace
------------------------------------------------------------------------
-
---- The “typesetters” namespace isn’t bad at all; there is no need
---- to remove it after loading.
-
-typesetters = typesetters or { }
-local typesetters = typesetters
-typesetters.kerns = typesetters.kerns or { }
-local kerns = typesetters.kerns
-kerns.mapping = kerns.mapping or { }
-kerns.factors = kerns.factors or { }
-
-local kern_callback = "typesetters.kerncharacters"
-
-typesetters.kernfont = typesetters.kernfont or { }
-local kernfont = typesetters.kernfont
-
------------------------------------------------------------------------
---- node-ini
------------------------------------------------------------------------
-
-nodes = nodes or { } --- should be present with luaotfload
-local bothways = function (t) return table.swapped (t, t) end
-
-local kerncodes = bothways({
- [0] = "fontkern",
- [1] = "userkern",
- [2] = "accentkern",
-})
-
-kerncodes.kerning = kerncodes.fontkern --- idiosyncrasy
-
-nodes.kerncodes = kerncodes
-
-nodes.skipcodes = bothways({
- [ 0] = "userskip",
- [ 1] = "lineskip",
- [ 2] = "baselineskip",
- [ 3] = "parskip",
- [ 4] = "abovedisplayskip",
- [ 5] = "belowdisplayskip",
- [ 6] = "abovedisplayshortskip",
- [ 7] = "belowdisplayshortskip",
- [ 8] = "leftskip",
- [ 9] = "rightskip",
- [ 10] = "topskip",
- [ 11] = "splittopskip",
- [ 12] = "tabskip",
- [ 13] = "spaceskip",
- [ 14] = "xspaceskip",
- [ 15] = "parfillskip",
- [ 16] = "thinmuskip",
- [ 17] = "medmuskip",
- [ 18] = "thickmuskip",
- [100] = "leaders",
- [101] = "cleaders",
- [102] = "xleaders",
- [103] = "gleaders",
-})
-
------------------------------------------------------------------------
---- node-res
------------------------------------------------------------------------
-
-nodes.pool = nodes.pool or { }
-local pool = nodes.pool
-
-local kern = new_node("kern", nodes.kerncodes.userkern)
-local glue_spec = new_node "glue_spec"
-
-pool.kern = function (k)
- local n = copy_node(kern)
- n.kern = k
- return n
-end
-
-pool.gluespec = function (width, stretch, shrink,
- stretch_order, shrink_order)
- local s = copy_node(glue_spec)
- if width then s.width = width end
- if stretch then s.stretch = stretch end
- if shrink then s.shrink = shrink end
- if stretch_order then s.stretch_order = stretch_order end
- if shrink_order then s.shrink_order = shrink_order end
- return s
-end
-
-pool.glue = function (width, stretch, shrink,
- stretch_order, shrink_order)
- local n = new_node"glue"
- if not width then
- -- no spec
- elseif width == false or tonumber(width) then
- local s = copy_node(glue_spec)
- if width then s.width = width end
- if stretch then s.stretch = stretch end
- if shrink then s.shrink = shrink end
- if stretch_order then s.stretch_order = stretch_order end
- if shrink_order then s.shrink_order = shrink_order end
- n.spec = s
- else
- -- shared
- n.spec = copy_node(width)
- end
- return n
-end
-
------------------------------------------------------------------------
---- font-hsh
------------------------------------------------------------------------
---- some initialization resembling font-hsh
-local fonthashes = fonts.hashes
-local identifiers = fonthashes.identifiers --- was: fontdata
-local chardata = fonthashes.characters
-local quaddata = fonthashes.quads
-local markdata = fonthashes.marks
-local parameters = fonthashes.parameters
-
---- ('a, 'a) hash -> (('a, 'a) hash -> 'a -> 'a) -> ('a, 'a) hash
-local setmetatableindex = function (t, f)
- local mt = getmetatable(t)
- if mt then
- mt.__index = f
- else
- setmetatable(t, { __index = f })
- end
- return t
-end
-
-if not parameters then
- parameters = { }
- setmetatableindex(parameters, function(t, k)
- if k == true then
- return parameters[currentfont()]
- else
- local parameters = identifiers[k].parameters
- t[k] = parameters
- return parameters
- end
- end)
- fonthashes.parameters = parameters
-end
-
-if not chardata then
- chardata = { }
- setmetatableindex(chardata, function(t, k)
- if k == true then
- return chardata[currentfont()]
- else
- local tfmdata = identifiers[k]
- if not tfmdata then --- unsafe
- tfmdata = font.fonts[k]
- end
- if tfmdata then
- local characters = tfmdata.characters
- t[k] = characters
- return characters
- end
- end
- end)
- fonthashes.characters = chardata
-end
-
-if not quaddata then
- quaddata = { }
- setmetatableindex(quaddata, function(t, k)
- if k == true then
- return quads[currentfont()]
- else
- local parameters = parameters[k]
- local quad = parameters and parameters.quad or 0
- t[k] = quad
- return quad
- end
- end)
- fonthashes.quads = quaddata
-end
-
-if not markdata then
- markdata = { }
- setmetatableindex(markdata, function(t, k)
- if k == true then
- return marks[currentfont()]
- else
- local resources = identifiers[k].resources or { }
- local marks = resources.marks or { }
- t[k] = marks
- return marks
- end
- end)
- fonthashes.marks = markdata
-end
-
---- next stems from the multilingual interface
-interfaces = interfaces or { }
-interfaces.variables = interfaces.variables or { }
-interfaces.variables.max = "max"
-
------------------------------------------------------------------------
---- attr-ini
------------------------------------------------------------------------
-
-attributes = attributes or { } --- to be removed with cleanup
-
-local hidden = {
- a_kerns = luatexbase.new_attribute("typo-krn:a_kerns", true),
- a_fontkern = luatexbase.new_attribute("typo-krn:a_fontkern", true),
-}
-
-attributes.private = attributes.private or function (attr_name)
- local res = hidden[attr_name]
- if not res then
- res = luatexbase.new_attribute(attr_name)
- end
- return res
-end
-
-if luatexbase.get_unset_value then
- attributes.unsetvalue = luatexbase.get_unset_value()
-else -- old luatexbase
- attributes.unsetvalue = (luatexbase.luatexversion < 37) and -1
- or -2147483647
-end
-
------------------------------------------------------------------------
---- luat-sto
------------------------------------------------------------------------
-
---- Storage is so ridiculously well designed in Context it’s a pity
---- we can’t just force every package author to use it.
-
-storage = storage or { }
-storage.register = storage.register or function (...)
- local t = { ... }
- --- sorry
- return t
-end
-
------------------------------------------------------------------------
---- node-fin
------------------------------------------------------------------------
-
-local plugin_store = { }
-
-local installattributehandler = function (plugin)
- --- Context has some load() magic here.
- plugin_store[plugin.name] = plugin.processor
-end
-
-nodes.installattributehandler = installattributehandler
-
------------------------------------------------------------------------
---- node-tsk
------------------------------------------------------------------------
-
-nodes.tasks = nodes.tasks or { }
-nodes.tasks.enableaction = function () end
-
------------------------------------------------------------------------
---- core-ctx
------------------------------------------------------------------------
-
-commands = commands or { }
-
---===================================================================--
---- LOAD
---===================================================================--
-
---- we should be ready at this moment to insert the libraries
-
-require "luaotfload-typo-krn" --- typesetters.kerns
-require "luaotfload-letterspace" --- typesetters.kernfont
-
---===================================================================--
---- CLEAN
---===================================================================--
---- interface
------------------------------------------------------------------------
-
-local factors = kerns.factors
-local mapping = kerns.mapping
-local unsetvalue = attributes.unset_value
-local process_kerns = plugin_store.kern
-
---- kern_callback : normal
---- · callback: process_kerns
---- · enabler: enablecharacterkerning
---- · disabler: disablecharacterkerning
---- · interface: kerns.set
-
---- kernfont_callback : fontwise
---- · callback: kernfont.handler
---- · enabler: enablefontkerning
---- · disabler: disablefontkerning
-
---- callback wrappers
-
---- (node_t -> node_t) -> string -> string list -> bool
-local registered_as = { } --- procname -> callbacks
-local add_processor = function (processor, name, ...)
- local callbacks = { ... }
- for i=1, #callbacks do
- luatexbase.add_to_callback(callbacks[i], processor, name)
- end
- registered_as[name] = callbacks --- for removal
- return true
-end
-
---- string -> bool
-local remove_processor = function (name)
- local callbacks = registered_as[name]
- if callbacks then
- for i=1, #callbacks do
- luatexbase.remove_from_callback(callbacks[i], name)
- end
- return true
- end
- return false --> unregistered
-end
-
---- we use the same callbacks as a node processor in Context
---- unit -> bool
-local enablecharacterkerning = function ( )
- return add_processor(function (head)
- return process_kerns("kerns", hidden.a_kerns, head)
- end,
- "typesetters.kerncharacters",
- "pre_linebreak_filter", "hpack_filter"
- )
-end
-
---- unit -> bool
-local disablecharacterkerning = function ( )
- return remove_processor "typesetters.kerncharacters"
-end
-
-kerns.enablecharacterkerning = enablecharacterkerning
-kerns.disablecharacterkerning = disablecharacterkerning
-
---- now for the simplistic variant
---- unit -> bool
-local enablefontkerning = function ( )
- return add_processor(
- kernfont.handler,
- "typesetters.kernfont",
- "pre_linebreak_filter", "hpack_filter"
- )
-end
-
---- unit -> bool
-local disablefontkerning = function ( )
- return remove_processor "typesetters.kernfont"
-end
-
---- fontwise kerning uses a font property for passing along the
---- letterspacing factor
-
-local fontkerning_enabled = false --- callback state
-
---- fontobj -> float -> unit
-local initializefontkerning = function (tfmdata, factor)
- if factor ~= "max" then
- factor = tonumber(factor) or 0
- end
- if factor == "max" or factor ~= 0 then
- local fontproperties = tfmdata.properties
- if fontproperties then
- --- hopefully this field stays unused otherwise
- fontproperties.kerncharacters = factor
- end
- if not fontkerning_enabled then
- fontkerning_enabled = enablefontkerning()
- end
- end
-end
-
---- like the font colorization, fontwise kerning is hooked into the
---- feature mechanism
-
-otffeatures.register {
- name = "letterspace", --"kerncharacters",
- description = "letterspace", --"kerncharacters",
- initializers = {
- base = initializefontkerning,
- node = initializefontkerning,
- }
-}
-
-kerns.set = nil
-
-local characterkerning_enabled = false
-
-kerns.set = function (factor)
- if factor ~= "max" then
- factor = tonumber(factor) or 0
- end
- if factor == "max" or factor ~= 0 then
- if not characterkerning_enabled then
- enablecharacterkerning()
- characterkerning_enabled = true
- end
- local a = factors[factor]
- if not a then
- a = #mapping + 1
- factors[factors], mapping[a] = a, factor
- end
- factor = a
- else
- factor = unsetvalue
- end
- texattribute[hidden.a_kerns] = factor
- return factor
-end
-
-
-
------------------------------------------------------------------------
---- options
------------------------------------------------------------------------
-
-kerns .keepligature = false --- supposed to be of type function
-kerns .keeptogether = false --- supposed to be of type function
-kernfont.keepligature = false --- supposed to be of type function
-kernfont.keeptogether = false --- supposed to be of type function
-
------------------------------------------------------------------------
---- erase fake Context layer
------------------------------------------------------------------------
-
-attributes = nil
---commands = nil --- used in lualibs
-storage = nil --- not to confuse with utilities.storage
-nodes.tasks = nil
-
-collectgarbage"collect"
-
---[[example--
-
-\input luaotfload.sty
-\def\setcharacterkerning#1{% #1 factor : float
- \directlua{typesetters.kerns.set(0.618)}%
-}
-%directlua{typesetters.kerns.enablecharacterkerning()}
-
-\font\iwona = "name:Iwona:mode=node" at 42pt
-\font\lmregular = "name:Latin Modern Roman:mode=node" at 42pt
-
-{\iwona
- foo
- {\setcharacterkerning{0.618}%
- bar}
- baz}
-
-{\lmregular
- foo {\setcharacterkerning{0.125}ff fi ffi fl Th} baz}
-
-{\lmregular
- \directlua{ %% I’m not exactly sure how those work
- typesetters.kerns.keepligature = function (start)
- print("[liga]", start)
- return true
- end
- typesetters.kerns.keeptogether = function (start)
- print("[keeptogether]", start)
- return true
- end}%
- foo {\setcharacterkerning{0.125}ff fi ffi fl Th} baz}
-
-\bye
---example]]--
-
--- vim:ts=2:sw=2:expandtab
diff --git a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-features.lua b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-features.lua
index 40883799c54..4b2f20619ff 100644
--- a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-features.lua
+++ b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-features.lua
@@ -1,5 +1,5 @@
if not modules then modules = { } end modules ["features"] = {
- version = "2.3a",
+ version = "2.4",
comment = "companion to luaotfload.lua",
author = "Hans Hagen, Khaled Hosny, Elie Roux, Philipp Gesang",
copyright = "PRAGMA ADE / ConTeXt Development Team",
@@ -14,11 +14,25 @@ local lpegmatch = lpeg.match
---[[ begin included font-ltx.lua ]]
--- this appears to be based in part on luatex-fonts-def.lua
-local fonts = fonts
+local fonts = fonts
+local definers = fonts.definers
+local handlers = fonts.handlers
+
+local as_script, normalize
+
+if handlers then
+ normalize = handlers.otf.features.normalize
+else
+ normalize = function () end
+ as_script = true
+end
+
--HH A bit of tuning for definitions.
-fonts.constructors.namemode = "specification" -- somehow latex needs this (changed name!) => will change into an overload
+if fonts.constructors then
+ fonts.constructors.namemode = "specification" -- somehow latex needs this (changed name!) => will change into an overload
+end
--[[HH--
tricky: we sort of bypass the parser and directly feed all into
@@ -39,9 +53,6 @@ local stringformat = string.format
local stringis_empty = string.is_empty
local mathceil = math.ceil
---- TODO an option to dump the default features for a script would make
---- a nice addition to luaotfload-tool
-
local defaults = {
dflt = {
"ccmp", "locl", "rlig", "liga", "clig",
@@ -1086,9 +1097,9 @@ local select_lookup = function (request)
end
local supported = {
- b = "bold",
- i = "italic",
- bi = "bolditalic",
+ b = "b",
+ i = "i",
+ bi = "bi",
aat = false,
icu = false,
gr = false,
@@ -1185,20 +1196,21 @@ local handle_request = function (specification)
--- The next line sets the “rand” feature to “random”; I haven’t
--- investigated it any further (luatex-fonts-ext), so it will
--- just stay here.
- specification.features.normal
- = fonts.handlers.otf.features.normalize(request.features)
+ specification.features.normal = normalize (request.features)
return specification
end
-local compare_requests = function (spec)
- local old = old_behavior(spec)
- local new = handle_request(spec)
- return new
+if as_script == true then --- skip the remainder of the file
+ fonts.names.handle_request = handle_request
+ report ("log", 5, "load",
+ "Exiting early from luaotfload-features.lua.")
+ return
+else
+ local registersplit = definers.registersplit
+ registersplit (":", handle_request, "cryptic")
+ registersplit ("", handle_request, "more cryptic") -- catches \font\text=[names]
end
-fonts.definers.registersplit(":", handle_request, "cryptic")
-fonts.definers.registersplit("", handle_request, "more cryptic") -- catches \font\text=[names]
-
---[[ end included font-ltx.lua ]]
--[[doc--
@@ -1411,9 +1423,13 @@ local tlig = {
[0x2014] = {0x002D, 0x002D, 0x002D}, -- emdash
[0x201C] = {0x2018, 0x2018}, -- quotedblleft
[0x201D] = {0x2019, 0x2019}, -- quotedblright
- [0x201E] = {0x002C, 0x002C}, -- quotedblbase
[0x00A1] = {0x0021, 0x2018}, -- exclamdown
[0x00BF] = {0x003F, 0x2018}, -- questiondown
+ --- next three originate in T1 encoding; Xetex applies
+ --- them too
+ [0x201E] = {0x002C, 0x002C}, -- quotedblbase
+ [0x00AB] = {0x003C, 0x003C}, -- LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
+ [0x00BB] = {0x003E, 0x003E}, -- RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
},
flags = { },
},
@@ -1430,8 +1446,8 @@ local tlig = {
},
}
-otf.addfeature("tlig", tlig)
-otf.addfeature("trep", { }) -- empty, all in tlig now
+otf.addfeature ("tlig", tlig)
+otf.addfeature ("trep", { })
local anum_arabic = { --- these are the same as in font-otc
[0x0030] = 0x0660,
diff --git a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-files.lua b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-files.lua
deleted file mode 100644
index 5722dd328ce..00000000000
--- a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-files.lua
+++ /dev/null
@@ -1,27 +0,0 @@
-return {
- { "mkglyphlist", "6bbd75b34221405e6fec73e44c72bd5f" },
- { "mkfilelist", "6b6c10784fb9470563d94e6d33927102" },
- { "mkcharacters", "df124e7b73de233413ae40f34f999e09" },
- { "luaotfload-typo-krn.lua", "a9f56a5da5a746cba726ba8a99949f20" },
- { "luaotfload-tool.lua", "cc71b0945f7f1823e97164cd64f746ce" },
- { "luaotfload-override.lua", "4c1addb1ac5a7d2160eef93910f0d798" },
- { "luaotfload-merged.lua", "bebd7c272bff23b052e61ec175a65157" },
- { "luaotfload.lua", "22bae222bd9cc447fc725d1a53c410a3" },
- { "luaotfload-loaders.lua", "059708af31aa0dad947ac6b364c0516e" },
- { "luaotfload-letterspace.lua", "c04aacdae748eab277e2b12459119d91" },
- { "luaotfload-glyphlist.lua", "8c832da1bec6cc8d310cc300a88de7f7" },
- { "luaotfload-fonts-tfm.lua", "8fd3865240e4e87e99e0739abeda2322" },
- { "luaotfload-fonts-lua.lua", "50b8edb1db7009b6c661ab71ff24a466" },
- { "luaotfload-fonts-ext.lua", "0eee87fb26b7d135da88ac0a43a8037a" },
- { "luaotfload-fonts-enc.lua", "32775266bfc9eb6bbd9a16e3a7de398d" },
- { "luaotfload-fonts-def.lua", "8dc05f2aa96d2fd0245242c8fd3320eb" },
- { "luaotfload-fonts-cbk.lua", "c1b7a4e5508d68f45a99cdc38c2d0885" },
- { "luaotfload-features.lua", "ae7a7a3d35e0e82061322b2d70daa624" },
- { "luaotfload-extralibs.lua", "e61f9181f04517e5a82abcaa07f01940" },
- { "luaotfload-database.lua", "79d98631c74adfbef9528c4c8d2a67be" },
- { "luaotfload-colors.lua", "096892eb4abf20caa879eee6a45eea6b" },
- { "luaotfload-characters.lua", "5843d02ff98a7419fae5a3cf904b101e" },
- { "luaotfload-basics-nod.lua", "9bacf64945d60f497127cee0cd987ab1" },
- { "luaotfload-basics-gen.lua", "ebf0bb51a55b81f3e04a46b3a6890edc" },
- { "luaotfload-auxiliary.lua", "f5780d55ef990d77bd66f56f0329be36" },
-} \ No newline at end of file
diff --git a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-merged.lua b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-fontloader.lua
index c17a54182ed..177324145e3 100644
--- a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-merged.lua
+++ b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-fontloader.lua
@@ -1,6 +1,6 @@
-- merged file : luatex-fonts-merged.lua
-- parent file : luatex-fonts.lua
--- merge date : 07/14/13 14:53:44
+-- merge date : 12/24/13 17:52:44
do -- begin closure to overcome local limits and interference
@@ -127,21 +127,30 @@ local dquote=P('"')
local space=P(" ")
local period=P(".")
local comma=P(",")
-local utfbom_32_be=P('\000\000\254\255')
-local utfbom_32_le=P('\255\254\000\000')
-local utfbom_16_be=P('\255\254')
-local utfbom_16_le=P('\254\255')
-local utfbom_8=P('\239\187\191')
+local utfbom_32_be=P('\000\000\254\255')
+local utfbom_32_le=P('\255\254\000\000')
+local utfbom_16_be=P('\254\255')
+local utfbom_16_le=P('\255\254')
+local utfbom_8=P('\239\187\191')
local utfbom=utfbom_32_be+utfbom_32_le+utfbom_16_be+utfbom_16_le+utfbom_8
local utftype=utfbom_32_be*Cc("utf-32-be")+utfbom_32_le*Cc("utf-32-le")+utfbom_16_be*Cc("utf-16-be")+utfbom_16_le*Cc("utf-16-le")+utfbom_8*Cc("utf-8")+alwaysmatched*Cc("utf-8")
+local utfstricttype=utfbom_32_be*Cc("utf-32-be")+utfbom_32_le*Cc("utf-32-le")+utfbom_16_be*Cc("utf-16-be")+utfbom_16_le*Cc("utf-16-le")+utfbom_8*Cc("utf-8")
local utfoffset=utfbom_32_be*Cc(4)+utfbom_32_le*Cc(4)+utfbom_16_be*Cc(2)+utfbom_16_le*Cc(2)+utfbom_8*Cc(3)+Cc(0)
local utf8next=R("\128\191")
+patterns.utfbom_32_be=utfbom_32_be
+patterns.utfbom_32_le=utfbom_32_le
+patterns.utfbom_16_be=utfbom_16_be
+patterns.utfbom_16_le=utfbom_16_le
+patterns.utfbom_8=utfbom_8
+patterns.utf_16_be_nl=P("\000\r\000\n")+P("\000\r")+P("\000\n")
+patterns.utf_16_le_nl=P("\r\000\n\000")+P("\r\000")+P("\n\000")
patterns.utf8one=R("\000\127")
patterns.utf8two=R("\194\223")*utf8next
patterns.utf8three=R("\224\239")*utf8next*utf8next
patterns.utf8four=R("\240\244")*utf8next*utf8next*utf8next
patterns.utfbom=utfbom
patterns.utftype=utftype
+patterns.utfstricttype=utfstricttype
patterns.utfoffset=utfoffset
local utf8char=patterns.utf8one+patterns.utf8two+patterns.utf8three+patterns.utf8four
local validutf8char=utf8char^0*endofstring*Cc(true)+Cc(false)
@@ -913,10 +922,13 @@ local function sortedhash(t,cmp)
s=sortedkeys(t)
end
local n=0
+ local m=#s
local function kv(s)
- n=n+1
- local k=s[n]
- return k,t[k]
+ if n<m then
+ n=n+1
+ local k=s[n]
+ return k,t[k]
+ end
end
return kv,s
else
@@ -1659,6 +1671,24 @@ function table.sorted(t,...)
sort(t,...)
return t
end
+function table.values(t,s)
+ if t then
+ local values,keys,v={},{},0
+ for key,value in next,t do
+ if not keys[value] then
+ v=v+1
+ values[v]=value
+ keys[k]=key
+ end
+ end
+ if s then
+ sort(values)
+ end
+ return values
+ else
+ return {}
+ end
+end
end -- closure
@@ -2416,9 +2446,9 @@ function string.booleanstring(str)
end
function string.is_boolean(str,default)
if type(str)=="string" then
- if str=="true" or str=="yes" or str=="on" or str=="t" then
+ if str=="true" or str=="yes" or str=="on" or str=="t" or str=="1" then
return true
- elseif str=="false" or str=="no" or str=="off" or str=="f" then
+ elseif str=="false" or str=="no" or str=="off" or str=="f" or str=="0" then
return false
end
end
@@ -2604,6 +2634,31 @@ function number.signed(i)
return "-",-i
end
end
+local zero=P("0")^1/""
+local plus=P("+")/""
+local minus=P("-")
+local separator=S(".")
+local digit=R("09")
+local trailing=zero^1*#S("eE")
+local exponent=(S("eE")*(plus+Cs((minus*zero^0*P(-1))/"")+minus)*zero^0*(P(-1)*Cc("0")+P(1)^1))
+local pattern_a=Cs(minus^0*digit^1*(separator/""*trailing+separator*(trailing+digit)^0)*exponent)
+local pattern_b=Cs((exponent+P(1))^0)
+function number.sparseexponent(f,n)
+ if not n then
+ n=f
+ f="%e"
+ end
+ local tn=type(n)
+ if tn=="string" then
+ local m=tonumber(n)
+ if m then
+ return lpegmatch((f=="%e" or f=="%E") and pattern_a or pattern_b,format(f,m))
+ end
+ elseif tn=="number" then
+ return lpegmatch((f=="%e" or f=="%E") and pattern_a or pattern_b,format(f,n))
+ end
+ return tostring(n)
+end
local preamble=[[
local type = type
local tostring = tostring
@@ -2621,6 +2676,8 @@ local tracedchar = string.tracedchar
local autosingle = string.autosingle
local autodouble = string.autodouble
local sequenced = table.sequenced
+local formattednumber = number.formatted
+local sparseexponent = number.sparseexponent
]]
local template=[[
%s
@@ -2693,6 +2750,14 @@ local format_E=function(f)
n=n+1
return format("format('%%%sE',a%s)",f,n)
end
+local format_j=function(f)
+ n=n+1
+ return format("sparseexponent('%%%se',a%s)",f,n)
+end
+local format_J=function(f)
+ n=n+1
+ return format("sparseexponent('%%%sE',a%s)",f,n)
+end
local format_x=function(f)
n=n+1
return format("format('%%%sx',a%s)",f,n)
@@ -2817,6 +2882,43 @@ end
local format_W=function(f)
return format("nspaces[%s]",tonumber(f) or 0)
end
+local digit=patterns.digit
+local period=patterns.period
+local three=digit*digit*digit
+local splitter=Cs (
+ (((1-(three^1*period))^1+C(three))*(Carg(1)*three)^1+C((1-period)^1))*(P(1)/""*Carg(2))*C(2)
+)
+patterns.formattednumber=splitter
+function number.formatted(n,sep1,sep2)
+ local s=type(s)=="string" and n or format("%0.2f",n)
+ if sep1==true then
+ return lpegmatch(splitter,s,1,".",",")
+ elseif sep1=="." then
+ return lpegmatch(splitter,s,1,sep1,sep2 or ",")
+ elseif sep1=="," then
+ return lpegmatch(splitter,s,1,sep1,sep2 or ".")
+ else
+ return lpegmatch(splitter,s,1,sep1 or ",",sep2 or ".")
+ end
+end
+local format_m=function(f)
+ n=n+1
+ if not f or f=="" then
+ f=","
+ end
+ return format([[formattednumber(a%s,%q,".")]],n,f)
+end
+local format_M=function(f)
+ n=n+1
+ if not f or f=="" then
+ f="."
+ end
+ return format([[formattednumber(a%s,%q,",")]],n,f)
+end
+local format_z=function(f)
+ n=n+(tonumber(f) or 1)
+ return "''"
+end
local format_rest=function(s)
return format("%q",s)
end
@@ -2850,11 +2952,13 @@ local builder=Cs { "start",
+V("c")+V("C")+V("S")
+V("Q")
+V("N")
-+V("r")+V("h")+V("H")+V("u")+V("U")+V("p")+V("b")+V("t")+V("T")+V("l")+V("L")+V("I")+V("h")
-+V("w")
++V("r")+V("h")+V("H")+V("u")+V("U")+V("p")+V("b")+V("t")+V("T")+V("l")+V("L")+V("I")+V("w")
+V("W")
+V("a")
-+V("A")
++V("A")
++V("j")+V("J")
++V("m")+V("M")
++V("z")
+V("*")
)+V("*")
)*(P(-1)+Carg(1))
@@ -2890,6 +2994,11 @@ local builder=Cs { "start",
["I"]=(prefix_any*P("I"))/format_I,
["w"]=(prefix_any*P("w"))/format_w,
["W"]=(prefix_any*P("W"))/format_W,
+ ["j"]=(prefix_any*P("j"))/format_j,
+ ["J"]=(prefix_any*P("J"))/format_J,
+ ["m"]=(prefix_tab*P("m"))/format_m,
+ ["M"]=(prefix_tab*P("M"))/format_M,
+ ["z"]=(prefix_any*P("z"))/format_z,
["a"]=(prefix_any*P("a"))/format_a,
["A"]=(prefix_any*P("A"))/format_A,
["*"]=Cs(((1-P("%"))^1+P("%%")/"%%")^1)/format_rest,
@@ -3023,6 +3132,7 @@ local remapper={
fea="font feature files",
pfa="type1 fonts",
pfb="type1 fonts",
+ afm="afm",
}
function resolvers.findfile(name,fileformat)
name=string.gsub(name,"\\","/")
@@ -3041,6 +3151,10 @@ function resolvers.findfile(name,fileformat)
return found
end
resolvers.findbinfile=resolvers.findfile
+function resolvers.loadbinfile(filename,filetype)
+ local data=io.loaddata(filename)
+ return true,data,#data
+end
function resolvers.resolve(s)
return s
end
@@ -3349,10 +3463,12 @@ nodes.handlers={}
local nodecodes={} for k,v in next,node.types () do nodecodes[string.gsub(v,"_","")]=k end
local whatcodes={} for k,v in next,node.whatsits() do whatcodes[string.gsub(v,"_","")]=k end
local glyphcodes={ [0]="character","glyph","ligature","ghost","left","right" }
+local disccodes={ [0]="discretionary","explicit","automatic","regular","first","second" }
nodes.nodecodes=nodecodes
nodes.whatcodes=whatcodes
nodes.whatsitcodes=whatcodes
nodes.glyphcodes=glyphcodes
+nodes.disccodes=disccodes
local free_node=node.free
local remove_node=node.remove
local new_node=node.new
@@ -3649,6 +3765,33 @@ function constructors.beforecopyingcharacters(target,original)
end
function constructors.aftercopyingcharacters(target,original)
end
+constructors.sharefonts=false
+constructors.nofsharedfonts=0
+local sharednames={}
+function constructors.trytosharefont(target,tfmdata)
+ if constructors.sharefonts then
+ local characters=target.characters
+ local n=1
+ local t={ target.psname }
+ local u=sortedkeys(characters)
+ for i=1,#u do
+ n=n+1;t[n]=k
+ n=n+1;t[n]=characters[u[i]].index or k
+ end
+ local h=md5.HEX(concat(t," "))
+ local s=sharednames[h]
+ if s then
+ if trace_defining then
+ report_defining("font %a uses backend resources of font %a",target.fullname,s)
+ end
+ target.fullname=s
+ constructors.nofsharedfonts=constructors.nofsharedfonts+1
+ target.properties.sharedwith=s
+ else
+ sharednames[h]=target.fullname
+ end
+ end
+end
function constructors.enhanceparameters(parameters)
local xheight=parameters.x_height
local quad=parameters.quad
@@ -3676,6 +3819,7 @@ function constructors.scale(tfmdata,specification)
if tonumber(specification) then
specification={ size=specification }
end
+ target.specification=specification
local scaledpoints=specification.size
local relativeid=specification.relativeid
local properties=tfmdata.properties or {}
@@ -3727,7 +3871,7 @@ function constructors.scale(tfmdata,specification)
targetproperties.script=properties.script or "dflt"
targetproperties.mode=properties.mode or "base"
local askedscaledpoints=scaledpoints
- local scaledpoints,delta=constructors.calculatescale(tfmdata,scaledpoints)
+ local scaledpoints,delta=constructors.calculatescale(tfmdata,scaledpoints,nil,specification)
local hdelta=delta
local vdelta=delta
target.designsize=parameters.designsize
@@ -3801,7 +3945,7 @@ function constructors.scale(tfmdata,specification)
end
target.type=isvirtual and "virtual" or "real"
target.postprocessors=tfmdata.postprocessors
- local targetslant=(parameters.slant or parameters[1] or 0)
+ local targetslant=(parameters.slant or parameters[1] or 0)*factors.pt
local targetspace=(parameters.space or parameters[2] or 0)*hdelta
local targetspace_stretch=(parameters.space_stretch or parameters[3] or 0)*hdelta
local targetspace_shrink=(parameters.space_shrink or parameters[4] or 0)*hdelta
@@ -4080,6 +4224,7 @@ function constructors.scale(tfmdata,specification)
targetcharacters[unicode]=chr
end
constructors.aftercopyingcharacters(target,tfmdata)
+ constructors.trytosharefont(target,tfmdata)
return target
end
function constructors.finalize(tfmdata)
@@ -4119,7 +4264,7 @@ function constructors.finalize(tfmdata)
parameters.slantfactor=tfmdata.slant or 0
end
if not parameters.designsize then
- parameters.designsize=tfmdata.designsize or 655360
+ parameters.designsize=tfmdata.designsize or (factors.pt*10)
end
if not parameters.units then
parameters.units=tfmdata.units_per_em or 1000
@@ -4243,11 +4388,11 @@ function constructors.hashinstance(specification,force)
size=math.round(constructors.scaled(size,designsizes[hash]))
specification.size=size
end
- if fallbacks then
- return hash..' @ '..tostring(size)..' @ '..fallbacks
- else
- return hash..' @ '..tostring(size)
- end
+ if fallbacks then
+ return hash..' @ '..tostring(size)..' @ '..fallbacks
+ else
+ return hash..' @ '..tostring(size)
+ end
end
function constructors.setname(tfmdata,specification)
if constructors.namemode=="specification" then
@@ -4880,33 +5025,38 @@ function mappings.addtounicode(data,filename)
if not unicode or unicode=="" then
local split=lpegmatch(namesplitter,name)
local nsplit=split and #split or 0
- if nsplit>=2 then
- local t,n={},0
- for l=1,nsplit do
- local base=split[l]
- local u=unicodes[base] or unicodevector[base]
- if not u then
+ local t,n={},0
+ unicode=true
+ for l=1,nsplit do
+ local base=split[l]
+ local u=unicodes[base] or unicodevector[base]
+ if not u then
+ break
+ elseif type(u)=="table" then
+ if u[1]>=private then
+ unicode=false
break
- elseif type(u)=="table" then
- n=n+1
- t[n]=u[1]
- else
- n=n+1
- t[n]=u
end
- end
- if n==0 then
- elseif n==1 then
- originals[index]=t[1]
- tounicode[index]=tounicode16(t[1],name)
+ n=n+1
+ t[n]=u[1]
else
- originals[index]=t
- tounicode[index]=tounicode16sequence(t)
+ if u>=private then
+ unicode=false
+ break
+ end
+ n=n+1
+ t[n]=u
end
- nl=nl+1
- unicode=true
+ end
+ if n==0 then
+ elseif n==1 then
+ originals[index]=t[1]
+ tounicode[index]=tounicode16(t[1],name)
else
+ originals[index]=t
+ tounicode[index]=tounicode16sequence(t)
end
+ nl=nl+1
end
if not unicode or unicode=="" then
local foundcodes,multiple=lpegmatch(uparser,name)
@@ -5017,6 +5167,1104 @@ fonts.names.resolvespec=fonts.names.resolve
function fonts.names.getfilename(askedname,suffix)
return ""
end
+function fonts.names.ignoredfile(filename)
+ return false
+end
+
+end -- closure
+
+do -- begin closure to overcome local limits and interference
+
+if not modules then modules={} end modules ['font-tfm']={
+ version=1.001,
+ comment="companion to font-ini.mkiv",
+ author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright="PRAGMA ADE / ConTeXt Development Team",
+ license="see context related readme files"
+}
+local next=next
+local match=string.match
+local trace_defining=false trackers.register("fonts.defining",function(v) trace_defining=v end)
+local trace_features=false trackers.register("tfm.features",function(v) trace_features=v end)
+local report_defining=logs.reporter("fonts","defining")
+local report_tfm=logs.reporter("fonts","tfm loading")
+local findbinfile=resolvers.findbinfile
+local fonts=fonts
+local handlers=fonts.handlers
+local readers=fonts.readers
+local constructors=fonts.constructors
+local encodings=fonts.encodings
+local tfm=constructors.newhandler("tfm")
+local tfmfeatures=constructors.newfeatures("tfm")
+local registertfmfeature=tfmfeatures.register
+constructors.resolvevirtualtoo=false
+fonts.formats.tfm="type1"
+function tfm.setfeatures(tfmdata,features)
+ local okay=constructors.initializefeatures("tfm",tfmdata,features,trace_features,report_tfm)
+ if okay then
+ return constructors.collectprocessors("tfm",tfmdata,features,trace_features,report_tfm)
+ else
+ return {}
+ end
+end
+local function read_from_tfm(specification)
+ local filename=specification.filename
+ local size=specification.size
+ if trace_defining then
+ report_defining("loading tfm file %a at size %s",filename,size)
+ end
+ local tfmdata=font.read_tfm(filename,size)
+ if tfmdata then
+ local features=specification.features and specification.features.normal or {}
+ local resources=tfmdata.resources or {}
+ local properties=tfmdata.properties or {}
+ local parameters=tfmdata.parameters or {}
+ local shared=tfmdata.shared or {}
+ properties.name=tfmdata.name
+ properties.fontname=tfmdata.fontname
+ properties.psname=tfmdata.psname
+ properties.filename=specification.filename
+ parameters.size=size
+ shared.rawdata={}
+ shared.features=features
+ shared.processes=next(features) and tfm.setfeatures(tfmdata,features) or nil
+ tfmdata.properties=properties
+ tfmdata.resources=resources
+ tfmdata.parameters=parameters
+ tfmdata.shared=shared
+ parameters.slant=parameters.slant or parameters[1] or 0
+ parameters.space=parameters.space or parameters[2] or 0
+ parameters.space_stretch=parameters.space_stretch or parameters[3] or 0
+ parameters.space_shrink=parameters.space_shrink or parameters[4] or 0
+ parameters.x_height=parameters.x_height or parameters[5] or 0
+ parameters.quad=parameters.quad or parameters[6] or 0
+ parameters.extra_space=parameters.extra_space or parameters[7] or 0
+ constructors.enhanceparameters(parameters)
+ if constructors.resolvevirtualtoo then
+ fonts.loggers.register(tfmdata,file.suffix(filename),specification)
+ local vfname=findbinfile(specification.name,'ovf')
+ if vfname and vfname~="" then
+ local vfdata=font.read_vf(vfname,size)
+ if vfdata then
+ local chars=tfmdata.characters
+ for k,v in next,vfdata.characters do
+ chars[k].commands=v.commands
+ end
+ properties.virtualized=true
+ tfmdata.fonts=vfdata.fonts
+ end
+ end
+ end
+ local allfeatures=tfmdata.shared.features or specification.features.normal
+ constructors.applymanipulators("tfm",tfmdata,allfeatures.normal,trace_features,report_tfm)
+ if not features.encoding then
+ local encoding,filename=match(properties.filename,"^(.-)%-(.*)$")
+ if filename and encoding and encodings.known and encodings.known[encoding] then
+ features.encoding=encoding
+ end
+ end
+ return tfmdata
+ end
+end
+local function check_tfm(specification,fullname)
+ local foundname=findbinfile(fullname,'tfm') or ""
+ if foundname=="" then
+ foundname=findbinfile(fullname,'ofm') or ""
+ end
+ if foundname=="" then
+ foundname=fonts.names.getfilename(fullname,"tfm") or ""
+ end
+ if foundname~="" then
+ specification.filename=foundname
+ specification.format="ofm"
+ return read_from_tfm(specification)
+ elseif trace_defining then
+ report_defining("loading tfm with name %a fails",specification.name)
+ end
+end
+readers.check_tfm=check_tfm
+function readers.tfm(specification)
+ local fullname=specification.filename or ""
+ if fullname=="" then
+ local forced=specification.forced or ""
+ if forced~="" then
+ fullname=specification.name.."."..forced
+ else
+ fullname=specification.name
+ end
+ end
+ return check_tfm(specification,fullname)
+end
+
+end -- closure
+
+do -- begin closure to overcome local limits and interference
+
+if not modules then modules={} end modules ['font-afm']={
+ version=1.001,
+ comment="companion to font-ini.mkiv",
+ author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright="PRAGMA ADE / ConTeXt Development Team",
+ license="see context related readme files"
+}
+local fonts,logs,trackers,containers,resolvers=fonts,logs,trackers,containers,resolvers
+local next,type,tonumber=next,type,tonumber
+local format,match,gmatch,lower,gsub,strip=string.format,string.match,string.gmatch,string.lower,string.gsub,string.strip
+local abs=math.abs
+local P,S,C,R,lpegmatch,patterns=lpeg.P,lpeg.S,lpeg.C,lpeg.R,lpeg.match,lpeg.patterns
+local derivetable=table.derive
+local trace_features=false trackers.register("afm.features",function(v) trace_features=v end)
+local trace_indexing=false trackers.register("afm.indexing",function(v) trace_indexing=v end)
+local trace_loading=false trackers.register("afm.loading",function(v) trace_loading=v end)
+local trace_defining=false trackers.register("fonts.defining",function(v) trace_defining=v end)
+local report_afm=logs.reporter("fonts","afm loading")
+local findbinfile=resolvers.findbinfile
+local definers=fonts.definers
+local readers=fonts.readers
+local constructors=fonts.constructors
+local afm=constructors.newhandler("afm")
+local pfb=constructors.newhandler("pfb")
+local afmfeatures=constructors.newfeatures("afm")
+local registerafmfeature=afmfeatures.register
+afm.version=1.410
+afm.cache=containers.define("fonts","afm",afm.version,true)
+afm.autoprefixed=true
+afm.helpdata={}
+afm.syncspace=true
+afm.addligatures=true
+afm.addtexligatures=true
+afm.addkerns=true
+local applyruntimefixes=fonts.treatments and fonts.treatments.applyfixes
+local function setmode(tfmdata,value)
+ if value then
+ tfmdata.properties.mode=lower(value)
+ end
+end
+registerafmfeature {
+ name="mode",
+ description="mode",
+ initializers={
+ base=setmode,
+ node=setmode,
+ }
+}
+local comment=P("Comment")
+local spacing=patterns.spacer
+local lineend=patterns.newline
+local words=C((1-lineend)^1)
+local number=C((R("09")+S("."))^1)/tonumber*spacing^0
+local data=lpeg.Carg(1)
+local pattern=(
+ comment*spacing*(
+ data*(
+ ("CODINGSCHEME"*spacing*words )/function(fd,a) end+("DESIGNSIZE"*spacing*number*words )/function(fd,a) fd[ 1]=a end+("CHECKSUM"*spacing*number*words )/function(fd,a) fd[ 2]=a end+("SPACE"*spacing*number*"plus"*number*"minus"*number)/function(fd,a,b,c) fd[ 3],fd[ 4],fd[ 5]=a,b,c end+("QUAD"*spacing*number )/function(fd,a) fd[ 6]=a end+("EXTRASPACE"*spacing*number )/function(fd,a) fd[ 7]=a end+("NUM"*spacing*number*number*number )/function(fd,a,b,c) fd[ 8],fd[ 9],fd[10]=a,b,c end+("DENOM"*spacing*number*number )/function(fd,a,b ) fd[11],fd[12]=a,b end+("SUP"*spacing*number*number*number )/function(fd,a,b,c) fd[13],fd[14],fd[15]=a,b,c end+("SUB"*spacing*number*number )/function(fd,a,b) fd[16],fd[17]=a,b end+("SUPDROP"*spacing*number )/function(fd,a) fd[18]=a end+("SUBDROP"*spacing*number )/function(fd,a) fd[19]=a end+("DELIM"*spacing*number*number )/function(fd,a,b) fd[20],fd[21]=a,b end+("AXISHEIGHT"*spacing*number )/function(fd,a) fd[22]=a end
+ )+(1-lineend)^0
+ )+(1-comment)^1
+)^0
+local function scan_comment(str)
+ local fd={}
+ lpegmatch(pattern,str,1,fd)
+ return fd
+end
+local keys={}
+function keys.FontName (data,line) data.metadata.fontname=strip (line)
+ data.metadata.fullname=strip (line) end
+function keys.ItalicAngle (data,line) data.metadata.italicangle=tonumber (line) end
+function keys.IsFixedPitch(data,line) data.metadata.isfixedpitch=toboolean(line,true) end
+function keys.CharWidth (data,line) data.metadata.charwidth=tonumber (line) end
+function keys.XHeight (data,line) data.metadata.xheight=tonumber (line) end
+function keys.Descender (data,line) data.metadata.descender=tonumber (line) end
+function keys.Ascender (data,line) data.metadata.ascender=tonumber (line) end
+function keys.Comment (data,line)
+ line=lower(line)
+ local designsize=match(line,"designsize[^%d]*(%d+)")
+ if designsize then data.metadata.designsize=tonumber(designsize) end
+end
+local function get_charmetrics(data,charmetrics,vector)
+ local characters=data.characters
+ local chr,ind={},0
+ for k,v in gmatch(charmetrics,"([%a]+) +(.-) *;") do
+ if k=='C' then
+ v=tonumber(v)
+ if v<0 then
+ ind=ind+1
+ else
+ ind=v
+ end
+ chr={
+ index=ind
+ }
+ elseif k=='WX' then
+ chr.width=tonumber(v)
+ elseif k=='N' then
+ characters[v]=chr
+ elseif k=='B' then
+ local llx,lly,urx,ury=match(v,"^ *(.-) +(.-) +(.-) +(.-)$")
+ chr.boundingbox={ tonumber(llx),tonumber(lly),tonumber(urx),tonumber(ury) }
+ elseif k=='L' then
+ local plus,becomes=match(v,"^(.-) +(.-)$")
+ local ligatures=chr.ligatures
+ if ligatures then
+ ligatures[plus]=becomes
+ else
+ chr.ligatures={ [plus]=becomes }
+ end
+ end
+ end
+end
+local function get_kernpairs(data,kernpairs)
+ local characters=data.characters
+ for one,two,value in gmatch(kernpairs,"KPX +(.-) +(.-) +(.-)\n") do
+ local chr=characters[one]
+ if chr then
+ local kerns=chr.kerns
+ if kerns then
+ kerns[two]=tonumber(value)
+ else
+ chr.kerns={ [two]=tonumber(value) }
+ end
+ end
+ end
+end
+local function get_variables(data,fontmetrics)
+ for key,rest in gmatch(fontmetrics,"(%a+) *(.-)[\n\r]") do
+ local keyhandler=keys[key]
+ if keyhandler then
+ keyhandler(data,rest)
+ end
+ end
+end
+local function get_indexes(data,pfbname)
+ data.resources.filename=resolvers.unresolve(pfbname)
+ local pfbblob=fontloader.open(pfbname)
+ if pfbblob then
+ local characters=data.characters
+ local pfbdata=fontloader.to_table(pfbblob)
+ if pfbdata then
+ local glyphs=pfbdata.glyphs
+ if glyphs then
+ if trace_loading then
+ report_afm("getting index data from %a",pfbname)
+ end
+ for index,glyph in next,glyphs do
+ local name=glyph.name
+ if name then
+ local char=characters[name]
+ if char then
+ if trace_indexing then
+ report_afm("glyph %a has index %a",name,index)
+ end
+ char.index=index
+ end
+ end
+ end
+ elseif trace_loading then
+ report_afm("no glyph data in pfb file %a",pfbname)
+ end
+ elseif trace_loading then
+ report_afm("no data in pfb file %a",pfbname)
+ end
+ fontloader.close(pfbblob)
+ elseif trace_loading then
+ report_afm("invalid pfb file %a",pfbname)
+ end
+end
+local function readafm(filename)
+ local ok,afmblob,size=resolvers.loadbinfile(filename)
+ if ok and afmblob then
+ local data={
+ resources={
+ filename=resolvers.unresolve(filename),
+ version=afm.version,
+ creator="context mkiv",
+ },
+ properties={
+ hasitalics=false,
+ },
+ goodies={},
+ metadata={
+ filename=file.removesuffix(file.basename(filename))
+ },
+ characters={
+ },
+ descriptions={
+ },
+ }
+ afmblob=gsub(afmblob,"StartCharMetrics(.-)EndCharMetrics",function(charmetrics)
+ if trace_loading then
+ report_afm("loading char metrics")
+ end
+ get_charmetrics(data,charmetrics,vector)
+ return ""
+ end)
+ afmblob=gsub(afmblob,"StartKernPairs(.-)EndKernPairs",function(kernpairs)
+ if trace_loading then
+ report_afm("loading kern pairs")
+ end
+ get_kernpairs(data,kernpairs)
+ return ""
+ end)
+ afmblob=gsub(afmblob,"StartFontMetrics%s+([%d%.]+)(.-)EndFontMetrics",function(version,fontmetrics)
+ if trace_loading then
+ report_afm("loading variables")
+ end
+ data.afmversion=version
+ get_variables(data,fontmetrics)
+ data.fontdimens=scan_comment(fontmetrics)
+ return ""
+ end)
+ return data
+ else
+ if trace_loading then
+ report_afm("no valid afm file %a",filename)
+ end
+ return nil
+ end
+end
+local addkerns,addligatures,addtexligatures,unify,normalize
+function afm.load(filename)
+ filename=resolvers.findfile(filename,'afm') or ""
+ if filename~="" and not fonts.names.ignoredfile(filename) then
+ local name=file.removesuffix(file.basename(filename))
+ local data=containers.read(afm.cache,name)
+ local attr=lfs.attributes(filename)
+ local size,time=attr.size or 0,attr.modification or 0
+ local pfbfile=file.replacesuffix(name,"pfb")
+ local pfbname=resolvers.findfile(pfbfile,"pfb") or ""
+ if pfbname=="" then
+ pfbname=resolvers.findfile(file.basename(pfbfile),"pfb") or ""
+ end
+ local pfbsize,pfbtime=0,0
+ if pfbname~="" then
+ local attr=lfs.attributes(pfbname)
+ pfbsize=attr.size or 0
+ pfbtime=attr.modification or 0
+ end
+ if not data or data.size~=size or data.time~=time or data.pfbsize~=pfbsize or data.pfbtime~=pfbtime then
+ report_afm("reading %a",filename)
+ data=readafm(filename)
+ if data then
+ if pfbname~="" then
+ get_indexes(data,pfbname)
+ elseif trace_loading then
+ report_afm("no pfb file for %a",filename)
+ end
+ report_afm("unifying %a",filename)
+ unify(data,filename)
+ if afm.addligatures then
+ report_afm("add ligatures")
+ addligatures(data)
+ end
+ if afm.addtexligatures then
+ report_afm("add tex ligatures")
+ addtexligatures(data)
+ end
+ if afm.addkerns then
+ report_afm("add extra kerns")
+ addkerns(data)
+ end
+ normalize(data)
+ report_afm("add tounicode data")
+ fonts.mappings.addtounicode(data,filename)
+ data.size=size
+ data.time=time
+ data.pfbsize=pfbsize
+ data.pfbtime=pfbtime
+ report_afm("saving %a in cache",name)
+ data=containers.write(afm.cache,name,data)
+ data=containers.read(afm.cache,name)
+ end
+ if applyruntimefixes and data then
+ applyruntimefixes(filename,data)
+ end
+ end
+ return data
+ else
+ return nil
+ end
+end
+local uparser=fonts.mappings.makenameparser()
+unify=function(data,filename)
+ local unicodevector=fonts.encodings.agl.unicodes
+ local unicodes,names={},{}
+ local private=constructors.privateoffset
+ local descriptions=data.descriptions
+ for name,blob in next,data.characters do
+ local code=unicodevector[name]
+ if not code then
+ code=lpegmatch(uparser,name)
+ if not code then
+ code=private
+ private=private+1
+ report_afm("assigning private slot %U for unknown glyph name %a",code,name)
+ end
+ end
+ local index=blob.index
+ unicodes[name]=code
+ names[name]=index
+ blob.name=name
+ descriptions[code]={
+ boundingbox=blob.boundingbox,
+ width=blob.width,
+ kerns=blob.kerns,
+ index=index,
+ name=name,
+ }
+ end
+ for unicode,description in next,descriptions do
+ local kerns=description.kerns
+ if kerns then
+ local krn={}
+ for name,kern in next,kerns do
+ local unicode=unicodes[name]
+ if unicode then
+ krn[unicode]=kern
+ else
+ print(unicode,name)
+ end
+ end
+ description.kerns=krn
+ end
+ end
+ data.characters=nil
+ local resources=data.resources
+ local filename=resources.filename or file.removesuffix(file.basename(filename))
+ resources.filename=resolvers.unresolve(filename)
+ resources.unicodes=unicodes
+ resources.marks={}
+ resources.names=names
+ resources.private=private
+end
+normalize=function(data)
+end
+local addthem=function(rawdata,ligatures)
+ if ligatures then
+ local descriptions=rawdata.descriptions
+ local resources=rawdata.resources
+ local unicodes=resources.unicodes
+ local names=resources.names
+ for ligname,ligdata in next,ligatures do
+ local one=descriptions[unicodes[ligname]]
+ if one then
+ for _,pair in next,ligdata do
+ local two,three=unicodes[pair[1]],unicodes[pair[2]]
+ if two and three then
+ local ol=one.ligatures
+ if ol then
+ if not ol[two] then
+ ol[two]=three
+ end
+ else
+ one.ligatures={ [two]=three }
+ end
+ end
+ end
+ end
+ end
+ end
+end
+addligatures=function(rawdata) addthem(rawdata,afm.helpdata.ligatures ) end
+addtexligatures=function(rawdata) addthem(rawdata,afm.helpdata.texligatures) end
+addkerns=function(rawdata)
+ local descriptions=rawdata.descriptions
+ local resources=rawdata.resources
+ local unicodes=resources.unicodes
+ local function do_it_left(what)
+ if what then
+ for unicode,description in next,descriptions do
+ local kerns=description.kerns
+ if kerns then
+ local extrakerns
+ for complex,simple in next,what do
+ complex=unicodes[complex]
+ simple=unicodes[simple]
+ if complex and simple then
+ local ks=kerns[simple]
+ if ks and not kerns[complex] then
+ if extrakerns then
+ extrakerns[complex]=ks
+ else
+ extrakerns={ [complex]=ks }
+ end
+ end
+ end
+ end
+ if extrakerns then
+ description.extrakerns=extrakerns
+ end
+ end
+ end
+ end
+ end
+ local function do_it_copy(what)
+ if what then
+ for complex,simple in next,what do
+ complex=unicodes[complex]
+ simple=unicodes[simple]
+ if complex and simple then
+ local complexdescription=descriptions[complex]
+ if complexdescription then
+ local simpledescription=descriptions[complex]
+ if simpledescription then
+ local extrakerns
+ local kerns=simpledescription.kerns
+ if kerns then
+ for unicode,kern in next,kerns do
+ if extrakerns then
+ extrakerns[unicode]=kern
+ else
+ extrakerns={ [unicode]=kern }
+ end
+ end
+ end
+ local extrakerns=simpledescription.extrakerns
+ if extrakerns then
+ for unicode,kern in next,extrakerns do
+ if extrakerns then
+ extrakerns[unicode]=kern
+ else
+ extrakerns={ [unicode]=kern }
+ end
+ end
+ end
+ if extrakerns then
+ complexdescription.extrakerns=extrakerns
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+ do_it_left(afm.helpdata.leftkerned)
+ do_it_left(afm.helpdata.bothkerned)
+ do_it_copy(afm.helpdata.bothkerned)
+ do_it_copy(afm.helpdata.rightkerned)
+end
+local function adddimensions(data)
+ if data then
+ for unicode,description in next,data.descriptions do
+ local bb=description.boundingbox
+ if bb then
+ local ht,dp=bb[4],-bb[2]
+ if ht==0 or ht<0 then
+ else
+ description.height=ht
+ end
+ if dp==0 or dp<0 then
+ else
+ description.depth=dp
+ end
+ end
+ end
+ end
+end
+local function copytotfm(data)
+ if data and data.descriptions then
+ local metadata=data.metadata
+ local resources=data.resources
+ local properties=derivetable(data.properties)
+ local descriptions=derivetable(data.descriptions)
+ local goodies=derivetable(data.goodies)
+ local characters={}
+ local parameters={}
+ local unicodes=resources.unicodes
+ for unicode,description in next,data.descriptions do
+ characters[unicode]={}
+ end
+ local filename=constructors.checkedfilename(resources)
+ local fontname=metadata.fontname or metadata.fullname
+ local fullname=metadata.fullname or metadata.fontname
+ local endash=unicodes['space']
+ local emdash=unicodes['emdash']
+ local spacer="space"
+ local spaceunits=500
+ local monospaced=metadata.isfixedpitch
+ local charwidth=metadata.charwidth
+ local italicangle=metadata.italicangle
+ local charxheight=metadata.xheight and metadata.xheight>0 and metadata.xheight
+ properties.monospaced=monospaced
+ parameters.italicangle=italicangle
+ parameters.charwidth=charwidth
+ parameters.charxheight=charxheight
+ if properties.monospaced then
+ if descriptions[endash] then
+ spaceunits,spacer=descriptions[endash].width,"space"
+ end
+ if not spaceunits and descriptions[emdash] then
+ spaceunits,spacer=descriptions[emdash].width,"emdash"
+ end
+ if not spaceunits and charwidth then
+ spaceunits,spacer=charwidth,"charwidth"
+ end
+ else
+ if descriptions[endash] then
+ spaceunits,spacer=descriptions[endash].width,"space"
+ end
+ if not spaceunits and charwidth then
+ spaceunits,spacer=charwidth,"charwidth"
+ end
+ end
+ spaceunits=tonumber(spaceunits)
+ if spaceunits<200 then
+ end
+ parameters.slant=0
+ parameters.space=spaceunits
+ parameters.space_stretch=500
+ parameters.space_shrink=333
+ parameters.x_height=400
+ parameters.quad=1000
+ if italicangle and italicangle~=0 then
+ parameters.italicangle=italicangle
+ parameters.italicfactor=math.cos(math.rad(90+italicangle))
+ parameters.slant=- math.tan(italicangle*math.pi/180)
+ end
+ if monospaced then
+ parameters.space_stretch=0
+ parameters.space_shrink=0
+ elseif afm.syncspace then
+ parameters.space_stretch=spaceunits/2
+ parameters.space_shrink=spaceunits/3
+ end
+ parameters.extra_space=parameters.space_shrink
+ if charxheight then
+ parameters.x_height=charxheight
+ else
+ local x=unicodes['x']
+ if x then
+ local x=descriptions[x]
+ if x then
+ parameters.x_height=x.height
+ end
+ end
+ end
+ local fd=data.fontdimens
+ if fd and fd[8] and fd[9] and fd[10] then
+ for k,v in next,fd do
+ parameters[k]=v
+ end
+ end
+ parameters.designsize=(metadata.designsize or 10)*65536
+ parameters.ascender=abs(metadata.ascender or 0)
+ parameters.descender=abs(metadata.descender or 0)
+ parameters.units=1000
+ properties.spacer=spacer
+ properties.encodingbytes=2
+ properties.format=fonts.formats[filename] or "type1"
+ properties.filename=filename
+ properties.fontname=fontname
+ properties.fullname=fullname
+ properties.psname=fullname
+ properties.name=filename or fullname or fontname
+ if next(characters) then
+ return {
+ characters=characters,
+ descriptions=descriptions,
+ parameters=parameters,
+ resources=resources,
+ properties=properties,
+ goodies=goodies,
+ }
+ end
+ end
+ return nil
+end
+function afm.setfeatures(tfmdata,features)
+ local okay=constructors.initializefeatures("afm",tfmdata,features,trace_features,report_afm)
+ if okay then
+ return constructors.collectprocessors("afm",tfmdata,features,trace_features,report_afm)
+ else
+ return {}
+ end
+end
+local function checkfeatures(specification)
+end
+local function afmtotfm(specification)
+ local afmname=specification.filename or specification.name
+ if specification.forced=="afm" or specification.format=="afm" then
+ if trace_loading then
+ report_afm("forcing afm format for %a",afmname)
+ end
+ else
+ local tfmname=findbinfile(afmname,"ofm") or ""
+ if tfmname~="" then
+ if trace_loading then
+ report_afm("fallback from afm to tfm for %a",afmname)
+ end
+ return
+ end
+ end
+ if afmname~="" then
+ local features=constructors.checkedfeatures("afm",specification.features.normal)
+ specification.features.normal=features
+ constructors.hashinstance(specification,true)
+ specification=definers.resolve(specification)
+ local cache_id=specification.hash
+ local tfmdata=containers.read(constructors.cache,cache_id)
+ if not tfmdata then
+ local rawdata=afm.load(afmname)
+ if rawdata and next(rawdata) then
+ adddimensions(rawdata)
+ tfmdata=copytotfm(rawdata)
+ if tfmdata and next(tfmdata) then
+ local shared=tfmdata.shared
+ if not shared then
+ shared={}
+ tfmdata.shared=shared
+ end
+ shared.rawdata=rawdata
+ shared.features=features
+ shared.processes=afm.setfeatures(tfmdata,features)
+ end
+ elseif trace_loading then
+ report_afm("no (valid) afm file found with name %a",afmname)
+ end
+ tfmdata=containers.write(constructors.cache,cache_id,tfmdata)
+ end
+ return tfmdata
+ end
+end
+local function read_from_afm(specification)
+ local tfmdata=afmtotfm(specification)
+ if tfmdata then
+ tfmdata.properties.name=specification.name
+ tfmdata=constructors.scale(tfmdata,specification)
+ local allfeatures=tfmdata.shared.features or specification.features.normal
+ constructors.applymanipulators("afm",tfmdata,allfeatures,trace_features,report_afm)
+ fonts.loggers.register(tfmdata,'afm',specification)
+ end
+ return tfmdata
+end
+local function prepareligatures(tfmdata,ligatures,value)
+ if value then
+ local descriptions=tfmdata.descriptions
+ for unicode,character in next,tfmdata.characters do
+ local description=descriptions[unicode]
+ local dligatures=description.ligatures
+ if dligatures then
+ local cligatures=character.ligatures
+ if not cligatures then
+ cligatures={}
+ character.ligatures=cligatures
+ end
+ for unicode,ligature in next,dligatures do
+ cligatures[unicode]={
+ char=ligature,
+ type=0
+ }
+ end
+ end
+ end
+ end
+end
+local function preparekerns(tfmdata,kerns,value)
+ if value then
+ local rawdata=tfmdata.shared.rawdata
+ local resources=rawdata.resources
+ local unicodes=resources.unicodes
+ local descriptions=tfmdata.descriptions
+ for u,chr in next,tfmdata.characters do
+ local d=descriptions[u]
+ local newkerns=d[kerns]
+ if newkerns then
+ local kerns=chr.kerns
+ if not kerns then
+ kerns={}
+ chr.kerns=kerns
+ end
+ for k,v in next,newkerns do
+ local uk=unicodes[k]
+ if uk then
+ kerns[uk]=v
+ end
+ end
+ end
+ end
+ end
+end
+local list={
+ [0x0027]=0x2019,
+}
+local function texreplacements(tfmdata,value)
+ local descriptions=tfmdata.descriptions
+ local characters=tfmdata.characters
+ for k,v in next,list do
+ characters [k]=characters [v]
+ descriptions[k]=descriptions[v]
+ end
+end
+local function ligatures (tfmdata,value) prepareligatures(tfmdata,'ligatures',value) end
+local function texligatures(tfmdata,value) prepareligatures(tfmdata,'texligatures',value) end
+local function kerns (tfmdata,value) preparekerns (tfmdata,'kerns',value) end
+local function extrakerns (tfmdata,value) preparekerns (tfmdata,'extrakerns',value) end
+registerafmfeature {
+ name="liga",
+ description="traditional ligatures",
+ initializers={
+ base=ligatures,
+ node=ligatures,
+ }
+}
+registerafmfeature {
+ name="kern",
+ description="intercharacter kerning",
+ initializers={
+ base=kerns,
+ node=kerns,
+ }
+}
+registerafmfeature {
+ name="extrakerns",
+ description="additional intercharacter kerning",
+ initializers={
+ base=extrakerns,
+ node=extrakerns,
+ }
+}
+registerafmfeature {
+ name='tlig',
+ description='tex ligatures',
+ initializers={
+ base=texligatures,
+ node=texligatures,
+ }
+}
+registerafmfeature {
+ name='trep',
+ description='tex replacements',
+ initializers={
+ base=texreplacements,
+ node=texreplacements,
+ }
+}
+local check_tfm=readers.check_tfm
+fonts.formats.afm="type1"
+fonts.formats.pfb="type1"
+local function check_afm(specification,fullname)
+ local foundname=findbinfile(fullname,'afm') or ""
+ if foundname=="" then
+ foundname=fonts.names.getfilename(fullname,"afm") or ""
+ end
+ if foundname=="" and afm.autoprefixed then
+ local encoding,shortname=match(fullname,"^(.-)%-(.*)$")
+ if encoding and shortname and fonts.encodings.known[encoding] then
+ shortname=findbinfile(shortname,'afm') or ""
+ if shortname~="" then
+ foundname=shortname
+ if trace_defining then
+ report_afm("stripping encoding prefix from filename %a",afmname)
+ end
+ end
+ end
+ end
+ if foundname~="" then
+ specification.filename=foundname
+ specification.format="afm"
+ return read_from_afm(specification)
+ end
+end
+function readers.afm(specification,method)
+ local fullname,tfmdata=specification.filename or "",nil
+ if fullname=="" then
+ local forced=specification.forced or ""
+ if forced~="" then
+ tfmdata=check_afm(specification,specification.name.."."..forced)
+ end
+ if not tfmdata then
+ method=method or definers.method or "afm or tfm"
+ if method=="tfm" then
+ tfmdata=check_tfm(specification,specification.name)
+ elseif method=="afm" then
+ tfmdata=check_afm(specification,specification.name)
+ elseif method=="tfm or afm" then
+ tfmdata=check_tfm(specification,specification.name) or check_afm(specification,specification.name)
+ else
+ tfmdata=check_afm(specification,specification.name) or check_tfm(specification,specification.name)
+ end
+ end
+ else
+ tfmdata=check_afm(specification,fullname)
+ end
+ return tfmdata
+end
+function readers.pfb(specification,method)
+ local original=specification.specification
+ if trace_defining then
+ report_afm("using afm reader for %a",original)
+ end
+ specification.specification=gsub(original,"%.pfb",".afm")
+ specification.forced="afm"
+ return readers.afm(specification,method)
+end
+
+end -- closure
+
+do -- begin closure to overcome local limits and interference
+
+if not modules then modules={} end modules ['font-afk']={
+ version=1.001,
+ comment="companion to font-afm.lua",
+ author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright="PRAGMA ADE / ConTeXt Development Team",
+ license="see context related readme files",
+ dataonly=true,
+}
+local allocate=utilities.storage.allocate
+fonts.handlers.afm.helpdata={
+ ligatures=allocate {
+ ['f']={
+ { 'f','ff' },
+ { 'i','fi' },
+ { 'l','fl' },
+ },
+ ['ff']={
+ { 'i','ffi' }
+ },
+ ['fi']={
+ { 'i','fii' }
+ },
+ ['fl']={
+ { 'i','fli' }
+ },
+ ['s']={
+ { 't','st' }
+ },
+ ['i']={
+ { 'j','ij' }
+ },
+ },
+ texligatures=allocate {
+ ['quoteleft']={
+ { 'quoteleft','quotedblleft' }
+ },
+ ['quoteright']={
+ { 'quoteright','quotedblright' }
+ },
+ ['hyphen']={
+ { 'hyphen','endash' }
+ },
+ ['endash']={
+ { 'hyphen','emdash' }
+ }
+ },
+ leftkerned=allocate {
+ AEligature="A",aeligature="a",
+ OEligature="O",oeligature="o",
+ IJligature="I",ijligature="i",
+ AE="A",ae="a",
+ OE="O",oe="o",
+ IJ="I",ij="i",
+ Ssharp="S",ssharp="s",
+ },
+ rightkerned=allocate {
+ AEligature="E",aeligature="e",
+ OEligature="E",oeligature="e",
+ IJligature="J",ijligature="j",
+ AE="E",ae="e",
+ OE="E",oe="e",
+ IJ="J",ij="j",
+ Ssharp="S",ssharp="s",
+ },
+ bothkerned=allocate {
+ Acircumflex="A",acircumflex="a",
+ Ccircumflex="C",ccircumflex="c",
+ Ecircumflex="E",ecircumflex="e",
+ Gcircumflex="G",gcircumflex="g",
+ Hcircumflex="H",hcircumflex="h",
+ Icircumflex="I",icircumflex="i",
+ Jcircumflex="J",jcircumflex="j",
+ Ocircumflex="O",ocircumflex="o",
+ Scircumflex="S",scircumflex="s",
+ Ucircumflex="U",ucircumflex="u",
+ Wcircumflex="W",wcircumflex="w",
+ Ycircumflex="Y",ycircumflex="y",
+ Agrave="A",agrave="a",
+ Egrave="E",egrave="e",
+ Igrave="I",igrave="i",
+ Ograve="O",ograve="o",
+ Ugrave="U",ugrave="u",
+ Ygrave="Y",ygrave="y",
+ Atilde="A",atilde="a",
+ Itilde="I",itilde="i",
+ Otilde="O",otilde="o",
+ Utilde="U",utilde="u",
+ Ntilde="N",ntilde="n",
+ Adiaeresis="A",adiaeresis="a",Adieresis="A",adieresis="a",
+ Ediaeresis="E",ediaeresis="e",Edieresis="E",edieresis="e",
+ Idiaeresis="I",idiaeresis="i",Idieresis="I",idieresis="i",
+ Odiaeresis="O",odiaeresis="o",Odieresis="O",odieresis="o",
+ Udiaeresis="U",udiaeresis="u",Udieresis="U",udieresis="u",
+ Ydiaeresis="Y",ydiaeresis="y",Ydieresis="Y",ydieresis="y",
+ Aacute="A",aacute="a",
+ Cacute="C",cacute="c",
+ Eacute="E",eacute="e",
+ Iacute="I",iacute="i",
+ Lacute="L",lacute="l",
+ Nacute="N",nacute="n",
+ Oacute="O",oacute="o",
+ Racute="R",racute="r",
+ Sacute="S",sacute="s",
+ Uacute="U",uacute="u",
+ Yacute="Y",yacute="y",
+ Zacute="Z",zacute="z",
+ Dstroke="D",dstroke="d",
+ Hstroke="H",hstroke="h",
+ Tstroke="T",tstroke="t",
+ Cdotaccent="C",cdotaccent="c",
+ Edotaccent="E",edotaccent="e",
+ Gdotaccent="G",gdotaccent="g",
+ Idotaccent="I",idotaccent="i",
+ Zdotaccent="Z",zdotaccent="z",
+ Amacron="A",amacron="a",
+ Emacron="E",emacron="e",
+ Imacron="I",imacron="i",
+ Omacron="O",omacron="o",
+ Umacron="U",umacron="u",
+ Ccedilla="C",ccedilla="c",
+ Kcedilla="K",kcedilla="k",
+ Lcedilla="L",lcedilla="l",
+ Ncedilla="N",ncedilla="n",
+ Rcedilla="R",rcedilla="r",
+ Scedilla="S",scedilla="s",
+ Tcedilla="T",tcedilla="t",
+ Ohungarumlaut="O",ohungarumlaut="o",
+ Uhungarumlaut="U",uhungarumlaut="u",
+ Aogonek="A",aogonek="a",
+ Eogonek="E",eogonek="e",
+ Iogonek="I",iogonek="i",
+ Uogonek="U",uogonek="u",
+ Aring="A",aring="a",
+ Uring="U",uring="u",
+ Abreve="A",abreve="a",
+ Ebreve="E",ebreve="e",
+ Gbreve="G",gbreve="g",
+ Ibreve="I",ibreve="i",
+ Obreve="O",obreve="o",
+ Ubreve="U",ubreve="u",
+ Ccaron="C",ccaron="c",
+ Dcaron="D",dcaron="d",
+ Ecaron="E",ecaron="e",
+ Lcaron="L",lcaron="l",
+ Ncaron="N",ncaron="n",
+ Rcaron="R",rcaron="r",
+ Scaron="S",scaron="s",
+ Tcaron="T",tcaron="t",
+ Zcaron="Z",zcaron="z",
+ dotlessI="I",dotlessi="i",
+ dotlessJ="J",dotlessj="j",
+ AEligature="AE",aeligature="ae",AE="AE",ae="ae",
+ OEligature="OE",oeligature="oe",OE="OE",oe="oe",
+ IJligature="IJ",ijligature="ij",IJ="IJ",ij="ij",
+ Lstroke="L",lstroke="l",Lslash="L",lslash="l",
+ Ostroke="O",ostroke="o",Oslash="O",oslash="o",
+ Ssharp="SS",ssharp="ss",
+ Aumlaut="A",aumlaut="a",
+ Eumlaut="E",eumlaut="e",
+ Iumlaut="I",iumlaut="i",
+ Oumlaut="O",oumlaut="o",
+ Uumlaut="U",uumlaut="u",
+ }
+}
end -- closure
@@ -5155,7 +6403,7 @@ local utfbyte=utf.byte
local format,gmatch,gsub,find,match,lower,strip=string.format,string.gmatch,string.gsub,string.find,string.match,string.lower,string.strip
local type,next,tonumber,tostring=type,next,tonumber,tostring
local abs=math.abs
-local getn=table.getn
+local insert=table.insert
local lpegmatch=lpeg.match
local reversed,concat,remove=table.reversed,table.concat,table.remove
local ioflush=io.flush
@@ -5179,7 +6427,7 @@ local report_otf=logs.reporter("fonts","otf loading")
local fonts=fonts
local otf=fonts.handlers.otf
otf.glists={ "gsub","gpos" }
-otf.version=2.743
+otf.version=2.749
otf.cache=containers.define("fonts","otf",otf.version,true)
local fontdata=fonts.hashes.identifiers
local chardata=characters and characters.data
@@ -5199,17 +6447,47 @@ local packdata=true
local syncspace=true
local forcenotdef=false
local includesubfonts=false
+local overloadkerns=false
+local applyruntimefixes=fonts.treatments and fonts.treatments.applyfixes
local wildcard="*"
local default="dflt"
local fontloaderfields=fontloader.fields
local mainfields=nil
local glyphfields=nil
+local formats=fonts.formats
+formats.otf="opentype"
+formats.ttf="truetype"
+formats.ttc="truetype"
+formats.dfont="truetype"
registerdirective("fonts.otf.loader.cleanup",function(v) cleanup=tonumber(v) or (v and 1) or 0 end)
registerdirective("fonts.otf.loader.force",function(v) forceload=v end)
registerdirective("fonts.otf.loader.usemetatables",function(v) usemetatables=v end)
registerdirective("fonts.otf.loader.pack",function(v) packdata=v end)
registerdirective("fonts.otf.loader.syncspace",function(v) syncspace=v end)
registerdirective("fonts.otf.loader.forcenotdef",function(v) forcenotdef=v end)
+registerdirective("fonts.otf.loader.overloadkerns",function(v) overloadkerns=v end)
+function otf.fileformat(filename)
+ local leader=lower(io.loadchunk(filename,4))
+ local suffix=lower(file.suffix(filename))
+ if leader=="otto" then
+ return formats.otf,suffix=="otf"
+ elseif leader=="ttcf" then
+ return formats.ttc,suffix=="ttc"
+ elseif suffix=="ttc" then
+ return formats.ttc,true
+ elseif suffix=="dfont" then
+ return formats.dfont,true
+ else
+ return formats.ttf,suffix=="ttf"
+ end
+end
+local function otf_format(filename)
+ local format,okay=otf.fileformat(filename)
+ if not okay then
+ report_otf("font %a is actually an %a file",filename,format)
+ end
+ return format
+end
local function load_featurefile(raw,featurefile)
if featurefile and featurefile~="" then
if trace_loading then
@@ -5396,7 +6674,7 @@ end
function enhancers.register(what,action)
actions[what]=action
end
-function otf.load(filename,format,sub,featurefile)
+function otf.load(filename,sub,featurefile)
local base=file.basename(file.removesuffix(filename))
local name=file.removesuffix(base)
local attr=lfs.attributes(filename)
@@ -5494,7 +6772,7 @@ function otf.load(filename,format,sub,featurefile)
data={
size=size,
time=time,
- format=format,
+ format=otf_format(filename),
featuredata=featurefiles,
resources={
filename=resolvers.unresolve(filename),
@@ -5515,7 +6793,7 @@ function otf.load(filename,format,sub,featurefile)
},
descriptions={},
goodies={},
- helpers={
+ helpers={
tounicodelist=splitter,
tounicodetable=lpeg.Ct(splitter),
},
@@ -5560,6 +6838,9 @@ function otf.load(filename,format,sub,featurefile)
report_otf("loading from cache using hash %a",hash)
end
enhance("unpack",data,filename,nil,false)
+ if applyruntimefixes then
+ applyruntimefixes(filename,data)
+ end
enhance("add dimensions",data,filename,nil,false)
if trace_sequences then
showfeatureorder(data,filename)
@@ -5819,20 +7100,20 @@ actions["check encoding"]=function(data,filename,raw)
if parentdescription then
local altuni=parentdescription.altuni
if not altuni then
- altuni={ { unicode=parent } }
+ altuni={ { unicode=unicode } }
parentdescription.altuni=altuni
duplicates[parent]={ unicode }
else
local done=false
for i=1,#altuni do
- if altuni[i].unicode==parent then
+ if altuni[i].unicode==unicode then
done=true
break
end
end
if not done then
- altuni[#altuni+1]={ unicode=parent }
- table.insert(duplicates[parent],unicode)
+ insert(altuni,{ unicode=unicode })
+ insert(duplicates[parent],unicode)
end
end
if trace_loading then
@@ -6035,14 +7316,6 @@ local g_directions={
gsub_reversecontextchain=-1,
gpos_reversecontextchain=-1,
}
-local function supported(features)
- for i=1,#features do
- if features[i].ismac then
- return false
- end
- end
- return true
-end
actions["reorganize subtables"]=function(data,filename,raw)
local resources=data.resources
local sequences={}
@@ -6056,7 +7329,6 @@ actions["reorganize subtables"]=function(data,filename,raw)
for k=1,#dw do
local gk=dw[k]
local features=gk.features
- if not features or supported(features) then
local typ=gk.type
local chain=g_directions[typ] or 0
local subtables=gk.subtables
@@ -6119,7 +7391,6 @@ actions["reorganize subtables"]=function(data,filename,raw)
markclass=markclass,
}
end
- end
end
end
end
@@ -6490,74 +7761,93 @@ actions["merge kern classes"]=function(data,filename,raw)
local resources=data.resources
local unicodes=resources.unicodes
local splitter=data.helpers.tounicodetable
+ local ignored=0
+ local blocked=0
for gp=1,#gposlist do
local gpos=gposlist[gp]
local subtables=gpos.subtables
if subtables then
+ local first_done={}
+ local split={}
for s=1,#subtables do
local subtable=subtables[s]
local kernclass=subtable.kernclass
+ local lookup=subtable.lookup or subtable.name
if kernclass then
- local split={}
- for k=1,#kernclass do
- local kcl=kernclass[k]
- local firsts=kcl.firsts
- local seconds=kcl.seconds
- local offsets=kcl.offsets
- local lookups=kcl.lookup
- if type(lookups)~="table" then
- lookups={ lookups }
- end
- for n,s in next,firsts do
- split[s]=split[s] or lpegmatch(splitter,s)
- end
- local maxseconds=0
- for n,s in next,seconds do
- if n>maxseconds then
- maxseconds=n
- end
- split[s]=split[s] or lpegmatch(splitter,s)
- end
- for l=1,#lookups do
- local lookup=lookups[l]
- for fk=1,#firsts do
- local fv=firsts[fk]
- local splt=split[fv]
- if splt then
- local extrakerns={}
- local baseoffset=(fk-1)*maxseconds
- for sk=2,maxseconds do
- local sv=seconds[sk]
- local splt=split[sv]
- if splt then
- local offset=offsets[baseoffset+sk]
- if offset then
- for i=1,#splt do
- extrakerns[splt[i]]=offset
- end
- end
+ if #kernclass>0 then
+ kernclass=kernclass[1]
+ lookup=type(kernclass.lookup)=="string" and kernclass.lookup or lookup
+ report_otf("fixing kernclass table of lookup %a",lookup)
+ end
+ local firsts=kernclass.firsts
+ local seconds=kernclass.seconds
+ local offsets=kernclass.offsets
+ for n,s in next,firsts do
+ split[s]=split[s] or lpegmatch(splitter,s)
+ end
+ local maxseconds=0
+ for n,s in next,seconds do
+ if n>maxseconds then
+ maxseconds=n
+ end
+ split[s]=split[s] or lpegmatch(splitter,s)
+ end
+ for fk=1,#firsts do
+ local fv=firsts[fk]
+ local splt=split[fv]
+ if splt then
+ local extrakerns={}
+ local baseoffset=(fk-1)*maxseconds
+ for sk=2,maxseconds do
+ local sv=seconds[sk]
+ local splt=split[sv]
+ if splt then
+ local offset=offsets[baseoffset+sk]
+ if offset then
+ for i=1,#splt do
+ extrakerns[splt[i]]=offset
end
end
- for i=1,#splt do
- local first_unicode=splt[i]
- local description=descriptions[first_unicode]
- if description then
- local kerns=description.kerns
- if not kerns then
- kerns={}
- description.kerns=kerns
- end
- local lookupkerns=kerns[lookup]
- if not lookupkerns then
- lookupkerns={}
- kerns[lookup]=lookupkerns
- end
+ end
+ end
+ for i=1,#splt do
+ local first_unicode=splt[i]
+ if first_done[first_unicode] then
+ report_otf("lookup %a: ignoring further kerns of %C",lookup,first_unicode)
+ blocked=blocked+1
+ else
+ first_done[first_unicode]=true
+ local description=descriptions[first_unicode]
+ if description then
+ local kerns=description.kerns
+ if not kerns then
+ kerns={}
+ description.kerns=kerns
+ end
+ local lookupkerns=kerns[lookup]
+ if not lookupkerns then
+ lookupkerns={}
+ kerns[lookup]=lookupkerns
+ end
+ if overloadkerns then
for second_unicode,kern in next,extrakerns do
lookupkerns[second_unicode]=kern
end
- elseif trace_loading then
- report_otf("no glyph data for %U",first_unicode)
+ else
+ for second_unicode,kern in next,extrakerns do
+ local k=lookupkerns[second_unicode]
+ if not k then
+ lookupkerns[second_unicode]=kern
+ elseif k~=kern then
+ if trace_loading then
+ report_otf("lookup %a: ignoring overload of kern between %C and %C, rejecting %a, keeping %a",lookup,first_unicode,second_unicode,k,kern)
+ end
+ ignored=ignored+1
+ end
+ end
end
+ elseif trace_loading then
+ report_otf("no glyph data for %U",first_unicode)
end
end
end
@@ -6568,6 +7858,12 @@ actions["merge kern classes"]=function(data,filename,raw)
end
end
end
+ if ignored>0 then
+ report_otf("%s kern overloads ignored",ignored)
+ end
+ if blocked>0 then
+ report_otf("%s succesive kerns blocked",blocked)
+ end
end
end
actions["check glyphs"]=function(data,filename,raw)
@@ -6781,10 +8077,19 @@ local function copytotfm(data,cache_id)
end
end
end
+ local filename=constructors.checkedfilename(resources)
+ local fontname=metadata.fontname
+ local fullname=metadata.fullname or fontname
+ local units=metadata.units_per_em or 1000
+ if units==0 then
+ units=1000
+ metadata.units_per_em=1000
+ report_otf("changing %a units to %a",0,units)
+ end
local monospaced=metadata.isfixedpitch or (pfminfo.panose and pfminfo.panose.proportion=="Monospaced")
local charwidth=pfminfo.avgwidth
- local italicangle=metadata.italicangle
local charxheight=pfminfo.os2_xheight and pfminfo.os2_xheight>0 and pfminfo.os2_xheight
+ local italicangle=metadata.italicangle
properties.monospaced=monospaced
parameters.italicangle=italicangle
parameters.charwidth=charwidth
@@ -6813,15 +8118,6 @@ local function copytotfm(data,cache_id)
end
end
spaceunits=tonumber(spaceunits) or 500
- local filename=constructors.checkedfilename(resources)
- local fontname=metadata.fontname
- local fullname=metadata.fullname or fontname
- local units=metadata.units_per_em or 1000
- if units==0 then
- units=1000
- metadata.units_per_em=1000
- report_otf("changing %a units to %a",0,units)
- end
parameters.slant=0
parameters.space=spaceunits
parameters.space_stretch=units/2
@@ -6830,10 +8126,10 @@ local function copytotfm(data,cache_id)
parameters.quad=units
if spaceunits<2*units/5 then
end
- if italicangle then
+ if italicangle and italicangle~=0 then
parameters.italicangle=italicangle
parameters.italicfactor=math.cos(math.rad(90+italicangle))
- parameters.slant=- math.round(math.tan(italicangle*math.pi/180))
+ parameters.slant=- math.tan(italicangle*math.pi/180)
end
if monospaced then
parameters.space_stretch=0
@@ -6860,7 +8156,7 @@ local function copytotfm(data,cache_id)
parameters.units=units
properties.space=spacer
properties.encodingbytes=2
- properties.format=data.format or fonts.formats[filename] or "opentype"
+ properties.format=data.format or otf_format(filename) or formats.otf
properties.noglyphnames=true
properties.filename=filename
properties.fontname=fontname
@@ -6885,9 +8181,8 @@ local function otftotfm(specification)
local name=specification.name
local sub=specification.sub
local filename=specification.filename
- local format=specification.format
local features=specification.features.normal
- local rawdata=otf.load(filename,format,sub,features and features.featurefile)
+ local rawdata=otf.load(filename,sub,features and features.featurefile)
if rawdata and next(rawdata) then
rawdata.lookuphash={}
tfmdata=copytotfm(rawdata,cache_id)
@@ -6969,41 +8264,33 @@ function otf.collectlookups(rawdata,kind,script,language)
end
return nil,nil
end
-local function check_otf(forced,specification,suffix,what)
+local function check_otf(forced,specification,suffix)
local name=specification.name
if forced then
- name=file.addsuffix(name,suffix,true)
+ name=specification.forcedname
end
local fullname=findbinfile(name,suffix) or ""
if fullname=="" then
fullname=fonts.names.getfilename(name,suffix) or ""
end
- if fullname~="" then
+ if fullname~="" and not fonts.names.ignoredfile(fullname) then
specification.filename=fullname
- specification.format=what
return read_from_otf(specification)
end
end
-local function opentypereader(specification,suffix,what)
+local function opentypereader(specification,suffix)
local forced=specification.forced or ""
- if forced=="otf" then
- return check_otf(true,specification,forced,"opentype")
- elseif forced=="ttf" or forced=="ttc" or forced=="dfont" then
- return check_otf(true,specification,forced,"truetype")
+ if formats[forced] then
+ return check_otf(true,specification,forced)
else
- return check_otf(false,specification,suffix,what)
+ return check_otf(false,specification,suffix)
end
end
-readers.opentype=opentypereader
-local formats=fonts.formats
-formats.otf="opentype"
-formats.ttf="truetype"
-formats.ttc="truetype"
-formats.dfont="truetype"
-function readers.otf (specification) return opentypereader(specification,"otf",formats.otf ) end
-function readers.ttf (specification) return opentypereader(specification,"ttf",formats.ttf ) end
-function readers.ttc (specification) return opentypereader(specification,"ttf",formats.ttc ) end
-function readers.dfont(specification) return opentypereader(specification,"ttf",formats.dfont) end
+readers.opentype=opentypereader
+function readers.otf (specification) return opentypereader(specification,"otf") end
+function readers.ttf (specification) return opentypereader(specification,"ttf") end
+function readers.ttc (specification) return opentypereader(specification,"ttf") end
+function readers.dfont(specification) return opentypereader(specification,"ttf") end
function otf.scriptandlanguage(tfmdata,attr)
local properties=tfmdata.properties
return properties.script or "dflt",properties.language or "dflt"
@@ -7594,6 +8881,7 @@ nodes.injections=nodes.injections or {}
local injections=nodes.injections
local nodecodes=nodes.nodecodes
local glyph_code=nodecodes.glyph
+local disc_code=nodecodes.disc
local kern_code=nodecodes.kern
local nuts=nodes.nuts
local nodepool=nuts.pool
@@ -7663,7 +8951,7 @@ function injections.setkern(current,factor,rlmode,x,tfmchr)
return 0,0
end
end
-function injections.setmark(start,base,factor,rlmode,ba,ma,index)
+function injections.setmark(start,base,factor,rlmode,ba,ma,index,baseismark)
local dx,dy=factor*(ba[1]-ma[1]),factor*(ba[2]-ma[2])
local bound=getattr(base,a_markbase)
local index=1
@@ -7684,7 +8972,7 @@ function injections.setmark(start,base,factor,rlmode,ba,ma,index)
setattr(base,a_markbase,bound)
setattr(start,a_markmark,bound)
setattr(start,a_markdone,index)
- marks[bound]={ [index]={ dx,dy,rlmode } }
+ marks[bound]={ [index]={ dx,dy,rlmode,baseismark } }
return dx,dy,bound
end
local function dir(n)
@@ -7805,7 +9093,7 @@ function injections.handler(head,where,keep)
local f=getfont(n)
if f~=nf then
nf=f
- tm=fontdata[nf].resources.marks
+ tm=fontdata[nf].resources.marks
end
if tm then
mk[n]=tm[getchar(n)]
@@ -7918,11 +9206,17 @@ function injections.handler(head,where,keep)
end
end
else
+ local wp=getfield(p,"width")
+ local wn=getfield(n,"width")
if rlmode and rlmode>=0 then
- ox=px-getfield(p,"width")+d[1]
+ ox=px-wp+d[1]
else
ox=px-d[1]
end
+ if wn~=0 then
+ insert_node_before(head,n,newkern(-wn/2))
+ insert_node_after(head,n,newkern(-wn/2))
+ end
end
setfield(n,"xoffset",ox)
local py=getfield(p,"yoffset")
@@ -7939,6 +9233,8 @@ function injections.handler(head,where,keep)
nofmarks=nofmarks-1
end
end
+ elseif not n_markmark then
+ break
else
end
end
@@ -8102,6 +9398,11 @@ local features={
medi=s_medi,
fina=s_fina,
isol=s_isol,
+ rphf=s_rphf,
+ half=s_half,
+ pref=s_pref,
+ blwf=s_blwf,
+ pstf=s_pstf,
}
analyzers.states=states
analyzers.features=features
@@ -8478,6 +9779,7 @@ local default="dflt"
local nodecodes=nodes.nodecodes
local whatcodes=nodes.whatcodes
local glyphcodes=nodes.glyphcodes
+local disccodes=nodes.disccodes
local glyph_code=nodecodes.glyph
local glue_code=nodecodes.glue
local disc_code=nodecodes.disc
@@ -8485,6 +9787,7 @@ local whatsit_code=nodecodes.whatsit
local math_code=nodecodes.math
local dir_code=whatcodes.dir
local localpar_code=whatcodes.localpar
+local discretionary_code=disccodes.discretionary
local ligature_code=glyphcodes.ligature
local privateattribute=attributes.private
local a_state=privateattribute('state')
@@ -8732,13 +10035,13 @@ local function get_alternative_glyph(start,alternatives,value,trace_alternatives
end
end
end
-local function multiple_glyphs(head,start,multiple)
+local function multiple_glyphs(head,start,multiple,ignoremarks)
local nofmultiples=#multiple
if nofmultiples>0 then
setfield(start,"char",multiple[1])
if nofmultiples>1 then
local sn=getnext(start)
- for k=2,nofmultiples do
+ for k=2,nofmultiples do
local n=copy_node(start)
setfield(n,"char",multiple[k])
setfield(n,"next",sn)
@@ -8773,11 +10076,11 @@ function handlers.gsub_alternate(head,start,kind,lookupname,alternative,sequence
end
return head,start,true
end
-function handlers.gsub_multiple(head,start,kind,lookupname,multiple)
+function handlers.gsub_multiple(head,start,kind,lookupname,multiple,sequence)
if trace_multiples then
logprocess("%s: replacing %s by multiple %s",pref(kind,lookupname),gref(getchar(start)),gref(multiple))
end
- return multiple_glyphs(head,start,multiple)
+ return multiple_glyphs(head,start,multiple,sequence.flags[1])
end
function handlers.gsub_ligature(head,start,kind,lookupname,ligature,sequence)
local s,stop,discfound=getnext(start),nil,false
@@ -8841,9 +10144,9 @@ function handlers.gsub_ligature(head,start,kind,lookupname,ligature,sequence)
break
end
end
- if stop then
- local lig=ligature.ligature
- if lig then
+ local lig=ligature.ligature
+ if lig then
+ if stop then
if trace_ligatures then
local stopchar=getchar(stop)
head,start=toligature(kind,lookupname,head,start,stop,lig,skipmark,discfound)
@@ -8851,9 +10154,14 @@ function handlers.gsub_ligature(head,start,kind,lookupname,ligature,sequence)
else
head,start=toligature(kind,lookupname,head,start,stop,lig,skipmark,discfound)
end
- return head,start,true
else
+ setfield(start,"char",lig)
+ if trace_ligatures then
+ logprocess("%s: replacing %s by (no real) ligature %s case 3",pref(kind,lookupname),gref(startchar),gref(lig))
+ end
end
+ return head,start,true
+ else
end
end
return head,start,false
@@ -9010,7 +10318,7 @@ function handlers.gpos_mark2mark(head,start,kind,lookupname,markanchors,sequence
if al[anchor] then
local ma=markanchors[anchor]
if ma then
- local dx,dy,bound=setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma)
+ local dx,dy,bound=setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma,true)
if trace_marks then
logprocess("%s, anchor %s, bound %s: anchoring mark %s to basemark %s => (%p,%p)",
pref(kind,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy)
@@ -9111,7 +10419,6 @@ function handlers.gpos_pair(head,start,kind,lookupname,kerns,sequence)
prev=snext
snext=getnext(snext)
else
- local krn=kerns[nextchar]
if not krn then
elseif type(krn)=="table" then
if lookuptype=="pair" then
@@ -9184,34 +10491,6 @@ function chainprocs.reversesub(head,start,stop,kind,chainname,currentcontext,loo
return head,start,false
end
end
-local function delete_till_stop(start,stop,ignoremarks)
- local n=1
- if start==stop then
- elseif ignoremarks then
- repeat
- local next=getnext(start)
- if not marks[getchar(next)] then
- local components=getfield(next,"components")
- if components then
- flush_node_list(components)
- end
- delete_node(start,next)
- end
- n=n+1
- until next==stop
- else
- repeat
- local next=getnext(start)
- local components=getfield(next,"components")
- if components then
- flush_node_list(components)
- end
- delete_node(start,next)
- n=n+1
- until next==stop
- end
- return n
-end
function chainprocs.gsub_single(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex)
local current=start
local subtables=currentlookup.subtables
@@ -9251,7 +10530,6 @@ function chainprocs.gsub_single(head,start,stop,kind,chainname,currentcontext,lo
end
chainmores.gsub_single=chainprocs.gsub_single
function chainprocs.gsub_multiple(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname)
- delete_till_stop(start,stop)
local startchar=getchar(start)
local subtables=currentlookup.subtables
local lookupname=subtables[1]
@@ -9270,7 +10548,7 @@ function chainprocs.gsub_multiple(head,start,stop,kind,chainname,currentcontext,
if trace_multiples then
logprocess("%s: replacing %s by multiple characters %s",cref(kind,chainname,chainlookupname,lookupname),gref(startchar),gref(replacements))
end
- return multiple_glyphs(head,start,replacements)
+ return multiple_glyphs(head,start,replacements,currentlookup.flags[1])
end
end
return head,start,false
@@ -9551,7 +10829,7 @@ function chainprocs.gpos_mark2mark(head,start,stop,kind,chainname,currentcontext
if al[anchor] then
local ma=markanchors[anchor]
if ma then
- local dx,dy,bound=setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma)
+ local dx,dy,bound=setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma,true)
if trace_marks then
logprocess("%s, anchor %s, bound %s: anchoring mark %s to basemark %s => (%p,%p)",
cref(kind,chainname,chainlookupname,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy)
@@ -9654,6 +10932,7 @@ function chainprocs.gpos_single(head,start,stop,kind,chainname,currentcontext,lo
end
return head,start,false
end
+chainmores.gpos_single=chainprocs.gpos_single
function chainprocs.gpos_pair(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex,sequence)
local snext=getnext(start)
if snext then
@@ -9722,6 +11001,7 @@ function chainprocs.gpos_pair(head,start,stop,kind,chainname,currentcontext,look
end
return head,start,false
end
+chainmores.gpos_pair=chainprocs.gpos_pair
local function show_skip(kind,chainname,char,ck,class)
if ck[9] then
logwarning("%s: skipping char %s, class %a, rule %a, lookuptype %a, %a => %a",cref(kind,chainname),gref(char),class,ck[1],ck[2],ck[9],ck[10])
@@ -9729,6 +11009,10 @@ local function show_skip(kind,chainname,char,ck,class)
logwarning("%s: skipping char %s, class %a, rule %a, lookuptype %a",cref(kind,chainname),gref(char),class,ck[1],ck[2])
end
end
+local quit_on_no_replacement=true
+directives.register("otf.chain.quitonnoreplacement",function(value)
+ quit_on_no_replacement=value
+end)
local function normal_handle_contextchain(head,start,kind,chainname,contexts,sequence,lookuphash)
local flags=sequence.flags
local done=false
@@ -9938,7 +11222,11 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq
if chainlookup then
local cp=chainprocs[chainlookup.type]
if cp then
- head,start,done=cp(head,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,nil,sequence)
+ local ok
+ head,start,ok=cp(head,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,nil,sequence)
+ if ok then
+ done=true
+ end
else
logprocess("%s: %s is not yet supported",cref(kind,chainname,chainlookupname),chainlookup.type)
end
@@ -9965,19 +11253,24 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq
end
end
local chainlookupname=chainlookups[i]
- local chainlookup=lookuptable[chainlookupname]
- local cp=chainlookup and chainmores[chainlookup.type]
- if cp then
- local ok,n
- head,start,ok,n=cp(head,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,i,sequence)
- if ok then
- done=true
- i=i+(n or 1)
- else
+ local chainlookup=lookuptable[chainlookupname]
+ if not chainlookup then
+ i=i+1
+ else
+ local cp=chainmores[chainlookup.type]
+ if not cp then
+ logprocess("%s: %s is not yet supported",cref(kind,chainname,chainlookupname),chainlookup.type)
i=i+1
+ else
+ local ok,n
+ head,start,ok,n=cp(head,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,i,sequence)
+ if ok then
+ done=true
+ i=i+(n or 1)
+ else
+ i=i+1
+ end
end
- else
- i=i+1
end
if start then
start=getnext(start)
@@ -9990,7 +11283,7 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq
if replacements then
head,start,done=chainprocs.reversesub(head,start,last,kind,chainname,ck,lookuphash,replacements)
else
- done=true
+ done=quit_on_no_replacement
if trace_contexts then
logprocess("%s: skipping match",cref(kind,chainname))
end
@@ -10187,6 +11480,65 @@ local function featuresprocessor(head,font,attr)
if not lookupcache then
report_missing_cache(typ,lookupname)
else
+ local function subrun(start)
+ local head=start
+ local done=false
+ while start do
+ local id=getid(start)
+ if id==glyph_code and getfont(start)==font and getsubtype(start)<256 then
+ local a=getattr(start,0)
+ if a then
+ a=(a==attr) and (not attribute or getattr(start,a_state)==attribute)
+ else
+ a=not attribute or getattr(start,a_state)==attribute
+ end
+ if a then
+ local lookupmatch=lookupcache[getchar(start)]
+ if lookupmatch then
+ local ok
+ head,start,ok=handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,1)
+ if ok then
+ done=true
+ end
+ end
+ if start then start=getnext(start) end
+ else
+ start=getnext(start)
+ end
+ else
+ start=getnext(start)
+ end
+ end
+ if done then
+ success=true
+ return head
+ end
+ end
+ local function kerndisc(disc)
+ local prev=getprev(disc)
+ local next=getnext(disc)
+ if prev and next then
+ setfield(prev,"next",next)
+ local a=getattr(prev,0)
+ if a then
+ a=(a==attr) and (not attribute or getattr(prev,a_state)==attribute)
+ else
+ a=not attribute or getattr(prev,a_state)==attribute
+ end
+ if a then
+ local lookupmatch=lookupcache[getchar(prev)]
+ if lookupmatch then
+ local h,d,ok=handler(head,prev,dataset[4],lookupname,lookupmatch,sequence,lookuphash,1)
+ if ok then
+ done=true
+ success=true
+ end
+ end
+ end
+ setfield(prev,"next",disc)
+ end
+ return next
+ end
while start do
local id=getid(start)
if id==glyph_code then
@@ -10213,6 +11565,27 @@ local function featuresprocessor(head,font,attr)
else
start=getnext(start)
end
+ elseif id==disc_code then
+ if getsubtype(start)==discretionary_code then
+ local pre=getfield(start,"pre")
+ if pre then
+ local new=subrun(pre)
+ if new then setfield(start,"pre",new) end
+ end
+ local post=getfield(start,"post")
+ if post then
+ local new=subrun(post)
+ if new then setfield(start,"post",new) end
+ end
+ local replace=getfield(start,"replace")
+ if replace then
+ local new=subrun(replace)
+ if new then setfield(start,"replace",new) end
+ end
+elseif typ=="gpos_single" or typ=="gpos_pair" then
+ kerndisc(start)
+ end
+ start=getnext(start)
elseif id==whatsit_code then
local subtype=getsubtype(start)
if subtype==dir_code then
@@ -10257,6 +11630,84 @@ local function featuresprocessor(head,font,attr)
end
end
else
+ local function subrun(start)
+ local head=start
+ local done=false
+ while start do
+ local id=getid(start)
+ if id==glyph_code and getfont(start)==font and getsubtype(start)<256 then
+ local a=getattr(start,0)
+ if a then
+ a=(a==attr) and (not attribute or getattr(start,a_state)==attribute)
+ else
+ a=not attribute or getattr(start,a_state)==attribute
+ end
+ if a then
+ for i=1,ns do
+ local lookupname=subtables[i]
+ local lookupcache=lookuphash[lookupname]
+ if lookupcache then
+ local lookupmatch=lookupcache[getchar(start)]
+ if lookupmatch then
+ local ok
+ head,start,ok=handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,i)
+ if ok then
+ done=true
+ break
+ elseif not start then
+ break
+ end
+ end
+ else
+ report_missing_cache(typ,lookupname)
+ end
+ end
+ if start then start=getnext(start) end
+ else
+ start=getnext(start)
+ end
+ else
+ start=getnext(start)
+ end
+ end
+ if done then
+ success=true
+ return head
+ end
+ end
+ local function kerndisc(disc)
+ local prev=getprev(disc)
+ local next=getnext(disc)
+ if prev and next then
+ setfield(prev,"next",next)
+ local a=getattr(prev,0)
+ if a then
+ a=(a==attr) and (not attribute or getattr(prev,a_state)==attribute)
+ else
+ a=not attribute or getattr(prev,a_state)==attribute
+ end
+ if a then
+ for i=1,ns do
+ local lookupname=subtables[i]
+ local lookupcache=lookuphash[lookupname]
+ if lookupcache then
+ local lookupmatch=lookupcache[getchar(prev)]
+ if lookupmatch then
+ local h,d,ok=handler(head,prev,dataset[4],lookupname,lookupmatch,sequence,lookuphash,i)
+ if ok then
+ done=true
+ break
+ end
+ end
+ else
+ report_missing_cache(typ,lookupname)
+ end
+ end
+ end
+ setfield(prev,"next",disc)
+ end
+ return next
+ end
while start do
local id=getid(start)
if id==glyph_code then
@@ -10294,6 +11745,27 @@ local function featuresprocessor(head,font,attr)
else
start=getnext(start)
end
+ elseif id==disc_code then
+ if getsubtype(start)==discretionary_code then
+ local pre=getfield(start,"pre")
+ if pre then
+ local new=subrun(pre)
+ if new then setfield(start,"pre",new) end
+ end
+ local post=getfield(start,"post")
+ if post then
+ local new=subrun(post)
+ if new then setfield(start,"post",new) end
+ end
+ local replace=getfield(start,"replace")
+ if replace then
+ local new=subrun(replace)
+ if new then setfield(start,"replace",new) end
+ end
+elseif typ=="gpos_single" or typ=="gpos_pair" then
+ kerndisc(start)
+ end
+ start=getnext(start)
elseif id==whatsit_code then
local subtype=getsubtype(start)
if subtype==dir_code then
@@ -11454,6 +12926,7 @@ if not modules then modules={} end modules ['font-def']={
local format,gmatch,match,find,lower,gsub=string.format,string.gmatch,string.match,string.find,string.lower,string.gsub
local tostring,next=tostring,next
local lpegmatch=lpeg.match
+local suffixonly,removesuffix=file.suffix,file.removesuffix
local allocate=utilities.storage.allocate
local trace_defining=false trackers .register("fonts.defining",function(v) trace_defining=v end)
local directive_embedall=false directives.register("fonts.embedall",function(v) directive_embedall=v end)
@@ -11501,7 +12974,7 @@ addlookup("file")
addlookup("name")
addlookup("spec")
local function getspecification(str)
- return lpegmatch(splitter,str)
+ return lpegmatch(splitter,str or "")
end
definers.getspecification=getspecification
function definers.registersplit(symbol,action,verbosename)
@@ -11543,10 +13016,11 @@ definers.resolvers=definers.resolvers or {}
local resolvers=definers.resolvers
function resolvers.file(specification)
local name=resolvefile(specification.name)
- local suffix=file.suffix(name)
+ local suffix=lower(suffixonly(name))
if fonts.formats[suffix] then
specification.forced=suffix
- specification.name=file.removesuffix(name)
+ specification.forcedname=name
+ specification.name=removesuffix(name)
else
specification.name=name
end
@@ -11558,10 +13032,11 @@ function resolvers.name(specification)
if resolved then
specification.resolved=resolved
specification.sub=sub
- local suffix=file.suffix(resolved)
+ local suffix=lower(suffixonly(resolved))
if fonts.formats[suffix] then
specification.forced=suffix
- specification.name=file.removesuffix(resolved)
+ specification.forcedname=resolved
+ specification.name=removesuffix(resolved)
else
specification.name=resolved
end
@@ -11577,8 +13052,9 @@ function resolvers.spec(specification)
if resolved then
specification.resolved=resolved
specification.sub=sub
- specification.forced=file.suffix(resolved)
- specification.name=file.removesuffix(resolved)
+ specification.forced=lower(suffixonly(resolved))
+ specification.forcedname=resolved
+ specification.name=removesuffix(resolved)
end
else
resolvers.name(specification)
@@ -11593,8 +13069,7 @@ function definers.resolve(specification)
end
if specification.forced=="" then
specification.forced=nil
- else
- specification.forced=specification.forced
+ specification.forcedname=nil
end
specification.hash=lower(specification.name..' @ '..constructors.hashfeatures(specification))
if specification.sub and specification.sub~="" then
@@ -11639,7 +13114,7 @@ function definers.loadfont(specification)
if not tfmdata then
local forced=specification.forced or ""
if forced~="" then
- local reader=readers[lower(forced)]
+ local reader=readers[lower(forced)]
tfmdata=reader and reader(specification)
if not tfmdata then
report_defining("forced type %a of %a not found",forced,specification.name)
diff --git a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-glyphlist.lua b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-glyphlist.lua
index 2db1fed9dcb..60afd7230a3 100644
--- a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-glyphlist.lua
+++ b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-glyphlist.lua
@@ -1,5 +1,5 @@
if not modules then modules = { } end modules ["font-age"] = {
- version = 2.300,
+ version = 2.400,
comment = "part of the luaotfload package",
author = "luaotfload team / mkglyphlist",
copyright = "derived from http://partners.adobe.com/public/developer/en/opentype/glyphlist.txt",
diff --git a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-letterspace.lua b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-letterspace.lua
index 880c7bddeda..7c5a9675354 100644
--- a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-letterspace.lua
+++ b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-letterspace.lua
@@ -1,57 +1,187 @@
if not modules then modules = { } end modules ['letterspace'] = {
- version = "2.3a",
+ version = "2.4",
comment = "companion to luaotfload.lua",
author = "Hans Hagen, PRAGMA-ADE, Hasselt NL; adapted by Philipp Gesang",
copyright = "PRAGMA ADE / ConTeXt Development Team",
license = "see context related readme files"
}
+local getmetatable = getmetatable
+local require = require
+local setmetatable = setmetatable
+local tonumber = tonumber
+
local next = next
local nodes, node, fonts = nodes, node, fonts
local find_node_tail = node.tail or node.slide
local free_node = node.free
-local free_nodelist = node.flush_list
local copy_node = node.copy
-local copy_nodelist = node.copy_list
+local new_node = node.new
local insert_node_before = node.insert_before
-local insert_node_after = node.insert_after
local nodepool = nodes.pool
-local tasks = nodes.tasks
local new_kern = nodepool.kern
local new_glue = nodepool.glue
local nodecodes = nodes.nodecodes
-local kerncodes = nodes.kerncodes
-local skipcodes = nodes.skipcodes
local glyph_code = nodecodes.glyph
local kern_code = nodecodes.kern
local disc_code = nodecodes.disc
-local glue_code = nodecodes.glue
-local hlist_code = nodecodes.hlist
-local vlist_code = nodecodes.vlist
local math_code = nodecodes.math
-local kerning_code = kerncodes.kerning
-local userkern_code = kerncodes.userkern
+local fonthashes = fonts.hashes
+local chardata = fonthashes.characters
+local quaddata = fonthashes.quads
+local otffeatures = fonts.constructors.newfeatures "otf"
+
+--[[doc--
+
+ Since the letterspacing method was derived initially from Context’s
+ typo-krn.lua we keep the sub-namespace “letterspace” inside the
+ “luaotfload” table.
+
+--doc]]--
+
+luaotfload.letterspace = luaotfload.letterspace or { }
+local letterspace = luaotfload.letterspace
+
+letterspace.keepligature = false
+letterspace.keeptogether = false
+
+---=================================================================---
+--- preliminary definitions
+---=================================================================---
+-- We set up a layer emulating some Context internals that are needed
+-- for the letterspacing callback.
+-----------------------------------------------------------------------
+--- node-ini
+-----------------------------------------------------------------------
+
+local bothways = function (t) return table.swapped (t, t) end
+local kerncodes = bothways { [0] = "fontkern"
+ , [1] = "userkern"
+ , [2] = "accentkern"
+ }
+
+kerncodes.kerning = kerncodes.fontkern --- idiosyncrasy
+local kerning_code = kerncodes.kerning
+local userkern_code = kerncodes.userkern
+
+
+-----------------------------------------------------------------------
+--- node-res
+-----------------------------------------------------------------------
+
+nodes.pool = nodes.pool or { }
+local pool = nodes.pool
+
+local kern = new_node ("kern", kerncodes.userkern)
+local glue_spec = new_node "glue_spec"
+
+pool.kern = function (k)
+ local n = copy_node (kern)
+ n.kern = k
+ return n
+end
+
+pool.glue = function (width, stretch, shrink,
+ stretch_order, shrink_order)
+ local n = new_node"glue"
+ if not width then
+ -- no spec
+ elseif width == false or tonumber(width) then
+ local s = copy_node(glue_spec)
+ if width then s.width = width end
+ if stretch then s.stretch = stretch end
+ if shrink then s.shrink = shrink end
+ if stretch_order then s.stretch_order = stretch_order end
+ if shrink_order then s.shrink_order = shrink_order end
+ n.spec = s
+ else
+ -- shared
+ n.spec = copy_node(width)
+ end
+ return n
+end
+-----------------------------------------------------------------------
+--- font-hsh
+-----------------------------------------------------------------------
+--- some initialization resembling font-hsh
local fonthashes = fonts.hashes
+local identifiers = fonthashes.identifiers --- was: fontdata
local chardata = fonthashes.characters
local quaddata = fonthashes.quads
+local parameters = fonthashes.parameters
-typesetters = typesetters or { }
-local typesetters = typesetters
+--- ('a, 'a) hash -> (('a, 'a) hash -> 'a -> 'a) -> ('a, 'a) hash
+local setmetatableindex = function (t, f)
+ local mt = getmetatable(t)
+ if mt then
+ mt.__index = f
+ else
+ setmetatable(t, { __index = f })
+ end
+ return t
+end
-typesetters.kernfont = typesetters.kernfont or { }
-local kernfont = typesetters.kernfont
+if not parameters then
+ parameters = { }
+ setmetatableindex(parameters, function(t, k)
+ if k == true then
+ return parameters[currentfont()]
+ else
+ local parameters = identifiers[k].parameters
+ t[k] = parameters
+ return parameters
+ end
+ end)
+ --fonthashes.parameters = parameters
+end
-kernfont.keepligature = false
-kernfont.keeptogether = false
+if not chardata then
+ chardata = { }
+ setmetatableindex(chardata, function(t, k)
+ if k == true then
+ return chardata[currentfont()]
+ else
+ local tfmdata = identifiers[k]
+ if not tfmdata then --- unsafe
+ tfmdata = font.fonts[k]
+ end
+ if tfmdata then
+ local characters = tfmdata.characters
+ t[k] = characters
+ return characters
+ end
+ end
+ end)
+ fonthashes.characters = chardata
+end
+
+if not quaddata then
+ quaddata = { }
+ setmetatableindex(quaddata, function(t, k)
+ if k == true then
+ return quads[currentfont()]
+ else
+ local parameters = parameters[k]
+ local quad = parameters and parameters.quad or 0
+ t[k] = quad
+ return quad
+ end
+ end)
+ --fonthashes.quads = quaddata
+end
+
+---=================================================================---
+--- character kerning functionality
+---=================================================================---
-local kern_injector = function (fillup,kern)
+local kern_injector = function (fillup, kern)
if fillup then
local g = new_glue(kern)
local s = g.spec
@@ -66,13 +196,11 @@ end
--[[doc--
Caveat lector.
- This is a preliminary, makeshift adaptation of the Context
- character kerning mechanism that emulates XeTeX-style fontwise
- letterspacing. Note that in its present state it is far inferior to
- the original, which is attribute-based and ignores font-boundaries.
- Nevertheless, due to popular demand the following callback has been
- added. It should not be relied upon to be present in future
- versions.
+ This is an adaptation of the Context character kerning mechanism
+ that emulates XeTeX-style fontwise letterspacing. Note that in its
+ present state it is far inferior to the original, which is
+ attribute-based and ignores font-boundaries. Nevertheless, due to
+ popular demand the following callback has been added.
--doc]]--
@@ -82,13 +210,15 @@ local kerncharacters
kerncharacters = function (head)
local start, done = head, false
local lastfont = nil
- local keepligature = kernfont.keepligature --- function
- local keeptogether = kernfont.keeptogether --- function
+ local keepligature = letterspace.keepligature --- function
+ local keeptogether = letterspace.keeptogether --- function
local fillup = false
local identifiers = fonthashes.identifiers
local kernfactors = kernfactors
+ local firstkern = true
+
while start do
local id = start.id
if id == glyph_code then
@@ -112,7 +242,14 @@ kerncharacters = function (head)
kernfactors[fontid] = krn
end
if not krn or krn == 0 then
+ firstkern = true
goto nextnode
+ elseif firstkern then
+ firstkern = false
+ if (id ~= disc_code) and (not start.components) then
+ --- not a ligature, skip node
+ goto nextnode
+ end
end
end
@@ -127,11 +264,12 @@ kerncharacters = function (head)
--- 2) resolve ligatures
local c = start.components
+
if c then
if keepligature and keepligature(start) then
-- keep 'm
else
- c = kerncharacters (c)
+ --- c = kerncharacters (c) --> taken care of after replacing
local s = start
local p, n = s.prev, s.next
local tail = find_node_tail(c)
@@ -265,7 +403,139 @@ kerncharacters = function (head)
return head, done
end
-kernfont.handler = kerncharacters
+---=================================================================---
+--- integration
+---=================================================================---
+
+--- · callback: kerncharacters
+--- · enabler: enablefontkerning
+--- · disabler: disablefontkerning
+
+--- callback wrappers
+
+--- (node_t -> node_t) -> string -> string list -> bool
+local registered_as = { } --- procname -> callbacks
+local add_processor = function (processor, name, ...)
+ local callbacks = { ... }
+ for i=1, #callbacks do
+ luatexbase.add_to_callback(callbacks[i], processor, name)
+ end
+ registered_as[name] = callbacks --- for removal
+ return true
+end
+
+--- string -> bool
+local remove_processor = function (name)
+ local callbacks = registered_as[name]
+ if callbacks then
+ for i=1, #callbacks do
+ luatexbase.remove_from_callback(callbacks[i], name)
+ end
+ return true
+ end
+ return false --> unregistered
+end
+
+--- now for the simplistic variant
+--- unit -> bool
+local enablefontkerning = function ( )
+ return add_processor( kerncharacters
+ , "luaotfload.letterspace"
+ , "pre_linebreak_filter"
+ , "hpack_filter")
+end
+
+--- unit -> bool
+local disablefontkerning = function ( )
+ return remove_processor "luaotfload.letterspace"
+end
+
+--[[doc--
+
+ Fontwise kerning is enabled via the “kernfactor” option at font
+ definition time. Unlike the Context implementation which relies on
+ Luatex attributes, it uses a font property for passing along the
+ letterspacing factor of a node.
+
+ The callback is activated the first time a letterspaced font is
+ requested and stays active until the end of the run. Since the font
+ is a property of individual glyphs, every glyph in the entire
+ document must be checked for the kern property. This is quite
+ inefficient compared to Context’s attribute based approach, but Xetex
+ compatibility reduces our options significantly.
+
+--doc]]--
+
+
+local fontkerning_enabled = false --- callback state
+
+--- fontobj -> float -> unit
+local initializefontkerning = function (tfmdata, factor)
+ if factor ~= "max" then
+ factor = tonumber (factor) or 0
+ end
+ if factor == "max" or factor ~= 0 then
+ local fontproperties = tfmdata.properties
+ if fontproperties then
+ --- hopefully this field stays unused otherwise
+ fontproperties.kerncharacters = factor
+ end
+ if not fontkerning_enabled then
+ fontkerning_enabled = enablefontkerning ()
+ end
+ end
+end
+
+--- like the font colorization, fontwise kerning is hooked into the
+--- feature mechanism
+
+otffeatures.register {
+ name = "kernfactor",
+ description = "kernfactor",
+ initializers = {
+ base = initializefontkerning,
+ node = initializefontkerning,
+ }
+}
+
+--[[doc--
+
+ The “letterspace” feature is essentially identical with the above
+ “kernfactor” method, but scales the factor to percentages to match
+ Xetex’s behavior. (See the Xetex reference, page 5, section 1.2.2.)
+
+ Since Xetex doesn’t appear to have a (documented) “max” keyword, we
+ assume all input values are numeric.
+
+--doc]]--
+
+local initializecompatfontkerning = function (tfmdata, percentage)
+ local factor = tonumber (percentage)
+ if not factor then
+ logs.names_report ("both", 0, "letterspace",
+ "Invalid argument to letterspace: %s (type %q), " ..
+ "was expecting percentage as Lua number instead.",
+ percentage, type (percentage))
+ return
+ end
+ return initializefontkerning (tfmdata, factor * 0.01)
+end
+
+otffeatures.register {
+ name = "letterspace",
+ description = "letterspace",
+ initializers = {
+ base = initializecompatfontkerning,
+ node = initializecompatfontkerning,
+ }
+}
+
+--[[example--
+
+See https://bitbucket.org/phg/lua-la-tex-tests/src/tip/pln-letterspace-8-compare.tex
+for an example.
+
+--example]]--
--- vim:sw=2:ts=2:expandtab:tw=71
diff --git a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-loaders.lua b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-loaders.lua
index 63e1d088cf7..523dc2468b2 100644
--- a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-loaders.lua
+++ b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-loaders.lua
@@ -1,33 +1,30 @@
if not modules then modules = { } end modules ["loaders"] = {
- version = "2.3a",
+ version = "2.4",
comment = "companion to luaotfload.lua",
author = "Hans Hagen, Khaled Hosny, Elie Roux, Philipp Gesang",
copyright = "PRAGMA ADE / ConTeXt Development Team",
license = "see context related readme files"
}
-local fonts = fonts
-local readers = fonts.readers
-
----
---- opentype reader (from font-otf.lua):
---- (spec : table) -> (suffix : string) -> (format : string) -> (font : table)
----
+local fonts = fonts
+local readers = fonts.readers
+local handlers = fonts.handlers
+local formats = fonts.formats
local pfb_reader = function (specification)
- return readers.opentype(specification,"pfb","type1")
-end
-
+ return readers.opentype (specification, "pfb", "type1")
+end
+
local pfa_reader = function (specification)
- return readers.opentype(specification,"pfa","type1")
+ return readers.opentype (specification, "pfa", "type1")
end
-fonts.formats.pfb = "type1"
-fonts.readers.pfb = pfb_reader
-fonts.handlers.pfb = { } --- empty, as with tfm
+formats.pfa = "type1"
+readers.pfa = pfa_reader
+handlers.pfa = { }
-fonts.formats.pfa = "type1"
-fonts.readers.pfa = pfa_reader
-fonts.handlers.pfa = { }
+formats.pfb = "type1"
+readers.pfb = pfb_reader
+handlers.pfb = { }
-- vim:tw=71:sw=2:ts=2:expandtab
diff --git a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-override.lua b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-override.lua
index 19cd505ebf5..b1773fa0406 100644
--- a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-override.lua
+++ b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-override.lua
@@ -1,5 +1,5 @@
if not modules then modules = { } end modules ['luat-ovr'] = {
- version = "2.3a",
+ version = "2.4",
comment = "companion to luatex-*.tex",
author = "Khaled Hosny, Elie Roux, Philipp Gesang",
copyright = "Luaotfload Development Team",
@@ -32,27 +32,15 @@ local texio_write = texio.write
local texiowrite = texio.write
local type = type
+local dummyfunction = function () end
+
local texjob = false
if tex and (tex.jobname or tex.formatname) then
--- TeX
texjob = true
end
---[[doc--
-We recreate the verbosity levels previously implemented in font-nms:
-
- ==========================================================
- lvl arg trace_loading trace_search suppress_output
- ----------------------------------------------------------
- (0) -> -q ⊥ ⊥ ⊤
- (1) -> ∅ ⊥ ⊥ ⊥
- (2) -> -v ⊤ ⊥ ⊥
- (>2) -> -vv ⊤ ⊤ ⊥
- ==========================================================
-
---doc]]--
-
-local loglevel = 1 --- default
+local loglevel = 0 --- default
local logout = "log"
--- int -> bool
@@ -74,7 +62,9 @@ logs.getloglevel = get_loglevel
logs.get_loglevel = get_loglevel
logs.get_log_level = get_loglevel
-local writeln --- scope so we can change it
+local writeln --- pointer to terminal/log writer
+local statusln --- terminal writer that reuses the current line
+local first_status = true --- indicate the begin of a status region
local log_msg = [[
logging output redirected to %s
@@ -99,8 +89,9 @@ end
local set_logout = function (s, finalizers)
if s == "stdout" then
- logout = "term"
+ logout = "redirect"
elseif s == "file" then --- inject custom logger
+ logout = "redirect"
local chan = choose_logfile ()
chan:write (stringformat ("logging initiated at %s",
osdate ("%F %T", ostime ())))
@@ -125,6 +116,7 @@ local set_logout = function (s, finalizers)
texiowrite = writefile
texiowrite_nl = writefile_nl
writeln = writefile_nl
+ statusln = dummyfunction
finalizers[#finalizers+1] = function ()
chan:write (stringformat ("\nlogging finished at %s\n",
@@ -143,8 +135,10 @@ logs.set_logout = set_logout
local log = function (category, fmt, ...)
local res = { module_name, "|", category, ":" }
- if fmt then res[#res+1] = stringformat(fmt, ...) end
- texiowrite_nl(logout, tableconcat(res, " "))
+ if fmt then
+ res [#res + 1] = stringformat (fmt, ...)
+ end
+ texiowrite_nl (logout, tableconcat(res, " "))
end
--- with faux db update with maximum verbosity:
@@ -161,18 +155,34 @@ end
io.stdout:setvbuf "no"
io.stderr:setvbuf "no"
+local kill_line = "\r\x1b[K"
+
if texjob == true then
+ --- We imitate the texio.* functions so the output is consistent.
writeln = function (str)
- texiowrite_nl ("term", str)
+ iowrite "\n"
+ iowrite(str)
+ end
+ statusln = function (str)
+ if first_status == false then
+ iowrite (kill_line)
+ end
+ iowrite (str)
end
else
writeln = function (str)
iowrite(str)
iowrite "\n"
end
+ statusln = function (str)
+ if first_status == false then
+ iowrite (kill_line)
+ end
+ iowrite (str)
+ end
end
-stdout = function (category, ...)
+stdout = function (writer, category, ...)
local res = { module_name, "|", category, ":" }
local nargs = select("#", ...)
if nargs == 0 then
@@ -183,7 +193,7 @@ stdout = function (category, ...)
else
res[#res+1] = stringformat(...)
end
- writeln(tableconcat(res, " "))
+ writer (tableconcat(res, " "))
end
--- at default (zero), we aim to be quiet
@@ -224,11 +234,11 @@ local names_report = function (mode, lvl, ...)
if loglevel >= lvl then
if mode == "log" then
log (...)
- elseif mode == "both" and log ~= "stdout" then
+ elseif mode == "both" and logout ~= "redirect" then
log (...)
- stdout (...)
+ stdout (writeln, ...)
else
- stdout (...)
+ stdout (writeln, ...)
end
end
end
@@ -237,6 +247,95 @@ logs.names_report = names_report
--[[doc--
+ status_logger -- Overwrites the most recently printed line of the
+ terminal. Its purpose is to provide feedback without spamming
+ stdout with irrelevant messages, i.e. when building the database.
+
+ Status logging must be initialized by calling status_start() and
+ properly reset via status_stop().
+
+ The arguments low and high indicate the loglevel threshold at which
+ linewise and full logging is triggered, respectively. E.g.
+
+ names_status (1, 4, "term", "Hello, world!")
+
+ will print nothing if the loglevel is less than one, reuse the
+ current line if the loglevel ranges from one to three inclusively,
+ and output the message on a separate line otherwise.
+
+--doc]]--
+
+local status_logger = function (mode, ...)
+ if mode == "log" then
+ log (...)
+ else
+ if mode == "both" and logout ~= "redirect" then
+ log (...)
+ stdout (statusln, ...)
+ else
+ stdout (statusln, ...)
+ end
+ first_status = false
+ end
+end
+
+--[[doc--
+
+ status_start -- Initialize status logging. This installs the status
+ logger if the loglevel is in the specified range, and the normal
+ logger otherwise. It also resets the first line state which
+ causing the next line printed using the status logger to not kill
+ the current line.
+
+--doc]]--
+
+local status_writer
+local status_low = 99
+local status_high = 99
+
+local status_start = function (low, high)
+ first_status = true
+ status_low = low
+ status_high = high
+
+ if os.type == "windows" --- Assume broken terminal.
+ or os.getenv "TERM" == "dumb"
+ then
+ status_writer = function (mode, ...)
+ names_report (mode, high, ...)
+ end
+ return
+ end
+
+ if low <= loglevel and loglevel < high then
+ status_writer = status_logger
+ else
+ status_writer = function (mode, ...)
+ names_report (mode, high, ...)
+ end
+ end
+end
+
+--[[doc--
+
+ status_stop -- Finalize a status region by outputting a newline and
+ printing a message.
+
+--doc]]--
+
+local status_stop = function (...)
+ if first_status == false then
+ status_writer(...)
+ writeln ""
+ end
+end
+
+logs.names_status = function (...) status_writer (...) end
+logs.names_status_start = status_start
+logs.names_status_stop = status_stop
+
+--[[doc--
+
The fontloader comes with the Context logging mechanisms
inaccessible. Instead, it provides dumb fallbacks based
on the functions in texio.write*() that can be overridden
@@ -273,7 +372,6 @@ texio.reporter = texioreporter
if fonts then --- need to be running TeX
if next(fonts.encodings.agl) then
- print(next, fonts.encodings.agl)
--- unnecessary because the file shouldn’t be loaded at this time
--- but we’re just making sure
fonts.encodings.agl = nil
diff --git a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-status.lua b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-status.lua
index 892c1c0455f..2ee0421a761 100644
--- a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-status.lua
+++ b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-status.lua
@@ -1,34 +1,33 @@
return {
["hashes"]={
- { "mkstatus", "6bdb95e6db9c8b876870db60e0049d06" },
- { "mkglyphlist", "6bbd75b34221405e6fec73e44c72bd5f" },
- { "mkcharacters", "df124e7b73de233413ae40f34f999e09" },
- { "luaotfload-typo-krn.lua", "d6b4823ee731539889719c57ee7259f3" },
- { "luaotfload-tool.lua", "af9160bc9b08ceacb72e9e38fce2fbb0" },
- { "luaotfload-override.lua", "047041a60ca793c845c8375f87b48f8d" },
- { "luaotfload-merged.lua", "caffcef2f4d47554b29da5a0de6b52e0" },
- { "luaotfload.lua", "5108046376f08f6053868918032d6ee9" },
- { "luaotfload-loaders.lua", "3e86397babbb0e1d6a7c0c415dc85dc3" },
- { "luaotfload-letterspace.lua", "0ab68661c8a7b3515621f35b5c792b00" },
- { "luaotfload-glyphlist.lua", "8c832da1bec6cc8d310cc300a88de7f7" },
+ { "mkstatus", "51af30205d30c2b7f5bfd5169d9911a0" },
+ { "mkglyphlist", "a8d64d29927ad8aebd1280a11d335895" },
+ { "mkcharacters", "f25363cbe04fc5f1210930a10635247f" },
+ { "luaotfload-tool.lua", "eb34dc11cc91c01de4626bba5ff9ae17" },
+ { "luaotfload-override.lua", "b878d157af7147e30a067a41d3ea3cfe" },
+ { "luaotfload-fontloader.lua", "21b625efe6630b76250e18f7d2832cff" },
+ { "luaotfload.lua", "2321885f4704a11001e51fcf0afc0835" },
+ { "luaotfload-loaders.lua", "b04c6d9aa2974d56895fe3dff3fea90d" },
+ { "luaotfload-letterspace.lua", "19e6181076dda7c8c34e7096cee1f873" },
+ { "luaotfload-glyphlist.lua", "ff440162d1b8a78a586375ee65630c21" },
{ "luaotfload-fonts-tfm.lua", "8fd3865240e4e87e99e0739abeda2322" },
{ "luaotfload-fonts-lua.lua", "50b8edb1db7009b6c661ab71ff24a466" },
{ "luaotfload-fonts-ext.lua", "0eee87fb26b7d135da88ac0a43a8037a" },
{ "luaotfload-fonts-enc.lua", "32775266bfc9eb6bbd9a16e3a7de398d" },
{ "luaotfload-fonts-def.lua", "8dc05f2aa96d2fd0245242c8fd3320eb" },
{ "luaotfload-fonts-cbk.lua", "c1b7a4e5508d68f45a99cdc38c2d0885" },
- { "luaotfload-features.lua", "b5c9403b60539ae6e3e0bf2efe0f0916" },
- { "luaotfload-extralibs.lua", "ffb6908641364d64c24159905689f30f" },
- { "luaotfload-database.lua", "f0fc2ba998795caa7dc5efd79f94fc10" },
- { "luaotfload-colors.lua", "986bcc37ba516e07da47141cc3b7dd9c" },
- { "luaotfload-characters.lua", "5843d02ff98a7419fae5a3cf904b101e" },
+ { "luaotfload-features.lua", "0d97a50852fb55be4133874c8f487832" },
+ { "luaotfload-diagnostics.lua", "eed12e7b7fe6545daa6786fed1405a4f" },
+ { "luaotfload-database.lua", "7280e8cbdf00a4767272a39c0545af29" },
+ { "luaotfload-colors.lua", "248c4c42586c17e934a4d046c3c873b9" },
+ { "luaotfload-characters.lua", "2673ffb19e8505af4782aad437a76a4f" },
{ "luaotfload-basics-nod.lua", "d6b26ec86c0b1c1516936b425df04432" },
- { "luaotfload-basics-gen.lua", "041710d9e3076d394487dca93039022b" },
- { "luaotfload-auxiliary.lua", "d071f1723afac0ca30b18573cd97cf2a" },
+ { "luaotfload-basics-gen.lua", "3dadc2cd31ab348f7effb28e335af3ae" },
+ { "luaotfload-auxiliary.lua", "7efa9d365f12006d5c4c7c38e0e2872c" },
},
["notes"]={
["committer"]="Philipp Gesang <phg42.2a@gmail.com>",
- ["revision"]="2331b6838f623d4800e79e92c50ce55f955d7776",
- ["timestamp"]="2013-07-24 00:19:09 +0200",
+ ["revision"]="a679d233954dc989621bb6754d838dc537fd3d02",
+ ["timestamp"]="2013-12-31 17:04:24 +0100",
},
} \ No newline at end of file
diff --git a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-typo-krn.lua b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-typo-krn.lua
deleted file mode 100644
index fb394044f62..00000000000
--- a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-typo-krn.lua
+++ /dev/null
@@ -1,335 +0,0 @@
-if not modules then modules = { } end modules ['typo-krn'] = {
- version = 1.001,
- comment = "companion to typo-krn.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
-local next, type, tonumber = next, type, tonumber
-local utfchar = utf.char
-
-local nodes, node, fonts = nodes, node, fonts
-
-local find_node_tail = node.tail or node.slide
-local free_node = node.free
-local free_nodelist = node.flush_list
-local copy_node = node.copy
-local copy_nodelist = node.copy_list
-local insert_node_before = node.insert_before
-local insert_node_after = node.insert_after
-local end_of_math = node.end_of_math
-
-local texsetattribute = tex.setattribute
-local unsetvalue = attributes.unsetvalue
-
-local nodepool = nodes.pool
-local tasks = nodes.tasks
-
-local new_gluespec = nodepool.gluespec
-local new_kern = nodepool.kern
-local new_glue = nodepool.glue
-
-local nodecodes = nodes.nodecodes
-local kerncodes = nodes.kerncodes
-local skipcodes = nodes.skipcodes
-
-local glyph_code = nodecodes.glyph
-local kern_code = nodecodes.kern
-local disc_code = nodecodes.disc
-local glue_code = nodecodes.glue
-local hlist_code = nodecodes.hlist
-local vlist_code = nodecodes.vlist
-local math_code = nodecodes.math
-
-local kerning_code = kerncodes.kerning
-local userkern_code = kerncodes.userkern
-local userskip_code = skipcodes.userskip
-local spaceskip_code = skipcodes.spaceskip
-local xspaceskip_code = skipcodes.xspaceskip
-
-local fonthashes = fonts.hashes
-local fontdata = fonthashes.identifiers
-local chardata = fonthashes.characters
-local quaddata = fonthashes.quads
-local markdata = fonthashes.marks
-
-local v_max = interfaces.variables.max
-
-typesetters = typesetters or { }
-local typesetters = typesetters
-
-typesetters.kerns = typesetters.kerns or { }
-local kerns = typesetters.kerns
-
-kerns.mapping = kerns.mapping or { }
-kerns.factors = kerns.factors or { }
-local a_kerns = attributes.private("kern")
-local a_fontkern = attributes.private('fontkern')
-kerns.attribute = kerns.attribute
-
-storage.register("typesetters/kerns/mapping", kerns.mapping, "typesetters.kerns.mapping")
-storage.register("typesetters/kerns/factors", kerns.factors, "typesetters.kerns.factors")
-
-local mapping = kerns.mapping
-local factors = kerns.factors
-
--- one must use liga=no and mode=base and kern=yes
--- use more helpers
--- make sure it runs after all others
--- there will be a width adaptor field in nodes so this will change
--- todo: interchar kerns / disc nodes / can be made faster
-
-local gluefactor = 4 -- assumes quad = .5 enspace
-
-kerns.keepligature = false -- just for fun (todo: control setting with key/value)
-kerns.keeptogether = false -- just for fun (todo: control setting with key/value)
-
--- can be optimized .. the prev thing .. but hardly worth the effort
-
-local function kern_injector(fillup,kern)
- if fillup then
- local g = new_glue(kern)
- local s = g.spec
- s.stretch = kern
- s.stretch_order = 1
- return g
- else
- return new_kern(kern)
- end
-end
-
-local function spec_injector(fillup,width,stretch,shrink)
- if fillup then
- local s = new_gluespec(width,2*stretch,2*shrink)
- s.stretch_order = 1
- return s
- else
- return new_gluespec(width,stretch,shrink)
- end
-end
-
--- needs checking ... base mode / node mode
-
-local function do_process(namespace,attribute,head,force) -- todo: glue so that we can fully stretch
- local start, done, lastfont = head, false, nil
- local keepligature = kerns.keepligature
- local keeptogether = kerns.keeptogether
- local fillup = false
- while start do
- -- faster to test for attr first
- local attr = force or start[attribute]
- if attr and attr > 0 then
- start[attribute] = unsetvalue
- local krn = mapping[attr]
- if krn == v_max then
- krn = .25
- fillup = true
- else
- fillup = false
- end
- if krn and krn ~= 0 then
- local id = start.id
- if id == glyph_code then
- lastfont = start.font
- local c = start.components
- if c then
- if keepligature and keepligature(start) then
- -- keep 'm
- else
- c = do_process(namespace,attribute,c,attr)
- local s = start
- local p, n = s.prev, s.next
- local tail = find_node_tail(c)
- if p then
- p.next = c
- c.prev = p
- else
- head = c
- end
- if n then
- n.prev = tail
- end
- tail.next = n
- start = c
- s.components = nil
- -- we now leak nodes !
- -- free_node(s)
- done = true
- end
- end
- local prev = start.prev
- if not prev then
- -- skip
- elseif markdata[lastfont][start.char] then
- -- skip
- else
- local pid = prev.id
- if not pid then
- -- nothing
- elseif pid == kern_code then
- if prev.subtype == kerning_code or prev[a_fontkern] then
- if keeptogether and prev.prev.id == glyph_code and keeptogether(prev.prev,start) then -- we could also pass start
- -- keep 'm
- else
- -- not yet ok, as injected kerns can be overlays (from node-inj.lua)
- prev.subtype = userkern_code
- prev.kern = prev.kern + quaddata[lastfont]*krn -- here
- done = true
- end
- end
- elseif pid == glyph_code then
- if prev.font == lastfont then
- local prevchar, lastchar = prev.char, start.char
- if keeptogether and keeptogether(prev,start) then
- -- keep 'm
- else
- local kerns = chardata[lastfont][prevchar].kerns
- local kern = kerns and kerns[lastchar] or 0
- krn = kern + quaddata[lastfont]*krn -- here
- insert_node_before(head,start,kern_injector(fillup,krn))
- done = true
- end
- else
- krn = quaddata[lastfont]*krn -- here
- insert_node_before(head,start,kern_injector(fillup,krn))
- done = true
- end
- elseif pid == disc_code then
- -- a bit too complicated, we can best not copy and just calculate
- -- but we could have multiple glyphs involved so ...
- local disc = prev -- disc
- local pre, post, replace = disc.pre, disc.post, disc.replace
- local prv, nxt = disc.prev, disc.next
- if pre and prv then -- must pair with start.prev
- -- this one happens in most cases
- local before = copy_node(prv)
- pre.prev = before
- before.next = pre
- before.prev = nil
- pre = do_process(namespace,attribute,before,attr)
- pre = pre.next
- pre.prev = nil
- disc.pre = pre
- free_node(before)
- end
- if post and nxt then -- must pair with start
- local after = copy_node(nxt)
- local tail = find_node_tail(post)
- tail.next = after
- after.prev = tail
- after.next = nil
- post = do_process(namespace,attribute,post,attr)
- tail.next = nil
- disc.post = post
- free_node(after)
- end
- if replace and prv and nxt then -- must pair with start and start.prev
- local before = copy_node(prv)
- local after = copy_node(nxt)
- local tail = find_node_tail(replace)
- replace.prev = before
- before.next = replace
- before.prev = nil
- tail.next = after
- after.prev = tail
- after.next = nil
- replace = do_process(namespace,attribute,before,attr)
- replace = replace.next
- replace.prev = nil
- after.prev.next = nil
- disc.replace = replace
- free_node(after)
- free_node(before)
- else
- if prv and prv.id == glyph_code and prv.font == lastfont then
- local prevchar, lastchar = prv.char, start.char
- local kerns = chardata[lastfont][prevchar].kerns
- local kern = kerns and kerns[lastchar] or 0
- krn = kern + quaddata[lastfont]*krn -- here
- else
- krn = quaddata[lastfont]*krn -- here
- end
- disc.replace = kern_injector(false,krn) -- only kerns permitted, no glue
- end
- end
- end
- elseif id == glue_code then
- local subtype = start.subtype
- if subtype == userskip_code or subtype == xspaceskip_code or subtype == spaceskip_code then
- local s = start.spec
- local w = s.width
- if w > 0 then
- local width, stretch, shrink = w+gluefactor*w*krn, s.stretch, s.shrink
- start.spec = spec_injector(fillup,width,stretch*width/w,shrink*width/w)
- done = true
- end
- end
- elseif id == kern_code then
- -- if start.subtype == kerning_code then -- handle with glyphs
- -- local sk = start.kern
- -- if sk > 0 then
- -- start.kern = sk*krn
- -- done = true
- -- end
- -- end
- elseif lastfont and (id == hlist_code or id == vlist_code) then -- todo: lookahead
- local p = start.prev
- if p and p.id ~= glue_code then
- insert_node_before(head,start,kern_injector(fillup,quaddata[lastfont]*krn))
- done = true
- end
- local n = start.next
- if n and n.id ~= glue_code then
- insert_node_after(head,start,kern_injector(fillup,quaddata[lastfont]*krn))
- done = true
- end
- elseif id == math_code then
- start = end_of_math(start)
- end
- end
- end
- if start then
- start = start.next
- end
- end
- return head, done
-end
-
-local enabled = false
-
-function kerns.set(factor)
- if factor ~= v_max then
- factor = tonumber(factor) or 0
- end
- if factor == v_max or factor ~= 0 then
- if not enabled then
- tasks.enableaction("processors","typesetters.kerns.handler")
- enabled = true
- end
- local a = factors[factor]
- if not a then
- a = #mapping + 1
- factors[factors], mapping[a] = a, factor
- end
- factor = a
- else
- factor = unsetvalue
- end
- texsetattribute(a_kerns,factor)
- return factor
-end
-
-local function process(namespace,attribute,head)
- return do_process(namespace,attribute,head) -- no direct map, because else fourth argument is tail == true
-end
-
-kerns.handler = nodes.installattributehandler {
- name = "kern",
- namespace = kerns,
- processor = process,
-}
-
--- interface
-
-commands.setcharacterkerning = kerns.set
diff --git a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload.lua b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload.lua
index 3708474c322..4aa050025a2 100644
--- a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload.lua
+++ b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload.lua
@@ -36,11 +36,15 @@ config.luaotfload.prioritize = config.luaotfload.prioritize or "sys"
config.luaotfload.names_dir = config.luaotfload.names_dir or "names"
config.luaotfload.cache_dir = config.luaotfload.cache_dir or "fonts"
config.luaotfload.index_file = config.luaotfload.index_file or "luaotfload-names.lua"
+config.luaotfload.formats = config.luaotfload.formats or "otf,ttf,ttc,dfont"
+if not config.luaotfload.strip then
+ config.luaotfload.strip = true
+end
luaotfload.module = {
name = "luaotfload",
- version = 2.3002,
- date = "2013/07/23",
+ version = 2.40003,
+ date = "2013/12/28",
description = "OpenType layout system.",
author = "Elie Roux & Hans Hagen",
copyright = "Elie Roux",
@@ -183,7 +187,7 @@ local our_environment = push_namespaces()
tex.attribute[0] = 0
-loadmodule"merged.lua"
+loadmodule"fontloader.lua"
---loadmodule"font-odv.lua" --- <= Devanagari support from Context
if fonts then
@@ -266,25 +270,33 @@ loadmodule"database.lua" --- “font-nms”
loadmodule"colors.lua" --- “font-clr”
+local filesuffix = file.suffix
+local fileremovesuffix = file.removesuffix
local request_resolvers = fonts.definers.resolvers
-local formats = fonts.formats -- nice table; does lowercasing ...
+local formats = fonts.formats
+local names = fonts.names
formats.ofm = "type1"
+fonts.encodings.known = fonts.encodings.known or { }
+
-local resolvefile = fonts.names.crude_file_lookup
---local resolvefile = fonts.names.crude_file_lookup_verbose
+local resolve_file = names.crude_file_lookup
+--local resolve_file = names.crude_file_lookup_verbose
+local resolve_name = names.resolve_name
-request_resolvers.file = function (specification)
- local name = resolvefile(specification.name)
- local suffix = file.suffix(name)
+local file_resolver = function (specification)
+ local name = resolve_file (specification.name)
+ local suffix = filesuffix(name)
if formats[suffix] then
- specification.forced = suffix
- specification.name = file.removesuffix(name)
+ specification.forced = suffix
+ specification.forcedname = file.removesuffix(name)
else
specification.name = name
end
end
+request_resolvers.file = file_resolver
+
--request_resolvers.anon = request_resolvers.name
@@ -296,7 +308,8 @@ request_resolvers.anon = function (specification)
for i=1, #type1_formats do
local format = type1_formats[i]
if resolvers.findfile(name, format) then
- specification.name = file.addsuffix(name, format)
+ specification.forcedname = file.addsuffix(name, format)
+ specification.forced = format
return
end
end
@@ -325,9 +338,9 @@ request_resolvers.path = function (specification)
logs.names_report("log", 1, "load",
"path lookup of %q unsuccessful, falling back to file:",
name)
- request_resolvers.file(specification)
+ file_resolver (specification)
else
- local suffix = file.suffix(name)
+ local suffix = filesuffix (name)
if formats[suffix] then
specification.forced = suffix
specification.name = file.removesuffix(name)
@@ -340,12 +353,12 @@ end
request_resolvers.kpse = function (specification)
local name = specification.name
- local suffix = file.suffix(name)
+ local suffix = filesuffix(name)
if suffix and formats[suffix] then
name = file.removesuffix(name)
if resolvers.findfile(name, suffix) then
- specification.forced = suffix
- specification.name = name
+ specification.forced = suffix
+ specification.forcedname = name
return
end
end
@@ -358,6 +371,23 @@ request_resolvers.kpse = function (specification)
end
end
+
+--- fonts.names.resolvers.name -- Customized version of the
+--- generic name resolver.
+
+request_resolvers.name = function (specification)
+ local resolved, subfont = resolve_name (specification)
+ if resolved then
+ specification.resolved = resolved
+ specification.sub = subfont
+ specification.forced = filesuffix (resolved)
+ specification.forcedname = resolved
+ specification.name = fileremovesuffix (resolved)
+ else
+ file_resolver (specification)
+ end
+end
+
create_callback("luaotfload.resolve_font", "simple", dummy_function)
request_resolvers.my = function (specification)
@@ -399,9 +429,9 @@ elseif font_definer == "patch" then
1)
end
-loadmodule"features.lua" --- contains what was “font-ltx” and “font-otc”
-loadmodule"extralibs.lua" --- load additional Context libraries
-loadmodule"auxiliary.lua" --- additionaly high-level functionality (new)
+loadmodule"features.lua" --- contains what was “font-ltx” and “font-otc”
+loadmodule"letterspace.lua" --- extra character kerning
+loadmodule"auxiliary.lua" --- additionaly high-level functionality (new)
luaotfload.aux.start_rewrite_fontname () --- to be migrated to fontspec
diff --git a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload.sty b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload.sty
index df73d383158..6a5afdba02d 100644
--- a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload.sty
+++ b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload.sty
@@ -29,7 +29,7 @@
\else
\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{luaotfload}%
- [2013/07/23 v2.3b OpenType layout system]
+ [2013/12/28 v2.4 OpenType layout system]
\RequirePackage{luatexbase}
\fi
\ifnum\luatexversion<76