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