@s8fy/pptx-parser 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +36 -0
- package/README.md +336 -0
- package/dist/index.cjs +24649 -0
- package/dist/index.d.cts +1212 -0
- package/dist/index.d.ts +1212 -0
- package/dist/index.js +24642 -0
- package/dist/index.umd.js +24653 -0
- package/dist/js-worker.js +23220 -0
- package/dist/mbt/main.wasm +0 -0
- package/dist/mbt/pdf-converter.wasm +0 -0
- package/dist/pdf-worker.js +1692 -0
- package/dist/wasm-worker.js +1137 -0
- package/package.json +74 -0
|
@@ -0,0 +1,1137 @@
|
|
|
1
|
+
//#region src/latin1.ts
|
|
2
|
+
/**
|
|
3
|
+
* Convert raw bytes to a Latin-1 string where each char code maps 1:1 to a byte.
|
|
4
|
+
* NOTE: Do not use TextDecoder('latin1') for binary transport; browsers alias it
|
|
5
|
+
* to Windows-1252 and remap bytes 0x80-0x9F to different code points.
|
|
6
|
+
*/
|
|
7
|
+
function uint8ArrayToLatin1(bytes) {
|
|
8
|
+
const len = bytes.length;
|
|
9
|
+
if (len === 0) return "";
|
|
10
|
+
const chunks = [];
|
|
11
|
+
const CHUNK = 8192;
|
|
12
|
+
for (let i = 0; i < len; i += CHUNK) chunks.push(String.fromCharCode.apply(null, bytes.subarray(i, i + CHUNK)));
|
|
13
|
+
return chunks.join("");
|
|
14
|
+
}
|
|
15
|
+
function arrayBufferToLatin1(buffer) {
|
|
16
|
+
return uint8ArrayToLatin1(new Uint8Array(buffer));
|
|
17
|
+
}
|
|
18
|
+
//#endregion
|
|
19
|
+
//#region src/wasm-loader.ts
|
|
20
|
+
function fileUrl(url) {
|
|
21
|
+
try {
|
|
22
|
+
const parsed = url instanceof URL ? url : new URL(url);
|
|
23
|
+
return parsed.protocol === "file:" ? parsed : void 0;
|
|
24
|
+
} catch {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
function isUnsupportedNodeFileFetch(error) {
|
|
29
|
+
const cause = error?.cause;
|
|
30
|
+
const detail = cause instanceof Error ? cause.message : "";
|
|
31
|
+
return error instanceof TypeError && error.message === "fetch failed" && detail.includes("not implemented");
|
|
32
|
+
}
|
|
33
|
+
async function readNodeFile(url) {
|
|
34
|
+
const fs = globalThis.process?.getBuiltinModule?.("node:fs")?.promises;
|
|
35
|
+
if (!fs) throw new Error(`Node.js file loading is unavailable for ${url.href}`);
|
|
36
|
+
const bytes = await fs.readFile(url);
|
|
37
|
+
return bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
|
|
38
|
+
}
|
|
39
|
+
function assertWasmBytes(bytes, url, label, contentType) {
|
|
40
|
+
const magic = new Uint8Array(bytes, 0, Math.min(bytes.byteLength, 4));
|
|
41
|
+
if (!(magic.length === 4 && magic[0] === 0 && magic[1] === 97 && magic[2] === 115 && magic[3] === 109)) throw new Error(`[${label}] Invalid WASM response from ${String(url)}: content-type=${contentType}, bytes=${bytes.byteLength}. Check that the WASM asset exists and is served by the app.`);
|
|
42
|
+
}
|
|
43
|
+
async function fetchWasmBytes(url, label) {
|
|
44
|
+
let response;
|
|
45
|
+
try {
|
|
46
|
+
response = await fetch(url);
|
|
47
|
+
} catch (error) {
|
|
48
|
+
const localFile = fileUrl(url);
|
|
49
|
+
if (!localFile || !isUnsupportedNodeFileFetch(error)) throw error;
|
|
50
|
+
const bytes = await readNodeFile(localFile);
|
|
51
|
+
assertWasmBytes(bytes, url, label, "application/wasm");
|
|
52
|
+
return bytes;
|
|
53
|
+
}
|
|
54
|
+
if (!response.ok) throw new Error(`[${label}] Failed to load WASM ${String(url)}: ${response.status} ${response.statusText}`);
|
|
55
|
+
const bytes = await response.arrayBuffer();
|
|
56
|
+
assertWasmBytes(bytes, url, label, response.headers.get("content-type") || "unknown");
|
|
57
|
+
return bytes;
|
|
58
|
+
}
|
|
59
|
+
function createWasmStringConstants() {
|
|
60
|
+
return new Proxy(Object.create(null), { get: (_target, prop) => typeof prop === "string" ? prop : void 0 });
|
|
61
|
+
}
|
|
62
|
+
//#endregion
|
|
63
|
+
//#region ../../node_modules/.pnpm/fflate@0.8.3/node_modules/fflate/esm/browser.js
|
|
64
|
+
var u8 = Uint8Array;
|
|
65
|
+
var u16 = Uint16Array;
|
|
66
|
+
var i32 = Int32Array;
|
|
67
|
+
var fleb = new u8([
|
|
68
|
+
0,
|
|
69
|
+
0,
|
|
70
|
+
0,
|
|
71
|
+
0,
|
|
72
|
+
0,
|
|
73
|
+
0,
|
|
74
|
+
0,
|
|
75
|
+
0,
|
|
76
|
+
1,
|
|
77
|
+
1,
|
|
78
|
+
1,
|
|
79
|
+
1,
|
|
80
|
+
2,
|
|
81
|
+
2,
|
|
82
|
+
2,
|
|
83
|
+
2,
|
|
84
|
+
3,
|
|
85
|
+
3,
|
|
86
|
+
3,
|
|
87
|
+
3,
|
|
88
|
+
4,
|
|
89
|
+
4,
|
|
90
|
+
4,
|
|
91
|
+
4,
|
|
92
|
+
5,
|
|
93
|
+
5,
|
|
94
|
+
5,
|
|
95
|
+
5,
|
|
96
|
+
0,
|
|
97
|
+
0,
|
|
98
|
+
0,
|
|
99
|
+
0
|
|
100
|
+
]);
|
|
101
|
+
var fdeb = new u8([
|
|
102
|
+
0,
|
|
103
|
+
0,
|
|
104
|
+
0,
|
|
105
|
+
0,
|
|
106
|
+
1,
|
|
107
|
+
1,
|
|
108
|
+
2,
|
|
109
|
+
2,
|
|
110
|
+
3,
|
|
111
|
+
3,
|
|
112
|
+
4,
|
|
113
|
+
4,
|
|
114
|
+
5,
|
|
115
|
+
5,
|
|
116
|
+
6,
|
|
117
|
+
6,
|
|
118
|
+
7,
|
|
119
|
+
7,
|
|
120
|
+
8,
|
|
121
|
+
8,
|
|
122
|
+
9,
|
|
123
|
+
9,
|
|
124
|
+
10,
|
|
125
|
+
10,
|
|
126
|
+
11,
|
|
127
|
+
11,
|
|
128
|
+
12,
|
|
129
|
+
12,
|
|
130
|
+
13,
|
|
131
|
+
13,
|
|
132
|
+
0,
|
|
133
|
+
0
|
|
134
|
+
]);
|
|
135
|
+
var clim = new u8([
|
|
136
|
+
16,
|
|
137
|
+
17,
|
|
138
|
+
18,
|
|
139
|
+
0,
|
|
140
|
+
8,
|
|
141
|
+
7,
|
|
142
|
+
9,
|
|
143
|
+
6,
|
|
144
|
+
10,
|
|
145
|
+
5,
|
|
146
|
+
11,
|
|
147
|
+
4,
|
|
148
|
+
12,
|
|
149
|
+
3,
|
|
150
|
+
13,
|
|
151
|
+
2,
|
|
152
|
+
14,
|
|
153
|
+
1,
|
|
154
|
+
15
|
|
155
|
+
]);
|
|
156
|
+
var freb = function(eb, start) {
|
|
157
|
+
var b = new u16(31);
|
|
158
|
+
for (var i = 0; i < 31; ++i) b[i] = start += 1 << eb[i - 1];
|
|
159
|
+
var r = new i32(b[30]);
|
|
160
|
+
for (var i = 1; i < 30; ++i) for (var j = b[i]; j < b[i + 1]; ++j) r[j] = j - b[i] << 5 | i;
|
|
161
|
+
return {
|
|
162
|
+
b,
|
|
163
|
+
r
|
|
164
|
+
};
|
|
165
|
+
};
|
|
166
|
+
var _a = freb(fleb, 2);
|
|
167
|
+
var fl = _a.b;
|
|
168
|
+
var revfl = _a.r;
|
|
169
|
+
fl[28] = 258, revfl[258] = 28;
|
|
170
|
+
var _b = freb(fdeb, 0);
|
|
171
|
+
var fd = _b.b;
|
|
172
|
+
_b.r;
|
|
173
|
+
var rev = new u16(32768);
|
|
174
|
+
for (var i = 0; i < 32768; ++i) {
|
|
175
|
+
var x = (i & 43690) >> 1 | (i & 21845) << 1;
|
|
176
|
+
x = (x & 52428) >> 2 | (x & 13107) << 2;
|
|
177
|
+
x = (x & 61680) >> 4 | (x & 3855) << 4;
|
|
178
|
+
rev[i] = ((x & 65280) >> 8 | (x & 255) << 8) >> 1;
|
|
179
|
+
}
|
|
180
|
+
var hMap = (function(cd, mb, r) {
|
|
181
|
+
var s = cd.length;
|
|
182
|
+
var i = 0;
|
|
183
|
+
var l = new u16(mb);
|
|
184
|
+
for (; i < s; ++i) if (cd[i]) ++l[cd[i] - 1];
|
|
185
|
+
var le = new u16(mb);
|
|
186
|
+
for (i = 1; i < mb; ++i) le[i] = le[i - 1] + l[i - 1] << 1;
|
|
187
|
+
var co;
|
|
188
|
+
if (r) {
|
|
189
|
+
co = new u16(1 << mb);
|
|
190
|
+
var rvb = 15 - mb;
|
|
191
|
+
for (i = 0; i < s; ++i) if (cd[i]) {
|
|
192
|
+
var sv = i << 4 | cd[i];
|
|
193
|
+
var r_1 = mb - cd[i];
|
|
194
|
+
var v = le[cd[i] - 1]++ << r_1;
|
|
195
|
+
for (var m = v | (1 << r_1) - 1; v <= m; ++v) co[rev[v] >> rvb] = sv;
|
|
196
|
+
}
|
|
197
|
+
} else {
|
|
198
|
+
co = new u16(s);
|
|
199
|
+
for (i = 0; i < s; ++i) if (cd[i]) co[i] = rev[le[cd[i] - 1]++] >> 15 - cd[i];
|
|
200
|
+
}
|
|
201
|
+
return co;
|
|
202
|
+
});
|
|
203
|
+
var flt = new u8(288);
|
|
204
|
+
for (var i = 0; i < 144; ++i) flt[i] = 8;
|
|
205
|
+
for (var i = 144; i < 256; ++i) flt[i] = 9;
|
|
206
|
+
for (var i = 256; i < 280; ++i) flt[i] = 7;
|
|
207
|
+
for (var i = 280; i < 288; ++i) flt[i] = 8;
|
|
208
|
+
var fdt = new u8(32);
|
|
209
|
+
for (var i = 0; i < 32; ++i) fdt[i] = 5;
|
|
210
|
+
var flrm = /*#__PURE__*/ hMap(flt, 9, 1);
|
|
211
|
+
var fdrm = /*#__PURE__*/ hMap(fdt, 5, 1);
|
|
212
|
+
var max = function(a) {
|
|
213
|
+
var m = a[0];
|
|
214
|
+
for (var i = 1; i < a.length; ++i) if (a[i] > m) m = a[i];
|
|
215
|
+
return m;
|
|
216
|
+
};
|
|
217
|
+
var bits = function(d, p, m) {
|
|
218
|
+
var o = p / 8 | 0;
|
|
219
|
+
return (d[o] | d[o + 1] << 8) >> (p & 7) & m;
|
|
220
|
+
};
|
|
221
|
+
var bits16 = function(d, p) {
|
|
222
|
+
var o = p / 8 | 0;
|
|
223
|
+
return (d[o] | d[o + 1] << 8 | d[o + 2] << 16) >> (p & 7);
|
|
224
|
+
};
|
|
225
|
+
var shft = function(p) {
|
|
226
|
+
return (p + 7) / 8 | 0;
|
|
227
|
+
};
|
|
228
|
+
var slc = function(v, s, e) {
|
|
229
|
+
if (s == null || s < 0) s = 0;
|
|
230
|
+
if (e == null || e > v.length) e = v.length;
|
|
231
|
+
return new u8(v.subarray(s, e));
|
|
232
|
+
};
|
|
233
|
+
var ec = [
|
|
234
|
+
"unexpected EOF",
|
|
235
|
+
"invalid block type",
|
|
236
|
+
"invalid length/literal",
|
|
237
|
+
"invalid distance",
|
|
238
|
+
"stream finished",
|
|
239
|
+
"no stream handler",
|
|
240
|
+
,
|
|
241
|
+
"no callback",
|
|
242
|
+
"invalid UTF-8 data",
|
|
243
|
+
"extra field too long",
|
|
244
|
+
"date not in range 1980-2099",
|
|
245
|
+
"filename too long",
|
|
246
|
+
"stream finishing",
|
|
247
|
+
"invalid zip data"
|
|
248
|
+
];
|
|
249
|
+
var err = function(ind, msg, nt) {
|
|
250
|
+
var e = new Error(msg || ec[ind]);
|
|
251
|
+
e.code = ind;
|
|
252
|
+
if (Error.captureStackTrace) Error.captureStackTrace(e, err);
|
|
253
|
+
if (!nt) throw e;
|
|
254
|
+
return e;
|
|
255
|
+
};
|
|
256
|
+
var inflt = function(dat, st, buf, dict) {
|
|
257
|
+
var sl = dat.length, dl = dict ? dict.length : 0;
|
|
258
|
+
if (!sl || st.f && !st.l) return buf || new u8(0);
|
|
259
|
+
var noBuf = !buf;
|
|
260
|
+
var resize = noBuf || st.i != 2;
|
|
261
|
+
var noSt = st.i;
|
|
262
|
+
if (noBuf) buf = new u8(sl * 3);
|
|
263
|
+
var cbuf = function(l) {
|
|
264
|
+
var bl = buf.length;
|
|
265
|
+
if (l > bl) {
|
|
266
|
+
var nbuf = new u8(Math.max(bl * 2, l));
|
|
267
|
+
nbuf.set(buf);
|
|
268
|
+
buf = nbuf;
|
|
269
|
+
}
|
|
270
|
+
};
|
|
271
|
+
var final = st.f || 0, pos = st.p || 0, bt = st.b || 0, lm = st.l, dm = st.d, lbt = st.m, dbt = st.n;
|
|
272
|
+
var tbts = sl * 8;
|
|
273
|
+
do {
|
|
274
|
+
if (!lm) {
|
|
275
|
+
final = bits(dat, pos, 1);
|
|
276
|
+
var type = bits(dat, pos + 1, 3);
|
|
277
|
+
pos += 3;
|
|
278
|
+
if (!type) {
|
|
279
|
+
var s = shft(pos) + 4, l = dat[s - 4] | dat[s - 3] << 8, t = s + l;
|
|
280
|
+
if (t > sl) {
|
|
281
|
+
if (noSt) err(0);
|
|
282
|
+
break;
|
|
283
|
+
}
|
|
284
|
+
if (resize) cbuf(bt + l);
|
|
285
|
+
buf.set(dat.subarray(s, t), bt);
|
|
286
|
+
st.b = bt += l, st.p = pos = t * 8, st.f = final;
|
|
287
|
+
continue;
|
|
288
|
+
} else if (type == 1) lm = flrm, dm = fdrm, lbt = 9, dbt = 5;
|
|
289
|
+
else if (type == 2) {
|
|
290
|
+
var hLit = bits(dat, pos, 31) + 257, hcLen = bits(dat, pos + 10, 15) + 4;
|
|
291
|
+
var tl = hLit + bits(dat, pos + 5, 31) + 1;
|
|
292
|
+
pos += 14;
|
|
293
|
+
var ldt = new u8(tl);
|
|
294
|
+
var clt = new u8(19);
|
|
295
|
+
for (var i = 0; i < hcLen; ++i) clt[clim[i]] = bits(dat, pos + i * 3, 7);
|
|
296
|
+
pos += hcLen * 3;
|
|
297
|
+
var clb = max(clt), clbmsk = (1 << clb) - 1;
|
|
298
|
+
var clm = hMap(clt, clb, 1);
|
|
299
|
+
for (var i = 0; i < tl;) {
|
|
300
|
+
var r = clm[bits(dat, pos, clbmsk)];
|
|
301
|
+
pos += r & 15;
|
|
302
|
+
var s = r >> 4;
|
|
303
|
+
if (s < 16) ldt[i++] = s;
|
|
304
|
+
else {
|
|
305
|
+
var c = 0, n = 0;
|
|
306
|
+
if (s == 16) n = 3 + bits(dat, pos, 3), pos += 2, c = ldt[i - 1];
|
|
307
|
+
else if (s == 17) n = 3 + bits(dat, pos, 7), pos += 3;
|
|
308
|
+
else if (s == 18) n = 11 + bits(dat, pos, 127), pos += 7;
|
|
309
|
+
while (n--) ldt[i++] = c;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
var lt = ldt.subarray(0, hLit), dt = ldt.subarray(hLit);
|
|
313
|
+
lbt = max(lt);
|
|
314
|
+
dbt = max(dt);
|
|
315
|
+
lm = hMap(lt, lbt, 1);
|
|
316
|
+
dm = hMap(dt, dbt, 1);
|
|
317
|
+
} else err(1);
|
|
318
|
+
if (pos > tbts) {
|
|
319
|
+
if (noSt) err(0);
|
|
320
|
+
break;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
if (resize) cbuf(bt + 131072);
|
|
324
|
+
var lms = (1 << lbt) - 1, dms = (1 << dbt) - 1;
|
|
325
|
+
var lpos = pos;
|
|
326
|
+
for (;; lpos = pos) {
|
|
327
|
+
var c = lm[bits16(dat, pos) & lms], sym = c >> 4;
|
|
328
|
+
pos += c & 15;
|
|
329
|
+
if (pos > tbts) {
|
|
330
|
+
if (noSt) err(0);
|
|
331
|
+
break;
|
|
332
|
+
}
|
|
333
|
+
if (!c) err(2);
|
|
334
|
+
if (sym < 256) buf[bt++] = sym;
|
|
335
|
+
else if (sym == 256) {
|
|
336
|
+
lpos = pos, lm = null;
|
|
337
|
+
break;
|
|
338
|
+
} else {
|
|
339
|
+
var add = sym - 254;
|
|
340
|
+
if (sym > 264) {
|
|
341
|
+
var i = sym - 257, b = fleb[i];
|
|
342
|
+
add = bits(dat, pos, (1 << b) - 1) + fl[i];
|
|
343
|
+
pos += b;
|
|
344
|
+
}
|
|
345
|
+
var d = dm[bits16(dat, pos) & dms], dsym = d >> 4;
|
|
346
|
+
if (!d) err(3);
|
|
347
|
+
pos += d & 15;
|
|
348
|
+
var dt = fd[dsym];
|
|
349
|
+
if (dsym > 3) {
|
|
350
|
+
var b = fdeb[dsym];
|
|
351
|
+
dt += bits16(dat, pos) & (1 << b) - 1, pos += b;
|
|
352
|
+
}
|
|
353
|
+
if (pos > tbts) {
|
|
354
|
+
if (noSt) err(0);
|
|
355
|
+
break;
|
|
356
|
+
}
|
|
357
|
+
if (resize) cbuf(bt + 131072);
|
|
358
|
+
var end = bt + add;
|
|
359
|
+
if (bt < dt) {
|
|
360
|
+
var shift = dl - dt, dend = Math.min(dt, end);
|
|
361
|
+
if (shift + bt < 0) err(3);
|
|
362
|
+
for (; bt < dend; ++bt) buf[bt] = dict[shift + bt];
|
|
363
|
+
}
|
|
364
|
+
for (; bt < end; ++bt) buf[bt] = buf[bt - dt];
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
st.l = lm, st.p = lpos, st.b = bt, st.f = final;
|
|
368
|
+
if (lm) final = 1, st.m = lbt, st.d = dm, st.n = dbt;
|
|
369
|
+
} while (!final);
|
|
370
|
+
return bt != buf.length && noBuf ? slc(buf, 0, bt) : buf.subarray(0, bt);
|
|
371
|
+
};
|
|
372
|
+
var et = /*#__PURE__*/ new u8(0);
|
|
373
|
+
var b2 = function(d, b) {
|
|
374
|
+
return d[b] | d[b + 1] << 8;
|
|
375
|
+
};
|
|
376
|
+
var b4 = function(d, b) {
|
|
377
|
+
return (d[b] | d[b + 1] << 8 | d[b + 2] << 16 | d[b + 3] << 24) >>> 0;
|
|
378
|
+
};
|
|
379
|
+
var b8 = function(d, b) {
|
|
380
|
+
return b4(d, b) + b4(d, b + 4) * 4294967296;
|
|
381
|
+
};
|
|
382
|
+
function inflateSync(data, opts) {
|
|
383
|
+
return inflt(data, { i: 2 }, opts && opts.out, opts && opts.dictionary);
|
|
384
|
+
}
|
|
385
|
+
var td = typeof TextDecoder != "undefined" && /*#__PURE__*/ new TextDecoder();
|
|
386
|
+
try {
|
|
387
|
+
td.decode(et, { stream: true });
|
|
388
|
+
} catch (e) {}
|
|
389
|
+
var dutf8 = function(d) {
|
|
390
|
+
for (var r = "", i = 0;;) {
|
|
391
|
+
var c = d[i++];
|
|
392
|
+
var eb = (c > 127) + (c > 223) + (c > 239);
|
|
393
|
+
if (i + eb > d.length) return {
|
|
394
|
+
s: r,
|
|
395
|
+
r: slc(d, i - 1)
|
|
396
|
+
};
|
|
397
|
+
if (!eb) r += String.fromCharCode(c);
|
|
398
|
+
else if (eb == 3) c = ((c & 15) << 18 | (d[i++] & 63) << 12 | (d[i++] & 63) << 6 | d[i++] & 63) - 65536, r += String.fromCharCode(55296 | c >> 10, 56320 | c & 1023);
|
|
399
|
+
else if (eb & 1) r += String.fromCharCode((c & 31) << 6 | d[i++] & 63);
|
|
400
|
+
else r += String.fromCharCode((c & 15) << 12 | (d[i++] & 63) << 6 | d[i++] & 63);
|
|
401
|
+
}
|
|
402
|
+
};
|
|
403
|
+
/**
|
|
404
|
+
* Converts a Uint8Array to a string
|
|
405
|
+
* @param dat The data to decode to string
|
|
406
|
+
* @param latin1 Whether or not to interpret the data as Latin-1. This should
|
|
407
|
+
* not need to be true unless encoding to binary string.
|
|
408
|
+
* @returns The original UTF-8/Latin-1 string
|
|
409
|
+
*/
|
|
410
|
+
function strFromU8(dat, latin1) {
|
|
411
|
+
if (latin1) {
|
|
412
|
+
var r = "";
|
|
413
|
+
for (var i = 0; i < dat.length; i += 16384) r += String.fromCharCode.apply(null, dat.subarray(i, i + 16384));
|
|
414
|
+
return r;
|
|
415
|
+
} else if (td) return td.decode(dat);
|
|
416
|
+
else {
|
|
417
|
+
var _a = dutf8(dat), s = _a.s, r = _a.r;
|
|
418
|
+
if (r.length) err(8);
|
|
419
|
+
return s;
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
var slzh = function(d, b) {
|
|
423
|
+
return b + 30 + b2(d, b + 26) + b2(d, b + 28);
|
|
424
|
+
};
|
|
425
|
+
var zh = function(d, b, z) {
|
|
426
|
+
var fnl = b2(d, b + 28), efl = b2(d, b + 30), fn = strFromU8(d.subarray(b + 46, b + 46 + fnl), !(b2(d, b + 8) & 2048)), es = b + 46 + fnl;
|
|
427
|
+
var _a = z64hs(d, es, efl, z, b4(d, b + 20), b4(d, b + 24), b4(d, b + 42)), sc = _a[0], su = _a[1], off = _a[2];
|
|
428
|
+
return [
|
|
429
|
+
b2(d, b + 10),
|
|
430
|
+
sc,
|
|
431
|
+
su,
|
|
432
|
+
fn,
|
|
433
|
+
es + efl + b2(d, b + 32),
|
|
434
|
+
off
|
|
435
|
+
];
|
|
436
|
+
};
|
|
437
|
+
var z64hs = function(d, b, l, z, sc, su, off) {
|
|
438
|
+
var nsc = sc == 4294967295, nsu = su == 4294967295, noff = off == 4294967295, e = b + l;
|
|
439
|
+
var nf = nsc + nsu + noff;
|
|
440
|
+
if (z && nf) {
|
|
441
|
+
for (; b + 4 < e; b += 4 + b2(d, b + 2)) if (b2(d, b) == 1) return [
|
|
442
|
+
nsc ? b8(d, b + 4 + 8 * nsu) : sc,
|
|
443
|
+
nsu ? b8(d, b + 4) : su,
|
|
444
|
+
noff ? b8(d, b + 4 + 8 * (nsu + nsc)) : off,
|
|
445
|
+
1
|
|
446
|
+
];
|
|
447
|
+
if (z < 2) err(13);
|
|
448
|
+
}
|
|
449
|
+
return [
|
|
450
|
+
sc,
|
|
451
|
+
su,
|
|
452
|
+
off,
|
|
453
|
+
0
|
|
454
|
+
];
|
|
455
|
+
};
|
|
456
|
+
/**
|
|
457
|
+
* Synchronously decompresses a ZIP archive. Prefer using `unzip` for better
|
|
458
|
+
* performance with more than one file.
|
|
459
|
+
* @param data The raw compressed ZIP file
|
|
460
|
+
* @param opts The ZIP extraction options
|
|
461
|
+
* @returns The decompressed files
|
|
462
|
+
*/
|
|
463
|
+
function unzipSync(data, opts) {
|
|
464
|
+
var files = {};
|
|
465
|
+
var e = data.length - 22;
|
|
466
|
+
for (; b4(data, e) != 101010256; --e) if (!e || data.length - e > 65558) err(13);
|
|
467
|
+
var c = b2(data, e + 8);
|
|
468
|
+
if (!c) return {};
|
|
469
|
+
var o = b4(data, e + 16);
|
|
470
|
+
var z = b4(data, e - 20) == 117853008;
|
|
471
|
+
if (z) {
|
|
472
|
+
var ze = b4(data, e - 12);
|
|
473
|
+
z = b4(data, ze) == 101075792;
|
|
474
|
+
if (z) {
|
|
475
|
+
c = b4(data, ze + 32);
|
|
476
|
+
o = b4(data, ze + 48);
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
var fltr = opts && opts.filter;
|
|
480
|
+
for (var i = 0; i < c; ++i) {
|
|
481
|
+
var _a = zh(data, o, z), c_2 = _a[0], sc = _a[1], su = _a[2], fn = _a[3], no = _a[4], off = _a[5], b = slzh(data, off);
|
|
482
|
+
o = no;
|
|
483
|
+
if (!fltr || fltr({
|
|
484
|
+
name: fn,
|
|
485
|
+
size: sc,
|
|
486
|
+
originalSize: su,
|
|
487
|
+
compression: c_2
|
|
488
|
+
})) {
|
|
489
|
+
if (!c_2) files[fn] = slc(data, b, b + sc);
|
|
490
|
+
else if (c_2 == 8) files[fn] = inflateSync(data.subarray(b, b + sc), { out: new u8(su) });
|
|
491
|
+
else err(14, "unknown compression type " + c_2);
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
return files;
|
|
495
|
+
}
|
|
496
|
+
//#endregion
|
|
497
|
+
//#region src/resource-policy.generated.ts
|
|
498
|
+
const RESOURCE_POLICY_CONTRACT = {
|
|
499
|
+
schemaVersion: "xdoc-resource-policy-registry/v1",
|
|
500
|
+
policyVersion: "2026.08",
|
|
501
|
+
profiles: [{
|
|
502
|
+
id: "native-api",
|
|
503
|
+
inputBytes: 314572800,
|
|
504
|
+
zipEntries: 5e4,
|
|
505
|
+
zipExpansionBytes: 1073741824,
|
|
506
|
+
zipEntryBytes: 268435456,
|
|
507
|
+
zipRatioThresholdBytes: 67108864,
|
|
508
|
+
zipRatioMax: 200,
|
|
509
|
+
xmlDepth: 256,
|
|
510
|
+
xmlNodes: 1e7,
|
|
511
|
+
textNodeBytes: 16777216,
|
|
512
|
+
imagePixels: 2e8,
|
|
513
|
+
outputBytes: 1073741824,
|
|
514
|
+
wallClockMs: 9e5,
|
|
515
|
+
tempDiskBytes: 4294967296
|
|
516
|
+
}, {
|
|
517
|
+
id: "browser-viewer",
|
|
518
|
+
inputBytes: 104857600,
|
|
519
|
+
zipEntries: 1e4,
|
|
520
|
+
zipExpansionBytes: 536870912,
|
|
521
|
+
zipEntryBytes: 134217728,
|
|
522
|
+
zipRatioThresholdBytes: 67108864,
|
|
523
|
+
zipRatioMax: 200,
|
|
524
|
+
xmlDepth: 256,
|
|
525
|
+
xmlNodes: 2e6,
|
|
526
|
+
textNodeBytes: 8388608,
|
|
527
|
+
imagePixels: 1e8,
|
|
528
|
+
outputBytes: 536870912,
|
|
529
|
+
wallClockMs: 6e4,
|
|
530
|
+
tempDiskBytes: null
|
|
531
|
+
}],
|
|
532
|
+
stableReasons: [
|
|
533
|
+
"resource_input_too_large",
|
|
534
|
+
"resource_zip_entries_exceeded",
|
|
535
|
+
"resource_zip_entry_too_large",
|
|
536
|
+
"resource_zip_expansion_exceeded",
|
|
537
|
+
"resource_zip_ratio_exceeded",
|
|
538
|
+
"resource_xml_depth_exceeded",
|
|
539
|
+
"resource_xml_nodes_exceeded",
|
|
540
|
+
"resource_text_node_too_large",
|
|
541
|
+
"resource_image_pixels_exceeded",
|
|
542
|
+
"resource_timeout",
|
|
543
|
+
"resource_temp_disk_exceeded",
|
|
544
|
+
"resource_output_too_large"
|
|
545
|
+
],
|
|
546
|
+
configurationBoundary: "compiled profiles; runtime configuration may only tighten; licenses and requests cannot change limits",
|
|
547
|
+
hostEnforcementResponsibilities: [
|
|
548
|
+
"process-memory",
|
|
549
|
+
"cpu-quota",
|
|
550
|
+
"process-count"
|
|
551
|
+
],
|
|
552
|
+
precedence: [
|
|
553
|
+
"raw-input",
|
|
554
|
+
"zip-entry-count",
|
|
555
|
+
"zip-entry-size",
|
|
556
|
+
"zip-expansion",
|
|
557
|
+
"zip-ratio",
|
|
558
|
+
"xml-structure",
|
|
559
|
+
"image-pixels",
|
|
560
|
+
"task-budgets",
|
|
561
|
+
"output"
|
|
562
|
+
]
|
|
563
|
+
};
|
|
564
|
+
const BROWSER_RESOURCE_LIMITS = {
|
|
565
|
+
id: "browser-viewer",
|
|
566
|
+
inputBytes: 104857600,
|
|
567
|
+
zipEntries: 1e4,
|
|
568
|
+
zipExpansionBytes: 536870912,
|
|
569
|
+
zipEntryBytes: 134217728,
|
|
570
|
+
zipRatioThresholdBytes: 67108864,
|
|
571
|
+
zipRatioMax: 200,
|
|
572
|
+
xmlDepth: 256,
|
|
573
|
+
xmlNodes: 2e6,
|
|
574
|
+
textNodeBytes: 8388608,
|
|
575
|
+
imagePixels: 1e8,
|
|
576
|
+
outputBytes: 536870912,
|
|
577
|
+
wallClockMs: 6e4,
|
|
578
|
+
tempDiskBytes: null
|
|
579
|
+
};
|
|
580
|
+
//#endregion
|
|
581
|
+
//#region src/resource-policy.ts
|
|
582
|
+
var ResourcePolicyError = class extends Error {
|
|
583
|
+
constructor(details) {
|
|
584
|
+
super(`${details.reason}: ${details.subject} observed ${details.observed} ${details.unit}; policy limit is ${details.limit} ${details.unit}. This is an engineering safety limit, not a plan entitlement; reduce or split the input.`);
|
|
585
|
+
this.name = "ResourcePolicyError";
|
|
586
|
+
this.details = details;
|
|
587
|
+
}
|
|
588
|
+
};
|
|
589
|
+
function fail(reason, stage, subject, limit, observed, unit) {
|
|
590
|
+
throw new ResourcePolicyError({
|
|
591
|
+
reason,
|
|
592
|
+
stage,
|
|
593
|
+
subject,
|
|
594
|
+
limit,
|
|
595
|
+
observed,
|
|
596
|
+
unit,
|
|
597
|
+
policyVersion: RESOURCE_POLICY_CONTRACT.policyVersion,
|
|
598
|
+
commercialUpgradeApplicable: false
|
|
599
|
+
});
|
|
600
|
+
}
|
|
601
|
+
function checkBrowserInput(bytes, subject = "PPTX input") {
|
|
602
|
+
if (bytes > BROWSER_RESOURCE_LIMITS.inputBytes) fail("resource_input_too_large", "input", subject, BROWSER_RESOURCE_LIMITS.inputBytes, bytes, "bytes");
|
|
603
|
+
}
|
|
604
|
+
function checkBrowserElapsed(startedAt, subject = "parse wall clock") {
|
|
605
|
+
checkBrowserElapsedMs(Math.max(0, Math.ceil(performance.now() - startedAt)), subject);
|
|
606
|
+
}
|
|
607
|
+
function checkBrowserElapsedMs(elapsed, subject = "parse wall clock") {
|
|
608
|
+
if (elapsed > BROWSER_RESOURCE_LIMITS.wallClockMs) fail("resource_timeout", "runtime", subject, BROWSER_RESOURCE_LIMITS.wallClockMs, elapsed, "milliseconds");
|
|
609
|
+
}
|
|
610
|
+
function resourcePolicyErrorFromDetails(value) {
|
|
611
|
+
if (!value || typeof value !== "object") return null;
|
|
612
|
+
const details = value;
|
|
613
|
+
if (typeof details.reason !== "string" || !RESOURCE_POLICY_CONTRACT.stableReasons.includes(details.reason) || typeof details.stage !== "string" || typeof details.subject !== "string" || !Number.isSafeInteger(details.limit) || (details.limit ?? -1) < 0 || !Number.isSafeInteger(details.observed) || (details.observed ?? -1) < 0 || typeof details.unit !== "string" || details.policyVersion !== RESOURCE_POLICY_CONTRACT.policyVersion || details.commercialUpgradeApplicable !== false) return null;
|
|
614
|
+
return new ResourcePolicyError({
|
|
615
|
+
reason: details.reason,
|
|
616
|
+
stage: details.stage,
|
|
617
|
+
subject: details.subject,
|
|
618
|
+
limit: details.limit,
|
|
619
|
+
observed: details.observed,
|
|
620
|
+
unit: details.unit,
|
|
621
|
+
policyVersion: details.policyVersion,
|
|
622
|
+
commercialUpgradeApplicable: false
|
|
623
|
+
});
|
|
624
|
+
}
|
|
625
|
+
function checkBrowserZipEntries(entries) {
|
|
626
|
+
if (entries.length > BROWSER_RESOURCE_LIMITS.zipEntries) fail("resource_zip_entries_exceeded", "zip", "ZIP entry count", BROWSER_RESOURCE_LIMITS.zipEntries, entries.length, "entries");
|
|
627
|
+
let expandedTotal = 0;
|
|
628
|
+
for (const entry of entries) {
|
|
629
|
+
const expanded = Math.max(0, entry.originalSize);
|
|
630
|
+
const compressed = Math.max(0, entry.size);
|
|
631
|
+
if (expanded > BROWSER_RESOURCE_LIMITS.zipEntryBytes) fail("resource_zip_entry_too_large", "zip", entry.name, BROWSER_RESOURCE_LIMITS.zipEntryBytes, expanded, "bytes");
|
|
632
|
+
expandedTotal = Math.min(Number.MAX_SAFE_INTEGER, expandedTotal + expanded);
|
|
633
|
+
if (expandedTotal > BROWSER_RESOURCE_LIMITS.zipExpansionBytes) fail("resource_zip_expansion_exceeded", "zip", `cumulative ZIP expansion at ${entry.name}`, BROWSER_RESOURCE_LIMITS.zipExpansionBytes, expandedTotal, "bytes");
|
|
634
|
+
if (expanded > BROWSER_RESOURCE_LIMITS.zipRatioThresholdBytes && (compressed <= 0 || expanded / compressed > BROWSER_RESOURCE_LIMITS.zipRatioMax)) fail("resource_zip_ratio_exceeded", "zip", entry.name, BROWSER_RESOURCE_LIMITS.zipRatioMax, compressed <= 0 ? expanded : Math.ceil(expanded / compressed), "ratio");
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
/**
|
|
638
|
+
* Decompress a browser PPTX only after fflate exposes each central-directory
|
|
639
|
+
* record to the policy filter. XML and image checks run before any parser or
|
|
640
|
+
* renderer consumes the expanded payloads.
|
|
641
|
+
*/
|
|
642
|
+
function unzipBrowserPptx(data) {
|
|
643
|
+
const input = data instanceof Uint8Array ? data : new Uint8Array(data);
|
|
644
|
+
checkBrowserInput(input.byteLength);
|
|
645
|
+
const entries = [];
|
|
646
|
+
unzipSync(input, { filter(info) {
|
|
647
|
+
entries.push(info);
|
|
648
|
+
return false;
|
|
649
|
+
} });
|
|
650
|
+
checkBrowserZipEntries(entries);
|
|
651
|
+
const files = unzipSync(input);
|
|
652
|
+
checkBrowserXmlParts(Object.entries(files));
|
|
653
|
+
for (const [name, bytes] of Object.entries(files)) checkBrowserImagePayload(bytes, name);
|
|
654
|
+
return files;
|
|
655
|
+
}
|
|
656
|
+
var XmlBudget = class {
|
|
657
|
+
constructor() {
|
|
658
|
+
this.nodes = 0;
|
|
659
|
+
}
|
|
660
|
+
observeNode(depth, subject) {
|
|
661
|
+
if (depth > BROWSER_RESOURCE_LIMITS.xmlDepth) fail("resource_xml_depth_exceeded", "xml", subject, BROWSER_RESOURCE_LIMITS.xmlDepth, depth, "levels");
|
|
662
|
+
this.nodes += 1;
|
|
663
|
+
if (this.nodes > BROWSER_RESOURCE_LIMITS.xmlNodes) fail("resource_xml_nodes_exceeded", "xml", subject, BROWSER_RESOURCE_LIMITS.xmlNodes, this.nodes, "nodes");
|
|
664
|
+
}
|
|
665
|
+
observeText(depth, bytes, subject) {
|
|
666
|
+
this.observeNode(depth, subject);
|
|
667
|
+
if (bytes > BROWSER_RESOURCE_LIMITS.textNodeBytes) fail("resource_text_node_too_large", "xml", subject, BROWSER_RESOURCE_LIMITS.textNodeBytes, bytes, "bytes");
|
|
668
|
+
}
|
|
669
|
+
};
|
|
670
|
+
function isXmlPart(name) {
|
|
671
|
+
const normalized = name.toLowerCase();
|
|
672
|
+
return normalized.endsWith(".xml") || normalized.endsWith(".rels") || normalized.endsWith(".svg") || normalized.endsWith(".vml");
|
|
673
|
+
}
|
|
674
|
+
function looksLikeXmlPayload(input) {
|
|
675
|
+
let position = input.length >= 3 && input[0] === 239 && input[1] === 187 && input[2] === 191 ? 3 : 0;
|
|
676
|
+
while (position < input.length && isSpace(input[position])) position += 1;
|
|
677
|
+
if (position + 1 >= input.length || input[position] !== 60) return false;
|
|
678
|
+
const next = input[position + 1];
|
|
679
|
+
return next === 63 || next === 33 || next === 95 || next === 58 || next >= 65 && next <= 90 || next >= 97 && next <= 122 || next >= 128;
|
|
680
|
+
}
|
|
681
|
+
function checkBrowserXmlParts(parts) {
|
|
682
|
+
const budget = new XmlBudget();
|
|
683
|
+
for (const [name, bytes] of parts) if (isXmlPart(name) || looksLikeXmlPayload(bytes)) scanXml(bytes, budget, name);
|
|
684
|
+
}
|
|
685
|
+
const ascii = (value) => new TextEncoder().encode(value);
|
|
686
|
+
const COMMENT = ascii("<!--");
|
|
687
|
+
const COMMENT_END = ascii("-->");
|
|
688
|
+
const CDATA = ascii("<![CDATA[");
|
|
689
|
+
const CDATA_END = ascii("]]>");
|
|
690
|
+
const PROCESSING = ascii("<?");
|
|
691
|
+
const PROCESSING_END = ascii("?>");
|
|
692
|
+
const DOCTYPE_UPPER = ascii("<!DOCTYPE");
|
|
693
|
+
const DOCTYPE_LOWER = ascii("<!doctype");
|
|
694
|
+
function matchesAt(input, start, expected) {
|
|
695
|
+
if (start < 0 || start + expected.length > input.length) return false;
|
|
696
|
+
for (let index = 0; index < expected.length; index += 1) if (input[start + index] !== expected[index]) return false;
|
|
697
|
+
return true;
|
|
698
|
+
}
|
|
699
|
+
function findTerminator(input, start, expected) {
|
|
700
|
+
const last = input.length - expected.length;
|
|
701
|
+
for (let index = start; index <= last; index += 1) if (matchesAt(input, index, expected)) return index;
|
|
702
|
+
return input.length;
|
|
703
|
+
}
|
|
704
|
+
function findTagEnd(input, start) {
|
|
705
|
+
let quote = 0;
|
|
706
|
+
for (let index = start; index < input.length; index += 1) {
|
|
707
|
+
const value = input[index];
|
|
708
|
+
if (quote !== 0) {
|
|
709
|
+
if (value === quote) quote = 0;
|
|
710
|
+
} else if (value === 34 || value === 39) quote = value;
|
|
711
|
+
else if (value === 62) return index + 1;
|
|
712
|
+
}
|
|
713
|
+
return input.length;
|
|
714
|
+
}
|
|
715
|
+
function skipDoctype(input, start) {
|
|
716
|
+
let quote = 0;
|
|
717
|
+
let subsetDepth = 0;
|
|
718
|
+
for (let index = start; index < input.length; index += 1) {
|
|
719
|
+
const value = input[index];
|
|
720
|
+
if (quote !== 0) {
|
|
721
|
+
if (value === quote) quote = 0;
|
|
722
|
+
} else if (value === 34 || value === 39) quote = value;
|
|
723
|
+
else if (value === 91) subsetDepth += 1;
|
|
724
|
+
else if (value === 93 && subsetDepth > 0) subsetDepth -= 1;
|
|
725
|
+
else if (value === 62 && subsetDepth === 0) return index + 1;
|
|
726
|
+
}
|
|
727
|
+
return input.length;
|
|
728
|
+
}
|
|
729
|
+
function isSpace(value) {
|
|
730
|
+
return value === 32 || value === 9 || value === 10 || value === 13;
|
|
731
|
+
}
|
|
732
|
+
function scanXml(input, budget, subject) {
|
|
733
|
+
let position = 0;
|
|
734
|
+
let depth = 0;
|
|
735
|
+
while (position < input.length) {
|
|
736
|
+
if (input[position] !== 60) {
|
|
737
|
+
const start = position;
|
|
738
|
+
while (position < input.length && input[position] !== 60) position += 1;
|
|
739
|
+
if (position > start) budget.observeText(depth, position - start, `${subject} text`);
|
|
740
|
+
continue;
|
|
741
|
+
}
|
|
742
|
+
if (matchesAt(input, position, COMMENT)) {
|
|
743
|
+
budget.observeNode(depth, `${subject} comment`);
|
|
744
|
+
const end = findTerminator(input, position + COMMENT.length, COMMENT_END);
|
|
745
|
+
position = end === input.length ? end : end + COMMENT_END.length;
|
|
746
|
+
continue;
|
|
747
|
+
}
|
|
748
|
+
if (matchesAt(input, position, CDATA)) {
|
|
749
|
+
const start = position + CDATA.length;
|
|
750
|
+
const end = findTerminator(input, start, CDATA_END);
|
|
751
|
+
budget.observeText(depth, end - start, `${subject} CDATA`);
|
|
752
|
+
position = end === input.length ? end : end + CDATA_END.length;
|
|
753
|
+
continue;
|
|
754
|
+
}
|
|
755
|
+
if (matchesAt(input, position, PROCESSING)) {
|
|
756
|
+
budget.observeNode(depth, `${subject} processing instruction`);
|
|
757
|
+
const end = findTerminator(input, position + PROCESSING.length, PROCESSING_END);
|
|
758
|
+
position = end === input.length ? end : end + PROCESSING_END.length;
|
|
759
|
+
continue;
|
|
760
|
+
}
|
|
761
|
+
if (matchesAt(input, position, DOCTYPE_UPPER) || matchesAt(input, position, DOCTYPE_LOWER)) {
|
|
762
|
+
position = skipDoctype(input, position + 2);
|
|
763
|
+
continue;
|
|
764
|
+
}
|
|
765
|
+
if (position + 1 < input.length && input[position + 1] === 47) {
|
|
766
|
+
position = findTagEnd(input, position + 2);
|
|
767
|
+
if (depth > 0) depth -= 1;
|
|
768
|
+
continue;
|
|
769
|
+
}
|
|
770
|
+
if (position + 1 < input.length && input[position + 1] === 33) {
|
|
771
|
+
position = findTagEnd(input, position + 2);
|
|
772
|
+
continue;
|
|
773
|
+
}
|
|
774
|
+
const end = findTagEnd(input, position + 1);
|
|
775
|
+
let tail = end - 2;
|
|
776
|
+
while (tail > position && isSpace(input[tail])) tail -= 1;
|
|
777
|
+
const selfClosing = tail > position && input[tail] === 47;
|
|
778
|
+
depth += 1;
|
|
779
|
+
budget.observeNode(depth, subject);
|
|
780
|
+
if (selfClosing) depth -= 1;
|
|
781
|
+
position = end;
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
function readU16BE(data, offset) {
|
|
785
|
+
return data[offset] * 256 + data[offset + 1];
|
|
786
|
+
}
|
|
787
|
+
function readU16LE(data, offset) {
|
|
788
|
+
return data[offset] + data[offset + 1] * 256;
|
|
789
|
+
}
|
|
790
|
+
function readU24LE(data, offset) {
|
|
791
|
+
return data[offset] + data[offset + 1] * 256 + data[offset + 2] * 65536;
|
|
792
|
+
}
|
|
793
|
+
function readU32BE(data, offset) {
|
|
794
|
+
return data[offset] * 16777216 + data[offset + 1] * 65536 + data[offset + 2] * 256 + data[offset + 3];
|
|
795
|
+
}
|
|
796
|
+
function readU32LE(data, offset) {
|
|
797
|
+
return data[offset] + data[offset + 1] * 256 + data[offset + 2] * 65536 + data[offset + 3] * 16777216;
|
|
798
|
+
}
|
|
799
|
+
function bytesEqual(data, offset, expected) {
|
|
800
|
+
if (offset < 0 || offset + expected.length > data.length) return false;
|
|
801
|
+
return expected.every((value, index) => data[offset + index] === value);
|
|
802
|
+
}
|
|
803
|
+
function probeImageDimensions(data) {
|
|
804
|
+
if (data.length >= 24 && bytesEqual(data, 0, [
|
|
805
|
+
137,
|
|
806
|
+
80,
|
|
807
|
+
78,
|
|
808
|
+
71,
|
|
809
|
+
13,
|
|
810
|
+
10,
|
|
811
|
+
26,
|
|
812
|
+
10
|
|
813
|
+
]) && bytesEqual(data, 12, [
|
|
814
|
+
73,
|
|
815
|
+
72,
|
|
816
|
+
68,
|
|
817
|
+
82
|
|
818
|
+
])) return {
|
|
819
|
+
width: readU32BE(data, 16),
|
|
820
|
+
height: readU32BE(data, 20)
|
|
821
|
+
};
|
|
822
|
+
if (data.length >= 10 && (bytesEqual(data, 0, [
|
|
823
|
+
71,
|
|
824
|
+
73,
|
|
825
|
+
70,
|
|
826
|
+
56,
|
|
827
|
+
55,
|
|
828
|
+
97
|
|
829
|
+
]) || bytesEqual(data, 0, [
|
|
830
|
+
71,
|
|
831
|
+
73,
|
|
832
|
+
70,
|
|
833
|
+
56,
|
|
834
|
+
57,
|
|
835
|
+
97
|
|
836
|
+
]))) return {
|
|
837
|
+
width: readU16LE(data, 6),
|
|
838
|
+
height: readU16LE(data, 8)
|
|
839
|
+
};
|
|
840
|
+
if (data.length >= 26 && bytesEqual(data, 0, [66, 77])) {
|
|
841
|
+
const dib = readU32LE(data, 14);
|
|
842
|
+
if (dib === 12) return {
|
|
843
|
+
width: readU16LE(data, 18),
|
|
844
|
+
height: readU16LE(data, 20)
|
|
845
|
+
};
|
|
846
|
+
if (dib >= 40) {
|
|
847
|
+
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
848
|
+
return {
|
|
849
|
+
width: Math.abs(view.getInt32(18, true)),
|
|
850
|
+
height: Math.abs(view.getInt32(22, true))
|
|
851
|
+
};
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
if (data.length >= 30 && bytesEqual(data, 0, [
|
|
855
|
+
82,
|
|
856
|
+
73,
|
|
857
|
+
70,
|
|
858
|
+
70
|
|
859
|
+
]) && bytesEqual(data, 8, [
|
|
860
|
+
87,
|
|
861
|
+
69,
|
|
862
|
+
66,
|
|
863
|
+
80
|
|
864
|
+
])) {
|
|
865
|
+
if (bytesEqual(data, 12, [
|
|
866
|
+
86,
|
|
867
|
+
80,
|
|
868
|
+
56,
|
|
869
|
+
88
|
|
870
|
+
])) return {
|
|
871
|
+
width: 1 + readU24LE(data, 24),
|
|
872
|
+
height: 1 + readU24LE(data, 27)
|
|
873
|
+
};
|
|
874
|
+
if (bytesEqual(data, 12, [
|
|
875
|
+
86,
|
|
876
|
+
80,
|
|
877
|
+
56,
|
|
878
|
+
76
|
|
879
|
+
]) && data[20] === 47) return {
|
|
880
|
+
width: 1 + (data[21] | (data[22] & 63) << 8),
|
|
881
|
+
height: 1 + (data[22] >> 6 | data[23] << 2 | (data[24] & 15) << 10)
|
|
882
|
+
};
|
|
883
|
+
if (bytesEqual(data, 12, [
|
|
884
|
+
86,
|
|
885
|
+
80,
|
|
886
|
+
56,
|
|
887
|
+
32
|
|
888
|
+
]) && bytesEqual(data, 23, [
|
|
889
|
+
157,
|
|
890
|
+
1,
|
|
891
|
+
42
|
|
892
|
+
])) return {
|
|
893
|
+
width: (data[26] | data[27] << 8) & 16383,
|
|
894
|
+
height: (data[28] | data[29] << 8) & 16383
|
|
895
|
+
};
|
|
896
|
+
}
|
|
897
|
+
if (data.length >= 4 && data[0] === 255 && data[1] === 216) {
|
|
898
|
+
let position = 2;
|
|
899
|
+
while (position + 3 < data.length) {
|
|
900
|
+
while (position < data.length && data[position] !== 255) position += 1;
|
|
901
|
+
while (position < data.length && data[position] === 255) position += 1;
|
|
902
|
+
if (position >= data.length) break;
|
|
903
|
+
const marker = data[position];
|
|
904
|
+
position += 1;
|
|
905
|
+
if (marker === 1 || marker === 216 || marker === 217 || marker >= 208 && marker <= 215) continue;
|
|
906
|
+
if (position + 1 >= data.length) break;
|
|
907
|
+
const length = readU16BE(data, position);
|
|
908
|
+
if (length < 2 || position + length > data.length) break;
|
|
909
|
+
if ((marker >= 192 && marker <= 195 || marker >= 197 && marker <= 199 || marker >= 201 && marker <= 203 || marker >= 205 && marker <= 207) && length >= 7) return {
|
|
910
|
+
width: readU16BE(data, position + 5),
|
|
911
|
+
height: readU16BE(data, position + 3)
|
|
912
|
+
};
|
|
913
|
+
position += length;
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
return probeTiff(data);
|
|
917
|
+
}
|
|
918
|
+
function checkBrowserImagePayload(data, subject = "image") {
|
|
919
|
+
const dimensions = probeImageDimensions(data);
|
|
920
|
+
if (dimensions) checkImageDimensions(dimensions.width, dimensions.height, subject);
|
|
921
|
+
}
|
|
922
|
+
function probeTiff(data) {
|
|
923
|
+
if (data.length < 8) return null;
|
|
924
|
+
const little = bytesEqual(data, 0, [
|
|
925
|
+
73,
|
|
926
|
+
73,
|
|
927
|
+
42,
|
|
928
|
+
0
|
|
929
|
+
]);
|
|
930
|
+
if (!little && !bytesEqual(data, 0, [
|
|
931
|
+
77,
|
|
932
|
+
77,
|
|
933
|
+
0,
|
|
934
|
+
42
|
|
935
|
+
])) return null;
|
|
936
|
+
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
937
|
+
const offset = view.getUint32(4, little);
|
|
938
|
+
if (offset + 2 > data.length) return null;
|
|
939
|
+
const count = view.getUint16(offset, little);
|
|
940
|
+
let width = null;
|
|
941
|
+
let height = null;
|
|
942
|
+
for (let index = 0; index < count; index += 1) {
|
|
943
|
+
const entry = offset + 2 + index * 12;
|
|
944
|
+
if (entry + 12 > data.length) return null;
|
|
945
|
+
const tag = view.getUint16(entry, little);
|
|
946
|
+
if ((tag === 256 || tag === 257) && view.getUint32(entry + 4, little) === 1) {
|
|
947
|
+
const type = view.getUint16(entry + 2, little);
|
|
948
|
+
const value = type === 3 ? view.getUint16(entry + 8, little) : type === 4 ? view.getUint32(entry + 8, little) : 0;
|
|
949
|
+
if (tag === 256) width = value;
|
|
950
|
+
else height = value;
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
return width && height ? {
|
|
954
|
+
width,
|
|
955
|
+
height
|
|
956
|
+
} : null;
|
|
957
|
+
}
|
|
958
|
+
function checkImageDimensions(width, height, subject) {
|
|
959
|
+
if (width <= 0 || height <= 0) return;
|
|
960
|
+
const observed = width * height;
|
|
961
|
+
if (!Number.isSafeInteger(observed) || observed > BROWSER_RESOURCE_LIMITS.imagePixels) fail("resource_image_pixels_exceeded", "image", subject, BROWSER_RESOURCE_LIMITS.imagePixels, Number.isSafeInteger(observed) ? observed : Number.MAX_SAFE_INTEGER, "pixels");
|
|
962
|
+
}
|
|
963
|
+
//#endregion
|
|
964
|
+
//#region src/worker-error.ts
|
|
965
|
+
function property(value, key) {
|
|
966
|
+
try {
|
|
967
|
+
return value && typeof value === "object" ? value[key] : void 0;
|
|
968
|
+
} catch {
|
|
969
|
+
return;
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
function cloneFact(value) {
|
|
973
|
+
try {
|
|
974
|
+
return structuredClone(value);
|
|
975
|
+
} catch {
|
|
976
|
+
return;
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
function serializeWorkerError(error, seen = /* @__PURE__ */ new Map()) {
|
|
980
|
+
const previous = seen.get(error);
|
|
981
|
+
if (previous) return previous;
|
|
982
|
+
const name = property(error, "name");
|
|
983
|
+
const message = property(error, "message");
|
|
984
|
+
let fallback = "Worker operation failed";
|
|
985
|
+
if (typeof message !== "string") try {
|
|
986
|
+
fallback = String(error);
|
|
987
|
+
} catch {}
|
|
988
|
+
const result = {
|
|
989
|
+
name: typeof name === "string" ? name : "Error",
|
|
990
|
+
message: typeof message === "string" ? message : fallback
|
|
991
|
+
};
|
|
992
|
+
seen.set(error, result);
|
|
993
|
+
const code = property(error, "code");
|
|
994
|
+
if (typeof code === "string" || typeof code === "number") result.code = code;
|
|
995
|
+
const details = cloneFact(property(error, "details"));
|
|
996
|
+
if (details !== void 0) result.details = details;
|
|
997
|
+
const cause = property(error, "cause");
|
|
998
|
+
if (cause !== void 0) {
|
|
999
|
+
if (cause instanceof Error || typeof property(cause, "message") === "string") result.cause = {
|
|
1000
|
+
kind: "error",
|
|
1001
|
+
error: serializeWorkerError(cause, seen)
|
|
1002
|
+
};
|
|
1003
|
+
else {
|
|
1004
|
+
const value = cloneFact(cause);
|
|
1005
|
+
if (value !== void 0) result.cause = {
|
|
1006
|
+
kind: "value",
|
|
1007
|
+
value
|
|
1008
|
+
};
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1011
|
+
return result;
|
|
1012
|
+
}
|
|
1013
|
+
//#endregion
|
|
1014
|
+
//#region src/wasm-worker.ts
|
|
1015
|
+
let instance = null;
|
|
1016
|
+
let instanceSource = null;
|
|
1017
|
+
async function ensureWasm(wasmUrl) {
|
|
1018
|
+
const sourceUrl = wasmUrl ?? new URL("./mbt/main.wasm", import.meta.url);
|
|
1019
|
+
const source = String(sourceUrl);
|
|
1020
|
+
if (instance && instanceSource === source) return instance;
|
|
1021
|
+
const wasmBytes = await fetchWasmBytes(sourceUrl, "wasm-worker");
|
|
1022
|
+
const { instance: inst } = await WebAssembly.instantiate(new Uint8Array(wasmBytes), { _: createWasmStringConstants() }, {
|
|
1023
|
+
builtins: ["js-string"],
|
|
1024
|
+
importedStringConstants: "_"
|
|
1025
|
+
});
|
|
1026
|
+
instance = inst.exports;
|
|
1027
|
+
instanceSource = source;
|
|
1028
|
+
instance._start();
|
|
1029
|
+
return instance;
|
|
1030
|
+
}
|
|
1031
|
+
async function parseWithWasmUnzip(mod, buffer) {
|
|
1032
|
+
const t0 = performance.now();
|
|
1033
|
+
checkBrowserInput(buffer.byteLength);
|
|
1034
|
+
const latin1 = arrayBufferToLatin1(buffer);
|
|
1035
|
+
const t1 = performance.now();
|
|
1036
|
+
const fileCount = mod.decompress(latin1);
|
|
1037
|
+
const t2 = performance.now();
|
|
1038
|
+
mod.parse_only();
|
|
1039
|
+
const t3 = performance.now();
|
|
1040
|
+
const jsonStr = mod.serialize_result();
|
|
1041
|
+
const t4 = performance.now();
|
|
1042
|
+
const result = JSON.parse(jsonStr);
|
|
1043
|
+
const t5 = performance.now();
|
|
1044
|
+
const violation = resourcePolicyErrorFromDetails(result.resourceViolation);
|
|
1045
|
+
if (violation) throw violation;
|
|
1046
|
+
checkBrowserElapsed(t0, "[wasm-parser] Worker parse");
|
|
1047
|
+
return {
|
|
1048
|
+
result,
|
|
1049
|
+
timing: {
|
|
1050
|
+
latin1: t1 - t0,
|
|
1051
|
+
decompress: t2 - t1,
|
|
1052
|
+
fileCount,
|
|
1053
|
+
parse: t3 - t2,
|
|
1054
|
+
serialize: t4 - t3,
|
|
1055
|
+
jsonParse: t5 - t4,
|
|
1056
|
+
total: t5 - t0
|
|
1057
|
+
}
|
|
1058
|
+
};
|
|
1059
|
+
}
|
|
1060
|
+
async function parseWithJsUnzip(mod, buffer) {
|
|
1061
|
+
const t0 = performance.now();
|
|
1062
|
+
checkBrowserInput(buffer.byteLength);
|
|
1063
|
+
const files = unzipBrowserPptx(buffer);
|
|
1064
|
+
const t1 = performance.now();
|
|
1065
|
+
let fileCount = 0;
|
|
1066
|
+
for (const [filename, data] of Object.entries(files)) {
|
|
1067
|
+
mod.add_predecompressed_file(filename, uint8ArrayToLatin1(data));
|
|
1068
|
+
fileCount++;
|
|
1069
|
+
}
|
|
1070
|
+
const t2 = performance.now();
|
|
1071
|
+
const jsonStr = mod.parse_from_predecompressed();
|
|
1072
|
+
const t3 = performance.now();
|
|
1073
|
+
const result = JSON.parse(jsonStr);
|
|
1074
|
+
const t4 = performance.now();
|
|
1075
|
+
const violation = resourcePolicyErrorFromDetails(result.resourceViolation);
|
|
1076
|
+
if (violation) throw violation;
|
|
1077
|
+
checkBrowserElapsed(t0, "[wasm-parser] Worker parse");
|
|
1078
|
+
return {
|
|
1079
|
+
result,
|
|
1080
|
+
timing: {
|
|
1081
|
+
latin1: 0,
|
|
1082
|
+
decompress: t1 - t0,
|
|
1083
|
+
fileCount,
|
|
1084
|
+
parse: t3 - t2,
|
|
1085
|
+
serialize: t2 - t1,
|
|
1086
|
+
jsonParse: t4 - t3,
|
|
1087
|
+
total: t4 - t0
|
|
1088
|
+
}
|
|
1089
|
+
};
|
|
1090
|
+
}
|
|
1091
|
+
const active = /* @__PURE__ */ new Set();
|
|
1092
|
+
self.onmessage = async (e) => {
|
|
1093
|
+
if ("kind" in e.data && e.data.kind === "cancel") {
|
|
1094
|
+
active.delete(e.data.id);
|
|
1095
|
+
return;
|
|
1096
|
+
}
|
|
1097
|
+
const id = e.data instanceof ArrayBuffer ? 0 : e.data.id ?? 0;
|
|
1098
|
+
active.add(id);
|
|
1099
|
+
try {
|
|
1100
|
+
const { buffer, unzipMode, wasmUrl } = e.data instanceof ArrayBuffer ? {
|
|
1101
|
+
buffer: e.data,
|
|
1102
|
+
unzipMode: "wasm"
|
|
1103
|
+
} : e.data;
|
|
1104
|
+
const mod = await ensureWasm(wasmUrl);
|
|
1105
|
+
if (!active.has(id)) return;
|
|
1106
|
+
const { result, timing } = unzipMode === "js" ? await parseWithJsUnzip(mod, buffer) : await parseWithWasmUnzip(mod, buffer);
|
|
1107
|
+
if (!active.has(id)) return;
|
|
1108
|
+
if (result.error) {
|
|
1109
|
+
self.postMessage({
|
|
1110
|
+
id,
|
|
1111
|
+
status: "error",
|
|
1112
|
+
error: serializeWorkerError(/* @__PURE__ */ new Error(`WASM parser error: ${result.error}`))
|
|
1113
|
+
});
|
|
1114
|
+
return;
|
|
1115
|
+
}
|
|
1116
|
+
self.postMessage({
|
|
1117
|
+
id,
|
|
1118
|
+
status: "ok",
|
|
1119
|
+
data: result,
|
|
1120
|
+
timing
|
|
1121
|
+
});
|
|
1122
|
+
} catch (err) {
|
|
1123
|
+
if (!active.has(id)) return;
|
|
1124
|
+
self.postMessage({
|
|
1125
|
+
id,
|
|
1126
|
+
status: "error",
|
|
1127
|
+
error: serializeWorkerError(err)
|
|
1128
|
+
});
|
|
1129
|
+
} finally {
|
|
1130
|
+
if (!active.delete(id)) self.postMessage({
|
|
1131
|
+
id,
|
|
1132
|
+
status: "error",
|
|
1133
|
+
error: serializeWorkerError(new DOMException("The operation was aborted.", "AbortError"))
|
|
1134
|
+
});
|
|
1135
|
+
}
|
|
1136
|
+
};
|
|
1137
|
+
//#endregion
|