summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/tex/context/base/mkxl/font-con.lmt
diff options
context:
space:
mode:
Diffstat (limited to 'Master/texmf-dist/tex/context/base/mkxl/font-con.lmt')
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/font-con.lmt1508
1 files changed, 1508 insertions, 0 deletions
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--
+<p>Here we only implement a few helper functions.</p>
+--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--
+<p>We need to normalize the scale factor (in scaled points). This has to
+do with the fact that <l n='tex'/> uses a negative multiple of 1000 as
+a signal for a font scaled based on the design size.</p>
+--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--
+<p>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.)</p>
+--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--
+<p>The reason why the scaler was originally split, is that for a while we experimented
+with a helper function. However, in practice the <l n='api'/> calls are too slow to
+make this profitable and the <l n='lua'/> based variant was just faster. A days
+wasted day but an experience richer.</p>
+--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--
+<p>A unique hash value is generated by:</p>
+--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--
+<p>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 <l n='luatex'/>.</p>
+--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--
+<p>We need to check for default features. For this we provide
+a helper function.</p>
+--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