emf-converter 1.6.0 → 2.0.1

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 CHANGED
@@ -44,22 +44,20 @@ const pngDataUrl = await convertEmfToDataUrl(emfBuffer);
44
44
  const wmfPng = await convertWmfToDataUrl(wmfBuffer);
45
45
 
46
46
  // Optional: limit output dimensions (aspect ratio preserved)
47
- const scaled = await convertEmfToDataUrl(emfBuffer, 1024, 768);
47
+ const scaled = await convertEmfToDataUrl(emfBuffer, { maxWidth: 1024, maxHeight: 768 });
48
48
  ```
49
49
 
50
50
  Both functions return `Promise<string | null>` — `null` if the buffer is invalid or no Canvas API is available.
51
51
 
52
52
  ## API
53
53
 
54
- ### `convertEmfToDataUrl(buffer, maxWidth?, maxHeight?, options?)` · `convertWmfToDataUrl(buffer, maxWidth?, maxHeight?, options?)`
54
+ ### `convertEmfToDataUrl(buffer, options?)` · `convertWmfToDataUrl(buffer, options?)`
55
55
 
56
- | Parameter | Type | Description |
57
- | ----------- | ------------------------------------- | ---------------------------------------------------- |
58
- | `buffer` | `ArrayBuffer` | Raw EMF/WMF file bytes |
59
- | `maxWidth` | `number` (optional) | Maximum output width in pixels |
60
- | `maxHeight` | `number` (optional) | Maximum output height in pixels |
61
- | `options` | `EmfConvertOptions \| number` (opt.) | Options object, or a numeric `dpiScale` (legacy) |
62
- | **Returns** | `Promise<string \| null>` | PNG data URL or `null` on failure |
56
+ | Parameter | Type | Description |
57
+ | ----------- | ----------------------------- | ---------------------------------------------------- |
58
+ | `buffer` | `ArrayBuffer` | Raw EMF/WMF file bytes |
59
+ | `options` | `EmfConvertOptions` (optional)| Output size, DPI scale, record limits, font mapping |
60
+ | **Returns** | `Promise<string \| null>` | PNG data URL or `null` on failure |
63
61
 
64
62
  #### `EmfConvertOptions`
65
63
 
@@ -73,7 +71,7 @@ Both functions return `Promise<string | null>` — `null` if the buffer is inval
73
71
  | `fontFamilyMap` | `Record<string, string>` | — | Maps Windows face names (case-insensitive) to fonts available locally, e.g. `{ calibri: 'Carlito' }` |
74
72
 
75
73
  ```ts
76
- const png = await convertEmfToDataUrl(buffer, undefined, undefined, {
74
+ const png = await convertEmfToDataUrl(buffer, {
77
75
  dpiScale: 2,
78
76
  fontFamilyMap: { calibri: 'Carlito', 'ms shell dlg': 'Tahoma' },
79
77
  });
package/dist/index.d.mts CHANGED
@@ -58,14 +58,12 @@ interface EmfConvertOptions {
58
58
  * - The logical bounds are zero-sized or negative.
59
59
  * - No canvas API is available (e.g. headless test environment).
60
60
  *
61
- * @param buffer - The raw EMF file bytes.
62
- * @param maxWidth - Optional cap on the output canvas width (pixels).
63
- * @param maxHeight - Optional cap on the output canvas height (pixels).
64
- * @param optionsOrDpiScale - Either an {@link EmfConvertOptions} object, or
65
- * a numeric DPI scale (for backward compatibility). Default DPI scale is 2.
61
+ * @param buffer - The raw EMF file bytes.
62
+ * @param options - Optional {@link EmfConvertOptions} controlling output size,
63
+ * DPI scale, record limits, and font mapping.
66
64
  * @returns A `data:image/png;base64,…` string, or `null` on failure.
67
65
  */
68
- declare function convertEmfToDataUrl(buffer: ArrayBuffer, maxWidth?: number, maxHeight?: number, optionsOrDpiScale?: EmfConvertOptions | number, recursionDepth?: number): Promise<string | null>;
66
+ declare function convertEmfToDataUrl(buffer: ArrayBuffer, options?: EmfConvertOptions, recursionDepth?: number): Promise<string | null>;
69
67
  /**
70
68
  * Converts a WMF (Windows Metafile) binary buffer to a PNG data-URL string.
71
69
  *
@@ -80,14 +78,12 @@ declare function convertEmfToDataUrl(buffer: ArrayBuffer, maxWidth?: number, max
80
78
  * - The header cannot be parsed or reports invalid dimensions.
81
79
  * - No canvas API is available.
82
80
  *
83
- * @param buffer - The raw WMF file bytes.
84
- * @param maxWidth - Optional cap on the output canvas width (pixels).
85
- * @param maxHeight - Optional cap on the output canvas height (pixels).
86
- * @param optionsOrDpiScale - Either an {@link EmfConvertOptions} object, or
87
- * a numeric DPI scale. Default DPI scale is 2.
81
+ * @param buffer - The raw WMF file bytes.
82
+ * @param options - Optional {@link EmfConvertOptions} controlling output size,
83
+ * DPI scale, record limits, and font mapping.
88
84
  * @returns A `data:image/png;base64,…` string, or `null` on failure.
89
85
  */
90
- declare function convertWmfToDataUrl(buffer: ArrayBuffer, maxWidth?: number, maxHeight?: number, optionsOrDpiScale?: EmfConvertOptions | number, recursionDepth?: number): Promise<string | null>;
86
+ declare function convertWmfToDataUrl(buffer: ArrayBuffer, options?: EmfConvertOptions, recursionDepth?: number): Promise<string | null>;
91
87
 
92
88
  /**
93
89
  * Canvas creation, styling, string reading, stock objects, and export helpers.
package/dist/index.d.ts CHANGED
@@ -58,14 +58,12 @@ interface EmfConvertOptions {
58
58
  * - The logical bounds are zero-sized or negative.
59
59
  * - No canvas API is available (e.g. headless test environment).
60
60
  *
61
- * @param buffer - The raw EMF file bytes.
62
- * @param maxWidth - Optional cap on the output canvas width (pixels).
63
- * @param maxHeight - Optional cap on the output canvas height (pixels).
64
- * @param optionsOrDpiScale - Either an {@link EmfConvertOptions} object, or
65
- * a numeric DPI scale (for backward compatibility). Default DPI scale is 2.
61
+ * @param buffer - The raw EMF file bytes.
62
+ * @param options - Optional {@link EmfConvertOptions} controlling output size,
63
+ * DPI scale, record limits, and font mapping.
66
64
  * @returns A `data:image/png;base64,…` string, or `null` on failure.
67
65
  */
68
- declare function convertEmfToDataUrl(buffer: ArrayBuffer, maxWidth?: number, maxHeight?: number, optionsOrDpiScale?: EmfConvertOptions | number, recursionDepth?: number): Promise<string | null>;
66
+ declare function convertEmfToDataUrl(buffer: ArrayBuffer, options?: EmfConvertOptions, recursionDepth?: number): Promise<string | null>;
69
67
  /**
70
68
  * Converts a WMF (Windows Metafile) binary buffer to a PNG data-URL string.
71
69
  *
@@ -80,14 +78,12 @@ declare function convertEmfToDataUrl(buffer: ArrayBuffer, maxWidth?: number, max
80
78
  * - The header cannot be parsed or reports invalid dimensions.
81
79
  * - No canvas API is available.
82
80
  *
83
- * @param buffer - The raw WMF file bytes.
84
- * @param maxWidth - Optional cap on the output canvas width (pixels).
85
- * @param maxHeight - Optional cap on the output canvas height (pixels).
86
- * @param optionsOrDpiScale - Either an {@link EmfConvertOptions} object, or
87
- * a numeric DPI scale. Default DPI scale is 2.
81
+ * @param buffer - The raw WMF file bytes.
82
+ * @param options - Optional {@link EmfConvertOptions} controlling output size,
83
+ * DPI scale, record limits, and font mapping.
88
84
  * @returns A `data:image/png;base64,…` string, or `null` on failure.
89
85
  */
90
- declare function convertWmfToDataUrl(buffer: ArrayBuffer, maxWidth?: number, maxHeight?: number, optionsOrDpiScale?: EmfConvertOptions | number, recursionDepth?: number): Promise<string | null>;
86
+ declare function convertWmfToDataUrl(buffer: ArrayBuffer, options?: EmfConvertOptions, recursionDepth?: number): Promise<string | null>;
91
87
 
92
88
  /**
93
89
  * Canvas creation, styling, string reading, stock objects, and export helpers.
package/dist/index.js CHANGED
@@ -423,19 +423,22 @@ function mapFontFamily(face, map) {
423
423
  }
424
424
  return resolved;
425
425
  }
426
- function applyFont(ctx, state) {
426
+ function fontSizePx(state, scale = 1) {
427
+ return Math.max(Math.abs(state.fontHeight) * Math.abs(scale || 1), 8);
428
+ }
429
+ function applyFont(ctx, state, scale = 1) {
427
430
  const italic = state.fontItalic ? "italic " : "";
428
431
  const weight = cssFontWeight(state.fontWeight);
429
432
  const weightPart = weight ? `${weight} ` : "";
430
- const size = Math.max(Math.abs(state.fontHeight), 8);
433
+ const size = fontSizePx(state, scale);
431
434
  const family = mapFontFamily(state.fontFamily, state.fontFamilyMap);
432
435
  ctx.font = `${italic}${weightPart}${size}px ${family}`;
433
436
  }
434
- function drawTextDecorations(ctx, state, x, y, width) {
437
+ function drawTextDecorations(ctx, state, x, y, width, scale = 1) {
435
438
  if (!state.fontUnderline && !state.fontStrikeOut) {
436
439
  return;
437
440
  }
438
- const size = Math.max(Math.abs(state.fontHeight), 8);
441
+ const size = fontSizePx(state, scale);
439
442
  const thickness = Math.max(1, Math.round(size / 14));
440
443
  const prevFill = ctx.fillStyle;
441
444
  ctx.fillStyle = state.textColor;
@@ -1400,16 +1403,12 @@ function handleExtTextOutW(rCtx, offset, dataOff, recSize) {
1400
1403
  if (nChars > 0 && offString > 0 && offset + offString + nChars * 2 <= maxOffset) {
1401
1404
  const text = readUtf16LE(view, offset + offString, nChars);
1402
1405
  if (text.length > 0) {
1403
- applyFont(ctx, state);
1406
+ const fontScale = Math.abs(gmh(rCtx, 1));
1407
+ applyFont(ctx, state, fontScale);
1404
1408
  ctx.fillStyle = state.textColor;
1405
- let alignBaseline = "alphabetic";
1409
+ const vAlign = state.textAlign & 24;
1410
+ const alignBaseline = vAlign === 24 ? "alphabetic" : vAlign === 8 ? "bottom" : "top";
1406
1411
  let alignHoriz = "left";
1407
- if (state.textAlign & 8) {
1408
- alignBaseline = "bottom";
1409
- }
1410
- if (state.textAlign & 24) {
1411
- alignBaseline = "alphabetic";
1412
- }
1413
1412
  if (state.textAlign & 6) {
1414
1413
  alignHoriz = "center";
1415
1414
  }
@@ -1420,7 +1419,7 @@ function handleExtTextOutW(rCtx, offset, dataOff, recSize) {
1420
1419
  ctx.textAlign = alignHoriz;
1421
1420
  if (state.bkMode === 2) {
1422
1421
  const measured = ctx.measureText(text);
1423
- const bgH = state.fontHeight || 12;
1422
+ const bgH = fontSizePx(state, fontScale);
1424
1423
  ctx.fillStyle = state.bkColor;
1425
1424
  ctx.fillRect(gmx(rCtx, refX), gmy(rCtx, refY) - bgH, measured.width, bgH);
1426
1425
  ctx.fillStyle = state.textColor;
@@ -1430,24 +1429,36 @@ function handleExtTextOutW(rCtx, offset, dataOff, recSize) {
1430
1429
  const w = ctx.measureText(text).width;
1431
1430
  const baseX = gmx(rCtx, refX);
1432
1431
  const startX = alignHoriz === "center" ? baseX - w / 2 : alignHoriz === "right" ? baseX - w : baseX;
1433
- drawTextDecorations(ctx, state, startX, gmy(rCtx, refY), w);
1432
+ drawTextDecorations(ctx, state, startX, gmy(rCtx, refY), w, fontScale);
1434
1433
  }
1435
1434
  }
1436
1435
  }
1437
1436
  }
1438
1437
  return true;
1439
1438
  }
1439
+ var ROP_PATCOPY = 15728673;
1440
+ var BS_NULL = 1;
1440
1441
  function handleBitBlt(rCtx, offset, dataOff, recSize) {
1441
- const { ctx, view } = rCtx;
1442
+ const { ctx, view, state } = rCtx;
1442
1443
  if (recSize >= 96) {
1443
1444
  const dstX = view.getInt32(dataOff + 16, true);
1444
1445
  const dstY = view.getInt32(dataOff + 20, true);
1445
1446
  const dstW = view.getInt32(dataOff + 24, true);
1446
1447
  const dstH = view.getInt32(dataOff + 28, true);
1448
+ const rop = view.getUint32(dataOff + 32, true);
1447
1449
  const offBmiSrc = view.getUint32(dataOff + 76, true);
1448
1450
  const cbBmiSrc = view.getUint32(dataOff + 80, true);
1449
1451
  const offBitsSrc = view.getUint32(dataOff + 84, true);
1450
1452
  const cbBitsSrc = view.getUint32(dataOff + 88, true);
1453
+ if (offBmiSrc === 0 && rop === ROP_PATCOPY) {
1454
+ const prevFill = ctx.fillStyle;
1455
+ if (state.brushStyle !== BS_NULL) {
1456
+ ctx.fillStyle = state.brushColor;
1457
+ ctx.fillRect(gmx(rCtx, dstX), gmy(rCtx, dstY), gmw(rCtx, dstW), gmh(rCtx, dstH));
1458
+ }
1459
+ ctx.fillStyle = prevFill;
1460
+ return true;
1461
+ }
1451
1462
  if (offBmiSrc > 0 && cbBmiSrc > 0 && offBitsSrc > 0 && cbBitsSrc > 0) {
1452
1463
  const imageData = decodeDibToImageData(
1453
1464
  view,
@@ -4609,7 +4620,7 @@ function handleWmfDrawRecord(wCtx, recType, offset, dataOff, recSize) {
4609
4620
  if (txOff + 4 <= offset + recSize) {
4610
4621
  const ty2 = view.getInt16(txOff, true);
4611
4622
  const txCoord = view.getInt16(txOff + 2, true);
4612
- applyFont(ctx, state);
4623
+ applyFont(ctx, state, Math.abs(mh(1)));
4613
4624
  ctx.fillStyle = state.textColor;
4614
4625
  ctx.fillText(text, mx(txCoord), my(ty2));
4615
4626
  }
@@ -4632,7 +4643,7 @@ function handleWmfDrawRecord(wCtx, recType, offset, dataOff, recSize) {
4632
4643
  }
4633
4644
  text += String.fromCharCode(ch);
4634
4645
  }
4635
- applyFont(ctx, state);
4646
+ applyFont(ctx, state, Math.abs(mh(1)));
4636
4647
  ctx.fillStyle = state.textColor;
4637
4648
  ctx.fillText(text, mx(txCoord), my(ty2));
4638
4649
  }
@@ -4857,19 +4868,7 @@ async function processDeferredImages(ctx, deferredImages, recursionDepth = 0) {
4857
4868
  continue;
4858
4869
  }
4859
4870
  emfLog(` Deferred image [${idx}]: recursively converting embedded metafile...`);
4860
- const metafileDataUrl = await convertEmfToDataUrl(
4861
- plainBuffer,
4862
- void 0,
4863
- void 0,
4864
- void 0,
4865
- recursionDepth + 1
4866
- ) ?? await convertWmfToDataUrl(
4867
- plainBuffer,
4868
- void 0,
4869
- void 0,
4870
- void 0,
4871
- recursionDepth + 1
4872
- );
4871
+ const metafileDataUrl = await convertEmfToDataUrl(plainBuffer, void 0, recursionDepth + 1) ?? await convertWmfToDataUrl(plainBuffer, void 0, recursionDepth + 1);
4873
4872
  if (metafileDataUrl) {
4874
4873
  emfLog(
4875
4874
  ` Deferred image [${idx}]: metafile converted, dataUrl length=${metafileDataUrl.length}`
@@ -4914,14 +4913,14 @@ async function processDeferredImages(ctx, deferredImages, recursionDepth = 0) {
4914
4913
  }
4915
4914
  ctx.setTransform(1, 0, 0, 1, 0, 0);
4916
4915
  }
4917
- async function convertEmfToDataUrl(buffer, maxWidth, maxHeight, optionsOrDpiScale, recursionDepth = 0) {
4916
+ async function convertEmfToDataUrl(buffer, options, recursionDepth = 0) {
4918
4917
  if (recursionDepth > MAX_METAFILE_RECURSION) {
4919
4918
  return null;
4920
4919
  }
4921
- const opts = typeof optionsOrDpiScale === "number" ? { dpiScale: optionsOrDpiScale } : optionsOrDpiScale ?? {};
4920
+ const opts = options ?? {};
4922
4921
  const dpiScale = opts.dpiScale ?? DEFAULT_DPI_SCALE;
4923
- const effectiveMaxWidth = maxWidth ?? opts.maxWidth;
4924
- const effectiveMaxHeight = maxHeight ?? opts.maxHeight;
4922
+ const effectiveMaxWidth = opts.maxWidth;
4923
+ const effectiveMaxHeight = opts.maxHeight;
4925
4924
  const replayOptions = {
4926
4925
  maxRecords: opts.maxRecords,
4927
4926
  maxRecordsEmfPlus: opts.maxRecords,
@@ -4999,14 +4998,14 @@ async function convertEmfToDataUrl(buffer, maxWidth, maxHeight, optionsOrDpiScal
4999
4998
  return null;
5000
4999
  }
5001
5000
  }
5002
- async function convertWmfToDataUrl(buffer, maxWidth, maxHeight, optionsOrDpiScale, recursionDepth = 0) {
5001
+ async function convertWmfToDataUrl(buffer, options, recursionDepth = 0) {
5003
5002
  if (recursionDepth > MAX_METAFILE_RECURSION) {
5004
5003
  return null;
5005
5004
  }
5006
- const opts = typeof optionsOrDpiScale === "number" ? { dpiScale: optionsOrDpiScale } : optionsOrDpiScale ?? {};
5005
+ const opts = options ?? {};
5007
5006
  const dpiScale = opts.dpiScale ?? DEFAULT_DPI_SCALE;
5008
- const effectiveMaxWidth = maxWidth ?? opts.maxWidth;
5009
- const effectiveMaxHeight = maxHeight ?? opts.maxHeight;
5007
+ const effectiveMaxWidth = opts.maxWidth;
5008
+ const effectiveMaxHeight = opts.maxHeight;
5010
5009
  const replayOptions = {
5011
5010
  maxRecords: opts.maxRecords,
5012
5011
  fontFamilyMap: opts.fontFamilyMap
package/dist/index.mjs CHANGED
@@ -421,19 +421,22 @@ function mapFontFamily(face, map) {
421
421
  }
422
422
  return resolved;
423
423
  }
424
- function applyFont(ctx, state) {
424
+ function fontSizePx(state, scale = 1) {
425
+ return Math.max(Math.abs(state.fontHeight) * Math.abs(scale || 1), 8);
426
+ }
427
+ function applyFont(ctx, state, scale = 1) {
425
428
  const italic = state.fontItalic ? "italic " : "";
426
429
  const weight = cssFontWeight(state.fontWeight);
427
430
  const weightPart = weight ? `${weight} ` : "";
428
- const size = Math.max(Math.abs(state.fontHeight), 8);
431
+ const size = fontSizePx(state, scale);
429
432
  const family = mapFontFamily(state.fontFamily, state.fontFamilyMap);
430
433
  ctx.font = `${italic}${weightPart}${size}px ${family}`;
431
434
  }
432
- function drawTextDecorations(ctx, state, x, y, width) {
435
+ function drawTextDecorations(ctx, state, x, y, width, scale = 1) {
433
436
  if (!state.fontUnderline && !state.fontStrikeOut) {
434
437
  return;
435
438
  }
436
- const size = Math.max(Math.abs(state.fontHeight), 8);
439
+ const size = fontSizePx(state, scale);
437
440
  const thickness = Math.max(1, Math.round(size / 14));
438
441
  const prevFill = ctx.fillStyle;
439
442
  ctx.fillStyle = state.textColor;
@@ -1398,16 +1401,12 @@ function handleExtTextOutW(rCtx, offset, dataOff, recSize) {
1398
1401
  if (nChars > 0 && offString > 0 && offset + offString + nChars * 2 <= maxOffset) {
1399
1402
  const text = readUtf16LE(view, offset + offString, nChars);
1400
1403
  if (text.length > 0) {
1401
- applyFont(ctx, state);
1404
+ const fontScale = Math.abs(gmh(rCtx, 1));
1405
+ applyFont(ctx, state, fontScale);
1402
1406
  ctx.fillStyle = state.textColor;
1403
- let alignBaseline = "alphabetic";
1407
+ const vAlign = state.textAlign & 24;
1408
+ const alignBaseline = vAlign === 24 ? "alphabetic" : vAlign === 8 ? "bottom" : "top";
1404
1409
  let alignHoriz = "left";
1405
- if (state.textAlign & 8) {
1406
- alignBaseline = "bottom";
1407
- }
1408
- if (state.textAlign & 24) {
1409
- alignBaseline = "alphabetic";
1410
- }
1411
1410
  if (state.textAlign & 6) {
1412
1411
  alignHoriz = "center";
1413
1412
  }
@@ -1418,7 +1417,7 @@ function handleExtTextOutW(rCtx, offset, dataOff, recSize) {
1418
1417
  ctx.textAlign = alignHoriz;
1419
1418
  if (state.bkMode === 2) {
1420
1419
  const measured = ctx.measureText(text);
1421
- const bgH = state.fontHeight || 12;
1420
+ const bgH = fontSizePx(state, fontScale);
1422
1421
  ctx.fillStyle = state.bkColor;
1423
1422
  ctx.fillRect(gmx(rCtx, refX), gmy(rCtx, refY) - bgH, measured.width, bgH);
1424
1423
  ctx.fillStyle = state.textColor;
@@ -1428,24 +1427,36 @@ function handleExtTextOutW(rCtx, offset, dataOff, recSize) {
1428
1427
  const w = ctx.measureText(text).width;
1429
1428
  const baseX = gmx(rCtx, refX);
1430
1429
  const startX = alignHoriz === "center" ? baseX - w / 2 : alignHoriz === "right" ? baseX - w : baseX;
1431
- drawTextDecorations(ctx, state, startX, gmy(rCtx, refY), w);
1430
+ drawTextDecorations(ctx, state, startX, gmy(rCtx, refY), w, fontScale);
1432
1431
  }
1433
1432
  }
1434
1433
  }
1435
1434
  }
1436
1435
  return true;
1437
1436
  }
1437
+ var ROP_PATCOPY = 15728673;
1438
+ var BS_NULL = 1;
1438
1439
  function handleBitBlt(rCtx, offset, dataOff, recSize) {
1439
- const { ctx, view } = rCtx;
1440
+ const { ctx, view, state } = rCtx;
1440
1441
  if (recSize >= 96) {
1441
1442
  const dstX = view.getInt32(dataOff + 16, true);
1442
1443
  const dstY = view.getInt32(dataOff + 20, true);
1443
1444
  const dstW = view.getInt32(dataOff + 24, true);
1444
1445
  const dstH = view.getInt32(dataOff + 28, true);
1446
+ const rop = view.getUint32(dataOff + 32, true);
1445
1447
  const offBmiSrc = view.getUint32(dataOff + 76, true);
1446
1448
  const cbBmiSrc = view.getUint32(dataOff + 80, true);
1447
1449
  const offBitsSrc = view.getUint32(dataOff + 84, true);
1448
1450
  const cbBitsSrc = view.getUint32(dataOff + 88, true);
1451
+ if (offBmiSrc === 0 && rop === ROP_PATCOPY) {
1452
+ const prevFill = ctx.fillStyle;
1453
+ if (state.brushStyle !== BS_NULL) {
1454
+ ctx.fillStyle = state.brushColor;
1455
+ ctx.fillRect(gmx(rCtx, dstX), gmy(rCtx, dstY), gmw(rCtx, dstW), gmh(rCtx, dstH));
1456
+ }
1457
+ ctx.fillStyle = prevFill;
1458
+ return true;
1459
+ }
1449
1460
  if (offBmiSrc > 0 && cbBmiSrc > 0 && offBitsSrc > 0 && cbBitsSrc > 0) {
1450
1461
  const imageData = decodeDibToImageData(
1451
1462
  view,
@@ -4607,7 +4618,7 @@ function handleWmfDrawRecord(wCtx, recType, offset, dataOff, recSize) {
4607
4618
  if (txOff + 4 <= offset + recSize) {
4608
4619
  const ty2 = view.getInt16(txOff, true);
4609
4620
  const txCoord = view.getInt16(txOff + 2, true);
4610
- applyFont(ctx, state);
4621
+ applyFont(ctx, state, Math.abs(mh(1)));
4611
4622
  ctx.fillStyle = state.textColor;
4612
4623
  ctx.fillText(text, mx(txCoord), my(ty2));
4613
4624
  }
@@ -4630,7 +4641,7 @@ function handleWmfDrawRecord(wCtx, recType, offset, dataOff, recSize) {
4630
4641
  }
4631
4642
  text += String.fromCharCode(ch);
4632
4643
  }
4633
- applyFont(ctx, state);
4644
+ applyFont(ctx, state, Math.abs(mh(1)));
4634
4645
  ctx.fillStyle = state.textColor;
4635
4646
  ctx.fillText(text, mx(txCoord), my(ty2));
4636
4647
  }
@@ -4855,19 +4866,7 @@ async function processDeferredImages(ctx, deferredImages, recursionDepth = 0) {
4855
4866
  continue;
4856
4867
  }
4857
4868
  emfLog(` Deferred image [${idx}]: recursively converting embedded metafile...`);
4858
- const metafileDataUrl = await convertEmfToDataUrl(
4859
- plainBuffer,
4860
- void 0,
4861
- void 0,
4862
- void 0,
4863
- recursionDepth + 1
4864
- ) ?? await convertWmfToDataUrl(
4865
- plainBuffer,
4866
- void 0,
4867
- void 0,
4868
- void 0,
4869
- recursionDepth + 1
4870
- );
4869
+ const metafileDataUrl = await convertEmfToDataUrl(plainBuffer, void 0, recursionDepth + 1) ?? await convertWmfToDataUrl(plainBuffer, void 0, recursionDepth + 1);
4871
4870
  if (metafileDataUrl) {
4872
4871
  emfLog(
4873
4872
  ` Deferred image [${idx}]: metafile converted, dataUrl length=${metafileDataUrl.length}`
@@ -4912,14 +4911,14 @@ async function processDeferredImages(ctx, deferredImages, recursionDepth = 0) {
4912
4911
  }
4913
4912
  ctx.setTransform(1, 0, 0, 1, 0, 0);
4914
4913
  }
4915
- async function convertEmfToDataUrl(buffer, maxWidth, maxHeight, optionsOrDpiScale, recursionDepth = 0) {
4914
+ async function convertEmfToDataUrl(buffer, options, recursionDepth = 0) {
4916
4915
  if (recursionDepth > MAX_METAFILE_RECURSION) {
4917
4916
  return null;
4918
4917
  }
4919
- const opts = typeof optionsOrDpiScale === "number" ? { dpiScale: optionsOrDpiScale } : optionsOrDpiScale ?? {};
4918
+ const opts = options ?? {};
4920
4919
  const dpiScale = opts.dpiScale ?? DEFAULT_DPI_SCALE;
4921
- const effectiveMaxWidth = maxWidth ?? opts.maxWidth;
4922
- const effectiveMaxHeight = maxHeight ?? opts.maxHeight;
4920
+ const effectiveMaxWidth = opts.maxWidth;
4921
+ const effectiveMaxHeight = opts.maxHeight;
4923
4922
  const replayOptions = {
4924
4923
  maxRecords: opts.maxRecords,
4925
4924
  maxRecordsEmfPlus: opts.maxRecords,
@@ -4997,14 +4996,14 @@ async function convertEmfToDataUrl(buffer, maxWidth, maxHeight, optionsOrDpiScal
4997
4996
  return null;
4998
4997
  }
4999
4998
  }
5000
- async function convertWmfToDataUrl(buffer, maxWidth, maxHeight, optionsOrDpiScale, recursionDepth = 0) {
4999
+ async function convertWmfToDataUrl(buffer, options, recursionDepth = 0) {
5001
5000
  if (recursionDepth > MAX_METAFILE_RECURSION) {
5002
5001
  return null;
5003
5002
  }
5004
- const opts = typeof optionsOrDpiScale === "number" ? { dpiScale: optionsOrDpiScale } : optionsOrDpiScale ?? {};
5003
+ const opts = options ?? {};
5005
5004
  const dpiScale = opts.dpiScale ?? DEFAULT_DPI_SCALE;
5006
- const effectiveMaxWidth = maxWidth ?? opts.maxWidth;
5007
- const effectiveMaxHeight = maxHeight ?? opts.maxHeight;
5005
+ const effectiveMaxWidth = opts.maxWidth;
5006
+ const effectiveMaxHeight = opts.maxHeight;
5008
5007
  const replayOptions = {
5009
5008
  maxRecords: opts.maxRecords,
5010
5009
  fontFamilyMap: opts.fontFamilyMap
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "emf-converter",
3
- "version": "1.6.0",
3
+ "version": "2.0.1",
4
4
  "description": "Convert EMF/WMF metafile binaries to PNG data URLs using Canvas",
5
5
  "keywords": [
6
6
  "canvas",