imagescript 1.3.1 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (132) hide show
  1. package/LICENSE +4 -1
  2. package/README.md +76 -26
  3. package/THIRD_PARTY_NOTICES.md +156 -0
  4. package/dist/animation.d.ts +55 -0
  5. package/dist/animation.js +167 -0
  6. package/dist/codecs/crc.d.ts +9 -0
  7. package/dist/codecs/crc.js +29 -0
  8. package/dist/codecs/exif.d.ts +11 -0
  9. package/dist/codecs/exif.js +27 -0
  10. package/dist/codecs/gif.d.ts +44 -0
  11. package/dist/codecs/gif.js +401 -0
  12. package/dist/codecs/jpeg.d.ts +35 -0
  13. package/dist/codecs/jpeg.js +980 -0
  14. package/dist/codecs/png.d.ts +97 -0
  15. package/dist/codecs/png.js +664 -0
  16. package/dist/codecs/quantize.d.ts +25 -0
  17. package/dist/codecs/quantize.js +302 -0
  18. package/dist/codecs/tga.d.ts +16 -0
  19. package/dist/codecs/tga.js +203 -0
  20. package/dist/codecs/tiff.d.ts +6 -0
  21. package/dist/codecs/tiff.js +546 -0
  22. package/dist/codecs/vp8-tables.d.ts +10 -0
  23. package/dist/codecs/vp8-tables.js +17 -0
  24. package/dist/codecs/vp8.d.ts +8 -0
  25. package/dist/codecs/vp8.js +711 -0
  26. package/dist/codecs/vp8l.d.ts +62 -0
  27. package/dist/codecs/vp8l.js +909 -0
  28. package/dist/codecs/webp.d.ts +67 -0
  29. package/dist/codecs/webp.js +280 -0
  30. package/dist/codecs/zlib.d.ts +22 -0
  31. package/dist/codecs/zlib.js +607 -0
  32. package/dist/frame.d.ts +57 -0
  33. package/dist/frame.js +65 -0
  34. package/dist/gif.d.ts +31 -0
  35. package/dist/gif.js +20 -0
  36. package/dist/image-type.d.ts +24 -0
  37. package/dist/image-type.js +64 -0
  38. package/dist/image.d.ts +553 -0
  39. package/dist/image.js +1156 -0
  40. package/dist/index.d.ts +23 -0
  41. package/dist/index.js +20 -0
  42. package/dist/package.json +1 -0
  43. package/dist/render/font.d.ts +92 -0
  44. package/dist/render/font.js +954 -0
  45. package/dist/render/hinting.d.ts +110 -0
  46. package/dist/render/hinting.js +1236 -0
  47. package/dist/render/svg.d.ts +11 -0
  48. package/dist/render/svg.js +1717 -0
  49. package/dist/render/text.d.ts +63 -0
  50. package/dist/render/text.js +342 -0
  51. package/dist/text-layout.d.ts +46 -0
  52. package/dist/text-layout.js +35 -0
  53. package/dist/util/blur.d.ts +13 -0
  54. package/dist/util/blur.js +146 -0
  55. package/dist/util/mem.d.ts +10 -0
  56. package/dist/util/mem.js +23 -0
  57. package/dist/util/resample.d.ts +12 -0
  58. package/dist/util/resample.js +99 -0
  59. package/dist/util/utf8.d.ts +5 -0
  60. package/dist/util/utf8.js +55 -0
  61. package/dist/version.d.ts +1 -0
  62. package/dist/version.js +1 -0
  63. package/licenses/Apache-2.0.txt +202 -0
  64. package/licenses/FTL.txt +169 -0
  65. package/licenses/README.ijg +260 -0
  66. package/package.json +31 -7
  67. package/ImageScript.d.ts +0 -954
  68. package/ImageScript.js +0 -1669
  69. package/bench/_.mjs +0 -166
  70. package/bench/all.mjs +0 -4
  71. package/bench/blur.mjs +0 -59
  72. package/bench/flip.mjs +0 -63
  73. package/bench/overlay.mjs +0 -35
  74. package/bench/package.json +0 -7
  75. package/bench/readme.md +0 -64
  76. package/bench/resize.mjs +0 -52
  77. package/codecs/LICENSE +0 -21
  78. package/codecs/lib.d.ts +0 -50
  79. package/codecs/lib.js +0 -6
  80. package/codecs/lib.mjs +0 -1
  81. package/codecs/node/bin/arm64-darwin.node +0 -0
  82. package/codecs/node/bin/arm64-linux.node +0 -0
  83. package/codecs/node/bin/x64-darwin.node +0 -0
  84. package/codecs/node/bin/x64-linux.node +0 -0
  85. package/codecs/node/bin/x64-win32.node +0 -0
  86. package/codecs/node/index.js +0 -3
  87. package/codecs/node/index.mjs +0 -6
  88. package/codecs/package.json +0 -14
  89. package/codecs/readme.md +0 -38
  90. package/codecs/wasm/bin/codecs.wasm +0 -0
  91. package/codecs/wasm/bin/simd.wasm +0 -0
  92. package/codecs/wasm/bundle/browser.js +0 -1
  93. package/codecs/wasm/index.js +0 -1
  94. package/codecs/wasm/index.mjs +0 -1
  95. package/png/README.md +0 -1
  96. package/png/node.js +0 -1137
  97. package/png/src/crc.mjs +0 -49
  98. package/png/src/mem.mjs +0 -24
  99. package/png/src/png.mjs +0 -254
  100. package/png/src/zlib.mjs +0 -618
  101. package/tsconfig.json +0 -21
  102. package/utils/mem.js +0 -26
  103. package/v2/codecs/magic.js +0 -25
  104. package/v2/framebuffer.d.ts +0 -67
  105. package/v2/framebuffer.js +0 -2352
  106. package/v2/framebuffer.mjs +0 -126
  107. package/v2/framebuffer.rs +0 -1264
  108. package/v2/index.d.ts +0 -84
  109. package/v2/index.js +0 -261
  110. package/v2/ops/blur.mjs +0 -303
  111. package/v2/ops/color.mjs +0 -277
  112. package/v2/ops/crop.mjs +0 -64
  113. package/v2/ops/fill.mjs +0 -33
  114. package/v2/ops/flip.mjs +0 -33
  115. package/v2/ops/index.mjs +0 -9
  116. package/v2/ops/iterator.mjs +0 -34
  117. package/v2/ops/overlay.mjs +0 -63
  118. package/v2/ops/resize.mjs +0 -166
  119. package/v2/ops/rotate.mjs +0 -117
  120. package/v2/util/mem.mjs +0 -24
  121. package/wasm/any/font.wasm +0 -0
  122. package/wasm/any/gif.wasm +0 -0
  123. package/wasm/any/jpeg.wasm +0 -0
  124. package/wasm/any/png.wasm +0 -0
  125. package/wasm/any/svg.wasm +0 -0
  126. package/wasm/any/tiff.wasm +0 -0
  127. package/wasm/node/font.js +0 -142
  128. package/wasm/node/gif.js +0 -76
  129. package/wasm/node/jpeg.js +0 -92
  130. package/wasm/node/png.js +0 -49
  131. package/wasm/node/svg.js +0 -56
  132. package/wasm/node/tiff.js +0 -47
@@ -0,0 +1,954 @@
1
+ export class Font {
2
+ data;
3
+ view;
4
+ tables;
5
+ unitsPerEm;
6
+ indexToLocFormat;
7
+ ascender;
8
+ descender;
9
+ lineGap;
10
+ numberOfHMetrics;
11
+ numGlyphs;
12
+ outlines;
13
+ cff = null;
14
+ cmap;
15
+ outlineCache;
16
+ kerning;
17
+ constructor(data) {
18
+ this.data = data;
19
+ this.view = new DataView(data.buffer, data.byteOffset, data.byteLength);
20
+ let offset = 0;
21
+ const signature = this.u32(0);
22
+ if (signature === 0x74746366)
23
+ offset = this.u32(12);
24
+ else if (signature === 0x774f4646 || signature === 0x774f4632)
25
+ throw new Error('Font: WOFF fonts are not supported');
26
+ const version = this.u32(offset);
27
+ if (version !== 0x00010000 && version !== 0x4f54544f && version !== 0x74727565)
28
+ throw new Error('Font: invalid font file');
29
+ this.tables = {};
30
+ const count = this.u16(offset + 4);
31
+ for (let i = 0; i < count; i++) {
32
+ const record = offset + 12 + i * 16;
33
+ const tag = String.fromCharCode(...data.subarray(record, record + 4));
34
+ this.tables[tag] = { offset: this.u32(record + 8), length: this.u32(record + 12) };
35
+ }
36
+ for (const tag of ['head', 'hhea', 'hmtx', 'maxp', 'cmap'])
37
+ if (!this.tables[tag])
38
+ throw new Error(`Font: missing ${tag} table`);
39
+ const head = this.tables.head.offset;
40
+ this.unitsPerEm = this.u16(head + 18);
41
+ this.indexToLocFormat = this.i16(head + 50);
42
+ const hhea = this.tables.hhea.offset;
43
+ this.ascender = this.i16(hhea + 4);
44
+ this.descender = this.i16(hhea + 6);
45
+ this.lineGap = this.i16(hhea + 8);
46
+ this.numberOfHMetrics = this.u16(hhea + 34);
47
+ this.numGlyphs = this.u16(this.tables.maxp.offset + 4);
48
+ if (this.tables.glyf && this.tables.loca)
49
+ this.outlines = 'glyf';
50
+ else if (this.tables['CFF ']) {
51
+ this.outlines = 'cff';
52
+ this.cff = parseCFF(this, this.tables['CFF '].offset);
53
+ }
54
+ else if (this.tables.CFF2)
55
+ throw new Error('Font: CFF2 outlines are not supported');
56
+ else
57
+ throw new Error('Font: no supported glyph outlines');
58
+ this.cmap = parseCmap(this, this.tables.cmap.offset);
59
+ this.outlineCache = new Map();
60
+ this.kerning = (this.tables.GPOS && parseGPOSKerning(this, this.tables.GPOS.offset)) || (this.tables.kern && parseKern(this, this.tables.kern.offset)) || null;
61
+ }
62
+ u8(o) { return this.data[o]; }
63
+ i8(o) { return this.view.getInt8(o); }
64
+ u16(o) { return this.view.getUint16(o); }
65
+ i16(o) { return this.view.getInt16(o); }
66
+ u32(o) { return this.view.getUint32(o); }
67
+ i32(o) { return this.view.getInt32(o); }
68
+ glyphIndex(codepoint) {
69
+ return this.cmap(codepoint) || 0;
70
+ }
71
+ advance(glyph) {
72
+ const hmtx = this.tables.hmtx.offset;
73
+ const index = Math.min(glyph, this.numberOfHMetrics - 1);
74
+ return this.u16(hmtx + index * 4);
75
+ }
76
+ leftSideBearing(glyph) {
77
+ const hmtx = this.tables.hmtx.offset;
78
+ if (glyph < this.numberOfHMetrics)
79
+ return this.i16(hmtx + glyph * 4 + 2);
80
+ return this.i16(hmtx + this.numberOfHMetrics * 4 + (glyph - this.numberOfHMetrics) * 2);
81
+ }
82
+ glyfPoints(glyph) {
83
+ if (this.outlines !== 'glyf' || glyph >= this.numGlyphs)
84
+ return null;
85
+ const loca = this.tables.loca.offset;
86
+ const start = this.indexToLocFormat ? this.u32(loca + glyph * 4) : this.u16(loca + glyph * 2) * 2;
87
+ const end = this.indexToLocFormat ? this.u32(loca + glyph * 4 + 4) : this.u16(loca + glyph * 2 + 2) * 2;
88
+ if (end <= start)
89
+ return null;
90
+ const offset = this.tables.glyf.offset + start;
91
+ const contourCount = this.i16(offset);
92
+ const xMin = this.i16(offset + 2);
93
+ if (contourCount < 0) {
94
+ const components = [];
95
+ let p = offset + 10, flags = 0;
96
+ do {
97
+ flags = this.u16(p);
98
+ const component = this.u16(p + 2);
99
+ p += 4;
100
+ let arg1, arg2;
101
+ if (flags & 1) {
102
+ arg1 = flags & 2 ? this.i16(p) : this.u16(p);
103
+ arg2 = flags & 2 ? this.i16(p + 2) : this.u16(p + 2);
104
+ p += 4;
105
+ }
106
+ else {
107
+ arg1 = flags & 2 ? this.i8(p) : this.u8(p);
108
+ arg2 = flags & 2 ? this.i8(p + 1) : this.u8(p + 1);
109
+ p += 2;
110
+ }
111
+ let matrix = [1, 0, 0, 1];
112
+ const f2dot14 = (o) => this.i16(o) / 16384;
113
+ if (flags & 8) {
114
+ const s = f2dot14(p);
115
+ matrix = [s, 0, 0, s];
116
+ p += 2;
117
+ }
118
+ else if (flags & 0x40) {
119
+ matrix = [f2dot14(p), 0, 0, f2dot14(p + 2)];
120
+ p += 4;
121
+ }
122
+ else if (flags & 0x80) {
123
+ matrix = [f2dot14(p), f2dot14(p + 2), f2dot14(p + 4), f2dot14(p + 6)];
124
+ p += 8;
125
+ }
126
+ components.push({
127
+ glyph: component,
128
+ dx: flags & 2 ? arg1 : 0,
129
+ dy: flags & 2 ? arg2 : 0,
130
+ matrix,
131
+ round: (flags & 4) !== 0,
132
+ match: flags & 2 ? null : [arg1, arg2],
133
+ useMetrics: (flags & 0x200) !== 0
134
+ });
135
+ } while (flags & 0x20);
136
+ const instructions = flags & 0x100 ? this.data.subarray(p + 2, p + 2 + this.u16(p)) : new Uint8Array(0);
137
+ return { xs: [], ys: [], on: [], ends: [], instructions, xMin, components };
138
+ }
139
+ const ends = [];
140
+ for (let i = 0; i < contourCount; i++)
141
+ ends.push(this.u16(offset + 10 + i * 2));
142
+ const pointCount = contourCount ? ends[contourCount - 1] + 1 : 0;
143
+ let p = offset + 10 + contourCount * 2;
144
+ const instructionLength = this.u16(p);
145
+ const instructions = this.data.subarray(p + 2, p + 2 + instructionLength);
146
+ p += 2 + instructionLength;
147
+ const flags = new Uint8Array(pointCount);
148
+ for (let i = 0; i < pointCount;) {
149
+ const flag = this.u8(p++);
150
+ flags[i++] = flag;
151
+ if (flag & 8)
152
+ for (let repeat = this.u8(p++); repeat > 0 && i < pointCount; repeat--)
153
+ flags[i++] = flag;
154
+ }
155
+ const xs = [], ys = [];
156
+ for (const [coords, short, same] of [[xs, 2, 16], [ys, 4, 32]]) {
157
+ let value = 0;
158
+ for (let i = 0; i < pointCount; i++) {
159
+ const flag = flags[i];
160
+ if (flag & short) {
161
+ const delta = this.u8(p++);
162
+ value += flag & same ? delta : -delta;
163
+ }
164
+ else if (!(flag & same)) {
165
+ value += this.i16(p);
166
+ p += 2;
167
+ }
168
+ coords.push(value);
169
+ }
170
+ }
171
+ return { xs, ys, on: Array.from(flags, f => (f & 1) === 1), ends, instructions, xMin, components: null };
172
+ }
173
+ outline(glyph) {
174
+ if (glyph >= this.numGlyphs)
175
+ glyph = 0;
176
+ let path = this.outlineCache.get(glyph);
177
+ if (!path) {
178
+ path = this.outlines === 'glyf' ? this.glyfOutline(glyph, 0) : this.cffOutline(glyph);
179
+ this.outlineCache.set(glyph, path);
180
+ }
181
+ return path;
182
+ }
183
+ glyfOutline(glyph, depth) {
184
+ if (depth > 8)
185
+ return [];
186
+ const loca = this.tables.loca.offset;
187
+ const start = this.indexToLocFormat ? this.u32(loca + glyph * 4) : this.u16(loca + glyph * 2) * 2;
188
+ const end = this.indexToLocFormat ? this.u32(loca + glyph * 4 + 4) : this.u16(loca + glyph * 2 + 2) * 2;
189
+ if (end <= start)
190
+ return [];
191
+ const offset = this.tables.glyf.offset + start;
192
+ const contours = this.i16(offset);
193
+ return contours >= 0 ? this.simpleGlyph(offset, contours) : this.compositeGlyph(offset, depth);
194
+ }
195
+ simpleGlyph(offset, contourCount) {
196
+ const endPoints = [];
197
+ for (let i = 0; i < contourCount; i++)
198
+ endPoints.push(this.u16(offset + 10 + i * 2));
199
+ const pointCount = contourCount ? endPoints[contourCount - 1] + 1 : 0;
200
+ let p = offset + 10 + contourCount * 2;
201
+ p += 2 + this.u16(p);
202
+ const flags = new Uint8Array(pointCount);
203
+ for (let i = 0; i < pointCount;) {
204
+ const flag = this.u8(p++);
205
+ flags[i++] = flag;
206
+ if (flag & 8) {
207
+ for (let repeat = this.u8(p++); repeat > 0 && i < pointCount; repeat--)
208
+ flags[i++] = flag;
209
+ }
210
+ }
211
+ const xs = new Int32Array(pointCount);
212
+ const ys = new Int32Array(pointCount);
213
+ for (const [coords, short, same] of [[xs, 2, 16], [ys, 4, 32]]) {
214
+ let value = 0;
215
+ for (let i = 0; i < pointCount; i++) {
216
+ const flag = flags[i];
217
+ if (flag & short) {
218
+ const delta = this.u8(p++);
219
+ value += flag & same ? delta : -delta;
220
+ }
221
+ else if (!(flag & same)) {
222
+ value += this.i16(p);
223
+ p += 2;
224
+ }
225
+ coords[i] = value;
226
+ }
227
+ }
228
+ const path = [];
229
+ let first = 0;
230
+ for (const last of endPoints) {
231
+ contourToPath(path, xs, ys, flags, first, last);
232
+ first = last + 1;
233
+ }
234
+ return path;
235
+ }
236
+ compositeGlyph(offset, depth) {
237
+ const path = [];
238
+ let p = offset + 10;
239
+ for (let more = true; more;) {
240
+ const flags = this.u16(p);
241
+ const glyph = this.u16(p + 2);
242
+ p += 4;
243
+ let dx, dy;
244
+ if (flags & 1) {
245
+ dx = flags & 2 ? this.i16(p) : this.u16(p);
246
+ dy = flags & 2 ? this.i16(p + 2) : this.u16(p + 2);
247
+ p += 4;
248
+ }
249
+ else {
250
+ dx = flags & 2 ? this.i8(p) : this.u8(p);
251
+ dy = flags & 2 ? this.i8(p + 1) : this.u8(p + 1);
252
+ p += 2;
253
+ }
254
+ if (!(flags & 2))
255
+ dx = dy = 0;
256
+ let a = 1, b = 0, c = 0, d = 1;
257
+ const f2dot14 = (o) => this.i16(o) / 16384;
258
+ if (flags & 8) {
259
+ a = d = f2dot14(p);
260
+ p += 2;
261
+ }
262
+ else if (flags & 0x40) {
263
+ a = f2dot14(p);
264
+ d = f2dot14(p + 2);
265
+ p += 4;
266
+ }
267
+ else if (flags & 0x80) {
268
+ a = f2dot14(p);
269
+ b = f2dot14(p + 2);
270
+ c = f2dot14(p + 4);
271
+ d = f2dot14(p + 6);
272
+ p += 8;
273
+ }
274
+ for (const command of this.glyfOutline(glyph, depth + 1)) {
275
+ const transformed = [command[0]];
276
+ for (let i = 1; i < command.length; i += 2) {
277
+ const x = command[i], y = command[i + 1];
278
+ transformed.push(a * x + c * y + dx, b * x + d * y + dy);
279
+ }
280
+ path.push(transformed);
281
+ }
282
+ more = !!(flags & 0x20);
283
+ }
284
+ return path;
285
+ }
286
+ cffOutline(glyph) {
287
+ return cffCharString(this.cff, glyph);
288
+ }
289
+ }
290
+ export function contourToPath(path, xs, ys, flags, first, last) {
291
+ if (last < first)
292
+ return;
293
+ const count = last - first + 1;
294
+ const on = (i) => flags[first + (i % count)] === true || (flags[first + (i % count)] & 1) === 1;
295
+ const x = (i) => xs[first + (i % count)];
296
+ const y = (i) => ys[first + (i % count)];
297
+ let start = -1;
298
+ for (let i = 0; i < count; i++)
299
+ if (on(i)) {
300
+ start = i;
301
+ break;
302
+ }
303
+ let sx, sy;
304
+ if (start === -1) {
305
+ sx = (x(0) + x(1)) / 2;
306
+ sy = (y(0) + y(1)) / 2;
307
+ start = 1;
308
+ }
309
+ else {
310
+ sx = x(start);
311
+ sy = y(start);
312
+ start++;
313
+ }
314
+ path.push(['M', sx, sy]);
315
+ let cx = 0, cy = 0, pending = false;
316
+ for (let n = 0; n < count; n++) {
317
+ const i = start + n;
318
+ const px = x(i), py = y(i);
319
+ if (on(i)) {
320
+ if (!pending)
321
+ path.push(['L', px, py]);
322
+ else {
323
+ path.push(['Q', cx, cy, px, py]);
324
+ pending = false;
325
+ }
326
+ }
327
+ else {
328
+ if (pending) {
329
+ const mx = (cx + px) / 2, my = (cy + py) / 2;
330
+ path.push(['Q', cx, cy, mx, my]);
331
+ }
332
+ cx = px;
333
+ cy = py;
334
+ pending = true;
335
+ }
336
+ }
337
+ if (pending)
338
+ path.push(['Q', cx, cy, sx, sy]);
339
+ path.push(['Z']);
340
+ }
341
+ function parseCmap(font, offset) {
342
+ const count = font.u16(offset + 2);
343
+ const subtables = [];
344
+ for (let i = 0; i < count; i++) {
345
+ const record = offset + 4 + i * 8;
346
+ const platform = font.u16(record);
347
+ const encoding = font.u16(record + 2);
348
+ const table = offset + font.u32(record + 4);
349
+ subtables.push({ platform, encoding, table, format: font.u16(table) });
350
+ }
351
+ const rank = ({ platform, encoding, format }) => {
352
+ if (![0, 4, 6, 12].includes(format))
353
+ return -1;
354
+ if (platform === 3 && encoding === 10)
355
+ return 6;
356
+ if (platform === 0 && (encoding === 4 || encoding === 6))
357
+ return 5;
358
+ if (platform === 3 && encoding === 1)
359
+ return 4;
360
+ if (platform === 0)
361
+ return 3;
362
+ if (platform === 3 && encoding === 0)
363
+ return 2;
364
+ if (platform === 1 && encoding === 0)
365
+ return 1;
366
+ return -1;
367
+ };
368
+ const best = subtables.filter(s => rank(s) >= 0).sort((a, b) => rank(b) - rank(a))[0];
369
+ if (!best)
370
+ throw new Error('Font: no supported character map');
371
+ const { table, format, platform, encoding } = best;
372
+ const symbol = platform === 3 && encoding === 0;
373
+ let lookup;
374
+ if (format === 0)
375
+ lookup = (c) => c < 256 ? font.u8(table + 6 + c) : 0;
376
+ else if (format === 6) {
377
+ const first = font.u16(table + 6), entries = font.u16(table + 8);
378
+ lookup = (c) => c >= first && c < first + entries ? font.u16(table + 10 + (c - first) * 2) : 0;
379
+ }
380
+ else if (format === 4) {
381
+ const segments = font.u16(table + 6) / 2;
382
+ const ends = table + 14;
383
+ const starts = ends + segments * 2 + 2;
384
+ const deltas = starts + segments * 2;
385
+ const rangeOffsets = deltas + segments * 2;
386
+ lookup = (c) => {
387
+ if (c > 0xffff)
388
+ return 0;
389
+ let lo = 0, hi = segments - 1;
390
+ while (lo < hi) {
391
+ const mid = lo + hi >> 1;
392
+ if (font.u16(ends + mid * 2) < c)
393
+ lo = mid + 1;
394
+ else
395
+ hi = mid;
396
+ }
397
+ const start = font.u16(starts + lo * 2);
398
+ if (c < start || c > font.u16(ends + lo * 2))
399
+ return 0;
400
+ const delta = font.u16(deltas + lo * 2);
401
+ const rangeOffset = font.u16(rangeOffsets + lo * 2);
402
+ if (!rangeOffset)
403
+ return c + delta & 0xffff;
404
+ const glyph = font.u16(rangeOffsets + lo * 2 + rangeOffset + (c - start) * 2);
405
+ return glyph ? glyph + delta & 0xffff : 0;
406
+ };
407
+ }
408
+ else {
409
+ const groups = font.u32(table + 12);
410
+ lookup = (c) => {
411
+ let lo = 0, hi = groups - 1;
412
+ while (lo <= hi) {
413
+ const mid = lo + hi >> 1;
414
+ const group = table + 16 + mid * 12;
415
+ if (c < font.u32(group))
416
+ hi = mid - 1;
417
+ else if (c > font.u32(group + 4))
418
+ lo = mid + 1;
419
+ else
420
+ return font.u32(group + 8) + c - font.u32(group);
421
+ }
422
+ return 0;
423
+ };
424
+ }
425
+ return symbol ? (c) => lookup(c) || (c < 256 ? lookup(0xf000 + c) : 0) : lookup;
426
+ }
427
+ function parseGPOSKerning(font, gpos) {
428
+ const featureList = gpos + font.u16(gpos + 6);
429
+ const lookupList = gpos + font.u16(gpos + 8);
430
+ const lookups = new Set();
431
+ for (let i = 0, count = font.u16(featureList); i < count; i++) {
432
+ const record = featureList + 2 + i * 6;
433
+ if (font.u32(record) !== 0x6b65726e)
434
+ continue;
435
+ const feature = featureList + font.u16(record + 4);
436
+ for (let j = 0, n = font.u16(feature + 2); j < n; j++)
437
+ lookups.add(font.u16(feature + 4 + j * 2));
438
+ }
439
+ const pairLookups = [];
440
+ for (const index of [...lookups].sort((a, b) => a - b)) {
441
+ if (index >= font.u16(lookupList))
442
+ continue;
443
+ const lookup = lookupList + font.u16(lookupList + 2 + index * 2);
444
+ const type = font.u16(lookup);
445
+ const subtables = [];
446
+ for (let i = 0, count = font.u16(lookup + 4); i < count; i++) {
447
+ let subtable = lookup + font.u16(lookup + 6 + i * 2);
448
+ if (type === 9) {
449
+ if (font.u16(subtable + 2) !== 2)
450
+ continue;
451
+ subtable += font.u32(subtable + 4);
452
+ }
453
+ else if (type !== 2)
454
+ continue;
455
+ const pair = parsePairPos(font, subtable);
456
+ if (pair)
457
+ subtables.push(pair);
458
+ }
459
+ if (subtables.length)
460
+ pairLookups.push(subtables);
461
+ }
462
+ if (!pairLookups.length)
463
+ return null;
464
+ return (left, right) => {
465
+ let total = 0;
466
+ for (const subtables of pairLookups) {
467
+ for (const subtable of subtables) {
468
+ const value = subtable(left, right);
469
+ if (value === undefined)
470
+ continue;
471
+ total += value;
472
+ break;
473
+ }
474
+ }
475
+ return total;
476
+ };
477
+ }
478
+ function valueRecord(format) {
479
+ let size = 0, xAdvance = -1;
480
+ for (let bit = 0; bit < 8; bit++) {
481
+ if (!(format & 1 << bit))
482
+ continue;
483
+ if (bit === 2)
484
+ xAdvance = size;
485
+ size += 2;
486
+ }
487
+ return { size, xAdvance };
488
+ }
489
+ function parsePairPos(font, table) {
490
+ const format = font.u16(table);
491
+ const coverage = parseCoverage(font, table + font.u16(table + 2));
492
+ const first = valueRecord(font.u16(table + 4));
493
+ const second = valueRecord(font.u16(table + 6));
494
+ if (first.xAdvance === -1)
495
+ return null;
496
+ if (format === 1) {
497
+ const sets = font.u16(table + 8);
498
+ const recordSize = 2 + first.size + second.size;
499
+ return (left, right) => {
500
+ const index = coverage(left);
501
+ if (index === -1 || index >= sets)
502
+ return undefined;
503
+ const set = table + font.u16(table + 10 + index * 2);
504
+ let lo = 0, hi = font.u16(set) - 1;
505
+ while (lo <= hi) {
506
+ const mid = lo + hi >> 1;
507
+ const record = set + 2 + mid * recordSize;
508
+ const glyph = font.u16(record);
509
+ if (glyph < right)
510
+ lo = mid + 1;
511
+ else if (glyph > right)
512
+ hi = mid - 1;
513
+ else
514
+ return font.i16(record + 2 + first.xAdvance);
515
+ }
516
+ return undefined;
517
+ };
518
+ }
519
+ if (format === 2) {
520
+ const classes1 = parseClassDef(font, table + font.u16(table + 8));
521
+ const classes2 = parseClassDef(font, table + font.u16(table + 10));
522
+ const class1Count = font.u16(table + 12), class2Count = font.u16(table + 14);
523
+ const recordSize = first.size + second.size;
524
+ return (left, right) => {
525
+ if (coverage(left) === -1)
526
+ return undefined;
527
+ const c1 = classes1(left), c2 = classes2(right);
528
+ if (c1 >= class1Count || c2 >= class2Count)
529
+ return undefined;
530
+ return font.i16(table + 16 + (c1 * class2Count + c2) * recordSize + first.xAdvance);
531
+ };
532
+ }
533
+ return null;
534
+ }
535
+ function parseCoverage(font, table) {
536
+ const format = font.u16(table), count = font.u16(table + 2);
537
+ return glyph => {
538
+ let lo = 0, hi = count - 1;
539
+ while (lo <= hi) {
540
+ const mid = lo + hi >> 1;
541
+ if (format === 1) {
542
+ const value = font.u16(table + 4 + mid * 2);
543
+ if (value < glyph)
544
+ lo = mid + 1;
545
+ else if (value > glyph)
546
+ hi = mid - 1;
547
+ else
548
+ return mid;
549
+ }
550
+ else {
551
+ const range = table + 4 + mid * 6;
552
+ if (font.u16(range + 2) < glyph)
553
+ lo = mid + 1;
554
+ else if (font.u16(range) > glyph)
555
+ hi = mid - 1;
556
+ else
557
+ return font.u16(range + 4) + glyph - font.u16(range);
558
+ }
559
+ }
560
+ return -1;
561
+ };
562
+ }
563
+ function parseClassDef(font, table) {
564
+ const format = font.u16(table);
565
+ if (format === 1) {
566
+ const start = font.u16(table + 2), count = font.u16(table + 4);
567
+ return glyph => glyph >= start && glyph < start + count ? font.u16(table + 6 + (glyph - start) * 2) : 0;
568
+ }
569
+ const count = font.u16(table + 2);
570
+ return glyph => {
571
+ let lo = 0, hi = count - 1;
572
+ while (lo <= hi) {
573
+ const mid = lo + hi >> 1;
574
+ const range = table + 4 + mid * 6;
575
+ if (font.u16(range + 2) < glyph)
576
+ lo = mid + 1;
577
+ else if (font.u16(range) > glyph)
578
+ hi = mid - 1;
579
+ else
580
+ return font.u16(range + 4);
581
+ }
582
+ return 0;
583
+ };
584
+ }
585
+ function parseKern(font, kern) {
586
+ if (font.u16(kern) !== 0)
587
+ return null;
588
+ const tables = [];
589
+ let offset = kern + 4;
590
+ for (let i = 0, count = font.u16(kern + 2); i < count; i++) {
591
+ const coverage = font.u16(offset + 4);
592
+ const length = coverage >> 8 === 0 ? 14 + font.u16(offset + 6) * 6 : font.u16(offset + 2);
593
+ if ((coverage & 0xff0f) === 1)
594
+ tables.push({ pairs: font.u16(offset + 6), start: offset + 14 });
595
+ offset += length;
596
+ }
597
+ if (!tables.length)
598
+ return null;
599
+ return (left, right) => {
600
+ const key = (left << 16 | right) >>> 0;
601
+ let total = 0;
602
+ for (const { pairs, start } of tables) {
603
+ let lo = 0, hi = pairs - 1;
604
+ while (lo <= hi) {
605
+ const mid = lo + hi >> 1;
606
+ const pair = start + mid * 6;
607
+ const value = font.u32(pair);
608
+ if (value < key)
609
+ lo = mid + 1;
610
+ else if (value > key)
611
+ hi = mid - 1;
612
+ else {
613
+ total += font.i16(pair + 4);
614
+ break;
615
+ }
616
+ }
617
+ }
618
+ return total;
619
+ };
620
+ }
621
+ function parseCFF(font, base) {
622
+ const data = font.data;
623
+ const readIndex = (offset) => {
624
+ const count = font.u16(offset);
625
+ if (count === 0)
626
+ return { items: [], end: offset + 2 };
627
+ const offSize = data[offset + 2];
628
+ const readOffset = (o) => {
629
+ let v = 0;
630
+ for (let i = 0; i < offSize; i++)
631
+ v = v * 256 + data[o + i];
632
+ return v;
633
+ };
634
+ const dataStart = offset + 3 + (count + 1) * offSize - 1;
635
+ const items = [];
636
+ for (let i = 0; i < count; i++) {
637
+ const start = dataStart + readOffset(offset + 3 + i * offSize);
638
+ const end = dataStart + readOffset(offset + 3 + (i + 1) * offSize);
639
+ items.push(data.subarray(start, end));
640
+ }
641
+ return { items, end: dataStart + readOffset(offset + 3 + count * offSize) };
642
+ };
643
+ const headerSize = data[base + 2];
644
+ const names = readIndex(base + headerSize);
645
+ const topDicts = readIndex(names.end);
646
+ const strings = readIndex(topDicts.end);
647
+ const globalSubrs = readIndex(strings.end).items;
648
+ const top = parseDict(topDicts.items[0]);
649
+ if (!top[17])
650
+ throw new Error('Font: CFF font has no charstrings');
651
+ const charStrings = readIndex(base + top[17][0]).items;
652
+ const readPrivate = (entry) => {
653
+ if (!entry)
654
+ return { subrs: [] };
655
+ const [size, offset] = entry;
656
+ const dict = parseDict(data.subarray(base + offset, base + offset + size));
657
+ const subrs = dict[19] ? readIndex(base + offset + dict[19][0]).items : [];
658
+ return { subrs };
659
+ };
660
+ let privateFor;
661
+ if (top[1236]) {
662
+ const fdArray = readIndex(base + top[1236][0]).items.map(dict => readPrivate(parseDict(dict)[18]));
663
+ const fdSelect = base + top[1237][0];
664
+ const format = data[fdSelect];
665
+ privateFor = glyph => {
666
+ if (format === 0)
667
+ return fdArray[data[fdSelect + 1 + glyph]];
668
+ const ranges = font.u16(fdSelect + 1);
669
+ for (let i = 0; i < ranges; i++) {
670
+ const r = fdSelect + 3 + i * 3;
671
+ const next = font.u16(r + 3);
672
+ if (glyph >= font.u16(r) && glyph < next)
673
+ return fdArray[data[r + 2]];
674
+ }
675
+ return fdArray[0];
676
+ };
677
+ }
678
+ else {
679
+ const shared = readPrivate(top[18]);
680
+ privateFor = () => shared;
681
+ }
682
+ return { charStrings, globalSubrs, privateFor };
683
+ }
684
+ function parseDict(bytes) {
685
+ const dict = {};
686
+ const operands = [];
687
+ for (let i = 0; i < bytes.length;) {
688
+ const b0 = bytes[i++];
689
+ if (b0 <= 21) {
690
+ const op = b0 === 12 ? 1200 + bytes[i++] : b0;
691
+ dict[op] = operands.splice(0);
692
+ }
693
+ else if (b0 === 28) {
694
+ operands.push((bytes[i] << 24 | bytes[i + 1] << 16) >> 16);
695
+ i += 2;
696
+ }
697
+ else if (b0 === 29) {
698
+ operands.push(bytes[i] << 24 | bytes[i + 1] << 16 | bytes[i + 2] << 8 | bytes[i + 3]);
699
+ i += 4;
700
+ }
701
+ else if (b0 === 30) {
702
+ let s = '';
703
+ for (let done = false; !done;) {
704
+ const byte = bytes[i++];
705
+ for (const nibble of [byte >> 4, byte & 15]) {
706
+ if (nibble === 15) {
707
+ done = true;
708
+ break;
709
+ }
710
+ s += ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.', 'E', 'E-', '', '-'][nibble];
711
+ }
712
+ }
713
+ operands.push(parseFloat(s));
714
+ }
715
+ else if (b0 >= 32 && b0 <= 246)
716
+ operands.push(b0 - 139);
717
+ else if (b0 >= 247 && b0 <= 250)
718
+ operands.push((b0 - 247) * 256 + bytes[i++] + 108);
719
+ else if (b0 >= 251 && b0 <= 254)
720
+ operands.push(-(b0 - 251) * 256 - bytes[i++] - 108);
721
+ }
722
+ return dict;
723
+ }
724
+ function subrBias(count) {
725
+ return count < 1240 ? 107 : count < 33900 ? 1131 : 32768;
726
+ }
727
+ function cffCharString(cff, glyph) {
728
+ const code = cff.charStrings[glyph];
729
+ if (!code)
730
+ return [];
731
+ const { subrs } = cff.privateFor(glyph);
732
+ const globalBias = subrBias(cff.globalSubrs.length);
733
+ const localBias = subrBias(subrs.length);
734
+ const path = [];
735
+ const stack = [];
736
+ let x = 0, y = 0;
737
+ let stems = 0;
738
+ let open = false;
739
+ let widthParsed = false;
740
+ let depth = 0;
741
+ const moveTo = (dx, dy) => {
742
+ if (open)
743
+ path.push(['Z']);
744
+ x += dx;
745
+ y += dy;
746
+ path.push(['M', x, y]);
747
+ open = true;
748
+ };
749
+ const lineTo = (dx, dy) => {
750
+ x += dx;
751
+ y += dy;
752
+ path.push(['L', x, y]);
753
+ };
754
+ const curveTo = (dx1, dy1, dx2, dy2, dx3, dy3) => {
755
+ const c1x = x + dx1, c1y = y + dy1;
756
+ const c2x = c1x + dx2, c2y = c1y + dy2;
757
+ x = c2x + dx3;
758
+ y = c2y + dy3;
759
+ path.push(['C', c1x, c1y, c2x, c2y, x, y]);
760
+ };
761
+ const stripWidth = (expectedEven) => {
762
+ if (!widthParsed && (stack.length % 2 === 1) === expectedEven)
763
+ stack.shift();
764
+ widthParsed = true;
765
+ };
766
+ const run = (code) => {
767
+ if (++depth > 10)
768
+ return true;
769
+ for (let i = 0; i < code.length;) {
770
+ const b0 = code[i++];
771
+ if (b0 >= 32 || b0 === 28) {
772
+ if (b0 === 28) {
773
+ stack.push((code[i] << 24 | code[i + 1] << 16) >> 16);
774
+ i += 2;
775
+ }
776
+ else if (b0 <= 246)
777
+ stack.push(b0 - 139);
778
+ else if (b0 <= 250)
779
+ stack.push((b0 - 247) * 256 + code[i++] + 108);
780
+ else if (b0 <= 254)
781
+ stack.push(-(b0 - 251) * 256 - code[i++] - 108);
782
+ else {
783
+ stack.push((code[i] << 24 | code[i + 1] << 16 | code[i + 2] << 8 | code[i + 3]) / 65536);
784
+ i += 4;
785
+ }
786
+ continue;
787
+ }
788
+ switch (b0) {
789
+ case 1:
790
+ case 3:
791
+ case 18:
792
+ case 23:
793
+ stripWidth(true);
794
+ stems += stack.length >> 1;
795
+ stack.length = 0;
796
+ break;
797
+ case 19:
798
+ case 20:
799
+ stripWidth(true);
800
+ stems += stack.length >> 1;
801
+ stack.length = 0;
802
+ i += stems + 7 >> 3;
803
+ break;
804
+ case 21:
805
+ stripWidth(true);
806
+ moveTo(stack[0], stack[1]);
807
+ stack.length = 0;
808
+ break;
809
+ case 22:
810
+ stripWidth(false);
811
+ moveTo(stack[0], 0);
812
+ stack.length = 0;
813
+ break;
814
+ case 4:
815
+ stripWidth(false);
816
+ moveTo(0, stack[0]);
817
+ stack.length = 0;
818
+ break;
819
+ case 5:
820
+ for (let j = 0; j + 1 < stack.length; j += 2)
821
+ lineTo(stack[j], stack[j + 1]);
822
+ stack.length = 0;
823
+ break;
824
+ case 6:
825
+ case 7: {
826
+ let horizontal = b0 === 6;
827
+ for (const v of stack) {
828
+ if (horizontal)
829
+ lineTo(v, 0);
830
+ else
831
+ lineTo(0, v);
832
+ horizontal = !horizontal;
833
+ }
834
+ stack.length = 0;
835
+ break;
836
+ }
837
+ case 8:
838
+ for (let j = 0; j + 5 < stack.length; j += 6)
839
+ curveTo(stack[j], stack[j + 1], stack[j + 2], stack[j + 3], stack[j + 4], stack[j + 5]);
840
+ stack.length = 0;
841
+ break;
842
+ case 24: {
843
+ let j = 0;
844
+ for (; j + 5 < stack.length - 2; j += 6)
845
+ curveTo(stack[j], stack[j + 1], stack[j + 2], stack[j + 3], stack[j + 4], stack[j + 5]);
846
+ lineTo(stack[j], stack[j + 1]);
847
+ stack.length = 0;
848
+ break;
849
+ }
850
+ case 25: {
851
+ let j = 0;
852
+ for (; j + 1 < stack.length - 6; j += 2)
853
+ lineTo(stack[j], stack[j + 1]);
854
+ curveTo(stack[j], stack[j + 1], stack[j + 2], stack[j + 3], stack[j + 4], stack[j + 5]);
855
+ stack.length = 0;
856
+ break;
857
+ }
858
+ case 26: {
859
+ let j = 0, dx1 = 0;
860
+ if (stack.length % 4)
861
+ dx1 = stack[j++];
862
+ for (; j + 3 < stack.length; j += 4) {
863
+ curveTo(dx1, stack[j], stack[j + 1], stack[j + 2], 0, stack[j + 3]);
864
+ dx1 = 0;
865
+ }
866
+ stack.length = 0;
867
+ break;
868
+ }
869
+ case 27: {
870
+ let j = 0, dy1 = 0;
871
+ if (stack.length % 4)
872
+ dy1 = stack[j++];
873
+ for (; j + 3 < stack.length; j += 4) {
874
+ curveTo(stack[j], dy1, stack[j + 1], stack[j + 2], stack[j + 3], 0);
875
+ dy1 = 0;
876
+ }
877
+ stack.length = 0;
878
+ break;
879
+ }
880
+ case 30:
881
+ case 31: {
882
+ let horizontal = b0 === 31;
883
+ for (let j = 0; j + 3 < stack.length; j += 4) {
884
+ const last = j + 5 === stack.length ? stack[j + 4] : 0;
885
+ if (horizontal)
886
+ curveTo(stack[j], 0, stack[j + 1], stack[j + 2], last, stack[j + 3]);
887
+ else
888
+ curveTo(0, stack[j], stack[j + 1], stack[j + 2], stack[j + 3], last);
889
+ horizontal = !horizontal;
890
+ }
891
+ stack.length = 0;
892
+ break;
893
+ }
894
+ case 10:
895
+ case 29: {
896
+ const local = b0 === 10;
897
+ const index = stack.pop() + (local ? localBias : globalBias);
898
+ const subr = (local ? subrs : cff.globalSubrs)[index];
899
+ if (subr && run(subr))
900
+ return true;
901
+ break;
902
+ }
903
+ case 11:
904
+ depth--;
905
+ return false;
906
+ case 14:
907
+ stripWidth(true);
908
+ if (open)
909
+ path.push(['Z']);
910
+ open = false;
911
+ return true;
912
+ case 12: {
913
+ const op = code[i++];
914
+ const s = stack;
915
+ if (op === 35) {
916
+ curveTo(s[0], s[1], s[2], s[3], s[4], s[5]);
917
+ curveTo(s[6], s[7], s[8], s[9], s[10], s[11]);
918
+ }
919
+ else if (op === 34) {
920
+ const startY = y;
921
+ curveTo(s[0], 0, s[1], s[2], s[3], 0);
922
+ curveTo(s[4], 0, s[5], startY - y, s[6], 0);
923
+ }
924
+ else if (op === 36) {
925
+ const startY = y;
926
+ curveTo(s[0], s[1], s[2], s[3], s[4], 0);
927
+ curveTo(s[5], 0, s[6], s[7], s[8], startY - (y + s[7]));
928
+ }
929
+ else if (op === 37) {
930
+ const startX = x, startY = y;
931
+ let dx = 0, dy = 0;
932
+ for (let j = 0; j < 10; j += 2) {
933
+ dx += s[j];
934
+ dy += s[j + 1];
935
+ }
936
+ curveTo(s[0], s[1], s[2], s[3], s[4], s[5]);
937
+ const lastX = Math.abs(dx) > Math.abs(dy);
938
+ curveTo(s[6], s[7], s[8], s[9], lastX ? s[10] : startX - (x + s[6] + s[8]), lastX ? startY - (y + s[7] + s[9]) : s[10]);
939
+ }
940
+ stack.length = 0;
941
+ break;
942
+ }
943
+ default:
944
+ stack.length = 0;
945
+ }
946
+ }
947
+ depth--;
948
+ return false;
949
+ };
950
+ run(code);
951
+ if (open)
952
+ path.push(['Z']);
953
+ return path;
954
+ }