pptx-react-viewer 1.22.1 → 1.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/dist/{Model3DScene-NLUSXHD6.mjs → Model3DScene-4XNBDASE.mjs} +2 -2
  3. package/dist/Model3DScene-4XNBDASE.mjs.br +0 -0
  4. package/dist/Model3DScene-4XNBDASE.mjs.gz +0 -0
  5. package/dist/{Model3DScene-3YB3JAJS.js → Model3DScene-AQS7KDM4.js} +3 -3
  6. package/dist/Model3DScene-AQS7KDM4.js.br +0 -0
  7. package/dist/Model3DScene-AQS7KDM4.js.gz +0 -0
  8. package/dist/{PowerPointViewer-WGVR_u4z.d.ts → PowerPointViewer-DFTatVS4.d.ts} +2 -2
  9. package/dist/{PowerPointViewer-WGVR_u4z.d.ts.map → PowerPointViewer-DFTatVS4.d.ts.map} +1 -1
  10. package/dist/{SurfaceChart3DScene-PUQ5DCO5.js → SurfaceChart3DScene-HA2SD6BY.js} +3 -3
  11. package/dist/SurfaceChart3DScene-HA2SD6BY.js.br +0 -0
  12. package/dist/SurfaceChart3DScene-HA2SD6BY.js.gz +0 -0
  13. package/dist/{SurfaceChart3DScene-YBMJQGH3.mjs → SurfaceChart3DScene-K7ZDXDBL.mjs} +2 -2
  14. package/dist/SurfaceChart3DScene-K7ZDXDBL.mjs.br +0 -0
  15. package/dist/SurfaceChart3DScene-K7ZDXDBL.mjs.gz +0 -0
  16. package/dist/{chunk-VSNXVKJI.js → chunk-2DXNTVFV.js} +159 -62
  17. package/dist/chunk-2DXNTVFV.js.br +0 -0
  18. package/dist/chunk-2DXNTVFV.js.gz +0 -0
  19. package/dist/{chunk-B7JRQB5F.mjs → chunk-36NC56XG.mjs} +311 -292
  20. package/dist/chunk-36NC56XG.mjs.br +0 -0
  21. package/dist/chunk-36NC56XG.mjs.gz +0 -0
  22. package/dist/{chunk-3ZGCM6RC.mjs → chunk-AFWMAFWR.mjs} +1076 -289
  23. package/dist/chunk-AFWMAFWR.mjs.br +0 -0
  24. package/dist/chunk-AFWMAFWR.mjs.gz +0 -0
  25. package/dist/{chunk-QVQTDWB7.js → chunk-H5RREYCO.js} +463 -454
  26. package/dist/chunk-H5RREYCO.js.br +0 -0
  27. package/dist/chunk-H5RREYCO.js.gz +0 -0
  28. package/dist/{chunk-5SBQZEKD.mjs → chunk-I42XL2WU.mjs} +130 -34
  29. package/dist/chunk-I42XL2WU.mjs.br +0 -0
  30. package/dist/chunk-I42XL2WU.mjs.gz +0 -0
  31. package/dist/{chunk-DZUI64SO.mjs → chunk-LFEVG3ED.mjs} +13 -4
  32. package/dist/chunk-LFEVG3ED.mjs.br +0 -0
  33. package/dist/chunk-LFEVG3ED.mjs.gz +0 -0
  34. package/dist/{chunk-YPZV4KJT.js → chunk-LS2AIX2H.js} +1087 -1068
  35. package/dist/chunk-LS2AIX2H.js.br +0 -0
  36. package/dist/chunk-LS2AIX2H.js.gz +0 -0
  37. package/dist/{chunk-AD3EAEY6.js → chunk-MS6S6YYR.js} +1076 -289
  38. package/dist/chunk-MS6S6YYR.js.br +0 -0
  39. package/dist/chunk-MS6S6YYR.js.gz +0 -0
  40. package/dist/{dist-3YRZ26TL.mjs → dist-5KQQ6GP2.mjs} +1 -1
  41. package/dist/dist-5KQQ6GP2.mjs.br +0 -0
  42. package/dist/dist-5KQQ6GP2.mjs.gz +0 -0
  43. package/dist/{dist-M2P6HBG4.js → dist-CVSSAFDL.js} +560 -560
  44. package/dist/dist-CVSSAFDL.js.br +0 -0
  45. package/dist/dist-CVSSAFDL.js.gz +0 -0
  46. package/dist/hooks-unstable.d.ts +5107 -5242
  47. package/dist/hooks-unstable.d.ts.map +1 -1
  48. package/dist/hooks-unstable.js +76 -76
  49. package/dist/hooks-unstable.js.br +0 -0
  50. package/dist/hooks-unstable.js.gz +0 -0
  51. package/dist/hooks-unstable.mjs +3 -3
  52. package/dist/hooks-unstable.mjs.br +0 -0
  53. package/dist/hooks-unstable.mjs.gz +0 -0
  54. package/dist/i18n.d.ts +3 -0
  55. package/dist/{index-SUL_8gq8.d.ts → index-CGKutKk1.d.ts} +4 -1
  56. package/dist/index-CGKutKk1.d.ts.map +1 -0
  57. package/dist/{index-ntF3kHHU.d.ts → index-COgh2UTL.d.ts} +2 -2
  58. package/dist/{index-ntF3kHHU.d.ts.map → index-COgh2UTL.d.ts.map} +1 -1
  59. package/dist/index.d.ts +3328 -3386
  60. package/dist/index.js +30 -30
  61. package/dist/index.js.br +0 -0
  62. package/dist/index.js.gz +0 -0
  63. package/dist/index.mjs +4 -4
  64. package/dist/index.mjs.br +0 -0
  65. package/dist/index.mjs.gz +0 -0
  66. package/dist/pptx-viewer.css +1 -1
  67. package/dist/pptx-viewer.css.br +0 -0
  68. package/dist/pptx-viewer.css.gz +0 -0
  69. package/dist/{types-Cdh0XYcS.d.ts → types-Da1M5VBY.d.ts} +2 -2
  70. package/dist/{types-Cdh0XYcS.d.ts.map → types-Da1M5VBY.d.ts.map} +1 -1
  71. package/dist/viewer/index.d.ts +5118 -5224
  72. package/dist/viewer/index.js +20 -20
  73. package/dist/viewer/index.js.br +0 -0
  74. package/dist/viewer/index.js.gz +0 -0
  75. package/dist/viewer/index.mjs +4 -4
  76. package/dist/viewer/index.mjs.br +0 -0
  77. package/dist/viewer/index.mjs.gz +0 -0
  78. package/package.json +1 -1
  79. package/dist/Model3DScene-3YB3JAJS.js.br +0 -0
  80. package/dist/Model3DScene-3YB3JAJS.js.gz +0 -0
  81. package/dist/Model3DScene-NLUSXHD6.mjs.br +0 -0
  82. package/dist/Model3DScene-NLUSXHD6.mjs.gz +0 -0
  83. package/dist/SurfaceChart3DScene-PUQ5DCO5.js.br +0 -0
  84. package/dist/SurfaceChart3DScene-PUQ5DCO5.js.gz +0 -0
  85. package/dist/SurfaceChart3DScene-YBMJQGH3.mjs.br +0 -0
  86. package/dist/SurfaceChart3DScene-YBMJQGH3.mjs.gz +0 -0
  87. package/dist/chunk-3ZGCM6RC.mjs.br +0 -0
  88. package/dist/chunk-3ZGCM6RC.mjs.gz +0 -0
  89. package/dist/chunk-5SBQZEKD.mjs.br +0 -0
  90. package/dist/chunk-5SBQZEKD.mjs.gz +0 -0
  91. package/dist/chunk-AD3EAEY6.js.br +0 -0
  92. package/dist/chunk-AD3EAEY6.js.gz +0 -0
  93. package/dist/chunk-B7JRQB5F.mjs.br +0 -0
  94. package/dist/chunk-B7JRQB5F.mjs.gz +0 -0
  95. package/dist/chunk-DZUI64SO.mjs.br +0 -0
  96. package/dist/chunk-DZUI64SO.mjs.gz +0 -0
  97. package/dist/chunk-QVQTDWB7.js.br +0 -0
  98. package/dist/chunk-QVQTDWB7.js.gz +0 -0
  99. package/dist/chunk-VSNXVKJI.js.br +0 -0
  100. package/dist/chunk-VSNXVKJI.js.gz +0 -0
  101. package/dist/chunk-YPZV4KJT.js.br +0 -0
  102. package/dist/chunk-YPZV4KJT.js.gz +0 -0
  103. package/dist/dist-3YRZ26TL.mjs.br +0 -0
  104. package/dist/dist-3YRZ26TL.mjs.gz +0 -0
  105. package/dist/dist-M2P6HBG4.js.br +0 -0
  106. package/dist/dist-M2P6HBG4.js.gz +0 -0
  107. package/dist/index-SUL_8gq8.d.ts.map +0 -1
@@ -3,7 +3,24 @@ import { XMLBuilder, XMLParser } from 'fast-xml-parser';
3
3
 
4
4
  // ../core/dist/index.mjs
5
5
 
6
- // ../../node_modules/.bun/mtx-decompressor@1.4.2/node_modules/mtx-decompressor/dist/index.mjs
6
+ // ../../node_modules/.bun/mtx-decompressor@1.6.0/node_modules/mtx-decompressor/dist/index.mjs
7
+ var WARNING_CODES = /* @__PURE__ */ new Set([
8
+ "WARN_BAD_VERSION"
9
+ /* WarnBadVersion */
10
+ ]);
11
+ var EotError = class _EotError extends Error {
12
+ code;
13
+ constructor(code, message) {
14
+ super(message);
15
+ this.name = "EotError";
16
+ this.code = code;
17
+ Object.setPrototypeOf(this, _EotError.prototype);
18
+ }
19
+ /** True when this represents a recoverable warning rather than a fatal error. */
20
+ get isWarning() {
21
+ return WARNING_CODES.has(this.code);
22
+ }
23
+ };
7
24
  var Stream = class _Stream {
8
25
  buf;
9
26
  size;
@@ -43,7 +60,26 @@ var Stream = class _Stream {
43
60
  this.buf = newBuf;
44
61
  this.reserved = n;
45
62
  }
63
+ /**
64
+ * Reject byte-level access while the stream sits mid-byte (`bitPos != 0`).
65
+ *
66
+ * libeot returns `EOT_OFF_BYTE_BOUNDARY` for any byte read/write/seek issued
67
+ * before a partial byte has been consumed. The bit-level reader
68
+ * ({@link readNBits}) is the sole legitimate mid-byte accessor and bypasses
69
+ * this guard by touching `buf`/`pos` directly. On valid input the only
70
+ * `readNBits` caller consumes whole bytes per point, so the stream is always
71
+ * byte-aligned when a byte accessor runs and this guard never fires.
72
+ */
73
+ ensureByteAligned() {
74
+ if (this.bitPos !== 0) {
75
+ throw new EotError(
76
+ "OFF_BYTE_BOUNDARY",
77
+ `Stream: byte-level access at a non-byte boundary (bitPos=${this.bitPos}, pos=${this.pos})`
78
+ );
79
+ }
80
+ }
46
81
  ensureWrite(n) {
82
+ this.ensureByteAligned();
47
83
  const needed = this.pos + n;
48
84
  if (needed > this.reserved) {
49
85
  this.reserve(Math.max(needed, this.reserved * 2 || 256));
@@ -53,6 +89,7 @@ var Stream = class _Stream {
53
89
  }
54
90
  }
55
91
  ensureRead(n) {
92
+ this.ensureByteAligned();
56
93
  if (this.pos + n > this.size) {
57
94
  throw new Error(
58
95
  `Stream: not enough data (need ${n} bytes at pos ${this.pos}, size ${this.size})`
@@ -60,14 +97,18 @@ var Stream = class _Stream {
60
97
  }
61
98
  }
62
99
  // --- Seek ---
100
+ // A seek requires the stream to be byte-aligned and never clears `bitPos`
101
+ // itself (mirroring libeot, which refuses to seek mid-byte rather than
102
+ // silently re-aligning). The alignment guard leaves `bitPos` at 0.
63
103
  seekAbsolute(pos2) {
104
+ this.ensureByteAligned();
64
105
  if (pos2 > this.size) {
65
106
  throw new Error(`Stream: seek past end (${pos2} > ${this.size})`);
66
107
  }
67
108
  this.pos = pos2;
68
- this.bitPos = 0;
69
109
  }
70
110
  seekRelative(offset) {
111
+ this.ensureByteAligned();
71
112
  const newPos = this.pos + offset;
72
113
  if (newPos < 0) {
73
114
  throw new Error("Stream: negative seek");
@@ -76,17 +117,23 @@ var Stream = class _Stream {
76
117
  throw new Error("Stream: seek past end");
77
118
  }
78
119
  this.pos = newPos;
79
- this.bitPos = 0;
80
120
  }
121
+ // Seek into already-reserved-but-unwritten space, extending `size` up to the
122
+ // seek target. libeot returns `EOT_SEEK_PAST_EOS` when the target exceeds the
123
+ // reserved capacity; we mirror that rather than growing the buffer, so an
124
+ // over-reach surfaces as a failure instead of a silent realloc.
81
125
  seekAbsoluteThroughReserve(pos2) {
126
+ this.ensureByteAligned();
82
127
  if (pos2 > this.reserved) {
83
- this.reserve(pos2);
128
+ throw new EotError(
129
+ "SEEK_PAST_EOS",
130
+ `Stream: seek to ${pos2} past reserved end (${this.reserved})`
131
+ );
84
132
  }
85
133
  if (pos2 > this.size) {
86
134
  this.size = pos2;
87
135
  }
88
136
  this.pos = pos2;
89
- this.bitPos = 0;
90
137
  }
91
138
  seekRelativeThroughReserve(offset) {
92
139
  this.seekAbsoluteThroughReserve(this.pos + offset);
@@ -140,6 +187,9 @@ var Stream = class _Stream {
140
187
  this.buf[this.pos++] = v & 255;
141
188
  }
142
189
  writeU24(v) {
190
+ if (v < 0 || v > 16777215) {
191
+ throw new Error(`Stream: writeU24 value out of range: ${v}`);
192
+ }
143
193
  this.ensureWrite(3);
144
194
  this.buf[this.pos++] = v >> 16 & 255;
145
195
  this.buf[this.pos++] = v >> 8 & 255;
@@ -163,6 +213,9 @@ var Stream = class _Stream {
163
213
  if (n === 0) {
164
214
  return 0;
165
215
  }
216
+ if (n > 32) {
217
+ throw new Error(`Stream: readNBits width out of range: ${n} (max 32)`);
218
+ }
166
219
  let value = 0;
167
220
  let bitsRemaining = n;
168
221
  while (bitsRemaining > 0) {
@@ -181,18 +234,36 @@ var Stream = class _Stream {
181
234
  }
182
235
  bitsRemaining -= bitsToRead;
183
236
  }
184
- return value;
237
+ return value >>> 0;
185
238
  }
186
239
  // --- Copy ---
187
- /** Copy `length` bytes from this stream to `dest`. */
240
+ /**
241
+ * Copy `length` bytes from this stream to `dest`.
242
+ *
243
+ * Both streams must be byte-aligned. The destination must already have the
244
+ * capacity reserved: libeot returns `EOT_OUT_OF_RESERVED_SPACE` when a copy
245
+ * would overrun the reserved buffer, so we throw rather than auto-growing —
246
+ * an under-reservation is a bug we want surfaced, not silently patched.
247
+ */
188
248
  copyTo(dest, length) {
249
+ this.ensureByteAligned();
250
+ dest.ensureByteAligned();
189
251
  if (this.pos + length > this.size) {
190
252
  throw new Error("Stream: not enough data for copy");
191
253
  }
192
- dest.ensureWrite(length);
254
+ const needed = dest.pos + length;
255
+ if (needed > dest.reserved) {
256
+ throw new EotError(
257
+ "OUT_OF_RESERVED_SPACE",
258
+ `Stream: copy of ${length} bytes exceeds reserved capacity (need ${needed}, reserved ${dest.reserved})`
259
+ );
260
+ }
193
261
  dest.buf.set(this.buf.subarray(this.pos, this.pos + length), dest.pos);
194
262
  this.pos += length;
195
263
  dest.pos += length;
264
+ if (dest.pos > dest.size) {
265
+ dest.size = dest.pos;
266
+ }
196
267
  }
197
268
  /** Read rest of data as 4-byte-aligned U32 values. Returns 0 on incomplete read. */
198
269
  readRestAsU32() {
@@ -213,19 +284,27 @@ var Stream = class _Stream {
213
284
  }
214
285
  return this.readU32();
215
286
  }
216
- /** Compute checksum of bytes from beginPos to endPos as 4-byte aligned U32 sum. */
287
+ /**
288
+ * Compute the SFNT-style checksum of bytes in `[beginPos, endPos)` as a sum
289
+ * of big-endian U32 words, zero-padding a final partial word. Bounds strictly
290
+ * on `endPos` (not the stream's `size`), so an unaligned range never folds in
291
+ * bytes past `endPos`. Leaves the stream position unchanged.
292
+ */
217
293
  checksumU32(beginPos, endPos) {
294
+ if (beginPos > endPos) {
295
+ throw new Error(`Stream: checksumU32 beginPos ${beginPos} > endPos ${endPos}`);
296
+ }
297
+ if (endPos > this.size) {
298
+ throw new Error(`Stream: checksumU32 endPos ${endPos} exceeds size ${this.size}`);
299
+ }
218
300
  let sum = 0;
219
- const savedPos = this.pos;
220
- this.pos = beginPos;
221
- while (this.pos < endPos) {
222
- const chunk = this.readRestAsU32();
223
- if (chunk === null) {
224
- break;
301
+ for (let p = beginPos; p < endPos; p += 4) {
302
+ let word = 0;
303
+ for (let i = 0; i < 4; i++) {
304
+ word = word << 8 | (p + i < endPos ? this.buf[p + i] : 0);
225
305
  }
226
- sum = sum + chunk >>> 0;
306
+ sum = sum + (word >>> 0) >>> 0;
227
307
  }
228
- this.pos = savedPos;
229
308
  return sum;
230
309
  }
231
310
  /** Get a copy of the written data. */
@@ -425,10 +504,9 @@ function read255Short(s) {
425
504
  }
426
505
  function unpackCVT(table, sIn) {
427
506
  sIn.seekAbsolute(table.offset);
428
- const tableLength = sIn.readU16();
429
- const numEntries = tableLength >>> 1;
507
+ const numEntries = sIn.readU16();
430
508
  const out = new Stream(null, 0);
431
- out.reserve(tableLength);
509
+ out.reserve(numEntries * 2);
432
510
  let lastValue = 0;
433
511
  for (let i = 0; i < numEntries; i++) {
434
512
  const code = sIn.readU8();
@@ -500,7 +578,13 @@ function decodePushInstructions(sIn, sOut, pushCount) {
500
578
  }
501
579
  while (remaining > 0) {
502
580
  const code = sIn.peekU8();
503
- if (code === 251 && remaining >= 3 && data.length >= 2) {
581
+ if (code === 251) {
582
+ if (remaining < 3 || data.length < 2) {
583
+ throw new EotError(
584
+ "CORRUPT_HOPCODE_DATA",
585
+ `corrupt hop3 (0xFB) push data: remaining=${remaining}, decoded=${data.length}`
586
+ );
587
+ }
504
588
  sIn.readU8();
505
589
  const prev = data[data.length - 2];
506
590
  put(prev);
@@ -508,7 +592,13 @@ function decodePushInstructions(sIn, sOut, pushCount) {
508
592
  put(val);
509
593
  put(prev);
510
594
  remaining -= 3;
511
- } else if (code === 252 && remaining >= 5 && data.length >= 2) {
595
+ } else if (code === 252) {
596
+ if (remaining < 5 || data.length < 2) {
597
+ throw new EotError(
598
+ "CORRUPT_HOPCODE_DATA",
599
+ `corrupt hop4 (0xFC) push data: remaining=${remaining}, decoded=${data.length}`
600
+ );
601
+ }
512
602
  sIn.readU8();
513
603
  const prev = data[data.length - 2];
514
604
  put(prev);
@@ -602,8 +692,8 @@ function decodeSimpleGlyph(numContours, streams, out, calcBBox, minX, minY, maxX
602
692
  }
603
693
  xDeltas[i] = dx;
604
694
  yDeltas[i] = dy;
605
- cumulativeX += dx;
606
- cumulativeY += dy;
695
+ cumulativeX += xDeltas[i];
696
+ cumulativeY += yDeltas[i];
607
697
  if (calcBBox) {
608
698
  if (cumulativeX < minX) {
609
699
  minX = cumulativeX;
@@ -807,8 +897,9 @@ function parseMaxp(table) {
807
897
  maxComponentElements
808
898
  };
809
899
  }
810
- function parseCTF(streams) {
900
+ function parseCTF(streams, options) {
811
901
  const s0 = streams[0];
902
+ const droppedTables = [];
812
903
  s0.readU32();
813
904
  const numTables = s0.readU16();
814
905
  s0.readU16();
@@ -819,10 +910,13 @@ function parseCTF(streams) {
819
910
  let locaIdx = -1;
820
911
  let maxpIdx = -1;
821
912
  let headIdx = -1;
913
+ let hmtxIdx = -1;
822
914
  for (let i = 0; i < numTables; i++) {
823
915
  const tag = s0.readChar() + s0.readChar() + s0.readChar() + s0.readChar();
824
916
  if (tag === "hdmx" || tag === "VDMX") {
825
917
  s0.seekRelative(12);
918
+ droppedTables.push(tag);
919
+ options?.onWarn?.(`Ignoring ${tag} table \u2014 MTX does not preserve it`);
826
920
  continue;
827
921
  }
828
922
  s0.seekRelative(4);
@@ -845,6 +939,8 @@ function parseCTF(streams) {
845
939
  maxpIdx = idx;
846
940
  } else if (tag === "head") {
847
941
  headIdx = idx;
942
+ } else if (tag === "hmtx") {
943
+ hmtxIdx = idx;
848
944
  } else ;
849
945
  }
850
946
  for (let i = 0; i < tables.length; i++) {
@@ -863,26 +959,29 @@ function parseCTF(streams) {
863
959
  }
864
960
  table.buf = buf;
865
961
  if (table.tag === "head") {
962
+ if (table.bufSize < 12) {
963
+ throw new EotError(
964
+ "MALFORMED_HEAD_TABLE",
965
+ `head table too small: ${table.bufSize} bytes (need at least 12)`
966
+ );
967
+ }
866
968
  table.buf[8] = 0;
867
969
  table.buf[9] = 0;
868
970
  table.buf[10] = 0;
869
971
  table.buf[11] = 0;
870
972
  }
871
973
  }
872
- let headData = { indexToLocFormat: 0 };
873
- if (headIdx >= 0) {
874
- headData = parseHead(tables[headIdx]);
974
+ if (maxpIdx < 0) {
975
+ throw new EotError("NO_MAXP_TABLE", "CTF font is missing a maxp table");
875
976
  }
876
- let maxpData = {
877
- numGlyphs: 0,
878
- maxPoints: 0,
879
- maxContours: 0,
880
- maxSizeOfInstructions: 0,
881
- maxComponentElements: 0
882
- };
883
- if (maxpIdx >= 0) {
884
- maxpData = parseMaxp(tables[maxpIdx]);
977
+ if (headIdx < 0) {
978
+ throw new EotError("NO_HEAD_TABLE", "CTF font is missing a head table");
885
979
  }
980
+ if (hmtxIdx < 0) {
981
+ throw new EotError("NO_HMTX_TABLE", "CTF font is missing an hmtx table");
982
+ }
983
+ const headData = parseHead(tables[headIdx]);
984
+ const maxpData = parseMaxp(tables[maxpIdx]);
886
985
  if (glyfIdx >= 0) {
887
986
  if (locaIdx < 0) {
888
987
  const locaTable = {
@@ -897,11 +996,11 @@ function parseCTF(streams) {
897
996
  }
898
997
  populateGlyfAndLoca(tables[glyfIdx], tables[locaIdx], headData, maxpData, streams);
899
998
  }
900
- return { tables };
999
+ return droppedTables.length > 0 ? { tables, droppedTables } : { tables };
901
1000
  }
902
1001
  function bitsUsed(x) {
903
1002
  if (x <= 0) {
904
- return 0;
1003
+ return 1;
905
1004
  }
906
1005
  return 32 - Math.clz32(x);
907
1006
  }
@@ -1093,12 +1192,15 @@ var BitIO = class {
1093
1192
  /**
1094
1193
  * @param data Source byte buffer.
1095
1194
  * @param offset Starting byte offset into `data`.
1096
- * @param size Number of bytes available from `offset`.
1195
+ * @param size Absolute end index into `data` (exclusive) — reading stops
1196
+ * once `index` reaches it. Defaults to `data.length`. Clamped
1197
+ * to `data.length` so an over-large value cannot read past the
1198
+ * end of the buffer and silently yield zero bits.
1097
1199
  */
1098
1200
  constructor(data, offset = 0, size) {
1099
1201
  this.data = data;
1100
1202
  this.index = offset;
1101
- this.size = size ?? data.length;
1203
+ this.size = Math.min(size ?? data.length, data.length);
1102
1204
  }
1103
1205
  /**
1104
1206
  * Read a single bit from the stream.
@@ -1111,13 +1213,14 @@ var BitIO = class {
1111
1213
  * shifted out of the original byte value).
1112
1214
  */
1113
1215
  inputBit() {
1114
- if (this.bitCount-- === 0) {
1216
+ if (this.bitCount === 0) {
1115
1217
  if (this.index >= this.size) {
1116
1218
  throw new Error("BitIO: end of data");
1117
1219
  }
1118
1220
  this.bitBuffer = this.data[this.index++];
1119
- this.bitCount = 7;
1221
+ this.bitCount = 8;
1120
1222
  }
1223
+ this.bitCount--;
1121
1224
  this.bitBuffer <<= 1;
1122
1225
  return (this.bitBuffer & 256) !== 0;
1123
1226
  }
@@ -1135,7 +1238,7 @@ var BitIO = class {
1135
1238
  value |= 1;
1136
1239
  }
1137
1240
  }
1138
- return value;
1241
+ return value >>> 0;
1139
1242
  }
1140
1243
  };
1141
1244
  var LEN_WIDTH = 3;
@@ -1145,8 +1248,8 @@ var MAX_2BYTE_DIST = 512;
1145
1248
  var PRELOAD_SIZE = 2 * 32 * 96 + 4 * 256;
1146
1249
  var LEN_MIN = 2;
1147
1250
  var DIST_MIN = 1;
1148
- var MAX_OUT_LEN = 4 * 1024 * 1024;
1149
- var MAX_OUT = 16 * 1024 * 1024;
1251
+ var MAX_OUT_LEN = (1 << 24) - 1;
1252
+ var MAX_OUT = 64 * 1024 * 1024;
1150
1253
  var RLE_INITIAL = 0;
1151
1254
  var RLE_NORMAL = 1;
1152
1255
  var RLE_SEEN_ESCAPE = 2;
@@ -1318,7 +1421,8 @@ function lzcompDecompress(data, size, version) {
1318
1421
  }
1319
1422
  }
1320
1423
  };
1321
- for (let pos2 = 0; pos2 < outLen; ) {
1424
+ let pos2 = 0;
1425
+ for (; pos2 < outLen; ) {
1322
1426
  const symbol = symEcoder.readSymbol();
1323
1427
  let value;
1324
1428
  if (symbol < 256) {
@@ -1349,6 +1453,12 @@ function lzcompDecompress(data, size, version) {
1349
1453
  pos2++;
1350
1454
  emitByte(value);
1351
1455
  }
1456
+ if (pos2 !== outLen) {
1457
+ throw new EotError(
1458
+ "MTX_ERROR",
1459
+ `LZCOMP decode overran output: pos=${pos2}, expected=${outLen}`
1460
+ );
1461
+ }
1352
1462
  return outBuf.subarray(0, outIdx);
1353
1463
  }
1354
1464
  function lgFloor(n) {
@@ -1452,14 +1562,18 @@ function dumpContainer(ctr) {
1452
1562
  beginningChecksum = beginningChecksum + word >>> 0;
1453
1563
  }
1454
1564
  totalChecksum = totalChecksum + beginningChecksum >>> 0;
1455
- const finalChecksum = 2981146554 - totalChecksum >>> 0;
1456
- if (headTable) {
1457
- const adjOffset = headTable.offset + 8;
1458
- buf[adjOffset] = finalChecksum >>> 24 & 255;
1459
- buf[adjOffset + 1] = finalChecksum >>> 16 & 255;
1460
- buf[adjOffset + 2] = finalChecksum >>> 8 & 255;
1461
- buf[adjOffset + 3] = finalChecksum & 255;
1565
+ if (!headTable) {
1566
+ throw new EotError(
1567
+ "NO_HEAD_TABLE",
1568
+ "cannot assemble SFNT: container is missing a head table"
1569
+ );
1462
1570
  }
1571
+ const finalChecksum = 2981146554 - totalChecksum >>> 0;
1572
+ const adjOffset = headTable.offset + 8;
1573
+ buf[adjOffset] = finalChecksum >>> 24 & 255;
1574
+ buf[adjOffset + 1] = finalChecksum >>> 16 & 255;
1575
+ buf[adjOffset + 2] = finalChecksum >>> 8 & 255;
1576
+ buf[adjOffset + 3] = finalChecksum & 255;
1463
1577
  out.pos = afterTables;
1464
1578
  return out.buf.subarray(0, out.pos);
1465
1579
  }
@@ -1505,14 +1619,64 @@ function decompressMtx(fontData, options) {
1505
1619
  }
1506
1620
  const { streams } = unpackMtx(data, data.length);
1507
1621
  const streamObjects = streams.map((buf) => new Stream(buf, buf.length));
1508
- const container = parseCTF(streamObjects);
1622
+ const container = parseCTF(streamObjects, { onWarn: options?.onWarn });
1509
1623
  return dumpContainer(container);
1510
1624
  }
1511
1625
  function decompressEotFont(fontData, compressed, encrypted) {
1512
1626
  return decompressMtx(fontData, { encrypted});
1513
1627
  }
1514
1628
 
1515
- // ../../node_modules/.bun/emf-converter@1.5.0/node_modules/emf-converter/dist/index.mjs
1629
+ // ../../node_modules/.bun/emf-converter@1.6.0/node_modules/emf-converter/dist/index.mjs
1630
+ function colorRefToHex(r, g, b) {
1631
+ const toHex3 = (v) => v.toString(16).padStart(2, "0");
1632
+ return `#${toHex3(r & 255)}${toHex3(g & 255)}${toHex3(b & 255)}`;
1633
+ }
1634
+ function readColorRef(view, offset) {
1635
+ const r = view.getUint8(offset);
1636
+ const g = view.getUint8(offset + 1);
1637
+ const b = view.getUint8(offset + 2);
1638
+ return colorRefToHex(r, g, b);
1639
+ }
1640
+ function argbToRgba(argb) {
1641
+ const a = (argb >>> 24 & 255) / 255;
1642
+ const r = argb >>> 16 & 255;
1643
+ const g = argb >>> 8 & 255;
1644
+ const b = argb & 255;
1645
+ return `rgba(${r},${g},${b},${a.toFixed(3)})`;
1646
+ }
1647
+ function lerpArgbToRgba(argbA, argbB, t) {
1648
+ const tc = Math.min(1, Math.max(0, t));
1649
+ const mix = (a2, b2) => Math.round(a2 + (b2 - a2) * tc);
1650
+ const aA = argbA >>> 24 & 255;
1651
+ const aB = argbB >>> 24 & 255;
1652
+ const r = mix(argbA >>> 16 & 255, argbB >>> 16 & 255);
1653
+ const g = mix(argbA >>> 8 & 255, argbB >>> 8 & 255);
1654
+ const b = mix(argbA & 255, argbB & 255);
1655
+ const a = mix(aA, aB) / 255;
1656
+ return `rgba(${r},${g},${b},${a.toFixed(3)})`;
1657
+ }
1658
+ function invertCssColor(color2) {
1659
+ const hex10 = /^#([0-9a-f]{6})$/i.exec(color2);
1660
+ if (hex10) {
1661
+ const v = parseInt(hex10[1], 16);
1662
+ const inv = 16777215 ^ v;
1663
+ return `#${inv.toString(16).padStart(6, "0")}`;
1664
+ }
1665
+ const shortHex = /^#([0-9a-f]{3})$/i.exec(color2);
1666
+ if (shortHex) {
1667
+ const [r, g, b] = shortHex[1].split("").map((c) => parseInt(c + c, 16));
1668
+ const toHex3 = (v) => (255 - v).toString(16).padStart(2, "0");
1669
+ return `#${toHex3(r)}${toHex3(g)}${toHex3(b)}`;
1670
+ }
1671
+ const rgba = /^rgba?\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(?:,\s*([\d.]+)\s*)?\)$/i.exec(color2);
1672
+ if (rgba) {
1673
+ const r = 255 - Math.min(255, parseInt(rgba[1], 10));
1674
+ const g = 255 - Math.min(255, parseInt(rgba[2], 10));
1675
+ const b = 255 - Math.min(255, parseInt(rgba[3], 10));
1676
+ return rgba[4] !== void 0 ? `rgba(${r},${g},${b},${rgba[4]})` : `rgb(${r},${g},${b})`;
1677
+ }
1678
+ return color2;
1679
+ }
1516
1680
  var EMR_HEADER = 1;
1517
1681
  var EMR_POLYBEZIER = 2;
1518
1682
  var EMR_POLYGON = 3;
@@ -1533,10 +1697,21 @@ var EMR_SETBKMODE = 18;
1533
1697
  var EMR_SETPOLYFILLMODE = 19;
1534
1698
  var EMR_SETROP2 = 20;
1535
1699
  var EMR_SETSTRETCHBLTMODE = 21;
1700
+ var R2_BLACK = 1;
1701
+ var R2_NOTMERGEPEN = 2;
1702
+ var R2_MASKNOTPEN = 3;
1703
+ var R2_NOTCOPYPEN = 4;
1704
+ var R2_MASKPENNOT = 5;
1536
1705
  var R2_NOT = 6;
1537
1706
  var R2_XORPEN = 7;
1707
+ var R2_NOTMASKPEN = 8;
1538
1708
  var R2_MASKPEN = 9;
1709
+ var R2_NOTXORPEN = 10;
1710
+ var R2_NOP = 11;
1711
+ var R2_MERGENOTPEN = 12;
1712
+ var R2_MERGEPENNOT = 14;
1539
1713
  var R2_MERGEPEN = 15;
1714
+ var R2_WHITE = 16;
1540
1715
  var MAX_CANVAS_DIMENSION = 8192;
1541
1716
  var MAX_RECORDS_DEFAULT = 2e5;
1542
1717
  var MAX_RECORDS_EMFPLUS_DEFAULT = 5e5;
@@ -1767,28 +1942,61 @@ function createTempCanvas(width, height) {
1767
1942
  }
1768
1943
  return null;
1769
1944
  }
1770
- function rop2ToGco(rop2) {
1945
+ function rop2Paint(rop2) {
1771
1946
  switch (rop2) {
1947
+ case R2_BLACK:
1948
+ return { gco: "source-over", colorTransform: "black", exact: true };
1949
+ case R2_WHITE:
1950
+ return { gco: "source-over", colorTransform: "white", exact: true };
1951
+ case R2_NOP:
1952
+ return { gco: "source-over", colorTransform: "skip", exact: true };
1953
+ case R2_NOTCOPYPEN:
1954
+ return { gco: "source-over", colorTransform: "invert", exact: true };
1955
+ case R2_NOT:
1956
+ return { gco: "difference", colorTransform: "white", exact: true };
1772
1957
  case R2_XORPEN:
1773
- return "xor";
1958
+ case R2_MASKPENNOT:
1959
+ return { gco: "difference", colorTransform: "none", exact: false };
1960
+ case R2_NOTXORPEN:
1961
+ return { gco: "difference", colorTransform: "invert", exact: false };
1774
1962
  case R2_MASKPEN:
1775
- return "multiply";
1963
+ return { gco: "darken", colorTransform: "none", exact: false };
1964
+ case R2_MASKNOTPEN:
1965
+ case R2_NOTMERGEPEN:
1966
+ return { gco: "darken", colorTransform: "invert", exact: false };
1776
1967
  case R2_MERGEPEN:
1777
- return "lighten";
1778
- case R2_NOT:
1779
- return "difference";
1968
+ case R2_MERGEPENNOT:
1969
+ return { gco: "lighten", colorTransform: "none", exact: false };
1970
+ case R2_MERGENOTPEN:
1971
+ case R2_NOTMASKPEN:
1972
+ return { gco: "lighten", colorTransform: "invert", exact: false };
1780
1973
  default:
1781
- return "source-over";
1974
+ return { gco: "source-over", colorTransform: "none", exact: true };
1975
+ }
1976
+ }
1977
+ function rop2TransformColor(color2, transform) {
1978
+ switch (transform) {
1979
+ case "invert":
1980
+ return invertCssColor(color2);
1981
+ case "black":
1982
+ return "#000000";
1983
+ case "white":
1984
+ return "#ffffff";
1985
+ case "skip":
1986
+ return "rgba(0,0,0,0)";
1987
+ default:
1988
+ return color2;
1782
1989
  }
1783
1990
  }
1784
1991
  function applyPen(ctx, state) {
1785
- ctx.globalCompositeOperation = rop2ToGco(state.rop2);
1992
+ const paint = rop2Paint(state.rop2);
1993
+ ctx.globalCompositeOperation = paint.gco;
1786
1994
  if (state.penStyle === 5) {
1787
1995
  ctx.strokeStyle = "rgba(0,0,0,0)";
1788
1996
  ctx.lineWidth = 0;
1789
1997
  return;
1790
1998
  }
1791
- ctx.strokeStyle = state.penColor;
1999
+ ctx.strokeStyle = rop2TransformColor(state.penColor, paint.colorTransform);
1792
2000
  ctx.lineWidth = Math.max(state.penWidth, 1);
1793
2001
  switch (state.penStyle) {
1794
2002
  case 1:
@@ -1809,12 +2017,13 @@ function applyPen(ctx, state) {
1809
2017
  }
1810
2018
  }
1811
2019
  function applyBrush(ctx, state) {
1812
- ctx.globalCompositeOperation = rop2ToGco(state.rop2);
2020
+ const paint = rop2Paint(state.rop2);
2021
+ ctx.globalCompositeOperation = paint.gco;
1813
2022
  if (state.brushStyle === 1) {
1814
2023
  ctx.fillStyle = "rgba(0,0,0,0)";
1815
2024
  return;
1816
2025
  }
1817
- ctx.fillStyle = state.brushColor;
2026
+ ctx.fillStyle = rop2TransformColor(state.brushColor, paint.colorTransform);
1818
2027
  }
1819
2028
  function cssFontWeight(weight) {
1820
2029
  if (!weight || weight === 400) {
@@ -2045,46 +2254,35 @@ function parseWmfHeader(view) {
2045
2254
  unitsPerInch
2046
2255
  };
2047
2256
  }
2048
- function colorRefToHex(r, g, b) {
2049
- const toHex3 = (v) => v.toString(16).padStart(2, "0");
2050
- return `#${toHex3(r & 255)}${toHex3(g & 255)}${toHex3(b & 255)}`;
2051
- }
2052
- function readColorRef(view, offset) {
2053
- const r = view.getUint8(offset);
2054
- const g = view.getUint8(offset + 1);
2055
- const b = view.getUint8(offset + 2);
2056
- return colorRefToHex(r, g, b);
2057
- }
2058
- function argbToRgba(argb) {
2059
- const a = (argb >>> 24 & 255) / 255;
2060
- const r = argb >>> 16 & 255;
2061
- const g = argb >>> 8 & 255;
2062
- const b = argb & 255;
2063
- return `rgba(${r},${g},${b},${a.toFixed(3)})`;
2064
- }
2065
2257
  function gmx(r, x) {
2258
+ const wt = r.state.worldTransform;
2259
+ const px2 = wt[0] * x + wt[4];
2066
2260
  if (r.useMappingMode) {
2067
- return (x - r.windowOrg.x) / (r.windowExt.cx || 1) * (r.viewportExt.cx || 1) + r.viewportOrg.x;
2261
+ return (px2 - r.windowOrg.x) / (r.windowExt.cx || 1) * (r.viewportExt.cx || 1) + r.viewportOrg.x;
2068
2262
  }
2069
- return (x - r.bounds.left) * r.sx;
2263
+ return (px2 - r.bounds.left) * r.sx;
2070
2264
  }
2071
2265
  function gmy(r, y) {
2266
+ const wt = r.state.worldTransform;
2267
+ const py = wt[3] * y + wt[5];
2072
2268
  if (r.useMappingMode) {
2073
- return (y - r.windowOrg.y) / (r.windowExt.cy || 1) * (r.viewportExt.cy || 1) + r.viewportOrg.y;
2269
+ return (py - r.windowOrg.y) / (r.windowExt.cy || 1) * (r.viewportExt.cy || 1) + r.viewportOrg.y;
2074
2270
  }
2075
- return (y - r.bounds.top) * r.sy;
2271
+ return (py - r.bounds.top) * r.sy;
2076
2272
  }
2077
2273
  function gmw(r, w) {
2274
+ const pw = r.state.worldTransform[0] * w;
2078
2275
  if (r.useMappingMode) {
2079
- return w / (r.windowExt.cx || 1) * (r.viewportExt.cx || 1);
2276
+ return pw / (r.windowExt.cx || 1) * (r.viewportExt.cx || 1);
2080
2277
  }
2081
- return w * r.sx;
2278
+ return pw * r.sx;
2082
2279
  }
2083
2280
  function gmh(r, h) {
2281
+ const ph = r.state.worldTransform[3] * h;
2084
2282
  if (r.useMappingMode) {
2085
- return h / (r.windowExt.cy || 1) * (r.viewportExt.cy || 1);
2283
+ return ph / (r.windowExt.cy || 1) * (r.viewportExt.cy || 1);
2086
2284
  }
2087
- return h * r.sy;
2285
+ return ph * r.sy;
2088
2286
  }
2089
2287
  function activateGdiMappingMode(r) {
2090
2288
  r.useMappingMode = true;
@@ -2285,6 +2483,216 @@ function handleEmfGdiShapeRecord(rCtx, recType, dataOff, recSize) {
2285
2483
  return false;
2286
2484
  }
2287
2485
  }
2486
+ var CLIP_HUGE = 1 << 24;
2487
+ function rectClipShape(x, y, w, h) {
2488
+ return { cmds: [{ op: "rect", x, y, w, h }], fillRule: "nonzero", simple: true };
2489
+ }
2490
+ function rectsClipShape(rects) {
2491
+ return {
2492
+ cmds: rects.map((r) => ({ op: "rect", x: r.x, y: r.y, w: r.w, h: r.h })),
2493
+ fillRule: "nonzero",
2494
+ simple: true
2495
+ };
2496
+ }
2497
+ function emptyClipShape() {
2498
+ return { cmds: [{ op: "rect", x: 0, y: 0, w: 0, h: 0 }], fillRule: "nonzero", simple: true };
2499
+ }
2500
+ function translateClipShape(shape, dx, dy) {
2501
+ return {
2502
+ ...shape,
2503
+ cmds: shape.cmds.map((c) => {
2504
+ switch (c.op) {
2505
+ case "rect":
2506
+ return { ...c, x: c.x + dx, y: c.y + dy };
2507
+ case "moveTo":
2508
+ case "lineTo":
2509
+ return { ...c, x: c.x + dx, y: c.y + dy };
2510
+ case "bezierCurveTo":
2511
+ return {
2512
+ ...c,
2513
+ cp1x: c.cp1x + dx,
2514
+ cp1y: c.cp1y + dy,
2515
+ cp2x: c.cp2x + dx,
2516
+ cp2y: c.cp2y + dy,
2517
+ x: c.x + dx,
2518
+ y: c.y + dy
2519
+ };
2520
+ case "closePath":
2521
+ return c;
2522
+ }
2523
+ })
2524
+ };
2525
+ }
2526
+ function translateClipRegion(region, dx, dy) {
2527
+ if (!region) {
2528
+ return null;
2529
+ }
2530
+ return region.map((s) => translateClipShape(s, dx, dy));
2531
+ }
2532
+ function isComposable(shape) {
2533
+ return shape.simple && shape.fillRule === "nonzero";
2534
+ }
2535
+ function invertClipShape(shape) {
2536
+ return {
2537
+ cmds: [
2538
+ { op: "rect", x: -CLIP_HUGE, y: -CLIP_HUGE, w: 2 * CLIP_HUGE, h: 2 * CLIP_HUGE },
2539
+ ...shape.cmds
2540
+ ],
2541
+ fillRule: "evenodd",
2542
+ simple: false
2543
+ };
2544
+ }
2545
+ function combineClip(current, shape, op) {
2546
+ switch (op) {
2547
+ case "replace":
2548
+ return { region: [shape], exact: true };
2549
+ case "intersect":
2550
+ return { region: current ? [...current, shape] : [shape], exact: true };
2551
+ case "exclude": {
2552
+ if (!isComposable(shape)) {
2553
+ return { region: current ? [...current, shape] : [shape], exact: false };
2554
+ }
2555
+ const inv = invertClipShape(shape);
2556
+ return { region: current ? [...current, inv] : [inv], exact: true };
2557
+ }
2558
+ case "union": {
2559
+ if (!current) {
2560
+ return { region: null, exact: true };
2561
+ }
2562
+ if (current.length === 1 && isComposable(current[0]) && isComposable(shape)) {
2563
+ return {
2564
+ region: [
2565
+ { cmds: [...current[0].cmds, ...shape.cmds], fillRule: "nonzero", simple: false }
2566
+ ],
2567
+ exact: false
2568
+ };
2569
+ }
2570
+ return { region: current, exact: false };
2571
+ }
2572
+ case "xor": {
2573
+ if (!current) {
2574
+ if (isComposable(shape)) {
2575
+ return { region: [invertClipShape(shape)], exact: true };
2576
+ }
2577
+ return { region: [shape], exact: false };
2578
+ }
2579
+ if (current.length === 1 && isComposable(current[0]) && isComposable(shape)) {
2580
+ return {
2581
+ region: [
2582
+ { cmds: [...current[0].cmds, ...shape.cmds], fillRule: "evenodd", simple: false }
2583
+ ],
2584
+ exact: true
2585
+ };
2586
+ }
2587
+ if (isComposable(shape)) {
2588
+ return { region: [...current, invertClipShape(shape)], exact: false };
2589
+ }
2590
+ return { region: [...current, shape], exact: false };
2591
+ }
2592
+ case "complement": {
2593
+ if (!current) {
2594
+ return { region: [emptyClipShape()], exact: true };
2595
+ }
2596
+ if (current.length === 1 && isComposable(current[0])) {
2597
+ return { region: [shape, invertClipShape(current[0])], exact: true };
2598
+ }
2599
+ return { region: [shape], exact: false };
2600
+ }
2601
+ }
2602
+ }
2603
+ function combineClipRegions(current, incoming, op) {
2604
+ if (op === "replace") {
2605
+ return { region: incoming, exact: true };
2606
+ }
2607
+ if (incoming && incoming.length === 1) {
2608
+ return combineClip(current, incoming[0], op);
2609
+ }
2610
+ if (!incoming) {
2611
+ switch (op) {
2612
+ case "intersect":
2613
+ return { region: current, exact: true };
2614
+ case "union":
2615
+ return { region: null, exact: true };
2616
+ case "exclude":
2617
+ return { region: [emptyClipShape()], exact: true };
2618
+ case "xor":
2619
+ case "complement": {
2620
+ if (!current) {
2621
+ return { region: [emptyClipShape()], exact: true };
2622
+ }
2623
+ if (current.length === 1) {
2624
+ return combineClip(null, current[0], "exclude");
2625
+ }
2626
+ return { region: current, exact: false };
2627
+ }
2628
+ }
2629
+ }
2630
+ switch (op) {
2631
+ case "intersect":
2632
+ return { region: current ? [...current, ...incoming] : incoming, exact: true };
2633
+ case "union":
2634
+ return { region: current, exact: false };
2635
+ case "xor":
2636
+ return { region: current ?? incoming, exact: false };
2637
+ case "exclude":
2638
+ return { region: current, exact: false };
2639
+ case "complement": {
2640
+ if (!current) {
2641
+ return { region: [emptyClipShape()], exact: true };
2642
+ }
2643
+ if (current.length === 1) {
2644
+ return combineClip(incoming, current[0], "exclude");
2645
+ }
2646
+ return { region: incoming, exact: false };
2647
+ }
2648
+ }
2649
+ }
2650
+ function replayClipCmds(ctx, cmds) {
2651
+ for (const c of cmds) {
2652
+ switch (c.op) {
2653
+ case "rect":
2654
+ ctx.rect(c.x, c.y, c.w, c.h);
2655
+ break;
2656
+ case "moveTo":
2657
+ ctx.moveTo(c.x, c.y);
2658
+ break;
2659
+ case "lineTo":
2660
+ ctx.lineTo(c.x, c.y);
2661
+ break;
2662
+ case "bezierCurveTo":
2663
+ ctx.bezierCurveTo(c.cp1x, c.cp1y, c.cp2x, c.cp2y, c.x, c.y);
2664
+ break;
2665
+ case "closePath":
2666
+ ctx.closePath();
2667
+ break;
2668
+ }
2669
+ }
2670
+ }
2671
+ function applyClipShapes(ctx, shapes) {
2672
+ for (const s of shapes) {
2673
+ ctx.beginPath();
2674
+ replayClipCmds(ctx, s.cmds);
2675
+ try {
2676
+ ctx.clip(s.fillRule);
2677
+ } catch {
2678
+ }
2679
+ }
2680
+ }
2681
+ function reapplyClipRegion(holder, region, identityTransform = false) {
2682
+ const { ctx } = holder;
2683
+ while (holder.clipSaveDepth > 0) {
2684
+ ctx.restore();
2685
+ holder.clipSaveDepth--;
2686
+ }
2687
+ if (region) {
2688
+ ctx.save();
2689
+ holder.clipSaveDepth = 1;
2690
+ if (identityTransform) {
2691
+ ctx.setTransform(1, 0, 0, 1, 0, 0);
2692
+ }
2693
+ applyClipShapes(ctx, region);
2694
+ }
2695
+ }
2288
2696
  function decodeRleBitmap(view, bitsOffset, bitsSize, width, height, _topDown, isRle4, colorTable, out, setPixel) {
2289
2697
  let x = 0;
2290
2698
  let y = height - 1;
@@ -2705,86 +3113,134 @@ function handleStretchDibits(rCtx, offset, dataOff, recSize) {
2705
3113
  }
2706
3114
  return true;
2707
3115
  }
3116
+ function gdiCombineClip(rCtx, shape, op) {
3117
+ const { ctx } = rCtx;
3118
+ if (rCtx.clipUntracked && op !== "replace") {
3119
+ switch (op) {
3120
+ case "intersect":
3121
+ case "complement": {
3122
+ ctx.save();
3123
+ rCtx.clipSaveDepth++;
3124
+ applyClipShapes(ctx, [shape]);
3125
+ return;
3126
+ }
3127
+ case "exclude":
3128
+ case "xor": {
3129
+ const inv = combineClip(null, shape, "exclude");
3130
+ ctx.save();
3131
+ rCtx.clipSaveDepth++;
3132
+ applyClipShapes(ctx, inv.region ?? [shape]);
3133
+ return;
3134
+ }
3135
+ case "union":
3136
+ return;
3137
+ }
3138
+ }
3139
+ const res = combineClip(op === "replace" ? null : rCtx.clipRegion ?? null, shape, op);
3140
+ if (!res.exact) ;
3141
+ rCtx.clipRegion = res.region;
3142
+ rCtx.clipUntracked = false;
3143
+ reapplyClipRegion(rCtx, res.region);
3144
+ }
3145
+ function readClipRectShape(rCtx, dataOff) {
3146
+ const { view } = rCtx;
3147
+ const left = view.getInt32(dataOff, true);
3148
+ const top = view.getInt32(dataOff + 4, true);
3149
+ const right = view.getInt32(dataOff + 8, true);
3150
+ const bottom = view.getInt32(dataOff + 12, true);
3151
+ return rectClipShape(
3152
+ gmx(rCtx, left),
3153
+ gmy(rCtx, top),
3154
+ gmw(rCtx, right - left),
3155
+ gmh(rCtx, bottom - top)
3156
+ );
3157
+ }
2708
3158
  function handleIntersectClipRect(rCtx, dataOff, recSize) {
2709
- const { ctx, view } = rCtx;
2710
3159
  if (recSize >= 24) {
2711
- const left = view.getInt32(dataOff, true);
2712
- const top = view.getInt32(dataOff + 4, true);
2713
- const right = view.getInt32(dataOff + 8, true);
2714
- const bottom = view.getInt32(dataOff + 12, true);
2715
- ctx.save();
2716
- rCtx.clipSaveDepth++;
2717
- ctx.beginPath();
2718
- ctx.rect(gmx(rCtx, left), gmy(rCtx, top), gmw(rCtx, right - left), gmh(rCtx, bottom - top));
2719
- try {
2720
- ctx.clip();
2721
- } catch {
2722
- }
3160
+ gdiCombineClip(rCtx, readClipRectShape(rCtx, dataOff), "intersect");
2723
3161
  }
2724
3162
  return true;
2725
3163
  }
3164
+ function handleExcludeClipRect(rCtx, dataOff, recSize) {
3165
+ if (recSize >= 24) {
3166
+ gdiCombineClip(rCtx, readClipRectShape(rCtx, dataOff), "exclude");
3167
+ }
3168
+ return true;
3169
+ }
3170
+ var RGN_MODE_OPS = {
3171
+ 1: "intersect",
3172
+ // RGN_AND
3173
+ 2: "union",
3174
+ // RGN_OR
3175
+ 3: "xor",
3176
+ // RGN_XOR
3177
+ 4: "exclude",
3178
+ // RGN_DIFF
3179
+ 5: "replace"
3180
+ // RGN_COPY
3181
+ };
2726
3182
  function handleExtSelectClipRgn(rCtx, dataOff, recSize) {
2727
- const { ctx, view } = rCtx;
3183
+ const { view } = rCtx;
2728
3184
  if (recSize < 16) {
2729
3185
  return true;
2730
3186
  }
2731
3187
  const cbRgnData = view.getUint32(dataOff, true);
2732
3188
  const iMode = view.getUint32(dataOff + 4, true);
2733
- if (iMode === 5) {
2734
- if (cbRgnData === 0) {
2735
- while (rCtx.clipSaveDepth > 0) {
2736
- ctx.restore();
2737
- rCtx.clipSaveDepth--;
2738
- }
2739
- return true;
2740
- }
2741
- const rgnStart = dataOff + 8;
2742
- if (cbRgnData < 32) {
2743
- return true;
2744
- }
2745
- const nCount = view.getUint32(rgnStart + 8, true);
2746
- if (nCount === 0) {
2747
- return true;
2748
- }
2749
- while (rCtx.clipSaveDepth > 0) {
2750
- ctx.restore();
2751
- rCtx.clipSaveDepth--;
2752
- }
2753
- ctx.save();
2754
- rCtx.clipSaveDepth++;
2755
- ctx.beginPath();
2756
- const rectsStart = rgnStart + 32;
2757
- for (let i = 0; i < nCount; i++) {
2758
- const rOff = rectsStart + i * 16;
2759
- if (rOff + 16 > dataOff + 8 + cbRgnData) {
2760
- break;
2761
- }
2762
- const left = view.getInt32(rOff, true);
2763
- const top = view.getInt32(rOff + 4, true);
2764
- const right = view.getInt32(rOff + 8, true);
2765
- const bottom = view.getInt32(rOff + 12, true);
2766
- ctx.rect(gmx(rCtx, left), gmy(rCtx, top), gmw(rCtx, right - left), gmh(rCtx, bottom - top));
3189
+ const op = RGN_MODE_OPS[iMode];
3190
+ if (!op) {
3191
+ return true;
3192
+ }
3193
+ if (cbRgnData === 0) {
3194
+ if (op === "replace") {
3195
+ rCtx.clipRegion = null;
3196
+ rCtx.clipUntracked = false;
3197
+ reapplyClipRegion(rCtx, null);
2767
3198
  }
2768
- try {
2769
- ctx.clip();
2770
- } catch {
3199
+ return true;
3200
+ }
3201
+ const rgnStart = dataOff + 8;
3202
+ if (cbRgnData < 32) {
3203
+ return true;
3204
+ }
3205
+ const nCount = view.getUint32(rgnStart + 8, true);
3206
+ if (nCount === 0) {
3207
+ return true;
3208
+ }
3209
+ const rects = [];
3210
+ const rectsStart = rgnStart + 32;
3211
+ for (let i = 0; i < nCount; i++) {
3212
+ const rOff = rectsStart + i * 16;
3213
+ if (rOff + 16 > dataOff + 8 + cbRgnData) {
3214
+ break;
2771
3215
  }
3216
+ const left = view.getInt32(rOff, true);
3217
+ const top = view.getInt32(rOff + 4, true);
3218
+ const right = view.getInt32(rOff + 8, true);
3219
+ const bottom = view.getInt32(rOff + 12, true);
3220
+ rects.push({
3221
+ x: gmx(rCtx, left),
3222
+ y: gmy(rCtx, top),
3223
+ w: gmw(rCtx, right - left),
3224
+ h: gmh(rCtx, bottom - top)
3225
+ });
2772
3226
  }
2773
- return true;
2774
- }
2775
- function handleExcludeClipRect(rCtx, dataOff, recSize) {
2776
- if (recSize >= 24) {
2777
- rCtx.view.getInt32(dataOff, true);
2778
- rCtx.view.getInt32(dataOff + 4, true);
2779
- rCtx.view.getInt32(dataOff + 8, true);
2780
- rCtx.view.getInt32(dataOff + 12, true);
3227
+ if (rects.length === 0) {
3228
+ return true;
2781
3229
  }
3230
+ gdiCombineClip(rCtx, rectsClipShape(rects), op);
2782
3231
  return true;
2783
3232
  }
2784
3233
  function handleOffsetClipRgn(rCtx, dataOff, recSize) {
2785
3234
  if (recSize >= 16) {
2786
- rCtx.view.getInt32(dataOff, true);
2787
- rCtx.view.getInt32(dataOff + 4, true);
3235
+ const dx = rCtx.view.getInt32(dataOff, true);
3236
+ const dy = rCtx.view.getInt32(dataOff + 4, true);
3237
+ if (rCtx.clipUntracked) {
3238
+ return true;
3239
+ }
3240
+ if (rCtx.clipRegion) {
3241
+ rCtx.clipRegion = translateClipRegion(rCtx.clipRegion, gmw(rCtx, dx), gmh(rCtx, dy));
3242
+ reapplyClipRegion(rCtx, rCtx.clipRegion);
3243
+ }
2788
3244
  }
2789
3245
  return true;
2790
3246
  }
@@ -3101,14 +3557,12 @@ function handleEmfGdiPolyPathRecord(rCtx, recType, offset, dataOff, recSize) {
3101
3557
  ctx.restore();
3102
3558
  rCtx.clipSaveDepth--;
3103
3559
  }
3104
- ctx.save();
3105
- rCtx.clipSaveDepth++;
3106
- ctx.clip();
3107
- } else {
3108
- ctx.save();
3109
- rCtx.clipSaveDepth++;
3110
- ctx.clip();
3111
3560
  }
3561
+ ctx.save();
3562
+ rCtx.clipSaveDepth++;
3563
+ ctx.clip(state.polyFillMode === 2 ? "nonzero" : "evenodd");
3564
+ rCtx.clipRegion = null;
3565
+ rCtx.clipUntracked = true;
3112
3566
  } catch {
3113
3567
  }
3114
3568
  return true;
@@ -3398,7 +3852,9 @@ function createEmfPlusState() {
3398
3852
  objectTable: /* @__PURE__ */ new Map(),
3399
3853
  worldTransform: [1, 0, 0, 1, 0, 0],
3400
3854
  saveStack: [],
3401
- saveIdMap: /* @__PURE__ */ new Map()
3855
+ saveIdMap: /* @__PURE__ */ new Map(),
3856
+ clipRegion: null,
3857
+ clipSaveDepth: 0
3402
3858
  };
3403
3859
  }
3404
3860
  function handleEmfGdiStateRecord(rCtx, recType, _offset, dataOff, recSize) {
@@ -3416,8 +3872,19 @@ function handleEmfGdiStateRecord(rCtx, recType, _offset, dataOff, recSize) {
3416
3872
  ctx.restore();
3417
3873
  rCtx.clipSaveDepth--;
3418
3874
  }
3875
+ rCtx.clipStack ?? (rCtx.clipStack = []);
3876
+ rCtx.clipStack.push({
3877
+ region: rCtx.clipRegion ?? null,
3878
+ untracked: rCtx.clipUntracked ?? false
3879
+ });
3419
3880
  rCtx.stateStack.push(cloneState(state));
3420
3881
  ctx.save();
3882
+ if (rCtx.clipUntracked) {
3883
+ rCtx.clipUntracked = false;
3884
+ rCtx.clipRegion = null;
3885
+ } else if (rCtx.clipRegion) {
3886
+ reapplyClipRegion(rCtx, rCtx.clipRegion);
3887
+ }
3421
3888
  return true;
3422
3889
  }
3423
3890
  case EMR_RESTOREDC: {
@@ -3428,16 +3895,23 @@ function handleEmfGdiStateRecord(rCtx, recType, _offset, dataOff, recSize) {
3428
3895
  }
3429
3896
  let rel = view.getInt32(dataOff, true);
3430
3897
  if (rel < 0) {
3431
- rel = rCtx.stateStack.length + rel;
3898
+ rel = rCtx.stateStack.length + rel + 1;
3432
3899
  }
3433
3900
  while (rCtx.stateStack.length > rel && rCtx.stateStack.length > 0) {
3434
3901
  rCtx.stateStack.pop();
3902
+ rCtx.clipStack?.pop();
3435
3903
  ctx.restore();
3436
3904
  }
3437
3905
  const restored = rCtx.stateStack.pop();
3438
3906
  if (restored) {
3907
+ const clipSnapshot = rCtx.clipStack?.pop();
3439
3908
  Object.assign(state, restored);
3440
3909
  ctx.restore();
3910
+ rCtx.clipRegion = clipSnapshot?.region ?? null;
3911
+ rCtx.clipUntracked = false;
3912
+ if (rCtx.clipRegion) {
3913
+ reapplyClipRegion(rCtx, rCtx.clipRegion);
3914
+ }
3441
3915
  }
3442
3916
  }
3443
3917
  return true;
@@ -3552,6 +4026,47 @@ function parseEmfPlusPath(data, off, maxLen) {
3552
4026
  const types = new Uint8Array(data.buffer, data.byteOffset + alignedPOff, pointCount);
3553
4027
  return { kind: "plus-path", points: points3, types: new Uint8Array(types) };
3554
4028
  }
4029
+ function emfPlusPathToClipCmds(path, m) {
4030
+ const tx = (x, y) => m[0] * x + m[2] * y + m[4];
4031
+ const ty = (x, y) => m[1] * x + m[3] * y + m[5];
4032
+ const cmds = [];
4033
+ const pts = path.points;
4034
+ const types = path.types;
4035
+ let i = 0;
4036
+ while (i < pts.length) {
4037
+ const t = types[i] & 15;
4038
+ const close = (types[i] & 128) !== 0;
4039
+ if (t === 0) {
4040
+ cmds.push({ op: "moveTo", x: tx(pts[i].x, pts[i].y), y: ty(pts[i].x, pts[i].y) });
4041
+ i++;
4042
+ } else if (t === 3) {
4043
+ if (i + 2 < pts.length) {
4044
+ cmds.push({
4045
+ op: "bezierCurveTo",
4046
+ cp1x: tx(pts[i].x, pts[i].y),
4047
+ cp1y: ty(pts[i].x, pts[i].y),
4048
+ cp2x: tx(pts[i + 1].x, pts[i + 1].y),
4049
+ cp2y: ty(pts[i + 1].x, pts[i + 1].y),
4050
+ x: tx(pts[i + 2].x, pts[i + 2].y),
4051
+ y: ty(pts[i + 2].x, pts[i + 2].y)
4052
+ });
4053
+ if ((types[i + 2] & 128) !== 0) {
4054
+ cmds.push({ op: "closePath" });
4055
+ }
4056
+ i += 3;
4057
+ continue;
4058
+ }
4059
+ break;
4060
+ } else {
4061
+ cmds.push({ op: "lineTo", x: tx(pts[i].x, pts[i].y), y: ty(pts[i].x, pts[i].y) });
4062
+ i++;
4063
+ }
4064
+ if (close) {
4065
+ cmds.push({ op: "closePath" });
4066
+ }
4067
+ }
4068
+ return cmds;
4069
+ }
3555
4070
  function replayEmfPlusPath(ctx, path) {
3556
4071
  ctx.beginPath();
3557
4072
  const pts = path.points;
@@ -3603,12 +4118,44 @@ function multiplyMatrix(m1, m2) {
3603
4118
  m1[4] * m2[1] + m1[5] * m2[3] + m2[5]
3604
4119
  ];
3605
4120
  }
3606
- function resolveBrushColor(rCtx, flags, brushIdOrColor) {
4121
+ function createBrushGradient(rCtx, grad) {
4122
+ const ctx = rCtx.ctx;
4123
+ try {
4124
+ let g = null;
4125
+ if (grad.type === "linear" && typeof ctx.createLinearGradient === "function") {
4126
+ if (grad.x1 === grad.x2 && grad.y1 === grad.y2) {
4127
+ return null;
4128
+ }
4129
+ g = ctx.createLinearGradient(grad.x1, grad.y1, grad.x2, grad.y2);
4130
+ } else if (grad.type === "radial" && typeof ctx.createRadialGradient === "function") {
4131
+ if (!(grad.r > 0)) {
4132
+ return null;
4133
+ }
4134
+ g = ctx.createRadialGradient(grad.cx, grad.cy, 0, grad.cx, grad.cy, grad.r);
4135
+ }
4136
+ if (!g) {
4137
+ return null;
4138
+ }
4139
+ for (const stop of grad.stops) {
4140
+ g.addColorStop(stop.offset, stop.color);
4141
+ }
4142
+ return g;
4143
+ } catch {
4144
+ return null;
4145
+ }
4146
+ }
4147
+ function resolveBrushPaint(rCtx, flags, brushIdOrColor) {
3607
4148
  if (flags & 32768) {
3608
4149
  return argbToRgba(brushIdOrColor);
3609
4150
  }
3610
4151
  const obj = rCtx.objectTable.get(brushIdOrColor & 255);
3611
4152
  if (obj && obj.kind === "plus-brush") {
4153
+ if (obj.gradient) {
4154
+ const g = createBrushGradient(rCtx, obj.gradient);
4155
+ if (g) {
4156
+ return g;
4157
+ }
4158
+ }
3612
4159
  return obj.color;
3613
4160
  }
3614
4161
  return "rgba(0,0,0,1)";
@@ -3672,88 +4219,83 @@ function popState(rCtx, stackId) {
3672
4219
  rCtx.saveIdMap = newMap;
3673
4220
  }
3674
4221
  }
3675
- function ensureClipSave(rCtx) {
3676
- if (rCtx.clipSaveDepth === 0) {
3677
- rCtx.ctx.save();
3678
- rCtx.clipSaveDepth = 1;
3679
- }
3680
- }
3681
- function resetClipState(rCtx) {
3682
- if (rCtx.clipSaveDepth > 0) {
3683
- rCtx.ctx.restore();
3684
- rCtx.clipSaveDepth = 0;
3685
- }
3686
- }
3687
- function applyClipCombineMode(rCtx, combineMode, opName, clipFn) {
3688
- switch (combineMode) {
3689
- case 0:
3690
- resetClipState(rCtx);
3691
- ensureClipSave(rCtx);
3692
- clipFn();
3693
- return true;
3694
- case 1:
3695
- ensureClipSave(rCtx);
3696
- clipFn();
3697
- return true;
3698
- case 2:
3699
- ensureClipSave(rCtx);
3700
- clipFn();
3701
- return true;
3702
- case 3:
3703
- return false;
3704
- case 4:
3705
- return false;
3706
- case 5:
3707
- return false;
3708
- default:
3709
- ensureClipSave(rCtx);
3710
- clipFn();
3711
- return true;
3712
- }
3713
- }
3714
- var MAX_REGION_TRACE_DEPTH = 64;
3715
- function traceRegionNodePath(ctx, node, depth = 0) {
3716
- if (depth > MAX_REGION_TRACE_DEPTH) {
3717
- ctx.rect(0, 0, 0, 0);
3718
- return;
4222
+ function plusDeviceMatrix(rCtx) {
4223
+ const wt = rCtx.worldTransform;
4224
+ const s = getPageUnitMultiplier(rCtx.pageUnit, rCtx.pageScale) * rCtx.dpiScale;
4225
+ return [wt[0] * s, wt[1] * s, wt[2] * s, wt[3] * s, wt[4] * s, wt[5] * s];
4226
+ }
4227
+ function transformedRectShape(x, y, w, h, m) {
4228
+ const tx = (px2, py) => m[0] * px2 + m[2] * py + m[4];
4229
+ const ty = (px2, py) => m[1] * px2 + m[3] * py + m[5];
4230
+ const cmds = [
4231
+ { op: "moveTo", x: tx(x, y), y: ty(x, y) },
4232
+ { op: "lineTo", x: tx(x + w, y), y: ty(x + w, y) },
4233
+ { op: "lineTo", x: tx(x + w, y + h), y: ty(x + w, y + h) },
4234
+ { op: "lineTo", x: tx(x, y + h), y: ty(x, y + h) },
4235
+ { op: "closePath" }
4236
+ ];
4237
+ return { cmds, fillRule: "nonzero", simple: true };
4238
+ }
4239
+ function pathClipShape(path, m) {
4240
+ return { cmds: emfPlusPathToClipCmds(path.path, m), fillRule: "nonzero", simple: true };
4241
+ }
4242
+ var REGION_NODE_OPS = {
4243
+ 0: "intersect",
4244
+ // legacy/lenient: treat 0 as And
4245
+ 1: "intersect",
4246
+ // RegionNodeDataTypeAnd
4247
+ 2: "union",
4248
+ // RegionNodeDataTypeOr
4249
+ 3: "xor",
4250
+ // RegionNodeDataTypeXor
4251
+ 4: "exclude",
4252
+ // RegionNodeDataTypeExclude
4253
+ 5: "complement"
4254
+ // RegionNodeDataTypeComplement
4255
+ };
4256
+ var MAX_REGION_FLATTEN_DEPTH = 64;
4257
+ function flattenRegionNode(node, m, depth = 0) {
4258
+ if (depth > MAX_REGION_FLATTEN_DEPTH) {
4259
+ return { region: [emptyClipShape()], exact: false };
3719
4260
  }
3720
4261
  switch (node.type) {
3721
4262
  case "rect":
3722
- ctx.rect(node.x, node.y, node.width, node.height);
3723
- break;
4263
+ return { region: [transformedRectShape(node.x, node.y, node.width, node.height, m)], exact: true };
3724
4264
  case "path":
3725
- replayEmfPlusPath(ctx, node.path);
3726
- break;
4265
+ return { region: [pathClipShape(node, m)], exact: true };
3727
4266
  case "infinite":
3728
- ctx.rect(-1e6, -1e6, 2e6, 2e6);
3729
- break;
4267
+ return { region: null, exact: true };
3730
4268
  case "empty":
3731
- ctx.rect(0, 0, 0, 0);
3732
- break;
3733
- case "combine":
3734
- traceRegionNodePath(ctx, node.left, depth + 1);
3735
- if (node.combineMode !== 0) {
3736
- emfWarn(
3737
- `traceRegionNodePath: combine mode ${node.combineMode} not fully supported, using left subtree only`
3738
- );
3739
- } else {
3740
- try {
3741
- ctx.clip();
3742
- } catch {
3743
- }
3744
- ctx.beginPath();
3745
- traceRegionNodePath(ctx, node.right, depth + 1);
3746
- }
3747
- break;
3748
- }
4269
+ return { region: [emptyClipShape()], exact: true };
4270
+ case "combine": {
4271
+ const left = flattenRegionNode(node.left, m, depth + 1);
4272
+ const right = flattenRegionNode(node.right, m, depth + 1);
4273
+ const op = REGION_NODE_OPS[node.combineMode] ?? "intersect";
4274
+ const combined = combineClipRegions(left.region, right.region, op);
4275
+ return { region: combined.region, exact: combined.exact && left.exact && right.exact };
4276
+ }
4277
+ }
4278
+ }
4279
+ var PLUS_COMBINE_OPS = {
4280
+ 0: "replace",
4281
+ 1: "intersect",
4282
+ 2: "union",
4283
+ 3: "xor",
4284
+ 4: "exclude",
4285
+ 5: "complement"
4286
+ };
4287
+ function reapplyPlusClip(rCtx) {
4288
+ reapplyClipRegion(rCtx, rCtx.clipRegion ?? null, true);
3749
4289
  }
3750
- function applyRegionNodeClip(ctx, rootNode, combineMode) {
3751
- ctx.beginPath();
3752
- traceRegionNodePath(ctx, rootNode);
3753
- try {
3754
- ctx.clip();
3755
- } catch {
3756
- }
4290
+ function applyPlusClipRegion(rCtx, incoming, combineMode, opName) {
4291
+ const op = PLUS_COMBINE_OPS[combineMode];
4292
+ const res = combineClipRegions(rCtx.clipRegion ?? null, incoming, op ?? "intersect");
4293
+ if (!res.exact) ;
4294
+ rCtx.clipRegion = res.region;
4295
+ reapplyPlusClip(rCtx);
4296
+ }
4297
+ function applyPlusClipShape(rCtx, shape, combineMode, opName) {
4298
+ applyPlusClipRegion(rCtx, [shape], combineMode);
3757
4299
  }
3758
4300
  function handleEmfPlusStateRecord(rCtx, recType, recFlags, dataOff, recDataSize) {
3759
4301
  const { view } = rCtx;
@@ -3855,30 +4397,24 @@ function handleEmfPlusStateRecord(rCtx, recType, recFlags, dataOff, recDataSize)
3855
4397
  const cy = view.getFloat32(dataOff + 4, true);
3856
4398
  const cw = view.getFloat32(dataOff + 8, true);
3857
4399
  const ch = view.getFloat32(dataOff + 12, true);
3858
- applyClipCombineMode(rCtx, combineMode, "SetClipRect", () => {
3859
- applyPlusWorldTransform(rCtx);
3860
- rCtx.ctx.beginPath();
3861
- rCtx.ctx.rect(cx, cy, cw, ch);
3862
- try {
3863
- rCtx.ctx.clip();
3864
- } catch {
3865
- }
3866
- });
4400
+ const shape = transformedRectShape(cx, cy, cw, ch, plusDeviceMatrix(rCtx));
4401
+ applyPlusClipShape(rCtx, shape, combineMode);
3867
4402
  }
3868
4403
  return true;
3869
4404
  }
3870
4405
  case EMFPLUS_RESETCLIP: {
3871
- resetClipState(rCtx);
3872
- ensureClipSave(rCtx);
4406
+ rCtx.clipRegion = null;
4407
+ reapplyPlusClip(rCtx);
3873
4408
  return true;
3874
4409
  }
3875
4410
  case EMFPLUS_SETCLIPREGION: {
3876
4411
  const regionId = recFlags & 255;
4412
+ const combineMode = recFlags >> 8 & 15;
3877
4413
  const regionObj = rCtx.objectTable.get(regionId);
3878
4414
  if (regionObj && regionObj.kind === "plus-region" && regionObj.nodes.length > 0) {
3879
- applyPlusWorldTransform(rCtx);
3880
- const rootNode = regionObj.nodes[0];
3881
- applyRegionNodeClip(rCtx.ctx, rootNode);
4415
+ const flattened = flattenRegionNode(regionObj.nodes[0], plusDeviceMatrix(rCtx));
4416
+ if (!flattened.exact) ;
4417
+ applyPlusClipRegion(rCtx, flattened.region, combineMode);
3882
4418
  }
3883
4419
  return true;
3884
4420
  }
@@ -3887,22 +4423,26 @@ function handleEmfPlusStateRecord(rCtx, recType, recFlags, dataOff, recDataSize)
3887
4423
  const combineMode = recFlags >> 8 & 15;
3888
4424
  const pathObj = rCtx.objectTable.get(pathId);
3889
4425
  if (pathObj && pathObj.kind === "plus-path") {
3890
- applyClipCombineMode(rCtx, combineMode, "SetClipPath", () => {
3891
- applyPlusWorldTransform(rCtx);
3892
- rCtx.ctx.beginPath();
3893
- replayEmfPlusPath(rCtx.ctx, pathObj);
3894
- try {
3895
- rCtx.ctx.clip();
3896
- } catch {
3897
- }
3898
- });
4426
+ const shape = {
4427
+ cmds: emfPlusPathToClipCmds(pathObj, plusDeviceMatrix(rCtx)),
4428
+ fillRule: "nonzero",
4429
+ simple: true
4430
+ };
4431
+ applyPlusClipShape(rCtx, shape, combineMode);
3899
4432
  }
3900
4433
  return true;
3901
4434
  }
3902
4435
  case EMFPLUS_OFFSETCLIP: {
3903
4436
  if (recDataSize >= 8) {
3904
- view.getFloat32(dataOff, true);
3905
- view.getFloat32(dataOff + 4, true);
4437
+ const dx = view.getFloat32(dataOff, true);
4438
+ const dy = view.getFloat32(dataOff + 4, true);
4439
+ if (rCtx.clipRegion) {
4440
+ const m = plusDeviceMatrix(rCtx);
4441
+ const ddx = m[0] * dx + m[2] * dy;
4442
+ const ddy = m[1] * dx + m[3] * dy;
4443
+ rCtx.clipRegion = translateClipRegion(rCtx.clipRegion, ddx, ddy);
4444
+ reapplyPlusClip(rCtx);
4445
+ }
3906
4446
  }
3907
4447
  return true;
3908
4448
  }
@@ -3973,7 +4513,7 @@ function handleEmfPlusDrawRecord(rCtx, recType, recFlags, dataOff, recDataSize)
3973
4513
  const count = view.getUint32(dataOff + 4, true);
3974
4514
  const compressed = (recFlags & 16384) !== 0;
3975
4515
  const rectSize = compressed ? 8 : 16;
3976
- ctx.fillStyle = resolveBrushColor(rCtx, recFlags, brushVal);
4516
+ ctx.fillStyle = resolveBrushPaint(rCtx, recFlags, brushVal);
3977
4517
  applyPlusWorldTransform(rCtx);
3978
4518
  let rOff = dataOff + 8;
3979
4519
  for (let i = 0; i < count && rOff + rectSize <= dataOff + recDataSize; i++) {
@@ -4023,7 +4563,7 @@ function handleEmfPlusDrawRecord(rCtx, recType, recFlags, dataOff, recDataSize)
4023
4563
  w = view.getFloat32(dataOff + 12, true);
4024
4564
  h = view.getFloat32(dataOff + 16, true);
4025
4565
  }
4026
- ctx.fillStyle = resolveBrushColor(rCtx, recFlags, brushVal);
4566
+ ctx.fillStyle = resolveBrushPaint(rCtx, recFlags, brushVal);
4027
4567
  applyPlusWorldTransform(rCtx);
4028
4568
  ctx.beginPath();
4029
4569
  ctx.ellipse(x + w / 2, y + h / 2, Math.abs(w) / 2, Math.abs(h) / 2, 0, 0, Math.PI * 2);
@@ -4069,7 +4609,7 @@ function handleEmfPlusDrawRecord(rCtx, recType, recFlags, dataOff, recDataSize)
4069
4609
  let aOff = dataOff;
4070
4610
  if (isFill) {
4071
4611
  const brushVal = view.getUint32(aOff, true);
4072
- ctx.fillStyle = resolveBrushColor(rCtx, recFlags, brushVal);
4612
+ ctx.fillStyle = resolveBrushPaint(rCtx, recFlags, brushVal);
4073
4613
  aOff += 4;
4074
4614
  }
4075
4615
  const startAngle = view.getFloat32(aOff, true) * Math.PI / 180;
@@ -4150,7 +4690,7 @@ function handleEmfPlusDrawRecord(rCtx, recType, recFlags, dataOff, recDataSize)
4150
4690
  const count = view.getUint32(dataOff + 4, true);
4151
4691
  const compressed = (recFlags & 16384) !== 0;
4152
4692
  const ptSize = compressed ? 4 : 8;
4153
- ctx.fillStyle = resolveBrushColor(rCtx, recFlags, brushVal);
4693
+ ctx.fillStyle = resolveBrushPaint(rCtx, recFlags, brushVal);
4154
4694
  applyPlusWorldTransform(rCtx);
4155
4695
  ctx.beginPath();
4156
4696
  let pOff = dataOff + 8;
@@ -4172,6 +4712,259 @@ function handleEmfPlusDrawRecord(rCtx, recType, recFlags, dataOff, recDataSize)
4172
4712
  return false;
4173
4713
  }
4174
4714
  }
4715
+ var BRUSH_DATA_PATH = 1;
4716
+ var BRUSH_DATA_TRANSFORM = 2;
4717
+ var BRUSH_DATA_PRESET_COLORS = 4;
4718
+ var BRUSH_DATA_BLEND_FACTORS_H = 8;
4719
+ var MAX_GRADIENT_ELEMENTS = 4096;
4720
+ function looksLikeGraphicsVersion(v) {
4721
+ return v >>> 12 === 900097;
4722
+ }
4723
+ function readTransform(view, off) {
4724
+ return [
4725
+ view.getFloat32(off, true),
4726
+ view.getFloat32(off + 4, true),
4727
+ view.getFloat32(off + 8, true),
4728
+ view.getFloat32(off + 12, true),
4729
+ view.getFloat32(off + 16, true),
4730
+ view.getFloat32(off + 20, true)
4731
+ ];
4732
+ }
4733
+ function applyMatrix(m, x, y) {
4734
+ return { x: m[0] * x + m[2] * y + m[4], y: m[1] * x + m[3] * y + m[5] };
4735
+ }
4736
+ function clamp01(v) {
4737
+ return Number.isFinite(v) ? Math.min(1, Math.max(0, v)) : 0;
4738
+ }
4739
+ function normaliseStops(stops) {
4740
+ return stops.map((s) => ({ offset: clamp01(s.offset), color: s.color })).sort((a, b) => a.offset - b.offset);
4741
+ }
4742
+ function readPresetColors(view, off, end) {
4743
+ if (off + 4 > end) {
4744
+ return null;
4745
+ }
4746
+ const count = view.getUint32(off, true);
4747
+ if (count === 0 || count > MAX_GRADIENT_ELEMENTS) {
4748
+ return null;
4749
+ }
4750
+ const posOff = off + 4;
4751
+ const colOff = posOff + count * 4;
4752
+ const next = colOff + count * 4;
4753
+ if (next > end) {
4754
+ return null;
4755
+ }
4756
+ const stops = [];
4757
+ for (let i = 0; i < count; i++) {
4758
+ stops.push({
4759
+ offset: view.getFloat32(posOff + i * 4, true),
4760
+ color: argbToRgba(view.getUint32(colOff + i * 4, true))
4761
+ });
4762
+ }
4763
+ return { stops, next };
4764
+ }
4765
+ function readBlendFactors(view, off, end) {
4766
+ if (off + 4 > end) {
4767
+ return null;
4768
+ }
4769
+ const count = view.getUint32(off, true);
4770
+ if (count === 0 || count > MAX_GRADIENT_ELEMENTS) {
4771
+ return null;
4772
+ }
4773
+ const posOff = off + 4;
4774
+ const facOff = posOff + count * 4;
4775
+ const next = facOff + count * 4;
4776
+ if (next > end) {
4777
+ return null;
4778
+ }
4779
+ const entries = [];
4780
+ for (let i = 0; i < count; i++) {
4781
+ entries.push({
4782
+ pos: view.getFloat32(posOff + i * 4, true),
4783
+ factor: view.getFloat32(facOff + i * 4, true)
4784
+ });
4785
+ }
4786
+ return { entries, next };
4787
+ }
4788
+ function parseLinearGradient(view, b, end) {
4789
+ if (b + 40 > end) {
4790
+ return null;
4791
+ }
4792
+ const flags = view.getUint32(b, true);
4793
+ const rx = view.getFloat32(b + 8, true);
4794
+ const ry = view.getFloat32(b + 12, true);
4795
+ const rw = view.getFloat32(b + 16, true);
4796
+ const rh = view.getFloat32(b + 20, true);
4797
+ const startArgb = view.getUint32(b + 24, true);
4798
+ const endArgb = view.getUint32(b + 28, true);
4799
+ let o = b + 40;
4800
+ let transform = null;
4801
+ if (flags & BRUSH_DATA_TRANSFORM && o + 24 <= end) {
4802
+ transform = readTransform(view, o);
4803
+ o += 24;
4804
+ }
4805
+ let stops = [
4806
+ { offset: 0, color: argbToRgba(startArgb) },
4807
+ { offset: 1, color: argbToRgba(endArgb) }
4808
+ ];
4809
+ if (flags & BRUSH_DATA_PRESET_COLORS) {
4810
+ const preset = readPresetColors(view, o, end);
4811
+ if (preset) {
4812
+ stops = preset.stops;
4813
+ }
4814
+ } else if (flags & BRUSH_DATA_BLEND_FACTORS_H) {
4815
+ const blend = readBlendFactors(view, o, end);
4816
+ if (blend) {
4817
+ stops = blend.entries.map((e) => ({
4818
+ offset: e.pos,
4819
+ color: lerpArgbToRgba(startArgb, endArgb, e.factor)
4820
+ }));
4821
+ }
4822
+ }
4823
+ let p1 = { x: rx, y: ry + rh / 2 };
4824
+ let p2 = { x: rx + rw, y: ry + rh / 2 };
4825
+ if (transform) {
4826
+ p1 = applyMatrix(transform, p1.x, p1.y);
4827
+ p2 = applyMatrix(transform, p2.x, p2.y);
4828
+ }
4829
+ emfLog(
4830
+ `parseEmfPlusBrushObject: linear gradient (${p1.x.toFixed(1)},${p1.y.toFixed(1)})\u2192(${p2.x.toFixed(1)},${p2.y.toFixed(1)}), ${stops.length} stop(s)`
4831
+ );
4832
+ return {
4833
+ kind: "plus-brush",
4834
+ color: argbToRgba(startArgb),
4835
+ gradient: {
4836
+ type: "linear",
4837
+ x1: p1.x,
4838
+ y1: p1.y,
4839
+ x2: p2.x,
4840
+ y2: p2.y,
4841
+ stops: normaliseStops(stops)
4842
+ }
4843
+ };
4844
+ }
4845
+ function parsePathGradient(view, b, end) {
4846
+ if (b + 24 > end) {
4847
+ return null;
4848
+ }
4849
+ const flags = view.getUint32(b, true);
4850
+ const centerArgb = view.getUint32(b + 8, true);
4851
+ let cx = view.getFloat32(b + 12, true);
4852
+ let cy = view.getFloat32(b + 16, true);
4853
+ const surroundCount = view.getUint32(b + 20, true);
4854
+ if (surroundCount > MAX_GRADIENT_ELEMENTS) {
4855
+ return { kind: "plus-brush", color: argbToRgba(centerArgb) };
4856
+ }
4857
+ const surround = [];
4858
+ let o = b + 24;
4859
+ for (let i = 0; i < surroundCount && o + 4 <= end; i++) {
4860
+ surround.push(view.getUint32(o, true));
4861
+ o += 4;
4862
+ }
4863
+ let boundaryPts = [];
4864
+ if (flags & BRUSH_DATA_PATH) {
4865
+ if (o + 4 <= end) {
4866
+ const pathSize = view.getInt32(o, true);
4867
+ o += 4;
4868
+ if (pathSize > 0 && o + pathSize <= end) {
4869
+ const path = parseEmfPlusPath(view, o, pathSize);
4870
+ if (path) {
4871
+ boundaryPts = path.points;
4872
+ }
4873
+ o += pathSize;
4874
+ }
4875
+ }
4876
+ } else if (o + 4 <= end) {
4877
+ const ptCount = view.getUint32(o, true);
4878
+ o += 4;
4879
+ if (ptCount > 0 && ptCount <= MAX_GRADIENT_ELEMENTS && o + ptCount * 8 <= end) {
4880
+ for (let i = 0; i < ptCount; i++) {
4881
+ boundaryPts.push({
4882
+ x: view.getFloat32(o + i * 8, true),
4883
+ y: view.getFloat32(o + i * 8 + 4, true)
4884
+ });
4885
+ }
4886
+ o += ptCount * 8;
4887
+ }
4888
+ }
4889
+ let transform = null;
4890
+ if (flags & BRUSH_DATA_TRANSFORM && o + 24 <= end) {
4891
+ transform = readTransform(view, o);
4892
+ o += 24;
4893
+ }
4894
+ if (transform) {
4895
+ ({ x: cx, y: cy } = applyMatrix(transform, cx, cy));
4896
+ boundaryPts = boundaryPts.map((p) => applyMatrix(transform, p.x, p.y));
4897
+ }
4898
+ const surroundArgb = surround.length > 0 ? surround[0] : centerArgb;
4899
+ let stops = [
4900
+ { offset: 0, color: argbToRgba(centerArgb) },
4901
+ { offset: 1, color: argbToRgba(surroundArgb) }
4902
+ ];
4903
+ if (flags & BRUSH_DATA_PRESET_COLORS) {
4904
+ const preset = readPresetColors(view, o, end);
4905
+ if (preset) {
4906
+ stops = preset.stops.map((s) => ({ offset: 1 - s.offset, color: s.color }));
4907
+ }
4908
+ } else if (flags & BRUSH_DATA_BLEND_FACTORS_H) {
4909
+ const blend = readBlendFactors(view, o, end);
4910
+ if (blend) {
4911
+ stops = blend.entries.map((e) => ({
4912
+ offset: 1 - e.pos,
4913
+ color: lerpArgbToRgba(surroundArgb, centerArgb, e.factor)
4914
+ }));
4915
+ }
4916
+ }
4917
+ let r = 0;
4918
+ for (const p of boundaryPts) {
4919
+ const d = Math.hypot(p.x - cx, p.y - cy);
4920
+ if (d > r) {
4921
+ r = d;
4922
+ }
4923
+ }
4924
+ if (!(r > 0)) {
4925
+ return { kind: "plus-brush", color: argbToRgba(centerArgb) };
4926
+ }
4927
+ emfLog(
4928
+ `parseEmfPlusBrushObject: path gradient centre=(${cx.toFixed(1)},${cy.toFixed(1)}), r=${r.toFixed(1)}, ${stops.length} stop(s)`
4929
+ );
4930
+ return {
4931
+ kind: "plus-brush",
4932
+ color: argbToRgba(centerArgb),
4933
+ gradient: { type: "radial", cx, cy, r, stops: normaliseStops(stops) }
4934
+ };
4935
+ }
4936
+ function parseEmfPlusBrushObject(view, dataOff, recDataSize) {
4937
+ if (recDataSize < 8) {
4938
+ return null;
4939
+ }
4940
+ const end = dataOff + recDataSize;
4941
+ const hasVersion = looksLikeGraphicsVersion(view.getUint32(dataOff, true));
4942
+ const typeOff = dataOff + (hasVersion ? 4 : 0);
4943
+ if (typeOff + 8 > end) {
4944
+ return null;
4945
+ }
4946
+ const brushType = view.getUint32(typeOff, true);
4947
+ const b = typeOff + 4;
4948
+ switch (brushType) {
4949
+ case EMFPLUS_BRUSHTYPE_SOLID:
4950
+ return { kind: "plus-brush", color: argbToRgba(view.getUint32(b, true)) };
4951
+ case EMFPLUS_BRUSHTYPE_HATCHFILL:
4952
+ if (b + 8 <= end) {
4953
+ return { kind: "plus-brush", color: argbToRgba(view.getUint32(b + 4, true)) };
4954
+ }
4955
+ return { kind: "plus-brush", color: "rgba(0,0,0,1)" };
4956
+ case EMFPLUS_BRUSHTYPE_LINEARGRADIENT: {
4957
+ const brush = parseLinearGradient(view, b, end);
4958
+ return brush ?? { kind: "plus-brush", color: "rgba(0,0,0,1)" };
4959
+ }
4960
+ case EMFPLUS_BRUSHTYPE_PATHGRADIENT: {
4961
+ const brush = parsePathGradient(view, b, end);
4962
+ return brush ?? { kind: "plus-brush", color: "rgba(0,0,0,1)" };
4963
+ }
4964
+ default:
4965
+ return { kind: "plus-brush", color: "rgba(0,0,0,1)" };
4966
+ }
4967
+ }
4175
4968
  var PIXELFORMAT_24BPP_RGB = 137224;
4176
4969
  var PIXELFORMAT_32BPP_RGB = 139273;
4177
4970
  var PIXELFORMAT_32BPP_ARGB = 2498570;
@@ -4278,7 +5071,8 @@ function parseEmfPlusPenObject(view, dataOff, recDataSize) {
4278
5071
  if (recDataSize < 20) {
4279
5072
  return null;
4280
5073
  }
4281
- const penFlags = view.getUint32(dataOff + 4, true);
5074
+ const hasVersion = looksLikeGraphicsVersion(view.getUint32(dataOff, true));
5075
+ const penFlags = view.getUint32(dataOff + (hasVersion ? 8 : 4), true);
4282
5076
  const penWidth = view.getFloat32(dataOff + 16, true);
4283
5077
  let brushOff = dataOff + 20;
4284
5078
  const flagSizes = [
@@ -4338,9 +5132,9 @@ function parseEmfPlusPenObject(view, dataOff, recDataSize) {
4338
5132
  }
4339
5133
  let penColor = "rgba(0,0,0,1)";
4340
5134
  if (brushOff + 8 <= dataOff + recDataSize) {
4341
- const penBrushType = view.getUint32(brushOff, true);
4342
- if (penBrushType === EMFPLUS_BRUSHTYPE_SOLID && brushOff + 8 <= dataOff + recDataSize) {
4343
- penColor = argbToRgba(view.getUint32(brushOff + 4, true));
5135
+ const brush = parseEmfPlusBrushObject(view, brushOff, dataOff + recDataSize - brushOff);
5136
+ if (brush) {
5137
+ penColor = brush.color;
4344
5138
  }
4345
5139
  }
4346
5140
  return { kind: "plus-pen", color: penColor, width: penWidth || 1, dashStyle };
@@ -4434,19 +5228,9 @@ function handleEmfPlusObjectRecord(rCtx, recFlags, dataOff, recDataSize) {
4434
5228
  // Brush
4435
5229
  // ---------------------------------------------------------------
4436
5230
  case EMFPLUS_OBJECTTYPE_BRUSH: {
4437
- if (recDataSize >= 8) {
4438
- const brushType = view.getUint32(dataOff, true);
4439
- let color2 = "rgba(0,0,0,1)";
4440
- if (brushType === EMFPLUS_BRUSHTYPE_SOLID && recDataSize >= 8) {
4441
- color2 = argbToRgba(view.getUint32(dataOff + 4, true));
4442
- } else if (brushType === EMFPLUS_BRUSHTYPE_LINEARGRADIENT && recDataSize >= 48) {
4443
- color2 = argbToRgba(view.getUint32(dataOff + 40, true));
4444
- } else if (brushType === EMFPLUS_BRUSHTYPE_PATHGRADIENT && recDataSize >= 12) {
4445
- color2 = argbToRgba(view.getUint32(dataOff + 8, true));
4446
- } else if (brushType === EMFPLUS_BRUSHTYPE_HATCHFILL && recDataSize >= 12) {
4447
- color2 = argbToRgba(view.getUint32(dataOff + 8, true));
4448
- }
4449
- objectTable.set(objectId, { kind: "plus-brush", color: color2 });
5231
+ const brush = parseEmfPlusBrushObject(view, dataOff, recDataSize);
5232
+ if (brush) {
5233
+ objectTable.set(objectId, brush);
4450
5234
  }
4451
5235
  break;
4452
5236
  }
@@ -4542,7 +5326,7 @@ function parseRegionNode(view, off, endOff, depth = 0) {
4542
5326
  }
4543
5327
  const nodeType = view.getUint32(off, true);
4544
5328
  let cursor = off + 4;
4545
- if (nodeType <= 4) {
5329
+ if (nodeType <= 5) {
4546
5330
  const leftResult = parseRegionNode(view, cursor, endOff, depth + 1);
4547
5331
  if (!leftResult) {
4548
5332
  return null;
@@ -4629,7 +5413,7 @@ function handleEmfPlusTextImageRecord(rCtx, recType, recFlags, dataOff, recDataS
4629
5413
  const pathId = recFlags & 255;
4630
5414
  const pathObj = objectTable.get(pathId);
4631
5415
  if (pathObj && pathObj.kind === "plus-path") {
4632
- ctx.fillStyle = resolveBrushColor(rCtx, recFlags, brushVal);
5416
+ ctx.fillStyle = resolveBrushPaint(rCtx, recFlags, brushVal);
4633
5417
  applyPlusWorldTransform(rCtx);
4634
5418
  replayEmfPlusPath(ctx, pathObj);
4635
5419
  ctx.fill();
@@ -4674,7 +5458,7 @@ function handleEmfPlusTextImageRecord(rCtx, recType, recFlags, dataOff, recDataS
4674
5458
  const italic = font.flags & 2 ? "italic " : "";
4675
5459
  const family = mapFontFamily(font.family, rCtx.fontFamilyMap);
4676
5460
  ctx.font = `${italic}${bold}${font.emSize}px ${family}`;
4677
- ctx.fillStyle = resolveBrushColor(rCtx, recFlags, brushVal);
5461
+ ctx.fillStyle = resolveBrushPaint(rCtx, recFlags, brushVal);
4678
5462
  ctx.textBaseline = "top";
4679
5463
  const sf = objectTable.get(formatId);
4680
5464
  if (sf && sf.kind === "plus-stringformat") {
@@ -4714,7 +5498,7 @@ function handleEmfPlusTextImageRecord(rCtx, recType, recFlags, dataOff, recDataS
4714
5498
  const italic = font.flags & 2 ? "italic " : "";
4715
5499
  const family = mapFontFamily(font.family, rCtx.fontFamilyMap);
4716
5500
  ctx.font = `${italic}${bold}${font.emSize}px ${family}`;
4717
- ctx.fillStyle = resolveBrushColor(rCtx, recFlags, brushVal);
5501
+ ctx.fillStyle = resolveBrushPaint(rCtx, recFlags, brushVal);
4718
5502
  ctx.textBaseline = "alphabetic";
4719
5503
  ctx.textAlign = "left";
4720
5504
  applyPlusWorldTransform(rCtx);
@@ -4889,7 +5673,8 @@ function replayEmfPlusRecords(view, offset, length, ctx, _canvasW, _canvasH, sta
4889
5673
  saveIdMap: s.saveIdMap,
4890
5674
  totalImageObjects: 0,
4891
5675
  totalDrawImageCalls: 0,
4892
- clipSaveDepth: 0,
5676
+ clipSaveDepth: s.clipSaveDepth,
5677
+ clipRegion: s.clipRegion,
4893
5678
  pageUnit: 2,
4894
5679
  pageScale: 1,
4895
5680
  continuationBuffer: null,
@@ -5024,6 +5809,8 @@ function replayEmfPlusRecords(view, offset, length, ctx, _canvasW, _canvasH, sta
5024
5809
  if (state) {
5025
5810
  state.worldTransform = rCtx.worldTransform;
5026
5811
  state.saveIdMap = rCtx.saveIdMap;
5812
+ state.clipRegion = rCtx.clipRegion ?? null;
5813
+ state.clipSaveDepth = rCtx.clipSaveDepth;
5027
5814
  }
5028
5815
  ctx.setTransform(1, 0, 0, 1, 0, 0);
5029
5816
  return rCtx.deferredImages;