font-ctx.lua /size: 113 Kb    last modification: 2020-07-01 14:35
1
if
not
modules
then
modules
=
{
}
end
modules
[
'
font-ctx
'
]
=
{
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
-- At some point I will clean up the code here so that at the tex end
10
-- the table interface is used.
11
--
12
-- Todo: make a proper 'next id' mechanism (register etc) or wait till 'true'
13
-- in virtual fonts indices is implemented.
14 15
local
tostring
,
next
,
type
,
rawget
,
tonumber
=
tostring
,
next
,
type
,
rawget
,
tonumber
16 17
local
format
,
gmatch
,
match
,
find
,
lower
,
upper
,
gsub
,
byte
,
topattern
=
string
.
format
,
string
.
gmatch
,
string
.
match
,
string
.
find
,
string
.
lower
,
string
.
upper
,
string
.
gsub
,
string
.
byte
,
string
.
topattern
18
local
concat
,
serialize
,
sort
,
fastcopy
,
mergedtable
=
table
.
concat
,
table
.
serialize
,
table
.
sort
,
table
.
fastcopy
,
table
.
merged
19
local
sortedhash
,
sortedkeys
,
sequenced
=
table
.
sortedhash
,
table
.
sortedkeys
,
table
.
sequenced
20
local
parsers
=
utilities
.
parsers
21
local
settings_to_hash
,
hash_to_string
,
settings_to_array
=
parsers
.
settings_to_hash
,
parsers
.
hash_to_string
,
parsers
.
settings_to_array
22
local
formatcolumns
=
utilities
.
formatters
.
formatcolumns
23
local
mergehashes
=
utilities
.
parsers
.
mergehashes
24
local
formatters
=
string
.
formatters
25
local
basename
=
file
.
basename
26 27
local
utfchar
,
utfbyte
=
utf
.
char
,
utf
.
byte
28
local
round
=
math
.
round
29 30
local
context
,
commands
=
context
,
commands
31 32
local
P
,
S
,
C
,
Cc
,
Cf
,
Cg
,
Ct
,
lpegmatch
=
lpeg
.
P
,
lpeg
.
S
,
lpeg
.
C
,
lpeg
.
Cc
,
lpeg
.
Cf
,
lpeg
.
Cg
,
lpeg
.
Ct
,
lpeg
.
match
33 34
local
trace_features
=
false
trackers
.
register
(
"
fonts.features
"
,
function
(
v
)
trace_features
=
v
end
)
35
local
trace_defining
=
false
trackers
.
register
(
"
fonts.defining
"
,
function
(
v
)
trace_defining
=
v
end
)
36
local
trace_designsize
=
false
trackers
.
register
(
"
fonts.designsize
"
,
function
(
v
)
trace_designsize
=
v
end
)
37
local
trace_usage
=
false
trackers
.
register
(
"
fonts.usage
"
,
function
(
v
)
trace_usage
=
v
end
)
38
local
trace_mapfiles
=
false
trackers
.
register
(
"
fonts.mapfiles
"
,
function
(
v
)
trace_mapfiles
=
v
end
)
39
local
trace_automode
=
false
trackers
.
register
(
"
fonts.automode
"
,
function
(
v
)
trace_automode
=
v
end
)
40
local
trace_merge
=
false
trackers
.
register
(
"
fonts.merge
"
,
function
(
v
)
trace_merge
=
v
end
)
41 42
local
report
=
logs
.
reporter
(
"
fonts
"
)
43
local
report_features
=
logs
.
reporter
(
"
fonts
"
,
"
features
"
)
44
local
report_cummulative
=
logs
.
reporter
(
"
fonts
"
,
"
cummulative
"
)
45
local
report_defining
=
logs
.
reporter
(
"
fonts
"
,
"
defining
"
)
46
local
report_status
=
logs
.
reporter
(
"
fonts
"
,
"
status
"
)
47
local
report_mapfiles
=
logs
.
reporter
(
"
fonts
"
,
"
mapfiles
"
)
48 49
local
setmetatableindex
=
table
.
setmetatableindex
50 51
local
implement
=
interfaces
.
implement
52 53
local
chardata
=
characters
.
data
54 55
local
fonts
=
fonts
56
local
handlers
=
fonts
.
handlers
57
local
otf
=
handlers
.
otf
-- brrr
58
local
afm
=
handlers
.
afm
-- brrr
59
local
tfm
=
handlers
.
tfm
-- brrr
60
local
names
=
fonts
.
names
61
local
definers
=
fonts
.
definers
62
local
specifiers
=
fonts
.
specifiers
63
local
constructors
=
fonts
.
constructors
64
local
loggers
=
fonts
.
loggers
65
local
fontgoodies
=
fonts
.
goodies
66
local
helpers
=
fonts
.
helpers
67
local
hashes
=
fonts
.
hashes
68
local
currentfont
=
font
.
current
69
local
definefont
=
font
.
define
70 71
local
getprivateslot
=
helpers
.
getprivateslot
72 73
local
cleanname
=
names
.
cleanname
74 75
local
encodings
=
fonts
.
encodings
76
----- aglunicodes = encodings.agl.unicodes
77
local
aglunicodes
=
nil
-- delayed loading
78 79
local
nuts
=
nodes
.
nuts
80
local
tonut
=
nuts
.
tonut
81 82
local
nextchar
=
nuts
.
traversers
.
char
83 84
local
getattr
=
nuts
.
getattr
85
local
setattr
=
nuts
.
setattr
86
local
getstate
=
nuts
.
getstate
87
local
setsubtype
=
nuts
.
setsubtype
88 89
local
texgetdimen
=
tex
.
getdimen
90
local
texsetcount
=
tex
.
setcount
91
local
texget
=
tex
.
get
92 93
local
texdefinefont
=
tex
.
definefont
94
local
texsp
=
tex
.
sp
95 96
local
fontdata
=
hashes
.
identifiers
97
local
characters
=
hashes
.
characters
98
local
descriptions
=
hashes
.
descriptions
99
local
properties
=
hashes
.
properties
100
local
resources
=
hashes
.
resources
101
local
unicodes
=
hashes
.
unicodes
102
local
csnames
=
hashes
.
csnames
103
local
lastmathids
=
hashes
.
lastmathids
104
local
exheights
=
hashes
.
exheights
105
local
emwidths
=
hashes
.
emwidths
106
local
parameters
=
hashes
.
parameters
107 108
local
designsizefilename
=
fontgoodies
.
designsizes
.
filename
109 110
local
ctx_char
=
context
.
char
111
local
ctx_safechar
=
context
.
safechar
112
local
ctx_getvalue
=
context
.
getvalue
113 114
local
otffeatures
=
otf
.
features
115
local
otftables
=
otf
.
tables
116 117
local
registerotffeature
=
otffeatures
.
register
118 119
local
sequencers
=
utilities
.
sequencers
120
local
appendgroup
=
sequencers
.
appendgroup
121
local
appendaction
=
sequencers
.
appendaction
122 123
specifiers
.
contextsetups
=
specifiers
.
contextsetups
or
{
}
124
specifiers
.
contextnumbers
=
specifiers
.
contextnumbers
or
{
}
125
specifiers
.
contextmerged
=
specifiers
.
contextmerged
or
{
}
126
specifiers
.
synonyms
=
specifiers
.
synonyms
or
{
}
127 128
local
setups
=
specifiers
.
contextsetups
129
local
numbers
=
specifiers
.
contextnumbers
130
local
merged
=
specifiers
.
contextmerged
131
local
synonyms
=
specifiers
.
synonyms
132 133
storage
.
register
(
"
fonts/setups
"
,
setups
,
"
fonts.specifiers.contextsetups
"
)
134
storage
.
register
(
"
fonts/numbers
"
,
numbers
,
"
fonts.specifiers.contextnumbers
"
)
135
storage
.
register
(
"
fonts/merged
"
,
merged
,
"
fonts.specifiers.contextmerged
"
)
136
storage
.
register
(
"
fonts/synonyms
"
,
synonyms
,
"
fonts.specifiers.synonyms
"
)
137 138
-- inspect(setups)
139 140
if
environment
.
initex
then
141
setmetatableindex
(
setups
,
function
(
t
,
k
)
142
return
type
(
k
)
=
=
"
number
"
and
rawget
(
t
,
numbers
[
k
]
)
or
nil
143
end
)
144
else
145
setmetatableindex
(
setups
,
function
(
t
,
k
)
146
local
v
=
type
(
k
)
=
=
"
number
"
and
rawget
(
t
,
numbers
[
k
]
)
147
if
v
then
148
t
[
k
]
=
v
149
return
v
150
end
151
end
)
152
end
153 154
-- this will move elsewhere ...
155 156
local
function
getfontname
(
tfmdata
)
157
return
basename
(
type
(
tfmdata
)
=
=
"
number
"
and
properties
[
tfmdata
]
.
name
or
tfmdata
.
properties
.
name
)
158
end
159 160
helpers
.
name
=
getfontname
161 162
local
addformatter
=
utilities
.
strings
.
formatters
.
add
163 164
addformatter
(
formatters
,
"
font:name
"
,
[[
"'"..fontname(%s).."'"
]]
,
{
fontname
=
helpers
.
name
}
)
165
addformatter
(
formatters
,
"
font:features
"
,
[[
"'"..sequenced(%s," ",true).."'"
]]
,
{
sequenced
=
table
.
sequenced
}
)
166 167
-- ... like font-sfm or so
168 169
constructors
.
resolvevirtualtoo
=
true
-- context specific (due to resolver)
170 171
if
CONTEXTLMTXMODE
and
CONTEXTLMTXMODE
>
0
then
172
constructors
.
fixprotrusion
=
false
173
end
174 175
constructors
.
sharefonts
=
true
-- experimental
176
constructors
.
nofsharedfonts
=
0
177
constructors
.
nofsharedhashes
=
0
178
constructors
.
nofsharedvectors
=
0
179
constructors
.
noffontsloaded
=
0
180
constructors
.
autocleanup
=
true
181 182
-- we can get rid of the tfm instance when we have fast access to the
183
-- scaled character dimensions at the tex end, e.g. a fontobject.width
184
-- actually we already have some of that now as virtual keys in glyphs
185
--
186
-- flushing the kern and ligature tables from memory saves a lot (only
187
-- base mode) but it complicates vf building where the new characters
188
-- demand this data .. solution: functions that access them
189 190
-- font.getcopy = font.getfont -- we always want the table that context uses
191 192
function
constructors
.
cleanuptable
(
tfmdata
)
193
if
constructors
.
autocleanup
and
tfmdata
.
properties
.
virtualized
then
194
for
k
,
v
in
next
,
tfmdata
.
characters
do
195
if
v
.
commands
then
v
.
commands
=
nil
end
196
-- if v.kerns then v.kerns = nil end
197
end
198
end
199
end
200 201
do
202 203
local
shares
=
{
}
204
local
hashes
=
{
}
205 206 207
local
nofinstances
=
0
208
local
instances
=
setmetatableindex
(
function
(
t
,
k
)
209
nofinstances
=
nofinstances
+
1
210
t
[
k
]
=
nofinstances
211
return
nofinstances
212
end
)
213 214
function
constructors
.
trytosharefont
(
target
,
tfmdata
)
215
constructors
.
noffontsloaded
=
constructors
.
noffontsloaded
+
1
216
if
constructors
.
sharefonts
then
217
local
fonthash
=
target
.
specification
.
hash
218
if
fonthash
then
219
local
properties
=
target
.
properties
220
local
fullname
=
target
.
fullname
221
local
fontname
=
target
.
fontname
222
local
psname
=
target
.
psname
223
-- for the moment here:
224
local
instance
=
properties
.
instance
225
if
instance
then
226
local
format
=
tfmdata
.
properties
.
format
227
if
format
=
=
"
opentype
"
then
228
target
.
streamprovider
=
1
229
elseif
format
=
=
"
truetype
"
then
230
target
.
streamprovider
=
2
231
else
232
target
.
streamprovider
=
0
233
end
234
if
target
.
streamprovider
>
0
then
235
if
fullname
then
236
fullname
=
fullname
.
.
"
:
"
.
.
instances
[
instance
]
237
target
.
fullname
=
fullname
238
end
239
if
fontname
then
240
fontname
=
fontname
.
.
"
:
"
.
.
instances
[
instance
]
241
target
.
fontname
=
fontname
242
end
243
if
psname
then
244
-- this one is used for the funny prefix in font names in pdf
245
-- so it has to be kind of unique in order to avoid subset prefix
246
-- clashes being reported
247
psname
=
psname
.
.
"
:
"
.
.
instances
[
instance
]
248
target
.
psname
=
psname
249
end
250
end
251
end
252
--
253
local
sharedname
=
hashes
[
fonthash
]
254
if
sharedname
then
255
-- this is ok for context as we know that only features can mess with font definitions
256
-- so a similar hash means that the fonts are similar too
257
if
trace_defining
then
258
report_defining
(
"
font %a uses backend resources of font %a (%s)
"
,
target
.
fullname
,
sharedname
,
"
common hash
"
)
259
end
260
target
.
fullname
=
sharedname
261
properties
.
sharedwith
=
sharedname
262
constructors
.
nofsharedfonts
=
constructors
.
nofsharedfonts
+
1
263
constructors
.
nofsharedhashes
=
constructors
.
nofsharedhashes
+
1
264
else
265
-- the one takes more time (in the worst case of many cjk fonts) but it also saves
266
-- embedding time .. haha, this is interesting: when i got a clash on subset tag
267
-- collision i saw in the source that these tags are also using a hash like below
268
-- so maybe we should have an option to pass it from lua
269
local
characters
=
target
.
characters
270
local
n
=
1
271
local
t
=
{
target
.
psname
}
272
-- for the moment here:
273
if
instance
then
274
n
=
n
+
1
275
t
[
n
]
=
instance
276
end
277
--
278
local
u
=
sortedkeys
(
characters
)
279
for
i
=
1
,
#
u
do
280
local
k
=
u
[
i
]
281
n
=
n
+
1
;
t
[
n
]
=
k
282
n
=
n
+
1
;
t
[
n
]
=
characters
[
k
]
.
index
or
k
283
end
284
local
checksum
=
md5
.
HEX
(
concat
(
t
,
"
"
)
)
285
local
sharedname
=
shares
[
checksum
]
286
local
fullname
=
target
.
fullname
287
if
sharedname
then
288
if
trace_defining
then
289
report_defining
(
"
font %a uses backend resources of font %a (%s)
"
,
fullname
,
sharedname
,
"
common vector
"
)
290
end
291
fullname
=
sharedname
292
properties
.
sharedwith
=
sharedname
293
constructors
.
nofsharedfonts
=
constructors
.
nofsharedfonts
+
1
294
constructors
.
nofsharedvectors
=
constructors
.
nofsharedvectors
+
1
295
else
296
shares
[
checksum
]
=
fullname
297
end
298
target
.
fullname
=
fullname
299
hashes
[
fonthash
]
=
fullname
300
end
301
end
302
end
303
end
304 305
end
306 307
directives
.
register
(
"
fonts.checksharing
"
,
function
(
v
)
308
if
not
v
then
309
report_defining
(
"
font sharing in backend is disabled
"
)
310
end
311
constructors
.
sharefonts
=
v
312
end
)
313 314
function
definers
.
resetnullfont
(
)
315
-- resetting is needed because tikz misuses nullfont
316
local
parameters
=
fonts
.
nulldata
.
parameters
317
--
318
parameters
.
slant
=
0
-- 1
319
parameters
.
space
=
0
-- 2
320
parameters
.
space_stretch
=
0
-- 3
321
parameters
.
space_shrink
=
0
-- 4
322
parameters
.
x_height
=
0
-- 5
323
parameters
.
quad
=
0
-- 6
324
parameters
.
extra_space
=
0
-- 7
325
parameters
.
designsize
=
655360
326
--
327
constructors
.
enhanceparameters
(
parameters
)
-- official copies for us
328
--
329
definers
.
resetnullfont
=
function
(
)
end
330
end
331 332
implement
{
333
name
=
"
resetnullfont
"
,
334
onlyonce
=
true
,
335
actions
=
function
(
)
336
for
i
=
1
,
7
do
337
-- we have no direct method yet
338
context
(
[[
\fontdimen%s\nullfont\zeropoint
]]
,
i
)
339
end
340
definers
.
resetnullfont
(
)
341
end
342
}
343 344
-- this cannot be a feature initializer as there is no auto namespace
345
-- so we never enter the loop then; we can store the defaults in the tma
346
-- file (features.gpos.mkmk = 1 etc)
347 348
local
needsnodemode
=
{
-- we will have node mode by default anyway
349
-- gsub_single = true,
350
gsub_multiple
=
true
,
351
-- gsub_alternate = true,
352
-- gsub_ligature = true,
353
gsub_context
=
true
,
354
gsub_contextchain
=
true
,
355
gsub_reversecontextchain
=
true
,
356
-- chainsub = true,
357
-- reversesub = true,
358
gpos_mark2base
=
true
,
359
gpos_mark2ligature
=
true
,
360
gpos_mark2mark
=
true
,
361
gpos_cursive
=
true
,
362
-- gpos_single = true,
363
-- gpos_pair = true,
364
gpos_context
=
true
,
365
gpos_contextchain
=
true
,
366
}
367 368
otftables
.
scripts
.
auto
=
"
automatic fallback to latn when no dflt present
"
369 370
-- setmetatableindex(otffeatures.descriptions,otftables.features)
371 372
local
function
checkedscript
(
tfmdata
,
resources
,
features
)
373
local
latn
=
false
374
local
script
=
false
375
if
resources
.
features
then
376
for
g
,
list
in
next
,
resources
.
features
do
377
for
f
,
scripts
in
next
,
list
do
378
if
scripts
.
dflt
then
379
script
=
"
dflt
"
380
break
381
elseif
scripts
.
latn
then
382
latn
=
true
383
end
384
end
385
end
386
end
387
if
not
script
then
388
script
=
latn
and
"
latn
"
or
"
dflt
"
389
end
390
if
trace_automode
then
391
report_defining
(
"
auto script mode, using script %a in font %!font:name!
"
,
script
,
tfmdata
)
392
end
393
features
.
script
=
script
394
return
script
395
end
396 397
-- basemode combined with dynamics is somewhat tricky
398 399
local
function
checkedmode
(
tfmdata
,
resources
,
features
)
400
local
sequences
=
resources
.
sequences
401
if
sequences
and
#
sequences
>
0
then
402
local
script
=
features
.
script
or
"
dflt
"
403
local
language
=
features
.
language
or
"
dflt
"
404
for
feature
,
value
in
next
,
features
do
405
if
value
then
406
local
found
=
false
407
for
i
=
1
,
#
sequences
do
408
local
sequence
=
sequences
[
i
]
409
local
features
=
sequence
.
features
410
if
features
then
411
local
scripts
=
features
[
feature
]
412
if
scripts
then
413
local
languages
=
scripts
[
script
]
414
if
languages
and
languages
[
language
]
then
415
if
found
then
416
-- more than one lookup
417
if
trace_automode
then
418
report_defining
(
"
forcing mode %a, font %!font:name!, feature %a, script %a, language %a, %s
"
,
419
"
node
"
,
tfmdata
,
feature
,
script
,
language
,
"
multiple lookups
"
)
420
end
421
features
.
mode
=
"
node
"
422
return
"
node
"
423
elseif
needsnodemode
[
sequence
.
type
]
then
424
if
trace_automode
then
425
report_defining
(
"
forcing mode %a, font %!font:name!, feature %a, script %a, language %a, %s
"
,
426
"
node
"
,
tfmdata
,
feature
,
script
,
language
,
"
no base support
"
)
427
end
428
features
.
mode
=
"
node
"
429
return
"
node
"
430
else
431
-- at least one lookup
432
found
=
true
433
end
434
end
435
end
436
end
437
end
438
end
439
end
440
end
441
if
trace_automode
then
442
report_defining
(
"
forcing mode base, font %!font:name!
"
,
tfmdata
)
443
end
444
features
.
mode
=
"
base
"
-- new, or is this wrong?
445
return
"
base
"
446
end
447 448
definers
.
checkedscript
=
checkedscript
449
definers
.
checkedmode
=
checkedmode
450 451
local
function
modechecker
(
tfmdata
,
features
,
mode
)
-- we cannot adapt features as they are shared!
452
if
trace_features
then
453
report_features
(
"
fontname %!font:name!, features %!font:features!
"
,
tfmdata
,
features
)
454
end
455
local
rawdata
=
tfmdata
.
shared
.
rawdata
456
local
resources
=
rawdata
and
rawdata
.
resources
457
local
script
=
features
.
script
458
if
resources
then
459
if
script
=
=
"
auto
"
then
460
script
=
checkedscript
(
tfmdata
,
resources
,
features
)
461
end
462
if
mode
=
=
"
auto
"
then
463
mode
=
checkedmode
(
tfmdata
,
resources
,
features
)
464
end
465
else
466
report_features
(
"
missing resources for font %!font:name!
"
,
tfmdata
)
467
end
468
return
mode
469
end
470 471
registerotffeature
{
472
-- we only set the checker and leave other settings of the mode
473
-- feature as they are
474
name
=
"
mode
"
,
475
modechecker
=
modechecker
,
476
}
477 478
-- -- default = true anyway
479
--
480
-- local normalinitializer = constructors.getfeatureaction("otf","initializers","node","analyze")
481
--
482
-- local function analyzeinitializer(tfmdata,value,features) -- attr
483
-- if value == "auto" and features then
484
-- value = features.init or features.medi or features.fina or features.isol or false
485
-- end
486
-- return normalinitializer(tfmdata,value,features)
487
-- end
488
--
489
-- registerotffeature {
490
-- name = "analyze",
491
-- initializers = {
492
-- node = analyzeinitializer,
493
-- },
494
-- }
495 496
do
497 498
local
beforecopyingcharacters
=
sequencers
.
new
{
499
name
=
"
beforecopyingcharacters
"
,
500
arguments
=
"
target,original
"
,
501
}
502 503
appendgroup
(
beforecopyingcharacters
,
"
before
"
)
-- user
504
appendgroup
(
beforecopyingcharacters
,
"
system
"
)
-- private
505
appendgroup
(
beforecopyingcharacters
,
"
after
"
)
-- user
506 507
function
constructors
.
beforecopyingcharacters
(
original
,
target
)
508
local
runner
=
beforecopyingcharacters
.
runner
509
if
runner
then
510
runner
(
original
,
target
)
511
end
512
end
513 514
local
aftercopyingcharacters
=
sequencers
.
new
{
515
name
=
"
aftercopyingcharacters
"
,
516
arguments
=
"
target,original
"
,
517
}
518 519
appendgroup
(
aftercopyingcharacters
,
"
before
"
)
-- user
520
appendgroup
(
aftercopyingcharacters
,
"
system
"
)
-- private
521
appendgroup
(
aftercopyingcharacters
,
"
after
"
)
-- user
522 523
function
constructors
.
aftercopyingcharacters
(
original
,
target
)
524
local
runner
=
aftercopyingcharacters
.
runner
525
if
runner
then
526
runner
(
original
,
target
)
527
end
528
end
529 530
end
531 532
--[[ldx-- 533<p>So far we haven't really dealt with features (or whatever we want 534to pass along with the font definition. We distinguish the following 535situations:</p> 536situations:</p> 537 538<code> 539name:xetex like specs 540name@virtual font spec 541name*context specification 542</code> 543--ldx]]
--
544 545
-- currently fonts are scaled while constructing the font, so we
546
-- have to do scaling of commands in the vf at that point using e.g.
547
-- "local scale = g.parameters.factor or 1" after all, we need to
548
-- work with copies anyway and scaling needs to be done at some point;
549
-- however, when virtual tricks are used as feature (makes more
550
-- sense) we scale the commands in fonts.constructors.scale (and set the
551
-- factor there)
552 553
local
loadfont
=
definers
.
loadfont
554 555
function
definers
.
loadfont
(
specification
,
size
,
id
)
-- overloads the one in font-def
556
local
variants
=
definers
.
methods
.
variants
557
local
virtualfeatures
=
specification
.
features
.
virtual
558
if
virtualfeatures
and
virtualfeatures
.
preset
then
559
local
variant
=
variants
[
virtualfeatures
.
preset
]
560
if
variant
then
561
return
variant
(
specification
,
size
,
id
)
562
end
563
else
564
local
tfmdata
=
loadfont
(
specification
,
size
,
id
)
565
-- constructors.checkvirtualid(tfmdata,id)
566
return
tfmdata
567
end
568
end
569 570
local
function
predefined
(
specification
)
571
local
variants
=
definers
.
methods
.
variants
572
local
detail
=
specification
.
detail
573
if
detail
~
=
"
"
and
variants
[
detail
]
then
574
specification
.
features
.
virtual
=
{
preset
=
detail
}
575
end
576
return
specification
577
end
578 579
definers
.
registersplit
(
"
@
"
,
predefined
,
"
virtual
"
)
580 581
local
normalize_features
=
otffeatures
.
normalize
-- should be general
582 583
local
function
definecontext
(
name
,
t
)
-- can be shared
584
local
number
=
setups
[
name
]
and
setups
[
name
]
.
number
or
0
-- hm, numbers[name]
585
if
number
=
=
0
then
586
number
=
#
numbers
+
1
587
numbers
[
number
]
=
name
588
end
589
t
.
number
=
number
590
setups
[
name
]
=
t
591
return
number
,
t
592
end
593 594
-- {a,b,c} as table (so we don' need to parse again when it gets applied)
595
-- we will update this ... when we have foo={a,b,c} then we can keep the table
596 597
-- \definefontfeature[demo][a={b,c}]
598
-- \definefontfeature[demo][a={b=12,c={34,35}}]
599 600
local
h
=
setmetatableindex
(
function
(
t
,
k
)
601
local
v
=
"
,
"
.
.
k
.
.
"
,
"
602
t
[
k
]
=
v
603
return
v
604
end
)
605 606
-- local function removefromhash(hash,key)
607
-- local pattern = h[key]
608
-- for k in next, hash do
609
-- if k ~= key and find(h[k],pattern) then -- if find(k,",") and ...
610
-- hash[k] = nil
611
-- end
612
-- end
613
-- end
614 615
local
function
presetcontext
(
name
,
parent
,
features
)
-- will go to con and shared
616
if
features
=
=
"
"
and
find
(
parent
,
"
=
"
,
1
,
true
)
then
617
features
=
parent
618
parent
=
"
"
619
end
620
if
not
features
or
features
=
=
"
"
then
621
features
=
{
}
622
elseif
type
(
features
)
=
=
"
string
"
then
623
features
=
normalize_features
(
settings_to_hash
(
features
)
)
624
-- if type(value) == "string" and find(value,"[=:]") then
625
-- local t = settings_to_hash_colon_too(value) -- clashes with foo=file:bar
626
for
key
,
value
in
next
,
features
do
627
if
type
(
value
)
=
=
"
string
"
and
find
(
value
,
"
[=]
"
)
then
628
local
t
=
settings_to_hash
(
value
)
629
if
next
(
t
)
then
630
features
[
key
]
=
sequenced
(
normalize_features
(
t
,
true
)
,
"
,
"
)
631
end
632
end
633
end
634
else
635
features
=
normalize_features
(
features
)
636
end
637
-- todo: synonyms, and not otf bound
638
if
parent
~
=
"
"
then
639
for
p
in
gmatch
(
parent
,
"
[^, ]+
"
)
do
640
local
s
=
setups
[
p
]
641
if
s
then
642
for
k
,
v
in
next
,
s
do
643
-- no, as then we cannot overload: e.g. math,mathextra
644
-- reverted, so we only take from parent when not set
645
if
features
[
k
]
=
=
nil
then
646
features
[
k
]
=
v
647
end
648
end
649
else
650
-- just ignore an undefined one .. i.e. we can refer to not yet defined
651
end
652
end
653
end
654
-- these are auto set so in order to prevent redundant definitions
655
-- we need to preset them (we hash the features and adding a default
656
-- setting during initialization may result in a different hash)
657
--
658
-- for k,v in next, triggers do
659
-- if features[v] == nil then -- not false !
660
-- local vv = default_features[v]
661
-- if vv then features[v] = vv end
662
-- end
663
-- end
664
--
665
for
feature
,
value
in
next
,
features
do
666
if
value
=
=
nil
then
-- not false !
667
local
default
=
default_features
[
feature
]
668
if
default
~
=
nil
then
669
features
[
feature
]
=
default
670
end
671
end
672
end
673
-- sparse 'm so that we get a better hash and less test (experimental
674
-- optimization)
675
local
t
=
{
}
-- can we avoid t ?
676
for
k
,
v
in
next
,
features
do
677
-- if v then t[k] = v end
678
t
[
k
]
=
v
679
end
680
-- the number is needed for dynamic features; maybe number should always be
681
-- renewed as we can redefine features ... i need a test
682
local
number
=
setups
[
name
]
and
setups
[
name
]
.
number
or
0
683
if
number
=
=
0
then
684
number
=
#
numbers
+
1
685
numbers
[
number
]
=
name
686
end
687
--
688
t
.
number
=
number
689
-- there is the special case of combined features as we have in math but maybe
690
-- this has to change some day ... otherwise we mess up dynamics (ok, we could
691
-- impose a limit there: no combined features)
692
--
693
-- done elsewhere (!)
694
--
695
-- removefromhash(setups,name) -- can have changed (like foo,extramath)
696
--
697
setups
[
name
]
=
t
698
return
number
,
t
699
end
700 701
local
function
adaptcontext
(
pattern
,
features
)
702
local
pattern
=
topattern
(
pattern
,
false
,
true
)
703
for
name
in
next
,
setups
do
704
if
find
(
name
,
pattern
)
then
705
presetcontext
(
name
,
name
,
features
)
706
end
707
end
708
end
709 710
-- local function contextnumber(name) -- will be replaced
711
-- local t = setups[name]
712
-- if not t then
713
-- return 0
714
-- elseif t.auto then -- check where used, autolanguage / autoscript?
715
-- local lng = tonumber(tex.language)
716
-- local tag = name .. ":" .. lng
717
-- local s = setups[tag]
718
-- if s then
719
-- return s.number or 0
720
-- else
721
-- local script, language = languages.association(lng)
722
-- if t.script ~= script or t.language ~= language then
723
-- local s = fastcopy(t)
724
-- local n = #numbers + 1
725
-- setups[tag] = s
726
-- numbers[n] = tag
727
-- s.number = n
728
-- s.script = script
729
-- s.language = language
730
-- return n
731
-- else
732
-- setups[tag] = t
733
-- return t.number or 0
734
-- end
735
-- end
736
-- else
737
-- return t.number or 0
738
-- end
739
-- end
740 741
local
function
contextnumber
(
name
)
-- will be replaced
742
local
t
=
setups
[
name
]
743
return
t
and
t
.
number
or
0
744
end
745 746
local
function
mergecontext
(
currentnumber
,
extraname
,
option
)
-- number string number (used in scrp-ini
747
local
extra
=
setups
[
extraname
]
748
if
extra
then
749
local
current
=
setups
[
numbers
[
currentnumber
]
]
750
local
mergedfeatures
=
{
}
751
local
mergedname
=
nil
752
if
option
<
0
then
753
if
current
then
754
for
k
,
v
in
next
,
current
do
755
if
not
extra
[
k
]
then
756
mergedfeatures
[
k
]
=
v
757
end
758
end
759
end
760
mergedname
=
currentnumber
.
.
"
-
"
.
.
extraname
761
else
762
if
current
then
763
for
k
,
v
in
next
,
current
do
764
mergedfeatures
[
k
]
=
v
765
end
766
end
767
for
k
,
v
in
next
,
extra
do
768
mergedfeatures
[
k
]
=
v
769
end
770
mergedname
=
currentnumber
.
.
"
+
"
.
.
extraname
771
end
772
local
number
=
#
numbers
+
1
773
mergedfeatures
.
number
=
number
774
numbers
[
number
]
=
mergedname
775
merged
[
number
]
=
option
776
setups
[
mergedname
]
=
mergedfeatures
777
return
number
-- contextnumber(mergedname)
778
else
779
return
currentnumber
780
end
781
end
782 783
local
extrasets
=
{
}
784 785
setmetatableindex
(
extrasets
,
function
(
t
,
k
)
786
local
v
=
mergehashes
(
setups
,
k
)
787
t
[
k
]
=
v
788
return
v
789
end
)
790 791
local
function
mergecontextfeatures
(
currentname
,
extraname
,
how
,
mergedname
)
-- string string
792
local
extra
=
setups
[
extraname
]
or
extrasets
[
extraname
]
793
if
extra
then
794
local
current
=
setups
[
currentname
]
795
local
mergedfeatures
=
{
}
796
if
how
=
=
"
+
"
then
797
if
current
then
798
for
k
,
v
in
next
,
current
do
799
mergedfeatures
[
k
]
=
v
800
end
801
end
802
for
k
,
v
in
next
,
extra
do
803
mergedfeatures
[
k
]
=
v
804
end
805
if
trace_merge
then
806
report_features
(
"
merge %a, method %a, current %|T, extra %|T, result %|T
"
,
mergedname
,
"
add
"
,
current
or
{
}
,
extra
,
mergedfeatures
)
807
end
808
elseif
how
=
=
"
-
"
then
809
if
current
then
810
for
k
,
v
in
next
,
current
do
811
mergedfeatures
[
k
]
=
v
812
end
813
end
814
for
k
,
v
in
next
,
extra
do
815
-- only boolean features
816
if
v
=
=
true
then
817
mergedfeatures
[
k
]
=
false
818
end
819
end
820
if
trace_merge
then
821
report_features
(
"
merge %a, method %a, current %|T, extra %|T, result %|T
"
,
mergedname
,
"
subtract
"
,
current
or
{
}
,
extra
,
mergedfeatures
)
822
end
823
else
-- =
824
for
k
,
v
in
next
,
extra
do
825
mergedfeatures
[
k
]
=
v
826
end
827
if
trace_merge
then
828
report_features
(
"
merge %a, method %a, result %|T
"
,
mergedname
,
"
replace
"
,
mergedfeatures
)
829
end
830
end
831
local
number
=
#
numbers
+
1
832
mergedfeatures
.
number
=
number
833
numbers
[
number
]
=
mergedname
834
merged
[
number
]
=
option
835
setups
[
mergedname
]
=
mergedfeatures
836
return
number
837
else
838
return
numbers
[
currentname
]
or
0
839
end
840
end
841 842
local
function
registercontext
(
fontnumber
,
extraname
,
option
)
843
local
extra
=
setups
[
extraname
]
844
if
extra
then
845
local
mergedfeatures
=
{
}
846
local
mergedname
=
nil
847
if
option
<
0
then
848
mergedname
=
fontnumber
.
.
"
-
"
.
.
extraname
849
else
850
mergedname
=
fontnumber
.
.
"
+
"
.
.
extraname
851
end
852
for
k
,
v
in
next
,
extra
do
853
mergedfeatures
[
k
]
=
v
854
end
855
local
number
=
#
numbers
+
1
856
mergedfeatures
.
number
=
number
857
numbers
[
number
]
=
mergedname
858
merged
[
number
]
=
option
859
setups
[
mergedname
]
=
mergedfeatures
860
return
number
-- contextnumber(mergedname)
861
else
862
return
0
863
end
864
end
865 866
local
function
registercontextfeature
(
mergedname
,
extraname
,
how
)
867
local
extra
=
setups
[
extraname
]
868
if
extra
then
869
local
mergedfeatures
=
{
}
870
for
k
,
v
in
next
,
extra
do
871
mergedfeatures
[
k
]
=
v
872
end
873
local
number
=
#
numbers
+
1
-- we somehow end up with steps of 2
874
mergedfeatures
.
number
=
number
875
numbers
[
number
]
=
mergedname
876
merged
[
number
]
=
how
=
=
"
=
"
and
1
or
2
-- 1=replace, 2=combine
877
setups
[
mergedname
]
=
mergedfeatures
878
return
number
-- contextnumber(mergedname)
879
else
880
report_features
(
"
unknown feature %a cannot be merged into %a using method %a
"
,
extraname
,
mergedname
,
how
)
881
return
0
882
end
883
end
884 885
specifiers
.
presetcontext
=
presetcontext
886
specifiers
.
contextnumber
=
contextnumber
887
specifiers
.
mergecontext
=
mergecontext
888
specifiers
.
registercontext
=
registercontext
889
specifiers
.
definecontext
=
definecontext
890 891
-- we extend the hasher:
892 893
constructors
.
hashmethods
.
virtual
=
function
(
list
)
894
local
s
=
{
}
895
local
n
=
0
896
for
k
,
v
in
next
,
list
do
897
n
=
n
+
1
898
-- if v == true then
899
-- s[n] = k .. '=true'
900
-- elseif v == false then
901
-- s[n] = k .. '=false'
902
-- else
903
-- s[n] = k .. "=" .. v
904
-- end
905
s
[
n
]
=
k
.
.
"
=
"
.
.
tostring
(
v
)
906
end
907
if
n
>
0
then
908
sort
(
s
)
909
return
concat
(
s
,
"
+
"
)
910
end
911
end
912 913
if
not
JITSUPPORTED
then
914 915
constructors
.
hashmethods
.
normal
=
function
(
list
)
916
local
s
=
{
}
917
local
n
=
0
918
for
k
,
v
in
next
,
list
do
919
if
not
k
then
920
-- no need to add to hash
921
elseif
k
=
=
"
number
"
or
k
=
=
"
features
"
then
922
-- no need to add to hash (maybe we need a skip list)
923
else
924
n
=
n
+
1
925
if
type
(
v
)
=
=
"
table
"
then
926
-- table.sequenced
927
local
t
=
{
}
928
local
m
=
0
929
for
k
,
v
in
next
,
v
do
930
m
=
m
+
1
931
t
[
m
]
=
format
(
"
%q=%q
"
,
k
,
v
)
932
end
933
sort
(
t
)
934
s
[
n
]
=
format
(
"
%q={%s}
"
,
k
,
concat
(
t
,
"
,
"
)
)
935
else
936
s
[
n
]
=
format
(
"
%q=%q
"
,
k
,
v
)
937
end
938
end
939
end
940
if
n
>
0
then
941
sort
(
s
)
942
return
concat
(
s
,
"
+
"
)
943
end
944
end
945 946
constructors
.
hashmethods
.
virtual
=
function
(
list
)
947
local
s
=
{
}
948
local
n
=
0
949
for
k
,
v
in
next
,
list
do
950
n
=
n
+
1
951
s
[
n
]
=
format
(
"
%q=%q
"
,
k
,
v
)
952
end
953
if
n
>
0
then
954
sort
(
s
)
955
return
concat
(
s
,
"
+
"
)
956
end
957
end
958 959
end
960 961
-- end of redefine
962 963
-- local withcache = { } -- concat might be less efficient than nested tables
964
--
965
-- local function withset(name,what)
966
-- local zero = texgetattribute(0)
967
-- local hash = zero .. "+" .. name .. "*" .. what
968
-- local done = withcache[hash]
969
-- if not done then
970
-- done = mergecontext(zero,name,what)
971
-- withcache[hash] = done
972
-- end
973
-- texsetattribute(0,done)
974
-- end
975
--
976
-- local function withfnt(name,what,font)
977
-- local font = font or currentfont()
978
-- local hash = font .. "*" .. name .. "*" .. what
979
-- local done = withcache[hash]
980
-- if not done then
981
-- done = registercontext(font,name,what)
982
-- withcache[hash] = done
983
-- end
984
-- texsetattribute(0,done)
985
-- end
986 987
function
specifiers
.
showcontext
(
name
)
988
return
setups
[
name
]
or
setups
[
numbers
[
name
]
]
or
setups
[
numbers
[
tonumber
(
name
)
]
]
or
{
}
989
end
990 991
-- we need a copy as we will add (fontclass) goodies to the features and
992
-- that is bad for a shared table
993 994
-- local function splitcontext(features) -- presetcontext creates dummy here
995
-- return fastcopy(setups[features] or (presetcontext(features,"","") and setups[features]))
996
-- end
997 998
-- local function splitcontext(features) -- presetcontext creates dummy here
999
-- local sf = setups[features]
1000
-- if not sf then
1001
-- local n -- number
1002
-- if find(features,",") then
1003
-- -- let's assume a combination which is not yet defined but just specified (as in math)
1004
-- n, sf = presetcontext(features,features,"")
1005
-- else
1006
-- -- we've run into an unknown feature and or a direct spec so we create a dummy
1007
-- n, sf = presetcontext(features,"","")
1008
-- end
1009
-- end
1010
-- return fastcopy(sf)
1011
-- end
1012 1013
local
function
splitcontext
(
features
)
-- presetcontext creates dummy here
1014
local
n
,
sf
1015
if
find
(
features
,
"
,
"
)
then
1016
--
1017
-- from elsewhere (!)
1018
--
1019
-- this will become:
1020
--
1021
-- if find(features,"^reset," then
1022
setups
[
features
]
=
nil
1023
-- end
1024
-- let's assume a combination which is not yet defined but just specified (as in math)
1025
n
,
sf
=
presetcontext
(
features
,
features
,
"
"
)
1026
else
1027
sf
=
setups
[
features
]
1028
if
not
sf
then
1029
-- we've run into an unknown feature and or a direct spec so we create a dummy
1030
n
,
sf
=
presetcontext
(
features
,
"
"
,
"
"
)
1031
end
1032
end
1033
return
fastcopy
(
sf
)
1034
end
1035 1036
-- local splitter = lpeg.splitat("=")
1037
--
1038
-- local function splitcontext(features)
1039
-- local setup = setups[features]
1040
-- if setup then
1041
-- return setup
1042
-- elseif find(features,",",1,true) then
1043
-- -- This is not that efficient but handy anyway for quick and dirty tests
1044
-- -- beware, due to the way of caching setups you can get the wrong results
1045
-- -- when components change. A safeguard is to nil the cache.
1046
-- local merge = nil
1047
-- for feature in gmatch(features,"[^, ]+") do
1048
-- if find(feature,"=",1,true) then
1049
-- local k, v = lpegmatch(splitter,feature)
1050
-- if k and v then
1051
-- if not merge then
1052
-- merge = { k = v }
1053
-- else
1054
-- merge[k] = v
1055
-- end
1056
-- end
1057
-- else
1058
-- local s = setups[feature]
1059
-- if not s then
1060
-- -- skip
1061
-- elseif not merge then
1062
-- merge = s
1063
-- else
1064
-- for k, v in next, s do
1065
-- merge[k] = v
1066
-- end
1067
-- end
1068
-- end
1069
-- end
1070
-- setup = merge and presetcontext(features,"",merge) and setups[features]
1071
-- -- actually we have to nil setups[features] in order to permit redefinitions
1072
-- setups[features] = nil
1073
-- end
1074
-- return setup or (presetcontext(features,"","") and setups[features]) -- creates dummy
1075
-- end
1076 1077
specifiers
.
splitcontext
=
splitcontext
1078 1079
function
specifiers
.
contexttostring
(
name
,
kind
,
separator
,
yes
,
no
,
strict
,
omit
)
-- not used
1080
return
hash_to_string
(
1081
mergedtable
(
handlers
[
kind
]
.
features
.
defaults
or
{
}
,
setups
[
name
]
or
{
}
)
,
1082
separator
,
yes
,
no
,
strict
,
omit
or
{
"
number
"
}
1083
)
1084
end
1085 1086
local
function
starred
(
features
)
-- no longer fallbacks here
1087
local
detail
=
features
.
detail
1088
if
detail
and
detail
~
=
"
"
then
1089
features
.
features
.
normal
=
splitcontext
(
detail
)
1090
else
1091
features
.
features
.
normal
=
{
}
1092
end
1093
return
features
1094
end
1095 1096
definers
.
registersplit
(
'
*
'
,
starred
,
"
featureset
"
)
1097 1098
-- sort of xetex mode, but without [] and / as we have file: and name: etc
1099 1100
local
space
=
P
(
"
"
)
1101
local
spaces
=
space
^
0
1102
local
separator
=
S
(
"
;,
"
)
1103
local
equal
=
P
(
"
=
"
)
1104
local
sometext
=
C
(
(
1
-
equal
-
space
-
separator
)
^
1
)
1105
local
truevalue
=
P
(
"
+
"
)
*
spaces
*
sometext
*
Cc
(
true
)
1106
local
falsevalue
=
P
(
"
-
"
)
*
spaces
*
sometext
*
Cc
(
false
)
1107
local
somevalue
=
sometext
*
spaces
*
Cc
(
true
)
1108
local
keyvalue
=
sometext
*
spaces
*
equal
*
spaces
*
sometext
1109
local
pattern
=
Cf
(
Ct
(
"
"
)
*
(
space
+
separator
+
Cg
(
falsevalue
+
truevalue
+
keyvalue
+
somevalue
)
)
^
0
,
rawset
)
1110 1111
local
function
colonized
(
specification
)
1112
specification
.
features
.
normal
=
normalize_features
(
lpegmatch
(
pattern
,
specification
.
detail
)
)
1113
return
specification
1114
end
1115 1116
definers
.
registersplit
(
"
:
"
,
colonized
,
"
direct
"
)
1117 1118
-- define (two steps)
1119 1120
local
sizepattern
,
splitpattern
,
specialscale
do
1121 1122
----- space = P(" ")
1123
----- spaces = space^0
1124
local
leftparent
=
(
P
"
(
"
)
1125
local
rightparent
=
(
P
"
)
"
)
1126
local
leftbrace
=
(
P
"
{
"
)
1127
local
rightbrace
=
(
P
"
}
"
)
1128
local
withinparents
=
leftparent
*
(
1
-
rightparent
)
^
0
*
rightparent
1129
local
withinbraces
=
leftbrace
*
(
1
-
rightbrace
)
^
0
*
rightbrace
1130
local
value
=
C
(
(
withinparents
+
withinbraces
+
(
1
-
space
)
)
^
1
)
1131
local
dimension
=
C
(
(
space
/
"
"
+
P
(
1
)
)
^
1
)
1132
local
rest
=
C
(
P
(
1
)
^
0
)
1133
local
scale_none
=
Cc
(
0
)
1134
local
scale_at
=
(
P
(
"
at
"
)
+
P
(
"
@
"
)
)
*
Cc
(
1
)
*
spaces
*
dimension
-- dimension
1135
local
scale_sa
=
P
(
"
sa
"
)
*
Cc
(
2
)
*
spaces
*
dimension
-- number
1136
local
scale_mo
=
P
(
"
mo
"
)
*
Cc
(
3
)
*
spaces
*
dimension
-- number
1137
local
scale_scaled
=
P
(
"
scaled
"
)
*
Cc
(
4
)
*
spaces
*
dimension
-- number
1138
local
scale_ht
=
P
(
"
ht
"
)
*
Cc
(
5
)
*
spaces
*
dimension
-- dimension
1139
local
scale_cp
=
P
(
"
cp
"
)
*
Cc
(
6
)
*
spaces
*
dimension
-- dimension
1140 1141
specialscale
=
{
[
5
]
=
"
ht
"
,
[
6
]
=
"
cp
"
}
1142 1143
sizepattern
=
spaces
*
(
scale_at
+
scale_sa
+
scale_mo
+
scale_ht
+
scale_cp
+
scale_scaled
+
scale_none
)
1144
splitpattern
=
spaces
*
value
*
spaces
*
rest
1145 1146
end
1147 1148
function
helpers
.
splitfontpattern
(
str
)
1149
local
name
,
size
=
lpegmatch
(
splitpattern
,
str
)
1150
local
kind
,
size
=
lpegmatch
(
sizepattern
,
size
)
1151
return
name
,
kind
,
size
1152
end
1153 1154
function
helpers
.
fontpatternhassize
(
str
)
1155
local
name
,
size
=
lpegmatch
(
splitpattern
,
str
)
1156
local
kind
,
size
=
lpegmatch
(
sizepattern
,
size
)
1157
return
size
or
false
1158
end
1159 1160
local
specification
-- still needed as local ?
1161 1162
local
getspecification
=
definers
.
getspecification
1163 1164
-- we can make helper macros which saves parsing (but normaly not
1165
-- that many calls, e.g. in mk a couple of 100 and in metafun 3500)
1166 1167
local
specifiers
=
{
}
1168 1169
do
-- else too many locals
1170 1171
local
starttiming
=
statistics
.
starttiming
1172
local
stoptiming
=
statistics
.
stoptiming
1173 1174
local
setmacro
=
tokens
.
setters
.
macro
1175 1176
implement
{
1177
name
=
"
definefont_one
"
,
1178
arguments
=
"
string
"
,
1179
actions
=
function
(
str
)
1180
starttiming
(
fonts
)
1181
if
trace_defining
then
1182
report_defining
(
"
memory usage before: %s
"
,
statistics
.
memused
(
)
)
1183
report_defining
(
"
start stage one: %s
"
,
str
)
1184
end
1185
local
fullname
,
size
=
lpegmatch
(
splitpattern
,
str
)
1186
local
lookup
,
name
,
sub
,
method
,
detail
=
getspecification
(
fullname
)
1187
if
not
name
then
1188
report_defining
(
"
strange definition %a
"
,
str
)
1189
-- ctx_setdefaultfontname()
1190
elseif
name
=
=
"
unknown
"
then
1191
-- ctx_setdefaultfontname()
1192
else
1193
-- ctx_setsomefontname(name)
1194
setmacro
(
"
somefontname
"
,
name
,
"
global
"
)
1195
end
1196
-- we can also use a count for the size
1197
if
size
and
size
~
=
"
"
then
1198
local
mode
,
size
=
lpegmatch
(
sizepattern
,
size
)
1199
if
size
and
mode
then
1200
texsetcount
(
"
scaledfontmode
"
,
mode
)
1201
-- ctx_setsomefontsize(size)
1202
setmacro
(
"
somefontsize
"
,
size
)
1203
else
1204
texsetcount
(
"
scaledfontmode
"
,
0
)
1205
-- ctx_setemptyfontsize()
1206
end
1207
elseif
true
then
1208
-- so we don't need to check in tex
1209
texsetcount
(
"
scaledfontmode
"
,
2
)
1210
-- ctx_setemptyfontsize()
1211
else
1212
texsetcount
(
"
scaledfontmode
"
,
0
)
1213
-- ctx_setemptyfontsize()
1214
end
1215
specification
=
definers
.
makespecification
(
str
,
lookup
,
name
,
sub
,
method
,
detail
,
size
)
1216
if
trace_defining
then
1217
report_defining
(
"
stop stage one
"
)
1218
end
1219
end
1220
}
1221 1222
local
function
nice_cs
(
cs
)
1223
return
(
gsub
(
cs
,
"
.->
"
,
"
"
)
)
1224
end
1225 1226
local
n
=
0
1227
local
busy
=
false
1228
local
combinefeatures
=
false
1229 1230
directives
.
register
(
"
fonts.features.combine
"
,
function
(
v
)
1231
combinefeatures
=
v
1232
end
)
1233 1234 1235 1236 1237 1238 1239
implement
{
1240
name
=
"
definefont_two
"
,
1241
arguments
=
{
1242
"
boolean
"
,
"
string
"
,
"
string
"
,
"
integer
"
,
"
integer
"
,
"
string
"
,
"
string
"
,
"
string
"
,
"
string
"
,
1243
"
integer
"
,
"
integer
"
,
"
integer
"
,
"
string
"
,
"
string
"
,
"
string
"
,
"
string
"
,
"
integer
"
,
1244
}
,
1245
actions
=
function
(
1246
global
,
-- \ifx\fontclass\empty\s!false\else\s!true\fi
1247
cs
,
-- {#csname}%
1248
str
,
-- \somefontfile
1249
size
,
-- \d_font_scaled_font_size
1250
inheritancemode
,
-- \c_font_feature_inheritance_mode
1251
classfeatures
,
-- \m_font_class_features
1252
fontfeatures
,
-- \m_font_features
1253
classfallbacks
,
-- \m_font_class_fallbacks
1254
fontfallbacks
,
-- \m_font_fallbacks
1255
mathsize
,
-- \fontface
1256
textsize
,
-- \d_font_scaled_text_face
1257
relativeid
,
-- \relativefontid
1258
classgoodies
,
-- \m_font_class_goodies
1259
goodies
,
-- \m_font_goodies
1260
classdesignsize
,
-- \m_font_class_designsize
1261
fontdesignsize
,
-- \m_font_designsize
1262
scaledfontmode
-- \scaledfontmode
1263
)
1264 1265
if
trace_defining
then
1266
report_defining
(
"
start stage two: %s, size %s, features %a & %a, mode %a
"
,
str
,
size
,
classfeatures
,
fontfeatures
,
inheritancemode
)
1267
end
1268
-- name is now resolved and size is scaled cf sa/mo
1269
local
lookup
,
name
,
sub
,
method
,
detail
=
getspecification
(
str
or
"
"
)
1270
-- new (todo: inheritancemode)
1271
local
designsize
=
fontdesignsize
~
=
"
"
and
fontdesignsize
or
classdesignsize
or
"
"
1272
local
designname
=
designsizefilename
(
name
,
designsize
,
size
)
1273
if
designname
and
designname
~
=
"
"
then
1274
if
trace_defining
or
trace_designsize
then
1275
report_defining
(
"
remapping name %a, specification %a, size %a, designsize %a
"
,
name
,
designsize
,
size
,
designname
)
1276
end
1277
-- we don't catch detail here
1278
local
o_lookup
,
o_name
,
o_sub
,
o_method
,
o_detail
=
getspecification
(
designname
)
1279
if
o_lookup
and
o_lookup
~
=
"
"
then
lookup
=
o_lookup
end
1280
if
o_method
and
o_method
~
=
"
"
then
method
=
o_method
end
1281
if
o_detail
and
o_detail
~
=
"
"
then
detail
=
o_detail
end
1282
name
=
o_name
1283
sub
=
o_sub
1284
end
1285
-- so far
1286
-- some settings can have been overloaded
1287
if
lookup
and
lookup
~
=
"
"
then
1288
specification
.
lookup
=
lookup
1289
end
1290
if
relativeid
and
relativeid
~
=
"
"
then
-- experimental hook
1291
local
id
=
tonumber
(
relativeid
)
or
0
1292
specification
.
relativeid
=
id
>
0
and
id
1293
end
1294
--
1295
specification
.
name
=
name
1296
specification
.
size
=
size
1297
specification
.
sub
=
(
sub
and
sub
~
=
"
"
and
sub
)
or
specification
.
sub
1298
specification
.
mathsize
=
mathsize
1299
specification
.
textsize
=
textsize
1300
specification
.
goodies
=
goodies
1301
specification
.
cs
=
cs
1302
specification
.
global
=
global
1303
specification
.
scalemode
=
scaledfontmode
-- context specific
1304
if
detail
and
detail
~
=
"
"
then
1305
specification
.
method
=
method
or
"
*
"
1306
specification
.
detail
=
detail
1307
elseif
specification
.
detail
and
specification
.
detail
~
=
"
"
then
1308
-- already set
1309
elseif
inheritancemode
=
=
0
then
1310
-- nothing
1311
elseif
inheritancemode
=
=
1
then
1312
-- fontonly
1313
if
fontfeatures
and
fontfeatures
~
=
"
"
then
1314
specification
.
method
=
"
*
"
1315
specification
.
detail
=
fontfeatures
1316
end
1317
if
fontfallbacks
and
fontfallbacks
~
=
"
"
then
1318
specification
.
fallbacks
=
fontfallbacks
1319
end
1320
elseif
inheritancemode
=
=
2
then
1321
-- classonly
1322
if
classfeatures
and
classfeatures
~
=
"
"
then
1323
specification
.
method
=
"
*
"
1324
specification
.
detail
=
classfeatures
1325
end
1326
if
classfallbacks
and
classfallbacks
~
=
"
"
then
1327
specification
.
fallbacks
=
classfallbacks
1328
end
1329
elseif
inheritancemode
=
=
3
then
1330
-- fontfirst
1331
if
combinefeatures
then
1332
if
classfeatures
and
classfeatures
~
=
"
"
then
1333
specification
.
method
=
"
*
"
1334
if
fontfeatures
and
fontfeatures
~
=
"
"
and
fontfeatures
~
=
classfeatures
then
1335
specification
.
detail
=
classfeatures
.
.
"
,
"
.
.
fontfeatures
1336
else
1337
specification
.
detail
=
classfeatures
1338
end
1339
elseif
fontfeatures
and
fontfeatures
~
=
"
"
then
1340
specification
.
method
=
"
*
"
1341
specification
.
detail
=
fontfeatures
1342
end
1343
else
1344
if
fontfeatures
and
fontfeatures
~
=
"
"
then
1345
specification
.
method
=
"
*
"
1346
specification
.
detail
=
fontfeatures
1347
elseif
classfeatures
and
classfeatures
~
=
"
"
then
1348
specification
.
method
=
"
*
"
1349
specification
.
detail
=
classfeatures
1350
end
1351
end
1352
if
fontfallbacks
and
fontfallbacks
~
=
"
"
then
1353
specification
.
fallbacks
=
fontfallbacks
1354
elseif
classfallbacks
and
classfallbacks
~
=
"
"
then
1355
specification
.
fallbacks
=
classfallbacks
1356
end
1357
elseif
inheritancemode
=
=
4
then
1358
-- classfirst
1359
if
combinefeatures
then
1360
if
fontfeatures
and
fontfeatures
~
=
"
"
then
1361
specification
.
method
=
"
*
"
1362
if
classfeatures
and
classfeatures
~
=
"
"
and
classfeatures
~
=
fontfeatures
then
1363
specification
.
detail
=
fontfeatures
.
.
"
,
"
.
.
classfeatures
1364
else
1365
specification
.
detail
=
fontfeatures
1366
end
1367
elseif
classfeatures
and
classfeatures
~
=
"
"
then
1368
specification
.
method
=
"
*
"
1369
specification
.
detail
=
classfeatures
1370
end
1371
else
1372
if
classfeatures
and
classfeatures
~
=
"
"
then
1373
specification
.
method
=
"
*
"
1374
specification
.
detail
=
classfeatures
1375
elseif
fontfeatures
and
fontfeatures
~
=
"
"
then
1376
specification
.
method
=
"
*
"
1377
specification
.
detail
=
fontfeatures
1378
end
1379
end
1380
if
classfallbacks
and
classfallbacks
~
=
"
"
then
1381
specification
.
fallbacks
=
classfallbacks
1382
elseif
fontfallbacks
and
fontfallbacks
~
=
"
"
then
1383
specification
.
fallbacks
=
fontfallbacks
1384
end
1385
end
1386
--
1387
local
tfmdata
=
definers
.
read
(
specification
,
size
)
-- id not yet known (size in spec?)
1388
--
1389
local
lastfontid
=
0
1390
local
tfmtype
=
type
(
tfmdata
)
1391
if
tfmtype
=
=
"
table
"
then
1392
-- setting the extra characters will move elsewhere
1393
local
characters
=
tfmdata
.
characters
1394
local
parameters
=
tfmdata
.
parameters
1395
local
properties
=
tfmdata
.
properties
1396
-- we use char0 as signal; cf the spec pdf can handle this (no char in slot)
1397
characters
[
0
]
=
nil
1398
-- characters[0x00A0] = { width = parameters.space }
1399
-- characters[0x2007] = { width = characters[0x0030] and characters[0x0030].width or parameters.space } -- figure
1400
-- characters[0x2008] = { width = characters[0x002E] and characters[0x002E].width or parameters.space } -- period
1401
--
1402
local
fallbacks
=
specification
.
fallbacks
or
"
"
1403
local
mathsize
=
(
mathsize
=
=
1
or
mathsize
=
=
2
or
mathsize
=
=
3
)
and
mathsize
or
nil
-- can be unset so we test 1 2 3
1404
if
fallbacks
~
=
"
"
and
mathsize
and
not
busy
then
1405
busy
=
true
1406
-- We need this ugly hack in order to resolve fontnames (at the \TEX end). Originally
1407
-- math was done in Lua after loading (plugged into aftercopying).
1408
--
1409
-- After tl 2017 I'll also do text fallbacks this way (although backups there are done
1410
-- in a completely different way.)
1411
if
trace_defining
then
1412
report_defining
(
"
defining %a, id %a, target %a, features %a / %a, fallbacks %a / %a, step %a
"
,
1413
name
,
id
,
nice_cs
(
cs
)
,
classfeatures
,
fontfeatures
,
classfallbacks
,
fontfallbacks
,
1
)
1414
end
1415
mathematics
.
resolvefallbacks
(
tfmdata
,
specification
,
fallbacks
)
1416
context
(
function
(
)
1417
busy
=
false
1418
mathematics
.
finishfallbacks
(
tfmdata
,
specification
,
fallbacks
)
1419
local
id
=
definefont
(
tfmdata
)
1420
csnames
[
id
]
=
specification
.
cs
1421
properties
.
id
=
id
1422
definers
.
register
(
tfmdata
,
id
)
-- to be sure, normally already done
1423
texdefinefont
(
global
,
cs
,
id
)
1424
constructors
.
cleanuptable
(
tfmdata
)
1425
constructors
.
finalize
(
tfmdata
)
1426
if
trace_defining
then
1427
report_defining
(
"
defining %a, id %a, target %a, features %a / %a, fallbacks %a / %a, step %a
"
,
1428
name
,
id
,
nice_cs
(
cs
)
,
classfeatures
,
fontfeatures
,
classfallbacks
,
fontfallbacks
,
2
)
1429
end
1430
-- resolved (when designsize is used):
1431
local
size
=
round
(
tfmdata
.
parameters
.
size
or
655360
)
1432
setmacro
(
"
somefontsize
"
,
size
.
.
"
sp
"
)
1433
-- ctx_setsomefontsize(size .. "sp")
1434
texsetcount
(
"
scaledfontsize
"
,
size
)
1435
lastfontid
=
id
1436
--
1437
if
trace_defining
then
1438
report_defining
(
"
memory usage after: %s
"
,
statistics
.
memused
(
)
)
1439
report_defining
(
"
stop stage two
"
)
1440
end
1441
--
1442
texsetcount
(
"
global
"
,
"
lastfontid
"
,
lastfontid
)
1443
specifiers
[
lastfontid
]
=
{
str
,
size
}
1444
if
not
mathsize
then
1445
-- forget about it (can't happen here)
1446
elseif
mathsize
=
=
0
then
1447
-- can't happen (here)
1448
else
1449
-- maybe only 1 2 3 (we already test for this)
1450
lastmathids
[
mathsize
]
=
lastfontid
1451
end
1452
stoptiming
(
fonts
)
1453
end
)
1454
return
1455
else
1456
local
id
=
definefont
(
tfmdata
)
1457
csnames
[
id
]
=
specification
.
cs
1458
properties
.
id
=
id
1459
definers
.
register
(
tfmdata
,
id
)
-- to be sure, normally already done
1460
texdefinefont
(
global
,
cs
,
id
)
1461
constructors
.
cleanuptable
(
tfmdata
)
1462
constructors
.
finalize
(
tfmdata
)
1463
if
trace_defining
then
1464
report_defining
(
"
defining %a, id %a, target %a, features %a / %a, fallbacks %a / %a, step %a
"
,
1465
name
,
id
,
nice_cs
(
cs
)
,
classfeatures
,
fontfeatures
,
classfallbacks
,
fontfallbacks
,
"
-
"
)
1466
end
1467
-- resolved (when designsize is used):
1468
local
size
=
round
(
tfmdata
.
parameters
.
size
or
655360
)
1469
setmacro
(
"
somefontsize
"
,
size
.
.
"
sp
"
)
1470
-- ctx_setsomefontsize(size .. "sp")
1471
texsetcount
(
"
scaledfontsize
"
,
size
)
1472
lastfontid
=
id
1473
end
1474
elseif
tfmtype
=
=
"
number
"
then
1475
if
trace_defining
then
1476
report_defining
(
"
reusing %s, id %a, target %a, features %a / %a, fallbacks %a / %a, goodies %a / %a, designsize %a / %a
"
,
1477
name
,
tfmdata
,
nice_cs
(
cs
)
,
classfeatures
,
fontfeatures
,
classfallbacks
,
fontfallbacks
,
classgoodies
,
goodies
,
classdesignsize
,
fontdesignsize
)
1478
end
1479
csnames
[
tfmdata
]
=
specification
.
cs
1480
texdefinefont
(
global
,
cs
,
tfmdata
)
1481
-- resolved (when designsize is used):
1482
local
size
=
round
(
fontdata
[
tfmdata
]
.
parameters
.
size
or
0
)
1483
-- ctx_setsomefontsize(size .. "sp")
1484
setmacro
(
"
somefontsize
"
,
size
.
.
"
sp
"
)
1485
texsetcount
(
"
scaledfontsize
"
,
size
)
1486
lastfontid
=
tfmdata
1487
else
1488
report_defining
(
"
unable to define %a as %a
"
,
name
,
nice_cs
(
cs
)
)
1489
lastfontid
=
-1
1490
texsetcount
(
"
scaledfontsize
"
,
0
)
1491
-- ctx_letvaluerelax(cs) -- otherwise the current definition takes the previous one
1492
end
1493
if
trace_defining
then
1494
report_defining
(
"
memory usage after: %s
"
,
statistics
.
memused
(
)
)
1495
report_defining
(
"
stop stage two
"
)
1496
end
1497
--
1498
texsetcount
(
"
global
"
,
"
lastfontid
"
,
lastfontid
)
1499
specifiers
[
lastfontid
]
=
{
str
,
size
}
1500
if
not
mathsize
then
1501
-- forget about it
1502
elseif
mathsize
=
=
0
then
1503
-- can't happen (here)
1504
else
1505
-- maybe only 1 2 3
1506
lastmathids
[
mathsize
]
=
lastfontid
1507
end
1508
--
1509
stoptiming
(
fonts
)
1510
end
1511
}
1512 1513
implement
{
1514
name
=
"
specifiedfontspec
"
,
1515
arguments
=
"
integer
"
,
1516
actions
=
function
(
id
)
1517
local
f
=
specifiers
[
id
]
1518
if
f
then
1519
context
(
f
[
1
]
)
1520
end
1521
end
1522
}
1523 1524
implement
{
1525
name
=
"
specifiedfontsize
"
,
1526
arguments
=
"
integer
"
,
1527
actions
=
function
(
id
)
1528
local
f
=
specifiers
[
id
]
1529
if
f
then
1530
context
(
f
[
2
]
)
1531
end
1532
end
1533
}
1534 1535
implement
{
1536
name
=
"
specifiedfont
"
,
1537
arguments
=
{
"
integer
"
,
"
number
"
}
,
1538
actions
=
function
(
id
,
size
)
1539
local
f
=
specifiers
[
id
]
1540
if
f
and
size
then
1541
context
(
"
%s at %0.2p
"
,
f
[
1
]
,
size
*
f
[
2
]
)
-- we round to 2 decimals (as at the tex end)
1542
end
1543
end
1544
}
1545
--
1546 1547
local
function
define
(
specification
)
1548
--
1549
local
name
=
specification
.
name
1550
if
not
name
or
name
=
=
"
"
then
1551
return
-1
1552
else
1553
starttiming
(
fonts
)
1554
--
1555
-- following calls expect a few properties to be set:
1556
--
1557
local
lookup
,
name
,
sub
,
method
,
detail
=
getspecification
(
name
or
"
"
)
1558
--
1559
specification
.
name
=
(
name
~
=
"
"
and
name
)
or
specification
.
name
1560
--
1561
specification
.
lookup
=
specification
.
lookup
or
(
lookup
~
=
"
"
and
lookup
)
or
"
file
"
1562
specification
.
size
=
specification
.
size
or
655260
1563
specification
.
sub
=
specification
.
sub
or
(
sub
~
=
"
"
and
sub
)
or
"
"
1564
specification
.
method
=
specification
.
method
or
(
method
~
=
"
"
and
method
)
or
"
*
"
1565
specification
.
detail
=
specification
.
detail
or
(
detail
~
=
"
"
and
detail
)
or
"
"
1566
--
1567
if
type
(
specification
.
size
)
=
=
"
string
"
then
1568
specification
.
size
=
texsp
(
specification
.
size
)
or
655260
1569
end
1570
--
1571
specification
.
specification
=
"
"
-- not used
1572
specification
.
resolved
=
"
"
1573
specification
.
forced
=
"
"
1574
specification
.
features
=
{
}
-- via detail, maybe some day
1575
--
1576
-- we don't care about mathsize textsize goodies fallbacks
1577
--
1578
local
cs
=
specification
.
cs
1579
if
cs
=
=
"
"
then
1580
cs
=
nil
1581
specification
.
cs
=
nil
1582
specification
.
global
=
false
1583
elseif
specification
.
global
=
=
nil
then
1584
specification
.
global
=
false
1585
end
1586
--
1587
local
tfmdata
=
definers
.
read
(
specification
,
specification
.
size
)
1588
if
not
tfmdata
then
1589
return
-1
,
nil
1590
elseif
type
(
tfmdata
)
=
=
"
number
"
then
1591
if
cs
then
1592
texdefinefont
(
specification
.
global
,
cs
,
tfmdata
)
1593
csnames
[
tfmdata
]
=
cs
1594
end
1595
stoptiming
(
fonts
)
1596
return
tfmdata
,
fontdata
[
tfmdata
]
1597
else
1598
local
id
=
definefont
(
tfmdata
)
1599
tfmdata
.
properties
.
id
=
id
1600
definers
.
register
(
tfmdata
,
id
)
1601
if
cs
then
1602
texdefinefont
(
specification
.
global
,
cs
,
id
)
1603
csnames
[
id
]
=
cs
1604
end
1605
constructors
.
cleanuptable
(
tfmdata
)
1606
constructors
.
finalize
(
tfmdata
)
1607
stoptiming
(
fonts
)
1608
return
id
,
tfmdata
1609
end
1610
end
1611
end
1612 1613
definers
.
define
=
define
1614 1615
-- local id, cs = fonts.definers.internal { }
1616
-- local id, cs = fonts.definers.internal { number = 2 }
1617
-- local id, cs = fonts.definers.internal { name = "dejavusans" }
1618 1619
local
n
=
0
1620 1621
function
definers
.
internal
(
specification
,
cs
)
1622
specification
=
specification
or
{
}
1623
local
name
=
specification
.
name
1624
local
size
=
tonumber
(
specification
.
size
)
1625
local
number
=
tonumber
(
specification
.
number
)
1626
local
id
=
nil
1627
if
not
size
then
1628
size
=
texgetdimen
(
"
bodyfontsize
"
)
1629
end
1630
if
number
then
1631
id
=
number
1632
elseif
name
and
name
~
=
"
"
then
1633
local
cs
=
cs
or
specification
.
cs
1634
if
not
cs
then
1635
n
=
n
+
1
-- beware ... there can be many and they are often used once
1636
-- cs = formatters["internal font %s"](n)
1637
cs
=
"
internal font
"
.
.
n
1638
else
1639
specification
.
cs
=
cs
1640
end
1641
id
=
define
{
1642
name
=
name
,
1643
size
=
size
,
1644
cs
=
cs
,
1645
}
1646
end
1647
if
not
id
then
1648
id
=
currentfont
(
)
1649
end
1650
return
id
,
csnames
[
id
]
1651
end
1652 1653
local
read
1654 1655
if
CONTEXTLMTXMODE
and
CONTEXTLMTXMODE
>
0
then
-- maybe always
1656
read
=
function
(
name
,
size
)
1657
return
(
define
{
name
=
name
,
size
=
size
}
or
0
)
1658
end
1659
else
1660
read
=
definers
.
read
1661
end
1662 1663
callbacks
.
register
(
'
define_font
'
,
read
,
"
definition of fonts (tfmdata preparation)
"
)
1664 1665
-- here
1666 1667
local
infofont
=
0
1668 1669
function
fonts
.
infofont
(
)
1670
if
infofont
=
=
0
then
1671
infofont
=
define
{
name
=
"
dejavusansmono
"
,
size
=
texsp
(
"
6pt
"
)
}
1672
end
1673
return
infofont
1674
end
1675 1676
-- abstract interfacing
1677 1678
implement
{
name
=
"
tf
"
,
actions
=
function
(
)
setmacro
(
"
fontalternative
"
,
"
tf
"
)
end
}
1679
implement
{
name
=
"
bf
"
,
actions
=
function
(
)
setmacro
(
"
fontalternative
"
,
"
bf
"
)
end
}
1680
implement
{
name
=
"
it
"
,
actions
=
function
(
)
setmacro
(
"
fontalternative
"
,
"
it
"
)
end
}
1681
implement
{
name
=
"
sl
"
,
actions
=
function
(
)
setmacro
(
"
fontalternative
"
,
"
sl
"
)
end
}
1682
implement
{
name
=
"
bi
"
,
actions
=
function
(
)
setmacro
(
"
fontalternative
"
,
"
bi
"
)
end
}
1683
implement
{
name
=
"
bs
"
,
actions
=
function
(
)
setmacro
(
"
fontalternative
"
,
"
bs
"
)
end
}
1684 1685
end
1686 1687
local
enable_auto_r_scale
=
false
1688 1689
experiments
.
register
(
"
fonts.autorscale
"
,
function
(
v
)
1690
enable_auto_r_scale
=
v
1691
end
)
1692 1693
-- Not ok, we can best use a database for this. The problem is that we
1694
-- have delayed definitions and so we never know what style is taken
1695
-- as start.
1696 1697
local
calculatescale
=
constructors
.
calculatescale
1698 1699
function
constructors
.
calculatescale
(
tfmdata
,
scaledpoints
,
relativeid
,
specification
)
1700
if
specification
then
1701
local
scalemode
=
specification
.
scalemode
1702
local
special
=
scalemode
and
specialscale
[
scalemode
]
1703
if
special
then
1704
-- we also have available specification.textsize
1705
local
parameters
=
tfmdata
.
parameters
1706
-- local designsize = parameters.designsize
1707
if
special
=
=
"
ht
"
then
1708
local
height
=
parameters
.
ascender
/
parameters
.
units
1709
scaledpoints
=
scaledpoints
/
height
1710
elseif
special
=
=
"
cp
"
then
1711
local
glyph
=
tfmdata
.
descriptions
[
utfbyte
(
"
X
"
)
]
1712
local
height
=
(
glyph
and
glyph
.
height
or
parameters
.
ascender
)
/
parameters
.
units
1713
scaledpoints
=
scaledpoints
/
height
1714
end
1715
end
1716
end
1717
local
scaledpoints
,
delta
=
calculatescale
(
tfmdata
,
scaledpoints
)
1718
-- if enable_auto_r_scale and relativeid then -- for the moment this is rather context specific (we need to hash rscale then)
1719
-- local relativedata = fontdata[relativeid]
1720
-- local rfmdata = relativedata and relativedata.unscaled and relativedata.unscaled -- just use metadata instead
1721
-- local id_x_height = rfmdata and rfmdata.parameters and rfmdata.parameters.x_height
1722
-- local tf_x_height = tfmdata and tfmdata.parameters and tfmdata.parameters.x_height
1723
-- if id_x_height and tf_x_height then
1724
-- local rscale = id_x_height/tf_x_height
1725
-- delta = rscale * delta
1726
-- scaledpoints = rscale * scaledpoints
1727
-- end
1728
-- end
1729
return
round
(
scaledpoints
)
,
round
(
delta
)
1730
end
1731 1732
local
designsizes
=
constructors
.
designsizes
1733 1734
-- called quite often when in mp labels
1735
-- otf.normalizedaxis
1736 1737
function
constructors
.
hashinstance
(
specification
,
force
)
1738
local
hash
=
specification
.
hash
1739
local
size
=
specification
.
size
1740
local
fallbacks
=
specification
.
fallbacks
1741
if
force
or
not
hash
then
1742
hash
=
constructors
.
hashfeatures
(
specification
)
1743
specification
.
hash
=
hash
1744
end
1745
if
size
<
1000
and
designsizes
[
hash
]
then
1746
size
=
round
(
constructors
.
scaled
(
size
,
designsizes
[
hash
]
)
)
1747
else
1748
size
=
round
(
size
)
1749
end
1750
specification
.
size
=
size
1751
if
fallbacks
then
1752
return
hash
.
.
'
@
'
.
.
size
.
.
'
@
'
.
.
fallbacks
1753
else
1754
local
scalemode
=
specification
.
scalemode
1755
local
special
=
scalemode
and
specialscale
[
scalemode
]
1756
if
special
then
1757
return
hash
.
.
'
@
'
.
.
size
.
.
'
@
'
.
.
special
1758
else
1759
return
hash
.
.
'
@
'
.
.
size
1760
end
1761
end
1762
end
1763 1764
-- We overload the (generic) resolver:
1765 1766
local
resolvers
=
definers
.
resolvers
1767
local
hashfeatures
=
constructors
.
hashfeatures
1768 1769
function
definers
.
resolve
(
specification
)
-- overload function in font-con.lua
1770
if
not
specification
.
resolved
or
specification
.
resolved
=
=
"
"
then
-- resolved itself not per se in mapping hash
1771
local
r
=
resolvers
[
specification
.
lookup
]
1772
if
r
then
1773
r
(
specification
)
1774
end
1775
end
1776
if
specification
.
forced
=
=
"
"
then
1777
specification
.
forced
=
nil
1778
else
1779
specification
.
forced
=
specification
.
forced
1780
end
1781
-- goodies are a context specific thing and are not always defined
1782
-- as feature, so we need to make sure we add them here before
1783
-- hashing because otherwise we get funny goodies applied
1784
local
goodies
=
specification
.
goodies
1785
if
goodies
and
goodies
~
=
"
"
then
1786
-- this adapts the features table so it has best be a copy
1787
local
normal
=
specification
.
features
.
normal
1788
if
not
normal
then
1789
specification
.
features
.
normal
=
{
goodies
=
goodies
}
1790
elseif
not
normal
.
goodies
then
1791
local
g
=
normal
.
goodies
1792
if
g
and
g
~
=
"
"
then
1793
normal
.
goodies
=
formatters
[
"
%s,%s
"
]
(
g
,
goodies
)
1794
else
1795
normal
.
goodies
=
goodies
1796
end
1797
end
1798
end
1799
-- so far for goodie hacks
1800
local
hash
=
hashfeatures
(
specification
)
1801
local
name
=
specification
.
name
or
"
badfont
"
1802
local
sub
=
specification
.
sub
1803
if
sub
and
sub
~
=
"
"
then
1804
specification
.
hash
=
lower
(
name
.
.
"
@
"
.
.
sub
.
.
'
@
'
.
.
hash
)
1805
else
1806
specification
.
hash
=
lower
(
name
.
.
"
@
"
.
.
'
@
'
.
.
hash
)
1807
end
1808
--
1809
return
specification
1810
end
1811 1812
-- soon to be obsolete:
1813 1814
local
mappings
=
fonts
.
mappings
1815 1816
local
loaded
=
{
-- prevent loading (happens in cont-sys files)
1817
-- ["original-base.map" ] = true,
1818
-- ["original-ams-base.map" ] = true,
1819
-- ["original-ams-euler.map"] = true,
1820
-- ["original-public-lm.map"] = true,
1821
}
1822 1823
function
mappings
.
loadfile
(
name
)
1824
name
=
file
.
addsuffix
(
name
,
"
map
"
)
1825
if
not
loaded
[
name
]
then
1826
if
trace_mapfiles
then
1827
report_mapfiles
(
"
loading map file %a
"
,
name
)
1828
end
1829
lpdf
.
setmapfile
(
name
)
1830
loaded
[
name
]
=
true
1831
end
1832
end
1833 1834
local
loaded
=
{
-- prevent double loading
1835
}
1836 1837
function
mappings
.
loadline
(
how
,
line
)
1838
if
line
then
1839
how
=
how
.
.
"
"
.
.
line
1840
elseif
how
=
=
"
"
then
1841
how
=
"
=
"
.
.
line
1842
end
1843
if
not
loaded
[
how
]
then
1844
if
trace_mapfiles
then
1845
report_mapfiles
(
"
processing map line %a
"
,
line
)
1846
end
1847
lpdf
.
setmapline
(
how
)
1848
loaded
[
how
]
=
true
1849
end
1850
end
1851 1852
function
mappings
.
reset
(
)
1853
lpdf
.
setmapfile
(
"
"
)
-- tricky ... backend related
1854
end
1855 1856
implement
{
1857
name
=
"
loadmapfile
"
,
1858
actions
=
mappings
.
loadfile
,
1859
arguments
=
"
string
"
1860
}
1861 1862
implement
{
1863
name
=
"
loadmapline
"
,
1864
actions
=
mappings
.
loadline
,
1865
arguments
=
"
string
"
1866
}
1867 1868
implement
{
1869
name
=
"
resetmapfiles
"
,
1870
actions
=
mappings
.
reset
,
1871
arguments
=
"
string
"
1872
}
1873 1874
-- we need an 'do after the banner hook'
1875 1876
-- => commands
1877 1878
local
pattern
=
P
(
"
P
"
)
1879
*
(
lpeg
.
patterns
.
hexdigit
^
4
/
function
(
s
)
return
tonumber
(
s
,
16
)
end
)
1880
*
P
(
-1
)
1881 1882
local
function
nametoslot
(
name
)
-- also supports PXXXXX (4+ positions)
1883
local
t
=
type
(
name
)
1884
if
t
=
=
"
string
"
then
1885
local
unic
=
unicodes
[
true
]
1886
local
slot
=
unic
[
name
]
1887
if
slot
then
1888
return
slot
1889
end
1890
--
1891
local
slot
=
unic
[
gsub
(
name
,
"
_
"
,
"
"
)
]
or
unic
[
gsub
(
name
,
"
_
"
,
"
-
"
)
]
or
1892
unic
[
gsub
(
name
,
"
-
"
,
"
"
)
]
or
unic
[
gsub
(
name
,
"
-
"
,
"
_
"
)
]
or
1893
unic
[
gsub
(
name
,
"
"
,
"
_
"
)
]
or
unic
[
gsub
(
name
,
"
"
,
"
-
"
)
]
1894
if
slot
then
1895
return
slot
1896
end
1897
--
1898
if
not
aglunicodes
then
1899
aglunicodes
=
encodings
.
agl
.
unicodes
1900
end
1901
local
char
=
characters
[
true
]
1902
local
slot
=
aglunicodes
[
name
]
1903
if
slot
and
char
[
slot
]
then
1904
return
slot
1905
end
1906
local
slot
=
lpegmatch
(
pattern
,
name
)
1907
if
slot
and
char
[
slot
]
then
1908
return
slot
1909
end
1910
-- not in font
1911
elseif
t
=
=
"
number
"
then
1912
if
characters
[
true
]
[
name
]
then
1913
return
slot
1914
else
1915
-- not in font
1916
end
1917
end
1918
end
1919 1920
local
found
=
{
}
1921 1922
local
function
descriptiontoslot
(
name
)
1923
local
t
=
type
(
name
)
1924
if
t
=
=
"
string
"
then
1925
-- slow
1926
local
list
=
sortedkeys
(
chardata
)
-- can be a cache with weak tables
1927
local
slot
=
found
[
name
]
1928
local
char
=
characters
[
true
]
1929
if
slot
then
1930
return
char
[
slot
]
and
slot
or
nil
1931
end
1932
local
NAME
=
upper
(
name
)
1933
for
i
=
1
,
#
list
do
1934
slot
=
list
[
i
]
1935
local
c
=
chardata
[
slot
]
1936
local
d
=
c
.
description
1937
if
d
=
=
NAME
then
1938
found
[
name
]
=
slot
1939
return
char
[
slot
]
and
slot
or
nil
1940
end
1941
end
1942
for
i
=
1
,
#
list
do
1943
slot
=
list
[
i
]
1944
local
c
=
chardata
[
slot
]
1945
local
s
=
c
.
synonyms
1946
if
s
then
1947
for
i
=
1
,
#
s
do
1948
local
si
=
s
[
i
]
1949
if
si
=
=
name
then
1950
found
[
name
]
=
si
1951
return
char
[
slot
]
and
slot
or
nil
1952
end
1953
end
1954
end
1955
end
1956
for
i
=
1
,
#
list
do
1957
slot
=
list
[
i
]
1958
local
c
=
chardata
[
slot
]
1959
local
d
=
c
.
description
1960
if
d
and
find
(
d
,
NAME
)
then
1961
found
[
name
]
=
slot
1962
return
char
[
slot
]
and
slot
or
nil
1963
end
1964
end
1965
for
i
=
1
,
#
list
do
1966
slot
=
list
[
i
]
1967
local
c
=
chardata
[
slot
]
1968
local
s
=
c
.
synonyms
1969
if
s
then
1970
for
i
=
1
,
#
s
do
1971
local
si
=
s
[
i
]
1972
if
find
(
s
[
i
]
,
name
)
then
1973
found
[
name
]
=
si
1974
return
char
[
slot
]
and
slot
or
nil
1975
end
1976
end
1977
end
1978
end
1979
-- not in font
1980
elseif
t
=
=
"
number
"
then
1981
if
characters
[
true
]
[
name
]
then
1982
return
slot
1983
else
1984
-- not in font
1985
end
1986
end
1987
end
1988 1989
local
function
indextoslot
(
font
,
index
)
1990
if
not
index
then
1991
index
=
font
1992
font
=
true
1993
end
1994
local
r
=
resources
[
font
]
1995
if
r
then
1996
local
indices
=
r
.
indices
1997
if
not
indices
then
1998
indices
=
{
}
1999
local
c
=
characters
[
font
]
2000
for
unicode
,
data
in
next
,
c
do
2001
local
di
=
data
.
index
2002
if
di
then
2003
indices
[
di
]
=
unicode
2004
end
2005
end
2006
r
.
indices
=
indices
2007
end
2008
return
indices
[
tonumber
(
index
)
]
2009
end
2010
end
2011 2012
do
-- else too many locals
2013 2014
local
entities
=
characters
.
entities
2015
local
lowered
=
{
}
-- delayed initialization
2016 2017
setmetatableindex
(
lowered
,
function
(
t
,
k
)
2018
for
k
,
v
in
next
,
entities
do
2019
local
l
=
lower
(
k
)
2020
if
not
entities
[
l
]
then
2021
lowered
[
l
]
=
v
2022
end
2023
end
2024
setmetatableindex
(
lowered
,
nil
)
2025
return
lowered
[
k
]
2026
end
)
2027 2028
local
methods
=
{
2029
-- entity
2030
e
=
function
(
name
)
2031
return
entities
[
name
]
or
lowered
[
name
]
or
name
2032
end
,
2033
-- hexadecimal unicode
2034
x
=
function
(
name
)
2035
local
n
=
tonumber
(
name
,
16
)
2036
return
n
and
utfchar
(
n
)
or
name
2037
end
,
2038
-- decimal unicode
2039
d
=
function
(
name
)
2040
local
n
=
tonumber
(
name
)
2041
return
n
and
utfchar
(
n
)
or
name
2042
end
,
2043
-- hexadecimal index (slot)
2044
s
=
function
(
name
)
2045
local
n
=
tonumber
(
name
,
16
)
2046
local
n
=
n
and
indextoslot
(
n
)
2047
return
n
and
utfchar
(
n
)
or
name
2048
end
,
2049
-- decimal index
2050
i
=
function
(
name
)
2051
local
n
=
tonumber
(
name
)
2052
local
n
=
n
and
indextoslot
(
n
)
2053
return
n
and
utfchar
(
n
)
or
name
2054
end
,
2055
-- name
2056
n
=
function
(
name
)
2057
local
n
=
nametoslot
(
name
)
2058
return
n
and
utfchar
(
n
)
or
name
2059
end
,
2060
-- unicode description (synonym)
2061
u
=
function
(
name
)
2062
local
n
=
descriptiontoslot
(
name
,
false
)
2063
return
n
and
utfchar
(
n
)
or
name
2064
end
,
2065
-- all
2066
a
=
function
(
name
)
2067
local
n
=
nametoslot
(
name
)
or
descriptiontoslot
(
name
)
2068
return
n
and
utfchar
(
n
)
or
name
2069
end
,
2070
-- char
2071
c
=
function
(
name
)
2072
return
name
2073
end
,
2074
}
2075 2076
-- -- nicer:
2077
--
2078
-- setmetatableindex(methods,function(t,k) return methods.c end)
2079
--
2080
-- local splitter = (C(1) * P(":") + Cc("c")) * C(P(1)^1) / function(method,name)
2081
-- return methods[method](name)
2082
-- end
2083
--
2084
-- -- more efficient:
2085 2086
local
splitter
=
C
(
1
)
*
P
(
"
:
"
)
*
C
(
P
(
1
)
^
1
)
/
function
(
method
,
name
)
2087
local
action
=
methods
[
method
]
2088
return
action
and
action
(
name
)
or
name
2089
end
2090 2091
local
function
tochar
(
str
)
2092
local
t
=
type
(
str
)
2093
if
t
=
=
"
number
"
then
2094
return
utfchar
(
str
)
2095
elseif
t
=
=
"
string
"
then
2096
return
lpegmatch
(
splitter
,
str
)
or
str
2097
else
2098
return
str
2099
end
2100
end
2101 2102
helpers
.
nametoslot
=
nametoslot
2103
helpers
.
descriptiontoslot
=
descriptiontoslot
2104
helpers
.
indextoslot
=
indextoslot
2105
helpers
.
tochar
=
tochar
2106 2107
-- interfaces:
2108 2109
implement
{
2110
name
=
"
fontchar
"
,
2111
actions
=
{
nametoslot
,
ctx_char
}
,
2112
arguments
=
"
string
"
,
2113
}
2114 2115
implement
{
2116
name
=
"
fontcharbyindex
"
,
2117
actions
=
{
indextoslot
,
ctx_char
}
,
2118
arguments
=
"
integer
"
,
2119
}
2120 2121
implement
{
2122
name
=
"
tochar
"
,
2123
actions
=
{
tochar
,
ctx_safechar
}
,
2124
arguments
=
"
string
"
,
2125
}
2126 2127
end
2128 2129
-- this will change ...
2130 2131
function
loggers
.
reportdefinedfonts
(
)
2132
if
trace_usage
then
2133
local
t
,
tn
=
{
}
,
0
2134
for
id
,
data
in
sortedhash
(
fontdata
)
do
2135
local
properties
=
data
.
properties
or
{
}
2136
local
parameters
=
data
.
parameters
or
{
}
2137
tn
=
tn
+
1
2138
t
[
tn
]
=
{
2139
formatters
[
"
%03i
"
]
(
id
or
0
)
,
2140
formatters
[
"
%p
"
]
(
parameters
.
size
or
0
)
,
2141
properties
.
type
or
"
real
"
,
2142
properties
.
format
or
"
unknown
"
,
2143
properties
.
name
or
"
"
,
2144
properties
.
psname
or
"
"
,
2145
properties
.
fullname
or
"
"
,
2146
properties
.
sharedwith
or
"
"
,
2147
}
2148
end
2149
formatcolumns
(
t
,
"
"
)
2150
--
2151
logs
.
startfilelogging
(
report
,
"
defined fonts
"
)
2152
for
k
=
1
,
tn
do
2153
report
(
t
[
k
]
)
2154
end
2155
logs
.
stopfilelogging
(
)
2156
end
2157
end
2158 2159
logs
.
registerfinalactions
(
loggers
.
reportdefinedfonts
)
2160 2161
function
loggers
.
reportusedfeatures
(
)
2162
-- numbers, setups, merged
2163
if
trace_usage
then
2164
local
t
,
n
=
{
}
,
#
numbers
2165
for
i
=
1
,
n
do
2166
local
name
=
numbers
[
i
]
2167
local
setup
=
setups
[
name
]
2168
local
n
=
setup
.
number
2169
setup
.
number
=
nil
-- we have no reason to show this
2170
t
[
i
]
=
{
i
,
name
,
sequenced
(
setup
,
false
,
true
)
}
-- simple mode
2171
setup
.
number
=
n
-- restore it (normally not needed as we're done anyway)
2172
end
2173
formatcolumns
(
t
,
"
"
)
2174
logs
.
startfilelogging
(
report
,
"
defined featuresets
"
)
2175
for
k
=
1
,
n
do
2176
report
(
t
[
k
]
)
2177
end
2178
logs
.
stopfilelogging
(
)
2179
end
2180
end
2181 2182
logs
.
registerfinalactions
(
loggers
.
reportusedfeatures
)
2183 2184
-- maybe move this to font-log.lua:
2185 2186
statistics
.
register
(
"
font engine
"
,
function
(
)
2187
local
elapsed
=
statistics
.
elapsedseconds
(
fonts
)
2188
local
nofshared
=
constructors
.
nofsharedfonts
or
0
2189
local
nofloaded
=
constructors
.
noffontsloaded
or
0
2190
if
nofshared
>
0
then
2191
return
format
(
"
otf %0.3f, afm %0.3f, tfm %0.3f, %s instances, %s shared in backend, %s common vectors, %s common hashes, load time %s
"
,
2192
otf
.
version
,
afm
.
version
,
tfm
.
version
,
nofloaded
,
2193
nofshared
,
constructors
.
nofsharedvectors
,
constructors
.
nofsharedhashes
,
2194
elapsed
)
2195
elseif
nofloaded
>
0
and
elapsed
then
2196
return
format
(
"
otf %0.3f, afm %0.3f, tfm %0.3f, %s instances, load time %s
"
,
2197
otf
.
version
,
afm
.
version
,
tfm
.
version
,
nofloaded
,
2198
elapsed
)
2199
else
2200
return
format
(
"
otf %0.3f, afm %0.3f, tfm %0.3f
"
,
2201
otf
.
version
,
afm
.
version
,
tfm
.
version
)
2202
end
2203
end
)
2204 2205
-- experimental mechanism for Mojca:
2206
--
2207
-- fonts.definetypeface {
2208
-- name = "mainbodyfont-light",
2209
-- preset = "antykwapoltawskiego-light",
2210
-- }
2211
--
2212
-- fonts.definetypeface {
2213
-- name = "mojcasfavourite",
2214
-- preset = "antykwapoltawskiego",
2215
-- normalweight = "light",
2216
-- boldweight = "bold",
2217
-- width = "condensed",
2218
-- }
2219 2220
local
Shapes
=
{
2221
serif
=
"
Serif
"
,
2222
sans
=
"
Sans
"
,
2223
mono
=
"
Mono
"
,
2224
}
2225 2226
local
ctx_startfontclass
=
context
.
startfontclass
2227
local
ctx_stopfontclass
=
context
.
stopfontclass
2228
local
ctx_definefontsynonym
=
context
.
definefontsynonym
2229
local
ctx_dofastdefinetypeface
=
context
.
dofastdefinetypeface
2230 2231
function
fonts
.
definetypeface
(
name
,
t
)
2232
if
type
(
name
)
=
=
"
table
"
then
2233
-- {name=abc,k=v,...}
2234
t
=
name
2235
elseif
t
then
2236
if
type
(
t
)
=
=
"
string
"
then
2237
-- "abc", "k=v,..."
2238
t
=
settings_to_hash
(
name
)
2239
else
2240
-- "abc", {k=v,...}
2241
end
2242
t
.
name
=
t
.
name
or
name
2243
else
2244
-- "name=abc,k=v,..."
2245
t
=
settings_to_hash
(
name
)
2246
end
2247
local
p
=
t
.
preset
and
fonts
.
typefaces
[
t
.
preset
]
or
{
}
2248
local
name
=
t
.
name
or
"
unknowntypeface
"
2249
local
shortcut
=
t
.
shortcut
or
p
.
shortcut
or
"
rm
"
2250
local
size
=
t
.
size
or
p
.
size
or
"
default
"
2251
local
shape
=
t
.
shape
or
p
.
shape
or
"
serif
"
2252
local
fontname
=
t
.
fontname
or
p
.
fontname
or
"
unknown
"
2253
local
normalweight
=
t
.
normalweight
or
t
.
weight
or
p
.
normalweight
or
p
.
weight
or
"
normal
"
2254
local
boldweight
=
t
.
boldweight
or
t
.
weight
or
p
.
boldweight
or
p
.
weight
or
"
normal
"
2255
local
normalwidth
=
t
.
normalwidth
or
t
.
width
or
p
.
normalwidth
or
p
.
width
or
"
normal
"
2256
local
boldwidth
=
t
.
boldwidth
or
t
.
width
or
p
.
boldwidth
or
p
.
width
or
"
normal
"
2257
Shape
=
Shapes
[
shape
]
or
"
Serif
"
2258
ctx_startfontclass
{
name
}
2259
ctx_definefontsynonym
(
{
formatters
[
"
%s
"
]
(
Shape
)
}
,
{
formatters
[
"
spec:%s-%s-regular-%s
"
]
(
fontname
,
normalweight
,
normalwidth
)
}
)
2260
ctx_definefontsynonym
(
{
formatters
[
"
%sBold
"
]
(
Shape
)
}
,
{
formatters
[
"
spec:%s-%s-regular-%s
"
]
(
fontname
,
boldweight
,
boldwidth
)
}
)
2261
ctx_definefontsynonym
(
{
formatters
[
"
%sBoldItalic
"
]
(
Shape
)
}
,
{
formatters
[
"
spec:%s-%s-italic-%s
"
]
(
fontname
,
boldweight
,
boldwidth
)
}
)
2262
ctx_definefontsynonym
(
{
formatters
[
"
%sItalic
"
]
(
Shape
)
}
,
{
formatters
[
"
spec:%s-%s-italic-%s
"
]
(
fontname
,
normalweight
,
normalwidth
)
}
)
2263
ctx_stopfontclass
(
)
2264
local
settings
=
sequenced
(
{
features
=
t
.
features
}
,
"
,
"
)
2265
ctx_dofastdefinetypeface
(
name
,
shortcut
,
shape
,
size
,
settings
)
2266
end
2267 2268
implement
{
2269
name
=
"
definetypeface
"
,
2270
actions
=
fonts
.
definetypeface
,
2271
arguments
=
"
2 strings
"
2272
}
2273 2274
function
fonts
.
current
(
)
-- todo: also handle name
2275
return
fontdata
[
currentfont
(
)
]
or
fontdata
[
0
]
2276
end
2277 2278
function
fonts
.
currentid
(
)
2279
return
currentfont
(
)
or
0
2280
end
2281 2282
-- for the moment here, this will become a chain of extras that is
2283
-- hooked into the ctx registration (or scaler or ...)
2284 2285
local
dimenfactors
=
number
.
dimenfactors
2286 2287
function
helpers
.
dimenfactor
(
unit
,
id
)
2288
if
unit
=
=
"
ex
"
then
2289
return
id
and
exheights
[
id
]
or
282460
-- lm 10pt
2290
elseif
unit
=
=
"
em
"
then
2291
return
id
and
emwidths
[
id
]
or
655360
-- lm 10pt
2292
else
2293
local
du
=
dimenfactors
[
unit
]
2294
return
du
and
1
/
du
or
tonumber
(
unit
)
or
1
2295
end
2296
end
2297 2298
local
function
digitwidth
(
font
)
-- max(quad/2,wd(0..9))
2299
local
tfmdata
=
fontdata
[
font
]
2300
local
parameters
=
tfmdata
.
parameters
2301
local
width
=
parameters
.
digitwidth
2302
if
not
width
then
2303
width
=
round
(
parameters
.
quad
/
2
)
-- maybe tex.scale
2304
local
characters
=
tfmdata
.
characters
2305
for
i
=
48
,
57
do
2306
local
wd
=
round
(
characters
[
i
]
.
width
)
2307
if
wd
>
width
then
2308
width
=
wd
2309
end
2310
end
2311
parameters
.
digitwidth
=
width
2312
end
2313
return
width
2314
end
2315 2316
helpers
.
getdigitwidth
=
digitwidth
2317
helpers
.
setdigitwidth
=
digitwidth
2318 2319
--
2320 2321
function
helpers
.
getparameters
(
tfmdata
)
2322
local
p
=
{
}
2323
local
m
=
p
2324
local
parameters
=
tfmdata
.
parameters
2325
while
true
do
2326
for
k
,
v
in
next
,
parameters
do
2327
m
[
k
]
=
v
2328
end
2329
parameters
=
getmetatable
(
parameters
)
2330
parameters
=
parameters
and
parameters
.
__index
2331
if
type
(
parameters
)
=
=
"
table
"
then
2332
m
=
{
}
2333
p
.
metatable
=
m
2334
else
2335
break
2336
end
2337
end
2338
return
p
2339
end
2340 2341
if
environment
.
initex
then
2342 2343
local
function
names
(
t
)
2344
local
nt
=
#
t
2345
if
nt
>
0
then
2346
local
n
=
{
}
2347
for
i
=
1
,
nt
do
2348
n
[
i
]
=
t
[
i
]
.
name
2349
end
2350
return
concat
(
n
,
"
"
)
2351
else
2352
return
"
-
"
2353
end
2354
end
2355 2356
statistics
.
register
(
"
font processing
"
,
function
(
)
2357
local
l
=
{
}
2358
for
what
,
handler
in
table
.
sortedpairs
(
handlers
)
do
2359
local
features
=
handler
and
handler
.
features
2360
if
features
then
2361
l
[
#
l
+
1
]
=
format
(
"
[%s (base initializers: %s) (base processors: %s) (base manipulators: %s) (node initializers: %s) (node processors: %s) (node manipulators: %s)]
"
,
2362
what
,
2363
names
(
features
.
initializers
.
base
)
,
2364
names
(
features
.
processors
.
base
)
,
2365
names
(
features
.
manipulators
.
base
)
,
2366
names
(
features
.
initializers
.
node
)
,
2367
names
(
features
.
processors
.
node
)
,
2368
names
(
features
.
manipulators
.
node
)
2369
)
2370
end
2371
end
2372
return
concat
(
l
,
"
|
"
)
2373
end
)
2374 2375
end
2376 2377
-- redefinition
2378 2379
-- local hashes = fonts.hashes
2380
-- local emwidths = hashes.emwidths
2381
-- local exheights = hashes.exheights
2382 2383
setmetatableindex
(
dimenfactors
,
function
(
t
,
k
)
2384
if
k
=
=
"
ex
"
then
2385
return
1
/
exheights
[
currentfont
(
)
]
2386
elseif
k
=
=
"
em
"
then
2387
return
1
/
emwidths
[
currentfont
(
)
]
2388
elseif
k
=
=
"
pct
"
or
k
=
=
"
%
"
then
2389
return
1
/
(
texget
(
"
hsize
"
)
/
100
)
2390
else
2391
-- error("wrong dimension: " .. (s or "?")) -- better a message
2392
return
false
2393
end
2394
end
)
2395 2396
dimenfactors
.
ex
=
nil
2397
dimenfactors
.
em
=
nil
2398
dimenfactors
[
"
%
"
]
=
nil
2399
dimenfactors
.
pct
=
nil
2400 2401
--[[ldx-- 2402<p>Before a font is passed to <l n='tex'/> we scale it. Here we also need 2403to scale virtual characters.</p> 2404--ldx]]
--
2405 2406
do
2407 2408
-- can become luat-tex.lua
2409 2410
local
texsetglyphdata
=
tex
.
setglyphdata
2411
local
texgetglyphdata
=
tex
.
getglyphdata
2412 2413
if
not
texsetglyphdata
then
2414 2415
local
texsetattribute
=
tex
.
setattribute
2416
local
texgetattribute
=
tex
.
getattribute
2417 2418
texsetglyphdata
=
function
(
n
)
return
texsetattribute
(
0
,
n
)
end
2419
texgetglyphdata
=
function
(
)
return
texgetattribute
(
0
)
end
2420 2421
tex
.
setglyphdata
=
texsetglyphdata
2422
tex
.
getglyphdata
=
texgetglyphdata
2423 2424
end
2425 2426
-- till here
2427 2428
local
setmacro
=
tokens
.
setters
.
macro
2429 2430
function
constructors
.
currentfonthasfeature
(
n
)
2431
local
f
=
fontdata
[
currentfont
(
)
]
2432
if
not
f
then
return
end
f
=
f
.
shared
2433
if
not
f
then
return
end
f
=
f
.
rawdata
2434
if
not
f
then
return
end
f
=
f
.
resources
2435
if
not
f
then
return
end
f
=
f
.
features
2436
return
f
and
(
f
.
gpos
[
n
]
or
f
.
gsub
[
n
]
)
2437
end
2438 2439
local
ctx_doifelse
=
commands
.
doifelse
2440
local
ctx_doif
=
commands
.
doif
2441 2442
implement
{
2443
name
=
"
doifelsecurrentfonthasfeature
"
,
2444
actions
=
{
constructors
.
currentfonthasfeature
,
ctx_doifelse
}
,
2445
arguments
=
"
string
"
2446
}
2447 2448
local
f_strip
=
formatters
[
"
%0.2fpt
"
]
-- normally this value is changed only once
2449
local
stripper
=
lpeg
.
patterns
.
stripzeros
2450 2451
local
cache
=
{
}
2452 2453
local
hows
=
{
2454
[
"
+
"
]
=
"
add
"
,
2455
[
"
-
"
]
=
"
subtract
"
,
2456
[
"
=
"
]
=
"
replace
"
,
2457
}
2458 2459
local
function
setfeature
(
how
,
parent
,
name
,
font
)
-- 0/1 test temporary for testing
2460
if
not
how
or
how
=
=
0
then
2461
if
trace_features
and
texgetglyphdata
(
)
~
=
0
then
2462
report_cummulative
(
"
font %!font:name!, reset
"
,
fontdata
[
font
or
true
]
)
2463
end
2464
texsetglyphdata
(
0
)
2465
elseif
how
=
=
true
or
how
=
=
1
then
2466
local
hash
=
"
feature >
"
.
.
parent
2467
local
done
=
cache
[
hash
]
2468
if
trace_features
and
done
then
2469
report_cummulative
(
"
font %!font:name!, revive %a : %!font:features!
"
,
fontdata
[
font
or
true
]
,
parent
,
setups
[
numbers
[
done
]
]
)
2470
end
2471
texsetglyphdata
(
done
or
0
)
2472
else
2473
local
full
=
parent
.
.
how
.
.
name
2474
local
hash
=
"
feature >
"
.
.
full
2475
local
done
=
cache
[
hash
]
2476
if
not
done
then
2477
local
n
=
setups
[
full
]
2478
if
n
then
2479
-- already defined
2480
else
2481
n
=
mergecontextfeatures
(
parent
,
name
,
how
,
full
)
2482
end
2483
done
=
registercontextfeature
(
hash
,
full
,
how
)
2484
cache
[
hash
]
=
done
2485
if
trace_features
then
2486
report_cummulative
(
"
font %!font:name!, %s %a : %!font:features!
"
,
fontdata
[
font
or
true
]
,
hows
[
how
]
,
full
,
setups
[
numbers
[
done
]
]
)
2487
end
2488
end
2489
texsetglyphdata
(
done
)
2490
end
2491
end
2492 2493
local
function
resetfeature
(
)
2494
if
trace_features
and
texgetglyphdata
(
)
~
=
0
then
2495
report_cummulative
(
"
font %!font:name!, reset
"
,
fontdata
[
true
]
)
2496
end
2497
texsetglyphdata
(
0
)
2498
end
2499 2500
local
function
setfontfeature
(
tag
)
2501
texsetglyphdata
(
contextnumber
(
tag
)
)
2502
end
2503 2504
local
function
resetfontfeature
(
)
2505
texsetglyphdata
(
0
)
2506
end
2507 2508
implement
{
2509
name
=
"
nbfs
"
,
2510
arguments
=
"
dimen
"
,
2511
actions
=
function
(
d
)
2512
context
(
lpegmatch
(
stripper
,
f_strip
(
d
/
65536
)
)
)
2513
end
2514
}
2515 2516
implement
{
2517
name
=
"
featureattribute
"
,
2518
arguments
=
"
string
"
,
2519
actions
=
{
contextnumber
,
context
}
2520
}
2521 2522
implement
{
2523
name
=
"
setfontfeature
"
,
2524
arguments
=
"
string
"
,
2525
actions
=
setfontfeature
,
2526
}
2527 2528
implement
{
2529
name
=
"
resetfontfeature
"
,
2530
-- arguments = { 0, 0 },
2531
actions
=
resetfontfeature
,
2532
}
2533 2534
implement
{
2535
name
=
"
setfontofid
"
,
2536
arguments
=
"
integer
"
,
2537
actions
=
function
(
id
)
2538
ctx_getvalue
(
csnames
[
id
]
)
2539
end
2540
}
2541 2542
implement
{
2543
name
=
"
definefontfeature
"
,
2544
arguments
=
"
3 strings
"
,
2545
actions
=
presetcontext
,
2546
}
2547 2548
implement
{
2549
name
=
"
doifelsefontfeature
"
,
2550
arguments
=
"
string
"
,
2551
actions
=
function
(
name
)
ctx_doifelse
(
contextnumber
(
name
)
>
1
)
end
,
2552
}
2553 2554
implement
{
2555
name
=
"
doifunknownfontfeature
"
,
2556
arguments
=
"
string
"
,
2557
actions
=
function
(
name
)
ctx_doif
(
contextnumber
(
name
)
=
=
0
)
end
,
2558
}
2559 2560
implement
{
2561
name
=
"
adaptfontfeature
"
,
2562
arguments
=
"
2 strings
"
,
2563
actions
=
adaptcontext
2564
}
2565 2566
local
function
registerlanguagefeatures
(
)
2567
local
specifications
=
languages
.
data
.
specifications
2568
for
i
=
1
,
#
specifications
do
2569
local
specification
=
specifications
[
i
]
2570
local
language
=
specification
.
opentype
2571
if
language
then
2572
local
script
=
specification
.
opentypescript
or
specification
.
script
2573
if
script
then
2574
local
context
=
specification
.
context
2575
if
type
(
context
)
=
=
"
table
"
then
2576
for
i
=
1
,
#
context
do
2577
definecontext
(
context
[
i
]
,
{
language
=
language
,
script
=
script
}
)
2578
end
2579
elseif
type
(
context
)
=
=
"
string
"
then
2580
definecontext
(
context
,
{
language
=
language
,
script
=
script
}
)
2581
end
2582
end
2583
end
2584
end
2585
end
2586 2587
constructors
.
setfeature
=
setfeature
2588
constructors
.
resetfeature
=
resetfeature
2589 2590
implement
{
name
=
"
resetfeature
"
,
actions
=
resetfeature
}
2591
implement
{
name
=
"
addfeature
"
,
actions
=
setfeature
,
arguments
=
{
"
'+'
"
,
"
string
"
,
"
string
"
}
}
2592
implement
{
name
=
"
subtractfeature
"
,
actions
=
setfeature
,
arguments
=
{
"
'-'
"
,
"
string
"
,
"
string
"
}
}
2593
implement
{
name
=
"
replacefeature
"
,
actions
=
setfeature
,
arguments
=
{
"
'='
"
,
"
string
"
,
"
string
"
}
}
2594
implement
{
name
=
"
revivefeature
"
,
actions
=
setfeature
,
arguments
=
{
true
,
"
string
"
}
}
2595 2596
implement
{
2597
name
=
"
featurelist
"
,
2598
actions
=
{
fonts
.
specifiers
.
contexttostring
,
context
}
,
2599
arguments
=
{
"
string
"
,
"
'otf'
"
,
"
string
"
,
"
'yes'
"
,
"
'no'
"
,
true
}
2600
}
2601 2602
implement
{
2603
name
=
"
registerlanguagefeatures
"
,
2604
actions
=
registerlanguagefeatures
,
2605
}
2606 2607
end
2608 2609
-- a fontkern plug:
2610 2611
-- nodes.injections.installnewkern(nuts.pool.fontkern)
2612 2613
do
2614 2615
local
report
=
logs
.
reporter
(
"
otf
"
,
"
variants
"
)
2616 2617
local
function
replace
(
tfmdata
,
feature
,
value
)
2618
local
characters
=
tfmdata
.
characters
2619
local
variants
=
tfmdata
.
resources
.
variants
2620
if
variants
then
2621
local
t
=
{
}
2622
for
k
,
v
in
sortedhash
(
variants
)
do
2623
t
[
#
t
+
1
]
=
formatters
[
"
0x%X (%i)
"
]
(
k
,
k
)
2624
end
2625
value
=
tonumber
(
value
)
or
0xFE00
-- 917762
2626
report
(
"
fontname : %s
"
,
tfmdata
.
properties
.
fontname
)
2627
report
(
"
available: % t
"
,
t
)
2628
local
v
=
variants
[
value
]
2629
if
v
then
2630
report
(
"
using : %X (%i)
"
,
value
,
value
)
2631
for
k
,
v
in
next
,
v
do
2632
local
c
=
characters
[
v
]
2633
if
c
then
2634
characters
[
k
]
=
c
2635
end
2636
end
2637
else
2638
report
(
"
unknown : %X (%i)
"
,
value
,
value
)
2639
end
2640
end
2641
end
2642 2643
registerotffeature
{
2644
name
=
'
variant
'
,
2645
description
=
'
unicode variant
'
,
2646
manipulators
=
{
2647
base
=
replace
,
2648
node
=
replace
,
2649
}
2650
}
2651 2652
end
2653 2654
-- here (todo: closure)
2655 2656
-- make a closure (200 limit):
2657 2658
do
2659 2660
local
trace_analyzing
=
false
trackers
.
register
(
"
otf.analyzing
"
,
function
(
v
)
trace_analyzing
=
v
end
)
2661 2662
local
analyzers
=
fonts
.
analyzers
2663
local
methods
=
analyzers
.
methods
2664 2665
local
unsetvalue
=
attributes
.
unsetvalue
2666 2667
local
a_color
=
attributes
.
private
(
'
color
'
)
2668
local
a_colormodel
=
attributes
.
private
(
'
colormodel
'
)
2669
local
m_color
=
attributes
.
list
[
a_color
]
or
{
}
2670 2671
local
glyph_code
=
nodes
.
nodecodes
.
glyph
2672 2673
local
states
=
analyzers
.
states
2674 2675
local
colornames
=
{
2676
[
states
.
init
]
=
"
font:1
"
,
2677
[
states
.
medi
]
=
"
font:2
"
,
2678
[
states
.
fina
]
=
"
font:3
"
,
2679
[
states
.
isol
]
=
"
font:4
"
,
2680
[
states
.
mark
]
=
"
font:5
"
,
2681
[
states
.
rest
]
=
"
font:6
"
,
2682
[
states
.
rphf
]
=
"
font:1
"
,
2683
[
states
.
half
]
=
"
font:2
"
,
2684
[
states
.
pref
]
=
"
font:3
"
,
2685
[
states
.
blwf
]
=
"
font:4
"
,
2686
[
states
.
pstf
]
=
"
font:5
"
,
2687
}
2688 2689
-- todo: traversers
2690
-- todo: check attr_list so that we can use the same .. helper: setcolorattr
2691 2692
local
function
markstates
(
head
)
2693
if
head
then
2694
head
=
tonut
(
head
)
2695
local
model
=
getattr
(
head
,
a_colormodel
)
or
1
2696
for
glyph
in
nextchar
,
head
do
2697
local
a
=
getstate
(
glyph
)
2698
if
a
then
2699
local
name
=
colornames
[
a
]
2700
if
name
then
2701
local
color
=
m_color
[
name
]
2702
if
color
then
2703
setattr
(
glyph
,
a_colormodel
,
model
)
2704
setattr
(
glyph
,
a_color
,
color
)
2705
end
2706
end
2707
end
2708
end
2709
end
2710
end
2711 2712
local
function
analyzeprocessor
(
head
,
font
,
attr
)
2713
local
tfmdata
=
fontdata
[
font
]
2714
local
script
,
language
=
otf
.
scriptandlanguage
(
tfmdata
,
attr
)
2715
local
action
=
methods
[
script
]
2716
if
not
action
then
2717
return
head
,
false
2718
end
2719
if
type
(
action
)
=
=
"
function
"
then
2720
local
head
,
done
=
action
(
head
,
font
,
attr
)
2721
if
done
and
trace_analyzing
then
2722
markstates
(
head
)
2723
end
2724
return
head
,
done
2725
end
2726
action
=
action
[
language
]
2727
if
action
then
2728
local
head
,
done
=
action
(
head
,
font
,
attr
)
2729
if
done
and
trace_analyzing
then
2730
markstates
(
head
)
2731
end
2732
return
head
,
done
2733
else
2734
return
head
,
false
2735
end
2736
end
2737 2738
registerotffeature
{
-- adapts
2739
name
=
"
analyze
"
,
2740
processors
=
{
2741
node
=
analyzeprocessor
,
2742
}
2743
}
2744 2745 2746
function
methods
.
nocolor
(
head
,
font
,
attr
)
2747
for
n
,
c
,
f
in
nextchar
,
head
do
2748
if
not
font
or
f
=
=
font
then
2749
setattr
(
n
,
a_color
,
unsetvalue
)
2750
end
2751
end
2752
return
head
,
true
2753
end
2754 2755
end
2756 2757 2758
local
function
purefontname
(
name
)
2759
if
type
(
name
)
=
=
"
number
"
then
2760
name
=
getfontname
(
name
)
2761
end
2762
if
type
(
name
)
=
=
"
string
"
then
2763
return
basename
(
name
)
2764
end
2765
end
2766 2767
implement
{
2768
name
=
"
purefontname
"
,
2769
actions
=
{
purefontname
,
context
}
,
2770
arguments
=
"
string
"
,
2771
}
2772 2773
local
sharedstorage
=
storage
.
shared
2774 2775
local
list
=
sharedstorage
.
bodyfontsizes
or
{
}
2776
local
unknown
=
sharedstorage
.
unknownbodyfontsizes
or
{
}
2777 2778
sharedstorage
.
bodyfontsizes
=
list
2779
sharedstorage
.
unknownbodyfontsizes
=
unknown
2780 2781
implement
{
2782
name
=
"
registerbodyfontsize
"
,
2783
arguments
=
"
string
"
,
2784
actions
=
function
(
size
)
2785
list
[
size
]
=
true
2786
end
2787
}
2788 2789
interfaces
.
implement
{
2790
name
=
"
registerunknownbodysize
"
,
2791
arguments
=
"
string
"
,
2792
actions
=
function
(
size
)
2793
if
not
unknown
[
size
]
then
2794
interfaces
.
showmessage
(
"
fonts
"
,
14
,
size
)
2795
end
2796
unknown
[
size
]
=
true
2797
end
,
2798
}
2799 2800
implement
{
2801
name
=
"
getbodyfontsizes
"
,
2802
arguments
=
"
string
"
,
2803
actions
=
function
(
separator
)
2804
context
(
concat
(
sortedkeys
(
list
)
,
separator
)
)
2805
end
2806
}
2807 2808
implement
{
2809
name
=
"
processbodyfontsizes
"
,
2810
arguments
=
"
string
"
,
2811
actions
=
function
(
command
)
2812
local
keys
=
sortedkeys
(
list
)
2813
if
command
then
2814
local
action
=
context
[
command
]
2815
for
i
=
1
,
#
keys
do
2816
action
(
keys
[
i
]
)
2817
end
2818
else
2819
context
(
concat
(
keys
,
"
,
"
)
)
2820
end
2821
end
2822
}
2823 2824
implement
{
2825
name
=
"
cleanfontname
"
,
2826
actions
=
{
cleanname
,
context
}
,
2827
arguments
=
"
string
"
2828
}
2829 2830
implement
{
2831
name
=
"
fontlookupinitialize
"
,
2832
actions
=
names
.
lookup
,
2833
arguments
=
"
string
"
,
2834
}
2835 2836
implement
{
2837
name
=
"
fontlookupnoffound
"
,
2838
actions
=
{
names
.
noflookups
,
context
}
,
2839
}
2840 2841
implement
{
2842
name
=
"
fontlookupgetkeyofindex
"
,
2843
actions
=
{
names
.
getlookupkey
,
context
}
,
2844
arguments
=
{
"
string
"
,
"
integer
"
}
2845
}
2846 2847
implement
{
2848
name
=
"
fontlookupgetkey
"
,
2849
actions
=
{
names
.
getlookupkey
,
context
}
,
2850
arguments
=
"
string
"
2851
}
2852 2853
-- this might move to a runtime module:
2854 2855
function
commands
.
showchardata
(
n
)
2856
local
tfmdata
=
fontdata
[
currentfont
(
)
]
2857
if
tfmdata
then
2858
if
type
(
n
)
=
=
"
string
"
then
2859
n
=
utfbyte
(
n
)
2860
end
2861
local
chr
=
tfmdata
.
characters
[
n
]
2862
if
chr
then
2863
report_status
(
"
%s @ %s => %U => %c => %s
"
,
tfmdata
.
properties
.
fullname
,
tfmdata
.
parameters
.
size
,
n
,
n
,
serialize
(
chr
,
false
)
)
2864
end
2865
end
2866
end
2867 2868
function
commands
.
showfontparameters
(
tfmdata
)
2869
-- this will become more clever
2870
local
tfmdata
=
tfmdata
or
fontdata
[
currentfont
(
)
]
2871
if
tfmdata
then
2872
local
parameters
=
tfmdata
.
parameters
2873
local
mathparameters
=
tfmdata
.
mathparameters
2874
local
properties
=
tfmdata
.
properties
2875
local
hasparameters
=
parameters
and
next
(
parameters
)
2876
local
hasmathparameters
=
mathparameters
and
next
(
mathparameters
)
2877
if
hasparameters
then
2878
report_status
(
"
%s @ %s => text parameters => %s
"
,
properties
.
fullname
,
parameters
.
size
,
serialize
(
parameters
,
false
)
)
2879
end
2880
if
hasmathparameters
then
2881
report_status
(
"
%s @ %s => math parameters => %s
"
,
properties
.
fullname
,
parameters
.
size
,
serialize
(
mathparameters
,
false
)
)
2882
end
2883
if
not
hasparameters
and
not
hasmathparameters
then
2884
report_status
(
"
%s @ %s => no text parameters and/or math parameters
"
,
properties
.
fullname
,
parameters
.
size
)
2885
end
2886
end
2887
end
2888 2889
implement
{
2890
name
=
"
currentdesignsize
"
,
2891
actions
=
function
(
)
2892
context
(
parameters
[
currentfont
(
)
]
.
designsize
)
2893
end
2894
}
2895 2896
implement
{
2897
name
=
"
doifelsefontpresent
"
,
2898
actions
=
{
names
.
exists
,
commands
.
doifelse
}
,
2899
arguments
=
"
string
"
2900
}
2901 2902
-- we use 0xFE000+ and 0xFF000+ in math and for runtime (text) extensions we
2903
-- use 0xFD000+
2904 2905
constructors
.
privateslots
=
constructors
.
privateslots
or
{
}
2906 2907
storage
.
register
(
"
fonts/constructors/privateslots
"
,
constructors
.
privateslots
,
"
fonts.constructors.privateslots
"
)
2908 2909
do
2910 2911
local
privateslots
=
constructors
.
privateslots
2912
local
lastprivateslot
=
0xFD000
2913 2914
constructors
.
privateslots
=
setmetatableindex
(
privateslots
,
function
(
t
,
k
)
2915
local
v
=
lastprivateslot
2916
lastprivateslot
=
lastprivateslot
+
1
2917
t
[
k
]
=
v
2918
return
v
2919
end
)
2920 2921
implement
{
2922
name
=
"
getprivateglyphslot
"
,
2923
actions
=
function
(
name
)
context
(
privateslots
[
name
]
)
end
,
2924
arguments
=
"
string
"
,
2925
}
2926 2927
end
2928 2929
-- an extra helper
2930 2931
function
helpers
.
getcoloredglyphs
(
tfmdata
)
2932
if
type
(
tfmdata
)
=
=
"
number
"
then
2933
tfmdata
=
fontdata
[
tfmdata
]
2934
end
2935
if
not
tfmdata
then
2936
tfmdata
=
fontdata
[
true
]
2937
end
2938
local
characters
=
tfmdata
.
characters
2939
local
descriptions
=
tfmdata
.
descriptions
2940
local
collected
=
{
}
2941
for
unicode
,
character
in
next
,
characters
do
2942
local
description
=
descriptions
[
unicode
]
2943
if
description
and
(
description
.
colors
or
character
.
svg
)
then
2944
collected
[
#
collected
+
1
]
=
unicode
2945
end
2946
end
2947
table
.
sort
(
collected
)
2948
return
collected
2949
end
2950 2951
-- for the font manual
2952 2953
statistics
.
register
(
"
body font sizes
"
,
function
(
)
2954
if
next
(
unknown
)
then
2955
return
formatters
[
"
defined: % t, undefined: % t
"
]
(
sortedkeys
(
list
)
,
sortedkeys
(
unknown
)
)
2956
end
2957
end
)
2958 2959
statistics
.
register
(
"
used fonts
"
,
function
(
)
2960
if
trace_usage
then
2961
local
filename
=
file
.
nameonly
(
environment
.
jobname
)
.
.
"
-fonts-usage.lua
"
2962
if
next
(
fontdata
)
then
2963
local
files
=
{
}
2964
local
list
=
{
}
2965
for
id
,
tfmdata
in
sortedhash
(
fontdata
)
do
2966
local
filename
=
tfmdata
.
properties
.
filename
2967
if
filename
then
2968
local
filedata
=
files
[
filename
]
2969
if
filedata
then
2970
filedata
.
instances
=
filedata
.
instances
+
1
2971
else
2972
local
rawdata
=
tfmdata
.
shared
and
tfmdata
.
shared
.
rawdata
2973
local
metadata
=
rawdata
and
rawdata
.
metadata
2974
files
[
filename
]
=
{
2975
instances
=
1
,
2976
filename
=
filename
,
2977
version
=
metadata
and
metadata
.
version
,
2978
size
=
rawdata
and
rawdata
.
size
,
2979
}
2980
end
2981
else
2982
-- what to do
2983
end
2984
end
2985
for
k
,
v
in
sortedhash
(
files
)
do
2986
list
[
#
list
+
1
]
=
v
2987
end
2988
table
.
save
(
filename
,
list
)
2989
else
2990
os
.
remove
(
filename
)
2991
end
2992
end
2993
end
)
2994 2995
-- new
2996 2997
do
2998 2999
local
settings_to_array
=
utilities
.
parsers
.
settings_to_array
3000
-- local namedcolorattributes = attributes.colors.namedcolorattributes
3001
-- local colorvalues = attributes.colors.values
3002 3003
-- implement {
3004
-- name = "definefontcolorpalette",
3005
-- arguments = "2 strings",
3006
-- actions = function(name,set)
3007
-- set = settings_to_array(set)
3008
-- for i=1,#set do
3009
-- local name = set[i]
3010
-- local space, color = namedcolorattributes(name)
3011
-- local values = colorvalues[color]
3012
-- if values then
3013
-- set[i] = { r = values[3], g = values[4], b = values[5] }
3014
-- else
3015
-- set[i] = { r = 0, g = 0, b = 0 }
3016
-- end
3017
-- end
3018
-- otf.registerpalette(name,set)
3019
-- end
3020
-- }
3021 3022
implement
{
3023
name
=
"
definefontcolorpalette
"
,
3024
arguments
=
"
2 strings
"
,
3025
actions
=
function
(
name
,
set
)
3026
otf
.
registerpalette
(
name
,
settings_to_array
(
set
)
)
3027
end
3028
}
3029 3030
end
3031 3032
do
3033 3034
local
pattern
=
C
(
(
1
-
S
(
"
*
"
)
)
^
1
)
-- strips all after * or ' at'
3035 3036
implement
{
3037
name
=
"
truefontname
"
,
3038
arguments
=
"
string
"
,
3039
actions
=
function
(
s
)
3040
-- context(match(s,"[^* ]+") or s)
3041
context
(
lpegmatch
(
pattern
,
s
)
or
s
)
3042
end
3043
}
3044 3045
end
3046 3047
do
3048 3049
local
function
getinstancespec
(
id
)
3050
local
data
=
fontdata
[
id
or
true
]
3051
local
shared
=
data
.
shared
3052
local
resources
=
shared
and
shared
.
rawdata
.
resources
3053
if
resources
then
3054
local
instancespec
=
data
.
properties
.
instance
3055
if
instancespec
then
3056
local
variabledata
=
resources
.
variabledata
3057
if
variabledata
then
3058
local
instances
=
variabledata
.
instances
3059
if
instances
then
3060
for
i
=
1
,
#
instances
do
3061
local
instance
=
instances
[
i
]
3062
if
cleanname
(
instance
.
subfamily
)
=
=
instancespec
then
3063
local
values
=
table
.
copy
(
instance
.
values
)
3064
local
axis
=
variabledata
.
axis
3065
for
i
=
1
,
#
values
do
3066
for
j
=
1
,
#
axis
do
3067
if
values
[
i
]
.
axis
=
=
axis
[
j
]
.
tag
then
3068
values
[
i
]
.
name
=
axis
[
j
]
.
name
3069
break
3070
end
3071
end
3072
end
3073
return
values
3074
end
3075
end
3076
end
3077
end
3078
end
3079
end
3080
end
3081 3082
helpers
.
getinstancespec
=
getinstancespec
3083 3084
implement
{
3085
name
=
"
currentfontinstancespec
"
,
3086
actions
=
function
(
)
3087
local
t
=
getinstancespec
(
)
-- current font
3088
if
t
then
3089
for
i
=
1
,
#
t
do
3090
if
i
>
1
then
3091
context
.
space
(
)
3092
end
3093
local
ti
=
t
[
i
]
3094
context
(
"
%s=%s
"
,
ti
.
name
,
ti
.
value
)
3095
end
3096
end
3097
end
3098
}
3099 3100
end
3101 3102
-- for the moment here (and not in font-con.lua):
3103 3104
do
3105 3106
local
identical
=
table
.
identical
3107
local
copy
=
table
.
copy
3108
local
fontdata
=
fonts
.
hashes
.
identifiers
3109
local
addcharacters
=
font
.
addcharacters
3110 3111
-- This helper is mostly meant to add last-resort (virtual) characters
3112
-- or runtime generated fonts (so we forget about features and such). It
3113
-- will probably take a while before it get used.
3114 3115
local
trace_adding
=
false
3116
local
report_adding
=
logs
.
reporter
(
"
fonts
"
,
"
add characters
"
)
3117 3118
trackers
.
register
(
"
fonts.addcharacters
"
,
function
(
v
)
trace_adding
=
v
end
)
3119 3120
if
addcharacters
then
3121 3122
function
fonts
.
constructors
.
addcharacters
(
id
,
list
)
3123
local
newchar
=
list
.
characters
3124
if
newchar
then
3125
local
data
=
fontdata
[
id
]
3126
local
newfont
=
list
.
fonts
3127
local
oldchar
=
data
.
characters
3128
local
oldfont
=
data
.
fonts
3129
addcharacters
(
id
,
{
3130
characters
=
newchar
,
3131
fonts
=
newfont
,
3132
nomath
=
not
data
.
properties
.
hasmath
,
3133
}
)
3134
-- this is just for tracing, as the assignment only uses the fonts list
3135
-- and doesn't store it otherwise
3136
if
newfont
then
3137
if
oldfont
then
3138
local
oldn
=
#
oldfont
3139
local
newn
=
#
newfont
3140
for
n
=
1
,
newn
do
3141
local
ok
=
false
3142
local
nf
=
newfont
[
n
]
3143
for
o
=
1
,
oldn
do
3144
if
identical
(
nf
,
oldfont
[
o
]
)
then
3145
ok
=
true
3146
break
3147
end
3148
end
3149
if
not
ok
then
3150
oldn
=
oldn
+
1
3151
oldfont
[
oldn
]
=
newfont
[
i
]
3152
end
3153
end
3154
else
3155
data
.
fonts
=
newfont
3156
end
3157
end
3158
-- this is because we need to know what goes on and also might
3159
-- want to access character data
3160
for
u
,
c
in
next
,
newchar
do
3161
if
trace_adding
then
3162
report_adding
(
"
adding character %U to font %!font:name!
"
,
u
,
id
)
3163
end
3164
oldchar
[
u
]
=
c
3165
end
3166
end
3167
end
3168 3169
else
3170
function
fonts
.
constructors
.
addcharacters
(
id
,
list
)
3171
report_adding
(
"
adding characters to %!font:name! is not yet supported
"
,
id
)
3172
end
3173
end
3174 3175
implement
{
3176
name
=
"
addfontpath
"
,
3177
arguments
=
"
string
"
,
3178
actions
=
function
(
list
)
3179
names
.
addruntimepath
(
settings_to_array
(
list
)
)
3180
end
3181
}
3182 3183
end
3184 3185
-- moved here
3186 3187
do
3188 3189
local
getfontoffamily
=
font
.
getfontoffamily
3190
local
new_glyph
=
nodes
.
pool
.
glyph
3191
local
fontproperties
=
fonts
.
hashes
.
properties
3192 3193
local
function
getprivateslot
(
id
,
name
)
3194
if
not
name
then
3195
name
=
id
3196
id
=
currentfont
(
)
3197
end
3198
local
properties
=
fontproperties
[
id
]
3199
local
privates
=
properties
and
properties
.
privates
3200
return
privates
and
privates
[
name
]
3201
end
3202 3203
local
function
getprivatenode
(
tfmdata
,
name
)
3204
if
type
(
tfmdata
)
=
=
"
number
"
then
3205
tfmdata
=
fontdata
[
tfmdata
]
3206
end
3207
local
properties
=
tfmdata
.
properties
3208
local
font
=
properties
.
id
3209
local
slot
=
getprivateslot
(
font
,
name
)
3210
if
slot
then
3211
-- todo: set current attribibutes
3212
local
char
=
tfmdata
.
characters
[
slot
]
3213
local
tonode
=
char
.
tonode
3214
if
tonode
then
3215
return
tonode
(
font
,
char
)
3216
else
3217
return
new_glyph
(
font
,
slot
)
3218
end
3219
end
3220
end
3221 3222
local
function
getprivatecharornode
(
tfmdata
,
name
)
3223
if
type
(
tfmdata
)
=
=
"
number
"
then
3224
tfmdata
=
fontdata
[
tfmdata
]
3225
end
3226
local
properties
=
tfmdata
.
properties
3227
local
font
=
properties
.
id
3228
local
slot
=
getprivateslot
(
font
,
name
)
3229
if
slot
then
3230
-- todo: set current attributes
3231
local
char
=
tfmdata
.
characters
[
slot
]
3232
local
tonode
=
char
.
tonode
3233
if
tonode
then
3234
return
"
node
"
,
tonode
(
tfmdata
,
char
)
3235
else
3236
return
"
char
"
,
slot
3237
end
3238
end
3239
end
3240 3241
helpers
.
getprivateslot
=
getprivateslot
3242
helpers
.
getprivatenode
=
getprivatenode
3243
helpers
.
getprivatecharornode
=
getprivatecharornode
3244 3245
implement
{
3246
name
=
"
getprivatechar
"
,
3247
arguments
=
"
string
"
,
3248
actions
=
function
(
name
)
3249
local
p
=
getprivateslot
(
name
)
3250
if
p
then
3251
context
(
utfchar
(
p
)
)
3252
end
3253
end
3254
}
3255 3256
implement
{
3257
name
=
"
getprivatemathchar
"
,
3258
arguments
=
"
string
"
,
3259
actions
=
function
(
name
)
3260
local
p
=
getprivateslot
(
getfontoffamily
(
0
)
,
name
)
3261
if
p
then
3262
context
(
utfchar
(
p
)
)
3263
end
3264
end
3265
}
3266 3267
implement
{
3268
name
=
"
getprivateslot
"
,
3269
arguments
=
"
string
"
,
3270
actions
=
function
(
name
)
3271
local
p
=
getprivateslot
(
name
)
3272
if
p
then
3273
context
(
p
)
3274
end
3275
end
3276
}
3277 3278
end
3279 3280
-- handy, for now here:
3281 3282
function
fonts
.
helpers
.
collectanchors
(
tfmdata
)
3283 3284
local
resources
=
tfmdata
.
resources
-- todo: use shared
3285 3286
if
not
resources
or
resources
.
anchors
then
3287
return
resources
.
anchors
3288
end
3289 3290
local
anchors
=
{
}
3291 3292
local
function
set
(
unicode
,
target
,
class
,
anchor
)
3293
local
a
=
anchors
[
unicode
]
3294
if
not
a
then
3295
anchors
[
unicode
]
=
{
[
target
]
=
{
anchor
}
}
3296
return
3297
end
3298
local
t
=
a
[
target
]
3299
if
not
t
then
3300
a
[
target
]
=
{
anchor
}
3301
return
3302
end
3303
local
x
=
anchor
[
1
]
3304
local
y
=
anchor
[
2
]
3305
for
k
,
v
in
next
,
t
do
3306
if
v
[
1
]
=
=
x
and
v
[
2
]
=
=
y
then
3307
return
3308
end
3309
end
3310
t
[
#
t
+
1
]
=
anchor
3311
end
3312 3313
local
function
getanchors
(
steps
,
target
)
3314
for
i
=
1
,
#
steps
do
3315
local
step
=
steps
[
i
]
3316
local
coverage
=
step
.
coverage
3317
for
unicode
,
data
in
next
,
coverage
do
3318
local
class
=
data
[
1
]
3319
local
anchor
=
data
[
2
]
3320
if
anchor
[
1
]
~
=
0
or
anchor
[
2
]
~
=
0
then
3321
set
(
unicode
,
target
,
class
,
anchor
)
3322
end
3323
end
3324
end
3325
end
3326 3327
local
function
getcursives
(
steps
)
3328
for
i
=
1
,
#
steps
do
3329
local
step
=
steps
[
i
]
3330
local
coverage
=
step
.
coverage
3331
for
unicode
,
data
in
next
,
coverage
do
3332
local
class
=
data
[
1
]
3333
local
en
=
data
[
2
]
3334
local
ex
=
data
[
3
]
3335
if
en
then
3336
set
(
unicode
,
"
entry
"
,
class
,
en
)
3337
end
3338
if
ex
then
3339
set
(
unicode
,
"
exit
"
,
class
,
ex
)
3340
end
3341
end
3342
end
3343
end
3344 3345
local
function
collect
(
list
)
3346
if
list
then
3347
for
i
=
1
,
#
list
do
3348
local
entry
=
list
[
i
]
3349
local
steps
=
entry
.
steps
3350
local
kind
=
entry
.
type
3351
if
kind
=
=
"
gpos_mark2mark
"
then
3352
getanchors
(
steps
,
"
mark
"
)
3353
elseif
kind
=
=
"
gpos_mark2base
"
then
3354
getanchors
(
steps
,
"
base
"
)
3355
elseif
kind
=
=
"
gpos_mark2ligature
"
then
3356
getanchors
(
steps
,
"
ligature
"
)
3357
elseif
kind
=
=
"
gpos_cursive
"
then
3358
getcursives
(
steps
)
3359
end
3360
end
3361
end
3362
end
3363 3364
collect
(
resources
.
sequences
)
3365
collect
(
resources
.
sublookups
)
3366 3367
local
function
sorter
(
a
,
b
)
3368
if
a
[
1
]
=
=
b
[
1
]
then
3369
return
a
[
2
]
<
b
[
2
]
3370
else
3371
return
a
[
1
]
<
b
[
1
]
3372
end
3373
end
3374 3375
for
unicode
,
old
in
next
,
anchors
do
3376
for
target
,
list
in
next
,
old
do
3377
sort
(
list
,
sorter
)
3378
end
3379
end
3380 3381
resources
.
anchors
=
anchors
3382 3383
return
anchors
3384 3385
end
3386 3387
if
CONTEXTLMTXMODE
>
0
then
3388
fonts
.
constructors
.
addtounicode
=
false
3389
fonts
.
constructors
.
autocleanup
=
false
3390
end
3391