font-otl.lua /size: 32 Kb    last modification: 2020-07-01 14:35
1
if
not
modules
then
modules
=
{
}
end
modules
[
'
font-otl
'
]
=
{
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
-- After some experimenting with an alternative loader (one that is needed for
10
-- getting outlines in mp) I decided not to be compatible with the old (built-in)
11
-- one. The approach used in font-otn is as follows: we load the font in a compact
12
-- format but still very compatible with the ff data structures. From there we
13
-- create hashes to access the data efficiently. The implementation of feature
14
-- processing is mostly based on looking at the data as organized in the glyphs and
15
-- lookups as well as the specification. Keeping the lookup data in the glyphs is
16
-- very instructive and handy for tracing. On the other hand hashing is what brings
17
-- speed. So, the in the new approach (the old one will stay around too) we no
18
-- longer keep data in the glyphs which saves us a (what in retrospect looks a bit
19
-- like) a reconstruction step. It also means that the data format of the cached
20
-- files changes. What method is used depends on that format. There is no fundamental
21
-- change in processing, and not even in data organation. Most has to do with
22
-- loading and storage.
23 24
-- todo: less tounicodes
25 26
local
lower
=
string
.
lower
27
local
type
,
next
,
tonumber
,
tostring
,
unpack
=
type
,
next
,
tonumber
,
tostring
,
unpack
28
local
abs
=
math
.
abs
29
local
derivetable
=
table
.
derive
30
local
formatters
=
string
.
formatters
31 32
local
setmetatableindex
=
table
.
setmetatableindex
33
local
allocate
=
utilities
.
storage
.
allocate
34
local
registertracker
=
trackers
.
register
35
local
registerdirective
=
directives
.
register
36
local
starttiming
=
statistics
.
starttiming
37
local
stoptiming
=
statistics
.
stoptiming
38
local
elapsedtime
=
statistics
.
elapsedtime
39
local
findbinfile
=
resolvers
.
findbinfile
40 41
----- trace_private = false registertracker("otf.private", function(v) trace_private = v end)
42
----- trace_subfonts = false registertracker("otf.subfonts", function(v) trace_subfonts = v end)
43
local
trace_loading
=
false
registertracker
(
"
otf.loading
"
,
function
(
v
)
trace_loading
=
v
end
)
44
local
trace_features
=
false
registertracker
(
"
otf.features
"
,
function
(
v
)
trace_features
=
v
end
)
45
----- trace_dynamics = false registertracker("otf.dynamics", function(v) trace_dynamics = v end)
46
----- trace_sequences = false registertracker("otf.sequences", function(v) trace_sequences = v end)
47
----- trace_markwidth = false registertracker("otf.markwidth", function(v) trace_markwidth = v end)
48
local
trace_defining
=
false
registertracker
(
"
fonts.defining
"
,
function
(
v
)
trace_defining
=
v
end
)
49 50
local
report_otf
=
logs
.
reporter
(
"
fonts
"
,
"
otf loading
"
)
51 52
local
fonts
=
fonts
53
local
otf
=
fonts
.
handlers
.
otf
54 55
otf
.
version
=
3
.
111
-- beware: also sync font-mis.lua and in mtx-fonts
56
otf
.
cache
=
containers
.
define
(
"
fonts
"
,
"
otl
"
,
otf
.
version
,
true
)
57
otf
.
svgcache
=
containers
.
define
(
"
fonts
"
,
"
svg
"
,
otf
.
version
,
true
)
58
otf
.
pngcache
=
containers
.
define
(
"
fonts
"
,
"
png
"
,
otf
.
version
,
true
)
59
otf
.
pdfcache
=
containers
.
define
(
"
fonts
"
,
"
pdf
"
,
otf
.
version
,
true
)
60
otf
.
mpscache
=
containers
.
define
(
"
fonts
"
,
"
mps
"
,
otf
.
version
,
true
)
61 62
otf
.
svgenabled
=
false
63
otf
.
pngenabled
=
false
64 65
local
otfreaders
=
otf
.
readers
66 67
local
hashes
=
fonts
.
hashes
68
local
definers
=
fonts
.
definers
69
local
readers
=
fonts
.
readers
70
local
constructors
=
fonts
.
constructors
71 72
local
otffeatures
=
constructors
.
features
.
otf
73
local
registerotffeature
=
otffeatures
.
register
74 75
local
otfenhancers
=
constructors
.
enhancers
.
otf
76
local
registerotfenhancer
=
otfenhancers
.
register
77 78
local
forceload
=
false
79
local
cleanup
=
0
-- mk: 0=885M 1=765M 2=735M (regular run 730M)
80
local
syncspace
=
true
81
local
forcenotdef
=
false
82 83
local
privateoffset
=
fonts
.
constructors
and
fonts
.
constructors
.
privateoffset
or
0xF0000
-- 0x10FFFF
84 85
local
applyruntimefixes
=
fonts
.
treatments
and
fonts
.
treatments
.
applyfixes
86 87
local
wildcard
=
"
*
"
88
local
default
=
"
dflt
"
89 90
local
formats
=
fonts
.
formats
91 92
formats
.
otf
=
"
opentype
"
93
formats
.
ttf
=
"
truetype
"
94
formats
.
ttc
=
"
truetype
"
95 96
registerdirective
(
"
fonts.otf.loader.cleanup
"
,
function
(
v
)
cleanup
=
tonumber
(
v
)
or
(
v
and
1
)
or
0
end
)
97
registerdirective
(
"
fonts.otf.loader.force
"
,
function
(
v
)
forceload
=
v
end
)
98
registerdirective
(
"
fonts.otf.loader.syncspace
"
,
function
(
v
)
syncspace
=
v
end
)
99
registerdirective
(
"
fonts.otf.loader.forcenotdef
"
,
function
(
v
)
forcenotdef
=
v
end
)
100 101
-- otfenhancers.patch("before","migrate metadata","cambria",function() end)
102 103
registerotfenhancer
(
"
check extra features
"
,
function
(
)
end
)
-- placeholder
104 105
-- Kai has memory problems on osx so here is an experiment (I only tested on windows as
106
-- my test mac is old and gets no updates and is therefore rather useless.):
107 108
local
checkmemory
=
utilities
.
lua
and
utilities
.
lua
.
checkmemory
109
local
threshold
=
100
-- MB
110
local
tracememory
=
false
111 112
registertracker
(
"
fonts.otf.loader.memory
"
,
function
(
v
)
tracememory
=
v
end
)
113 114
if
not
checkmemory
then
-- we need a generic plug (this code might move):
115 116
local
collectgarbage
=
collectgarbage
117 118
checkmemory
=
function
(
previous
,
threshold
)
-- threshold in MB
119
local
current
=
collectgarbage
(
"
count
"
)
120
if
previous
then
121
local
checked
=
(
threshold
or
64
)
*
1024
122
if
current
-
previous
>
checked
then
123
collectgarbage
(
"
collect
"
)
124
current
=
collectgarbage
(
"
count
"
)
125
end
126
end
127
return
current
128
end
129 130
end
131 132
function
otf
.
load
(
filename
,
sub
,
instance
)
133
local
base
=
file
.
basename
(
file
.
removesuffix
(
filename
)
)
134
local
name
=
file
.
removesuffix
(
base
)
-- already no suffix
135
local
attr
=
lfs
.
attributes
(
filename
)
136
local
size
=
attr
and
attr
.
size
or
0
137
local
time
=
attr
and
attr
.
modification
or
0
138
-- sub can be number of string
139
if
sub
=
=
"
"
then
140
sub
=
false
141
end
142
local
hash
=
name
143
if
sub
then
144
hash
=
hash
.
.
"
-
"
.
.
sub
145
end
146
if
instance
then
147
hash
=
hash
.
.
"
-
"
.
.
instance
148
end
149
hash
=
containers
.
cleanname
(
hash
)
150
local
data
=
containers
.
read
(
otf
.
cache
,
hash
)
151
local
reload
=
not
data
or
data
.
size
~
=
size
or
data
.
time
~
=
time
or
data
.
tableversion
~
=
otfreaders
.
tableversion
152
if
forceload
then
153
report_otf
(
"
forced reload of %a due to hard coded flag
"
,
filename
)
154
reload
=
true
155
end
156
if
reload
then
157
report_otf
(
"
loading %a, hash %a
"
,
filename
,
hash
)
158
--
159
starttiming
(
otfreaders
,
true
)
160
data
=
otfreaders
.
loadfont
(
filename
,
sub
or
1
,
instance
)
-- we can pass the number instead (if it comes from a name search)
161
if
data
then
162
-- todo: make this a plugin
163
local
used
=
checkmemory
(
)
164
local
resources
=
data
.
resources
165
local
svgshapes
=
resources
.
svgshapes
166
local
pngshapes
=
resources
.
pngshapes
167
if
cleanup
=
=
0
then
168
checkmemory
(
used
,
threshold
,
tracememory
)
169
end
170
if
svgshapes
then
171
resources
.
svgshapes
=
nil
172
if
otf
.
svgenabled
then
173
local
timestamp
=
os
.
date
(
)
174
-- work in progress ... a bit boring to do
175
containers
.
write
(
otf
.
svgcache
,
hash
,
{
176
svgshapes
=
svgshapes
,
177
timestamp
=
timestamp
,
178
}
)
179
data
.
properties
.
svg
=
{
180
hash
=
hash
,
181
timestamp
=
timestamp
,
182
}
183
end
184
if
cleanup
>
1
then
185
collectgarbage
(
"
collect
"
)
186
else
187
checkmemory
(
used
,
threshold
,
tracememory
)
188
end
189
end
190
if
pngshapes
then
191
resources
.
pngshapes
=
nil
192
if
otf
.
pngenabled
then
193
local
timestamp
=
os
.
date
(
)
194
-- work in progress ... a bit boring to do
195
containers
.
write
(
otf
.
pngcache
,
hash
,
{
196
pngshapes
=
pngshapes
,
197
timestamp
=
timestamp
,
198
}
)
199
data
.
properties
.
png
=
{
200
hash
=
hash
,
201
timestamp
=
timestamp
,
202
}
203
end
204
if
cleanup
>
1
then
205
collectgarbage
(
"
collect
"
)
206
else
207
checkmemory
(
used
,
threshold
,
tracememory
)
208
end
209
end
210
--
211
otfreaders
.
compact
(
data
)
212
if
cleanup
=
=
0
then
213
checkmemory
(
used
,
threshold
,
tracememory
)
214
end
215
otfreaders
.
rehash
(
data
,
"
unicodes
"
)
216
otfreaders
.
addunicodetable
(
data
)
217
otfreaders
.
extend
(
data
)
218
if
cleanup
=
=
0
then
219
checkmemory
(
used
,
threshold
,
tracememory
)
220
end
221
otfreaders
.
pack
(
data
)
222
report_otf
(
"
loading done
"
)
223
report_otf
(
"
saving %a in cache
"
,
filename
)
224
data
=
containers
.
write
(
otf
.
cache
,
hash
,
data
)
225
if
cleanup
>
1
then
226
collectgarbage
(
"
collect
"
)
227
else
228
checkmemory
(
used
,
threshold
,
tracememory
)
229
end
230
stoptiming
(
otfreaders
)
231
if
elapsedtime
then
232
report_otf
(
"
loading, optimizing, packing and caching time %s
"
,
elapsedtime
(
otfreaders
)
)
233
end
234
if
cleanup
>
3
then
235
collectgarbage
(
"
collect
"
)
236
else
237
checkmemory
(
used
,
threshold
,
tracememory
)
238
end
239
data
=
containers
.
read
(
otf
.
cache
,
hash
)
-- this frees the old table and load the sparse one
240
if
cleanup
>
2
then
241
collectgarbage
(
"
collect
"
)
242
else
243
checkmemory
(
used
,
threshold
,
tracememory
)
244
end
245
else
246
stoptiming
(
otfreaders
)
247
data
=
nil
248
report_otf
(
"
loading failed due to read error
"
)
249
end
250
end
251
if
data
then
252
if
trace_defining
then
253
report_otf
(
"
loading from cache using hash %a
"
,
hash
)
254
end
255
--
256
otfreaders
.
unpack
(
data
)
257
otfreaders
.
expand
(
data
)
-- inline tables
258
otfreaders
.
addunicodetable
(
data
)
-- only when not done yet
259
--
260
otfenhancers
.
apply
(
data
,
filename
,
data
)
-- in context one can also use treatments
261
--
262
-- constructors.addcoreunicodes(data.resources.unicodes) -- still needed ?
263
--
264
if
applyruntimefixes
then
265
applyruntimefixes
(
filename
,
data
)
-- e.g. see treatments.lfg
266
end
267
--
268
data
.
metadata
.
math
=
data
.
resources
.
mathconstants
269
--
270
-- delayed tables (experiment)
271
--
272
local
classes
=
data
.
resources
.
classes
273
if
not
classes
then
274
local
descriptions
=
data
.
descriptions
275
classes
=
setmetatableindex
(
function
(
t
,
k
)
276
local
d
=
descriptions
[
k
]
277
local
v
=
(
d
and
d
.
class
or
"
base
"
)
or
false
278
t
[
k
]
=
v
279
return
v
280
end
)
281
data
.
resources
.
classes
=
classes
282
end
283
--
284
end
285 286
return
data
287
end
288 289
-- modes: node, base, none
290 291
function
otf
.
setfeatures
(
tfmdata
,
features
)
292
local
okay
=
constructors
.
initializefeatures
(
"
otf
"
,
tfmdata
,
features
,
trace_features
,
report_otf
)
293
if
okay
then
294
return
constructors
.
collectprocessors
(
"
otf
"
,
tfmdata
,
features
,
trace_features
,
report_otf
)
295
else
296
return
{
}
-- will become false
297
end
298
end
299 300
-- the first version made a top/mid/not extensible table, now we just
301
-- pass on the variants data and deal with it in the tfm scaler (there
302
-- is no longer an extensible table anyway)
303
--
304
-- we cannot share descriptions as virtual fonts might extend them (ok,
305
-- we could use a cache with a hash
306
--
307
-- we already assign an empty table to characters as we can add for
308
-- instance protruding info and loop over characters; one is not supposed
309
-- to change descriptions and if one does so one should make a copy!
310 311
local
function
copytotfm
(
data
,
cache_id
)
312
if
data
then
313
local
metadata
=
data
.
metadata
314
local
properties
=
derivetable
(
data
.
properties
)
315
local
descriptions
=
derivetable
(
data
.
descriptions
)
316
local
goodies
=
derivetable
(
data
.
goodies
)
317
local
characters
=
{
}
318
local
parameters
=
{
}
319
local
mathparameters
=
{
}
320
--
321
local
resources
=
data
.
resources
322
local
unicodes
=
resources
.
unicodes
323
local
spaceunits
=
500
324
local
spacer
=
"
space
"
325
local
designsize
=
metadata
.
designsize
or
100
326
local
minsize
=
metadata
.
minsize
or
designsize
327
local
maxsize
=
metadata
.
maxsize
or
designsize
328
local
mathspecs
=
metadata
.
math
329
--
330
if
designsize
=
=
0
then
331
designsize
=
100
332
minsize
=
100
333
maxsize
=
100
334
end
335
if
mathspecs
then
336
for
name
,
value
in
next
,
mathspecs
do
337
mathparameters
[
name
]
=
value
338
end
339
end
340
for
unicode
in
next
,
data
.
descriptions
do
-- use parent table
341
characters
[
unicode
]
=
{
}
342
end
343
if
mathspecs
then
344
for
unicode
,
character
in
next
,
characters
do
345
local
d
=
descriptions
[
unicode
]
-- we could use parent table here
346
local
m
=
d
.
math
347
if
m
then
348
-- watch out: luatex uses horiz_variants for the parts
349
--
350
local
italic
=
m
.
italic
351
local
vitalic
=
m
.
vitalic
352
--
353
local
variants
=
m
.
hvariants
354
local
parts
=
m
.
hparts
355
if
variants
then
356
local
c
=
character
357
for
i
=
1
,
#
variants
do
358
-- local un = variants[i].glyph
359
local
un
=
variants
[
i
]
360
c
.
next
=
un
361
c
=
characters
[
un
]
362
end
-- c is now last in chain
363
c
.
horiz_variants
=
parts
364
elseif
parts
then
365
character
.
horiz_variants
=
parts
366
italic
=
m
.
hitalic
367
end
368
--
369
local
variants
=
m
.
vvariants
370
local
parts
=
m
.
vparts
371
if
variants
then
372
local
c
=
character
373
for
i
=
1
,
#
variants
do
374
-- local un = variants[i].glyph
375
local
un
=
variants
[
i
]
376
c
.
next
=
un
377
c
=
characters
[
un
]
378
end
-- c is now last in chain
379
c
.
vert_variants
=
parts
380
elseif
parts
then
381
character
.
vert_variants
=
parts
382
end
383
--
384
if
italic
and
italic
~
=
0
then
385
character
.
italic
=
italic
386
end
387
--
388
if
vitalic
and
vitalic
~
=
0
then
389
character
.
vert_italic
=
vitalic
390
end
391
--
392
local
accent
=
m
.
accent
-- taccent?
393
if
accent
then
394
character
.
accent
=
accent
395
end
396
--
397
local
kerns
=
m
.
kerns
398
if
kerns
then
399
character
.
mathkerns
=
kerns
400
end
401
end
402
end
403
end
404
-- we need a runtime lookup because of running from cdrom or zip, brrr (shouldn't
405
-- we use the basename then?)
406
local
filename
=
constructors
.
checkedfilename
(
resources
)
407
local
fontname
=
metadata
.
fontname
408
local
fullname
=
metadata
.
fullname
or
fontname
409
local
psname
=
fontname
or
fullname
410
local
subfont
=
metadata
.
subfontindex
411
local
units
=
metadata
.
units
or
1000
412
--
413
if
units
=
=
0
then
-- catch bugs in fonts
414
units
=
1000
-- maybe 2000 when ttf
415
metadata
.
units
=
1000
416
report_otf
(
"
changing %a units to %a
"
,
0
,
units
)
417
end
418
--
419
local
monospaced
=
metadata
.
monospaced
420
local
charwidth
=
metadata
.
averagewidth
-- or unset
421
local
charxheight
=
metadata
.
xheight
-- or unset
422
local
italicangle
=
metadata
.
italicangle
423
local
hasitalics
=
metadata
.
hasitalics
424
properties
.
monospaced
=
monospaced
425
properties
.
hasitalics
=
hasitalics
426
parameters
.
italicangle
=
italicangle
427
parameters
.
charwidth
=
charwidth
428
parameters
.
charxheight
=
charxheight
429
--
430
local
space
=
0x0020
431
local
emdash
=
0x2014
432
if
monospaced
then
433
if
descriptions
[
space
]
then
434
spaceunits
,
spacer
=
descriptions
[
space
]
.
width
,
"
space
"
435
end
436
if
not
spaceunits
and
descriptions
[
emdash
]
then
437
spaceunits
,
spacer
=
descriptions
[
emdash
]
.
width
,
"
emdash
"
438
end
439
if
not
spaceunits
and
charwidth
then
440
spaceunits
,
spacer
=
charwidth
,
"
charwidth
"
441
end
442
else
443
if
descriptions
[
space
]
then
444
spaceunits
,
spacer
=
descriptions
[
space
]
.
width
,
"
space
"
445
end
446
if
not
spaceunits
and
descriptions
[
emdash
]
then
447
spaceunits
,
spacer
=
descriptions
[
emdash
]
.
width
/
2
,
"
emdash/2
"
448
end
449
if
not
spaceunits
and
charwidth
then
450
spaceunits
,
spacer
=
charwidth
,
"
charwidth
"
451
end
452
end
453
spaceunits
=
tonumber
(
spaceunits
)
or
units
/
2
454
--
455
parameters
.
slant
=
0
456
parameters
.
space
=
spaceunits
-- 3.333 (cmr10)
457
parameters
.
space_stretch
=
1
*
units
/
2
-- 500 -- 1.666 (cmr10)
458
parameters
.
space_shrink
=
1
*
units
/
3
-- 333 -- 1.111 (cmr10)
459
parameters
.
x_height
=
2
*
units
/
5
-- 400
460
parameters
.
quad
=
units
-- 1000
461
if
spaceunits
<
2
*
units
/
5
then
462
-- todo: warning
463
end
464
if
italicangle
and
italicangle
~
=
0
then
465
parameters
.
italicangle
=
italicangle
466
parameters
.
italicfactor
=
math
.
cos
(
math
.
rad
(
90
+
italicangle
)
)
467
parameters
.
slant
=
-
math
.
tan
(
italicangle
*
math
.
pi
/
180
)
468
end
469
if
monospaced
then
470
parameters
.
space_stretch
=
0
471
parameters
.
space_shrink
=
0
472
elseif
syncspace
then
--
473
parameters
.
space_stretch
=
spaceunits
/
2
474
parameters
.
space_shrink
=
spaceunits
/
3
475
end
476
parameters
.
extra_space
=
parameters
.
space_shrink
-- 1.111 (cmr10)
477
if
charxheight
then
478
parameters
.
x_height
=
charxheight
479
else
480
local
x
=
0x0078
481
if
x
then
482
local
x
=
descriptions
[
x
]
483
if
x
then
484
parameters
.
x_height
=
x
.
height
485
end
486
end
487
end
488
--
489
parameters
.
designsize
=
(
designsize
/
10
)
*
65536
490
parameters
.
minsize
=
(
minsize
/
10
)
*
65536
491
parameters
.
maxsize
=
(
maxsize
/
10
)
*
65536
492
parameters
.
ascender
=
abs
(
metadata
.
ascender
or
0
)
493
parameters
.
descender
=
abs
(
metadata
.
descender
or
0
)
494
parameters
.
units
=
units
495
parameters
.
vheight
=
metadata
.
defaultvheight
496
--
497
properties
.
space
=
spacer
498
properties
.
encodingbytes
=
2
499
properties
.
format
=
data
.
format
or
formats
.
otf
500
properties
.
filename
=
filename
501
properties
.
fontname
=
fontname
502
properties
.
fullname
=
fullname
503
properties
.
psname
=
psname
504
properties
.
name
=
filename
or
fullname
505
properties
.
subfont
=
subfont
506
--
507
-- properties.name = specification.name
508
-- properties.sub = specification.sub
509
--
510
properties
.
private
=
properties
.
private
or
data
.
private
or
privateoffset
511
--
512
return
{
513
characters
=
characters
,
514
descriptions
=
descriptions
,
515
parameters
=
parameters
,
516
mathparameters
=
mathparameters
,
517
resources
=
resources
,
518
properties
=
properties
,
519
goodies
=
goodies
,
520
}
521
end
522
end
523 524
-- These woff files are a kind of joke in a tex environment because one can simply convert
525
-- them to ttf/otf and use them as such (after all, we cache them too). The successor format
526
-- woff2 is more complex so there we can as well call an external converter which in the end
527
-- makes this code kind of obsolete before it's even used. Although ... it might become a
528
-- more general conversion plug in.
529 530
local
converters
=
{
531
woff
=
{
532
cachename
=
"
webfonts
"
,
533
action
=
otf
.
readers
.
woff2otf
,
534
}
535
}
536 537
-- We can get differences between daylight saving etc ... but it makes no sense to
538
-- mess with trickery .. so be it when you use a different binary.
539 540
local
function
checkconversion
(
specification
)
541
local
filename
=
specification
.
filename
542
local
converter
=
converters
[
lower
(
file
.
suffix
(
filename
)
)
]
543
if
converter
then
544
local
base
=
file
.
basename
(
filename
)
545
local
name
=
file
.
removesuffix
(
base
)
546
local
attr
=
lfs
.
attributes
(
filename
)
547
local
size
=
attr
and
attr
.
size
or
0
548
local
time
=
attr
and
attr
.
modification
or
0
549
if
size
>
0
then
550
local
cleanname
=
containers
.
cleanname
(
name
)
551
local
cachename
=
caches
.
setfirstwritablefile
(
cleanname
,
converter
.
cachename
)
552
if
not
io
.
exists
(
cachename
)
or
(
time
~
=
lfs
.
attributes
(
cachename
)
.
modification
)
then
553
report_otf
(
"
caching font %a in %a
"
,
filename
,
cachename
)
554
converter
.
action
(
filename
,
cachename
)
-- todo infoonly
555
lfs
.
touch
(
cachename
,
time
,
time
)
556
end
557
specification
.
filename
=
cachename
558
end
559
end
560
end
561 562
local
function
otftotfm
(
specification
)
563
local
cache_id
=
specification
.
hash
564
local
tfmdata
=
containers
.
read
(
constructors
.
cache
,
cache_id
)
565
if
not
tfmdata
then
566 567
checkconversion
(
specification
)
-- for the moment here
568 569
local
name
=
specification
.
name
570
local
sub
=
specification
.
sub
571
local
subindex
=
specification
.
subindex
572
local
filename
=
specification
.
filename
573
local
features
=
specification
.
features
.
normal
574
local
instance
=
specification
.
instance
or
(
features
and
features
.
axis
)
575
local
rawdata
=
otf
.
load
(
filename
,
sub
,
instance
)
576
if
rawdata
and
next
(
rawdata
)
then
577
local
descriptions
=
rawdata
.
descriptions
578
rawdata
.
lookuphash
=
{
}
-- to be done
579
tfmdata
=
copytotfm
(
rawdata
,
cache_id
)
580
if
tfmdata
and
next
(
tfmdata
)
then
581
-- at this moment no characters are assigned yet, only empty slots
582
local
features
=
constructors
.
checkedfeatures
(
"
otf
"
,
features
)
583
local
shared
=
tfmdata
.
shared
584
if
not
shared
then
585
shared
=
{
}
586
tfmdata
.
shared
=
shared
587
end
588
shared
.
rawdata
=
rawdata
589
-- shared.features = features -- default
590
shared
.
dynamics
=
{
}
591
-- shared.processes = { }
592
tfmdata
.
changed
=
{
}
593
shared
.
features
=
features
594
shared
.
processes
=
otf
.
setfeatures
(
tfmdata
,
features
)
595
end
596
end
597
containers
.
write
(
constructors
.
cache
,
cache_id
,
tfmdata
)
598
end
599
return
tfmdata
600
end
601 602
local
function
read_from_otf
(
specification
)
603
local
tfmdata
=
otftotfm
(
specification
)
604
if
tfmdata
then
605
-- this late ? .. needs checking
606
tfmdata
.
properties
.
name
=
specification
.
name
607
tfmdata
.
properties
.
sub
=
specification
.
sub
608
--
609
tfmdata
=
constructors
.
scale
(
tfmdata
,
specification
)
610
local
allfeatures
=
tfmdata
.
shared
.
features
or
specification
.
features
.
normal
611
constructors
.
applymanipulators
(
"
otf
"
,
tfmdata
,
allfeatures
,
trace_features
,
report_otf
)
612
constructors
.
setname
(
tfmdata
,
specification
)
-- only otf?
613
fonts
.
loggers
.
register
(
tfmdata
,
file
.
suffix
(
specification
.
filename
)
,
specification
)
614
end
615
return
tfmdata
616
end
617 618
local
function
checkmathsize
(
tfmdata
,
mathsize
)
619
local
mathdata
=
tfmdata
.
shared
.
rawdata
.
metadata
.
math
620
local
mathsize
=
tonumber
(
mathsize
)
621
if
mathdata
then
-- we cannot use mathparameters as luatex will complain
622
local
parameters
=
tfmdata
.
parameters
623
parameters
.
scriptpercentage
=
mathdata
.
ScriptPercentScaleDown
624
parameters
.
scriptscriptpercentage
=
mathdata
.
ScriptScriptPercentScaleDown
625
parameters
.
mathsize
=
mathsize
-- only when a number !
626
end
627
end
628 629
registerotffeature
{
630
name
=
"
mathsize
"
,
631
description
=
"
apply mathsize specified in the font
"
,
632
initializers
=
{
633
base
=
checkmathsize
,
634
node
=
checkmathsize
,
635
}
636
}
637 638
-- readers
639 640
function
otf
.
collectlookups
(
rawdata
,
kind
,
script
,
language
)
641
if
not
kind
then
642
return
643
end
644
if
not
script
then
645
script
=
default
646
end
647
if
not
language
then
648
language
=
default
649
end
650
local
lookupcache
=
rawdata
.
lookupcache
651
if
not
lookupcache
then
652
lookupcache
=
{
}
653
rawdata
.
lookupcache
=
lookupcache
654
end
655
local
kindlookup
=
lookupcache
[
kind
]
656
if
not
kindlookup
then
657
kindlookup
=
{
}
658
lookupcache
[
kind
]
=
kindlookup
659
end
660
local
scriptlookup
=
kindlookup
[
script
]
661
if
not
scriptlookup
then
662
scriptlookup
=
{
}
663
kindlookup
[
script
]
=
scriptlookup
664
end
665
local
languagelookup
=
scriptlookup
[
language
]
666
if
not
languagelookup
then
667
local
sequences
=
rawdata
.
resources
.
sequences
668
local
featuremap
=
{
}
669
local
featurelist
=
{
}
670
if
sequences
then
671
for
s
=
1
,
#
sequences
do
672
local
sequence
=
sequences
[
s
]
673
local
features
=
sequence
.
features
674
if
features
then
675
features
=
features
[
kind
]
676
if
features
then
677
-- features = features[script] or features[default] or features[wildcard]
678
features
=
features
[
script
]
or
features
[
wildcard
]
679
if
features
then
680
-- features = features[language] or features[default] or features[wildcard]
681
features
=
features
[
language
]
or
features
[
wildcard
]
682
if
features
then
683
if
not
featuremap
[
sequence
]
then
684
featuremap
[
sequence
]
=
true
685
featurelist
[
#
featurelist
+
1
]
=
sequence
686
end
687
end
688
end
689
end
690
end
691
end
692
if
#
featurelist
=
=
0
then
693
featuremap
,
featurelist
=
false
,
false
694
end
695
else
696
featuremap
,
featurelist
=
false
,
false
697
end
698
languagelookup
=
{
featuremap
,
featurelist
}
699
scriptlookup
[
language
]
=
languagelookup
700
end
701
return
unpack
(
languagelookup
)
702
end
703 704
-- moved from font-oth.lua, todo: also afm
705 706
local
function
getgsub
(
tfmdata
,
k
,
kind
,
value
)
707
local
shared
=
tfmdata
.
shared
708
local
rawdata
=
shared
and
shared
.
rawdata
709
if
rawdata
then
710
local
sequences
=
rawdata
.
resources
.
sequences
711
if
sequences
then
712
local
properties
=
tfmdata
.
properties
713
local
validlookups
,
lookuplist
=
otf
.
collectlookups
(
rawdata
,
kind
,
properties
.
script
,
properties
.
language
)
714
if
validlookups
then
715
-- local choice = tonumber(value) or 1 -- no random here (yet)
716
for
i
=
1
,
#
lookuplist
do
717
local
lookup
=
lookuplist
[
i
]
718
local
steps
=
lookup
.
steps
719
local
nofsteps
=
lookup
.
nofsteps
720
for
i
=
1
,
nofsteps
do
721
local
coverage
=
steps
[
i
]
.
coverage
722
if
coverage
then
723
local
found
=
coverage
[
k
]
724
if
found
then
725
return
found
,
lookup
.
type
726
end
727
end
728
end
729
end
730
end
731
end
732
end
733
end
734 735
otf
.
getgsub
=
getgsub
-- returns value, gsub_kind
736 737
function
otf
.
getsubstitution
(
tfmdata
,
k
,
kind
,
value
)
738
local
found
,
kind
=
getgsub
(
tfmdata
,
k
,
kind
,
value
)
739
if
not
found
then
740
--
741
elseif
kind
=
=
"
gsub_single
"
then
742
return
found
743
elseif
kind
=
=
"
gsub_alternate
"
then
744
local
choice
=
tonumber
(
value
)
or
1
-- no random here (yet)
745
return
found
[
choice
]
or
found
[
1
]
or
k
746
end
747
return
k
748
end
749 750
otf
.
getalternate
=
otf
.
getsubstitution
751 752
function
otf
.
getmultiple
(
tfmdata
,
k
,
kind
)
753
local
found
,
kind
=
getgsub
(
tfmdata
,
k
,
kind
)
754
if
found
and
kind
=
=
"
gsub_multiple
"
then
755
return
found
756
end
757
return
{
k
}
758
end
759 760
function
otf
.
getkern
(
tfmdata
,
left
,
right
,
kind
)
761
local
kerns
=
getgsub
(
tfmdata
,
left
,
kind
or
"
kern
"
,
true
)
-- for now we use getsub
762
if
kerns
then
763
local
found
=
kerns
[
right
]
764
local
kind
=
type
(
found
)
765
if
kind
=
=
"
table
"
then
766
found
=
found
[
1
]
[
3
]
-- can be more clever
767
elseif
kind
~
=
"
number
"
then
768
found
=
false
769
end
770
if
found
then
771
return
found
*
tfmdata
.
parameters
.
factor
772
end
773
end
774
return
0
775
end
776 777
local
function
check_otf
(
forced
,
specification
,
suffix
)
778
local
name
=
specification
.
name
779
if
forced
then
780
name
=
specification
.
forcedname
-- messy
781
end
782
local
fullname
=
findbinfile
(
name
,
suffix
)
or
"
"
783
if
fullname
=
=
"
"
then
784
fullname
=
fonts
.
names
.
getfilename
(
name
,
suffix
)
or
"
"
785
end
786
if
fullname
~
=
"
"
and
not
fonts
.
names
.
ignoredfile
(
fullname
)
then
787
specification
.
filename
=
fullname
788
return
read_from_otf
(
specification
)
789
end
790
end
791 792
local
function
opentypereader
(
specification
,
suffix
)
793
local
forced
=
specification
.
forced
or
"
"
794
if
formats
[
forced
]
then
795
return
check_otf
(
true
,
specification
,
forced
)
796
else
797
return
check_otf
(
false
,
specification
,
suffix
)
798
end
799
end
800 801
readers
.
opentype
=
opentypereader
-- kind of useless and obsolete
802 803
function
readers
.
otf
(
specification
)
return
opentypereader
(
specification
,
"
otf
"
)
end
804
function
readers
.
ttf
(
specification
)
return
opentypereader
(
specification
,
"
ttf
"
)
end
805
function
readers
.
ttc
(
specification
)
return
opentypereader
(
specification
,
"
ttf
"
)
end
806 807
function
readers
.
woff
(
specification
)
808
checkconversion
(
specification
)
809
opentypereader
(
specification
,
"
"
)
810
end
811 812
-- this will be overloaded
813 814
function
otf
.
scriptandlanguage
(
tfmdata
,
attr
)
815
local
properties
=
tfmdata
.
properties
816
return
properties
.
script
or
"
dflt
"
,
properties
.
language
or
"
dflt
"
817
end
818 819
-- a little bit of abstraction
820 821
local
function
justset
(
coverage
,
unicode
,
replacement
)
822
coverage
[
unicode
]
=
replacement
823
end
824 825
otf
.
coverup
=
{
826
stepkey
=
"
steps
"
,
827
actions
=
{
828
chainsubstitution
=
justset
,
829
chainposition
=
justset
,
830
substitution
=
justset
,
831
alternate
=
justset
,
832
multiple
=
justset
,
833
kern
=
justset
,
834
pair
=
justset
,
835
single
=
justset
,
836
ligature
=
function
(
coverage
,
unicode
,
ligature
)
837
local
first
=
ligature
[
1
]
838
local
tree
=
coverage
[
first
]
839
if
not
tree
then
840
tree
=
{
}
841
coverage
[
first
]
=
tree
842
end
843
for
i
=
2
,
#
ligature
do
844
local
l
=
ligature
[
i
]
845
local
t
=
tree
[
l
]
846
if
not
t
then
847
t
=
{
}
848
tree
[
l
]
=
t
849
end
850
tree
=
t
851
end
852
tree
.
ligature
=
unicode
853
end
,
854
}
,
855
register
=
function
(
coverage
,
featuretype
,
format
)
856
return
{
857
format
=
format
,
858
coverage
=
coverage
,
859
}
860
end
861
}
862