emf-converter 1.1.3 → 1.1.5

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.d.mts CHANGED
@@ -48,7 +48,7 @@ interface EmfConvertOptions {
48
48
  * a numeric DPI scale (for backward compatibility). Default DPI scale is 2.
49
49
  * @returns A `data:image/png;base64,…` string, or `null` on failure.
50
50
  */
51
- declare function convertEmfToDataUrl(buffer: ArrayBuffer, maxWidth?: number, maxHeight?: number, optionsOrDpiScale?: EmfConvertOptions | number): Promise<string | null>;
51
+ declare function convertEmfToDataUrl(buffer: ArrayBuffer, maxWidth?: number, maxHeight?: number, optionsOrDpiScale?: EmfConvertOptions | number, recursionDepth?: number): Promise<string | null>;
52
52
  /**
53
53
  * Converts a WMF (Windows Metafile) binary buffer to a PNG data-URL string.
54
54
  *
@@ -70,7 +70,7 @@ declare function convertEmfToDataUrl(buffer: ArrayBuffer, maxWidth?: number, max
70
70
  * a numeric DPI scale. Default DPI scale is 2.
71
71
  * @returns A `data:image/png;base64,…` string, or `null` on failure.
72
72
  */
73
- declare function convertWmfToDataUrl(buffer: ArrayBuffer, maxWidth?: number, maxHeight?: number, optionsOrDpiScale?: EmfConvertOptions | number): Promise<string | null>;
73
+ declare function convertWmfToDataUrl(buffer: ArrayBuffer, maxWidth?: number, maxHeight?: number, optionsOrDpiScale?: EmfConvertOptions | number, recursionDepth?: number): Promise<string | null>;
74
74
 
75
75
  /**
76
76
  * Canvas creation, styling, string reading, stock objects, and export helpers.
package/dist/index.d.ts CHANGED
@@ -48,7 +48,7 @@ interface EmfConvertOptions {
48
48
  * a numeric DPI scale (for backward compatibility). Default DPI scale is 2.
49
49
  * @returns A `data:image/png;base64,…` string, or `null` on failure.
50
50
  */
51
- declare function convertEmfToDataUrl(buffer: ArrayBuffer, maxWidth?: number, maxHeight?: number, optionsOrDpiScale?: EmfConvertOptions | number): Promise<string | null>;
51
+ declare function convertEmfToDataUrl(buffer: ArrayBuffer, maxWidth?: number, maxHeight?: number, optionsOrDpiScale?: EmfConvertOptions | number, recursionDepth?: number): Promise<string | null>;
52
52
  /**
53
53
  * Converts a WMF (Windows Metafile) binary buffer to a PNG data-URL string.
54
54
  *
@@ -70,7 +70,7 @@ declare function convertEmfToDataUrl(buffer: ArrayBuffer, maxWidth?: number, max
70
70
  * a numeric DPI scale. Default DPI scale is 2.
71
71
  * @returns A `data:image/png;base64,…` string, or `null` on failure.
72
72
  */
73
- declare function convertWmfToDataUrl(buffer: ArrayBuffer, maxWidth?: number, maxHeight?: number, optionsOrDpiScale?: EmfConvertOptions | number): Promise<string | null>;
73
+ declare function convertWmfToDataUrl(buffer: ArrayBuffer, maxWidth?: number, maxHeight?: number, optionsOrDpiScale?: EmfConvertOptions | number, recursionDepth?: number): Promise<string | null>;
74
74
 
75
75
  /**
76
76
  * Canvas creation, styling, string reading, stock objects, and export helpers.
package/dist/index.js CHANGED
@@ -74,6 +74,8 @@ function createTempCanvas(width, height) {
74
74
  if (width <= 0 || height <= 0) {
75
75
  return null;
76
76
  }
77
+ width = Math.max(1, Math.min(Math.floor(width), 8192));
78
+ height = Math.max(1, Math.min(Math.floor(height), 8192));
77
79
  if (typeof OffscreenCanvas !== "undefined") {
78
80
  const canvas = new OffscreenCanvas(width, height);
79
81
  const ctx = canvas.getContext("2d");
@@ -134,18 +136,34 @@ function applyFont(ctx, state) {
134
136
  ctx.font = `${italic}${weight}${size}px ${state.fontFamily}`;
135
137
  }
136
138
  function readUtf16LE(view, offset, charCount) {
137
- const chars = [];
138
- for (let i = 0; i < charCount; i++) {
139
- if (offset + i * 2 + 1 >= view.byteLength) {
140
- break;
141
- }
142
- const code = view.getUint16(offset + i * 2, true);
143
- if (code === 0) {
144
- break;
139
+ if (charCount <= 0) {
140
+ return "";
141
+ }
142
+ const maxBytes = view.byteLength - offset;
143
+ if (maxBytes <= 0) {
144
+ return "";
145
+ }
146
+ const usableChars = Math.min(charCount, Math.floor(maxBytes / 2));
147
+ if (usableChars <= 0) {
148
+ return "";
149
+ }
150
+ let decoded;
151
+ try {
152
+ const bytes = new Uint8Array(view.buffer, view.byteOffset + offset, usableChars * 2);
153
+ decoded = new TextDecoder("utf-16le").decode(bytes);
154
+ } catch {
155
+ const chars = [];
156
+ for (let i = 0; i < usableChars; i++) {
157
+ const code = view.getUint16(offset + i * 2, true);
158
+ if (code === 0) {
159
+ return chars.join("");
160
+ }
161
+ chars.push(String.fromCharCode(code));
145
162
  }
146
- chars.push(String.fromCharCode(code));
163
+ return chars.join("");
147
164
  }
148
- return chars.join("");
165
+ const nul = decoded.indexOf(String.fromCharCode(0));
166
+ return nul === -1 ? decoded : decoded.slice(0, nul);
149
167
  }
150
168
  function getStockObject(index) {
151
169
  switch (index) {
@@ -2135,7 +2153,12 @@ function applyClipCombineMode(rCtx, combineMode, opName, clipFn) {
2135
2153
  return true;
2136
2154
  }
2137
2155
  }
2138
- function traceRegionNodePath(ctx, node) {
2156
+ var MAX_REGION_TRACE_DEPTH = 64;
2157
+ function traceRegionNodePath(ctx, node, depth = 0) {
2158
+ if (depth > MAX_REGION_TRACE_DEPTH) {
2159
+ ctx.rect(0, 0, 0, 0);
2160
+ return;
2161
+ }
2139
2162
  switch (node.type) {
2140
2163
  case "rect":
2141
2164
  ctx.rect(node.x, node.y, node.width, node.height);
@@ -2150,7 +2173,7 @@ function traceRegionNodePath(ctx, node) {
2150
2173
  ctx.rect(0, 0, 0, 0);
2151
2174
  break;
2152
2175
  case "combine":
2153
- traceRegionNodePath(ctx, node.left);
2176
+ traceRegionNodePath(ctx, node.left, depth + 1);
2154
2177
  if (node.combineMode !== 0) {
2155
2178
  emfWarn(
2156
2179
  `traceRegionNodePath: combine mode ${node.combineMode} not fully supported, using left subtree only`
@@ -2161,7 +2184,7 @@ function traceRegionNodePath(ctx, node) {
2161
2184
  } catch {
2162
2185
  }
2163
2186
  ctx.beginPath();
2164
- traceRegionNodePath(ctx, node.right);
2187
+ traceRegionNodePath(ctx, node.right, depth + 1);
2165
2188
  }
2166
2189
  break;
2167
2190
  }
@@ -2959,19 +2982,23 @@ function handleEmfPlusObjectRecord(rCtx, recFlags, dataOff, recDataSize) {
2959
2982
  }
2960
2983
  }
2961
2984
  }
2962
- function parseRegionNode(view, off, endOff) {
2985
+ var MAX_REGION_NODE_DEPTH = 64;
2986
+ function parseRegionNode(view, off, endOff, depth = 0) {
2963
2987
  if (off + 4 > endOff) {
2964
2988
  return null;
2965
2989
  }
2990
+ if (depth > MAX_REGION_NODE_DEPTH) {
2991
+ return null;
2992
+ }
2966
2993
  const nodeType = view.getUint32(off, true);
2967
2994
  let cursor = off + 4;
2968
2995
  if (nodeType <= 4) {
2969
- const leftResult = parseRegionNode(view, cursor, endOff);
2996
+ const leftResult = parseRegionNode(view, cursor, endOff, depth + 1);
2970
2997
  if (!leftResult) {
2971
2998
  return null;
2972
2999
  }
2973
3000
  cursor += leftResult.bytesRead;
2974
- const rightResult = parseRegionNode(view, cursor, endOff);
3001
+ const rightResult = parseRegionNode(view, cursor, endOff, depth + 1);
2975
3002
  if (!rightResult) {
2976
3003
  return null;
2977
3004
  }
@@ -3361,18 +3388,22 @@ function replayEmfPlusRecords(view, offset, length, ctx, _canvasW, _canvasH, sta
3361
3388
  if (recDataSize >= 4) {
3362
3389
  const totalSize = view.getUint32(dataOff, true);
3363
3390
  const objectType = recFlags >> 8 & 127;
3364
- rCtx.continuationTotalSize = totalSize;
3365
- rCtx.continuationObjectId = objectId;
3366
- rCtx.continuationObjectType = objectType;
3367
- rCtx.continuationBuffer = new Uint8Array(totalSize);
3368
- const chunkSize = recDataSize - 4;
3369
- const chunk = new Uint8Array(
3370
- view.buffer,
3371
- view.byteOffset + dataOff + 4,
3372
- Math.min(chunkSize, totalSize)
3373
- );
3374
- rCtx.continuationBuffer.set(chunk, 0);
3375
- rCtx.continuationOffset = chunk.length;
3391
+ const MAX_CONTINUATION_BYTES = 64 * 1024 * 1024;
3392
+ const remainingEmfPlusBytes = view.byteLength - dataOff;
3393
+ if (!Number.isFinite(totalSize) || totalSize <= 0 || totalSize > MAX_CONTINUATION_BYTES || totalSize > remainingEmfPlusBytes || recDataSize - 4 < 0) ; else {
3394
+ rCtx.continuationTotalSize = totalSize;
3395
+ rCtx.continuationObjectId = objectId;
3396
+ rCtx.continuationObjectType = objectType;
3397
+ rCtx.continuationBuffer = new Uint8Array(totalSize);
3398
+ const chunkSize = recDataSize - 4;
3399
+ const chunk = new Uint8Array(
3400
+ view.buffer,
3401
+ view.byteOffset + dataOff + 4,
3402
+ Math.min(chunkSize, totalSize)
3403
+ );
3404
+ rCtx.continuationBuffer.set(chunk, 0);
3405
+ rCtx.continuationOffset = chunk.length;
3406
+ }
3376
3407
  }
3377
3408
  } else {
3378
3409
  const remaining = rCtx.continuationTotalSize - rCtx.continuationOffset;
@@ -4017,8 +4048,11 @@ function replayWmfRecords(view, ctx, header, canvasW, canvasH) {
4017
4048
  }
4018
4049
 
4019
4050
  // src/emf-converter.ts
4020
- async function processDeferredImages(ctx, deferredImages) {
4021
- emfLog(`processDeferredImages: processing ${deferredImages.length} deferred images...`);
4051
+ var MAX_METAFILE_RECURSION = 3;
4052
+ async function processDeferredImages(ctx, deferredImages, recursionDepth = 0) {
4053
+ emfLog(
4054
+ `processDeferredImages: processing ${deferredImages.length} deferred images (recursionDepth=${recursionDepth})...`
4055
+ );
4022
4056
  for (let idx = 0; idx < deferredImages.length; idx++) {
4023
4057
  const img = deferredImages[idx];
4024
4058
  emfLog(
@@ -4037,8 +4071,26 @@ async function processDeferredImages(ctx, deferredImages) {
4037
4071
  img.transform[5]
4038
4072
  );
4039
4073
  if (img.isMetafile) {
4074
+ if (recursionDepth >= MAX_METAFILE_RECURSION) {
4075
+ emfWarn(
4076
+ ` Deferred image [${idx}]: skipping embedded metafile \u2014 recursion depth ${recursionDepth} >= ${MAX_METAFILE_RECURSION}`
4077
+ );
4078
+ continue;
4079
+ }
4040
4080
  emfLog(` Deferred image [${idx}]: recursively converting embedded metafile...`);
4041
- const metafileDataUrl = await convertEmfToDataUrl(plainBuffer) ?? await convertWmfToDataUrl(plainBuffer);
4081
+ const metafileDataUrl = await convertEmfToDataUrl(
4082
+ plainBuffer,
4083
+ void 0,
4084
+ void 0,
4085
+ void 0,
4086
+ recursionDepth + 1
4087
+ ) ?? await convertWmfToDataUrl(
4088
+ plainBuffer,
4089
+ void 0,
4090
+ void 0,
4091
+ void 0,
4092
+ recursionDepth + 1
4093
+ );
4042
4094
  if (metafileDataUrl) {
4043
4095
  emfLog(
4044
4096
  ` Deferred image [${idx}]: metafile converted, dataUrl length=${metafileDataUrl.length}`
@@ -4083,7 +4135,10 @@ async function processDeferredImages(ctx, deferredImages) {
4083
4135
  }
4084
4136
  ctx.setTransform(1, 0, 0, 1, 0, 0);
4085
4137
  }
4086
- async function convertEmfToDataUrl(buffer, maxWidth, maxHeight, optionsOrDpiScale) {
4138
+ async function convertEmfToDataUrl(buffer, maxWidth, maxHeight, optionsOrDpiScale, recursionDepth = 0) {
4139
+ if (recursionDepth > MAX_METAFILE_RECURSION) {
4140
+ return null;
4141
+ }
4087
4142
  const opts = typeof optionsOrDpiScale === "number" ? { dpiScale: optionsOrDpiScale } : optionsOrDpiScale ?? {};
4088
4143
  const dpiScale = opts.dpiScale ?? DEFAULT_DPI_SCALE;
4089
4144
  const effectiveMaxWidth = maxWidth ?? opts.maxWidth;
@@ -4152,7 +4207,10 @@ async function convertEmfToDataUrl(buffer, maxWidth, maxHeight, optionsOrDpiScal
4152
4207
  return null;
4153
4208
  }
4154
4209
  }
4155
- async function convertWmfToDataUrl(buffer, maxWidth, maxHeight, optionsOrDpiScale) {
4210
+ async function convertWmfToDataUrl(buffer, maxWidth, maxHeight, optionsOrDpiScale, recursionDepth = 0) {
4211
+ if (recursionDepth > MAX_METAFILE_RECURSION) {
4212
+ return null;
4213
+ }
4156
4214
  const opts = typeof optionsOrDpiScale === "number" ? { dpiScale: optionsOrDpiScale } : optionsOrDpiScale ?? {};
4157
4215
  const dpiScale = opts.dpiScale ?? DEFAULT_DPI_SCALE;
4158
4216
  const effectiveMaxWidth = maxWidth ?? opts.maxWidth;
package/dist/index.mjs CHANGED
@@ -72,6 +72,8 @@ function createTempCanvas(width, height) {
72
72
  if (width <= 0 || height <= 0) {
73
73
  return null;
74
74
  }
75
+ width = Math.max(1, Math.min(Math.floor(width), 8192));
76
+ height = Math.max(1, Math.min(Math.floor(height), 8192));
75
77
  if (typeof OffscreenCanvas !== "undefined") {
76
78
  const canvas = new OffscreenCanvas(width, height);
77
79
  const ctx = canvas.getContext("2d");
@@ -132,18 +134,34 @@ function applyFont(ctx, state) {
132
134
  ctx.font = `${italic}${weight}${size}px ${state.fontFamily}`;
133
135
  }
134
136
  function readUtf16LE(view, offset, charCount) {
135
- const chars = [];
136
- for (let i = 0; i < charCount; i++) {
137
- if (offset + i * 2 + 1 >= view.byteLength) {
138
- break;
139
- }
140
- const code = view.getUint16(offset + i * 2, true);
141
- if (code === 0) {
142
- break;
137
+ if (charCount <= 0) {
138
+ return "";
139
+ }
140
+ const maxBytes = view.byteLength - offset;
141
+ if (maxBytes <= 0) {
142
+ return "";
143
+ }
144
+ const usableChars = Math.min(charCount, Math.floor(maxBytes / 2));
145
+ if (usableChars <= 0) {
146
+ return "";
147
+ }
148
+ let decoded;
149
+ try {
150
+ const bytes = new Uint8Array(view.buffer, view.byteOffset + offset, usableChars * 2);
151
+ decoded = new TextDecoder("utf-16le").decode(bytes);
152
+ } catch {
153
+ const chars = [];
154
+ for (let i = 0; i < usableChars; i++) {
155
+ const code = view.getUint16(offset + i * 2, true);
156
+ if (code === 0) {
157
+ return chars.join("");
158
+ }
159
+ chars.push(String.fromCharCode(code));
143
160
  }
144
- chars.push(String.fromCharCode(code));
161
+ return chars.join("");
145
162
  }
146
- return chars.join("");
163
+ const nul = decoded.indexOf(String.fromCharCode(0));
164
+ return nul === -1 ? decoded : decoded.slice(0, nul);
147
165
  }
148
166
  function getStockObject(index) {
149
167
  switch (index) {
@@ -2133,7 +2151,12 @@ function applyClipCombineMode(rCtx, combineMode, opName, clipFn) {
2133
2151
  return true;
2134
2152
  }
2135
2153
  }
2136
- function traceRegionNodePath(ctx, node) {
2154
+ var MAX_REGION_TRACE_DEPTH = 64;
2155
+ function traceRegionNodePath(ctx, node, depth = 0) {
2156
+ if (depth > MAX_REGION_TRACE_DEPTH) {
2157
+ ctx.rect(0, 0, 0, 0);
2158
+ return;
2159
+ }
2137
2160
  switch (node.type) {
2138
2161
  case "rect":
2139
2162
  ctx.rect(node.x, node.y, node.width, node.height);
@@ -2148,7 +2171,7 @@ function traceRegionNodePath(ctx, node) {
2148
2171
  ctx.rect(0, 0, 0, 0);
2149
2172
  break;
2150
2173
  case "combine":
2151
- traceRegionNodePath(ctx, node.left);
2174
+ traceRegionNodePath(ctx, node.left, depth + 1);
2152
2175
  if (node.combineMode !== 0) {
2153
2176
  emfWarn(
2154
2177
  `traceRegionNodePath: combine mode ${node.combineMode} not fully supported, using left subtree only`
@@ -2159,7 +2182,7 @@ function traceRegionNodePath(ctx, node) {
2159
2182
  } catch {
2160
2183
  }
2161
2184
  ctx.beginPath();
2162
- traceRegionNodePath(ctx, node.right);
2185
+ traceRegionNodePath(ctx, node.right, depth + 1);
2163
2186
  }
2164
2187
  break;
2165
2188
  }
@@ -2957,19 +2980,23 @@ function handleEmfPlusObjectRecord(rCtx, recFlags, dataOff, recDataSize) {
2957
2980
  }
2958
2981
  }
2959
2982
  }
2960
- function parseRegionNode(view, off, endOff) {
2983
+ var MAX_REGION_NODE_DEPTH = 64;
2984
+ function parseRegionNode(view, off, endOff, depth = 0) {
2961
2985
  if (off + 4 > endOff) {
2962
2986
  return null;
2963
2987
  }
2988
+ if (depth > MAX_REGION_NODE_DEPTH) {
2989
+ return null;
2990
+ }
2964
2991
  const nodeType = view.getUint32(off, true);
2965
2992
  let cursor = off + 4;
2966
2993
  if (nodeType <= 4) {
2967
- const leftResult = parseRegionNode(view, cursor, endOff);
2994
+ const leftResult = parseRegionNode(view, cursor, endOff, depth + 1);
2968
2995
  if (!leftResult) {
2969
2996
  return null;
2970
2997
  }
2971
2998
  cursor += leftResult.bytesRead;
2972
- const rightResult = parseRegionNode(view, cursor, endOff);
2999
+ const rightResult = parseRegionNode(view, cursor, endOff, depth + 1);
2973
3000
  if (!rightResult) {
2974
3001
  return null;
2975
3002
  }
@@ -3359,18 +3386,22 @@ function replayEmfPlusRecords(view, offset, length, ctx, _canvasW, _canvasH, sta
3359
3386
  if (recDataSize >= 4) {
3360
3387
  const totalSize = view.getUint32(dataOff, true);
3361
3388
  const objectType = recFlags >> 8 & 127;
3362
- rCtx.continuationTotalSize = totalSize;
3363
- rCtx.continuationObjectId = objectId;
3364
- rCtx.continuationObjectType = objectType;
3365
- rCtx.continuationBuffer = new Uint8Array(totalSize);
3366
- const chunkSize = recDataSize - 4;
3367
- const chunk = new Uint8Array(
3368
- view.buffer,
3369
- view.byteOffset + dataOff + 4,
3370
- Math.min(chunkSize, totalSize)
3371
- );
3372
- rCtx.continuationBuffer.set(chunk, 0);
3373
- rCtx.continuationOffset = chunk.length;
3389
+ const MAX_CONTINUATION_BYTES = 64 * 1024 * 1024;
3390
+ const remainingEmfPlusBytes = view.byteLength - dataOff;
3391
+ if (!Number.isFinite(totalSize) || totalSize <= 0 || totalSize > MAX_CONTINUATION_BYTES || totalSize > remainingEmfPlusBytes || recDataSize - 4 < 0) ; else {
3392
+ rCtx.continuationTotalSize = totalSize;
3393
+ rCtx.continuationObjectId = objectId;
3394
+ rCtx.continuationObjectType = objectType;
3395
+ rCtx.continuationBuffer = new Uint8Array(totalSize);
3396
+ const chunkSize = recDataSize - 4;
3397
+ const chunk = new Uint8Array(
3398
+ view.buffer,
3399
+ view.byteOffset + dataOff + 4,
3400
+ Math.min(chunkSize, totalSize)
3401
+ );
3402
+ rCtx.continuationBuffer.set(chunk, 0);
3403
+ rCtx.continuationOffset = chunk.length;
3404
+ }
3374
3405
  }
3375
3406
  } else {
3376
3407
  const remaining = rCtx.continuationTotalSize - rCtx.continuationOffset;
@@ -4015,8 +4046,11 @@ function replayWmfRecords(view, ctx, header, canvasW, canvasH) {
4015
4046
  }
4016
4047
 
4017
4048
  // src/emf-converter.ts
4018
- async function processDeferredImages(ctx, deferredImages) {
4019
- emfLog(`processDeferredImages: processing ${deferredImages.length} deferred images...`);
4049
+ var MAX_METAFILE_RECURSION = 3;
4050
+ async function processDeferredImages(ctx, deferredImages, recursionDepth = 0) {
4051
+ emfLog(
4052
+ `processDeferredImages: processing ${deferredImages.length} deferred images (recursionDepth=${recursionDepth})...`
4053
+ );
4020
4054
  for (let idx = 0; idx < deferredImages.length; idx++) {
4021
4055
  const img = deferredImages[idx];
4022
4056
  emfLog(
@@ -4035,8 +4069,26 @@ async function processDeferredImages(ctx, deferredImages) {
4035
4069
  img.transform[5]
4036
4070
  );
4037
4071
  if (img.isMetafile) {
4072
+ if (recursionDepth >= MAX_METAFILE_RECURSION) {
4073
+ emfWarn(
4074
+ ` Deferred image [${idx}]: skipping embedded metafile \u2014 recursion depth ${recursionDepth} >= ${MAX_METAFILE_RECURSION}`
4075
+ );
4076
+ continue;
4077
+ }
4038
4078
  emfLog(` Deferred image [${idx}]: recursively converting embedded metafile...`);
4039
- const metafileDataUrl = await convertEmfToDataUrl(plainBuffer) ?? await convertWmfToDataUrl(plainBuffer);
4079
+ const metafileDataUrl = await convertEmfToDataUrl(
4080
+ plainBuffer,
4081
+ void 0,
4082
+ void 0,
4083
+ void 0,
4084
+ recursionDepth + 1
4085
+ ) ?? await convertWmfToDataUrl(
4086
+ plainBuffer,
4087
+ void 0,
4088
+ void 0,
4089
+ void 0,
4090
+ recursionDepth + 1
4091
+ );
4040
4092
  if (metafileDataUrl) {
4041
4093
  emfLog(
4042
4094
  ` Deferred image [${idx}]: metafile converted, dataUrl length=${metafileDataUrl.length}`
@@ -4081,7 +4133,10 @@ async function processDeferredImages(ctx, deferredImages) {
4081
4133
  }
4082
4134
  ctx.setTransform(1, 0, 0, 1, 0, 0);
4083
4135
  }
4084
- async function convertEmfToDataUrl(buffer, maxWidth, maxHeight, optionsOrDpiScale) {
4136
+ async function convertEmfToDataUrl(buffer, maxWidth, maxHeight, optionsOrDpiScale, recursionDepth = 0) {
4137
+ if (recursionDepth > MAX_METAFILE_RECURSION) {
4138
+ return null;
4139
+ }
4085
4140
  const opts = typeof optionsOrDpiScale === "number" ? { dpiScale: optionsOrDpiScale } : optionsOrDpiScale ?? {};
4086
4141
  const dpiScale = opts.dpiScale ?? DEFAULT_DPI_SCALE;
4087
4142
  const effectiveMaxWidth = maxWidth ?? opts.maxWidth;
@@ -4150,7 +4205,10 @@ async function convertEmfToDataUrl(buffer, maxWidth, maxHeight, optionsOrDpiScal
4150
4205
  return null;
4151
4206
  }
4152
4207
  }
4153
- async function convertWmfToDataUrl(buffer, maxWidth, maxHeight, optionsOrDpiScale) {
4208
+ async function convertWmfToDataUrl(buffer, maxWidth, maxHeight, optionsOrDpiScale, recursionDepth = 0) {
4209
+ if (recursionDepth > MAX_METAFILE_RECURSION) {
4210
+ return null;
4211
+ }
4154
4212
  const opts = typeof optionsOrDpiScale === "number" ? { dpiScale: optionsOrDpiScale } : optionsOrDpiScale ?? {};
4155
4213
  const dpiScale = opts.dpiScale ?? DEFAULT_DPI_SCALE;
4156
4214
  const effectiveMaxWidth = maxWidth ?? opts.maxWidth;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "emf-converter",
3
- "version": "1.1.3",
3
+ "version": "1.1.5",
4
4
  "description": "Convert EMF/WMF metafile binaries to PNG data URLs using Canvas",
5
5
  "keywords": [
6
6
  "canvas",
@@ -48,6 +48,6 @@
48
48
  "devDependencies": {
49
49
  "tsup": "^8.5.1",
50
50
  "typescript": "^5.9.3",
51
- "vitest": "^4.1.0"
51
+ "vitest": "^4.1.5"
52
52
  }
53
53
  }