From 479745e00677d933056cf25f1fc2e904a4b9c567 Mon Sep 17 00:00:00 2001 From: Khaled Hosny Date: Tue, 4 Oct 2011 01:15:57 +0200 Subject: Sync with ContTeXt beta 2011.10.03 12:59 Does not run yet. --- otfl-font-otb.lua | 787 +++++++++++++++++++++++++++++++++--------------------- 1 file changed, 490 insertions(+), 297 deletions(-) (limited to 'otfl-font-otb.lua') diff --git a/otfl-font-otb.lua b/otfl-font-otb.lua index 8ee39b8..b078ab3 100644 --- a/otfl-font-otb.lua +++ b/otfl-font-otb.lua @@ -5,59 +5,45 @@ if not modules then modules = { } end modules ['font-otb'] = { copyright = "PRAGMA ADE / ConTeXt Development Team", license = "see context related readme files" } - local concat = table.concat local format, gmatch, gsub, find, match, lower, strip = string.format, string.gmatch, string.gsub, string.find, string.match, string.lower, string.strip local type, next, tonumber, tostring = type, next, tonumber, tostring local lpegmatch = lpeg.match +local utfchar = utf.char + +local trace_baseinit = false trackers.register("otf.baseinit", function(v) trace_baseinit = v end) +local trace_singles = false trackers.register("otf.singles", function(v) trace_singles = v end) +local trace_multiples = false trackers.register("otf.multiples", function(v) trace_multiples = v end) +local trace_alternatives = false trackers.register("otf.alternatives", function(v) trace_alternatives = v end) +local trace_ligatures = false trackers.register("otf.ligatures", function(v) trace_ligatures = v end) +local trace_kerns = false trackers.register("otf.kerns", function(v) trace_kerns = v end) +local trace_preparing = false trackers.register("otf.preparing", function(v) trace_preparing = v end) -local fonts = fonts -local otf = fonts.otf -local tfm = fonts.tfm +local report_prepare = logs.reporter("fonts","otf prepare") -local trace_baseinit = false trackers.register("otf.baseinit", function(v) trace_baseinit = v end) -local trace_singles = false trackers.register("otf.singles", function(v) trace_singles = v end) -local trace_multiples = false trackers.register("otf.multiples", function(v) trace_multiples = v end) -local trace_alternatives = false trackers.register("otf.alternatives", function(v) trace_alternatives = v end) -local trace_ligatures = false trackers.register("otf.ligatures", function(v) trace_ligatures = v end) -local trace_kerns = false trackers.register("otf.kerns", function(v) trace_kerns = v end) -local trace_preparing = false trackers.register("otf.preparing", function(v) trace_preparing = v end) +local fonts = fonts +local otf = fonts.handlers.otf -local report_prepare = logs.reporter("fonts","otf prepare") +local otffeatures = fonts.constructors.newfeatures("otf") +local registerotffeature = otffeatures.register local wildcard = "*" local default = "dflt" -local split_at_space = lpeg.Ct(lpeg.splitat(" ")) -- no trailing or multiple spaces anyway - -local pcache, fcache = { }, { } -- could be weak - local function gref(descriptions,n) if type(n) == "number" then local name = descriptions[n].name if name then - return format("U+%04X (%s)",n,name) + return format("U+%05X (%s)",n,name) else - return format("U+%04X") + return format("U+%05X") end elseif n then local num, nam = { }, { } - for i=1,#n do + for i=2,#n do -- first is likely a key local ni = n[i] - -- ! ! ! could be a helper ! ! ! - if type(ni) == "table" then - local nnum, nnam = { }, { } - for j=1,#ni do - local nj = ni[j] - nnum[j] = format("U+%04X",nj) - nnam[j] = descriptions[nj].name or "?" - end - num[i] = concat(nnum,"|") - nam[i] = concat(nnam,"|") - else - num[i] = format("U+%04X",ni) - nam[i] = descriptions[ni].name or "?" - end + num[i] = format("U+%05X",ni) + nam[i] = descriptions[ni].name or "?" end return format("%s (%s)",concat(num," "), concat(nam," ")) else @@ -65,324 +51,531 @@ local function gref(descriptions,n) end end -local function cref(kind,lookupname) +local function cref(feature,lookupname) if lookupname then - return format("feature %s, lookup %s",kind,lookupname) + return format("feature %s, lookup %s",feature,lookupname) else - return format("feature %s",kind) + return format("feature %s",feature) end end -local function resolve_ligatures(tfmdata,ligatures,kind) - kind = kind or "unknown" - local unicodes = tfmdata.unicodes - local characters = tfmdata.characters - local descriptions = tfmdata.descriptions - local changed = tfmdata.changed - local done = { } - while true do - local ok = false - for k,v in next, ligatures do - local lig = v[1] - if not done[lig] then - local ligs = lpegmatch(split_at_space,lig) - if #ligs == 2 then - local uc = v[2] - local c, f, s = characters[uc], ligs[1], ligs[2] - local uft, ust = unicodes[f] or 0, unicodes[s] or 0 - if not uft or not ust then - report_prepare("%s: unicode problem with base ligature %s = %s + %s",cref(kind),gref(descriptions,uc),gref(descriptions,uft),gref(descriptions,ust)) - -- some kind of error - else - if type(uft) == "number" then uft = { uft } end - if type(ust) == "number" then ust = { ust } end - for ufi=1,#uft do - local uf = uft[ufi] - for usi=1,#ust do - local us = ust[usi] - if changed[uf] or changed[us] then - if trace_baseinit and trace_ligatures then - report_prepare("%s: base ligature %s + %s ignored",cref(kind),gref(descriptions,uf),gref(descriptions,us)) - end - else - local first, second = characters[uf], us - if first and second then - local t = first.ligatures - if not t then - t = { } - first.ligatures = t - end - if type(uc) == "number" then - t[second] = { type = 0, char = uc } - else - t[second] = { type = 0, char = uc[1] } -- can this still happen? - end - if trace_baseinit and trace_ligatures then - report_prepare("%s: base ligature %s + %s => %s",cref(kind),gref(descriptions,uf),gref(descriptions,us),gref(descriptions,uc)) - end - end +local basemethods = { } +local basemethod = "" + +local function applybasemethod(what,...) + local m = basemethods[basemethod][what] + if m then + return m(...) + end +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. + +local basehash, basehashes, applied = { }, 1, { } + +local function registerbasehash(tfmdata) + local properties = tfmdata.properties + local hash = concat(applied," ") + local base = basehash[hash] + if not base then + basehashes = basehashes + 1 + base = basehashes + basehash[hash] = base + end + properties.basehash = base + properties.fullname = properties.fullname .. "-" .. base + -- report_prepare("fullname base hash: '%s', featureset '%s'",tfmdata.properties.fullname,hash) + applied = { } +end + +local function registerbasefeature(feature,value) + applied[#applied+1] = feature .. "=" .. tostring(value) +end + +-- The original basemode ligature builder used the names of components +-- and did some expression juggling to get the chain right. The current +-- variant starts with unicodes but still uses names to make the chain. +-- This is needed because we have to create intermediates when needed +-- but use predefined snippets when available. To some extend the +-- current builder is more stupid but I don't worry that much about it +-- as ligatures are rather predicatable. +-- +-- Personally I think that an ff + i == ffi rule as used in for instance +-- latin modern is pretty weird as no sane person will key that in and +-- expect a glyph for that ligature plus the following character. Anyhow, +-- as we need to deal with this, we do, but no guarantes are given. +-- +-- latin modern dejavu +-- +-- f+f 102 102 102 102 +-- f+i 102 105 102 105 +-- f+l 102 108 102 108 +-- f+f+i 102 102 105 +-- f+f+l 102 102 108 102 102 108 +-- ff+i 64256 105 64256 105 +-- ff+l 64256 108 +-- +-- As you can see here, latin modern is less complete than dejavu but +-- in practice one will not notice it. +-- +-- The while loop is needed because we need to resolve for instance +-- pseudo names like hyphen_hyphen to endash so in practice we end +-- up with a bit too many definitions but the overhead is neglectable. +-- +-- Todo: if changed[first] or changed[second] then ... end + +local trace = false + +local function finalize_ligatures(tfmdata,ligatures) + local nofligatures = #ligatures + if nofligatures > 0 then + local characters = tfmdata.characters + local descriptions = tfmdata.descriptions + local resources = tfmdata.resources + local unicodes = resources.unicodes + local private = resources.private + local alldone = false + while not alldone do + local done = 0 + for i=1,nofligatures do + local ligature = ligatures[i] + if ligature then + local unicode, lookupdata = ligature[1], ligature[2] + if trace then + print("BUILDING",concat(lookupdata," "),unicode) + end + local size = #lookupdata + local firstcode = lookupdata[1] -- [2] + local firstdata = characters[firstcode] + local okay = false + if firstdata then + local firstname = "ctx_" .. firstcode + for i=1,size-1 do -- for i=2,size-1 do + local firstdata = characters[firstcode] + if not firstdata then + firstcode = private + if trace then + print(" DEFINING",firstname,firstcode) + end + unicodes[firstname] = firstcode + firstdata = { intermediate = true, ligatures = { } } + characters[firstcode] = firstdata + descriptions[firstcode] = { name = firstname } + private = private + 1 + end + local target + local secondcode = lookupdata[i+1] + local secondname = firstname .. "_" .. secondcode + if i == size - 1 then + target = unicode + if not unicodes[secondname] then + unicodes[secondname] = unicode -- map final ligature onto intermediates + end + okay = true + else + target = unicodes[secondname] + if not target then + break end end + if trace then + print("CODES",firstname,firstcode,secondname,secondcode,target) + end + local firstligs = firstdata.ligatures + if firstligs then + firstligs[secondcode] = { char = target } + else + firstdata.ligatures = { [secondcode] = { char = target } } + end + firstcode = target + firstname = secondname end end - ok, done[lig] = true, descriptions[uc].name + if okay then + ligatures[i] = false + done = done + 1 + end end end + alldone = done == 0 end - if ok then - -- done has "a b c" = "a_b_c" and ligatures the already set ligatures: "a b" = 123 - -- and here we add extras (f i i = fi + i and alike) - -- - -- we could use a hash for fnc and pattern - -- - -- this might be interfering ! - for d,n in next, done do - local pattern = pcache[d] if not pattern then pattern = "^(" .. d .. ") " pcache[d] = pattern end - local fnc = fcache[n] if not fnc then fnc = function() return n .. " " end fcache[n] = fnc end - for k,v in next, ligatures do - v[1] = gsub(v[1],pattern,fnc) - end + if trace then + for k, v in next, characters do + if v.ligatures then table.print(v,k) end end - else - break end + tfmdata.resources.private = private end end -local splitter = lpeg.splitat(" ") - -local function prepare_base_substitutions(tfmdata,kind,value) -- we can share some code with the node features - if value then - local otfdata = tfmdata.shared.otfdata - local validlookups, lookuplist = otf.collectlookups(otfdata,kind,tfmdata.script,tfmdata.language) - if validlookups then - local ligatures = { } - local unicodes = tfmdata.unicodes -- names to unicodes - local indices = tfmdata.indices - local characters = tfmdata.characters - local descriptions = tfmdata.descriptions - local changed = tfmdata.changed - -- - local actions = { - substitution = function(p,lookup,k,glyph,unicode) - local pv = p[2] -- p.variant - if pv then - local upv = unicodes[pv] - if upv then - if type(upv) == "table" then -- zero change that table - upv = upv[1] - end - if characters[upv] then - if trace_baseinit and trace_singles then - report_prepare("%s: base substitution %s => %s",cref(kind,lookup),gref(descriptions,k),gref(descriptions,upv)) - end - changed[k] = upv - end - end - end - end, - alternate = function(p,lookup,k,glyph,unicode) - local pc = p[2] -- p.components - if pc then - -- a bit optimized ugliness - if value == 1 then - pc = lpegmatch(splitter,pc) - elseif value == 2 then - local a, b = lpegmatch(splitter,pc) - pc = b or a - else - pc = { lpegmatch(splitter,pc) } - pc = pc[value] or pc[#pc] - end - if pc then - local upc = unicodes[pc] - if upc then - if type(upc) == "table" then -- zero change that table - upc = upc[1] - end - if characters[upc] then - if trace_baseinit and trace_alternatives then - report_prepare("%s: base alternate %s %s => %s",cref(kind,lookup),tostring(value),gref(descriptions,k),gref(descriptions,upc)) - end - changed[k] = upc - end - end - end - end - end, - ligature = function(p,lookup,k,glyph,unicode) - local pc = p[2] - if pc then - if trace_baseinit and trace_ligatures then - local upc = { lpegmatch(splitter,pc) } - for i=1,#upc do upc[i] = unicodes[upc[i]] end - -- we assume that it's no table - report_prepare("%s: base ligature %s => %s",cref(kind,lookup),gref(descriptions,upc),gref(descriptions,k)) - end - ligatures[#ligatures+1] = { pc, k } - end - end, - } - -- - for k,c in next, characters do - local glyph = descriptions[k] - local lookups = glyph.slookups - if lookups then - for l=1,#lookuplist do - local lookup = lookuplist[l] - local p = lookups[lookup] - if p then - local a = actions[p[1]] - if a then - a(p,lookup,k,glyph,unicode) - end - end +local function preparesubstitutions(tfmdata,feature,value,validlookups,lookuplist) + local characters = tfmdata.characters + local descriptions = tfmdata.descriptions + local resources = tfmdata.resources + local changed = tfmdata.changed + local unicodes = resources.unicodes + local lookuphash = resources.lookuphash + local lookuptypes = resources.lookuptypes + + local ligatures = { } + + local actions = { + substitution = function(lookupdata,lookupname,description,unicode) + if trace_baseinit and trace_singles then + report_prepare("%s: base substitution %s => %s",cref(feature,lookupname), + gref(descriptions,unicode),gref(descriptions,lookupdatat)) + end + changed[unicode] = lookupdata + end, + alternate = function(lookupdata,lookupname,description,unicode) + local replacement = lookupdata[value] or lookupdata[#lookupdata] + if trace_baseinit and trace_alternatives then + report_prepare("%s: base alternate %s %s => %s",cref(feature,lookupname), + tostring(value),gref(descriptions,unicode),gref(descriptions,replacement)) + end + changed[unicode] = replacement + end, + ligature = function(lookupdata,lookupname,description,unicode) + if trace_baseinit and trace_alternatives then + report_prepare("%s: base ligature %s %s => %s",cref(feature,lookupname), + tostring(value),gref(descriptions,lookupdata),gref(descriptions,unicode)) + end + ligatures[#ligatures+1] = { unicode, lookupdata } + end, + } + + for unicode, character in next, characters do + local description = descriptions[unicode] + local lookups = description.slookups + if lookups then + for l=1,#lookuplist do + local lookupname = lookuplist[l] + local lookupdata = lookups[lookupname] + if lookupdata then + local lookuptype = lookuptypes[lookupname] + local action = actions[lookuptype] + if action then + action(lookupdata,lookupname,description,unicode) end end - local lookups = glyph.mlookups - if lookups then - for l=1,#lookuplist do - local lookup = lookuplist[l] - local ps = lookups[lookup] - if ps then - for i=1,#ps do - local p = ps[i] - local a = actions[p[1]] - if a then - a(p,lookup,k,glyph,unicode) - end - end + end + end + local lookups = description.mlookups + if lookups then + for l=1,#lookuplist do + local lookupname = lookuplist[l] + local lookuplist = lookups[lookupname] + if lookuplist then + local lookuptype = lookuptypes[lookupname] + local action = actions[lookuptype] + if action then + for i=1,#lookuplist do + action(lookuplist[i],lookupname,description,unicode) end end end end - resolve_ligatures(tfmdata,ligatures,kind) end - else - tfmdata.ligatures = tfmdata.ligatures or { } -- left over from what ? end + + finalize_ligatures(tfmdata,ligatures) end -local function preparebasekerns(tfmdata,kind,value) -- todo what kind of kerns, currently all - if value then - local otfdata = tfmdata.shared.otfdata - local validlookups, lookuplist = otf.collectlookups(otfdata,kind,tfmdata.script,tfmdata.language) - if validlookups then - local unicodes = tfmdata.unicodes -- names to unicodes - local indices = tfmdata.indices - local characters = tfmdata.characters - local descriptions = tfmdata.descriptions - local sharedkerns = { } - for u, chr in next, characters do - local d = descriptions[u] - if d then - local dk = d.kerns -- shared - if dk then - local s = sharedkerns[dk] - if s == false then - -- skip - elseif s then - chr.kerns = s - else - local t, done = chr.kerns or { }, false - for l=1,#lookuplist do - local lookup = lookuplist[l] - local kerns = dk[lookup] - if kerns then - for k, v in next, kerns do - if v ~= 0 and not t[k] then -- maybe no 0 test here - t[k], done = v, true - if trace_baseinit and trace_kerns then - report_prepare("%s: base kern %s + %s => %s",cref(kind,lookup),gref(descriptions,u),gref(descriptions,k),v) - end - end - end +local function preparepositionings(tfmdata,feature,value,validlookups,lookuplist) -- todo what kind of kerns, currently all + local characters = tfmdata.characters + local descriptions = tfmdata.descriptions + local resources = tfmdata.resources + local unicodes = resources.unicodes + local sharedkerns = { } + local traceindeed = trace_baseinit and trace_kerns + for unicode, character in next, characters do + local description = descriptions[unicode] + local rawkerns = description.kerns -- shared + if rawkerns then + local s = sharedkerns[rawkerns] + if s == false then + -- skip + elseif s then + character.kerns = s + else + local newkerns = character.kerns + local done = false + for l=1,#lookuplist do + local lookup = lookuplist[l] + local kerns = rawkerns[lookup] + if kerns then + for otherunicode, value in next, kerns do + if value == 0 then + -- maybe no 0 test here + elseif not newkerns then + newkerns = { [otherunicode] = value } + done = true + if traceindeed then + report_prepare("%s: base kern %s + %s => %s",cref(feature,lookup), + gref(descriptions,unicode),gref(descriptions,otherunicode),value) + end + elseif not newkerns[otherunicode] then -- first wins + newkerns[otherunicode] = value + done = true + if traceindeed then + report_prepare("%s: base kern %s + %s => %s",cref(feature,lookup), + gref(descriptions,unicode),gref(descriptions,otherunicode),value) end - end - if done then - sharedkerns[dk] = t - chr.kerns = t -- no empty assignments - else - sharedkerns[dk] = false end end end end + if done then + sharedkerns[rawkerns] = newkerns + character.kerns = newkerns -- no empty assignments + else + sharedkerns[rawkerns] = false + end end end end end --- In principle we could register each feature individually which was --- what we did in earlier versions. However, after the rewrite it --- made more sense to collect them in an overall features initializer --- just as with the node variant. There it was needed because we need --- to do complete mixed runs and not run featurewise (as we did before). - -local supported_gsub = { - 'liga', 'dlig', 'rlig', 'hlig', - 'pnum', 'onum', 'tnum', 'lnum', - 'zero', - 'smcp', 'cpsp', 'c2sc', 'ornm', 'aalt', - 'hwid', 'fwid', - 'ssty', 'rtlm', -- math --- 'tlig', 'trep', +basemethods.independent = { + preparesubstitutions = preparesubstitutions, + preparepositionings = preparepositionings, } -local supported_gpos = { - 'kern' -} +local function makefake(tfmdata,name,present) + local resources = tfmdata.resources + local private = resources.private + local character = { intermediate = true, ligatures = { } } + resources.unicodes[name] = private + tfmdata.characters[private] = character + tfmdata.descriptions[private] = { name = name } + resources.private = private + 1 + present[name] = private + return character +end + +local function make_1(present,tree,name) + for k, v in next, tree do + if k == "ligature" then + present[name] = v + else + make_1(present,v,name .. "_" .. k) + end + end +end + +local function make_2(present,tfmdata,characters,tree,name,preceding,unicode,done,lookupname) + for k, v in next, tree do + if k == "ligature" then + local character = characters[preceding] + if not character then + if trace_baseinit then + report_prepare("weird ligature in lookup %s: U+%05X (%s), preceding U+%05X (%s)",lookupname,v,utfchar(v),preceding,utfchar(preceding)) + end + character = makefake(tfmdata,name,present) + end + local ligatures = character.ligatures + if ligatures then + ligatures[unicode] = { char = v } + else + character.ligatures = { [unicode] = { char = v } } + end + if done then + local d = done[lookupname] + if not d then + done[lookupname] = { "dummy", v } + else + d[#d+1] = v + end + end + else + local code = present[name] or unicode + local name = name .. "_" .. k + make_2(present,tfmdata,characters,v,name,code,k,done,lookupname) + end + end +end + +local function preparesubstitutions(tfmdata,feature,value,validlookups,lookuplist) + local characters = tfmdata.characters + local descriptions = tfmdata.descriptions + local resources = tfmdata.resources + local changed = tfmdata.changed + local lookuphash = resources.lookuphash + local lookuptypes = resources.lookuptypes + + local ligatures = { } + + for l=1,#lookuplist do + local lookupname = lookuplist[l] + local lookupdata = lookuphash[lookupname] + local lookuptype = lookuptypes[lookupname] + for unicode, data in next, lookupdata do + if lookuptype == "substitution" then + if trace_baseinit and trace_singles then + report_prepare("%s: base substitution %s => %s",cref(feature,lookupname), + gref(descriptions,unicode),gref(descriptions,data)) + end + changed[unicode] = data + elseif lookuptype == "alternate" then + local replacement = data[value] or data[#data] + if trace_baseinit and trace_alternatives then + report_prepare("%s: base alternate %s %s => %s",cref(feature,lookupname), + tostring(value),gref(descriptions,unicode),gref(descriptions,replacement)) + end + changed[unicode] = replacement + elseif lookuptype == "ligature" then + ligatures[#ligatures+1] = { unicode, data, lookupname } + end + end + end + + local nofligatures = #ligatures + + if nofligatures > 0 then + + local characters = tfmdata.characters + local present = { } + local done = trace_baseinit and trace_ligatures and { } + + for i=1,nofligatures do + local ligature = ligatures[i] + local unicode, tree = ligature[1], ligature[2] + make_1(present,tree,"ctx_"..unicode) + end + + for i=1,nofligatures do + local ligature = ligatures[i] + local unicode, tree, lookupname = ligature[1], ligature[2], ligature[3] + make_2(present,tfmdata,characters,tree,"ctx_"..unicode,unicode,unicode,done,lookupname) + end + + if done then + for lookupname, list in next, done do + report_prepare("%s: base ligatures %s => %s",cref(feature,lookupname), + tostring(value),gref(descriptions,done)) + end + end + + end -function otf.features.registerbasesubstitution(tag) - supported_gsub[#supported_gsub+1] = tag end -function otf.features.registerbasekern(tag) - supported_gsub[#supported_gpos+1] = tag + +local function preparepositionings(tfmdata,feature,value,validlookups,lookuplist) + local characters = tfmdata.characters + local descriptions = tfmdata.descriptions + local resources = tfmdata.resources + local lookuphash = resources.lookuphash + local traceindeed = trace_baseinit and trace_kerns + + -- check out this sharedkerns trickery + + for l=1,#lookuplist do + local lookupname = lookuplist[l] + local lookupdata = lookuphash[lookupname] + for unicode, data in next, lookupdata do + local character = characters[unicode] + local kerns = character.kerns + if not kerns then + kerns = { } + character.kerns = kerns + end + if traceindeed then + for otherunicode, kern in next, data do + if not kerns[otherunicode] and kern ~= 0 then + kerns[otherunicode] = kern + report_prepare("%s: base kern %s + %s => %s",cref(feature,lookup), + gref(descriptions,unicode),gref(descriptions,otherunicode),kern) + end + end + else + for otherunicode, kern in next, data do + if not kerns[otherunicode] and kern ~= 0 then + kerns[otherunicode] = kern + end + end + end + end + end + +end + +local function initializehashes(tfmdata) + nodeinitializers.features(tfmdata) end -local basehash, basehashes = { }, 1 +basemethods.shared = { + initializehashes = initializehashes, + preparesubstitutions = preparesubstitutions, + preparepositionings = preparepositionings, +} + +basemethod = "independent" -function fonts.initializers.base.otf.features(tfmdata,value) +local function featuresinitializer(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] - local value = features[feature] - prepare_base_substitutions(tfmdata,feature,value) - if value then - h[#h+1] = feature .. "=" .. tostring(value) + applybasemethod("initializehashes",tfmdata) + local collectlookups = otf.collectlookups + local rawdata = tfmdata.shared.rawdata + local properties = tfmdata.properties + local script = properties.script + local language = properties.language + local basesubstitutions = rawdata.resources.features.gsub + local basepositionings = rawdata.resources.features.gpos + if basesubstitutions then + for feature, data in next, basesubstitutions do + local value = features[feature] + if value then + local validlookups, lookuplist = collectlookups(rawdata,feature,script,language) + if validlookups then + applybasemethod("preparesubstitutions",tfmdata,feature,value,validlookups,lookuplist) + registerbasefeature(feature,value) + end + end end end - for f=1,#supported_gpos do - local feature = supported_gpos[f] - local value = features[feature] - preparebasekerns(tfmdata,feature,features[feature]) - if value then - h[#h+1] = feature .. "=" .. tostring(value) + if basepositions then + for feature, data in next, basepositions do + local value = features[feature] + if value then + local validlookups, lookuplist = collectlookups(rawdata,feature,script,language) + if validlookups then + applybasemethod("preparepositionings",tfmdata,feature,features[feature],validlookups,lookuplist) + registerbasefeature(feature,value) + end + end 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 -- tfmdata.psname is the original - --~ report_prepare("fullname base hash: '%s', featureset '%s'",tfmdata.fullname,hash) + registerbasehash(tfmdata) end if trace_preparing then - report_prepare("preparation time is %0.3f seconds for %s",os.clock()-t,tfmdata.fullname or "?") + report_prepare("preparation time is %0.3f seconds for %s",os.clock()-t,tfmdata.properties.fullname or "?") end end end + +registerotffeature { + name = "features", + description = "features", + default = true, + initializers = { +--~ position = 1, -- after setscript (temp hack ... we need to force script / language to 1 + base = featuresinitializer, + } +} + +-- independent : collect lookups independently (takes more runtime ... neglectable) +-- shared : shares lookups with node mode (takes more memory ... noticeable) + +directives.register("fonts.otf.loader.basemethod", function(v) + if basemethods[v] then + basemethod = v + end +end) -- cgit v1.2.3