woff2-decode 0.1.2 → 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/dist/index.cjs CHANGED
@@ -26,8 +26,8 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
26
26
  }) : target, mod));
27
27
 
28
28
  //#endregion
29
- let brotli_decompress = require("brotli/decompress");
30
- brotli_decompress = __toESM(brotli_decompress);
29
+ let brotli_decompress_js = require("brotli/decompress.js");
30
+ brotli_decompress_js = __toESM(brotli_decompress_js);
31
31
 
32
32
  //#region src/brotli.ts
33
33
  let nativeBrotli = null;
@@ -46,10 +46,10 @@ function tryLoadNative() {
46
46
  nativeBrotli = tryLoadNative();
47
47
  function decompress(data) {
48
48
  if (nativeBrotli) return nativeBrotli(data);
49
- return (0, brotli_decompress.default)(data);
49
+ return (0, brotli_decompress_js.default)(data);
50
50
  }
51
51
  function decompressPure(data) {
52
- return (0, brotli_decompress.default)(data);
52
+ return (0, brotli_decompress_js.default)(data);
53
53
  }
54
54
  const hasNative = nativeBrotli !== null;
55
55
 
@@ -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.view.byteLength;
75
+ return this.u8.byteLength;
74
76
  }
75
77
  get remaining() {
76
- return this.view.byteLength - this.pos;
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.view.byteLength || this.pos + n < this.pos) return false;
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.view.byteLength || offset < 0) return false;
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.view.byteLength) return null;
93
- return this.view.getUint8(this.pos++);
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.view.byteLength) return null;
97
- const val = this.view.getUint16(this.pos, false);
98
- this.pos += 2;
99
- return val;
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.view.byteLength) return null;
103
- const val = this.view.getInt16(this.pos, false);
104
- this.pos += 2;
105
- return val;
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.view.byteLength) return null;
109
- const val = this.view.getUint32(this.pos, false);
110
- this.pos += 4;
111
- return val;
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.view.byteLength) return null;
115
- const val = this.view.getInt32(this.pos, false);
116
- this.pos += 4;
117
- return val;
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.view.byteLength || n < 0) return null;
121
- const result = new Uint8Array(this.view.buffer, this.view.byteOffset + this.pos, n);
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.view.byteLength || idx < 0) return null;
128
- return this.view.getUint8(idx);
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.view.byteLength || offset < 0 || length < 0) return null;
132
- return new Uint8Array(this.view.buffer, this.view.byteOffset + offset, length);
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 tableKey = `${table.tag}:${table.srcOffset}`;
473
- const existing = writtenTables.get(tableKey);
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(tableKey, {
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
- const locaKey = `${TAG_LOCA}:${locaTable.srcOffset}`;
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(tableKey, {
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(`${TAG_HEAD}:${headTable.srcOffset}`);
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 buf = new Buffer$1(data, glyfTable.srcOffset, glyfTable.transformLength);
562
- const version = buf.readU16();
563
- const optionFlags = buf.readU16();
564
- const numGlyphs = buf.readU16();
565
- const indexFormat = buf.readU16();
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 = buf.readU32();
570
- const nPointsStreamSize = buf.readU32();
571
- const flagStreamSize = buf.readU32();
572
- const glyphStreamSize = buf.readU32();
573
- const compositeStreamSize = buf.readU32();
574
- const bboxStreamSize = buf.readU32();
575
- const instructionStreamSize = buf.readU32();
576
- if (nContourStreamSize === null || nPointsStreamSize === null || flagStreamSize === null || glyphStreamSize === null || compositeStreamSize === null || bboxStreamSize === null || instructionStreamSize === null) throw new Error("Invalid glyf substream sizes");
577
- let offset = buf.offset;
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 = new Buffer$1(data, glyfTable.srcOffset + offset, nPointsStreamSize);
635
+ const nPointsStream = makeByteStream(data, offset, nPointsStreamSize);
581
636
  offset += nPointsStreamSize;
582
- const flagStream = new Buffer$1(data, glyfTable.srcOffset + offset, flagStreamSize);
637
+ const flagStream = makeByteStream(data, offset, flagStreamSize);
583
638
  offset += flagStreamSize;
584
- const glyphStream = new Buffer$1(data, glyfTable.srcOffset + offset, glyphStreamSize);
639
+ const glyphStream = makeByteStream(data, offset, glyphStreamSize);
585
640
  offset += glyphStreamSize;
586
- const compositeStream = new Buffer$1(data, glyfTable.srcOffset + offset, compositeStreamSize);
641
+ const compositeStream = makeByteStream(data, offset, compositeStreamSize);
587
642
  offset += compositeStreamSize;
588
- const bboxStream = new Buffer$1(data, glyfTable.srcOffset + offset, bboxStreamSize);
643
+ const bboxStream = makeByteStream(data, offset, bboxStreamSize);
589
644
  offset += bboxStreamSize;
590
- const instructionStream = new Buffer$1(data, glyfTable.srcOffset + offset, instructionStreamSize);
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(glyfTable.srcOffset + offset + instructionStreamSize, glyfTable.srcOffset + offset + instructionStreamSize + overlapBitmapLength);
650
+ overlapBitmap = data.subarray(offset + instructionStreamSize, offset + instructionStreamSize + overlapBitmapLength);
596
651
  }
597
- const bboxBitmapLength = numGlyphs + 31 >> 5 << 2;
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.push(glyfOffset);
606
- const nContours = nContourStream.readS16();
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 = read255UShort(glyphStream) ?? 0;
673
+ if (haveInstructions) instructionSize = bsRead255UShort(glyphStream);
618
674
  const glyphSize = 10 + compositeData.byteLength + (haveInstructions ? 2 + instructionSize : 0);
619
675
  ensureCapacity(glyphSize);
620
- new DataView(glyfOutput.buffer, glyfOffset).setInt16(0, -1);
621
- const bbox = bboxStream.readBytes(8);
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] = new DataView(bbox.buffer, bbox.byteOffset).getInt16(0);
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
- new DataView(glyfOutput.buffer, instrOffset).setUint16(0, instructionSize);
629
- const instructions = instructionStream.readBytes(instructionSize);
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
- const result = reconstructSimpleGlyph(nContours, haveBbox, (overlapBitmap?.[glyphId >> 3] ?? 0) & 128 >> (glyphId & 7), nPointsStream, flagStream, glyphStream, bboxStream, instructionStream);
637
- ensureCapacity(result.byteLength);
638
- glyfOutput.set(result, glyfOffset);
639
- if (result.byteLength >= 4) fontInfo.xMins[glyphId] = new DataView(result.buffer, result.byteOffset).getInt16(2);
640
- glyfOffset += result.byteLength;
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.push(glyfOffset);
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.offset;
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.readU16() ?? 0;
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.skip(argSize);
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 reconstructSimpleGlyph(nContours, haveBbox, hasOverlapBit, nPointsStream, flagStream, glyphStream, bboxStream, instructionStream) {
691
- const pointsPerContour = [];
692
- let totalPoints = 0;
693
- for (let i = 0; i < nContours; i++) {
694
- const n = read255UShort(nPointsStream);
695
- if (n === null) throw new Error("Failed to read points count");
696
- pointsPerContour.push(n);
697
- totalPoints += n;
698
- }
699
- const points = decodeTriplets(flagStream, glyphStream, totalPoints);
700
- const instructionSize = read255UShort(glyphStream) ?? 0;
701
- const flagsAndCoords = encodePointsToGlyf(points, hasOverlapBit !== 0);
702
- const glyphSize = 10 + 2 * nContours + 2 + instructionSize + flagsAndCoords.byteLength;
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
- const flag = flagStream.readU8();
753
- if (flag === null) throw new Error("Failed to read triplet flag");
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, dy;
818
+ let dx;
819
+ let dy;
757
820
  if (flagLow < 10) {
758
821
  dx = 0;
759
- const b = glyphStream.readU8();
760
- if (b === null) throw new Error("Failed to read triplet data");
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
- const b = glyphStream.readU8();
765
- if (b === null) throw new Error("Failed to read triplet data");
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
- const b = glyphStream.readU8();
771
- if (b === null) throw new Error("Failed to read triplet data");
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
- const b0 = glyphStream.readU8();
779
- const b1 = glyphStream.readU8();
780
- if (b0 === null || b1 === null) throw new Error("Failed to read triplet data");
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 + (Math.floor(idx / 12) << 8) + b0;
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
- const b0 = glyphStream.readU8();
788
- const b1 = glyphStream.readU8();
789
- const b2 = glyphStream.readU8();
790
- if (b0 === null || b1 === null || b2 === null) throw new Error("Failed to read triplet data");
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
- const b0 = glyphStream.readU8();
797
- const b1 = glyphStream.readU8();
798
- const b2 = glyphStream.readU8();
799
- const b3 = glyphStream.readU8();
800
- if (b0 === null || b1 === null || b2 === null || b3 === null) throw new Error("Failed to read triplet data");
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
- points.push({
809
- x,
810
- y,
811
- onCurve
812
- });
813
- }
814
- return points;
815
- }
816
- function encodePointsToGlyf(points, hasOverlapBit) {
817
- const FLAG_ON_CURVE = 1;
818
- const FLAG_X_SHORT = 2;
819
- const FLAG_Y_SHORT = 4;
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
- flag |= FLAG_X_SHORT;
839
- if (dx > 0) flag |= FLAG_X_SAME;
840
- xCoords.push(Math.abs(dx));
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
- if (repeatCount > 0) encodedFlags.push(repeatCount);
862
- encodedFlags.push(flag);
863
- repeatCount = 0;
888
+ xOut[xLen++] = dx >> 8 & 255;
889
+ xOut[xLen++] = dx & 255;
864
890
  }
865
- lastFlag = flag;
866
- }
867
- if (repeatCount > 0) encodedFlags.push(repeatCount);
868
- let xSize = 0;
869
- let ySize = 0;
870
- let xIdx = 0;
871
- let yIdx = 0;
872
- for (const flag of flags) {
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 ((flag & FLAG_Y_SHORT) !== 0) {
881
- ySize += 1;
882
- yIdx++;
883
- } else if ((flag & FLAG_Y_SAME) === 0) {
884
- ySize += 2;
885
- yIdx++;
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
- const output = new Uint8Array(encodedFlags.length + xSize + ySize);
889
- let offset = 0;
890
- for (const f of encodedFlags) output[offset++] = f;
891
- xIdx = 0;
892
- for (const flag of flags) if ((flag & FLAG_X_SHORT) !== 0) output[offset++] = xCoords[xIdx++];
893
- else if ((flag & FLAG_X_SAME) === 0) {
894
- const val = xCoords[xIdx++];
895
- output[offset++] = val >> 8 & 255;
896
- output[offset++] = val & 255;
897
- }
898
- yIdx = 0;
899
- for (const flag of flags) if ((flag & FLAG_Y_SHORT) !== 0) output[offset++] = yCoords[yIdx++];
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 buf = new Buffer$1(data, table.srcOffset, table.srcLength);
909
- const hmtxFlags = buf.readU8();
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
- const w = buf.readU16();
916
- if (w === null) throw new Error("Failed to read advance width");
917
- advanceWidths.push(w);
918
- }
919
- const lsbs = [];
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
- view.setUint16(offset, advanceWidths[i]);
942
+ writeUint16BE(output, offset, advanceWidths[i]);
937
943
  offset += 2;
938
944
  }
939
- view.setInt16(offset, lsbs[i]);
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 view = new DataView(data.buffer, data.byteOffset);
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 + view.getUint32(i) >>> 0;
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];