font-syn.lua /size: 77 Kb    last modification: 2020-07-01 14:35
1
if
not
modules
then
modules
=
{
}
end
modules
[
'
font-syn
'
]
=
{
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
-- todo: subs in lookups requests
10
-- todo: see if the (experimental) lua reader (on my machine) be used (it's a bit slower so maybe wait till lua 5.3)
11 12
-- identifying ttf/otf/ttc/afm : 2200 fonts:
13
--
14
-- old ff loader: 140 sec
15
-- new lua loader: 5 sec
16 17
-- maybe find(...,strictname,1,true)
18 19
local
next
,
tonumber
,
type
,
tostring
=
next
,
tonumber
,
type
,
tostring
20
local
sub
,
gsub
,
match
,
find
,
lower
,
upper
=
string
.
sub
,
string
.
gsub
,
string
.
match
,
string
.
find
,
string
.
lower
,
string
.
upper
21
local
concat
,
sort
,
fastcopy
,
tohash
=
table
.
concat
,
table
.
sort
,
table
.
fastcopy
,
table
.
tohash
22
local
serialize
,
sortedhash
=
table
.
serialize
,
table
.
sortedhash
23
local
lpegmatch
=
lpeg
.
match
24
local
unpack
=
unpack
or
table
.
unpack
25
local
formatters
,
topattern
=
string
.
formatters
,
string
.
topattern
26
local
round
=
math
.
round
27
local
P
,
R
,
S
,
C
,
Cc
,
Ct
,
Cs
=
lpeg
.
P
,
lpeg
.
R
,
lpeg
.
S
,
lpeg
.
C
,
lpeg
.
Cc
,
lpeg
.
Ct
,
lpeg
.
Cs
28
local
lpegmatch
,
lpegpatterns
=
lpeg
.
match
,
lpeg
.
patterns
29
local
isfile
,
modificationtime
=
lfs
.
isfile
,
lfs
.
modification
30 31
local
allocate
=
utilities
.
storage
.
allocate
32
local
sparse
=
utilities
.
storage
.
sparse
33
local
setmetatableindex
=
table
.
setmetatableindex
34 35
local
removesuffix
=
file
.
removesuffix
36
local
splitbase
=
file
.
splitbase
37
local
splitname
=
file
.
splitname
38
local
basename
=
file
.
basename
39
local
nameonly
=
file
.
nameonly
40
local
pathpart
=
file
.
pathpart
41
local
suffixonly
=
file
.
suffix
42
local
filejoin
=
file
.
join
43
local
is_qualified_path
=
file
.
is_qualified_path
44
local
exists
=
io
.
exists
45 46
local
findfile
=
resolvers
.
findfile
47
local
cleanpath
=
resolvers
.
cleanpath
48
local
resolveprefix
=
resolvers
.
resolve
49 50
local
settings_to_hash
=
utilities
.
parsers
.
settings_to_hash_tolerant
51 52
local
trace_names
=
false
trackers
.
register
(
"
fonts.names
"
,
function
(
v
)
trace_names
=
v
end
)
53
local
trace_warnings
=
false
trackers
.
register
(
"
fonts.warnings
"
,
function
(
v
)
trace_warnings
=
v
end
)
54
local
trace_specifications
=
false
trackers
.
register
(
"
fonts.specifications
"
,
function
(
v
)
trace_specifications
=
v
end
)
55
local
trace_rejections
=
false
trackers
.
register
(
"
fonts.rejections
"
,
function
(
v
)
trace_rejections
=
v
end
)
56 57
local
report_names
=
logs
.
reporter
(
"
fonts
"
,
"
names
"
)
58 59
--[[ldx-- 60<p>This module implements a name to filename resolver. Names are resolved 61using a table that has keys filtered from the font related files.</p> 62--ldx]]
--
63 64
fonts
=
fonts
or
{
}
-- also used elsewhere
65 66
local
names
=
fonts
.
names
or
allocate
{
}
67
fonts
.
names
=
names
68 69
local
filters
=
names
.
filters
or
{
}
70
names
.
filters
=
filters
71 72
local
treatments
=
fonts
.
treatments
or
{
}
73
fonts
.
treatments
=
treatments
74 75
names
.
data
=
names
.
data
or
allocate
{
}
76 77
names
.
version
=
1
.
131
78
names
.
basename
=
"
names
"
79
names
.
saved
=
false
80
names
.
loaded
=
false
81
names
.
be_clever
=
true
82
names
.
enabled
=
true
83
names
.
cache
=
containers
.
define
(
"
fonts
"
,
"
data
"
,
names
.
version
,
true
)
84 85
local
usesystemfonts
=
true
86
local
autoreload
=
true
87 88
directives
.
register
(
"
fonts.autoreload
"
,
function
(
v
)
autoreload
=
toboolean
(
v
)
end
)
89
directives
.
register
(
"
fonts.usesystemfonts
"
,
function
(
v
)
usesystemfonts
=
toboolean
(
v
)
end
)
90 91
--[[ldx-- 92<p>A few helpers.</p> 93--ldx]]
--
94 95
-- -- what to do with these -- --
96
--
97
-- thin -> thin
98
--
99
-- regu -> regular -> normal
100
-- norm -> normal -> normal
101
-- stan -> standard -> normal
102
-- medi -> medium
103
-- ultr -> ultra
104
-- ligh -> light
105
-- heav -> heavy
106
-- blac -> black
107
-- thin
108
-- book
109
-- verylight
110
--
111
-- buch -> book
112
-- buchschrift -> book
113
-- halb -> demi
114
-- halbfett -> demi
115
-- mitt -> medium
116
-- mittel -> medium
117
-- fett -> bold
118
-- mage -> light
119
-- mager -> light
120
-- nord -> normal
121
-- gras -> normal
122 123
local
weights
=
Cs
(
-- not extra
124
P
(
"
demibold
"
)
125
+
P
(
"
semibold
"
)
126
+
P
(
"
mediumbold
"
)
127
+
P
(
"
ultrabold
"
)
128
+
P
(
"
extrabold
"
)
129
+
P
(
"
ultralight
"
)
130
+
P
(
"
extralight
"
)
131
+
P
(
"
bold
"
)
132
+
P
(
"
demi
"
)
-- / "semibold"
133
+
P
(
"
semi
"
)
-- / "semibold"
134
+
P
(
"
light
"
)
135
+
P
(
"
medium
"
)
136
+
P
(
"
heavy
"
)
137
+
P
(
"
ultra
"
)
138
+
P
(
"
black
"
)
139
--+ P("bol") / "bold" -- blocks
140
+
P
(
"
bol
"
)
141
+
P
(
"
regular
"
)
/
"
normal
"
142
)
143 144
-- local weights = {
145
-- [100] = "thin",
146
-- [200] = "extralight",
147
-- [300] = "light",
148
-- [400] = "normal",
149
-- [500] = "medium",
150
-- [600] = "semibold", -- demi demibold
151
-- [700] = "bold",
152
-- [800] = "extrabold",
153
-- [900] = "black",
154
-- }
155 156
local
normalized_weights
=
sparse
{
157
regular
=
"
normal
"
,
158
}
159 160
local
styles
=
Cs
(
161
P
(
"
reverseoblique
"
)
/
"
reverseitalic
"
162
+
P
(
"
regular
"
)
/
"
normal
"
163
+
P
(
"
italic
"
)
164
+
P
(
"
oblique
"
)
/
"
italic
"
165
+
P
(
"
slanted
"
)
166
+
P
(
"
roman
"
)
/
"
normal
"
167
+
P
(
"
ital
"
)
/
"
italic
"
-- might be tricky
168
+
P
(
"
ita
"
)
/
"
italic
"
-- might be tricky
169
--+ P("obli") / "oblique"
170
)
171 172
local
normalized_styles
=
sparse
{
173
reverseoblique
=
"
reverseitalic
"
,
174
regular
=
"
normal
"
,
175
oblique
=
"
italic
"
,
176
}
177 178
local
widths
=
Cs
(
179
P
(
"
condensed
"
)
180
+
P
(
"
thin
"
)
181
+
P
(
"
expanded
"
)
182
+
P
(
"
cond
"
)
/
"
condensed
"
183
--+ P("expa") / "expanded"
184
+
P
(
"
normal
"
)
185
+
P
(
"
book
"
)
/
"
normal
"
186
)
187 188
local
normalized_widths
=
sparse
(
)
189 190
local
variants
=
Cs
(
-- fax casual
191
P
(
"
smallcaps
"
)
192
+
P
(
"
oldstyle
"
)
193
+
P
(
"
caps
"
)
/
"
smallcaps
"
194
)
195 196
local
normalized_variants
=
sparse
(
)
197 198
names
.
knownweights
=
{
199
"
black
"
,
200
"
bold
"
,
201
"
demi
"
,
202
"
demibold
"
,
203
"
extrabold
"
,
204
"
heavy
"
,
205
"
light
"
,
206
"
medium
"
,
207
"
mediumbold
"
,
208
"
normal
"
,
209
"
regular
"
,
210
"
semi
"
,
211
"
semibold
"
,
212
"
ultra
"
,
213
"
ultrabold
"
,
214
"
ultralight
"
,
215
}
216 217
names
.
knownstyles
=
{
218
"
italic
"
,
219
"
normal
"
,
220
"
oblique
"
,
221
"
regular
"
,
222
"
reverseitalic
"
,
223
"
reverseoblique
"
,
224
"
roman
"
,
225
"
slanted
"
,
226
}
227 228
names
.
knownwidths
=
{
229
"
book
"
,
230
"
condensed
"
,
231
"
expanded
"
,
232
"
normal
"
,
233
"
thin
"
,
234
}
235 236
names
.
knownvariants
=
{
237
"
normal
"
,
238
"
oldstyle
"
,
239
"
smallcaps
"
,
240
}
241 242
local
remappedweights
=
{
243
[
"
"
]
=
"
normal
"
,
244
[
"
bol
"
]
=
"
bold
"
,
245
}
246 247
local
remappedstyles
=
{
248
[
"
"
]
=
"
normal
"
,
249
}
250 251
local
remappedwidths
=
{
252
[
"
"
]
=
"
normal
"
,
253
}
254 255
local
remappedvariants
=
{
256
[
"
"
]
=
"
normal
"
,
257
}
258 259
names
.
remappedweights
=
remappedweights
setmetatableindex
(
remappedweights
,
"
self
"
)
260
names
.
remappedstyles
=
remappedstyles
setmetatableindex
(
remappedstyles
,
"
self
"
)
261
names
.
remappedwidths
=
remappedwidths
setmetatableindex
(
remappedwidths
,
"
self
"
)
262
names
.
remappedvariants
=
remappedvariants
setmetatableindex
(
remappedvariants
,
"
self
"
)
263 264
local
any
=
P
(
1
)
265 266
local
analyzed_table
267 268
local
analyzer
=
Cs
(
269
(
270
weights
/
function
(
s
)
analyzed_table
[
1
]
=
s
return
"
"
end
271
+
styles
/
function
(
s
)
analyzed_table
[
2
]
=
s
return
"
"
end
272
+
widths
/
function
(
s
)
analyzed_table
[
3
]
=
s
return
"
"
end
273
+
variants
/
function
(
s
)
analyzed_table
[
4
]
=
s
return
"
"
end
274
+
any
275
)
^
0
276
)
277 278
local
splitter
=
lpeg
.
splitat
(
"
-
"
)
279 280
function
names
.
splitspec
(
askedname
)
281
local
name
,
weight
,
style
,
width
,
variant
=
lpegmatch
(
splitter
,
askedname
)
282
weight
=
weight
and
lpegmatch
(
weights
,
weight
)
or
weight
283
style
=
style
and
lpegmatch
(
styles
,
style
)
or
style
284
width
=
width
and
lpegmatch
(
widths
,
width
)
or
width
285
variant
=
variant
and
lpegmatch
(
variants
,
variant
)
or
variant
286
if
trace_names
then
287
report_names
(
"
requested name %a split in name %a, weight %a, style %a, width %a and variant %a
"
,
288
askedname
,
name
,
weight
,
style
,
width
,
variant
)
289
end
290
if
not
weight
or
not
weight
or
not
width
or
not
variant
then
291
weight
,
style
,
width
,
variant
=
weight
or
"
normal
"
,
style
or
"
normal
"
,
width
or
"
normal
"
,
variant
or
"
normal
"
292
if
trace_names
then
293
report_names
(
"
request %a normalized to '%s-%s-%s-%s-%s'
"
,
294
askedname
,
name
,
weight
,
style
,
width
,
variant
)
295
end
296
end
297
return
name
or
askedname
,
weight
,
style
,
width
,
variant
298
end
299 300
local
function
analyzespec
(
somename
)
301
if
somename
then
302
analyzed_table
=
{
}
303
local
name
=
lpegmatch
(
analyzer
,
somename
)
304
return
name
,
analyzed_table
[
1
]
,
analyzed_table
[
2
]
,
analyzed_table
[
3
]
,
analyzed_table
[
4
]
305
end
306
end
307 308
--[[ldx-- 309<p>It would make sense to implement the filters in the related modules, 310but to keep the overview, we define them here.</p> 311--ldx]]
--
312 313
filters
.
afm
=
fonts
.
handlers
.
afm
.
readers
.
getinfo
314
filters
.
otf
=
fonts
.
handlers
.
otf
.
readers
.
getinfo
315
filters
.
ttf
=
filters
.
otf
316
filters
.
ttc
=
filters
.
otf
317
-------.ttx = filters.otf
318 319
-- local function normalize(t) -- only for afm parsing
320
-- local boundingbox = t.boundingbox or t.fontbbox
321
-- if boundingbox then
322
-- for i=1,#boundingbox do
323
-- boundingbox[i] = tonumber(boundingbox[i])
324
-- end
325
-- else
326
-- boundingbox = { 0, 0, 0, 0 }
327
-- end
328
-- return {
329
-- copyright = t.copyright,
330
-- fontname = t.fontname,
331
-- fullname = t.fullname,
332
-- familyname = t.familyname,
333
-- weight = t.weight,
334
-- widtht = t.width,
335
-- italicangle = tonumber(t.italicangle) or 0,
336
-- monospaced = t.monospaced or toboolean(t.isfixedpitch) or false,
337
-- boundingbox = boundingbox,
338
-- version = t.version, -- not used
339
-- capheight = tonumber(t.capheight),
340
-- xheight = tonumber(t.xheight),
341
-- ascender = tonumber(t.ascender),
342
-- descender = tonumber(t.descender),
343
-- }
344
-- end
345
--
346
-- function filters.afm(name)
347
-- -- we could parse the afm file as well, and then report an error but
348
-- -- it's not worth the trouble
349
-- local pfbname = findfile(removesuffix(name)..".pfb","pfb") or ""
350
-- if pfbname == "" then
351
-- pfbname = findfile(nameonly(name)..".pfb","pfb") or ""
352
-- end
353
-- if pfbname ~= "" then
354
-- local f = io.open(name)
355
-- if f then
356
-- local hash = { }
357
-- local okay = false
358
-- for line in f:lines() do -- slow but only a few lines at the beginning
359
-- if find(line,"StartCharMetrics",1,true) then
360
-- break
361
-- else
362
-- local key, value = match(line,"^(.+)%s+(.+)%s*$")
363
-- if key and #key > 0 then
364
-- hash[lower(key)] = value
365
-- end
366
-- end
367
-- end
368
-- f:close()
369
-- return normalize(hash)
370
-- end
371
-- end
372
-- return nil, "no matching pfb file"
373
-- end
374 375
-- local p_spaces = lpegpatterns.whitespace
376
-- local p_number = (R("09")+S(".-+"))^1 / tonumber
377
-- local p_boolean = P("false") * Cc(false)
378
-- + P("false") * Cc(false)
379
-- local p_string = P("(") * C((lpegpatterns.nestedparents + 1 - P(")"))^1) * P(")")
380
-- local p_array = P("[") * Ct((p_number + p_boolean + p_string + p_spaces^1)^1) * P("]")
381
-- + P("{") * Ct((p_number + p_boolean + p_string + p_spaces^1)^1) * P("}")
382
--
383
-- local p_key = P("/") * C(R("AZ","az")^1)
384
-- local p_value = p_string
385
-- + p_number
386
-- + p_boolean
387
-- + p_array
388
--
389
-- local p_entry = p_key * p_spaces^0 * p_value
390
--
391
-- function filters.pfb(name)
392
-- local f = io.open(name)
393
-- if f then
394
-- local hash = { }
395
-- local okay = false
396
-- for line in f:lines() do -- slow but only a few lines at the beginning
397
-- if find(line,"dict begin",1,true) then
398
-- okay = true
399
-- elseif not okay then
400
-- -- go on
401
-- elseif find(line,"currentdict end",1,true) then
402
-- break
403
-- else
404
-- local key, value = lpegmatch(p_entry,line)
405
-- if key and value then
406
-- hash[lower(key)] = value
407
-- end
408
-- end
409
-- end
410
-- f:close()
411
-- return normalize(hash)
412
-- end
413
-- end
414 415
--[[ldx-- 416<p>The scanner loops over the filters using the information stored in 417the file databases. Watch how we check not only for the names, but also 418for combination with the weight of a font.</p> 419--ldx]]
--
420 421
filters
.
list
=
{
422
"
otf
"
,
"
ttf
"
,
"
ttc
"
,
"
afm
"
,
-- no longer dfont support (for now)
423
}
424 425
-- to be considered: loop over paths per list entry (so first all otf ttf etc)
426 427
names
.
fontconfigfile
=
"
fonts.conf
"
-- a bit weird format, bonus feature
428
names
.
osfontdirvariable
=
"
OSFONTDIR
"
-- the official way, in minimals etc
429
names
.
extrafontsvariable
=
"
EXTRAFONTS
"
-- the official way, in minimals etc
430
names
.
runtimefontsvariable
=
"
RUNTIMEFONTS
"
-- the official way, in minimals etc
431 432
filters
.
paths
=
{
}
433
filters
.
names
=
{
}
434 435
function
names
.
getpaths
(
trace
)
436
local
hash
,
result
,
r
=
{
}
,
{
}
,
0
437
local
function
collect
(
t
,
where
)
438
for
i
=
1
,
#
t
do
439
local
v
=
cleanpath
(
t
[
i
]
)
440
v
=
gsub
(
v
,
"
/+$
"
,
"
"
)
-- not needed any more
441
local
key
=
lower
(
v
)
442
report_names
(
"
variable %a specifies path %a
"
,
where
,
v
)
443
if
not
hash
[
key
]
then
444
r
=
r
+
1
445
result
[
r
]
=
v
446
hash
[
key
]
=
true
447
end
448
end
449
end
450
local
path
=
names
.
osfontdirvariable
or
"
"
451
if
path
~
=
"
"
then
452
collect
(
resolvers
.
expandedpathlist
(
path
)
,
path
)
453
end
454
local
path
=
names
.
extrafontsvariable
or
"
"
455
if
path
~
=
"
"
then
456
collect
(
resolvers
.
expandedpathlist
(
path
)
,
path
)
457
end
458
if
xml
then
459
local
confname
=
resolvers
.
expansion
(
"
FONTCONFIG_FILE
"
)
or
"
"
460
if
confname
=
=
"
"
then
461
confname
=
names
.
fontconfigfile
or
"
"
462
end
463
if
confname
~
=
"
"
then
464
-- first look in the tex tree
465
local
name
=
findfile
(
confname
,
"
fontconfig files
"
)
or
"
"
466
if
name
=
=
"
"
then
467
-- after all, fontconfig is a unix thing
468
name
=
filejoin
(
"
/etc
"
,
confname
)
469
if
not
isfile
(
name
)
then
470
name
=
"
"
-- force quit
471
end
472
end
473
if
name
~
=
"
"
and
isfile
(
name
)
then
474
if
trace_names
then
475
report_names
(
"
%s fontconfig file %a
"
,
"
loading
"
,
name
)
476
end
477
local
xmldata
=
xml
.
load
(
name
)
478
-- begin of untested mess
479
xml
.
include
(
xmldata
,
"
include
"
,
"
"
,
true
,
function
(
incname
)
480
if
not
is_qualified_path
(
incname
)
then
481
local
path
=
pathpart
(
name
)
-- main name
482
if
path
~
=
"
"
then
483
incname
=
filejoin
(
path
,
incname
)
484
end
485
end
486
if
isfile
(
incname
)
then
487
if
trace_names
then
488
report_names
(
"
%s fontconfig file %a
"
,
"
merging included
"
,
incname
)
489
end
490
return
io
.
loaddata
(
incname
)
491
elseif
trace_names
then
492
report_names
(
"
%s fontconfig file: %a
"
,
"
ignoring included
"
,
incname
)
493
end
494
end
)
495
-- end of untested mess
496
local
fontdirs
=
xml
.
collect_texts
(
xmldata
,
"
dir
"
,
true
)
497
if
trace_names
then
498
report_names
(
"
%s dirs found in fontconfig
"
,
#
fontdirs
)
499
end
500
collect
(
fontdirs
,
"
fontconfig file
"
)
501
end
502
end
503
end
504
sort
(
result
)
505
function
names
.
getpaths
(
)
506
return
result
507
end
508
return
result
509
end
510 511
local
function
cleanname
(
name
)
512
return
(
gsub
(
lower
(
name
)
,
"
[^%a%d]
"
,
"
"
)
)
513
end
514 515
local
function
cleanfilename
(
fullname
,
defaultsuffix
)
516
if
fullname
then
517
local
path
,
name
,
suffix
=
splitname
(
fullname
)
518
if
name
then
519
name
=
gsub
(
lower
(
name
)
,
"
[^%a%d]
"
,
"
"
)
520
if
suffix
and
suffix
~
=
"
"
then
521
return
name
.
.
"
.
"
.
.
suffix
522
elseif
defaultsuffix
and
defaultsuffix
~
=
"
"
then
523
return
name
.
.
"
.
"
.
.
defaultsuffix
524
else
525
return
name
526
end
527
end
528
end
529
return
"
badfontname
"
530
end
531 532
local
sorter
=
function
(
a
,
b
)
533
return
a
>
b
-- longest first
534
end
535 536
-- local sorter = nil
537 538
names
.
cleanname
=
cleanname
539
names
.
cleanfilename
=
cleanfilename
540 541
-- local function check_names(result)
542
-- local names = result.names
543
-- if names then
544
-- for i=1,#names do
545
-- local name = names[i]
546
-- if name.lang == "English (US)" then
547
-- return name.names
548
-- end
549
-- end
550
-- end
551
-- return result
552
-- end
553 554 555
local
function
check_name
(
data
,
result
,
filename
,
modification
,
suffix
,
subfont
)
556
-- shortcuts
557
local
specifications
=
data
.
specifications
558
-- fetch
559
local
fullname
=
result
.
fullname
560
local
fontname
=
result
.
fontname
561
local
family
=
result
.
family
562
local
subfamily
=
result
.
subfamily
563
local
familyname
=
result
.
familyname
564
local
subfamilyname
=
result
.
subfamilyname
565
-- local compatiblename = result.compatiblename
566
-- local cfffullname = result.cfffullname
567
local
weight
=
result
.
weight
568
local
width
=
result
.
width
569
local
italicangle
=
tonumber
(
result
.
italicangle
)
570
local
subfont
=
subfont
571
local
rawname
=
fullname
or
fontname
or
familyname
572
local
filebase
=
removesuffix
(
basename
(
filename
)
)
573
local
cleanfilename
=
cleanname
(
filebase
)
-- for WS
574
-- normalize
575
fullname
=
fullname
and
cleanname
(
fullname
)
576
fontname
=
fontname
and
cleanname
(
fontname
)
577
family
=
family
and
cleanname
(
family
)
578
subfamily
=
subfamily
and
cleanname
(
subfamily
)
579
familyname
=
familyname
and
cleanname
(
familyname
)
580
subfamilyname
=
subfamilyname
and
cleanname
(
subfamilyname
)
581
-- compatiblename = compatiblename and cleanname(compatiblename)
582
-- cfffullname = cfffullname and cleanname(cfffullname)
583
weight
=
weight
and
cleanname
(
weight
)
584
width
=
width
and
cleanname
(
width
)
585
italicangle
=
italicangle
=
=
0
and
nil
586
-- analyze
587
local
a_name
,
a_weight
,
a_style
,
a_width
,
a_variant
=
analyzespec
(
fullname
or
fontname
or
familyname
)
588
-- check
589
local
width
=
width
or
a_width
590
local
variant
=
a_variant
591
local
style
=
subfamilyname
or
subfamily
-- can re really trust subfamilyname?
592
if
style
then
593
style
=
gsub
(
style
,
"
[^%a]
"
,
"
"
)
594
elseif
italicangle
then
595
style
=
"
italic
"
596
end
597
if
not
variant
or
variant
=
=
"
"
then
598
variant
=
"
normal
"
599
end
600
if
not
weight
or
weight
=
=
"
"
then
601
weight
=
a_weight
602
end
603
if
not
style
or
style
=
=
"
"
then
604
style
=
a_style
605
end
606
if
not
familyname
then
607
familyname
=
a_name
608
end
609
fontname
=
fontname
or
fullname
or
familyname
or
filebase
-- maybe cleanfilename
610
fullname
=
fullname
or
fontname
611
familyname
=
familyname
or
fontname
612
-- we do these sparse -- todo: check table type or change names in ff loader
613
local
units
=
result
.
units
or
1000
-- can be zero too
614
local
designsize
=
result
.
designsize
or
0
615
local
minsize
=
result
.
minsize
or
0
616
local
maxsize
=
result
.
maxsize
or
0
617
local
angle
=
result
.
italicangle
or
0
618
local
pfmwidth
=
result
.
pfmwidth
or
0
619
local
pfmweight
=
result
.
pfmweight
or
0
620
--
621
local
instancenames
=
result
.
instancenames
622
--
623
specifications
[
#
specifications
+
1
]
=
{
624
filename
=
filename
,
-- unresolved
625
cleanfilename
=
cleanfilename
,
626
-- subfontindex = subfont,
627
format
=
lower
(
suffix
)
,
628
subfont
=
subfont
,
629
rawname
=
rawname
,
630
fullname
=
fullname
,
631
fontname
=
fontname
,
632
family
=
family
,
633
subfamily
=
subfamily
,
634
familyname
=
familyname
,
635
subfamilyname
=
subfamilyname
,
636
-- compatiblename = compatiblename, -- nor used / needed
637
-- cfffullname = cfffullname,
638
weight
=
weight
,
639
style
=
style
,
640
width
=
width
,
641
variant
=
variant
,
642
units
=
units
~
=
1000
and
units
or
nil
,
643
pfmwidth
=
pfmwidth
~
=
0
and
pfmwidth
or
nil
,
644
pfmweight
=
pfmweight
~
=
0
and
pfmweight
or
nil
,
645
angle
=
angle
~
=
0
and
angle
or
nil
,
646
minsize
=
minsize
~
=
0
and
minsize
or
nil
,
647
maxsize
=
maxsize
~
=
0
and
maxsize
or
nil
,
648
designsize
=
designsize
~
=
0
and
designsize
or
nil
,
649
modification
=
modification
~
=
0
and
modification
or
nil
,
650
instancenames
=
instancenames
or
nil
,
651
}
652
end
653 654
local
function
cleanupkeywords
(
)
655
local
data
=
names
.
data
656
local
specifications
=
names
.
data
.
specifications
657
if
specifications
then
658
local
weights
=
{
}
659
local
styles
=
{
}
660
local
widths
=
{
}
661
local
variants
=
{
}
662
for
i
=
1
,
#
specifications
do
663
local
s
=
specifications
[
i
]
664
-- fix (sofar styles are taken from the name, and widths from the specification)
665
local
_
,
b_weight
,
b_style
,
b_width
,
b_variant
=
analyzespec
(
s
.
weight
)
666
local
_
,
c_weight
,
c_style
,
c_width
,
c_variant
=
analyzespec
(
s
.
style
)
667
local
_
,
d_weight
,
d_style
,
d_width
,
d_variant
=
analyzespec
(
s
.
width
)
668
local
_
,
e_weight
,
e_style
,
e_width
,
e_variant
=
analyzespec
(
s
.
variant
)
669
local
_
,
f_weight
,
f_style
,
f_width
,
f_variant
=
analyzespec
(
s
.
fullname
or
"
"
)
670
local
weight
=
b_weight
or
c_weight
or
d_weight
or
e_weight
or
f_weight
or
"
normal
"
671
local
style
=
b_style
or
c_style
or
d_style
or
e_style
or
f_style
or
"
normal
"
672
local
width
=
b_width
or
c_width
or
d_width
or
e_width
or
f_width
or
"
normal
"
673
local
variant
=
b_variant
or
c_variant
or
d_variant
or
e_variant
or
f_variant
or
"
normal
"
674
weight
=
remappedweights
[
weight
or
"
"
]
675
style
=
remappedstyles
[
style
or
"
"
]
676
width
=
remappedwidths
[
width
or
"
"
]
677
variant
=
remappedvariants
[
variant
or
"
"
]
678
weights
[
weight
]
=
(
weights
[
weight
]
or
0
)
+
1
679
styles
[
style
]
=
(
styles
[
style
]
or
0
)
+
1
680
widths
[
width
]
=
(
widths
[
width
]
or
0
)
+
1
681
variants
[
variant
]
=
(
variants
[
variant
]
or
0
)
+
1
682
if
weight
~
=
s
.
weight
then
683
s
.
fontweight
=
s
.
weight
684
end
685
s
.
weight
,
s
.
style
,
s
.
width
,
s
.
variant
=
weight
,
style
,
width
,
variant
686
end
687
local
statistics
=
data
.
statistics
688
statistics
.
used_weights
=
weights
689
statistics
.
used_styles
=
styles
690
statistics
.
used_widths
=
widths
691
statistics
.
used_variants
=
variants
692
end
693
end
694 695
local
function
collectstatistics
(
runtime
)
696
local
data
=
names
.
data
697
local
specifications
=
data
.
specifications
698
local
statistics
=
data
.
statistics
699
if
specifications
then
700
local
f_w
=
formatters
[
"
%i
"
]
701
local
f_a
=
formatters
[
"
%0.2f
"
]
702
-- normal stuff
703
local
weights
=
{
}
704
local
styles
=
{
}
705
local
widths
=
{
}
706
local
variants
=
{
}
707
-- weird stuff
708
local
angles
=
{
}
709
-- extra stuff
710
local
pfmweights
=
{
}
setmetatableindex
(
pfmweights
,
"
table
"
)
711
local
pfmwidths
=
{
}
setmetatableindex
(
pfmwidths
,
"
table
"
)
712
-- main loop
713
for
i
=
1
,
#
specifications
do
714
local
s
=
specifications
[
i
]
715
-- normal stuff
716
local
weight
=
s
.
weight
717
local
style
=
s
.
style
718
local
width
=
s
.
width
719
local
variant
=
s
.
variant
720
if
weight
then
weights
[
weight
]
=
(
weights
[
weight
]
or
0
)
+
1
end
721
if
style
then
styles
[
style
]
=
(
styles
[
style
]
or
0
)
+
1
end
722
if
width
then
widths
[
width
]
=
(
widths
[
width
]
or
0
)
+
1
end
723
if
variant
then
variants
[
variant
]
=
(
variants
[
variant
]
or
0
)
+
1
end
724
-- weird stuff
725
local
angle
=
f_a
(
tonumber
(
s
.
angle
)
or
0
)
726
angles
[
angle
]
=
(
angles
[
angles
]
or
0
)
+
1
727
-- extra stuff
728
local
pfmweight
=
f_w
(
s
.
pfmweight
or
0
)
729
local
pfmwidth
=
f_w
(
s
.
pfmwidth
or
0
)
730
local
tweights
=
pfmweights
[
pfmweight
]
731
local
twidths
=
pfmwidths
[
pfmwidth
]
732
tweights
[
pfmweight
]
=
(
tweights
[
pfmweight
]
or
0
)
+
1
733
twidths
[
pfmwidth
]
=
(
twidths
[
pfmwidth
]
or
0
)
+
1
734
end
735
--
736
statistics
.
weights
=
weights
737
statistics
.
styles
=
styles
738
statistics
.
widths
=
widths
739
statistics
.
variants
=
variants
740
statistics
.
angles
=
angles
741
statistics
.
pfmweights
=
pfmweights
742
statistics
.
pfmwidths
=
pfmwidths
743
statistics
.
fonts
=
#
specifications
744
--
745
setmetatableindex
(
pfmweights
,
nil
)
746
setmetatableindex
(
pfmwidths
,
nil
)
747
--
748
report_names
(
"
"
)
749
report_names
(
"
statistics:
"
)
750
report_names
(
"
"
)
751
report_names
(
"
weights
"
)
752
report_names
(
"
"
)
753
report_names
(
formatters
[
"
%T
"
]
(
weights
)
)
754
report_names
(
"
"
)
755
report_names
(
"
styles
"
)
756
report_names
(
"
"
)
757
report_names
(
formatters
[
"
%T
"
]
(
styles
)
)
758
report_names
(
"
"
)
759
report_names
(
"
widths
"
)
760
report_names
(
"
"
)
761
report_names
(
formatters
[
"
%T
"
]
(
widths
)
)
762
report_names
(
"
"
)
763
report_names
(
"
variants
"
)
764
report_names
(
"
"
)
765
report_names
(
formatters
[
"
%T
"
]
(
variants
)
)
766
report_names
(
"
"
)
767
report_names
(
"
angles
"
)
768
report_names
(
"
"
)
769
report_names
(
formatters
[
"
%T
"
]
(
angles
)
)
770
report_names
(
"
"
)
771
report_names
(
"
pfmweights
"
)
772
report_names
(
"
"
)
773
for
k
,
v
in
sortedhash
(
pfmweights
)
do
774
report_names
(
formatters
[
"
%-10s: %T
"
]
(
k
,
v
)
)
775
end
776
report_names
(
"
"
)
777
report_names
(
"
pfmwidths
"
)
778
report_names
(
"
"
)
779
for
k
,
v
in
sortedhash
(
pfmwidths
)
do
780
report_names
(
formatters
[
"
%-10s: %T
"
]
(
k
,
v
)
)
781
end
782
report_names
(
"
"
)
783
report_names
(
"
registered fonts : %i
"
,
statistics
.
fonts
)
784
report_names
(
"
read files : %i
"
,
statistics
.
readfiles
)
785
report_names
(
"
skipped files : %i
"
,
statistics
.
skippedfiles
)
786
report_names
(
"
duplicate files : %i
"
,
statistics
.
duplicatefiles
)
787
if
runtime
then
788
report_names
(
"
total scan time : %0.3f seconds
"
,
runtime
)
789
end
790
end
791
end
792 793
local
function
collecthashes
(
)
794
local
data
=
names
.
data
795
local
mappings
=
data
.
mappings
796
local
fallbacks
=
data
.
fallbacks
797
local
specifications
=
data
.
specifications
798
local
nofmappings
=
0
799
local
noffallbacks
=
0
800
if
specifications
then
801
-- maybe multiple passes (for the compatible and cffnames so that they have less preference)
802
local
conflicts
=
setmetatableindex
(
"
table
"
)
803
for
index
=
1
,
#
specifications
do
804
local
specification
=
specifications
[
index
]
805
local
format
=
specification
.
format
806
local
fullname
=
specification
.
fullname
807
local
fontname
=
specification
.
fontname
808
-- local rawname = specification.rawname
809
-- local compatiblename = specification.compatiblename
810
-- local cfffullname = specification.cfffullname
811
local
familyname
=
specification
.
familyname
or
specification
.
family
812
local
subfamilyname
=
specification
.
subfamilyname
813
local
subfamily
=
specification
.
subfamily
814
local
weight
=
specification
.
weight
815
local
mapping
=
mappings
[
format
]
816
local
fallback
=
fallbacks
[
format
]
817
local
instancenames
=
specification
.
instancenames
818
if
fullname
and
not
mapping
[
fullname
]
then
819
mapping
[
fullname
]
=
index
820
nofmappings
=
nofmappings
+
1
821
end
822
if
fontname
and
not
mapping
[
fontname
]
then
823
mapping
[
fontname
]
=
index
824
nofmappings
=
nofmappings
+
1
825
end
826
if
instancenames
then
827
for
i
=
1
,
#
instancenames
do
828
local
instance
=
fullname
.
.
instancenames
[
i
]
829
mapping
[
instance
]
=
index
830
nofmappings
=
nofmappings
+
1
831
end
832
end
833
-- if compatiblename and not mapping[compatiblename] then
834
-- mapping[compatiblename] = index
835
-- nofmappings = nofmappings + 1
836
-- end
837
-- if cfffullname and not mapping[cfffullname] then
838
-- mapping[cfffullname] = index
839
-- nofmappings = nofmappings + 1
840
-- end
841
if
familyname
then
842
if
weight
and
weight
~
=
sub
(
familyname
,
#
familyname
-
#
weight
+
1
,
#
familyname
)
then
843
local
madename
=
familyname
.
.
weight
844
if
not
mapping
[
madename
]
and
not
fallback
[
madename
]
then
845
fallback
[
madename
]
=
index
846
noffallbacks
=
noffallbacks
+
1
847
end
848
end
849
if
subfamily
and
subfamily
~
=
sub
(
familyname
,
#
familyname
-
#
subfamily
+
1
,
#
familyname
)
then
850
local
extraname
=
familyname
.
.
subfamily
851
if
not
mapping
[
extraname
]
and
not
fallback
[
extraname
]
then
852
fallback
[
extraname
]
=
index
853
noffallbacks
=
noffallbacks
+
1
854
end
855
end
856
if
subfamilyname
and
subfamilyname
~
=
sub
(
familyname
,
#
familyname
-
#
subfamilyname
+
1
,
#
familyname
)
then
857
local
extraname
=
familyname
.
.
subfamilyname
858
if
not
mapping
[
extraname
]
and
not
fallback
[
extraname
]
then
859
fallback
[
extraname
]
=
index
860
noffallbacks
=
noffallbacks
+
1
861
end
862
end
863
-- dangerous ... first match takes slot
864
if
not
mapping
[
familyname
]
and
not
fallback
[
familyname
]
then
865
fallback
[
familyname
]
=
index
866
noffallbacks
=
noffallbacks
+
1
867
end
868
local
conflict
=
conflicts
[
format
]
869
conflict
[
familyname
]
=
(
conflict
[
familyname
]
or
0
)
+
1
870
end
871
end
872
for
format
,
conflict
in
next
,
conflicts
do
873
local
fallback
=
fallbacks
[
format
]
874
for
familyname
,
n
in
next
,
conflict
do
875
if
n
>
1
then
876
fallback
[
familyname
]
=
nil
877
noffallbacks
=
noffallbacks
-
n
878
end
879
end
880
end
881
end
882
return
nofmappings
,
noffallbacks
883
end
884 885
local
function
collectfamilies
(
)
886
local
data
=
names
.
data
887
local
specifications
=
data
.
specifications
888
local
families
=
data
.
families
889
for
index
=
1
,
#
specifications
do
890
local
familyname
=
specifications
[
index
]
.
familyname
891
local
family
=
families
[
familyname
]
892
if
not
family
then
893
families
[
familyname
]
=
{
index
}
894
else
895
family
[
#
family
+
1
]
=
index
896
end
897
end
898
end
899 900
local
function
checkduplicate
(
where
)
-- fails on "Romantik" but that's a border case anyway
901
local
data
=
names
.
data
902
local
mapping
=
data
[
where
]
903
local
specifications
=
data
.
specifications
904
local
loaded
=
{
}
905
if
specifications
and
mapping
then
906
-- was: for _, m in sortedhash(mapping) do
907
local
order
=
filters
.
list
908
for
i
=
1
,
#
order
do
909
local
m
=
mapping
[
order
[
i
]
]
910
for
k
,
v
in
sortedhash
(
m
)
do
911
local
s
=
specifications
[
v
]
912
local
hash
=
formatters
[
"
%s-%s-%s-%s-%s
"
]
(
s
.
familyname
,
s
.
weight
or
"
*
"
,
s
.
style
or
"
*
"
,
s
.
width
or
"
*
"
,
s
.
variant
or
"
*
"
)
913
local
h
=
loaded
[
hash
]
914
if
h
then
915
local
ok
=
true
916
local
fn
=
s
.
filename
917
for
i
=
1
,
#
h
do
918
if
h
[
i
]
=
=
fn
then
919
ok
=
false
920
break
921
end
922
end
923
if
ok
then
924
h
[
#
h
+
1
]
=
fn
925
end
926
else
927
loaded
[
hash
]
=
{
s
.
filename
}
928
end
929
end
930
end
931
end
932
local
n
=
0
933
for
k
,
v
in
sortedhash
(
loaded
)
do
934
local
nv
=
#
v
935
if
nv
>
1
then
936
if
trace_warnings
then
937
report_names
(
"
lookup %a clashes with %a
"
,
k
,
v
)
938
end
939
n
=
n
+
nv
940
end
941
end
942
report_names
(
"
%a double lookups in %a
"
,
n
,
where
)
943
end
944 945
local
function
checkduplicates
(
)
946
checkduplicate
(
"
mappings
"
)
947
checkduplicate
(
"
fallbacks
"
)
948
end
949 950
local
function
sorthashes
(
)
951
local
data
=
names
.
data
952
local
list
=
filters
.
list
953
local
mappings
=
data
.
mappings
954
local
fallbacks
=
data
.
fallbacks
955
local
sorted_mappings
=
{
}
956
local
sorted_fallbacks
=
{
}
957
data
.
sorted_mappings
=
sorted_mappings
958
data
.
sorted_fallbacks
=
sorted_fallbacks
959
for
i
=
1
,
#
list
do
960
local
l
=
list
[
i
]
961
sorted_mappings
[
l
]
=
table
.
keys
(
mappings
[
l
]
)
962
sorted_fallbacks
[
l
]
=
table
.
keys
(
fallbacks
[
l
]
)
963
sort
(
sorted_mappings
[
l
]
,
sorter
)
964
sort
(
sorted_fallbacks
[
l
]
,
sorter
)
965
end
966
local
sorted_families
=
table
.
keys
(
data
.
families
)
967
data
.
sorted_families
=
sorted_families
968
sort
(
sorted_families
,
sorter
)
969
end
970 971
local
function
unpackreferences
(
)
972
local
data
=
names
.
data
973
local
specifications
=
data
.
specifications
974
if
specifications
then
975
for
k
,
v
in
sortedhash
(
data
.
families
)
do
976
for
i
=
1
,
#
v
do
977
v
[
i
]
=
specifications
[
v
[
i
]
]
978
end
979
end
980
local
mappings
=
data
.
mappings
981
if
mappings
then
982
for
_
,
m
in
sortedhash
(
mappings
)
do
983
for
k
,
v
in
sortedhash
(
m
)
do
984
m
[
k
]
=
specifications
[
v
]
985
end
986
end
987
end
988
local
fallbacks
=
data
.
fallbacks
989
if
fallbacks
then
990
for
_
,
f
in
sortedhash
(
fallbacks
)
do
991
for
k
,
v
in
sortedhash
(
f
)
do
992
f
[
k
]
=
specifications
[
v
]
993
end
994
end
995
end
996
end
997
end
998 999
local
function
analyzefiles
(
olddata
)
1000 1001
if
not
trace_warnings
then
1002
report_names
(
"
warnings are disabled (tracker 'fonts.warnings')
"
)
1003
end
1004 1005
local
data
=
names
.
data
1006
local
done
=
{
}
1007
local
totalnofread
=
0
1008
local
totalnofskipped
=
0
1009
local
totalnofduplicates
=
0
1010
local
nofread
=
0
1011
local
nofskipped
=
0
1012
local
nofduplicates
=
0
1013
local
skip_paths
=
filters
.
paths
1014
local
skip_names
=
filters
.
names
1015
local
specifications
=
data
.
specifications
1016
local
oldindices
=
olddata
and
olddata
.
indices
or
{
}
1017
local
oldspecifications
=
olddata
and
olddata
.
specifications
or
{
}
1018
local
oldrejected
=
olddata
and
olddata
.
rejected
or
{
}
1019
local
treatmentdata
=
treatments
.
data
or
{
}
-- when used outside context
1020
----- walked = setmetatableindex("number")
1021 1022
local
function
walk_tree
(
pathlist
,
suffix
,
identify
)
1023
if
pathlist
then
1024
for
i
=
1
,
#
pathlist
do
1025
local
path
=
pathlist
[
i
]
1026
path
=
cleanpath
(
path
.
.
"
/
"
)
1027
path
=
gsub
(
path
,
"
/+
"
,
"
/
"
)
1028
local
pattern
=
path
.
.
"
**.
"
.
.
suffix
-- ** forces recurse
1029
report_names
(
"
globbing path %a
"
,
pattern
)
1030
local
t
=
dir
.
glob
(
pattern
)
1031
sort
(
t
,
sorter
)
1032
for
j
=
1
,
#
t
do
1033
local
completename
=
t
[
j
]
1034
identify
(
completename
,
basename
(
completename
)
,
suffix
,
completename
)
1035
end
1036
-- walked[path] = walked[path] + #t
1037
end
1038
end
1039
end
1040 1041
local
function
identify
(
completename
,
name
,
suffix
,
storedname
)
1042
local
pathpart
,
basepart
=
splitbase
(
completename
)
1043
nofread
=
nofread
+
1
1044
local
treatment
=
treatmentdata
[
completename
]
or
treatmentdata
[
basepart
]
1045
if
treatment
and
treatment
.
ignored
then
1046
if
trace_names
or
trace_rejections
then
1047
report_names
(
"
%s font %a is ignored, reason %a
"
,
suffix
,
completename
,
treatment
.
comment
or
"
unknown
"
)
1048
end
1049
nofskipped
=
nofskipped
+
1
1050
elseif
done
[
name
]
then
1051
if
lower
(
completename
)
~
=
lower
(
done
[
name
]
)
then
1052
-- already done (avoid otf afm clash)
1053
if
trace_names
or
trace_rejections
then
1054
report_names
(
"
%s font %a already done as %a
"
,
suffix
,
completename
,
done
[
name
]
)
1055
end
1056
nofduplicates
=
nofduplicates
+
1
1057
nofskipped
=
nofskipped
+
1
1058
end
1059
elseif
not
exists
(
completename
)
then
1060
-- weird error
1061
if
trace_names
or
trace_rejections
then
1062
report_names
(
"
%s font %a does not really exist
"
,
suffix
,
completename
)
1063
end
1064
nofskipped
=
nofskipped
+
1
1065
elseif
not
is_qualified_path
(
completename
)
and
findfile
(
completename
,
suffix
)
=
=
"
"
then
1066
-- not locatable by backend anyway
1067
if
trace_names
or
trace_rejections
then
1068
report_names
(
"
%s font %a cannot be found by backend
"
,
suffix
,
completename
)
1069
end
1070
nofskipped
=
nofskipped
+
1
1071
else
1072
if
#
skip_paths
>
0
then
1073
for
i
=
1
,
#
skip_paths
do
1074
if
find
(
pathpart
,
skip_paths
[
i
]
)
then
1075
if
trace_names
or
trace_rejections
then
1076
report_names
(
"
rejecting path of %s font %a
"
,
suffix
,
completename
)
1077
end
1078
nofskipped
=
nofskipped
+
1
1079
return
1080
end
1081
end
1082
end
1083
if
#
skip_names
>
0
then
1084
for
i
=
1
,
#
skip_paths
do
1085
if
find
(
basepart
,
skip_names
[
i
]
)
then
1086
done
[
name
]
=
true
1087
if
trace_names
or
trace_rejections
then
1088
report_names
(
"
rejecting name of %s font %a
"
,
suffix
,
completename
)
1089
end
1090
nofskipped
=
nofskipped
+
1
1091
return
1092
end
1093
end
1094
end
1095
if
trace_names
then
1096
report_names
(
"
identifying %s font %a
"
,
suffix
,
completename
)
1097
end
1098
-- needs checking with ttc / ttx : date not updated ?
1099
local
result
=
nil
1100
local
modification
=
modificationtime
(
completename
)
1101
if
olddata
and
modification
and
modification
>
0
then
1102
local
oldindex
=
oldindices
[
storedname
]
-- index into specifications
1103
if
oldindex
then
1104
local
oldspecification
=
oldspecifications
[
oldindex
]
1105
if
oldspecification
and
oldspecification
.
filename
=
=
storedname
then
-- double check for out of sync
1106
local
oldmodification
=
oldspecification
.
modification
1107
if
oldmodification
=
=
modification
then
1108
result
=
oldspecification
1109
specifications
[
#
specifications
+
1
]
=
result
1110
else
1111
-- ??
1112
end
1113
else
1114
-- ??
1115
end
1116
elseif
oldrejected
[
storedname
]
=
=
modification
then
1117
result
=
false
1118
end
1119
end
1120
if
result
=
=
nil
then
1121
local
lsuffix
=
lower
(
suffix
)
1122
local
result
,
message
=
filters
[
lsuffix
]
(
completename
)
1123
if
result
then
1124
if
#
result
>
0
then
1125
for
r
=
1
,
#
result
do
1126
check_name
(
data
,
result
[
r
]
,
storedname
,
modification
,
suffix
,
r
)
-- subfonts start at zero
1127
end
1128
else
1129
check_name
(
data
,
result
,
storedname
,
modification
,
suffix
)
1130
end
1131
if
trace_warnings
and
message
and
message
~
=
"
"
then
1132
report_names
(
"
warning when identifying %s font %a, %s
"
,
suffix
,
completename
,
message
)
1133
end
1134
elseif
trace_warnings
then
1135
nofskipped
=
nofskipped
+
1
1136
report_names
(
"
error when identifying %s font %a, %s
"
,
suffix
,
completename
,
message
or
"
unknown
"
)
1137
end
1138
end
1139
done
[
name
]
=
completename
1140
end
1141
logs
.
flush
(
)
-- a bit overkill for each font, maybe not needed here
1142
end
1143 1144
local
function
traverse
(
what
,
method
)
1145
local
list
=
filters
.
list
1146
for
n
=
1
,
#
list
do
1147
local
suffix
=
list
[
n
]
1148
local
t
=
os
.
gettimeofday
(
)
-- use elapser
1149
nofread
,
nofskipped
,
nofduplicates
=
0
,
0
,
0
1150
suffix
=
lower
(
suffix
)
1151
report_names
(
"
identifying %s font files with suffix %a
"
,
what
,
suffix
)
1152
method
(
suffix
)
1153
suffix
=
upper
(
suffix
)
1154
report_names
(
"
identifying %s font files with suffix %a
"
,
what
,
suffix
)
1155
method
(
suffix
)
1156
totalnofread
,
totalnofskipped
,
totalnofduplicates
=
totalnofread
+
nofread
,
totalnofskipped
+
nofskipped
,
totalnofduplicates
+
nofduplicates
1157
local
elapsed
=
os
.
gettimeofday
(
)
-
t
1158
report_names
(
"
%s %s files identified, %s skipped, %s duplicates, %s hash entries added, runtime %0.3f seconds
"
,
nofread
,
what
,
nofskipped
,
nofduplicates
,
nofread
-
nofskipped
,
elapsed
)
1159
end
1160
logs
.
flush
(
)
1161
end
1162 1163
-- problem .. this will not take care of duplicates
1164 1165
local
function
withtree
(
suffix
)
1166
resolvers
.
dowithfilesintree
(
"
.*%.
"
.
.
suffix
.
.
"
$
"
,
function
(
method
,
root
,
path
,
name
)
1167
if
method
=
=
"
file
"
or
method
=
=
"
tree
"
then
1168
local
completename
=
root
.
.
"
/
"
.
.
path
.
.
"
/
"
.
.
name
1169
completename
=
resolveprefix
(
completename
)
-- no shortcut
1170
identify
(
completename
,
name
,
suffix
,
name
)
1171
return
true
1172
end
1173
end
,
function
(
blobtype
,
blobpath
,
pattern
)
1174
blobpath
=
resolveprefix
(
blobpath
)
-- no shortcut
1175
report_names
(
"
scanning path %a for %s files
"
,
blobpath
,
suffix
)
1176
end
,
function
(
blobtype
,
blobpath
,
pattern
,
total
,
checked
,
done
)
1177
blobpath
=
resolveprefix
(
blobpath
)
-- no shortcut
1178
report_names
(
"
%s %s files checked, %s okay
"
,
checked
,
suffix
,
done
)
1179
end
)
1180
end
1181 1182
local
function
withlsr
(
suffix
)
-- all trees
1183
-- we do this only for a stupid names run, not used for context itself,
1184
-- using the vars is too clumsy so we just stick to a full scan instead
1185
local
pathlist
=
resolvers
.
splitpath
(
resolvers
.
showpath
(
"
ls-R
"
)
or
"
"
)
1186
walk_tree
(
pathlist
,
suffix
,
identify
)
1187
end
1188 1189
local
function
withsystem
(
suffix
)
-- OSFONTDIR cum suis
1190
walk_tree
(
names
.
getpaths
(
trace
)
,
suffix
,
identify
)
1191
end
1192 1193
traverse
(
"
tree
"
,
withtree
)
-- TEXTREE only
1194 1195
if
not
usesystemfonts
then
1196
report_names
(
"
ignoring system fonts
"
)
1197
elseif
texconfig
.
kpse_init
then
1198
traverse
(
"
lsr
"
,
withlsr
)
1199
else
1200
traverse
(
"
system
"
,
withsystem
)
1201
end
1202 1203
data
.
statistics
.
readfiles
=
totalnofread
1204
data
.
statistics
.
skippedfiles
=
totalnofskipped
1205
data
.
statistics
.
duplicatefiles
=
totalnofduplicates
1206 1207
-- for k, v in sortedhash(walked) do
1208
-- report_names("%s : %i",k,v)
1209
-- end
1210 1211
end
1212 1213
local
function
addfilenames
(
)
1214
local
data
=
names
.
data
1215
local
specifications
=
data
.
specifications
1216
local
indices
=
{
}
1217
local
files
=
{
}
1218
for
i
=
1
,
#
specifications
do
1219
local
fullname
=
specifications
[
i
]
.
filename
1220
files
[
cleanfilename
(
fullname
)
]
=
fullname
1221
indices
[
fullname
]
=
i
1222
end
1223
data
.
files
=
files
1224
data
.
indices
=
indices
1225
end
1226 1227
local
function
rejectclashes
(
)
-- just to be sure, so no explicit afm will be found then
1228
local
specifications
=
names
.
data
.
specifications
1229
local
used
=
{
}
1230
local
okay
=
{
}
1231
local
rejected
=
{
}
-- only keep modification
1232
local
o
=
0
1233
for
i
=
1
,
#
specifications
do
1234
local
s
=
specifications
[
i
]
1235
local
f
=
s
.
fontname
1236
if
f
then
1237
local
fnd
=
used
[
f
]
1238
local
fnm
=
s
.
filename
1239
if
fnd
then
1240
if
trace_warnings
then
1241
report_names
(
"
fontname %a clashes, %a rejected in favor of %a
"
,
f
,
fnm
,
fnd
)
1242
end
1243
rejected
[
f
]
=
s
.
modification
1244
else
1245
used
[
f
]
=
fnm
1246
o
=
o
+
1
1247
okay
[
o
]
=
s
1248
end
1249
else
1250
o
=
o
+
1
1251
okay
[
o
]
=
s
1252
end
1253
end
1254
local
d
=
#
specifications
-
#
okay
1255
if
d
>
0
then
1256
report_names
(
"
%s files rejected due to clashes
"
,
d
)
1257
end
1258
names
.
data
.
specifications
=
okay
1259
names
.
data
.
rejected
=
rejected
1260
end
1261 1262
local
function
resetdata
(
)
1263
local
mappings
=
{
}
1264
local
fallbacks
=
{
}
1265
for
_
,
k
in
next
,
filters
.
list
do
1266
mappings
[
k
]
=
{
}
1267
fallbacks
[
k
]
=
{
}
1268
end
1269
names
.
data
=
{
1270
version
=
names
.
version
,
1271
mappings
=
mappings
,
1272
fallbacks
=
fallbacks
,
1273
specifications
=
{
}
,
1274
families
=
{
}
,
1275
statistics
=
{
}
,
1276
names
=
{
}
,
1277
indices
=
{
}
,
1278
rejected
=
{
}
,
1279
datastate
=
resolvers
.
datastate
(
)
,
1280
}
1281
end
1282 1283
function
names
.
identify
(
force
)
1284
local
starttime
=
os
.
gettimeofday
(
)
-- use elapser
1285
resetdata
(
)
1286
analyzefiles
(
not
force
and
names
.
readdata
(
names
.
basename
)
)
1287
rejectclashes
(
)
1288
collectfamilies
(
)
1289
cleanupkeywords
(
)
1290
collecthashes
(
)
1291
checkduplicates
(
)
1292
addfilenames
(
)
1293
-- sorthashes() -- will be resorted when saved
1294
collectstatistics
(
os
.
gettimeofday
(
)
-
starttime
)
1295
end
1296 1297
function
names
.
is_permitted
(
name
)
1298
return
containers
.
is_usable
(
names
.
cache
,
name
)
1299
end
1300
function
names
.
writedata
(
name
,
data
)
1301
containers
.
write
(
names
.
cache
,
name
,
data
)
1302
end
1303
function
names
.
readdata
(
name
)
1304
return
containers
.
read
(
names
.
cache
,
name
)
1305
end
1306 1307
function
names
.
load
(
reload
,
force
)
1308
if
not
names
.
loaded
then
1309
if
reload
then
1310
if
names
.
is_permitted
(
names
.
basename
)
then
1311
names
.
identify
(
force
)
1312
names
.
writedata
(
names
.
basename
,
names
.
data
)
1313
else
1314
report_names
(
"
unable to access database cache
"
)
1315
end
1316
names
.
saved
=
true
1317
end
1318
local
data
=
names
.
readdata
(
names
.
basename
)
1319
names
.
data
=
data
1320
if
not
names
.
saved
then
1321
if
not
data
or
not
next
(
data
)
or
not
data
.
specifications
or
not
next
(
data
.
specifications
)
then
1322
names
.
load
(
true
)
1323
end
1324
names
.
saved
=
true
1325
end
1326
if
not
data
then
1327
report_names
(
"
accessing the data table failed
"
)
1328
else
1329
unpackreferences
(
)
1330
sorthashes
(
)
1331
end
1332
names
.
loaded
=
true
1333
end
1334
end
1335 1336
local
function
list_them
(
mapping
,
sorted
,
pattern
,
t
,
all
)
1337
if
mapping
[
pattern
]
then
1338
t
[
pattern
]
=
mapping
[
pattern
]
1339
else
1340
for
k
=
1
,
#
sorted
do
1341
local
v
=
sorted
[
k
]
1342
if
not
t
[
v
]
and
find
(
v
,
pattern
)
then
1343
t
[
v
]
=
mapping
[
v
]
1344
if
not
all
then
1345
return
1346
end
1347
end
1348
end
1349
end
1350
end
1351 1352
function
names
.
list
(
pattern
,
reload
,
all
)
-- here?
1353
names
.
load
(
)
-- todo reload
1354
if
names
.
loaded
then
1355
local
t
=
{
}
1356
local
data
=
names
.
data
1357
if
data
then
1358
local
list
=
filters
.
list
1359
local
mappings
=
data
.
mappings
1360
local
sorted_mappings
=
data
.
sorted_mappings
1361
local
fallbacks
=
data
.
fallbacks
1362
local
sorted_fallbacks
=
data
.
sorted_fallbacks
1363
for
i
=
1
,
#
list
do
1364
local
format
=
list
[
i
]
1365
list_them
(
mappings
[
format
]
,
sorted_mappings
[
format
]
,
pattern
,
t
,
all
)
1366
if
next
(
t
)
and
not
all
then
1367
return
t
1368
end
1369
list_them
(
fallbacks
[
format
]
,
sorted_fallbacks
[
format
]
,
pattern
,
t
,
all
)
1370
if
next
(
t
)
and
not
all
then
1371
return
t
1372
end
1373
end
1374
end
1375
return
t
1376
end
1377
end
1378 1379
local
reloaded
=
false
1380 1381
local
function
is_reloaded
(
)
1382
if
not
reloaded
then
1383
local
data
=
names
.
data
1384
if
autoreload
then
1385
local
c_status
=
serialize
(
resolvers
.
datastate
(
)
)
1386
local
f_status
=
serialize
(
data
.
datastate
)
1387
if
c_status
=
=
f_status
then
1388
if
trace_names
then
1389
report_names
(
"
font database has matching configuration and file hashes
"
)
1390
end
1391
return
1392
else
1393
report_names
(
"
font database has mismatching configuration and file hashes
"
)
1394
end
1395
else
1396
report_names
(
"
font database is regenerated (controlled by directive 'fonts.autoreload')
"
)
1397
end
1398
names
.
loaded
=
false
1399
reloaded
=
true
1400
logs
.
flush
(
)
1401
names
.
load
(
true
)
1402
end
1403
end
1404 1405
--[[ldx-- 1406<p>The resolver also checks if the cached names are loaded. Being clever 1407here is for testing purposes only (it deals with names prefixed by an 1408encoding name).</p> 1409--ldx]]
--
1410 1411
local
function
fuzzy
(
mapping
,
sorted
,
name
,
sub
)
1412
local
condensed
=
gsub
(
name
,
"
[^%a%d]
"
,
"
"
)
1413
for
k
=
1
,
#
sorted
do
1414
local
v
=
sorted
[
k
]
1415
if
find
(
v
,
condensed
)
then
1416
return
mapping
[
v
]
,
v
1417
end
1418
end
1419
end
1420 1421
-- we could cache a lookup .. maybe some day ... (only when auto loaded!)
1422 1423
local
function
checkinstance
(
found
,
askedname
)
1424
local
instancenames
=
found
.
instancenames
1425
if
instancenames
then
1426
local
fullname
=
found
.
fullname
1427
for
i
=
1
,
#
instancenames
do
1428
local
instancename
=
instancenames
[
i
]
1429
if
fullname
.
.
instancename
=
=
askedname
then
1430
local
f
=
fastcopy
(
found
)
1431
f
.
instances
=
nil
1432
f
.
instance
=
instancename
1433
return
f
1434
end
1435
end
1436
end
1437
return
found
1438
end
1439 1440
local
function
foundname
(
name
,
sub
)
-- sub is not used currently
1441
local
data
=
names
.
data
1442
local
mappings
=
data
.
mappings
1443
local
sorted_mappings
=
data
.
sorted_mappings
1444
local
fallbacks
=
data
.
fallbacks
1445
local
sorted_fallbacks
=
data
.
sorted_fallbacks
1446
local
list
=
filters
.
list
1447
-- dilemma: we lookup in the order otf ttf ttc ... afm but now an otf fallback
1448
-- can come after an afm match ... well, one should provide nice names anyway
1449
-- and having two lists is not an option
1450
for
i
=
1
,
#
list
do
1451
local
l
=
list
[
i
]
1452
local
found
=
mappings
[
l
]
[
name
]
1453
if
found
then
1454
if
trace_names
then
1455
report_names
(
"
resolved via direct name match: %a
"
,
name
)
1456
end
1457
return
checkinstance
(
found
,
name
)
1458
end
1459
end
1460
for
i
=
1
,
#
list
do
1461
local
l
=
list
[
i
]
1462
local
found
,
fname
=
fuzzy
(
mappings
[
l
]
,
sorted_mappings
[
l
]
,
name
,
sub
)
1463
if
found
then
1464
if
trace_names
then
1465
report_names
(
"
resolved via fuzzy name match: %a onto %a
"
,
name
,
fname
)
1466
end
1467
return
checkinstance
(
found
,
name
)
1468
end
1469
end
1470
for
i
=
1
,
#
list
do
1471
local
l
=
list
[
i
]
1472
local
found
=
fallbacks
[
l
]
[
name
]
1473
if
found
then
1474
if
trace_names
then
1475
report_names
(
"
resolved via direct fallback match: %a
"
,
name
)
1476
end
1477
return
checkinstance
(
found
,
name
)
1478
end
1479
end
1480
for
i
=
1
,
#
list
do
1481
local
l
=
list
[
i
]
1482
local
found
,
fname
=
fuzzy
(
sorted_mappings
[
l
]
,
sorted_fallbacks
[
l
]
,
name
,
sub
)
1483
if
found
then
1484
if
trace_names
then
1485
report_names
(
"
resolved via fuzzy fallback match: %a onto %a
"
,
name
,
fname
)
1486
end
1487
return
checkinstance
(
found
,
name
)
1488
end
1489
end
1490
if
trace_names
then
1491
report_names
(
"
font with name %a cannot be found
"
,
name
)
1492
end
1493
end
1494 1495
function
names
.
resolvedspecification
(
askedname
,
sub
)
1496
if
askedname
and
askedname
~
=
"
"
and
names
.
enabled
then
1497
askedname
=
cleanname
(
askedname
)
1498
names
.
load
(
)
1499
local
found
=
foundname
(
askedname
,
sub
)
1500
if
not
found
and
is_reloaded
(
)
then
1501
found
=
foundname
(
askedname
,
sub
)
1502
end
1503
return
found
1504
end
1505
end
1506 1507
function
names
.
resolve
(
askedname
,
sub
)
1508
local
found
=
names
.
resolvedspecification
(
askedname
,
sub
)
1509
if
found
then
1510
return
found
.
filename
,
found
.
subfont
and
found
.
rawname
,
found
.
subfont
,
found
.
instance
1511
end
1512
end
1513 1514
-- function names.getfilename(askedname,suffix) -- last resort, strip funny chars
1515
-- names.load()
1516
-- local files = names.data.files
1517
-- askedname = files and files[cleanfilename(askedname,suffix)] or ""
1518
-- if askedname == "" then
1519
-- return ""
1520
-- else -- never entered
1521
-- return resolvers.findbinfile(askedname,suffix) or ""
1522
-- end
1523
-- end
1524 1525
local
runtimefiles
=
{
}
1526
local
runtimedone
=
false
1527 1528
local
function
addruntimepath
(
path
)
1529
names
.
load
(
)
1530
local
paths
=
type
(
path
)
=
=
"
table
"
and
path
or
{
path
}
1531
local
suffixes
=
tohash
(
filters
.
list
)
1532
for
i
=
1
,
#
paths
do
1533
local
path
=
resolveprefix
(
paths
[
i
]
)
1534
if
path
~
=
"
"
then
1535
local
list
=
dir
.
glob
(
path
.
.
"
/*
"
)
1536
for
i
=
1
,
#
list
do
1537
local
fullname
=
list
[
i
]
1538
local
suffix
=
lower
(
suffixonly
(
fullname
)
)
1539
if
suffixes
[
suffix
]
then
1540
local
c
=
cleanfilename
(
fullname
)
1541
runtimefiles
[
c
]
=
fullname
1542
if
trace_names
then
1543
report_names
(
"
adding runtime filename %a for %a
"
,
c
,
fullname
)
1544
end
1545
end
1546
end
1547
end
1548
end
1549
end
1550 1551
local
function
addruntimefiles
(
variable
)
1552
local
paths
=
variable
and
resolvers
.
expandedpathlistfromvariable
(
variable
)
1553
if
paths
and
#
paths
>
0
then
1554
addruntimepath
(
paths
)
1555
end
1556
end
1557 1558
names
.
addruntimepath
=
addruntimepath
1559
names
.
addruntimefiles
=
addruntimefiles
1560 1561
function
names
.
getfilename
(
askedname
,
suffix
)
-- last resort, strip funny chars
1562
if
not
runtimedone
then
1563
addruntimefiles
(
names
.
runtimefontsvariable
)
1564
runtimedone
=
true
1565
end
1566
local
cleanname
=
cleanfilename
(
askedname
,
suffix
)
1567
local
found
=
runtimefiles
[
cleanname
]
1568
if
found
then
1569
return
found
1570
end
1571
names
.
load
(
)
1572
local
files
=
names
.
data
.
files
1573
local
found
=
files
and
files
[
cleanname
]
or
"
"
1574
if
found
=
=
"
"
and
is_reloaded
(
)
then
1575
files
=
names
.
data
.
files
1576
found
=
files
and
files
[
cleanname
]
or
"
"
1577
end
1578
if
found
and
found
~
=
"
"
then
1579
return
resolvers
.
findbinfile
(
found
,
suffix
)
or
"
"
-- we still need to locate it
1580
end
1581
end
1582 1583
-- specified search
1584 1585
local
function
s_collect_weight_style_width_variant
(
found
,
done
,
all
,
weight
,
style
,
width
,
variant
,
family
)
1586
if
family
then
1587
for
i
=
1
,
#
family
do
1588
local
f
=
family
[
i
]
1589
if
f
and
weight
=
=
f
.
weight
and
style
=
=
f
.
style
and
width
=
=
f
.
width
and
variant
=
=
f
.
variant
then
1590
found
[
#
found
+
1
]
,
done
[
f
]
=
f
,
true
1591
if
not
all
then
return
end
1592
end
1593
end
1594
end
1595
end
1596
local
function
m_collect_weight_style_width_variant
(
found
,
done
,
all
,
weight
,
style
,
width
,
variant
,
families
,
sorted
,
strictname
)
1597
for
i
=
1
,
#
sorted
do
1598
local
k
=
sorted
[
i
]
1599
local
family
=
families
[
k
]
1600
for
i
=
1
,
#
family
do
1601
local
f
=
family
[
i
]
1602
if
not
done
[
f
]
and
weight
=
=
f
.
weight
and
style
=
=
f
.
style
and
width
=
=
f
.
width
and
variant
=
=
f
.
variant
and
find
(
f
.
fontname
,
strictname
)
then
1603
found
[
#
found
+
1
]
,
done
[
f
]
=
f
,
true
1604
if
not
all
then
return
end
1605
end
1606
end
1607
end
1608
end
1609 1610
local
function
s_collect_weight_style_width
(
found
,
done
,
all
,
weight
,
style
,
width
,
family
)
1611
if
family
then
1612
for
i
=
1
,
#
family
do
1613
local
f
=
family
[
i
]
1614
if
f
and
weight
=
=
f
.
weight
and
style
=
=
f
.
style
and
width
=
=
f
.
width
then
1615
found
[
#
found
+
1
]
,
done
[
f
]
=
f
,
true
1616
if
not
all
then
return
end
1617
end
1618
end
1619
end
1620
end
1621
local
function
m_collect_weight_style_width
(
found
,
done
,
all
,
weight
,
style
,
width
,
families
,
sorted
,
strictname
)
1622
for
i
=
1
,
#
sorted
do
1623
local
k
=
sorted
[
i
]
1624
local
family
=
families
[
k
]
1625
for
i
=
1
,
#
family
do
1626
local
f
=
family
[
i
]
1627
if
not
done
[
f
]
and
weight
=
=
f
.
weight
and
style
=
=
f
.
style
and
width
=
=
f
.
width
and
find
(
f
.
fontname
,
strictname
)
then
1628
found
[
#
found
+
1
]
,
done
[
f
]
=
f
,
true
1629
if
not
all
then
return
end
1630
end
1631
end
1632
end
1633
end
1634 1635
local
function
s_collect_weight_style
(
found
,
done
,
all
,
weight
,
style
,
family
)
1636
if
family
then
1637
for
i
=
1
,
#
family
do
local
f
=
family
[
i
]
1638
if
f
and
weight
=
=
f
.
weight
and
style
=
=
f
.
style
then
1639
found
[
#
found
+
1
]
,
done
[
f
]
=
f
,
true
1640
if
not
all
then
return
end
1641
end
1642
end
1643
end
1644
end
1645
local
function
m_collect_weight_style
(
found
,
done
,
all
,
weight
,
style
,
families
,
sorted
,
strictname
)
1646
for
i
=
1
,
#
sorted
do
1647
local
k
=
sorted
[
i
]
1648
local
family
=
families
[
k
]
1649
for
i
=
1
,
#
family
do
1650
local
f
=
family
[
i
]
1651
if
not
done
[
f
]
and
weight
=
=
f
.
weight
and
style
=
=
f
.
style
and
find
(
f
.
fontname
,
strictname
)
then
1652
found
[
#
found
+
1
]
,
done
[
f
]
=
f
,
true
1653
if
not
all
then
return
end
1654
end
1655
end
1656
end
1657
end
1658 1659
local
function
s_collect_style_width
(
found
,
done
,
all
,
style
,
width
,
family
)
1660
if
family
then
1661
for
i
=
1
,
#
family
do
local
f
=
family
[
i
]
1662
if
f
and
style
=
=
f
.
style
and
width
=
=
f
.
width
then
1663
found
[
#
found
+
1
]
,
done
[
f
]
=
f
,
true
1664
if
not
all
then
return
end
1665
end
1666
end
1667
end
1668
end
1669
local
function
m_collect_style_width
(
found
,
done
,
all
,
style
,
width
,
families
,
sorted
,
strictname
)
1670
for
i
=
1
,
#
sorted
do
1671
local
k
=
sorted
[
i
]
1672
local
family
=
families
[
k
]
1673
for
i
=
1
,
#
family
do
1674
local
f
=
family
[
i
]
1675
if
not
done
[
f
]
and
style
=
=
f
.
style
and
width
=
=
f
.
width
and
find
(
f
.
fontname
,
strictname
)
then
1676
found
[
#
found
+
1
]
,
done
[
f
]
=
f
,
true
1677
if
not
all
then
return
end
1678
end
1679
end
1680
end
1681
end
1682 1683
local
function
s_collect_weight
(
found
,
done
,
all
,
weight
,
family
)
1684
if
family
then
1685
for
i
=
1
,
#
family
do
local
f
=
family
[
i
]
1686
if
f
and
weight
=
=
f
.
weight
then
1687
found
[
#
found
+
1
]
,
done
[
f
]
=
f
,
true
1688
if
not
all
then
return
end
1689
end
1690
end
1691
end
1692
end
1693
local
function
m_collect_weight
(
found
,
done
,
all
,
weight
,
families
,
sorted
,
strictname
)
1694
for
i
=
1
,
#
sorted
do
1695
local
k
=
sorted
[
i
]
1696
local
family
=
families
[
k
]
1697
for
i
=
1
,
#
family
do
1698
local
f
=
family
[
i
]
1699
if
not
done
[
f
]
and
weight
=
=
f
.
weight
and
find
(
f
.
fontname
,
strictname
)
then
1700
found
[
#
found
+
1
]
,
done
[
f
]
=
f
,
true
1701
if
not
all
then
return
end
1702
end
1703
end
1704
end
1705
end
1706 1707
local
function
s_collect_style
(
found
,
done
,
all
,
style
,
family
)
1708
if
family
then
1709
for
i
=
1
,
#
family
do
local
f
=
family
[
i
]
1710
if
f
and
style
=
=
f
.
style
then
1711
found
[
#
found
+
1
]
,
done
[
f
]
=
f
,
true
1712
if
not
all
then
return
end
1713
end
1714
end
1715
end
1716
end
1717
local
function
m_collect_style
(
found
,
done
,
all
,
style
,
families
,
sorted
,
strictname
)
1718
for
i
=
1
,
#
sorted
do
1719
local
k
=
sorted
[
i
]
1720
local
family
=
families
[
k
]
1721
for
i
=
1
,
#
family
do
1722
local
f
=
family
[
i
]
1723
if
not
done
[
f
]
and
style
=
=
f
.
style
and
find
(
f
.
fontname
,
strictname
)
then
1724
found
[
#
found
+
1
]
,
done
[
f
]
=
f
,
true
1725
if
not
all
then
return
end
1726
end
1727
end
1728
end
1729
end
1730 1731
local
function
s_collect_width
(
found
,
done
,
all
,
width
,
family
)
1732
if
family
then
1733
for
i
=
1
,
#
family
do
local
f
=
family
[
i
]
1734
if
f
and
width
=
=
f
.
width
then
1735
found
[
#
found
+
1
]
,
done
[
f
]
=
f
,
true
1736
if
not
all
then
return
end
1737
end
1738
end
1739
end
1740
end
1741
local
function
m_collect_width
(
found
,
done
,
all
,
width
,
families
,
sorted
,
strictname
)
1742
for
i
=
1
,
#
sorted
do
1743
local
k
=
sorted
[
i
]
1744
local
family
=
families
[
k
]
1745
for
i
=
1
,
#
family
do
1746
local
f
=
family
[
i
]
1747
if
not
done
[
f
]
and
width
=
=
f
.
width
and
find
(
f
.
fontname
,
strictname
)
then
1748
found
[
#
found
+
1
]
,
done
[
f
]
=
f
,
true
1749
if
not
all
then
return
end
1750
end
1751
end
1752
end
1753
end
1754 1755
local
function
s_collect
(
found
,
done
,
all
,
family
)
1756
if
family
then
1757
for
i
=
1
,
#
family
do
local
f
=
family
[
i
]
1758
if
f
then
1759
found
[
#
found
+
1
]
,
done
[
f
]
=
f
,
true
1760
if
not
all
then
return
end
1761
end
1762
end
1763
end
1764
end
1765
local
function
m_collect
(
found
,
done
,
all
,
families
,
sorted
,
strictname
)
1766
for
i
=
1
,
#
sorted
do
1767
local
k
=
sorted
[
i
]
1768
local
family
=
families
[
k
]
1769
for
i
=
1
,
#
family
do
1770
local
f
=
family
[
i
]
1771
if
not
done
[
f
]
and
find
(
f
.
fontname
,
strictname
)
then
1772
found
[
#
found
+
1
]
,
done
[
f
]
=
f
,
true
1773
if
not
all
then
return
end
1774
end
1775
end
1776
end
1777
end
1778 1779
local
function
collect
(
stage
,
found
,
done
,
name
,
weight
,
style
,
width
,
variant
,
all
)
1780
local
data
=
names
.
data
1781
local
families
=
data
.
families
1782
local
sorted
=
data
.
sorted_families
1783
local
strictname
=
"
^
"
.
.
name
-- to be checked
1784
local
family
=
families
[
name
]
1785
if
trace_names
then
1786
report_names
(
"
resolving name %a, weight %a, style %a, width %a, variant %a
"
,
name
,
weight
,
style
,
width
,
variant
)
1787
end
1788
if
weight
and
weight
~
=
"
"
then
1789
if
style
and
style
~
=
"
"
then
1790
if
width
and
width
~
=
"
"
then
1791
if
variant
and
variant
~
=
"
"
then
1792
if
trace_names
then
1793
report_names
(
"
resolving stage %s, name %a, weight %a, style %a, width %a, variant %a
"
,
stage
,
name
,
weight
,
style
,
width
,
variant
)
1794
end
1795
s_collect_weight_style_width_variant
(
found
,
done
,
all
,
weight
,
style
,
width
,
variant
,
family
)
1796
m_collect_weight_style_width_variant
(
found
,
done
,
all
,
weight
,
style
,
width
,
variant
,
families
,
sorted
,
strictname
)
1797
else
1798
if
trace_names
then
1799
report_names
(
"
resolving stage %s, name %a, weight %a, style %a, width %a
"
,
stage
,
name
,
weight
,
style
,
width
)
1800
end
1801
s_collect_weight_style_width
(
found
,
done
,
all
,
weight
,
style
,
width
,
family
)
1802
m_collect_weight_style_width
(
found
,
done
,
all
,
weight
,
style
,
width
,
families
,
sorted
,
strictname
)
1803
end
1804
else
1805
if
trace_names
then
1806
report_names
(
"
resolving stage %s, name %a, weight %a, style %a
"
,
stage
,
name
,
weight
,
style
)
1807
end
1808
s_collect_weight_style
(
found
,
done
,
all
,
weight
,
style
,
family
)
1809
m_collect_weight_style
(
found
,
done
,
all
,
weight
,
style
,
families
,
sorted
,
strictname
)
1810
end
1811
else
1812
if
trace_names
then
1813
report_names
(
"
resolving stage %s, name %a, weight %a
"
,
stage
,
name
,
weight
)
1814
end
1815
s_collect_weight
(
found
,
done
,
all
,
weight
,
family
)
1816
m_collect_weight
(
found
,
done
,
all
,
weight
,
families
,
sorted
,
strictname
)
1817
end
1818
elseif
style
and
style
~
=
"
"
then
1819
if
width
and
width
~
=
"
"
then
1820
if
trace_names
then
1821
report_names
(
"
resolving stage %s, name %a, style %a, width %a
"
,
stage
,
name
,
style
,
width
)
1822
end
1823
s_collect_style_width
(
found
,
done
,
all
,
style
,
width
,
family
)
1824
m_collect_style_width
(
found
,
done
,
all
,
style
,
width
,
families
,
sorted
,
strictname
)
1825
else
1826
if
trace_names
then
1827
report_names
(
"
resolving stage %s, name %a, style %a
"
,
stage
,
name
,
style
)
1828
end
1829
s_collect_style
(
found
,
done
,
all
,
style
,
family
)
1830
m_collect_style
(
found
,
done
,
all
,
style
,
families
,
sorted
,
strictname
)
1831
end
1832
elseif
width
and
width
~
=
"
"
then
1833
if
trace_names
then
1834
report_names
(
"
resolving stage %s, name %a, width %a
"
,
stage
,
name
,
width
)
1835
end
1836
s_collect_width
(
found
,
done
,
all
,
width
,
family
)
1837
m_collect_width
(
found
,
done
,
all
,
width
,
families
,
sorted
,
strictname
)
1838
else
1839
if
trace_names
then
1840
report_names
(
"
resolving stage %s, name %a
"
,
stage
,
name
)
1841
end
1842
s_collect
(
found
,
done
,
all
,
family
)
1843
m_collect
(
found
,
done
,
all
,
families
,
sorted
,
strictname
)
1844
end
1845
end
1846 1847
local
function
heuristic
(
name
,
weight
,
style
,
width
,
variant
,
all
)
-- todo: fallbacks
1848
local
found
,
done
=
{
}
,
{
}
1849
--~ print(name,weight,style,width,variant)
1850
weight
,
style
,
width
,
variant
=
weight
or
"
normal
"
,
style
or
"
normal
"
,
width
or
"
normal
"
,
variant
or
"
normal
"
1851
name
=
cleanname
(
name
)
1852
collect
(
1
,
found
,
done
,
name
,
weight
,
style
,
width
,
variant
,
all
)
1853
-- still needed ?
1854
if
#
found
=
=
0
and
variant
~
=
"
normal
"
then
-- not weight
1855
variant
=
"
normal
"
1856
collect
(
4
,
found
,
done
,
name
,
weight
,
style
,
width
,
variant
,
all
)
1857
end
1858
if
#
found
=
=
0
and
width
~
=
"
normal
"
then
1859
width
=
"
normal
"
1860
collect
(
2
,
found
,
done
,
name
,
weight
,
style
,
width
,
variant
,
all
)
1861
end
1862
if
#
found
=
=
0
and
weight
~
=
"
normal
"
then
-- not style
1863
weight
=
"
normal
"
1864
collect
(
3
,
found
,
done
,
name
,
weight
,
style
,
width
,
variant
,
all
)
1865
end
1866
if
#
found
=
=
0
and
style
~
=
"
normal
"
then
-- not weight
1867
style
=
"
normal
"
1868
collect
(
4
,
found
,
done
,
name
,
weight
,
style
,
width
,
variant
,
all
)
1869
end
1870
--
1871
local
nf
=
#
found
1872
if
trace_names
then
1873
if
nf
then
1874
local
t
=
{
}
1875
for
i
=
1
,
nf
do
1876
t
[
i
]
=
formatters
[
"
%a
"
]
(
found
[
i
]
.
fontname
)
1877
end
1878
report_names
(
"
name %a resolved to %s instances: % t
"
,
name
,
nf
,
t
)
1879
else
1880
report_names
(
"
name %a unresolved
"
,
name
)
1881
end
1882
end
1883
if
all
then
1884
return
nf
>
0
and
found
1885
else
1886
return
found
[
1
]
1887
end
1888
end
1889 1890
function
names
.
specification
(
askedname
,
weight
,
style
,
width
,
variant
,
reload
,
all
)
1891
if
askedname
and
askedname
~
=
"
"
and
names
.
enabled
then
1892
askedname
=
cleanname
(
askedname
)
-- or cleanname
1893
names
.
load
(
reload
)
1894
local
found
=
heuristic
(
askedname
,
weight
,
style
,
width
,
variant
,
all
)
1895
if
not
found
and
is_reloaded
(
)
then
1896
found
=
heuristic
(
askedname
,
weight
,
style
,
width
,
variant
,
all
)
1897
if
not
filename
then
1898
found
=
foundname
(
askedname
)
-- old method
1899
end
1900
end
1901
return
found
1902
end
1903
end
1904 1905
function
names
.
collect
(
askedname
,
weight
,
style
,
width
,
variant
,
reload
,
all
)
1906
if
askedname
and
askedname
~
=
"
"
and
names
.
enabled
then
1907
askedname
=
cleanname
(
askedname
)
-- or cleanname
1908
names
.
load
(
reload
)
1909
local
list
=
heuristic
(
askedname
,
weight
,
style
,
width
,
variant
,
true
)
1910
if
not
list
or
#
list
=
=
0
and
is_reloaded
(
)
then
1911
list
=
heuristic
(
askedname
,
weight
,
style
,
width
,
variant
,
true
)
1912
end
1913
return
list
1914
end
1915
end
1916 1917
function
names
.
collectspec
(
askedname
,
reload
,
all
)
1918
local
name
,
weight
,
style
,
width
,
variant
=
names
.
splitspec
(
askedname
)
1919
return
names
.
collect
(
name
,
weight
,
style
,
width
,
variant
,
reload
,
all
)
1920
end
1921 1922
function
names
.
resolvespec
(
askedname
,
sub
)
-- redefined later
1923
local
found
=
names
.
specification
(
names
.
splitspec
(
askedname
)
)
1924
if
found
then
1925
return
found
.
filename
,
found
.
subfont
and
found
.
rawname
1926
end
1927
end
1928 1929
function
names
.
collectfiles
(
askedname
,
reload
)
-- no all
1930
if
askedname
and
askedname
~
=
"
"
and
names
.
enabled
then
1931
askedname
=
cleanname
(
askedname
)
-- or cleanname
1932
names
.
load
(
reload
)
1933
local
list
=
{
}
1934
local
specifications
=
names
.
data
.
specifications
1935
for
i
=
1
,
#
specifications
do
1936
local
s
=
specifications
[
i
]
1937
if
find
(
cleanname
(
basename
(
s
.
filename
)
)
,
askedname
)
then
1938
list
[
#
list
+
1
]
=
s
1939
end
1940
end
1941
return
list
1942
end
1943
end
1944 1945
-- todo:
1946
--
1947
-- blacklisted = {
1948
-- ["cmr10.ttf"] = "completely messed up",
1949
-- }
1950 1951
function
names
.
exists
(
name
)
1952
local
found
=
false
1953
local
list
=
filters
.
list
1954
for
k
=
1
,
#
list
do
1955
local
v
=
list
[
k
]
1956
found
=
(
findfile
(
name
,
v
)
or
"
"
)
~
=
"
"
1957
if
found
then
1958
return
found
1959
end
1960
end
1961
return
(
findfile
(
name
,
"
tfm
"
)
or
"
"
)
~
=
"
"
or
(
names
.
resolve
(
name
)
or
"
"
)
~
=
"
"
1962
end
1963 1964
local
lastlookups
,
lastpattern
=
{
}
,
"
"
1965 1966
-- function names.lookup(pattern,name,reload) -- todo: find
1967
-- if lastpattern ~= pattern then
1968
-- names.load(reload)
1969
-- local specifications = names.data.specifications
1970
-- local families = names.data.families
1971
-- local lookups = specifications
1972
-- if name then
1973
-- lookups = families[name]
1974
-- elseif not find(pattern,"=",1,true) then
1975
-- lookups = families[pattern]
1976
-- end
1977
-- if trace_names then
1978
-- report_names("starting with %s lookups for %a",#lookups,pattern)
1979
-- end
1980
-- if lookups then
1981
-- for key, value in gmatch(pattern,"([^=,]+)=([^=,]+)") do
1982
-- local t, n = { }, 0
1983
-- if find(value,"*",1,true) then
1984
-- value = topattern(value)
1985
-- for i=1,#lookups do
1986
-- local s = lookups[i]
1987
-- if find(s[key],value) then
1988
-- n = n + 1
1989
-- t[n] = lookups[i]
1990
-- end
1991
-- end
1992
-- else
1993
-- for i=1,#lookups do
1994
-- local s = lookups[i]
1995
-- if s[key] == value then
1996
-- n = n + 1
1997
-- t[n] = lookups[i]
1998
-- end
1999
-- end
2000
-- end
2001
-- if trace_names then
2002
-- report_names("%s matches for key %a with value %a",#t,key,value)
2003
-- end
2004
-- lookups = t
2005
-- end
2006
-- end
2007
-- lastpattern = pattern
2008
-- lastlookups = lookups or { }
2009
-- end
2010
-- return #lastlookups
2011
-- end
2012 2013
local
function
look_them_up
(
lookups
,
specification
)
2014
for
key
,
value
in
sortedhash
(
specification
)
do
2015
local
t
=
{
}
2016
local
n
=
0
2017
if
find
(
value
,
"
*
"
,
1
,
true
)
then
2018
value
=
topattern
(
value
)
2019
for
i
=
1
,
#
lookups
do
2020
local
s
=
lookups
[
i
]
2021
if
find
(
s
[
key
]
,
value
)
then
2022
n
=
n
+
1
2023
t
[
n
]
=
lookups
[
i
]
2024
end
2025
end
2026
else
2027
for
i
=
1
,
#
lookups
do
2028
local
s
=
lookups
[
i
]
2029
if
s
[
key
]
=
=
value
then
2030
n
=
n
+
1
2031
t
[
n
]
=
lookups
[
i
]
2032
end
2033
end
2034
end
2035
if
trace_names
then
2036
report_names
(
"
%s matches for key %a with value %a
"
,
#
t
,
key
,
value
)
2037
end
2038
lookups
=
t
2039
end
2040
return
lookups
2041
end
2042 2043
local
function
first_look
(
name
,
reload
)
2044
names
.
load
(
reload
)
2045
local
data
=
names
.
data
2046
local
specifications
=
data
.
specifications
2047
local
families
=
data
.
families
2048
if
name
then
2049
return
families
[
name
]
2050
else
2051
return
specifications
2052
end
2053
end
2054 2055
function
names
.
lookup
(
pattern
,
name
,
reload
)
-- todo: find
2056
names
.
load
(
reload
)
2057
local
data
=
names
.
data
2058
local
specifications
=
data
.
specifications
2059
local
families
=
data
.
families
2060
local
lookups
=
specifications
2061
if
name
then
2062
name
=
cleanname
(
name
)
2063
end
2064
if
type
(
pattern
)
=
=
"
table
"
then
2065
local
familyname
=
pattern
.
familyname
2066
if
familyname
then
2067
familyname
=
cleanname
(
familyname
)
2068
pattern
.
familyname
=
familyname
2069
end
2070
local
lookups
=
first_look
(
name
or
familyname
,
reload
)
2071
if
lookups
then
2072
if
trace_names
then
2073
report_names
(
"
starting with %s lookups for '%T'
"
,
#
lookups
,
pattern
)
2074
end
2075
lookups
=
look_them_up
(
lookups
,
pattern
)
2076
end
2077
lastpattern
=
false
2078
lastlookups
=
lookups
or
{
}
2079
elseif
lastpattern
~
=
pattern
then
2080
local
lookups
=
first_look
(
name
or
(
not
find
(
pattern
,
"
=
"
,
1
,
true
)
and
pattern
)
,
reload
)
2081
if
lookups
then
2082
if
trace_names
then
2083
report_names
(
"
starting with %s lookups for %a
"
,
#
lookups
,
pattern
)
2084
end
2085
local
specification
=
settings_to_hash
(
pattern
)
2086
local
familyname
=
specification
.
familyname
2087
if
familyname
then
2088
familyname
=
cleanname
(
familyname
)
2089
specification
.
familyname
=
familyname
2090
end
2091
lookups
=
look_them_up
(
lookups
,
specification
)
2092
end
2093
lastpattern
=
pattern
2094
lastlookups
=
lookups
or
{
}
2095
end
2096
return
#
lastlookups
2097
end
2098 2099
function
names
.
getlookupkey
(
key
,
n
)
2100
local
l
=
lastlookups
[
n
or
1
]
2101
return
(
l
and
l
[
key
]
)
or
"
"
2102
end
2103 2104
function
names
.
noflookups
(
)
2105
return
#
lastlookups
2106
end
2107 2108
function
names
.
getlookups
(
pattern
,
name
,
reload
)
2109
if
pattern
then
2110
names
.
lookup
(
pattern
,
name
,
reload
)
2111
end
2112
return
lastlookups
2113
end
2114 2115
-- The following is new ... watch the overload!
2116 2117
local
specifications
=
allocate
(
)
2118
names
.
specifications
=
specifications
2119 2120
-- files = {
2121
-- name = "antykwapoltawskiego",
2122
-- list = {
2123
-- ["AntPoltLtCond-Regular.otf"] = {
2124
-- -- name = "antykwapoltawskiego",
2125
-- style = "regular",
2126
-- weight = "light",
2127
-- width = "condensed",
2128
-- },
2129
-- },
2130
-- }
2131 2132
function
names
.
register
(
files
)
2133
if
files
then
2134
local
list
,
commonname
=
files
.
list
,
files
.
name
2135
if
list
then
2136
local
n
,
m
=
0
,
0
2137
for
filename
,
filespec
in
sortedhash
(
list
)
do
2138
local
name
=
lower
(
filespec
.
name
or
commonname
)
2139
if
name
and
name
~
=
"
"
then
2140
local
style
=
normalized_styles
[
lower
(
filespec
.
style
or
"
normal
"
)
]
2141
local
width
=
normalized_widths
[
lower
(
filespec
.
width
or
"
normal
"
)
]
2142
local
weight
=
normalized_weights
[
lower
(
filespec
.
weight
or
"
normal
"
)
]
2143
local
variant
=
normalized_variants
[
lower
(
filespec
.
variant
or
"
normal
"
)
]
2144
local
weights
=
specifications
[
name
]
if
not
weights
then
weights
=
{
}
specifications
[
name
]
=
weights
end
2145
local
styles
=
weights
[
weight
]
if
not
styles
then
styles
=
{
}
weights
[
weight
]
=
styles
end
2146
local
widths
=
styles
[
style
]
if
not
widths
then
widths
=
{
}
styles
[
style
]
=
widths
end
2147
local
variants
=
widths
[
width
]
if
not
variants
then
variants
=
{
}
widths
[
width
]
=
variants
end
2148
variants
[
variant
]
=
filename
2149
n
=
n
+
1
2150
else
2151
m
=
m
+
1
2152
end
2153
end
2154
if
trace_specifications
then
2155
report_names
(
"
%s filenames registered, %s filenames rejected
"
,
n
,
m
)
2156
end
2157
end
2158
end
2159
end
2160 2161
function
names
.
registered
(
name
,
weight
,
style
,
width
,
variant
)
2162
local
ok
=
specifications
[
name
]
2163
ok
=
ok
and
(
ok
[
(
weight
and
weight
~
=
"
"
and
weight
)
or
"
normal
"
]
or
ok
.
normal
)
2164
ok
=
ok
and
(
ok
[
(
style
and
style
~
=
"
"
and
style
)
or
"
normal
"
]
or
ok
.
normal
)
2165
ok
=
ok
and
(
ok
[
(
width
and
width
~
=
"
"
and
width
)
or
"
normal
"
]
or
ok
.
normal
)
2166
ok
=
ok
and
(
ok
[
(
variant
and
variant
~
=
"
"
and
variant
)
or
"
normal
"
]
or
ok
.
normal
)
2167
--
2168
-- todo: same fallbacks as with database
2169
--
2170
if
ok
then
2171
return
{
2172
filename
=
ok
,
2173
subname
=
"
"
,
2174
-- rawname = nil,
2175
}
2176
end
2177
end
2178 2179
function
names
.
resolvespec
(
askedname
,
sub
)
-- overloads previous definition
2180
local
name
,
weight
,
style
,
width
,
variant
=
names
.
splitspec
(
askedname
)
2181
if
trace_specifications
then
2182
report_names
(
"
resolving specification: %a to name=%s, weight=%s, style=%s, width=%s, variant=%s
"
,
askedname
,
name
,
weight
,
style
,
width
,
variant
)
2183
end
2184
local
found
=
names
.
registered
(
name
,
weight
,
style
,
width
,
variant
)
2185
if
found
and
found
.
filename
then
2186
if
trace_specifications
then
2187
report_names
(
"
resolved by registered names: %a to %s
"
,
askedname
,
found
.
filename
)
2188
end
2189
return
found
.
filename
,
found
.
subname
,
found
.
rawname
2190
else
2191
found
=
names
.
specification
(
name
,
weight
,
style
,
width
,
variant
)
2192
if
found
and
found
.
filename
then
2193
if
trace_specifications
then
2194
report_names
(
"
resolved by font database: %a to %s
"
,
askedname
,
found
.
filename
)
2195
end
2196
return
found
.
filename
,
found
.
subfont
and
found
.
rawname
2197
end
2198
end
2199
if
trace_specifications
then
2200
report_names
(
"
unresolved: %s
"
,
askedname
)
2201
end
2202
end
2203 2204
function
fonts
.
names
.
ignoredfile
(
filename
)
-- only supported in mkiv
2205
return
false
-- will be overloaded
2206
end
2207 2208
-- example made for luatex list (unlikely to be used):
2209
--
2210
-- local command = [[reg QUERY "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts"]]
2211
-- local pattern = ".-[\n\r]+%s+(.-)%s%(([^%)]+)%)%s+REG_SZ%s+(%S+)%s+"
2212
--
2213
-- local function getnamesfromregistry()
2214
-- local data = os.resultof(command)
2215
-- local list = { }
2216
-- for name, format, filename in string.gmatch(data,pattern) do
2217
-- list[name] = filename
2218
-- end
2219
-- return list
2220
-- end
2221