From 1ef10e57241a65e8ce2aeefdd83874e8999b7b44 Mon Sep 17 00:00:00 2001 From: Elie Roux Date: Wed, 10 Jun 2009 20:21:00 +0300 Subject: syncing with latest ConTeXt version (more feautures, less bugs), thanks to Khaled Hosny --- README | 3 + luaotfload.dtx | 30 +- otfl-data-con.lua | 6 +- otfl-font-cid.lua | 2 +- otfl-font-def.lua | 18 +- otfl-font-dum.lua | 30 +- otfl-font-otb.lua | 33 +- otfl-font-otc.lua | 215 +++++++++---- otfl-font-otd.lua | 78 +++++ otfl-font-otf.lua | 201 +++++------- otfl-font-oti.lua | 57 ++++ otfl-font-otn.lua | 157 +++++---- otfl-font-ott.lua | 935 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ otfl-font-tfm.lua | 23 +- otfl-font-xtx.lua | 26 +- otfl-luat-dum.lua | 7 +- otfl-node-fnt.lua | 18 +- otfl-node-ini.lua | 4 +- 18 files changed, 1574 insertions(+), 269 deletions(-) create mode 100644 otfl-font-otd.lua create mode 100644 otfl-font-oti.lua create mode 100644 otfl-font-ott.lua diff --git a/README b/README index 841b028..3cef5e1 100644 --- a/README +++ b/README @@ -40,8 +40,11 @@ Source files: otfl-font-ota.lua . otfl-font-otb.lua . otfl-font-otc.lua . + otfl-font-otd.lua . otfl-font-otf.lua . + otfl-font-oti.lua . otfl-font-otn.lua . + otfl-font-ott.lua . otfl-font-tfm.lua . otfl-font-xtx.lua . otfl-luat-dum.lua . diff --git a/luaotfload.dtx b/luaotfload.dtx index b93b987..2ed3a04 100644 --- a/luaotfload.dtx +++ b/luaotfload.dtx @@ -166,9 +166,12 @@ and the derived files % \item \texttt{node-dum.lua} % \item \texttt{font-ini.lua} % \item \texttt{font-tfm.lua} +% \item \texttt{font-cid.lua} +% \item \texttt{font-ott.lua} % \item \texttt{font-otf.lua} +% \item \texttt{font-otd.lua} +% \item \texttt{font-oti.lua} % \item \texttt{font-otb.lua} -% \item \texttt{font-cid.lua} % \item \texttt{font-otn.lua} % \item \texttt{font-ota.lua} % \item \texttt{font-otc.lua} @@ -271,6 +274,24 @@ function table.sortedhashkeys(tab) -- fast one return srt end +function table.reverse_hash(h) + local r = { } + for k,v in next, h do + r[v] = string.lower(string.gsub(k," ","")) + end + return r +end + +function table.reverse(t) + local tt = { } + if #t > 0 then + for i=#t,1,-1 do + tt[#tt+1] = t[i] + end + end + return tt +end + % \end{macrocode} % % We start loading some lua files. These two are some code not used by Con\TeX t at all that allow other modules to be used, it provides some low-level Con\TeX t functions. @@ -304,7 +325,7 @@ end % \end{macrocode} % -% Some more modules. We don't load \texttt{font-ott.lua}, as it only contains the meaning of the options, ans it works perfectly without it. We don't load neither \texttt{font-enc.lua} nor \texttt{font-afm.lua} as it will never be used here. +% Some more modules. We don't load neither \texttt{font-enc.lua} nor \texttt{font-afm.lua} as it will never be used here. % % \begin{macrocode} @@ -315,9 +336,12 @@ luaotfload.loadmodule('node-dum.lua') luaotfload.loadmodule('font-ini.lua') luaotfload.loadmodule('font-tfm.lua') +luaotfload.loadmodule('font-cid.lua') +luaotfload.loadmodule('font-ott.lua') luaotfload.loadmodule('font-otf.lua') +luaotfload.loadmodule('font-otd.lua') +luaotfload.loadmodule('font-oti.lua') luaotfload.loadmodule('font-otb.lua') -luaotfload.loadmodule('font-cid.lua') luaotfload.loadmodule('font-otn.lua') luaotfload.loadmodule('font-ota.lua') luaotfload.loadmodule('font-otc.lua') diff --git a/otfl-data-con.lua b/otfl-data-con.lua index ec146f0..02ee9ee 100644 --- a/otfl-data-con.lua +++ b/otfl-data-con.lua @@ -58,7 +58,7 @@ function containers.define(category, subcategory, version, enabled) enabled = enabled, version = version or 1.000, trace = false, - path = caches and caches.setpath(category,subcategory), + path = caches and caches.setpath and caches.setpath(category,subcategory), } c[subcategory] = s end @@ -116,3 +116,7 @@ end function containers.content(container,name) return container.storage[name] end + +function containers.cleanname(name) + return (gsub(lower(name),"[^%w%d]+","-")) +end diff --git a/otfl-font-cid.lua b/otfl-font-cid.lua index b1ddaae..b8dfc42 100644 --- a/otfl-font-cid.lua +++ b/otfl-font-cid.lua @@ -79,7 +79,7 @@ end local template = "%s-%s-%s.cidmap" local function locate(registry,ordering,supplement) - local filename = format(template,registry,ordering,supplement) + local filename = string.lower(format(template,registry,ordering,supplement)) local cidmap = fonts.cid.map[filename] if not cidmap then if trace_loading then diff --git a/otfl-font-def.lua b/otfl-font-def.lua index 284f8ef..f91575a 100644 --- a/otfl-font-def.lua +++ b/otfl-font-def.lua @@ -83,7 +83,8 @@ function define.add_specifier(symbol) local method = lpeg.S(specifiers) local lookup = lpeg.C(lpeg.P("file")+lpeg.P("name")) * colon -- hard test, else problems with : method local sub = left * lpeg.C(lpeg.P(1-left-right-method)^1) * right - local specification = lpeg.C(method) * lpeg.C(lpeg.P(1-method)^1) +--~ local specification = lpeg.C(method) * lpeg.C(lpeg.P(1-method)^1) + local specification = lpeg.C(method) * lpeg.C(lpeg.P(1)^1) local name = lpeg.C((1-sub-specification)^1) splitter = lpeg.P((lookup + lpeg.Cc("")) * name * (sub + lpeg.Cc("")) * (specification + lpeg.Cc(""))) end @@ -147,7 +148,7 @@ function tfm.hash_features(specification) local f = sortedhashkeys(normal) for i=1,#f do local v = f[i] - if v ~= "number" then + if v ~= "number" and v ~= "features" then -- i need to figure this out, features t[#t+1] = v .. '=' .. tostring(normal[v]) end end @@ -160,6 +161,9 @@ function tfm.hash_features(specification) t[#t+1] = v .. '=' .. tostring(vtf[v]) end end +--~ if specification.mathsize then +--~ t[#t+1] = "mathsize=" .. specification.mathsize +--~ end if #t > 0 then return concat(t,"+") end @@ -364,13 +368,13 @@ function readers.afm(specification,method) if not tfmtable then method = method or define.method or "afm or tfm" if method == "tfm" then - tfmtable = check_tfm(specification,fullname) + tfmtable = check_tfm(specification,specification.name) elseif method == "afm" then - tfmtable = check_afm(specification,fullname) + tfmtable = check_afm(specification,specification.name) elseif method == "tfm or afm" then - tfmtable = check_tfm(specification,fullname) or check_afm(specification,fullname) - else-- method == "afm or tfm" then - tfmtable = check_afm(specification,fullname) or check_tfm(specification,fullname) + tfmtable = check_tfm(specification,specification.name) or check_afm(specification,specification.name) + else -- method == "afm or tfm" or method == "" then + tfmtable = check_afm(specification,specification.name) or check_tfm(specification,specification.name) end end else diff --git a/otfl-font-dum.lua b/otfl-font-dum.lua index 0dedc85..0d28128 100644 --- a/otfl-font-dum.lua +++ b/otfl-font-dum.lua @@ -72,14 +72,42 @@ function fonts.names.resolve(name,sub) end loaded = true end - if type(data) == "table" and data.version == 1.07 then + if type(data) == "table" and data.version == 1.08 then local condensed = string.gsub(name,"[^%a%d]","") local found = data.mapping and data.mapping[condensed] if found then local filename, is_sub = found[3], found[4] + if is_sub then is_sub = found[2] end return filename, is_sub else return name, false -- fallback to filename end end end + +-- For the moment we put this (adapted) pseudo feature here. + +table.insert(fonts.triggers,"itlc") + +local function itlc(tfmdata,value) + if value then + -- the magic 40 and it formula come from Dohyun Kim + local metadata = tfmdata.shared.otfdata.metadata + if metadata then + local italicangle = metadata.italicangle + if italicangle and italicangle ~= 0 then + local uwidth = (metadata.uwidth or 40)/2 + for unicode, d in next, tfmdata.descriptions do + local it = d.boundingbox[3] - d.width + uwidth + if it ~= 0 then + d.italic = it + end + end + tfmdata.has_italic = true + end + end + end +end + +fonts.initializers.base.otf.itlc = itlc +fonts.initializers.node.otf.itlc = itlc diff --git a/otfl-font-otb.lua b/otfl-font-otb.lua index 20ddbfc..5ef44a0 100644 --- a/otfl-font-otb.lua +++ b/otfl-font-otb.lua @@ -22,6 +22,7 @@ local trace_kerns = false trackers.register("otf.kerns", function local trace_preparing = false trackers.register("otf.preparing", function(v) trace_preparing = v end) local wildcard = "*" +local default = "dflt" local split_at_space = lpeg.Ct(lpeg.splitat(" ")) -- no trailing or multiple spaces anyway @@ -141,8 +142,8 @@ local function collect_lookups(otfdata,kind,script,language) local sequence = sequences[s] local features = sequence.features features = features and features[kind] - features = features and (features[script] or features[wildcard]) - features = features and (features[language] or features[wildcard]) + features = features and (features[script] or features[default] or features[wildcard]) + features = features and (features[language] or features[default] or features[wildcard]) if features then local subtables = sequence.subtables if subtables then @@ -322,20 +323,46 @@ function otf.features.register_base_kern(tag) supported_gsub[#supported_gpos+1] = tag end +local basehash, basehashes = { }, 1 + function fonts.initializers.base.otf.features(tfmdata,value) if true then -- value then -- not shared local t = trace_preparing and os.clock() local features = tfmdata.shared.features if features then + local h = { } for f=1,#supported_gsub do local feature = supported_gsub[f] - prepare_base_substitutions(tfmdata,feature,features[feature]) + local value = features[feature] + prepare_base_substitutions(tfmdata,feature,value) + if value then + h[#h+1] = feature .. "=" .. tostring(value) + end end for f=1,#supported_gpos do local feature = supported_gpos[f] + local value = features[feature] prepare_base_kerns(tfmdata,feature,features[feature]) + if value then + h[#h+1] = feature .. "=" .. tostring(value) + end + end + local hash = concat(h," ") + local base = basehash[hash] + if not base then + basehashes = basehashes + 1 + base = basehashes + basehash[hash] = base end + -- We need to make sure that luatex sees the difference between + -- base fonts that have different glyphs in the same slots in fonts + -- that have the same fullname (or filename). LuaTeX will merge fonts + -- eventually (and subset later on). If needed we can use a more + -- verbose name as long as we don't use <()<>[]{}/%> and the length + -- is < 128. + tfmdata.fullname = tfmdata.fullname .. "-" .. base +--~ logs.report("otf define","fullname base hash: '%s', featureset '%s'",tfmdata.fullname,hash) end if trace_preparing then logs.report("otf define","preparation time is %0.3f seconds for %s",os.clock()-t,tfmdata.fullname or "?") diff --git a/otfl-font-otc.lua b/otfl-font-otc.lua index 40631fb..f75da39 100644 --- a/otfl-font-otc.lua +++ b/otfl-font-otc.lua @@ -18,79 +18,188 @@ local trace_loading = false trackers.register("otf.loading", function(v) trace_ local otf = fonts.otf local tfm = fonts.tfm --- for good old times (usage is to be avoided) - -local tlig_list = { - endash = "hyphen hyphen", - emdash = "hyphen hyphen hyphen", ---~ quotedblleft = "quoteleft quoteleft", ---~ quotedblright = "quoteright quoteright", ---~ quotedblleft = "grave grave", ---~ quotedblright = "quotesingle quotesingle", ---~ quotedblbase = "comma comma", -} -local trep_list = { ---~ [0x0022] = 0x201D, - [0x0027] = 0x2019, ---~ [0x0060] = 0x2018, -} - -- instead of "script = "DFLT", langs = { 'dflt' }" we now use wildcards (we used to -- have always); some day we can write a "force always when true" trick for other -- features as well -local tlig_feature = { - features = { { scripts = { { script = "*", langs = { "*" }, } }, tag = "tlig", comment = "added bij mkiv" }, }, - name = "ctx_tlig", - subtables = { { name = "ctx_tlig_1" } }, - type = "gsub_ligature", - flags = { }, +local extra_lists = { + tlig = { + { + endash = "hyphen hyphen", + emdash = "hyphen hyphen hyphen", + -- quotedblleft = "quoteleft quoteleft", + -- quotedblright = "quoteright quoteright", + -- quotedblleft = "grave grave", + -- quotedblright = "quotesingle quotesingle", + -- quotedblbase = "comma comma", + }, + }, + trep = { + { + -- [0x0022] = 0x201D, + [0x0027] = 0x2019, + -- [0x0060] = 0x2018, + }, + }, + anum = { + { -- arabic + [0x0030] = 0x0660, + [0x0031] = 0x0661, + [0x0032] = 0x0662, + [0x0033] = 0x0663, + [0x0034] = 0x0664, + [0x0035] = 0x0665, + [0x0036] = 0x0666, + [0x0037] = 0x0667, + [0x0038] = 0x0668, + [0x0039] = 0x0669, + }, + { -- persian + [0x0030] = 0x06F0, + [0x0031] = 0x06F1, + [0x0032] = 0x06F2, + [0x0033] = 0x06F3, + [0x0034] = 0x06F4, + [0x0035] = 0x06F5, + [0x0036] = 0x06F6, + [0x0037] = 0x06F7, + [0x0038] = 0x06F8, + [0x0039] = 0x06F9, + }, + }, } -local trep_feature = { - features = { { scripts = { { script = "*", langs = { "*" }, } }, tag = "trep", comment = "added bij mkiv" }, }, - name = "ctx_trep", - subtables = { { name = "ctx_trep_1" } }, - type = "gsub_single", - flags = { }, + +local extra_features = { -- maybe just 1..n so that we prescribe order + tlig = { + { + features = { { scripts = { { script = "*", langs = { "*" }, } }, tag = "tlig", comment = "added bij mkiv" }, }, + name = "ctx_tlig_1", + subtables = { { name = "ctx_tlig_1_s" } }, + type = "gsub_ligature", + flags = { }, + }, + }, + trep = { + { + features = { { scripts = { { script = "*", langs = { "*" }, } }, tag = "trep", comment = "added bij mkiv" }, }, + name = "ctx_trep_1", + subtables = { { name = "ctx_trep_1_s" } }, + type = "gsub_single", + flags = { }, + }, + }, + anum = { + { + features = { { scripts = { { script = "arab", langs = { "dflt", "FAR" }, } }, tag = "anum", comment = "added bij mkiv" }, }, + name = "ctx_anum_1", + subtables = { { name = "ctx_anum_1_s" } }, + type = "gsub_single", + flags = { }, + }, + { + features = { { scripts = { { script = "arab", langs = { "URD" }, } }, tag = "anum", comment = "added bij mkiv" }, }, + name = "ctx_anum_2", + subtables = { { name = "ctx_anum_2_s" } }, + type = "gsub_single", + flags = { }, + }, + }, } +fonts.otf.enhancers["add some missing characters"] = function(data,filename) + -- todo +end + fonts.otf.enhancers["enrich with features"] = function(data,filename) - local glyphs = data.glyphs - local indices = data.map.map - for unicode, index in next, indices do - local glyph = glyphs[index] - local l = tlig_list[glyph.name] - if l then - local o = glyph.lookups or { } - o["ctx_tlig_1"] = { { "ligature", l, glyph.name } } - glyph.lookups = o - end - local r = trep_list[unicode] - if r then - local replacement = indices[r] - if replacement then - local o = glyph.lookups or { } - o["ctx_trep_1"] = { { "substitution", glyphs[replacement].name } } --- - glyph.lookups = o + -- could be done elsewhere (true can be #) + local used = { } + for i=1,#otf.glists do + local g = data[otf.glists[i]] + if g then + for i=1,#g do + local f = g[i].features + if f then + for i=1,#f do + local t = f[i].tag + if t then used[t] = true end + end + end end end end + -- + local glyphs = data.glyphs + local indices = data.map.map data.gsub = data.gsub or { } - if trace_loading then - logs.report("load otf","enhance: registering tlig feature") - end - insert(data.gsub,1,table.fastcopy(tlig_feature)) - if trace_loading then - logs.report("load otf","enhance: registering trep feature") + for kind, specifications in next, extra_features do + if not used[kind] then + local done = 0 + for s=1,#specifications do + local added = false + local specification = specifications[s] + local list = extra_lists[kind][s] + local name = specification.name .. "_s" + if specification.type == "gsub_ligature" then + for unicode, index in next, indices do + local glyph = glyphs[index] + local ligature = list[glyph.name] + if ligature then + local o = glyph.lookups or { } + -- o[name] = { "ligature", ligature, glyph.name } + o[name] = { + { + ["type"] = "ligature", + ["specification"] = { + char = glyph.name, + components = ligature, + } + } + } + glyph.lookups, done, added = o, done+1, true + end + end + elseif specification.type == "gsub_single" then + for unicode, index in next, indices do + local glyph = glyphs[index] + local r = list[unicode] + if r then + local replacement = indices[r] + if replacement and glyphs[replacement] then + local o = glyph.lookups or { } + -- o[name] = { { "substitution", glyphs[replacement].name } } + o[name] = { + { + ["type"] = "substitution", + ["specification"] = { + variant = glyphs[replacement].name, + } + } + } + glyph.lookups, done, added = o, done+1, true + end + end + end + end + if added then + insert(data.gsub,s,table.fastcopy(specification)) -- right order + end + end + if done > 0 then + if trace_loading then + logs.report("load otf","enhance: registering %s feature (%s glyphs affected)",kind,done) + end + end + end end - insert(data.gsub,1,table.fastcopy(trep_feature)) end otf.tables.features['tlig'] = 'TeX Ligatures' otf.tables.features['trep'] = 'TeX Replacements' +otf.tables.features['anum'] = 'Arabic Digits' otf.features.register_base_substitution('tlig') otf.features.register_base_substitution('trep') +otf.features.register_base_substitution('anum') -- the functionality is defined elsewhere diff --git a/otfl-font-otd.lua b/otfl-font-otd.lua new file mode 100644 index 0000000..78a8281 --- /dev/null +++ b/otfl-font-otd.lua @@ -0,0 +1,78 @@ +if not modules then modules = { } end modules ['font-otd'] = { + version = 1.001, + comment = "companion to font-ini.tex", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +local trace_dynamics = false trackers.register("otf.dynamics", function(v) trace_dynamics = v end) + +fonts = fonts or { } +fonts.otf = fonts.otf or { } + +local otf = fonts.otf +local fontdata = fonts.ids + +otf.features = otf.features or { } +otf.features.default = otf.features.default or { } + +local context_setups = fonts.define.specify.context_setups +local context_numbers = fonts.define.specify.context_numbers + +local a_to_script = { } otf.a_to_script = a_to_script +local a_to_language = { } otf.a_to_language = a_to_language + +function otf.set_dynamics(font,dynamics,attribute) + features = context_setups[context_numbers[attribute]] -- can be moved to caller + if features then + local script = features.script or 'dflt' + local language = features.language or 'dflt' + local ds = dynamics[script] + if not ds then + ds = { } + dynamics[script] = ds + end + local dsl = ds[language] + if not dsl then + dsl = { } + ds[language] = dsl + end + local dsla = dsl[attribute] + if dsla then + -- if trace_dynamics then + -- logs.report("otf define","using dynamics %s: attribute %s, script %s, language %s",context_numbers[attribute],attribute,script,language) + -- end + return dsla + else + local tfmdata = fontdata[font] + a_to_script [attribute] = script + a_to_language[attribute] = language + -- we need to save some values + local saved = { + script = tfmdata.script, + language = tfmdata.language, + mode = tfmdata.mode, + features = tfmdata.shared.features + } + tfmdata.mode = "node" + tfmdata.language = language + tfmdata.script = script + tfmdata.shared.features = { } + -- end of save + dsla = otf.set_features(tfmdata,fonts.define.check(features,otf.features.default)) + if trace_dynamics then + logs.report("otf define","setting dynamics %s: attribute %s, script %s, language %s",context_numbers[attribute],attribute,script,language) + end + -- we need to restore some values + tfmdata.script = saved.script + tfmdata.language = saved.language + tfmdata.mode = saved.mode + tfmdata.shared.features = saved.features + -- end of restore + dynamics[script][language][attribute] = dsla -- cache + return dsla + end + end + return nil -- { } +end diff --git a/otfl-font-otf.lua b/otfl-font-otf.lua index a63b9e3..20273f8 100644 --- a/otfl-font-otf.lua +++ b/otfl-font-otf.lua @@ -61,6 +61,7 @@ and don't change to frequently.

fonts = fonts or { } fonts.otf = fonts.otf or { } +fonts.tfm = fonts.tfm or { } local otf = fonts.otf local tfm = fonts.tfm @@ -78,12 +79,14 @@ otf.features.list = otf.features.list or { } otf.features.default = otf.features.default or { } otf.enhancers = otf.enhancers or { } +otf.glists = { "gsub", "gpos" } -otf.version = 2.619 -otf.pack = true +otf.version = 2.626 -- beware: also sync font-mis.lua +otf.pack = true -- beware: also sync font-mis.lua otf.syncspace = true otf.notdef = false otf.cache = containers.define("fonts", "otf", otf.version, true) +otf.cleanup_aat = false -- only context --[[ldx--

We start with a lot of tables and related functions.

@@ -197,13 +200,16 @@ local enhancers = { -- away from the enhancers namespace "patch bugs", "merge cid fonts", "prepare unicode", "cleanup ttf tables", "compact glyphs", "reverse coverage", - "enrich with features", + "cleanup aat", "enrich with features", "add some missing characters", "reorganize kerns", -- moved here "flatten glyph lookups", "flatten anchor tables", "flatten feature tables", - "prepare luatex tables", "analyse features", "analyse anchors", "analyse marks", "analyse unicodes", "analyse subtables", + "prepare luatex tables", + "analyse features", "rehash features", + "analyse anchors", "analyse marks", "analyse unicodes", "analyse subtables", "check italic correction","check math", "share widths", "strip not needed data", + "migrate metadata", } function otf.load(filename,format,sub,featurefile) @@ -213,11 +219,10 @@ function otf.load(filename,format,sub,featurefile) end if sub == "" then sub = false end local hash = name - if sub then -- name cleanup will move to cache code + if sub then hash = hash .. "-" .. sub - hash = lower(hash) - hash = gsub(hash,"[^%w%d]+","-") end + hash = containers.cleanname(hash) local data = containers.read(otf.cache(), hash) local size = lfs.attributes(filename,"size") or 0 if not data or data.verbose ~= fonts.verbose or data.size ~= size then @@ -261,7 +266,7 @@ function otf.load(filename,format,sub,featurefile) end end if data then - otf.enhance("unpack",data,filename,false) -- no mesage here + otf.enhance("unpack",data,filename,false) -- no message here otf.add_dimensions(data) if trace_sequences then otf.show_feature_order(data,filename) @@ -356,6 +361,11 @@ otf.enhancers["prepare luatex tables"] = function(data,filename) luatex.creator = "context mkiv" end +otf.enhancers["cleanup aat"] = function(data,filename) + if otf.cleanup_aat then + end +end + local function analyze_features(g, features) if g then local t, done = { }, { } @@ -380,9 +390,40 @@ local function analyze_features(g, features) end otf.enhancers["analyse features"] = function(data,filename) - local luatex = data.luatex - luatex.gposfeatures = analyze_features(data.gpos) - luatex.gsubfeatures = analyze_features(data.gsub) + -- local luatex = data.luatex + -- luatex.gposfeatures = analyze_features(data.gpos) + -- luatex.gsubfeatures = analyze_features(data.gsub) +end + +otf.enhancers["rehash features"] = function(data,filename) + local features = { } + data.luatex.features = features + for k, what in next, otf.glists do + local dw = data[what] + if dw then + local f = { } + features[what] = f + for i=1,#dw do + local d= dw[i] + local dfeatures = d.features + if dfeatures then + for i=1,#dfeatures do + local df = dfeatures[i] + local tag = strip(lower(df.tag)) + local ft = f[tag] if not ft then ft = {} f[tag] = ft end + local dscripts = df.scripts + for script, languages in next, dscripts do + script = strip(lower(script)) + local fts = ft[script] if not fts then fts = {} ft[script] = fts end + for i=1,#languages do + fts[strip(lower(languages[i]))] = true + end + end + end + end + end + end + end end otf.enhancers["analyse anchors"] = function(data,filename) @@ -433,6 +474,7 @@ local ligsplitter = lpeg.Ct(other * (lpeg.P("_") * other)^0) otf.enhancers["analyse unicodes"] = function(data,filename) local unicodes = data.luatex.unicodes + -- we need to move this code unicodes['space'] = unicodes['space'] or 32 -- handly later on unicodes['hyphen'] = unicodes['hyphen'] or 45 -- handly later on unicodes['zwj'] = unicodes['zwj'] or zwj -- handly later on @@ -616,7 +658,18 @@ otf.enhancers["prepare unicode"] = function(data,filename) if not luatex then luatex = { } data.luatex = luatex end local indices, unicodes, multiples, internals = { }, { }, { }, { } local glyphs = data.glyphs - local mapmap = data.map.map + local mapmap = data.map + if not mapmap then + logs.report("load otf","no map in %s",filename) + mapmap = { } + data.map = { map = mapmap } + elseif not mapmap.map then + logs.report("load otf","no unicode map in %s",filename) + mapmap = { } + data.map.map = mapmap + else + mapmap = mapmap.map + end local criterium = fonts.private local private = fonts.private for index, glyph in next, glyphs do @@ -720,6 +773,7 @@ end otf.enhancers["check italic correction"] = function(data,filename) local glyphs = data.glyphs + local ok = false for index, glyph in next, glyphs do local ic = glyph.italic_correction if ic then @@ -727,8 +781,12 @@ otf.enhancers["check italic correction"] = function(data,filename) glyph.italic = ic end glyph.italic_correction = nil + ok = true end end + -- we can use this to avoid calculations + otf.tables.valid_fields[#otf.tables.valid_fields+1] = "has_italic" + data.has_italic = true end otf.enhancers["check math"] = function(data,filename) @@ -993,6 +1051,9 @@ otf.enhancers["strip not needed data"] = function(data,filename) data.gsub = nil data.anchor_classes = nil end +end + +otf.enhancers["migrate metadata"] = function(data,filename) local global_fields = otf.tables.global_fields local metadata = { } for k,v in next, data do @@ -1082,7 +1143,7 @@ end otf.enhancers["flatten feature tables"] = function(data,filename) -- is this needed? do we still use them at all? - for _, tag in next, { "gsub", "gpos" } do + for _, tag in next, otf.glists do if data[tag] then if trace_loading then logs.report("load otf", "flattening %s table", tag) @@ -1229,6 +1290,7 @@ function otf.otf_to_tfm(specification) tfmdata.marks = otfdata.luatex.marks tfmdata.originals = otfdata.luatex.originals tfmdata.changed = { } + tfmdata.has_italic = otfdata.metadata.has_italic if not tfmdata.language then tfmdata.language = 'dflt' end if not tfmdata.script then tfmdata.script = 'dflt' end shared.processes, shared.features = otf.set_features(tfmdata,fonts.define.check(features,otf.features.default)) @@ -1374,14 +1436,11 @@ function otf.copy_to_tfm(data,cache_id) -- we can save a copy when we reorder th end spaceunits = tonumber(spaceunits) or tfm.units/2 -- 500 -- brrr parameters.slant = 0 - parameters.space = spaceunits - parameters.space_stretch = tfm.units/2 -- 500 - -- parameters.space_shrink = 2*tfm.units/3 -- 333 - parameters.space_shrink = 1*tfm.units/3 -- 333 - -- parameters.x_height = 4*tfm.units/5 -- 400 + parameters.space = spaceunits -- 3.333 (cmr10) + parameters.space_stretch = tfm.units/2 -- 500 -- 1.666 (cmr10) + parameters.space_shrink = 1*tfm.units/3 -- 333 -- 1.111 (cmr10) parameters.x_height = 2*tfm.units/5 -- 400 parameters.quad = tfm.units -- 1000 - parameters.extra_space = 0 if spaceunits < 2*tfm.units/5 then -- todo: warning end @@ -1399,6 +1458,7 @@ function otf.copy_to_tfm(data,cache_id) -- we can save a copy when we reorder th parameters.space_stretch = spaceunits/2 parameters.space_shrink = spaceunits/3 end + parameters.extra_space = parameters.space_shrink -- 1.111 (cmr10) if pfminfo.os2_xheight and pfminfo.os2_xheight > 0 then parameters.x_height = pfminfo.os2_xheight else @@ -1479,106 +1539,3 @@ function tfm.read_from_open_type(specification) end return tfmtable end - -local a_to_script = { } otf.a_to_script = a_to_script -local a_to_language = { } otf.a_to_language = a_to_language - -otf.default_language = 'latn' -otf.default_script = 'dflt' - -local context_setups = fonts.define.specify.context_setups -local context_numbers = fonts.define.specify.context_numbers - -function otf.set_dynamics(font,dynamics,attribute) - features = context_setups[context_numbers[attribute]] -- can be moved to caller - if features then - local script = features.script or 'dflt' - local language = features.language or 'dflt' - local ds = dynamics[script] - if not ds then - ds = { } - dynamics[script] = ds - end - local dsl = ds[language] - if not dsl then - dsl = { } - ds[language] = dsl - end - local dsla = dsl[attribute] - if dsla then - -- if trace_dynamics then - -- logs.report("otf define","using dynamics %s: attribute %s, script %s, language %s",context_numbers[attribute],attribute,script,language) - -- end - return dsla - else - local tfmdata = fontdata[font] - a_to_script [attribute] = script - a_to_language[attribute] = language - -- we need to save some values - local saved = { - script = tfmdata.script, - language = tfmdata.language, - mode = tfmdata.mode, - features = tfmdata.shared.features - } - tfmdata.mode = "node" - tfmdata.language = language - tfmdata.script = script - tfmdata.shared.features = { } - -- end of save - dsla = otf.set_features(tfmdata,fonts.define.check(features,otf.features.default)) - if trace_dynamics then - logs.report("otf define","setting dynamics %s: attribute %s, script %s, language %s",context_numbers[attribute],attribute,script,language) - end - -- we need to restore some values - tfmdata.script = saved.script - tfmdata.language = saved.language - tfmdata.mode = saved.mode - tfmdata.shared.features = saved.features - -- end of restore - dynamics[script][language][attribute] = dsla -- cache - return dsla - end - end - return nil -- { } -end - --- common stuff - -function otf.features.language(tfmdata,value) - if value then - value = lower(value) - if otf.tables.languages[value] then - tfmdata.language = value - end - end -end - -function otf.features.script(tfmdata,value) - if value then - value = lower(value) - if otf.tables.scripts[value] then - tfmdata.script = value - end - end -end - -function otf.features.mode(tfmdata,value) - if value then - tfmdata.mode = lower(value) - end -end - -fonts.initializers.base.otf.language = otf.features.language -fonts.initializers.base.otf.script = otf.features.script -fonts.initializers.base.otf.mode = otf.features.mode -fonts.initializers.base.otf.method = otf.features.mode - -fonts.initializers.node.otf.language = otf.features.language -fonts.initializers.node.otf.script = otf.features.script -fonts.initializers.node.otf.mode = otf.features.mode -fonts.initializers.node.otf.method = otf.features.mode - -otf.features.register("features",true) -- we always do features -table.insert(fonts.processors,"features") -- we need a proper function for doing this - diff --git a/otfl-font-oti.lua b/otfl-font-oti.lua new file mode 100644 index 0000000..cbac6d3 --- /dev/null +++ b/otfl-font-oti.lua @@ -0,0 +1,57 @@ +if not modules then modules = { } end modules ['font-oti'] = { + version = 1.001, + comment = "companion to font-ini.tex", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +-- i need to check features=yes|no also in relation to hashing + +local lower = string.lower + +local otf = fonts.otf + +otf.default_language = 'latn' +otf.default_script = 'dflt' + +local languages = otf.tables.languages +local scripts = otf.tables.scripts + +function otf.features.language(tfmdata,value) + if value then + value = lower(value) + if languages[value] then + tfmdata.language = value + end + end +end + +function otf.features.script(tfmdata,value) + if value then + value = lower(value) + if scripts[value] then + tfmdata.script = value + end + end +end + +function otf.features.mode(tfmdata,value) + if value then + tfmdata.mode = lower(value) + end +end + +fonts.initializers.base.otf.language = otf.features.language +fonts.initializers.base.otf.script = otf.features.script +fonts.initializers.base.otf.mode = otf.features.mode +fonts.initializers.base.otf.method = otf.features.mode + +fonts.initializers.node.otf.language = otf.features.language +fonts.initializers.node.otf.script = otf.features.script +fonts.initializers.node.otf.mode = otf.features.mode +fonts.initializers.node.otf.method = otf.features.mode + +otf.features.register("features",true) -- we always do features +table.insert(fonts.processors,"features") -- we need a proper function for doing this + diff --git a/otfl-font-otn.lua b/otfl-font-otn.lua index c4c42fa..d23a8a0 100644 --- a/otfl-font-otn.lua +++ b/otfl-font-otn.lua @@ -134,6 +134,8 @@ local trace_cursive = false trackers.register("otf.cursive", function local trace_preparing = false trackers.register("otf.preparing", function(v) trace_preparing = v end) local trace_bugs = false trackers.register("otf.bugs", function(v) trace_bugs = v end) local trace_details = false trackers.register("otf.details", function(v) trace_details = v end) +local trace_applied = false trackers.register("otf.applied", function(v) trace_applied = v end) +local trace_steps = false trackers.register("otf.steps", function(v) trace_steps = v end) trackers.register("otf.verbose_chain", function(v) otf.setcontextchain(v and "verbose") end) trackers.register("otf.normal_chain", function(v) otf.setcontextchain(v and "normal") end) @@ -155,6 +157,7 @@ local has_attribute = node.has_attribute local zwnj = 0x200C local zwj = 0x200D local wildcard = "*" +local default = "dflt" local split_at_space = lpeg.Ct(lpeg.splitat(" ")) -- no trailing or multiple spaces anyway @@ -201,11 +204,13 @@ local lookuptable = false local anchorlookups = false local handlers = { } local rlmode = 0 +local featurevalue = false -- we cheat a bit and assume that a font,attr combination are kind of ranged local context_setups = fonts.define.specify.context_setups local context_numbers = fonts.define.specify.context_numbers +local context_merged = fonts.define.specify.context_merged -- we cannot optimize with "start = first_character(head)" because then we don't -- know which rlmode we're in which messes up cursive handling later on @@ -226,7 +231,10 @@ local registerstep = (nodes and nodes.tracers and nodes.tracers.steppers.regi local registermessage = (nodes and nodes.tracers and nodes.tracers.steppers.message) or function() end local function logprocess(...) - logs.report("otf direct",registermessage(...)) + if trace_steps then + registermessage(...) + end + logs.report("otf direct",...) end local function logwarning(...) logs.report("otf direct",...) @@ -259,9 +267,9 @@ local function cref(kind,chainname,chainlookupname,lookupname,index) if index then return format("feature %s, chain %s, sub %s, lookup %s, index %s",kind,chainname,chainlookupname,lookupname,index) elseif lookupname then - return format("feature %s, chain %s, sub %s, lookup %s",kind,chainname,chainlookupname,lookupname) + return format("feature %s, chain %s, sub %s, lookup %s",kind,chainname or "?",chainlookupname or "?",lookupname) elseif chainlookupname then - return format("feature %s, chain %s, sub %s",kind,chainname,chainlookupname) + return format("feature %s, chain %s, sub %s",kind,chainname or "?",chainlookupname) elseif chainname then return format("feature %s, chain %s",kind,chainname) else @@ -369,7 +377,7 @@ function handlers.gsub_single(start,kind,lookupname,replacement) end local function alternative_glyph(start,alternatives,kind,chainname,chainlookupname,lookupname) -- chainname and chainlookupname optional - local value, choice, n = tfmdata.shared.features[kind], nil, #alternatives + local value, choice, n = featurevalue or tfmdata.shared.features[kind], nil, #alternatives -- global value, brrr if value == "random" then local r = math.random(1,n) value, choice = format("random, choice %s",r), alternatives[r] @@ -381,6 +389,8 @@ local function alternative_glyph(start,alternatives,kind,chainname,chainlookupna value, choice = "default, choice 1", alternatives[1] elseif value > n then value, choice = format("no %s variants, taking %s",value,n), alternatives[n] + elseif value == 0 then + value, choice = format("choice %s (no change)",value), start.char elseif value < 1 then value, choice = format("no %s variants, taking %s",value,1), alternatives[1] else @@ -554,7 +564,7 @@ function handlers.gpos_mark2base(start,kind,lookupname,markanchors,sequence) end else -- if trace_bugs then -- logwarning("%s: char %s is missing in font",pref(kind,lookupname),gref(basechar)) - fonts.register_missing(currentfont,basechar) + fonts.register_message(currentfont,basechar,"no base anchors") end elseif trace_bugs then logwarning("%s: prev node is no char",pref(kind,lookupname)) @@ -624,7 +634,7 @@ function handlers.gpos_mark2ligature(start,kind,lookupname,markanchors,sequence) end else -- if trace_bugs then -- logwarning("%s: char %s is missing in font",pref(kind,lookupname),gref(basechar)) - fonts.register_missing(currentfont,basechar) + fonts.register_message(currentfont,basechar,"no base anchors") end elseif trace_bugs then logwarning("%s: prev node is no char",pref(kind,lookupname)) @@ -638,8 +648,8 @@ end function handlers.gpos_mark2mark(start,kind,lookupname,markanchors,sequence) local markchar = start.char if marks[markchar] then - local alreadydone = markonce and has_attribute(start,markmark) - if not alreadydone then +--~ local alreadydone = markonce and has_attribute(start,markmark) +--~ if not alreadydone then local base = start.prev -- [glyph] [basemark] [start=mark] if base and base.id == glyph and base.subtype<256 and base.font == currentfont then -- subtype test can go local basechar = base.char @@ -670,14 +680,14 @@ function handlers.gpos_mark2mark(start,kind,lookupname,markanchors,sequence) end else -- if trace_bugs then -- logwarning("%s: char %s is missing in font",pref(kind,lookupname),gref(basechar)) - fonts.register_missing(currentfont,basechar) + fonts.register_message(currentfont,basechar,"no base anchors") end elseif trace_bugs then logwarning("%s: prev node is no mark",pref(kind,lookupname)) end - elseif trace_marks and trace_details then - logprocess("%s, mark %s is already bound (n=%s), ignoring mark2mark",pref(kind,lookupname),gref(markchar),alreadydone) - end +--~ elseif trace_marks and trace_details then +--~ logprocess("%s, mark %s is already bound (n=%s), ignoring mark2mark",pref(kind,lookupname),gref(markchar),alreadydone) +--~ end elseif trace_bugs then logwarning("%s: mark %s is no mark",pref(kind,lookupname),gref(markchar)) end @@ -725,7 +735,7 @@ function handlers.gpos_cursive(start,kind,lookupname,exitanchors,sequence) -- to end else -- if trace_bugs then -- logwarning("%s: char %s is missing in font",pref(kind,lookupname),gref(startchar)) - fonts.register_missing(currentfont,startchar) + fonts.register_message(currentfont,startchar,"no entry anchors") end break end @@ -822,7 +832,10 @@ local chainmores = { } local chainprocs = { } local function logprocess(...) - logs.report("otf subchain",registermessage(...)) + if trace_steps then + registermessage(...) + end + logs.report("otf subchain",...) end local function logwarning(...) logs.report("otf subchain",...) @@ -862,7 +875,10 @@ end -- end local function logprocess(...) - logs.report("otf chain",registermessage(...)) + if trace_steps then + registermessage(...) + end + logs.report("otf chain",...) end local function logwarning(...) logs.report("otf chain",...) @@ -1270,8 +1286,8 @@ end function chainprocs.gpos_mark2mark(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname) local markchar = start.char if marks[markchar] then - local alreadydone = markonce and has_attribute(start,markmark) - if not alreadydone then +--~ local alreadydone = markonce and has_attribute(start,markmark) +--~ if not alreadydone then -- local markanchors = descriptions[markchar].anchors markanchors = markanchors and markanchors.mark local subtables = currentlookup.subtables local lookupname = subtables[1] @@ -1312,9 +1328,9 @@ function chainprocs.gpos_mark2mark(start,stop,kind,chainname,currentcontext,cach elseif trace_bugs then logwarning("%s: mark %s has no anchors",cref(kind,chainname,chainlookupname,lookupname),gref(markchar)) end - elseif trace_marks and trace_details then - logprocess("%s, mark %s is already bound (n=%s), ignoring mark2mark",pref(kind,lookupname),gref(markchar),alreadydone) - end +--~ elseif trace_marks and trace_details then +--~ logprocess("%s, mark %s is already bound (n=%s), ignoring mark2mark",pref(kind,lookupname),gref(markchar),alreadydone) +--~ end elseif trace_bugs then logwarning("%s: mark %s is no mark",cref(kind,chainname,chainlookupname),gref(markchar)) end @@ -1371,7 +1387,7 @@ function chainprocs.gpos_cursive(start,stop,kind,chainname,currentcontext,cache, end else -- if trace_bugs then -- logwarning("%s: char %s is missing in font",pref(kind,lookupname),gref(startchar)) - fonts.register_missing(currentfont,startchar) + fonts.register_message(currentfont,startchar,"no entry anchors") end break end @@ -1761,7 +1777,10 @@ otf.setcontextchain() local missing = { } -- we only report once local function logprocess(...) - logs.report("otf process",registermessage(...)) + if trace_steps then + registermessage(...) + end + logs.report("otf process",...) end local function logwarning(...) logs.report("otf process",...) @@ -1798,14 +1817,21 @@ function fonts.methods.node.otf.features(head,font,attr) local sequences = luatex.sequences lookuptable = luatex.lookups local done = false - local script, language, enabled - if attr and attr > 0 then + local script, language, s_enabled, a_enabled, dyn + local attribute_driven = attr and attr ~= 0 + if attribute_driven then local features = context_setups[context_numbers[attr]] -- could be a direct list + dyn = context_merged[attr] or 0 language, script = features.language or "dflt", features.script or "dflt" - enabled = features -- shared.features -- can be made local to the resolver + a_enabled = features -- shared.features -- can be made local to the resolver + if dyn == 2 or dyn == -2 then + -- font based + s_enabled = shared.features + end else language, script = tfmdata.language or "dflt", tfmdata.script or "dflt" - enabled = shared.features -- can be made local to the resolver + s_enabled = shared.features -- can be made local to the resolver + dyn = 0 end -- we can save some runtime by caching feature tests local res = resolved[font] if not res then res = { } resolved[font] = res end @@ -1817,44 +1843,62 @@ function fonts.methods.node.otf.features(head,font,attr) local success = false local sequence = sequences[s] local r = ra[s] -- cache - if not r then - local typ = sequence.type - -- we could save this in the tma/c file ---~ local chain ---~ if typ == "gsub_contextchain" or typ == "gpos_contextchain" then ---~ chain = 1 ---~ elseif typ == "gsub_reversecontextchain" or typ == "gpos_reversecontextchain" then ---~ chain = -1 ---~ else ---~ chain = 0 ---~ end -local chain = sequence.chain or 0 + if r == nil then + -- + -- this bit will move to font-ctx and become a function + --- + local chain = sequence.chain or 0 local features = sequence.features if not features then -- indirect lookup, part of chain (todo: make this a separate table) - r = { false, false, chain } + r = false -- { false, false, chain } else - local valid, attribute, kind = false, false + local valid, attribute, kind, what = false, false for k,v in next, features do - if enabled[k] then + -- we can quit earlier but for the moment we want the tracing + local s_e = s_enabled and s_enabled[k] + local a_e = a_enabled and a_enabled[k] + if s_e or a_e then local l = v[script] or v[wildcard] - if l and (l[language] or l[wildcard]) then - valid = true - kind = k - attribute = special_attributes[k] or false -- only first, so we assume simple fina's - break + if l then + -- not l[language] or l[default] or l[wildcard] because we want tracing + -- only first attribute match check, so we assume simple fina's + -- default can become a font feature itself + if l[language] then +--~ valid, what = true, language + valid, what = s_e or a_e, language + -- elseif l[default] then + -- valid, what = true, default + elseif l[wildcard] then +--~ valid, what = true, wildcard + valid, what = s_e or a_e, wildcard + end + if valid then + kind, attribute = k, special_attributes[k] or false + if a_e and dyn < 0 then + valid = false + end + if trace_applied then + local typ, action = match(sequence.type,"(.*)_(.*)") + logs.report("otf node mode", + "%s font: %03i, dynamic: %03i, kind: %s, lookup: %3i, script: %-4s, language: %-4s (%-4s), type: %s, action: %s, name: %s", + (valid and "+") or "-",font,attr or 0,kind,s,script,language,what,typ,action,sequence.name) + end + break + end end end end - if kind then + if valid then r = { valid, attribute, chain, kind } else - r = { valid, attribute, chain, "generic" } -- false anyway + r = false -- { valid, attribute, chain, "generic" } -- false anyway, could be flag instead of table end end ra[s] = r end - if r[1] then -- valid + featurevalue = r and r[1] -- todo: pass to function instead of using a global + if featurevalue then local attribute, chain, typ, subtables = r[2], r[3], sequence.type, sequence.subtables if chain < 0 then -- this is a limited case, no special treatments like 'init' etc @@ -1865,7 +1909,8 @@ local chain = sequence.chain or 0 while start do local id = start.id if id == glyph then - if start.subtype<256 and start.font == font and (not attr or has_attribute(start,0,attr)) then +--~ if start.subtype<256 and start.font == font and (not attr or has_attribute(start,0,attr)) then + if start.subtype<256 and start.font == font and has_attribute(start,0,attr) then for i=1,#subtables do local lookupname = subtables[i] local lookupcache = thecache[lookupname] @@ -1904,7 +1949,8 @@ local chain = sequence.chain or 0 while start do local id = start.id if id == glyph then - if start.font == font and start.subtype<256 and (not attr or has_attribute(start,0,attr)) and (not attribute or has_attribute(start,state,attribute)) then +--~ if start.font == font and start.subtype<256 and (not attr or has_attribute(start,0,attr)) and (not attribute or has_attribute(start,state,attribute)) then + if start.font == font and start.subtype<256 and has_attribute(start,0,attr) and (not attribute or has_attribute(start,state,attribute)) then local lookupmatch = lookupcache[start.char] if lookupmatch then -- sequence kan weg @@ -1965,7 +2011,8 @@ local chain = sequence.chain or 0 while start do local id = start.id if id == glyph then - if start.subtype<256 and start.font == font and (not attr or has_attribute(start,0,attr)) and (not attribute or has_attribute(start,state,attribute)) then +--~ if start.subtype<256 and start.font == font and (not attr or has_attribute(start,0,attr)) and (not attribute or has_attribute(start,state,attribute)) then + if start.subtype<256 and start.font == font and has_attribute(start,0,attr) and (not attribute or has_attribute(start,state,attribute)) then for i=1,ns do local lookupname = subtables[i] local lookupcache = thecache[lookupname] @@ -2034,9 +2081,9 @@ local chain = sequence.chain or 0 if success then done = true end ---~ if trace_steps then - registerstep(head) ---~ end + if trace_steps then -- ? + registerstep(head) + end end end return head, done diff --git a/otfl-font-ott.lua b/otfl-font-ott.lua new file mode 100644 index 0000000..6676ff6 --- /dev/null +++ b/otfl-font-ott.lua @@ -0,0 +1,935 @@ +if not modules then modules = { } end modules ['font-otf'] = { + version = 1.001, + comment = "companion to font-otf.lua (tables)", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +local type, next, tonumber, tostring = type, next, tonumber, tostring +local gsub, lower = string.gsub, string.lower + +fonts = fonts or { } +fonts.otf = fonts.otf or { } + +local otf = fonts.otf + +otf.tables = otf.tables or { } +otf.meanings = otf.meanings or { } + +otf.tables.scripts = { + ['dflt'] = 'Default', + + ['arab'] = 'Arabic', + ['armn'] = 'Armenian', + ['bali'] = 'Balinese', + ['beng'] = 'Bengali', + ['bopo'] = 'Bopomofo', + ['brai'] = 'Braille', + ['bugi'] = 'Buginese', + ['buhd'] = 'Buhid', + ['byzm'] = 'Byzantine Music', + ['cans'] = 'Canadian Syllabics', + ['cher'] = 'Cherokee', + ['copt'] = 'Coptic', + ['cprt'] = 'Cypriot Syllabary', + ['cyrl'] = 'Cyrillic', + ['deva'] = 'Devanagari', + ['dsrt'] = 'Deseret', + ['ethi'] = 'Ethiopic', + ['geor'] = 'Georgian', + ['glag'] = 'Glagolitic', + ['goth'] = 'Gothic', + ['grek'] = 'Greek', + ['gujr'] = 'Gujarati', + ['guru'] = 'Gurmukhi', + ['hang'] = 'Hangul', + ['hani'] = 'CJK Ideographic', + ['hano'] = 'Hanunoo', + ['hebr'] = 'Hebrew', + ['ital'] = 'Old Italic', + ['jamo'] = 'Hangul Jamo', + ['java'] = 'Javanese', + ['kana'] = 'Hiragana and Katakana', + ['khar'] = 'Kharosthi', + ['khmr'] = 'Khmer', + ['knda'] = 'Kannada', + ['lao' ] = 'Lao', + ['latn'] = 'Latin', + ['limb'] = 'Limbu', + ['linb'] = 'Linear B', + ['math'] = 'Mathematical Alphanumeric Symbols', + ['mlym'] = 'Malayalam', + ['mong'] = 'Mongolian', + ['musc'] = 'Musical Symbols', + ['mymr'] = 'Myanmar', + ['nko' ] = "N'ko", + ['ogam'] = 'Ogham', + ['orya'] = 'Oriya', + ['osma'] = 'Osmanya', + ['phag'] = 'Phags-pa', + ['phnx'] = 'Phoenician', + ['runr'] = 'Runic', + ['shaw'] = 'Shavian', + ['sinh'] = 'Sinhala', + ['sylo'] = 'Syloti Nagri', + ['syrc'] = 'Syriac', + ['tagb'] = 'Tagbanwa', + ['tale'] = 'Tai Le', + ['talu'] = 'Tai Lu', + ['taml'] = 'Tamil', + ['telu'] = 'Telugu', + ['tfng'] = 'Tifinagh', + ['tglg'] = 'Tagalog', + ['thaa'] = 'Thaana', + ['thai'] = 'Thai', + ['tibt'] = 'Tibetan', + ['ugar'] = 'Ugaritic Cuneiform', + ['xpeo'] = 'Old Persian Cuneiform', + ['xsux'] = 'Sumero-Akkadian Cuneiform', + ['yi' ] = 'Yi' +} + +otf.tables.languages = { + ['dflt'] = 'Default', + + ['aba'] = 'Abaza', + ['abk'] = 'Abkhazian', + ['ady'] = 'Adyghe', + ['afk'] = 'Afrikaans', + ['afr'] = 'Afar', + ['agw'] = 'Agaw', + ['als'] = 'Alsatian', + ['alt'] = 'Altai', + ['amh'] = 'Amharic', + ['ara'] = 'Arabic', + ['ari'] = 'Aari', + ['ark'] = 'Arakanese', + ['asm'] = 'Assamese', + ['ath'] = 'Athapaskan', + ['avr'] = 'Avar', + ['awa'] = 'Awadhi', + ['aym'] = 'Aymara', + ['aze'] = 'Azeri', + ['bad'] = 'Badaga', + ['bag'] = 'Baghelkhandi', + ['bal'] = 'Balkar', + ['bau'] = 'Baule', + ['bbr'] = 'Berber', + ['bch'] = 'Bench', + ['bcr'] = 'Bible Cree', + ['bel'] = 'Belarussian', + ['bem'] = 'Bemba', + ['ben'] = 'Bengali', + ['bgr'] = 'Bulgarian', + ['bhi'] = 'Bhili', + ['bho'] = 'Bhojpuri', + ['bik'] = 'Bikol', + ['bil'] = 'Bilen', + ['bkf'] = 'Blackfoot', + ['bli'] = 'Balochi', + ['bln'] = 'Balante', + ['blt'] = 'Balti', + ['bmb'] = 'Bambara', + ['bml'] = 'Bamileke', + ['bos'] = 'Bosnian', + ['bre'] = 'Breton', + ['brh'] = 'Brahui', + ['bri'] = 'Braj Bhasha', + ['brm'] = 'Burmese', + ['bsh'] = 'Bashkir', + ['bti'] = 'Beti', + ['cat'] = 'Catalan', + ['ceb'] = 'Cebuano', + ['che'] = 'Chechen', + ['chg'] = 'Chaha Gurage', + ['chh'] = 'Chattisgarhi', + ['chi'] = 'Chichewa', + ['chk'] = 'Chukchi', + ['chp'] = 'Chipewyan', + ['chr'] = 'Cherokee', + ['chu'] = 'Chuvash', + ['cmr'] = 'Comorian', + ['cop'] = 'Coptic', + ['cos'] = 'Corsican', + ['cre'] = 'Cree', + ['crr'] = 'Carrier', + ['crt'] = 'Crimean Tatar', + ['csl'] = 'Church Slavonic', + ['csy'] = 'Czech', + ['dan'] = 'Danish', + ['dar'] = 'Dargwa', + ['dcr'] = 'Woods Cree', + ['deu'] = 'German', + ['dgr'] = 'Dogri', + ['div'] = 'Divehi', + ['djr'] = 'Djerma', + ['dng'] = 'Dangme', + ['dnk'] = 'Dinka', + ['dri'] = 'Dari', + ['dun'] = 'Dungan', + ['dzn'] = 'Dzongkha', + ['ebi'] = 'Ebira', + ['ecr'] = 'Eastern Cree', + ['edo'] = 'Edo', + ['efi'] = 'Efik', + ['ell'] = 'Greek', + ['eng'] = 'English', + ['erz'] = 'Erzya', + ['esp'] = 'Spanish', + ['eti'] = 'Estonian', + ['euq'] = 'Basque', + ['evk'] = 'Evenki', + ['evn'] = 'Even', + ['ewe'] = 'Ewe', + ['fan'] = 'French Antillean', + ['far'] = 'Farsi', + ['fin'] = 'Finnish', + ['fji'] = 'Fijian', + ['fle'] = 'Flemish', + ['fne'] = 'Forest Nenets', + ['fon'] = 'Fon', + ['fos'] = 'Faroese', + ['fra'] = 'French', + ['fri'] = 'Frisian', + ['frl'] = 'Friulian', + ['fta'] = 'Futa', + ['ful'] = 'Fulani', + ['gad'] = 'Ga', + ['gae'] = 'Gaelic', + ['gag'] = 'Gagauz', + ['gal'] = 'Galician', + ['gar'] = 'Garshuni', + ['gaw'] = 'Garhwali', + ['gez'] = "Ge'ez", + ['gil'] = 'Gilyak', + ['gmz'] = 'Gumuz', + ['gon'] = 'Gondi', + ['grn'] = 'Greenlandic', + ['gro'] = 'Garo', + ['gua'] = 'Guarani', + ['guj'] = 'Gujarati', + ['hai'] = 'Haitian', + ['hal'] = 'Halam', + ['har'] = 'Harauti', + ['hau'] = 'Hausa', + ['haw'] = 'Hawaiin', + ['hbn'] = 'Hammer-Banna', + ['hil'] = 'Hiligaynon', + ['hin'] = 'Hindi', + ['hma'] = 'High Mari', + ['hnd'] = 'Hindko', + ['ho'] = 'Ho', + ['hri'] = 'Harari', + ['hrv'] = 'Croatian', + ['hun'] = 'Hungarian', + ['hye'] = 'Armenian', + ['ibo'] = 'Igbo', + ['ijo'] = 'Ijo', + ['ilo'] = 'Ilokano', + ['ind'] = 'Indonesian', + ['ing'] = 'Ingush', + ['inu'] = 'Inuktitut', + ['iri'] = 'Irish', + ['irt'] = 'Irish Traditional', + ['isl'] = 'Icelandic', + ['ism'] = 'Inari Sami', + ['ita'] = 'Italian', + ['iwr'] = 'Hebrew', + ['jan'] = 'Japanese', + ['jav'] = 'Javanese', + ['jii'] = 'Yiddish', + ['jud'] = 'Judezmo', + ['jul'] = 'Jula', + ['kab'] = 'Kabardian', + ['kac'] = 'Kachchi', + ['kal'] = 'Kalenjin', + ['kan'] = 'Kannada', + ['kar'] = 'Karachay', + ['kat'] = 'Georgian', + ['kaz'] = 'Kazakh', + ['keb'] = 'Kebena', + ['kge'] = 'Khutsuri Georgian', + ['kha'] = 'Khakass', + ['khk'] = 'Khanty-Kazim', + ['khm'] = 'Khmer', + ['khs'] = 'Khanty-Shurishkar', + ['khv'] = 'Khanty-Vakhi', + ['khw'] = 'Khowar', + ['kik'] = 'Kikuyu', + ['kir'] = 'Kirghiz', + ['kis'] = 'Kisii', + ['kkn'] = 'Kokni', + ['klm'] = 'Kalmyk', + ['kmb'] = 'Kamba', + ['kmn'] = 'Kumaoni', + ['kmo'] = 'Komo', + ['kms'] = 'Komso', + ['knr'] = 'Kanuri', + ['kod'] = 'Kodagu', + ['koh'] = 'Korean Old Hangul', + ['kok'] = 'Konkani', + ['kon'] = 'Kikongo', + ['kop'] = 'Komi-Permyak', + ['kor'] = 'Korean', + ['koz'] = 'Komi-Zyrian', + ['kpl'] = 'Kpelle', + ['kri'] = 'Krio', + ['krk'] = 'Karakalpak', + ['krl'] = 'Karelian', + ['krm'] = 'Karaim', + ['krn'] = 'Karen', + ['krt'] = 'Koorete', + ['ksh'] = 'Kashmiri', + ['ksi'] = 'Khasi', + ['ksm'] = 'Kildin Sami', + ['kui'] = 'Kui', + ['kul'] = 'Kulvi', + ['kum'] = 'Kumyk', + ['kur'] = 'Kurdish', + ['kuu'] = 'Kurukh', + ['kuy'] = 'Kuy', + ['kyk'] = 'Koryak', + ['lad'] = 'Ladin', + ['lah'] = 'Lahuli', + ['lak'] = 'Lak', + ['lam'] = 'Lambani', + ['lao'] = 'Lao', + ['lat'] = 'Latin', + ['laz'] = 'Laz', + ['lcr'] = 'L-Cree', + ['ldk'] = 'Ladakhi', + ['lez'] = 'Lezgi', + ['lin'] = 'Lingala', + ['lma'] = 'Low Mari', + ['lmb'] = 'Limbu', + ['lmw'] = 'Lomwe', + ['lsb'] = 'Lower Sorbian', + ['lsm'] = 'Lule Sami', + ['lth'] = 'Lithuanian', + ['ltz'] = 'Luxembourgish', + ['lub'] = 'Luba', + ['lug'] = 'Luganda', + ['luh'] = 'Luhya', + ['luo'] = 'Luo', + ['lvi'] = 'Latvian', + ['maj'] = 'Majang', + ['mak'] = 'Makua', + ['mal'] = 'Malayalam Traditional', + ['man'] = 'Mansi', + ['map'] = 'Mapudungun', + ['mar'] = 'Marathi', + ['maw'] = 'Marwari', + ['mbn'] = 'Mbundu', + ['mch'] = 'Manchu', + ['mcr'] = 'Moose Cree', + ['mde'] = 'Mende', + ['men'] = "Me'en", + ['miz'] = 'Mizo', + ['mkd'] = 'Macedonian', + ['mle'] = 'Male', + ['mlg'] = 'Malagasy', + ['mln'] = 'Malinke', + ['mlr'] = 'Malayalam Reformed', + ['mly'] = 'Malay', + ['mnd'] = 'Mandinka', + ['mng'] = 'Mongolian', + ['mni'] = 'Manipuri', + ['mnk'] = 'Maninka', + ['mnx'] = 'Manx Gaelic', + ['moh'] = 'Mohawk', + ['mok'] = 'Moksha', + ['mol'] = 'Moldavian', + ['mon'] = 'Mon', + ['mor'] = 'Moroccan', + ['mri'] = 'Maori', + ['mth'] = 'Maithili', + ['mts'] = 'Maltese', + ['mun'] = 'Mundari', + ['nag'] = 'Naga-Assamese', + ['nan'] = 'Nanai', + ['nas'] = 'Naskapi', + ['ncr'] = 'N-Cree', + ['ndb'] = 'Ndebele', + ['ndg'] = 'Ndonga', + ['nep'] = 'Nepali', + ['new'] = 'Newari', + ['ngr'] = 'Nagari', + ['nhc'] = 'Norway House Cree', + ['nis'] = 'Nisi', + ['niu'] = 'Niuean', + ['nkl'] = 'Nkole', + ['nko'] = "N'ko", + ['nld'] = 'Dutch', + ['nog'] = 'Nogai', + ['nor'] = 'Norwegian', + ['nsm'] = 'Northern Sami', + ['nta'] = 'Northern Tai', + ['nto'] = 'Esperanto', + ['nyn'] = 'Nynorsk', + ['oci'] = 'Occitan', + ['ocr'] = 'Oji-Cree', + ['ojb'] = 'Ojibway', + ['ori'] = 'Oriya', + ['oro'] = 'Oromo', + ['oss'] = 'Ossetian', + ['paa'] = 'Palestinian Aramaic', + ['pal'] = 'Pali', + ['pan'] = 'Punjabi', + ['pap'] = 'Palpa', + ['pas'] = 'Pashto', + ['pgr'] = 'Polytonic Greek', + ['pil'] = 'Pilipino', + ['plg'] = 'Palaung', + ['plk'] = 'Polish', + ['pro'] = 'Provencal', + ['ptg'] = 'Portuguese', + ['qin'] = 'Chin', + ['raj'] = 'Rajasthani', + ['rbu'] = 'Russian Buriat', + ['rcr'] = 'R-Cree', + ['ria'] = 'Riang', + ['rms'] = 'Rhaeto-Romanic', + ['rom'] = 'Romanian', + ['roy'] = 'Romany', + ['rsy'] = 'Rusyn', + ['rua'] = 'Ruanda', + ['rus'] = 'Russian', + ['sad'] = 'Sadri', + ['san'] = 'Sanskrit', + ['sat'] = 'Santali', + ['say'] = 'Sayisi', + ['sek'] = 'Sekota', + ['sel'] = 'Selkup', + ['sgo'] = 'Sango', + ['shn'] = 'Shan', + ['sib'] = 'Sibe', + ['sid'] = 'Sidamo', + ['sig'] = 'Silte Gurage', + ['sks'] = 'Skolt Sami', + ['sky'] = 'Slovak', + ['sla'] = 'Slavey', + ['slv'] = 'Slovenian', + ['sml'] = 'Somali', + ['smo'] = 'Samoan', + ['sna'] = 'Sena', + ['snd'] = 'Sindhi', + ['snh'] = 'Sinhalese', + ['snk'] = 'Soninke', + ['sog'] = 'Sodo Gurage', + ['sot'] = 'Sotho', + ['sqi'] = 'Albanian', + ['srb'] = 'Serbian', + ['srk'] = 'Saraiki', + ['srr'] = 'Serer', + ['ssl'] = 'South Slavey', + ['ssm'] = 'Southern Sami', + ['sur'] = 'Suri', + ['sva'] = 'Svan', + ['sve'] = 'Swedish', + ['swa'] = 'Swadaya Aramaic', + ['swk'] = 'Swahili', + ['swz'] = 'Swazi', + ['sxt'] = 'Sutu', + ['syr'] = 'Syriac', + ['tab'] = 'Tabasaran', + ['taj'] = 'Tajiki', + ['tam'] = 'Tamil', + ['tat'] = 'Tatar', + ['tcr'] = 'TH-Cree', + ['tel'] = 'Telugu', + ['tgn'] = 'Tongan', + ['tgr'] = 'Tigre', + ['tgy'] = 'Tigrinya', + ['tha'] = 'Thai', + ['tht'] = 'Tahitian', + ['tib'] = 'Tibetan', + ['tkm'] = 'Turkmen', + ['tmn'] = 'Temne', + ['tna'] = 'Tswana', + ['tne'] = 'Tundra Nenets', + ['tng'] = 'Tonga', + ['tod'] = 'Todo', + ['trk'] = 'Turkish', + ['tsg'] = 'Tsonga', + ['tua'] = 'Turoyo Aramaic', + ['tul'] = 'Tulu', + ['tuv'] = 'Tuvin', + ['twi'] = 'Twi', + ['udm'] = 'Udmurt', + ['ukr'] = 'Ukrainian', + ['urd'] = 'Urdu', + ['usb'] = 'Upper Sorbian', + ['uyg'] = 'Uyghur', + ['uzb'] = 'Uzbek', + ['ven'] = 'Venda', + ['vit'] = 'Vietnamese', + ['wa' ] = 'Wa', + ['wag'] = 'Wagdi', + ['wcr'] = 'West-Cree', + ['wel'] = 'Welsh', + ['wlf'] = 'Wolof', + ['xbd'] = 'Tai Lue', + ['xhs'] = 'Xhosa', + ['yak'] = 'Yakut', + ['yba'] = 'Yoruba', + ['ycr'] = 'Y-Cree', + ['yic'] = 'Yi Classic', + ['yim'] = 'Yi Modern', + ['zhh'] = 'Chinese Hong Kong', + ['zhp'] = 'Chinese Phonetic', + ['zhs'] = 'Chinese Simplified', + ['zht'] = 'Chinese Traditional', + ['znd'] = 'Zande', + ['zul'] = 'Zulu' +} + +otf.tables.features = { + ['aalt'] = 'Access All Alternates', + ['abvf'] = 'Above-Base Forms', + ['abvm'] = 'Above-Base Mark Positioning', + ['abvs'] = 'Above-Base Substitutions', + ['afrc'] = 'Alternative Fractions', + ['akhn'] = 'Akhands', + ['blwf'] = 'Below-Base Forms', + ['blwm'] = 'Below-Base Mark Positioning', + ['blws'] = 'Below-Base Substitutions', + ['c2pc'] = 'Petite Capitals From Capitals', + ['c2sc'] = 'Small Capitals From Capitals', + ['calt'] = 'Contextual Alternates', + ['case'] = 'Case-Sensitive Forms', + ['ccmp'] = 'Glyph Composition/Decomposition', + ['cjct'] = 'Conjunct Forms', + ['clig'] = 'Contextual Ligatures', + ['cpsp'] = 'Capital Spacing', + ['cswh'] = 'Contextual Swash', + ['curs'] = 'Cursive Positioning', + ['dflt'] = 'Default Processing', + ['dist'] = 'Distances', + ['dlig'] = 'Discretionary Ligatures', + ['dnom'] = 'Denominators', + ['dtls'] = 'Dotless Forms', -- math + ['expt'] = 'Expert Forms', + ['falt'] = 'Final glyph Alternates', + ['fin2'] = 'Terminal Forms #2', + ['fin3'] = 'Terminal Forms #3', + ['fina'] = 'Terminal Forms', + ['flac'] = 'Flattened Accents Over Capitals', -- math + ['frac'] = 'Fractions', + ['fwid'] = 'Full Width', + ['half'] = 'Half Forms', + ['haln'] = 'Halant Forms', + ['halt'] = 'Alternate Half Width', + ['hist'] = 'Historical Forms', + ['hkna'] = 'Horizontal Kana Alternates', + ['hlig'] = 'Historical Ligatures', + ['hngl'] = 'Hangul', + ['hojo'] = 'Hojo Kanji Forms', + ['hwid'] = 'Half Width', + ['init'] = 'Initial Forms', + ['isol'] = 'Isolated Forms', + ['ital'] = 'Italics', + ['jalt'] = 'Justification Alternatives', + ['jp04'] = 'JIS2004 Forms', + ['jp78'] = 'JIS78 Forms', + ['jp83'] = 'JIS83 Forms', + ['jp90'] = 'JIS90 Forms', + ['kern'] = 'Kerning', + ['lfbd'] = 'Left Bounds', + ['liga'] = 'Standard Ligatures', + ['ljmo'] = 'Leading Jamo Forms', + ['lnum'] = 'Lining Figures', + ['locl'] = 'Localized Forms', + ['mark'] = 'Mark Positioning', + ['med2'] = 'Medial Forms #2', + ['medi'] = 'Medial Forms', + ['mgrk'] = 'Mathematical Greek', + ['mkmk'] = 'Mark to Mark Positioning', + ['mset'] = 'Mark Positioning via Substitution', + ['nalt'] = 'Alternate Annotation Forms', + ['nlck'] = 'NLC Kanji Forms', + ['nukt'] = 'Nukta Forms', + ['numr'] = 'Numerators', + ['onum'] = 'Old Style Figures', + ['opbd'] = 'Optical Bounds', + ['ordn'] = 'Ordinals', + ['ornm'] = 'Ornaments', + ['palt'] = 'Proportional Alternate Width', + ['pcap'] = 'Petite Capitals', + ['pnum'] = 'Proportional Figures', + ['pref'] = 'Pre-base Forms', + ['pres'] = 'Pre-base Substitutions', + ['pstf'] = 'Post-base Forms', + ['psts'] = 'Post-base Substitutions', + ['pwid'] = 'Proportional Widths', + ['qwid'] = 'Quarter Widths', + ['rand'] = 'Randomize', + ['rkrf'] = 'Rakar Forms', + ['rlig'] = 'Required Ligatures', + ['rphf'] = 'Reph Form', + ['rtbd'] = 'Right Bounds', + ['rtla'] = 'Right-To-Left Alternates', + ['ruby'] = 'Ruby Notation Forms', + ['salt'] = 'Stylistic Alternates', + ['sinf'] = 'Scientific Inferiors', + ['size'] = 'Optical Size', + ['smcp'] = 'Small Capitals', + ['smpl'] = 'Simplified Forms', + ['ss01'] = 'Stylistic Set 1', + ['ss02'] = 'Stylistic Set 2', + ['ss03'] = 'Stylistic Set 3', + ['ss04'] = 'Stylistic Set 4', + ['ss05'] = 'Stylistic Set 5', + ['ss06'] = 'Stylistic Set 6', + ['ss07'] = 'Stylistic Set 7', + ['ss08'] = 'Stylistic Set 8', + ['ss09'] = 'Stylistic Set 9', + ['ss10'] = 'Stylistic Set 10', + ['ss11'] = 'Stylistic Set 11', + ['ss12'] = 'Stylistic Set 12', + ['ss13'] = 'Stylistic Set 13', + ['ss14'] = 'Stylistic Set 14', + ['ss15'] = 'Stylistic Set 15', + ['ss16'] = 'Stylistic Set 16', + ['ss17'] = 'Stylistic Set 17', + ['ss18'] = 'Stylistic Set 18', + ['ss19'] = 'Stylistic Set 19', + ['ss20'] = 'Stylistic Set 20', + ['ssty'] = 'Script Style', -- math + ['subs'] = 'Subscript', + ['sups'] = 'Superscript', + ['swsh'] = 'Swash', + ['titl'] = 'Titling', + ['tjmo'] = 'Trailing Jamo Forms', + ['tnam'] = 'Traditional Name Forms', + ['tnum'] = 'Tabular Figures', + ['trad'] = 'Traditional Forms', + ['twid'] = 'Third Widths', + ['unic'] = 'Unicase', + ['valt'] = 'Alternate Vertical Metrics', + ['vatu'] = 'Vattu Variants', + ['vert'] = 'Vertical Writing', + ['vhal'] = 'Alternate Vertical Half Metrics', + ['vjmo'] = 'Vowel Jamo Forms', + ['vkna'] = 'Vertical Kana Alternates', + ['vkrn'] = 'Vertical Kerning', + ['vpal'] = 'Proportional Alternate Vertical Metrics', + ['vrt2'] = 'Vertical Rotation', + ['zero'] = 'Slashed Zero', + + ['trep'] = 'Traditional TeX Replacements', + ['tlig'] = 'Traditional TeX Ligatures', +} + +otf.tables.baselines = { + ['hang'] = 'Hanging baseline', + ['icfb'] = 'Ideographic character face bottom edge baseline', + ['icft'] = 'Ideographic character face tope edige baseline', + ['ideo'] = 'Ideographic em-box bottom edge baseline', + ['idtp'] = 'Ideographic em-box top edge baseline', + ['math'] = 'Mathmatical centered baseline', + ['romn'] = 'Roman baseline' +} + +-- can be sped up by local tables + +function otf.tables.to_tag(id) + return stringformat("%4s",lower(id)) +end + +local function resolve(tab,id) + if tab and id then + id = lower(id) + return tab[id] or tab[gsub(id," ","")] or tab['dflt'] or '' + else + return "unknown" + end +end + +function otf.meanings.script(id) + return resolve(otf.tables.scripts,id) +end +function otf.meanings.language(id) + return resolve(otf.tables.languages,id) +end +function otf.meanings.feature(id) + return resolve(otf.tables.features,id) +end +function otf.meanings.baseline(id) + return resolve(otf.tables.baselines,id) +end + +otf.tables.to_scripts = table.reverse_hash(otf.tables.scripts ) +otf.tables.to_languages = table.reverse_hash(otf.tables.languages) +otf.tables.to_features = table.reverse_hash(otf.tables.features ) + +local scripts = otf.tables.scripts +local languages = otf.tables.languages +local features = otf.tables.features + +local to_scripts = otf.tables.to_scripts +local to_languages = otf.tables.to_languages +local to_features = otf.tables.to_features + +function otf.meanings.normalize(features) + local h = { } + for k,v in next, features do + k = lower(k) + if k == "language" or k == "lang" then + v = gsub(lower(v),"[^a-z0-9%-]","") + k = language + if not languages[v] then + h.language = to_languages[v] or "dflt" + else + h.language = v + end + elseif k == "script" then + v = gsub(lower(v),"[^a-z0-9%-]","") + if not scripts[v] then + h.script = to_scripts[v] or "dflt" + else + h.script = v + end + else + if type(v) == "string" then + local b = v:is_boolean() + if type(b) == "nil" then + v = tonumber(v) or lower(v) + else + v = b + end + end + h[to_features[k] or k] = v + end + end + return h +end + +-- When I feel the need ... + +--~ otf.tables.aat = { +--~ [ 0] = { +--~ name = "allTypographicFeaturesType", +--~ [ 0] = "allTypeFeaturesOnSelector", +--~ [ 1] = "allTypeFeaturesOffSelector", +--~ }, +--~ [ 1] = { +--~ name = "ligaturesType", +--~ [0 ] = "requiredLigaturesOnSelector", +--~ [1 ] = "requiredLigaturesOffSelector", +--~ [2 ] = "commonLigaturesOnSelector", +--~ [3 ] = "commonLigaturesOffSelector", +--~ [4 ] = "rareLigaturesOnSelector", +--~ [5 ] = "rareLigaturesOffSelector", +--~ [6 ] = "logosOnSelector ", +--~ [7 ] = "logosOffSelector ", +--~ [8 ] = "rebusPicturesOnSelector", +--~ [9 ] = "rebusPicturesOffSelector", +--~ [10] = "diphthongLigaturesOnSelector", +--~ [11] = "diphthongLigaturesOffSelector", +--~ [12] = "squaredLigaturesOnSelector", +--~ [13] = "squaredLigaturesOffSelector", +--~ [14] = "abbrevSquaredLigaturesOnSelector", +--~ [15] = "abbrevSquaredLigaturesOffSelector", +--~ }, +--~ [ 2] = { +--~ name = "cursiveConnectionType", +--~ [ 0] = "unconnectedSelector", +--~ [ 1] = "partiallyConnectedSelector", +--~ [ 2] = "cursiveSelector ", +--~ }, +--~ [ 3] = { +--~ name = "letterCaseType", +--~ [ 0] = "upperAndLowerCaseSelector", +--~ [ 1] = "allCapsSelector ", +--~ [ 2] = "allLowerCaseSelector", +--~ [ 3] = "smallCapsSelector ", +--~ [ 4] = "initialCapsSelector", +--~ [ 5] = "initialCapsAndSmallCapsSelector", +--~ }, +--~ [ 4] = { +--~ name = "verticalSubstitutionType", +--~ [ 0] = "substituteVerticalFormsOnSelector", +--~ [ 1] = "substituteVerticalFormsOffSelector", +--~ }, +--~ [ 5] = { +--~ name = "linguisticRearrangementType", +--~ [ 0] = "linguisticRearrangementOnSelector", +--~ [ 1] = "linguisticRearrangementOffSelector", +--~ }, +--~ [ 6] = { +--~ name = "numberSpacingType", +--~ [ 0] = "monospacedNumbersSelector", +--~ [ 1] = "proportionalNumbersSelector", +--~ }, +--~ [ 7] = { +--~ name = "appleReserved1Type", +--~ }, +--~ [ 8] = { +--~ name = "smartSwashType", +--~ [ 0] = "wordInitialSwashesOnSelector", +--~ [ 1] = "wordInitialSwashesOffSelector", +--~ [ 2] = "wordFinalSwashesOnSelector", +--~ [ 3] = "wordFinalSwashesOffSelector", +--~ [ 4] = "lineInitialSwashesOnSelector", +--~ [ 5] = "lineInitialSwashesOffSelector", +--~ [ 6] = "lineFinalSwashesOnSelector", +--~ [ 7] = "lineFinalSwashesOffSelector", +--~ [ 8] = "nonFinalSwashesOnSelector", +--~ [ 9] = "nonFinalSwashesOffSelector", +--~ }, +--~ [ 9] = { +--~ name = "diacriticsType", +--~ [ 0] = "showDiacriticsSelector", +--~ [ 1] = "hideDiacriticsSelector", +--~ [ 2] = "decomposeDiacriticsSelector", +--~ }, +--~ [10] = { +--~ name = "verticalPositionType", +--~ [ 0] = "normalPositionSelector", +--~ [ 1] = "superiorsSelector ", +--~ [ 2] = "inferiorsSelector ", +--~ [ 3] = "ordinalsSelector ", +--~ }, +--~ [11] = { +--~ name = "fractionsType", +--~ [ 0] = "noFractionsSelector", +--~ [ 1] = "verticalFractionsSelector", +--~ [ 2] = "diagonalFractionsSelector", +--~ }, +--~ [12] = { +--~ name = "appleReserved2Type", +--~ }, +--~ [13] = { +--~ name = "overlappingCharactersType", +--~ [ 0] = "preventOverlapOnSelector", +--~ [ 1] = "preventOverlapOffSelector", +--~ }, +--~ [14] = { +--~ name = "typographicExtrasType", +--~ [0 ] = "hyphensToEmDashOnSelector", +--~ [1 ] = "hyphensToEmDashOffSelector", +--~ [2 ] = "hyphenToEnDashOnSelector", +--~ [3 ] = "hyphenToEnDashOffSelector", +--~ [4 ] = "unslashedZeroOnSelector", +--~ [5 ] = "unslashedZeroOffSelector", +--~ [6 ] = "formInterrobangOnSelector", +--~ [7 ] = "formInterrobangOffSelector", +--~ [8 ] = "smartQuotesOnSelector", +--~ [9 ] = "smartQuotesOffSelector", +--~ [10] = "periodsToEllipsisOnSelector", +--~ [11] = "periodsToEllipsisOffSelector", +--~ }, +--~ [15] = { +--~ name = "mathematicalExtrasType", +--~ [ 0] = "hyphenToMinusOnSelector", +--~ [ 1] = "hyphenToMinusOffSelector", +--~ [ 2] = "asteriskToMultiplyOnSelector", +--~ [ 3] = "asteriskToMultiplyOffSelector", +--~ [ 4] = "slashToDivideOnSelector", +--~ [ 5] = "slashToDivideOffSelector", +--~ [ 6] = "inequalityLigaturesOnSelector", +--~ [ 7] = "inequalityLigaturesOffSelector", +--~ [ 8] = "exponentsOnSelector", +--~ [ 9] = "exponentsOffSelector", +--~ }, +--~ [16] = { +--~ name = "ornamentSetsType", +--~ [ 0] = "noOrnamentsSelector", +--~ [ 1] = "dingbatsSelector ", +--~ [ 2] = "piCharactersSelector", +--~ [ 3] = "fleuronsSelector ", +--~ [ 4] = "decorativeBordersSelector", +--~ [ 5] = "internationalSymbolsSelector", +--~ [ 6] = "mathSymbolsSelector", +--~ }, +--~ [17] = { +--~ name = "characterAlternativesType", +--~ [ 0] = "noAlternatesSelector", +--~ }, +--~ [18] = { +--~ name = "designComplexityType", +--~ [ 0] = "designLevel1Selector", +--~ [ 1] = "designLevel2Selector", +--~ [ 2] = "designLevel3Selector", +--~ [ 3] = "designLevel4Selector", +--~ [ 4] = "designLevel5Selector", +--~ }, +--~ [19] = { +--~ name = "styleOptionsType", +--~ [ 0] = "noStyleOptionsSelector", +--~ [ 1] = "displayTextSelector", +--~ [ 2] = "engravedTextSelector", +--~ [ 3] = "illuminatedCapsSelector", +--~ [ 4] = "titlingCapsSelector", +--~ [ 5] = "tallCapsSelector ", +--~ }, +--~ [20] = { +--~ name = "characterShapeType", +--~ [0 ] = "traditionalCharactersSelector", +--~ [1 ] = "simplifiedCharactersSelector", +--~ [2 ] = "jis1978CharactersSelector", +--~ [3 ] = "jis1983CharactersSelector", +--~ [4 ] = "jis1990CharactersSelector", +--~ [5 ] = "traditionalAltOneSelector", +--~ [6 ] = "traditionalAltTwoSelector", +--~ [7 ] = "traditionalAltThreeSelector", +--~ [8 ] = "traditionalAltFourSelector", +--~ [9 ] = "traditionalAltFiveSelector", +--~ [10] = "expertCharactersSelector", +--~ }, +--~ [21] = { +--~ name = "numberCaseType", +--~ [ 0] = "lowerCaseNumbersSelector", +--~ [ 1] = "upperCaseNumbersSelector", +--~ }, +--~ [22] = { +--~ name = "textSpacingType", +--~ [ 0] = "proportionalTextSelector", +--~ [ 1] = "monospacedTextSelector", +--~ [ 2] = "halfWidthTextSelector", +--~ [ 3] = "normallySpacedTextSelector", +--~ }, +--~ [23] = { +--~ name = "transliterationType", +--~ [ 0] = "noTransliterationSelector", +--~ [ 1] = "hanjaToHangulSelector", +--~ [ 2] = "hiraganaToKatakanaSelector", +--~ [ 3] = "katakanaToHiraganaSelector", +--~ [ 4] = "kanaToRomanizationSelector", +--~ [ 5] = "romanizationToHiraganaSelector", +--~ [ 6] = "romanizationToKatakanaSelector", +--~ [ 7] = "hanjaToHangulAltOneSelector", +--~ [ 8] = "hanjaToHangulAltTwoSelector", +--~ [ 9] = "hanjaToHangulAltThreeSelector", +--~ }, +--~ [24] = { +--~ name = "annotationType", +--~ [ 0] = "noAnnotationSelector", +--~ [ 1] = "boxAnnotationSelector", +--~ [ 2] = "roundedBoxAnnotationSelector", +--~ [ 3] = "circleAnnotationSelector", +--~ [ 4] = "invertedCircleAnnotationSelector", +--~ [ 5] = "parenthesisAnnotationSelector", +--~ [ 6] = "periodAnnotationSelector", +--~ [ 7] = "romanNumeralAnnotationSelector", +--~ [ 8] = "diamondAnnotationSelector", +--~ }, +--~ [25] = { +--~ name = "kanaSpacingType", +--~ [ 0] = "fullWidthKanaSelector", +--~ [ 1] = "proportionalKanaSelector", +--~ }, +--~ [26] = { +--~ name = "ideographicSpacingType", +--~ [ 0] = "fullWidthIdeographsSelector", +--~ [ 1] = "proportionalIdeographsSelector", +--~ }, +--~ [103] = { +--~ name = "cjkRomanSpacingType", +--~ [ 0] = "halfWidthCJKRomanSelector", +--~ [ 1] = "proportionalCJKRomanSelector", +--~ [ 2] = "defaultCJKRomanSelector", +--~ [ 3] = "fullWidthCJKRomanSelector", +--~ }, +--~ } diff --git a/otfl-font-tfm.lua b/otfl-font-tfm.lua index ab8a4db..9210fee 100644 --- a/otfl-font-tfm.lua +++ b/otfl-font-tfm.lua @@ -101,10 +101,8 @@ function tfm.read_from_tfm(specification) end tfm.enhance(tfmdata,specification) end - else - if trace_defining then - logs.report("define font","loading tfm with name %s fails",specification.name) - end + elseif trace_defining then + logs.report("define font","loading tfm with name %s fails",specification.name) end return tfmdata end @@ -226,6 +224,10 @@ end local charactercache = { } +-- The scaler is only used for otf and afm and virtual fonts. If +-- a virtual font has italic correction make sur eto set the +-- has_italic flag. Some more flags will be added in the future. + function tfm.do_scale(tfmtable, scaledpoints) tfm.prepare_base_kerns(tfmtable) -- optimalization if scaledpoints < 0 then @@ -246,6 +248,7 @@ function tfm.do_scale(tfmtable, scaledpoints) local hasmath = tfmtable.math_parameters ~= nil or tfmtable.MathConstants ~= nil local nodemode = tfmtable.mode == "node" local hasquality = tfmtable.auto_expand or tfmtable.auto_protrude + local hasitalic = tfmtable.has_italic -- t.parameters = { } t.characters = { } @@ -372,9 +375,11 @@ function tfm.do_scale(tfmtable, scaledpoints) end end -- todo: hasitalic - local vi = description.italic or v.italic - if vi then - chr.italic = vi*delta + if hasitalic then + local vi = description.italic or v.italic + if vi and vi ~= 0 then + chr.italic = vi*delta + end end -- to be tested if hasmath then @@ -531,10 +536,14 @@ function tfm.do_scale(tfmtable, scaledpoints) if not tp[22] then tp[22] = 0 end -- mathaxisheight if t.MathConstants then t.MathConstants.AccentBaseHeight = nil end -- safeguard t.tounicode = 1 + t.cidinfo = tfmtable.cidinfo -- we have t.name=metricfile and t.fullname=RealName and t.filename=diskfilename -- when collapsing fonts, luatex looks as both t.name and t.fullname as ttc files -- can have multiple subfonts --~ collectgarbage("collect") +--~ t.fontname = t.fontname or t.fullname +--~ t.name = t.name or t.fontname +--~ print(t.fullname,table.serialize(characters[string.byte('W')].kerns)) return t, delta end diff --git a/otfl-font-xtx.lua b/otfl-font-xtx.lua index 5672068..63e421f 100644 --- a/otfl-font-xtx.lua +++ b/otfl-font-xtx.lua @@ -61,17 +61,18 @@ local list = { } fonts.define.specify.colonized_default_lookup = "file" -local function issome() list.lookup = fonts.define.specify.colonized_default_lookup end -local function isfile() list.lookup = 'file' end -local function isname() list.lookup = 'name' end -local function thename(s) list.name = s end -local function iscrap(s) list.crap = string.lower(s) end -local function istrue(s) list[s] = 'yes' end -local function isfalse(s) list[s] = 'no' end -local function iskey(k,v) list[k] = v end +local function issome () list.lookup = fonts.define.specify.colonized_default_lookup end +local function isfile () list.lookup = 'file' end +local function isname () list.lookup = 'name' end +local function thename(s) list.name = s end +local function issub (v) list.sub = v end +local function iscrap (s) list.crap = string.lower(s) end +local function istrue (s) list[s] = 'yes' end +local function isfalse(s) list[s] = 'no' end +local function iskey (k,v) list[k] = v end local spaces = lpeg.P(" ")^0 -local namespec = (1-lpeg.S("/: "))^0 +local namespec = (1-lpeg.S("/: ("))^0 local crapspec = spaces * lpeg.P("/") * (((1-lpeg.P(":"))^0)/iscrap) * spaces local filename = (lpeg.P("file:")/isfile * (namespec/thename)) + (lpeg.P("[") * lpeg.P(true)/isname * (((1-lpeg.P("]"))^0)/thename) * lpeg.P("]")) local fontname = (lpeg.P("name:")/isname * (namespec/thename)) + lpeg.P(true)/issome * (namespec/thename) @@ -80,9 +81,10 @@ local truevalue = lpeg.P("+") * spaces * (sometext/istrue) local falsevalue = lpeg.P("-") * spaces * (sometext/isfalse) local keyvalue = (lpeg.C(sometext) * spaces * lpeg.P("=") * spaces * lpeg.C(sometext))/iskey local somevalue = sometext/istrue +local subvalue = lpeg.P("(") * (lpeg.C(lpeg.P(1-lpeg.S("()"))^1)/issub) * lpeg.P(")") -- for Kim local option = spaces * (keyvalue + falsevalue + truevalue + somevalue) * spaces local options = lpeg.P(":") * spaces * (lpeg.P(";")^0 * option)^0 -local pattern = (filename + fontname) * crapspec^0 * options^0 +local pattern = (filename + fontname) * subvalue^0 * crapspec^0 * options^0 function fonts.define.specify.colonized(specification) -- xetex mode list = { } @@ -102,6 +104,10 @@ function fonts.define.specify.colonized(specification) -- xetex mode specification.lookup = list.lookup list.lookup = nil end + if list.sub then + specification.sub = list.sub + list.sub = nil + end specification.features.normal = list return specification end diff --git a/otfl-luat-dum.lua b/otfl-luat-dum.lua index 4d7d9c5..f2ff505 100644 --- a/otfl-luat-dum.lua +++ b/otfl-luat-dum.lua @@ -20,6 +20,7 @@ trackers = { } storage = { register = dummyfunction, + shared = { }, } logs = { report = dummyfunction, @@ -41,12 +42,14 @@ resolvers = resolvers or { } -- no fancy file helpers used local remapper = { otf = "opentype fonts", ttf = "truetype fonts", - ttc = "truetype fonts" + ttc = "truetype fonts", + cid = "other text files", -- will become "cid files" } function resolvers.find_file(name,kind) name = string.gsub(name,"\\","\/") - return kpse.find_file(name,(kind ~= "" and kind) or "tex") + kind = string.lower(kind) + return kpse.find_file(name,(kind and kind ~= "" and (remapper[kind] or kind)) or "tex") end function resolvers.findbinfile(name,kind) diff --git a/otfl-node-fnt.lua b/otfl-node-fnt.lua index 52b5a24..3ad9060 100644 --- a/otfl-node-fnt.lua +++ b/otfl-node-fnt.lua @@ -33,6 +33,18 @@ local fontdata = fonts.ids -- happen often; we could consider processing sublists but that might need mor -- checking later on; the current approach also permits variants +if tex.attribute[0] < 0 then + + texio.write_nl("log","!") + texio.write_nl("log","! Attribute 0 is reserved for ConTeXt's font feature management and has to be") + texio.write_nl("log","! set to zero. Also, some attributes in the range 1-255 are used for special") + texio.write_nl("log","! purposed so setting them at the TeX end might break the font handler.") + texio.write_nl("log","!") + + tex.attribute[0] = 0 -- else no features + +end + function nodes.process_characters(head) -- either next or not, but definitely no already processed list starttiming(nodes) @@ -54,7 +66,7 @@ function nodes.process_characters(head) if shared then local dynamics = shared.dynamics if dynamics then - local d = shared.set_dynamics(font,dynamics,attr) + local d = shared.set_dynamics(font,dynamics,attr) -- still valid? if d then used[attr] = d a = a + 1 @@ -95,7 +107,7 @@ function nodes.process_characters(head) head, done = h or head, done or d if n > 1 then for i=2,n do - local h, d = processors[i](head,font,false) + local h, d = processors[i](head,font,0) -- false) head, done = h or head, done or d end end @@ -107,7 +119,7 @@ function nodes.process_characters(head) head, done = h or head, done or d if n > 1 then for i=2,n do - local h, d = processors[i](head,font,false) + local h, d = processors[i](head,font,0) -- false) head, done = h or head, done or d end end diff --git a/otfl-node-ini.lua b/otfl-node-ini.lua index aeeb689..8185e30 100644 --- a/otfl-node-ini.lua +++ b/otfl-node-ini.lua @@ -49,13 +49,15 @@ are only used when no attribute is set at the \TEX\ end which normally happens in .

--ldx]]-- -local last = 127 +storage.shared.attributes_last_private = storage.shared.attributes_last_private or 127 function attributes.private(name) -- at the lua end (hidden from user) local number = numbers[name] if not number then + local last = storage.shared.attributes_last_private or 127 if last < 255 then last = last + 1 + storage.shared.attributes_last_private = last end number = last numbers[name], names[number], list[number] = number, name, { } -- cgit v1.2.3