font-dsp.lua /size: 150 Kb    last modification: 2020-07-01 14:35
1
if
not
modules
then
modules
=
{
}
end
modules
[
'
font-dsp
'
]
=
{
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
-- many 0,0 entry/exit
10 11
-- This loader went through a few iterations. First I made a ff compatible one so
12
-- that we could do some basic checking. Also some verbosity was added (named
13
-- glyphs). Eventually all that was dropped for a context friendly format, simply
14
-- because keeping the different table models in sync too to much time. I have the
15
-- old file somewhere. A positive side effect is that we get an (upto) much smaller
16
-- smaller tma/tmc file. In the end the loader will be not much slower than the
17
-- c based ff one.
18 19
-- Being binary encoded, an opentype is rather compact. When expanded into a Lua table
20
-- quite some memory can be used. This is very noticeable in the ff loader, which for
21
-- a good reason uses a verbose format. However, when we use that data we create a couple
22
-- of hashes. In the Lua loader we create these hashes directly, which save quite some
23
-- memory.
24
--
25
-- We convert a font file only once and then cache it. Before creating the cached instance
26
-- packing takes place: common tables get shared. After (re)loading and unpacking we then
27
-- get a rather efficient internal representation of the font. In the new loader there is a
28
-- pitfall. Because we use some common coverage magic we put a bit more information in
29
-- the mark and cursive coverage tables than strickly needed: a reference to the coverage
30
-- itself. This permits a fast lookup of the second glyph involved. In the marks we
31
-- expand the class indicator to a class hash, in the cursive we use a placeholder that gets
32
-- a self reference. This means that we cannot pack these subtables unless we add a unique
33
-- id per entry (the same one per coverage) and that makes the tables larger. Because only a
34
-- few fonts benefit from this, I decided to not do this. Experiments demonstrated that it
35
-- only gives a few percent gain (on for instance husayni we can go from 845K to 828K
36
-- bytecode). Better stay conceptually clean than messy compact.
37 38
-- When we can reduce all basic lookups to one step we might safe a bit in the processing
39
-- so then only chains are multiple.
40 41
-- I used to flatten kerns here but that has been moved elsewhere because it polutes the code
42
-- here and can be done fast afterwards. One can even wonder if it makes sense to do it as we
43
-- pack anyway. In a similar fashion the unique placeholders in anchors in marks have been
44
-- removed because packing doesn't save much there anyway.
45 46
-- Although we have a bit more efficient tables in the cached files, the internals are still
47
-- pretty similar. And although we have a slightly more direct coverage access the processing
48
-- of node lists is not noticeable faster for latin texts, but for arabic we gain some 10%
49
-- (and could probably gain a bit more).
50 51
-- All this packing in the otf format is somewhat obsessive as nowadays 4K resolution
52
-- multi-gig videos pass through our networks and storage and memory is abundant.
53 54
-- Although we use a few table readers there i sno real gain in there (apart from having
55
-- less code. After all there are often not that many demanding features.
56 57
local
next
,
type
,
tonumber
=
next
,
type
,
tonumber
58
local
band
=
bit32
.
band
59
local
extract
=
bit32
.
extract
60
local
bor
=
bit32
.
bor
61
local
lshift
=
bit32
.
lshift
62
local
rshift
=
bit32
.
rshift
63
local
gsub
=
string
.
gsub
64
local
lower
=
string
.
lower
65
local
sub
=
string
.
sub
66
local
strip
=
string
.
strip
67
local
tohash
=
table
.
tohash
68
local
concat
=
table
.
concat
69
local
copy
=
table
.
copy
70
local
reversed
=
table
.
reversed
71
local
sort
=
table
.
sort
72
local
insert
=
table
.
insert
73
local
round
=
math
.
round
74 75
local
settings_to_hash
=
utilities
.
parsers
.
settings_to_hash_colon_too
76
local
setmetatableindex
=
table
.
setmetatableindex
77
local
formatters
=
string
.
formatters
78
local
sortedkeys
=
table
.
sortedkeys
79
local
sortedhash
=
table
.
sortedhash
80
local
sequenced
=
table
.
sequenced
81 82
local
report
=
logs
.
reporter
(
"
otf reader
"
)
83 84
local
readers
=
fonts
.
handlers
.
otf
.
readers
85
local
streamreader
=
readers
.
streamreader
86 87
local
setposition
=
streamreader
.
setposition
88
local
getposition
=
streamreader
.
getposition
89
local
readuinteger
=
streamreader
.
readcardinal1
90
local
readushort
=
streamreader
.
readcardinal2
91
local
readulong
=
streamreader
.
readcardinal4
92
local
readinteger
=
streamreader
.
readinteger1
93
local
readshort
=
streamreader
.
readinteger2
94
local
readstring
=
streamreader
.
readstring
95
local
readtag
=
streamreader
.
readtag
96
local
readbytes
=
streamreader
.
readbytes
97
local
readfixed
=
streamreader
.
readfixed4
98
local
read2dot14
=
streamreader
.
read2dot14
99
local
skipshort
=
streamreader
.
skipshort
100
local
skipbytes
=
streamreader
.
skip
101
local
readbytetable
=
streamreader
.
readbytetable
102
local
readbyte
=
streamreader
.
readbyte
103
local
readcardinaltable
=
streamreader
.
readcardinaltable
104
local
readintegertable
=
streamreader
.
readintegertable
105
local
readfword
=
readshort
106 107
local
short
=
2
108
local
ushort
=
2
109
local
ulong
=
4
110 111
directives
.
register
(
"
fonts.streamreader
"
,
function
(
)
112 113
streamreader
=
utilities
.
streams
114 115
setposition
=
streamreader
.
setposition
116
getposition
=
streamreader
.
getposition
117
readuinteger
=
streamreader
.
readcardinal1
118
readushort
=
streamreader
.
readcardinal2
119
readulong
=
streamreader
.
readcardinal4
120
readinteger
=
streamreader
.
readinteger1
121
readshort
=
streamreader
.
readinteger2
122
readstring
=
streamreader
.
readstring
123
readtag
=
streamreader
.
readtag
124
readbytes
=
streamreader
.
readbytes
125
readfixed
=
streamreader
.
readfixed4
126
read2dot14
=
streamreader
.
read2dot14
127
skipshort
=
streamreader
.
skipshort
128
skipbytes
=
streamreader
.
skip
129
readbytetable
=
streamreader
.
readbytetable
130
readbyte
=
streamreader
.
readbyte
131
readcardinaltable
=
streamreader
.
readcardinaltable
132
readintegertable
=
streamreader
.
readintegertable
133
readfword
=
readshort
134 135
end
)
136 137
local
gsubhandlers
=
{
}
138
local
gposhandlers
=
{
}
139 140
readers
.
gsubhandlers
=
gsubhandlers
141
readers
.
gposhandlers
=
gposhandlers
142 143
local
helpers
=
readers
.
helpers
144
local
gotodatatable
=
helpers
.
gotodatatable
145
local
setvariabledata
=
helpers
.
setvariabledata
146 147
local
lookupidoffset
=
-1
-- will become 1 when we migrate (only -1 for comparign with old)
148 149
local
classes
=
{
150
"
base
"
,
151
"
ligature
"
,
152
"
mark
"
,
153
"
component
"
,
154
}
155 156
local
gsubtypes
=
{
157
"
single
"
,
158
"
multiple
"
,
159
"
alternate
"
,
160
"
ligature
"
,
161
"
context
"
,
162
"
chainedcontext
"
,
163
"
extension
"
,
164
"
reversechainedcontextsingle
"
,
165
}
166 167
local
gpostypes
=
{
168
"
single
"
,
169
"
pair
"
,
170
"
cursive
"
,
171
"
marktobase
"
,
172
"
marktoligature
"
,
173
"
marktomark
"
,
174
"
context
"
,
175
"
chainedcontext
"
,
176
"
extension
"
,
177
}
178 179
local
chaindirections
=
{
180
context
=
0
,
181
chainedcontext
=
1
,
182
reversechainedcontextsingle
=
-1
,
183
}
184 185
local
function
setmetrics
(
data
,
where
,
tag
,
d
)
186
local
w
=
data
[
where
]
187
if
w
then
188
local
v
=
w
[
tag
]
189
if
v
then
190
-- it looks like some fonts set the value and not the delta
191
-- report("adding %s to %s.%s value %s",d,where,tag,v)
192
w
[
tag
]
=
v
+
d
193
end
194
end
195
end
196 197
local
variabletags
=
{
198
hasc
=
function
(
data
,
d
)
setmetrics
(
data
,
"
windowsmetrics
"
,
"
typoascender
"
,
d
)
end
,
199
hdsc
=
function
(
data
,
d
)
setmetrics
(
data
,
"
windowsmetrics
"
,
"
typodescender
"
,
d
)
end
,
200
hlgp
=
function
(
data
,
d
)
setmetrics
(
data
,
"
windowsmetrics
"
,
"
typolinegap
"
,
d
)
end
,
201
hcla
=
function
(
data
,
d
)
setmetrics
(
data
,
"
windowsmetrics
"
,
"
winascent
"
,
d
)
end
,
202
hcld
=
function
(
data
,
d
)
setmetrics
(
data
,
"
windowsmetrics
"
,
"
windescent
"
,
d
)
end
,
203
vasc
=
function
(
data
,
d
)
setmetrics
(
data
,
"
vhea not done
"
,
"
ascent
"
,
d
)
end
,
204
vdsc
=
function
(
data
,
d
)
setmetrics
(
data
,
"
vhea not done
"
,
"
descent
"
,
d
)
end
,
205
vlgp
=
function
(
data
,
d
)
setmetrics
(
data
,
"
vhea not done
"
,
"
linegap
"
,
d
)
end
,
206
xhgt
=
function
(
data
,
d
)
setmetrics
(
data
,
"
windowsmetrics
"
,
"
xheight
"
,
d
)
end
,
207
cpht
=
function
(
data
,
d
)
setmetrics
(
data
,
"
windowsmetrics
"
,
"
capheight
"
,
d
)
end
,
208
sbxs
=
function
(
data
,
d
)
setmetrics
(
data
,
"
windowsmetrics
"
,
"
subscriptxsize
"
,
d
)
end
,
209
sbys
=
function
(
data
,
d
)
setmetrics
(
data
,
"
windowsmetrics
"
,
"
subscriptysize
"
,
d
)
end
,
210
sbxo
=
function
(
data
,
d
)
setmetrics
(
data
,
"
windowsmetrics
"
,
"
subscriptxoffset
"
,
d
)
end
,
211
sbyo
=
function
(
data
,
d
)
setmetrics
(
data
,
"
windowsmetrics
"
,
"
subscriptyoffset
"
,
d
)
end
,
212
spxs
=
function
(
data
,
d
)
setmetrics
(
data
,
"
windowsmetrics
"
,
"
superscriptxsize
"
,
d
)
end
,
213
spys
=
function
(
data
,
d
)
setmetrics
(
data
,
"
windowsmetrics
"
,
"
superscriptysize
"
,
d
)
end
,
214
spxo
=
function
(
data
,
d
)
setmetrics
(
data
,
"
windowsmetrics
"
,
"
superscriptxoffset
"
,
d
)
end
,
215
spyo
=
function
(
data
,
d
)
setmetrics
(
data
,
"
windowsmetrics
"
,
"
superscriptyoffset
"
,
d
)
end
,
216
strs
=
function
(
data
,
d
)
setmetrics
(
data
,
"
windowsmetrics
"
,
"
strikeoutsize
"
,
d
)
end
,
217
stro
=
function
(
data
,
d
)
setmetrics
(
data
,
"
windowsmetrics
"
,
"
strikeoutpos
"
,
d
)
end
,
218
unds
=
function
(
data
,
d
)
setmetrics
(
data
,
"
postscript
"
,
"
underlineposition
"
,
d
)
end
,
219
undo
=
function
(
data
,
d
)
setmetrics
(
data
,
"
postscript
"
,
"
underlinethickness
"
,
d
)
end
,
220
}
221 222
local
read_cardinal
=
{
223
streamreader
.
readcardinal1
,
224
streamreader
.
readcardinal2
,
225
streamreader
.
readcardinal3
,
226
streamreader
.
readcardinal4
,
227
}
228 229
local
read_integer
=
{
230
streamreader
.
readinteger1
,
231
streamreader
.
readinteger2
,
232
streamreader
.
readinteger3
,
233
streamreader
.
readinteger4
,
234
}
235 236
-- Traditionally we use these unique names (so that we can flatten the lookup list
237
-- (we create subsets runtime) but I will adapt the old code to newer names.
238 239
-- chainsub
240
-- reversesub
241 242
local
lookupnames
=
{
243
gsub
=
{
244
single
=
"
gsub_single
"
,
245
multiple
=
"
gsub_multiple
"
,
246
alternate
=
"
gsub_alternate
"
,
247
ligature
=
"
gsub_ligature
"
,
248
context
=
"
gsub_context
"
,
249
chainedcontext
=
"
gsub_contextchain
"
,
250
reversechainedcontextsingle
=
"
gsub_reversecontextchain
"
,
-- reversesub
251
}
,
252
gpos
=
{
253
single
=
"
gpos_single
"
,
254
pair
=
"
gpos_pair
"
,
255
cursive
=
"
gpos_cursive
"
,
256
marktobase
=
"
gpos_mark2base
"
,
257
marktoligature
=
"
gpos_mark2ligature
"
,
258
marktomark
=
"
gpos_mark2mark
"
,
259
context
=
"
gpos_context
"
,
260
chainedcontext
=
"
gpos_contextchain
"
,
261
}
262
}
263 264
-- keep this as reference:
265
--
266
-- local lookupbits = {
267
-- [0x0001] = "righttoleft",
268
-- [0x0002] = "ignorebaseglyphs",
269
-- [0x0004] = "ignoreligatures",
270
-- [0x0008] = "ignoremarks",
271
-- [0x0010] = "usemarkfilteringset",
272
-- [0x00E0] = "reserved",
273
-- [0xFF00] = "markattachmenttype",
274
-- }
275
--
276
-- local lookupstate = setmetatableindex(function(t,k)
277
-- local v = { }
278
-- for kk, vv in next, lookupbits do
279
-- if band(k,kk) ~= 0 then
280
-- v[vv] = true
281
-- end
282
-- end
283
-- t[k] = v
284
-- return v
285
-- end)
286 287
local
lookupflags
=
setmetatableindex
(
function
(
t
,
k
)
288
local
v
=
{
289
band
(
k
,
0x0008
)
~
=
0
and
true
or
false
,
-- ignoremarks
290
band
(
k
,
0x0004
)
~
=
0
and
true
or
false
,
-- ignoreligatures
291
band
(
k
,
0x0002
)
~
=
0
and
true
or
false
,
-- ignorebaseglyphs
292
band
(
k
,
0x0001
)
~
=
0
and
true
or
false
,
-- r2l
293
}
294
t
[
k
]
=
v
295
return
v
296
end
)
297 298
-- Variation stores: it's not entirely clear if the regions are a shared
299
-- resource (it looks like they are). Anyway, we play safe and use a
300
-- share.
301 302
-- values can be anything the min/max permits so we can either think of
303
-- real values of a fraction along the axis (probably easier)
304 305
-- wght=400,wdth=100,ital=1
306 307
local
function
axistofactors
(
str
)
308
local
t
=
settings_to_hash
(
str
)
309
for
k
,
v
in
next
,
t
do
310
t
[
k
]
=
tonumber
(
v
)
or
v
-- this also normalizes numbers itself
311
end
312
return
t
313
end
314 315
local
hash
=
table
.
setmetatableindex
(
function
(
t
,
k
)
316
local
v
=
sequenced
(
axistofactors
(
k
)
,
"
,
"
)
317
t
[
k
]
=
v
318
return
v
319
end
)
320 321
helpers
.
normalizedaxishash
=
hash
322 323
local
cleanname
=
fonts
.
names
and
fonts
.
names
.
cleanname
or
function
(
name
)
324
return
name
and
(
gsub
(
lower
(
name
)
,
"
[^%a%d]
"
,
"
"
)
)
or
nil
325
end
326 327
helpers
.
cleanname
=
cleanname
328 329
function
helpers
.
normalizedaxis
(
str
)
330
return
hash
[
str
]
or
str
331
end
332 333
-- contradicting spec ... (signs) so i'll check it and fix it once we have
334
-- proper fonts
335 336
local
function
getaxisscale
(
segments
,
minimum
,
default
,
maximum
,
user
)
337
--
338
-- returns the right values cf example in standard
339
--
340
if
not
minimum
or
not
default
or
not
maximum
then
341
return
false
342
end
343
if
user
<
minimum
then
344
user
=
minimum
345
elseif
user
>
maximum
then
346
user
=
maximum
347
end
348
if
user
<
default
then
349
default
=
-
(
default
-
user
)
/
(
default
-
minimum
)
350
elseif
user
>
default
then
351
default
=
(
user
-
default
)
/
(
maximum
-
default
)
352
else
353
default
=
0
354
end
355
if
not
segments
then
356
return
default
357
end
358
local
e
359
for
i
=
1
,
#
segments
do
360
local
s
=
segments
[
i
]
361
if
type
(
s
)
~
=
"
number
"
then
362
report
(
"
using default axis scale
"
)
363
return
default
364
elseif
s
[
1
]
>
=
default
then
365
if
s
[
2
]
=
=
default
then
366
return
default
367
else
368
e
=
i
369
break
370
end
371
end
372
end
373
if
e
then
374
local
b
=
segments
[
e
-1
]
375
local
e
=
segments
[
e
]
376
return
b
[
2
]
+
(
e
[
2
]
-
b
[
2
]
)
*
(
default
-
b
[
1
]
)
/
(
e
[
1
]
-
b
[
1
]
)
377
else
378
return
false
379
end
380
end
381 382
local
function
getfactors
(
data
,
instancespec
)
383
if
instancespec
=
=
true
then
384
-- take default
385
elseif
type
(
instancespec
)
~
=
"
string
"
or
instancespec
=
=
"
"
then
386
return
387
end
388
local
variabledata
=
data
.
variabledata
389
if
not
variabledata
then
390
return
391
end
392
local
instances
=
variabledata
.
instances
393
local
axis
=
variabledata
.
axis
394
local
segments
=
variabledata
.
segments
395
if
instances
and
axis
then
396
local
values
397
if
instancespec
=
=
true
then
398
-- first instance:
399
-- values = instances[1].values
400
-- axis defaults:
401
values
=
{
}
402
for
i
=
1
,
#
axis
do
403
values
[
i
]
=
{
404
-- axis = axis[i].tag,
405
value
=
axis
[
i
]
.
default
,
406
}
407
end
408 409
else
410
for
i
=
1
,
#
instances
do
411
local
instance
=
instances
[
i
]
412
if
cleanname
(
instance
.
subfamily
)
=
=
instancespec
then
413
values
=
instance
.
values
414
break
415
end
416
end
417
end
418
if
values
then
419
local
factors
=
{
}
420
for
i
=
1
,
#
axis
do
421
local
a
=
axis
[
i
]
422
factors
[
i
]
=
getaxisscale
(
segments
,
a
.
minimum
,
a
.
default
,
a
.
maximum
,
values
[
i
]
.
value
)
423
end
424
return
factors
425
end
426
local
values
=
axistofactors
(
hash
[
instancespec
]
or
instancespec
)
427
if
values
then
428
local
factors
=
{
}
429
for
i
=
1
,
#
axis
do
430
local
a
=
axis
[
i
]
431
local
d
=
a
.
default
432
factors
[
i
]
=
getaxisscale
(
segments
,
a
.
minimum
,
d
,
a
.
maximum
,
values
[
a
.
name
or
a
.
tag
]
or
d
)
433
end
434
return
factors
435
end
436
end
437
end
438 439
local
function
getscales
(
regions
,
factors
)
440
local
scales
=
{
}
441
for
i
=
1
,
#
regions
do
442
local
region
=
regions
[
i
]
443
local
s
=
1
444
for
j
=
1
,
#
region
do
445
local
axis
=
region
[
j
]
446
local
f
=
factors
[
j
]
447
local
start
=
axis
.
start
448
local
peak
=
axis
.
peak
449
local
stop
=
axis
.
stop
450
-- get rid of these tests, false flag
451
if
start
>
peak
or
peak
>
stop
then
452
-- * 1
453
elseif
start
<
0
and
stop
>
0
and
peak
~
=
0
then
454
-- * 1
455
elseif
peak
=
=
0
then
456
-- * 1
457
elseif
f
<
start
or
f
>
stop
then
458
-- * 0
459
s
=
0
460
break
461
elseif
f
<
peak
then
462
-- s = - s * (f - start) / (peak - start)
463
s
=
s
*
(
f
-
start
)
/
(
peak
-
start
)
464
elseif
f
>
peak
then
465
s
=
s
*
(
stop
-
f
)
/
(
stop
-
peak
)
466
else
467
-- * 1
468
end
469
end
470
scales
[
i
]
=
s
471
end
472
return
scales
473
end
474 475
helpers
.
getaxisscale
=
getaxisscale
476
helpers
.
getfactors
=
getfactors
477
helpers
.
getscales
=
getscales
478
helpers
.
axistofactors
=
axistofactors
479 480
local
function
readvariationdata
(
f
,
storeoffset
,
factors
)
-- store
481
local
position
=
getposition
(
f
)
482
setposition
(
f
,
storeoffset
)
483
-- header
484
local
format
=
readushort
(
f
)
485
local
regionoffset
=
storeoffset
+
readulong
(
f
)
486
local
nofdeltadata
=
readushort
(
f
)
487
local
deltadata
=
readcardinaltable
(
f
,
nofdeltadata
,
ulong
)
488
-- regions
489
setposition
(
f
,
regionoffset
)
490
local
nofaxis
=
readushort
(
f
)
491
local
nofregions
=
readushort
(
f
)
492
local
regions
=
{
}
493
for
i
=
1
,
nofregions
do
-- 0
494
local
t
=
{
}
495
for
i
=
1
,
nofaxis
do
496
t
[
i
]
=
{
-- maybe no keys, just 1..3
497
start
=
read2dot14
(
f
)
,
498
peak
=
read2dot14
(
f
)
,
499
stop
=
read2dot14
(
f
)
,
500
}
501
end
502
regions
[
i
]
=
t
503
end
504
-- deltas
505
if
factors
then
506
for
i
=
1
,
nofdeltadata
do
507
setposition
(
f
,
storeoffset
+
deltadata
[
i
]
)
508
local
nofdeltasets
=
readushort
(
f
)
509
local
nofshorts
=
readushort
(
f
)
510
local
nofregions
=
readushort
(
f
)
511
local
usedregions
=
{
}
512
local
deltas
=
{
}
513
for
i
=
1
,
nofregions
do
514
usedregions
[
i
]
=
regions
[
readushort
(
f
)
+
1
]
515
end
516
-- we could test before and save a for
517
for
i
=
1
,
nofdeltasets
do
518
local
t
=
readintegertable
(
f
,
nofshorts
,
short
)
519
for
i
=
nofshorts
+
1
,
nofregions
do
520
t
[
i
]
=
readinteger
(
f
)
521
end
522
deltas
[
i
]
=
t
523
end
524
deltadata
[
i
]
=
{
525
regions
=
usedregions
,
526
deltas
=
deltas
,
527
scales
=
factors
and
getscales
(
usedregions
,
factors
)
or
nil
,
528
}
529
end
530
end
531
setposition
(
f
,
position
)
532
return
regions
,
deltadata
533
end
534 535
helpers
.
readvariationdata
=
readvariationdata
536 537
-- Beware: only use the simple variant if we don't set keys/values (otherwise too many entries). We
538
-- could also have a variant that applies a function but there is no real benefit in this.
539 540
local
function
readcoverage
(
f
,
offset
,
simple
)
541
setposition
(
f
,
offset
)
542
local
coverageformat
=
readushort
(
f
)
543
if
coverageformat
=
=
1
then
544
local
nofcoverage
=
readushort
(
f
)
545
if
simple
then
546
-- often 1 or 2
547
if
nofcoverage
=
=
1
then
548
return
{
readushort
(
f
)
}
549
elseif
nofcoverage
=
=
2
then
550
return
{
readushort
(
f
)
,
readushort
(
f
)
}
551
else
552
return
readcardinaltable
(
f
,
nofcoverage
,
ushort
)
553
end
554
elseif
nofcoverage
=
=
1
then
555
return
{
[
readushort
(
f
)
]
=
0
}
556
elseif
nofcoverage
=
=
2
then
557
return
{
[
readushort
(
f
)
]
=
0
,
[
readushort
(
f
)
]
=
1
}
558
else
559
local
coverage
=
{
}
560
for
i
=
0
,
nofcoverage
-1
do
561
coverage
[
readushort
(
f
)
]
=
i
-- index in record
562
end
563
return
coverage
564
end
565
elseif
coverageformat
=
=
2
then
566
local
nofranges
=
readushort
(
f
)
567
local
coverage
=
{
}
568
local
n
=
simple
and
1
or
0
-- needs checking
569
for
i
=
1
,
nofranges
do
570
local
firstindex
=
readushort
(
f
)
571
local
lastindex
=
readushort
(
f
)
572
local
coverindex
=
readushort
(
f
)
573
if
simple
then
574
for
i
=
firstindex
,
lastindex
do
575
coverage
[
n
]
=
i
576
n
=
n
+
1
577
end
578
else
579
for
i
=
firstindex
,
lastindex
do
580
coverage
[
i
]
=
n
581
n
=
n
+
1
582
end
583
end
584
end
585
return
coverage
586
else
587
report
(
"
unknown coverage format %a
"
,
coverageformat
)
588
return
{
}
589
end
590
end
591 592
local
function
readclassdef
(
f
,
offset
,
preset
)
593
setposition
(
f
,
offset
)
594
local
classdefformat
=
readushort
(
f
)
595
local
classdef
=
{
}
596
if
type
(
preset
)
=
=
"
number
"
then
597
for
k
=
0
,
preset
-1
do
598
classdef
[
k
]
=
1
599
end
600
end
601
if
classdefformat
=
=
1
then
602
local
index
=
readushort
(
f
)
603
local
nofclassdef
=
readushort
(
f
)
604
for
i
=
1
,
nofclassdef
do
605
classdef
[
index
]
=
readushort
(
f
)
+
1
606
index
=
index
+
1
607
end
608
elseif
classdefformat
=
=
2
then
609
local
nofranges
=
readushort
(
f
)
610
local
n
=
0
611
for
i
=
1
,
nofranges
do
612
local
firstindex
=
readushort
(
f
)
613
local
lastindex
=
readushort
(
f
)
614
local
class
=
readushort
(
f
)
+
1
615
for
i
=
firstindex
,
lastindex
do
616
classdef
[
i
]
=
class
617
end
618
end
619
else
620
report
(
"
unknown classdef format %a
"
,
classdefformat
)
621
end
622
if
type
(
preset
)
=
=
"
table
"
then
623
for
k
in
next
,
preset
do
624
if
not
classdef
[
k
]
then
625
classdef
[
k
]
=
1
626
end
627
end
628
end
629
return
classdef
630
end
631 632
local
function
classtocoverage
(
defs
)
633
if
defs
then
634
local
list
=
{
}
635
for
index
,
class
in
next
,
defs
do
636
local
c
=
list
[
class
]
637
if
c
then
638
c
[
#
c
+
1
]
=
index
639
else
640
list
[
class
]
=
{
index
}
641
end
642
end
643
return
list
644
end
645
end
646 647
-- extra readers
648 649
local
skips
=
{
[
0
]
=
650
0
,
-- ----
651
1
,
-- ---x
652
1
,
-- --y-
653
2
,
-- --yx
654
1
,
-- -h--
655
2
,
-- -h-x
656
2
,
-- -hy-
657
3
,
-- -hyx
658
2
,
-- v--x
659
2
,
-- v-y-
660
3
,
-- v-yx
661
2
,
-- vh--
662
3
,
-- vh-x
663
3
,
-- vhy-
664
4
,
-- vhyx
665
}
666 667
-- We can assume that 0 is nothing and in fact we can start at 1 as
668
-- usual in Lua to make sure of that.
669 670
local
function
readvariation
(
f
,
offset
)
671
local
p
=
getposition
(
f
)
672
setposition
(
f
,
offset
)
673
local
outer
=
readushort
(
f
)
674
local
inner
=
readushort
(
f
)
675
local
format
=
readushort
(
f
)
676
setposition
(
f
,
p
)
677
if
format
=
=
0x8000
then
678
return
outer
,
inner
679
end
680
end
681 682
local
function
readposition
(
f
,
format
,
mainoffset
,
getdelta
)
683
if
format
=
=
0
then
684
return
false
685
end
686
-- a few happen often
687
if
format
=
=
0x04
then
688
local
h
=
readshort
(
f
)
689
if
h
=
=
0
then
690
return
true
-- all zero
691
else
692
return
{
0
,
0
,
h
,
0
}
693
end
694
end
695
if
format
=
=
0x05
then
696
local
x
=
readshort
(
f
)
697
local
h
=
readshort
(
f
)
698
if
x
=
=
0
and
h
=
=
0
then
699
return
true
-- all zero
700
else
701
return
{
x
,
0
,
h
,
0
}
702
end
703
end
704
if
format
=
=
0x44
then
705
local
h
=
readshort
(
f
)
706
if
getdelta
then
707
local
d
=
readshort
(
f
)
-- short or ushort
708
if
d
>
0
then
709
local
outer
,
inner
=
readvariation
(
f
,
mainoffset
+
d
)
710
if
outer
then
711
h
=
h
+
getdelta
(
outer
,
inner
)
712
end
713
end
714
else
715
skipshort
(
f
,
1
)
716
end
717
if
h
=
=
0
then
718
return
true
-- all zero
719
else
720
return
{
0
,
0
,
h
,
0
}
721
end
722
end
723
--
724
-- todo:
725
--
726
-- if format == 0x55 then
727
-- local x = readshort(f)
728
-- local h = readshort(f)
729
-- ....
730
-- end
731
--
732
local
x
=
band
(
format
,
0x1
)
~
=
0
and
readshort
(
f
)
or
0
-- x placement
733
local
y
=
band
(
format
,
0x2
)
~
=
0
and
readshort
(
f
)
or
0
-- y placement
734
local
h
=
band
(
format
,
0x4
)
~
=
0
and
readshort
(
f
)
or
0
-- h advance
735
local
v
=
band
(
format
,
0x8
)
~
=
0
and
readshort
(
f
)
or
0
-- v advance
736
if
format
>
=
0x10
then
737
local
X
=
band
(
format
,
0x10
)
~
=
0
and
skipshort
(
f
)
or
0
738
local
Y
=
band
(
format
,
0x20
)
~
=
0
and
skipshort
(
f
)
or
0
739
local
H
=
band
(
format
,
0x40
)
~
=
0
and
skipshort
(
f
)
or
0
740
local
V
=
band
(
format
,
0x80
)
~
=
0
and
skipshort
(
f
)
or
0
741
local
s
=
skips
[
extract
(
format
,
4
,
4
)
]
742
if
s
>
0
then
743
skipshort
(
f
,
s
)
744
end
745
if
getdelta
then
746
if
X
>
0
then
747
local
outer
,
inner
=
readvariation
(
f
,
mainoffset
+
X
)
748
if
outer
then
749
x
=
x
+
getdelta
(
outer
,
inner
)
750
end
751
end
752
if
Y
>
0
then
753
local
outer
,
inner
=
readvariation
(
f
,
mainoffset
+
Y
)
754
if
outer
then
755
y
=
y
+
getdelta
(
outer
,
inner
)
756
end
757
end
758
if
H
>
0
then
759
local
outer
,
inner
=
readvariation
(
f
,
mainoffset
+
H
)
760
if
outer
then
761
h
=
h
+
getdelta
(
outer
,
inner
)
762
end
763
end
764
if
V
>
0
then
765
local
outer
,
inner
=
readvariation
(
f
,
mainoffset
+
V
)
766
if
outer
then
767
v
=
v
+
getdelta
(
outer
,
inner
)
768
end
769
end
770
end
771
return
{
x
,
y
,
h
,
v
}
772
elseif
x
=
=
0
and
y
=
=
0
and
h
=
=
0
and
v
=
=
0
then
773
return
true
-- all zero
774
else
775
return
{
x
,
y
,
h
,
v
}
776
end
777
end
778 779
local
function
readanchor
(
f
,
offset
,
getdelta
)
-- maybe also ignore 0's as in pos
780
if
not
offset
or
offset
=
=
0
then
781
return
nil
-- false
782
end
783
setposition
(
f
,
offset
)
784
-- no need to skip as we position each
785
local
format
=
readshort
(
f
)
-- 1: x y 2: x y index 3 x y X Y
786
local
x
=
readshort
(
f
)
787
local
y
=
readshort
(
f
)
788
if
format
=
=
3
then
789
if
getdelta
then
790
local
X
=
readshort
(
f
)
791
local
Y
=
readshort
(
f
)
792
if
X
>
0
then
793
local
outer
,
inner
=
readvariation
(
f
,
offset
+
X
)
794
if
outer
then
795
x
=
x
+
getdelta
(
outer
,
inner
)
796
end
797
end
798
if
Y
>
0
then
799
local
outer
,
inner
=
readvariation
(
f
,
offset
+
Y
)
800
if
outer
then
801
y
=
y
+
getdelta
(
outer
,
inner
)
802
end
803
end
804
else
805
skipshort
(
f
,
2
)
806
end
807
return
{
x
,
y
}
-- , { xindex, yindex }
808
else
809
return
{
x
,
y
}
810
end
811
end
812 813
-- common handlers: inlining can be faster but we cache anyway
814
-- so we don't bother too much about speed here
815 816
local
function
readfirst
(
f
,
offset
)
817
if
offset
then
818
setposition
(
f
,
offset
)
819
end
820
return
{
readushort
(
f
)
}
821
end
822 823
-- quite often 0, 1, 2
824 825
function
readarray
(
f
,
offset
)
826
if
offset
then
827
setposition
(
f
,
offset
)
828
end
829
local
n
=
readushort
(
f
)
830
if
n
=
=
1
then
831
return
{
readushort
(
f
)
}
,
1
832
elseif
n
>
0
then
833
return
readcardinaltable
(
f
,
n
,
ushort
)
,
n
834
end
835
end
836 837
local
function
readcoveragearray
(
f
,
offset
,
t
,
simple
)
838
if
not
t
then
839
return
nil
840
end
841
local
n
=
#
t
842
if
n
=
=
0
then
843
return
nil
844
end
845
for
i
=
1
,
n
do
846
t
[
i
]
=
readcoverage
(
f
,
offset
+
t
[
i
]
,
simple
)
847
end
848
return
t
849
end
850 851
local
function
covered
(
subset
,
all
)
852
local
used
,
u
853
for
i
=
1
,
#
subset
do
854
local
s
=
subset
[
i
]
855
if
all
[
s
]
then
856
if
used
then
857
u
=
u
+
1
858
used
[
u
]
=
s
859
else
860
u
=
1
861
used
=
{
s
}
862
end
863
end
864
end
865
return
used
866
end
867 868
-- We generalize the chained lookups so that we can do with only one handler
869
-- when processing them.
870 871
-- pruned
872 873
local
function
readlookuparray
(
f
,
noflookups
,
nofcurrent
)
874
local
lookups
=
{
}
875
if
noflookups
>
0
then
876
local
length
=
0
877
for
i
=
1
,
noflookups
do
878
local
index
=
readushort
(
f
)
+
1
879
if
index
>
length
then
880
length
=
index
881
end
882
local
lookup
=
readushort
(
f
)
+
1
883
local
list
=
lookups
[
index
]
884
if
list
then
885
list
[
#
list
+
1
]
=
lookup
886
else
887
lookups
[
index
]
=
{
lookup
}
888
end
889
end
890
for
index
=
1
,
length
do
891
if
not
lookups
[
index
]
then
892
lookups
[
index
]
=
false
893
end
894
end
895
-- if length > nofcurrent then
896
-- report("more lookups than currently matched characters")
897
-- end
898
end
899
return
lookups
900
end
901 902
-- not pruned
903
--
904
-- local function readlookuparray(f,noflookups,nofcurrent)
905
-- local lookups = { }
906
-- for i=1,nofcurrent do
907
-- lookups[i] = false
908
-- end
909
-- for i=1,noflookups do
910
-- local index = readushort(f) + 1
911
-- if index > nofcurrent then
912
-- report("more lookups than currently matched characters")
913
-- for i=nofcurrent+1,index-1 do
914
-- lookups[i] = false
915
-- end
916
-- nofcurrent = index
917
-- end
918
-- lookups[index] = readushort(f) + 1
919
-- end
920
-- return lookups
921
-- end
922 923
local
function
unchainedcontext
(
f
,
fontdata
,
lookupid
,
lookupoffset
,
offset
,
glyphs
,
nofglyphs
,
what
)
924
local
tableoffset
=
lookupoffset
+
offset
925
setposition
(
f
,
tableoffset
)
926
local
subtype
=
readushort
(
f
)
927
if
subtype
=
=
1
then
928
local
coverage
=
readushort
(
f
)
929
local
subclasssets
=
readarray
(
f
)
930
local
rules
=
{
}
931
if
subclasssets
then
932
coverage
=
readcoverage
(
f
,
tableoffset
+
coverage
,
true
)
933
for
i
=
1
,
#
subclasssets
do
934
local
offset
=
subclasssets
[
i
]
935
if
offset
>
0
then
936
local
firstcoverage
=
coverage
[
i
]
937
local
rulesoffset
=
tableoffset
+
offset
938
local
subclassrules
=
readarray
(
f
,
rulesoffset
)
939
for
rule
=
1
,
#
subclassrules
do
940
setposition
(
f
,
rulesoffset
+
subclassrules
[
rule
]
)
941
local
nofcurrent
=
readushort
(
f
)
942
local
noflookups
=
readushort
(
f
)
943
local
current
=
{
{
firstcoverage
}
}
944
for
i
=
2
,
nofcurrent
do
945
current
[
i
]
=
{
readushort
(
f
)
}
946
end
947
local
lookups
=
readlookuparray
(
f
,
noflookups
,
nofcurrent
)
948
rules
[
#
rules
+
1
]
=
{
949
current
=
current
,
950
lookups
=
lookups
951
}
952
end
953
end
954
end
955
else
956
report
(
"
empty subclassset in %a subtype %i
"
,
"
unchainedcontext
"
,
subtype
)
957
end
958
return
{
959
format
=
"
glyphs
"
,
960
rules
=
rules
,
961
}
962
elseif
subtype
=
=
2
then
963
-- We expand the classes as later on we do a pack over the whole table so then we get
964
-- back efficiency. This way we can also apply the coverage to the first current.
965
local
coverage
=
readushort
(
f
)
966
local
currentclassdef
=
readushort
(
f
)
967
local
subclasssets
=
readarray
(
f
)
968
local
rules
=
{
}
969
if
subclasssets
then
970
coverage
=
readcoverage
(
f
,
tableoffset
+
coverage
)
971
currentclassdef
=
readclassdef
(
f
,
tableoffset
+
currentclassdef
,
coverage
)
972
local
currentclasses
=
classtocoverage
(
currentclassdef
,
fontdata
.
glyphs
)
973
for
class
=
1
,
#
subclasssets
do
974
local
offset
=
subclasssets
[
class
]
975
if
offset
>
0
then
976
local
firstcoverage
=
currentclasses
[
class
]
977
if
firstcoverage
then
978
firstcoverage
=
covered
(
firstcoverage
,
coverage
)
-- bonus
979
if
firstcoverage
then
980
local
rulesoffset
=
tableoffset
+
offset
981
local
subclassrules
=
readarray
(
f
,
rulesoffset
)
982
for
rule
=
1
,
#
subclassrules
do
983
setposition
(
f
,
rulesoffset
+
subclassrules
[
rule
]
)
984
local
nofcurrent
=
readushort
(
f
)
985
local
noflookups
=
readushort
(
f
)
986
local
current
=
{
firstcoverage
}
987
for
i
=
2
,
nofcurrent
do
988
current
[
i
]
=
currentclasses
[
readushort
(
f
)
+
1
]
989
end
990
local
lookups
=
readlookuparray
(
f
,
noflookups
,
nofcurrent
)
991
rules
[
#
rules
+
1
]
=
{
992
current
=
current
,
993
lookups
=
lookups
994
}
995
end
996
else
997
report
(
"
no coverage
"
)
998
end
999
else
1000
report
(
"
no coverage class
"
)
1001
end
1002
end
1003
end
1004
else
1005
report
(
"
empty subclassset in %a subtype %i
"
,
"
unchainedcontext
"
,
subtype
)
1006
end
1007
return
{
1008
format
=
"
class
"
,
1009
rules
=
rules
,
1010
}
1011
elseif
subtype
=
=
3
then
1012
local
nofglyphs
=
readushort
(
f
)
1013
local
noflookups
=
readushort
(
f
)
1014
local
current
=
readcardinaltable
(
f
,
nofglyphs
,
ushort
)
1015
local
lookups
=
readlookuparray
(
f
,
noflookups
,
#
current
)
1016
current
=
readcoveragearray
(
f
,
tableoffset
,
current
,
true
)
1017
return
{
1018
format
=
"
coverage
"
,
1019
rules
=
{
1020
{
1021
current
=
current
,
1022
lookups
=
lookups
,
1023
}
1024
}
1025
}
1026
else
1027
report
(
"
unsupported subtype %a in %a %s
"
,
subtype
,
"
unchainedcontext
"
,
what
)
1028
end
1029
end
1030 1031
-- todo: optimize for n=1 ?
1032 1033
-- class index needs checking, probably no need for +1
1034 1035
local
function
chainedcontext
(
f
,
fontdata
,
lookupid
,
lookupoffset
,
offset
,
glyphs
,
nofglyphs
,
what
)
1036
local
tableoffset
=
lookupoffset
+
offset
1037
setposition
(
f
,
tableoffset
)
1038
local
subtype
=
readushort
(
f
)
1039
if
subtype
=
=
1
then
1040
local
coverage
=
readushort
(
f
)
1041
local
subclasssets
=
readarray
(
f
)
1042
local
rules
=
{
}
1043
if
subclasssets
then
1044
coverage
=
readcoverage
(
f
,
tableoffset
+
coverage
,
true
)
1045
for
i
=
1
,
#
subclasssets
do
1046
local
offset
=
subclasssets
[
i
]
1047
if
offset
>
0
then
1048
local
firstcoverage
=
coverage
[
i
]
1049
local
rulesoffset
=
tableoffset
+
offset
1050
local
subclassrules
=
readarray
(
f
,
rulesoffset
)
1051
for
rule
=
1
,
#
subclassrules
do
1052
setposition
(
f
,
rulesoffset
+
subclassrules
[
rule
]
)
1053
local
nofbefore
=
readushort
(
f
)
1054
local
before
1055
if
nofbefore
>
0
then
1056
before
=
{
}
1057
for
i
=
1
,
nofbefore
do
1058
before
[
i
]
=
{
readushort
(
f
)
}
1059
end
1060
end
1061
local
nofcurrent
=
readushort
(
f
)
1062
local
current
=
{
{
firstcoverage
}
}
1063
for
i
=
2
,
nofcurrent
do
1064
current
[
i
]
=
{
readushort
(
f
)
}
1065
end
1066
local
nofafter
=
readushort
(
f
)
1067
local
after
1068
if
nofafter
>
0
then
1069
after
=
{
}
1070
for
i
=
1
,
nofafter
do
1071
after
[
i
]
=
{
readushort
(
f
)
}
1072
end
1073
end
1074
local
noflookups
=
readushort
(
f
)
1075
local
lookups
=
readlookuparray
(
f
,
noflookups
,
nofcurrent
)
1076
rules
[
#
rules
+
1
]
=
{
1077
before
=
before
,
1078
current
=
current
,
1079
after
=
after
,
1080
lookups
=
lookups
,
1081
}
1082
end
1083
end
1084
end
1085
else
1086
report
(
"
empty subclassset in %a subtype %i
"
,
"
chainedcontext
"
,
subtype
)
1087
end
1088
return
{
1089
format
=
"
glyphs
"
,
1090
rules
=
rules
,
1091
}
1092
elseif
subtype
=
=
2
then
1093
local
coverage
=
readushort
(
f
)
1094
local
beforeclassdef
=
readushort
(
f
)
1095
local
currentclassdef
=
readushort
(
f
)
1096
local
afterclassdef
=
readushort
(
f
)
1097
local
subclasssets
=
readarray
(
f
)
1098
local
rules
=
{
}
1099
if
subclasssets
then
1100
local
coverage
=
readcoverage
(
f
,
tableoffset
+
coverage
)
1101
local
beforeclassdef
=
readclassdef
(
f
,
tableoffset
+
beforeclassdef
,
nofglyphs
)
1102
local
currentclassdef
=
readclassdef
(
f
,
tableoffset
+
currentclassdef
,
coverage
)
1103
local
afterclassdef
=
readclassdef
(
f
,
tableoffset
+
afterclassdef
,
nofglyphs
)
1104
local
beforeclasses
=
classtocoverage
(
beforeclassdef
,
fontdata
.
glyphs
)
1105
local
currentclasses
=
classtocoverage
(
currentclassdef
,
fontdata
.
glyphs
)
1106
local
afterclasses
=
classtocoverage
(
afterclassdef
,
fontdata
.
glyphs
)
1107
for
class
=
1
,
#
subclasssets
do
1108
local
offset
=
subclasssets
[
class
]
1109
if
offset
>
0
then
1110
local
firstcoverage
=
currentclasses
[
class
]
1111
if
firstcoverage
then
1112
firstcoverage
=
covered
(
firstcoverage
,
coverage
)
-- bonus
1113
if
firstcoverage
then
1114
local
rulesoffset
=
tableoffset
+
offset
1115
local
subclassrules
=
readarray
(
f
,
rulesoffset
)
1116
for
rule
=
1
,
#
subclassrules
do
1117
-- watch out, in context we first get the counts and then the arrays while
1118
-- here we get them mixed
1119
setposition
(
f
,
rulesoffset
+
subclassrules
[
rule
]
)
1120
local
nofbefore
=
readushort
(
f
)
1121
local
before
1122
if
nofbefore
>
0
then
1123
before
=
{
}
1124
for
i
=
1
,
nofbefore
do
1125
before
[
i
]
=
beforeclasses
[
readushort
(
f
)
+
1
]
1126
end
1127
end
1128
local
nofcurrent
=
readushort
(
f
)
1129
local
current
=
{
firstcoverage
}
1130
for
i
=
2
,
nofcurrent
do
1131
current
[
i
]
=
currentclasses
[
readushort
(
f
)
+
1
]
1132
end
1133
local
nofafter
=
readushort
(
f
)
1134
local
after
1135
if
nofafter
>
0
then
1136
after
=
{
}
1137
for
i
=
1
,
nofafter
do
1138
after
[
i
]
=
afterclasses
[
readushort
(
f
)
+
1
]
1139
end
1140
end
1141
-- no sequence index here (so why in context as it saves nothing)
1142
local
noflookups
=
readushort
(
f
)
1143
local
lookups
=
readlookuparray
(
f
,
noflookups
,
nofcurrent
)
1144
rules
[
#
rules
+
1
]
=
{
1145
before
=
before
,
1146
current
=
current
,
1147
after
=
after
,
1148
lookups
=
lookups
,
1149
}
1150
end
1151
else
1152
report
(
"
no coverage
"
)
1153
end
1154
else
1155
report
(
"
class is not covered
"
)
1156
end
1157
end
1158
end
1159
else
1160
report
(
"
empty subclassset in %a subtype %i
"
,
"
chainedcontext
"
,
subtype
)
1161
end
1162
return
{
1163
format
=
"
class
"
,
1164
rules
=
rules
,
1165
}
1166
elseif
subtype
=
=
3
then
1167
local
before
=
readarray
(
f
)
1168
local
current
=
readarray
(
f
)
1169
local
after
=
readarray
(
f
)
1170
local
noflookups
=
readushort
(
f
)
1171
local
lookups
=
readlookuparray
(
f
,
noflookups
,
#
current
)
1172
before
=
readcoveragearray
(
f
,
tableoffset
,
before
,
true
)
1173
current
=
readcoveragearray
(
f
,
tableoffset
,
current
,
true
)
1174
after
=
readcoveragearray
(
f
,
tableoffset
,
after
,
true
)
1175
return
{
1176
format
=
"
coverage
"
,
1177
rules
=
{
1178
{
1179
before
=
before
,
1180
current
=
current
,
1181
after
=
after
,
1182
lookups
=
lookups
,
1183
}
1184
}
1185
}
1186
else
1187
report
(
"
unsupported subtype %a in %a %s
"
,
subtype
,
"
chainedcontext
"
,
what
)
1188
end
1189
end
1190 1191
local
function
extension
(
f
,
fontdata
,
lookupid
,
lookupoffset
,
offset
,
glyphs
,
nofglyphs
,
types
,
handlers
,
what
)
1192
local
tableoffset
=
lookupoffset
+
offset
1193
setposition
(
f
,
tableoffset
)
1194
local
subtype
=
readushort
(
f
)
1195
if
subtype
=
=
1
then
1196
local
lookuptype
=
types
[
readushort
(
f
)
]
1197
local
faroffset
=
readulong
(
f
)
1198
local
handler
=
handlers
[
lookuptype
]
1199
if
handler
then
1200
-- maybe we can just pass one offset (or tableoffset first)
1201
return
handler
(
f
,
fontdata
,
lookupid
,
tableoffset
+
faroffset
,
0
,
glyphs
,
nofglyphs
)
,
lookuptype
1202
else
1203
report
(
"
no handler for lookuptype %a subtype %a in %s %s
"
,
lookuptype
,
subtype
,
what
,
"
extension
"
)
1204
end
1205
else
1206
report
(
"
unsupported subtype %a in %s %s
"
,
subtype
,
what
,
"
extension
"
)
1207
end
1208
end
1209 1210
-- gsub handlers
1211 1212
function
gsubhandlers
.
single
(
f
,
fontdata
,
lookupid
,
lookupoffset
,
offset
,
glyphs
,
nofglyphs
)
1213
local
tableoffset
=
lookupoffset
+
offset
1214
setposition
(
f
,
tableoffset
)
1215
local
subtype
=
readushort
(
f
)
1216
if
subtype
=
=
1
then
1217
local
coverage
=
readushort
(
f
)
1218
local
delta
=
readshort
(
f
)
-- can be negative
1219
local
coverage
=
readcoverage
(
f
,
tableoffset
+
coverage
)
-- not simple as we need to set key/value anyway
1220
for
index
in
next
,
coverage
do
1221
local
newindex
=
(
index
+
delta
)
%
65536
-- modulo is new in 1.8.3
1222
if
index
>
nofglyphs
or
newindex
>
nofglyphs
then
1223
report
(
"
invalid index in %s format %i: %i -> %i (max %i)
"
,
"
single
"
,
subtype
,
index
,
newindex
,
nofglyphs
)
1224
coverage
[
index
]
=
nil
1225
else
1226
coverage
[
index
]
=
newindex
1227
end
1228
end
1229
return
{
1230
coverage
=
coverage
1231
}
1232
elseif
subtype
=
=
2
then
-- in streamreader a seek and fetch is faster than a temp table
1233
local
coverage
=
readushort
(
f
)
1234
local
nofreplacements
=
readushort
(
f
)
1235
local
replacements
=
readcardinaltable
(
f
,
nofreplacements
,
ushort
)
1236
local
coverage
=
readcoverage
(
f
,
tableoffset
+
coverage
)
-- not simple as we need to set key/value anyway
1237
for
index
,
newindex
in
next
,
coverage
do
1238
newindex
=
newindex
+
1
1239
if
index
>
nofglyphs
or
newindex
>
nofglyphs
then
1240
report
(
"
invalid index in %s format %i: %i -> %i (max %i)
"
,
"
single
"
,
subtype
,
index
,
newindex
,
nofglyphs
)
1241
coverage
[
index
]
=
nil
1242
else
1243
coverage
[
index
]
=
replacements
[
newindex
]
1244
end
1245
end
1246
return
{
1247
coverage
=
coverage
1248
}
1249
else
1250
report
(
"
unsupported subtype %a in %a substitution
"
,
subtype
,
"
single
"
)
1251
end
1252
end
1253 1254
-- we see coverage format 0x300 in some old ms fonts
1255 1256
local
function
sethandler
(
f
,
fontdata
,
lookupid
,
lookupoffset
,
offset
,
glyphs
,
nofglyphs
,
what
)
1257
local
tableoffset
=
lookupoffset
+
offset
1258
setposition
(
f
,
tableoffset
)
1259
local
subtype
=
readushort
(
f
)
1260
if
subtype
=
=
1
then
1261
local
coverage
=
readushort
(
f
)
1262
local
nofsequence
=
readushort
(
f
)
1263
local
sequences
=
readcardinaltable
(
f
,
nofsequence
,
ushort
)
1264
for
i
=
1
,
nofsequence
do
1265
setposition
(
f
,
tableoffset
+
sequences
[
i
]
)
1266
sequences
[
i
]
=
readcardinaltable
(
f
,
readushort
(
f
)
,
ushort
)
1267
end
1268
local
coverage
=
readcoverage
(
f
,
tableoffset
+
coverage
)
1269
for
index
,
newindex
in
next
,
coverage
do
1270
newindex
=
newindex
+
1
1271
if
index
>
nofglyphs
or
newindex
>
nofglyphs
then
1272
report
(
"
invalid index in %s format %i: %i -> %i (max %i)
"
,
what
,
subtype
,
index
,
newindex
,
nofglyphs
)
1273
coverage
[
index
]
=
nil
1274
else
1275
coverage
[
index
]
=
sequences
[
newindex
]
1276
end
1277
end
1278
return
{
1279
coverage
=
coverage
1280
}
1281
else
1282
report
(
"
unsupported subtype %a in %a substitution
"
,
subtype
,
what
)
1283
end
1284
end
1285 1286
function
gsubhandlers
.
multiple
(
f
,
fontdata
,
lookupid
,
lookupoffset
,
offset
,
glyphs
,
nofglyphs
)
1287
return
sethandler
(
f
,
fontdata
,
lookupid
,
lookupoffset
,
offset
,
glyphs
,
nofglyphs
,
"
multiple
"
)
1288
end
1289 1290
function
gsubhandlers
.
alternate
(
f
,
fontdata
,
lookupid
,
lookupoffset
,
offset
,
glyphs
,
nofglyphs
)
1291
return
sethandler
(
f
,
fontdata
,
lookupid
,
lookupoffset
,
offset
,
glyphs
,
nofglyphs
,
"
alternate
"
)
1292
end
1293 1294
function
gsubhandlers
.
ligature
(
f
,
fontdata
,
lookupid
,
lookupoffset
,
offset
,
glyphs
,
nofglyphs
)
1295
local
tableoffset
=
lookupoffset
+
offset
1296
setposition
(
f
,
tableoffset
)
1297
local
subtype
=
readushort
(
f
)
1298
if
subtype
=
=
1
then
1299
local
coverage
=
readushort
(
f
)
1300
local
nofsets
=
readushort
(
f
)
1301
local
ligatures
=
readcardinaltable
(
f
,
nofsets
,
ushort
)
1302
for
i
=
1
,
nofsets
do
1303
local
offset
=
lookupoffset
+
offset
+
ligatures
[
i
]
1304
setposition
(
f
,
offset
)
1305
local
n
=
readushort
(
f
)
1306
if
n
=
=
1
then
1307
ligatures
[
i
]
=
{
offset
+
readushort
(
f
)
}
1308
else
1309
local
l
=
{
}
1310
for
i
=
1
,
n
do
1311
l
[
i
]
=
offset
+
readushort
(
f
)
1312
end
1313
ligatures
[
i
]
=
l
1314
end
1315
end
1316
local
coverage
=
readcoverage
(
f
,
tableoffset
+
coverage
)
1317
for
index
,
newindex
in
next
,
coverage
do
1318
local
hash
=
{
}
1319
local
ligatures
=
ligatures
[
newindex
+
1
]
1320
for
i
=
1
,
#
ligatures
do
1321
local
offset
=
ligatures
[
i
]
1322
setposition
(
f
,
offset
)
1323
local
lig
=
readushort
(
f
)
1324
local
cnt
=
readushort
(
f
)
1325
local
hsh
=
hash
1326
for
i
=
2
,
cnt
do
1327
local
c
=
readushort
(
f
)
1328
local
h
=
hsh
[
c
]
1329
if
not
h
then
1330
h
=
{
}
1331
hsh
[
c
]
=
h
1332
end
1333
hsh
=
h
1334
end
1335
hsh
.
ligature
=
lig
1336
end
1337
coverage
[
index
]
=
hash
1338
end
1339
return
{
1340
coverage
=
coverage
1341
}
1342
else
1343
report
(
"
unsupported subtype %a in %a substitution
"
,
subtype
,
"
ligature
"
)
1344
end
1345
end
1346 1347
function
gsubhandlers
.
context
(
f
,
fontdata
,
lookupid
,
lookupoffset
,
offset
,
glyphs
,
nofglyphs
)
1348
return
unchainedcontext
(
f
,
fontdata
,
lookupid
,
lookupoffset
,
offset
,
glyphs
,
nofglyphs
,
"
substitution
"
)
,
"
context
"
1349
end
1350 1351
function
gsubhandlers
.
chainedcontext
(
f
,
fontdata
,
lookupid
,
lookupoffset
,
offset
,
glyphs
,
nofglyphs
)
1352
return
chainedcontext
(
f
,
fontdata
,
lookupid
,
lookupoffset
,
offset
,
glyphs
,
nofglyphs
,
"
substitution
"
)
,
"
chainedcontext
"
1353
end
1354 1355
function
gsubhandlers
.
extension
(
f
,
fontdata
,
lookupid
,
lookupoffset
,
offset
,
glyphs
,
nofglyphs
)
1356
return
extension
(
f
,
fontdata
,
lookupid
,
lookupoffset
,
offset
,
glyphs
,
nofglyphs
,
gsubtypes
,
gsubhandlers
,
"
substitution
"
)
1357
end
1358 1359
function
gsubhandlers
.
reversechainedcontextsingle
(
f
,
fontdata
,
lookupid
,
lookupoffset
,
offset
,
glyphs
,
nofglyphs
)
1360
local
tableoffset
=
lookupoffset
+
offset
1361
setposition
(
f
,
tableoffset
)
1362
local
subtype
=
readushort
(
f
)
1363
if
subtype
=
=
1
then
-- NEEDS CHECKING
1364
local
current
=
readfirst
(
f
)
1365
local
before
=
readarray
(
f
)
1366
local
after
=
readarray
(
f
)
1367
local
replacements
=
readarray
(
f
)
1368
current
=
readcoveragearray
(
f
,
tableoffset
,
current
,
true
)
1369
before
=
readcoveragearray
(
f
,
tableoffset
,
before
,
true
)
1370
after
=
readcoveragearray
(
f
,
tableoffset
,
after
,
true
)
1371
return
{
1372
format
=
"
reversecoverage
"
,
-- reversesub
1373
rules
=
{
1374
{
1375
before
=
before
,
1376
current
=
current
,
1377
after
=
after
,
1378
replacements
=
replacements
,
1379
}
1380
}
1381
}
,
"
reversechainedcontextsingle
"
1382
else
1383
report
(
"
unsupported subtype %a in %a substitution
"
,
subtype
,
"
reversechainedcontextsingle
"
)
1384
end
1385
end
1386 1387
-- gpos handlers
1388 1389
local
function
readpairsets
(
f
,
tableoffset
,
sets
,
format1
,
format2
,
mainoffset
,
getdelta
)
1390
local
done
=
{
}
1391
for
i
=
1
,
#
sets
do
1392
local
offset
=
sets
[
i
]
1393
local
reused
=
done
[
offset
]
1394
if
not
reused
then
1395
offset
=
tableoffset
+
offset
1396
setposition
(
f
,
offset
)
1397
local
n
=
readushort
(
f
)
1398
reused
=
{
}
1399
for
i
=
1
,
n
do
1400
reused
[
i
]
=
{
1401
readushort
(
f
)
,
-- second glyph id
1402
readposition
(
f
,
format1
,
offset
,
getdelta
)
,
1403
readposition
(
f
,
format2
,
offset
,
getdelta
)
,
1404
}
1405
end
1406
done
[
offset
]
=
reused
1407
end
1408
sets
[
i
]
=
reused
1409
end
1410
return
sets
1411
end
1412 1413
local
function
readpairclasssets
(
f
,
nofclasses1
,
nofclasses2
,
format1
,
format2
,
mainoffset
,
getdelta
)
1414
local
classlist1
=
{
}
1415
for
i
=
1
,
nofclasses1
do
1416
local
classlist2
=
{
}
1417
classlist1
[
i
]
=
classlist2
1418
for
j
=
1
,
nofclasses2
do
1419
local
one
=
readposition
(
f
,
format1
,
mainoffset
,
getdelta
)
1420
local
two
=
readposition
(
f
,
format2
,
mainoffset
,
getdelta
)
1421
if
one
or
two
then
1422
classlist2
[
j
]
=
{
one
,
two
}
1423
else
1424
classlist2
[
j
]
=
false
1425
end
1426
end
1427
end
1428
return
classlist1
1429
end
1430 1431
-- no real gain in kerns as we pack
1432 1433
function
gposhandlers
.
single
(
f
,
fontdata
,
lookupid
,
lookupoffset
,
offset
,
glyphs
,
nofglyphs
)
1434
local
tableoffset
=
lookupoffset
+
offset
1435
setposition
(
f
,
tableoffset
)
1436
local
subtype
=
readushort
(
f
)
1437
local
getdelta
=
fontdata
.
temporary
.
getdelta
1438
if
subtype
=
=
1
then
1439
local
coverage
=
readushort
(
f
)
1440
local
format
=
readushort
(
f
)
1441
local
value
=
readposition
(
f
,
format
,
tableoffset
,
getdelta
)
1442
local
coverage
=
readcoverage
(
f
,
tableoffset
+
coverage
)
1443
for
index
,
newindex
in
next
,
coverage
do
1444
coverage
[
index
]
=
value
-- will be packed and shared anyway
1445
end
1446
return
{
1447
format
=
"
single
"
,
1448
coverage
=
coverage
,
1449
}
1450
elseif
subtype
=
=
2
then
1451
local
coverage
=
readushort
(
f
)
1452
local
format
=
readushort
(
f
)
1453
local
nofvalues
=
readushort
(
f
)
1454
local
values
=
{
}
1455
for
i
=
1
,
nofvalues
do
1456
values
[
i
]
=
readposition
(
f
,
format
,
tableoffset
,
getdelta
)
1457
end
1458
local
coverage
=
readcoverage
(
f
,
tableoffset
+
coverage
)
1459
for
index
,
newindex
in
next
,
coverage
do
1460
coverage
[
index
]
=
values
[
newindex
+
1
]
1461
end
1462
return
{
1463
format
=
"
single
"
,
1464
coverage
=
coverage
,
1465
}
1466
else
1467
report
(
"
unsupported subtype %a in %a positioning
"
,
subtype
,
"
single
"
)
1468
end
1469
end
1470 1471
-- this needs checking! if no second pair then another advance over the list
1472 1473
-- ValueFormat1 applies to the ValueRecord of the first glyph in each pair. ValueRecords for all first glyphs must use ValueFormat1. If ValueFormat1 is set to zero (0), the corresponding glyph has no ValueRecord and, therefore, should not be repositioned.
1474
-- ValueFormat2 applies to the ValueRecord of the second glyph in each pair. ValueRecords for all second glyphs must use ValueFormat2. If ValueFormat2 is set to null, then the second glyph of the pair is the “next” glyph for which a lookup should be performed.
1475 1476
-- local simple = {
1477
-- [true] = { [true] = { true, true }, [false] = { true } },
1478
-- [false] = { [true] = { false, true }, [false] = { false } },
1479
-- }
1480 1481
-- function gposhandlers.pair(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofglyphs)
1482
-- local tableoffset = lookupoffset + offset
1483
-- setposition(f,tableoffset)
1484
-- local subtype = readushort(f)
1485
-- local getdelta = fontdata.temporary.getdelta
1486
-- if subtype == 1 then
1487
-- local coverage = readushort(f)
1488
-- local format1 = readushort(f)
1489
-- local format2 = readushort(f)
1490
-- local sets = readarray(f)
1491
-- sets = readpairsets(f,tableoffset,sets,format1,format2,mainoffset,getdelta)
1492
-- coverage = readcoverage(f,tableoffset + coverage)
1493
-- local shared = { } -- partial sparse, when set also needs to be handled in the packer
1494
-- for index, newindex in next, coverage do
1495
-- local set = sets[newindex+1]
1496
-- local hash = { }
1497
-- for i=1,#set do
1498
-- local value = set[i]
1499
-- if value then
1500
-- local other = value[1]
1501
-- if shared then
1502
-- local s = shared[value]
1503
-- if s == nil then
1504
-- local first = value[2]
1505
-- local second = value[3]
1506
-- if first or second then
1507
-- s = { first, second or nil } -- needs checking
1508
-- else
1509
-- s = false
1510
-- end
1511
-- shared[value] = s
1512
-- end
1513
-- hash[other] = s or nil
1514
-- else
1515
-- local first = value[2]
1516
-- local second = value[3]
1517
-- if first or second then
1518
-- hash[other] = { first, second or nil } -- needs checking
1519
-- else
1520
-- hash[other] = nil -- what if set, maybe warning
1521
-- end
1522
-- end
1523
-- end
1524
-- end
1525
-- coverage[index] = hash
1526
-- end
1527
-- return {
1528
-- shared = shared and true or nil,
1529
-- format = "pair",
1530
-- coverage = coverage,
1531
-- }
1532
-- elseif subtype == 2 then
1533
-- local coverage = readushort(f)
1534
-- local format1 = readushort(f)
1535
-- local format2 = readushort(f)
1536
-- local classdef1 = readushort(f)
1537
-- local classdef2 = readushort(f)
1538
-- local nofclasses1 = readushort(f) -- incl class 0
1539
-- local nofclasses2 = readushort(f) -- incl class 0
1540
-- local classlist = readpairclasssets(f,nofclasses1,nofclasses2,format1,format2,tableoffset,getdelta)
1541
-- coverage = readcoverage(f,tableoffset+coverage)
1542
-- classdef1 = readclassdef(f,tableoffset+classdef1,coverage)
1543
-- classdef2 = readclassdef(f,tableoffset+classdef2,nofglyphs)
1544
-- local usedcoverage = { }
1545
-- local shared = { } -- partial sparse, when set also needs to be handled in the packer
1546
-- for g1, c1 in next, classdef1 do
1547
-- if coverage[g1] then
1548
-- local l1 = classlist[c1]
1549
-- if l1 then
1550
-- local hash = { }
1551
-- for paired, class in next, classdef2 do
1552
-- local offsets = l1[class]
1553
-- if offsets then
1554
-- local first = offsets[1]
1555
-- local second = offsets[2]
1556
-- if first or second then
1557
-- if shared then
1558
-- local s1 = shared[first]
1559
-- if s1 == nil then
1560
-- s1 = { }
1561
-- shared[first] = s1
1562
-- end
1563
-- local s2 = s1[second]
1564
-- if s2 == nil then
1565
-- s2 = { first, second or nil }
1566
-- s1[second] = s2
1567
-- end
1568
-- hash[paired] = s2
1569
-- else
1570
-- hash[paired] = { first, second or nil }
1571
-- end
1572
-- else
1573
-- -- upto the next lookup for this combination
1574
-- end
1575
-- end
1576
-- end
1577
-- usedcoverage[g1] = hash
1578
-- end
1579
-- end
1580
-- end
1581
-- return {
1582
-- shared = shared and true or nil,
1583
-- format = "pair",
1584
-- coverage = usedcoverage,
1585
-- }
1586
-- elseif subtype == 3 then
1587
-- report("yet unsupported subtype %a in %a positioning",subtype,"pair")
1588
-- else
1589
-- report("unsupported subtype %a in %a positioning",subtype,"pair")
1590
-- end
1591
-- end
1592 1593
function
gposhandlers
.
pair
(
f
,
fontdata
,
lookupid
,
lookupoffset
,
offset
,
glyphs
,
nofglyphs
)
1594
local
tableoffset
=
lookupoffset
+
offset
1595
setposition
(
f
,
tableoffset
)
1596
local
subtype
=
readushort
(
f
)
1597
local
getdelta
=
fontdata
.
temporary
.
getdelta
1598
if
subtype
=
=
1
then
1599
local
coverage
=
readushort
(
f
)
1600
local
format1
=
readushort
(
f
)
1601
local
format2
=
readushort
(
f
)
1602
local
sets
=
readarray
(
f
)
1603
sets
=
readpairsets
(
f
,
tableoffset
,
sets
,
format1
,
format2
,
mainoffset
,
getdelta
)
1604
coverage
=
readcoverage
(
f
,
tableoffset
+
coverage
)
1605
local
shared
=
{
}
-- partial sparse, when set also needs to be handled in the packer
1606
for
index
,
newindex
in
next
,
coverage
do
1607
local
set
=
sets
[
newindex
+
1
]
1608
local
hash
=
{
}
1609
for
i
=
1
,
#
set
do
1610
local
value
=
set
[
i
]
1611
if
value
then
1612
local
other
=
value
[
1
]
1613
local
share
=
shared
[
value
]
1614
if
share
=
=
nil
then
1615
local
first
=
value
[
2
]
1616
local
second
=
value
[
3
]
1617
if
first
or
second
then
1618
share
=
{
first
,
second
or
nil
}
-- needs checking
1619
else
1620
share
=
false
1621
end
1622
shared
[
value
]
=
share
1623
end
1624
hash
[
other
]
=
share
or
nil
-- really overload ?
1625
end
1626
end
1627
coverage
[
index
]
=
hash
1628
end
1629
return
{
1630
shared
=
shared
and
true
or
nil
,
1631
format
=
"
pair
"
,
1632
coverage
=
coverage
,
1633
}
1634
elseif
subtype
=
=
2
then
1635
local
coverage
=
readushort
(
f
)
1636
local
format1
=
readushort
(
f
)
1637
local
format2
=
readushort
(
f
)
1638
local
classdef1
=
readushort
(
f
)
1639
local
classdef2
=
readushort
(
f
)
1640
local
nofclasses1
=
readushort
(
f
)
-- incl class 0
1641
local
nofclasses2
=
readushort
(
f
)
-- incl class 0
1642
local
classlist
=
readpairclasssets
(
f
,
nofclasses1
,
nofclasses2
,
format1
,
format2
,
tableoffset
,
getdelta
)
1643
coverage
=
readcoverage
(
f
,
tableoffset
+
coverage
)
1644
classdef1
=
readclassdef
(
f
,
tableoffset
+
classdef1
,
coverage
)
1645
classdef2
=
readclassdef
(
f
,
tableoffset
+
classdef2
,
nofglyphs
)
1646
local
usedcoverage
=
{
}
1647
local
shared
=
{
}
-- partial sparse, when set also needs to be handled in the packer
1648
for
g1
,
c1
in
next
,
classdef1
do
1649
if
coverage
[
g1
]
then
1650
local
l1
=
classlist
[
c1
]
1651
if
l1
then
1652
local
hash
=
{
}
1653
for
paired
,
class
in
next
,
classdef2
do
1654
local
offsets
=
l1
[
class
]
1655
if
offsets
then
1656
local
first
=
offsets
[
1
]
1657
local
second
=
offsets
[
2
]
1658
if
first
or
second
then
1659
local
s1
=
shared
[
first
]
1660
if
s1
=
=
nil
then
1661
s1
=
{
}
1662
shared
[
first
]
=
s1
1663
end
1664
local
s2
=
s1
[
second
]
1665
if
s2
=
=
nil
then
1666
s2
=
{
first
,
second
or
nil
}
1667
s1
[
second
]
=
s2
1668
end
1669
hash
[
paired
]
=
s2
1670
end
1671
end
1672
end
1673
usedcoverage
[
g1
]
=
hash
1674
end
1675
end
1676
end
1677
return
{
1678
shared
=
shared
and
true
or
nil
,
1679
format
=
"
pair
"
,
1680
coverage
=
usedcoverage
,
1681
}
1682
elseif
subtype
=
=
3
then
1683
report
(
"
yet unsupported subtype %a in %a positioning
"
,
subtype
,
"
pair
"
)
1684
else
1685
report
(
"
unsupported subtype %a in %a positioning
"
,
subtype
,
"
pair
"
)
1686
end
1687
end
1688 1689
function
gposhandlers
.
cursive
(
f
,
fontdata
,
lookupid
,
lookupoffset
,
offset
,
glyphs
,
nofglyphs
)
1690
local
tableoffset
=
lookupoffset
+
offset
1691
setposition
(
f
,
tableoffset
)
1692
local
subtype
=
readushort
(
f
)
1693
local
getdelta
=
fontdata
.
temporary
.
getdelta
1694
if
subtype
=
=
1
then
1695
local
coverage
=
tableoffset
+
readushort
(
f
)
1696
local
nofrecords
=
readushort
(
f
)
1697
local
records
=
{
}
1698
for
i
=
1
,
nofrecords
do
1699
local
entry
=
readushort
(
f
)
1700
local
exit
=
readushort
(
f
)
1701
records
[
i
]
=
{
1702
-- entry = entry ~= 0 and (tableoffset + entry) or false,
1703
-- exit = exit ~= 0 and (tableoffset + exit ) or nil,
1704
entry
~
=
0
and
(
tableoffset
+
entry
)
or
false
,
1705
exit
~
=
0
and
(
tableoffset
+
exit
)
or
nil
,
1706
}
1707
end
1708
-- slot 1 will become hash after loading and it must be unique because we
1709
-- pack the tables (packed we turn the cc-* into a zero)
1710
local
cc
=
(
fontdata
.
temporary
.
cursivecount
or
0
)
+
1
1711
fontdata
.
temporary
.
cursivecount
=
cc
1712
cc
=
"
cc-
"
.
.
cc
1713
coverage
=
readcoverage
(
f
,
coverage
)
1714
for
i
=
1
,
nofrecords
do
1715
local
r
=
records
[
i
]
1716
records
[
i
]
=
{
1717
-- 1,
1718
cc
,
1719
-- readanchor(f,r.entry,getdelta) or false,
1720
-- readanchor(f,r.exit, getdelta) or nil,
1721
readanchor
(
f
,
r
[
1
]
,
getdelta
)
or
false
,
1722
readanchor
(
f
,
r
[
2
]
,
getdelta
)
or
nil
,
1723
}
1724
end
1725
for
index
,
newindex
in
next
,
coverage
do
1726
coverage
[
index
]
=
records
[
newindex
+
1
]
1727
end
1728
return
{
1729
coverage
=
coverage
,
1730
}
1731
else
1732
report
(
"
unsupported subtype %a in %a positioning
"
,
subtype
,
"
cursive
"
)
1733
end
1734
end
1735 1736
local
function
handlemark
(
f
,
fontdata
,
lookupid
,
lookupoffset
,
offset
,
glyphs
,
nofglyphs
,
ligature
)
1737
local
tableoffset
=
lookupoffset
+
offset
1738
setposition
(
f
,
tableoffset
)
1739
local
subtype
=
readushort
(
f
)
1740
local
getdelta
=
fontdata
.
temporary
.
getdelta
1741
if
subtype
=
=
1
then
1742
-- we are one based, not zero
1743
local
markcoverage
=
tableoffset
+
readushort
(
f
)
1744
local
basecoverage
=
tableoffset
+
readushort
(
f
)
1745
local
nofclasses
=
readushort
(
f
)
1746
local
markoffset
=
tableoffset
+
readushort
(
f
)
1747
local
baseoffset
=
tableoffset
+
readushort
(
f
)
1748
--
1749
local
markcoverage
=
readcoverage
(
f
,
markcoverage
)
1750
local
basecoverage
=
readcoverage
(
f
,
basecoverage
,
true
)
-- TO BE CHECKED: true
1751
--
1752
setposition
(
f
,
markoffset
)
1753
local
markclasses
=
{
}
1754
local
nofmarkclasses
=
readushort
(
f
)
1755
--
1756
local
lastanchor
=
fontdata
.
lastanchor
or
0
1757
local
usedanchors
=
{
}
1758
--
1759
for
i
=
1
,
nofmarkclasses
do
1760
local
class
=
readushort
(
f
)
+
1
1761
local
offset
=
readushort
(
f
)
1762
if
offset
=
=
0
then
1763
markclasses
[
i
]
=
false
1764
else
1765
markclasses
[
i
]
=
{
class
,
markoffset
+
offset
}
1766
end
1767
usedanchors
[
class
]
=
true
1768
end
1769
for
i
=
1
,
nofmarkclasses
do
1770
local
mc
=
markclasses
[
i
]
1771
if
mc
then
1772
mc
[
2
]
=
readanchor
(
f
,
mc
[
2
]
,
getdelta
)
1773
end
1774
end
1775
--
1776
setposition
(
f
,
baseoffset
)
1777
local
nofbaserecords
=
readushort
(
f
)
1778
local
baserecords
=
{
}
1779
--
1780
if
ligature
then
1781
-- 3 components
1782
-- 1 : class .. nofclasses -- NULL when empty
1783
-- 2 : class .. nofclasses -- NULL when empty
1784
-- 3 : class .. nofclasses -- NULL when empty
1785
for
i
=
1
,
nofbaserecords
do
-- here i is the class
1786
local
offset
=
readushort
(
f
)
1787
if
offset
=
=
0
then
1788
baserecords
[
i
]
=
false
1789
else
1790
baserecords
[
i
]
=
baseoffset
+
offset
1791
end
1792
end
1793
for
i
=
1
,
nofbaserecords
do
1794
local
recordoffset
=
baserecords
[
i
]
1795
if
recordoffset
then
1796
setposition
(
f
,
recordoffset
)
1797
local
nofcomponents
=
readushort
(
f
)
1798
local
components
=
{
}
1799
for
i
=
1
,
nofcomponents
do
1800
local
classes
=
{
}
1801
for
i
=
1
,
nofclasses
do
1802
local
offset
=
readushort
(
f
)
1803
if
offset
~
=
0
then
1804
classes
[
i
]
=
recordoffset
+
offset
1805
else
1806
classes
[
i
]
=
false
1807
end
1808
end
1809
components
[
i
]
=
classes
1810
end
1811
baserecords
[
i
]
=
components
1812
end
1813
end
1814
local
baseclasses
=
{
}
-- setmetatableindex("table")
1815
for
i
=
1
,
nofclasses
do
1816
baseclasses
[
i
]
=
{
}
1817
end
1818
for
i
=
1
,
nofbaserecords
do
1819
local
components
=
baserecords
[
i
]
1820
if
components
then
1821
local
b
=
basecoverage
[
i
]
1822
for
c
=
1
,
#
components
do
1823
local
classes
=
components
[
c
]
1824
if
classes
then
1825
for
i
=
1
,
nofclasses
do
1826
local
anchor
=
readanchor
(
f
,
classes
[
i
]
,
getdelta
)
1827
local
bclass
=
baseclasses
[
i
]
1828
local
bentry
=
bclass
[
b
]
1829
if
bentry
then
1830
bentry
[
c
]
=
anchor
1831
else
1832
bclass
[
b
]
=
{
[
c
]
=
anchor
}
1833
end
1834
end
1835
end
1836
end
1837
end
1838
end
1839
for
index
,
newindex
in
next
,
markcoverage
do
1840
markcoverage
[
index
]
=
markclasses
[
newindex
+
1
]
or
nil
1841
end
1842
return
{
1843
format
=
"
ligature
"
,
1844
baseclasses
=
baseclasses
,
1845
coverage
=
markcoverage
,
1846
}
1847
else
1848
for
i
=
1
,
nofbaserecords
do
1849
local
r
=
{
}
1850
for
j
=
1
,
nofclasses
do
1851
local
offset
=
readushort
(
f
)
1852
if
offset
=
=
0
then
1853
r
[
j
]
=
false
1854
else
1855
r
[
j
]
=
baseoffset
+
offset
1856
end
1857
end
1858
baserecords
[
i
]
=
r
1859
end
1860
local
baseclasses
=
{
}
-- setmetatableindex("table")
1861
for
i
=
1
,
nofclasses
do
1862
baseclasses
[
i
]
=
{
}
1863
end
1864
for
i
=
1
,
nofbaserecords
do
1865
local
r
=
baserecords
[
i
]
1866
local
b
=
basecoverage
[
i
]
1867
for
j
=
1
,
nofclasses
do
1868
baseclasses
[
j
]
[
b
]
=
readanchor
(
f
,
r
[
j
]
,
getdelta
)
1869
end
1870
end
1871
for
index
,
newindex
in
next
,
markcoverage
do
1872
markcoverage
[
index
]
=
markclasses
[
newindex
+
1
]
or
nil
1873
end
1874
-- we could actually already calculate the displacement if we want
1875
return
{
1876
format
=
"
base
"
,
1877
baseclasses
=
baseclasses
,
1878
coverage
=
markcoverage
,
1879
}
1880
end
1881
else
1882
report
(
"
unsupported subtype %a in
"
,
subtype
)
1883
end
1884 1885
end
1886 1887
function
gposhandlers
.
marktobase
(
f
,
fontdata
,
lookupid
,
lookupoffset
,
offset
,
glyphs
,
nofglyphs
)
1888
return
handlemark
(
f
,
fontdata
,
lookupid
,
lookupoffset
,
offset
,
glyphs
,
nofglyphs
)
1889
end
1890 1891
function
gposhandlers
.
marktoligature
(
f
,
fontdata
,
lookupid
,
lookupoffset
,
offset
,
glyphs
,
nofglyphs
)
1892
return
handlemark
(
f
,
fontdata
,
lookupid
,
lookupoffset
,
offset
,
glyphs
,
nofglyphs
,
true
)
1893
end
1894 1895
function
gposhandlers
.
marktomark
(
f
,
fontdata
,
lookupid
,
lookupoffset
,
offset
,
glyphs
,
nofglyphs
)
1896
return
handlemark
(
f
,
fontdata
,
lookupid
,
lookupoffset
,
offset
,
glyphs
,
nofglyphs
)
1897
end
1898 1899
function
gposhandlers
.
context
(
f
,
fontdata
,
lookupid
,
lookupoffset
,
offset
,
glyphs
,
nofglyphs
)
1900
return
unchainedcontext
(
f
,
fontdata
,
lookupid
,
lookupoffset
,
offset
,
glyphs
,
nofglyphs
,
"
positioning
"
)
,
"
context
"
1901
end
1902 1903
function
gposhandlers
.
chainedcontext
(
f
,
fontdata
,
lookupid
,
lookupoffset
,
offset
,
glyphs
,
nofglyphs
)
1904
return
chainedcontext
(
f
,
fontdata
,
lookupid
,
lookupoffset
,
offset
,
glyphs
,
nofglyphs
,
"
positioning
"
)
,
"
chainedcontext
"
1905
end
1906 1907
function
gposhandlers
.
extension
(
f
,
fontdata
,
lookupid
,
lookupoffset
,
offset
,
glyphs
,
nofglyphs
)
1908
return
extension
(
f
,
fontdata
,
lookupid
,
lookupoffset
,
offset
,
glyphs
,
nofglyphs
,
gpostypes
,
gposhandlers
,
"
positioning
"
)
1909
end
1910 1911
-- main loader
1912 1913
do
1914 1915
local
plugins
=
{
}
1916 1917
function
plugins
.
size
(
f
,
fontdata
,
tableoffset
,
feature
)
1918
if
fontdata
.
designsize
then
1919
-- yes, there are fonts with multiple size entries ... it probably relates
1920
-- to the other two fields (menu entries in some language)
1921
else
1922
local
function
check
(
offset
)
1923
setposition
(
f
,
offset
)
1924
local
designsize
=
readushort
(
f
)
1925
if
designsize
>
0
then
-- we could also have a threshold
1926
local
fontstyleid
=
readushort
(
f
)
1927
local
guimenuid
=
readushort
(
f
)
1928
local
minsize
=
readushort
(
f
)
1929
local
maxsize
=
readushort
(
f
)
1930
if
minsize
=
=
0
and
maxsize
=
=
0
and
fontstyleid
=
=
0
and
guimenuid
=
=
0
then
1931
minsize
=
designsize
1932
maxsize
=
designsize
1933
end
1934
if
designsize
>
=
minsize
and
designsize
<
=
maxsize
then
1935
return
minsize
,
maxsize
,
designsize
1936
end
1937
end
1938
end
1939
local
minsize
,
maxsize
,
designsize
=
check
(
tableoffset
+
feature
.
offset
+
feature
.
parameters
)
1940
if
not
designsize
then
1941
-- some old adobe fonts have: tableoffset+feature.parameters and we could
1942
-- use some heuristic but why bother ... this extra check will be removed
1943
-- some day and/or when we run into an issue
1944
minsize
,
maxsize
,
designsize
=
check
(
tableoffset
+
feature
.
parameters
)
1945
if
designsize
then
1946
report
(
"
bad size feature in %a, falling back to wrong offset
"
,
fontdata
.
filename
or
"
?
"
)
1947
else
1948
report
(
"
bad size feature in %a,
"
,
fontdata
.
filename
or
"
?
"
)
1949
end
1950
end
1951
if
designsize
then
1952
fontdata
.
minsize
=
minsize
1953
fontdata
.
maxsize
=
maxsize
1954
fontdata
.
designsize
=
designsize
1955
end
1956
end
1957
end
1958 1959
-- function plugins.rvrn(f,fontdata,tableoffset,feature)
1960
-- -- todo, at least a message
1961
-- end
1962 1963
-- feature order needs checking ... as we loop over a hash ... however, in the file
1964
-- they are sorted so order is not that relevant
1965 1966
local
function
reorderfeatures
(
fontdata
,
scripts
,
features
)
1967
local
scriptlangs
=
{
}
1968
local
featurehash
=
{
}
1969
local
featureorder
=
{
}
1970
for
script
,
languages
in
next
,
scripts
do
1971
for
language
,
record
in
next
,
languages
do
1972
local
hash
=
{
}
1973
local
list
=
record
.
featureindices
1974
for
k
=
1
,
#
list
do
1975
local
index
=
list
[
k
]
1976
local
feature
=
features
[
index
]
1977
local
lookups
=
feature
.
lookups
1978
local
tag
=
feature
.
tag
1979
if
tag
then
1980
hash
[
tag
]
=
true
1981
end
1982
if
lookups
then
1983
for
i
=
1
,
#
lookups
do
1984
local
lookup
=
lookups
[
i
]
1985
local
o
=
featureorder
[
lookup
]
1986
if
o
then
1987
local
okay
=
true
1988
for
i
=
1
,
#
o
do
1989
if
o
[
i
]
=
=
tag
then
1990
okay
=
false
1991
break
1992
end
1993
end
1994
if
okay
then
1995
o
[
#
o
+
1
]
=
tag
1996
end
1997
else
1998
featureorder
[
lookup
]
=
{
tag
}
1999
end
2000
local
f
=
featurehash
[
lookup
]
2001
if
f
then
2002
local
h
=
f
[
tag
]
2003
if
h
then
2004
local
s
=
h
[
script
]
2005
if
s
then
2006
s
[
language
]
=
true
2007
else
2008
h
[
script
]
=
{
[
language
]
=
true
}
2009
end
2010
else
2011
f
[
tag
]
=
{
[
script
]
=
{
[
language
]
=
true
}
}
2012
end
2013
else
2014
featurehash
[
lookup
]
=
{
[
tag
]
=
{
[
script
]
=
{
[
language
]
=
true
}
}
}
2015
end
2016
--
2017
local
h
=
scriptlangs
[
tag
]
2018
if
h
then
2019
local
s
=
h
[
script
]
2020
if
s
then
2021
s
[
language
]
=
true
2022
else
2023
h
[
script
]
=
{
[
language
]
=
true
}
2024
end
2025
else
2026
scriptlangs
[
tag
]
=
{
[
script
]
=
{
[
language
]
=
true
}
}
2027
end
2028
end
2029
end
2030
end
2031
end
2032
end
2033
return
scriptlangs
,
featurehash
,
featureorder
2034
end
2035 2036
local
function
readscriplan
(
f
,
fontdata
,
scriptoffset
)
2037
setposition
(
f
,
scriptoffset
)
2038
local
nofscripts
=
readushort
(
f
)
2039
local
scripts
=
{
}
2040
for
i
=
1
,
nofscripts
do
2041
scripts
[
readtag
(
f
)
]
=
scriptoffset
+
readushort
(
f
)
2042
end
2043
-- script list -> language system info
2044
local
languagesystems
=
setmetatableindex
(
"
table
"
)
2045
for
script
,
offset
in
next
,
scripts
do
2046
setposition
(
f
,
offset
)
2047
local
defaultoffset
=
readushort
(
f
)
2048
local
noflanguages
=
readushort
(
f
)
2049
local
languages
=
{
}
2050
if
defaultoffset
>
0
then
2051
languages
.
dflt
=
languagesystems
[
offset
+
defaultoffset
]
2052
end
2053
for
i
=
1
,
noflanguages
do
2054
local
language
=
readtag
(
f
)
2055
local
offset
=
offset
+
readushort
(
f
)
2056
languages
[
language
]
=
languagesystems
[
offset
]
2057
end
2058
scripts
[
script
]
=
languages
2059
end
2060
-- script list -> language system info -> feature list
2061
for
offset
,
usedfeatures
in
next
,
languagesystems
do
2062
if
offset
>
0
then
2063
setposition
(
f
,
offset
)
2064
local
featureindices
=
{
}
2065
usedfeatures
.
featureindices
=
featureindices
2066
usedfeatures
.
lookuporder
=
readushort
(
f
)
-- reserved, not used (yet)
2067
usedfeatures
.
requiredindex
=
readushort
(
f
)
-- relates to required (can be 0xFFFF)
2068
local
noffeatures
=
readushort
(
f
)
2069
for
i
=
1
,
noffeatures
do
2070
featureindices
[
i
]
=
readushort
(
f
)
+
1
2071
end
2072
end
2073
end
2074
return
scripts
2075
end
2076 2077
local
function
readfeatures
(
f
,
fontdata
,
featureoffset
)
2078
setposition
(
f
,
featureoffset
)
2079
local
features
=
{
}
2080
local
noffeatures
=
readushort
(
f
)
2081
for
i
=
1
,
noffeatures
do
2082
-- also shared?
2083
features
[
i
]
=
{
2084
tag
=
readtag
(
f
)
,
2085
offset
=
readushort
(
f
)
2086
}
2087
end
2088
--
2089
for
i
=
1
,
noffeatures
do
2090
local
feature
=
features
[
i
]
2091
local
offset
=
featureoffset
+
feature
.
offset
2092
setposition
(
f
,
offset
)
2093
local
parameters
=
readushort
(
f
)
-- feature.parameters
2094
local
noflookups
=
readushort
(
f
)
2095
if
noflookups
>
0
then
2096
-- local lookups = { }
2097
-- feature.lookups = lookups
2098
-- for j=1,noflookups do
2099
-- lookups[j] = readushort(f) + 1
2100
-- end
2101
local
lookups
=
readcardinaltable
(
f
,
noflookups
,
ushort
)
2102
feature
.
lookups
=
lookups
2103
for
j
=
1
,
noflookups
do
2104
lookups
[
j
]
=
lookups
[
j
]
+
1
2105
end
2106
end
2107
if
parameters
>
0
then
2108
feature
.
parameters
=
parameters
2109
local
plugin
=
plugins
[
feature
.
tag
]
2110
if
plugin
then
2111
plugin
(
f
,
fontdata
,
featureoffset
,
feature
)
2112
end
2113
end
2114
end
2115
return
features
2116
end
2117 2118
local
function
readlookups
(
f
,
lookupoffset
,
lookuptypes
,
featurehash
,
featureorder
)
2119
setposition
(
f
,
lookupoffset
)
2120
local
noflookups
=
readushort
(
f
)
2121
local
lookups
=
readcardinaltable
(
f
,
noflookups
,
ushort
)
2122
for
lookupid
=
1
,
noflookups
do
2123
local
offset
=
lookups
[
lookupid
]
2124
setposition
(
f
,
lookupoffset
+
offset
)
2125
local
subtables
=
{
}
2126
local
typebits
=
readushort
(
f
)
2127
local
flagbits
=
readushort
(
f
)
2128
local
lookuptype
=
lookuptypes
[
typebits
]
2129
local
lookupflags
=
lookupflags
[
flagbits
]
2130
local
nofsubtables
=
readushort
(
f
)
2131
for
j
=
1
,
nofsubtables
do
2132
subtables
[
j
]
=
offset
+
readushort
(
f
)
-- we can probably put lookupoffset here
2133
end
2134
-- which one wins?
2135
local
markclass
=
band
(
flagbits
,
0x0010
)
~
=
0
-- usemarkfilteringset
2136
if
markclass
then
2137
markclass
=
readushort
(
f
)
-- + 1
2138
end
2139
local
markset
=
rshift
(
flagbits
,
8
)
2140
if
markset
>
0
then
2141
markclass
=
markset
-- + 1
2142
end
2143
lookups
[
lookupid
]
=
{
2144
type
=
lookuptype
,
2145
-- chain = chaindirections[lookuptype] or nil,
2146
flags
=
lookupflags
,
2147
name
=
lookupid
,
2148
subtables
=
subtables
,
2149
markclass
=
markclass
,
2150
features
=
featurehash
[
lookupid
]
,
-- not if extension
2151
order
=
featureorder
[
lookupid
]
,
2152
}
2153
end
2154
return
lookups
2155
end
2156 2157
local
f_lookupname
=
formatters
[
"
%s_%s_%s
"
]
2158 2159
local
function
resolvelookups
(
f
,
lookupoffset
,
fontdata
,
lookups
,
lookuptypes
,
lookuphandlers
,
what
,
tableoffset
)
2160 2161
local
sequences
=
fontdata
.
sequences
or
{
}
2162
local
sublookuplist
=
fontdata
.
sublookups
or
{
}
2163
fontdata
.
sequences
=
sequences
2164
fontdata
.
sublookups
=
sublookuplist
2165
local
nofsublookups
=
#
sublookuplist
2166
local
nofsequences
=
#
sequences
-- 0
2167
local
lastsublookup
=
nofsublookups
2168
local
lastsequence
=
nofsequences
2169
local
lookupnames
=
lookupnames
[
what
]
2170
local
sublookuphash
=
{
}
2171
local
sublookupcheck
=
{
}
2172
local
glyphs
=
fontdata
.
glyphs
2173
local
nofglyphs
=
fontdata
.
nofglyphs
or
#
glyphs
2174
local
noflookups
=
#
lookups
2175
local
lookupprefix
=
sub
(
what
,
2
,
2
)
-- g[s|p][ub|os]
2176
--
2177
local
usedlookups
=
false
-- setmetatableindex("number")
2178
--
2179
for
lookupid
=
1
,
noflookups
do
2180
local
lookup
=
lookups
[
lookupid
]
2181
local
lookuptype
=
lookup
.
type
2182
local
subtables
=
lookup
.
subtables
2183
local
features
=
lookup
.
features
2184
local
handler
=
lookuphandlers
[
lookuptype
]
2185
if
handler
then
2186
local
nofsubtables
=
#
subtables
2187
local
order
=
lookup
.
order
2188
local
flags
=
lookup
.
flags
2189
-- this is expected in the font handler (faster checking)
2190
if
flags
[
1
]
then
flags
[
1
]
=
"
mark
"
end
2191
if
flags
[
2
]
then
flags
[
2
]
=
"
ligature
"
end
2192
if
flags
[
3
]
then
flags
[
3
]
=
"
base
"
end
2193
--
2194
local
markclass
=
lookup
.
markclass
2195
-- local chain = lookup.chain
2196
if
nofsubtables
>
0
then
2197
local
steps
=
{
}
2198
local
nofsteps
=
0
2199
local
oldtype
=
nil
2200
for
s
=
1
,
nofsubtables
do
2201
local
step
,
lt
=
handler
(
f
,
fontdata
,
lookupid
,
lookupoffset
,
subtables
[
s
]
,
glyphs
,
nofglyphs
)
2202
if
lt
then
2203
lookuptype
=
lt
2204
if
oldtype
and
lt
~
=
oldtype
then
2205
report
(
"
messy %s lookup type %a and %a
"
,
what
,
lookuptype
,
oldtype
)
2206
end
2207
oldtype
=
lookuptype
2208
end
2209
if
not
step
then
2210
report
(
"
unsupported %s lookup type %a
"
,
what
,
lookuptype
)
2211
else
2212
nofsteps
=
nofsteps
+
1
2213
steps
[
nofsteps
]
=
step
2214
local
rules
=
step
.
rules
2215
if
rules
then
2216
for
i
=
1
,
#
rules
do
2217
local
rule
=
rules
[
i
]
2218
local
before
=
rule
.
before
2219
local
current
=
rule
.
current
2220
local
after
=
rule
.
after
2221
local
replacements
=
rule
.
replacements
2222
if
before
then
2223
for
i
=
1
,
#
before
do
2224
before
[
i
]
=
tohash
(
before
[
i
]
)
2225
end
2226
-- as with original ctx ff loader
2227
rule
.
before
=
reversed
(
before
)
2228
end
2229
if
current
then
2230
if
replacements
then
2231
-- We have a reverse lookup and therefore only one current entry. We might need
2232
-- to reverse the order in the before and after lists so that needs checking.
2233
local
first
=
current
[
1
]
2234
local
hash
=
{
}
2235
local
repl
=
{
}
2236
for
i
=
1
,
#
first
do
2237
local
c
=
first
[
i
]
2238
hash
[
c
]
=
true
2239
repl
[
c
]
=
replacements
[
i
]
2240
end
2241
rule
.
current
=
{
hash
}
2242
rule
.
replacements
=
repl
2243
else
2244
for
i
=
1
,
#
current
do
2245
current
[
i
]
=
tohash
(
current
[
i
]
)
2246
end
2247
end
2248
else
2249
-- weird lookup
2250
end
2251
if
after
then
2252
for
i
=
1
,
#
after
do
2253
after
[
i
]
=
tohash
(
after
[
i
]
)
2254
end
2255
end
2256
if
usedlookups
then
2257
local
lookups
=
rule
.
lookups
2258
if
lookups
then
2259
for
k
,
v
in
next
,
lookups
do
2260
if
v
then
2261
for
k
,
v
in
next
,
v
do
2262
usedlookups
[
v
]
=
usedlookups
[
v
]
+
1
2263
end
2264
end
2265
end
2266
end
2267
end
2268
end
2269
end
2270
end
2271
end
2272
if
nofsteps
~
=
nofsubtables
then
2273
report
(
"
bogus subtables removed in %s lookup type %a
"
,
what
,
lookuptype
)
2274
end
2275
lookuptype
=
lookupnames
[
lookuptype
]
or
lookuptype
2276
if
features
then
2277
nofsequences
=
nofsequences
+
1
2278
-- report("registering %i as sequence step %i",lookupid,nofsequences)
2279
local
l
=
{
2280
index
=
nofsequences
,
2281
name
=
f_lookupname
(
lookupprefix
,
"
s
"
,
lookupid
+
lookupidoffset
)
,
2282
steps
=
steps
,
2283
nofsteps
=
nofsteps
,
2284
type
=
lookuptype
,
2285
markclass
=
markclass
or
nil
,
2286
flags
=
flags
,
2287
-- chain = chain,
2288
order
=
order
,
2289
features
=
features
,
2290
}
2291
sequences
[
nofsequences
]
=
l
2292
lookup
.
done
=
l
2293
else
2294
nofsublookups
=
nofsublookups
+
1
2295
-- report("registering %i as sublookup %i",lookupid,nofsublookups)
2296
local
l
=
{
2297
index
=
nofsublookups
,
2298
name
=
f_lookupname
(
lookupprefix
,
"
l
"
,
lookupid
+
lookupidoffset
)
,
2299
steps
=
steps
,
2300
nofsteps
=
nofsteps
,
2301
type
=
lookuptype
,
2302
markclass
=
markclass
or
nil
,
2303
flags
=
flags
,
2304
-- chain = chain,
2305
}
2306
sublookuplist
[
nofsublookups
]
=
l
2307
sublookuphash
[
lookupid
]
=
nofsublookups
2308
sublookupcheck
[
lookupid
]
=
0
2309
lookup
.
done
=
l
2310
end
2311
else
2312
report
(
"
no subtables for lookup %a
"
,
lookupid
)
2313
end
2314
else
2315
report
(
"
no handler for lookup %a with type %a
"
,
lookupid
,
lookuptype
)
2316
end
2317
end
2318 2319
if
usedlookups
then
2320
report
(
"
used %s lookups: % t
"
,
what
,
sortedkeys
(
usedlookups
)
)
2321
end
2322 2323
-- When we have a context, we have sublookups that resolve into lookups for which we need to
2324
-- know the type. We split the main lookuptable in two parts: sequences (the main lookups)
2325
-- and subtable lookups (simple specs with no features). We could keep them merged and might do
2326
-- that once we only use this loader. Then we can also move the simple specs into the sequence.
2327
-- After all, we pack afterwards.
2328 2329
local
reported
=
{
}
2330 2331
local
function
report_issue
(
i
,
what
,
sequence
,
kind
)
2332
local
name
=
sequence
.
name
2333
if
not
reported
[
name
]
then
2334
report
(
"
rule %i in %s lookup %a has %s lookups
"
,
i
,
what
,
name
,
kind
)
2335
reported
[
name
]
=
true
2336
end
2337
end
2338 2339
for
i
=
lastsequence
+
1
,
nofsequences
do
2340
local
sequence
=
sequences
[
i
]
2341
local
steps
=
sequence
.
steps
2342
for
i
=
1
,
#
steps
do
2343
local
step
=
steps
[
i
]
2344
local
rules
=
step
.
rules
2345
if
rules
then
2346
for
i
=
1
,
#
rules
do
2347
local
rule
=
rules
[
i
]
2348
local
rlookups
=
rule
.
lookups
2349
if
not
rlookups
then
2350
report_issue
(
i
,
what
,
sequence
,
"
no
"
)
2351
elseif
not
next
(
rlookups
)
then
2352
-- can be ok as it aborts a chain sequence
2353
-- report_issue(i,what,sequence,"empty")
2354
rule
.
lookups
=
nil
2355
else
2356
-- we can have holes in rlookups flagged false and we can have multiple lookups
2357
-- applied (first time seen in seguemj)
2358
local
length
=
#
rlookups
2359
for
index
=
1
,
length
do
2360
local
lookuplist
=
rlookups
[
index
]
2361
if
lookuplist
then
2362
local
length
=
#
lookuplist
2363
local
found
=
{
}
2364
local
noffound
=
0
2365
for
index
=
1
,
length
do
2366
local
lookupid
=
lookuplist
[
index
]
2367
if
lookupid
then
2368
local
h
=
sublookuphash
[
lookupid
]
2369
if
not
h
then
2370
-- here we have a lookup that is used independent as well
2371
-- as in another one
2372
local
lookup
=
lookups
[
lookupid
]
2373
if
lookup
then
2374
local
d
=
lookup
.
done
2375
if
d
then
2376
nofsublookups
=
nofsublookups
+
1
2377
-- report("registering %i as sublookup %i",lookupid,nofsublookups)
2378
local
l
=
{
2379
index
=
nofsublookups
,
-- handy for tracing
2380
name
=
f_lookupname
(
lookupprefix
,
"
d
"
,
lookupid
+
lookupidoffset
)
,
2381
derived
=
true
,
-- handy for tracing
2382
steps
=
d
.
steps
,
2383
nofsteps
=
d
.
nofsteps
,
2384
type
=
d
.
lookuptype
or
"
gsub_single
"
,
-- todo: check type
2385
markclass
=
d
.
markclass
or
nil
,
2386
flags
=
d
.
flags
,
2387
-- chain = d.chain,
2388
}
2389
sublookuplist
[
nofsublookups
]
=
copy
(
l
)
-- we repack later
2390
sublookuphash
[
lookupid
]
=
nofsublookups
2391
sublookupcheck
[
lookupid
]
=
1
2392
h
=
nofsublookups
2393
else
2394
report_issue
(
i
,
what
,
sequence
,
"
missing
"
)
2395
rule
.
lookups
=
nil
2396
break
2397
end
2398
else
2399
report_issue
(
i
,
what
,
sequence
,
"
bad
"
)
2400
rule
.
lookups
=
nil
2401
break
2402
end
2403
else
2404
sublookupcheck
[
lookupid
]
=
sublookupcheck
[
lookupid
]
+
1
2405
end
2406
if
h
then
2407
noffound
=
noffound
+
1
2408
found
[
noffound
]
=
h
2409
end
2410
end
2411
end
2412
rlookups
[
index
]
=
noffound
>
0
and
found
or
false
2413
else
2414
rlookups
[
index
]
=
false
2415
end
2416
end
2417
end
2418
end
2419
end
2420
end
2421
end
2422 2423
for
i
,
n
in
sortedhash
(
sublookupcheck
)
do
2424
local
l
=
lookups
[
i
]
2425
local
t
=
l
.
type
2426
if
n
=
=
0
and
t
~
=
"
extension
"
then
2427
local
d
=
l
.
done
2428
report
(
"
%s lookup %s of type %a is not used
"
,
what
,
d
and
d
.
name
or
l
.
name
,
t
)
2429
end
2430
end
2431 2432
end
2433 2434
local
function
loadvariations
(
f
,
fontdata
,
variationsoffset
,
lookuptypes
,
featurehash
,
featureorder
)
2435
setposition
(
f
,
variationsoffset
)
2436
local
version
=
readulong
(
f
)
-- two times readushort
2437
local
nofrecords
=
readulong
(
f
)
2438
local
records
=
{
}
2439
for
i
=
1
,
nofrecords
do
2440
records
[
i
]
=
{
2441
conditions
=
readulong
(
f
)
,
2442
substitutions
=
readulong
(
f
)
,
2443
}
2444
end
2445
for
i
=
1
,
nofrecords
do
2446
local
record
=
records
[
i
]
2447
local
offset
=
record
.
conditions
2448
if
offset
=
=
0
then
2449
record
.
condition
=
nil
2450
record
.
matchtype
=
"
always
"
2451
else
2452
local
offset
=
variationsoffset
+
offset
2453
setposition
(
f
,
offset
)
2454
local
nofconditions
=
readushort
(
f
)
2455
local
conditions
=
{
}
2456
for
i
=
1
,
nofconditions
do
2457
conditions
[
i
]
=
offset
+
readulong
(
f
)
2458
end
2459
record
.
conditions
=
conditions
2460
record
.
matchtype
=
"
condition
"
2461
end
2462
end
2463
for
i
=
1
,
nofrecords
do
2464
local
record
=
records
[
i
]
2465
if
record
.
matchtype
=
=
"
condition
"
then
2466
local
conditions
=
record
.
conditions
2467
for
i
=
1
,
#
conditions
do
2468
setposition
(
f
,
conditions
[
i
]
)
2469
conditions
[
i
]
=
{
2470
format
=
readushort
(
f
)
,
2471
axis
=
readushort
(
f
)
,
2472
minvalue
=
read2dot14
(
f
)
,
2473
maxvalue
=
read2dot14
(
f
)
,
2474
}
2475
end
2476
end
2477
end
2478 2479
for
i
=
1
,
nofrecords
do
2480
local
record
=
records
[
i
]
2481
local
offset
=
record
.
substitutions
2482
if
offset
=
=
0
then
2483
record
.
substitutions
=
{
}
2484
else
2485
setposition
(
f
,
variationsoffset
+
offset
)
2486
local
version
=
readulong
(
f
)
2487
local
nofsubstitutions
=
readushort
(
f
)
2488
local
substitutions
=
{
}
2489
for
i
=
1
,
nofsubstitutions
do
2490
substitutions
[
readushort
(
f
)
]
=
readulong
(
f
)
2491
end
2492
for
index
,
alternates
in
sortedhash
(
substitutions
)
do
2493
if
index
=
=
0
then
2494
record
.
substitutions
=
false
2495
else
2496
local
tableoffset
=
variationsoffset
+
offset
+
alternates
2497
setposition
(
f
,
tableoffset
)
2498
local
parameters
=
readulong
(
f
)
-- feature parameters
2499
local
noflookups
=
readushort
(
f
)
2500
local
lookups
=
readcardinaltable
(
f
,
noflookups
,
ushort
)
-- not sure what to do with these
2501
-- todo : resolve to proper lookups
2502
record
.
substitutions
=
lookups
2503
end
2504
end
2505
end
2506
end
2507
setvariabledata
(
fontdata
,
"
features
"
,
records
)
2508
end
2509 2510
local
function
readscripts
(
f
,
fontdata
,
what
,
lookuptypes
,
lookuphandlers
,
lookupstoo
)
2511
local
tableoffset
=
gotodatatable
(
f
,
fontdata
,
what
,
true
)
2512
if
tableoffset
then
2513
local
version
=
readulong
(
f
)
2514
local
scriptoffset
=
tableoffset
+
readushort
(
f
)
2515
local
featureoffset
=
tableoffset
+
readushort
(
f
)
2516
local
lookupoffset
=
tableoffset
+
readushort
(
f
)
2517
local
variationsoffset
=
version
>
0x00010000
and
(
tableoffset
+
readulong
(
f
)
)
or
0
2518
if
not
scriptoffset
then
2519
return
2520
end
2521
local
scripts
=
readscriplan
(
f
,
fontdata
,
scriptoffset
)
2522
local
features
=
readfeatures
(
f
,
fontdata
,
featureoffset
)
2523
--
2524
local
scriptlangs
,
featurehash
,
featureorder
=
reorderfeatures
(
fontdata
,
scripts
,
features
)
2525
--
2526
if
fontdata
.
features
then
2527
fontdata
.
features
[
what
]
=
scriptlangs
2528
else
2529
fontdata
.
features
=
{
[
what
]
=
scriptlangs
}
2530
end
2531
--
2532
if
not
lookupstoo
then
2533
return
2534
end
2535
--
2536
local
lookups
=
readlookups
(
f
,
lookupoffset
,
lookuptypes
,
featurehash
,
featureorder
)
2537
--
2538
if
lookups
then
2539
resolvelookups
(
f
,
lookupoffset
,
fontdata
,
lookups
,
lookuptypes
,
lookuphandlers
,
what
,
tableoffset
)
2540
end
2541
--
2542
if
variationsoffset
>
0
then
2543
loadvariations
(
f
,
fontdata
,
variationsoffset
,
lookuptypes
,
featurehash
,
featureorder
)
2544
end
2545
end
2546
end
2547 2548
local
function
checkkerns
(
f
,
fontdata
,
specification
)
2549
local
datatable
=
fontdata
.
tables
.
kern
2550
if
not
datatable
then
2551
return
-- no kerns
2552
end
2553
local
features
=
fontdata
.
features
2554
local
gposfeatures
=
features
and
features
.
gpos
2555
local
name
2556
if
not
gposfeatures
or
not
gposfeatures
.
kern
then
2557
name
=
"
kern
"
2558
elseif
specification
.
globalkerns
then
2559
name
=
"
globalkern
"
2560
else
2561
report
(
"
ignoring global kern table, using gpos kern feature
"
)
2562
return
2563
end
2564
setposition
(
f
,
datatable
.
offset
)
2565
local
version
=
readushort
(
f
)
2566
local
noftables
=
readushort
(
f
)
2567
if
noftables
>
1
then
2568
report
(
"
adding global kern table as gpos feature %a
"
,
name
)
2569
local
kerns
=
setmetatableindex
(
"
table
"
)
2570
for
i
=
1
,
noftables
do
2571
local
version
=
readushort
(
f
)
2572
local
length
=
readushort
(
f
)
2573
local
coverage
=
readushort
(
f
)
2574
-- bit 8-15 of coverage: format 0 or 2
2575
local
format
=
rshift
(
coverage
,
8
)
-- is this ok
2576
if
format
=
=
0
then
2577
local
nofpairs
=
readushort
(
f
)
2578
local
searchrange
=
readushort
(
f
)
2579
local
entryselector
=
readushort
(
f
)
2580
local
rangeshift
=
readushort
(
f
)
2581
for
i
=
1
,
nofpairs
do
2582
kerns
[
readushort
(
f
)
]
[
readushort
(
f
)
]
=
readfword
(
f
)
2583
end
2584
elseif
format
=
=
2
then
2585
-- apple specific so let's ignore it
2586
else
2587
-- not supported by ms
2588
end
2589
end
2590
local
feature
=
{
dflt
=
{
dflt
=
true
}
}
2591
if
not
features
then
2592
fontdata
.
features
=
{
gpos
=
{
[
name
]
=
feature
}
}
2593
elseif
not
gposfeatures
then
2594
fontdata
.
features
.
gpos
=
{
[
name
]
=
feature
}
2595
else
2596
gposfeatures
[
name
]
=
feature
2597
end
2598
local
sequences
=
fontdata
.
sequences
2599
if
not
sequences
then
2600
sequences
=
{
}
2601
fontdata
.
sequences
=
sequences
2602
end
2603
local
nofsequences
=
#
sequences
+
1
2604
sequences
[
nofsequences
]
=
{
2605
index
=
nofsequences
,
2606
name
=
name
,
2607
steps
=
{
2608
{
2609
coverage
=
kerns
,
2610
format
=
"
kern
"
,
2611
}
,
2612
}
,
2613
nofsteps
=
1
,
2614
type
=
"
gpos_pair
"
,
2615
flags
=
{
false
,
false
,
false
,
false
}
,
2616
order
=
{
name
}
,
2617
features
=
{
[
name
]
=
feature
}
,
2618
}
2619
else
2620
report
(
"
ignoring empty kern table of feature %a
"
,
name
)
2621
end
2622
end
2623 2624
function
readers
.
gsub
(
f
,
fontdata
,
specification
)
2625
if
specification
.
details
then
2626
readscripts
(
f
,
fontdata
,
"
gsub
"
,
gsubtypes
,
gsubhandlers
,
specification
.
lookups
)
2627
end
2628
end
2629 2630
function
readers
.
gpos
(
f
,
fontdata
,
specification
)
2631
if
specification
.
details
then
2632
readscripts
(
f
,
fontdata
,
"
gpos
"
,
gpostypes
,
gposhandlers
,
specification
.
lookups
)
2633
if
specification
.
lookups
then
2634
checkkerns
(
f
,
fontdata
,
specification
)
2635
end
2636
end
2637
end
2638 2639
end
2640 2641
function
readers
.
gdef
(
f
,
fontdata
,
specification
)
2642
if
not
specification
.
glyphs
then
2643
return
2644
end
2645
local
datatable
=
fontdata
.
tables
.
gdef
2646
if
datatable
then
2647
local
tableoffset
=
datatable
.
offset
2648
setposition
(
f
,
tableoffset
)
2649
local
version
=
readulong
(
f
)
2650
local
classoffset
=
readushort
(
f
)
2651
local
attachmentoffset
=
readushort
(
f
)
-- used for bitmaps
2652
local
ligaturecarets
=
readushort
(
f
)
-- used in editors (maybe nice for tracing)
2653
local
markclassoffset
=
readushort
(
f
)
2654
local
marksetsoffset
=
version
>
=
0x00010002
and
readushort
(
f
)
or
0
2655
local
varsetsoffset
=
version
>
=
0x00010003
and
readulong
(
f
)
or
0
2656
local
glyphs
=
fontdata
.
glyphs
2657
local
marks
=
{
}
2658
local
markclasses
=
setmetatableindex
(
"
table
"
)
2659
local
marksets
=
setmetatableindex
(
"
table
"
)
2660
fontdata
.
marks
=
marks
2661
fontdata
.
markclasses
=
markclasses
2662
fontdata
.
marksets
=
marksets
2663
-- class definitions
2664
if
classoffset
~
=
0
then
2665
setposition
(
f
,
tableoffset
+
classoffset
)
2666
local
classformat
=
readushort
(
f
)
2667
if
classformat
=
=
1
then
2668
local
firstindex
=
readushort
(
f
)
2669
local
lastindex
=
firstindex
+
readushort
(
f
)
-
1
2670
for
index
=
firstindex
,
lastindex
do
2671
local
class
=
classes
[
readushort
(
f
)
]
2672
if
class
=
=
"
mark
"
then
2673
marks
[
index
]
=
true
2674
end
2675
glyphs
[
index
]
.
class
=
class
2676
end
2677
elseif
classformat
=
=
2
then
2678
local
nofranges
=
readushort
(
f
)
2679
for
i
=
1
,
nofranges
do
2680
local
firstindex
=
readushort
(
f
)
2681
local
lastindex
=
readushort
(
f
)
2682
local
class
=
classes
[
readushort
(
f
)
]
2683
if
class
then
2684
for
index
=
firstindex
,
lastindex
do
2685
glyphs
[
index
]
.
class
=
class
2686
if
class
=
=
"
mark
"
then
2687
marks
[
index
]
=
true
2688
end
2689
end
2690
end
2691
end
2692
end
2693
end
2694
-- mark classes
2695
if
markclassoffset
~
=
0
then
2696
setposition
(
f
,
tableoffset
+
markclassoffset
)
2697
local
classformat
=
readushort
(
f
)
2698
if
classformat
=
=
1
then
2699
local
firstindex
=
readushort
(
f
)
2700
local
lastindex
=
firstindex
+
readushort
(
f
)
-
1
2701
for
index
=
firstindex
,
lastindex
do
2702
markclasses
[
readushort
(
f
)
]
[
index
]
=
true
2703
end
2704
elseif
classformat
=
=
2
then
2705
local
nofranges
=
readushort
(
f
)
2706
for
i
=
1
,
nofranges
do
2707
local
firstindex
=
readushort
(
f
)
2708
local
lastindex
=
readushort
(
f
)
2709
local
class
=
markclasses
[
readushort
(
f
)
]
2710
for
index
=
firstindex
,
lastindex
do
2711
class
[
index
]
=
true
2712
end
2713
end
2714
end
2715
end
2716
-- mark sets : todo: just make the same as class sets above
2717
if
marksetsoffset
~
=
0
then
2718
marksetsoffset
=
tableoffset
+
marksetsoffset
2719
setposition
(
f
,
marksetsoffset
)
2720
local
format
=
readushort
(
f
)
2721
if
format
=
=
1
then
2722
local
nofsets
=
readushort
(
f
)
2723
local
sets
=
readcardinaltable
(
f
,
nofsets
,
ulong
)
2724
for
i
=
1
,
nofsets
do
2725
local
offset
=
sets
[
i
]
2726
if
offset
~
=
0
then
2727
marksets
[
i
]
=
readcoverage
(
f
,
marksetsoffset
+
offset
)
2728
end
2729
end
2730
end
2731
end
2732 2733
local
factors
=
specification
.
factors
2734 2735
if
(
specification
.
variable
or
factors
)
and
varsetsoffset
~
=
0
then
2736 2737
local
regions
,
deltas
=
readvariationdata
(
f
,
tableoffset
+
varsetsoffset
,
factors
)
2738 2739
-- setvariabledata(fontdata,"gregions",regions)
2740 2741
if
factors
then
2742
fontdata
.
temporary
.
getdelta
=
function
(
outer
,
inner
)
2743
local
delta
=
deltas
[
outer
+
1
]
2744
if
delta
then
2745
local
d
=
delta
.
deltas
[
inner
+
1
]
2746
if
d
then
2747
local
scales
=
delta
.
scales
2748
local
dd
=
0
2749
for
i
=
1
,
#
scales
do
2750
local
di
=
d
[
i
]
2751
if
di
then
2752
dd
=
dd
+
scales
[
i
]
*
di
2753
else
2754
break
2755
end
2756
end
2757
return
round
(
dd
)
2758
end
2759
end
2760
return
0
2761
end
2762
end
2763 2764
end
2765
end
2766
end
2767 2768
-- We keep this code here instead of font-otm.lua because we need coverage
2769
-- helpers. Okay, these helpers could go to the main reader file some day.
2770 2771
local
function
readmathvalue
(
f
)
2772
local
v
=
readshort
(
f
)
2773
skipshort
(
f
,
1
)
-- offset to device table
2774
return
v
2775
end
2776 2777
local
function
readmathconstants
(
f
,
fontdata
,
offset
)
2778
setposition
(
f
,
offset
)
2779
fontdata
.
mathconstants
=
{
2780
ScriptPercentScaleDown
=
readshort
(
f
)
,
2781
ScriptScriptPercentScaleDown
=
readshort
(
f
)
,
2782
DelimitedSubFormulaMinHeight
=
readushort
(
f
)
,
2783
DisplayOperatorMinHeight
=
readushort
(
f
)
,
2784
MathLeading
=
readmathvalue
(
f
)
,
2785
AxisHeight
=
readmathvalue
(
f
)
,
2786
AccentBaseHeight
=
readmathvalue
(
f
)
,
2787
FlattenedAccentBaseHeight
=
readmathvalue
(
f
)
,
2788
SubscriptShiftDown
=
readmathvalue
(
f
)
,
2789
SubscriptTopMax
=
readmathvalue
(
f
)
,
2790
SubscriptBaselineDropMin
=
readmathvalue
(
f
)
,
2791
SuperscriptShiftUp
=
readmathvalue
(
f
)
,
2792
SuperscriptShiftUpCramped
=
readmathvalue
(
f
)
,
2793
SuperscriptBottomMin
=
readmathvalue
(
f
)
,
2794
SuperscriptBaselineDropMax
=
readmathvalue
(
f
)
,
2795
SubSuperscriptGapMin
=
readmathvalue
(
f
)
,
2796
SuperscriptBottomMaxWithSubscript
=
readmathvalue
(
f
)
,
2797
SpaceAfterScript
=
readmathvalue
(
f
)
,
2798
UpperLimitGapMin
=
readmathvalue
(
f
)
,
2799
UpperLimitBaselineRiseMin
=
readmathvalue
(
f
)
,
2800
LowerLimitGapMin
=
readmathvalue
(
f
)
,
2801
LowerLimitBaselineDropMin
=
readmathvalue
(
f
)
,
2802
StackTopShiftUp
=
readmathvalue
(
f
)
,
2803
StackTopDisplayStyleShiftUp
=
readmathvalue
(
f
)
,
2804
StackBottomShiftDown
=
readmathvalue
(
f
)
,
2805
StackBottomDisplayStyleShiftDown
=
readmathvalue
(
f
)
,
2806
StackGapMin
=
readmathvalue
(
f
)
,
2807
StackDisplayStyleGapMin
=
readmathvalue
(
f
)
,
2808
StretchStackTopShiftUp
=
readmathvalue
(
f
)
,
2809
StretchStackBottomShiftDown
=
readmathvalue
(
f
)
,
2810
StretchStackGapAboveMin
=
readmathvalue
(
f
)
,
2811
StretchStackGapBelowMin
=
readmathvalue
(
f
)
,
2812
FractionNumeratorShiftUp
=
readmathvalue
(
f
)
,
2813
FractionNumeratorDisplayStyleShiftUp
=
readmathvalue
(
f
)
,
2814
FractionDenominatorShiftDown
=
readmathvalue
(
f
)
,
2815
FractionDenominatorDisplayStyleShiftDown
=
readmathvalue
(
f
)
,
2816
FractionNumeratorGapMin
=
readmathvalue
(
f
)
,
2817
FractionNumeratorDisplayStyleGapMin
=
readmathvalue
(
f
)
,
2818
FractionRuleThickness
=
readmathvalue
(
f
)
,
2819
FractionDenominatorGapMin
=
readmathvalue
(
f
)
,
2820
FractionDenominatorDisplayStyleGapMin
=
readmathvalue
(
f
)
,
2821
SkewedFractionHorizontalGap
=
readmathvalue
(
f
)
,
2822
SkewedFractionVerticalGap
=
readmathvalue
(
f
)
,
2823
OverbarVerticalGap
=
readmathvalue
(
f
)
,
2824
OverbarRuleThickness
=
readmathvalue
(
f
)
,
2825
OverbarExtraAscender
=
readmathvalue
(
f
)
,
2826
UnderbarVerticalGap
=
readmathvalue
(
f
)
,
2827
UnderbarRuleThickness
=
readmathvalue
(
f
)
,
2828
UnderbarExtraDescender
=
readmathvalue
(
f
)
,
2829
RadicalVerticalGap
=
readmathvalue
(
f
)
,
2830
RadicalDisplayStyleVerticalGap
=
readmathvalue
(
f
)
,
2831
RadicalRuleThickness
=
readmathvalue
(
f
)
,
2832
RadicalExtraAscender
=
readmathvalue
(
f
)
,
2833
RadicalKernBeforeDegree
=
readmathvalue
(
f
)
,
2834
RadicalKernAfterDegree
=
readmathvalue
(
f
)
,
2835
RadicalDegreeBottomRaisePercent
=
readshort
(
f
)
,
2836
}
2837
end
2838 2839
local
function
readmathglyphinfo
(
f
,
fontdata
,
offset
)
2840
setposition
(
f
,
offset
)
2841
local
italics
=
readushort
(
f
)
2842
local
accents
=
readushort
(
f
)
2843
local
extensions
=
readushort
(
f
)
2844
local
kerns
=
readushort
(
f
)
2845
local
glyphs
=
fontdata
.
glyphs
2846
if
italics
~
=
0
then
2847
setposition
(
f
,
offset
+
italics
)
2848
local
coverage
=
readushort
(
f
)
2849
local
nofglyphs
=
readushort
(
f
)
2850
coverage
=
readcoverage
(
f
,
offset
+
italics
+
coverage
,
true
)
2851
setposition
(
f
,
offset
+
italics
+
4
)
2852
for
i
=
1
,
nofglyphs
do
2853
local
italic
=
readmathvalue
(
f
)
2854
if
italic
~
=
0
then
2855
local
glyph
=
glyphs
[
coverage
[
i
]
]
2856
local
math
=
glyph
.
math
2857
if
not
math
then
2858
glyph
.
math
=
{
italic
=
italic
}
2859
else
2860
math
.
italic
=
italic
2861
end
2862
end
2863
end
2864
fontdata
.
hasitalics
=
true
2865
end
2866
if
accents
~
=
0
then
2867
setposition
(
f
,
offset
+
accents
)
2868
local
coverage
=
readushort
(
f
)
2869
local
nofglyphs
=
readushort
(
f
)
2870
coverage
=
readcoverage
(
f
,
offset
+
accents
+
coverage
,
true
)
2871
setposition
(
f
,
offset
+
accents
+
4
)
2872
for
i
=
1
,
nofglyphs
do
2873
local
accent
=
readmathvalue
(
f
)
2874
if
accent
~
=
0
then
2875
local
glyph
=
glyphs
[
coverage
[
i
]
]
2876
local
math
=
glyph
.
math
2877
if
not
math
then
2878
glyph
.
math
=
{
accent
=
accent
}
2879
else
2880
math
.
accent
=
accent
2881
end
2882
end
2883
end
2884
end
2885
if
extensions
~
=
0
then
2886
setposition
(
f
,
offset
+
extensions
)
2887
end
2888
if
kerns
~
=
0
then
2889
local
kernoffset
=
offset
+
kerns
2890
setposition
(
f
,
kernoffset
)
2891
local
coverage
=
readushort
(
f
)
2892
local
nofglyphs
=
readushort
(
f
)
2893
if
nofglyphs
>
0
then
2894
local
function
get
(
offset
)
2895
setposition
(
f
,
kernoffset
+
offset
)
2896
local
n
=
readushort
(
f
)
2897
if
n
=
=
0
then
2898
local
k
=
readmathvalue
(
f
)
2899
if
k
=
=
0
then
2900
-- no need for it (happens sometimes)
2901
else
2902
return
{
{
kern
=
k
}
}
2903
end
2904
else
2905
local
l
=
{
}
2906
for
i
=
1
,
n
do
2907
l
[
i
]
=
{
height
=
readmathvalue
(
f
)
}
2908
end
2909
for
i
=
1
,
n
do
2910
l
[
i
]
.
kern
=
readmathvalue
(
f
)
2911
end
2912
l
[
n
+
1
]
=
{
kern
=
readmathvalue
(
f
)
}
2913
return
l
2914
end
2915
end
2916
local
kernsets
=
{
}
2917
for
i
=
1
,
nofglyphs
do
2918
local
topright
=
readushort
(
f
)
2919
local
topleft
=
readushort
(
f
)
2920
local
bottomright
=
readushort
(
f
)
2921
local
bottomleft
=
readushort
(
f
)
2922
kernsets
[
i
]
=
{
2923
topright
=
topright
~
=
0
and
topright
or
nil
,
2924
topleft
=
topleft
~
=
0
and
topleft
or
nil
,
2925
bottomright
=
bottomright
~
=
0
and
bottomright
or
nil
,
2926
bottomleft
=
bottomleft
~
=
0
and
bottomleft
or
nil
,
2927
}
2928
end
2929
coverage
=
readcoverage
(
f
,
kernoffset
+
coverage
,
true
)
2930
for
i
=
1
,
nofglyphs
do
2931
local
kernset
=
kernsets
[
i
]
2932
if
next
(
kernset
)
then
2933
local
k
=
kernset
.
topright
if
k
then
kernset
.
topright
=
get
(
k
)
end
2934
local
k
=
kernset
.
topleft
if
k
then
kernset
.
topleft
=
get
(
k
)
end
2935
local
k
=
kernset
.
bottomright
if
k
then
kernset
.
bottomright
=
get
(
k
)
end
2936
local
k
=
kernset
.
bottomleft
if
k
then
kernset
.
bottomleft
=
get
(
k
)
end
2937
if
next
(
kernset
)
then
2938
local
glyph
=
glyphs
[
coverage
[
i
]
]
2939
local
math
=
glyph
.
math
2940
if
math
then
2941
math
.
kerns
=
kernset
2942
else
2943
glyph
.
math
=
{
kerns
=
kernset
}
2944
end
2945
end
2946
end
2947
end
2948
end
2949
end
2950
end
2951 2952
local
function
readmathvariants
(
f
,
fontdata
,
offset
)
2953
setposition
(
f
,
offset
)
2954
local
glyphs
=
fontdata
.
glyphs
2955
local
minoverlap
=
readushort
(
f
)
2956
local
vcoverage
=
readushort
(
f
)
2957
local
hcoverage
=
readushort
(
f
)
2958
local
vnofglyphs
=
readushort
(
f
)
2959
local
hnofglyphs
=
readushort
(
f
)
2960
local
vconstruction
=
readcardinaltable
(
f
,
vnofglyphs
,
ushort
)
2961
local
hconstruction
=
readcardinaltable
(
f
,
hnofglyphs
,
ushort
)
2962 2963
fontdata
.
mathconstants
.
MinConnectorOverlap
=
minoverlap
2964 2965
-- variants[i] = {
2966
-- glyph = readushort(f),
2967
-- advance = readushort(f),
2968
-- }
2969 2970
local
function
get
(
offset
,
coverage
,
nofglyphs
,
construction
,
kvariants
,
kparts
,
kitalic
)
2971
if
coverage
~
=
0
and
nofglyphs
>
0
then
2972
local
coverage
=
readcoverage
(
f
,
offset
+
coverage
,
true
)
2973
for
i
=
1
,
nofglyphs
do
2974
local
c
=
construction
[
i
]
2975
if
c
~
=
0
then
2976
local
index
=
coverage
[
i
]
2977
local
glyph
=
glyphs
[
index
]
2978
local
math
=
glyph
.
math
2979
setposition
(
f
,
offset
+
c
)
2980
local
assembly
=
readushort
(
f
)
2981
local
nofvariants
=
readushort
(
f
)
2982
if
nofvariants
>
0
then
2983
local
variants
,
v
=
nil
,
0
2984
for
i
=
1
,
nofvariants
do
2985
local
variant
=
readushort
(
f
)
2986
if
variant
=
=
index
then
2987
-- ignore
2988
elseif
variants
then
2989
v
=
v
+
1
2990
variants
[
v
]
=
variant
2991
else
2992
v
=
1
2993
variants
=
{
variant
}
2994
end
2995
skipshort
(
f
)
2996
end
2997
if
not
variants
then
2998
-- only self
2999
elseif
not
math
then
3000
math
=
{
[
kvariants
]
=
variants
}
3001
glyph
.
math
=
math
3002
else
3003
math
[
kvariants
]
=
variants
3004
end
3005
end
3006
if
assembly
~
=
0
then
3007
setposition
(
f
,
offset
+
c
+
assembly
)
3008
local
italic
=
readmathvalue
(
f
)
3009
local
nofparts
=
readushort
(
f
)
3010
local
parts
=
{
}
3011
for
i
=
1
,
nofparts
do
3012
local
p
=
{
3013
glyph
=
readushort
(
f
)
,
3014
start
=
readushort
(
f
)
,
3015
[
"
end
"
]
=
readushort
(
f
)
,
3016
advance
=
readushort
(
f
)
,
3017
}
3018
local
flags
=
readushort
(
f
)
3019
if
band
(
flags
,
0x0001
)
~
=
0
then
3020
p
.
extender
=
1
-- true
3021
end
3022
parts
[
i
]
=
p
3023
end
3024
if
not
math
then
3025
math
=
{
3026
[
kparts
]
=
parts
3027
}
3028
glyph
.
math
=
math
3029
else
3030
math
[
kparts
]
=
parts
3031
end
3032
if
italic
and
italic
~
=
0
then
3033
math
[
kitalic
]
=
italic
3034
end
3035
end
3036
end
3037
end
3038
end
3039
end
3040 3041
get
(
offset
,
vcoverage
,
vnofglyphs
,
vconstruction
,
"
vvariants
"
,
"
vparts
"
,
"
vitalic
"
)
3042
get
(
offset
,
hcoverage
,
hnofglyphs
,
hconstruction
,
"
hvariants
"
,
"
hparts
"
,
"
hitalic
"
)
3043
end
3044 3045
function
readers
.
math
(
f
,
fontdata
,
specification
)
3046
local
tableoffset
=
gotodatatable
(
f
,
fontdata
,
"
math
"
,
specification
.
glyphs
)
3047
if
tableoffset
then
3048
local
version
=
readulong
(
f
)
3049
-- if version ~= 0x00010000 then
3050
-- report("table version %a of %a is not supported (yet), maybe font %s is bad",version,"math",fontdata.filename)
3051
-- return
3052
-- end
3053
local
constants
=
readushort
(
f
)
3054
local
glyphinfo
=
readushort
(
f
)
3055
local
variants
=
readushort
(
f
)
3056
if
constants
=
=
0
then
3057
report
(
"
the math table of %a has no constants
"
,
fontdata
.
filename
)
3058
else
3059
readmathconstants
(
f
,
fontdata
,
tableoffset
+
constants
)
3060
end
3061
if
glyphinfo
~
=
0
then
3062
readmathglyphinfo
(
f
,
fontdata
,
tableoffset
+
glyphinfo
)
3063
end
3064
if
variants
~
=
0
then
3065
readmathvariants
(
f
,
fontdata
,
tableoffset
+
variants
)
3066
end
3067
end
3068
end
3069 3070
function
readers
.
colr
(
f
,
fontdata
,
specification
)
3071
local
tableoffset
=
gotodatatable
(
f
,
fontdata
,
"
colr
"
,
specification
.
glyphs
)
3072
if
tableoffset
then
3073
local
version
=
readushort
(
f
)
3074
if
version
~
=
0
then
3075
report
(
"
table version %a of %a is not supported (yet), maybe font %s is bad
"
,
version
,
"
colr
"
,
fontdata
.
filename
)
3076
return
3077
end
3078
if
not
fontdata
.
tables
.
cpal
then
3079
report
(
"
color table %a in font %a has no mandate %a table
"
,
"
colr
"
,
fontdata
.
filename
,
"
cpal
"
)
3080
fontdata
.
colorpalettes
=
{
}
3081
end
3082
local
glyphs
=
fontdata
.
glyphs
3083
local
nofglyphs
=
readushort
(
f
)
3084
local
baseoffset
=
readulong
(
f
)
3085
local
layeroffset
=
readulong
(
f
)
3086
local
noflayers
=
readushort
(
f
)
3087
local
layerrecords
=
{
}
3088
local
maxclass
=
0
3089
-- The special value 0xFFFF is foreground (but we index from 1). It
3090
-- more looks like indices into a palette so 'class' is a better name
3091
-- than 'palette'.
3092
setposition
(
f
,
tableoffset
+
layeroffset
)
3093
for
i
=
1
,
noflayers
do
3094
local
slot
=
readushort
(
f
)
3095
local
class
=
readushort
(
f
)
3096
if
class
<
0xFFFF
then
3097
class
=
class
+
1
3098
if
class
>
maxclass
then
3099
maxclass
=
class
3100
end
3101
end
3102
layerrecords
[
i
]
=
{
3103
slot
=
slot
,
3104
class
=
class
,
3105
}
3106
end
3107
fontdata
.
maxcolorclass
=
maxclass
3108
setposition
(
f
,
tableoffset
+
baseoffset
)
3109
for
i
=
0
,
nofglyphs
-1
do
3110
local
glyphindex
=
readushort
(
f
)
3111
local
firstlayer
=
readushort
(
f
)
3112
local
noflayers
=
readushort
(
f
)
3113
local
t
=
{
}
3114
for
i
=
1
,
noflayers
do
3115
t
[
i
]
=
layerrecords
[
firstlayer
+
i
]
3116
end
3117
glyphs
[
glyphindex
]
.
colors
=
t
3118
end
3119
end
3120
fontdata
.
hascolor
=
true
3121
end
3122 3123
function
readers
.
cpal
(
f
,
fontdata
,
specification
)
3124
local
tableoffset
=
gotodatatable
(
f
,
fontdata
,
"
cpal
"
,
specification
.
glyphs
)
3125
if
tableoffset
then
3126
local
version
=
readushort
(
f
)
3127
-- if version > 1 then
3128
-- report("table version %a of %a is not supported (yet), maybe font %s is bad",version,"cpal",fontdata.filename)
3129
-- return
3130
-- end
3131
local
nofpaletteentries
=
readushort
(
f
)
3132
local
nofpalettes
=
readushort
(
f
)
3133
local
nofcolorrecords
=
readushort
(
f
)
3134
local
firstcoloroffset
=
readulong
(
f
)
3135
local
colorrecords
=
{
}
3136
local
palettes
=
readcardinaltable
(
f
,
nofpalettes
,
ushort
)
3137
if
version
=
=
1
then
3138
-- used for guis
3139
local
palettettypesoffset
=
readulong
(
f
)
3140
local
palettelabelsoffset
=
readulong
(
f
)
3141
local
paletteentryoffset
=
readulong
(
f
)
3142
end
3143
setposition
(
f
,
tableoffset
+
firstcoloroffset
)
3144
for
i
=
1
,
nofcolorrecords
do
3145
local
b
,
g
,
r
,
a
=
readbytes
(
f
,
4
)
3146
colorrecords
[
i
]
=
{
3147
r
,
g
,
b
,
a
~
=
255
and
a
or
nil
,
3148
}
3149
end
3150
for
i
=
1
,
nofpalettes
do
3151
local
p
=
{
}
3152
local
o
=
palettes
[
i
]
3153
for
j
=
1
,
nofpaletteentries
do
3154
p
[
j
]
=
colorrecords
[
o
+
j
]
3155
end
3156
palettes
[
i
]
=
p
3157
end
3158
fontdata
.
colorpalettes
=
palettes
3159
end
3160
end
3161 3162
local
compress
=
gzip
and
gzip
.
compress
3163
local
compressed
=
compress
and
gzip
.
compressed
3164 3165
-- At some point I will delay loading and only store the offsets (in context lmtx
3166
-- only).
3167 3168
-- compressed = false
3169 3170
function
readers
.
svg
(
f
,
fontdata
,
specification
)
3171
local
tableoffset
=
gotodatatable
(
f
,
fontdata
,
"
svg
"
,
specification
.
glyphs
)
3172
if
tableoffset
then
3173
local
version
=
readushort
(
f
)
3174
-- if version ~= 0 then
3175
-- report("table version %a of %a is not supported (yet), maybe font %s is bad",version,"svg",fontdata.filename)
3176
-- return
3177
-- end
3178
local
glyphs
=
fontdata
.
glyphs
3179
local
indexoffset
=
tableoffset
+
readulong
(
f
)
3180
local
reserved
=
readulong
(
f
)
3181
setposition
(
f
,
indexoffset
)
3182
local
nofentries
=
readushort
(
f
)
3183
local
entries
=
{
}
3184
for
i
=
1
,
nofentries
do
3185
entries
[
i
]
=
{
3186
first
=
readushort
(
f
)
,
3187
last
=
readushort
(
f
)
,
3188
offset
=
indexoffset
+
readulong
(
f
)
,
3189
length
=
readulong
(
f
)
,
3190
}
3191
end
3192
for
i
=
1
,
nofentries
do
3193
local
entry
=
entries
[
i
]
3194
setposition
(
f
,
entry
.
offset
)
3195
local
data
=
readstring
(
f
,
entry
.
length
)
3196
if
compressed
and
not
compressed
(
data
)
then
3197
data
=
compress
(
data
)
3198
end
3199
entries
[
i
]
=
{
3200
first
=
entry
.
first
,
3201
last
=
entry
.
last
,
3202
data
=
data
3203
}
3204
end
3205
fontdata
.
svgshapes
=
entries
3206
end
3207
fontdata
.
hascolor
=
true
3208
end
3209 3210
function
readers
.
sbix
(
f
,
fontdata
,
specification
)
3211
local
tableoffset
=
gotodatatable
(
f
,
fontdata
,
"
sbix
"
,
specification
.
glyphs
)
3212
if
tableoffset
then
3213
local
version
=
readushort
(
f
)
3214
local
flags
=
readushort
(
f
)
3215
local
nofstrikes
=
readulong
(
f
)
3216
local
strikes
=
{
}
3217
local
nofglyphs
=
fontdata
.
nofglyphs
3218
for
i
=
1
,
nofstrikes
do
3219
strikes
[
i
]
=
readulong
(
f
)
3220
end
3221
local
shapes
=
{
}
3222
local
done
=
0
3223
for
i
=
1
,
nofstrikes
do
3224
local
strikeoffset
=
strikes
[
i
]
+
tableoffset
3225
setposition
(
f
,
strikeoffset
)
3226
strikes
[
i
]
=
{
3227
ppem
=
readushort
(
f
)
,
3228
ppi
=
readushort
(
f
)
,
3229
offset
=
strikeoffset
3230
}
3231
end
3232
-- highest first
3233
sort
(
strikes
,
function
(
a
,
b
)
3234
if
b
.
ppem
=
=
a
.
ppem
then
3235
return
b
.
ppi
<
a
.
ppi
3236
else
3237
return
b
.
ppem
<
a
.
ppem
3238
end
3239
end
)
3240
local
glyphs
=
{
}
3241
local
delayed
=
CONTEXTLMTXMODE
and
CONTEXTLMTXMODE
>
0
or
fonts
.
handlers
.
typethree
3242
for
i
=
1
,
nofstrikes
do
3243
local
strike
=
strikes
[
i
]
3244
local
strikeppem
=
strike
.
ppem
3245
local
strikeppi
=
strike
.
ppi
3246
local
strikeoffset
=
strike
.
offset
3247
setposition
(
f
,
strikeoffset
)
3248
for
i
=
0
,
nofglyphs
do
3249
glyphs
[
i
]
=
readulong
(
f
)
3250
end
3251
local
glyphoffset
=
glyphs
[
0
]
3252
for
i
=
0
,
nofglyphs
-1
do
3253
local
nextoffset
=
glyphs
[
i
+
1
]
3254
if
not
shapes
[
i
]
then
3255
local
datasize
=
nextoffset
-
glyphoffset
3256
if
datasize
>
0
then
3257
setposition
(
f
,
strikeoffset
+
glyphoffset
)
3258
local
x
=
readshort
(
f
)
3259
local
y
=
readshort
(
f
)
3260
local
tag
=
readtag
(
f
)
-- or just skip, we never needed it till now
3261
local
size
=
datasize
-
8
3262
local
data
=
nil
3263
local
offset
=
nil
3264
if
delayed
then
3265
offset
=
getposition
(
f
)
3266
data
=
nil
3267
else
3268
data
=
readstring
(
f
,
size
)
3269
size
=
nil
3270
end
3271
shapes
[
i
]
=
{
3272
x
=
x
,
3273
y
=
y
,
3274
o
=
offset
,
3275
s
=
size
,
3276
data
=
data
,
3277
-- tag = tag, -- maybe for tracing
3278
-- ppem = strikeppem, -- not used, for tracing
3279
-- ppi = strikeppi, -- not used, for tracing
3280
}
3281
done
=
done
+
1
3282
if
done
=
=
nofglyphs
then
3283
break
3284
end
3285
end
3286
end
3287
glyphoffset
=
nextoffset
3288
end
3289
end
3290
fontdata
.
pngshapes
=
shapes
3291
end
3292
end
3293 3294
-- Another bitmap (so not that useful) format. But Luigi found a font that
3295
-- has them , so ...
3296 3297
do
3298 3299
local
function
getmetrics
(
f
)
3300
return
{
3301
ascender
=
readinteger
(
f
)
,
3302
descender
=
readinteger
(
f
)
,
3303
widthmax
=
readuinteger
(
f
)
,
3304
caretslopedumerator
=
readinteger
(
f
)
,
3305
caretslopedenominator
=
readinteger
(
f
)
,
3306
caretoffset
=
readinteger
(
f
)
,
3307
minorigin
=
readinteger
(
f
)
,
3308
minadvance
=
readinteger
(
f
)
,
3309
maxbefore
=
readinteger
(
f
)
,
3310
minafter
=
readinteger
(
f
)
,
3311
pad1
=
readinteger
(
f
)
,
3312
pad2
=
readinteger
(
f
)
,
3313
}
3314
end
3315 3316
-- bad names
3317 3318
local
function
getbigmetrics
(
f
)
3319
-- bigmetrics, maybe just skip 9 bytes
3320
return
{
3321
height
=
readuinteger
(
f
)
,
3322
width
=
readuinteger
(
f
)
,
3323
horiBearingX
=
readinteger
(
f
)
,
3324
horiBearingY
=
readinteger
(
f
)
,
3325
horiAdvance
=
readuinteger
(
f
)
,
3326
vertBearingX
=
readinteger
(
f
)
,
3327
vertBearingY
=
readinteger
(
f
)
,
3328
vertAdvance
=
readuinteger
(
f
)
,
3329
}
3330
end
3331 3332
local
function
getsmallmetrics
(
f
)
3333
-- smallmetrics, maybe just skip 5 bytes
3334
return
{
3335
height
=
readuinteger
(
f
)
,
3336
width
=
readuinteger
(
f
)
,
3337
bearingX
=
readinteger
(
f
)
,
3338
bearingY
=
readinteger
(
f
)
,
3339
advance
=
readuinteger
(
f
)
,
3340
}
3341
end
3342 3343
function
readers
.
cblc
(
f
,
fontdata
,
specification
)
3344
-- should we delay this ?
3345
local
ctdttableoffset
=
gotodatatable
(
f
,
fontdata
,
"
cbdt
"
,
specification
.
glyphs
)
3346
if
not
ctdttableoffset
then
3347
return
3348
end
3349
local
cblctableoffset
=
gotodatatable
(
f
,
fontdata
,
"
cblc
"
,
specification
.
glyphs
)
3350
if
cblctableoffset
then
3351
local
majorversion
=
readushort
(
f
)
3352
local
minorversion
=
readushort
(
f
)
3353
local
nofsizetables
=
readulong
(
f
)
3354
local
sizetables
=
{
}
3355
local
shapes
=
{
}
3356
local
subtables
=
{
}
3357
for
i
=
1
,
nofsizetables
do
3358
sizetables
[
i
]
=
{
3359
subtables
=
readulong
(
f
)
,
3360
indexsize
=
readulong
(
f
)
,
3361
nofsubtables
=
readulong
(
f
)
,
3362
colorref
=
readulong
(
f
)
,
3363
hormetrics
=
getmetrics
(
f
)
,
3364
vermetrics
=
getmetrics
(
f
)
,
3365
firstindex
=
readushort
(
f
)
,
3366
lastindex
=
readushort
(
f
)
,
3367
ppemx
=
readbyte
(
f
)
,
3368
ppemy
=
readbyte
(
f
)
,
3369
bitdepth
=
readbyte
(
f
)
,
3370
flags
=
readbyte
(
f
)
,
3371
}
3372
end
3373
sort
(
sizetables
,
function
(
a
,
b
)
3374
if
b
.
ppemx
=
=
a
.
ppemx
then
3375
return
b
.
bitdepth
<
a
.
bitdepth
3376
else
3377
return
b
.
ppemx
<
a
.
ppemx
3378
end
3379
end
)
3380
for
i
=
1
,
nofsizetables
do
3381
local
s
=
sizetables
[
i
]
3382
local
d
=
false
3383
for
j
=
s
.
firstindex
,
s
.
lastindex
do
3384
if
not
shapes
[
j
]
then
3385
shapes
[
j
]
=
i
3386
d
=
true
3387
end
3388
end
3389
if
d
then
3390
s
.
used
=
true
3391
end
3392
end
3393
for
i
=
1
,
nofsizetables
do
3394
local
s
=
sizetables
[
i
]
3395
if
s
.
used
then
3396
local
offset
=
s
.
subtables
3397
setposition
(
f
,
cblctableoffset
+
offset
)
3398
for
j
=
1
,
s
.
nofsubtables
do
3399
local
firstindex
=
readushort
(
f
)
3400
local
lastindex
=
readushort
(
f
)
3401
local
tableoffset
=
readulong
(
f
)
+
offset
3402
for
k
=
firstindex
,
lastindex
do
3403
if
shapes
[
k
]
=
=
i
then
3404
local
s
=
subtables
[
tableoffset
]
3405
if
not
s
then
3406
s
=
{
3407
firstindex
=
firstindex
,
3408
lastindex
=
lastindex
,
3409
}
3410
subtables
[
tableoffset
]
=
s
3411
end
3412
shapes
[
k
]
=
s
3413
end
3414
end
3415
end
3416
end
3417
end
3418 3419
-- there is no need to sort in string stream but we have a nicer trace
3420
-- if needed
3421 3422
for
offset
,
subtable
in
sortedhash
(
subtables
)
do
3423
local
tabletype
=
readushort
(
f
)
3424
subtable
.
format
=
readushort
(
f
)
3425
local
baseoffset
=
readulong
(
f
)
+
ctdttableoffset
3426
local
offsets
=
{
}
3427
local
metrics
=
nil
3428
if
tabletype
=
=
1
then
3429
-- we have the usual one more to get the size
3430
for
i
=
subtable
.
firstindex
,
subtable
.
lastindex
do
3431
offsets
[
i
]
=
readulong
(
f
)
+
baseoffset
3432
end
3433
skipbytes
(
f
,
4
)
3434
elseif
tabletype
=
=
2
then
3435
local
size
=
readulong
(
f
)
3436
local
done
=
baseoffset
3437
metrics
=
getbigmetrics
(
f
)
3438
for
i
=
subtable
.
firstindex
,
subtable
.
lastindex
do
3439
offsets
[
i
]
=
done
3440
done
=
done
+
size
3441
end
3442
elseif
tabletype
=
=
3
then
3443
-- we have the usual one more to get the size
3444
local
n
=
subtable
.
lastindex
-
subtable
.
firstindex
+
2
3445
for
i
=
subtable
.
firstindex
,
subtable
.
lastindex
do
3446
offsets
[
i
]
=
readushort
(
f
)
+
baseoffset
3447
end
3448
if
math
.
odd
(
n
)
then
3449
skipbytes
(
f
,
4
)
3450
else
3451
skipbytes
(
f
,
2
)
3452
end
3453
elseif
tabletype
=
=
4
then
3454
for
i
=
1
,
readulong
(
f
)
do
3455
offsets
[
readushort
(
f
)
]
=
readushort
(
f
)
+
baseoffset
3456
end
3457
elseif
tabletype
=
=
5
then
3458
local
size
=
readulong
(
f
)
3459
local
done
=
baseoffset
3460
metrics
=
getbigmetrics
(
f
)
3461
local
n
=
readulong
(
f
)
3462
for
i
=
1
,
n
do
3463
offsets
[
readushort
(
f
)
]
=
done
3464
done
=
done
+
size
3465
end
3466
if
math
.
odd
(
n
)
then
3467
skipbytes
(
f
,
2
)
3468
end
3469
else
3470
return
-- unsupported format
3471
end
3472
subtable
.
offsets
=
offsets
3473
subtable
.
metrics
=
metrics
3474
end
3475 3476
-- we only support a few sensible types ... there are hardly any fonts so
3477
-- why are there so many variants ... not the best spec
3478 3479
local
default
=
{
width
=
0
,
height
=
0
}
3480
local
glyphs
=
fontdata
.
glyphs
3481
local
delayed
=
CONTEXTLMTXMODE
and
CONTEXTLMTXMODE
>
0
or
fonts
.
handlers
.
typethree
3482 3483
for
index
,
subtable
in
sortedhash
(
shapes
)
do
3484
if
type
(
subtable
)
=
=
"
table
"
then
3485
local
data
=
nil
3486
local
size
=
nil
3487
local
metrics
=
default
3488
local
format
=
subtable
.
format
3489
local
offset
=
subtable
.
offsets
[
index
]
3490
setposition
(
f
,
offset
)
3491
if
format
=
=
17
then
3492
metrics
=
getsmallmetrics
(
f
)
3493
size
=
true
3494
elseif
format
=
=
18
then
3495
metrics
=
getbigmetrics
(
f
)
3496
size
=
true
3497
elseif
format
=
=
19
then
3498
metrics
=
subtable
.
metrics
3499
size
=
true
3500
else
3501
-- forget about it
3502
end
3503
if
size
then
3504
size
=
readulong
(
f
)
3505
if
delayed
then
3506
offset
=
getposition
(
f
)
3507
data
=
nil
3508
else
3509
offset
=
nil
3510
data
=
readstring
(
f
,
size
)
3511
size
=
nil
3512
end
3513
else
3514
offset
=
nil
3515
end
3516
local
x
=
metrics
.
width
3517
local
y
=
metrics
.
height
3518
shapes
[
index
]
=
{
3519
x
=
x
,
3520
y
=
y
,
3521
o
=
offset
,
3522
s
=
size
,
3523
data
=
data
,
3524
}
3525
-- I'll look into this in more details when needed
3526
-- as we can use the bearings to get better boxes.
3527
local
glyph
=
glyphs
[
index
]
3528
if
not
glyph
.
boundingbox
then
3529
local
width
=
glyph
.
width
3530
local
height
=
width
*
y
/
x
3531
glyph
.
boundingbox
=
{
0
,
0
,
width
,
height
}
3532
end
3533
else
3534
shapes
[
index
]
=
{
3535
x
=
0
,
3536
y
=
0
,
3537
data
=
"
"
,
-- or just nil
3538
}
3539
end
3540
end
3541 3542
fontdata
.
pngshapes
=
shapes
-- we cheat
3543
end
3544
end
3545 3546
function
readers
.
cbdt
(
f
,
fontdata
,
specification
)
3547
-- local tableoffset = gotodatatable(f,fontdata,"ctdt",specification.glyphs)
3548
-- if tableoffset then
3549
-- local majorversion = readushort(f)
3550
-- local minorversion = readushort(f)
3551
-- end
3552
end
3553 3554
-- function readers.ebdt(f,fontdata,specification)
3555
-- if specification.glyphs then
3556
-- end
3557
-- end
3558 3559
-- function readers.ebsc(f,fontdata,specification)
3560
-- if specification.glyphs then
3561
-- end
3562
-- end
3563 3564
-- function readers.eblc(f,fontdata,specification)
3565
-- if specification.glyphs then
3566
-- end
3567
-- end
3568 3569
end
3570 3571
-- + AVAR : optional
3572
-- + CFF2 : otf outlines
3573
-- - CVAR : ttf hinting, not needed
3574
-- + FVAR : the variations
3575
-- + GVAR : ttf outline changes
3576
-- + HVAR : horizontal changes
3577
-- + MVAR : metric changes
3578
-- + STAT : relations within fonts
3579
-- * VVAR : vertical changes
3580
--
3581
-- * BASE : extra baseline adjustments
3582
-- - GASP : not needed
3583
-- + GDEF : not needed (carets)
3584
-- + GPOS : adapted device tables (needed?)
3585
-- + GSUB : new table
3586
-- + NAME : 25 added
3587 3588
function
readers
.
stat
(
f
,
fontdata
,
specification
)
3589
local
tableoffset
=
gotodatatable
(
f
,
fontdata
,
"
stat
"
,
true
)
-- specification.variable
3590
if
tableoffset
then
3591
local
extras
=
fontdata
.
extras
3592
local
version
=
readulong
(
f
)
-- 0x00010000
3593
local
axissize
=
readushort
(
f
)
3594
local
nofaxis
=
readushort
(
f
)
3595
local
axisoffset
=
readulong
(
f
)
3596
local
nofvalues
=
readushort
(
f
)
3597
local
valuesoffset
=
readulong
(
f
)
3598
local
fallbackname
=
extras
[
readushort
(
f
)
]
-- beta fonts mess up
3599
local
axis
=
{
}
3600
local
values
=
{
}
3601
setposition
(
f
,
tableoffset
+
axisoffset
)
3602
for
i
=
1
,
nofaxis
do
3603
local
tag
=
readtag
(
f
)
3604
axis
[
i
]
=
{
3605
tag
=
tag
,
3606
name
=
lower
(
extras
[
readushort
(
f
)
]
or
tag
)
,
3607
ordering
=
readushort
(
f
)
,
-- maybe gaps
3608
variants
=
{
}
3609
}
3610
end
3611
-- flags:
3612
--
3613
-- 0x0001 : OlderSiblingFontAttribute
3614
-- 0x0002 : ElidableAxisValueName
3615
-- 0xFFFC : reservedFlags
3616
--
3617
setposition
(
f
,
tableoffset
+
valuesoffset
)
3618
for
i
=
1
,
nofvalues
do
3619
values
[
i
]
=
readushort
(
f
)
3620
end
3621
for
i
=
1
,
nofvalues
do
3622
setposition
(
f
,
tableoffset
+
valuesoffset
+
values
[
i
]
)
3623
local
format
=
readushort
(
f
)
3624
local
index
=
readushort
(
f
)
+
1
3625
local
flags
=
readushort
(
f
)
3626
local
name
=
lower
(
extras
[
readushort
(
f
)
]
or
"
no name
"
)
3627
local
value
=
readfixed
(
f
)
3628
local
variant
3629
if
format
=
=
1
then
3630
variant
=
{
3631
flags
=
flags
,
3632
name
=
name
,
3633
value
=
value
,
3634
}
3635
elseif
format
=
=
2
then
3636
variant
=
{
3637
flags
=
flags
,
3638
name
=
name
,
3639
value
=
value
,
3640
minimum
=
readfixed
(
f
)
,
3641
maximum
=
readfixed
(
f
)
,
3642
}
3643
elseif
format
=
=
3
then
3644
variant
=
{
3645
flags
=
flags
,
3646
name
=
name
,
3647
value
=
value
,
3648
link
=
readfixed
(
f
)
,
3649
}
3650
end
3651
insert
(
axis
[
index
]
.
variants
,
variant
)
3652
end
3653
sort
(
axis
,
function
(
a
,
b
)
3654
return
a
.
ordering
<
b
.
ordering
3655
end
)
3656
for
i
=
1
,
#
axis
do
3657
local
a
=
axis
[
i
]
3658
sort
(
a
.
variants
,
function
(
a
,
b
)
3659
return
a
.
name
<
b
.
name
3660
end
)
3661
a
.
ordering
=
nil
3662
end
3663
setvariabledata
(
fontdata
,
"
designaxis
"
,
axis
)
3664
setvariabledata
(
fontdata
,
"
fallbackname
"
,
fallbackname
)
3665
end
3666
end
3667 3668
-- The avar table is optional and used in combination with fvar. Given the
3669
-- detailed explanation about bad values we expect the worst and do some
3670
-- checking.
3671 3672
function
readers
.
avar
(
f
,
fontdata
,
specification
)
3673
local
tableoffset
=
gotodatatable
(
f
,
fontdata
,
"
avar
"
,
true
)
-- specification.variable
3674
if
tableoffset
then
3675 3676
local
function
collect
(
)
3677
local
nofvalues
=
readushort
(
f
)
3678
local
values
=
{
}
3679
local
lastfrom
=
false
3680
local
lastto
=
false
3681
for
i
=
1
,
nofvalues
do
3682
local
from
=
read2dot14
(
f
)
3683
local
to
=
read2dot14
(
f
)
3684
if
lastfrom
and
from
<
=
lastfrom
then
3685
-- ignore
3686
elseif
lastto
and
to
>
=
lastto
then
3687
-- ignore
3688
else
3689
values
[
#
values
+
1
]
=
{
from
,
to
}
3690
lastfrom
,
lastto
=
from
,
to
3691
end
3692
end
3693
nofvalues
=
#
values
3694
if
nofvalues
>
2
then
3695
local
some
=
values
[
1
]
3696
if
some
[
1
]
=
=
-1
and
some
[
2
]
=
=
-1
then
3697
some
=
values
[
nofvalues
]
3698
if
some
[
1
]
=
=
1
and
some
[
2
]
=
=
1
then
3699
for
i
=
2
,
nofvalues
-1
do
3700
some
=
values
[
i
]
3701
if
some
[
1
]
=
=
0
and
some
[
2
]
=
=
0
then
3702
return
values
3703
end
3704
end
3705
end
3706
end
3707
end
3708
return
false
3709
end
3710 3711
local
version
=
readulong
(
f
)
-- 0x00010000
3712
local
reserved
=
readushort
(
f
)
3713
local
nofaxis
=
readushort
(
f
)
3714
local
segments
=
{
}
3715
for
i
=
1
,
nofaxis
do
3716
segments
[
i
]
=
collect
(
)
3717
end
3718
setvariabledata
(
fontdata
,
"
segments
"
,
segments
)
3719
end
3720
end
3721 3722
function
readers
.
fvar
(
f
,
fontdata
,
specification
)
3723
local
tableoffset
=
gotodatatable
(
f
,
fontdata
,
"
fvar
"
,
true
)
-- specification.variable or specification.instancenames
3724
if
tableoffset
then
3725
local
version
=
readulong
(
f
)
-- 0x00010000
3726
local
offsettoaxis
=
tableoffset
+
readushort
(
f
)
3727
local
reserved
=
skipshort
(
f
)
3728
-- pair 1
3729
local
nofaxis
=
readushort
(
f
)
3730
local
sizeofaxis
=
readushort
(
f
)
3731
-- pair 2
3732
local
nofinstances
=
readushort
(
f
)
3733
local
sizeofinstances
=
readushort
(
f
)
3734
--
3735
local
extras
=
fontdata
.
extras
3736
local
axis
=
{
}
3737
local
instances
=
{
}
3738
--
3739
setposition
(
f
,
offsettoaxis
)
3740
--
3741
for
i
=
1
,
nofaxis
do
3742
axis
[
i
]
=
{
3743
tag
=
readtag
(
f
)
,
-- ital opsz slnt wdth wght
3744
minimum
=
readfixed
(
f
)
,
3745
default
=
readfixed
(
f
)
,
3746
maximum
=
readfixed
(
f
)
,
3747
flags
=
readushort
(
f
)
,
3748
name
=
lower
(
extras
[
readushort
(
f
)
]
or
"
bad name
"
)
,
3749
}
3750
local
n
=
sizeofaxis
-
20
3751
if
n
>
0
then
3752
skipbytes
(
f
,
n
)
3753
elseif
n
<
0
then
3754
-- error
3755
end
3756
end
3757
--
3758
local
nofbytes
=
2
+
2
+
2
+
nofaxis
*
4
3759
local
readpsname
=
nofbytes
<
=
sizeofinstances
3760
local
skippable
=
sizeofinstances
-
nofbytes
3761
for
i
=
1
,
nofinstances
do
3762
local
subfamid
=
readushort
(
f
)
3763
local
flags
=
readushort
(
f
)
-- 0, not used yet
3764
local
values
=
{
}
3765
for
i
=
1
,
nofaxis
do
3766
values
[
i
]
=
{
3767
axis
=
axis
[
i
]
.
tag
,
3768
value
=
readfixed
(
f
)
,
3769
}
3770
end
3771
local
psnameid
=
readpsname
and
readushort
(
f
)
or
0xFFFF
3772
if
subfamid
=
=
2
or
subfamid
=
=
17
then
3773
-- okay
3774
elseif
subfamid
=
=
0xFFFF
then
3775
subfamid
=
nil
3776
elseif
subfamid
<
=
256
or
subfamid
>
=
32768
then
3777
subfamid
=
nil
-- actually an error
3778
end
3779
if
psnameid
=
=
6
then
3780
-- okay
3781
elseif
psnameid
=
=
0xFFFF
then
3782
psnameid
=
nil
3783
elseif
psnameid
<
=
256
or
psnameid
>
=
32768
then
3784
psnameid
=
nil
-- actually an error
3785
end
3786
instances
[
i
]
=
{
3787
-- flags = flags,
3788
subfamily
=
extras
[
subfamid
]
,
3789
psname
=
psnameid
and
extras
[
psnameid
]
or
nil
,
3790
values
=
values
,
3791
}
3792
if
skippable
>
0
then
3793
skipbytes
(
f
,
skippable
)
3794
end
3795
end
3796
setvariabledata
(
fontdata
,
"
axis
"
,
axis
)
3797
setvariabledata
(
fontdata
,
"
instances
"
,
instances
)
3798
end
3799
end
3800 3801
function
readers
.
hvar
(
f
,
fontdata
,
specification
)
3802
local
factors
=
specification
.
factors
3803
if
not
factors
then
3804
return
3805
end
3806
local
tableoffset
=
gotodatatable
(
f
,
fontdata
,
"
hvar
"
,
specification
.
variable
)
3807
if
not
tableoffset
then
3808
return
3809
end
3810 3811
local
version
=
readulong
(
f
)
-- 0x00010000
3812
local
variationoffset
=
tableoffset
+
readulong
(
f
)
-- the store
3813
local
advanceoffset
=
tableoffset
+
readulong
(
f
)
3814
local
lsboffset
=
tableoffset
+
readulong
(
f
)
3815
local
rsboffset
=
tableoffset
+
readulong
(
f
)
3816 3817
local
regions
=
{
}
3818
local
variations
=
{
}
3819
local
innerindex
=
{
}
-- size is mapcount
3820
local
outerindex
=
{
}
-- size is mapcount
3821 3822
if
variationoffset
>
0
then
3823
regions
,
deltas
=
readvariationdata
(
f
,
variationoffset
,
factors
)
3824
end
3825 3826
if
not
regions
then
3827
-- for now .. what to do ?
3828
return
3829
end
3830 3831
if
advanceoffset
>
0
then
3832
--
3833
-- innerIndexBitCountMask = 0x000F
3834
-- mapEntrySizeMask = 0x0030
3835
-- reservedFlags = 0xFFC0
3836
--
3837
-- outerIndex = entry >> ((entryFormat & innerIndexBitCountMask) + 1)
3838
-- innerIndex = entry & ((1 << ((entryFormat & innerIndexBitCountMask) + 1)) - 1)
3839
--
3840
setposition
(
f
,
advanceoffset
)
3841
local
format
=
readushort
(
f
)
-- todo: check
3842
local
mapcount
=
readushort
(
f
)
3843
local
entrysize
=
rshift
(
band
(
format
,
0x0030
)
,
4
)
+
1
3844
local
nofinnerbits
=
band
(
format
,
0x000F
)
+
1
-- n of inner bits
3845
local
innermask
=
lshift
(
1
,
nofinnerbits
)
-
1
3846
local
readcardinal
=
read_cardinal
[
entrysize
]
-- 1 upto 4 bytes
3847
for
i
=
0
,
mapcount
-1
do
3848
local
mapdata
=
readcardinal
(
f
)
3849
outerindex
[
i
]
=
rshift
(
mapdata
,
nofinnerbits
)
3850
innerindex
[
i
]
=
band
(
mapdata
,
innermask
)
3851
end
3852
-- use last entry when no match i
3853
setvariabledata
(
fontdata
,
"
hvarwidths
"
,
true
)
3854
local
glyphs
=
fontdata
.
glyphs
3855
for
i
=
0
,
fontdata
.
nofglyphs
-1
do
3856
local
glyph
=
glyphs
[
i
]
3857
local
width
=
glyph
.
width
3858
if
width
then
3859
local
outer
=
outerindex
[
i
]
or
0
3860
local
inner
=
innerindex
[
i
]
or
i
3861
if
outer
and
inner
then
-- not needed
3862
local
delta
=
deltas
[
outer
+
1
]
3863
if
delta
then
3864
local
d
=
delta
.
deltas
[
inner
+
1
]
3865
if
d
then
3866
local
scales
=
delta
.
scales
3867
local
deltaw
=
0
3868
for
i
=
1
,
#
scales
do
3869
local
di
=
d
[
i
]
3870
if
di
then
3871
deltaw
=
deltaw
+
scales
[
i
]
*
di
3872
else
3873
break
-- can't happen
3874
end
3875
end
3876
-- report("index: %i, outer: %i, inner: %i, deltas: %|t, scales: %|t, width: %i, delta %i",
3877
-- i,outer,inner,d,scales,width,round(deltaw))
3878
glyph
.
width
=
width
+
round
(
deltaw
)
3879
end
3880
end
3881
end
3882
end
3883
end
3884 3885
end
3886 3887
-- if lsboffset > 0 then
3888
-- -- we don't use left side bearings
3889
-- end
3890 3891
-- if rsboffset > 0 then
3892
-- -- we don't use right side bearings
3893
-- end
3894 3895
-- setvariabledata(fontdata,"hregions",regions)
3896 3897
end
3898 3899
function
readers
.
vvar
(
f
,
fontdata
,
specification
)
3900
if
not
specification
.
variable
then
3901
return
3902
end
3903
end
3904 3905
function
readers
.
mvar
(
f
,
fontdata
,
specification
)
3906
local
tableoffset
=
gotodatatable
(
f
,
fontdata
,
"
mvar
"
,
specification
.
variable
)
3907
if
tableoffset
then
3908
local
version
=
readulong
(
f
)
-- 0x00010000
3909
local
reserved
=
skipshort
(
f
,
1
)
3910
local
recordsize
=
readushort
(
f
)
3911
local
nofrecords
=
readushort
(
f
)
3912
local
offsettostore
=
tableoffset
+
readushort
(
f
)
3913
local
dimensions
=
{
}
3914
local
factors
=
specification
.
factors
3915
if
factors
then
3916
local
regions
,
deltas
=
readvariationdata
(
f
,
offsettostore
,
factors
)
3917
for
i
=
1
,
nofrecords
do
3918
local
tag
=
readtag
(
f
)
3919
local
var
=
variabletags
[
tag
]
3920
if
var
then
3921
local
outer
=
readushort
(
f
)
3922
local
inner
=
readushort
(
f
)
3923
local
delta
=
deltas
[
outer
+
1
]
3924
if
delta
then
3925
local
d
=
delta
.
deltas
[
inner
+
1
]
3926
if
d
then
3927
local
scales
=
delta
.
scales
3928
local
dd
=
0
3929
for
i
=
1
,
#
scales
do
3930
dd
=
dd
+
scales
[
i
]
*
d
[
i
]
3931
end
3932
var
(
fontdata
,
round
(
dd
)
)
3933
end
3934
end
3935
else
3936
skipshort
(
f
,
2
)
3937
end
3938
if
recordsize
>
8
then
-- 4 + 2 + 2
3939
skipbytes
(
recordsize
-8
)
3940
end
3941
end
3942
end
3943
-- setvariabledata(fontdata,"mregions",regions)
3944
end
3945
end
3946