emf-converter 1.4.3 → 1.6.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 +40 -16
- package/dist/index.d.mts +17 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.js +1221 -424
- package/dist/index.mjs +1221 -424
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,6 +1,229 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
// src/emf-
|
|
3
|
+
// src/emf-color-helpers.ts
|
|
4
|
+
function colorRefToHex(r, g, b) {
|
|
5
|
+
const toHex = (v) => v.toString(16).padStart(2, "0");
|
|
6
|
+
return `#${toHex(r & 255)}${toHex(g & 255)}${toHex(b & 255)}`;
|
|
7
|
+
}
|
|
8
|
+
function readColorRef(view, offset) {
|
|
9
|
+
const r = view.getUint8(offset);
|
|
10
|
+
const g = view.getUint8(offset + 1);
|
|
11
|
+
const b = view.getUint8(offset + 2);
|
|
12
|
+
return colorRefToHex(r, g, b);
|
|
13
|
+
}
|
|
14
|
+
function argbToRgba(argb) {
|
|
15
|
+
const a = (argb >>> 24 & 255) / 255;
|
|
16
|
+
const r = argb >>> 16 & 255;
|
|
17
|
+
const g = argb >>> 8 & 255;
|
|
18
|
+
const b = argb & 255;
|
|
19
|
+
return `rgba(${r},${g},${b},${a.toFixed(3)})`;
|
|
20
|
+
}
|
|
21
|
+
function lerpArgbToRgba(argbA, argbB, t) {
|
|
22
|
+
const tc = Math.min(1, Math.max(0, t));
|
|
23
|
+
const mix = (a2, b2) => Math.round(a2 + (b2 - a2) * tc);
|
|
24
|
+
const aA = argbA >>> 24 & 255;
|
|
25
|
+
const aB = argbB >>> 24 & 255;
|
|
26
|
+
const r = mix(argbA >>> 16 & 255, argbB >>> 16 & 255);
|
|
27
|
+
const g = mix(argbA >>> 8 & 255, argbB >>> 8 & 255);
|
|
28
|
+
const b = mix(argbA & 255, argbB & 255);
|
|
29
|
+
const a = mix(aA, aB) / 255;
|
|
30
|
+
return `rgba(${r},${g},${b},${a.toFixed(3)})`;
|
|
31
|
+
}
|
|
32
|
+
function invertCssColor(color) {
|
|
33
|
+
const hex = /^#([0-9a-f]{6})$/i.exec(color);
|
|
34
|
+
if (hex) {
|
|
35
|
+
const v = parseInt(hex[1], 16);
|
|
36
|
+
const inv = 16777215 ^ v;
|
|
37
|
+
return `#${inv.toString(16).padStart(6, "0")}`;
|
|
38
|
+
}
|
|
39
|
+
const shortHex = /^#([0-9a-f]{3})$/i.exec(color);
|
|
40
|
+
if (shortHex) {
|
|
41
|
+
const [r, g, b] = shortHex[1].split("").map((c) => parseInt(c + c, 16));
|
|
42
|
+
const toHex = (v) => (255 - v).toString(16).padStart(2, "0");
|
|
43
|
+
return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
|
|
44
|
+
}
|
|
45
|
+
const rgba = /^rgba?\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(?:,\s*([\d.]+)\s*)?\)$/i.exec(color);
|
|
46
|
+
if (rgba) {
|
|
47
|
+
const r = 255 - Math.min(255, parseInt(rgba[1], 10));
|
|
48
|
+
const g = 255 - Math.min(255, parseInt(rgba[2], 10));
|
|
49
|
+
const b = 255 - Math.min(255, parseInt(rgba[3], 10));
|
|
50
|
+
return rgba[4] !== void 0 ? `rgba(${r},${g},${b},${rgba[4]})` : `rgb(${r},${g},${b})`;
|
|
51
|
+
}
|
|
52
|
+
return color;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// src/emf-constants.ts
|
|
56
|
+
var EMR_HEADER = 1;
|
|
57
|
+
var EMR_POLYBEZIER = 2;
|
|
58
|
+
var EMR_POLYGON = 3;
|
|
59
|
+
var EMR_POLYLINE = 4;
|
|
60
|
+
var EMR_POLYBEZIERTO = 5;
|
|
61
|
+
var EMR_POLYLINETO = 6;
|
|
62
|
+
var EMR_POLYPOLYLINE = 7;
|
|
63
|
+
var EMR_POLYPOLYGON = 8;
|
|
64
|
+
var EMR_SETWINDOWEXTEX = 9;
|
|
65
|
+
var EMR_SETWINDOWORGEX = 10;
|
|
66
|
+
var EMR_SETVIEWPORTEXTEX = 11;
|
|
67
|
+
var EMR_SETVIEWPORTORGEX = 12;
|
|
68
|
+
var EMR_SETBRUSHORGEX = 13;
|
|
69
|
+
var EMR_EOF = 14;
|
|
70
|
+
var EMR_SETPIXELV = 15;
|
|
71
|
+
var EMR_SETMAPMODE = 17;
|
|
72
|
+
var EMR_SETBKMODE = 18;
|
|
73
|
+
var EMR_SETPOLYFILLMODE = 19;
|
|
74
|
+
var EMR_SETROP2 = 20;
|
|
75
|
+
var EMR_SETSTRETCHBLTMODE = 21;
|
|
76
|
+
var R2_BLACK = 1;
|
|
77
|
+
var R2_NOTMERGEPEN = 2;
|
|
78
|
+
var R2_MASKNOTPEN = 3;
|
|
79
|
+
var R2_NOTCOPYPEN = 4;
|
|
80
|
+
var R2_MASKPENNOT = 5;
|
|
81
|
+
var R2_NOT = 6;
|
|
82
|
+
var R2_XORPEN = 7;
|
|
83
|
+
var R2_NOTMASKPEN = 8;
|
|
84
|
+
var R2_MASKPEN = 9;
|
|
85
|
+
var R2_NOTXORPEN = 10;
|
|
86
|
+
var R2_NOP = 11;
|
|
87
|
+
var R2_MERGENOTPEN = 12;
|
|
88
|
+
var R2_MERGEPENNOT = 14;
|
|
89
|
+
var R2_MERGEPEN = 15;
|
|
90
|
+
var R2_WHITE = 16;
|
|
91
|
+
var MAX_CANVAS_DIMENSION = 8192;
|
|
92
|
+
var MAX_RECORDS_DEFAULT = 2e5;
|
|
93
|
+
var MAX_RECORDS_EMFPLUS_DEFAULT = 5e5;
|
|
94
|
+
var EMR_SETTEXTALIGN = 22;
|
|
95
|
+
var EMR_SETTEXTCOLOR = 24;
|
|
96
|
+
var EMR_SETBKCOLOR = 25;
|
|
97
|
+
var EMR_OFFSETCLIPRGN = 26;
|
|
98
|
+
var EMR_MOVETOEX = 27;
|
|
99
|
+
var EMR_SETMETARGN = 28;
|
|
100
|
+
var EMR_EXCLUDECLIPRECT = 29;
|
|
101
|
+
var EMR_INTERSECTCLIPRECT = 30;
|
|
102
|
+
var EMR_SCALEVIEWPORTEXTEX = 31;
|
|
103
|
+
var EMR_SCALEWINDOWEXTEX = 32;
|
|
104
|
+
var EMR_SAVEDC = 33;
|
|
105
|
+
var EMR_RESTOREDC = 34;
|
|
106
|
+
var EMR_SETWORLDTRANSFORM = 35;
|
|
107
|
+
var EMR_MODIFYWORLDTRANSFORM = 36;
|
|
108
|
+
var EMR_SELECTOBJECT = 37;
|
|
109
|
+
var EMR_CREATEPEN = 38;
|
|
110
|
+
var EMR_CREATEBRUSHINDIRECT = 39;
|
|
111
|
+
var EMR_DELETEOBJECT = 40;
|
|
112
|
+
var EMR_ELLIPSE = 42;
|
|
113
|
+
var EMR_RECTANGLE = 43;
|
|
114
|
+
var EMR_ROUNDRECT = 44;
|
|
115
|
+
var EMR_ARC = 45;
|
|
116
|
+
var EMR_CHORD = 46;
|
|
117
|
+
var EMR_PIE = 47;
|
|
118
|
+
var EMR_LINETO = 54;
|
|
119
|
+
var EMR_ARCTO = 55;
|
|
120
|
+
var EMR_SETMITERLIMIT = 58;
|
|
121
|
+
var EMR_BEGINPATH = 59;
|
|
122
|
+
var EMR_ENDPATH = 60;
|
|
123
|
+
var EMR_CLOSEFIGURE = 61;
|
|
124
|
+
var EMR_FILLPATH = 62;
|
|
125
|
+
var EMR_STROKEANDFILLPATH = 63;
|
|
126
|
+
var EMR_STROKEPATH = 64;
|
|
127
|
+
var EMR_SELECTCLIPPATH = 67;
|
|
128
|
+
var EMR_COMMENT = 70;
|
|
129
|
+
var EMR_EXTSELECTCLIPRGN = 75;
|
|
130
|
+
var EMR_BITBLT = 76;
|
|
131
|
+
var EMR_STRETCHDIBITS = 81;
|
|
132
|
+
var EMR_EXTCREATEFONTINDIRECTW = 82;
|
|
133
|
+
var EMR_EXTTEXTOUTW = 84;
|
|
134
|
+
var EMR_POLYBEZIER16 = 85;
|
|
135
|
+
var EMR_POLYGON16 = 86;
|
|
136
|
+
var EMR_POLYLINE16 = 87;
|
|
137
|
+
var EMR_POLYBEZIERTO16 = 88;
|
|
138
|
+
var EMR_POLYLINETO16 = 89;
|
|
139
|
+
var EMR_POLYPOLYGON16 = 91;
|
|
140
|
+
var EMR_EXTCREATEPEN = 95;
|
|
141
|
+
var EMR_SETICMMODE = 98;
|
|
142
|
+
var EMR_SETLAYOUT = 115;
|
|
143
|
+
var STOCK_OBJECT_BASE = 2147483648;
|
|
144
|
+
var EMFPLUS_SIGNATURE = 726027589;
|
|
145
|
+
var EMR_COMMENT_PUBLIC_SIGNATURE = 1128875079;
|
|
146
|
+
var EMFPLUS_HEADER = 16385;
|
|
147
|
+
var EMFPLUS_ENDOFFILE = 16386;
|
|
148
|
+
var EMFPLUS_GETDC = 16388;
|
|
149
|
+
var EMFPLUS_OBJECT = 16392;
|
|
150
|
+
var EMFPLUS_FILLRECTS = 16394;
|
|
151
|
+
var EMFPLUS_DRAWRECTS = 16395;
|
|
152
|
+
var EMFPLUS_FILLPOLYGON = 16396;
|
|
153
|
+
var EMFPLUS_DRAWLINES = 16397;
|
|
154
|
+
var EMFPLUS_FILLELLIPSE = 16398;
|
|
155
|
+
var EMFPLUS_DRAWELLIPSE = 16399;
|
|
156
|
+
var EMFPLUS_FILLPIE = 16400;
|
|
157
|
+
var EMFPLUS_DRAWPIE = 16401;
|
|
158
|
+
var EMFPLUS_DRAWARC = 16402;
|
|
159
|
+
var EMFPLUS_FILLPATH = 16404;
|
|
160
|
+
var EMFPLUS_DRAWPATH = 16405;
|
|
161
|
+
var EMFPLUS_DRAWIMAGE = 16410;
|
|
162
|
+
var EMFPLUS_DRAWIMAGEPOINTS = 16411;
|
|
163
|
+
var EMFPLUS_DRAWSTRING = 16412;
|
|
164
|
+
var EMFPLUS_SETANTIALIASMODE = 16414;
|
|
165
|
+
var EMFPLUS_SETTEXTRENDERINGHINT = 16415;
|
|
166
|
+
var EMFPLUS_SETINTERPOLATIONMODE = 16417;
|
|
167
|
+
var EMFPLUS_SETPIXELOFFSETMODE = 16418;
|
|
168
|
+
var EMFPLUS_SETCOMPOSITINGQUALITY = 16420;
|
|
169
|
+
var EMFPLUS_SAVE = 16421;
|
|
170
|
+
var EMFPLUS_RESTORE = 16422;
|
|
171
|
+
var EMFPLUS_BEGINCONTAINERNOPARAMS = 16424;
|
|
172
|
+
var EMFPLUS_ENDCONTAINER = 16425;
|
|
173
|
+
var EMFPLUS_SETWORLDTRANSFORM = 16426;
|
|
174
|
+
var EMFPLUS_RESETWORLDTRANSFORM = 16427;
|
|
175
|
+
var EMFPLUS_MULTIPLYWORLDTRANSFORM = 16428;
|
|
176
|
+
var EMFPLUS_TRANSLATEWORLDTRANSFORM = 16429;
|
|
177
|
+
var EMFPLUS_SCALEWORLDTRANSFORM = 16430;
|
|
178
|
+
var EMFPLUS_ROTATEWORLDTRANSFORM = 16431;
|
|
179
|
+
var EMFPLUS_SETPAGETRANSFORM = 16432;
|
|
180
|
+
var EMFPLUS_RESETCLIP = 16433;
|
|
181
|
+
var EMFPLUS_SETCLIPRECT = 16434;
|
|
182
|
+
var EMFPLUS_SETCLIPPATH = 16435;
|
|
183
|
+
var EMFPLUS_SETCLIPREGION = 16436;
|
|
184
|
+
var EMFPLUS_DRAWDRIVERSTRING = 16438;
|
|
185
|
+
var EMFPLUS_OFFSETCLIP = 16437;
|
|
186
|
+
var EMFPLUS_OBJECTTYPE_BRUSH = 1;
|
|
187
|
+
var EMFPLUS_OBJECTTYPE_PEN = 2;
|
|
188
|
+
var EMFPLUS_OBJECTTYPE_PATH = 3;
|
|
189
|
+
var EMFPLUS_OBJECTTYPE_IMAGEATTRIBUTES = 4;
|
|
190
|
+
var EMFPLUS_OBJECTTYPE_IMAGE = 5;
|
|
191
|
+
var EMFPLUS_OBJECTTYPE_FONT = 6;
|
|
192
|
+
var EMFPLUS_OBJECTTYPE_STRINGFORMAT = 7;
|
|
193
|
+
var EMFPLUS_OBJECTTYPE_REGION = 8;
|
|
194
|
+
var EMFPLUS_BRUSHTYPE_SOLID = 0;
|
|
195
|
+
var EMFPLUS_BRUSHTYPE_HATCHFILL = 1;
|
|
196
|
+
var EMFPLUS_BRUSHTYPE_PATHGRADIENT = 3;
|
|
197
|
+
var EMFPLUS_BRUSHTYPE_LINEARGRADIENT = 4;
|
|
198
|
+
var META_EOF = 0;
|
|
199
|
+
var META_SETBKCOLOR = 513;
|
|
200
|
+
var META_SETBKMODE = 258;
|
|
201
|
+
var META_SETROP2 = 260;
|
|
202
|
+
var META_SETPOLYFILLMODE = 262;
|
|
203
|
+
var META_SETTEXTCOLOR = 521;
|
|
204
|
+
var META_SETTEXTALIGN = 302;
|
|
205
|
+
var META_SETWINDOWORG = 523;
|
|
206
|
+
var META_SETWINDOWEXT = 524;
|
|
207
|
+
var META_MOVETO = 532;
|
|
208
|
+
var META_LINETO = 531;
|
|
209
|
+
var META_RECTANGLE = 1051;
|
|
210
|
+
var META_ROUNDRECT = 1564;
|
|
211
|
+
var META_ELLIPSE = 1048;
|
|
212
|
+
var META_ARC = 2071;
|
|
213
|
+
var META_PIE = 2074;
|
|
214
|
+
var META_CHORD = 2096;
|
|
215
|
+
var META_POLYGON = 804;
|
|
216
|
+
var META_POLYLINE = 805;
|
|
217
|
+
var META_SELECTOBJECT = 301;
|
|
218
|
+
var META_DELETEOBJECT = 496;
|
|
219
|
+
var META_CREATEPENINDIRECT = 762;
|
|
220
|
+
var META_CREATEBRUSHINDIRECT = 764;
|
|
221
|
+
var META_CREATEFONTINDIRECT = 763;
|
|
222
|
+
var META_TEXTOUT = 1313;
|
|
223
|
+
var META_EXTTEXTOUT = 2610;
|
|
224
|
+
var META_SAVEDC = 30;
|
|
225
|
+
var META_RESTOREDC = 295;
|
|
226
|
+
var META_POLYPOLYGON = 1336;
|
|
4
227
|
var emfLog = (...args) => {
|
|
5
228
|
};
|
|
6
229
|
var emfWarn = (...args) => {
|
|
@@ -8,7 +231,7 @@ var emfWarn = (...args) => {
|
|
|
8
231
|
|
|
9
232
|
// src/emf-canvas-helpers.ts
|
|
10
233
|
var DEFAULT_DPI_SCALE = 1;
|
|
11
|
-
function createCanvas(width, height, maxWidth, maxHeight, dpiScale = DEFAULT_DPI_SCALE) {
|
|
234
|
+
function createCanvas(width, height, maxWidth, maxHeight, dpiScale = DEFAULT_DPI_SCALE, maxCanvasDimension = MAX_CANVAS_DIMENSION) {
|
|
12
235
|
const effectiveScale = Math.max(1, Math.min(dpiScale, 4));
|
|
13
236
|
let w = Math.round(width * effectiveScale);
|
|
14
237
|
let h = Math.round(height * effectiveScale);
|
|
@@ -28,8 +251,9 @@ function createCanvas(width, height, maxWidth, maxHeight, dpiScale = DEFAULT_DPI
|
|
|
28
251
|
scaleX *= factor;
|
|
29
252
|
scaleY *= factor;
|
|
30
253
|
}
|
|
31
|
-
const
|
|
32
|
-
const
|
|
254
|
+
const dimCap = Math.max(1, Math.floor(maxCanvasDimension));
|
|
255
|
+
const clampedW = Math.max(1, Math.min(w, dimCap));
|
|
256
|
+
const clampedH = Math.max(1, Math.min(h, dimCap));
|
|
33
257
|
if (clampedW !== w || clampedH !== h) {
|
|
34
258
|
console.warn(
|
|
35
259
|
`[emf-converter] Canvas size clamped from ${w}\xD7${h} to ${clampedW}\xD7${clampedH}. Output may lose detail.`
|
|
@@ -74,8 +298,8 @@ function createTempCanvas(width, height) {
|
|
|
74
298
|
if (width <= 0 || height <= 0) {
|
|
75
299
|
return null;
|
|
76
300
|
}
|
|
77
|
-
width = Math.max(1, Math.min(Math.floor(width),
|
|
78
|
-
height = Math.max(1, Math.min(Math.floor(height),
|
|
301
|
+
width = Math.max(1, Math.min(Math.floor(width), MAX_CANVAS_DIMENSION));
|
|
302
|
+
height = Math.max(1, Math.min(Math.floor(height), MAX_CANVAS_DIMENSION));
|
|
79
303
|
if (typeof OffscreenCanvas !== "undefined") {
|
|
80
304
|
const canvas = new OffscreenCanvas(width, height);
|
|
81
305
|
const ctx = canvas.getContext("2d");
|
|
@@ -96,13 +320,61 @@ function createTempCanvas(width, height) {
|
|
|
96
320
|
}
|
|
97
321
|
return null;
|
|
98
322
|
}
|
|
323
|
+
function rop2Paint(rop2) {
|
|
324
|
+
switch (rop2) {
|
|
325
|
+
case R2_BLACK:
|
|
326
|
+
return { gco: "source-over", colorTransform: "black", exact: true };
|
|
327
|
+
case R2_WHITE:
|
|
328
|
+
return { gco: "source-over", colorTransform: "white", exact: true };
|
|
329
|
+
case R2_NOP:
|
|
330
|
+
return { gco: "source-over", colorTransform: "skip", exact: true };
|
|
331
|
+
case R2_NOTCOPYPEN:
|
|
332
|
+
return { gco: "source-over", colorTransform: "invert", exact: true };
|
|
333
|
+
case R2_NOT:
|
|
334
|
+
return { gco: "difference", colorTransform: "white", exact: true };
|
|
335
|
+
case R2_XORPEN:
|
|
336
|
+
case R2_MASKPENNOT:
|
|
337
|
+
return { gco: "difference", colorTransform: "none", exact: false };
|
|
338
|
+
case R2_NOTXORPEN:
|
|
339
|
+
return { gco: "difference", colorTransform: "invert", exact: false };
|
|
340
|
+
case R2_MASKPEN:
|
|
341
|
+
return { gco: "darken", colorTransform: "none", exact: false };
|
|
342
|
+
case R2_MASKNOTPEN:
|
|
343
|
+
case R2_NOTMERGEPEN:
|
|
344
|
+
return { gco: "darken", colorTransform: "invert", exact: false };
|
|
345
|
+
case R2_MERGEPEN:
|
|
346
|
+
case R2_MERGEPENNOT:
|
|
347
|
+
return { gco: "lighten", colorTransform: "none", exact: false };
|
|
348
|
+
case R2_MERGENOTPEN:
|
|
349
|
+
case R2_NOTMASKPEN:
|
|
350
|
+
return { gco: "lighten", colorTransform: "invert", exact: false };
|
|
351
|
+
default:
|
|
352
|
+
return { gco: "source-over", colorTransform: "none", exact: true };
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
function rop2TransformColor(color, transform) {
|
|
356
|
+
switch (transform) {
|
|
357
|
+
case "invert":
|
|
358
|
+
return invertCssColor(color);
|
|
359
|
+
case "black":
|
|
360
|
+
return "#000000";
|
|
361
|
+
case "white":
|
|
362
|
+
return "#ffffff";
|
|
363
|
+
case "skip":
|
|
364
|
+
return "rgba(0,0,0,0)";
|
|
365
|
+
default:
|
|
366
|
+
return color;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
99
369
|
function applyPen(ctx, state) {
|
|
370
|
+
const paint = rop2Paint(state.rop2);
|
|
371
|
+
ctx.globalCompositeOperation = paint.gco;
|
|
100
372
|
if (state.penStyle === 5) {
|
|
101
373
|
ctx.strokeStyle = "rgba(0,0,0,0)";
|
|
102
374
|
ctx.lineWidth = 0;
|
|
103
375
|
return;
|
|
104
376
|
}
|
|
105
|
-
ctx.strokeStyle = state.penColor;
|
|
377
|
+
ctx.strokeStyle = rop2TransformColor(state.penColor, paint.colorTransform);
|
|
106
378
|
ctx.lineWidth = Math.max(state.penWidth, 1);
|
|
107
379
|
switch (state.penStyle) {
|
|
108
380
|
case 1:
|
|
@@ -123,17 +395,57 @@ function applyPen(ctx, state) {
|
|
|
123
395
|
}
|
|
124
396
|
}
|
|
125
397
|
function applyBrush(ctx, state) {
|
|
398
|
+
const paint = rop2Paint(state.rop2);
|
|
399
|
+
ctx.globalCompositeOperation = paint.gco;
|
|
126
400
|
if (state.brushStyle === 1) {
|
|
127
401
|
ctx.fillStyle = "rgba(0,0,0,0)";
|
|
128
402
|
return;
|
|
129
403
|
}
|
|
130
|
-
ctx.fillStyle = state.brushColor;
|
|
404
|
+
ctx.fillStyle = rop2TransformColor(state.brushColor, paint.colorTransform);
|
|
405
|
+
}
|
|
406
|
+
function cssFontWeight(weight) {
|
|
407
|
+
if (!weight || weight === 400) {
|
|
408
|
+
return "";
|
|
409
|
+
}
|
|
410
|
+
const rounded = Math.round(weight / 100) * 100;
|
|
411
|
+
if (rounded === 700) {
|
|
412
|
+
return "bold";
|
|
413
|
+
}
|
|
414
|
+
if (rounded >= 100 && rounded <= 900) {
|
|
415
|
+
return String(rounded);
|
|
416
|
+
}
|
|
417
|
+
return weight >= 700 ? "bold" : "";
|
|
418
|
+
}
|
|
419
|
+
function mapFontFamily(face, map) {
|
|
420
|
+
const resolved = map?.[face.toLowerCase().trim()] ?? face;
|
|
421
|
+
if (/[\s,]/.test(resolved) && !/^["']/.test(resolved)) {
|
|
422
|
+
return `"${resolved}"`;
|
|
423
|
+
}
|
|
424
|
+
return resolved;
|
|
131
425
|
}
|
|
132
426
|
function applyFont(ctx, state) {
|
|
133
427
|
const italic = state.fontItalic ? "italic " : "";
|
|
134
|
-
const weight = state.fontWeight
|
|
428
|
+
const weight = cssFontWeight(state.fontWeight);
|
|
429
|
+
const weightPart = weight ? `${weight} ` : "";
|
|
430
|
+
const size = Math.max(Math.abs(state.fontHeight), 8);
|
|
431
|
+
const family = mapFontFamily(state.fontFamily, state.fontFamilyMap);
|
|
432
|
+
ctx.font = `${italic}${weightPart}${size}px ${family}`;
|
|
433
|
+
}
|
|
434
|
+
function drawTextDecorations(ctx, state, x, y, width) {
|
|
435
|
+
if (!state.fontUnderline && !state.fontStrikeOut) {
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
135
438
|
const size = Math.max(Math.abs(state.fontHeight), 8);
|
|
136
|
-
|
|
439
|
+
const thickness = Math.max(1, Math.round(size / 14));
|
|
440
|
+
const prevFill = ctx.fillStyle;
|
|
441
|
+
ctx.fillStyle = state.textColor;
|
|
442
|
+
if (state.fontUnderline) {
|
|
443
|
+
ctx.fillRect(x, y + Math.round(size * 0.12), width, thickness);
|
|
444
|
+
}
|
|
445
|
+
if (state.fontStrikeOut) {
|
|
446
|
+
ctx.fillRect(x, y - Math.round(size * 0.3), width, thickness);
|
|
447
|
+
}
|
|
448
|
+
ctx.fillStyle = prevFill;
|
|
137
449
|
}
|
|
138
450
|
function readUtf16LE(view, offset, charCount) {
|
|
139
451
|
if (charCount <= 0) {
|
|
@@ -192,6 +504,8 @@ function getStockObject(index) {
|
|
|
192
504
|
height: 12,
|
|
193
505
|
weight: 400,
|
|
194
506
|
italic: false,
|
|
507
|
+
underline: false,
|
|
508
|
+
strikeOut: false,
|
|
195
509
|
family: "monospace"
|
|
196
510
|
};
|
|
197
511
|
case 12:
|
|
@@ -203,6 +517,8 @@ function getStockObject(index) {
|
|
|
203
517
|
height: 12,
|
|
204
518
|
weight: 400,
|
|
205
519
|
italic: false,
|
|
520
|
+
underline: false,
|
|
521
|
+
strikeOut: false,
|
|
206
522
|
family: "sans-serif"
|
|
207
523
|
};
|
|
208
524
|
default:
|
|
@@ -235,161 +551,6 @@ async function exportCanvasToPngDataUrl(canvas) {
|
|
|
235
551
|
return null;
|
|
236
552
|
}
|
|
237
553
|
|
|
238
|
-
// src/emf-constants.ts
|
|
239
|
-
var EMR_HEADER = 1;
|
|
240
|
-
var EMR_POLYBEZIER = 2;
|
|
241
|
-
var EMR_POLYGON = 3;
|
|
242
|
-
var EMR_POLYLINE = 4;
|
|
243
|
-
var EMR_POLYBEZIERTO = 5;
|
|
244
|
-
var EMR_POLYLINETO = 6;
|
|
245
|
-
var EMR_POLYPOLYLINE = 7;
|
|
246
|
-
var EMR_POLYPOLYGON = 8;
|
|
247
|
-
var EMR_SETWINDOWEXTEX = 9;
|
|
248
|
-
var EMR_SETWINDOWORGEX = 10;
|
|
249
|
-
var EMR_SETVIEWPORTEXTEX = 11;
|
|
250
|
-
var EMR_SETVIEWPORTORGEX = 12;
|
|
251
|
-
var EMR_SETBRUSHORGEX = 13;
|
|
252
|
-
var EMR_EOF = 14;
|
|
253
|
-
var EMR_SETPIXELV = 15;
|
|
254
|
-
var EMR_SETMAPMODE = 17;
|
|
255
|
-
var EMR_SETBKMODE = 18;
|
|
256
|
-
var EMR_SETPOLYFILLMODE = 19;
|
|
257
|
-
var EMR_SETROP2 = 20;
|
|
258
|
-
var EMR_SETSTRETCHBLTMODE = 21;
|
|
259
|
-
var EMR_SETTEXTALIGN = 22;
|
|
260
|
-
var EMR_SETTEXTCOLOR = 24;
|
|
261
|
-
var EMR_SETBKCOLOR = 25;
|
|
262
|
-
var EMR_OFFSETCLIPRGN = 26;
|
|
263
|
-
var EMR_MOVETOEX = 27;
|
|
264
|
-
var EMR_SETMETARGN = 28;
|
|
265
|
-
var EMR_EXCLUDECLIPRECT = 29;
|
|
266
|
-
var EMR_INTERSECTCLIPRECT = 30;
|
|
267
|
-
var EMR_SCALEVIEWPORTEXTEX = 31;
|
|
268
|
-
var EMR_SCALEWINDOWEXTEX = 32;
|
|
269
|
-
var EMR_SAVEDC = 33;
|
|
270
|
-
var EMR_RESTOREDC = 34;
|
|
271
|
-
var EMR_SETWORLDTRANSFORM = 35;
|
|
272
|
-
var EMR_MODIFYWORLDTRANSFORM = 36;
|
|
273
|
-
var EMR_SELECTOBJECT = 37;
|
|
274
|
-
var EMR_CREATEPEN = 38;
|
|
275
|
-
var EMR_CREATEBRUSHINDIRECT = 39;
|
|
276
|
-
var EMR_DELETEOBJECT = 40;
|
|
277
|
-
var EMR_ELLIPSE = 42;
|
|
278
|
-
var EMR_RECTANGLE = 43;
|
|
279
|
-
var EMR_ROUNDRECT = 44;
|
|
280
|
-
var EMR_ARC = 45;
|
|
281
|
-
var EMR_CHORD = 46;
|
|
282
|
-
var EMR_PIE = 47;
|
|
283
|
-
var EMR_LINETO = 54;
|
|
284
|
-
var EMR_ARCTO = 55;
|
|
285
|
-
var EMR_SETMITERLIMIT = 58;
|
|
286
|
-
var EMR_BEGINPATH = 59;
|
|
287
|
-
var EMR_ENDPATH = 60;
|
|
288
|
-
var EMR_CLOSEFIGURE = 61;
|
|
289
|
-
var EMR_FILLPATH = 62;
|
|
290
|
-
var EMR_STROKEANDFILLPATH = 63;
|
|
291
|
-
var EMR_STROKEPATH = 64;
|
|
292
|
-
var EMR_SELECTCLIPPATH = 67;
|
|
293
|
-
var EMR_COMMENT = 70;
|
|
294
|
-
var EMR_EXTSELECTCLIPRGN = 75;
|
|
295
|
-
var EMR_BITBLT = 76;
|
|
296
|
-
var EMR_STRETCHDIBITS = 81;
|
|
297
|
-
var EMR_EXTCREATEFONTINDIRECTW = 82;
|
|
298
|
-
var EMR_EXTTEXTOUTW = 84;
|
|
299
|
-
var EMR_POLYBEZIER16 = 85;
|
|
300
|
-
var EMR_POLYGON16 = 86;
|
|
301
|
-
var EMR_POLYLINE16 = 87;
|
|
302
|
-
var EMR_POLYBEZIERTO16 = 88;
|
|
303
|
-
var EMR_POLYLINETO16 = 89;
|
|
304
|
-
var EMR_POLYPOLYGON16 = 91;
|
|
305
|
-
var EMR_EXTCREATEPEN = 95;
|
|
306
|
-
var EMR_SETICMMODE = 98;
|
|
307
|
-
var EMR_SETLAYOUT = 115;
|
|
308
|
-
var STOCK_OBJECT_BASE = 2147483648;
|
|
309
|
-
var EMFPLUS_SIGNATURE = 726027589;
|
|
310
|
-
var EMR_COMMENT_PUBLIC_SIGNATURE = 1128875079;
|
|
311
|
-
var EMFPLUS_HEADER = 16385;
|
|
312
|
-
var EMFPLUS_ENDOFFILE = 16386;
|
|
313
|
-
var EMFPLUS_GETDC = 16388;
|
|
314
|
-
var EMFPLUS_OBJECT = 16392;
|
|
315
|
-
var EMFPLUS_FILLRECTS = 16394;
|
|
316
|
-
var EMFPLUS_DRAWRECTS = 16395;
|
|
317
|
-
var EMFPLUS_FILLPOLYGON = 16396;
|
|
318
|
-
var EMFPLUS_DRAWLINES = 16397;
|
|
319
|
-
var EMFPLUS_FILLELLIPSE = 16398;
|
|
320
|
-
var EMFPLUS_DRAWELLIPSE = 16399;
|
|
321
|
-
var EMFPLUS_FILLPIE = 16400;
|
|
322
|
-
var EMFPLUS_DRAWPIE = 16401;
|
|
323
|
-
var EMFPLUS_DRAWARC = 16402;
|
|
324
|
-
var EMFPLUS_FILLPATH = 16404;
|
|
325
|
-
var EMFPLUS_DRAWPATH = 16405;
|
|
326
|
-
var EMFPLUS_DRAWIMAGE = 16410;
|
|
327
|
-
var EMFPLUS_DRAWIMAGEPOINTS = 16411;
|
|
328
|
-
var EMFPLUS_DRAWSTRING = 16412;
|
|
329
|
-
var EMFPLUS_SETANTIALIASMODE = 16414;
|
|
330
|
-
var EMFPLUS_SETTEXTRENDERINGHINT = 16415;
|
|
331
|
-
var EMFPLUS_SETINTERPOLATIONMODE = 16417;
|
|
332
|
-
var EMFPLUS_SETPIXELOFFSETMODE = 16418;
|
|
333
|
-
var EMFPLUS_SETCOMPOSITINGQUALITY = 16420;
|
|
334
|
-
var EMFPLUS_SAVE = 16421;
|
|
335
|
-
var EMFPLUS_RESTORE = 16422;
|
|
336
|
-
var EMFPLUS_BEGINCONTAINERNOPARAMS = 16424;
|
|
337
|
-
var EMFPLUS_ENDCONTAINER = 16425;
|
|
338
|
-
var EMFPLUS_SETWORLDTRANSFORM = 16426;
|
|
339
|
-
var EMFPLUS_RESETWORLDTRANSFORM = 16427;
|
|
340
|
-
var EMFPLUS_MULTIPLYWORLDTRANSFORM = 16428;
|
|
341
|
-
var EMFPLUS_TRANSLATEWORLDTRANSFORM = 16429;
|
|
342
|
-
var EMFPLUS_SCALEWORLDTRANSFORM = 16430;
|
|
343
|
-
var EMFPLUS_ROTATEWORLDTRANSFORM = 16431;
|
|
344
|
-
var EMFPLUS_SETPAGETRANSFORM = 16432;
|
|
345
|
-
var EMFPLUS_RESETCLIP = 16433;
|
|
346
|
-
var EMFPLUS_SETCLIPRECT = 16434;
|
|
347
|
-
var EMFPLUS_SETCLIPPATH = 16435;
|
|
348
|
-
var EMFPLUS_SETCLIPREGION = 16436;
|
|
349
|
-
var EMFPLUS_DRAWDRIVERSTRING = 16438;
|
|
350
|
-
var EMFPLUS_OFFSETCLIP = 16437;
|
|
351
|
-
var EMFPLUS_OBJECTTYPE_BRUSH = 1;
|
|
352
|
-
var EMFPLUS_OBJECTTYPE_PEN = 2;
|
|
353
|
-
var EMFPLUS_OBJECTTYPE_PATH = 3;
|
|
354
|
-
var EMFPLUS_OBJECTTYPE_IMAGEATTRIBUTES = 4;
|
|
355
|
-
var EMFPLUS_OBJECTTYPE_IMAGE = 5;
|
|
356
|
-
var EMFPLUS_OBJECTTYPE_FONT = 6;
|
|
357
|
-
var EMFPLUS_OBJECTTYPE_STRINGFORMAT = 7;
|
|
358
|
-
var EMFPLUS_OBJECTTYPE_REGION = 8;
|
|
359
|
-
var EMFPLUS_BRUSHTYPE_SOLID = 0;
|
|
360
|
-
var EMFPLUS_BRUSHTYPE_HATCHFILL = 1;
|
|
361
|
-
var EMFPLUS_BRUSHTYPE_PATHGRADIENT = 3;
|
|
362
|
-
var EMFPLUS_BRUSHTYPE_LINEARGRADIENT = 4;
|
|
363
|
-
var META_EOF = 0;
|
|
364
|
-
var META_SETBKCOLOR = 513;
|
|
365
|
-
var META_SETBKMODE = 258;
|
|
366
|
-
var META_SETROP2 = 260;
|
|
367
|
-
var META_SETPOLYFILLMODE = 262;
|
|
368
|
-
var META_SETTEXTCOLOR = 521;
|
|
369
|
-
var META_SETTEXTALIGN = 302;
|
|
370
|
-
var META_SETWINDOWORG = 523;
|
|
371
|
-
var META_SETWINDOWEXT = 524;
|
|
372
|
-
var META_MOVETO = 532;
|
|
373
|
-
var META_LINETO = 531;
|
|
374
|
-
var META_RECTANGLE = 1051;
|
|
375
|
-
var META_ROUNDRECT = 1564;
|
|
376
|
-
var META_ELLIPSE = 1048;
|
|
377
|
-
var META_ARC = 2071;
|
|
378
|
-
var META_PIE = 2074;
|
|
379
|
-
var META_CHORD = 2096;
|
|
380
|
-
var META_POLYGON = 804;
|
|
381
|
-
var META_POLYLINE = 805;
|
|
382
|
-
var META_SELECTOBJECT = 301;
|
|
383
|
-
var META_DELETEOBJECT = 496;
|
|
384
|
-
var META_CREATEPENINDIRECT = 762;
|
|
385
|
-
var META_CREATEBRUSHINDIRECT = 764;
|
|
386
|
-
var META_CREATEFONTINDIRECT = 763;
|
|
387
|
-
var META_TEXTOUT = 1313;
|
|
388
|
-
var META_EXTTEXTOUT = 2610;
|
|
389
|
-
var META_SAVEDC = 30;
|
|
390
|
-
var META_RESTOREDC = 295;
|
|
391
|
-
var META_POLYPOLYGON = 1336;
|
|
392
|
-
|
|
393
554
|
// src/emf-header-parser.ts
|
|
394
555
|
function parseEmfHeader(view) {
|
|
395
556
|
emfLog("parseEmfHeader: byteLength =", view.byteLength);
|
|
@@ -474,49 +635,36 @@ function parseWmfHeader(view) {
|
|
|
474
635
|
};
|
|
475
636
|
}
|
|
476
637
|
|
|
477
|
-
// src/emf-color-helpers.ts
|
|
478
|
-
function colorRefToHex(r, g, b) {
|
|
479
|
-
const toHex = (v) => v.toString(16).padStart(2, "0");
|
|
480
|
-
return `#${toHex(r & 255)}${toHex(g & 255)}${toHex(b & 255)}`;
|
|
481
|
-
}
|
|
482
|
-
function readColorRef(view, offset) {
|
|
483
|
-
const r = view.getUint8(offset);
|
|
484
|
-
const g = view.getUint8(offset + 1);
|
|
485
|
-
const b = view.getUint8(offset + 2);
|
|
486
|
-
return colorRefToHex(r, g, b);
|
|
487
|
-
}
|
|
488
|
-
function argbToRgba(argb) {
|
|
489
|
-
const a = (argb >>> 24 & 255) / 255;
|
|
490
|
-
const r = argb >>> 16 & 255;
|
|
491
|
-
const g = argb >>> 8 & 255;
|
|
492
|
-
const b = argb & 255;
|
|
493
|
-
return `rgba(${r},${g},${b},${a.toFixed(3)})`;
|
|
494
|
-
}
|
|
495
|
-
|
|
496
638
|
// src/emf-gdi-coord.ts
|
|
497
639
|
function gmx(r, x) {
|
|
640
|
+
const wt = r.state.worldTransform;
|
|
641
|
+
const px = wt[0] * x + wt[4];
|
|
498
642
|
if (r.useMappingMode) {
|
|
499
|
-
return (
|
|
643
|
+
return (px - r.windowOrg.x) / (r.windowExt.cx || 1) * (r.viewportExt.cx || 1) + r.viewportOrg.x;
|
|
500
644
|
}
|
|
501
|
-
return (
|
|
645
|
+
return (px - r.bounds.left) * r.sx;
|
|
502
646
|
}
|
|
503
647
|
function gmy(r, y) {
|
|
648
|
+
const wt = r.state.worldTransform;
|
|
649
|
+
const py = wt[3] * y + wt[5];
|
|
504
650
|
if (r.useMappingMode) {
|
|
505
|
-
return (
|
|
651
|
+
return (py - r.windowOrg.y) / (r.windowExt.cy || 1) * (r.viewportExt.cy || 1) + r.viewportOrg.y;
|
|
506
652
|
}
|
|
507
|
-
return (
|
|
653
|
+
return (py - r.bounds.top) * r.sy;
|
|
508
654
|
}
|
|
509
655
|
function gmw(r, w) {
|
|
656
|
+
const pw = r.state.worldTransform[0] * w;
|
|
510
657
|
if (r.useMappingMode) {
|
|
511
|
-
return
|
|
658
|
+
return pw / (r.windowExt.cx || 1) * (r.viewportExt.cx || 1);
|
|
512
659
|
}
|
|
513
|
-
return
|
|
660
|
+
return pw * r.sx;
|
|
514
661
|
}
|
|
515
662
|
function gmh(r, h) {
|
|
663
|
+
const ph = r.state.worldTransform[3] * h;
|
|
516
664
|
if (r.useMappingMode) {
|
|
517
|
-
return
|
|
665
|
+
return ph / (r.windowExt.cy || 1) * (r.viewportExt.cy || 1);
|
|
518
666
|
}
|
|
519
|
-
return
|
|
667
|
+
return ph * r.sy;
|
|
520
668
|
}
|
|
521
669
|
function activateGdiMappingMode(r) {
|
|
522
670
|
r.useMappingMode = true;
|
|
@@ -696,27 +844,239 @@ function handleArcFamily(rCtx, recType, dataOff, recSize) {
|
|
|
696
844
|
}
|
|
697
845
|
return true;
|
|
698
846
|
}
|
|
699
|
-
function handleEmfGdiShapeRecord(rCtx, recType, dataOff, recSize) {
|
|
700
|
-
switch (recType) {
|
|
701
|
-
case EMR_SETPIXELV:
|
|
702
|
-
return handleSetPixelV(rCtx, dataOff, recSize);
|
|
703
|
-
case EMR_MOVETOEX:
|
|
704
|
-
return handleMoveToEx(rCtx, dataOff, recSize);
|
|
705
|
-
case EMR_LINETO:
|
|
706
|
-
return handleLineTo(rCtx, dataOff, recSize);
|
|
707
|
-
case EMR_RECTANGLE:
|
|
708
|
-
return handleRectangle(rCtx, dataOff, recSize);
|
|
709
|
-
case EMR_ROUNDRECT:
|
|
710
|
-
return handleRoundRect(rCtx, dataOff, recSize);
|
|
711
|
-
case EMR_ELLIPSE:
|
|
712
|
-
return handleEllipse(rCtx, dataOff, recSize);
|
|
713
|
-
case EMR_ARC:
|
|
714
|
-
case EMR_ARCTO:
|
|
715
|
-
case EMR_CHORD:
|
|
716
|
-
case EMR_PIE:
|
|
717
|
-
return handleArcFamily(rCtx, recType, dataOff, recSize);
|
|
718
|
-
default:
|
|
719
|
-
return false;
|
|
847
|
+
function handleEmfGdiShapeRecord(rCtx, recType, dataOff, recSize) {
|
|
848
|
+
switch (recType) {
|
|
849
|
+
case EMR_SETPIXELV:
|
|
850
|
+
return handleSetPixelV(rCtx, dataOff, recSize);
|
|
851
|
+
case EMR_MOVETOEX:
|
|
852
|
+
return handleMoveToEx(rCtx, dataOff, recSize);
|
|
853
|
+
case EMR_LINETO:
|
|
854
|
+
return handleLineTo(rCtx, dataOff, recSize);
|
|
855
|
+
case EMR_RECTANGLE:
|
|
856
|
+
return handleRectangle(rCtx, dataOff, recSize);
|
|
857
|
+
case EMR_ROUNDRECT:
|
|
858
|
+
return handleRoundRect(rCtx, dataOff, recSize);
|
|
859
|
+
case EMR_ELLIPSE:
|
|
860
|
+
return handleEllipse(rCtx, dataOff, recSize);
|
|
861
|
+
case EMR_ARC:
|
|
862
|
+
case EMR_ARCTO:
|
|
863
|
+
case EMR_CHORD:
|
|
864
|
+
case EMR_PIE:
|
|
865
|
+
return handleArcFamily(rCtx, recType, dataOff, recSize);
|
|
866
|
+
default:
|
|
867
|
+
return false;
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
// src/emf-clip-region.ts
|
|
872
|
+
var CLIP_HUGE = 1 << 24;
|
|
873
|
+
function rectClipShape(x, y, w, h) {
|
|
874
|
+
return { cmds: [{ op: "rect", x, y, w, h }], fillRule: "nonzero", simple: true };
|
|
875
|
+
}
|
|
876
|
+
function rectsClipShape(rects) {
|
|
877
|
+
return {
|
|
878
|
+
cmds: rects.map((r) => ({ op: "rect", x: r.x, y: r.y, w: r.w, h: r.h })),
|
|
879
|
+
fillRule: "nonzero",
|
|
880
|
+
simple: true
|
|
881
|
+
};
|
|
882
|
+
}
|
|
883
|
+
function emptyClipShape() {
|
|
884
|
+
return { cmds: [{ op: "rect", x: 0, y: 0, w: 0, h: 0 }], fillRule: "nonzero", simple: true };
|
|
885
|
+
}
|
|
886
|
+
function translateClipShape(shape, dx, dy) {
|
|
887
|
+
return {
|
|
888
|
+
...shape,
|
|
889
|
+
cmds: shape.cmds.map((c) => {
|
|
890
|
+
switch (c.op) {
|
|
891
|
+
case "rect":
|
|
892
|
+
return { ...c, x: c.x + dx, y: c.y + dy };
|
|
893
|
+
case "moveTo":
|
|
894
|
+
case "lineTo":
|
|
895
|
+
return { ...c, x: c.x + dx, y: c.y + dy };
|
|
896
|
+
case "bezierCurveTo":
|
|
897
|
+
return {
|
|
898
|
+
...c,
|
|
899
|
+
cp1x: c.cp1x + dx,
|
|
900
|
+
cp1y: c.cp1y + dy,
|
|
901
|
+
cp2x: c.cp2x + dx,
|
|
902
|
+
cp2y: c.cp2y + dy,
|
|
903
|
+
x: c.x + dx,
|
|
904
|
+
y: c.y + dy
|
|
905
|
+
};
|
|
906
|
+
case "closePath":
|
|
907
|
+
return c;
|
|
908
|
+
}
|
|
909
|
+
})
|
|
910
|
+
};
|
|
911
|
+
}
|
|
912
|
+
function translateClipRegion(region, dx, dy) {
|
|
913
|
+
if (!region) {
|
|
914
|
+
return null;
|
|
915
|
+
}
|
|
916
|
+
return region.map((s) => translateClipShape(s, dx, dy));
|
|
917
|
+
}
|
|
918
|
+
function isComposable(shape) {
|
|
919
|
+
return shape.simple && shape.fillRule === "nonzero";
|
|
920
|
+
}
|
|
921
|
+
function invertClipShape(shape) {
|
|
922
|
+
return {
|
|
923
|
+
cmds: [
|
|
924
|
+
{ op: "rect", x: -CLIP_HUGE, y: -CLIP_HUGE, w: 2 * CLIP_HUGE, h: 2 * CLIP_HUGE },
|
|
925
|
+
...shape.cmds
|
|
926
|
+
],
|
|
927
|
+
fillRule: "evenodd",
|
|
928
|
+
simple: false
|
|
929
|
+
};
|
|
930
|
+
}
|
|
931
|
+
function combineClip(current, shape, op) {
|
|
932
|
+
switch (op) {
|
|
933
|
+
case "replace":
|
|
934
|
+
return { region: [shape], exact: true };
|
|
935
|
+
case "intersect":
|
|
936
|
+
return { region: current ? [...current, shape] : [shape], exact: true };
|
|
937
|
+
case "exclude": {
|
|
938
|
+
if (!isComposable(shape)) {
|
|
939
|
+
return { region: current ? [...current, shape] : [shape], exact: false };
|
|
940
|
+
}
|
|
941
|
+
const inv = invertClipShape(shape);
|
|
942
|
+
return { region: current ? [...current, inv] : [inv], exact: true };
|
|
943
|
+
}
|
|
944
|
+
case "union": {
|
|
945
|
+
if (!current) {
|
|
946
|
+
return { region: null, exact: true };
|
|
947
|
+
}
|
|
948
|
+
if (current.length === 1 && isComposable(current[0]) && isComposable(shape)) {
|
|
949
|
+
return {
|
|
950
|
+
region: [
|
|
951
|
+
{ cmds: [...current[0].cmds, ...shape.cmds], fillRule: "nonzero", simple: false }
|
|
952
|
+
],
|
|
953
|
+
exact: false
|
|
954
|
+
};
|
|
955
|
+
}
|
|
956
|
+
return { region: current, exact: false };
|
|
957
|
+
}
|
|
958
|
+
case "xor": {
|
|
959
|
+
if (!current) {
|
|
960
|
+
if (isComposable(shape)) {
|
|
961
|
+
return { region: [invertClipShape(shape)], exact: true };
|
|
962
|
+
}
|
|
963
|
+
return { region: [shape], exact: false };
|
|
964
|
+
}
|
|
965
|
+
if (current.length === 1 && isComposable(current[0]) && isComposable(shape)) {
|
|
966
|
+
return {
|
|
967
|
+
region: [
|
|
968
|
+
{ cmds: [...current[0].cmds, ...shape.cmds], fillRule: "evenodd", simple: false }
|
|
969
|
+
],
|
|
970
|
+
exact: true
|
|
971
|
+
};
|
|
972
|
+
}
|
|
973
|
+
if (isComposable(shape)) {
|
|
974
|
+
return { region: [...current, invertClipShape(shape)], exact: false };
|
|
975
|
+
}
|
|
976
|
+
return { region: [...current, shape], exact: false };
|
|
977
|
+
}
|
|
978
|
+
case "complement": {
|
|
979
|
+
if (!current) {
|
|
980
|
+
return { region: [emptyClipShape()], exact: true };
|
|
981
|
+
}
|
|
982
|
+
if (current.length === 1 && isComposable(current[0])) {
|
|
983
|
+
return { region: [shape, invertClipShape(current[0])], exact: true };
|
|
984
|
+
}
|
|
985
|
+
return { region: [shape], exact: false };
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
}
|
|
989
|
+
function combineClipRegions(current, incoming, op) {
|
|
990
|
+
if (op === "replace") {
|
|
991
|
+
return { region: incoming, exact: true };
|
|
992
|
+
}
|
|
993
|
+
if (incoming && incoming.length === 1) {
|
|
994
|
+
return combineClip(current, incoming[0], op);
|
|
995
|
+
}
|
|
996
|
+
if (!incoming) {
|
|
997
|
+
switch (op) {
|
|
998
|
+
case "intersect":
|
|
999
|
+
return { region: current, exact: true };
|
|
1000
|
+
case "union":
|
|
1001
|
+
return { region: null, exact: true };
|
|
1002
|
+
case "exclude":
|
|
1003
|
+
return { region: [emptyClipShape()], exact: true };
|
|
1004
|
+
case "xor":
|
|
1005
|
+
case "complement": {
|
|
1006
|
+
if (!current) {
|
|
1007
|
+
return { region: [emptyClipShape()], exact: true };
|
|
1008
|
+
}
|
|
1009
|
+
if (current.length === 1) {
|
|
1010
|
+
return combineClip(null, current[0], "exclude");
|
|
1011
|
+
}
|
|
1012
|
+
return { region: current, exact: false };
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
}
|
|
1016
|
+
switch (op) {
|
|
1017
|
+
case "intersect":
|
|
1018
|
+
return { region: current ? [...current, ...incoming] : incoming, exact: true };
|
|
1019
|
+
case "union":
|
|
1020
|
+
return { region: current, exact: false };
|
|
1021
|
+
case "xor":
|
|
1022
|
+
return { region: current ?? incoming, exact: false };
|
|
1023
|
+
case "exclude":
|
|
1024
|
+
return { region: current, exact: false };
|
|
1025
|
+
case "complement": {
|
|
1026
|
+
if (!current) {
|
|
1027
|
+
return { region: [emptyClipShape()], exact: true };
|
|
1028
|
+
}
|
|
1029
|
+
if (current.length === 1) {
|
|
1030
|
+
return combineClip(incoming, current[0], "exclude");
|
|
1031
|
+
}
|
|
1032
|
+
return { region: incoming, exact: false };
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
1036
|
+
function replayClipCmds(ctx, cmds) {
|
|
1037
|
+
for (const c of cmds) {
|
|
1038
|
+
switch (c.op) {
|
|
1039
|
+
case "rect":
|
|
1040
|
+
ctx.rect(c.x, c.y, c.w, c.h);
|
|
1041
|
+
break;
|
|
1042
|
+
case "moveTo":
|
|
1043
|
+
ctx.moveTo(c.x, c.y);
|
|
1044
|
+
break;
|
|
1045
|
+
case "lineTo":
|
|
1046
|
+
ctx.lineTo(c.x, c.y);
|
|
1047
|
+
break;
|
|
1048
|
+
case "bezierCurveTo":
|
|
1049
|
+
ctx.bezierCurveTo(c.cp1x, c.cp1y, c.cp2x, c.cp2y, c.x, c.y);
|
|
1050
|
+
break;
|
|
1051
|
+
case "closePath":
|
|
1052
|
+
ctx.closePath();
|
|
1053
|
+
break;
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
function applyClipShapes(ctx, shapes) {
|
|
1058
|
+
for (const s of shapes) {
|
|
1059
|
+
ctx.beginPath();
|
|
1060
|
+
replayClipCmds(ctx, s.cmds);
|
|
1061
|
+
try {
|
|
1062
|
+
ctx.clip(s.fillRule);
|
|
1063
|
+
} catch {
|
|
1064
|
+
}
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
1067
|
+
function reapplyClipRegion(holder, region, identityTransform = false) {
|
|
1068
|
+
const { ctx } = holder;
|
|
1069
|
+
while (holder.clipSaveDepth > 0) {
|
|
1070
|
+
ctx.restore();
|
|
1071
|
+
holder.clipSaveDepth--;
|
|
1072
|
+
}
|
|
1073
|
+
if (region) {
|
|
1074
|
+
ctx.save();
|
|
1075
|
+
holder.clipSaveDepth = 1;
|
|
1076
|
+
if (identityTransform) {
|
|
1077
|
+
ctx.setTransform(1, 0, 0, 1, 0, 0);
|
|
1078
|
+
}
|
|
1079
|
+
applyClipShapes(ctx, region);
|
|
720
1080
|
}
|
|
721
1081
|
}
|
|
722
1082
|
|
|
@@ -1066,6 +1426,12 @@ function handleExtTextOutW(rCtx, offset, dataOff, recSize) {
|
|
|
1066
1426
|
ctx.fillStyle = state.textColor;
|
|
1067
1427
|
}
|
|
1068
1428
|
ctx.fillText(text, gmx(rCtx, refX), gmy(rCtx, refY));
|
|
1429
|
+
if (state.fontUnderline || state.fontStrikeOut) {
|
|
1430
|
+
const w = ctx.measureText(text).width;
|
|
1431
|
+
const baseX = gmx(rCtx, refX);
|
|
1432
|
+
const startX = alignHoriz === "center" ? baseX - w / 2 : alignHoriz === "right" ? baseX - w : baseX;
|
|
1433
|
+
drawTextDecorations(ctx, state, startX, gmy(rCtx, refY), w);
|
|
1434
|
+
}
|
|
1069
1435
|
}
|
|
1070
1436
|
}
|
|
1071
1437
|
}
|
|
@@ -1141,86 +1507,134 @@ function handleStretchDibits(rCtx, offset, dataOff, recSize) {
|
|
|
1141
1507
|
}
|
|
1142
1508
|
return true;
|
|
1143
1509
|
}
|
|
1510
|
+
function gdiCombineClip(rCtx, shape, op) {
|
|
1511
|
+
const { ctx } = rCtx;
|
|
1512
|
+
if (rCtx.clipUntracked && op !== "replace") {
|
|
1513
|
+
switch (op) {
|
|
1514
|
+
case "intersect":
|
|
1515
|
+
case "complement": {
|
|
1516
|
+
ctx.save();
|
|
1517
|
+
rCtx.clipSaveDepth++;
|
|
1518
|
+
applyClipShapes(ctx, [shape]);
|
|
1519
|
+
return;
|
|
1520
|
+
}
|
|
1521
|
+
case "exclude":
|
|
1522
|
+
case "xor": {
|
|
1523
|
+
const inv = combineClip(null, shape, "exclude");
|
|
1524
|
+
ctx.save();
|
|
1525
|
+
rCtx.clipSaveDepth++;
|
|
1526
|
+
applyClipShapes(ctx, inv.region ?? [shape]);
|
|
1527
|
+
return;
|
|
1528
|
+
}
|
|
1529
|
+
case "union":
|
|
1530
|
+
return;
|
|
1531
|
+
}
|
|
1532
|
+
}
|
|
1533
|
+
const res = combineClip(op === "replace" ? null : rCtx.clipRegion ?? null, shape, op);
|
|
1534
|
+
if (!res.exact) ;
|
|
1535
|
+
rCtx.clipRegion = res.region;
|
|
1536
|
+
rCtx.clipUntracked = false;
|
|
1537
|
+
reapplyClipRegion(rCtx, res.region);
|
|
1538
|
+
}
|
|
1539
|
+
function readClipRectShape(rCtx, dataOff) {
|
|
1540
|
+
const { view } = rCtx;
|
|
1541
|
+
const left = view.getInt32(dataOff, true);
|
|
1542
|
+
const top = view.getInt32(dataOff + 4, true);
|
|
1543
|
+
const right = view.getInt32(dataOff + 8, true);
|
|
1544
|
+
const bottom = view.getInt32(dataOff + 12, true);
|
|
1545
|
+
return rectClipShape(
|
|
1546
|
+
gmx(rCtx, left),
|
|
1547
|
+
gmy(rCtx, top),
|
|
1548
|
+
gmw(rCtx, right - left),
|
|
1549
|
+
gmh(rCtx, bottom - top)
|
|
1550
|
+
);
|
|
1551
|
+
}
|
|
1144
1552
|
function handleIntersectClipRect(rCtx, dataOff, recSize) {
|
|
1145
|
-
const { ctx, view } = rCtx;
|
|
1146
1553
|
if (recSize >= 24) {
|
|
1147
|
-
|
|
1148
|
-
const top = view.getInt32(dataOff + 4, true);
|
|
1149
|
-
const right = view.getInt32(dataOff + 8, true);
|
|
1150
|
-
const bottom = view.getInt32(dataOff + 12, true);
|
|
1151
|
-
ctx.save();
|
|
1152
|
-
rCtx.clipSaveDepth++;
|
|
1153
|
-
ctx.beginPath();
|
|
1154
|
-
ctx.rect(gmx(rCtx, left), gmy(rCtx, top), gmw(rCtx, right - left), gmh(rCtx, bottom - top));
|
|
1155
|
-
try {
|
|
1156
|
-
ctx.clip();
|
|
1157
|
-
} catch {
|
|
1158
|
-
}
|
|
1554
|
+
gdiCombineClip(rCtx, readClipRectShape(rCtx, dataOff), "intersect");
|
|
1159
1555
|
}
|
|
1160
1556
|
return true;
|
|
1161
1557
|
}
|
|
1558
|
+
function handleExcludeClipRect(rCtx, dataOff, recSize) {
|
|
1559
|
+
if (recSize >= 24) {
|
|
1560
|
+
gdiCombineClip(rCtx, readClipRectShape(rCtx, dataOff), "exclude");
|
|
1561
|
+
}
|
|
1562
|
+
return true;
|
|
1563
|
+
}
|
|
1564
|
+
var RGN_MODE_OPS = {
|
|
1565
|
+
1: "intersect",
|
|
1566
|
+
// RGN_AND
|
|
1567
|
+
2: "union",
|
|
1568
|
+
// RGN_OR
|
|
1569
|
+
3: "xor",
|
|
1570
|
+
// RGN_XOR
|
|
1571
|
+
4: "exclude",
|
|
1572
|
+
// RGN_DIFF
|
|
1573
|
+
5: "replace"
|
|
1574
|
+
// RGN_COPY
|
|
1575
|
+
};
|
|
1162
1576
|
function handleExtSelectClipRgn(rCtx, dataOff, recSize) {
|
|
1163
|
-
const {
|
|
1577
|
+
const { view } = rCtx;
|
|
1164
1578
|
if (recSize < 16) {
|
|
1165
1579
|
return true;
|
|
1166
1580
|
}
|
|
1167
1581
|
const cbRgnData = view.getUint32(dataOff, true);
|
|
1168
1582
|
const iMode = view.getUint32(dataOff + 4, true);
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
if (cbRgnData < 32) {
|
|
1179
|
-
return true;
|
|
1180
|
-
}
|
|
1181
|
-
const nCount = view.getUint32(rgnStart + 8, true);
|
|
1182
|
-
if (nCount === 0) {
|
|
1183
|
-
return true;
|
|
1184
|
-
}
|
|
1185
|
-
while (rCtx.clipSaveDepth > 0) {
|
|
1186
|
-
ctx.restore();
|
|
1187
|
-
rCtx.clipSaveDepth--;
|
|
1188
|
-
}
|
|
1189
|
-
ctx.save();
|
|
1190
|
-
rCtx.clipSaveDepth++;
|
|
1191
|
-
ctx.beginPath();
|
|
1192
|
-
const rectsStart = rgnStart + 32;
|
|
1193
|
-
for (let i = 0; i < nCount; i++) {
|
|
1194
|
-
const rOff = rectsStart + i * 16;
|
|
1195
|
-
if (rOff + 16 > dataOff + 8 + cbRgnData) {
|
|
1196
|
-
break;
|
|
1197
|
-
}
|
|
1198
|
-
const left = view.getInt32(rOff, true);
|
|
1199
|
-
const top = view.getInt32(rOff + 4, true);
|
|
1200
|
-
const right = view.getInt32(rOff + 8, true);
|
|
1201
|
-
const bottom = view.getInt32(rOff + 12, true);
|
|
1202
|
-
ctx.rect(gmx(rCtx, left), gmy(rCtx, top), gmw(rCtx, right - left), gmh(rCtx, bottom - top));
|
|
1203
|
-
}
|
|
1204
|
-
try {
|
|
1205
|
-
ctx.clip();
|
|
1206
|
-
} catch {
|
|
1583
|
+
const op = RGN_MODE_OPS[iMode];
|
|
1584
|
+
if (!op) {
|
|
1585
|
+
return true;
|
|
1586
|
+
}
|
|
1587
|
+
if (cbRgnData === 0) {
|
|
1588
|
+
if (op === "replace") {
|
|
1589
|
+
rCtx.clipRegion = null;
|
|
1590
|
+
rCtx.clipUntracked = false;
|
|
1591
|
+
reapplyClipRegion(rCtx, null);
|
|
1207
1592
|
}
|
|
1593
|
+
return true;
|
|
1208
1594
|
}
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1595
|
+
const rgnStart = dataOff + 8;
|
|
1596
|
+
if (cbRgnData < 32) {
|
|
1597
|
+
return true;
|
|
1598
|
+
}
|
|
1599
|
+
const nCount = view.getUint32(rgnStart + 8, true);
|
|
1600
|
+
if (nCount === 0) {
|
|
1601
|
+
return true;
|
|
1602
|
+
}
|
|
1603
|
+
const rects = [];
|
|
1604
|
+
const rectsStart = rgnStart + 32;
|
|
1605
|
+
for (let i = 0; i < nCount; i++) {
|
|
1606
|
+
const rOff = rectsStart + i * 16;
|
|
1607
|
+
if (rOff + 16 > dataOff + 8 + cbRgnData) {
|
|
1608
|
+
break;
|
|
1609
|
+
}
|
|
1610
|
+
const left = view.getInt32(rOff, true);
|
|
1611
|
+
const top = view.getInt32(rOff + 4, true);
|
|
1612
|
+
const right = view.getInt32(rOff + 8, true);
|
|
1613
|
+
const bottom = view.getInt32(rOff + 12, true);
|
|
1614
|
+
rects.push({
|
|
1615
|
+
x: gmx(rCtx, left),
|
|
1616
|
+
y: gmy(rCtx, top),
|
|
1617
|
+
w: gmw(rCtx, right - left),
|
|
1618
|
+
h: gmh(rCtx, bottom - top)
|
|
1619
|
+
});
|
|
1620
|
+
}
|
|
1621
|
+
if (rects.length === 0) {
|
|
1622
|
+
return true;
|
|
1217
1623
|
}
|
|
1624
|
+
gdiCombineClip(rCtx, rectsClipShape(rects), op);
|
|
1218
1625
|
return true;
|
|
1219
1626
|
}
|
|
1220
1627
|
function handleOffsetClipRgn(rCtx, dataOff, recSize) {
|
|
1221
1628
|
if (recSize >= 16) {
|
|
1222
|
-
rCtx.view.getInt32(dataOff, true);
|
|
1223
|
-
rCtx.view.getInt32(dataOff + 4, true);
|
|
1629
|
+
const dx = rCtx.view.getInt32(dataOff, true);
|
|
1630
|
+
const dy = rCtx.view.getInt32(dataOff + 4, true);
|
|
1631
|
+
if (rCtx.clipUntracked) {
|
|
1632
|
+
return true;
|
|
1633
|
+
}
|
|
1634
|
+
if (rCtx.clipRegion) {
|
|
1635
|
+
rCtx.clipRegion = translateClipRegion(rCtx.clipRegion, gmw(rCtx, dx), gmh(rCtx, dy));
|
|
1636
|
+
reapplyClipRegion(rCtx, rCtx.clipRegion);
|
|
1637
|
+
}
|
|
1224
1638
|
}
|
|
1225
1639
|
return true;
|
|
1226
1640
|
}
|
|
@@ -1543,14 +1957,12 @@ function handleEmfGdiPolyPathRecord(rCtx, recType, offset, dataOff, recSize) {
|
|
|
1543
1957
|
ctx.restore();
|
|
1544
1958
|
rCtx.clipSaveDepth--;
|
|
1545
1959
|
}
|
|
1546
|
-
ctx.save();
|
|
1547
|
-
rCtx.clipSaveDepth++;
|
|
1548
|
-
ctx.clip();
|
|
1549
|
-
} else {
|
|
1550
|
-
ctx.save();
|
|
1551
|
-
rCtx.clipSaveDepth++;
|
|
1552
|
-
ctx.clip();
|
|
1553
1960
|
}
|
|
1961
|
+
ctx.save();
|
|
1962
|
+
rCtx.clipSaveDepth++;
|
|
1963
|
+
ctx.clip(state.polyFillMode === 2 ? "nonzero" : "evenodd");
|
|
1964
|
+
rCtx.clipRegion = null;
|
|
1965
|
+
rCtx.clipUntracked = true;
|
|
1554
1966
|
} catch {
|
|
1555
1967
|
}
|
|
1556
1968
|
return true;
|
|
@@ -1613,12 +2025,16 @@ function handleEmfObjectRecord(rCtx, recType, dataOff, recSize) {
|
|
|
1613
2025
|
const height = view.getInt32(dataOff + 4, true);
|
|
1614
2026
|
const weight = view.getInt32(dataOff + 20, true);
|
|
1615
2027
|
const italic = view.getUint8(dataOff + 24);
|
|
2028
|
+
const underline = view.getUint8(dataOff + 25);
|
|
2029
|
+
const strikeOut = view.getUint8(dataOff + 26);
|
|
1616
2030
|
const family = readUtf16LE(view, dataOff + 28, 32) || "sans-serif";
|
|
1617
2031
|
rCtx.objectTable.set(ihFont, {
|
|
1618
2032
|
kind: "font",
|
|
1619
2033
|
height: Math.abs(height),
|
|
1620
2034
|
weight,
|
|
1621
2035
|
italic: italic !== 0,
|
|
2036
|
+
underline: underline !== 0,
|
|
2037
|
+
strikeOut: strikeOut !== 0,
|
|
1622
2038
|
family
|
|
1623
2039
|
});
|
|
1624
2040
|
}
|
|
@@ -1643,6 +2059,8 @@ function handleEmfObjectRecord(rCtx, recType, dataOff, recSize) {
|
|
|
1643
2059
|
state.fontHeight = obj.height;
|
|
1644
2060
|
state.fontWeight = obj.weight;
|
|
1645
2061
|
state.fontItalic = obj.italic;
|
|
2062
|
+
state.fontUnderline = obj.underline;
|
|
2063
|
+
state.fontStrikeOut = obj.strikeOut;
|
|
1646
2064
|
state.fontFamily = obj.family;
|
|
1647
2065
|
break;
|
|
1648
2066
|
}
|
|
@@ -1819,6 +2237,9 @@ function defaultState() {
|
|
|
1819
2237
|
fontWeight: 400,
|
|
1820
2238
|
fontItalic: false,
|
|
1821
2239
|
fontFamily: "sans-serif",
|
|
2240
|
+
fontUnderline: false,
|
|
2241
|
+
fontStrikeOut: false,
|
|
2242
|
+
rop2: 13,
|
|
1822
2243
|
curX: 0,
|
|
1823
2244
|
curY: 0,
|
|
1824
2245
|
polyFillMode: 1,
|
|
@@ -1837,7 +2258,9 @@ function createEmfPlusState() {
|
|
|
1837
2258
|
objectTable: /* @__PURE__ */ new Map(),
|
|
1838
2259
|
worldTransform: [1, 0, 0, 1, 0, 0],
|
|
1839
2260
|
saveStack: [],
|
|
1840
|
-
saveIdMap: /* @__PURE__ */ new Map()
|
|
2261
|
+
saveIdMap: /* @__PURE__ */ new Map(),
|
|
2262
|
+
clipRegion: null,
|
|
2263
|
+
clipSaveDepth: 0
|
|
1841
2264
|
};
|
|
1842
2265
|
}
|
|
1843
2266
|
|
|
@@ -1857,8 +2280,19 @@ function handleEmfGdiStateRecord(rCtx, recType, _offset, dataOff, recSize) {
|
|
|
1857
2280
|
ctx.restore();
|
|
1858
2281
|
rCtx.clipSaveDepth--;
|
|
1859
2282
|
}
|
|
2283
|
+
rCtx.clipStack ?? (rCtx.clipStack = []);
|
|
2284
|
+
rCtx.clipStack.push({
|
|
2285
|
+
region: rCtx.clipRegion ?? null,
|
|
2286
|
+
untracked: rCtx.clipUntracked ?? false
|
|
2287
|
+
});
|
|
1860
2288
|
rCtx.stateStack.push(cloneState(state));
|
|
1861
2289
|
ctx.save();
|
|
2290
|
+
if (rCtx.clipUntracked) {
|
|
2291
|
+
rCtx.clipUntracked = false;
|
|
2292
|
+
rCtx.clipRegion = null;
|
|
2293
|
+
} else if (rCtx.clipRegion) {
|
|
2294
|
+
reapplyClipRegion(rCtx, rCtx.clipRegion);
|
|
2295
|
+
}
|
|
1862
2296
|
return true;
|
|
1863
2297
|
}
|
|
1864
2298
|
case EMR_RESTOREDC: {
|
|
@@ -1869,16 +2303,23 @@ function handleEmfGdiStateRecord(rCtx, recType, _offset, dataOff, recSize) {
|
|
|
1869
2303
|
}
|
|
1870
2304
|
let rel = view.getInt32(dataOff, true);
|
|
1871
2305
|
if (rel < 0) {
|
|
1872
|
-
rel = rCtx.stateStack.length + rel;
|
|
2306
|
+
rel = rCtx.stateStack.length + rel + 1;
|
|
1873
2307
|
}
|
|
1874
2308
|
while (rCtx.stateStack.length > rel && rCtx.stateStack.length > 0) {
|
|
1875
2309
|
rCtx.stateStack.pop();
|
|
2310
|
+
rCtx.clipStack?.pop();
|
|
1876
2311
|
ctx.restore();
|
|
1877
2312
|
}
|
|
1878
2313
|
const restored = rCtx.stateStack.pop();
|
|
1879
2314
|
if (restored) {
|
|
2315
|
+
const clipSnapshot = rCtx.clipStack?.pop();
|
|
1880
2316
|
Object.assign(state, restored);
|
|
1881
2317
|
ctx.restore();
|
|
2318
|
+
rCtx.clipRegion = clipSnapshot?.region ?? null;
|
|
2319
|
+
rCtx.clipUntracked = false;
|
|
2320
|
+
if (rCtx.clipRegion) {
|
|
2321
|
+
reapplyClipRegion(rCtx, rCtx.clipRegion);
|
|
2322
|
+
}
|
|
1882
2323
|
}
|
|
1883
2324
|
}
|
|
1884
2325
|
return true;
|
|
@@ -1908,7 +2349,12 @@ function handleEmfGdiStateRecord(rCtx, recType, _offset, dataOff, recSize) {
|
|
|
1908
2349
|
}
|
|
1909
2350
|
return true;
|
|
1910
2351
|
}
|
|
1911
|
-
case EMR_SETROP2:
|
|
2352
|
+
case EMR_SETROP2: {
|
|
2353
|
+
if (recSize >= 12) {
|
|
2354
|
+
state.rop2 = view.getUint32(dataOff, true);
|
|
2355
|
+
}
|
|
2356
|
+
return true;
|
|
2357
|
+
}
|
|
1912
2358
|
case EMR_SETSTRETCHBLTMODE:
|
|
1913
2359
|
case EMR_SETMITERLIMIT:
|
|
1914
2360
|
case EMR_SETTEXTALIGN: {
|
|
@@ -1992,6 +2438,47 @@ function parseEmfPlusPath(data, off, maxLen) {
|
|
|
1992
2438
|
const types = new Uint8Array(data.buffer, data.byteOffset + alignedPOff, pointCount);
|
|
1993
2439
|
return { kind: "plus-path", points, types: new Uint8Array(types) };
|
|
1994
2440
|
}
|
|
2441
|
+
function emfPlusPathToClipCmds(path, m) {
|
|
2442
|
+
const tx = (x, y) => m[0] * x + m[2] * y + m[4];
|
|
2443
|
+
const ty = (x, y) => m[1] * x + m[3] * y + m[5];
|
|
2444
|
+
const cmds = [];
|
|
2445
|
+
const pts = path.points;
|
|
2446
|
+
const types = path.types;
|
|
2447
|
+
let i = 0;
|
|
2448
|
+
while (i < pts.length) {
|
|
2449
|
+
const t = types[i] & 15;
|
|
2450
|
+
const close = (types[i] & 128) !== 0;
|
|
2451
|
+
if (t === 0) {
|
|
2452
|
+
cmds.push({ op: "moveTo", x: tx(pts[i].x, pts[i].y), y: ty(pts[i].x, pts[i].y) });
|
|
2453
|
+
i++;
|
|
2454
|
+
} else if (t === 3) {
|
|
2455
|
+
if (i + 2 < pts.length) {
|
|
2456
|
+
cmds.push({
|
|
2457
|
+
op: "bezierCurveTo",
|
|
2458
|
+
cp1x: tx(pts[i].x, pts[i].y),
|
|
2459
|
+
cp1y: ty(pts[i].x, pts[i].y),
|
|
2460
|
+
cp2x: tx(pts[i + 1].x, pts[i + 1].y),
|
|
2461
|
+
cp2y: ty(pts[i + 1].x, pts[i + 1].y),
|
|
2462
|
+
x: tx(pts[i + 2].x, pts[i + 2].y),
|
|
2463
|
+
y: ty(pts[i + 2].x, pts[i + 2].y)
|
|
2464
|
+
});
|
|
2465
|
+
if ((types[i + 2] & 128) !== 0) {
|
|
2466
|
+
cmds.push({ op: "closePath" });
|
|
2467
|
+
}
|
|
2468
|
+
i += 3;
|
|
2469
|
+
continue;
|
|
2470
|
+
}
|
|
2471
|
+
break;
|
|
2472
|
+
} else {
|
|
2473
|
+
cmds.push({ op: "lineTo", x: tx(pts[i].x, pts[i].y), y: ty(pts[i].x, pts[i].y) });
|
|
2474
|
+
i++;
|
|
2475
|
+
}
|
|
2476
|
+
if (close) {
|
|
2477
|
+
cmds.push({ op: "closePath" });
|
|
2478
|
+
}
|
|
2479
|
+
}
|
|
2480
|
+
return cmds;
|
|
2481
|
+
}
|
|
1995
2482
|
function replayEmfPlusPath(ctx, path) {
|
|
1996
2483
|
ctx.beginPath();
|
|
1997
2484
|
const pts = path.points;
|
|
@@ -2045,12 +2532,44 @@ function multiplyMatrix(m1, m2) {
|
|
|
2045
2532
|
m1[4] * m2[1] + m1[5] * m2[3] + m2[5]
|
|
2046
2533
|
];
|
|
2047
2534
|
}
|
|
2048
|
-
function
|
|
2535
|
+
function createBrushGradient(rCtx, grad) {
|
|
2536
|
+
const ctx = rCtx.ctx;
|
|
2537
|
+
try {
|
|
2538
|
+
let g = null;
|
|
2539
|
+
if (grad.type === "linear" && typeof ctx.createLinearGradient === "function") {
|
|
2540
|
+
if (grad.x1 === grad.x2 && grad.y1 === grad.y2) {
|
|
2541
|
+
return null;
|
|
2542
|
+
}
|
|
2543
|
+
g = ctx.createLinearGradient(grad.x1, grad.y1, grad.x2, grad.y2);
|
|
2544
|
+
} else if (grad.type === "radial" && typeof ctx.createRadialGradient === "function") {
|
|
2545
|
+
if (!(grad.r > 0)) {
|
|
2546
|
+
return null;
|
|
2547
|
+
}
|
|
2548
|
+
g = ctx.createRadialGradient(grad.cx, grad.cy, 0, grad.cx, grad.cy, grad.r);
|
|
2549
|
+
}
|
|
2550
|
+
if (!g) {
|
|
2551
|
+
return null;
|
|
2552
|
+
}
|
|
2553
|
+
for (const stop of grad.stops) {
|
|
2554
|
+
g.addColorStop(stop.offset, stop.color);
|
|
2555
|
+
}
|
|
2556
|
+
return g;
|
|
2557
|
+
} catch {
|
|
2558
|
+
return null;
|
|
2559
|
+
}
|
|
2560
|
+
}
|
|
2561
|
+
function resolveBrushPaint(rCtx, flags, brushIdOrColor) {
|
|
2049
2562
|
if (flags & 32768) {
|
|
2050
2563
|
return argbToRgba(brushIdOrColor);
|
|
2051
2564
|
}
|
|
2052
2565
|
const obj = rCtx.objectTable.get(brushIdOrColor & 255);
|
|
2053
2566
|
if (obj && obj.kind === "plus-brush") {
|
|
2567
|
+
if (obj.gradient) {
|
|
2568
|
+
const g = createBrushGradient(rCtx, obj.gradient);
|
|
2569
|
+
if (g) {
|
|
2570
|
+
return g;
|
|
2571
|
+
}
|
|
2572
|
+
}
|
|
2054
2573
|
return obj.color;
|
|
2055
2574
|
}
|
|
2056
2575
|
return "rgba(0,0,0,1)";
|
|
@@ -2114,88 +2633,83 @@ function popState(rCtx, stackId) {
|
|
|
2114
2633
|
rCtx.saveIdMap = newMap;
|
|
2115
2634
|
}
|
|
2116
2635
|
}
|
|
2117
|
-
function
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
}
|
|
2636
|
+
function plusDeviceMatrix(rCtx) {
|
|
2637
|
+
const wt = rCtx.worldTransform;
|
|
2638
|
+
const s = getPageUnitMultiplier(rCtx.pageUnit, rCtx.pageScale) * rCtx.dpiScale;
|
|
2639
|
+
return [wt[0] * s, wt[1] * s, wt[2] * s, wt[3] * s, wt[4] * s, wt[5] * s];
|
|
2122
2640
|
}
|
|
2123
|
-
function
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2641
|
+
function transformedRectShape(x, y, w, h, m) {
|
|
2642
|
+
const tx = (px, py) => m[0] * px + m[2] * py + m[4];
|
|
2643
|
+
const ty = (px, py) => m[1] * px + m[3] * py + m[5];
|
|
2644
|
+
const cmds = [
|
|
2645
|
+
{ op: "moveTo", x: tx(x, y), y: ty(x, y) },
|
|
2646
|
+
{ op: "lineTo", x: tx(x + w, y), y: ty(x + w, y) },
|
|
2647
|
+
{ op: "lineTo", x: tx(x + w, y + h), y: ty(x + w, y + h) },
|
|
2648
|
+
{ op: "lineTo", x: tx(x, y + h), y: ty(x, y + h) },
|
|
2649
|
+
{ op: "closePath" }
|
|
2650
|
+
];
|
|
2651
|
+
return { cmds, fillRule: "nonzero", simple: true };
|
|
2128
2652
|
}
|
|
2129
|
-
function
|
|
2130
|
-
|
|
2131
|
-
case 0:
|
|
2132
|
-
resetClipState(rCtx);
|
|
2133
|
-
ensureClipSave(rCtx);
|
|
2134
|
-
clipFn();
|
|
2135
|
-
return true;
|
|
2136
|
-
case 1:
|
|
2137
|
-
ensureClipSave(rCtx);
|
|
2138
|
-
clipFn();
|
|
2139
|
-
return true;
|
|
2140
|
-
case 2:
|
|
2141
|
-
ensureClipSave(rCtx);
|
|
2142
|
-
clipFn();
|
|
2143
|
-
return true;
|
|
2144
|
-
case 3:
|
|
2145
|
-
return false;
|
|
2146
|
-
case 4:
|
|
2147
|
-
return false;
|
|
2148
|
-
case 5:
|
|
2149
|
-
return false;
|
|
2150
|
-
default:
|
|
2151
|
-
ensureClipSave(rCtx);
|
|
2152
|
-
clipFn();
|
|
2153
|
-
return true;
|
|
2154
|
-
}
|
|
2653
|
+
function pathClipShape(path, m) {
|
|
2654
|
+
return { cmds: emfPlusPathToClipCmds(path.path, m), fillRule: "nonzero", simple: true };
|
|
2155
2655
|
}
|
|
2156
|
-
var
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2656
|
+
var REGION_NODE_OPS = {
|
|
2657
|
+
0: "intersect",
|
|
2658
|
+
// legacy/lenient: treat 0 as And
|
|
2659
|
+
1: "intersect",
|
|
2660
|
+
// RegionNodeDataTypeAnd
|
|
2661
|
+
2: "union",
|
|
2662
|
+
// RegionNodeDataTypeOr
|
|
2663
|
+
3: "xor",
|
|
2664
|
+
// RegionNodeDataTypeXor
|
|
2665
|
+
4: "exclude",
|
|
2666
|
+
// RegionNodeDataTypeExclude
|
|
2667
|
+
5: "complement"
|
|
2668
|
+
// RegionNodeDataTypeComplement
|
|
2669
|
+
};
|
|
2670
|
+
var MAX_REGION_FLATTEN_DEPTH = 64;
|
|
2671
|
+
function flattenRegionNode(node, m, depth = 0) {
|
|
2672
|
+
if (depth > MAX_REGION_FLATTEN_DEPTH) {
|
|
2673
|
+
return { region: [emptyClipShape()], exact: false };
|
|
2161
2674
|
}
|
|
2162
2675
|
switch (node.type) {
|
|
2163
2676
|
case "rect":
|
|
2164
|
-
|
|
2165
|
-
break;
|
|
2677
|
+
return { region: [transformedRectShape(node.x, node.y, node.width, node.height, m)], exact: true };
|
|
2166
2678
|
case "path":
|
|
2167
|
-
|
|
2168
|
-
break;
|
|
2679
|
+
return { region: [pathClipShape(node, m)], exact: true };
|
|
2169
2680
|
case "infinite":
|
|
2170
|
-
|
|
2171
|
-
break;
|
|
2681
|
+
return { region: null, exact: true };
|
|
2172
2682
|
case "empty":
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
} else {
|
|
2182
|
-
try {
|
|
2183
|
-
ctx.clip();
|
|
2184
|
-
} catch {
|
|
2185
|
-
}
|
|
2186
|
-
ctx.beginPath();
|
|
2187
|
-
traceRegionNodePath(ctx, node.right, depth + 1);
|
|
2188
|
-
}
|
|
2189
|
-
break;
|
|
2683
|
+
return { region: [emptyClipShape()], exact: true };
|
|
2684
|
+
case "combine": {
|
|
2685
|
+
const left = flattenRegionNode(node.left, m, depth + 1);
|
|
2686
|
+
const right = flattenRegionNode(node.right, m, depth + 1);
|
|
2687
|
+
const op = REGION_NODE_OPS[node.combineMode] ?? "intersect";
|
|
2688
|
+
const combined = combineClipRegions(left.region, right.region, op);
|
|
2689
|
+
return { region: combined.region, exact: combined.exact && left.exact && right.exact };
|
|
2690
|
+
}
|
|
2190
2691
|
}
|
|
2191
2692
|
}
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2693
|
+
var PLUS_COMBINE_OPS = {
|
|
2694
|
+
0: "replace",
|
|
2695
|
+
1: "intersect",
|
|
2696
|
+
2: "union",
|
|
2697
|
+
3: "xor",
|
|
2698
|
+
4: "exclude",
|
|
2699
|
+
5: "complement"
|
|
2700
|
+
};
|
|
2701
|
+
function reapplyPlusClip(rCtx) {
|
|
2702
|
+
reapplyClipRegion(rCtx, rCtx.clipRegion ?? null, true);
|
|
2703
|
+
}
|
|
2704
|
+
function applyPlusClipRegion(rCtx, incoming, combineMode, opName) {
|
|
2705
|
+
const op = PLUS_COMBINE_OPS[combineMode];
|
|
2706
|
+
const res = combineClipRegions(rCtx.clipRegion ?? null, incoming, op ?? "intersect");
|
|
2707
|
+
if (!res.exact) ;
|
|
2708
|
+
rCtx.clipRegion = res.region;
|
|
2709
|
+
reapplyPlusClip(rCtx);
|
|
2710
|
+
}
|
|
2711
|
+
function applyPlusClipShape(rCtx, shape, combineMode, opName) {
|
|
2712
|
+
applyPlusClipRegion(rCtx, [shape], combineMode);
|
|
2199
2713
|
}
|
|
2200
2714
|
function handleEmfPlusStateRecord(rCtx, recType, recFlags, dataOff, recDataSize) {
|
|
2201
2715
|
const { view } = rCtx;
|
|
@@ -2297,30 +2811,24 @@ function handleEmfPlusStateRecord(rCtx, recType, recFlags, dataOff, recDataSize)
|
|
|
2297
2811
|
const cy = view.getFloat32(dataOff + 4, true);
|
|
2298
2812
|
const cw = view.getFloat32(dataOff + 8, true);
|
|
2299
2813
|
const ch = view.getFloat32(dataOff + 12, true);
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
rCtx.ctx.beginPath();
|
|
2303
|
-
rCtx.ctx.rect(cx, cy, cw, ch);
|
|
2304
|
-
try {
|
|
2305
|
-
rCtx.ctx.clip();
|
|
2306
|
-
} catch {
|
|
2307
|
-
}
|
|
2308
|
-
});
|
|
2814
|
+
const shape = transformedRectShape(cx, cy, cw, ch, plusDeviceMatrix(rCtx));
|
|
2815
|
+
applyPlusClipShape(rCtx, shape, combineMode);
|
|
2309
2816
|
}
|
|
2310
2817
|
return true;
|
|
2311
2818
|
}
|
|
2312
2819
|
case EMFPLUS_RESETCLIP: {
|
|
2313
|
-
|
|
2314
|
-
|
|
2820
|
+
rCtx.clipRegion = null;
|
|
2821
|
+
reapplyPlusClip(rCtx);
|
|
2315
2822
|
return true;
|
|
2316
2823
|
}
|
|
2317
2824
|
case EMFPLUS_SETCLIPREGION: {
|
|
2318
2825
|
const regionId = recFlags & 255;
|
|
2826
|
+
const combineMode = recFlags >> 8 & 15;
|
|
2319
2827
|
const regionObj = rCtx.objectTable.get(regionId);
|
|
2320
2828
|
if (regionObj && regionObj.kind === "plus-region" && regionObj.nodes.length > 0) {
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2829
|
+
const flattened = flattenRegionNode(regionObj.nodes[0], plusDeviceMatrix(rCtx));
|
|
2830
|
+
if (!flattened.exact) ;
|
|
2831
|
+
applyPlusClipRegion(rCtx, flattened.region, combineMode);
|
|
2324
2832
|
}
|
|
2325
2833
|
return true;
|
|
2326
2834
|
}
|
|
@@ -2329,22 +2837,26 @@ function handleEmfPlusStateRecord(rCtx, recType, recFlags, dataOff, recDataSize)
|
|
|
2329
2837
|
const combineMode = recFlags >> 8 & 15;
|
|
2330
2838
|
const pathObj = rCtx.objectTable.get(pathId);
|
|
2331
2839
|
if (pathObj && pathObj.kind === "plus-path") {
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
} catch {
|
|
2339
|
-
}
|
|
2340
|
-
});
|
|
2840
|
+
const shape = {
|
|
2841
|
+
cmds: emfPlusPathToClipCmds(pathObj, plusDeviceMatrix(rCtx)),
|
|
2842
|
+
fillRule: "nonzero",
|
|
2843
|
+
simple: true
|
|
2844
|
+
};
|
|
2845
|
+
applyPlusClipShape(rCtx, shape, combineMode);
|
|
2341
2846
|
}
|
|
2342
2847
|
return true;
|
|
2343
2848
|
}
|
|
2344
2849
|
case EMFPLUS_OFFSETCLIP: {
|
|
2345
2850
|
if (recDataSize >= 8) {
|
|
2346
|
-
view.getFloat32(dataOff, true);
|
|
2347
|
-
view.getFloat32(dataOff + 4, true);
|
|
2851
|
+
const dx = view.getFloat32(dataOff, true);
|
|
2852
|
+
const dy = view.getFloat32(dataOff + 4, true);
|
|
2853
|
+
if (rCtx.clipRegion) {
|
|
2854
|
+
const m = plusDeviceMatrix(rCtx);
|
|
2855
|
+
const ddx = m[0] * dx + m[2] * dy;
|
|
2856
|
+
const ddy = m[1] * dx + m[3] * dy;
|
|
2857
|
+
rCtx.clipRegion = translateClipRegion(rCtx.clipRegion, ddx, ddy);
|
|
2858
|
+
reapplyPlusClip(rCtx);
|
|
2859
|
+
}
|
|
2348
2860
|
}
|
|
2349
2861
|
return true;
|
|
2350
2862
|
}
|
|
@@ -2417,7 +2929,7 @@ function handleEmfPlusDrawRecord(rCtx, recType, recFlags, dataOff, recDataSize)
|
|
|
2417
2929
|
const count = view.getUint32(dataOff + 4, true);
|
|
2418
2930
|
const compressed = (recFlags & 16384) !== 0;
|
|
2419
2931
|
const rectSize = compressed ? 8 : 16;
|
|
2420
|
-
ctx.fillStyle =
|
|
2932
|
+
ctx.fillStyle = resolveBrushPaint(rCtx, recFlags, brushVal);
|
|
2421
2933
|
applyPlusWorldTransform(rCtx);
|
|
2422
2934
|
let rOff = dataOff + 8;
|
|
2423
2935
|
for (let i = 0; i < count && rOff + rectSize <= dataOff + recDataSize; i++) {
|
|
@@ -2467,7 +2979,7 @@ function handleEmfPlusDrawRecord(rCtx, recType, recFlags, dataOff, recDataSize)
|
|
|
2467
2979
|
w = view.getFloat32(dataOff + 12, true);
|
|
2468
2980
|
h = view.getFloat32(dataOff + 16, true);
|
|
2469
2981
|
}
|
|
2470
|
-
ctx.fillStyle =
|
|
2982
|
+
ctx.fillStyle = resolveBrushPaint(rCtx, recFlags, brushVal);
|
|
2471
2983
|
applyPlusWorldTransform(rCtx);
|
|
2472
2984
|
ctx.beginPath();
|
|
2473
2985
|
ctx.ellipse(x + w / 2, y + h / 2, Math.abs(w) / 2, Math.abs(h) / 2, 0, 0, Math.PI * 2);
|
|
@@ -2513,7 +3025,7 @@ function handleEmfPlusDrawRecord(rCtx, recType, recFlags, dataOff, recDataSize)
|
|
|
2513
3025
|
let aOff = dataOff;
|
|
2514
3026
|
if (isFill) {
|
|
2515
3027
|
const brushVal = view.getUint32(aOff, true);
|
|
2516
|
-
ctx.fillStyle =
|
|
3028
|
+
ctx.fillStyle = resolveBrushPaint(rCtx, recFlags, brushVal);
|
|
2517
3029
|
aOff += 4;
|
|
2518
3030
|
}
|
|
2519
3031
|
const startAngle = view.getFloat32(aOff, true) * Math.PI / 180;
|
|
@@ -2594,7 +3106,7 @@ function handleEmfPlusDrawRecord(rCtx, recType, recFlags, dataOff, recDataSize)
|
|
|
2594
3106
|
const count = view.getUint32(dataOff + 4, true);
|
|
2595
3107
|
const compressed = (recFlags & 16384) !== 0;
|
|
2596
3108
|
const ptSize = compressed ? 4 : 8;
|
|
2597
|
-
ctx.fillStyle =
|
|
3109
|
+
ctx.fillStyle = resolveBrushPaint(rCtx, recFlags, brushVal);
|
|
2598
3110
|
applyPlusWorldTransform(rCtx);
|
|
2599
3111
|
ctx.beginPath();
|
|
2600
3112
|
let pOff = dataOff + 8;
|
|
@@ -2617,6 +3129,261 @@ function handleEmfPlusDrawRecord(rCtx, recType, recFlags, dataOff, recDataSize)
|
|
|
2617
3129
|
}
|
|
2618
3130
|
}
|
|
2619
3131
|
|
|
3132
|
+
// src/emf-plus-brush-parser.ts
|
|
3133
|
+
var BRUSH_DATA_PATH = 1;
|
|
3134
|
+
var BRUSH_DATA_TRANSFORM = 2;
|
|
3135
|
+
var BRUSH_DATA_PRESET_COLORS = 4;
|
|
3136
|
+
var BRUSH_DATA_BLEND_FACTORS_H = 8;
|
|
3137
|
+
var MAX_GRADIENT_ELEMENTS = 4096;
|
|
3138
|
+
function looksLikeGraphicsVersion(v) {
|
|
3139
|
+
return v >>> 12 === 900097;
|
|
3140
|
+
}
|
|
3141
|
+
function readTransform(view, off) {
|
|
3142
|
+
return [
|
|
3143
|
+
view.getFloat32(off, true),
|
|
3144
|
+
view.getFloat32(off + 4, true),
|
|
3145
|
+
view.getFloat32(off + 8, true),
|
|
3146
|
+
view.getFloat32(off + 12, true),
|
|
3147
|
+
view.getFloat32(off + 16, true),
|
|
3148
|
+
view.getFloat32(off + 20, true)
|
|
3149
|
+
];
|
|
3150
|
+
}
|
|
3151
|
+
function applyMatrix(m, x, y) {
|
|
3152
|
+
return { x: m[0] * x + m[2] * y + m[4], y: m[1] * x + m[3] * y + m[5] };
|
|
3153
|
+
}
|
|
3154
|
+
function clamp01(v) {
|
|
3155
|
+
return Number.isFinite(v) ? Math.min(1, Math.max(0, v)) : 0;
|
|
3156
|
+
}
|
|
3157
|
+
function normaliseStops(stops) {
|
|
3158
|
+
return stops.map((s) => ({ offset: clamp01(s.offset), color: s.color })).sort((a, b) => a.offset - b.offset);
|
|
3159
|
+
}
|
|
3160
|
+
function readPresetColors(view, off, end) {
|
|
3161
|
+
if (off + 4 > end) {
|
|
3162
|
+
return null;
|
|
3163
|
+
}
|
|
3164
|
+
const count = view.getUint32(off, true);
|
|
3165
|
+
if (count === 0 || count > MAX_GRADIENT_ELEMENTS) {
|
|
3166
|
+
return null;
|
|
3167
|
+
}
|
|
3168
|
+
const posOff = off + 4;
|
|
3169
|
+
const colOff = posOff + count * 4;
|
|
3170
|
+
const next = colOff + count * 4;
|
|
3171
|
+
if (next > end) {
|
|
3172
|
+
return null;
|
|
3173
|
+
}
|
|
3174
|
+
const stops = [];
|
|
3175
|
+
for (let i = 0; i < count; i++) {
|
|
3176
|
+
stops.push({
|
|
3177
|
+
offset: view.getFloat32(posOff + i * 4, true),
|
|
3178
|
+
color: argbToRgba(view.getUint32(colOff + i * 4, true))
|
|
3179
|
+
});
|
|
3180
|
+
}
|
|
3181
|
+
return { stops, next };
|
|
3182
|
+
}
|
|
3183
|
+
function readBlendFactors(view, off, end) {
|
|
3184
|
+
if (off + 4 > end) {
|
|
3185
|
+
return null;
|
|
3186
|
+
}
|
|
3187
|
+
const count = view.getUint32(off, true);
|
|
3188
|
+
if (count === 0 || count > MAX_GRADIENT_ELEMENTS) {
|
|
3189
|
+
return null;
|
|
3190
|
+
}
|
|
3191
|
+
const posOff = off + 4;
|
|
3192
|
+
const facOff = posOff + count * 4;
|
|
3193
|
+
const next = facOff + count * 4;
|
|
3194
|
+
if (next > end) {
|
|
3195
|
+
return null;
|
|
3196
|
+
}
|
|
3197
|
+
const entries = [];
|
|
3198
|
+
for (let i = 0; i < count; i++) {
|
|
3199
|
+
entries.push({
|
|
3200
|
+
pos: view.getFloat32(posOff + i * 4, true),
|
|
3201
|
+
factor: view.getFloat32(facOff + i * 4, true)
|
|
3202
|
+
});
|
|
3203
|
+
}
|
|
3204
|
+
return { entries, next };
|
|
3205
|
+
}
|
|
3206
|
+
function parseLinearGradient(view, b, end) {
|
|
3207
|
+
if (b + 40 > end) {
|
|
3208
|
+
return null;
|
|
3209
|
+
}
|
|
3210
|
+
const flags = view.getUint32(b, true);
|
|
3211
|
+
const rx = view.getFloat32(b + 8, true);
|
|
3212
|
+
const ry = view.getFloat32(b + 12, true);
|
|
3213
|
+
const rw = view.getFloat32(b + 16, true);
|
|
3214
|
+
const rh = view.getFloat32(b + 20, true);
|
|
3215
|
+
const startArgb = view.getUint32(b + 24, true);
|
|
3216
|
+
const endArgb = view.getUint32(b + 28, true);
|
|
3217
|
+
let o = b + 40;
|
|
3218
|
+
let transform = null;
|
|
3219
|
+
if (flags & BRUSH_DATA_TRANSFORM && o + 24 <= end) {
|
|
3220
|
+
transform = readTransform(view, o);
|
|
3221
|
+
o += 24;
|
|
3222
|
+
}
|
|
3223
|
+
let stops = [
|
|
3224
|
+
{ offset: 0, color: argbToRgba(startArgb) },
|
|
3225
|
+
{ offset: 1, color: argbToRgba(endArgb) }
|
|
3226
|
+
];
|
|
3227
|
+
if (flags & BRUSH_DATA_PRESET_COLORS) {
|
|
3228
|
+
const preset = readPresetColors(view, o, end);
|
|
3229
|
+
if (preset) {
|
|
3230
|
+
stops = preset.stops;
|
|
3231
|
+
}
|
|
3232
|
+
} else if (flags & BRUSH_DATA_BLEND_FACTORS_H) {
|
|
3233
|
+
const blend = readBlendFactors(view, o, end);
|
|
3234
|
+
if (blend) {
|
|
3235
|
+
stops = blend.entries.map((e) => ({
|
|
3236
|
+
offset: e.pos,
|
|
3237
|
+
color: lerpArgbToRgba(startArgb, endArgb, e.factor)
|
|
3238
|
+
}));
|
|
3239
|
+
}
|
|
3240
|
+
}
|
|
3241
|
+
let p1 = { x: rx, y: ry + rh / 2 };
|
|
3242
|
+
let p2 = { x: rx + rw, y: ry + rh / 2 };
|
|
3243
|
+
if (transform) {
|
|
3244
|
+
p1 = applyMatrix(transform, p1.x, p1.y);
|
|
3245
|
+
p2 = applyMatrix(transform, p2.x, p2.y);
|
|
3246
|
+
}
|
|
3247
|
+
emfLog(
|
|
3248
|
+
`parseEmfPlusBrushObject: linear gradient (${p1.x.toFixed(1)},${p1.y.toFixed(1)})\u2192(${p2.x.toFixed(1)},${p2.y.toFixed(1)}), ${stops.length} stop(s)`
|
|
3249
|
+
);
|
|
3250
|
+
return {
|
|
3251
|
+
kind: "plus-brush",
|
|
3252
|
+
color: argbToRgba(startArgb),
|
|
3253
|
+
gradient: {
|
|
3254
|
+
type: "linear",
|
|
3255
|
+
x1: p1.x,
|
|
3256
|
+
y1: p1.y,
|
|
3257
|
+
x2: p2.x,
|
|
3258
|
+
y2: p2.y,
|
|
3259
|
+
stops: normaliseStops(stops)
|
|
3260
|
+
}
|
|
3261
|
+
};
|
|
3262
|
+
}
|
|
3263
|
+
function parsePathGradient(view, b, end) {
|
|
3264
|
+
if (b + 24 > end) {
|
|
3265
|
+
return null;
|
|
3266
|
+
}
|
|
3267
|
+
const flags = view.getUint32(b, true);
|
|
3268
|
+
const centerArgb = view.getUint32(b + 8, true);
|
|
3269
|
+
let cx = view.getFloat32(b + 12, true);
|
|
3270
|
+
let cy = view.getFloat32(b + 16, true);
|
|
3271
|
+
const surroundCount = view.getUint32(b + 20, true);
|
|
3272
|
+
if (surroundCount > MAX_GRADIENT_ELEMENTS) {
|
|
3273
|
+
return { kind: "plus-brush", color: argbToRgba(centerArgb) };
|
|
3274
|
+
}
|
|
3275
|
+
const surround = [];
|
|
3276
|
+
let o = b + 24;
|
|
3277
|
+
for (let i = 0; i < surroundCount && o + 4 <= end; i++) {
|
|
3278
|
+
surround.push(view.getUint32(o, true));
|
|
3279
|
+
o += 4;
|
|
3280
|
+
}
|
|
3281
|
+
let boundaryPts = [];
|
|
3282
|
+
if (flags & BRUSH_DATA_PATH) {
|
|
3283
|
+
if (o + 4 <= end) {
|
|
3284
|
+
const pathSize = view.getInt32(o, true);
|
|
3285
|
+
o += 4;
|
|
3286
|
+
if (pathSize > 0 && o + pathSize <= end) {
|
|
3287
|
+
const path = parseEmfPlusPath(view, o, pathSize);
|
|
3288
|
+
if (path) {
|
|
3289
|
+
boundaryPts = path.points;
|
|
3290
|
+
}
|
|
3291
|
+
o += pathSize;
|
|
3292
|
+
}
|
|
3293
|
+
}
|
|
3294
|
+
} else if (o + 4 <= end) {
|
|
3295
|
+
const ptCount = view.getUint32(o, true);
|
|
3296
|
+
o += 4;
|
|
3297
|
+
if (ptCount > 0 && ptCount <= MAX_GRADIENT_ELEMENTS && o + ptCount * 8 <= end) {
|
|
3298
|
+
for (let i = 0; i < ptCount; i++) {
|
|
3299
|
+
boundaryPts.push({
|
|
3300
|
+
x: view.getFloat32(o + i * 8, true),
|
|
3301
|
+
y: view.getFloat32(o + i * 8 + 4, true)
|
|
3302
|
+
});
|
|
3303
|
+
}
|
|
3304
|
+
o += ptCount * 8;
|
|
3305
|
+
}
|
|
3306
|
+
}
|
|
3307
|
+
let transform = null;
|
|
3308
|
+
if (flags & BRUSH_DATA_TRANSFORM && o + 24 <= end) {
|
|
3309
|
+
transform = readTransform(view, o);
|
|
3310
|
+
o += 24;
|
|
3311
|
+
}
|
|
3312
|
+
if (transform) {
|
|
3313
|
+
({ x: cx, y: cy } = applyMatrix(transform, cx, cy));
|
|
3314
|
+
boundaryPts = boundaryPts.map((p) => applyMatrix(transform, p.x, p.y));
|
|
3315
|
+
}
|
|
3316
|
+
const surroundArgb = surround.length > 0 ? surround[0] : centerArgb;
|
|
3317
|
+
let stops = [
|
|
3318
|
+
{ offset: 0, color: argbToRgba(centerArgb) },
|
|
3319
|
+
{ offset: 1, color: argbToRgba(surroundArgb) }
|
|
3320
|
+
];
|
|
3321
|
+
if (flags & BRUSH_DATA_PRESET_COLORS) {
|
|
3322
|
+
const preset = readPresetColors(view, o, end);
|
|
3323
|
+
if (preset) {
|
|
3324
|
+
stops = preset.stops.map((s) => ({ offset: 1 - s.offset, color: s.color }));
|
|
3325
|
+
}
|
|
3326
|
+
} else if (flags & BRUSH_DATA_BLEND_FACTORS_H) {
|
|
3327
|
+
const blend = readBlendFactors(view, o, end);
|
|
3328
|
+
if (blend) {
|
|
3329
|
+
stops = blend.entries.map((e) => ({
|
|
3330
|
+
offset: 1 - e.pos,
|
|
3331
|
+
color: lerpArgbToRgba(surroundArgb, centerArgb, e.factor)
|
|
3332
|
+
}));
|
|
3333
|
+
}
|
|
3334
|
+
}
|
|
3335
|
+
let r = 0;
|
|
3336
|
+
for (const p of boundaryPts) {
|
|
3337
|
+
const d = Math.hypot(p.x - cx, p.y - cy);
|
|
3338
|
+
if (d > r) {
|
|
3339
|
+
r = d;
|
|
3340
|
+
}
|
|
3341
|
+
}
|
|
3342
|
+
if (!(r > 0)) {
|
|
3343
|
+
return { kind: "plus-brush", color: argbToRgba(centerArgb) };
|
|
3344
|
+
}
|
|
3345
|
+
emfLog(
|
|
3346
|
+
`parseEmfPlusBrushObject: path gradient centre=(${cx.toFixed(1)},${cy.toFixed(1)}), r=${r.toFixed(1)}, ${stops.length} stop(s)`
|
|
3347
|
+
);
|
|
3348
|
+
return {
|
|
3349
|
+
kind: "plus-brush",
|
|
3350
|
+
color: argbToRgba(centerArgb),
|
|
3351
|
+
gradient: { type: "radial", cx, cy, r, stops: normaliseStops(stops) }
|
|
3352
|
+
};
|
|
3353
|
+
}
|
|
3354
|
+
function parseEmfPlusBrushObject(view, dataOff, recDataSize) {
|
|
3355
|
+
if (recDataSize < 8) {
|
|
3356
|
+
return null;
|
|
3357
|
+
}
|
|
3358
|
+
const end = dataOff + recDataSize;
|
|
3359
|
+
const hasVersion = looksLikeGraphicsVersion(view.getUint32(dataOff, true));
|
|
3360
|
+
const typeOff = dataOff + (hasVersion ? 4 : 0);
|
|
3361
|
+
if (typeOff + 8 > end) {
|
|
3362
|
+
return null;
|
|
3363
|
+
}
|
|
3364
|
+
const brushType = view.getUint32(typeOff, true);
|
|
3365
|
+
const b = typeOff + 4;
|
|
3366
|
+
switch (brushType) {
|
|
3367
|
+
case EMFPLUS_BRUSHTYPE_SOLID:
|
|
3368
|
+
return { kind: "plus-brush", color: argbToRgba(view.getUint32(b, true)) };
|
|
3369
|
+
case EMFPLUS_BRUSHTYPE_HATCHFILL:
|
|
3370
|
+
if (b + 8 <= end) {
|
|
3371
|
+
return { kind: "plus-brush", color: argbToRgba(view.getUint32(b + 4, true)) };
|
|
3372
|
+
}
|
|
3373
|
+
return { kind: "plus-brush", color: "rgba(0,0,0,1)" };
|
|
3374
|
+
case EMFPLUS_BRUSHTYPE_LINEARGRADIENT: {
|
|
3375
|
+
const brush = parseLinearGradient(view, b, end);
|
|
3376
|
+
return brush ?? { kind: "plus-brush", color: "rgba(0,0,0,1)" };
|
|
3377
|
+
}
|
|
3378
|
+
case EMFPLUS_BRUSHTYPE_PATHGRADIENT: {
|
|
3379
|
+
const brush = parsePathGradient(view, b, end);
|
|
3380
|
+
return brush ?? { kind: "plus-brush", color: "rgba(0,0,0,1)" };
|
|
3381
|
+
}
|
|
3382
|
+
default:
|
|
3383
|
+
return { kind: "plus-brush", color: "rgba(0,0,0,1)" };
|
|
3384
|
+
}
|
|
3385
|
+
}
|
|
3386
|
+
|
|
2620
3387
|
// src/emf-plus-bitmap-decoder.ts
|
|
2621
3388
|
var PIXELFORMAT_24BPP_RGB = 137224;
|
|
2622
3389
|
var PIXELFORMAT_32BPP_RGB = 139273;
|
|
@@ -2726,7 +3493,8 @@ function parseEmfPlusPenObject(view, dataOff, recDataSize) {
|
|
|
2726
3493
|
if (recDataSize < 20) {
|
|
2727
3494
|
return null;
|
|
2728
3495
|
}
|
|
2729
|
-
const
|
|
3496
|
+
const hasVersion = looksLikeGraphicsVersion(view.getUint32(dataOff, true));
|
|
3497
|
+
const penFlags = view.getUint32(dataOff + (hasVersion ? 8 : 4), true);
|
|
2730
3498
|
const penWidth = view.getFloat32(dataOff + 16, true);
|
|
2731
3499
|
let brushOff = dataOff + 20;
|
|
2732
3500
|
const flagSizes = [
|
|
@@ -2786,9 +3554,9 @@ function parseEmfPlusPenObject(view, dataOff, recDataSize) {
|
|
|
2786
3554
|
}
|
|
2787
3555
|
let penColor = "rgba(0,0,0,1)";
|
|
2788
3556
|
if (brushOff + 8 <= dataOff + recDataSize) {
|
|
2789
|
-
const
|
|
2790
|
-
if (
|
|
2791
|
-
penColor =
|
|
3557
|
+
const brush = parseEmfPlusBrushObject(view, brushOff, dataOff + recDataSize - brushOff);
|
|
3558
|
+
if (brush) {
|
|
3559
|
+
penColor = brush.color;
|
|
2792
3560
|
}
|
|
2793
3561
|
}
|
|
2794
3562
|
return { kind: "plus-pen", color: penColor, width: penWidth || 1, dashStyle };
|
|
@@ -2884,19 +3652,9 @@ function handleEmfPlusObjectRecord(rCtx, recFlags, dataOff, recDataSize) {
|
|
|
2884
3652
|
// Brush
|
|
2885
3653
|
// ---------------------------------------------------------------
|
|
2886
3654
|
case EMFPLUS_OBJECTTYPE_BRUSH: {
|
|
2887
|
-
|
|
2888
|
-
|
|
2889
|
-
|
|
2890
|
-
if (brushType === EMFPLUS_BRUSHTYPE_SOLID && recDataSize >= 8) {
|
|
2891
|
-
color = argbToRgba(view.getUint32(dataOff + 4, true));
|
|
2892
|
-
} else if (brushType === EMFPLUS_BRUSHTYPE_LINEARGRADIENT && recDataSize >= 48) {
|
|
2893
|
-
color = argbToRgba(view.getUint32(dataOff + 40, true));
|
|
2894
|
-
} else if (brushType === EMFPLUS_BRUSHTYPE_PATHGRADIENT && recDataSize >= 12) {
|
|
2895
|
-
color = argbToRgba(view.getUint32(dataOff + 8, true));
|
|
2896
|
-
} else if (brushType === EMFPLUS_BRUSHTYPE_HATCHFILL && recDataSize >= 12) {
|
|
2897
|
-
color = argbToRgba(view.getUint32(dataOff + 8, true));
|
|
2898
|
-
}
|
|
2899
|
-
objectTable.set(objectId, { kind: "plus-brush", color });
|
|
3655
|
+
const brush = parseEmfPlusBrushObject(view, dataOff, recDataSize);
|
|
3656
|
+
if (brush) {
|
|
3657
|
+
objectTable.set(objectId, brush);
|
|
2900
3658
|
}
|
|
2901
3659
|
break;
|
|
2902
3660
|
}
|
|
@@ -2992,7 +3750,7 @@ function parseRegionNode(view, off, endOff, depth = 0) {
|
|
|
2992
3750
|
}
|
|
2993
3751
|
const nodeType = view.getUint32(off, true);
|
|
2994
3752
|
let cursor = off + 4;
|
|
2995
|
-
if (nodeType <=
|
|
3753
|
+
if (nodeType <= 5) {
|
|
2996
3754
|
const leftResult = parseRegionNode(view, cursor, endOff, depth + 1);
|
|
2997
3755
|
if (!leftResult) {
|
|
2998
3756
|
return null;
|
|
@@ -3081,7 +3839,7 @@ function handleEmfPlusTextImageRecord(rCtx, recType, recFlags, dataOff, recDataS
|
|
|
3081
3839
|
const pathId = recFlags & 255;
|
|
3082
3840
|
const pathObj = objectTable.get(pathId);
|
|
3083
3841
|
if (pathObj && pathObj.kind === "plus-path") {
|
|
3084
|
-
ctx.fillStyle =
|
|
3842
|
+
ctx.fillStyle = resolveBrushPaint(rCtx, recFlags, brushVal);
|
|
3085
3843
|
applyPlusWorldTransform(rCtx);
|
|
3086
3844
|
replayEmfPlusPath(ctx, pathObj);
|
|
3087
3845
|
ctx.fill();
|
|
@@ -3124,8 +3882,9 @@ function handleEmfPlusTextImageRecord(rCtx, recType, recFlags, dataOff, recDataS
|
|
|
3124
3882
|
if (text.length > 0 && font && font.kind === "plus-font") {
|
|
3125
3883
|
const bold = font.flags & 1 ? "bold " : "";
|
|
3126
3884
|
const italic = font.flags & 2 ? "italic " : "";
|
|
3127
|
-
|
|
3128
|
-
ctx.
|
|
3885
|
+
const family = mapFontFamily(font.family, rCtx.fontFamilyMap);
|
|
3886
|
+
ctx.font = `${italic}${bold}${font.emSize}px ${family}`;
|
|
3887
|
+
ctx.fillStyle = resolveBrushPaint(rCtx, recFlags, brushVal);
|
|
3129
3888
|
ctx.textBaseline = "top";
|
|
3130
3889
|
const sf = objectTable.get(formatId);
|
|
3131
3890
|
if (sf && sf.kind === "plus-stringformat") {
|
|
@@ -3163,8 +3922,9 @@ function handleEmfPlusTextImageRecord(rCtx, recType, recFlags, dataOff, recDataS
|
|
|
3163
3922
|
if (text.length > 0) {
|
|
3164
3923
|
const bold = font.flags & 1 ? "bold " : "";
|
|
3165
3924
|
const italic = font.flags & 2 ? "italic " : "";
|
|
3166
|
-
|
|
3167
|
-
ctx.
|
|
3925
|
+
const family = mapFontFamily(font.family, rCtx.fontFamilyMap);
|
|
3926
|
+
ctx.font = `${italic}${bold}${font.emSize}px ${family}`;
|
|
3927
|
+
ctx.fillStyle = resolveBrushPaint(rCtx, recFlags, brushVal);
|
|
3168
3928
|
ctx.textBaseline = "alphabetic";
|
|
3169
3929
|
ctx.textAlign = "left";
|
|
3170
3930
|
applyPlusWorldTransform(rCtx);
|
|
@@ -3329,7 +4089,7 @@ var EMFPLUS_REC_NAMES = {
|
|
|
3329
4089
|
16424: "BeginContainerNoParams",
|
|
3330
4090
|
16425: "EndContainer"
|
|
3331
4091
|
};
|
|
3332
|
-
function replayEmfPlusRecords(view, offset, length, ctx, _canvasW, _canvasH, state, dpiScale = 1) {
|
|
4092
|
+
function replayEmfPlusRecords(view, offset, length, ctx, _canvasW, _canvasH, state, dpiScale = 1, maxRecords = MAX_RECORDS_EMFPLUS_DEFAULT, fontFamilyMap) {
|
|
3333
4093
|
const s = state ?? createEmfPlusState();
|
|
3334
4094
|
const rCtx = {
|
|
3335
4095
|
ctx,
|
|
@@ -3341,7 +4101,8 @@ function replayEmfPlusRecords(view, offset, length, ctx, _canvasW, _canvasH, sta
|
|
|
3341
4101
|
saveIdMap: s.saveIdMap,
|
|
3342
4102
|
totalImageObjects: 0,
|
|
3343
4103
|
totalDrawImageCalls: 0,
|
|
3344
|
-
clipSaveDepth:
|
|
4104
|
+
clipSaveDepth: s.clipSaveDepth,
|
|
4105
|
+
clipRegion: s.clipRegion,
|
|
3345
4106
|
pageUnit: 2,
|
|
3346
4107
|
pageScale: 1,
|
|
3347
4108
|
continuationBuffer: null,
|
|
@@ -3349,10 +4110,10 @@ function replayEmfPlusRecords(view, offset, length, ctx, _canvasW, _canvasH, sta
|
|
|
3349
4110
|
continuationObjectType: 0,
|
|
3350
4111
|
continuationTotalSize: 0,
|
|
3351
4112
|
continuationOffset: 0,
|
|
3352
|
-
dpiScale
|
|
4113
|
+
dpiScale,
|
|
4114
|
+
fontFamilyMap
|
|
3353
4115
|
};
|
|
3354
4116
|
const end = offset + length;
|
|
3355
|
-
const maxRecords = 5e5;
|
|
3356
4117
|
let recordCount = 0;
|
|
3357
4118
|
const emfPlusRecordTypes = /* @__PURE__ */ new Map();
|
|
3358
4119
|
emfLog(`replayEmfPlusRecords: offset=0x${offset.toString(16)}, length=${length}`);
|
|
@@ -3475,6 +4236,8 @@ function replayEmfPlusRecords(view, offset, length, ctx, _canvasW, _canvasH, sta
|
|
|
3475
4236
|
if (state) {
|
|
3476
4237
|
state.worldTransform = rCtx.worldTransform;
|
|
3477
4238
|
state.saveIdMap = rCtx.saveIdMap;
|
|
4239
|
+
state.clipRegion = rCtx.clipRegion ?? null;
|
|
4240
|
+
state.clipSaveDepth = rCtx.clipSaveDepth;
|
|
3478
4241
|
}
|
|
3479
4242
|
ctx.setTransform(1, 0, 0, 1, 0, 0);
|
|
3480
4243
|
return rCtx.deferredImages;
|
|
@@ -3512,12 +4275,14 @@ var GDI_NAMES = {
|
|
|
3512
4275
|
88: "EMR_POLYBEZIERTO16",
|
|
3513
4276
|
91: "EMR_POLYPOLYGON16"
|
|
3514
4277
|
};
|
|
3515
|
-
function replayEmfRecords(view, ctx, bounds, canvasW, canvasH, dpiScale = 1) {
|
|
4278
|
+
function replayEmfRecords(view, ctx, bounds, canvasW, canvasH, dpiScale = 1, replayOptions = {}) {
|
|
3516
4279
|
emfLog(
|
|
3517
4280
|
`replayEmfRecords: bounds=(${bounds.left},${bounds.top})\u2192(${bounds.right},${bounds.bottom}), canvas=${canvasW}\xD7${canvasH}`
|
|
3518
4281
|
);
|
|
3519
4282
|
const allDeferredImages = [];
|
|
3520
4283
|
const emfPlusState = createEmfPlusState();
|
|
4284
|
+
const maxRecords = replayOptions.maxRecords ?? MAX_RECORDS_DEFAULT;
|
|
4285
|
+
const maxRecordsEmfPlus = replayOptions.maxRecordsEmfPlus ?? MAX_RECORDS_EMFPLUS_DEFAULT;
|
|
3521
4286
|
const logicalW = bounds.right - bounds.left || 1;
|
|
3522
4287
|
const logicalH = bounds.bottom - bounds.top || 1;
|
|
3523
4288
|
const sx = canvasW / logicalW;
|
|
@@ -3529,7 +4294,7 @@ function replayEmfRecords(view, ctx, bounds, canvasW, canvasH, dpiScale = 1) {
|
|
|
3529
4294
|
ctx,
|
|
3530
4295
|
view,
|
|
3531
4296
|
objectTable: /* @__PURE__ */ new Map(),
|
|
3532
|
-
state: defaultState(),
|
|
4297
|
+
state: { ...defaultState(), fontFamilyMap: replayOptions.fontFamilyMap },
|
|
3533
4298
|
stateStack: [],
|
|
3534
4299
|
inPath: false,
|
|
3535
4300
|
windowOrg: { x: bounds.left, y: bounds.top },
|
|
@@ -3546,7 +4311,6 @@ function replayEmfRecords(view, ctx, bounds, canvasW, canvasH, dpiScale = 1) {
|
|
|
3546
4311
|
};
|
|
3547
4312
|
let offset = 0;
|
|
3548
4313
|
const maxOffset = view.byteLength;
|
|
3549
|
-
const maxRecords = 2e5;
|
|
3550
4314
|
let recordCount = 0;
|
|
3551
4315
|
let emfPlusCommentCount = 0;
|
|
3552
4316
|
const gdiRecordTypes = /* @__PURE__ */ new Map();
|
|
@@ -3576,7 +4340,9 @@ function replayEmfRecords(view, ctx, bounds, canvasW, canvasH, dpiScale = 1) {
|
|
|
3576
4340
|
canvasW,
|
|
3577
4341
|
canvasH,
|
|
3578
4342
|
emfPlusState,
|
|
3579
|
-
dpiScale
|
|
4343
|
+
dpiScale,
|
|
4344
|
+
maxRecordsEmfPlus,
|
|
4345
|
+
replayOptions.fontFamilyMap
|
|
3580
4346
|
);
|
|
3581
4347
|
emfLog(
|
|
3582
4348
|
`replayEmfRecords: EMF+ comment #${emfPlusCommentCount} returned ${deferred.length} deferred images`
|
|
@@ -3886,7 +4652,7 @@ function createWmfCoord(windowOrg, windowExt, canvasW, canvasH) {
|
|
|
3886
4652
|
mh: (h) => h / (windowExt.cy || 1) * canvasH
|
|
3887
4653
|
};
|
|
3888
4654
|
}
|
|
3889
|
-
function replayWmfRecords(view, ctx, header, canvasW, canvasH) {
|
|
4655
|
+
function replayWmfRecords(view, ctx, header, canvasW, canvasH, replayOptions = {}) {
|
|
3890
4656
|
const logicalW = header.boundsRight - header.boundsLeft || 1;
|
|
3891
4657
|
const logicalH = header.boundsBottom - header.boundsTop || 1;
|
|
3892
4658
|
const windowOrg = { x: header.boundsLeft, y: header.boundsTop };
|
|
@@ -3900,12 +4666,12 @@ function replayWmfRecords(view, ctx, header, canvasW, canvasH) {
|
|
|
3900
4666
|
}
|
|
3901
4667
|
return slot;
|
|
3902
4668
|
};
|
|
3903
|
-
const state = defaultState();
|
|
4669
|
+
const state = { ...defaultState(), fontFamilyMap: replayOptions.fontFamilyMap };
|
|
3904
4670
|
const stateStack = [];
|
|
3905
4671
|
const wCtx = { view, ctx, state, coord };
|
|
3906
4672
|
let offset = header.headerSize;
|
|
3907
4673
|
const maxOffset = view.byteLength;
|
|
3908
|
-
const maxRecords =
|
|
4674
|
+
const maxRecords = replayOptions.maxRecords ?? MAX_RECORDS_DEFAULT;
|
|
3909
4675
|
let recordCount = 0;
|
|
3910
4676
|
while (offset + 6 <= maxOffset && recordCount < maxRecords) {
|
|
3911
4677
|
const recSizeWords = view.getUint32(offset, true);
|
|
@@ -3962,6 +4728,9 @@ function replayWmfRecords(view, ctx, header, canvasW, canvasH) {
|
|
|
3962
4728
|
}
|
|
3963
4729
|
break;
|
|
3964
4730
|
case META_SETROP2:
|
|
4731
|
+
if (recSize >= 8) {
|
|
4732
|
+
state.rop2 = view.getUint16(dataOff, true);
|
|
4733
|
+
}
|
|
3965
4734
|
break;
|
|
3966
4735
|
case META_SETPOLYFILLMODE:
|
|
3967
4736
|
if (recSize >= 8) {
|
|
@@ -4010,6 +4779,8 @@ function replayWmfRecords(view, ctx, header, canvasW, canvasH) {
|
|
|
4010
4779
|
height: Math.abs(view.getInt16(dataOff, true)),
|
|
4011
4780
|
weight: view.getInt16(dataOff + 8, true),
|
|
4012
4781
|
italic: view.getUint8(dataOff + 10) !== 0,
|
|
4782
|
+
underline: view.getUint8(dataOff + 11) !== 0,
|
|
4783
|
+
strikeOut: view.getUint8(dataOff + 12) !== 0,
|
|
4013
4784
|
family: family || "sans-serif"
|
|
4014
4785
|
});
|
|
4015
4786
|
}
|
|
@@ -4032,6 +4803,8 @@ function replayWmfRecords(view, ctx, header, canvasW, canvasH) {
|
|
|
4032
4803
|
state.fontHeight = obj.height;
|
|
4033
4804
|
state.fontWeight = obj.weight;
|
|
4034
4805
|
state.fontItalic = obj.italic;
|
|
4806
|
+
state.fontUnderline = obj.underline;
|
|
4807
|
+
state.fontStrikeOut = obj.strikeOut;
|
|
4035
4808
|
state.fontFamily = obj.family;
|
|
4036
4809
|
break;
|
|
4037
4810
|
}
|
|
@@ -4149,6 +4922,11 @@ async function convertEmfToDataUrl(buffer, maxWidth, maxHeight, optionsOrDpiScal
|
|
|
4149
4922
|
const dpiScale = opts.dpiScale ?? DEFAULT_DPI_SCALE;
|
|
4150
4923
|
const effectiveMaxWidth = maxWidth ?? opts.maxWidth;
|
|
4151
4924
|
const effectiveMaxHeight = maxHeight ?? opts.maxHeight;
|
|
4925
|
+
const replayOptions = {
|
|
4926
|
+
maxRecords: opts.maxRecords,
|
|
4927
|
+
maxRecordsEmfPlus: opts.maxRecords,
|
|
4928
|
+
fontFamilyMap: opts.fontFamilyMap
|
|
4929
|
+
};
|
|
4152
4930
|
try {
|
|
4153
4931
|
emfLog("=== convertEmfToDataUrl START ===");
|
|
4154
4932
|
emfLog(
|
|
@@ -4174,7 +4952,14 @@ async function convertEmfToDataUrl(buffer, maxWidth, maxHeight, optionsOrDpiScal
|
|
|
4174
4952
|
const logicalW = renderBounds.right - renderBounds.left;
|
|
4175
4953
|
const logicalH = renderBounds.bottom - renderBounds.top;
|
|
4176
4954
|
emfLog(`convertEmfToDataUrl: logicalSize=${logicalW}\xD7${logicalH}`);
|
|
4177
|
-
const setup = createCanvas(
|
|
4955
|
+
const setup = createCanvas(
|
|
4956
|
+
logicalW,
|
|
4957
|
+
logicalH,
|
|
4958
|
+
effectiveMaxWidth,
|
|
4959
|
+
effectiveMaxHeight,
|
|
4960
|
+
dpiScale,
|
|
4961
|
+
opts.maxCanvasDimension
|
|
4962
|
+
);
|
|
4178
4963
|
if (!setup) {
|
|
4179
4964
|
emfLog("convertEmfToDataUrl: createCanvas returned null \u2014 returning null");
|
|
4180
4965
|
return null;
|
|
@@ -4191,7 +4976,8 @@ async function convertEmfToDataUrl(buffer, maxWidth, maxHeight, optionsOrDpiScal
|
|
|
4191
4976
|
renderBounds,
|
|
4192
4977
|
canvas.width,
|
|
4193
4978
|
canvas.height,
|
|
4194
|
-
dpiScale
|
|
4979
|
+
dpiScale,
|
|
4980
|
+
replayOptions
|
|
4195
4981
|
);
|
|
4196
4982
|
emfLog(
|
|
4197
4983
|
`convertEmfToDataUrl: replayEmfRecords returned ${deferredImages.length} deferred images`
|
|
@@ -4221,6 +5007,10 @@ async function convertWmfToDataUrl(buffer, maxWidth, maxHeight, optionsOrDpiScal
|
|
|
4221
5007
|
const dpiScale = opts.dpiScale ?? DEFAULT_DPI_SCALE;
|
|
4222
5008
|
const effectiveMaxWidth = maxWidth ?? opts.maxWidth;
|
|
4223
5009
|
const effectiveMaxHeight = maxHeight ?? opts.maxHeight;
|
|
5010
|
+
const replayOptions = {
|
|
5011
|
+
maxRecords: opts.maxRecords,
|
|
5012
|
+
fontFamilyMap: opts.fontFamilyMap
|
|
5013
|
+
};
|
|
4224
5014
|
try {
|
|
4225
5015
|
emfLog(
|
|
4226
5016
|
"=== convertWmfToDataUrl START ===",
|
|
@@ -4239,13 +5029,20 @@ async function convertWmfToDataUrl(buffer, maxWidth, maxHeight, optionsOrDpiScal
|
|
|
4239
5029
|
emfLog("convertWmfToDataUrl: invalid dimensions \u2014 returning null");
|
|
4240
5030
|
return null;
|
|
4241
5031
|
}
|
|
4242
|
-
const setup = createCanvas(
|
|
5032
|
+
const setup = createCanvas(
|
|
5033
|
+
logicalW,
|
|
5034
|
+
logicalH,
|
|
5035
|
+
effectiveMaxWidth,
|
|
5036
|
+
effectiveMaxHeight,
|
|
5037
|
+
dpiScale,
|
|
5038
|
+
opts.maxCanvasDimension
|
|
5039
|
+
);
|
|
4243
5040
|
if (!setup) {
|
|
4244
5041
|
return null;
|
|
4245
5042
|
}
|
|
4246
5043
|
const { canvas, ctx } = setup;
|
|
4247
5044
|
ctx.save();
|
|
4248
|
-
replayWmfRecords(view, ctx, header, canvas.width, canvas.height);
|
|
5045
|
+
replayWmfRecords(view, ctx, header, canvas.width, canvas.height, replayOptions);
|
|
4249
5046
|
ctx.restore();
|
|
4250
5047
|
const result = await exportCanvasToPngDataUrl(canvas);
|
|
4251
5048
|
emfLog(`convertWmfToDataUrl: result=${result ? `dataUrl len=${result.length}` : "null"}`);
|