summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-features.lua
diff options
context:
space:
mode:
Diffstat (limited to 'Master/texmf-dist/tex/luatex/luaotfload/luaotfload-features.lua')
-rw-r--r--Master/texmf-dist/tex/luatex/luaotfload/luaotfload-features.lua684
1 files changed, 28 insertions, 656 deletions
diff --git a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-features.lua b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-features.lua
index b6e889ec033..a57aac3ea6f 100644
--- a/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-features.lua
+++ b/Master/texmf-dist/tex/luatex/luaotfload/luaotfload-features.lua
@@ -1,5 +1,5 @@
if not modules then modules = { } end modules ["features"] = {
- version = "2.7",
+ version = "2.8",
comment = "companion to luaotfload-main.lua",
author = "Hans Hagen, Khaled Hosny, Elie Roux, Philipp Gesang",
copyright = "PRAGMA ADE / ConTeXt Development Team",
@@ -9,7 +9,6 @@ if not modules then modules = { } end modules ["features"] = {
local type = type
local next = next
local tonumber = tonumber
-local tostring = tostring
local lpeg = require "lpeg"
local lpegmatch = lpeg.match
@@ -20,16 +19,14 @@ local C = lpeg.C
local table = table
local tabletohash = table.tohash
local tablesort = table.sort
-local setmetatableindex = table.setmetatableindex
-local insert = table.insert
----[[ begin included font-ltx.lua ]]
--- this appears to be based in part on luatex-fonts-def.lua
local fonts = fonts
local definers = fonts.definers
local handlers = fonts.handlers
local fontidentifiers = fonts.hashes and fonts.hashes.identifiers
+local otf = handlers.otf
local config = config or { luaotfload = { run = { } } }
@@ -37,35 +34,25 @@ local as_script = true
local normalize = function () end
if config.luaotfload.run.live ~= false then
- normalize = handlers.otf.features.normalize
+ normalize = otf.features.normalize
as_script = false
end
---HH A bit of tuning for definitions.
-
-if fonts.constructors then
- fonts.constructors.namemode = "specification" -- somehow latex needs this (changed name!) => will change into an overload
-end
-
---[[HH--
+--[[HH (font-xtx) --
tricky: we sort of bypass the parser and directly feed all into
the sub parser
--HH]]--
-function fonts.definers.getspecification(str)
+function definers.getspecification(str)
return "", str, "", ":", str
end
local log = luaotfload.log
local report = log.report
-local stringfind = string.find
-local stringlower = string.lower
local stringgsub = string.gsub
-local stringsub = string.sub
local stringformat = string.format
local stringis_empty = string.is_empty
-local mathceil = math.ceil
local cmp_by_idx = function (a, b) return a.idx < b.idx end
@@ -1256,15 +1243,18 @@ local handle_request = function (specification)
specification.features = features
end
features.normal = normalize (request.features)
- local subfont = tonumber (specification.sub)
+ local subfont = tonumber (request.sub)
if subfont and subfont >= 0 then
specification.sub = subfont + 1
+ else
+ specification.sub = false
end
return specification
end
fonts.names.handle_request = handle_request
+
if as_script == true then --- skip the remainder of the file
report ("log", 5, "features",
"Exiting early from luaotfload-features.lua.")
@@ -1275,543 +1265,9 @@ else
registersplit ("", handle_request, "xetex path style") -- catches \font\text=[names]
end
----[[ end included font-ltx.lua ]]
-
-- We assume that the other otf stuff is loaded already; though there’s
-- another check below during the initialization phase.
----[[ begin snippet from font-otc.lua ]]
-local trace_loading = false trackers.register("otf.loading", function(v) trace_loading = v end)
-local report_otf = logs.reporter("fonts","otf loading")
-
---[[HH--
-
- In the userdata interface we can not longer tweak the loaded font as
- conveniently as before. For instance, instead of pushing extra data in
- in the table using the original structure, we now have to operate on
- the mkiv representation. And as the fontloader interface is modelled
- after fontforge we cannot change that one too much either.
-
---HH]]--
-
---- start locals for addfeature()
-
-local utf8 = unicode.utf8
-local utfbyte = utf8.byte
-local utflen = utf8.len
-
-local otf = handlers and handlers.otf --- filled in later during initialization
-
-local normalized = {
- substitution = "substitution",
- single = "substitution",
- ligature = "ligature",
- alternate = "alternate",
- multiple = "multiple",
- kern = "kern",
- pair = "pair",
- chainsubstitution = "chainsubstitution",
- chainposition = "chainposition",
-}
-
-local types = {
- substitution = "gsub_single",
- ligature = "gsub_ligature",
- alternate = "gsub_alternate",
- multiple = "gsub_multiple",
- kern = "gpos_pair",
- pair = "gpos_pair",
- chainsubstitution = "gsub_contextchain",
- chainposition = "gpos_contextchain",
-}
-
-setmetatableindex(types, function(t,k) t[k] = k return k end) -- "key"
-
---- stop locals for addfeature()
-
-local everywhere = { ["*"] = { ["*"] = true } } -- or: { ["*"] = { "*" } }
-local noflags = { false, false, false, false }
-
-local tohash = table.tohash
-
-local function current_addfeature(data,feature,specifications)
- local descriptions = data.descriptions
- local resources = data.resources
- local features = resources.features
- local sequences = resources.sequences
- if not features or not sequences then
- return
- end
- -- feature has to be unique but the name entry wins eventually
-
- local fontfeatures = resources.features or everywhere
- local unicodes = resources.unicodes
- local splitter = lpeg.splitter(" ",unicodes)
- local done = 0
- local skip = 0
- local aglunicodes = false
-
- if not specifications[1] then
- -- so we accept a one entry specification
- specifications = { specifications }
- end
-
- local function tounicode(code)
- if not code then
- return
- end
- if type(code) == "number" then
- return code
- end
- local u = unicodes[code]
- if u then
- return u
- end
- if utflen(code) == 1 then
- u = utfbyte(code)
- if u then
- return u
- end
- end
- if not aglunicodes then
- aglunicodes = fonts.encodings.agl.unicodes -- delayed
- end
- return aglunicodes[code]
- end
-
- local coverup = otf.coverup
- local coveractions = coverup.actions
- local stepkey = coverup.stepkey
- local register = coverup.register
-
- local function prepare_substitution(list,featuretype)
- local coverage = { }
- local cover = coveractions[featuretype]
- for code, replacement in next, list do
- local unicode = tounicode(code)
- local description = descriptions[unicode]
- if description then
- if type(replacement) == "table" then
- replacement = replacement[1]
- end
- replacement = tounicode(replacement)
- if replacement and descriptions[replacement] then
- cover(coverage,unicode,replacement)
- done = done + 1
- else
- skip = skip + 1
- end
- else
- skip = skip + 1
- end
- end
- return coverage
- end
-
- local function prepare_alternate(list,featuretype)
- local coverage = { }
- local cover = coveractions[featuretype]
- for code, replacement in next, list do
- local unicode = tounicode(code)
- local description = descriptions[unicode]
- if not description then
- skip = skip + 1
- elseif type(replacement) == "table" then
- local r = { }
- for i=1,#replacement do
- local u = tounicode(replacement[i])
- r[i] = descriptions[u] and u or unicode
- end
- cover(coverage,unicode,r)
- done = done + 1
- else
- local u = tounicode(replacement)
- if u then
- cover(coverage,unicode,{ u })
- done = done + 1
- else
- skip = skip + 1
- end
- end
- end
- return coverage
- end
-
- local function prepare_multiple(list,featuretype)
- local coverage = { }
- local cover = coveractions[featuretype]
- for code, replacement in next, list do
- local unicode = tounicode(code)
- local description = descriptions[unicode]
- if not description then
- skip = skip + 1
- elseif type(replacement) == "table" then
- local r, n = { }, 0
- for i=1,#replacement do
- local u = tounicode(replacement[i])
- if descriptions[u] then
- n = n + 1
- r[n] = u
- end
- end
- if n > 0 then
- cover(coverage,unicode,r)
- done = done + 1
- else
- skip = skip + 1
- end
- else
- local u = tounicode(replacement)
- if u then
- cover(coverage,unicode,{ u })
- done = done + 1
- else
- skip = skip + 1
- end
- end
- end
- return coverage
- end
-
- local function prepare_ligature(list,featuretype)
- local coverage = { }
- local cover = coveractions[featuretype]
- for code, ligature in next, list do
- local unicode = tounicode(code)
- local description = descriptions[unicode]
- if description then
- if type(ligature) == "string" then
- ligature = { lpegmatch(splitter,ligature) }
- end
- local present = true
- for i=1,#ligature do
- local l = ligature[i]
- local u = tounicode(l)
- if descriptions[u] then
- ligature[i] = u
- else
- present = false
- break
- end
- end
- if present then
- cover(coverage,unicode,ligature)
- done = done + 1
- else
- skip = skip + 1
- end
- else
- skip = skip + 1
- end
- end
- return coverage
- end
-
- local function prepare_kern(list,featuretype)
- local coverage = { }
- local cover = coveractions[featuretype]
- for code, replacement in next, list do
- local unicode = tounicode(code)
- local description = descriptions[unicode]
- if description and type(replacement) == "table" then
- local r = { }
- for k, v in next, replacement do
- local u = tounicode(k)
- if u then
- r[u] = v
- end
- end
- if next(r) then
- cover(coverage,unicode,r)
- done = done + 1
- else
- skip = skip + 1
- end
- else
- skip = skip + 1
- end
- end
- return coverage
- end
-
- local function prepare_pair(list,featuretype)
- local coverage = { }
- local cover = coveractions[featuretype]
- if cover then
- for code, replacement in next, list do
- local unicode = tounicode(code)
- local description = descriptions[unicode]
- if description and type(replacement) == "table" then
- local r = { }
- for k, v in next, replacement do
- local u = tounicode(k)
- if u then
- r[u] = v
- end
- end
- if next(r) then
- cover(coverage,unicode,r)
- done = done + 1
- else
- skip = skip + 1
- end
- else
- skip = skip + 1
- end
- end
- else
- report_otf("unknown cover type %a",featuretype)
- end
- return coverage
- end
-
- local function prepare_chain(list,featuretype,sublookups)
- -- todo: coveractions
- local rules = list.rules
- local coverage = { }
- if rules then
- local rulehash = { }
- local rulesize = 0
- local sequence = { }
- local nofsequences = 0
- local lookuptype = types[featuretype]
- for nofrules=1,#rules do
- local rule = rules[nofrules]
- local current = rule.current
- local before = rule.before
- local after = rule.after
- local replacements = rule.replacements or false
- local sequence = { }
- local nofsequences = 0
- if before then
- for n=1,#before do
- nofsequences = nofsequences + 1
- sequence[nofsequences] = before[n]
- end
- end
- local start = nofsequences + 1
- for n=1,#current do
- nofsequences = nofsequences + 1
- sequence[nofsequences] = current[n]
- end
- local stop = nofsequences
- if after then
- for n=1,#after do
- nofsequences = nofsequences + 1
- sequence[nofsequences] = after[n]
- end
- end
- local lookups = rule.lookups or false
- local subtype = nil
- if lookups and sublookups then
- for k, v in next, lookups do
- local lookup = sublookups[v]
- if lookup then
- lookups[k] = lookup
- if not subtype then
- subtype = lookup.type
- end
- else
- -- already expanded
- end
- end
- end
- if nofsequences > 0 then -- we merge coverage into one
- -- we copy as we can have different fonts
- local hashed = { }
- for i=1,nofsequences do
- local t = { }
- local s = sequence[i]
- for i=1,#s do
- local u = tounicode(s[i])
- if u then
- t[u] = true
- end
- end
- hashed[i] = t
- end
- sequence = hashed
- -- now we create the rule
- rulesize = rulesize + 1
- rulehash[rulesize] = {
- nofrules, -- 1
- lookuptype, -- 2
- sequence, -- 3
- start, -- 4
- stop, -- 5
- lookups, -- 6 (6/7 also signal of what to do)
- replacements, -- 7
- subtype, -- 8
- }
- for unic in next, sequence[start] do
- local cu = coverage[unic]
- if not cu then
- coverage[unic] = rulehash -- can now be done cleaner i think
- end
- end
- end
- end
- end
- return coverage
- end
-
- for s=1,#specifications do
- local specification = specifications[s]
- local valid = specification.valid
- local feature = specification.name or feature
- if not valid or valid(data,specification,feature) then
- local initialize = specification.initialize
- if initialize then
- -- when false is returned we initialize only once
- specification.initialize = initialize(specification,data) and initialize or nil
- end
- local askedfeatures = specification.features or everywhere
- local askedsteps = specification.steps or specification.subtables or { specification.data } or { }
- local featuretype = normalized[specification.type or "substitution"] or "substitution"
- local featureflags = specification.flags or noflags
- local featureorder = specification.order or { feature }
- local featurechain = (featuretype == "chainsubstitution" or featuretype == "chainposition") and 1 or 0
- local nofsteps = 0
- local steps = { }
- local sublookups = specification.lookups
- local category = nil
- if sublookups then
- local s = { }
- for i=1,#sublookups do
- local specification = sublookups[i]
- local askedsteps = specification.steps or specification.subtables or { specification.data } or { }
- local featuretype = normalized[specification.type or "substitution"] or "substitution"
- local featureflags = specification.flags or noflags
- local nofsteps = 0
- local steps = { }
- for i=1,#askedsteps do
- local list = askedsteps[i]
- local coverage = nil
- local format = nil
- if featuretype == "substitution" then
- coverage = prepare_substitution(list,featuretype)
- elseif featuretype == "ligature" then
- coverage = prepare_ligature(list,featuretype)
- elseif featuretype == "alternate" then
- coverage = prepare_alternate(list,featuretype)
- elseif featuretype == "multiple" then
- coverage = prepare_multiple(list,featuretype)
- elseif featuretype == "kern" then
- format = "kern"
- coverage = prepare_kern(list,featuretype)
- elseif featuretype == "pair" then
- format = "pair"
- coverage = prepare_pair(list,featuretype)
- end
- if coverage and next(coverage) then
- nofsteps = nofsteps + 1
- steps[nofsteps] = register(coverage,featuretype,format,feature,nofsteps,descriptions,resources)
- end
- end
- s[i] = {
- [stepkey] = steps,
- nofsteps = nofsteps,
- type = types[featuretype],
- }
- end
- sublookups = s
- end
- for i=1,#askedsteps do
- local list = askedsteps[i]
- local coverage = nil
- local format = nil
- if featuretype == "substitution" then
- category = "gsub"
- coverage = prepare_substitution(list,featuretype)
- elseif featuretype == "ligature" then
- category = "gsub"
- coverage = prepare_ligature(list,featuretype)
- elseif featuretype == "alternate" then
- category = "gsub"
- coverage = prepare_alternate(list,featuretype)
- elseif featuretype == "multiple" then
- category = "gsub"
- coverage = prepare_multiple(list,featuretype)
- elseif featuretype == "kern" then
- category = "gpos"
- format = "kern"
- coverage = prepare_kern(list,featuretype)
- elseif featuretype == "pair" then
- category = "gpos"
- format = "pair"
- coverage = prepare_pair(list,featuretype)
- elseif featuretype == "chainsubstitution" then
- category = "gsub"
- coverage = prepare_chain(list,featuretype,sublookups)
- elseif featuretype == "chainposition" then
- category = "gpos"
- coverage = prepare_chain(list,featuretype,sublookups)
- else
- report_otf("not registering feature %a, unknown category",feature)
- return
- end
- if coverage and next(coverage) then
- nofsteps = nofsteps + 1
- steps[nofsteps] = register(coverage,featuretype,format,feature,nofsteps,descriptions,resources)
- end
- end
- if nofsteps > 0 then
- -- script = { lang1, lang2, lang3 } or script = { lang1 = true, ... }
- for k, v in next, askedfeatures do
- if v[1] then
- askedfeatures[k] = tohash(v)
- end
- end
- if featureflags[1] then featureflags[1] = "mark" end
- if featureflags[2] then featureflags[2] = "ligature" end
- if featureflags[3] then featureflags[3] = "base" end
- local sequence = {
- chain = featurechain,
- features = { [feature] = askedfeatures },
- flags = featureflags,
- name = feature, -- redundant
- order = featureorder,
- [stepkey] = steps,
- nofsteps = nofsteps,
- type = types[featuretype],
- }
- -- todo : before|after|index
- if specification.prepend then
- insert(sequences,1,sequence)
- else
- insert(sequences,sequence)
- end
- -- register in metadata (merge as there can be a few)
- local features = fontfeatures[category]
- if not features then
- features = { }
- fontfeatures[category] = features
- end
- local k = features[feature]
- if not k then
- k = { }
- features[feature] = k
- end
- --
- for script, languages in next, askedfeatures do
- local kk = k[script]
- if not kk then
- kk = { }
- k[script] = kk
- end
- for language, value in next, languages do
- kk[language] = value
- end
- end
- end
- end
- end
- if trace_loading then
- report_otf("registering feature %a, affected glyphs %a, skipped glyphs %a",feature,done,skip)
- end
-end
-
----[[ end snippet from font-otc.lua ]]
local tlig_specification = {
{
@@ -1861,64 +1317,6 @@ local tlig_specification = {
},
}
-local anum_arabic = { --- these are the same as in font-otc
- [0x0030] = 0x0660,
- [0x0031] = 0x0661,
- [0x0032] = 0x0662,
- [0x0033] = 0x0663,
- [0x0034] = 0x0664,
- [0x0035] = 0x0665,
- [0x0036] = 0x0666,
- [0x0037] = 0x0667,
- [0x0038] = 0x0668,
- [0x0039] = 0x0669,
-}
-
-local anum_persian = {--- these are the same as in font-otc
- [0x0030] = 0x06F0,
- [0x0031] = 0x06F1,
- [0x0032] = 0x06F2,
- [0x0033] = 0x06F3,
- [0x0034] = 0x06F4,
- [0x0035] = 0x06F5,
- [0x0036] = 0x06F6,
- [0x0037] = 0x06F7,
- [0x0038] = 0x06F8,
- [0x0039] = 0x06F9,
-}
-
-local function valid(data)
- local features = data.resources.features
- if features then
- for k, v in next, features do
- for k, v in next, v do
- if v.arab then
- return true
- end
- end
- end
- end
-end
-
-local anum_specification = {
- {
- type = "substitution",
- features = { arab = { far = true, urd = true, snd = true } },
- data = anum_persian,
- flags = noflags,
- order = { "anum" },
- valid = valid,
- },
- {
- type = "substitution",
- features = { arab = { ["*"] = true } },
- data = anum_arabic,
- flags = noflags,
- order = { "anum" },
- valid = valid,
- },
-}
-
local rot13_specification = {
type = "substitution",
features = everywhere,
@@ -1942,50 +1340,27 @@ local rot13_specification = {
prepend = true,
}
-local extrafeatures = {
- tlig = { tlig_specification, "tex ligatures and substitutions" },
- anum = { anum_specification, "arabic numerals" },
- rot13 = { rot13_specification, "rot13" },
+local interrolig_specification = {
+ { type = "ligature", data = { [0x203d] = {0x21, 0x3f}, [0x2e18] = {0xa1, 0xbf}, }, },
+ { type = "ligature", data = { [0x203d] = {0x3f, 0x21}, [0x2e18] = {0xbf, 0xa1}, }, },
}
-function add_otf_feature (name, specification)
- if type (name) == "table" then
- specification = name
- name = specification.name
- end
- if type (name) == "string" then
- extrafeatures[name] = specification
- end
-end
-
-otf.addfeature = add_otf_feature
+local autofeatures = {
+ --- always present with Luaotfload; anum for Arabic and Persian is
+ --- predefined in font-otc.
+ { "tlig" , tlig_specification , "tex ligatures and substitutions" },
+ { "rot13", rot13_specification, "rot13" },
+ { "!!??", interrolig_specification, "interrobang substitutions" },
+}
-local install_extra_features = function (data, filename, raw)
- local metadata = data and data.metadata
- if not metadata then
- logreport ("both", 4, "features",
- "no metadata received from font “%s”; not \z
- installing extra features.", filename)
- return
- end
- local format = data.format
- if not format then
- logreport ("both", 4, "features",
- "no format info for font “%s”/“%s”; not \z
- installing extra features.",
- fontname, filename)
- return
- end
- for feature, specification in next, extrafeatures do
- if not fontname then fontname = "<unknown>" end
- if not subfont then subfont = -1 end
- local fontname = tostring (data.metadata.fontname) or "<unknown>"
- local subfont = tonumber (metadata.subfontindex) or -1
- logreport ("both", 3, "features",
- "register synthetic feature “%s” for %s font “%s”(%d)",
- feature, format, fontname, subfont)
- otf.features.register { name = feature, description = specification[2] }
- otf.enhancers.addfeature (data, feature, specification[1])
+local add_auto_features = function ()
+ local nfeats = #autofeatures
+ logreport ("both", 5, "features",
+ "auto-installing %d feature definitions", nfeats)
+ for i = 1, nfeats do
+ local name, spec, desc = unpack (autofeatures [i])
+ spec.description = desc
+ otf.addfeature (name, spec)
end
end
@@ -2001,10 +1376,7 @@ return {
return false
end
- otf = fonts.handlers.otf
- otf.enhancers.addfeature = current_addfeature
- otf.enhancers.register ("check extra features",
- install_extra_features)
+ add_auto_features ()
return true
end