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