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
@@ -9,7 +9,24 @@ var JSZip6__default = /*#__PURE__*/_interopDefault(JSZip6);
9
9
 
10
10
  // ../core/dist/index.mjs
11
11
 
12
- // ../../node_modules/.bun/mtx-decompressor@1.4.2/node_modules/mtx-decompressor/dist/index.mjs
12
+ // ../../node_modules/.bun/mtx-decompressor@1.6.0/node_modules/mtx-decompressor/dist/index.mjs
13
+ var WARNING_CODES = /* @__PURE__ */ new Set([
14
+ "WARN_BAD_VERSION"
15
+ /* WarnBadVersion */
16
+ ]);
17
+ var EotError = class _EotError extends Error {
18
+ code;
19
+ constructor(code, message) {
20
+ super(message);
21
+ this.name = "EotError";
22
+ this.code = code;
23
+ Object.setPrototypeOf(this, _EotError.prototype);
24
+ }
25
+ /** True when this represents a recoverable warning rather than a fatal error. */
26
+ get isWarning() {
27
+ return WARNING_CODES.has(this.code);
28
+ }
29
+ };
13
30
  var Stream = class _Stream {
14
31
  buf;
15
32
  size;
@@ -49,7 +66,26 @@ var Stream = class _Stream {
49
66
  this.buf = newBuf;
50
67
  this.reserved = n;
51
68
  }
69
+ /**
70
+ * Reject byte-level access while the stream sits mid-byte (`bitPos != 0`).
71
+ *
72
+ * libeot returns `EOT_OFF_BYTE_BOUNDARY` for any byte read/write/seek issued
73
+ * before a partial byte has been consumed. The bit-level reader
74
+ * ({@link readNBits}) is the sole legitimate mid-byte accessor and bypasses
75
+ * this guard by touching `buf`/`pos` directly. On valid input the only
76
+ * `readNBits` caller consumes whole bytes per point, so the stream is always
77
+ * byte-aligned when a byte accessor runs and this guard never fires.
78
+ */
79
+ ensureByteAligned() {
80
+ if (this.bitPos !== 0) {
81
+ throw new EotError(
82
+ "OFF_BYTE_BOUNDARY",
83
+ `Stream: byte-level access at a non-byte boundary (bitPos=${this.bitPos}, pos=${this.pos})`
84
+ );
85
+ }
86
+ }
52
87
  ensureWrite(n) {
88
+ this.ensureByteAligned();
53
89
  const needed = this.pos + n;
54
90
  if (needed > this.reserved) {
55
91
  this.reserve(Math.max(needed, this.reserved * 2 || 256));
@@ -59,6 +95,7 @@ var Stream = class _Stream {
59
95
  }
60
96
  }
61
97
  ensureRead(n) {
98
+ this.ensureByteAligned();
62
99
  if (this.pos + n > this.size) {
63
100
  throw new Error(
64
101
  `Stream: not enough data (need ${n} bytes at pos ${this.pos}, size ${this.size})`
@@ -66,14 +103,18 @@ var Stream = class _Stream {
66
103
  }
67
104
  }
68
105
  // --- Seek ---
106
+ // A seek requires the stream to be byte-aligned and never clears `bitPos`
107
+ // itself (mirroring libeot, which refuses to seek mid-byte rather than
108
+ // silently re-aligning). The alignment guard leaves `bitPos` at 0.
69
109
  seekAbsolute(pos2) {
110
+ this.ensureByteAligned();
70
111
  if (pos2 > this.size) {
71
112
  throw new Error(`Stream: seek past end (${pos2} > ${this.size})`);
72
113
  }
73
114
  this.pos = pos2;
74
- this.bitPos = 0;
75
115
  }
76
116
  seekRelative(offset) {
117
+ this.ensureByteAligned();
77
118
  const newPos = this.pos + offset;
78
119
  if (newPos < 0) {
79
120
  throw new Error("Stream: negative seek");
@@ -82,17 +123,23 @@ var Stream = class _Stream {
82
123
  throw new Error("Stream: seek past end");
83
124
  }
84
125
  this.pos = newPos;
85
- this.bitPos = 0;
86
126
  }
127
+ // Seek into already-reserved-but-unwritten space, extending `size` up to the
128
+ // seek target. libeot returns `EOT_SEEK_PAST_EOS` when the target exceeds the
129
+ // reserved capacity; we mirror that rather than growing the buffer, so an
130
+ // over-reach surfaces as a failure instead of a silent realloc.
87
131
  seekAbsoluteThroughReserve(pos2) {
132
+ this.ensureByteAligned();
88
133
  if (pos2 > this.reserved) {
89
- this.reserve(pos2);
134
+ throw new EotError(
135
+ "SEEK_PAST_EOS",
136
+ `Stream: seek to ${pos2} past reserved end (${this.reserved})`
137
+ );
90
138
  }
91
139
  if (pos2 > this.size) {
92
140
  this.size = pos2;
93
141
  }
94
142
  this.pos = pos2;
95
- this.bitPos = 0;
96
143
  }
97
144
  seekRelativeThroughReserve(offset) {
98
145
  this.seekAbsoluteThroughReserve(this.pos + offset);
@@ -146,6 +193,9 @@ var Stream = class _Stream {
146
193
  this.buf[this.pos++] = v & 255;
147
194
  }
148
195
  writeU24(v) {
196
+ if (v < 0 || v > 16777215) {
197
+ throw new Error(`Stream: writeU24 value out of range: ${v}`);
198
+ }
149
199
  this.ensureWrite(3);
150
200
  this.buf[this.pos++] = v >> 16 & 255;
151
201
  this.buf[this.pos++] = v >> 8 & 255;
@@ -169,6 +219,9 @@ var Stream = class _Stream {
169
219
  if (n === 0) {
170
220
  return 0;
171
221
  }
222
+ if (n > 32) {
223
+ throw new Error(`Stream: readNBits width out of range: ${n} (max 32)`);
224
+ }
172
225
  let value = 0;
173
226
  let bitsRemaining = n;
174
227
  while (bitsRemaining > 0) {
@@ -187,18 +240,36 @@ var Stream = class _Stream {
187
240
  }
188
241
  bitsRemaining -= bitsToRead;
189
242
  }
190
- return value;
243
+ return value >>> 0;
191
244
  }
192
245
  // --- Copy ---
193
- /** Copy `length` bytes from this stream to `dest`. */
246
+ /**
247
+ * Copy `length` bytes from this stream to `dest`.
248
+ *
249
+ * Both streams must be byte-aligned. The destination must already have the
250
+ * capacity reserved: libeot returns `EOT_OUT_OF_RESERVED_SPACE` when a copy
251
+ * would overrun the reserved buffer, so we throw rather than auto-growing —
252
+ * an under-reservation is a bug we want surfaced, not silently patched.
253
+ */
194
254
  copyTo(dest, length) {
255
+ this.ensureByteAligned();
256
+ dest.ensureByteAligned();
195
257
  if (this.pos + length > this.size) {
196
258
  throw new Error("Stream: not enough data for copy");
197
259
  }
198
- dest.ensureWrite(length);
260
+ const needed = dest.pos + length;
261
+ if (needed > dest.reserved) {
262
+ throw new EotError(
263
+ "OUT_OF_RESERVED_SPACE",
264
+ `Stream: copy of ${length} bytes exceeds reserved capacity (need ${needed}, reserved ${dest.reserved})`
265
+ );
266
+ }
199
267
  dest.buf.set(this.buf.subarray(this.pos, this.pos + length), dest.pos);
200
268
  this.pos += length;
201
269
  dest.pos += length;
270
+ if (dest.pos > dest.size) {
271
+ dest.size = dest.pos;
272
+ }
202
273
  }
203
274
  /** Read rest of data as 4-byte-aligned U32 values. Returns 0 on incomplete read. */
204
275
  readRestAsU32() {
@@ -219,19 +290,27 @@ var Stream = class _Stream {
219
290
  }
220
291
  return this.readU32();
221
292
  }
222
- /** Compute checksum of bytes from beginPos to endPos as 4-byte aligned U32 sum. */
293
+ /**
294
+ * Compute the SFNT-style checksum of bytes in `[beginPos, endPos)` as a sum
295
+ * of big-endian U32 words, zero-padding a final partial word. Bounds strictly
296
+ * on `endPos` (not the stream's `size`), so an unaligned range never folds in
297
+ * bytes past `endPos`. Leaves the stream position unchanged.
298
+ */
223
299
  checksumU32(beginPos, endPos) {
300
+ if (beginPos > endPos) {
301
+ throw new Error(`Stream: checksumU32 beginPos ${beginPos} > endPos ${endPos}`);
302
+ }
303
+ if (endPos > this.size) {
304
+ throw new Error(`Stream: checksumU32 endPos ${endPos} exceeds size ${this.size}`);
305
+ }
224
306
  let sum = 0;
225
- const savedPos = this.pos;
226
- this.pos = beginPos;
227
- while (this.pos < endPos) {
228
- const chunk = this.readRestAsU32();
229
- if (chunk === null) {
230
- break;
307
+ for (let p = beginPos; p < endPos; p += 4) {
308
+ let word = 0;
309
+ for (let i = 0; i < 4; i++) {
310
+ word = word << 8 | (p + i < endPos ? this.buf[p + i] : 0);
231
311
  }
232
- sum = sum + chunk >>> 0;
312
+ sum = sum + (word >>> 0) >>> 0;
233
313
  }
234
- this.pos = savedPos;
235
314
  return sum;
236
315
  }
237
316
  /** Get a copy of the written data. */
@@ -431,10 +510,9 @@ function read255Short(s) {
431
510
  }
432
511
  function unpackCVT(table, sIn) {
433
512
  sIn.seekAbsolute(table.offset);
434
- const tableLength = sIn.readU16();
435
- const numEntries = tableLength >>> 1;
513
+ const numEntries = sIn.readU16();
436
514
  const out = new Stream(null, 0);
437
- out.reserve(tableLength);
515
+ out.reserve(numEntries * 2);
438
516
  let lastValue = 0;
439
517
  for (let i = 0; i < numEntries; i++) {
440
518
  const code = sIn.readU8();
@@ -506,7 +584,13 @@ function decodePushInstructions(sIn, sOut, pushCount) {
506
584
  }
507
585
  while (remaining > 0) {
508
586
  const code = sIn.peekU8();
509
- if (code === 251 && remaining >= 3 && data.length >= 2) {
587
+ if (code === 251) {
588
+ if (remaining < 3 || data.length < 2) {
589
+ throw new EotError(
590
+ "CORRUPT_HOPCODE_DATA",
591
+ `corrupt hop3 (0xFB) push data: remaining=${remaining}, decoded=${data.length}`
592
+ );
593
+ }
510
594
  sIn.readU8();
511
595
  const prev = data[data.length - 2];
512
596
  put(prev);
@@ -514,7 +598,13 @@ function decodePushInstructions(sIn, sOut, pushCount) {
514
598
  put(val);
515
599
  put(prev);
516
600
  remaining -= 3;
517
- } else if (code === 252 && remaining >= 5 && data.length >= 2) {
601
+ } else if (code === 252) {
602
+ if (remaining < 5 || data.length < 2) {
603
+ throw new EotError(
604
+ "CORRUPT_HOPCODE_DATA",
605
+ `corrupt hop4 (0xFC) push data: remaining=${remaining}, decoded=${data.length}`
606
+ );
607
+ }
518
608
  sIn.readU8();
519
609
  const prev = data[data.length - 2];
520
610
  put(prev);
@@ -608,8 +698,8 @@ function decodeSimpleGlyph(numContours, streams, out, calcBBox, minX, minY, maxX
608
698
  }
609
699
  xDeltas[i] = dx;
610
700
  yDeltas[i] = dy;
611
- cumulativeX += dx;
612
- cumulativeY += dy;
701
+ cumulativeX += xDeltas[i];
702
+ cumulativeY += yDeltas[i];
613
703
  if (calcBBox) {
614
704
  if (cumulativeX < minX) {
615
705
  minX = cumulativeX;
@@ -813,8 +903,9 @@ function parseMaxp(table) {
813
903
  maxComponentElements
814
904
  };
815
905
  }
816
- function parseCTF(streams) {
906
+ function parseCTF(streams, options) {
817
907
  const s0 = streams[0];
908
+ const droppedTables = [];
818
909
  s0.readU32();
819
910
  const numTables = s0.readU16();
820
911
  s0.readU16();
@@ -825,10 +916,13 @@ function parseCTF(streams) {
825
916
  let locaIdx = -1;
826
917
  let maxpIdx = -1;
827
918
  let headIdx = -1;
919
+ let hmtxIdx = -1;
828
920
  for (let i = 0; i < numTables; i++) {
829
921
  const tag = s0.readChar() + s0.readChar() + s0.readChar() + s0.readChar();
830
922
  if (tag === "hdmx" || tag === "VDMX") {
831
923
  s0.seekRelative(12);
924
+ droppedTables.push(tag);
925
+ options?.onWarn?.(`Ignoring ${tag} table \u2014 MTX does not preserve it`);
832
926
  continue;
833
927
  }
834
928
  s0.seekRelative(4);
@@ -851,6 +945,8 @@ function parseCTF(streams) {
851
945
  maxpIdx = idx;
852
946
  } else if (tag === "head") {
853
947
  headIdx = idx;
948
+ } else if (tag === "hmtx") {
949
+ hmtxIdx = idx;
854
950
  } else ;
855
951
  }
856
952
  for (let i = 0; i < tables.length; i++) {
@@ -869,26 +965,29 @@ function parseCTF(streams) {
869
965
  }
870
966
  table.buf = buf;
871
967
  if (table.tag === "head") {
968
+ if (table.bufSize < 12) {
969
+ throw new EotError(
970
+ "MALFORMED_HEAD_TABLE",
971
+ `head table too small: ${table.bufSize} bytes (need at least 12)`
972
+ );
973
+ }
872
974
  table.buf[8] = 0;
873
975
  table.buf[9] = 0;
874
976
  table.buf[10] = 0;
875
977
  table.buf[11] = 0;
876
978
  }
877
979
  }
878
- let headData = { indexToLocFormat: 0 };
879
- if (headIdx >= 0) {
880
- headData = parseHead(tables[headIdx]);
980
+ if (maxpIdx < 0) {
981
+ throw new EotError("NO_MAXP_TABLE", "CTF font is missing a maxp table");
881
982
  }
882
- let maxpData = {
883
- numGlyphs: 0,
884
- maxPoints: 0,
885
- maxContours: 0,
886
- maxSizeOfInstructions: 0,
887
- maxComponentElements: 0
888
- };
889
- if (maxpIdx >= 0) {
890
- maxpData = parseMaxp(tables[maxpIdx]);
983
+ if (headIdx < 0) {
984
+ throw new EotError("NO_HEAD_TABLE", "CTF font is missing a head table");
891
985
  }
986
+ if (hmtxIdx < 0) {
987
+ throw new EotError("NO_HMTX_TABLE", "CTF font is missing an hmtx table");
988
+ }
989
+ const headData = parseHead(tables[headIdx]);
990
+ const maxpData = parseMaxp(tables[maxpIdx]);
892
991
  if (glyfIdx >= 0) {
893
992
  if (locaIdx < 0) {
894
993
  const locaTable = {
@@ -903,11 +1002,11 @@ function parseCTF(streams) {
903
1002
  }
904
1003
  populateGlyfAndLoca(tables[glyfIdx], tables[locaIdx], headData, maxpData, streams);
905
1004
  }
906
- return { tables };
1005
+ return droppedTables.length > 0 ? { tables, droppedTables } : { tables };
907
1006
  }
908
1007
  function bitsUsed(x) {
909
1008
  if (x <= 0) {
910
- return 0;
1009
+ return 1;
911
1010
  }
912
1011
  return 32 - Math.clz32(x);
913
1012
  }
@@ -1099,12 +1198,15 @@ var BitIO = class {
1099
1198
  /**
1100
1199
  * @param data Source byte buffer.
1101
1200
  * @param offset Starting byte offset into `data`.
1102
- * @param size Number of bytes available from `offset`.
1201
+ * @param size Absolute end index into `data` (exclusive) — reading stops
1202
+ * once `index` reaches it. Defaults to `data.length`. Clamped
1203
+ * to `data.length` so an over-large value cannot read past the
1204
+ * end of the buffer and silently yield zero bits.
1103
1205
  */
1104
1206
  constructor(data, offset = 0, size) {
1105
1207
  this.data = data;
1106
1208
  this.index = offset;
1107
- this.size = size ?? data.length;
1209
+ this.size = Math.min(size ?? data.length, data.length);
1108
1210
  }
1109
1211
  /**
1110
1212
  * Read a single bit from the stream.
@@ -1117,13 +1219,14 @@ var BitIO = class {
1117
1219
  * shifted out of the original byte value).
1118
1220
  */
1119
1221
  inputBit() {
1120
- if (this.bitCount-- === 0) {
1222
+ if (this.bitCount === 0) {
1121
1223
  if (this.index >= this.size) {
1122
1224
  throw new Error("BitIO: end of data");
1123
1225
  }
1124
1226
  this.bitBuffer = this.data[this.index++];
1125
- this.bitCount = 7;
1227
+ this.bitCount = 8;
1126
1228
  }
1229
+ this.bitCount--;
1127
1230
  this.bitBuffer <<= 1;
1128
1231
  return (this.bitBuffer & 256) !== 0;
1129
1232
  }
@@ -1141,7 +1244,7 @@ var BitIO = class {
1141
1244
  value |= 1;
1142
1245
  }
1143
1246
  }
1144
- return value;
1247
+ return value >>> 0;
1145
1248
  }
1146
1249
  };
1147
1250
  var LEN_WIDTH = 3;
@@ -1151,8 +1254,8 @@ var MAX_2BYTE_DIST = 512;
1151
1254
  var PRELOAD_SIZE = 2 * 32 * 96 + 4 * 256;
1152
1255
  var LEN_MIN = 2;
1153
1256
  var DIST_MIN = 1;
1154
- var MAX_OUT_LEN = 4 * 1024 * 1024;
1155
- var MAX_OUT = 16 * 1024 * 1024;
1257
+ var MAX_OUT_LEN = (1 << 24) - 1;
1258
+ var MAX_OUT = 64 * 1024 * 1024;
1156
1259
  var RLE_INITIAL = 0;
1157
1260
  var RLE_NORMAL = 1;
1158
1261
  var RLE_SEEN_ESCAPE = 2;
@@ -1324,7 +1427,8 @@ function lzcompDecompress(data, size, version) {
1324
1427
  }
1325
1428
  }
1326
1429
  };
1327
- for (let pos2 = 0; pos2 < outLen; ) {
1430
+ let pos2 = 0;
1431
+ for (; pos2 < outLen; ) {
1328
1432
  const symbol = symEcoder.readSymbol();
1329
1433
  let value;
1330
1434
  if (symbol < 256) {
@@ -1355,6 +1459,12 @@ function lzcompDecompress(data, size, version) {
1355
1459
  pos2++;
1356
1460
  emitByte(value);
1357
1461
  }
1462
+ if (pos2 !== outLen) {
1463
+ throw new EotError(
1464
+ "MTX_ERROR",
1465
+ `LZCOMP decode overran output: pos=${pos2}, expected=${outLen}`
1466
+ );
1467
+ }
1358
1468
  return outBuf.subarray(0, outIdx);
1359
1469
  }
1360
1470
  function lgFloor(n) {
@@ -1458,14 +1568,18 @@ function dumpContainer(ctr) {
1458
1568
  beginningChecksum = beginningChecksum + word >>> 0;
1459
1569
  }
1460
1570
  totalChecksum = totalChecksum + beginningChecksum >>> 0;
1461
- const finalChecksum = 2981146554 - totalChecksum >>> 0;
1462
- if (headTable) {
1463
- const adjOffset = headTable.offset + 8;
1464
- buf[adjOffset] = finalChecksum >>> 24 & 255;
1465
- buf[adjOffset + 1] = finalChecksum >>> 16 & 255;
1466
- buf[adjOffset + 2] = finalChecksum >>> 8 & 255;
1467
- buf[adjOffset + 3] = finalChecksum & 255;
1571
+ if (!headTable) {
1572
+ throw new EotError(
1573
+ "NO_HEAD_TABLE",
1574
+ "cannot assemble SFNT: container is missing a head table"
1575
+ );
1468
1576
  }
1577
+ const finalChecksum = 2981146554 - totalChecksum >>> 0;
1578
+ const adjOffset = headTable.offset + 8;
1579
+ buf[adjOffset] = finalChecksum >>> 24 & 255;
1580
+ buf[adjOffset + 1] = finalChecksum >>> 16 & 255;
1581
+ buf[adjOffset + 2] = finalChecksum >>> 8 & 255;
1582
+ buf[adjOffset + 3] = finalChecksum & 255;
1469
1583
  out.pos = afterTables;
1470
1584
  return out.buf.subarray(0, out.pos);
1471
1585
  }
@@ -1511,14 +1625,64 @@ function decompressMtx(fontData, options) {
1511
1625
  }
1512
1626
  const { streams } = unpackMtx(data, data.length);
1513
1627
  const streamObjects = streams.map((buf) => new Stream(buf, buf.length));
1514
- const container = parseCTF(streamObjects);
1628
+ const container = parseCTF(streamObjects, { onWarn: options?.onWarn });
1515
1629
  return dumpContainer(container);
1516
1630
  }
1517
1631
  function decompressEotFont(fontData, compressed, encrypted) {
1518
1632
  return decompressMtx(fontData, { encrypted});
1519
1633
  }
1520
1634
 
1521
- // ../../node_modules/.bun/emf-converter@1.5.0/node_modules/emf-converter/dist/index.mjs
1635
+ // ../../node_modules/.bun/emf-converter@1.6.0/node_modules/emf-converter/dist/index.mjs
1636
+ function colorRefToHex(r, g, b) {
1637
+ const toHex3 = (v) => v.toString(16).padStart(2, "0");
1638
+ return `#${toHex3(r & 255)}${toHex3(g & 255)}${toHex3(b & 255)}`;
1639
+ }
1640
+ function readColorRef(view, offset) {
1641
+ const r = view.getUint8(offset);
1642
+ const g = view.getUint8(offset + 1);
1643
+ const b = view.getUint8(offset + 2);
1644
+ return colorRefToHex(r, g, b);
1645
+ }
1646
+ function argbToRgba(argb) {
1647
+ const a = (argb >>> 24 & 255) / 255;
1648
+ const r = argb >>> 16 & 255;
1649
+ const g = argb >>> 8 & 255;
1650
+ const b = argb & 255;
1651
+ return `rgba(${r},${g},${b},${a.toFixed(3)})`;
1652
+ }
1653
+ function lerpArgbToRgba(argbA, argbB, t) {
1654
+ const tc = Math.min(1, Math.max(0, t));
1655
+ const mix = (a2, b2) => Math.round(a2 + (b2 - a2) * tc);
1656
+ const aA = argbA >>> 24 & 255;
1657
+ const aB = argbB >>> 24 & 255;
1658
+ const r = mix(argbA >>> 16 & 255, argbB >>> 16 & 255);
1659
+ const g = mix(argbA >>> 8 & 255, argbB >>> 8 & 255);
1660
+ const b = mix(argbA & 255, argbB & 255);
1661
+ const a = mix(aA, aB) / 255;
1662
+ return `rgba(${r},${g},${b},${a.toFixed(3)})`;
1663
+ }
1664
+ function invertCssColor(color2) {
1665
+ const hex10 = /^#([0-9a-f]{6})$/i.exec(color2);
1666
+ if (hex10) {
1667
+ const v = parseInt(hex10[1], 16);
1668
+ const inv = 16777215 ^ v;
1669
+ return `#${inv.toString(16).padStart(6, "0")}`;
1670
+ }
1671
+ const shortHex = /^#([0-9a-f]{3})$/i.exec(color2);
1672
+ if (shortHex) {
1673
+ const [r, g, b] = shortHex[1].split("").map((c) => parseInt(c + c, 16));
1674
+ const toHex3 = (v) => (255 - v).toString(16).padStart(2, "0");
1675
+ return `#${toHex3(r)}${toHex3(g)}${toHex3(b)}`;
1676
+ }
1677
+ const rgba = /^rgba?\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(?:,\s*([\d.]+)\s*)?\)$/i.exec(color2);
1678
+ if (rgba) {
1679
+ const r = 255 - Math.min(255, parseInt(rgba[1], 10));
1680
+ const g = 255 - Math.min(255, parseInt(rgba[2], 10));
1681
+ const b = 255 - Math.min(255, parseInt(rgba[3], 10));
1682
+ return rgba[4] !== void 0 ? `rgba(${r},${g},${b},${rgba[4]})` : `rgb(${r},${g},${b})`;
1683
+ }
1684
+ return color2;
1685
+ }
1522
1686
  var EMR_HEADER = 1;
1523
1687
  var EMR_POLYBEZIER = 2;
1524
1688
  var EMR_POLYGON = 3;
@@ -1539,10 +1703,21 @@ var EMR_SETBKMODE = 18;
1539
1703
  var EMR_SETPOLYFILLMODE = 19;
1540
1704
  var EMR_SETROP2 = 20;
1541
1705
  var EMR_SETSTRETCHBLTMODE = 21;
1706
+ var R2_BLACK = 1;
1707
+ var R2_NOTMERGEPEN = 2;
1708
+ var R2_MASKNOTPEN = 3;
1709
+ var R2_NOTCOPYPEN = 4;
1710
+ var R2_MASKPENNOT = 5;
1542
1711
  var R2_NOT = 6;
1543
1712
  var R2_XORPEN = 7;
1713
+ var R2_NOTMASKPEN = 8;
1544
1714
  var R2_MASKPEN = 9;
1715
+ var R2_NOTXORPEN = 10;
1716
+ var R2_NOP = 11;
1717
+ var R2_MERGENOTPEN = 12;
1718
+ var R2_MERGEPENNOT = 14;
1545
1719
  var R2_MERGEPEN = 15;
1720
+ var R2_WHITE = 16;
1546
1721
  var MAX_CANVAS_DIMENSION = 8192;
1547
1722
  var MAX_RECORDS_DEFAULT = 2e5;
1548
1723
  var MAX_RECORDS_EMFPLUS_DEFAULT = 5e5;
@@ -1773,28 +1948,61 @@ function createTempCanvas(width, height) {
1773
1948
  }
1774
1949
  return null;
1775
1950
  }
1776
- function rop2ToGco(rop2) {
1951
+ function rop2Paint(rop2) {
1777
1952
  switch (rop2) {
1953
+ case R2_BLACK:
1954
+ return { gco: "source-over", colorTransform: "black", exact: true };
1955
+ case R2_WHITE:
1956
+ return { gco: "source-over", colorTransform: "white", exact: true };
1957
+ case R2_NOP:
1958
+ return { gco: "source-over", colorTransform: "skip", exact: true };
1959
+ case R2_NOTCOPYPEN:
1960
+ return { gco: "source-over", colorTransform: "invert", exact: true };
1961
+ case R2_NOT:
1962
+ return { gco: "difference", colorTransform: "white", exact: true };
1778
1963
  case R2_XORPEN:
1779
- return "xor";
1964
+ case R2_MASKPENNOT:
1965
+ return { gco: "difference", colorTransform: "none", exact: false };
1966
+ case R2_NOTXORPEN:
1967
+ return { gco: "difference", colorTransform: "invert", exact: false };
1780
1968
  case R2_MASKPEN:
1781
- return "multiply";
1969
+ return { gco: "darken", colorTransform: "none", exact: false };
1970
+ case R2_MASKNOTPEN:
1971
+ case R2_NOTMERGEPEN:
1972
+ return { gco: "darken", colorTransform: "invert", exact: false };
1782
1973
  case R2_MERGEPEN:
1783
- return "lighten";
1784
- case R2_NOT:
1785
- return "difference";
1974
+ case R2_MERGEPENNOT:
1975
+ return { gco: "lighten", colorTransform: "none", exact: false };
1976
+ case R2_MERGENOTPEN:
1977
+ case R2_NOTMASKPEN:
1978
+ return { gco: "lighten", colorTransform: "invert", exact: false };
1786
1979
  default:
1787
- return "source-over";
1980
+ return { gco: "source-over", colorTransform: "none", exact: true };
1981
+ }
1982
+ }
1983
+ function rop2TransformColor(color2, transform) {
1984
+ switch (transform) {
1985
+ case "invert":
1986
+ return invertCssColor(color2);
1987
+ case "black":
1988
+ return "#000000";
1989
+ case "white":
1990
+ return "#ffffff";
1991
+ case "skip":
1992
+ return "rgba(0,0,0,0)";
1993
+ default:
1994
+ return color2;
1788
1995
  }
1789
1996
  }
1790
1997
  function applyPen(ctx, state) {
1791
- ctx.globalCompositeOperation = rop2ToGco(state.rop2);
1998
+ const paint = rop2Paint(state.rop2);
1999
+ ctx.globalCompositeOperation = paint.gco;
1792
2000
  if (state.penStyle === 5) {
1793
2001
  ctx.strokeStyle = "rgba(0,0,0,0)";
1794
2002
  ctx.lineWidth = 0;
1795
2003
  return;
1796
2004
  }
1797
- ctx.strokeStyle = state.penColor;
2005
+ ctx.strokeStyle = rop2TransformColor(state.penColor, paint.colorTransform);
1798
2006
  ctx.lineWidth = Math.max(state.penWidth, 1);
1799
2007
  switch (state.penStyle) {
1800
2008
  case 1:
@@ -1815,12 +2023,13 @@ function applyPen(ctx, state) {
1815
2023
  }
1816
2024
  }
1817
2025
  function applyBrush(ctx, state) {
1818
- ctx.globalCompositeOperation = rop2ToGco(state.rop2);
2026
+ const paint = rop2Paint(state.rop2);
2027
+ ctx.globalCompositeOperation = paint.gco;
1819
2028
  if (state.brushStyle === 1) {
1820
2029
  ctx.fillStyle = "rgba(0,0,0,0)";
1821
2030
  return;
1822
2031
  }
1823
- ctx.fillStyle = state.brushColor;
2032
+ ctx.fillStyle = rop2TransformColor(state.brushColor, paint.colorTransform);
1824
2033
  }
1825
2034
  function cssFontWeight(weight) {
1826
2035
  if (!weight || weight === 400) {
@@ -2051,46 +2260,35 @@ function parseWmfHeader(view) {
2051
2260
  unitsPerInch
2052
2261
  };
2053
2262
  }
2054
- function colorRefToHex(r, g, b) {
2055
- const toHex3 = (v) => v.toString(16).padStart(2, "0");
2056
- return `#${toHex3(r & 255)}${toHex3(g & 255)}${toHex3(b & 255)}`;
2057
- }
2058
- function readColorRef(view, offset) {
2059
- const r = view.getUint8(offset);
2060
- const g = view.getUint8(offset + 1);
2061
- const b = view.getUint8(offset + 2);
2062
- return colorRefToHex(r, g, b);
2063
- }
2064
- function argbToRgba(argb) {
2065
- const a = (argb >>> 24 & 255) / 255;
2066
- const r = argb >>> 16 & 255;
2067
- const g = argb >>> 8 & 255;
2068
- const b = argb & 255;
2069
- return `rgba(${r},${g},${b},${a.toFixed(3)})`;
2070
- }
2071
2263
  function gmx(r, x) {
2264
+ const wt = r.state.worldTransform;
2265
+ const px2 = wt[0] * x + wt[4];
2072
2266
  if (r.useMappingMode) {
2073
- return (x - r.windowOrg.x) / (r.windowExt.cx || 1) * (r.viewportExt.cx || 1) + r.viewportOrg.x;
2267
+ return (px2 - r.windowOrg.x) / (r.windowExt.cx || 1) * (r.viewportExt.cx || 1) + r.viewportOrg.x;
2074
2268
  }
2075
- return (x - r.bounds.left) * r.sx;
2269
+ return (px2 - r.bounds.left) * r.sx;
2076
2270
  }
2077
2271
  function gmy(r, y) {
2272
+ const wt = r.state.worldTransform;
2273
+ const py = wt[3] * y + wt[5];
2078
2274
  if (r.useMappingMode) {
2079
- return (y - r.windowOrg.y) / (r.windowExt.cy || 1) * (r.viewportExt.cy || 1) + r.viewportOrg.y;
2275
+ return (py - r.windowOrg.y) / (r.windowExt.cy || 1) * (r.viewportExt.cy || 1) + r.viewportOrg.y;
2080
2276
  }
2081
- return (y - r.bounds.top) * r.sy;
2277
+ return (py - r.bounds.top) * r.sy;
2082
2278
  }
2083
2279
  function gmw(r, w) {
2280
+ const pw = r.state.worldTransform[0] * w;
2084
2281
  if (r.useMappingMode) {
2085
- return w / (r.windowExt.cx || 1) * (r.viewportExt.cx || 1);
2282
+ return pw / (r.windowExt.cx || 1) * (r.viewportExt.cx || 1);
2086
2283
  }
2087
- return w * r.sx;
2284
+ return pw * r.sx;
2088
2285
  }
2089
2286
  function gmh(r, h) {
2287
+ const ph = r.state.worldTransform[3] * h;
2090
2288
  if (r.useMappingMode) {
2091
- return h / (r.windowExt.cy || 1) * (r.viewportExt.cy || 1);
2289
+ return ph / (r.windowExt.cy || 1) * (r.viewportExt.cy || 1);
2092
2290
  }
2093
- return h * r.sy;
2291
+ return ph * r.sy;
2094
2292
  }
2095
2293
  function activateGdiMappingMode(r) {
2096
2294
  r.useMappingMode = true;
@@ -2291,6 +2489,216 @@ function handleEmfGdiShapeRecord(rCtx, recType, dataOff, recSize) {
2291
2489
  return false;
2292
2490
  }
2293
2491
  }
2492
+ var CLIP_HUGE = 1 << 24;
2493
+ function rectClipShape(x, y, w, h) {
2494
+ return { cmds: [{ op: "rect", x, y, w, h }], fillRule: "nonzero", simple: true };
2495
+ }
2496
+ function rectsClipShape(rects) {
2497
+ return {
2498
+ cmds: rects.map((r) => ({ op: "rect", x: r.x, y: r.y, w: r.w, h: r.h })),
2499
+ fillRule: "nonzero",
2500
+ simple: true
2501
+ };
2502
+ }
2503
+ function emptyClipShape() {
2504
+ return { cmds: [{ op: "rect", x: 0, y: 0, w: 0, h: 0 }], fillRule: "nonzero", simple: true };
2505
+ }
2506
+ function translateClipShape(shape, dx, dy) {
2507
+ return {
2508
+ ...shape,
2509
+ cmds: shape.cmds.map((c) => {
2510
+ switch (c.op) {
2511
+ case "rect":
2512
+ return { ...c, x: c.x + dx, y: c.y + dy };
2513
+ case "moveTo":
2514
+ case "lineTo":
2515
+ return { ...c, x: c.x + dx, y: c.y + dy };
2516
+ case "bezierCurveTo":
2517
+ return {
2518
+ ...c,
2519
+ cp1x: c.cp1x + dx,
2520
+ cp1y: c.cp1y + dy,
2521
+ cp2x: c.cp2x + dx,
2522
+ cp2y: c.cp2y + dy,
2523
+ x: c.x + dx,
2524
+ y: c.y + dy
2525
+ };
2526
+ case "closePath":
2527
+ return c;
2528
+ }
2529
+ })
2530
+ };
2531
+ }
2532
+ function translateClipRegion(region, dx, dy) {
2533
+ if (!region) {
2534
+ return null;
2535
+ }
2536
+ return region.map((s) => translateClipShape(s, dx, dy));
2537
+ }
2538
+ function isComposable(shape) {
2539
+ return shape.simple && shape.fillRule === "nonzero";
2540
+ }
2541
+ function invertClipShape(shape) {
2542
+ return {
2543
+ cmds: [
2544
+ { op: "rect", x: -CLIP_HUGE, y: -CLIP_HUGE, w: 2 * CLIP_HUGE, h: 2 * CLIP_HUGE },
2545
+ ...shape.cmds
2546
+ ],
2547
+ fillRule: "evenodd",
2548
+ simple: false
2549
+ };
2550
+ }
2551
+ function combineClip(current, shape, op) {
2552
+ switch (op) {
2553
+ case "replace":
2554
+ return { region: [shape], exact: true };
2555
+ case "intersect":
2556
+ return { region: current ? [...current, shape] : [shape], exact: true };
2557
+ case "exclude": {
2558
+ if (!isComposable(shape)) {
2559
+ return { region: current ? [...current, shape] : [shape], exact: false };
2560
+ }
2561
+ const inv = invertClipShape(shape);
2562
+ return { region: current ? [...current, inv] : [inv], exact: true };
2563
+ }
2564
+ case "union": {
2565
+ if (!current) {
2566
+ return { region: null, exact: true };
2567
+ }
2568
+ if (current.length === 1 && isComposable(current[0]) && isComposable(shape)) {
2569
+ return {
2570
+ region: [
2571
+ { cmds: [...current[0].cmds, ...shape.cmds], fillRule: "nonzero", simple: false }
2572
+ ],
2573
+ exact: false
2574
+ };
2575
+ }
2576
+ return { region: current, exact: false };
2577
+ }
2578
+ case "xor": {
2579
+ if (!current) {
2580
+ if (isComposable(shape)) {
2581
+ return { region: [invertClipShape(shape)], exact: true };
2582
+ }
2583
+ return { region: [shape], exact: false };
2584
+ }
2585
+ if (current.length === 1 && isComposable(current[0]) && isComposable(shape)) {
2586
+ return {
2587
+ region: [
2588
+ { cmds: [...current[0].cmds, ...shape.cmds], fillRule: "evenodd", simple: false }
2589
+ ],
2590
+ exact: true
2591
+ };
2592
+ }
2593
+ if (isComposable(shape)) {
2594
+ return { region: [...current, invertClipShape(shape)], exact: false };
2595
+ }
2596
+ return { region: [...current, shape], exact: false };
2597
+ }
2598
+ case "complement": {
2599
+ if (!current) {
2600
+ return { region: [emptyClipShape()], exact: true };
2601
+ }
2602
+ if (current.length === 1 && isComposable(current[0])) {
2603
+ return { region: [shape, invertClipShape(current[0])], exact: true };
2604
+ }
2605
+ return { region: [shape], exact: false };
2606
+ }
2607
+ }
2608
+ }
2609
+ function combineClipRegions(current, incoming, op) {
2610
+ if (op === "replace") {
2611
+ return { region: incoming, exact: true };
2612
+ }
2613
+ if (incoming && incoming.length === 1) {
2614
+ return combineClip(current, incoming[0], op);
2615
+ }
2616
+ if (!incoming) {
2617
+ switch (op) {
2618
+ case "intersect":
2619
+ return { region: current, exact: true };
2620
+ case "union":
2621
+ return { region: null, exact: true };
2622
+ case "exclude":
2623
+ return { region: [emptyClipShape()], exact: true };
2624
+ case "xor":
2625
+ case "complement": {
2626
+ if (!current) {
2627
+ return { region: [emptyClipShape()], exact: true };
2628
+ }
2629
+ if (current.length === 1) {
2630
+ return combineClip(null, current[0], "exclude");
2631
+ }
2632
+ return { region: current, exact: false };
2633
+ }
2634
+ }
2635
+ }
2636
+ switch (op) {
2637
+ case "intersect":
2638
+ return { region: current ? [...current, ...incoming] : incoming, exact: true };
2639
+ case "union":
2640
+ return { region: current, exact: false };
2641
+ case "xor":
2642
+ return { region: current ?? incoming, exact: false };
2643
+ case "exclude":
2644
+ return { region: current, exact: false };
2645
+ case "complement": {
2646
+ if (!current) {
2647
+ return { region: [emptyClipShape()], exact: true };
2648
+ }
2649
+ if (current.length === 1) {
2650
+ return combineClip(incoming, current[0], "exclude");
2651
+ }
2652
+ return { region: incoming, exact: false };
2653
+ }
2654
+ }
2655
+ }
2656
+ function replayClipCmds(ctx, cmds) {
2657
+ for (const c of cmds) {
2658
+ switch (c.op) {
2659
+ case "rect":
2660
+ ctx.rect(c.x, c.y, c.w, c.h);
2661
+ break;
2662
+ case "moveTo":
2663
+ ctx.moveTo(c.x, c.y);
2664
+ break;
2665
+ case "lineTo":
2666
+ ctx.lineTo(c.x, c.y);
2667
+ break;
2668
+ case "bezierCurveTo":
2669
+ ctx.bezierCurveTo(c.cp1x, c.cp1y, c.cp2x, c.cp2y, c.x, c.y);
2670
+ break;
2671
+ case "closePath":
2672
+ ctx.closePath();
2673
+ break;
2674
+ }
2675
+ }
2676
+ }
2677
+ function applyClipShapes(ctx, shapes) {
2678
+ for (const s of shapes) {
2679
+ ctx.beginPath();
2680
+ replayClipCmds(ctx, s.cmds);
2681
+ try {
2682
+ ctx.clip(s.fillRule);
2683
+ } catch {
2684
+ }
2685
+ }
2686
+ }
2687
+ function reapplyClipRegion(holder, region, identityTransform = false) {
2688
+ const { ctx } = holder;
2689
+ while (holder.clipSaveDepth > 0) {
2690
+ ctx.restore();
2691
+ holder.clipSaveDepth--;
2692
+ }
2693
+ if (region) {
2694
+ ctx.save();
2695
+ holder.clipSaveDepth = 1;
2696
+ if (identityTransform) {
2697
+ ctx.setTransform(1, 0, 0, 1, 0, 0);
2698
+ }
2699
+ applyClipShapes(ctx, region);
2700
+ }
2701
+ }
2294
2702
  function decodeRleBitmap(view, bitsOffset, bitsSize, width, height, _topDown, isRle4, colorTable, out, setPixel) {
2295
2703
  let x = 0;
2296
2704
  let y = height - 1;
@@ -2711,86 +3119,134 @@ function handleStretchDibits(rCtx, offset, dataOff, recSize) {
2711
3119
  }
2712
3120
  return true;
2713
3121
  }
3122
+ function gdiCombineClip(rCtx, shape, op) {
3123
+ const { ctx } = rCtx;
3124
+ if (rCtx.clipUntracked && op !== "replace") {
3125
+ switch (op) {
3126
+ case "intersect":
3127
+ case "complement": {
3128
+ ctx.save();
3129
+ rCtx.clipSaveDepth++;
3130
+ applyClipShapes(ctx, [shape]);
3131
+ return;
3132
+ }
3133
+ case "exclude":
3134
+ case "xor": {
3135
+ const inv = combineClip(null, shape, "exclude");
3136
+ ctx.save();
3137
+ rCtx.clipSaveDepth++;
3138
+ applyClipShapes(ctx, inv.region ?? [shape]);
3139
+ return;
3140
+ }
3141
+ case "union":
3142
+ return;
3143
+ }
3144
+ }
3145
+ const res = combineClip(op === "replace" ? null : rCtx.clipRegion ?? null, shape, op);
3146
+ if (!res.exact) ;
3147
+ rCtx.clipRegion = res.region;
3148
+ rCtx.clipUntracked = false;
3149
+ reapplyClipRegion(rCtx, res.region);
3150
+ }
3151
+ function readClipRectShape(rCtx, dataOff) {
3152
+ const { view } = rCtx;
3153
+ const left = view.getInt32(dataOff, true);
3154
+ const top = view.getInt32(dataOff + 4, true);
3155
+ const right = view.getInt32(dataOff + 8, true);
3156
+ const bottom = view.getInt32(dataOff + 12, true);
3157
+ return rectClipShape(
3158
+ gmx(rCtx, left),
3159
+ gmy(rCtx, top),
3160
+ gmw(rCtx, right - left),
3161
+ gmh(rCtx, bottom - top)
3162
+ );
3163
+ }
2714
3164
  function handleIntersectClipRect(rCtx, dataOff, recSize) {
2715
- const { ctx, view } = rCtx;
2716
3165
  if (recSize >= 24) {
2717
- const left = view.getInt32(dataOff, true);
2718
- const top = view.getInt32(dataOff + 4, true);
2719
- const right = view.getInt32(dataOff + 8, true);
2720
- const bottom = view.getInt32(dataOff + 12, true);
2721
- ctx.save();
2722
- rCtx.clipSaveDepth++;
2723
- ctx.beginPath();
2724
- ctx.rect(gmx(rCtx, left), gmy(rCtx, top), gmw(rCtx, right - left), gmh(rCtx, bottom - top));
2725
- try {
2726
- ctx.clip();
2727
- } catch {
2728
- }
3166
+ gdiCombineClip(rCtx, readClipRectShape(rCtx, dataOff), "intersect");
2729
3167
  }
2730
3168
  return true;
2731
3169
  }
3170
+ function handleExcludeClipRect(rCtx, dataOff, recSize) {
3171
+ if (recSize >= 24) {
3172
+ gdiCombineClip(rCtx, readClipRectShape(rCtx, dataOff), "exclude");
3173
+ }
3174
+ return true;
3175
+ }
3176
+ var RGN_MODE_OPS = {
3177
+ 1: "intersect",
3178
+ // RGN_AND
3179
+ 2: "union",
3180
+ // RGN_OR
3181
+ 3: "xor",
3182
+ // RGN_XOR
3183
+ 4: "exclude",
3184
+ // RGN_DIFF
3185
+ 5: "replace"
3186
+ // RGN_COPY
3187
+ };
2732
3188
  function handleExtSelectClipRgn(rCtx, dataOff, recSize) {
2733
- const { ctx, view } = rCtx;
3189
+ const { view } = rCtx;
2734
3190
  if (recSize < 16) {
2735
3191
  return true;
2736
3192
  }
2737
3193
  const cbRgnData = view.getUint32(dataOff, true);
2738
3194
  const iMode = view.getUint32(dataOff + 4, true);
2739
- if (iMode === 5) {
2740
- if (cbRgnData === 0) {
2741
- while (rCtx.clipSaveDepth > 0) {
2742
- ctx.restore();
2743
- rCtx.clipSaveDepth--;
2744
- }
2745
- return true;
2746
- }
2747
- const rgnStart = dataOff + 8;
2748
- if (cbRgnData < 32) {
2749
- return true;
2750
- }
2751
- const nCount = view.getUint32(rgnStart + 8, true);
2752
- if (nCount === 0) {
2753
- return true;
2754
- }
2755
- while (rCtx.clipSaveDepth > 0) {
2756
- ctx.restore();
2757
- rCtx.clipSaveDepth--;
2758
- }
2759
- ctx.save();
2760
- rCtx.clipSaveDepth++;
2761
- ctx.beginPath();
2762
- const rectsStart = rgnStart + 32;
2763
- for (let i = 0; i < nCount; i++) {
2764
- const rOff = rectsStart + i * 16;
2765
- if (rOff + 16 > dataOff + 8 + cbRgnData) {
2766
- break;
2767
- }
2768
- const left = view.getInt32(rOff, true);
2769
- const top = view.getInt32(rOff + 4, true);
2770
- const right = view.getInt32(rOff + 8, true);
2771
- const bottom = view.getInt32(rOff + 12, true);
2772
- ctx.rect(gmx(rCtx, left), gmy(rCtx, top), gmw(rCtx, right - left), gmh(rCtx, bottom - top));
3195
+ const op = RGN_MODE_OPS[iMode];
3196
+ if (!op) {
3197
+ return true;
3198
+ }
3199
+ if (cbRgnData === 0) {
3200
+ if (op === "replace") {
3201
+ rCtx.clipRegion = null;
3202
+ rCtx.clipUntracked = false;
3203
+ reapplyClipRegion(rCtx, null);
2773
3204
  }
2774
- try {
2775
- ctx.clip();
2776
- } catch {
3205
+ return true;
3206
+ }
3207
+ const rgnStart = dataOff + 8;
3208
+ if (cbRgnData < 32) {
3209
+ return true;
3210
+ }
3211
+ const nCount = view.getUint32(rgnStart + 8, true);
3212
+ if (nCount === 0) {
3213
+ return true;
3214
+ }
3215
+ const rects = [];
3216
+ const rectsStart = rgnStart + 32;
3217
+ for (let i = 0; i < nCount; i++) {
3218
+ const rOff = rectsStart + i * 16;
3219
+ if (rOff + 16 > dataOff + 8 + cbRgnData) {
3220
+ break;
2777
3221
  }
3222
+ const left = view.getInt32(rOff, true);
3223
+ const top = view.getInt32(rOff + 4, true);
3224
+ const right = view.getInt32(rOff + 8, true);
3225
+ const bottom = view.getInt32(rOff + 12, true);
3226
+ rects.push({
3227
+ x: gmx(rCtx, left),
3228
+ y: gmy(rCtx, top),
3229
+ w: gmw(rCtx, right - left),
3230
+ h: gmh(rCtx, bottom - top)
3231
+ });
2778
3232
  }
2779
- return true;
2780
- }
2781
- function handleExcludeClipRect(rCtx, dataOff, recSize) {
2782
- if (recSize >= 24) {
2783
- rCtx.view.getInt32(dataOff, true);
2784
- rCtx.view.getInt32(dataOff + 4, true);
2785
- rCtx.view.getInt32(dataOff + 8, true);
2786
- rCtx.view.getInt32(dataOff + 12, true);
3233
+ if (rects.length === 0) {
3234
+ return true;
2787
3235
  }
3236
+ gdiCombineClip(rCtx, rectsClipShape(rects), op);
2788
3237
  return true;
2789
3238
  }
2790
3239
  function handleOffsetClipRgn(rCtx, dataOff, recSize) {
2791
3240
  if (recSize >= 16) {
2792
- rCtx.view.getInt32(dataOff, true);
2793
- rCtx.view.getInt32(dataOff + 4, true);
3241
+ const dx = rCtx.view.getInt32(dataOff, true);
3242
+ const dy = rCtx.view.getInt32(dataOff + 4, true);
3243
+ if (rCtx.clipUntracked) {
3244
+ return true;
3245
+ }
3246
+ if (rCtx.clipRegion) {
3247
+ rCtx.clipRegion = translateClipRegion(rCtx.clipRegion, gmw(rCtx, dx), gmh(rCtx, dy));
3248
+ reapplyClipRegion(rCtx, rCtx.clipRegion);
3249
+ }
2794
3250
  }
2795
3251
  return true;
2796
3252
  }
@@ -3107,14 +3563,12 @@ function handleEmfGdiPolyPathRecord(rCtx, recType, offset, dataOff, recSize) {
3107
3563
  ctx.restore();
3108
3564
  rCtx.clipSaveDepth--;
3109
3565
  }
3110
- ctx.save();
3111
- rCtx.clipSaveDepth++;
3112
- ctx.clip();
3113
- } else {
3114
- ctx.save();
3115
- rCtx.clipSaveDepth++;
3116
- ctx.clip();
3117
3566
  }
3567
+ ctx.save();
3568
+ rCtx.clipSaveDepth++;
3569
+ ctx.clip(state.polyFillMode === 2 ? "nonzero" : "evenodd");
3570
+ rCtx.clipRegion = null;
3571
+ rCtx.clipUntracked = true;
3118
3572
  } catch {
3119
3573
  }
3120
3574
  return true;
@@ -3404,7 +3858,9 @@ function createEmfPlusState() {
3404
3858
  objectTable: /* @__PURE__ */ new Map(),
3405
3859
  worldTransform: [1, 0, 0, 1, 0, 0],
3406
3860
  saveStack: [],
3407
- saveIdMap: /* @__PURE__ */ new Map()
3861
+ saveIdMap: /* @__PURE__ */ new Map(),
3862
+ clipRegion: null,
3863
+ clipSaveDepth: 0
3408
3864
  };
3409
3865
  }
3410
3866
  function handleEmfGdiStateRecord(rCtx, recType, _offset, dataOff, recSize) {
@@ -3422,8 +3878,19 @@ function handleEmfGdiStateRecord(rCtx, recType, _offset, dataOff, recSize) {
3422
3878
  ctx.restore();
3423
3879
  rCtx.clipSaveDepth--;
3424
3880
  }
3881
+ rCtx.clipStack ?? (rCtx.clipStack = []);
3882
+ rCtx.clipStack.push({
3883
+ region: rCtx.clipRegion ?? null,
3884
+ untracked: rCtx.clipUntracked ?? false
3885
+ });
3425
3886
  rCtx.stateStack.push(cloneState(state));
3426
3887
  ctx.save();
3888
+ if (rCtx.clipUntracked) {
3889
+ rCtx.clipUntracked = false;
3890
+ rCtx.clipRegion = null;
3891
+ } else if (rCtx.clipRegion) {
3892
+ reapplyClipRegion(rCtx, rCtx.clipRegion);
3893
+ }
3427
3894
  return true;
3428
3895
  }
3429
3896
  case EMR_RESTOREDC: {
@@ -3434,16 +3901,23 @@ function handleEmfGdiStateRecord(rCtx, recType, _offset, dataOff, recSize) {
3434
3901
  }
3435
3902
  let rel = view.getInt32(dataOff, true);
3436
3903
  if (rel < 0) {
3437
- rel = rCtx.stateStack.length + rel;
3904
+ rel = rCtx.stateStack.length + rel + 1;
3438
3905
  }
3439
3906
  while (rCtx.stateStack.length > rel && rCtx.stateStack.length > 0) {
3440
3907
  rCtx.stateStack.pop();
3908
+ rCtx.clipStack?.pop();
3441
3909
  ctx.restore();
3442
3910
  }
3443
3911
  const restored = rCtx.stateStack.pop();
3444
3912
  if (restored) {
3913
+ const clipSnapshot = rCtx.clipStack?.pop();
3445
3914
  Object.assign(state, restored);
3446
3915
  ctx.restore();
3916
+ rCtx.clipRegion = clipSnapshot?.region ?? null;
3917
+ rCtx.clipUntracked = false;
3918
+ if (rCtx.clipRegion) {
3919
+ reapplyClipRegion(rCtx, rCtx.clipRegion);
3920
+ }
3447
3921
  }
3448
3922
  }
3449
3923
  return true;
@@ -3558,6 +4032,47 @@ function parseEmfPlusPath(data, off, maxLen) {
3558
4032
  const types = new Uint8Array(data.buffer, data.byteOffset + alignedPOff, pointCount);
3559
4033
  return { kind: "plus-path", points: points3, types: new Uint8Array(types) };
3560
4034
  }
4035
+ function emfPlusPathToClipCmds(path, m) {
4036
+ const tx = (x, y) => m[0] * x + m[2] * y + m[4];
4037
+ const ty = (x, y) => m[1] * x + m[3] * y + m[5];
4038
+ const cmds = [];
4039
+ const pts = path.points;
4040
+ const types = path.types;
4041
+ let i = 0;
4042
+ while (i < pts.length) {
4043
+ const t = types[i] & 15;
4044
+ const close = (types[i] & 128) !== 0;
4045
+ if (t === 0) {
4046
+ cmds.push({ op: "moveTo", x: tx(pts[i].x, pts[i].y), y: ty(pts[i].x, pts[i].y) });
4047
+ i++;
4048
+ } else if (t === 3) {
4049
+ if (i + 2 < pts.length) {
4050
+ cmds.push({
4051
+ op: "bezierCurveTo",
4052
+ cp1x: tx(pts[i].x, pts[i].y),
4053
+ cp1y: ty(pts[i].x, pts[i].y),
4054
+ cp2x: tx(pts[i + 1].x, pts[i + 1].y),
4055
+ cp2y: ty(pts[i + 1].x, pts[i + 1].y),
4056
+ x: tx(pts[i + 2].x, pts[i + 2].y),
4057
+ y: ty(pts[i + 2].x, pts[i + 2].y)
4058
+ });
4059
+ if ((types[i + 2] & 128) !== 0) {
4060
+ cmds.push({ op: "closePath" });
4061
+ }
4062
+ i += 3;
4063
+ continue;
4064
+ }
4065
+ break;
4066
+ } else {
4067
+ cmds.push({ op: "lineTo", x: tx(pts[i].x, pts[i].y), y: ty(pts[i].x, pts[i].y) });
4068
+ i++;
4069
+ }
4070
+ if (close) {
4071
+ cmds.push({ op: "closePath" });
4072
+ }
4073
+ }
4074
+ return cmds;
4075
+ }
3561
4076
  function replayEmfPlusPath(ctx, path) {
3562
4077
  ctx.beginPath();
3563
4078
  const pts = path.points;
@@ -3609,12 +4124,44 @@ function multiplyMatrix(m1, m2) {
3609
4124
  m1[4] * m2[1] + m1[5] * m2[3] + m2[5]
3610
4125
  ];
3611
4126
  }
3612
- function resolveBrushColor(rCtx, flags, brushIdOrColor) {
4127
+ function createBrushGradient(rCtx, grad) {
4128
+ const ctx = rCtx.ctx;
4129
+ try {
4130
+ let g = null;
4131
+ if (grad.type === "linear" && typeof ctx.createLinearGradient === "function") {
4132
+ if (grad.x1 === grad.x2 && grad.y1 === grad.y2) {
4133
+ return null;
4134
+ }
4135
+ g = ctx.createLinearGradient(grad.x1, grad.y1, grad.x2, grad.y2);
4136
+ } else if (grad.type === "radial" && typeof ctx.createRadialGradient === "function") {
4137
+ if (!(grad.r > 0)) {
4138
+ return null;
4139
+ }
4140
+ g = ctx.createRadialGradient(grad.cx, grad.cy, 0, grad.cx, grad.cy, grad.r);
4141
+ }
4142
+ if (!g) {
4143
+ return null;
4144
+ }
4145
+ for (const stop of grad.stops) {
4146
+ g.addColorStop(stop.offset, stop.color);
4147
+ }
4148
+ return g;
4149
+ } catch {
4150
+ return null;
4151
+ }
4152
+ }
4153
+ function resolveBrushPaint(rCtx, flags, brushIdOrColor) {
3613
4154
  if (flags & 32768) {
3614
4155
  return argbToRgba(brushIdOrColor);
3615
4156
  }
3616
4157
  const obj = rCtx.objectTable.get(brushIdOrColor & 255);
3617
4158
  if (obj && obj.kind === "plus-brush") {
4159
+ if (obj.gradient) {
4160
+ const g = createBrushGradient(rCtx, obj.gradient);
4161
+ if (g) {
4162
+ return g;
4163
+ }
4164
+ }
3618
4165
  return obj.color;
3619
4166
  }
3620
4167
  return "rgba(0,0,0,1)";
@@ -3678,88 +4225,83 @@ function popState(rCtx, stackId) {
3678
4225
  rCtx.saveIdMap = newMap;
3679
4226
  }
3680
4227
  }
3681
- function ensureClipSave(rCtx) {
3682
- if (rCtx.clipSaveDepth === 0) {
3683
- rCtx.ctx.save();
3684
- rCtx.clipSaveDepth = 1;
3685
- }
3686
- }
3687
- function resetClipState(rCtx) {
3688
- if (rCtx.clipSaveDepth > 0) {
3689
- rCtx.ctx.restore();
3690
- rCtx.clipSaveDepth = 0;
3691
- }
3692
- }
3693
- function applyClipCombineMode(rCtx, combineMode, opName, clipFn) {
3694
- switch (combineMode) {
3695
- case 0:
3696
- resetClipState(rCtx);
3697
- ensureClipSave(rCtx);
3698
- clipFn();
3699
- return true;
3700
- case 1:
3701
- ensureClipSave(rCtx);
3702
- clipFn();
3703
- return true;
3704
- case 2:
3705
- ensureClipSave(rCtx);
3706
- clipFn();
3707
- return true;
3708
- case 3:
3709
- return false;
3710
- case 4:
3711
- return false;
3712
- case 5:
3713
- return false;
3714
- default:
3715
- ensureClipSave(rCtx);
3716
- clipFn();
3717
- return true;
3718
- }
3719
- }
3720
- var MAX_REGION_TRACE_DEPTH = 64;
3721
- function traceRegionNodePath(ctx, node, depth = 0) {
3722
- if (depth > MAX_REGION_TRACE_DEPTH) {
3723
- ctx.rect(0, 0, 0, 0);
3724
- return;
4228
+ function plusDeviceMatrix(rCtx) {
4229
+ const wt = rCtx.worldTransform;
4230
+ const s = getPageUnitMultiplier(rCtx.pageUnit, rCtx.pageScale) * rCtx.dpiScale;
4231
+ return [wt[0] * s, wt[1] * s, wt[2] * s, wt[3] * s, wt[4] * s, wt[5] * s];
4232
+ }
4233
+ function transformedRectShape(x, y, w, h, m) {
4234
+ const tx = (px2, py) => m[0] * px2 + m[2] * py + m[4];
4235
+ const ty = (px2, py) => m[1] * px2 + m[3] * py + m[5];
4236
+ const cmds = [
4237
+ { op: "moveTo", x: tx(x, y), y: ty(x, y) },
4238
+ { op: "lineTo", x: tx(x + w, y), y: ty(x + w, y) },
4239
+ { op: "lineTo", x: tx(x + w, y + h), y: ty(x + w, y + h) },
4240
+ { op: "lineTo", x: tx(x, y + h), y: ty(x, y + h) },
4241
+ { op: "closePath" }
4242
+ ];
4243
+ return { cmds, fillRule: "nonzero", simple: true };
4244
+ }
4245
+ function pathClipShape(path, m) {
4246
+ return { cmds: emfPlusPathToClipCmds(path.path, m), fillRule: "nonzero", simple: true };
4247
+ }
4248
+ var REGION_NODE_OPS = {
4249
+ 0: "intersect",
4250
+ // legacy/lenient: treat 0 as And
4251
+ 1: "intersect",
4252
+ // RegionNodeDataTypeAnd
4253
+ 2: "union",
4254
+ // RegionNodeDataTypeOr
4255
+ 3: "xor",
4256
+ // RegionNodeDataTypeXor
4257
+ 4: "exclude",
4258
+ // RegionNodeDataTypeExclude
4259
+ 5: "complement"
4260
+ // RegionNodeDataTypeComplement
4261
+ };
4262
+ var MAX_REGION_FLATTEN_DEPTH = 64;
4263
+ function flattenRegionNode(node, m, depth = 0) {
4264
+ if (depth > MAX_REGION_FLATTEN_DEPTH) {
4265
+ return { region: [emptyClipShape()], exact: false };
3725
4266
  }
3726
4267
  switch (node.type) {
3727
4268
  case "rect":
3728
- ctx.rect(node.x, node.y, node.width, node.height);
3729
- break;
4269
+ return { region: [transformedRectShape(node.x, node.y, node.width, node.height, m)], exact: true };
3730
4270
  case "path":
3731
- replayEmfPlusPath(ctx, node.path);
3732
- break;
4271
+ return { region: [pathClipShape(node, m)], exact: true };
3733
4272
  case "infinite":
3734
- ctx.rect(-1e6, -1e6, 2e6, 2e6);
3735
- break;
4273
+ return { region: null, exact: true };
3736
4274
  case "empty":
3737
- ctx.rect(0, 0, 0, 0);
3738
- break;
3739
- case "combine":
3740
- traceRegionNodePath(ctx, node.left, depth + 1);
3741
- if (node.combineMode !== 0) {
3742
- emfWarn(
3743
- `traceRegionNodePath: combine mode ${node.combineMode} not fully supported, using left subtree only`
3744
- );
3745
- } else {
3746
- try {
3747
- ctx.clip();
3748
- } catch {
3749
- }
3750
- ctx.beginPath();
3751
- traceRegionNodePath(ctx, node.right, depth + 1);
3752
- }
3753
- break;
3754
- }
4275
+ return { region: [emptyClipShape()], exact: true };
4276
+ case "combine": {
4277
+ const left = flattenRegionNode(node.left, m, depth + 1);
4278
+ const right = flattenRegionNode(node.right, m, depth + 1);
4279
+ const op = REGION_NODE_OPS[node.combineMode] ?? "intersect";
4280
+ const combined = combineClipRegions(left.region, right.region, op);
4281
+ return { region: combined.region, exact: combined.exact && left.exact && right.exact };
4282
+ }
4283
+ }
4284
+ }
4285
+ var PLUS_COMBINE_OPS = {
4286
+ 0: "replace",
4287
+ 1: "intersect",
4288
+ 2: "union",
4289
+ 3: "xor",
4290
+ 4: "exclude",
4291
+ 5: "complement"
4292
+ };
4293
+ function reapplyPlusClip(rCtx) {
4294
+ reapplyClipRegion(rCtx, rCtx.clipRegion ?? null, true);
3755
4295
  }
3756
- function applyRegionNodeClip(ctx, rootNode, combineMode) {
3757
- ctx.beginPath();
3758
- traceRegionNodePath(ctx, rootNode);
3759
- try {
3760
- ctx.clip();
3761
- } catch {
3762
- }
4296
+ function applyPlusClipRegion(rCtx, incoming, combineMode, opName) {
4297
+ const op = PLUS_COMBINE_OPS[combineMode];
4298
+ const res = combineClipRegions(rCtx.clipRegion ?? null, incoming, op ?? "intersect");
4299
+ if (!res.exact) ;
4300
+ rCtx.clipRegion = res.region;
4301
+ reapplyPlusClip(rCtx);
4302
+ }
4303
+ function applyPlusClipShape(rCtx, shape, combineMode, opName) {
4304
+ applyPlusClipRegion(rCtx, [shape], combineMode);
3763
4305
  }
3764
4306
  function handleEmfPlusStateRecord(rCtx, recType, recFlags, dataOff, recDataSize) {
3765
4307
  const { view } = rCtx;
@@ -3861,30 +4403,24 @@ function handleEmfPlusStateRecord(rCtx, recType, recFlags, dataOff, recDataSize)
3861
4403
  const cy = view.getFloat32(dataOff + 4, true);
3862
4404
  const cw = view.getFloat32(dataOff + 8, true);
3863
4405
  const ch = view.getFloat32(dataOff + 12, true);
3864
- applyClipCombineMode(rCtx, combineMode, "SetClipRect", () => {
3865
- applyPlusWorldTransform(rCtx);
3866
- rCtx.ctx.beginPath();
3867
- rCtx.ctx.rect(cx, cy, cw, ch);
3868
- try {
3869
- rCtx.ctx.clip();
3870
- } catch {
3871
- }
3872
- });
4406
+ const shape = transformedRectShape(cx, cy, cw, ch, plusDeviceMatrix(rCtx));
4407
+ applyPlusClipShape(rCtx, shape, combineMode);
3873
4408
  }
3874
4409
  return true;
3875
4410
  }
3876
4411
  case EMFPLUS_RESETCLIP: {
3877
- resetClipState(rCtx);
3878
- ensureClipSave(rCtx);
4412
+ rCtx.clipRegion = null;
4413
+ reapplyPlusClip(rCtx);
3879
4414
  return true;
3880
4415
  }
3881
4416
  case EMFPLUS_SETCLIPREGION: {
3882
4417
  const regionId = recFlags & 255;
4418
+ const combineMode = recFlags >> 8 & 15;
3883
4419
  const regionObj = rCtx.objectTable.get(regionId);
3884
4420
  if (regionObj && regionObj.kind === "plus-region" && regionObj.nodes.length > 0) {
3885
- applyPlusWorldTransform(rCtx);
3886
- const rootNode = regionObj.nodes[0];
3887
- applyRegionNodeClip(rCtx.ctx, rootNode);
4421
+ const flattened = flattenRegionNode(regionObj.nodes[0], plusDeviceMatrix(rCtx));
4422
+ if (!flattened.exact) ;
4423
+ applyPlusClipRegion(rCtx, flattened.region, combineMode);
3888
4424
  }
3889
4425
  return true;
3890
4426
  }
@@ -3893,22 +4429,26 @@ function handleEmfPlusStateRecord(rCtx, recType, recFlags, dataOff, recDataSize)
3893
4429
  const combineMode = recFlags >> 8 & 15;
3894
4430
  const pathObj = rCtx.objectTable.get(pathId);
3895
4431
  if (pathObj && pathObj.kind === "plus-path") {
3896
- applyClipCombineMode(rCtx, combineMode, "SetClipPath", () => {
3897
- applyPlusWorldTransform(rCtx);
3898
- rCtx.ctx.beginPath();
3899
- replayEmfPlusPath(rCtx.ctx, pathObj);
3900
- try {
3901
- rCtx.ctx.clip();
3902
- } catch {
3903
- }
3904
- });
4432
+ const shape = {
4433
+ cmds: emfPlusPathToClipCmds(pathObj, plusDeviceMatrix(rCtx)),
4434
+ fillRule: "nonzero",
4435
+ simple: true
4436
+ };
4437
+ applyPlusClipShape(rCtx, shape, combineMode);
3905
4438
  }
3906
4439
  return true;
3907
4440
  }
3908
4441
  case EMFPLUS_OFFSETCLIP: {
3909
4442
  if (recDataSize >= 8) {
3910
- view.getFloat32(dataOff, true);
3911
- view.getFloat32(dataOff + 4, true);
4443
+ const dx = view.getFloat32(dataOff, true);
4444
+ const dy = view.getFloat32(dataOff + 4, true);
4445
+ if (rCtx.clipRegion) {
4446
+ const m = plusDeviceMatrix(rCtx);
4447
+ const ddx = m[0] * dx + m[2] * dy;
4448
+ const ddy = m[1] * dx + m[3] * dy;
4449
+ rCtx.clipRegion = translateClipRegion(rCtx.clipRegion, ddx, ddy);
4450
+ reapplyPlusClip(rCtx);
4451
+ }
3912
4452
  }
3913
4453
  return true;
3914
4454
  }
@@ -3979,7 +4519,7 @@ function handleEmfPlusDrawRecord(rCtx, recType, recFlags, dataOff, recDataSize)
3979
4519
  const count = view.getUint32(dataOff + 4, true);
3980
4520
  const compressed = (recFlags & 16384) !== 0;
3981
4521
  const rectSize = compressed ? 8 : 16;
3982
- ctx.fillStyle = resolveBrushColor(rCtx, recFlags, brushVal);
4522
+ ctx.fillStyle = resolveBrushPaint(rCtx, recFlags, brushVal);
3983
4523
  applyPlusWorldTransform(rCtx);
3984
4524
  let rOff = dataOff + 8;
3985
4525
  for (let i = 0; i < count && rOff + rectSize <= dataOff + recDataSize; i++) {
@@ -4029,7 +4569,7 @@ function handleEmfPlusDrawRecord(rCtx, recType, recFlags, dataOff, recDataSize)
4029
4569
  w = view.getFloat32(dataOff + 12, true);
4030
4570
  h = view.getFloat32(dataOff + 16, true);
4031
4571
  }
4032
- ctx.fillStyle = resolveBrushColor(rCtx, recFlags, brushVal);
4572
+ ctx.fillStyle = resolveBrushPaint(rCtx, recFlags, brushVal);
4033
4573
  applyPlusWorldTransform(rCtx);
4034
4574
  ctx.beginPath();
4035
4575
  ctx.ellipse(x + w / 2, y + h / 2, Math.abs(w) / 2, Math.abs(h) / 2, 0, 0, Math.PI * 2);
@@ -4075,7 +4615,7 @@ function handleEmfPlusDrawRecord(rCtx, recType, recFlags, dataOff, recDataSize)
4075
4615
  let aOff = dataOff;
4076
4616
  if (isFill) {
4077
4617
  const brushVal = view.getUint32(aOff, true);
4078
- ctx.fillStyle = resolveBrushColor(rCtx, recFlags, brushVal);
4618
+ ctx.fillStyle = resolveBrushPaint(rCtx, recFlags, brushVal);
4079
4619
  aOff += 4;
4080
4620
  }
4081
4621
  const startAngle = view.getFloat32(aOff, true) * Math.PI / 180;
@@ -4156,7 +4696,7 @@ function handleEmfPlusDrawRecord(rCtx, recType, recFlags, dataOff, recDataSize)
4156
4696
  const count = view.getUint32(dataOff + 4, true);
4157
4697
  const compressed = (recFlags & 16384) !== 0;
4158
4698
  const ptSize = compressed ? 4 : 8;
4159
- ctx.fillStyle = resolveBrushColor(rCtx, recFlags, brushVal);
4699
+ ctx.fillStyle = resolveBrushPaint(rCtx, recFlags, brushVal);
4160
4700
  applyPlusWorldTransform(rCtx);
4161
4701
  ctx.beginPath();
4162
4702
  let pOff = dataOff + 8;
@@ -4178,6 +4718,259 @@ function handleEmfPlusDrawRecord(rCtx, recType, recFlags, dataOff, recDataSize)
4178
4718
  return false;
4179
4719
  }
4180
4720
  }
4721
+ var BRUSH_DATA_PATH = 1;
4722
+ var BRUSH_DATA_TRANSFORM = 2;
4723
+ var BRUSH_DATA_PRESET_COLORS = 4;
4724
+ var BRUSH_DATA_BLEND_FACTORS_H = 8;
4725
+ var MAX_GRADIENT_ELEMENTS = 4096;
4726
+ function looksLikeGraphicsVersion(v) {
4727
+ return v >>> 12 === 900097;
4728
+ }
4729
+ function readTransform(view, off) {
4730
+ return [
4731
+ view.getFloat32(off, true),
4732
+ view.getFloat32(off + 4, true),
4733
+ view.getFloat32(off + 8, true),
4734
+ view.getFloat32(off + 12, true),
4735
+ view.getFloat32(off + 16, true),
4736
+ view.getFloat32(off + 20, true)
4737
+ ];
4738
+ }
4739
+ function applyMatrix(m, x, y) {
4740
+ return { x: m[0] * x + m[2] * y + m[4], y: m[1] * x + m[3] * y + m[5] };
4741
+ }
4742
+ function clamp01(v) {
4743
+ return Number.isFinite(v) ? Math.min(1, Math.max(0, v)) : 0;
4744
+ }
4745
+ function normaliseStops(stops) {
4746
+ return stops.map((s) => ({ offset: clamp01(s.offset), color: s.color })).sort((a, b) => a.offset - b.offset);
4747
+ }
4748
+ function readPresetColors(view, off, end) {
4749
+ if (off + 4 > end) {
4750
+ return null;
4751
+ }
4752
+ const count = view.getUint32(off, true);
4753
+ if (count === 0 || count > MAX_GRADIENT_ELEMENTS) {
4754
+ return null;
4755
+ }
4756
+ const posOff = off + 4;
4757
+ const colOff = posOff + count * 4;
4758
+ const next = colOff + count * 4;
4759
+ if (next > end) {
4760
+ return null;
4761
+ }
4762
+ const stops = [];
4763
+ for (let i = 0; i < count; i++) {
4764
+ stops.push({
4765
+ offset: view.getFloat32(posOff + i * 4, true),
4766
+ color: argbToRgba(view.getUint32(colOff + i * 4, true))
4767
+ });
4768
+ }
4769
+ return { stops, next };
4770
+ }
4771
+ function readBlendFactors(view, off, end) {
4772
+ if (off + 4 > end) {
4773
+ return null;
4774
+ }
4775
+ const count = view.getUint32(off, true);
4776
+ if (count === 0 || count > MAX_GRADIENT_ELEMENTS) {
4777
+ return null;
4778
+ }
4779
+ const posOff = off + 4;
4780
+ const facOff = posOff + count * 4;
4781
+ const next = facOff + count * 4;
4782
+ if (next > end) {
4783
+ return null;
4784
+ }
4785
+ const entries = [];
4786
+ for (let i = 0; i < count; i++) {
4787
+ entries.push({
4788
+ pos: view.getFloat32(posOff + i * 4, true),
4789
+ factor: view.getFloat32(facOff + i * 4, true)
4790
+ });
4791
+ }
4792
+ return { entries, next };
4793
+ }
4794
+ function parseLinearGradient(view, b, end) {
4795
+ if (b + 40 > end) {
4796
+ return null;
4797
+ }
4798
+ const flags = view.getUint32(b, true);
4799
+ const rx = view.getFloat32(b + 8, true);
4800
+ const ry = view.getFloat32(b + 12, true);
4801
+ const rw = view.getFloat32(b + 16, true);
4802
+ const rh = view.getFloat32(b + 20, true);
4803
+ const startArgb = view.getUint32(b + 24, true);
4804
+ const endArgb = view.getUint32(b + 28, true);
4805
+ let o = b + 40;
4806
+ let transform = null;
4807
+ if (flags & BRUSH_DATA_TRANSFORM && o + 24 <= end) {
4808
+ transform = readTransform(view, o);
4809
+ o += 24;
4810
+ }
4811
+ let stops = [
4812
+ { offset: 0, color: argbToRgba(startArgb) },
4813
+ { offset: 1, color: argbToRgba(endArgb) }
4814
+ ];
4815
+ if (flags & BRUSH_DATA_PRESET_COLORS) {
4816
+ const preset = readPresetColors(view, o, end);
4817
+ if (preset) {
4818
+ stops = preset.stops;
4819
+ }
4820
+ } else if (flags & BRUSH_DATA_BLEND_FACTORS_H) {
4821
+ const blend = readBlendFactors(view, o, end);
4822
+ if (blend) {
4823
+ stops = blend.entries.map((e) => ({
4824
+ offset: e.pos,
4825
+ color: lerpArgbToRgba(startArgb, endArgb, e.factor)
4826
+ }));
4827
+ }
4828
+ }
4829
+ let p1 = { x: rx, y: ry + rh / 2 };
4830
+ let p2 = { x: rx + rw, y: ry + rh / 2 };
4831
+ if (transform) {
4832
+ p1 = applyMatrix(transform, p1.x, p1.y);
4833
+ p2 = applyMatrix(transform, p2.x, p2.y);
4834
+ }
4835
+ emfLog(
4836
+ `parseEmfPlusBrushObject: linear gradient (${p1.x.toFixed(1)},${p1.y.toFixed(1)})\u2192(${p2.x.toFixed(1)},${p2.y.toFixed(1)}), ${stops.length} stop(s)`
4837
+ );
4838
+ return {
4839
+ kind: "plus-brush",
4840
+ color: argbToRgba(startArgb),
4841
+ gradient: {
4842
+ type: "linear",
4843
+ x1: p1.x,
4844
+ y1: p1.y,
4845
+ x2: p2.x,
4846
+ y2: p2.y,
4847
+ stops: normaliseStops(stops)
4848
+ }
4849
+ };
4850
+ }
4851
+ function parsePathGradient(view, b, end) {
4852
+ if (b + 24 > end) {
4853
+ return null;
4854
+ }
4855
+ const flags = view.getUint32(b, true);
4856
+ const centerArgb = view.getUint32(b + 8, true);
4857
+ let cx = view.getFloat32(b + 12, true);
4858
+ let cy = view.getFloat32(b + 16, true);
4859
+ const surroundCount = view.getUint32(b + 20, true);
4860
+ if (surroundCount > MAX_GRADIENT_ELEMENTS) {
4861
+ return { kind: "plus-brush", color: argbToRgba(centerArgb) };
4862
+ }
4863
+ const surround = [];
4864
+ let o = b + 24;
4865
+ for (let i = 0; i < surroundCount && o + 4 <= end; i++) {
4866
+ surround.push(view.getUint32(o, true));
4867
+ o += 4;
4868
+ }
4869
+ let boundaryPts = [];
4870
+ if (flags & BRUSH_DATA_PATH) {
4871
+ if (o + 4 <= end) {
4872
+ const pathSize = view.getInt32(o, true);
4873
+ o += 4;
4874
+ if (pathSize > 0 && o + pathSize <= end) {
4875
+ const path = parseEmfPlusPath(view, o, pathSize);
4876
+ if (path) {
4877
+ boundaryPts = path.points;
4878
+ }
4879
+ o += pathSize;
4880
+ }
4881
+ }
4882
+ } else if (o + 4 <= end) {
4883
+ const ptCount = view.getUint32(o, true);
4884
+ o += 4;
4885
+ if (ptCount > 0 && ptCount <= MAX_GRADIENT_ELEMENTS && o + ptCount * 8 <= end) {
4886
+ for (let i = 0; i < ptCount; i++) {
4887
+ boundaryPts.push({
4888
+ x: view.getFloat32(o + i * 8, true),
4889
+ y: view.getFloat32(o + i * 8 + 4, true)
4890
+ });
4891
+ }
4892
+ o += ptCount * 8;
4893
+ }
4894
+ }
4895
+ let transform = null;
4896
+ if (flags & BRUSH_DATA_TRANSFORM && o + 24 <= end) {
4897
+ transform = readTransform(view, o);
4898
+ o += 24;
4899
+ }
4900
+ if (transform) {
4901
+ ({ x: cx, y: cy } = applyMatrix(transform, cx, cy));
4902
+ boundaryPts = boundaryPts.map((p) => applyMatrix(transform, p.x, p.y));
4903
+ }
4904
+ const surroundArgb = surround.length > 0 ? surround[0] : centerArgb;
4905
+ let stops = [
4906
+ { offset: 0, color: argbToRgba(centerArgb) },
4907
+ { offset: 1, color: argbToRgba(surroundArgb) }
4908
+ ];
4909
+ if (flags & BRUSH_DATA_PRESET_COLORS) {
4910
+ const preset = readPresetColors(view, o, end);
4911
+ if (preset) {
4912
+ stops = preset.stops.map((s) => ({ offset: 1 - s.offset, color: s.color }));
4913
+ }
4914
+ } else if (flags & BRUSH_DATA_BLEND_FACTORS_H) {
4915
+ const blend = readBlendFactors(view, o, end);
4916
+ if (blend) {
4917
+ stops = blend.entries.map((e) => ({
4918
+ offset: 1 - e.pos,
4919
+ color: lerpArgbToRgba(surroundArgb, centerArgb, e.factor)
4920
+ }));
4921
+ }
4922
+ }
4923
+ let r = 0;
4924
+ for (const p of boundaryPts) {
4925
+ const d = Math.hypot(p.x - cx, p.y - cy);
4926
+ if (d > r) {
4927
+ r = d;
4928
+ }
4929
+ }
4930
+ if (!(r > 0)) {
4931
+ return { kind: "plus-brush", color: argbToRgba(centerArgb) };
4932
+ }
4933
+ emfLog(
4934
+ `parseEmfPlusBrushObject: path gradient centre=(${cx.toFixed(1)},${cy.toFixed(1)}), r=${r.toFixed(1)}, ${stops.length} stop(s)`
4935
+ );
4936
+ return {
4937
+ kind: "plus-brush",
4938
+ color: argbToRgba(centerArgb),
4939
+ gradient: { type: "radial", cx, cy, r, stops: normaliseStops(stops) }
4940
+ };
4941
+ }
4942
+ function parseEmfPlusBrushObject(view, dataOff, recDataSize) {
4943
+ if (recDataSize < 8) {
4944
+ return null;
4945
+ }
4946
+ const end = dataOff + recDataSize;
4947
+ const hasVersion = looksLikeGraphicsVersion(view.getUint32(dataOff, true));
4948
+ const typeOff = dataOff + (hasVersion ? 4 : 0);
4949
+ if (typeOff + 8 > end) {
4950
+ return null;
4951
+ }
4952
+ const brushType = view.getUint32(typeOff, true);
4953
+ const b = typeOff + 4;
4954
+ switch (brushType) {
4955
+ case EMFPLUS_BRUSHTYPE_SOLID:
4956
+ return { kind: "plus-brush", color: argbToRgba(view.getUint32(b, true)) };
4957
+ case EMFPLUS_BRUSHTYPE_HATCHFILL:
4958
+ if (b + 8 <= end) {
4959
+ return { kind: "plus-brush", color: argbToRgba(view.getUint32(b + 4, true)) };
4960
+ }
4961
+ return { kind: "plus-brush", color: "rgba(0,0,0,1)" };
4962
+ case EMFPLUS_BRUSHTYPE_LINEARGRADIENT: {
4963
+ const brush = parseLinearGradient(view, b, end);
4964
+ return brush ?? { kind: "plus-brush", color: "rgba(0,0,0,1)" };
4965
+ }
4966
+ case EMFPLUS_BRUSHTYPE_PATHGRADIENT: {
4967
+ const brush = parsePathGradient(view, b, end);
4968
+ return brush ?? { kind: "plus-brush", color: "rgba(0,0,0,1)" };
4969
+ }
4970
+ default:
4971
+ return { kind: "plus-brush", color: "rgba(0,0,0,1)" };
4972
+ }
4973
+ }
4181
4974
  var PIXELFORMAT_24BPP_RGB = 137224;
4182
4975
  var PIXELFORMAT_32BPP_RGB = 139273;
4183
4976
  var PIXELFORMAT_32BPP_ARGB = 2498570;
@@ -4284,7 +5077,8 @@ function parseEmfPlusPenObject(view, dataOff, recDataSize) {
4284
5077
  if (recDataSize < 20) {
4285
5078
  return null;
4286
5079
  }
4287
- const penFlags = view.getUint32(dataOff + 4, true);
5080
+ const hasVersion = looksLikeGraphicsVersion(view.getUint32(dataOff, true));
5081
+ const penFlags = view.getUint32(dataOff + (hasVersion ? 8 : 4), true);
4288
5082
  const penWidth = view.getFloat32(dataOff + 16, true);
4289
5083
  let brushOff = dataOff + 20;
4290
5084
  const flagSizes = [
@@ -4344,9 +5138,9 @@ function parseEmfPlusPenObject(view, dataOff, recDataSize) {
4344
5138
  }
4345
5139
  let penColor = "rgba(0,0,0,1)";
4346
5140
  if (brushOff + 8 <= dataOff + recDataSize) {
4347
- const penBrushType = view.getUint32(brushOff, true);
4348
- if (penBrushType === EMFPLUS_BRUSHTYPE_SOLID && brushOff + 8 <= dataOff + recDataSize) {
4349
- penColor = argbToRgba(view.getUint32(brushOff + 4, true));
5141
+ const brush = parseEmfPlusBrushObject(view, brushOff, dataOff + recDataSize - brushOff);
5142
+ if (brush) {
5143
+ penColor = brush.color;
4350
5144
  }
4351
5145
  }
4352
5146
  return { kind: "plus-pen", color: penColor, width: penWidth || 1, dashStyle };
@@ -4440,19 +5234,9 @@ function handleEmfPlusObjectRecord(rCtx, recFlags, dataOff, recDataSize) {
4440
5234
  // Brush
4441
5235
  // ---------------------------------------------------------------
4442
5236
  case EMFPLUS_OBJECTTYPE_BRUSH: {
4443
- if (recDataSize >= 8) {
4444
- const brushType = view.getUint32(dataOff, true);
4445
- let color2 = "rgba(0,0,0,1)";
4446
- if (brushType === EMFPLUS_BRUSHTYPE_SOLID && recDataSize >= 8) {
4447
- color2 = argbToRgba(view.getUint32(dataOff + 4, true));
4448
- } else if (brushType === EMFPLUS_BRUSHTYPE_LINEARGRADIENT && recDataSize >= 48) {
4449
- color2 = argbToRgba(view.getUint32(dataOff + 40, true));
4450
- } else if (brushType === EMFPLUS_BRUSHTYPE_PATHGRADIENT && recDataSize >= 12) {
4451
- color2 = argbToRgba(view.getUint32(dataOff + 8, true));
4452
- } else if (brushType === EMFPLUS_BRUSHTYPE_HATCHFILL && recDataSize >= 12) {
4453
- color2 = argbToRgba(view.getUint32(dataOff + 8, true));
4454
- }
4455
- objectTable.set(objectId, { kind: "plus-brush", color: color2 });
5237
+ const brush = parseEmfPlusBrushObject(view, dataOff, recDataSize);
5238
+ if (brush) {
5239
+ objectTable.set(objectId, brush);
4456
5240
  }
4457
5241
  break;
4458
5242
  }
@@ -4548,7 +5332,7 @@ function parseRegionNode(view, off, endOff, depth = 0) {
4548
5332
  }
4549
5333
  const nodeType = view.getUint32(off, true);
4550
5334
  let cursor = off + 4;
4551
- if (nodeType <= 4) {
5335
+ if (nodeType <= 5) {
4552
5336
  const leftResult = parseRegionNode(view, cursor, endOff, depth + 1);
4553
5337
  if (!leftResult) {
4554
5338
  return null;
@@ -4635,7 +5419,7 @@ function handleEmfPlusTextImageRecord(rCtx, recType, recFlags, dataOff, recDataS
4635
5419
  const pathId = recFlags & 255;
4636
5420
  const pathObj = objectTable.get(pathId);
4637
5421
  if (pathObj && pathObj.kind === "plus-path") {
4638
- ctx.fillStyle = resolveBrushColor(rCtx, recFlags, brushVal);
5422
+ ctx.fillStyle = resolveBrushPaint(rCtx, recFlags, brushVal);
4639
5423
  applyPlusWorldTransform(rCtx);
4640
5424
  replayEmfPlusPath(ctx, pathObj);
4641
5425
  ctx.fill();
@@ -4680,7 +5464,7 @@ function handleEmfPlusTextImageRecord(rCtx, recType, recFlags, dataOff, recDataS
4680
5464
  const italic = font.flags & 2 ? "italic " : "";
4681
5465
  const family = mapFontFamily(font.family, rCtx.fontFamilyMap);
4682
5466
  ctx.font = `${italic}${bold}${font.emSize}px ${family}`;
4683
- ctx.fillStyle = resolveBrushColor(rCtx, recFlags, brushVal);
5467
+ ctx.fillStyle = resolveBrushPaint(rCtx, recFlags, brushVal);
4684
5468
  ctx.textBaseline = "top";
4685
5469
  const sf = objectTable.get(formatId);
4686
5470
  if (sf && sf.kind === "plus-stringformat") {
@@ -4720,7 +5504,7 @@ function handleEmfPlusTextImageRecord(rCtx, recType, recFlags, dataOff, recDataS
4720
5504
  const italic = font.flags & 2 ? "italic " : "";
4721
5505
  const family = mapFontFamily(font.family, rCtx.fontFamilyMap);
4722
5506
  ctx.font = `${italic}${bold}${font.emSize}px ${family}`;
4723
- ctx.fillStyle = resolveBrushColor(rCtx, recFlags, brushVal);
5507
+ ctx.fillStyle = resolveBrushPaint(rCtx, recFlags, brushVal);
4724
5508
  ctx.textBaseline = "alphabetic";
4725
5509
  ctx.textAlign = "left";
4726
5510
  applyPlusWorldTransform(rCtx);
@@ -4895,7 +5679,8 @@ function replayEmfPlusRecords(view, offset, length, ctx, _canvasW, _canvasH, sta
4895
5679
  saveIdMap: s.saveIdMap,
4896
5680
  totalImageObjects: 0,
4897
5681
  totalDrawImageCalls: 0,
4898
- clipSaveDepth: 0,
5682
+ clipSaveDepth: s.clipSaveDepth,
5683
+ clipRegion: s.clipRegion,
4899
5684
  pageUnit: 2,
4900
5685
  pageScale: 1,
4901
5686
  continuationBuffer: null,
@@ -5030,6 +5815,8 @@ function replayEmfPlusRecords(view, offset, length, ctx, _canvasW, _canvasH, sta
5030
5815
  if (state) {
5031
5816
  state.worldTransform = rCtx.worldTransform;
5032
5817
  state.saveIdMap = rCtx.saveIdMap;
5818
+ state.clipRegion = rCtx.clipRegion ?? null;
5819
+ state.clipSaveDepth = rCtx.clipSaveDepth;
5033
5820
  }
5034
5821
  ctx.setTransform(1, 0, 0, 1, 0, 0);
5035
5822
  return rCtx.deferredImages;