summaryrefslogtreecommitdiff
path: root/tex/context/base/font-tfm.lua
diff options
context:
space:
mode:
Diffstat (limited to 'tex/context/base/font-tfm.lua')
-rw-r--r--tex/context/base/font-tfm.lua977
1 files changed, 439 insertions, 538 deletions
diff --git a/tex/context/base/font-tfm.lua b/tex/context/base/font-tfm.lua
index 1955b58bc..2ee633b77 100644
--- a/tex/context/base/font-tfm.lua
+++ b/tex/context/base/font-tfm.lua
@@ -6,31 +6,51 @@ if not modules then modules = { } end modules ['font-tfm'] = {
license = "see context related readme files"
}
+local utf = unicode.utf8
+
+local next, format, match, lower = next, string.format, string.match, string.lower
+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.tfm = fonts.tfm or { }
+fonts.ids = fonts.ids or { }
local tfm = fonts.tfm
-fonts.loaded = fonts.loaded or { }
-fonts.dontembed = fonts.dontembed or { }
-fonts.logger = fonts.logger or { }
-fonts.loadtime = 0
-fonts.triggers = fonts.triggers or { } -- brrr
+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]]--
-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 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
@@ -38,14 +58,14 @@ function tfm.enhance(tfmdata,specification)
tfmdata.encoding = encoding
local vector = data.vector
local original = { }
- for k, v in pairs(characters) do
+ 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 fonts.trace then
+ if trace_defining then
logs.report("define font","mapping %s onto %s",k,v)
end
characters[k] = original[v]
@@ -56,21 +76,9 @@ function tfm.enhance(tfmdata,specification)
end
function 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(specification.name, 'ofm')
- else
- fname = input.findbinfile(fname, 'ofm')
- end
- if fname and fname ~= "" then
- if fonts.trace then
+ 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
@@ -78,12 +86,12 @@ function tfm.read_from_tfm(specification)
tfmdata.descriptions = tfmdata.descriptions or { }
if tfm.resolve_vf then
fonts.logger.save(tfmdata,file.extname(fname),specification) -- strange, why here
- fname = input.findbinfile(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'
@@ -93,10 +101,8 @@ function tfm.read_from_tfm(specification)
end
tfm.enhance(tfmdata,specification)
end
- else
- if fonts.trace then
- logs.report("define font","loading tfm with name %s fails",specification.name)
- end
+ elseif trace_defining then
+ logs.report("define font","loading tfm with name %s fails",specification.name)
end
return tfmdata
end
@@ -107,21 +113,17 @@ 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 tfm.setfactor(f)
- tfm.factor = factors[f or 'pt'] or factors.pt
- end
-
- tfm.setfactor()
+local factors = {
+ pt = 65536.0,
+ bp = 65781.8,
+}
+function tfm.setfactor(f)
+ tfm.factor = factors[f or 'pt'] or factors.pt
end
+tfm.setfactor()
+
function tfm.scaled(scaledpoints, designsize) -- handles designsize in sp as well
if scaledpoints < 0 then
if designsize then
@@ -160,9 +162,11 @@ function tfm.check_virtual_id(tfmdata, id)
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
@@ -177,25 +181,91 @@ excessive memory usage in CJK fonts, we no longer pass the boundingbox.)</p>
fonts.trace_scaling = false
+-- 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 sur eto set the
+-- has_italic flag. Some more flags will be added in the future.
+
function tfm.do_scale(tfmtable, scaledpoints)
- local trace = fonts.trace_scaling
+ tfm.prepare_base_kerns(tfmtable) -- optimalization
if scaledpoints < 0 then
scaledpoints = (- scaledpoints/1000) * tfmtable.designsize -- already in sp
end
---~ print(">>>",tfmtable.units)
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
- end
- -- new
+ -- 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
+ -- status
+ local isvirtual = tfmtable.type == "virtual" or tfmtable.virtualized
+ local hasmath = tfmtable.math_parameters ~= nil or 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
@@ -205,107 +275,271 @@ function 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
- -- we can loop over (descriptions or characters), in which case
- -- we don't need to init characters in afm/otf (saves some mem)
- -- but then .. beware of protruding etc
- local descriptions = tfmtable.descriptions or { }
- t.descriptions = descriptions
+ local characters = tfmtable.characters
local nameneeded = not tfmtable.shared.otfdata --hack
--- loop over descriptions
- -- afm and otf have descriptions, tfm not
- for k,v in pairs(tfmtable.characters) do
- local description = descriptions[k] or v
- local chr
- -- there is no need (yet) to assign a value to chr.tonunicode
- if nameneeded then
- chr = {
- name = description.name, -- is this used at all?
- index = description.index or k,
- width = delta*(description.width or 0),
- height = delta*(description.height or 0),
- depth = delta*(description.depth or 0),
- }
+ 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
+ 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
- chr = {
- index = description.index or k,
- width = delta*(description.width or 0),
- height = delta*(description.height or 0),
- depth = delta*(description.depth or 0),
- }
- end
- if trace 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
- local ve = v.expansion_factor
- if ve then
- chr.expansion_factor = ve*1000 -- expansionfactor
- end
- local vl = v.left_protruding
- if vl then
- chr.left_protruding = protrusionfactor*chr.width*vl
+ description = descriptions[k] or v
+ index = (indices and indices[k]) or k
+ end
+ 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]
+ 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 ok = false
- for i=1,#vc do
- local key = vc[i][1]
- -- if key == "right" or key == "left" or key == "down" or key == "up" then
- if key == "right" or key == "down" then
- ok = true
- break
+ 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
- if ok then
- local tt = { }
+ 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 ivc = vc[i]
- local key = ivc[1]
- -- if key == "right" or key == "left" or key == "down" or key == "up" then
+ local key = vc[i][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
+ ok = true
+ break
end
end
- chr.commands = tt
- else
- chr.commands = vc
+ 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
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 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
+ t.tounicode = 1
+ -- 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
+--~ collectgarbage("collect")
return t, delta
end
@@ -320,14 +554,25 @@ tfm.auto_cleanup = true
local lastfont = nil
--- we can get rid of the tfm instance when we hav efast access to the
+-- 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' then
- for k, v in pairs(tfmdata.characters) do
- if v.commands then v.commands = nil end
+ 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
@@ -349,235 +594,6 @@ function tfm.scale(tfmtable, scaledpoints)
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>
---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","registering %s as %s",specification.name,source)
- end
- specification.source = source
- fonts.loaded[specification.specification] = specification
- fonts.used[specification.name] = source
- end
-end
-
---~ 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
---~ end
---~ return table.concat(t,separator or " ")
---~ else
---~ return "none"
---~ end
---~ end
-
-function fonts.logger.report(separator)
- local s = table.sortedkeys(fonts.used)
- if #s > 0 then
- local t = { }
- for _,v in ipairs(s) do
- t[#t+1] = v .. ":" .. fonts.used[v]
- end
- return table.concat(t,separator or " ")
- else
- return "none"
- end
-end
-
-function fonts.logger.format(name)
- return fonts.used[name] or "unknown"
-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]]--
-
-table.insert(fonts.triggers,"equaldigits")
-
-function fonts.initializers.common.equaldigits(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
- 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
- end
- end
-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 afmdata = tfmdata.shared.afmdata
- if afmdata then
- local encodingfile = value .. '.enc'
- local encoding = fonts.enc.load(encodingfile)
- if encoding then
- local vector = encoding.vector
- 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 and type(oldchr) == "number" and type(newchr) == "number" then
- -- logs.report("encoding","%s (%s) -> %s (%s)",p,oldchr or -1,name,newchr or -1)
- characters[oldchr] = characters[newchr]
- 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
- end
- end
- end
-
- -- when needed we can provide this as features in e.g. afm files
-
- function fonts.initializers.common.remap(tfmdata,value,pattern) -- will go away
- if value then
- local afmdata = tfmdata.shared.afmdata
- if afmdata then
- local characters = tfmdata.characters
- local descriptions = tfmdata.descriptions
- local unicodes = afmdata.luatex.unicodes
- local done = false
- for u, _ in pairs(characters) do
- local name = descriptions[u].name
- if name then
- local p = pattern:match(name)
- if p then
- local oldchr, newchr = unicodes[p], unicodes[name]
- if oldchr and newchr and type(oldchr) == "number" and type(newchr) == "number" then
- characters[oldchr] = characters[newchr]
- end
- 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
- if afmdata then
- local characters = tfmdata.characters
- local descriptions = tfmdata.descriptions
- local unicodes = afmdata.luatex.unicodes
- for u, _ in pairs(characters) do
- local name = descriptions[u].name
- if name then
- local p = name:lower()
- if p then
- local oldchr, newchr = unicodes[p], unicodes[name]
- if oldchr and newchr and type(oldchr) == "number" and type(newchr) == "number" then
- characters[oldchr] = characters[newchr]
- end
- end
- end
- end
- end
- 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]]--
@@ -587,47 +603,31 @@ fonts.analyzers.aux = fonts.analyzers.aux or { }
fonts.analyzers.methods = fonts.analyzers.methods or { }
fonts.analyzers.initializers = fonts.analyzers.initializers or { }
-do
-
- local glyph = node.id('glyph')
- local fontdata = 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 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
+-- 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
@@ -637,132 +637,22 @@ do
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]]--
-
-do
-
- local glyph = node.id('glyph')
- local fontdata = 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 descriptions = tfmdata.descriptions
- local current, last, done, n = head, nil, false, 0
- while current do
- if current.id == glyph and current.font == font then
- local d = descriptions[current.char]
- if d and d.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
-
- function fonts.removemarks(head,font)
- local current, done, characters, descriptions = head, false, tfmdata.characters, tfmdata.descriptions
- while current do
- if current.id == glyph and current.font == font and descriptions[current.char].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
+ 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 head, done
+ current = current.next
end
-
- 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
- end
- end
- return head, done
- else
- return head, false
- 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 tfm.replacements(tfm,value)
@@ -800,7 +690,7 @@ function tfm.enhance(tfmdata,specification)
tfmdata.filename = specification.name
if not features.encoding then
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
features.encoding = encoding
end
@@ -809,6 +699,7 @@ tfmdata.filename = specification.name
end
function tfm.set_features(tfmdata)
+ -- todo: no local functions
local shared = tfmdata.shared
-- local tfmdata = shared.tfmdata
local features = shared.features
@@ -818,7 +709,8 @@ function tfm.set_features(tfmdata)
if fi and fi.tfm then
local function initialize(list) -- using tex lig and kerning
if list then
- for _, f in ipairs(list) do
+ 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
@@ -839,7 +731,8 @@ function tfm.set_features(tfmdata)
if fm and fm.tfm then
local function register(list) -- node manipulations
if list then
- for _, f in ipairs(list) do
+ 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] }
@@ -866,13 +759,13 @@ function tfm.reencode(tfmdata,encoding)
if data then
local characters, original, vector = tfmdata.characters, { }, data.vector
tfmdata.encoding = encoding -- not needed
- for k, v in pairs(characters) do
+ for k, v in next, characters do
v.name, v.index, original[k] = vector[k], k, v
end
- for k,v in pairs(data.unicodes) do
+ for k,v in next, data.unicodes do
if k ~= v then
- if fonts.trace then
- logs.report("define font","reencoding %04X to %04X",k,v)
+ if trace_defining then
+ logs.report("define font","reencoding U+%04X to U+%04X",k,v)
end
characters[k] = original[v]
end
@@ -893,13 +786,13 @@ 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 pairs(characters) do
+ for k, v in next, characters do
original[k], characters[k] = v, nil
end
- for k,v in pairs(vector) do
+ for k,v in next, vector do
if k ~= v then
- if fonts.trace then
- logs.report("define font","remapping %04X to %04X",k,v)
+ if trace_defining then
+ logs.report("define font","remapping U+%04X to U+%04X",k,v)
end
local c = original[k]
characters[v] = c
@@ -915,3 +808,11 @@ tfm.features.register('remap')
fonts.initializers.base.tfm.remap = tfm.remap
fonts.initializers.node.tfm.remap = tfm.remap
+
+-- status info
+
+statistics.register("fonts load time", function()
+ if statistics.elapsedindeed(fonts) then
+ return format("%s seconds",statistics.elapsedtime(fonts))
+ end
+end)