summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/tex/context/base/font-ctx.lua
diff options
context:
space:
mode:
Diffstat (limited to 'Master/texmf-dist/tex/context/base/font-ctx.lua')
-rw-r--r--Master/texmf-dist/tex/context/base/font-ctx.lua1159
1 files changed, 908 insertions, 251 deletions
diff --git a/Master/texmf-dist/tex/context/base/font-ctx.lua b/Master/texmf-dist/tex/context/base/font-ctx.lua
index 76e9f095a48..887aa99841e 100644
--- a/Master/texmf-dist/tex/context/base/font-ctx.lua
+++ b/Master/texmf-dist/tex/context/base/font-ctx.lua
@@ -6,34 +6,221 @@ if not modules then modules = { } end modules ['font-ctx'] = {
license = "see context related readme files"
}
--- needs a cleanup: merge of replace, lang/script etc
+-- At some point I will clean up the code here so that at the tex end
+-- the table interface is used.
-local texsprint, count, texsetcount = tex.sprint, tex.count, tex.setcount
-local format, concat, gmatch, match, find, lower, gsub, byte = string.format, table.concat, string.gmatch, string.match, string.find, string.lower, string.gsub, string.byte
+local texcount, texsetcount = tex.count, tex.setcount
+local format, gmatch, match, find, lower, gsub, byte = string.format, string.gmatch, string.match, string.find, string.lower, string.gsub, string.byte
+local concat, serialize, sort = table.concat, table.serialize, table.sort
+local settings_to_hash, hash_to_string = utilities.parsers.settings_to_hash, utilities.parsers.hash_to_string
+local formatcolumns = utilities.formatters.formatcolumns
local tostring, next, type = tostring, next, type
-local lpegmatch = lpeg.match
+local utfchar, utfbyte = utf.char, utf.byte
local round = math.round
-local ctxcatcodes = tex.ctxcatcodes
+local P, S, C, Cc, Cf, Cg, Ct, lpegmatch = lpeg.P, lpeg.S, lpeg.C, lpeg.Cc, lpeg.Cf, lpeg.Cg, lpeg.Ct, lpeg.match
+
+local trace_defining = false trackers.register("fonts.defining", function(v) trace_defining = v end)
+local trace_usage = false trackers.register("fonts.usage", function(v) trace_usage = v end)
+local trace_mapfiles = false trackers.register("fonts.mapfiles", function(v) trace_mapfiles = v end)
+local trace_automode = false trackers.register("fonts.automode", function(v) trace_automode = v end)
+
+local report_defining = logs.reporter("fonts","defining")
+local report_status = logs.reporter("fonts","status")
+local report_mapfiles = logs.reporter("fonts","mapfiles")
+
+local setmetatableindex = table.setmetatableindex
+
+local fonts = fonts
+local handlers = fonts.handlers
+local otf = handlers.otf -- brrr
+local names = fonts.names
+local definers = fonts.definers
+local specifiers = fonts.specifiers
+local constructors = fonts.constructors
+local loggers = fonts.loggers
+local helpers = fonts.helpers
+local hashes = fonts.hashes
+local fontdata = hashes.identifiers
+local currentfont = font.current
+local texattribute = tex.attribute
+
+local otffeatures = fonts.constructors.newfeatures("otf")
+local registerotffeature = otffeatures.register
+local baseprocessors = otffeatures.processors.base
+local baseinitializers = otffeatures.initializers.base
+
+specifiers.contextsetups = specifiers.contextsetups or { }
+specifiers.contextnumbers = specifiers.contextnumbers or { }
+specifiers.contextmerged = specifiers.contextmerged or { }
+specifiers.synonyms = specifiers.synonyms or { }
+
+local setups = specifiers.contextsetups
+local numbers = specifiers.contextnumbers
+local merged = specifiers.contextmerged
+local synonyms = specifiers.synonyms
+
+storage.register("fonts/setups" , setups , "fonts.specifiers.contextsetups" )
+storage.register("fonts/numbers", numbers, "fonts.specifiers.contextnumbers")
+storage.register("fonts/merged", merged, "fonts.specifiers.contextmerged")
+storage.register("fonts/synonyms", synonyms, "fonts.specifiers.synonyms")
+
+constructors.resolvevirtualtoo = true -- context specific (due to resolver)
+
+local allocate, mark = utilities.storage.allocate, utilities.storage.mark
+
+local nulldata = {
+ name = "nullfont",
+ characters = { },
+ descriptions = { },
+ properties = { },
+ parameters = { -- lmromanregular @ 12pt
+ slant = 0, -- 1
+ space = 256377, -- 2
+ spacestretch = 128188, -- 3
+ spaceshrink = 85459, -- 4
+ xheight = 338952, -- 5
+ quad = 786432, -- 6
+ extraspace = 85459, -- 7
+ },
+}
+
+function definers.resetnullfont()
+ -- resetting is needed because tikz misuses nullfont
+ local parameters = nulldata.parameters
+ parameters.slant = 0 -- 1
+ parameters.space = 0 -- 2
+ parameters.spacestretch = 0 -- 3
+ parameters.spaceshrink = 0 -- 4
+ parameters.xheight = 0 -- 5
+ parameters.quad = 0 -- 6
+ parameters.extraspace = 0 -- 7
+ definers.resetnullfont = function() end
+end
+
+setmetatableindex(fontdata, function(t,k) return nulldata end)
+
+local chardata = allocate() -- chardata
+local parameters = allocate()
+local csnames = allocate() -- namedata
+local quaddata = allocate() -- quaddata
+local xheightdata = allocate() -- xheightdata
+
+hashes.characters = chardata
+hashes.parameters = parameters
+hashes.quads = quaddata
+hashes.xheights = xheightdata
+
+setmetatableindex(chardata, function(t,k)
+ local characters = fontdata[k].characters
+ t[k] = characters
+ return characters
+end)
-local trace_defining = false trackers.register("fonts.defining", function(v) trace_defining = v end)
+setmetatableindex(parameters, function(t,k)
+ local parameters = fontdata[k].parameters
+ t[k] = parameters
+ return parameters
+end)
-local tfm = fonts.tfm
-local define = fonts.define
-local fontdata = fonts.identifiers
-local specify = define.specify
+setmetatableindex(quaddata, function(t,k)
+ local parameters = parameters[k]
+ local quad = parameters and parameters.quad or 0
+ t[k] = quad
+ return quad
+end)
-specify.context_setups = specify.context_setups or { }
-specify.context_numbers = specify.context_numbers or { }
-specify.context_merged = specify.context_merged or { }
-specify.synonyms = specify.synonyms or { }
+setmetatableindex(xheightdata, function(t,k)
+ local parameters = parameters[k]
+ local xheight = parameters and parameters.xheight or 0
+ t[k] = xheight
+ return quad
+end)
-local setups = specify.context_setups
-local numbers = specify.context_numbers
-local merged = specify.context_merged
-local synonyms = specify.synonyms
-local triggers = fonts.triggers
+-- this cannot be a feature initializer as there is no auto namespace
+-- so we never enter the loop then; we can store the defaults in the tma
+-- file (features.gpos.mkmk = 1 etc)
+
+local needsnodemode = {
+ gpos_mark2mark = true,
+ gpos_mark2base = true,
+ gpos_mark2ligature = true,
+}
+
+local function modechecker(tfmdata,features,mode) -- we cannot adapt features as they are shared!
+ if mode == "auto" then
+ local rawdata = tfmdata.shared.rawdata
+ local resources = rawdata and rawdata.resources
+ local sequences = resources.sequences
+ if sequences and #sequences > 0 then
+ local script = features.script or "dflt"
+ local language = features.language or "dflt"
+ for feature, value in next, features do
+ if value then
+ local found = false
+ for i=1,#sequences do
+ local sequence = sequences[i]
+ local features = sequence.features
+ if features then
+ local scripts = features[feature]
+ if scripts then
+ local languages = scripts[script]
+ if languages and languages[language] then
+ if found then
+ -- more than one lookup
+ if trace_automode then
+ report_defining("forcing node mode in font %s for feature %s, script %s, language %s (multiple lookups)",file.basename(tfmdata.properties.name),feature,script,language)
+ end
+ features.mode = "node"
+ return "node"
+ elseif needsnodemode[sequence.type] then
+ if trace_automode then
+ report_defining("forcing node mode in font %s for feature %s, script %s, language %s (no base support)",file.basename(tfmdata.properties.name),feature,script,language)
+ end
+ features.mode = "node"
+ return "node"
+ else
+ -- at least one lookup
+ found = true
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+ return "base"
+ else
+ return mode
+ end
+end
+
+registerotffeature {
+ -- we only set the checker and leave other settings of the mode
+ -- feature as they are
+ name = "mode",
+ modechecker = modechecker,
+}
+
+-- -- default = true anyway
+--
+-- local normalinitializer = constructors.getfeatureaction("otf","initializers","node","analyze")
+--
+-- local function analyzeinitializer(tfmdata,value,features) -- attr
+-- if value == "auto" and features then
+-- value = features.init or features.medi or features.fina or features.isol or false
+-- end
+-- return normalinitializer(tfmdata,value,features)
+-- end
+--
+-- registerotffeature {
+-- name = "analyze",
+-- initializers = {
+-- node = analyzeinitializer,
+-- },
+-- }
--[[ldx--
<p>So far we haven't really dealt with features (or whatever we want
@@ -48,29 +235,45 @@ name*context specification
</code>
--ldx]]--
-function specify.predefined(specification)
- local detail = specification.detail
- if detail ~= "" then
- -- detail = gsub(detail,"["..define.splitsymbols.."].*$","") -- get rid of *whatever specs and such
- if define.methods[detail] then -- since these may be appended at the
- specification.features.vtf = { preset = detail } -- tex end by default
+-- currently fonts are scaled while constructing the font, so we
+-- have to do scaling of commands in the vf at that point using e.g.
+-- "local scale = g.parameters.factor or 1" after all, we need to
+-- work with copies anyway and scaling needs to be done at some point;
+-- however, when virtual tricks are used as feature (makes more
+-- sense) we scale the commands in fonts.constructors.scale (and set the
+-- factor there)
+
+local loadfont = definers.loadfont
+
+function definers.loadfont(specification,size,id) -- overloads the one in font-def
+ local variants = definers.methods.variants
+ local virtualfeatures = specification.features.virtual
+ if virtualfeatures and virtualfeatures.preset then
+ local variant = variants[virtualfeatures.preset]
+ if variant then
+ return variant(specification,size,id)
end
+ else
+ local tfmdata = loadfont(specification,size,id)
+ -- constructors.checkvirtualid(tfmdata,id)
+ return tfmdata
end
- return specification
end
-define.register_split("@", specify.predefined)
+local function predefined(specification)
+ local variants = definers.methods.variants
+ local detail = specification.detail
+ if detail ~= "" and variants[detail] then
+ specification.features.virtual = { preset = detail }
+ end
+ return specification
+end
-storage.register("fonts/setups" , define.specify.context_setups , "fonts.define.specify.context_setups" )
-storage.register("fonts/numbers", define.specify.context_numbers, "fonts.define.specify.context_numbers")
-storage.register("fonts/merged", define.specify.context_merged, "fonts.define.specify.context_merged")
-storage.register("fonts/synonyms", define.specify.synonyms, "fonts.define.specify.synonyms")
+definers.registersplit("@", predefined,"virtual")
-local normalize_meanings = fonts.otf.meanings.normalize
-local settings_to_hash = aux.settings_to_hash
-local default_features = fonts.otf.features.default
+local normalize_features = otffeatures.normalize -- should be general
-local function preset_context(name,parent,features) -- currently otf only
+local function presetcontext(name,parent,features) -- will go to con and shared
if features == "" and find(parent,"=") then
features = parent
parent = ""
@@ -78,9 +281,9 @@ local function preset_context(name,parent,features) -- currently otf only
if features == "" then
features = { }
elseif type(features) == "string" then
- features = normalize_meanings(settings_to_hash(features))
+ features = normalize_features(settings_to_hash(features))
else
- features = normalize_meanings(features)
+ features = normalize_features(features)
end
-- todo: synonyms, and not otf bound
if parent ~= "" then
@@ -98,10 +301,18 @@ local function preset_context(name,parent,features) -- currently otf only
-- these are auto set so in order to prevent redundant definitions
-- we need to preset them (we hash the features and adding a default
-- setting during initialization may result in a different hash)
- for k,v in next, triggers do
- if features[v] == nil then -- not false !
- local vv = default_features[v]
- if vv then features[v] = vv end
+--~ for k,v in next, triggers do
+--~ if features[v] == nil then -- not false !
+--~ local vv = default_features[v]
+--~ if vv then features[v] = vv end
+--~ end
+--~ end
+ for feature,value in next, features do
+ if value == nil then -- not false !
+ local default = default_features[feature]
+ if default ~= nil then
+ features[feature] = default
+ end
end
end
-- sparse 'm so that we get a better hash and less test (experimental
@@ -111,7 +322,8 @@ local function preset_context(name,parent,features) -- currently otf only
if v then t[k] = v end
end
-- needed for dynamic features
- local number = (setups[name] and setups[name].number) or 0
+ -- maybe number should always be renewed as we can redefine features
+ local number = (setups[name] and setups[name].number) or 0 -- hm, numbers[name]
if number == 0 then
number = #numbers + 1
numbers[number] = name
@@ -121,7 +333,7 @@ local function preset_context(name,parent,features) -- currently otf only
return number, t
end
-local function context_number(name) -- will be replaced
+local function contextnumber(name) -- will be replaced
local t = setups[name]
if not t then
return 0
@@ -152,7 +364,7 @@ local function context_number(name) -- will be replaced
end
end
-local function merge_context(currentnumber,extraname,option)
+local function mergecontext(currentnumber,extraname,option)
local current = setups[numbers[currentnumber]]
local extra = setups[extraname]
if extra then
@@ -182,13 +394,13 @@ local function merge_context(currentnumber,extraname,option)
numbers[number] = mergedname
merged[number] = option
setups[mergedname] = mergedfeatures
- return number -- context_number(mergedname)
+ return number -- contextnumber(mergedname)
else
return currentnumber
end
end
-local function register_context(fontnumber,extraname,option)
+local function registercontext(fontnumber,extraname,option)
local extra = setups[extraname]
if extra then
local mergedfeatures, mergedname = { }, nil
@@ -205,74 +417,152 @@ local function register_context(fontnumber,extraname,option)
numbers[number] = mergedname
merged[number] = option
setups[mergedname] = mergedfeatures
- return number -- context_number(mergedname)
+ return number -- contextnumber(mergedname)
else
return 0
end
end
-specify.preset_context = preset_context
-specify.context_number = context_number
-specify.merge_context = merge_context
-specify.register_context = register_context
+specifiers.presetcontext = presetcontext
+specifiers.contextnumber = contextnumber
+specifiers.mergecontext = mergecontext
+specifiers.registercontext = registercontext
-local current_font = font.current
-local tex_attribute = tex.attribute
+-- we extend the hasher:
+
+constructors.hashmethods.virtual = function(list)
+ local s = { }
+ local n = 0
+ for k, v in next, list do
+ n = n + 1
+ s[n] = k
+ end
+ if n > 0 then
+ sort(s)
+ for i=1,n do
+ local k = s[i]
+ s[i] = k .. '=' .. tostring(list[k])
+ end
+ return concat(s,"+")
+ end
+end
+
+-- end of redefine
local cache = { } -- concat might be less efficient than nested tables
-function fonts.withset(name,what)
- local zero = tex_attribute[0]
+local function withset(name,what)
+ local zero = texattribute[0]
local hash = zero .. "+" .. name .. "*" .. what
local done = cache[hash]
if not done then
- done = merge_context(zero,name,what)
+ done = mergecontext(zero,name,what)
cache[hash] = done
end
- tex_attribute[0] = done
+ texattribute[0] = done
end
-function fonts.withfnt(name,what)
- local font = current_font()
+
+local function withfnt(name,what)
+ local font = currentfont()
local hash = font .. "*" .. name .. "*" .. what
local done = cache[hash]
if not done then
- done = register_context(font,name,what)
+ done = registercontext(font,name,what)
cache[hash] = done
end
- tex_attribute[0] = done
+ texattribute[0] = done
end
-function specify.show_context(name)
+function specifiers.showcontext(name)
return setups[name] or setups[numbers[name]] or setups[numbers[tonumber(name)]] or { }
end
-local function split_context(features)
- return setups[features] or (preset_context(features,"","") and setups[features])
+-- todo: support a,b,c
+
+local function splitcontext(features) -- presetcontext creates dummy here
+ return setups[features] or (presetcontext(features,"","") and setups[features])
end
-specify.split_context = split_context
+--~ local splitter = lpeg.splitat("=")
+
+--~ local function splitcontext(features)
+--~ local setup = setups[features]
+--~ if setup then
+--~ return setup
+--~ elseif find(features,",") then
+--~ -- This is not that efficient but handy anyway for quick and dirty tests
+--~ -- beware, due to the way of caching setups you can get the wrong results
+--~ -- when components change. A safeguard is to nil the cache.
+--~ local merge = nil
+--~ for feature in gmatch(features,"[^, ]+") do
+--~ if find(feature,"=") then
+--~ local k, v = lpegmatch(splitter,feature)
+--~ if k and v then
+--~ if not merge then
+--~ merge = { k = v }
+--~ else
+--~ merge[k] = v
+--~ end
+--~ end
+--~ else
+--~ local s = setups[feature]
+--~ if not s then
+--~ -- skip
+--~ elseif not merge then
+--~ merge = s
+--~ else
+--~ for k, v in next, s do
+--~ merge[k] = v
+--~ end
+--~ end
+--~ end
+--~ end
+--~ setup = merge and presetcontext(features,"",merge) and setups[features]
+--~ -- actually we have to nil setups[features] in order to permit redefinitions
+--~ setups[features] = nil
+--~ end
+--~ return setup or (presetcontext(features,"","") and setups[features]) -- creates dummy
+--~ end
-function specify.context_tostring(name,kind,separator,yes,no,strict,omit) -- not used
- return aux.hash_to_string(table.merged(fonts[kind].features.default or {},setups[name] or {}),separator,yes,no,strict,omit)
-end
+specifiers.splitcontext = splitcontext
-local splitter = lpeg.splitat(",")
+function specifiers.contexttostring(name,kind,separator,yes,no,strict,omit) -- not used
+ return hash_to_string(table.merged(handlers[kind].features.defaults or {},setups[name] or {}),separator,yes,no,strict,omit)
+end
-function specify.starred(features) -- no longer fallbacks here
+local function starred(features) -- no longer fallbacks here
local detail = features.detail
if detail and detail ~= "" then
- features.features.normal = split_context(detail)
+ features.features.normal = splitcontext(detail)
else
features.features.normal = { }
end
return features
end
-define.register_split('*',specify.starred)
+definers.registersplit('*',starred,"featureset")
--- define (two steps)
+-- sort of xetex mode, but without [] and / as we have file: and name: etc
-local P, C, Cc = lpeg.P, lpeg.C, lpeg.Cc
+local space = P(" ")
+local separator = S(";,")
+local equal = P("=")
+local spaces = space^0
+local sometext = C((1-equal-space-separator)^1)
+local truevalue = P("+") * spaces * sometext * Cc(true) -- "yes"
+local falsevalue = P("-") * spaces * sometext * Cc(false) -- "no"
+local keyvalue = sometext * spaces * equal * spaces * sometext
+local somevalue = sometext * spaces * Cc(true) -- "yes"
+local pattern = Cf(Ct("") * (space + separator + Cg(keyvalue + falsevalue + truevalue + somevalue))^0, rawset)
+
+local function colonized(specification)
+ specification.features.normal = normalize_features(lpegmatch(pattern,specification.detail))
+ return specification
+end
+
+definers.registersplit(":",colonized,"direct")
+
+-- define (two steps)
local space = P(" ")
local spaces = space^0
@@ -290,56 +580,63 @@ local scale_scaled = P("scaled") * Cc(4) * spaces * dimension -- value
local sizepattern = spaces * (scale_at + scale_sa + scale_mo + scale_scaled + scale_none)
local splitpattern = spaces * value * spaces * rest
-local specification --
+local specification -- still needed as local ?
-local get_specification = define.get_specification
+local getspecification = definers.getspecification
-- we can make helper macros which saves parsing (but normaly not
-- that many calls, e.g. in mk a couple of 100 and in metafun 3500)
-function define.command_1(str)
+local setdefaultfontname = context.fntsetdefname
+local setsomefontname = context.fntsetsomename
+local setemptyfontsize = context.fntsetnopsize
+local setsomefontsize = context.fntsetsomesize
+
+function definers.stage_one(str)
statistics.starttiming(fonts)
local fullname, size = lpegmatch(splitpattern,str)
- local lookup, name, sub, method, detail = get_specification(fullname)
+ local lookup, name, sub, method, detail = getspecification(fullname)
if not name then
- logs.report("define font","strange definition '%s'",str)
- texsprint(ctxcatcodes,"\\fcglet\\somefontname\\defaultfontfile")
+ report_defining("strange definition '%s'",str)
+ setdefaultfontname()
elseif name == "unknown" then
- texsprint(ctxcatcodes,"\\fcglet\\somefontname\\defaultfontfile")
+ setdefaultfontname()
else
- texsprint(ctxcatcodes,"\\fcxdef\\somefontname{",name,"}")
+ setsomefontname(name)
end
-- we can also use a count for the size
if size and size ~= "" then
local mode, size = lpegmatch(sizepattern,size)
if size and mode then
- count.scaledfontmode = mode
- texsprint(ctxcatcodes,"\\def\\somefontsize{",size,"}")
+ texcount.scaledfontmode = mode
+ setsomefontsize(size)
else
- count.scaledfontmode = 0
- texsprint(ctxcatcodes,"\\let\\somefontsize\\empty")
+ texcount.scaledfontmode = 0
+ setemptyfontsize()
end
elseif true then
-- so we don't need to check in tex
- count.scaledfontmode = 2
- texsprint(ctxcatcodes,"\\let\\somefontsize\\empty")
+ texcount.scaledfontmode = 2
+ setemptyfontsize()
else
- count.scaledfontmode = 0
- texsprint(ctxcatcodes,"\\let\\somefontsize\\empty")
+ texcount.scaledfontmode = 0
+ setemptyfontsize()
end
- specification = define.makespecification(str,lookup,name,sub,method,detail,size)
+ specification = definers.makespecification(str,lookup,name,sub,method,detail,size)
end
local n = 0
-- we can also move rscale to here (more consistent)
+-- the argument list will become a table
-function define.command_2(global,cs,str,size,classfeatures,fontfeatures,classfallbacks,fontfallbacks,mathsize,textsize,relativeid)
+function definers.stage_two(global,cs,str,size,inheritancemode,classfeatures,fontfeatures,classfallbacks,fontfallbacks,
+ mathsize,textsize,relativeid,classgoodies,goodies)
if trace_defining then
- logs.report("define font","memory usage before: %s",statistics.memused())
+ report_defining("memory usage before: %s",statistics.memused())
end
-- name is now resolved and size is scaled cf sa/mo
- local lookup, name, sub, method, detail = get_specification(str or "")
+ local lookup, name, sub, method, detail = getspecification(str or "")
-- asome settings can be overloaded
if lookup and lookup ~= "" then
specification.lookup = lookup
@@ -348,148 +645,201 @@ function define.command_2(global,cs,str,size,classfeatures,fontfeatures,classfal
local id = tonumber(relativeid) or 0
specification.relativeid = id > 0 and id
end
- specification.name = name
- specification.size = size
- specification.sub = (sub and sub ~= "" and sub) or specification.sub
+ specification.name = name
+ specification.size = size
+ specification.sub = (sub and sub ~= "" and sub) or specification.sub
specification.mathsize = mathsize
specification.textsize = textsize
+ specification.goodies = goodies
+ specification.cs = cs
+ specification.global = global
if detail and detail ~= "" then
- specification.method, specification.detail = method or "*", detail
+ specification.method = method or "*"
+ specification.detail = detail
elseif specification.detail and specification.detail ~= "" then
-- already set
- elseif fontfeatures and fontfeatures ~= "" then
- specification.method, specification.detail = "*", fontfeatures
- elseif classfeatures and classfeatures ~= "" then
- specification.method, specification.detail = "*", classfeatures
+ elseif inheritancemode == 0 then
+ -- nothing
+ elseif inheritancemode == 1 then
+ -- fontonly
+ if fontfeatures and fontfeatures ~= "" then
+ specification.method = "*"
+ specification.detail = fontfeatures
+ end
+ if fontfallbacks and fontfallbacks ~= "" then
+ specification.fallbacks = fontfallbacks
+ end
+ elseif inheritancemode == 2 then
+ -- classonly
+ if classfeatures and classfeatures ~= "" then
+ specification.method = "*"
+ specification.detail = classfeatures
+ end
+ if classfallbacks and classfallbacks ~= "" then
+ specification.fallbacks = classfallbacks
+ end
+ elseif inheritancemode == 3 then
+ -- fontfirst
+ if fontfeatures and fontfeatures ~= "" then
+ specification.method = "*"
+ specification.detail = fontfeatures
+ elseif classfeatures and classfeatures ~= "" then
+ specification.method = "*"
+ specification.detail = classfeatures
+ end
+ if fontfallbacks and fontfallbacks ~= "" then
+ specification.fallbacks = fontfallbacks
+ elseif classfallbacks and classfallbacks ~= "" then
+ specification.fallbacks = classfallbacks
+ end
+ elseif inheritancemode == 4 then
+ -- classfirst
+ if classfeatures and classfeatures ~= "" then
+ specification.method = "*"
+ specification.detail = classfeatures
+ elseif fontfeatures and fontfeatures ~= "" then
+ specification.method = "*"
+ specification.detail = fontfeatures
+ end
+ if classfallbacks and classfallbacks ~= "" then
+ specification.fallbacks = classfallbacks
+ elseif fontfallbacks and fontfallbacks ~= "" then
+ specification.fallbacks = fontfallbacks
+ end
end
- if fontfallbacks and fontfallbacks ~= "" then
- specification.fallbacks = fontfallbacks
- elseif classfallbacks and classfallbacks ~= "" then
- specification.fallbacks = classfallbacks
+ local tfmdata = definers.read(specification,size) -- id not yet known
+ local cs = specification.cs
+ if cs then
+ csnames[cs] = tfmdata -- new (beware: locals can be forgotten)
end
- local tfmdata = define.read(specification,size) -- id not yet known
if not tfmdata then
- logs.report("define font","unable to define %s as \\%s",name,cs)
+ report_defining("unable to define %s as \\%s",name,cs)
texsetcount("global","lastfontid",-1)
+ context.letvaluerelax(cs) -- otherwise the current definition takes the previous one
elseif type(tfmdata) == "number" then
if trace_defining then
- logs.report("define font","reusing %s with id %s as \\%s (features: %s/%s, fallbacks: %s/%s)",name,tfmdata,cs,classfeatures,fontfeatures,classfallbacks,fontfallbacks)
+ report_defining("reusing %s with id %s as \\%s (features: %s/%s, fallbacks: %s/%s, goodies: %s/%s)",
+ name,tfmdata,cs,classfeatures,fontfeatures,classfallbacks,fontfallbacks,classgoodies,goodies)
end
tex.definefont(global,cs,tfmdata)
-- resolved (when designsize is used):
- texsprint(ctxcatcodes,format("\\def\\somefontsize{%isp}",fontdata[tfmdata].size))
+ setsomefontsize(fontdata[tfmdata].parameters.size .. "sp")
texsetcount("global","lastfontid",tfmdata)
else
+tfmdata.characters[0] = nil -- we use char0 as signal
-- local t = os.clock(t)
local id = font.define(tfmdata)
-- print(name,os.clock()-t)
- tfmdata.id = id
- define.register(tfmdata,id)
+ tfmdata.properties.id = id
+ definers.register(tfmdata,id) -- to be sure, normally already done
tex.definefont(global,cs,id)
- tfm.cleanup_table(tfmdata)
+ constructors.cleanuptable(tfmdata)
+ constructors.finalize(tfmdata)
if trace_defining then
- logs.report("define font","defining %s with id %s as \\%s (features: %s/%s, fallbacks: %s/%s)",name,id,cs,classfeatures,fontfeatures,classfallbacks,fontfallbacks)
+ report_defining("defining %s with id %s as \\%s (features: %s/%s, fallbacks: %s/%s)",name,id,cs,classfeatures,fontfeatures,classfallbacks,fontfallbacks)
end
-- resolved (when designsize is used):
- texsprint(ctxcatcodes,format("\\def\\somefontsize{%isp}",tfmdata.size))
+ setsomefontsize((tfmdata.parameters.size or 655360) .. "sp")
--~ if specification.fallbacks then
--~ fonts.collections.prepare(specification.fallbacks)
--~ end
texsetcount("global","lastfontid",id)
end
if trace_defining then
- logs.report("define font","memory usage after: %s",statistics.memused())
+ report_defining("memory usage after: %s",statistics.memused())
end
statistics.stoptiming(fonts)
end
+function definers.define(specification)
+ --
+ local name = specification.name
+ if not name or name == "" then
+ return -1
+ else
+ statistics.starttiming(fonts)
+ --
+ -- following calls expect a few properties to be set:
+ --
+ local lookup, name, sub, method, detail = getspecification(name or "")
+ --
+ specification.name = (name ~= "" and name) or specification.name
+ --
+ specification.lookup = specification.lookup or (lookup ~= "" and lookup) or "file"
+ specification.size = specification.size or 655260
+ specification.sub = specification.sub or (sub ~= "" and sub) or ""
+ specification.method = specification.method or (method ~= "" and method) or "*"
+ specification.detail = specification.detail or (detail ~= "" and detail) or ""
+ --
+ specification.specification = "" -- not used
+ specification.resolved = ""
+ specification.forced = ""
+ specification.features = { } -- via detail, maybe some day
+ --
+ -- we don't care about mathsize textsize goodies fallbacks
+ --
+ if specification.cs == "" then
+ specification.cs = nil
+ specification.global = false
+ elseif specification.global == nil then
+ specification.global = false
+ end
+ --
+ local tfmdata = definers.read(specification,specification.size)
+ if not tfmdata then
+ return -1, nil
+ elseif type(tfmdata) == "number" then
+ if specification.cs then
+ tex.definefont(specification.global,specification.cs,tfmdata)
+ end
+ return tfmdata, fontdata[tfmdata]
+ else
+ local id = font.define(tfmdata)
+ tfmdata.properties.id = id
+ definers.register(tfmdata,id)
+ if specification.cs then
+ tex.definefont(specification.global,specification.cs,id)
+ end
+ constructors.cleanuptable(tfmdata)
+ constructors.finalize(tfmdata)
+ return id, tfmdata
+ end
+ statistics.stoptiming(fonts)
+ end
+end
+
local enable_auto_r_scale = false
experiments.register("fonts.autorscale", function(v)
enable_auto_r_scale = v
end)
-local calculate_scale = fonts.tfm.calculate_scale
-
-function fonts.tfm.calculate_scale(tfmtable, scaledpoints, relativeid)
- local scaledpoints, delta, units = calculate_scale(tfmtable, scaledpoints, relativeid)
- if enable_auto_r_scale and relativeid then -- for the moment this is rather context specific
- local relativedata = fontdata[relativeid]
- local id_x_height = relativedata and relativedata.parameters and relativedata.parameters.x_height
- local tf_x_height = id_x_height and tfmtable.parameters and tfmtable.parameters.x_height * delta
- if tf_x_height then
- scaledpoints = (id_x_height/tf_x_height) * scaledpoints
- delta = scaledpoints/units
- end
- end
- return scaledpoints, delta, units
-end
-
---~ table.insert(readers.sequence,1,'vtf')
-
---~ function readers.vtf(specification)
---~ if specification.features.vtf and specification.features.vtf.preset then
---~ return tfm.make(specification)
---~ else
---~ return nil
+-- Not ok, we can best use a database for this. The problem is that we
+-- have delayed definitions and so we never know what style is taken
+-- as start.
+
+local calculatescale = constructors.calculatescale
+
+function constructors.calculatescale(tfmdata,scaledpoints,relativeid)
+ local scaledpoints, delta = calculatescale(tfmdata,scaledpoints)
+--~ if enable_auto_r_scale and relativeid then -- for the moment this is rather context specific
+--~ local relativedata = fontdata[relativeid]
+--~ local rfmdata = relativedata and relativedata.unscaled and relativedata.unscaled
+--~ local id_x_height = rfmdata and rfmdata.parameters and rfmdata.parameters.x_height
+--~ local tf_x_height = tfmdata and tfmdata.parameters and tfmdata.parameters.x_height
+--~ if id_x_height and tf_x_height then
+--~ local rscale = id_x_height/tf_x_height
+--~ delta = rscale * delta
+--~ scaledpoints = rscale * scaledpoints
+--~ end
--~ end
---~ end
-
--- we need a place for this .. outside the generic scope
-
-local dimenfactors = number.dimenfactors
-
-function fonts.dimenfactor(unit,tfmdata)
- if unit == "ex" then
- return (tfmdata and tfmdata.parameters.x_height) or 655360
- elseif unit == "em" then
- return (tfmdata and tfmdata.parameters.em_height) or 655360
- else
- return dimenfactors[unit] or unit
- end
-end
-
-function fonts.cleanname(name)
- texsprint(ctxcatcodes,fonts.names.cleanname(name))
-end
-
-local p, f = 1, "%0.1fpt" -- normally this value is changed only once
-
-local stripper = lpeg.patterns.strip_zeros
-
-function fonts.nbfs(amount,precision)
- if precision ~= p then
- p = precision
- f = "%0." .. p .. "fpt"
- end
- texsprint(ctxcatcodes,lpegmatch(stripper,format(f,amount/65536)))
-end
-
--- for the moment here, this will become a chain of extras that is
--- hooked into the ctx registration (or scaler or ...)
-
-function fonts.set_digit_width(font)
- local tfmtable = fontdata[font]
- local parameters = tfmtable.parameters
- local width = parameters.digitwidth
- if not width then
- width = round(parameters.quad/2) -- maybe tex.scale
- local characters = tfmtable.characters
- for i=48,57 do
- local wd = round(characters[i].width)
- if wd > width then
- width = wd
- end
- end
- parameters.digitwidth = width
- end
- return width
+ return scaledpoints, delta
end
-fonts.get_digit_width = fonts.set_digit_width
-
-- soon to be obsolete:
+local mappings = fonts.mappings
+
local loaded = { -- prevent loading (happens in cont-sys files)
["original-base.map" ] = true,
["original-ams-base.map" ] = true,
@@ -497,9 +847,12 @@ local loaded = { -- prevent loading (happens in cont-sys files)
["original-public-lm.map"] = true,
}
-function fonts.map.loadfile(name)
+function mappings.loadfile(name)
name = file.addsuffix(name,"map")
if not loaded[name] then
+ if trace_mapfiles then
+ report_mapfiles("loading map file '%s'",name)
+ end
pdf.mapfile(name)
loaded[name] = true
end
@@ -508,117 +861,421 @@ end
local loaded = { -- prevent double loading
}
-function fonts.map.loadline(how,line)
+function mappings.loadline(how,line)
if line then
how = how .. " " .. line
elseif how == "" then
how = "= " .. line
end
if not loaded[how] then
+ if trace_mapfiles then
+ report_mapfiles("processing map line '%s'",line)
+ end
pdf.mapline(how)
loaded[how] = true
end
end
-function fonts.map.reset()
+function mappings.reset()
pdf.mapfile("")
end
-fonts.map.reset() -- resets the default file
+mappings.reset() -- resets the default file
-- we need an 'do after the banner hook'
--- pdf.mapfile("mkiv-base.map") -- loads the default file
+-- => commands
+
+local function nametoslot(name)
+ local t = type(name)
+ if t == "string" then
+ local tfmdata = fonts.hashes.identifiers[currentfont()]
+ local shared = tfmdata and tfmdata.shared
+ local fntdata = shared and shared.rawdata
+ return fntdata and fntdata.resources.unicodes[name]
+ elseif t == "number" then
+ return n
+ end
+end
+
+helpers.nametoslot = nametoslot
-local nounicode = byte("?")
+-- this will change ...
-local function name_to_slot(name) -- maybe some day rawdata
- local tfmdata = fonts.ids[font.current()]
- local shared = tfmdata and tfmdata.shared
- local fntdata = shared and shared.otfdata or shared.afmdata
- if fntdata then
- local unicode = fntdata.luatex.unicodes[name]
- if not unicode then
- return nounicode
- elseif type(unicode) == "number" then
- return unicode
- else -- multiple unicodes
- return unicode[1]
+function loggers.reportdefinedfonts()
+ if trace_usage then
+ local t, tn = { }, 0
+ for id, data in table.sortedhash(fontdata) do
+ local properties = data.properties or { }
+ local parameters = data.parameters or { }
+ tn = tn + 1
+ t[tn] = {
+ format("%03i",id or 0),
+ format("%09i",parameters.size or 0),
+ properties.type or "real",
+ properties.format or "unknown",
+ properties.name or "",
+ properties.psname or "",
+ properties.fullname or "",
+ }
+report_status("%s: %s",properties.name,concat(table.sortedkeys(data)," "))
+ end
+ formatcolumns(t," ")
+ report_status()
+ report_status("defined fonts:")
+ report_status()
+ for k=1,tn do
+ report_status(t[k])
end
end
- return nounicode
end
-fonts.name_to_slot = name_to_slot
-
-function fonts.char(n) -- todo: afm en tfm
- if type(n) == "string" then
- n = name_to_slot(n)
+luatex.registerstopactions(loggers.reportdefinedfonts)
+
+function loggers.reportusedfeatures()
+ -- numbers, setups, merged
+ if trace_usage then
+ local t, n = { }, #numbers
+ for i=1,n do
+ local name = numbers[i]
+ local setup = setups[name]
+ local n = setup.number
+ setup.number = nil -- we have no reason to show this
+ t[i] = { i, name, table.sequenced(setup,false,true) } -- simple mode
+ setup.number = n -- restore it (normally not needed as we're done anyway)
+ end
+ formatcolumns(t," ")
+ report_status()
+ report_status("defined featuresets:")
+ report_status()
+ for k=1,n do
+ report_status(t[k])
+ end
end
- if type(n) == "number" then
- texsprint(ctxcatcodes,format("\\char%s ",n))
+end
+
+luatex.registerstopactions(loggers.reportusedfeatures)
+
+statistics.register("fonts load time", function()
+ return statistics.elapsedseconds(fonts)
+end)
+
+-- experimental mechanism for Mojca:
+--
+-- fonts.definetypeface {
+-- name = "mainbodyfont-light",
+-- preset = "antykwapoltawskiego-light",
+-- }
+--
+-- fonts.definetypeface {
+-- name = "mojcasfavourite",
+-- preset = "antykwapoltawskiego",
+-- normalweight = "light",
+-- boldweight = "bold",
+-- width = "condensed",
+-- }
+
+local Shapes = {
+ serif = "Serif",
+ sans = "Sans",
+ mono = "Mono",
+}
+
+function fonts.definetypeface(name,t)
+ if type(name) == "table" then
+ -- {name=abc,k=v,...}
+ t = name
+ elseif t then
+ if type(t) == "string" then
+ -- "abc", "k=v,..."
+ t = settings_to_hash(name)
+ else
+ -- "abc", {k=v,...}
+ end
+ t.name = t.name or name
+ else
+ -- "name=abc,k=v,..."
+ t = settings_to_hash(name)
end
+ local p = t.preset and fonts.typefaces[t.preset] or { }
+ local name = t.name or "unknowntypeface"
+ local shortcut = t.shortcut or p.shortcut or "rm"
+ local size = t.size or p.size or "default"
+ local shape = t.shape or p.shape or "serif"
+ local fontname = t.fontname or p.fontname or "unknown"
+ local normalweight = t.normalweight or t.weight or p.normalweight or p.weight or "normal"
+ local boldweight = t.boldweight or t.weight or p.boldweight or p.weight or "normal"
+ local normalwidth = t.normalwidth or t.width or p.normalwidth or p.width or "normal"
+ local boldwidth = t.boldwidth or t.width or p.boldwidth or p.width or "normal"
+ Shape = Shapes[shape] or "Serif"
+ context.startfontclass { name }
+ context.definefontsynonym( { format("%s", Shape) }, { format("spec:%s-%s-regular-%s", fontname, normalweight, normalwidth) } )
+ context.definefontsynonym( { format("%sBold", Shape) }, { format("spec:%s-%s-regular-%s", fontname, boldweight, boldwidth ) } )
+ context.definefontsynonym( { format("%sBoldItalic", Shape) }, { format("spec:%s-%s-italic-%s", fontname, boldweight, boldwidth ) } )
+ context.definefontsynonym( { format("%sItalic", Shape) }, { format("spec:%s-%s-italic-%s", fontname, normalweight, normalwidth) } )
+ context.stopfontclass()
+ local settings = table.sequenced({ features= t.features },",")
+ context.dofastdefinetypeface(name, shortcut, shape, size, settings)
end
--- moved from ini:
+function fonts.current() -- todo: also handle name
+ return fontdata[currentfont()] or fontdata[0]
+end
-fonts.color = { } -- dummy in ini
+function fonts.currentid()
+ return currentfont() or 0
+end
-local attribute = attributes.private('color')
-local mapping = (attributes and attributes.list[attribute]) or { }
+-- interfaces
-local set_attribute = node.set_attribute
-local unset_attribute = node.unset_attribute
+function commands.fontchar(n)
+ n = nametoslot(n)
+ if n then
+ context.char(n)
+ end
+end
-function fonts.color.set(n,c)
- local mc = mapping[c]
- if not mc then
- unset_attribute(n,attribute)
- else
- set_attribute(n,attribute,mc)
+function commands.doifelsecurrentfonthasfeature(name) -- can be made faster with a supportedfeatures hash
+ local f = fontdata[currentfont()]
+ f = f and f.shared
+ f = f and f.rawdata
+ f = f and f.resources
+ f = f and f.features
+ commands.doifelse(f and (f.gpos[name] or f.gsub[name]))
+end
+
+local p, f = 1, "%0.1fpt" -- normally this value is changed only once
+
+local stripper = lpeg.patterns.stripzeros
+
+function commands.nbfs(amount,precision)
+ if precision ~= p then
+ p = precision
+ f = "%0." .. p .. "fpt"
end
+ context(lpegmatch(stripper,format(f,amount/65536)))
end
-function fonts.color.reset(n)
- unset_attribute(n,attribute)
+function commands.featureattribute(tag)
+ tex.write(contextnumber(tag))
end
--- this will become obsolete:
+function commands.setfontfeature(tag)
+ texattribute[0] = contextnumber(tag)
+end
-fonts.otf.name_to_slot = name_to_slot
-fonts.afm.name_to_slot = name_to_slot
+function commands.resetfontfeature()
+ texattribute[0] = 0
+end
-fonts.otf.char = fonts.char
-fonts.afm.char = fonts.char
+function commands.addfs(tag) withset(tag, 1) end
+function commands.subfs(tag) withset(tag,-1) end
+function commands.addff(tag) withfnt(tag, 2) end
+function commands.subff(tag) withfnt(tag,-2) end
--- this will change ...
+-- function commands.addfontfeaturetoset (tag) withset(tag, 1) end
+-- function commands.subtractfontfeaturefromset (tag) withset(tag,-1) end
+-- function commands.addfontfeaturetofont (tag) withfnt(tag, 2) end
+-- function commands.subtractfontfeaturefromfont(tag) withfnt(tag,-2) end
-function fonts.show_char_data(n)
- local tfmdata = fonts.ids[font.current()]
+function commands.cleanfontname (name) context(names.cleanname(name)) end
+
+function commands.fontlookupinitialize (name) names.lookup(name) end
+function commands.fontlookupnoffound () context(names.noflookups()) end
+function commands.fontlookupgetkeyofindex(key,index) context(names.getlookupkey(key,index)) end
+function commands.fontlookupgetkey (key) context(names.getlookupkey(key)) end
+
+-- this might move to a runtime module:
+
+function commands.showchardata(n)
+ local tfmdata = fontdata[currentfont()]
if tfmdata then
if type(n) == "string" then
- n = utf.byte(n)
+ n = utfbyte(n)
end
local chr = tfmdata.characters[n]
if chr then
- write_nl(format("%s @ %s => U%04X => %s => ",tfmdata.fullname,tfmdata.size,n,utf.char(n)) .. serialize(chr,false))
+ report_status("%s @ %s => U%05X => %s => %s",tfmdata.properties.fullname,tfmdata.parameters.size,n,utfchar(n),serialize(chr,false))
end
end
end
-function fonts.show_font_parameters()
- local tfmdata = fonts.ids[font.current()]
+function commands.showfontparameters(tfmdata)
+ -- this will become more clever
+ local tfmdata = tfmdata or fontdata[currentfont()]
if tfmdata then
- local parameters, mathconstants = tfmdata.parameters, tfmdata.MathConstants
- local hasparameters, hasmathconstants = parameters and next(parameters), mathconstants and next(mathconstants)
+ local parameters = tfmdata.parameters
+ local mathparameters = tfmdata.mathparameters
+ local properties = tfmdata.properties
+ local hasparameters = parameters and next(parameters)
+ local hasmathparameters = mathparameters and next(mathparameters)
if hasparameters then
- write_nl(format("%s @ %s => parameters => ",tfmdata.fullname,tfmdata.size) .. serialize(parameters,false))
+ report_status("%s @ %s => text parameters => %s",properties.fullname,parameters.size,serialize(parameters,false))
+ end
+ if hasmathparameters then
+ report_status("%s @ %s => math parameters => %s",properties.fullname,parameters.size,serialize(mathparameters,false))
+ end
+ if not hasparameters and not hasmathparameters then
+ report_status("%s @ %s => no text parameters and/or math parameters",properties.fullname,parameters.size)
+ end
+ end
+end
+
+-- for the moment here, this will become a chain of extras that is
+-- hooked into the ctx registration (or scaler or ...)
+
+local dimenfactors = number.dimenfactors
+
+function helpers.dimenfactor(unit,tfmdata) -- could be a method of a font instance
+ if unit == "ex" then
+ return (tfmdata and tfmdata.parameters.x_height) or 655360
+ elseif unit == "em" then
+ return (tfmdata and tfmdata.parameters.em_width) or 655360
+ else
+ return dimenfactors[unit] or unit
+ end
+end
+
+local function digitwidth(font) -- max(quad/2,wd(0..9))
+ local tfmdata = fontdata[font]
+ local parameters = tfmdata.parameters
+ local width = parameters.digitwidth
+ if not width then
+ width = round(parameters.quad/2) -- maybe tex.scale
+ local characters = tfmdata.characters
+ for i=48,57 do
+ local wd = round(characters[i].width)
+ if wd > width then
+ width = wd
+ end
end
- if hasmathconstants then
- write_nl(format("%s @ %s => math constants => ",tfmdata.fullname,tfmdata.size) .. serialize(mathconstants,false))
+ parameters.digitwidth = width
+ end
+ return width
+end
+
+helpers.getdigitwidth = digitwidth
+helpers.setdigitwidth = digitwidth
+
+--
+
+function helpers.getparameters(tfmdata)
+ local p = { }
+ local m = p
+ local parameters = tfmdata.parameters
+ while true do
+ for k, v in next, parameters do
+ m[k] = v
end
- if not hasparameters and not hasmathconstants then
- write_nl(format("%s @ %s => no parameters and/or mathconstants",tfmdata.fullname,tfmdata.size))
+ parameters = getmetatable(parameters)
+ parameters = parameters and parameters.__index
+ if type(parameters) == "table" then
+ m = { }
+ p.metatable = m
+ else
+ break
end
end
+ return p
+end
+
+if environment.initex then
+
+ local function names(t)
+ local nt = #t
+ if nt > 0 then
+ local n = { }
+ for i=1,nt do
+ n[i] = t[i].name
+ end
+ return concat(n," ")
+ else
+ return "-"
+ end
+ end
+
+ statistics.register("font processing", function()
+ local l = { }
+ for what, handler in table.sortedpairs(handlers) do
+ local features = handler.features
+ if features then
+ local t = { }
+ t[#t+1] = "["
+ t[#t+1] = what
+ t[#t+1] = format("(base initializers: %s)",names(features.initializers.base))
+ t[#t+1] = format("(base processors: %s)", names(features.processors .base))
+ t[#t+1] = format("(base manipulators: %s)",names(features.manipulators.base))
+ t[#t+1] = format("(node initializers: %s)",names(features.initializers.node))
+ t[#t+1] = format("(node processors: %s)", names(features.processors .node))
+ t[#t+1] = format("(node manipulators: %s)",names(features.manipulators.node))
+ t[#t+1] = "]"
+ l[#l+1] = concat(t, " ")
+ end
+ end
+ return concat(l, " | ")
+ end)
+
end
+
+-- redefinition
+
+local quads = hashes.quads
+local xheights = hashes.xheights
+local currentfont = font.current
+local texdimen = tex.dimen
+
+setmetatableindex(number.dimenfactors, function(t,k)
+ if k == "ex" then
+ return xheigths[currentfont()]
+ elseif k == "em" then
+ return quads[currentfont()]
+ elseif k == "%" then
+ return dimen.hsize/100
+ else
+ -- error("wrong dimension: " .. (s or "?")) -- better a message
+ return false
+ end
+end)
+
+--[[ldx--
+<p>Before a font is passed to <l n='tex'/> we scale it. Here we also need
+to scale virtual characters.</p>
+--ldx]]--
+
+-- function constructors.getvirtualid(tfmdata)
+-- -- since we don't know the id yet, we use 0 as signal
+-- local tf = tfmdata.fonts
+-- if not tf then
+-- local properties = tfmdata.properties
+-- if properties then
+-- properties.virtualized = true
+-- else
+-- tfmdata.properties = { virtualized = true }
+-- end
+-- tf = { }
+-- tfmdata.fonts = tf
+-- end
+-- local ntf = #tf + 1
+-- tf[ntf] = { id = 0 }
+-- return ntf
+-- end
+--
+-- function constructors.checkvirtualid(tfmdata, id) -- will go
+-- local properties = tfmdata.properties
+-- if tfmdata and tfmdata.type == "virtual" or (properties and properties.virtualized) then
+-- local vfonts = tfmdata.fonts
+-- if not vffonts or #vfonts == 0 then
+-- if properties then
+-- properties.virtualized = false
+-- end
+-- tfmdata.fonts = nil
+-- else
+-- for f=1,#vfonts do
+-- local fnt = vfonts[f]
+-- if fnt.id and fnt.id == 0 then
+-- fnt.id = id
+-- end
+-- end
+-- end
+-- end
+-- end