node-fnt.lmt /size: 18 Kb    last modification: 2021-10-28 13:51
1
if
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 9
if
not
context
then
os
.
exit
(
)
end
-- generic function in node-dum
10 11
local
next
,
type
=
next
,
type
12
local
concat
,
keys
=
table
.
concat
,
table
.
keys
13 14
local
nodes
,
node
,
fonts
=
nodes
,
node
,
fonts
15 16
local
trace_characters
=
false
trackers
.
register
(
"
nodes.characters
"
,
function
(
v
)
trace_characters
=
v
end
)
17
local
trace_fontrun
=
false
trackers
.
register
(
"
nodes.fontrun
"
,
function
(
v
)
trace_fontrun
=
v
end
)
18
local
trace_variants
=
false
trackers
.
register
(
"
nodes.variants
"
,
function
(
v
)
trace_variants
=
v
end
)
19 20
-- bad namespace for directives
21 22
local
force_discrun
=
true
directives
.
register
(
"
nodes.discrun
"
,
function
(
v
)
force_discrun
=
v
end
)
23
local
force_boundaryrun
=
true
directives
.
register
(
"
nodes.boundaryrun
"
,
function
(
v
)
force_boundaryrun
=
v
end
)
24
local
force_basepass
=
true
directives
.
register
(
"
nodes.basepass
"
,
function
(
v
)
force_basepass
=
v
end
)
25
local
keep_redundant
=
false
directives
.
register
(
"
nodes.keepredundant
"
,
function
(
v
)
keep_redundant
=
v
end
)
26 27
local
report_fonts
=
logs
.
reporter
(
"
fonts
"
,
"
processing
"
)
28 29
local
fonthashes
=
fonts
.
hashes
30
local
fontdata
=
fonthashes
.
identifiers
31
local
fontvariants
=
fonthashes
.
variants
32
local
fontmodes
=
fonthashes
.
modes
33 34
local
otf
=
fonts
.
handlers
.
otf
35 36
local
starttiming
=
statistics
.
starttiming
37
local
stoptiming
=
statistics
.
stoptiming
38 39
local
nodecodes
=
nodes
.
nodecodes
40
local
boundarycodes
=
nodes
.
boundarycodes
41 42
local
handlers
=
nodes
.
handlers
43 44
local
nuts
=
nodes
.
nuts
45 46
local
getid
=
nuts
.
getid
47
local
getsubtype
=
nuts
.
getsubtype
48
local
getreplace
=
nuts
.
getreplace
49
local
getnext
=
nuts
.
getnext
50
local
getprev
=
nuts
.
getprev
51
local
getboth
=
nuts
.
getboth
52
local
getdata
=
nuts
.
getdata
53
local
getglyphdata
=
nuts
.
getglyphdata
54 55
local
setchar
=
nuts
.
setchar
56
local
setlink
=
nuts
.
setlink
57
local
setnext
=
nuts
.
setnext
58
local
setprev
=
nuts
.
setprev
59 60
local
isglyph
=
nuts
.
isglyph
-- unchecked
61
local
ischar
=
nuts
.
ischar
-- checked
62 63
local
nextboundary
=
nuts
.
traversers
.
boundary
64
local
nextdisc
=
nuts
.
traversers
.
disc
65
local
nextchar
=
nuts
.
traversers
.
char
66 67
local
flushnode
=
nuts
.
flush
68 69
local
disc_code
=
nodecodes
.
disc
70
local
boundary_code
=
nodecodes
.
boundary
71 72
local
wordboundary_code
=
boundarycodes
.
word
73 74
local
protectglyphs
=
nuts
.
protectglyphs
75
local
unprotectglyphs
=
nuts
.
unprotectglyphs
76 77
local
setmetatableindex
=
table
.
setmetatableindex
78 79
-- some tests with using an array of dynamics[id] and processes[id] demonstrated
80
-- that there was nothing to gain (unless we also optimize other parts)
81
--
82
-- maybe getting rid of the intermediate shared can save some time
83 84
local
run
=
0
85 86
local
setfontdynamics
=
{
}
87
local
fontprocesses
=
{
}
88 89
setmetatableindex
(
setfontdynamics
,
function
(
t
,
font
)
90
local
tfmdata
=
fontdata
[
font
]
91
local
shared
=
tfmdata
.
shared
92
local
f
=
shared
and
shared
.
dynamics
and
otf
.
setdynamics
or
false
93
if
f
then
94
local
v
=
{
}
95
t
[
font
]
=
v
96
setmetatableindex
(
v
,
function
(
t
,
k
)
97
local
v
=
f
(
font
,
k
)
98
t
[
k
]
=
v
99
return
v
100
end
)
101
return
v
102
else
103
t
[
font
]
=
false
104
return
false
105
end
106
end
)
107 108
setmetatableindex
(
fontprocesses
,
function
(
t
,
font
)
109
local
tfmdata
=
fontdata
[
font
]
110
local
shared
=
tfmdata
.
shared
-- we need to check shared, only when same features
111
local
processes
=
shared
and
shared
.
processes
112
if
processes
and
#
processes
>
0
then
113
t
[
font
]
=
processes
114
return
processes
115
else
116
t
[
font
]
=
false
117
return
false
118
end
119
end
)
120 121
fonts
.
hashes
.
setdynamics
=
setfontdynamics
122
fonts
.
hashes
.
processes
=
fontprocesses
123 124
-- if we forget about basemode we don't need to test too much here and we can consider running
125
-- over sub-ranges .. this involves a bit more initializations but who cares .. in that case we
126
-- also need to use the stop criterium (we already use head too) ... we cannot use traverse
127
-- then, so i'll test it on some local clone first ... the only pitfall is changed directions
128
-- inside a run which means that we need to keep track of this which in turn complicates matters
129
-- in a way i don't like
130 131
-- we need to deal with the basemode fonts here and can only run over ranges as we otherwise get
132
-- luatex craches due to all kind of asserts in the disc/lig builder
133 134
-- there is no gain in merging used (dynamic 0) and dynamics apart from a bit less code
135 136
local
ligaturing
=
nuts
.
ligaturing
137
local
kerning
=
nuts
.
kerning
138 139
local
function
start_trace
(
head
)
140
run
=
run
+
1
141
report_fonts
(
)
142
report_fonts
(
"
checking node list, run %s
"
,
run
)
143
report_fonts
(
)
144
local
n
=
head
145
while
n
do
146
local
char
,
id
=
isglyph
(
n
)
147
if
char
then
148
local
font
=
id
149
local
dynamic
=
getglyphdata
(
n
)
or
0
150
report_fonts
(
"
font %03i, dynamic %03i, glyph %C
"
,
font
,
dynamic
,
char
)
151
elseif
id
=
=
disc_code
then
152
report_fonts
(
"
[disc] %s
"
,
nodes
.
listtoutf
(
n
,
true
,
false
,
n
)
)
153
elseif
id
=
=
boundary_code
then
154
report_fonts
(
"
[boundary] %i:%i
"
,
getsubtype
(
n
)
,
getdata
(
n
)
)
155
else
156
report_fonts
(
"
[%s]
"
,
nodecodes
[
id
]
)
157
end
158
n
=
getnext
(
n
)
159
end
160
end
161 162
local
function
stop_trace
(
u
,
usedfonts
,
d
,
dynamicfonts
,
b
,
basefonts
,
r
,
redundant
)
163
report_fonts
(
)
164
report_fonts
(
"
statics : %s
"
,
u
>
0
and
concat
(
keys
(
usedfonts
)
,
"
"
)
or
"
none
"
)
165
report_fonts
(
"
dynamics: %s
"
,
d
>
0
and
concat
(
keys
(
dynamicfonts
)
,
"
"
)
or
"
none
"
)
166
report_fonts
(
"
built-in: %s
"
,
b
>
0
and
b
or
"
none
"
)
167
report_fonts
(
"
removed : %s
"
,
r
>
0
and
r
or
"
none
"
)
168
report_fonts
(
)
169
end
170 171
do
172 173
local
usedfonts
174
local
dynamicfonts
175
local
basefonts
-- could be reused
176
local
basefont
177
local
prevfont
178
local
prevdynamic
179
local
variants
180
local
redundant
-- could be reused
181
local
firstnone
182
local
lastfont
183
local
lastproc
184
local
lastnone
185 186
local
d
,
u
,
b
,
r
187 188
local
function
protectnone
(
)
189
protectglyphs
(
firstnone
,
lastnone
)
190
firstnone
=
nil
191
end
192 193
local
function
setnone
(
n
)
194
if
firstnone
then
195
protectnone
(
)
196
end
197
if
basefont
then
198
basefont
[
2
]
=
getprev
(
n
)
199
basefont
=
false
200
end
201
if
not
firstnone
then
202
firstnone
=
n
203
end
204
lastnone
=
n
205
end
206 207
local
function
setbase
(
n
)
208
if
firstnone
then
209
protectnone
(
)
210
end
211
if
force_basepass
then
212
if
basefont
then
213
basefont
[
2
]
=
getprev
(
n
)
214
end
215
b
=
b
+
1
216
basefont
=
{
n
,
false
}
217
basefonts
[
b
]
=
basefont
218
end
219
end
220 221
local
function
setnode
(
n
,
font
,
dynamic
)
-- we could use prevfont and prevdynamic when we set then first
222
if
firstnone
then
223
protectnone
(
)
224
end
225
if
basefont
then
226
basefont
[
2
]
=
getprev
(
n
)
227
basefont
=
false
228
end
229
if
dynamic
>
0
then
230
local
used
=
dynamicfonts
[
font
]
231
if
not
used
then
232
used
=
{
}
233
dynamicfonts
[
font
]
=
used
234
end
235
if
not
used
[
dynamic
]
then
236
local
fd
=
setfontdynamics
[
font
]
237
if
fd
then
238
used
[
dynamic
]
=
fd
[
dynamic
]
239
d
=
d
+
1
240
end
241
end
242
else
243
local
used
=
usedfonts
[
font
]
244
if
not
used
then
245
lastfont
=
font
246
lastproc
=
fontprocesses
[
font
]
247
if
lastproc
then
248
usedfonts
[
font
]
=
lastproc
249
u
=
u
+
1
250
end
251
end
252
end
253
end
254 255
function
handlers
.
characters
(
head
,
groupcode
,
direction
)
256
-- either next or not, but definitely no already processed list
257
starttiming
(
nodes
)
258 259
usedfonts
=
{
}
260
dynamicfonts
=
{
}
261
basefonts
=
{
}
262
basefont
=
nil
263
prevfont
=
nil
264
prevdynamic
=
0
265
variants
=
nil
266
redundant
=
nil
267
firstnone
=
nil
268
lastfont
=
nil
269
lastproc
=
nil
270
lastnone
=
nil
271 272
local
fontmode
=
nil
-- base none or other
273 274
d
,
u
,
b
,
r
=
0
,
0
,
0
,
0
275 276
if
trace_fontrun
then
277
start_trace
(
head
)
278
end
279 280
-- There is no gain in checking for a single glyph and then having a fast path. On the
281
-- metafun manual (with some 2500 single char lists) the difference is just noise.
282 283
for
n
,
char
,
font
,
dynamic
in
nextchar
,
head
do
284 285
if
font
~
=
prevfont
then
286
prevfont
=
font
287
fontmode
=
fontmodes
[
font
]
288
if
fontmode
=
=
"
none
"
then
289
prevdynamic
=
0
290
variants
=
false
291
setnone
(
n
)
292
elseif
fontmode
=
=
"
base
"
then
293
prevdynamic
=
0
294
variants
=
false
295
setbase
(
n
)
296
else
297
-- local dynamic = getglyphdata(n) or 0 -- zero dynamic is reserved for fonts in context
298
prevdynamic
=
dynamic
299
variants
=
fontvariants
[
font
]
300
setnode
(
n
,
font
,
dynamic
)
301
end
302
elseif
fontmode
=
=
"
node
"
then
303
local
dynamic
=
getglyphdata
(
n
)
or
0
-- zero dynamic is reserved for fonts in context
304
if
dynamic
~
=
prevdynamic
then
305
prevdynamic
=
dynamic
306
variants
=
fontvariants
[
font
]
307
setnode
(
n
,
font
,
dynamic
)
308
end
309
elseif
firstnone
then
310
lastnone
=
n
311
end
312 313
if
variants
then
314
if
(
char
>
=
0xFE00
and
char
<
=
0xFE0F
)
or
(
char
>
=
0xE0100
and
char
<
=
0xE01EF
)
then
315
-- if variants and char >= 0xFE00 then
316
-- if char < 0xFE0F or (char >= 0xE0100 and char <= 0xE01EF) then
317
local
hash
=
variants
[
char
]
318
if
hash
then
319
local
p
=
getprev
(
n
)
320
if
p
then
321
local
char
=
ischar
(
p
)
-- checked
322
local
variant
=
hash
[
char
]
323
if
variant
then
324
if
trace_variants
then
325
report_fonts
(
"
replacing %C by %C
"
,
char
,
variant
)
326
end
327
setchar
(
p
,
variant
)
328
if
redundant
then
329
r
=
r
+
1
330
redundant
[
r
]
=
n
331
else
332
r
=
1
333
redundant
=
{
n
}
334
end
335
end
336
end
337
elseif
keep_redundant
then
338
-- go on, can be used for tracing
339
elseif
redundant
then
340
r
=
r
+
1
341
redundant
[
r
]
=
n
342
else
343
r
=
1
344
redundant
=
{
n
}
345
end
346
end
347
end
348 349
end
350 351
if
firstnone
then
352
protectnone
(
)
353
end
354 355
if
force_boundaryrun
then
356 357
-- we can inject wordboundaries and then let the hyphenator do its work
358
-- but we need to get rid of those nodes in order to build ligatures
359
-- and kern (a rather context thing)
360 361
for
b
,
subtype
in
nextboundary
,
head
do
362
if
subtype
=
=
wordboundary_code
then
363
if
redundant
then
364
r
=
r
+
1
365
redundant
[
r
]
=
b
366
else
367
r
=
1
368
redundant
=
{
b
}
369
end
370
end
371
end
372 373
end
374 375
if
redundant
then
376
for
i
=
1
,
r
do
377
local
r
=
redundant
[
i
]
378
local
p
,
n
=
getboth
(
r
)
379
if
r
=
=
head
then
380
head
=
n
381
setprev
(
n
)
382
else
383
setlink
(
p
,
n
)
384
end
385
if
b
>
0
then
386
for
i
=
1
,
b
do
387
local
bi
=
basefonts
[
i
]
388
local
b1
=
bi
[
1
]
389
local
b2
=
bi
[
2
]
390
if
b1
=
=
b2
then
391
if
b1
=
=
r
then
392
bi
[
1
]
=
false
393
bi
[
2
]
=
false
394
end
395
elseif
b1
=
=
r
then
396
bi
[
1
]
=
n
397
elseif
b2
=
=
r
then
398
bi
[
2
]
=
p
399
end
400
end
401
end
402
flushnode
(
r
)
403
end
404
end
405 406
if
force_discrun
then
407
-- basefont is not supported in disc only runs ... it would mean a lot of
408
-- ranges .. we could try to run basemode as a separate processor run but not
409
-- for now (we can consider it when the new node code is tested
410
for
disc
in
nextdisc
,
head
do
411
-- doing only replace is good enough because pre and post are normally used
412
-- for hyphens and these come from fonts that part of the hyphenated word
413
local
r
=
getreplace
(
disc
)
414
if
r
then
415
local
prevfont
=
nil
416
local
prevdynamic
=
nil
417
local
none
=
false
418
firstnone
=
nil
419
basefont
=
nil
420
for
n
,
char
,
font
,
dynamic
in
nextchar
,
r
do
421
-- local dynamic = getglyphdata(n) or 0 -- zero dynamic is reserved for fonts in context
422
if
font
~
=
prevfont
or
dynamic
~
=
prevdynamic
then
423
prevfont
=
font
424
prevdynamic
=
dynamic
425
local
fontmode
=
fontmodes
[
font
]
426
if
fontmode
=
=
"
none
"
then
427
setnone
(
n
)
428
elseif
fontmode
=
=
"
base
"
then
429
-- so the replace gets an extra treatment ... so be it
430
setbase
(
n
)
431
else
432
setnode
(
n
,
font
,
dynamic
)
433
end
434
elseif
firstnone
then
435
-- lastnone = n
436
lastnone
=
nil
437
end
438
-- we assume one font for now (and if there are more and we get into issues then
439
-- we can always remove the break)
440
break
441
end
442
if
firstnone
then
443
protectnone
(
)
444
end
445
end
446
end
447 448
end
449 450
if
trace_fontrun
then
451
stop_trace
(
u
,
usedfonts
,
d
,
dynamicfonts
,
b
,
basefonts
,
r
,
redundant
)
452
end
453 454
-- in context we always have at least 2 processors
455
if
u
=
=
0
then
456
-- skip
457
elseif
u
=
=
1
then
458
for
i
=
1
,
#
lastproc
do
459
head
=
lastproc
[
i
]
(
head
,
lastfont
,
0
,
direction
)
460
end
461
else
462
for
font
,
processors
in
next
,
usedfonts
do
-- unordered
463
for
i
=
1
,
#
processors
do
464
head
=
processors
[
i
]
(
head
,
font
,
0
,
direction
,
u
)
-- u triggers disc optimizer
465
end
466
end
467
end
468
if
d
=
=
0
then
469
-- skip
470
elseif
d
=
=
1
then
471
local
font
,
dynamics
=
next
(
dynamicfonts
)
472
for
dynamic
,
processors
in
next
,
dynamics
do
-- unordered, dynamic can switch in between
473
for
i
=
1
,
#
processors
do
474
head
=
processors
[
i
]
(
head
,
font
,
dynamic
,
direction
)
475
end
476
end
477
else
478
for
font
,
dynamics
in
next
,
dynamicfonts
do
479
for
dynamic
,
processors
in
next
,
dynamics
do
-- unordered, dynamic can switch in between
480
for
i
=
1
,
#
processors
do
481
head
=
processors
[
i
]
(
head
,
font
,
dynamic
,
direction
,
d
)
-- d triggers disc optimizer
482
end
483
end
484
end
485
end
486
if
b
=
=
0
then
487
-- skip
488
elseif
b
=
=
1
then
489
-- only one font
490
local
range
=
basefonts
[
1
]
491
local
start
=
range
[
1
]
492
local
stop
=
range
[
2
]
493
if
(
start
or
stop
)
and
(
start
~
=
stop
)
then
494
local
front
=
head
=
=
start
495
if
stop
then
496
start
=
ligaturing
(
start
,
stop
)
497
start
=
kerning
(
start
,
stop
)
498
elseif
start
then
-- safeguard
499
start
=
ligaturing
(
start
)
500
start
=
kerning
(
start
)
501
end
502
if
front
and
head
~
=
start
then
503
head
=
start
504
end
505
end
506
else
507
-- multiple fonts
508
for
i
=
1
,
b
do
509
local
range
=
basefonts
[
i
]
510
local
start
=
range
[
1
]
511
local
stop
=
range
[
2
]
512
if
start
then
-- and start ~= stop but that seldom happens
513
local
front
=
head
=
=
start
514
local
prev
=
getprev
(
start
)
515
local
next
=
getnext
(
stop
)
516
if
stop
then
517
start
,
stop
=
ligaturing
(
start
,
stop
)
518
start
,
stop
=
kerning
(
start
,
stop
)
519
else
520
start
=
ligaturing
(
start
)
521
start
=
kerning
(
start
)
522
end
523
-- is done automatically
524
if
prev
then
525
setlink
(
prev
,
start
)
526
end
527
if
next
then
528
setlink
(
stop
,
next
)
529
end
530
-- till here
531
if
front
and
head
~
=
start
then
532
head
=
start
533
end
534
end
535
end
536
end
537 538
stoptiming
(
nodes
)
539 540
if
trace_characters
then
541
nodes
.
report
(
head
)
542
end
543 544
return
head
545
end
546 547
end
548 549
handlers
.
protectglyphs
=
protectglyphs
550
handlers
.
unprotectglyphs
=
unprotectglyphs
551