summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/tex/luatex
diff options
context:
space:
mode:
authorKarl Berry <karl@freefriends.org>2023-01-02 21:31:04 +0000
committerKarl Berry <karl@freefriends.org>2023-01-02 21:31:04 +0000
commitb9587c576db3679c338a1d395c182023ff4d711c (patch)
treed36ea573074a6c7d740f15e323b5ec7720e976bc /Master/texmf-dist/tex/luatex
parent7bde2bb3891ad0c87d05ad80fabb4258dc5b84fb (diff)
blopentype (2jan23)
git-svn-id: svn://tug.org/texlive/trunk@65441 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist/tex/luatex')
-rw-r--r--Master/texmf-dist/tex/luatex/blopentype/blot-base.lua88
-rw-r--r--Master/texmf-dist/tex/luatex/blopentype/blot-files.tex39
-rw-r--r--Master/texmf-dist/tex/luatex/blopentype/blot-fonts.lua1213
-rw-r--r--Master/texmf-dist/tex/luatex/blopentype/blot-fonts.tex308
-rw-r--r--Master/texmf-dist/tex/luatex/blopentype/blot-lua.tex26
-rw-r--r--Master/texmf-dist/tex/luatex/blopentype/blot-sets.lua19
-rw-r--r--Master/texmf-dist/tex/luatex/blopentype/blot.tex254
7 files changed, 1947 insertions, 0 deletions
diff --git a/Master/texmf-dist/tex/luatex/blopentype/blot-base.lua b/Master/texmf-dist/tex/luatex/blopentype/blot-base.lua
new file mode 100644
index 00000000000..ef933ee0888
--- /dev/null
+++ b/Master/texmf-dist/tex/luatex/blopentype/blot-base.lua
@@ -0,0 +1,88 @@
+require"gates.lua"
+
+pitex = gates.new("pitex")
+pitex.misc = gates.new("pitex.misc")
+
+function pitex.log (message, ...)
+ texio.write_nl(string.format("\n" .. message .. "\n", ...))
+end
+function pitex.error (...)
+ tex.error ("! PiTeX error: " .. string.format(...) .. ".")
+end
+
+pitex.callback = gates.new("pitex.callback")
+
+-- Creates a gate with a callback's name and put it in that callback, if not
+-- already there. Adds the subgate(s).
+function pitex.callback.register (c, f)
+ if pitex.callback.type(c) == 2 then
+ pitex.callback.add(f, c)
+ else
+ pitex.callback.list{c}
+ pitex.callback.add(f, c)
+ callback.register(c, pitex.callback.execute[c])
+ end
+end
+
+-- Latin1 to UTF-8.
+local char = unicode.utf8.char
+local function convert_char (ch)
+-- return char(string.byte(ch))
+end
+function pitex.callback.convert (buf)
+ return string.gsub(buf,".",convert_char)
+end
+
+pitex.callback.register("process_input_buffer", "convert")
+
+function remove_conversion ()
+ pitex.callback.close("convert", "process_input_buffer")
+end
+function restore_conversion ()
+ pitex.callback.open("convert", "process_input_buffer")
+end
+
+--[[
+
+require("nodeinspector")
+new_inspection = nodeinspector.new_inspection
+
+local french_highmarks = {
+ string.byte("?"),
+ string.byte("!"),
+ string.byte(":"),
+ string.byte(";"),
+ }
+local french_marks = {
+ string.byte("?"),
+ string.byte("!"),
+ string.byte(":"),
+ string.byte(";"),
+ string.byte(","),
+ string.byte("."),
+ string.byte("("),
+ string.byte("["),
+ string.byte("{"),
+ }
+
+local function french_punctuation (head, ...)
+ for _, glue in ipairs(arg) do
+ if glue.id == 10 then
+ head = node.remove(head, glue)
+ node.free(glue)
+ end
+ end
+ local kern = node.new(11, 1)
+ kern.kern = tex.sp(".15em")
+ node.insert_after(head, arg[1], kern)
+end
+
+pitex.callback.french_punctuation = new_inspection(
+ french_punctuation,
+ {{id = 37, _char = french_marks}, {true, id = 37, char = french_highmarks}},
+ {{id = 10, subtype = 0}, {id = 11, subtype = 0}})
+
+pitex.callback.register("kerning", "french_punctuation")
+pitex.callback.original_kerning = node.kerning
+pitex.callback.register("kerning", "original_kerning")
+--]] \ No newline at end of file
diff --git a/Master/texmf-dist/tex/luatex/blopentype/blot-files.tex b/Master/texmf-dist/tex/luatex/blopentype/blot-files.tex
new file mode 100644
index 00000000000..212b05fc076
--- /dev/null
+++ b/Master/texmf-dist/tex/luatex/blopentype/blot-files.tex
@@ -0,0 +1,39 @@
+\newread\ptx@temp_read
+\def\iffile{%
+ \ifnext[
+ {\ptx@iffile}
+ {\ptx@iffile[]}%
+ }
+\def\ptx@iffile[#1]#2{%
+ \directlua{
+ local f = kpse.find_file("\luaescapestring{#2}"\reverse\iffemptystring{#1}{, "#1"})
+ local x = f and "firstoftwo" or "secondoftwo"
+ tex.print(\the\texcatcodes, "\noexpand\\" .. x)
+ }%
+ }
+\long\def\ifffile{%
+ \ifnext[
+ {\ptx@ifffile}
+ {\ptx@ifffile[]}%
+ }
+\long\def\ptx@ifffile[#1]#2#3{%
+ \iffile[#1]{#2}{#3}{}%
+ }
+
+\long\def\inputfileor{%
+ \ifnext[
+ {\ptx@inputfileor}
+ {\ptx@inputfileor[]}%
+ }
+\long\def\ptx@inputfileor[#1]#2{%
+ \iffile[#1]{#2}{\input{#2}\relax}%
+ }
+
+\newwrite\ptx@auxfile
+\def\ptx@write_toaux{%
+ \ifnext*
+ {\gobbleoneand{\write\ptx@auxfile}}
+ {\immediate\write\ptx@auxfile}%
+ }
+
+\let\writeout\ptx@write_toaux
diff --git a/Master/texmf-dist/tex/luatex/blopentype/blot-fonts.lua b/Master/texmf-dist/tex/luatex/blopentype/blot-fonts.lua
new file mode 100644
index 00000000000..638f83e984e
--- /dev/null
+++ b/Master/texmf-dist/tex/luatex/blopentype/blot-fonts.lua
@@ -0,0 +1,1213 @@
+-- *** UTILITY FUNCTIONS ***
+local function get_locals (tab)
+ local tb = {}
+ for lib, keys in pairs(tab) do
+ keys = string.explode(keys)
+ for _, k in ipairs(keys) do
+ tb[k] = _G[lib][k]
+ end
+ end
+ return tb
+end
+
+-- str
+-- string manipulation
+local str = get_locals {string = "explode gsub match format lower upper"}
+
+-- Removes space at the beginning and end and conflates multiple spaces.
+function str.trim (s)
+ s = str.match(s, "^%s*(.-)%s*$")
+ s = str.gsub(s, "%s+", " ")
+ return s
+end
+
+-- Extracts a pattern from a string, i.e. removes it and returns it.
+-- If "full", then the entire pattern is removed, not only the captures.
+function str.extract (s, pat, full)
+ local cap = str.match(s, pat)
+ if cap then
+ if full then
+ s = str.gsub(s, pat, "")
+ else
+ s = str.gsub(s, cap, "")
+ end
+ end
+ return cap, s
+end
+-- /str
+
+
+-- lp
+-- advanced string manipulation
+lpeg.locale(lpeg)
+local lp = get_locals {lpeg = "match P C S Ct V alnum"}
+lp.space = lpeg.space^0
+-- /lp
+
+
+-- tab
+-- table manipulation
+local tab = get_locals {table = "insert remove sort"}
+
+-- Adds val to tab, creating it if necessary.
+function tab.update (tb, val)
+ tb = tb or {}
+ tab.insert(tb, val)
+ return tb
+end
+
+-- Adds subtable (empty, or val) to tb at entry key, unless it already exists.
+function tab.subtable (tb, key, val)
+ tb[key] = tb[key] or val or {}
+end
+
+-- Writes a table to an extenal file.
+local function write_key (key, ind)
+ return ind .. '["' .. key .. '"] = '
+end
+
+function tab.write (tb, f, ind)
+ for a, b in pairs (tb) do
+ if type(a) == "string" then
+ a = '"' .. a .. '"'
+ end
+ a = "[" .. a .. "]"
+ if type(b) == "table" then
+ f:write(ind, a, " = {")
+ tab.write(b, f, ind .. " ")
+ f:write(ind, "},")
+ else
+ if type(b) == "boolean" then
+ b = b and "true" or "false"
+ elseif type(b) == "string" then
+ b = '"' .. b .. '"'
+ end
+ f:write(ind, a, " = ", b, ",")
+ end
+ end
+end
+
+-- Returns a full copy of a table. Not copying of metatables necessary for the
+-- moment.
+function tab.copy (tb)
+ local t = {}
+ for k, v in pairs (tb) do
+ if type(v) == "table" then
+ v = tab.copy(v)
+ end
+ t[k] = v
+ end
+ return t
+end
+
+-- Sorts two tables containing modifiers (Italic, etc.).
+function tab.sortmods (a, b)
+ local A, B = "", ""
+ for _, x in ipairs (a) do
+ A = A .. " " .. x
+ end
+ for _, x in ipairs (b) do
+ B = B .. " " .. x
+ end
+ return A < B
+end
+
+-- Turns an array into a hash.
+function tab.tohash(tb)
+ local t = {}
+ for _, k in ipairs(tb) do
+ t[k] = true
+ end
+ return t
+end
+-- /tab
+
+-- lfs
+-- files etc.
+local lfs = get_locals {lfs = "dir isdir isfile mkdir", kpse = "expand_var show_path find_file"}
+
+-- Returns anything after the last dot, i.e. an extension.
+function lfs.extension (s)
+ return str.lower(str.match(s, "%.([^%.]*)$"))
+end
+
+local extensions = {
+ otf = "opentype",
+ ttf = "truetype",
+ ttc = "truetype",
+}
+function lfs.type (s)
+ return extensions[lfs.extension(s)]
+end
+
+local kpse_extensions = {
+ otf = "opentype fonts",
+ ttf = "truetype fonts",
+ ttc = "truetype fonts",
+}
+function lfs.kpse (s)
+ return kpse_extensions[lfs.extension(s)]
+end
+
+-- Returns anything after the last slash, i.e. a pathless file.
+function lfs.nopath (f)
+ return str.match(f, "[^/]*$")
+end
+
+-- Creates a directory; the arguments are the successive subdirectories.
+function lfs.ensure_dir (...)
+ local arg, path = {...}
+ for _, d in ipairs(arg) do
+ if path then
+ path = path .. "/" .. d
+ else
+ path = d
+ end
+ path = str.gsub(path, "//", "/")
+ if not lfs.isdir(path) then
+ lfs.mkdir(path)
+ end
+ end
+ return path
+end
+
+-- Turns "foo/blahblah/../" into "foo/" (such going into and leaving
+-- directories happens with kpse). Also puts everything to lowercase.
+function lfs.smooth_file (f)
+ f = str.gsub(f, "/.-/%.%./", "/")
+ f = str.gsub(f, "^%a", str.lower)
+ return f
+end
+-- /lfs
+
+
+-- wri
+-- messages
+local wri, write_nl = {}, texio.write_nl
+
+function wri.report (s, ...)
+-- write_nl(str.format(s, unpack(arg)))
+end
+
+function wri.error (s, ...)
+-- tex.error(str.format(s, unpack(arg)))
+end
+-- /wri
+
+
+-- various
+-- the last one is, of course, not the least
+local io = get_locals {io = "open lines"}
+local os = get_locals {os = "date"}
+local num = get_locals {math = "abs tan rad floor pi", tex = "sp"}
+local fl = get_locals {fontloader = "open close to_table info", font = "read_tfm"}
+-- /various
+
+
+-- *** END OF UTILITY FUNCTIONS ***
+
+
+
+--- *** CREATING THE LIBRARY *** ---
+local settings
+if lfs.find_file"blot-sets.lua" then
+ settings = require"blot-sets.lua"
+else
+ settings = {normal = {}, features = {}}
+end
+local font_families = {}
+local normal_names = {}
+for _, name in ipairs(settings.normal) do
+ normal_names[name] = true
+end
+local local_path = lfs.expand_var("$TEXMFHOME")
+local foundry_path = lfs.ensure_dir (local_path, "tex", "luatex", "foundry")
+-- local local_path = lfs.expand_var("$TEXMFHOME")
+-- local foundry_path = lfs.ensure_dir (local_path, "fonts", "truetype", "public", "gfs")
+local library_file = foundry_path .. "/" .. "readable.txt"
+-- local library_file = "c:/texlive/texmf-local/tex/plain/pitex/readable.txt"
+-- local library_file = "readable.txt"
+
+-- Analyze a font file and return a name and a table
+-- with modifiers.
+local function extract_font (file, names)
+ local fi, subname
+ -- Trying to open a font in ttc, using the names returned by fontloader.info
+ if names then
+ local name = names.fullname
+ if name then
+ fi = fl.open (file, name)
+ end
+ if not fi then
+ name = names.fontname
+ if name then
+ fi = fl.open
+ end
+ if not fi then
+ fl.error("Can't open %s", file)
+ return
+ end
+ end
+ subname = name
+ else
+ fi = fl.open(file)
+ end
+ -- Getting the most precise information. Not necessarily the best
+ -- solution, but since the user can modify the library, it's not so bad.
+ local fam, name = fi.names[1].names.preffamilyname or fi.names[1].names.family or fi.familyname, fi.fontname
+ local spec = fi.names[1].names.prefmodifiers or fi.names[1].names.subfamily or ""
+ local subfam, _spec
+ local t = { [0] = file }
+ -- Removing mods like Regular, Book, etc.
+ for name in pairs(normal_names) do
+ spec = str.gsub(spec, name, "")
+ end
+ if subname then
+ tab.insert(t, "[font = " .. subname .. "]")
+ end
+ if spec ~= "" then
+ spec = str.explode(spec)
+ for _, s in ipairs(spec) do
+ tab.insert(t, s)
+ end
+ end
+ fl.close(fi)
+ return fam, t
+end
+
+-- Searches directories for font files, and pass them to
+-- extract_font. The fonts are collected in a table.
+-- The fonts_done table is updated when the library is read,
+-- so when a font is missing and one needs to recheck files,
+-- only those that arent in the libraries are considered.
+local fonts_done = {}
+local function check_fonts (rep, tb)
+ for f in lfs.dir (rep) do
+ if f ~= "." and f ~= ".." then
+ f = str.gsub(rep, "/$", "") .. "/" .. f
+ if lfs.isdir(f) then
+ check_fonts(f, tb)
+ elseif lfs.isfile(f) and not fonts_done[lfs.nopath(f)] then
+ local e = lfs.extension(f)
+ if e == "ttf" or e == "otf" then
+ local fam, file = extract_font(f)
+ if fam then
+ tab.subtable(tb, fam)
+ tab.insert(tb[fam], file)
+ end
+ elseif e == "ttc" then
+ local info = fl.info(f)
+ for _, i in ipairs(info) do
+ local fam, file = extract_font(f, i)
+ if fam then
+ tab.subtable(tb, fam)
+ tab.insert(tb[fam], file)
+ end
+ end
+ end
+ end
+ end
+ end
+end
+
+-- Writes the library to an external file.
+-- Type is "a" if what's going on is recheck_fonts.
+local function write_lib (fams, file, type)
+ local read_table = {}
+ for fam, tb in pairs(fams) do
+ tab.insert(read_table, fam)
+ for _, ttb in ipairs(tb) do
+ tab.sort(ttb)
+ end
+ tab.sort(tb, tab.sortmods)
+ end
+ tab.sort(read_table)
+ local readable = io.open(file, type)
+ for n, fam in ipairs(read_table) do
+ local log
+ if type == "a" then
+ log = true
+ if n == 1 then
+ wri.report("\nAdding new font(s):")
+ readable:write("\n\n% Added automatically " .. os.date() .. "\n\n")
+ end
+ wri.report(fam)
+ end
+ readable:write(fam .. " :")
+ for n, f in ipairs(fams[fam]) do
+ log = log and " "
+ readable:write("\n ")
+ for _, t in ipairs(f) do
+ log = log and log .. " " .. t
+ readable:write(" " .. t)
+ end
+ log = log and log .. " " .. '"' .. f[0] .. '"'
+ readable:write(" " .. '"' .. lfs.nopath(f[0]) .. '",')
+ if log then wri.report(log) end
+ if n == #fams[fam] then
+ readable:write("\n\n")
+ end
+ end
+ end
+ readable:close()
+end
+
+
+-- If there is no library, we create it.
+local font_paths = lfs.show_path("opentype fonts")
+font_paths = str.gsub(font_paths, "\\", "/")
+font_paths = str.gsub(font_paths, "/+", "/")
+font_paths = str.gsub(font_paths, "!!", "")
+font_paths = str.explode(font_paths, ";+")
+
+if not lfs.find_file(library_file) then
+ wri.report("I must create the library; please wait, that can take some time.")
+ for _, rep in ipairs(font_paths) do
+ check_fonts(rep, font_families)
+ end
+ write_lib(font_families, library_file, "w")
+end
+
+-- Reads the library file, turning it into a table.
+local explode_semicolon = lp.P{
+ lp.Ct(lp.V"data"^1),
+ data = lp.C(((1 - lp.S";[") + (lp.S"[" * (1 - lp.S"]")^0 * lp.S"]" ))^1) / str.trim * (lp.S";" + -1),
+ }
+local explode_comma = lp.P{
+ lp.Ct(lp.V"data"^1),
+ data = lp.C(((1 - lp.S",[") + (lp.S"[" * (1 - lp.S"]")^0 * lp.S"]" ))^1) / str.trim * (lp.S"," + -1),
+ }
+
+local function load_library (lib)
+ local LIB = ""
+ local lib_file = lfs.find_file(lib)
+ if not lib_file then
+ wri.error("I can't find library %s.", lib)
+ return
+ end
+
+ for l in io.lines(lib_file) do
+ if not str.match(l, "^%s*%%") then
+ if str.match(l, "^%s*$") then
+ LIB = LIB .. ";"
+ else
+ LIB = LIB .. " " .. l
+ end
+ end
+ end
+ LIB = str.gsub(LIB, ";%s*;", ";;")
+ LIB = str.gsub(LIB, ";+", ";")
+ LIB = str.gsub(LIB, "^;", "")
+ LIB = str.gsub(LIB, "%s+", " ")
+
+ LIB = lp.match(explode_semicolon, LIB)
+
+ local newlib = {}
+ for _, t in ipairs(LIB) do
+ local fam, files = str.match(t, "(.-):(.*)")
+ local current_mods
+ if files then
+ files = lp.match(explode_comma, files)
+ fam = str.explode(fam, ",")
+ local root
+ for n, f in ipairs (fam) do
+ f = str.trim(f)
+ if n == 1 then
+ root = f
+ if type(newlib[f]) == "string" then
+ wri.error("Name `%s' is already used as an alias for `%s'; it is now overwritten to denote a family", f, newlib[f])
+ newlib[f] = {}
+ else
+ newlib[f] = newlib[f] or {}
+ end
+ else
+ if newlib[f] then
+ wri.error("The name `%s' is already used. I ignore it as an alias for `%s'", f, root)
+ else
+ newlib[f] = root
+ end
+ end
+ end
+ for _, f in ipairs(files) do
+ local reset
+ reset, f = str.extract(f, "^%.%.", true)
+ if reset then current_mods = nil end
+ local mods, file, feats = str.match(f, '([^"]*)"(.*)"')
+ if mods then
+ fonts_done[lfs.nopath(file)] = true
+ feats, mods = str.extract(mods, "%[([^%]]-)]", true)
+ mods = str.explode(mods)
+ if current_mods then
+ for _, t in ipairs(current_mods) do
+ tab.insert(mods, t)
+ end
+ if current_mods.feats then
+ feats = feats or ""
+ feats = current_mods.feats .. "," .. feats
+ end
+ end
+ local sizes, real_mods = {}, {}
+ for n, s in ipairs(mods) do
+ if tonumber(s) then
+ tab.insert(sizes, tonumber(s))
+ else
+ tab.insert(real_mods, s)
+ end
+ end
+ sizes = #sizes > 0 and sizes or {0}
+ tab.sort(real_mods)
+ local T = newlib[root]
+ for _, t in ipairs(real_mods) do
+ t = str.trim(t)
+ if t ~= "" then
+ T[t] = T[t] or {}
+ T = T[t]
+ end
+ end
+ T.__files = T.__files or {}
+ for _, s in ipairs(sizes) do
+ T.__files[s] = {str.trim(file), feats}
+ end
+ else
+ feats, mods = str.extract(f, "%[([^%]]-)]", true)
+ if current_mods then
+ for _, mod in ipairs(str.explode(mods)) do
+ tab.insert(current_mods, mod)
+ end
+ if feats then
+ current_mods.feats = current_mods.feats and current_mods.feats .. "," .. feats or feats
+ end
+ else
+ current_mods = str.explode(mods)
+ current_mods.feats = feats
+ end
+ end
+ end
+ end
+ end
+ return newlib
+end
+
+local library = {}
+library.default = load_library(library_file)
+
+-- Same as above, but used when rechecking (if a font isn't found in libraries).
+local function recheck_fonts ()
+ local tb = {}
+ for _, rep in ipairs(font_paths) do
+ check_fonts(rep, tb)
+ end
+ write_lib(tb, library_file, "a")
+ library.default = load_library(library_file)
+end
+
+
+-- This is public.
+function new_library (lib)
+ local l = load_library(lib)
+ if l then
+ tab.insert(library, l)
+ end
+end
+
+--- *** END OF LIBRARY MANAGEMENT *** ---
+
+
+
+--- *** FONT CREATION *** ---
+
+
+-- Creates a new substitution for trep.
+local function add_sub (f, num, sub)
+ num, sub = f.map.map[num], f.map.map[sub]
+ if f.glyphs[num] and f.glyphs[sub] then
+ local x = f.glyphs[num]
+ tab.subtable(x, "lookups")
+ x.lookups.tex_trep = { { type = "substitution", specification = {variant = f.glyphs[sub].name} } }
+ end
+end
+
+-- Creates a new ligature for tlig.
+local function add_lig (f, lig, ...)
+ lig = f.map.map[lig]
+ if lig then
+ arg = {...}
+ local components
+ for _, c in ipairs(arg) do
+ c = f.map.map[c]
+ c = c and f.glyphs[c]
+ if c then
+ c = c.name
+ components = components and components .. " " .. c or c
+ else
+ components = nil
+ break
+ end
+ end
+ if components then
+ local x = f.glyphs[lig]
+ tab.subtable(x, "lookups")
+ tab.subtable(x.lookups, "tex_tlig")
+ tab.insert(x.lookups.tex_tlig, { type = "ligature",
+ specification = {char = x.name, components = components} })
+ end
+ end
+end
+
+-- Loops over all the constitutents of ligatures, creating intermediary
+-- ligatures if necessary. E.g. "f f i" is broken into:
+-- f + f = ff.lig
+-- ff.lig + i = ffi.lig
+-- Then when loading the font if the intermediary ligatures do not exist
+-- (e.g. "1/" in "1 / 4") a phantom character is added to the font; which might
+-- be dangerous (e.g. "1/" will create a node without character if no "4" follows).
+-- The ".lig" suffix is arbitrary but all glyphs marked as ligatures are also registered
+-- with such a name, so if there's an "f f" ligature in a font, no matter its name, "ff.lig"
+-- will point to it.
+local function ligature (comp, tb, phantoms)
+ local i = str.gsub(comp[1], "%.lig$", "") .. comp[2] .. ".lig"
+ phantoms[i] = true
+ tab.insert(tb.all_ligs, i)
+ tab.subtable(tb, comp[1])
+ tb[comp[1]][comp[2]] = { char = i, type = 0 } -- The type could be something else.
+ tab.remove(comp, 1)
+ tab.remove(comp, 1)
+ if #comp > 0 then
+ tab.insert(comp, 1, i)
+ ligature(comp, tb, phantoms)
+ end
+end
+
+local function get_lookups (t, lookup_table)
+ if t then
+ for _, tb in pairs(t) do
+ local _tb = { tags = {} }
+ if tb.features then
+ for _, feats in pairs(tb.features) do
+ local _tag = {}
+ if feats.scripts then
+ for _, scr in pairs(feats.scripts) do
+ _tag[scr.script] = {}
+ for _, lang in pairs(scr.langs) do
+ tab.insert (_tag[scr.script], str.trim(lang))
+ end
+ end
+ end
+ for _, sub in pairs(tb.subtables) do
+ tab.insert(_tb, sub.name)
+ end
+ _tb.tags[feats.tag] = _tag
+ end
+ end
+ if tb.name then
+ local tp = tb.type or "no_type"
+ tab.subtable(lookup_table, tp)
+ lookup_table[tp][tb.name] = _tb
+ end
+ end
+ end
+end
+
+function create_font (filename, extension, path, subfont, write)
+
+ local data = fl.open(filename, subfont)
+ fontfile = fl.to_table(data)
+ fl.close(data)
+
+ local lookups = {}
+
+ local name_touni = { }
+ local max_char = 0
+ for chr, gly in pairs(fontfile.map.map) do
+ max_char = chr > max_char and chr or max_char
+ -- Some glyphs have the same name in some fonts,
+ -- e.g. the several hyphens.
+ local name = fontfile.glyphs[gly].name
+ while name_touni[name] do
+ name = name .. "_"
+ end
+ name_touni[name] = chr
+ end
+
+ if fontfile.gsub then
+ tab.insert(fontfile.gsub,
+ { type = "gsub_single",
+ name = "tex_trep",
+ subtables = { {name = "tex_trep"} },
+ features = { { tag = "trep"} } })
+ tab.insert(fontfile.gsub,
+ { type = "gsub_ligature",
+ name = "tex_tlig",
+ subtables = { {name = "tex_tlig"} },
+ features = { { tag = "tlig"} } })
+ for _, tb in ipairs(fontfile.gsub) do
+ for __, ttb in ipairs(tb.subtables) do
+ if tb.type == "gsub_contextchain" then
+ lookups[tb.name] = lookups[tb.name] or { type = tb.type }
+ tab.insert(lookups[tb.name], ttb.name)
+ end
+ lookups[ttb.name] = { type = tb.type }
+ lookups["-" .. ttb.name] = { type = tb.type }
+ end
+ end
+ end
+
+ if fontfile.gpos then
+ for _, tb in ipairs(fontfile.gpos) do
+ for __, ttb in ipairs(tb.subtables) do
+ lookups[ttb.name] = { type = tb.type }
+ lookups["-" .. ttb.name] = { type = tb.type }
+ end
+ end
+ end
+
+ if fontfile.kerns then
+ for _, class in ipairs(fontfile.kerns) do
+ local max = 0
+ for a in pairs (class.seconds) do
+ max = max < a and a or max
+ end
+ if type(class.lookup) == "string" then
+ lookups[class.lookup] = { type = "gpos_pair", firsts = class.firsts, seconds = class.seconds, offsets = class.offsets, max = max}
+ else
+ for _, lk in ipairs(class.lookup) do
+ lookups[lk] = { type = "gpos_pair", firsts = class.firsts, seconds = class.seconds, offsets = class.offsets, max = max}
+ end
+ end
+ end
+ end
+
+ add_sub(fontfile, 96, 8216) -- ` to quoteleft
+ add_sub(fontfile, 39, 8217) -- ' to apostrophe (quoteright)
+
+ add_lig(fontfile, 8220, 8216, 8216) -- quoteleft + quoteleft to quotedblleft
+ add_lig(fontfile, 8221, 8217, 8217) -- quoteright + quoteright to quotedblright
+ add_lig(fontfile, 8211, 45, 45) -- -- to endash
+ add_lig(fontfile, 8212, 8211, 45) -- --- (i.e. endash + -) to emdash
+ add_lig(fontfile, 161, 63, 96) -- ?` to inverted question mark
+ add_lig(fontfile, 161, 63, 8216) -- The same, with `turned to quoteleft.
+ add_lig(fontfile, 191, 33, 96) -- !` to inverted exclamation mark
+ add_lig(fontfile, 191, 33, 8216) -- Idem.
+
+ local characters, phantom_ligatures = {}, {}
+ for chr, gly in pairs(fontfile.map.map) do
+ local glyph, char = fontfile.glyphs[gly], {}
+
+ char.index = gly
+ char.name = glyph.name
+ char.width = glyph.width
+ if glyph.boundingbox then
+ char.depth = -glyph.boundingbox[2]
+ char.height = glyph.boundingbox[4]
+ end
+ if glyph.italic_correction then
+ char.italic = glyph.italic_correction
+ elseif glyph.width and glyph.boundingbox then
+ char.italic = glyph.boundingbox[3] - glyph.width
+ end
+ tab.subtable(characters, chr)
+ characters[chr] = char
+
+ if glyph.lookups then
+ for lk, tb in pairs(glyph.lookups) do
+ local _lk = "-" .. lk
+ if lookups[lk] and lookups[_lk] then
+ for _, l in ipairs(tb) do
+ if l.type == "substitution" then
+
+ tab.subtable(lookups[lk], "pairs")
+ lookups[lk].pairs[glyph.name] = l.specification.variant
+
+ tab.subtable(lookups[_lk], "pairs")
+ lookups[_lk].pairs[l.specification.variant] = glyph.name
+
+ elseif l.type == "ligature" then
+
+ local comp, lig = str.explode(l.specification.components), l.specification.char
+ local lig = ""
+ for _, c in ipairs(comp) do
+ lig = lig .. c
+ end
+
+ tab.subtable(lookups, lk)
+ tab.subtable(lookups[lk], "ligs", {all_ligs = {}})
+ ligature(comp, lookups[lk].ligs, phantom_ligatures)
+ name_touni[lig .. ".lig"] = chr
+
+ end
+ end
+ end
+ end
+ end
+
+ if glyph.kerns then
+ for _, kern in pairs(glyph.kerns) do
+ local lks = type(kern.lookup) == "table" and kern.lookup or {kern.lookup}
+ for _, lk in ipairs(lks) do
+ tab.subtable(lookups[lk], "kerns")
+ tab.subtable(lookups[lk].kerns, glyph.name)
+ lookups[lk].kerns[glyph.name][kern.char] = kern.off
+ end
+ end
+ end
+
+ end
+
+ for lig in pairs(phantom_ligatures) do
+ if not name_touni[lig] then
+ max_char = max_char + 1
+ name_touni[lig] = max_char
+ characters[max_char] = {name = lig}
+ end
+ end
+
+ local lookup_table = {}
+ get_lookups(fontfile.gsub, lookup_table)
+ get_lookups(fontfile.gpos, lookup_table)
+ get_lookups(fontfile.lookups, lookup_table, true)
+
+ if fontfile.lookups then
+ for name, lk in pairs(fontfile.lookups) do
+ local tb, format = {}, lk.format
+ for _, rule in ipairs(lk.rules) do
+ local ttb = { lookups = rule.lookups }
+ for pos, seq in pairs(rule[format]) do
+ ttb[pos] = {}
+ for _, glyfs in ipairs(seq) do
+ glyfs = str.explode(glyfs)
+ glyfs = tab.tohash(glyfs)
+ tab.insert(ttb[pos], glyfs)
+ end
+ end
+ tab.insert(tb, ttb)
+ end
+ lookups[name] = tb
+ end
+ end
+
+ local loaded_font = {
+
+ direction = 0,
+ filename = filename,
+ format = extension,
+ fullname = fontfile.names[1].names.fullname,
+ name = fontfile.fontname,
+ psname = fontfile.fontname,
+ type = "real",
+ units_per_em = fontfile.units_per_em,
+
+ auto_expand = true,
+
+ cidinfo = fontfile.cidinfo,
+
+ -- Used only to adjust absoluteslant.
+ italicangle = -fontfile.italicangle,
+ name_to_unicode = name_touni,
+ max_char = max_char,
+ lookups = lookups,
+ lookup_table = lookup_table,
+ characters = characters
+ }
+
+ if write then
+ local f = io.open(path, "w")
+ f:write("return {")
+ tab.write(loaded_font, f, "\n")
+ f:write("}")
+ f:close()
+ end
+
+ return loaded_font
+
+end
+
+-- GETTING A FONT
+
+-- Finds a font file, and returns the original
+-- file and the Lua version.
+local name_luafile, get_features
+local function is_font (name, mods, size)
+
+ local lib, library_filename
+ for l, t in ipairs(library) do
+ if t[name] then
+ library_filename = t[name]
+ lib = t
+ break
+ end
+ end
+ if not library_filename then
+ library_filename = library.default[name]
+ lib = library.default
+ end
+
+ if library_filename then
+ if type(library_filename) == "string" then
+ library_filename = lib[library_filename]
+ end
+ tab.sort(mods)
+ local T = library_filename
+ for _, t in ipairs(mods) do
+ local found
+ for tag in pairs(T) do
+ if str.match(tag, "^" .. t) then
+ T = T[tag]
+ found = true
+ break
+ end
+ end
+ if not found then
+ T = nil
+ break
+ end
+ end
+
+ if T and T.__files then
+ local file, feats
+ local diff = 10000
+ for s, f in pairs(T.__files) do
+ if num.abs(s - size) < diff then
+ diff = num.abs(s - size)
+ file, feats = f[1], f[2]
+ end
+ end
+ file, _file = lfs.find_file(file, lfs.kpse(file)), file
+ if file then
+ file = str.gsub(file, "\\", "/")
+ else
+ return 1, _file
+ end
+ local features = {}
+ if feats then
+ get_features(feats, features)
+ end
+ local lua = name_luafile(file, features.font)
+ lua = lfs.isfile(lua) and lua
+ return file, lua, feats
+ end
+ end
+end
+
+-- Returns the full path to the Lua version of the font.
+-- "sub" is a font in ttc.
+function name_luafile (file, sub)
+ local lua
+ sub = sub and "_" .. sub or ""
+ sub = str.gsub(sub, " ", "_")
+ if str.match(file, "/") then
+ lua = str.match(file, ".*/(.-)%....$")
+ else
+ lua = str.match(file, "(.-)%....$")
+ end
+ lua = lua .. sub
+ return foundry_path .. "/" .. lua .. ".lua"
+end
+
+local function apply_size (font, size, letterspacing, parameters)
+ if (size < 0) then size = (- 655.36) * size end
+ local to_size = size / font.units_per_em
+ font.size = size
+ font.designsize = size
+ font.to_size = to_size
+ local italic = font.italicangle or 0
+ local space, stretch, shrink, extra
+ if parameters == "mono" then -- Creates a monospaced font with space equal to the
+ -- width of an "m" and no stretch or shrink.
+ space = font.characters[109].width * to_size
+ stretch, shrink, extra = 0, 0, 0
+ else
+ parameters = parameters and str.explode(parameters) or {}
+ space = (parameters[1] or 0.25) * size
+ stretch = (parameters[2] or 0.166666) * size
+ shrink = (parameters[3] or 0.111111) * size
+ extra = (parameters[4] or 0.111111) * size
+ end
+ font.parameters = {
+ slant = size * num.floor(num.tan(italic * num.pi/180)), -- \fontdimen 1
+ space = space, -- \fontdimen 2
+ space_stretch = stretch, -- \fontdimen 3
+ space_shrink = shrink, -- \fontdimen 4
+ x_height = size * 0.4, -- \fontdimen 5
+ quad = size, -- \fontdimen 6
+ extra_space = extra -- \fontdimen 7
+ }
+
+ letterspacing = letterspacing or 1
+ for c, t in pairs(font.characters) do
+ if t.width then
+ t.width, t.height, t.depth = t.width * to_size * letterspacing, t.height * to_size, t.depth * to_size
+ t.expansion_factor = 1000
+ if t.italic then
+ t.italic = t.italic * to_size
+ end
+ end
+ end
+
+ return font
+end
+
+
+local get_mods = lp.Ct((lp.space * lp.S"/" * lp.C(lp.alnum^1))^0)
+local get_feats = lp.Ct((lp.C((1 - lp.S",;")^1) * (lp.S",;" + -1))^1)
+function get_features (features, tb)
+ features = lp.match(get_feats, features) or {}
+ for _, f in ipairs(features) do
+ if str.match(f, "=") then
+ local key, val = str.match(f, "%s*(.-)%s*=%s*(.*)")
+ val = str.trim(val)
+ if val == "false" then
+ tb[key] = nil
+ else
+ tb[key] = val
+ end
+ else
+ f = str.trim(f)
+ neg, f = str.extract(f, "^%-")
+ if neg then
+ tb[f] = nil
+ else
+ _, f = str.extract(f, "^%+")
+ tb[f] = true
+ end
+ end
+ end
+end
+
+local lookup_functions = {}
+
+function lookup_functions.gsub_single (tb, f)
+ local name_touni = f.name_to_unicode
+ for a, b in pairs(tb.pairs) do
+ local _a, _b = name_touni[a], name_touni[b]
+ f.max_char = f.max_char + 1
+ f.characters[f.max_char] = f.characters[_a]
+ f.characters[_a] = f.characters[_b]
+ name_touni[a], name_touni[b] = f.max_char, _a
+ end
+ return f
+end
+
+function lookup_functions.gsub_ligature (tb, f)
+ local name_touni = f.name_to_unicode
+ tb.ligs.all_ligs = nil
+ for a, tb in pairs(tb.ligs) do
+ a = name_touni[a]
+ tab.subtable(f.characters[a], "ligatures")
+ for b, ttb in pairs(tb) do
+ b, c = name_touni[b], name_touni[ttb.char]
+ f.characters[a].ligatures[b] = {char = c, type = ttb.type}
+ end
+ end
+ return f
+end
+
+local function kern_pairs (tb, firsts, seconds, offset)
+ for _, c1 in ipairs(str.explode(firsts)) do
+ for __, c2 in ipairs(str.explode(seconds)) do
+ tab.subtable(tb, c1)
+ tb[c1][c2] = offset
+ end
+ end
+end
+
+local function kern_classes (firsts, seconds, offsets, max)
+ local kerns = {}
+ for f, F in pairs (firsts) do
+ for s, S in pairs (seconds) do
+ local off = offsets[(f-1) * max + s]
+ if off then
+ kern_pairs (kerns, F, S, off)
+ end
+ end
+ end
+ return kerns
+end
+
+local function apply_kerns (f, kerns, to_size)
+ local name_touni = f.name_to_unicode
+ to_size = to_size or 1
+ for c1, ttb in pairs(kerns) do
+ for c2, off in pairs(ttb) do
+ tab.subtable(f.characters[name_touni[c1]], "kerns")
+ f.characters[name_touni[c1]].kerns[name_touni[c2]] = off * to_size
+ end
+ end
+end
+
+function lookup_functions.gpos_pair (tb, f)
+ -- These are the big kern classes.
+ if tb.offsets then
+ local name_touni = f.name_to_unicode
+ for n, off in pairs(tb.offsets) do
+ tb.offsets[n] = off * f.to_size
+ end
+ local kerns = kern_classes(tb.firsts, tb.seconds, tb.offsets, tb.max)
+ apply_kerns(f, kerns)
+ end
+ -- These are the ones retrieved from individual glyphs.
+ if tb.kerns then
+ apply_kerns(f, tb.kerns, f.to_size)
+ end
+ return f
+end
+
+function lookup_functions.gsub_contextchain (tb, f)
+ local name_touni = f.name_to_unicode
+ local T = f.contextchain or {}
+ for _, llk in ipairs(tb) do
+ if fontfile.lookups then
+ local sub, Sub = fontfile.lookups[llk].rules[1].lookups
+ local chain = fontfile.lookups[llk].rules[1].coverage
+ local cur, current = str.explode(chain.current[1]), {}
+ local aft, after = chain.after and str.explode(chain.after[1]) or {}, {}
+ for _, c in ipairs(cur) do
+ c = name_touni[c]
+ if sub then
+ Sub = {}
+ for n, x in ipairs(sub) do
+ Sub[n] = name_touni[fontfile.glyphs[f.characters[c].index].lookups[x .. "_s"][1].specification.variant]
+ end
+ end
+ local t = { lookup = Sub}
+ tab.subtable(T, C)
+ for __, a in ipairs(aft) do
+ tab.subtable(t, "after")
+ t.after[name_touni[a]] = true
+ end
+ tab.insert(T[c], t)
+ end
+ end
+ end
+ f.contextchain = T
+ return f
+end
+
+local function _isactive (tb, ft, sc, lg)
+ for t in pairs(tb.tags) do
+ if ft[t] then
+ if t[sc] then
+ for _, lang in pairs(tb[sc]) do
+ if lang == lg then
+ return true
+ end
+ end
+ else
+ return true
+ end
+ end
+ end
+end
+
+
+local lookup_types = {
+ "gsub_single",
+ "gsub_ligature",
+ "gpos_pair"
+ }
+
+local function activate_lookups (font, features, script, lang)
+ for _, type in ipairs(lookup_types) do
+ if font.lookup_table and font.lookup_table[type] then
+ for l, tb in pairs(font.lookup_table[type]) do
+ if _isactive(tb, features, script, lang) then
+ for _, lk in ipairs(tb) do
+ local lt = font.lookups[lk]
+ if lt then
+ font = lookup_functions[type](lt, font)
+ end
+ end
+ end
+ end
+ end
+ end
+ return font
+end
+
+local function load_font (name, size, id, done)
+ local loaded_font = lfs.find_file(name, "tfm")
+ if loaded_font then
+ loaded_font = fl.read_tfm(loaded_font, size)
+ else
+ local original = str.trim(str.match(name, "[^:]*"))
+ local family, mods, feats
+ family, name = str.extract(name, "([^/:]*)")
+ family = str.trim(family)
+ mods, name = str.extract(name, "[^:]*")
+ mods = lp.match(get_mods, mods) or {}
+ feats = str.extract(name, ":(.*)") or ""
+ local features = tab.copy(settings.features)
+ get_features(feats, features)
+
+ local at_size
+ if features.size then
+ at_size = features.size
+ else
+ at_size = size
+ at_size = at_size > 0 and at_size or at_size * - 655.36
+ at_size = at_size / 65536
+ end
+ local source, lua, add_feats = is_font(family, mods, at_size)
+ if add_feats then get_features(add_feats, features) end
+
+ if type(source) == "string" then
+ local cache = features.cache or "yes"
+ if lua then
+ if cache == "no" or cache == "rewrite" then
+ loaded_font = create_font(source, lfs.type(source), lua, features.font, cache == "rewrite")
+ else
+ loaded_font = dofile(lua)
+ end
+ else
+ lua = name_luafile(source, features.font)
+ loaded_font = create_font(source, lfs.type(source), lua, features.font, cache ~= "no")
+ end
+ else
+ if not done then
+ if type(source) == "number" then
+ wri.error("The library says `%s' matches `%s', but I can't find that file anywhere. Clean up your library!", original, lua)
+ else
+ recheck_fonts()
+ return load_font(original, size, id, true)
+ end
+ else
+ wri.error("I can't find `%s'. I return a default font to avoid further errors.", original)
+ end
+ end
+
+ if loaded_font then
+
+ local expansion = features.expansion and str.explode(features.expansion) or {}
+ loaded_font.stretch = expansion[1] or 0
+ loaded_font.shrink = expansion[2] or 0
+ loaded_font.step = expansion[3] or 0
+
+ local extend = features.extend or 1
+ loaded_font.extend = extend * 1000
+ local slant
+ if features.absoluteslant then
+ local italic = loaded_font.italicangle or 0
+ slant = features.absoluteslant - italic
+ else
+ slant = features.slant or 0
+ end
+ loaded_font.slant = num.tan(num.rad(slant)) * 1000
+
+ loaded_font = apply_size(loaded_font, size, features.letterspacing, features.space)
+ loaded_font = activate_lookups(loaded_font, features, features.script, features.lang)
+
+ loaded_font.name = loaded_font.name .. id
+ loaded_font.fullname = loaded_font.fullname .. id
+ local embedding = features.embedding or "subset"
+ if embedding ~= "no" and embedding ~= "subset" and embedding ~= "full" then
+ wri.error("Invalid value `%s' for the `embedding' feature. Value should be `no', `subset' or `full'.", embedding)
+ embedding = "subset"
+ end
+ loaded_font.embedding = embedding
+ else
+ loaded_font = fl.read_tfm(lfs.find_file("cmr10", "tfm"), size)
+ end
+ end
+ return loaded_font
+end
+
+callback.register("define_font", load_font)
diff --git a/Master/texmf-dist/tex/luatex/blopentype/blot-fonts.tex b/Master/texmf-dist/tex/luatex/blopentype/blot-fonts.tex
new file mode 100644
index 00000000000..adf4622fe58
--- /dev/null
+++ b/Master/texmf-dist/tex/luatex/blopentype/blot-fonts.tex
@@ -0,0 +1,308 @@
+\inputltcfile blot-fonts.lua
+\pdfadjustspacing=2
+
+\def\currentfont{}
+\def\currentsize{normal}
+\def\currentstyle{rm}
+\def\currentweight{rg}
+\def\currentcase{lc}
+\def\makecurrentfont{%
+ \csname\currentfont @\currentsize @\currentstyle @\currentweight @\currentcase\endcsname
+ }
+\def\normalsize{%
+ \def\currentsize{normal}%
+ \makecurrentfont
+ }
+\def\small{%
+ \def\currentsize{small}%
+ \makecurrentfont
+ }
+\def\verysmall{%
+ \def\currentsize{verysmall}%
+ \makecurrentfont
+ }
+\def\smaller{%
+ \passexpanded\ifstring\currentsize{normal}%
+ {\def\currentsize{small}}
+ {\def\currentsize{verysmall}}
+ \makecurrentfont
+ }
+\def\big{%
+ \def\currentsize{big}%
+ \makecurrentfont
+ }
+\def\verybig{%
+ \def\currentsize{verybig}%
+ \makecurrentfont
+ }
+\def\bigger{%
+ \passexpanded\ifstring\currentsize{normal}%
+ {\def\currentsize{big}}
+ {\def\currentsize{verybig}}
+ \makecurrentfont
+ }
+\def\it{%
+ \def\currentstyle{it}%
+ \makecurrentfont
+ }
+\def\rm{%
+ \def\currentstyle{rm}%
+ \makecurrentfont
+ }
+\def\rmstring{rm}
+\def\emph{%
+ \ifx\currentstyle\rmstring
+ \expandafter\ital
+ \else
+ \expandafter\rom
+ \fi
+ }
+\def\bf{%
+ \def\currentweight{bf}%
+ \makecurrentfont
+ }
+%\def\normalweight{%
+\def\rg{%
+ \def\currentweight{rg}%
+ \makecurrentfont
+ }
+\def\sc{%
+ \def\currentcase{sc}%
+ \makecurrentfont
+ }
+%\def\normalcase{%
+\def\lc{%
+ \def\currentcase{lc}%
+ \makecurrentfont
+ }
+\freedef\ital{{\it#1}}
+\freedef\bold{{\bf#1}}
+\freedef\scap{{\sc#1}}
+\freedef\rom{{\rm#1}}
+
+
+\restrictparameter font :
+ command % The command to be used to switch to that font.
+ name % Actually the part common to all files' names for that font.
+ size % Well, the size.
+ small % Smaller size.
+ verysmall % Smaller yet.
+ big % Bigger.
+ verybig % Insanely big.
+ bold % Modifier for bold font.
+ italic % Modifier for italic font.
+ math % If set to "true", then used for math font.
+ % small, verysmall and italic should be specified
+ % (which shows I don't use it very often).
+ features % Font features (e.g. +onum, etc.).
+ slant % A number representing an angle, loading an italic
+ % font if no real italic is a available.
+ slantsc % The same, for small caps.
+
+% The font parameter should disappear some day, it's clumsy.
+\def\setfont#1:{%
+ \setparameter font : command = #1%
+ }
+
+\setparameter metafont : % No relation :)
+ size = 10pt
+ bold = Bold
+ italic = Italic
+ features = "+onum; +liga; +trep; +tlig; expansion=30 20 5;"
+
+\setparameter font :
+ meta = metafont
+
+\newdimen\ptx@fontsize
+\luacode
+ptx_fonts = {}
+function ptx_getfont(Font, ...)
+ if ptx_fonts[Font] then
+ local tempfont = ptx_fonts[Font]
+ for _, style in ipairs(arg) do
+ if ptx_getstyle[style] then
+ tempfont = tempfont:gsub("(%w+)@(%w+)@(%w+)@(%w+)@(%w+)",ptx_getstyle[style])
+ else
+ tempfont = nil
+ texio.write_nl("! PiTeX error: Unknown font style `style'.")
+ return Font
+ end
+ end
+ return font.id(tempfont)
+ else
+ texio.write_nl("! PiTeX error: " .. Font .. " is not a PiTeX font. It can't be changed.")
+ end
+end
+ptx_getstyle = {
+ normal = "%1@normal@%3@%4@%5",
+ small = "%1@small@%3@%4@%5",
+ verysmall = "%1@verysmall@%3@%4@%5",
+ big = "%1@big@%3@%4@%5",
+ verybig = "%1@verybig@%3@%4@%5",
+ rm = "%1@%2@rm@%4@%5",
+ it = "%1@%2@it@%4@%5",
+ rg = "%1@%2@%3@rg@%5",
+ bf = "%1@%2@%3@bf@%5",
+ lc = "%1@%2@%3@%4@lc",
+ sc = "%1@%2@%3@%4@sc"
+ }
+\luacode/
+
+\newstring{+} \newstring{-}
+\def\ptx@dofont#1#2#3#4#5{%
+ \edef\ptx@temp{\passvaluenobraces\commandtoname font : command }%
+ \ifexpression{ -\ifstring{#4}{normal} & { \ifprefix-{#5} | \ifprefix+{#5} } }
+ {\def\ptx@dofont_size{\dimexpr\usevalue font : size + #5\relax}}
+ {\def\ptx@dofont_size{#5}}%
+ \ptx@dofont_load{\ptx@temp @#4@#1@#2@lc}{#3}{}{\ptx@dofont_size}%
+ \ifstring{#1}{rm}
+ {\ptx@dofont_load{\ptx@temp @#4@#1@#2@sc}{#3}{+smcp;}{\ptx@dofont_size}%
+ \ifattribute font : slant % Loading a slanted version of the font if no italic was given
+ {\ptx@dofont_load{\ptx@temp @#4@it@#2@lc}{#3}{slant=\usevalue font: slant ;}{\ptx@dofont_size}
+ \ifattribute font : slantsc {}
+ {\ptx@dofont_load{\ptx@temp @#4@it@#2@sc}{#3}{+smcp;slant=\usevalue font: slant ;}{\ptx@dofont_size}}}{}%
+ \ifattribute font : slantsc
+ {\ptx@dofont_load{\ptx@temp @#4@it@#2@sc}{#3}{+smcp;slant=\usevalue font: slantsc ;}{\ptx@dofont_size}}{}}%
+ {\ifattribute font : slantsc {}{\ptx@dofont_load{\ptx@temp @#4@it@#2@sc}{#3}{+smcp;}{\ptx@dofont_size}}}
+ }
+
+\def\ptx@dofont_load#1#2#3#4{%
+ \passcs\font{#1}="\usevalue font : name #2:#3\usevalue font : features " at #4\relax
+ \ptx@lua{ptx_fonts[font.id("#1")] = "#1"}%
+ }
+
+
+
+\def\dofont#1#2#3{%
+ \passvalue{\ptx@dofont{#1}{#2}{#3}{normal}} font : size
+ \passvalue{\ptx@dofont{#1}{#2}{#3}{small}} font : small
+ \passvalue{\ptx@dofont{#1}{#2}{#3}{verysmall}} font : verysmall
+ \passvalue{\ptx@dofont{#1}{#2}{#3}{big}} font : big
+ \passvalue{\ptx@dofont{#1}{#2}{#3}{verybig}} font : verybig
+%
+ \ifvalue font : math = true
+ {\ifstring{#1#2}{rmrg}
+ {\passcs{\textfont0=}{\passvalue\commandtoname font : command @normal@#1@#2@lc}%
+ \passcs{\scriptfont0=}{\passvalue\commandtoname font : command @small@#1@#2@lc}%
+ \passcs{\scriptscriptfont0=}{\passvalue\commandtoname font : command @verysmall@#1@#2@lc}
+ \passcs{\textfont2=}{\passvalue\commandtoname font : command @normal@#1@#2@lc}%
+ \passcs{\scriptfont2=}{\passvalue\commandtoname font : command @small@#1@#2@lc}%
+ \passcs{\scriptscriptfont2=}{\passvalue\commandtoname font : command @verysmall@#1@#2@lc}}
+ {\iffstring{#1#2}{itrg}
+ {\passcs{\textfont1=}{\passvalue\commandtoname font : command @normal@#1@#2@lc}%
+ \passcs{\scriptfont1=}{\passvalue\commandtoname font : command @verysmall@#1@#2@lc}%
+ \passcs{\scriptscriptfont1=}{\passvalue\commandtoname font : command @verysmall@#1@#2@lc}}}%
+ }{}%
+ }
+
+\gates new \FontLoader {FontLoader}
+\FontLoader list {fontloader}
+ [fl_command]
+ {%
+ \passvaluenobraces\edef font : command {%
+ \def\noexpand\currentfont{\passvalue\commandtoname font : command }%
+ \noexpand\makecurrentfont}}
+ (fl_load)
+ . ( fl_roman )
+ . . [ fl_doromanregular ]
+ {\dofont{rm}{rg}{}}
+ . . [ fl_doromanbold ] ? {conditional = -\ifvalue font : bold = none }
+ {\dofont{rm}{bf}{/\usevalue font : bold }}
+ . ( fl_italic ) ? {conditional = -\ifvalue font : italic = none }
+ . . [ fl_doitalicregular ]
+ {\dofont{it}{rg}{/\usevalue font : italic }}
+ . . [ fl_doitalicbold ] ? {conditional = -\ifvalue font : bold = none }
+ {\dofont{it}{bf}{/\usevalue font : italic /\usevalue font : bold }}
+ [fl_use] ? {conditional = \ifvalue font : command = {\mainfont} }
+ {\mainfont}
+ (fl_post)
+ . [fl_delete]
+ {\deleteparameter font:}
+ . [fl_meta]
+ {\setattribute font : meta = metafont }
+
+\defactiveparameter font {%
+ \FontLoader execute {fontloader}%
+ }
+
+
+
+
+
+
+
+\long\def\color#1#2{%
+ \pdfcolorstack0 push {#1 rg #1 RG}%
+ #2%
+ \pdfcolorstack0 pop%
+ }
+
+\newattribute\ptx@underline_attribute
+\luacode
+local do_underline = function (head,order,ratio,sign)
+ local item, leader = head, false
+ while item do
+ leader = false
+ if node.has_attribute(item,\attributenumber\ptx@underline_attribute)
+ and not (item.id == 10 and (item.subtype == 8 or item.subtype == 9)) then
+ local item_line = node.new(2)
+ item_line.depth = tex.sp("1.4pt")
+ item_line.height = tex.sp("-1pt")
+ if node.has_field(item,"spec") then
+ item_line.width = item.spec.width
+ if order == 0 then
+ if sign == 1 then
+ item_line.width = item_line.width + ratio * item.spec.stretch
+ else
+ item_line.width = item_line.width - ratio * item.spec.shrink
+ end
+ else
+ if item.spec.stretch_order > 0 or item.spec.shrink_order > 0 then
+ lualog(item.spec.stretch_order)
+ item_line.width = 1
+ item.subtype = 100
+ item.leader = item_line
+ leader = true
+ end
+ end
+ elseif node.has_field(item,"width") then
+ item_line.width = item.width
+ elseif node.has_field(item,"kern") then
+ item_line.width = item.kern
+ end
+ if leader then
+ item = item.next
+ else
+ local item_kern = node.new(11)
+ item_kern.kern = -item_line.width
+ node.insert_after(head,item,item_line)
+ node.insert_after(head,item,item_kern)
+ item = item_line.next
+ end
+ else
+ item = item.next
+ end
+ end
+end
+pitex.misc.underline = function (head)
+ for line in node.traverse_id(0,head) do
+ do_underline(line.list,line.glue_order,line.glue_set,line.glue_sign)
+ end
+ return head
+end
+pitex.callback.register("post_linebreak_filter", "pitex.misc:underline")
+pitex.callback.close("pitex.misc:underline", "post_linebreak_filter")
+\luacode/
+
+\freedef\underline{%
+ \ptx@lua{%
+ pitex.callback.ajar("pitex:underline", "post_linebreak_filter")
+ }
+% local plf = callback.list_functions("post_linebreak_filter")
+% if not plf or not plf["underline"] then
+% callback.add_function(underline, "underline", "post_linebreak_filter")
+% end}%
+ \quitvmode % Otherwise it might also underline the indentation box.
+ \ptx@underline_attribute=0\relax
+ #1\unsetattribute\ptx@underline_attribute
+ }
diff --git a/Master/texmf-dist/tex/luatex/blopentype/blot-lua.tex b/Master/texmf-dist/tex/luatex/blopentype/blot-lua.tex
new file mode 100644
index 00000000000..f00e58c9d41
--- /dev/null
+++ b/Master/texmf-dist/tex/luatex/blopentype/blot-lua.tex
@@ -0,0 +1,26 @@
+\def\inputltcfile#1 {\directlua{dofile(kpse.find_file("#1"))}}
+
+\inputltcfile blot-base.lua
+
+\def\ptx@lua{\directlua name {Internal PiTeX chunk}}
+
+\newcatcodetable\luacatcodes{\#\%\^^M\~=12}
+\newtoks\ptx@luacode_list
+\newif\ifptx@luacode_store
+% Mimicks a block (\newblock isn't defined yet
+% and anyway it wouldn't be very useful).
+\def\luacode{%
+ \begingroup
+ \catcodetable\luacatcodes
+ \ifnext[
+ {\ptx@luacode_store}
+ {\ptx@luacode_store[]}}
+\bgroup
+\setcatcodes{\^^M=12}%
+\long\gdef\ptx@luacode_store[#1]^^M#2\luacode/{% So line count is right.
+ \endgroup%
+ \ifemptystring{#1}%
+ {\ptx@lua{#2}}%
+ {\def#1{#2}}%
+ }%
+\egroup
diff --git a/Master/texmf-dist/tex/luatex/blopentype/blot-sets.lua b/Master/texmf-dist/tex/luatex/blopentype/blot-sets.lua
new file mode 100644
index 00000000000..5c24caa9378
--- /dev/null
+++ b/Master/texmf-dist/tex/luatex/blopentype/blot-sets.lua
@@ -0,0 +1,19 @@
+return {
+
+ features = {
+ kern = true,
+ liga = true,
+ trep = true,
+ tlig = true,
+ script = "latn",
+ lang = "dflt",
+ --stretch = 30,
+ --shrink = 20,
+ --step = 10,
+ },
+
+ normal = {
+ "Normal", "Regular", "Book",
+ },
+
+}
diff --git a/Master/texmf-dist/tex/luatex/blopentype/blot.tex b/Master/texmf-dist/tex/luatex/blopentype/blot.tex
new file mode 100644
index 00000000000..44a89e553ef
--- /dev/null
+++ b/Master/texmf-dist/tex/luatex/blopentype/blot.tex
@@ -0,0 +1,254 @@
+% blopentype: a basic luatex open type loader
+%
+% some (most) pitex macros still in place.- 30.12.2022
+%
+% User interface
+%
+\message{This is blopentype, v0.0.0 December 2022}
+
+\input luatex85.sty % deprecated 30.12.2022
+\input yax % which itself \input's texapi
+\input gates
+
+\setcatcodes{\@\_=11}
+\suppressoutererror=1
+
+% MESSAGES
+\def\ptx@error{\senderror{PiTeX}}
+\def\ptx@log#1{%
+ \immediate\write17{^^J#1^^J}%
+ }
+\def\ptx@warn#1{%
+ \ptx@log{PiTeX warning: #1}%
+ }
+
+% ATTRIBUTES
+\newcount\ptx@attribute_count
+\ptx@attribute_count=100 % The first 100 attributes are scratch.
+\def\newattribute#1{%
+ \advance\ptx@attribute_count1
+ \attributedef#1=\ptx@attribute_count
+ \xdefcs{ptx@attribute:\commandtoname#1}{\the\ptx@attribute_count}%
+ }
+\def\unsetattribute#1{#1=-"7FFFFFFF\relax}
+\def\attributenumber#1{\usecs{ptx@attribute:\commandtoname#1}}
+
+% FREEDEF
+\def\freedef#1{%
+ \def#1{%
+ \ifnextnospace"
+ {\ptx@freedef_quote#1}
+ {\ifnextnospace/
+ {\ptx@freedef_slash#1}
+ {\usecs{ptx@freedef_user:\commandtoname#1}}}%
+ }%
+ \defcs{ptx@freedef_user:\commandtoname#1}##1%
+ }
+\def\ptx@freedef_quote#1"#2"{%
+ \usecs{ptx@freedef_user:\commandtoname#1}{#2}%
+ }
+\def\ptx@freedef_slash#1/#2/{%
+ \usecs{ptx@freedef_user:\commandtoname#1}{#2}%
+ }
+
+
+\newbox\ptx@box_temp
+
+\newif\ifmaintext
+\maintexttrue
+
+% CATCODE TABLES
+\newcount\ptx@catcodetable_count
+\ptx@catcodetable_count=100 % First 100 are scratch.
+\def\newcatcodetable#1#2{%
+ \global\advance\ptx@catcodetable_count1
+ \chardef#1=\ptx@catcodetable_count
+ \begingroup
+ \setcatcodes{#2}%
+ \savecatcodetable#1%
+ \endgroup
+ }
+
+\newcatcodetable\texcatcodes{\@\_=12}
+
+\def\inputltmfile#1 {\input #1.tex }
+
+\inputltmfile blot-lua
+\inputltmfile blot-files
+\inputltmfile blot-fonts
+\restorecatcodes
+\endinput
+%
+% the remaining macros are leftovers from PiTeX, awaiting removal.- 30.12.2022
+%
+%
+\inputltmfile sections
+\inputltmfile blocks
+\inputltmfile references
+\inputltmfile verbatim
+\inputltmfile inserts
+\inputltmfile output
+\input navigator.tex
+
+% AUXILIARY FILE
+\iffile{\jobname.aux}{%
+ \ptx@lua{%
+ remove_conversion()
+ tex.print("\luaescapestring{\noexpand\input\noexpand\jobname.aux}")
+ }%
+ \directlua{restore_conversion()}}
+\immediate\openout\ptx@auxfile=\jobname.aux
+
+% PDF SETTINGS
+\restrictparameter document : author title pdftitle date pdfdate subject keywords mode layout version\par
+\restrictattribute document : mode outlines bookmarks thumbs thumbnails attachments files oc\par
+\restrictattribute document : layout onepage onecolumn twopage twocolumn twopage* twocolumn*\par
+
+\suppressoutererror=1
+\let\ptx@bye\bye
+\def\bye{%
+ \passvalueand{\setattribute navigator : title = } document : pdftitle { }{}
+ % The "date" attribute in the "document" parameter isn't supposed to hold a
+ % PDF-date, as navigator expects.
+ \deleteattribute document : date
+ \passvalueand{\setattribute navigator : date = } document : pdfdate { }{}
+ \finishpdffile\ptx@bye
+ }
+
+\setattribute navigator : meta = document
+
+% Turns a dimen into PostScript points, without the unit (as wanted by PDF).
+\def\pdfpoint#1{%
+ \directlua{%
+ local d = "\the\dimexpr#1"
+ d = string.gsub(d, "pt", "")
+ tex.print(tostring(d * (72/72.27)))
+ }}
+
+
+
+
+% TEX SETTINGS
+\long\def\ptx@tex_set#1#2#3{%
+ \ifcs{#2}
+ {\usecs{#2}=#3\relax}
+ {\ptx@error{No TeX parameter `#2'}}
+ }
+
+\defactiveparameter tex {%
+ \parameterloop #1 : \ptx@tex_set
+ }
+
+\frenchspacing
+\maxdepth=\maxdimen
+
+\def\antigobblespace{%
+ \ifcatnext a{ }{\iffnext({ }}%
+ }
+
+\def\strut#1#2{%
+ \vrule height#1 depth#2 width0pt
+ }
+
+\newdimen\extraboxspace
+\newdimen\ptx@extraboxspace_top
+\newdimen\ptx@extraboxspace_right
+\newdimen\ptx@extraboxspace_bottom
+\newdimen\ptx@extraboxspace_left
+
+\newfornoempty\ptx@colorbox_loop{1}#2,{%
+ \ifcase#1
+ \ptx@extraboxspace_top =#2
+ \ptx@extraboxspace_right =#2
+ \ptx@extraboxspace_bottom =#2
+ \ptx@extraboxspace_left =#2
+ \or
+ \ptx@extraboxspace_right =#2
+ \ptx@extraboxspace_left =#2
+ \or
+ \ptx@extraboxspace_bottom =#2
+ \or
+ \ptx@extraboxspace_left =#2
+ \fi
+ \passarguments{\numexpr(#1+1)}%
+ }
+\def\colorbox{%
+ \ifnextnospace[\ptx@colorbox_setborders
+ {\ptx@extraboxspace_top =\extraboxspace
+ \ptx@extraboxspace_right =\extraboxspace
+ \ptx@extraboxspace_bottom =\extraboxspace
+ \ptx@extraboxspace_left =\extraboxspace
+ \ptx@colorbox_do}%
+ }
+\def\ptx@colorbox_setborders[#1]{%
+ \ptx@colorbox_loop{0}{#1,}%
+ \ptx@colorbox_do
+ }
+{\setcatcodes{pt=12}
+\gdef\noPT#1pt{#1 }}
+\def\ptx@colorbox_do#1#2{%
+ \bgroup
+ \setbox\ptx@box_temp=\hbox{#2}%
+ \hbox{%
+ \pdfliteral{
+ q #1 rg #1 RG
+ -\expandafter\noPT\the\ptx@extraboxspace_left
+ \expandafter\noPT\the\dimexpr(\ht\ptx@box_temp+\ptx@extraboxspace_top)\relax
+ \expandafter\noPT\the\dimexpr(\wd\ptx@box_temp+\ptx@extraboxspace_left+\ptx@extraboxspace_right)\relax
+ -\expandafter\noPT\the\dimexpr(\ht\ptx@box_temp+\ptx@extraboxspace_top+\dp\ptx@box_temp+\ptx@extraboxspace_bottom)\relax
+ re f Q}%
+ #2}%
+ \egroup
+ }
+
+\def\og{\char"00AB~} \def\fg{~\char"00BB\antigobblespace}
+
+\def\trace{\tracingcommands3 \tracingmacros2 }
+\def\untrace{\tracingcommands0 \tracingmacros0 }
+
+\restorecatcodes
+
+\endinput
+%
+% These are leftovers from Isambert's code: they'll be removed as
+% documentation improved.- 30.12.2022
+%
+% This is piTeX, a set of macros I (Paul Isambert) use to
+% typeset documentations for my packages (that's why it is
+% archived on CTAN).
+%
+% Perhaps in the future, when this achieves some kind of
+% format-like completude, it'll be publicly announced. In the
+% meanwhile, a documentation exists (pitex-doc.pdf, also readable
+% in a text editor as pitex-doc.txt).
+%
+%
+% You can of course use those macros, but you are on your
+% own, and the files will probably be modified without announcement.
+% The file is supposed to be \input on plain TeX with LuaTeX, at least v.0.6.
+%
+%
+% The files needed are:
+%
+% texapi.tex (an independent package for programming)
+% yax.tex (an independent package for key=value interface)
+% gates.tex and gates.lua (an independant package for overall architecture)
+% navigator.tex (an independant package for PDF features)
+% lua.ptx and base.ptxlua (Lua side)
+% files.ptx (file management)
+% fonts.ptx, fonts.ptxlua and foundry-settings.lua
+% (fonts, should be independant some day; actually
+% fonts.ptxlua can be used independantly, but there is
+% no doc)
+% sections.ptx (sectionning commands)
+% blocks.ptx (text blocks)
+% references.ptx (labels and references)
+% verbatim.ptx (typesetting verbatim)
+% inserts.ptx (footnotes and figures, a mess)
+% output.ptx (output routine)
+%
+% The file i-pitex.lua is needed only to typeset the documentation with the
+% Interpreter package.
+%
+%
+% Date: November 2011.