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
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
32
33
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
211
212local function setmathparameters(tfmdata,characters,mathparameters,dx,dy,squeeze,multiplier)
213
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
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
272
273
274
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
310 hshift,
311 vshift
312 )
313 else
314 character.commands = {
315
316 hshift,
317 vshift,
318 charcommand[unicode]
319 }
320 end
321 else
322 if commands then
323 prependcommands ( commands,
324
325 hshift
326 )
327 else
328 character.commands = {
329
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 |