font-imp-effects.lua /size: 12 Kb    last modification: 2021-10-28 13:50
1if not modules then modules = { } end modules ['font-imp-effects'] = {
2    version   = 1.001,
3    comment   = "companion to font-ini.mkiv",
4    author    = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
5    copyright = "PRAGMA ADE / ConTeXt Development Team",
6    license   = "see context related readme files"
7}
8
9-- todo: pickup from goodies: if type(effect) then ...
10
11local next, type, tonumber = next, type, tonumber
12local is_boolean = string.is_boolean
13
14local fonts              = fonts
15
16local handlers           = fonts.handlers
17local registerotffeature = handlers.otf.features.register
18local registerafmfeature = handlers.afm.features.register
19
20local settings_to_hash   = utilities.parsers.settings_to_hash_colon_too
21
22local helpers            = fonts.helpers
23local prependcommands    = helpers.prependcommands
24local charcommand        = helpers.commands.char
25local leftcommand        = helpers.commands.left
26local rightcommand       = helpers.commands.right
27local upcommand          = helpers.commands.up
28local downcommand        = helpers.commands.down
29local dummycommand       = helpers.commands.dummy
30
31----- constructors       = fonts.constructors
32----- getmathparameter   = constructors.getmathparameter
33----- setmathparameter   = constructors.setmathparameter
34
35local report_effect      = logs.reporter("fonts","effect")
36local report_slant       = logs.reporter("fonts","slant")
37local report_extend      = logs.reporter("fonts","extend")
38local report_squeeze     = logs.reporter("fonts","squeeze")
39
40local trace              = false
41
42trackers.register("fonts.effect", function(v) trace = v end)
43trackers.register("fonts.slant",  function(v) trace = v end)
44trackers.register("fonts.extend", function(v) trace = v end)
45trackers.register("fonts.squeeze",function(v) trace = v end)
46
47local function initializeslant(tfmdata,value)
48    value = tonumber(value)
49    if not value then
50        value =  0
51    elseif value >  1 then
52        value =  1
53    elseif value < -1 then
54        value = -1
55    end
56    if trace then
57        report_slant("applying %0.3f",value)
58    end
59    tfmdata.parameters.slantfactor = value
60end
61
62local specification = {
63    name        = "slant",
64    description = "slant glyphs",
65    initializers = {
66        base = initializeslant,
67        node = initializeslant,
68    }
69}
70
71registerotffeature(specification)
72registerafmfeature(specification)
73
74local function initializeextend(tfmdata,value)
75    value = tonumber(value)
76    if not value then
77        value =  0
78    elseif value >  10 then
79        value =  10
80    elseif value < -10 then
81        value = -10
82    end
83    if trace then
84        report_extend("applying %0.3f",value)
85    end
86    tfmdata.parameters.extendfactor = value
87end
88
89local specification = {
90    name        = "extend",
91    description = "scale glyphs horizontally",
92    initializers = {
93        base = initializeextend,
94        node = initializeextend,
95    }
96}
97
98registerotffeature(specification)
99registerafmfeature(specification)
100
101local function initializesqueeze(tfmdata,value)
102    value = tonumber(value)
103    if not value then
104        value =  0
105    elseif value >  10 then
106        value =  10
107    elseif value < -10 then
108        value = -10
109    end
110    if trace then
111        report_squeeze("applying %0.3f",value)
112    end
113    tfmdata.parameters.squeezefactor = value
114end
115
116local specification = {
117    name        = "squeeze",
118    description = "scale glyphs vertically",
119    initializers = {
120        base = initializesqueeze,
121        node = initializesqueeze,
122    }
123}
124
125registerotffeature(specification)
126registerafmfeature(specification)
127
128local effects = {
129    inner   = 0,
130    normal  = 0,
131    outer   = 1,
132    outline = 1,
133    both    = 2,
134    hidden  = 3,
135}
136
137local function initializeeffect(tfmdata,value)
138    local spec
139    if type(value) == "number" then
140        spec = { width = value }
141    else
142        spec = settings_to_hash(value)
143    end
144    local effect = spec.effect or "both"
145    local width  = tonumber(spec.width) or 0
146    local mode   = effects[effect]
147    if not mode then
148        report_effect("invalid effect %a",effect)
149    elseif width == 0 and mode == 0 then
150        report_effect("invalid width %a for effect %a",width,effect)
151    else
152        local parameters = tfmdata.parameters
153        local properties = tfmdata.properties
154        parameters.mode  = mode
155        parameters.width = width * 1000
156        if is_boolean(spec.auto) == true then
157            local squeeze = 1 - width/20
158            local average = (1 - squeeze) * width * 100
159            spec.squeeze  = squeeze
160            spec.extend   = 1 + width/2
161            spec.wdelta   = average
162            spec.hdelta   = average/2
163            spec.ddelta   = average/2
164            spec.vshift   = average/2
165        end
166        local factor  = tonumber(spec.factor)  or 0
167        local hfactor = tonumber(spec.hfactor) or factor
168        local vfactor = tonumber(spec.vfactor) or factor
169        local delta   = tonumber(spec.delta)   or 1
170        local wdelta  = tonumber(spec.wdelta)  or delta
171        local hdelta  = tonumber(spec.hdelta)  or delta
172        local ddelta  = tonumber(spec.ddelta)  or hdelta
173        local vshift  = tonumber(spec.vshift)  or 0
174        local slant   = spec.slant
175        local extend  = spec.extend
176        local squeeze = spec.squeeze
177        if slant then
178            initializeslant(tfmdata,slant)
179        end
180        if extend then
181            initializeextend(tfmdata,extend)
182        end
183        if squeeze then
184            initializesqueeze(tfmdata,squeeze)
185        end
186        properties.effect = {
187            effect  = effect,
188            width   = width,
189            factor  = factor,
190            hfactor = hfactor,
191            vfactor = vfactor,
192            wdelta  = wdelta,
193            hdelta  = hdelta,
194            ddelta  = ddelta,
195            vshift  = vshift,
196            slant   = tfmdata.parameters.slantfactor,
197            extend  = tfmdata.parameters.extendfactor,
198            squeeze = tfmdata.parameters.squeezefactor,
199        }
200    end
201end
202
203local rules = {
204    "RadicalRuleThickness",
205    "OverbarRuleThickness",
206    "FractionRuleThickness",
207    "UnderbarRuleThickness",
208}
209
210-- radicals are not yet ok
211
212local function setmathparameters(tfmdata,characters,mathparameters,dx,dy,squeeze,multiplier)
213    -- hm, this was "if delta ~= 0 then" but delta was gone
214    if dy ~= 0 then
215        for i=1,#rules do
216            local name  = rules[i]
217            local value = mathparameters[name]
218            if value then
219               mathparameters[name] = (squeeze or 1) * (value + dy)
220            end
221        end
222    end
223end
224
225local function setmathcharacters(tfmdata,characters,mathparameters,dx,dy,squeeze,wdelta,hdelta,ddelta)
226
227    -- still not the perfect rule
228
229    local function wdpatch(char)
230        if wsnap ~= 0 then
231            char.width  = char.width + wdelta/2
232        end
233    end
234
235    local function htpatch(char)
236        if hsnap ~= 0 then
237            local height = char.height
238            if height then
239                char.height = char.height + 2 * dy
240            end
241        end
242    end
243
244    local character = characters[0x221A]
245
246    if character and character.next then
247        local char = character
248        local next = character.next
249        wdpatch(char)
250        htpatch(char)
251        while next do
252            char = characters[next]
253            wdpatch(char)
254            htpatch(char)
255            next = char.next
256        end
257        if char then
258            local v = char.vert_variants
259            if v then
260                local top = v[#v]
261                if top then
262                    local char = characters[top.glyph]
263                    htpatch(char)
264                end
265            end
266        end
267    end
268
269end
270
271-- local show_effect = { "lua", function(f,c)
272--     report_effect("font id %i, char %C",f,c)
273--     inspect(fonts.hashes.characters[f][c])
274-- end }
275
276local function manipulateeffect(tfmdata)
277    local effect = tfmdata.properties.effect
278    if effect then
279        local characters     = tfmdata.characters
280        local parameters     = tfmdata.parameters
281        local mathparameters = tfmdata.mathparameters
282        local multiplier     = effect.width * 100
283        local factor         = parameters.factor
284        local hfactor        = parameters.hfactor
285        local vfactor        = parameters.vfactor
286        local wdelta         = effect.wdelta * hfactor * multiplier
287        local hdelta         = effect.hdelta * vfactor * multiplier
288        local ddelta         = effect.ddelta * vfactor * multiplier
289        local vshift         = effect.vshift * vfactor * multiplier
290        local squeeze        = effect.squeeze
291        local hshift         = wdelta / 2
292        local dx             = multiplier * vfactor
293        local dy             = vshift
294        local factor         = (1 + effect.factor)  * factor
295        local hfactor        = (1 + effect.hfactor) * hfactor
296        local vfactor        = (1 + effect.vfactor) * vfactor
297        vshift = vshift ~= 0 and upcommand[vshift] or false
298        hshift = rightcommand[hshift]
299        for unicode, character in next, characters do
300            local oldwidth  = character.width
301            local oldheight = character.height
302            local olddepth  = character.depth
303            if oldwidth and oldwidth > 0 then
304                character.width = oldwidth + wdelta
305                local commands = character.commands
306                if vshift then
307                    if commands then
308                        prependcommands ( commands,
309                         -- show_effect,
310                            hshift,
311                            vshift
312                        )
313                    else
314                        character.commands = {
315                         -- show_effect,
316                            hshift,
317                            vshift,
318                            charcommand[unicode]
319                        }
320                    end
321                else
322                    if commands then
323                      prependcommands ( commands,
324                       -- show_effect,
325                          hshift
326                      )
327                    else
328                        character.commands = {
329                         -- show_effect,
330                            hshift,
331                            charcommand[unicode]
332                      }
333                    end
334                end
335            end
336            if oldheight and oldheight > 0 then
337               character.height = oldheight + hdelta
338            end
339            if olddepth and olddepth > 0 then
340               character.depth = olddepth + ddelta
341            end
342        end
343        if mathparameters then
344            setmathparameters(tfmdata,characters,mathparameters,dx,dy,squeeze,multiplier)
345            setmathcharacters(tfmdata,characters,mathparameters,dx,dy,squeeze,wdelta,hdelta,ddelta)
346        end
347        parameters.factor  = factor
348        parameters.hfactor = hfactor
349        parameters.vfactor = vfactor
350        if trace then
351            report_effect("applying")
352            report_effect("  effect  : %s", effect.effect)
353            report_effect("  width   : %s => %s", effect.width,  multiplier)
354            report_effect("  factor  : %s => %s", effect.factor, factor )
355            report_effect("  hfactor : %s => %s", effect.hfactor,hfactor)
356            report_effect("  vfactor : %s => %s", effect.vfactor,vfactor)
357            report_effect("  wdelta  : %s => %s", effect.wdelta, wdelta)
358            report_effect("  hdelta  : %s => %s", effect.hdelta, hdelta)
359            report_effect("  ddelta  : %s => %s", effect.ddelta, ddelta)
360        end
361    end
362end
363
364local specification = {
365    name        = "effect",
366    description = "apply effects to glyphs",
367    initializers = {
368        base = initializeeffect,
369        node = initializeeffect,
370    },
371    manipulators = {
372        base = manipulateeffect,
373        node = manipulateeffect,
374    },
375}
376
377registerotffeature(specification)
378registerafmfeature(specification)
379
380local function initializeoutline(tfmdata,value)
381    value = tonumber(value)
382    if not value then
383        value = 0
384    else
385        value = tonumber(value) or 0
386    end
387    local parameters = tfmdata.parameters
388    local properties = tfmdata.properties
389    parameters.mode  = effects.outline
390    parameters.width = value * 1000
391    properties.effect = {
392        effect = effect,
393        width  = width,
394    }
395end
396
397local specification = {
398    name        = "outline",
399    description = "outline glyphs",
400    initializers = {
401        base = initializeoutline,
402        node = initializeoutline,
403    }
404}
405
406registerotffeature(specification)
407registerafmfeature(specification)
408