if not modules then modules = { } end modules ['font-tfm'] = { version = 1.001, comment = "companion to font-ini.tex", author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", copyright = "PRAGMA ADE / ConTeXt Development Team", license = "see context related readme files" } --[[ldx--
Here we only implement a few helper functions.
--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 --[[ldx--The next function encapsulates the standard
We need to normalize the scale factor (in scaled points). This has to
do with the fact that
Before a font is passed to
Beware, the boundingbox is passed as reference so we may not overwrite it in the process; numbers are of course copies. Here 65536 equals 1pt. (Due to excessive memory usage in CJK fonts, we no longer pass the boundingbox.)
--ldx]]-- fonts.trace_scaling = false function fonts.tfm.do_scale(tfmtable, scaledpoints) local trace = fonts.trace_scaling 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 end -- new if tfmtable.fonts then t.fonts = table.fastcopy(tfmtable.fonts) end -- local zerobox = { 0, 0, 0, 0 } local tp = t.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 tp.space_shrink = (tfmp.space_shrink or tfmp[4] or 0)*delta tp.x_height = (tfmp.x_height or tfmp[5] or 0)*delta tp.quad = (tfmp.quad or tfmp[6] or 0)*delta 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 end local vl = v.left_protruding if vl then chr.left_protruding = protrusionfactor*chr.width*vl end local vr = v.right_protruding if vr then chr.right_protruding = protrusionfactor*chr.width*vr end local vi = description.italic if vi then chr.italic = vi*delta 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 end local vl = v.ligatures if vl then if true then chr.ligatures = vl -- shared else local tt = { } for i,l in pairs(vl) do tt[i] = l 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 end end chr.commands = tt end tc[k] = chr end -- t.encodingbytes, t.filename, t.fullname, t.name: elsewhere t.size = scaledpoints if t.fonts then t.fonts = table.fastcopy(t.fonts) -- maybe we virtualize more afterwards end return t, delta end --[[ldx--The reason why the scaler is split, is that for a while we experimented
with a helper function. However, in practice the
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.
--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 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.format(name) return fonts.used[name] or "unknown" end --[[ldx--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.
--ldx]]-- fonts.initializers = fonts.initializers or { } fonts.initializers.common = fonts.initializers.common or { } --[[ldx--This feature will remove inter-digit kerns.
--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--This feature will give all glyphs an equal height and/or depth. Valid
values are
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
Analyzers run per script and/or language and are needed in order to process features right.
--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 { } 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 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 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--We move marks into the components list. This saves much nasty testing later on.
--ldx]]-- 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 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 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 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 -- auto complete font with missing composed characters table.insert(fonts.manipulators,"compose") function fonts.initializers.common.compose(tfmdata,value) if value then fonts.vf.aux.compose_characters(tfmdata) end end