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