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