diff options
Diffstat (limited to 'Master/texmf-dist/tex/context/base/font-def.lua')
-rw-r--r-- | Master/texmf-dist/tex/context/base/font-def.lua | 747 |
1 files changed, 331 insertions, 416 deletions
diff --git a/Master/texmf-dist/tex/context/base/font-def.lua b/Master/texmf-dist/tex/context/base/font-def.lua index af6f5f39460..28afe670b55 100644 --- a/Master/texmf-dist/tex/context/base/font-def.lua +++ b/Master/texmf-dist/tex/context/base/font-def.lua @@ -6,7 +6,13 @@ if not modules then modules = { } end modules ['font-def'] = { license = "see context related readme files" } --- check reuse of lmroman1o-regular vs lmr10 +local format, concat, gmatch, match, find, lower = string.format, table.concat, string.gmatch, string.match, string.find, string.lower +local tostring, next = tostring, next + +local trace_defining = false trackers.register("fonts.defining", function(v) trace_defining = v end) + +trackers.register("fonts.loading", "fonts.defining", "otf.loading", "afm.loading", "tfm.loading") +trackers.register("fonts.all", "fonts.*", "otf.*", "afm.*", "tfm.*") --[[ldx-- <p>Here we deal with defining fonts. We do so by intercepting the @@ -16,40 +22,31 @@ default loader that only handles <l n='tfm'/>.</p> fonts = fonts or { } fonts.define = fonts.define or { } fonts.tfm = fonts.tfm or { } +fonts.ids = fonts.ids or { } fonts.vf = fonts.vf or { } fonts.used = fonts.used or { } -fonts.tfm.version = 1.01 -fonts.tfm.cache = containers.define("fonts", "tfm", fonts.tfm.version, false) -- better in font-tfm +local tfm = fonts.tfm +local vf = fonts.vf +local define = fonts.define ---[[ldx-- -<p>Choosing a font by name and specififying its size is only part of the -game. In order to prevent complex commands, <l n='xetex'/> introduced -a method to pass feature information as part of the font name. At the -risk of introducing nasty parsing and compatinility problems, this -syntax was expanded over time.</p> - -<p>For the sake of users who have defined fonts using that syntax, we -will support it, but we will provide additional methods as well. -Normally users will not use this direct way, but use a more abstract -interface.</p> - --ldx]]-- - ---~ name, kind, features = fonts.features.split_xetex("blabla / B : + lnum ; foo = bar ; - whatever ; whow ; + hans ; test = yes") - -fonts.define.method = 3 -- 1: tfm 2: tfm and if not then afm 3: afm and if not then tfm -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 { } - -fonts.tfm.fonts = fonts.tfm.fonts or { } -fonts.tfm.readers = fonts.tfm.readers or { } -fonts.tfm.internalized = fonts.tfm.internalized or { } -- internal tex numbers -fonts.tfm.id = fonts.tfm.id or { } -- font data, maybe use just fonts.ids (faster lookup) - -fonts.tfm.readers.sequence = { 'otf', 'ttf', 'afm', 'tfm' } +tfm.version = 1.01 +tfm.cache = containers.define("fonts", "tfm", tfm.version, false) -- better in font-tfm + +define.method = "afm or tfm" -- afm, tfm, afm or tfm, tfm or afm +define.specify = fonts.define.specify or { } +define.methods = fonts.define.methods or { } + +tfm.fonts = tfm.fonts or { } +tfm.readers = tfm.readers or { } +tfm.internalized = tfm.internalized or { } -- internal tex numbers + +tfm.readers.sequence = { 'otf', 'ttf', 'afm', 'tfm' } + +tfm.auto_afm = true + +local readers = tfm.readers +local sequence = readers.sequence --[[ldx-- <p>We hardly gain anything when we cache the final (pre scaled) @@ -72,28 +69,52 @@ 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 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 specification = lpeg.C(method) * lpeg.C(lpeg.P(1)^1) + local name = lpeg.C((1-sub-specification)^1) + splitter = lpeg.P((lookup + lpeg.Cc("")) * name * (sub + lpeg.Cc("")) * (specification + lpeg.Cc(""))) +end + +function define.get_specification(str) + return splitter:match(str) +end + +function define.register_split(symbol,action) + define.add_specifier(symbol) + define.specify[symbol] = action +end + +function define.makespecification(specification, lookup, name, sub, method, detail, size) + size = size or 655360 + if trace_defining 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 specification.lookup then +--~ lookup = specification.lookup -- can come from xetex [] syntax +--~ specification.lookup = nil +--~ end + 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 @@ -103,55 +124,55 @@ 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 define.analyze(specification, size) + -- can be optimized with locals + local lookup, name, sub, method, detail = define.get_specification(specification or "") + return define.makespecification(specification,lookup, name, sub, method, detail, size) end --[[ldx-- <p>A unique hash value is generated by:</p> --ldx]]-- -function fonts.tfm.hash_features(specification) - if specification.features then +local sortedhashkeys = table.sortedhashkeys + +function tfm.hash_features(specification) + local features = specification.features + if features then local t = { } - local normal = specification.features.normal - if not table.is_empty(normal) then - for _, v in pairs(table.sortedkeys(normal)) do - if v ~= "number" then + local normal = features.normal + if normal and next(normal) then + local f = sortedhashkeys(normal) + for i=1,#f do + local v = f[i] + if v ~= "number" and v ~= "features" then -- i need to figure this out, features t[#t+1] = v .. '=' .. tostring(normal[v]) end end end - local vtf = specification.features.vtf - if not table.is_empty(vtf) then - for _, v in pairs(table.sortedkeys(vtf)) do + local vtf = features.vtf + if vtf and next(vtf) then + local f = sortedhashkeys(vtf) + for i=1,#f do + local v = f[i] t[#t+1] = v .. '=' .. tostring(vtf[v]) end end - if next(t) then - return table.concat(t,"+") +--~ if specification.mathsize then +--~ t[#t+1] = "mathsize=" .. specification.mathsize +--~ end + if #t > 0 then + return concat(t,"+") end end return "unknown" end ---~ function fonts.tfm.hash_instance(specification) ---~ if not specification.hash then ---~ specification.hash = fonts.tfm.hash_features(specification) ---~ end ---~ return specification.hash .. ' @ ' .. tostring(specification.size) ---~ end - fonts.designsizes = { } --[[ldx-- @@ -162,38 +183,57 @@ when we get rid of base mode we can optimize even further by sharing, but then w loose our testcases for <l n='luatex'/>.</p> --ldx]]-- -function fonts.tfm.hash_instance(specification,force) - local hash, size = specification.hash, specification.size +function tfm.hash_instance(specification,force) + local hash, size, fallbacks = specification.hash, specification.size, specification.fallbacks if force or not hash then - hash = fonts.tfm.hash_features(specification) + hash = tfm.hash_features(specification) specification.hash = hash end if size < 1000 and fonts.designsizes[hash] then - size = fonts.tfm.scaled(size, fonts.designsizes[hash]) + size = math.round(tfm.scaled(size, fonts.designsizes[hash])) specification.size = size end - return hash .. ' @ ' .. tostring(size) +--~ local mathsize = specification.mathsize or 0 +--~ if mathsize > 0 then +--~ local textsize = specification.textsize +--~ if fallbacks then +--~ return hash .. ' @ ' .. tostring(size) .. ' [ ' .. tostring(mathsize) .. ' : ' .. tostring(textsize) .. ' ] @ ' .. fallbacks +--~ else +--~ return hash .. ' @ ' .. tostring(size) .. ' [ ' .. tostring(mathsize) .. ' : ' .. tostring(textsize) .. ' ]' +--~ end +--~ else + if fallbacks then + return hash .. ' @ ' .. tostring(size) .. ' @ ' .. fallbacks + else + return hash .. ' @ ' .. tostring(size) + end +--~ end end --[[ldx-- <p>We can resolve the filename using the next function:</p> --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) +function define.resolve(specification) + 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 .. ' @ ' .. fonts.tfm.hash_features(specification) +--~ specification.hash = specification.name .. ' @ ' .. tfm.hash_features(specification) + specification.hash = lower(specification.name .. ' @ ' .. tfm.hash_features(specification)) if specification.sub and specification.sub ~= "" then specification.hash = specification.sub .. ' @ ' .. specification.hash end @@ -216,25 +256,29 @@ features (esp in virtual fonts) so let's not do that now.</p> specification yet.</p> --ldx]]-- -function fonts.tfm.read(specification) - garbagecollector.push() - input.starttiming(fonts) - local hash = fonts.tfm.hash_instance(specification) - local tfmtable = fonts.tfm.fonts[hash] -- hashes by size ! +function tfm.read(specification) + 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 = fonts.tfm.readers[specification.forced](specification) + local forced = specification.forced or "" + if forced ~= "" then + tfmtable = readers[lower(forced)](specification) if not tfmtable then - logs.error("define font",string.format("forced type %s of %s not found",specification.forced,specification.name)) + logs.report("define font","forced type %s of %s not found",forced,specification.name) end else - for _, reader in ipairs(fonts.tfm.readers.sequence) do - if fonts.tfm.readers[reader] then -- not really needed - if fonts.trace then - logs.report("define font",string.format("trying type %s for %s with file %s",reader,specification.name,specification.filename or "unknown")) + for s=1,#sequence do -- reader sequence + local reader = sequence[s] + if readers[reader] then -- not really needed + if trace_defining then + logs.report("define font","trying (sequence driven) type %s for %s with file %s",reader,specification.name,specification.filename or "unknown") + end + tfmtable = readers[reader](specification) + if tfmtable then + break + else + specification.filename = nil end - tfmtable = fonts.tfm.readers[reader](specification) - if tfmtable then break end end end end @@ -244,15 +288,13 @@ function fonts.tfm.read(specification) else tfmtable.embedding = "subset" end - fonts.tfm.fonts[hash] = tfmtable + tfm.fonts[hash] = tfmtable fonts.designsizes[specification.hash] = tfmtable.designsize -- we only know this for sure after loading once --~ tfmtable.mode = specification.features.normal.mode or "base" end end - input.stoptiming(fonts) - garbagecollector.pop() if not tfmtable then - logs.error("define font",string.format("font with name %s is not found",specification.name)) + logs.report("define font","font with name %s is not found",specification.name) end return tfmtable end @@ -261,319 +303,159 @@ end <p>For virtual fonts we need a slightly different approach:</p> --ldx]]-- -function fonts.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) +function tfm.read_and_define(name,size) -- no id + local specification = define.analyze(name,size) + local method = specification.method + if method and define.specify[method] then + specification = define.specify[method](specification) end - specification = fonts.define.resolve(specification) - local hash = fonts.tfm.hash_instance(specification) - local id = fonts.tfm.internalized[hash] + specification = define.resolve(specification) + local hash = tfm.hash_instance(specification) + local id = define.registered(hash) if not id then - local fontdata = fonts.tfm.read(specification) + local fontdata = tfm.read(specification) if fontdata then - if not fonts.tfm.internalized[hash] then - id = font.define(fontdata) - fonts.tfm.id[id] = fontdata - fonts.tfm.internalized[hash] = id - if fonts.trace then - logs.report("define font", string.format("loading at 1 id %s, hash: %s",id,hash)) - end - else - id = fonts.tfm.internalized[hash] - end + fontdata.hash = hash + id = font.define(fontdata) + define.register(fontdata,id) + tfm.cleanup_table(fontdata) else id = 0 -- signal end end - return fonts.tfm.id[id], id + return fonts.ids[id], 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]]-- -function fonts.tfm.readers.opentype(specification,suffix,what) - if fonts.define.auto_otf then - local fullname, tfmtable = nil, nil - fullname = input.findbinfile(texmf.instance,specification.name,suffix) or "" - if fullname == "" then - local fb = fonts.names.old_to_new[specification.name] - if fb then - fullname = input.findbinfile(texmf.instance,fb,suffix) or "" - end - end - if fullname == "" then - local fb = fonts.names.new_to_old[specification.name] - if fb then - fullname = input.findbinfile(texmf.instance,fb,suffix) or "" - end - end - if fullname ~= "" then - specification.filename, specification.format = fullname, what -- hm, so we do set the filename, then - tfmtable = fonts.tfm.read_from_open_type(specification) -- we need to do it for all matches / todo - end - return tfmtable - else - return nil +local function check_tfm(specification,fullname) + -- ofm directive blocks local path search unless set; btw, in context we + -- don't support ofm files anyway as this format is obsolete + local foundname = resolvers.findbinfile(fullname, 'tfm') or "" -- just to be sure + if foundname == "" then + foundname = resolvers.findbinfile(fullname, 'ofm') or "" -- bonus for usage outside context end -end - -function fonts.tfm.readers.otf(specification) return fonts.tfm.readers.opentype(specification,"otf","opentype") end -function fonts.tfm.readers.ttf(specification) return fonts.tfm.readers.opentype(specification,"ttf","truetype") end -function fonts.tfm.readers.ttc(specification) return fonts.tfm.readers.opentype(specification,"ttf","truetype") end -- !! - -function fonts.tfm.readers.afm(specification,method) - local fullname, tfmtable = nil, nil - method = method or fonts.define.method - if method == 2 then - fullname = input.findbinfile(texmf.instance,specification.name,"ofm") or "" - if fullname == "" then - tfmtable = fonts.tfm.read_from_afm(specification) - else -- redundant - specification.filename = fullname - tfmtable = fonts.tfm.read_from_tfm(specification) - end - elseif method == 3 then -- maybe also findbinfile here - if fonts.define.auto_afm then - tfmtable = fonts.tfm.read_from_afm(specification) - end - elseif method == 4 then -- maybe also findbinfile here - tfmtable = fonts.tfm.read_from_afm(specification) - end - return tfmtable -end - -function fonts.tfm.readers.tfm(specification) - local fullname, tfmtable = nil, nil - tfmtable = fonts.tfm.read_from_tfm(specification) - return tfmtable -end - ---[[ldx-- -<p>So far we haven't really dealt with features (or whatever we want -to pass along with the font definition. We distinguish the following -situations:</p> -situations:</p> - -<code> -name:xetex like specs -name@virtual font spec -name*context specification -</code> - -<p>Of course one can always define more.</p> ---ldx]]-- - -function fonts.define.specify.predefined(specification) - if specification.detail ~= "" and fonts.define.methods[specification.detail] then - specification.features.vtf = { preset = specification.detail } + if foundname ~= "" then + specification.filename, specification.format = foundname, "ofm" + return tfm.read_from_tfm(specification) end - return specification end -fonts.define.register_split("@", fonts.define.specify.predefined) - -function fonts.define.specify.colonized(specification) -- xetex mode - local list = { } - if specification.detail and specification.detail ~= "" then - local expanded_features = { } - local function expand(features) - for _,v in pairs(features:split(";")) do - expanded_features[#expanded_features+1] = v - end - end - expand(specification.detail) - for _,v in pairs(expanded_features) do - local a, b = v:match("^%s*(%S+)%s*=%s*(%S+)%s*$") - if a and b then - list[a] = b:is_boolean() - if type(list[a]) == "nil" then - list[a] = b - end - else - local a, b = v:match("^%s*([%+%-]?)%s*(%S+)%s*$") - if a and b then - list[b] = a ~= "-" +local function check_afm(specification,fullname) + local foundname = resolvers.findbinfile(fullname, 'afm') or "" -- just to be sure + if foundname == "" and tfm.auto_afm then + local encoding, shortname = match(fullname,"^(.-)%-(.*)$") -- context: encoding-name.* + if encoding and shortname and fonts.enc.known[encoding] then + shortname = resolvers.findbinfile(shortname,'afm') or "" -- just to be sure + if shortname ~= "" then + foundname = shortname + -- tfm.set_normal_feature(specification,'encoding',encoding) -- will go away + if trace_loading then + logs.report("load afm","stripping encoding prefix from filename %s",afmname) end end end end - specification.features.normal = list - return specification + if foundname ~= "" then + specification.filename, specification.format = foundname, "afm" + return tfm.read_from_afm(specification) + end end -function fonts.tfm.make(specification) - local fvm = fonts.define.methods[specification.features.vtf.preset] - if fvm then - return fvm(specification) +function readers.tfm(specification) + local fullname, tfmtable = specification.filename or "", nil + if fullname == "" then + local forced = specification.forced or "" + if forced ~= "" then + tfmtable = check_tfm(specification,specification.name .. "." .. forced) + end + if not tfmtable then + tfmtable = check_tfm(specification,specification.name) + end else - return nil + tfmtable = check_tfm(specification,fullname) end + return tfmtable end -fonts.define.register_split(":", fonts.define.specify.colonized) - -fonts.define.specify.context_setups = fonts.define.specify.context_setups or { } -fonts.define.specify.context_numbers = fonts.define.specify.context_numbers or { } -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) - if features == "" then - if parent:find("=") then - features = parent - parent = "" +function readers.afm(specification,method) + local fullname, tfmtable = specification.filename or "", nil + if fullname == "" then + local forced = specification.forced or "" + if forced ~= "" then + tfmtable = check_afm(specification,specification.name .. "." .. forced) end - end - local fds = fonts.define.specify - local setups, numbers, synonyms = fds.context_setups, fds.context_numbers, fds.synonyms - local number = (setups[name] and setups[name].number) or 0 - local t = (features == "" and { }) or fonts.otf.meanings.normalize(aux.settings_to_hash(features)) - -- todo: synonyms, and not otf bound - if parent ~= "" then - for p in parent:gmatch("[^, ]+") do - local s = setups[p] - if s then - for k,v in pairs(s) do - if t[k] == nil then - t[k] = v - end - end + if not tfmtable then + method = method or define.method or "afm or tfm" + if method == "tfm" then + tfmtable = check_tfm(specification,specification.name) + elseif method == "afm" then + tfmtable = check_afm(specification,specification.name) + elseif method == "tfm or afm" then + tfmtable = check_tfm(specification,specification.name) or check_afm(specification,specification.name) + else -- method == "afm or tfm" or method == "" then + tfmtable = check_afm(specification,specification.name) or check_tfm(specification,specification.name) end end - end - -- 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) - local default = fonts.otf.features.default - for k,v in pairs(fonts.triggers) do - if type(t[v]) == "nil" then - local vv = default[v] - if vv then t[v] = vv end - end - end - -- sparse 'm so that we get a better hash and less test (experimental - -- optimization) - local tt = { } - for k,v in pairs(t) do - if v then tt[k] = v end - end - -- needed for dynamic features - if number == 0 then - numbers[#numbers+1] = name - tt.number = #numbers else - tt.number = number + tfmtable = check_afm(specification,fullname) end - setups[name] = tt + return tfmtable 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 - - local default = 0 - local setups = fonts.define.specify.context_setups - local numbers = fonts.define.specify.context_numbers - - function fonts.define.specify.context_number(name) - local t = setups[name] - if not t then - return default - elseif t.auto then - local lng = tonumber(tex.language) - local tag = name .. ":" .. lng - local s = setups[tag] - if s then - return s.number or default - else - local script, language = languages.association(lng) - if t.script ~= script or t.language ~= language then - local s = table.fastcopy(t) - local n = #numbers + 1 - setups[tag] = s - numbers[n] = tag - s.number = n - s.script = script - s.language = language - return n - else - setups[tag] = t - return t.number or default - end - end - else - return t.number or default +local function check_otf(specification,suffix,what) + local fullname, tfmtable = resolvers.findbinfile(specification.name,suffix) or "", nil + if fullname == "" then + local fb = fonts.names.old_to_new[specification.name] + if fb then + fullname = resolvers.findbinfile(fb,suffix) or "" end end - -end - -function fonts.define.specify.context_tostring(name,kind,separator,yes,no,strict,omit) - 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 - -function fonts.define.specify.split_context(features) - if fonts.define.specify.context_setups[features] then - return fonts.define.specify.context_setups[features] - else -- ? ? ? - return fonts.define.specify.preset_context("***",features) + if fullname == "" then + local fb = fonts.names.new_to_old[specification.name] + if fb then + fullname = resolvers.findbinfile(fb,suffix) or "" + end end + if fullname ~= "" then + specification.filename, specification.format = fullname, what -- hm, so we do set the filename, then + tfmtable = tfm.read_from_open_type(specification) -- we need to do it for all matches / todo + end + return tfmtable end -function fonts.define.specify.starred(features) - if features.detail and features.detail ~= "" then - features.features.normal = fonts.define.specify.split_context(features.detail) +function readers.opentype(specification,suffix,what) + local forced = specification.forced or "" + if forced == "otf" then + return check_otf(specification,forced,"opentype") + elseif forced == "ttf" then + return check_otf(specification,forced,"truetype") + elseif forced == "ttf" then + return check_otf(specification,forced,"truetype") else - features.features.normal = { } + return check_otf(specification,suffix,what) end - return features end -fonts.define.register_split('*',fonts.define.specify.starred) +function readers.otf (specification) return readers.opentype(specification,"otf","opentype") end +function readers.ttf (specification) return readers.opentype(specification,"ttf","truetype") end +function readers.ttc (specification) return readers.opentype(specification,"ttf","truetype") end -- !! +function readers.dfont(specification) return readers.opentype(specification,"ttf","truetype") end -- !! --[[ldx-- <p>We need to check for default features. For this we provide a helper function.</p> --ldx]]-- -function fonts.define.check(features,defaults) -- nb adapts features ! +function define.check(features,defaults) -- nb adapts features ! local done = false if table.is_empty(features) then features, done = table.fastcopy(defaults), true else - for k,v in pairs(defaults) do + for k,v in next, defaults do if features[k] == nil then features[k], done = v, true end @@ -594,43 +476,84 @@ not gain much. By the way, passing id's back to in the callback was introduced later in the development.</p> --ldx]]-- -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) +define.last = nil + +function define.register(fontdata,id) + if fontdata and id then + local hash = fontdata.hash + if not tfm.internalized[hash] then + if trace_defining then + logs.report("define font","loading at 2 id %s, hash: %s",id or "?",hash or "?") + end + fonts.ids[id] = fontdata + tfm.internalized[hash] = id + end end - specification = fonts.define.resolve(specification) - local hash = fonts.tfm.hash_instance(specification) - if true then - --~ local fontdata = containers.read(fonts.cache(),hash) -- for tracing purposes +end + +function define.registered(hash) + local id = tfm.internalized[hash] + return id, id and fonts.ids[id] +end + +local cache_them = false + +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 = define.methods[specification.features.vtf.preset] + if fvm then + return fvm(specification) + else + return nil + end +end + +function define.read(specification,size,id) -- id can be optional, name can already be table + statistics.starttiming(fonts) + if type(specification) == "string" then + specification = define.analyze(specification,size) + end + local method = specification.method + if method and define.specify[method] then + specification = define.specify[method](specification) + end + specification = define.resolve(specification) + local hash = tfm.hash_instance(specification) + if cache_them then + local fontdata = containers.read(fonts.cache(),hash) -- for tracing purposes end - local fontdata = fonts.tfm.internalized[hash] -- id + local fontdata = define.registered(hash) -- id if not fontdata then if specification.features.vtf and specification.features.vtf.preset then - fontdata = fonts.tfm.make(specification) + fontdata = tfm.make(specification) else - fontdata = fonts.tfm.read(specification) + fontdata = tfm.read(specification) if fontdata then - fonts.tfm.check_virtual_id(fontdata) + 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 fonts.tfm.internalized[hash] then - fonts.tfm.id[id] = fontdata - fonts.tfm.internalized[hash] = id - if fonts.trace then - logs.report("define font", string.format("loading at 2 id %s, hash: %s",id,hash)) + if fontdata then + fontdata.hash = hash + fontdata.cache = "no" + if id then + define.register(fontdata,id) end - else - fontdata = fonts.tfm.internalized[hash] end end + define.last = fontdata or id -- todo ! ! ! ! ! if not fontdata then - logs.error("define font", string.format("unknown font %s, loading aborted",specification.name)) - elseif fonts.trace and type(fontdata) == "table" then - logs.report("define font",string.format("using %s font with id %s, n:%s s:%s b:%s e:%s p:%s f:%s", + logs.report("define font", "unknown font %s, loading aborted",specification.name) + elseif trace_defining and type(fontdata) == "table" then + logs.report("define font","using %s font with id %s, name:%s size:%s bytes:%s encoding:%s fullname:%s filename:%s", fontdata.type or "unknown", id or "?", fontdata.name or "?", @@ -638,41 +561,33 @@ function fonts.define.read(name,size,id) fontdata.encodingbytes or "?", fontdata.encodingname or "unicode", fontdata.fullname or "?", - file.basename(fontdata.filename or "?"))) + file.basename(fontdata.filename or "?")) + end + statistics.stoptiming(fonts) return fontdata end ---~ table.insert(fonts.tfm.readers.sequence,1,'vtf') - ---~ function fonts.tfm.readers.vtf(specification) ---~ if specification.features.vtf and specification.features.vtf.preset then ---~ return fonts.tfm.make(specification) ---~ else ---~ return nil ---~ end ---~ end - -function fonts.vf.find(name) +function vf.find(name) name = file.removesuffix(file.basename(name)) - if fonts.tfm.resolve_vf then + if tfm.resolve_vf then local format = fonts.logger.format(name) if format == 'tfm' or format == 'ofm' then - if fonts.trace then - logs.report("define font",string.format("locating vf for %s",name)) + if trace_defining then + logs.report("define font","locating vf for %s",name) end - return input.findbinfile(texmf.instance,name,"ovf") + return resolvers.findbinfile(name,"ovf") else - if fonts.trace then - logs.report("define font",string.format("vf for %s is already taken care of",name)) + if trace_defining then + logs.report("define font","vf for %s is already taken care of",name) end return nil -- "" end else - if fonts.trace then - logs.report("define font",string.format("locating vf for %s",name)) + if trace_defining then + logs.report("define font","locating vf for %s",name) end - return input.findbinfile(texmf.instance,name,"ovf") + return resolvers.findbinfile(name,"ovf") end end @@ -680,5 +595,5 @@ end <p>We overload both the <l n='tfm'/> and <l n='vf'/> readers.</p> --ldx]]-- -callback.register('define_font' , fonts.define.read) -callback.register('find_vf_file', fonts.vf.find ) -- not that relevant any more +callback.register('define_font' , define.read) +callback.register('find_vf_file', vf.find ) -- not that relevant any more |