node-fnt.lua /size: 18 Kb    last modification: 2021-10-28 13:50
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 v = shared and shared.dynamics and otf.setdynamics or false
93
-- t[font] = v
94
-- return v
95
-- end)
96 97
setmetatableindex
(
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
114
end
)
115 116
setmetatableindex
(
fontprocesses
,
function
(
t
,
font
)
117
local
tfmdata
=
fontdata
[
font
]
118
local
shared
=
tfmdata
.
shared
-- we need to check shared, only when same features
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
127
end
)
128 129
fonts
.
hashes
.
setdynamics
=
setfontdynamics
130
fonts
.
hashes
.
processes
=
fontprocesses
131 132
-- if we forget about basemode we don't need to test too much here and we can consider running
133
-- over sub-ranges .. this involves a bit more initializations but who cares .. in that case we
134
-- also need to use the stop criterium (we already use head too) ... we cannot use traverse
135
-- then, so i'll test it on some local clone first ... the only pitfall is changed directions
136
-- inside a run which means that we need to keep track of this which in turn complicates matters
137
-- in a way i don't like
138 139
-- we need to deal with the basemode fonts here and can only run over ranges as we otherwise get
140
-- luatex craches due to all kind of asserts in the disc/lig builder
141 142
-- there is no gain in merging used (dynamic 0) and dynamics apart from a bit less code
143 144
local
ligaturing
=
nuts
.
ligaturing
145
local
kerning
=
nuts
.
kerning
146 147
local
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
168
end
169 170
local
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
(
)
177
end
178 179
do
180 181
local
usedfonts
182
local
attrfonts
183
local
basefonts
-- could be reused
184
local
basefont
185
local
prevfont
186
local
prevattr
187
local
variants
188
local
redundant
-- could be reused
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
)
-- we could use prevfont and prevattr when we set then first
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
-- either next or not, but definitely no already processed list
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
-- There is no gain in checking for a single glyph and then having a fast path. On the
287
-- metafun manual (with some 2500 single char lists) the difference is just noise.
288 289
for
n
,
char
,
font
in
nextchar
,
head
do
290
-- local attr = (none and prevattr) or getglyphdata(n) or 0 -- zero attribute is reserved for fonts in context
291
local
attr
=
getglyphdata
(
n
)
or
0
-- zero attribute is reserved for fonts in context
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
)
-- checked
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
-- go on, can be used for tracing
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
-- we can inject wordboundaries and then let the hyphenator do its work
349
-- but we need to get rid of those nodes in order to build ligatures
350
-- and kern (a rather context thing)
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
-- basefont is not supported in disc only runs ... it would mean a lot of
399
-- ranges .. we could try to run basemode as a separate processor run but not
400
-- for now (we can consider it when the new node code is tested
401
for
d
in
nextdisc
,
head
do
402
-- doing only replace is good enough because pre and post are normally used
403
-- for hyphens and these come from fonts that part of the hyphenated word
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
-- zero attribute is reserved for fonts in context
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
-- so the replace gets an extra treatment ... so be it
421
setbase
(
n
)
422
else
423
setnode
(
n
,
font
,
attr
)
424
end
425
elseif
firstnone
then
426
-- lastnone = n
427
lastnone
=
nil
428
end
429
-- we assume one font for now (and if there are more and we get into issues then
430
-- we can always remove the break)
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
-- in context we always have at least 2 processors
446
if
u
=
=
0
then
447
-- skip
448
elseif
u
=
=
1
then
449
local
attr
=
a
>
0
and
0
or
false
-- 0 is the savest way
450
for
i
=
1
,
#
lastproc
do
451
head
=
lastproc
[
i
]
(
head
,
lastfont
,
attr
,
direction
)
452
end
453
else
454
-- local attr = a == 0 and false or 0 -- 0 is the savest way
455
local
attr
=
a
>
0
and
0
or
false
-- 0 is the savest way
456
for
font
,
processors
in
next
,
usedfonts
do
-- unordered
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
-- skip
464
elseif
a
=
=
1
then
465
local
font
,
dynamics
=
next
(
attrfonts
)
466
for
attribute
,
processors
in
next
,
dynamics
do
-- unordered, attr can switch in between
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
-- unordered, attr can switch in between
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
-- skip
482
elseif
b
=
=
1
then
483
-- only one font
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
-- safeguard
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
-- multiple fonts
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
-- and start ~= stop but that seldom happens
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
-- is done automatically
518
if
prev
then
519
setlink
(
prev
,
start
)
520
end
521
if
next
then
522
setlink
(
stop
,
next
)
523
end
524
-- till here
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 541
end
542 543
handlers
.
protectglyphs
=
protectglyphs
544
handlers
.
unprotectglyphs
=
unprotectglyphs
545