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.
- package/CHANGELOG.md +4 -0
- package/dist/{Model3DScene-NLUSXHD6.mjs → Model3DScene-4XNBDASE.mjs} +2 -2
- package/dist/Model3DScene-4XNBDASE.mjs.br +0 -0
- package/dist/Model3DScene-4XNBDASE.mjs.gz +0 -0
- package/dist/{Model3DScene-3YB3JAJS.js → Model3DScene-AQS7KDM4.js} +3 -3
- package/dist/Model3DScene-AQS7KDM4.js.br +0 -0
- package/dist/Model3DScene-AQS7KDM4.js.gz +0 -0
- package/dist/{PowerPointViewer-WGVR_u4z.d.ts → PowerPointViewer-DFTatVS4.d.ts} +2 -2
- package/dist/{PowerPointViewer-WGVR_u4z.d.ts.map → PowerPointViewer-DFTatVS4.d.ts.map} +1 -1
- package/dist/{SurfaceChart3DScene-PUQ5DCO5.js → SurfaceChart3DScene-HA2SD6BY.js} +3 -3
- package/dist/SurfaceChart3DScene-HA2SD6BY.js.br +0 -0
- package/dist/SurfaceChart3DScene-HA2SD6BY.js.gz +0 -0
- package/dist/{SurfaceChart3DScene-YBMJQGH3.mjs → SurfaceChart3DScene-K7ZDXDBL.mjs} +2 -2
- package/dist/SurfaceChart3DScene-K7ZDXDBL.mjs.br +0 -0
- package/dist/SurfaceChart3DScene-K7ZDXDBL.mjs.gz +0 -0
- package/dist/{chunk-VSNXVKJI.js → chunk-2DXNTVFV.js} +159 -62
- package/dist/chunk-2DXNTVFV.js.br +0 -0
- package/dist/chunk-2DXNTVFV.js.gz +0 -0
- package/dist/{chunk-B7JRQB5F.mjs → chunk-36NC56XG.mjs} +311 -292
- package/dist/chunk-36NC56XG.mjs.br +0 -0
- package/dist/chunk-36NC56XG.mjs.gz +0 -0
- package/dist/{chunk-3ZGCM6RC.mjs → chunk-AFWMAFWR.mjs} +1076 -289
- package/dist/chunk-AFWMAFWR.mjs.br +0 -0
- package/dist/chunk-AFWMAFWR.mjs.gz +0 -0
- package/dist/{chunk-QVQTDWB7.js → chunk-H5RREYCO.js} +463 -454
- package/dist/chunk-H5RREYCO.js.br +0 -0
- package/dist/chunk-H5RREYCO.js.gz +0 -0
- package/dist/{chunk-5SBQZEKD.mjs → chunk-I42XL2WU.mjs} +130 -34
- package/dist/chunk-I42XL2WU.mjs.br +0 -0
- package/dist/chunk-I42XL2WU.mjs.gz +0 -0
- package/dist/{chunk-DZUI64SO.mjs → chunk-LFEVG3ED.mjs} +13 -4
- package/dist/chunk-LFEVG3ED.mjs.br +0 -0
- package/dist/chunk-LFEVG3ED.mjs.gz +0 -0
- package/dist/{chunk-YPZV4KJT.js → chunk-LS2AIX2H.js} +1087 -1068
- package/dist/chunk-LS2AIX2H.js.br +0 -0
- package/dist/chunk-LS2AIX2H.js.gz +0 -0
- package/dist/{chunk-AD3EAEY6.js → chunk-MS6S6YYR.js} +1076 -289
- package/dist/chunk-MS6S6YYR.js.br +0 -0
- package/dist/chunk-MS6S6YYR.js.gz +0 -0
- package/dist/{dist-3YRZ26TL.mjs → dist-5KQQ6GP2.mjs} +1 -1
- package/dist/dist-5KQQ6GP2.mjs.br +0 -0
- package/dist/dist-5KQQ6GP2.mjs.gz +0 -0
- package/dist/{dist-M2P6HBG4.js → dist-CVSSAFDL.js} +560 -560
- package/dist/dist-CVSSAFDL.js.br +0 -0
- package/dist/dist-CVSSAFDL.js.gz +0 -0
- package/dist/hooks-unstable.d.ts +5107 -5242
- package/dist/hooks-unstable.d.ts.map +1 -1
- package/dist/hooks-unstable.js +76 -76
- package/dist/hooks-unstable.js.br +0 -0
- package/dist/hooks-unstable.js.gz +0 -0
- package/dist/hooks-unstable.mjs +3 -3
- package/dist/hooks-unstable.mjs.br +0 -0
- package/dist/hooks-unstable.mjs.gz +0 -0
- package/dist/i18n.d.ts +3 -0
- package/dist/{index-SUL_8gq8.d.ts → index-CGKutKk1.d.ts} +4 -1
- package/dist/index-CGKutKk1.d.ts.map +1 -0
- package/dist/{index-ntF3kHHU.d.ts → index-COgh2UTL.d.ts} +2 -2
- package/dist/{index-ntF3kHHU.d.ts.map → index-COgh2UTL.d.ts.map} +1 -1
- package/dist/index.d.ts +3328 -3386
- package/dist/index.js +30 -30
- package/dist/index.js.br +0 -0
- package/dist/index.js.gz +0 -0
- package/dist/index.mjs +4 -4
- package/dist/index.mjs.br +0 -0
- package/dist/index.mjs.gz +0 -0
- package/dist/pptx-viewer.css +1 -1
- package/dist/pptx-viewer.css.br +0 -0
- package/dist/pptx-viewer.css.gz +0 -0
- package/dist/{types-Cdh0XYcS.d.ts → types-Da1M5VBY.d.ts} +2 -2
- package/dist/{types-Cdh0XYcS.d.ts.map → types-Da1M5VBY.d.ts.map} +1 -1
- package/dist/viewer/index.d.ts +5118 -5224
- package/dist/viewer/index.js +20 -20
- package/dist/viewer/index.js.br +0 -0
- package/dist/viewer/index.js.gz +0 -0
- package/dist/viewer/index.mjs +4 -4
- package/dist/viewer/index.mjs.br +0 -0
- package/dist/viewer/index.mjs.gz +0 -0
- package/package.json +1 -1
- package/dist/Model3DScene-3YB3JAJS.js.br +0 -0
- package/dist/Model3DScene-3YB3JAJS.js.gz +0 -0
- package/dist/Model3DScene-NLUSXHD6.mjs.br +0 -0
- package/dist/Model3DScene-NLUSXHD6.mjs.gz +0 -0
- package/dist/SurfaceChart3DScene-PUQ5DCO5.js.br +0 -0
- package/dist/SurfaceChart3DScene-PUQ5DCO5.js.gz +0 -0
- package/dist/SurfaceChart3DScene-YBMJQGH3.mjs.br +0 -0
- package/dist/SurfaceChart3DScene-YBMJQGH3.mjs.gz +0 -0
- package/dist/chunk-3ZGCM6RC.mjs.br +0 -0
- package/dist/chunk-3ZGCM6RC.mjs.gz +0 -0
- package/dist/chunk-5SBQZEKD.mjs.br +0 -0
- package/dist/chunk-5SBQZEKD.mjs.gz +0 -0
- package/dist/chunk-AD3EAEY6.js.br +0 -0
- package/dist/chunk-AD3EAEY6.js.gz +0 -0
- package/dist/chunk-B7JRQB5F.mjs.br +0 -0
- package/dist/chunk-B7JRQB5F.mjs.gz +0 -0
- package/dist/chunk-DZUI64SO.mjs.br +0 -0
- package/dist/chunk-DZUI64SO.mjs.gz +0 -0
- package/dist/chunk-QVQTDWB7.js.br +0 -0
- package/dist/chunk-QVQTDWB7.js.gz +0 -0
- package/dist/chunk-VSNXVKJI.js.br +0 -0
- package/dist/chunk-VSNXVKJI.js.gz +0 -0
- package/dist/chunk-YPZV4KJT.js.br +0 -0
- package/dist/chunk-YPZV4KJT.js.gz +0 -0
- package/dist/dist-3YRZ26TL.mjs.br +0 -0
- package/dist/dist-3YRZ26TL.mjs.gz +0 -0
- package/dist/dist-M2P6HBG4.js.br +0 -0
- package/dist/dist-M2P6HBG4.js.gz +0 -0
- 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.
|
|
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
|
-
|
|
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
|
-
/**
|
|
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.
|
|
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
|
-
/**
|
|
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
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
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 +
|
|
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
|
|
429
|
-
const numEntries = tableLength >>> 1;
|
|
507
|
+
const numEntries = sIn.readU16();
|
|
430
508
|
const out = new Stream(null, 0);
|
|
431
|
-
out.reserve(
|
|
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
|
|
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
|
|
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 +=
|
|
606
|
-
cumulativeY +=
|
|
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
|
-
|
|
873
|
-
|
|
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
|
-
|
|
877
|
-
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
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 =
|
|
1149
|
-
var MAX_OUT =
|
|
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
|
-
|
|
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
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
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.
|
|
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
|
|
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
|
-
|
|
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 "
|
|
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
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 (
|
|
2261
|
+
return (px2 - r.windowOrg.x) / (r.windowExt.cx || 1) * (r.viewportExt.cx || 1) + r.viewportOrg.x;
|
|
2068
2262
|
}
|
|
2069
|
-
return (
|
|
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 (
|
|
2269
|
+
return (py - r.windowOrg.y) / (r.windowExt.cy || 1) * (r.viewportExt.cy || 1) + r.viewportOrg.y;
|
|
2074
2270
|
}
|
|
2075
|
-
return (
|
|
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
|
|
2276
|
+
return pw / (r.windowExt.cx || 1) * (r.viewportExt.cx || 1);
|
|
2080
2277
|
}
|
|
2081
|
-
return
|
|
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
|
|
2283
|
+
return ph / (r.windowExt.cy || 1) * (r.viewportExt.cy || 1);
|
|
2086
2284
|
}
|
|
2087
|
-
return
|
|
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
|
-
|
|
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 {
|
|
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
|
-
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
|
|
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
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
3676
|
-
|
|
3677
|
-
|
|
3678
|
-
|
|
3679
|
-
|
|
3680
|
-
|
|
3681
|
-
|
|
3682
|
-
|
|
3683
|
-
|
|
3684
|
-
|
|
3685
|
-
|
|
3686
|
-
}
|
|
3687
|
-
|
|
3688
|
-
|
|
3689
|
-
|
|
3690
|
-
|
|
3691
|
-
|
|
3692
|
-
|
|
3693
|
-
|
|
3694
|
-
|
|
3695
|
-
|
|
3696
|
-
|
|
3697
|
-
|
|
3698
|
-
|
|
3699
|
-
|
|
3700
|
-
|
|
3701
|
-
|
|
3702
|
-
|
|
3703
|
-
|
|
3704
|
-
|
|
3705
|
-
|
|
3706
|
-
|
|
3707
|
-
|
|
3708
|
-
|
|
3709
|
-
|
|
3710
|
-
|
|
3711
|
-
|
|
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
|
-
|
|
3723
|
-
break;
|
|
4263
|
+
return { region: [transformedRectShape(node.x, node.y, node.width, node.height, m)], exact: true };
|
|
3724
4264
|
case "path":
|
|
3725
|
-
|
|
3726
|
-
break;
|
|
4265
|
+
return { region: [pathClipShape(node, m)], exact: true };
|
|
3727
4266
|
case "infinite":
|
|
3728
|
-
|
|
3729
|
-
break;
|
|
4267
|
+
return { region: null, exact: true };
|
|
3730
4268
|
case "empty":
|
|
3731
|
-
|
|
3732
|
-
|
|
3733
|
-
|
|
3734
|
-
|
|
3735
|
-
|
|
3736
|
-
|
|
3737
|
-
|
|
3738
|
-
|
|
3739
|
-
|
|
3740
|
-
|
|
3741
|
-
|
|
3742
|
-
|
|
3743
|
-
|
|
3744
|
-
|
|
3745
|
-
|
|
3746
|
-
|
|
3747
|
-
|
|
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
|
|
3751
|
-
|
|
3752
|
-
|
|
3753
|
-
|
|
3754
|
-
|
|
3755
|
-
|
|
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
|
-
|
|
3859
|
-
|
|
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
|
-
|
|
3872
|
-
|
|
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
|
-
|
|
3880
|
-
|
|
3881
|
-
|
|
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
|
-
|
|
3891
|
-
|
|
3892
|
-
|
|
3893
|
-
|
|
3894
|
-
|
|
3895
|
-
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
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
|
|
4342
|
-
if (
|
|
4343
|
-
penColor =
|
|
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
|
-
|
|
4438
|
-
|
|
4439
|
-
|
|
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 <=
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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:
|
|
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;
|