font-otr.lua /size: 92 Kb    last modification: 2020-07-01 14:35
1
if
not
modules
then
modules
=
{
}
end
modules
[
'
font-otr
'
]
=
{
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
-- When looking into a cid font relates issue in the ff library I wondered if
10
-- it made sense to use Lua to filter the information from the otf and ttf
11
-- files. Quite some ff code relates to special fonts and in practice we only
12
-- use rather normal opentype fonts.
13
--
14
-- The code here is based on the documentation (and examples) at the microsoft
15
-- website. The code will be extended and improved stepwise. After some experiments
16
-- I decided to convert to a format more suitable for the context font handler
17
-- because it makes no sense to rehash all those lookups again.
18
--
19
-- Currently we can use this code for getting basic info about the font, loading
20
-- shapes and loading the extensive table. I'm not sure if I will provide a ff
21
-- compatible output as well (We're not that far from it as currently I can load
22
-- all data reasonable fast.)
23 24
-- We can omit redundant glyphs names i.e. ones that match the agl or
25
-- are just a unicode string but it doesn't save that much. It will be an option
26
-- some day.
27 28
-- Optimizing the widths will be done anyway as it save quite some on a cjk font
29
-- and the existing (old) code if okay.
30 31
-- todo: more messages (only if really needed)
32
--
33
-- considered, in math:
34
--
35
-- start -> first (so we can skip the first same-size one)
36
-- end -> last
37
--
38
-- Widths and weights are kind of messy: for instance lmmonolt has a pfmweight of
39
-- 400 while it should be 300. So, for now we mostly stick to the old compromis.
40 41
-- We don't really need all those language tables so they might be dropped some
42
-- day.
43 44
-- The new reader is faster on some aspects and slower on other. The memory footprint
45
-- is lower. The string reader is a bit faster than the file reader. The new reader
46
-- gives more efficient tables and has bit more analysis. In practice these times are
47
-- not that relevant because we cache. The otf files take a it more time because we
48
-- need to calculate the boundingboxes. In theory the processing of text should be
49
-- somewhat faster especially for complex fonts with many lookups.
50
--
51
-- old new str reader
52
-- lmroman12-regular.otf 0.103 0.203 0.195
53
-- latinmodern-math.otf 0.454 0.768 0.712
54
-- husayni.ttf 1.142 1.526 1.259
55
--
56
-- If there is demand I will consider making a ff compatible table dumper but it's
57
-- probably more fun to provide a way to show features applied.
58 59
-- I experimented a bit with f:readbyte(n) and f:readshort() and so and it is indeed
60
-- faster but it might not be the real bottleneck as we still need to juggle data. It
61
-- is probably more memory efficient as no intermediate strings are involved.
62 63
-- if not characters then
64
-- require("char-def")
65
-- require("char-ini")
66
-- end
67 68
local
next
,
type
,
tonumber
,
rawget
=
next
,
type
,
tonumber
,
rawget
69
local
byte
,
lower
,
char
,
gsub
=
string
.
byte
,
string
.
lower
,
string
.
char
,
string
.
gsub
70
local
fullstrip
=
string
.
fullstrip
71
local
floor
,
round
=
math
.
floor
,
math
.
round
72
local
P
,
R
,
S
,
C
,
Cs
,
Cc
,
Ct
,
Carg
,
Cmt
=
lpeg
.
P
,
lpeg
.
R
,
lpeg
.
S
,
lpeg
.
C
,
lpeg
.
Cs
,
lpeg
.
Cc
,
lpeg
.
Ct
,
lpeg
.
Carg
,
lpeg
.
Cmt
73
local
lpegmatch
=
lpeg
.
match
74
local
rshift
=
bit32
.
rshift
75 76
local
setmetatableindex
=
table
.
setmetatableindex
77
local
sortedkeys
=
table
.
sortedkeys
78
local
sortedhash
=
table
.
sortedhash
79
local
stripstring
=
string
.
nospaces
80
local
utf16_to_utf8_be
=
utf
.
utf16_to_utf8_be
81 82
local
report
=
logs
.
reporter
(
"
otf reader
"
)
83
local
report_cmap
=
logs
.
reporter
(
"
otf reader
"
,
"
cmap
"
)
84 85
local
trace_cmap
=
false
trackers
.
register
(
"
otf.cmap
"
,
function
(
v
)
trace_cmap
=
v
end
)
86
local
trace_cmap_details
=
false
trackers
.
register
(
"
otf.cmap.details
"
,
function
(
v
)
trace_cmap_details
=
v
end
)
87 88
fonts
=
fonts
or
{
}
89
local
handlers
=
fonts
.
handlers
or
{
}
90
fonts
.
handlers
=
handlers
91
local
otf
=
handlers
.
otf
or
{
}
92
handlers
.
otf
=
otf
93
local
readers
=
otf
.
readers
or
{
}
94
otf
.
readers
=
readers
95 96
----- streamreader = utilities.streams -- faster on big files (not true any longer)
97
local
streamreader
=
utilities
.
files
-- faster on identify (also uses less memory)
98
local
streamwriter
=
utilities
.
files
99 100
readers
.
streamreader
=
streamreader
101
readers
.
streamwriter
=
streamwriter
102 103
local
openfile
=
streamreader
.
open
104
local
closefile
=
streamreader
.
close
105
----- skipbytes = streamreader.skip
106
local
setposition
=
streamreader
.
setposition
107
local
skipshort
=
streamreader
.
skipshort
108
local
readbytes
=
streamreader
.
readbytes
109
local
readstring
=
streamreader
.
readstring
110
local
readbyte
=
streamreader
.
readcardinal1
-- 8-bit unsigned integer
111
local
readushort
=
streamreader
.
readcardinal2
-- 16-bit unsigned integer
112
local
readuint
=
streamreader
.
readcardinal3
-- 24-bit unsigned integer
113
local
readulong
=
streamreader
.
readcardinal4
-- 32-bit unsigned integer
114
----- readchar = streamreader.readinteger1 -- 8-bit signed integer
115
local
readshort
=
streamreader
.
readinteger2
-- 16-bit signed integer
116
local
readlong
=
streamreader
.
readinteger4
-- 32-bit unsigned integer
117
local
readfixed
=
streamreader
.
readfixed4
118
local
read2dot14
=
streamreader
.
read2dot14
-- 16-bit signed fixed number with the low 14 bits of fraction (2.14) (F2DOT14)
119
local
readfword
=
readshort
-- 16-bit signed integer that describes a quantity in FUnits
120
local
readufword
=
readushort
-- 16-bit unsigned integer that describes a quantity in FUnits
121
local
readoffset
=
readushort
122
local
readcardinaltable
=
streamreader
.
readcardinaltable
123
local
readintegertable
=
streamreader
.
readintegertable
124 125
function
streamreader
.
readtag
(
f
)
126
return
lower
(
stripstring
(
readstring
(
f
,
4
)
)
)
127
end
128 129
local
short
=
2
130
local
ushort
=
2
131
local
ulong
=
4
132 133
directives
.
register
(
"
fonts.streamreader
"
,
function
(
)
134 135
streamreader
=
utilities
.
streams
136 137
openfile
=
streamreader
.
open
138
closefile
=
streamreader
.
close
139
setposition
=
streamreader
.
setposition
140
skipshort
=
streamreader
.
skipshort
141
readbytes
=
streamreader
.
readbytes
142
readstring
=
streamreader
.
readstring
143
readbyte
=
streamreader
.
readcardinal1
144
readushort
=
streamreader
.
readcardinal2
145
readuint
=
streamreader
.
readcardinal3
146
readulong
=
streamreader
.
readcardinal4
147
readshort
=
streamreader
.
readinteger2
148
readlong
=
streamreader
.
readinteger4
149
readfixed
=
streamreader
.
readfixed4
150
read2dot14
=
streamreader
.
read2dot14
151
readfword
=
readshort
152
readufword
=
readushort
153
readoffset
=
readushort
154
readcardinaltable
=
streamreader
.
readcardinaltable
155
readintegertable
=
streamreader
.
readintegertable
156 157
function
streamreader
.
readtag
(
f
)
158
return
lower
(
stripstring
(
readstring
(
f
,
4
)
)
)
159
end
160 161
end
)
162 163
-- date represented in number of seconds since 12:00 midnight, January 1, 1904. The value is represented as a
164
-- signed 64-bit integer
165 166
local
function
readlongdatetime
(
f
)
167
local
a
,
b
,
c
,
d
,
e
,
f
,
g
,
h
=
readbytes
(
f
,
8
)
168
return
0x100000000
*
d
+
0x1000000
*
e
+
0x10000
*
f
+
0x100
*
g
+
h
169
end
170 171
local
tableversion
=
0
.
004
172
readers
.
tableversion
=
tableversion
173
local
privateoffset
=
fonts
.
constructors
and
fonts
.
constructors
.
privateoffset
or
0xF0000
-- 0x10FFFF
174 175
-- We have quite some data tables. We are somewhat ff compatible with names but as I used
176
-- the information from the microsoft site there can be differences. Eventually I might end
177
-- up with a different ordering and naming.
178 179
local
reservednames
=
{
[
0
]
=
180
"
copyright
"
,
181
"
family
"
,
182
"
subfamily
"
,
183
"
uniqueid
"
,
184
"
fullname
"
,
185
"
version
"
,
186
"
postscriptname
"
,
187
"
trademark
"
,
188
"
manufacturer
"
,
189
"
designer
"
,
190
"
description
"
,
-- descriptor in ff
191
"
vendorurl
"
,
192
"
designerurl
"
,
193
"
license
"
,
194
"
licenseurl
"
,
195
"
reserved
"
,
196
"
typographicfamily
"
,
-- preffamilyname
197
"
typographicsubfamily
"
,
-- prefmodifiers
198
"
compatiblefullname
"
,
-- for mac
199
"
sampletext
"
,
200
"
cidfindfontname
"
,
201
"
wwsfamily
"
,
202
"
wwssubfamily
"
,
203
"
lightbackgroundpalette
"
,
204
"
darkbackgroundpalette
"
,
205
"
variationspostscriptnameprefix
"
,
206
}
207 208
-- more at: https://www.microsoft.com/typography/otspec/name.htm
209 210
-- setmetatableindex(reservednames,function(t,k)
211
-- local v = "name_" .. k
212
-- t[k] = v
213
-- return v
214
-- end)
215 216
local
platforms
=
{
[
0
]
=
217
"
unicode
"
,
218
"
macintosh
"
,
219
"
iso
"
,
220
"
windows
"
,
221
"
custom
"
,
222
}
223 224
local
encodings
=
{
225
-- these stay:
226
unicode
=
{
[
0
]
=
227
"
unicode 1.0 semantics
"
,
228
"
unicode 1.1 semantics
"
,
229
"
iso/iec 10646
"
,
230
"
unicode 2.0 bmp
"
,
-- cmap subtable formats 0, 4, 6
231
"
unicode 2.0 full
"
,
-- cmap subtable formats 0, 4, 6, 10, 12
232
"
unicode variation sequences
"
,
-- cmap subtable format 14).
233
"
unicode full repertoire
"
,
-- cmap subtable formats 0, 4, 6, 10, 12, 13
234
}
,
235
-- these can go:
236
macintosh
=
{
[
0
]
=
237
"
roman
"
,
"
japanese
"
,
"
chinese (traditional)
"
,
"
korean
"
,
"
arabic
"
,
"
hebrew
"
,
"
greek
"
,
"
russian
"
,
238
"
rsymbol
"
,
"
devanagari
"
,
"
gurmukhi
"
,
"
gujarati
"
,
"
oriya
"
,
"
bengali
"
,
"
tamil
"
,
"
telugu
"
,
"
kannada
"
,
239
"
malayalam
"
,
"
sinhalese
"
,
"
burmese
"
,
"
khmer
"
,
"
thai
"
,
"
laotian
"
,
"
georgian
"
,
"
armenian
"
,
240
"
chinese (simplified)
"
,
"
tibetan
"
,
"
mongolian
"
,
"
geez
"
,
"
slavic
"
,
"
vietnamese
"
,
"
sindhi
"
,
241
"
uninterpreted
"
,
242
}
,
243
-- these stay:
244
iso
=
{
[
0
]
=
245
"
7-bit ascii
"
,
246
"
iso 10646
"
,
247
"
iso 8859-1
"
,
248
}
,
249
-- these stay:
250
windows
=
{
[
0
]
=
251
"
symbol
"
,
252
"
unicode bmp
"
,
-- this is utf16
253
"
shiftjis
"
,
254
"
prc
"
,
255
"
big5
"
,
256
"
wansung
"
,
257
"
johab
"
,
258
"
reserved 7
"
,
259
"
reserved 8
"
,
260
"
reserved 9
"
,
261
"
unicode ucs-4
"
,
262
}
,
263
custom
=
{
264
--custom: 0-255 : otf windows nt compatibility mapping
265
}
266
}
267 268
local
decoders
=
{
269
unicode
=
{
}
,
270
macintosh
=
{
}
,
271
iso
=
{
}
,
272
windows
=
{
273
-- maybe always utf16
274
[
"
unicode semantics
"
]
=
utf16_to_utf8_be
,
275
[
"
unicode bmp
"
]
=
utf16_to_utf8_be
,
276
[
"
unicode full
"
]
=
utf16_to_utf8_be
,
277
[
"
unicode 1.0 semantics
"
]
=
utf16_to_utf8_be
,
278
[
"
unicode 1.1 semantics
"
]
=
utf16_to_utf8_be
,
279
[
"
unicode 2.0 bmp
"
]
=
utf16_to_utf8_be
,
280
[
"
unicode 2.0 full
"
]
=
utf16_to_utf8_be
,
281
[
"
unicode variation sequences
"
]
=
utf16_to_utf8_be
,
282
[
"
unicode full repertoire
"
]
=
utf16_to_utf8_be
,
283
}
,
284
custom
=
{
}
,
285
}
286 287
-- This is bit over the top as we can just look for either windows, unicode or macintosh
288
-- names (in that order). A font with no english name is probably a weird one anyway.
289 290
local
languages
=
{
291
-- these stay:
292
unicode
=
{
293
[
0
]
=
"
english
"
,
294
}
,
295
-- english can stay:
296
macintosh
=
{
297
[
0
]
=
"
english
"
,
298
-- [ 1] = "french",
299
-- [ 2] = "german",
300
-- [ 3] = "italian",
301
-- [ 4] = "dutch",
302
-- [ 5] = "swedish",
303
-- [ 6] = "spanish",
304
-- [ 7] = "danish",
305
-- [ 8] = "portuguese",
306
-- [ 9] = "norwegian",
307
-- [ 10] = "hebrew",
308
-- [ 11] = "japanese",
309
-- [ 12] = "arabic",
310
-- [ 13] = "finnish",
311
-- [ 14] = "greek",
312
-- [ 15] = "icelandic",
313
-- [ 16] = "maltese",
314
-- [ 17] = "turkish",
315
-- [ 18] = "croatian",
316
-- [ 19] = "chinese (traditional)",
317
-- [ 20] = "urdu",
318
-- [ 21] = "hindi",
319
-- [ 22] = "thai",
320
-- [ 23] = "korean",
321
-- [ 24] = "lithuanian",
322
-- [ 25] = "polish",
323
-- [ 26] = "hungarian",
324
-- [ 27] = "estonian",
325
-- [ 28] = "latvian",
326
-- [ 29] = "sami",
327
-- [ 30] = "faroese",
328
-- [ 31] = "farsi/persian",
329
-- [ 32] = "russian",
330
-- [ 33] = "chinese (simplified)",
331
-- [ 34] = "flemish",
332
-- [ 35] = "irish gaelic",
333
-- [ 36] = "albanian",
334
-- [ 37] = "romanian",
335
-- [ 38] = "czech",
336
-- [ 39] = "slovak",
337
-- [ 40] = "slovenian",
338
-- [ 41] = "yiddish",
339
-- [ 42] = "serbian",
340
-- [ 43] = "macedonian",
341
-- [ 44] = "bulgarian",
342
-- [ 45] = "ukrainian",
343
-- [ 46] = "byelorussian",
344
-- [ 47] = "uzbek",
345
-- [ 48] = "kazakh",
346
-- [ 49] = "azerbaijani (cyrillic script)",
347
-- [ 50] = "azerbaijani (arabic script)",
348
-- [ 51] = "armenian",
349
-- [ 52] = "georgian",
350
-- [ 53] = "moldavian",
351
-- [ 54] = "kirghiz",
352
-- [ 55] = "tajiki",
353
-- [ 56] = "turkmen",
354
-- [ 57] = "mongolian (mongolian script)",
355
-- [ 58] = "mongolian (cyrillic script)",
356
-- [ 59] = "pashto",
357
-- [ 60] = "kurdish",
358
-- [ 61] = "kashmiri",
359
-- [ 62] = "sindhi",
360
-- [ 63] = "tibetan",
361
-- [ 64] = "nepali",
362
-- [ 65] = "sanskrit",
363
-- [ 66] = "marathi",
364
-- [ 67] = "bengali",
365
-- [ 68] = "assamese",
366
-- [ 69] = "gujarati",
367
-- [ 70] = "punjabi",
368
-- [ 71] = "oriya",
369
-- [ 72] = "malayalam",
370
-- [ 73] = "kannada",
371
-- [ 74] = "tamil",
372
-- [ 75] = "telugu",
373
-- [ 76] = "sinhalese",
374
-- [ 77] = "burmese",
375
-- [ 78] = "khmer",
376
-- [ 79] = "lao",
377
-- [ 80] = "vietnamese",
378
-- [ 81] = "indonesian",
379
-- [ 82] = "tagalong",
380
-- [ 83] = "malay (roman script)",
381
-- [ 84] = "malay (arabic script)",
382
-- [ 85] = "amharic",
383
-- [ 86] = "tigrinya",
384
-- [ 87] = "galla",
385
-- [ 88] = "somali",
386
-- [ 89] = "swahili",
387
-- [ 90] = "kinyarwanda/ruanda",
388
-- [ 91] = "rundi",
389
-- [ 92] = "nyanja/chewa",
390
-- [ 93] = "malagasy",
391
-- [ 94] = "esperanto",
392
-- [128] = "welsh",
393
-- [129] = "basque",
394
-- [130] = "catalan",
395
-- [131] = "latin",
396
-- [132] = "quenchua",
397
-- [133] = "guarani",
398
-- [134] = "aymara",
399
-- [135] = "tatar",
400
-- [136] = "uighur",
401
-- [137] = "dzongkha",
402
-- [138] = "javanese (roman script)",
403
-- [139] = "sundanese (roman script)",
404
-- [140] = "galician",
405
-- [141] = "afrikaans",
406
-- [142] = "breton",
407
-- [143] = "inuktitut",
408
-- [144] = "scottish gaelic",
409
-- [145] = "manx gaelic",
410
-- [146] = "irish gaelic (with dot above)",
411
-- [147] = "tongan",
412
-- [148] = "greek (polytonic)",
413
-- [149] = "greenlandic",
414
-- [150] = "azerbaijani (roman script)",
415
}
,
416
-- these can stay:
417
iso
=
{
418
}
,
419
-- english can stay:
420
windows
=
{
421
-- [0x0436] = "afrikaans - south africa",
422
-- [0x041c] = "albanian - albania",
423
-- [0x0484] = "alsatian - france",
424
-- [0x045e] = "amharic - ethiopia",
425
-- [0x1401] = "arabic - algeria",
426
-- [0x3c01] = "arabic - bahrain",
427
-- [0x0c01] = "arabic - egypt",
428
-- [0x0801] = "arabic - iraq",
429
-- [0x2c01] = "arabic - jordan",
430
-- [0x3401] = "arabic - kuwait",
431
-- [0x3001] = "arabic - lebanon",
432
-- [0x1001] = "arabic - libya",
433
-- [0x1801] = "arabic - morocco",
434
-- [0x2001] = "arabic - oman",
435
-- [0x4001] = "arabic - qatar",
436
-- [0x0401] = "arabic - saudi arabia",
437
-- [0x2801] = "arabic - syria",
438
-- [0x1c01] = "arabic - tunisia",
439
-- [0x3801] = "arabic - u.a.e.",
440
-- [0x2401] = "arabic - yemen",
441
-- [0x042b] = "armenian - armenia",
442
-- [0x044d] = "assamese - india",
443
-- [0x082c] = "azeri (cyrillic) - azerbaijan",
444
-- [0x042c] = "azeri (latin) - azerbaijan",
445
-- [0x046d] = "bashkir - russia",
446
-- [0x042d] = "basque - basque",
447
-- [0x0423] = "belarusian - belarus",
448
-- [0x0845] = "bengali - bangladesh",
449
-- [0x0445] = "bengali - india",
450
-- [0x201a] = "bosnian (cyrillic) - bosnia and herzegovina",
451
-- [0x141a] = "bosnian (latin) - bosnia and herzegovina",
452
-- [0x047e] = "breton - france",
453
-- [0x0402] = "bulgarian - bulgaria",
454
-- [0x0403] = "catalan - catalan",
455
-- [0x0c04] = "chinese - hong kong s.a.r.",
456
-- [0x1404] = "chinese - macao s.a.r.",
457
-- [0x0804] = "chinese - people's republic of china",
458
-- [0x1004] = "chinese - singapore",
459
-- [0x0404] = "chinese - taiwan",
460
-- [0x0483] = "corsican - france",
461
-- [0x041a] = "croatian - croatia",
462
-- [0x101a] = "croatian (latin) - bosnia and herzegovina",
463
-- [0x0405] = "czech - czech republic",
464
-- [0x0406] = "danish - denmark",
465
-- [0x048c] = "dari - afghanistan",
466
-- [0x0465] = "divehi - maldives",
467
-- [0x0813] = "dutch - belgium",
468
-- [0x0413] = "dutch - netherlands",
469
-- [0x0c09] = "english - australia",
470
-- [0x2809] = "english - belize",
471
-- [0x1009] = "english - canada",
472
-- [0x2409] = "english - caribbean",
473
-- [0x4009] = "english - india",
474
-- [0x1809] = "english - ireland",
475
-- [0x2009] = "english - jamaica",
476
-- [0x4409] = "english - malaysia",
477
-- [0x1409] = "english - new zealand",
478
-- [0x3409] = "english - republic of the philippines",
479
-- [0x4809] = "english - singapore",
480
-- [0x1c09] = "english - south africa",
481
-- [0x2c09] = "english - trinidad and tobago",
482
-- [0x0809] = "english - united kingdom",
483
[
0x0409
]
=
"
english - united states
"
,
484
-- [0x3009] = "english - zimbabwe",
485
-- [0x0425] = "estonian - estonia",
486
-- [0x0438] = "faroese - faroe islands",
487
-- [0x0464] = "filipino - philippines",
488
-- [0x040b] = "finnish - finland",
489
-- [0x080c] = "french - belgium",
490
-- [0x0c0c] = "french - canada",
491
-- [0x040c] = "french - france",
492
-- [0x140c] = "french - luxembourg",
493
-- [0x180c] = "french - principality of monoco",
494
-- [0x100c] = "french - switzerland",
495
-- [0x0462] = "frisian - netherlands",
496
-- [0x0456] = "galician - galician",
497
-- [0x0437] = "georgian -georgia",
498
-- [0x0c07] = "german - austria",
499
-- [0x0407] = "german - germany",
500
-- [0x1407] = "german - liechtenstein",
501
-- [0x1007] = "german - luxembourg",
502
-- [0x0807] = "german - switzerland",
503
-- [0x0408] = "greek - greece",
504
-- [0x046f] = "greenlandic - greenland",
505
-- [0x0447] = "gujarati - india",
506
-- [0x0468] = "hausa (latin) - nigeria",
507
-- [0x040d] = "hebrew - israel",
508
-- [0x0439] = "hindi - india",
509
-- [0x040e] = "hungarian - hungary",
510
-- [0x040f] = "icelandic - iceland",
511
-- [0x0470] = "igbo - nigeria",
512
-- [0x0421] = "indonesian - indonesia",
513
-- [0x045d] = "inuktitut - canada",
514
-- [0x085d] = "inuktitut (latin) - canada",
515
-- [0x083c] = "irish - ireland",
516
-- [0x0434] = "isixhosa - south africa",
517
-- [0x0435] = "isizulu - south africa",
518
-- [0x0410] = "italian - italy",
519
-- [0x0810] = "italian - switzerland",
520
-- [0x0411] = "japanese - japan",
521
-- [0x044b] = "kannada - india",
522
-- [0x043f] = "kazakh - kazakhstan",
523
-- [0x0453] = "khmer - cambodia",
524
-- [0x0486] = "k'iche - guatemala",
525
-- [0x0487] = "kinyarwanda - rwanda",
526
-- [0x0441] = "kiswahili - kenya",
527
-- [0x0457] = "konkani - india",
528
-- [0x0412] = "korean - korea",
529
-- [0x0440] = "kyrgyz - kyrgyzstan",
530
-- [0x0454] = "lao - lao p.d.r.",
531
-- [0x0426] = "latvian - latvia",
532
-- [0x0427] = "lithuanian - lithuania",
533
-- [0x082e] = "lower sorbian - germany",
534
-- [0x046e] = "luxembourgish - luxembourg",
535
-- [0x042f] = "macedonian (fyrom) - former yugoslav republic of macedonia",
536
-- [0x083e] = "malay - brunei darussalam",
537
-- [0x043e] = "malay - malaysia",
538
-- [0x044c] = "malayalam - india",
539
-- [0x043a] = "maltese - malta",
540
-- [0x0481] = "maori - new zealand",
541
-- [0x047a] = "mapudungun - chile",
542
-- [0x044e] = "marathi - india",
543
-- [0x047c] = "mohawk - mohawk",
544
-- [0x0450] = "mongolian (cyrillic) - mongolia",
545
-- [0x0850] = "mongolian (traditional) - people's republic of china",
546
-- [0x0461] = "nepali - nepal",
547
-- [0x0414] = "norwegian (bokmal) - norway",
548
-- [0x0814] = "norwegian (nynorsk) - norway",
549
-- [0x0482] = "occitan - france",
550
-- [0x0448] = "odia (formerly oriya) - india",
551
-- [0x0463] = "pashto - afghanistan",
552
-- [0x0415] = "polish - poland",
553
-- [0x0416] = "portuguese - brazil",
554
-- [0x0816] = "portuguese - portugal",
555
-- [0x0446] = "punjabi - india",
556
-- [0x046b] = "quechua - bolivia",
557
-- [0x086b] = "quechua - ecuador",
558
-- [0x0c6b] = "quechua - peru",
559
-- [0x0418] = "romanian - romania",
560
-- [0x0417] = "romansh - switzerland",
561
-- [0x0419] = "russian - russia",
562
-- [0x243b] = "sami (inari) - finland",
563
-- [0x103b] = "sami (lule) - norway",
564
-- [0x143b] = "sami (lule) - sweden",
565
-- [0x0c3b] = "sami (northern) - finland",
566
-- [0x043b] = "sami (northern) - norway",
567
-- [0x083b] = "sami (northern) - sweden",
568
-- [0x203b] = "sami (skolt) - finland",
569
-- [0x183b] = "sami (southern) - norway",
570
-- [0x1c3b] = "sami (southern) - sweden",
571
-- [0x044f] = "sanskrit - india",
572
-- [0x1c1a] = "serbian (cyrillic) - bosnia and herzegovina",
573
-- [0x0c1a] = "serbian (cyrillic) - serbia",
574
-- [0x181a] = "serbian (latin) - bosnia and herzegovina",
575
-- [0x081a] = "serbian (latin) - serbia",
576
-- [0x046c] = "sesotho sa leboa - south africa",
577
-- [0x0432] = "setswana - south africa",
578
-- [0x045b] = "sinhala - sri lanka",
579
-- [0x041b] = "slovak - slovakia",
580
-- [0x0424] = "slovenian - slovenia",
581
-- [0x2c0a] = "spanish - argentina",
582
-- [0x400a] = "spanish - bolivia",
583
-- [0x340a] = "spanish - chile",
584
-- [0x240a] = "spanish - colombia",
585
-- [0x140a] = "spanish - costa rica",
586
-- [0x1c0a] = "spanish - dominican republic",
587
-- [0x300a] = "spanish - ecuador",
588
-- [0x440a] = "spanish - el salvador",
589
-- [0x100a] = "spanish - guatemala",
590
-- [0x480a] = "spanish - honduras",
591
-- [0x080a] = "spanish - mexico",
592
-- [0x4c0a] = "spanish - nicaragua",
593
-- [0x180a] = "spanish - panama",
594
-- [0x3c0a] = "spanish - paraguay",
595
-- [0x280a] = "spanish - peru",
596
-- [0x500a] = "spanish - puerto rico",
597
-- [0x0c0a] = "spanish (modern sort) - spain",
598
-- [0x040a] = "spanish (traditional sort) - spain",
599
-- [0x540a] = "spanish - united states",
600
-- [0x380a] = "spanish - uruguay",
601
-- [0x200a] = "spanish - venezuela",
602
-- [0x081d] = "sweden - finland",
603
-- [0x041d] = "swedish - sweden",
604
-- [0x045a] = "syriac - syria",
605
-- [0x0428] = "tajik (cyrillic) - tajikistan",
606
-- [0x085f] = "tamazight (latin) - algeria",
607
-- [0x0449] = "tamil - india",
608
-- [0x0444] = "tatar - russia",
609
-- [0x044a] = "telugu - india",
610
-- [0x041e] = "thai - thailand",
611
-- [0x0451] = "tibetan - prc",
612
-- [0x041f] = "turkish - turkey",
613
-- [0x0442] = "turkmen - turkmenistan",
614
-- [0x0480] = "uighur - prc",
615
-- [0x0422] = "ukrainian - ukraine",
616
-- [0x042e] = "upper sorbian - germany",
617
-- [0x0420] = "urdu - islamic republic of pakistan",
618
-- [0x0843] = "uzbek (cyrillic) - uzbekistan",
619
-- [0x0443] = "uzbek (latin) - uzbekistan",
620
-- [0x042a] = "vietnamese - vietnam",
621
-- [0x0452] = "welsh - united kingdom",
622
-- [0x0488] = "wolof - senegal",
623
-- [0x0485] = "yakut - russia",
624
-- [0x0478] = "yi - prc",
625
-- [0x046a] = "yoruba - nigeria",
626
}
,
627
custom
=
{
628
}
,
629
}
630 631
local
standardromanencoding
=
{
[
0
]
=
-- taken from wikipedia
632
"
notdef
"
,
"
.null
"
,
"
nonmarkingreturn
"
,
"
space
"
,
"
exclam
"
,
"
quotedbl
"
,
633
"
numbersign
"
,
"
dollar
"
,
"
percent
"
,
"
ampersand
"
,
"
quotesingle
"
,
"
parenleft
"
,
634
"
parenright
"
,
"
asterisk
"
,
"
plus
"
,
"
comma
"
,
"
hyphen
"
,
"
period
"
,
"
slash
"
,
635
"
zero
"
,
"
one
"
,
"
two
"
,
"
three
"
,
"
four
"
,
"
five
"
,
"
six
"
,
"
seven
"
,
"
eight
"
,
636
"
nine
"
,
"
colon
"
,
"
semicolon
"
,
"
less
"
,
"
equal
"
,
"
greater
"
,
"
question
"
,
"
at
"
,
637
"
A
"
,
"
B
"
,
"
C
"
,
"
D
"
,
"
E
"
,
"
F
"
,
"
G
"
,
"
H
"
,
"
I
"
,
"
J
"
,
"
K
"
,
"
L
"
,
"
M
"
,
"
N
"
,
"
O
"
,
638
"
P
"
,
"
Q
"
,
"
R
"
,
"
S
"
,
"
T
"
,
"
U
"
,
"
V
"
,
"
W
"
,
"
X
"
,
"
Y
"
,
"
Z
"
,
"
bracketleft
"
,
639
"
backslash
"
,
"
bracketright
"
,
"
asciicircum
"
,
"
underscore
"
,
"
grave
"
,
"
a
"
,
"
b
"
,
640
"
c
"
,
"
d
"
,
"
e
"
,
"
f
"
,
"
g
"
,
"
h
"
,
"
i
"
,
"
j
"
,
"
k
"
,
"
l
"
,
"
m
"
,
"
n
"
,
"
o
"
,
"
p
"
,
"
q
"
,
641
"
r
"
,
"
s
"
,
"
t
"
,
"
u
"
,
"
v
"
,
"
w
"
,
"
x
"
,
"
y
"
,
"
z
"
,
"
braceleft
"
,
"
bar
"
,
642
"
braceright
"
,
"
asciitilde
"
,
"
Adieresis
"
,
"
Aring
"
,
"
Ccedilla
"
,
"
Eacute
"
,
643
"
Ntilde
"
,
"
Odieresis
"
,
"
Udieresis
"
,
"
aacute
"
,
"
agrave
"
,
"
acircumflex
"
,
644
"
adieresis
"
,
"
atilde
"
,
"
aring
"
,
"
ccedilla
"
,
"
eacute
"
,
"
egrave
"
,
645
"
ecircumflex
"
,
"
edieresis
"
,
"
iacute
"
,
"
igrave
"
,
"
icircumflex
"
,
"
idieresis
"
,
646
"
ntilde
"
,
"
oacute
"
,
"
ograve
"
,
"
ocircumflex
"
,
"
odieresis
"
,
"
otilde
"
,
"
uacute
"
,
647
"
ugrave
"
,
"
ucircumflex
"
,
"
udieresis
"
,
"
dagger
"
,
"
degree
"
,
"
cent
"
,
"
sterling
"
,
648
"
section
"
,
"
bullet
"
,
"
paragraph
"
,
"
germandbls
"
,
"
registered
"
,
"
copyright
"
,
649
"
trademark
"
,
"
acute
"
,
"
dieresis
"
,
"
notequal
"
,
"
AE
"
,
"
Oslash
"
,
"
infinity
"
,
650
"
plusminus
"
,
"
lessequal
"
,
"
greaterequal
"
,
"
yen
"
,
"
mu
"
,
"
partialdiff
"
,
651
"
summation
"
,
"
product
"
,
"
pi
"
,
"
integral
"
,
"
ordfeminine
"
,
"
ordmasculine
"
,
652
"
Omega
"
,
"
ae
"
,
"
oslash
"
,
"
questiondown
"
,
"
exclamdown
"
,
"
logicalnot
"
,
653
"
radical
"
,
"
florin
"
,
"
approxequal
"
,
"
Delta
"
,
"
guillemotleft
"
,
654
"
guillemotright
"
,
"
ellipsis
"
,
"
nonbreakingspace
"
,
"
Agrave
"
,
"
Atilde
"
,
655
"
Otilde
"
,
"
OE
"
,
"
oe
"
,
"
endash
"
,
"
emdash
"
,
"
quotedblleft
"
,
"
quotedblright
"
,
656
"
quoteleft
"
,
"
quoteright
"
,
"
divide
"
,
"
lozenge
"
,
"
ydieresis
"
,
"
Ydieresis
"
,
657
"
fraction
"
,
"
currency
"
,
"
guilsinglleft
"
,
"
guilsinglright
"
,
"
fi
"
,
"
fl
"
,
658
"
daggerdbl
"
,
"
periodcentered
"
,
"
quotesinglbase
"
,
"
quotedblbase
"
,
659
"
perthousand
"
,
"
Acircumflex
"
,
"
Ecircumflex
"
,
"
Aacute
"
,
"
Edieresis
"
,
"
Egrave
"
,
660
"
Iacute
"
,
"
Icircumflex
"
,
"
Idieresis
"
,
"
Igrave
"
,
"
Oacute
"
,
"
Ocircumflex
"
,
661
"
apple
"
,
"
Ograve
"
,
"
Uacute
"
,
"
Ucircumflex
"
,
"
Ugrave
"
,
"
dotlessi
"
,
662
"
circumflex
"
,
"
tilde
"
,
"
macron
"
,
"
breve
"
,
"
dotaccent
"
,
"
ring
"
,
"
cedilla
"
,
663
"
hungarumlaut
"
,
"
ogonek
"
,
"
caron
"
,
"
Lslash
"
,
"
lslash
"
,
"
Scaron
"
,
"
scaron
"
,
664
"
Zcaron
"
,
"
zcaron
"
,
"
brokenbar
"
,
"
Eth
"
,
"
eth
"
,
"
Yacute
"
,
"
yacute
"
,
"
Thorn
"
,
665
"
thorn
"
,
"
minus
"
,
"
multiply
"
,
"
onesuperior
"
,
"
twosuperior
"
,
"
threesuperior
"
,
666
"
onehalf
"
,
"
onequarter
"
,
"
threequarters
"
,
"
franc
"
,
"
Gbreve
"
,
"
gbreve
"
,
667
"
Idotaccent
"
,
"
Scedilla
"
,
"
scedilla
"
,
"
Cacute
"
,
"
cacute
"
,
"
Ccaron
"
,
"
ccaron
"
,
668
"
dcroat
"
,
669
}
670 671
local
weights
=
{
672
[
100
]
=
"
thin
"
,
673
[
200
]
=
"
extralight
"
,
674
[
300
]
=
"
light
"
,
675
[
400
]
=
"
normal
"
,
676
[
500
]
=
"
medium
"
,
677
[
600
]
=
"
semibold
"
,
-- demi demibold
678
[
700
]
=
"
bold
"
,
679
[
800
]
=
"
extrabold
"
,
680
[
900
]
=
"
black
"
,
681
}
682 683
local
widths
=
{
684
[
1
]
=
"
ultracondensed
"
,
685
[
2
]
=
"
extracondensed
"
,
686
[
3
]
=
"
condensed
"
,
687
[
4
]
=
"
semicondensed
"
,
688
[
5
]
=
"
normal
"
,
689
[
6
]
=
"
semiexpanded
"
,
690
[
7
]
=
"
expanded
"
,
691
[
8
]
=
"
extraexpanded
"
,
692
[
9
]
=
"
ultraexpanded
"
,
693
}
694 695
setmetatableindex
(
weights
,
function
(
t
,
k
)
696
local
r
=
floor
(
(
k
+
50
)
/
100
)
*
100
697
local
v
=
(
r
>
900
and
"
black
"
)
or
rawget
(
t
,
r
)
or
"
normal
"
698
return
v
699
end
)
700 701
setmetatableindex
(
widths
,
function
(
t
,
k
)
702
return
"
normal
"
703
end
)
704 705
local
panoseweights
=
{
706
[
0
]
=
"
normal
"
,
707
[
1
]
=
"
normal
"
,
708
[
2
]
=
"
verylight
"
,
709
[
3
]
=
"
light
"
,
710
[
4
]
=
"
thin
"
,
711
[
5
]
=
"
book
"
,
712
[
6
]
=
"
medium
"
,
713
[
7
]
=
"
demi
"
,
714
[
8
]
=
"
bold
"
,
715
[
9
]
=
"
heavy
"
,
716
[
10
]
=
"
black
"
,
717
}
718 719
local
panosewidths
=
{
720
[
0
]
=
"
normal
"
,
721
[
1
]
=
"
normal
"
,
722
[
2
]
=
"
normal
"
,
723
[
3
]
=
"
normal
"
,
724
[
4
]
=
"
normal
"
,
725
[
5
]
=
"
expanded
"
,
726
[
6
]
=
"
condensed
"
,
727
[
7
]
=
"
veryexpanded
"
,
728
[
8
]
=
"
verycondensed
"
,
729
[
9
]
=
"
monospaced
"
,
730
}
731 732
-- We implement a reader per table.
733 734
-- helper
735 736
local
helpers
=
{
}
737
readers
.
helpers
=
helpers
738 739
local
function
gotodatatable
(
f
,
fontdata
,
tag
,
criterium
)
740
if
criterium
and
f
then
741
local
tables
=
fontdata
.
tables
742
if
tables
then
743
local
datatable
=
tables
[
tag
]
744
if
datatable
then
745
local
tableoffset
=
datatable
.
offset
746
setposition
(
f
,
tableoffset
)
747
return
tableoffset
748
end
749
else
750
report
(
"
no tables
"
)
751
end
752
end
753
end
754 755
local
function
reportskippedtable
(
f
,
fontdata
,
tag
,
criterium
)
756
if
criterium
and
f
then
757
local
tables
=
fontdata
.
tables
758
if
tables
then
759
local
datatable
=
tables
[
tag
]
760
if
datatable
then
761
report
(
"
loading of table %a skipped
"
,
tag
)
762
end
763
else
764
report
(
"
no tables
"
)
765
end
766
end
767
end
768 769
local
function
setvariabledata
(
fontdata
,
tag
,
data
)
770
local
variabledata
=
fontdata
.
variabledata
771
if
variabledata
then
772
variabledata
[
tag
]
=
data
773
else
774
fontdata
.
variabledata
=
{
[
tag
]
=
data
}
775
end
776
end
777 778
helpers
.
gotodatatable
=
gotodatatable
779
helpers
.
setvariabledata
=
setvariabledata
780
helpers
.
reportskippedtable
=
reportskippedtable
781 782
-- The name table is probably the first one to load. After all this one provides
783
-- useful information about what we deal with. The complication is that we need
784
-- to filter the best one available.
785 786
local
platformnames
=
{
787
postscriptname
=
true
,
788
fullname
=
true
,
789
family
=
true
,
790
subfamily
=
true
,
791
typographicfamily
=
true
,
792
typographicsubfamily
=
true
,
793
compatiblefullname
=
true
,
794
}
795 796
local
platformextras
=
{
797
uniqueid
=
true
,
798
version
=
true
,
799
copyright
=
true
,
800
license
=
true
,
801
licenseurl
=
true
,
802
manufacturer
=
true
,
803
vendorurl
=
true
,
804
}
805 806
function
readers
.
name
(
f
,
fontdata
,
specification
)
807
local
tableoffset
=
gotodatatable
(
f
,
fontdata
,
"
name
"
,
true
)
808
if
tableoffset
then
809
local
format
=
readushort
(
f
)
810
local
nofnames
=
readushort
(
f
)
811
local
offset
=
readushort
(
f
)
812
-- we can also provide a raw list as extra, todo as option
813
local
start
=
tableoffset
+
offset
814
local
namelists
=
{
815
unicode
=
{
}
,
816
windows
=
{
}
,
817
macintosh
=
{
}
,
818
-- iso = { },
819
-- windows = { },
820
}
821
for
i
=
1
,
nofnames
do
822
local
platform
=
platforms
[
readushort
(
f
)
]
823
if
platform
then
824
local
namelist
=
namelists
[
platform
]
825
if
namelist
then
826
local
encoding
=
readushort
(
f
)
827
local
language
=
readushort
(
f
)
828
local
encodings
=
encodings
[
platform
]
829
local
languages
=
languages
[
platform
]
830
if
encodings
and
languages
then
831
local
encoding
=
encodings
[
encoding
]
832
local
language
=
languages
[
language
]
833
if
encoding
and
language
then
834
local
index
=
readushort
(
f
)
835
local
name
=
reservednames
[
index
]
836
namelist
[
#
namelist
+
1
]
=
{
837
platform
=
platform
,
838
encoding
=
encoding
,
839
language
=
language
,
840
name
=
name
,
841
index
=
index
,
842
length
=
readushort
(
f
)
,
843
offset
=
start
+
readushort
(
f
)
,
844
}
845
else
846
skipshort
(
f
,
3
)
847
end
848
else
849
skipshort
(
f
,
3
)
850
end
851
else
852
skipshort
(
f
,
5
)
853
end
854
else
855
skipshort
(
f
,
5
)
856
end
857
end
858
-- if format == 1 then
859
-- local noftags = readushort(f)
860
-- for i=1,noftags do
861
-- local length = readushort(f)
862
-- local offset = readushort(f)
863
-- end
864
-- end
865
--
866
-- we need to choose one we like, for instance an unicode one
867
--
868
local
names
=
{
}
869
local
done
=
{
}
870
local
extras
=
{
}
871
--
872
-- there is quite some logic in ff ... hard to follow so we start simple
873
-- and extend when we run into it (todo: proper reverse hash) .. we're only
874
-- interested in english anyway
875
--
876
local
function
decoded
(
platform
,
encoding
,
content
)
877
local
decoder
=
decoders
[
platform
]
878
if
decoder
then
879
decoder
=
decoder
[
encoding
]
880
end
881
if
decoder
then
882
return
decoder
(
content
)
883
else
884
return
content
885
end
886
end
887
--
888
local
function
filter
(
platform
,
e
,
l
)
889
local
namelist
=
namelists
[
platform
]
890
for
i
=
1
,
#
namelist
do
891
local
name
=
namelist
[
i
]
892
local
nametag
=
name
.
name
893
local
index
=
name
.
index
894
if
not
done
[
nametag
or
i
]
then
895
local
encoding
=
name
.
encoding
896
local
language
=
name
.
language
897
if
(
not
e
or
encoding
=
=
e
)
and
(
not
l
or
language
=
=
l
)
then
898
setposition
(
f
,
name
.
offset
)
899
local
content
=
decoded
(
platform
,
encoding
,
readstring
(
f
,
name
.
length
)
)
900
if
nametag
then
901
names
[
nametag
]
=
{
902
content
=
content
,
903
platform
=
platform
,
904
encoding
=
encoding
,
905
language
=
language
,
906
}
907
end
908
extras
[
index
]
=
content
909
done
[
nametag
or
i
]
=
true
910
end
911
end
912
end
913
end
914
--
915
filter
(
"
windows
"
,
"
unicode bmp
"
,
"
english - united states
"
)
916
-- filter("unicode") -- which one ?
917
filter
(
"
macintosh
"
,
"
roman
"
,
"
english
"
)
918
filter
(
"
windows
"
)
919
filter
(
"
macintosh
"
)
920
filter
(
"
unicode
"
)
921
--
922
fontdata
.
names
=
names
923
fontdata
.
extras
=
extras
924
--
925
if
specification
.
platformnames
then
926
local
collected
=
{
}
927
local
platformextras
=
specification
.
platformextras
and
platformextras
928
for
platform
,
namelist
in
next
,
namelists
do
929
local
filtered
=
false
930
for
i
=
1
,
#
namelist
do
931
local
entry
=
namelist
[
i
]
932
local
name
=
entry
.
name
933
if
platformnames
[
name
]
or
(
platformextras
and
platformextras
[
name
]
)
then
934
setposition
(
f
,
entry
.
offset
)
935
local
content
=
decoded
(
platform
,
entry
.
encoding
,
readstring
(
f
,
entry
.
length
)
)
936
if
filtered
then
937
filtered
[
name
]
=
content
938
else
939
filtered
=
{
[
name
]
=
content
}
940
end
941
end
942
end
943
if
filtered
then
944
collected
[
platform
]
=
filtered
945
end
946
end
947
fontdata
.
platformnames
=
collected
948
end
949
else
950
fontdata
.
names
=
{
}
951
end
952
end
953 954
----- validutf = lpeg.patterns.utf8character^0 * P(-1)
955
local
validutf
=
lpeg
.
patterns
.
validutf8
956 957
local
function
getname
(
fontdata
,
key
)
958
local
names
=
fontdata
.
names
959
if
names
then
960
local
value
=
names
[
key
]
961
if
value
then
962
local
content
=
value
.
content
963
return
lpegmatch
(
validutf
,
content
)
and
content
or
nil
964
end
965
end
966
end
967 968
-- This table is an original windows (with its precursor os/2) table. In ff this one is
969
-- part of the pfminfo table but here we keep it separate (for now). We will create a
970
-- properties table afterwards.
971 972
readers
[
"
os/2
"
]
=
function
(
f
,
fontdata
)
973
local
tableoffset
=
gotodatatable
(
f
,
fontdata
,
"
os/2
"
,
true
)
974
if
tableoffset
then
975
local
version
=
readushort
(
f
)
976
local
windowsmetrics
=
{
977
version
=
version
,
978
averagewidth
=
readshort
(
f
)
,
-- ushort?
979
weightclass
=
readushort
(
f
)
,
980
widthclass
=
readushort
(
f
)
,
981
fstype
=
readushort
(
f
)
,
982
subscriptxsize
=
readshort
(
f
)
,
983
subscriptysize
=
readshort
(
f
)
,
984
subscriptxoffset
=
readshort
(
f
)
,
985
subscriptyoffset
=
readshort
(
f
)
,
986
superscriptxsize
=
readshort
(
f
)
,
987
superscriptysize
=
readshort
(
f
)
,
988
superscriptxoffset
=
readshort
(
f
)
,
989
superscriptyoffset
=
readshort
(
f
)
,
990
strikeoutsize
=
readshort
(
f
)
,
991
strikeoutpos
=
readshort
(
f
)
,
992
familyclass
=
readshort
(
f
)
,
993
panose
=
{
readbytes
(
f
,
10
)
}
,
994
unicoderanges
=
{
readulong
(
f
)
,
readulong
(
f
)
,
readulong
(
f
)
,
readulong
(
f
)
}
,
995
vendor
=
readstring
(
f
,
4
)
,
996
fsselection
=
readushort
(
f
)
,
997
firstcharindex
=
readushort
(
f
)
,
998
lastcharindex
=
readushort
(
f
)
,
999
typoascender
=
readshort
(
f
)
,
1000
typodescender
=
readshort
(
f
)
,
1001
typolinegap
=
readshort
(
f
)
,
1002
winascent
=
readushort
(
f
)
,
1003
windescent
=
readushort
(
f
)
,
1004
}
1005
if
version
>
=
1
then
1006
windowsmetrics
.
codepageranges
=
{
readulong
(
f
)
,
readulong
(
f
)
}
1007
end
1008
if
version
>
=
2
then
1009
windowsmetrics
.
xheight
=
readshort
(
f
)
1010
windowsmetrics
.
capheight
=
readshort
(
f
)
1011
windowsmetrics
.
defaultchar
=
readushort
(
f
)
1012
windowsmetrics
.
breakchar
=
readushort
(
f
)
1013
-- windowsmetrics.maxcontexts = readushort(f)
1014
-- windowsmetrics.loweropticalpointsize = readushort(f)
1015
-- windowsmetrics.upperopticalpointsize = readushort(f)
1016
end
1017
--
1018
-- todo: unicoderanges
1019
--
1020
windowsmetrics
.
weight
=
windowsmetrics
.
weightclass
and
weights
[
windowsmetrics
.
weightclass
]
1021
windowsmetrics
.
width
=
windowsmetrics
.
widthclass
and
widths
[
windowsmetrics
.
widthclass
]
1022
--
1023
windowsmetrics
.
panoseweight
=
panoseweights
[
windowsmetrics
.
panose
[
3
]
]
1024
windowsmetrics
.
panosewidth
=
panosewidths
[
windowsmetrics
.
panose
[
4
]
]
1025
--
1026
fontdata
.
windowsmetrics
=
windowsmetrics
1027
else
1028
fontdata
.
windowsmetrics
=
{
}
1029
end
1030
end
1031 1032
readers
.
head
=
function
(
f
,
fontdata
)
1033
local
tableoffset
=
gotodatatable
(
f
,
fontdata
,
"
head
"
,
true
)
1034
if
tableoffset
then
1035
local
version
=
readulong
(
f
)
1036
local
fontversion
=
readulong
(
f
)
1037
local
fontheader
=
{
1038
version
=
version
,
1039
fontversion
=
number
.
to16dot16
(
fontversion
)
,
1040
fontversionnumber
=
fontversion
,
1041
-- checksum = readulong(f),
1042
checksum
=
readushort
(
f
)
*
0x10000
+
readushort
(
f
)
,
1043
magic
=
readulong
(
f
)
,
1044
flags
=
readushort
(
f
)
,
1045
units
=
readushort
(
f
)
,
1046
created
=
readlongdatetime
(
f
)
,
1047
modified
=
readlongdatetime
(
f
)
,
1048
xmin
=
readshort
(
f
)
,
1049
ymin
=
readshort
(
f
)
,
1050
xmax
=
readshort
(
f
)
,
1051
ymax
=
readshort
(
f
)
,
1052
macstyle
=
readushort
(
f
)
,
1053
smallpixels
=
readushort
(
f
)
,
1054
directionhint
=
readshort
(
f
)
,
1055
indextolocformat
=
readshort
(
f
)
,
1056
glyphformat
=
readshort
(
f
)
,
1057
}
1058
fontdata
.
fontheader
=
fontheader
1059
else
1060
fontdata
.
fontheader
=
{
}
1061
end
1062
fontdata
.
nofglyphs
=
0
1063
end
1064 1065
-- This table is a rather simple one. No treatment of values is needed here. Most
1066
-- variables are not used but nofmetrics is quite important.
1067 1068
readers
.
hhea
=
function
(
f
,
fontdata
,
specification
)
1069
local
tableoffset
=
gotodatatable
(
f
,
fontdata
,
"
hhea
"
,
specification
.
details
)
1070
if
tableoffset
then
1071
fontdata
.
horizontalheader
=
{
1072
version
=
readulong
(
f
)
,
1073
ascender
=
readfword
(
f
)
,
1074
descender
=
readfword
(
f
)
,
1075
linegap
=
readfword
(
f
)
,
1076
maxadvancewidth
=
readufword
(
f
)
,
1077
minleftsidebearing
=
readfword
(
f
)
,
1078
minrightsidebearing
=
readfword
(
f
)
,
1079
maxextent
=
readfword
(
f
)
,
1080
caretsloperise
=
readshort
(
f
)
,
1081
caretsloperun
=
readshort
(
f
)
,
1082
caretoffset
=
readshort
(
f
)
,
1083
reserved_1
=
readshort
(
f
)
,
1084
reserved_2
=
readshort
(
f
)
,
1085
reserved_3
=
readshort
(
f
)
,
1086
reserved_4
=
readshort
(
f
)
,
1087
metricdataformat
=
readshort
(
f
)
,
1088
nofmetrics
=
readushort
(
f
)
,
1089
}
1090
else
1091
fontdata
.
horizontalheader
=
{
1092
nofmetrics
=
0
,
1093
}
1094
end
1095
end
1096 1097
readers
.
vhea
=
function
(
f
,
fontdata
,
specification
)
1098
local
tableoffset
=
gotodatatable
(
f
,
fontdata
,
"
vhea
"
,
specification
.
details
)
1099
if
tableoffset
then
1100
fontdata
.
verticalheader
=
{
1101
version
=
readulong
(
f
)
,
1102
ascender
=
readfword
(
f
)
,
1103
descender
=
readfword
(
f
)
,
1104
linegap
=
readfword
(
f
)
,
1105
maxadvanceheight
=
readufword
(
f
)
,
1106
mintopsidebearing
=
readfword
(
f
)
,
1107
minbottomsidebearing
=
readfword
(
f
)
,
1108
maxextent
=
readfword
(
f
)
,
1109
caretsloperise
=
readshort
(
f
)
,
1110
caretsloperun
=
readshort
(
f
)
,
1111
caretoffset
=
readshort
(
f
)
,
1112
reserved_1
=
readshort
(
f
)
,
1113
reserved_2
=
readshort
(
f
)
,
1114
reserved_3
=
readshort
(
f
)
,
1115
reserved_4
=
readshort
(
f
)
,
1116
metricdataformat
=
readshort
(
f
)
,
1117
nofmetrics
=
readushort
(
f
)
,
1118
}
1119
else
1120
fontdata
.
verticalheader
=
{
1121
nofmetrics
=
0
,
1122
}
1123
end
1124
end
1125 1126
-- We probably never need all these variables, but we do need the nofglyphs when loading other
1127
-- tables. Again we use the microsoft names but see no reason to have "max" in each name.
1128 1129
-- fontdata.maximumprofile can be bad
1130 1131
readers
.
maxp
=
function
(
f
,
fontdata
,
specification
)
1132
local
tableoffset
=
gotodatatable
(
f
,
fontdata
,
"
maxp
"
,
specification
.
details
)
1133
if
tableoffset
then
1134
local
version
=
readulong
(
f
)
1135
local
nofglyphs
=
readushort
(
f
)
1136
fontdata
.
nofglyphs
=
nofglyphs
1137
if
version
=
=
0x00005000
then
1138
fontdata
.
maximumprofile
=
{
1139
version
=
version
,
1140
nofglyphs
=
nofglyphs
,
1141
}
1142
elseif
version
=
=
0x00010000
then
1143
fontdata
.
maximumprofile
=
{
1144
version
=
version
,
1145
nofglyphs
=
nofglyphs
,
1146
points
=
readushort
(
f
)
,
1147
contours
=
readushort
(
f
)
,
1148
compositepoints
=
readushort
(
f
)
,
1149
compositecontours
=
readushort
(
f
)
,
1150
zones
=
readushort
(
f
)
,
1151
twilightpoints
=
readushort
(
f
)
,
1152
storage
=
readushort
(
f
)
,
1153
functiondefs
=
readushort
(
f
)
,
1154
instructiondefs
=
readushort
(
f
)
,
1155
stackelements
=
readushort
(
f
)
,
1156
sizeofinstructions
=
readushort
(
f
)
,
1157
componentelements
=
readushort
(
f
)
,
1158
componentdepth
=
readushort
(
f
)
,
1159
}
1160
else
1161
fontdata
.
maximumprofile
=
{
1162
version
=
version
,
1163
nofglyphs
=
0
,
1164
}
1165
end
1166
end
1167
end
1168 1169
-- Here we filter the (advance) widths (that can be different from the boundingbox width of
1170
-- course).
1171 1172
readers
.
hmtx
=
function
(
f
,
fontdata
,
specification
)
1173
local
tableoffset
=
gotodatatable
(
f
,
fontdata
,
"
hmtx
"
,
specification
.
glyphs
)
1174
if
tableoffset
then
1175
local
horizontalheader
=
fontdata
.
horizontalheader
1176
local
nofmetrics
=
horizontalheader
.
nofmetrics
1177
local
glyphs
=
fontdata
.
glyphs
1178
local
nofglyphs
=
fontdata
.
nofglyphs
1179
local
width
=
0
-- advance
1180
local
leftsidebearing
=
0
1181
for
i
=
0
,
nofmetrics
-1
do
1182
local
glyph
=
glyphs
[
i
]
1183
width
=
readshort
(
f
)
-- readushort
1184
leftsidebearing
=
readshort
(
f
)
1185
if
width
~
=
0
then
1186
glyph
.
width
=
width
1187
end
1188
-- if leftsidebearing ~= 0 then
1189
-- glyph.lsb = leftsidebearing
1190
-- end
1191
end
1192
-- The next can happen in for instance a monospace font or in a cjk font
1193
-- with fixed widths.
1194
for
i
=
nofmetrics
,
nofglyphs
-1
do
1195
local
glyph
=
glyphs
[
i
]
1196
if
width
~
=
0
then
1197
glyph
.
width
=
width
1198
end
1199
-- if leftsidebearing ~= 0 then
1200
-- glyph.lsb = leftsidebearing
1201
-- end
1202
end
1203
end
1204
end
1205 1206
readers
.
vmtx
=
function
(
f
,
fontdata
,
specification
)
1207
local
tableoffset
=
gotodatatable
(
f
,
fontdata
,
"
vmtx
"
,
specification
.
glyphs
)
1208
if
tableoffset
then
1209
local
verticalheader
=
fontdata
.
verticalheader
1210
local
nofmetrics
=
verticalheader
.
nofmetrics
1211
local
glyphs
=
fontdata
.
glyphs
1212
local
nofglyphs
=
fontdata
.
nofglyphs
1213
local
vheight
=
0
1214
local
vdefault
=
verticalheader
.
ascender
-
verticalheader
.
descender
1215
local
topsidebearing
=
0
1216
for
i
=
0
,
nofmetrics
-1
do
1217
local
glyph
=
glyphs
[
i
]
1218
vheight
=
readushort
(
f
)
1219
topsidebearing
=
readshort
(
f
)
1220
if
vheight
~
=
0
and
vheight
~
=
vdefault
then
1221
glyph
.
vheight
=
vheight
1222
end
1223
if
topsidebearing
~
=
0
then
1224
glyph
.
tsb
=
topsidebearing
1225
end
1226
end
1227
-- The next can happen in for instance a monospace font or in a cjk font
1228
-- with fixed heights.
1229
for
i
=
nofmetrics
,
nofglyphs
-1
do
1230
local
glyph
=
glyphs
[
i
]
1231
if
vheight
~
=
0
and
vheight
~
=
vdefault
then
1232
glyph
.
vheight
=
vheight
1233
end
1234
end
1235
end
1236
end
1237 1238
readers
.
vorg
=
function
(
f
,
fontdata
,
specification
)
1239
reportskippedtable
(
f
,
fontdata
,
"
vorg
"
,
specification
.
glyphs
)
1240
end
1241 1242
-- The post table relates to postscript (printing) but has some relevant properties for other
1243
-- usage as well. We just use the names from the microsoft specification. The version 2.0
1244
-- description is somewhat fuzzy but it is a hybrid with overloads.
1245 1246
readers
.
post
=
function
(
f
,
fontdata
,
specification
)
1247
local
tableoffset
=
gotodatatable
(
f
,
fontdata
,
"
post
"
,
true
)
1248
if
tableoffset
then
1249
local
version
=
readulong
(
f
)
1250
fontdata
.
postscript
=
{
1251
version
=
version
,
1252
italicangle
=
round
(
1000
*
readfixed
(
f
)
)
/
1000
,
1253
underlineposition
=
readfword
(
f
)
,
1254
underlinethickness
=
readfword
(
f
)
,
1255
monospaced
=
readulong
(
f
)
,
1256
minmemtype42
=
readulong
(
f
)
,
1257
maxmemtype42
=
readulong
(
f
)
,
1258
minmemtype1
=
readulong
(
f
)
,
1259
maxmemtype1
=
readulong
(
f
)
,
1260
}
1261
if
not
specification
.
glyphs
then
1262
-- enough done
1263
elseif
version
=
=
0x00010000
then
1264
-- mac encoding (258 glyphs)
1265
for
index
=
0
,
#
standardromanencoding
do
1266
glyphs
[
index
]
.
name
=
standardromanencoding
[
index
]
1267
end
1268
elseif
version
=
=
0x00020000
then
1269
local
glyphs
=
fontdata
.
glyphs
1270
local
nofglyphs
=
readushort
(
f
)
1271
local
indices
=
{
}
1272
local
names
=
{
}
1273
local
maxnames
=
0
1274
for
i
=
0
,
nofglyphs
-1
do
1275
local
nameindex
=
readushort
(
f
)
1276
if
nameindex
>
=
258
then
1277
maxnames
=
maxnames
+
1
1278
nameindex
=
nameindex
-
257
1279
indices
[
nameindex
]
=
i
1280
else
1281
glyphs
[
i
]
.
name
=
standardromanencoding
[
nameindex
]
1282
end
1283
end
1284
for
i
=
1
,
maxnames
do
1285
local
mapping
=
indices
[
i
]
1286
if
not
mapping
then
1287
report
(
"
quit post name fetching at %a of %a: %s
"
,
i
,
maxnames
,
"
no index
"
)
1288
break
1289
else
1290
local
length
=
readbyte
(
f
)
1291
if
length
>
0
then
1292
glyphs
[
mapping
]
.
name
=
readstring
(
f
,
length
)
1293
else
1294
report
(
"
quit post name fetching at %a of %a: %s
"
,
i
,
maxnames
,
"
overflow
"
)
1295
break
1296
end
1297
end
1298
end
1299
end
1300
else
1301
fontdata
.
postscript
=
{
}
1302
end
1303
end
1304 1305
readers
.
cff
=
function
(
f
,
fontdata
,
specification
)
1306
reportskippedtable
(
f
,
fontdata
,
"
cff
"
,
specification
.
glyphs
)
1307
end
1308 1309
-- Not all cmaps make sense .. e.g. dfont is obsolete and probably more are not relevant. Let's see
1310
-- what we run into. There is some weird calculation going on here because we offset in a table
1311
-- being a blob of memory or file. Anyway, I can't stand lunatic formats like this esp when there
1312
-- is no real gain.
1313 1314
local
formatreaders
=
{
}
1315
local
duplicatestoo
=
true
1316 1317
local
sequence
=
{
1318
-- these is some provision against redundant loading
1319
{
3
,
1
,
4
}
,
1320
{
3
,
10
,
12
}
,
1321
{
0
,
3
,
4
}
,
1322
{
0
,
3
,
12
}
,
1323
{
0
,
1
,
4
}
,
1324
{
0
,
1
,
12
}
,
-- for some old mac fonts
1325
{
0
,
0
,
6
}
,
1326
{
3
,
0
,
6
}
,
1327
{
3
,
0
,
4
}
,
-- for (likely) old crap
1328
-- variants
1329
{
0
,
5
,
14
}
,
1330
-- last resort ranges
1331
{
0
,
4
,
12
}
,
1332
{
3
,
10
,
13
}
,
1333
}
1334 1335
local
supported
=
{
}
1336 1337
for
i
=
1
,
#
sequence
do
1338
local
si
=
sequence
[
i
]
1339
local
sp
,
se
,
sf
=
si
[
1
]
,
si
[
2
]
,
si
[
3
]
1340
local
p
=
supported
[
sp
]
1341
if
not
p
then
1342
p
=
{
}
1343
supported
[
sp
]
=
p
1344
end
1345
local
e
=
p
[
se
]
1346
if
not
e
then
1347
e
=
{
}
1348
p
[
se
]
=
e
1349
end
1350
e
[
sf
]
=
true
1351
end
1352 1353
formatreaders
[
4
]
=
function
(
f
,
fontdata
,
offset
)
1354
setposition
(
f
,
offset
+
2
)
-- skip format
1355
--
1356
local
length
=
readushort
(
f
)
-- in bytes of subtable
1357
local
language
=
readushort
(
f
)
1358
local
nofsegments
=
readushort
(
f
)
/
2
1359
--
1360
skipshort
(
f
,
3
)
-- searchrange entryselector rangeshift
1361
--
1362
local
mapping
=
fontdata
.
mapping
1363
local
glyphs
=
fontdata
.
glyphs
1364
local
duplicates
=
fontdata
.
duplicates
1365
local
nofdone
=
0
1366
local
endchars
=
readcardinaltable
(
f
,
nofsegments
,
ushort
)
1367
local
reserved
=
readushort
(
f
)
-- 0
1368
local
startchars
=
readcardinaltable
(
f
,
nofsegments
,
ushort
)
1369
local
deltas
=
readcardinaltable
(
f
,
nofsegments
,
ushort
)
1370
local
offsets
=
readcardinaltable
(
f
,
nofsegments
,
ushort
)
1371
-- format length language nofsegments searchrange entryselector rangeshift 4-tables
1372
local
size
=
(
length
-
2
*
2
-
5
*
2
-
4
*
2
*
nofsegments
)
/
2
1373
local
indices
=
readcardinaltable
(
f
,
size
-1
,
ushort
)
1374
--
1375
for
segment
=
1
,
nofsegments
do
1376
local
startchar
=
startchars
[
segment
]
1377
local
endchar
=
endchars
[
segment
]
1378
local
offset
=
offsets
[
segment
]
1379
local
delta
=
deltas
[
segment
]
1380
if
startchar
=
=
0xFFFF
and
endchar
=
=
0xFFFF
then
1381
-- break
1382
elseif
startchar
=
=
0xFFFF
and
offset
=
=
0
then
1383
-- break
1384
elseif
offset
=
=
0xFFFF
then
1385
-- bad encoding
1386
elseif
offset
=
=
0
then
1387
if
trace_cmap_details
then
1388
report
(
"
format 4.%i segment %2i from %C upto %C at index %H
"
,
1
,
segment
,
startchar
,
endchar
,
(
startchar
+
delta
)
%
65536
)
1389
end
1390
for
unicode
=
startchar
,
endchar
do
1391
local
index
=
(
unicode
+
delta
)
%
65536
1392
if
index
and
index
>
0
then
1393
local
glyph
=
glyphs
[
index
]
1394
if
glyph
then
1395
local
gu
=
glyph
.
unicode
1396
if
not
gu
then
1397
glyph
.
unicode
=
unicode
1398
nofdone
=
nofdone
+
1
1399
elseif
gu
~
=
unicode
then
1400
if
duplicatestoo
then
1401
local
d
=
duplicates
[
gu
]
1402
if
d
then
1403
d
[
unicode
]
=
true
1404
else
1405
duplicates
[
gu
]
=
{
[
unicode
]
=
true
}
1406
end
1407
else
1408
-- no duplicates ... weird side effects in lm
1409
report
(
"
duplicate case 1: %C %04i %s
"
,
unicode
,
index
,
glyphs
[
index
]
.
name
)
1410
end
1411
end
1412
if
not
mapping
[
index
]
then
1413
mapping
[
index
]
=
unicode
1414
end
1415
end
1416
end
1417
end
1418
else
1419
local
shift
=
(
segment
-
nofsegments
+
offset
/
2
)
-
startchar
1420
if
trace_cmap_details
then
1421
report_cmap
(
"
format 4.%i segment %2i from %C upto %C at index %H
"
,
0
,
segment
,
startchar
,
endchar
,
(
startchar
+
delta
)
%
65536
)
1422
end
1423
for
unicode
=
startchar
,
endchar
do
1424
local
slot
=
shift
+
unicode
1425
local
index
=
indices
[
slot
]
1426
if
index
and
index
>
0
then
1427
index
=
(
index
+
delta
)
%
65536
1428
local
glyph
=
glyphs
[
index
]
1429
if
glyph
then
1430
local
gu
=
glyph
.
unicode
1431
if
not
gu
then
1432
glyph
.
unicode
=
unicode
1433
nofdone
=
nofdone
+
1
1434
elseif
gu
~
=
unicode
then
1435
if
duplicatestoo
then
1436
local
d
=
duplicates
[
gu
]
1437
if
d
then
1438
d
[
unicode
]
=
true
1439
else
1440
duplicates
[
gu
]
=
{
[
unicode
]
=
true
}
1441
end
1442
else
1443
-- no duplicates ... weird side effects in lm
1444
report
(
"
duplicate case 2: %C %04i %s
"
,
unicode
,
index
,
glyphs
[
index
]
.
name
)
1445
end
1446
end
1447
if
not
mapping
[
index
]
then
1448
mapping
[
index
]
=
unicode
1449
end
1450
end
1451
end
1452
end
1453
end
1454
end
1455
return
nofdone
1456
end
1457 1458
formatreaders
[
6
]
=
function
(
f
,
fontdata
,
offset
)
1459
setposition
(
f
,
offset
)
-- + 2 + 2 + 2 -- skip format length language
1460
local
format
=
readushort
(
f
)
1461
local
length
=
readushort
(
f
)
1462
local
language
=
readushort
(
f
)
1463
local
mapping
=
fontdata
.
mapping
1464
local
glyphs
=
fontdata
.
glyphs
1465
local
duplicates
=
fontdata
.
duplicates
1466
local
start
=
readushort
(
f
)
1467
local
count
=
readushort
(
f
)
1468
local
stop
=
start
+
count
-1
1469
local
nofdone
=
0
1470
if
trace_cmap_details
then
1471
report_cmap
(
"
format 6 from %C to %C
"
,
2
,
start
,
stop
)
1472
end
1473
for
unicode
=
start
,
stop
do
1474
local
index
=
readushort
(
f
)
1475
if
index
>
0
then
1476
local
glyph
=
glyphs
[
index
]
1477
if
glyph
then
1478
local
gu
=
glyph
.
unicode
1479
if
not
gu
then
1480
glyph
.
unicode
=
unicode
1481
nofdone
=
nofdone
+
1
1482
elseif
gu
~
=
unicode
then
1483
-- report("format 6 overloading %C to %C",gu,unicode)
1484
-- glyph.unicode = unicode
1485
-- no duplicates ... weird side effects in lm
1486
end
1487
if
not
mapping
[
index
]
then
1488
mapping
[
index
]
=
unicode
1489
end
1490
end
1491
end
1492
end
1493
return
nofdone
1494
end
1495 1496
formatreaders
[
12
]
=
function
(
f
,
fontdata
,
offset
)
1497
setposition
(
f
,
offset
+
2
+
2
+
4
+
4
)
-- skip format reserved length language
1498
local
mapping
=
fontdata
.
mapping
1499
local
glyphs
=
fontdata
.
glyphs
1500
local
duplicates
=
fontdata
.
duplicates
1501
local
nofgroups
=
readulong
(
f
)
1502
local
nofdone
=
0
1503
for
i
=
1
,
nofgroups
do
1504
local
first
=
readulong
(
f
)
1505
local
last
=
readulong
(
f
)
1506
local
index
=
readulong
(
f
)
1507
if
trace_cmap_details
then
1508
report_cmap
(
"
format 12 from %C to %C starts at index %i
"
,
first
,
last
,
index
)
1509
end
1510
for
unicode
=
first
,
last
do
1511
local
glyph
=
glyphs
[
index
]
1512
if
glyph
then
1513
local
gu
=
glyph
.
unicode
1514
if
not
gu
then
1515
glyph
.
unicode
=
unicode
1516
nofdone
=
nofdone
+
1
1517
elseif
gu
~
=
unicode
then
1518
-- e.g. sourcehan fonts need this
1519
local
d
=
duplicates
[
gu
]
1520
if
d
then
1521
d
[
unicode
]
=
true
1522
else
1523
duplicates
[
gu
]
=
{
[
unicode
]
=
true
}
1524
end
1525
end
1526
if
not
mapping
[
index
]
then
1527
mapping
[
index
]
=
unicode
1528
end
1529
end
1530
index
=
index
+
1
1531
end
1532
end
1533
return
nofdone
1534
end
1535 1536
formatreaders
[
13
]
=
function
(
f
,
fontdata
,
offset
)
1537
--
1538
-- this vector is only used for simple fallback fonts
1539
--
1540
setposition
(
f
,
offset
+
2
+
2
+
4
+
4
)
-- skip format reserved length language
1541
local
mapping
=
fontdata
.
mapping
1542
local
glyphs
=
fontdata
.
glyphs
1543
local
duplicates
=
fontdata
.
duplicates
1544
local
nofgroups
=
readulong
(
f
)
1545
local
nofdone
=
0
1546
for
i
=
1
,
nofgroups
do
1547
local
first
=
readulong
(
f
)
1548
local
last
=
readulong
(
f
)
1549
local
index
=
readulong
(
f
)
1550
if
first
<
privateoffset
then
1551
if
trace_cmap_details
then
1552
report_cmap
(
"
format 13 from %C to %C get index %i
"
,
first
,
last
,
index
)
1553
end
1554
local
glyph
=
glyphs
[
index
]
1555
local
unicode
=
glyph
.
unicode
1556
if
not
unicode
then
1557
unicode
=
first
1558
glyph
.
unicode
=
unicode
1559
first
=
first
+
1
1560
end
1561
local
list
=
duplicates
[
unicode
]
1562
mapping
[
index
]
=
unicode
1563
if
not
list
then
1564
list
=
{
}
1565
duplicates
[
unicode
]
=
list
1566
end
1567
if
last
>
=
privateoffset
then
1568
local
limit
=
privateoffset
-
1
1569
report
(
"
format 13 from %C to %C pruned to %C
"
,
first
,
last
,
limit
)
1570
last
=
limit
1571
end
1572
for
unicode
=
first
,
last
do
1573
list
[
unicode
]
=
true
1574
end
1575
nofdone
=
nofdone
+
last
-
first
+
1
1576
else
1577
report
(
"
format 13 from %C to %C ignored
"
,
first
,
last
)
1578
end
1579
end
1580
return
nofdone
1581
end
1582 1583
formatreaders
[
14
]
=
function
(
f
,
fontdata
,
offset
)
1584
if
offset
and
offset
~
=
0
then
1585
setposition
(
f
,
offset
)
1586
local
format
=
readushort
(
f
)
1587
local
length
=
readulong
(
f
)
1588
local
nofrecords
=
readulong
(
f
)
1589
local
records
=
{
}
1590
local
variants
=
{
}
1591
local
nofdone
=
0
1592
fontdata
.
variants
=
variants
1593
for
i
=
1
,
nofrecords
do
1594
records
[
i
]
=
{
1595
selector
=
readuint
(
f
)
,
1596
default
=
readulong
(
f
)
,
-- default offset
1597
other
=
readulong
(
f
)
,
-- non-default offset
1598
}
1599
end
1600
for
i
=
1
,
nofrecords
do
1601
local
record
=
records
[
i
]
1602
local
selector
=
record
.
selector
1603
local
default
=
record
.
default
1604
local
other
=
record
.
other
1605
--
1606
-- there is no need to map the defaults to themselves
1607
--
1608
-- if default ~= 0 then
1609
-- setposition(f,offset+default)
1610
-- local nofranges = readulong(f)
1611
-- for i=1,nofranges do
1612
-- local start = readuint(f)
1613
-- local extra = readbyte(f)
1614
-- for i=start,start+extra do
1615
-- mapping[i] = i
1616
-- end
1617
-- end
1618
-- end
1619
local
other
=
record
.
other
1620
if
other
~
=
0
then
1621
setposition
(
f
,
offset
+
other
)
1622
local
mapping
=
{
}
1623
local
count
=
readulong
(
f
)
1624
for
i
=
1
,
count
do
1625
mapping
[
readuint
(
f
)
]
=
readushort
(
f
)
1626
end
1627
nofdone
=
nofdone
+
count
1628
variants
[
selector
]
=
mapping
1629
end
1630
end
1631
return
nofdone
1632
else
1633
return
0
1634
end
1635
end
1636 1637
local
function
checkcmap
(
f
,
fontdata
,
records
,
platform
,
encoding
,
format
)
1638
local
pdata
=
records
[
platform
]
1639
if
not
pdata
then
1640
if
trace_cmap_details
then
1641
report_cmap
(
"
skipped, %s, p=%i e=%i f=%i
"
,
"
no platform
"
,
platform
,
encoding
,
format
)
1642
end
1643
return
0
1644
end
1645
local
edata
=
pdata
[
encoding
]
1646
if
not
edata
then
1647
if
trace_cmap_details
then
1648
report_cmap
(
"
skipped, %s, p=%i e=%i f=%i
"
,
"
no encoding
"
,
platform
,
encoding
,
format
)
1649
end
1650
return
0
1651
end
1652
local
fdata
=
edata
[
format
]
1653
if
not
fdata
then
1654
if
trace_cmap_details
then
1655
report_cmap
(
"
skipped, %s, p=%i e=%i f=%i
"
,
"
no format
"
,
platform
,
encoding
,
format
)
1656
end
1657
return
0
1658
elseif
type
(
fdata
)
~
=
"
number
"
then
1659
if
trace_cmap_details
then
1660
report_cmap
(
"
skipped, %s, p=%i e=%i f=%i
"
,
"
already done
"
,
platform
,
encoding
,
format
)
1661
end
1662
return
0
1663
end
1664
edata
[
format
]
=
true
-- done
1665
local
reader
=
formatreaders
[
format
]
1666
if
not
reader
then
1667
if
trace_cmap_details
then
1668
report_cmap
(
"
skipped, %s, p=%i e=%i f=%i
"
,
"
unsupported format
"
,
platform
,
encoding
,
format
)
1669
end
1670
return
0
1671
end
1672
local
n
=
reader
(
f
,
fontdata
,
fdata
)
or
0
1673
if
trace_cmap_details
or
trace_cmap
then
1674
local
p
=
platforms
[
platform
]
1675
local
e
=
encodings
[
p
]
1676
report_cmap
(
"
checked, platform %i (%s), encoding %i (%s), format %i, new unicodes %i
"
,
1677
platform
,
p
,
encoding
,
e
and
e
[
encoding
]
or
"
?
"
,
format
,
n
)
1678
end
1679
return
n
1680
end
1681 1682
function
readers
.
cmap
(
f
,
fontdata
,
specification
)
1683
local
tableoffset
=
gotodatatable
(
f
,
fontdata
,
"
cmap
"
,
specification
.
glyphs
)
1684
if
tableoffset
then
1685
local
version
=
readushort
(
f
)
1686
local
noftables
=
readushort
(
f
)
1687
local
records
=
{
}
1688
local
unicodecid
=
false
1689
local
variantcid
=
false
1690
local
variants
=
{
}
1691
local
duplicates
=
fontdata
.
duplicates
or
{
}
1692
fontdata
.
duplicates
=
duplicates
1693
for
i
=
1
,
noftables
do
1694
local
platform
=
readushort
(
f
)
1695
local
encoding
=
readushort
(
f
)
1696
local
offset
=
readulong
(
f
)
1697
local
record
=
records
[
platform
]
1698
if
not
record
then
1699
records
[
platform
]
=
{
1700
[
encoding
]
=
{
1701
offsets
=
{
offset
}
,
1702
formats
=
{
}
,
1703
}
1704
}
1705
else
1706
local
subtables
=
record
[
encoding
]
1707
if
not
subtables
then
1708
record
[
encoding
]
=
{
1709
offsets
=
{
offset
}
,
1710
formats
=
{
}
,
1711
}
1712
else
1713
local
offsets
=
subtables
.
offsets
1714
offsets
[
#
offsets
+
1
]
=
offset
1715
end
1716
end
1717
end
1718
if
trace_cmap
then
1719
report
(
"
found cmaps:
"
)
1720
end
1721
for
platform
,
record
in
sortedhash
(
records
)
do
1722
local
p
=
platforms
[
platform
]
1723
local
e
=
encodings
[
p
]
1724
local
sp
=
supported
[
platform
]
1725
local
ps
=
p
or
"
?
"
1726
if
trace_cmap
then
1727
if
sp
then
1728
report
(
"
platform %i: %s
"
,
platform
,
ps
)
1729
else
1730
report
(
"
platform %i: %s (unsupported)
"
,
platform
,
ps
)
1731
end
1732
end
1733
for
encoding
,
subtables
in
sortedhash
(
record
)
do
1734
local
se
=
sp
and
sp
[
encoding
]
1735
local
es
=
e
and
e
[
encoding
]
or
"
?
"
1736
if
trace_cmap
then
1737
if
se
then
1738
report
(
"
encoding %i: %s
"
,
encoding
,
es
)
1739
else
1740
report
(
"
encoding %i: %s (unsupported)
"
,
encoding
,
es
)
1741
end
1742
end
1743
local
offsets
=
subtables
.
offsets
1744
local
formats
=
subtables
.
formats
1745
for
i
=
1
,
#
offsets
do
1746
local
offset
=
tableoffset
+
offsets
[
i
]
1747
setposition
(
f
,
offset
)
1748
formats
[
readushort
(
f
)
]
=
offset
1749
end
1750
record
[
encoding
]
=
formats
1751
if
trace_cmap
then
1752
local
list
=
sortedkeys
(
formats
)
1753
for
i
=
1
,
#
list
do
1754
if
not
(
se
and
se
[
list
[
i
]
]
)
then
1755
list
[
i
]
=
list
[
i
]
.
.
"
(unsupported)
"
1756
end
1757
end
1758
report
(
"
formats: % t
"
,
list
)
1759
end
1760
end
1761
end
1762
--
1763
local
ok
=
false
1764
for
i
=
1
,
#
sequence
do
1765
local
si
=
sequence
[
i
]
1766
local
sp
,
se
,
sf
=
si
[
1
]
,
si
[
2
]
,
si
[
3
]
1767
if
checkcmap
(
f
,
fontdata
,
records
,
sp
,
se
,
sf
)
>
0
then
1768
ok
=
true
1769
end
1770
end
1771
if
not
ok
then
1772
report
(
"
no useable unicode cmap found
"
)
1773
end
1774
--
1775
fontdata
.
cidmaps
=
{
1776
version
=
version
,
1777
noftables
=
noftables
,
1778
records
=
records
,
1779
}
1780
else
1781
fontdata
.
cidmaps
=
{
}
1782
end
1783
end
1784 1785
-- The glyf table depends on the loca table. We have one entry to much in the locations table (the
1786
-- last one is a dummy) because we need to calculate the size of a glyph blob from the delta,
1787
-- although we not need it in our usage (yet). We can remove the locations table when we're done.
1788 1789
function
readers
.
loca
(
f
,
fontdata
,
specification
)
1790
reportskippedtable
(
f
,
fontdata
,
"
loca
"
,
specification
.
glyphs
)
1791
end
1792 1793
function
readers
.
glyf
(
f
,
fontdata
,
specification
)
-- part goes to cff module
1794
reportskippedtable
(
f
,
fontdata
,
"
glyf
"
,
specification
.
glyphs
)
1795
end
1796 1797
-- The MicroSoft variant is pretty clean and is supported (implemented elsewhere)
1798
-- just because I wanted to see how such a font looks like.
1799 1800
function
readers
.
colr
(
f
,
fontdata
,
specification
)
1801
reportskippedtable
(
f
,
fontdata
,
"
colr
"
,
specification
.
glyphs
)
1802
end
1803
function
readers
.
cpal
(
f
,
fontdata
,
specification
)
1804
reportskippedtable
(
f
,
fontdata
,
"
cpal
"
,
specification
.
glyphs
)
1805
end
1806 1807
-- This one is also supported, if only because I could locate a proper font for
1808
-- testing.
1809 1810
function
readers
.
svg
(
f
,
fontdata
,
specification
)
1811
reportskippedtable
(
f
,
fontdata
,
"
svg
"
,
specification
.
glyphs
)
1812
end
1813 1814
-- There is a font from apple to test the next one. Will there be more? Anyhow,
1815
-- it's relatively easy to support, so I did it.
1816 1817
function
readers
.
sbix
(
f
,
fontdata
,
specification
)
1818
reportskippedtable
(
f
,
fontdata
,
"
sbix
"
,
specification
.
glyphs
)
1819
end
1820 1821
-- I'm only willing to look into the next variant if I see a decent and complete (!)
1822
-- font and more can show up. It makes no sense to waste time on ideas. Okay, the
1823
-- apple font also has these tables.
1824 1825
function
readers
.
cbdt
(
f
,
fontdata
,
specification
)
1826
reportskippedtable
(
f
,
fontdata
,
"
cbdt
"
,
specification
.
glyphs
)
1827
end
1828
function
readers
.
cblc
(
f
,
fontdata
,
specification
)
1829
reportskippedtable
(
f
,
fontdata
,
"
cblc
"
,
specification
.
glyphs
)
1830
end
1831
function
readers
.
ebdt
(
f
,
fontdata
,
specification
)
1832
reportskippedtable
(
f
,
fontdata
,
"
ebdt
"
,
specification
.
glyphs
)
1833
end
1834
function
readers
.
ebsc
(
f
,
fontdata
,
specification
)
1835
reportskippedtable
(
f
,
fontdata
,
"
ebsc
"
,
specification
.
glyphs
)
1836
end
1837
function
readers
.
eblc
(
f
,
fontdata
,
specification
)
1838
reportskippedtable
(
f
,
fontdata
,
"
eblc
"
,
specification
.
glyphs
)
1839
end
1840 1841
-- Here we have a table that we really need for later processing although a more advanced gpos table
1842
-- can also be available. Todo: we need a 'fake' lookup for this (analogue to ff).
1843 1844
function
readers
.
kern
(
f
,
fontdata
,
specification
)
1845
local
tableoffset
=
gotodatatable
(
f
,
fontdata
,
"
kern
"
,
specification
.
kerns
)
1846
if
tableoffset
then
1847
local
version
=
readushort
(
f
)
1848
local
noftables
=
readushort
(
f
)
1849
for
i
=
1
,
noftables
do
1850
local
version
=
readushort
(
f
)
1851
local
length
=
readushort
(
f
)
1852
local
coverage
=
readushort
(
f
)
1853
-- bit 8-15 of coverage: format 0 or 2
1854
local
format
=
rshift
(
coverage
,
8
)
-- is this ok?
1855
if
format
=
=
0
then
1856
local
nofpairs
=
readushort
(
f
)
1857
local
searchrange
=
readushort
(
f
)
1858
local
entryselector
=
readushort
(
f
)
1859
local
rangeshift
=
readushort
(
f
)
1860
local
kerns
=
{
}
1861
local
glyphs
=
fontdata
.
glyphs
1862
for
i
=
1
,
nofpairs
do
1863
local
left
=
readushort
(
f
)
1864
local
right
=
readushort
(
f
)
1865
local
kern
=
readfword
(
f
)
1866
local
glyph
=
glyphs
[
left
]
1867
local
kerns
=
glyph
.
kerns
1868
if
kerns
then
1869
kerns
[
right
]
=
kern
1870
else
1871
glyph
.
kerns
=
{
[
right
]
=
kern
}
1872
end
1873
end
1874
elseif
format
=
=
2
then
1875
report
(
"
todo: kern classes
"
)
1876
else
1877
report
(
"
todo: kerns
"
)
1878
end
1879
end
1880
end
1881
end
1882 1883
function
readers
.
gdef
(
f
,
fontdata
,
specification
)
1884
reportskippedtable
(
f
,
fontdata
,
"
gdef
"
,
specification
.
details
)
1885
end
1886 1887
function
readers
.
gsub
(
f
,
fontdata
,
specification
)
1888
reportskippedtable
(
f
,
fontdata
,
"
gsub
"
,
specification
.
details
)
1889
end
1890 1891
function
readers
.
gpos
(
f
,
fontdata
,
specification
)
1892
reportskippedtable
(
f
,
fontdata
,
"
gpos
"
,
specification
.
details
)
1893
end
1894 1895
function
readers
.
math
(
f
,
fontdata
,
specification
)
1896
reportskippedtable
(
f
,
fontdata
,
"
math
"
,
specification
.
details
)
1897
end
1898 1899
-- Now comes the loader. The order of reading these matters as we need to know
1900
-- some properties in order to read following tables. When details is true we also
1901
-- initialize the glyphs data.
1902 1903
local
function
getinfo
(
maindata
,
sub
,
platformnames
,
rawfamilynames
,
metricstoo
,
instancenames
)
1904
local
fontdata
=
sub
and
maindata
.
subfonts
and
maindata
.
subfonts
[
sub
]
or
maindata
1905
local
names
=
fontdata
.
names
1906
local
info
=
nil
1907
if
names
then
1908
local
metrics
=
fontdata
.
windowsmetrics
or
{
}
1909
local
postscript
=
fontdata
.
postscript
or
{
}
1910
local
fontheader
=
fontdata
.
fontheader
or
{
}
1911
local
cffinfo
=
fontdata
.
cffinfo
or
{
}
1912
local
verticalheader
=
fontdata
.
verticalheader
or
{
}
1913
local
filename
=
fontdata
.
filename
1914
local
weight
=
getname
(
fontdata
,
"
weight
"
)
or
(
cffinfo
and
cffinfo
.
weight
)
or
(
metrics
and
metrics
.
weight
)
1915
local
width
=
getname
(
fontdata
,
"
width
"
)
or
(
cffinfo
and
cffinfo
.
width
)
or
(
metrics
and
metrics
.
width
)
1916
local
fontname
=
getname
(
fontdata
,
"
postscriptname
"
)
1917
local
fullname
=
getname
(
fontdata
,
"
fullname
"
)
1918
local
family
=
getname
(
fontdata
,
"
family
"
)
1919
local
subfamily
=
getname
(
fontdata
,
"
subfamily
"
)
1920
local
familyname
=
getname
(
fontdata
,
"
typographicfamily
"
)
1921
local
subfamilyname
=
getname
(
fontdata
,
"
typographicsubfamily
"
)
1922
local
compatiblename
=
getname
(
fontdata
,
"
compatiblefullname
"
)
-- kind of useless
1923
if
rawfamilynames
then
1924
-- for PG (for now, as i need to check / adapt context to catch a no-fallback case)
1925
else
1926
if
not
familyname
then
familyname
=
family
end
1927
if
not
subfamilyname
then
subfamilyname
=
subfamily
end
1928
end
1929
if
platformnames
then
1930
platformnames
=
fontdata
.
platformnames
1931
end
1932
if
instancenames
then
1933
local
variabledata
=
fontdata
.
variabledata
1934
if
variabledata
then
1935
local
instances
=
variabledata
and
variabledata
.
instances
1936
if
instances
then
1937
instancenames
=
{
}
1938
for
i
=
1
,
#
instances
do
1939
instancenames
[
i
]
=
lower
(
stripstring
(
instances
[
i
]
.
subfamily
)
)
1940
end
1941
else
1942
instancenames
=
nil
1943
end
1944
else
1945
instancenames
=
nil
1946
end
1947
end
1948
info
=
{
-- we inherit some inconsistencies/choices from ff
1949
subfontindex
=
fontdata
.
subfontindex
or
sub
or
0
,
1950
-- filename = filename,
1951
version
=
getname
(
fontdata
,
"
version
"
)
,
1952
-- format = fontdata.format,
1953
fontname
=
fontname
,
1954
fullname
=
fullname
,
1955
-- cfffullname = cff.fullname,
1956
family
=
family
,
1957
subfamily
=
subfamily
,
1958
familyname
=
familyname
,
1959
subfamilyname
=
subfamilyname
,
1960
compatiblename
=
compatiblename
,
1961
weight
=
weight
and
lower
(
weight
)
,
1962
width
=
width
and
lower
(
width
)
,
1963
pfmweight
=
metrics
.
weightclass
or
400
,
-- will become weightclass
1964
pfmwidth
=
metrics
.
widthclass
or
5
,
-- will become widthclass
1965
panosewidth
=
metrics
.
panosewidth
,
1966
panoseweight
=
metrics
.
panoseweight
,
1967
italicangle
=
postscript
.
italicangle
or
0
,
1968
units
=
fontheader
.
units
or
0
,
1969
designsize
=
fontdata
.
designsize
,
1970
minsize
=
fontdata
.
minsize
,
1971
maxsize
=
fontdata
.
maxsize
,
1972
boundingbox
=
fontheader
and
{
fontheader
.
xmin
or
0
,
fontheader
.
ymin
or
0
,
fontheader
.
xmax
or
0
,
fontheader
.
ymax
or
0
}
or
nil
,
1973
monospaced
=
(
tonumber
(
postscript
.
monospaced
or
0
)
>
0
)
or
metrics
.
panosewidth
=
=
"
monospaced
"
,
1974
averagewidth
=
metrics
.
averagewidth
,
1975
xheight
=
metrics
.
xheight
,
-- can be missing
1976
capheight
=
metrics
.
capheight
or
fontdata
.
maxy
,
-- can be missing
1977
ascender
=
metrics
.
typoascender
,
1978
descender
=
metrics
.
typodescender
,
1979
platformnames
=
platformnames
or
nil
,
1980
instancenames
=
instancenames
or
nil
,
1981
tableoffsets
=
fontdata
.
tableoffsets
,
1982
defaultvheight
=
(
verticalheader
.
ascender
or
0
)
-
(
verticalheader
.
descender
or
0
)
1983
}
1984
if
metricstoo
then
1985
local
keys
=
{
1986
"
version
"
,
1987
"
ascender
"
,
"
descender
"
,
"
linegap
"
,
1988
-- "caretoffset", "caretsloperise", "caretsloperun",
1989
"
maxadvancewidth
"
,
"
maxadvanceheight
"
,
"
maxextent
"
,
1990
-- "metricdataformat",
1991
"
minbottomsidebearing
"
,
"
mintopsidebearing
"
,
1992
}
1993
local
h
=
fontdata
.
horizontalheader
or
{
}
1994
local
v
=
fontdata
.
verticalheader
or
{
}
1995
if
h
then
1996
local
th
=
{
}
1997
local
tv
=
{
}
1998
for
i
=
1
,
#
keys
do
1999
local
key
=
keys
[
i
]
2000
th
[
key
]
=
h
[
key
]
or
0
2001
tv
[
key
]
=
v
[
key
]
or
0
2002
end
2003
info
.
horizontalmetrics
=
th
2004
info
.
verticalmetrics
=
tv
2005
end
2006
end
2007
elseif
n
then
2008
info
=
{
2009
filename
=
fontdata
.
filename
,
2010
comment
=
"
there is no info for subfont
"
.
.
n
,
2011
}
2012
else
2013
info
=
{
2014
filename
=
fontdata
.
filename
,
2015
comment
=
"
there is no info
"
,
2016
}
2017
end
2018
-- inspect(info)
2019
return
info
2020
end
2021 2022
local
function
loadtables
(
f
,
specification
,
offset
)
2023
if
offset
then
2024
setposition
(
f
,
offset
)
2025
end
2026
local
tables
=
{
}
2027
local
basename
=
file
.
basename
(
specification
.
filename
)
2028
local
filesize
=
specification
.
filesize
2029
local
filetime
=
specification
.
filetime
2030
local
fontdata
=
{
-- some can/will go
2031
filename
=
basename
,
2032
filesize
=
filesize
,
2033
filetime
=
filetime
,
2034
version
=
readstring
(
f
,
4
)
,
2035
noftables
=
readushort
(
f
)
,
2036
searchrange
=
readushort
(
f
)
,
-- not needed
2037
entryselector
=
readushort
(
f
)
,
-- not needed
2038
rangeshift
=
readushort
(
f
)
,
-- not needed
2039
tables
=
tables
,
2040
foundtables
=
false
,
2041
}
2042
for
i
=
1
,
fontdata
.
noftables
do
2043
local
tag
=
lower
(
stripstring
(
readstring
(
f
,
4
)
)
)
2044
-- local checksum = readulong(f) -- not used
2045
local
checksum
=
readushort
(
f
)
*
0x10000
+
readushort
(
f
)
2046
local
offset
=
readulong
(
f
)
2047
local
length
=
readulong
(
f
)
2048
if
offset
+
length
>
filesize
then
2049
report
(
"
bad %a table in file %a
"
,
tag
,
basename
)
2050
end
2051
tables
[
tag
]
=
{
2052
checksum
=
checksum
,
2053
offset
=
offset
,
2054
length
=
length
,
2055
}
2056
end
2057
-- inspect(tables)
2058
fontdata
.
foundtables
=
sortedkeys
(
tables
)
2059
if
tables
.
cff
or
tables
.
cff2
then
2060
fontdata
.
format
=
"
opentype
"
2061
else
2062
fontdata
.
format
=
"
truetype
"
2063
end
2064
return
fontdata
,
tables
2065
end
2066 2067
local
function
prepareglyps
(
fontdata
)
2068
local
glyphs
=
setmetatableindex
(
function
(
t
,
k
)
2069
local
v
=
{
2070
-- maybe more defaults
2071
index
=
k
,
2072
}
2073
t
[
k
]
=
v
2074
return
v
2075
end
)
2076
fontdata
.
glyphs
=
glyphs
2077
fontdata
.
mapping
=
{
}
2078
end
2079 2080
local
function
readtable
(
tag
,
f
,
fontdata
,
specification
,
...
)
2081
local
reader
=
readers
[
tag
]
2082
if
reader
then
2083
reader
(
f
,
fontdata
,
specification
,
...
)
2084
end
2085
end
2086 2087
local
function
readdata
(
f
,
offset
,
specification
)
2088 2089
local
fontdata
,
tables
=
loadtables
(
f
,
specification
,
offset
)
2090 2091
if
specification
.
glyphs
then
2092
prepareglyps
(
fontdata
)
2093
end
2094 2095
fontdata
.
temporary
=
{
}
2096 2097
readtable
(
"
name
"
,
f
,
fontdata
,
specification
)
2098 2099
local
askedname
=
specification
.
askedname
2100
if
askedname
then
2101
local
fullname
=
getname
(
fontdata
,
"
fullname
"
)
or
"
"
2102
local
cleanname
=
gsub
(
askedname
,
"
[^a-zA-Z0-9]
"
,
"
"
)
2103
local
foundname
=
gsub
(
fullname
,
"
[^a-zA-Z0-9]
"
,
"
"
)
2104
if
lower
(
cleanname
)
~
=
lower
(
foundname
)
then
2105
return
-- keep searching
2106
end
2107
end
2108 2109
readtable
(
"
stat
"
,
f
,
fontdata
,
specification
)
2110
readtable
(
"
avar
"
,
f
,
fontdata
,
specification
)
2111
readtable
(
"
fvar
"
,
f
,
fontdata
,
specification
)
2112 2113
local
variabledata
=
fontdata
.
variabledata
2114 2115
if
variabledata
then
2116
local
instances
=
variabledata
.
instances
2117
local
axis
=
variabledata
.
axis
2118
if
axis
and
(
not
instances
or
#
instances
=
=
0
)
then
2119
instances
=
{
}
2120
variabledata
.
instances
=
instances
2121
local
function
add
(
n
,
subfamily
,
value
)
2122
local
values
=
{
}
2123
for
i
=
1
,
#
axis
do
2124
local
a
=
axis
[
i
]
2125
values
[
i
]
=
{
2126
axis
=
a
.
tag
,
2127
value
=
i
=
=
n
and
value
or
a
.
default
,
2128
}
2129
end
2130
instances
[
#
instances
+
1
]
=
{
2131
subfamily
=
subfamily
,
2132
values
=
values
,
2133
}
2134
end
2135
for
i
=
1
,
#
axis
do
2136
local
a
=
axis
[
i
]
2137
local
tag
=
a
.
tag
2138
add
(
i
,
"
default
"
.
.
tag
,
a
.
default
)
2139
add
(
i
,
"
minimum
"
.
.
tag
,
a
.
minimum
)
2140
add
(
i
,
"
maximum
"
.
.
tag
,
a
.
maximum
)
2141
end
2142
-- report("%i fake instances added",#instances)
2143
end
2144
end
2145
if
not
specification
.
factors
then
2146
local
instance
=
specification
.
instance
2147
if
type
(
instance
)
=
=
"
string
"
then
2148
local
factors
=
helpers
.
getfactors
(
fontdata
,
instance
)
2149
if
factors
then
2150
specification
.
factors
=
factors
2151
fontdata
.
factors
=
factors
2152
fontdata
.
instance
=
instance
2153
report
(
"
user instance: %s, factors: % t
"
,
instance
,
factors
)
2154
else
2155
report
(
"
user instance: %s, bad factors
"
,
instance
)
2156
end
2157
end
2158
end
2159 2160
if
not
fontdata
.
factors
then
2161
if
fontdata
.
variabledata
then
2162
local
factors
=
helpers
.
getfactors
(
fontdata
,
true
)
2163
if
factors
then
2164
specification
.
factors
=
factors
2165
fontdata
.
factors
=
factors
2166
-- report("factors: % t",factors)
2167
-- else
2168
-- report("bad factors")
2169
end
2170
else
2171
-- report("unknown instance")
2172
end
2173
end
2174 2175
readtable
(
"
os/2
"
,
f
,
fontdata
,
specification
)
2176
readtable
(
"
head
"
,
f
,
fontdata
,
specification
)
2177
readtable
(
"
maxp
"
,
f
,
fontdata
,
specification
)
2178
readtable
(
"
hhea
"
,
f
,
fontdata
,
specification
)
2179
readtable
(
"
vhea
"
,
f
,
fontdata
,
specification
)
2180
readtable
(
"
hmtx
"
,
f
,
fontdata
,
specification
)
2181
readtable
(
"
vmtx
"
,
f
,
fontdata
,
specification
)
2182
readtable
(
"
vorg
"
,
f
,
fontdata
,
specification
)
2183
readtable
(
"
post
"
,
f
,
fontdata
,
specification
)
2184 2185
readtable
(
"
mvar
"
,
f
,
fontdata
,
specification
)
2186
readtable
(
"
hvar
"
,
f
,
fontdata
,
specification
)
2187
readtable
(
"
vvar
"
,
f
,
fontdata
,
specification
)
2188 2189
readtable
(
"
gdef
"
,
f
,
fontdata
,
specification
)
2190 2191
readtable
(
"
cff
"
,
f
,
fontdata
,
specification
)
2192
readtable
(
"
cff2
"
,
f
,
fontdata
,
specification
)
2193 2194
readtable
(
"
cmap
"
,
f
,
fontdata
,
specification
)
2195
readtable
(
"
loca
"
,
f
,
fontdata
,
specification
)
-- maybe load it in glyf
2196
readtable
(
"
glyf
"
,
f
,
fontdata
,
specification
)
-- loads gvar
2197 2198
readtable
(
"
colr
"
,
f
,
fontdata
,
specification
)
2199
readtable
(
"
cpal
"
,
f
,
fontdata
,
specification
)
2200 2201
readtable
(
"
svg
"
,
f
,
fontdata
,
specification
)
2202 2203
readtable
(
"
sbix
"
,
f
,
fontdata
,
specification
)
2204 2205
readtable
(
"
cbdt
"
,
f
,
fontdata
,
specification
)
2206
readtable
(
"
cblc
"
,
f
,
fontdata
,
specification
)
2207
readtable
(
"
ebdt
"
,
f
,
fontdata
,
specification
)
2208
readtable
(
"
eblc
"
,
f
,
fontdata
,
specification
)
2209 2210
readtable
(
"
kern
"
,
f
,
fontdata
,
specification
)
2211
readtable
(
"
gsub
"
,
f
,
fontdata
,
specification
)
2212
readtable
(
"
gpos
"
,
f
,
fontdata
,
specification
)
2213 2214
readtable
(
"
math
"
,
f
,
fontdata
,
specification
)
2215 2216
fontdata
.
locations
=
nil
2217
fontdata
.
cidmaps
=
nil
2218
fontdata
.
dictionaries
=
nil
2219
-- fontdata.cff = nil
2220 2221
if
specification
.
tableoffsets
then
2222
fontdata
.
tableoffsets
=
tables
2223
setmetatableindex
(
tables
,
{
2224
version
=
fontdata
.
version
,
2225
noftables
=
fontdata
.
noftables
,
2226
searchrange
=
fontdata
.
searchrange
,
2227
entryselector
=
fontdata
.
entryselector
,
2228
rangeshift
=
fontdata
.
rangeshift
,
2229
}
)
2230
end
2231
return
fontdata
2232
end
2233 2234
local
function
loadfontdata
(
specification
)
2235
local
filename
=
specification
.
filename
2236
local
fileattr
=
lfs
.
attributes
(
filename
)
2237
local
filesize
=
fileattr
and
fileattr
.
size
or
0
2238
local
filetime
=
fileattr
and
fileattr
.
modification
or
0
2239
local
f
=
openfile
(
filename
,
true
)
-- zero based
2240
if
not
f
then
2241
report
(
"
unable to open %a
"
,
filename
)
2242
elseif
filesize
=
=
0
then
2243
report
(
"
empty file %a
"
,
filename
)
2244
closefile
(
f
)
2245
else
2246
specification
.
filesize
=
filesize
2247
specification
.
filetime
=
filetime
2248
local
version
=
readstring
(
f
,
4
)
2249
local
fontdata
=
nil
2250
if
version
=
=
"
OTTO
"
or
version
=
=
"
true
"
or
version
=
=
"
\0\1\0\0
"
then
2251
fontdata
=
readdata
(
f
,
0
,
specification
)
2252
elseif
version
=
=
"
ttcf
"
then
2253
local
subfont
=
tonumber
(
specification
.
subfont
)
2254
local
ttcversion
=
readulong
(
f
)
2255
local
nofsubfonts
=
readulong
(
f
)
2256
local
offsets
=
readcardinaltable
(
f
,
nofsubfonts
,
ulong
)
2257
if
subfont
then
-- a number of not
2258
if
subfont
>
=
1
and
subfont
<
=
nofsubfonts
then
2259
fontdata
=
readdata
(
f
,
offsets
[
subfont
]
,
specification
)
2260
else
2261
report
(
"
no subfont %a in file %a
"
,
subfont
,
filename
)
2262
end
2263
else
2264
subfont
=
specification
.
subfont
2265
if
type
(
subfont
)
=
=
"
string
"
and
subfont
~
=
"
"
then
2266
specification
.
askedname
=
subfont
2267
for
i
=
1
,
nofsubfonts
do
2268
fontdata
=
readdata
(
f
,
offsets
[
i
]
,
specification
)
2269
if
fontdata
then
2270
fontdata
.
subfontindex
=
i
2271
report
(
"
subfont named %a has index %a
"
,
subfont
,
i
)
2272
break
2273
end
2274
end
2275
if
not
fontdata
then
2276
report
(
"
no subfont named %a
"
,
subfont
)
2277
end
2278
else
2279
local
subfonts
=
{
}
2280
fontdata
=
{
2281
filename
=
filename
,
2282
filesize
=
filesize
,
2283
filetime
=
filetime
,
2284
version
=
version
,
2285
subfonts
=
subfonts
,
2286
ttcversion
=
ttcversion
,
2287
nofsubfonts
=
nofsubfonts
,
2288
}
2289
for
i
=
1
,
nofsubfonts
do
2290
subfonts
[
i
]
=
readdata
(
f
,
offsets
[
i
]
,
specification
)
2291
end
2292
end
2293
end
2294
else
2295
report
(
"
unknown version %a in file %a
"
,
version
,
filename
)
2296
end
2297
closefile
(
f
)
2298
return
fontdata
or
{
}
2299
end
2300
end
2301 2302
local
function
loadfont
(
specification
,
n
,
instance
)
2303
if
type
(
specification
)
=
=
"
string
"
then
2304
specification
=
{
2305
filename
=
specification
,
2306
info
=
true
,
-- always true (for now)
2307
details
=
true
,
2308
glyphs
=
true
,
2309
shapes
=
true
,
2310
kerns
=
true
,
2311
variable
=
true
,
2312
globalkerns
=
true
,
2313
lookups
=
true
,
2314
-- true or number:
2315
subfont
=
n
or
true
,
2316
tounicode
=
false
,
2317
instance
=
instance
2318
}
2319
end
2320
-- if shapes only then
2321
if
specification
.
shapes
or
specification
.
lookups
or
specification
.
kerns
then
2322
specification
.
glyphs
=
true
2323
end
2324
if
specification
.
glyphs
then
2325
specification
.
details
=
true
2326
end
2327
if
specification
.
details
then
2328
specification
.
info
=
true
-- not really used any more
2329
end
2330
if
specification
.
platformnames
then
2331
specification
.
platformnames
=
true
-- not really used any more
2332
end
2333
if
specification
.
instance
or
instance
then
2334
specification
.
variable
=
true
2335
specification
.
instance
=
specification
.
instance
or
instance
2336
end
2337
local
function
message
(
str
)
2338
report
(
"
fatal error in file %a: %s\n%s
"
,
specification
.
filename
,
str
,
debug
and
debug
.
traceback
(
)
)
2339
end
2340
local
ok
,
result
=
xpcall
(
loadfontdata
,
message
,
specification
)
2341
if
ok
then
2342
return
result
2343
end
2344
-- return loadfontdata(specification)
2345
end
2346 2347
-- we need even less, but we can have a 'detail' variant
2348 2349
function
readers
.
loadshapes
(
filename
,
n
,
instance
,
streams
)
2350
local
fontdata
=
loadfont
{
2351
filename
=
filename
,
2352
shapes
=
true
,
2353
streams
=
streams
,
2354
variable
=
true
,
2355
subfont
=
n
,
2356
instance
=
instance
,
2357
}
2358
if
fontdata
then
2359
-- easier on luajit but still we can hit the 64 K stack constants issue
2360
for
k
,
v
in
next
,
fontdata
.
glyphs
do
2361
v
.
class
=
nil
2362
v
.
index
=
nil
2363
v
.
math
=
nil
2364
-- v.name = nil
2365
end
2366
local
names
=
fontdata
.
names
2367
if
names
then
2368
for
k
,
v
in
next
,
names
do
2369
names
[
k
]
=
fullstrip
(
v
.
content
)
2370
end
2371
end
2372
end
2373
return
fontdata
and
{
2374
-- version = 0.123 -- todo
2375
filename
=
filename
,
2376
format
=
fontdata
.
format
,
2377
glyphs
=
fontdata
.
glyphs
,
2378
units
=
fontdata
.
fontheader
.
units
,
2379
cffinfo
=
fontdata
.
cffinfo
,
2380
fontheader
=
fontdata
.
fontheader
,
2381
horizontalheader
=
fontdata
.
horizontalheader
,
2382
verticalheader
=
fontdata
.
verticalheader
,
2383
maximumprofile
=
fontdata
.
maximumprofile
,
2384
names
=
fontdata
.
names
,
2385
postscript
=
fontdata
.
postscript
,
2386
}
or
{
2387
filename
=
filename
,
2388
format
=
"
unknown
"
,
2389
glyphs
=
{
}
,
2390
units
=
0
,
2391
}
2392
end
2393 2394
function
readers
.
loadfont
(
filename
,
n
,
instance
)
2395
local
fontdata
=
loadfont
{
2396
filename
=
filename
,
2397
glyphs
=
true
,
2398
shapes
=
false
,
2399
lookups
=
true
,
2400
variable
=
true
,
2401
-- kerns = true,
2402
-- globalkerns = true, -- only for testing, e.g. cambria has different gpos and kern
2403
subfont
=
n
,
2404
instance
=
instance
,
2405
}
2406
if
fontdata
then
2407
return
{
2408
tableversion
=
tableversion
,
2409
creator
=
"
context mkiv
"
,
2410
size
=
fontdata
.
filesize
,
2411
time
=
fontdata
.
filetime
,
2412
glyphs
=
fontdata
.
glyphs
,
2413
descriptions
=
fontdata
.
descriptions
,
2414
format
=
fontdata
.
format
,
2415
goodies
=
{
}
,
2416
metadata
=
getinfo
(
fontdata
,
n
,
false
,
false
,
true
,
true
)
,
-- no platformnames here !
2417
properties
=
{
2418
hasitalics
=
fontdata
.
hasitalics
or
false
,
2419
maxcolorclass
=
fontdata
.
maxcolorclass
,
2420
hascolor
=
fontdata
.
hascolor
or
false
,
2421
instance
=
fontdata
.
instance
,
2422
factors
=
fontdata
.
factors
,
2423
}
,
2424
resources
=
{
2425
-- filename = fontdata.filename,
2426
filename
=
filename
,
2427
private
=
privateoffset
,
2428
duplicates
=
fontdata
.
duplicates
or
{
}
,
2429
features
=
fontdata
.
features
or
{
}
,
-- we need to add these in the loader
2430
sublookups
=
fontdata
.
sublookups
or
{
}
,
-- we need to add these in the loader
2431
marks
=
fontdata
.
marks
or
{
}
,
-- we need to add these in the loader
2432
markclasses
=
fontdata
.
markclasses
or
{
}
,
-- we need to add these in the loader
2433
marksets
=
fontdata
.
marksets
or
{
}
,
-- we need to add these in the loader
2434
sequences
=
fontdata
.
sequences
or
{
}
,
-- we need to add these in the loader
2435
variants
=
fontdata
.
variants
,
-- variant -> unicode -> glyph
2436
version
=
getname
(
fontdata
,
"
version
"
)
,
2437
cidinfo
=
fontdata
.
cidinfo
,
2438
mathconstants
=
fontdata
.
mathconstants
,
2439
colorpalettes
=
fontdata
.
colorpalettes
,
2440
svgshapes
=
fontdata
.
svgshapes
,
2441
pngshapes
=
fontdata
.
pngshapes
,
2442
variabledata
=
fontdata
.
variabledata
,
2443
foundtables
=
fontdata
.
foundtables
,
2444
}
,
2445
}
2446
end
2447
end
2448 2449
function
readers
.
getinfo
(
filename
,
specification
)
-- string, nil|number|table
2450
-- platformnames is optional and not used by context (a too unpredictable mess
2451
-- that only add to the confusion) .. so it's only for checking things
2452
local
subfont
=
nil
2453
local
platformnames
=
false
2454
local
rawfamilynames
=
false
2455
local
instancenames
=
true
2456
local
tableoffsets
=
false
2457
if
type
(
specification
)
=
=
"
table
"
then
2458
subfont
=
tonumber
(
specification
.
subfont
)
2459
platformnames
=
specification
.
platformnames
2460
rawfamilynames
=
specification
.
rawfamilynames
2461
tableoffsets
=
specification
.
tableoffsets
2462
else
2463
subfont
=
tonumber
(
specification
)
2464
end
2465
local
fontdata
=
loadfont
{
2466
filename
=
filename
,
2467
details
=
true
,
2468
platformnames
=
platformnames
,
2469
instancenames
=
true
,
2470
tableoffsets
=
tableoffsets
,
2471
-- rawfamilynames = rawfamilynames,
2472
}
2473
if
fontdata
then
2474
local
subfonts
=
fontdata
.
subfonts
2475
if
not
subfonts
then
2476
return
getinfo
(
fontdata
,
nil
,
platformnames
,
rawfamilynames
,
false
,
instancenames
)
2477
elseif
not
subfont
then
2478
local
info
=
{
}
2479
for
i
=
1
,
#
subfonts
do
2480
info
[
i
]
=
getinfo
(
fontdata
,
i
,
platformnames
,
rawfamilynames
,
false
,
instancenames
)
2481
end
2482
return
info
2483
elseif
subfont
>
=
1
and
subfont
<
=
#
subfonts
then
2484
return
getinfo
(
fontdata
,
subfont
,
platformnames
,
rawfamilynames
,
false
,
instancenames
)
2485
else
2486
return
{
2487
filename
=
filename
,
2488
comment
=
"
there is no subfont
"
.
.
subfont
.
.
"
in this file
"
2489
}
2490
end
2491
else
2492
return
{
2493
filename
=
filename
,
2494
comment
=
"
the file cannot be opened for reading
"
,
2495
}
2496
end
2497
end
2498 2499
function
readers
.
rehash
(
fontdata
,
hashmethod
)
2500
report
(
"
the %a helper is not yet implemented
"
,
"
rehash
"
)
2501
end
2502 2503
function
readers
.
checkhash
(
fontdata
)
2504
report
(
"
the %a helper is not yet implemented
"
,
"
checkhash
"
)
2505
end
2506 2507
function
readers
.
pack
(
fontdata
,
hashmethod
)
2508
report
(
"
the %a helper is not yet implemented
"
,
"
pack
"
)
2509
end
2510 2511
function
readers
.
unpack
(
fontdata
)
2512
report
(
"
the %a helper is not yet implemented
"
,
"
unpack
"
)
2513
end
2514 2515
function
readers
.
expand
(
fontdata
)
2516
report
(
"
the %a helper is not yet implemented
"
,
"
unpack
"
)
2517
end
2518 2519
function
readers
.
compact
(
fontdata
)
2520
report
(
"
the %a helper is not yet implemented
"
,
"
compact
"
)
2521
end
2522 2523
-- plug in
2524 2525
local
extenders
=
{
}
2526 2527
function
readers
.
registerextender
(
extender
)
2528
extenders
[
#
extenders
+
1
]
=
extender
2529
end
2530 2531
function
readers
.
extend
(
fontdata
)
2532
for
i
=
1
,
#
extenders
do
2533
local
extender
=
extenders
[
i
]
2534
local
name
=
extender
.
name
or
"
unknown
"
2535
local
action
=
extender
.
action
2536
if
action
then
2537
action
(
fontdata
)
2538
end
2539
end
2540
end
2541