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