emf-converter 1.4.2 → 1.5.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/dist/index.js CHANGED
@@ -1,6 +1,166 @@
1
1
  'use strict';
2
2
 
3
- // src/emf-logging.ts
3
+ // src/emf-constants.ts
4
+ var EMR_HEADER = 1;
5
+ var EMR_POLYBEZIER = 2;
6
+ var EMR_POLYGON = 3;
7
+ var EMR_POLYLINE = 4;
8
+ var EMR_POLYBEZIERTO = 5;
9
+ var EMR_POLYLINETO = 6;
10
+ var EMR_POLYPOLYLINE = 7;
11
+ var EMR_POLYPOLYGON = 8;
12
+ var EMR_SETWINDOWEXTEX = 9;
13
+ var EMR_SETWINDOWORGEX = 10;
14
+ var EMR_SETVIEWPORTEXTEX = 11;
15
+ var EMR_SETVIEWPORTORGEX = 12;
16
+ var EMR_SETBRUSHORGEX = 13;
17
+ var EMR_EOF = 14;
18
+ var EMR_SETPIXELV = 15;
19
+ var EMR_SETMAPMODE = 17;
20
+ var EMR_SETBKMODE = 18;
21
+ var EMR_SETPOLYFILLMODE = 19;
22
+ var EMR_SETROP2 = 20;
23
+ var EMR_SETSTRETCHBLTMODE = 21;
24
+ var R2_NOT = 6;
25
+ var R2_XORPEN = 7;
26
+ var R2_MASKPEN = 9;
27
+ var R2_MERGEPEN = 15;
28
+ var MAX_CANVAS_DIMENSION = 8192;
29
+ var MAX_RECORDS_DEFAULT = 2e5;
30
+ var MAX_RECORDS_EMFPLUS_DEFAULT = 5e5;
31
+ var EMR_SETTEXTALIGN = 22;
32
+ var EMR_SETTEXTCOLOR = 24;
33
+ var EMR_SETBKCOLOR = 25;
34
+ var EMR_OFFSETCLIPRGN = 26;
35
+ var EMR_MOVETOEX = 27;
36
+ var EMR_SETMETARGN = 28;
37
+ var EMR_EXCLUDECLIPRECT = 29;
38
+ var EMR_INTERSECTCLIPRECT = 30;
39
+ var EMR_SCALEVIEWPORTEXTEX = 31;
40
+ var EMR_SCALEWINDOWEXTEX = 32;
41
+ var EMR_SAVEDC = 33;
42
+ var EMR_RESTOREDC = 34;
43
+ var EMR_SETWORLDTRANSFORM = 35;
44
+ var EMR_MODIFYWORLDTRANSFORM = 36;
45
+ var EMR_SELECTOBJECT = 37;
46
+ var EMR_CREATEPEN = 38;
47
+ var EMR_CREATEBRUSHINDIRECT = 39;
48
+ var EMR_DELETEOBJECT = 40;
49
+ var EMR_ELLIPSE = 42;
50
+ var EMR_RECTANGLE = 43;
51
+ var EMR_ROUNDRECT = 44;
52
+ var EMR_ARC = 45;
53
+ var EMR_CHORD = 46;
54
+ var EMR_PIE = 47;
55
+ var EMR_LINETO = 54;
56
+ var EMR_ARCTO = 55;
57
+ var EMR_SETMITERLIMIT = 58;
58
+ var EMR_BEGINPATH = 59;
59
+ var EMR_ENDPATH = 60;
60
+ var EMR_CLOSEFIGURE = 61;
61
+ var EMR_FILLPATH = 62;
62
+ var EMR_STROKEANDFILLPATH = 63;
63
+ var EMR_STROKEPATH = 64;
64
+ var EMR_SELECTCLIPPATH = 67;
65
+ var EMR_COMMENT = 70;
66
+ var EMR_EXTSELECTCLIPRGN = 75;
67
+ var EMR_BITBLT = 76;
68
+ var EMR_STRETCHDIBITS = 81;
69
+ var EMR_EXTCREATEFONTINDIRECTW = 82;
70
+ var EMR_EXTTEXTOUTW = 84;
71
+ var EMR_POLYBEZIER16 = 85;
72
+ var EMR_POLYGON16 = 86;
73
+ var EMR_POLYLINE16 = 87;
74
+ var EMR_POLYBEZIERTO16 = 88;
75
+ var EMR_POLYLINETO16 = 89;
76
+ var EMR_POLYPOLYGON16 = 91;
77
+ var EMR_EXTCREATEPEN = 95;
78
+ var EMR_SETICMMODE = 98;
79
+ var EMR_SETLAYOUT = 115;
80
+ var STOCK_OBJECT_BASE = 2147483648;
81
+ var EMFPLUS_SIGNATURE = 726027589;
82
+ var EMR_COMMENT_PUBLIC_SIGNATURE = 1128875079;
83
+ var EMFPLUS_HEADER = 16385;
84
+ var EMFPLUS_ENDOFFILE = 16386;
85
+ var EMFPLUS_GETDC = 16388;
86
+ var EMFPLUS_OBJECT = 16392;
87
+ var EMFPLUS_FILLRECTS = 16394;
88
+ var EMFPLUS_DRAWRECTS = 16395;
89
+ var EMFPLUS_FILLPOLYGON = 16396;
90
+ var EMFPLUS_DRAWLINES = 16397;
91
+ var EMFPLUS_FILLELLIPSE = 16398;
92
+ var EMFPLUS_DRAWELLIPSE = 16399;
93
+ var EMFPLUS_FILLPIE = 16400;
94
+ var EMFPLUS_DRAWPIE = 16401;
95
+ var EMFPLUS_DRAWARC = 16402;
96
+ var EMFPLUS_FILLPATH = 16404;
97
+ var EMFPLUS_DRAWPATH = 16405;
98
+ var EMFPLUS_DRAWIMAGE = 16410;
99
+ var EMFPLUS_DRAWIMAGEPOINTS = 16411;
100
+ var EMFPLUS_DRAWSTRING = 16412;
101
+ var EMFPLUS_SETANTIALIASMODE = 16414;
102
+ var EMFPLUS_SETTEXTRENDERINGHINT = 16415;
103
+ var EMFPLUS_SETINTERPOLATIONMODE = 16417;
104
+ var EMFPLUS_SETPIXELOFFSETMODE = 16418;
105
+ var EMFPLUS_SETCOMPOSITINGQUALITY = 16420;
106
+ var EMFPLUS_SAVE = 16421;
107
+ var EMFPLUS_RESTORE = 16422;
108
+ var EMFPLUS_BEGINCONTAINERNOPARAMS = 16424;
109
+ var EMFPLUS_ENDCONTAINER = 16425;
110
+ var EMFPLUS_SETWORLDTRANSFORM = 16426;
111
+ var EMFPLUS_RESETWORLDTRANSFORM = 16427;
112
+ var EMFPLUS_MULTIPLYWORLDTRANSFORM = 16428;
113
+ var EMFPLUS_TRANSLATEWORLDTRANSFORM = 16429;
114
+ var EMFPLUS_SCALEWORLDTRANSFORM = 16430;
115
+ var EMFPLUS_ROTATEWORLDTRANSFORM = 16431;
116
+ var EMFPLUS_SETPAGETRANSFORM = 16432;
117
+ var EMFPLUS_RESETCLIP = 16433;
118
+ var EMFPLUS_SETCLIPRECT = 16434;
119
+ var EMFPLUS_SETCLIPPATH = 16435;
120
+ var EMFPLUS_SETCLIPREGION = 16436;
121
+ var EMFPLUS_DRAWDRIVERSTRING = 16438;
122
+ var EMFPLUS_OFFSETCLIP = 16437;
123
+ var EMFPLUS_OBJECTTYPE_BRUSH = 1;
124
+ var EMFPLUS_OBJECTTYPE_PEN = 2;
125
+ var EMFPLUS_OBJECTTYPE_PATH = 3;
126
+ var EMFPLUS_OBJECTTYPE_IMAGEATTRIBUTES = 4;
127
+ var EMFPLUS_OBJECTTYPE_IMAGE = 5;
128
+ var EMFPLUS_OBJECTTYPE_FONT = 6;
129
+ var EMFPLUS_OBJECTTYPE_STRINGFORMAT = 7;
130
+ var EMFPLUS_OBJECTTYPE_REGION = 8;
131
+ var EMFPLUS_BRUSHTYPE_SOLID = 0;
132
+ var EMFPLUS_BRUSHTYPE_HATCHFILL = 1;
133
+ var EMFPLUS_BRUSHTYPE_PATHGRADIENT = 3;
134
+ var EMFPLUS_BRUSHTYPE_LINEARGRADIENT = 4;
135
+ var META_EOF = 0;
136
+ var META_SETBKCOLOR = 513;
137
+ var META_SETBKMODE = 258;
138
+ var META_SETROP2 = 260;
139
+ var META_SETPOLYFILLMODE = 262;
140
+ var META_SETTEXTCOLOR = 521;
141
+ var META_SETTEXTALIGN = 302;
142
+ var META_SETWINDOWORG = 523;
143
+ var META_SETWINDOWEXT = 524;
144
+ var META_MOVETO = 532;
145
+ var META_LINETO = 531;
146
+ var META_RECTANGLE = 1051;
147
+ var META_ROUNDRECT = 1564;
148
+ var META_ELLIPSE = 1048;
149
+ var META_ARC = 2071;
150
+ var META_PIE = 2074;
151
+ var META_CHORD = 2096;
152
+ var META_POLYGON = 804;
153
+ var META_POLYLINE = 805;
154
+ var META_SELECTOBJECT = 301;
155
+ var META_DELETEOBJECT = 496;
156
+ var META_CREATEPENINDIRECT = 762;
157
+ var META_CREATEBRUSHINDIRECT = 764;
158
+ var META_CREATEFONTINDIRECT = 763;
159
+ var META_TEXTOUT = 1313;
160
+ var META_EXTTEXTOUT = 2610;
161
+ var META_SAVEDC = 30;
162
+ var META_RESTOREDC = 295;
163
+ var META_POLYPOLYGON = 1336;
4
164
  var emfLog = (...args) => {
5
165
  };
6
166
  var emfWarn = (...args) => {
@@ -8,7 +168,7 @@ var emfWarn = (...args) => {
8
168
 
9
169
  // src/emf-canvas-helpers.ts
10
170
  var DEFAULT_DPI_SCALE = 1;
11
- function createCanvas(width, height, maxWidth, maxHeight, dpiScale = DEFAULT_DPI_SCALE) {
171
+ function createCanvas(width, height, maxWidth, maxHeight, dpiScale = DEFAULT_DPI_SCALE, maxCanvasDimension = MAX_CANVAS_DIMENSION) {
12
172
  const effectiveScale = Math.max(1, Math.min(dpiScale, 4));
13
173
  let w = Math.round(width * effectiveScale);
14
174
  let h = Math.round(height * effectiveScale);
@@ -28,8 +188,9 @@ function createCanvas(width, height, maxWidth, maxHeight, dpiScale = DEFAULT_DPI
28
188
  scaleX *= factor;
29
189
  scaleY *= factor;
30
190
  }
31
- const clampedW = Math.max(1, Math.min(w, 8192));
32
- const clampedH = Math.max(1, Math.min(h, 8192));
191
+ const dimCap = Math.max(1, Math.floor(maxCanvasDimension));
192
+ const clampedW = Math.max(1, Math.min(w, dimCap));
193
+ const clampedH = Math.max(1, Math.min(h, dimCap));
33
194
  if (clampedW !== w || clampedH !== h) {
34
195
  console.warn(
35
196
  `[emf-converter] Canvas size clamped from ${w}\xD7${h} to ${clampedW}\xD7${clampedH}. Output may lose detail.`
@@ -74,8 +235,8 @@ function createTempCanvas(width, height) {
74
235
  if (width <= 0 || height <= 0) {
75
236
  return null;
76
237
  }
77
- width = Math.max(1, Math.min(Math.floor(width), 8192));
78
- height = Math.max(1, Math.min(Math.floor(height), 8192));
238
+ width = Math.max(1, Math.min(Math.floor(width), MAX_CANVAS_DIMENSION));
239
+ height = Math.max(1, Math.min(Math.floor(height), MAX_CANVAS_DIMENSION));
79
240
  if (typeof OffscreenCanvas !== "undefined") {
80
241
  const canvas = new OffscreenCanvas(width, height);
81
242
  const ctx = canvas.getContext("2d");
@@ -96,7 +257,22 @@ function createTempCanvas(width, height) {
96
257
  }
97
258
  return null;
98
259
  }
260
+ function rop2ToGco(rop2) {
261
+ switch (rop2) {
262
+ case R2_XORPEN:
263
+ return "xor";
264
+ case R2_MASKPEN:
265
+ return "multiply";
266
+ case R2_MERGEPEN:
267
+ return "lighten";
268
+ case R2_NOT:
269
+ return "difference";
270
+ default:
271
+ return "source-over";
272
+ }
273
+ }
99
274
  function applyPen(ctx, state) {
275
+ ctx.globalCompositeOperation = rop2ToGco(state.rop2);
100
276
  if (state.penStyle === 5) {
101
277
  ctx.strokeStyle = "rgba(0,0,0,0)";
102
278
  ctx.lineWidth = 0;
@@ -123,17 +299,56 @@ function applyPen(ctx, state) {
123
299
  }
124
300
  }
125
301
  function applyBrush(ctx, state) {
302
+ ctx.globalCompositeOperation = rop2ToGco(state.rop2);
126
303
  if (state.brushStyle === 1) {
127
304
  ctx.fillStyle = "rgba(0,0,0,0)";
128
305
  return;
129
306
  }
130
307
  ctx.fillStyle = state.brushColor;
131
308
  }
309
+ function cssFontWeight(weight) {
310
+ if (!weight || weight === 400) {
311
+ return "";
312
+ }
313
+ const rounded = Math.round(weight / 100) * 100;
314
+ if (rounded === 700) {
315
+ return "bold";
316
+ }
317
+ if (rounded >= 100 && rounded <= 900) {
318
+ return String(rounded);
319
+ }
320
+ return weight >= 700 ? "bold" : "";
321
+ }
322
+ function mapFontFamily(face, map) {
323
+ const resolved = map?.[face.toLowerCase().trim()] ?? face;
324
+ if (/[\s,]/.test(resolved) && !/^["']/.test(resolved)) {
325
+ return `"${resolved}"`;
326
+ }
327
+ return resolved;
328
+ }
132
329
  function applyFont(ctx, state) {
133
330
  const italic = state.fontItalic ? "italic " : "";
134
- const weight = state.fontWeight >= 700 ? "bold " : "";
331
+ const weight = cssFontWeight(state.fontWeight);
332
+ const weightPart = weight ? `${weight} ` : "";
135
333
  const size = Math.max(Math.abs(state.fontHeight), 8);
136
- ctx.font = `${italic}${weight}${size}px ${state.fontFamily}`;
334
+ const family = mapFontFamily(state.fontFamily, state.fontFamilyMap);
335
+ ctx.font = `${italic}${weightPart}${size}px ${family}`;
336
+ }
337
+ function drawTextDecorations(ctx, state, x, y, width) {
338
+ if (!state.fontUnderline && !state.fontStrikeOut) {
339
+ return;
340
+ }
341
+ const size = Math.max(Math.abs(state.fontHeight), 8);
342
+ const thickness = Math.max(1, Math.round(size / 14));
343
+ const prevFill = ctx.fillStyle;
344
+ ctx.fillStyle = state.textColor;
345
+ if (state.fontUnderline) {
346
+ ctx.fillRect(x, y + Math.round(size * 0.12), width, thickness);
347
+ }
348
+ if (state.fontStrikeOut) {
349
+ ctx.fillRect(x, y - Math.round(size * 0.3), width, thickness);
350
+ }
351
+ ctx.fillStyle = prevFill;
137
352
  }
138
353
  function readUtf16LE(view, offset, charCount) {
139
354
  if (charCount <= 0) {
@@ -192,6 +407,8 @@ function getStockObject(index) {
192
407
  height: 12,
193
408
  weight: 400,
194
409
  italic: false,
410
+ underline: false,
411
+ strikeOut: false,
195
412
  family: "monospace"
196
413
  };
197
414
  case 12:
@@ -203,6 +420,8 @@ function getStockObject(index) {
203
420
  height: 12,
204
421
  weight: 400,
205
422
  italic: false,
423
+ underline: false,
424
+ strikeOut: false,
206
425
  family: "sans-serif"
207
426
  };
208
427
  default:
@@ -235,161 +454,6 @@ async function exportCanvasToPngDataUrl(canvas) {
235
454
  return null;
236
455
  }
237
456
 
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
457
  // src/emf-header-parser.ts
394
458
  function parseEmfHeader(view) {
395
459
  emfLog("parseEmfHeader: byteLength =", view.byteLength);
@@ -1066,6 +1130,12 @@ function handleExtTextOutW(rCtx, offset, dataOff, recSize) {
1066
1130
  ctx.fillStyle = state.textColor;
1067
1131
  }
1068
1132
  ctx.fillText(text, gmx(rCtx, refX), gmy(rCtx, refY));
1133
+ if (state.fontUnderline || state.fontStrikeOut) {
1134
+ const w = ctx.measureText(text).width;
1135
+ const baseX = gmx(rCtx, refX);
1136
+ const startX = alignHoriz === "center" ? baseX - w / 2 : alignHoriz === "right" ? baseX - w : baseX;
1137
+ drawTextDecorations(ctx, state, startX, gmy(rCtx, refY), w);
1138
+ }
1069
1139
  }
1070
1140
  }
1071
1141
  }
@@ -1613,12 +1683,16 @@ function handleEmfObjectRecord(rCtx, recType, dataOff, recSize) {
1613
1683
  const height = view.getInt32(dataOff + 4, true);
1614
1684
  const weight = view.getInt32(dataOff + 20, true);
1615
1685
  const italic = view.getUint8(dataOff + 24);
1686
+ const underline = view.getUint8(dataOff + 25);
1687
+ const strikeOut = view.getUint8(dataOff + 26);
1616
1688
  const family = readUtf16LE(view, dataOff + 28, 32) || "sans-serif";
1617
1689
  rCtx.objectTable.set(ihFont, {
1618
1690
  kind: "font",
1619
1691
  height: Math.abs(height),
1620
1692
  weight,
1621
1693
  italic: italic !== 0,
1694
+ underline: underline !== 0,
1695
+ strikeOut: strikeOut !== 0,
1622
1696
  family
1623
1697
  });
1624
1698
  }
@@ -1643,6 +1717,8 @@ function handleEmfObjectRecord(rCtx, recType, dataOff, recSize) {
1643
1717
  state.fontHeight = obj.height;
1644
1718
  state.fontWeight = obj.weight;
1645
1719
  state.fontItalic = obj.italic;
1720
+ state.fontUnderline = obj.underline;
1721
+ state.fontStrikeOut = obj.strikeOut;
1646
1722
  state.fontFamily = obj.family;
1647
1723
  break;
1648
1724
  }
@@ -1819,6 +1895,9 @@ function defaultState() {
1819
1895
  fontWeight: 400,
1820
1896
  fontItalic: false,
1821
1897
  fontFamily: "sans-serif",
1898
+ fontUnderline: false,
1899
+ fontStrikeOut: false,
1900
+ rop2: 13,
1822
1901
  curX: 0,
1823
1902
  curY: 0,
1824
1903
  polyFillMode: 1,
@@ -1908,7 +1987,12 @@ function handleEmfGdiStateRecord(rCtx, recType, _offset, dataOff, recSize) {
1908
1987
  }
1909
1988
  return true;
1910
1989
  }
1911
- case EMR_SETROP2:
1990
+ case EMR_SETROP2: {
1991
+ if (recSize >= 12) {
1992
+ state.rop2 = view.getUint32(dataOff, true);
1993
+ }
1994
+ return true;
1995
+ }
1912
1996
  case EMR_SETSTRETCHBLTMODE:
1913
1997
  case EMR_SETMITERLIMIT:
1914
1998
  case EMR_SETTEXTALIGN: {
@@ -3124,7 +3208,8 @@ function handleEmfPlusTextImageRecord(rCtx, recType, recFlags, dataOff, recDataS
3124
3208
  if (text.length > 0 && font && font.kind === "plus-font") {
3125
3209
  const bold = font.flags & 1 ? "bold " : "";
3126
3210
  const italic = font.flags & 2 ? "italic " : "";
3127
- ctx.font = `${italic}${bold}${font.emSize}px ${font.family}`;
3211
+ const family = mapFontFamily(font.family, rCtx.fontFamilyMap);
3212
+ ctx.font = `${italic}${bold}${font.emSize}px ${family}`;
3128
3213
  ctx.fillStyle = resolveBrushColor(rCtx, recFlags, brushVal);
3129
3214
  ctx.textBaseline = "top";
3130
3215
  const sf = objectTable.get(formatId);
@@ -3163,7 +3248,8 @@ function handleEmfPlusTextImageRecord(rCtx, recType, recFlags, dataOff, recDataS
3163
3248
  if (text.length > 0) {
3164
3249
  const bold = font.flags & 1 ? "bold " : "";
3165
3250
  const italic = font.flags & 2 ? "italic " : "";
3166
- ctx.font = `${italic}${bold}${font.emSize}px ${font.family}`;
3251
+ const family = mapFontFamily(font.family, rCtx.fontFamilyMap);
3252
+ ctx.font = `${italic}${bold}${font.emSize}px ${family}`;
3167
3253
  ctx.fillStyle = resolveBrushColor(rCtx, recFlags, brushVal);
3168
3254
  ctx.textBaseline = "alphabetic";
3169
3255
  ctx.textAlign = "left";
@@ -3329,7 +3415,7 @@ var EMFPLUS_REC_NAMES = {
3329
3415
  16424: "BeginContainerNoParams",
3330
3416
  16425: "EndContainer"
3331
3417
  };
3332
- function replayEmfPlusRecords(view, offset, length, ctx, _canvasW, _canvasH, state, dpiScale = 1) {
3418
+ function replayEmfPlusRecords(view, offset, length, ctx, _canvasW, _canvasH, state, dpiScale = 1, maxRecords = MAX_RECORDS_EMFPLUS_DEFAULT, fontFamilyMap) {
3333
3419
  const s = state ?? createEmfPlusState();
3334
3420
  const rCtx = {
3335
3421
  ctx,
@@ -3349,10 +3435,10 @@ function replayEmfPlusRecords(view, offset, length, ctx, _canvasW, _canvasH, sta
3349
3435
  continuationObjectType: 0,
3350
3436
  continuationTotalSize: 0,
3351
3437
  continuationOffset: 0,
3352
- dpiScale
3438
+ dpiScale,
3439
+ fontFamilyMap
3353
3440
  };
3354
3441
  const end = offset + length;
3355
- const maxRecords = 5e5;
3356
3442
  let recordCount = 0;
3357
3443
  const emfPlusRecordTypes = /* @__PURE__ */ new Map();
3358
3444
  emfLog(`replayEmfPlusRecords: offset=0x${offset.toString(16)}, length=${length}`);
@@ -3512,12 +3598,14 @@ var GDI_NAMES = {
3512
3598
  88: "EMR_POLYBEZIERTO16",
3513
3599
  91: "EMR_POLYPOLYGON16"
3514
3600
  };
3515
- function replayEmfRecords(view, ctx, bounds, canvasW, canvasH, dpiScale = 1) {
3601
+ function replayEmfRecords(view, ctx, bounds, canvasW, canvasH, dpiScale = 1, replayOptions = {}) {
3516
3602
  emfLog(
3517
3603
  `replayEmfRecords: bounds=(${bounds.left},${bounds.top})\u2192(${bounds.right},${bounds.bottom}), canvas=${canvasW}\xD7${canvasH}`
3518
3604
  );
3519
3605
  const allDeferredImages = [];
3520
3606
  const emfPlusState = createEmfPlusState();
3607
+ const maxRecords = replayOptions.maxRecords ?? MAX_RECORDS_DEFAULT;
3608
+ const maxRecordsEmfPlus = replayOptions.maxRecordsEmfPlus ?? MAX_RECORDS_EMFPLUS_DEFAULT;
3521
3609
  const logicalW = bounds.right - bounds.left || 1;
3522
3610
  const logicalH = bounds.bottom - bounds.top || 1;
3523
3611
  const sx = canvasW / logicalW;
@@ -3529,7 +3617,7 @@ function replayEmfRecords(view, ctx, bounds, canvasW, canvasH, dpiScale = 1) {
3529
3617
  ctx,
3530
3618
  view,
3531
3619
  objectTable: /* @__PURE__ */ new Map(),
3532
- state: defaultState(),
3620
+ state: { ...defaultState(), fontFamilyMap: replayOptions.fontFamilyMap },
3533
3621
  stateStack: [],
3534
3622
  inPath: false,
3535
3623
  windowOrg: { x: bounds.left, y: bounds.top },
@@ -3546,7 +3634,6 @@ function replayEmfRecords(view, ctx, bounds, canvasW, canvasH, dpiScale = 1) {
3546
3634
  };
3547
3635
  let offset = 0;
3548
3636
  const maxOffset = view.byteLength;
3549
- const maxRecords = 2e5;
3550
3637
  let recordCount = 0;
3551
3638
  let emfPlusCommentCount = 0;
3552
3639
  const gdiRecordTypes = /* @__PURE__ */ new Map();
@@ -3576,7 +3663,9 @@ function replayEmfRecords(view, ctx, bounds, canvasW, canvasH, dpiScale = 1) {
3576
3663
  canvasW,
3577
3664
  canvasH,
3578
3665
  emfPlusState,
3579
- dpiScale
3666
+ dpiScale,
3667
+ maxRecordsEmfPlus,
3668
+ replayOptions.fontFamilyMap
3580
3669
  );
3581
3670
  emfLog(
3582
3671
  `replayEmfRecords: EMF+ comment #${emfPlusCommentCount} returned ${deferred.length} deferred images`
@@ -3886,20 +3975,26 @@ function createWmfCoord(windowOrg, windowExt, canvasW, canvasH) {
3886
3975
  mh: (h) => h / (windowExt.cy || 1) * canvasH
3887
3976
  };
3888
3977
  }
3889
- function replayWmfRecords(view, ctx, header, canvasW, canvasH) {
3978
+ function replayWmfRecords(view, ctx, header, canvasW, canvasH, replayOptions = {}) {
3890
3979
  const logicalW = header.boundsRight - header.boundsLeft || 1;
3891
3980
  const logicalH = header.boundsBottom - header.boundsTop || 1;
3892
3981
  const windowOrg = { x: header.boundsLeft, y: header.boundsTop };
3893
3982
  const windowExt = { cx: logicalW, cy: logicalH };
3894
3983
  const coord = createWmfCoord(windowOrg, windowExt, canvasW, canvasH);
3895
3984
  const objectTable = /* @__PURE__ */ new Map();
3896
- let nextObjectSlot = 0;
3897
- const state = defaultState();
3985
+ const allocObjectSlot = () => {
3986
+ let slot = 0;
3987
+ while (objectTable.has(slot)) {
3988
+ slot++;
3989
+ }
3990
+ return slot;
3991
+ };
3992
+ const state = { ...defaultState(), fontFamilyMap: replayOptions.fontFamilyMap };
3898
3993
  const stateStack = [];
3899
3994
  const wCtx = { view, ctx, state, coord };
3900
3995
  let offset = header.headerSize;
3901
3996
  const maxOffset = view.byteLength;
3902
- const maxRecords = 2e5;
3997
+ const maxRecords = replayOptions.maxRecords ?? MAX_RECORDS_DEFAULT;
3903
3998
  let recordCount = 0;
3904
3999
  while (offset + 6 <= maxOffset && recordCount < maxRecords) {
3905
4000
  const recSizeWords = view.getUint32(offset, true);
@@ -3956,6 +4051,9 @@ function replayWmfRecords(view, ctx, header, canvasW, canvasH) {
3956
4051
  }
3957
4052
  break;
3958
4053
  case META_SETROP2:
4054
+ if (recSize >= 8) {
4055
+ state.rop2 = view.getUint16(dataOff, true);
4056
+ }
3959
4057
  break;
3960
4058
  case META_SETPOLYFILLMODE:
3961
4059
  if (recSize >= 8) {
@@ -3969,7 +4067,7 @@ function replayWmfRecords(view, ctx, header, canvasW, canvasH) {
3969
4067
  break;
3970
4068
  case META_CREATEPENINDIRECT:
3971
4069
  if (recSize >= 16) {
3972
- const slot = nextObjectSlot++;
4070
+ const slot = allocObjectSlot();
3973
4071
  objectTable.set(slot, {
3974
4072
  kind: "pen",
3975
4073
  style: view.getUint16(dataOff, true) & 255,
@@ -3980,7 +4078,7 @@ function replayWmfRecords(view, ctx, header, canvasW, canvasH) {
3980
4078
  break;
3981
4079
  case META_CREATEBRUSHINDIRECT:
3982
4080
  if (recSize >= 14) {
3983
- const slot = nextObjectSlot++;
4081
+ const slot = allocObjectSlot();
3984
4082
  objectTable.set(slot, {
3985
4083
  kind: "brush",
3986
4084
  style: view.getUint16(dataOff, true),
@@ -3998,12 +4096,14 @@ function replayWmfRecords(view, ctx, header, canvasW, canvasH) {
3998
4096
  }
3999
4097
  family += String.fromCharCode(ch);
4000
4098
  }
4001
- const slot = nextObjectSlot++;
4099
+ const slot = allocObjectSlot();
4002
4100
  objectTable.set(slot, {
4003
4101
  kind: "font",
4004
4102
  height: Math.abs(view.getInt16(dataOff, true)),
4005
4103
  weight: view.getInt16(dataOff + 8, true),
4006
4104
  italic: view.getUint8(dataOff + 10) !== 0,
4105
+ underline: view.getUint8(dataOff + 11) !== 0,
4106
+ strikeOut: view.getUint8(dataOff + 12) !== 0,
4007
4107
  family: family || "sans-serif"
4008
4108
  });
4009
4109
  }
@@ -4026,6 +4126,8 @@ function replayWmfRecords(view, ctx, header, canvasW, canvasH) {
4026
4126
  state.fontHeight = obj.height;
4027
4127
  state.fontWeight = obj.weight;
4028
4128
  state.fontItalic = obj.italic;
4129
+ state.fontUnderline = obj.underline;
4130
+ state.fontStrikeOut = obj.strikeOut;
4029
4131
  state.fontFamily = obj.family;
4030
4132
  break;
4031
4133
  }
@@ -4143,6 +4245,11 @@ async function convertEmfToDataUrl(buffer, maxWidth, maxHeight, optionsOrDpiScal
4143
4245
  const dpiScale = opts.dpiScale ?? DEFAULT_DPI_SCALE;
4144
4246
  const effectiveMaxWidth = maxWidth ?? opts.maxWidth;
4145
4247
  const effectiveMaxHeight = maxHeight ?? opts.maxHeight;
4248
+ const replayOptions = {
4249
+ maxRecords: opts.maxRecords,
4250
+ maxRecordsEmfPlus: opts.maxRecords,
4251
+ fontFamilyMap: opts.fontFamilyMap
4252
+ };
4146
4253
  try {
4147
4254
  emfLog("=== convertEmfToDataUrl START ===");
4148
4255
  emfLog(
@@ -4168,7 +4275,14 @@ async function convertEmfToDataUrl(buffer, maxWidth, maxHeight, optionsOrDpiScal
4168
4275
  const logicalW = renderBounds.right - renderBounds.left;
4169
4276
  const logicalH = renderBounds.bottom - renderBounds.top;
4170
4277
  emfLog(`convertEmfToDataUrl: logicalSize=${logicalW}\xD7${logicalH}`);
4171
- const setup = createCanvas(logicalW, logicalH, effectiveMaxWidth, effectiveMaxHeight, dpiScale);
4278
+ const setup = createCanvas(
4279
+ logicalW,
4280
+ logicalH,
4281
+ effectiveMaxWidth,
4282
+ effectiveMaxHeight,
4283
+ dpiScale,
4284
+ opts.maxCanvasDimension
4285
+ );
4172
4286
  if (!setup) {
4173
4287
  emfLog("convertEmfToDataUrl: createCanvas returned null \u2014 returning null");
4174
4288
  return null;
@@ -4185,7 +4299,8 @@ async function convertEmfToDataUrl(buffer, maxWidth, maxHeight, optionsOrDpiScal
4185
4299
  renderBounds,
4186
4300
  canvas.width,
4187
4301
  canvas.height,
4188
- dpiScale
4302
+ dpiScale,
4303
+ replayOptions
4189
4304
  );
4190
4305
  emfLog(
4191
4306
  `convertEmfToDataUrl: replayEmfRecords returned ${deferredImages.length} deferred images`
@@ -4215,6 +4330,10 @@ async function convertWmfToDataUrl(buffer, maxWidth, maxHeight, optionsOrDpiScal
4215
4330
  const dpiScale = opts.dpiScale ?? DEFAULT_DPI_SCALE;
4216
4331
  const effectiveMaxWidth = maxWidth ?? opts.maxWidth;
4217
4332
  const effectiveMaxHeight = maxHeight ?? opts.maxHeight;
4333
+ const replayOptions = {
4334
+ maxRecords: opts.maxRecords,
4335
+ fontFamilyMap: opts.fontFamilyMap
4336
+ };
4218
4337
  try {
4219
4338
  emfLog(
4220
4339
  "=== convertWmfToDataUrl START ===",
@@ -4233,13 +4352,20 @@ async function convertWmfToDataUrl(buffer, maxWidth, maxHeight, optionsOrDpiScal
4233
4352
  emfLog("convertWmfToDataUrl: invalid dimensions \u2014 returning null");
4234
4353
  return null;
4235
4354
  }
4236
- const setup = createCanvas(logicalW, logicalH, effectiveMaxWidth, effectiveMaxHeight, dpiScale);
4355
+ const setup = createCanvas(
4356
+ logicalW,
4357
+ logicalH,
4358
+ effectiveMaxWidth,
4359
+ effectiveMaxHeight,
4360
+ dpiScale,
4361
+ opts.maxCanvasDimension
4362
+ );
4237
4363
  if (!setup) {
4238
4364
  return null;
4239
4365
  }
4240
4366
  const { canvas, ctx } = setup;
4241
4367
  ctx.save();
4242
- replayWmfRecords(view, ctx, header, canvas.width, canvas.height);
4368
+ replayWmfRecords(view, ctx, header, canvas.width, canvas.height, replayOptions);
4243
4369
  ctx.restore();
4244
4370
  const result = await exportCanvasToPngDataUrl(canvas);
4245
4371
  emfLog(`convertWmfToDataUrl: result=${result ? `dataUrl len=${result.length}` : "null"}`);