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