emf-converter 1.1.4 → 1.1.6
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 +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +91 -33
- package/dist/index.mjs +91 -33
- package/package.json +1 -1
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
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
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.
|
|
163
|
+
return chars.join("");
|
|
147
164
|
}
|
|
148
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
3365
|
-
|
|
3366
|
-
|
|
3367
|
-
|
|
3368
|
-
|
|
3369
|
-
|
|
3370
|
-
|
|
3371
|
-
|
|
3372
|
-
|
|
3373
|
-
|
|
3374
|
-
|
|
3375
|
-
|
|
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
|
-
|
|
4021
|
-
|
|
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(
|
|
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
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
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.
|
|
161
|
+
return chars.join("");
|
|
145
162
|
}
|
|
146
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
3363
|
-
|
|
3364
|
-
|
|
3365
|
-
|
|
3366
|
-
|
|
3367
|
-
|
|
3368
|
-
|
|
3369
|
-
|
|
3370
|
-
|
|
3371
|
-
|
|
3372
|
-
|
|
3373
|
-
|
|
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
|
-
|
|
4019
|
-
|
|
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(
|
|
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;
|