diff options
Diffstat (limited to 'Master/texmf-dist/tex/luatex/luaotfload/luaotfload-features.lua')
-rw-r--r-- | Master/texmf-dist/tex/luatex/luaotfload/luaotfload-features.lua | 684 |
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 |