summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/tex/context/base/font-tfm.lua
diff options
context:
space:
mode:
Diffstat (limited to 'Master/texmf-dist/tex/context/base/font-tfm.lua')
-rw-r--r--Master/texmf-dist/tex/context/base/font-tfm.lua1114
1 files changed, 614 insertions, 500 deletions
diff --git a/Master/texmf-dist/tex/context/base/font-tfm.lua b/Master/texmf-dist/tex/context/base/font-tfm.lua
index d0a838bcd50..b2015723e36 100644
--- a/Master/texmf-dist/tex/context/base/font-tfm.lua
+++ b/Master/texmf-dist/tex/context/base/font-tfm.lua
@@ -6,94 +6,103 @@ if not modules then modules = { } end modules ['font-tfm'] = {
license = "see context related readme files"
}
+local utf = unicode.utf8
+
+local next, format, match, lower, gsub = next, string.format, string.match, string.lower, string.gsub
+local concat, sortedkeys, utfbyte, serialize = table.concat, table.sortedkeys, utf.byte, table.serialize
+
+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)
+
+-- tfmdata has also fast access to indices and unicodes
+-- to be checked: otf -> tfm -> tfmscaled
+--
+-- watch out: no negative depths and negative eights permitted in regular fonts
+
--[[ldx--
<p>Here we only implement a few helper functions.</p>
--ldx]]--
-fonts = fonts or { }
-fonts.loaded = fonts.loaded or { }
-fonts.dontembed = fonts.dontembed or { }
-fonts.logger = fonts.logger or { }
-fonts.loadtime = 0
-fonts.tfm = fonts.tfm or { }
-fonts.triggers = fonts.triggers or { } -- brrr
+fonts = fonts or { }
+fonts.tfm = fonts.tfm or { }
+fonts.ids = fonts.ids or { }
+
+local tfm = fonts.tfm
+
+fonts.loaded = fonts.loaded or { }
+fonts.dontembed = fonts.dontembed or { }
+fonts.triggers = fonts.triggers or { } -- brrr
+fonts.initializers = fonts.initializers or { }
+fonts.initializers.common = fonts.initializers.common or { }
+
+local fontdata = fonts.ids
+local glyph = node.id('glyph')
+local set_attribute = node.set_attribute
--[[ldx--
<p>The next function encapsulates the standard <l n='tfm'/> loader as
supplied by <l n='luatex'/>.</p>
--ldx]]--
-fonts.tfm.resolve_vf = true -- false
+tfm.resolve_vf = true -- false
+tfm.share_base_kerns = false -- true (.5 sec slower on mk but brings down mem from 410M to 310M, beware: then script/lang share too)
+tfm.mathactions = { }
-function fonts.tfm.enhance(tfmdata,specification)
+function tfm.enhance(tfmdata,specification)
local name, size = specification.name, specification.size
- local encoding, filename = name:match("^(.-)%-(.*)$") -- context: encoding-name.*
+ local encoding, filename = match(name,"^(.-)%-(.*)$") -- context: encoding-name.*
if filename and encoding and fonts.enc.known[encoding] then
local data = fonts.enc.load(encoding)
if data then
local characters = tfmdata.characters
tfmdata.encoding = encoding
local vector = data.vector
- for k, v in pairs(characters) do
+ local original = { }
+ for k, v in next, characters do
v.name = vector[k]
v.index = k
+ original[k] = v
end
- for k,v in pairs(data.unicodes) do
+ for k,v in next, data.unicodes do
if k ~= v then
- -- if not characters[k] then
- if fonts.trace then
- logs.report("define font",string.format("mapping %s onto %s",k,v))
- end
- characters[k] = characters[v]
- -- end
+ if trace_defining then
+ logs.report("define font","mapping %s onto %s",k,v)
+ end
+ characters[k] = original[v]
end
end
end
end
end
-function fonts.tfm.read_from_tfm(specification)
- local fname, tfmdata = specification.filename, nil
- if fname then
- -- safeguard, we use tfm as fallback
- local suffix = file.extname(fname)
- if suffix ~= "" and suffix ~= "tfm" then
- fname = ""
- end
- end
- if not fname or fname == "" then
- fname = input.findbinfile(texmf.instance, specification.name, 'ofm')
- else
- fname = input.findbinfile(texmf.instance, fname, 'ofm')
- end
- if fname and fname ~= "" then
- if fonts.trace then
- logs.report("define font",string.format("loading tfm file %s at size %s",fname,specification.size))
+function tfm.read_from_tfm(specification)
+ local fname, tfmdata = specification.filename or "", nil
+ if fname ~= "" then
+ if trace_defining then
+ logs.report("define font","loading tfm file %s at size %s",fname,specification.size)
end
tfmdata = font.read_tfm(fname,specification.size) -- not cached, fast enough
if tfmdata then
- if fonts.tfm.resolve_vf then
+ tfmdata.descriptions = tfmdata.descriptions or { }
+ if tfm.resolve_vf then
fonts.logger.save(tfmdata,file.extname(fname),specification) -- strange, why here
- fname = input.findbinfile(texmf.instance, specification.name, 'ovf')
+ fname = resolvers.findbinfile(specification.name, 'ovf')
if fname and fname ~= "" then
local vfdata = font.read_vf(fname,specification.size) -- not cached, fast enough
if vfdata then
local chars = tfmdata.characters
- for k,v in pairs(vfdata.characters) do -- no ipairs, can have holes
+ for k,v in next, vfdata.characters do
chars[k].commands = v.commands
end
tfmdata.type = 'virtual'
tfmdata.fonts = vfdata.fonts
end
end
---~ print(table.serialize(tfmdata))
end
- fonts.tfm.enhance(tfmdata,specification)
- end
- else
- if fonts.trace then
- logs.report("define font",string.format("loading tfm with name %s fails",specification.name))
+ tfm.enhance(tfmdata,specification)
end
+ elseif trace_defining then
+ logs.report("define font","loading tfm with name %s fails",specification.name)
end
return tfmdata
end
@@ -104,51 +113,39 @@ 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]]--
-do
-
- local factors = {
- pt = 65536.0,
- bp = 65781.8,
- }
-
- function fonts.tfm.setfactor(f)
- fonts.tfm.factor = factors[f or 'pt'] or factors.pt
- end
-
- fonts.tfm.setfactor()
+local factors = {
+ pt = 65536.0,
+ bp = 65781.8,
+}
+function tfm.setfactor(f)
+ tfm.factor = factors[f or 'pt'] or factors.pt
end
-function fonts.tfm.scaled(scaledpoints, designsize) -- handles designsize in sp as well
+tfm.setfactor()
+
+function tfm.scaled(scaledpoints, designsize) -- handles designsize in sp as well
if scaledpoints < 0 then
if designsize then
- if designsize > fonts.tfm.factor then -- or just 1000 / when? mp?
+ if designsize > tfm.factor then -- or just 1000 / when? mp?
return (- scaledpoints/1000) * designsize -- sp's
else
- return (- scaledpoints/1000) * designsize * fonts.tfm.factor
+ return (- scaledpoints/1000) * designsize * tfm.factor
end
else
- return (- scaledpoints/1000) * 10 * fonts.tfm.factor
+ return (- scaledpoints/1000) * 10 * tfm.factor
end
else
return scaledpoints
end
end
---~ function fonts.tfm.scaled(scaledpoints, designsize)
---~ if scaledpoints < 0 then
---~ return (- scaledpoints/1000) * (designsize or 10) * fonts.tfm.factor
---~ else
---~ return scaledpoints
---~ end
---~ end
-
--[[ldx--
<p>Before a font is passed to <l n='tex'/> we scale it. Here we also need
to scale virtual characters.</p>
--ldx]]--
-function fonts.tfm.get_virtual_id(tfmdata)
+function tfm.get_virtual_id(tfmdata)
-- since we don't know the id yet, we use 0 as signal
if not tfmdata.fonts then
tfmdata.type = "virtual"
@@ -160,14 +157,16 @@ function fonts.tfm.get_virtual_id(tfmdata)
end
end
-function fonts.tfm.check_virtual_id(tfmdata, id)
+function tfm.check_virtual_id(tfmdata, id)
if tfmdata and tfmdata.type == "virtual" then
if not tfmdata.fonts or #tfmdata.fonts == 0 then
tfmdata.type, tfmdata.fonts = "real", nil
else
- for k,v in ipairs(tfmdata.fonts) do
- if v.id and v.id == 0 then
- v.id = id
+ local vfonts = tfmdata.fonts
+ for f=1,#vfonts do
+ local fnt = vfonts[f]
+ if fnt.id and fnt.id == 0 then
+ fnt.id = id
end
end
end
@@ -182,24 +181,91 @@ excessive memory usage in CJK fonts, we no longer pass the boundingbox.)</p>
fonts.trace_scaling = false
-function fonts.tfm.do_scale(tfmtable, scaledpoints)
- local trace = fonts.trace_scaling
+-- the following hack costs a bit of runtime but safes memory
+--
+-- basekerns are scaled and will be hashed by table id
+-- sharedkerns are unscaled and are be hashed by concatenated indexes
+
+function tfm.check_base_kerns(tfmdata)
+ if tfm.share_base_kerns then
+ local sharedkerns = tfmdata.sharedkerns
+ if sharedkerns then
+ local basekerns = { }
+ tfmdata.basekerns = basekerns
+ return sharedkerns, basekerns
+ end
+ end
+ return nil, nil
+end
+
+function tfm.prepare_base_kerns(tfmdata)
+ if tfm.share_base_kerns and not tfmdata.sharedkerns then
+ local sharedkerns = { }
+ tfmdata.sharedkerns = sharedkerns
+ for u, chr in next, tfmdata.characters do
+ local kerns = chr.kerns
+ if kerns then
+ local hash = concat(sortedkeys(kerns), " ")
+ local base = sharedkerns[hash]
+ if not base then
+ sharedkerns[hash] = kerns
+ else
+ chr.kerns = base
+ end
+ end
+ end
+ end
+end
+
+-- we can have cache scaled characters when we are in node mode and don't have
+-- protruding and expansion: hash == fullname @ size @ protruding @ expansion
+-- but in practice (except from mk) the otf hash will be enough already so it
+-- makes no sense to mess up the code now
+
+local charactercache = { }
+
+-- The scaler is only used for otf and afm and virtual fonts. If
+-- a virtual font has italic correction make sure to set the
+-- has_italic flag. Some more flags will be added in the future.
+
+function tfm.do_scale(tfmtable, scaledpoints)
+ tfm.prepare_base_kerns(tfmtable) -- optimalization
if scaledpoints < 0 then
scaledpoints = (- scaledpoints/1000) * tfmtable.designsize -- already in sp
end
local delta = scaledpoints/(tfmtable.units or 1000) -- brr, some open type fonts have 2048
local t = { }
- t.factor = delta
- for k,v in pairs(tfmtable) do
- t[k] = (type(v) == "table" and { }) or v
+ -- unicoded unique descriptions shared cidinfo characters changed parameters indices
+ for k,v in next, tfmtable do
+ if type(v) == "table" then
+ -- print(k)
+ else
+ t[k] = v
+ end
end
- -- new
+ -- status
+ local isvirtual = tfmtable.type == "virtual" or tfmtable.virtualized
+ local hasmath = (tfmtable.math_parameters ~= nil and next(tfmtable.math_parameters) ~= nil) or (tfmtable.MathConstants ~= nil and next(tfmtable.MathConstants) ~= nil)
+ local nodemode = tfmtable.mode == "node"
+ local hasquality = tfmtable.auto_expand or tfmtable.auto_protrude
+ local hasitalic = tfmtable.has_italic
+ --
+ t.parameters = { }
+ t.characters = { }
+ t.MathConstants = { }
+ -- fast access
+ local descriptions = tfmtable.descriptions or { }
+ t.unicodes = tfmtable.unicodes
+ t.indices = tfmtable.indices
+ t.marks = tfmtable.marks
+ t.descriptions = descriptions
if tfmtable.fonts then
- t.fonts = table.fastcopy(tfmtable.fonts)
+ t.fonts = table.fastcopy(tfmtable.fonts) -- hm also at the end
end
- -- local zerobox = { 0, 0, 0, 0 }
local tp = t.parameters
+ local mp = t.math_parameters
local tfmp = tfmtable.parameters -- let's check for indexes
+ --
tp.slant = (tfmp.slant or tfmp[1] or 0)
tp.space = (tfmp.space or tfmp[2] or 0)*delta
tp.space_stretch = (tfmp.space_stretch or tfmp[3] or 0)*delta
@@ -209,83 +275,288 @@ function fonts.tfm.do_scale(tfmtable, scaledpoints)
tp.extra_space = (tfmp.extra_space or tfmp[7] or 0)*delta
local protrusionfactor = (tp.quad ~= 0 and 1000/tp.quad) or 0
local tc = t.characters
- for k,v in pairs(tfmtable.characters) do
- local description = v.description or v -- shared data
- local chr = {
- description = description,
- unicode = description.unicode,
- name = description.name,
- index = description.index or k,
- width = delta*(description.width or 0),
- height = delta*(description.height or 0),
- depth = delta*(description.depth or 0),
- class = description.class
- }
- if trace then
- logs.report("define font", string.format("n=%s, u=%s, i=%s, n=%s c=%s",k,description.unicode,description.index,description.name or '-',description.class or '-'))
- end
- -- local vb = v.boundingbox
- -- if vb then
- -- chr.boundingbox = { vb[1]*delta, vb[2]*delta, vb[3]*delta, vb[4]*delta }
- -- else
- -- -- chr.boundingbox = zerobox -- most afm en otf files have bboxes so ..
- -- end
- local ve = v.expansion_factor
- if ve then
- chr.expansion_factor = ve*1000 -- expansionfactor
+ local characters = tfmtable.characters
+ local nameneeded = not tfmtable.shared.otfdata --hack
+ local changed = tfmtable.changed or { } -- for base mode
+ local ischanged = not table.is_empty(changed)
+ local indices = tfmtable.indices
+ local luatex = tfmtable.luatex
+ local tounicode = luatex and luatex.tounicode
+ local defaultwidth = luatex and luatex.defaultwidth or 0
+ local defaultheight = luatex and luatex.defaultheight or 0
+ local defaultdepth = luatex and luatex.defaultdepth or 0
+ -- experimental, sharing kerns (unscaled and scaled) saves memory
+ local sharedkerns, basekerns = tfm.check_base_kerns(tfmtable)
+ -- loop over descriptions (afm and otf have descriptions, tfm not)
+ -- there is no need (yet) to assign a value to chr.tonunicode
+ local scaledwidth = defaultwidth * delta
+ local scaledheight = defaultheight * delta
+ local scaleddepth = defaultdepth * delta
+ local stackmath = tfmtable.ignore_stack_math ~= true
+local private = fonts.private
+ for k,v in next, characters do
+ local chr, description, index
+ if ischanged then
+ -- basemode hack
+ local c = changed[k]
+ if c then
+ description = descriptions[c] or v
+ v = characters[c] or v
+ index = (indices and indices[c]) or c
+ else
+ description = descriptions[k] or v
+ index = (indices and indices[k]) or k
+ end
+ else
+ description = descriptions[k] or v
+ index = (indices and indices[k]) or k
end
- local vl = v.left_protruding
- if vl then
- chr.left_protruding = protrusionfactor*chr.width*vl
+ local width = description.width
+ local height = description.height
+ local depth = description.depth
+ if width then width = delta*width else width = scaledwidth end
+ if height then height = delta*height else height = scaledheight end
+ -- if depth then depth = delta*depth else depth = scaleddepth end
+ if depth and depth ~= 0 then
+ depth = delta*depth
+ if nameneeded then
+ chr = {
+ name = description.name,
+ index = index,
+ height = height,
+ depth = depth,
+ width = width,
+ }
+ else
+ chr = {
+ index = index,
+ height = height,
+ depth = depth,
+ width = width,
+ }
+ end
+ else
+ -- this saves a little bit of memory time and memory, esp for big cjk fonts
+ if nameneeded then
+ chr = {
+ name = description.name,
+ index = index,
+ height = height,
+ width = width,
+ }
+ else
+ chr = {
+ index = index,
+ height = height,
+ width = width,
+ }
+ end
end
- local vr = v.right_protruding
- if vr then
- chr.right_protruding = protrusionfactor*chr.width*vr
+ -- if trace_scaling then
+ -- logs.report("define font","t=%s, u=%s, i=%s, n=%s c=%s",k,chr.tounicode or k,description.index,description.name or '-',description.class or '-')
+ -- end
+ if tounicode then
+ local tu = tounicode[index] -- nb: index!
+ if tu then
+ chr.tounicode = tu
+ end
end
- local vi = description.italic
- if vi then
- chr.italic = vi*delta
+ if hasquality then
+ local ve = v.expansion_factor
+ if ve then
+ chr.expansion_factor = ve*1000 -- expansionfactor, hm, can happen elsewhere
+ end
+ local vl = v.left_protruding
+ if vl then
+ chr.left_protruding = protrusionfactor*width*vl
+ end
+ local vr = v.right_protruding
+ if vr then
+ chr.right_protruding = protrusionfactor*width*vr
+ end
end
- local vk = v.kerns
- if vk then
- local tt = {}
- for k,v in pairs(vk) do tt[k] = v*delta end
- chr.kerns = tt
+ -- todo: hasitalic
+ if hasitalic then
+ local vi = description.italic or v.italic
+ if vi and vi ~= 0 then
+ chr.italic = vi*delta
+ end
end
- local vl = v.ligatures
- if vl then
- if true then
- chr.ligatures = vl -- shared
+ -- to be tested
+ if hasmath then
+ -- todo, just operate on descriptions.math
+ local vn = v.next
+ if vn then
+ chr.next = vn
else
- local tt = { }
- for i,l in pairs(vl) do
- tt[i] = l
+ local vv = v.vert_variants
+ if vv then
+ local t = { }
+ for i=1,#vv do
+ local vvi = vv[i]
+ t[i] = {
+ ["start"] = (vvi["start"] or 0)*delta,
+ ["end"] = (vvi["end"] or 0)*delta,
+ ["advance"] = (vvi["advance"] or 0)*delta,
+ ["extender"] = vvi["extender"],
+ ["glyph"] = vvi["glyph"],
+ }
+ end
+ chr.vert_variants = t
+ else
+ local hv = v.horiz_variants
+ if hv then
+ local t = { }
+ for i=1,#hv do
+ local hvi = hv[i]
+ t[i] = {
+ ["start"] = (hvi["start"] or 0)*delta,
+ ["end"] = (hvi["end"] or 0)*delta,
+ ["advance"] = (hvi["advance"] or 0)*delta,
+ ["extender"] = hvi["extender"],
+ ["glyph"] = hvi["glyph"],
+ }
+ end
+ chr.horiz_variants = t
+ end
+ end
+ end
+ local vt = description.top_accent
+ if vt then
+ chr.top_accent = delta*vt
+ end
+ if stackmath then
+ local mk = v.mathkerns
+ if mk then
+ local kerns = { }
+ -- for k, v in next, mk do
+ -- local kk = { }
+ -- for i=1,#v do
+ -- local vi = v[i]
+ -- kk[i] = { height = delta*vi.height, kern = delta*vi.kern }
+ -- end
+ -- kerns[k] = kk
+ -- end
+ local v = mk.top_right if v then local k = { } for i=1,#v do local vi = v[i]
+ k[i] = { height = delta*vi.height, kern = delta*vi.kern }
+ end kerns.top_right = k end
+ local v = mk.top_left if v then local k = { } for i=1,#v do local vi = v[i]
+ k[i] = { height = delta*vi.height, kern = delta*vi.kern }
+ end kerns.top_left = k end
+ local v = mk.bottom_left if v then local k = { } for i=1,#v do local vi = v[i]
+ k[i] = { height = delta*vi.height, kern = delta*vi.kern }
+ end kerns.bottom_left = k end
+ local v = mk.bottom_right if v then local k = { } for i=1,#v do local vi = v[i]
+ k[i] = { height = delta*vi.height, kern = delta*vi.kern }
+ end kerns.bottom_right = k end
+ chr.mathkern = kerns -- singular
end
- chr.ligatures = tt
end
end
- local vc = v.commands
- if vc then
- -- we assume non scaled commands here
- local tt = { }
- for i=1,#vc do
- local ivc = vc[i]
- local key = ivc[1]
- if key == "right" or key == "left" or key == "down" or key == "up" then
- tt[#tt+1] = { key, ivc[2]*delta }
- else -- not comment
- tt[#tt+1] = ivc -- shared since in cache and untouched
+ if not nodemode then
+ local vk = v.kerns
+ if vk then
+ if sharedkerns then
+ local base = basekerns[vk] -- hashed by table id, not content
+ if not base then
+ base = {}
+ for k,v in next, vk do base[k] = v*delta end
+ basekerns[vk] = base
+ end
+ chr.kerns = base
+ else
+ local tt = {}
+ for k,v in next, vk do tt[k] = v*delta end
+ chr.kerns = tt
+ end
+ end
+ local vl = v.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
+ if isvirtual then
+ local vc = v.commands
+ if vc then
+ -- we assume non scaled commands here
+ local ok = false
+ for i=1,#vc do
+ local key = vc[i][1]
+ if key == "right" or key == "down" then
+ ok = true
+ break
+ end
+ end
+ if ok then
+ local tt = { }
+ for i=1,#vc do
+ local ivc = vc[i]
+ local key = ivc[1]
+ if key == "right" or key == "down" then
+ tt[#tt+1] = { key, ivc[2]*delta }
+ else -- not comment
+ tt[#tt+1] = ivc -- shared since in cache and untouched
+ end
+ end
+ chr.commands = tt
+ else
+ chr.commands = vc
end
end
- chr.commands = tt
end
tc[k] = chr
end
-- t.encodingbytes, t.filename, t.fullname, t.name: elsewhere
t.size = scaledpoints
+ t.factor = delta
if t.fonts then
t.fonts = table.fastcopy(t.fonts) -- maybe we virtualize more afterwards
end
+ if hasmath then
+ -- mathematics.extras.copy(t) -- can be done elsewhere if needed
+ local ma = tfm.mathactions
+ for i=1,#ma do
+ ma[i](t,tfmtable,delta)
+ end
+ end
+ -- needed for \high cum suis
+ local tpx = tp.x_height
+if hasmath then
+ if not tp[13] then tp[13] = .86*tpx end -- mathsupdisplay
+ if not tp[14] then tp[14] = .86*tpx end -- mathsupnormal
+ if not tp[15] then tp[15] = .86*tpx end -- mathsupcramped
+ if not tp[16] then tp[16] = .48*tpx end -- mathsubnormal
+ if not tp[17] then tp[17] = .48*tpx end -- mathsubcombined
+ if not tp[22] then tp[22] = 0 end -- mathaxisheight
+ if t.MathConstants then t.MathConstants.AccentBaseHeight = nil end -- safeguard
+end
+ t.tounicode = 1
+ t.cidinfo = tfmtable.cidinfo
+ -- we have t.name=metricfile and t.fullname=RealName and t.filename=diskfilename
+ -- when collapsing fonts, luatex looks as both t.name and t.fullname as ttc files
+ -- can have multiple subfonts
+ if hasmath then
+ if trace_defining then
+ logs.report("define font","math enabled for: %s %s %s",t.name or "noname",t.fullname or "nofullname",t.filename or "nofilename")
+ end
+ else
+ if trace_defining then
+ logs.report("define font","math disabled for: %s %s %s",t.name or "noname",t.fullname or "nofullname",t.filename or "nofilename")
+ end
+ t.nomath, t.MathConstants = true, nil
+ end
+ -- fullname is used in the subsetting
+ if not t.psname then
+ t.psname = t.fullname -- else bad luck
+ end
return t, delta
end
@@ -296,426 +567,269 @@ make this profitable and the <l n='lua'/> based variant was just faster. A days
wasted day but an experience richer.</p>
--ldx]]--
-function fonts.tfm.scale(tfmtable, scaledpoints)
- local t, factor = fonts.tfm.do_scale(tfmtable, scaledpoints)
+tfm.auto_cleanup = true
+
+local lastfont = nil
+
+-- we can get rid of the tfm instance when we have fast access to the
+-- scaled character dimensions at the tex end, e.g. a fontobject.width
+--
+-- flushing the kern and ligature tables from memory saves a lot (only
+-- base mode) but it complicates vf building where the new characters
+-- demand this data
+
+--~ for id, f in pairs(fonts.ids) do -- or font.fonts
+--~ local ffi = font.fonts[id]
+--~ f.characters = ffi.characters
+--~ f.kerns = ffi.kerns
+--~ f.ligatures = ffi.ligatures
+--~ end
+
+function tfm.cleanup_table(tfmdata) -- we need a cleanup callback, now we miss the last one
+ if tfm.auto_cleanup then -- ok, we can hook this into everyshipout or so ... todo
+ if tfmdata.type == 'virtual' or tfmdata.virtualized then
+ for k, v in next, tfmdata.characters do
+ if v.commands then v.commands = nil end
+ end
+ end
+ end
+end
+
+function tfm.cleanup(tfmdata) -- we need a cleanup callback, now we miss the last one
+end
+
+function tfm.scale(tfmtable, scaledpoints)
+ local t, factor = tfm.do_scale(tfmtable, scaledpoints)
t.factor = factor
t.ascender = factor*(tfmtable.ascender or 0)
t.descender = factor*(tfmtable.descender or 0)
t.shared = tfmtable.shared or { }
t.unique = table.fastcopy(tfmtable.unique or {})
---~ print("scaling", t.name, t.factor) -- , fonts.tfm.hash_features(tfmtable.specification))
+--~ print("scaling", t.name, t.factor) -- , tfm.hash_features(tfmtable.specification))
+ tfm.cleanup(t)
return t
end
--[[ldx--
-<p>The following functions are used for reporting about the fonts
-used. The message itself is not that useful in regular runs but since
-we now have several readers it may be handy to know what reader is
-used for which font.</p>
+<p>Analyzers run per script and/or language and are needed in order to
+process features right.</p>
--ldx]]--
-function fonts.logger.save(tfmtable,source,specification) -- save file name in spec here ! ! ! ! ! !
- if tfmtable and specification and specification.specification then
- if fonts.trace then
- logs.report("define font",string.format("registering %s as %s",specification.name,source))
- end
- specification.source = source
- fonts.loaded[specification.specification] = specification
- fonts.used[specification.name] = source
- end
-end
+fonts.analyzers = fonts.analyzers or { }
+fonts.analyzers.aux = fonts.analyzers.aux or { }
+fonts.analyzers.methods = fonts.analyzers.methods or { }
+fonts.analyzers.initializers = fonts.analyzers.initializers or { }
-function fonts.logger.report(separator)
- local s = table.sortedkeys(fonts.loaded)
- if #s > 0 then
- local t = { }
- for _,v in ipairs(s) do
- t[#t+1] = v .. ":" .. fonts.loaded[v].source
+-- todo: analyzers per script/lang, cross font, so we need an font id hash -> script
+-- e.g. latin -> hyphenate, arab -> 1/2/3 analyze
+
+-- an example analyzer (should move to font-ota.lua)
+
+local state = attributes.private('state')
+
+function fonts.analyzers.aux.setstate(head,font)
+ local tfmdata = fontdata[font]
+ local characters = tfmdata.characters
+ local descriptions = tfmdata.descriptions
+ local first, last, current, n, done = nil, nil, head, 0, false -- maybe make n boolean
+ while current do
+ if current.id == glyph and current.font == font then
+ local d = descriptions[current.char]
+ if d then
+ if d.class == "mark" then
+ done = true
+ set_attribute(current,state,5) -- mark
+ elseif n == 0 then
+ first, last, n = current, current, 1
+ set_attribute(current,state,1) -- init
+ else
+ last, n = current, n+1
+ set_attribute(current,state,2) -- medi
+ end
+ else -- finish
+ if first and first == last then
+ set_attribute(last,state,4) -- isol
+ elseif last then
+ set_attribute(last,state,3) -- fina
+ end
+ first, last, n = nil, nil, 0
+ end
+ else -- finish
+ if first and first == last then
+ set_attribute(last,state,4) -- isol
+ elseif last then
+ set_attribute(last,state,3) -- fina
+ end
+ first, last, n = nil, nil, 0
end
- return table.concat(t,separator or " ")
- else
- return "none"
+ current = current.next
+ end
+ if first and first == last then
+ set_attribute(last,state,4) -- isol
+ elseif last then
+ set_attribute(last,state,3) -- fina
end
+ return head, done
end
-function fonts.logger.format(name)
- return fonts.used[name] or "unknown"
+function tfm.replacements(tfm,value)
+ -- tfm.characters[0x0022] = table.fastcopy(tfm.characters[0x201D])
+ -- tfm.characters[0x0027] = table.fastcopy(tfm.characters[0x2019])
+ -- tfm.characters[0x0060] = table.fastcopy(tfm.characters[0x2018])
+ -- tfm.characters[0x0022] = tfm.characters[0x201D]
+ tfm.characters[0x0027] = tfm.characters[0x2019]
+ -- tfm.characters[0x0060] = tfm.characters[0x2018]
end
---[[ldx--
-<p>When we implement functions that deal with features, most of them
-will depend of the font format. Here we define the few that are kind
-of neutral.</p>
---ldx]]--
-
-fonts.initializers = fonts.initializers or { }
-fonts.initializers.common = fonts.initializers.common or { }
-
---[[ldx--
-<p>This feature will remove inter-digit kerns.</p>
---ldx]]--
+-- auto complete font with missing composed characters
-table.insert(fonts.triggers,"equaldigits")
+table.insert(fonts.manipulators,"compose")
-function fonts.initializers.common.equaldigits(tfmdata,value)
+function fonts.initializers.common.compose(tfmdata,value)
if value then
- local chr = tfmdata.characters
- for i = utf.byte('0'), utf.byte('9') do
- local c = chr[i]
- if c then
- c.kerns = nil
- end
- end
+ fonts.vf.aux.compose_characters(tfmdata)
end
end
---[[ldx--
-<p>This feature will give all glyphs an equal height and/or depth. Valid
-values are <type>none</type>, <type>height</type>, <type>depth</type> and
-<type>both</type>.</p>
---ldx]]--
-
-table.insert(fonts.triggers,"lineheight")
-
-function fonts.initializers.common.lineheight(tfmdata,value)
- if value and type(value) == "string" then
- if value == "none" then
- for _,v in pairs(tfmdata.characters) do
- v.height, v.depth = 0, 0
- end
- else
- local ascender, descender = tfmdata.ascender, tfmdata.descender
- if ascender and descender then
- local ht, dp = ascender or 0, descender or 0
- if value == "height" then
- dp = 0
- elseif value == "depth" then
- ht = 0
- end
- if ht > 0 then
- if dp > 0 then
- for _,v in pairs(tfmdata.characters) do
- v.height, v.depth = ht, dp
- end
- else
- for _,v in pairs(tfmdata.characters) do
- v.height = ht
- end
- end
- elseif dp > 0 then
- for _,v in pairs(tfmdata.characters) do
- v.depth = dp
- end
- end
- end
+-- tfm features, experimental
+
+tfm.features = tfm.features or { }
+tfm.features.list = tfm.features.list or { }
+tfm.features.default = tfm.features.default or { }
+
+function tfm.enhance(tfmdata,specification)
+ -- we don't really share tfm data because we always reload
+ -- but this is more in sycn with afm and such
+ local features = (specification.features and specification.features.normal ) or { }
+ tfmdata.shared = tfmdata.shared or { }
+ tfmdata.shared.features = features
+ -- tfmdata.shared.tfmdata = tfmdata -- circular
+ tfmdata.filename = specification.name
+ if not features.encoding then
+ local name, size = specification.name, specification.size
+ local encoding, filename = match(name,"^(.-)%-(.*)$") -- context: encoding-name.*
+ if filename and encoding and fonts.enc.known[encoding] then
+ features.encoding = encoding
end
end
+ tfm.set_features(tfmdata)
end
---[[ldx--
-<p>It does not make sense any more to support messed up encoding vectors
-so we stick to those that implement oldstyle and small caps. After all,
-we move on. We can extend the next function on demand. This features is
-only used with <l n='afm'/> files.</p>
---ldx]]--
-
-do
-
- local smallcaps = lpeg.P(".sc") + lpeg.P(".smallcaps") + lpeg.P(".caps") + lpeg.P("small")
- local oldstyle = lpeg.P(".os") + lpeg.P(".oldstyle") + lpeg.P(".onum")
-
- smallcaps = lpeg.Cs((1-smallcaps)^1) * smallcaps^1
- oldstyle = lpeg.Cs((1-oldstyle )^1) * oldstyle ^1
-
- function fonts.initializers.common.encoding(tfmdata,value)
- if value then
- local encodingfile = value .. '.enc'
- local encoding = fonts.enc.load(encodingfile)
- if encoding then
- -- tfmdata.encoding = value
- local vector = encoding.vector
- local afmdata = tfmdata.shared.afmdata
- local characters = tfmdata.characters
- local unicodes = afmdata.luatex.unicodes
- local function remap(pattern,name)
- local p = pattern:match(name)
- if p then
- local oldchr, newchr = unicodes[p], unicodes[name]
- if oldchr and newchr then
- -- texio.write_nl(string.format("%s (%s) -> %s (%s)",p,oldchr or -1,name,newchr or -1))
- characters[oldchr] = characters[newchr]
+function tfm.set_features(tfmdata)
+ -- todo: no local functions
+ local shared = tfmdata.shared
+-- local tfmdata = shared.tfmdata
+ local features = shared.features
+ if not table.is_empty(features) then
+ local mode = tfmdata.mode or fonts.mode
+ local fi = fonts.initializers[mode]
+ if fi and fi.tfm then
+ local function initialize(list) -- using tex lig and kerning
+ if list then
+ for i=1,#list do
+ local f = list[i]
+ local value = features[f]
+ if value and fi.tfm[f] then -- brr
+ if tfm.trace_features then
+ logs.report("define font","initializing feature %s to %s for mode %s for font %s",f,tostring(value),mode or 'unknown',tfmdata.name or 'unknown')
+ end
+ fi.tfm[f](tfmdata,value)
+ mode = tfmdata.mode or fonts.mode
+ fi = fonts.initializers[mode]
end
end
- return p
- end
- for _, name in pairs(vector) do
- local ok = remap(smallcaps,name) or remap(oldstyle,name)
- end
- if fonts.map.data[tfmdata.name] then
- fonts.map.data[tfmdata.name].encoding = encodingfile
end
end
+ initialize(fonts.triggers)
+ initialize(tfm.features.list)
+ initialize(fonts.manipulators)
end
- end
-
- -- when needed we can provide this as features in e.g. afm files
-
- function fonts.initializers.common.remap(tfmdata,value,pattern)
- if value then
- local afmdata = tfmdata.shared.afmdata
- local characters = tfmdata.characters
- local unicodes = afmdata.luatex.unicodes
- local done = false
- for i, blob in pairs(characters) do
- local name = blob.description.name
- if name then
- local p = pattern:match(name)
- if p then
- local oldchr, newchr = unicodes[p], unicodes[name]
- if oldchr and newchr then
- characters[oldchr] = characters[newchr]
- end
- end
- end
- end
- end
- end
-
- function fonts.initializers.common.oldstyle(tfmdata,value)
- fonts.initializers.common.remap(tfmdata,value,oldstyle)
- end
- function fonts.initializers.common.smallcaps(tfmdata,value)
- fonts.initializers.common.remap(tfmdata,value,smallcaps)
- end
-
- function fonts.initializers.common.fakecaps(tfmdata,value)
- if value then
- -- todo: scale down
- local afmdata = tfmdata.shared.afmdata
- local characters = tfmdata.characters
- local unicodes = afmdata.luatex.unicodes
- for i, blob in pairs(characters) do
- local name = blob.description.name
- if name then
- local p = name:lower()
- if p then
- local oldchr, newchr = unicodes[p], unicodes[name]
- if oldchr and newchr then
- characters[oldchr] = characters[newchr]
+ local fm = fonts.methods[mode]
+ if fm and fm.tfm then
+ local function register(list) -- node manipulations
+ if list then
+ for i=1,#list do
+ local f = list[i]
+ if features[f] and fm.tfm[f] then -- brr
+ if not shared.processors then -- maybe also predefine
+ shared.processors = { fm.tfm[f] }
+ else
+ shared.processors[#shared.processors+1] = fm.tfm[f]
+ end
end
end
end
end
+ register(tfm.features.list)
end
end
-
end
---~ function fonts.initializers.common.install(format,feature) -- 'afm','lineheight'
---~ fonts.initializers.base[format][feature] = fonts.initializers.common[feature]
---~ fonts.initializers.node[format][feature] = fonts.initializers.common[feature]
---~ end
-
---[[ldx--
-<p>Analyzers run per script and/or language and are needed in order to
-process features right.</p>
---ldx]]--
-
-fonts.analyzers = fonts.analyzers or { }
-fonts.analyzers.aux = fonts.analyzers.aux or { }
-fonts.analyzers.methods = fonts.analyzers.methods or { }
-fonts.analyzers.initializers = fonts.analyzers.initializers or { }
+function tfm.features.register(name,default)
+ tfm.features.list[#tfm.features.list+1] = name
+ tfm.features.default[name] = default
+end
-do
-
- local glyph = node.id('glyph')
- local fontdata = fonts.tfm.id
- local set_attribute = node.set_attribute
--- local unset_attribute = node.unset_attribute
--- local has_attribute = node.has_attribute
-
- local state = attributes.numbers['state'] or 100
-
- -- todo: analyzers per script/lang, cross font, so we need an font id hash -> script
- -- e.g. latin -> hyphenate, arab -> 1/2/3 analyze
-
- -- an example analyzer
-
- function fonts.analyzers.aux.setstate(head,font)
- local characters = fontdata[font].characters
- local first, last, current, n, done = nil, nil, head, 0, false -- maybe make n boolean
- while current do
- if current.id == glyph and current.font == font then
- local c = characters[current.char]
- if c then
- if c.description.class == "mark" then
- done = true
- set_attribute(current,state,5) -- mark
- elseif n == 0 then
- first, last, n = current, current, 1
- set_attribute(current,state,1) -- init
- else
- last, n = current, n+1
- set_attribute(current,state,2) -- medi
- end
- else -- finish
- if first and first == last then
- set_attribute(last,state,4) -- isol
- elseif last then
- set_attribute(last,state,3) -- fina
+function tfm.reencode(tfmdata,encoding)
+ if encoding and fonts.enc.known[encoding] then
+ local data = fonts.enc.load(encoding)
+ if data then
+ local characters, original, vector = tfmdata.characters, { }, data.vector
+ tfmdata.encoding = encoding -- not needed
+ for k, v in next, characters do
+ v.name, v.index, original[k] = vector[k], k, v
+ end
+ for k,v in next, data.unicodes do
+ if k ~= v then
+ if trace_defining then
+ logs.report("define font","reencoding U+%04X to U+%04X",k,v)
end
- first, last, n = nil, nil, 0
- end
- else -- finish
- if first and first == last then
- set_attribute(last,state,4) -- isol
- elseif last then
- set_attribute(last,state,3) -- fina
+ characters[k] = original[v]
end
- first, last, n = nil, nil, 0
end
- current = current.next
end
- if first and first == last then
- set_attribute(last,state,4) -- isol
- elseif last then
- set_attribute(last,state,3) -- fina
- end
- return head, done
end
-
end
---[[ldx--
-<p>We move marks into the components list. This saves much nasty testing later on.</p>
---ldx]]--
+tfm.features.register('reencode')
-do
-
- local glyph = node.id('glyph')
- local fontdata = fonts.tfm.id
- local marknumber = attributes.numbers['mark'] or 200
- local set_attribute = node.set_attribute
-
- function fonts.pushmarks(head,font)
- local tfmdata = fontdata[font]
- local characters = tfmdata.characters
- local current, last, done, n = head, nil, false, 0
- while current do
- if current.id == glyph and current.font == font then
- local c = characters[current.char]
- if c and c.description.class == "mark" then
- -- check if head
- if last and not last.components then
- last.components = current
- current.prev = nil -- last.components.prev = nil
- done = true
- n = 1
- else
- n = n + 1
- end
- set_attribute(current,marknumber,n)
- current = current.next
- elseif last and last.components then
- -- finish 'm
- current.prev.next = nil
- current.prev = last
- last.next = current
- last = current
- last = nil
- else
- last = current
- current = current.next
- end
- elseif last and last.components then
- current.prev.next = nil
- current.prev = last
- last.next = current
- last = nil
- else
- last = nil
- current = current.next
- end
- end
- if last and last.components then
- last.next = nil
- end
- tfmdata.shared.markspushed = done
- return head, done
- end
+fonts.initializers.base.tfm.reencode = tfm.reencode
+fonts.initializers.node.tfm.reencode = tfm.reencode
- function fonts.removemarks(head,font)
- local current, done, characters = head, false, tfmdata.characters
- while current do
- if current.id == glyph and current.font == font and characters[current.char].description.class == "mark" then
- local next, prev = current.next, current.prev
- if next then
- next.prev = prev
- end
- if prev then
- prev.next = next
- else
- head = next
- end
- node.free(current)
- current = next
- done = true
- else
- current = current.next
- end
- end
- return head, done
- end
+fonts.enc = fonts.enc or { }
+fonts.enc.remappings = fonts.enc.remappings or { }
- function fonts.popmarks(head,font)
- local tfmdata = fontdata[font]
- if tfmdata.shared.markspushed then
- local current, done, characters = head, false, tfmdata.characters
- while current do
- if current.id == glyph and current.font == font then
- local components = current.components
- if components then
- local last, next = components, current.next
- while last.next do last = last.next end
- if next then
- next.prev = last
- end
- last.next= next
- current.next = components
- components.prev = current
- current.components = nil
- current = last.next
- done = true
- else
- current = current.next
- end
- else
- current = current.next
+function tfm.remap(tfmdata,remapping)
+ local vector = remapping and fonts.enc.remappings[remapping]
+ if vector then
+ local characters, original = tfmdata.characters, { }
+ for k, v in next, characters do
+ original[k], characters[k] = v, nil
+ end
+ for k,v in next, vector do
+ if k ~= v then
+ if trace_defining then
+ logs.report("define font","remapping U+%04X to U+%04X",k,v)
end
+ local c = original[k]
+ characters[v] = c
+ c.index = k
end
- return head, done
- else
- return head, false
end
+ tfmdata.encodingbytes = 2
+ tfmdata.format = 'type1'
end
-
end
-function fonts.tfm.replacements(tfm,value)
---~ tfm.characters[0x0022] = table.fastcopy(tfm.characters[0x201D])
---~ tfm.characters[0x0027] = table.fastcopy(tfm.characters[0x2019])
---~ tfm.characters[0x0060] = table.fastcopy(tfm.characters[0x2018])
---~ tfm.characters[0x0022] = tfm.characters[0x201D]
- tfm.characters[0x0027] = tfm.characters[0x2019]
---~ tfm.characters[0x0060] = tfm.characters[0x2018]
-end
+tfm.features.register('remap')
--- auto complete font with missing composed characters
+fonts.initializers.base.tfm.remap = tfm.remap
+fonts.initializers.node.tfm.remap = tfm.remap
-table.insert(fonts.manipulators,"compose")
+-- status info
-function fonts.initializers.common.compose(tfmdata,value)
- if value then
- fonts.vf.aux.compose_characters(tfmdata)
+statistics.register("fonts load time", function()
+ if statistics.elapsedindeed(fonts) then
+ return format("%s seconds",statistics.elapsedtime(fonts))
end
-end
+end)