summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/tex/context/base/font-ext.lua
diff options
context:
space:
mode:
Diffstat (limited to 'Master/texmf-dist/tex/context/base/font-ext.lua')
-rw-r--r--Master/texmf-dist/tex/context/base/font-ext.lua440
1 files changed, 292 insertions, 148 deletions
diff --git a/Master/texmf-dist/tex/context/base/font-ext.lua b/Master/texmf-dist/tex/context/base/font-ext.lua
index 17c302c5325..05bdaf2fc3a 100644
--- a/Master/texmf-dist/tex/context/base/font-ext.lua
+++ b/Master/texmf-dist/tex/context/base/font-ext.lua
@@ -1,12 +1,20 @@
if not modules then modules = { } end modules ['font-ext'] = {
version = 1.001,
- comment = "companion to font-ini.tex and hand-ini.tex",
+ comment = "companion to font-ini.mkiv and hand-ini.mkiv",
author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright = "PRAGMA ADE / ConTeXt Development Team",
license = "see context related readme files"
}
+local utf = unicode.utf8
local next, type, byte = next, type, string.byte
+local gmatch, concat = string.gmatch, table.concat
+local utfchar = utf.char
+
+local trace_protrusion = false trackers.register("fonts.protrusion", function(v) trace_protrusion = v end)
+local trace_expansion = false trackers.register("fonts.expansion", function(v) trace_expansion = v end)
+
+commands = commands or { }
--[[ldx--
<p>When we implement functions that deal with features, most of them
@@ -16,9 +24,11 @@ of neutral.</p>
fonts.triggers = fonts.triggers or { }
fonts.initializers = fonts.initializers or { }
+fonts.methods = fonts.methods or { }
fonts.initializers.common = fonts.initializers.common or { }
local initializers = fonts.initializers
+local methods = fonts.methods
--[[ldx--
<p>This feature will remove inter-digit kerns.</p>
@@ -81,116 +91,22 @@ function initializers.common.lineheight(tfmdata,value)
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]]--
+-- -- -- -- -- --
+-- shared
+-- -- -- -- -- --
---~ 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 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 next, 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 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 next, 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 initializers.common.oldstyle(tfmdata,value)
---~ initializers.common.remap(tfmdata,value,oldstyle)
---~ end
---~ function initializers.common.smallcaps(tfmdata,value)
---~ initializers.common.remap(tfmdata,value,smallcaps)
---~ end
---~
---~ function 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 next, characters do
---~ local name = descriptions[u].name
---~ if name then
---~ local p = lower(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
---~
---~ end
---~
---~ function initializers.common.install(format,feature) -- 'afm','lineheight'
---~ initializers.base[format][feature] = initializers.common[feature]
---~ initializers.node[format][feature] = initializers.common[feature]
---~ end
+local function get_class_and_vector(tfmdata,value,where) -- "expansions"
+ local g_where = tfmdata.goodies and tfmdata.goodies[where]
+ local f_where = fonts[where]
+ local g_classes = g_where and g_where.classes
+ local class = g_where and g_where[value] or f_where.classes[value]
+ if class then
+ local class_vector = class.vector
+ local g_vectors = g_where and g_where.vectors
+ local vector = g_vectors and g_vectors[class_vector] or f_where.vectors[class_vector]
+ return class, vector
+ end
+end
-- -- -- -- -- --
-- expansion (hz)
@@ -228,21 +144,37 @@ vectors['default'] = {
[byte('2')] = 0.7, [byte('3')] = 0.7, [byte('6')] = 0.7, [byte('8')] = 0.7, [byte('9')] = 0.7,
}
+vectors['quality'] = vectors['default'] -- metatable ?
+
+--~ function table.locator(...)
+--~ local k = { ... }
+--~ return function(t)
+--~ for i=1,#k do
+--~ t = t[k[i]]
+--~ if not k then
+--~ return false
+--~ end
+--~ end
+--~ return t
+--~ end
+--~ end
+
+--~ local locate = table.locator { "goodies", "expansions" }
+
function initializers.common.expansion(tfmdata,value)
if value then
- local class = classes[value]
+ local class, vector = get_class_and_vector(tfmdata,value,"expansions")
if class then
- local vector = vectors[class.vector]
if vector then
- tfmdata.stretch = (class.stretch or 0) * 10
- tfmdata.shrink = (class.shrink or 0) * 10
- tfmdata.step = (class.step or 0) * 10
- tfmdata.auto_expand = true
- local factor = class.factor or 1
- local data = characters.data
+ local stretch, shrink, step, factor = class.stretch or 0, class.shrink or 0, class.step or 0, class.factor or 1
+ if trace_expansion then
+ logs.report("fonts","set expansion class %s, vector: %s, factor: %s, stretch: %s, shrink: %s, step: %s",value,class_vector,factor,stretch,shrink,step)
+ end
+ tfmdata.stretch, tfmdata.shrink, tfmdata.step, tfmdata.auto_expand = stretch * 10, shrink * 10, step * 10, true
+ local data = characters and characters.data
for i, chr in next, tfmdata.characters do
local v = vector[i]
- if not v then
+ if data and not v then -- we could move the data test outside (needed for plain)
local d = data[i]
if d then
local s = d.shcode
@@ -261,7 +193,11 @@ function initializers.common.expansion(tfmdata,value)
chr.expansion_factor = factor
end
end
+ elseif trace_expansion then
+ logs.report("fonts","unknown expansion vector '%s' in class '%s",class_vector,value)
end
+ elseif trace_expansion then
+ logs.report("fonts","unknown expansion class '%s'",value)
end
end
end
@@ -274,6 +210,8 @@ initializers.node.otf.expansion = initializers.common.expansion
initializers.base.afm.expansion = initializers.common.expansion
initializers.node.afm.expansion = initializers.common.expansion
+fonts.goodies.register("expansions", function(...) return fonts.goodies.report("expansions", trace_expansion, ...) end)
+
-- -- -- -- -- --
-- protrusion
-- -- -- -- -- --
@@ -288,7 +226,7 @@ local vectors = fonts.protrusions.vectors
-- the values need to be revisioned
-classes.preset = { factor = 1 }
+classes.preset = { factor = 1, left = 1, right = 1 }
function commands.setupfontprotrusion(class,settings)
aux.getparameters(classes,class,'preset',settings)
@@ -392,41 +330,156 @@ vectors['quality'] = table.merge( {},
vectors['alpha']
)
+-- As this is experimental code, users should not depend on it. The
+-- implications are still discussed on the ConTeXt Dev List and we're
+-- not sure yet what exactly the spec is (the next code is tested with
+-- a gyre font patched by / fea file made by Khaled Hosny). The double
+-- trick should not be needed it proper hanging punctuation is used in
+-- which case values < 1 can be used.
+--
+-- preferred (in context, usine vectors):
+--
+-- \definefontfeature[whatever][default][mode=node,protrusion=quality]
+--
+-- using lfbd and rtbd, with possibibility to enable only one side :
+--
+-- \definefontfeature[whocares][default][mode=node,protrusion=yes, opbd=yes,script=latn]
+-- \definefontfeature[whocares][default][mode=node,protrusion=right,opbd=yes,script=latn]
+--
+-- idem, using multiplier
+--
+-- \definefontfeature[whocares][default][mode=node,protrusion=2,opbd=yes,script=latn]
+-- \definefontfeature[whocares][default][mode=node,protrusion=double,opbd=yes,script=latn]
+--
+-- idem, using named feature file (less frozen):
+--
+-- \definefontfeature[whocares][default][mode=node,protrusion=2,opbd=yes,script=latn,featurefile=texgyrepagella-regularxx.fea]
+
+classes['double'] = { -- for testing opbd
+ factor = 2, left = 1, right = 1,
+}
+
+local function map_opbd_onto_protrusion(tfmdata,value,opbd)
+ local characters, descriptions = tfmdata.characters, tfmdata.descriptions
+ local otfdata = tfmdata.shared.otfdata
+ local singles = otfdata.shared.featuredata.gpos_single
+ local script, language = tfmdata.script, tfmdata.language
+ local done, factor, left, right = false, 1, 1, 1
+ local class = classes[value]
+ if class then
+ factor = class.factor or 1
+ left = class.left or 1
+ right = class.right or 1
+ else
+ factor = tonumber(value) or 1
+ end
+ if opbd ~= "right" then
+ local validlookups, lookuplist = fonts.otf.collect_lookups(otfdata,"lfbd",script,language)
+ if validlookups then
+ for i=1,#lookuplist do
+ local lookup = lookuplist[i]
+ local data = singles[lookup]
+ if data then
+ if trace_protrusion then
+ logs.report("fonts","set left protrusion using lfbd lookup '%s'",lookup)
+ end
+ for k, v in next, data do
+ -- local p = - v[3] / descriptions[k].width-- or 1 ~= 0 too but the same
+ local p = - (v[1] / 1000) * factor * left
+ characters[k].left_protruding = p
+ if trace_protrusion then
+ logs.report("opbd","lfbd -> %s -> 0x%05X (%s) -> %0.03f (%s)",lookup,k,utfchar(k),p,concat(v," "))
+ end
+ end
+ done = true
+ end
+ end
+ end
+ end
+ if opbd ~= "left" then
+ local validlookups, lookuplist = fonts.otf.collect_lookups(otfdata,"rtbd",script,language)
+ if validlookups then
+ for i=1,#lookuplist do
+ local lookup = lookuplist[i]
+ local data = singles[lookup]
+ if data then
+ if trace_protrusion then
+ logs.report("fonts","set right protrusion using rtbd lookup '%s'",lookup)
+ end
+ for k, v in next, data do
+ -- local p = v[3] / descriptions[k].width -- or 3
+ local p = (v[1] / 1000) * factor * right
+ characters[k].right_protruding = p
+ if trace_protrusion then
+ logs.report("opbd","rtbd -> %s -> 0x%05X (%s) -> %0.03f (%s)",lookup,k,utfchar(k),p,concat(v," "))
+ end
+ end
+ end
+ done = true
+ end
+ end
+ end
+ tfmdata.auto_protrude = done
+end
+
+-- The opbd test is just there because it was discussed on the
+-- context development list. However, the mentioned fxlbi.otf font
+-- only has some kerns for digits. So, consider this feature not
+-- supported till we have a proper test font.
+
function initializers.common.protrusion(tfmdata,value)
if value then
- local class = classes[value]
- if class then
- local vector = vectors[class.vector]
- if vector then
- local factor = class.factor or 1
- local data = characters.data
- local emwidth = tfmdata.parameters.quad
- tfmdata.auto_protrude = true
- for i, chr in next, tfmdata.characters do
- local v, pl, pr = vector[i], nil, nil
- if v then
- pl, pr = v[1], v[2]
- else
- local d = data[i]
- if d then
- local s = d.shcode
- if not s then
- -- sorry
- elseif type(s) == "table" then
- local vl, vr = vector[s[1]], vector[s[#s]]
- if vl then pl = vl[1] end
- if vr then pr = vr[2] end
- else
- v = vector[s]
- if v then
- pl, pr = v[1], v[2]
+ local opbd = tfmdata.shared.features.opbd
+ if opbd then
+ -- possible values: left right both yes no (experimental)
+ map_opbd_onto_protrusion(tfmdata,value,opbd)
+ else
+ local class, vector = get_class_and_vector(tfmdata,value,"protrusions")
+ if class then
+ if vector then
+ local factor = class.factor or 1
+ local left = class.left or 1
+ local right = class.right or 1
+ if trace_protrusion then
+ logs.report("fonts","set protrusion class %s, vector: %s, factor: %s, left: %s, right: %s",value,class_vector,factor,left,right)
+ end
+ local data = characters.data
+ local emwidth = tfmdata.parameters.quad
+ tfmdata.auto_protrude = true
+ for i, chr in next, tfmdata.characters do
+ local v, pl, pr = vector[i], nil, nil
+ if v then
+ pl, pr = v[1], v[2]
+ else
+ local d = data[i]
+ if d then
+ local s = d.shcode
+ if not s then
+ -- sorry
+ elseif type(s) == "table" then
+ local vl, vr = vector[s[1]], vector[s[#s]]
+ if vl then pl = vl[1] end
+ if vr then pr = vr[2] end
+ else
+ v = vector[s]
+ if v then
+ pl, pr = v[1], v[2]
+ end
end
end
end
+ if pl and pl ~= 0 then
+ chr.left_protruding = left *pl*factor
+ end
+ if pr and pr ~= 0 then
+ chr.right_protruding = right*pr*factor
+ end
end
- if pl and pl ~= 0 then chr.left_protruding = pl*factor end
- if pr and pr ~= 0 then chr.right_protruding = pr*factor end
+ elseif trace_protrusion then
+ logs.report("fonts","unknown protrusion vector '%s' in class '%s",class_vector,value)
end
+ elseif trace_protrusion then
+ logs.report("fonts","unknown protrusion class '%s'",value)
end
end
end
@@ -440,6 +493,10 @@ initializers.node.otf.protrusion = initializers.common.protrusion
initializers.base.afm.protrusion = initializers.common.protrusion
initializers.node.afm.protrusion = initializers.common.protrusion
+fonts.goodies.register("protrusions", function(...) return fonts.goodies.report("protrusions", trace_protrusion, ...) end)
+
+-- -- --
+
function initializers.common.nostackmath(tfmdata,value)
tfmdata.ignore_stack_math = value
end
@@ -477,3 +534,90 @@ initializers.node.otf.itlc = initializers.common.itlc
initializers.base.afm.itlc = initializers.common.itlc
initializers.node.afm.itlc = initializers.common.itlc
+
+-- slanting
+
+table.insert(fonts.triggers,"slant")
+
+function initializers.common.slant(tfmdata,value)
+ value = tonumber(value)
+ if not value then
+ value = 0
+ elseif value > 1 then
+ value = 1
+ elseif value < -1 then
+ value = -1
+ end
+ tfmdata.slant_factor = value
+end
+
+initializers.base.otf.slant = initializers.common.slant
+initializers.node.otf.slant = initializers.common.slant
+
+initializers.base.afm.slant = initializers.common.slant
+initializers.node.afm.slant = initializers.common.slant
+
+table.insert(fonts.triggers,"extend")
+
+function initializers.common.extend(tfmdata,value)
+ value = tonumber(value)
+ if not value then
+ value = 0
+ elseif value > 10 then
+ value = 10
+ elseif value < -10 then
+ value = -10
+ end
+ tfmdata.extend_factor = value
+end
+
+initializers.base.otf.extend = initializers.common.extend
+initializers.node.otf.extend = initializers.common.extend
+
+initializers.base.afm.extend = initializers.common.extend
+initializers.node.afm.extend = initializers.common.extend
+
+-- historic stuff, move from font-ota
+
+local delete_node = nodes.delete
+local glyph = node.id("glyph")
+local fontdata = fonts.ids
+
+fonts.strippables = fonts.strippables or { -- just a placeholder
+ [0x200C] = true, -- zwnj
+ [0x200D] = true, -- zwj
+}
+
+local strippables = fonts.strippables
+
+local function processformatters(head,font)
+ local how = fontdata[font].shared.features.formatters
+ if how == nil or how == "strip" then -- nil when forced
+ local current, done = head, false
+ while current do
+ if current.id == glyph and current.subtype<256 and current.font == font then
+ local char = current.char
+ if strippables[char] then
+ head, current = delete_node(head,current)
+ done = true
+ else
+ current = current.next
+ end
+ else
+ current = current.next
+ end
+ end
+ return head, done
+ else
+ return head, false
+ end
+end
+
+methods.node.otf.formatters = processformatters
+methods.base.otf.formatters = processformatters
+
+fonts.otf.tables.features['formatters'] = 'Hide Formatting Characters'
+
+fonts.otf.features.register("formatters")
+
+table.insert(fonts.manipulators,"formatters") -- at end