1if not modules then modules = { } end modules ['node-fnt'] = {
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
9if not context then os.exit() end
10
11local next, type = next, type
12local concat, keys = table.concat, table.keys
13
14local nodes, node, fonts = nodes, node, fonts
15
16local trace_characters = false trackers.register("nodes.characters", function(v) trace_characters = v end)
17local trace_fontrun = false trackers.register("nodes.fontrun", function(v) trace_fontrun = v end)
18local trace_variants = false trackers.register("nodes.variants", function(v) trace_variants = v end)
19
20
21
22local force_discrun = true directives.register("nodes.discrun", function(v) force_discrun = v end)
23local force_boundaryrun = true directives.register("nodes.boundaryrun", function(v) force_boundaryrun = v end)
24local force_basepass = true directives.register("nodes.basepass", function(v) force_basepass = v end)
25local keep_redundant = false directives.register("nodes.keepredundant",function(v) keep_redundant = v end)
26
27local report_fonts = logs.reporter("fonts","processing")
28
29local fonthashes = fonts.hashes
30local fontdata = fonthashes.identifiers
31local fontvariants = fonthashes.variants
32local fontmodes = fonthashes.modes
33
34local otf = fonts.handlers.otf
35
36local starttiming = statistics.starttiming
37local stoptiming = statistics.stoptiming
38
39local nodecodes = nodes.nodecodes
40local boundarycodes = nodes.boundarycodes
41
42local handlers = nodes.handlers
43
44local nuts = nodes.nuts
45
46local getid = nuts.getid
47local getsubtype = nuts.getsubtype
48local getreplace = nuts.getreplace
49local getnext = nuts.getnext
50local getprev = nuts.getprev
51local getboth = nuts.getboth
52local getdata = nuts.getdata
53local getglyphdata = nuts.getglyphdata
54
55local setchar = nuts.setchar
56local setlink = nuts.setlink
57local setnext = nuts.setnext
58local setprev = nuts.setprev
59
60local isglyph = nuts.isglyph
61local ischar = nuts.ischar
62
63local nextboundary = nuts.traversers.boundary
64local nextdisc = nuts.traversers.disc
65local nextchar = nuts.traversers.char
66
67local flushnode = nuts.flush
68
69local disc_code = nodecodes.disc
70local boundary_code = nodecodes.boundary
71
72local wordboundary_code = boundarycodes.word
73
74local protectglyphs = nuts.protectglyphs
75local unprotectglyphs = nuts.unprotectglyphs
76
77local setmetatableindex = table.setmetatableindex
78
79
80
81
82
83
84local run = 0
85
86local setfontdynamics = { }
87local fontprocesses = { }
88
89
90
91
92
93
94
95
96
97setmetatableindex(setfontdynamics, function(t,font)
98 local tfmdata = fontdata[font]
99 local shared = tfmdata.shared
100 local f = shared and shared.dynamics and otf.setdynamics or false
101 if f then
102 local v = { }
103 t[font] = v
104 setmetatableindex(v,function(t,k)
105 local v = f(font,k)
106 t[k] = v
107 return v
108 end)
109 return v
110 else
111 t[font] = false
112 return false
113 end
114end)
115
116setmetatableindex(fontprocesses, function(t,font)
117 local tfmdata = fontdata[font]
118 local shared = tfmdata.shared
119 local processes = shared and shared.processes
120 if processes and #processes > 0 then
121 t[font] = processes
122 return processes
123 else
124 t[font] = false
125 return false
126 end
127end)
128
129fonts.hashes.setdynamics = setfontdynamics
130fonts.hashes.processes = fontprocesses
131
132
133
134
135
136
137
138
139
140
141
142
143
144local ligaturing = nuts.ligaturing
145local kerning = nuts.kerning
146
147local function start_trace(head)
148 run = run + 1
149 report_fonts()
150 report_fonts("checking node list, run %s",run)
151 report_fonts()
152 local n = head
153 while n do
154 local char, id = isglyph(n)
155 if char then
156 local font = id
157 local attr = getglyphdata(n) or 0
158 report_fonts("font %03i, dynamic %03i, glyph %C",font,attr,char)
159 elseif id == disc_code then
160 report_fonts("[disc] %s",nodes.listtoutf(n,true,false,n))
161 elseif id == boundary_code then
162 report_fonts("[boundary] %i:%i",getsubtype(n),getdata(n))
163 else
164 report_fonts("[%s]",nodecodes[id])
165 end
166 n = getnext(n)
167 end
168end
169
170local function stop_trace(u,usedfonts,a,attrfonts,b,basefonts,r,redundant)
171 report_fonts()
172 report_fonts("statics : %s",u > 0 and concat(keys(usedfonts)," ") or "none")
173 report_fonts("dynamics: %s",a > 0 and concat(keys(attrfonts)," ") or "none")
174 report_fonts("built-in: %s",b > 0 and b or "none")
175 report_fonts("removed : %s",r > 0 and r or "none")
176 report_fonts()
177end
178
179do
180
181 local usedfonts
182 local attrfonts
183 local basefonts
184 local basefont
185 local prevfont
186 local prevattr
187 local variants
188 local redundant
189 local firstnone
190 local lastfont
191 local lastproc
192 local lastnone
193
194 local a, u, b, r
195
196 local function protectnone()
197 protectglyphs(firstnone,lastnone)
198 firstnone = nil
199 end
200
201 local function setnone(n)
202 if firstnone then
203 protectnone()
204 end
205 if basefont then
206 basefont[2] = getprev(n)
207 basefont = false
208 end
209 if not firstnone then
210 firstnone = n
211 end
212 lastnone = n
213 end
214
215 local function setbase(n)
216 if firstnone then
217 protectnone()
218 end
219 if force_basepass then
220 if basefont then
221 basefont[2] = getprev(n)
222 end
223 b = b + 1
224 basefont = { n, false }
225 basefonts[b] = basefont
226 end
227 end
228
229 local function setnode(n,font,attr)
230 if firstnone then
231 protectnone()
232 end
233 if basefont then
234 basefont[2] = getprev(n)
235 basefont = false
236 end
237 if attr > 0 then
238 local used = attrfonts[font]
239 if not used then
240 used = { }
241 attrfonts[font] = used
242 end
243 if not used[attr] then
244 local fd = setfontdynamics[font]
245 if fd then
246 used[attr] = fd[attr]
247 a = a + 1
248 end
249 end
250 else
251 local used = usedfonts[font]
252 if not used then
253 lastfont = font
254 lastproc = fontprocesses[font]
255 if lastproc then
256 usedfonts[font] = lastproc
257 u = u + 1
258 end
259 end
260 end
261 end
262
263 function handlers.characters(head,groupcode,size,packtype,direction)
264
265 starttiming(nodes)
266
267 usedfonts = { }
268 attrfonts = { }
269 basefonts = { }
270 basefont = nil
271 prevfont = nil
272 prevattr = 0
273 variants = nil
274 redundant = nil
275 firstnone = nil
276 lastfont = nil
277 lastproc = nil
278 lastnone = nil
279
280 a, u, b, r = 0, 0, 0, 0
281
282 if trace_fontrun then
283 start_trace(head)
284 end
285
286
287
288
289 for n, char, font in nextchar, head do
290
291 local attr = getglyphdata(n) or 0
292 if font ~= prevfont or attr ~= prevattr then
293 prevfont = font
294 prevattr = attr
295 variants = fontvariants[font]
296 local fontmode = fontmodes[font]
297 if fontmode == "none" then
298 setnone(n)
299 elseif fontmode == "base" then
300 setbase(n)
301 else
302 setnode(n,font,attr)
303 end
304 elseif firstnone then
305 lastnone = n
306 end
307 if variants then
308 if (char >= 0xFE00 and char <= 0xFE0F) or (char >= 0xE0100 and char <= 0xE01EF) then
309 local hash = variants[char]
310 if hash then
311 local p = getprev(n)
312 if p then
313 local char = ischar(p)
314 local variant = hash[char]
315 if variant then
316 if trace_variants then
317 report_fonts("replacing %C by %C",char,variant)
318 end
319 setchar(p,variant)
320 if redundant then
321 r = r + 1
322 redundant[r] = n
323 else
324 r = 1
325 redundant = { n }
326 end
327 end
328 end
329 elseif keep_redundant then
330
331 elseif redundant then
332 r = r + 1
333 redundant[r] = n
334 else
335 r = 1
336 redundant = { n }
337 end
338 end
339 end
340 end
341
342 if firstnone then
343 protectnone()
344 end
345
346 if force_boundaryrun then
347
348
349
350
351
352 for b, subtype in nextboundary, head do
353 if subtype == wordboundary_code then
354 if redundant then
355 r = r + 1
356 redundant[r] = b
357 else
358 r = 1
359 redundant = { b }
360 end
361 end
362 end
363
364 end
365
366 if redundant then
367 for i=1,r do
368 local r = redundant[i]
369 local p, n = getboth(r)
370 if r == head then
371 head = n
372 setprev(n)
373 else
374 setlink(p,n)
375 end
376 if b > 0 then
377 for i=1,b do
378 local bi = basefonts[i]
379 local b1 = bi[1]
380 local b2 = bi[2]
381 if b1 == b2 then
382 if b1 == r then
383 bi[1] = false
384 bi[2] = false
385 end
386 elseif b1 == r then
387 bi[1] = n
388 elseif b2 == r then
389 bi[2] = p
390 end
391 end
392 end
393 flushnode(r)
394 end
395 end
396
397 if force_discrun then
398
399
400
401 for d in nextdisc, head do
402
403
404 local r = getreplace(d)
405 if r then
406 local prevfont = nil
407 local prevattr = nil
408 local none = false
409 firstnone = nil
410 basefont = nil
411 for n, char, font in nextchar, r do
412 local attr = getglyphdata(n) or 0
413 if font ~= prevfont or attr ~= prevattr then
414 prevfont = font
415 prevattr = attr
416 local fontmode = fontmodes[font]
417 if fontmode == "none" then
418 setnone(n)
419 elseif fontmode == "base" then
420
421 setbase(n)
422 else
423 setnode(n,font,attr)
424 end
425 elseif firstnone then
426
427 lastnone = nil
428 end
429
430
431 break
432 end
433 if firstnone then
434 protectnone()
435 end
436 end
437 end
438
439 end
440
441 if trace_fontrun then
442 stop_trace(u,usedfonts,a,attrfonts,b,basefonts,r,redundant)
443 end
444
445
446 if u == 0 then
447
448 elseif u == 1 then
449 local attr = a > 0 and 0 or false
450 for i=1,#lastproc do
451 head = lastproc[i](head,lastfont,attr,direction)
452 end
453 else
454
455 local attr = a > 0 and 0 or false
456 for font, processors in next, usedfonts do
457 for i=1,#processors do
458 head = processors[i](head,font,attr,direction,u)
459 end
460 end
461 end
462 if a == 0 then
463
464 elseif a == 1 then
465 local font, dynamics = next(attrfonts)
466 for attribute, processors in next, dynamics do
467 for i=1,#processors do
468 head = processors[i](head,font,attribute,direction)
469 end
470 end
471 else
472 for font, dynamics in next, attrfonts do
473 for attribute, processors in next, dynamics do
474 for i=1,#processors do
475 head = processors[i](head,font,attribute,direction,a)
476 end
477 end
478 end
479 end
480 if b == 0 then
481
482 elseif b == 1 then
483
484 local range = basefonts[1]
485 local start = range[1]
486 local stop = range[2]
487 if (start or stop) and (start ~= stop) then
488 local front = head == start
489 if stop then
490 start = ligaturing(start,stop)
491 start = kerning(start,stop)
492 elseif start then
493 start = ligaturing(start)
494 start = kerning(start)
495 end
496 if front and head ~= start then
497 head = start
498 end
499 end
500 else
501
502 for i=1,b do
503 local range = basefonts[i]
504 local start = range[1]
505 local stop = range[2]
506 if start then
507 local front = head == start
508 local prev = getprev(start)
509 local next = getnext(stop)
510 if stop then
511 start, stop = ligaturing(start,stop)
512 start, stop = kerning(start,stop)
513 else
514 start = ligaturing(start)
515 start = kerning(start)
516 end
517
518 if prev then
519 setlink(prev,start)
520 end
521 if next then
522 setlink(stop,next)
523 end
524
525 if front and head ~= start then
526 head = start
527 end
528 end
529 end
530 end
531
532 stoptiming(nodes)
533
534 if trace_characters then
535 nodes.report(head)
536 end
537
538 return head
539 end
540
541end
542
543handlers.protectglyphs = protectglyphs
544handlers.unprotectglyphs = unprotectglyphs
545 |