diff options
Diffstat (limited to 'Master/texmf-dist/tex/context/base/mkiv/font-ext.lua')
-rw-r--r-- | Master/texmf-dist/tex/context/base/mkiv/font-ext.lua | 1856 |
1 files changed, 1856 insertions, 0 deletions
diff --git a/Master/texmf-dist/tex/context/base/mkiv/font-ext.lua b/Master/texmf-dist/tex/context/base/mkiv/font-ext.lua new file mode 100644 index 00000000000..d873dccd4d3 --- /dev/null +++ b/Master/texmf-dist/tex/context/base/mkiv/font-ext.lua @@ -0,0 +1,1856 @@ +if not modules then modules = { } end modules ['font-ext'] = { + version = 1.001, + 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 next, type, tonumber = next, type, tonumber +local byte, find, formatters = string.byte, string.find, string.formatters +local utfchar = utf.char +local sortedhash, sortedkeys, sort = table.sortedhash, table.sortedkeys, table.sort + +local context = context +local fonts = fonts +local utilities = utilities + +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) + +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 +will depend of the font format. Here we define the few that are kind +of neutral.</p> +--ldx]]-- + +local handlers = fonts.handlers +local hashes = fonts.hashes +local otf = handlers.otf +local afm = handlers.afm + +local registerotffeature = otf.features.register +local registerafmfeature = afm.features.register + +local fontdata = hashes.identifiers +local fontproperties = hashes.properties + +local constructors = fonts.constructors +local getprivate = constructors.getprivate + +local allocate = utilities.storage.allocate +local settings_to_array = utilities.parsers.settings_to_array +local settings_to_hash = utilities.parsers.settings_to_hash +local getparameters = utilities.parsers.getparameters +local gettexdimen = tex.getdimen +local family_font = node.family_font + +local setmetatableindex = table.setmetatableindex + +local implement = interfaces.implement +local variables = interfaces.variables + + +local v_background = variables.background +local v_frame = variables.frame +local v_empty = variables.empty +local v_none = variables.none + +-- -- -- -- -- -- +-- shared +-- -- -- -- -- -- + +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 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 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 + +-- -- -- -- -- -- +-- expansion (hz) +-- -- -- -- -- -- + +local expansions = fonts.expansions or allocate() + +fonts.expansions = expansions + +local classes = expansions.classes or allocate() +local vectors = expansions.vectors or allocate() + +expansions.classes = classes +expansions.vectors = vectors + +-- beware, pdftex itself uses percentages * 10 +-- +-- todo: get rid of byte() here + +classes.preset = { stretch = 2, shrink = 2, step = .5, factor = 1 } + +classes['quality'] = { + stretch = 2, shrink = 2, step = .5, vector = 'default', factor = 1 +} + +vectors['default'] = { + [byte('A')] = 0.5, [byte('B')] = 0.7, [byte('C')] = 0.7, [byte('D')] = 0.5, [byte('E')] = 0.7, + [byte('F')] = 0.7, [byte('G')] = 0.5, [byte('H')] = 0.7, [byte('K')] = 0.7, [byte('M')] = 0.7, + [byte('N')] = 0.7, [byte('O')] = 0.5, [byte('P')] = 0.7, [byte('Q')] = 0.5, [byte('R')] = 0.7, + [byte('S')] = 0.7, [byte('U')] = 0.7, [byte('W')] = 0.7, [byte('Z')] = 0.7, + [byte('a')] = 0.7, [byte('b')] = 0.7, [byte('c')] = 0.7, [byte('d')] = 0.7, [byte('e')] = 0.7, + [byte('g')] = 0.7, [byte('h')] = 0.7, [byte('k')] = 0.7, [byte('m')] = 0.7, [byte('n')] = 0.7, + [byte('o')] = 0.7, [byte('p')] = 0.7, [byte('q')] = 0.7, [byte('s')] = 0.7, [byte('u')] = 0.7, + [byte('w')] = 0.7, [byte('z')] = 0.7, + [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 ? + +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 = 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 + report_expansions("setting class %a, vector %a, factor %a, stretch %a, shrink %a, step %a", + value,class.vector,factor,stretch,shrink,step) + end + tfmdata.parameters.expansion = { + stretch = 10 * stretch, + shrink = 10 * shrink, + step = 10 * step, + factor = factor, + } + local data = characters and characters.data + for i, chr in next, tfmdata.characters do + local v = vector[i] + 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 + if not s then + -- sorry + elseif type(s) == "table" then + v = ((vector[s[1]] or 0) + (vector[s[#s]] or 0)) / 2 + else + v = vector[s] or 0 + end + end + end + if v and v ~= 0 then + chr.expansion_factor = v*factor + else -- can be option + chr.expansion_factor = factor + end + end + elseif trace_expansion then + report_expansions("unknown vector %a in class %a",class.vector,value) + end + elseif trace_expansion then + report_expansions("unknown class %a",value) + end + end +end + +local expansion_specification = { + name = "expansion", + description = "apply hz optimization", + initializers = { + base = initializeexpansion, + node = initializeexpansion, + } +} + +registerotffeature(expansion_specification) +registerafmfeature(expansion_specification) + +fonts.goodies.register("expansions", function(...) return fonts.goodies.report("expansions", trace_expansion, ...) end) + +implement { + name = "setupfontexpansion", + arguments = "2 strings", + actions = function(class,settings) getparameters(classes,class,'preset',settings) end +} + +-- -- -- -- -- -- +-- protrusion +-- -- -- -- -- -- + +fonts.protrusions = allocate() +local protrusions = fonts.protrusions + +protrusions.classes = allocate() +protrusions.vectors = allocate() + +local classes = protrusions.classes +local vectors = protrusions.vectors + +-- the values need to be revisioned + +classes.preset = { factor = 1, left = 1, right = 1 } + +classes['pure'] = { + vector = 'pure', factor = 1 +} +classes['punctuation'] = { + vector = 'punctuation', factor = 1 +} +classes['alpha'] = { + vector = 'alpha', factor = 1 +} +classes['quality'] = { + vector = 'quality', factor = 1 +} + +vectors['pure'] = { + + [0x002C] = { 0, 1 }, -- comma + [0x002E] = { 0, 1 }, -- period + [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 、 + [0x3002] = { 0, 1 }, -- ideographic full stop 。 + [0x060C] = { 0, 1 }, -- arabic comma ، + [0x061B] = { 0, 1 }, -- arabic semicolon ؛ + [0x06D4] = { 0, 1 }, -- arabic full stop ۔ + +} + +vectors['punctuation'] = { + + [0x003F] = { 0, 0.20 }, -- ? + [0x00BF] = { 0, 0.20 }, -- ¿ + [0x0021] = { 0, 0.20 }, -- ! + [0x00A1] = { 0, 0.20 }, -- ¡ + [0x0028] = { 0.05, 0 }, -- ( + [0x0029] = { 0, 0.05 }, -- ) + [0x005B] = { 0.05, 0 }, -- [ + [0x005D] = { 0, 0.05 }, -- ] + [0x002C] = { 0, 0.70 }, -- comma + [0x002E] = { 0, 0.70 }, -- period + [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 + [0x061B] = { 0, 0.50 }, -- arabic semicolon + [0x06D4] = { 0, 0.70 }, -- arabic full stop + [0x061F] = { 0, 0.20 }, -- ؟ + + -- todo: left and right quotes: .5 double, .7 single + + [0x2039] = { 0.70, 0.70 }, -- left single guillemet ‹ + [0x203A] = { 0.70, 0.70 }, -- right single guillemet › + [0x00AB] = { 0.50, 0.50 }, -- left guillemet « + [0x00BB] = { 0.50, 0.50 }, -- right guillemet » + + [0x2018] = { 0.70, 0.70 }, -- left single quotation mark ‘ + [0x2019] = { 0, 0.70 }, -- right single quotation mark ’ + [0x201A] = { 0.70, 0 }, -- single low-9 quotation mark , + [0x201B] = { 0.70, 0 }, -- single high-reversed-9 quotation mark ‛ + [0x201C] = { 0.50, 0.50 }, -- left double quotation mark “ + [0x201D] = { 0, 0.50 }, -- right double quotation mark ” + [0x201E] = { 0.50, 0 }, -- double low-9 quotation mark „ + [0x201F] = { 0.50, 0 }, -- double high-reversed-9 quotation mark ‟ + +} + +vectors['alpha'] = { + + [byte("A")] = { .05, .05 }, + [byte("F")] = { 0, .05 }, + [byte("J")] = { .05, 0 }, + [byte("K")] = { 0, .05 }, + [byte("L")] = { 0, .05 }, + [byte("T")] = { .05, .05 }, + [byte("V")] = { .05, .05 }, + [byte("W")] = { .05, .05 }, + [byte("X")] = { .05, .05 }, + [byte("Y")] = { .05, .05 }, + + [byte("k")] = { 0, .05 }, + [byte("r")] = { 0, .05 }, + [byte("t")] = { 0, .05 }, + [byte("v")] = { .05, .05 }, + [byte("w")] = { .05, .05 }, + [byte("x")] = { .05, .05 }, + [byte("y")] = { .05, .05 }, + +} + +vectors['quality'] = table.merged( + vectors['punctuation'], + 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 = tfmdata.characters + local descriptions = tfmdata.descriptions + local properties = tfmdata.properties + local resources = tfmdata.resources + local rawdata = tfmdata.shared.rawdata + local lookuphash = rawdata.lookuphash + local lookuptags = resources.lookuptags + local script = properties.script + local language = properties.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 = otf.collectlookups(rawdata,"lfbd",script,language) + if validlookups then + for i=1,#lookuplist do + local lookup = lookuplist[i] + local steps = lookup.steps + if steps then + if trace_protrusion then + report_protrusions("setting left using lfbd") + end + for i=1,#steps do + local step = steps[i] + local coverage = step.coverage + if coverage then + for k, v in next, coverage 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 + report_protrusions("lfbd -> %C -> %p",k,p) + end + end + end + end + done = true + end + end + end + end + if opbd ~= "left" then + local validlookups, lookuplist = otf.collectlookups(rawdata,"rtbd",script,language) + if validlookups then + for i=1,#lookuplist do + local lookup = lookuplist[i] + local steps = lookup.steps + if steps then + if trace_protrusion then + report_protrusions("setting right using rtbd") + end + for i=1,#steps do + local step = steps[i] + local coverage = step.coverage + if coverage then + for k, v in next, coverage 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 + report_protrusions("rtbd -> %C -> %p",k,p) + end + end + end + end + end + done = true + end + end + end +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. + +local function initializeprotrusion(tfmdata,value) + if value then + 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 + report_protrusions("setting class %a, vector %a, factor %a, left %a, right %a", + value,class.vector,factor,left,right) + end + local data = characters.data + local emwidth = tfmdata.parameters.quad + tfmdata.parameters.protrusion = { + factor = factor, + left = left, + right = right, + } + 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 + elseif trace_protrusion then + report_protrusions("unknown vector %a in class %a",class.vector,value) + end + elseif trace_protrusion then + report_protrusions("unknown class %a",value) + end + end + end +end + +local protrusion_specification = { + name = "protrusion", + description = "l/r margin character protrusion", + initializers = { + base = initializeprotrusion, + node = initializeprotrusion, + } +} + +registerotffeature(protrusion_specification) +registerafmfeature(protrusion_specification) + +fonts.goodies.register("protrusions", function(...) return fonts.goodies.report("protrusions", trace_protrusion, ...) end) + +implement { + name = "setupfontprotrusion", + arguments = "2 strings", + actions = function(class,settings) getparameters(classes,class,'preset',settings) end +} + +-- -- -- + +local function initializenostackmath(tfmdata,value) + tfmdata.properties.nostackmath = value and true +end + +registerotffeature { + name = "nostackmath", + description = "disable math stacking mechanism", + initializers = { + base = initializenostackmath, + node = initializenostackmath, + } +} + +local function initializerealdimensions(tfmdata,value) + tfmdata.properties.realdimensions = value and true +end + +registerotffeature { + name = "realdimensions", + description = "accept negative dimenions", + initializers = { + base = initializerealdimensions, + node = initializerealdimensions, + } +} + +local function initializeitlc(tfmdata,value) -- hm, always value + if value then + -- the magic 40 and it formula come from Dohyun Kim but we might need another guess + local parameters = tfmdata.parameters + local italicangle = parameters.italicangle + if italicangle and italicangle ~= 0 then + local properties = tfmdata.properties + local factor = tonumber(value) or 1 + properties.hasitalics = true + properties.autoitalicamount = factor * (parameters.uwidth or 40)/2 + end + end +end + +local italic_specification = { + name = "itlc", + description = "italic correction", + initializers = { + base = initializeitlc, + node = initializeitlc, + } +} + +registerotffeature(italic_specification) +registerafmfeature(italic_specification) + +local function initializetextitalics(tfmdata,value) -- yes no delay + tfmdata.properties.textitalics = toboolean(value) +end + +local textitalics_specification = { + name = "textitalics", + description = "use alternative text italic correction", + initializers = { + base = initializetextitalics, + node = initializetextitalics, + } +} + +registerotffeature(textitalics_specification) +registerafmfeature(textitalics_specification) + +-- local function initializemathitalics(tfmdata,value) -- yes no delay +-- tfmdata.properties.mathitalics = toboolean(value) +-- end +-- +-- local mathitalics_specification = { +-- name = "mathitalics", +-- description = "use alternative math italic correction", +-- initializers = { +-- base = initializemathitalics, +-- node = initializemathitalics, +-- } +-- } + +-- registerotffeature(mathitalics_specification) +-- registerafmfeature(mathitalics_specification) + +-- slanting + +local function initializeslant(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.parameters.slantfactor = value +end + +local slant_specification = { + name = "slant", + description = "slant glyphs", + initializers = { + base = initializeslant, + node = initializeslant, + } +} + +registerotffeature(slant_specification) +registerafmfeature(slant_specification) + +local function initializeextend(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.parameters.extendfactor = value +end + +local extend_specification = { + name = "extend", + description = "scale glyphs horizontally", + initializers = { + base = initializeextend, + node = initializeextend, + } +} + +registerotffeature(extend_specification) +registerafmfeature(extend_specification) + +-- For Wolfgang Schuster: +-- +-- \definefontfeature[thisway][default][script=hang,language=zhs,dimensions={2,2,2}] +-- \definedfont[file:kozminpr6nregular*thisway] +-- +-- For the moment we don't mess with the descriptions. + +local function manipulatedimensions(tfmdata,key,value) + if type(value) == "string" and value ~= "" then + local characters = tfmdata.characters + local parameters = tfmdata.parameters + local emwidth = parameters.quad + local exheight = parameters.xheight + local newwidth = false + local newheight = false + local newdepth = false + if value == "strut" then + newheight = gettexdimen("strutht") + newdepth = gettexdimen("strutdp") + elseif value == "mono" then + newwidth = emwidth + else + local spec = settings_to_array(value) + newwidth = tonumber(spec[1]) + newheight = tonumber(spec[2]) + newdepth = tonumber(spec[3]) + if newwidth then newwidth = newwidth * emwidth end + if newheight then newheight = newheight * exheight end + if newdepth then newdepth = newdepth * exheight end + end + if newwidth or newheight or newdepth then + local additions = { } + for unicode, old_c in next, characters do + local oldwidth = old_c.width + local oldheight = old_c.height + local olddepth = old_c.depth + local width = newwidth or oldwidth or 0 + local height = newheight or oldheight or 0 + local depth = newdepth or olddepth or 0 + if oldwidth ~= width or oldheight ~= height or olddepth ~= depth then + local private = getprivate(tfmdata) + local newslot = { "slot", 1, private } -- { "slot", 0, private } + local new_c + local commands = oldwidth ~= width and { + { "right", (width - oldwidth) / 2 }, + newslot, + } or { + newslot, + } + if height > 0 then + if depth > 0 then + new_c = { + width = width, + height = height, + depth = depth, + commands = commands, + } + else + new_c = { + width = width, + height = height, + commands = commands, + } + end + else + if depth > 0 then + new_c = { + width = width, + depth = depth, + commands = commands, + } + else + new_c = { + width = width, + commands = commands, + } + end + end + setmetatableindex(new_c,old_c) + characters[unicode] = new_c + additions[private] = old_c + end + end + for k, v in next, additions do + characters[k] = v + end + -- elseif height > 0 and depth > 0 then + -- for unicode, old_c in next, characters do + -- old_c.height = height + -- old_c.depth = depth + -- end + -- elseif height > 0 then + -- for unicode, old_c in next, characters do + -- old_c.height = height + -- end + -- elseif depth > 0 then + -- for unicode, old_c in next, characters do + -- old_c.depth = depth + -- end + end + end +end + +local dimensions_specification = { + name = "dimensions", + description = "force dimensions", + manipulators = { + base = manipulatedimensions, + node = manipulatedimensions, + } +} + +registerotffeature(dimensions_specification) +registerafmfeature(dimensions_specification) + +-------------------------------------------------------------------------------------------------------------- + +-- local function fakemonospace(tfmdata) +-- local resources = tfmdata.resources +-- local gposfeatures = resources.features.gpos +-- local characters = tfmdata.characters +-- local descriptions = tfmdata.descriptions +-- local sequences = resources.sequences +-- local coverage = { } +-- local units = tfmdata.shared.rawdata.metadata.units +-- for k, v in next, characters do +-- local w = descriptions[k].width +-- local d = units - w +-- coverage[k] = { -d/2, 0, units, 0 } +-- end +-- local f = { dflt = { dflt = true } } +-- local s = #sequences + 1 +-- local t = { +-- features = { fakemono = f }, +-- flags = { false, false, false, false }, +-- index = s, +-- name = "p_s_" .. s, +-- nofsteps = 1, +-- order = { "fakemono" }, +-- skiphash = false, +-- type = "gpos_single", +-- steps = { +-- { +-- format = "single", +-- coverage = coverage, +-- } +-- } +-- } +-- gposfeatures["fakemono"] = f +-- sequences[s] = t +-- end +-- +-- fonts.constructors.features.otf.register { +-- name = "fakemono", +-- description = "fake monospaced", +-- initializers = { +-- node = fakemonospace, +-- }, +-- } + +-------------------------------------------------------------------------------------------------------------- + +-- for zhichu chen (see mailing list archive): we might add a few more variants +-- in due time +-- +-- \definefontfeature[boxed][default][boundingbox=yes] % paleblue +-- +-- maybe: +-- +-- \definecolor[DummyColor][s=.75,t=.5,a=1] {\DummyColor test} \nopdfcompression +-- +-- local gray = { "pdf", "origin", "/Tr1 gs .75 g" } +-- local black = { "pdf", "origin", "/Tr0 gs 0 g" } + + +-- boundingbox={yes|background|frame|empty|<color>} + +local push = { "push" } +local pop = { "pop" } + +----- gray = { "pdf", "origin", ".75 g .75 G" } +----- black = { "pdf", "origin", "0 g 0 G" } +----- gray = { "pdf", ".75 g" } +----- black = { "pdf", "0 g" } + +-- local bp = number.dimenfactors.bp +-- +-- local downcache = setmetatableindex(function(t,d) +-- local v = { "down", d } +-- t[d] = v +-- return v +-- end) +-- +-- local backcache = setmetatableindex(function(t,h) +-- local h = h * bp +-- local v = setmetatableindex(function(t,w) +-- -- local v = { "rule", h, w } +-- local v = { "pdf", "origin", formatters["0 0 %.6F %.6F re F"](w*bp,h) } +-- t[w] = v +-- return v +-- end) +-- t[h] = v +-- return v +-- end) +-- +-- local forecache = setmetatableindex(function(t,h) +-- local h = h * bp +-- local v = setmetatableindex(function(t,w) +-- local v = { "pdf", "origin", formatters["%.6F w 0 0 %.6F %.6F re S"](0.25*65536*bp,w*bp,h) } +-- t[w] = v +-- return v +-- end) +-- t[h] = v +-- return v +-- end) + +local bp = number.dimenfactors.bp +local r = 16384 * bp -- 65536 // 4 + +local backcache = setmetatableindex(function(t,h) + local h = h * bp + local v = setmetatableindex(function(t,d) + local d = d * bp + local v = setmetatableindex(function(t,w) + local v = { "pdf", "origin", formatters["%.6F w 0 %.6F %.6F %.6F re f"](r,-d,w*bp,h+d) } + t[w] = v + return v + end) + t[d] = v + return v + end) + t[h] = v + return v +end) + +local forecache = setmetatableindex(function(t,h) + local h = h * bp + local v = setmetatableindex(function(t,d) + local d = d * bp + local v = setmetatableindex(function(t,w) + -- the frame goes through the boundingbox + -- local v = { "pdf", "origin", formatters["[] 0 d 0 J %.6F w %.6F %.6F %.6F re S"](r,-d,w*bp,h+d) } + local v = { "pdf", "origin", formatters["[] 0 d 0 J %.6F w %.6F %.6F %.6F %.6F re S"](r,r/2,-d+r/2,w*bp-r,h+d-r) } + t[w] = v + return v + end) + t[d] = v + return v + end) + t[h] = v + return v +end) + +local startcolor = nil +local stopcolor = nil + +local function showboundingbox(tfmdata,key,value) + if value then + if not backcolors then + local vfspecials = backends.pdf.tables.vfspecials + startcolor = vfspecials.startcolor + stopcolor = vfspecials.stopcolor + end + local characters = tfmdata.characters + local additions = { } + local rulecache = backcache + local showchar = true + local color = "palegray" + if type(value) == "string" then + value = settings_to_array(value) + for i=1,#value do + local v = value[i] + if v == v_frame then + rulecache = forecache + elseif v == v_background then + rulecache = backcache + elseif v == v_empty then + showchar = false + elseif v == v_none then + color = nil + else + color = v + end + end + end + local gray = color and startcolor(color) or nil + local black = gray and stopcolor or nil + for unicode, old_c in next, characters do + local private = getprivate(tfmdata) + local width = old_c.width or 0 + local height = old_c.height or 0 + local depth = old_c.depth or 0 + local char = showchar and { "slot", 1, private } or nil -- { "slot", 0, private } + -- local new_c + -- if depth == 0 then + -- new_c = { + -- width = width, + -- height = height, + -- commands = { + -- push, + -- gray, + -- rulecache[height][width], + -- black, + -- pop, + -- char, + -- } + -- } + -- else + -- new_c = { + -- width = width, + -- height = height, + -- depth = depth, + -- commands = { + -- push, + -- downcache[depth], + -- gray, + -- rulecache[height+depth][width], + -- black, + -- pop, + -- char, + -- } + -- } + -- end + local rule = rulecache[height][depth][width] + local new_c = { + width = width, + height = height, + depth = depth, + commands = gray and { + -- push, + gray, + rule, + black, + -- pop, + char, + } or { + rule, + char, + } + } + setmetatableindex(new_c,old_c) + characters[unicode] = new_c + additions[private] = old_c + end + for k, v in next, additions do + characters[k] = v + end + end +end + +registerotffeature { + name = "boundingbox", + description = "show boundingbox", + manipulators = { + base = showboundingbox, + node = showboundingbox, + } +} + +-- -- for notosans but not general +-- +-- do +-- +-- local v_local = interfaces and interfaces.variables and interfaces.variables["local"] or "local" +-- +-- local utfbyte = utf.byte +-- +-- local function initialize(tfmdata,key,value) +-- local characters = tfmdata.characters +-- local parameters = tfmdata.parameters +-- local oldchar = 32 +-- local newchar = 32 +-- if value == "locl" or value == v_local then +-- newchar = fonts.handlers.otf.getsubstitution(tfmdata,oldchar,"locl",true) or oldchar +-- elseif value == true then +-- -- use normal space +-- elseif value then +-- newchar = utfbyte(value) +-- else +-- return +-- end +-- local newchar = newchar and characters[newchar] +-- local newspace = newchar and newchar.width +-- if newspace > 0 then +-- parameters.space = newspace +-- parameters.space_stretch = newspace/2 +-- parameters.space_shrink = newspace/3 +-- parameters.extra_space = parameters.space_shrink +-- end +-- end +-- +-- registerotffeature { +-- name = 'space', -- true|false|locl|character +-- description = 'space settings', +-- manipulators = { +-- base = initialize, +-- node = initialize, +-- } +-- } +-- +-- end + +do + + local P, lpegpatterns, lpegmatch = lpeg.P, lpeg.patterns, lpeg.match + + local amount, stretch, shrink, extra + + local factor = lpegpatterns.unsigned + local space = lpegpatterns.space + local pattern = ( + (factor / function(n) amount = tonumber(n) or amount end) + + (P("+") + P("plus" )) * space^0 * (factor / function(n) stretch = tonumber(n) or stretch end) + + (P("-") + P("minus")) * space^0 * (factor / function(n) shrink = tonumber(n) or shrink end) + + ( P("extra")) * space^0 * (factor / function(n) extra = tonumber(n) or extra end) + + space^1 + )^1 + + local function initialize(tfmdata,key,value) + local characters = tfmdata.characters + local parameters = tfmdata.parameters + if type(value) == "string" then + local emwidth = parameters.quad + amount, stretch, shrink, extra = 0, 0, 0, false + lpegmatch(pattern,value) + if not extra then + if shrink ~= 0 then + extra = shrink + elseif stretch ~= 0 then + extra = stretch + else + extra = amount + end + end + parameters.space = amount * emwidth + parameters.space_stretch = stretch * emwidth + parameters.space_shrink = shrink * emwidth + parameters.extra_space = extra * emwidth + end + end + + -- 1.1 + 1.2 - 1.3 minus 1.4 plus 1.1 extra 1.4 -- last one wins + + registerotffeature { + name = "spacing", + description = "space settings", + manipulators = { + base = initialize, + node = initialize, + } + } + +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, +-- } +-- } + +-- not to be used! experimental code, only needed when testing + +local is_letter = characters.is_letter +local always = true + +local function collapseitalics(tfmdata,key,value) + local threshold = value == true and 100 or tonumber(value) + if threshold and threshold > 0 then + if threshold > 100 then + threshold = 100 + end + for unicode, data in next, tfmdata.characters do + if always or is_letter[unicode] or is_letter[data.unicode] then + local italic = data.italic + if italic and italic ~= 0 then + local width = data.width + if width and width ~= 0 then + local delta = threshold * italic / 100 + data.width = width + delta + data.italic = italic - delta + end + end + end + end + end +end + +local dimensions_specification = { + name = "collapseitalics", + description = "collapse italics", + manipulators = { + base = collapseitalics, + node = collapseitalics, + } +} + +registerotffeature(dimensions_specification) +registerafmfeature(dimensions_specification) + +-- a handy helper (might change or be moved to another namespace) + +local nodepool = nodes.pool +local new_glyph = nodepool.glyph + +local helpers = fonts.helpers +local currentfont = font.current + +local currentprivate = 0xE000 +local maximumprivate = 0xEFFF + +-- if we run out of space we can think of another range but by sharing we can +-- use these privates for mechanisms like alignments-on-character and such + +local sharedprivates = setmetatableindex(function(t,k) + v = currentprivate + if currentprivate < maximumprivate then + currentprivate = currentprivate + 1 + else + -- reuse last slot, todo: warning + end + t[k] = v + return v +end) + +function helpers.addprivate(tfmdata,name,characterdata) + local properties = tfmdata.properties + local characters = tfmdata.characters + local privates = properties.privates + if not privates then + privates = { } + properties.privates = privates + end + if not name then + name = formatters["anonymous_private_0x%05X"](currentprivate) + end + local usedprivate = sharedprivates[name] + privates[name] = usedprivate + characters[usedprivate] = characterdata + return usedprivate +end + +local function getprivateslot(id,name) + if not name then + name = id + id = currentfont() + end + local properties = fontproperties[id] + local privates = properties and properties.privates + return privates and privates[name] +end + +local function getprivatenode(tfmdata,name) + if type(tfmdata) == "number" then + tfmdata = fontdata[tfmdata] + end + local properties = tfmdata.properties + local font = properties.id + local slot = getprivateslot(font,name) + if slot then + -- todo: set current attribibutes + local char = tfmdata.characters[slot] + local tonode = char.tonode + if tonode then + return tonode(font,char) + else + return new_glyph(font,slot) + end + end +end + +local function getprivatecharornode(tfmdata,name) + if type(tfmdata) == "number" then + tfmdata = fontdata[tfmdata] + end + local properties = tfmdata.properties + local font = properties.id + local slot = getprivateslot(font,name) + if slot then + -- todo: set current attributes + local char = tfmdata.characters[slot] + local tonode = char.tonode + if tonode then + return "node", tonode(tfmdata,char) + else + return "char", slot + end + end +end + +helpers.getprivateslot = getprivateslot +helpers.getprivatenode = getprivatenode +helpers.getprivatecharornode = getprivatecharornode + +function helpers.getprivates(tfmdata) + if type(tfmdata) == "number" then + tfmdata = fontdata[tfmdata] + end + local properties = tfmdata.properties + return properties and properties.privates +end + +function helpers.hasprivate(tfmdata,name) + if type(tfmdata) == "number" then + tfmdata = fontdata[tfmdata] + end + local properties = tfmdata.properties + local privates = properties and properties.privates + return privates and privates[name] or false +end + +-- relatively new: + +do + + local extraprivates = { } + + function fonts.helpers.addextraprivate(name,f) + extraprivates[#extraprivates+1] = { name, f } + end + + local function addextraprivates(tfmdata) + for i=1,#extraprivates do + local e = extraprivates[i] + local c = e[2](tfmdata) + if c then + fonts.helpers.addprivate(tfmdata, e[1], c) + end + end + end + + constructors.newfeatures.otf.register { + name = "extraprivates", + description = "extra privates", + default = true, + manipulators = { + base = addextraprivates, + node = addextraprivates, + } + } + +end + +implement { + name = "getprivatechar", + arguments = "string", + actions = function(name) + local p = getprivateslot(name) + if p then + context(utfchar(p)) + end + end +} + +implement { + name = "getprivatemathchar", + arguments = "string", + actions = function(name) + local p = getprivateslot(family_font(0),name) + if p then + context(utfchar(p)) + end + end +} + +implement { + name = "getprivateslot", + arguments = "string", + actions = function(name) + local p = getprivateslot(name) + if p then + context(p) + end + end +} + +-- requested for latex but not supported unless really needed in context: +-- +-- registerotffeature { +-- name = "ignoremathconstants", +-- description = "ignore math constants table", +-- initializers = { +-- base = function(tfmdata,value) +-- if value then +-- tfmdata.mathparameters = nil +-- end +-- end +-- } +-- } + +-- tfmdata.properties.mathnolimitsmode = tonumber(value) or 0 + +do + + local splitter = lpeg.splitat(",",tonumber) + local lpegmatch = lpeg.match + + local function initialize(tfmdata,value) + local mathparameters = tfmdata.mathparameters + if mathparameters then + local sup, sub + if type(value) == "string" then + sup, sub = lpegmatch(splitter,value) + if not sup then + sub, sup = 0, 0 + elseif not sub then + sub, sup = sup, 0 + end + elseif type(value) == "number" then + sup, sub = 0, value + end + mathparameters.NoLimitSupFactor = sup + mathparameters.NoLimitSubFactor = sub + end + end + + registerotffeature { + name = "mathnolimitsmode", + description = "influence nolimits placement", + initializers = { + base = initialize, + node = initialize, + } + } + +end + +do + + local function initialize(tfmdata,value) + local properties = tfmdata.properties + if properties then + properties.identity = value == "vertical" and "vertical" or "horizontal" + end + end + + registerotffeature { + name = "identity", + description = "set font identity", + initializers = { + base = initialize, + node = initialize, + } + } + + local function initialize(tfmdata,value) + local properties = tfmdata.properties + if properties then + properties.writingmode = value == "vertical" and "vertical" or "horizontal" + end + end + + registerotffeature { + name = "writingmode", + description = "set font direction", + initializers = { + base = initialize, + node = initialize, + } + } + +end + +do -- another hack for a crappy font + + local function additalictowidth(tfmdata,key,value) + local characters = tfmdata.characters + local additions = { } + for unicode, old_c in next, characters do + -- maybe check for math + local oldwidth = old_c.width + local olditalic = old_c.italic + if olditalic and olditalic ~= 0 then + local private = getprivate(tfmdata) + local new_c = { + width = oldwidth + olditalic, + height = old_c.height, + depth = old_c.depth, + commands = { + -- { "slot", 1, private }, + -- { "slot", 0, private }, + { "char", private }, + { "right", olditalic }, + }, + } + setmetatableindex(new_c,old_c) + characters[unicode] = new_c + additions[private] = old_c + end + end + for k, v in next, additions do + characters[k] = v + end + end + + registerotffeature { + name = "italicwidths", + description = "add italic to width", + manipulators = { + base = additalictowidth, + -- node = additalictowidth, -- only makes sense for math + } + } + +end + +do + + local tounicode = fonts.mappings.tounicode + + local function check(tfmdata,key,value) + if value == "ligatures" then + local private = fonts.constructors and fonts.constructors.privateoffset or 0xF0000 + local collected = fonts.handlers.otf.readers.getcomponents(tfmdata.shared.rawdata) + if collected and next(collected)then + for unicode, char in next, tfmdata.characters do + if true then -- if unicode >= private or (unicode >= 0xE000 and unicode <= 0xF8FF) then + local u = collected[unicode] + if u then + local n = #u + for i=1,n do + if u[i] > private then + n = 0 + break + end + end + if n > 0 then + if n == 1 then + u = u[1] + end + char.unicode = u + char.tounicode = tounicode(u) + end + end + end + end + end + end + end + + -- forceunicodes=ligatures : aggressive lig resolving (e.g. for emoji) + -- + -- kind of like: \enabletrackers[fonts.mapping.forceligatures] + + registerotffeature { + name = "forceunicodes", + description = "forceunicodes", + manipulators = { + base = check, + node = check, + } + } + +end + +do + + -- This is a rather special test-only feature that I added for the sake of testing + -- Idris's husayni. We wanted to know if uniscribe obeys the order of lookups in a + -- font, in spite of what the description of handling arabic suggests. And indeed, + -- mixed-in lookups of other features (like all these ss* in husayni) are handled + -- the same in context as in uniscribe. If one sets reorderlookups=arab then we sort + -- according to the "assumed" order so e.g. the ss* move to after the standard + -- features. The observed difference in rendering is an indication that uniscribe is + -- quite faithful to the font (while e.g. tests with the hb plugin demonstrate some + -- interference, apart from some hard coded init etc expectations). Anyway, it means + -- that we're okay with the (generic) node processor. A pitfall is that in context + -- we can actually control more, so we can trigger an analyze pass with e.g. + -- dflt/dflt while the libraries depend on the script settings for that. Uniscribe + -- probably also parses the string and when seeing arabic will follow a different + -- code path, although it seems to treat all features equal. + + local trace_reorder = trackers.register("fonts.reorderlookups",function(v) trace_reorder = v end) + local report_reorder = logs.reporter("fonts","reorder") + + local vectors = { } + + vectors.arab = { + gsub = { + ccmp = 1, + isol = 2, + fina = 3, + medi = 4, + init = 5, + rlig = 6, + rclt = 7, + calt = 8, + liga = 9, + dlig = 10, + cswh = 11, + mset = 12, + }, + gpos = { + curs = 1, + kern = 2, + mark = 3, + mkmk = 4, + }, + } + + function otf.reorderlookups(tfmdata,vector) + local order = vectors[vector] + if not order then + return + end + local oldsequences = tfmdata.resources.sequences + if oldsequences then + local sequences = { } + for i=1,#oldsequences do + sequences[i] = oldsequences[i] + end + for i=1,#sequences do + local s = sequences[i] + local features = s.features + local kind = s.type + local index = s.index + if features then + local when + local what + for feature in sortedhash(features) do + if not what then + what = find(kind,"^gsub") and "gsub" or "gpos" + end + local newwhen = order[what][feature] + if not newwhen then + -- skip + elseif not when then + when = newwhen + elseif newwhen < when then + when = newwhen + end + end + s.ondex = s.index + s.index = i + s.what = what == "gsub" and 1 or 2 + s.when = when or 99 + else + s.ondex = s.index + s.index = i + s.what = 1 + s.when = 99 + end + end + sort(sequences,function(a,b) + local what_a = a.what + local what_b = b.what + if what_a ~= what_b then + return a.index < b.index + end + local when_a = a.when + local when_b = b.when + if when_a == when_b then + return a.index < b.index + else + return when_a < when_b + end + end) + local swapped = 0 + for i=1,#sequences do + local sequence = sequences[i] + local features = sequence.features + if features then + local index = sequence.index + if index ~= i then + swapped = swapped + 1 + end + if trace_reorder then + if swapped == 1 then + report_reorder() + report_reorder("start swapping lookups in font %!font:name!",tfmdata) + report_reorder() + report_reorder("gsub order: % t",table.swapped(order.gsub)) + report_reorder("gpos order: % t",table.swapped(order.gpos)) + report_reorder() + end + report_reorder("%03i : lookup %03i, type %s, sorted %2i, moved %s, % t", + i,index,sequence.what == 1 and "gsub" or "gpos",sequence.when or 99, + (index > i and "-") or (index < i and "+") or "=",sortedkeys(features)) + end + end + sequence.what = nil + sequence.when = nil + sequence.index = sequence.ondex + end + if swapped > 0 then + if trace_reorder then + report_reorder() + report_reorder("stop swapping lookups, %i lookups swapped",swapped) + report_reorder() + end +-- tfmdata.resources.sequences = sequences + tfmdata.shared.reorderedsequences = sequences + end + end + end + + -- maybe delay till ra is filled + + local function reorderlookups(tfmdata,key,value) + if value then + otf.reorderlookups(tfmdata,value) + end + end + + registerotffeature { + name = "reorderlookups", + description = "reorder lookups", + manipulators = { + base = reorderlookups, + node = reorderlookups, + } + } + +end + +-- maybe useful + +local function initializeoutline(tfmdata,value) + value = tonumber(value) + if not value then + value = 0 + else + value = tonumber(value) or 0 + end + if value then + value = value * 1000 + end + tfmdata.parameters.mode = 1 + tfmdata.parameters.width = value +end + +local outline_specification = { + name = "outline", + description = "outline glyphs", + initializers = { + base = initializeoutline, + node = initializeoutline, + } +} + +registerotffeature(outline_specification) +registerafmfeature(outline_specification) + +-- definitely ugly + +local report_effect = logs.reporter("fonts","effect") +local trace_effect = false + +trackers.register("fonts.effect", function(v) trace_effect = v end) + +local effects = { + inner = 0, + normal = 0, + outer = 1, + outline = 1, + both = 2, + hidden = 3, +} + +local function initializeeffect(tfmdata,value) + local spec + if type(value) == "number" then + spec = { width = value } + else + spec = settings_to_hash(value) + end + local effect = spec.effect or "both" + local width = tonumber(spec.width) or 0 + local mode = effects[effect] + if not mode then + report_effect("invalid effect %a",effect) + elseif width == 0 and mode == 0 then + report_effect("invalid width %a for effect %a",width,effect) + else + local parameters = tfmdata.parameters + local properties = tfmdata.properties + parameters.mode = mode + parameters.width = width * 1000 + local factor = tonumber(spec.factor) or 0 + local hfactor = tonumber(spec.vfactor) or factor + local vfactor = tonumber(spec.hfactor) or factor + local delta = tonumber(spec.delta) or 1 + local wdelta = tonumber(spec.wdelta) or delta + local hdelta = tonumber(spec.hdelta) or delta + local ddelta = tonumber(spec.ddelta) or hdelta + properties.effect = { + effect = effect, + width = width, + factor = factor, + hfactor = hfactor, + vfactor = vfactor, + wdelta = wdelta, + hdelta = hdelta, + ddelta = ddelta, + } + end +end + +local function manipulateeffect(tfmdata) + local effect = tfmdata.properties.effect + if effect then + local characters = tfmdata.characters + local parameters = tfmdata.parameters + local multiplier = effect.width * 100 + local wdelta = effect.wdelta * parameters.hfactor * multiplier + local hdelta = effect.hdelta * parameters.vfactor * multiplier + local ddelta = effect.ddelta * parameters.vfactor * multiplier + local hshift = wdelta / 2 + local factor = (1 + effect.factor) * parameters.factor + local hfactor = (1 + effect.hfactor) * parameters.hfactor + local vfactor = (1 + effect.vfactor) * parameters.vfactor + for unicode, old_c in next, characters do + local oldwidth = old_c.width + local oldheight = old_c.height + local olddepth = old_c.depth + if oldwidth and oldwidth > 0 then + old_c.width = oldwidth + wdelta + old_c.commands = { + { "right", hshift }, + { "char", unicode }, + } + end + if oldheight and oldheight > 0 then + old_c.height = oldheight + hdelta + end + if olddepth and olddepth > 0 then + old_c.depth = olddepth + ddelta + end + end + parameters.factor = factor + parameters.hfactor = hfactor + parameters.vfactor = vfactor + if trace_effect then + report_effect("applying effect") + report_effect(" effect : %s", effect.effect) + report_effect(" width : %s => %s", effect.width, multiplier) + report_effect(" factor : %s => %s", effect.factor, factor ) + report_effect(" hfactor : %s => %s", effect.hfactor,hfactor) + report_effect(" vfactor : %s => %s", effect.vfactor,vfactor) + report_effect(" wdelta : %s => %s", effect.wdelta, wdelta) + report_effect(" hdelta : %s => %s", effect.hdelta, hdelta) + report_effect(" ddelta : %s => %s", effect.ddelta, ddelta) + end + end +end + +local effect_specification = { + name = "effect", + description = "apply effects to glyphs", + initializers = { + base = initializeeffect, + node = initializeeffect, + }, + manipulators = { + base = manipulateeffect, + node = manipulateeffect, + }, +} + +registerotffeature(effect_specification) +registerafmfeature(effect_specification) |