From 974640d66e61e81cb197ad96fdff7b08343e4c5a Mon Sep 17 00:00:00 2001 From: Karl Berry Date: Sat, 6 Mar 2021 21:58:15 +0000 Subject: context git-svn-id: svn://tug.org/texlive/trunk@58167 c570f23f-e606-0410-a88d-b1316a301751 --- .../texmf-dist/tex/context/base/mkxl/font-con.lmt | 1508 ++++++++++++++++++++ 1 file changed, 1508 insertions(+) create mode 100644 Master/texmf-dist/tex/context/base/mkxl/font-con.lmt (limited to 'Master/texmf-dist/tex/context/base/mkxl/font-con.lmt') diff --git a/Master/texmf-dist/tex/context/base/mkxl/font-con.lmt b/Master/texmf-dist/tex/context/base/mkxl/font-con.lmt new file mode 100644 index 00000000000..bebc4e4984f --- /dev/null +++ b/Master/texmf-dist/tex/context/base/mkxl/font-con.lmt @@ -0,0 +1,1508 @@ +if not modules then modules = { } end modules ['font-con'] = { + version = 1.001, + comment = "companion to font-ini.mkiv", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +-- some names of table entries will be changed (no _) + +local next, tostring, tonumber, rawget = next, tostring, tonumber, rawget +local format, match, lower, gsub, find = string.format, string.match, string.lower, string.gsub, string.find +local sort, insert, concat = table.sort, table.insert, table.concat +local sortedkeys, sortedhash, serialize, fastcopy = table.sortedkeys, table.sortedhash, table.serialize, table.fastcopy +local derivetable = table.derive +local ioflush = io.flush +local round = math.round +local setmetatable, getmetatable, rawget, rawset = setmetatable, getmetatable, rawget, rawset + +local trace_defining = false trackers.register("fonts.defining", function(v) trace_defining = v end) +local trace_scaling = false trackers.register("fonts.scaling", function(v) trace_scaling = v end) + +local report_defining = logs.reporter("fonts","defining") + +-- watch out: no negative depths and negative eights permitted in regular fonts + +--[[ldx-- +

Here we only implement a few helper functions.

+--ldx]]-- + +local fonts = fonts +local constructors = fonts.constructors or { } +fonts.constructors = constructors +local handlers = fonts.handlers or { } -- can have preloaded tables +fonts.handlers = handlers + +local allocate = utilities.storage.allocate +local setmetatableindex = table.setmetatableindex + +-- will be directives + +constructors.version = 1.01 +constructors.cache = containers.define("fonts", "constructors", constructors.version, false) + +constructors.privateoffset = fonts.privateoffsets.textbase or 0xF0000 + +-- This might become an interface: + +local designsizes = allocate() +constructors.designsizes = designsizes +local loadedfonts = allocate() +constructors.loadedfonts = loadedfonts + +----- scalecommands = fonts.helpers.scalecommands + +--[[ldx-- +

We need to normalize the scale factor (in scaled points). This has to +do with the fact that uses a negative multiple of 1000 as +a signal for a font scaled based on the design size.

+--ldx]]-- + +local factors = { + pt = 65536.0, + bp = 65781.8, +} + +function constructors.setfactor(f) + constructors.factor = factors[f or 'pt'] or factors.pt +end + +constructors.setfactor() + +function constructors.scaled(scaledpoints, designsize) -- handles designsize in sp as well + if scaledpoints < 0 then + local factor = constructors.factor + if designsize then + if designsize > factor then -- or just 1000 / when? mp? + return (- scaledpoints/1000) * designsize -- sp's + else + return (- scaledpoints/1000) * designsize * factor + end + else + return (- scaledpoints/1000) * 10 * factor + end + else + return scaledpoints + end +end + +function constructors.getprivate(tfmdata) + local properties = tfmdata.properties + local private = properties.private + properties.private = private + 1 + return private +end + +function constructors.setmathparameter(tfmdata,name,value) + local m = tfmdata.mathparameters + local c = tfmdata.MathConstants + if m then + m[name] = value + end + if c and c ~= m then + c[name] = value + end +end + +function constructors.getmathparameter(tfmdata,name) + local p = tfmdata.mathparameters or tfmdata.MathConstants + if p then + return p[name] + end +end + +--[[ldx-- +

Beware, the boundingbox is passed as reference so we may not overwrite it +in the process; numbers are of course copies. Here 65536 equals 1pt. (Due to +excessive memory usage in CJK fonts, we no longer pass the boundingbox.)

+--ldx]]-- + +-- The scaler is only used for otf and afm and virtual fonts. If a virtual font has italic +-- correction make sure to set the hasitalics flag. Some more flags will be added in the +-- future. + +--[[ldx-- +

The reason why the scaler was originally split, is that for a while we experimented +with a helper function. However, in practice the calls are too slow to +make this profitable and the based variant was just faster. A days +wasted day but an experience richer.

+--ldx]]-- + +-- experimental, sharing kerns (unscaled and scaled) saves memory +-- local sharedkerns, basekerns = constructors.check_base_kerns(tfmdata) +-- loop over descriptions (afm and otf have descriptions, tfm not) +-- there is no need (yet) to assign a value to chr.tonunicode + +-- constructors.prepare_base_kerns(tfmdata) -- optimalization + +-- we have target.name=metricfile and target.fullname=RealName and target.filename=diskfilename +-- when collapsing fonts, luatex looks as both target.name and target.fullname as ttc files +-- can have multiple subfonts + +function constructors.calculatescale(tfmdata,scaledpoints) + -- implemented in font-ctx.lmt +end + +function constructors.assignmathparameters(target,original) + -- implemented in math-act.lua +end + +function constructors.beforecopyingcharacters(target,original) + -- can be used for additional tweaking +end + +function constructors.aftercopyingcharacters(target,original) + -- can be used for additional tweaking +end + +function constructors.trytosharefont(target,tfmdata) + -- implemented in font-def.lmt +end + +local synonyms = { + exheight = "x_height", + xheight = "x_height", + ex = "x_height", + emwidth = "quad", + em = "quad", + spacestretch = "space_stretch", + stretch = "space_stretch", + spaceshrink = "space_shrink", + shrink = "space_shrink", + extraspace = "extra_space", + xspace = "extra_space", + slantperpoint = "slant", +} + +function constructors.enhanceparameters(parameters) + local mt = getmetatable(parameters) + local getter = function(t,k) + if not k then + return nil + end + local s = synonyms[k] + if s then + return rawget(t,s) or (mt and mt[s]) or nil + end + if k == "spacing" then + return { + width = t.space, + stretch = t.space_stretch, + shrink = t.space_shrink, + extra = t.extra_space, + } + end + return mt and mt[k] or nil + end + local setter = function(t,k,v) + if not k then + return 0 + end + local s = synonyms[k] + if s then + rawset(t,s,v) + elseif k == "spacing" then + if type(v) == "table" then + rawset(t,"space",v.width or 0) + rawset(t,"space_stretch",v.stretch or 0) + rawset(t,"space_shrink",v.shrink or 0) + rawset(t,"extra_space",v.extra or 0) + end + else + rawset(t,k,v) + end + end + setmetatable(parameters, { + __index = getter, + __newindex = setter, + }) +end + +local function mathkerns(v,vdelta) + local k = { } + for i=1,#v do + local entry = v[i] + local height = entry.height + local kern = entry.kern + k[i] = { + height = height and vdelta*height or 0, + kern = kern and vdelta*kern or 0, + } + end + return k +end + +local psfake = 0 + +local function fixedpsname(psname,fallback) + local usedname = psname + if psname and psname ~= "" then + if find(psname," ",1,true) then + usedname = gsub(psname,"[%s]+","-") + else + -- we assume that the name is sane enough (we might sanitize completely some day) + end + elseif not fallback or fallback == "" then + psfake = psfake + 1 + psname = "fakename-" .. psfake + else + -- filenames can be a mess so we do a drastic cleanup + psname = fallback + usedname = gsub(psname,"[^a-zA-Z0-9]+","-") + end + return usedname, psname ~= usedname +end + +function constructors.scale(tfmdata,specification) + local target = { } -- the new table + -- + if tonumber(specification) then + specification = { size = specification } + end + target.specification = specification + -- + local scalecommands = fonts.helpers.scalecommands -- defined later + -- + local scaledpoints = specification.size + local relativeid = specification.relativeid + -- + local properties = tfmdata.properties or { } + local goodies = tfmdata.goodies or { } + local resources = tfmdata.resources or { } + local descriptions = tfmdata.descriptions or { } -- bad news if empty + local characters = tfmdata.characters or { } -- bad news if empty + local changed = tfmdata.changed or { } -- for base mode + local shared = tfmdata.shared or { } + local parameters = tfmdata.parameters or { } + local mathparameters = tfmdata.mathparameters or { } + -- + local targetcharacters = { } + local targetdescriptions = derivetable(descriptions) + local targetparameters = derivetable(parameters) + local targetproperties = derivetable(properties) + local targetgoodies = goodies -- we need to loop so no metatable + target.characters = targetcharacters + target.descriptions = targetdescriptions + target.parameters = targetparameters + -- target.mathparameters = targetmathparameters -- happens elsewhere + target.properties = targetproperties + target.goodies = targetgoodies + target.shared = shared + target.resources = resources + target.unscaled = tfmdata -- the original unscaled one + -- + -- specification.mathsize : 1=text 2=script 3=scriptscript + -- specification.textsize : natural (text)size + -- parameters.mathsize : 1=text 2=script 3=scriptscript >1000 enforced size (feature value other than yes) + -- + local mathsize = tonumber(specification.mathsize) or 0 + local textsize = tonumber(specification.textsize) or scaledpoints + local forcedsize = tonumber(parameters.mathsize ) or 0 -- can be set by the feature "mathsize" + local extrafactor = tonumber(specification.factor ) or 1 + if (mathsize == 2 or forcedsize == 2) and parameters.scriptpercentage then + scaledpoints = parameters.scriptpercentage * textsize / 100 + elseif (mathsize == 3 or forcedsize == 3) and parameters.scriptscriptpercentage then + scaledpoints = parameters.scriptscriptpercentage * textsize / 100 + elseif forcedsize > 1000 then -- safeguard + scaledpoints = forcedsize + else + -- in context x and xx also use mathsize + end + targetparameters.mathsize = mathsize + targetparameters.textsize = textsize + targetparameters.forcedsize = forcedsize + targetparameters.extrafactor = extrafactor + -- + local defaultwidth = resources.defaultwidth or 0 + local defaultheight = resources.defaultheight or 0 + local defaultdepth = resources.defaultdepth or 0 + local units = parameters.units or 1000 + -- + -- boundary keys are no longer needed as we now have a string 'right_boundary' + -- that can be used in relevant tables (kerns and ligatures) ... not that I ever + -- used them + -- + -- boundarychar_label = 0, -- not needed + -- boundarychar = 65536, -- there is now a string 'right_boundary' + -- false_boundarychar = 65536, -- produces invalid tfm in luatex + -- + targetproperties.language = properties.language or "dflt" -- inherited + targetproperties.script = properties.script or "dflt" -- inherited + targetproperties.mode = properties.mode or "base" -- inherited + targetproperties.method = properties.method + -- + local askedscaledpoints = scaledpoints + local scaledpoints, delta = constructors.calculatescale(tfmdata,scaledpoints,nil,specification) -- no shortcut, dan be redefined + -- + local hdelta = delta + local vdelta = delta + -- + target.designsize = parameters.designsize -- not really needed so it might become obsolete + target.units = units + -- + target.size = scaledpoints + -- + target.subfont = properties.subfont + target.cidinfo = properties.cidinfo + target.format = properties.format + -- + local original = properties.original or tfmdata.original + local fontname = properties.fontname or tfmdata.fontname + local fullname = properties.fullname or tfmdata.fullname + local filename = properties.filename or tfmdata.filename + local psname = properties.psname or tfmdata.psname + local name = properties.name or tfmdata.name + -- + -- The psname used in pdf file as well as for selecting subfont in ttc although + -- we don't need that subfont look up here (mapfile stuff). + -- + local psname, psfixed = fixedpsname(psname,fontname or fullname or file.nameonly(filename)) + -- + target.original = original + target.fontname = fontname + target.fullname = fullname + target.filename = filename + target.psname = psname + target.name = name + -- + properties.fontname = fontname + properties.fullname = fullname + properties.filename = filename + properties.psname = psname + properties.name = name + -- expansion (hz) + local expansion = parameters.expansion + if expansion then + target.stretch = expansion.stretch + target.shrink = expansion.shrink + target.step = expansion.step + end + -- slanting + local slantfactor = parameters.slantfactor or 0 + if slantfactor ~= 0 then + target.slant = slantfactor * 1000 + else + target.slant = 0 + end + -- widening + local extendfactor = parameters.extendfactor or 0 + if extendfactor ~= 0 and extendfactor ~= 1 then + hdelta = hdelta * extendfactor + target.extend = extendfactor * 1000 + else + target.extend = 1000 -- extent ? + end + -- squeezing + local squeezefactor = parameters.squeezefactor or 0 + if squeezefactor ~= 0 and squeezefactor ~= 1 then + vdelta = vdelta * squeezefactor + target.squeeze = squeezefactor * 1000 + else + target.squeeze = 1000 -- extent ? + end + -- effects + local mode = parameters.mode or 0 + if mode ~= 0 then + target.mode = mode + end + local width = parameters.width or 0 + if width ~= 0 then + target.width = width * delta * 1000 / 655360 + end + -- + targetparameters.factor = delta + targetparameters.hfactor = hdelta + targetparameters.vfactor = vdelta + targetparameters.size = scaledpoints + targetparameters.units = units + targetparameters.scaledpoints = askedscaledpoints + targetparameters.mode = mode + targetparameters.width = width + -- + local hasquality = parameters.expansion or parameters.protrusion + local hasitalics = properties.hasitalics + local autoitalicamount = properties.autoitalicamount + local stackmath = not properties.nostackmath + local haskerns = properties.haskerns or properties.mode == "base" -- we can have afm in node mode + local hasligatures = properties.hasligatures or properties.mode == "base" -- we can have afm in node mode + local realdimensions = properties.realdimensions + local writingmode = properties.writingmode or "horizontal" + -- + if changed and not next(changed) then + changed = false + end + -- + target.writingmode = writingmode == "vertical" and "vertical" or "horizontal" + -- + target.postprocessors = tfmdata.postprocessors + -- + local targetslant = (parameters.slant or parameters[1] or 0) * factors.pt -- per point + local targetspace = (parameters.space or parameters[2] or 0) * hdelta + local targetspace_stretch = (parameters.space_stretch or parameters[3] or 0) * hdelta + local targetspace_shrink = (parameters.space_shrink or parameters[4] or 0) * hdelta + local targetx_height = (parameters.x_height or parameters[5] or 0) * vdelta + local targetquad = (parameters.quad or parameters[6] or 0) * hdelta + local targetextra_space = (parameters.extra_space or parameters[7] or 0) * hdelta + -- + targetparameters.slant = targetslant -- slantperpoint + targetparameters.space = targetspace + targetparameters.space_stretch = targetspace_stretch + targetparameters.space_shrink = targetspace_shrink + targetparameters.x_height = targetx_height + targetparameters.quad = targetquad + targetparameters.extra_space = targetextra_space + -- + local hshift = parameters.hshift + if hshift then + targetparameters.hshift = delta * hshift + end + local vshift = parameters.vshift + if vshift then + targetparameters.vshift = delta * vshift + end + -- + local ascender = parameters.ascender + if ascender then + targetparameters.ascender = delta * ascender + end + local descender = parameters.descender + if descender then + targetparameters.descender = delta * descender + end + -- + constructors.enhanceparameters(targetparameters) -- official copies for us, now virtual + -- + local scaledwidth = defaultwidth * hdelta + local scaledheight = defaultheight * vdelta + local scaleddepth = defaultdepth * vdelta + -- + local hasmath = (properties.hasmath or next(mathparameters)) and true + -- + if hasmath then + constructors.assignmathparameters(target,tfmdata) -- does scaling and whatever is needed + + properties.hasmath = true + target.nomath = false + target.MathConstants = target.mathparameters + + properties.compactmath = true + target.compactmath = true + + local textscale = parameters.textscale -- or 1000 + local scriptscale = parameters.scriptscale -- or 700 + local scriptscriptscale = parameters.scriptscriptscale -- or 500 + + parameters.textscale = textscale + parameters.scriptscale = scriptscale + parameters.scriptscriptscale = scriptscriptscale + + target.textscale = textscale + target.scriptscale = scriptscale + target.scriptscriptscale = scriptscriptscale + + targetparameters.textscale = textscale + targetparameters.scriptscale = scriptscale + targetparameters.scriptscriptscale = scriptscriptscale + + -- todo: maybe retrofit to font-con.lua + + local oldmath = properties.oldmath + targetproperties.oldmath = oldmath + target.oldmath = oldmath + + else + properties.hasmath = false + target.nomath = true + target.mathparameters = nil -- nop + end + -- + -- During the transition to opentype the engine had troubles with italics so we had some + -- additional code for fixing that. In node mode (text) we don't care much if italics gets + -- passed because the engine does nothing with them then. + -- + if hasmath then + local mathitalics = properties.mathitalics + if mathitalics == false then + if trace_defining then + report_defining("%s italics %s for font %a, fullname %a, filename %a","math",hasitalics and "ignored" or "disabled",name,fullname,filename) + end + hasitalics = false + autoitalicamount = false + end + else + local textitalics = properties.textitalics + if textitalics == false then + if trace_defining then + report_defining("%s italics %s for font %a, fullname %a, filename %a","text",hasitalics and "ignored" or "disabled",name,fullname,filename) + end + hasitalics = false + autoitalicamount = false + end + end + -- + -- end of context specific trickery + -- + if trace_defining then + report_defining("defining tfm, name %a, fullname %a, filename %a, %spsname %a, hscale %a, vscale %a, math %a, italics %a", + name,fullname,filename,psfixed and "(fixed) " or "",psname,hdelta,vdelta, + hasmath and "enabled" or "disabled",hasitalics and "enabled" or "disabled") + end + -- + constructors.beforecopyingcharacters(target,tfmdata) + -- + local sharedkerns = { } + -- + -- we can have a dumb mode (basemode without math etc) that skips most + -- + for unicode, character in next, characters do + local chr, description, index + if changed then + local c = changed[unicode] + if c and c ~= unicode then + if c then + description = descriptions[c] or descriptions[unicode] or character + character = characters[c] or character + index = description.index or c + else + description = descriptions[unicode] or character + index = description.index or unicode + end + else + description = descriptions[unicode] or character + index = description.index or unicode + end + else + description = descriptions[unicode] or character + index = description.index or unicode + end + local width = description.width + local height = description.height + local depth = description.depth + local isunicode = description.unicode + if realdimensions then + -- this is mostly for checking issues + if not height or height == 0 then + local bb = description.boundingbox + local ht = bb[4] + if ht ~= 0 then + height = ht + end + if not depth or depth == 0 then + local dp = -bb[2] + if dp ~= 0 then + depth = dp + end + end + elseif not depth or depth == 0 then + local dp = -description.boundingbox[2] + if dp ~= 0 then + depth = dp + end + end + end + if width then width = hdelta*width else width = scaledwidth end + if height then height = vdelta*height else height = scaledheight end + -- if depth then depth = vdelta*depth else depth = scaleddepth end + if depth and depth ~= 0 then + depth = delta*depth + if isunicode then + chr = { + index = index, + height = height, + depth = depth, + width = width, + unicode = isunicode, + } + else + chr = { + index = index, + height = height, + depth = depth, + width = width, + } + end + else + if isunicode then + chr = { + index = index, + height = height, + width = width, + unicode = isunicode, + } + else + chr = { + index = index, + height = height, + width = width, + } + end + end + if hasquality then + -- we could move these calculations elsewhere (saves calculations) + local ve = character.expansion_factor + if ve then + chr.expansion_factor = ve*1000 -- expansionfactor, hm, can happen elsewhere + end + local vl = character.left_protruding + if vl then + chr.left_protruding = width*vl + end + local vr = character.right_protruding + if vr then + chr.right_protruding = width*vr + end + end + -- + if hasmath then + -- + -- todo, just operate on descriptions.math + local vn = character.next + if vn then + chr.next = vn + else + local vv = character.vert_variants + if vv then + local t = { } + for i=1,#vv do + local vvi = vv[i] + local s = vvi["start"] or 0 + local e = vvi["end"] or 0 + local a = vvi["advance"] or 0 + t[i] = { -- zero check nicer for 5.3 + ["start"] = s == 0 and 0 or s * vdelta, + ["end"] = e == 0 and 0 or e * vdelta, + ["advance"] = a == 0 and 0 or a * vdelta, + ["extender"] = vvi["extender"], + ["glyph"] = vvi["glyph"], + } + end + chr.vert_variants = t + else + local hv = character.horiz_variants + if hv then + local t = { } + for i=1,#hv do + local hvi = hv[i] + local s = hvi["start"] or 0 + local e = hvi["end"] or 0 + local a = hvi["advance"] or 0 + t[i] = { -- zero check nicer for 5.3 + ["start"] = s == 0 and 0 or s * hdelta, + ["end"] = e == 0 and 0 or e * hdelta, + ["advance"] = a == 0 and 0 or a * hdelta, + ["extender"] = hvi["extender"], + ["glyph"] = hvi["glyph"], + } + end + chr.horiz_variants = t + end + end + -- todo also check mathitalics (or that one can go away) + end + local vi = character.vert_italic + if vi and vi ~= 0 then + chr.vert_italic = vi*hdelta + end + local va = character.accent + if va then + chr.top_accent = vdelta*va + end + if stackmath then + local mk = character.mathkerns + if mk then + local tr = mk.topright + local tl = mk.topleft + local br = mk.bottomright + local bl = mk.bottomleft + chr.mathkern = { -- singular -> should be patched in luatex ! + top_right = tr and mathkerns(tr,vdelta) or nil, + top_left = tl and mathkerns(tl,vdelta) or nil, + bottom_right = br and mathkerns(br,vdelta) or nil, + bottom_left = bl and mathkerns(bl,vdelta) or nil, + } + end + end + if hasitalics then + local vi = character.italic + if vi and vi ~= 0 then + chr.italic = vi*hdelta + end + end + elseif autoitalicamount then -- itlc feature + local vi = description.italic + if not vi then + local bb = description.boundingbox + if bb then + local vi = bb[3] - description.width + autoitalicamount + if vi > 0 then -- < 0 indicates no overshoot or a very small auto italic + chr.italic = vi*hdelta + end + else + -- report_defining("no boundingbox for character %C in font %a, fullname %a, filename %a",unicode,name,fullname,filename) + end + elseif vi ~= 0 then + chr.italic = vi*hdelta + end + elseif hasitalics then -- unlikely + local vi = character.italic + if vi and vi ~= 0 then + chr.italic = vi*hdelta + end + end + if haskerns then + local vk = character.kerns + if vk then + local s = sharedkerns[vk] + if not s then + s = { } + for k,v in next, vk do s[k] = v*hdelta end + sharedkerns[vk] = s + end + chr.kerns = s + end + end + if hasligatures then + local vl = character.ligatures + if vl then + if true then + chr.ligatures = vl -- shared + else + local tt = { } + for i, l in next, vl do + tt[i] = l + end + chr.ligatures = tt + end + end + end + local vc = character.commands + if vc then + chr.commands = scalecommands(vc,hdelta,vdelta) + end + targetcharacters[unicode] = chr + end + -- + properties.setitalics = hasitalics -- for postprocessing + -- + -- if hasmath then properties.mode = "none" end -- possible optimization but it needs testing + -- + constructors.aftercopyingcharacters(target,tfmdata) + -- + constructors.trytosharefont(target,tfmdata) + -- + -- catch inconsistencies (for now) + -- + local vfonts = target.fonts + if not vfonts or #vfonts == 0 then + target.fonts = { { id = 0 } } + end + -- + return target +end + +function constructors.finalize(tfmdata) + if tfmdata.properties and tfmdata.properties.finalized then + return + end + -- + if not tfmdata.characters then + return nil + end + -- + if not tfmdata.goodies then + tfmdata.goodies = { } + end + -- + local parameters = tfmdata.parameters + if not parameters then + return nil + end + -- + if not parameters.expansion then + parameters.expansion = { + stretch = tfmdata.stretch or 0, + shrink = tfmdata.shrink or 0, + step = tfmdata.step or 0, + } + end + -- + if not parameters.size then + parameters.size = tfmdata.size + end + -- + if not parameters.mode then + parameters.mode = 0 + end + -- + if not parameters.width then + parameters.width = 0 + end + -- + if not parameters.slantfactor then + parameters.slantfactor = (tfmdata.slant or 0)/1000 + end + -- + if not parameters.extendfactor then + parameters.extendfactor = (tfmdata.extend or 1000)/1000 + end + -- + if not parameters.squeezefactor then + parameters.squeezefactor = (tfmdata.squeeze or 1000)/1000 + end + -- + local designsize = parameters.designsize + if designsize then + parameters.minsize = tfmdata.minsize or designsize + parameters.maxsize = tfmdata.maxsize or designsize + else + designsize = factors.pt * 10 + parameters.designsize = designsize + parameters.minsize = designsize + parameters.maxsize = designsize + end + parameters.minsize = tfmdata.minsize or parameters.designsize + parameters.maxsize = tfmdata.maxsize or parameters.designsize + -- + if not parameters.units then + parameters.units = tfmdata.units or 1000 + end + -- + if not tfmdata.descriptions then + local descriptions = { } -- yes or no + setmetatableindex(descriptions, function(t,k) local v = { } t[k] = v return v end) + tfmdata.descriptions = descriptions + end + -- + local properties = tfmdata.properties + if not properties then + properties = { } + tfmdata.properties = properties + end + -- + properties.fontname = properties.fontname or tfmdata.fontname + properties.filename = properties.filename or tfmdata.filename + properties.fullname = properties.fullname or tfmdata.fullname + properties.name = properties.name or tfmdata.name + properties.psname = properties.psname or tfmdata.psname + -- + properties.subfont = tfmdata.subfont or nil + properties.cidinfo = tfmdata.cidinfo or nil + properties.format = tfmdata.format or "type1" + properties.writingmode = tfmdata.writingmode or "horizontal" + properties.usedbitmap = tfmdata.usedbitmap + -- + if not tfmdata.resources then + tfmdata.resources = { } + end + if not tfmdata.shared then + tfmdata.shared = { } + end + -- + -- tfmdata.fonts + -- tfmdata.unscaled + -- + if not properties.hasmath then + properties.hasmath = not tfmdata.nomath + end + -- + tfmdata.MathConstants = nil + tfmdata.postprocessors = nil + -- + tfmdata.fontname = nil + tfmdata.filename = nil + tfmdata.fullname = nil + tfmdata.name = nil -- most tricky part + tfmdata.psname = nil + -- + tfmdata.subfont = nil + tfmdata.cidinfo = nil + tfmdata.format = nil + tfmdata.nomath = nil + tfmdata.designsize = nil + -- + tfmdata.size = nil + tfmdata.stretch = nil + tfmdata.shrink = nil + tfmdata.step = nil + tfmdata.slant = nil + tfmdata.extend = nil + tfmdata.squeeze = nil + tfmdata.mode = nil + tfmdata.width = nil + tfmdata.units = nil + -- + tfmdata.cache = nil + -- + properties.finalized = true + -- + return tfmdata +end + +--[[ldx-- +

A unique hash value is generated by:

+--ldx]]-- + +local hashmethods = { } +constructors.hashmethods = hashmethods + +function constructors.hashfeatures(specification) -- will be overloaded + local features = specification.features + if features then + local t, n = { }, 0 + for category, list in sortedhash(features) do + if next(list) then + local hasher = hashmethods[category] + if hasher then + local hash = hasher(list) + if hash then + n = n + 1 + t[n] = category .. ":" .. hash + end + end + end + end + if n > 0 then + return concat(t," & ") + end + end + return "unknown" +end + +hashmethods.normal = function(list) + local s = { } + local n = 0 + for k, v in next, list do + if not k then + -- no need to add to hash + elseif k == "number" or k == "features" then + -- no need to add to hash (maybe we need a skip list) + else + n = n + 1 + if type(v) == "table" then + -- table.sequenced + local t = { } + local m = 0 + for k, v in next, v do + m = m + 1 + t[m] = k .. '=' .. tostring(v) + end + sort(t) + s[n] = k .. '={' .. concat(t,",") .. "}" + else + s[n] = k .. '=' .. tostring(v) + end + end + end + if n > 0 then + sort(s) + return concat(s,"+") + end +end + +--[[ldx-- +

In principle we can share tfm tables when we are in need for a font, but then +we need to define a font switch as an id/attr switch which is no fun, so in that +case users can best use dynamic features ... so, we will not use that speedup. Okay, +when we get rid of base mode we can optimize even further by sharing, but then we +loose our testcases for .

+--ldx]]-- + +function constructors.hashinstance(specification,force) + -- implemented in font-ctx.lmt +end + +function constructors.setname(tfmdata,specification) + -- dummy (still called in generic font-otl) +end + +function constructors.checkedfilename(data) + local foundfilename = data.foundfilename + if not foundfilename then + local askedfilename = data.filename or "" + if askedfilename ~= "" then + askedfilename = resolvers.resolve(askedfilename) -- no shortcut + foundfilename = resolvers.findbinfile(askedfilename,"") or "" + if foundfilename == "" then + report_defining("source file %a is not found",askedfilename) + foundfilename = resolvers.findbinfile(file.basename(askedfilename),"") or "" + if foundfilename ~= "" then + report_defining("using source file %a due to cache mismatch",foundfilename) + end + end + end + data.foundfilename = foundfilename + end + return foundfilename +end + +local formats = allocate() +fonts.formats = formats + +setmetatableindex(formats, function(t,k) + local l = lower(k) + if rawget(t,k) then + t[k] = l + return l + end + return rawget(t,file.suffix(l)) +end) + +do + + local function setindeed(mode,source,target,group,name,position) + local action = source[mode] + if not action then + return + end + local t = target[mode] + if not t then + report_defining("fatal error in setting feature %a, group %a, mode %a",name,group,mode) + os.exit() + elseif position then + -- todo: remove existing + insert(t, position, { name = name, action = action }) + else + for i=1,#t do + local ti = t[i] + if ti.name == name then + ti.action = action + return + end + end + insert(t, { name = name, action = action }) + end + end + + local function set(group,name,target,source) + target = target[group] + if not target then + report_defining("fatal target error in setting feature %a, group %a",name,group) + os.exit() + end + local source = source[group] + if not source then + report_defining("fatal source error in setting feature %a, group %a",name,group) + os.exit() + end + local position = source.position + setindeed("node",source,target,group,name,position) + setindeed("base",source,target,group,name,position) + setindeed("plug",source,target,group,name,position) + end + + local function register(where,specification) + local name = specification.name + if name and name ~= "" then + local default = specification.default + local description = specification.description + local initializers = specification.initializers + local processors = specification.processors + local manipulators = specification.manipulators + local modechecker = specification.modechecker + if default then + where.defaults[name] = default + end + if description and description ~= "" then + where.descriptions[name] = description + end + if initializers then + set('initializers',name,where,specification) + end + if processors then + set('processors', name,where,specification) + end + if manipulators then + set('manipulators',name,where,specification) + end + if modechecker then + where.modechecker = modechecker + end + end + end + + constructors.registerfeature = register + + function constructors.getfeatureaction(what,where,mode,name) + what = handlers[what].features + if what then + where = what[where] + if where then + mode = where[mode] + if mode then + for i=1,#mode do + local m = mode[i] + if m.name == name then + return m.action + end + end + end + end + end + end + + local newfeatures = { } + constructors.newfeatures = newfeatures -- downward compatible + constructors.features = newfeatures + + local function setnewfeatures(what) + local handler = handlers[what] + local features = handler.features + if not features then + local tables = handler.tables -- can be preloaded + local statistics = handler.statistics -- can be preloaded + features = allocate { + defaults = { }, + descriptions = tables and tables.features or { }, + used = statistics and statistics.usedfeatures or { }, + initializers = { base = { }, node = { }, plug = { } }, + processors = { base = { }, node = { }, plug = { } }, + manipulators = { base = { }, node = { }, plug = { } }, + finalizers = { base = { }, node = { }, plug = { } }, + } + features.register = function(specification) return register(features,specification) end + handler.features = features -- will also become hidden + end + return features + end + + setmetatable(newfeatures, { + __call = function(t,k) local v = t[k] return v end, + __index = function(t,k) local v = setnewfeatures(k) t[k] = v return v end, + }) + +end + +do + + local newhandler = { } + constructors.handlers = newhandler -- downward compatible + constructors.newhandler = newhandler + + local function setnewhandler(what) -- could be a metatable newindex + local handler = handlers[what] + if not handler then + handler = { } + handlers[what] = handler + end + return handler + end + + setmetatable(newhandler, { + __call = function(t,k) local v = t[k] return v end, + __index = function(t,k) local v = setnewhandler(k) t[k] = v return v end, + }) + +end + +do + -- a pitty that we need to be generic as we have nicer mechanisms for this ... + + local newenhancer = { } + constructors.enhancers = newenhancer + constructors.newenhancer = newenhancer + + local function setnewenhancer(format) + + local handler = handlers[format] + local enhancers = handler.enhancers + + if not enhancers then + + local actions = allocate() -- no need to allocate thee + local before = allocate() + local after = allocate() + local order = allocate() + local known = { } + local nofsteps = 0 + local patches = { before = before, after = after } + + local trace = false + local report = logs.reporter("fonts",format .. " enhancing") + + trackers.register(format .. ".loading", function(v) trace = v end) + + local function enhance(name,data,filename,raw) + local enhancer = actions[name] + if enhancer then + if trace then + report("apply enhancement %a to file %a",name,filename) + ioflush() + end + enhancer(data,filename,raw) + else + -- no message as we can have private ones + end + end + + local function apply(data,filename,raw) + local basename = file.basename(lower(filename)) + if trace then + report("%s enhancing file %a","start",filename) + end + ioflush() -- we want instant messages + for e=1,nofsteps do + local enhancer = order[e] + local b = before[enhancer] + if b then + for pattern, action in next, b do + if find(basename,pattern) then + action(data,filename,raw) + end + end + end + enhance(enhancer,data,filename,raw) -- we have one installed: check extra features + local a = after[enhancer] + if a then + for pattern, action in next, a do + if find(basename,pattern) then + action(data,filename,raw) + end + end + end + ioflush() -- we want instant messages + end + if trace then + report("%s enhancing file %a","stop",filename) + end + ioflush() -- we want instant messages + end + + local function register(what,action) + if action then + if actions[what] then + -- overloading, e.g."check extra features" + else + nofsteps = nofsteps + 1 + order[nofsteps] = what + known[what] = nofsteps + end + actions[what] = action + else + report("bad enhancer %a",what) + end + end + + -- We used to have a lot of enhancers but no longer with the new font loader. The order of enhancers + -- is the order of definition. The before/after patches are there for old times sake and happen + -- before or after a (named) enhancer. An example of a set enhancer is "check extra features" so one + -- one set patches before or after that is applied. Unknown enhancers are auto-registered. It's a bit + -- messy but we keep it for compatibility reasons. + -- + -- fonts.handlers.otf.enhancers.patches.register("before","some patches","somefont",function(data,filename) + -- print("!!!!!!!") -- before | after + -- end) + -- + -- fonts.handlers.otf.enhancers.register("more patches",function(data,filename) + -- print("???????") -- enhance + -- end) + + local function patch(what,where,pattern,action) + local pw = patches[what] + if pw then + local ww = pw[where] + if ww then + ww[pattern] = action + else + pw[where] = { [pattern] = action } + if not known[where] then + nofsteps = nofsteps + 1 + order[nofsteps] = where + known[where] = nofsteps + end + end + end + end + + enhancers = { + register = register, + apply = apply, + patch = patch, + report = report, + patches = { + register = patch, + report = report, + }, -- for old times sake + } + + handler.enhancers = enhancers + end + return enhancers + end + + setmetatable(newenhancer, { + __call = function(t,k) local v = t[k] return v end, + __index = function(t,k) local v = setnewenhancer(k) t[k] = v return v end, + }) + +end + +--[[ldx-- +

We need to check for default features. For this we provide +a helper function.

+--ldx]]-- + +function constructors.checkedfeatures(what,features) + local defaults = handlers[what].features.defaults + if features and next(features) then + features = fastcopy(features) -- can be inherited (mt) but then no loops possible + for key, value in next, defaults do + if features[key] == nil then + features[key] = value + end + end + return features + else + return fastcopy(defaults) -- we can change features in place + end +end + +-- before scaling + +function constructors.initializefeatures(what,tfmdata,features,trace,report) + if features and next(features) then + local properties = tfmdata.properties or { } -- brrr + local whathandler = handlers[what] + local whatfeatures = whathandler.features + local whatmodechecker = whatfeatures.modechecker + -- properties.mode can be enforces (for instance in font-otd) + local mode = properties.mode or (whatmodechecker and whatmodechecker(tfmdata,features,features.mode)) or features.mode or "base" + properties.mode = mode -- also status + features.mode = mode -- both properties.mode or features.mode can be changed + -- + local done = { } + while true do + local redo = false + local initializers = whatfeatures.initializers[mode] + if initializers then + for i=1,#initializers do + local step = initializers[i] + local feature = step.name +-- we could intercept mode here .. needs a rewrite of this whole loop then but it's cleaner that way + local value = features[feature] + if not value then + -- disabled + elseif done[feature] then + -- already done + else + local action = step.action + if trace then + report("initializing feature %a to %a for mode %a for font %a",feature, + value,mode,tfmdata.properties.fullname) + end + action(tfmdata,value,features) -- can set mode (e.g. goodies) so it can trigger a restart + if mode ~= properties.mode or mode ~= features.mode then + if whatmodechecker then + properties.mode = whatmodechecker(tfmdata,features,properties.mode) -- force checking + features.mode = properties.mode + end + if mode ~= properties.mode then + mode = properties.mode + redo = true + end + end + done[feature] = true + end + if redo then + break + end + end + if not redo then + break + end + else + break + end + end + properties.mode = mode -- to be sure + return true + else + return false + end +end + +-- while typesetting + +function constructors.collectprocessors(what,tfmdata,features,trace,report) + local processes = { } + local nofprocesses = 0 + if features and next(features) then + local properties = tfmdata.properties + local whathandler = handlers[what] + local whatfeatures = whathandler.features + local whatprocessors = whatfeatures.processors + local mode = properties.mode + local processors = whatprocessors[mode] + if processors then + for i=1,#processors do + local step = processors[i] + local feature = step.name + if features[feature] then + local action = step.action + if trace then + report("installing feature processor %a for mode %a for font %a",feature,mode,tfmdata.properties.fullname) + end + if action then + nofprocesses = nofprocesses + 1 + processes[nofprocesses] = action + end + end + end + elseif trace then + report("no feature processors for mode %a for font %a",mode,properties.fullname) + end + end + return processes +end + +-- after scaling + +local function apply(key,what,tfmdata,features,trace,report) + if features and next(features) then + local properties = tfmdata.properties + local whathandler = handlers[what] + local whatfeatures = whathandler.features + local whatactions = whatfeatures[key] + local mode = properties.mode + local actions = whatactions[mode] + if actions then + for i=1,#actions do + local step = actions[i] + local feature = step.name + local value = features[feature] + if value then + local action = step.action + if trace then + report("applying feature %s %a for mode %a for font %a",key,feature,mode,properties.fullname) + end + if action then + action(tfmdata,feature,value) + end + end + end + end + end +end + +function constructors.applymanipulators(what,tfmdata,features,trace,report) + if features and next(features) then + apply("manipulators",what,tfmdata,features,trace,report) + end +end + +function constructors.applyfinalizers(what,tfmdata,features,trace,report) + if features and next(features) then + apply("finalizers",what,tfmdata,features,trace,report) + end +end + +function constructors.addcoreunicodes(unicodes) -- maybe make this a metatable if used at all + if not unicodes then + unicodes = { } + end + unicodes.space = 0x0020 + unicodes.hyphen = 0x002D + unicodes.zwj = 0x200D + unicodes.zwnj = 0x200C + return unicodes +end -- cgit v1.2.3