if not modules then modules = { } end modules ['good-ini'] = { version = 1.000, comment = "companion to font-lib.mkiv", author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", copyright = "PRAGMA ADE / ConTeXt Development Team", license = "see context related readme files" } -- depends on ctx local type, next = type, next local gmatch, find, topattern = string.gmatch, string.find, string.topattern local sortedhash, insert, contains = table.sortedhash, table.insert, table.contains local fonts = fonts local trace_goodies = false trackers.register("fonts.goodies", function(v) trace_goodies = v end) local trace_extensions = false trackers.register("fonts.goodies.extensions", function(v) trace_extensions = v end) local report_goodies = logs.reporter("fonts","goodies") local allocate = utilities.storage.allocate local implement = interfaces.implement local findfile = resolvers.findfile local formatters = string.formatters local otf = fonts.handlers.otf local afm = fonts.handlers.afm local tfm = fonts.handlers.tfm local registerotffeature = otf.features.register local registerafmfeature = afm.features.register local registertfmfeature = tfm.features.register local addotffeature = otf.enhancers.addfeature local fontgoodies = fonts.goodies or { } fonts.goodies = fontgoodies local data = fontgoodies.data or { } fontgoodies.data = data -- no allocate as we want to see what is there local list = fontgoodies.list or { } fontgoodies.list = list -- no allocate as we want to see what is there fontgoodies.suffixes = { "lfg", "lua" } -- lfg is context specific and should not be used elsewhere local contextsetups = fonts.specifiers.contextsetups function fontgoodies.report(what,trace,goodies) if trace_goodies or trace then local whatever = goodies[what] if whatever then report_goodies("goodie %a found in %a",what,goodies.name) end end end local function locate(filename) local suffixes = fontgoodies.suffixes for i=1,#suffixes do local suffix = suffixes[i] local fullname = findfile(file.addsuffix(filename,suffix)) if fullname and fullname ~= "" then return fullname end end end local function loadgoodies(filename) -- maybe a merge is better local goodies = data[filename] -- we assume no suffix is given if goodies ~= nil then -- found or tagged unfound elseif type(filename) == "string" then local fullname = locate(filename) if not fullname or fullname == "" then report_goodies("goodie file %a is not found (suffixes: % t)",filename,fontgoodies.suffixes) data[filename] = false -- signal for not found else goodies = dofile(fullname) or false if not goodies then report_goodies("goodie file %a is invalid",fullname) return nil elseif trace_goodies then report_goodies("goodie file %a is loaded",fullname) end goodies.name = goodies.name or "no name" for i=1,#list do local g = list[i] if trace_goodies then report_goodies("handling goodie %a",g[1]) end g[2](goodies) end goodies.initialized = true data[filename] = goodies end end return goodies end function fontgoodies.register(name,fnc,prepend) -- will be a proper sequencer for i=1,#list do local g = list[i] if g[1] == name then g[2] = fnc --overload return end end local g = { name, fnc } if prepend then insert(list,g,prepend == true and 1 or prepend) else insert(list,g) end end fontgoodies.load = loadgoodies if implement then implement { name = "loadfontgoodies", actions = loadgoodies, arguments = "string", overload = true, -- for now, permits new font loader } end -- register goodies file local function setgoodies(tfmdata,value) local goodies = tfmdata.goodies if not goodies then -- actually an error goodies = { } tfmdata.goodies = goodies end for filename in gmatch(value,"[^, ]+") do -- we need to check for duplicates local ok = loadgoodies(filename) if ok then if trace_goodies then report_goodies("assigning goodie %a",filename) end goodies[#goodies+1] = ok end end end -- featuresets local function flattenedfeatures(t,tt) -- first set value dominates if not tt then tt = { } end for i=1,#t do local ti = t[i] local ty = type(ti) if ty == "table" then flattenedfeatures(ti,tt) elseif ty == "string" then local set = contextsetups[ti] if set then for k, v in next, set do if k ~= "number" then tt[k] = v or nil end end else -- bad end elseif tt[ti] == nil then tt[ti] = true end end for k, v in next, t do if type(k) ~= "number" then -- not tonumber(k) if type(v) == "table" then flattenedfeatures(v,tt) elseif tt[k] == nil then tt[k] = v end end end return tt end -- fonts.features.flattened = flattenedfeatures local function prepare_features(goodies,name,set) if set then local ff = flattenedfeatures(set) local fullname = goodies.name .. "::" .. name local n, s = fonts.specifiers.presetcontext(fullname,"",ff) goodies.featuresets[name] = s -- set if trace_goodies then report_goodies("feature set %a gets number %a and name %a",name,n,fullname) end return n end end fontgoodies.prepare_features = prepare_features local function initialize(goodies) local featuresets = goodies.featuresets if featuresets then if trace_goodies then report_goodies("checking featuresets in %a",goodies.name) end for name, set in next, featuresets do prepare_features(goodies,name,set) end end end fontgoodies.register("featureset",initialize) local function setfeatureset(tfmdata,set,features) local goodies = tfmdata.goodies -- shared ? if goodies then local properties = tfmdata.properties local what for i=1,#goodies do -- last one wins local g = goodies[i] what = g.featuresets and g.featuresets[set] or what end if what then for feature, value in next, what do if features[feature] == nil then features[feature] = value end end properties.mode = what.mode or properties.mode end end end -- postprocessors (we could hash processor and share code) function fontgoodies.registerpostprocessor(tfmdata,f,prepend) local postprocessors = tfmdata.postprocessors if not postprocessors then tfmdata.postprocessors = { f } elseif prepend then insert(postprocessors,f,prepend == true and 1 or prepend) else insert(postprocessors,f) end end local function setpostprocessor(tfmdata,processor) local goodies = tfmdata.goodies if goodies and type(processor) == "string" then local found = { } local asked = utilities.parsers.settings_to_array(processor) for i=1,#goodies do local g = goodies[i] local p = g.postprocessors if p then for i=1,#asked do local a = asked[i] local f = p[a] if type(f) == "function" then found[a] = f end end end end local postprocessors = tfmdata.postprocessors or { } for i=1,#asked do local a = asked[i] local f = found[a] if f then postprocessors[#postprocessors+1] = f end end if #postprocessors > 0 then tfmdata.postprocessors = postprocessors end end end local function setextrafeatures(tfmdata) local goodies = tfmdata.goodies if goodies then for i=1,#goodies do local g = goodies[i] local f = g.features if f then local rawdata = tfmdata.shared.rawdata local done = { } -- indexed for i=1,#f do local specification = f[i] local feature = specification.name if feature then addotffeature(rawdata,feature,specification) registerotffeature { name = feature, description = formatters["extra: %s"](feature) } end done[i] = true end -- hashed for feature, specification in sortedhash(f) do if not done[feature] then feature = specification.name or feature specification.name = feature addotffeature(rawdata,feature,specification) registerotffeature { name = feature, description = formatters["extra: %s"](feature) } end end end end end end local function setextensions(tfmdata) local goodies = tfmdata.goodies if goodies then local shared = tfmdata.shared local metadata = shared and shared.rawdata and shared.rawdata.metadata for i=1,#goodies do local g = goodies[i] local e = g.extensions if e then local goodie = g.name or "unknown" local fontname = metadata and metadata.fontname if trace_extensions then report_goodies("checking extensions for font %a",fontname or "unknown") end for i=1,#e do local entry = e[i] local fnames = entry.fonts local tnames = type(fnames) local valid = false if not fontname then valid = true elseif tnames == "table" then if fnames[fontname] then valid = true elseif #fnames > 0 and contains(fnames,fontname) then valid = true end elseif tnames == "string" then fnames = topattern(fnames) valid = find(fontname,fnames) and true else valid = true end if valid then local name = "extension-" .. i if trace_extensions then report_goodies("adding extension %a from %a for font %a",name,goodie,fontname or "unknown") end otf.enhancers.addfeature(tfmdata.shared.rawdata,name,entry) end end end end end end -- installation local goodies_specification = { name = "goodies", description = "goodies on top of built in features", initializers = { position = 1, base = setgoodies, node = setgoodies, } } registerotffeature(goodies_specification) registerafmfeature(goodies_specification) registertfmfeature(goodies_specification) -- maybe more of the following could be for type one too registerotffeature { name = "extrafeatures", description = "extra features", default = true, initializers = { position = 2, base = setextrafeatures, node = setextrafeatures, } } registerotffeature { name = "extensions", description = "extensions to features", default = true, initializers = { position = 2, base = setextensions, node = setextensions, } } registerotffeature { name = "featureset", description = "goodie feature set", initializers = { position = 3, base = setfeatureset, node = setfeatureset, } } registerotffeature { name = "postprocessor", description = "goodie postprocessor", initializers = { base = setpostprocessor, node = setpostprocessor, } }