font-chk.lua /size: 15 Kb    last modification: 2020-07-01 14:35
1
if
not
modules
then
modules
=
{
}
end
modules
[
'
font-chk
'
]
=
{
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
-- possible optimization: delayed initialization of vectors
10
-- move to the nodes namespace
11 12
local
next
=
next
13
local
floor
=
math
.
floor
14 15
local
context
=
context
16 17
local
formatters
=
string
.
formatters
18
local
bpfactor
=
number
.
dimenfactors
.
bp
19
local
fastcopy
=
table
.
fastcopy
20
local
sortedkeys
=
table
.
sortedkeys
21
local
sortedhash
=
table
.
sortedhash
22 23
local
report
=
logs
.
reporter
(
"
fonts
"
)
24
local
report_checking
=
logs
.
reporter
(
"
fonts
"
,
"
checking
"
)
25 26
local
allocate
=
utilities
.
storage
.
allocate
27 28
local
fonts
=
fonts
29 30
fonts
.
checkers
=
fonts
.
checkers
or
{
}
31
local
checkers
=
fonts
.
checkers
32 33
local
fonthashes
=
fonts
.
hashes
34
local
fontdata
=
fonthashes
.
identifiers
35
local
fontcharacters
=
fonthashes
.
characters
36 37
local
currentfont
=
font
.
current
38
local
addcharacters
=
font
.
addcharacters
39 40
local
helpers
=
fonts
.
helpers
41 42
local
addprivate
=
helpers
.
addprivate
43
local
hasprivate
=
helpers
.
hasprivate
44
local
getprivateslot
=
helpers
.
getprivateslot
45
local
getprivatecharornode
=
helpers
.
getprivatecharornode
46 47
local
otffeatures
=
fonts
.
constructors
.
features
.
otf
48
local
afmfeatures
=
fonts
.
constructors
.
features
.
afm
49 50
local
registerotffeature
=
otffeatures
.
register
51
local
registerafmfeature
=
afmfeatures
.
register
52 53
local
is_character
=
characters
.
is_character
54
local
chardata
=
characters
.
data
55 56
local
tasks
=
nodes
.
tasks
57
local
enableaction
=
tasks
.
enableaction
58
local
disableaction
=
tasks
.
disableaction
59 60
local
implement
=
interfaces
.
implement
61 62
local
glyph_code
=
nodes
.
nodecodes
.
glyph
63 64
local
new_special
=
nodes
.
pool
.
special
-- todo: literal
65
local
hpack_node
=
node
.
hpack
66 67
local
nuts
=
nodes
.
nuts
68
local
tonut
=
nuts
.
tonut
69 70
local
isglyph
=
nuts
.
isglyph
71
local
setchar
=
nuts
.
setchar
72 73
local
nextglyph
=
nuts
.
traversers
.
glyph
74 75
local
remove_node
=
nuts
.
remove
76
local
insert_node_after
=
nuts
.
insert_after
77 78
-- maybe in fonts namespace
79
-- deletion can be option
80 81
local
action
=
false
82 83
-- to tfmdata.properties ?
84 85
local
function
onetimemessage
(
font
,
char
,
message
)
-- char == false returns table
86
local
tfmdata
=
fontdata
[
font
]
87
local
shared
=
tfmdata
.
shared
88
if
not
shared
then
89
shared
=
{
}
90
tfmdata
.
shared
=
shared
91
end
92
local
messages
=
shared
.
messages
93
if
not
messages
then
94
messages
=
{
}
95
shared
.
messages
=
messages
96
end
97
local
category
=
messages
[
message
]
98
if
not
category
then
99
category
=
{
}
100
messages
[
message
]
=
category
101
end
102
if
char
=
=
false
then
103
return
sortedkeys
(
category
)
,
category
104
end
105
local
cc
=
category
[
char
]
106
if
not
cc
then
107
report_checking
(
"
char %C in font %a with id %a: %s
"
,
char
,
tfmdata
.
properties
.
fullname
,
font
,
message
)
108
category
[
char
]
=
1
109
else
110
category
[
char
]
=
cc
+
1
111
end
112
end
113 114
fonts
.
loggers
.
onetimemessage
=
onetimemessage
115 116
local
mapping
=
allocate
{
-- this is just an experiment to illustrate some principles elsewhere
117
lu
=
"
placeholder uppercase red
"
,
118
ll
=
"
placeholder lowercase red
"
,
119
lt
=
"
placeholder uppercase red
"
,
120
lm
=
"
placeholder lowercase red
"
,
121
lo
=
"
placeholder lowercase red
"
,
122
mn
=
"
placeholder mark green
"
,
123
mc
=
"
placeholder mark green
"
,
124
me
=
"
placeholder mark green
"
,
125
nd
=
"
placeholder lowercase blue
"
,
126
nl
=
"
placeholder lowercase blue
"
,
127
no
=
"
placeholder lowercase blue
"
,
128
pc
=
"
placeholder punctuation cyan
"
,
129
pd
=
"
placeholder punctuation cyan
"
,
130
ps
=
"
placeholder punctuation cyan
"
,
131
pe
=
"
placeholder punctuation cyan
"
,
132
pi
=
"
placeholder punctuation cyan
"
,
133
pf
=
"
placeholder punctuation cyan
"
,
134
po
=
"
placeholder punctuation cyan
"
,
135
sm
=
"
placeholder lowercase magenta
"
,
136
sc
=
"
placeholder lowercase yellow
"
,
137
sk
=
"
placeholder lowercase yellow
"
,
138
so
=
"
placeholder lowercase yellow
"
,
139
}
140 141
table
.
setmetatableindex
(
mapping
,
142
function
(
t
,
k
)
143
local
v
=
"
placeholder unknown gray
"
144
t
[
k
]
=
v
145
return
v
146
end
147
)
148 149
local
fakes
=
allocate
{
150
{
151
name
=
"
lowercase
"
,
152
code
=
"
.025 -.175 m .425 -.175 l .425 .525 l .025 .525 l .025 -.175 l .025 0 l .425 0 l .025 -.175 m h S
"
,
153
width
=
.
45
,
154
height
=
.
55
,
155
depth
=
.
20
,
156
}
,
157
{
158
name
=
"
uppercase
"
,
159
code
=
"
.025 -.225 m .625 -.225 l .625 .675 l .025 .675 l .025 -.225 l .025 0 l .625 0 l .025 -.225 m h S
"
,
160
width
=
.
65
,
161
height
=
.
70
,
162
depth
=
.
25
,
163
}
,
164
{
165
name
=
"
mark
"
,
166
code
=
"
.025 .475 m .125 .475 l .125 .675 l .025 .675 l .025 .475 l h B
"
,
167
width
=
.
15
,
168
height
=
.
70
,
169
depth
=
-
.
50
,
170
}
,
171
{
172
name
=
"
punctuation
"
,
173
code
=
"
.025 -.175 m .125 -.175 l .125 .525 l .025 .525 l .025 -.175 l h B
"
,
174
width
=
.
15
,
175
height
=
.
55
,
176
depth
=
.
20
,
177
}
,
178
{
179
name
=
"
unknown
"
,
180
code
=
"
.025 0 m .425 0 l .425 .175 l .025 .175 l .025 0 l h B
"
,
181
width
=
.
45
,
182
height
=
.
20
,
183
depth
=
0
,
184
}
,
185
}
186 187
local
variants
=
allocate
{
188
{
tag
=
"
gray
"
,
r
=
.
6
,
g
=
.
6
,
b
=
.
6
}
,
189
{
tag
=
"
red
"
,
r
=
.
6
,
g
=
0
,
b
=
0
}
,
190
{
tag
=
"
green
"
,
r
=
0
,
g
=
.
6
,
b
=
0
}
,
191
{
tag
=
"
blue
"
,
r
=
0
,
g
=
0
,
b
=
.
6
}
,
192
{
tag
=
"
cyan
"
,
r
=
0
,
g
=
.
6
,
b
=
.
6
}
,
193
{
tag
=
"
magenta
"
,
r
=
.
6
,
g
=
0
,
b
=
.
6
}
,
194
{
tag
=
"
yellow
"
,
r
=
.
6
,
g
=
.
6
,
b
=
0
}
,
195
}
196 197
-- bah .. low level pdf ... should be a rule or plugged in
198 199
----- pdf_blob = "pdf: q %.6F 0 0 %.6F 0 0 cm %s %s %s rg %s %s %s RG 10 M 1 j 1 J 0.05 w %s Q"
200
local
pdf_blob
=
"
q %.6F 0 0 %.6F 0 0 cm %s %s %s rg %s %s %s RG 10 M 1 j 1 J 0.05 w %s Q
"
201 202
local
cache
=
{
}
-- saves some tables but not that impressive
203 204
local
function
missingtonode
(
tfmdata
,
character
)
205
local
commands
=
character
.
commands
206
local
fake
=
hpack_node
(
new_special
(
commands
[
1
]
[
2
]
)
)
-- todo: literal
207
fake
.
width
=
character
.
width
208
fake
.
height
=
character
.
height
209
fake
.
depth
=
character
.
depth
210
return
fake
211
end
212 213
local
function
addmissingsymbols
(
tfmdata
)
-- we can have an alternative with rules
214
local
characters
=
tfmdata
.
characters
215
local
properties
=
tfmdata
.
properties
216
local
size
=
tfmdata
.
parameters
.
size
217
local
scale
=
size
*
bpfactor
218
local
tonode
=
nil
219
local
collected
=
{
}
220
if
properties
.
finalized
and
not
addcharacters
then
221
tonode
=
missingtonode
222
end
223
for
i
=
1
,
#
variants
do
224
local
v
=
variants
[
i
]
225
local
tag
,
r
,
g
,
b
=
v
.
tag
,
v
.
r
,
v
.
g
,
v
.
b
226
for
i
=
1
,
#
fakes
do
227
local
fake
=
fakes
[
i
]
228
local
name
=
fake
.
name
229
local
privatename
=
formatters
[
"
placeholder %s %s
"
]
(
name
,
tag
)
230
if
not
hasprivate
(
tfmdata
,
privatename
)
then
231
local
hash
=
formatters
[
"
%s_%s_%1.3f_%1.3f_%1.3f_%i
"
]
(
name
,
tag
,
r
,
g
,
b
,
floor
(
size
)
)
232
local
char
=
cache
[
hash
]
233
if
not
char
then
234
char
=
{
235
tonode
=
tonode
,
236
width
=
size
*
fake
.
width
,
237
height
=
size
*
fake
.
height
,
238
depth
=
size
*
fake
.
depth
,
239
-- commands = { { "special", formatters[pdf_blob](scale,scale,r,g,b,r,g,b,fake.code) } }
240
commands
=
{
{
"
pdf
"
,
formatters
[
pdf_blob
]
(
scale
,
scale
,
r
,
g
,
b
,
r
,
g
,
b
,
fake
.
code
)
}
}
241
}
242
cache
[
hash
]
=
char
243
end
244
local
u
=
addprivate
(
tfmdata
,
privatename
,
char
)
245
if
not
tonode
then
246
collected
[
u
]
=
char
247
end
248
end
249
end
250
end
251
if
#
collected
>
0
then
252
addcharacters
(
properties
.
id
,
{
253
type
=
"
real
"
,
254
characters
=
collected
,
255
}
)
256
end
257
end
258 259
registerotffeature
{
260
name
=
"
missing
"
,
261
description
=
"
missing symbols
"
,
262
manipulators
=
{
263
base
=
addmissingsymbols
,
264
node
=
addmissingsymbols
,
265
}
266
}
267 268
fonts
.
loggers
.
add_placeholders
=
function
(
id
)
addmissingsymbols
(
fontdata
[
id
or
true
]
)
end
269
fonts
.
loggers
.
category_to_placeholder
=
mapping
270 271
function
commands
.
getplaceholderchar
(
name
)
272
local
id
=
currentfont
(
)
273
addmissingsymbols
(
fontdata
[
id
]
)
274
context
(
getprivatenode
(
fontdata
[
id
]
,
name
)
)
275
end
276 277
-- todo in luatex: option to add characters (just slots, no kerns etc)
278
-- we can do that now so ...
279 280
local
function
placeholder
(
font
,
char
)
281
local
tfmdata
=
fontdata
[
font
]
282
local
category
=
chardata
[
char
]
.
category
or
"
unknown
"
283
local
fakechar
=
mapping
[
category
]
284
local
slot
=
getprivateslot
(
font
,
fakechar
)
285
if
not
slot
then
286
addmissingsymbols
(
tfmdata
)
287
slot
=
getprivateslot
(
font
,
fakechar
)
288
end
289
return
getprivatecharornode
(
tfmdata
,
fakechar
)
290
end
291 292
checkers
.
placeholder
=
placeholder
293 294
function
checkers
.
missing
(
head
)
295
local
lastfont
,
characters
,
found
=
nil
,
nil
,
nil
296
for
n
,
char
,
font
in
nextglyph
,
head
do
-- faster than while loop so we delay removal
297
if
font
~
=
lastfont
then
298
characters
=
fontcharacters
[
font
]
299
lastfont
=
font
300
end
301
if
font
>
0
and
not
characters
[
char
]
and
is_character
[
chardata
[
char
]
.
category
or
"
unknown
"
]
then
302
if
action
=
=
"
remove
"
then
303
onetimemessage
(
font
,
char
,
"
missing (will be deleted)
"
)
304
elseif
action
=
=
"
replace
"
then
305
onetimemessage
(
font
,
char
,
"
missing (will be flagged)
"
)
306
else
307
onetimemessage
(
font
,
char
,
"
missing
"
)
308
end
309
if
not
found
then
310
found
=
{
n
}
311
else
312
found
[
#
found
+
1
]
=
n
313
end
314
end
315
end
316
if
not
found
then
317
-- all well
318
elseif
action
=
=
"
remove
"
then
319
for
i
=
1
,
#
found
do
320
head
=
remove_node
(
head
,
found
[
i
]
,
true
)
321
end
322
elseif
action
=
=
"
replace
"
then
323
for
i
=
1
,
#
found
do
324
local
node
=
found
[
i
]
325
local
char
,
font
=
isglyph
(
node
)
326
local
kind
,
char
=
placeholder
(
font
,
char
)
327
if
kind
=
=
"
node
"
then
328
insert_node_after
(
head
,
node
,
tonut
(
char
)
)
329
head
=
remove_node
(
head
,
node
,
true
)
330
elseif
kind
=
=
"
char
"
then
331
setchar
(
node
,
char
)
332
else
333
-- error
334
end
335
end
336
else
337
-- maye write a report to the log
338
end
339
return
head
340
end
341 342
local
relevant
=
{
343
"
missing (will be deleted)
"
,
344
"
missing (will be flagged)
"
,
345
"
missing
"
346
}
347 348
local
function
getmissing
(
id
)
349
if
id
then
350
local
list
=
getmissing
(
currentfont
(
)
)
351
if
list
then
352
local
_
,
list
=
next
(
getmissing
(
currentfont
(
)
)
)
353
return
list
354
else
355
return
{
}
356
end
357
else
358
local
t
=
{
}
359
for
id
,
d
in
next
,
fontdata
do
360
local
shared
=
d
.
shared
361
local
messages
=
shared
and
shared
.
messages
362
if
messages
then
363
local
filename
=
d
.
properties
.
filename
364
if
not
filename
then
365
filename
=
tostring
(
d
)
366
end
367
local
tf
=
t
[
filename
]
or
{
}
368
for
i
=
1
,
#
relevant
do
369
local
tm
=
messages
[
relevant
[
i
]
]
370
if
tm
then
371
for
k
,
v
in
next
,
tm
do
372
tf
[
k
]
=
(
tf
[
k
]
or
0
)
+
v
373
end
374
end
375
end
376
if
next
(
tf
)
then
377
t
[
filename
]
=
tf
378
end
379
end
380
end
381
local
l
=
{
}
382
for
k
,
v
in
next
,
t
do
383
l
[
k
]
=
sortedkeys
(
v
)
384
end
385
return
l
,
t
386
end
387
end
388 389
checkers
.
getmissing
=
getmissing
390 391 392
do
393 394
local
reported
=
true
395 396
callback
.
register
(
"
glyph_not_found
"
,
function
(
font
,
char
)
397
if
font
>
0
then
398
if
char
>
0
then
399
onetimemessage
(
font
,
char
,
"
missing
"
)
400
else
401
-- we have a special case
402
end
403
elseif
not
reported
then
404
report
(
"
nullfont is used, maybe no bodyfont is defined
"
)
405
reported
=
true
406
end
407
end
)
408 409
trackers
.
register
(
"
fonts.missing
"
,
function
(
v
)
410
if
v
then
411
enableaction
(
"
processors
"
,
"
fonts.checkers.missing
"
)
412
else
413
disableaction
(
"
processors
"
,
"
fonts.checkers.missing
"
)
414
end
415
if
v
=
=
"
replace
"
then
416
otffeatures
.
defaults
.
missing
=
true
417
end
418
action
=
v
419
end
)
420 421
logs
.
registerfinalactions
(
function
(
)
422
local
collected
,
details
=
getmissing
(
)
423
if
next
(
collected
)
then
424
for
filename
,
list
in
sortedhash
(
details
)
do
425
logs
.
startfilelogging
(
report
,
"
missing characters
"
,
filename
)
426
for
u
,
v
in
sortedhash
(
list
)
do
427
report
(
"
%4i %U %c %s
"
,
v
,
u
,
u
,
chardata
[
u
]
.
description
)
428
end
429
logs
.
stopfilelogging
(
)
430
end
431
if
logs
.
loggingerrors
(
)
then
432
for
filename
,
list
in
sortedhash
(
details
)
do
433
logs
.
starterrorlogging
(
report
,
"
missing characters
"
,
filename
)
434
for
u
,
v
in
sortedhash
(
list
)
do
435
report
(
"
%4i %U %c %s
"
,
v
,
u
,
u
,
chardata
[
u
]
.
description
)
436
end
437
logs
.
stoperrorlogging
(
)
438
end
439
end
440
end
441
end
)
442 443
end
444 445
-- for the moment here
446 447
local
function
expandglyph
(
characters
,
index
,
done
)
448
done
=
done
or
{
}
449
if
not
done
[
index
]
then
450
local
data
=
characters
[
index
]
451
if
data
then
452
done
[
index
]
=
true
453
local
d
=
fastcopy
(
data
)
454
local
n
=
d
.
next
455
if
n
then
456
d
.
next
=
expandglyph
(
characters
,
n
,
done
)
457
end
458
local
h
=
d
.
horiz_variants
459
if
h
then
460
for
i
=
1
,
#
h
do
461
h
[
i
]
.
glyph
=
expandglyph
(
characters
,
h
[
i
]
.
glyph
,
done
)
462
end
463
end
464
local
v
=
d
.
vert_variants
465
if
v
then
466
for
i
=
1
,
#
v
do
467
v
[
i
]
.
glyph
=
expandglyph
(
characters
,
v
[
i
]
.
glyph
,
done
)
468
end
469
end
470
return
d
471
end
472
end
473
end
474 475
helpers
.
expandglyph
=
expandglyph
476 477
-- should not be needed as we add .notdef in the engine
478 479
local
dummyzero
=
{
480
-- width = 0,
481
-- height = 0,
482
-- depth = 0,
483
commands
=
{
{
"
special
"
,
"
"
}
}
,
484
}
485 486
local
function
adddummysymbols
(
tfmdata
)
487
local
characters
=
tfmdata
.
characters
488
if
not
characters
[
0
]
then
489
characters
[
0
]
=
dummyzero
490
end
491
-- if not characters[1] then
492
-- characters[1] = dummyzero -- test only
493
-- end
494
end
495 496
local
dummies_specification
=
{
497
name
=
"
dummies
"
,
498
description
=
"
dummy symbols
"
,
499
default
=
true
,
500
manipulators
=
{
501
base
=
adddummysymbols
,
502
node
=
adddummysymbols
,
503
}
504
}
505 506
registerotffeature
(
dummies_specification
)
507
registerafmfeature
(
dummies_specification
)
508 509
--
510 511
local
function
addvisualspace
(
tfmdata
)
512
local
spacechar
=
tfmdata
.
characters
[
32
]
513
if
spacechar
and
not
spacechar
.
commands
then
514
local
w
=
spacechar
.
width
515
local
h
=
tfmdata
.
parameters
.
xheight
516
local
c
=
{
517
width
=
w
,
518
commands
=
{
{
"
rule
"
,
h
,
w
}
}
519
}
520
local
u
=
addprivate
(
tfmdata
,
"
visualspace
"
,
c
)
521
end
522
end
523 524
local
visualspace_specification
=
{
525
name
=
"
visualspace
"
,
526
description
=
"
visual space
"
,
527
default
=
true
,
528
manipulators
=
{
529
base
=
addvisualspace
,
530
node
=
addvisualspace
,
531
}
532
}
533 534
registerotffeature
(
visualspace_specification
)
535
registerafmfeature
(
visualspace_specification
)
536