summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xBuild/source/texk/texlive/linked_scripts/luaotfload/luaotfload-tool.lua287
-rwxr-xr-xBuild/source/texk/texlive/linked_scripts/texlive/tlmgr.pl8
-rw-r--r--Master/texmf-dist/doc/luatex/luaotfload/NEWS6
-rw-r--r--Master/texmf-dist/doc/luatex/luaotfload/README6
-rw-r--r--Master/texmf-dist/doc/luatex/luaotfload/filegraph.pdfbin43147 -> 43148 bytes
-rw-r--r--Master/texmf-dist/doc/luatex/luaotfload/luaotfload.pdfbin201945 -> 202351 bytes
-rw-r--r--Master/texmf-dist/doc/man/man1/luaotfload-tool.1201
-rw-r--r--Master/texmf-dist/doc/man/man1/luaotfload-tool.man1.pdfbin0 -> 9167 bytes
-rwxr-xr-xMaster/texmf-dist/scripts/luaotfload/luaotfload-legacy-tool.lua105
-rwxr-xr-xMaster/texmf-dist/scripts/luaotfload/luaotfload-tool.lua287
-rwxr-xr-xMaster/texmf-dist/scripts/luaotfload/mkglyphlist22
-rw-r--r--Master/texmf-dist/source/luatex/luaotfload/Makefile38
-rw-r--r--Master/texmf-dist/source/luatex/luaotfload/luaotfload.dtx125
-rw-r--r--Master/texmf-dist/tex/luatex/luaotfload/luaotfload-auxiliary.lua41
-rw-r--r--Master/texmf-dist/tex/luatex/luaotfload/luaotfload-database.lua323
-rw-r--r--Master/texmf-dist/tex/luatex/luaotfload/luaotfload-features.lua45
-rw-r--r--Master/texmf-dist/tex/luatex/luaotfload/luaotfload-glyphlist.lua (renamed from Master/texmf-dist/tex/luatex/luaotfload/font-age.lua)12
-rw-r--r--Master/texmf-dist/tex/luatex/luaotfload/luaotfload-legacy-attributes.lua27
-rw-r--r--Master/texmf-dist/tex/luatex/luaotfload/luaotfload-legacy-database.lua724
-rw-r--r--Master/texmf-dist/tex/luatex/luaotfload/luaotfload-legacy-merged.lua8157
-rwxr-xr-xMaster/texmf-dist/tex/luatex/luaotfload/luaotfload-legacy-tool.lua105
-rw-r--r--Master/texmf-dist/tex/luatex/luaotfload/luaotfload-legacy.lua266
-rw-r--r--Master/texmf-dist/tex/luatex/luaotfload/luaotfload-merged.lua118
-rw-r--r--Master/texmf-dist/tex/luatex/luaotfload/luaotfload-override.lua63
-rwxr-xr-xMaster/texmf-dist/tex/luatex/luaotfload/luaotfload-tool.lua287
-rw-r--r--Master/texmf-dist/tex/luatex/luaotfload/luaotfload.lua61
-rw-r--r--Master/texmf-dist/tex/luatex/luaotfload/luaotfload.sty10
-rwxr-xr-xMaster/tlpkg/libexec/ctan2tds1
-rw-r--r--Master/tlpkg/tlpsrc/luaotfload.tlpsrc1
29 files changed, 11028 insertions, 298 deletions
diff --git a/Build/source/texk/texlive/linked_scripts/luaotfload/luaotfload-tool.lua b/Build/source/texk/texlive/linked_scripts/luaotfload/luaotfload-tool.lua
index 0cd19b1d3a4..352697ced37 100755
--- a/Build/source/texk/texlive/linked_scripts/luaotfload/luaotfload-tool.lua
+++ b/Build/source/texk/texlive/linked_scripts/luaotfload/luaotfload-tool.lua
@@ -26,12 +26,29 @@ see the luaotfload documentation for more info. Report bugs to
--doc]]--
+--[[doc--
+
+ We test for Lua 5.1 by means of capability detection to see if
+ we’re running an outdated Luatex. If so, we hand over control to
+ the legacy db runner.
+
+ \url{http://lua-users.org/wiki/LuaVersionCompatibility}
+
+--doc]]--
+
kpse.set_program_name"luatex"
+if _G.getfenv then
+ local oldscript = kpse.find_file"luaotfload-legacy-tool.lua"
+ return require(oldscript)
+end
+
local stringformat = string.format
local texiowrite_nl = texio.write_nl
local stringlower = string.lower
+local C, Ct, P = lpeg.C, lpeg.Ct, lpeg.P
+local lpegmatch = lpeg.match
local loader_file = "luatexbase.loader.lua"
local loader_path = assert(kpse.find_file(loader_file, "lua"),
@@ -64,8 +81,6 @@ local config = config
config.luaotfload = config.luaotfload or { }
do -- we don’t have file.basename and the likes yet, so inline parser ftw
- local C, P = lpeg.C, lpeg.P
- local lpegmatch = lpeg.match
local slash = P"/"
local dot = P"."
local noslash = 1 - slash
@@ -90,6 +105,19 @@ config.lualibs.load_extended = false
require"lualibs"
+--- prepare directories: the cache function in Luatex-Fonts
+--- checks for writable directory only on startup, so everything
+--- has to be laid out before we load basics-gen
+
+local cachepath = kpse.expand_var"$TEXMFVAR"
+if not lfs.isdir(cachepath) then
+ dir.mkdirs(cachepath)
+ if not lfs.isdir(cachepath) then
+ texiowrite(stringformat(
+ "ERROR could not create directory %s", cachepath))
+ end
+end
+
--[[doc--
\fileent{luatex-basics-gen.lua} calls functions from the
\luafunction{texio.*} library; too much for our taste.
@@ -129,17 +157,21 @@ This tool is part of the luaotfload package. Valid options are:
-v --verbose=LEVEL be more verbose (print the searched directories)
-vv print the loaded fonts
-vvv print all steps of directory searching
+ --log=stdout redirect log output to stdout
+
-V --version print version and exit
-h --help print this message
--alias=<name> force behavior of “luaotfload-tool” or legacy
“mkluatexfontdb”
+
-------------------------------------------------------------------------------
DATABASE
-u --update update the database
-f --force force re-indexing all fonts
- -c --flush-cache empty cache of font requests
+ -l --flush-lookups empty lookup cache of font requests
+ -D --dry-run skip loading of fonts, just scan
--find="font name" query the database for a font name
-F --fuzzy look for approximate matches if --find fails
@@ -147,18 +179,26 @@ This tool is part of the luaotfload package. Valid options are:
(default: n = 1)
-i --info display font metadata
- --log=stdout redirect log output to stdout
+ --list=<criterion> output list of entries by field <criterion>
+ --list=<criterion>:<value> restrict to entries with <criterion>=<value>
+ --fields=<f1>,<f2>,…,<fn> which fields <f> to print with --list
The font database will be saved to
%s
%s
+-------------------------------------------------------------------------------
+ FONT CACHE
+
+ --cache=<directive> operate on font cache, where <directive> is
+ “show”, “purge”, or “erase”
+
]],
mkluatexfontdb = [[
Usage: %s [OPTION]...
-Rebuild the LuaTeX font database.
+Rebuild or update the LuaTeX font database.
Valid options:
-f --force force re-indexing all fonts
@@ -228,7 +268,8 @@ set.
--]]--
local action_sequence = {
- "loglevel", "help", "version", "flush", "generate", "query"
+ "loglevel", "help", "version", "cache",
+ "flush", "generate", "list", "query",
}
local action_pending = table.tohash(action_sequence, false)
@@ -241,6 +282,7 @@ actions.loglevel = function (job)
logs.set_loglevel(job.log_level)
logs.names_report("info", 3, "util",
"setting log level", "%d", job.log_level)
+ logs.names_report("log", 0, "util", "lua=%s", _VERSION)
return true, true
end
@@ -256,28 +298,47 @@ end
actions.generate = function (job)
local fontnames, savedname
- fontnames = names.update(fontnames, job.force_reload)
+ fontnames = names.update(fontnames, job.force_reload, job.dry_run)
logs.names_report("info", 2, "db",
"Fonts in the database: %i", #fontnames.mappings)
- savedname = names.save(fontnames)
- if savedname then --- FIXME have names.save return bool
+ local success = names.save(fontnames)
+ if success then
return true, true
end
return false, false
end
actions.flush = function (job)
- local success, lookups = names.flush_cache()
+ local success, lookups = names.flush_lookup_cache()
if success then
- local savedname = names.save_lookups()
- logs.names_report("info", 2, "cache", "Cache emptied")
- if savedname then
+ local success = names.save_lookups()
+ if success then
+ logs.names_report("info", 2, "cache", "Lookup cache emptied")
return true, true
end
end
return false, false
end
+local cache_directives = {
+ ["purge"] = names.purge_cache,
+ ["erase"] = names.erase_cache,
+ ["show"] = names.show_cache,
+}
+
+actions.cache = function (job)
+ local directive = cache_directives[job.cache]
+ if not directive or type(directive) ~= "function" then
+ logs.names_report("info", 2, "cache",
+ "Invalid font cache directive %s.", job.cache)
+ return false, false
+ end
+ if directive() then
+ return true, true
+ end
+ return false, false
+end
+
actions.query = function (job)
local query = job.query
@@ -286,7 +347,7 @@ actions.query = function (job)
lookup = "name",
specification = "name:" .. query,
optsize = 0,
- }
+ }
local foundname, subfont, success =
fonts.names.resolve(nil, nil, tmpspec)
@@ -317,6 +378,158 @@ actions.query = function (job)
return true, true
end
+--- --list=<criterion>
+--- --list=<criterion>:<value>
+---
+--- --list=<criterion> --fields=<f1>,<f2>,<f3>,...<fn>
+
+local get_fields get_fields = function (entry, fields, acc, n)
+ if not acc then
+ return get_fields(entry, fields, { }, 1)
+ end
+
+ local field = fields[n]
+ if field then
+ local value = entry[field]
+ acc[#acc+1] = value or false
+ return get_fields(entry, fields, acc, n+1)
+ end
+ return acc
+end
+
+local comma = P","
+local noncomma = 1-comma
+local split_comma = Ct((C(noncomma^1) + comma)^1)
+
+local texiowrite_nl = texio.write_nl
+local tableconcat = table.concat
+local stringexplode = string.explode
+
+local separator = "\t" --- could be “,” for csv
+
+local format_fields format_fields = function (fields, acc, n)
+ if not acc then
+ return format_fields(fields, { }, 1)
+ end
+
+ local field = fields[n]
+ if field ~= nil then
+ if field == false then
+ acc[#acc+1] = "<none>"
+ else
+ acc[#acc+1] = tostring(field)
+ end
+ return format_fields(fields, acc, n+1)
+ end
+ return tableconcat(acc, separator)
+end
+
+local set_primary_field
+set_primary_field = function (fields, addme, acc, n)
+ if not acc then
+ return set_primary_field(fields, addme, { addme }, 1)
+ end
+
+ local field = fields[n]
+ if field then
+ if field ~= addme then
+ acc[#acc+1] = field
+ end
+ return set_primary_field(fields, addme, acc, n+1)
+ end
+ return acc
+end
+
+actions.list = function (job)
+ local criterion = job.criterion
+
+ local asked_fields = job.asked_fields
+ if asked_fields then
+ asked_fields = lpegmatch(split_comma, asked_fields)
+ else
+ --- some defaults
+ asked_fields = { "fullname", "version", }
+ end
+
+ if not names.data then
+ names.data = names.load()
+ end
+
+ local mappings = names.data.mappings
+ local nmappings = #mappings
+
+ if criterion == "*" then
+ logs.names_report(false, 1, "list", "all %d entries", nmappings)
+ for i=1, nmappings do
+ local entry = mappings[i]
+ local fields = get_fields(entry, asked_fields)
+ --- we could collect these instead ...
+ local formatted = format_fields(fields)
+ texiowrite_nl(formatted)
+ end
+
+ else
+ criterion = stringexplode(criterion, ":") --> { field, value }
+ local asked_value = criterion[2]
+ criterion = criterion[1]
+ asked_fields = set_primary_field(asked_fields, criterion)
+
+ logs.names_report(false, 1, "list", "by %s", criterion)
+
+ --- firstly, build a list of fonts to operate on
+ local targets = { }
+ if asked_value then --- only those whose value matches
+ logs.names_report(false, 2, "list", "restricting to value %s", asked_value)
+ for i=1, nmappings do
+ local entry = mappings[i]
+ if entry[criterion]
+ and tostring(entry[criterion]) == asked_value
+ then
+ targets[#targets+1] = entry
+ end
+ end
+
+ else --- whichever have the field, sorted
+ local categories, by_category = { }, { }
+ for i=1, nmappings do
+ local entry = mappings[i]
+ local value = entry[criterion]
+ if value then
+ --value = tostring(value)
+ local entries = by_category[value]
+ if not entries then
+ entries = { entry }
+ categories[#categories+1] = value
+ else
+ entries[#entries+1] = entry
+ end
+ by_category[value] = entries
+ end
+ end
+ table.sort(categories)
+
+ for i=1, #categories do
+ local entries = by_category[categories[i]]
+ for j=1, #entries do
+ targets[#targets+1] = entries[j]
+ end
+ end
+ end
+ local ntargets = #targets
+ logs.names_report(false, 2, "list", "%d entries", ntargets)
+
+ --- now, output the collection
+ for i=1, ntargets do
+ local entry = targets[i]
+ local fields = get_fields(entry, asked_fields)
+ local formatted = format_fields(fields)
+ texiowrite_nl(formatted)
+ end
+ end
+
+ return true, true
+end
+
--[[--
Command-line processing.
mkluatexfontdb.lua relies on the script alt_getopt to process argv and
@@ -330,27 +543,32 @@ alt_getopt.
local process_cmdline = function ( ) -- unit -> jobspec
local result = { -- jobspec
force_reload = nil,
+ criterion = "",
query = "",
log_level = 1, --- 2 is approx. the old behavior
}
local long_options = {
- alias = 1,
- ["flush-cache"] = "c",
- find = 1,
- force = "f",
- fuzzy = "F",
- help = "h",
- info = "i",
- limit = 1,
- log = 1,
- quiet = "q",
- update = "u",
- verbose = 1 ,
- version = "V",
+ alias = 1,
+ cache = 1,
+ ["dry-run"] = "D",
+ ["flush-lookups"] = "l",
+ fields = 1,
+ find = 1,
+ force = "f",
+ fuzzy = "F",
+ help = "h",
+ info = "i",
+ limit = 1,
+ list = 1,
+ log = 1,
+ quiet = "q",
+ update = "u",
+ verbose = 1 ,
+ version = "V",
}
- local short_options = "cfFiquvVh"
+ local short_options = "DfFilquvVh"
local options, _, optarg =
alt_getopt.get_ordered_opts (arg, short_options, long_options)
@@ -399,14 +617,25 @@ local process_cmdline = function ( ) -- unit -> jobspec
result.show_info = true
elseif v == "alias" then
config.luaotfload.self = optarg[n]
- elseif v == "c" then
+ elseif v == "l" then
action_pending["flush"] = true
+ elseif v == "list" then
+ action_pending["list"] = true
+ result.criterion = optarg[n]
+ elseif v == "fields" then
+ result.asked_fields = optarg[n]
+ elseif v == "cache" then
+ action_pending["cache"] = true
+ result.cache = optarg[n]
+ elseif v == "D" then
+ result.dry_run = true
end
end
if config.luaotfload.self == "mkluatexfontdb" then
action_pending["generate"] = true
result.log_level = math.max(2, result.log_level)
+ logs.set_logout"stdout"
end
return result
end
diff --git a/Build/source/texk/texlive/linked_scripts/texlive/tlmgr.pl b/Build/source/texk/texlive/linked_scripts/texlive/tlmgr.pl
index a5931e662b2..578920e65c2 100755
--- a/Build/source/texk/texlive/linked_scripts/texlive/tlmgr.pl
+++ b/Build/source/texk/texlive/linked_scripts/texlive/tlmgr.pl
@@ -1,12 +1,12 @@
#!/usr/bin/env perl
-# $Id: tlmgr.pl 30343 2013-05-09 02:11:32Z preining $
+# $Id: tlmgr.pl 30404 2013-05-11 21:54:20Z karl $
#
# Copyright 2008-2013 Norbert Preining
# This file is licensed under the GNU General Public License version 2
# or any later version.
-my $svnrev = '$Revision: 30343 $';
-my $datrev = '$Date: 2013-05-09 04:11:32 +0200 (Thu, 09 May 2013) $';
+my $svnrev = '$Revision: 30404 $';
+my $datrev = '$Date: 2013-05-11 23:54:20 +0200 (Sat, 11 May 2013) $';
my $tlmgrrevision;
my $prg;
if ($svnrev =~ m/: ([0-9]+) /) {
@@ -4711,7 +4711,7 @@ sub check_runfiles {
my $Master = $localtlpdb->root;
# build a list of all runtime files associated to 'normal' packages
#
- (my $non_normal = `ls $Master/bin`) =~ s/\n/\$|/g; # binaries
+ (my $non_normal = `ls "$Master/bin"`) =~ s/\n/\$|/g; # binaries
$non_normal .= '^0+texlive|^bin-|^collection-|^scheme-|^texlive-|^texworks';
my @runtime_files = ();
#
diff --git a/Master/texmf-dist/doc/luatex/luaotfload/NEWS b/Master/texmf-dist/doc/luatex/luaotfload/NEWS
index df205dcf694..2b7d4a07b9e 100644
--- a/Master/texmf-dist/doc/luatex/luaotfload/NEWS
+++ b/Master/texmf-dist/doc/luatex/luaotfload/NEWS
@@ -1,7 +1,7 @@
Change History
--------------
-2013/05/06, luaotfload v2.2:
+2013/05/11, luaotfload v2.2:
* There is now a central, non-personal dev repo on github:
https://github.com/lualatex/luaotfload
* Synchronisation with ConTeXt from TeXLive 2013, inducing
@@ -26,6 +26,10 @@ Change History
* Hans adapted the font loader to several of our requests (attribute
allocation, custom merged package name etc.)
* Auxiliary functions for package authors
+ * Man page for luaotfload-tool
+ * Unsupported v1.3 codebase is included in the luaotfload-*lua files. Crude,
+ but effective way to support older Luatex binaries
+ * implemented basic cache control/stats option for luaotfload-tool
2013/04/27, luaotfload v1.3:
* blacklisting lingoes.ttf (segfaults)
diff --git a/Master/texmf-dist/doc/luatex/luaotfload/README b/Master/texmf-dist/doc/luatex/luaotfload/README
index 4730e881de0..aec8e2495c1 100644
--- a/Master/texmf-dist/doc/luatex/luaotfload/README
+++ b/Master/texmf-dist/doc/luatex/luaotfload/README
@@ -53,6 +53,12 @@ Here are the recommended installation methods (preferred first).
4. Try to figure it out by looking at the Makefile and comments in the sources.
+Prerequisites
+-------------------------------------------------------------------------------
+
+Typesetting the documentation requires LuaTeX and GraphViz. The Docutils are
+needed to generate the manpage.
+
License
-------------------------------------------------------------------------------
diff --git a/Master/texmf-dist/doc/luatex/luaotfload/filegraph.pdf b/Master/texmf-dist/doc/luatex/luaotfload/filegraph.pdf
index 292363e815b..dbf165b720e 100644
--- a/Master/texmf-dist/doc/luatex/luaotfload/filegraph.pdf
+++ b/Master/texmf-dist/doc/luatex/luaotfload/filegraph.pdf
Binary files differ
diff --git a/Master/texmf-dist/doc/luatex/luaotfload/luaotfload.pdf b/Master/texmf-dist/doc/luatex/luaotfload/luaotfload.pdf
index 6f14e28e25f..c000fa53ee4 100644
--- a/Master/texmf-dist/doc/luatex/luaotfload/luaotfload.pdf
+++ b/Master/texmf-dist/doc/luatex/luaotfload/luaotfload.pdf
Binary files differ
diff --git a/Master/texmf-dist/doc/man/man1/luaotfload-tool.1 b/Master/texmf-dist/doc/man/man1/luaotfload-tool.1
new file mode 100644
index 00000000000..21940932d9e
--- /dev/null
+++ b/Master/texmf-dist/doc/man/man1/luaotfload-tool.1
@@ -0,0 +1,201 @@
+.\" Man page generated from reStructuredText.
+.
+.TH LUAOTFLOAD-TOOL 1 "2013-05-06" "2.2" "text processing"
+.SH NAME
+luaotfload-tool \- generate and query the Luaotfload font names database
+.
+.nr rst2man-indent-level 0
+.
+.de1 rstReportMargin
+\\$1 \\n[an-margin]
+level \\n[rst2man-indent-level]
+level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
+-
+\\n[rst2man-indent0]
+\\n[rst2man-indent1]
+\\n[rst2man-indent2]
+..
+.de1 INDENT
+.\" .rstReportMargin pre:
+. RS \\$1
+. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
+. nr rst2man-indent-level +1
+.\" .rstReportMargin post:
+..
+.de UNINDENT
+. RE
+.\" indent \\n[an-margin]
+.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
+.nr rst2man-indent-level -1
+.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
+.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
+..
+.SH SYNOPSIS
+.sp
+\fBluaotfload\fP [ \-cfFiquvVh ]
+.sp
+\fBluaotfload\fP \-\-update [ \-\-force ] [ \-\-quiet ] [ \-\-verbose ] [ \-\-dry\-run ]
+.sp
+\fBluaotfload\fP \-\-find=FONTNAME [ \-\-fuzzy ] [ \-\-info ]
+.sp
+\fBluaotfload\fP \-\-flush\-lookups
+.sp
+\fBluaotfload\fP \-\-cache=DIRECTIVE
+.sp
+\fBluaotfload\fP \-\-list=CRITERION[:VALUE] [ \-\-fields=F1,F2,...,Fn ]
+.sp
+\fBluaotfload\fP \-\-help
+.sp
+\fBluaotfload\fP \-\-version
+.SH DESCRIPTION
+.sp
+luaotfload\-tool accesses the font names database that is required by
+the \fILuaotfload\fP package. There are two general modes: \fBupdate\fP and
+\fBquery\fP.
+.INDENT 0.0
+.IP \(bu 2
+\fBupdate\fP: update the database or rebuild it entirely;
+.IP \(bu 2
+\fBquery\fP: resolve a font name or display close matches.
+.UNINDENT
+.sp
+A third mode for clearing the lookup cache is currently experimental.
+.sp
+Note that if the script is named \fBmkluatexfontdb\fP it will behave like
+earlier versions (<=1.3) and always update the database first. Also,
+the verbosity level will be set to 2.
+.SH OPTIONS
+.SS update mode
+.INDENT 0.0
+.TP
+.B \-\-update, \-u
+Update the database; indexes new fonts.
+.TP
+.B \-\-force, \-f
+Force rebuilding of the database; re\-indexes
+all fonts.
+.TP
+.B \-\-dry\-run, \-D
+Don’t load fonts, scan directories only.
+(For debugging file system related issues.)
+.UNINDENT
+.SS query mode
+.INDENT 0.0
+.TP
+.BI \-\-find\fB= NAME
+Resolve a font name; this looks up <name> in
+the database and prints the file name it is
+mapped to.
+.TP
+.B \-\-fuzzy, \-F
+Show approximate matches to the file name if
+the lookup was unsuccessful (requires
+\fB\-\-find\fP).
+.TP
+.B \-\-info, \-i
+Display basic information to a resolved font
+file (requires \fB\-\-find\fP).
+.TP
+.BI \-\-list\fB= CRITERION
+Show entries, where \fICRITERION\fP is one of the
+following:
+.INDENT 7.0
+.IP 1. 3
+the character \fB*\fP, selecting all entries;
+.IP 2. 3
+a field of a database entry, for instance
+\fIfullname\fP or \fIunits_per_em\fP, according to
+which the output will be sorted; or
+.IP 3. 3
+an expression of the form \fBfield:value\fP to
+limit the output to entries whose \fBfield\fP
+matches \fBvalue\fP.
+.UNINDENT
+.TP
+.BI \-\-fields\fB= FIELDS
+Comma\-separated list of fields that should be
+printed. The default is \fIfullname,version\fP.
+(Only meaningful with \fB\-\-list\fP.)
+.UNINDENT
+.SS font and lookup caches
+.INDENT 0.0
+.TP
+.B \-\-flush\-lookups
+Clear font name lookup cache (experimental).
+.TP
+.BI \-\-cache\fB= DIRECTIVE
+Cache control, where \fIDIRECTIVE\fP is one of the
+following:
+.INDENT 7.0
+.IP 1. 3
+\fBpurge\fP \-> delete Lua files from cache;
+.IP 2. 3
+\fBerase\fP \-> delete Lua and Luc files from
+cache;
+.IP 3. 3
+\fBshow\fP \-> print stats.
+.UNINDENT
+.UNINDENT
+.SS miscellaneous
+.INDENT 0.0
+.TP
+.BI \-\-verbose\fB= N, \ \-v
+Set verbosity level to \fIn\fP or the number of
+repetitions of \fB\-v\fP.
+.TP
+.B \-\-quiet
+No verbose output (log level set to zero).
+.TP
+.BI \-\-log\fB= stdout
+Redirect log output to terminal (for database
+troubleshooting).
+.TP
+.B \-\-version, \-V
+Show version number and exit.
+.TP
+.B \-\-help, \-h
+Show help message and exit.
+.UNINDENT
+.SH FILES
+.sp
+The font name database is usually located in the directory
+\fBtexmf\-var/luatex\-cache/generic/names/\fP (\fB$TEXMFCACHE\fP as set in
+\fBtexmf.cnf\fP) of your \fITeX Live\fP distribution as
+\fBluaotfload\-names.lua\fP. The experimental lookup cache will be
+created as \fBluaotfload\-lookup\-cache.lua\fP in the same directory.
+Both files are safe to delete, at the cost of regenerating them with
+the next run of \fILuaTeX\fP.
+.SH SEE ALSO
+.sp
+\fBluatex\fP (1), \fBlua\fP (1)
+.INDENT 0.0
+.IP \(bu 2
+\fBtexdoc luaotfload\fP to display the manual for the \fILuaotfload\fP
+package
+.IP \(bu 2
+Luaotfload development \fI\%https://github.com/lualatex/luaotfload\fP
+.IP \(bu 2
+LuaLaTeX mailing list \fI\%http://tug.org/pipermail/lualatex\-dev/\fP
+.IP \(bu 2
+LuaTeX \fI\%http://luatex.org/\fP
+.IP \(bu 2
+ConTeXt \fI\%http://wiki.contextgarden.net\fP
+.IP \(bu 2
+Luaotfload on CTAN \fI\%http://ctan.org/pkg/luaotfload\fP
+.UNINDENT
+.SH BUGS
+.sp
+Tons, probably.
+.SH AUTHORS
+.sp
+\fILuaotfload\fP is maintained by the LuaLaTeX dev team
+(\fI\%https://github.com/lualatex/\fP).
+The fontloader code is provided by Hans Hagen of Pragma ADE, Hasselt
+NL (\fI\%http://pragma\-ade.com/\fP).
+.sp
+This manual page was written by Philipp Gesang
+<\fI\%philipp.gesang@alumni.uni\-heidelberg.de\fP>.
+.SH COPYRIGHT
+GPL v2.0
+.\" Generated by docutils manpage writer.
+.
diff --git a/Master/texmf-dist/doc/man/man1/luaotfload-tool.man1.pdf b/Master/texmf-dist/doc/man/man1/luaotfload-tool.man1.pdf
new file mode 100644
index 00000000000..9d3ac9c716b
--- /dev/null
+++ b/Master/texmf-dist/doc/man/man1/luaotfload-tool.man1.pdf
Binary files differ
diff --git a/Master/texmf-dist/scripts/luaotfload/luaotfload-legacy-tool.lua b/Master/texmf-dist/scripts/luaotfload/luaotfload-legacy-tool.lua
new file mode 100755
index 00000000000..30ab13b6e3a
--- /dev/null
+++ b/Master/texmf-dist/scripts/luaotfload/luaotfload-legacy-tool.lua
@@ -0,0 +1,105 @@
+#!/usr/bin/env texlua
+--[[
+This file is copyright 2010 Elie Roux and Khaled Hosny and is under CC0
+license (see http://creativecommons.org/publicdomain/zero/1.0/legalcode).
+
+This file is a wrapper for the luaotfload's font names module. It is part of the
+luaotfload bundle, please see the luaotfload documentation for more info.
+--]]
+
+kpse.set_program_name("luatex")
+
+require("lualibs") ---> current
+require("luaotfload-legacy-database") ---> old
+require("alt_getopt") ---> ?
+
+local name = "mkluatexfontdb"
+local version = "1.31 (legacy)"
+
+local names = fonts.names
+
+local function help_msg()
+ texio.write(string.format([[
+Usage: %s [OPTION]...
+
+================================================================================
+ please update your luatex binary
+ this version is unsupported and likely to break things
+================================================================================
+
+Rebuild the LuaTeX font database.
+
+Valid options:
+ -f --force force re-indexing all fonts
+ -q --quiet don't output anything
+ -v --verbose=LEVEL be more verbose (print the searched directories)
+ -vv print the loaded fonts
+ -vvv print all steps of directory searching
+ -V --version print version and exit
+ -h --help print this message
+
+The output database file is named otfl-names.lua and is placed under:
+
+ %s
+
+contact: https://github.com/lualatex/luaotfload
+
+]], name, names.path.localdir))
+end
+
+local function version_msg()
+ texio.write(string.format(
+ "%s version %s, database version %s.\n", name, version, names.version))
+end
+
+--[[
+Command-line processing.
+Here we fill cmdargs with the good values, and then analyze it.
+--]]
+
+local long_opts = {
+ force = "f",
+ quiet = "q",
+ help = "h",
+ verbose = 1 ,
+ version = "V",
+}
+
+local short_opts = "fqpvVh"
+
+local force_reload = nil
+
+local function process_cmdline()
+ local opts, optind, optarg = alt_getopt.get_ordered_opts (arg, short_opts, long_opts)
+ local log_level = 1
+ for i,v in ipairs(opts) do
+ if v == "q" then
+ log_level = 0
+ elseif v == "v" then
+ if log_level > 0 then
+ log_level = log_level + 1
+ else
+ log_level = 2
+ end
+ elseif v == "V" then
+ version_msg()
+ os.exit(0)
+ elseif v == "h" then
+ help_msg()
+ os.exit(0)
+ elseif v == "f" then
+ force_reload = 1
+ end
+ end
+ names.set_log_level(log_level)
+end
+
+local function generate(force)
+ local fontnames, saved
+ fontnames = names.update(fontnames, force)
+ logs.report("%s fonts in the database", #fontnames.mappings)
+ saved = names.save(fontnames)
+end
+
+process_cmdline()
+generate(force_reload)
diff --git a/Master/texmf-dist/scripts/luaotfload/luaotfload-tool.lua b/Master/texmf-dist/scripts/luaotfload/luaotfload-tool.lua
index 0cd19b1d3a4..352697ced37 100755
--- a/Master/texmf-dist/scripts/luaotfload/luaotfload-tool.lua
+++ b/Master/texmf-dist/scripts/luaotfload/luaotfload-tool.lua
@@ -26,12 +26,29 @@ see the luaotfload documentation for more info. Report bugs to
--doc]]--
+--[[doc--
+
+ We test for Lua 5.1 by means of capability detection to see if
+ we’re running an outdated Luatex. If so, we hand over control to
+ the legacy db runner.
+
+ \url{http://lua-users.org/wiki/LuaVersionCompatibility}
+
+--doc]]--
+
kpse.set_program_name"luatex"
+if _G.getfenv then
+ local oldscript = kpse.find_file"luaotfload-legacy-tool.lua"
+ return require(oldscript)
+end
+
local stringformat = string.format
local texiowrite_nl = texio.write_nl
local stringlower = string.lower
+local C, Ct, P = lpeg.C, lpeg.Ct, lpeg.P
+local lpegmatch = lpeg.match
local loader_file = "luatexbase.loader.lua"
local loader_path = assert(kpse.find_file(loader_file, "lua"),
@@ -64,8 +81,6 @@ local config = config
config.luaotfload = config.luaotfload or { }
do -- we don’t have file.basename and the likes yet, so inline parser ftw
- local C, P = lpeg.C, lpeg.P
- local lpegmatch = lpeg.match
local slash = P"/"
local dot = P"."
local noslash = 1 - slash
@@ -90,6 +105,19 @@ config.lualibs.load_extended = false
require"lualibs"
+--- prepare directories: the cache function in Luatex-Fonts
+--- checks for writable directory only on startup, so everything
+--- has to be laid out before we load basics-gen
+
+local cachepath = kpse.expand_var"$TEXMFVAR"
+if not lfs.isdir(cachepath) then
+ dir.mkdirs(cachepath)
+ if not lfs.isdir(cachepath) then
+ texiowrite(stringformat(
+ "ERROR could not create directory %s", cachepath))
+ end
+end
+
--[[doc--
\fileent{luatex-basics-gen.lua} calls functions from the
\luafunction{texio.*} library; too much for our taste.
@@ -129,17 +157,21 @@ This tool is part of the luaotfload package. Valid options are:
-v --verbose=LEVEL be more verbose (print the searched directories)
-vv print the loaded fonts
-vvv print all steps of directory searching
+ --log=stdout redirect log output to stdout
+
-V --version print version and exit
-h --help print this message
--alias=<name> force behavior of “luaotfload-tool” or legacy
“mkluatexfontdb”
+
-------------------------------------------------------------------------------
DATABASE
-u --update update the database
-f --force force re-indexing all fonts
- -c --flush-cache empty cache of font requests
+ -l --flush-lookups empty lookup cache of font requests
+ -D --dry-run skip loading of fonts, just scan
--find="font name" query the database for a font name
-F --fuzzy look for approximate matches if --find fails
@@ -147,18 +179,26 @@ This tool is part of the luaotfload package. Valid options are:
(default: n = 1)
-i --info display font metadata
- --log=stdout redirect log output to stdout
+ --list=<criterion> output list of entries by field <criterion>
+ --list=<criterion>:<value> restrict to entries with <criterion>=<value>
+ --fields=<f1>,<f2>,…,<fn> which fields <f> to print with --list
The font database will be saved to
%s
%s
+-------------------------------------------------------------------------------
+ FONT CACHE
+
+ --cache=<directive> operate on font cache, where <directive> is
+ “show”, “purge”, or “erase”
+
]],
mkluatexfontdb = [[
Usage: %s [OPTION]...
-Rebuild the LuaTeX font database.
+Rebuild or update the LuaTeX font database.
Valid options:
-f --force force re-indexing all fonts
@@ -228,7 +268,8 @@ set.
--]]--
local action_sequence = {
- "loglevel", "help", "version", "flush", "generate", "query"
+ "loglevel", "help", "version", "cache",
+ "flush", "generate", "list", "query",
}
local action_pending = table.tohash(action_sequence, false)
@@ -241,6 +282,7 @@ actions.loglevel = function (job)
logs.set_loglevel(job.log_level)
logs.names_report("info", 3, "util",
"setting log level", "%d", job.log_level)
+ logs.names_report("log", 0, "util", "lua=%s", _VERSION)
return true, true
end
@@ -256,28 +298,47 @@ end
actions.generate = function (job)
local fontnames, savedname
- fontnames = names.update(fontnames, job.force_reload)
+ fontnames = names.update(fontnames, job.force_reload, job.dry_run)
logs.names_report("info", 2, "db",
"Fonts in the database: %i", #fontnames.mappings)
- savedname = names.save(fontnames)
- if savedname then --- FIXME have names.save return bool
+ local success = names.save(fontnames)
+ if success then
return true, true
end
return false, false
end
actions.flush = function (job)
- local success, lookups = names.flush_cache()
+ local success, lookups = names.flush_lookup_cache()
if success then
- local savedname = names.save_lookups()
- logs.names_report("info", 2, "cache", "Cache emptied")
- if savedname then
+ local success = names.save_lookups()
+ if success then
+ logs.names_report("info", 2, "cache", "Lookup cache emptied")
return true, true
end
end
return false, false
end
+local cache_directives = {
+ ["purge"] = names.purge_cache,
+ ["erase"] = names.erase_cache,
+ ["show"] = names.show_cache,
+}
+
+actions.cache = function (job)
+ local directive = cache_directives[job.cache]
+ if not directive or type(directive) ~= "function" then
+ logs.names_report("info", 2, "cache",
+ "Invalid font cache directive %s.", job.cache)
+ return false, false
+ end
+ if directive() then
+ return true, true
+ end
+ return false, false
+end
+
actions.query = function (job)
local query = job.query
@@ -286,7 +347,7 @@ actions.query = function (job)
lookup = "name",
specification = "name:" .. query,
optsize = 0,
- }
+ }
local foundname, subfont, success =
fonts.names.resolve(nil, nil, tmpspec)
@@ -317,6 +378,158 @@ actions.query = function (job)
return true, true
end
+--- --list=<criterion>
+--- --list=<criterion>:<value>
+---
+--- --list=<criterion> --fields=<f1>,<f2>,<f3>,...<fn>
+
+local get_fields get_fields = function (entry, fields, acc, n)
+ if not acc then
+ return get_fields(entry, fields, { }, 1)
+ end
+
+ local field = fields[n]
+ if field then
+ local value = entry[field]
+ acc[#acc+1] = value or false
+ return get_fields(entry, fields, acc, n+1)
+ end
+ return acc
+end
+
+local comma = P","
+local noncomma = 1-comma
+local split_comma = Ct((C(noncomma^1) + comma)^1)
+
+local texiowrite_nl = texio.write_nl
+local tableconcat = table.concat
+local stringexplode = string.explode
+
+local separator = "\t" --- could be “,” for csv
+
+local format_fields format_fields = function (fields, acc, n)
+ if not acc then
+ return format_fields(fields, { }, 1)
+ end
+
+ local field = fields[n]
+ if field ~= nil then
+ if field == false then
+ acc[#acc+1] = "<none>"
+ else
+ acc[#acc+1] = tostring(field)
+ end
+ return format_fields(fields, acc, n+1)
+ end
+ return tableconcat(acc, separator)
+end
+
+local set_primary_field
+set_primary_field = function (fields, addme, acc, n)
+ if not acc then
+ return set_primary_field(fields, addme, { addme }, 1)
+ end
+
+ local field = fields[n]
+ if field then
+ if field ~= addme then
+ acc[#acc+1] = field
+ end
+ return set_primary_field(fields, addme, acc, n+1)
+ end
+ return acc
+end
+
+actions.list = function (job)
+ local criterion = job.criterion
+
+ local asked_fields = job.asked_fields
+ if asked_fields then
+ asked_fields = lpegmatch(split_comma, asked_fields)
+ else
+ --- some defaults
+ asked_fields = { "fullname", "version", }
+ end
+
+ if not names.data then
+ names.data = names.load()
+ end
+
+ local mappings = names.data.mappings
+ local nmappings = #mappings
+
+ if criterion == "*" then
+ logs.names_report(false, 1, "list", "all %d entries", nmappings)
+ for i=1, nmappings do
+ local entry = mappings[i]
+ local fields = get_fields(entry, asked_fields)
+ --- we could collect these instead ...
+ local formatted = format_fields(fields)
+ texiowrite_nl(formatted)
+ end
+
+ else
+ criterion = stringexplode(criterion, ":") --> { field, value }
+ local asked_value = criterion[2]
+ criterion = criterion[1]
+ asked_fields = set_primary_field(asked_fields, criterion)
+
+ logs.names_report(false, 1, "list", "by %s", criterion)
+
+ --- firstly, build a list of fonts to operate on
+ local targets = { }
+ if asked_value then --- only those whose value matches
+ logs.names_report(false, 2, "list", "restricting to value %s", asked_value)
+ for i=1, nmappings do
+ local entry = mappings[i]
+ if entry[criterion]
+ and tostring(entry[criterion]) == asked_value
+ then
+ targets[#targets+1] = entry
+ end
+ end
+
+ else --- whichever have the field, sorted
+ local categories, by_category = { }, { }
+ for i=1, nmappings do
+ local entry = mappings[i]
+ local value = entry[criterion]
+ if value then
+ --value = tostring(value)
+ local entries = by_category[value]
+ if not entries then
+ entries = { entry }
+ categories[#categories+1] = value
+ else
+ entries[#entries+1] = entry
+ end
+ by_category[value] = entries
+ end
+ end
+ table.sort(categories)
+
+ for i=1, #categories do
+ local entries = by_category[categories[i]]
+ for j=1, #entries do
+ targets[#targets+1] = entries[j]
+ end
+ end
+ end
+ local ntargets = #targets
+ logs.names_report(false, 2, "list", "%d entries", ntargets)
+
+ --- now, output the collection
+ for i=1, ntargets do
+ local entry = targets[i]
+ local fields = get_fields(entry, asked_fields)
+ local formatted = format_fields(fields)
+ texiowrite_nl(formatted)
+ end
+ end
+
+ return true, true
+end
+
--[[--
Command-line processing.
mkluatexfontdb.lua relies on the script alt_getopt to process argv and
@@ -330,27 +543,32 @@ alt_getopt.
local process_cmdline = function ( ) -- unit -> jobspec
local result = { -- jobspec
force_reload = nil,
+ criterion = "",
query = "",
log_level = 1, --- 2 is approx. the old behavior
}
local long_options = {
- alias = 1,
- ["flush-cache"] = "c",
- find = 1,
- force = "f",
- fuzzy = "F",
- help = "h",
- info = "i",
- limit = 1,
- log = 1,
- quiet = "q",
- update = "u",
- verbose = 1 ,
- version = "V",
+ alias = 1,
+ cache = 1,
+ ["dry-run"] = "D",
+ ["flush-lookups"] = "l",
+ fields = 1,
+ find = 1,
+ force = "f",
+ fuzzy = "F",
+ help = "h",
+ info = "i",
+ limit = 1,
+ list = 1,
+ log = 1,
+ quiet = "q",
+ update = "u",
+ verbose = 1 ,
+ version = "V",
}
- local short_options = "cfFiquvVh"
+ local short_options = "DfFilquvVh"
local options, _, optarg =
alt_getopt.get_ordered_opts (arg, short_options, long_options)
@@ -399,14 +617,25 @@ local process_cmdline = function ( ) -- unit -> jobspec
result.show_info = true
elseif v == "alias" then
config.luaotfload.self = optarg[n]
- elseif v == "c" then
+ elseif v == "l" then
action_pending["flush"] = true
+ elseif v == "list" then
+ action_pending["list"] = true
+ result.criterion = optarg[n]
+ elseif v == "fields" then
+ result.asked_fields = optarg[n]
+ elseif v == "cache" then
+ action_pending["cache"] = true
+ result.cache = optarg[n]
+ elseif v == "D" then
+ result.dry_run = true
end
end
if config.luaotfload.self == "mkluatexfontdb" then
action_pending["generate"] = true
result.log_level = math.max(2, result.log_level)
+ logs.set_logout"stdout"
end
return result
end
diff --git a/Master/texmf-dist/scripts/luaotfload/mkglyphlist b/Master/texmf-dist/scripts/luaotfload/mkglyphlist
index 281c7368a73..94aac39d7a1 100755
--- a/Master/texmf-dist/scripts/luaotfload/mkglyphlist
+++ b/Master/texmf-dist/scripts/luaotfload/mkglyphlist
@@ -17,10 +17,25 @@
-- config
-----------------------------------------------------------------------
local glyphfile = "./glyphlist.txt"
-local font_age = "./font-age.lua"
+local font_age = "./luaotfload-glyphlist.lua"
local glyph_source = "http://partners.adobe.com/public/developer/en/opentype/glyphlist.txt"
-----------------------------------------------------------------------
+-- fallbacks
+-----------------------------------------------------------------------
+--- Hans adds a small list of mappings that are not in the original
+--- glyph list but seem to be normalizations of some sort. I trust his
+--- experience, so I’ll just include them here. Background:
+--- http://www.ntg.nl/pipermail/ntg-context/2013/073089.html
+
+local fallbacks = {
+ ["SF10000"]=9484, ["SF20000"]=9492, ["SF30000"]=9488,
+ ["SF40000"]=9496, ["SF50000"]=9532, ["SF60000"]=9516,
+ ["SF70000"]=9524, ["SF80000"]=9500, ["SF90000"]=9508,
+ ["afii208"]=8213,
+}
+
+-----------------------------------------------------------------------
-- includes
-----------------------------------------------------------------------
require"lpeg"
@@ -73,6 +88,9 @@ local get_glyphs = function (data)
print("error: could not parse glyph list")
os.exit(-1)
end
+ for name, glyph in next, fallbacks do
+ res[name] = res[name] or glyph
+ end
return res
end
@@ -93,7 +111,7 @@ end
--[[doc--
Everything below has been autogenerated. Run mkglyphlist to rebuild
-font-age.lua.
+luaotfload-glyphlist.lua.
--doc]]--
]==]
diff --git a/Master/texmf-dist/source/luatex/luaotfload/Makefile b/Master/texmf-dist/source/luatex/luaotfload/Makefile
index 98b54911ba0..c1e98f652fe 100644
--- a/Master/texmf-dist/source/luatex/luaotfload/Makefile
+++ b/Master/texmf-dist/source/luatex/luaotfload/Makefile
@@ -3,23 +3,29 @@
NAME = luaotfload
DOC = $(NAME).pdf
DTX = $(NAME).dtx
-OTFL = $(wildcard luaotfload-*.lua) luaotfload-blacklist.cnf $(GLYPHS)
-SCRIPT = luaotfload-tool.lua
+OTFL = $(wildcard luaotfload-*.lua) luaotfload-blacklist.cnf
GLYPHSCRIPT = mkglyphlist
GLYPHSOURCE = glyphlist.txt
-GRAPH = filegraph
-DOTPDF = $(GRAPH).pdf
-DOT = $(GRAPH).dot
+SCRIPTNAME = luaotfload-tool
+SCRIPT = $(SCRIPTNAME).lua
+MANSOURCE = $(SCRIPTNAME).rst
+MANPAGE = $(SCRIPTNAME).1
+OLDSCRIPT = luaotfload-legacy-tool.lua
+
+GRAPH = filegraph
+DOTPDF = $(GRAPH).pdf
+DOT = $(GRAPH).dot
# Files grouped by generation mode
-GLYPHS = font-age.lua
+GLYPHS = luaotfload-glyphlist.lua
GRAPHED = $(DOTPDF)
+MAN = $(MANPAGE)
COMPILED = $(DOC)
UNPACKED = luaotfload.sty luaotfload.lua
-GENERATED = $(GRAPHED) $(UNPACKED) $(COMPILED) $(GLYPHS)
-SOURCE = $(DTX) $(OTFL) README Makefile NEWS $(SCRIPT) $(GLYPHSCRIPT)
+GENERATED = $(GRAPHED) $(UNPACKED) $(COMPILED) $(GLYPHS) $(MAN)
+SOURCE = $(DTX) $(MANSOURCE) $(OTFL) README Makefile NEWS $(SCRIPT) $(OLDSCRIPT) $(GLYPHSCRIPT)
# test files
TESTDIR = tests
@@ -28,9 +34,10 @@ TESTFILES_SYS = $(TESTDIR)/systemfonts.tex $(TESTDIR)/fontconfig_conf_reading.t
TESTFILES_TL = $(filter-out $(TESTFILES_SYS), $(TESTFILES))
# Files grouped by installation location
-SCRIPTFILES = $(SCRIPT) $(GLYPHSCRIPT)
+SCRIPTFILES = $(SCRIPT) $(OLDSCRIPT) $(GLYPHSCRIPT)
RUNFILES = $(UNPACKED) $(OTFL)
DOCFILES = $(DOC) $(DOTPDF) README NEWS
+MANFILES = $(MANPAGE)
SRCFILES = $(DTX) Makefile
# The following definitions should be equivalent
@@ -42,6 +49,7 @@ FORMAT = luatex
SCRIPTDIR = $(TEXMFROOT)/scripts/$(NAME)
RUNDIR = $(TEXMFROOT)/tex/$(FORMAT)/$(NAME)
DOCDIR = $(TEXMFROOT)/doc/$(FORMAT)/$(NAME)
+MANDIR = $(TEXMFROOT)/doc/man/man1/
SRCDIR = $(TEXMFROOT)/source/$(FORMAT)/$(NAME)
TEXMFROOT = $(shell kpsewhich --var-value TEXMFHOME)
@@ -49,18 +57,20 @@ CTAN_ZIP = $(NAME).zip
TDS_ZIP = $(NAME).tds.zip
ZIPS = $(CTAN_ZIP) $(TDS_ZIP)
-DO_TEX = tex --interaction=batchmode $< >/dev/null
+DO_TEX = luatex --interaction=batchmode $< >/dev/null
# (with the next version of latexmk: -pdf -pdflatex=lualatex)
DO_LATEX = latexmk -pdf -e '$$pdflatex = q(lualatex %O %S)' -silent $< >/dev/null
DO_GRAPHVIZ = dot -Tpdf -o $@ $< > /dev/null
DO_GLYPHLIST = texlua ./mkglyphlist > /dev/null
+DO_DOCUTILS = rst2man2 $< >$@ 2>/dev/null
all: $(GENERATED)
graph: $(GRAPHED)
-doc: $(GRAPHED) $(COMPILED)
+doc: $(GRAPHED) $(COMPILED) $(MAN)
+manual: $(MAN)
unpack: $(UNPACKED)
glyphs: $(GLYPHS)
-ctan: check $(CTAN_ZIP)
+ctan: $(CTAN_ZIP)
tds: $(TDS_ZIP)
world: all ctan
@@ -76,6 +86,9 @@ $(COMPILED): $(DTX)
$(UNPACKED): $(DTX)
$(DO_TEX)
+$(MAN): $(MANSOURCE)
+ $(DO_DOCUTILS)
+
$(CTAN_ZIP): $(SOURCE) $(COMPILED) $(TDS_ZIP)
@echo "Making $@ for CTAN upload."
@$(RM) -- $@
@@ -86,6 +99,7 @@ define run-install
@mkdir -p $(RUNDIR) && cp $(RUNFILES) $(RUNDIR)
@mkdir -p $(DOCDIR) && cp $(DOCFILES) $(DOCDIR)
@mkdir -p $(SRCDIR) && cp $(SRCFILES) $(SRCDIR)
+@mkdir -p $(MANDIR) && cp $(MANFILES) $(MANDIR)
endef
$(TDS_ZIP): TEXMFROOT=./tmp-texmf
diff --git a/Master/texmf-dist/source/luatex/luaotfload/luaotfload.dtx b/Master/texmf-dist/source/luatex/luaotfload/luaotfload.dtx
index 2f88225781a..7ccd6e88c65 100644
--- a/Master/texmf-dist/source/luatex/luaotfload/luaotfload.dtx
+++ b/Master/texmf-dist/source/luatex/luaotfload/luaotfload.dtx
@@ -133,7 +133,7 @@ and the derived files
\usepackage{fontspec}
\usepackage{unicode-math}
\setmainfont[
- Numbers = OldStyle,
+% Numbers = OldStyle, %% buggy with font cache
Ligatures = TeX,
BoldFont = {Linux Libertine O Bold},
ItalicFont = {Linux Libertine O Italic},
@@ -146,14 +146,15 @@ and the derived files
\usepackage{hologo}
-\newcommand\TEX {\TeX\xspace}
-\newcommand\LUA {Lua\xspace}
-\newcommand\PDFTEX {pdf\TeX\xspace}
-\newcommand\LUATEX {Lua\TeX\xspace}
-\newcommand\XETEX {\XeTeX\xspace}
-\newcommand\LATEX {\LaTeX\xspace}
-\newcommand\CONTEXT {Con\TeX t\xspace}
-\newcommand\OpenType{\identifier{Open\kern-.25ex Type}\xspace}
+\newcommand\TEX {\TeX\xspace}
+\newcommand\LUA {Lua\xspace}
+\newcommand\PDFTEX {pdf\TeX\xspace}
+\newcommand\LUATEX {Lua\TeX\xspace}
+\newcommand\XETEX {\XeTeX\xspace}
+\newcommand\LATEX {\LaTeX\xspace}
+\newcommand\LUALATEX {Lua\LaTeX\xspace}
+\newcommand\CONTEXT {Con\TeX t\xspace}
+\newcommand\OpenType {\identifier{Open\kern-.25ex Type}\xspace}
\def\definehighlight[#1][#2]%
{\ifcsname #1\endcsname\else
@@ -258,7 +259,7 @@ and the derived files
% functionality like ligatures, old-style numbers, small capitals,
% etc., and support more complex writing systems like Arabic and
% Indic\footnote{%
-% Unfortunately, \identifier{luaotfload} doesn't support Indic
+% Unfortunately, \identifier{luaotfload} doesn‘t support many Indic
% scripts right now.
% Assistance in implementing the prerequisites is greatly
% appreciated.
@@ -282,6 +283,27 @@ and the derived files
% Additionally, it provides means for accessing fonts known to the operating
% system conveniently by indexing the metadata.
%
+%
+% \section{Thanks}
+%
+% \identifier{Luaotfload} is part of \LUALATEX, the community-driven
+% project to provide a foundation for using the \LATEX format with the
+% full capabilites of the \LUATEX engine.
+% As such, the distinction between end users, contributors, and project
+% maintainers is intentionally kept less strict, lest we unduly
+% personalize the common effort.
+%
+% Nevertheless, the current maintainers would like to express their
+% gratitude to Khaled Hosny, Akira Kakuto, Hironori Kitagawa and Dohyun
+% Kim.
+% Their contributions -- be it patches, advice, or systematic
+% testing -- made the switch from version 1.x to 2.2 possible.
+% Also, Hans Hagen, the author of the font loader, made porting the
+% code to \LATEX a breeze due to the extra effort he invested into
+% isolating it from the rest of \CONTEXT, not to mention his assistance
+% in the task and willingness to respond to our suggestions.
+%
+%
% \section{Loading Fonts}
%
% \identifier{luaotfload} supports an extended font request syntax:
@@ -407,7 +429,7 @@ and the derived files
% \fileent{GFSBodoni.otf}, and
% \fileent{PTZ56F.ttf}.
%
-% \subsection{\hologo{XeTeX} Compatibility Layer}
+% \subsection{Compatibility Layer}
%
% In addition to the regular prefixed requests, \identifier{luaotfload}
% accepts loading fonts the \XETEX way.
@@ -947,6 +969,11 @@ and the derived files
%
% \verb|luaotfload-tool --help| will list the available command line
% switches, including some not discussed in detail here.
+% For a full documentation of \identifier{luaotfload-tool} and its
+% capabilities refer to the manpage
+% (\verb|man 1 luaotfload-tool|).\footnote{%
+% Or see \verb|luaotfload-tool.rst| in the source directory.
+% }
%
% \subsection{Blacklisting Fonts}
% \label{font-blacklist}
@@ -1105,14 +1132,14 @@ and the derived files
%
% Also, the merged file at some point
% loads the Adobe Glyph List from a \LUA table that is contained in
-% \fileent{font-age.lua}, which is automatically generated by the
+% \fileent{luaotfload-glyphlist.lua}, which is automatically generated by the %
% script \fileent{mkglyphlist}.\footnote{%
% See \fileent{luaotfload-font-enc.lua}.
-% The hard-coded file name is why the file lacks the \fileent{luaotfload-}
-% prefix.
+% The hard-coded file name is why we have to replace the procedure
+% that loads the file in \fileent{luaotfload-override.lua}.
% }
% There is a make target \identifier{glyphs} that will create a fresh
-% \fileent{font-age.lua} so we don’t need to import it from \CONTEXT
+% glyph list so we don’t need to import it from \CONTEXT
% any longer.
%
% In addition to these, \identifier{luaotfload} requires a number of
@@ -1219,11 +1246,12 @@ local luaotfload = luaotfload
config = config or { }
config.luaotfload = config.luaotfload or { }
-------.luaotfload.resolver = config.luaotfload.resolver or "normal"
-config.luaotfload.resolver = config.luaotfload.resolver or "cached"
-config.luaotfload.definer = config.luaotfload.definer or "patch"
+------.luaotfload.resolver = config.luaotfload.resolver or "normal"
+config.luaotfload.resolver = config.luaotfload.resolver or "cached"
+config.luaotfload.definer = config.luaotfload.definer or "patch"
+config.luaotfload.compatibility = config.luaotfload.compatibility or false
config.luaotfload.loglevel = config.luaotfload.loglevel or 1
-config.luaotfload.color_callback = config.luaotfload.color_callback or "pre_linebreak_filter"
+config.luaotfload.color_callback = config.luaotfload.color_callback or "pre_linebreak_filter"
--luaotfload.prefer_merge = config.luaotfload.prefer_merge or true
luaotfload.module = {
@@ -1254,17 +1282,6 @@ local reset_callback, call_callback =
local dummy_function = function () end
-% \end{macrocode}
-% No final decision has been made on how to handle font definition. At
-% the moment, there are three candidates: The \identifier{generic}
-% callback as hard-coded in the font loader, the \identifier{old}
-% wrapper, and a simplified version of the latter (\identifier{patch})
-% that does nothing besides applying font patches.
-%
-% \begin{macrocode}
-
-luaotfload.font_definer = "patch" --- | “generic” | “old”
-
local error, warning, info, log =
luatexbase.provides_module(luaotfload.module)
@@ -1295,6 +1312,23 @@ if tex.luatexversion < luatex_version then
end
% \end{macrocode}
+% Prepare directories: the cache function in Luatex-Fonts checks for a
+% writable directory only once during startup. If there is no
+% \fileent{texmf-var} at this time it will create a subdirectory
+% \fileent{./luatex-cache} instead.
+% Thus everything has to be laid out before we load the fontloader.
+%
+% \begin{macrocode}
+
+local cachepath = kpse.expand_var"$TEXMFVAR"
+if not lfs.isdir(cachepath) then
+ dir.mkdirs(cachepath)
+ if not lfs.isdir(cachepath) then
+ error( "ERROR could not create directory %s", cachepath)
+ end
+end
+
+% \end{macrocode}
% \subsection{Module loading}
% We load the files imported from \CONTEXT with this function.
% It automatically prepends the prefix \fileent{luaotfload-} to its
@@ -1606,13 +1640,22 @@ formats.ofm = "type1"
% most cases when looking up font files, only when generating the database.
%
% \begin{macrocode}
-request_resolvers.file = function (specification)
- local found = fonts.names.crude_file_lookup(specification.name)
- --local found = fonts.names.crude_file_lookup_verbose(specification.name)
- specification.name = found[1]
- --if format then specification.forced = format end
+
+local resolvefile = fonts.names.crude_file_lookup
+--local resolvefile = fonts.names.crude_file_lookup_verbose
+
+function request_resolvers.file(specification)
+ local name=resolvefile(specification.name)
+ local suffix=file.suffix(name)
+ if formats[suffix] then
+ specification.forced=suffix
+ specification.name=file.removesuffix(name)
+ else
+ specification.name=name
+ end
end
+
% \end{macrocode}
% We classify as \verb|anon:| those requests that have neither a
% prefix nor brackets. According to Khaled\footnote{%
@@ -1699,8 +1742,6 @@ local patch_defined_font = function (specification, size, id)
return tfmdata
end
-caches.compilemethod = "both"
-
reset_callback("define_font")
% \end{macrocode}
@@ -1751,10 +1792,16 @@ loadmodule"auxiliary.lua" --- additionaly high-level functionality (new)
\else
\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{luaotfload}%
- [2013/04/16 v2.2 OpenType layout system]
+ [2013/05/10 v2.2 OpenType layout system]
\RequirePackage{luatexbase}
\fi
-\RequireLuaModule{luaotfload}
+\ifnum\luatexversion<76
+ %% here some deprecation warning would be in order
+ \RequireLuaModule{lualibs}
+ \RequireLuaModule{luaotfload-legacy}
+\else
+ \RequireLuaModule{luaotfload}
+\fi
\endinput
% \end{macrocode}
% \iffalse
diff --git a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-auxiliary.lua b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-auxiliary.lua
index 5de964d3fc4..999bc22d36c 100644
--- a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-auxiliary.lua
+++ b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-auxiliary.lua
@@ -20,6 +20,7 @@ config.luaotfload = config.luaotfload or { }
local aux = luaotfload.aux
local log = luaotfload.log
+local warning = luaotfload.log
local identifiers = fonts.hashes.identifiers
local fontid = font.id
@@ -30,6 +31,7 @@ local stringlower = string.lower
local stringformat = string.format
local stringgsub = string.gsub
local stringbyte = string.byte
+local stringfind = string.find
-----------------------------------------------------------------------
--- font patches
@@ -60,28 +62,45 @@ local add_fontdata_fallbacks = function (fontdata)
--- bother to do any checks, will access
--- fontdata.shared no matter what ...
fontdata.units = fontdata.units_per_em
+
else --- otf
- metadata = fontdata.shared.rawdata.metadata
- fontdata.units = fontparameters.units
+ metadata = fontdata.shared.rawdata.metadata
+ fontdata.name = metadata.origname or fontdata.name
+ fontdata.units = fontdata.units_per_em
+ fontdata.size = fontdata.size or fontparameters.size
local resources = fontdata.resources
- fontdata.size = fontparameters.size
--- for legacy fontspec.lua and unicode-math.lua
- fontdata.shared.otfdata = metadata
- fontdata.shared.otfdata.metadata = metadata --- brr, that’s meta indeed
- --- for microtype.lua
+ fontdata.shared.otfdata = {
+ pfminfo = { os2_capheight = metadata.pfminfo.os2_capheight },
+ metadata = { ascent = metadata.ascent },
+ }
+ --- for microtype and fontspec
+ local fake_features = { } -- wrong: table.copy(resources.features)
+ setmetatable(fake_features, { __index = function (tab, idx)
+ warning("some package (probably fontspec) is outdated")
+ warning(
+ "attempt to index " ..
+ "tfmdata.shared.otfdata.luatex.features (%s)",
+ idx)
+ --os.exit(1)
+ return nil --- empty anyways
+ end})
fontdata.shared.otfdata.luatex = {
unicodes = resources.unicodes,
- features = resources.features,
+ features = fake_features,
}
end
end
return fontdata
end
-luatexbase.add_to_callback(
- "luaotfload.patch_font",
- add_fontdata_fallbacks,
- "luaotfload.fontdata_fallbacks")
+--if config.luaotfload.compatibility == true then
+if true then
+ luatexbase.add_to_callback(
+ "luaotfload.patch_font",
+ add_fontdata_fallbacks,
+ "luaotfload.fontdata_fallbacks")
+end
--[[doc--
diff --git a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-database.lua b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-database.lua
index 576341f2308..60a7977bce5 100644
--- a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-database.lua
+++ b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-database.lua
@@ -71,7 +71,7 @@ fonts.definers = fonts.definers or { }
local names = fonts.names
-names.version = 2.203
+names.version = 2.204
names.data = nil --- contains the loaded database
names.lookups = nil --- contains the lookup cache
names.path = {
@@ -146,10 +146,11 @@ This is a sketch of the luaotfload db:
psname : string;
subfamily : string;
}
- size : int list;
- slant : int;
- weight : int;
- width : int;
+ size : int list;
+ slant : int;
+ weight : int;
+ width : int;
+ units_per_em : int; // mainly 1000, but also 2048 or 256
}
and filestatus = (fullname, { index : int list; timestamp : int }) dict
@@ -238,7 +239,7 @@ end
local crude_file_lookup
local crude_file_lookup_verbose
local find_closest
-local flush_cache
+local flush_lookup_cache
local font_fullinfo
local load_names
local load_lookups
@@ -258,8 +259,8 @@ local fonts_reloaded = false
--- limit output when approximate font matching (luaotfload-tool -F)
local fuzzy_limit = 1 --- display closest only
---- unit -> dbobj
-load_names = function ( )
+--- bool? -> dbobj
+load_names = function (dry_run)
local starttime = os.gettimeofday()
local foundname, data = load_lua_file(names.path.path)
@@ -272,8 +273,11 @@ load_names = function ( )
report("both", 0, "db",
[[Font names database not found, generating new one.
This can take several minutes; please be patient.]])
- data = update_names(fontnames_init(false))
- save_names(data)
+ 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.")
+ end
end
fonts_loaded = true
return data
@@ -316,7 +320,7 @@ end
local type1_formats = { "tfm", "ofm", }
---- string -> (string * bool | int)
+--- string -> string
crude_file_lookup_verbose = function (filename)
if not names.data then names.data = load_names() end
local data = names.data
@@ -326,18 +330,18 @@ crude_file_lookup_verbose = function (filename)
--- look up in db first ...
found = data.barenames[filename]
if found and mappings[found] then
- found = mappings[found].filename
+ found = mappings[found].filename[1]
report("info", 0, "db",
"crude file lookup: req=%s; hit=bare; ret=%s",
- filename, found[1])
+ filename, found)
return found
end
found = data.basenames[filename]
if found and mappings[found] then
- found = mappings[found].filename
+ found = mappings[found].filename[1]
report("info", 0, "db",
"crude file lookup: req=%s; hit=base; ret=%s",
- filename, found[1])
+ filename, found)
return found
end
@@ -345,13 +349,13 @@ crude_file_lookup_verbose = function (filename)
for i=1, #type1_formats do
local format = type1_formats[i]
if resolvers.findfile(filename, format) then
- return { file.addsuffix(filename, format), false }, format
+ return file.addsuffix(filename, format), format
end
end
- return { filename, false }, nil
+ return filename, nil
end
---- string -> (string * bool | int)
+--- string -> string
crude_file_lookup = function (filename)
if not names.data then names.data = load_names() end
local data = names.data
@@ -360,15 +364,15 @@ crude_file_lookup = function (filename)
or data.basenames[filename]
if found then
found = data.mappings[found]
- if found then return found.filename end
+ if found then return found.filename[1] end
end
for i=1, #type1_formats do
local format = type1_formats[i]
if resolvers.findfile(filename, format) then
- return { file.addsuffix(filename, format), false }, format
+ return file.addsuffix(filename, format), format
end
end
- return { filename, false }, nil
+ return filename, nil
end
--[[doc--
@@ -442,7 +446,10 @@ resolve_cached = function (_, _, specification)
--- TODO this should trigger a save only once the
--- document is compiled (finish_pdffile callback?)
report("both", 5, "cache", "saving updated cache")
- save_lookups()
+ local success = save_lookups()
+ if not success then --- sad, but not critical
+ report("both", 0, "cache", "could not write to cache")
+ end
return filename, subfont, true
end
@@ -674,9 +681,12 @@ end --- resolve()
reload_db = function (why, caller, ...)
report("both", 1, "db", "reload initiated; reason: “%s”", why)
names.data = update_names()
- save_names()
- fonts_reloaded = true
- return caller(...)
+ local success = save_names()
+ if success then
+ fonts_reloaded = true
+ return caller(...)
+ end
+ report("both", 0, "db", "Database update unsuccessful.")
end
--- string -> string -> int
@@ -836,13 +846,16 @@ font_fullinfo = function (filename, subfont)
report("log", 1, "db", "broken font rejected", "%s", basefile)
return
end
- tfmdata.fontname = metadata.fontname
- tfmdata.fullname = metadata.fullname
- tfmdata.familyname = metadata.familyname
- tfmdata.filename = { filename, subfont } -- always store full path
- tfmdata.weight = metadata.pfminfo.weight
- tfmdata.width = metadata.pfminfo.width
- tfmdata.slant = metadata.italicangle
+ tfmdata.fontname = metadata.fontname
+ tfmdata.fullname = metadata.fullname
+ tfmdata.familyname = metadata.familyname
+ tfmdata.filename = { filename, subfont } -- always store full path
+ 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,
@@ -855,7 +868,9 @@ end
--- we return true if the fond is new or re-indexed
--- string -> dbobj -> dbobj -> bool
local load_font = function (fullname, fontnames, newfontnames)
- if not fullname then return false end
+ if not fullname then
+ return false
+ end
local newmappings = newfontnames.mappings
local newstatus = newfontnames.status
@@ -1057,39 +1072,52 @@ for key, value in next, font_extensions do
font_extensions_set[value] = true
end
---- string -> dbobj -> dbobj -> bool -> (int * int)
-local scan_dir = function (dirname, fontnames, newfontnames)
- --[[
- This function scans a directory and populates the list of fonts
+--[[doc--
+
+ scan_dir() scans a directory and populates the list of fonts
with all the fonts it finds.
- - dirname is the name of the directory to scan
- - names is the font database to fill -> no such term!!!
- - texmf used to be a boolean saying if we are scanning a texmf directory
- ]]
+
+ · dirname : name of the directory to scan
+ · fontnames : current font db object
+ · newnames : font db object to fill
+ · dry_run : don’t touch anything
+
+--doc]]--
+
+--- string -> dbobj -> dbobj -> bool -> (int * int)
+local scan_dir = function (dirname, fontnames, newfontnames, dry_run)
local n_scanned, n_new = 0, 0 --- total of fonts collected
- report("log", 2, "db", "scanning", "%s", dirname)
+ report("both", 2, "db", "scanning directory %s", dirname)
for _,i in next, font_extensions do
for _,ext in next, { i, stringupper(i) } do
local found = dirglob(stringformat("%s/**.%s$", dirname, ext))
local n_found = #found
--- note that glob fails silently on broken symlinks, which
--- happens sometimes in TeX Live.
- report("log", 2, "db", "%s '%s' fonts found", n_found, ext)
+ report("both", 4, "db", "%s '%s' fonts found", n_found, ext)
n_scanned = n_scanned + n_found
for j=1, n_found do
local fullname = found[j]
fullname = path_normalize(fullname)
- report("log", 2, "db", "loading font “%s”", fullname)
- local new = load_font(fullname, fontnames, newfontnames)
- if new then n_new = n_new + 1 end
+ local new
+ if dry_run == true then
+ report("both", 1, "db", "would have been loading “%s”", fullname)
+ else
+ report("both", 4, "db", "loading font “%s”", fullname)
+ local new = load_font(fullname, fontnames, newfontnames)
+ if new == true then
+ n_new = n_new + 1
+ end
+ end
end
end
end
- report("log", 2, "db", "%d fonts found in '%s'", n_scanned, dirname)
+ report("both", 4, "db", "%d fonts found in '%s'", n_scanned, dirname)
return n_scanned, n_new
end
-local function scan_texmf_fonts(fontnames, newfontnames)
+--- 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
@@ -1104,7 +1132,8 @@ local function scan_texmf_fonts(fontnames, newfontnames)
fontdirs = fontdirs .. stringgsub(kpseexpand_path("$TTFONTS"), "^%.", "")
if not stringis_empty(fontdirs) then
for _,d in next, filesplitpath(fontdirs) do
- local found, new = scan_dir(d, fontnames, newfontnames)
+ report("info", 4, "db", "Entering directory %s", d)
+ local found, new = scan_dir(d, fontnames, newfontnames, dry_run)
n_scanned = n_scanned + found
n_new = n_new + new
end
@@ -1338,6 +1367,7 @@ do --- closure for read_fonts_conf()
end --- read_fonts_conf closure
--- TODO stuff those paths into some writable table
+--- unit -> string list
local function get_os_dirs()
if os.name == 'macosx' then
return {
@@ -1360,7 +1390,8 @@ local function get_os_dirs()
return {}
end
-local function scan_os_fonts(fontnames, newfontnames)
+--- 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
@@ -1370,23 +1401,27 @@ local function scan_os_fonts(fontnames, newfontnames)
]]
report("info", 2, "db", "Scanning OS fonts...")
report("info", 3, "db", "Searching in static system directories...")
- for _,d in next, get_os_dirs() do
- local found, new = scan_dir(d, fontnames, newfontnames)
+ for _, d in next, get_os_dirs() do
+ local found, new = scan_dir(d, fontnames, newfontnames, dry_run)
n_scanned = n_scanned + found
n_new = n_new + new
end
return n_scanned, n_new
end
-flush_cache = function ()
+--- unit -> (bool, lookup_cache)
+flush_lookup_cache = function ()
if not names.lookups then names.lookups = load_lookups() end
names.lookups = { }
collectgarbage"collect"
return true, names.lookups
end
---- dbobj -> bool -> dbobj
-update_names = function (fontnames, force)
+--- 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")
@@ -1407,7 +1442,7 @@ update_names = function (fontnames, force)
fontnames = fontnames_init(false)
else
if not fontnames then
- fontnames = load_names()
+ fontnames = load_names(dry_run)
end
if fontnames.version ~= names.version then
report("both", 1, "db", "No font names database or old "
@@ -1419,11 +1454,11 @@ update_names = function (fontnames, force)
read_blacklist()
local scanned, new
- scanned, new = scan_texmf_fonts(fontnames, newfontnames)
+ scanned, new = scan_texmf_fonts(fontnames, newfontnames, dry_run)
n_scanned = n_scanned + scanned
n_new = n_new + new
- scanned, new = scan_os_fonts(fontnames, newfontnames)
+ scanned, new = scan_os_fonts(fontnames, newfontnames, dry_run)
n_scanned = n_scanned + scanned
n_new = n_new + new
@@ -1452,7 +1487,7 @@ end
--- file. As we update it after every single addition this saves us
--- quite some time.
---- unit -> string
+--- unit -> bool
save_lookups = function ( )
---- this is boilerplate and should be refactored into something
---- usable by both the db and the cache writers
@@ -1466,19 +1501,19 @@ save_lookups = function ( )
os.remove(lucname)
caches.compile(lookups, luaname, lucname)
report("both", 3, "cache", "Lookup cache saved")
- return names.path.lookup_path
+ return true
end
end
end
report("info", 0, "cache", "Could not write lookup cache")
- return nil
+ return false
end
--- save_names() is usually called without the argument
---- dbobj -> unit
+--- dbobj? -> bool
save_names = function (fontnames)
if not fontnames then fontnames = names.data end
- local path = ensure_names_path()
+ local path = ensure_names_path()
if fileiswritable(path) then
local luaname, lucname = make_name(names.path.path)
if luaname then
@@ -1487,38 +1522,170 @@ save_names = function (fontnames)
if lucname and type(caches.compile) == "function" then
os.remove(lucname)
caches.compile(fontnames, luaname, lucname)
- report("info", 0, "db", "Font names database saved")
- return names.path.path
+ report("info", 1, "db", "Font names database saved")
+ return true
end
end
end
report("both", 0, "db", "Failed to save names database")
- return nil
+ return false
end
-scan_external_dir = function (dir)
- local old_names, new_names
- if fonts_loaded then
- old_names = names.data
- else
- old_names = load_names()
+--[[doc--
+
+ Below set of functions is modeled after mtx-cache.
+
+--doc]]--
+
+--- string -> string -> string list -> string list -> string list -> unit
+local print_cache = function (category, path, luanames, lucnames, rest)
+ local report_indeed = function (...)
+ report("info", 0, "cache", ...)
end
- new_names = tablecopy(old_names)
- local n_scanned, n_new = scan_dir(dir, old_names, new_names)
- names.data = new_names
- return n_scanned, n_new
+ report_indeed("Luaotfload cache: %s", category)
+ report_indeed("location: %s", path)
+ report_indeed("[raw] %4i", #luanames)
+ report_indeed("[compiled] %4i", #lucnames)
+ report_indeed("[other] %4i", #rest)
+ report_indeed("[total] %4i", #luanames + #lucnames + #rest)
+end
+
+--- string -> string -> string list -> bool -> bool
+local purge_from_cache = function (category, path, list, all)
+ report("info", 2, "cache", "Luaotfload cache: %s %s",
+ (all and "erase" or "purge"), category)
+ report("info", 2, "cache", "location: %s",path)
+ local n = 0
+ for i=1,#list do
+ local filename = list[i]
+ if string.find(filename,"luatex%-cache") then -- safeguard
+ if all then
+ report("info", 5, "cache", "removing %s", filename)
+ os.remove(filename)
+ n = n + 1
+ else
+ local suffix = file.suffix(filename)
+ if suffix == "lua" then
+ local checkname = file.replacesuffix(
+ filename, "lua", "luc")
+ if lfs.isfile(checkname) then
+ report("info", 5, "cache", "removing %s", filename)
+ os.remove(filename)
+ n = n + 1
+ end
+ end
+ end
+ end
+ end
+ report("info", 2, "cache", "removed lua files : %i", n)
+ return true
+end
+--- string -> string list -> int -> string list -> string list -> string list ->
+--- (string list * string list * string list * string list)
+local collect_cache collect_cache = function (path, all, n, luanames,
+ lucnames, rest)
+ if not all then
+ local all = dirglob(path .. "/**/*")
+ local luanames, lucnames, rest = { }, { }, { }
+ return collect_cache(nil, all, 1, luanames, lucnames, rest)
+ end
+
+ local filename = all[n]
+ if filename then
+ local suffix = file.suffix(filename)
+ if suffix == "lua" then
+ luanames[#luanames+1] = filename
+ elseif suffix == "luc" then
+ lucnames[#lucnames+1] = filename
+ else
+ rest[#rest+1] = filename
+ end
+ return collect_cache(nil, all, n+1, luanames, lucnames, rest)
+ end
+ return luanames, lucnames, rest, all
end
+--- unit -> unit
+local purge_cache = function ( )
+ local writable_path = caches.getwritablepath()
+ local luanames, lucnames, rest = collect_cache(writable_path)
+ if logs.get_loglevel() > 1 then
+ print_cache("writable path", writable_path, luanames, lucnames, rest)
+ end
+ local success = purge_from_cache("writable path", writable_path, luanames, false)
+ return success
+end
+
+--- unit -> unit
+local erase_cache = function ( )
+ local writable_path = caches.getwritablepath()
+ local luanames, lucnames, rest, all = collect_cache(writable_path)
+ if logs.get_loglevel() > 1 then
+ print_cache("writable path", writable_path, luanames, lucnames, rest)
+ end
+ local success = purge_from_cache("writable path", writable_path, all, true)
+ return success
+end
+
+local separator = function ( )
+ report("info", 0, string.rep("-", 67))
+end
+
+--- unit -> unit
+local show_cache = function ( )
+ local readable_paths = caches.getreadablepaths()
+ local writable_path = caches.getwritablepath()
+ local luanames, lucnames, rest = collect_cache(writable_path)
+
+ separator()
+ print_cache("writable path", writable_path, luanames, lucnames, rest)
+ texiowrite_nl""
+ for i=1,#readable_paths do
+ local readable_path = readable_paths[i]
+ if readable_path ~= writable_path then
+ local luanames, lucnames = collect_cache(readable_path)
+ print_cache("readable path",
+ readable_path,luanames,lucnames,rest)
+ end
+ end
+ separator()
+ return true
+end
+
+--- is this used anywhere? we decided to comment it for the
+--- time being.
+--- https://github.com/lualatex/luaotfload/pull/61
+--scan_external_dir = function (dir)
+-- local old_names, new_names
+-- if fonts_loaded then
+-- old_names = names.data
+-- else
+-- old_names = load_names()
+-- end
+-- new_names = tablecopy(old_names)
+-- local n_scanned, n_new = scan_dir(dir, old_names, new_names)
+-- names.data = new_names
+-- return n_scanned, n_new
+--end
+
+-----------------------------------------------------------------------
--- export functionality to the namespace “fonts.names”
-names.flush_cache = flush_cache
+-----------------------------------------------------------------------
+
+names.flush_lookup_cache = flush_lookup_cache
names.save_lookups = save_lookups
names.load = load_names
names.save = save_names
-names.scan = scan_external_dir
+-----.scan = scan_external_dir
names.update = update_names
names.crude_file_lookup = crude_file_lookup
names.crude_file_lookup_verbose = crude_file_lookup_verbose
+--- font cache
+names.purge_cache = purge_cache
+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")
diff --git a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-features.lua b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-features.lua
index f91aee7b9fe..220b362fe74 100644
--- a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-features.lua
+++ b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-features.lua
@@ -32,6 +32,7 @@ local old_feature_list = { }
local report = logs.names_report
+local stringfind = string.find
local stringlower = string.lower
local stringgsub = string.gsub
local stringis_empty = string.is_empty
@@ -212,13 +213,28 @@ local toboolean = function (s)
if s == "false" then return false end
--if s == "yes" then return true end --- Context style
--if s == "no" then return false end
- return s
+ return stringlower(s)
+end
+
+--- dirty test if a file: request is actually a path: lookup; don’t
+--- ask!
+local check_garbage = function (_,i, garbage)
+ if stringfind(garbage, "/") then
+ report("log", 0, "load", --- ffs use path!
+ "warning: path in file: lookups is deprecated; ")
+ report("log", 0, "load", "use bracket syntax instead!")
+ report("log", 0, "load",
+ "position: %d; full match: “%s”",
+ i, garbage)
+ return true
+ end
+ return false
end
local lpegmatch = lpeg.match
local P, S, R = lpeg.P, lpeg.S, lpeg.R
-local C, Cc, Cf, Cg, Cs, Ct
- = lpeg.C, lpeg.Cc, lpeg.Cf, lpeg.Cg, lpeg.Cs, lpeg.Ct
+local C, Cc, Cf, Cg, Cmt, Cs, Ct
+ = lpeg.C, lpeg.Cc, lpeg.Cf, lpeg.Cg, lpeg.Cmt, lpeg.Cs, lpeg.Ct
--- terminals and low-level classes -----------------------------------
--- note we could use the predefined ones from lpeg.patterns
@@ -259,9 +275,17 @@ local modifier = slash * (other_modifier --> ignore
local modifier_list = Cg(Ct(modifier^0), "modifiers")
--- lookups -----------------------------------------------------------
-local fontname = C((1-S"/:(")^1) --- like luatex-fonts
+local fontname = C((1-S":(/")^1) --- like luatex-fonts
+local unsupported = Cmt((1-S":(")^1, check_garbage)
local prefixed = P"name:" * ws * Cg(fontname, "name")
- + P"file:" * ws * Cg(fontname, "file")
+--- initially we intended file: to emulate the behavior of
+--- luatex-fonts, i.e. no paths allowed. after all, we do have XeTeX
+--- emulation with the path lookup and it interferes with db lookups.
+--- turns out fontspec and other widely used packages rely on file:
+--- with paths already, so we’ll add a less strict rule here. anyways,
+--- we’ll emit a warning.
+ + P"file:" * ws * Cg(unsupported, "path")
+ + P"file:" * ws * Cg(fontname, "file")
local unprefixed = Cg(fontname, "anon")
local path_lookup = lbrk * Cg(C((1-rbrk)^1), "path") * rbrk
@@ -313,7 +337,9 @@ local import_values = {
--- That’s what the 1.x parser did, not quite as graciously,
--- with an array of branch expressions.
-- "style", "optsize",--> from slashed notation; handled otherwise
- "lookup", "sub", "mode",
+ { "lookup", false },
+ { "sub", false },
+ { "mode", true },
}
local lookup_types = { "anon", "file", "name", "path" }
@@ -387,11 +413,14 @@ local handle_request = function (specification)
end
for n=1, #import_values do
- local feat = import_values[n]
+ local feat = import_values[n][1]
+ local keep = import_values[n][2]
local newvalue = request.features[feat]
if newvalue then
specification[feat] = request.features[feat]
- request.features[feat] = nil
+ if not keep then
+ request.features[feat] = nil
+ end
end
end
diff --git a/Master/texmf-dist/tex/luatex/luaotfload/font-age.lua b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-glyphlist.lua
index 3a7098e0ffd..a8cf266ad8b 100644
--- a/Master/texmf-dist/tex/luatex/luaotfload/font-age.lua
+++ b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-glyphlist.lua
@@ -14,7 +14,7 @@ end
--[[doc--
Everything below has been autogenerated. Run mkglyphlist to rebuild
-font-age.lua.
+luaotfload-glyphlist.lua.
--doc]]--
return {
@@ -527,9 +527,11 @@ return {
["SF070000"]=9524,
["SF080000"]=9500,
["SF090000"]=9508,
+ ["SF10000"]=9484,
["SF100000"]=9472,
["SF110000"]=9474,
["SF190000"]=9569,
+ ["SF20000"]=9492,
["SF200000"]=9570,
["SF210000"]=9558,
["SF220000"]=9557,
@@ -539,10 +541,12 @@ return {
["SF260000"]=9565,
["SF270000"]=9564,
["SF280000"]=9563,
+ ["SF30000"]=9488,
["SF360000"]=9566,
["SF370000"]=9567,
["SF380000"]=9562,
["SF390000"]=9556,
+ ["SF40000"]=9496,
["SF400000"]=9577,
["SF410000"]=9574,
["SF420000"]=9568,
@@ -553,11 +557,16 @@ return {
["SF470000"]=9572,
["SF480000"]=9573,
["SF490000"]=9561,
+ ["SF50000"]=9532,
["SF500000"]=9560,
["SF510000"]=9554,
["SF520000"]=9555,
["SF530000"]=9579,
["SF540000"]=9578,
+ ["SF60000"]=9516,
+ ["SF70000"]=9524,
+ ["SF80000"]=9500,
+ ["SF90000"]=9508,
["Sacute"]=346,
["Sacutedotaccent"]=7780,
["Sampigreek"]=992,
@@ -910,6 +919,7 @@ return {
["afii10831"]=63175,
["afii10832"]=63176,
["afii10846"]=1241,
+ ["afii208"]=8213,
["afii299"]=8206,
["afii300"]=8207,
["afii301"]=8205,
diff --git a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-legacy-attributes.lua b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-legacy-attributes.lua
new file mode 100644
index 00000000000..c6130b4f781
--- /dev/null
+++ b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-legacy-attributes.lua
@@ -0,0 +1,27 @@
+-----------------------------------------------------------------------
+-- FILE: otfl-luat-att.lua
+-- USAGE: with old luaotfload
+-- DESCRIPTION: setting attributes abide luatexbase rules
+-- REQUIREMENTS: some old luatex
+-- AUTHOR: Philipp Gesang (Phg), <phg42.2a@gmail.com>
+-- CREATED: 2013-05-10 20:37:19+0200
+-----------------------------------------------------------------------
+--
+
+if not modules then modules = { } end modules ['otfl-luat-att'] = {
+ version = math.pi/42,
+ comment = "companion to luaotfload.lua",
+ author = "Philipp Gesang",
+ copyright = "Luaotfload Development Team",
+ license = "GNU GPL v2"
+}
+
+function attributes.private(name)
+ local attr = "otfl@" .. name
+ local number = luatexbase.attributes[attr]
+ if not number then
+ number = luatexbase.new_attribute(attr)
+ end
+ return number
+end
+
diff --git a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-legacy-database.lua b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-legacy-database.lua
new file mode 100644
index 00000000000..4af05f5486d
--- /dev/null
+++ b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-legacy-database.lua
@@ -0,0 +1,724 @@
+if not modules then modules = { } end modules ['font-nms'] = {
+ version = "old",
+ comment = "companion to luaotfload.lua",
+ author = "Khaled Hosny and Elie Roux",
+ copyright = "Luaotfload Development Team",
+ license = "GNU GPL v2"
+}
+
+fonts = fonts or { }
+fonts.names = fonts.names or { }
+
+local names = fonts.names
+local names_dir = "luatex-cache/generic/names"
+names.version = "old" -- not the same as in context
+names.data = nil
+names.path = {
+ basename = "otfl-names.lua", --- different from current
+ localdir = file.join(kpse.expand_var("$TEXMFVAR"), names_dir),
+ systemdir = file.join(kpse.expand_var("$TEXMFSYSVAR"), names_dir),
+}
+
+
+local splitpath, expandpath = file.split_path, kpse.expand_path
+local glob, basename = dir.glob, file.basename
+local upper, lower, format = string.upper, string.lower, string.format
+local gsub, match, rpadd = string.gsub, string.match, string.rpadd
+local gmatch, sub, find = string.gmatch, string.sub, string.find
+local utfgsub = unicode.utf8.gsub
+
+local trace_short = false --tracing adapted to rebuilding of the database inside a document
+local trace_search = false --trackers.register("names.search", function(v) trace_search = v end)
+local trace_loading = false --trackers.register("names.loading", function(v) trace_loading = v end)
+
+local function sanitize(str)
+ if str then
+ return utfgsub(lower(str), "[^%a%d]", "")
+ else
+ return str -- nil
+ end
+end
+
+local function fontnames_init()
+ return {
+ mappings = { },
+ status = { },
+ version = names.version,
+ }
+end
+
+local function load_names()
+ local localpath = file.join(names.path.localdir, names.path.basename)
+ local systempath = file.join(names.path.systemdir, names.path.basename)
+ local kpsefound = kpse.find_file(names.path.basename)
+ local foundname
+ local data
+ if kpsefound and file.isreadable(kpsefound) then
+ data = dofile(kpsefound)
+ foundname = kpsefound
+ elseif file.isreadable(localpath) then
+ data = dofile(localpath)
+ foundname = localpath
+ elseif file.isreadable(systempath) then
+ data = dofile(systempath)
+ foundname = systempath
+ end
+ if data then
+ logs.info("Font names database loaded: " .. foundname)
+ else
+ logs.info([[Font names database not found, generating new one.
+ This can take several minutes; please be patient.]])
+ data = names.update(fontnames_init())
+ names.save(data)
+ end
+ return data
+end
+
+local synonyms = {
+ regular = { "normal", "roman", "plain", "book", "medium" },
+ -- boldregular was for old versions of Linux Libertine, is it still useful?
+ -- semibold is in new versions of Linux Libertine, but there is also a bold,
+ -- not sure it's useful here...
+ bold = { "demi", "demibold", "semibold", "boldregular" },
+ italic = { "regularitalic", "normalitalic", "oblique", "slanted" },
+ bolditalic = { "boldoblique", "boldslanted", "demiitalic", "demioblique", "demislanted", "demibolditalic", "semibolditalic" },
+}
+
+local loaded = false
+local reloaded = false
+
+function names.resolve(specification)
+ local name = sanitize(specification.name)
+ local style = sanitize(specification.style) or "regular"
+
+ local size
+ if specification.optsize then
+ size = tonumber(specification.optsize)
+ elseif specification.size then
+ size = specification.size / 65536
+ end
+
+
+ if not loaded then
+ names.data = names.load()
+ loaded = true
+ end
+
+ local data = names.data
+ if type(data) == "table" and data.version == names.version then
+ if data.mappings then
+ local found = { }
+ for _,face in next, data.mappings do
+ local family, subfamily, fullname, psname
+ local optsize, dsnsize, maxsize, minsize
+
+ if face.names then
+ family = sanitize(face.names.family)
+ subfamily = sanitize(face.names.subfamily)
+ fullname = sanitize(face.names.fullname)
+ psname = sanitize(face.names.psname)
+ end
+ local fontname = sanitize(face.fontname)
+ local pfullname = sanitize(face.fullname)
+ if #face.size > 0 then
+ optsize = face.size
+ dsnsize = optsize[1] and optsize[1] / 10
+ -- can be nil
+ maxsize = optsize[2] and optsize[2] / 10 or dsnsize
+ minsize = optsize[3] and optsize[3] / 10 or dsnsize
+ end
+ if name == family then
+ if subfamily == style then
+ if optsize then
+ if dsnsize == size
+ or (size > minsize and size <= maxsize) then
+ found[1] = face
+ break
+ else
+ found[#found+1] = face
+ end
+ else
+ found[1] = face
+ break
+ end
+ elseif synonyms[style] and
+ table.contains(synonyms[style], subfamily) then
+ if optsize then
+ if dsnsize == size
+ or (size > minsize and size <= maxsize) then
+ found[1] = face
+ break
+ else
+ found[#found+1] = face
+ end
+ else
+ found[1] = face
+ break
+ end
+ elseif subfamily == "regular" or
+ table.contains(synonyms.regular, subfamily) then
+ found.fallback = face
+ elseif name == fullname
+ or name == pfullname
+ or name == fontname
+ or name == psname then
+ if optsize then
+ if dsnsize == size
+ or (size > minsize and size <= maxsize) then
+ found[1] = face
+ break
+ else
+ found[#found+1] = face
+ end
+ else
+ found[1] = face
+ break
+ end
+ end
+ else
+ if name == fullname
+ or name == pfullname
+ or name == fontname
+ or name == psname then
+ if optsize then
+ if dsnsize == size
+ or (size > minsize and size <= maxsize) then
+ found[1] = face
+ break
+ else
+ found[#found+1] = face
+ end
+ else
+ found[1] = face
+ break
+ end
+ end
+ end
+ end
+ if #found == 1 then
+ if kpse.lookup(found[1].filename[1]) then
+ logs.report("load font",
+ "font family='%s', subfamily='%s' found: %s",
+ name, style, found[1].filename[1])
+ return found[1].filename[1], found[1].filename[2]
+ elseif lfs.isfile(found[1].found_at) then
+ logs.report("load font",
+ "font family='%s', subfamily='%s' found: %s",
+ name, style, found[1].found_at)
+ return found[1].found_at, found[1].filename[2]
+ 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 = math.abs(dsnsize-size)
+ if difference < least then
+ closest = face
+ least = difference
+ end
+ end
+ if kpse.lookup(closest.filename[1]) then
+ logs.report("load font",
+ "font family='%s', subfamily='%s' found: %s",
+ name, style, closest.filename[1])
+ return closest.filename[1], closest.filename[2]
+ elseif lfs.isfile(closest.found_at) then
+ logs.report("load font",
+ "font family='%s', subfamily='%s' found: %s",
+ name, style, closest.found_at)
+ return closest.found_at, closest.filename[2]
+ end
+ elseif found.fallback then
+ return found.fallback.filename[1], found.fallback.filename[2]
+ end
+ -- no font found so far
+ if not reloaded then
+ -- try reloading the database
+ names.data = names.update(names.data)
+ names.save(names.data)
+ reloaded = true
+ return names.resolve(specification)
+ else
+ -- else, fallback to filename
+ return specification.name, false
+ end
+ end
+ else
+ if not reloaded then
+ names.data = names.update()
+ names.save(names.data)
+ reloaded = true
+ return names.resolve(specification)
+ else
+ return specification.name, false
+ end
+ end
+end
+
+names.resolvespec = names.resolve
+
+function names.set_log_level(level)
+ if level == 2 then
+ trace_loading = true
+ elseif level >= 3 then
+ trace_loading = true
+ trace_search = true
+ end
+end
+
+local lastislog = 0
+
+local function log(fmt, ...)
+ lastislog = 1
+ texio.write_nl(format("luaotfload | %s", format(fmt,...)))
+ io.flush()
+end
+
+logs = logs or { }
+logs.report = logs.report or log
+logs.info = logs.info or log
+
+local function font_fullinfo(filename, subfont, texmf)
+ local found_at = filename
+ local t = { }
+ local f = fontloader.open(filename, subfont)
+ if not f then
+ if trace_loading then
+ logs.report("error: failed to open %s", filename)
+ end
+ return
+ end
+ local m = fontloader.to_table(f)
+ fontloader.close(f)
+ collectgarbage('collect')
+ -- see http://www.microsoft.com/typography/OTSPEC/features_pt.htm#size
+ if m.fontstyle_name then
+ for _,v in next, m.fontstyle_name do
+ if v.lang == 1033 then
+ t.fontstyle_name = v.name
+ end
+ end
+ end
+ if m.names then
+ for _,v in next, m.names do
+ if v.lang == "English (US)" then
+ t.names = {
+ -- see
+ -- http://developer.apple.com/textfonts/
+ -- TTRefMan/RM06/Chap6name.html
+ fullname = v.names.compatfull or v.names.fullname,
+ family = v.names.preffamilyname or v.names.family,
+ subfamily= t.fontstyle_name or v.names.prefmodifiers or v.names.subfamily,
+ psname = v.names.postscriptname
+ }
+ end
+ end
+ else
+ -- no names table, propably a broken font
+ if trace_loading then
+ logs.report("broken font rejected: %s", basefile)
+ end
+ return
+ end
+ t.fontname = m.fontname
+ t.fullname = m.fullname
+ t.familyname = m.familyname
+ t.filename = { texmf and basename(filename) or filename, subfont }
+ t.weight = m.pfminfo.weight
+ t.width = m.pfminfo.width
+ t.slant = m.italicangle
+ -- don't waste the space with zero values
+ t.size = {
+ m.design_size ~= 0 and m.design_size or nil,
+ m.design_range_top ~= 0 and m.design_range_top or nil,
+ m.design_range_bottom ~= 0 and m.design_range_bottom or nil,
+ }
+ -- rather, waste space on paths
+ t.found_at = found_at
+ return t
+end
+
+local function load_font(filename, fontnames, newfontnames, texmf)
+ local newmappings = newfontnames.mappings
+ local newstatus = newfontnames.status
+ local mappings = fontnames.mappings
+ local status = fontnames.status
+ local basefile = texmf and basename(filename) or filename
+ if filename then
+ if table.contains(names.blacklist, filename) or
+ table.contains(names.blacklist, basename(filename)) then
+ if trace_search then
+ logs.report("ignoring font '%s'", filename)
+ end
+ return
+ end
+ local timestamp, db_timestamp
+ db_timestamp = status[basefile] and status[basefile].timestamp
+ timestamp = lfs.attributes(filename, "modification")
+
+ local index_status = newstatus[basefile] or (not texmf and newstatus[basename(filename)])
+ if index_status and index_status.timestamp == timestamp then
+ -- already indexed this run
+ return
+ end
+
+ newstatus[basefile] = newstatus[basefile] or { }
+ newstatus[basefile].timestamp = timestamp
+ newstatus[basefile].index = newstatus[basefile].index or { }
+
+ if db_timestamp == timestamp and not newstatus[basefile].index[1] then
+ for _,v in next, status[basefile].index do
+ local index = #newstatus[basefile].index
+ newmappings[#newmappings+1] = mappings[v]
+ newstatus[basefile].index[index+1] = #newmappings
+ end
+ if trace_loading then
+ logs.report("font already indexed: %s", basefile)
+ end
+ return
+ end
+ local info = fontloader.info(filename)
+ if info then
+ if type(info) == "table" and #info > 1 then
+ for i in next, info do
+ local fullinfo = font_fullinfo(filename, i-1, texmf)
+ if not fullinfo then
+ return
+ end
+ local index = newstatus[basefile].index[i]
+ if not index then
+ index = #newmappings+1
+ end
+ newmappings[index] = fullinfo
+ newstatus[basefile].index[i] = index
+ end
+ else
+ local fullinfo = font_fullinfo(filename, false, texmf)
+ if not fullinfo then
+ return
+ end
+ local index = newstatus[basefile].index[1]
+ if not index then
+ index = #newmappings+1
+ end
+ newmappings[index] = fullinfo
+ newstatus[basefile].index[1] = index
+ end
+ else
+ if trace_loading then
+ logs.report("failed to load %s", basefile)
+ end
+ end
+ end
+end
+
+local function path_normalize(path)
+ --[[
+ path normalization:
+ - a\b\c -> a/b/c
+ - a/../b -> b
+ - /cygdrive/a/b -> a:/b
+ - reading symlinks under non-Win32
+ - using kpse.readable_file on Win32
+ ]]
+ if os.type == "windows" or os.type == "msdos" or os.name == "cygwin" then
+ path = path:gsub('\\', '/')
+ path = path:lower()
+ path = path:gsub('^/cygdrive/(%a)/', '%1:/')
+ end
+ if os.type ~= "windows" and os.type ~= "msdos" then
+ local dest = lfs.readlink(path)
+ if dest then
+ if kpse.readable_file(dest) then
+ path = dest
+ elseif kpse.readable_file(file.join(file.dirname(path), dest)) then
+ path = file.join(file.dirname(path), dest)
+ else
+ -- broken symlink?
+ end
+ end
+ end
+ path = file.collapse_path(path)
+ return path
+end
+
+fonts.path_normalize = path_normalize
+
+names.blacklist = { }
+
+local function read_blacklist()
+ local files = {
+ kpse.lookup("otfl-blacklist.cnf", {all=true, format="tex"})
+ }
+ local blacklist = names.blacklist
+
+ if files and type(files) == "table" then
+ for _,v in next, files do
+ for line in io.lines(v) do
+ line = line:strip() -- to get rid of lines like " % foo"
+ if line:find("^%%") or line:is_empty() then
+ -- comment or empty line
+ else
+ line = line:split("%")[1]
+ line = line:strip()
+ if trace_search then
+ logs.report("blacklisted file: %s", line)
+ end
+ blacklist[#blacklist+1] = line
+ end
+ end
+ end
+ end
+end
+
+local font_extensions = { "otf", "ttf", "ttc", "dfont" }
+
+local function scan_dir(dirname, fontnames, newfontnames, texmf)
+ --[[
+ This function scans a directory and populates the list of fonts
+ with all the fonts it finds.
+ - dirname is the name of the directory to scan
+ - names is the font database to fill
+ - texmf is a boolean saying if we are scanning a texmf directory
+ ]]
+ local list, found = { }, { }
+ local nbfound = 0
+ if trace_search then
+ logs.report("scanning '%s'", dirname)
+ end
+ for _,i in next, font_extensions do
+ for _,ext in next, { i, upper(i) } do
+ found = glob(format("%s/**.%s$", dirname, ext))
+ -- note that glob fails silently on broken symlinks, which happens
+ -- sometimes in TeX Live.
+ if trace_search then
+ logs.report("%s '%s' fonts found", #found, ext)
+ end
+ nbfound = nbfound + #found
+ table.append(list, found)
+ end
+ end
+ if trace_search then
+ logs.report("%d fonts found in '%s'", nbfound, dirname)
+ end
+
+ for _,file in next, list do
+ file = path_normalize(file)
+ if trace_loading then
+ logs.report("loading font: %s", file)
+ end
+ load_font(file, fontnames, newfontnames, texmf)
+ end
+end
+
+local function scan_texmf_fonts(fontnames, newfontnames)
+ --[[
+ This function scans all fonts in the texmf tree, through kpathsea
+ variables OPENTYPEFONTS and TTFONTS of texmf.cnf
+ ]]
+ if expandpath("$OSFONTDIR"):is_empty() then
+ logs.info("Scanning TEXMF fonts...")
+ else
+ logs.info("Scanning TEXMF and OS fonts...")
+ end
+ local fontdirs = expandpath("$OPENTYPEFONTS"):gsub("^%.", "")
+ fontdirs = fontdirs .. expandpath("$TTFONTS"):gsub("^%.", "")
+ if not fontdirs:is_empty() then
+ for _,d in next, splitpath(fontdirs) do
+ scan_dir(d, fontnames, newfontnames, true)
+ end
+ end
+end
+
+--[[
+ For the OS fonts, there are several options:
+ - if OSFONTDIR is set (which is the case under windows by default but
+ not on the other OSs), it scans it at the same time as the texmf tree,
+ in the scan_texmf_fonts.
+ - in addition:
+ - under Windows and Mac OSX, we take a look at some hardcoded directories
+ - under Unix, we read /etc/fonts/fonts.conf and read the directories in it
+
+ This means that if you have fonts in fancy directories, you need to set them
+ in OSFONTDIR if they cannot be found by fontconfig.
+]]
+
+local function read_fonts_conf(path, results, passed_paths)
+ --[[
+ This function parses /etc/fonts/fonts.conf and returns all the dir it finds.
+ The code is minimal, please report any error it may generate.
+ ]]
+ local f = io.open(path)
+ table.insert(passed_paths, path)
+ if not f then
+ logs.info("Warning: unable to read "..path.. ", skipping...")
+ return results
+ end
+ local incomments = false
+ for line in f:lines() do
+ while line and line ~= "" do
+ -- spaghetti code... hmmm...
+ if incomments then
+ local tmp = find(line, '-->')
+ if tmp then
+ incomments = false
+ line = sub(line, tmp+3)
+ else
+ line = nil
+ end
+ else
+ local tmp = find(line, '<!--')
+ local newline = line
+ if tmp then
+ -- for the analysis, we take everything that is before the
+ -- comment sign
+ newline = sub(line, 1, tmp-1)
+ -- and we loop again with the comment
+ incomments = true
+ line = sub(line, tmp+4)
+ else
+ -- if there is no comment start, the block after that will
+ -- end the analysis, we exit the while loop
+ line = nil
+ end
+ for dir in gmatch(newline, '<dir>([^<]+)</dir>') do
+ -- now we need to replace ~ by kpse.expand_path('~')
+ if sub(dir, 1, 1) == '~' then
+ dir = file.join(kpse.expand_path('~'), sub(dir, 2))
+ end
+ -- we exclude paths with texmf in them, as they should be
+ -- found anyway
+ if not find(dir, 'texmf') then
+ results[#results+1] = dir
+ end
+ end
+ for include in gmatch(newline, '<include[^<]*>([^<]+)</include>') do
+ -- include here can be four things: a directory or a file,
+ -- in absolute or relative path.
+ if sub(include, 1, 1) == '~' then
+ include = file.join(kpse.expand_path('~'),sub(include, 2))
+ -- First if the path is relative, we make it absolute:
+ elseif not lfs.isfile(include) and not lfs.isdir(include) then
+ include = file.join(file.dirname(path), include)
+ end
+ if lfs.isfile(include) and kpse.readable_file(include) and not table.contains(passed_paths, include) then
+ -- we exclude path with texmf in them, as they should
+ -- be found otherwise
+ read_fonts_conf(include, results, passed_paths)
+ elseif lfs.isdir(include) then
+ for _,f in next, glob(file.join(include, "*.conf")) do
+ if not table.contains(passed_paths, f) then
+ read_fonts_conf(f, results, passed_paths)
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+ f:close()
+ return results
+end
+
+-- for testing purpose
+names.read_fonts_conf = read_fonts_conf
+
+local function get_os_dirs()
+ if os.name == 'macosx' then
+ return {
+ file.join(kpse.expand_path('~'), "Library/Fonts"),
+ "/Library/Fonts",
+ "/System/Library/Fonts",
+ "/Network/Library/Fonts",
+ }
+ elseif os.type == "windows" or os.type == "msdos" or os.name == "cygwin" then
+ local windir = os.getenv("WINDIR")
+ return { file.join(windir, 'Fonts') }
+ else
+ return read_fonts_conf("/etc/fonts/fonts.conf", {}, {})
+ end
+end
+
+local function scan_os_fonts(fontnames, newfontnames)
+ --[[
+ This function scans the OS fonts through
+ - fontcache for Unix (reads the fonts.conf file and scans the directories)
+ - a static set of directories for Windows and MacOSX
+ ]]
+ logs.info("Scanning OS fonts...")
+ if trace_search then
+ logs.info("Searching in static system directories...")
+ end
+ for _,d in next, get_os_dirs() do
+ scan_dir(d, fontnames, newfontnames, false)
+ end
+end
+
+local function update_names(fontnames, force)
+ --[[
+ The main function, scans everything
+ - fontnames is the final table to return
+ - force is whether we rebuild it from scratch or not
+ ]]
+ logs.info("Updating the font names database:")
+
+ if force then
+ fontnames = fontnames_init()
+ else
+ if not fontnames then
+ fontnames = names.load()
+ end
+ if fontnames.version ~= names.version then
+ fontnames = fontnames_init()
+ if trace_search then
+ logs.report("No font names database or old one found; "
+ .."generating new one")
+ end
+ end
+ end
+ local newfontnames = fontnames_init()
+ read_blacklist()
+ scan_texmf_fonts(fontnames, newfontnames)
+ scan_os_fonts(fontnames, newfontnames)
+ return newfontnames
+end
+
+local function save_names(fontnames)
+ local savepath = names.path.localdir
+ if not lfs.isdir(savepath) then
+ dir.mkdirs(savepath)
+ end
+ savepath = file.join(savepath, names.path.basename)
+ if file.iswritable(savepath) then
+ table.tofile(savepath, fontnames, true)
+ logs.info("Font names database saved: %s \n", savepath)
+ return savepath
+ else
+ logs.info("Failed to save names database\n")
+ return nil
+ end
+end
+
+local function scan_external_dir(dir)
+ local old_names, new_names
+ if loaded then
+ old_names = names.data
+ else
+ old_names = names.load()
+ loaded = true
+ end
+ new_names = table.copy(old_names)
+ scan_dir(dir, old_names, new_names)
+ names.data = new_names
+end
+
+names.scan = scan_external_dir
+names.load = load_names
+names.update = update_names
+names.save = save_names
+
+-- vim:ft=lua:sw=4:ts=4:expandtab
diff --git a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-legacy-merged.lua b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-legacy-merged.lua
new file mode 100644
index 00000000000..9bec2986dea
--- /dev/null
+++ b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-legacy-merged.lua
@@ -0,0 +1,8157 @@
+-- merged file : luaotfload-legacy-merged.lua
+-- parent file : luaotfload-legacy.lua
+-- merge date : Fri May 10 20:57:35 2013
+
+do -- begin closure to overcome local limits and interference
+
+if not modules then modules={} end modules ['luat-dum']={
+ version=1.100,
+ comment="companion to luatex-*.tex",
+ author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright="PRAGMA ADE / ConTeXt Development Team",
+ license="see context related readme files"
+}
+local dummyfunction=function() end
+statistics={
+ register=dummyfunction,
+ starttiming=dummyfunction,
+ stoptiming=dummyfunction,
+}
+directives={
+ register=dummyfunction,
+ enable=dummyfunction,
+ disable=dummyfunction,
+}
+trackers={
+ register=dummyfunction,
+ enable=dummyfunction,
+ disable=dummyfunction,
+}
+experiments={
+ register=dummyfunction,
+ enable=dummyfunction,
+ disable=dummyfunction,
+}
+storage={
+ register=dummyfunction,
+ shared={},
+}
+logs={
+ report=dummyfunction,
+ simple=dummyfunction,
+}
+tasks={
+ new=dummyfunction,
+ actions=dummyfunction,
+ appendaction=dummyfunction,
+ prependaction=dummyfunction,
+}
+callbacks={
+ register=function(n,f) return callback.register(n,f) end,
+}
+texconfig.kpse_init=true
+resolvers=resolvers or {}
+local remapper={
+ otf="opentype fonts",
+ ttf="truetype fonts",
+ ttc="truetype fonts",
+ dfont="truetype fonts",
+ cid="cid maps",
+ fea="font feature files",
+}
+function resolvers.find_file(name,kind)
+ name=string.gsub(name,"\\","/")
+ kind=string.lower(kind)
+ return kpse.find_file(name,(kind and kind~="" and (remapper[kind] or kind)) or file.extname(name,"tex"))
+end
+function resolvers.findbinfile(name,kind)
+ if not kind or kind=="" then
+ kind=file.extname(name)
+ end
+ return resolvers.find_file(name,(kind and remapper[kind]) or kind)
+end
+caches={}
+local writable,readables=nil,{}
+if not caches.namespace or caches.namespace=="" or caches.namespace=="context" then
+ caches.namespace='generic'
+end
+do
+ local cachepaths
+ if kpse.expand_var('$TEXMFCACHE')~='$TEXMFCACHE' then
+ cachepaths=kpse.expand_var('$TEXMFCACHE')
+ elseif kpse.expand_var('$TEXMFVAR')~='$TEXMFVAR' then
+ cachepaths=kpse.expand_var('$TEXMFVAR')
+ end
+ if not cachepaths then
+ cachepaths="."
+ end
+ cachepaths=string.split(cachepaths,os.type=="windows" and ";" or ":")
+ for i=1,#cachepaths do
+ local done
+ writable=file.join(cachepaths[i],"luatex-cache")
+ writable=file.join(writable,caches.namespace)
+ writable,done=dir.mkdirs(writable)
+ if done then
+ break
+ end
+ end
+ for i=1,#cachepaths do
+ if file.isreadable(cachepaths[i]) then
+ readables[#readables+1]=file.join(cachepaths[i],"luatex-cache",caches.namespace)
+ end
+ end
+ if not writable then
+ texio.write_nl("quiting: fix your writable cache path\n")
+ os.exit()
+ elseif #readables==0 then
+ texio.write_nl("quiting: fix your readable cache path\n")
+ os.exit()
+ elseif #readables==1 and readables[1]==writable then
+ texio.write(string.format("(using cache: %s)",writable))
+ else
+ texio.write(string.format("(using write cache: %s)",writable))
+ texio.write(string.format("(using read cache: %s)",table.concat(readables," ")))
+ end
+end
+function caches.getwritablepath(category,subcategory)
+ local path=file.join(writable,category)
+ lfs.mkdir(path)
+ path=file.join(path,subcategory)
+ lfs.mkdir(path)
+ return path
+end
+function caches.getreadablepaths(category,subcategory)
+ local t={}
+ for i=1,#readables do
+ t[i]=file.join(readables[i],category,subcategory)
+ end
+ return t
+end
+local function makefullname(path,name)
+ if path and path~="" then
+ name="temp-"..name
+ return file.addsuffix(file.join(path,name),"lua")
+ end
+end
+function caches.iswritable(path,name)
+ local fullname=makefullname(path,name)
+ return fullname and file.iswritable(fullname)
+end
+function caches.loaddata(paths,name)
+ for i=1,#paths do
+ local fullname=makefullname(paths[i],name)
+ if fullname then
+ texio.write(string.format("(load: %s)",fullname))
+ local data=loadfile(fullname)
+ return data and data()
+ end
+ end
+end
+function caches.savedata(path,name,data)
+ local fullname=makefullname(path,name)
+ if fullname then
+ texio.write(string.format("(save: %s)",fullname))
+ table.tofile(fullname,data,'return',false,true,false)
+ end
+end
+
+end -- closure
+
+do -- begin closure to overcome local limits and interference
+
+if not modules then modules={} end modules ['luat-ovr']={
+ version=1.001,
+ comment="companion to luatex-*.tex",
+ author="Khaled Hosny and Elie Roux",
+ copyright="Luaotfload Development Team",
+ license="GNU GPL v2"
+}
+local write_nl,format,name=texio.write_nl,string.format,"luaotfload"
+local dummyfunction=function() end
+callbacks={
+ register=dummyfunction,
+}
+function logs.report(category,fmt,...)
+ if fmt then
+ write_nl('log',format("%s | %s: %s",name,category,format(fmt,...)))
+ elseif category then
+ write_nl('log',format("%s | %s",name,category))
+ else
+ write_nl('log',format("%s |",name))
+ end
+end
+function logs.info(category,fmt,...)
+ if fmt then
+ write_nl(format("%s | %s: %s",name,category,format(fmt,...)))
+ elseif category then
+ write_nl(format("%s | %s",name,category))
+ else
+ write_nl(format("%s |",name))
+ end
+ io.flush()
+end
+function logs.simple(fmt,...)
+ if fmt then
+ write_nl('log',format("%s | %s",name,format(fmt,...)))
+ else
+ write_nl('log',format("%s |",name))
+ end
+end
+tex.attribute[0]=0
+tex.ctxcatcodes=luatexbase.catcodetables.latex
+
+end -- closure
+
+do -- begin closure to overcome local limits and interference
+
+if not modules then modules={} end modules ['data-con']={
+ version=1.100,
+ comment="companion to luat-lib.mkiv",
+ author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright="PRAGMA ADE / ConTeXt Development Team",
+ license="see context related readme files"
+}
+local format,lower,gsub=string.format,string.lower,string.gsub
+local trace_cache=false trackers.register("resolvers.cache",function(v) trace_cache=v end)
+local trace_containers=false trackers.register("resolvers.containers",function(v) trace_containers=v end)
+local trace_storage=false trackers.register("resolvers.storage",function(v) trace_storage=v end)
+containers=containers or {}
+containers.usecache=true
+local function report(container,tag,name)
+ if trace_cache or trace_containers then
+ logs.report(format("%s cache",container.subcategory),"%s: %s",tag,name or 'invalid')
+ end
+end
+local allocated={}
+local mt={
+ __index=function(t,k)
+ if k=="writable" then
+ local writable=caches.getwritablepath(t.category,t.subcategory) or { "." }
+ t.writable=writable
+ return writable
+ elseif k=="readables" then
+ local readables=caches.getreadablepaths(t.category,t.subcategory) or { "." }
+ t.readables=readables
+ return readables
+ end
+ end
+}
+function containers.define(category,subcategory,version,enabled)
+ if category and subcategory then
+ local c=allocated[category]
+ if not c then
+ c={}
+ allocated[category]=c
+ end
+ local s=c[subcategory]
+ if not s then
+ s={
+ category=category,
+ subcategory=subcategory,
+ storage={},
+ enabled=enabled,
+ version=version or math.pi,
+ trace=false,
+ }
+ setmetatable(s,mt)
+ c[subcategory]=s
+ end
+ return s
+ end
+end
+function containers.is_usable(container,name)
+ return container.enabled and caches and caches.iswritable(container.writable,name)
+end
+function containers.is_valid(container,name)
+ if name and name~="" then
+ local storage=container.storage[name]
+ return storage and storage.cache_version==container.version
+ else
+ return false
+ end
+end
+function containers.read(container,name)
+ local storage=container.storage
+ local stored=storage[name]
+ if not stored and container.enabled and caches and containers.usecache then
+ stored=caches.loaddata(container.readables,name)
+ if stored and stored.cache_version==container.version then
+ report(container,"loaded",name)
+ else
+ stored=nil
+ end
+ storage[name]=stored
+ elseif stored then
+ report(container,"reusing",name)
+ end
+ return stored
+end
+function containers.write(container,name,data)
+ if data then
+ data.cache_version=container.version
+ if container.enabled and caches then
+ local unique,shared=data.unique,data.shared
+ data.unique,data.shared=nil,nil
+ caches.savedata(container.writable,name,data)
+ report(container,"saved",name)
+ data.unique,data.shared=unique,shared
+ end
+ report(container,"stored",name)
+ container.storage[name]=data
+ end
+ return data
+end
+function containers.content(container,name)
+ return container.storage[name]
+end
+function containers.cleanname(name)
+ return (gsub(lower(name),"[^%w%d]+","-"))
+end
+
+end -- closure
+
+do -- begin closure to overcome local limits and interference
+
+if not modules then modules={} end modules ['font-ini']={
+ 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 utf=unicode.utf8
+local format,serialize=string.format,table.serialize
+local write_nl=texio.write_nl
+local lower=string.lower
+if not fontloader then fontloader=fontforge end
+fontloader.totable=fontloader.to_table
+fonts=fonts or {}
+fonts.ids=fonts.ids or {} fonts.identifiers=fonts.ids
+fonts.chr=fonts.chr or {} fonts.characters=fonts.chr
+fonts.qua=fonts.qua or {} fonts.quads=fonts.qua
+fonts.tfm=fonts.tfm or {}
+fonts.mode='base'
+fonts.private=0xF0000
+fonts.verbose=false
+fonts.ids[0]={
+ characters={},
+ descriptions={},
+ name="nullfont",
+}
+fonts.chr[0]={}
+fonts.methods=fonts.methods or {
+ base={ tfm={},afm={},otf={},vtf={},fix={} },
+ node={ tfm={},afm={},otf={},vtf={},fix={} },
+}
+fonts.initializers=fonts.initializers or {
+ base={ tfm={},afm={},otf={},vtf={},fix={} },
+ node={ tfm={},afm={},otf={},vtf={},fix={} }
+}
+fonts.triggers=fonts.triggers or {
+ 'mode',
+ 'language',
+ 'script',
+ 'strategy',
+}
+fonts.processors=fonts.processors or {}
+fonts.manipulators=fonts.manipulators or {}
+fonts.define=fonts.define or {}
+fonts.define.specify=fonts.define.specify or {}
+fonts.define.specify.synonyms=fonts.define.specify.synonyms or {}
+if not fonts.color then
+ fonts.color={
+ set=function() end,
+ reset=function() end,
+ }
+end
+fonts.formats={}
+function fonts.fontformat(filename,default)
+ local extname=lower(file.extname(filename))
+ local format=fonts.formats[extname]
+ if format then
+ return format
+ else
+ logs.report("fonts define","unable to determine font format for '%s'",filename)
+ return default
+ end
+end
+
+end -- closure
+
+do -- begin closure to overcome local limits and interference
+
+if not modules then modules={} end modules ['node-dum']={
+ version=1.001,
+ comment="companion to luatex-*.tex",
+ author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright="PRAGMA ADE / ConTeXt Development Team",
+ license="see context related readme files"
+}
+nodes=nodes or {}
+fonts=fonts or {}
+attributes=attributes or {}
+local traverse_id=node.traverse_id
+local free_node=node.free
+local remove_node=node.remove
+local new_node=node.new
+local glyph=node.id('glyph')
+local fontdata=fonts.ids or {}
+function nodes.simple_font_handler(head)
+ head=nodes.process_characters(head)
+ nodes.inject_kerns(head)
+ nodes.protect_glyphs(head)
+ head=node.ligaturing(head)
+ head=node.kerning(head)
+ return head
+end
+if tex.attribute[0]~=0 then
+ texio.write_nl("log","!")
+ texio.write_nl("log","! Attribute 0 is reserved for ConTeXt's font feature management and has to be")
+ texio.write_nl("log","! set to zero. Also, some attributes in the range 1-255 are used for special")
+ texio.write_nl("log","! purposed so setting them at the TeX end might break the font handler.")
+ texio.write_nl("log","!")
+ tex.attribute[0]=0
+end
+nodes.protect_glyphs=node.protect_glyphs
+nodes.unprotect_glyphs=node.unprotect_glyphs
+function nodes.process_characters(head)
+ local usedfonts,done,prevfont={},false,nil
+ for n in traverse_id(glyph,head) do
+ local font=n.font
+ if font~=prevfont then
+ prevfont=font
+ local used=usedfonts[font]
+ if not used then
+ local tfmdata=fontdata[font]
+ if tfmdata then
+ local shared=tfmdata.shared
+ if shared then
+ local processors=shared.processes
+ if processors and #processors>0 then
+ usedfonts[font]=processors
+ done=true
+ end
+ end
+ end
+ end
+ end
+ end
+ if done then
+ for font,processors in next,usedfonts do
+ for i=1,#processors do
+ local h,d=processors[i](head,font,0)
+ head,done=h or head,done or d
+ end
+ end
+ end
+ return head,true
+end
+function nodes.kern(k)
+ local n=new_node("kern",1)
+ n.kern=k
+ return n
+end
+function nodes.remove(head,current,free_too)
+ local t=current
+ head,current=remove_node(head,current)
+ if t then
+ if free_too then
+ free_node(t)
+ t=nil
+ else
+ t.next,t.prev=nil,nil
+ end
+ end
+ return head,current,t
+end
+function nodes.delete(head,current)
+ return nodes.remove(head,current,true)
+end
+nodes.before=node.insert_before
+nodes.after=node.insert_after
+attributes.unsetvalue=-0x7FFFFFFF
+local numbers,last={},127
+function attributes.private(name)
+ local number=numbers[name]
+ if not number then
+ if last<255 then
+ last=last+1
+ end
+ number=last
+ numbers[name]=number
+ end
+ return number
+end
+
+end -- closure
+
+do -- begin closure to overcome local limits and interference
+
+if not modules then modules={} end modules ['node-inj']={
+ version=1.001,
+ comment="companion to node-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 trace_injections=false trackers.register("nodes.injections",function(v) trace_injections=v end)
+fonts=fonts or {}
+fonts.tfm=fonts.tfm or {}
+fonts.ids=fonts.ids or {}
+local fontdata=fonts.ids
+local glyph=node.id('glyph')
+local kern=node.id('kern')
+local traverse_id=node.traverse_id
+local unset_attribute=node.unset_attribute
+local has_attribute=node.has_attribute
+local set_attribute=node.set_attribute
+local insert_node_before=node.insert_before
+local insert_node_after=node.insert_after
+local newkern=nodes.kern
+local markbase=attributes.private('markbase')
+local markmark=attributes.private('markmark')
+local markdone=attributes.private('markdone')
+local cursbase=attributes.private('cursbase')
+local curscurs=attributes.private('curscurs')
+local cursdone=attributes.private('cursdone')
+local kernpair=attributes.private('kernpair')
+local cursives={}
+local marks={}
+local kerns={}
+function nodes.set_cursive(start,nxt,factor,rlmode,exit,entry,tfmstart,tfmnext)
+ local dx,dy=factor*(exit[1]-entry[1]),factor*(exit[2]-entry[2])
+ local ws,wn=tfmstart.width,tfmnext.width
+ local bound=#cursives+1
+ set_attribute(start,cursbase,bound)
+ set_attribute(nxt,curscurs,bound)
+ cursives[bound]={ rlmode,dx,dy,ws,wn }
+ return dx,dy,bound
+end
+function nodes.set_pair(current,factor,rlmode,r2lflag,spec,tfmchr)
+ local x,y,w,h=factor*spec[1],factor*spec[2],factor*spec[3],factor*spec[4]
+ if x~=0 or w~=0 or y~=0 or h~=0 then
+ local bound=has_attribute(current,kernpair)
+ if bound then
+ local kb=kerns[bound]
+ kb[2],kb[3],kb[4],kb[5]=(kb[2] or 0)+x,(kb[3] or 0)+y,(kb[4] or 0)+w,(kb[5] or 0)+h
+ else
+ bound=#kerns+1
+ set_attribute(current,kernpair,bound)
+ kerns[bound]={ rlmode,x,y,w,h,r2lflag,tfmchr.width }
+ end
+ return x,y,w,h,bound
+ end
+ return x,y,w,h
+end
+function nodes.set_kern(current,factor,rlmode,x,tfmchr)
+ local dx=factor*x
+ if dx~=0 then
+ local bound=#kerns+1
+ set_attribute(current,kernpair,bound)
+ kerns[bound]={ rlmode,dx }
+ return dx,bound
+ else
+ return 0,0
+ end
+end
+function nodes.set_mark(start,base,factor,rlmode,ba,ma,index)
+ local dx,dy=factor*(ba[1]-ma[1]),factor*(ba[2]-ma[2])
+ local bound=has_attribute(base,markbase)
+ if bound then
+ local mb=marks[bound]
+ if mb then
+ if not index then index=#mb+1 end
+ mb[index]={ dx,dy }
+ set_attribute(start,markmark,bound)
+ set_attribute(start,markdone,index)
+ return dx,dy,bound
+ else
+ logs.report("nodes mark","possible problem, U+%04X is base without data (id: %s)",base.char,bound)
+ end
+ end
+ index=index or 1
+ bound=#marks+1
+ set_attribute(base,markbase,bound)
+ set_attribute(start,markmark,bound)
+ set_attribute(start,markdone,index)
+ marks[bound]={ [index]={ dx,dy,rlmode } }
+ return dx,dy,bound
+end
+function nodes.trace_injection(head)
+ local function dir(n)
+ return (n and n<0 and "r-to-l") or (n and n>0 and "l-to-r") or ("unset")
+ end
+ local function report(...)
+ logs.report("nodes finisher",...)
+ end
+ report("begin run")
+ for n in traverse_id(glyph,head) do
+ if n.subtype<256 then
+ local kp=has_attribute(n,kernpair)
+ local mb=has_attribute(n,markbase)
+ local mm=has_attribute(n,markmark)
+ local md=has_attribute(n,markdone)
+ local cb=has_attribute(n,cursbase)
+ local cc=has_attribute(n,curscurs)
+ report("char U+%05X, font=%s",n.char,n.font)
+ if kp then
+ local k=kerns[kp]
+ if k[3] then
+ report(" pairkern: dir=%s, x=%s, y=%s, w=%s, h=%s",dir(k[1]),k[2] or "?",k[3] or "?",k[4] or "?",k[5] or "?")
+ else
+ report(" kern: dir=%s, dx=%s",dir(k[1]),k[2] or "?")
+ end
+ end
+ if mb then
+ report(" markbase: bound=%s",mb)
+ end
+ if mm then
+ local m=marks[mm]
+ if mb then
+ local m=m[mb]
+ if m then
+ report(" markmark: bound=%s, index=%s, dx=%s, dy=%s",mm,md or "?",m[1] or "?",m[2] or "?")
+ else
+ report(" markmark: bound=%s, missing index",mm)
+ end
+ else
+ m=m[1]
+ report(" markmark: bound=%s, dx=%s, dy=%s",mm,m[1] or "?",m[2] or "?")
+ end
+ end
+ if cb then
+ report(" cursbase: bound=%s",cb)
+ end
+ if cc then
+ local c=cursives[cc]
+ report(" curscurs: bound=%s, dir=%s, dx=%s, dy=%s",cc,dir(c[1]),c[2] or "?",c[3] or "?")
+ end
+ end
+ end
+ report("end run")
+end
+function nodes.inject_kerns(head,where,keep)
+ local has_marks,has_cursives,has_kerns=next(marks),next(cursives),next(kerns)
+ if has_marks or has_cursives then
+ if trace_injections then
+ nodes.trace_injection(head)
+ end
+ local done,ky,rl,valid,cx,wx,mk=false,{},{},{},{},{},{}
+ if has_kerns then
+ local nf,tm=nil,nil
+ for n in traverse_id(glyph,head) do
+ if n.subtype<256 then
+ valid[#valid+1]=n
+ if n.font~=nf then
+ nf=n.font
+ tm=fontdata[nf].marks
+ end
+ mk[n]=tm[n.char]
+ local k=has_attribute(n,kernpair)
+ if k then
+ local kk=kerns[k]
+ if kk then
+ local x,y,w,h=kk[2] or 0,kk[3] or 0,kk[4] or 0,kk[5] or 0
+ local dy=y-h
+ if dy~=0 then
+ ky[n]=dy
+ end
+ if w~=0 or x~=0 then
+ wx[n]=kk
+ end
+ rl[n]=kk[1]
+ end
+ end
+ end
+ end
+ else
+ local nf,tm=nil,nil
+ for n in traverse_id(glyph,head) do
+ if n.subtype<256 then
+ valid[#valid+1]=n
+ if n.font~=nf then
+ nf=n.font
+ tm=fontdata[nf].marks
+ end
+ mk[n]=tm[n.char]
+ end
+ end
+ end
+ if #valid>0 then
+ local cx={}
+ if has_kerns and next(ky) then
+ for n,k in next,ky do
+ n.yoffset=k
+ end
+ end
+ if has_cursives then
+ local p_cursbase,p=nil,nil
+ local t,d,maxt={},{},0
+ for i=1,#valid do
+ local n=valid[i]
+ if not mk[n] then
+ local n_cursbase=has_attribute(n,cursbase)
+ if p_cursbase then
+ local n_curscurs=has_attribute(n,curscurs)
+ if p_cursbase==n_curscurs then
+ local c=cursives[n_curscurs]
+ if c then
+ local rlmode,dx,dy,ws,wn=c[1],c[2],c[3],c[4],c[5]
+ if rlmode>=0 then
+ dx=dx-ws
+ else
+ dx=dx+wn
+ end
+ if dx~=0 then
+ cx[n]=dx
+ rl[n]=rlmode
+ end
+ dy=-dy
+ maxt=maxt+1
+ t[maxt]=p
+ d[maxt]=dy
+ else
+ maxt=0
+ end
+ end
+ elseif maxt>0 then
+ local ny=n.yoffset
+ for i=maxt,1,-1 do
+ ny=ny+d[i]
+ local ti=t[i]
+ ti.yoffset=ti.yoffset+ny
+ end
+ maxt=0
+ end
+ if not n_cursbase and maxt>0 then
+ local ny=n.yoffset
+ for i=maxt,1,-1 do
+ ny=ny+d[i]
+ local ti=t[i]
+ ti.yoffset=ny
+ end
+ maxt=0
+ end
+ p_cursbase,p=n_cursbase,n
+ end
+ end
+ if maxt>0 then
+ local ny=n.yoffset
+ for i=maxt,1,-1 do
+ ny=ny+d[i]
+ local ti=t[i]
+ ti.yoffset=ny
+ end
+ maxt=0
+ end
+ if not keep then
+ cursives={}
+ end
+ end
+ if has_marks then
+ for i=1,#valid do
+ local p=valid[i]
+ local p_markbase=has_attribute(p,markbase)
+ if p_markbase then
+ local mrks=marks[p_markbase]
+ for n in traverse_id(glyph,p.next) do
+ local n_markmark=has_attribute(n,markmark)
+ if p_markbase==n_markmark then
+ local index=has_attribute(n,markdone) or 1
+ local d=mrks[index]
+ if d then
+ local rlmode=d[3]
+ if rlmode and rlmode>0 then
+ local k=wx[p]
+ if k then
+ n.xoffset=p.xoffset-(p.width-d[1])-k[2]
+ else
+ n.xoffset=p.xoffset-(p.width-d[1])
+ end
+ else
+ local k=wx[p]
+ if k then
+ n.xoffset=p.xoffset-d[1]-k[2]
+ else
+ n.xoffset=p.xoffset-d[1]
+ end
+ end
+ if mk[p] then
+ n.yoffset=p.yoffset+d[2]
+ else
+ n.yoffset=n.yoffset+p.yoffset+d[2]
+ end
+ end
+ else
+ break
+ end
+ end
+ end
+ end
+ if not keep then
+ marks={}
+ end
+ end
+ if next(wx) then
+ for n,k in next,wx do
+ local rl,x,w,r2l=k[1],k[2] or 0,k[4] or 0,k[6]
+ local wx=w-x
+ if r2l then
+ if wx~=0 then
+ insert_node_before(head,n,newkern(wx))
+ end
+ if x~=0 then
+ insert_node_after (head,n,newkern(x))
+ end
+ else
+ if x~=0 then
+ insert_node_before(head,n,newkern(x))
+ end
+ if wx~=0 then
+ insert_node_after(head,n,newkern(wx))
+ end
+ end
+ end
+ end
+ if next(cx) then
+ for n,k in next,cx do
+ if k~=0 then
+ local rln=rl[n]
+ if rln and rln<0 then
+ insert_node_before(head,n,newkern(-k))
+ else
+ insert_node_before(head,n,newkern(k))
+ end
+ end
+ end
+ end
+ if not keep then
+ kerns={}
+ end
+ return head,true
+ elseif not keep then
+ kerns,cursives,marks={},{},{}
+ end
+ elseif has_kerns then
+ if trace_injections then
+ nodes.trace_injection(head)
+ end
+ for n in traverse_id(glyph,head) do
+ if n.subtype<256 then
+ local k=has_attribute(n,kernpair)
+ if k then
+ local kk=kerns[k]
+ if kk then
+ local rl,x,y,w=kk[1],kk[2] or 0,kk[3],kk[4]
+ if y and y~=0 then
+ n.yoffset=y
+ end
+ if w then
+ local r2l=kk[6]
+ local wx=w-x
+ if r2l then
+ if wx~=0 then
+ insert_node_before(head,n,newkern(wx))
+ end
+ if x~=0 then
+ insert_node_after (head,n,newkern(x))
+ end
+ else
+ if x~=0 then
+ insert_node_before(head,n,newkern(x))
+ end
+ if wx~=0 then
+ insert_node_after(head,n,newkern(wx))
+ end
+ end
+ else
+ if x~=0 then
+ insert_node_before(head,n,newkern(x))
+ end
+ end
+ end
+ end
+ end
+ end
+ if not keep then
+ kerns={}
+ end
+ return head,true
+ else
+ end
+ return head,false
+end
+
+end -- closure
+
+do -- begin closure to overcome local limits and interference
+
+
+if not modules then modules={} end modules ['otfl-luat-att']={
+ version=math.pi/42,
+ comment="companion to luaotfload.lua",
+ author="Philipp Gesang",
+ copyright="Luaotfload Development Team",
+ license="GNU GPL v2"
+}
+function attributes.private(name)
+ local attr="otfl@"..name
+ local number=luatexbase.attributes[attr]
+ if not number then
+ number=luatexbase.new_attribute(attr)
+ end
+ return number
+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 utf=unicode.utf8
+local next,format,match,lower,gsub=next,string.format,string.match,string.lower,string.gsub
+local concat,sortedkeys,utfbyte,serialize=table.concat,table.sortedkeys,utf.byte,table.serialize
+local trace_defining=false trackers.register("fonts.defining",function(v) trace_defining=v end)
+local trace_scaling=false trackers.register("fonts.scaling",function(v) trace_scaling=v end)
+fonts=fonts or {}
+fonts.tfm=fonts.tfm or {}
+fonts.ids=fonts.ids or {}
+local tfm=fonts.tfm
+fonts.loaded=fonts.loaded or {}
+fonts.dontembed=fonts.dontembed or {}
+fonts.triggers=fonts.triggers or {}
+fonts.initializers=fonts.initializers or {}
+fonts.initializers.common=fonts.initializers.common or {}
+local fontdata=fonts.ids
+local disc=node.id('disc')
+local glyph=node.id('glyph')
+local set_attribute=node.set_attribute
+tfm.resolve_vf=true
+tfm.share_base_kerns=false
+tfm.mathactions={}
+tfm.fontname_mode="fullpath"
+tfm.enhance=tfm.enhance or function() end
+fonts.formats.tfm="type1"
+function tfm.read_from_tfm(specification)
+ local fname,tfmdata=specification.filename or "",nil
+ if fname~="" then
+ if trace_defining then
+ logs.report("define font","loading tfm file %s at size %s",fname,specification.size)
+ end
+ tfmdata=font.read_tfm(fname,specification.size)
+ if tfmdata then
+ tfmdata.descriptions=tfmdata.descriptions or {}
+ if tfm.resolve_vf then
+ fonts.logger.save(tfmdata,file.extname(fname),specification)
+ fname=resolvers.findbinfile(specification.name,'ovf')
+ if fname and fname~="" then
+ local vfdata=font.read_vf(fname,specification.size)
+ if vfdata then
+ local chars=tfmdata.characters
+ for k,v in next,vfdata.characters do
+ chars[k].commands=v.commands
+ end
+ tfmdata.type='virtual'
+ tfmdata.fonts=vfdata.fonts
+ end
+ end
+ end
+ tfm.enhance(tfmdata,specification)
+ end
+ elseif trace_defining then
+ logs.report("define font","loading tfm with name %s fails",specification.name)
+ end
+ return tfmdata
+end
+local factors={
+ pt=65536.0,
+ bp=65781.8,
+}
+function tfm.setfactor(f)
+ tfm.factor=factors[f or 'pt'] or factors.pt
+end
+tfm.setfactor()
+function tfm.scaled(scaledpoints,designsize)
+ if scaledpoints<0 then
+ if designsize then
+ if designsize>tfm.factor then
+ return (- scaledpoints/1000)*designsize
+ else
+ return (- scaledpoints/1000)*designsize*tfm.factor
+ end
+ else
+ return (- scaledpoints/1000)*10*tfm.factor
+ end
+ else
+ return scaledpoints
+ end
+end
+function tfm.get_virtual_id(tfmdata)
+ if not tfmdata.fonts then
+ tfmdata.type="virtual"
+ tfmdata.fonts={ { id=0 } }
+ return 1
+ else
+ tfmdata.fonts[#tfmdata.fonts+1]={ id=0 }
+ return #tfmdata.fonts
+ end
+end
+function tfm.check_virtual_id(tfmdata,id)
+ if tfmdata and tfmdata.type=="virtual" then
+ if not tfmdata.fonts or #tfmdata.fonts==0 then
+ tfmdata.type,tfmdata.fonts="real",nil
+ else
+ local vfonts=tfmdata.fonts
+ for f=1,#vfonts do
+ local fnt=vfonts[f]
+ if fnt.id and fnt.id==0 then
+ fnt.id=id
+ end
+ end
+ end
+ end
+end
+fonts.trace_scaling=false
+local charactercache={}
+function tfm.calculate_scale(tfmtable,scaledpoints,relativeid)
+ if scaledpoints<0 then
+ scaledpoints=(- scaledpoints/1000)*tfmtable.designsize
+ end
+ local units=tfmtable.units or 1000
+ local delta=scaledpoints/units
+ return scaledpoints,delta,units
+end
+function tfm.do_scale(tfmtable,scaledpoints,relativeid)
+ local t={}
+ local scaledpoints,delta,units=tfm.calculate_scale(tfmtable,scaledpoints,relativeid)
+ t.units_per_em=units or 1000
+ local hdelta,vdelta=delta,delta
+ for k,v in next,tfmtable do
+ if type(v)=="table" then
+ else
+ t[k]=v
+ end
+ end
+ local extend_factor=tfmtable.extend_factor or 0
+ if extend_factor~=0 and extend_factor~=1 then
+ hdelta=hdelta*extend_factor
+ t.extend=extend_factor*1000
+ else
+ t.extend=1000
+ end
+ local slant_factor=tfmtable.slant_factor or 0
+ if slant_factor~=0 then
+ t.slant=slant_factor*1000
+ else
+ t.slant=0
+ end
+ local isvirtual=tfmtable.type=="virtual" or tfmtable.virtualized
+ local hasmath=(tfmtable.math_parameters~=nil and next(tfmtable.math_parameters)~=nil) or (tfmtable.MathConstants~=nil and next(tfmtable.MathConstants)~=nil)
+ local nodemode=tfmtable.mode=="node"
+ local hasquality=tfmtable.auto_expand or tfmtable.auto_protrude
+ local hasitalic=tfmtable.has_italic
+ t.parameters={}
+ t.characters={}
+ t.MathConstants={}
+ local descriptions=tfmtable.descriptions or {}
+ t.unicodes=tfmtable.unicodes
+ t.indices=tfmtable.indices
+ t.marks=tfmtable.marks
+t.goodies=tfmtable.goodies
+t.colorscheme=tfmtable.colorscheme
+ t.descriptions=descriptions
+ if tfmtable.fonts then
+ t.fonts=table.fastcopy(tfmtable.fonts)
+ end
+ local tp=t.parameters
+ local mp=t.math_parameters
+ local tfmp=tfmtable.parameters
+ tp.slant=(tfmp.slant or tfmp[1] or 0)
+ tp.space=(tfmp.space or tfmp[2] or 0)*hdelta
+ tp.space_stretch=(tfmp.space_stretch or tfmp[3] or 0)*hdelta
+ tp.space_shrink=(tfmp.space_shrink or tfmp[4] or 0)*hdelta
+ tp.x_height=(tfmp.x_height or tfmp[5] or 0)*vdelta
+ tp.quad=(tfmp.quad or tfmp[6] or 0)*hdelta
+ tp.extra_space=(tfmp.extra_space or tfmp[7] or 0)*hdelta
+ local protrusionfactor=(tp.quad~=0 and 1000/tp.quad) or 0
+ local tc=t.characters
+ local characters=tfmtable.characters
+ local nameneeded=not tfmtable.shared.otfdata
+ local changed=tfmtable.changed or {}
+ local ischanged=changed and next(changed)
+ local indices=tfmtable.indices
+ local luatex=tfmtable.luatex
+ local tounicode=luatex and luatex.tounicode
+ local defaultwidth=luatex and luatex.defaultwidth or 0
+ local defaultheight=luatex and luatex.defaultheight or 0
+ local defaultdepth=luatex and luatex.defaultdepth or 0
+ local scaledwidth=defaultwidth*hdelta
+ local scaledheight=defaultheight*vdelta
+ local scaleddepth=defaultdepth*vdelta
+ local stackmath=tfmtable.ignore_stack_math~=true
+ local private=fonts.private
+ local sharedkerns={}
+ for k,v in next,characters do
+ local chr,description,index
+ if ischanged then
+ local c=changed[k]
+ if c then
+ description=descriptions[c] or v
+ v=characters[c] or v
+ index=(indices and indices[c]) or c
+ else
+ description=descriptions[k] or v
+ index=(indices and indices[k]) or k
+ end
+ else
+ description=descriptions[k] or v
+ index=(indices and indices[k]) or k
+ end
+ local width=description.width
+ local height=description.height
+ local depth=description.depth
+ if width then width=hdelta*width else width=scaledwidth end
+ if height then height=vdelta*height else height=scaledheight end
+ if depth and depth~=0 then
+ depth=delta*depth
+ if nameneeded then
+ chr={
+ name=description.name,
+ index=index,
+ height=height,
+ depth=depth,
+ width=width,
+ }
+ else
+ chr={
+ index=index,
+ height=height,
+ depth=depth,
+ width=width,
+ }
+ end
+ else
+ if nameneeded then
+ chr={
+ name=description.name,
+ index=index,
+ height=height,
+ width=width,
+ }
+ else
+ chr={
+ index=index,
+ height=height,
+ width=width,
+ }
+ end
+ end
+ if tounicode then
+ local tu=tounicode[index]
+ if tu then
+ chr.tounicode=tu
+ end
+ end
+ if hasquality then
+ local ve=v.expansion_factor
+ if ve then
+ chr.expansion_factor=ve*1000
+ end
+ local vl=v.left_protruding
+ if vl then
+ chr.left_protruding=protrusionfactor*width*vl
+ end
+ local vr=v.right_protruding
+ if vr then
+ chr.right_protruding=protrusionfactor*width*vr
+ end
+ end
+ if hasitalic then
+ local vi=description.italic or v.italic
+ if vi and vi~=0 then
+ chr.italic=vi*hdelta
+ end
+ end
+ if hasmath then
+ local vn=v.next
+ if vn then
+ chr.next=vn
+ else
+ local vv=v.vert_variants
+ if vv then
+ local t={}
+ for i=1,#vv do
+ local vvi=vv[i]
+ t[i]={
+ ["start"]=(vvi["start"] or 0)*vdelta,
+ ["end"]=(vvi["end"] or 0)*vdelta,
+ ["advance"]=(vvi["advance"] or 0)*vdelta,
+ ["extender"]=vvi["extender"],
+ ["glyph"]=vvi["glyph"],
+ }
+ end
+ chr.vert_variants=t
+ else
+ local hv=v.horiz_variants
+ if hv then
+ local t={}
+ for i=1,#hv do
+ local hvi=hv[i]
+ t[i]={
+ ["start"]=(hvi["start"] or 0)*hdelta,
+ ["end"]=(hvi["end"] or 0)*hdelta,
+ ["advance"]=(hvi["advance"] or 0)*hdelta,
+ ["extender"]=hvi["extender"],
+ ["glyph"]=hvi["glyph"],
+ }
+ end
+ chr.horiz_variants=t
+ end
+ end
+ end
+ local vt=description.top_accent
+ if vt then
+ chr.top_accent=vdelta*vt
+ end
+ if stackmath then
+ local mk=v.mathkerns
+ if mk then
+ local kerns={}
+ local v=mk.top_right if v then local k={} for i=1,#v do local vi=v[i]
+ k[i]={ height=vdelta*vi.height,kern=vdelta*vi.kern }
+ end kerns.top_right=k end
+ local v=mk.top_left if v then local k={} for i=1,#v do local vi=v[i]
+ k[i]={ height=vdelta*vi.height,kern=vdelta*vi.kern }
+ end kerns.top_left=k end
+ local v=mk.bottom_left if v then local k={} for i=1,#v do local vi=v[i]
+ k[i]={ height=vdelta*vi.height,kern=vdelta*vi.kern }
+ end kerns.bottom_left=k end
+ local v=mk.bottom_right if v then local k={} for i=1,#v do local vi=v[i]
+ k[i]={ height=vdelta*vi.height,kern=vdelta*vi.kern }
+ end kerns.bottom_right=k end
+ chr.mathkern=kerns
+ end
+ end
+ end
+ if not nodemode then
+ local vk=v.kerns
+ if vk then
+ local s=sharedkerns[vk]
+ if not s then
+ s={}
+ for k,v in next,vk do s[k]=v*hdelta end
+ sharedkerns[vk]=s
+ end
+ chr.kerns=s
+ end
+ local vl=v.ligatures
+ if vl then
+ if true then
+ chr.ligatures=vl
+ else
+ local tt={}
+ for i,l in next,vl do
+ tt[i]=l
+ end
+ chr.ligatures=tt
+ end
+ end
+ end
+ if isvirtual then
+ local vc=v.commands
+ if vc then
+ local ok=false
+ for i=1,#vc do
+ local key=vc[i][1]
+ if key=="right" or key=="down" then
+ ok=true
+ break
+ end
+ end
+ if ok then
+ local tt={}
+ for i=1,#vc do
+ local ivc=vc[i]
+ local key=ivc[1]
+ if key=="right" then
+ tt[#tt+1]={ key,ivc[2]*hdelta }
+ elseif key=="down" then
+ tt[#tt+1]={ key,ivc[2]*vdelta }
+ elseif key=="rule" then
+ tt[#tt+1]={ key,ivc[2]*vdelta,ivc[3]*hdelta }
+ else
+ tt[#tt+1]=ivc
+ end
+ end
+ chr.commands=tt
+ else
+ chr.commands=vc
+ end
+ end
+ end
+ tc[k]=chr
+ end
+ t.size=scaledpoints
+ t.factor=delta
+ t.hfactor=hdelta
+ t.vfactor=vdelta
+ if t.fonts then
+ t.fonts=table.fastcopy(t.fonts)
+ end
+ if hasmath then
+ local ma=tfm.mathactions
+ for i=1,#ma do
+ ma[i](t,tfmtable,delta,hdelta,vdelta)
+ end
+ end
+ local tpx=tp.x_height
+ if hasmath then
+ if not tp[13] then tp[13]=.86*tpx end
+ if not tp[14] then tp[14]=.86*tpx end
+ if not tp[15] then tp[15]=.86*tpx end
+ if not tp[16] then tp[16]=.48*tpx end
+ if not tp[17] then tp[17]=.48*tpx end
+ if not tp[22] then tp[22]=0 end
+ if t.MathConstants then t.MathConstants.AccentBaseHeight=nil end
+ end
+ t.tounicode=1
+ t.cidinfo=tfmtable.cidinfo
+ if hasmath then
+ if trace_defining then
+ logs.report("define font","math enabled for: name '%s', fullname: '%s', filename: '%s'",t.name or "noname",t.fullname or "nofullname",t.filename or "nofilename")
+ end
+ else
+ if trace_defining then
+ logs.report("define font","math disabled for: name '%s', fullname: '%s', filename: '%s'",t.name or "noname",t.fullname or "nofullname",t.filename or "nofilename")
+ end
+ t.nomath,t.MathConstants=true,nil
+ end
+ if not t.psname then
+ t.psname=t.fontname or (t.fullname and fonts.names.cleanname(t.fullname))
+ end
+ if trace_defining then
+ logs.report("define font","used for accesing subfont: '%s'",t.psname or "nopsname")
+ logs.report("define font","used for subsetting: '%s'",t.fontname or "nofontname")
+ end
+ return t,delta
+end
+tfm.auto_cleanup=true
+local lastfont=nil
+function tfm.cleanup_table(tfmdata)
+ if tfm.auto_cleanup then
+ if tfmdata.type=='virtual' or tfmdata.virtualized then
+ for k,v in next,tfmdata.characters do
+ if v.commands then v.commands=nil end
+ end
+ else
+ end
+ end
+end
+function tfm.cleanup(tfmdata)
+end
+function tfm.scale(tfmtable,scaledpoints,relativeid)
+ local t,factor=tfm.do_scale(tfmtable,scaledpoints,relativeid)
+ t.factor=factor
+ t.ascender=factor*(tfmtable.ascender or 0)
+ t.descender=factor*(tfmtable.descender or 0)
+ t.shared=tfmtable.shared or {}
+ t.unique=table.fastcopy(tfmtable.unique or {})
+ tfm.cleanup(t)
+ return t
+end
+fonts.analyzers=fonts.analyzers or {}
+fonts.analyzers.aux=fonts.analyzers.aux or {}
+fonts.analyzers.methods=fonts.analyzers.methods or {}
+fonts.analyzers.initializers=fonts.analyzers.initializers or {}
+local state=attributes.private('state')
+function fonts.analyzers.aux.setstate(head,font)
+ local tfmdata=fontdata[font]
+ local characters=tfmdata.characters
+ local descriptions=tfmdata.descriptions
+ local first,last,current,n,done=nil,nil,head,0,false
+ while current do
+ local id=current.id
+ if id==glyph and current.font==font then
+ local d=descriptions[current.char]
+ if d then
+ if d.class=="mark" then
+ done=true
+ set_attribute(current,state,5)
+ elseif n==0 then
+ first,last,n=current,current,1
+ set_attribute(current,state,1)
+ else
+ last,n=current,n+1
+ set_attribute(current,state,2)
+ end
+ else
+ if first and first==last then
+ set_attribute(last,state,4)
+ elseif last then
+ set_attribute(last,state,3)
+ end
+ first,last,n=nil,nil,0
+ end
+ elseif id==disc then
+ set_attribute(current,state,2)
+ last=current
+ else
+ if first and first==last then
+ set_attribute(last,state,4)
+ elseif last then
+ set_attribute(last,state,3)
+ end
+ first,last,n=nil,nil,0
+ end
+ current=current.next
+ end
+ if first and first==last then
+ set_attribute(last,state,4)
+ elseif last then
+ set_attribute(last,state,3)
+ end
+ return head,done
+end
+function tfm.replacements(tfm,value)
+ tfm.characters[0x0027]=tfm.characters[0x2019]
+end
+function tfm.checked_filename(metadata,whatever)
+ local foundfilename=metadata.foundfilename
+ if not foundfilename then
+ local askedfilename=metadata.filename or ""
+ if askedfilename~="" then
+ foundfilename=resolvers.findbinfile(askedfilename,"") or ""
+ if foundfilename=="" then
+ logs.report("fonts","source file '%s' is not found",askedfilename)
+ foundfilename=resolvers.findbinfile(file.basename(askedfilename),"") or ""
+ if foundfilename~="" then
+ logs.report("fonts","using source file '%s' (cache mismatch)",foundfilename)
+ end
+ end
+ elseif whatever then
+ logs.report("fonts","no source file for '%s'",whatever)
+ foundfilename=""
+ end
+ metadata.foundfilename=foundfilename
+ end
+ return foundfilename
+end
+statistics.register("fonts load time",function()
+ return statistics.elapsedseconds(fonts)
+end)
+
+end -- closure
+
+do -- begin closure to overcome local limits and interference
+
+if not modules then modules={} end modules ['font-cid']={
+ version=1.001,
+ comment="companion to font-otf.lua (cidmaps)",
+ author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright="PRAGMA ADE / ConTeXt Development Team",
+ license="see context related readme files"
+}
+local format,match,lower=string.format,string.match,string.lower
+local tonumber=tonumber
+local lpegmatch=lpeg.match
+local trace_loading=false trackers.register("otf.loading",function(v) trace_loading=v end)
+fonts=fonts or {}
+fonts.cid=fonts.cid or {}
+fonts.cid.map=fonts.cid.map or {}
+fonts.cid.max=fonts.cid.max or 10
+local number=lpeg.C(lpeg.R("09","af","AF")^1)
+local space=lpeg.S(" \n\r\t")
+local spaces=space^0
+local period=lpeg.P(".")
+local periods=period*period
+local name=lpeg.P("/")*lpeg.C((1-space)^1)
+local unicodes,names={},{}
+local function do_one(a,b)
+ unicodes[tonumber(a)]=tonumber(b,16)
+end
+local function do_range(a,b,c)
+ c=tonumber(c,16)
+ for i=tonumber(a),tonumber(b) do
+ unicodes[i]=c
+ c=c+1
+ end
+end
+local function do_name(a,b)
+ names[tonumber(a)]=b
+end
+local grammar=lpeg.P { "start",
+ start=number*spaces*number*lpeg.V("series"),
+ series=(spaces*(lpeg.V("one")+lpeg.V("range")+lpeg.V("named")) )^1,
+ one=(number*spaces*number)/do_one,
+ range=(number*periods*number*spaces*number)/do_range,
+ named=(number*spaces*name)/do_name
+}
+function fonts.cid.load(filename)
+ local data=io.loaddata(filename)
+ if data then
+ unicodes,names={},{}
+ lpegmatch(grammar,data)
+ local supplement,registry,ordering=match(filename,"^(.-)%-(.-)%-()%.(.-)$")
+ return {
+ supplement=supplement,
+ registry=registry,
+ ordering=ordering,
+ filename=filename,
+ unicodes=unicodes,
+ names=names
+ }
+ else
+ return nil
+ end
+end
+local template="%s-%s-%s.cidmap"
+local function locate(registry,ordering,supplement)
+ local filename=format(template,registry,ordering,supplement)
+ local hashname=lower(filename)
+ local cidmap=fonts.cid.map[hashname]
+ if not cidmap then
+ if trace_loading then
+ logs.report("load otf","checking cidmap, registry: %s, ordering: %s, supplement: %s, filename: %s",registry,ordering,supplement,filename)
+ end
+ local fullname=resolvers.find_file(filename,'cid') or ""
+ if fullname~="" then
+ cidmap=fonts.cid.load(fullname)
+ if cidmap then
+ if trace_loading then
+ logs.report("load otf","using cidmap file %s",filename)
+ end
+ fonts.cid.map[hashname]=cidmap
+ cidmap.usedname=file.basename(filename)
+ return cidmap
+ end
+ end
+ end
+ return cidmap
+end
+function fonts.cid.getmap(registry,ordering,supplement)
+ local supplement=tonumber(supplement)
+ if trace_loading then
+ logs.report("load otf","needed cidmap, registry: %s, ordering: %s, supplement: %s",registry,ordering,supplement)
+ end
+ local cidmap=locate(registry,ordering,supplement)
+ if not cidmap then
+ local cidnum=nil
+ if supplement<fonts.cid.max then
+ for supplement=supplement+1,fonts.cid.max do
+ local c=locate(registry,ordering,supplement)
+ if c then
+ cidmap,cidnum=c,supplement
+ break
+ end
+ end
+ end
+ if not cidmap and supplement>0 then
+ for supplement=supplement-1,0,-1 do
+ local c=locate(registry,ordering,supplement)
+ if c then
+ cidmap,cidnum=c,supplement
+ break
+ end
+ end
+ end
+ if cidmap and cidnum>0 then
+ for s=0,cidnum-1 do
+ filename=format(template,registry,ordering,s)
+ if not fonts.cid.map[filename] then
+ fonts.cid.map[filename]=cidmap
+ end
+ end
+ end
+ end
+ return cidmap
+end
+
+end -- closure
+
+do -- begin closure to overcome local limits and interference
+
+if not modules then modules={} end modules ['font-otf']={
+ version=1.001,
+ comment="companion to font-otf.lua (tables)",
+ author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright="PRAGMA ADE / ConTeXt Development Team",
+ license="see context related readme files"
+}
+local type,next,tonumber,tostring=type,next,tonumber,tostring
+local gsub,lower=string.gsub,string.lower
+fonts=fonts or {}
+fonts.otf=fonts.otf or {}
+local otf=fonts.otf
+otf.tables=otf.tables or {}
+otf.meanings=otf.meanings or {}
+otf.tables.scripts={
+ ['dflt']='Default',
+ ['arab']='Arabic',
+ ['armn']='Armenian',
+ ['bali']='Balinese',
+ ['beng']='Bengali',
+ ['bopo']='Bopomofo',
+ ['brai']='Braille',
+ ['bugi']='Buginese',
+ ['buhd']='Buhid',
+ ['byzm']='Byzantine Music',
+ ['cans']='Canadian Syllabics',
+ ['cher']='Cherokee',
+ ['copt']='Coptic',
+ ['cprt']='Cypriot Syllabary',
+ ['cyrl']='Cyrillic',
+ ['deva']='Devanagari',
+ ['dsrt']='Deseret',
+ ['ethi']='Ethiopic',
+ ['geor']='Georgian',
+ ['glag']='Glagolitic',
+ ['goth']='Gothic',
+ ['grek']='Greek',
+ ['gujr']='Gujarati',
+ ['guru']='Gurmukhi',
+ ['hang']='Hangul',
+ ['hani']='CJK Ideographic',
+ ['hano']='Hanunoo',
+ ['hebr']='Hebrew',
+ ['ital']='Old Italic',
+ ['jamo']='Hangul Jamo',
+ ['java']='Javanese',
+ ['kana']='Hiragana and Katakana',
+ ['khar']='Kharosthi',
+ ['khmr']='Khmer',
+ ['knda']='Kannada',
+ ['lao' ]='Lao',
+ ['latn']='Latin',
+ ['limb']='Limbu',
+ ['linb']='Linear B',
+ ['math']='Mathematical Alphanumeric Symbols',
+ ['mlym']='Malayalam',
+ ['mong']='Mongolian',
+ ['musc']='Musical Symbols',
+ ['mymr']='Myanmar',
+ ['nko' ]="N'ko",
+ ['ogam']='Ogham',
+ ['orya']='Oriya',
+ ['osma']='Osmanya',
+ ['phag']='Phags-pa',
+ ['phnx']='Phoenician',
+ ['runr']='Runic',
+ ['shaw']='Shavian',
+ ['sinh']='Sinhala',
+ ['sylo']='Syloti Nagri',
+ ['syrc']='Syriac',
+ ['tagb']='Tagbanwa',
+ ['tale']='Tai Le',
+ ['talu']='Tai Lu',
+ ['taml']='Tamil',
+ ['telu']='Telugu',
+ ['tfng']='Tifinagh',
+ ['tglg']='Tagalog',
+ ['thaa']='Thaana',
+ ['thai']='Thai',
+ ['tibt']='Tibetan',
+ ['ugar']='Ugaritic Cuneiform',
+ ['xpeo']='Old Persian Cuneiform',
+ ['xsux']='Sumero-Akkadian Cuneiform',
+ ['yi' ]='Yi',
+}
+otf.tables.languages={
+ ['dflt']='Default',
+ ['aba']='Abaza',
+ ['abk']='Abkhazian',
+ ['ady']='Adyghe',
+ ['afk']='Afrikaans',
+ ['afr']='Afar',
+ ['agw']='Agaw',
+ ['als']='Alsatian',
+ ['alt']='Altai',
+ ['amh']='Amharic',
+ ['ara']='Arabic',
+ ['ari']='Aari',
+ ['ark']='Arakanese',
+ ['asm']='Assamese',
+ ['ath']='Athapaskan',
+ ['avr']='Avar',
+ ['awa']='Awadhi',
+ ['aym']='Aymara',
+ ['aze']='Azeri',
+ ['bad']='Badaga',
+ ['bag']='Baghelkhandi',
+ ['bal']='Balkar',
+ ['bau']='Baule',
+ ['bbr']='Berber',
+ ['bch']='Bench',
+ ['bcr']='Bible Cree',
+ ['bel']='Belarussian',
+ ['bem']='Bemba',
+ ['ben']='Bengali',
+ ['bgr']='Bulgarian',
+ ['bhi']='Bhili',
+ ['bho']='Bhojpuri',
+ ['bik']='Bikol',
+ ['bil']='Bilen',
+ ['bkf']='Blackfoot',
+ ['bli']='Balochi',
+ ['bln']='Balante',
+ ['blt']='Balti',
+ ['bmb']='Bambara',
+ ['bml']='Bamileke',
+ ['bos']='Bosnian',
+ ['bre']='Breton',
+ ['brh']='Brahui',
+ ['bri']='Braj Bhasha',
+ ['brm']='Burmese',
+ ['bsh']='Bashkir',
+ ['bti']='Beti',
+ ['cat']='Catalan',
+ ['ceb']='Cebuano',
+ ['che']='Chechen',
+ ['chg']='Chaha Gurage',
+ ['chh']='Chattisgarhi',
+ ['chi']='Chichewa',
+ ['chk']='Chukchi',
+ ['chp']='Chipewyan',
+ ['chr']='Cherokee',
+ ['chu']='Chuvash',
+ ['cmr']='Comorian',
+ ['cop']='Coptic',
+ ['cos']='Corsican',
+ ['cre']='Cree',
+ ['crr']='Carrier',
+ ['crt']='Crimean Tatar',
+ ['csl']='Church Slavonic',
+ ['csy']='Czech',
+ ['dan']='Danish',
+ ['dar']='Dargwa',
+ ['dcr']='Woods Cree',
+ ['deu']='German',
+ ['dgr']='Dogri',
+ ['div']='Divehi',
+ ['djr']='Djerma',
+ ['dng']='Dangme',
+ ['dnk']='Dinka',
+ ['dri']='Dari',
+ ['dun']='Dungan',
+ ['dzn']='Dzongkha',
+ ['ebi']='Ebira',
+ ['ecr']='Eastern Cree',
+ ['edo']='Edo',
+ ['efi']='Efik',
+ ['ell']='Greek',
+ ['eng']='English',
+ ['erz']='Erzya',
+ ['esp']='Spanish',
+ ['eti']='Estonian',
+ ['euq']='Basque',
+ ['evk']='Evenki',
+ ['evn']='Even',
+ ['ewe']='Ewe',
+ ['fan']='French Antillean',
+ ['far']='Farsi',
+ ['fin']='Finnish',
+ ['fji']='Fijian',
+ ['fle']='Flemish',
+ ['fne']='Forest Nenets',
+ ['fon']='Fon',
+ ['fos']='Faroese',
+ ['fra']='French',
+ ['fri']='Frisian',
+ ['frl']='Friulian',
+ ['fta']='Futa',
+ ['ful']='Fulani',
+ ['gad']='Ga',
+ ['gae']='Gaelic',
+ ['gag']='Gagauz',
+ ['gal']='Galician',
+ ['gar']='Garshuni',
+ ['gaw']='Garhwali',
+ ['gez']="Ge'ez",
+ ['gil']='Gilyak',
+ ['gmz']='Gumuz',
+ ['gon']='Gondi',
+ ['grn']='Greenlandic',
+ ['gro']='Garo',
+ ['gua']='Guarani',
+ ['guj']='Gujarati',
+ ['hai']='Haitian',
+ ['hal']='Halam',
+ ['har']='Harauti',
+ ['hau']='Hausa',
+ ['haw']='Hawaiin',
+ ['hbn']='Hammer-Banna',
+ ['hil']='Hiligaynon',
+ ['hin']='Hindi',
+ ['hma']='High Mari',
+ ['hnd']='Hindko',
+ ['ho']='Ho',
+ ['hri']='Harari',
+ ['hrv']='Croatian',
+ ['hun']='Hungarian',
+ ['hye']='Armenian',
+ ['ibo']='Igbo',
+ ['ijo']='Ijo',
+ ['ilo']='Ilokano',
+ ['ind']='Indonesian',
+ ['ing']='Ingush',
+ ['inu']='Inuktitut',
+ ['iri']='Irish',
+ ['irt']='Irish Traditional',
+ ['isl']='Icelandic',
+ ['ism']='Inari Sami',
+ ['ita']='Italian',
+ ['iwr']='Hebrew',
+ ['jan']='Japanese',
+ ['jav']='Javanese',
+ ['jii']='Yiddish',
+ ['jud']='Judezmo',
+ ['jul']='Jula',
+ ['kab']='Kabardian',
+ ['kac']='Kachchi',
+ ['kal']='Kalenjin',
+ ['kan']='Kannada',
+ ['kar']='Karachay',
+ ['kat']='Georgian',
+ ['kaz']='Kazakh',
+ ['keb']='Kebena',
+ ['kge']='Khutsuri Georgian',
+ ['kha']='Khakass',
+ ['khk']='Khanty-Kazim',
+ ['khm']='Khmer',
+ ['khs']='Khanty-Shurishkar',
+ ['khv']='Khanty-Vakhi',
+ ['khw']='Khowar',
+ ['kik']='Kikuyu',
+ ['kir']='Kirghiz',
+ ['kis']='Kisii',
+ ['kkn']='Kokni',
+ ['klm']='Kalmyk',
+ ['kmb']='Kamba',
+ ['kmn']='Kumaoni',
+ ['kmo']='Komo',
+ ['kms']='Komso',
+ ['knr']='Kanuri',
+ ['kod']='Kodagu',
+ ['koh']='Korean Old Hangul',
+ ['kok']='Konkani',
+ ['kon']='Kikongo',
+ ['kop']='Komi-Permyak',
+ ['kor']='Korean',
+ ['koz']='Komi-Zyrian',
+ ['kpl']='Kpelle',
+ ['kri']='Krio',
+ ['krk']='Karakalpak',
+ ['krl']='Karelian',
+ ['krm']='Karaim',
+ ['krn']='Karen',
+ ['krt']='Koorete',
+ ['ksh']='Kashmiri',
+ ['ksi']='Khasi',
+ ['ksm']='Kildin Sami',
+ ['kui']='Kui',
+ ['kul']='Kulvi',
+ ['kum']='Kumyk',
+ ['kur']='Kurdish',
+ ['kuu']='Kurukh',
+ ['kuy']='Kuy',
+ ['kyk']='Koryak',
+ ['lad']='Ladin',
+ ['lah']='Lahuli',
+ ['lak']='Lak',
+ ['lam']='Lambani',
+ ['lao']='Lao',
+ ['lat']='Latin',
+ ['laz']='Laz',
+ ['lcr']='L-Cree',
+ ['ldk']='Ladakhi',
+ ['lez']='Lezgi',
+ ['lin']='Lingala',
+ ['lma']='Low Mari',
+ ['lmb']='Limbu',
+ ['lmw']='Lomwe',
+ ['lsb']='Lower Sorbian',
+ ['lsm']='Lule Sami',
+ ['lth']='Lithuanian',
+ ['ltz']='Luxembourgish',
+ ['lub']='Luba',
+ ['lug']='Luganda',
+ ['luh']='Luhya',
+ ['luo']='Luo',
+ ['lvi']='Latvian',
+ ['maj']='Majang',
+ ['mak']='Makua',
+ ['mal']='Malayalam Traditional',
+ ['man']='Mansi',
+ ['map']='Mapudungun',
+ ['mar']='Marathi',
+ ['maw']='Marwari',
+ ['mbn']='Mbundu',
+ ['mch']='Manchu',
+ ['mcr']='Moose Cree',
+ ['mde']='Mende',
+ ['men']="Me'en",
+ ['miz']='Mizo',
+ ['mkd']='Macedonian',
+ ['mle']='Male',
+ ['mlg']='Malagasy',
+ ['mln']='Malinke',
+ ['mlr']='Malayalam Reformed',
+ ['mly']='Malay',
+ ['mnd']='Mandinka',
+ ['mng']='Mongolian',
+ ['mni']='Manipuri',
+ ['mnk']='Maninka',
+ ['mnx']='Manx Gaelic',
+ ['moh']='Mohawk',
+ ['mok']='Moksha',
+ ['mol']='Moldavian',
+ ['mon']='Mon',
+ ['mor']='Moroccan',
+ ['mri']='Maori',
+ ['mth']='Maithili',
+ ['mts']='Maltese',
+ ['mun']='Mundari',
+ ['nag']='Naga-Assamese',
+ ['nan']='Nanai',
+ ['nas']='Naskapi',
+ ['ncr']='N-Cree',
+ ['ndb']='Ndebele',
+ ['ndg']='Ndonga',
+ ['nep']='Nepali',
+ ['new']='Newari',
+ ['ngr']='Nagari',
+ ['nhc']='Norway House Cree',
+ ['nis']='Nisi',
+ ['niu']='Niuean',
+ ['nkl']='Nkole',
+ ['nko']="N'ko",
+ ['nld']='Dutch',
+ ['nog']='Nogai',
+ ['nor']='Norwegian',
+ ['nsm']='Northern Sami',
+ ['nta']='Northern Tai',
+ ['nto']='Esperanto',
+ ['nyn']='Nynorsk',
+ ['oci']='Occitan',
+ ['ocr']='Oji-Cree',
+ ['ojb']='Ojibway',
+ ['ori']='Oriya',
+ ['oro']='Oromo',
+ ['oss']='Ossetian',
+ ['paa']='Palestinian Aramaic',
+ ['pal']='Pali',
+ ['pan']='Punjabi',
+ ['pap']='Palpa',
+ ['pas']='Pashto',
+ ['pgr']='Polytonic Greek',
+ ['pil']='Pilipino',
+ ['plg']='Palaung',
+ ['plk']='Polish',
+ ['pro']='Provencal',
+ ['ptg']='Portuguese',
+ ['qin']='Chin',
+ ['raj']='Rajasthani',
+ ['rbu']='Russian Buriat',
+ ['rcr']='R-Cree',
+ ['ria']='Riang',
+ ['rms']='Rhaeto-Romanic',
+ ['rom']='Romanian',
+ ['roy']='Romany',
+ ['rsy']='Rusyn',
+ ['rua']='Ruanda',
+ ['rus']='Russian',
+ ['sad']='Sadri',
+ ['san']='Sanskrit',
+ ['sat']='Santali',
+ ['say']='Sayisi',
+ ['sek']='Sekota',
+ ['sel']='Selkup',
+ ['sgo']='Sango',
+ ['shn']='Shan',
+ ['sib']='Sibe',
+ ['sid']='Sidamo',
+ ['sig']='Silte Gurage',
+ ['sks']='Skolt Sami',
+ ['sky']='Slovak',
+ ['sla']='Slavey',
+ ['slv']='Slovenian',
+ ['sml']='Somali',
+ ['smo']='Samoan',
+ ['sna']='Sena',
+ ['snd']='Sindhi',
+ ['snh']='Sinhalese',
+ ['snk']='Soninke',
+ ['sog']='Sodo Gurage',
+ ['sot']='Sotho',
+ ['sqi']='Albanian',
+ ['srb']='Serbian',
+ ['srk']='Saraiki',
+ ['srr']='Serer',
+ ['ssl']='South Slavey',
+ ['ssm']='Southern Sami',
+ ['sur']='Suri',
+ ['sva']='Svan',
+ ['sve']='Swedish',
+ ['swa']='Swadaya Aramaic',
+ ['swk']='Swahili',
+ ['swz']='Swazi',
+ ['sxt']='Sutu',
+ ['syr']='Syriac',
+ ['tab']='Tabasaran',
+ ['taj']='Tajiki',
+ ['tam']='Tamil',
+ ['tat']='Tatar',
+ ['tcr']='TH-Cree',
+ ['tel']='Telugu',
+ ['tgn']='Tongan',
+ ['tgr']='Tigre',
+ ['tgy']='Tigrinya',
+ ['tha']='Thai',
+ ['tht']='Tahitian',
+ ['tib']='Tibetan',
+ ['tkm']='Turkmen',
+ ['tmn']='Temne',
+ ['tna']='Tswana',
+ ['tne']='Tundra Nenets',
+ ['tng']='Tonga',
+ ['tod']='Todo',
+ ['trk']='Turkish',
+ ['tsg']='Tsonga',
+ ['tua']='Turoyo Aramaic',
+ ['tul']='Tulu',
+ ['tuv']='Tuvin',
+ ['twi']='Twi',
+ ['udm']='Udmurt',
+ ['ukr']='Ukrainian',
+ ['urd']='Urdu',
+ ['usb']='Upper Sorbian',
+ ['uyg']='Uyghur',
+ ['uzb']='Uzbek',
+ ['ven']='Venda',
+ ['vit']='Vietnamese',
+ ['wa' ]='Wa',
+ ['wag']='Wagdi',
+ ['wcr']='West-Cree',
+ ['wel']='Welsh',
+ ['wlf']='Wolof',
+ ['xbd']='Tai Lue',
+ ['xhs']='Xhosa',
+ ['yak']='Yakut',
+ ['yba']='Yoruba',
+ ['ycr']='Y-Cree',
+ ['yic']='Yi Classic',
+ ['yim']='Yi Modern',
+ ['zhh']='Chinese Hong Kong',
+ ['zhp']='Chinese Phonetic',
+ ['zhs']='Chinese Simplified',
+ ['zht']='Chinese Traditional',
+ ['znd']='Zande',
+ ['zul']='Zulu'
+}
+otf.tables.features={
+ ['aalt']='Access All Alternates',
+ ['abvf']='Above-Base Forms',
+ ['abvm']='Above-Base Mark Positioning',
+ ['abvs']='Above-Base Substitutions',
+ ['afrc']='Alternative Fractions',
+ ['akhn']='Akhands',
+ ['blwf']='Below-Base Forms',
+ ['blwm']='Below-Base Mark Positioning',
+ ['blws']='Below-Base Substitutions',
+ ['c2pc']='Petite Capitals From Capitals',
+ ['c2sc']='Small Capitals From Capitals',
+ ['calt']='Contextual Alternates',
+ ['case']='Case-Sensitive Forms',
+ ['ccmp']='Glyph Composition/Decomposition',
+ ['cjct']='Conjunct Forms',
+ ['clig']='Contextual Ligatures',
+ ['cpsp']='Capital Spacing',
+ ['cswh']='Contextual Swash',
+ ['curs']='Cursive Positioning',
+ ['dflt']='Default Processing',
+ ['dist']='Distances',
+ ['dlig']='Discretionary Ligatures',
+ ['dnom']='Denominators',
+ ['dtls']='Dotless Forms',
+ ['expt']='Expert Forms',
+ ['falt']='Final glyph Alternates',
+ ['fin2']='Terminal Forms #2',
+ ['fin3']='Terminal Forms #3',
+ ['fina']='Terminal Forms',
+ ['flac']='Flattened Accents Over Capitals',
+ ['frac']='Fractions',
+ ['fwid']='Full Width',
+ ['half']='Half Forms',
+ ['haln']='Halant Forms',
+ ['halt']='Alternate Half Width',
+ ['hist']='Historical Forms',
+ ['hkna']='Horizontal Kana Alternates',
+ ['hlig']='Historical Ligatures',
+ ['hngl']='Hangul',
+ ['hojo']='Hojo Kanji Forms',
+ ['hwid']='Half Width',
+ ['init']='Initial Forms',
+ ['isol']='Isolated Forms',
+ ['ital']='Italics',
+ ['jalt']='Justification Alternatives',
+ ['jp04']='JIS2004 Forms',
+ ['jp78']='JIS78 Forms',
+ ['jp83']='JIS83 Forms',
+ ['jp90']='JIS90 Forms',
+ ['kern']='Kerning',
+ ['lfbd']='Left Bounds',
+ ['liga']='Standard Ligatures',
+ ['ljmo']='Leading Jamo Forms',
+ ['lnum']='Lining Figures',
+ ['locl']='Localized Forms',
+ ['mark']='Mark Positioning',
+ ['med2']='Medial Forms #2',
+ ['medi']='Medial Forms',
+ ['mgrk']='Mathematical Greek',
+ ['mkmk']='Mark to Mark Positioning',
+ ['mset']='Mark Positioning via Substitution',
+ ['nalt']='Alternate Annotation Forms',
+ ['nlck']='NLC Kanji Forms',
+ ['nukt']='Nukta Forms',
+ ['numr']='Numerators',
+ ['onum']='Old Style Figures',
+ ['opbd']='Optical Bounds',
+ ['ordn']='Ordinals',
+ ['ornm']='Ornaments',
+ ['palt']='Proportional Alternate Width',
+ ['pcap']='Petite Capitals',
+ ['pnum']='Proportional Figures',
+ ['pref']='Pre-base Forms',
+ ['pres']='Pre-base Substitutions',
+ ['pstf']='Post-base Forms',
+ ['psts']='Post-base Substitutions',
+ ['pwid']='Proportional Widths',
+ ['qwid']='Quarter Widths',
+ ['rand']='Randomize',
+ ['rkrf']='Rakar Forms',
+ ['rlig']='Required Ligatures',
+ ['rphf']='Reph Form',
+ ['rtbd']='Right Bounds',
+ ['rtla']='Right-To-Left Alternates',
+ ['rtlm']='Right To Left Math',
+ ['ruby']='Ruby Notation Forms',
+ ['salt']='Stylistic Alternates',
+ ['sinf']='Scientific Inferiors',
+ ['size']='Optical Size',
+ ['smcp']='Small Capitals',
+ ['smpl']='Simplified Forms',
+ ['ss01']='Stylistic Set 1',
+ ['ss02']='Stylistic Set 2',
+ ['ss03']='Stylistic Set 3',
+ ['ss04']='Stylistic Set 4',
+ ['ss05']='Stylistic Set 5',
+ ['ss06']='Stylistic Set 6',
+ ['ss07']='Stylistic Set 7',
+ ['ss08']='Stylistic Set 8',
+ ['ss09']='Stylistic Set 9',
+ ['ss10']='Stylistic Set 10',
+ ['ss11']='Stylistic Set 11',
+ ['ss12']='Stylistic Set 12',
+ ['ss13']='Stylistic Set 13',
+ ['ss14']='Stylistic Set 14',
+ ['ss15']='Stylistic Set 15',
+ ['ss16']='Stylistic Set 16',
+ ['ss17']='Stylistic Set 17',
+ ['ss18']='Stylistic Set 18',
+ ['ss19']='Stylistic Set 19',
+ ['ss20']='Stylistic Set 20',
+ ['ssty']='Script Style',
+ ['subs']='Subscript',
+ ['sups']='Superscript',
+ ['swsh']='Swash',
+ ['titl']='Titling',
+ ['tjmo']='Trailing Jamo Forms',
+ ['tnam']='Traditional Name Forms',
+ ['tnum']='Tabular Figures',
+ ['trad']='Traditional Forms',
+ ['twid']='Third Widths',
+ ['unic']='Unicase',
+ ['valt']='Alternate Vertical Metrics',
+ ['vatu']='Vattu Variants',
+ ['vert']='Vertical Writing',
+ ['vhal']='Alternate Vertical Half Metrics',
+ ['vjmo']='Vowel Jamo Forms',
+ ['vkna']='Vertical Kana Alternates',
+ ['vkrn']='Vertical Kerning',
+ ['vpal']='Proportional Alternate Vertical Metrics',
+ ['vrt2']='Vertical Rotation',
+ ['zero']='Slashed Zero',
+ ['trep']='Traditional TeX Replacements',
+ ['tlig']='Traditional TeX Ligatures',
+}
+otf.tables.baselines={
+ ['hang']='Hanging baseline',
+ ['icfb']='Ideographic character face bottom edge baseline',
+ ['icft']='Ideographic character face tope edige baseline',
+ ['ideo']='Ideographic em-box bottom edge baseline',
+ ['idtp']='Ideographic em-box top edge baseline',
+ ['math']='Mathmatical centered baseline',
+ ['romn']='Roman baseline'
+}
+function otf.tables.to_tag(id)
+ return stringformat("%4s",lower(id))
+end
+local function resolve(tab,id)
+ if tab and id then
+ id=lower(id)
+ return tab[id] or tab[gsub(id," ","")] or tab['dflt'] or ''
+ else
+ return "unknown"
+ end
+end
+function otf.meanings.script(id)
+ return resolve(otf.tables.scripts,id)
+end
+function otf.meanings.language(id)
+ return resolve(otf.tables.languages,id)
+end
+function otf.meanings.feature(id)
+ return resolve(otf.tables.features,id)
+end
+function otf.meanings.baseline(id)
+ return resolve(otf.tables.baselines,id)
+end
+otf.tables.to_scripts=table.reverse_hash(otf.tables.scripts )
+otf.tables.to_languages=table.reverse_hash(otf.tables.languages)
+otf.tables.to_features=table.reverse_hash(otf.tables.features )
+local scripts=otf.tables.scripts
+local languages=otf.tables.languages
+local features=otf.tables.features
+local to_scripts=otf.tables.to_scripts
+local to_languages=otf.tables.to_languages
+local to_features=otf.tables.to_features
+for k,v in next,to_features do
+ local stripped=gsub(k,"%-"," ")
+ to_features[stripped]=v
+ local stripped=gsub(k,"[^a-zA-Z0-9]","")
+ to_features[stripped]=v
+end
+for k,v in next,to_features do
+ to_features[lower(k)]=v
+end
+otf.meanings.checkers={
+ rand=function(v)
+ return v and "random"
+ end
+}
+local checkers=otf.meanings.checkers
+function otf.meanings.normalize(features)
+ local h={}
+ for k,v in next,features do
+ k=lower(k)
+ if k=="language" or k=="lang" then
+ v=gsub(lower(v),"[^a-z0-9%-]","")
+ if not languages[v] then
+ h.language=to_languages[v] or "dflt"
+ else
+ h.language=v
+ end
+ elseif k=="script" then
+ v=gsub(lower(v),"[^a-z0-9%-]","")
+ if not scripts[v] then
+ h.script=to_scripts[v] or "dflt"
+ else
+ h.script=v
+ end
+ else
+ if type(v)=="string" then
+ local b=v:is_boolean()
+ if type(b)=="nil" then
+ v=tonumber(v) or lower(v)
+ else
+ v=b
+ end
+ end
+ k=to_features[k] or k
+ local c=checkers[k]
+ h[k]=c and c(v) or v
+ end
+ end
+ return h
+end
+
+end -- closure
+
+do -- begin closure to overcome local limits and interference
+
+if not modules then modules={} end modules ['font-map']={
+ 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 utf=unicode.utf8
+local match,format,find,concat,gsub,lower=string.match,string.format,string.find,table.concat,string.gsub,string.lower
+local lpegmatch=lpeg.match
+local utfbyte=utf.byte
+local trace_loading=false trackers.register("otf.loading",function(v) trace_loading=v end)
+local trace_unimapping=false trackers.register("otf.unimapping",function(v) trace_unimapping=v end)
+local ctxcatcodes=tex and tex.ctxcatcodes
+fonts=fonts or {}
+fonts.map=fonts.map or {}
+local function load_lum_table(filename)
+ local lumname=file.replacesuffix(file.basename(filename),"lum")
+ local lumfile=resolvers.find_file(lumname,"map") or ""
+ if lumfile~="" and lfs.isfile(lumfile) then
+ if trace_loading or trace_unimapping then
+ logs.report("load otf","enhance: loading %s ",lumfile)
+ end
+ lumunic=dofile(lumfile)
+ return lumunic,lumfile
+ end
+end
+local hex=lpeg.R("AF","09")
+local hexfour=(hex*hex*hex*hex)/function(s) return tonumber(s,16) end
+local hexsix=(hex^1)/function(s) return tonumber(s,16) end
+local dec=(lpeg.R("09")^1)/tonumber
+local period=lpeg.P(".")
+local unicode=lpeg.P("uni")*(hexfour*(period+lpeg.P(-1))*lpeg.Cc(false)+lpeg.Ct(hexfour^1)*lpeg.Cc(true))
+local ucode=lpeg.P("u")*(hexsix*(period+lpeg.P(-1))*lpeg.Cc(false)+lpeg.Ct(hexsix^1)*lpeg.Cc(true))
+local index=lpeg.P("index")*dec*lpeg.Cc(false)
+local parser=unicode+ucode+index
+local parsers={}
+local function make_name_parser(str)
+ if not str or str=="" then
+ return parser
+ else
+ local p=parsers[str]
+ if not p then
+ p=lpeg.P(str)*period*dec*lpeg.Cc(false)
+ parsers[str]=p
+ end
+ return p
+ end
+end
+local function tounicode16(unicode)
+ if unicode<0x10000 then
+ return format("%04X",unicode)
+ else
+ return format("%04X%04X",unicode/1024+0xD800,unicode%1024+0xDC00)
+ end
+end
+local function tounicode16sequence(unicodes)
+ local t={}
+ for l=1,#unicodes do
+ local unicode=unicodes[l]
+ if unicode<0x10000 then
+ t[l]=format("%04X",unicode)
+ else
+ t[l]=format("%04X%04X",unicode/1024+0xD800,unicode%1024+0xDC00)
+ end
+ end
+ return concat(t)
+end
+fonts.map.load_lum_table=load_lum_table
+fonts.map.make_name_parser=make_name_parser
+fonts.map.tounicode16=tounicode16
+fonts.map.tounicode16sequence=tounicode16sequence
+local separator=lpeg.S("_.")
+local other=lpeg.C((1-separator)^1)
+local ligsplitter=lpeg.Ct(other*(separator*other)^0)
+fonts.map.add_to_unicode=function(data,filename)
+ local unicodes=data.luatex and data.luatex.unicodes
+ if not unicodes then
+ return
+ end
+ unicodes['space']=unicodes['space'] or 32
+ unicodes['hyphen']=unicodes['hyphen'] or 45
+ unicodes['zwj']=unicodes['zwj'] or 0x200D
+ unicodes['zwnj']=unicodes['zwnj'] or 0x200C
+ local tounicode,originals,ns,nl,private,unknown={},{},0,0,fonts.private,format("%04X",utfbyte("?"))
+ data.luatex.tounicode,data.luatex.originals=tounicode,originals
+ local lumunic,uparser,oparser
+ if false then
+ lumunic=load_lum_table(filename)
+ lumunic=lumunic and lumunic.tounicode
+ end
+ local cidinfo,cidnames,cidcodes=data.cidinfo
+ local usedmap=cidinfo and cidinfo.usedname
+ usedmap=usedmap and lower(usedmap)
+ usedmap=usedmap and fonts.cid.map[usedmap]
+ if usedmap then
+ oparser=usedmap and make_name_parser(cidinfo.ordering)
+ cidnames=usedmap.names
+ cidcodes=usedmap.unicodes
+ end
+ uparser=make_name_parser()
+ local aglmap=fonts.map and fonts.map.agl_to_unicode
+ for index,glyph in next,data.glyphs do
+ local name,unic=glyph.name,glyph.unicode or -1
+ if unic==-1 or unic>=private or (unic>=0xE000 and unic<=0xF8FF) or unic==0xFFFE or unic==0xFFFF then
+ local unicode=(lumunic and lumunic[name]) or (aglmap and aglmap[name])
+ if unicode then
+ originals[index],tounicode[index],ns=unicode,tounicode16(unicode),ns+1
+ end
+ if (not unicode) and usedmap then
+ local foundindex=lpegmatch(oparser,name)
+ if foundindex then
+ unicode=cidcodes[foundindex]
+ if unicode then
+ originals[index],tounicode[index],ns=unicode,tounicode16(unicode),ns+1
+ else
+ local reference=cidnames[foundindex]
+ if reference then
+ local foundindex=lpegmatch(oparser,reference)
+ if foundindex then
+ unicode=cidcodes[foundindex]
+ if unicode then
+ originals[index],tounicode[index],ns=unicode,tounicode16(unicode),ns+1
+ end
+ end
+ if not unicode then
+ local foundcodes,multiple=lpegmatch(uparser,reference)
+ if foundcodes then
+ if multiple then
+ originals[index],tounicode[index],nl,unicode=foundcodes,tounicode16sequence(foundcodes),nl+1,true
+ else
+ originals[index],tounicode[index],ns,unicode=foundcodes,tounicode16(foundcodes),ns+1,foundcodes
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+ if not unicode then
+ local split=lpegmatch(ligsplitter,name)
+ local nplit=(split and #split) or 0
+ if nplit==0 then
+ elseif nplit==1 then
+ local base=split[1]
+ unicode=unicodes[base] or (aglmap and aglmap[base])
+ if unicode then
+ if type(unicode)=="table" then
+ unicode=unicode[1]
+ end
+ originals[index],tounicode[index],ns=unicode,tounicode16(unicode),ns+1
+ end
+ else
+ local t={}
+ for l=1,nplit do
+ local base=split[l]
+ local u=unicodes[base] or (aglmap and aglmap[base])
+ if not u then
+ break
+ elseif type(u)=="table" then
+ t[#t+1]=u[1]
+ else
+ t[#t+1]=u
+ end
+ end
+ if #t>0 then
+ originals[index],tounicode[index],nl,unicode=t,tounicode16sequence(t),nl+1,true
+ end
+ end
+ end
+ if not unicode then
+ local foundcodes,multiple=lpegmatch(uparser,name)
+ if foundcodes then
+ if multiple then
+ originals[index],tounicode[index],nl,unicode=foundcodes,tounicode16sequence(foundcodes),nl+1,true
+ else
+ originals[index],tounicode[index],ns,unicode=foundcodes,tounicode16(foundcodes),ns+1,foundcodes
+ end
+ end
+ end
+ if not unicode then
+ originals[index],tounicode[index]=0xFFFD,"FFFD"
+ end
+ end
+ end
+ if trace_unimapping then
+ for index,glyph in table.sortedhash(data.glyphs) do
+ local toun,name,unic=tounicode[index],glyph.name,glyph.unicode or -1
+ if toun then
+ logs.report("load otf","internal: 0x%05X, name: %s, unicode: 0x%05X, tounicode: %s",index,name,unic,toun)
+ else
+ logs.report("load otf","internal: 0x%05X, name: %s, unicode: 0x%05X",index,name,unic)
+ end
+ end
+ end
+ if trace_loading and (ns>0 or nl>0) then
+ logs.report("load otf","enhance: %s tounicode entries added (%s ligatures)",nl+ns,ns)
+ end
+end
+
+end -- closure
+
+do -- begin closure to overcome local limits and interference
+
+if not modules then modules={} end modules ['font-otf']={
+ 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 utf=unicode.utf8
+local concat,utfbyte=table.concat,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 lpegmatch=lpeg.match
+local trace_private=false trackers.register("otf.private",function(v) trace_private=v end)
+local trace_loading=false trackers.register("otf.loading",function(v) trace_loading=v end)
+local trace_features=false trackers.register("otf.features",function(v) trace_features=v end)
+local trace_dynamics=false trackers.register("otf.dynamics",function(v) trace_dynamics=v end)
+local trace_sequences=false trackers.register("otf.sequences",function(v) trace_sequences=v end)
+local trace_math=false trackers.register("otf.math",function(v) trace_math=v end)
+local trace_defining=false trackers.register("fonts.defining",function(v) trace_defining=v end)
+fonts=fonts or {}
+fonts.otf=fonts.otf or {}
+fonts.tfm=fonts.tfm or {}
+local otf=fonts.otf
+local tfm=fonts.tfm
+local fontdata=fonts.ids
+otf.tables=otf.tables or {}
+otf.meanings=otf.meanings or {}
+otf.tables.features=otf.tables.features or {}
+otf.tables.languages=otf.tables.languages or {}
+otf.tables.scripts=otf.tables.scripts or {}
+otf.features=otf.features or {}
+otf.features.list=otf.features.list or {}
+otf.features.default=otf.features.default or {}
+otf.enhancers=otf.enhancers or {}
+otf.glists={ "gsub","gpos" }
+otf.version=2.653
+otf.pack=true
+otf.syncspace=true
+otf.notdef=false
+otf.cache=containers.define("fonts","otf",otf.version,true)
+otf.cleanup_aat=false
+local wildcard="*"
+local default="dflt"
+otf.tables.global_fields=table.tohash {
+ "lookups",
+ "glyphs",
+ "subfonts",
+ "luatex",
+ "pfminfo",
+ "cidinfo",
+ "tables",
+ "names",
+ "unicodes",
+ "names",
+ "anchor_classes",
+ "kern_classes",
+ "gpos",
+ "gsub"
+}
+otf.tables.valid_fields={
+ "anchor_classes",
+ "ascent",
+ "cache_version",
+ "cidinfo",
+ "copyright",
+ "creationtime",
+ "descent",
+ "design_range_bottom",
+ "design_range_top",
+ "design_size",
+ "encodingchanged",
+ "extrema_bound",
+ "familyname",
+ "fontname",
+ "fontstyle_id",
+ "fontstyle_name",
+ "fullname",
+ "glyphs",
+ "hasvmetrics",
+ "head_optimized_for_cleartype",
+ "horiz_base",
+ "issans",
+ "isserif",
+ "italicangle",
+ "kerns",
+ "lookups",
+ "macstyle",
+ "modificationtime",
+ "onlybitmaps",
+ "origname",
+ "os2_version",
+ "pfminfo",
+ "private",
+ "serifcheck",
+ "sfd_version",
+ "strokedfont",
+ "strokewidth",
+ "subfonts",
+ "table_version",
+ "ttf_tables",
+ "uni_interp",
+ "uniqueid",
+ "units_per_em",
+ "upos",
+ "use_typo_metrics",
+ "uwidth",
+ "validation_state",
+ "verbose",
+ "version",
+ "vert_base",
+ "weight",
+ "weight_width_slope_only",
+ "xuid",
+}
+local function load_featurefile(ff,featurefile)
+ if featurefile then
+ featurefile=resolvers.find_file(file.addsuffix(featurefile,'fea'),'fea')
+ if featurefile and featurefile~="" then
+ if trace_loading then
+ logs.report("load otf","featurefile: %s",featurefile)
+ end
+ fontloader.apply_featurefile(ff,featurefile)
+ end
+ end
+end
+function otf.enhance(name,data,filename,verbose)
+ local enhancer=otf.enhancers[name]
+ if enhancer then
+ if (verbose~=nil and verbose) or trace_loading then
+ logs.report("load otf","enhance: %s (%s)",name,filename)
+ end
+ enhancer(data,filename)
+ end
+end
+local enhancers={
+ "patch bugs",
+ "merge cid fonts","prepare unicode","cleanup ttf tables","compact glyphs","reverse coverage",
+ "cleanup aat","enrich with features","add some missing characters",
+ "reorganize mark classes",
+ "reorganize kerns",
+ "flatten glyph lookups","flatten anchor tables","flatten feature tables",
+ "simplify glyph lookups",
+ "prepare luatex tables",
+ "analyse features","rehash features",
+ "analyse anchors","analyse marks","analyse unicodes","analyse subtables",
+ "check italic correction","check math",
+ "share widths",
+ "strip not needed data",
+ "migrate metadata",
+ "check math parameters",
+}
+function otf.load(filename,format,sub,featurefile)
+ local name=file.basename(file.removesuffix(filename))
+ local attr=lfs.attributes(filename)
+ local size,time=attr.size or 0,attr.modification or 0
+ if featurefile then
+ local fattr=lfs.attributes(featurefile)
+ local fsize,ftime=fattr and fattr.size or 0,fattr and fattr.modification or 0
+ name=name.."@"..file.removesuffix(file.basename(featurefile))..ftime..fsize
+ end
+ if sub=="" then sub=false end
+ local hash=name
+ if sub then
+ hash=hash.."-"..sub
+ end
+ hash=containers.cleanname(hash)
+ local data=containers.read(otf.cache,hash)
+ if not data or data.verbose~=fonts.verbose or data.size~=size or data.time~=time then
+ logs.report("load otf","loading: %s (hash: %s)",filename,hash)
+ local ff,messages
+ if sub then
+ ff,messages=fontloader.open(filename,sub)
+ else
+ ff,messages=fontloader.open(filename)
+ end
+ if trace_loading and messages and #messages>0 then
+ if type(messages)=="string" then
+ logs.report("load otf","warning: %s",messages)
+ else
+ for m=1,#messages do
+ logs.report("load otf","warning: %s",tostring(messages[m]))
+ end
+ end
+ else
+ logs.report("load otf","font loaded okay")
+ end
+ if ff then
+ load_featurefile(ff,featurefile)
+ data=fontloader.to_table(ff)
+ fontloader.close(ff)
+ if data then
+ logs.report("load otf","file size: %s",size)
+ logs.report("load otf","enhancing ...")
+ for e=1,#enhancers do
+ otf.enhance(enhancers[e],data,filename)
+ io.flush()
+ end
+ if otf.pack and not fonts.verbose then
+ otf.enhance("pack",data,filename)
+ end
+ data.size=size
+ data.time=time
+ data.verbose=fonts.verbose
+ logs.report("load otf","saving in cache: %s",filename)
+ data=containers.write(otf.cache,hash,data)
+ collectgarbage("collect")
+ data=containers.read(otf.cache,hash)
+ collectgarbage("collect")
+ else
+ logs.report("load otf","loading failed (table conversion error)")
+ end
+ else
+ logs.report("load otf","loading failed (file read error)")
+ end
+ end
+ if data then
+ if trace_defining then
+ logs.report("define font","loading from cache: %s",hash)
+ end
+ otf.enhance("unpack",data,filename,false)
+ otf.add_dimensions(data)
+ if trace_sequences then
+ otf.show_feature_order(data,filename)
+ end
+ end
+ return data
+end
+function otf.add_dimensions(data)
+ if data then
+ local force=otf.notdef
+ local luatex=data.luatex
+ local defaultwidth=luatex.defaultwidth or 0
+ local defaultheight=luatex.defaultheight or 0
+ local defaultdepth=luatex.defaultdepth or 0
+ for _,d in next,data.glyphs do
+ local bb,wd=d.boundingbox,d.width
+ if not wd then
+ d.width=defaultwidth
+ elseif wd~=0 and d.class=="mark" then
+ d.width=-wd
+ end
+ if force and not d.name then
+ d.name=".notdef"
+ end
+ if bb then
+ local ht,dp=bb[4],-bb[2]
+ if ht==0 or ht<0 then
+ else
+ d.height=ht
+ end
+ if dp==0 or dp<0 then
+ else
+ d.depth=dp
+ end
+ end
+ end
+ end
+end
+function otf.show_feature_order(otfdata,filename)
+ local sequences=otfdata.luatex.sequences
+ if sequences and #sequences>0 then
+ if trace_loading then
+ logs.report("otf check","font %s has %s sequences",filename,#sequences)
+ logs.report("otf check"," ")
+ end
+ for nos=1,#sequences do
+ local sequence=sequences[nos]
+ local typ=sequence.type or "no-type"
+ local name=sequence.name or "no-name"
+ local subtables=sequence.subtables or { "no-subtables" }
+ local features=sequence.features
+ if trace_loading then
+ logs.report("otf check","%3i %-15s %-20s [%s]",nos,name,typ,concat(subtables,","))
+ end
+ if features then
+ for feature,scripts in next,features do
+ local tt={}
+ for script,languages in next,scripts do
+ local ttt={}
+ for language,_ in next,languages do
+ ttt[#ttt+1]=language
+ end
+ tt[#tt+1]=format("[%s: %s]",script,concat(ttt," "))
+ end
+ if trace_loading then
+ logs.report("otf check"," %s: %s",feature,concat(tt," "))
+ end
+ end
+ end
+ end
+ if trace_loading then
+ logs.report("otf check","\n")
+ end
+ elseif trace_loading then
+ logs.report("otf check","font %s has no sequences",filename)
+ end
+end
+otf.enhancers["reorganize mark classes"]=function(data,filename)
+ if data.mark_classes then
+ local unicodes=data.luatex.unicodes
+ local reverse={}
+ for name,class in next,data.mark_classes do
+ local t={}
+ for s in gmatch(class,"[^ ]+") do
+ local us=unicodes[s]
+ if type(us)=="table" then
+ for u=1,#us do
+ t[us[u]]=true
+ end
+ else
+ t[us]=true
+ end
+ end
+ reverse[name]=t
+ end
+ data.luatex.markclasses=reverse
+ data.mark_classes=nil
+ end
+end
+otf.enhancers["prepare luatex tables"]=function(data,filename)
+ data.luatex=data.luatex or {}
+ local luatex=data.luatex
+ luatex.filename=filename
+ luatex.version=otf.version
+ luatex.creator="context mkiv"
+end
+otf.enhancers["cleanup aat"]=function(data,filename)
+ if otf.cleanup_aat then
+ end
+end
+local function analyze_features(g,features)
+ if g then
+ local t,done={},{}
+ for k=1,#g do
+ local f=features or g[k].features
+ if f then
+ for k=1,#f do
+ local tag=f[k].tag
+ if not done[tag] then
+ t[#t+1]=tag
+ done[tag]=true
+ end
+ end
+ end
+ end
+ if #t>0 then
+ return t
+ end
+ end
+ return nil
+end
+otf.enhancers["analyse features"]=function(data,filename)
+end
+otf.enhancers["rehash features"]=function(data,filename)
+ local features={}
+ data.luatex.features=features
+ for k,what in next,otf.glists do
+ local dw=data[what]
+ if dw then
+ local f={}
+ features[what]=f
+ for i=1,#dw do
+ local d=dw[i]
+ local dfeatures=d.features
+ if dfeatures then
+ for i=1,#dfeatures do
+ local df=dfeatures[i]
+ local tag=strip(lower(df.tag))
+ local ft=f[tag] if not ft then ft={} f[tag]=ft end
+ local dscripts=df.scripts
+ for script,languages in next,dscripts do
+ script=strip(lower(script))
+ local fts=ft[script] if not fts then fts={} ft[script]=fts end
+ for i=1,#languages do
+ fts[strip(lower(languages[i]))]=true
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+end
+otf.enhancers["analyse anchors"]=function(data,filename)
+ local classes=data.anchor_classes
+ local luatex=data.luatex
+ local anchor_to_lookup,lookup_to_anchor={},{}
+ luatex.anchor_to_lookup,luatex.lookup_to_anchor=anchor_to_lookup,lookup_to_anchor
+ if classes then
+ for c=1,#classes do
+ local class=classes[c]
+ local anchor=class.name
+ local lookups=class.lookup
+ if type(lookups)~="table" then
+ lookups={ lookups }
+ end
+ local a=anchor_to_lookup[anchor]
+ if not a then a={} anchor_to_lookup[anchor]=a end
+ for l=1,#lookups do
+ local lookup=lookups[l]
+ local l=lookup_to_anchor[lookup]
+ if not l then l={} lookup_to_anchor[lookup]=l end
+ l[anchor]=true
+ a[lookup]=true
+ end
+ end
+ end
+end
+otf.enhancers["analyse marks"]=function(data,filename)
+ local glyphs=data.glyphs
+ local marks={}
+ data.luatex.marks=marks
+ for unicode,index in next,data.luatex.indices do
+ local glyph=glyphs[index]
+ if glyph.class=="mark" then
+ marks[unicode]=true
+ end
+ end
+end
+otf.enhancers["analyse unicodes"]=fonts.map.add_to_unicode
+otf.enhancers["analyse subtables"]=function(data,filename)
+ data.luatex=data.luatex or {}
+ local luatex=data.luatex
+ local sequences={}
+ local lookups={}
+ luatex.sequences=sequences
+ luatex.lookups=lookups
+ for _,g in next,{ data.gsub,data.gpos } do
+ for k=1,#g do
+ local gk=g[k]
+ local typ=gk.type
+ if typ=="gsub_contextchain" or typ=="gpos_contextchain" then
+ gk.chain=1
+ elseif typ=="gsub_reversecontextchain" or typ=="gpos_reversecontextchain" then
+ gk.chain=-1
+ else
+ gk.chain=0
+ end
+ local features=gk.features
+ if features then
+ sequences[#sequences+1]=gk
+ local t={}
+ for f=1,#features do
+ local feature=features[f]
+ local hash={}
+ for s,languages in next,feature.scripts do
+ s=lower(s)
+ local h=hash[s]
+ if not h then h={} hash[s]=h end
+ for l=1,#languages do
+ h[strip(lower(languages[l]))]=true
+ end
+ end
+ t[feature.tag]=hash
+ end
+ gk.features=t
+ else
+ lookups[gk.name]=gk
+ gk.name=nil
+ end
+ local subtables=gk.subtables
+ if subtables then
+ local t={}
+ for s=1,#subtables do
+ local subtable=subtables[s]
+ local name=subtable.name
+ t[#t+1]=name
+ end
+ gk.subtables=t
+ end
+ local flags=gk.flags
+ if flags then
+ gk.flags={
+ (flags.ignorecombiningmarks and "mark") or false,
+ (flags.ignoreligatures and "ligature") or false,
+ (flags.ignorebaseglyphs and "base") or false,
+ flags.r2l or false,
+ }
+ if flags.mark_class then
+ gk.markclass=luatex.markclasses[flags.mark_class]
+ end
+ end
+ end
+ end
+end
+otf.enhancers["merge cid fonts"]=function(data,filename)
+ if data.subfonts then
+ if data.glyphs and next(data.glyphs) then
+ logs.report("load otf","replacing existing glyph table due to subfonts")
+ end
+ local cidinfo=data.cidinfo
+ local verbose=fonts.verbose
+ if cidinfo.registry then
+ local cidmap,cidname=fonts.cid.getmap(cidinfo.registry,cidinfo.ordering,cidinfo.supplement)
+ if cidmap then
+ cidinfo.usedname=cidmap.usedname
+ local glyphs,uni_to_int,int_to_uni,nofnames,nofunicodes={},{},{},0,0
+ local unicodes,names=cidmap.unicodes,cidmap.names
+ for n,subfont in next,data.subfonts do
+ for index,g in next,subfont.glyphs do
+ if not next(g) then
+ else
+ local unicode,name=unicodes[index],names[index]
+ g.cidindex=n
+ g.boundingbox=g.boundingbox
+ g.name=g.name or name or "unknown"
+ if unicode then
+ uni_to_int[unicode]=index
+ int_to_uni[index]=unicode
+ nofunicodes=nofunicodes+1
+ g.unicode=unicode
+ elseif name then
+ nofnames=nofnames+1
+ g.unicode=-1
+ end
+ glyphs[index]=g
+ end
+ end
+ subfont.glyphs=nil
+ end
+ if trace_loading then
+ logs.report("load otf","cid font remapped, %s unicode points, %s symbolic names, %s glyphs",nofunicodes,nofnames,nofunicodes+nofnames)
+ end
+ data.glyphs=glyphs
+ data.map=data.map or {}
+ data.map.map=uni_to_int
+ data.map.backmap=int_to_uni
+ elseif trace_loading then
+ logs.report("load otf","unable to remap cid font, missing cid file for %s",filename)
+ end
+ elseif trace_loading then
+ logs.report("load otf","font %s has no glyphs",filename)
+ end
+ end
+end
+otf.enhancers["prepare unicode"]=function(data,filename)
+ local luatex=data.luatex
+ if not luatex then luatex={} data.luatex=luatex end
+ local indices,unicodes,multiples,internals={},{},{},{}
+ local glyphs=data.glyphs
+ local mapmap=data.map
+ if not mapmap then
+ logs.report("load otf","no map in %s",filename)
+ mapmap={}
+ data.map={ map=mapmap }
+ elseif not mapmap.map then
+ logs.report("load otf","no unicode map in %s",filename)
+ mapmap={}
+ data.map.map=mapmap
+ else
+ mapmap=mapmap.map
+ end
+ local criterium=fonts.private
+ local private=fonts.private
+ for index,glyph in next,glyphs do
+ if index>0 then
+ local name=glyph.name
+ if name then
+ local unicode=glyph.unicode
+ if unicode==-1 or unicode>=criterium then
+ glyph.unicode=private
+ indices[private]=index
+ unicodes[name]=private
+ internals[index]=true
+ if trace_private then
+ logs.report("load otf","enhance: glyph %s at index U+%04X is moved to private unicode slot U+%04X",name,index,private)
+ end
+ private=private+1
+ else
+ indices[unicode]=index
+ unicodes[name]=unicode
+ end
+ end
+ end
+ end
+ for unicode,index in next,mapmap do
+ if not internals[index] then
+ local name=glyphs[index].name
+ if name then
+ local un=unicodes[name]
+ if not un then
+ unicodes[name]=unicode
+ elseif type(un)=="number" then
+ if un~=unicode then
+ multiples[#multiples+1]=name
+ unicodes[name]={ un,unicode }
+ indices[unicode]=index
+ end
+ else
+ local ok=false
+ for u=1,#un do
+ if un[u]==unicode then
+ ok=true
+ break
+ end
+ end
+ if not ok then
+ multiples[#multiples+1]=name
+ un[#un+1]=unicode
+ indices[unicode]=index
+ end
+ end
+ end
+ end
+ end
+ if trace_loading then
+ if #multiples>0 then
+ logs.report("load otf","%s glyph are reused: %s",#multiples,concat(multiples," "))
+ else
+ logs.report("load otf","no glyph are reused")
+ end
+ end
+ luatex.indices=indices
+ luatex.unicodes=unicodes
+ luatex.private=private
+end
+otf.enhancers["cleanup ttf tables"]=function(data,filename)
+ local ttf_tables=data.ttf_tables
+ if ttf_tables then
+ for k=1,#ttf_tables do
+ if ttf_tables[k].data then ttf_tables[k].data="deleted" end
+ end
+ end
+ data.ttf_tab_saved=nil
+end
+otf.enhancers["compact glyphs"]=function(data,filename)
+ table.compact(data.glyphs)
+ if data.subfonts then
+ for _,subfont in next,data.subfonts do
+ table.compact(subfont.glyphs)
+ end
+ end
+end
+otf.enhancers["reverse coverage"]=function(data,filename)
+ if data.lookups then
+ for _,v in next,data.lookups do
+ if v.rules then
+ for _,vv in next,v.rules do
+ local c=vv.coverage
+ if c and c.before then
+ c.before=table.reverse(c.before)
+ end
+ end
+ end
+ end
+ end
+end
+otf.enhancers["check italic correction"]=function(data,filename)
+ local glyphs=data.glyphs
+ local ok=false
+ for index,glyph in next,glyphs do
+ local ic=glyph.italic_correction
+ if ic then
+ if ic~=0 then
+ glyph.italic=ic
+ end
+ glyph.italic_correction=nil
+ ok=true
+ end
+ end
+ otf.tables.valid_fields[#otf.tables.valid_fields+1]="has_italic"
+ data.has_italic=true
+end
+otf.enhancers["check math"]=function(data,filename)
+ if data.math then
+ local glyphs=data.glyphs
+ local unicodes=data.luatex.unicodes
+ for index,glyph in next,glyphs do
+ local mk=glyph.mathkern
+ local hv=glyph.horiz_variants
+ local vv=glyph.vert_variants
+ if mk or hv or vv then
+ local math={}
+ glyph.math=math
+ if mk then
+ for k,v in next,mk do
+ if not next(v) then
+ mk[k]=nil
+ end
+ end
+ math.kerns=mk
+ glyph.mathkern=nil
+ end
+ if hv then
+ math.horiz_variants=hv.variants
+ local p=hv.parts
+ if p and #p>0 then
+ for i=1,#p do
+ local pi=p[i]
+ pi.glyph=unicodes[pi.component] or 0
+ end
+ math.horiz_parts=p
+ end
+ local ic=hv.italic_correction
+ if ic and ic~=0 then
+ math.horiz_italic_correction=ic
+ end
+ glyph.horiz_variants=nil
+ end
+ if vv then
+ local uc=unicodes[index]
+ math.vert_variants=vv.variants
+ local p=vv.parts
+ if p and #p>0 then
+ for i=1,#p do
+ local pi=p[i]
+ pi.glyph=unicodes[pi.component] or 0
+ end
+ math.vert_parts=p
+ end
+ local ic=vv.italic_correction
+ if ic and ic~=0 then
+ math.vert_italic_correction=ic
+ end
+ glyph.vert_variants=nil
+ end
+ local ic=glyph.italic_correction
+ if ic then
+ if ic~=0 then
+ math.italic_correction=ic
+ end
+ glyph.italic_correction=nil
+ end
+ end
+ end
+ end
+end
+otf.enhancers["share widths"]=function(data,filename)
+ local glyphs=data.glyphs
+ local widths={}
+ for index,glyph in next,glyphs do
+ local width=glyph.width
+ widths[width]=(widths[width] or 0)+1
+ end
+ local wd,most=0,1
+ for k,v in next,widths do
+ if v>most then
+ wd,most=k,v
+ end
+ end
+ if most>1000 then
+ if trace_loading then
+ logs.report("load otf","most common width: %s (%s times), sharing (cjk font)",wd,most)
+ end
+ for k,v in next,glyphs do
+ if v.width==wd then
+ v.width=nil
+ end
+ end
+ data.luatex.defaultwidth=wd
+ end
+end
+otf.enhancers["reorganize kerns"]=function(data,filename)
+ local glyphs,mapmap,unicodes=data.glyphs,data.luatex.indices,data.luatex.unicodes
+ local mkdone=false
+ local function do_it(lookup,first_unicode,kerns)
+ local glyph=glyphs[mapmap[first_unicode]]
+ if glyph then
+ local mykerns=glyph.mykerns
+ if not mykerns then
+ mykerns={}
+ glyph.mykerns=mykerns
+ end
+ local lookupkerns=mykerns[lookup]
+ if not lookupkerns then
+ lookupkerns={}
+ mykerns[lookup]=lookupkerns
+ end
+ for second_unicode,kern in next,kerns do
+ lookupkerns[second_unicode]=kern
+ end
+ elseif trace_loading then
+ logs.report("load otf","no glyph data for U+%04X",first_unicode)
+ end
+ end
+ for index,glyph in next,glyphs do
+ if glyph.kerns then
+ local mykerns={}
+ for k,v in next,glyph.kerns do
+ local vc,vo,vl=v.char,v.off,v.lookup
+ if vc and vo and vl then
+ local uvc=unicodes[vc]
+ if not uvc then
+ if trace_loading then
+ logs.report("load otf","problems with unicode %s of kern %s at glyph %s",vc,k,index)
+ end
+ else
+ if type(vl)~="table" then
+ vl={ vl }
+ end
+ for l=1,#vl do
+ local vll=vl[l]
+ local mkl=mykerns[vll]
+ if not mkl then
+ mkl={}
+ mykerns[vll]=mkl
+ end
+ if type(uvc)=="table" then
+ for u=1,#uvc do
+ mkl[uvc[u]]=vo
+ end
+ else
+ mkl[uvc]=vo
+ end
+ end
+ end
+ end
+ end
+ glyph.mykerns=mykerns
+ glyph.kerns=nil
+ mkdone=true
+ end
+ end
+ if trace_loading and mkdone then
+ logs.report("load otf","replacing 'kerns' tables by 'mykerns' tables")
+ end
+ if data.kerns then
+ if trace_loading then
+ logs.report("load otf","removing global 'kern' table")
+ end
+ data.kerns=nil
+ end
+ local dgpos=data.gpos
+ if dgpos then
+ local separator=lpeg.P(" ")
+ local other=((1-separator)^0)/unicodes
+ local splitter=lpeg.Ct(other*(separator*other)^0)
+ for gp=1,#dgpos do
+ local gpos=dgpos[gp]
+ local subtables=gpos.subtables
+ if subtables then
+ for s=1,#subtables do
+ local subtable=subtables[s]
+ local kernclass=subtable.kernclass
+ if kernclass then
+ local split={}
+ for k=1,#kernclass do
+ local kcl=kernclass[k]
+ local firsts,seconds,offsets,lookups=kcl.firsts,kcl.seconds,kcl.offsets,kcl.lookup
+ if type(lookups)~="table" then
+ lookups={ lookups }
+ end
+ local maxfirsts,maxseconds=#firsts,#seconds
+ for _,s in next,firsts do
+ split[s]=split[s] or lpegmatch(splitter,s)
+ end
+ for _,s in next,seconds do
+ 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 kerns,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
+ local second_unicode=splt[i]
+ if tonumber(second_unicode) then
+ kerns[second_unicode]=offset
+ else for s=1,#second_unicode do
+ kerns[second_unicode[s]]=offset
+ end end
+ end
+ end
+ end
+ end
+ for i=1,#splt do
+ local first_unicode=splt[i]
+ if tonumber(first_unicode) then
+ do_it(lookup,first_unicode,kerns)
+ else for f=1,#first_unicode do
+ do_it(lookup,first_unicode[f],kerns)
+ end end
+ end
+ end
+ end
+ end
+ end
+ subtable.comment="The kernclass table is merged into mykerns in the indexed glyph tables."
+ subtable.kernclass={}
+ end
+ end
+ end
+ end
+ end
+end
+otf.enhancers["strip not needed data"]=function(data,filename)
+ local verbose=fonts.verbose
+ local int_to_uni=data.luatex.unicodes
+ for k,v in next,data.glyphs do
+ local d=v.dependents
+ if d then v.dependents=nil end
+ local a=v.altuni
+ if a then v.altuni=nil end
+ if verbose then
+ local code=int_to_uni[k]
+ if code then
+ local vu=v.unicode
+ if not vu then
+ v.unicode=code
+ elseif type(vu)=="table" then
+ if vu[#vu]==code then
+ else
+ vu[#vu+1]=code
+ end
+ elseif vu~=code then
+ v.unicode={ vu,code }
+ end
+ end
+ else
+ v.unicode=nil
+ v.index=nil
+ end
+ end
+ data.luatex.comment="Glyph tables have their original index. When present, mykern tables are indexed by unicode."
+ data.map=nil
+ data.names=nil
+ data.glyphcnt=nil
+ data.glyphmax=nil
+ if true then
+ data.gpos=nil
+ data.gsub=nil
+ data.anchor_classes=nil
+ end
+end
+otf.enhancers["migrate metadata"]=function(data,filename)
+ local global_fields=otf.tables.global_fields
+ local metadata={}
+ for k,v in next,data do
+ if not global_fields[k] then
+ metadata[k]=v
+ data[k]=nil
+ end
+ end
+ data.metadata=metadata
+ local pfminfo=data.pfminfo
+ metadata.isfixedpitch=metadata.isfixedpitch or (pfminfo.panose and pfminfo.panose["proportion"]=="Monospaced")
+ metadata.charwidth=pfminfo and pfminfo.avgwidth
+end
+local private_math_parameters={
+ "FractionDelimiterSize",
+ "FractionDelimiterDisplayStyleSize",
+}
+otf.enhancers["check math parameters"]=function(data,filename)
+ local mathdata=data.metadata.math
+ if mathdata then
+ for m=1,#private_math_parameters do
+ local pmp=private_math_parameters[m]
+ if not mathdata[pmp] then
+ if trace_loading then
+ logs.report("load otf","setting math parameter '%s' to 0",pmp)
+ end
+ mathdata[pmp]=0
+ end
+ end
+ end
+end
+otf.enhancers["flatten glyph lookups"]=function(data,filename)
+ for k,v in next,data.glyphs do
+ local lookups=v.lookups
+ if lookups then
+ for kk,vv in next,lookups do
+ for kkk=1,#vv do
+ local vvv=vv[kkk]
+ local s=vvv.specification
+ if s then
+ local t=vvv.type
+ if t=="ligature" then
+ vv[kkk]={ "ligature",s.components,s.char }
+ elseif t=="alternate" then
+ vv[kkk]={ "alternate",s.components }
+ elseif t=="substitution" then
+ vv[kkk]={ "substitution",s.variant }
+ elseif t=="multiple" then
+ vv[kkk]={ "multiple",s.components }
+ elseif t=="position" then
+ vv[kkk]={ "position",{ s.x or 0,s.y or 0,s.h or 0,s.v or 0 } }
+ elseif t=="pair" then
+ local one,two,paired=s.offsets[1],s.offsets[2],s.paired or ""
+ if one then
+ if two then
+ vv[kkk]={ "pair",paired,{ one.x or 0,one.y or 0,one.h or 0,one.v or 0 },{ two.x or 0,two.y or 0,two.h or 0,two.v or 0 } }
+ else
+ vv[kkk]={ "pair",paired,{ one.x or 0,one.y or 0,one.h or 0,one.v or 0 } }
+ end
+ else
+ if two then
+ vv[kkk]={ "pair",paired,{},{ two.x or 0,two.y or 0,two.h or 0,two.v or 0} }
+ else
+ vv[kkk]={ "pair",paired }
+ end
+ end
+ else
+ if trace_loading then
+ logs.report("load otf","flattening needed, report to context list")
+ end
+ for a,b in next,s do
+ if trace_loading and vvv[a] then
+ logs.report("load otf","flattening conflict, report to context list")
+ end
+ vvv[a]=b
+ end
+ vvv.specification=nil
+ end
+ end
+ end
+ end
+ end
+ end
+end
+otf.enhancers["simplify glyph lookups"]=function(data,filename)
+ for k,v in next,data.glyphs do
+ local lookups=v.lookups
+ if lookups then
+ local slookups,mlookups
+ for kk,vv in next,lookups do
+ if #vv==1 then
+ if not slookups then
+ slookups={}
+ v.slookups=slookups
+ end
+ slookups[kk]=vv[1]
+ else
+ if not mlookups then
+ mlookups={}
+ v.mlookups=mlookups
+ end
+ mlookups[kk]=vv
+ end
+ end
+ v.lookups=nil
+ end
+ end
+end
+otf.enhancers["flatten anchor tables"]=function(data,filename)
+ for k,v in next,data.glyphs do
+ if v.anchors then
+ for kk,vv in next,v.anchors do
+ for kkk,vvv in next,vv do
+ if vvv.x or vvv.y then
+ vv[kkk]={ vvv.x or 0,vvv.y or 0 }
+ else
+ for kkkk=1,#vvv do
+ local vvvv=vvv[kkkk]
+ vvv[kkkk]={ vvvv.x or 0,vvvv.y or 0 }
+ end
+ end
+ end
+ end
+ end
+ end
+end
+otf.enhancers["flatten feature tables"]=function(data,filename)
+ for _,tag in next,otf.glists do
+ if data[tag] then
+ if trace_loading then
+ logs.report("load otf","flattening %s table",tag)
+ end
+ for k,v in next,data[tag] do
+ local features=v.features
+ if features then
+ for kk=1,#features do
+ local vv=features[kk]
+ local t={}
+ local scripts=vv.scripts
+ for kkk=1,#scripts do
+ local vvv=scripts[kkk]
+ t[vvv.script]=vvv.langs
+ end
+ vv.scripts=t
+ end
+ end
+ end
+ end
+ end
+end
+otf.enhancers.patches=otf.enhancers.patches or {}
+otf.enhancers["patch bugs"]=function(data,filename)
+ local basename=file.basename(lower(filename))
+ for pattern,action in next,otf.enhancers.patches do
+ if find(basename,pattern) then
+ action(data,filename)
+ end
+ end
+end
+fonts.otf.enhancers["enrich with features"]=function(data,filename)
+end
+function otf.features.register(name,default)
+ otf.features.list[#otf.features.list+1]=name
+ otf.features.default[name]=default
+end
+function otf.set_features(tfmdata,features)
+ local processes={}
+ if features and next(features) then
+ local lists={
+ fonts.triggers,
+ fonts.processors,
+ fonts.manipulators,
+ }
+ local mode=tfmdata.mode or fonts.mode
+ local initializers=fonts.initializers
+ local fi=initializers[mode]
+ if fi then
+ local fiotf=fi.otf
+ if fiotf then
+ local done={}
+ for l=1,4 do
+ local list=lists[l]
+ if list then
+ for i=1,#list do
+ local f=list[i]
+ local value=features[f]
+ if value and fiotf[f] then
+ if not done[f] then
+ if trace_features then
+ logs.report("define otf","initializing feature %s to %s for mode %s for font %s",f,tostring(value),mode or 'unknown',tfmdata.fullname or 'unknown')
+ end
+ fiotf[f](tfmdata,value)
+ mode=tfmdata.mode or fonts.mode
+ local im=initializers[mode]
+ if im then
+ fiotf=initializers[mode].otf
+ end
+ done[f]=true
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+ local fm=fonts.methods[mode]
+ if fm then
+ local fmotf=fm.otf
+ if fmotf then
+ for l=1,4 do
+ local list=lists[l]
+ if list then
+ for i=1,#list do
+ local f=list[i]
+ if fmotf[f] then
+ if trace_features then
+ logs.report("define otf","installing feature handler %s for mode %s for font %s",f,mode or 'unknown',tfmdata.fullname or 'unknown')
+ end
+ processes[#processes+1]=fmotf[f]
+ end
+ end
+ end
+ end
+ end
+ else
+ end
+ end
+ return processes,features
+end
+function otf.otf_to_tfm(specification)
+ local name=specification.name
+ local sub=specification.sub
+ local filename=specification.filename
+ local format=specification.format
+ local features=specification.features.normal
+ local cache_id=specification.hash
+ local tfmdata=containers.read(tfm.cache,cache_id)
+ if not tfmdata then
+ local otfdata=otf.load(filename,format,sub,features and features.featurefile)
+ if otfdata and next(otfdata) then
+ otfdata.shared=otfdata.shared or {
+ featuredata={},
+ anchorhash={},
+ initialized=false,
+ }
+ tfmdata=otf.copy_to_tfm(otfdata,cache_id)
+ if tfmdata and next(tfmdata) then
+ tfmdata.unique=tfmdata.unique or {}
+ tfmdata.shared=tfmdata.shared or {}
+ local shared=tfmdata.shared
+ shared.otfdata=otfdata
+ shared.features=features
+ shared.dynamics={}
+ shared.processes={}
+ shared.set_dynamics=otf.set_dynamics
+ tfmdata.luatex=otfdata.luatex
+ tfmdata.indices=otfdata.luatex.indices
+ tfmdata.unicodes=otfdata.luatex.unicodes
+ tfmdata.marks=otfdata.luatex.marks
+ tfmdata.originals=otfdata.luatex.originals
+ tfmdata.changed={}
+ tfmdata.has_italic=otfdata.metadata.has_italic
+ if not tfmdata.language then tfmdata.language='dflt' end
+ if not tfmdata.script then tfmdata.script='dflt' end
+ shared.processes,shared.features=otf.set_features(tfmdata,fonts.define.check(features,otf.features.default))
+ end
+ end
+ containers.write(tfm.cache,cache_id,tfmdata)
+ end
+ return tfmdata
+end
+fonts.formats.dfont="truetype"
+fonts.formats.ttc="truetype"
+fonts.formats.ttf="truetype"
+fonts.formats.otf="opentype"
+function otf.copy_to_tfm(data,cache_id)
+ if data then
+ local glyphs,pfminfo,metadata=data.glyphs or {},data.pfminfo or {},data.metadata or {}
+ local luatex=data.luatex
+ local unicodes=luatex.unicodes
+ local indices=luatex.indices
+ local characters,parameters,math_parameters,descriptions={},{},{},{}
+ local designsize=metadata.designsize or metadata.design_size or 100
+ if designsize==0 then
+ designsize=100
+ end
+ local spaceunits,spacer=500,"space"
+ for u,i in next,indices do
+ characters[u]={}
+ descriptions[u]=glyphs[i]
+ end
+ if metadata.math then
+ for name,value in next,metadata.math do
+ math_parameters[name]=value
+ end
+ for u,char in next,characters do
+ local d=descriptions[u]
+ local m=d.math
+ if m then
+ local variants,parts,c,uc=m.horiz_variants,m.horiz_parts,char,u
+ if variants then
+ for n in gmatch(variants,"[^ ]+") do
+ local un=unicodes[n]
+ if un and uc~=un then
+ c.next=un
+ c=characters[un]
+ uc=un
+ end
+ end
+ c.horiz_variants=parts
+ elseif parts then
+ c.horiz_variants=parts
+ end
+ local variants,parts,c,uc=m.vert_variants,m.vert_parts,char,u
+ if variants then
+ for n in gmatch(variants,"[^ ]+") do
+ local un=unicodes[n]
+ if un and uc~=un then
+ c.next=un
+ c=characters[un]
+ uc=un
+ end
+ end
+ c.vert_variants=parts
+ elseif parts then
+ c.vert_variants=parts
+ end
+ local italic_correction=m.vert_italic_correction
+ if italic_correction then
+ c.vert_italic_correction=italic_correction
+ end
+ local kerns=m.kerns
+ if kerns then
+ char.mathkerns=kerns
+ end
+ end
+ end
+ end
+ local endash,emdash,space=0x20,0x2014,"space"
+ if metadata.isfixedpitch 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 metadata.charwidth then
+ spaceunits,spacer=metadata.charwidth,"charwidth"
+ end
+ else
+ if descriptions[endash] then
+ spaceunits,spacer=descriptions[endash].width,"space"
+ end
+ if not spaceunits and descriptions[emdash] then
+ spaceunits,spacer=descriptions[emdash].width/2,"emdash/2"
+ end
+ if not spaceunits and metadata.charwidth then
+ spaceunits,spacer=metadata.charwidth,"charwidth"
+ end
+ end
+ spaceunits=tonumber(spaceunits) or tfm.units/2
+ local filename=fonts.tfm.checked_filename(luatex)
+ local fontname=metadata.fontname
+ local fullname=metadata.fullname or fontname
+ local cidinfo=data.cidinfo
+ local units=metadata.units_per_em or 1000
+ cidinfo.registry=cidinfo and cidinfo.registry or ""
+ parameters.slant=0
+ parameters.space=spaceunits
+ parameters.space_stretch=units/2
+ parameters.space_shrink=1*units/3
+ parameters.x_height=2*units/5
+ parameters.quad=units
+ if spaceunits<2*units/5 then
+ end
+ local italicangle=metadata.italicangle
+ if italicangle then
+ parameters.slant=parameters.slant-math.round(math.tan(italicangle*math.pi/180))
+ end
+ if metadata.isfixedpitch then
+ parameters.space_stretch=0
+ parameters.space_shrink=0
+ elseif otf.syncspace then
+ parameters.space_stretch=spaceunits/2
+ parameters.space_shrink=spaceunits/3
+ end
+ parameters.extra_space=parameters.space_shrink
+ if pfminfo.os2_xheight and pfminfo.os2_xheight>0 then
+ parameters.x_height=pfminfo.os2_xheight
+ else
+ local x=0x78
+ if x then
+ local x=descriptions[x]
+ if x then
+ parameters.x_height=x.height
+ end
+ end
+ end
+ return {
+ characters=characters,
+ parameters=parameters,
+ math_parameters=math_parameters,
+ descriptions=descriptions,
+ indices=indices,
+ unicodes=unicodes,
+ type="real",
+ direction=0,
+ boundarychar_label=0,
+ boundarychar=65536,
+ designsize=(designsize/10)*65536,
+ spacer="500 units",
+ encodingbytes=2,
+ filename=filename,
+ fontname=fontname,
+ fullname=fullname,
+ psname=fontname or fullname,
+ name=filename or fullname,
+ units=units,
+ format=fonts.fontformat(filename,"opentype"),
+ cidinfo=cidinfo,
+ ascender=abs(metadata.ascent or 0),
+ descender=abs(metadata.descent or 0),
+ spacer=spacer,
+ italicangle=italicangle,
+ }
+ else
+ return nil
+ end
+end
+otf.features.register('mathsize')
+function tfm.read_from_open_type(specification)
+ local tfmtable=otf.otf_to_tfm(specification)
+ if tfmtable then
+ local otfdata=tfmtable.shared.otfdata
+ tfmtable.name=specification.name
+ tfmtable.sub=specification.sub
+ local s=specification.size
+ local m=otfdata.metadata.math
+ if m then
+ local f=specification.features
+ if f then
+ local f=f.normal
+ if f and f.mathsize then
+ local mathsize=specification.mathsize or 0
+ if mathsize==2 then
+ local p=m.ScriptPercentScaleDown
+ if p then
+ local ps=p*specification.textsize/100
+ if trace_math then
+ logs.report("define font","asked script size: %s, used: %s (%2.2f %%)",s,ps,(ps/s)*100)
+ end
+ s=ps
+ end
+ elseif mathsize==3 then
+ local p=m.ScriptScriptPercentScaleDown
+ if p then
+ local ps=p*specification.textsize/100
+ if trace_math then
+ logs.report("define font","asked scriptscript size: %s, used: %s (%2.2f %%)",s,ps,(ps/s)*100)
+ end
+ s=ps
+ end
+ end
+ end
+ end
+ end
+ tfmtable=tfm.scale(tfmtable,s,specification.relativeid)
+ if tfm.fontname_mode=="specification" then
+ local specname=specification.specification
+ if specname then
+ tfmtable.name=specname
+ if trace_defining then
+ logs.report("define font","overloaded fontname: '%s'",specname)
+ end
+ end
+ end
+ fonts.logger.save(tfmtable,file.extname(specification.filename),specification)
+ end
+ return tfmtable
+end
+function otf.collect_lookups(otfdata,kind,script,language)
+ local sequences=otfdata.luatex.sequences
+ if sequences then
+ local featuremap,featurelist={},{}
+ for s=1,#sequences do
+ local sequence=sequences[s]
+ local features=sequence.features
+ features=features and features[kind]
+ features=features and (features[script] or features[default] or features[wildcard])
+ features=features and (features[language] or features[default] or features[wildcard])
+ if features then
+ local subtables=sequence.subtables
+ if subtables then
+ for s=1,#subtables do
+ local ss=subtables[s]
+ if not featuremap[s] then
+ featuremap[ss]=true
+ featurelist[#featurelist+1]=ss
+ end
+ end
+ end
+ end
+ end
+ if #featurelist>0 then
+ return featuremap,featurelist
+ end
+ end
+ return nil,nil
+end
+
+end -- closure
+
+do -- begin closure to overcome local limits and interference
+
+if not modules then modules={} end modules ['font-otd']={
+ 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 trace_dynamics=false trackers.register("otf.dynamics",function(v) trace_dynamics=v end)
+fonts=fonts or {}
+fonts.otf=fonts.otf or {}
+local otf=fonts.otf
+local fontdata=fonts.ids
+otf.features=otf.features or {}
+otf.features.default=otf.features.default or {}
+local context_setups=fonts.define.specify.context_setups
+local context_numbers=fonts.define.specify.context_numbers
+local a_to_script={} otf.a_to_script=a_to_script
+local a_to_language={} otf.a_to_language=a_to_language
+function otf.set_dynamics(font,dynamics,attribute)
+ local features=context_setups[context_numbers[attribute]]
+ if features then
+ local script=features.script or 'dflt'
+ local language=features.language or 'dflt'
+ local ds=dynamics[script]
+ if not ds then
+ ds={}
+ dynamics[script]=ds
+ end
+ local dsl=ds[language]
+ if not dsl then
+ dsl={}
+ ds[language]=dsl
+ end
+ local dsla=dsl[attribute]
+ if dsla then
+ return dsla
+ else
+ local tfmdata=fontdata[font]
+ a_to_script [attribute]=script
+ a_to_language[attribute]=language
+ local saved={
+ script=tfmdata.script,
+ language=tfmdata.language,
+ mode=tfmdata.mode,
+ features=tfmdata.shared.features
+ }
+ tfmdata.mode="node"
+ tfmdata.language=language
+ tfmdata.script=script
+ tfmdata.shared.features={}
+ local set=fonts.define.check(features,otf.features.default)
+ dsla=otf.set_features(tfmdata,set)
+ if trace_dynamics then
+ logs.report("otf define","setting dynamics %s: attribute %s, script %s, language %s, set: %s",context_numbers[attribute],attribute,script,language,table.sequenced(set))
+ end
+ tfmdata.script=saved.script
+ tfmdata.language=saved.language
+ tfmdata.mode=saved.mode
+ tfmdata.shared.features=saved.features
+ dynamics[script][language][attribute]=dsla
+ return dsla
+ end
+ end
+ return nil
+end
+
+end -- closure
+
+do -- begin closure to overcome local limits and interference
+
+if not modules then modules={} end modules ['font-oti']={
+ 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 lower=string.lower
+local otf=fonts.otf
+otf.default_language='latn'
+otf.default_script='dflt'
+local languages=otf.tables.languages
+local scripts=otf.tables.scripts
+function otf.features.language(tfmdata,value)
+ if value then
+ value=lower(value)
+ if languages[value] then
+ tfmdata.language=value
+ end
+ end
+end
+function otf.features.script(tfmdata,value)
+ if value then
+ value=lower(value)
+ if scripts[value] then
+ tfmdata.script=value
+ end
+ end
+end
+function otf.features.mode(tfmdata,value)
+ if value then
+ tfmdata.mode=lower(value)
+ end
+end
+fonts.initializers.base.otf.language=otf.features.language
+fonts.initializers.base.otf.script=otf.features.script
+fonts.initializers.base.otf.mode=otf.features.mode
+fonts.initializers.base.otf.method=otf.features.mode
+fonts.initializers.node.otf.language=otf.features.language
+fonts.initializers.node.otf.script=otf.features.script
+fonts.initializers.node.otf.mode=otf.features.mode
+fonts.initializers.node.otf.method=otf.features.mode
+otf.features.register("features",true)
+table.insert(fonts.processors,"features")
+
+end -- closure
+
+do -- begin closure to overcome local limits and interference
+
+if not modules then modules={} end modules ['font-otb']={
+ 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 concat=table.concat
+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 lpegmatch=lpeg.match
+local otf=fonts.otf
+local tfm=fonts.tfm
+local trace_baseinit=false trackers.register("otf.baseinit",function(v) trace_baseinit=v end)
+local trace_singles=false trackers.register("otf.singles",function(v) trace_singles=v end)
+local trace_multiples=false trackers.register("otf.multiples",function(v) trace_multiples=v end)
+local trace_alternatives=false trackers.register("otf.alternatives",function(v) trace_alternatives=v end)
+local trace_ligatures=false trackers.register("otf.ligatures",function(v) trace_ligatures=v end)
+local trace_kerns=false trackers.register("otf.kerns",function(v) trace_kerns=v end)
+local trace_preparing=false trackers.register("otf.preparing",function(v) trace_preparing=v end)
+local wildcard="*"
+local default="dflt"
+local split_at_space=lpeg.Ct(lpeg.splitat(" "))
+local pcache,fcache={},{}
+local function gref(descriptions,n)
+ if type(n)=="number" then
+ local name=descriptions[n].name
+ if name then
+ return format("U+%04X (%s)",n,name)
+ else
+ return format("U+%04X")
+ end
+ elseif n then
+ local num,nam={},{}
+ for i=1,#n do
+ local ni=n[i]
+ num[i]=format("U+%04X",ni)
+ nam[i]=descriptions[ni].name or "?"
+ end
+ return format("%s (%s)",concat(num," "),concat(nam," "))
+ else
+ return "?"
+ end
+end
+local function cref(kind,lookupname)
+ if lookupname then
+ return format("feature %s, lookup %s",kind,lookupname)
+ else
+ return format("feature %s",kind)
+ end
+end
+local function resolve_ligatures(tfmdata,ligatures,kind)
+ kind=kind or "unknown"
+ local unicodes=tfmdata.unicodes
+ local characters=tfmdata.characters
+ local descriptions=tfmdata.descriptions
+ local changed=tfmdata.changed
+ local done={}
+ while true do
+ local ok=false
+ for k,v in next,ligatures do
+ local lig=v[1]
+ if not done[lig] then
+ local ligs=lpegmatch(split_at_space,lig)
+ if #ligs==2 then
+ local uc=v[2]
+ local c,f,s=characters[uc],ligs[1],ligs[2]
+ local uft,ust=unicodes[f] or 0,unicodes[s] or 0
+ if not uft or not ust then
+ logs.report("define otf","%s: unicode problem with base ligature %s = %s + %s",cref(kind),gref(descriptions,uc),gref(descriptions,uft),gref(descriptions,ust))
+ else
+ if type(uft)=="number" then uft={ uft } end
+ if type(ust)=="number" then ust={ ust } end
+ for ufi=1,#uft do
+ local uf=uft[ufi]
+ for usi=1,#ust do
+ local us=ust[usi]
+ if changed[uf] or changed[us] then
+ if trace_baseinit and trace_ligatures then
+ logs.report("define otf","%s: base ligature %s + %s ignored",cref(kind),gref(descriptions,uf),gref(descriptions,us))
+ end
+ else
+ local first,second=characters[uf],us
+ if first and second then
+ local t=first.ligatures
+ if not t then
+ t={}
+ first.ligatures=t
+ end
+ if type(uc)=="number" then
+ t[second]={ type=0,char=uc }
+ else
+ t[second]={ type=0,char=uc[1] }
+ end
+ if trace_baseinit and trace_ligatures then
+ logs.report("define otf","%s: base ligature %s + %s => %s",cref(kind),gref(descriptions,uf),gref(descriptions,us),gref(descriptions,uc))
+ end
+ end
+ end
+ end
+ end
+ end
+ ok,done[lig]=true,descriptions[uc].name
+ end
+ end
+ end
+ if ok then
+ for d,n in next,done do
+ local pattern=pcache[d] if not pattern then pattern="^("..d..") " pcache[d]=pattern end
+ local fnc=fcache[n] if not fnc then fnc=function() return n.." " end fcache[n]=fnc end
+ for k,v in next,ligatures do
+ v[1]=gsub(v[1],pattern,fnc)
+ end
+ end
+ else
+ break
+ end
+ end
+end
+local splitter=lpeg.splitat(" ")
+local function prepare_base_substitutions(tfmdata,kind,value)
+ if value then
+ local otfdata=tfmdata.shared.otfdata
+ local validlookups,lookuplist=otf.collect_lookups(otfdata,kind,tfmdata.script,tfmdata.language)
+ if validlookups then
+ local ligatures={}
+ local unicodes=tfmdata.unicodes
+ local indices=tfmdata.indices
+ local characters=tfmdata.characters
+ local descriptions=tfmdata.descriptions
+ local changed=tfmdata.changed
+ local actions={
+ substitution=function(p,lookup,k,glyph,unicode)
+ local pv=p[2]
+ if pv then
+ local upv=unicodes[pv]
+ if upv then
+ if type(upv)=="table" then
+ upv=upv[1]
+ end
+ if characters[upv] then
+ if trace_baseinit and trace_singles then
+ logs.report("define otf","%s: base substitution %s => %s",cref(kind,lookup),gref(descriptions,k),gref(descriptions,upv))
+ end
+ changed[k]=upv
+ end
+ end
+ end
+ end,
+ alternate=function(p,lookup,k,glyph,unicode)
+ local pc=p[2]
+ if pc then
+ if value==1 then
+ pc=lpegmatch(splitter,pc)
+ elseif value==2 then
+ local a,b=lpegmatch(splitter,pc)
+ pc=b or a
+ else
+ pc={ lpegmatch(splitter,pc) }
+ pc=pc[value] or pc[#pc]
+ end
+ if pc then
+ local upc=unicodes[pc]
+ if upc then
+ if type(upc)=="table" then
+ upc=upc[1]
+ end
+ if characters[upc] then
+ if trace_baseinit and trace_alternatives then
+ logs.report("define otf","%s: base alternate %s %s => %s",cref(kind,lookup),tostring(value),gref(descriptions,k),gref(descriptions,upc))
+ end
+ changed[k]=upc
+ end
+ end
+ end
+ end
+ end,
+ ligature=function(p,lookup,k,glyph,unicode)
+ local pc=p[2]
+ if pc then
+ if trace_baseinit and trace_ligatures then
+ local upc={ lpegmatch(splitter,pc) }
+ for i=1,#upc do upc[i]=unicodes[upc[i]] end
+ logs.report("define otf","%s: base ligature %s => %s",cref(kind,lookup),gref(descriptions,upc),gref(descriptions,k))
+ end
+ ligatures[#ligatures+1]={ pc,k }
+ end
+ end,
+ }
+ for k,c in next,characters do
+ local glyph=descriptions[k]
+ local lookups=glyph.slookups
+ if lookups then
+ for l=1,#lookuplist do
+ local lookup=lookuplist[l]
+ local p=lookups[lookup]
+ if p then
+ local a=actions[p[1]]
+ if a then
+ a(p,lookup,k,glyph,unicode)
+ end
+ end
+ end
+ end
+ local lookups=glyph.mlookups
+ if lookups then
+ for l=1,#lookuplist do
+ local lookup=lookuplist[l]
+ local ps=lookups[lookup]
+ if ps then
+ for i=1,#ps do
+ local p=ps[i]
+ local a=actions[p[1]]
+ if a then
+ a(p,lookup,k,glyph,unicode)
+ end
+ end
+ end
+ end
+ end
+ end
+ resolve_ligatures(tfmdata,ligatures,kind)
+ end
+ else
+ tfmdata.ligatures=tfmdata.ligatures or {}
+ end
+end
+local function prepare_base_kerns(tfmdata,kind,value)
+ if value then
+ local otfdata=tfmdata.shared.otfdata
+ local validlookups,lookuplist=otf.collect_lookups(otfdata,kind,tfmdata.script,tfmdata.language)
+ if validlookups then
+ local unicodes=tfmdata.unicodes
+ local indices=tfmdata.indices
+ local characters=tfmdata.characters
+ local descriptions=tfmdata.descriptions
+ local sharedkerns={}
+ for u,chr in next,characters do
+ local d=descriptions[u]
+ if d then
+ local dk=d.mykerns
+ if dk then
+ local s=sharedkerns[dk]
+ if s==false then
+ elseif s then
+ chr.kerns=s
+ else
+ local t,done=chr.kerns or {},false
+ for l=1,#lookuplist do
+ local lookup=lookuplist[l]
+ local kerns=dk[lookup]
+ if kerns then
+ for k,v in next,kerns do
+ if v~=0 and not t[k] then
+ t[k],done=v,true
+ if trace_baseinit and trace_kerns then
+ logs.report("define otf","%s: base kern %s + %s => %s",cref(kind,lookup),gref(descriptions,u),gref(descriptions,k),v)
+ end
+ end
+ end
+ end
+ end
+ if done then
+ sharedkerns[dk]=t
+ chr.kerns=t
+ else
+ sharedkerns[dk]=false
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+end
+local supported_gsub={
+ 'liga','dlig','rlig','hlig',
+ 'pnum','onum','tnum','lnum',
+ 'zero',
+ 'smcp','cpsp','c2sc','ornm','aalt',
+ 'hwid','fwid',
+ 'ssty','rtlm',
+}
+local supported_gpos={
+ 'kern'
+}
+function otf.features.register_base_substitution(tag)
+ supported_gsub[#supported_gsub+1]=tag
+end
+function otf.features.register_base_kern(tag)
+ supported_gsub[#supported_gpos+1]=tag
+end
+local basehash,basehashes={},1
+function fonts.initializers.base.otf.features(tfmdata,value)
+ if true then
+ local t=trace_preparing and os.clock()
+ local features=tfmdata.shared.features
+ if features then
+ local h={}
+ for f=1,#supported_gsub do
+ local feature=supported_gsub[f]
+ local value=features[feature]
+ prepare_base_substitutions(tfmdata,feature,value)
+ if value then
+ h[#h+1]=feature.."="..tostring(value)
+ end
+ end
+ for f=1,#supported_gpos do
+ local feature=supported_gpos[f]
+ local value=features[feature]
+ prepare_base_kerns(tfmdata,feature,features[feature])
+ if value then
+ h[#h+1]=feature.."="..tostring(value)
+ end
+ end
+ local hash=concat(h," ")
+ local base=basehash[hash]
+ if not base then
+ basehashes=basehashes+1
+ base=basehashes
+ basehash[hash]=base
+ end
+ tfmdata.fullname=tfmdata.fullname.."-"..base
+ end
+ if trace_preparing then
+ logs.report("otf define","preparation time is %0.3f seconds for %s",os.clock()-t,tfmdata.fullname or "?")
+ end
+ end
+end
+
+end -- closure
+
+do -- begin closure to overcome local limits and interference
+
+if not modules then modules={} end modules ['font-otn']={
+ 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 concat,insert,remove=table.concat,table.insert,table.remove
+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 lpegmatch=lpeg.match
+local otf=fonts.otf
+local tfm=fonts.tfm
+local trace_lookups=false trackers.register("otf.lookups",function(v) trace_lookups=v end)
+local trace_singles=false trackers.register("otf.singles",function(v) trace_singles=v end)
+local trace_multiples=false trackers.register("otf.multiples",function(v) trace_multiples=v end)
+local trace_alternatives=false trackers.register("otf.alternatives",function(v) trace_alternatives=v end)
+local trace_ligatures=false trackers.register("otf.ligatures",function(v) trace_ligatures=v end)
+local trace_contexts=false trackers.register("otf.contexts",function(v) trace_contexts=v end)
+local trace_marks=false trackers.register("otf.marks",function(v) trace_marks=v end)
+local trace_kerns=false trackers.register("otf.kerns",function(v) trace_kerns=v end)
+local trace_cursive=false trackers.register("otf.cursive",function(v) trace_cursive=v end)
+local trace_preparing=false trackers.register("otf.preparing",function(v) trace_preparing=v end)
+local trace_bugs=false trackers.register("otf.bugs",function(v) trace_bugs=v end)
+local trace_details=false trackers.register("otf.details",function(v) trace_details=v end)
+local trace_applied=false trackers.register("otf.applied",function(v) trace_applied=v end)
+local trace_steps=false trackers.register("otf.steps",function(v) trace_steps=v end)
+local trace_skips=false trackers.register("otf.skips",function(v) trace_skips=v end)
+local trace_directions=false trackers.register("otf.directions",function(v) trace_directions=v end)
+trackers.register("otf.verbose_chain",function(v) otf.setcontextchain(v and "verbose") end)
+trackers.register("otf.normal_chain",function(v) otf.setcontextchain(v and "normal") end)
+trackers.register("otf.replacements","otf.singles,otf.multiples,otf.alternatives,otf.ligatures")
+trackers.register("otf.positions","otf.marks,otf.kerns,otf.cursive")
+trackers.register("otf.actions","otf.replacements,otf.positions")
+trackers.register("otf.injections","nodes.injections")
+trackers.register("*otf.sample","otf.steps,otf.actions,otf.analyzing")
+local insert_node_after=node.insert_after
+local delete_node=nodes.delete
+local copy_node=node.copy
+local find_node_tail=node.tail or node.slide
+local set_attribute=node.set_attribute
+local has_attribute=node.has_attribute
+local zwnj=0x200C
+local zwj=0x200D
+local wildcard="*"
+local default="dflt"
+local split_at_space=lpeg.splitters[" "] or lpeg.Ct(lpeg.splitat(" "))
+local glyph=node.id('glyph')
+local glue=node.id('glue')
+local kern=node.id('kern')
+local disc=node.id('disc')
+local whatsit=node.id('whatsit')
+local state=attributes.private('state')
+local markbase=attributes.private('markbase')
+local markmark=attributes.private('markmark')
+local markdone=attributes.private('markdone')
+local cursbase=attributes.private('cursbase')
+local curscurs=attributes.private('curscurs')
+local cursdone=attributes.private('cursdone')
+local kernpair=attributes.private('kernpair')
+local set_mark=nodes.set_mark
+local set_cursive=nodes.set_cursive
+local set_kern=nodes.set_kern
+local set_pair=nodes.set_pair
+local markonce=true
+local cursonce=true
+local kernonce=true
+local fontdata=fonts.ids
+otf.features.process={}
+local tfmdata=false
+local otfdata=false
+local characters=false
+local descriptions=false
+local marks=false
+local indices=false
+local unicodes=false
+local currentfont=false
+local lookuptable=false
+local anchorlookups=false
+local handlers={}
+local rlmode=0
+local featurevalue=false
+local context_setups=fonts.define.specify.context_setups
+local context_numbers=fonts.define.specify.context_numbers
+local context_merged=fonts.define.specify.context_merged
+local special_attributes={
+ init=1,
+ medi=2,
+ fina=3,
+ isol=4
+}
+local checkstep=(nodes and nodes.tracers and nodes.tracers.steppers.check) or function() end
+local registerstep=(nodes and nodes.tracers and nodes.tracers.steppers.register) or function() end
+local registermessage=(nodes and nodes.tracers and nodes.tracers.steppers.message) or function() end
+local function logprocess(...)
+ if trace_steps then
+ registermessage(...)
+ end
+ logs.report("otf direct",...)
+end
+local function logwarning(...)
+ logs.report("otf direct",...)
+end
+local function gref(n)
+ if type(n)=="number" then
+ local description=descriptions[n]
+ local name=description and description.name
+ if name then
+ return format("U+%04X (%s)",n,name)
+ else
+ return format("U+%04X",n)
+ end
+ elseif not n then
+ return "<error in tracing>"
+ else
+ local num,nam={},{}
+ for i=1,#n do
+ local ni=n[i]
+ num[#num+1]=format("U+%04X",ni)
+ local dni=descriptions[ni]
+ nam[#num]=(dni and dni.name) or "?"
+ end
+ return format("%s (%s)",concat(num," "),concat(nam," "))
+ end
+end
+local function cref(kind,chainname,chainlookupname,lookupname,index)
+ if index then
+ return format("feature %s, chain %s, sub %s, lookup %s, index %s",kind,chainname,chainlookupname,lookupname,index)
+ elseif lookupname then
+ return format("feature %s, chain %s, sub %s, lookup %s",kind,chainname or "?",chainlookupname or "?",lookupname)
+ elseif chainlookupname then
+ return format("feature %s, chain %s, sub %s",kind,chainname or "?",chainlookupname)
+ elseif chainname then
+ return format("feature %s, chain %s",kind,chainname)
+ else
+ return format("feature %s",kind)
+ end
+end
+local function pref(kind,lookupname)
+ return format("feature %s, lookup %s",kind,lookupname)
+end
+local function markstoligature(kind,lookupname,start,stop,char)
+ local n=copy_node(start)
+ local keep=start
+ local current
+ current,start=insert_node_after(start,start,n)
+ local snext=stop.next
+ current.next=snext
+ if snext then
+ snext.prev=current
+ end
+ start.prev,stop.next=nil,nil
+ current.char,current.subtype,current.components=char,2,start
+ return keep
+end
+local function toligature(kind,lookupname,start,stop,char,markflag,discfound)
+ if start~=stop then
+ if discfound then
+ local lignode=copy_node(start)
+ lignode.font,lignode.char,lignode.subtype=start.font,char,2
+ local next,prev=stop.next,start.prev
+ stop.next=nil
+ lignode=node.do_ligature_n(start,stop,lignode)
+ prev.next=lignode
+ if next then
+ next.prev=lignode
+ end
+ lignode.next,lignode.prev=next,prev
+ start=lignode
+ else
+ local deletemarks=markflag~="mark"
+ local n=copy_node(start)
+ local current
+ current,start=insert_node_after(start,start,n)
+ local snext=stop.next
+ current.next=snext
+ if snext then
+ snext.prev=current
+ end
+ start.prev,stop.next=nil,nil
+ current.char,current.subtype,current.components=char,2,start
+ local head=current
+ if deletemarks then
+ if trace_marks then
+ while start do
+ if marks[start.char] then
+ logwarning("%s: remove mark %s",pref(kind,lookupname),gref(start.char))
+ end
+ start=start.next
+ end
+ end
+ else
+ local i=0
+ while start do
+ if marks[start.char] then
+ set_attribute(start,markdone,i)
+ if trace_marks then
+ logwarning("%s: keep mark %s, gets index %s",pref(kind,lookupname),gref(start.char),i)
+ end
+ head,current=insert_node_after(head,current,copy_node(start))
+ else
+ i=i+1
+ end
+ start=start.next
+ end
+ start=current.next
+ while start and start.id==glyph do
+ if marks[start.char] then
+ set_attribute(start,markdone,i)
+ if trace_marks then
+ logwarning("%s: keep mark %s, gets index %s",pref(kind,lookupname),gref(start.char),i)
+ end
+ else
+ break
+ end
+ start=start.next
+ end
+ end
+ return head
+ end
+ else
+ start.char=char
+ end
+ return start
+end
+function handlers.gsub_single(start,kind,lookupname,replacement)
+ if trace_singles then
+ logprocess("%s: replacing %s by single %s",pref(kind,lookupname),gref(start.char),gref(replacement))
+ end
+ start.char=replacement
+ return start,true
+end
+local function alternative_glyph(start,alternatives,kind,chainname,chainlookupname,lookupname)
+ local value,choice,n=featurevalue or tfmdata.shared.features[kind],nil,#alternatives
+ if value=="random" then
+ local r=math.random(1,n)
+ value,choice=format("random, choice %s",r),alternatives[r]
+ elseif value=="first" then
+ value,choice=format("first, choice %s",1),alternatives[1]
+ elseif value=="last" then
+ value,choice=format("last, choice %s",n),alternatives[n]
+ else
+ value=tonumber(value)
+ if type(value)~="number" then
+ value,choice="default, choice 1",alternatives[1]
+ elseif value>n then
+ value,choice=format("no %s variants, taking %s",value,n),alternatives[n]
+ elseif value==0 then
+ value,choice=format("choice %s (no change)",value),start.char
+ elseif value<1 then
+ value,choice=format("no %s variants, taking %s",value,1),alternatives[1]
+ else
+ value,choice=format("choice %s",value),alternatives[value]
+ end
+ end
+ if not choice then
+ logwarning("%s: no variant %s for %s",cref(kind,chainname,chainlookupname,lookupname),value,gref(start.char))
+ choice,value=start.char,format("no replacement instead of %s",value)
+ end
+ return choice,value
+end
+function handlers.gsub_alternate(start,kind,lookupname,alternative,sequence)
+ local choice,index=alternative_glyph(start,alternative,kind,lookupname)
+ if trace_alternatives then
+ logprocess("%s: replacing %s by alternative %s (%s)",pref(kind,lookupname),gref(start.char),gref(choice),index)
+ end
+ start.char=choice
+ return start,true
+end
+function handlers.gsub_multiple(start,kind,lookupname,multiple)
+ if trace_multiples then
+ logprocess("%s: replacing %s by multiple %s",pref(kind,lookupname),gref(start.char),gref(multiple))
+ end
+ start.char=multiple[1]
+ if #multiple>1 then
+ for k=2,#multiple do
+ local n=copy_node(start)
+ n.char=multiple[k]
+ local sn=start.next
+ n.next=sn
+ n.prev=start
+ if sn then
+ sn.prev=n
+ end
+ start.next=n
+ start=n
+ end
+ end
+ return start,true
+end
+function handlers.gsub_ligature(start,kind,lookupname,ligature,sequence)
+ local s,stop,discfound=start.next,nil,false
+ local startchar=start.char
+ if marks[startchar] then
+ while s do
+ local id=s.id
+ if id==glyph and s.subtype<256 then
+ if s.font==currentfont then
+ local char=s.char
+ local lg=ligature[1][char]
+ if not lg then
+ break
+ else
+ stop=s
+ ligature=lg
+ s=s.next
+ end
+ else
+ break
+ end
+ else
+ break
+ end
+ end
+ if stop and ligature[2] then
+ if trace_ligatures then
+ local stopchar=stop.char
+ start=markstoligature(kind,lookupname,start,stop,ligature[2])
+ logprocess("%s: replacing %s upto %s by ligature %s",pref(kind,lookupname),gref(startchar),gref(stopchar),gref(start.char))
+ else
+ start=markstoligature(kind,lookupname,start,stop,ligature[2])
+ end
+ return start,true
+ end
+ else
+ local skipmark=sequence.flags[1]
+ while s do
+ local id=s.id
+ if id==glyph and s.subtype<256 then
+ if s.font==currentfont then
+ local char=s.char
+ if skipmark and marks[char] then
+ s=s.next
+ else
+ local lg=ligature[1][char]
+ if not lg then
+ break
+ else
+ stop=s
+ ligature=lg
+ s=s.next
+ end
+ end
+ else
+ break
+ end
+ elseif id==disc then
+ discfound=true
+ s=s.next
+ else
+ break
+ end
+ end
+ if stop and ligature[2] then
+ if trace_ligatures then
+ local stopchar=stop.char
+ start=toligature(kind,lookupname,start,stop,ligature[2],skipmark,discfound)
+ logprocess("%s: replacing %s upto %s by ligature %s",pref(kind,lookupname),gref(startchar),gref(stopchar),gref(start.char))
+ else
+ start=toligature(kind,lookupname,start,stop,ligature[2],skipmark,discfound)
+ end
+ return start,true
+ end
+ end
+ return start,false
+end
+function handlers.gpos_mark2base(start,kind,lookupname,markanchors,sequence)
+ local markchar=start.char
+ if marks[markchar] then
+ local base=start.prev
+ if base and base.id==glyph and base.subtype<256 and base.font==currentfont then
+ local basechar=base.char
+ if marks[basechar] then
+ while true do
+ base=base.prev
+ if base and base.id==glyph and base.subtype<256 and base.font==currentfont then
+ basechar=base.char
+ if not marks[basechar] then
+ break
+ end
+ else
+ if trace_bugs then
+ logwarning("%s: no base for mark %s",pref(kind,lookupname),gref(markchar))
+ end
+ return start,false
+ end
+ end
+ end
+ local baseanchors=descriptions[basechar]
+ if baseanchors then
+ baseanchors=baseanchors.anchors
+ end
+ if baseanchors then
+ local baseanchors=baseanchors['basechar']
+ if baseanchors then
+ local al=anchorlookups[lookupname]
+ for anchor,ba in next,baseanchors do
+ if al[anchor] then
+ local ma=markanchors[anchor]
+ if ma then
+ local dx,dy,bound=set_mark(start,base,tfmdata.factor,rlmode,ba,ma)
+ if trace_marks then
+ logprocess("%s, anchor %s, bound %s: anchoring mark %s to basechar %s => (%s,%s)",
+ pref(kind,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy)
+ end
+ return start,true
+ end
+ end
+ end
+ if trace_bugs then
+ logwarning("%s, no matching anchors for mark %s and base %s",pref(kind,lookupname),gref(markchar),gref(basechar))
+ end
+ end
+ else
+ fonts.register_message(currentfont,basechar,"no base anchors")
+ end
+ elseif trace_bugs then
+ logwarning("%s: prev node is no char",pref(kind,lookupname))
+ end
+ elseif trace_bugs then
+ logwarning("%s: mark %s is no mark",pref(kind,lookupname),gref(markchar))
+ end
+ return start,false
+end
+function handlers.gpos_mark2ligature(start,kind,lookupname,markanchors,sequence)
+ local markchar=start.char
+ if marks[markchar] then
+ local base=start.prev
+ local index=1
+ if base and base.id==glyph and base.subtype<256 and base.font==currentfont then
+ local basechar=base.char
+ if marks[basechar] then
+ index=index+1
+ while true do
+ base=base.prev
+ if base and base.id==glyph and base.subtype<256 and base.font==currentfont then
+ basechar=base.char
+ if marks[basechar] then
+ index=index+1
+ else
+ break
+ end
+ else
+ if trace_bugs then
+ logwarning("%s: no base for mark %s",pref(kind,lookupname),gref(markchar))
+ end
+ return start,false
+ end
+ end
+ end
+ local i=has_attribute(start,markdone)
+ if i then index=i end
+ local baseanchors=descriptions[basechar]
+ if baseanchors then
+ baseanchors=baseanchors.anchors
+ if baseanchors then
+ local baseanchors=baseanchors['baselig']
+ if baseanchors then
+ local al=anchorlookups[lookupname]
+ for anchor,ba in next,baseanchors do
+ if al[anchor] then
+ local ma=markanchors[anchor]
+ if ma then
+ ba=ba[index]
+ if ba then
+ local dx,dy,bound=set_mark(start,base,tfmdata.factor,rlmode,ba,ma,index)
+ if trace_marks then
+ logprocess("%s, anchor %s, index %s, bound %s: anchoring mark %s to baselig %s at index %s => (%s,%s)",
+ pref(kind,lookupname),anchor,index,bound,gref(markchar),gref(basechar),index,dx,dy)
+ end
+ return start,true
+ end
+ end
+ end
+ end
+ if trace_bugs then
+ logwarning("%s: no matching anchors for mark %s and baselig %s",pref(kind,lookupname),gref(markchar),gref(basechar))
+ end
+ end
+ end
+ else
+ fonts.register_message(currentfont,basechar,"no base anchors")
+ end
+ elseif trace_bugs then
+ logwarning("%s: prev node is no char",pref(kind,lookupname))
+ end
+ elseif trace_bugs then
+ logwarning("%s: mark %s is no mark",pref(kind,lookupname),gref(markchar))
+ end
+ return start,false
+end
+function handlers.gpos_mark2mark(start,kind,lookupname,markanchors,sequence)
+ local markchar=start.char
+ if marks[markchar] then
+ local base=start.prev
+ if base and base.id==glyph and base.subtype<256 and base.font==currentfont then
+ local basechar=base.char
+ local baseanchors=descriptions[basechar]
+ if baseanchors then
+ baseanchors=baseanchors.anchors
+ if baseanchors then
+ baseanchors=baseanchors['basemark']
+ if baseanchors then
+ local al=anchorlookups[lookupname]
+ for anchor,ba in next,baseanchors do
+ if al[anchor] then
+ local ma=markanchors[anchor]
+ if ma then
+ local dx,dy,bound=set_mark(start,base,tfmdata.factor,rlmode,ba,ma)
+ if trace_marks then
+ logprocess("%s, anchor %s, bound %s: anchoring mark %s to basemark %s => (%s,%s)",
+ pref(kind,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy)
+ end
+ return start,true
+ end
+ end
+ end
+ if trace_bugs then
+ logwarning("%s: no matching anchors for mark %s and basemark %s",pref(kind,lookupname),gref(markchar),gref(basechar))
+ end
+ end
+ end
+ else
+ fonts.register_message(currentfont,basechar,"no base anchors")
+ end
+ elseif trace_bugs then
+ logwarning("%s: prev node is no mark",pref(kind,lookupname))
+ end
+ elseif trace_bugs then
+ logwarning("%s: mark %s is no mark",pref(kind,lookupname),gref(markchar))
+ end
+ return start,false
+end
+function handlers.gpos_cursive(start,kind,lookupname,exitanchors,sequence)
+ local alreadydone=cursonce and has_attribute(start,cursbase)
+ if not alreadydone then
+ local done=false
+ local startchar=start.char
+ if marks[startchar] then
+ if trace_cursive then
+ logprocess("%s: ignoring cursive for mark %s",pref(kind,lookupname),gref(startchar))
+ end
+ else
+ local nxt=start.next
+ while not done and nxt and nxt.id==glyph and nxt.subtype<256 and nxt.font==currentfont do
+ local nextchar=nxt.char
+ if marks[nextchar] then
+ nxt=nxt.next
+ else
+ local entryanchors=descriptions[nextchar]
+ if entryanchors then
+ entryanchors=entryanchors.anchors
+ if entryanchors then
+ entryanchors=entryanchors['centry']
+ if entryanchors then
+ local al=anchorlookups[lookupname]
+ for anchor,entry in next,entryanchors do
+ if al[anchor] then
+ local exit=exitanchors[anchor]
+ if exit then
+ local dx,dy,bound=set_cursive(start,nxt,tfmdata.factor,rlmode,exit,entry,characters[startchar],characters[nextchar])
+ if trace_cursive then
+ logprocess("%s: moving %s to %s cursive (%s,%s) using anchor %s and bound %s in rlmode %s",pref(kind,lookupname),gref(startchar),gref(nextchar),dx,dy,anchor,bound,rlmode)
+ end
+ done=true
+ break
+ end
+ end
+ end
+ end
+ end
+ else
+ fonts.register_message(currentfont,startchar,"no entry anchors")
+ end
+ break
+ end
+ end
+ end
+ return start,done
+ else
+ if trace_cursive and trace_details then
+ logprocess("%s, cursive %s is already done",pref(kind,lookupname),gref(start.char),alreadydone)
+ end
+ return start,false
+ end
+end
+function handlers.gpos_single(start,kind,lookupname,kerns,sequence)
+ local startchar=start.char
+ local dx,dy,w,h=set_pair(start,tfmdata.factor,rlmode,sequence.flags[4],kerns,characters[startchar])
+ if trace_kerns then
+ logprocess("%s: shifting single %s by (%s,%s) and correction (%s,%s)",pref(kind,lookupname),gref(startchar),dx,dy,w,h)
+ end
+ return start,false
+end
+function handlers.gpos_pair(start,kind,lookupname,kerns,sequence)
+ local snext=start.next
+ if not snext then
+ return start,false
+ else
+ local prev,done=start,false
+ local factor=tfmdata.factor
+ while snext and snext.id==glyph and snext.subtype<256 and snext.font==currentfont do
+ local nextchar=snext.char
+local krn=kerns[nextchar]
+ if not krn and marks[nextchar] then
+ prev=snext
+ snext=snext.next
+ else
+ local krn=kerns[nextchar]
+ if not krn then
+ elseif type(krn)=="table" then
+ if krn[1]=="pair" then
+ local a,b=krn[3],krn[4]
+ if a and #a>0 then
+ local startchar=start.char
+ local x,y,w,h=set_pair(start,factor,rlmode,sequence.flags[4],a,characters[startchar])
+ if trace_kerns then
+ logprocess("%s: shifting first of pair %s and %s by (%s,%s) and correction (%s,%s)",pref(kind,lookupname),gref(startchar),gref(nextchar),x,y,w,h)
+ end
+ end
+ if b and #b>0 then
+ local startchar=start.char
+ local x,y,w,h=set_pair(snext,factor,rlmode,sequence.flags[4],b,characters[nextchar])
+ if trace_kerns then
+ logprocess("%s: shifting second of pair %s and %s by (%s,%s) and correction (%s,%s)",pref(kind,lookupname),gref(startchar),gref(nextchar),x,y,w,h)
+ end
+ end
+ else
+ logs.report("%s: check this out (old kern stuff)",pref(kind,lookupname))
+ local a,b=krn[3],krn[7]
+ if a and a~=0 then
+ local k=set_kern(snext,factor,rlmode,a)
+ if trace_kerns then
+ logprocess("%s: inserting first kern %s between %s and %s",pref(kind,lookupname),k,gref(prev.char),gref(nextchar))
+ end
+ end
+ if b and b~=0 then
+ logwarning("%s: ignoring second kern xoff %s",pref(kind,lookupname),b*factor)
+ end
+ end
+ done=true
+ elseif krn~=0 then
+ local k=set_kern(snext,factor,rlmode,krn)
+ if trace_kerns then
+ logprocess("%s: inserting kern %s between %s and %s",pref(kind,lookupname),k,gref(prev.char),gref(nextchar))
+ end
+ done=true
+ end
+ break
+ end
+ end
+ return start,done
+ end
+end
+local chainmores={}
+local chainprocs={}
+local function logprocess(...)
+ if trace_steps then
+ registermessage(...)
+ end
+ logs.report("otf subchain",...)
+end
+local function logwarning(...)
+ logs.report("otf subchain",...)
+end
+function chainmores.chainsub(start,stop,kind,chainname,currentcontext,cache,lookuplist,chainlookupname,n)
+ logprocess("%s: a direct call to chainsub cannot happen",cref(kind,chainname,chainlookupname))
+ return start,false
+end
+function chainmores.gsub_multiple(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname,n)
+ logprocess("%s: gsub_multiple not yet supported",cref(kind,chainname,chainlookupname))
+ return start,false
+end
+function chainmores.gsub_alternate(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname,n)
+ logprocess("%s: gsub_alternate not yet supported",cref(kind,chainname,chainlookupname))
+ return start,false
+end
+local function logprocess(...)
+ if trace_steps then
+ registermessage(...)
+ end
+ logs.report("otf chain",...)
+end
+local function logwarning(...)
+ logs.report("otf chain",...)
+end
+function chainprocs.chainsub(start,stop,kind,chainname,currentcontext,cache,lookuplist,chainlookupname)
+ logwarning("%s: a direct call to chainsub cannot happen",cref(kind,chainname,chainlookupname))
+ return start,false
+end
+function chainprocs.reversesub(start,stop,kind,chainname,currentcontext,cache,replacements)
+ local char=start.char
+ local replacement=replacements[char]
+ if replacement then
+ if trace_singles then
+ logprocess("%s: single reverse replacement of %s by %s",cref(kind,chainname),gref(char),gref(replacement))
+ end
+ start.char=replacement
+ return start,true
+ else
+ return start,false
+ end
+end
+local function delete_till_stop(start,stop,ignoremarks)
+ if start~=stop then
+ local done=false
+ while not done do
+ done=start==stop
+ delete_node(start,start.next)
+ end
+ end
+end
+function chainprocs.gsub_single(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname,chainindex)
+ if not chainindex then
+ delete_till_stop(start,stop)
+ end
+ local current=start
+ local subtables=currentlookup.subtables
+ while current do
+ if current.id==glyph then
+ local currentchar=current.char
+ local lookupname=subtables[1]
+ local replacement=cache.gsub_single[lookupname]
+ if not replacement then
+ if trace_bugs then
+ logwarning("%s: no single hits",cref(kind,chainname,chainlookupname,lookupname,chainindex))
+ end
+ else
+ replacement=replacement[currentchar]
+ if not replacement then
+ if trace_bugs then
+ logwarning("%s: no single for %s",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(currentchar))
+ end
+ else
+ if trace_singles then
+ logprocess("%s: replacing single %s by %s",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(currentchar),gref(replacement))
+ end
+ current.char=replacement
+ end
+ end
+ return start,true
+ elseif current==stop then
+ break
+ else
+ current=current.next
+ end
+ end
+ return start,false
+end
+chainmores.gsub_single=chainprocs.gsub_single
+function chainprocs.gsub_multiple(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname)
+ delete_till_stop(start,stop)
+ local startchar=start.char
+ local subtables=currentlookup.subtables
+ local lookupname=subtables[1]
+ local replacements=cache.gsub_multiple[lookupname]
+ if not replacements then
+ if trace_bugs then
+ logwarning("%s: no multiple hits",cref(kind,chainname,chainlookupname,lookupname))
+ end
+ else
+ replacements=replacements[startchar]
+ if not replacements then
+ if trace_bugs then
+ logwarning("%s: no multiple for %s",cref(kind,chainname,chainlookupname,lookupname),gref(startchar))
+ end
+ else
+ if trace_multiples then
+ logprocess("%s: replacing %s by multiple characters %s",cref(kind,chainname,chainlookupname,lookupname),gref(startchar),gref(replacements))
+ end
+ local sn=start.next
+ for k=1,#replacements do
+ if k==1 then
+ start.char=replacements[k]
+ else
+ local n=copy_node(start)
+ n.char=replacements[k]
+ n.next,n.prev=sn,start
+ if sn then
+ sn.prev=n
+ end
+ start.next,start=n,n
+ end
+ end
+ return start,true
+ end
+ end
+ return start,false
+end
+function chainprocs.gsub_alternate(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname)
+ delete_till_stop(start,stop)
+ local current=start
+ local subtables=currentlookup.subtables
+ while current do
+ if current.id==glyph then
+ local currentchar=current.char
+ local lookupname=subtables[1]
+ local alternatives=cache.gsub_alternate[lookupname]
+ if not alternatives then
+ if trace_bugs then
+ logwarning("%s: no alternative hits",cref(kind,chainname,chainlookupname,lookupname))
+ end
+ else
+ alternatives=alternatives[currentchar]
+ if not alternatives then
+ if trace_bugs then
+ logwarning("%s: no alternative for %s",cref(kind,chainname,chainlookupname,lookupname),gref(currentchar))
+ end
+ else
+ local choice,index=alternative_glyph(current,alternatives,kind,chainname,chainlookupname,lookupname)
+ current.char=choice
+ if trace_alternatives then
+ logprocess("%s: replacing single %s by alternative %s (%s)",cref(kind,chainname,chainlookupname,lookupname),index,gref(currentchar),gref(choice),index)
+ end
+ end
+ end
+ return start,true
+ elseif current==stop then
+ break
+ else
+ current=current.next
+ end
+ end
+ return start,false
+end
+function chainprocs.gsub_ligature(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname,chainindex)
+ local startchar=start.char
+ local subtables=currentlookup.subtables
+ local lookupname=subtables[1]
+ local ligatures=cache.gsub_ligature[lookupname]
+ if not ligatures then
+ if trace_bugs then
+ logwarning("%s: no ligature hits",cref(kind,chainname,chainlookupname,lookupname,chainindex))
+ end
+ else
+ ligatures=ligatures[startchar]
+ if not ligatures then
+ if trace_bugs then
+ logwarning("%s: no ligatures starting with %s",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar))
+ end
+ else
+ local s,discfound,last,nofreplacements=start.next,false,stop,0
+ while s do
+ local id=s.id
+ if id==disc then
+ s=s.next
+ discfound=true
+ else
+ local schar=s.char
+ if marks[schar] then
+ s=s.next
+ else
+ local lg=ligatures[1][schar]
+ if not lg then
+ break
+ else
+ ligatures,last,nofreplacements=lg,s,nofreplacements+1
+ if s==stop then
+ break
+ else
+ s=s.next
+ end
+ end
+ end
+ end
+ end
+ local l2=ligatures[2]
+ if l2 then
+ if chainindex then
+ stop=last
+ end
+ if trace_ligatures then
+ if start==stop then
+ logprocess("%s: replacing character %s by ligature %s",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar),gref(l2))
+ else
+ logprocess("%s: replacing character %s upto %s by ligature %s",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar),gref(stop.char),gref(l2))
+ end
+ end
+ start=toligature(kind,lookupname,start,stop,l2,currentlookup.flags[1],discfound)
+ return start,true,nofreplacements
+ elseif trace_bugs then
+ if start==stop then
+ logwarning("%s: replacing character %s by ligature fails",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar))
+ else
+ logwarning("%s: replacing character %s upto %s by ligature fails",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar),gref(stop.char))
+ end
+ end
+ end
+ end
+ return start,false,0
+end
+chainmores.gsub_ligature=chainprocs.gsub_ligature
+function chainprocs.gpos_mark2base(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname)
+ local markchar=start.char
+ if marks[markchar] then
+ local subtables=currentlookup.subtables
+ local lookupname=subtables[1]
+ local markanchors=cache.gpos_mark2base[lookupname]
+ if markanchors then
+ markanchors=markanchors[markchar]
+ end
+ if markanchors then
+ local base=start.prev
+ if base and base.id==glyph and base.subtype<256 and base.font==currentfont then
+ local basechar=base.char
+ if marks[basechar] then
+ while true do
+ base=base.prev
+ if base and base.id==glyph and base.subtype<256 and base.font==currentfont then
+ basechar=base.char
+ if not marks[basechar] then
+ break
+ end
+ else
+ if trace_bugs then
+ logwarning("%s: no base for mark %s",pref(kind,lookupname),gref(markchar))
+ end
+ return start,false
+ end
+ end
+ end
+ local baseanchors=descriptions[basechar].anchors
+ if baseanchors then
+ local baseanchors=baseanchors['basechar']
+ if baseanchors then
+ local al=anchorlookups[lookupname]
+ for anchor,ba in next,baseanchors do
+ if al[anchor] then
+ local ma=markanchors[anchor]
+ if ma then
+ local dx,dy,bound=set_mark(start,base,tfmdata.factor,rlmode,ba,ma)
+ if trace_marks then
+ logprocess("%s, anchor %s, bound %s: anchoring mark %s to basechar %s => (%s,%s)",
+ cref(kind,chainname,chainlookupname,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy)
+ end
+ return start,true
+ end
+ end
+ end
+ if trace_bugs then
+ logwarning("%s, no matching anchors for mark %s and base %s",cref(kind,chainname,chainlookupname,lookupname),gref(markchar),gref(basechar))
+ end
+ end
+ end
+ elseif trace_bugs then
+ logwarning("%s: prev node is no char",cref(kind,chainname,chainlookupname,lookupname))
+ end
+ elseif trace_bugs then
+ logwarning("%s: mark %s has no anchors",cref(kind,chainname,chainlookupname,lookupname),gref(markchar))
+ end
+ elseif trace_bugs then
+ logwarning("%s: mark %s is no mark",cref(kind,chainname,chainlookupname),gref(markchar))
+ end
+ return start,false
+end
+function chainprocs.gpos_mark2ligature(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname)
+ local markchar=start.char
+ if marks[markchar] then
+ local subtables=currentlookup.subtables
+ local lookupname=subtables[1]
+ local markanchors=cache.gpos_mark2ligature[lookupname]
+ if markanchors then
+ markanchors=markanchors[markchar]
+ end
+ if markanchors then
+ local base=start.prev
+ local index=1
+ if base and base.id==glyph and base.subtype<256 and base.font==currentfont then
+ local basechar=base.char
+ if marks[basechar] then
+ index=index+1
+ while true do
+ base=base.prev
+ if base and base.id==glyph and base.subtype<256 and base.font==currentfont then
+ basechar=base.char
+ if marks[basechar] then
+ index=index+1
+ else
+ break
+ end
+ else
+ if trace_bugs then
+ logwarning("%s: no base for mark %s",cref(kind,chainname,chainlookupname,lookupname),markchar)
+ end
+ return start,false
+ end
+ end
+ end
+ local i=has_attribute(start,markdone)
+ if i then index=i end
+ local baseanchors=descriptions[basechar].anchors
+ if baseanchors then
+ local baseanchors=baseanchors['baselig']
+ if baseanchors then
+ local al=anchorlookups[lookupname]
+ for anchor,ba in next,baseanchors do
+ if al[anchor] then
+ local ma=markanchors[anchor]
+ if ma then
+ ba=ba[index]
+ if ba then
+ local dx,dy,bound=set_mark(start,base,tfmdata.factor,rlmode,ba,ma,index)
+ if trace_marks then
+ logprocess("%s, anchor %s, bound %s: anchoring mark %s to baselig %s at index %s => (%s,%s)",
+ cref(kind,chainname,chainlookupname,lookupname),anchor,a or bound,gref(markchar),gref(basechar),index,dx,dy)
+ end
+ return start,true
+ end
+ end
+ end
+ end
+ if trace_bugs then
+ logwarning("%s: no matching anchors for mark %s and baselig %s",cref(kind,chainname,chainlookupname,lookupname),gref(markchar),gref(basechar))
+ end
+ end
+ end
+ elseif trace_bugs then
+ logwarning("feature %s, lookup %s: prev node is no char",kind,lookupname)
+ end
+ elseif trace_bugs then
+ logwarning("%s: mark %s has no anchors",cref(kind,chainname,chainlookupname,lookupname),gref(markchar))
+ end
+ elseif trace_bugs then
+ logwarning("%s: mark %s is no mark",cref(kind,chainname,chainlookupname),gref(markchar))
+ end
+ return start,false
+end
+function chainprocs.gpos_mark2mark(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname)
+ local markchar=start.char
+ if marks[markchar] then
+ local subtables=currentlookup.subtables
+ local lookupname=subtables[1]
+ local markanchors=cache.gpos_mark2mark[lookupname]
+ if markanchors then
+ markanchors=markanchors[markchar]
+ end
+ if markanchors then
+ local base=start.prev
+ if base and base.id==glyph and base.subtype<256 and base.font==currentfont then
+ local basechar=base.char
+ local baseanchors=descriptions[basechar].anchors
+ if baseanchors then
+ baseanchors=baseanchors['basemark']
+ if baseanchors then
+ local al=anchorlookups[lookupname]
+ for anchor,ba in next,baseanchors do
+ if al[anchor] then
+ local ma=markanchors[anchor]
+ if ma then
+ local dx,dy,bound=set_mark(start,base,tfmdata.factor,rlmode,ba,ma)
+ if trace_marks then
+ logprocess("%s, anchor %s, bound %s: anchoring mark %s to basemark %s => (%s,%s)",
+ cref(kind,chainname,chainlookupname,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy)
+ end
+ return start,true
+ end
+ end
+ end
+ if trace_bugs then
+ logwarning("%s: no matching anchors for mark %s and basemark %s",gref(kind,chainname,chainlookupname,lookupname),gref(markchar),gref(basechar))
+ end
+ end
+ end
+ elseif trace_bugs then
+ logwarning("%s: prev node is no mark",cref(kind,chainname,chainlookupname,lookupname))
+ end
+ elseif trace_bugs then
+ logwarning("%s: mark %s has no anchors",cref(kind,chainname,chainlookupname,lookupname),gref(markchar))
+ end
+ elseif trace_bugs then
+ logwarning("%s: mark %s is no mark",cref(kind,chainname,chainlookupname),gref(markchar))
+ end
+ return start,false
+end
+function chainprocs.gpos_cursive(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname)
+ local alreadydone=cursonce and has_attribute(start,cursbase)
+ if not alreadydone then
+ local startchar=start.char
+ local subtables=currentlookup.subtables
+ local lookupname=subtables[1]
+ local exitanchors=cache.gpos_cursive[lookupname]
+ if exitanchors then
+ exitanchors=exitanchors[startchar]
+ end
+ if exitanchors then
+ local done=false
+ if marks[startchar] then
+ if trace_cursive then
+ logprocess("%s: ignoring cursive for mark %s",pref(kind,lookupname),gref(startchar))
+ end
+ else
+ local nxt=start.next
+ while not done and nxt and nxt.id==glyph and nxt.subtype<256 and nxt.font==currentfont do
+ local nextchar=nxt.char
+ if marks[nextchar] then
+ nxt=nxt.next
+ else
+ local entryanchors=descriptions[nextchar]
+ if entryanchors then
+ entryanchors=entryanchors.anchors
+ if entryanchors then
+ entryanchors=entryanchors['centry']
+ if entryanchors then
+ local al=anchorlookups[lookupname]
+ for anchor,entry in next,entryanchors do
+ if al[anchor] then
+ local exit=exitanchors[anchor]
+ if exit then
+ local dx,dy,bound=set_cursive(start,nxt,tfmdata.factor,rlmode,exit,entry,characters[startchar],characters[nextchar])
+ if trace_cursive then
+ logprocess("%s: moving %s to %s cursive (%s,%s) using anchor %s and bound %s in rlmode %s",pref(kind,lookupname),gref(startchar),gref(nextchar),dx,dy,anchor,bound,rlmode)
+ end
+ done=true
+ break
+ end
+ end
+ end
+ end
+ end
+ else
+ fonts.register_message(currentfont,startchar,"no entry anchors")
+ end
+ break
+ end
+ end
+ end
+ return start,done
+ else
+ if trace_cursive and trace_details then
+ logprocess("%s, cursive %s is already done",pref(kind,lookupname),gref(start.char),alreadydone)
+ end
+ return start,false
+ end
+ end
+ return start,false
+end
+function chainprocs.gpos_single(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname,chainindex,sequence)
+ local startchar=start.char
+ local subtables=currentlookup.subtables
+ local lookupname=subtables[1]
+ local kerns=cache.gpos_single[lookupname]
+ if kerns then
+ kerns=kerns[startchar]
+ if kerns then
+ local dx,dy,w,h=set_pair(start,tfmdata.factor,rlmode,sequence.flags[4],kerns,characters[startchar])
+ if trace_kerns then
+ logprocess("%s: shifting single %s by (%s,%s) and correction (%s,%s)",cref(kind,chainname,chainlookupname),gref(startchar),dx,dy,w,h)
+ end
+ end
+ end
+ return start,false
+end
+function chainprocs.gpos_pair(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname,chainindex,sequence)
+ local snext=start.next
+ if snext then
+ local startchar=start.char
+ local subtables=currentlookup.subtables
+ local lookupname=subtables[1]
+ local kerns=cache.gpos_pair[lookupname]
+ if kerns then
+ kerns=kerns[startchar]
+ if kerns then
+ local prev,done=start,false
+ local factor=tfmdata.factor
+ while snext and snext.id==glyph and snext.subtype<256 and snext.font==currentfont do
+ local nextchar=snext.char
+ local krn=kerns[nextchar]
+ if not krn and marks[nextchar] then
+ prev=snext
+ snext=snext.next
+ else
+ if not krn then
+ elseif type(krn)=="table" then
+ if krn[1]=="pair" then
+ local a,b=krn[3],krn[4]
+ if a and #a>0 then
+ local startchar=start.char
+ local x,y,w,h=set_pair(start,factor,rlmode,sequence.flags[4],a,characters[startchar])
+ if trace_kerns then
+ logprocess("%s: shifting first of pair %s and %s by (%s,%s) and correction (%s,%s)",cref(kind,chainname,chainlookupname),gref(startchar),gref(nextchar),x,y,w,h)
+ end
+ end
+ if b and #b>0 then
+ local startchar=start.char
+ local x,y,w,h=set_pair(snext,factor,rlmode,sequence.flags[4],b,characters[nextchar])
+ if trace_kerns then
+ logprocess("%s: shifting second of pair %s and %s by (%s,%s) and correction (%s,%s)",cref(kind,chainname,chainlookupname),gref(startchar),gref(nextchar),x,y,w,h)
+ end
+ end
+ else
+ logs.report("%s: check this out (old kern stuff)",cref(kind,chainname,chainlookupname))
+ local a,b=krn[3],krn[7]
+ if a and a~=0 then
+ local k=set_kern(snext,factor,rlmode,a)
+ if trace_kerns then
+ logprocess("%s: inserting first kern %s between %s and %s",cref(kind,chainname,chainlookupname),k,gref(prev.char),gref(nextchar))
+ end
+ end
+ if b and b~=0 then
+ logwarning("%s: ignoring second kern xoff %s",cref(kind,chainname,chainlookupname),b*factor)
+ end
+ end
+ done=true
+ elseif krn~=0 then
+ local k=set_kern(snext,factor,rlmode,krn)
+ if trace_kerns then
+ logprocess("%s: inserting kern %s between %s and %s",cref(kind,chainname,chainlookupname),k,gref(prev.char),gref(nextchar))
+ end
+ done=true
+ end
+ break
+ end
+ end
+ return start,done
+ end
+ end
+ end
+ return start,false
+end
+local function show_skip(kind,chainname,char,ck,class)
+ if ck[9] then
+ logwarning("%s: skipping char %s (%s) in rule %s, lookuptype %s (%s=>%s)",cref(kind,chainname),gref(char),class,ck[1],ck[2],ck[9],ck[10])
+ else
+ logwarning("%s: skipping char %s (%s) in rule %s, lookuptype %s",cref(kind,chainname),gref(char),class,ck[1],ck[2])
+ end
+end
+local function normal_handle_contextchain(start,kind,chainname,contexts,sequence,cache)
+ local flags,done=sequence.flags,false
+ local skipmark,skipligature,skipbase=flags[1],flags[2],flags[3]
+ local someskip=skipmark or skipligature or skipbase
+ local markclass=sequence.markclass
+ local skipped=false
+ for k=1,#contexts do
+ local match,current,last=true,start,start
+ local ck=contexts[k]
+ local seq=ck[3]
+ local s=#seq
+ if s==1 then
+ match=current.id==glyph and current.subtype<256 and current.font==currentfont and seq[1][current.char]
+ else
+ local f,l=ck[4],ck[5]
+ if f==l then
+ match=true
+ else
+ local n=f+1
+ last=last.next
+ while n<=l do
+ if last then
+ local id=last.id
+ if id==glyph then
+ if last.subtype<256 and last.font==currentfont then
+ local char=last.char
+ local ccd=descriptions[char]
+ if ccd then
+ local class=ccd.class
+ if class==skipmark or class==skipligature or class==skipbase or (markclass and class=="mark" and not markclass[char]) then
+ skipped=true
+ if trace_skips then
+ show_skip(kind,chainname,char,ck,class)
+ end
+ last=last.next
+ elseif seq[n][char] then
+ if n<l then
+ last=last.next
+ end
+ n=n+1
+ else
+ match=false break
+ end
+ else
+ match=false break
+ end
+ else
+ match=false break
+ end
+ elseif id==disc then
+ last=last.next
+ else
+ match=false break
+ end
+ else
+ match=false break
+ end
+ end
+ end
+ if match and f>1 then
+ local prev=start.prev
+ if prev then
+ local n=f-1
+ while n>=1 do
+ if prev then
+ local id=prev.id
+ if id==glyph then
+ if prev.subtype<256 and prev.font==currentfont then
+ local char=prev.char
+ local ccd=descriptions[char]
+ if ccd then
+ local class=ccd.class
+ if class==skipmark or class==skipligature or class==skipbase or (markclass and class=="mark" and not markclass[char]) then
+ skipped=true
+ if trace_skips then
+ show_skip(kind,chainname,char,ck,class)
+ end
+ elseif seq[n][char] then
+ n=n -1
+ else
+ match=false break
+ end
+ else
+ match=false break
+ end
+ else
+ match=false break
+ end
+ elseif id==disc then
+ elseif seq[n][32] then
+ n=n -1
+ else
+ match=false break
+ end
+ prev=prev.prev
+ elseif seq[n][32] then
+ n=n -1
+ else
+ match=false break
+ end
+ end
+ elseif f==2 then
+ match=seq[1][32]
+ else
+ for n=f-1,1 do
+ if not seq[n][32] then
+ match=false break
+ end
+ end
+ end
+ end
+ if match and s>l then
+ local current=last.next
+ if current then
+ local n=l+1
+ while n<=s do
+ if current then
+ local id=current.id
+ if id==glyph then
+ if current.subtype<256 and current.font==currentfont then
+ local char=current.char
+ local ccd=descriptions[char]
+ if ccd then
+ local class=ccd.class
+ if class==skipmark or class==skipligature or class==skipbase or (markclass and class=="mark" and not markclass[char]) then
+ skipped=true
+ if trace_skips then
+ show_skip(kind,chainname,char,ck,class)
+ end
+ elseif seq[n][char] then
+ n=n+1
+ else
+ match=false break
+ end
+ else
+ match=false break
+ end
+ else
+ match=false break
+ end
+ elseif id==disc then
+ elseif seq[n][32] then
+ n=n+1
+ else
+ match=false break
+ end
+ current=current.next
+ elseif seq[n][32] then
+ n=n+1
+ else
+ match=false break
+ end
+ end
+ elseif s-l==1 then
+ match=seq[s][32]
+ else
+ for n=l+1,s do
+ if not seq[n][32] then
+ match=false break
+ end
+ end
+ end
+ end
+ end
+ if match then
+ if trace_contexts then
+ local rule,lookuptype,f,l=ck[1],ck[2],ck[4],ck[5]
+ local char=start.char
+ if ck[9] then
+ logwarning("%s: rule %s matches at char %s for (%s,%s,%s) chars, lookuptype %s (%s=>%s)",cref(kind,chainname),rule,gref(char),f-1,l-f+1,s-l,lookuptype,ck[9],ck[10])
+ else
+ logwarning("%s: rule %s matches at char %s for (%s,%s,%s) chars, lookuptype %s",cref(kind,chainname),rule,gref(char),f-1,l-f+1,s-l,lookuptype)
+ end
+ end
+ local chainlookups=ck[6]
+ if chainlookups then
+ local nofchainlookups=#chainlookups
+ if nofchainlookups==1 then
+ local chainlookupname=chainlookups[1]
+ local chainlookup=lookuptable[chainlookupname]
+ local cp=chainprocs[chainlookup.type]
+ if cp then
+ start,done=cp(start,last,kind,chainname,ck,cache,chainlookup,chainlookupname,nil,sequence)
+ else
+ logprocess("%s: %s is not yet supported",cref(kind,chainname,chainlookupname),chainlookup.type)
+ end
+ else
+ local i=1
+ repeat
+if skipped then
+ while true do
+ local char=start.char
+ local ccd=descriptions[char]
+ if ccd then
+ local class=ccd.class
+ if class==skipmark or class==skipligature or class==skipbase or (markclass and class=="mark" and not markclass[char]) then
+ start=start.next
+ else
+ break
+ end
+ else
+ break
+ end
+ end
+end
+ local chainlookupname=chainlookups[i]
+ local chainlookup=lookuptable[chainlookupname]
+ local cp=chainmores[chainlookup.type]
+ if cp then
+ local ok,n
+ start,ok,n=cp(start,last,kind,chainname,ck,cache,chainlookup,chainlookupname,i,sequence)
+ if ok then
+ done=true
+ i=i+(n or 1)
+ else
+ i=i+1
+ end
+ else
+ logprocess("%s: multiple subchains for %s are not yet supported",cref(kind,chainname,chainlookupname),chainlookup.type)
+ i=i+1
+ end
+ start=start.next
+ until i>nofchainlookups
+ end
+ else
+ local replacements=ck[7]
+ if replacements then
+ start,done=chainprocs.reversesub(start,last,kind,chainname,ck,cache,replacements)
+ else
+ done=true
+ if trace_contexts then
+ logprocess("%s: skipping match",cref(kind,chainname))
+ end
+ end
+ end
+ end
+ end
+ return start,done
+end
+local verbose_handle_contextchain=function(font,...)
+ logwarning("no verbose handler installed, reverting to 'normal'")
+ otf.setcontextchain()
+ return normal_handle_contextchain(...)
+end
+otf.chainhandlers={
+ normal=normal_handle_contextchain,
+ verbose=verbose_handle_contextchain,
+}
+function otf.setcontextchain(method)
+ if not method or method=="normal" or not otf.chainhandlers[method] then
+ if handlers.contextchain then
+ logwarning("installing normal contextchain handler")
+ end
+ handlers.contextchain=normal_handle_contextchain
+ else
+ logwarning("installing contextchain handler '%s'",method)
+ local handler=otf.chainhandlers[method]
+ handlers.contextchain=function(...)
+ return handler(currentfont,...)
+ end
+ end
+ handlers.gsub_context=handlers.contextchain
+ handlers.gsub_contextchain=handlers.contextchain
+ handlers.gsub_reversecontextchain=handlers.contextchain
+ handlers.gpos_contextchain=handlers.contextchain
+ handlers.gpos_context=handlers.contextchain
+end
+otf.setcontextchain()
+local missing={}
+local function logprocess(...)
+ if trace_steps then
+ registermessage(...)
+ end
+ logs.report("otf process",...)
+end
+local function logwarning(...)
+ logs.report("otf process",...)
+end
+local function report_missing_cache(typ,lookup)
+ local f=missing[currentfont] if not f then f={} missing[currentfont]=f end
+ local t=f[typ] if not t then t={} f[typ]=t end
+ if not t[lookup] then
+ t[lookup]=true
+ logwarning("missing cache for lookup %s of type %s in font %s (%s)",lookup,typ,currentfont,tfmdata.fullname)
+ end
+end
+local resolved={}
+function fonts.methods.node.otf.features(head,font,attr)
+ if trace_steps then
+ checkstep(head)
+ end
+ tfmdata=fontdata[font]
+ local shared=tfmdata.shared
+ otfdata=shared.otfdata
+ local luatex=otfdata.luatex
+ descriptions=tfmdata.descriptions
+ characters=tfmdata.characters
+ indices=tfmdata.indices
+ unicodes=tfmdata.unicodes
+ marks=tfmdata.marks
+ anchorlookups=luatex.lookup_to_anchor
+ currentfont=font
+ rlmode=0
+ local featuredata=otfdata.shared.featuredata
+ local sequences=luatex.sequences
+ lookuptable=luatex.lookups
+ local done=false
+ local script,language,s_enabled,a_enabled,dyn
+ local attribute_driven=attr and attr~=0
+ if attribute_driven then
+ local features=context_setups[context_numbers[attr]]
+ dyn=context_merged[attr] or 0
+ language,script=features.language or "dflt",features.script or "dflt"
+ a_enabled=features
+ if dyn==2 or dyn==-2 then
+ s_enabled=shared.features
+ end
+ else
+ language,script=tfmdata.language or "dflt",tfmdata.script or "dflt"
+ s_enabled=shared.features
+ dyn=0
+ end
+ local res=resolved[font] if not res then res={} resolved[font]=res end
+ local rs=res [script] if not rs then rs={} res [script]=rs end
+ local rl=rs [language] if not rl then rl={} rs [language]=rl end
+ local ra=rl [attr] if ra==nil then ra={} rl [attr]=ra end
+ for s=1,#sequences do
+ local pardir,txtdir,success=0,{},false
+ local sequence=sequences[s]
+ local r=ra[s]
+ if r==nil then
+ local chain=sequence.chain or 0
+ local features=sequence.features
+ if not features then
+ r=false
+ else
+ local valid,attribute,kind,what=false,false
+ for k,v in next,features do
+ local s_e=s_enabled and s_enabled[k]
+ local a_e=a_enabled and a_enabled[k]
+ if s_e or a_e then
+ local l=v[script] or v[wildcard]
+ if l then
+ if l[language] then
+ valid,what=s_e or a_e,language
+ elseif l[wildcard] then
+ valid,what=s_e or a_e,wildcard
+ end
+ if valid then
+ kind,attribute=k,special_attributes[k] or false
+ if a_e and dyn<0 then
+ valid=false
+ end
+ if trace_applied then
+ local typ,action=match(sequence.type,"(.*)_(.*)")
+ logs.report("otf node mode",
+ "%s font: %03i, dynamic: %03i, kind: %s, lookup: %3i, script: %-4s, language: %-4s (%-4s), type: %s, action: %s, name: %s",
+ (valid and "+") or "-",font,attr or 0,kind,s,script,language,what,typ,action,sequence.name)
+ end
+ break
+ end
+ end
+ end
+ end
+ if valid then
+ r={ valid,attribute,chain,kind }
+ else
+ r=false
+ end
+ end
+ ra[s]=r
+ end
+ featurevalue=r and r[1]
+ if featurevalue then
+ local attribute,chain,typ,subtables=r[2],r[3],sequence.type,sequence.subtables
+ if chain<0 then
+ local handler=handlers[typ]
+ local thecache=featuredata[typ] or {}
+ local start=find_node_tail(head)
+ while start do
+ local id=start.id
+ if id==glyph then
+ if start.subtype<256 and start.font==font then
+ local a=has_attribute(start,0)
+ if a then
+ a=a==attr
+ else
+ a=true
+ end
+ if a then
+ for i=1,#subtables do
+ local lookupname=subtables[i]
+ local lookupcache=thecache[lookupname]
+ if lookupcache then
+ local lookupmatch=lookupcache[start.char]
+ if lookupmatch then
+ start,success=handler(start,r[4],lookupname,lookupmatch,sequence,featuredata,i)
+ if success then
+ break
+ end
+ end
+ else
+ report_missing_cache(typ,lookupname)
+ end
+ end
+ if start then start=start.prev end
+ else
+ start=start.prev
+ end
+ else
+ start=start.prev
+ end
+ else
+ start=start.prev
+ end
+ end
+ else
+ local handler=handlers[typ]
+ local ns=#subtables
+ local thecache=featuredata[typ] or {}
+ local start=head
+ rlmode=0
+ if ns==1 then
+ local lookupname=subtables[1]
+ local lookupcache=thecache[lookupname]
+ if not lookupcache then
+ report_missing_cache(typ,lookupname)
+ else
+ while start do
+ local id=start.id
+ if id==glyph then
+ if start.subtype<256 and start.font==font then
+ local a=has_attribute(start,0)
+ if a then
+ a=(a==attr) and (not attribute or has_attribute(start,state,attribute))
+ else
+ a=not attribute or has_attribute(start,state,attribute)
+ end
+ if a then
+ local lookupmatch=lookupcache[start.char]
+ if lookupmatch then
+ local ok
+ start,ok=handler(start,r[4],lookupname,lookupmatch,sequence,featuredata,1)
+ if ok then
+ success=true
+ end
+ end
+ if start then start=start.next end
+ else
+ start=start.next
+ end
+ else
+ start=start.next
+ end
+ elseif id==whatsit then
+ local subtype=start.subtype
+ if subtype==7 then
+ local dir=start.dir
+ if dir=="+TRT" or dir=="+TLT" then
+ insert(txtdir,dir)
+ elseif dir=="-TRT" or dir=="-TLT" then
+ remove(txtdir)
+ end
+ local d=txtdir[#txtdir]
+ if d=="+TRT" then
+ rlmode=-1
+ elseif d=="+TLT" then
+ rlmode=1
+ else
+ rlmode=pardir
+ end
+ if trace_directions then
+ logs.report("fonts","directions after textdir %s: pardir=%s, txtdir=%s:%s, rlmode=%s",dir,pardir,#txtdir,txtdir[#txtdir] or "unset",rlmode)
+ end
+ elseif subtype==6 then
+ local dir=start.dir
+ if dir=="TRT" then
+ pardir=-1
+ elseif dir=="TLT" then
+ pardir=1
+ else
+ pardir=0
+ end
+ rlmode=pardir
+ if trace_directions then
+ logs.report("fonts","directions after pardir %s: pardir=%s, txtdir=%s:%s, rlmode=%s",dir,pardir,#txtdir,txtdir[#txtdir] or "unset",rlmode)
+ end
+ end
+ start=start.next
+ else
+ start=start.next
+ end
+ end
+ end
+ else
+ while start do
+ local id=start.id
+ if id==glyph then
+ if start.subtype<256 and start.font==font then
+ local a=has_attribute(start,0)
+ if a then
+ a=(a==attr) and (not attribute or has_attribute(start,state,attribute))
+ else
+ a=not attribute or has_attribute(start,state,attribute)
+ end
+ if a then
+ for i=1,ns do
+ local lookupname=subtables[i]
+ local lookupcache=thecache[lookupname]
+ if lookupcache then
+ local lookupmatch=lookupcache[start.char]
+ if lookupmatch then
+ local ok
+ start,ok=handler(start,r[4],lookupname,lookupmatch,sequence,featuredata,i)
+ if ok then
+ success=true
+ break
+ end
+ end
+ else
+ report_missing_cache(typ,lookupname)
+ end
+ end
+ if start then start=start.next end
+ else
+ start=start.next
+ end
+ else
+ start=start.next
+ end
+ elseif id==whatsit then
+ local subtype=start.subtype
+ if subtype==7 then
+ local dir=start.dir
+ if dir=="+TRT" or dir=="+TLT" then
+ insert(txtdir,dir)
+ elseif dir=="-TRT" or dir=="-TLT" then
+ remove(txtdir)
+ end
+ local d=txtdir[#txtdir]
+ if d=="+TRT" then
+ rlmode=-1
+ elseif d=="+TLT" then
+ rlmode=1
+ else
+ rlmode=pardir
+ end
+ if trace_directions then
+ logs.report("fonts","directions after textdir %s: pardir=%s, txtdir=%s:%s, rlmode=%s",dir,pardir,#txtdir,txtdir[#txtdir] or "unset",rlmode)
+ end
+ elseif subtype==6 then
+ local dir=start.dir
+ if dir=="TRT" then
+ pardir=-1
+ elseif dir=="TLT" then
+ pardir=1
+ else
+ pardir=0
+ end
+ rlmode=pardir
+ if trace_directions then
+ logs.report("fonts","directions after pardir %s: pardir=%s, txtdir=%s:%s, rlmode=%s",dir,pardir,#txtdir,txtdir[#txtdir] or "unset",rlmode)
+ end
+ end
+ start=start.next
+ else
+ start=start.next
+ end
+ end
+ end
+ end
+ if success then
+ done=true
+ end
+ if trace_steps then
+ registerstep(head)
+ end
+ end
+ end
+ return head,done
+end
+otf.features.prepare={}
+local function split(replacement,original,cache,unicodes)
+ local o,t,n={},{},0
+ for s in gmatch(original,"[^ ]+") do
+ local us=unicodes[s]
+ if type(us)=="number" then
+ o[#o+1]=us
+ else
+ o[#o+1]=us[1]
+ end
+ end
+ for s in gmatch(replacement,"[^ ]+") do
+ n=n+1
+ local us=unicodes[s]
+ if type(us)=="number" then
+ t[o[n]]=us
+ else
+ t[o[n]]=us[1]
+ end
+ end
+ return t
+end
+local function uncover(covers,result,cache,unicodes)
+ for n=1,#covers do
+ local c=covers[n]
+ local cc=cache[c]
+ if not cc then
+ local t={}
+ for s in gmatch(c,"[^ ]+") do
+ local us=unicodes[s]
+ if type(us)=="number" then
+ t[us]=true
+ else
+ for i=1,#us do
+ t[us[i]]=true
+ end
+ end
+ end
+ cache[c]=t
+ result[#result+1]=t
+ else
+ result[#result+1]=cc
+ end
+ end
+end
+local function prepare_lookups(tfmdata)
+ local otfdata=tfmdata.shared.otfdata
+ local featuredata=otfdata.shared.featuredata
+ local anchor_to_lookup=otfdata.luatex.anchor_to_lookup
+ local lookup_to_anchor=otfdata.luatex.lookup_to_anchor
+ local multiple=featuredata.gsub_multiple
+ local alternate=featuredata.gsub_alternate
+ local single=featuredata.gsub_single
+ local ligature=featuredata.gsub_ligature
+ local pair=featuredata.gpos_pair
+ local position=featuredata.gpos_single
+ local kerns=featuredata.gpos_pair
+ local mark=featuredata.gpos_mark2mark
+ local cursive=featuredata.gpos_cursive
+ local unicodes=tfmdata.unicodes
+ local indices=tfmdata.indices
+ local descriptions=tfmdata.descriptions
+ local action={
+ substitution=function(p,lookup,glyph,unicode)
+ local old,new=unicode,unicodes[p[2]]
+ if type(new)=="table" then
+ new=new[1]
+ end
+ local s=single[lookup]
+ if not s then s={} single[lookup]=s end
+ s[old]=new
+ end,
+ multiple=function (p,lookup,glyph,unicode)
+ local old,new=unicode,{}
+ local m=multiple[lookup]
+ if not m then m={} multiple[lookup]=m end
+ m[old]=new
+ for pc in gmatch(p[2],"[^ ]+") do
+ local upc=unicodes[pc]
+ if type(upc)=="number" then
+ new[#new+1]=upc
+ else
+ new[#new+1]=upc[1]
+ end
+ end
+ end,
+ alternate=function(p,lookup,glyph,unicode)
+ local old,new=unicode,{}
+ local a=alternate[lookup]
+ if not a then a={} alternate[lookup]=a end
+ a[old]=new
+ for pc in gmatch(p[2],"[^ ]+") do
+ local upc=unicodes[pc]
+ if type(upc)=="number" then
+ new[#new+1]=upc
+ else
+ new[#new+1]=upc[1]
+ end
+ end
+ end,
+ ligature=function (p,lookup,glyph,unicode)
+ local first=true
+ local t=ligature[lookup]
+ if not t then t={} ligature[lookup]=t end
+ for s in gmatch(p[2],"[^ ]+") do
+ if first then
+ local u=unicodes[s]
+ if not u then
+ logs.report("define otf","lookup %s: ligature %s => %s ignored due to invalid unicode",lookup,p[2],glyph.name)
+ break
+ elseif type(u)=="number" then
+ if not t[u] then
+ t[u]={ {} }
+ end
+ t=t[u]
+ else
+ local tt=t
+ local tu
+ for i=1,#u do
+ local u=u[i]
+ if i==1 then
+ if not t[u] then
+ t[u]={ {} }
+ end
+ tu=t[u]
+ t=tu
+ else
+ if not t[u] then
+ tt[u]=tu
+ end
+ end
+ end
+ end
+ first=false
+ else
+ s=unicodes[s]
+ local t1=t[1]
+ if not t1[s] then
+ t1[s]={ {} }
+ end
+ t=t1[s]
+ end
+ end
+ t[2]=unicode
+ end,
+ position=function(p,lookup,glyph,unicode)
+ local s=position[lookup]
+ if not s then s={} position[lookup]=s end
+ s[unicode]=p[2]
+ end,
+ pair=function(p,lookup,glyph,unicode)
+ local s=pair[lookup]
+ if not s then s={} pair[lookup]=s end
+ local others=s[unicode]
+ if not others then others={} s[unicode]=others end
+ local two=p[2]
+ local upc=unicodes[two]
+ if not upc then
+ for pc in gmatch(two,"[^ ]+") do
+ local upc=unicodes[pc]
+ if type(upc)=="number" then
+ others[upc]=p
+ else
+ for i=1,#upc do
+ others[upc[i]]=p
+ end
+ end
+ end
+ elseif type(upc)=="number" then
+ others[upc]=p
+ else
+ for i=1,#upc do
+ others[upc[i]]=p
+ end
+ end
+ end,
+ }
+ for unicode,glyph in next,descriptions do
+ local lookups=glyph.slookups
+ if lookups then
+ for lookup,p in next,lookups do
+ action[p[1]](p,lookup,glyph,unicode)
+ end
+ end
+ local lookups=glyph.mlookups
+ if lookups then
+ for lookup,whatever in next,lookups do
+ for i=1,#whatever do
+ local p=whatever[i]
+ action[p[1]](p,lookup,glyph,unicode)
+ end
+ end
+ end
+ local list=glyph.mykerns
+ if list then
+ for lookup,krn in next,list do
+ local k=kerns[lookup]
+ if not k then k={} kerns[lookup]=k end
+ k[unicode]=krn
+ end
+ end
+ local oanchor=glyph.anchors
+ if oanchor then
+ for typ,anchors in next,oanchor do
+ if typ=="mark" then
+ for name,anchor in next,anchors do
+ local lookups=anchor_to_lookup[name]
+ if lookups then
+ for lookup,_ in next,lookups do
+ local f=mark[lookup]
+ if not f then f={} mark[lookup]=f end
+ f[unicode]=anchors
+ end
+ end
+ end
+ elseif typ=="cexit" then
+ for name,anchor in next,anchors do
+ local lookups=anchor_to_lookup[name]
+ if lookups then
+ for lookup,_ in next,lookups do
+ local f=cursive[lookup]
+ if not f then f={} cursive[lookup]=f end
+ f[unicode]=anchors
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+end
+luatex=luatex or {}
+local function prepare_contextchains(tfmdata)
+ local otfdata=tfmdata.shared.otfdata
+ local lookups=otfdata.lookups
+ if lookups then
+ local featuredata=otfdata.shared.featuredata
+ local contextchain=featuredata.gsub_contextchain
+ local reversecontextchain=featuredata.gsub_reversecontextchain
+ local characters=tfmdata.characters
+ local unicodes=tfmdata.unicodes
+ local indices=tfmdata.indices
+ local cache=luatex.covers
+ if not cache then
+ cache={}
+ luatex.covers=cache
+ end
+ for lookupname,lookupdata in next,otfdata.lookups do
+ local lookuptype=lookupdata.type
+ if not lookuptype then
+ logs.report("otf process","missing lookuptype for %s",lookupname)
+ else
+ local rules=lookupdata.rules
+ if rules then
+ local fmt=lookupdata.format
+ if fmt=="coverage" then
+ if lookuptype~="chainsub" and lookuptype~="chainpos" then
+ logs.report("otf process","unsupported coverage %s for %s",lookuptype,lookupname)
+ else
+ local contexts=contextchain[lookupname]
+ if not contexts then
+ contexts={}
+ contextchain[lookupname]=contexts
+ end
+ local t={}
+ for nofrules=1,#rules do
+ local rule=rules[nofrules]
+ local coverage=rule.coverage
+ if coverage and coverage.current then
+ local current,before,after,sequence=coverage.current,coverage.before,coverage.after,{}
+ if before then
+ uncover(before,sequence,cache,unicodes)
+ end
+ local start=#sequence+1
+ uncover(current,sequence,cache,unicodes)
+ local stop=#sequence
+ if after then
+ uncover(after,sequence,cache,unicodes)
+ end
+ if sequence[1] then
+ t[#t+1]={ nofrules,lookuptype,sequence,start,stop,rule.lookups }
+ for unic,_ in next,sequence[start] do
+ local cu=contexts[unic]
+ if not cu then
+ contexts[unic]=t
+ end
+ end
+ end
+ end
+ end
+ end
+ elseif fmt=="reversecoverage" then
+ if lookuptype~="reversesub" then
+ logs.report("otf process","unsupported reverse coverage %s for %s",lookuptype,lookupname)
+ else
+ local contexts=reversecontextchain[lookupname]
+ if not contexts then
+ contexts={}
+ reversecontextchain[lookupname]=contexts
+ end
+ local t={}
+ for nofrules=1,#rules do
+ local rule=rules[nofrules]
+ local reversecoverage=rule.reversecoverage
+ if reversecoverage and reversecoverage.current then
+ local current,before,after,replacements,sequence=reversecoverage.current,reversecoverage.before,reversecoverage.after,reversecoverage.replacements,{}
+ if before then
+ uncover(before,sequence,cache,unicodes)
+ end
+ local start=#sequence+1
+ uncover(current,sequence,cache,unicodes)
+ local stop=#sequence
+ if after then
+ uncover(after,sequence,cache,unicodes)
+ end
+ if replacements then
+ replacements=split(replacements,current[1],cache,unicodes)
+ end
+ if sequence[1] then
+ t[#t+1]={ nofrules,lookuptype,sequence,start,stop,rule.lookups,replacements }
+ for unic,_ in next,sequence[start] do
+ local cu=contexts[unic]
+ if not cu then
+ contexts[unic]=t
+ end
+ end
+ end
+ end
+ end
+ end
+ elseif fmt=="glyphs" then
+ if lookuptype~="chainsub" and lookuptype~="chainpos" then
+ logs.report("otf process","unsupported coverage %s for %s",lookuptype,lookupname)
+ else
+ local contexts=contextchain[lookupname]
+ if not contexts then
+ contexts={}
+ contextchain[lookupname]=contexts
+ end
+ local t={}
+ for nofrules=1,#rules do
+ local rule=rules[nofrules]
+ local glyphs=rule.glyphs
+ if glyphs and glyphs.names then
+ local fore,back,names,sequence=glyphs.fore,glyphs.back,glyphs.names,{}
+ if fore and fore~="" then
+ fore=lpegmatch(split_at_space,fore)
+ uncover(fore,sequence,cache,unicodes)
+ end
+ local start=#sequence+1
+ names=lpegmatch(split_at_space,names)
+ uncover(names,sequence,cache,unicodes)
+ local stop=#sequence
+ if back and back~="" then
+ back=lpegmatch(split_at_space,back)
+ uncover(back,sequence,cache,unicodes)
+ end
+ if sequence[1] then
+ t[#t+1]={ nofrules,lookuptype,sequence,start,stop,rule.lookups }
+ for unic,_ in next,sequence[start] do
+ local cu=contexts[unic]
+ if not cu then
+ contexts[unic]=t
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+end
+function fonts.initializers.node.otf.features(tfmdata,value)
+ if true then
+ if not tfmdata.shared.otfdata.shared.initialized then
+ local t=trace_preparing and os.clock()
+ local otfdata=tfmdata.shared.otfdata
+ local featuredata=otfdata.shared.featuredata
+ featuredata.gsub_multiple={}
+ featuredata.gsub_alternate={}
+ featuredata.gsub_single={}
+ featuredata.gsub_ligature={}
+ featuredata.gsub_contextchain={}
+ featuredata.gsub_reversecontextchain={}
+ featuredata.gpos_pair={}
+ featuredata.gpos_single={}
+ featuredata.gpos_mark2base={}
+ featuredata.gpos_mark2ligature=featuredata.gpos_mark2base
+ featuredata.gpos_mark2mark=featuredata.gpos_mark2base
+ featuredata.gpos_cursive={}
+ featuredata.gpos_contextchain=featuredata.gsub_contextchain
+ featuredata.gpos_reversecontextchain=featuredata.gsub_reversecontextchain
+ prepare_contextchains(tfmdata)
+ prepare_lookups(tfmdata)
+ otfdata.shared.initialized=true
+ if trace_preparing then
+ logs.report("otf process","preparation time is %0.3f seconds for %s",os.clock()-t,tfmdata.fullname or "?")
+ end
+ end
+ end
+end
+
+end -- closure
+
+do -- begin closure to overcome local limits and interference
+
+if not modules then modules={} end modules ['font-ota']={
+ version=1.001,
+ comment="companion to font-otf.lua (analysing)",
+ author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright="PRAGMA ADE / ConTeXt Development Team",
+ license="see context related readme files"
+}
+local type,tostring,match,format,concat=type,tostring,string.match,string.format,table.concat
+if not trackers then trackers={ register=function() end } end
+local trace_analyzing=false trackers.register("otf.analyzing",function(v) trace_analyzing=v end)
+local trace_cjk=false trackers.register("cjk.injections",function(v) trace_cjk=v end)
+trackers.register("cjk.analyzing","otf.analyzing")
+fonts=fonts or {}
+fonts.analyzers=fonts.analyzers or {}
+fonts.analyzers.initializers=fonts.analyzers.initializers or { node={ otf={} } }
+fonts.analyzers.methods=fonts.analyzers.methods or { node={ otf={} } }
+local otf=fonts.otf
+local tfm=fonts.tfm
+local initializers=fonts.analyzers.initializers
+local methods=fonts.analyzers.methods
+local glyph=node.id('glyph')
+local glue=node.id('glue')
+local penalty=node.id('penalty')
+local set_attribute=node.set_attribute
+local has_attribute=node.has_attribute
+local traverse_id=node.traverse_id
+local traverse_node_list=node.traverse
+local fontdata=fonts.ids
+local state=attributes.private('state')
+local fcs=(fonts.color and fonts.color.set) or function() end
+local fcr=(fonts.color and fonts.color.reset) or function() end
+local a_to_script=otf.a_to_script
+local a_to_language=otf.a_to_language
+function fonts.initializers.node.otf.analyze(tfmdata,value,attr)
+ local script,language
+ if attr and attr>0 then
+ script,language=a_to_script[attr],a_to_language[attr]
+ else
+ script,language=tfmdata.script,tfmdata.language
+ end
+ local action=initializers[script]
+ if action then
+ if type(action)=="function" then
+ return action(tfmdata,value)
+ else
+ local action=action[language]
+ if action then
+ return action(tfmdata,value)
+ end
+ end
+ end
+ return nil
+end
+function fonts.methods.node.otf.analyze(head,font,attr)
+ local tfmdata=fontdata[font]
+ local script,language
+ if attr and attr>0 then
+ script,language=a_to_script[attr],a_to_language[attr]
+ else
+ script,language=tfmdata.script,tfmdata.language
+ end
+ local action=methods[script]
+ if action then
+ if type(action)=="function" then
+ return action(head,font,attr)
+ else
+ action=action[language]
+ if action then
+ return action(head,font,attr)
+ end
+ end
+ end
+ return head,false
+end
+otf.features.register("analyze",true)
+table.insert(fonts.triggers,"analyze")
+fonts.analyzers.methods.latn=fonts.analyzers.aux.setstate
+local zwnj=0x200C
+local zwj=0x200D
+local isol={
+ [0x0600]=true,[0x0601]=true,[0x0602]=true,[0x0603]=true,
+ [0x0608]=true,[0x060B]=true,[0x0621]=true,[0x0674]=true,
+ [0x06DD]=true,[zwnj]=true,
+}
+local isol_fina={
+ [0x0622]=true,[0x0623]=true,[0x0624]=true,[0x0625]=true,
+ [0x0627]=true,[0x0629]=true,[0x062F]=true,[0x0630]=true,
+ [0x0631]=true,[0x0632]=true,[0x0648]=true,[0x0671]=true,
+ [0x0672]=true,[0x0673]=true,[0x0675]=true,[0x0676]=true,
+ [0x0677]=true,[0x0688]=true,[0x0689]=true,[0x068A]=true,
+ [0x068B]=true,[0x068C]=true,[0x068D]=true,[0x068E]=true,
+ [0x068F]=true,[0x0690]=true,[0x0691]=true,[0x0692]=true,
+ [0x0693]=true,[0x0694]=true,[0x0695]=true,[0x0696]=true,
+ [0x0697]=true,[0x0698]=true,[0x0699]=true,[0x06C0]=true,
+ [0x06C3]=true,[0x06C4]=true,[0x06C5]=true,[0x06C6]=true,
+ [0x06C7]=true,[0x06C8]=true,[0x06C9]=true,[0x06CA]=true,
+ [0x06CB]=true,[0x06CD]=true,[0x06CF]=true,[0x06D2]=true,
+ [0x06D3]=true,[0x06D5]=true,[0x06EE]=true,[0x06EF]=true,
+ [0x0759]=true,[0x075A]=true,[0x075B]=true,[0x076B]=true,
+ [0x076C]=true,[0x0771]=true,[0x0773]=true,[0x0774]=true,
+ [0x0778]=true,[0x0779]=true,[0xFEF5]=true,[0xFEF7]=true,
+ [0xFEF9]=true,[0xFEFB]=true,
+}
+local isol_fina_medi_init={
+ [0x0626]=true,[0x0628]=true,[0x062A]=true,[0x062B]=true,
+ [0x062C]=true,[0x062D]=true,[0x062E]=true,[0x0633]=true,
+ [0x0634]=true,[0x0635]=true,[0x0636]=true,[0x0637]=true,
+ [0x0638]=true,[0x0639]=true,[0x063A]=true,[0x063B]=true,
+ [0x063C]=true,[0x063D]=true,[0x063E]=true,[0x063F]=true,
+ [0x0640]=true,[0x0641]=true,[0x0642]=true,[0x0643]=true,
+ [0x0644]=true,[0x0645]=true,[0x0646]=true,[0x0647]=true,
+ [0x0649]=true,[0x064A]=true,[0x066E]=true,[0x066F]=true,
+ [0x0678]=true,[0x0679]=true,[0x067A]=true,[0x067B]=true,
+ [0x067C]=true,[0x067D]=true,[0x067E]=true,[0x067F]=true,
+ [0x0680]=true,[0x0681]=true,[0x0682]=true,[0x0683]=true,
+ [0x0684]=true,[0x0685]=true,[0x0686]=true,[0x0687]=true,
+ [0x069A]=true,[0x069B]=true,[0x069C]=true,[0x069D]=true,
+ [0x069E]=true,[0x069F]=true,[0x06A0]=true,[0x06A1]=true,
+ [0x06A2]=true,[0x06A3]=true,[0x06A4]=true,[0x06A5]=true,
+ [0x06A6]=true,[0x06A7]=true,[0x06A8]=true,[0x06A9]=true,
+ [0x06AA]=true,[0x06AB]=true,[0x06AC]=true,[0x06AD]=true,
+ [0x06AE]=true,[0x06AF]=true,[0x06B0]=true,[0x06B1]=true,
+ [0x06B2]=true,[0x06B3]=true,[0x06B4]=true,[0x06B5]=true,
+ [0x06B6]=true,[0x06B7]=true,[0x06B8]=true,[0x06B9]=true,
+ [0x06BA]=true,[0x06BB]=true,[0x06BC]=true,[0x06BD]=true,
+ [0x06BE]=true,[0x06BF]=true,[0x06C1]=true,[0x06C2]=true,
+ [0x06CC]=true,[0x06CE]=true,[0x06D0]=true,[0x06D1]=true,
+ [0x06FA]=true,[0x06FB]=true,[0x06FC]=true,[0x06FF]=true,
+ [0x0750]=true,[0x0751]=true,[0x0752]=true,[0x0753]=true,
+ [0x0754]=true,[0x0755]=true,[0x0756]=true,[0x0757]=true,
+ [0x0758]=true,[0x075C]=true,[0x075D]=true,[0x075E]=true,
+ [0x075F]=true,[0x0760]=true,[0x0761]=true,[0x0762]=true,
+ [0x0763]=true,[0x0764]=true,[0x0765]=true,[0x0766]=true,
+ [0x0767]=true,[0x0768]=true,[0x0769]=true,[0x076A]=true,
+ [0x076D]=true,[0x076E]=true,[0x076F]=true,[0x0770]=true,
+ [0x0772]=true,[0x0775]=true,[0x0776]=true,[0x0777]=true,
+ [0x077A]=true,[0x077B]=true,[0x077C]=true,[0x077D]=true,
+ [0x077E]=true,[0x077F]=true,[zwj]=true,
+}
+local arab_warned={}
+local function warning(current,what)
+ local char=current.char
+ if not arab_warned[char] then
+ log.report("analyze","arab: character %s (U+%04X) has no %s class",char,char,what)
+ arab_warned[char]=true
+ end
+end
+function fonts.analyzers.methods.nocolor(head,font,attr)
+ for n in traverse_node_list(head,glyph) do
+ if not font or n.font==font then
+ fcr(n)
+ end
+ end
+ return head,true
+end
+local function finish(first,last)
+ if last then
+ if first==last then
+ local fc=first.char
+ if isol_fina_medi_init[fc] or isol_fina[fc] then
+ set_attribute(first,state,4)
+ if trace_analyzing then fcs(first,"font:isol") end
+ else
+ warning(first,"isol")
+ set_attribute(first,state,0)
+ if trace_analyzing then fcr(first) end
+ end
+ else
+ local lc=last.char
+ if isol_fina_medi_init[lc] or isol_fina[lc] then
+ set_attribute(last,state,3)
+ if trace_analyzing then fcs(last,"font:fina") end
+ else
+ warning(last,"fina")
+ set_attribute(last,state,0)
+ if trace_analyzing then fcr(last) end
+ end
+ end
+ first,last=nil,nil
+ elseif first then
+ local fc=first.char
+ if isol_fina_medi_init[fc] or isol_fina[fc] then
+ set_attribute(first,state,4)
+ if trace_analyzing then fcs(first,"font:isol") end
+ else
+ warning(first,"isol")
+ set_attribute(first,state,0)
+ if trace_analyzing then fcr(first) end
+ end
+ first=nil
+ end
+ return first,last
+end
+function fonts.analyzers.methods.arab(head,font,attr)
+ local tfmdata=fontdata[font]
+ local marks=tfmdata.marks
+ local first,last,current,done=nil,nil,head,false
+ while current do
+ if current.id==glyph and current.subtype<256 and current.font==font and not has_attribute(current,state) then
+ done=true
+ local char=current.char
+ if marks[char] then
+ set_attribute(current,state,5)
+ if trace_analyzing then fcs(current,"font:mark") end
+ elseif isol[char] then
+ first,last=finish(first,last)
+ set_attribute(current,state,4)
+ if trace_analyzing then fcs(current,"font:isol") end
+ first,last=nil,nil
+ elseif not first then
+ if isol_fina_medi_init[char] then
+ set_attribute(current,state,1)
+ if trace_analyzing then fcs(current,"font:init") end
+ first,last=first or current,current
+ elseif isol_fina[char] then
+ set_attribute(current,state,4)
+ if trace_analyzing then fcs(current,"font:isol") end
+ first,last=nil,nil
+ else
+ first,last=finish(first,last)
+ end
+ elseif isol_fina_medi_init[char] then
+ first,last=first or current,current
+ set_attribute(current,state,2)
+ if trace_analyzing then fcs(current,"font:medi") end
+ elseif isol_fina[char] then
+ if not has_attribute(last,state,1) then
+ set_attribute(last,state,2)
+ if trace_analyzing then fcs(last,"font:medi") end
+ end
+ set_attribute(current,state,3)
+ if trace_analyzing then fcs(current,"font:fina") end
+ first,last=nil,nil
+ elseif char>=0x0600 and char<=0x06FF then
+ if trace_analyzing then fcs(current,"font:rest") end
+ first,last=finish(first,last)
+ else
+ first,last=finish(first,last)
+ end
+ else
+ first,last=finish(first,last)
+ end
+ current=current.next
+ end
+ first,last=finish(first,last)
+ return head,done
+end
+
+end -- closure
+
+do -- begin closure to overcome local limits and interference
+
+if not modules then modules={} end modules ['font-otc']={
+ version=1.001,
+ comment="companion to font-otf.lua (context)",
+ author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright="PRAGMA ADE / ConTeXt Development Team",
+ license="see context related readme files"
+}
+local format,insert=string.format,table.insert
+local type,next=type,next
+local trace_loading=false trackers.register("otf.loading",function(v) trace_loading=v end)
+local otf=fonts.otf
+local tfm=fonts.tfm
+local extra_lists={
+ tlig={
+ {
+ endash="hyphen hyphen",
+ emdash="hyphen hyphen hyphen",
+ quotedblleft="quoteleft quoteleft",
+ quotedblright="quoteright quoteright",
+ quotedblleft="grave grave",
+ quotedblright="quotesingle quotesingle",
+ quotedblbase="comma comma",
+ exclamdown="exclam grave",
+ questiondown="question grave",
+ guillemotleft="less less",
+ guillemotright="greater greater",
+ },
+ },
+ trep={
+ {
+ [0x0022]=0x201D,
+ [0x0027]=0x2019,
+ [0x0060]=0x2018,
+ },
+ },
+ anum={
+ {
+ [0x0030]=0x0660,
+ [0x0031]=0x0661,
+ [0x0032]=0x0662,
+ [0x0033]=0x0663,
+ [0x0034]=0x0664,
+ [0x0035]=0x0665,
+ [0x0036]=0x0666,
+ [0x0037]=0x0667,
+ [0x0038]=0x0668,
+ [0x0039]=0x0669,
+ },
+ {
+ [0x0030]=0x06F0,
+ [0x0031]=0x06F1,
+ [0x0032]=0x06F2,
+ [0x0033]=0x06F3,
+ [0x0034]=0x06F4,
+ [0x0035]=0x06F5,
+ [0x0036]=0x06F6,
+ [0x0037]=0x06F7,
+ [0x0038]=0x06F8,
+ [0x0039]=0x06F9,
+ },
+ },
+}
+local extra_features={
+ tlig={
+ {
+ features={ { scripts={ { script="*",langs={ "*" },} },tag="tlig",comment="added bij mkiv" },},
+ name="ctx_tlig_1",
+ subtables={ { name="ctx_tlig_1_s" } },
+ type="gsub_ligature",
+ flags={},
+ },
+ },
+ trep={
+ {
+ features={ { scripts={ { script="*",langs={ "*" },} },tag="trep",comment="added bij mkiv" },},
+ name="ctx_trep_1",
+ subtables={ { name="ctx_trep_1_s" } },
+ type="gsub_single",
+ flags={},
+ },
+ },
+ anum={
+ {
+ features={ { scripts={ { script="arab",langs={ "dflt","ARA" },} },tag="anum",comment="added bij mkiv" },},
+ name="ctx_anum_1",
+ subtables={ { name="ctx_anum_1_s" } },
+ type="gsub_single",
+ flags={},
+ },
+ {
+ features={ { scripts={ { script="arab",langs={ "FAR" },} },tag="anum",comment="added bij mkiv" },},
+ name="ctx_anum_2",
+ subtables={ { name="ctx_anum_2_s" } },
+ type="gsub_single",
+ flags={},
+ },
+ },
+}
+fonts.otf.enhancers["add some missing characters"]=function(data,filename)
+end
+fonts.otf.enhancers["enrich with features"]=function(data,filename)
+ local used={}
+ for i=1,#otf.glists do
+ local g=data[otf.glists[i]]
+ if g then
+ for i=1,#g do
+ local f=g[i].features
+ if f then
+ for i=1,#f do
+ local t=f[i].tag
+ if t then used[t]=true end
+ end
+ end
+ end
+ end
+ end
+ local glyphs=data.glyphs
+ local indices=data.map.map
+ data.gsub=data.gsub or {}
+ for kind,specifications in next,extra_features do
+ if not used[kind] then
+ local done=0
+ for s=1,#specifications do
+ local added=false
+ local specification=specifications[s]
+ local list=extra_lists[kind][s]
+ local name=specification.name.."_s"
+ if specification.type=="gsub_ligature" then
+ for unicode,index in next,indices do
+ local glyph=glyphs[index]
+ local ligature=list[glyph.name]
+ if ligature then
+ local o=glyph.lookups or {}
+ o[name]={
+ {
+ ["type"]="ligature",
+ ["specification"]={
+ char=glyph.name,
+ components=ligature,
+ }
+ }
+ }
+ glyph.lookups,done,added=o,done+1,true
+ end
+ end
+ elseif specification.type=="gsub_single" then
+ for unicode,index in next,indices do
+ local glyph=glyphs[index]
+ local r=list[unicode]
+ if r then
+ local replacement=indices[r]
+ if replacement and glyphs[replacement] then
+ local o=glyph.lookups or {}
+ o[name]={
+ {
+ ["type"]="substitution",
+ ["specification"]={
+ variant=glyphs[replacement].name,
+ }
+ }
+ }
+ glyph.lookups,done,added=o,done+1,true
+ end
+ end
+ end
+ end
+ if added then
+ insert(data.gsub,s,table.fastcopy(specification))
+ end
+ end
+ if done>0 then
+ if trace_loading then
+ logs.report("load otf","enhance: registering %s feature (%s glyphs affected)",kind,done)
+ end
+ end
+ end
+ end
+end
+otf.tables.features['tlig']='TeX Ligatures'
+otf.tables.features['trep']='TeX Replacements'
+otf.tables.features['anum']='Arabic Digits'
+otf.features.register_base_substitution('tlig')
+otf.features.register_base_substitution('trep')
+otf.features.register_base_substitution('anum')
+fonts.initializers.base.otf.equaldigits=fonts.initializers.common.equaldigits
+fonts.initializers.node.otf.equaldigits=fonts.initializers.common.equaldigits
+fonts.initializers.base.otf.lineheight=fonts.initializers.common.lineheight
+fonts.initializers.node.otf.lineheight=fonts.initializers.common.lineheight
+fonts.initializers.base.otf.compose=fonts.initializers.common.compose
+fonts.initializers.node.otf.compose=fonts.initializers.common.compose
+
+end -- closure
+
+do -- begin closure to overcome local limits and interference
+
+if not modules then modules={} end modules ['font-def']={
+ 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 format,concat,gmatch,match,find,lower=string.format,table.concat,string.gmatch,string.match,string.find,string.lower
+local tostring,next=tostring,next
+local lpegmatch=lpeg.match
+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)
+trackers.register("fonts.loading","fonts.defining","otf.loading","afm.loading","tfm.loading")
+trackers.register("fonts.all","fonts.*","otf.*","afm.*","tfm.*")
+fonts=fonts or {}
+fonts.define=fonts.define or {}
+fonts.tfm=fonts.tfm or {}
+fonts.ids=fonts.ids or {}
+fonts.vf=fonts.vf or {}
+fonts.used=fonts.used or {}
+local tfm=fonts.tfm
+local vf=fonts.vf
+local define=fonts.define
+tfm.version=1.01
+tfm.cache=containers.define("fonts","tfm",tfm.version,false)
+define.method="afm or tfm"
+define.specify=fonts.define.specify or {}
+define.methods=fonts.define.methods or {}
+tfm.fonts=tfm.fonts or {}
+tfm.readers=tfm.readers or {}
+tfm.internalized=tfm.internalized or {}
+tfm.readers.sequence={ 'otf','ttf','afm','tfm' }
+tfm.auto_afm=true
+local readers=tfm.readers
+local sequence=readers.sequence
+fonts.version=1.05
+fonts.cache=containers.define("fonts","def",fonts.version,false)
+local splitter,specifiers=nil,""
+local P,C,S,Cc=lpeg.P,lpeg.C,lpeg.S,lpeg.Cc
+local left=P("(")
+local right=P(")")
+local colon=P(":")
+local space=P(" ")
+define.defaultlookup="file"
+local prefixpattern=P(false)
+function define.add_specifier(symbol)
+ specifiers=specifiers..symbol
+ local method=S(specifiers)
+ local lookup=C(prefixpattern)*colon
+ local sub=left*C(P(1-left-right-method)^1)*right
+ local specification=C(method)*C(P(1)^1)
+ local name=C((1-sub-specification)^1)
+ splitter=P((lookup+Cc(""))*name*(sub+Cc(""))*(specification+Cc("")))
+end
+function define.add_lookup(str,default)
+ prefixpattern=prefixpattern+P(str)
+end
+define.add_lookup("file")
+define.add_lookup("name")
+define.add_lookup("spec")
+function define.get_specification(str)
+ return lpegmatch(splitter,str)
+end
+function define.register_split(symbol,action)
+ define.add_specifier(symbol)
+ define.specify[symbol]=action
+end
+function define.makespecification(specification,lookup,name,sub,method,detail,size)
+ size=size or 655360
+ if trace_defining then
+ logs.report("define font","%s -> lookup: %s, name: %s, sub: %s, method: %s, detail: %s",
+ specification,(lookup~="" and lookup) or "[file]",(name~="" and name) or "-",
+ (sub~="" and sub) or "-",(method~="" and method) or "-",(detail~="" and detail) or "-")
+ end
+ if not lookup or lookup=="" then
+ lookup=define.defaultlookup
+ end
+ local t={
+ lookup=lookup,
+ specification=specification,
+ size=size,
+ name=name,
+ sub=sub,
+ method=method,
+ detail=detail,
+ resolved="",
+ forced="",
+ features={},
+ }
+ return t
+end
+function define.analyze(specification,size)
+ local lookup,name,sub,method,detail=define.get_specification(specification or "")
+ return define.makespecification(specification,lookup,name,sub,method,detail,size)
+end
+local sortedhashkeys=table.sortedhashkeys
+function tfm.hash_features(specification)
+ local features=specification.features
+ if features then
+ local t={}
+ local normal=features.normal
+ if normal and next(normal) then
+ local f=sortedhashkeys(normal)
+ for i=1,#f do
+ local v=f[i]
+ if v~="number" and v~="features" then
+ t[#t+1]=v..'='..tostring(normal[v])
+ end
+ end
+ end
+ local vtf=features.vtf
+ if vtf and next(vtf) then
+ local f=sortedhashkeys(vtf)
+ for i=1,#f do
+ local v=f[i]
+ t[#t+1]=v..'='..tostring(vtf[v])
+ end
+ end
+ if #t>0 then
+ return concat(t,"+")
+ end
+ end
+ return "unknown"
+end
+fonts.designsizes={}
+function tfm.hash_instance(specification,force)
+ local hash,size,fallbacks=specification.hash,specification.size,specification.fallbacks
+ if force or not hash then
+ hash=tfm.hash_features(specification)
+ specification.hash=hash
+ end
+ if size<1000 and fonts.designsizes[hash] then
+ size=math.round(tfm.scaled(size,fonts.designsizes[hash]))
+ specification.size=size
+ end
+ if fallbacks then
+ return hash..' @ '..tostring(size)..' @ '..fallbacks
+ else
+ return hash..' @ '..tostring(size)
+ end
+end
+define.resolvers=resolvers
+function define.resolvers.file(specification)
+ local suffix=file.suffix(specification.name)
+ if fonts.formats[suffix] then
+ specification.forced=suffix
+ specification.name=file.removesuffix(specification.name)
+ end
+end
+function define.resolvers.name(specification)
+ local resolve=fonts.names.resolve
+ if resolve then
+ local resolved,sub=fonts.names.resolve(specification)
+ specification.resolved,specification.sub=resolved,sub
+ if resolved then
+ local suffix=file.suffix(resolved)
+ if fonts.formats[suffix] then
+ specification.forced=suffix
+ specification.name=file.removesuffix(resolved)
+ else
+ specification.name=resolved
+ end
+ end
+ else
+ define.resolvers.file(specification)
+ end
+end
+function define.resolvers.spec(specification)
+ local resolvespec=fonts.names.resolvespec
+ if resolvespec then
+ specification.resolved,specification.sub=fonts.names.resolvespec(specification)
+ if specification.resolved then
+ specification.forced=file.extname(specification.resolved)
+ specification.name=file.removesuffix(specification.resolved)
+ end
+ else
+ define.resolvers.name(specification)
+ end
+end
+function define.resolve(specification)
+ if not specification.resolved or specification.resolved=="" then
+ local r=define.resolvers[specification.lookup]
+ if r then
+ r(specification)
+ end
+ end
+ if specification.forced=="" then
+ specification.forced=nil
+ else
+ specification.forced=specification.forced
+ end
+ specification.hash=lower(specification.name..' @ '..tfm.hash_features(specification))
+ if specification.sub and specification.sub~="" then
+ specification.hash=specification.sub..' @ '..specification.hash
+ end
+ return specification
+end
+function tfm.read(specification)
+ local hash=tfm.hash_instance(specification)
+ local tfmtable=tfm.fonts[hash]
+ if not tfmtable then
+ local forced=specification.forced or ""
+ if forced~="" then
+ tfmtable=readers[lower(forced)](specification)
+ if not tfmtable then
+ logs.report("define font","forced type %s of %s not found",forced,specification.name)
+ end
+ else
+ for s=1,#sequence do
+ local reader=sequence[s]
+ if readers[reader] then
+ if trace_defining then
+ logs.report("define font","trying (reader sequence driven) type %s for %s with file %s",reader,specification.name,specification.filename or "unknown")
+ end
+ tfmtable=readers[reader](specification)
+ if tfmtable then
+ break
+ else
+ specification.filename=nil
+ end
+ end
+ end
+ end
+ if tfmtable then
+ if directive_embedall then
+ tfmtable.embedding="full"
+ elseif tfmtable.filename and fonts.dontembed[tfmtable.filename] then
+ tfmtable.embedding="no"
+ else
+ tfmtable.embedding="subset"
+ end
+ tfm.fonts[hash]=tfmtable
+ fonts.designsizes[specification.hash]=tfmtable.designsize
+ end
+ end
+ if not tfmtable then
+ logs.report("define font","font with name %s is not found",specification.name)
+ end
+ return tfmtable
+end
+function tfm.read_and_define(name,size)
+ local specification=define.analyze(name,size)
+ local method=specification.method
+ if method and define.specify[method] then
+ specification=define.specify[method](specification)
+ end
+ specification=define.resolve(specification)
+ local hash=tfm.hash_instance(specification)
+ local id=define.registered(hash)
+ if not id then
+ local fontdata=tfm.read(specification)
+ if fontdata then
+ fontdata.hash=hash
+ id=font.define(fontdata)
+ define.register(fontdata,id)
+ tfm.cleanup_table(fontdata)
+ else
+ id=0
+ end
+ end
+ return fonts.ids[id],id
+end
+local function check_tfm(specification,fullname)
+ local foundname=resolvers.findbinfile(fullname,'tfm') or ""
+ if foundname=="" then
+ foundname=resolvers.findbinfile(fullname,'ofm') or ""
+ end
+ if foundname~="" then
+ specification.filename,specification.format=foundname,"ofm"
+ return tfm.read_from_tfm(specification)
+ end
+end
+local function check_afm(specification,fullname)
+ local foundname=resolvers.findbinfile(fullname,'afm') or ""
+ if foundname=="" and tfm.auto_afm then
+ local encoding,shortname=match(fullname,"^(.-)%-(.*)$")
+ if encoding and shortname and fonts.enc.known[encoding] then
+ shortname=resolvers.findbinfile(shortname,'afm') or ""
+ if shortname~="" then
+ foundname=shortname
+ if trace_loading then
+ logs.report("load afm","stripping encoding prefix from filename %s",afmname)
+ end
+ end
+ end
+ end
+ if foundname~="" then
+ specification.filename,specification.format=foundname,"afm"
+ return tfm.read_from_afm(specification)
+ end
+end
+function readers.tfm(specification)
+ local fullname,tfmtable=specification.filename or "",nil
+ if fullname=="" then
+ local forced=specification.forced or ""
+ if forced~="" then
+ tfmtable=check_tfm(specification,specification.name.."."..forced)
+ end
+ if not tfmtable then
+ tfmtable=check_tfm(specification,specification.name)
+ end
+ else
+ tfmtable=check_tfm(specification,fullname)
+ end
+ return tfmtable
+end
+function readers.afm(specification,method)
+ local fullname,tfmtable=specification.filename or "",nil
+ if fullname=="" then
+ local forced=specification.forced or ""
+ if forced~="" then
+ tfmtable=check_afm(specification,specification.name.."."..forced)
+ end
+ if not tfmtable then
+ method=method or define.method or "afm or tfm"
+ if method=="tfm" then
+ tfmtable=check_tfm(specification,specification.name)
+ elseif method=="afm" then
+ tfmtable=check_afm(specification,specification.name)
+ elseif method=="tfm or afm" then
+ tfmtable=check_tfm(specification,specification.name) or check_afm(specification,specification.name)
+ else
+ tfmtable=check_afm(specification,specification.name) or check_tfm(specification,specification.name)
+ end
+ end
+ else
+ tfmtable=check_afm(specification,fullname)
+ end
+ return tfmtable
+end
+local function check_otf(forced,specification,suffix,what)
+ local name=specification.name
+ if forced then
+ name=file.addsuffix(name,suffix,true)
+ end
+ local fullname,tfmtable=resolvers.findbinfile(name,suffix) or "",nil
+ if fullname=="" then
+ local fb=fonts.names.old_to_new[name]
+ if fb then
+ fullname=resolvers.findbinfile(fb,suffix) or ""
+ end
+ end
+ if fullname=="" then
+ local fb=fonts.names.new_to_old[name]
+ if fb then
+ fullname=resolvers.findbinfile(fb,suffix) or ""
+ end
+ end
+ if fullname~="" then
+ specification.filename,specification.format=fullname,what
+ tfmtable=tfm.read_from_open_type(specification)
+ end
+ return tfmtable
+end
+function readers.opentype(specification,suffix,what)
+ 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")
+ else
+ return check_otf(false,specification,suffix,what)
+ end
+end
+function readers.otf (specification) return readers.opentype(specification,"otf","opentype") end
+function readers.ttf (specification) return readers.opentype(specification,"ttf","truetype") end
+function readers.ttc (specification) return readers.opentype(specification,"ttf","truetype") end
+function readers.dfont(specification) return readers.opentype(specification,"ttf","truetype") end
+function define.check(features,defaults)
+ local done=false
+ if features and next(features) then
+ for k,v in next,defaults do
+ if features[k]==nil then
+ features[k],done=v,true
+ end
+ end
+ else
+ features,done=table.fastcopy(defaults),true
+ end
+ return features,done
+end
+define.last=nil
+function define.register(fontdata,id)
+ if fontdata and id then
+ local hash=fontdata.hash
+ if not tfm.internalized[hash] then
+ if trace_defining then
+ logs.report("define font","loading at 2 id %s, hash: %s",id or "?",hash or "?")
+ end
+ fonts.identifiers[id]=fontdata
+ fonts.characters [id]=fontdata.characters
+ fonts.quads [id]=fontdata.parameters.quad
+ tfm.internalized[hash]=id
+ end
+ end
+end
+function define.registered(hash)
+ local id=tfm.internalized[hash]
+ return id,id and fonts.ids[id]
+end
+local cache_them=false
+function tfm.make(specification)
+ local fvm=define.methods[specification.features.vtf.preset]
+ if fvm then
+ return fvm(specification)
+ else
+ return nil
+ end
+end
+function define.read(specification,size,id)
+ statistics.starttiming(fonts)
+ if type(specification)=="string" then
+ specification=define.analyze(specification,size)
+ end
+ local method=specification.method
+ if method and define.specify[method] then
+ specification=define.specify[method](specification)
+ end
+ specification=define.resolve(specification)
+ local hash=tfm.hash_instance(specification)
+ if cache_them then
+ local fontdata=containers.read(fonts.cache,hash)
+ end
+ local fontdata=define.registered(hash)
+ if not fontdata then
+ if specification.features.vtf and specification.features.vtf.preset then
+ fontdata=tfm.make(specification)
+ else
+ fontdata=tfm.read(specification)
+ if fontdata then
+ tfm.check_virtual_id(fontdata)
+ end
+ end
+ if cache_them then
+ fontdata=containers.write(fonts.cache,hash,fontdata)
+ end
+ if fontdata then
+ fontdata.hash=hash
+ fontdata.cache="no"
+ if id then
+ define.register(fontdata,id)
+ end
+ end
+ end
+ define.last=fontdata or id
+ if not fontdata then
+ logs.report("define font","unknown font %s, loading aborted",specification.name)
+ elseif trace_defining and type(fontdata)=="table" then
+ logs.report("define font","using %s font with id %s, name:%s size:%s bytes:%s encoding:%s fullname:%s filename:%s",
+ fontdata.type or "unknown",
+ id or "?",
+ fontdata.name or "?",
+ fontdata.size or "default",
+ fontdata.encodingbytes or "?",
+ fontdata.encodingname or "unicode",
+ fontdata.fullname or "?",
+ file.basename(fontdata.filename or "?"))
+ end
+ statistics.stoptiming(fonts)
+ return fontdata
+end
+function vf.find(name)
+ name=file.removesuffix(file.basename(name))
+ if tfm.resolve_vf then
+ local format=fonts.logger.format(name)
+ if format=='tfm' or format=='ofm' then
+ if trace_defining then
+ logs.report("define font","locating vf for %s",name)
+ end
+ return resolvers.findbinfile(name,"ovf")
+ else
+ if trace_defining then
+ logs.report("define font","vf for %s is already taken care of",name)
+ end
+ return nil
+ end
+ else
+ if trace_defining then
+ logs.report("define font","locating vf for %s",name)
+ end
+ return resolvers.findbinfile(name,"ovf")
+ end
+end
+callbacks.register('define_font',define.read,"definition of fonts (tfmtable preparation)")
+callbacks.register('find_vf_file',vf.find,"locating virtual fonts, insofar needed")
+
+end -- closure
+
+do -- begin closure to overcome local limits and interference
+
+if not modules then modules={} end modules ['font-xtx']={
+ 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 texsprint,count=tex.sprint,tex.count
+local format,concat,gmatch,match,find,lower=string.format,table.concat,string.gmatch,string.match,string.find,string.lower
+local tostring,next=tostring,next
+local lpegmatch=lpeg.match
+local trace_defining=false trackers.register("fonts.defining",function(v) trace_defining=v end)
+local list={}
+fonts.define.specify.colonized_default_lookup="file"
+local function isstyle(s)
+ local style=string.lower(s):split("/")
+ for _,v in ipairs(style) do
+ if v=="b" then
+ list.style="bold"
+ elseif v=="i" then
+ list.style="italic"
+ elseif v=="bi" or v=="ib" then
+ list.style="bolditalic"
+ elseif v:find("^s=") then
+ list.optsize=v:split("=")[2]
+ elseif v=="aat" or v=="icu" or v=="gr" then
+ logs.report("load font","unsupported font option: %s",v)
+ elseif not v:is_empty() then
+ list.style=v:gsub("[^%a%d]","")
+ end
+ end
+end
+fonts=fonts or {}
+fonts.otf=fonts.otf or {}
+local otf=fonts.otf
+otf.tables=otf.tables or {}
+otf.tables.defaults={
+ dflt={
+ "ccmp","locl","rlig","liga","clig",
+ "kern","mark","mkmk","itlc",
+ },
+ arab={
+ "ccmp","locl","isol","fina","fin2",
+ "fin3","medi","med2","init","rlig",
+ "calt","liga","cswh","mset","curs",
+ "kern","mark","mkmk",
+ },
+ deva={
+ "ccmp","locl","init","nukt","akhn",
+ "rphf","blwf","half","pstf","vatu",
+ "pres","blws","abvs","psts","haln",
+ "calt","blwm","abvm","dist","kern",
+ "mark","mkmk",
+ },
+ khmr={
+ "ccmp","locl","pref","blwf","abvf",
+ "pstf","pres","blws","abvs","psts",
+ "clig","calt","blwm","abvm","dist",
+ "kern","mark","mkmk",
+ },
+ thai={
+ "ccmp","locl","liga","kern","mark",
+ "mkmk",
+ },
+ hang={
+ "ccmp","ljmo","vjmo","tjmo",
+ },
+}
+otf.tables.defaults.beng=otf.tables.defaults.deva
+otf.tables.defaults.guru=otf.tables.defaults.deva
+otf.tables.defaults.gujr=otf.tables.defaults.deva
+otf.tables.defaults.orya=otf.tables.defaults.deva
+otf.tables.defaults.taml=otf.tables.defaults.deva
+otf.tables.defaults.telu=otf.tables.defaults.deva
+otf.tables.defaults.knda=otf.tables.defaults.deva
+otf.tables.defaults.mlym=otf.tables.defaults.deva
+otf.tables.defaults.sinh=otf.tables.defaults.deva
+otf.tables.defaults.syrc=otf.tables.defaults.arab
+otf.tables.defaults.mong=otf.tables.defaults.arab
+otf.tables.defaults.nko=otf.tables.defaults.arab
+otf.tables.defaults.tibt=otf.tables.defaults.khmr
+otf.tables.defaults.lao=otf.tables.defaults.thai
+local function parse_script(script)
+ if otf.tables.scripts[script] then
+ local dflt
+ if otf.tables.defaults[script] then
+ logs.report("load font","auto-selecting default features for script: %s",script)
+ dflt=otf.tables.defaults[script]
+ else
+ logs.report("load font","auto-selecting default features for script: dflt (was %s)",script)
+ dflt=otf.tables.defaults["dflt"]
+ end
+ for _,v in next,dflt do
+ list[v]="yes"
+ end
+ else
+ logs.report("load font","unknown script: %s",script)
+ end
+end
+local function issome () list.lookup=fonts.define.specify.colonized_default_lookup end
+local function isfile () list.lookup='file' end
+local function isname () list.lookup='name' end
+local function thename(s) list.name=s end
+local function issub (v) list.sub=v end
+local function iskey (k,v)
+ if k=="script" then
+ parse_script(v)
+ end
+ list[k]=v
+end
+local function istrue (s) list[s]=true end
+local function isfalse(s) list[s]=false end
+local spaces=lpeg.P(" ")^0
+local namespec=(1-lpeg.S("/:("))^0
+local filespec=(lpeg.R("az","AZ")*lpeg.P(":"))^-1*(1-lpeg.S(":("))^1
+local crapspec=spaces*lpeg.P("/")*(((1-lpeg.P(":"))^0)/isstyle)*spaces
+local filename=(lpeg.P("file:")/isfile*(filespec/thename))+(lpeg.P("[")*lpeg.P(true)/isfile*(((1-lpeg.P("]"))^0)/thename)*lpeg.P("]"))
+local fontname=(lpeg.P("name:")/isname*(namespec/thename))+lpeg.P(true)/issome*(namespec/thename)
+local sometext=(lpeg.R("az","AZ","09")+lpeg.S("+-."))^1
+local truevalue=lpeg.P("+")*spaces*(sometext/istrue)
+local falsevalue=lpeg.P("-")*spaces*(sometext/isfalse)
+local keyvalue=lpeg.P("+")+(lpeg.C(sometext)*spaces*lpeg.P("=")*spaces*lpeg.C(sometext))/iskey
+local somevalue=sometext/istrue
+local subvalue=lpeg.P("(")*(lpeg.C(lpeg.P(1-lpeg.S("()"))^1)/issub)*lpeg.P(")")
+local option=spaces*(keyvalue+falsevalue+truevalue+somevalue)*spaces
+local options=lpeg.P(":")*spaces*(lpeg.P(";")^0*option)^0
+local pattern=(filename+fontname)*subvalue^0*crapspec^0*options^0
+local normalize_meanings=fonts.otf.meanings.normalize
+function fonts.define.specify.colonized(specification)
+ list={}
+ lpegmatch(pattern,specification.specification)
+ if list.style then
+ specification.style=list.style
+ list.style=nil
+ end
+ if list.optsize then
+ specification.optsize=list.optsize
+ list.optsize=nil
+ end
+ if list.name then
+ if resolvers.find_file(list.name,"tfm") then
+ list.lookup="file"
+ list.name=file.addsuffix(list.name,"tfm")
+ elseif resolvers.find_file(list.name,"ofm") then
+ list.lookup="file"
+ list.name=file.addsuffix(list.name,"ofm")
+ end
+ specification.name=list.name
+ list.name=nil
+ end
+ if list.lookup then
+ specification.lookup=list.lookup
+ list.lookup=nil
+ end
+ if list.sub then
+ specification.sub=list.sub
+ list.sub=nil
+ end
+ specification.features.normal=normalize_meanings(list)
+ return specification
+end
+fonts.define.register_split(":",fonts.define.specify.colonized)
+
+end -- closure
+
+do -- begin closure to overcome local limits and interference
+
+if not modules then modules={} end modules ['font-dum']={
+ version=1.001,
+ comment="companion to luatex-*.tex",
+ author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright="PRAGMA ADE / ConTeXt Development Team",
+ license="see context related readme files"
+}
+fonts=fonts or {}
+fonts.otf.pack=false
+fonts.tfm.resolve_vf=false
+fonts.tfm.fontname_mode="specification"
+fonts.tfm.readers=fonts.tfm.readers or {}
+fonts.tfm.readers.sequence={ 'otf','ttf','tfm' }
+fonts.tfm.readers.afm=nil
+fonts.define=fonts.define or {}
+fonts.define.specify.colonized_default_lookup="name"
+function fonts.define.get_specification(str)
+ return "",str,"",":",str
+end
+fonts.logger=fonts.logger or {}
+function fonts.logger.save()
+end
+fonts.names=fonts.names or {}
+fonts.names.version=1.001
+fonts.names.basename="luatex-fonts-names.lua"
+fonts.names.new_to_old={}
+fonts.names.old_to_new={}
+local data,loaded=nil,false
+local fileformats={ "lua","tex","other text files" }
+function fonts.names.resolve(name,sub)
+ if not loaded then
+ local basename=fonts.names.basename
+ if basename and basename~="" then
+ for i=1,#fileformats do
+ local format=fileformats[i]
+ local foundname=resolvers.find_file(basename,format) or ""
+ if foundname~="" then
+ data=dofile(foundname)
+ break
+ end
+ end
+ end
+ loaded=true
+ end
+ if type(data)=="table" and data.version==fonts.names.version then
+ local condensed=string.gsub(string.lower(name),"[^%a%d]","")
+ local found=data.mappings and data.mappings[condensed]
+ if found then
+ local fontname,filename,subfont=found[1],found[2],found[3]
+ if subfont then
+ return filename,fontname
+ else
+ return filename,false
+ end
+ else
+ return name,false
+ end
+ end
+end
+fonts.names.resolvespec=fonts.names.resolve
+table.insert(fonts.triggers,"itlc")
+local function itlc(tfmdata,value)
+ if value then
+ local metadata=tfmdata.shared.otfdata.metadata
+ if metadata then
+ local italicangle=metadata.italicangle
+ if italicangle and italicangle~=0 then
+ local uwidth=(metadata.uwidth or 40)/2
+ for unicode,d in next,tfmdata.descriptions do
+ local it=d.boundingbox[3]-d.width+uwidth
+ if it~=0 then
+ d.italic=it
+ end
+ end
+ tfmdata.has_italic=true
+ end
+ end
+ end
+end
+fonts.initializers.base.otf.itlc=itlc
+fonts.initializers.node.otf.itlc=itlc
+function fonts.initializers.common.slant(tfmdata,value)
+ value=tonumber(value)
+ if not value then
+ value=0
+ elseif value>1 then
+ value=1
+ elseif value<-1 then
+ value=-1
+ end
+ tfmdata.slant_factor=value
+end
+function fonts.initializers.common.extend(tfmdata,value)
+ value=tonumber(value)
+ if not value then
+ value=0
+ elseif value>10 then
+ value=10
+ elseif value<-10 then
+ value=-10
+ end
+ tfmdata.extend_factor=value
+end
+table.insert(fonts.triggers,"slant")
+table.insert(fonts.triggers,"extend")
+fonts.initializers.base.otf.slant=fonts.initializers.common.slant
+fonts.initializers.node.otf.slant=fonts.initializers.common.slant
+fonts.initializers.base.otf.extend=fonts.initializers.common.extend
+fonts.initializers.node.otf.extend=fonts.initializers.common.extend
+fonts.protrusions=fonts.protrusions or {}
+fonts.protrusions.setups=fonts.protrusions.setups or {}
+local setups=fonts.protrusions.setups
+local function map_opbd_onto_protrusion(tfmdata,value,opbd)
+ local characters,descriptions=tfmdata.characters,tfmdata.descriptions
+ local otfdata=tfmdata.shared.otfdata
+ local singles=otfdata.shared.featuredata.gpos_single
+ local script,language=tfmdata.script,tfmdata.language
+ local done,factor,left,right=false,1,1,1
+ local setup=setups[value]
+ if setup then
+ factor=setup.factor or 1
+ left=setup.left or 1
+ right=setup.right or 1
+ else
+ factor=tonumber(value) or 1
+ end
+ if opbd~="right" then
+ local validlookups,lookuplist=fonts.otf.collect_lookups(otfdata,"lfbd",script,language)
+ if validlookups then
+ for i=1,#lookuplist do
+ local lookup=lookuplist[i]
+ local data=singles[lookup]
+ if data then
+ if trace_protrusion then
+ logs.report("fonts","set left protrusion using lfbd lookup '%s'",lookup)
+ end
+ for k,v in next,data do
+ local p=- (v[1]/1000)*factor*left
+ characters[k].left_protruding=p
+ if trace_protrusion then
+ logs.report("opbd","lfbd -> %s -> 0x%05X (%s) -> %0.03f (%s)",lookup,k,utfchar(k),p,concat(v," "))
+ end
+ end
+ done=true
+ end
+ end
+ end
+ end
+ if opbd~="left" then
+ local validlookups,lookuplist=fonts.otf.collect_lookups(otfdata,"rtbd",script,language)
+ if validlookups then
+ for i=1,#lookuplist do
+ local lookup=lookuplist[i]
+ local data=singles[lookup]
+ if data then
+ if trace_protrusion then
+ logs.report("fonts","set right protrusion using rtbd lookup '%s'",lookup)
+ end
+ for k,v in next,data do
+ local p=(v[1]/1000)*factor*right
+ characters[k].right_protruding=p
+ if trace_protrusion then
+ logs.report("opbd","rtbd -> %s -> 0x%05X (%s) -> %0.03f (%s)",lookup,k,utfchar(k),p,concat(v," "))
+ end
+ end
+ end
+ done=true
+ end
+ end
+ end
+ tfmdata.auto_protrude=done
+end
+function fonts.initializers.common.protrusion(tfmdata,value)
+ if value then
+ local opbd=tfmdata.shared.features.opbd
+ if opbd then
+ map_opbd_onto_protrusion(tfmdata,value,opbd)
+ elseif value then
+ local setup=setups[value]
+ if setup then
+ local factor,left,right=setup.factor or 1,setup.left or 1,setup.right or 1
+ local emwidth=tfmdata.parameters.quad
+ tfmdata.auto_protrude=true
+ for i,chr in next,tfmdata.characters do
+ local v,pl,pr=setup[i],nil,nil
+ if v then
+ pl,pr=v[1],v[2]
+ end
+ if pl and pl~=0 then chr.left_protruding=left*pl*factor end
+ if pr and pr~=0 then chr.right_protruding=right*pr*factor end
+ end
+ end
+ end
+ end
+end
+fonts.expansions=fonts.expansions or {}
+fonts.expansions.setups=fonts.expansions.setups or {}
+local setups=fonts.expansions.setups
+function fonts.initializers.common.expansion(tfmdata,value)
+ if value then
+ local setup=setups[value]
+ if setup then
+ local stretch,shrink,step,factor=setup.stretch or 0,setup.shrink or 0,setup.step or 0,setup.factor or 1
+ tfmdata.stretch,tfmdata.shrink,tfmdata.step,tfmdata.auto_expand=stretch*10,shrink*10,step*10,true
+ for i,chr in next,tfmdata.characters do
+ local v=setup[i]
+ if v and v~=0 then
+ chr.expansion_factor=v*factor
+ else
+ chr.expansion_factor=factor
+ end
+ end
+ end
+ end
+end
+table.insert(fonts.manipulators,"protrusion")
+table.insert(fonts.manipulators,"expansion")
+fonts.initializers.base.otf.protrusion=fonts.initializers.common.protrusion
+fonts.initializers.node.otf.protrusion=fonts.initializers.common.protrusion
+fonts.initializers.base.otf.expansion=fonts.initializers.common.expansion
+fonts.initializers.node.otf.expansion=fonts.initializers.common.expansion
+function fonts.register_message()
+end
+local byte=string.byte
+fonts.expansions.setups['default']={
+ stretch=2,shrink=2,step=.5,factor=1,
+ [byte('A')]=0.5,[byte('B')]=0.7,[byte('C')]=0.7,[byte('D')]=0.5,[byte('E')]=0.7,
+ [byte('F')]=0.7,[byte('G')]=0.5,[byte('H')]=0.7,[byte('K')]=0.7,[byte('M')]=0.7,
+ [byte('N')]=0.7,[byte('O')]=0.5,[byte('P')]=0.7,[byte('Q')]=0.5,[byte('R')]=0.7,
+ [byte('S')]=0.7,[byte('U')]=0.7,[byte('W')]=0.7,[byte('Z')]=0.7,
+ [byte('a')]=0.7,[byte('b')]=0.7,[byte('c')]=0.7,[byte('d')]=0.7,[byte('e')]=0.7,
+ [byte('g')]=0.7,[byte('h')]=0.7,[byte('k')]=0.7,[byte('m')]=0.7,[byte('n')]=0.7,
+ [byte('o')]=0.7,[byte('p')]=0.7,[byte('q')]=0.7,[byte('s')]=0.7,[byte('u')]=0.7,
+ [byte('w')]=0.7,[byte('z')]=0.7,
+ [byte('2')]=0.7,[byte('3')]=0.7,[byte('6')]=0.7,[byte('8')]=0.7,[byte('9')]=0.7,
+}
+fonts.protrusions.setups['default']={
+ factor=1,left=1,right=1,
+ [0x002C]={ 0,1 },
+ [0x002E]={ 0,1 },
+ [0x003A]={ 0,1 },
+ [0x003B]={ 0,1 },
+ [0x002D]={ 0,1 },
+ [0x2013]={ 0,0.50 },
+ [0x2014]={ 0,0.33 },
+ [0x3001]={ 0,1 },
+ [0x3002]={ 0,1 },
+ [0x060C]={ 0,1 },
+ [0x061B]={ 0,1 },
+ [0x06D4]={ 0,1 },
+}
+fonts.otf.meanings=fonts.otf.meanings or {}
+fonts.otf.meanings.normalize=fonts.otf.meanings.normalize or function(t)
+ if t.rand then
+ t.rand="random"
+ end
+end
+function fonts.otf.name_to_slot(name)
+ local tfmdata=fonts.ids[font.current()]
+ if tfmdata and tfmdata.shared then
+ local otfdata=tfmdata.shared.otfdata
+ local unicode=otfdata.luatex.unicodes[name]
+ return unicode and (type(unicode)=="number" and unicode or unicode[1])
+ end
+end
+function fonts.otf.char(n)
+ if type(n)=="string" then
+ n=fonts.otf.name_to_slot(n)
+ end
+ if type(n)=="number" then
+ tex.sprint("\\char"..n)
+ end
+end
+fonts.strippables=table.tohash {
+ 0x000AD,0x017B4,0x017B5,0x0200B,0x0200C,0x0200D,0x0200E,0x0200F,0x0202A,0x0202B,
+ 0x0202C,0x0202D,0x0202E,0x02060,0x02061,0x02062,0x02063,0x0206A,0x0206B,0x0206C,
+ 0x0206D,0x0206E,0x0206F,0x0FEFF,0x1D173,0x1D174,0x1D175,0x1D176,0x1D177,0x1D178,
+ 0x1D179,0x1D17A,0xE0001,0xE0020,0xE0021,0xE0022,0xE0023,0xE0024,0xE0025,0xE0026,
+ 0xE0027,0xE0028,0xE0029,0xE002A,0xE002B,0xE002C,0xE002D,0xE002E,0xE002F,0xE0030,
+ 0xE0031,0xE0032,0xE0033,0xE0034,0xE0035,0xE0036,0xE0037,0xE0038,0xE0039,0xE003A,
+ 0xE003B,0xE003C,0xE003D,0xE003E,0xE003F,0xE0040,0xE0041,0xE0042,0xE0043,0xE0044,
+ 0xE0045,0xE0046,0xE0047,0xE0048,0xE0049,0xE004A,0xE004B,0xE004C,0xE004D,0xE004E,
+ 0xE004F,0xE0050,0xE0051,0xE0052,0xE0053,0xE0054,0xE0055,0xE0056,0xE0057,0xE0058,
+ 0xE0059,0xE005A,0xE005B,0xE005C,0xE005D,0xE005E,0xE005F,0xE0060,0xE0061,0xE0062,
+ 0xE0063,0xE0064,0xE0065,0xE0066,0xE0067,0xE0068,0xE0069,0xE006A,0xE006B,0xE006C,
+ 0xE006D,0xE006E,0xE006F,0xE0070,0xE0071,0xE0072,0xE0073,0xE0074,0xE0075,0xE0076,
+ 0xE0077,0xE0078,0xE0079,0xE007A,0xE007B,0xE007C,0xE007D,0xE007E,0xE007F,
+}
+
+end -- closure
+
+do -- begin closure to overcome local limits and interference
+
+if not modules then modules={} end modules ['font-clr']={
+ version=1.001,
+ comment="companion to font-otf.lua (font color)",
+ author="Khaled Hosny and Elie Roux",
+ copyright="Luaotfload Development Team",
+ license="GPL"
+}
+fonts.triggers=fonts.triggers or {}
+fonts.initializers=fonts.initializers or {}
+fonts.initializers.common=fonts.initializers.common or {}
+local initializers,format=fonts.initializers,string.format
+table.insert(fonts.triggers,"color")
+function initializers.common.color(tfmdata,value)
+ local sanitized
+ if value then
+ value=tostring(value)
+ if #value==6 or #value==8 then
+ sanitized=value
+ elseif #value==7 then
+ _,_,sanitized=value:find("(......)")
+ elseif #value>8 then
+ _,_,sanitized=value:find("(........)")
+ else
+ end
+ end
+ if sanitized then
+ tfmdata.color=sanitized
+ add_color_callback()
+ end
+end
+initializers.base.otf.color=initializers.common.color
+initializers.node.otf.color=initializers.common.color
+local function hex2dec(hex,one)
+ if one then
+ return format("%.1g",tonumber(hex,16)/255)
+ else
+ return format("%.3g",tonumber(hex,16)/255)
+ end
+end
+local res
+local function pageresources(a)
+ local res2
+ if not res then
+ res="/TransGs1<</ca 1/CA 1>>"
+ end
+ res2=format("/TransGs%s<</ca %s/CA %s>>",a,a,a)
+ res=format("%s%s",res,res:find(res2) and "" or res2)
+end
+local function hex_to_rgba(hex)
+ local r,g,b,a,push,pop,res3
+ if hex then
+ if #hex==6 then
+ _,_,r,g,b=hex:find('(..)(..)(..)')
+ elseif #hex==8 then
+ _,_,r,g,b,a=hex:find('(..)(..)(..)(..)')
+ a=hex2dec(a,true)
+ pageresources(a)
+ end
+ else
+ return nil
+ end
+ r=hex2dec(r)
+ g=hex2dec(g)
+ b=hex2dec(b)
+ if a then
+ push=format('/TransGs%g gs %s %s %s rg',a,r,g,b)
+ pop='0 g /TransGs1 gs'
+ else
+ push=format('%s %s %s rg',r,g,b)
+ pop='0 g'
+ end
+ return push,pop
+end
+local glyph=node.id('glyph')
+local hlist=node.id('hlist')
+local vlist=node.id('vlist')
+local whatsit=node.id('whatsit')
+local pgi=node.id('page_insert')
+local sbox=node.id('sub_box')
+local function lookup_next_color(head)
+ for n in node.traverse(head) do
+ if n.id==glyph then
+ if fonts.ids[n.font] and fonts.ids[n.font].color then
+ return fonts.ids[n.font].color
+ else
+ return -1
+ end
+ elseif n.id==vlist or n.id==hlist or n.id==sbox then
+ local r=lookup_next_color(n.list)
+ if r==-1 then
+ return -1
+ elseif r then
+ return r
+ end
+ elseif n.id==whatsit or n.id==pgi then
+ return -1
+ end
+ end
+ return nil
+end
+local function node_colorize(head,current_color,next_color)
+ for n in node.traverse(head) do
+ if n.id==hlist or n.id==vlist or n.id==sbox then
+ local next_color_in=lookup_next_color(n.next) or next_color
+ n.list,current_color=node_colorize(n.list,current_color,next_color_in)
+ elseif n.id==glyph then
+ local tfmdata=fonts.ids[n.font]
+ if tfmdata and tfmdata.color then
+ if tfmdata.color~=current_color then
+ local pushcolor=hex_to_rgba(tfmdata.color)
+ local push=node.new(whatsit,8)
+ push.mode=1
+ push.data=pushcolor
+ head=node.insert_before(head,n,push)
+ current_color=tfmdata.color
+ end
+ local next_color_in=lookup_next_color (n.next) or next_color
+ if next_color_in~=tfmdata.color then
+ local _,popcolor=hex_to_rgba(tfmdata.color)
+ local pop=node.new(whatsit,8)
+ pop.mode=1
+ pop.data=popcolor
+ head=node.insert_after(head,n,pop)
+ current_color=nil
+ end
+ end
+ end
+ end
+ return head,current_color
+end
+local function font_colorize(head)
+ if res then
+ local r="/ExtGState<<"..res..">>"
+ tex.pdfpageresources=tex.pdfpageresources:gsub(r,"")
+ end
+ local h=node_colorize(head,nil,nil)
+ if res and res:find("%S") then
+ local r="/ExtGState<<"..res..">>"
+ tex.pdfpageresources=tex.pdfpageresources..r
+ end
+ return h
+end
+local color_callback_activated=0
+function add_color_callback()
+ if color_callback_activated==0 then
+ luatexbase.add_to_callback("pre_output_filter",font_colorize,"loaotfload.colorize")
+ color_callback_activated=1
+ end
+end
+
+end -- closure
diff --git a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-legacy-tool.lua b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-legacy-tool.lua
new file mode 100755
index 00000000000..30ab13b6e3a
--- /dev/null
+++ b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-legacy-tool.lua
@@ -0,0 +1,105 @@
+#!/usr/bin/env texlua
+--[[
+This file is copyright 2010 Elie Roux and Khaled Hosny and is under CC0
+license (see http://creativecommons.org/publicdomain/zero/1.0/legalcode).
+
+This file is a wrapper for the luaotfload's font names module. It is part of the
+luaotfload bundle, please see the luaotfload documentation for more info.
+--]]
+
+kpse.set_program_name("luatex")
+
+require("lualibs") ---> current
+require("luaotfload-legacy-database") ---> old
+require("alt_getopt") ---> ?
+
+local name = "mkluatexfontdb"
+local version = "1.31 (legacy)"
+
+local names = fonts.names
+
+local function help_msg()
+ texio.write(string.format([[
+Usage: %s [OPTION]...
+
+================================================================================
+ please update your luatex binary
+ this version is unsupported and likely to break things
+================================================================================
+
+Rebuild the LuaTeX font database.
+
+Valid options:
+ -f --force force re-indexing all fonts
+ -q --quiet don't output anything
+ -v --verbose=LEVEL be more verbose (print the searched directories)
+ -vv print the loaded fonts
+ -vvv print all steps of directory searching
+ -V --version print version and exit
+ -h --help print this message
+
+The output database file is named otfl-names.lua and is placed under:
+
+ %s
+
+contact: https://github.com/lualatex/luaotfload
+
+]], name, names.path.localdir))
+end
+
+local function version_msg()
+ texio.write(string.format(
+ "%s version %s, database version %s.\n", name, version, names.version))
+end
+
+--[[
+Command-line processing.
+Here we fill cmdargs with the good values, and then analyze it.
+--]]
+
+local long_opts = {
+ force = "f",
+ quiet = "q",
+ help = "h",
+ verbose = 1 ,
+ version = "V",
+}
+
+local short_opts = "fqpvVh"
+
+local force_reload = nil
+
+local function process_cmdline()
+ local opts, optind, optarg = alt_getopt.get_ordered_opts (arg, short_opts, long_opts)
+ local log_level = 1
+ for i,v in ipairs(opts) do
+ if v == "q" then
+ log_level = 0
+ elseif v == "v" then
+ if log_level > 0 then
+ log_level = log_level + 1
+ else
+ log_level = 2
+ end
+ elseif v == "V" then
+ version_msg()
+ os.exit(0)
+ elseif v == "h" then
+ help_msg()
+ os.exit(0)
+ elseif v == "f" then
+ force_reload = 1
+ end
+ end
+ names.set_log_level(log_level)
+end
+
+local function generate(force)
+ local fontnames, saved
+ fontnames = names.update(fontnames, force)
+ logs.report("%s fonts in the database", #fontnames.mappings)
+ saved = names.save(fontnames)
+end
+
+process_cmdline()
+generate(force_reload)
diff --git a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-legacy.lua b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-legacy.lua
new file mode 100644
index 00000000000..e0c223033c1
--- /dev/null
+++ b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-legacy.lua
@@ -0,0 +1,266 @@
+module("luaotfload", package.seeall)
+
+luaotfload.module = {
+ name = "luaotfload-legacy",
+ version = 1.31,
+ date = "2013/04/25",
+ description = "Unsupported Luaotfload",
+ author = "Elie Roux & Hans Hagen",
+ copyright = "Elie Roux",
+ license = "GPL v2"
+}
+
+local error, warning, info, log = luatexbase.provides_module(luaotfload.module)
+
+--[[doc--
+
+ This used to be a necessary initalization in order not to rebuild an
+ existing font. Maybe 600 should be replaced by |\pdfpkresolution|
+ or |texconfig.pk_dpi| (and it should be replaced dynamically), but
+ we don't have access (yet) to the |texconfig| table, so we let it be
+ 600. Anyway, it does still work fine even if |\pdfpkresolution| is
+ changed.
+
+--doc]]--
+
+kpse.init_prog("", 600, "/")
+
+--[[doc--
+
+ The minimal required \luatex version.
+ We are tolerant folks.
+
+--doc]]--
+
+local luatex_version = 60
+if tex.luatexversion < luatex_version then
+ warning("LuaTeX v%.2f is old, v%.2f is required, v0.76 recommended.",
+ tex.luatexversion/100,
+ luatex_version /100)
+end
+
+--[[doc--
+
+ \subsection{Module loading}
+ We load the outdated \context files with this function. It
+ automatically adds the |otfl-| prefix to it, so that we call it with
+ the actual \context name.
+
+--doc]]--
+
+function luaotfload.loadmodule(tofind)
+ local found = kpse.find_file(tofind,"tex")
+ if found then
+ log("loading file %s.", found)
+ dofile(found)
+ else
+ error("file %s not found.", tofind)
+ end
+end
+local loadmodule = luaotfload.loadmodule
+
+--[[doc--
+
+ Keep away from these lines!
+
+--doc]]--
+loadmodule"luaotfload-legacy-merged.lua"
+
+if not fonts then
+ loadmodule("otfl-luat-dum.lua") -- not used in context at all
+ loadmodule("otfl-luat-ovr.lua") -- override some luat-dum functions
+ loadmodule("otfl-data-con.lua") -- maybe some day we don't need this one
+ loadmodule("otfl-font-ini.lua")
+ loadmodule("otfl-node-dum.lua")
+ loadmodule("otfl-node-inj.lua")
+--[[doc--
+ By default \context takes some private attributes for internal use. To
+ avoide attribute clashes with other packages, we override the function
+ that allocates new attributes, making it a wraper around
+ |luatexbase.new_attribute()|. We also prefix attributes with |otfl@| to
+ avoid possiple name clashes.
+--doc]]--
+ loadmodule("luaotfload-legacy-attributes.lua") -- patch attributes
+--[[doc--
+ Font handling modules.
+--doc]]--
+ loadmodule("otfl-font-tfm.lua")
+ loadmodule("otfl-font-cid.lua")
+ loadmodule("otfl-font-ott.lua")
+ loadmodule("otfl-font-map.lua")
+ loadmodule("otfl-font-otf.lua")
+ loadmodule("otfl-font-otd.lua")
+ loadmodule("otfl-font-oti.lua")
+ loadmodule("otfl-font-otb.lua")
+ loadmodule("otfl-font-otn.lua")
+ loadmodule("otfl-font-ota.lua")
+ loadmodule("otfl-font-otc.lua")
+ loadmodule("otfl-font-def.lua")
+--[[doc--
+ \textsf{old luaotfload} specific modules.
+--doc]]--
+ loadmodule("otfl-font-xtx.lua")
+ loadmodule("otfl-font-dum.lua")
+ loadmodule("otfl-font-clr.lua")
+end
+loadmodule"luaotfload-legacy-database.lua" --- unmerged coz needed in db script
+
+--[[doc--
+
+ This is a patch for |otfl-font-def.lua|, that defines a reader for ofm
+ fonts, this is necessary if we set the forced field of the specification
+ to |ofm|.
+
+--doc]]--
+
+if fonts and fonts.tfm and fonts.tfm.readers then
+ fonts.tfm.readers.ofm = fonts.tfm.readers.tfm
+end
+
+--[[doc--
+
+ \subsection{Post-processing TFM table}
+ Here we do some final touches to the loaded TFM table before passing it
+ to the \tex end.
+ First we create a callback for patching fonts on the fly, to be used by
+ other packages.
+
+--doc]]--
+
+luatexbase.create_callback("luaotfload.patch_font", "simple", function() end)
+
+--[[doc--
+
+ then define a function where font manipulation will take place.
+
+--doc]]--
+
+local function def_font(...)
+ local fontdata = fonts.define.read(...)
+ if type(fontdata) == "table" and fontdata.shared then
+--[[doc--
+
+ Then we populate |MathConstants| table, which is required for
+ OpenType math.
+
+ Note: actually it isn’t, but you’re asking for it by using outdated
+ code.
+
+--doc]]--
+ local otfdata = fontdata.shared.otfdata
+ if otfdata.metadata.math then
+ local mc = { }
+ for k,v in next, otfdata.metadata.math do
+ if k:find("Percent") then
+ -- keep percent values as is
+ mc[k] = v
+ else
+ mc[k] = v / fontdata.units * fontdata.size
+ end
+ end
+ -- for \overwithdelims
+ mc.FractionDelimiterSize = 1.01 * fontdata.size
+ mc.FractionDelimiterDisplayStyleSize = 2.39 * fontdata.size
+
+ fontdata.MathConstants = mc
+ end
+--[[doc--
+
+ Execute any registered font patching callbacks.
+
+--doc]]--
+ luatexbase.call_callback("luaotfload.patch_font", fontdata)
+ end
+ return fontdata
+end
+
+--[[doc--
+\subsection{\context override}
+
+ We have a unified function for both file and name resolver. This
+ line is commented as it makes database reload too often. This means
+ that in some cases, a font in the database will not be found if
+ it's not in the texmf tree. A similar thing will reappear in next
+ version.
+
+--doc]]--
+
+--fonts.define.resolvers.file = fonts.define.resolvers.name
+
+--[[doc--
+
+ Overriding some defaults set in \context code.
+
+--doc]]--
+
+fonts.mode = "node"
+
+--[[doc--
+
+ The following features are useful in math (e.g. in XITS Math font),
+ but \textsf{luaotfload} does not recognize them in |base| mode.
+
+--doc]]--
+
+local register_base_sub = fonts.otf.features.register_base_substitution
+local gsubs = {
+ "ss01", "ss02", "ss03", "ss04", "ss05",
+ "ss06", "ss07", "ss08", "ss09", "ss10",
+ "ss11", "ss12", "ss13", "ss14", "ss15",
+ "ss16", "ss17", "ss18", "ss19", "ss20",
+}
+
+for _,v in next, gsubs do
+ register_base_sub(v)
+end
+
+--[[doc--
+
+ Finally we register the callbacks
+
+--doc]]--
+
+luatexbase.add_to_callback("pre_linebreak_filter",
+ nodes.simple_font_handler,
+ "luaotfload.pre_linebreak_filter")
+luatexbase.add_to_callback("hpack_filter",
+ nodes.simple_font_handler,
+ "luaotfload.hpack_filter")
+luatexbase.reset_callback("define_font")
+luatexbase.add_to_callback("define_font",
+ def_font,
+ "luaotfload.define_font", 1)
+luatexbase.add_to_callback("find_vf_file",
+ fonts.vf.find,
+ "luaotfload.find_vf_file")
+--[[doc--
+
+ XXX: see https://github.com/wspr/unicode-math/issues/185
+ \luatex does not provide interface to accessing
+ |(Script)ScriptPercentScaleDown| math constants, so we
+ emulate \xetex behaviour by setting |\fontdimen10| and
+ |\fontdimen11|.
+
+ Note: actually, it does now, but not unless you update.
+
+--doc]]--
+
+local function set_sscale_diments(fontdata)
+ local mc = fontdata.MathConstants
+ if mc then
+ if mc["ScriptPercentScaleDown"] then
+ fontdata.parameters[10] = mc.ScriptPercentScaleDown
+ else -- resort to plain TeX default
+ fontdata.parameters[10] = 70
+ end
+ if mc["ScriptScriptPercentScaleDown"] then
+ fontdata.parameters[11] = mc.ScriptScriptPercentScaleDown
+ else -- resort to plain TeX default
+ fontdata.parameters[11] = 50
+ end
+ end
+end
+
+luatexbase.add_to_callback("luaotfload.patch_font", set_sscale_diments, "unicodemath.set_sscale_diments")
+
+-- vim:ts=2:sw=2:expandtab:ft=lua
diff --git a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-merged.lua b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-merged.lua
index bf22bac29e3..008b02af7ab 100644
--- a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-merged.lua
+++ b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-merged.lua
@@ -1,6 +1,6 @@
-- merged file : luatex-fonts-merged.lua
-- parent file : luatex-fonts.lua
--- merge date : 04/29/13 20:30:03
+-- merge date : 05/10/13 15:05:28
do -- begin closure to overcome local limits and interference
@@ -4586,7 +4586,7 @@ local floor=math.floor
local trace_loading=false trackers.register("fonts.loading",function(v) trace_loading=v end)
local trace_mapping=false trackers.register("fonts.mapping",function(v) trace_unimapping=v end)
local report_fonts=logs.reporter("fonts","loading")
-local fonts=fonts
+local fonts=fonts or {}
local mappings=fonts.mappings or {}
fonts.mappings=mappings
local function loadlumtable(filename)
@@ -4602,7 +4602,7 @@ local function loadlumtable(filename)
end
local hex=R("AF","09")
local hexfour=(hex*hex*hex*hex)/function(s) return tonumber(s,16) end
-local hexsix=(hex^1)/function(s) return tonumber(s,16) end
+local hexsix=(hex*hex*hex*hex*hex*hex)/function(s) return tonumber(s,16) end
local dec=(R("09")^1)/tonumber
local period=P(".")
local unicode=P("uni")*(hexfour*(period+P(-1))*Cc(false)+Ct(hexfour^1)*Cc(true))
@@ -4622,16 +4622,16 @@ local function makenameparser(str)
return p
end
end
-local function tounicode16(unicode)
+local function tounicode16(unicode,name)
if unicode<0x10000 then
return format("%04X",unicode)
elseif unicode<0x1FFFFFFFFF then
return format("%04X%04X",floor(unicode/1024),unicode%1024+0xDC00)
else
- report_fonts("can't convert %a into tounicode",unicode)
+ report_fonts("can't convert %a in %a into tounicode",unicode,name)
end
end
-local function tounicode16sequence(unicodes)
+local function tounicode16sequence(unicodes,name)
local t={}
for l=1,#unicodes do
local unicode=unicodes[l]
@@ -4640,7 +4640,7 @@ local function tounicode16sequence(unicodes)
elseif unicode<0x1FFFFFFFFF then
t[l]=format("%04X%04X",floor(unicode/1024),unicode%1024+0xDC00)
else
- report_fonts ("can't convert %a into tounicode",unicode)
+ report_fonts ("can't convert %a in %a into tounicode",unicode,name)
end
end
return concat(t)
@@ -4702,7 +4702,7 @@ function mappings.addtounicode(data,filename)
local unicode=lumunic and lumunic[name] or unicodevector[name]
if unicode then
originals[index]=unicode
- tounicode[index]=tounicode16(unicode)
+ tounicode[index]=tounicode16(unicode,name)
ns=ns+1
end
if (not unicode) and usedmap then
@@ -4711,7 +4711,7 @@ function mappings.addtounicode(data,filename)
unicode=cidcodes[foundindex]
if unicode then
originals[index]=unicode
- tounicode[index]=tounicode16(unicode)
+ tounicode[index]=tounicode16(unicode,name)
ns=ns+1
else
local reference=cidnames[foundindex]
@@ -4721,11 +4721,11 @@ function mappings.addtounicode(data,filename)
unicode=cidcodes[foundindex]
if unicode then
originals[index]=unicode
- tounicode[index]=tounicode16(unicode)
+ tounicode[index]=tounicode16(unicode,name)
ns=ns+1
end
end
- if not unicode then
+ if not unicode or unicode=="" then
local foundcodes,multiple=lpegmatch(uparser,reference)
if foundcodes then
originals[index]=foundcodes
@@ -4734,7 +4734,7 @@ function mappings.addtounicode(data,filename)
nl=nl+1
unicode=true
else
- tounicode[index]=tounicode16(foundcodes)
+ tounicode[index]=tounicode16(foundcodes,name)
ns=ns+1
unicode=foundcodes
end
@@ -4744,7 +4744,7 @@ function mappings.addtounicode(data,filename)
end
end
end
- if not unicode then
+ if not unicode or unicode=="" then
local split=lpegmatch(ligsplitter,name)
local nplit=split and #split or 0
if nplit>=2 then
@@ -4765,7 +4765,7 @@ function mappings.addtounicode(data,filename)
if n==0 then
elseif n==1 then
originals[index]=t[1]
- tounicode[index]=tounicode16(t[1])
+ tounicode[index]=tounicode16(t[1],name)
else
originals[index]=t
tounicode[index]=tounicode16sequence(t)
@@ -4775,17 +4775,17 @@ function mappings.addtounicode(data,filename)
else
end
end
- if not unicode then
+ if not unicode or unicode=="" then
local foundcodes,multiple=lpegmatch(uparser,name)
if foundcodes then
if multiple then
originals[index]=foundcodes
- tounicode[index]=tounicode16sequence(foundcodes)
+ tounicode[index]=tounicode16sequence(foundcodes,name)
nl=nl+1
unicode=true
else
originals[index]=foundcodes
- tounicode[index]=tounicode16(foundcodes)
+ tounicode[index]=tounicode16(foundcodes,name)
ns=ns+1
unicode=foundcodes
end
@@ -5046,7 +5046,7 @@ local report_otf=logs.reporter("fonts","otf loading")
local fonts=fonts
local otf=fonts.handlers.otf
otf.glists={ "gsub","gpos" }
-otf.version=2.742
+otf.version=2.743
otf.cache=containers.define("fonts","otf",otf.version,true)
local fontdata=fonts.hashes.identifiers
local chardata=characters and characters.data
@@ -5065,6 +5065,7 @@ local usemetatables=false
local packdata=true
local syncspace=true
local forcenotdef=false
+local includesubfonts=false
local wildcard="*"
local default="dflt"
local fontloaderfields=fontloader.fields
@@ -5191,8 +5192,8 @@ local ordered_enhancers={
"check glyphs",
"check metadata",
"check extra features",
- "add duplicates",
"check encoding",
+ "add duplicates",
"cleanup tables",
}
local actions=allocate()
@@ -5536,7 +5537,7 @@ actions["prepare glyphs"]=function(data,filename,raw)
local duplicates=resources.duplicates
local variants=resources.variants
if rawsubfonts then
- metadata.subfonts={}
+ metadata.subfonts=includesubfonts and {}
properties.cidinfo=rawcidinfo
if rawcidinfo.registry then
local cidmap=fonts.cid.getmap(rawcidinfo)
@@ -5547,7 +5548,9 @@ actions["prepare glyphs"]=function(data,filename,raw)
for cidindex=1,#rawsubfonts do
local subfont=rawsubfonts[cidindex]
local cidglyphs=subfont.glyphs
- metadata.subfonts[cidindex]=somecopy(subfont)
+ if includesubfonts then
+ metadata.subfonts[cidindex]=somecopy(subfont)
+ end
for index=0,subfont.glyphcnt-1 do
local glyph=cidglyphs[index]
if glyph then
@@ -5556,6 +5559,10 @@ actions["prepare glyphs"]=function(data,filename,raw)
if not unicode or unicode==-1 or unicode>=criterium then
unicode=cidunicodes[index]
end
+ if unicode and descriptions[unicode] then
+ report_otf("preventing glyph %a at index %H to overload unicode %U",name or "noname",index,unicode)
+ unicode=-1
+ end
if not unicode or unicode==-1 or unicode>=criterium then
if not name then
name=format("u%06X",private)
@@ -5659,7 +5666,8 @@ actions["check encoding"]=function(data,filename,raw)
local resources=data.resources
local properties=data.properties
local unicodes=resources.unicodes
- local indices=resources.indices
+ local indices=resources.indices
+ local duplicates=resources.duplicates
local mapdata=raw.map or {}
local unicodetoindex=mapdata and mapdata.map or {}
local encname=lower(data.enc_name or mapdata.enc_name or "")
@@ -5671,10 +5679,35 @@ actions["check encoding"]=function(data,filename,raw)
for unicode,index in next,unicodetoindex do
if unicode<=criterium and not descriptions[unicode] then
local parent=indices[index]
- if parent then
- report_otf("weird, unicode %U points to %U with index %H",unicode,parent,index)
- else
+ if not parent then
report_otf("weird, unicode %U points to nowhere with index %H",unicode,index)
+ else
+ local parentdescription=descriptions[parent]
+ if parentdescription then
+ local altuni=parentdescription.altuni
+ if not altuni then
+ altuni={ { unicode=parent } }
+ parentdescription.altuni=altuni
+ duplicates[parent]={ unicode }
+ else
+ local done=false
+ for i=1,#altuni do
+ if altuni[i].unicode==parent then
+ done=true
+ break
+ end
+ end
+ if not done then
+ altuni[#altuni+1]={ unicode=parent }
+ table.insert(duplicates[parent],unicode)
+ end
+ end
+ if trace_loading then
+ report_otf("weird, unicode %U points to nowhere with index %H",unicode,index)
+ end
+ else
+ report_otf("weird, unicode %U points to %U with index %H",unicode,index)
+ end
end
end
end
@@ -8468,16 +8501,18 @@ local function toligature(kind,lookupname,head,start,stop,char,markflag,discfoun
logwarning("%s: keep mark %s, gets index %s",pref(kind,lookupname),gref(char),start[a_ligacomp])
end
head,current=insert_node_after(head,current,copy_node(start))
+ elseif trace_marks then
+ logwarning("%s: delete mark %s",pref(kind,lookupname),gref(char))
end
start=start.next
end
- local start=components
- while start and start.id==glyph_code do
+ local start=current.next
+ while start and start.id==glyph_code do
local char=start.char
if marks[char] then
start[a_ligacomp]=baseindex+(start[a_ligacomp] or componentindex)
if trace_marks then
- logwarning("%s: keep mark %s, gets index %s",pref(kind,lookupname),gref(char),start[a_ligacomp])
+ logwarning("%s: set mark %s, gets index %s",pref(kind,lookupname),gref(char),start[a_ligacomp])
end
else
break
@@ -8751,6 +8786,10 @@ function handlers.gpos_mark2ligature(head,start,kind,lookupname,markanchors,sequ
pref(kind,lookupname),anchor,index,bound,gref(markchar),gref(basechar),index,dx,dy)
end
return head,start,true
+ else
+ if trace_bugs then
+ logwarning("%s: no matching anchors for mark %s and baselig %s with index %a",pref(kind,lookupname),gref(markchar),gref(basechar),index)
+ end
end
end
end
@@ -10685,6 +10724,12 @@ local function packdata(data)
end
end
end
+ local altuni=description.altuni
+ if altuni then
+ for i=1,#altuni do
+ altuni[i]=pack_flat(altuni[i])
+ end
+ end
end
local lookups=data.lookups
if lookups then
@@ -10789,6 +10834,10 @@ local function packdata(data)
mlookups[tag]=pack_normal(mlookup)
end
end
+ local altuni=description.altuni
+ if altuni then
+ description.altuni=pack_normal(altuni)
+ end
end
local lookups=data.lookups
if lookups then
@@ -10980,6 +11029,19 @@ local function unpackdata(data)
end
end
end
+ local altuni=description.altuni
+ if altuni then
+ local altuni=tables[altuni]
+ if altuni then
+ description.altuni=altuni
+ for i=1,#altuni do
+ local tv=tables[altuni[i]]
+ if tv then
+ altuni[i]=tv
+ end
+ end
+ end
+ end
end
local lookups=data.lookups
if lookups then
diff --git a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-override.lua b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-override.lua
index 863187fbd7d..99a6611e256 100644
--- a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-override.lua
+++ b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-override.lua
@@ -30,14 +30,25 @@ We recreate the verbosity levels previously implemented in font-nms:
local loglevel = 1 --- default
local logout = "log"
+--- int -> bool
local set_loglevel = function (n)
if type(n) == "number" then
loglevel = n
end
+ return true
end
+logs.setloglevel = set_loglevel
logs.set_loglevel = set_loglevel
logs.set_log_level = set_loglevel --- accomodating lazy typists
+--- unit -> int
+local get_loglevel = function ( )
+ return loglevel
+end
+logs.getloglevel = get_loglevel
+logs.get_loglevel = get_loglevel
+logs.get_log_level = get_loglevel
+
local set_logout = function (s)
if s == "stdout" then
logout = "term"
@@ -63,7 +74,7 @@ end
local level_ids = { common = 0, loading = 1, search = 2 }
-logs.names_report = function (mode, lvl, ...)
+local names_report = function (mode, lvl, ...)
if type(lvl) == "string" then
lvl = level_ids[lvl]
end
@@ -81,4 +92,54 @@ logs.names_report = function (mode, lvl, ...)
end
end
+logs.names_report = names_report
+
+--[[doc--
+
+ Adobe Glyph List.
+ -------------------------------------------------------------------
+
+ Context provides a somewhat different font-age.lua from an unclear
+ origin. Unfortunately, the file name it reads from is hard-coded
+ in font-enc.lua, so we have to replace the entire table.
+
+ This shouldn’t cause any complications. Due to its implementation
+ the glyph list will be loaded upon loading a OTF or TTF for the
+ first time during a TeX run. (If one sticks to TFM/OFM then it is
+ never read at all.) For this reason we can install a metatable that
+ looks up the file of our choosing and only falls back to the
+ Context one in case it cannot be found.
+
+--doc]]--
+
+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
+ collectgarbage"collect"
+ end
+
+
+ fonts.encodings.agl = { }
+
+ setmetatable(fonts.encodings.agl, { __index = function (t, k)
+ if k == "unicodes" then
+ local glyphlist = resolvers.findfile"luaotfload-glyphlist.lua"
+ if glyphlist then
+ names_report("both", 0, "load", "loading the Adobe glyph list")
+ else
+ glyphlist = resolvers.findfile"font-age.lua"
+ names_report("both", 0, "load", "loading the extended glyph list from ConTeXt")
+ end
+ local unicodes = dofile(glyphlist)
+ fonts.encodings.agl = { unicodes = unicodes }
+ return unicodes
+ else
+ return nil
+ end
+ end })
+end
+
-- vim:tw=71:sw=4:ts=4:expandtab
diff --git a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-tool.lua b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-tool.lua
index 0cd19b1d3a4..352697ced37 100755
--- a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-tool.lua
+++ b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-tool.lua
@@ -26,12 +26,29 @@ see the luaotfload documentation for more info. Report bugs to
--doc]]--
+--[[doc--
+
+ We test for Lua 5.1 by means of capability detection to see if
+ we’re running an outdated Luatex. If so, we hand over control to
+ the legacy db runner.
+
+ \url{http://lua-users.org/wiki/LuaVersionCompatibility}
+
+--doc]]--
+
kpse.set_program_name"luatex"
+if _G.getfenv then
+ local oldscript = kpse.find_file"luaotfload-legacy-tool.lua"
+ return require(oldscript)
+end
+
local stringformat = string.format
local texiowrite_nl = texio.write_nl
local stringlower = string.lower
+local C, Ct, P = lpeg.C, lpeg.Ct, lpeg.P
+local lpegmatch = lpeg.match
local loader_file = "luatexbase.loader.lua"
local loader_path = assert(kpse.find_file(loader_file, "lua"),
@@ -64,8 +81,6 @@ local config = config
config.luaotfload = config.luaotfload or { }
do -- we don’t have file.basename and the likes yet, so inline parser ftw
- local C, P = lpeg.C, lpeg.P
- local lpegmatch = lpeg.match
local slash = P"/"
local dot = P"."
local noslash = 1 - slash
@@ -90,6 +105,19 @@ config.lualibs.load_extended = false
require"lualibs"
+--- prepare directories: the cache function in Luatex-Fonts
+--- checks for writable directory only on startup, so everything
+--- has to be laid out before we load basics-gen
+
+local cachepath = kpse.expand_var"$TEXMFVAR"
+if not lfs.isdir(cachepath) then
+ dir.mkdirs(cachepath)
+ if not lfs.isdir(cachepath) then
+ texiowrite(stringformat(
+ "ERROR could not create directory %s", cachepath))
+ end
+end
+
--[[doc--
\fileent{luatex-basics-gen.lua} calls functions from the
\luafunction{texio.*} library; too much for our taste.
@@ -129,17 +157,21 @@ This tool is part of the luaotfload package. Valid options are:
-v --verbose=LEVEL be more verbose (print the searched directories)
-vv print the loaded fonts
-vvv print all steps of directory searching
+ --log=stdout redirect log output to stdout
+
-V --version print version and exit
-h --help print this message
--alias=<name> force behavior of “luaotfload-tool” or legacy
“mkluatexfontdb”
+
-------------------------------------------------------------------------------
DATABASE
-u --update update the database
-f --force force re-indexing all fonts
- -c --flush-cache empty cache of font requests
+ -l --flush-lookups empty lookup cache of font requests
+ -D --dry-run skip loading of fonts, just scan
--find="font name" query the database for a font name
-F --fuzzy look for approximate matches if --find fails
@@ -147,18 +179,26 @@ This tool is part of the luaotfload package. Valid options are:
(default: n = 1)
-i --info display font metadata
- --log=stdout redirect log output to stdout
+ --list=<criterion> output list of entries by field <criterion>
+ --list=<criterion>:<value> restrict to entries with <criterion>=<value>
+ --fields=<f1>,<f2>,…,<fn> which fields <f> to print with --list
The font database will be saved to
%s
%s
+-------------------------------------------------------------------------------
+ FONT CACHE
+
+ --cache=<directive> operate on font cache, where <directive> is
+ “show”, “purge”, or “erase”
+
]],
mkluatexfontdb = [[
Usage: %s [OPTION]...
-Rebuild the LuaTeX font database.
+Rebuild or update the LuaTeX font database.
Valid options:
-f --force force re-indexing all fonts
@@ -228,7 +268,8 @@ set.
--]]--
local action_sequence = {
- "loglevel", "help", "version", "flush", "generate", "query"
+ "loglevel", "help", "version", "cache",
+ "flush", "generate", "list", "query",
}
local action_pending = table.tohash(action_sequence, false)
@@ -241,6 +282,7 @@ actions.loglevel = function (job)
logs.set_loglevel(job.log_level)
logs.names_report("info", 3, "util",
"setting log level", "%d", job.log_level)
+ logs.names_report("log", 0, "util", "lua=%s", _VERSION)
return true, true
end
@@ -256,28 +298,47 @@ end
actions.generate = function (job)
local fontnames, savedname
- fontnames = names.update(fontnames, job.force_reload)
+ fontnames = names.update(fontnames, job.force_reload, job.dry_run)
logs.names_report("info", 2, "db",
"Fonts in the database: %i", #fontnames.mappings)
- savedname = names.save(fontnames)
- if savedname then --- FIXME have names.save return bool
+ local success = names.save(fontnames)
+ if success then
return true, true
end
return false, false
end
actions.flush = function (job)
- local success, lookups = names.flush_cache()
+ local success, lookups = names.flush_lookup_cache()
if success then
- local savedname = names.save_lookups()
- logs.names_report("info", 2, "cache", "Cache emptied")
- if savedname then
+ local success = names.save_lookups()
+ if success then
+ logs.names_report("info", 2, "cache", "Lookup cache emptied")
return true, true
end
end
return false, false
end
+local cache_directives = {
+ ["purge"] = names.purge_cache,
+ ["erase"] = names.erase_cache,
+ ["show"] = names.show_cache,
+}
+
+actions.cache = function (job)
+ local directive = cache_directives[job.cache]
+ if not directive or type(directive) ~= "function" then
+ logs.names_report("info", 2, "cache",
+ "Invalid font cache directive %s.", job.cache)
+ return false, false
+ end
+ if directive() then
+ return true, true
+ end
+ return false, false
+end
+
actions.query = function (job)
local query = job.query
@@ -286,7 +347,7 @@ actions.query = function (job)
lookup = "name",
specification = "name:" .. query,
optsize = 0,
- }
+ }
local foundname, subfont, success =
fonts.names.resolve(nil, nil, tmpspec)
@@ -317,6 +378,158 @@ actions.query = function (job)
return true, true
end
+--- --list=<criterion>
+--- --list=<criterion>:<value>
+---
+--- --list=<criterion> --fields=<f1>,<f2>,<f3>,...<fn>
+
+local get_fields get_fields = function (entry, fields, acc, n)
+ if not acc then
+ return get_fields(entry, fields, { }, 1)
+ end
+
+ local field = fields[n]
+ if field then
+ local value = entry[field]
+ acc[#acc+1] = value or false
+ return get_fields(entry, fields, acc, n+1)
+ end
+ return acc
+end
+
+local comma = P","
+local noncomma = 1-comma
+local split_comma = Ct((C(noncomma^1) + comma)^1)
+
+local texiowrite_nl = texio.write_nl
+local tableconcat = table.concat
+local stringexplode = string.explode
+
+local separator = "\t" --- could be “,” for csv
+
+local format_fields format_fields = function (fields, acc, n)
+ if not acc then
+ return format_fields(fields, { }, 1)
+ end
+
+ local field = fields[n]
+ if field ~= nil then
+ if field == false then
+ acc[#acc+1] = "<none>"
+ else
+ acc[#acc+1] = tostring(field)
+ end
+ return format_fields(fields, acc, n+1)
+ end
+ return tableconcat(acc, separator)
+end
+
+local set_primary_field
+set_primary_field = function (fields, addme, acc, n)
+ if not acc then
+ return set_primary_field(fields, addme, { addme }, 1)
+ end
+
+ local field = fields[n]
+ if field then
+ if field ~= addme then
+ acc[#acc+1] = field
+ end
+ return set_primary_field(fields, addme, acc, n+1)
+ end
+ return acc
+end
+
+actions.list = function (job)
+ local criterion = job.criterion
+
+ local asked_fields = job.asked_fields
+ if asked_fields then
+ asked_fields = lpegmatch(split_comma, asked_fields)
+ else
+ --- some defaults
+ asked_fields = { "fullname", "version", }
+ end
+
+ if not names.data then
+ names.data = names.load()
+ end
+
+ local mappings = names.data.mappings
+ local nmappings = #mappings
+
+ if criterion == "*" then
+ logs.names_report(false, 1, "list", "all %d entries", nmappings)
+ for i=1, nmappings do
+ local entry = mappings[i]
+ local fields = get_fields(entry, asked_fields)
+ --- we could collect these instead ...
+ local formatted = format_fields(fields)
+ texiowrite_nl(formatted)
+ end
+
+ else
+ criterion = stringexplode(criterion, ":") --> { field, value }
+ local asked_value = criterion[2]
+ criterion = criterion[1]
+ asked_fields = set_primary_field(asked_fields, criterion)
+
+ logs.names_report(false, 1, "list", "by %s", criterion)
+
+ --- firstly, build a list of fonts to operate on
+ local targets = { }
+ if asked_value then --- only those whose value matches
+ logs.names_report(false, 2, "list", "restricting to value %s", asked_value)
+ for i=1, nmappings do
+ local entry = mappings[i]
+ if entry[criterion]
+ and tostring(entry[criterion]) == asked_value
+ then
+ targets[#targets+1] = entry
+ end
+ end
+
+ else --- whichever have the field, sorted
+ local categories, by_category = { }, { }
+ for i=1, nmappings do
+ local entry = mappings[i]
+ local value = entry[criterion]
+ if value then
+ --value = tostring(value)
+ local entries = by_category[value]
+ if not entries then
+ entries = { entry }
+ categories[#categories+1] = value
+ else
+ entries[#entries+1] = entry
+ end
+ by_category[value] = entries
+ end
+ end
+ table.sort(categories)
+
+ for i=1, #categories do
+ local entries = by_category[categories[i]]
+ for j=1, #entries do
+ targets[#targets+1] = entries[j]
+ end
+ end
+ end
+ local ntargets = #targets
+ logs.names_report(false, 2, "list", "%d entries", ntargets)
+
+ --- now, output the collection
+ for i=1, ntargets do
+ local entry = targets[i]
+ local fields = get_fields(entry, asked_fields)
+ local formatted = format_fields(fields)
+ texiowrite_nl(formatted)
+ end
+ end
+
+ return true, true
+end
+
--[[--
Command-line processing.
mkluatexfontdb.lua relies on the script alt_getopt to process argv and
@@ -330,27 +543,32 @@ alt_getopt.
local process_cmdline = function ( ) -- unit -> jobspec
local result = { -- jobspec
force_reload = nil,
+ criterion = "",
query = "",
log_level = 1, --- 2 is approx. the old behavior
}
local long_options = {
- alias = 1,
- ["flush-cache"] = "c",
- find = 1,
- force = "f",
- fuzzy = "F",
- help = "h",
- info = "i",
- limit = 1,
- log = 1,
- quiet = "q",
- update = "u",
- verbose = 1 ,
- version = "V",
+ alias = 1,
+ cache = 1,
+ ["dry-run"] = "D",
+ ["flush-lookups"] = "l",
+ fields = 1,
+ find = 1,
+ force = "f",
+ fuzzy = "F",
+ help = "h",
+ info = "i",
+ limit = 1,
+ list = 1,
+ log = 1,
+ quiet = "q",
+ update = "u",
+ verbose = 1 ,
+ version = "V",
}
- local short_options = "cfFiquvVh"
+ local short_options = "DfFilquvVh"
local options, _, optarg =
alt_getopt.get_ordered_opts (arg, short_options, long_options)
@@ -399,14 +617,25 @@ local process_cmdline = function ( ) -- unit -> jobspec
result.show_info = true
elseif v == "alias" then
config.luaotfload.self = optarg[n]
- elseif v == "c" then
+ elseif v == "l" then
action_pending["flush"] = true
+ elseif v == "list" then
+ action_pending["list"] = true
+ result.criterion = optarg[n]
+ elseif v == "fields" then
+ result.asked_fields = optarg[n]
+ elseif v == "cache" then
+ action_pending["cache"] = true
+ result.cache = optarg[n]
+ elseif v == "D" then
+ result.dry_run = true
end
end
if config.luaotfload.self == "mkluatexfontdb" then
action_pending["generate"] = true
result.log_level = math.max(2, result.log_level)
+ logs.set_logout"stdout"
end
return result
end
diff --git a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload.lua b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload.lua
index 343abc5f830..0de49c021fe 100644
--- a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload.lua
+++ b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload.lua
@@ -26,11 +26,12 @@ local luaotfload = luaotfload
config = config or { }
config.luaotfload = config.luaotfload or { }
-------.luaotfload.resolver = config.luaotfload.resolver or "normal"
-config.luaotfload.resolver = config.luaotfload.resolver or "cached"
-config.luaotfload.definer = config.luaotfload.definer or "patch"
+------.luaotfload.resolver = config.luaotfload.resolver or "normal"
+config.luaotfload.resolver = config.luaotfload.resolver or "cached"
+config.luaotfload.definer = config.luaotfload.definer or "patch"
+config.luaotfload.compatibility = config.luaotfload.compatibility or false
config.luaotfload.loglevel = config.luaotfload.loglevel or 1
-config.luaotfload.color_callback = config.luaotfload.color_callback or "pre_linebreak_filter"
+config.luaotfload.color_callback = config.luaotfload.color_callback or "pre_linebreak_filter"
--luaotfload.prefer_merge = config.luaotfload.prefer_merge or true
luaotfload.module = {
@@ -61,9 +62,6 @@ local reset_callback, call_callback =
local dummy_function = function () end
-
-luaotfload.font_definer = "patch" --- | ^^e2^^80^^9cgeneric^^e2^^80^^9d | ^^e2^^80^^9cold^^e2^^80^^9d
-
local error, warning, info, log =
luatexbase.provides_module(luaotfload.module)
@@ -82,7 +80,16 @@ if tex.luatexversion < luatex_version then
end
-local fl_prefix = "luaotfload" -- ^^e2^^80^^9cluatex^^e2^^80^^9d for luatex-plain
+local cachepath = kpse.expand_var"$TEXMFVAR"
+if not lfs.isdir(cachepath) then
+ dir.mkdirs(cachepath)
+ if not lfs.isdir(cachepath) then
+ error( "ERROR could not create directory %s", cachepath)
+ end
+end
+
+
+local fl_prefix = "luaotfload" -- “luatex” for luatex-plain
local loadmodule = function (name)
require(fl_prefix .."-"..name)
end
@@ -119,7 +126,7 @@ do
attributes = attributes or { }
attributes.private = function (name)
- local attr = "luaotfload@" .. name --- used to be: ^^e2^^80^^9cotfl@^^e2^^80^^9d
+ local attr = "luaotfload@" .. name --- used to be: “otfl@”
local number = the_attributes[attr]
if not number then
number = new_attribute(attr)
@@ -182,13 +189,13 @@ if fonts then
if not fonts._merge_loaded_message_done_ then
--- a program talking first person -- HH sure believes in strong AI ...
- log[[^^e2^^80^^9cI am using the merged version of 'luaotfload.lua' here. If]]
+ log[[“I am using the merged version of 'luaotfload.lua' here. If]]
log[[ you run into problems or experience unexpected behaviour,]]
log[[ and if you have ConTeXt installed you can try to delete the]]
log[[ file 'luaotfload-font-merged.lua' as I might then use the]]
log[[ possibly updated libraries. The merged version is not]]
log[[ supported as it is a frozen instance. Problems can be]]
- log[[ reported to the ConTeXt mailing list.^^e2^^80^^9d]]
+ log[[ reported to the ConTeXt mailing list.”]]
end
fonts._merge_loaded_message_done_ = true
@@ -251,24 +258,32 @@ add_to_callback("find_vf_file",
find_vf_file, "luaotfload.find_vf_file")
loadmodule"lib-dir.lua" --- required by luaofload-database.lua
-loadmodule"override.lua" --- ^^e2^^80^^9cluat-ovr^^e2^^80^^9d
+loadmodule"override.lua" --- “luat-ovr”
logs.set_loglevel(config.luaotfload.loglevel)
-loadmodule"loaders.lua" --- ^^e2^^80^^9cfont-pfb^^e2^^80^^9d new in 2.0, added 2011
-loadmodule"database.lua" --- ^^e2^^80^^9cfont-nms^^e2^^80^^9d
-loadmodule"colors.lua" --- ^^e2^^80^^9cfont-clr^^e2^^80^^9d
+loadmodule"loaders.lua" --- “font-pfb” new in 2.0, added 2011
+loadmodule"database.lua" --- “font-nms”
+loadmodule"colors.lua" --- “font-clr”
local request_resolvers = fonts.definers.resolvers
local formats = fonts.formats
formats.ofm = "type1"
-request_resolvers.file = function (specification)
- local found = fonts.names.crude_file_lookup(specification.name)
- --local found = fonts.names.crude_file_lookup_verbose(specification.name)
- specification.name = found[1]
- --if format then specification.forced = format end
+
+local resolvefile = fonts.names.crude_file_lookup
+--local resolvefile = fonts.names.crude_file_lookup_verbose
+
+function request_resolvers.file(specification)
+ local name=resolvefile(specification.name)
+ local suffix=file.suffix(name)
+ if formats[suffix] then
+ specification.forced=suffix
+ specification.name=file.removesuffix(name)
+ else
+ specification.name=name
+ end
end
@@ -305,7 +320,7 @@ local read_font_file = fonts.definers.read
local patch_defined_font = function (specification, size, id)
local tfmdata = read_font_file(specification, size, id)
if type(tfmdata) == "table" and tfmdata.shared then
- --- We need to test for the ^^e2^^80^^9cshared^^e2^^80^^9d field here
+ --- We need to test for the “shared” field here
--- or else the fontspec capheight callback will
--- operate on tfm fonts.
call_callback("luaotfload.patch_font", tfmdata)
@@ -313,8 +328,6 @@ local patch_defined_font = function (specification, size, id)
return tfmdata
end
-caches.compilemethod = "both"
-
reset_callback("define_font")
@@ -332,7 +345,7 @@ elseif font_definer == "patch" then
1)
end
-loadmodule"features.lua" --- contains what was ^^e2^^80^^9cfont-ltx^^e2^^80^^9d and ^^e2^^80^^9cfont-otc^^e2^^80^^9d
+loadmodule"features.lua" --- contains what was “font-ltx” and “font-otc”
loadmodule"auxiliary.lua" --- additionaly high-level functionality (new)
-- vim:tw=71:sw=4:ts=4:expandtab
diff --git a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload.sty b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload.sty
index 77930c8d9b2..6afa40c8665 100644
--- a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload.sty
+++ b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload.sty
@@ -29,10 +29,16 @@
\else
\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{luaotfload}%
- [2013/04/16 v2.2 OpenType layout system]
+ [2013/05/10 v2.2 OpenType layout system]
\RequirePackage{luatexbase}
\fi
-\RequireLuaModule{luaotfload}
+\ifnum\luatexversion<76
+ %% here some deprecation warning would be in order
+ \RequireLuaModule{lualibs}
+ \RequireLuaModule{luaotfload-legacy}
+\else
+ \RequireLuaModule{luaotfload}
+\fi
\endinput
%%
%% End of file `luaotfload.sty'.
diff --git a/Master/tlpkg/libexec/ctan2tds b/Master/tlpkg/libexec/ctan2tds
index ed24118a9aa..6b8da32ed27 100755
--- a/Master/tlpkg/libexec/ctan2tds
+++ b/Master/tlpkg/libexec/ctan2tds
@@ -1143,6 +1143,7 @@ chomp ($Build = `cd $Master/../Build/source && pwd`);
'latex2e-help-texinfo' => '&POSTlatex2ehelptexinfo',
'listbib' => '&POSTlistbib',
'lithuanian' => '&POSTlithuanian',
+ 'luaotfload' => '&POST_do_man',
'luatexja' => '&POSTluatexja',
'm-tx' => '&POST_do_man',
'montex' => '&POSTmontex',
diff --git a/Master/tlpkg/tlpsrc/luaotfload.tlpsrc b/Master/tlpkg/tlpsrc/luaotfload.tlpsrc
index cca3c7d7a3b..9a41a47865c 100644
--- a/Master/tlpkg/tlpsrc/luaotfload.tlpsrc
+++ b/Master/tlpkg/tlpsrc/luaotfload.tlpsrc
@@ -1,2 +1,3 @@
binpattern f bin/${ARCH}/mkluatexfontdb
binpattern f bin/${ARCH}/luaotfload-tool
+docpattern +f texmf-dist/doc/man/man1/luaotfload-tool.*