font-def.lua /size: 19 Kb    last modification: 2020-07-01 14:35
1
if
not
modules
then
modules
=
{
}
end
modules
[
'
font-def
'
]
=
{
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
-- We can overload some of the definers.functions so we don't local them.
10 11
local
lower
,
gsub
=
string
.
lower
,
string
.
gsub
12
local
tostring
,
next
=
tostring
,
next
13
local
lpegmatch
=
lpeg
.
match
14
local
suffixonly
,
removesuffix
,
basename
=
file
.
suffix
,
file
.
removesuffix
,
file
.
basename
15
local
formatters
=
string
.
formatters
16
local
sortedhash
,
sortedkeys
=
table
.
sortedhash
,
table
.
sortedkeys
17 18
local
allocate
=
utilities
.
storage
.
allocate
19 20
local
trace_defining
=
false
trackers
.
register
(
"
fonts.defining
"
,
function
(
v
)
trace_defining
=
v
end
)
21
local
directive_embedall
=
false
directives
.
register
(
"
fonts.embedall
"
,
function
(
v
)
directive_embedall
=
v
end
)
22 23
trackers
.
register
(
"
fonts.loading
"
,
"
fonts.defining
"
,
"
otf.loading
"
,
"
afm.loading
"
,
"
tfm.loading
"
)
24 25
local
report_defining
=
logs
.
reporter
(
"
fonts
"
,
"
defining
"
)
26 27
--[[ldx-- 28<p>Here we deal with defining fonts. We do so by intercepting the 29default loader that only handles <l n='tfm'/>.</p> 30--ldx]]
--
31 32
local
fonts
=
fonts
33
local
fontdata
=
fonts
.
hashes
.
identifiers
34
local
readers
=
fonts
.
readers
35
local
definers
=
fonts
.
definers
36
local
specifiers
=
fonts
.
specifiers
37
local
constructors
=
fonts
.
constructors
38
local
fontgoodies
=
fonts
.
goodies
39 40
readers
.
sequence
=
allocate
{
'
otf
'
,
'
ttf
'
,
'
afm
'
,
'
tfm
'
,
'
lua
'
}
-- dfont ttc
41 42
local
variants
=
allocate
(
)
43
specifiers
.
variants
=
variants
44 45
definers
.
methods
=
definers
.
methods
or
{
}
46 47
local
internalized
=
allocate
(
)
-- internal tex numbers (private)
48 49
local
loadedfonts
=
constructors
.
loadedfonts
50
local
designsizes
=
constructors
.
designsizes
51 52
-- not in generic (some day I'll make two defs, one for context, one for generic)
53 54
local
resolvefile
=
fontgoodies
and
fontgoodies
.
filenames
and
fontgoodies
.
filenames
.
resolve
or
function
(
s
)
return
s
end
55 56
--[[ldx-- 57<p>We hardly gain anything when we cache the final (pre scaled) 58<l n='tfm'/> table. But it can be handy for debugging, so we no 59longer carry this code along. Also, we now have quite some reference 60to other tables so we would end up with lots of catches.</p> 61--ldx]]
--
62 63
--[[ldx-- 64<p>We can prefix a font specification by <type>name:</type> or 65<type>file:</type>. The first case will result in a lookup in the 66synonym table.</p> 67 68<typing> 69[ name: | file: ] identifier [ separator [ specification ] ] 70</typing> 71 72<p>The following function split the font specification into components 73and prepares a table that will move along as we proceed.</p> 74--ldx]]
--
75 76
-- beware, we discard additional specs
77
--
78
-- method:name method:name(sub) method:name(sub)*spec method:name*spec
79
-- name name(sub) name(sub)*spec name*spec
80
-- name@spec*oeps
81 82
local
function
makespecification
(
specification
,
lookup
,
name
,
sub
,
method
,
detail
,
size
)
83
size
=
size
or
655360
84
if
not
lookup
or
lookup
=
=
"
"
then
85
lookup
=
definers
.
defaultlookup
86
end
87
if
trace_defining
then
88
report_defining
(
"
specification %a, lookup %a, name %a, sub %a, method %a, detail %a
"
,
89
specification
,
lookup
,
name
,
sub
,
method
,
detail
)
90
end
91
local
t
=
{
92
lookup
=
lookup
,
-- forced type
93
specification
=
specification
,
-- full specification
94
size
=
size
,
-- size in scaled points or -1000*n
95
name
=
name
,
-- font or filename
96
sub
=
sub
,
-- subfont (eg in ttc)
97
method
=
method
,
-- specification method
98
detail
=
detail
,
-- specification
99
resolved
=
"
"
,
-- resolved font name
100
forced
=
"
"
,
-- forced loader
101
features
=
{
}
,
-- preprocessed features
102
}
103
return
t
104
end
105 106
definers
.
makespecification
=
makespecification
107 108
if
context
then
109 110
local
splitter
,
splitspecifiers
=
nil
,
"
"
-- not so nice
111 112
local
P
,
C
,
S
,
Cc
,
Cs
=
lpeg
.
P
,
lpeg
.
C
,
lpeg
.
S
,
lpeg
.
Cc
,
lpeg
.
Cs
113 114
local
left
=
P
(
"
(
"
)
115
local
right
=
P
(
"
)
"
)
116
local
colon
=
P
(
"
:
"
)
117
local
space
=
P
(
"
"
)
118
local
lbrace
=
P
(
"
{
"
)
119
local
rbrace
=
P
(
"
}
"
)
120 121
definers
.
defaultlookup
=
"
file
"
122 123
local
prefixpattern
=
P
(
false
)
124 125
local
function
addspecifier
(
symbol
)
126
splitspecifiers
=
splitspecifiers
.
.
symbol
127
local
method
=
S
(
splitspecifiers
)
128
local
lookup
=
C
(
prefixpattern
)
*
colon
129
local
sub
=
left
*
C
(
P
(
1
-
left
-
right
-
method
)
^
1
)
*
right
130
local
specification
=
C
(
method
)
*
C
(
P
(
1
)
^
1
)
131
local
name
=
Cs
(
(
lbrace
/
"
"
)
*
(
1
-
rbrace
)
^
1
*
(
rbrace
/
"
"
)
+
(
1
-
sub
-
specification
)
^
1
)
132
splitter
=
P
(
(
lookup
+
Cc
(
"
"
)
)
*
name
*
(
sub
+
Cc
(
"
"
)
)
*
(
specification
+
Cc
(
"
"
)
)
)
133
end
134 135
local
function
addlookup
(
str
)
136
prefixpattern
=
prefixpattern
+
P
(
str
)
137
end
138 139
definers
.
addlookup
=
addlookup
140 141
addlookup
(
"
file
"
)
142
addlookup
(
"
name
"
)
143
addlookup
(
"
spec
"
)
144 145
local
function
getspecification
(
str
)
146
return
lpegmatch
(
splitter
,
str
or
"
"
)
-- weird catch
147
end
148 149
definers
.
getspecification
=
getspecification
150 151
function
definers
.
registersplit
(
symbol
,
action
,
verbosename
)
152
addspecifier
(
symbol
)
153
variants
[
symbol
]
=
action
154
if
verbosename
then
155
variants
[
verbosename
]
=
action
156
end
157
end
158 159
function
definers
.
analyze
(
specification
,
size
)
160
-- can be optimized with locals
161
local
lookup
,
name
,
sub
,
method
,
detail
=
getspecification
(
specification
or
"
"
)
162
return
makespecification
(
specification
,
lookup
,
name
,
sub
,
method
,
detail
,
size
)
163
end
164 165
end
166 167
--[[ldx-- 168<p>We can resolve the filename using the next function:</p> 169--ldx]]
--
170 171
definers
.
resolvers
=
definers
.
resolvers
or
{
}
172
local
resolvers
=
definers
.
resolvers
173 174
-- todo: reporter
175 176
function
resolvers
.
file
(
specification
)
177
local
name
=
resolvefile
(
specification
.
name
)
-- catch for renames
178
local
suffix
=
lower
(
suffixonly
(
name
)
)
179
if
fonts
.
formats
[
suffix
]
then
180
specification
.
forced
=
suffix
181
specification
.
forcedname
=
name
182
specification
.
name
=
removesuffix
(
name
)
183
else
184
specification
.
name
=
name
-- can be resolved
185
end
186
end
187 188
function
resolvers
.
name
(
specification
)
189
local
resolve
=
fonts
.
names
.
resolve
190
if
resolve
then
191
local
resolved
,
sub
,
subindex
,
instance
=
resolve
(
specification
.
name
,
specification
.
sub
,
specification
)
-- we pass specification for overloaded versions
192
if
resolved
then
193
specification
.
resolved
=
resolved
194
specification
.
sub
=
sub
195
specification
.
subindex
=
subindex
196
-- new, needed for experiments
197
if
instance
then
198
specification
.
instance
=
instance
199
local
features
=
specification
.
features
200
if
not
features
then
201
features
=
{
}
202
specification
.
features
=
features
203
end
204
local
normal
=
features
.
normal
205
if
not
normal
then
206
normal
=
{
}
207
features
.
normal
=
normal
208
end
209
normal
.
instance
=
instance
210
end
211
--
212
local
suffix
=
lower
(
suffixonly
(
resolved
)
)
213
if
fonts
.
formats
[
suffix
]
then
214
specification
.
forced
=
suffix
215
specification
.
forcedname
=
resolved
216
specification
.
name
=
removesuffix
(
resolved
)
217
else
218
specification
.
name
=
resolved
219
end
220
end
221
else
222
resolvers
.
file
(
specification
)
223
end
224
end
225 226
function
resolvers
.
spec
(
specification
)
227
local
resolvespec
=
fonts
.
names
.
resolvespec
228
if
resolvespec
then
229
local
resolved
,
sub
,
subindex
=
resolvespec
(
specification
.
name
,
specification
.
sub
,
specification
)
-- we pass specification for overloaded versions
230
if
resolved
then
231
specification
.
resolved
=
resolved
232
specification
.
sub
=
sub
233
specification
.
subindex
=
subindex
234
specification
.
forced
=
lower
(
suffixonly
(
resolved
)
)
235
specification
.
forcedname
=
resolved
236
specification
.
name
=
removesuffix
(
resolved
)
237
end
238
else
239
resolvers
.
name
(
specification
)
240
end
241
end
242 243
function
definers
.
resolve
(
specification
)
244
if
not
specification
.
resolved
or
specification
.
resolved
=
=
"
"
then
-- resolved itself not per se in mapping hash
245
local
r
=
resolvers
[
specification
.
lookup
]
246
if
r
then
247
r
(
specification
)
248
end
249
end
250
if
specification
.
forced
=
=
"
"
then
251
specification
.
forced
=
nil
252
specification
.
forcedname
=
nil
253
end
254
specification
.
hash
=
lower
(
specification
.
name
.
.
'
@
'
.
.
constructors
.
hashfeatures
(
specification
)
)
255
if
specification
.
sub
and
specification
.
sub
~
=
"
"
then
256
specification
.
hash
=
specification
.
sub
.
.
'
@
'
.
.
specification
.
hash
257
end
258
return
specification
259
end
260 261
--[[ldx-- 262<p>The main read function either uses a forced reader (as determined by 263a lookup) or tries to resolve the name using the list of readers.</p> 264 265<p>We need to cache when possible. We do cache raw tfm data (from <l 266n='tfm'/>, <l n='afm'/> or <l n='otf'/>). After that we can cache based 267on specificstion (name) and size, that is, <l n='tex'/> only needs a number 268for an already loaded fonts. However, it may make sense to cache fonts 269before they're scaled as well (store <l n='tfm'/>'s with applied methods 270and features). However, there may be a relation between the size and 271features (esp in virtual fonts) so let's not do that now.</p> 272 273<p>Watch out, here we do load a font, but we don't prepare the 274specification yet.</p> 275--ldx]]
--
276 277
-- very experimental:
278 279
function
definers
.
applypostprocessors
(
tfmdata
)
280
local
postprocessors
=
tfmdata
.
postprocessors
281
if
postprocessors
then
282
local
properties
=
tfmdata
.
properties
283
for
i
=
1
,
#
postprocessors
do
284
local
extrahash
=
postprocessors
[
i
]
(
tfmdata
)
-- after scaling etc
285
if
type
(
extrahash
)
=
=
"
string
"
and
extrahash
~
=
"
"
then
286
-- e.g. a reencoding needs this
287
extrahash
=
gsub
(
lower
(
extrahash
)
,
"
[^a-z]
"
,
"
-
"
)
288
properties
.
fullname
=
formatters
[
"
%s-%s
"
]
(
properties
.
fullname
,
extrahash
)
289
end
290
end
291
end
292
return
tfmdata
293
end
294 295
-- function definers.applypostprocessors(tfmdata)
296
-- return tfmdata
297
-- end
298 299
local
function
checkembedding
(
tfmdata
)
300
local
properties
=
tfmdata
.
properties
301
local
embedding
302
if
directive_embedall
then
303
embedding
=
"
full
"
304
elseif
properties
and
properties
.
filename
and
constructors
.
dontembed
[
properties
.
filename
]
then
305
embedding
=
"
no
"
306
else
307
embedding
=
"
subset
"
308
end
309
if
properties
then
310
properties
.
embedding
=
embedding
311
else
312
tfmdata
.
properties
=
{
embedding
=
embedding
}
313
end
314
tfmdata
.
embedding
=
embedding
315
end
316 317
local
function
checkfeatures
(
tfmdata
)
318
local
resources
=
tfmdata
.
resources
319
local
shared
=
tfmdata
.
shared
320
if
resources
and
shared
then
321
local
features
=
resources
.
features
322
local
usedfeatures
=
shared
.
features
323
if
features
and
usedfeatures
then
324
local
usedlanguage
=
usedfeatures
.
language
or
"
dflt
"
325
local
usedscript
=
usedfeatures
.
script
or
"
dflt
"
326
local
function
check
(
what
)
327
if
what
then
328
local
foundlanguages
=
{
}
329
for
feature
,
scripts
in
next
,
what
do
330
if
usedscript
=
=
"
auto
"
or
scripts
[
"
*
"
]
then
331
-- ok
332
elseif
not
scripts
[
usedscript
]
then
333
-- report_defining("font %!font:name!, feature %a, no script %a",
334
-- tfmdata,feature,usedscript)
335
else
336
for
script
,
languages
in
next
,
scripts
do
337
if
languages
[
"
*
"
]
then
338
-- ok
339
elseif
context
and
not
languages
[
usedlanguage
]
then
340
report_defining
(
"
font %!font:name!, feature %a, script %a, no language %a
"
,
341
tfmdata
,
feature
,
script
,
usedlanguage
)
342
end
343
end
344
end
345
for
script
,
languages
in
next
,
scripts
do
346
for
language
in
next
,
languages
do
347
foundlanguages
[
language
]
=
true
348
end
349
end
350
end
351
if
false
then
352
foundlanguages
[
"
*
"
]
=
nil
353
foundlanguages
=
sortedkeys
(
foundlanguages
)
354
for
feature
,
scripts
in
sortedhash
(
what
)
do
355
for
script
,
languages
in
next
,
scripts
do
356
if
not
languages
[
"
*
"
]
then
357
for
i
=
1
,
#
foundlanguages
do
358
local
language
=
foundlanguages
[
i
]
359
if
context
and
not
languages
[
language
]
then
360
report_defining
(
"
font %!font:name!, feature %a, script %a, no language %a
"
,
361
tfmdata
,
feature
,
script
,
language
)
362
end
363
end
364
end
365
end
366
end
367
end
368
end
369
end
370
check
(
features
.
gsub
)
371
check
(
features
.
gpos
)
372
end
373
end
374
end
375 376
function
definers
.
loadfont
(
specification
)
377
local
hash
=
constructors
.
hashinstance
(
specification
)
378
-- todo: also hash by instance / factors
379
local
tfmdata
=
loadedfonts
[
hash
]
-- hashes by size !
380
if
not
tfmdata
then
381
-- normally context will not end up here often (if so there is an issue somewhere)
382
local
forced
=
specification
.
forced
or
"
"
383
if
forced
~
=
"
"
then
384
local
reader
=
readers
[
lower
(
forced
)
]
-- normally forced is already lowered
385
tfmdata
=
reader
and
reader
(
specification
)
386
if
not
tfmdata
then
387
report_defining
(
"
forced type %a of %a not found
"
,
forced
,
specification
.
name
)
388
end
389
else
390
local
sequence
=
readers
.
sequence
-- can be overloaded so only a shortcut here
391
for
s
=
1
,
#
sequence
do
392
local
reader
=
sequence
[
s
]
393
if
readers
[
reader
]
then
-- we skip not loaded readers
394
if
trace_defining
then
395
report_defining
(
"
trying (reader sequence driven) type %a for %a with file %a
"
,
reader
,
specification
.
name
,
specification
.
filename
)
396
end
397
tfmdata
=
readers
[
reader
]
(
specification
)
398
if
tfmdata
then
399
break
400
else
401
specification
.
filename
=
nil
402
end
403
end
404
end
405
end
406
if
tfmdata
then
407
tfmdata
=
definers
.
applypostprocessors
(
tfmdata
)
408
checkembedding
(
tfmdata
)
-- todo: general postprocessor
409
loadedfonts
[
hash
]
=
tfmdata
410
designsizes
[
specification
.
hash
]
=
tfmdata
.
parameters
.
designsize
411
checkfeatures
(
tfmdata
)
412
end
413
end
414
if
not
tfmdata
then
415
report_defining
(
"
font with asked name %a is not found using lookup %a
"
,
specification
.
name
,
specification
.
lookup
)
416
end
417
return
tfmdata
418
end
419 420
function
constructors
.
readanddefine
(
name
,
size
)
-- no id -- maybe a dummy first
421
local
specification
=
definers
.
analyze
(
name
,
size
)
422
local
method
=
specification
.
method
423
if
method
and
variants
[
method
]
then
424
specification
=
variants
[
method
]
(
specification
)
425
end
426
specification
=
definers
.
resolve
(
specification
)
427
local
hash
=
constructors
.
hashinstance
(
specification
)
428
local
id
=
definers
.
registered
(
hash
)
429
if
not
id
then
430
local
tfmdata
=
definers
.
loadfont
(
specification
)
431
if
tfmdata
then
432
tfmdata
.
properties
.
hash
=
hash
433
id
=
font
.
define
(
tfmdata
)
434
definers
.
register
(
tfmdata
,
id
)
435
else
436
id
=
0
-- signal
437
end
438
end
439
return
fontdata
[
id
]
,
id
440
end
441 442
--[[ldx-- 443<p>So far the specifiers. Now comes the real definer. Here we cache 444based on id's. Here we also intercept the virtual font handler. Since 445it evolved stepwise I may rewrite this bit (combine code).</p> 446 447In the previously defined reader (the one resulting in a <l n='tfm'/> 448table) we cached the (scaled) instances. Here we cache them again, but 449this time based on id. We could combine this in one cache but this does 450not gain much. By the way, passing id's back to in the callback was 451introduced later in the development.</p> 452--ldx]]
--
453 454
function
definers
.
registered
(
hash
)
455
local
id
=
internalized
[
hash
]
456
return
id
,
id
and
fontdata
[
id
]
457
end
458 459
function
definers
.
register
(
tfmdata
,
id
)
460
if
tfmdata
and
id
then
461
local
hash
=
tfmdata
.
properties
.
hash
462
if
not
hash
then
463
report_defining
(
"
registering font, id %a, name %a, invalid hash
"
,
id
,
tfmdata
.
properties
.
filename
or
"
?
"
)
464
elseif
not
internalized
[
hash
]
then
465
internalized
[
hash
]
=
id
466
if
trace_defining
then
467
report_defining
(
"
registering font, id %s, hash %a
"
,
id
,
hash
)
468
end
469
fontdata
[
id
]
=
tfmdata
470
end
471
end
472
end
473 474
function
definers
.
read
(
specification
,
size
,
id
)
-- id can be optional, name can already be table
475
statistics
.
starttiming
(
fonts
)
476
if
type
(
specification
)
=
=
"
string
"
then
477
specification
=
definers
.
analyze
(
specification
,
size
)
478
end
479
local
method
=
specification
.
method
480
if
method
and
variants
[
method
]
then
481
specification
=
variants
[
method
]
(
specification
)
482
end
483
specification
=
definers
.
resolve
(
specification
)
484
local
hash
=
constructors
.
hashinstance
(
specification
)
485
local
tfmdata
=
definers
.
registered
(
hash
)
-- id
486
if
tfmdata
then
487
if
trace_defining
then
488
report_defining
(
"
already hashed: %s
"
,
hash
)
489
end
490
else
491
tfmdata
=
definers
.
loadfont
(
specification
)
-- can be overloaded
492
if
tfmdata
then
493
if
trace_defining
then
494
report_defining
(
"
loaded and hashed: %s
"
,
hash
)
495
end
496
tfmdata
.
properties
.
hash
=
hash
497
if
id
then
498
definers
.
register
(
tfmdata
,
id
)
499
end
500
else
501
if
trace_defining
then
502
report_defining
(
"
not loaded and hashed: %s
"
,
hash
)
503
end
504
end
505
end
506
if
not
tfmdata
then
-- or id?
507
report_defining
(
"
unknown font %a, loading aborted
"
,
specification
.
name
)
508
elseif
trace_defining
and
type
(
tfmdata
)
=
=
"
table
"
then
509
local
properties
=
tfmdata
.
properties
or
{
}
510
local
parameters
=
tfmdata
.
parameters
or
{
}
511
report_defining
(
"
using %a font with id %a, name %a, size %a, bytes %a, encoding %a, fullname %a, filename %a
"
,
512
properties
.
format
or
"
unknown
"
,
id
or
"
-
"
,
properties
.
name
,
parameters
.
size
,
properties
.
encodingbytes
,
513
properties
.
encodingname
,
properties
.
fullname
,
basename
(
properties
.
filename
)
)
514
end
515
statistics
.
stoptiming
(
fonts
)
516
return
tfmdata
517
end
518 519
function
font
.
getfont
(
id
)
520
return
fontdata
[
id
]
-- otherwise issues
521
end
522 523
--[[ldx-- 524<p>We overload the <l n='tfm'/> reader.</p> 525--ldx]]
--
526 527
if
not
context
then
528
callbacks
.
register
(
'
define_font
'
,
definers
.
read
,
"
definition of fonts (tfmdata preparation)
"
)
529
end
530