font-oup.lua /size: 114 Kb    last modification: 2020-07-01 14:35
1
if
not
modules
then
modules
=
{
}
end
modules
[
'
font-oup
'
]
=
{
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
local
next
,
type
=
next
,
type
10
local
P
,
R
,
S
=
lpeg
.
P
,
lpeg
.
R
,
lpeg
.
S
11
local
lpegmatch
=
lpeg
.
match
12
local
insert
,
remove
,
copy
,
unpack
=
table
.
insert
,
table
.
remove
,
table
.
copy
,
table
.
unpack
13 14
local
formatters
=
string
.
formatters
15
local
sortedkeys
=
table
.
sortedkeys
16
local
sortedhash
=
table
.
sortedhash
17
local
tohash
=
table
.
tohash
18
local
setmetatableindex
=
table
.
setmetatableindex
19 20
local
report_error
=
logs
.
reporter
(
"
otf reader
"
,
"
error
"
)
21
local
report_markwidth
=
logs
.
reporter
(
"
otf reader
"
,
"
markwidth
"
)
22
local
report_cleanup
=
logs
.
reporter
(
"
otf reader
"
,
"
cleanup
"
)
23
local
report_optimizations
=
logs
.
reporter
(
"
otf reader
"
,
"
merges
"
)
24
local
report_unicodes
=
logs
.
reporter
(
"
otf reader
"
,
"
unicodes
"
)
25 26
local
trace_markwidth
=
false
trackers
.
register
(
"
otf.markwidth
"
,
function
(
v
)
trace_markwidth
=
v
end
)
27
local
trace_cleanup
=
false
trackers
.
register
(
"
otf.cleanups
"
,
function
(
v
)
trace_cleanups
=
v
end
)
28
local
trace_optimizations
=
false
trackers
.
register
(
"
otf.optimizations
"
,
function
(
v
)
trace_optimizations
=
v
end
)
29
local
trace_unicodes
=
false
trackers
.
register
(
"
otf.unicodes
"
,
function
(
v
)
trace_unicodes
=
v
end
)
30 31
local
readers
=
fonts
.
handlers
.
otf
.
readers
32
local
privateoffset
=
fonts
.
constructors
and
fonts
.
constructors
.
privateoffset
or
0xF0000
-- 0x10FFFF
33 34
local
f_private
=
formatters
[
"
P%05X
"
]
35
local
f_unicode
=
formatters
[
"
U%05X
"
]
36
local
f_index
=
formatters
[
"
I%05X
"
]
37
local
f_character_y
=
formatters
[
"
%C
"
]
38
local
f_character_n
=
formatters
[
"
[ %C ]
"
]
39 40
local
check_duplicates
=
true
-- can become an option (pseudo feature) / aways needed anyway
41
local
check_soft_hyphen
=
true
-- can become an option (pseudo feature) / needed for tagging
42 43
directives
.
register
(
"
otf.checksofthyphen
"
,
function
(
v
)
44
check_soft_hyphen
=
v
45
end
)
46 47
local
function
replaced
(
list
,
index
,
replacement
)
48
if
type
(
list
)
=
=
"
number
"
then
49
return
replacement
50
elseif
type
(
replacement
)
=
=
"
table
"
then
51
local
t
=
{
}
52
local
n
=
index
-1
53
for
i
=
1
,
n
do
54
t
[
i
]
=
list
[
i
]
55
end
56
for
i
=
1
,
#
replacement
do
57
n
=
n
+
1
58
t
[
n
]
=
replacement
[
i
]
59
end
60
for
i
=
index
+
1
,
#
list
do
61
n
=
n
+
1
62
t
[
n
]
=
list
[
i
]
63
end
64
else
65
list
[
index
]
=
replacement
66
return
list
67
end
68
end
69 70
local
function
unifyresources
(
fontdata
,
indices
)
71
local
descriptions
=
fontdata
.
descriptions
72
local
resources
=
fontdata
.
resources
73
if
not
descriptions
or
not
resources
then
74
return
75
end
76
--
77
local
nofindices
=
#
indices
78
--
79
local
variants
=
fontdata
.
resources
.
variants
80
if
variants
then
81
for
selector
,
unicodes
in
next
,
variants
do
82
for
unicode
,
index
in
next
,
unicodes
do
83
unicodes
[
unicode
]
=
indices
[
index
]
84
end
85
end
86
end
87
--
88
local
function
remark
(
marks
)
89
if
marks
then
90
local
newmarks
=
{
}
91
for
k
,
v
in
next
,
marks
do
92
local
u
=
indices
[
k
]
93
if
u
then
94
newmarks
[
u
]
=
v
95
elseif
trace_optimizations
then
96
report_optimizations
(
"
discarding mark %i
"
,
k
)
97
end
98
end
99
return
newmarks
100
end
101
end
102
--
103
local
marks
=
resources
.
marks
104
if
marks
then
105
resources
.
marks
=
remark
(
marks
)
106
end
107
--
108
local
markclasses
=
resources
.
markclasses
109
if
markclasses
then
110
for
class
,
marks
in
next
,
markclasses
do
111
markclasses
[
class
]
=
remark
(
marks
)
112
end
113
end
114
--
115
local
marksets
=
resources
.
marksets
116
if
marksets
then
117
for
class
,
marks
in
next
,
marksets
do
118
marksets
[
class
]
=
remark
(
marks
)
119
end
120
end
121
--
122
local
done
=
{
}
-- we need to deal with shared !
123
--
124
local
duplicates
=
check_duplicates
and
resources
.
duplicates
125
if
duplicates
and
not
next
(
duplicates
)
then
126
duplicates
=
false
127
end
128
--
129
local
function
recover
(
cover
)
-- can be packed
130
for
i
=
1
,
#
cover
do
131
local
c
=
cover
[
i
]
132
if
not
done
[
c
]
then
133
local
t
=
{
}
134
for
k
,
v
in
next
,
c
do
135
local
ug
=
indices
[
k
]
136
if
ug
then
137
t
[
ug
]
=
v
138
else
139
report_error
(
"
case %i, bad index in unifying %s: %s of %s
"
,
1
,
"
coverage
"
,
k
,
nofindices
)
140
end
141
end
142
cover
[
i
]
=
t
143
done
[
c
]
=
d
144
end
145
end
146
end
147
--
148
local
function
recursed
(
c
,
kind
)
-- ligs are not packed
149
local
t
=
{
}
150
for
g
,
d
in
next
,
c
do
151
if
type
(
d
)
=
=
"
table
"
then
152
local
ug
=
indices
[
g
]
153
if
ug
then
154
t
[
ug
]
=
recursed
(
d
,
kind
)
155
else
156
report_error
(
"
case %i, bad index in unifying %s: %s of %s
"
,
1
,
kind
,
g
,
nofindices
)
157
end
158
else
159
t
[
g
]
=
indices
[
d
]
-- ligature
160
end
161
end
162
return
t
163
end
164
--
165
-- the duplicates need checking (probably only in cjk fonts): currently we only check
166
-- gsub_single, gsub_alternate, gsub_multiple, gpos_single and gpos_cursive
167
--
168
local
function
unifythem
(
sequences
)
169
if
not
sequences
then
170
return
171
end
172
for
i
=
1
,
#
sequences
do
173
local
sequence
=
sequences
[
i
]
174
local
kind
=
sequence
.
type
175
local
steps
=
sequence
.
steps
176
local
features
=
sequence
.
features
177
if
steps
then
178
for
i
=
1
,
#
steps
do
179
local
step
=
steps
[
i
]
180
if
kind
=
=
"
gsub_single
"
then
181
local
c
=
step
.
coverage
182
if
c
then
183
local
t1
=
done
[
c
]
184
if
not
t1
then
185
t1
=
{
}
186
if
duplicates
then
187
for
g1
,
d1
in
next
,
c
do
188
local
ug1
=
indices
[
g1
]
189
if
ug1
then
190
local
ud1
=
indices
[
d1
]
191
if
ud1
then
192
t1
[
ug1
]
=
ud1
193
local
dg1
=
duplicates
[
ug1
]
194
if
dg1
then
195
for
u
in
next
,
dg1
do
196
t1
[
u
]
=
ud1
197
end
198
end
199
else
200
report_error
(
"
case %i, bad index in unifying %s: %s of %s
"
,
3
,
kind
,
d1
,
nofindices
)
201
end
202
else
203
report_error
(
"
case %i, bad index in unifying %s: %s of %s
"
,
1
,
kind
,
g1
,
nofindices
)
204
end
205
end
206
else
207
for
g1
,
d1
in
next
,
c
do
208
local
ug1
=
indices
[
g1
]
209
if
ug1
then
210
t1
[
ug1
]
=
indices
[
d1
]
211
else
212
report_error
(
"
fuzzy case %i in unifying %s: %i
"
,
2
,
kind
,
g1
)
213
end
214
end
215
end
216
done
[
c
]
=
t1
217
end
218
step
.
coverage
=
t1
219
end
220
elseif
kind
=
=
"
gpos_pair
"
then
221
local
c
=
step
.
coverage
222
if
c
then
223
local
t1
=
done
[
c
]
224
if
not
t1
then
225
t1
=
{
}
226
for
g1
,
d1
in
next
,
c
do
227
local
ug1
=
indices
[
g1
]
228
if
ug1
then
229
local
t2
=
done
[
d1
]
230
if
not
t2
then
231
t2
=
{
}
232
for
g2
,
d2
in
next
,
d1
do
233
local
ug2
=
indices
[
g2
]
234
if
ug2
then
235
t2
[
ug2
]
=
d2
236
else
237
report_error
(
"
case %i, bad index in unifying %s: %s of %s
"
,
1
,
kind
,
g2
,
nofindices
,
nofindices
)
238
end
239
end
240
done
[
d1
]
=
t2
241
end
242
t1
[
ug1
]
=
t2
243
else
244
report_error
(
"
case %i, bad index in unifying %s: %s of %s
"
,
2
,
kind
,
g1
,
nofindices
)
245
end
246
end
247
done
[
c
]
=
t1
248
end
249
step
.
coverage
=
t1
250
end
251
elseif
kind
=
=
"
gsub_ligature
"
then
252
local
c
=
step
.
coverage
253
if
c
then
254
step
.
coverage
=
recursed
(
c
,
kind
)
255
end
256
elseif
kind
=
=
"
gsub_alternate
"
or
kind
=
=
"
gsub_multiple
"
then
257
local
c
=
step
.
coverage
258
if
c
then
259
local
t1
=
done
[
c
]
260
if
not
t1
then
261
t1
=
{
}
262
if
duplicates
then
263
for
g1
,
d1
in
next
,
c
do
264
for
i
=
1
,
#
d1
do
265
local
d1i
=
d1
[
i
]
266
local
d1u
=
indices
[
d1i
]
267
if
d1u
then
268
d1
[
i
]
=
d1u
269
else
270
report_error
(
"
case %i, bad index in unifying %s: %s of %s
"
,
1
,
kind
,
i
,
d1i
,
nofindices
)
271
end
272
end
273
local
ug1
=
indices
[
g1
]
274
if
ug1
then
275
t1
[
ug1
]
=
d1
276
local
dg1
=
duplicates
[
ug1
]
277
if
dg1
then
278
for
u
in
next
,
dg1
do
279
t1
[
u
]
=
copy
(
d1
)
280
end
281
end
282
else
283
report_error
(
"
case %i, bad index in unifying %s: %s of %s
"
,
2
,
kind
,
g1
,
nofindices
)
284
end
285
end
286
else
287
for
g1
,
d1
in
next
,
c
do
288
for
i
=
1
,
#
d1
do
289
local
d1i
=
d1
[
i
]
290
local
d1u
=
indices
[
d1i
]
291
if
d1u
then
292
d1
[
i
]
=
d1u
293
else
294
report_error
(
"
case %i, bad index in unifying %s: %s of %s
"
,
2
,
kind
,
d1i
,
nofindices
)
295
end
296
end
297
t1
[
indices
[
g1
]
]
=
d1
298
end
299
end
300
done
[
c
]
=
t1
301
end
302
step
.
coverage
=
t1
303
end
304
elseif
kind
=
=
"
gpos_single
"
then
305
local
c
=
step
.
coverage
306
if
c
then
307
local
t1
=
done
[
c
]
308
if
not
t1
then
309
t1
=
{
}
310
if
duplicates
then
311
for
g1
,
d1
in
next
,
c
do
312
local
ug1
=
indices
[
g1
]
313
if
ug1
then
314
t1
[
ug1
]
=
d1
315
local
dg1
=
duplicates
[
ug1
]
316
if
dg1
then
317
for
u
in
next
,
dg1
do
318
t1
[
u
]
=
d1
319
end
320
end
321
else
322
report_error
(
"
case %i, bad index in unifying %s: %s of %s
"
,
1
,
kind
,
g1
,
nofindices
)
323
end
324
end
325
else
326
for
g1
,
d1
in
next
,
c
do
327
local
ug1
=
indices
[
g1
]
328
if
ug1
then
329
t1
[
ug1
]
=
d1
330
else
331
report_error
(
"
case %i, bad index in unifying %s: %s of %s
"
,
2
,
kind
,
g1
,
nofindices
)
332
end
333
end
334
end
335
done
[
c
]
=
t1
336
end
337
step
.
coverage
=
t1
338
end
339
elseif
kind
=
=
"
gpos_mark2base
"
or
kind
=
=
"
gpos_mark2mark
"
or
kind
=
=
"
gpos_mark2ligature
"
then
340
local
c
=
step
.
coverage
341
if
c
then
342
local
t1
=
done
[
c
]
343
if
not
t1
then
344
t1
=
{
}
345
for
g1
,
d1
in
next
,
c
do
346
local
ug1
=
indices
[
g1
]
347
if
ug1
then
348
t1
[
ug1
]
=
d1
349
else
350
report_error
(
"
case %i, bad index in unifying %s: %s of %s
"
,
1
,
kind
,
g1
,
nofindices
)
351
end
352
end
353
done
[
c
]
=
t1
354
end
355
step
.
coverage
=
t1
356
end
357
local
c
=
step
.
baseclasses
358
if
c
then
359
local
t1
=
done
[
c
]
360
if
not
t1
then
361
for
g1
,
d1
in
next
,
c
do
362
local
t2
=
done
[
d1
]
363
if
not
t2
then
364
t2
=
{
}
365
for
g2
,
d2
in
next
,
d1
do
366
local
ug2
=
indices
[
g2
]
367
if
ug2
then
368
t2
[
ug2
]
=
d2
369
else
370
report_error
(
"
case %i, bad index in unifying %s: %s of %s
"
,
2
,
kind
,
g2
,
nofindices
)
371
end
372
end
373
done
[
d1
]
=
t2
374
end
375
c
[
g1
]
=
t2
376
end
377
done
[
c
]
=
c
378
end
379
end
380
elseif
kind
=
=
"
gpos_cursive
"
then
381
local
c
=
step
.
coverage
382
if
c
then
383
local
t1
=
done
[
c
]
384
if
not
t1
then
385
t1
=
{
}
386
if
duplicates
then
387
for
g1
,
d1
in
next
,
c
do
388
local
ug1
=
indices
[
g1
]
389
if
ug1
then
390
t1
[
ug1
]
=
d1
391
--
392
local
dg1
=
duplicates
[
ug1
]
393
if
dg1
then
394
-- probably needs a bit more
395
for
u
in
next
,
dg1
do
396
t1
[
u
]
=
copy
(
d1
)
397
end
398
end
399
else
400
report_error
(
"
case %i, bad index in unifying %s: %s of %s
"
,
1
,
kind
,
g1
,
nofindices
)
401
end
402
end
403
else
404
for
g1
,
d1
in
next
,
c
do
405
local
ug1
=
indices
[
g1
]
406
if
ug1
then
407
t1
[
ug1
]
=
d1
408
else
409
report_error
(
"
case %i, bad index in unifying %s: %s of %s
"
,
2
,
kind
,
g1
,
nofindices
)
410
end
411
end
412
end
413
done
[
c
]
=
t1
414
end
415
step
.
coverage
=
t1
416
end
417
end
418
--
419
local
rules
=
step
.
rules
420
if
rules
then
421
for
i
=
1
,
#
rules
do
422
local
rule
=
rules
[
i
]
423
--
424
local
before
=
rule
.
before
if
before
then
recover
(
before
)
end
425
local
after
=
rule
.
after
if
after
then
recover
(
after
)
end
426
local
current
=
rule
.
current
if
current
then
recover
(
current
)
end
427
--
428
local
replacements
=
rule
.
replacements
429
if
replacements
then
430
if
not
done
[
replacements
]
then
431
local
r
=
{
}
432
for
k
,
v
in
next
,
replacements
do
433
r
[
indices
[
k
]
]
=
indices
[
v
]
434
end
435
rule
.
replacements
=
r
436
done
[
replacements
]
=
r
437
end
438
end
439
end
440
end
441
end
442
end
443
end
444
end
445
--
446
unifythem
(
resources
.
sequences
)
447
unifythem
(
resources
.
sublookups
)
448
end
449 450
local
function
copyduplicates
(
fontdata
)
451
if
check_duplicates
then
452
local
descriptions
=
fontdata
.
descriptions
453
local
resources
=
fontdata
.
resources
454
local
duplicates
=
resources
.
duplicates
455
if
check_soft_hyphen
then
456
-- ebgaramond has a zero width empty soft hyphen
457
-- antykwatorunsks lacks a soft hyphen
458
local
ds
=
descriptions
[
0xAD
]
459
if
not
ds
or
ds
.
width
=
=
0
then
460
if
ds
then
461
descriptions
[
0xAD
]
=
nil
462
if
trace_unicodes
then
463
report_unicodes
(
"
patching soft hyphen
"
)
464
end
465
else
466
if
trace_unicodes
then
467
report_unicodes
(
"
adding soft hyphen
"
)
468
end
469
end
470
if
not
duplicates
then
471
duplicates
=
{
}
472
resources
.
duplicates
=
duplicates
473
end
474
local
dh
=
duplicates
[
0x2D
]
475
if
dh
then
476
dh
[
#
dh
+
1
]
=
{
[
0xAD
]
=
true
}
477
else
478
duplicates
[
0x2D
]
=
{
[
0xAD
]
=
true
}
479
end
480
end
481
end
482
if
duplicates
then
483
for
u
,
d
in
next
,
duplicates
do
484
local
du
=
descriptions
[
u
]
485
if
du
then
486
local
t
=
{
f_character_y
(
u
)
,
"
@
"
,
f_index
(
du
.
index
)
,
"
->
"
}
487
local
n
=
0
488
local
m
=
25
489
for
u
in
next
,
d
do
490
if
descriptions
[
u
]
then
491
if
n
<
m
then
492
t
[
n
+
4
]
=
f_character_n
(
u
)
493
end
494
else
495
local
c
=
copy
(
du
)
496
c
.
unicode
=
u
-- better this way
497
descriptions
[
u
]
=
c
498
if
n
<
m
then
499
t
[
n
+
4
]
=
f_character_y
(
u
)
500
end
501
end
502
n
=
n
+
1
503
end
504
if
trace_unicodes
then
505
if
n
<
=
m
then
506
report_unicodes
(
"
%i : % t
"
,
n
,
t
)
507
else
508
report_unicodes
(
"
%i : % t ...
"
,
n
,
t
)
509
end
510
end
511
else
512
-- what a mess
513
end
514
end
515
end
516
end
517
end
518 519
local
ignore
=
{
-- should we fix them?
520
[
"
notdef
"
]
=
true
,
521
[
"
.notdef
"
]
=
true
,
522
[
"
null
"
]
=
true
,
523
[
"
.null
"
]
=
true
,
524
[
"
nonmarkingreturn
"
]
=
true
,
525
}
526 527 528
local
function
checklookups
(
fontdata
,
missing
,
nofmissing
)
529
local
descriptions
=
fontdata
.
descriptions
530
local
resources
=
fontdata
.
resources
531
if
missing
and
nofmissing
and
nofmissing
<
=
0
then
532
return
533
end
534
--
535
local
singles
=
{
}
536
local
alternates
=
{
}
537
local
ligatures
=
{
}
538 539
if
not
missing
then
540
missing
=
{
}
541
nofmissing
=
0
542
for
u
,
d
in
next
,
descriptions
do
543
if
not
d
.
unicode
then
544
nofmissing
=
nofmissing
+
1
545
missing
[
u
]
=
true
546
end
547
end
548
end
549
local
function
collectthem
(
sequences
)
550
if
not
sequences
then
551
return
552
end
553
for
i
=
1
,
#
sequences
do
554
local
sequence
=
sequences
[
i
]
555
local
kind
=
sequence
.
type
556
local
steps
=
sequence
.
steps
557
if
steps
then
558
for
i
=
1
,
#
steps
do
559
local
step
=
steps
[
i
]
560
if
kind
=
=
"
gsub_single
"
then
561
local
c
=
step
.
coverage
562
if
c
then
563
singles
[
#
singles
+
1
]
=
c
564
end
565
elseif
kind
=
=
"
gsub_alternate
"
then
566
local
c
=
step
.
coverage
567
if
c
then
568
alternates
[
#
alternates
+
1
]
=
c
569
end
570
elseif
kind
=
=
"
gsub_ligature
"
then
571
local
c
=
step
.
coverage
572
if
c
then
573
ligatures
[
#
ligatures
+
1
]
=
c
574
end
575
end
576
end
577
end
578
end
579
end
580 581
collectthem
(
resources
.
sequences
)
582
collectthem
(
resources
.
sublookups
)
583 584
local
loops
=
0
585
while
true
do
586
loops
=
loops
+
1
587
local
old
=
nofmissing
588
for
i
=
1
,
#
singles
do
589
local
c
=
singles
[
i
]
590
for
g1
,
g2
in
next
,
c
do
591
if
missing
[
g1
]
then
592
local
u2
=
descriptions
[
g2
]
.
unicode
593
if
u2
then
594
missing
[
g1
]
=
false
595
descriptions
[
g1
]
.
unicode
=
u2
596
nofmissing
=
nofmissing
-
1
597
end
598
end
599
if
missing
[
g2
]
then
600
local
u1
=
descriptions
[
g1
]
.
unicode
601
if
u1
then
602
missing
[
g2
]
=
false
603
descriptions
[
g2
]
.
unicode
=
u1
604
nofmissing
=
nofmissing
-
1
605
end
606
end
607
end
608
end
609
for
i
=
1
,
#
alternates
do
610
local
c
=
alternates
[
i
]
611
-- maybe first a g1 loop and then a g2
612
for
g1
,
d1
in
next
,
c
do
613
if
missing
[
g1
]
then
614
for
i
=
1
,
#
d1
do
615
local
g2
=
d1
[
i
]
616
local
u2
=
descriptions
[
g2
]
.
unicode
617
if
u2
then
618
missing
[
g1
]
=
false
619
descriptions
[
g1
]
.
unicode
=
u2
620
nofmissing
=
nofmissing
-
1
621
end
622
end
623
end
624
if
not
missing
[
g1
]
then
625
for
i
=
1
,
#
d1
do
626
local
g2
=
d1
[
i
]
627
if
missing
[
g2
]
then
628
local
u1
=
descriptions
[
g1
]
.
unicode
629
if
u1
then
630
missing
[
g2
]
=
false
631
descriptions
[
g2
]
.
unicode
=
u1
632
nofmissing
=
nofmissing
-
1
633
end
634
end
635
end
636
end
637
end
638
end
639
if
nofmissing
<
=
0
then
640
if
trace_unicodes
then
641
report_unicodes
(
"
all missings done in %s loops
"
,
loops
)
642
end
643
return
644
elseif
old
=
=
nofmissing
then
645
break
646
end
647
end
648 649
local
t
,
n
-- no need to insert/remove and allocate many times
650 651
local
function
recursed
(
c
)
652
for
g
,
d
in
next
,
c
do
653
if
g
~
=
"
ligature
"
then
654
local
u
=
descriptions
[
g
]
.
unicode
655
if
u
then
656
n
=
n
+
1
657
t
[
n
]
=
u
658
recursed
(
d
)
659
n
=
n
-
1
660
end
661
elseif
missing
[
d
]
then
662
local
l
=
{
}
663
local
m
=
0
664
for
i
=
1
,
n
do
665
local
u
=
t
[
i
]
666
if
type
(
u
)
=
=
"
table
"
then
667
for
i
=
1
,
#
u
do
668
m
=
m
+
1
669
l
[
m
]
=
u
[
i
]
670
end
671
else
672
m
=
m
+
1
673
l
[
m
]
=
u
674
end
675
end
676
missing
[
d
]
=
false
677
descriptions
[
d
]
.
unicode
=
l
678
nofmissing
=
nofmissing
-
1
679
end
680
end
681
end
682 683
if
nofmissing
>
0
then
684
t
=
{
}
685
n
=
0
686
local
loops
=
0
687
while
true
do
688
loops
=
loops
+
1
689
local
old
=
nofmissing
690
for
i
=
1
,
#
ligatures
do
691
recursed
(
ligatures
[
i
]
)
692
end
693
if
nofmissing
<
=
0
then
694
if
trace_unicodes
then
695
report_unicodes
(
"
all missings done in %s loops
"
,
loops
)
696
end
697
return
698
elseif
old
=
=
nofmissing
then
699
break
700
end
701
end
702
t
=
nil
703
n
=
0
704
end
705 706
if
trace_unicodes
and
nofmissing
>
0
then
707
local
done
=
{
}
708
for
i
,
r
in
next
,
missing
do
709
if
r
then
710
local
data
=
descriptions
[
i
]
711
local
name
=
data
and
data
.
name
or
f_index
(
i
)
712
if
not
ignore
[
name
]
then
713
done
[
name
]
=
true
714
end
715
end
716
end
717
if
next
(
done
)
then
718
report_unicodes
(
"
not unicoded: % t
"
,
sortedkeys
(
done
)
)
719
end
720
end
721
end
722 723
local
firstprivate
=
fonts
.
privateoffsets
and
fonts
.
privateoffsets
.
textbase
or
0xF0000
724
local
puafirst
=
0xE000
725
local
pualast
=
0xF8FF
726 727
local
function
unifymissing
(
fontdata
)
728
if
not
fonts
.
mappings
then
729
require
(
"
font-map
"
)
730
require
(
"
font-agl
"
)
731
end
732
local
unicodes
=
{
}
733
local
resources
=
fontdata
.
resources
734
resources
.
unicodes
=
unicodes
735
for
unicode
,
d
in
next
,
fontdata
.
descriptions
do
736
if
unicode
<
privateoffset
then
737
if
unicode
>
=
puafirst
and
unicode
<
=
pualast
then
738
-- report_unicodes("resolving private unicode %U",unicode)
739
else
740
local
name
=
d
.
name
741
if
name
then
742
unicodes
[
name
]
=
unicode
743
end
744
end
745
else
746
-- report_unicodes("resolving private unicode %U",unicode)
747
end
748
end
749
fonts
.
mappings
.
addtounicode
(
fontdata
,
fontdata
.
filename
,
checklookups
)
750
resources
.
unicodes
=
nil
751
end
752 753
local
function
unifyglyphs
(
fontdata
,
usenames
)
754
local
private
=
fontdata
.
private
or
privateoffset
755
local
glyphs
=
fontdata
.
glyphs
756
local
indices
=
{
}
757
local
descriptions
=
{
}
758
local
names
=
usenames
and
{
}
759
local
resources
=
fontdata
.
resources
760
local
zero
=
glyphs
[
0
]
761
local
zerocode
=
zero
.
unicode
762
if
not
zerocode
then
763
zerocode
=
private
764
zero
.
unicode
=
zerocode
765
private
=
private
+
1
766
end
767
descriptions
[
zerocode
]
=
zero
768
if
names
then
769
local
name
=
glyphs
[
0
]
.
name
or
f_private
(
zerocode
)
770
indices
[
0
]
=
name
771
names
[
name
]
=
zerocode
772
else
773
indices
[
0
]
=
zerocode
774
end
775
--
776
if
names
then
777
-- seldom uses, we don't issue message ... this branch might even go away
778
for
index
=
1
,
#
glyphs
do
779
local
glyph
=
glyphs
[
index
]
780
local
unicode
=
glyph
.
unicode
-- this is the primary one
781
if
not
unicode
then
782
unicode
=
private
783
local
name
=
glyph
.
name
or
f_private
(
unicode
)
784
indices
[
index
]
=
name
785
names
[
name
]
=
unicode
786
private
=
private
+
1
787
elseif
unicode
>
=
firstprivate
then
788
unicode
=
private
789
local
name
=
glyph
.
name
or
f_private
(
unicode
)
790
indices
[
index
]
=
name
791
names
[
name
]
=
unicode
792
private
=
private
+
1
793
elseif
unicode
>
=
puafirst
and
unicode
<
=
pualast
then
794
local
name
=
glyph
.
name
or
f_private
(
unicode
)
795
indices
[
index
]
=
name
796
names
[
name
]
=
unicode
797
elseif
descriptions
[
unicode
]
then
798
unicode
=
private
799
local
name
=
glyph
.
name
or
f_private
(
unicode
)
800
indices
[
index
]
=
name
801
names
[
name
]
=
unicode
802
private
=
private
+
1
803
else
804
local
name
=
glyph
.
name
or
f_unicode
(
unicode
)
805
indices
[
index
]
=
name
806
names
[
name
]
=
unicode
807
end
808
descriptions
[
unicode
]
=
glyph
809
end
810
elseif
trace_unicodes
then
811
for
index
=
1
,
#
glyphs
do
812
local
glyph
=
glyphs
[
index
]
813
local
unicode
=
glyph
.
unicode
-- this is the primary one
814
if
not
unicode
then
815
unicode
=
private
816
indices
[
index
]
=
unicode
817
private
=
private
+
1
818
elseif
unicode
>
=
firstprivate
then
819
local
name
=
glyph
.
name
820
if
name
then
821
report_unicodes
(
"
moving glyph %a indexed %05X from private %U to %U
"
,
name
,
index
,
unicode
,
private
)
822
else
823
report_unicodes
(
"
moving glyph indexed %05X from private %U to %U
"
,
index
,
unicode
,
private
)
824
end
825
unicode
=
private
826
indices
[
index
]
=
unicode
827
private
=
private
+
1
828
elseif
unicode
>
=
puafirst
and
unicode
<
=
pualast
then
829
local
name
=
glyph
.
name
830
if
name
then
831
report_unicodes
(
"
keeping private unicode %U for glyph %a indexed %05X
"
,
unicode
,
name
,
index
)
832
else
833
report_unicodes
(
"
keeping private unicode %U for glyph indexed %05X
"
,
unicode
,
index
)
834
end
835
indices
[
index
]
=
unicode
836
elseif
descriptions
[
unicode
]
then
837
local
name
=
glyph
.
name
838
if
name
then
839
report_unicodes
(
"
assigning duplicate unicode %U to %U for glyph %a indexed %05X
"
,
unicode
,
private
,
name
,
index
)
840
else
841
report_unicodes
(
"
assigning duplicate unicode %U to %U for glyph indexed %05X
"
,
unicode
,
private
,
index
)
842
end
843
unicode
=
private
844
indices
[
index
]
=
unicode
845
private
=
private
+
1
846
else
847
indices
[
index
]
=
unicode
848
end
849
descriptions
[
unicode
]
=
glyph
850
end
851
else
852
for
index
=
1
,
#
glyphs
do
853
local
glyph
=
glyphs
[
index
]
854
local
unicode
=
glyph
.
unicode
-- this is the primary one
855
if
not
unicode
then
856
unicode
=
private
857
indices
[
index
]
=
unicode
858
private
=
private
+
1
859
elseif
unicode
>
=
firstprivate
then
860
local
name
=
glyph
.
name
861
unicode
=
private
862
indices
[
index
]
=
unicode
863
private
=
private
+
1
864
elseif
unicode
>
=
puafirst
and
unicode
<
=
pualast
then
865
local
name
=
glyph
.
name
866
indices
[
index
]
=
unicode
867
elseif
descriptions
[
unicode
]
then
868
local
name
=
glyph
.
name
869
unicode
=
private
870
indices
[
index
]
=
unicode
871
private
=
private
+
1
872
else
873
indices
[
index
]
=
unicode
874
end
875
descriptions
[
unicode
]
=
glyph
876
end
877
end
878
--
879
for
index
=
1
,
#
glyphs
do
880
local
math
=
glyphs
[
index
]
.
math
881
if
math
then
882
local
list
=
math
.
vparts
883
if
list
then
884
for
i
=
1
,
#
list
do
local
l
=
list
[
i
]
l
.
glyph
=
indices
[
l
.
glyph
]
end
885
end
886
local
list
=
math
.
hparts
887
if
list
then
888
for
i
=
1
,
#
list
do
local
l
=
list
[
i
]
l
.
glyph
=
indices
[
l
.
glyph
]
end
889
end
890
local
list
=
math
.
vvariants
891
if
list
then
892
-- for i=1,#list do local l = list[i] l.glyph = indices[l.glyph] end
893
for
i
=
1
,
#
list
do
list
[
i
]
=
indices
[
list
[
i
]
]
end
894
end
895
local
list
=
math
.
hvariants
896
if
list
then
897
-- for i=1,#list do local l = list[i] l.glyph = indices[l.glyph] end
898
for
i
=
1
,
#
list
do
list
[
i
]
=
indices
[
list
[
i
]
]
end
899
end
900
end
901
end
902
--
903
local
colorpalettes
=
resources
.
colorpalettes
904
if
colorpalettes
then
905
for
index
=
1
,
#
glyphs
do
906
local
colors
=
glyphs
[
index
]
.
colors
907
if
colors
then
908
for
i
=
1
,
#
colors
do
909
local
c
=
colors
[
i
]
910
c
.
slot
=
indices
[
c
.
slot
]
911
end
912
end
913
end
914
end
915
--
916
fontdata
.
private
=
private
917
fontdata
.
glyphs
=
nil
918
fontdata
.
names
=
names
919
fontdata
.
descriptions
=
descriptions
920
fontdata
.
hashmethod
=
hashmethod
921
--
922
return
indices
,
names
923
end
924 925
local
p_crappyname
do
926 927
local
p_hex
=
R
(
"
af
"
,
"
AF
"
,
"
09
"
)
928
local
p_digit
=
R
(
"
09
"
)
929
local
p_done
=
S
(
"
._-
"
)
^
0
+
P
(
-1
)
930
local
p_alpha
=
R
(
"
az
"
,
"
AZ
"
)
931
local
p_ALPHA
=
R
(
"
AZ
"
)
932 933
p_crappyname
=
(
934
-- (P("uni") + P("UNI") + P("Uni") + P("U") + P("u"))
935
lpeg
.
utfchartabletopattern
(
{
"
uni
"
,
"
u
"
}
,
true
)
936
*
S
(
"
Xx_
"
)
^
0
937
*
p_hex
^
1
938
-- + (P("identity") + P("Identity") + P("IDENTITY") + P("glyph") + P("jamo"))
939
+
lpeg
.
utfchartabletopattern
(
{
"
identity
"
,
"
glyph
"
,
"
jamo
"
}
,
true
)
940
*
p_hex
^
1
941
-- + (P("index") + P("Index") + P("INDEX")+ P("afii"))
942
+
lpeg
.
utfchartabletopattern
(
{
"
index
"
,
"
afii
"
}
,
true
)
943
*
p_digit
^
1
944
-- also happens l
945
+
p_digit
946
*
p_hex
^
3
947
+
p_alpha
948
*
p_digit
^
1
949
-- sort of special
950
+
P
(
"
aj
"
)
951
*
p_digit
^
1
952
+
P
(
"
eh_
"
)
953
*
(
p_digit
^
1
+
p_ALPHA
*
p_digit
^
1
)
954
+
(
1
-
P
(
"
_
"
)
)
^
1
955
*
P
(
"
_uni
"
)
956
*
p_hex
^
1
957
+
P
(
"
_
"
)
958
*
P
(
1
)
^
1
959
)
*
p_done
960 961
end
962 963
-- In context we only keep glyph names because of tracing and access by name
964
-- so weird names make no sense.
965 966
local
forcekeep
=
false
-- only for testing something
967 968
directives
.
register
(
"
otf.keepnames
"
,
function
(
v
)
969
report_cleanup
(
"
keeping weird glyph names, expect larger files and more memory usage
"
)
970
forcekeep
=
v
971
end
)
972 973
local
function
stripredundant
(
fontdata
)
974
local
descriptions
=
fontdata
.
descriptions
975
if
descriptions
then
976
local
n
=
0
977
local
c
=
0
978
-- in context we always strip
979
if
(
not
context
and
fonts
.
privateoffsets
.
keepnames
)
or
forcekeep
then
980
for
unicode
,
d
in
next
,
descriptions
do
981
if
d
.
class
=
=
"
base
"
then
982
d
.
class
=
nil
983
c
=
c
+
1
984
end
985
end
986
else
987
for
unicode
,
d
in
next
,
descriptions
do
988
local
name
=
d
.
name
989
if
name
and
lpegmatch
(
p_crappyname
,
name
)
then
990
d
.
name
=
nil
991
n
=
n
+
1
992
end
993
if
d
.
class
=
=
"
base
"
then
994
d
.
class
=
nil
995
c
=
c
+
1
996
end
997
end
998
end
999
if
trace_cleanup
then
1000
if
n
>
0
then
1001
report_cleanup
(
"
%s bogus names removed (verbose unicode)
"
,
n
)
1002
end
1003
if
c
>
0
then
1004
report_cleanup
(
"
%s base class tags removed (default is base)
"
,
c
)
1005
end
1006
end
1007
end
1008
end
1009 1010
readers
.
stripredundant
=
stripredundant
1011 1012
function
readers
.
getcomponents
(
fontdata
)
-- handy for resolving ligatures when names are missing
1013
local
resources
=
fontdata
.
resources
1014
if
resources
then
1015
local
sequences
=
resources
.
sequences
1016
if
sequences
then
1017
local
collected
=
{
}
1018
for
i
=
1
,
#
sequences
do
1019
local
sequence
=
sequences
[
i
]
1020
if
sequence
.
type
=
=
"
gsub_ligature
"
then
1021
local
steps
=
sequence
.
steps
1022
if
steps
then
1023
local
l
=
{
}
1024
local
function
traverse
(
p
,
k
,
v
)
1025
if
k
=
=
"
ligature
"
then
1026
collected
[
v
]
=
{
unpack
(
l
)
}
1027
else
1028
insert
(
l
,
k
)
1029
for
k
,
vv
in
next
,
v
do
1030
traverse
(
p
,
k
,
vv
)
1031
end
1032
remove
(
l
)
1033
end
1034
end
1035
for
i
=
1
,
#
steps
do
1036
-- we actually had/have this in base mode
1037
local
c
=
steps
[
i
]
.
coverage
1038
if
c
then
1039
for
k
,
v
in
next
,
c
do
1040
traverse
(
k
,
k
,
v
)
1041
end
1042
end
1043
end
1044
end
1045
end
1046
end
1047
if
next
(
collected
)
then
1048
-- remove self referring
1049
-- for k, v in next, collected do
1050
-- for i=1,#v do
1051
-- local vi = v[i]
1052
-- if vi == k then
1053
-- -- report("removing self referring ligature @ slot %5X from collected (1)",k)
1054
-- collected[k] = nil
1055
-- end
1056
-- end
1057
-- end
1058
while
true
do
1059
local
done
=
false
1060
for
k
,
v
in
next
,
collected
do
1061
for
i
=
1
,
#
v
do
1062
local
vi
=
v
[
i
]
1063
if
vi
=
=
k
then
1064
-- report("removing self referring ligature @ slot %5X from collected (2)",k)
1065
collected
[
k
]
=
nil
1066
break
1067
else
1068
local
c
=
collected
[
vi
]
1069
if
c
then
1070
done
=
true
1071
local
t
=
{
}
1072
local
n
=
i
-
1
1073
for
j
=
1
,
n
do
1074
t
[
j
]
=
v
[
j
]
1075
end
1076
for
j
=
1
,
#
c
do
1077
n
=
n
+
1
1078
t
[
n
]
=
c
[
j
]
1079
end
1080
for
j
=
i
+
1
,
#
v
do
1081
n
=
n
+
1
1082
t
[
n
]
=
v
[
j
]
1083
end
1084
collected
[
k
]
=
t
1085
break
1086
end
1087
end
1088
end
1089
end
1090
if
not
done
then
1091
break
1092
end
1093
end
1094
return
collected
1095
end
1096
end
1097
end
1098
end
1099 1100
readers
.
unifymissing
=
unifymissing
1101 1102
function
readers
.
rehash
(
fontdata
,
hashmethod
)
-- TODO: combine loops in one
1103
if
not
(
fontdata
and
fontdata
.
glyphs
)
then
1104
return
1105
end
1106
if
hashmethod
=
=
"
indices
"
then
1107
fontdata
.
hashmethod
=
"
indices
"
1108
elseif
hashmethod
=
=
"
names
"
then
1109
fontdata
.
hashmethod
=
"
names
"
1110
local
indices
=
unifyglyphs
(
fontdata
,
true
)
1111
unifyresources
(
fontdata
,
indices
)
1112
copyduplicates
(
fontdata
)
1113
unifymissing
(
fontdata
)
1114
-- stripredundant(fontdata)
1115
else
1116
fontdata
.
hashmethod
=
"
unicodes
"
1117
local
indices
=
unifyglyphs
(
fontdata
)
1118
unifyresources
(
fontdata
,
indices
)
1119
copyduplicates
(
fontdata
)
1120
unifymissing
(
fontdata
)
1121
stripredundant
(
fontdata
)
1122
end
1123
-- maybe here components
1124
end
1125 1126
function
readers
.
checkhash
(
fontdata
)
1127
local
hashmethod
=
fontdata
.
hashmethod
1128
if
hashmethod
=
=
"
unicodes
"
then
1129
fontdata
.
names
=
nil
-- just to be sure
1130
elseif
hashmethod
=
=
"
names
"
and
fontdata
.
names
then
1131
unifyresources
(
fontdata
,
fontdata
.
names
)
1132
copyduplicates
(
fontdata
)
1133
fontdata
.
hashmethod
=
"
unicodes
"
1134
fontdata
.
names
=
nil
-- no need for it
1135
else
1136
readers
.
rehash
(
fontdata
,
"
unicodes
"
)
1137
end
1138
end
1139 1140
function
readers
.
addunicodetable
(
fontdata
)
1141
local
resources
=
fontdata
.
resources
1142
local
unicodes
=
resources
.
unicodes
1143
if
not
unicodes
then
1144
local
descriptions
=
fontdata
.
descriptions
1145
if
descriptions
then
1146
unicodes
=
{
}
1147
resources
.
unicodes
=
unicodes
1148
for
u
,
d
in
next
,
descriptions
do
1149
local
n
=
d
.
name
1150
if
n
then
1151
unicodes
[
n
]
=
u
1152
end
1153
end
1154
end
1155
end
1156
end
1157 1158
-- for the moment here:
1159 1160
local
concat
,
sort
=
table
.
concat
,
table
.
sort
1161
local
next
,
type
,
tostring
=
next
,
type
,
tostring
1162 1163
local
criterium
=
1
1164
local
threshold
=
0
1165 1166
local
trace_packing
=
false
trackers
.
register
(
"
otf.packing
"
,
function
(
v
)
trace_packing
=
v
end
)
1167
local
trace_loading
=
false
trackers
.
register
(
"
otf.loading
"
,
function
(
v
)
trace_loading
=
v
end
)
1168 1169
local
report_otf
=
logs
.
reporter
(
"
fonts
"
,
"
otf loading
"
)
1170 1171
local
function
tabstr_normal
(
t
)
1172
local
s
=
{
}
1173
local
n
=
0
1174
for
k
,
v
in
next
,
t
do
1175
n
=
n
+
1
1176
if
type
(
v
)
=
=
"
table
"
then
1177
s
[
n
]
=
k
.
.
"
>
"
.
.
tabstr_normal
(
v
)
1178
elseif
v
=
=
true
then
1179
s
[
n
]
=
k
.
.
"
+
"
-- "=true"
1180
elseif
v
then
1181
s
[
n
]
=
k
.
.
"
=
"
.
.
v
1182
else
1183
s
[
n
]
=
k
.
.
"
-
"
-- "=false"
1184
end
1185
end
1186
if
n
=
=
0
then
1187
return
"
"
1188
elseif
n
=
=
1
then
1189
return
s
[
1
]
1190
else
1191
sort
(
s
)
-- costly but needed (occasional wrong hit otherwise)
1192
return
concat
(
s
,
"
,
"
)
1193
end
1194
end
1195 1196
local
function
tabstr_flat
(
t
)
1197
local
s
=
{
}
1198
local
n
=
0
1199
for
k
,
v
in
next
,
t
do
1200
n
=
n
+
1
1201
s
[
n
]
=
k
.
.
"
=
"
.
.
v
1202
end
1203
if
n
=
=
0
then
1204
return
"
"
1205
elseif
n
=
=
1
then
1206
return
s
[
1
]
1207
else
1208
sort
(
s
)
-- costly but needed (occasional wrong hit otherwise)
1209
return
concat
(
s
,
"
,
"
)
1210
end
1211
end
1212 1213
local
function
tabstr_mixed
(
t
)
-- indexed
1214
local
s
=
{
}
1215
local
n
=
#
t
1216
if
n
=
=
0
then
1217
return
"
"
1218
elseif
n
=
=
1
then
1219
local
k
=
t
[
1
]
1220
if
k
=
=
true
then
1221
return
"
++
"
-- we need to distinguish from "true"
1222
elseif
k
=
=
false
then
1223
return
"
--
"
-- we need to distinguish from "false"
1224
else
1225
return
tostring
(
k
)
-- number or string
1226
end
1227
else
1228
for
i
=
1
,
n
do
1229
local
k
=
t
[
i
]
1230
if
k
=
=
true
then
1231
s
[
i
]
=
"
++
"
-- we need to distinguish from "true"
1232
elseif
k
=
=
false
then
1233
s
[
i
]
=
"
--
"
-- we need to distinguish from "false"
1234
else
1235
s
[
i
]
=
k
-- number or string
1236
end
1237
end
1238
return
concat
(
s
,
"
,
"
)
1239
end
1240
end
1241 1242
local
function
tabstr_boolean
(
t
)
1243
local
s
=
{
}
1244
local
n
=
0
1245
for
k
,
v
in
next
,
t
do
1246
n
=
n
+
1
1247
if
v
then
1248
s
[
n
]
=
k
.
.
"
+
"
1249
else
1250
s
[
n
]
=
k
.
.
"
-
"
1251
end
1252
end
1253
if
n
=
=
0
then
1254
return
"
"
1255
elseif
n
=
=
1
then
1256
return
s
[
1
]
1257
else
1258
sort
(
s
)
-- costly but needed (occasional wrong hit otherwise)
1259
return
concat
(
s
,
"
,
"
)
1260
end
1261
end
1262 1263
-- beware: we cannot unpack and repack the same table because then sharing
1264
-- interferes (we could catch this if needed) .. so for now: save, reload
1265
-- and repack in such cases (never needed anyway) .. a tricky aspect is that
1266
-- we then need to sort more thanks to random hashing
1267 1268
function
readers
.
pack
(
data
)
1269 1270
if
data
then
1271 1272
local
h
,
t
,
c
=
{
}
,
{
}
,
{
}
1273
local
hh
,
tt
,
cc
=
{
}
,
{
}
,
{
}
1274
local
nt
,
ntt
=
0
,
0
1275 1276
local
function
pack_normal
(
v
)
1277
local
tag
=
tabstr_normal
(
v
)
1278
local
ht
=
h
[
tag
]
1279
if
ht
then
1280
c
[
ht
]
=
c
[
ht
]
+
1
1281
return
ht
1282
else
1283
nt
=
nt
+
1
1284
t
[
nt
]
=
v
1285
h
[
tag
]
=
nt
1286
c
[
nt
]
=
1
1287
return
nt
1288
end
1289
end
1290 1291
local
function
pack_normal_cc
(
v
)
1292
local
tag
=
tabstr_normal
(
v
)
1293
local
ht
=
h
[
tag
]
1294
if
ht
then
1295
c
[
ht
]
=
c
[
ht
]
+
1
1296
return
ht
1297
else
1298
v
[
1
]
=
0
1299
nt
=
nt
+
1
1300
t
[
nt
]
=
v
1301
h
[
tag
]
=
nt
1302
c
[
nt
]
=
1
1303
return
nt
1304
end
1305
end
1306 1307
local
function
pack_flat
(
v
)
1308
local
tag
=
tabstr_flat
(
v
)
1309
local
ht
=
h
[
tag
]
1310
if
ht
then
1311
c
[
ht
]
=
c
[
ht
]
+
1
1312
return
ht
1313
else
1314
nt
=
nt
+
1
1315
t
[
nt
]
=
v
1316
h
[
tag
]
=
nt
1317
c
[
nt
]
=
1
1318
return
nt
1319
end
1320
end
1321 1322
local
function
pack_indexed
(
v
)
1323
local
tag
=
concat
(
v
,
"
"
)
1324
local
ht
=
h
[
tag
]
1325
if
ht
then
1326
c
[
ht
]
=
c
[
ht
]
+
1
1327
return
ht
1328
else
1329
nt
=
nt
+
1
1330
t
[
nt
]
=
v
1331
h
[
tag
]
=
nt
1332
c
[
nt
]
=
1
1333
return
nt
1334
end
1335
end
1336 1337
local
function
pack_mixed
(
v
)
1338
local
tag
=
tabstr_mixed
(
v
)
1339
local
ht
=
h
[
tag
]
1340
if
ht
then
1341
c
[
ht
]
=
c
[
ht
]
+
1
1342
return
ht
1343
else
1344
nt
=
nt
+
1
1345
t
[
nt
]
=
v
1346
h
[
tag
]
=
nt
1347
c
[
nt
]
=
1
1348
return
nt
1349
end
1350
end
1351 1352
-- saves a lot on noto sans
1353 1354
-- can be made more clever
1355 1356
local
function
pack_boolean
(
v
)
1357
local
tag
=
tabstr_boolean
(
v
)
1358
local
ht
=
h
[
tag
]
1359
if
ht
then
1360
c
[
ht
]
=
c
[
ht
]
+
1
1361
return
ht
1362
else
1363
nt
=
nt
+
1
1364
t
[
nt
]
=
v
1365
h
[
tag
]
=
nt
1366
c
[
nt
]
=
1
1367
return
nt
1368
end
1369
end
1370 1371
local
function
pack_final
(
v
)
1372
-- v == number
1373
if
c
[
v
]
<
=
criterium
then
1374
return
t
[
v
]
1375
else
1376
-- compact hash
1377
local
hv
=
hh
[
v
]
1378
if
hv
then
1379
return
hv
1380
else
1381
ntt
=
ntt
+
1
1382
tt
[
ntt
]
=
t
[
v
]
1383
hh
[
v
]
=
ntt
1384
cc
[
ntt
]
=
c
[
v
]
1385
return
ntt
1386
end
1387
end
1388
end
1389 1390
local
function
pack_final_cc
(
v
)
1391
-- v == number
1392
if
c
[
v
]
<
=
criterium
then
1393
return
t
[
v
]
1394
else
1395
-- compact hash
1396
local
hv
=
hh
[
v
]
1397
if
hv
then
1398
return
hv
1399
else
1400
ntt
=
ntt
+
1
1401
tt
[
ntt
]
=
t
[
v
]
1402
hh
[
v
]
=
ntt
1403
cc
[
ntt
]
=
c
[
v
]
1404
return
ntt
1405
end
1406
end
1407
end
1408 1409
local
function
success
(
stage
,
pass
)
1410
if
nt
=
=
0
then
1411
if
trace_loading
or
trace_packing
then
1412
report_otf
(
"
pack quality: nothing to pack
"
)
1413
end
1414
return
false
1415
elseif
nt
>
=
threshold
then
1416
local
one
=
0
1417
local
two
=
0
1418
local
rest
=
0
1419
if
pass
=
=
1
then
1420
for
k
,
v
in
next
,
c
do
1421
if
v
=
=
1
then
1422
one
=
one
+
1
1423
elseif
v
=
=
2
then
1424
two
=
two
+
1
1425
else
1426
rest
=
rest
+
1
1427
end
1428
end
1429
else
1430
for
k
,
v
in
next
,
cc
do
1431
if
v
>
20
then
1432
rest
=
rest
+
1
1433
elseif
v
>
10
then
1434
two
=
two
+
1
1435
else
1436
one
=
one
+
1
1437
end
1438
end
1439
data
.
tables
=
tt
1440
end
1441
if
trace_loading
or
trace_packing
then
1442
report_otf
(
"
pack quality: stage %s, pass %s, %s packed, 1-10:%s, 11-20:%s, rest:%s (criterium: %s)
"
,
1443
stage
,
pass
,
one
+
two
+
rest
,
one
,
two
,
rest
,
criterium
)
1444
end
1445
return
true
1446
else
1447
if
trace_loading
or
trace_packing
then
1448
report_otf
(
"
pack quality: stage %s, pass %s, %s packed, aborting pack (threshold: %s)
"
,
1449
stage
,
pass
,
nt
,
threshold
)
1450
end
1451
return
false
1452
end
1453
end
1454 1455
local
function
packers
(
pass
)
1456
if
pass
=
=
1
then
1457
return
pack_normal
,
pack_indexed
,
pack_flat
,
pack_boolean
,
pack_mixed
,
pack_normal_cc
1458
else
1459
return
pack_final
,
pack_final
,
pack_final
,
pack_final
,
pack_final
,
pack_final_cc
1460
end
1461
end
1462 1463
local
resources
=
data
.
resources
1464
local
sequences
=
resources
.
sequences
1465
local
sublookups
=
resources
.
sublookups
1466
local
features
=
resources
.
features
1467
local
palettes
=
resources
.
colorpalettes
1468
local
variable
=
resources
.
variabledata
1469 1470
local
chardata
=
characters
and
characters
.
data
1471
local
descriptions
=
data
.
descriptions
or
data
.
glyphs
1472 1473
if
not
descriptions
then
1474
return
1475
end
1476 1477
for
pass
=
1
,
2
do
1478 1479
if
trace_packing
then
1480
report_otf
(
"
start packing: stage 1, pass %s
"
,
pass
)
1481
end
1482 1483
local
pack_normal
,
pack_indexed
,
pack_flat
,
pack_boolean
,
pack_mixed
,
pack_normal_cc
=
packers
(
pass
)
1484 1485
for
unicode
,
description
in
next
,
descriptions
do
1486
local
boundingbox
=
description
.
boundingbox
1487
if
boundingbox
then
1488
description
.
boundingbox
=
pack_indexed
(
boundingbox
)
1489
end
1490
local
math
=
description
.
math
1491
if
math
then
1492
local
kerns
=
math
.
kerns
1493
if
kerns
then
1494
for
tag
,
kern
in
next
,
kerns
do
1495
kerns
[
tag
]
=
pack_normal
(
kern
)
1496
end
1497
end
1498
end
1499
-- if palettes then
1500
-- local color = description.color
1501
-- if color then
1502
-- for i=1,#color do
1503
-- color[i] = pack_normal(color[i])
1504
-- end
1505
-- end
1506
-- end
1507
end
1508 1509
local
function
packthem
(
sequences
)
1510
for
i
=
1
,
#
sequences
do
1511
local
sequence
=
sequences
[
i
]
1512
local
kind
=
sequence
.
type
1513
local
steps
=
sequence
.
steps
1514
local
order
=
sequence
.
order
1515
local
features
=
sequence
.
features
1516
local
flags
=
sequence
.
flags
1517
if
steps
then
1518
for
i
=
1
,
#
steps
do
1519
local
step
=
steps
[
i
]
1520
if
kind
=
=
"
gpos_pair
"
then
1521
local
c
=
step
.
coverage
1522
if
c
then
1523
if
step
.
format
~
=
"
pair
"
then
1524
for
g1
,
d1
in
next
,
c
do
1525
c
[
g1
]
=
pack_normal
(
d1
)
1526
end
1527
elseif
step
.
shared
then
1528
-- This branch results from classes. We already share at the reader end. Maybe
1529
-- the sharing should be moved there altogether but it becomes kind of messy
1530
-- then. Here we're still wasting time because in the second pass we serialize
1531
-- and hash. So we compromise. We could merge the two passes ...
1532
local
shared
=
{
}
1533
for
g1
,
d1
in
next
,
c
do
1534
for
g2
,
d2
in
next
,
d1
do
1535
if
not
shared
[
d2
]
then
1536
local
f
=
d2
[
1
]
if
f
and
f
~
=
true
then
d2
[
1
]
=
pack_indexed
(
f
)
end
1537
local
s
=
d2
[
2
]
if
s
and
s
~
=
true
then
d2
[
2
]
=
pack_indexed
(
s
)
end
1538
shared
[
d2
]
=
true
1539
end
1540
end
1541
end
1542
if
pass
=
=
2
then
1543
step
.
shared
=
nil
-- weird, so dups
1544
end
1545
else
1546
for
g1
,
d1
in
next
,
c
do
1547
for
g2
,
d2
in
next
,
d1
do
1548
local
f
=
d2
[
1
]
if
f
and
f
~
=
true
then
d2
[
1
]
=
pack_indexed
(
f
)
end
1549
local
s
=
d2
[
2
]
if
s
and
s
~
=
true
then
d2
[
2
]
=
pack_indexed
(
s
)
end
1550
end
1551
end
1552
end
1553
end
1554
elseif
kind
=
=
"
gpos_single
"
then
1555
local
c
=
step
.
coverage
1556
if
c
then
1557
if
step
.
format
=
=
"
single
"
then
1558
for
g1
,
d1
in
next
,
c
do
1559
if
d1
and
d1
~
=
true
then
1560
c
[
g1
]
=
pack_indexed
(
d1
)
1561
end
1562
end
1563
else
1564
step
.
coverage
=
pack_normal
(
c
)
1565
end
1566
end
1567
elseif
kind
=
=
"
gpos_cursive
"
then
1568
local
c
=
step
.
coverage
1569
if
c
then
1570
for
g1
,
d1
in
next
,
c
do
1571
local
f
=
d1
[
2
]
if
f
then
d1
[
2
]
=
pack_indexed
(
f
)
end
1572
local
s
=
d1
[
3
]
if
s
then
d1
[
3
]
=
pack_indexed
(
s
)
end
1573
end
1574
end
1575
elseif
kind
=
=
"
gpos_mark2base
"
or
kind
=
=
"
gpos_mark2mark
"
then
1576
local
c
=
step
.
baseclasses
1577
if
c
then
1578
for
g1
,
d1
in
next
,
c
do
1579
for
g2
,
d2
in
next
,
d1
do
1580
d1
[
g2
]
=
pack_indexed
(
d2
)
1581
end
1582
end
1583
end
1584
local
c
=
step
.
coverage
1585
if
c
then
1586
for
g1
,
d1
in
next
,
c
do
1587
d1
[
2
]
=
pack_indexed
(
d1
[
2
]
)
1588
end
1589
end
1590
elseif
kind
=
=
"
gpos_mark2ligature
"
then
1591
local
c
=
step
.
baseclasses
1592
if
c
then
1593
for
g1
,
d1
in
next
,
c
do
1594
for
g2
,
d2
in
next
,
d1
do
1595
for
g3
,
d3
in
next
,
d2
do
1596
d2
[
g3
]
=
pack_indexed
(
d3
)
1597
end
1598
end
1599
end
1600
end
1601
local
c
=
step
.
coverage
1602
if
c
then
1603
for
g1
,
d1
in
next
,
c
do
1604
d1
[
2
]
=
pack_indexed
(
d1
[
2
]
)
1605
end
1606
end
1607
end
1608
-- if ... chain ...
1609
local
rules
=
step
.
rules
1610
if
rules
then
1611
for
i
=
1
,
#
rules
do
1612
local
rule
=
rules
[
i
]
1613
local
r
=
rule
.
before
if
r
then
for
i
=
1
,
#
r
do
r
[
i
]
=
pack_boolean
(
r
[
i
]
)
end
end
1614
local
r
=
rule
.
after
if
r
then
for
i
=
1
,
#
r
do
r
[
i
]
=
pack_boolean
(
r
[
i
]
)
end
end
1615
local
r
=
rule
.
current
if
r
then
for
i
=
1
,
#
r
do
r
[
i
]
=
pack_boolean
(
r
[
i
]
)
end
end
1616
-- local r = rule.lookups if r then rule.lookups = pack_mixed (r) end
1617
local
r
=
rule
.
replacements
if
r
then
rule
.
replacements
=
pack_flat
(
r
)
end
1618
end
1619
end
1620
end
1621
end
1622
if
order
then
1623
sequence
.
order
=
pack_indexed
(
order
)
1624
end
1625
if
features
then
1626
for
script
,
feature
in
next
,
features
do
1627
features
[
script
]
=
pack_normal
(
feature
)
1628
end
1629
end
1630
if
flags
then
1631
sequence
.
flags
=
pack_normal
(
flags
)
1632
end
1633
end
1634
end
1635 1636
if
sequences
then
1637
packthem
(
sequences
)
1638
end
1639 1640
if
sublookups
then
1641
packthem
(
sublookups
)
1642
end
1643 1644
if
features
then
1645
for
k
,
list
in
next
,
features
do
1646
for
feature
,
spec
in
next
,
list
do
1647
list
[
feature
]
=
pack_normal
(
spec
)
1648
end
1649
end
1650
end
1651 1652
if
palettes
then
1653
for
i
=
1
,
#
palettes
do
1654
local
p
=
palettes
[
i
]
1655
for
j
=
1
,
#
p
do
1656
p
[
j
]
=
pack_indexed
(
p
[
j
]
)
1657
end
1658
end
1659 1660
end
1661 1662
if
variable
then
1663 1664
-- todo: segments
1665 1666
local
instances
=
variable
.
instances
1667
if
instances
then
1668
for
i
=
1
,
#
instances
do
1669
local
v
=
instances
[
i
]
.
values
1670
for
j
=
1
,
#
v
do
1671
v
[
j
]
=
pack_normal
(
v
[
j
]
)
1672
end
1673
end
1674
end
1675 1676
local
function
packdeltas
(
main
)
1677
if
main
then
1678
local
deltas
=
main
.
deltas
1679
if
deltas
then
1680
for
i
=
1
,
#
deltas
do
1681
local
di
=
deltas
[
i
]
1682
local
d
=
di
.
deltas
1683
-- local r = di.regions
1684
for
j
=
1
,
#
d
do
1685
d
[
j
]
=
pack_indexed
(
d
[
j
]
)
1686
end
1687
di
.
regions
=
pack_indexed
(
di
.
regions
)
1688
end
1689
end
1690
local
regions
=
main
.
regions
1691
if
regions
then
1692
for
i
=
1
,
#
regions
do
1693
local
r
=
regions
[
i
]
1694
for
j
=
1
,
#
r
do
1695
r
[
j
]
=
pack_normal
(
r
[
j
]
)
1696
end
1697
end
1698
end
1699
end
1700
end
1701 1702
packdeltas
(
variable
.
global
)
1703
packdeltas
(
variable
.
horizontal
)
1704
packdeltas
(
variable
.
vertical
)
1705
packdeltas
(
variable
.
metrics
)
1706 1707
end
1708 1709
if
not
success
(
1
,
pass
)
then
1710
return
1711
end
1712 1713
end
1714 1715
if
nt
>
0
then
1716 1717
for
pass
=
1
,
2
do
1718 1719
if
trace_packing
then
1720
report_otf
(
"
start packing: stage 2, pass %s
"
,
pass
)
1721
end
1722 1723
local
pack_normal
,
pack_indexed
,
pack_flat
,
pack_boolean
,
pack_mixed
,
pack_normal_cc
=
packers
(
pass
)
1724 1725
for
unicode
,
description
in
next
,
descriptions
do
1726
local
math
=
description
.
math
1727
if
math
then
1728
local
kerns
=
math
.
kerns
1729
if
kerns
then
1730
math
.
kerns
=
pack_normal
(
kerns
)
1731
end
1732
end
1733
end
1734 1735
local
function
packthem
(
sequences
)
1736
for
i
=
1
,
#
sequences
do
1737
local
sequence
=
sequences
[
i
]
1738
local
kind
=
sequence
.
type
1739
local
steps
=
sequence
.
steps
1740
local
features
=
sequence
.
features
1741
if
steps
then
1742
for
i
=
1
,
#
steps
do
1743
local
step
=
steps
[
i
]
1744
if
kind
=
=
"
gpos_pair
"
then
1745
local
c
=
step
.
coverage
1746
if
c
then
1747
if
step
.
format
=
=
"
pair
"
then
1748
for
g1
,
d1
in
next
,
c
do
1749
for
g2
,
d2
in
next
,
d1
do
1750
d1
[
g2
]
=
pack_normal
(
d2
)
1751
end
1752
end
1753
end
1754
end
1755
-- elseif kind == "gpos_cursive" then
1756
-- local c = step.coverage -- new
1757
-- if c then
1758
-- for g1, d1 in next, c do
1759
-- c[g1] = pack_normal_cc(d1)
1760
-- end
1761
-- end
1762
elseif
kind
=
=
"
gpos_mark2ligature
"
then
1763
local
c
=
step
.
baseclasses
-- new
1764
if
c
then
1765
for
g1
,
d1
in
next
,
c
do
1766
for
g2
,
d2
in
next
,
d1
do
1767
d1
[
g2
]
=
pack_normal
(
d2
)
1768
end
1769
end
1770
end
1771
end
1772
local
rules
=
step
.
rules
1773
if
rules
then
1774
for
i
=
1
,
#
rules
do
1775
local
rule
=
rules
[
i
]
1776
local
r
=
rule
.
before
if
r
then
rule
.
before
=
pack_normal
(
r
)
end
1777
local
r
=
rule
.
after
if
r
then
rule
.
after
=
pack_normal
(
r
)
end
1778
local
r
=
rule
.
current
if
r
then
rule
.
current
=
pack_normal
(
r
)
end
1779
end
1780
end
1781
end
1782
end
1783
if
features
then
1784
sequence
.
features
=
pack_normal
(
features
)
1785
end
1786
end
1787
end
1788
if
sequences
then
1789
packthem
(
sequences
)
1790
end
1791
if
sublookups
then
1792
packthem
(
sublookups
)
1793
end
1794
if
variable
then
1795
local
function
unpackdeltas
(
main
)
1796
if
main
then
1797
local
regions
=
main
.
regions
1798
if
regions
then
1799
main
.
regions
=
pack_normal
(
regions
)
1800
end
1801
end
1802
end
1803
unpackdeltas
(
variable
.
global
)
1804
unpackdeltas
(
variable
.
horizontal
)
1805
unpackdeltas
(
variable
.
vertical
)
1806
unpackdeltas
(
variable
.
metrics
)
1807
end
1808
-- if not success(2,pass) then
1809
-- -- return
1810
-- end
1811
end
1812 1813
for
pass
=
1
,
2
do
1814
if
trace_packing
then
1815
report_otf
(
"
start packing: stage 3, pass %s
"
,
pass
)
1816
end
1817 1818
local
pack_normal
,
pack_indexed
,
pack_flat
,
pack_boolean
,
pack_mixed
,
pack_normal_cc
=
packers
(
pass
)
1819 1820
local
function
packthem
(
sequences
)
1821
for
i
=
1
,
#
sequences
do
1822
local
sequence
=
sequences
[
i
]
1823
local
kind
=
sequence
.
type
1824
local
steps
=
sequence
.
steps
1825
local
features
=
sequence
.
features
1826
if
steps
then
1827
for
i
=
1
,
#
steps
do
1828
local
step
=
steps
[
i
]
1829
if
kind
=
=
"
gpos_pair
"
then
1830
local
c
=
step
.
coverage
1831
if
c
then
1832
if
step
.
format
=
=
"
pair
"
then
1833
for
g1
,
d1
in
next
,
c
do
1834
c
[
g1
]
=
pack_normal
(
d1
)
1835
end
1836
end
1837
end
1838
elseif
kind
=
=
"
gpos_cursive
"
then
1839
local
c
=
step
.
coverage
1840
if
c
then
1841
for
g1
,
d1
in
next
,
c
do
1842
c
[
g1
]
=
pack_normal_cc
(
d1
)
1843
end
1844
end
1845
end
1846
end
1847
end
1848
end
1849
end
1850 1851
if
sequences
then
1852
packthem
(
sequences
)
1853
end
1854
if
sublookups
then
1855
packthem
(
sublookups
)
1856
end
1857 1858
end
1859 1860
end
1861 1862
end
1863
end
1864 1865
local
unpacked_mt
=
{
1866
__index
=
1867
function
(
t
,
k
)
1868
t
[
k
]
=
false
1869
return
k
-- next time true
1870
end
1871
}
1872 1873
function
readers
.
unpack
(
data
)
1874 1875
if
data
then
1876
local
tables
=
data
.
tables
1877
if
tables
then
1878
local
resources
=
data
.
resources
1879
local
descriptions
=
data
.
descriptions
or
data
.
glyphs
1880
local
sequences
=
resources
.
sequences
1881
local
sublookups
=
resources
.
sublookups
1882
local
features
=
resources
.
features
1883
local
palettes
=
resources
.
colorpalettes
1884
local
variable
=
resources
.
variabledata
1885
local
unpacked
=
{
}
1886
setmetatable
(
unpacked
,
unpacked_mt
)
1887
for
unicode
,
description
in
next
,
descriptions
do
1888
local
tv
=
tables
[
description
.
boundingbox
]
1889
if
tv
then
1890
description
.
boundingbox
=
tv
1891
end
1892
local
math
=
description
.
math
1893
if
math
then
1894
local
kerns
=
math
.
kerns
1895
if
kerns
then
1896
local
tm
=
tables
[
kerns
]
1897
if
tm
then
1898
math
.
kerns
=
tm
1899
kerns
=
unpacked
[
tm
]
1900
end
1901
if
kerns
then
1902
for
k
,
kern
in
next
,
kerns
do
1903
local
tv
=
tables
[
kern
]
1904
if
tv
then
1905
kerns
[
k
]
=
tv
1906
end
1907
end
1908
end
1909
end
1910
end
1911
-- if palettes then
1912
-- local color = description.color
1913
-- if color then
1914
-- for i=1,#color do
1915
-- local tv = tables[color[i]]
1916
-- if tv then
1917
-- color[i] = tv
1918
-- end
1919
-- end
1920
-- end
1921
-- end
1922
end
1923 1924
-- local function expandranges(t,ranges)
1925
-- for i=1,#ranges do
1926
-- local r = ranges[i]
1927
-- for k=r[1],r[2] do
1928
-- t[k] = true
1929
-- end
1930
-- end
1931
-- end
1932 1933
local
function
unpackthem
(
sequences
)
1934
for
i
=
1
,
#
sequences
do
1935
local
sequence
=
sequences
[
i
]
1936
local
kind
=
sequence
.
type
1937
local
steps
=
sequence
.
steps
1938
local
order
=
sequence
.
order
1939
local
features
=
sequence
.
features
1940
local
flags
=
sequence
.
flags
1941
local
markclass
=
sequence
.
markclass
1942
if
features
then
1943
local
tv
=
tables
[
features
]
1944
if
tv
then
1945
sequence
.
features
=
tv
1946
features
=
tv
1947
end
1948
for
script
,
feature
in
next
,
features
do
1949
local
tv
=
tables
[
feature
]
1950
if
tv
then
1951
features
[
script
]
=
tv
1952
end
1953
end
1954
end
1955
if
steps
then
1956
for
i
=
1
,
#
steps
do
1957
local
step
=
steps
[
i
]
1958
if
kind
=
=
"
gpos_pair
"
then
1959
local
c
=
step
.
coverage
1960
if
c
then
1961
if
step
.
format
=
=
"
pair
"
then
1962
for
g1
,
d1
in
next
,
c
do
1963
local
tv
=
tables
[
d1
]
1964
if
tv
then
1965
c
[
g1
]
=
tv
1966
d1
=
tv
1967
end
1968
for
g2
,
d2
in
next
,
d1
do
1969
local
tv
=
tables
[
d2
]
1970
if
tv
then
1971
d1
[
g2
]
=
tv
1972
d2
=
tv
1973
end
1974
local
f
=
tables
[
d2
[
1
]
]
if
f
then
d2
[
1
]
=
f
end
1975
local
s
=
tables
[
d2
[
2
]
]
if
s
then
d2
[
2
]
=
s
end
1976
end
1977
end
1978
else
1979
for
g1
,
d1
in
next
,
c
do
1980
local
tv
=
tables
[
d1
]
1981
if
tv
then
1982
c
[
g1
]
=
tv
1983
end
1984
end
1985
end
1986
end
1987
elseif
kind
=
=
"
gpos_single
"
then
1988
local
c
=
step
.
coverage
1989
if
c
then
1990
if
step
.
format
=
=
"
single
"
then
1991
for
g1
,
d1
in
next
,
c
do
1992
local
tv
=
tables
[
d1
]
1993
if
tv
then
1994
c
[
g1
]
=
tv
1995
end
1996
end
1997
else
1998
local
tv
=
tables
[
c
]
1999
if
tv
then
2000
step
.
coverage
=
tv
2001
end
2002
end
2003
end
2004
elseif
kind
=
=
"
gpos_cursive
"
then
2005
local
c
=
step
.
coverage
2006
if
c
then
2007
for
g1
,
d1
in
next
,
c
do
2008
local
tv
=
tables
[
d1
]
2009
if
tv
then
2010
d1
=
tv
2011
c
[
g1
]
=
d1
2012
end
2013
local
f
=
tables
[
d1
[
2
]
]
if
f
then
d1
[
2
]
=
f
end
2014
local
s
=
tables
[
d1
[
3
]
]
if
s
then
d1
[
3
]
=
s
end
2015
end
2016
end
2017
elseif
kind
=
=
"
gpos_mark2base
"
or
kind
=
=
"
gpos_mark2mark
"
then
2018
local
c
=
step
.
baseclasses
2019
if
c
then
2020
for
g1
,
d1
in
next
,
c
do
2021
for
g2
,
d2
in
next
,
d1
do
2022
local
tv
=
tables
[
d2
]
2023
if
tv
then
2024
d1
[
g2
]
=
tv
2025
end
2026
end
2027
end
2028
end
2029
local
c
=
step
.
coverage
2030
if
c
then
2031
for
g1
,
d1
in
next
,
c
do
2032
local
tv
=
tables
[
d1
[
2
]
]
2033
if
tv
then
2034
d1
[
2
]
=
tv
2035
end
2036
end
2037
end
2038
elseif
kind
=
=
"
gpos_mark2ligature
"
then
2039
local
c
=
step
.
baseclasses
2040
if
c
then
2041
for
g1
,
d1
in
next
,
c
do
2042
for
g2
,
d2
in
next
,
d1
do
2043
local
tv
=
tables
[
d2
]
-- new
2044
if
tv
then
2045
d2
=
tv
2046
d1
[
g2
]
=
d2
2047
end
2048
for
g3
,
d3
in
next
,
d2
do
2049
local
tv
=
tables
[
d2
[
g3
]
]
2050
if
tv
then
2051
d2
[
g3
]
=
tv
2052
end
2053
end
2054
end
2055
end
2056
end
2057
local
c
=
step
.
coverage
2058
if
c
then
2059
for
g1
,
d1
in
next
,
c
do
2060
local
tv
=
tables
[
d1
[
2
]
]
2061
if
tv
then
2062
d1
[
2
]
=
tv
2063
end
2064
end
2065
end
2066
end
2067
local
rules
=
step
.
rules
2068
if
rules
then
2069
for
i
=
1
,
#
rules
do
2070
local
rule
=
rules
[
i
]
2071
local
before
=
rule
.
before
2072
if
before
then
2073
local
tv
=
tables
[
before
]
2074
if
tv
then
2075
rule
.
before
=
tv
2076
before
=
tv
2077
end
2078
for
i
=
1
,
#
before
do
2079
local
tv
=
tables
[
before
[
i
]
]
2080
if
tv
then
2081
before
[
i
]
=
tv
2082
end
2083
end
2084
-- for i=1,#before do
2085
-- local bi = before[i]
2086
-- local tv = tables[bi]
2087
-- if tv then
2088
-- bi = tv
2089
-- before[i] = bi
2090
-- end
2091
-- local ranges = bi.ranges
2092
-- if ranges then
2093
-- expandranges(bi,ranges)
2094
-- end
2095
-- end
2096
end
2097
local
after
=
rule
.
after
2098
if
after
then
2099
local
tv
=
tables
[
after
]
2100
if
tv
then
2101
rule
.
after
=
tv
2102
after
=
tv
2103
end
2104
for
i
=
1
,
#
after
do
2105
local
tv
=
tables
[
after
[
i
]
]
2106
if
tv
then
2107
after
[
i
]
=
tv
2108
end
2109
end
2110
-- for i=1,#after do
2111
-- local ai = after[i]
2112
-- local tv = tables[ai]
2113
-- if tv then
2114
-- ai = tv
2115
-- after[i] = ai
2116
-- end
2117
-- local ranges = ai.ranges
2118
-- if ranges then
2119
-- expandranges(ai,ranges)
2120
-- end
2121
-- end
2122
end
2123
local
current
=
rule
.
current
2124
if
current
then
2125
local
tv
=
tables
[
current
]
2126
if
tv
then
2127
rule
.
current
=
tv
2128
current
=
tv
2129
end
2130
for
i
=
1
,
#
current
do
2131
local
tv
=
tables
[
current
[
i
]
]
2132
if
tv
then
2133
current
[
i
]
=
tv
2134
end
2135
end
2136
-- for i=1,#current do
2137
-- local ci = current[i]
2138
-- local tv = tables[ci]
2139
-- if tv then
2140
-- ci = tv
2141
-- current[i] = ci
2142
-- end
2143
-- local ranges = ci.ranges
2144
-- if ranges then
2145
-- expandranges(ci,ranges)
2146
-- end
2147
-- end
2148
end
2149
-- local lookups = rule.lookups
2150
-- if lookups then
2151
-- local tv = tables[lookups]
2152
-- if tv then
2153
-- rule.lookups = tv
2154
-- end
2155
-- end
2156
local
replacements
=
rule
.
replacements
2157
if
replacements
then
2158
local
tv
=
tables
[
replacements
]
2159
if
tv
then
2160
rule
.
replacements
=
tv
2161
end
2162
end
2163
end
2164
end
2165
end
2166
end
2167
if
order
then
2168
local
tv
=
tables
[
order
]
2169
if
tv
then
2170
sequence
.
order
=
tv
2171
end
2172
end
2173
if
flags
then
2174
local
tv
=
tables
[
flags
]
2175
if
tv
then
2176
sequence
.
flags
=
tv
2177
end
2178
end
2179
end
2180
end
2181 2182
if
sequences
then
2183
unpackthem
(
sequences
)
2184
end
2185 2186
if
sublookups
then
2187
unpackthem
(
sublookups
)
2188
end
2189 2190
if
features
then
2191
for
k
,
list
in
next
,
features
do
2192
for
feature
,
spec
in
next
,
list
do
2193
local
tv
=
tables
[
spec
]
2194
if
tv
then
2195
list
[
feature
]
=
tv
2196
end
2197
end
2198
end
2199
end
2200 2201
if
palettes
then
2202
for
i
=
1
,
#
palettes
do
2203
local
p
=
palettes
[
i
]
2204
for
j
=
1
,
#
p
do
2205
local
tv
=
tables
[
p
[
j
]
]
2206
if
tv
then
2207
p
[
j
]
=
tv
2208
end
2209
end
2210
end
2211
end
2212 2213
if
variable
then
2214 2215
-- todo: segments
2216 2217
local
instances
=
variable
.
instances
2218
if
instances
then
2219
for
i
=
1
,
#
instances
do
2220
local
v
=
instances
[
i
]
.
values
2221
for
j
=
1
,
#
v
do
2222
local
tv
=
tables
[
v
[
j
]
]
2223
if
tv
then
2224
v
[
j
]
=
tv
2225
end
2226
end
2227
end
2228
end
2229 2230
local
function
unpackdeltas
(
main
)
2231
if
main
then
2232
local
deltas
=
main
.
deltas
2233
if
deltas
then
2234
for
i
=
1
,
#
deltas
do
2235
local
di
=
deltas
[
i
]
2236
local
d
=
di
.
deltas
2237
local
r
=
di
.
regions
2238
for
j
=
1
,
#
d
do
2239
local
tv
=
tables
[
d
[
j
]
]
2240
if
tv
then
2241
d
[
j
]
=
tv
2242
end
2243
end
2244
local
tv
=
di
.
regions
2245
if
tv
then
2246
di
.
regions
=
tv
2247
end
2248
end
2249
end
2250
local
regions
=
main
.
regions
2251
if
regions
then
2252
local
tv
=
tables
[
regions
]
2253
if
tv
then
2254
main
.
regions
=
tv
2255
regions
=
tv
2256
end
2257
for
i
=
1
,
#
regions
do
2258
local
r
=
regions
[
i
]
2259
for
j
=
1
,
#
r
do
2260
local
tv
=
tables
[
r
[
j
]
]
2261
if
tv
then
2262
r
[
j
]
=
tv
2263
end
2264
end
2265
end
2266
end
2267
end
2268
end
2269 2270
unpackdeltas
(
variable
.
global
)
2271
unpackdeltas
(
variable
.
horizontal
)
2272
unpackdeltas
(
variable
.
vertical
)
2273
unpackdeltas
(
variable
.
metrics
)
2274 2275
end
2276 2277
data
.
tables
=
nil
2278
end
2279
end
2280
end
2281 2282
local
mt
=
{
2283
__index
=
function
(
t
,
k
)
-- maybe set it
2284
if
k
=
=
"
height
"
then
2285
local
ht
=
t
.
boundingbox
[
4
]
2286
return
ht
<
0
and
0
or
ht
2287
elseif
k
=
=
"
depth
"
then
2288
local
dp
=
-
t
.
boundingbox
[
2
]
2289
return
dp
<
0
and
0
or
dp
2290
elseif
k
=
=
"
width
"
then
2291
return
0
2292
elseif
k
=
=
"
name
"
then
-- or maybe uni*
2293
return
forcenotdef
and
"
.notdef
"
2294
end
2295
end
2296
}
2297 2298
local
function
sameformat
(
sequence
,
steps
,
first
,
nofsteps
,
kind
)
2299
return
true
2300
end
2301 2302
local
function
mergesteps_1
(
lookup
,
strict
)
2303
local
steps
=
lookup
.
steps
2304
local
nofsteps
=
lookup
.
nofsteps
2305
local
first
=
steps
[
1
]
2306
if
strict
then
2307
local
f
=
first
.
format
2308
for
i
=
2
,
nofsteps
do
2309
if
steps
[
i
]
.
format
~
=
f
then
2310
if
trace_optimizations
then
2311
report_optimizations
(
"
not merging %a steps of %a lookup %a, different formats
"
,
nofsteps
,
lookup
.
type
,
lookup
.
name
)
2312
end
2313
return
0
2314
end
2315
end
2316
end
2317
if
trace_optimizations
then
2318
report_optimizations
(
"
merging %a steps of %a lookup %a
"
,
nofsteps
,
lookup
.
type
,
lookup
.
name
)
2319
end
2320
local
target
=
first
.
coverage
2321
for
i
=
2
,
nofsteps
do
2322
local
c
=
steps
[
i
]
.
coverage
2323
if
c
then
2324
for
k
,
v
in
next
,
c
do
2325
if
not
target
[
k
]
then
2326
target
[
k
]
=
v
2327
end
2328
end
2329
end
2330
end
2331
lookup
.
nofsteps
=
1
2332
lookup
.
merged
=
true
2333
lookup
.
steps
=
{
first
}
2334
return
nofsteps
-
1
2335
end
2336 2337
local
function
mergesteps_2
(
lookup
)
-- pairs
2338
-- this can be tricky as we can have a match on a mark with no marks skip flag
2339
-- in which case with multiple steps a hit can prevent a next step while in the
2340
-- merged case we can hit differently (a messy font then anyway)
2341
local
steps
=
lookup
.
steps
2342
local
nofsteps
=
lookup
.
nofsteps
2343
local
first
=
steps
[
1
]
2344
if
strict
then
2345
local
f
=
first
.
format
2346
for
i
=
2
,
nofsteps
do
2347
if
steps
[
i
]
.
format
~
=
f
then
2348
if
trace_optimizations
then
2349
report_optimizations
(
"
not merging %a steps of %a lookup %a, different formats
"
,
nofsteps
,
lookup
.
type
,
lookup
.
name
)
2350
end
2351
return
0
2352
end
2353
end
2354
end
2355
if
trace_optimizations
then
2356
report_optimizations
(
"
merging %a steps of %a lookup %a
"
,
nofsteps
,
lookup
.
type
,
lookup
.
name
)
2357
end
2358
local
target
=
first
.
coverage
2359
for
i
=
2
,
nofsteps
do
2360
local
c
=
steps
[
i
]
.
coverage
2361
if
c
then
2362
for
k
,
v
in
next
,
c
do
2363
local
tk
=
target
[
k
]
2364
if
tk
then
2365
for
kk
,
vv
in
next
,
v
do
2366
if
tk
[
kk
]
=
=
nil
then
2367
tk
[
kk
]
=
vv
2368
end
2369
end
2370
else
2371
target
[
k
]
=
v
2372
end
2373
end
2374
end
2375
end
2376
lookup
.
nofsteps
=
1
2377
lookup
.
merged
=
true
2378
lookup
.
steps
=
{
first
}
2379
return
nofsteps
-
1
2380
end
2381 2382
-- we could have a coverage[first][second] = { } already here (because eventually
2383
-- we also have something like that after loading)
2384 2385
local
function
mergesteps_3
(
lookup
,
strict
)
-- marks
2386
local
steps
=
lookup
.
steps
2387
local
nofsteps
=
lookup
.
nofsteps
2388
if
trace_optimizations
then
2389
report_optimizations
(
"
merging %a steps of %a lookup %a
"
,
nofsteps
,
lookup
.
type
,
lookup
.
name
)
2390
end
2391
-- check first
2392
local
coverage
=
{
}
2393
for
i
=
1
,
nofsteps
do
2394
local
c
=
steps
[
i
]
.
coverage
2395
if
c
then
2396
for
k
,
v
in
next
,
c
do
2397
local
tk
=
coverage
[
k
]
-- { class, { x, y } }
2398
if
tk
then
2399
if
trace_optimizations
then
2400
report_optimizations
(
"
quitting merge due to multiple checks
"
)
2401
end
2402
return
nofsteps
2403
else
2404
coverage
[
k
]
=
v
2405
end
2406
end
2407
end
2408
end
2409
-- merge indeed
2410
local
first
=
steps
[
1
]
2411
local
baseclasses
=
{
}
-- let's assume sparse step.baseclasses
2412
for
i
=
1
,
nofsteps
do
2413
local
offset
=
i
*
10
-- we assume max 10 classes per step
2414
local
step
=
steps
[
i
]
2415
for
k
,
v
in
sortedhash
(
step
.
baseclasses
)
do
2416
baseclasses
[
offset
+
k
]
=
v
2417
end
2418
for
k
,
v
in
next
,
step
.
coverage
do
2419
v
[
1
]
=
offset
+
v
[
1
]
2420
end
2421
end
2422
first
.
baseclasses
=
baseclasses
2423
first
.
coverage
=
coverage
2424
lookup
.
nofsteps
=
1
2425
lookup
.
merged
=
true
2426
lookup
.
steps
=
{
first
}
2427
return
nofsteps
-
1
2428
end
2429 2430
local
function
nested
(
old
,
new
)
2431
for
k
,
v
in
next
,
old
do
2432
if
k
=
=
"
ligature
"
then
2433
if
not
new
.
ligature
then
2434
new
.
ligature
=
v
2435
end
2436
else
2437
local
n
=
new
[
k
]
2438
if
n
then
2439
nested
(
v
,
n
)
2440
else
2441
new
[
k
]
=
v
2442
end
2443
end
2444
end
2445
end
2446 2447
local
function
mergesteps_4
(
lookup
)
-- ligatures
2448
local
steps
=
lookup
.
steps
2449
local
nofsteps
=
lookup
.
nofsteps
2450
local
first
=
steps
[
1
]
2451
if
trace_optimizations
then
2452
report_optimizations
(
"
merging %a steps of %a lookup %a
"
,
nofsteps
,
lookup
.
type
,
lookup
.
name
)
2453
end
2454
local
target
=
first
.
coverage
2455
for
i
=
2
,
nofsteps
do
2456
local
c
=
steps
[
i
]
.
coverage
2457
if
c
then
2458
for
k
,
v
in
next
,
c
do
2459
local
tk
=
target
[
k
]
2460
if
tk
then
2461
nested
(
v
,
tk
)
2462
else
2463
target
[
k
]
=
v
2464
end
2465
end
2466
end
2467
end
2468
lookup
.
nofsteps
=
1
2469
lookup
.
steps
=
{
first
}
2470
return
nofsteps
-
1
2471
end
2472 2473
-- so we assume only one cursive entry and exit and even then the first one seems
2474
-- to win anyway: no exit or entry quite the lookup match and then we take the
2475
-- next step; this means that we can as well merge them
2476 2477
local
function
mergesteps_5
(
lookup
)
-- cursive
2478
local
steps
=
lookup
.
steps
2479
local
nofsteps
=
lookup
.
nofsteps
2480
local
first
=
steps
[
1
]
2481
if
trace_optimizations
then
2482
report_optimizations
(
"
merging %a steps of %a lookup %a
"
,
nofsteps
,
lookup
.
type
,
lookup
.
name
)
2483
end
2484
local
target
=
first
.
coverage
2485
local
hash
=
nil
2486
for
k
,
v
in
next
,
target
do
2487
hash
=
v
[
1
]
2488
break
2489
end
2490
for
i
=
2
,
nofsteps
do
2491
local
c
=
steps
[
i
]
.
coverage
2492
if
c
then
2493
for
k
,
v
in
next
,
c
do
2494
local
tk
=
target
[
k
]
2495
if
tk
then
2496
if
not
tk
[
2
]
then
2497
tk
[
2
]
=
v
[
2
]
2498
end
2499
if
not
tk
[
3
]
then
2500
tk
[
3
]
=
v
[
3
]
2501
end
2502
else
2503
target
[
k
]
=
v
2504
v
[
1
]
=
hash
2505
end
2506
end
2507
end
2508
end
2509
lookup
.
nofsteps
=
1
2510
lookup
.
merged
=
true
2511
lookup
.
steps
=
{
first
}
2512
return
nofsteps
-
1
2513
end
2514 2515
local
function
checkkerns
(
lookup
)
2516
local
steps
=
lookup
.
steps
2517
local
nofsteps
=
lookup
.
nofsteps
2518
local
kerned
=
0
2519
for
i
=
1
,
nofsteps
do
2520
local
step
=
steps
[
i
]
2521
if
step
.
format
=
=
"
pair
"
then
2522
local
coverage
=
step
.
coverage
2523
local
kerns
=
true
2524
for
g1
,
d1
in
next
,
coverage
do
2525
if
d1
=
=
true
then
2526
-- all zero
2527
elseif
not
d1
then
2528
-- null
2529
elseif
d1
[
1
]
~
=
0
or
d1
[
2
]
~
=
0
or
d1
[
4
]
~
=
0
then
2530
kerns
=
false
2531
break
2532
end
2533
end
2534
if
kerns
then
2535
if
trace_optimizations
then
2536
report_optimizations
(
"
turning pairs of step %a of %a lookup %a into kerns
"
,
i
,
lookup
.
type
,
lookup
.
name
)
2537
end
2538
local
c
=
{
}
2539
for
g1
,
d1
in
next
,
coverage
do
2540
if
d1
and
d1
~
=
true
then
2541
c
[
g1
]
=
d1
[
3
]
2542
end
2543
end
2544
step
.
coverage
=
c
2545
step
.
format
=
"
move
"
2546
kerned
=
kerned
+
1
2547
end
2548
end
2549
end
2550
return
kerned
2551
end
2552 2553
-- There are several options to optimize but we have this somewhat fuzzy aspect of
2554
-- advancing (depending on the second of a pair) so we need to retain that information.
2555
--
2556
-- We can have:
2557
--
2558
-- true, nil|false
2559
--
2560
-- which effectively means: nothing to be done and advance to next (so not next of
2561
-- next) and because coverage should be not overlapping we can wipe these. However,
2562
-- checking for (true,nil) (false,nil) and omitting them doesn't gain much.
2563 2564
-- Because we pack we cannot mix tables and numbers so we can only turn a whole set in
2565
-- format kern instead of pair.
2566 2567
local
function
checkpairs
(
lookup
)
2568
local
steps
=
lookup
.
steps
2569
local
nofsteps
=
lookup
.
nofsteps
2570
local
kerned
=
0
2571 2572
local
function
onlykerns
(
step
)
2573
local
coverage
=
step
.
coverage
2574
for
g1
,
d1
in
next
,
coverage
do
2575
for
g2
,
d2
in
next
,
d1
do
2576
if
d2
[
2
]
then
2577
--- true or { a, b, c, d }
2578
return
false
2579
else
2580
local
v
=
d2
[
1
]
2581
if
v
=
=
true
then
2582
-- all zero
2583
elseif
v
and
(
v
[
1
]
~
=
0
or
v
[
2
]
~
=
0
or
v
[
4
]
~
=
0
)
then
2584
return
false
2585
end
2586
end
2587
end
2588
end
2589
return
coverage
2590
end
2591 2592
for
i
=
1
,
nofsteps
do
2593
local
step
=
steps
[
i
]
2594
if
step
.
format
=
=
"
pair
"
then
2595
local
coverage
=
onlykerns
(
step
)
2596
if
coverage
then
2597
if
trace_optimizations
then
2598
report_optimizations
(
"
turning pairs of step %a of %a lookup %a into kerns
"
,
i
,
lookup
.
type
,
lookup
.
name
)
2599
end
2600
for
g1
,
d1
in
next
,
coverage
do
2601
local
d
=
{
}
2602
for
g2
,
d2
in
next
,
d1
do
2603
local
v
=
d2
[
1
]
2604
if
v
=
=
true
then
2605
-- ignore -- d1[g2] = nil
2606
elseif
v
then
2607
d
[
g2
]
=
v
[
3
]
-- d1[g2] = v[3]
2608
end
2609
end
2610
coverage
[
g1
]
=
d
2611
end
2612
step
.
format
=
"
move
"
2613
kerned
=
kerned
+
1
2614
end
2615
end
2616
end
2617
return
kerned
2618
end
2619 2620
local
compact_pairs
=
true
2621
local
compact_singles
=
true
2622 2623
local
merge_pairs
=
true
2624
local
merge_singles
=
true
2625
local
merge_substitutions
=
true
2626
local
merge_alternates
=
true
2627
local
merge_multiples
=
true
2628
local
merge_ligatures
=
true
2629
local
merge_cursives
=
true
2630
local
merge_marks
=
true
2631 2632
directives
.
register
(
"
otf.compact.pairs
"
,
function
(
v
)
compact_pairs
=
v
end
)
2633
directives
.
register
(
"
otf.compact.singles
"
,
function
(
v
)
compact_singles
=
v
end
)
2634 2635
directives
.
register
(
"
otf.merge.pairs
"
,
function
(
v
)
merge_pairs
=
v
end
)
2636
directives
.
register
(
"
otf.merge.singles
"
,
function
(
v
)
merge_singles
=
v
end
)
2637
directives
.
register
(
"
otf.merge.substitutions
"
,
function
(
v
)
merge_substitutions
=
v
end
)
2638
directives
.
register
(
"
otf.merge.alternates
"
,
function
(
v
)
merge_alternates
=
v
end
)
2639
directives
.
register
(
"
otf.merge.multiples
"
,
function
(
v
)
merge_multiples
=
v
end
)
2640
directives
.
register
(
"
otf.merge.ligatures
"
,
function
(
v
)
merge_ligatures
=
v
end
)
2641
directives
.
register
(
"
otf.merge.cursives
"
,
function
(
v
)
merge_cursives
=
v
end
)
2642
directives
.
register
(
"
otf.merge.marks
"
,
function
(
v
)
merge_marks
=
v
end
)
2643 2644
function
readers
.
compact
(
data
)
2645
if
not
data
or
data
.
compacted
then
2646
return
2647
else
2648
data
.
compacted
=
true
2649
end
2650
local
resources
=
data
.
resources
2651
local
merged
=
0
2652
local
kerned
=
0
2653
local
allsteps
=
0
2654
local
function
compact
(
what
)
2655
local
lookups
=
resources
[
what
]
2656
if
lookups
then
2657
for
i
=
1
,
#
lookups
do
2658
local
lookup
=
lookups
[
i
]
2659
local
nofsteps
=
lookup
.
nofsteps
2660
local
kind
=
lookup
.
type
2661
allsteps
=
allsteps
+
nofsteps
2662
if
nofsteps
>
1
then
2663
local
merg
=
merged
2664
if
kind
=
=
"
gsub_single
"
then
2665
if
merge_substitutions
then
2666
merged
=
merged
+
mergesteps_1
(
lookup
)
2667
end
2668
elseif
kind
=
=
"
gsub_alternate
"
then
2669
if
merge_alternates
then
2670
merged
=
merged
+
mergesteps_1
(
lookup
)
2671
end
2672
elseif
kind
=
=
"
gsub_multiple
"
then
2673
if
merge_multiples
then
2674
merged
=
merged
+
mergesteps_1
(
lookup
)
2675
end
2676
elseif
kind
=
=
"
gsub_ligature
"
then
2677
if
merge_ligatures
then
2678
merged
=
merged
+
mergesteps_4
(
lookup
)
2679
end
2680
elseif
kind
=
=
"
gpos_single
"
then
2681
if
merge_singles
then
2682
merged
=
merged
+
mergesteps_1
(
lookup
,
true
)
2683
end
2684
if
compact_singles
then
2685
kerned
=
kerned
+
checkkerns
(
lookup
)
2686
end
2687
elseif
kind
=
=
"
gpos_pair
"
then
2688
if
merge_pairs
then
2689
merged
=
merged
+
mergesteps_2
(
lookup
)
2690
end
2691
if
compact_pairs
then
2692
kerned
=
kerned
+
checkpairs
(
lookup
)
2693
end
2694
elseif
kind
=
=
"
gpos_cursive
"
then
2695
if
merge_cursives
then
2696
merged
=
merged
+
mergesteps_5
(
lookup
)
2697
end
2698
elseif
kind
=
=
"
gpos_mark2mark
"
or
kind
=
=
"
gpos_mark2base
"
or
kind
=
=
"
gpos_mark2ligature
"
then
2699
if
merge_marks
then
2700
merged
=
merged
+
mergesteps_3
(
lookup
)
2701
end
2702
end
2703
if
merg
~
=
merged
then
2704
lookup
.
merged
=
true
2705
end
2706
elseif
nofsteps
=
=
1
then
2707
local
kern
=
kerned
2708
if
kind
=
=
"
gpos_single
"
then
2709
if
compact_singles
then
2710
kerned
=
kerned
+
checkkerns
(
lookup
)
2711
end
2712
elseif
kind
=
=
"
gpos_pair
"
then
2713
if
compact_pairs
then
2714
kerned
=
kerned
+
checkpairs
(
lookup
)
2715
end
2716
end
2717
if
kern
~
=
kerned
then
2718
-- lookup.kerned = true
2719
end
2720
end
2721
end
2722
elseif
trace_optimizations
then
2723
report_optimizations
(
"
no lookups in %a
"
,
what
)
2724
end
2725
end
2726
compact
(
"
sequences
"
)
2727
compact
(
"
sublookups
"
)
2728
if
trace_optimizations
then
2729
if
merged
>
0
then
2730
report_optimizations
(
"
%i steps of %i removed due to merging
"
,
merged
,
allsteps
)
2731
end
2732
if
kerned
>
0
then
2733
report_optimizations
(
"
%i steps of %i steps turned from pairs into kerns
"
,
kerned
,
allsteps
)
2734
end
2735
end
2736
end
2737 2738
local
function
mergesteps
(
t
,
k
)
2739
if
k
=
=
"
merged
"
then
2740
local
merged
=
{
}
2741
for
i
=
1
,
#
t
do
2742
local
step
=
t
[
i
]
2743
local
coverage
=
step
.
coverage
2744
for
k
in
next
,
coverage
do
2745
local
m
=
merged
[
k
]
2746
if
m
then
2747
m
[
2
]
=
i
2748
-- m[#m+1] = step
2749
else
2750
merged
[
k
]
=
{
i
,
i
}
2751
-- merged[k] = { step }
2752
end
2753
end
2754
end
2755
t
.
merged
=
merged
2756
return
merged
2757
end
2758
end
2759 2760
local
function
checkmerge
(
sequence
)
2761
local
steps
=
sequence
.
steps
2762
if
steps
then
2763
setmetatableindex
(
steps
,
mergesteps
)
2764
end
2765
end
2766 2767
local
function
checkflags
(
sequence
,
resources
)
2768
if
not
sequence
.
skiphash
then
2769
local
flags
=
sequence
.
flags
2770
if
flags
then
2771
local
skipmark
=
flags
[
1
]
2772
local
skipligature
=
flags
[
2
]
2773
local
skipbase
=
flags
[
3
]
2774
local
markclass
=
sequence
.
markclass
2775
local
skipsome
=
skipmark
or
skipligature
or
skipbase
or
markclass
or
false
2776
if
skipsome
then
2777
sequence
.
skiphash
=
setmetatableindex
(
function
(
t
,
k
)
2778
local
c
=
resources
.
classes
[
k
]
-- delayed table
2779
local
v
=
c
=
=
skipmark
2780
or
(
markclass
and
c
=
=
"
mark
"
and
not
markclass
[
k
]
)
2781
or
c
=
=
skipligature
2782
or
c
=
=
skipbase
2783
or
false
2784
t
[
k
]
=
v
2785
return
v
2786
end
)
2787
else
2788
sequence
.
skiphash
=
false
2789
end
2790
else
2791
sequence
.
skiphash
=
false
2792
end
2793
end
2794
end
2795 2796
local
function
checksteps
(
sequence
)
2797
local
steps
=
sequence
.
steps
2798
if
steps
then
2799
for
i
=
1
,
#
steps
do
2800
steps
[
i
]
.
index
=
i
2801
end
2802
end
2803
end
2804 2805
if
fonts
.
helpers
then
2806
fonts
.
helpers
.
checkmerge
=
checkmerge
2807
fonts
.
helpers
.
checkflags
=
checkflags
2808
fonts
.
helpers
.
checksteps
=
checksteps
-- has to happen last
2809
end
2810 2811
function
readers
.
expand
(
data
)
2812
if
not
data
or
data
.
expanded
then
2813
return
2814
else
2815
data
.
expanded
=
true
2816
end
2817
local
resources
=
data
.
resources
2818
local
sublookups
=
resources
.
sublookups
2819
local
sequences
=
resources
.
sequences
-- were one level up
2820
local
markclasses
=
resources
.
markclasses
2821
local
descriptions
=
data
.
descriptions
2822
if
descriptions
then
2823
local
defaultwidth
=
resources
.
defaultwidth
or
0
2824
local
defaultheight
=
resources
.
defaultheight
or
0
2825
local
defaultdepth
=
resources
.
defaultdepth
or
0
2826
local
basename
=
trace_markwidth
and
file
.
basename
(
resources
.
filename
)
2827
for
u
,
d
in
next
,
descriptions
do
2828
local
bb
=
d
.
boundingbox
2829
local
wd
=
d
.
width
2830
if
not
wd
then
2831
-- or bb?
2832
d
.
width
=
defaultwidth
2833
elseif
trace_markwidth
and
wd
~
=
0
and
d
.
class
=
=
"
mark
"
then
2834
report_markwidth
(
"
mark %a with width %b found in %a
"
,
d
.
name
or
"
<noname>
"
,
wd
,
basename
)
2835
end
2836
if
bb
then
2837
local
ht
=
bb
[
4
]
2838
local
dp
=
-
bb
[
2
]
2839
if
ht
=
=
0
or
ht
<
0
then
2840
-- not set
2841
else
2842
d
.
height
=
ht
2843
end
2844
if
dp
=
=
0
or
dp
<
0
then
2845
-- not set
2846
else
2847
d
.
depth
=
dp
2848
end
2849
end
2850
end
2851
end
2852 2853
-- using a merged combined hash as first test saves some 30% on ebgaramond and
2854
-- about 15% on arabtype .. then moving the a test also saves a bit (even when
2855
-- often a is not set at all so that one is a bit debatable
2856 2857
local
function
expandlookups
(
sequences
)
2858
if
sequences
then
2859
-- we also need to do sublookups
2860
for
i
=
1
,
#
sequences
do
2861
local
sequence
=
sequences
[
i
]
2862
local
steps
=
sequence
.
steps
2863
if
steps
then
2864
local
nofsteps
=
sequence
.
nofsteps
2865 2866
local
kind
=
sequence
.
type
2867
local
markclass
=
sequence
.
markclass
2868
if
markclass
then
2869
if
not
markclasses
then
2870
report_warning
(
"
missing markclasses
"
)
2871
sequence
.
markclass
=
false
2872
else
2873
sequence
.
markclass
=
markclasses
[
markclass
]
2874
end
2875
end
2876 2877
for
i
=
1
,
nofsteps
do
2878
local
step
=
steps
[
i
]
2879
local
baseclasses
=
step
.
baseclasses
2880
if
baseclasses
then
2881
local
coverage
=
step
.
coverage
2882
for
k
,
v
in
next
,
coverage
do
2883
v
[
1
]
=
baseclasses
[
v
[
1
]
]
-- slot 1 is a placeholder
2884
end
2885
elseif
kind
=
=
"
gpos_cursive
"
then
2886
local
coverage
=
step
.
coverage
2887
for
k
,
v
in
next
,
coverage
do
2888
v
[
1
]
=
coverage
-- slot 1 is a placeholder
2889
end
2890
end
2891
local
rules
=
step
.
rules
2892
if
rules
then
2893
local
rulehash
=
{
n
=
0
}
-- is contexts in font-ots
2894
local
rulesize
=
0
2895
local
coverage
=
{
}
2896
local
lookuptype
=
sequence
.
type
2897
local
nofrules
=
#
rules
2898
step
.
coverage
=
coverage
-- combined hits
2899
for
currentrule
=
1
,
nofrules
do
2900
local
rule
=
rules
[
currentrule
]
2901
local
current
=
rule
.
current
2902
local
before
=
rule
.
before
2903
local
after
=
rule
.
after
2904
local
replacements
=
rule
.
replacements
or
false
2905
local
sequence
=
{
}
2906
local
nofsequences
=
0
2907
if
before
then
2908
for
n
=
1
,
#
before
do
2909
nofsequences
=
nofsequences
+
1
2910
sequence
[
nofsequences
]
=
before
[
n
]
2911
end
2912
end
2913
local
start
=
nofsequences
+
1
2914
for
n
=
1
,
#
current
do
2915
nofsequences
=
nofsequences
+
1
2916
sequence
[
nofsequences
]
=
current
[
n
]
2917
end
2918
local
stop
=
nofsequences
2919
if
after
then
2920
for
n
=
1
,
#
after
do
2921
nofsequences
=
nofsequences
+
1
2922
sequence
[
nofsequences
]
=
after
[
n
]
2923
end
2924
end
2925
local
lookups
=
rule
.
lookups
or
false
2926
local
subtype
=
nil
2927
if
lookups
then
2928
for
i
=
1
,
#
lookups
do
2929
local
lookups
=
lookups
[
i
]
2930
if
lookups
then
2931
for
k
,
v
in
next
,
lookups
do
-- actually this one is indexed
2932
local
lookup
=
sublookups
[
v
]
2933
if
lookup
then
2934
lookups
[
k
]
=
lookup
2935
if
not
subtype
then
2936
subtype
=
lookup
.
type
2937
end
2938
else
2939
-- already expanded
2940
end
2941
end
2942
end
2943
end
2944
end
2945
if
sequence
[
1
]
then
-- we merge coverage into one
2946
sequence
.
n
=
#
sequence
-- tiny speedup
2947
local
ruledata
=
{
2948
currentrule
,
-- 1 -- original rule number, only use this for tracing!
2949
lookuptype
,
-- 2
2950
sequence
,
-- 3
2951
start
,
-- 4
2952
stop
,
-- 5
2953
lookups
,
-- 6 (6/7 also signal of what to do)
2954
replacements
,
-- 7
2955
subtype
,
-- 8
2956
}
2957
--
2958
-- possible optimization: per [unic] a rulehash, but beware:
2959
-- contexts have unique coverage and chains can have multiple
2960
-- hits (rules) per coverage entry
2961
--
2962
-- so: we can combine multiple steps as well as multiple rules
2963
-- but that takes careful checking, in which case we can go the
2964
-- step list approach and turn contexts into steps .. in fact,
2965
-- if we turn multiple contexts into steps we're already ok as
2966
-- steps gets a coverage hash by metatable
2967
--
2968
rulesize
=
rulesize
+
1
2969
rulehash
[
rulesize
]
=
ruledata
2970
rulehash
.
n
=
rulesize
-- tiny speedup
2971
--
2972
if
true
then
-- nofrules > 1
2973 2974
for
unic
in
next
,
sequence
[
start
]
do
2975
local
cu
=
coverage
[
unic
]
2976
if
cu
then
2977
local
n
=
#
cu
+
1
2978
cu
[
n
]
=
ruledata
2979
cu
.
n
=
n
2980
else
2981
coverage
[
unic
]
=
{
ruledata
,
n
=
1
}
2982
end
2983
end
2984 2985
else
2986 2987
for
unic
in
next
,
sequence
[
start
]
do
2988
local
cu
=
coverage
[
unic
]
2989
if
cu
then
2990
-- we can have a contextchains with many matches which we
2991
-- can actually optimize
2992
else
2993
coverage
[
unic
]
=
rulehash
2994
end
2995
end
2996 2997
end
2998
end
2999
end
3000
end
3001
end
3002 3003
checkmerge
(
sequence
)
3004
checkflags
(
sequence
,
resources
)
3005
checksteps
(
sequence
)
3006 3007
end
3008
end
3009
end
3010
end
3011 3012
expandlookups
(
sequences
)
3013
expandlookups
(
sublookups
)
3014
end
3015