woff2-decode 0.1.3 → 0.2.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.
- package/README.md +7 -7
- package/dist/brotli.d.ts +2 -1
- package/dist/brotli.d.ts.map +1 -1
- package/dist/buffer.d.ts +1 -0
- package/dist/buffer.d.ts.map +1 -1
- package/dist/decode.d.ts +1 -1
- package/dist/decode.d.ts.map +1 -1
- package/dist/index.browser.js +4248 -0
- package/dist/index.browser.js.map +1 -0
- package/dist/index.cjs +321 -287
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +321 -287
- package/dist/index.js.map +1 -1
- package/dist/index.umd.js +4253 -0
- package/dist/index.umd.js.map +1 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -9,6 +9,7 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
|
|
|
9
9
|
//#endregion
|
|
10
10
|
//#region src/brotli.ts
|
|
11
11
|
let nativeBrotli = null;
|
|
12
|
+
let browserBrotli = null;
|
|
12
13
|
function tryLoadNative() {
|
|
13
14
|
try {
|
|
14
15
|
if (typeof process !== "undefined" && process.versions?.node) {
|
|
@@ -21,9 +22,24 @@ function tryLoadNative() {
|
|
|
21
22
|
} catch {}
|
|
22
23
|
return null;
|
|
23
24
|
}
|
|
25
|
+
function tryLoadBrowserBrotli() {
|
|
26
|
+
try {
|
|
27
|
+
if (typeof DecompressionStream !== "undefined") {
|
|
28
|
+
new DecompressionStream("brotli");
|
|
29
|
+
return async (buf) => {
|
|
30
|
+
const arrayBuf = buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
|
|
31
|
+
const decompressed = new Blob([arrayBuf]).stream().pipeThrough(new DecompressionStream("brotli"));
|
|
32
|
+
return new Uint8Array(await new Response(decompressed).arrayBuffer());
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
} catch {}
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
24
38
|
nativeBrotli = tryLoadNative();
|
|
25
|
-
|
|
39
|
+
browserBrotli = tryLoadBrowserBrotli();
|
|
40
|
+
async function decompress(data) {
|
|
26
41
|
if (nativeBrotli) return nativeBrotli(data);
|
|
42
|
+
if (browserBrotli) return browserBrotli(data);
|
|
27
43
|
return brotliDecompress(data);
|
|
28
44
|
}
|
|
29
45
|
function decompressPure(data) {
|
|
@@ -38,9 +54,11 @@ var Buffer$1 = class {
|
|
|
38
54
|
this.pos = 0;
|
|
39
55
|
if (data instanceof Uint8Array) {
|
|
40
56
|
const len = length ?? data.byteLength - offset;
|
|
57
|
+
this.u8 = data.subarray(offset, offset + len);
|
|
41
58
|
this.view = new DataView(data.buffer, data.byteOffset + offset, len);
|
|
42
59
|
} else {
|
|
43
60
|
const len = length ?? data.byteLength - offset;
|
|
61
|
+
this.u8 = new Uint8Array(data, offset, len);
|
|
44
62
|
this.view = new DataView(data, offset, len);
|
|
45
63
|
}
|
|
46
64
|
}
|
|
@@ -48,66 +66,67 @@ var Buffer$1 = class {
|
|
|
48
66
|
return this.pos;
|
|
49
67
|
}
|
|
50
68
|
get length() {
|
|
51
|
-
return this.
|
|
69
|
+
return this.u8.byteLength;
|
|
52
70
|
}
|
|
53
71
|
get remaining() {
|
|
54
|
-
return this.
|
|
72
|
+
return this.u8.byteLength - this.pos;
|
|
55
73
|
}
|
|
56
74
|
get buffer() {
|
|
57
75
|
return this.view;
|
|
58
76
|
}
|
|
59
77
|
skip(n) {
|
|
60
|
-
if (this.pos + n > this.
|
|
78
|
+
if (this.pos + n > this.u8.byteLength || this.pos + n < this.pos) return false;
|
|
61
79
|
this.pos += n;
|
|
62
80
|
return true;
|
|
63
81
|
}
|
|
64
82
|
seek(offset) {
|
|
65
|
-
if (offset > this.
|
|
83
|
+
if (offset > this.u8.byteLength || offset < 0) return false;
|
|
66
84
|
this.pos = offset;
|
|
67
85
|
return true;
|
|
68
86
|
}
|
|
69
87
|
readU8() {
|
|
70
|
-
if (this.pos + 1 > this.
|
|
71
|
-
return this.
|
|
88
|
+
if (this.pos + 1 > this.u8.byteLength) return null;
|
|
89
|
+
return this.u8[this.pos++];
|
|
72
90
|
}
|
|
73
91
|
readU16() {
|
|
74
|
-
if (this.pos + 2 > this.
|
|
75
|
-
const
|
|
76
|
-
this.pos
|
|
77
|
-
return
|
|
92
|
+
if (this.pos + 2 > this.u8.byteLength) return null;
|
|
93
|
+
const idx = this.pos;
|
|
94
|
+
this.pos = idx + 2;
|
|
95
|
+
return this.u8[idx] << 8 | this.u8[idx + 1];
|
|
78
96
|
}
|
|
79
97
|
readS16() {
|
|
80
|
-
if (this.pos + 2 > this.
|
|
81
|
-
const
|
|
82
|
-
this.pos
|
|
83
|
-
|
|
98
|
+
if (this.pos + 2 > this.u8.byteLength) return null;
|
|
99
|
+
const idx = this.pos;
|
|
100
|
+
this.pos = idx + 2;
|
|
101
|
+
const val = this.u8[idx] << 8 | this.u8[idx + 1];
|
|
102
|
+
return (val & 32768) !== 0 ? val - 65536 : val;
|
|
84
103
|
}
|
|
85
104
|
readU32() {
|
|
86
|
-
if (this.pos + 4 > this.
|
|
87
|
-
const
|
|
88
|
-
this.pos
|
|
89
|
-
return
|
|
105
|
+
if (this.pos + 4 > this.u8.byteLength) return null;
|
|
106
|
+
const idx = this.pos;
|
|
107
|
+
this.pos = idx + 4;
|
|
108
|
+
return this.u8[idx] * 16777216 + (this.u8[idx + 1] << 16 | this.u8[idx + 2] << 8 | this.u8[idx + 3]) >>> 0;
|
|
90
109
|
}
|
|
91
110
|
readS32() {
|
|
92
|
-
if (this.pos + 4 > this.
|
|
93
|
-
const
|
|
94
|
-
this.pos
|
|
95
|
-
return
|
|
111
|
+
if (this.pos + 4 > this.u8.byteLength) return null;
|
|
112
|
+
const idx = this.pos;
|
|
113
|
+
this.pos = idx + 4;
|
|
114
|
+
return this.u8[idx] << 24 | this.u8[idx + 1] << 16 | this.u8[idx + 2] << 8 | this.u8[idx + 3];
|
|
96
115
|
}
|
|
97
116
|
readBytes(n) {
|
|
98
|
-
if (this.pos + n > this.
|
|
99
|
-
const result =
|
|
117
|
+
if (this.pos + n > this.u8.byteLength || n < 0) return null;
|
|
118
|
+
const result = this.u8.subarray(this.pos, this.pos + n);
|
|
100
119
|
this.pos += n;
|
|
101
120
|
return result;
|
|
102
121
|
}
|
|
103
122
|
peekU8(offset = 0) {
|
|
104
123
|
const idx = this.pos + offset;
|
|
105
|
-
if (idx >= this.
|
|
106
|
-
return this.
|
|
124
|
+
if (idx >= this.u8.byteLength || idx < 0) return null;
|
|
125
|
+
return this.u8[idx];
|
|
107
126
|
}
|
|
108
127
|
subarray(offset, length) {
|
|
109
|
-
if (offset + length > this.
|
|
110
|
-
return
|
|
128
|
+
if (offset + length > this.u8.byteLength || offset < 0 || length < 0) return null;
|
|
129
|
+
return this.u8.subarray(offset, offset + length);
|
|
111
130
|
}
|
|
112
131
|
};
|
|
113
132
|
|
|
@@ -223,12 +242,19 @@ function readBase128(buf) {
|
|
|
223
242
|
//#region src/decode.ts
|
|
224
243
|
const SFNT_HEADER_SIZE = 12;
|
|
225
244
|
const SFNT_ENTRY_SIZE = 16;
|
|
226
|
-
|
|
245
|
+
const FLAG_ON_CURVE = 1;
|
|
246
|
+
const FLAG_X_SHORT = 2;
|
|
247
|
+
const FLAG_Y_SHORT = 4;
|
|
248
|
+
const FLAG_REPEAT = 8;
|
|
249
|
+
const FLAG_X_SAME = 16;
|
|
250
|
+
const FLAG_Y_SAME = 32;
|
|
251
|
+
const FLAG_OVERLAP_SIMPLE = 64;
|
|
252
|
+
async function decode(data, options) {
|
|
227
253
|
const input = data instanceof Uint8Array ? data : new Uint8Array(data);
|
|
228
254
|
const header = readHeader(new Buffer$1(input), input.byteLength);
|
|
229
255
|
if (!header) throw new Error("Failed to read WOFF2 header");
|
|
230
256
|
const compressedData = input.subarray(header.compressedOffset, header.compressedOffset + header.compressedLength);
|
|
231
|
-
const decompressed =
|
|
257
|
+
const decompressed = options?.forcePureBrotli ? decompressPure(compressedData) : await decompress(compressedData);
|
|
232
258
|
if (!decompressed || decompressed.byteLength !== header.uncompressedSize) throw new Error(`Brotli decompression failed: expected ${header.uncompressedSize} bytes, got ${decompressed?.byteLength ?? 0}`);
|
|
233
259
|
let outputSize = computeOffsetToFirstTable(header);
|
|
234
260
|
for (const table of header.tables) {
|
|
@@ -344,7 +370,8 @@ function readTableDirectory(buf, numTables) {
|
|
|
344
370
|
srcOffset,
|
|
345
371
|
srcLength: transformLength,
|
|
346
372
|
dstOffset: 0,
|
|
347
|
-
dstLength: origLength
|
|
373
|
+
dstLength: origLength,
|
|
374
|
+
key: `${tag}:${srcOffset}`
|
|
348
375
|
});
|
|
349
376
|
srcOffset += transformLength;
|
|
350
377
|
}
|
|
@@ -447,8 +474,8 @@ function reconstructFont(decompressed, header, fontIndex, fontInfo, output, outV
|
|
|
447
474
|
for (const table of sortedTables) {
|
|
448
475
|
const entryOffset = fontInfo.tableEntryByTag.get(table.tag);
|
|
449
476
|
if (entryOffset === void 0) continue;
|
|
450
|
-
const
|
|
451
|
-
const existing = writtenTables.get(
|
|
477
|
+
const tKey = table.key;
|
|
478
|
+
const existing = writtenTables.get(tKey);
|
|
452
479
|
if (existing) {
|
|
453
480
|
updateTableEntry(outView, entryOffset, existing.checksum, existing.dstOffset, existing.dstLength);
|
|
454
481
|
if (isTTC) {
|
|
@@ -473,7 +500,7 @@ function reconstructFont(decompressed, header, fontIndex, fontInfo, output, outV
|
|
|
473
500
|
fontChecksum = fontChecksum + checksum >>> 0;
|
|
474
501
|
fontChecksum = fontChecksum + computeTableEntryChecksum(checksum, dstOffset, tableData.byteLength) >>> 0;
|
|
475
502
|
}
|
|
476
|
-
writtenTables.set(
|
|
503
|
+
writtenTables.set(tKey, {
|
|
477
504
|
dstOffset,
|
|
478
505
|
dstLength: tableData.byteLength,
|
|
479
506
|
checksum
|
|
@@ -488,8 +515,7 @@ function reconstructFont(decompressed, header, fontIndex, fontInfo, output, outV
|
|
|
488
515
|
fontChecksum = fontChecksum + locaChecksum >>> 0;
|
|
489
516
|
fontChecksum = fontChecksum + computeTableEntryChecksum(locaChecksum, dstOffset, result.locaData.byteLength) >>> 0;
|
|
490
517
|
}
|
|
491
|
-
|
|
492
|
-
writtenTables.set(locaKey, {
|
|
518
|
+
writtenTables.set(locaTable.key, {
|
|
493
519
|
dstOffset,
|
|
494
520
|
dstLength: result.locaData.byteLength,
|
|
495
521
|
checksum: locaChecksum
|
|
@@ -515,7 +541,7 @@ function reconstructFont(decompressed, header, fontIndex, fontInfo, output, outV
|
|
|
515
541
|
fontChecksum = fontChecksum + checksum >>> 0;
|
|
516
542
|
fontChecksum = fontChecksum + computeTableEntryChecksum(checksum, dstOffset, tableData.byteLength) >>> 0;
|
|
517
543
|
}
|
|
518
|
-
writtenTables.set(
|
|
544
|
+
writtenTables.set(tKey, {
|
|
519
545
|
dstOffset,
|
|
520
546
|
dstLength: tableData.byteLength,
|
|
521
547
|
checksum
|
|
@@ -524,7 +550,7 @@ function reconstructFont(decompressed, header, fontIndex, fontInfo, output, outV
|
|
|
524
550
|
}
|
|
525
551
|
const headTable = sortedTables.find((t) => t.tag === TAG_HEAD);
|
|
526
552
|
if (headTable) {
|
|
527
|
-
const headEntry = writtenTables.get(
|
|
553
|
+
const headEntry = writtenTables.get(headTable.key);
|
|
528
554
|
if (headEntry && headEntry.dstLength >= 12) {
|
|
529
555
|
const finalChecksum = isTTC ? fontChecksum : computeChecksum(output, 0, dstOffset);
|
|
530
556
|
outView.setUint32(headEntry.dstOffset + 8, 2981146554 - finalChecksum >>> 0);
|
|
@@ -535,54 +561,100 @@ function reconstructFont(decompressed, header, fontIndex, fontInfo, output, outV
|
|
|
535
561
|
function computeTableEntryChecksum(checksum, offset, length) {
|
|
536
562
|
return checksum + offset + length >>> 0;
|
|
537
563
|
}
|
|
564
|
+
function makeByteStream(data, start, length) {
|
|
565
|
+
return {
|
|
566
|
+
data,
|
|
567
|
+
pos: start,
|
|
568
|
+
end: start + length
|
|
569
|
+
};
|
|
570
|
+
}
|
|
571
|
+
function bsReadU8(stream) {
|
|
572
|
+
if (stream.pos >= stream.end) throw new Error("Stream overflow");
|
|
573
|
+
return stream.data[stream.pos++];
|
|
574
|
+
}
|
|
575
|
+
function bsReadU16(stream) {
|
|
576
|
+
if (stream.pos + 2 > stream.end) throw new Error("Stream overflow");
|
|
577
|
+
const idx = stream.pos;
|
|
578
|
+
stream.pos = idx + 2;
|
|
579
|
+
return stream.data[idx] << 8 | stream.data[idx + 1];
|
|
580
|
+
}
|
|
581
|
+
function bsReadS16(stream) {
|
|
582
|
+
if (stream.pos + 2 > stream.end) throw new Error("Stream overflow");
|
|
583
|
+
const idx = stream.pos;
|
|
584
|
+
stream.pos = idx + 2;
|
|
585
|
+
const val = stream.data[idx] << 8 | stream.data[idx + 1];
|
|
586
|
+
return (val & 32768) !== 0 ? val - 65536 : val;
|
|
587
|
+
}
|
|
588
|
+
function fsReadU32(stream) {
|
|
589
|
+
if (stream.pos + 4 > stream.end) throw new Error("Stream overflow");
|
|
590
|
+
const idx = stream.pos;
|
|
591
|
+
stream.pos = idx + 4;
|
|
592
|
+
return stream.data[idx] * 16777216 + (stream.data[idx + 1] << 16 | stream.data[idx + 2] << 8 | stream.data[idx + 3]) >>> 0;
|
|
593
|
+
}
|
|
594
|
+
function bsSkip(stream, n) {
|
|
595
|
+
if (stream.pos + n > stream.end || n < 0) throw new Error("Stream overflow");
|
|
596
|
+
stream.pos += n;
|
|
597
|
+
}
|
|
598
|
+
function fsReadBytes(stream, n) {
|
|
599
|
+
if (stream.pos + n > stream.end || n < 0) throw new Error("Stream overflow");
|
|
600
|
+
const start = stream.pos;
|
|
601
|
+
stream.pos += n;
|
|
602
|
+
return stream.data.subarray(start, start + n);
|
|
603
|
+
}
|
|
604
|
+
function bsRead255UShort(stream) {
|
|
605
|
+
const code = bsReadU8(stream);
|
|
606
|
+
if (code === 253) return bsReadU16(stream);
|
|
607
|
+
else if (code === 255) return 253 + bsReadU8(stream);
|
|
608
|
+
else if (code === 254) return 506 + bsReadU8(stream);
|
|
609
|
+
return code;
|
|
610
|
+
}
|
|
538
611
|
function reconstructGlyf(data, glyfTable, locaTable, fontInfo) {
|
|
539
|
-
const
|
|
540
|
-
|
|
541
|
-
const optionFlags =
|
|
542
|
-
const numGlyphs =
|
|
543
|
-
const indexFormat =
|
|
544
|
-
if (version === null || optionFlags === null || numGlyphs === null || indexFormat === null) throw new Error("Invalid glyf transform header");
|
|
612
|
+
const headerStream = makeByteStream(data, glyfTable.srcOffset, glyfTable.transformLength);
|
|
613
|
+
bsReadU16(headerStream);
|
|
614
|
+
const optionFlags = bsReadU16(headerStream);
|
|
615
|
+
const numGlyphs = bsReadU16(headerStream);
|
|
616
|
+
const indexFormat = bsReadU16(headerStream);
|
|
545
617
|
fontInfo.numGlyphs = numGlyphs;
|
|
546
618
|
fontInfo.indexFormat = indexFormat;
|
|
547
|
-
const nContourStreamSize =
|
|
548
|
-
const nPointsStreamSize =
|
|
549
|
-
const flagStreamSize =
|
|
550
|
-
const glyphStreamSize =
|
|
551
|
-
const compositeStreamSize =
|
|
552
|
-
const bboxStreamSize =
|
|
553
|
-
const instructionStreamSize =
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
const nContourStream = new Buffer$1(data, glyfTable.srcOffset + offset, nContourStreamSize);
|
|
619
|
+
const nContourStreamSize = fsReadU32(headerStream);
|
|
620
|
+
const nPointsStreamSize = fsReadU32(headerStream);
|
|
621
|
+
const flagStreamSize = fsReadU32(headerStream);
|
|
622
|
+
const glyphStreamSize = fsReadU32(headerStream);
|
|
623
|
+
const compositeStreamSize = fsReadU32(headerStream);
|
|
624
|
+
const bboxStreamSize = fsReadU32(headerStream);
|
|
625
|
+
const instructionStreamSize = fsReadU32(headerStream);
|
|
626
|
+
let offset = headerStream.pos;
|
|
627
|
+
const nContourStream = makeByteStream(data, offset, nContourStreamSize);
|
|
557
628
|
offset += nContourStreamSize;
|
|
558
|
-
const nPointsStream =
|
|
629
|
+
const nPointsStream = makeByteStream(data, offset, nPointsStreamSize);
|
|
559
630
|
offset += nPointsStreamSize;
|
|
560
|
-
const flagStream =
|
|
631
|
+
const flagStream = makeByteStream(data, offset, flagStreamSize);
|
|
561
632
|
offset += flagStreamSize;
|
|
562
|
-
const glyphStream =
|
|
633
|
+
const glyphStream = makeByteStream(data, offset, glyphStreamSize);
|
|
563
634
|
offset += glyphStreamSize;
|
|
564
|
-
const compositeStream =
|
|
635
|
+
const compositeStream = makeByteStream(data, offset, compositeStreamSize);
|
|
565
636
|
offset += compositeStreamSize;
|
|
566
|
-
const bboxStream =
|
|
637
|
+
const bboxStream = makeByteStream(data, offset, bboxStreamSize);
|
|
567
638
|
offset += bboxStreamSize;
|
|
568
|
-
const instructionStream =
|
|
639
|
+
const instructionStream = makeByteStream(data, offset, instructionStreamSize);
|
|
569
640
|
const hasOverlapBitmap = (optionFlags & 1) !== 0;
|
|
570
641
|
let overlapBitmap = null;
|
|
571
642
|
if (hasOverlapBitmap) {
|
|
572
643
|
const overlapBitmapLength = numGlyphs + 7 >> 3;
|
|
573
|
-
overlapBitmap = data.subarray(
|
|
644
|
+
overlapBitmap = data.subarray(offset + instructionStreamSize, offset + instructionStreamSize + overlapBitmapLength);
|
|
574
645
|
}
|
|
575
|
-
const
|
|
576
|
-
const bboxBitmap = bboxStream.readBytes(bboxBitmapLength);
|
|
577
|
-
if (!bboxBitmap) throw new Error("Failed to read bbox bitmap");
|
|
646
|
+
const bboxBitmap = fsReadBytes(bboxStream, numGlyphs + 31 >> 5 << 2);
|
|
578
647
|
let glyfOutput = new Uint8Array(glyfTable.origLength * 2);
|
|
579
648
|
let glyfOffset = 0;
|
|
580
|
-
const locaValues =
|
|
649
|
+
const locaValues = new Uint32Array(numGlyphs + 1);
|
|
581
650
|
fontInfo.xMins = new Int16Array(numGlyphs);
|
|
651
|
+
let contourEndsScratch = new Uint16Array(128);
|
|
652
|
+
let flagsScratch = new Uint8Array(512);
|
|
653
|
+
let xScratch = new Uint8Array(512);
|
|
654
|
+
let yScratch = new Uint8Array(512);
|
|
582
655
|
for (let glyphId = 0; glyphId < numGlyphs; glyphId++) {
|
|
583
|
-
locaValues
|
|
584
|
-
const nContours = nContourStream
|
|
585
|
-
if (nContours === null) throw new Error(`Failed to read nContours for glyph ${glyphId}`);
|
|
656
|
+
locaValues[glyphId] = glyfOffset;
|
|
657
|
+
const nContours = bsReadS16(nContourStream);
|
|
586
658
|
const haveBbox = (bboxBitmap[glyphId >> 3] & 128 >> (glyphId & 7)) !== 0;
|
|
587
659
|
if (nContours === 0) {
|
|
588
660
|
if (haveBbox) throw new Error(`Empty glyph ${glyphId} has bbox`);
|
|
@@ -592,34 +664,76 @@ function reconstructGlyf(data, glyfTable, locaTable, fontInfo) {
|
|
|
592
664
|
if (!haveBbox) throw new Error(`Composite glyph ${glyphId} missing bbox`);
|
|
593
665
|
const { compositeData, haveInstructions } = readCompositeGlyph(compositeStream);
|
|
594
666
|
let instructionSize = 0;
|
|
595
|
-
if (haveInstructions) instructionSize =
|
|
667
|
+
if (haveInstructions) instructionSize = bsRead255UShort(glyphStream);
|
|
596
668
|
const glyphSize = 10 + compositeData.byteLength + (haveInstructions ? 2 + instructionSize : 0);
|
|
597
669
|
ensureCapacity(glyphSize);
|
|
598
|
-
|
|
599
|
-
const bbox = bboxStream
|
|
600
|
-
if (!bbox) throw new Error("Failed to read bbox");
|
|
670
|
+
writeInt16BE(glyfOutput, glyfOffset, -1);
|
|
671
|
+
const bbox = fsReadBytes(bboxStream, 8);
|
|
601
672
|
glyfOutput.set(bbox, glyfOffset + 2);
|
|
602
|
-
fontInfo.xMins[glyphId] =
|
|
673
|
+
fontInfo.xMins[glyphId] = readInt16BE(bbox, 0);
|
|
603
674
|
glyfOutput.set(compositeData, glyfOffset + 10);
|
|
604
675
|
if (haveInstructions) {
|
|
605
676
|
const instrOffset = glyfOffset + 10 + compositeData.byteLength;
|
|
606
|
-
|
|
607
|
-
const instructions = instructionStream
|
|
608
|
-
if (!instructions) throw new Error("Failed to read instructions");
|
|
677
|
+
writeUint16BE(glyfOutput, instrOffset, instructionSize);
|
|
678
|
+
const instructions = fsReadBytes(instructionStream, instructionSize);
|
|
609
679
|
glyfOutput.set(instructions, instrOffset + 2);
|
|
610
680
|
}
|
|
611
681
|
glyfOffset += glyphSize;
|
|
612
682
|
glyfOffset = pad4(glyfOffset);
|
|
613
683
|
} else {
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
684
|
+
if (nContours > contourEndsScratch.length) contourEndsScratch = new Uint16Array(nContours * 2);
|
|
685
|
+
let totalPoints = 0;
|
|
686
|
+
let endPoint = -1;
|
|
687
|
+
for (let i = 0; i < nContours; i++) {
|
|
688
|
+
const n = bsRead255UShort(nPointsStream);
|
|
689
|
+
totalPoints += n;
|
|
690
|
+
endPoint += n;
|
|
691
|
+
contourEndsScratch[i] = endPoint;
|
|
692
|
+
}
|
|
693
|
+
const scratchSize = totalPoints * 2;
|
|
694
|
+
if (scratchSize > flagsScratch.length) flagsScratch = new Uint8Array(scratchSize);
|
|
695
|
+
if (scratchSize > xScratch.length) xScratch = new Uint8Array(scratchSize);
|
|
696
|
+
if (scratchSize > yScratch.length) yScratch = new Uint8Array(scratchSize);
|
|
697
|
+
const encoded = encodeTripletsToScratch(flagStream, glyphStream, totalPoints, ((overlapBitmap?.[glyphId >> 3] ?? 0) & 128 >> (glyphId & 7)) !== 0, flagsScratch, xScratch, yScratch);
|
|
698
|
+
const instructionSize = bsRead255UShort(glyphStream);
|
|
699
|
+
const glyphSize = 10 + 2 * nContours + 2 + instructionSize + encoded.flagsLen + encoded.xLen + encoded.yLen;
|
|
700
|
+
ensureCapacity(glyphSize);
|
|
701
|
+
writeInt16BE(glyfOutput, glyfOffset, nContours);
|
|
702
|
+
let xMin = 0;
|
|
703
|
+
if (haveBbox) {
|
|
704
|
+
const bbox = fsReadBytes(bboxStream, 8);
|
|
705
|
+
glyfOutput.set(bbox, glyfOffset + 2);
|
|
706
|
+
xMin = readInt16BE(bbox, 0);
|
|
707
|
+
} else {
|
|
708
|
+
writeInt16BE(glyfOutput, glyfOffset + 2, encoded.xMin);
|
|
709
|
+
writeInt16BE(glyfOutput, glyfOffset + 4, encoded.yMin);
|
|
710
|
+
writeInt16BE(glyfOutput, glyfOffset + 6, encoded.xMax);
|
|
711
|
+
writeInt16BE(glyfOutput, glyfOffset + 8, encoded.yMax);
|
|
712
|
+
xMin = encoded.xMin;
|
|
713
|
+
}
|
|
714
|
+
let writeOffset = glyfOffset + 10;
|
|
715
|
+
for (let i = 0; i < nContours; i++) {
|
|
716
|
+
writeUint16BE(glyfOutput, writeOffset, contourEndsScratch[i]);
|
|
717
|
+
writeOffset += 2;
|
|
718
|
+
}
|
|
719
|
+
writeUint16BE(glyfOutput, writeOffset, instructionSize);
|
|
720
|
+
writeOffset += 2;
|
|
721
|
+
if (instructionSize > 0) {
|
|
722
|
+
const instructions = fsReadBytes(instructionStream, instructionSize);
|
|
723
|
+
glyfOutput.set(instructions, writeOffset);
|
|
724
|
+
writeOffset += instructionSize;
|
|
725
|
+
}
|
|
726
|
+
glyfOutput.set(flagsScratch.subarray(0, encoded.flagsLen), writeOffset);
|
|
727
|
+
writeOffset += encoded.flagsLen;
|
|
728
|
+
glyfOutput.set(xScratch.subarray(0, encoded.xLen), writeOffset);
|
|
729
|
+
writeOffset += encoded.xLen;
|
|
730
|
+
glyfOutput.set(yScratch.subarray(0, encoded.yLen), writeOffset);
|
|
731
|
+
fontInfo.xMins[glyphId] = xMin;
|
|
732
|
+
glyfOffset += glyphSize;
|
|
619
733
|
glyfOffset = pad4(glyfOffset);
|
|
620
734
|
}
|
|
621
735
|
}
|
|
622
|
-
locaValues
|
|
736
|
+
locaValues[numGlyphs] = glyfOffset;
|
|
623
737
|
const locaSize = indexFormat ? (numGlyphs + 1) * 4 : (numGlyphs + 1) * 2;
|
|
624
738
|
const locaData = new Uint8Array(locaSize);
|
|
625
739
|
const locaView = new DataView(locaData.buffer);
|
|
@@ -644,11 +758,11 @@ function readCompositeGlyph(stream) {
|
|
|
644
758
|
const FLAG_WE_HAVE_AN_X_AND_Y_SCALE = 64;
|
|
645
759
|
const FLAG_WE_HAVE_A_TWO_BY_TWO = 128;
|
|
646
760
|
const FLAG_WE_HAVE_INSTRUCTIONS = 256;
|
|
647
|
-
const startOffset = stream.
|
|
761
|
+
const startOffset = stream.pos;
|
|
648
762
|
let haveInstructions = false;
|
|
649
763
|
let flags = FLAG_MORE_COMPONENTS;
|
|
650
764
|
while (flags & FLAG_MORE_COMPONENTS) {
|
|
651
|
-
flags = stream
|
|
765
|
+
flags = bsReadU16(stream);
|
|
652
766
|
haveInstructions = haveInstructions || (flags & FLAG_WE_HAVE_INSTRUCTIONS) !== 0;
|
|
653
767
|
let argSize = 2;
|
|
654
768
|
if (flags & FLAG_ARG_1_AND_2_ARE_WORDS) argSize += 4;
|
|
@@ -656,126 +770,91 @@ function readCompositeGlyph(stream) {
|
|
|
656
770
|
if (flags & FLAG_WE_HAVE_A_SCALE) argSize += 2;
|
|
657
771
|
else if (flags & FLAG_WE_HAVE_AN_X_AND_Y_SCALE) argSize += 4;
|
|
658
772
|
else if (flags & FLAG_WE_HAVE_A_TWO_BY_TWO) argSize += 8;
|
|
659
|
-
stream
|
|
773
|
+
bsSkip(stream, argSize);
|
|
660
774
|
}
|
|
661
|
-
const compositeData = stream.subarray(startOffset, stream.offset - startOffset);
|
|
662
|
-
if (!compositeData) throw new Error("Failed to read composite glyph data");
|
|
663
775
|
return {
|
|
664
|
-
compositeData,
|
|
776
|
+
compositeData: stream.data.subarray(startOffset, stream.pos),
|
|
665
777
|
haveInstructions
|
|
666
778
|
};
|
|
667
779
|
}
|
|
668
|
-
function
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
const output = new Uint8Array(glyphSize);
|
|
682
|
-
const view = new DataView(output.buffer);
|
|
683
|
-
let offset = 0;
|
|
684
|
-
view.setInt16(offset, nContours);
|
|
685
|
-
offset += 2;
|
|
686
|
-
if (haveBbox) {
|
|
687
|
-
const bbox = bboxStream.readBytes(8);
|
|
688
|
-
if (!bbox) throw new Error("Failed to read bbox");
|
|
689
|
-
output.set(bbox, offset);
|
|
690
|
-
} else {
|
|
691
|
-
let xMin = 0, yMin = 0, xMax = 0, yMax = 0;
|
|
692
|
-
if (points.length > 0) {
|
|
693
|
-
xMin = xMax = points[0].x;
|
|
694
|
-
yMin = yMax = points[0].y;
|
|
695
|
-
for (const p of points) {
|
|
696
|
-
xMin = Math.min(xMin, p.x);
|
|
697
|
-
xMax = Math.max(xMax, p.x);
|
|
698
|
-
yMin = Math.min(yMin, p.y);
|
|
699
|
-
yMax = Math.max(yMax, p.y);
|
|
700
|
-
}
|
|
701
|
-
}
|
|
702
|
-
view.setInt16(offset, xMin);
|
|
703
|
-
view.setInt16(offset + 2, yMin);
|
|
704
|
-
view.setInt16(offset + 4, xMax);
|
|
705
|
-
view.setInt16(offset + 6, yMax);
|
|
706
|
-
}
|
|
707
|
-
offset += 8;
|
|
708
|
-
let endPoint = -1;
|
|
709
|
-
for (let i = 0; i < nContours; i++) {
|
|
710
|
-
endPoint += pointsPerContour[i];
|
|
711
|
-
view.setUint16(offset, endPoint);
|
|
712
|
-
offset += 2;
|
|
713
|
-
}
|
|
714
|
-
view.setUint16(offset, instructionSize);
|
|
715
|
-
offset += 2;
|
|
716
|
-
if (instructionSize > 0) {
|
|
717
|
-
const instructions = instructionStream.readBytes(instructionSize);
|
|
718
|
-
if (!instructions) throw new Error("Failed to read instructions");
|
|
719
|
-
output.set(instructions, offset);
|
|
720
|
-
offset += instructionSize;
|
|
721
|
-
}
|
|
722
|
-
output.set(flagsAndCoords, offset);
|
|
723
|
-
return output;
|
|
724
|
-
}
|
|
725
|
-
function decodeTriplets(flagStream, glyphStream, nPoints) {
|
|
726
|
-
const points = [];
|
|
780
|
+
function encodeTripletsToScratch(flagStream, glyphStream, nPoints, hasOverlapBit, flagsOut, xOut, yOut) {
|
|
781
|
+
if (nPoints === 0) return {
|
|
782
|
+
flagsLen: 0,
|
|
783
|
+
xLen: 0,
|
|
784
|
+
yLen: 0,
|
|
785
|
+
xMin: 0,
|
|
786
|
+
yMin: 0,
|
|
787
|
+
xMax: 0,
|
|
788
|
+
yMax: 0
|
|
789
|
+
};
|
|
790
|
+
let flagsLen = 0;
|
|
791
|
+
let xLen = 0;
|
|
792
|
+
let yLen = 0;
|
|
727
793
|
let x = 0;
|
|
728
794
|
let y = 0;
|
|
795
|
+
let xMin = 0;
|
|
796
|
+
let yMin = 0;
|
|
797
|
+
let xMax = 0;
|
|
798
|
+
let yMax = 0;
|
|
799
|
+
let lastFlag = -1;
|
|
800
|
+
let repeatCount = 0;
|
|
801
|
+
const flagData = flagStream.data;
|
|
802
|
+
let flagPos = flagStream.pos;
|
|
803
|
+
const flagEnd = flagStream.end;
|
|
804
|
+
const glyphData = glyphStream.data;
|
|
805
|
+
let glyphPos = glyphStream.pos;
|
|
806
|
+
const glyphEnd = glyphStream.end;
|
|
729
807
|
for (let i = 0; i < nPoints; i++) {
|
|
730
|
-
|
|
731
|
-
|
|
808
|
+
if (flagPos >= flagEnd) throw new Error("Stream overflow");
|
|
809
|
+
const flag = flagData[flagPos++];
|
|
732
810
|
const onCurve = (flag & 128) === 0;
|
|
733
811
|
const flagLow = flag & 127;
|
|
734
|
-
let dx
|
|
812
|
+
let dx;
|
|
813
|
+
let dy;
|
|
735
814
|
if (flagLow < 10) {
|
|
736
815
|
dx = 0;
|
|
737
|
-
|
|
738
|
-
|
|
816
|
+
if (glyphPos >= glyphEnd) throw new Error("Stream overflow");
|
|
817
|
+
const b = glyphData[glyphPos++];
|
|
739
818
|
dy = ((flagLow & 14) << 7) + b;
|
|
740
819
|
if ((flagLow & 1) === 0) dy = -dy;
|
|
741
820
|
} else if (flagLow < 20) {
|
|
742
|
-
|
|
743
|
-
|
|
821
|
+
if (glyphPos >= glyphEnd) throw new Error("Stream overflow");
|
|
822
|
+
const b = glyphData[glyphPos++];
|
|
744
823
|
dx = ((flagLow - 10 & 14) << 7) + b;
|
|
745
824
|
if ((flagLow & 1) === 0) dx = -dx;
|
|
746
825
|
dy = 0;
|
|
747
826
|
} else if (flagLow < 84) {
|
|
748
|
-
|
|
749
|
-
|
|
827
|
+
if (glyphPos >= glyphEnd) throw new Error("Stream overflow");
|
|
828
|
+
const b = glyphData[glyphPos++];
|
|
750
829
|
const b0 = flagLow - 20;
|
|
751
830
|
dx = 1 + (b0 & 48) + (b >> 4);
|
|
752
831
|
dy = 1 + ((b0 & 12) << 2) + (b & 15);
|
|
753
832
|
if ((flagLow & 1) === 0) dx = -dx;
|
|
754
833
|
if ((flagLow & 2) === 0) dy = -dy;
|
|
755
834
|
} else if (flagLow < 120) {
|
|
756
|
-
|
|
757
|
-
const
|
|
758
|
-
|
|
835
|
+
if (glyphPos + 1 >= glyphEnd) throw new Error("Stream overflow");
|
|
836
|
+
const b0 = glyphData[glyphPos++];
|
|
837
|
+
const b1 = glyphData[glyphPos++];
|
|
759
838
|
const idx = flagLow - 84;
|
|
760
|
-
dx = 1 + (
|
|
839
|
+
dx = 1 + ((idx / 12 | 0) << 8) + b0;
|
|
761
840
|
dy = 1 + (idx % 12 >> 2 << 8) + b1;
|
|
762
841
|
if ((flagLow & 1) === 0) dx = -dx;
|
|
763
842
|
if ((flagLow & 2) === 0) dy = -dy;
|
|
764
843
|
} else if (flagLow < 124) {
|
|
765
|
-
|
|
766
|
-
const
|
|
767
|
-
const
|
|
768
|
-
|
|
844
|
+
if (glyphPos + 2 >= glyphEnd) throw new Error("Stream overflow");
|
|
845
|
+
const b0 = glyphData[glyphPos++];
|
|
846
|
+
const b1 = glyphData[glyphPos++];
|
|
847
|
+
const b2 = glyphData[glyphPos++];
|
|
769
848
|
dx = (b0 << 4) + (b1 >> 4);
|
|
770
849
|
dy = ((b1 & 15) << 8) + b2;
|
|
771
850
|
if ((flagLow & 1) === 0) dx = -dx;
|
|
772
851
|
if ((flagLow & 2) === 0) dy = -dy;
|
|
773
852
|
} else {
|
|
774
|
-
|
|
775
|
-
const
|
|
776
|
-
const
|
|
777
|
-
const
|
|
778
|
-
|
|
853
|
+
if (glyphPos + 3 >= glyphEnd) throw new Error("Stream overflow");
|
|
854
|
+
const b0 = glyphData[glyphPos++];
|
|
855
|
+
const b1 = glyphData[glyphPos++];
|
|
856
|
+
const b2 = glyphData[glyphPos++];
|
|
857
|
+
const b3 = glyphData[glyphPos++];
|
|
779
858
|
dx = (b0 << 8) + b1;
|
|
780
859
|
dy = (b2 << 8) + b3;
|
|
781
860
|
if ((flagLow & 1) === 0) dx = -dx;
|
|
@@ -783,138 +862,81 @@ function decodeTriplets(flagStream, glyphStream, nPoints) {
|
|
|
783
862
|
}
|
|
784
863
|
x += dx;
|
|
785
864
|
y += dy;
|
|
786
|
-
|
|
787
|
-
x
|
|
788
|
-
y
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
const FLAG_REPEAT = 8;
|
|
799
|
-
const FLAG_X_SAME = 16;
|
|
800
|
-
const FLAG_Y_SAME = 32;
|
|
801
|
-
const FLAG_OVERLAP_SIMPLE = 64;
|
|
802
|
-
if (points.length === 0) return new Uint8Array(0);
|
|
803
|
-
const flags = [];
|
|
804
|
-
const xCoords = [];
|
|
805
|
-
const yCoords = [];
|
|
806
|
-
let lastX = 0;
|
|
807
|
-
let lastY = 0;
|
|
808
|
-
for (let i = 0; i < points.length; i++) {
|
|
809
|
-
const p = points[i];
|
|
810
|
-
let flag = p.onCurve ? FLAG_ON_CURVE : 0;
|
|
811
|
-
if (hasOverlapBit && i === 0) flag |= FLAG_OVERLAP_SIMPLE;
|
|
812
|
-
const dx = p.x - lastX;
|
|
813
|
-
const dy = p.y - lastY;
|
|
814
|
-
if (dx === 0) flag |= FLAG_X_SAME;
|
|
865
|
+
if (i === 0) {
|
|
866
|
+
xMin = xMax = x;
|
|
867
|
+
yMin = yMax = y;
|
|
868
|
+
} else {
|
|
869
|
+
if (x < xMin) xMin = x;
|
|
870
|
+
if (x > xMax) xMax = x;
|
|
871
|
+
if (y < yMin) yMin = y;
|
|
872
|
+
if (y > yMax) yMax = y;
|
|
873
|
+
}
|
|
874
|
+
let outFlag = onCurve ? FLAG_ON_CURVE : 0;
|
|
875
|
+
if (hasOverlapBit && i === 0) outFlag |= FLAG_OVERLAP_SIMPLE;
|
|
876
|
+
if (dx === 0) outFlag |= FLAG_X_SAME;
|
|
815
877
|
else if (dx >= -255 && dx <= 255) {
|
|
816
|
-
|
|
817
|
-
if (dx > 0)
|
|
818
|
-
|
|
819
|
-
} else xCoords.push(dx);
|
|
820
|
-
if (dy === 0) flag |= FLAG_Y_SAME;
|
|
821
|
-
else if (dy >= -255 && dy <= 255) {
|
|
822
|
-
flag |= FLAG_Y_SHORT;
|
|
823
|
-
if (dy > 0) flag |= FLAG_Y_SAME;
|
|
824
|
-
yCoords.push(Math.abs(dy));
|
|
825
|
-
} else yCoords.push(dy);
|
|
826
|
-
flags.push(flag);
|
|
827
|
-
lastX = p.x;
|
|
828
|
-
lastY = p.y;
|
|
829
|
-
}
|
|
830
|
-
const encodedFlags = [];
|
|
831
|
-
let lastFlag = -1;
|
|
832
|
-
let repeatCount = 0;
|
|
833
|
-
for (let i = 0; i < flags.length; i++) {
|
|
834
|
-
const flag = flags[i];
|
|
835
|
-
if (flag === lastFlag && repeatCount < 255) {
|
|
836
|
-
encodedFlags[encodedFlags.length - 1] |= FLAG_REPEAT;
|
|
837
|
-
repeatCount++;
|
|
878
|
+
outFlag |= FLAG_X_SHORT;
|
|
879
|
+
if (dx > 0) outFlag |= FLAG_X_SAME;
|
|
880
|
+
xOut[xLen++] = dx > 0 ? dx : -dx;
|
|
838
881
|
} else {
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
repeatCount = 0;
|
|
882
|
+
xOut[xLen++] = dx >> 8 & 255;
|
|
883
|
+
xOut[xLen++] = dx & 255;
|
|
842
884
|
}
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
if ((flag & FLAG_X_SHORT) !== 0) {
|
|
852
|
-
xSize += 1;
|
|
853
|
-
xIdx++;
|
|
854
|
-
} else if ((flag & FLAG_X_SAME) === 0) {
|
|
855
|
-
xSize += 2;
|
|
856
|
-
xIdx++;
|
|
885
|
+
if (dy === 0) outFlag |= FLAG_Y_SAME;
|
|
886
|
+
else if (dy >= -255 && dy <= 255) {
|
|
887
|
+
outFlag |= FLAG_Y_SHORT;
|
|
888
|
+
if (dy > 0) outFlag |= FLAG_Y_SAME;
|
|
889
|
+
yOut[yLen++] = dy > 0 ? dy : -dy;
|
|
890
|
+
} else {
|
|
891
|
+
yOut[yLen++] = dy >> 8 & 255;
|
|
892
|
+
yOut[yLen++] = dy & 255;
|
|
857
893
|
}
|
|
858
|
-
if (
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
} else
|
|
862
|
-
|
|
863
|
-
|
|
894
|
+
if (outFlag === lastFlag && repeatCount < 255) {
|
|
895
|
+
flagsOut[flagsLen - 1] |= FLAG_REPEAT;
|
|
896
|
+
repeatCount++;
|
|
897
|
+
} else {
|
|
898
|
+
if (repeatCount > 0) {
|
|
899
|
+
flagsOut[flagsLen++] = repeatCount;
|
|
900
|
+
repeatCount = 0;
|
|
901
|
+
}
|
|
902
|
+
flagsOut[flagsLen++] = outFlag;
|
|
903
|
+
lastFlag = outFlag;
|
|
864
904
|
}
|
|
865
905
|
}
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
else if ((flag & FLAG_Y_SAME) === 0) {
|
|
879
|
-
const val = yCoords[yIdx++];
|
|
880
|
-
output[offset++] = val >> 8 & 255;
|
|
881
|
-
output[offset++] = val & 255;
|
|
882
|
-
}
|
|
883
|
-
return output;
|
|
906
|
+
if (repeatCount > 0) flagsOut[flagsLen++] = repeatCount;
|
|
907
|
+
flagStream.pos = flagPos;
|
|
908
|
+
glyphStream.pos = glyphPos;
|
|
909
|
+
return {
|
|
910
|
+
flagsLen,
|
|
911
|
+
xLen,
|
|
912
|
+
yLen,
|
|
913
|
+
xMin,
|
|
914
|
+
yMin,
|
|
915
|
+
xMax,
|
|
916
|
+
yMax
|
|
917
|
+
};
|
|
884
918
|
}
|
|
885
919
|
function reconstructHmtx(data, table, numGlyphs, numHMetrics, xMins) {
|
|
886
|
-
const
|
|
887
|
-
const hmtxFlags =
|
|
888
|
-
if (hmtxFlags === null) throw new Error("Failed to read hmtx flags");
|
|
920
|
+
const hmtxStream = makeByteStream(data, table.srcOffset, table.srcLength);
|
|
921
|
+
const hmtxFlags = bsReadU8(hmtxStream);
|
|
889
922
|
const hasProportionalLsbs = (hmtxFlags & 1) === 0;
|
|
890
923
|
const hasMonospaceLsbs = (hmtxFlags & 2) === 0;
|
|
891
|
-
const advanceWidths =
|
|
892
|
-
for (let i = 0; i < numHMetrics; i++)
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
for (let i = 0; i < numHMetrics; i++) if (hasProportionalLsbs) {
|
|
899
|
-
const lsb = buf.readS16();
|
|
900
|
-
if (lsb === null) throw new Error("Failed to read proportional LSB");
|
|
901
|
-
lsbs.push(lsb);
|
|
902
|
-
} else lsbs.push(xMins[i]);
|
|
903
|
-
for (let i = numHMetrics; i < numGlyphs; i++) if (hasMonospaceLsbs) {
|
|
904
|
-
const lsb = buf.readS16();
|
|
905
|
-
if (lsb === null) throw new Error("Failed to read monospace LSB");
|
|
906
|
-
lsbs.push(lsb);
|
|
907
|
-
} else lsbs.push(xMins[i]);
|
|
924
|
+
const advanceWidths = new Uint16Array(numHMetrics);
|
|
925
|
+
for (let i = 0; i < numHMetrics; i++) advanceWidths[i] = bsReadU16(hmtxStream);
|
|
926
|
+
const lsbs = new Int16Array(numGlyphs);
|
|
927
|
+
for (let i = 0; i < numHMetrics; i++) if (hasProportionalLsbs) lsbs[i] = bsReadS16(hmtxStream);
|
|
928
|
+
else lsbs[i] = xMins[i];
|
|
929
|
+
for (let i = numHMetrics; i < numGlyphs; i++) if (hasMonospaceLsbs) lsbs[i] = bsReadS16(hmtxStream);
|
|
930
|
+
else lsbs[i] = xMins[i];
|
|
908
931
|
const outputSize = numHMetrics * 4 + (numGlyphs - numHMetrics) * 2;
|
|
909
932
|
const output = new Uint8Array(outputSize);
|
|
910
|
-
const view = new DataView(output.buffer);
|
|
911
933
|
let offset = 0;
|
|
912
934
|
for (let i = 0; i < numGlyphs; i++) {
|
|
913
935
|
if (i < numHMetrics) {
|
|
914
|
-
|
|
936
|
+
writeUint16BE(output, offset, advanceWidths[i]);
|
|
915
937
|
offset += 2;
|
|
916
938
|
}
|
|
917
|
-
|
|
939
|
+
writeInt16BE(output, offset, lsbs[i]);
|
|
918
940
|
offset += 2;
|
|
919
941
|
}
|
|
920
942
|
return output;
|
|
@@ -924,12 +946,24 @@ function updateTableEntry(view, entryOffset, checksum, offset, length) {
|
|
|
924
946
|
view.setUint32(entryOffset + 8, offset);
|
|
925
947
|
view.setUint32(entryOffset + 12, length);
|
|
926
948
|
}
|
|
949
|
+
function readInt16BE(data, offset) {
|
|
950
|
+
const val = data[offset] << 8 | data[offset + 1];
|
|
951
|
+
return (val & 32768) !== 0 ? val - 65536 : val;
|
|
952
|
+
}
|
|
953
|
+
function writeInt16BE(data, offset, value) {
|
|
954
|
+
data[offset] = value >> 8 & 255;
|
|
955
|
+
data[offset + 1] = value & 255;
|
|
956
|
+
}
|
|
957
|
+
function writeUint16BE(data, offset, value) {
|
|
958
|
+
data[offset] = value >> 8 & 255;
|
|
959
|
+
data[offset + 1] = value & 255;
|
|
960
|
+
}
|
|
927
961
|
function computeChecksum(data, offset, length) {
|
|
928
962
|
let sum = 0;
|
|
929
963
|
const end = offset + length;
|
|
930
|
-
const
|
|
964
|
+
const dataView = new DataView(data.buffer, data.byteOffset);
|
|
931
965
|
const alignedEnd = offset + (length & -4);
|
|
932
|
-
for (let i = offset; i < alignedEnd; i += 4) sum = sum +
|
|
966
|
+
for (let i = offset; i < alignedEnd; i += 4) sum = sum + dataView.getUint32(i) >>> 0;
|
|
933
967
|
if (end > alignedEnd) {
|
|
934
968
|
let last = 0;
|
|
935
969
|
for (let i = alignedEnd; i < end; i++) last = last << 8 | data[i];
|