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.lua468
1 files changed, 249 insertions, 219 deletions
diff --git a/Master/texmf-dist/tex/context/base/font-ext.lua b/Master/texmf-dist/tex/context/base/font-ext.lua
index 05bdaf2fc3a..4ea9cf3caa0 100644
--- a/Master/texmf-dist/tex/context/base/font-ext.lua
+++ b/Master/texmf-dist/tex/context/base/font-ext.lua
@@ -10,11 +10,15 @@ local utf = unicode.utf8
local next, type, byte = next, type, string.byte
local gmatch, concat = string.gmatch, table.concat
local utfchar = utf.char
+local getparameters = utilities.parsers.getparameters
+
+local allocate = utilities.storage.allocate
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 { }
+local report_expansions = logs.reporter("fonts","expansions")
+local report_protrusions = logs.reporter("fonts","protrusions")
--[[ldx--
<p>When we implement functions that deal with features, most of them
@@ -22,74 +26,13 @@ will depend of the font format. Here we define the few that are kind
of neutral.</p>
--ldx]]--
-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>
---ldx]]--
-
-table.insert(fonts.triggers,"equaldigits")
-
-function initializers.common.equaldigits(tfmdata,value)
- if value then
- local chr = tfmdata.characters
- for i = utfbyte('0'), utfbyte('9') do
- local c = chr[i]
- if c then
- c.kerns = nil
- end
- end
- end
-end
+local fonts = fonts
---[[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]]--
+local otffeatures = fonts.constructors.newfeatures("otf")
+local registerotffeature = otffeatures.register
-table.insert(fonts.triggers,"lineheight")
-
-function initializers.common.lineheight(tfmdata,value)
- if value and type(value) == "string" then
- if value == "none" then
- for _,v in next, 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 next, tfmdata.characters do
- v.height, v.depth = ht, dp
- end
- else
- for _,v in next, tfmdata.characters do
- v.height = ht
- end
- end
- elseif dp > 0 then
- for _,v in next, tfmdata.characters do
- v.depth = dp
- end
- end
- end
- end
- end
-end
+local afmfeatures = fonts.constructors.newfeatures("afm")
+local registerafmfeature = afmfeatures.register
-- -- -- -- -- --
-- shared
@@ -99,11 +42,13 @@ 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]
+ local f_classes = f_where and f_where.classes
+ local class = (g_classes and g_classes[value]) or (f_classes and f_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]
+ local f_vectors = f_where and f_where.vectors
+ local vector = (g_vectors and g_vectors[class_vector]) or (f_vectors and f_vectors[class_vector])
return class, vector
end
end
@@ -112,20 +57,21 @@ end
-- expansion (hz)
-- -- -- -- -- --
-fonts.expansions = fonts.expansions or { }
-fonts.expansions.classes = fonts.expansions.classes or { }
-fonts.expansions.vectors = fonts.expansions.vectors or { }
+fonts.expansions = allocate()
+local expansions = fonts.expansions
-local expansions = fonts.expansions
-local classes = fonts.expansions.classes
-local vectors = fonts.expansions.vectors
+expansions.classes = allocate()
+local classes = expansions.classes
+
+expansions.vectors = allocate()
+local vectors = expansions.vectors
-- beware, pdftex itself uses percentages * 10
classes.preset = { stretch = 2, shrink = 2, step = .5, factor = 1 }
function commands.setupfontexpansion(class,settings)
- aux.getparameters(classes,class,'preset',settings)
+ getparameters(classes,class,'preset',settings)
end
classes['quality'] = {
@@ -146,31 +92,26 @@ vectors['default'] = {
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)
+local function initializeexpansion(tfmdata,value)
if value then
local class, vector = get_class_and_vector(tfmdata,value,"expansions")
if class then
if vector then
- local stretch, shrink, step, factor = class.stretch or 0, class.shrink or 0, class.step or 0, class.factor or 1
+ local stretch = class.stretch or 0
+ local shrink = class.shrink or 0
+ local step = class.step or 0
+ local factor = 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)
+ report_expansions("setting 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
+ tfmdata.parameters.expansion = {
+ stretch = 10 * stretch,
+ shrink = 10 * shrink,
+ step = 10 * step,
+ factor = factor,
+ auto = true,
+ }
local data = characters and characters.data
for i, chr in next, tfmdata.characters do
local v = vector[i]
@@ -194,42 +135,55 @@ function initializers.common.expansion(tfmdata,value)
end
end
elseif trace_expansion then
- logs.report("fonts","unknown expansion vector '%s' in class '%s",class_vector,value)
+ report_expansions("unknown vector '%s' in class '%s",class.vector,value)
end
elseif trace_expansion then
- logs.report("fonts","unknown expansion class '%s'",value)
+ report_expansions("unknown class '%s'",value)
end
end
end
-table.insert(fonts.manipulators,"expansion")
-
-initializers.base.otf.expansion = initializers.common.expansion
-initializers.node.otf.expansion = initializers.common.expansion
+registerotffeature {
+ name = "expansion",
+ description = "apply hz optimization",
+ initializers = {
+ base = initializeexpansion,
+ node = initializeexpansion,
+ }
+}
-initializers.base.afm.expansion = initializers.common.expansion
-initializers.node.afm.expansion = initializers.common.expansion
+registerafmfeature {
+ name = "expansion",
+ description = "apply hz optimization",
+ initializers = {
+ base = initializeexpansion,
+ node = initializeexpansion,
+ }
+}
fonts.goodies.register("expansions", function(...) return fonts.goodies.report("expansions", trace_expansion, ...) end)
+local report_opbd = logs.reporter("fonts","otf opbd")
+
-- -- -- -- -- --
-- protrusion
-- -- -- -- -- --
-fonts.protrusions = fonts.protrusions or { }
-fonts.protrusions.classes = fonts.protrusions.classes or { }
-fonts.protrusions.vectors = fonts.protrusions.vectors or { }
+fonts.protrusions = allocate()
+local protrusions = fonts.protrusions
+
+protrusions.classes = allocate()
+protrusions.vectors = allocate()
-local protrusions = fonts.protrusions
-local classes = fonts.protrusions.classes
-local vectors = fonts.protrusions.vectors
+local classes = protrusions.classes
+local vectors = protrusions.vectors
-- the values need to be revisioned
classes.preset = { factor = 1, left = 1, right = 1 }
function commands.setupfontprotrusion(class,settings)
- aux.getparameters(classes,class,'preset',settings)
+ getparameters(classes,class,'preset',settings)
end
classes['pure'] = {
@@ -252,6 +206,7 @@ vectors['pure'] = {
[0x003A] = { 0, 1 }, -- colon
[0x003B] = { 0, 1 }, -- semicolon
[0x002D] = { 0, 1 }, -- hyphen
+ [0x00AD] = { 0, 1 }, -- also hyphen
[0x2013] = { 0, 0.50 }, -- endash
[0x2014] = { 0, 0.33 }, -- emdash
[0x3001] = { 0, 1 }, -- ideographic comma 、
@@ -277,6 +232,7 @@ vectors['punctuation'] = {
[0x003A] = { 0, 0.50 }, -- colon
[0x003B] = { 0, 0.50 }, -- semicolon
[0x002D] = { 0, 0.70 }, -- hyphen
+ [0x00AD] = { 0, 0.70 }, -- also hyphen
[0x2013] = { 0, 0.30 }, -- endash
[0x2014] = { 0, 0.20 }, -- emdash
[0x060C] = { 0, 0.70 }, -- arabic comma
@@ -325,7 +281,7 @@ vectors['alpha'] = {
}
-vectors['quality'] = table.merge( {},
+vectors['quality'] = table.merged(
vectors['punctuation'],
vectors['alpha']
)
@@ -360,10 +316,13 @@ classes['double'] = { -- for testing opbd
}
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 characters = tfmdata.characters
+ local descriptions = tfmdata.descriptions
+ local properties = tfmdata.properties
+ local rawdata = tfmdata.shared.rawdata
+ local lookuphash = rawdata.lookuphash
+ local script = properties.script
+ local language = properties.language
local done, factor, left, right = false, 1, 1, 1
local class = classes[value]
if class then
@@ -374,21 +333,21 @@ local function map_opbd_onto_protrusion(tfmdata,value,opbd)
factor = tonumber(value) or 1
end
if opbd ~= "right" then
- local validlookups, lookuplist = fonts.otf.collect_lookups(otfdata,"lfbd",script,language)
+ local validlookups, lookuplist = otf.collectlookups(rawdata,"lfbd",script,language)
if validlookups then
for i=1,#lookuplist do
local lookup = lookuplist[i]
- local data = singles[lookup]
+ local data = lookuphash[lookup]
if data then
if trace_protrusion then
- logs.report("fonts","set left protrusion using lfbd lookup '%s'",lookup)
+ report_protrusions("setting left 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," "))
+ report_protrusions("lfbd -> %s -> U+%05X (%s) -> %0.03f (%s)",lookup,k,utfchar(k),p,concat(v," "))
end
end
done = true
@@ -397,21 +356,21 @@ local function map_opbd_onto_protrusion(tfmdata,value,opbd)
end
end
if opbd ~= "left" then
- local validlookups, lookuplist = fonts.otf.collect_lookups(otfdata,"rtbd",script,language)
+ local validlookups, lookuplist = otf.collectlookups(rawdata,"rtbd",script,language)
if validlookups then
for i=1,#lookuplist do
local lookup = lookuplist[i]
- local data = singles[lookup]
+ local data = lookuphash[lookup]
if data then
if trace_protrusion then
- logs.report("fonts","set right protrusion using rtbd lookup '%s'",lookup)
+ report_protrusions("setting right 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," "))
+ report_protrusions("rtbd -> %s -> U+%05X (%s) -> %0.03f (%s)",lookup,k,utfchar(k),p,concat(v," "))
end
end
end
@@ -419,7 +378,9 @@ local function map_opbd_onto_protrusion(tfmdata,value,opbd)
end
end
end
- tfmdata.auto_protrude = done
+ tfmdata.parameters.protrusion {
+ auto = true
+ }
end
-- The opbd test is just there because it was discussed on the
@@ -427,7 +388,7 @@ end
-- 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)
+local function initializeprotrusion(tfmdata,value)
if value then
local opbd = tfmdata.shared.features.opbd
if opbd then
@@ -441,11 +402,17 @@ function initializers.common.protrusion(tfmdata,value)
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)
+ report_protrusions("setting 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
+ tfmdata.parameters.protrusion = {
+ factor = factor,
+ left = left,
+ right = right,
+ auto = true,
+ }
for i, chr in next, tfmdata.characters do
local v, pl, pr = vector[i], nil, nil
if v then
@@ -476,70 +443,89 @@ function initializers.common.protrusion(tfmdata,value)
end
end
elseif trace_protrusion then
- logs.report("fonts","unknown protrusion vector '%s' in class '%s",class_vector,value)
+ report_protrusions("unknown vector '%s' in class '%s",class.vector,value)
end
elseif trace_protrusion then
- logs.report("fonts","unknown protrusion class '%s'",value)
+ report_protrusions("unknown class '%s'",value)
end
end
end
end
-table.insert(fonts.manipulators,"protrusion")
-
-initializers.base.otf.protrusion = initializers.common.protrusion
-initializers.node.otf.protrusion = initializers.common.protrusion
+registerotffeature {
+ name = "protrusion",
+ description = "shift characters into the left and or right margin",
+ initializers = {
+ base = initializeprotrusion,
+ node = initializeprotrusion,
+ }
+}
-initializers.base.afm.protrusion = initializers.common.protrusion
-initializers.node.afm.protrusion = initializers.common.protrusion
+registerafmfeature {
+ name = "protrusion",
+ description = "shift characters into the left and or right margin",
+ initializers = {
+ base = initializeprotrusion,
+ node = initializeprotrusion,
+ }
+}
fonts.goodies.register("protrusions", function(...) return fonts.goodies.report("protrusions", trace_protrusion, ...) end)
-- -- --
-function initializers.common.nostackmath(tfmdata,value)
- tfmdata.ignore_stack_math = value
+local function initializenostackmath(tfmdata,value)
+ tfmdata.properties.no_stackmath = value and true
end
-table.insert(fonts.manipulators,"nostackmath")
-
-initializers.base.otf.nostackmath = initializers.common.nostackmath
-initializers.node.otf.nostackmath = initializers.common.nostackmath
-
-table.insert(fonts.triggers,"itlc")
+registerotffeature {
+ name = "nostackmath",
+ description = "disable math stacking mechanism",
+ initializers = {
+ base = initializenostackmath,
+ node = initializenostackmath,
+ }
+}
-function initializers.common.itlc(tfmdata,value)
+local function initializeitlc(tfmdata,value)
if value then
-- the magic 40 and it formula come from Dohyun Kim
- local fontdata = tfmdata.shared.otfdata or tfmdata.shared.afmdata
- local metadata = fontdata and fontdata.metadata
- if metadata then
- local italicangle = metadata.italicangle
- if italicangle and italicangle ~= 0 then
- local uwidth = (metadata.uwidth or 40)/2
- for unicode, d in next, tfmdata.descriptions do
- local it = d.boundingbox[3] - d.width + uwidth
- if it ~= 0 then
- d.italic = it
- end
+ local parameters = tfmdata.parameters
+ local italicangle = parameters.italicangle
+ if italicangle and italicangle ~= 0 then
+ local uwidth = (parameters.uwidth or 40)/2
+ for unicode, d in next, tfmdata.descriptions do
+ local it = d.boundingbox[3] - d.width + uwidth
+ if it ~= 0 then
+ d.italic = it
end
- tfmdata.has_italic = true
end
+ tfmdata.properties.italic_correction = true
end
end
end
-initializers.base.otf.itlc = initializers.common.itlc
-initializers.node.otf.itlc = initializers.common.itlc
+registerotffeature {
+ name = "itlc",
+ description = "italic correction",
+ initializers = {
+ base = initializeitlc,
+ node = initializeitlc,
+ }
+}
-initializers.base.afm.itlc = initializers.common.itlc
-initializers.node.afm.itlc = initializers.common.itlc
+registerafmfeature {
+ name = "itlc",
+ description = "italic correction",
+ initializers = {
+ base = initializeitlc,
+ node = initializeitlc,
+ }
+}
-- slanting
-table.insert(fonts.triggers,"slant")
-
-function initializers.common.slant(tfmdata,value)
+local function initializeslant(tfmdata,value)
value = tonumber(value)
if not value then
value = 0
@@ -548,18 +534,28 @@ function initializers.common.slant(tfmdata,value)
elseif value < -1 then
value = -1
end
- tfmdata.slant_factor = value
+ tfmdata.parameters.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
+registerotffeature {
+ name = "slant",
+ description = "slant glyphs",
+ initializers = {
+ base = initializeslant,
+ node = initializeslant,
+ }
+}
-table.insert(fonts.triggers,"extend")
+registerafmfeature {
+ name = "slant",
+ description = "slant glyphs",
+ initializers = {
+ base = initializeslant,
+ node = initializeslant,
+ }
+}
-function initializers.common.extend(tfmdata,value)
+local function initializeextend(tfmdata,value)
value = tonumber(value)
if not value then
value = 0
@@ -568,56 +564,90 @@ function initializers.common.extend(tfmdata,value)
elseif value < -10 then
value = -10
end
- tfmdata.extend_factor = value
+ tfmdata.parameters.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
+registerotffeature {
+ name = "extend",
+ description = "scale glyphs horizontally",
+ initializers = {
+ base = initializeextend,
+ node = initializeextend,
+ }
}
-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")
+registerafmfeature {
+ name = "extend",
+ description = "scale glyphs horizontally",
+ initializers = {
+ base = initializeextend,
+ node = initializeextend,
+ }
+}
-table.insert(fonts.manipulators,"formatters") -- at end
+-- -- historic stuff, move from font-ota (handled differently, typo-rep)
+--
+-- local delete_node = nodes.delete
+-- local fontdata = fonts.hashes.identifiers
+--
+-- local nodecodes = nodes.nodecodes
+-- local glyph_code = nodecodes.glyph
+--
+-- local strippables = allocate()
+-- fonts.strippables = strippables
+--
+-- strippables.joiners = table.tohash {
+-- 0x200C, -- zwnj
+-- 0x200D, -- zwj
+-- }
+--
+-- strippables.all = table.tohash {
+-- 0x000AD, 0x017B4, 0x017B5, 0x0200B, 0x0200C, 0x0200D, 0x0200E, 0x0200F, 0x0202A, 0x0202B,
+-- 0x0202C, 0x0202D, 0x0202E, 0x02060, 0x02061, 0x02062, 0x02063, 0x0206A, 0x0206B, 0x0206C,
+-- 0x0206D, 0x0206E, 0x0206F, 0x0FEFF, 0x1D173, 0x1D174, 0x1D175, 0x1D176, 0x1D177, 0x1D178,
+-- 0x1D179, 0x1D17A, 0xE0001, 0xE0020, 0xE0021, 0xE0022, 0xE0023, 0xE0024, 0xE0025, 0xE0026,
+-- 0xE0027, 0xE0028, 0xE0029, 0xE002A, 0xE002B, 0xE002C, 0xE002D, 0xE002E, 0xE002F, 0xE0030,
+-- 0xE0031, 0xE0032, 0xE0033, 0xE0034, 0xE0035, 0xE0036, 0xE0037, 0xE0038, 0xE0039, 0xE003A,
+-- 0xE003B, 0xE003C, 0xE003D, 0xE003E, 0xE003F, 0xE0040, 0xE0041, 0xE0042, 0xE0043, 0xE0044,
+-- 0xE0045, 0xE0046, 0xE0047, 0xE0048, 0xE0049, 0xE004A, 0xE004B, 0xE004C, 0xE004D, 0xE004E,
+-- 0xE004F, 0xE0050, 0xE0051, 0xE0052, 0xE0053, 0xE0054, 0xE0055, 0xE0056, 0xE0057, 0xE0058,
+-- 0xE0059, 0xE005A, 0xE005B, 0xE005C, 0xE005D, 0xE005E, 0xE005F, 0xE0060, 0xE0061, 0xE0062,
+-- 0xE0063, 0xE0064, 0xE0065, 0xE0066, 0xE0067, 0xE0068, 0xE0069, 0xE006A, 0xE006B, 0xE006C,
+-- 0xE006D, 0xE006E, 0xE006F, 0xE0070, 0xE0071, 0xE0072, 0xE0073, 0xE0074, 0xE0075, 0xE0076,
+-- 0xE0077, 0xE0078, 0xE0079, 0xE007A, 0xE007B, 0xE007C, 0xE007D, 0xE007E, 0xE007F,
+-- }
+--
+-- strippables[true] = strippables.joiners
+--
+-- local function processformatters(head,font)
+-- local subset = fontdata[font].shared.features.formatters
+-- local vector = subset and strippables[subset]
+-- if vector then
+-- local current, done = head, false
+-- while current do
+-- if current.id == glyph_code and current.subtype<256 and current.font == font then
+-- local char = current.char
+-- if vector[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
+--
+-- registerotffeature {
+-- name = "formatters",
+-- description = "hide formatting characters",
+-- methods = {
+-- base = processformatters,
+-- node = processformatters,
+-- }
+-- }