summaryrefslogtreecommitdiff
path: root/tex/context/base/font-def.lua
diff options
context:
space:
mode:
Diffstat (limited to 'tex/context/base/font-def.lua')
-rw-r--r--tex/context/base/font-def.lua358
1 files changed, 241 insertions, 117 deletions
diff --git a/tex/context/base/font-def.lua b/tex/context/base/font-def.lua
index cc7c45a9e..474cde41d 100644
--- a/tex/context/base/font-def.lua
+++ b/tex/context/base/font-def.lua
@@ -8,6 +8,8 @@ if not modules then modules = { } end modules ['font-def'] = {
-- check reuse of lmroman1o-regular vs lmr10
+local texsprint, count, dimen, format, concat = tex.sprint, tex.count, tex.dimen, string.format, table.concat
+
--[[ldx--
<p>Here we deal with defining fonts. We do so by intercepting the
default loader that only handles <l n='tfm'/>.</p>
@@ -44,7 +46,6 @@ fonts.define.method = 3 -- 1: tfm 2: tfm and if not then afm 3: afm and
fonts.define.auto_afm = true
fonts.define.auto_otf = true
fonts.define.specify = fonts.define.specify or { }
-fonts.define.splitsymbols = ""
fonts.define.methods = fonts.define.methods or { }
tfm.fonts = tfm.fonts or { }
@@ -75,28 +76,47 @@ synonym table.</p>
and prepares a table that will move along as we proceed.</p>
--ldx]]--
-function fonts.define.analyze(name, size, id)
- name = name or 'unknown'
- local specification = name
- local lookup, rest = specification:match("^(.-):(.+)$")
- local sub = ""
- if lookup == 'file' or lookup == 'name' then
- name = rest
- else
- lookup = 'file'
- end
- local font, method, detail = name:match("^(.-)(["..fonts.define.splitsymbols.."])(.+)$")
- if method and detail then
- name = font
- else
- method, detail = "", ""
+-- beware, we discard additional specs
+--
+-- method:name method:name(sub) method:name(sub)*spec method:name*spec
+-- name name(sub) name(sub)*spec name*spec
+-- name@spec*oeps
+
+local splitter, specifiers = nil, ""
+
+function fonts.define.add_specifier(symbol)
+ specifiers = specifiers .. symbol
+ local left = lpeg.P("(")
+ local right = lpeg.P(")")
+ local colon = lpeg.P(":")
+ 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 name = lpeg.C((1-sub-specification)^1)
+ splitter = lpeg.P((lookup + lpeg.Cc("")) * name * (sub + lpeg.Cc("")) * (specification + lpeg.Cc("")))
+end
+
+function fonts.define.get_specification(str)
+ return splitter:match(str)
+end
+
+function fonts.define.register_split(symbol,action)
+ fonts.define.add_specifier(symbol)
+ fonts.define.specify[symbol] = action
+end
+
+function fonts.define.makespecification(specification, lookup, name, sub, method, detail, size)
+ size = size or 655360
+ if fonts.trace then
+ logs.report("define font","%s -> lookup: %s, name: %s, sub: %s, method: %s, detail: %s",
+ specification, (lookup ~= "" and lookup) or "[file]", (name ~= "" and name) or "-",
+ (sub ~= "" and sub) or "-", (method ~= "" and method) or "-", (detail ~= "" and detail) or "-")
end
- local mainfont, subfont = name:match("^(.*-)(%(.*-)(%)$")
- if mainfont and subfont then
- name, sub = mainfont, subfont
+ if lookup ~= 'name' then -- for the moment only two lookups, maybe some day also system:
+ lookup = 'file'
end
- size = size or (65536*10)
- return {
+ local t = {
lookup = lookup, -- forced type
specification = specification, -- full specification
size = size, -- size in scaled points or -1000*n
@@ -106,18 +126,14 @@ function fonts.define.analyze(name, size, id)
detail = detail, -- specification
resolved = "", -- resolved font name
forced = "", -- forced loader
- id = id, -- font id
features = { }, -- preprocessed features
- -- hash = nil
- -- filename = nil,
- -- encoding = nil,
- -- format = nil,
}
+ return t
end
-function fonts.define.register_split(symbol,action)
- fonts.define.splitsymbols = fonts.define.splitsymbols .. "%" .. symbol
- fonts.define.specify[symbol] = action
+function fonts.define.analyze(specification, size)
+ local lookup, name, sub, method, detail = fonts.define.get_specification(specification or "")
+ return fonts.define.makespecification(specification,lookup, name, sub, method, detail, size)
end
--[[ldx--
@@ -130,7 +146,7 @@ function tfm.hash_features(specification)
local t = { }
local normal = features.normal
if normal and next(normal) then
- local f = table.sortedkeys(normal)
+ local f = table.sortedhashkeys(normal)
for i=1,#f do
local v = f[i]
if v ~= "number" then
@@ -140,27 +156,19 @@ function tfm.hash_features(specification)
end
local vtf = features.vtf
if vtf and next(vtf) then
- local f = table.sortedkeys(vtf)
+ local f = table.sortedhashkeys(vtf)
for i=1,#f do
local v = f[i]
t[#t+1] = v .. '=' .. tostring(vtf[v])
end
end
if #t > 0 then
- return table.concat(t,"+")
+ return concat(t,"+")
end
end
return "unknown"
end
-
---~ function tfm.hash_instance(specification)
---~ if not specification.hash then
---~ specification.hash = tfm.hash_features(specification)
---~ end
---~ return specification.hash .. ' @ ' .. tostring(specification.size)
---~ end
-
fonts.designsizes = { }
--[[ldx--
@@ -172,16 +180,20 @@ loose our testcases for <l n='luatex'/>.</p>
--ldx]]--
function tfm.hash_instance(specification,force)
- local hash, size = specification.hash, specification.size
+ local hash, size, fallbacks = specification.hash, specification.size, specification.fallbacks
if force or not hash then
hash = tfm.hash_features(specification)
specification.hash = hash
end
if size < 1000 and fonts.designsizes[hash] then
- size = tfm.scaled(size, fonts.designsizes[hash])
+ size = math.round(tfm.scaled(size, fonts.designsizes[hash]))
specification.size = size
end
- return hash .. ' @ ' .. tostring(size)
+ if fallbacks then
+ return hash .. ' @ ' .. tostring(size) .. ' @ ' .. fallbacks
+ else
+ return hash .. ' @ ' .. tostring(size)
+ end
end
--[[ldx--
@@ -189,18 +201,22 @@ end
--ldx]]--
function fonts.define.resolve(specification)
- if specification.lookup == 'name' then
- specification.resolved, specification.sub = fonts.names.resolve(specification.name,specification.sub)
- if specification.resolved then
- specification.forced = file.extname(specification.resolved)
- specification.name = file.removesuffix(specification.resolved)
+ if not specification.resolved or specification.resolved == "" then -- resolved itself not per se in mapping hash
+ if specification.lookup == 'name' then
+ specification.resolved, specification.sub = fonts.names.resolve(specification.name,specification.sub)
+ if specification.resolved then
+ specification.forced = file.extname(specification.resolved)
+ specification.name = file.removesuffix(specification.resolved)
+ end
+ elseif specification.lookup == 'file' then
+ specification.forced = file.extname(specification.name)
+ specification.name = file.removesuffix(specification.name)
end
- elseif specification.lookup == 'file' then
- specification.forced = file.extname(specification.name)
- specification.name = file.removesuffix(specification.name)
end
if specification.forced == "" then
specification.forced = nil
+ else
+ specification.forced = specification.forced
end
specification.hash = specification.name .. ' @ ' .. tfm.hash_features(specification)
if specification.sub and specification.sub ~= "" then
@@ -226,13 +242,12 @@ specification yet.</p>
--ldx]]--
function tfm.read(specification)
- garbagecollector.push()
- input.starttiming(fonts)
+--~ input.starttiming(fonts)
local hash = tfm.hash_instance(specification)
local tfmtable = tfm.fonts[hash] -- hashes by size !
if not tfmtable then
if specification.forced and specification.forced ~= "" then
- tfmtable = tfm.readers[specification.forced](specification)
+ tfmtable = tfm.readers[specification.forced:lower()](specification)
if not tfmtable then
logs.report("define font","forced type %s of %s not found",specification.forced,specification.name)
end
@@ -258,8 +273,7 @@ function tfm.read(specification)
--~ tfmtable.mode = specification.features.normal.mode or "base"
end
end
- input.stoptiming(fonts)
- garbagecollector.pop()
+--~ input.stoptiming(fonts)
if not tfmtable then
logs.report("define font","font with name %s is not found",specification.name)
end
@@ -271,26 +285,21 @@ end
--ldx]]--
function tfm.read_and_define(name,size) -- no id
- local specification = fonts.define.analyze(name,size,nil)
- if specification.method and fonts.define.specify[specification.method] then
- specification = fonts.define.specify[specification.method](specification)
+ local specification = fonts.define.analyze(name,size)
+ local method = specification.method
+ if method and fonts.define.specify[method] then
+ specification = fonts.define.specify[method](specification)
end
specification = fonts.define.resolve(specification)
local hash = tfm.hash_instance(specification)
- local id = tfm.internalized[hash]
+ local id = fonts.define.registered(hash)
if not id then
local fontdata = tfm.read(specification)
if fontdata then
- if not tfm.internalized[hash] then
- id = font.define(fontdata)
- tfm.id[id] = fontdata
- tfm.internalized[hash] = id
- if fonts.trace then
- logs.report("define font","loading at 1 id %s, hash: %s",id,hash)
- end
- else
- id = tfm.internalized[hash]
- end
+ fontdata.hash = hash
+ id = font.define(fontdata)
+ fonts.define.register(fontdata,id)
+tfm.cleanup_table(fontdata)
else
id = 0 -- signal
end
@@ -299,17 +308,6 @@ function tfm.read_and_define(name,size) -- no id
end
--[[ldx--
-<p>A naive callback could be the following:</p>
-
-<code>
-callback.register('define_font', function(name,size,id)
- return fonts.define.read(fonts.define.resolve(fonts.define.analyze(name,size,id)))
-end)
-</code>
---ldx]]--
-
-
---[[ldx--
<p>Next follow the readers. This code was written while <l n='luatex'/>
evolved. Each one has its own way of dealing with its format.</p>
--ldx]]--
@@ -389,7 +387,7 @@ name*context specification
function fonts.define.specify.predefined(specification)
local detail = specification.detail
if detail ~= "" then
- detail = detail:gsub("["..fonts.define.splitsymbols.."].*$","") -- get rid of *whatever specs and such
+ -- detail = detail:gsub("["..fonts.define.splitsymbols.."].*$","") -- get rid of *whatever specs and such
if fonts.define.methods[detail] then -- since these may be appended at the
specification.features.vtf = { preset = detail } -- tex end by default
end
@@ -429,6 +427,13 @@ function fonts.define.specify.colonized(specification) -- xetex mode
end
function tfm.make(specification)
+ -- 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.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.tfm.scale (and set the
+ -- factor there)
local fvm = fonts.define.methods[specification.features.vtf.preset]
if fvm then
return fvm(specification)
@@ -446,15 +451,6 @@ fonts.define.specify.synonyms = fonts.define.specify.synonyms or {
input.storage.register(false,"fonts/setups" , fonts.define.specify.context_setups , "fonts.define.specify.context_setups" )
input.storage.register(false,"fonts/numbers", fonts.define.specify.context_numbers, "fonts.define.specify.context_numbers")
---~ local t = aux.settings_to_hash(features)
---~ for k,v in pairs(t) do
---~ k = synonyms[k] or k
---~ t[k] = v:is_boolean()
---~ if type(t[k]) == "nil" then
---~ t[k] = v
---~ end
---~ end
-
fonts.triggers = fonts.triggers or { }
function fonts.define.specify.preset_context(name,parent,features)
@@ -507,11 +503,6 @@ function fonts.define.specify.preset_context(name,parent,features)
setups[name] = tt
end
---~ function fonts.define.specify.context_number(name)
---~ local s = fonts.define.specify.context_setups[name]
---~ return (s and s.number) or -1
---~ end
-
do
-- here we clone features according to languages
@@ -553,7 +544,7 @@ do
end
-function fonts.define.specify.context_tostring(name,kind,separator,yes,no,strict,omit)
+function fonts.define.specify.context_tostring(name,kind,separator,yes,no,strict,omit) -- not used
return aux.hash_to_string(table.merged(fonts[kind].features.default or {},fonts.define.specify.context_setups[name] or {}),separator,yes,no,strict,omit)
end
@@ -565,9 +556,12 @@ function fonts.define.specify.split_context(features)
end
end
-function fonts.define.specify.starred(features)
- if features.detail and features.detail ~= "" then
- features.features.normal = fonts.define.specify.split_context(features.detail)
+local splitter = lpeg.splitat(",")
+
+function fonts.define.specify.starred(features) -- no longer fallbacks here
+ local detail = features.detail
+ if detail and detail ~= "" then
+ features.features.normal = fonts.define.specify.split_context(detail)
else
features.features.normal = { }
end
@@ -609,18 +603,41 @@ introduced later in the development.</p>
fonts.define.last = nil
-function fonts.define.read(name,size,id)
- local specification = fonts.define.analyze(name,size,id)
- if specification.method and fonts.define.specify[specification.method] then
- specification = fonts.define.specify[specification.method](specification)
+function fonts.define.register(fontdata,id)
+ if fontdata and id then
+ local hash = fontdata.hash
+ if not tfm.internalized[hash] then
+ if fonts.trace then
+ logs.report("define font","loading at 2 id %s, hash: %s",id or "?",hash or "?")
+ end
+ tfm.id[id] = fontdata
+ tfm.internalized[hash] = id
+ end
+ end
+end
+
+function fonts.define.registered(hash)
+ local id = tfm.internalized[hash]
+ return id, id and tfm.id[id]
+end
+
+local cache_them = false
+
+function fonts.define.read(specification,size,id) -- id can be optional, name can already be table
+ input.starttiming(fonts)
+ if type(specification) == "string" then
+ specification = fonts.define.analyze(specification,size)
+ end
+ local method = specification.method
+ if method and fonts.define.specify[method] then
+ specification = fonts.define.specify[method](specification)
end
specification = fonts.define.resolve(specification)
local hash = tfm.hash_instance(specification)
- if true then
- --~ local fontdata = containers.read(fonts.cache(),hash) -- for tracing purposes
+ if cache_them then
+ local fontdata = containers.read(fonts.cache(),hash) -- for tracing purposes
end
- local fontdata = tfm.internalized[hash] -- id
- fonts.define.last = fontdata or id
+ local fontdata = fonts.define.registered(hash) -- id
if not fontdata then
if specification.features.vtf and specification.features.vtf.preset then
fontdata = tfm.make(specification)
@@ -630,19 +647,17 @@ function fonts.define.read(name,size,id)
tfm.check_virtual_id(fontdata)
end
end
- if true then
- --~ fontdata = containers.write(fonts.cache(),hash,fontdata) -- for tracing purposes
+ if cache_them then
+ fontdata = containers.write(fonts.cache(),hash,fontdata) -- for tracing purposes
end
- if not tfm.internalized[hash] then
- tfm.id[id] = fontdata
- tfm.internalized[hash] = id
- if fonts.trace then
- logs.report("define font","loading at 2 id %s, hash: %s",id,hash)
+ if fontdata then
+ fontdata.hash = hash
+ if id then
+ fonts.define.register(fontdata,id)
end
- else
- fontdata = tfm.internalized[hash]
end
end
+ fonts.define.last = fontdata or id -- todo ! ! ! ! !
if not fontdata then
logs.report("define font", "unknown font %s, loading aborted",specification.name)
elseif fonts.trace and type(fontdata) == "table" then
@@ -656,9 +671,118 @@ function fonts.define.read(name,size,id)
fontdata.fullname or "?",
file.basename(fontdata.filename or "?"))
end
+ input.stoptiming(fonts)
return fontdata
end
+-- define (two steps)
+
+local P, C, Cc = lpeg.P, lpeg.C, lpeg.Cc
+
+local space = P(" ")
+local spaces = space^0
+local value = C((1-space)^1)
+local rest = C(P(1)^0)
+local scale_none = Cc(0)
+local scale_at = P("at") * Cc(1) * spaces * value
+local scale_sa = P("sa") * Cc(2) * spaces * value
+local scale_mo = P("mo") * Cc(3) * spaces * value
+local scale_scaled = P("scaled") * Cc(4) * spaces * value
+
+local sizepattern = spaces * (scale_at + scale_sa + scale_mo + scale_scaled + scale_none)
+local splitpattern = spaces * value * spaces * rest
+
+local specification --
+
+function fonts.define.command_1(str)
+ input.starttiming(fonts)
+ local fullname, size = splitpattern:match(str)
+ local lookup, name, sub, method, detail = fonts.define.get_specification(fullname)
+ if not name then
+ logs.report("define font","strange definition '%s'",str)
+ texsprint(tex.ctxcatcodes,"\\glet\\somefontname\\defaultfontfile")
+ elseif name == "unknown" then
+ texsprint(tex.ctxcatcodes,"\\glet\\somefontname\\defaultfontfile")
+ else
+ texsprint(tex.ctxcatcodes,format("\\xdef\\somefontname{%s}",name))
+ end
+ -- we can also use a count for the size
+ if size and size ~= "" then
+ local mode, size = sizepattern:match(size)
+ if size and mode then
+ count.scaledfontmode = mode
+ texsprint(tex.ctxcatcodes,format("\\def\\somefontsize{%s}",size))
+ else
+ count.scaledfontmode = 0
+ texsprint(tex.ctxcatcodes,format("\\let\\somefontsize\\empty",size))
+ end
+ else
+ count.scaledfontmode = 0
+ texsprint(tex.ctxcatcodes,format("\\let\\somefontsize\\empty",size))
+ end
+ specification = fonts.define.makespecification(str,lookup,name,sub,method,detail,size)
+end
+
+function fonts.define.command_2(global,cs,name,size,classfeatures,fontfeatures,classfallbacks,fontfallbacks)
+ local trace = fonts.trace
+ -- name is now resolved and size is scaled cf sa/mo
+ local lookup, name, sub, method, detail = fonts.define.get_specification(name or "")
+ -- asome settings can be overloaded
+ if lookup and lookup ~= "" then specification.lookup = lookup end
+ specification.name = name
+ specification.size = size
+ specification.sub = sub
+ if detail and detail ~= "" then
+ specification.method, specification.detail = method or "*", 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
+ end
+ if trace then
+ logs.report("define font","memory usage before: %s",ctx.memused())
+ end
+if fontfallbacks and fontfallbacks ~= "" then
+ specification.fallbacks = fontfallbacks
+elseif classfallbacks and classfallbacks ~= "" then
+ specification.fallbacks = classfallbacks
+end
+ local tfmdata = fonts.define.read(specification,size) -- id not yet known
+ if not tfmdata then
+ logs.report("define font","unable to define %s as \\%s",name,cs)
+ elseif type(tfmdata) == "number" then
+ if trace 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)
+ end
+ tex.definefont(global,cs,tfmdata)
+ -- resolved (when designsize is used):
+ texsprint(tex.ctxcatcodes,format("\\def\\somefontsize{%isp}",tfm.id[tfmdata].size))
+ else
+ -- local t = os.clock(t)
+ local id = font.define(tfmdata)
+ -- print(name,os.clock()-t)
+ tfmdata.id = id
+ fonts.define.register(tfmdata,id)
+ tex.definefont(global,cs,id)
+ tfm.cleanup_table(tfmdata)
+ if fonts.trace 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)
+ end
+ -- resolved (when designsize is used):
+ texsprint(tex.ctxcatcodes,format("\\def\\somefontsize{%isp}",tfmdata.size))
+ --~ if specification.fallbacks then
+ --~ fonts.collections.prepare(specification.fallbacks)
+ --~ end
+ end
+ if trace then
+ logs.report("define font","memory usage after: %s",ctx.memused())
+ end
+ input.stoptiming(fonts)
+end
+
+
--~ table.insert(tfm.readers.sequence,1,'vtf')
--~ function tfm.readers.vtf(specification)