emf-converter 3.0.2 → 3.2.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/README.md +13 -6
- package/dist/index.js +2691 -1106
- package/dist/index.mjs +2691 -1106
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -52,6 +52,469 @@ function invertCssColor(color) {
|
|
|
52
52
|
return color;
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
+
// src/emf-dib-rle-decoder.ts
|
|
56
|
+
function decodeRleBitmap(view, bitsOffset, bitsSize, width, height, _topDown, isRle4, colorTable, out, setPixel) {
|
|
57
|
+
let x = 0;
|
|
58
|
+
let y = height - 1;
|
|
59
|
+
let off = bitsOffset;
|
|
60
|
+
const endOff = bitsOffset + bitsSize;
|
|
61
|
+
while (off + 1 < endOff && y >= 0) {
|
|
62
|
+
const first = view.getUint8(off);
|
|
63
|
+
const second = view.getUint8(off + 1);
|
|
64
|
+
off += 2;
|
|
65
|
+
if (first === 0) {
|
|
66
|
+
if (second === 0) {
|
|
67
|
+
x = 0;
|
|
68
|
+
y--;
|
|
69
|
+
} else if (second === 1) {
|
|
70
|
+
break;
|
|
71
|
+
} else if (second === 2) {
|
|
72
|
+
if (off + 1 >= endOff) {
|
|
73
|
+
break;
|
|
74
|
+
}
|
|
75
|
+
x += view.getUint8(off);
|
|
76
|
+
y -= view.getUint8(off + 1);
|
|
77
|
+
off += 2;
|
|
78
|
+
} else {
|
|
79
|
+
const count = second;
|
|
80
|
+
if (!isRle4) {
|
|
81
|
+
for (let i = 0; i < count && off < endOff && x < width; i++) {
|
|
82
|
+
const idx = view.getUint8(off++);
|
|
83
|
+
if (idx < colorTable.length) {
|
|
84
|
+
setPixel(
|
|
85
|
+
x,
|
|
86
|
+
height - 1 - y,
|
|
87
|
+
colorTable[idx][0],
|
|
88
|
+
colorTable[idx][1],
|
|
89
|
+
colorTable[idx][2],
|
|
90
|
+
255
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
x++;
|
|
94
|
+
}
|
|
95
|
+
if (count & 1) {
|
|
96
|
+
off++;
|
|
97
|
+
}
|
|
98
|
+
} else {
|
|
99
|
+
const bytes = Math.ceil(count / 2);
|
|
100
|
+
let pi = 0;
|
|
101
|
+
for (let i = 0; i < bytes && off < endOff; i++) {
|
|
102
|
+
const byte = view.getUint8(off++);
|
|
103
|
+
for (let nibble = 0; nibble < 2 && pi < count; nibble++) {
|
|
104
|
+
const idx = nibble === 0 ? byte >> 4 & 15 : byte & 15;
|
|
105
|
+
if (idx < colorTable.length && x < width) {
|
|
106
|
+
setPixel(
|
|
107
|
+
x,
|
|
108
|
+
height - 1 - y,
|
|
109
|
+
colorTable[idx][0],
|
|
110
|
+
colorTable[idx][1],
|
|
111
|
+
colorTable[idx][2],
|
|
112
|
+
255
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
x++;
|
|
116
|
+
pi++;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
if (bytes & 1) {
|
|
120
|
+
off++;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
} else if (!isRle4) {
|
|
125
|
+
const idx = second;
|
|
126
|
+
const c = idx < colorTable.length ? colorTable[idx] : [0, 0, 0];
|
|
127
|
+
for (let i = 0; i < first && x < width; i++) {
|
|
128
|
+
setPixel(x, height - 1 - y, c[0], c[1], c[2], 255);
|
|
129
|
+
x++;
|
|
130
|
+
}
|
|
131
|
+
} else {
|
|
132
|
+
const hi = second >> 4 & 15;
|
|
133
|
+
const lo = second & 15;
|
|
134
|
+
for (let i = 0; i < first && x < width; i++) {
|
|
135
|
+
const idx = (i & 1) === 0 ? hi : lo;
|
|
136
|
+
if (idx < colorTable.length) {
|
|
137
|
+
setPixel(
|
|
138
|
+
x,
|
|
139
|
+
height - 1 - y,
|
|
140
|
+
colorTable[idx][0],
|
|
141
|
+
colorTable[idx][1],
|
|
142
|
+
colorTable[idx][2],
|
|
143
|
+
255
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
x++;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return createImageDataCompat(
|
|
151
|
+
new Uint8ClampedArray(out.buffer, out.byteOffset, out.byteLength),
|
|
152
|
+
width,
|
|
153
|
+
height
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// src/emf-dib-uncompressed.ts
|
|
158
|
+
function countTrailingZeros(v) {
|
|
159
|
+
if (v === 0) {
|
|
160
|
+
return 0;
|
|
161
|
+
}
|
|
162
|
+
let c = 0;
|
|
163
|
+
let val = v;
|
|
164
|
+
while ((val & 1) === 0) {
|
|
165
|
+
val >>>= 1;
|
|
166
|
+
c++;
|
|
167
|
+
}
|
|
168
|
+
return c;
|
|
169
|
+
}
|
|
170
|
+
var BI_BITFIELDS = 3;
|
|
171
|
+
function parseBitfieldMasks(view, bmiOffset, headerSize, compression, bitCount) {
|
|
172
|
+
let rMask = 0, gMask = 0, bMask = 0;
|
|
173
|
+
let rShift = 0, gShift = 0, bShift = 0;
|
|
174
|
+
let rMax = 1, gMax = 1, bMax = 1;
|
|
175
|
+
if (compression === BI_BITFIELDS) {
|
|
176
|
+
const bfOff = bmiOffset + headerSize;
|
|
177
|
+
if (bfOff + 12 > view.byteLength) {
|
|
178
|
+
return null;
|
|
179
|
+
}
|
|
180
|
+
rMask = view.getUint32(bfOff, true);
|
|
181
|
+
gMask = view.getUint32(bfOff + 4, true);
|
|
182
|
+
bMask = view.getUint32(bfOff + 8, true);
|
|
183
|
+
rShift = countTrailingZeros(rMask);
|
|
184
|
+
gShift = countTrailingZeros(gMask);
|
|
185
|
+
bShift = countTrailingZeros(bMask);
|
|
186
|
+
rMax = rMask >>> rShift || 1;
|
|
187
|
+
gMax = gMask >>> gShift || 1;
|
|
188
|
+
bMax = bMask >>> bShift || 1;
|
|
189
|
+
} else if (bitCount === 16) {
|
|
190
|
+
rMask = 31744;
|
|
191
|
+
gMask = 992;
|
|
192
|
+
bMask = 31;
|
|
193
|
+
rShift = 10;
|
|
194
|
+
gShift = 5;
|
|
195
|
+
bShift = 0;
|
|
196
|
+
rMax = 31;
|
|
197
|
+
gMax = 31;
|
|
198
|
+
bMax = 31;
|
|
199
|
+
}
|
|
200
|
+
return { rMask, gMask, bMask, rShift, gShift, bShift, rMax, gMax, bMax };
|
|
201
|
+
}
|
|
202
|
+
function decodeUncompressedRows(view, bitsOffset, width, height, topDown, bitCount, colorTable, masks, out) {
|
|
203
|
+
const rowStride = Math.floor((bitCount * width + 31) / 32) * 4;
|
|
204
|
+
const { rMask, gMask, bMask, rShift, gShift, bShift, rMax, gMax, bMax } = masks;
|
|
205
|
+
for (let y = 0; y < height; y++) {
|
|
206
|
+
const srcY = topDown ? y : height - 1 - y;
|
|
207
|
+
const rowStart = bitsOffset + srcY * rowStride;
|
|
208
|
+
if (rowStart + rowStride > view.byteLength) {
|
|
209
|
+
continue;
|
|
210
|
+
}
|
|
211
|
+
for (let x = 0; x < width; x++) {
|
|
212
|
+
const dstPx = (y * width + x) * 4;
|
|
213
|
+
if (bitCount === 1) {
|
|
214
|
+
const byteIdx = rowStart + (x >> 3);
|
|
215
|
+
const bit = view.getUint8(byteIdx) >> 7 - (x & 7) & 1;
|
|
216
|
+
if (bit < colorTable.length) {
|
|
217
|
+
out[dstPx] = colorTable[bit][0];
|
|
218
|
+
out[dstPx + 1] = colorTable[bit][1];
|
|
219
|
+
out[dstPx + 2] = colorTable[bit][2];
|
|
220
|
+
}
|
|
221
|
+
out[dstPx + 3] = 255;
|
|
222
|
+
} else if (bitCount === 4) {
|
|
223
|
+
const byteIdx = rowStart + (x >> 1);
|
|
224
|
+
const nibble = (x & 1) === 0 ? view.getUint8(byteIdx) >> 4 & 15 : view.getUint8(byteIdx) & 15;
|
|
225
|
+
if (nibble < colorTable.length) {
|
|
226
|
+
out[dstPx] = colorTable[nibble][0];
|
|
227
|
+
out[dstPx + 1] = colorTable[nibble][1];
|
|
228
|
+
out[dstPx + 2] = colorTable[nibble][2];
|
|
229
|
+
}
|
|
230
|
+
out[dstPx + 3] = 255;
|
|
231
|
+
} else if (bitCount === 8) {
|
|
232
|
+
const idx = view.getUint8(rowStart + x);
|
|
233
|
+
if (idx < colorTable.length) {
|
|
234
|
+
out[dstPx] = colorTable[idx][0];
|
|
235
|
+
out[dstPx + 1] = colorTable[idx][1];
|
|
236
|
+
out[dstPx + 2] = colorTable[idx][2];
|
|
237
|
+
}
|
|
238
|
+
out[dstPx + 3] = 255;
|
|
239
|
+
} else if (bitCount === 16) {
|
|
240
|
+
const val = view.getUint16(rowStart + x * 2, true);
|
|
241
|
+
out[dstPx] = Math.round(((val & rMask) >>> rShift) * 255 / rMax);
|
|
242
|
+
out[dstPx + 1] = Math.round(((val & gMask) >>> gShift) * 255 / gMax);
|
|
243
|
+
out[dstPx + 2] = Math.round(((val & bMask) >>> bShift) * 255 / bMax);
|
|
244
|
+
out[dstPx + 3] = 255;
|
|
245
|
+
} else if (bitCount === 24) {
|
|
246
|
+
const srcPx = rowStart + x * 3;
|
|
247
|
+
out[dstPx] = view.getUint8(srcPx + 2);
|
|
248
|
+
out[dstPx + 1] = view.getUint8(srcPx + 1);
|
|
249
|
+
out[dstPx + 2] = view.getUint8(srcPx);
|
|
250
|
+
out[dstPx + 3] = 255;
|
|
251
|
+
} else {
|
|
252
|
+
const srcPx = rowStart + x * 4;
|
|
253
|
+
const bb = view.getUint8(srcPx);
|
|
254
|
+
const gg = view.getUint8(srcPx + 1);
|
|
255
|
+
const rr = view.getUint8(srcPx + 2);
|
|
256
|
+
const aa = view.getUint8(srcPx + 3);
|
|
257
|
+
out[dstPx] = rr;
|
|
258
|
+
out[dstPx + 1] = gg;
|
|
259
|
+
out[dstPx + 2] = bb;
|
|
260
|
+
out[dstPx + 3] = aa === 0 ? 255 : aa;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// src/emf-dib-decoder.ts
|
|
267
|
+
function decodeDibToImageData(view, bmiOffset, bitsOffset, bitsSize) {
|
|
268
|
+
if (bmiOffset < 0 || bitsOffset < 0 || bmiOffset + 40 > view.byteLength || bitsOffset + bitsSize > view.byteLength) {
|
|
269
|
+
return null;
|
|
270
|
+
}
|
|
271
|
+
const headerSize = view.getUint32(bmiOffset, true);
|
|
272
|
+
if (headerSize < 40 || bmiOffset + headerSize > view.byteLength) {
|
|
273
|
+
return null;
|
|
274
|
+
}
|
|
275
|
+
const width = view.getInt32(bmiOffset + 4, true);
|
|
276
|
+
const heightRaw = view.getInt32(bmiOffset + 8, true);
|
|
277
|
+
const planes = view.getUint16(bmiOffset + 12, true);
|
|
278
|
+
const bitCount = view.getUint16(bmiOffset + 14, true);
|
|
279
|
+
const compression = view.getUint32(bmiOffset + 16, true);
|
|
280
|
+
if (planes !== 1 || width <= 0 || heightRaw === 0) {
|
|
281
|
+
return null;
|
|
282
|
+
}
|
|
283
|
+
if (width > 8192 || Math.abs(heightRaw) > 8192) {
|
|
284
|
+
return null;
|
|
285
|
+
}
|
|
286
|
+
const BI_RGB = 0;
|
|
287
|
+
const BI_RLE8 = 1;
|
|
288
|
+
const BI_RLE4 = 2;
|
|
289
|
+
const BI_BITFIELDS2 = 3;
|
|
290
|
+
if (bitCount !== 1 && bitCount !== 4 && bitCount !== 8 && bitCount !== 16 && bitCount !== 24 && bitCount !== 32) {
|
|
291
|
+
return null;
|
|
292
|
+
}
|
|
293
|
+
if (compression === BI_RLE8 && bitCount !== 8) {
|
|
294
|
+
return null;
|
|
295
|
+
}
|
|
296
|
+
if (compression === BI_RLE4 && bitCount !== 4) {
|
|
297
|
+
return null;
|
|
298
|
+
}
|
|
299
|
+
if (compression === BI_BITFIELDS2 && bitCount !== 16 && bitCount !== 32) {
|
|
300
|
+
return null;
|
|
301
|
+
}
|
|
302
|
+
if (compression !== BI_RGB && compression !== BI_RLE8 && compression !== BI_RLE4 && compression !== BI_BITFIELDS2) {
|
|
303
|
+
return null;
|
|
304
|
+
}
|
|
305
|
+
const height = Math.abs(heightRaw);
|
|
306
|
+
const topDown = heightRaw < 0;
|
|
307
|
+
const colorTable = [];
|
|
308
|
+
if (bitCount <= 8) {
|
|
309
|
+
const maxColors = 1 << bitCount;
|
|
310
|
+
const colorsUsed = view.getUint32(bmiOffset + 32, true) || maxColors;
|
|
311
|
+
const numColors = Math.min(colorsUsed, maxColors);
|
|
312
|
+
const ctOffset = bmiOffset + headerSize;
|
|
313
|
+
if (ctOffset + numColors * 4 > view.byteLength) {
|
|
314
|
+
return null;
|
|
315
|
+
}
|
|
316
|
+
for (let i = 0; i < numColors; i++) {
|
|
317
|
+
const b = view.getUint8(ctOffset + i * 4);
|
|
318
|
+
const g = view.getUint8(ctOffset + i * 4 + 1);
|
|
319
|
+
const r = view.getUint8(ctOffset + i * 4 + 2);
|
|
320
|
+
colorTable.push([r, g, b]);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
const masks = parseBitfieldMasks(view, bmiOffset, headerSize, compression, bitCount);
|
|
324
|
+
if (!masks) {
|
|
325
|
+
return null;
|
|
326
|
+
}
|
|
327
|
+
const out = new Uint8ClampedArray(width * height * 4);
|
|
328
|
+
if (compression === BI_RLE8 || compression === BI_RLE4) {
|
|
329
|
+
const setPixel = (x, y, r, g, b, a) => {
|
|
330
|
+
const dstPx = (y * width + x) * 4;
|
|
331
|
+
out[dstPx] = r;
|
|
332
|
+
out[dstPx + 1] = g;
|
|
333
|
+
out[dstPx + 2] = b;
|
|
334
|
+
out[dstPx + 3] = a;
|
|
335
|
+
};
|
|
336
|
+
return decodeRleBitmap(
|
|
337
|
+
view,
|
|
338
|
+
bitsOffset,
|
|
339
|
+
bitsSize,
|
|
340
|
+
width,
|
|
341
|
+
height,
|
|
342
|
+
topDown,
|
|
343
|
+
compression === BI_RLE4,
|
|
344
|
+
colorTable,
|
|
345
|
+
out,
|
|
346
|
+
setPixel
|
|
347
|
+
);
|
|
348
|
+
}
|
|
349
|
+
decodeUncompressedRows(
|
|
350
|
+
view,
|
|
351
|
+
bitsOffset,
|
|
352
|
+
width,
|
|
353
|
+
height,
|
|
354
|
+
topDown,
|
|
355
|
+
bitCount,
|
|
356
|
+
colorTable,
|
|
357
|
+
masks,
|
|
358
|
+
out
|
|
359
|
+
);
|
|
360
|
+
return createImageDataCompat(out, width, height);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
// src/emf-gdi-brush-pattern.ts
|
|
364
|
+
var HATCH_ROWS = [
|
|
365
|
+
[0, 0, 0, 255, 0, 0, 0, 0],
|
|
366
|
+
// HS_HORIZONTAL
|
|
367
|
+
[8, 8, 8, 8, 8, 8, 8, 8],
|
|
368
|
+
// HS_VERTICAL
|
|
369
|
+
[128, 64, 32, 16, 8, 4, 2, 1],
|
|
370
|
+
// HS_FDIAGONAL
|
|
371
|
+
[1, 2, 4, 8, 16, 32, 64, 128],
|
|
372
|
+
// HS_BDIAGONAL
|
|
373
|
+
[8, 8, 8, 255, 8, 8, 8, 8],
|
|
374
|
+
// HS_CROSS
|
|
375
|
+
[129, 66, 36, 24, 24, 36, 66, 129]
|
|
376
|
+
// HS_DIAGCROSS
|
|
377
|
+
];
|
|
378
|
+
function hatchBit(hatch, x, y) {
|
|
379
|
+
const rows = HATCH_ROWS[hatch] ?? HATCH_ROWS[0];
|
|
380
|
+
return (rows[y & 7] >> 7 - (x & 7) & 1) === 1;
|
|
381
|
+
}
|
|
382
|
+
function cssHexToRgb(color) {
|
|
383
|
+
const m = /^#([0-9a-f]{6}|[0-9a-f]{3})$/i.exec(color.trim());
|
|
384
|
+
if (!m) {
|
|
385
|
+
return 0;
|
|
386
|
+
}
|
|
387
|
+
const hex = m[1].length === 3 ? m[1].replace(/./g, (c) => c + c) : m[1];
|
|
388
|
+
return parseInt(hex, 16);
|
|
389
|
+
}
|
|
390
|
+
function decodeMonoBits(view, bmiOffset, bitsOffset) {
|
|
391
|
+
if (bmiOffset + 16 > view.byteLength) {
|
|
392
|
+
return null;
|
|
393
|
+
}
|
|
394
|
+
const width = view.getInt32(bmiOffset + 4, true);
|
|
395
|
+
const heightRaw = view.getInt32(bmiOffset + 8, true);
|
|
396
|
+
const bitCount = view.getUint16(bmiOffset + 14, true);
|
|
397
|
+
const height = Math.abs(heightRaw);
|
|
398
|
+
if (bitCount !== 1 || width <= 0 || height === 0 || width > 256 || height > 256) {
|
|
399
|
+
return null;
|
|
400
|
+
}
|
|
401
|
+
const stride = (width + 31 >> 5) * 4;
|
|
402
|
+
if (bitsOffset + stride * height > view.byteLength) {
|
|
403
|
+
return null;
|
|
404
|
+
}
|
|
405
|
+
const bits = new Uint8Array(width * height);
|
|
406
|
+
for (let row = 0; row < height; row++) {
|
|
407
|
+
const y = heightRaw > 0 ? height - 1 - row : row;
|
|
408
|
+
const rowOff = bitsOffset + row * stride;
|
|
409
|
+
for (let x = 0; x < width; x++) {
|
|
410
|
+
bits[y * width + x] = view.getUint8(rowOff + (x >> 3)) >> 7 - (x & 7) & 1;
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
return { width, height, bits };
|
|
414
|
+
}
|
|
415
|
+
function parsePatternBrush(view, offset, dataOff, mono) {
|
|
416
|
+
if (dataOff + 24 > view.byteLength) {
|
|
417
|
+
return null;
|
|
418
|
+
}
|
|
419
|
+
const offBmi = view.getUint32(dataOff + 8, true);
|
|
420
|
+
const offBits = view.getUint32(dataOff + 16, true);
|
|
421
|
+
const cbBits = view.getUint32(dataOff + 20, true);
|
|
422
|
+
const bmi = offset + offBmi;
|
|
423
|
+
const bitsAt = offset + offBits;
|
|
424
|
+
const bitCount = bmi + 16 <= view.byteLength ? view.getUint16(bmi + 14, true) : 0;
|
|
425
|
+
if (mono || bitCount === 1) {
|
|
426
|
+
const decoded = decodeMonoBits(view, bmi, bitsAt);
|
|
427
|
+
if (decoded && mono) {
|
|
428
|
+
return { kind: "mono", ...decoded };
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
const image = decodeDibToImageData(view, bmi, bitsAt, cbBits);
|
|
432
|
+
if (!image) {
|
|
433
|
+
return null;
|
|
434
|
+
}
|
|
435
|
+
const rgb = new Uint32Array(image.width * image.height);
|
|
436
|
+
const d = image.data;
|
|
437
|
+
for (let i = 0; i < rgb.length; i++) {
|
|
438
|
+
rgb[i] = d[i * 4] << 16 | d[i * 4 + 1] << 8 | d[i * 4 + 2];
|
|
439
|
+
}
|
|
440
|
+
return { kind: "bitmap", width: image.width, height: image.height, rgb };
|
|
441
|
+
}
|
|
442
|
+
function realizeBrush(state) {
|
|
443
|
+
if (state.brushStyle === 1) {
|
|
444
|
+
return { kind: "none" };
|
|
445
|
+
}
|
|
446
|
+
const pattern = state.brushPattern;
|
|
447
|
+
if (!pattern) {
|
|
448
|
+
return { kind: "solid", rgb: cssHexToRgb(state.brushColor) };
|
|
449
|
+
}
|
|
450
|
+
if (pattern.kind === "bitmap") {
|
|
451
|
+
return { kind: "tile", width: pattern.width, height: pattern.height, rgb: pattern.rgb };
|
|
452
|
+
}
|
|
453
|
+
const bk = cssHexToRgb(state.bkColor);
|
|
454
|
+
if (pattern.kind === "hatch") {
|
|
455
|
+
const fg = cssHexToRgb(state.brushColor);
|
|
456
|
+
const rgb2 = new Uint32Array(64);
|
|
457
|
+
for (let y = 0; y < 8; y++) {
|
|
458
|
+
for (let x = 0; x < 8; x++) {
|
|
459
|
+
rgb2[y * 8 + x] = hatchBit(pattern.hatch, x, y) ? fg : bk;
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
return { kind: "tile", width: 8, height: 8, rgb: rgb2 };
|
|
463
|
+
}
|
|
464
|
+
const text = cssHexToRgb(state.textColor);
|
|
465
|
+
const rgb = new Uint32Array(pattern.width * pattern.height);
|
|
466
|
+
for (let i = 0; i < rgb.length; i++) {
|
|
467
|
+
rgb[i] = pattern.bits[i] ? bk : text;
|
|
468
|
+
}
|
|
469
|
+
return { kind: "tile", width: pattern.width, height: pattern.height, rgb };
|
|
470
|
+
}
|
|
471
|
+
function sampleTile(tile, devX, devY, orgX, orgY) {
|
|
472
|
+
const tx = ((devX - orgX) % tile.width + tile.width) % tile.width;
|
|
473
|
+
const ty = ((devY - orgY) % tile.height + tile.height) % tile.height;
|
|
474
|
+
return tile.rgb[ty * tile.width + tx];
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
// src/emf-gdi-text-layout.ts
|
|
478
|
+
function cumulativeGlyphOffsets(dx) {
|
|
479
|
+
const offsets = [];
|
|
480
|
+
let acc = 0;
|
|
481
|
+
for (let i = 0; i < dx.length; i++) {
|
|
482
|
+
offsets.push(acc);
|
|
483
|
+
acc += dx[i];
|
|
484
|
+
}
|
|
485
|
+
return offsets;
|
|
486
|
+
}
|
|
487
|
+
function totalGlyphAdvance(dx) {
|
|
488
|
+
let sum = 0;
|
|
489
|
+
for (const d of dx) {
|
|
490
|
+
sum += d;
|
|
491
|
+
}
|
|
492
|
+
return sum;
|
|
493
|
+
}
|
|
494
|
+
function alignmentStartOffset(totalWidth, align) {
|
|
495
|
+
if (align === "center") {
|
|
496
|
+
return -totalWidth / 2;
|
|
497
|
+
}
|
|
498
|
+
if (align === "right") {
|
|
499
|
+
return -totalWidth;
|
|
500
|
+
}
|
|
501
|
+
return 0;
|
|
502
|
+
}
|
|
503
|
+
var DEG_TO_RAD = Math.PI / 180;
|
|
504
|
+
function escapementToCanvasRadians(tenthsOfDegree) {
|
|
505
|
+
return -(tenthsOfDegree / 10) * DEG_TO_RAD;
|
|
506
|
+
}
|
|
507
|
+
var CELL_TO_CHAR_HEIGHT_RATIO = 1.15;
|
|
508
|
+
function resolveFontPixelHeight(lfHeight) {
|
|
509
|
+
if (lfHeight === 0) {
|
|
510
|
+
return 0;
|
|
511
|
+
}
|
|
512
|
+
if (lfHeight < 0) {
|
|
513
|
+
return -lfHeight;
|
|
514
|
+
}
|
|
515
|
+
return lfHeight / CELL_TO_CHAR_HEIGHT_RATIO;
|
|
516
|
+
}
|
|
517
|
+
|
|
55
518
|
// src/emf-constants.ts
|
|
56
519
|
var EMR_HEADER = 1;
|
|
57
520
|
var EMR_POLYBEZIER = 2;
|
|
@@ -66,6 +529,9 @@ var EMR_SETWINDOWORGEX = 10;
|
|
|
66
529
|
var EMR_SETVIEWPORTEXTEX = 11;
|
|
67
530
|
var EMR_SETVIEWPORTORGEX = 12;
|
|
68
531
|
var EMR_SETBRUSHORGEX = 13;
|
|
532
|
+
var EMR_CREATEMONOBRUSH = 93;
|
|
533
|
+
var EMR_CREATEDIBPATTERNBRUSHPT = 94;
|
|
534
|
+
var EMR_STRETCHBLT = 77;
|
|
69
535
|
var EMR_EOF = 14;
|
|
70
536
|
var EMR_SETPIXELV = 15;
|
|
71
537
|
var EMR_SETMAPMODE = 17;
|
|
@@ -193,6 +659,7 @@ var EMFPLUS_OBJECTTYPE_STRINGFORMAT = 7;
|
|
|
193
659
|
var EMFPLUS_OBJECTTYPE_REGION = 8;
|
|
194
660
|
var EMFPLUS_BRUSHTYPE_SOLID = 0;
|
|
195
661
|
var EMFPLUS_BRUSHTYPE_HATCHFILL = 1;
|
|
662
|
+
var EMFPLUS_BRUSHTYPE_TEXTUREFILL = 2;
|
|
196
663
|
var EMFPLUS_BRUSHTYPE_PATHGRADIENT = 3;
|
|
197
664
|
var EMFPLUS_BRUSHTYPE_LINEARGRADIENT = 4;
|
|
198
665
|
var META_EOF = 0;
|
|
@@ -443,7 +910,8 @@ function applyPen(ctx, state) {
|
|
|
443
910
|
function applyBrush(ctx, state) {
|
|
444
911
|
const paint = rop2Paint(state.rop2);
|
|
445
912
|
ctx.globalCompositeOperation = paint.gco;
|
|
446
|
-
|
|
913
|
+
const realized = realizeBrush(state);
|
|
914
|
+
if (realized.kind === "none") {
|
|
447
915
|
ctx.fillStyle = "rgba(0,0,0,0)";
|
|
448
916
|
return;
|
|
449
917
|
}
|
|
@@ -470,7 +938,7 @@ function mapFontFamily(face, map) {
|
|
|
470
938
|
return resolved;
|
|
471
939
|
}
|
|
472
940
|
function fontSizePx(state, scale = 1) {
|
|
473
|
-
return Math.max(
|
|
941
|
+
return Math.max(resolveFontPixelHeight(state.fontHeight) * Math.abs(scale || 1), 8);
|
|
474
942
|
}
|
|
475
943
|
function applyFont(ctx, state, scale = 1) {
|
|
476
944
|
const italic = state.fontItalic ? "italic " : "";
|
|
@@ -622,6 +1090,28 @@ function canvasDrawImage(ctx, image, dx, dy, dw, dh) {
|
|
|
622
1090
|
const draw = ctx.drawImage;
|
|
623
1091
|
draw.call(ctx, image, dx, dy, dw, dh);
|
|
624
1092
|
}
|
|
1093
|
+
function canvasGetImageData(ctx, x, y, w, h) {
|
|
1094
|
+
const get = ctx.getImageData;
|
|
1095
|
+
return get.call(ctx, x, y, w, h);
|
|
1096
|
+
}
|
|
1097
|
+
function canvasPutImageData(ctx, data, x, y) {
|
|
1098
|
+
const put = ctx.putImageData;
|
|
1099
|
+
put.call(ctx, data, x, y);
|
|
1100
|
+
}
|
|
1101
|
+
function canvasCreatePattern(ctx, image, repetition) {
|
|
1102
|
+
const create = ctx.createPattern;
|
|
1103
|
+
return create.call(ctx, image, repetition);
|
|
1104
|
+
}
|
|
1105
|
+
function createImageDataCompat(data, width, height) {
|
|
1106
|
+
if (typeof ImageData !== "undefined") {
|
|
1107
|
+
return new ImageData(data, width, height);
|
|
1108
|
+
}
|
|
1109
|
+
if (nodeCanvasModule) {
|
|
1110
|
+
const Ctor = nodeCanvasModule.ImageData;
|
|
1111
|
+
return new Ctor(data, width, height);
|
|
1112
|
+
}
|
|
1113
|
+
throw new ReferenceError("ImageData is not defined");
|
|
1114
|
+
}
|
|
625
1115
|
|
|
626
1116
|
// src/emf-header-parser.ts
|
|
627
1117
|
function parseEmfHeader(view) {
|
|
@@ -741,854 +1231,1267 @@ function gmh(r, h) {
|
|
|
741
1231
|
function activateGdiMappingMode(r) {
|
|
742
1232
|
r.useMappingMode = true;
|
|
743
1233
|
}
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
const
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
return
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
const t = view.getInt32(dataOff + 4, true);
|
|
792
|
-
const r = view.getInt32(dataOff + 8, true);
|
|
793
|
-
const b = view.getInt32(dataOff + 12, true);
|
|
794
|
-
if (inPath) {
|
|
795
|
-
ctx.rect(gmx(rCtx, l), gmy(rCtx, t), gmw(rCtx, r - l), gmh(rCtx, b - t));
|
|
796
|
-
} else {
|
|
797
|
-
applyBrush(ctx, state);
|
|
798
|
-
ctx.fillRect(gmx(rCtx, l), gmy(rCtx, t), gmw(rCtx, r - l), gmh(rCtx, b - t));
|
|
799
|
-
applyPen(ctx, state);
|
|
800
|
-
ctx.strokeRect(gmx(rCtx, l), gmy(rCtx, t), gmw(rCtx, r - l), gmh(rCtx, b - t));
|
|
801
|
-
}
|
|
802
|
-
}
|
|
803
|
-
return true;
|
|
804
|
-
}
|
|
805
|
-
function handleRoundRect(rCtx, dataOff, recSize) {
|
|
806
|
-
const { ctx, view, state, inPath } = rCtx;
|
|
807
|
-
if (recSize >= 32) {
|
|
808
|
-
const l = view.getInt32(dataOff, true);
|
|
809
|
-
const t = view.getInt32(dataOff + 4, true);
|
|
810
|
-
const r = view.getInt32(dataOff + 8, true);
|
|
811
|
-
const b = view.getInt32(dataOff + 12, true);
|
|
812
|
-
const rw = Math.abs(gmw(rCtx, view.getInt32(dataOff + 16, true))) / 2;
|
|
813
|
-
const rh = Math.abs(gmh(rCtx, view.getInt32(dataOff + 20, true))) / 2;
|
|
814
|
-
const x1 = gmx(rCtx, l);
|
|
815
|
-
const y1 = gmy(rCtx, t);
|
|
816
|
-
const w = gmw(rCtx, r - l);
|
|
817
|
-
const h = gmh(rCtx, b - t);
|
|
818
|
-
const drawRoundRect = () => {
|
|
819
|
-
const radius = Math.min(rw, rh, w / 2, h / 2);
|
|
820
|
-
ctx.moveTo(x1 + radius, y1);
|
|
821
|
-
ctx.lineTo(x1 + w - radius, y1);
|
|
822
|
-
ctx.arcTo(x1 + w, y1, x1 + w, y1 + radius, radius);
|
|
823
|
-
ctx.lineTo(x1 + w, y1 + h - radius);
|
|
824
|
-
ctx.arcTo(x1 + w, y1 + h, x1 + w - radius, y1 + h, radius);
|
|
825
|
-
ctx.lineTo(x1 + radius, y1 + h);
|
|
826
|
-
ctx.arcTo(x1, y1 + h, x1, y1 + h - radius, radius);
|
|
827
|
-
ctx.lineTo(x1, y1 + radius);
|
|
828
|
-
ctx.arcTo(x1, y1, x1 + radius, y1, radius);
|
|
829
|
-
ctx.closePath();
|
|
830
|
-
};
|
|
831
|
-
if (inPath) {
|
|
832
|
-
drawRoundRect();
|
|
833
|
-
} else {
|
|
834
|
-
ctx.beginPath();
|
|
835
|
-
drawRoundRect();
|
|
836
|
-
applyBrush(ctx, state);
|
|
837
|
-
ctx.fill();
|
|
838
|
-
applyPen(ctx, state);
|
|
839
|
-
ctx.stroke();
|
|
840
|
-
}
|
|
841
|
-
}
|
|
842
|
-
return true;
|
|
843
|
-
}
|
|
844
|
-
function handleEllipse(rCtx, dataOff, recSize) {
|
|
845
|
-
const { ctx, view, state, inPath } = rCtx;
|
|
846
|
-
if (recSize >= 24) {
|
|
847
|
-
const l = view.getInt32(dataOff, true);
|
|
848
|
-
const t = view.getInt32(dataOff + 4, true);
|
|
849
|
-
const r = view.getInt32(dataOff + 8, true);
|
|
850
|
-
const b = view.getInt32(dataOff + 12, true);
|
|
851
|
-
const cx = gmx(rCtx, (l + r) / 2);
|
|
852
|
-
const cy = gmy(rCtx, (t + b) / 2);
|
|
853
|
-
const rx = Math.abs(gmw(rCtx, r - l)) / 2;
|
|
854
|
-
const ry = Math.abs(gmh(rCtx, b - t)) / 2;
|
|
855
|
-
if (inPath) {
|
|
856
|
-
ctx.ellipse(cx, cy, rx, ry, 0, 0, Math.PI * 2);
|
|
857
|
-
} else {
|
|
858
|
-
ctx.beginPath();
|
|
859
|
-
ctx.ellipse(cx, cy, rx, ry, 0, 0, Math.PI * 2);
|
|
860
|
-
applyBrush(ctx, state);
|
|
861
|
-
ctx.fill();
|
|
862
|
-
applyPen(ctx, state);
|
|
863
|
-
ctx.stroke();
|
|
864
|
-
}
|
|
865
|
-
}
|
|
866
|
-
return true;
|
|
867
|
-
}
|
|
868
|
-
function handleArcFamily(rCtx, recType, dataOff, recSize) {
|
|
869
|
-
const { ctx, view, state, inPath } = rCtx;
|
|
870
|
-
if (recSize >= 40) {
|
|
871
|
-
const l = view.getInt32(dataOff, true);
|
|
872
|
-
const t = view.getInt32(dataOff + 4, true);
|
|
873
|
-
const r = view.getInt32(dataOff + 8, true);
|
|
874
|
-
const b = view.getInt32(dataOff + 12, true);
|
|
875
|
-
const startX = view.getInt32(dataOff + 16, true);
|
|
876
|
-
const startY = view.getInt32(dataOff + 20, true);
|
|
877
|
-
const endX = view.getInt32(dataOff + 24, true);
|
|
878
|
-
const endY = view.getInt32(dataOff + 28, true);
|
|
879
|
-
const cxA = (l + r) / 2;
|
|
880
|
-
const cyA = (t + b) / 2;
|
|
881
|
-
const rx = Math.abs(r - l) / 2;
|
|
882
|
-
const ry = Math.abs(b - t) / 2;
|
|
883
|
-
const startAngle = Math.atan2((startY - cyA) / (ry || 1), (startX - cxA) / (rx || 1));
|
|
884
|
-
const endAngle = Math.atan2((endY - cyA) / (ry || 1), (endX - cxA) / (rx || 1));
|
|
885
|
-
const mcx = gmx(rCtx, cxA);
|
|
886
|
-
const mcy = gmy(rCtx, cyA);
|
|
887
|
-
const mrx = Math.abs(gmw(rCtx, rx));
|
|
888
|
-
const mry = Math.abs(gmh(rCtx, ry));
|
|
889
|
-
const isArcTo = recType === EMR_ARCTO;
|
|
890
|
-
const needsFill = recType === EMR_PIE || recType === EMR_CHORD;
|
|
891
|
-
if (!inPath) {
|
|
892
|
-
ctx.beginPath();
|
|
893
|
-
}
|
|
894
|
-
if (recType === EMR_PIE) {
|
|
895
|
-
ctx.moveTo(mcx, mcy);
|
|
896
|
-
}
|
|
897
|
-
if (isArcTo) {
|
|
898
|
-
ctx.lineTo(mcx + mrx * Math.cos(startAngle), mcy + mry * Math.sin(startAngle));
|
|
899
|
-
}
|
|
900
|
-
ctx.ellipse(mcx, mcy, mrx, mry, 0, startAngle, endAngle, false);
|
|
901
|
-
if (recType === EMR_PIE || recType === EMR_CHORD) {
|
|
902
|
-
ctx.closePath();
|
|
903
|
-
}
|
|
904
|
-
if (!inPath) {
|
|
905
|
-
if (needsFill) {
|
|
906
|
-
applyBrush(ctx, state);
|
|
907
|
-
ctx.fill();
|
|
908
|
-
}
|
|
909
|
-
applyPen(ctx, state);
|
|
910
|
-
ctx.stroke();
|
|
911
|
-
}
|
|
912
|
-
if (isArcTo) {
|
|
913
|
-
state.curX = endX;
|
|
914
|
-
state.curY = endY;
|
|
915
|
-
}
|
|
916
|
-
}
|
|
917
|
-
return true;
|
|
918
|
-
}
|
|
919
|
-
function handleEmfGdiShapeRecord(rCtx, recType, dataOff, recSize) {
|
|
920
|
-
switch (recType) {
|
|
921
|
-
case EMR_SETPIXELV:
|
|
922
|
-
return handleSetPixelV(rCtx, dataOff, recSize);
|
|
923
|
-
case EMR_MOVETOEX:
|
|
924
|
-
return handleMoveToEx(rCtx, dataOff, recSize);
|
|
925
|
-
case EMR_LINETO:
|
|
926
|
-
return handleLineTo(rCtx, dataOff, recSize);
|
|
927
|
-
case EMR_RECTANGLE:
|
|
928
|
-
return handleRectangle(rCtx, dataOff, recSize);
|
|
929
|
-
case EMR_ROUNDRECT:
|
|
930
|
-
return handleRoundRect(rCtx, dataOff, recSize);
|
|
931
|
-
case EMR_ELLIPSE:
|
|
932
|
-
return handleEllipse(rCtx, dataOff, recSize);
|
|
933
|
-
case EMR_ARC:
|
|
934
|
-
case EMR_ARCTO:
|
|
935
|
-
case EMR_CHORD:
|
|
936
|
-
case EMR_PIE:
|
|
937
|
-
return handleArcFamily(rCtx, recType, dataOff, recSize);
|
|
938
|
-
default:
|
|
939
|
-
return false;
|
|
940
|
-
}
|
|
941
|
-
}
|
|
942
|
-
|
|
943
|
-
// src/emf-clip-region.ts
|
|
944
|
-
var CLIP_HUGE = 1 << 24;
|
|
945
|
-
function rectClipShape(x, y, w, h) {
|
|
946
|
-
return { cmds: [{ op: "rect", x, y, w, h }], fillRule: "nonzero", simple: true };
|
|
947
|
-
}
|
|
948
|
-
function rectsClipShape(rects) {
|
|
949
|
-
return {
|
|
950
|
-
cmds: rects.map((r) => ({ op: "rect", x: r.x, y: r.y, w: r.w, h: r.h })),
|
|
951
|
-
fillRule: "nonzero",
|
|
952
|
-
simple: true
|
|
953
|
-
};
|
|
954
|
-
}
|
|
955
|
-
function emptyClipShape() {
|
|
956
|
-
return { cmds: [{ op: "rect", x: 0, y: 0, w: 0, h: 0 }], fillRule: "nonzero", simple: true };
|
|
957
|
-
}
|
|
958
|
-
function translateClipShape(shape, dx, dy) {
|
|
959
|
-
return {
|
|
960
|
-
...shape,
|
|
961
|
-
cmds: shape.cmds.map((c) => {
|
|
962
|
-
switch (c.op) {
|
|
963
|
-
case "rect":
|
|
964
|
-
return { ...c, x: c.x + dx, y: c.y + dy };
|
|
965
|
-
case "moveTo":
|
|
966
|
-
case "lineTo":
|
|
967
|
-
return { ...c, x: c.x + dx, y: c.y + dy };
|
|
968
|
-
case "bezierCurveTo":
|
|
969
|
-
return {
|
|
970
|
-
...c,
|
|
971
|
-
cp1x: c.cp1x + dx,
|
|
972
|
-
cp1y: c.cp1y + dy,
|
|
973
|
-
cp2x: c.cp2x + dx,
|
|
974
|
-
cp2y: c.cp2y + dy,
|
|
975
|
-
x: c.x + dx,
|
|
976
|
-
y: c.y + dy
|
|
977
|
-
};
|
|
978
|
-
case "closePath":
|
|
979
|
-
return c;
|
|
980
|
-
}
|
|
981
|
-
})
|
|
1234
|
+
function hasWorldRotation(r) {
|
|
1235
|
+
const wt = r.state.worldTransform;
|
|
1236
|
+
return wt[1] !== 0 || wt[2] !== 0;
|
|
1237
|
+
}
|
|
1238
|
+
function gdiDeviceMatrix(r) {
|
|
1239
|
+
const wt = r.state.worldTransform;
|
|
1240
|
+
if (r.useMappingMode) {
|
|
1241
|
+
const kx = (r.viewportExt.cx || 1) / (r.windowExt.cx || 1);
|
|
1242
|
+
const ky = (r.viewportExt.cy || 1) / (r.windowExt.cy || 1);
|
|
1243
|
+
return [
|
|
1244
|
+
wt[0] * kx,
|
|
1245
|
+
wt[1] * ky,
|
|
1246
|
+
wt[2] * kx,
|
|
1247
|
+
wt[3] * ky,
|
|
1248
|
+
(wt[4] - r.windowOrg.x) * kx + r.viewportOrg.x,
|
|
1249
|
+
(wt[5] - r.windowOrg.y) * ky + r.viewportOrg.y
|
|
1250
|
+
];
|
|
1251
|
+
}
|
|
1252
|
+
const sx = r.sx || 1;
|
|
1253
|
+
const sy = r.sy || 1;
|
|
1254
|
+
return [wt[0] * sx, wt[1] * sy, wt[2] * sx, wt[3] * sy, (wt[4] - r.bounds.left) * sx, (wt[5] - r.bounds.top) * sy];
|
|
1255
|
+
}
|
|
1256
|
+
function gmapPoint(r, x, y) {
|
|
1257
|
+
const m = gdiDeviceMatrix(r);
|
|
1258
|
+
return { x: m[0] * x + m[2] * y + m[4], y: m[1] * x + m[3] * y + m[5] };
|
|
1259
|
+
}
|
|
1260
|
+
function gdiEllipseParams(r, cx0, cy0, rx0, ry0) {
|
|
1261
|
+
const m = gdiDeviceMatrix(r);
|
|
1262
|
+
const center = gmapPoint(r, cx0, cy0);
|
|
1263
|
+
const a11 = m[0] * rx0;
|
|
1264
|
+
const a12 = m[2] * ry0;
|
|
1265
|
+
const a21 = m[1] * rx0;
|
|
1266
|
+
const a22 = m[3] * ry0;
|
|
1267
|
+
const p = a11 * a11 + a12 * a12;
|
|
1268
|
+
const q = a11 * a21 + a12 * a22;
|
|
1269
|
+
const s = a21 * a21 + a22 * a22;
|
|
1270
|
+
const mid = (p + s) / 2;
|
|
1271
|
+
const spread = Math.sqrt(Math.max(0, ((p - s) / 2) ** 2 + q * q));
|
|
1272
|
+
const lambda1 = Math.max(0, mid + spread);
|
|
1273
|
+
const lambda2 = Math.max(0, mid - spread);
|
|
1274
|
+
const rotation = 0.5 * Math.atan2(2 * q, p - s);
|
|
1275
|
+
return {
|
|
1276
|
+
cx: center.x,
|
|
1277
|
+
cy: center.y,
|
|
1278
|
+
rx: Math.sqrt(lambda1),
|
|
1279
|
+
ry: Math.sqrt(lambda2),
|
|
1280
|
+
rotation
|
|
982
1281
|
};
|
|
983
1282
|
}
|
|
984
|
-
|
|
985
|
-
|
|
1283
|
+
|
|
1284
|
+
// src/emf-rop3.ts
|
|
1285
|
+
function rop3Index(rop) {
|
|
1286
|
+
return rop >>> 16 & 255;
|
|
1287
|
+
}
|
|
1288
|
+
function rop3Operands(index) {
|
|
1289
|
+
const t = index & 255;
|
|
1290
|
+
return {
|
|
1291
|
+
usesP: (t >> 4 & 15) !== (t & 15),
|
|
1292
|
+
usesS: (t >> 2 & 51) !== (t & 51),
|
|
1293
|
+
usesD: (t >> 1 & 85) !== (t & 85)
|
|
1294
|
+
};
|
|
1295
|
+
}
|
|
1296
|
+
function evalRop3(index, p, s, d, mask = 16777215) {
|
|
1297
|
+
let out = 0;
|
|
1298
|
+
const np = ~p;
|
|
1299
|
+
const ns = ~s;
|
|
1300
|
+
const nd = ~d;
|
|
1301
|
+
for (let i = 0; i < 8; i++) {
|
|
1302
|
+
if (index & 1 << i) {
|
|
1303
|
+
out |= (i & 4 ? p : np) & (i & 2 ? s : ns) & (i & 1 ? d : nd);
|
|
1304
|
+
}
|
|
1305
|
+
}
|
|
1306
|
+
return out & mask;
|
|
1307
|
+
}
|
|
1308
|
+
function classifyRop3(rop) {
|
|
1309
|
+
const index = rop3Index(rop);
|
|
1310
|
+
switch (index) {
|
|
1311
|
+
case 204:
|
|
1312
|
+
return { kind: "copy" };
|
|
1313
|
+
case 0:
|
|
1314
|
+
return { kind: "solid", color: "black" };
|
|
1315
|
+
case 255:
|
|
1316
|
+
return { kind: "solid", color: "white" };
|
|
1317
|
+
case 170:
|
|
1318
|
+
return { kind: "noop" };
|
|
1319
|
+
case 85:
|
|
1320
|
+
return { kind: "invert-dest" };
|
|
1321
|
+
default:
|
|
1322
|
+
return { kind: "ternary", index, operands: rop3Operands(index) };
|
|
1323
|
+
}
|
|
1324
|
+
}
|
|
1325
|
+
function applyRop3(dst, src, pattern, index, originX, originY) {
|
|
1326
|
+
const d = dst.data;
|
|
1327
|
+
const s = src ? src.data : null;
|
|
1328
|
+
const w = dst.width;
|
|
1329
|
+
const h = dst.height;
|
|
1330
|
+
const solidP = typeof pattern === "number" ? pattern : 0;
|
|
1331
|
+
const sampleP = typeof pattern === "function" ? pattern : null;
|
|
1332
|
+
for (let y = 0; y < h; y++) {
|
|
1333
|
+
for (let x = 0; x < w; x++) {
|
|
1334
|
+
const i = (y * w + x) * 4;
|
|
1335
|
+
const dv = d[i] << 16 | d[i + 1] << 8 | d[i + 2];
|
|
1336
|
+
const sv = s ? s[i] << 16 | s[i + 1] << 8 | s[i + 2] : 0;
|
|
1337
|
+
const pv = sampleP ? sampleP(originX + x, originY + y) : solidP;
|
|
1338
|
+
const r = evalRop3(index, pv, sv, dv);
|
|
1339
|
+
d[i] = r >> 16 & 255;
|
|
1340
|
+
d[i + 1] = r >> 8 & 255;
|
|
1341
|
+
d[i + 2] = r & 255;
|
|
1342
|
+
d[i + 3] = 255;
|
|
1343
|
+
}
|
|
1344
|
+
}
|
|
1345
|
+
}
|
|
1346
|
+
function clampPositiveRect(x, y, w, h, canvasW, canvasH) {
|
|
1347
|
+
let left = w < 0 ? x + w : x;
|
|
1348
|
+
let top = h < 0 ? y + h : y;
|
|
1349
|
+
let width = Math.abs(w);
|
|
1350
|
+
let height = Math.abs(h);
|
|
1351
|
+
left = Math.round(left);
|
|
1352
|
+
top = Math.round(top);
|
|
1353
|
+
width = Math.round(width);
|
|
1354
|
+
height = Math.round(height);
|
|
1355
|
+
const right = Math.min(left + width, Math.round(canvasW));
|
|
1356
|
+
const bottom = Math.min(top + height, Math.round(canvasH));
|
|
1357
|
+
left = Math.max(left, 0);
|
|
1358
|
+
top = Math.max(top, 0);
|
|
1359
|
+
width = right - left;
|
|
1360
|
+
height = bottom - top;
|
|
1361
|
+
if (width <= 0 || height <= 0) {
|
|
986
1362
|
return null;
|
|
987
1363
|
}
|
|
988
|
-
return
|
|
1364
|
+
return { x: left, y: top, w: width, h: height };
|
|
989
1365
|
}
|
|
990
|
-
|
|
991
|
-
|
|
1366
|
+
|
|
1367
|
+
// src/emf-rop2-exact.ts
|
|
1368
|
+
var ROP2_EXACT_INDEX = {
|
|
1369
|
+
[R2_MASKPEN]: 160,
|
|
1370
|
+
// P & D
|
|
1371
|
+
[R2_MERGEPEN]: 250,
|
|
1372
|
+
// P | D
|
|
1373
|
+
[R2_XORPEN]: 90,
|
|
1374
|
+
// P ^ D
|
|
1375
|
+
[R2_NOTXORPEN]: 165,
|
|
1376
|
+
// ~(P ^ D)
|
|
1377
|
+
[R2_MASKPENNOT]: 80,
|
|
1378
|
+
// P & ~D
|
|
1379
|
+
[R2_MERGEPENNOT]: 245,
|
|
1380
|
+
// P | ~D
|
|
1381
|
+
[R2_MASKNOTPEN]: 10,
|
|
1382
|
+
// ~P & D
|
|
1383
|
+
[R2_MERGENOTPEN]: 175,
|
|
1384
|
+
// ~P | D
|
|
1385
|
+
[R2_NOTMASKPEN]: 95,
|
|
1386
|
+
// ~(P & D)
|
|
1387
|
+
[R2_NOTMERGEPEN]: 5
|
|
1388
|
+
// ~(P | D)
|
|
1389
|
+
};
|
|
1390
|
+
function isExactRop2Bitwise(rop2) {
|
|
1391
|
+
return rop2 in ROP2_EXACT_INDEX;
|
|
992
1392
|
}
|
|
993
|
-
function
|
|
994
|
-
return
|
|
995
|
-
cmds: [
|
|
996
|
-
{ op: "rect", x: -CLIP_HUGE, y: -CLIP_HUGE, w: 2 * CLIP_HUGE, h: 2 * CLIP_HUGE },
|
|
997
|
-
...shape.cmds
|
|
998
|
-
],
|
|
999
|
-
fillRule: "evenodd",
|
|
1000
|
-
simple: false
|
|
1001
|
-
};
|
|
1393
|
+
function packRgb(r, g, b) {
|
|
1394
|
+
return r << 16 | g << 8 | b;
|
|
1002
1395
|
}
|
|
1003
|
-
function
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1396
|
+
function surfaceSize(ctx) {
|
|
1397
|
+
const canvas = ctx.canvas;
|
|
1398
|
+
const w = canvas?.width;
|
|
1399
|
+
const h = canvas?.height;
|
|
1400
|
+
return typeof w === "number" && typeof h === "number" && w > 0 && h > 0 ? { w, h } : null;
|
|
1401
|
+
}
|
|
1402
|
+
function paintWithExactRop2(ctx, rop2, color, draw) {
|
|
1403
|
+
const index = ROP2_EXACT_INDEX[rop2];
|
|
1404
|
+
if (index === void 0) {
|
|
1405
|
+
return false;
|
|
1406
|
+
}
|
|
1407
|
+
const size = surfaceSize(ctx);
|
|
1408
|
+
if (!size) {
|
|
1409
|
+
return false;
|
|
1410
|
+
}
|
|
1411
|
+
try {
|
|
1412
|
+
const temp = createTempCanvas(size.w, size.h);
|
|
1413
|
+
if (!temp) {
|
|
1414
|
+
return false;
|
|
1015
1415
|
}
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1416
|
+
temp.ctx.fillStyle = color;
|
|
1417
|
+
temp.ctx.strokeStyle = color;
|
|
1418
|
+
draw(temp.ctx);
|
|
1419
|
+
const paint = canvasGetImageData(temp.ctx, 0, 0, size.w, size.h);
|
|
1420
|
+
const dest = canvasGetImageData(ctx, 0, 0, size.w, size.h);
|
|
1421
|
+
const pd = paint.data;
|
|
1422
|
+
const dd = dest.data;
|
|
1423
|
+
for (let i = 0; i < dd.length; i += 4) {
|
|
1424
|
+
const coverage = pd[i + 3] / 255;
|
|
1425
|
+
if (coverage <= 0) {
|
|
1426
|
+
continue;
|
|
1027
1427
|
}
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1428
|
+
const p = packRgb(pd[i], pd[i + 1], pd[i + 2]);
|
|
1429
|
+
const d = packRgb(dd[i], dd[i + 1], dd[i + 2]);
|
|
1430
|
+
const combined = evalRop3(index, p, d, d);
|
|
1431
|
+
const cr = combined >> 16 & 255;
|
|
1432
|
+
const cg = combined >> 8 & 255;
|
|
1433
|
+
const cb = combined & 255;
|
|
1434
|
+
dd[i] = Math.round(dd[i] + (cr - dd[i]) * coverage);
|
|
1435
|
+
dd[i + 1] = Math.round(dd[i + 1] + (cg - dd[i + 1]) * coverage);
|
|
1436
|
+
dd[i + 2] = Math.round(dd[i + 2] + (cb - dd[i + 2]) * coverage);
|
|
1437
|
+
dd[i + 3] = 255;
|
|
1438
|
+
}
|
|
1439
|
+
canvasPutImageData(ctx, dest, 0, 0);
|
|
1440
|
+
return true;
|
|
1441
|
+
} catch {
|
|
1442
|
+
return false;
|
|
1443
|
+
}
|
|
1444
|
+
}
|
|
1445
|
+
|
|
1446
|
+
// src/emf-gdi-shape-paint.ts
|
|
1447
|
+
function surfaceSize2(ctx) {
|
|
1448
|
+
const canvas = ctx.canvas;
|
|
1449
|
+
const w = canvas?.width;
|
|
1450
|
+
const h = canvas?.height;
|
|
1451
|
+
return typeof w === "number" && typeof h === "number" && w > 0 && h > 0 ? { w, h } : null;
|
|
1452
|
+
}
|
|
1453
|
+
function fillCurrentPathWithGdiPattern(rCtx, fillRule) {
|
|
1454
|
+
const { ctx, bounds, state } = rCtx;
|
|
1455
|
+
const realized = realizeBrush(state);
|
|
1456
|
+
if (realized.kind !== "tile") {
|
|
1457
|
+
return false;
|
|
1458
|
+
}
|
|
1459
|
+
const size = surfaceSize2(ctx);
|
|
1460
|
+
if (!size || typeof ctx.isPointInPath !== "function") {
|
|
1461
|
+
return false;
|
|
1462
|
+
}
|
|
1463
|
+
const sx = rCtx.sx || 1;
|
|
1464
|
+
const sy = rCtx.sy || 1;
|
|
1465
|
+
try {
|
|
1466
|
+
const img = canvasGetImageData(ctx, 0, 0, size.w, size.h);
|
|
1467
|
+
const d = img.data;
|
|
1468
|
+
for (let y = 0; y < size.h; y++) {
|
|
1469
|
+
for (let x = 0; x < size.w; x++) {
|
|
1470
|
+
if (!ctx.isPointInPath(x + 0.5, y + 0.5, fillRule)) {
|
|
1471
|
+
continue;
|
|
1034
1472
|
}
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
};
|
|
1044
|
-
}
|
|
1045
|
-
if (isComposable(shape)) {
|
|
1046
|
-
return { region: [...current, invertClipShape(shape)], exact: false };
|
|
1047
|
-
}
|
|
1048
|
-
return { region: [...current, shape], exact: false };
|
|
1049
|
-
}
|
|
1050
|
-
case "complement": {
|
|
1051
|
-
if (!current) {
|
|
1052
|
-
return { region: [emptyClipShape()], exact: true };
|
|
1053
|
-
}
|
|
1054
|
-
if (current.length === 1 && isComposable(current[0])) {
|
|
1055
|
-
return { region: [shape, invertClipShape(current[0])], exact: true };
|
|
1473
|
+
const lx = Math.floor(bounds.left + (x + 0.5) / sx);
|
|
1474
|
+
const ly = Math.floor(bounds.top + (y + 0.5) / sy);
|
|
1475
|
+
const c = sampleTile(realized, lx, ly, state.brushOrgX, state.brushOrgY);
|
|
1476
|
+
const o = (y * size.w + x) * 4;
|
|
1477
|
+
d[o] = c >>> 16 & 255;
|
|
1478
|
+
d[o + 1] = c >>> 8 & 255;
|
|
1479
|
+
d[o + 2] = c & 255;
|
|
1480
|
+
d[o + 3] = 255;
|
|
1056
1481
|
}
|
|
1057
|
-
return { region: [shape], exact: false };
|
|
1058
1482
|
}
|
|
1483
|
+
canvasPutImageData(ctx, img, 0, 0);
|
|
1484
|
+
return true;
|
|
1485
|
+
} catch {
|
|
1486
|
+
return false;
|
|
1059
1487
|
}
|
|
1060
1488
|
}
|
|
1061
|
-
function
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
if (incoming && incoming.length === 1) {
|
|
1066
|
-
return combineClip(current, incoming[0], op);
|
|
1489
|
+
function fillShapeExactOrFast(rCtx, buildPath, fillRule = "nonzero") {
|
|
1490
|
+
const { ctx, state } = rCtx;
|
|
1491
|
+
if (state.brushStyle === 1) {
|
|
1492
|
+
return;
|
|
1067
1493
|
}
|
|
1068
|
-
if (
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
return { region: current, exact: true };
|
|
1072
|
-
case "union":
|
|
1073
|
-
return { region: null, exact: true };
|
|
1074
|
-
case "exclude":
|
|
1075
|
-
return { region: [emptyClipShape()], exact: true };
|
|
1076
|
-
case "xor":
|
|
1077
|
-
case "complement": {
|
|
1078
|
-
if (!current) {
|
|
1079
|
-
return { region: [emptyClipShape()], exact: true };
|
|
1080
|
-
}
|
|
1081
|
-
if (current.length === 1) {
|
|
1082
|
-
return combineClip(null, current[0], "exclude");
|
|
1083
|
-
}
|
|
1084
|
-
return { region: current, exact: false };
|
|
1085
|
-
}
|
|
1494
|
+
if (realizeBrush(state).kind === "tile") {
|
|
1495
|
+
if (fillCurrentPathWithGdiPattern(rCtx, fillRule)) {
|
|
1496
|
+
return;
|
|
1086
1497
|
}
|
|
1087
|
-
}
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
return { region: current, exact: false };
|
|
1097
|
-
case "complement": {
|
|
1098
|
-
if (!current) {
|
|
1099
|
-
return { region: [emptyClipShape()], exact: true };
|
|
1100
|
-
}
|
|
1101
|
-
if (current.length === 1) {
|
|
1102
|
-
return combineClip(incoming, current[0], "exclude");
|
|
1498
|
+
} else {
|
|
1499
|
+
const paint = rop2Paint(state.rop2);
|
|
1500
|
+
if (!paint.exact && isExactRop2Bitwise(state.rop2)) {
|
|
1501
|
+
const handled = paintWithExactRop2(ctx, state.rop2, state.brushColor, (scratch) => {
|
|
1502
|
+
buildPath(scratch);
|
|
1503
|
+
scratch.fill(fillRule);
|
|
1504
|
+
});
|
|
1505
|
+
if (handled) {
|
|
1506
|
+
return;
|
|
1103
1507
|
}
|
|
1104
|
-
return { region: incoming, exact: false };
|
|
1105
1508
|
}
|
|
1106
1509
|
}
|
|
1510
|
+
applyBrush(ctx, state);
|
|
1511
|
+
ctx.fill(fillRule);
|
|
1107
1512
|
}
|
|
1108
|
-
function
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1513
|
+
function applyPenGeometry(ctx, state) {
|
|
1514
|
+
ctx.lineWidth = Math.max(state.penWidth, 1);
|
|
1515
|
+
switch (state.penStyle) {
|
|
1516
|
+
case 1:
|
|
1517
|
+
ctx.setLineDash([8, 4]);
|
|
1518
|
+
break;
|
|
1519
|
+
case 2:
|
|
1520
|
+
ctx.setLineDash([2, 2]);
|
|
1521
|
+
break;
|
|
1522
|
+
case 3:
|
|
1523
|
+
ctx.setLineDash([8, 4, 2, 4]);
|
|
1524
|
+
break;
|
|
1525
|
+
case 4:
|
|
1526
|
+
ctx.setLineDash([8, 4, 2, 4, 2, 4]);
|
|
1527
|
+
break;
|
|
1528
|
+
default:
|
|
1529
|
+
ctx.setLineDash([]);
|
|
1530
|
+
break;
|
|
1531
|
+
}
|
|
1532
|
+
}
|
|
1533
|
+
function strokeShapeExactOrFast(rCtx, buildPath) {
|
|
1534
|
+
const { ctx, state } = rCtx;
|
|
1535
|
+
const paint = rop2Paint(state.rop2);
|
|
1536
|
+
if (!paint.exact && isExactRop2Bitwise(state.rop2) && state.penStyle !== 5) {
|
|
1537
|
+
const handled = paintWithExactRop2(ctx, state.rop2, state.penColor, (scratch) => {
|
|
1538
|
+
applyPenGeometry(scratch, state);
|
|
1539
|
+
buildPath(scratch);
|
|
1540
|
+
scratch.stroke();
|
|
1541
|
+
});
|
|
1542
|
+
if (handled) {
|
|
1543
|
+
return;
|
|
1126
1544
|
}
|
|
1127
1545
|
}
|
|
1546
|
+
applyPen(ctx, state);
|
|
1547
|
+
ctx.stroke();
|
|
1128
1548
|
}
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1549
|
+
|
|
1550
|
+
// src/emf-gdi-draw-shapes.ts
|
|
1551
|
+
function needsPathBasedRectangle(rCtx) {
|
|
1552
|
+
const paint = rop2Paint(rCtx.state.rop2);
|
|
1553
|
+
const ropExact = !paint.exact && isExactRop2Bitwise(rCtx.state.rop2);
|
|
1554
|
+
return ropExact || realizeBrush(rCtx.state).kind === "tile";
|
|
1555
|
+
}
|
|
1556
|
+
function handleSetPixelV(rCtx, dataOff, recSize) {
|
|
1557
|
+
const { ctx, view } = rCtx;
|
|
1558
|
+
if (recSize >= 20) {
|
|
1559
|
+
const x = view.getInt32(dataOff, true);
|
|
1560
|
+
const y = view.getInt32(dataOff + 4, true);
|
|
1561
|
+
const color = readColorRef(view, dataOff + 8);
|
|
1562
|
+
const p = hasWorldRotation(rCtx) ? gmapPoint(rCtx, x, y) : { x: gmx(rCtx, x), y: gmy(rCtx, y) };
|
|
1563
|
+
ctx.fillStyle = color;
|
|
1564
|
+
ctx.fillRect(p.x, p.y, 1, 1);
|
|
1565
|
+
}
|
|
1566
|
+
return true;
|
|
1567
|
+
}
|
|
1568
|
+
function handleMoveToEx(rCtx, dataOff, recSize) {
|
|
1569
|
+
const { ctx, view, state, inPath } = rCtx;
|
|
1570
|
+
if (recSize >= 16) {
|
|
1571
|
+
state.curX = view.getInt32(dataOff, true);
|
|
1572
|
+
state.curY = view.getInt32(dataOff + 4, true);
|
|
1573
|
+
if (inPath) {
|
|
1574
|
+
const p = hasWorldRotation(rCtx) ? gmapPoint(rCtx, state.curX, state.curY) : { x: gmx(rCtx, state.curX), y: gmy(rCtx, state.curY) };
|
|
1575
|
+
ctx.moveTo(p.x, p.y);
|
|
1136
1576
|
}
|
|
1137
1577
|
}
|
|
1578
|
+
return true;
|
|
1138
1579
|
}
|
|
1139
|
-
function
|
|
1140
|
-
const { ctx } =
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1580
|
+
function handleLineTo(rCtx, dataOff, recSize) {
|
|
1581
|
+
const { ctx, view, state, inPath } = rCtx;
|
|
1582
|
+
if (recSize >= 16) {
|
|
1583
|
+
const lx = view.getInt32(dataOff, true);
|
|
1584
|
+
const ly = view.getInt32(dataOff + 4, true);
|
|
1585
|
+
const rotated = hasWorldRotation(rCtx);
|
|
1586
|
+
const to = rotated ? gmapPoint(rCtx, lx, ly) : { x: gmx(rCtx, lx), y: gmy(rCtx, ly) };
|
|
1587
|
+
if (inPath) {
|
|
1588
|
+
ctx.lineTo(to.x, to.y);
|
|
1589
|
+
} else {
|
|
1590
|
+
const from = rotated ? gmapPoint(rCtx, state.curX, state.curY) : { x: gmx(rCtx, state.curX), y: gmy(rCtx, state.curY) };
|
|
1591
|
+
const build = (c) => {
|
|
1592
|
+
c.beginPath();
|
|
1593
|
+
c.moveTo(from.x, from.y);
|
|
1594
|
+
c.lineTo(to.x, to.y);
|
|
1595
|
+
};
|
|
1596
|
+
build(ctx);
|
|
1597
|
+
strokeShapeExactOrFast(rCtx, build);
|
|
1598
|
+
}
|
|
1599
|
+
state.curX = lx;
|
|
1600
|
+
state.curY = ly;
|
|
1144
1601
|
}
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1602
|
+
return true;
|
|
1603
|
+
}
|
|
1604
|
+
function handleRectangle(rCtx, dataOff, recSize) {
|
|
1605
|
+
const { ctx, view, state, inPath } = rCtx;
|
|
1606
|
+
if (recSize >= 24) {
|
|
1607
|
+
const l = view.getInt32(dataOff, true);
|
|
1608
|
+
const t = view.getInt32(dataOff + 4, true);
|
|
1609
|
+
const r = view.getInt32(dataOff + 8, true);
|
|
1610
|
+
const b = view.getInt32(dataOff + 12, true);
|
|
1611
|
+
const rotated = hasWorldRotation(rCtx);
|
|
1612
|
+
if (rotated) {
|
|
1613
|
+
const p1 = gmapPoint(rCtx, l, t);
|
|
1614
|
+
const p2 = gmapPoint(rCtx, r, t);
|
|
1615
|
+
const p3 = gmapPoint(rCtx, r, b);
|
|
1616
|
+
const p4 = gmapPoint(rCtx, l, b);
|
|
1617
|
+
const build = (c) => {
|
|
1618
|
+
c.beginPath();
|
|
1619
|
+
c.moveTo(p1.x, p1.y);
|
|
1620
|
+
c.lineTo(p2.x, p2.y);
|
|
1621
|
+
c.lineTo(p3.x, p3.y);
|
|
1622
|
+
c.lineTo(p4.x, p4.y);
|
|
1623
|
+
c.closePath();
|
|
1624
|
+
};
|
|
1625
|
+
if (inPath) {
|
|
1626
|
+
build(ctx);
|
|
1627
|
+
} else {
|
|
1628
|
+
build(ctx);
|
|
1629
|
+
fillShapeExactOrFast(rCtx, build);
|
|
1630
|
+
strokeShapeExactOrFast(rCtx, build);
|
|
1631
|
+
}
|
|
1632
|
+
return true;
|
|
1633
|
+
}
|
|
1634
|
+
if (inPath) {
|
|
1635
|
+
ctx.rect(gmx(rCtx, l), gmy(rCtx, t), gmw(rCtx, r - l), gmh(rCtx, b - t));
|
|
1636
|
+
} else {
|
|
1637
|
+
const x = gmx(rCtx, l);
|
|
1638
|
+
const y = gmy(rCtx, t);
|
|
1639
|
+
const w = gmw(rCtx, r - l);
|
|
1640
|
+
const h = gmh(rCtx, b - t);
|
|
1641
|
+
if (needsPathBasedRectangle(rCtx)) {
|
|
1642
|
+
const build = (c) => {
|
|
1643
|
+
c.beginPath();
|
|
1644
|
+
c.rect(x, y, w, h);
|
|
1645
|
+
};
|
|
1646
|
+
build(ctx);
|
|
1647
|
+
fillShapeExactOrFast(rCtx, build);
|
|
1648
|
+
strokeShapeExactOrFast(rCtx, build);
|
|
1649
|
+
} else {
|
|
1650
|
+
applyBrush(ctx, state);
|
|
1651
|
+
ctx.fillRect(x, y, w, h);
|
|
1652
|
+
applyPen(ctx, state);
|
|
1653
|
+
ctx.strokeRect(x, y, w, h);
|
|
1654
|
+
}
|
|
1150
1655
|
}
|
|
1151
|
-
applyClipShapes(ctx, region);
|
|
1152
1656
|
}
|
|
1657
|
+
return true;
|
|
1153
1658
|
}
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
const
|
|
1163
|
-
const
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1659
|
+
function handleRoundRect(rCtx, dataOff, recSize) {
|
|
1660
|
+
const { ctx, view, inPath } = rCtx;
|
|
1661
|
+
if (recSize >= 32) {
|
|
1662
|
+
const l = view.getInt32(dataOff, true);
|
|
1663
|
+
const t = view.getInt32(dataOff + 4, true);
|
|
1664
|
+
const r = view.getInt32(dataOff + 8, true);
|
|
1665
|
+
const b = view.getInt32(dataOff + 12, true);
|
|
1666
|
+
const cornerW = view.getInt32(dataOff + 16, true);
|
|
1667
|
+
const cornerH = view.getInt32(dataOff + 20, true);
|
|
1668
|
+
const rotated = hasWorldRotation(rCtx);
|
|
1669
|
+
const rw = Math.abs(rotated ? cornerW / 2 : gmw(rCtx, cornerW) / 2);
|
|
1670
|
+
const rh = Math.abs(rotated ? cornerH / 2 : gmh(rCtx, cornerH) / 2);
|
|
1671
|
+
const x1 = rotated ? l : gmx(rCtx, l);
|
|
1672
|
+
const y1 = rotated ? t : gmy(rCtx, t);
|
|
1673
|
+
const w = rotated ? r - l : gmw(rCtx, r - l);
|
|
1674
|
+
const h = rotated ? b - t : gmh(rCtx, b - t);
|
|
1675
|
+
const map = (x, y) => rotated ? gmapPoint(rCtx, x, y) : { x, y };
|
|
1676
|
+
const drawRoundRect = (c) => {
|
|
1677
|
+
const radius = Math.min(rw, rh, Math.abs(w) / 2, Math.abs(h) / 2);
|
|
1678
|
+
const start = map(x1 + radius, y1);
|
|
1679
|
+
c.moveTo(start.x, start.y);
|
|
1680
|
+
if (rotated) {
|
|
1681
|
+
const corners = [
|
|
1682
|
+
[x1 + w - radius, y1],
|
|
1683
|
+
[x1 + w, y1 + radius],
|
|
1684
|
+
[x1 + w, y1 + h - radius],
|
|
1685
|
+
[x1 + w - radius, y1 + h],
|
|
1686
|
+
[x1 + radius, y1 + h],
|
|
1687
|
+
[x1, y1 + h - radius],
|
|
1688
|
+
[x1, y1 + radius],
|
|
1689
|
+
[x1 + radius, y1]
|
|
1690
|
+
];
|
|
1691
|
+
for (const [px, py] of corners) {
|
|
1692
|
+
const q = map(px, py);
|
|
1693
|
+
c.lineTo(q.x, q.y);
|
|
1174
1694
|
}
|
|
1175
|
-
x += view.getUint8(off);
|
|
1176
|
-
y -= view.getUint8(off + 1);
|
|
1177
|
-
off += 2;
|
|
1178
1695
|
} else {
|
|
1179
|
-
const
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
const idx = nibble === 0 ? byte >> 4 & 15 : byte & 15;
|
|
1205
|
-
if (idx < colorTable.length && x < width) {
|
|
1206
|
-
setPixel(
|
|
1207
|
-
x,
|
|
1208
|
-
height - 1 - y,
|
|
1209
|
-
colorTable[idx][0],
|
|
1210
|
-
colorTable[idx][1],
|
|
1211
|
-
colorTable[idx][2],
|
|
1212
|
-
255
|
|
1213
|
-
);
|
|
1214
|
-
}
|
|
1215
|
-
x++;
|
|
1216
|
-
pi++;
|
|
1217
|
-
}
|
|
1218
|
-
}
|
|
1219
|
-
if (bytes & 1) {
|
|
1220
|
-
off++;
|
|
1221
|
-
}
|
|
1222
|
-
}
|
|
1223
|
-
}
|
|
1224
|
-
} else if (!isRle4) {
|
|
1225
|
-
const idx = second;
|
|
1226
|
-
const c = idx < colorTable.length ? colorTable[idx] : [0, 0, 0];
|
|
1227
|
-
for (let i = 0; i < first && x < width; i++) {
|
|
1228
|
-
setPixel(x, height - 1 - y, c[0], c[1], c[2], 255);
|
|
1229
|
-
x++;
|
|
1230
|
-
}
|
|
1696
|
+
const corner2 = map(x1 + w - radius, y1);
|
|
1697
|
+
c.lineTo(corner2.x, corner2.y);
|
|
1698
|
+
const arc1a = map(x1 + w, y1);
|
|
1699
|
+
const arc1b = map(x1 + w, y1 + radius);
|
|
1700
|
+
c.arcTo(arc1a.x, arc1a.y, arc1b.x, arc1b.y, radius);
|
|
1701
|
+
const corner3 = map(x1 + w, y1 + h - radius);
|
|
1702
|
+
c.lineTo(corner3.x, corner3.y);
|
|
1703
|
+
const arc2a = map(x1 + w, y1 + h);
|
|
1704
|
+
const arc2b = map(x1 + w - radius, y1 + h);
|
|
1705
|
+
c.arcTo(arc2a.x, arc2a.y, arc2b.x, arc2b.y, radius);
|
|
1706
|
+
const corner4 = map(x1 + radius, y1 + h);
|
|
1707
|
+
c.lineTo(corner4.x, corner4.y);
|
|
1708
|
+
const arc3a = map(x1, y1 + h);
|
|
1709
|
+
const arc3b = map(x1, y1 + h - radius);
|
|
1710
|
+
c.arcTo(arc3a.x, arc3a.y, arc3b.x, arc3b.y, radius);
|
|
1711
|
+
const corner1 = map(x1, y1 + radius);
|
|
1712
|
+
c.lineTo(corner1.x, corner1.y);
|
|
1713
|
+
const arc4a = map(x1, y1);
|
|
1714
|
+
const arc4b = map(x1 + radius, y1);
|
|
1715
|
+
c.arcTo(arc4a.x, arc4a.y, arc4b.x, arc4b.y, radius);
|
|
1716
|
+
}
|
|
1717
|
+
c.closePath();
|
|
1718
|
+
};
|
|
1719
|
+
if (inPath) {
|
|
1720
|
+
drawRoundRect(ctx);
|
|
1231
1721
|
} else {
|
|
1232
|
-
const
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
height - 1 - y,
|
|
1240
|
-
colorTable[idx][0],
|
|
1241
|
-
colorTable[idx][1],
|
|
1242
|
-
colorTable[idx][2],
|
|
1243
|
-
255
|
|
1244
|
-
);
|
|
1245
|
-
}
|
|
1246
|
-
x++;
|
|
1247
|
-
}
|
|
1722
|
+
const build = (c) => {
|
|
1723
|
+
c.beginPath();
|
|
1724
|
+
drawRoundRect(c);
|
|
1725
|
+
};
|
|
1726
|
+
build(ctx);
|
|
1727
|
+
fillShapeExactOrFast(rCtx, build);
|
|
1728
|
+
strokeShapeExactOrFast(rCtx, build);
|
|
1248
1729
|
}
|
|
1249
1730
|
}
|
|
1250
|
-
return
|
|
1251
|
-
new Uint8ClampedArray(out.buffer, out.byteOffset, out.byteLength),
|
|
1252
|
-
width,
|
|
1253
|
-
height
|
|
1254
|
-
);
|
|
1731
|
+
return true;
|
|
1255
1732
|
}
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1733
|
+
function handleEllipse(rCtx, dataOff, recSize) {
|
|
1734
|
+
const { ctx, view, inPath } = rCtx;
|
|
1735
|
+
if (recSize >= 24) {
|
|
1736
|
+
const l = view.getInt32(dataOff, true);
|
|
1737
|
+
const t = view.getInt32(dataOff + 4, true);
|
|
1738
|
+
const r = view.getInt32(dataOff + 8, true);
|
|
1739
|
+
const b = view.getInt32(dataOff + 12, true);
|
|
1740
|
+
const params = hasWorldRotation(rCtx) ? gdiEllipseParams(rCtx, (l + r) / 2, (t + b) / 2, Math.abs(r - l) / 2, Math.abs(b - t) / 2) : {
|
|
1741
|
+
cx: gmx(rCtx, (l + r) / 2),
|
|
1742
|
+
cy: gmy(rCtx, (t + b) / 2),
|
|
1743
|
+
rx: Math.abs(gmw(rCtx, r - l)) / 2,
|
|
1744
|
+
ry: Math.abs(gmh(rCtx, b - t)) / 2,
|
|
1745
|
+
rotation: 0
|
|
1746
|
+
};
|
|
1747
|
+
if (inPath) {
|
|
1748
|
+
ctx.ellipse(params.cx, params.cy, params.rx, params.ry, params.rotation, 0, Math.PI * 2);
|
|
1749
|
+
} else {
|
|
1750
|
+
const build = (c) => {
|
|
1751
|
+
c.beginPath();
|
|
1752
|
+
c.ellipse(params.cx, params.cy, params.rx, params.ry, params.rotation, 0, Math.PI * 2);
|
|
1753
|
+
};
|
|
1754
|
+
build(ctx);
|
|
1755
|
+
fillShapeExactOrFast(rCtx, build);
|
|
1756
|
+
strokeShapeExactOrFast(rCtx, build);
|
|
1757
|
+
}
|
|
1267
1758
|
}
|
|
1268
|
-
return
|
|
1759
|
+
return true;
|
|
1269
1760
|
}
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
const
|
|
1277
|
-
|
|
1278
|
-
|
|
1761
|
+
function handleArcFamily(rCtx, recType, dataOff, recSize) {
|
|
1762
|
+
const { ctx, view, state, inPath } = rCtx;
|
|
1763
|
+
if (recSize >= 40) {
|
|
1764
|
+
const l = view.getInt32(dataOff, true);
|
|
1765
|
+
const t = view.getInt32(dataOff + 4, true);
|
|
1766
|
+
const r = view.getInt32(dataOff + 8, true);
|
|
1767
|
+
const b = view.getInt32(dataOff + 12, true);
|
|
1768
|
+
const startX = view.getInt32(dataOff + 16, true);
|
|
1769
|
+
const startY = view.getInt32(dataOff + 20, true);
|
|
1770
|
+
const endX = view.getInt32(dataOff + 24, true);
|
|
1771
|
+
const endY = view.getInt32(dataOff + 28, true);
|
|
1772
|
+
const cxA = (l + r) / 2;
|
|
1773
|
+
const cyA = (t + b) / 2;
|
|
1774
|
+
const rx = Math.abs(r - l) / 2;
|
|
1775
|
+
const ry = Math.abs(b - t) / 2;
|
|
1776
|
+
const startAngle = Math.atan2((startY - cyA) / (ry || 1), (startX - cxA) / (rx || 1));
|
|
1777
|
+
const endAngle = Math.atan2((endY - cyA) / (ry || 1), (endX - cxA) / (rx || 1));
|
|
1778
|
+
const rotated = hasWorldRotation(rCtx);
|
|
1779
|
+
const params = rotated ? gdiEllipseParams(rCtx, cxA, cyA, rx, ry) : {
|
|
1780
|
+
cx: gmx(rCtx, cxA),
|
|
1781
|
+
cy: gmy(rCtx, cyA),
|
|
1782
|
+
rx: Math.abs(gmw(rCtx, rx)),
|
|
1783
|
+
ry: Math.abs(gmh(rCtx, ry)),
|
|
1784
|
+
rotation: 0
|
|
1785
|
+
};
|
|
1786
|
+
const isArcTo = recType === EMR_ARCTO;
|
|
1787
|
+
const needsFill = recType === EMR_PIE || recType === EMR_CHORD;
|
|
1788
|
+
const startPoint = rotated ? gmapPoint(rCtx, cxA + rx * Math.cos(startAngle), cyA + ry * Math.sin(startAngle)) : { x: params.cx + params.rx * Math.cos(startAngle), y: params.cy + params.ry * Math.sin(startAngle) };
|
|
1789
|
+
const build = (c) => {
|
|
1790
|
+
if (recType === EMR_PIE) {
|
|
1791
|
+
c.moveTo(params.cx, params.cy);
|
|
1792
|
+
}
|
|
1793
|
+
if (isArcTo) {
|
|
1794
|
+
c.lineTo(startPoint.x, startPoint.y);
|
|
1795
|
+
}
|
|
1796
|
+
c.ellipse(params.cx, params.cy, params.rx, params.ry, params.rotation, startAngle, endAngle, false);
|
|
1797
|
+
if (needsFill) {
|
|
1798
|
+
c.closePath();
|
|
1799
|
+
}
|
|
1800
|
+
};
|
|
1801
|
+
if (!inPath) {
|
|
1802
|
+
ctx.beginPath();
|
|
1803
|
+
}
|
|
1804
|
+
build(ctx);
|
|
1805
|
+
if (!inPath) {
|
|
1806
|
+
const buildWithPath = (c) => {
|
|
1807
|
+
c.beginPath();
|
|
1808
|
+
build(c);
|
|
1809
|
+
};
|
|
1810
|
+
if (needsFill) {
|
|
1811
|
+
fillShapeExactOrFast(rCtx, buildWithPath);
|
|
1812
|
+
}
|
|
1813
|
+
strokeShapeExactOrFast(rCtx, buildWithPath);
|
|
1814
|
+
}
|
|
1815
|
+
if (isArcTo) {
|
|
1816
|
+
state.curX = endX;
|
|
1817
|
+
state.curY = endY;
|
|
1279
1818
|
}
|
|
1280
|
-
rMask = view.getUint32(bfOff, true);
|
|
1281
|
-
gMask = view.getUint32(bfOff + 4, true);
|
|
1282
|
-
bMask = view.getUint32(bfOff + 8, true);
|
|
1283
|
-
rShift = countTrailingZeros(rMask);
|
|
1284
|
-
gShift = countTrailingZeros(gMask);
|
|
1285
|
-
bShift = countTrailingZeros(bMask);
|
|
1286
|
-
rMax = rMask >>> rShift || 1;
|
|
1287
|
-
gMax = gMask >>> gShift || 1;
|
|
1288
|
-
bMax = bMask >>> bShift || 1;
|
|
1289
|
-
} else if (bitCount === 16) {
|
|
1290
|
-
rMask = 31744;
|
|
1291
|
-
gMask = 992;
|
|
1292
|
-
bMask = 31;
|
|
1293
|
-
rShift = 10;
|
|
1294
|
-
gShift = 5;
|
|
1295
|
-
bShift = 0;
|
|
1296
|
-
rMax = 31;
|
|
1297
|
-
gMax = 31;
|
|
1298
|
-
bMax = 31;
|
|
1299
1819
|
}
|
|
1300
|
-
return
|
|
1820
|
+
return true;
|
|
1301
1821
|
}
|
|
1302
|
-
function
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1822
|
+
function handleEmfGdiShapeRecord(rCtx, recType, dataOff, recSize) {
|
|
1823
|
+
switch (recType) {
|
|
1824
|
+
case EMR_SETPIXELV:
|
|
1825
|
+
return handleSetPixelV(rCtx, dataOff, recSize);
|
|
1826
|
+
case EMR_MOVETOEX:
|
|
1827
|
+
return handleMoveToEx(rCtx, dataOff, recSize);
|
|
1828
|
+
case EMR_LINETO:
|
|
1829
|
+
return handleLineTo(rCtx, dataOff, recSize);
|
|
1830
|
+
case EMR_RECTANGLE:
|
|
1831
|
+
return handleRectangle(rCtx, dataOff, recSize);
|
|
1832
|
+
case EMR_ROUNDRECT:
|
|
1833
|
+
return handleRoundRect(rCtx, dataOff, recSize);
|
|
1834
|
+
case EMR_ELLIPSE:
|
|
1835
|
+
return handleEllipse(rCtx, dataOff, recSize);
|
|
1836
|
+
case EMR_ARC:
|
|
1837
|
+
case EMR_ARCTO:
|
|
1838
|
+
case EMR_CHORD:
|
|
1839
|
+
case EMR_PIE:
|
|
1840
|
+
return handleArcFamily(rCtx, recType, dataOff, recSize);
|
|
1841
|
+
default:
|
|
1842
|
+
return false;
|
|
1843
|
+
}
|
|
1844
|
+
}
|
|
1845
|
+
|
|
1846
|
+
// src/emf-gdi-stretch.ts
|
|
1847
|
+
var BLACKONWHITE = 1;
|
|
1848
|
+
var WHITEONBLACK = 2;
|
|
1849
|
+
var HALFTONE = 4;
|
|
1850
|
+
function gdiNearest(i, srcLen, dstLen) {
|
|
1851
|
+
const k = Math.floor((2 * i + 1) * srcLen / (2 * dstLen));
|
|
1852
|
+
return Math.max(0, Math.min(srcLen - 1, k));
|
|
1853
|
+
}
|
|
1854
|
+
function axisSamples(srcStart, srcLen, dstLen, reverse, combine) {
|
|
1855
|
+
const out = [];
|
|
1856
|
+
const at = (k) => reverse ? srcStart + srcLen - 1 - k : srcStart + k;
|
|
1857
|
+
if (dstLen <= 0 || srcLen <= 0) {
|
|
1858
|
+
return out;
|
|
1859
|
+
}
|
|
1860
|
+
let prev = -1;
|
|
1861
|
+
for (let i = 0; i < dstLen; i++) {
|
|
1862
|
+
const c = gdiNearest(i, srcLen, dstLen);
|
|
1863
|
+
const run = [];
|
|
1864
|
+
const from = combine && dstLen < srcLen ? prev + 1 : c;
|
|
1865
|
+
for (let k = from; k <= c; k++) {
|
|
1866
|
+
run.push(at(k));
|
|
1867
|
+
}
|
|
1868
|
+
out.push(run);
|
|
1869
|
+
prev = c;
|
|
1870
|
+
}
|
|
1871
|
+
return out;
|
|
1872
|
+
}
|
|
1873
|
+
function stretchGdi(src, sx, sy, sw, sh, dw, dh, mode) {
|
|
1874
|
+
const W = Math.max(0, Math.round(Math.abs(dw)));
|
|
1875
|
+
const H = Math.max(0, Math.round(Math.abs(dh)));
|
|
1876
|
+
const flipX = dw < 0 !== sw < 0;
|
|
1877
|
+
const flipY = dh < 0 !== sh < 0;
|
|
1878
|
+
const combine = mode === BLACKONWHITE || mode === WHITEONBLACK;
|
|
1879
|
+
const cols = axisSamples(Math.round(Math.min(sx, sx + sw)), Math.round(Math.abs(sw)), W, flipX, combine);
|
|
1880
|
+
const rows = axisSamples(Math.round(Math.min(sy, sy + sh)), Math.round(Math.abs(sh)), H, flipY, combine);
|
|
1881
|
+
const data = new Uint8ClampedArray(W * H * 4);
|
|
1882
|
+
const s = src.data;
|
|
1883
|
+
const read = (x, y) => {
|
|
1884
|
+
if (x < 0 || y < 0 || x >= src.width || y >= src.height) {
|
|
1885
|
+
return 0;
|
|
1886
|
+
}
|
|
1887
|
+
const i = (y * src.width + x) * 4;
|
|
1888
|
+
return s[i] << 16 | s[i + 1] << 8 | s[i + 2];
|
|
1889
|
+
};
|
|
1890
|
+
for (let y = 0; y < H; y++) {
|
|
1891
|
+
const ys = rows[y] ?? [];
|
|
1892
|
+
for (let x = 0; x < W; x++) {
|
|
1893
|
+
const xs = cols[x] ?? [];
|
|
1894
|
+
let v = mode === BLACKONWHITE ? 16777215 : 0;
|
|
1895
|
+
let first = true;
|
|
1896
|
+
for (const yy of ys) {
|
|
1897
|
+
for (const xx of xs) {
|
|
1898
|
+
const p = read(xx, yy);
|
|
1899
|
+
if (mode === BLACKONWHITE) {
|
|
1900
|
+
v &= p;
|
|
1901
|
+
} else if (mode === WHITEONBLACK) {
|
|
1902
|
+
v |= p;
|
|
1903
|
+
} else if (first) {
|
|
1904
|
+
v = p;
|
|
1905
|
+
}
|
|
1906
|
+
first = false;
|
|
1337
1907
|
}
|
|
1338
|
-
out[dstPx + 3] = 255;
|
|
1339
|
-
} else if (bitCount === 16) {
|
|
1340
|
-
const val = view.getUint16(rowStart + x * 2, true);
|
|
1341
|
-
out[dstPx] = Math.round(((val & rMask) >>> rShift) * 255 / rMax);
|
|
1342
|
-
out[dstPx + 1] = Math.round(((val & gMask) >>> gShift) * 255 / gMax);
|
|
1343
|
-
out[dstPx + 2] = Math.round(((val & bMask) >>> bShift) * 255 / bMax);
|
|
1344
|
-
out[dstPx + 3] = 255;
|
|
1345
|
-
} else if (bitCount === 24) {
|
|
1346
|
-
const srcPx = rowStart + x * 3;
|
|
1347
|
-
out[dstPx] = view.getUint8(srcPx + 2);
|
|
1348
|
-
out[dstPx + 1] = view.getUint8(srcPx + 1);
|
|
1349
|
-
out[dstPx + 2] = view.getUint8(srcPx);
|
|
1350
|
-
out[dstPx + 3] = 255;
|
|
1351
|
-
} else {
|
|
1352
|
-
const srcPx = rowStart + x * 4;
|
|
1353
|
-
const bb = view.getUint8(srcPx);
|
|
1354
|
-
const gg = view.getUint8(srcPx + 1);
|
|
1355
|
-
const rr = view.getUint8(srcPx + 2);
|
|
1356
|
-
const aa = view.getUint8(srcPx + 3);
|
|
1357
|
-
out[dstPx] = rr;
|
|
1358
|
-
out[dstPx + 1] = gg;
|
|
1359
|
-
out[dstPx + 2] = bb;
|
|
1360
|
-
out[dstPx + 3] = aa === 0 ? 255 : aa;
|
|
1361
1908
|
}
|
|
1909
|
+
const o = (y * W + x) * 4;
|
|
1910
|
+
data[o] = v >> 16 & 255;
|
|
1911
|
+
data[o + 1] = v >> 8 & 255;
|
|
1912
|
+
data[o + 2] = v & 255;
|
|
1913
|
+
data[o + 3] = 255;
|
|
1362
1914
|
}
|
|
1363
1915
|
}
|
|
1916
|
+
return { width: W, height: H, data };
|
|
1364
1917
|
}
|
|
1365
1918
|
|
|
1366
|
-
// src/emf-
|
|
1367
|
-
function
|
|
1368
|
-
if (
|
|
1369
|
-
return
|
|
1919
|
+
// src/emf-gdi-draw-bitmap.ts
|
|
1920
|
+
function patternOperand(rCtx, brush) {
|
|
1921
|
+
if (brush.kind === "solid") {
|
|
1922
|
+
return brush.rgb;
|
|
1370
1923
|
}
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
return null;
|
|
1924
|
+
if (brush.kind === "none") {
|
|
1925
|
+
return 0;
|
|
1374
1926
|
}
|
|
1375
|
-
const
|
|
1376
|
-
const
|
|
1377
|
-
const
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1927
|
+
const { state, bounds } = rCtx;
|
|
1928
|
+
const sx = rCtx.sx || 1;
|
|
1929
|
+
const sy = rCtx.sy || 1;
|
|
1930
|
+
return (x, y) => sampleTile(
|
|
1931
|
+
brush,
|
|
1932
|
+
Math.floor(bounds.left + (x + 0.5) / sx),
|
|
1933
|
+
Math.floor(bounds.top + (y + 0.5) / sy),
|
|
1934
|
+
state.brushOrgX,
|
|
1935
|
+
state.brushOrgY
|
|
1936
|
+
);
|
|
1937
|
+
}
|
|
1938
|
+
function drawSourceMapped(target, decoded, req, offsetX, offsetY, mode) {
|
|
1939
|
+
const dLeft = Math.min(req.dx, req.dx + req.dw) - offsetX;
|
|
1940
|
+
const dTop = Math.min(req.dy, req.dy + req.dh) - offsetY;
|
|
1941
|
+
if (mode !== HALFTONE) {
|
|
1942
|
+
const px = stretchGdi(decoded.pixels, req.sx, req.sy, req.sw, req.sh, req.dw, req.dh, mode);
|
|
1943
|
+
const tile = createTempCanvas(px.width, px.height);
|
|
1944
|
+
if (!tile) {
|
|
1945
|
+
return;
|
|
1946
|
+
}
|
|
1947
|
+
canvasPutImageData(tile.ctx, createImageDataCompat(px.data, px.width, px.height), 0, 0);
|
|
1948
|
+
target.save();
|
|
1949
|
+
target.globalCompositeOperation = "source-over";
|
|
1950
|
+
canvasDrawImage(target, tile.canvas, Math.round(dLeft), Math.round(dTop), px.width, px.height);
|
|
1951
|
+
target.restore();
|
|
1952
|
+
return;
|
|
1382
1953
|
}
|
|
1383
|
-
|
|
1954
|
+
const flipX = req.dw < 0 !== req.sw < 0;
|
|
1955
|
+
const flipY = req.dh < 0 !== req.sh < 0;
|
|
1956
|
+
const adw = Math.abs(req.dw);
|
|
1957
|
+
const adh = Math.abs(req.dh);
|
|
1958
|
+
target.save();
|
|
1959
|
+
target.imageSmoothingEnabled = true;
|
|
1960
|
+
target.transform(
|
|
1961
|
+
flipX ? -1 : 1,
|
|
1962
|
+
0,
|
|
1963
|
+
0,
|
|
1964
|
+
flipY ? -1 : 1,
|
|
1965
|
+
flipX ? dLeft * 2 + adw : 0,
|
|
1966
|
+
flipY ? dTop * 2 + adh : 0
|
|
1967
|
+
);
|
|
1968
|
+
const draw = target.drawImage;
|
|
1969
|
+
draw.call(
|
|
1970
|
+
target,
|
|
1971
|
+
decoded.canvas,
|
|
1972
|
+
Math.min(req.sx, req.sx + req.sw),
|
|
1973
|
+
Math.min(req.sy, req.sy + req.sh),
|
|
1974
|
+
Math.abs(req.sw),
|
|
1975
|
+
Math.abs(req.sh),
|
|
1976
|
+
dLeft,
|
|
1977
|
+
dTop,
|
|
1978
|
+
adw,
|
|
1979
|
+
adh
|
|
1980
|
+
);
|
|
1981
|
+
target.restore();
|
|
1982
|
+
}
|
|
1983
|
+
function decodeSource(rCtx, req) {
|
|
1984
|
+
if (!req.source) {
|
|
1384
1985
|
return null;
|
|
1385
1986
|
}
|
|
1386
|
-
const
|
|
1387
|
-
|
|
1388
|
-
const BI_RLE4 = 2;
|
|
1389
|
-
const BI_BITFIELDS2 = 3;
|
|
1390
|
-
if (bitCount !== 1 && bitCount !== 4 && bitCount !== 8 && bitCount !== 16 && bitCount !== 24 && bitCount !== 32) {
|
|
1987
|
+
const image = decodeDibToImageData(rCtx.view, req.source.bmi, req.source.bits, req.source.cbBits);
|
|
1988
|
+
if (!image) {
|
|
1391
1989
|
return null;
|
|
1392
1990
|
}
|
|
1393
|
-
|
|
1991
|
+
const temp = createTempCanvas(image.width, image.height);
|
|
1992
|
+
if (!temp) {
|
|
1394
1993
|
return null;
|
|
1395
1994
|
}
|
|
1396
|
-
|
|
1995
|
+
canvasPutImageData(temp.ctx, image, 0, 0);
|
|
1996
|
+
let { sy } = req;
|
|
1997
|
+
if (req.dibOrigin === "bottom-left" && rCtx.view.getInt32(req.source.bmi + 8, true) > 0) {
|
|
1998
|
+
sy = image.height - sy - req.sh;
|
|
1999
|
+
}
|
|
2000
|
+
return {
|
|
2001
|
+
decoded: { pixels: image, canvas: temp.canvas },
|
|
2002
|
+
req: { ...req, sy }
|
|
2003
|
+
};
|
|
2004
|
+
}
|
|
2005
|
+
function fillRectWith(ctx, style, gco, req) {
|
|
2006
|
+
const prevGco = ctx.globalCompositeOperation;
|
|
2007
|
+
const prevFill = ctx.fillStyle;
|
|
2008
|
+
ctx.globalCompositeOperation = gco;
|
|
2009
|
+
ctx.fillStyle = style;
|
|
2010
|
+
ctx.fillRect(req.dx, req.dy, req.dw, req.dh);
|
|
2011
|
+
ctx.globalCompositeOperation = prevGco;
|
|
2012
|
+
ctx.fillStyle = prevFill;
|
|
2013
|
+
}
|
|
2014
|
+
function runTernary(rCtx, req, index, usesP, usesS) {
|
|
2015
|
+
const { ctx } = rCtx;
|
|
2016
|
+
const rect = clampPositiveRect(req.dx, req.dy, req.dw, req.dh, rCtx.canvasW, rCtx.canvasH);
|
|
2017
|
+
if (!rect) {
|
|
2018
|
+
return;
|
|
2019
|
+
}
|
|
2020
|
+
if (typeof ctx.getImageData !== "function") {
|
|
2021
|
+
return;
|
|
2022
|
+
}
|
|
2023
|
+
const brush = realizeBrush(rCtx.state);
|
|
2024
|
+
if (usesP && brush.kind === "none") {
|
|
2025
|
+
return;
|
|
2026
|
+
}
|
|
2027
|
+
const layer = createTempCanvas(rect.w, rect.h);
|
|
2028
|
+
if (!layer) {
|
|
2029
|
+
return;
|
|
2030
|
+
}
|
|
2031
|
+
let src = null;
|
|
2032
|
+
if (usesS) {
|
|
2033
|
+
const decoded = decodeSource(rCtx, req);
|
|
2034
|
+
if (!decoded) {
|
|
2035
|
+
return;
|
|
2036
|
+
}
|
|
2037
|
+
drawSourceMapped(layer.ctx, decoded.decoded, decoded.req, rect.x, rect.y, rCtx.state.stretchBltMode);
|
|
2038
|
+
src = canvasGetImageData(layer.ctx, 0, 0, rect.w, rect.h);
|
|
2039
|
+
}
|
|
2040
|
+
const dst = canvasGetImageData(ctx, rect.x, rect.y, rect.w, rect.h);
|
|
2041
|
+
applyRop3(dst, src, patternOperand(rCtx, brush), index, rect.x, rect.y);
|
|
2042
|
+
canvasPutImageData(layer.ctx, dst, 0, 0);
|
|
2043
|
+
ctx.save();
|
|
2044
|
+
ctx.setTransform(1, 0, 0, 1, 0, 0);
|
|
2045
|
+
ctx.globalCompositeOperation = "source-over";
|
|
2046
|
+
ctx.globalAlpha = 1;
|
|
2047
|
+
canvasDrawImage(ctx, layer.canvas, rect.x, rect.y, rect.w, rect.h);
|
|
2048
|
+
ctx.restore();
|
|
2049
|
+
}
|
|
2050
|
+
function alignMirroredDest(rCtx, req) {
|
|
2051
|
+
const pxX = rCtx.useMappingMode ? 1 : rCtx.sx;
|
|
2052
|
+
const pxY = rCtx.useMappingMode ? 1 : rCtx.sy;
|
|
2053
|
+
return {
|
|
2054
|
+
...req,
|
|
2055
|
+
dx: req.dw < 0 ? req.dx + pxX : req.dx,
|
|
2056
|
+
dy: req.dh < 0 ? req.dy + pxY : req.dy
|
|
2057
|
+
};
|
|
2058
|
+
}
|
|
2059
|
+
function executeBlit(rCtx, request) {
|
|
2060
|
+
const { ctx } = rCtx;
|
|
2061
|
+
const req = alignMirroredDest(rCtx, request);
|
|
2062
|
+
const plan = req.plan;
|
|
2063
|
+
switch (plan.kind) {
|
|
2064
|
+
case "noop":
|
|
2065
|
+
return;
|
|
2066
|
+
case "solid":
|
|
2067
|
+
fillRectWith(ctx, plan.color === "black" ? "#000000" : "#ffffff", "source-over", req);
|
|
2068
|
+
return;
|
|
2069
|
+
case "invert-dest":
|
|
2070
|
+
fillRectWith(ctx, "#ffffff", "difference", req);
|
|
2071
|
+
return;
|
|
2072
|
+
case "copy": {
|
|
2073
|
+
const decoded = decodeSource(rCtx, req);
|
|
2074
|
+
if (decoded) {
|
|
2075
|
+
drawSourceMapped(ctx, decoded.decoded, decoded.req, 0, 0, rCtx.state.stretchBltMode);
|
|
2076
|
+
}
|
|
2077
|
+
return;
|
|
2078
|
+
}
|
|
2079
|
+
case "ternary": {
|
|
2080
|
+
const brush = realizeBrush(rCtx.state);
|
|
2081
|
+
if (plan.index === 240 && brush.kind !== "tile") {
|
|
2082
|
+
if (brush.kind === "solid") {
|
|
2083
|
+
fillRectWith(ctx, rCtx.state.brushColor, "source-over", req);
|
|
2084
|
+
}
|
|
2085
|
+
return;
|
|
2086
|
+
}
|
|
2087
|
+
runTernary(rCtx, req, plan.index, plan.operands.usesP, plan.operands.usesS);
|
|
2088
|
+
}
|
|
2089
|
+
}
|
|
2090
|
+
}
|
|
2091
|
+
function sourceOf(offset, offBmi, cbBmi, offBits, cbBits) {
|
|
2092
|
+
return offBmi > 0 && cbBmi > 0 && offBits > 0 && cbBits > 0 ? { bmi: offset + offBmi, bits: offset + offBits, cbBits } : null;
|
|
2093
|
+
}
|
|
2094
|
+
function handleBlt(rCtx, offset, dataOff, recSize, stretch) {
|
|
2095
|
+
const { view } = rCtx;
|
|
2096
|
+
if (recSize < (stretch ? 108 : 96)) {
|
|
2097
|
+
return true;
|
|
2098
|
+
}
|
|
2099
|
+
const xDest = view.getInt32(dataOff + 16, true);
|
|
2100
|
+
const yDest = view.getInt32(dataOff + 20, true);
|
|
2101
|
+
const cxDest = view.getInt32(dataOff + 24, true);
|
|
2102
|
+
const cyDest = view.getInt32(dataOff + 28, true);
|
|
2103
|
+
const rop = view.getUint32(dataOff + 32, true);
|
|
2104
|
+
const xSrc = view.getInt32(dataOff + 36, true);
|
|
2105
|
+
const ySrc = view.getInt32(dataOff + 40, true);
|
|
2106
|
+
const m11 = view.getFloat32(dataOff + 44, true);
|
|
2107
|
+
const m12 = view.getFloat32(dataOff + 48, true);
|
|
2108
|
+
const m21 = view.getFloat32(dataOff + 52, true);
|
|
2109
|
+
const m22 = view.getFloat32(dataOff + 56, true);
|
|
2110
|
+
const mdx = view.getFloat32(dataOff + 60, true);
|
|
2111
|
+
const mdy = view.getFloat32(dataOff + 64, true);
|
|
2112
|
+
const cxSrc = stretch ? view.getInt32(dataOff + 92, true) : cxDest;
|
|
2113
|
+
const cySrc = stretch ? view.getInt32(dataOff + 96, true) : cyDest;
|
|
2114
|
+
const source = recSize >= 100 ? sourceOf(
|
|
2115
|
+
offset,
|
|
2116
|
+
view.getUint32(dataOff + 76, true),
|
|
2117
|
+
view.getUint32(dataOff + 80, true),
|
|
2118
|
+
view.getUint32(dataOff + 84, true),
|
|
2119
|
+
view.getUint32(dataOff + 88, true)
|
|
2120
|
+
) : null;
|
|
2121
|
+
const identity = m11 === 0 && m22 === 0 && m12 === 0 && m21 === 0;
|
|
2122
|
+
const a = identity ? 1 : m11;
|
|
2123
|
+
const d = identity ? 1 : m22;
|
|
2124
|
+
executeBlit(rCtx, {
|
|
2125
|
+
plan: classifyRop3(rop),
|
|
2126
|
+
dx: gmx(rCtx, xDest),
|
|
2127
|
+
dy: gmy(rCtx, yDest),
|
|
2128
|
+
dw: gmw(rCtx, cxDest),
|
|
2129
|
+
dh: gmh(rCtx, cyDest),
|
|
2130
|
+
source,
|
|
2131
|
+
sx: a * xSrc + (identity ? 0 : m21 * ySrc + mdx),
|
|
2132
|
+
sy: d * ySrc + (identity ? 0 : m12 * xSrc + mdy),
|
|
2133
|
+
sw: a * cxSrc,
|
|
2134
|
+
sh: d * cySrc,
|
|
2135
|
+
dibOrigin: "top-left"
|
|
2136
|
+
});
|
|
2137
|
+
return true;
|
|
2138
|
+
}
|
|
2139
|
+
function handleStretchDibits(rCtx, offset, dataOff, recSize) {
|
|
2140
|
+
const { view } = rCtx;
|
|
2141
|
+
if (recSize < 80) {
|
|
2142
|
+
return true;
|
|
2143
|
+
}
|
|
2144
|
+
executeBlit(rCtx, {
|
|
2145
|
+
plan: classifyRop3(view.getUint32(dataOff + 60, true)),
|
|
2146
|
+
dx: gmx(rCtx, view.getInt32(dataOff + 16, true)),
|
|
2147
|
+
dy: gmy(rCtx, view.getInt32(dataOff + 20, true)),
|
|
2148
|
+
dw: gmw(rCtx, view.getInt32(dataOff + 64, true)),
|
|
2149
|
+
dh: gmh(rCtx, view.getInt32(dataOff + 68, true)),
|
|
2150
|
+
source: sourceOf(
|
|
2151
|
+
offset,
|
|
2152
|
+
view.getUint32(dataOff + 40, true),
|
|
2153
|
+
view.getUint32(dataOff + 44, true),
|
|
2154
|
+
view.getUint32(dataOff + 48, true),
|
|
2155
|
+
view.getUint32(dataOff + 52, true)
|
|
2156
|
+
),
|
|
2157
|
+
sx: view.getInt32(dataOff + 24, true),
|
|
2158
|
+
sy: view.getInt32(dataOff + 28, true),
|
|
2159
|
+
sw: view.getInt32(dataOff + 32, true),
|
|
2160
|
+
sh: view.getInt32(dataOff + 36, true),
|
|
2161
|
+
dibOrigin: "bottom-left"
|
|
2162
|
+
});
|
|
2163
|
+
return true;
|
|
2164
|
+
}
|
|
2165
|
+
function handleEmfGdiBitmapRecord(rCtx, recType, offset, dataOff, recSize) {
|
|
2166
|
+
switch (recType) {
|
|
2167
|
+
case EMR_BITBLT:
|
|
2168
|
+
return handleBlt(rCtx, offset, dataOff, recSize, false);
|
|
2169
|
+
case EMR_STRETCHBLT:
|
|
2170
|
+
return handleBlt(rCtx, offset, dataOff, recSize, true);
|
|
2171
|
+
case EMR_STRETCHDIBITS:
|
|
2172
|
+
return handleStretchDibits(rCtx, offset, dataOff, recSize);
|
|
2173
|
+
default:
|
|
2174
|
+
return false;
|
|
2175
|
+
}
|
|
2176
|
+
}
|
|
2177
|
+
|
|
2178
|
+
// src/emf-gdi-draw-text.ts
|
|
2179
|
+
function readDxArray(view, offset, dataOff, nChars, viewEnd) {
|
|
2180
|
+
const offDx = view.getUint32(dataOff + 64, true);
|
|
2181
|
+
if (offDx === 0) {
|
|
1397
2182
|
return null;
|
|
1398
2183
|
}
|
|
1399
|
-
|
|
2184
|
+
const start = offset + offDx;
|
|
2185
|
+
const end = start + nChars * 4;
|
|
2186
|
+
if (start < 0 || end > viewEnd) {
|
|
1400
2187
|
return null;
|
|
1401
2188
|
}
|
|
1402
|
-
|
|
2189
|
+
const dx = [];
|
|
2190
|
+
for (let i = 0; i < nChars; i++) {
|
|
2191
|
+
dx.push(view.getUint32(start + i * 4, true));
|
|
2192
|
+
}
|
|
2193
|
+
return dx;
|
|
2194
|
+
}
|
|
2195
|
+
function horizontalAlign(textAlign) {
|
|
2196
|
+
if (textAlign & 2) {
|
|
2197
|
+
return "right";
|
|
2198
|
+
}
|
|
2199
|
+
if (textAlign & 6) {
|
|
2200
|
+
return "center";
|
|
2201
|
+
}
|
|
2202
|
+
return "left";
|
|
2203
|
+
}
|
|
2204
|
+
function verticalBaseline(textAlign) {
|
|
2205
|
+
const vAlign = textAlign & 24;
|
|
2206
|
+
return vAlign === 24 ? "alphabetic" : vAlign === 8 ? "bottom" : "top";
|
|
2207
|
+
}
|
|
2208
|
+
function paintRun(ctx, state, text, dxDevice, startX, y, align, fontScale) {
|
|
2209
|
+
const totalWidth = dxDevice ? totalGlyphAdvance(dxDevice) : ctx.measureText(text).width;
|
|
2210
|
+
const runStartX = dxDevice ? startX + alignmentStartOffset(totalWidth, align) : startX;
|
|
2211
|
+
if (state.bkMode === 2) {
|
|
2212
|
+
const bgH = fontSizePx(state, fontScale);
|
|
2213
|
+
const prevFill = ctx.fillStyle;
|
|
2214
|
+
ctx.fillStyle = state.bkColor;
|
|
2215
|
+
ctx.fillRect(runStartX, y - bgH, totalWidth, bgH);
|
|
2216
|
+
ctx.fillStyle = prevFill;
|
|
2217
|
+
}
|
|
2218
|
+
ctx.fillStyle = state.textColor;
|
|
2219
|
+
if (dxDevice) {
|
|
2220
|
+
const offsets = cumulativeGlyphOffsets(dxDevice);
|
|
2221
|
+
const prevAlign = ctx.textAlign;
|
|
2222
|
+
ctx.textAlign = "left";
|
|
2223
|
+
for (let i = 0; i < text.length && i < offsets.length; i++) {
|
|
2224
|
+
ctx.fillText(text[i], runStartX + offsets[i], y);
|
|
2225
|
+
}
|
|
2226
|
+
ctx.textAlign = prevAlign;
|
|
2227
|
+
} else {
|
|
2228
|
+
ctx.fillText(text, startX, y);
|
|
2229
|
+
}
|
|
2230
|
+
if (state.fontUnderline || state.fontStrikeOut) {
|
|
2231
|
+
drawTextDecorations(ctx, state, runStartX, y, totalWidth, fontScale);
|
|
2232
|
+
}
|
|
2233
|
+
}
|
|
2234
|
+
function handleExtTextOutW(rCtx, offset, dataOff, recSize) {
|
|
2235
|
+
const { ctx, view, state } = rCtx;
|
|
2236
|
+
if (recSize < 76) {
|
|
2237
|
+
return true;
|
|
2238
|
+
}
|
|
2239
|
+
const refX = view.getInt32(dataOff + 28, true);
|
|
2240
|
+
const refY = view.getInt32(dataOff + 32, true);
|
|
2241
|
+
const nChars = view.getUint32(dataOff + 36, true);
|
|
2242
|
+
const offString = view.getUint32(dataOff + 40, true);
|
|
2243
|
+
const viewEnd = view.byteLength;
|
|
2244
|
+
if (nChars === 0 || offString === 0 || offset + offString + nChars * 2 > viewEnd) {
|
|
2245
|
+
return true;
|
|
2246
|
+
}
|
|
2247
|
+
const text = readUtf16LE(view, offset + offString, nChars);
|
|
2248
|
+
if (text.length === 0) {
|
|
2249
|
+
return true;
|
|
2250
|
+
}
|
|
2251
|
+
const fontScale = Math.abs(gmh(rCtx, 1));
|
|
2252
|
+
applyFont(ctx, state, fontScale);
|
|
2253
|
+
const align = horizontalAlign(state.textAlign);
|
|
2254
|
+
ctx.textBaseline = verticalBaseline(state.textAlign);
|
|
2255
|
+
ctx.textAlign = align === "center" ? "center" : align === "right" ? "right" : "left";
|
|
2256
|
+
const dxLogical = readDxArray(view, offset, dataOff, nChars, viewEnd);
|
|
2257
|
+
const dxDevice = dxLogical ? dxLogical.map((v) => gmw(rCtx, v)) : null;
|
|
2258
|
+
if (dxDevice) {
|
|
2259
|
+
ctx.textAlign = "left";
|
|
2260
|
+
}
|
|
2261
|
+
const baseX = gmx(rCtx, refX);
|
|
2262
|
+
const baseY = gmy(rCtx, refY);
|
|
2263
|
+
const radians = escapementToCanvasRadians(state.fontEscapementTenthDeg);
|
|
2264
|
+
if (radians !== 0) {
|
|
2265
|
+
ctx.save();
|
|
2266
|
+
ctx.translate(baseX, baseY);
|
|
2267
|
+
ctx.rotate(radians);
|
|
2268
|
+
paintRun(ctx, state, text, dxDevice, 0, 0, align, fontScale);
|
|
2269
|
+
ctx.restore();
|
|
2270
|
+
} else {
|
|
2271
|
+
paintRun(ctx, state, text, dxDevice, baseX, baseY, align, fontScale);
|
|
2272
|
+
}
|
|
2273
|
+
return true;
|
|
2274
|
+
}
|
|
2275
|
+
function handleEmfGdiDrawTextRecord(rCtx, recType, offset, dataOff, recSize) {
|
|
2276
|
+
if (recType === EMR_EXTTEXTOUTW) {
|
|
2277
|
+
return handleExtTextOutW(rCtx, offset, dataOff, recSize);
|
|
2278
|
+
}
|
|
2279
|
+
return false;
|
|
2280
|
+
}
|
|
2281
|
+
|
|
2282
|
+
// src/emf-clip-region.ts
|
|
2283
|
+
var CLIP_HUGE = 1 << 24;
|
|
2284
|
+
function rectClipShape(x, y, w, h) {
|
|
2285
|
+
return { cmds: [{ op: "rect", x, y, w, h }], fillRule: "nonzero", simple: true };
|
|
2286
|
+
}
|
|
2287
|
+
function rectsClipShape(rects) {
|
|
2288
|
+
return {
|
|
2289
|
+
cmds: rects.map((r) => ({ op: "rect", x: r.x, y: r.y, w: r.w, h: r.h })),
|
|
2290
|
+
fillRule: "nonzero",
|
|
2291
|
+
simple: true
|
|
2292
|
+
};
|
|
2293
|
+
}
|
|
2294
|
+
function emptyClipShape() {
|
|
2295
|
+
return { cmds: [{ op: "rect", x: 0, y: 0, w: 0, h: 0 }], fillRule: "nonzero", simple: true };
|
|
2296
|
+
}
|
|
2297
|
+
function translateClipShape(shape, dx, dy) {
|
|
2298
|
+
return {
|
|
2299
|
+
...shape,
|
|
2300
|
+
cmds: shape.cmds.map((c) => {
|
|
2301
|
+
switch (c.op) {
|
|
2302
|
+
case "rect":
|
|
2303
|
+
return { ...c, x: c.x + dx, y: c.y + dy };
|
|
2304
|
+
case "moveTo":
|
|
2305
|
+
case "lineTo":
|
|
2306
|
+
return { ...c, x: c.x + dx, y: c.y + dy };
|
|
2307
|
+
case "bezierCurveTo":
|
|
2308
|
+
return {
|
|
2309
|
+
...c,
|
|
2310
|
+
cp1x: c.cp1x + dx,
|
|
2311
|
+
cp1y: c.cp1y + dy,
|
|
2312
|
+
cp2x: c.cp2x + dx,
|
|
2313
|
+
cp2y: c.cp2y + dy,
|
|
2314
|
+
x: c.x + dx,
|
|
2315
|
+
y: c.y + dy
|
|
2316
|
+
};
|
|
2317
|
+
case "closePath":
|
|
2318
|
+
return c;
|
|
2319
|
+
}
|
|
2320
|
+
})
|
|
2321
|
+
};
|
|
2322
|
+
}
|
|
2323
|
+
function translateClipRegion(region, dx, dy) {
|
|
2324
|
+
if (!region) {
|
|
1403
2325
|
return null;
|
|
1404
2326
|
}
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
2327
|
+
return region.map((s) => translateClipShape(s, dx, dy));
|
|
2328
|
+
}
|
|
2329
|
+
function isComposable(shape) {
|
|
2330
|
+
return shape.simple && shape.fillRule === "nonzero";
|
|
2331
|
+
}
|
|
2332
|
+
function invertClipShape(shape) {
|
|
2333
|
+
return {
|
|
2334
|
+
cmds: [
|
|
2335
|
+
{ op: "rect", x: -CLIP_HUGE, y: -CLIP_HUGE, w: 2 * CLIP_HUGE, h: 2 * CLIP_HUGE },
|
|
2336
|
+
...shape.cmds
|
|
2337
|
+
],
|
|
2338
|
+
fillRule: "evenodd",
|
|
2339
|
+
simple: false
|
|
2340
|
+
};
|
|
2341
|
+
}
|
|
2342
|
+
function combineClip(current, shape, op) {
|
|
2343
|
+
switch (op) {
|
|
2344
|
+
case "replace":
|
|
2345
|
+
return { region: [shape], exact: true };
|
|
2346
|
+
case "intersect":
|
|
2347
|
+
return { region: current ? [...current, shape] : [shape], exact: true };
|
|
2348
|
+
case "exclude": {
|
|
2349
|
+
if (!isComposable(shape)) {
|
|
2350
|
+
return { region: current ? [...current, shape] : [shape], exact: false };
|
|
2351
|
+
}
|
|
2352
|
+
const inv = invertClipShape(shape);
|
|
2353
|
+
return { region: current ? [...current, inv] : [inv], exact: true };
|
|
1415
2354
|
}
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
2355
|
+
case "union": {
|
|
2356
|
+
if (!current) {
|
|
2357
|
+
return { region: null, exact: true };
|
|
2358
|
+
}
|
|
2359
|
+
if (current.length === 1 && isComposable(current[0]) && isComposable(shape)) {
|
|
2360
|
+
return {
|
|
2361
|
+
region: [
|
|
2362
|
+
{ cmds: [...current[0].cmds, ...shape.cmds], fillRule: "nonzero", simple: false }
|
|
2363
|
+
],
|
|
2364
|
+
exact: false
|
|
2365
|
+
};
|
|
2366
|
+
}
|
|
2367
|
+
return { region: current, exact: false };
|
|
2368
|
+
}
|
|
2369
|
+
case "xor": {
|
|
2370
|
+
if (!current) {
|
|
2371
|
+
if (isComposable(shape)) {
|
|
2372
|
+
return { region: [invertClipShape(shape)], exact: true };
|
|
2373
|
+
}
|
|
2374
|
+
return { region: [shape], exact: false };
|
|
2375
|
+
}
|
|
2376
|
+
if (current.length === 1 && isComposable(current[0]) && isComposable(shape)) {
|
|
2377
|
+
return {
|
|
2378
|
+
region: [
|
|
2379
|
+
{ cmds: [...current[0].cmds, ...shape.cmds], fillRule: "evenodd", simple: false }
|
|
2380
|
+
],
|
|
2381
|
+
exact: true
|
|
2382
|
+
};
|
|
2383
|
+
}
|
|
2384
|
+
if (isComposable(shape)) {
|
|
2385
|
+
return { region: [...current, invertClipShape(shape)], exact: false };
|
|
2386
|
+
}
|
|
2387
|
+
return { region: [...current, shape], exact: false };
|
|
2388
|
+
}
|
|
2389
|
+
case "complement": {
|
|
2390
|
+
if (!current) {
|
|
2391
|
+
return { region: [emptyClipShape()], exact: true };
|
|
2392
|
+
}
|
|
2393
|
+
if (current.length === 1 && isComposable(current[0])) {
|
|
2394
|
+
return { region: [shape, invertClipShape(current[0])], exact: true };
|
|
2395
|
+
}
|
|
2396
|
+
return { region: [shape], exact: false };
|
|
1421
2397
|
}
|
|
1422
2398
|
}
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
2399
|
+
}
|
|
2400
|
+
function combineClipRegions(current, incoming, op) {
|
|
2401
|
+
if (op === "replace") {
|
|
2402
|
+
return { region: incoming, exact: true };
|
|
1426
2403
|
}
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
const setPixel = (x, y, r, g, b, a) => {
|
|
1430
|
-
const dstPx = (y * width + x) * 4;
|
|
1431
|
-
out[dstPx] = r;
|
|
1432
|
-
out[dstPx + 1] = g;
|
|
1433
|
-
out[dstPx + 2] = b;
|
|
1434
|
-
out[dstPx + 3] = a;
|
|
1435
|
-
};
|
|
1436
|
-
return decodeRleBitmap(
|
|
1437
|
-
view,
|
|
1438
|
-
bitsOffset,
|
|
1439
|
-
bitsSize,
|
|
1440
|
-
width,
|
|
1441
|
-
height,
|
|
1442
|
-
topDown,
|
|
1443
|
-
compression === BI_RLE4,
|
|
1444
|
-
colorTable,
|
|
1445
|
-
out,
|
|
1446
|
-
setPixel
|
|
1447
|
-
);
|
|
2404
|
+
if (incoming && incoming.length === 1) {
|
|
2405
|
+
return combineClip(current, incoming[0], op);
|
|
1448
2406
|
}
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
}
|
|
1462
|
-
|
|
1463
|
-
// src/emf-gdi-draw-text-bitmap.ts
|
|
1464
|
-
function handleExtTextOutW(rCtx, offset, dataOff, recSize) {
|
|
1465
|
-
const { ctx, view, state } = rCtx;
|
|
1466
|
-
if (recSize >= 76) {
|
|
1467
|
-
const refX = view.getInt32(dataOff + 28, true);
|
|
1468
|
-
const refY = view.getInt32(dataOff + 32, true);
|
|
1469
|
-
const nChars = view.getUint32(dataOff + 36, true);
|
|
1470
|
-
const offString = view.getUint32(dataOff + 40, true);
|
|
1471
|
-
const maxOffset = view.byteLength;
|
|
1472
|
-
if (nChars > 0 && offString > 0 && offset + offString + nChars * 2 <= maxOffset) {
|
|
1473
|
-
const text = readUtf16LE(view, offset + offString, nChars);
|
|
1474
|
-
if (text.length > 0) {
|
|
1475
|
-
const fontScale = Math.abs(gmh(rCtx, 1));
|
|
1476
|
-
applyFont(ctx, state, fontScale);
|
|
1477
|
-
ctx.fillStyle = state.textColor;
|
|
1478
|
-
const vAlign = state.textAlign & 24;
|
|
1479
|
-
const alignBaseline = vAlign === 24 ? "alphabetic" : vAlign === 8 ? "bottom" : "top";
|
|
1480
|
-
let alignHoriz = "left";
|
|
1481
|
-
if (state.textAlign & 6) {
|
|
1482
|
-
alignHoriz = "center";
|
|
1483
|
-
}
|
|
1484
|
-
if (state.textAlign & 2) {
|
|
1485
|
-
alignHoriz = "right";
|
|
1486
|
-
}
|
|
1487
|
-
ctx.textBaseline = alignBaseline;
|
|
1488
|
-
ctx.textAlign = alignHoriz;
|
|
1489
|
-
if (state.bkMode === 2) {
|
|
1490
|
-
const measured = ctx.measureText(text);
|
|
1491
|
-
const bgH = fontSizePx(state, fontScale);
|
|
1492
|
-
ctx.fillStyle = state.bkColor;
|
|
1493
|
-
ctx.fillRect(gmx(rCtx, refX), gmy(rCtx, refY) - bgH, measured.width, bgH);
|
|
1494
|
-
ctx.fillStyle = state.textColor;
|
|
2407
|
+
if (!incoming) {
|
|
2408
|
+
switch (op) {
|
|
2409
|
+
case "intersect":
|
|
2410
|
+
return { region: current, exact: true };
|
|
2411
|
+
case "union":
|
|
2412
|
+
return { region: null, exact: true };
|
|
2413
|
+
case "exclude":
|
|
2414
|
+
return { region: [emptyClipShape()], exact: true };
|
|
2415
|
+
case "xor":
|
|
2416
|
+
case "complement": {
|
|
2417
|
+
if (!current) {
|
|
2418
|
+
return { region: [emptyClipShape()], exact: true };
|
|
1495
2419
|
}
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
const w = ctx.measureText(text).width;
|
|
1499
|
-
const baseX = gmx(rCtx, refX);
|
|
1500
|
-
const startX = alignHoriz === "center" ? baseX - w / 2 : alignHoriz === "right" ? baseX - w : baseX;
|
|
1501
|
-
drawTextDecorations(ctx, state, startX, gmy(rCtx, refY), w, fontScale);
|
|
2420
|
+
if (current.length === 1) {
|
|
2421
|
+
return combineClip(null, current[0], "exclude");
|
|
1502
2422
|
}
|
|
2423
|
+
return { region: current, exact: false };
|
|
1503
2424
|
}
|
|
1504
2425
|
}
|
|
1505
2426
|
}
|
|
1506
|
-
|
|
2427
|
+
switch (op) {
|
|
2428
|
+
case "intersect":
|
|
2429
|
+
return { region: current ? [...current, ...incoming] : incoming, exact: true };
|
|
2430
|
+
case "union":
|
|
2431
|
+
return { region: current, exact: false };
|
|
2432
|
+
case "xor":
|
|
2433
|
+
return { region: current ?? incoming, exact: false };
|
|
2434
|
+
case "exclude":
|
|
2435
|
+
return { region: current, exact: false };
|
|
2436
|
+
case "complement": {
|
|
2437
|
+
if (!current) {
|
|
2438
|
+
return { region: [emptyClipShape()], exact: true };
|
|
2439
|
+
}
|
|
2440
|
+
if (current.length === 1) {
|
|
2441
|
+
return combineClip(incoming, current[0], "exclude");
|
|
2442
|
+
}
|
|
2443
|
+
return { region: incoming, exact: false };
|
|
2444
|
+
}
|
|
2445
|
+
}
|
|
1507
2446
|
}
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
ctx.fillRect(gmx(rCtx, dstX), gmy(rCtx, dstY), gmw(rCtx, dstW), gmh(rCtx, dstH));
|
|
1527
|
-
}
|
|
1528
|
-
ctx.fillStyle = prevFill;
|
|
1529
|
-
return true;
|
|
2447
|
+
function replayClipCmds(ctx, cmds) {
|
|
2448
|
+
for (const c of cmds) {
|
|
2449
|
+
switch (c.op) {
|
|
2450
|
+
case "rect":
|
|
2451
|
+
ctx.rect(c.x, c.y, c.w, c.h);
|
|
2452
|
+
break;
|
|
2453
|
+
case "moveTo":
|
|
2454
|
+
ctx.moveTo(c.x, c.y);
|
|
2455
|
+
break;
|
|
2456
|
+
case "lineTo":
|
|
2457
|
+
ctx.lineTo(c.x, c.y);
|
|
2458
|
+
break;
|
|
2459
|
+
case "bezierCurveTo":
|
|
2460
|
+
ctx.bezierCurveTo(c.cp1x, c.cp1y, c.cp2x, c.cp2y, c.x, c.y);
|
|
2461
|
+
break;
|
|
2462
|
+
case "closePath":
|
|
2463
|
+
ctx.closePath();
|
|
2464
|
+
break;
|
|
1530
2465
|
}
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
if (temp) {
|
|
1541
|
-
temp.ctx.putImageData(imageData, 0, 0);
|
|
1542
|
-
canvasDrawImage(
|
|
1543
|
-
ctx,
|
|
1544
|
-
temp.canvas,
|
|
1545
|
-
gmx(rCtx, dstX),
|
|
1546
|
-
gmy(rCtx, dstY),
|
|
1547
|
-
gmw(rCtx, dstW),
|
|
1548
|
-
gmh(rCtx, dstH)
|
|
1549
|
-
);
|
|
1550
|
-
}
|
|
1551
|
-
}
|
|
2466
|
+
}
|
|
2467
|
+
}
|
|
2468
|
+
function applyClipShapes(ctx, shapes) {
|
|
2469
|
+
for (const s of shapes) {
|
|
2470
|
+
ctx.beginPath();
|
|
2471
|
+
replayClipCmds(ctx, s.cmds);
|
|
2472
|
+
try {
|
|
2473
|
+
ctx.clip(s.fillRule);
|
|
2474
|
+
} catch {
|
|
1552
2475
|
}
|
|
1553
2476
|
}
|
|
1554
|
-
return true;
|
|
1555
2477
|
}
|
|
1556
|
-
function
|
|
1557
|
-
const { ctx
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
if (offBmiSrc > 0 && cbBmiSrc > 0 && offBitsSrc > 0 && cbBitsSrc > 0) {
|
|
1568
|
-
const imageData = decodeDibToImageData(
|
|
1569
|
-
view,
|
|
1570
|
-
offset + offBmiSrc,
|
|
1571
|
-
offset + offBitsSrc,
|
|
1572
|
-
cbBitsSrc
|
|
1573
|
-
);
|
|
1574
|
-
if (imageData) {
|
|
1575
|
-
const temp = createTempCanvas(imageData.width, imageData.height);
|
|
1576
|
-
if (temp) {
|
|
1577
|
-
temp.ctx.putImageData(imageData, 0, 0);
|
|
1578
|
-
canvasDrawImage(
|
|
1579
|
-
ctx,
|
|
1580
|
-
temp.canvas,
|
|
1581
|
-
gmx(rCtx, dstX),
|
|
1582
|
-
gmy(rCtx, dstY),
|
|
1583
|
-
gmw(rCtx, dstW),
|
|
1584
|
-
gmh(rCtx, dstH)
|
|
1585
|
-
);
|
|
1586
|
-
}
|
|
1587
|
-
}
|
|
2478
|
+
function reapplyClipRegion(holder, region, identityTransform = false) {
|
|
2479
|
+
const { ctx } = holder;
|
|
2480
|
+
while (holder.clipSaveDepth > 0) {
|
|
2481
|
+
ctx.restore();
|
|
2482
|
+
holder.clipSaveDepth--;
|
|
2483
|
+
}
|
|
2484
|
+
if (region) {
|
|
2485
|
+
ctx.save();
|
|
2486
|
+
holder.clipSaveDepth = 1;
|
|
2487
|
+
if (identityTransform) {
|
|
2488
|
+
ctx.setTransform(1, 0, 0, 1, 0, 0);
|
|
1588
2489
|
}
|
|
2490
|
+
applyClipShapes(ctx, region);
|
|
1589
2491
|
}
|
|
1590
|
-
return true;
|
|
1591
2492
|
}
|
|
2493
|
+
|
|
2494
|
+
// src/emf-gdi-clip-records.ts
|
|
1592
2495
|
function gdiCombineClip(rCtx, shape, op) {
|
|
1593
2496
|
const { ctx } = rCtx;
|
|
1594
2497
|
if (rCtx.clipUntracked && op !== "replace") {
|
|
@@ -1720,14 +2623,8 @@ function handleOffsetClipRgn(rCtx, dataOff, recSize) {
|
|
|
1720
2623
|
}
|
|
1721
2624
|
return true;
|
|
1722
2625
|
}
|
|
1723
|
-
function
|
|
2626
|
+
function handleEmfGdiClipRecord(rCtx, recType, dataOff, recSize) {
|
|
1724
2627
|
switch (recType) {
|
|
1725
|
-
case EMR_EXTTEXTOUTW:
|
|
1726
|
-
return handleExtTextOutW(rCtx, offset, dataOff, recSize);
|
|
1727
|
-
case EMR_BITBLT:
|
|
1728
|
-
return handleBitBlt(rCtx, offset, dataOff, recSize);
|
|
1729
|
-
case EMR_STRETCHDIBITS:
|
|
1730
|
-
return handleStretchDibits(rCtx, offset, dataOff, recSize);
|
|
1731
2628
|
case EMR_INTERSECTCLIPRECT:
|
|
1732
2629
|
return handleIntersectClipRect(rCtx, dataOff, recSize);
|
|
1733
2630
|
case EMR_EXTSELECTCLIPRGN:
|
|
@@ -1741,12 +2638,36 @@ function handleEmfGdiTextBitmapRecord(rCtx, recType, offset, dataOff, recSize) {
|
|
|
1741
2638
|
}
|
|
1742
2639
|
}
|
|
1743
2640
|
|
|
2641
|
+
// src/emf-gdi-draw-text-bitmap.ts
|
|
2642
|
+
function handleEmfGdiTextBitmapRecord(rCtx, recType, offset, dataOff, recSize) {
|
|
2643
|
+
return handleEmfGdiDrawTextRecord(rCtx, recType, offset, dataOff, recSize) || handleEmfGdiBitmapRecord(rCtx, recType, offset, dataOff, recSize) || handleEmfGdiClipRecord(rCtx, recType, dataOff, recSize);
|
|
2644
|
+
}
|
|
2645
|
+
|
|
1744
2646
|
// src/emf-gdi-draw-handlers.ts
|
|
1745
2647
|
function handleEmfGdiDrawRecord(rCtx, recType, offset, dataOff, recSize) {
|
|
1746
2648
|
return handleEmfGdiShapeRecord(rCtx, recType, dataOff, recSize) || handleEmfGdiTextBitmapRecord(rCtx, recType, offset, dataOff, recSize);
|
|
1747
2649
|
}
|
|
1748
2650
|
|
|
1749
2651
|
// src/emf-gdi-polypolygon-helpers.ts
|
|
2652
|
+
function buildPolyPolygonPath(target, rCtx, readPoint, countsOff, numPolys, totalPoints, close) {
|
|
2653
|
+
const { view } = rCtx;
|
|
2654
|
+
let pIdx = 0;
|
|
2655
|
+
for (let p = 0; p < numPolys; p++) {
|
|
2656
|
+
const count = view.getUint32(countsOff + p * 4, true);
|
|
2657
|
+
for (let i = 0; i < count && pIdx < totalPoints; i++) {
|
|
2658
|
+
const pt = readPoint(pIdx);
|
|
2659
|
+
if (i === 0) {
|
|
2660
|
+
target.moveTo(pt.x, pt.y);
|
|
2661
|
+
} else {
|
|
2662
|
+
target.lineTo(pt.x, pt.y);
|
|
2663
|
+
}
|
|
2664
|
+
pIdx++;
|
|
2665
|
+
}
|
|
2666
|
+
if (close) {
|
|
2667
|
+
target.closePath();
|
|
2668
|
+
}
|
|
2669
|
+
}
|
|
2670
|
+
}
|
|
1750
2671
|
function handlePolyPolygon32(rCtx, offset, dataOff, recSize) {
|
|
1751
2672
|
const { ctx, view, state, inPath } = rCtx;
|
|
1752
2673
|
const numPolys = view.getUint32(dataOff + 16, true);
|
|
@@ -1759,33 +2680,25 @@ function handlePolyPolygon32(rCtx, offset, dataOff, recSize) {
|
|
|
1759
2680
|
if (ptOff + totalPoints * 8 > offset + recSize) {
|
|
1760
2681
|
return;
|
|
1761
2682
|
}
|
|
2683
|
+
const readPoint = (pIdx) => gmapPoint(rCtx, view.getInt32(ptOff + pIdx * 8, true), view.getInt32(ptOff + pIdx * 8 + 4, true));
|
|
2684
|
+
const build = (target) => {
|
|
2685
|
+
buildPolyPolygonPath(target, rCtx, readPoint, countsOff, numPolys, totalPoints, true);
|
|
2686
|
+
};
|
|
1762
2687
|
if (!inPath) {
|
|
1763
2688
|
ctx.beginPath();
|
|
1764
2689
|
}
|
|
1765
|
-
|
|
1766
|
-
for (let p = 0; p < numPolys; p++) {
|
|
1767
|
-
const count = view.getUint32(countsOff + p * 4, true);
|
|
1768
|
-
for (let i = 0; i < count && pIdx < totalPoints; i++) {
|
|
1769
|
-
const px = view.getInt32(ptOff + pIdx * 8, true);
|
|
1770
|
-
const py = view.getInt32(ptOff + pIdx * 8 + 4, true);
|
|
1771
|
-
if (i === 0) {
|
|
1772
|
-
ctx.moveTo(gmx(rCtx, px), gmy(rCtx, py));
|
|
1773
|
-
} else {
|
|
1774
|
-
ctx.lineTo(gmx(rCtx, px), gmy(rCtx, py));
|
|
1775
|
-
}
|
|
1776
|
-
pIdx++;
|
|
1777
|
-
}
|
|
1778
|
-
ctx.closePath();
|
|
1779
|
-
}
|
|
2690
|
+
build(ctx);
|
|
1780
2691
|
if (!inPath) {
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
2692
|
+
const buildWithPath = (target) => {
|
|
2693
|
+
target.beginPath();
|
|
2694
|
+
build(target);
|
|
2695
|
+
};
|
|
2696
|
+
fillShapeExactOrFast(rCtx, buildWithPath, state.polyFillMode === 2 ? "nonzero" : "evenodd");
|
|
2697
|
+
strokeShapeExactOrFast(rCtx, buildWithPath);
|
|
1785
2698
|
}
|
|
1786
2699
|
}
|
|
1787
2700
|
function handlePolyPolyline32(rCtx, offset, dataOff, recSize) {
|
|
1788
|
-
const { ctx, view,
|
|
2701
|
+
const { ctx, view, inPath } = rCtx;
|
|
1789
2702
|
const numPolys = view.getUint32(dataOff + 16, true);
|
|
1790
2703
|
const totalPoints = view.getUint32(dataOff + 20, true);
|
|
1791
2704
|
if (numPolys === 0 || numPolys >= 1e4 || totalPoints >= 1e5) {
|
|
@@ -1796,26 +2709,20 @@ function handlePolyPolyline32(rCtx, offset, dataOff, recSize) {
|
|
|
1796
2709
|
if (ptOff + totalPoints * 8 > offset + recSize) {
|
|
1797
2710
|
return;
|
|
1798
2711
|
}
|
|
2712
|
+
const readPoint = (pIdx) => gmapPoint(rCtx, view.getInt32(ptOff + pIdx * 8, true), view.getInt32(ptOff + pIdx * 8 + 4, true));
|
|
2713
|
+
const build = (target) => {
|
|
2714
|
+
buildPolyPolygonPath(target, rCtx, readPoint, countsOff, numPolys, totalPoints, false);
|
|
2715
|
+
};
|
|
1799
2716
|
if (!inPath) {
|
|
1800
2717
|
ctx.beginPath();
|
|
1801
2718
|
}
|
|
1802
|
-
|
|
1803
|
-
for (let p = 0; p < numPolys; p++) {
|
|
1804
|
-
const count = view.getUint32(countsOff + p * 4, true);
|
|
1805
|
-
for (let i = 0; i < count && pIdx < totalPoints; i++) {
|
|
1806
|
-
const px = view.getInt32(ptOff + pIdx * 8, true);
|
|
1807
|
-
const py = view.getInt32(ptOff + pIdx * 8 + 4, true);
|
|
1808
|
-
if (i === 0) {
|
|
1809
|
-
ctx.moveTo(gmx(rCtx, px), gmy(rCtx, py));
|
|
1810
|
-
} else {
|
|
1811
|
-
ctx.lineTo(gmx(rCtx, px), gmy(rCtx, py));
|
|
1812
|
-
}
|
|
1813
|
-
pIdx++;
|
|
1814
|
-
}
|
|
1815
|
-
}
|
|
2719
|
+
build(ctx);
|
|
1816
2720
|
if (!inPath) {
|
|
1817
|
-
|
|
1818
|
-
|
|
2721
|
+
const buildWithPath = (target) => {
|
|
2722
|
+
target.beginPath();
|
|
2723
|
+
build(target);
|
|
2724
|
+
};
|
|
2725
|
+
strokeShapeExactOrFast(rCtx, buildWithPath);
|
|
1819
2726
|
}
|
|
1820
2727
|
}
|
|
1821
2728
|
function handlePolyPolygon16(rCtx, offset, dataOff, recSize) {
|
|
@@ -1830,29 +2737,21 @@ function handlePolyPolygon16(rCtx, offset, dataOff, recSize) {
|
|
|
1830
2737
|
if (ptOff + totalPoints * 4 > offset + recSize) {
|
|
1831
2738
|
return;
|
|
1832
2739
|
}
|
|
2740
|
+
const readPoint = (pIdx) => gmapPoint(rCtx, view.getInt16(ptOff + pIdx * 4, true), view.getInt16(ptOff + pIdx * 4 + 2, true));
|
|
2741
|
+
const build = (target) => {
|
|
2742
|
+
buildPolyPolygonPath(target, rCtx, readPoint, countsOff, numPolys, totalPoints, true);
|
|
2743
|
+
};
|
|
1833
2744
|
if (!inPath) {
|
|
1834
2745
|
ctx.beginPath();
|
|
1835
2746
|
}
|
|
1836
|
-
|
|
1837
|
-
for (let p = 0; p < numPolys; p++) {
|
|
1838
|
-
const count = view.getUint32(countsOff + p * 4, true);
|
|
1839
|
-
for (let i = 0; i < count && pIdx < totalPoints; i++) {
|
|
1840
|
-
const px = view.getInt16(ptOff + pIdx * 4, true);
|
|
1841
|
-
const py = view.getInt16(ptOff + pIdx * 4 + 2, true);
|
|
1842
|
-
if (i === 0) {
|
|
1843
|
-
ctx.moveTo(gmx(rCtx, px), gmy(rCtx, py));
|
|
1844
|
-
} else {
|
|
1845
|
-
ctx.lineTo(gmx(rCtx, px), gmy(rCtx, py));
|
|
1846
|
-
}
|
|
1847
|
-
pIdx++;
|
|
1848
|
-
}
|
|
1849
|
-
ctx.closePath();
|
|
1850
|
-
}
|
|
2747
|
+
build(ctx);
|
|
1851
2748
|
if (!inPath) {
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
2749
|
+
const buildWithPath = (target) => {
|
|
2750
|
+
target.beginPath();
|
|
2751
|
+
build(target);
|
|
2752
|
+
};
|
|
2753
|
+
fillShapeExactOrFast(rCtx, buildWithPath, state.polyFillMode === 2 ? "nonzero" : "evenodd");
|
|
2754
|
+
strokeShapeExactOrFast(rCtx, buildWithPath);
|
|
1856
2755
|
}
|
|
1857
2756
|
}
|
|
1858
2757
|
|
|
@@ -1870,43 +2769,44 @@ function handlePoly32(rCtx, recType, offset, dataOff, recSize) {
|
|
|
1870
2769
|
const isPolygon = recType === EMR_POLYGON;
|
|
1871
2770
|
const isBezier = recType === EMR_POLYBEZIER || recType === EMR_POLYBEZIERTO;
|
|
1872
2771
|
const isTo = recType === EMR_POLYBEZIERTO || recType === EMR_POLYLINETO;
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
)
|
|
1890
|
-
|
|
2772
|
+
const pt = (i) => gmapPoint(rCtx, view.getInt32(ptOff + i * 8, true), view.getInt32(ptOff + i * 8 + 4, true));
|
|
2773
|
+
const build = (target) => {
|
|
2774
|
+
if (!isTo) {
|
|
2775
|
+
const p0 = pt(0);
|
|
2776
|
+
target.moveTo(p0.x, p0.y);
|
|
2777
|
+
}
|
|
2778
|
+
let i = isTo ? 0 : 1;
|
|
2779
|
+
if (isBezier) {
|
|
2780
|
+
while (i + 2 < count) {
|
|
2781
|
+
const p1 = pt(i);
|
|
2782
|
+
const p2 = pt(i + 1);
|
|
2783
|
+
const p3 = pt(i + 2);
|
|
2784
|
+
target.bezierCurveTo(p1.x, p1.y, p2.x, p2.y, p3.x, p3.y);
|
|
2785
|
+
i += 3;
|
|
2786
|
+
}
|
|
2787
|
+
} else {
|
|
2788
|
+
for (; i < count; i++) {
|
|
2789
|
+
const p = pt(i);
|
|
2790
|
+
target.lineTo(p.x, p.y);
|
|
2791
|
+
}
|
|
1891
2792
|
}
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
ctx.lineTo(
|
|
1895
|
-
gmx(rCtx, view.getInt32(ptOff + i * 8, true)),
|
|
1896
|
-
gmy(rCtx, view.getInt32(ptOff + i * 8 + 4, true))
|
|
1897
|
-
);
|
|
2793
|
+
if (isPolygon) {
|
|
2794
|
+
target.closePath();
|
|
1898
2795
|
}
|
|
2796
|
+
};
|
|
2797
|
+
if (!inPath) {
|
|
2798
|
+
ctx.beginPath();
|
|
1899
2799
|
}
|
|
1900
|
-
|
|
1901
|
-
ctx.closePath();
|
|
1902
|
-
}
|
|
2800
|
+
build(ctx);
|
|
1903
2801
|
if (!inPath) {
|
|
2802
|
+
const buildWithPath = (target) => {
|
|
2803
|
+
target.beginPath();
|
|
2804
|
+
build(target);
|
|
2805
|
+
};
|
|
1904
2806
|
if (isPolygon) {
|
|
1905
|
-
|
|
1906
|
-
ctx.fill(state.polyFillMode === 2 ? "nonzero" : "evenodd");
|
|
2807
|
+
fillShapeExactOrFast(rCtx, buildWithPath, state.polyFillMode === 2 ? "nonzero" : "evenodd");
|
|
1907
2808
|
}
|
|
1908
|
-
|
|
1909
|
-
ctx.stroke();
|
|
2809
|
+
strokeShapeExactOrFast(rCtx, buildWithPath);
|
|
1910
2810
|
}
|
|
1911
2811
|
if (count > 0) {
|
|
1912
2812
|
const last = count - 1;
|
|
@@ -1928,43 +2828,44 @@ function handlePoly16(rCtx, recType, offset, dataOff, recSize) {
|
|
|
1928
2828
|
const isPolygon = recType === EMR_POLYGON16;
|
|
1929
2829
|
const isBezier = recType === EMR_POLYBEZIER16 || recType === EMR_POLYBEZIERTO16;
|
|
1930
2830
|
const isTo = recType === EMR_POLYBEZIERTO16 || recType === EMR_POLYLINETO16;
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
)
|
|
1948
|
-
|
|
2831
|
+
const pt = (i) => gmapPoint(rCtx, view.getInt16(ptOff + i * 4, true), view.getInt16(ptOff + i * 4 + 2, true));
|
|
2832
|
+
const build = (target) => {
|
|
2833
|
+
if (!isTo) {
|
|
2834
|
+
const p0 = pt(0);
|
|
2835
|
+
target.moveTo(p0.x, p0.y);
|
|
2836
|
+
}
|
|
2837
|
+
let i = isTo ? 0 : 1;
|
|
2838
|
+
if (isBezier) {
|
|
2839
|
+
while (i + 2 < count) {
|
|
2840
|
+
const p1 = pt(i);
|
|
2841
|
+
const p2 = pt(i + 1);
|
|
2842
|
+
const p3 = pt(i + 2);
|
|
2843
|
+
target.bezierCurveTo(p1.x, p1.y, p2.x, p2.y, p3.x, p3.y);
|
|
2844
|
+
i += 3;
|
|
2845
|
+
}
|
|
2846
|
+
} else {
|
|
2847
|
+
for (; i < count; i++) {
|
|
2848
|
+
const p = pt(i);
|
|
2849
|
+
target.lineTo(p.x, p.y);
|
|
2850
|
+
}
|
|
1949
2851
|
}
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
ctx.lineTo(
|
|
1953
|
-
gmx(rCtx, view.getInt16(ptOff + i * 4, true)),
|
|
1954
|
-
gmy(rCtx, view.getInt16(ptOff + i * 4 + 2, true))
|
|
1955
|
-
);
|
|
2852
|
+
if (isPolygon) {
|
|
2853
|
+
target.closePath();
|
|
1956
2854
|
}
|
|
2855
|
+
};
|
|
2856
|
+
if (!inPath) {
|
|
2857
|
+
ctx.beginPath();
|
|
1957
2858
|
}
|
|
1958
|
-
|
|
1959
|
-
ctx.closePath();
|
|
1960
|
-
}
|
|
2859
|
+
build(ctx);
|
|
1961
2860
|
if (!inPath) {
|
|
2861
|
+
const buildWithPath = (target) => {
|
|
2862
|
+
target.beginPath();
|
|
2863
|
+
build(target);
|
|
2864
|
+
};
|
|
1962
2865
|
if (isPolygon) {
|
|
1963
|
-
|
|
1964
|
-
ctx.fill(state.polyFillMode === 2 ? "nonzero" : "evenodd");
|
|
2866
|
+
fillShapeExactOrFast(rCtx, buildWithPath, state.polyFillMode === 2 ? "nonzero" : "evenodd");
|
|
1965
2867
|
}
|
|
1966
|
-
|
|
1967
|
-
ctx.stroke();
|
|
2868
|
+
strokeShapeExactOrFast(rCtx, buildWithPath);
|
|
1968
2869
|
}
|
|
1969
2870
|
if (count > 0) {
|
|
1970
2871
|
const last = count - 1;
|
|
@@ -2017,16 +2918,24 @@ function handleEmfGdiPolyPathRecord(rCtx, recType, offset, dataOff, recSize) {
|
|
|
2017
2918
|
case EMR_CLOSEFIGURE:
|
|
2018
2919
|
ctx.closePath();
|
|
2019
2920
|
return true;
|
|
2020
|
-
case EMR_FILLPATH:
|
|
2021
|
-
|
|
2022
|
-
|
|
2921
|
+
case EMR_FILLPATH: {
|
|
2922
|
+
const fillRule = state.polyFillMode === 2 ? "nonzero" : "evenodd";
|
|
2923
|
+
if (!fillCurrentPathWithGdiPattern(rCtx, fillRule)) {
|
|
2924
|
+
applyBrush(ctx, state);
|
|
2925
|
+
ctx.fill(fillRule);
|
|
2926
|
+
}
|
|
2023
2927
|
return true;
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2928
|
+
}
|
|
2929
|
+
case EMR_STROKEANDFILLPATH: {
|
|
2930
|
+
const fillRule = state.polyFillMode === 2 ? "nonzero" : "evenodd";
|
|
2931
|
+
if (!fillCurrentPathWithGdiPattern(rCtx, fillRule)) {
|
|
2932
|
+
applyBrush(ctx, state);
|
|
2933
|
+
ctx.fill(fillRule);
|
|
2934
|
+
}
|
|
2027
2935
|
applyPen(ctx, state);
|
|
2028
2936
|
ctx.stroke();
|
|
2029
2937
|
return true;
|
|
2938
|
+
}
|
|
2030
2939
|
case EMR_STROKEPATH:
|
|
2031
2940
|
applyPen(ctx, state);
|
|
2032
2941
|
ctx.stroke();
|
|
@@ -2091,12 +3000,30 @@ function handleEmfObjectRecord(rCtx, recType, dataOff, recSize) {
|
|
|
2091
3000
|
case EMR_CREATEBRUSHINDIRECT: {
|
|
2092
3001
|
if (recSize >= 24) {
|
|
2093
3002
|
const ihBrush = view.getUint32(dataOff, true);
|
|
2094
|
-
const brushStyle = view.getUint32(dataOff + 4, true);
|
|
2095
|
-
const color = readColorRef(view, dataOff + 8);
|
|
3003
|
+
const brushStyle = view.getUint32(dataOff + 4, true);
|
|
3004
|
+
const color = readColorRef(view, dataOff + 8);
|
|
3005
|
+
const hatch = view.getUint32(dataOff + 12, true);
|
|
3006
|
+
rCtx.objectTable.set(ihBrush, {
|
|
3007
|
+
kind: "brush",
|
|
3008
|
+
style: brushStyle,
|
|
3009
|
+
color,
|
|
3010
|
+
...brushStyle === 2 && hatch <= 5 ? { pattern: { kind: "hatch", hatch } } : {}
|
|
3011
|
+
});
|
|
3012
|
+
}
|
|
3013
|
+
return true;
|
|
3014
|
+
}
|
|
3015
|
+
case EMR_CREATEMONOBRUSH:
|
|
3016
|
+
case EMR_CREATEDIBPATTERNBRUSHPT: {
|
|
3017
|
+
if (recSize >= 32) {
|
|
3018
|
+
const ihBrush = view.getUint32(dataOff, true);
|
|
3019
|
+
const mono = recType === EMR_CREATEMONOBRUSH;
|
|
3020
|
+
const pattern = parsePatternBrush(view, dataOff - 8, dataOff, mono);
|
|
2096
3021
|
rCtx.objectTable.set(ihBrush, {
|
|
2097
3022
|
kind: "brush",
|
|
2098
|
-
style:
|
|
2099
|
-
|
|
3023
|
+
style: pattern ? mono ? 3 : 6 : 0,
|
|
3024
|
+
// Flat stand-in for fills that do not realise the pattern.
|
|
3025
|
+
color: "#808080",
|
|
3026
|
+
...pattern ? { pattern } : {}
|
|
2100
3027
|
});
|
|
2101
3028
|
}
|
|
2102
3029
|
return true;
|
|
@@ -2105,19 +3032,23 @@ function handleEmfObjectRecord(rCtx, recType, dataOff, recSize) {
|
|
|
2105
3032
|
if (recSize >= 332) {
|
|
2106
3033
|
const ihFont = view.getUint32(dataOff, true);
|
|
2107
3034
|
const height = view.getInt32(dataOff + 4, true);
|
|
3035
|
+
const escapementTenthDeg = view.getInt32(dataOff + 12, true);
|
|
2108
3036
|
const weight = view.getInt32(dataOff + 20, true);
|
|
2109
3037
|
const italic = view.getUint8(dataOff + 24);
|
|
2110
3038
|
const underline = view.getUint8(dataOff + 25);
|
|
2111
3039
|
const strikeOut = view.getUint8(dataOff + 26);
|
|
2112
|
-
const family = readUtf16LE(view, dataOff +
|
|
3040
|
+
const family = readUtf16LE(view, dataOff + 32, 32) || "sans-serif";
|
|
2113
3041
|
rCtx.objectTable.set(ihFont, {
|
|
2114
3042
|
kind: "font",
|
|
2115
|
-
|
|
3043
|
+
// Sign kept (not Math.abs'd): resolveFontPixelHeight() needs it
|
|
3044
|
+
// to distinguish GDI's cell-height vs character-height convention.
|
|
3045
|
+
height,
|
|
2116
3046
|
weight,
|
|
2117
3047
|
italic: italic !== 0,
|
|
2118
3048
|
underline: underline !== 0,
|
|
2119
3049
|
strikeOut: strikeOut !== 0,
|
|
2120
|
-
family
|
|
3050
|
+
family,
|
|
3051
|
+
escapementTenthDeg
|
|
2121
3052
|
});
|
|
2122
3053
|
}
|
|
2123
3054
|
return true;
|
|
@@ -2136,6 +3067,7 @@ function handleEmfObjectRecord(rCtx, recType, dataOff, recSize) {
|
|
|
2136
3067
|
case "brush":
|
|
2137
3068
|
state.brushStyle = obj.style;
|
|
2138
3069
|
state.brushColor = obj.color;
|
|
3070
|
+
state.brushPattern = obj.pattern ?? null;
|
|
2139
3071
|
break;
|
|
2140
3072
|
case "font":
|
|
2141
3073
|
state.fontHeight = obj.height;
|
|
@@ -2144,6 +3076,7 @@ function handleEmfObjectRecord(rCtx, recType, dataOff, recSize) {
|
|
|
2144
3076
|
state.fontUnderline = obj.underline;
|
|
2145
3077
|
state.fontStrikeOut = obj.strikeOut;
|
|
2146
3078
|
state.fontFamily = obj.family;
|
|
3079
|
+
state.fontEscapementTenthDeg = obj.escapementTenthDeg ?? 0;
|
|
2147
3080
|
break;
|
|
2148
3081
|
}
|
|
2149
3082
|
}
|
|
@@ -2312,15 +3245,22 @@ function defaultState() {
|
|
|
2312
3245
|
penStyle: 0,
|
|
2313
3246
|
brushColor: "#ffffff",
|
|
2314
3247
|
brushStyle: 0,
|
|
3248
|
+
brushPattern: null,
|
|
3249
|
+
brushOrgX: 0,
|
|
3250
|
+
brushOrgY: 0,
|
|
3251
|
+
stretchBltMode: 1,
|
|
2315
3252
|
textColor: "#000000",
|
|
2316
3253
|
bkColor: "#ffffff",
|
|
2317
3254
|
bkMode: 1,
|
|
2318
|
-
|
|
3255
|
+
// Negative (character-height convention) so the no-font-selected default
|
|
3256
|
+
// resolves to exactly 12px, matching this library's historical fallback.
|
|
3257
|
+
fontHeight: -12,
|
|
2319
3258
|
fontWeight: 400,
|
|
2320
3259
|
fontItalic: false,
|
|
2321
3260
|
fontFamily: "sans-serif",
|
|
2322
3261
|
fontUnderline: false,
|
|
2323
3262
|
fontStrikeOut: false,
|
|
3263
|
+
fontEscapementTenthDeg: 0,
|
|
2324
3264
|
rop2: 13,
|
|
2325
3265
|
curX: 0,
|
|
2326
3266
|
curY: 0,
|
|
@@ -2437,7 +3377,19 @@ function handleEmfGdiStateRecord(rCtx, recType, _offset, dataOff, recSize) {
|
|
|
2437
3377
|
}
|
|
2438
3378
|
return true;
|
|
2439
3379
|
}
|
|
2440
|
-
case EMR_SETSTRETCHBLTMODE:
|
|
3380
|
+
case EMR_SETSTRETCHBLTMODE: {
|
|
3381
|
+
if (recSize >= 12) {
|
|
3382
|
+
state.stretchBltMode = view.getUint32(dataOff, true);
|
|
3383
|
+
}
|
|
3384
|
+
return true;
|
|
3385
|
+
}
|
|
3386
|
+
case EMR_SETBRUSHORGEX: {
|
|
3387
|
+
if (recSize >= 16) {
|
|
3388
|
+
state.brushOrgX = view.getInt32(dataOff, true);
|
|
3389
|
+
state.brushOrgY = view.getInt32(dataOff + 4, true);
|
|
3390
|
+
}
|
|
3391
|
+
return true;
|
|
3392
|
+
}
|
|
2441
3393
|
case EMR_SETMITERLIMIT:
|
|
2442
3394
|
case EMR_SETTEXTALIGN: {
|
|
2443
3395
|
if (recType === EMR_SETTEXTALIGN && recSize >= 12) {
|
|
@@ -2480,6 +3432,411 @@ function readPointFromView(view, offset, compressed) {
|
|
|
2480
3432
|
};
|
|
2481
3433
|
}
|
|
2482
3434
|
|
|
3435
|
+
// src/emf-plus-brush-gradient.ts
|
|
3436
|
+
var IDENTITY = [1, 0, 0, 1, 0, 0];
|
|
3437
|
+
function mulMatrix(a, b) {
|
|
3438
|
+
return [
|
|
3439
|
+
a[0] * b[0] + a[2] * b[1],
|
|
3440
|
+
a[1] * b[0] + a[3] * b[1],
|
|
3441
|
+
a[0] * b[2] + a[2] * b[3],
|
|
3442
|
+
a[1] * b[2] + a[3] * b[3],
|
|
3443
|
+
a[0] * b[4] + a[2] * b[5] + a[4],
|
|
3444
|
+
a[1] * b[4] + a[3] * b[5] + a[5]
|
|
3445
|
+
];
|
|
3446
|
+
}
|
|
3447
|
+
function invertLinear(m) {
|
|
3448
|
+
const det = m[0] * m[3] - m[1] * m[2];
|
|
3449
|
+
if (!Number.isFinite(det) || Math.abs(det) < 1e-12) {
|
|
3450
|
+
return null;
|
|
3451
|
+
}
|
|
3452
|
+
return [m[3] / det, -m[1] / det, -m[2] / det, m[0] / det];
|
|
3453
|
+
}
|
|
3454
|
+
function lerpArgb(a, b, t) {
|
|
3455
|
+
const ch = (shift) => {
|
|
3456
|
+
const ca = a >>> shift & 255;
|
|
3457
|
+
const cb = b >>> shift & 255;
|
|
3458
|
+
return Math.round(ca + (cb - ca) * t);
|
|
3459
|
+
};
|
|
3460
|
+
return (ch(24) << 24 | ch(16) << 16 | ch(8) << 8 | ch(0)) >>> 0;
|
|
3461
|
+
}
|
|
3462
|
+
function piecewise(xs, ys, x) {
|
|
3463
|
+
const n = Math.min(xs.length, ys.length);
|
|
3464
|
+
if (n === 0) {
|
|
3465
|
+
return x;
|
|
3466
|
+
}
|
|
3467
|
+
if (x <= xs[0]) {
|
|
3468
|
+
return ys[0];
|
|
3469
|
+
}
|
|
3470
|
+
for (let i = 1; i < n; i++) {
|
|
3471
|
+
if (x <= xs[i]) {
|
|
3472
|
+
const span = xs[i] - xs[i - 1];
|
|
3473
|
+
const f = span > 0 ? (x - xs[i - 1]) / span : 1;
|
|
3474
|
+
return ys[i - 1] + (ys[i] - ys[i - 1]) * f;
|
|
3475
|
+
}
|
|
3476
|
+
}
|
|
3477
|
+
return ys[n - 1];
|
|
3478
|
+
}
|
|
3479
|
+
function stopColorAt(stops, t) {
|
|
3480
|
+
if (stops.length === 0 || stops.some((s) => s.argb === void 0)) {
|
|
3481
|
+
return null;
|
|
3482
|
+
}
|
|
3483
|
+
if (t <= stops[0].offset) {
|
|
3484
|
+
return stops[0].argb;
|
|
3485
|
+
}
|
|
3486
|
+
for (let i = 1; i < stops.length; i++) {
|
|
3487
|
+
const b = stops[i];
|
|
3488
|
+
if (t <= b.offset) {
|
|
3489
|
+
const a = stops[i - 1];
|
|
3490
|
+
const span = b.offset - a.offset;
|
|
3491
|
+
return lerpArgb(a.argb, b.argb, span > 0 ? (t - a.offset) / span : 1);
|
|
3492
|
+
}
|
|
3493
|
+
}
|
|
3494
|
+
return stops[stops.length - 1].argb;
|
|
3495
|
+
}
|
|
3496
|
+
function pathGradientColorAt(shape, x, y) {
|
|
3497
|
+
const { center, boundary, boundaryArgb } = shape;
|
|
3498
|
+
const n = boundary.length;
|
|
3499
|
+
const px = x - center.x;
|
|
3500
|
+
const py = y - center.y;
|
|
3501
|
+
let found = -1;
|
|
3502
|
+
let bestS = 0;
|
|
3503
|
+
let bestU = 0;
|
|
3504
|
+
const eps = 1e-9;
|
|
3505
|
+
for (let i = 0; i < n; i++) {
|
|
3506
|
+
const v0 = boundary[i];
|
|
3507
|
+
const v1 = boundary[(i + 1) % n];
|
|
3508
|
+
const ax = v0.x - center.x;
|
|
3509
|
+
const ay = v0.y - center.y;
|
|
3510
|
+
const ex = v1.x - v0.x;
|
|
3511
|
+
const ey = v1.y - v0.y;
|
|
3512
|
+
const det = ax * ey - ay * ex;
|
|
3513
|
+
if (Math.abs(det) < 1e-12) {
|
|
3514
|
+
continue;
|
|
3515
|
+
}
|
|
3516
|
+
const alpha = (px * ey - py * ex) / det;
|
|
3517
|
+
const beta = (ax * py - ay * px) / det;
|
|
3518
|
+
if (alpha < -eps || beta < -eps || beta > alpha + eps || alpha > 1 + eps) {
|
|
3519
|
+
continue;
|
|
3520
|
+
}
|
|
3521
|
+
found = i;
|
|
3522
|
+
bestS = Math.max(0, alpha);
|
|
3523
|
+
bestU = alpha > 0 ? Math.min(1, Math.max(0, beta / alpha)) : 0;
|
|
3524
|
+
}
|
|
3525
|
+
if (found < 0) {
|
|
3526
|
+
return null;
|
|
3527
|
+
}
|
|
3528
|
+
let s = bestS;
|
|
3529
|
+
if (shape.focus) {
|
|
3530
|
+
const f = Math.min(0.999, Math.max(0, (shape.focus.x + shape.focus.y) / 2));
|
|
3531
|
+
s = s <= f ? 0 : (s - f) / (1 - f);
|
|
3532
|
+
}
|
|
3533
|
+
const pos = 1 - s;
|
|
3534
|
+
if (shape.preset && shape.preset.positions.length > 0) {
|
|
3535
|
+
const { positions, argb } = shape.preset;
|
|
3536
|
+
if (pos <= positions[0]) {
|
|
3537
|
+
return argb[0];
|
|
3538
|
+
}
|
|
3539
|
+
for (let k = 1; k < positions.length; k++) {
|
|
3540
|
+
if (pos <= positions[k]) {
|
|
3541
|
+
const span = positions[k] - positions[k - 1];
|
|
3542
|
+
return lerpArgb(argb[k - 1], argb[k], span > 0 ? (pos - positions[k - 1]) / span : 1);
|
|
3543
|
+
}
|
|
3544
|
+
}
|
|
3545
|
+
return argb[positions.length - 1];
|
|
3546
|
+
}
|
|
3547
|
+
const c0 = boundaryArgb[found] ?? shape.centerArgb;
|
|
3548
|
+
const c1 = boundaryArgb[(found + 1) % n] ?? c0;
|
|
3549
|
+
const surround = lerpArgb(c0, c1, bestU);
|
|
3550
|
+
const factor = shape.blend ? piecewise(shape.blend.positions, shape.blend.factors, pos) : pos;
|
|
3551
|
+
return lerpArgb(surround, shape.centerArgb, Math.min(1, Math.max(0, factor)));
|
|
3552
|
+
}
|
|
3553
|
+
var MAX_TILE = 2048;
|
|
3554
|
+
function mirrorFlags(wrap) {
|
|
3555
|
+
return {
|
|
3556
|
+
x: wrap === "tile-flip-x" || wrap === "tile-flip-xy",
|
|
3557
|
+
y: wrap === "tile-flip-y" || wrap === "tile-flip-xy"
|
|
3558
|
+
};
|
|
3559
|
+
}
|
|
3560
|
+
function buildPattern(ctx, rect, tw, th, wrap, brush, delta, sampling, colorAt) {
|
|
3561
|
+
if (typeof ctx.createPattern !== "function") {
|
|
3562
|
+
return null;
|
|
3563
|
+
}
|
|
3564
|
+
const mirror = wrap === "clamp" ? { x: false, y: false } : mirrorFlags(wrap);
|
|
3565
|
+
const w = tw * (mirror.x ? 2 : 1);
|
|
3566
|
+
const h = th * (mirror.y ? 2 : 1);
|
|
3567
|
+
const temp = createTempCanvas(w, h);
|
|
3568
|
+
if (!temp) {
|
|
3569
|
+
return null;
|
|
3570
|
+
}
|
|
3571
|
+
const stepX = rect.w / tw;
|
|
3572
|
+
const stepY = rect.h / th;
|
|
3573
|
+
const offX = (i) => {
|
|
3574
|
+
const d = i % tw * stepX + (0.5 + sampling.phaseX) * stepX;
|
|
3575
|
+
return i < tw ? d : rect.w - sampling.lagX - (d - (0.5 + sampling.phaseX) * stepX) - 0.5 * stepX;
|
|
3576
|
+
};
|
|
3577
|
+
const offY = (j) => {
|
|
3578
|
+
const d = (j % th + 0.5) * stepY;
|
|
3579
|
+
return j < th ? d : rect.h - sampling.lagY - d;
|
|
3580
|
+
};
|
|
3581
|
+
const data = new Uint8ClampedArray(w * h * 4);
|
|
3582
|
+
for (let j = 0; j < h; j++) {
|
|
3583
|
+
const by = rect.y + offY(j);
|
|
3584
|
+
for (let i = 0; i < w; i++) {
|
|
3585
|
+
const c = colorAt(rect.x + offX(i), by);
|
|
3586
|
+
if (c === null) {
|
|
3587
|
+
continue;
|
|
3588
|
+
}
|
|
3589
|
+
const o = (j * w + i) * 4;
|
|
3590
|
+
data[o] = c >>> 16 & 255;
|
|
3591
|
+
data[o + 1] = c >>> 8 & 255;
|
|
3592
|
+
data[o + 2] = c & 255;
|
|
3593
|
+
data[o + 3] = c >>> 24 & 255;
|
|
3594
|
+
}
|
|
3595
|
+
}
|
|
3596
|
+
canvasPutImageData(temp.ctx, createImageDataCompat(data, w, h), 0, 0);
|
|
3597
|
+
const pattern = canvasCreatePattern(ctx, temp.canvas, wrap === "clamp" ? "no-repeat" : "repeat");
|
|
3598
|
+
if (!pattern || typeof pattern.setTransform !== "function") {
|
|
3599
|
+
return null;
|
|
3600
|
+
}
|
|
3601
|
+
const place = [stepX, 0, 0, stepY, rect.x + sampling.phaseX * stepX, rect.y];
|
|
3602
|
+
const m = mulMatrix([1, 0, 0, 1, delta.x, delta.y], mulMatrix(brush, place));
|
|
3603
|
+
try {
|
|
3604
|
+
pattern.setTransform({ a: m[0], b: m[1], c: m[2], d: m[3], e: m[4], f: m[5] });
|
|
3605
|
+
} catch {
|
|
3606
|
+
return null;
|
|
3607
|
+
}
|
|
3608
|
+
return pattern;
|
|
3609
|
+
}
|
|
3610
|
+
function halfPixelDelta(device) {
|
|
3611
|
+
const inv = invertLinear(device);
|
|
3612
|
+
if (!inv) {
|
|
3613
|
+
return { x: 0, y: 0 };
|
|
3614
|
+
}
|
|
3615
|
+
return { x: inv[0] * 0.5 + inv[2] * 0.5, y: inv[1] * 0.5 + inv[3] * 0.5 };
|
|
3616
|
+
}
|
|
3617
|
+
var SUPERSAMPLE = 4;
|
|
3618
|
+
function texelsFor(full, axis, length) {
|
|
3619
|
+
const scale = axis === "x" ? Math.hypot(full[0], full[1]) : Math.hypot(full[2], full[3]);
|
|
3620
|
+
return Math.max(2, Math.min(MAX_TILE, Math.ceil(Math.abs(length) * scale * SUPERSAMPLE)));
|
|
3621
|
+
}
|
|
3622
|
+
function linearGradientEndpoints(rect, transform) {
|
|
3623
|
+
const m = transform ?? IDENTITY;
|
|
3624
|
+
const map = (x, y) => ({
|
|
3625
|
+
x: m[0] * x + m[2] * y + m[4],
|
|
3626
|
+
y: m[1] * x + m[3] * y + m[5]
|
|
3627
|
+
});
|
|
3628
|
+
const p1 = map(rect.x, rect.y + rect.h / 2);
|
|
3629
|
+
const pe = map(rect.x + rect.w, rect.y + rect.h / 2);
|
|
3630
|
+
const nx = m[3];
|
|
3631
|
+
const ny = -m[2];
|
|
3632
|
+
const nn = nx * nx + ny * ny;
|
|
3633
|
+
if (nn < 1e-12) {
|
|
3634
|
+
return { x1: p1.x, y1: p1.y, x2: pe.x, y2: pe.y };
|
|
3635
|
+
}
|
|
3636
|
+
const t = ((pe.x - p1.x) * nx + (pe.y - p1.y) * ny) / nn;
|
|
3637
|
+
return { x1: p1.x, y1: p1.y, x2: p1.x + nx * t, y2: p1.y + ny * t };
|
|
3638
|
+
}
|
|
3639
|
+
function plainLinear(ctx, grad) {
|
|
3640
|
+
if (typeof ctx.createLinearGradient !== "function") {
|
|
3641
|
+
return null;
|
|
3642
|
+
}
|
|
3643
|
+
const pts = grad.rect ? linearGradientEndpoints(grad.rect, grad.transform) : grad;
|
|
3644
|
+
if (pts.x1 === pts.x2 && pts.y1 === pts.y2) {
|
|
3645
|
+
return null;
|
|
3646
|
+
}
|
|
3647
|
+
const g = ctx.createLinearGradient(pts.x1, pts.y1, pts.x2, pts.y2);
|
|
3648
|
+
for (const stop of grad.stops) {
|
|
3649
|
+
g.addColorStop(stop.offset, stop.color);
|
|
3650
|
+
}
|
|
3651
|
+
return g;
|
|
3652
|
+
}
|
|
3653
|
+
var SEAM_BIAS = 1e-5;
|
|
3654
|
+
var MAX_PERIODS = 2048;
|
|
3655
|
+
function surfaceSize3(ctx) {
|
|
3656
|
+
const canvas = ctx.canvas;
|
|
3657
|
+
const w = canvas?.width;
|
|
3658
|
+
const h = canvas?.height;
|
|
3659
|
+
return typeof w === "number" && typeof h === "number" && w > 0 && h > 0 ? { w, h } : null;
|
|
3660
|
+
}
|
|
3661
|
+
function tiledLinear(ctx, grad, rect, device) {
|
|
3662
|
+
const size = surfaceSize3(ctx);
|
|
3663
|
+
const inv = invertLinear(device);
|
|
3664
|
+
if (!size || !inv || typeof ctx.createLinearGradient !== "function") {
|
|
3665
|
+
return null;
|
|
3666
|
+
}
|
|
3667
|
+
const e = linearGradientEndpoints(rect, grad.transform);
|
|
3668
|
+
const delta = halfPixelDelta(device);
|
|
3669
|
+
const dx = e.x2 - e.x1;
|
|
3670
|
+
const dy = e.y2 - e.y1;
|
|
3671
|
+
const x1 = e.x1 + delta.x + dx * SEAM_BIAS;
|
|
3672
|
+
const y1 = e.y1 + delta.y + dy * SEAM_BIAS;
|
|
3673
|
+
const len2 = dx * dx + dy * dy;
|
|
3674
|
+
if (!(len2 > 1e-12)) {
|
|
3675
|
+
return null;
|
|
3676
|
+
}
|
|
3677
|
+
let tMin = Infinity;
|
|
3678
|
+
let tMax = -Infinity;
|
|
3679
|
+
for (const [cx, cy] of [
|
|
3680
|
+
[0, 0],
|
|
3681
|
+
[size.w, 0],
|
|
3682
|
+
[0, size.h],
|
|
3683
|
+
[size.w, size.h]
|
|
3684
|
+
]) {
|
|
3685
|
+
const px = cx - device[4];
|
|
3686
|
+
const py = cy - device[5];
|
|
3687
|
+
const wx = inv[0] * px + inv[2] * py;
|
|
3688
|
+
const wy = inv[1] * px + inv[3] * py;
|
|
3689
|
+
const t = ((wx - x1) * dx + (wy - y1) * dy) / len2;
|
|
3690
|
+
tMin = Math.min(tMin, t);
|
|
3691
|
+
tMax = Math.max(tMax, t);
|
|
3692
|
+
}
|
|
3693
|
+
const k0 = Math.floor(tMin);
|
|
3694
|
+
const k1 = Math.max(k0 + 1, Math.ceil(tMax));
|
|
3695
|
+
if (k1 - k0 > MAX_PERIODS) {
|
|
3696
|
+
return null;
|
|
3697
|
+
}
|
|
3698
|
+
const g = ctx.createLinearGradient(x1 + dx * k0, y1 + dy * k0, x1 + dx * k1, y1 + dy * k1);
|
|
3699
|
+
const span = k1 - k0;
|
|
3700
|
+
const mirrorX = mirrorFlags(grad.wrapMode).x;
|
|
3701
|
+
const reversed = [...grad.stops].reverse();
|
|
3702
|
+
for (let k = k0; k < k1; k++) {
|
|
3703
|
+
const mirrored = mirrorX && (k % 2 + 2) % 2 === 1;
|
|
3704
|
+
for (const stop of mirrored ? reversed : grad.stops) {
|
|
3705
|
+
const local = mirrored ? 1 - stop.offset : stop.offset;
|
|
3706
|
+
g.addColorStop(Math.min(1, Math.max(0, (k - k0 + local) / span)), stop.color);
|
|
3707
|
+
}
|
|
3708
|
+
}
|
|
3709
|
+
return g;
|
|
3710
|
+
}
|
|
3711
|
+
function linearPaint(ctx, grad, device) {
|
|
3712
|
+
const rect = grad.rect;
|
|
3713
|
+
if (grad.wrapMode !== "clamp" && rect && rect.w !== 0) {
|
|
3714
|
+
const unrolled = tiledLinear(ctx, grad, rect, device);
|
|
3715
|
+
if (unrolled) {
|
|
3716
|
+
return unrolled;
|
|
3717
|
+
}
|
|
3718
|
+
if (stopColorAt(grad.stops, 0) !== null) {
|
|
3719
|
+
const brush = grad.transform ?? IDENTITY;
|
|
3720
|
+
const tw = texelsFor(mulMatrix(device, brush), "x", rect.w);
|
|
3721
|
+
const pattern = buildPattern(
|
|
3722
|
+
ctx,
|
|
3723
|
+
rect,
|
|
3724
|
+
tw,
|
|
3725
|
+
1,
|
|
3726
|
+
grad.wrapMode,
|
|
3727
|
+
brush,
|
|
3728
|
+
halfPixelDelta(device),
|
|
3729
|
+
{ phaseX: 0.5, lagX: 0, lagY: 0 },
|
|
3730
|
+
(bx) => stopColorAt(grad.stops, (bx - rect.x) / rect.w)
|
|
3731
|
+
);
|
|
3732
|
+
if (pattern) {
|
|
3733
|
+
return pattern;
|
|
3734
|
+
}
|
|
3735
|
+
}
|
|
3736
|
+
}
|
|
3737
|
+
return plainLinear(ctx, grad);
|
|
3738
|
+
}
|
|
3739
|
+
function boundaryBox(points) {
|
|
3740
|
+
if (points.length < 3) {
|
|
3741
|
+
return null;
|
|
3742
|
+
}
|
|
3743
|
+
let x0 = Infinity;
|
|
3744
|
+
let y0 = Infinity;
|
|
3745
|
+
let x1 = -Infinity;
|
|
3746
|
+
let y1 = -Infinity;
|
|
3747
|
+
for (const p of points) {
|
|
3748
|
+
x0 = Math.min(x0, p.x);
|
|
3749
|
+
y0 = Math.min(y0, p.y);
|
|
3750
|
+
x1 = Math.max(x1, p.x);
|
|
3751
|
+
y1 = Math.max(y1, p.y);
|
|
3752
|
+
}
|
|
3753
|
+
return x1 > x0 && y1 > y0 ? { x: x0, y: y0, w: x1 - x0, h: y1 - y0 } : null;
|
|
3754
|
+
}
|
|
3755
|
+
function pathPaint(ctx, shape, wrap, device) {
|
|
3756
|
+
const box = boundaryBox(shape.boundary);
|
|
3757
|
+
if (!box) {
|
|
3758
|
+
return null;
|
|
3759
|
+
}
|
|
3760
|
+
const brush = shape.transform ?? IDENTITY;
|
|
3761
|
+
const full = mulMatrix(device, brush);
|
|
3762
|
+
const pxX = Math.hypot(full[0], full[1]);
|
|
3763
|
+
const pxY = Math.hypot(full[2], full[3]);
|
|
3764
|
+
return buildPattern(
|
|
3765
|
+
ctx,
|
|
3766
|
+
box,
|
|
3767
|
+
texelsFor(full, "x", box.w),
|
|
3768
|
+
texelsFor(full, "y", box.h),
|
|
3769
|
+
wrap,
|
|
3770
|
+
brush,
|
|
3771
|
+
halfPixelDelta(device),
|
|
3772
|
+
{ phaseX: 0, lagX: pxX > 0 ? 1 / pxX : 0, lagY: pxY > 0 ? 1 / pxY : 0 },
|
|
3773
|
+
(bx, by) => pathGradientColorAt(shape, bx, by)
|
|
3774
|
+
);
|
|
3775
|
+
}
|
|
3776
|
+
function radialFallback(ctx, grad) {
|
|
3777
|
+
if (!(grad.r > 0) || typeof ctx.createRadialGradient !== "function") {
|
|
3778
|
+
return null;
|
|
3779
|
+
}
|
|
3780
|
+
const g = ctx.createRadialGradient(grad.cx, grad.cy, 0, grad.cx, grad.cy, grad.r);
|
|
3781
|
+
for (const stop of grad.stops) {
|
|
3782
|
+
g.addColorStop(stop.offset, stop.color);
|
|
3783
|
+
}
|
|
3784
|
+
return g;
|
|
3785
|
+
}
|
|
3786
|
+
function createBrushGradient(ctx, grad, device = IDENTITY) {
|
|
3787
|
+
try {
|
|
3788
|
+
if (grad.type === "linear") {
|
|
3789
|
+
return linearPaint(ctx, grad, device);
|
|
3790
|
+
}
|
|
3791
|
+
if (grad.shape) {
|
|
3792
|
+
const pattern = pathPaint(ctx, grad.shape, grad.wrapMode, device);
|
|
3793
|
+
if (pattern) {
|
|
3794
|
+
return pattern;
|
|
3795
|
+
}
|
|
3796
|
+
emfLog("createBrushGradient: path gradient pattern unavailable, using the radial approximation");
|
|
3797
|
+
}
|
|
3798
|
+
return radialFallback(ctx, grad);
|
|
3799
|
+
} catch {
|
|
3800
|
+
return null;
|
|
3801
|
+
}
|
|
3802
|
+
}
|
|
3803
|
+
|
|
3804
|
+
// src/emf-plus-brush-texture.ts
|
|
3805
|
+
var IDENTITY2 = [1, 0, 0, 1, 0, 0];
|
|
3806
|
+
function packArgb(rgba, i) {
|
|
3807
|
+
return (rgba[i + 3] << 24 | rgba[i] << 16 | rgba[i + 1] << 8 | rgba[i + 2]) >>> 0;
|
|
3808
|
+
}
|
|
3809
|
+
function createBrushTexture(ctx, texture, device = IDENTITY2) {
|
|
3810
|
+
const { width, height, rgba } = texture;
|
|
3811
|
+
if (width <= 0 || height <= 0) {
|
|
3812
|
+
return null;
|
|
3813
|
+
}
|
|
3814
|
+
try {
|
|
3815
|
+
const brush = texture.transform ?? IDENTITY2;
|
|
3816
|
+
const colorAt = (bx, by) => {
|
|
3817
|
+
const ix = Math.floor(bx);
|
|
3818
|
+
const iy = Math.floor(by);
|
|
3819
|
+
if (ix < 0 || iy < 0 || ix >= width || iy >= height) {
|
|
3820
|
+
return null;
|
|
3821
|
+
}
|
|
3822
|
+
return packArgb(rgba, (iy * width + ix) * 4);
|
|
3823
|
+
};
|
|
3824
|
+
return buildPattern(
|
|
3825
|
+
ctx,
|
|
3826
|
+
{ x: 0, y: 0, w: width, h: height },
|
|
3827
|
+
width,
|
|
3828
|
+
height,
|
|
3829
|
+
texture.wrapMode,
|
|
3830
|
+
brush,
|
|
3831
|
+
halfPixelDelta(device),
|
|
3832
|
+
{ phaseX: 0, lagX: 0, lagY: 0 },
|
|
3833
|
+
colorAt
|
|
3834
|
+
);
|
|
3835
|
+
} catch {
|
|
3836
|
+
return null;
|
|
3837
|
+
}
|
|
3838
|
+
}
|
|
3839
|
+
|
|
2483
3840
|
// src/emf-plus-path.ts
|
|
2484
3841
|
function parseEmfPlusPath(data, off, maxLen) {
|
|
2485
3842
|
if (maxLen < 12) {
|
|
@@ -2614,32 +3971,6 @@ function multiplyMatrix(m1, m2) {
|
|
|
2614
3971
|
m1[4] * m2[1] + m1[5] * m2[3] + m2[5]
|
|
2615
3972
|
];
|
|
2616
3973
|
}
|
|
2617
|
-
function createBrushGradient(rCtx, grad) {
|
|
2618
|
-
const ctx = rCtx.ctx;
|
|
2619
|
-
try {
|
|
2620
|
-
let g = null;
|
|
2621
|
-
if (grad.type === "linear" && typeof ctx.createLinearGradient === "function") {
|
|
2622
|
-
if (grad.x1 === grad.x2 && grad.y1 === grad.y2) {
|
|
2623
|
-
return null;
|
|
2624
|
-
}
|
|
2625
|
-
g = ctx.createLinearGradient(grad.x1, grad.y1, grad.x2, grad.y2);
|
|
2626
|
-
} else if (grad.type === "radial" && typeof ctx.createRadialGradient === "function") {
|
|
2627
|
-
if (!(grad.r > 0)) {
|
|
2628
|
-
return null;
|
|
2629
|
-
}
|
|
2630
|
-
g = ctx.createRadialGradient(grad.cx, grad.cy, 0, grad.cx, grad.cy, grad.r);
|
|
2631
|
-
}
|
|
2632
|
-
if (!g) {
|
|
2633
|
-
return null;
|
|
2634
|
-
}
|
|
2635
|
-
for (const stop of grad.stops) {
|
|
2636
|
-
g.addColorStop(stop.offset, stop.color);
|
|
2637
|
-
}
|
|
2638
|
-
return g;
|
|
2639
|
-
} catch {
|
|
2640
|
-
return null;
|
|
2641
|
-
}
|
|
2642
|
-
}
|
|
2643
3974
|
function resolveBrushPaint(rCtx, flags, brushIdOrColor) {
|
|
2644
3975
|
if (flags & 32768) {
|
|
2645
3976
|
return argbToRgba(brushIdOrColor);
|
|
@@ -2647,11 +3978,17 @@ function resolveBrushPaint(rCtx, flags, brushIdOrColor) {
|
|
|
2647
3978
|
const obj = rCtx.objectTable.get(brushIdOrColor & 255);
|
|
2648
3979
|
if (obj && obj.kind === "plus-brush") {
|
|
2649
3980
|
if (obj.gradient) {
|
|
2650
|
-
const g = createBrushGradient(rCtx, obj.gradient);
|
|
3981
|
+
const g = createBrushGradient(rCtx.ctx, obj.gradient, plusWorldMatrix(rCtx));
|
|
2651
3982
|
if (g) {
|
|
2652
3983
|
return g;
|
|
2653
3984
|
}
|
|
2654
3985
|
}
|
|
3986
|
+
if (obj.texture) {
|
|
3987
|
+
const p = createBrushTexture(rCtx.ctx, obj.texture, plusWorldMatrix(rCtx));
|
|
3988
|
+
if (p) {
|
|
3989
|
+
return p;
|
|
3990
|
+
}
|
|
3991
|
+
}
|
|
2655
3992
|
return obj.color;
|
|
2656
3993
|
}
|
|
2657
3994
|
return "rgba(0,0,0,1)";
|
|
@@ -2682,18 +4019,14 @@ function getPageUnitMultiplier(pageUnit, pageScale) {
|
|
|
2682
4019
|
}
|
|
2683
4020
|
return unitToPixel * pageScale;
|
|
2684
4021
|
}
|
|
2685
|
-
function
|
|
4022
|
+
function plusWorldMatrix(rCtx) {
|
|
2686
4023
|
const wt = rCtx.worldTransform;
|
|
2687
|
-
const
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
wt[3] * m * d,
|
|
2694
|
-
wt[4] * m * d,
|
|
2695
|
-
wt[5] * m * d
|
|
2696
|
-
);
|
|
4024
|
+
const k = getPageUnitMultiplier(rCtx.pageUnit, rCtx.pageScale) * rCtx.dpiScale;
|
|
4025
|
+
return [wt[0] * k, wt[1] * k, wt[2] * k, wt[3] * k, wt[4] * k, wt[5] * k];
|
|
4026
|
+
}
|
|
4027
|
+
function applyPlusWorldTransform(rCtx) {
|
|
4028
|
+
const m = plusWorldMatrix(rCtx);
|
|
4029
|
+
rCtx.ctx.setTransform(m[0], m[1], m[2], m[3], m[4], m[5]);
|
|
2697
4030
|
}
|
|
2698
4031
|
function pushState(rCtx, stackId) {
|
|
2699
4032
|
rCtx.saveStack.push({
|
|
@@ -3173,42 +4506,216 @@ function handleEmfPlusDrawRecord(rCtx, recType, recFlags, dataOff, recDataSize)
|
|
|
3173
4506
|
} else {
|
|
3174
4507
|
ctx.lineTo(pt.x, pt.y);
|
|
3175
4508
|
}
|
|
3176
|
-
pOff += ptSize;
|
|
4509
|
+
pOff += ptSize;
|
|
4510
|
+
}
|
|
4511
|
+
if (recFlags & 8192) {
|
|
4512
|
+
ctx.closePath();
|
|
4513
|
+
}
|
|
4514
|
+
ctx.stroke();
|
|
4515
|
+
}
|
|
4516
|
+
return true;
|
|
4517
|
+
}
|
|
4518
|
+
case EMFPLUS_FILLPOLYGON: {
|
|
4519
|
+
if (recDataSize >= 8) {
|
|
4520
|
+
const brushVal = view.getUint32(dataOff, true);
|
|
4521
|
+
const count = view.getUint32(dataOff + 4, true);
|
|
4522
|
+
const compressed = (recFlags & 16384) !== 0;
|
|
4523
|
+
const ptSize = compressed ? 4 : 8;
|
|
4524
|
+
ctx.fillStyle = resolveBrushPaint(rCtx, recFlags, brushVal);
|
|
4525
|
+
applyPlusWorldTransform(rCtx);
|
|
4526
|
+
ctx.beginPath();
|
|
4527
|
+
let pOff = dataOff + 8;
|
|
4528
|
+
for (let i = 0; i < count && pOff + ptSize <= dataOff + recDataSize; i++) {
|
|
4529
|
+
const pt = readPointFromView(view, pOff, compressed);
|
|
4530
|
+
if (i === 0) {
|
|
4531
|
+
ctx.moveTo(pt.x, pt.y);
|
|
4532
|
+
} else {
|
|
4533
|
+
ctx.lineTo(pt.x, pt.y);
|
|
4534
|
+
}
|
|
4535
|
+
pOff += ptSize;
|
|
4536
|
+
}
|
|
4537
|
+
ctx.closePath();
|
|
4538
|
+
ctx.fill();
|
|
4539
|
+
}
|
|
4540
|
+
return true;
|
|
4541
|
+
}
|
|
4542
|
+
default:
|
|
4543
|
+
return false;
|
|
4544
|
+
}
|
|
4545
|
+
}
|
|
4546
|
+
|
|
4547
|
+
// src/emf-plus-bitmap-decoder.ts
|
|
4548
|
+
var PIXELFORMAT_24BPP_RGB = 137224;
|
|
4549
|
+
var PIXELFORMAT_32BPP_RGB = 139273;
|
|
4550
|
+
var PIXELFORMAT_32BPP_ARGB = 2498570;
|
|
4551
|
+
var PIXELFORMAT_32BPP_PARGB = 925707;
|
|
4552
|
+
function decodeEmfPlusBitmapPixels(view, pixelStart, width, height, stride, pixelFormat) {
|
|
4553
|
+
const absStride = Math.abs(stride);
|
|
4554
|
+
const topDown = stride > 0;
|
|
4555
|
+
const rowBytes = width * 4;
|
|
4556
|
+
const bmpRowStride = rowBytes + 3 & -4;
|
|
4557
|
+
const pixelDataSize = bmpRowStride * height;
|
|
4558
|
+
const bmpData = new Uint8Array(pixelDataSize);
|
|
4559
|
+
for (let y = 0; y < height; y++) {
|
|
4560
|
+
const srcRow = topDown ? y : height - 1 - y;
|
|
4561
|
+
const rowOff = pixelStart + srcRow * absStride;
|
|
4562
|
+
const dstRow = (height - 1 - y) * bmpRowStride;
|
|
4563
|
+
switch (pixelFormat) {
|
|
4564
|
+
case PIXELFORMAT_32BPP_ARGB:
|
|
4565
|
+
case PIXELFORMAT_32BPP_PARGB: {
|
|
4566
|
+
for (let x = 0; x < width; x++) {
|
|
4567
|
+
const off = rowOff + x * 4;
|
|
4568
|
+
if (off + 3 >= view.byteLength) {
|
|
4569
|
+
break;
|
|
4570
|
+
}
|
|
4571
|
+
let b = view.getUint8(off);
|
|
4572
|
+
let g = view.getUint8(off + 1);
|
|
4573
|
+
let r = view.getUint8(off + 2);
|
|
4574
|
+
const a = view.getUint8(off + 3);
|
|
4575
|
+
if (pixelFormat === PIXELFORMAT_32BPP_PARGB && a > 0 && a < 255) {
|
|
4576
|
+
r = Math.min(255, Math.round(r * 255 / a));
|
|
4577
|
+
g = Math.min(255, Math.round(g * 255 / a));
|
|
4578
|
+
b = Math.min(255, Math.round(b * 255 / a));
|
|
4579
|
+
}
|
|
4580
|
+
const di = dstRow + x * 4;
|
|
4581
|
+
bmpData[di] = b;
|
|
4582
|
+
bmpData[di + 1] = g;
|
|
4583
|
+
bmpData[di + 2] = r;
|
|
4584
|
+
bmpData[di + 3] = a;
|
|
4585
|
+
}
|
|
4586
|
+
break;
|
|
4587
|
+
}
|
|
4588
|
+
case PIXELFORMAT_32BPP_RGB: {
|
|
4589
|
+
for (let x = 0; x < width; x++) {
|
|
4590
|
+
const off = rowOff + x * 4;
|
|
4591
|
+
if (off + 3 >= view.byteLength) {
|
|
4592
|
+
break;
|
|
4593
|
+
}
|
|
4594
|
+
const di = dstRow + x * 4;
|
|
4595
|
+
bmpData[di] = view.getUint8(off);
|
|
4596
|
+
bmpData[di + 1] = view.getUint8(off + 1);
|
|
4597
|
+
bmpData[di + 2] = view.getUint8(off + 2);
|
|
4598
|
+
bmpData[di + 3] = 255;
|
|
4599
|
+
}
|
|
4600
|
+
break;
|
|
4601
|
+
}
|
|
4602
|
+
case PIXELFORMAT_24BPP_RGB: {
|
|
4603
|
+
for (let x = 0; x < width; x++) {
|
|
4604
|
+
const off = rowOff + x * 3;
|
|
4605
|
+
if (off + 2 >= view.byteLength) {
|
|
4606
|
+
break;
|
|
4607
|
+
}
|
|
4608
|
+
const di = dstRow + x * 4;
|
|
4609
|
+
bmpData[di] = view.getUint8(off);
|
|
4610
|
+
bmpData[di + 1] = view.getUint8(off + 1);
|
|
4611
|
+
bmpData[di + 2] = view.getUint8(off + 2);
|
|
4612
|
+
bmpData[di + 3] = 255;
|
|
4613
|
+
}
|
|
4614
|
+
break;
|
|
4615
|
+
}
|
|
4616
|
+
default:
|
|
4617
|
+
return null;
|
|
4618
|
+
}
|
|
4619
|
+
}
|
|
4620
|
+
return wrapBmpFile(bmpData, bmpRowStride, width, height, pixelDataSize);
|
|
4621
|
+
}
|
|
4622
|
+
function decodeEmfPlusBitmapPixelsToRgba(view, pixelStart, width, height, stride, pixelFormat) {
|
|
4623
|
+
const absStride = Math.abs(stride);
|
|
4624
|
+
const topDown = stride > 0;
|
|
4625
|
+
const rgba = new Uint8ClampedArray(width * height * 4);
|
|
4626
|
+
for (let y = 0; y < height; y++) {
|
|
4627
|
+
const srcRow = topDown ? y : height - 1 - y;
|
|
4628
|
+
const rowOff = pixelStart + srcRow * absStride;
|
|
4629
|
+
const dstRow = y * width * 4;
|
|
4630
|
+
switch (pixelFormat) {
|
|
4631
|
+
case PIXELFORMAT_32BPP_ARGB:
|
|
4632
|
+
case PIXELFORMAT_32BPP_PARGB: {
|
|
4633
|
+
for (let x = 0; x < width; x++) {
|
|
4634
|
+
const off = rowOff + x * 4;
|
|
4635
|
+
if (off + 3 >= view.byteLength) {
|
|
4636
|
+
break;
|
|
4637
|
+
}
|
|
4638
|
+
let b = view.getUint8(off);
|
|
4639
|
+
let g = view.getUint8(off + 1);
|
|
4640
|
+
let r = view.getUint8(off + 2);
|
|
4641
|
+
const a = view.getUint8(off + 3);
|
|
4642
|
+
if (pixelFormat === PIXELFORMAT_32BPP_PARGB && a > 0 && a < 255) {
|
|
4643
|
+
r = Math.min(255, Math.round(r * 255 / a));
|
|
4644
|
+
g = Math.min(255, Math.round(g * 255 / a));
|
|
4645
|
+
b = Math.min(255, Math.round(b * 255 / a));
|
|
4646
|
+
}
|
|
4647
|
+
const di = dstRow + x * 4;
|
|
4648
|
+
rgba[di] = r;
|
|
4649
|
+
rgba[di + 1] = g;
|
|
4650
|
+
rgba[di + 2] = b;
|
|
4651
|
+
rgba[di + 3] = a;
|
|
3177
4652
|
}
|
|
3178
|
-
|
|
3179
|
-
|
|
4653
|
+
break;
|
|
4654
|
+
}
|
|
4655
|
+
case PIXELFORMAT_32BPP_RGB: {
|
|
4656
|
+
for (let x = 0; x < width; x++) {
|
|
4657
|
+
const off = rowOff + x * 4;
|
|
4658
|
+
if (off + 3 >= view.byteLength) {
|
|
4659
|
+
break;
|
|
4660
|
+
}
|
|
4661
|
+
const di = dstRow + x * 4;
|
|
4662
|
+
rgba[di] = view.getUint8(off + 2);
|
|
4663
|
+
rgba[di + 1] = view.getUint8(off + 1);
|
|
4664
|
+
rgba[di + 2] = view.getUint8(off);
|
|
4665
|
+
rgba[di + 3] = 255;
|
|
3180
4666
|
}
|
|
3181
|
-
|
|
4667
|
+
break;
|
|
3182
4668
|
}
|
|
3183
|
-
|
|
3184
|
-
|
|
3185
|
-
|
|
3186
|
-
|
|
3187
|
-
|
|
3188
|
-
const count = view.getUint32(dataOff + 4, true);
|
|
3189
|
-
const compressed = (recFlags & 16384) !== 0;
|
|
3190
|
-
const ptSize = compressed ? 4 : 8;
|
|
3191
|
-
ctx.fillStyle = resolveBrushPaint(rCtx, recFlags, brushVal);
|
|
3192
|
-
applyPlusWorldTransform(rCtx);
|
|
3193
|
-
ctx.beginPath();
|
|
3194
|
-
let pOff = dataOff + 8;
|
|
3195
|
-
for (let i = 0; i < count && pOff + ptSize <= dataOff + recDataSize; i++) {
|
|
3196
|
-
const pt = readPointFromView(view, pOff, compressed);
|
|
3197
|
-
if (i === 0) {
|
|
3198
|
-
ctx.moveTo(pt.x, pt.y);
|
|
3199
|
-
} else {
|
|
3200
|
-
ctx.lineTo(pt.x, pt.y);
|
|
4669
|
+
case PIXELFORMAT_24BPP_RGB: {
|
|
4670
|
+
for (let x = 0; x < width; x++) {
|
|
4671
|
+
const off = rowOff + x * 3;
|
|
4672
|
+
if (off + 2 >= view.byteLength) {
|
|
4673
|
+
break;
|
|
3201
4674
|
}
|
|
3202
|
-
|
|
4675
|
+
const di = dstRow + x * 4;
|
|
4676
|
+
rgba[di] = view.getUint8(off + 2);
|
|
4677
|
+
rgba[di + 1] = view.getUint8(off + 1);
|
|
4678
|
+
rgba[di + 2] = view.getUint8(off);
|
|
4679
|
+
rgba[di + 3] = 255;
|
|
3203
4680
|
}
|
|
3204
|
-
|
|
3205
|
-
ctx.fill();
|
|
4681
|
+
break;
|
|
3206
4682
|
}
|
|
3207
|
-
|
|
4683
|
+
default:
|
|
4684
|
+
return null;
|
|
3208
4685
|
}
|
|
3209
|
-
default:
|
|
3210
|
-
return false;
|
|
3211
4686
|
}
|
|
4687
|
+
return rgba;
|
|
4688
|
+
}
|
|
4689
|
+
function wrapBmpFile(bmpData, bmpRowStride, width, height, pixelDataSize) {
|
|
4690
|
+
const fileHeaderSize = 14;
|
|
4691
|
+
const dibHeaderSize = 108;
|
|
4692
|
+
const fileSize = fileHeaderSize + dibHeaderSize + pixelDataSize;
|
|
4693
|
+
const bmpFile = new ArrayBuffer(fileSize);
|
|
4694
|
+
const bmpView = new DataView(bmpFile);
|
|
4695
|
+
const bmpBytes = new Uint8Array(bmpFile);
|
|
4696
|
+
bmpView.setUint8(0, 66);
|
|
4697
|
+
bmpView.setUint8(1, 77);
|
|
4698
|
+
bmpView.setUint32(2, fileSize, true);
|
|
4699
|
+
bmpView.setUint32(6, 0, true);
|
|
4700
|
+
bmpView.setUint32(10, fileHeaderSize + dibHeaderSize, true);
|
|
4701
|
+
bmpView.setUint32(14, dibHeaderSize, true);
|
|
4702
|
+
bmpView.setInt32(18, width, true);
|
|
4703
|
+
bmpView.setInt32(22, height, true);
|
|
4704
|
+
bmpView.setUint16(26, 1, true);
|
|
4705
|
+
bmpView.setUint16(28, 32, true);
|
|
4706
|
+
bmpView.setUint32(30, 3, true);
|
|
4707
|
+
bmpView.setUint32(34, pixelDataSize, true);
|
|
4708
|
+
bmpView.setInt32(38, 2835, true);
|
|
4709
|
+
bmpView.setInt32(42, 2835, true);
|
|
4710
|
+
bmpView.setUint32(46, 0, true);
|
|
4711
|
+
bmpView.setUint32(50, 0, true);
|
|
4712
|
+
bmpView.setUint32(54, 16711680, true);
|
|
4713
|
+
bmpView.setUint32(58, 65280, true);
|
|
4714
|
+
bmpView.setUint32(62, 255, true);
|
|
4715
|
+
bmpView.setUint32(66, 4278190080, true);
|
|
4716
|
+
bmpView.setUint32(70, 1934772034, true);
|
|
4717
|
+
bmpBytes.set(bmpData, fileHeaderSize + dibHeaderSize);
|
|
4718
|
+
return bmpFile;
|
|
3212
4719
|
}
|
|
3213
4720
|
|
|
3214
4721
|
// src/emf-plus-brush-parser.ts
|
|
@@ -3216,6 +4723,7 @@ var BRUSH_DATA_PATH = 1;
|
|
|
3216
4723
|
var BRUSH_DATA_TRANSFORM = 2;
|
|
3217
4724
|
var BRUSH_DATA_PRESET_COLORS = 4;
|
|
3218
4725
|
var BRUSH_DATA_BLEND_FACTORS_H = 8;
|
|
4726
|
+
var BRUSH_DATA_FOCUS_SCALES = 64;
|
|
3219
4727
|
var MAX_GRADIENT_ELEMENTS = 4096;
|
|
3220
4728
|
function looksLikeGraphicsVersion(v) {
|
|
3221
4729
|
return v >>> 12 === 900097;
|
|
@@ -3236,8 +4744,22 @@ function applyMatrix(m, x, y) {
|
|
|
3236
4744
|
function clamp01(v) {
|
|
3237
4745
|
return Number.isFinite(v) ? Math.min(1, Math.max(0, v)) : 0;
|
|
3238
4746
|
}
|
|
4747
|
+
function readWrapMode(raw) {
|
|
4748
|
+
switch (raw) {
|
|
4749
|
+
case 0:
|
|
4750
|
+
return "tile";
|
|
4751
|
+
case 1:
|
|
4752
|
+
return "tile-flip-x";
|
|
4753
|
+
case 2:
|
|
4754
|
+
return "tile-flip-y";
|
|
4755
|
+
case 3:
|
|
4756
|
+
return "tile-flip-xy";
|
|
4757
|
+
default:
|
|
4758
|
+
return "clamp";
|
|
4759
|
+
}
|
|
4760
|
+
}
|
|
3239
4761
|
function normaliseStops(stops) {
|
|
3240
|
-
return stops.map((s) => ({ offset: clamp01(s.offset)
|
|
4762
|
+
return stops.map((s) => ({ ...s, offset: clamp01(s.offset) })).sort((a, b) => a.offset - b.offset);
|
|
3241
4763
|
}
|
|
3242
4764
|
function readPresetColors(view, off, end) {
|
|
3243
4765
|
if (off + 4 > end) {
|
|
@@ -3255,9 +4777,11 @@ function readPresetColors(view, off, end) {
|
|
|
3255
4777
|
}
|
|
3256
4778
|
const stops = [];
|
|
3257
4779
|
for (let i = 0; i < count; i++) {
|
|
4780
|
+
const argb = view.getUint32(colOff + i * 4, true);
|
|
3258
4781
|
stops.push({
|
|
3259
4782
|
offset: view.getFloat32(posOff + i * 4, true),
|
|
3260
|
-
color: argbToRgba(
|
|
4783
|
+
color: argbToRgba(argb),
|
|
4784
|
+
argb
|
|
3261
4785
|
});
|
|
3262
4786
|
}
|
|
3263
4787
|
return { stops, next };
|
|
@@ -3290,6 +4814,7 @@ function parseLinearGradient(view, b, end) {
|
|
|
3290
4814
|
return null;
|
|
3291
4815
|
}
|
|
3292
4816
|
const flags = view.getUint32(b, true);
|
|
4817
|
+
const wrapMode = readWrapMode(view.getUint32(b + 4, true));
|
|
3293
4818
|
const rx = view.getFloat32(b + 8, true);
|
|
3294
4819
|
const ry = view.getFloat32(b + 12, true);
|
|
3295
4820
|
const rw = view.getFloat32(b + 16, true);
|
|
@@ -3303,8 +4828,8 @@ function parseLinearGradient(view, b, end) {
|
|
|
3303
4828
|
o += 24;
|
|
3304
4829
|
}
|
|
3305
4830
|
let stops = [
|
|
3306
|
-
{ offset: 0, color: argbToRgba(startArgb) },
|
|
3307
|
-
{ offset: 1, color: argbToRgba(endArgb) }
|
|
4831
|
+
{ offset: 0, color: argbToRgba(startArgb), argb: startArgb },
|
|
4832
|
+
{ offset: 1, color: argbToRgba(endArgb), argb: endArgb }
|
|
3308
4833
|
];
|
|
3309
4834
|
if (flags & BRUSH_DATA_PRESET_COLORS) {
|
|
3310
4835
|
const preset = readPresetColors(view, o, end);
|
|
@@ -3316,7 +4841,8 @@ function parseLinearGradient(view, b, end) {
|
|
|
3316
4841
|
if (blend) {
|
|
3317
4842
|
stops = blend.entries.map((e) => ({
|
|
3318
4843
|
offset: e.pos,
|
|
3319
|
-
color: lerpArgbToRgba(startArgb, endArgb, e.factor)
|
|
4844
|
+
color: lerpArgbToRgba(startArgb, endArgb, e.factor),
|
|
4845
|
+
argb: lerpArgb2(startArgb, endArgb, e.factor)
|
|
3320
4846
|
}));
|
|
3321
4847
|
}
|
|
3322
4848
|
}
|
|
@@ -3338,15 +4864,75 @@ function parseLinearGradient(view, b, end) {
|
|
|
3338
4864
|
y1: p1.y,
|
|
3339
4865
|
x2: p2.x,
|
|
3340
4866
|
y2: p2.y,
|
|
3341
|
-
stops: normaliseStops(stops)
|
|
4867
|
+
stops: normaliseStops(stops),
|
|
4868
|
+
wrapMode,
|
|
4869
|
+
rect: { x: rx, y: ry, w: rw, h: rh },
|
|
4870
|
+
transform
|
|
3342
4871
|
}
|
|
3343
4872
|
};
|
|
3344
4873
|
}
|
|
4874
|
+
function decodeTextureImage(view, off, end) {
|
|
4875
|
+
if (off + 28 > end) {
|
|
4876
|
+
return null;
|
|
4877
|
+
}
|
|
4878
|
+
const imageDataType = view.getUint32(off + 4, true);
|
|
4879
|
+
if (imageDataType !== 1) {
|
|
4880
|
+
return null;
|
|
4881
|
+
}
|
|
4882
|
+
const width = view.getInt32(off + 8, true);
|
|
4883
|
+
const height = view.getInt32(off + 12, true);
|
|
4884
|
+
const stride = view.getInt32(off + 16, true);
|
|
4885
|
+
const pixelFormat = view.getUint32(off + 20, true);
|
|
4886
|
+
const pixelStart = off + 28;
|
|
4887
|
+
const absStride = Math.abs(stride);
|
|
4888
|
+
if (width <= 0 || height <= 0 || width > 8192 || height > 8192 || pixelStart + absStride * height > end) {
|
|
4889
|
+
return null;
|
|
4890
|
+
}
|
|
4891
|
+
const rgba = decodeEmfPlusBitmapPixelsToRgba(view, pixelStart, width, height, stride, pixelFormat);
|
|
4892
|
+
return rgba ? { width, height, rgba } : null;
|
|
4893
|
+
}
|
|
4894
|
+
function parseTextureBrush(view, b, end) {
|
|
4895
|
+
if (b + 8 > end) {
|
|
4896
|
+
return null;
|
|
4897
|
+
}
|
|
4898
|
+
const flags = view.getUint32(b, true);
|
|
4899
|
+
const wrapMode = readWrapMode(view.getUint32(b + 4, true));
|
|
4900
|
+
let o = b + 8;
|
|
4901
|
+
let transform = null;
|
|
4902
|
+
if (flags & BRUSH_DATA_TRANSFORM && o + 24 <= end) {
|
|
4903
|
+
transform = readTransform(view, o);
|
|
4904
|
+
o += 24;
|
|
4905
|
+
}
|
|
4906
|
+
const image = decodeTextureImage(view, o, end);
|
|
4907
|
+
if (!image) {
|
|
4908
|
+
return null;
|
|
4909
|
+
}
|
|
4910
|
+
const texture = {
|
|
4911
|
+
width: image.width,
|
|
4912
|
+
height: image.height,
|
|
4913
|
+
rgba: image.rgba,
|
|
4914
|
+
wrapMode,
|
|
4915
|
+
transform
|
|
4916
|
+
};
|
|
4917
|
+
let r = 0;
|
|
4918
|
+
let g = 0;
|
|
4919
|
+
let bl = 0;
|
|
4920
|
+
const n = image.width * image.height;
|
|
4921
|
+
for (let i = 0; i < n; i++) {
|
|
4922
|
+
r += image.rgba[i * 4];
|
|
4923
|
+
g += image.rgba[i * 4 + 1];
|
|
4924
|
+
bl += image.rgba[i * 4 + 2];
|
|
4925
|
+
}
|
|
4926
|
+
const avg = n > 0 ? `rgba(${Math.round(r / n)},${Math.round(g / n)},${Math.round(bl / n)},1)` : "rgba(128,128,128,1)";
|
|
4927
|
+
emfLog(`parseEmfPlusBrushObject: texture fill ${image.width}x${image.height}, wrapMode=${wrapMode}`);
|
|
4928
|
+
return { kind: "plus-brush", color: avg, texture };
|
|
4929
|
+
}
|
|
3345
4930
|
function parsePathGradient(view, b, end) {
|
|
3346
4931
|
if (b + 24 > end) {
|
|
3347
4932
|
return null;
|
|
3348
4933
|
}
|
|
3349
4934
|
const flags = view.getUint32(b, true);
|
|
4935
|
+
const wrapMode = readWrapMode(view.getUint32(b + 4, true));
|
|
3350
4936
|
const centerArgb = view.getUint32(b + 8, true);
|
|
3351
4937
|
let cx = view.getFloat32(b + 12, true);
|
|
3352
4938
|
let cy = view.getFloat32(b + 16, true);
|
|
@@ -3361,6 +4947,8 @@ function parsePathGradient(view, b, end) {
|
|
|
3361
4947
|
o += 4;
|
|
3362
4948
|
}
|
|
3363
4949
|
let boundaryPts = [];
|
|
4950
|
+
let boundaryArgb = [];
|
|
4951
|
+
const surroundAt = (k) => surround.length > 0 ? surround[Math.min(k, surround.length - 1)] : centerArgb;
|
|
3364
4952
|
if (flags & BRUSH_DATA_PATH) {
|
|
3365
4953
|
if (o + 4 <= end) {
|
|
3366
4954
|
const pathSize = view.getInt32(o, true);
|
|
@@ -3368,7 +4956,9 @@ function parsePathGradient(view, b, end) {
|
|
|
3368
4956
|
if (pathSize > 0 && o + pathSize <= end) {
|
|
3369
4957
|
const path = parseEmfPlusPath(view, o, pathSize);
|
|
3370
4958
|
if (path) {
|
|
3371
|
-
|
|
4959
|
+
const flat = flattenFirstFigure(path.points, path.types, surroundAt);
|
|
4960
|
+
boundaryPts = flat.points;
|
|
4961
|
+
boundaryArgb = flat.argb;
|
|
3372
4962
|
}
|
|
3373
4963
|
o += pathSize;
|
|
3374
4964
|
}
|
|
@@ -3382,6 +4972,7 @@ function parsePathGradient(view, b, end) {
|
|
|
3382
4972
|
x: view.getFloat32(o + i * 8, true),
|
|
3383
4973
|
y: view.getFloat32(o + i * 8 + 4, true)
|
|
3384
4974
|
});
|
|
4975
|
+
boundaryArgb.push(surroundAt(i));
|
|
3385
4976
|
}
|
|
3386
4977
|
o += ptCount * 8;
|
|
3387
4978
|
}
|
|
@@ -3391,31 +4982,50 @@ function parsePathGradient(view, b, end) {
|
|
|
3391
4982
|
transform = readTransform(view, o);
|
|
3392
4983
|
o += 24;
|
|
3393
4984
|
}
|
|
3394
|
-
|
|
3395
|
-
|
|
3396
|
-
|
|
4985
|
+
const center = { x: cx, y: cy };
|
|
4986
|
+
const m = transform;
|
|
4987
|
+
const worldBoundary = m ? boundaryPts.map((p) => applyMatrix(m, p.x, p.y)) : boundaryPts;
|
|
4988
|
+
if (m) {
|
|
4989
|
+
({ x: cx, y: cy } = applyMatrix(m, cx, cy));
|
|
3397
4990
|
}
|
|
3398
4991
|
const surroundArgb = surround.length > 0 ? surround[0] : centerArgb;
|
|
3399
4992
|
let stops = [
|
|
3400
|
-
{ offset: 0, color: argbToRgba(centerArgb) },
|
|
3401
|
-
{ offset: 1, color: argbToRgba(surroundArgb) }
|
|
4993
|
+
{ offset: 0, color: argbToRgba(centerArgb), argb: centerArgb },
|
|
4994
|
+
{ offset: 1, color: argbToRgba(surroundArgb), argb: surroundArgb }
|
|
3402
4995
|
];
|
|
4996
|
+
let blend = null;
|
|
4997
|
+
let preset = null;
|
|
3403
4998
|
if (flags & BRUSH_DATA_PRESET_COLORS) {
|
|
3404
|
-
const
|
|
3405
|
-
if (
|
|
3406
|
-
stops =
|
|
4999
|
+
const presetData = readPresetColors(view, o, end);
|
|
5000
|
+
if (presetData) {
|
|
5001
|
+
stops = presetData.stops.map((s) => ({ ...s, offset: 1 - s.offset }));
|
|
5002
|
+
preset = {
|
|
5003
|
+
positions: presetData.stops.map((s) => s.offset),
|
|
5004
|
+
argb: presetData.stops.map((s) => s.argb ?? 0)
|
|
5005
|
+
};
|
|
5006
|
+
o = presetData.next;
|
|
3407
5007
|
}
|
|
3408
5008
|
} else if (flags & BRUSH_DATA_BLEND_FACTORS_H) {
|
|
3409
|
-
const
|
|
3410
|
-
if (
|
|
3411
|
-
stops =
|
|
5009
|
+
const blendData = readBlendFactors(view, o, end);
|
|
5010
|
+
if (blendData) {
|
|
5011
|
+
stops = blendData.entries.map((e) => ({
|
|
3412
5012
|
offset: 1 - e.pos,
|
|
3413
|
-
color: lerpArgbToRgba(surroundArgb, centerArgb, e.factor)
|
|
5013
|
+
color: lerpArgbToRgba(surroundArgb, centerArgb, e.factor),
|
|
5014
|
+
argb: lerpArgb2(surroundArgb, centerArgb, e.factor)
|
|
3414
5015
|
}));
|
|
5016
|
+
blend = {
|
|
5017
|
+
positions: blendData.entries.map((e) => e.pos),
|
|
5018
|
+
factors: blendData.entries.map((e) => e.factor)
|
|
5019
|
+
};
|
|
5020
|
+
o = blendData.next;
|
|
3415
5021
|
}
|
|
3416
5022
|
}
|
|
5023
|
+
let focus = null;
|
|
5024
|
+
if (flags & BRUSH_DATA_FOCUS_SCALES && o + 12 <= end && view.getUint32(o, true) === 2) {
|
|
5025
|
+
focus = { x: view.getFloat32(o + 4, true), y: view.getFloat32(o + 8, true) };
|
|
5026
|
+
}
|
|
3417
5027
|
let r = 0;
|
|
3418
|
-
for (const p of
|
|
5028
|
+
for (const p of worldBoundary) {
|
|
3419
5029
|
const d = Math.hypot(p.x - cx, p.y - cy);
|
|
3420
5030
|
if (d > r) {
|
|
3421
5031
|
r = d;
|
|
@@ -3425,14 +5035,85 @@ function parsePathGradient(view, b, end) {
|
|
|
3425
5035
|
return { kind: "plus-brush", color: argbToRgba(centerArgb) };
|
|
3426
5036
|
}
|
|
3427
5037
|
emfLog(
|
|
3428
|
-
`parseEmfPlusBrushObject: path gradient centre=(${cx.toFixed(1)},${cy.toFixed(1)}),
|
|
5038
|
+
`parseEmfPlusBrushObject: path gradient centre=(${cx.toFixed(1)},${cy.toFixed(1)}), ${boundaryPts.length} boundary point(s)`
|
|
3429
5039
|
);
|
|
3430
5040
|
return {
|
|
3431
5041
|
kind: "plus-brush",
|
|
3432
5042
|
color: argbToRgba(centerArgb),
|
|
3433
|
-
gradient: {
|
|
5043
|
+
gradient: {
|
|
5044
|
+
type: "radial",
|
|
5045
|
+
cx,
|
|
5046
|
+
cy,
|
|
5047
|
+
r,
|
|
5048
|
+
stops: normaliseStops(stops),
|
|
5049
|
+
wrapMode,
|
|
5050
|
+
shape: {
|
|
5051
|
+
center,
|
|
5052
|
+
centerArgb,
|
|
5053
|
+
boundary: boundaryPts,
|
|
5054
|
+
boundaryArgb,
|
|
5055
|
+
blend,
|
|
5056
|
+
preset,
|
|
5057
|
+
focus,
|
|
5058
|
+
transform
|
|
5059
|
+
}
|
|
5060
|
+
}
|
|
3434
5061
|
};
|
|
3435
5062
|
}
|
|
5063
|
+
function lerpArgb2(a, b, t) {
|
|
5064
|
+
const tc = Math.min(1, Math.max(0, t));
|
|
5065
|
+
let out = 0;
|
|
5066
|
+
for (let shift = 24; shift >= 0; shift -= 8) {
|
|
5067
|
+
const ca = a >>> shift & 255;
|
|
5068
|
+
const cb = b >>> shift & 255;
|
|
5069
|
+
out = out * 256 + Math.round(ca + (cb - ca) * tc);
|
|
5070
|
+
}
|
|
5071
|
+
return out >>> 0;
|
|
5072
|
+
}
|
|
5073
|
+
var BEZIER_STEPS = 24;
|
|
5074
|
+
function flattenFirstFigure(points, types, colorAt) {
|
|
5075
|
+
const outPts = [];
|
|
5076
|
+
const outArgb = [];
|
|
5077
|
+
for (let k = 0; k < points.length; k++) {
|
|
5078
|
+
const kind = types[k] & 7;
|
|
5079
|
+
if (k > 0 && kind === 0) {
|
|
5080
|
+
break;
|
|
5081
|
+
}
|
|
5082
|
+
if (kind === 3 && k > 0 && k + 2 < points.length) {
|
|
5083
|
+
const p0 = points[k - 1];
|
|
5084
|
+
const p1 = points[k];
|
|
5085
|
+
const p2 = points[k + 1];
|
|
5086
|
+
const p3 = points[k + 2];
|
|
5087
|
+
const c0 = colorAt(k - 1);
|
|
5088
|
+
const c3 = colorAt(k + 2);
|
|
5089
|
+
for (let i = 1; i <= BEZIER_STEPS; i++) {
|
|
5090
|
+
const t = i / BEZIER_STEPS;
|
|
5091
|
+
const u = 1 - t;
|
|
5092
|
+
outPts.push({
|
|
5093
|
+
x: u * u * u * p0.x + 3 * u * u * t * p1.x + 3 * u * t * t * p2.x + t * t * t * p3.x,
|
|
5094
|
+
y: u * u * u * p0.y + 3 * u * u * t * p1.y + 3 * u * t * t * p2.y + t * t * t * p3.y
|
|
5095
|
+
});
|
|
5096
|
+
outArgb.push(lerpArgb2(c0, c3, t));
|
|
5097
|
+
}
|
|
5098
|
+
k += 2;
|
|
5099
|
+
if (types[k] & 128) {
|
|
5100
|
+
break;
|
|
5101
|
+
}
|
|
5102
|
+
continue;
|
|
5103
|
+
}
|
|
5104
|
+
outPts.push(points[k]);
|
|
5105
|
+
outArgb.push(colorAt(k));
|
|
5106
|
+
if (types[k] & 128) {
|
|
5107
|
+
break;
|
|
5108
|
+
}
|
|
5109
|
+
}
|
|
5110
|
+
const n = outPts.length;
|
|
5111
|
+
if (n > 1 && outPts[0].x === outPts[n - 1].x && outPts[0].y === outPts[n - 1].y) {
|
|
5112
|
+
outPts.pop();
|
|
5113
|
+
outArgb.pop();
|
|
5114
|
+
}
|
|
5115
|
+
return { points: outPts, argb: outArgb };
|
|
5116
|
+
}
|
|
3436
5117
|
function parseEmfPlusBrushObject(view, dataOff, recDataSize) {
|
|
3437
5118
|
if (recDataSize < 8) {
|
|
3438
5119
|
return null;
|
|
@@ -3453,6 +5134,10 @@ function parseEmfPlusBrushObject(view, dataOff, recDataSize) {
|
|
|
3453
5134
|
return { kind: "plus-brush", color: argbToRgba(view.getUint32(b + 4, true)) };
|
|
3454
5135
|
}
|
|
3455
5136
|
return { kind: "plus-brush", color: "rgba(0,0,0,1)" };
|
|
5137
|
+
case EMFPLUS_BRUSHTYPE_TEXTUREFILL: {
|
|
5138
|
+
const brush = parseTextureBrush(view, b, end);
|
|
5139
|
+
return brush ?? { kind: "plus-brush", color: "rgba(0,0,0,1)" };
|
|
5140
|
+
}
|
|
3456
5141
|
case EMFPLUS_BRUSHTYPE_LINEARGRADIENT: {
|
|
3457
5142
|
const brush = parseLinearGradient(view, b, end);
|
|
3458
5143
|
return brush ?? { kind: "plus-brush", color: "rgba(0,0,0,1)" };
|
|
@@ -3466,110 +5151,6 @@ function parseEmfPlusBrushObject(view, dataOff, recDataSize) {
|
|
|
3466
5151
|
}
|
|
3467
5152
|
}
|
|
3468
5153
|
|
|
3469
|
-
// src/emf-plus-bitmap-decoder.ts
|
|
3470
|
-
var PIXELFORMAT_24BPP_RGB = 137224;
|
|
3471
|
-
var PIXELFORMAT_32BPP_RGB = 139273;
|
|
3472
|
-
var PIXELFORMAT_32BPP_ARGB = 2498570;
|
|
3473
|
-
var PIXELFORMAT_32BPP_PARGB = 925707;
|
|
3474
|
-
function decodeEmfPlusBitmapPixels(view, pixelStart, width, height, stride, pixelFormat) {
|
|
3475
|
-
const absStride = Math.abs(stride);
|
|
3476
|
-
const topDown = stride > 0;
|
|
3477
|
-
const rowBytes = width * 4;
|
|
3478
|
-
const bmpRowStride = rowBytes + 3 & -4;
|
|
3479
|
-
const pixelDataSize = bmpRowStride * height;
|
|
3480
|
-
const bmpData = new Uint8Array(pixelDataSize);
|
|
3481
|
-
for (let y = 0; y < height; y++) {
|
|
3482
|
-
const srcRow = topDown ? y : height - 1 - y;
|
|
3483
|
-
const rowOff = pixelStart + srcRow * absStride;
|
|
3484
|
-
const dstRow = (height - 1 - y) * bmpRowStride;
|
|
3485
|
-
switch (pixelFormat) {
|
|
3486
|
-
case PIXELFORMAT_32BPP_ARGB:
|
|
3487
|
-
case PIXELFORMAT_32BPP_PARGB: {
|
|
3488
|
-
for (let x = 0; x < width; x++) {
|
|
3489
|
-
const off = rowOff + x * 4;
|
|
3490
|
-
if (off + 3 >= view.byteLength) {
|
|
3491
|
-
break;
|
|
3492
|
-
}
|
|
3493
|
-
let b = view.getUint8(off);
|
|
3494
|
-
let g = view.getUint8(off + 1);
|
|
3495
|
-
let r = view.getUint8(off + 2);
|
|
3496
|
-
const a = view.getUint8(off + 3);
|
|
3497
|
-
if (pixelFormat === PIXELFORMAT_32BPP_PARGB && a > 0 && a < 255) {
|
|
3498
|
-
r = Math.min(255, Math.round(r * 255 / a));
|
|
3499
|
-
g = Math.min(255, Math.round(g * 255 / a));
|
|
3500
|
-
b = Math.min(255, Math.round(b * 255 / a));
|
|
3501
|
-
}
|
|
3502
|
-
const di = dstRow + x * 4;
|
|
3503
|
-
bmpData[di] = b;
|
|
3504
|
-
bmpData[di + 1] = g;
|
|
3505
|
-
bmpData[di + 2] = r;
|
|
3506
|
-
bmpData[di + 3] = a;
|
|
3507
|
-
}
|
|
3508
|
-
break;
|
|
3509
|
-
}
|
|
3510
|
-
case PIXELFORMAT_32BPP_RGB: {
|
|
3511
|
-
for (let x = 0; x < width; x++) {
|
|
3512
|
-
const off = rowOff + x * 4;
|
|
3513
|
-
if (off + 3 >= view.byteLength) {
|
|
3514
|
-
break;
|
|
3515
|
-
}
|
|
3516
|
-
const di = dstRow + x * 4;
|
|
3517
|
-
bmpData[di] = view.getUint8(off);
|
|
3518
|
-
bmpData[di + 1] = view.getUint8(off + 1);
|
|
3519
|
-
bmpData[di + 2] = view.getUint8(off + 2);
|
|
3520
|
-
bmpData[di + 3] = 255;
|
|
3521
|
-
}
|
|
3522
|
-
break;
|
|
3523
|
-
}
|
|
3524
|
-
case PIXELFORMAT_24BPP_RGB: {
|
|
3525
|
-
for (let x = 0; x < width; x++) {
|
|
3526
|
-
const off = rowOff + x * 3;
|
|
3527
|
-
if (off + 2 >= view.byteLength) {
|
|
3528
|
-
break;
|
|
3529
|
-
}
|
|
3530
|
-
const di = dstRow + x * 4;
|
|
3531
|
-
bmpData[di] = view.getUint8(off);
|
|
3532
|
-
bmpData[di + 1] = view.getUint8(off + 1);
|
|
3533
|
-
bmpData[di + 2] = view.getUint8(off + 2);
|
|
3534
|
-
bmpData[di + 3] = 255;
|
|
3535
|
-
}
|
|
3536
|
-
break;
|
|
3537
|
-
}
|
|
3538
|
-
default:
|
|
3539
|
-
return null;
|
|
3540
|
-
}
|
|
3541
|
-
}
|
|
3542
|
-
const fileHeaderSize = 14;
|
|
3543
|
-
const dibHeaderSize = 108;
|
|
3544
|
-
const fileSize = fileHeaderSize + dibHeaderSize + pixelDataSize;
|
|
3545
|
-
const bmpFile = new ArrayBuffer(fileSize);
|
|
3546
|
-
const bmpView = new DataView(bmpFile);
|
|
3547
|
-
const bmpBytes = new Uint8Array(bmpFile);
|
|
3548
|
-
bmpView.setUint8(0, 66);
|
|
3549
|
-
bmpView.setUint8(1, 77);
|
|
3550
|
-
bmpView.setUint32(2, fileSize, true);
|
|
3551
|
-
bmpView.setUint32(6, 0, true);
|
|
3552
|
-
bmpView.setUint32(10, fileHeaderSize + dibHeaderSize, true);
|
|
3553
|
-
bmpView.setUint32(14, dibHeaderSize, true);
|
|
3554
|
-
bmpView.setInt32(18, width, true);
|
|
3555
|
-
bmpView.setInt32(22, height, true);
|
|
3556
|
-
bmpView.setUint16(26, 1, true);
|
|
3557
|
-
bmpView.setUint16(28, 32, true);
|
|
3558
|
-
bmpView.setUint32(30, 3, true);
|
|
3559
|
-
bmpView.setUint32(34, pixelDataSize, true);
|
|
3560
|
-
bmpView.setInt32(38, 2835, true);
|
|
3561
|
-
bmpView.setInt32(42, 2835, true);
|
|
3562
|
-
bmpView.setUint32(46, 0, true);
|
|
3563
|
-
bmpView.setUint32(50, 0, true);
|
|
3564
|
-
bmpView.setUint32(54, 16711680, true);
|
|
3565
|
-
bmpView.setUint32(58, 65280, true);
|
|
3566
|
-
bmpView.setUint32(62, 255, true);
|
|
3567
|
-
bmpView.setUint32(66, 4278190080, true);
|
|
3568
|
-
bmpView.setUint32(70, 1934772034, true);
|
|
3569
|
-
bmpBytes.set(bmpData, fileHeaderSize + dibHeaderSize);
|
|
3570
|
-
return bmpFile;
|
|
3571
|
-
}
|
|
3572
|
-
|
|
3573
5154
|
// src/emf-plus-object-complex.ts
|
|
3574
5155
|
function parseEmfPlusPenObject(view, dataOff, recDataSize) {
|
|
3575
5156
|
if (recDataSize < 20) {
|
|
@@ -4453,7 +6034,7 @@ function replayEmfRecords(view, ctx, bounds, canvasW, canvasH, dpiScale = 1, rep
|
|
|
4453
6034
|
);
|
|
4454
6035
|
break;
|
|
4455
6036
|
}
|
|
4456
|
-
if (recType ===
|
|
6037
|
+
if (recType === EMR_SETMETARGN || recType === EMR_SETICMMODE || recType === EMR_SETLAYOUT || recType === EMR_HEADER) {
|
|
4457
6038
|
offset += recSize;
|
|
4458
6039
|
continue;
|
|
4459
6040
|
}
|
|
@@ -4848,8 +6429,8 @@ function replayWmfRecords(view, ctx, header, canvasW, canvasH, replayOptions = {
|
|
|
4848
6429
|
case META_CREATEFONTINDIRECT:
|
|
4849
6430
|
if (recSize >= 24) {
|
|
4850
6431
|
let family = "";
|
|
4851
|
-
for (let i = 0; i < 32 && dataOff +
|
|
4852
|
-
const ch = view.getUint8(dataOff +
|
|
6432
|
+
for (let i = 0; i < 32 && dataOff + 18 + i < offset + recSize; i++) {
|
|
6433
|
+
const ch = view.getUint8(dataOff + 18 + i);
|
|
4853
6434
|
if (ch === 0) {
|
|
4854
6435
|
break;
|
|
4855
6436
|
}
|
|
@@ -4858,12 +6439,15 @@ function replayWmfRecords(view, ctx, header, canvasW, canvasH, replayOptions = {
|
|
|
4858
6439
|
const slot = allocObjectSlot();
|
|
4859
6440
|
objectTable.set(slot, {
|
|
4860
6441
|
kind: "font",
|
|
4861
|
-
|
|
6442
|
+
// Sign kept (not Math.abs'd): resolveFontPixelHeight() needs it
|
|
6443
|
+
// to distinguish GDI's cell-height vs character-height convention.
|
|
6444
|
+
height: view.getInt16(dataOff, true),
|
|
4862
6445
|
weight: view.getInt16(dataOff + 8, true),
|
|
4863
6446
|
italic: view.getUint8(dataOff + 10) !== 0,
|
|
4864
6447
|
underline: view.getUint8(dataOff + 11) !== 0,
|
|
4865
6448
|
strikeOut: view.getUint8(dataOff + 12) !== 0,
|
|
4866
|
-
family: family || "sans-serif"
|
|
6449
|
+
family: family || "sans-serif",
|
|
6450
|
+
escapementTenthDeg: view.getInt16(dataOff + 4, true)
|
|
4867
6451
|
});
|
|
4868
6452
|
}
|
|
4869
6453
|
break;
|
|
@@ -4888,6 +6472,7 @@ function replayWmfRecords(view, ctx, header, canvasW, canvasH, replayOptions = {
|
|
|
4888
6472
|
state.fontUnderline = obj.underline;
|
|
4889
6473
|
state.fontStrikeOut = obj.strikeOut;
|
|
4890
6474
|
state.fontFamily = obj.family;
|
|
6475
|
+
state.fontEscapementTenthDeg = obj.escapementTenthDeg ?? 0;
|
|
4891
6476
|
break;
|
|
4892
6477
|
}
|
|
4893
6478
|
}
|