@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,1692 @@
|
|
|
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
|
+
function latin1ToUint8Array(latin1) {
|
|
19
|
+
const len = latin1.length;
|
|
20
|
+
const bytes = new Uint8Array(len);
|
|
21
|
+
for (let i = 0; i < len; i++) bytes[i] = latin1.charCodeAt(i) & 255;
|
|
22
|
+
return bytes;
|
|
23
|
+
}
|
|
24
|
+
//#endregion
|
|
25
|
+
//#region src/wasm-loader.ts
|
|
26
|
+
function fileUrl(url) {
|
|
27
|
+
try {
|
|
28
|
+
const parsed = url instanceof URL ? url : new URL(url);
|
|
29
|
+
return parsed.protocol === "file:" ? parsed : void 0;
|
|
30
|
+
} catch {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
function isUnsupportedNodeFileFetch(error) {
|
|
35
|
+
const cause = error?.cause;
|
|
36
|
+
const detail = cause instanceof Error ? cause.message : "";
|
|
37
|
+
return error instanceof TypeError && error.message === "fetch failed" && detail.includes("not implemented");
|
|
38
|
+
}
|
|
39
|
+
async function readNodeFile(url) {
|
|
40
|
+
const fs = globalThis.process?.getBuiltinModule?.("node:fs")?.promises;
|
|
41
|
+
if (!fs) throw new Error(`Node.js file loading is unavailable for ${url.href}`);
|
|
42
|
+
const bytes = await fs.readFile(url);
|
|
43
|
+
return bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
|
|
44
|
+
}
|
|
45
|
+
function assertWasmBytes(bytes, url, label, contentType) {
|
|
46
|
+
const magic = new Uint8Array(bytes, 0, Math.min(bytes.byteLength, 4));
|
|
47
|
+
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.`);
|
|
48
|
+
}
|
|
49
|
+
async function fetchWasmBytes(url, label) {
|
|
50
|
+
let response;
|
|
51
|
+
try {
|
|
52
|
+
response = await fetch(url);
|
|
53
|
+
} catch (error) {
|
|
54
|
+
const localFile = fileUrl(url);
|
|
55
|
+
if (!localFile || !isUnsupportedNodeFileFetch(error)) throw error;
|
|
56
|
+
const bytes = await readNodeFile(localFile);
|
|
57
|
+
assertWasmBytes(bytes, url, label, "application/wasm");
|
|
58
|
+
return bytes;
|
|
59
|
+
}
|
|
60
|
+
if (!response.ok) throw new Error(`[${label}] Failed to load WASM ${String(url)}: ${response.status} ${response.statusText}`);
|
|
61
|
+
const bytes = await response.arrayBuffer();
|
|
62
|
+
assertWasmBytes(bytes, url, label, response.headers.get("content-type") || "unknown");
|
|
63
|
+
return bytes;
|
|
64
|
+
}
|
|
65
|
+
//#endregion
|
|
66
|
+
//#region src/runtime-module-url.ts
|
|
67
|
+
const classicScriptUrl = (() => {
|
|
68
|
+
if (typeof document === "undefined" || typeof HTMLScriptElement === "undefined") return void 0;
|
|
69
|
+
return document.currentScript instanceof HTMLScriptElement && document.currentScript.src ? document.currentScript.src : void 0;
|
|
70
|
+
})();
|
|
71
|
+
/** @internal Convert a Node CJS filename to a URL without losing a UNC authority. */
|
|
72
|
+
function fileNameUrl(fileName) {
|
|
73
|
+
const normalized = fileName.replaceAll("\\", "/");
|
|
74
|
+
if (normalized.startsWith("//")) {
|
|
75
|
+
const shareSeparator = normalized.indexOf("/", 2);
|
|
76
|
+
if (shareSeparator > 2) {
|
|
77
|
+
const url = new URL("file://localhost/");
|
|
78
|
+
url.hostname = normalized.slice(2, shareSeparator);
|
|
79
|
+
url.pathname = normalized.slice(shareSeparator);
|
|
80
|
+
return url.href;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
const url = new URL("file:///");
|
|
84
|
+
url.pathname = normalized.startsWith("/") ? normalized : `/${normalized}`;
|
|
85
|
+
return url.href;
|
|
86
|
+
}
|
|
87
|
+
/** Resolve the current bundle URL in ESM, Node CJS, or a classic UMD script. */
|
|
88
|
+
function runtimeModuleUrl(importMetaUrl) {
|
|
89
|
+
if (typeof importMetaUrl === "string" && importMetaUrl) return importMetaUrl;
|
|
90
|
+
if (typeof __filename === "string" && __filename) return fileNameUrl(__filename);
|
|
91
|
+
return classicScriptUrl;
|
|
92
|
+
}
|
|
93
|
+
/** Resolve a package-relative runtime asset in ESM, Node CJS, or a classic UMD script. */
|
|
94
|
+
function runtimeAssetUrl(asset, importMetaUrl) {
|
|
95
|
+
const moduleUrl = runtimeModuleUrl(importMetaUrl);
|
|
96
|
+
if (!moduleUrl) throw new Error(`Unable to resolve runtime asset URL: ${asset}`);
|
|
97
|
+
return new URL(asset, moduleUrl);
|
|
98
|
+
}
|
|
99
|
+
//#endregion
|
|
100
|
+
//#region ../../node_modules/.pnpm/fflate@0.8.3/node_modules/fflate/esm/browser.js
|
|
101
|
+
var u8 = Uint8Array;
|
|
102
|
+
var u16 = Uint16Array;
|
|
103
|
+
var i32 = Int32Array;
|
|
104
|
+
var fleb = new u8([
|
|
105
|
+
0,
|
|
106
|
+
0,
|
|
107
|
+
0,
|
|
108
|
+
0,
|
|
109
|
+
0,
|
|
110
|
+
0,
|
|
111
|
+
0,
|
|
112
|
+
0,
|
|
113
|
+
1,
|
|
114
|
+
1,
|
|
115
|
+
1,
|
|
116
|
+
1,
|
|
117
|
+
2,
|
|
118
|
+
2,
|
|
119
|
+
2,
|
|
120
|
+
2,
|
|
121
|
+
3,
|
|
122
|
+
3,
|
|
123
|
+
3,
|
|
124
|
+
3,
|
|
125
|
+
4,
|
|
126
|
+
4,
|
|
127
|
+
4,
|
|
128
|
+
4,
|
|
129
|
+
5,
|
|
130
|
+
5,
|
|
131
|
+
5,
|
|
132
|
+
5,
|
|
133
|
+
0,
|
|
134
|
+
0,
|
|
135
|
+
0,
|
|
136
|
+
0
|
|
137
|
+
]);
|
|
138
|
+
var fdeb = new u8([
|
|
139
|
+
0,
|
|
140
|
+
0,
|
|
141
|
+
0,
|
|
142
|
+
0,
|
|
143
|
+
1,
|
|
144
|
+
1,
|
|
145
|
+
2,
|
|
146
|
+
2,
|
|
147
|
+
3,
|
|
148
|
+
3,
|
|
149
|
+
4,
|
|
150
|
+
4,
|
|
151
|
+
5,
|
|
152
|
+
5,
|
|
153
|
+
6,
|
|
154
|
+
6,
|
|
155
|
+
7,
|
|
156
|
+
7,
|
|
157
|
+
8,
|
|
158
|
+
8,
|
|
159
|
+
9,
|
|
160
|
+
9,
|
|
161
|
+
10,
|
|
162
|
+
10,
|
|
163
|
+
11,
|
|
164
|
+
11,
|
|
165
|
+
12,
|
|
166
|
+
12,
|
|
167
|
+
13,
|
|
168
|
+
13,
|
|
169
|
+
0,
|
|
170
|
+
0
|
|
171
|
+
]);
|
|
172
|
+
var clim = new u8([
|
|
173
|
+
16,
|
|
174
|
+
17,
|
|
175
|
+
18,
|
|
176
|
+
0,
|
|
177
|
+
8,
|
|
178
|
+
7,
|
|
179
|
+
9,
|
|
180
|
+
6,
|
|
181
|
+
10,
|
|
182
|
+
5,
|
|
183
|
+
11,
|
|
184
|
+
4,
|
|
185
|
+
12,
|
|
186
|
+
3,
|
|
187
|
+
13,
|
|
188
|
+
2,
|
|
189
|
+
14,
|
|
190
|
+
1,
|
|
191
|
+
15
|
|
192
|
+
]);
|
|
193
|
+
var freb = function(eb, start) {
|
|
194
|
+
var b = new u16(31);
|
|
195
|
+
for (var i = 0; i < 31; ++i) b[i] = start += 1 << eb[i - 1];
|
|
196
|
+
var r = new i32(b[30]);
|
|
197
|
+
for (var i = 1; i < 30; ++i) for (var j = b[i]; j < b[i + 1]; ++j) r[j] = j - b[i] << 5 | i;
|
|
198
|
+
return {
|
|
199
|
+
b,
|
|
200
|
+
r
|
|
201
|
+
};
|
|
202
|
+
};
|
|
203
|
+
var _a = freb(fleb, 2);
|
|
204
|
+
var fl = _a.b;
|
|
205
|
+
var revfl = _a.r;
|
|
206
|
+
fl[28] = 258, revfl[258] = 28;
|
|
207
|
+
var _b = freb(fdeb, 0);
|
|
208
|
+
var fd = _b.b;
|
|
209
|
+
_b.r;
|
|
210
|
+
var rev = new u16(32768);
|
|
211
|
+
for (var i = 0; i < 32768; ++i) {
|
|
212
|
+
var x = (i & 43690) >> 1 | (i & 21845) << 1;
|
|
213
|
+
x = (x & 52428) >> 2 | (x & 13107) << 2;
|
|
214
|
+
x = (x & 61680) >> 4 | (x & 3855) << 4;
|
|
215
|
+
rev[i] = ((x & 65280) >> 8 | (x & 255) << 8) >> 1;
|
|
216
|
+
}
|
|
217
|
+
var hMap = (function(cd, mb, r) {
|
|
218
|
+
var s = cd.length;
|
|
219
|
+
var i = 0;
|
|
220
|
+
var l = new u16(mb);
|
|
221
|
+
for (; i < s; ++i) if (cd[i]) ++l[cd[i] - 1];
|
|
222
|
+
var le = new u16(mb);
|
|
223
|
+
for (i = 1; i < mb; ++i) le[i] = le[i - 1] + l[i - 1] << 1;
|
|
224
|
+
var co;
|
|
225
|
+
if (r) {
|
|
226
|
+
co = new u16(1 << mb);
|
|
227
|
+
var rvb = 15 - mb;
|
|
228
|
+
for (i = 0; i < s; ++i) if (cd[i]) {
|
|
229
|
+
var sv = i << 4 | cd[i];
|
|
230
|
+
var r_1 = mb - cd[i];
|
|
231
|
+
var v = le[cd[i] - 1]++ << r_1;
|
|
232
|
+
for (var m = v | (1 << r_1) - 1; v <= m; ++v) co[rev[v] >> rvb] = sv;
|
|
233
|
+
}
|
|
234
|
+
} else {
|
|
235
|
+
co = new u16(s);
|
|
236
|
+
for (i = 0; i < s; ++i) if (cd[i]) co[i] = rev[le[cd[i] - 1]++] >> 15 - cd[i];
|
|
237
|
+
}
|
|
238
|
+
return co;
|
|
239
|
+
});
|
|
240
|
+
var flt = new u8(288);
|
|
241
|
+
for (var i = 0; i < 144; ++i) flt[i] = 8;
|
|
242
|
+
for (var i = 144; i < 256; ++i) flt[i] = 9;
|
|
243
|
+
for (var i = 256; i < 280; ++i) flt[i] = 7;
|
|
244
|
+
for (var i = 280; i < 288; ++i) flt[i] = 8;
|
|
245
|
+
var fdt = new u8(32);
|
|
246
|
+
for (var i = 0; i < 32; ++i) fdt[i] = 5;
|
|
247
|
+
var flrm = /*#__PURE__*/ hMap(flt, 9, 1);
|
|
248
|
+
var fdrm = /*#__PURE__*/ hMap(fdt, 5, 1);
|
|
249
|
+
var max = function(a) {
|
|
250
|
+
var m = a[0];
|
|
251
|
+
for (var i = 1; i < a.length; ++i) if (a[i] > m) m = a[i];
|
|
252
|
+
return m;
|
|
253
|
+
};
|
|
254
|
+
var bits = function(d, p, m) {
|
|
255
|
+
var o = p / 8 | 0;
|
|
256
|
+
return (d[o] | d[o + 1] << 8) >> (p & 7) & m;
|
|
257
|
+
};
|
|
258
|
+
var bits16 = function(d, p) {
|
|
259
|
+
var o = p / 8 | 0;
|
|
260
|
+
return (d[o] | d[o + 1] << 8 | d[o + 2] << 16) >> (p & 7);
|
|
261
|
+
};
|
|
262
|
+
var shft = function(p) {
|
|
263
|
+
return (p + 7) / 8 | 0;
|
|
264
|
+
};
|
|
265
|
+
var slc = function(v, s, e) {
|
|
266
|
+
if (s == null || s < 0) s = 0;
|
|
267
|
+
if (e == null || e > v.length) e = v.length;
|
|
268
|
+
return new u8(v.subarray(s, e));
|
|
269
|
+
};
|
|
270
|
+
var ec = [
|
|
271
|
+
"unexpected EOF",
|
|
272
|
+
"invalid block type",
|
|
273
|
+
"invalid length/literal",
|
|
274
|
+
"invalid distance",
|
|
275
|
+
"stream finished",
|
|
276
|
+
"no stream handler",
|
|
277
|
+
,
|
|
278
|
+
"no callback",
|
|
279
|
+
"invalid UTF-8 data",
|
|
280
|
+
"extra field too long",
|
|
281
|
+
"date not in range 1980-2099",
|
|
282
|
+
"filename too long",
|
|
283
|
+
"stream finishing",
|
|
284
|
+
"invalid zip data"
|
|
285
|
+
];
|
|
286
|
+
var err = function(ind, msg, nt) {
|
|
287
|
+
var e = new Error(msg || ec[ind]);
|
|
288
|
+
e.code = ind;
|
|
289
|
+
if (Error.captureStackTrace) Error.captureStackTrace(e, err);
|
|
290
|
+
if (!nt) throw e;
|
|
291
|
+
return e;
|
|
292
|
+
};
|
|
293
|
+
var inflt = function(dat, st, buf, dict) {
|
|
294
|
+
var sl = dat.length, dl = dict ? dict.length : 0;
|
|
295
|
+
if (!sl || st.f && !st.l) return buf || new u8(0);
|
|
296
|
+
var noBuf = !buf;
|
|
297
|
+
var resize = noBuf || st.i != 2;
|
|
298
|
+
var noSt = st.i;
|
|
299
|
+
if (noBuf) buf = new u8(sl * 3);
|
|
300
|
+
var cbuf = function(l) {
|
|
301
|
+
var bl = buf.length;
|
|
302
|
+
if (l > bl) {
|
|
303
|
+
var nbuf = new u8(Math.max(bl * 2, l));
|
|
304
|
+
nbuf.set(buf);
|
|
305
|
+
buf = nbuf;
|
|
306
|
+
}
|
|
307
|
+
};
|
|
308
|
+
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;
|
|
309
|
+
var tbts = sl * 8;
|
|
310
|
+
do {
|
|
311
|
+
if (!lm) {
|
|
312
|
+
final = bits(dat, pos, 1);
|
|
313
|
+
var type = bits(dat, pos + 1, 3);
|
|
314
|
+
pos += 3;
|
|
315
|
+
if (!type) {
|
|
316
|
+
var s = shft(pos) + 4, l = dat[s - 4] | dat[s - 3] << 8, t = s + l;
|
|
317
|
+
if (t > sl) {
|
|
318
|
+
if (noSt) err(0);
|
|
319
|
+
break;
|
|
320
|
+
}
|
|
321
|
+
if (resize) cbuf(bt + l);
|
|
322
|
+
buf.set(dat.subarray(s, t), bt);
|
|
323
|
+
st.b = bt += l, st.p = pos = t * 8, st.f = final;
|
|
324
|
+
continue;
|
|
325
|
+
} else if (type == 1) lm = flrm, dm = fdrm, lbt = 9, dbt = 5;
|
|
326
|
+
else if (type == 2) {
|
|
327
|
+
var hLit = bits(dat, pos, 31) + 257, hcLen = bits(dat, pos + 10, 15) + 4;
|
|
328
|
+
var tl = hLit + bits(dat, pos + 5, 31) + 1;
|
|
329
|
+
pos += 14;
|
|
330
|
+
var ldt = new u8(tl);
|
|
331
|
+
var clt = new u8(19);
|
|
332
|
+
for (var i = 0; i < hcLen; ++i) clt[clim[i]] = bits(dat, pos + i * 3, 7);
|
|
333
|
+
pos += hcLen * 3;
|
|
334
|
+
var clb = max(clt), clbmsk = (1 << clb) - 1;
|
|
335
|
+
var clm = hMap(clt, clb, 1);
|
|
336
|
+
for (var i = 0; i < tl;) {
|
|
337
|
+
var r = clm[bits(dat, pos, clbmsk)];
|
|
338
|
+
pos += r & 15;
|
|
339
|
+
var s = r >> 4;
|
|
340
|
+
if (s < 16) ldt[i++] = s;
|
|
341
|
+
else {
|
|
342
|
+
var c = 0, n = 0;
|
|
343
|
+
if (s == 16) n = 3 + bits(dat, pos, 3), pos += 2, c = ldt[i - 1];
|
|
344
|
+
else if (s == 17) n = 3 + bits(dat, pos, 7), pos += 3;
|
|
345
|
+
else if (s == 18) n = 11 + bits(dat, pos, 127), pos += 7;
|
|
346
|
+
while (n--) ldt[i++] = c;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
var lt = ldt.subarray(0, hLit), dt = ldt.subarray(hLit);
|
|
350
|
+
lbt = max(lt);
|
|
351
|
+
dbt = max(dt);
|
|
352
|
+
lm = hMap(lt, lbt, 1);
|
|
353
|
+
dm = hMap(dt, dbt, 1);
|
|
354
|
+
} else err(1);
|
|
355
|
+
if (pos > tbts) {
|
|
356
|
+
if (noSt) err(0);
|
|
357
|
+
break;
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
if (resize) cbuf(bt + 131072);
|
|
361
|
+
var lms = (1 << lbt) - 1, dms = (1 << dbt) - 1;
|
|
362
|
+
var lpos = pos;
|
|
363
|
+
for (;; lpos = pos) {
|
|
364
|
+
var c = lm[bits16(dat, pos) & lms], sym = c >> 4;
|
|
365
|
+
pos += c & 15;
|
|
366
|
+
if (pos > tbts) {
|
|
367
|
+
if (noSt) err(0);
|
|
368
|
+
break;
|
|
369
|
+
}
|
|
370
|
+
if (!c) err(2);
|
|
371
|
+
if (sym < 256) buf[bt++] = sym;
|
|
372
|
+
else if (sym == 256) {
|
|
373
|
+
lpos = pos, lm = null;
|
|
374
|
+
break;
|
|
375
|
+
} else {
|
|
376
|
+
var add = sym - 254;
|
|
377
|
+
if (sym > 264) {
|
|
378
|
+
var i = sym - 257, b = fleb[i];
|
|
379
|
+
add = bits(dat, pos, (1 << b) - 1) + fl[i];
|
|
380
|
+
pos += b;
|
|
381
|
+
}
|
|
382
|
+
var d = dm[bits16(dat, pos) & dms], dsym = d >> 4;
|
|
383
|
+
if (!d) err(3);
|
|
384
|
+
pos += d & 15;
|
|
385
|
+
var dt = fd[dsym];
|
|
386
|
+
if (dsym > 3) {
|
|
387
|
+
var b = fdeb[dsym];
|
|
388
|
+
dt += bits16(dat, pos) & (1 << b) - 1, pos += b;
|
|
389
|
+
}
|
|
390
|
+
if (pos > tbts) {
|
|
391
|
+
if (noSt) err(0);
|
|
392
|
+
break;
|
|
393
|
+
}
|
|
394
|
+
if (resize) cbuf(bt + 131072);
|
|
395
|
+
var end = bt + add;
|
|
396
|
+
if (bt < dt) {
|
|
397
|
+
var shift = dl - dt, dend = Math.min(dt, end);
|
|
398
|
+
if (shift + bt < 0) err(3);
|
|
399
|
+
for (; bt < dend; ++bt) buf[bt] = dict[shift + bt];
|
|
400
|
+
}
|
|
401
|
+
for (; bt < end; ++bt) buf[bt] = buf[bt - dt];
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
st.l = lm, st.p = lpos, st.b = bt, st.f = final;
|
|
405
|
+
if (lm) final = 1, st.m = lbt, st.d = dm, st.n = dbt;
|
|
406
|
+
} while (!final);
|
|
407
|
+
return bt != buf.length && noBuf ? slc(buf, 0, bt) : buf.subarray(0, bt);
|
|
408
|
+
};
|
|
409
|
+
var et = /*#__PURE__*/ new u8(0);
|
|
410
|
+
var b2 = function(d, b) {
|
|
411
|
+
return d[b] | d[b + 1] << 8;
|
|
412
|
+
};
|
|
413
|
+
var b4 = function(d, b) {
|
|
414
|
+
return (d[b] | d[b + 1] << 8 | d[b + 2] << 16 | d[b + 3] << 24) >>> 0;
|
|
415
|
+
};
|
|
416
|
+
var b8 = function(d, b) {
|
|
417
|
+
return b4(d, b) + b4(d, b + 4) * 4294967296;
|
|
418
|
+
};
|
|
419
|
+
function inflateSync(data, opts) {
|
|
420
|
+
return inflt(data, { i: 2 }, opts && opts.out, opts && opts.dictionary);
|
|
421
|
+
}
|
|
422
|
+
var td = typeof TextDecoder != "undefined" && /*#__PURE__*/ new TextDecoder();
|
|
423
|
+
try {
|
|
424
|
+
td.decode(et, { stream: true });
|
|
425
|
+
} catch (e) {}
|
|
426
|
+
var dutf8 = function(d) {
|
|
427
|
+
for (var r = "", i = 0;;) {
|
|
428
|
+
var c = d[i++];
|
|
429
|
+
var eb = (c > 127) + (c > 223) + (c > 239);
|
|
430
|
+
if (i + eb > d.length) return {
|
|
431
|
+
s: r,
|
|
432
|
+
r: slc(d, i - 1)
|
|
433
|
+
};
|
|
434
|
+
if (!eb) r += String.fromCharCode(c);
|
|
435
|
+
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);
|
|
436
|
+
else if (eb & 1) r += String.fromCharCode((c & 31) << 6 | d[i++] & 63);
|
|
437
|
+
else r += String.fromCharCode((c & 15) << 12 | (d[i++] & 63) << 6 | d[i++] & 63);
|
|
438
|
+
}
|
|
439
|
+
};
|
|
440
|
+
/**
|
|
441
|
+
* Converts a Uint8Array to a string
|
|
442
|
+
* @param dat The data to decode to string
|
|
443
|
+
* @param latin1 Whether or not to interpret the data as Latin-1. This should
|
|
444
|
+
* not need to be true unless encoding to binary string.
|
|
445
|
+
* @returns The original UTF-8/Latin-1 string
|
|
446
|
+
*/
|
|
447
|
+
function strFromU8(dat, latin1) {
|
|
448
|
+
if (latin1) {
|
|
449
|
+
var r = "";
|
|
450
|
+
for (var i = 0; i < dat.length; i += 16384) r += String.fromCharCode.apply(null, dat.subarray(i, i + 16384));
|
|
451
|
+
return r;
|
|
452
|
+
} else if (td) return td.decode(dat);
|
|
453
|
+
else {
|
|
454
|
+
var _a = dutf8(dat), s = _a.s, r = _a.r;
|
|
455
|
+
if (r.length) err(8);
|
|
456
|
+
return s;
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
var slzh = function(d, b) {
|
|
460
|
+
return b + 30 + b2(d, b + 26) + b2(d, b + 28);
|
|
461
|
+
};
|
|
462
|
+
var zh = function(d, b, z) {
|
|
463
|
+
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;
|
|
464
|
+
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];
|
|
465
|
+
return [
|
|
466
|
+
b2(d, b + 10),
|
|
467
|
+
sc,
|
|
468
|
+
su,
|
|
469
|
+
fn,
|
|
470
|
+
es + efl + b2(d, b + 32),
|
|
471
|
+
off
|
|
472
|
+
];
|
|
473
|
+
};
|
|
474
|
+
var z64hs = function(d, b, l, z, sc, su, off) {
|
|
475
|
+
var nsc = sc == 4294967295, nsu = su == 4294967295, noff = off == 4294967295, e = b + l;
|
|
476
|
+
var nf = nsc + nsu + noff;
|
|
477
|
+
if (z && nf) {
|
|
478
|
+
for (; b + 4 < e; b += 4 + b2(d, b + 2)) if (b2(d, b) == 1) return [
|
|
479
|
+
nsc ? b8(d, b + 4 + 8 * nsu) : sc,
|
|
480
|
+
nsu ? b8(d, b + 4) : su,
|
|
481
|
+
noff ? b8(d, b + 4 + 8 * (nsu + nsc)) : off,
|
|
482
|
+
1
|
|
483
|
+
];
|
|
484
|
+
if (z < 2) err(13);
|
|
485
|
+
}
|
|
486
|
+
return [
|
|
487
|
+
sc,
|
|
488
|
+
su,
|
|
489
|
+
off,
|
|
490
|
+
0
|
|
491
|
+
];
|
|
492
|
+
};
|
|
493
|
+
/**
|
|
494
|
+
* Synchronously decompresses a ZIP archive. Prefer using `unzip` for better
|
|
495
|
+
* performance with more than one file.
|
|
496
|
+
* @param data The raw compressed ZIP file
|
|
497
|
+
* @param opts The ZIP extraction options
|
|
498
|
+
* @returns The decompressed files
|
|
499
|
+
*/
|
|
500
|
+
function unzipSync(data, opts) {
|
|
501
|
+
var files = {};
|
|
502
|
+
var e = data.length - 22;
|
|
503
|
+
for (; b4(data, e) != 101010256; --e) if (!e || data.length - e > 65558) err(13);
|
|
504
|
+
var c = b2(data, e + 8);
|
|
505
|
+
if (!c) return {};
|
|
506
|
+
var o = b4(data, e + 16);
|
|
507
|
+
var z = b4(data, e - 20) == 117853008;
|
|
508
|
+
if (z) {
|
|
509
|
+
var ze = b4(data, e - 12);
|
|
510
|
+
z = b4(data, ze) == 101075792;
|
|
511
|
+
if (z) {
|
|
512
|
+
c = b4(data, ze + 32);
|
|
513
|
+
o = b4(data, ze + 48);
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
var fltr = opts && opts.filter;
|
|
517
|
+
for (var i = 0; i < c; ++i) {
|
|
518
|
+
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);
|
|
519
|
+
o = no;
|
|
520
|
+
if (!fltr || fltr({
|
|
521
|
+
name: fn,
|
|
522
|
+
size: sc,
|
|
523
|
+
originalSize: su,
|
|
524
|
+
compression: c_2
|
|
525
|
+
})) {
|
|
526
|
+
if (!c_2) files[fn] = slc(data, b, b + sc);
|
|
527
|
+
else if (c_2 == 8) files[fn] = inflateSync(data.subarray(b, b + sc), { out: new u8(su) });
|
|
528
|
+
else err(14, "unknown compression type " + c_2);
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
return files;
|
|
532
|
+
}
|
|
533
|
+
//#endregion
|
|
534
|
+
//#region src/resource-policy.generated.ts
|
|
535
|
+
const RESOURCE_POLICY_CONTRACT = {
|
|
536
|
+
schemaVersion: "xdoc-resource-policy-registry/v1",
|
|
537
|
+
policyVersion: "2026.08",
|
|
538
|
+
profiles: [{
|
|
539
|
+
id: "native-api",
|
|
540
|
+
inputBytes: 314572800,
|
|
541
|
+
zipEntries: 5e4,
|
|
542
|
+
zipExpansionBytes: 1073741824,
|
|
543
|
+
zipEntryBytes: 268435456,
|
|
544
|
+
zipRatioThresholdBytes: 67108864,
|
|
545
|
+
zipRatioMax: 200,
|
|
546
|
+
xmlDepth: 256,
|
|
547
|
+
xmlNodes: 1e7,
|
|
548
|
+
textNodeBytes: 16777216,
|
|
549
|
+
imagePixels: 2e8,
|
|
550
|
+
outputBytes: 1073741824,
|
|
551
|
+
wallClockMs: 9e5,
|
|
552
|
+
tempDiskBytes: 4294967296
|
|
553
|
+
}, {
|
|
554
|
+
id: "browser-viewer",
|
|
555
|
+
inputBytes: 104857600,
|
|
556
|
+
zipEntries: 1e4,
|
|
557
|
+
zipExpansionBytes: 536870912,
|
|
558
|
+
zipEntryBytes: 134217728,
|
|
559
|
+
zipRatioThresholdBytes: 67108864,
|
|
560
|
+
zipRatioMax: 200,
|
|
561
|
+
xmlDepth: 256,
|
|
562
|
+
xmlNodes: 2e6,
|
|
563
|
+
textNodeBytes: 8388608,
|
|
564
|
+
imagePixels: 1e8,
|
|
565
|
+
outputBytes: 536870912,
|
|
566
|
+
wallClockMs: 6e4,
|
|
567
|
+
tempDiskBytes: null
|
|
568
|
+
}],
|
|
569
|
+
stableReasons: [
|
|
570
|
+
"resource_input_too_large",
|
|
571
|
+
"resource_zip_entries_exceeded",
|
|
572
|
+
"resource_zip_entry_too_large",
|
|
573
|
+
"resource_zip_expansion_exceeded",
|
|
574
|
+
"resource_zip_ratio_exceeded",
|
|
575
|
+
"resource_xml_depth_exceeded",
|
|
576
|
+
"resource_xml_nodes_exceeded",
|
|
577
|
+
"resource_text_node_too_large",
|
|
578
|
+
"resource_image_pixels_exceeded",
|
|
579
|
+
"resource_timeout",
|
|
580
|
+
"resource_temp_disk_exceeded",
|
|
581
|
+
"resource_output_too_large"
|
|
582
|
+
],
|
|
583
|
+
configurationBoundary: "compiled profiles; runtime configuration may only tighten; licenses and requests cannot change limits",
|
|
584
|
+
hostEnforcementResponsibilities: [
|
|
585
|
+
"process-memory",
|
|
586
|
+
"cpu-quota",
|
|
587
|
+
"process-count"
|
|
588
|
+
],
|
|
589
|
+
precedence: [
|
|
590
|
+
"raw-input",
|
|
591
|
+
"zip-entry-count",
|
|
592
|
+
"zip-entry-size",
|
|
593
|
+
"zip-expansion",
|
|
594
|
+
"zip-ratio",
|
|
595
|
+
"xml-structure",
|
|
596
|
+
"image-pixels",
|
|
597
|
+
"task-budgets",
|
|
598
|
+
"output"
|
|
599
|
+
]
|
|
600
|
+
};
|
|
601
|
+
const BROWSER_RESOURCE_LIMITS = {
|
|
602
|
+
id: "browser-viewer",
|
|
603
|
+
inputBytes: 104857600,
|
|
604
|
+
zipEntries: 1e4,
|
|
605
|
+
zipExpansionBytes: 536870912,
|
|
606
|
+
zipEntryBytes: 134217728,
|
|
607
|
+
zipRatioThresholdBytes: 67108864,
|
|
608
|
+
zipRatioMax: 200,
|
|
609
|
+
xmlDepth: 256,
|
|
610
|
+
xmlNodes: 2e6,
|
|
611
|
+
textNodeBytes: 8388608,
|
|
612
|
+
imagePixels: 1e8,
|
|
613
|
+
outputBytes: 536870912,
|
|
614
|
+
wallClockMs: 6e4,
|
|
615
|
+
tempDiskBytes: null
|
|
616
|
+
};
|
|
617
|
+
//#endregion
|
|
618
|
+
//#region src/resource-policy.ts
|
|
619
|
+
var ResourcePolicyError = class extends Error {
|
|
620
|
+
constructor(details) {
|
|
621
|
+
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.`);
|
|
622
|
+
this.name = "ResourcePolicyError";
|
|
623
|
+
this.details = details;
|
|
624
|
+
}
|
|
625
|
+
};
|
|
626
|
+
function fail(reason, stage, subject, limit, observed, unit) {
|
|
627
|
+
throw new ResourcePolicyError({
|
|
628
|
+
reason,
|
|
629
|
+
stage,
|
|
630
|
+
subject,
|
|
631
|
+
limit,
|
|
632
|
+
observed,
|
|
633
|
+
unit,
|
|
634
|
+
policyVersion: RESOURCE_POLICY_CONTRACT.policyVersion,
|
|
635
|
+
commercialUpgradeApplicable: false
|
|
636
|
+
});
|
|
637
|
+
}
|
|
638
|
+
function checkBrowserInput(bytes, subject = "PPTX input") {
|
|
639
|
+
if (bytes > BROWSER_RESOURCE_LIMITS.inputBytes) fail("resource_input_too_large", "input", subject, BROWSER_RESOURCE_LIMITS.inputBytes, bytes, "bytes");
|
|
640
|
+
}
|
|
641
|
+
function checkBrowserOutput(bytes, subject = "output") {
|
|
642
|
+
if (bytes > BROWSER_RESOURCE_LIMITS.outputBytes) fail("resource_output_too_large", "output", subject, BROWSER_RESOURCE_LIMITS.outputBytes, bytes, "bytes");
|
|
643
|
+
}
|
|
644
|
+
function checkBrowserElapsed(startedAt, subject = "parse wall clock") {
|
|
645
|
+
checkBrowserElapsedMs(Math.max(0, Math.ceil(performance.now() - startedAt)), subject);
|
|
646
|
+
}
|
|
647
|
+
function checkBrowserElapsedMs(elapsed, subject = "parse wall clock") {
|
|
648
|
+
if (elapsed > BROWSER_RESOURCE_LIMITS.wallClockMs) fail("resource_timeout", "runtime", subject, BROWSER_RESOURCE_LIMITS.wallClockMs, elapsed, "milliseconds");
|
|
649
|
+
}
|
|
650
|
+
function isResourcePolicyError(error) {
|
|
651
|
+
return error instanceof ResourcePolicyError;
|
|
652
|
+
}
|
|
653
|
+
function resourcePolicyErrorFromDetails(value) {
|
|
654
|
+
if (!value || typeof value !== "object") return null;
|
|
655
|
+
const details = value;
|
|
656
|
+
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;
|
|
657
|
+
return new ResourcePolicyError({
|
|
658
|
+
reason: details.reason,
|
|
659
|
+
stage: details.stage,
|
|
660
|
+
subject: details.subject,
|
|
661
|
+
limit: details.limit,
|
|
662
|
+
observed: details.observed,
|
|
663
|
+
unit: details.unit,
|
|
664
|
+
policyVersion: details.policyVersion,
|
|
665
|
+
commercialUpgradeApplicable: false
|
|
666
|
+
});
|
|
667
|
+
}
|
|
668
|
+
function checkBrowserZipEntries(entries) {
|
|
669
|
+
if (entries.length > BROWSER_RESOURCE_LIMITS.zipEntries) fail("resource_zip_entries_exceeded", "zip", "ZIP entry count", BROWSER_RESOURCE_LIMITS.zipEntries, entries.length, "entries");
|
|
670
|
+
let expandedTotal = 0;
|
|
671
|
+
for (const entry of entries) {
|
|
672
|
+
const expanded = Math.max(0, entry.originalSize);
|
|
673
|
+
const compressed = Math.max(0, entry.size);
|
|
674
|
+
if (expanded > BROWSER_RESOURCE_LIMITS.zipEntryBytes) fail("resource_zip_entry_too_large", "zip", entry.name, BROWSER_RESOURCE_LIMITS.zipEntryBytes, expanded, "bytes");
|
|
675
|
+
expandedTotal = Math.min(Number.MAX_SAFE_INTEGER, expandedTotal + expanded);
|
|
676
|
+
if (expandedTotal > BROWSER_RESOURCE_LIMITS.zipExpansionBytes) fail("resource_zip_expansion_exceeded", "zip", `cumulative ZIP expansion at ${entry.name}`, BROWSER_RESOURCE_LIMITS.zipExpansionBytes, expandedTotal, "bytes");
|
|
677
|
+
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");
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
/**
|
|
681
|
+
* Decompress a browser PPTX only after fflate exposes each central-directory
|
|
682
|
+
* record to the policy filter. XML and image checks run before any parser or
|
|
683
|
+
* renderer consumes the expanded payloads.
|
|
684
|
+
*/
|
|
685
|
+
function unzipBrowserPptx(data) {
|
|
686
|
+
const input = data instanceof Uint8Array ? data : new Uint8Array(data);
|
|
687
|
+
checkBrowserInput(input.byteLength);
|
|
688
|
+
const entries = [];
|
|
689
|
+
unzipSync(input, { filter(info) {
|
|
690
|
+
entries.push(info);
|
|
691
|
+
return false;
|
|
692
|
+
} });
|
|
693
|
+
checkBrowserZipEntries(entries);
|
|
694
|
+
const files = unzipSync(input);
|
|
695
|
+
checkBrowserXmlParts(Object.entries(files));
|
|
696
|
+
for (const [name, bytes] of Object.entries(files)) checkBrowserImagePayload(bytes, name);
|
|
697
|
+
return files;
|
|
698
|
+
}
|
|
699
|
+
var XmlBudget = class {
|
|
700
|
+
constructor() {
|
|
701
|
+
this.nodes = 0;
|
|
702
|
+
}
|
|
703
|
+
observeNode(depth, subject) {
|
|
704
|
+
if (depth > BROWSER_RESOURCE_LIMITS.xmlDepth) fail("resource_xml_depth_exceeded", "xml", subject, BROWSER_RESOURCE_LIMITS.xmlDepth, depth, "levels");
|
|
705
|
+
this.nodes += 1;
|
|
706
|
+
if (this.nodes > BROWSER_RESOURCE_LIMITS.xmlNodes) fail("resource_xml_nodes_exceeded", "xml", subject, BROWSER_RESOURCE_LIMITS.xmlNodes, this.nodes, "nodes");
|
|
707
|
+
}
|
|
708
|
+
observeText(depth, bytes, subject) {
|
|
709
|
+
this.observeNode(depth, subject);
|
|
710
|
+
if (bytes > BROWSER_RESOURCE_LIMITS.textNodeBytes) fail("resource_text_node_too_large", "xml", subject, BROWSER_RESOURCE_LIMITS.textNodeBytes, bytes, "bytes");
|
|
711
|
+
}
|
|
712
|
+
};
|
|
713
|
+
function isXmlPart(name) {
|
|
714
|
+
const normalized = name.toLowerCase();
|
|
715
|
+
return normalized.endsWith(".xml") || normalized.endsWith(".rels") || normalized.endsWith(".svg") || normalized.endsWith(".vml");
|
|
716
|
+
}
|
|
717
|
+
function looksLikeXmlPayload(input) {
|
|
718
|
+
let position = input.length >= 3 && input[0] === 239 && input[1] === 187 && input[2] === 191 ? 3 : 0;
|
|
719
|
+
while (position < input.length && isSpace(input[position])) position += 1;
|
|
720
|
+
if (position + 1 >= input.length || input[position] !== 60) return false;
|
|
721
|
+
const next = input[position + 1];
|
|
722
|
+
return next === 63 || next === 33 || next === 95 || next === 58 || next >= 65 && next <= 90 || next >= 97 && next <= 122 || next >= 128;
|
|
723
|
+
}
|
|
724
|
+
function checkBrowserXmlParts(parts) {
|
|
725
|
+
const budget = new XmlBudget();
|
|
726
|
+
for (const [name, bytes] of parts) if (isXmlPart(name) || looksLikeXmlPayload(bytes)) scanXml(bytes, budget, name);
|
|
727
|
+
}
|
|
728
|
+
const ascii = (value) => new TextEncoder().encode(value);
|
|
729
|
+
const COMMENT = ascii("<!--");
|
|
730
|
+
const COMMENT_END = ascii("-->");
|
|
731
|
+
const CDATA = ascii("<![CDATA[");
|
|
732
|
+
const CDATA_END = ascii("]]>");
|
|
733
|
+
const PROCESSING = ascii("<?");
|
|
734
|
+
const PROCESSING_END = ascii("?>");
|
|
735
|
+
const DOCTYPE_UPPER = ascii("<!DOCTYPE");
|
|
736
|
+
const DOCTYPE_LOWER = ascii("<!doctype");
|
|
737
|
+
function matchesAt(input, start, expected) {
|
|
738
|
+
if (start < 0 || start + expected.length > input.length) return false;
|
|
739
|
+
for (let index = 0; index < expected.length; index += 1) if (input[start + index] !== expected[index]) return false;
|
|
740
|
+
return true;
|
|
741
|
+
}
|
|
742
|
+
function findTerminator(input, start, expected) {
|
|
743
|
+
const last = input.length - expected.length;
|
|
744
|
+
for (let index = start; index <= last; index += 1) if (matchesAt(input, index, expected)) return index;
|
|
745
|
+
return input.length;
|
|
746
|
+
}
|
|
747
|
+
function findTagEnd(input, start) {
|
|
748
|
+
let quote = 0;
|
|
749
|
+
for (let index = start; index < input.length; index += 1) {
|
|
750
|
+
const value = input[index];
|
|
751
|
+
if (quote !== 0) {
|
|
752
|
+
if (value === quote) quote = 0;
|
|
753
|
+
} else if (value === 34 || value === 39) quote = value;
|
|
754
|
+
else if (value === 62) return index + 1;
|
|
755
|
+
}
|
|
756
|
+
return input.length;
|
|
757
|
+
}
|
|
758
|
+
function skipDoctype(input, start) {
|
|
759
|
+
let quote = 0;
|
|
760
|
+
let subsetDepth = 0;
|
|
761
|
+
for (let index = start; index < input.length; index += 1) {
|
|
762
|
+
const value = input[index];
|
|
763
|
+
if (quote !== 0) {
|
|
764
|
+
if (value === quote) quote = 0;
|
|
765
|
+
} else if (value === 34 || value === 39) quote = value;
|
|
766
|
+
else if (value === 91) subsetDepth += 1;
|
|
767
|
+
else if (value === 93 && subsetDepth > 0) subsetDepth -= 1;
|
|
768
|
+
else if (value === 62 && subsetDepth === 0) return index + 1;
|
|
769
|
+
}
|
|
770
|
+
return input.length;
|
|
771
|
+
}
|
|
772
|
+
function isSpace(value) {
|
|
773
|
+
return value === 32 || value === 9 || value === 10 || value === 13;
|
|
774
|
+
}
|
|
775
|
+
function scanXml(input, budget, subject) {
|
|
776
|
+
let position = 0;
|
|
777
|
+
let depth = 0;
|
|
778
|
+
while (position < input.length) {
|
|
779
|
+
if (input[position] !== 60) {
|
|
780
|
+
const start = position;
|
|
781
|
+
while (position < input.length && input[position] !== 60) position += 1;
|
|
782
|
+
if (position > start) budget.observeText(depth, position - start, `${subject} text`);
|
|
783
|
+
continue;
|
|
784
|
+
}
|
|
785
|
+
if (matchesAt(input, position, COMMENT)) {
|
|
786
|
+
budget.observeNode(depth, `${subject} comment`);
|
|
787
|
+
const end = findTerminator(input, position + COMMENT.length, COMMENT_END);
|
|
788
|
+
position = end === input.length ? end : end + COMMENT_END.length;
|
|
789
|
+
continue;
|
|
790
|
+
}
|
|
791
|
+
if (matchesAt(input, position, CDATA)) {
|
|
792
|
+
const start = position + CDATA.length;
|
|
793
|
+
const end = findTerminator(input, start, CDATA_END);
|
|
794
|
+
budget.observeText(depth, end - start, `${subject} CDATA`);
|
|
795
|
+
position = end === input.length ? end : end + CDATA_END.length;
|
|
796
|
+
continue;
|
|
797
|
+
}
|
|
798
|
+
if (matchesAt(input, position, PROCESSING)) {
|
|
799
|
+
budget.observeNode(depth, `${subject} processing instruction`);
|
|
800
|
+
const end = findTerminator(input, position + PROCESSING.length, PROCESSING_END);
|
|
801
|
+
position = end === input.length ? end : end + PROCESSING_END.length;
|
|
802
|
+
continue;
|
|
803
|
+
}
|
|
804
|
+
if (matchesAt(input, position, DOCTYPE_UPPER) || matchesAt(input, position, DOCTYPE_LOWER)) {
|
|
805
|
+
position = skipDoctype(input, position + 2);
|
|
806
|
+
continue;
|
|
807
|
+
}
|
|
808
|
+
if (position + 1 < input.length && input[position + 1] === 47) {
|
|
809
|
+
position = findTagEnd(input, position + 2);
|
|
810
|
+
if (depth > 0) depth -= 1;
|
|
811
|
+
continue;
|
|
812
|
+
}
|
|
813
|
+
if (position + 1 < input.length && input[position + 1] === 33) {
|
|
814
|
+
position = findTagEnd(input, position + 2);
|
|
815
|
+
continue;
|
|
816
|
+
}
|
|
817
|
+
const end = findTagEnd(input, position + 1);
|
|
818
|
+
let tail = end - 2;
|
|
819
|
+
while (tail > position && isSpace(input[tail])) tail -= 1;
|
|
820
|
+
const selfClosing = tail > position && input[tail] === 47;
|
|
821
|
+
depth += 1;
|
|
822
|
+
budget.observeNode(depth, subject);
|
|
823
|
+
if (selfClosing) depth -= 1;
|
|
824
|
+
position = end;
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
function readU16BE(data, offset) {
|
|
828
|
+
return data[offset] * 256 + data[offset + 1];
|
|
829
|
+
}
|
|
830
|
+
function readU16LE(data, offset) {
|
|
831
|
+
return data[offset] + data[offset + 1] * 256;
|
|
832
|
+
}
|
|
833
|
+
function readU24LE(data, offset) {
|
|
834
|
+
return data[offset] + data[offset + 1] * 256 + data[offset + 2] * 65536;
|
|
835
|
+
}
|
|
836
|
+
function readU32BE(data, offset) {
|
|
837
|
+
return data[offset] * 16777216 + data[offset + 1] * 65536 + data[offset + 2] * 256 + data[offset + 3];
|
|
838
|
+
}
|
|
839
|
+
function readU32LE(data, offset) {
|
|
840
|
+
return data[offset] + data[offset + 1] * 256 + data[offset + 2] * 65536 + data[offset + 3] * 16777216;
|
|
841
|
+
}
|
|
842
|
+
function bytesEqual(data, offset, expected) {
|
|
843
|
+
if (offset < 0 || offset + expected.length > data.length) return false;
|
|
844
|
+
return expected.every((value, index) => data[offset + index] === value);
|
|
845
|
+
}
|
|
846
|
+
function probeImageDimensions(data) {
|
|
847
|
+
if (data.length >= 24 && bytesEqual(data, 0, [
|
|
848
|
+
137,
|
|
849
|
+
80,
|
|
850
|
+
78,
|
|
851
|
+
71,
|
|
852
|
+
13,
|
|
853
|
+
10,
|
|
854
|
+
26,
|
|
855
|
+
10
|
|
856
|
+
]) && bytesEqual(data, 12, [
|
|
857
|
+
73,
|
|
858
|
+
72,
|
|
859
|
+
68,
|
|
860
|
+
82
|
|
861
|
+
])) return {
|
|
862
|
+
width: readU32BE(data, 16),
|
|
863
|
+
height: readU32BE(data, 20)
|
|
864
|
+
};
|
|
865
|
+
if (data.length >= 10 && (bytesEqual(data, 0, [
|
|
866
|
+
71,
|
|
867
|
+
73,
|
|
868
|
+
70,
|
|
869
|
+
56,
|
|
870
|
+
55,
|
|
871
|
+
97
|
|
872
|
+
]) || bytesEqual(data, 0, [
|
|
873
|
+
71,
|
|
874
|
+
73,
|
|
875
|
+
70,
|
|
876
|
+
56,
|
|
877
|
+
57,
|
|
878
|
+
97
|
|
879
|
+
]))) return {
|
|
880
|
+
width: readU16LE(data, 6),
|
|
881
|
+
height: readU16LE(data, 8)
|
|
882
|
+
};
|
|
883
|
+
if (data.length >= 26 && bytesEqual(data, 0, [66, 77])) {
|
|
884
|
+
const dib = readU32LE(data, 14);
|
|
885
|
+
if (dib === 12) return {
|
|
886
|
+
width: readU16LE(data, 18),
|
|
887
|
+
height: readU16LE(data, 20)
|
|
888
|
+
};
|
|
889
|
+
if (dib >= 40) {
|
|
890
|
+
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
891
|
+
return {
|
|
892
|
+
width: Math.abs(view.getInt32(18, true)),
|
|
893
|
+
height: Math.abs(view.getInt32(22, true))
|
|
894
|
+
};
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
if (data.length >= 30 && bytesEqual(data, 0, [
|
|
898
|
+
82,
|
|
899
|
+
73,
|
|
900
|
+
70,
|
|
901
|
+
70
|
|
902
|
+
]) && bytesEqual(data, 8, [
|
|
903
|
+
87,
|
|
904
|
+
69,
|
|
905
|
+
66,
|
|
906
|
+
80
|
|
907
|
+
])) {
|
|
908
|
+
if (bytesEqual(data, 12, [
|
|
909
|
+
86,
|
|
910
|
+
80,
|
|
911
|
+
56,
|
|
912
|
+
88
|
|
913
|
+
])) return {
|
|
914
|
+
width: 1 + readU24LE(data, 24),
|
|
915
|
+
height: 1 + readU24LE(data, 27)
|
|
916
|
+
};
|
|
917
|
+
if (bytesEqual(data, 12, [
|
|
918
|
+
86,
|
|
919
|
+
80,
|
|
920
|
+
56,
|
|
921
|
+
76
|
|
922
|
+
]) && data[20] === 47) return {
|
|
923
|
+
width: 1 + (data[21] | (data[22] & 63) << 8),
|
|
924
|
+
height: 1 + (data[22] >> 6 | data[23] << 2 | (data[24] & 15) << 10)
|
|
925
|
+
};
|
|
926
|
+
if (bytesEqual(data, 12, [
|
|
927
|
+
86,
|
|
928
|
+
80,
|
|
929
|
+
56,
|
|
930
|
+
32
|
|
931
|
+
]) && bytesEqual(data, 23, [
|
|
932
|
+
157,
|
|
933
|
+
1,
|
|
934
|
+
42
|
|
935
|
+
])) return {
|
|
936
|
+
width: (data[26] | data[27] << 8) & 16383,
|
|
937
|
+
height: (data[28] | data[29] << 8) & 16383
|
|
938
|
+
};
|
|
939
|
+
}
|
|
940
|
+
if (data.length >= 4 && data[0] === 255 && data[1] === 216) {
|
|
941
|
+
let position = 2;
|
|
942
|
+
while (position + 3 < data.length) {
|
|
943
|
+
while (position < data.length && data[position] !== 255) position += 1;
|
|
944
|
+
while (position < data.length && data[position] === 255) position += 1;
|
|
945
|
+
if (position >= data.length) break;
|
|
946
|
+
const marker = data[position];
|
|
947
|
+
position += 1;
|
|
948
|
+
if (marker === 1 || marker === 216 || marker === 217 || marker >= 208 && marker <= 215) continue;
|
|
949
|
+
if (position + 1 >= data.length) break;
|
|
950
|
+
const length = readU16BE(data, position);
|
|
951
|
+
if (length < 2 || position + length > data.length) break;
|
|
952
|
+
if ((marker >= 192 && marker <= 195 || marker >= 197 && marker <= 199 || marker >= 201 && marker <= 203 || marker >= 205 && marker <= 207) && length >= 7) return {
|
|
953
|
+
width: readU16BE(data, position + 5),
|
|
954
|
+
height: readU16BE(data, position + 3)
|
|
955
|
+
};
|
|
956
|
+
position += length;
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
return probeTiff(data);
|
|
960
|
+
}
|
|
961
|
+
function checkBrowserImagePayload(data, subject = "image") {
|
|
962
|
+
const dimensions = probeImageDimensions(data);
|
|
963
|
+
if (dimensions) checkImageDimensions(dimensions.width, dimensions.height, subject);
|
|
964
|
+
}
|
|
965
|
+
function probeTiff(data) {
|
|
966
|
+
if (data.length < 8) return null;
|
|
967
|
+
const little = bytesEqual(data, 0, [
|
|
968
|
+
73,
|
|
969
|
+
73,
|
|
970
|
+
42,
|
|
971
|
+
0
|
|
972
|
+
]);
|
|
973
|
+
if (!little && !bytesEqual(data, 0, [
|
|
974
|
+
77,
|
|
975
|
+
77,
|
|
976
|
+
0,
|
|
977
|
+
42
|
|
978
|
+
])) return null;
|
|
979
|
+
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
980
|
+
const offset = view.getUint32(4, little);
|
|
981
|
+
if (offset + 2 > data.length) return null;
|
|
982
|
+
const count = view.getUint16(offset, little);
|
|
983
|
+
let width = null;
|
|
984
|
+
let height = null;
|
|
985
|
+
for (let index = 0; index < count; index += 1) {
|
|
986
|
+
const entry = offset + 2 + index * 12;
|
|
987
|
+
if (entry + 12 > data.length) return null;
|
|
988
|
+
const tag = view.getUint16(entry, little);
|
|
989
|
+
if ((tag === 256 || tag === 257) && view.getUint32(entry + 4, little) === 1) {
|
|
990
|
+
const type = view.getUint16(entry + 2, little);
|
|
991
|
+
const value = type === 3 ? view.getUint16(entry + 8, little) : type === 4 ? view.getUint32(entry + 8, little) : 0;
|
|
992
|
+
if (tag === 256) width = value;
|
|
993
|
+
else height = value;
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
return width && height ? {
|
|
997
|
+
width,
|
|
998
|
+
height
|
|
999
|
+
} : null;
|
|
1000
|
+
}
|
|
1001
|
+
function checkImageDimensions(width, height, subject) {
|
|
1002
|
+
if (width <= 0 || height <= 0) return;
|
|
1003
|
+
const observed = width * height;
|
|
1004
|
+
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");
|
|
1005
|
+
}
|
|
1006
|
+
//#endregion
|
|
1007
|
+
//#region src/worker-error.ts
|
|
1008
|
+
function property(value, key) {
|
|
1009
|
+
try {
|
|
1010
|
+
return value && typeof value === "object" ? value[key] : void 0;
|
|
1011
|
+
} catch {
|
|
1012
|
+
return;
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
function cloneFact(value) {
|
|
1016
|
+
try {
|
|
1017
|
+
return structuredClone(value);
|
|
1018
|
+
} catch {
|
|
1019
|
+
return;
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
function serializeWorkerError(error, seen = /* @__PURE__ */ new Map()) {
|
|
1023
|
+
const previous = seen.get(error);
|
|
1024
|
+
if (previous) return previous;
|
|
1025
|
+
const name = property(error, "name");
|
|
1026
|
+
const message = property(error, "message");
|
|
1027
|
+
let fallback = "Worker operation failed";
|
|
1028
|
+
if (typeof message !== "string") try {
|
|
1029
|
+
fallback = String(error);
|
|
1030
|
+
} catch {}
|
|
1031
|
+
const result = {
|
|
1032
|
+
name: typeof name === "string" ? name : "Error",
|
|
1033
|
+
message: typeof message === "string" ? message : fallback
|
|
1034
|
+
};
|
|
1035
|
+
seen.set(error, result);
|
|
1036
|
+
const code = property(error, "code");
|
|
1037
|
+
if (typeof code === "string" || typeof code === "number") result.code = code;
|
|
1038
|
+
const details = cloneFact(property(error, "details"));
|
|
1039
|
+
if (details !== void 0) result.details = details;
|
|
1040
|
+
const cause = property(error, "cause");
|
|
1041
|
+
if (cause !== void 0) {
|
|
1042
|
+
if (cause instanceof Error || typeof property(cause, "message") === "string") result.cause = {
|
|
1043
|
+
kind: "error",
|
|
1044
|
+
error: serializeWorkerError(cause, seen)
|
|
1045
|
+
};
|
|
1046
|
+
else {
|
|
1047
|
+
const value = cloneFact(cause);
|
|
1048
|
+
if (value !== void 0) result.cause = {
|
|
1049
|
+
kind: "value",
|
|
1050
|
+
value
|
|
1051
|
+
};
|
|
1052
|
+
}
|
|
1053
|
+
}
|
|
1054
|
+
return result;
|
|
1055
|
+
}
|
|
1056
|
+
//#endregion
|
|
1057
|
+
//#region src/pdf-converter.ts
|
|
1058
|
+
const officeFontFallbacksBridgeTypeface = "__xdoc_office_font_fallbacks__";
|
|
1059
|
+
const PDF_EMOJI_FALLBACK_TYPEFACE = "__pdf_emoji_fallback__";
|
|
1060
|
+
const PDF_BRIDGE_WORD_BYTES = 4;
|
|
1061
|
+
let pdfWasmSource = null;
|
|
1062
|
+
let pdfWasmInstance = null;
|
|
1063
|
+
let pdfWasmLoadSource = null;
|
|
1064
|
+
let pdfWasmLoadPromise = null;
|
|
1065
|
+
const conversionTailsByInstance = /* @__PURE__ */ new WeakMap();
|
|
1066
|
+
let unicodeFallbackFontCacheKey = null;
|
|
1067
|
+
let unicodeFallbackFontPromise = null;
|
|
1068
|
+
let emojiFallbackFontCacheKey = null;
|
|
1069
|
+
let emojiFallbackFontPromise = null;
|
|
1070
|
+
function createPdfWasmImports() {
|
|
1071
|
+
return {
|
|
1072
|
+
__moonbit_time_unstable: { now: () => BigInt(Date.now()) },
|
|
1073
|
+
console: { log: (...args) => {
|
|
1074
|
+
console.log(...args);
|
|
1075
|
+
} }
|
|
1076
|
+
};
|
|
1077
|
+
}
|
|
1078
|
+
function bytesToArrayBuffer(bytes) {
|
|
1079
|
+
return bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
|
|
1080
|
+
}
|
|
1081
|
+
function hasBufferedPdfBridge(mod) {
|
|
1082
|
+
return typeof mod.reset_pdf_bridge_input === "function" && typeof mod.push_pdf_bridge_input_word === "function" && typeof mod.register_pdf_font_buffered === "function" && typeof mod.register_pdf_emoji_bitmap_buffered === "function" && typeof mod.register_pdf_math_fallback_buffered === "function" && typeof mod.set_pdf_office_font_fallbacks === "function" && typeof mod.run_pdf_conversion_buffered === "function" && typeof mod.pdf_bridge_output_len === "function" && typeof mod.pdf_bridge_output_word === "function" && typeof mod.clear_pdf_bridge_output === "function";
|
|
1083
|
+
}
|
|
1084
|
+
function assertPdfWasmApi(mod) {
|
|
1085
|
+
if (!hasBufferedPdfBridge(mod) && typeof mod.convert_pptx_to_pdf_from_string !== "function") throw new Error("PDF converter WASM does not expose a supported host bridge");
|
|
1086
|
+
}
|
|
1087
|
+
function utf8Bytes(value) {
|
|
1088
|
+
return new TextEncoder().encode(value);
|
|
1089
|
+
}
|
|
1090
|
+
function writePdfBridgeInput(mod, chunks) {
|
|
1091
|
+
mod.reset_pdf_bridge_input();
|
|
1092
|
+
let written = 0;
|
|
1093
|
+
for (const chunk of chunks) for (let offset = 0; offset < chunk.byteLength; offset += PDF_BRIDGE_WORD_BYTES) {
|
|
1094
|
+
const byteCount = Math.min(PDF_BRIDGE_WORD_BYTES, chunk.byteLength - offset);
|
|
1095
|
+
let word = 0;
|
|
1096
|
+
for (let index = 0; index < byteCount; index += 1) word |= (chunk[offset + index] ?? 0) << index * 8;
|
|
1097
|
+
written += byteCount;
|
|
1098
|
+
if (mod.push_pdf_bridge_input_word(word, byteCount) !== written) throw new Error("PDF converter WASM rejected buffered input");
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
1101
|
+
function readPdfBridgeOutput(mod, reportedLength) {
|
|
1102
|
+
try {
|
|
1103
|
+
const length = mod.pdf_bridge_output_len();
|
|
1104
|
+
if (!Number.isSafeInteger(length) || length !== reportedLength || length < 0) throw new Error("PDF converter WASM returned an invalid buffered output length");
|
|
1105
|
+
checkBrowserOutput(length, "PDF output");
|
|
1106
|
+
const output = new Uint8Array(length);
|
|
1107
|
+
for (let offset = 0; offset < length; offset += PDF_BRIDGE_WORD_BYTES) {
|
|
1108
|
+
const word = mod.pdf_bridge_output_word(offset / PDF_BRIDGE_WORD_BYTES);
|
|
1109
|
+
if (!Number.isInteger(word)) throw new Error("PDF converter WASM returned an invalid buffered output word");
|
|
1110
|
+
for (let index = 0; index < PDF_BRIDGE_WORD_BYTES && offset + index < length; index += 1) output[offset + index] = word >>> index * 8 & 255;
|
|
1111
|
+
}
|
|
1112
|
+
return output;
|
|
1113
|
+
} finally {
|
|
1114
|
+
mod.clear_pdf_bridge_output();
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
function decodeXmlEntities(str) {
|
|
1118
|
+
if (!str.includes("&")) return str;
|
|
1119
|
+
return str.replace(/</g, "<").replace(/>/g, ">").replace(/"/g, "\"").replace(/'/g, "'").replace(/&#x([0-9a-fA-F]+);/g, (_, hex) => String.fromCodePoint(parseInt(hex, 16))).replace(/&#(\d+);/g, (_, dec) => String.fromCodePoint(parseInt(dec, 10))).replace(/&/g, "&");
|
|
1120
|
+
}
|
|
1121
|
+
function containsNonLatinCodepoint(text) {
|
|
1122
|
+
for (const char of text) if ((char.codePointAt(0) ?? 0) > 255) return true;
|
|
1123
|
+
return false;
|
|
1124
|
+
}
|
|
1125
|
+
function isDefaultEmojiPresentationCodepoint(codepoint) {
|
|
1126
|
+
return codepoint >= 126976 && codepoint <= 129791 || codepoint === 169 || codepoint === 174 || codepoint === 8252 || codepoint === 8265 || codepoint === 8482 || codepoint === 8505 || codepoint === 8986 || codepoint === 8987 || codepoint === 9167 || codepoint >= 9193 && codepoint <= 9199 || codepoint === 9200 || codepoint === 9203 || codepoint >= 9208 && codepoint <= 9210 || codepoint === 9410 || codepoint >= 9725 && codepoint <= 9726 || codepoint >= 9748 && codepoint <= 9749 || codepoint >= 9800 && codepoint <= 9811 || codepoint === 9855 || codepoint === 9875 || codepoint === 9889 || codepoint >= 9898 && codepoint <= 9899 || codepoint >= 9917 && codepoint <= 9918 || codepoint >= 9924 && codepoint <= 9925 || codepoint === 9934 || codepoint === 9940 || codepoint === 9962 || codepoint >= 9970 && codepoint <= 9971 || codepoint === 9973 || codepoint === 9978 || codepoint === 9981 || codepoint === 9989 || codepoint >= 9994 && codepoint <= 9995 || codepoint === 10024 || codepoint === 10060 || codepoint === 10062 || codepoint >= 10067 && codepoint <= 10069 || codepoint === 10071 || codepoint >= 10133 && codepoint <= 10135 || codepoint === 10160 || codepoint === 10175 || codepoint >= 10548 && codepoint <= 10549 || codepoint >= 11013 && codepoint <= 11015 || codepoint >= 11035 && codepoint <= 11036 || codepoint === 11088 || codepoint === 11093 || codepoint === 12336 || codepoint === 12349 || codepoint === 12951 || codepoint === 12953;
|
|
1127
|
+
}
|
|
1128
|
+
function codepointHasExplicitEmojiPresentation(text, index) {
|
|
1129
|
+
const codepoint = text.codePointAt(index) ?? 0;
|
|
1130
|
+
return (text.codePointAt(index + (codepoint > 65535 ? 2 : 1)) ?? 0) === 65039;
|
|
1131
|
+
}
|
|
1132
|
+
function collectEmojiBitmapFallbackCodepoints(buffer) {
|
|
1133
|
+
const codepoints = /* @__PURE__ */ new Set();
|
|
1134
|
+
try {
|
|
1135
|
+
const files = unzipBrowserPptx(buffer);
|
|
1136
|
+
const decoder = new TextDecoder();
|
|
1137
|
+
const textRegex = /<a:t(?:\s[^>]*)?>([\s\S]*?)<\/a:t>/gu;
|
|
1138
|
+
for (const [filename, bytes] of Object.entries(files)) {
|
|
1139
|
+
if (!filename.endsWith(".xml")) continue;
|
|
1140
|
+
if (!filename.startsWith("ppt/slides/") && !filename.startsWith("ppt/slideLayouts/") && !filename.startsWith("ppt/notesSlides/")) continue;
|
|
1141
|
+
const xml = decoder.decode(bytes);
|
|
1142
|
+
for (const match of xml.matchAll(textRegex)) {
|
|
1143
|
+
const text = decodeXmlEntities(match[1] ?? "");
|
|
1144
|
+
for (let i = 0; i < text.length; i += 1) {
|
|
1145
|
+
const codepoint = text.codePointAt(i) ?? 0;
|
|
1146
|
+
if (isDefaultEmojiPresentationCodepoint(codepoint) || codepointHasExplicitEmojiPresentation(text, i)) codepoints.add(codepoint);
|
|
1147
|
+
if (codepoint > 65535) i += 1;
|
|
1148
|
+
}
|
|
1149
|
+
}
|
|
1150
|
+
}
|
|
1151
|
+
} catch (error) {
|
|
1152
|
+
if (isResourcePolicyError(error)) throw error;
|
|
1153
|
+
return [];
|
|
1154
|
+
}
|
|
1155
|
+
return [...codepoints].sort((a, b) => a - b);
|
|
1156
|
+
}
|
|
1157
|
+
function pptxHasUnicodeText(buffer) {
|
|
1158
|
+
try {
|
|
1159
|
+
const files = unzipBrowserPptx(buffer);
|
|
1160
|
+
const decoder = new TextDecoder();
|
|
1161
|
+
const textRegex = /<a:t(?:\s[^>]*)?>([\s\S]*?)<\/a:t>/gu;
|
|
1162
|
+
for (const [filename, bytes] of Object.entries(files)) {
|
|
1163
|
+
if (!filename.endsWith(".xml")) continue;
|
|
1164
|
+
if (!filename.startsWith("ppt/slides/") && !filename.startsWith("ppt/slideLayouts/") && !filename.startsWith("ppt/notesSlides/")) continue;
|
|
1165
|
+
const xml = decoder.decode(bytes);
|
|
1166
|
+
for (const match of xml.matchAll(textRegex)) if (containsNonLatinCodepoint(decodeXmlEntities(match[1] ?? ""))) return true;
|
|
1167
|
+
}
|
|
1168
|
+
} catch (error) {
|
|
1169
|
+
if (isResourcePolicyError(error)) throw error;
|
|
1170
|
+
return false;
|
|
1171
|
+
}
|
|
1172
|
+
return false;
|
|
1173
|
+
}
|
|
1174
|
+
async function resolveUnicodeFallbackFont(options) {
|
|
1175
|
+
const font = options?.unicodeFallbackFont;
|
|
1176
|
+
if (!font) return null;
|
|
1177
|
+
if (font.data) {
|
|
1178
|
+
const data = font.data instanceof Uint8Array ? font.data : new Uint8Array(font.data);
|
|
1179
|
+
if (data.byteLength === 0) return null;
|
|
1180
|
+
return {
|
|
1181
|
+
path: font.path ?? "unicode-fallback.ttf",
|
|
1182
|
+
data
|
|
1183
|
+
};
|
|
1184
|
+
}
|
|
1185
|
+
if (!font.url) return null;
|
|
1186
|
+
const cacheKey = `${font.path ?? "unicode-fallback.ttf"}|${String(font.url)}`;
|
|
1187
|
+
if (unicodeFallbackFontCacheKey !== cacheKey) {
|
|
1188
|
+
unicodeFallbackFontCacheKey = cacheKey;
|
|
1189
|
+
unicodeFallbackFontPromise = null;
|
|
1190
|
+
}
|
|
1191
|
+
unicodeFallbackFontPromise ??= (async () => {
|
|
1192
|
+
try {
|
|
1193
|
+
const response = await fetch(font.url);
|
|
1194
|
+
if (!response.ok) return null;
|
|
1195
|
+
return new Uint8Array(await response.arrayBuffer());
|
|
1196
|
+
} catch {
|
|
1197
|
+
return null;
|
|
1198
|
+
}
|
|
1199
|
+
})();
|
|
1200
|
+
const data = await unicodeFallbackFontPromise;
|
|
1201
|
+
if (!data || data.byteLength === 0) return null;
|
|
1202
|
+
return {
|
|
1203
|
+
path: font.path ?? "unicode-fallback.ttf",
|
|
1204
|
+
data
|
|
1205
|
+
};
|
|
1206
|
+
}
|
|
1207
|
+
async function resolveEmojiFallbackFont(options) {
|
|
1208
|
+
const font = options?.emojiFallbackFont;
|
|
1209
|
+
if (!font) return null;
|
|
1210
|
+
if (font.data) {
|
|
1211
|
+
const data = font.data instanceof Uint8Array ? font.data : new Uint8Array(font.data);
|
|
1212
|
+
if (data.byteLength === 0) return null;
|
|
1213
|
+
return {
|
|
1214
|
+
path: font.path ?? "emoji-fallback.ttf",
|
|
1215
|
+
data
|
|
1216
|
+
};
|
|
1217
|
+
}
|
|
1218
|
+
if (!font.url) return null;
|
|
1219
|
+
const cacheKey = `${font.path ?? "emoji-fallback.ttf"}|${String(font.url)}`;
|
|
1220
|
+
if (emojiFallbackFontCacheKey !== cacheKey) {
|
|
1221
|
+
emojiFallbackFontCacheKey = cacheKey;
|
|
1222
|
+
emojiFallbackFontPromise = null;
|
|
1223
|
+
}
|
|
1224
|
+
emojiFallbackFontPromise ??= (async () => {
|
|
1225
|
+
try {
|
|
1226
|
+
const response = await fetch(font.url);
|
|
1227
|
+
if (!response.ok) return null;
|
|
1228
|
+
return new Uint8Array(await response.arrayBuffer());
|
|
1229
|
+
} catch {
|
|
1230
|
+
return null;
|
|
1231
|
+
}
|
|
1232
|
+
})();
|
|
1233
|
+
const data = await emojiFallbackFontPromise;
|
|
1234
|
+
if (!data || data.byteLength === 0) return null;
|
|
1235
|
+
return {
|
|
1236
|
+
path: font.path ?? "emoji-fallback.ttf",
|
|
1237
|
+
data
|
|
1238
|
+
};
|
|
1239
|
+
}
|
|
1240
|
+
async function instantiatePdfWasm(options) {
|
|
1241
|
+
const wasmOpts = {
|
|
1242
|
+
builtins: ["js-string"],
|
|
1243
|
+
importedStringConstants: "_"
|
|
1244
|
+
};
|
|
1245
|
+
const imports = createPdfWasmImports();
|
|
1246
|
+
const wasmBytes = options?.wasmBytes;
|
|
1247
|
+
if (wasmBytes) {
|
|
1248
|
+
const source = wasmBytes instanceof Uint8Array ? wasmBytes : new Uint8Array(wasmBytes);
|
|
1249
|
+
return WebAssembly.instantiate(source, imports, wasmOpts);
|
|
1250
|
+
}
|
|
1251
|
+
const wasmUrl = options?.wasmUrl ?? defaultPdfWasmUrl();
|
|
1252
|
+
const source = new Uint8Array(await fetchWasmBytes(wasmUrl, "pdf-converter"));
|
|
1253
|
+
return WebAssembly.instantiate(source, imports, wasmOpts);
|
|
1254
|
+
}
|
|
1255
|
+
function defaultPdfWasmUrl() {
|
|
1256
|
+
try {
|
|
1257
|
+
return new URL("./mbt/pdf-converter.wasm", import.meta.url);
|
|
1258
|
+
} catch {
|
|
1259
|
+
return runtimeAssetUrl("./mbt/pdf-converter.wasm", import.meta.url);
|
|
1260
|
+
}
|
|
1261
|
+
}
|
|
1262
|
+
async function loadPdfWasm(options) {
|
|
1263
|
+
if (options?.wasmBytes) {
|
|
1264
|
+
const exports = (await instantiatePdfWasm(options)).instance.exports;
|
|
1265
|
+
exports._start();
|
|
1266
|
+
assertPdfWasmApi(exports);
|
|
1267
|
+
return exports;
|
|
1268
|
+
}
|
|
1269
|
+
const source = String(options?.wasmUrl ?? defaultPdfWasmUrl());
|
|
1270
|
+
if (pdfWasmSource === source && pdfWasmInstance) return pdfWasmInstance;
|
|
1271
|
+
if (pdfWasmLoadPromise) {
|
|
1272
|
+
if (pdfWasmLoadSource === source) return pdfWasmLoadPromise;
|
|
1273
|
+
try {
|
|
1274
|
+
await pdfWasmLoadPromise;
|
|
1275
|
+
} catch {}
|
|
1276
|
+
return loadPdfWasm(options);
|
|
1277
|
+
}
|
|
1278
|
+
const pending = (async () => {
|
|
1279
|
+
const exports = (await instantiatePdfWasm({
|
|
1280
|
+
...options,
|
|
1281
|
+
wasmUrl: source
|
|
1282
|
+
})).instance.exports;
|
|
1283
|
+
exports._start();
|
|
1284
|
+
assertPdfWasmApi(exports);
|
|
1285
|
+
return exports;
|
|
1286
|
+
})();
|
|
1287
|
+
pdfWasmLoadSource = source;
|
|
1288
|
+
pdfWasmLoadPromise = pending;
|
|
1289
|
+
try {
|
|
1290
|
+
const exports = await pending;
|
|
1291
|
+
if (pdfWasmLoadPromise === pending) {
|
|
1292
|
+
pdfWasmSource = source;
|
|
1293
|
+
pdfWasmInstance = exports;
|
|
1294
|
+
}
|
|
1295
|
+
return exports;
|
|
1296
|
+
} finally {
|
|
1297
|
+
if (pdfWasmLoadPromise === pending) {
|
|
1298
|
+
pdfWasmLoadSource = null;
|
|
1299
|
+
pdfWasmLoadPromise = null;
|
|
1300
|
+
}
|
|
1301
|
+
}
|
|
1302
|
+
}
|
|
1303
|
+
async function serializePdfWasmConversion(mod, convert) {
|
|
1304
|
+
const current = (conversionTailsByInstance.get(mod) ?? Promise.resolve()).then(convert);
|
|
1305
|
+
const tail = current.then(() => void 0, () => void 0);
|
|
1306
|
+
conversionTailsByInstance.set(mod, tail);
|
|
1307
|
+
try {
|
|
1308
|
+
return await current;
|
|
1309
|
+
} finally {
|
|
1310
|
+
if (conversionTailsByInstance.get(mod) === tail) conversionTailsByInstance.delete(mod);
|
|
1311
|
+
}
|
|
1312
|
+
}
|
|
1313
|
+
function createBufferedPdfBridge(mod) {
|
|
1314
|
+
return {
|
|
1315
|
+
supportsRegularFonts: typeof mod.clear_registered_regular_fonts === "function",
|
|
1316
|
+
supportsEmojiBitmaps: typeof mod.clear_registered_emoji_bitmap_glyphs === "function",
|
|
1317
|
+
supportsMathFallback: typeof mod.clear_math_fallback_fonts === "function",
|
|
1318
|
+
clearRegularFonts: () => mod.clear_registered_regular_fonts?.(),
|
|
1319
|
+
clearEmojiBitmaps: () => mod.clear_registered_emoji_bitmap_glyphs?.(),
|
|
1320
|
+
clearMathFallback: () => mod.clear_math_fallback_fonts?.(),
|
|
1321
|
+
registerRegularFont: (typeface, style, path, data) => {
|
|
1322
|
+
const encodedTypeface = utf8Bytes(typeface);
|
|
1323
|
+
const encodedStyle = utf8Bytes(style);
|
|
1324
|
+
const encodedPath = utf8Bytes(path);
|
|
1325
|
+
writePdfBridgeInput(mod, [
|
|
1326
|
+
encodedTypeface,
|
|
1327
|
+
encodedStyle,
|
|
1328
|
+
encodedPath,
|
|
1329
|
+
data
|
|
1330
|
+
]);
|
|
1331
|
+
if (mod.register_pdf_font_buffered(encodedTypeface.length, encodedStyle.length, encodedPath.length) !== 1) throw new Error("PDF converter WASM rejected a buffered regular font");
|
|
1332
|
+
},
|
|
1333
|
+
registerEmojiBitmap: (bitmap) => {
|
|
1334
|
+
const mime = utf8Bytes(bitmap.mime);
|
|
1335
|
+
writePdfBridgeInput(mod, [mime, bitmap.data]);
|
|
1336
|
+
if (mod.register_pdf_emoji_bitmap_buffered(bitmap.codepoint, mime.length, bitmap.width, bitmap.height) !== 1) throw new Error("PDF converter WASM rejected a buffered emoji bitmap");
|
|
1337
|
+
},
|
|
1338
|
+
registerMathFallback: (family, data) => {
|
|
1339
|
+
const encodedFamily = utf8Bytes(family);
|
|
1340
|
+
writePdfBridgeInput(mod, [encodedFamily, data]);
|
|
1341
|
+
if (mod.register_pdf_math_fallback_buffered(encodedFamily.length) !== 1) throw new Error("PDF converter WASM rejected a buffered math fallback font");
|
|
1342
|
+
},
|
|
1343
|
+
setOfficeFontFallbacks: (enabled) => mod.set_pdf_office_font_fallbacks(enabled ? 1 : 0),
|
|
1344
|
+
convert: ({ buffer, fallbackFont, licensePayload, runtimePayload, watermarkPayload }) => {
|
|
1345
|
+
const pptx = new Uint8Array(buffer);
|
|
1346
|
+
const fallbackPath = utf8Bytes(fallbackFont?.path ?? "");
|
|
1347
|
+
const fallbackData = fallbackFont?.data ?? /* @__PURE__ */ new Uint8Array();
|
|
1348
|
+
const license = utf8Bytes(licensePayload);
|
|
1349
|
+
const runtime = utf8Bytes(runtimePayload);
|
|
1350
|
+
const watermarks = utf8Bytes(watermarkPayload);
|
|
1351
|
+
writePdfBridgeInput(mod, [
|
|
1352
|
+
pptx,
|
|
1353
|
+
fallbackPath,
|
|
1354
|
+
fallbackData,
|
|
1355
|
+
license,
|
|
1356
|
+
runtime,
|
|
1357
|
+
watermarks
|
|
1358
|
+
]);
|
|
1359
|
+
const outputLength = mod.run_pdf_conversion_buffered(pptx.length, fallbackPath.length, fallbackData.length, license.length, runtime.length, watermarks.length);
|
|
1360
|
+
if (!Number.isSafeInteger(outputLength) || outputLength < 0) throw new Error("PDF converter WASM rejected the buffered conversion request");
|
|
1361
|
+
return readPdfBridgeOutput(mod, outputLength);
|
|
1362
|
+
}
|
|
1363
|
+
};
|
|
1364
|
+
}
|
|
1365
|
+
function createDirectPdfBridge(mod) {
|
|
1366
|
+
const convertFromString = mod.convert_pptx_to_pdf_from_string;
|
|
1367
|
+
if (!convertFromString) throw new Error("PDF converter WASM does not expose a supported host bridge");
|
|
1368
|
+
return {
|
|
1369
|
+
supportsRegularFonts: typeof mod.clear_registered_regular_fonts === "function" && (typeof mod.register_font_style_from_string === "function" || typeof mod.register_regular_font_from_string === "function"),
|
|
1370
|
+
supportsEmojiBitmaps: typeof mod.clear_registered_emoji_bitmap_glyphs === "function" && typeof mod.register_emoji_bitmap_glyph_from_string === "function",
|
|
1371
|
+
supportsMathFallback: typeof mod.clear_math_fallback_fonts === "function" && typeof mod.register_math_fallback_font_from_string === "function",
|
|
1372
|
+
clearRegularFonts: () => mod.clear_registered_regular_fonts?.(),
|
|
1373
|
+
clearEmojiBitmaps: () => mod.clear_registered_emoji_bitmap_glyphs?.(),
|
|
1374
|
+
clearMathFallback: () => mod.clear_math_fallback_fonts?.(),
|
|
1375
|
+
registerRegularFont: (typeface, style, path, data) => {
|
|
1376
|
+
const latin1 = arrayBufferToLatin1(bytesToArrayBuffer(data));
|
|
1377
|
+
if (mod.register_font_style_from_string) mod.register_font_style_from_string(typeface, style, path, latin1);
|
|
1378
|
+
else if (style === "regular") mod.register_regular_font_from_string?.(typeface, path, latin1);
|
|
1379
|
+
},
|
|
1380
|
+
registerEmojiBitmap: (bitmap) => mod.register_emoji_bitmap_glyph_from_string?.(bitmap.codepoint, bitmap.mime, bitmap.width, bitmap.height, arrayBufferToLatin1(bytesToArrayBuffer(bitmap.data))),
|
|
1381
|
+
registerMathFallback: (family, data) => mod.register_math_fallback_font_from_string?.(family, arrayBufferToLatin1(bytesToArrayBuffer(data))),
|
|
1382
|
+
setOfficeFontFallbacks: (enabled) => mod.register_regular_font_from_string?.(officeFontFallbacksBridgeTypeface, enabled ? "true" : "false", "x"),
|
|
1383
|
+
convert: ({ buffer, fallbackFont, licensePayload, runtimePayload, watermarkPayload }) => {
|
|
1384
|
+
const pptxLatin1 = arrayBufferToLatin1(buffer);
|
|
1385
|
+
if (fallbackFont?.data) {
|
|
1386
|
+
const fallbackData = arrayBufferToLatin1(bytesToArrayBuffer(fallbackFont.data));
|
|
1387
|
+
if (mod.convert_pptx_to_pdf_with_unicode_fallback_license_and_watermarks_from_string) return mod.convert_pptx_to_pdf_with_unicode_fallback_license_and_watermarks_from_string(pptxLatin1, fallbackFont.path, fallbackData, licensePayload, runtimePayload, watermarkPayload);
|
|
1388
|
+
if (watermarkPayload && mod.convert_pptx_to_pdf_with_unicode_fallback_and_watermarks_from_string) return mod.convert_pptx_to_pdf_with_unicode_fallback_and_watermarks_from_string(pptxLatin1, fallbackFont.path, fallbackData, watermarkPayload);
|
|
1389
|
+
if (mod.convert_pptx_to_pdf_with_unicode_fallback_from_string) return mod.convert_pptx_to_pdf_with_unicode_fallback_from_string(pptxLatin1, fallbackFont.path, fallbackData);
|
|
1390
|
+
return convertFromString(pptxLatin1);
|
|
1391
|
+
}
|
|
1392
|
+
if (mod.convert_pptx_to_pdf_with_license_from_string) return mod.convert_pptx_to_pdf_with_license_from_string(pptxLatin1, licensePayload, runtimePayload, watermarkPayload);
|
|
1393
|
+
if (watermarkPayload && mod.convert_pptx_to_pdf_with_watermarks_from_string) return mod.convert_pptx_to_pdf_with_watermarks_from_string(pptxLatin1, watermarkPayload);
|
|
1394
|
+
return convertFromString(pptxLatin1);
|
|
1395
|
+
}
|
|
1396
|
+
};
|
|
1397
|
+
}
|
|
1398
|
+
function createPdfBridge(mod) {
|
|
1399
|
+
return hasBufferedPdfBridge(mod) ? createBufferedPdfBridge(mod) : createDirectPdfBridge(mod);
|
|
1400
|
+
}
|
|
1401
|
+
function resetPdfBridgeState(bridge) {
|
|
1402
|
+
bridge.clearRegularFonts();
|
|
1403
|
+
bridge.clearEmojiBitmaps();
|
|
1404
|
+
bridge.clearMathFallback();
|
|
1405
|
+
bridge.setOfficeFontFallbacks(false);
|
|
1406
|
+
}
|
|
1407
|
+
async function registerRegularFonts(bridge, options) {
|
|
1408
|
+
const fonts = [...options?.regularFonts ?? []];
|
|
1409
|
+
const emojiFallback = await resolveEmojiFallbackFont(options);
|
|
1410
|
+
if (emojiFallback) fonts.push({
|
|
1411
|
+
typeface: PDF_EMOJI_FALLBACK_TYPEFACE,
|
|
1412
|
+
path: emojiFallback.path,
|
|
1413
|
+
data: emojiFallback.data
|
|
1414
|
+
});
|
|
1415
|
+
if (!fonts.length || !bridge.supportsRegularFonts) return;
|
|
1416
|
+
for (const font of fonts) {
|
|
1417
|
+
if (!font?.typeface || !font.data) continue;
|
|
1418
|
+
const data = font.data instanceof Uint8Array ? font.data : new Uint8Array(font.data);
|
|
1419
|
+
if (data.byteLength === 0) continue;
|
|
1420
|
+
const style = font.style ?? "regular";
|
|
1421
|
+
const path = font.path ?? `${font.typeface}-${style}.ttf`;
|
|
1422
|
+
bridge.registerRegularFont(font.typeface, style, path, data);
|
|
1423
|
+
}
|
|
1424
|
+
}
|
|
1425
|
+
function emojiBitmapFallbackEnabled(options) {
|
|
1426
|
+
return options?.emojiBitmapFallback !== false;
|
|
1427
|
+
}
|
|
1428
|
+
function emojiBitmapFallbackFont(options) {
|
|
1429
|
+
return (typeof options?.emojiBitmapFallback === "object" ? options.emojiBitmapFallback.fontFamily : "") || "\"Apple Color Emoji\", \"Segoe UI Emoji\", \"Noto Color Emoji\", \"Twemoji Mozilla\", sans-serif";
|
|
1430
|
+
}
|
|
1431
|
+
function emojiBitmapFallbackPixelSize(options) {
|
|
1432
|
+
const configured = typeof options?.emojiBitmapFallback === "object" ? options.emojiBitmapFallback.pixelSize : 0;
|
|
1433
|
+
return Number.isFinite(configured) && configured >= 64 && configured <= 256 ? configured : 128;
|
|
1434
|
+
}
|
|
1435
|
+
function emojiPresentationText(codepoint) {
|
|
1436
|
+
const text = String.fromCodePoint(codepoint);
|
|
1437
|
+
return codepoint >= 8960 && codepoint <= 10175 ? `${text}\ufe0f` : text;
|
|
1438
|
+
}
|
|
1439
|
+
function canvasToBlob(canvas) {
|
|
1440
|
+
return new Promise((resolve) => canvas.toBlob(resolve, "image/png"));
|
|
1441
|
+
}
|
|
1442
|
+
function cropTransparentCanvas(source, padding) {
|
|
1443
|
+
const ctx = source.getContext("2d");
|
|
1444
|
+
if (!ctx) return source;
|
|
1445
|
+
const { width, height } = source;
|
|
1446
|
+
const data = ctx.getImageData(0, 0, width, height).data;
|
|
1447
|
+
let minX = width;
|
|
1448
|
+
let minY = height;
|
|
1449
|
+
let maxX = -1;
|
|
1450
|
+
let maxY = -1;
|
|
1451
|
+
for (let y = 0; y < height; y += 1) for (let x = 0; x < width; x += 1) {
|
|
1452
|
+
if (data[(y * width + x) * 4 + 3] === 0) continue;
|
|
1453
|
+
if (x < minX) minX = x;
|
|
1454
|
+
if (y < minY) minY = y;
|
|
1455
|
+
if (x > maxX) maxX = x;
|
|
1456
|
+
if (y > maxY) maxY = y;
|
|
1457
|
+
}
|
|
1458
|
+
if (maxX < minX || maxY < minY) return source;
|
|
1459
|
+
minX = Math.max(0, minX - padding);
|
|
1460
|
+
minY = Math.max(0, minY - padding);
|
|
1461
|
+
maxX = Math.min(width - 1, maxX + padding);
|
|
1462
|
+
maxY = Math.min(height - 1, maxY + padding);
|
|
1463
|
+
const cropped = document.createElement("canvas");
|
|
1464
|
+
cropped.width = maxX - minX + 1;
|
|
1465
|
+
cropped.height = maxY - minY + 1;
|
|
1466
|
+
cropped.getContext("2d")?.drawImage(source, minX, minY, cropped.width, cropped.height, 0, 0, cropped.width, cropped.height);
|
|
1467
|
+
return cropped;
|
|
1468
|
+
}
|
|
1469
|
+
async function renderEmojiBitmapFallback(codepoint, options) {
|
|
1470
|
+
if (typeof document === "undefined") return null;
|
|
1471
|
+
const canvas = document.createElement("canvas");
|
|
1472
|
+
const size = emojiBitmapFallbackPixelSize(options);
|
|
1473
|
+
canvas.width = size;
|
|
1474
|
+
canvas.height = size;
|
|
1475
|
+
const ctx = canvas.getContext("2d");
|
|
1476
|
+
if (!ctx) return null;
|
|
1477
|
+
ctx.clearRect(0, 0, size, size);
|
|
1478
|
+
ctx.font = `${Math.round(size * .78)}px ${emojiBitmapFallbackFont(options)}`;
|
|
1479
|
+
ctx.textAlign = "center";
|
|
1480
|
+
ctx.textBaseline = "middle";
|
|
1481
|
+
ctx.fillText(emojiPresentationText(codepoint), size / 2, size / 2);
|
|
1482
|
+
const cropped = cropTransparentCanvas(canvas, Math.max(2, Math.round(size * .03)));
|
|
1483
|
+
const blob = await canvasToBlob(cropped);
|
|
1484
|
+
if (!blob || blob.size === 0) return null;
|
|
1485
|
+
return {
|
|
1486
|
+
codepoint,
|
|
1487
|
+
mime: "image/png",
|
|
1488
|
+
width: cropped.width,
|
|
1489
|
+
height: cropped.height,
|
|
1490
|
+
data: new Uint8Array(await blob.arrayBuffer())
|
|
1491
|
+
};
|
|
1492
|
+
}
|
|
1493
|
+
async function registerEmojiBitmapFallbacks(bridge, buffer, options, prepared) {
|
|
1494
|
+
if (!emojiBitmapFallbackEnabled(options) || !bridge.supportsEmojiBitmaps || prepared === void 0 && typeof document === "undefined") return;
|
|
1495
|
+
const bitmaps = [];
|
|
1496
|
+
if (prepared) bitmaps.push(...prepared);
|
|
1497
|
+
else {
|
|
1498
|
+
const codepoints = collectEmojiBitmapFallbackCodepoints(buffer);
|
|
1499
|
+
for (const codepoint of codepoints) {
|
|
1500
|
+
const bitmap = await renderEmojiBitmapFallback(codepoint, options);
|
|
1501
|
+
if (bitmap) bitmaps.push(bitmap);
|
|
1502
|
+
}
|
|
1503
|
+
}
|
|
1504
|
+
if (bitmaps.length === 0) return;
|
|
1505
|
+
for (const bitmap of bitmaps) bridge.registerEmojiBitmap(bitmap);
|
|
1506
|
+
}
|
|
1507
|
+
async function resolveMathFallbackBytes(options) {
|
|
1508
|
+
const explicit = options?.mathFallbackFont;
|
|
1509
|
+
if (explicit?.data) {
|
|
1510
|
+
const data = explicit.data instanceof Uint8Array ? explicit.data : new Uint8Array(explicit.data);
|
|
1511
|
+
if (data.byteLength > 0) return {
|
|
1512
|
+
family: explicit.family ?? "math-fallback",
|
|
1513
|
+
data
|
|
1514
|
+
};
|
|
1515
|
+
}
|
|
1516
|
+
if (explicit?.url) try {
|
|
1517
|
+
const response = await fetch(explicit.url);
|
|
1518
|
+
if (response.ok) {
|
|
1519
|
+
const data = new Uint8Array(await response.arrayBuffer());
|
|
1520
|
+
if (data.byteLength > 0) return {
|
|
1521
|
+
family: explicit.family ?? "math-fallback",
|
|
1522
|
+
data
|
|
1523
|
+
};
|
|
1524
|
+
}
|
|
1525
|
+
} catch {}
|
|
1526
|
+
const shared = await resolveUnicodeFallbackFont(options);
|
|
1527
|
+
if (shared) return {
|
|
1528
|
+
family: shared.path,
|
|
1529
|
+
data: shared.data
|
|
1530
|
+
};
|
|
1531
|
+
return null;
|
|
1532
|
+
}
|
|
1533
|
+
async function registerMathFallback(bridge, options) {
|
|
1534
|
+
if (!bridge.supportsMathFallback) return;
|
|
1535
|
+
const resolved = await resolveMathFallbackBytes(options);
|
|
1536
|
+
if (resolved) bridge.registerMathFallback(resolved.family, resolved.data);
|
|
1537
|
+
}
|
|
1538
|
+
function watermarksJson(options) {
|
|
1539
|
+
return options?.watermarks?.length ? JSON.stringify(options.watermarks) : "";
|
|
1540
|
+
}
|
|
1541
|
+
function licenseJson(options) {
|
|
1542
|
+
if (options?.license === void 0 || options.license === null) return "";
|
|
1543
|
+
return typeof options.license === "string" ? options.license : JSON.stringify(options.license);
|
|
1544
|
+
}
|
|
1545
|
+
function runtimeOrigin() {
|
|
1546
|
+
return typeof location !== "undefined" ? location.origin : "";
|
|
1547
|
+
}
|
|
1548
|
+
function runtimeHostname() {
|
|
1549
|
+
return typeof location !== "undefined" ? location.hostname : "";
|
|
1550
|
+
}
|
|
1551
|
+
function licenseRuntimeJson(options) {
|
|
1552
|
+
return JSON.stringify({
|
|
1553
|
+
feature: "pdf-export",
|
|
1554
|
+
nowUnixMs: options?.licenseRuntime?.now ?? Date.now(),
|
|
1555
|
+
origin: options?.licenseRuntime?.origin ?? runtimeOrigin(),
|
|
1556
|
+
hostname: options?.licenseRuntime?.hostname ?? runtimeHostname(),
|
|
1557
|
+
deploymentId: options?.licenseRuntime?.deploymentId ?? "",
|
|
1558
|
+
sdkVersion: options?.licenseRuntime?.sdkVersion ?? "",
|
|
1559
|
+
inputBytes: options?.licenseRuntime?.inputBytes,
|
|
1560
|
+
restrictionPolicySnapshot: options?.licenseRuntime?.restrictionPolicySnapshot
|
|
1561
|
+
});
|
|
1562
|
+
}
|
|
1563
|
+
/** @internal Worker entry; browser callers should use pptxToPdf(). */
|
|
1564
|
+
async function pptxToPdfOnCurrentThread(buffer, options, preparedEmojiBitmaps) {
|
|
1565
|
+
options?.signal?.throwIfAborted();
|
|
1566
|
+
const resourceStarted = performance.now();
|
|
1567
|
+
checkBrowserInput(buffer.byteLength);
|
|
1568
|
+
const mod = await loadPdfWasm(options);
|
|
1569
|
+
options?.signal?.throwIfAborted();
|
|
1570
|
+
return serializePdfWasmConversion(mod, () => convertPptxWithPdfWasm(mod, buffer, resourceStarted, options, preparedEmojiBitmaps));
|
|
1571
|
+
}
|
|
1572
|
+
async function convertPptxWithPdfWasm(mod, buffer, resourceStarted, options, preparedEmojiBitmaps) {
|
|
1573
|
+
options?.signal?.throwIfAborted();
|
|
1574
|
+
const bridge = createPdfBridge(mod);
|
|
1575
|
+
let pdfResult;
|
|
1576
|
+
try {
|
|
1577
|
+
resetPdfBridgeState(bridge);
|
|
1578
|
+
await registerRegularFonts(bridge, options);
|
|
1579
|
+
options?.signal?.throwIfAborted();
|
|
1580
|
+
await registerEmojiBitmapFallbacks(bridge, buffer, options, preparedEmojiBitmaps);
|
|
1581
|
+
options?.signal?.throwIfAborted();
|
|
1582
|
+
await registerMathFallback(bridge, options);
|
|
1583
|
+
options?.signal?.throwIfAborted();
|
|
1584
|
+
const fallbackFont = pptxHasUnicodeText(buffer) ? await resolveUnicodeFallbackFont(options) : null;
|
|
1585
|
+
bridge.setOfficeFontFallbacks(options?.officeFontFallbacks === true);
|
|
1586
|
+
options?.signal?.throwIfAborted();
|
|
1587
|
+
pdfResult = bridge.convert({
|
|
1588
|
+
buffer,
|
|
1589
|
+
fallbackFont,
|
|
1590
|
+
licensePayload: licenseJson(options),
|
|
1591
|
+
runtimePayload: licenseRuntimeJson(options),
|
|
1592
|
+
watermarkPayload: watermarksJson(options)
|
|
1593
|
+
});
|
|
1594
|
+
} finally {
|
|
1595
|
+
resetPdfBridgeState(bridge);
|
|
1596
|
+
}
|
|
1597
|
+
const errorPrefix = typeof pdfResult === "string" ? pdfResult : uint8ArrayToLatin1(pdfResult.subarray(0, 15));
|
|
1598
|
+
if (errorPrefix.startsWith("ERROR_RESOURCE:")) {
|
|
1599
|
+
const rawDetails = (typeof pdfResult === "string" ? pdfResult : new TextDecoder().decode(pdfResult)).slice(15);
|
|
1600
|
+
const violation = resourcePolicyErrorFromDetails(JSON.parse(rawDetails));
|
|
1601
|
+
if (violation) throw violation;
|
|
1602
|
+
throw new Error("PDF conversion returned an invalid Resource Policy failure");
|
|
1603
|
+
}
|
|
1604
|
+
if (errorPrefix.startsWith("ERROR:")) {
|
|
1605
|
+
const errorResult = typeof pdfResult === "string" ? pdfResult : new TextDecoder().decode(pdfResult);
|
|
1606
|
+
throw new Error(errorResult.slice(6) || "Unknown PDF conversion error");
|
|
1607
|
+
}
|
|
1608
|
+
const pdf = typeof pdfResult === "string" ? latin1ToUint8Array(pdfResult) : pdfResult;
|
|
1609
|
+
checkBrowserOutput(pdf.byteLength, "PDF output");
|
|
1610
|
+
checkBrowserElapsed(resourceStarted, "PDF conversion wall clock");
|
|
1611
|
+
return pdf;
|
|
1612
|
+
}
|
|
1613
|
+
//#endregion
|
|
1614
|
+
//#region src/pdf-worker.ts
|
|
1615
|
+
const ctx = self;
|
|
1616
|
+
const waitingForEmoji = /* @__PURE__ */ new Map();
|
|
1617
|
+
const active = /* @__PURE__ */ new Map();
|
|
1618
|
+
async function handlePdfRequest(request, emojiBitmaps) {
|
|
1619
|
+
const { id, buffer, options } = request;
|
|
1620
|
+
const abort = active.get(id);
|
|
1621
|
+
if (!abort) return;
|
|
1622
|
+
try {
|
|
1623
|
+
const bytes = await pptxToPdfOnCurrentThread(buffer, {
|
|
1624
|
+
...options,
|
|
1625
|
+
signal: abort.signal
|
|
1626
|
+
}, emojiBitmaps);
|
|
1627
|
+
if (abort.signal.aborted) return;
|
|
1628
|
+
const data = bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
|
|
1629
|
+
ctx.postMessage({
|
|
1630
|
+
id,
|
|
1631
|
+
status: "success",
|
|
1632
|
+
data
|
|
1633
|
+
}, [data]);
|
|
1634
|
+
} catch (error) {
|
|
1635
|
+
if (abort.signal.aborted) return;
|
|
1636
|
+
ctx.postMessage({
|
|
1637
|
+
id,
|
|
1638
|
+
status: "error",
|
|
1639
|
+
error: serializeWorkerError(error)
|
|
1640
|
+
});
|
|
1641
|
+
} finally {
|
|
1642
|
+
active.delete(id);
|
|
1643
|
+
if (abort.signal.aborted) ctx.postMessage({
|
|
1644
|
+
id,
|
|
1645
|
+
status: "error",
|
|
1646
|
+
error: serializeWorkerError(abort.signal.reason)
|
|
1647
|
+
});
|
|
1648
|
+
}
|
|
1649
|
+
}
|
|
1650
|
+
ctx.onmessage = (event) => {
|
|
1651
|
+
const request = event.data;
|
|
1652
|
+
if (request.kind === "cancel") {
|
|
1653
|
+
const abort = active.get(request.id);
|
|
1654
|
+
abort?.abort();
|
|
1655
|
+
active.delete(request.id);
|
|
1656
|
+
if (waitingForEmoji.delete(request.id) && abort) ctx.postMessage({
|
|
1657
|
+
id: request.id,
|
|
1658
|
+
status: "error",
|
|
1659
|
+
error: serializeWorkerError(abort.signal.reason)
|
|
1660
|
+
});
|
|
1661
|
+
return;
|
|
1662
|
+
}
|
|
1663
|
+
if (request.kind === "convert") {
|
|
1664
|
+
active.set(request.id, new AbortController());
|
|
1665
|
+
try {
|
|
1666
|
+
const codepoints = request.options?.emojiBitmapFallback === false ? [] : collectEmojiBitmapFallbackCodepoints(request.buffer);
|
|
1667
|
+
if (codepoints.length > 0) {
|
|
1668
|
+
waitingForEmoji.set(request.id, request);
|
|
1669
|
+
ctx.postMessage({
|
|
1670
|
+
id: request.id,
|
|
1671
|
+
status: "emoji-codepoints",
|
|
1672
|
+
codepoints
|
|
1673
|
+
});
|
|
1674
|
+
return;
|
|
1675
|
+
}
|
|
1676
|
+
handlePdfRequest(request, []);
|
|
1677
|
+
} catch (error) {
|
|
1678
|
+
active.delete(request.id);
|
|
1679
|
+
ctx.postMessage({
|
|
1680
|
+
id: request.id,
|
|
1681
|
+
status: "error",
|
|
1682
|
+
error: serializeWorkerError(error)
|
|
1683
|
+
});
|
|
1684
|
+
}
|
|
1685
|
+
return;
|
|
1686
|
+
}
|
|
1687
|
+
const pending = waitingForEmoji.get(request.id);
|
|
1688
|
+
if (!pending) return;
|
|
1689
|
+
waitingForEmoji.delete(request.id);
|
|
1690
|
+
handlePdfRequest(pending, request.emojiBitmaps);
|
|
1691
|
+
};
|
|
1692
|
+
//#endregion
|