pptx-react-viewer 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.js +865 -159
- package/dist/index.mjs +866 -161
- package/dist/viewer/index.js +883 -165
- package/dist/viewer/index.mjs +884 -167
- package/node_modules/emf-converter/dist/index.d.mts +2 -2
- package/node_modules/emf-converter/dist/index.d.ts +2 -2
- package/node_modules/emf-converter/dist/index.js +91 -33
- package/node_modules/emf-converter/dist/index.mjs +91 -33
- package/node_modules/emf-converter/package.json +1 -1
- package/node_modules/mtx-decompressor/dist/index.js +39 -9
- package/node_modules/mtx-decompressor/dist/index.mjs +39 -9
- package/node_modules/mtx-decompressor/package.json +1 -1
- package/node_modules/pptx-viewer-core/dist/{SvgExporter-BQ4KbRO9.d.mts → SvgExporter-BTkk4oNQ.d.mts} +1 -1
- package/node_modules/pptx-viewer-core/dist/{SvgExporter-0TxiiorD.d.ts → SvgExporter-CTDG-t_z.d.ts} +1 -1
- package/node_modules/pptx-viewer-core/dist/cli/index.d.mts +2 -2
- package/node_modules/pptx-viewer-core/dist/cli/index.d.ts +2 -2
- package/node_modules/pptx-viewer-core/dist/cli/index.js +0 -0
- package/node_modules/pptx-viewer-core/dist/cli/index.mjs +0 -0
- package/node_modules/pptx-viewer-core/dist/converter/index.d.mts +3 -3
- package/node_modules/pptx-viewer-core/dist/converter/index.d.ts +3 -3
- package/node_modules/pptx-viewer-core/dist/converter/index.js +0 -0
- package/node_modules/pptx-viewer-core/dist/converter/index.mjs +0 -0
- package/node_modules/pptx-viewer-core/dist/index.d.mts +961 -59
- package/node_modules/pptx-viewer-core/dist/index.d.ts +961 -59
- package/node_modules/pptx-viewer-core/dist/index.js +29880 -16692
- package/node_modules/pptx-viewer-core/dist/index.mjs +29859 -16692
- package/node_modules/pptx-viewer-core/dist/{presentation-ArhfImJ5.d.mts → presentation-4fhI3din.d.mts} +835 -26
- package/node_modules/pptx-viewer-core/dist/{presentation-ArhfImJ5.d.ts → presentation-4fhI3din.d.ts} +835 -26
- package/node_modules/pptx-viewer-core/dist/{signature-inspection-status-BcJSdOvb.d.mts → signature-inspection-status-BCUpfCQh.d.mts} +13 -2
- package/node_modules/pptx-viewer-core/dist/{signature-inspection-status-BcJSdOvb.d.ts → signature-inspection-status-BCUpfCQh.d.ts} +13 -2
- package/node_modules/pptx-viewer-core/dist/signature-node/index.d.mts +2 -2
- package/node_modules/pptx-viewer-core/dist/signature-node/index.d.ts +2 -2
- package/node_modules/pptx-viewer-core/dist/signature-node/index.js +17 -3
- package/node_modules/pptx-viewer-core/dist/signature-node/index.mjs +16 -4
- package/node_modules/pptx-viewer-core/dist/{text-operations-rhJV-A_W.d.ts → text-operations-B9EwbptL.d.ts} +1 -1
- package/node_modules/pptx-viewer-core/dist/{text-operations-CLj-sJyk.d.mts → text-operations-C89Jn6S0.d.mts} +1 -1
- package/node_modules/pptx-viewer-core/package.json +1 -1
- package/package.json +6 -4
|
@@ -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.
|
|
@@ -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.
|
|
@@ -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;
|
|
@@ -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;
|
|
@@ -593,14 +593,8 @@ function decodeSimpleGlyph(numContours, streams, out, calcBBox, minX, minY, maxX
|
|
|
593
593
|
const flag = flagBytes[i];
|
|
594
594
|
onCurve[i] = flag & 128 ? 0 : 1;
|
|
595
595
|
const enc = TRIPLET_ENCODINGS[flag & 127];
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
for (let b = 0; b < extraBytes; b++) {
|
|
599
|
-
subBuf[b] = sGlyph.readU8();
|
|
600
|
-
}
|
|
601
|
-
const sub = new Stream(subBuf, extraBytes);
|
|
602
|
-
let dx = sub.readNBits(enc.xBits) + enc.deltaX;
|
|
603
|
-
let dy = sub.readNBits(enc.yBits) + enc.deltaY;
|
|
596
|
+
let dx = sGlyph.readNBits(enc.xBits) + enc.deltaX;
|
|
597
|
+
let dy = sGlyph.readNBits(enc.yBits) + enc.deltaY;
|
|
604
598
|
if (enc.xSign !== 0) {
|
|
605
599
|
dx *= enc.xSign;
|
|
606
600
|
}
|
|
@@ -1158,6 +1152,8 @@ var MAX_2BYTE_DIST = 512;
|
|
|
1158
1152
|
var PRELOAD_SIZE = 2 * 32 * 96 + 4 * 256;
|
|
1159
1153
|
var LEN_MIN = 2;
|
|
1160
1154
|
var DIST_MIN = 1;
|
|
1155
|
+
var MAX_OUT_LEN = 4 * 1024 * 1024;
|
|
1156
|
+
var MAX_OUT = 16 * 1024 * 1024;
|
|
1161
1157
|
var RLE_INITIAL = 0;
|
|
1162
1158
|
var RLE_NORMAL = 1;
|
|
1163
1159
|
var RLE_SEEN_ESCAPE = 2;
|
|
@@ -1167,6 +1163,9 @@ function setDistRange(length) {
|
|
|
1167
1163
|
let distMax = DIST_MIN + ((1 << DIST_WIDTH * numDistRanges) - 1);
|
|
1168
1164
|
while (distMax < length) {
|
|
1169
1165
|
numDistRanges++;
|
|
1166
|
+
if (numDistRanges > 8) {
|
|
1167
|
+
throw new Error("LZCOMP setDistRange: numDistRanges exceeds bound (8)");
|
|
1168
|
+
}
|
|
1170
1169
|
distMax = DIST_MIN + ((1 << DIST_WIDTH * numDistRanges) - 1);
|
|
1171
1170
|
}
|
|
1172
1171
|
const DUP2 = 256 + (1 << LEN_WIDTH) * numDistRanges;
|
|
@@ -1197,7 +1196,11 @@ function decodeLength(lenEcoder, symbol, numDistRangesOut) {
|
|
|
1197
1196
|
let firstTime = true;
|
|
1198
1197
|
let value = 0;
|
|
1199
1198
|
let done;
|
|
1199
|
+
let iters = 0;
|
|
1200
1200
|
do {
|
|
1201
|
+
if (++iters > 16) {
|
|
1202
|
+
throw new Error("LZCOMP decodeLength: iteration cap exceeded");
|
|
1203
|
+
}
|
|
1201
1204
|
let bits;
|
|
1202
1205
|
if (firstTime) {
|
|
1203
1206
|
bits = symbol - 256;
|
|
@@ -1236,6 +1239,9 @@ function lzcompDecompress(data, size, version) {
|
|
|
1236
1239
|
const distEcoder = new AHuff(bio, 1 << DIST_WIDTH);
|
|
1237
1240
|
const lenEcoder = new AHuff(bio, 1 << LEN_WIDTH);
|
|
1238
1241
|
const outLen = bio.readValue(24);
|
|
1242
|
+
if (outLen > MAX_OUT_LEN) {
|
|
1243
|
+
throw new Error(`LZCOMP outLen ${outLen} exceeds maximum (${MAX_OUT_LEN})`);
|
|
1244
|
+
}
|
|
1239
1245
|
const { DUP2, DUP4, DUP6, NUM_SYMS } = setDistRange(outLen);
|
|
1240
1246
|
const symEcoder = new AHuff(bio, NUM_SYMS);
|
|
1241
1247
|
const windowSize = PRELOAD_SIZE + outLen;
|
|
@@ -1252,6 +1258,9 @@ function lzcompDecompress(data, size, version) {
|
|
|
1252
1258
|
if (!usingRunLength) {
|
|
1253
1259
|
if (outIdx >= outBufSize) {
|
|
1254
1260
|
outBufSize += outBufSize >>> 1;
|
|
1261
|
+
if (outBufSize > MAX_OUT) {
|
|
1262
|
+
throw new Error("LZCOMP output exceeds maximum size budget");
|
|
1263
|
+
}
|
|
1255
1264
|
const tmp = new Uint8Array(outBufSize);
|
|
1256
1265
|
tmp.set(outBuf);
|
|
1257
1266
|
outBuf = tmp;
|
|
@@ -1270,6 +1279,9 @@ function lzcompDecompress(data, size, version) {
|
|
|
1270
1279
|
} else {
|
|
1271
1280
|
if (outIdx >= outBufSize) {
|
|
1272
1281
|
outBufSize += outBufSize >>> 1;
|
|
1282
|
+
if (outBufSize > MAX_OUT) {
|
|
1283
|
+
throw new Error("LZCOMP output exceeds maximum size budget");
|
|
1284
|
+
}
|
|
1273
1285
|
const tmp = new Uint8Array(outBufSize);
|
|
1274
1286
|
tmp.set(outBuf);
|
|
1275
1287
|
outBuf = tmp;
|
|
@@ -1282,6 +1294,9 @@ function lzcompDecompress(data, size, version) {
|
|
|
1282
1294
|
if (rleCount === 0) {
|
|
1283
1295
|
if (outIdx >= outBufSize) {
|
|
1284
1296
|
outBufSize += outBufSize >>> 1;
|
|
1297
|
+
if (outBufSize > MAX_OUT) {
|
|
1298
|
+
throw new Error("LZCOMP output exceeds maximum size budget");
|
|
1299
|
+
}
|
|
1285
1300
|
const tmp = new Uint8Array(outBufSize);
|
|
1286
1301
|
tmp.set(outBuf);
|
|
1287
1302
|
outBuf = tmp;
|
|
@@ -1295,6 +1310,9 @@ function lzcompDecompress(data, size, version) {
|
|
|
1295
1310
|
case RLE_NEED_BYTE: {
|
|
1296
1311
|
if (outIdx + rleCount > outBufSize) {
|
|
1297
1312
|
outBufSize = outIdx + rleCount + (outBufSize >>> 1);
|
|
1313
|
+
if (outBufSize > MAX_OUT) {
|
|
1314
|
+
throw new Error("LZCOMP output exceeds maximum size budget");
|
|
1315
|
+
}
|
|
1298
1316
|
const tmp = new Uint8Array(outBufSize);
|
|
1299
1317
|
tmp.set(outBuf);
|
|
1300
1318
|
outBuf = tmp;
|
|
@@ -1458,11 +1476,23 @@ function dumpContainer(ctr) {
|
|
|
1458
1476
|
// src/mtx-decompress.ts
|
|
1459
1477
|
var ENCRYPTION_KEY = 80;
|
|
1460
1478
|
function unpackMtx(data, size) {
|
|
1479
|
+
if (size < 10 || data.length < 10) {
|
|
1480
|
+
throw new Error("MTX data too small: header requires at least 10 bytes");
|
|
1481
|
+
}
|
|
1461
1482
|
const versionMagic = data[0];
|
|
1462
1483
|
const offset2 = data[4] << 16 | data[5] << 8 | data[6];
|
|
1463
1484
|
const offset3 = data[7] << 16 | data[8] << 8 | data[9];
|
|
1485
|
+
if (offset2 < 10 || offset3 < offset2 || offset3 > size) {
|
|
1486
|
+
throw new Error(
|
|
1487
|
+
`MTX header offsets out of bounds: offset2=${offset2}, offset3=${offset3}, size=${size}`
|
|
1488
|
+
);
|
|
1489
|
+
}
|
|
1464
1490
|
const offsets = [10, offset2, offset3];
|
|
1465
|
-
const blockSizes = [
|
|
1491
|
+
const blockSizes = [
|
|
1492
|
+
Math.max(0, offset2 - 10),
|
|
1493
|
+
Math.max(0, offset3 - offset2),
|
|
1494
|
+
Math.max(0, size - offset3)
|
|
1495
|
+
];
|
|
1466
1496
|
const streams = [];
|
|
1467
1497
|
const decompressedSizes = [];
|
|
1468
1498
|
for (let i = 0; i < 3; i++) {
|