renderpdf-js 0.1.0 → 0.2.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/dist/index.js CHANGED
@@ -1,18 +1,18 @@
1
1
  import e from "@pdf-lib/fontkit";
2
- import { PDFArray as t, PDFDict as n, PDFDocument as r, PDFHexString as i, PDFName as a, TextRenderingMode as o, beginText as s, clip as c, endPath as l, endText as u, popGraphicsState as d, pushGraphicsState as f, rectangle as p, rgb as m, setFillingColor as h, setFontAndSize as g, setGraphicsState as _, setTextMatrix as v, setTextRenderingMode as ee, showText as te } from "pdf-lib";
3
- import { toPng as y } from "html-to-image";
2
+ import { PDFArray as t, PDFDict as n, PDFDocument as r, PDFHexString as i, PDFName as a, TextRenderingMode as o, beginText as s, clip as c, concatTransformationMatrix as l, drawObject as u, endPath as d, endText as f, popGraphicsState as p, pushGraphicsState as m, rectangle as h, rgb as g, setFillingColor as _, setFontAndSize as v, setGraphicsState as y, setTextMatrix as b, setTextRenderingMode as x, showText as S } from "pdf-lib";
3
+ import { toPng as C } from "html-to-image";
4
4
  // src/diagnostics.ts
5
- var b = class extends Error {
5
+ var w = class extends Error {
6
6
  code;
7
7
  constructor(e, t, n) {
8
8
  super(t, n), this.code = e, this.name = "RenderPDFError";
9
9
  }
10
10
  };
11
- function x(e) {
11
+ function T(e) {
12
12
  if (e?.aborted) throw new DOMException("PDF rendering was cancelled.", "AbortError");
13
13
  }
14
14
  // src/breaks.ts
15
- var S = /* @__PURE__ */ new Set([
15
+ var E = /* @__PURE__ */ new Set([
16
16
  "block",
17
17
  "flow-root",
18
18
  "flex",
@@ -24,14 +24,14 @@ var S = /* @__PURE__ */ new Set([
24
24
  "table-header-group",
25
25
  "table-footer-group"
26
26
  ]);
27
- function C(e, t) {
27
+ function D(e, t) {
28
28
  let n = e.getPropertyValue(`break-${t}`).trim(), r = e.getPropertyValue(`page-break-${t}`).trim();
29
29
  return n && n !== "auto" ? n : r || n || "auto";
30
30
  }
31
- function w(e, t) {
32
- if (e !== "auto" && e !== "avoid" && e !== "avoid-page" && (t === "inside" || e !== "page" && e !== "always")) throw new b("UNSUPPORTED_CSS", `Unsupported CSS break-${t}: ${e}; only page/always boundaries and avoid/avoid-page preferences are supported.`);
31
+ function O(e, t) {
32
+ if (e !== "auto" && e !== "avoid" && e !== "avoid-page" && (t === "inside" || e !== "page" && e !== "always")) throw new w("UNSUPPORTED_CSS", `Unsupported CSS break-${t}: ${e}; only page/always boundaries and avoid/avoid-page preferences are supported.`);
33
33
  }
34
- function T(e, t) {
34
+ function ee(e, t) {
35
35
  let n = e.getBoundingClientRect();
36
36
  return {
37
37
  x: n.left - t.left,
@@ -40,20 +40,20 @@ function T(e, t) {
40
40
  height: n.height
41
41
  };
42
42
  }
43
- function ne(e) {
43
+ function te(e) {
44
44
  let t = getComputedStyle(e), n = e.getBoundingClientRect();
45
- return t.visibility === "visible" && S.has(t.display) && n.width > 0 && n.height > 0;
45
+ return t.visibility === "visible" && E.has(t.display) && n.width > 0 && n.height > 0;
46
46
  }
47
- function re(e, t) {
47
+ function ne(e, t) {
48
48
  let n = t === "previous" ? e.previousElementSibling : e.nextElementSibling;
49
49
  for (; n;) {
50
50
  if (!(n instanceof HTMLElement)) return;
51
51
  let e = getComputedStyle(n);
52
- if (e.display !== "none" && e.visibility === "visible") return ne(n) ? n : void 0;
52
+ if (e.display !== "none" && e.visibility === "visible") return te(n) ? n : void 0;
53
53
  n = t === "previous" ? n.previousElementSibling : n.nextElementSibling;
54
54
  }
55
55
  }
56
- function ie(e, t) {
56
+ function re(e, t) {
57
57
  let n = Math.min(e.x, t.x), r = Math.min(e.y, t.y);
58
58
  return {
59
59
  x: n,
@@ -62,8 +62,8 @@ function ie(e, t) {
62
62
  height: Math.max(e.y + e.height, t.y + t.height) - r
63
63
  };
64
64
  }
65
- function ae(e, t) {
66
- if (!e.isConnected || e.ownerDocument !== document) throw new b("INVALID_SOURCE", "CSS page-break collection requires a connected source in the current document.");
65
+ function ie(e, t) {
66
+ if (!e.isConnected || e.ownerDocument !== document) throw new w("INVALID_SOURCE", "CSS page-break collection requires a connected source in the current document.");
67
67
  let n = e.getBoundingClientRect(), r = t.operations.filter((e) => e.kind === "raster"), i = /* @__PURE__ */ new Map(), a = [];
68
68
  for (let t of [e, ...e.querySelectorAll("*")]) {
69
69
  if (!(t instanceof HTMLElement)) continue;
@@ -71,25 +71,25 @@ function ae(e, t) {
71
71
  if (e.display === "none" || e.visibility !== "visible") continue;
72
72
  let o = t.getBoundingClientRect();
73
73
  if (o.width === 0 && o.height === 0 && t.getClientRects().length === 0) continue;
74
- let s = C(e, "before"), c = C(e, "after"), l = C(e, "inside");
75
- w(s, "before"), w(c, "after"), w(l, "inside");
74
+ let s = D(e, "before"), c = D(e, "after"), l = D(e, "inside");
75
+ O(s, "before"), O(c, "after"), O(l, "inside");
76
76
  let u = r.find((e) => e.element.contains(t));
77
- if (!u && S.has(e.display) && (e.widows && e.widows !== "2" || e.orphans && e.orphans !== "2")) throw new b("UNSUPPORTED_CSS", "Nondefault CSS widows/orphans are unsupported; this renderer preserves measured text lines without a line-count pagination policy.");
77
+ if (!u && E.has(e.display) && (e.widows && e.widows !== "2" || e.orphans && e.orphans !== "2")) throw new w("UNSUPPORTED_CSS", "Nondefault CSS widows/orphans are unsupported; this renderer preserves measured text lines without a line-count pagination policy.");
78
78
  if (s === "auto" && c === "auto" && l === "auto") continue;
79
79
  let d = s === "page" || s === "always", f = c === "page" || c === "always";
80
80
  if (u && u.element !== t) {
81
- if (d || f) throw new b("PAGINATION_FAILED", "A forced CSS page break lies inside an atomic raster region; move the break outside that region.");
81
+ if (d || f) throw new w("PAGINATION_FAILED", "A forced CSS page break lies inside an atomic raster region; move the break outside that region.");
82
82
  continue;
83
83
  }
84
- if (!S.has(e.display)) throw new b("UNSUPPORTED_CSS", "Explicit CSS page breaks require a block, table group or table row; inline fragments and table cells are unsupported.");
85
- let p = T(t, n);
84
+ if (!E.has(e.display)) throw new w("UNSUPPORTED_CSS", "Explicit CSS page breaks require a block, table group or table row; inline fragments and table cells are unsupported.");
85
+ let p = ee(t, n);
86
86
  d && a.push(p.y), f && a.push(p.y + p.height), l !== "auto" && !u && i.set(`${p.x}:${p.y}:${p.width}:${p.height}`, p);
87
87
  for (let e of ["previous", "next"]) {
88
88
  let r = e === "previous" ? s : c;
89
89
  if (r !== "avoid" && r !== "avoid-page") continue;
90
- let a = re(t, e);
90
+ let a = ne(t, e);
91
91
  if (!a) continue;
92
- let o = ie(p, T(a, n));
92
+ let o = re(p, ee(a, n));
93
93
  i.set(`${o.x}:${o.y}:${o.width}:${o.height}`, o);
94
94
  }
95
95
  }
@@ -98,8 +98,28 @@ function ae(e, t) {
98
98
  avoidBreakBounds: [...i.values()]
99
99
  };
100
100
  }
101
+ // src/export-options.ts
102
+ function ae(e) {
103
+ if (e === void 0) return;
104
+ if (!e || typeof e != "object" || Array.isArray(e)) throw new w("INVALID_OPTIONS", "PDF metadata must be an object.");
105
+ let { title: t, author: n, subject: r, keywords: i } = e;
106
+ if ([
107
+ t,
108
+ n,
109
+ r
110
+ ].some((e) => e !== void 0 && typeof e != "string") || i !== void 0 && (!Array.isArray(i) || Array.from(i).some((e) => typeof e != "string"))) throw new w("INVALID_OPTIONS", "PDF metadata title, author and subject must be strings; keywords must be an array of strings.");
111
+ return Object.freeze({
112
+ ...t === void 0 ? {} : { title: t },
113
+ ...n === void 0 ? {} : { author: n },
114
+ ...r === void 0 ? {} : { subject: r },
115
+ ...i === void 0 ? {} : { keywords: Object.freeze([...i]) }
116
+ });
117
+ }
118
+ function oe(e, t) {
119
+ e.setTitle(t?.title ?? "RenderPDF.js"), t?.author !== void 0 && e.setAuthor(t.author), t?.subject !== void 0 && e.setSubject(t.subject), t?.keywords !== void 0 && e.setKeywords([...t.keywords]);
120
+ }
101
121
  // src/fonts.ts
102
- var E = /* @__PURE__ */ new WeakMap(), D = /* @__PURE__ */ new WeakMap(), oe = /* @__PURE__ */ new Set([
122
+ var se = /* @__PURE__ */ new WeakMap(), ce = /* @__PURE__ */ new WeakMap(), le = /* @__PURE__ */ new Set([
103
123
  "serif",
104
124
  "sans-serif",
105
125
  "monospace",
@@ -119,40 +139,40 @@ var E = /* @__PURE__ */ new WeakMap(), D = /* @__PURE__ */ new WeakMap(), oe = /
119
139
  "revert",
120
140
  "revert-layer"
121
141
  ]);
122
- function O(e) {
123
- if (e.byteLength < 12) throw new b("FONT_LOAD_FAILED", "Provide a complete static TTF/OTF font program.");
142
+ function ue(e) {
143
+ if (e.byteLength < 12) throw new w("FONT_LOAD_FAILED", "Provide a complete static TTF/OTF font program.");
124
144
  let t = new DataView(e.buffer, e.byteOffset, e.byteLength), n = t.getUint32(0);
125
- if (n !== 65536 && n !== 1330926671) throw new b("FONT_LOAD_FAILED", "Only static TTF/OTF programs are supported; WOFF fonts and font collections are unsupported.");
145
+ if (n !== 65536 && n !== 1330926671) throw new w("FONT_LOAD_FAILED", "Only static TTF/OTF programs are supported; WOFF fonts and font collections are unsupported.");
126
146
  let r = t.getUint16(4);
127
- if (r === 0 || 12 + r * 16 > e.byteLength) throw new b("FONT_LOAD_FAILED", "The TTF/OTF font table directory is incomplete.");
128
- for (let e = 0; e < r; e += 1) if (t.getUint32(12 + e * 16) === 1719034226) throw new b("FONT_LOAD_FAILED", "Variable fonts are unsupported. Provide an explicitly generated static TTF/OTF instance.");
147
+ if (r === 0 || 12 + r * 16 > e.byteLength) throw new w("FONT_LOAD_FAILED", "The TTF/OTF font table directory is incomplete.");
148
+ for (let e = 0; e < r; e += 1) if (t.getUint32(12 + e * 16) === 1719034226) throw new w("FONT_LOAD_FAILED", "Variable fonts are unsupported. Provide an explicitly generated static TTF/OTF instance.");
129
149
  }
130
150
  function k(e, t, n, r, i) {
131
151
  for (let a of e.fonts) {
132
152
  if (a === i || a.family.replace(/^["']|["']$/g, "").toLowerCase() !== t.toLowerCase() || a.style !== r) continue;
133
153
  let e = a.weight === "normal" ? [400] : a.weight === "bold" ? [700] : a.weight.split(/\s+/).map(Number);
134
- if (e[0] === n || e.length === 2 && n >= e[0] && n <= e[1]) throw new b("INVALID_OPTIONS", `Font ${t} ${n} ${r} conflicts with an existing browser font face. Choose a family owned by this registration.`);
154
+ if (e[0] === n || e.length === 2 && n >= e[0] && n <= e[1]) throw new w("INVALID_OPTIONS", `Font ${t} ${n} ${r} conflicts with an existing browser font face. Choose a family owned by this registration.`);
135
155
  }
136
156
  }
137
157
  function A(e) {
138
158
  for (let t of e) {
139
- let e = t && typeof t == "object" ? D.get(t) : void 0;
140
- if (!e || typeof document > "u" || e.document !== document || !document.fonts.has(e.face)) throw new b("MISSING_FONT", "Provide font handles returned by registerFont() whose browser faces remain registered in the current document.");
159
+ let e = t && typeof t == "object" ? ce.get(t) : void 0;
160
+ if (!e || typeof document > "u" || e.document !== document || !document.fonts.has(e.face)) throw new w("MISSING_FONT", "Provide font handles returned by registerFont() whose browser faces remain registered in the current document.");
141
161
  k(document, t.family, t.weight, t.style, e.face);
142
162
  }
143
163
  }
144
- async function se(e) {
145
- if (!e || typeof e != "object" || typeof e.family != "string") throw new b("INVALID_OPTIONS", "Font registration requires a CSS family and font bytes.");
164
+ async function de(e) {
165
+ if (!e || typeof e != "object" || typeof e.family != "string") throw new w("INVALID_OPTIONS", "Font registration requires a CSS family and font bytes.");
146
166
  let t = e.family.trim(), n = e.weight ?? 400, r = e.style ?? "normal";
147
- if (!t || !/^[\w -]+$/.test(t) || oe.has(t.toLowerCase()) || !Number.isInteger(n) || n < 1 || n > 1e3 || r !== "normal" && r !== "italic" || !(e.data instanceof Uint8Array) && !(e.data instanceof ArrayBuffer) || e.data.byteLength === 0) throw new b("INVALID_OPTIONS", "Provide a plain custom CSS family (letters, digits, spaces, hyphens, or underscores), nonempty TTF/OTF bytes, integer weight from 1 to 1000, and normal or italic style.");
148
- if (typeof document > "u" || typeof FontFace > "u" || !document.fonts) throw new b("FONT_LOAD_FAILED", "Font registration requires a browser document with FontFace support.");
167
+ if (!t || !/^[\w -]+$/.test(t) || le.has(t.toLowerCase()) || !Number.isInteger(n) || n < 1 || n > 1e3 || r !== "normal" && r !== "italic" || !(e.data instanceof Uint8Array) && !(e.data instanceof ArrayBuffer) || e.data.byteLength === 0) throw new w("INVALID_OPTIONS", "Provide a plain custom CSS family (letters, digits, spaces, hyphens, or underscores), nonempty TTF/OTF bytes, integer weight from 1 to 1000, and normal or italic style.");
168
+ if (typeof document > "u" || typeof FontFace > "u" || !document.fonts) throw new w("FONT_LOAD_FAILED", "Font registration requires a browser document with FontFace support.");
149
169
  let i = new Uint8Array(e.data instanceof Uint8Array ? e.data : new Uint8Array(e.data));
150
- O(i);
151
- let a = `${t.toLowerCase()}:${n}:${r}`, o = document, s = E.get(o);
152
- s || (s = /* @__PURE__ */ new Map(), E.set(o, s));
170
+ ue(i);
171
+ let a = `${t.toLowerCase()}:${n}:${r}`, o = document, s = se.get(o);
172
+ s || (s = /* @__PURE__ */ new Map(), se.set(o, s));
153
173
  let c = s.get(a);
154
174
  if (k(o, t, n, r, c?.face), c) {
155
- if (c.bytes.length !== i.length || c.bytes.some((e, t) => e !== i[t])) throw new b("INVALID_OPTIONS", `Font ${t} ${n} ${r} is already registered with different bytes.`);
175
+ if (c.bytes.length !== i.length || c.bytes.some((e, t) => e !== i[t])) throw new w("INVALID_OPTIONS", `Font ${t} ${n} ${r} is already registered with different bytes.`);
156
176
  return c.promise;
157
177
  }
158
178
  async function l() {
@@ -170,12 +190,12 @@ async function se(e) {
170
190
  return i.slice();
171
191
  }
172
192
  });
173
- return D.set(a, {
193
+ return ce.set(a, {
174
194
  document: o,
175
195
  face: e
176
196
  }), a;
177
197
  } catch (e) {
178
- throw s.delete(a), e instanceof b ? e : new b("FONT_LOAD_FAILED", `Could not load font ${t} ${n} ${r}.`, { cause: e });
198
+ throw s.delete(a), e instanceof w ? e : new w("FONT_LOAD_FAILED", `Could not load font ${t} ${n} ${r}.`, { cause: e });
179
199
  }
180
200
  }
181
201
  let u = Promise.resolve().then(l), d = {
@@ -185,8 +205,8 @@ async function se(e) {
185
205
  return s.set(a, d), u;
186
206
  }
187
207
  // src/planner.ts
188
- var j = /^data:image\/(?:png|jpeg|gif|webp|avif);base64,[a-z\d+/=\s]+$/i;
189
- function M(e) {
208
+ var fe = /^data:image\/(?:png|jpeg|gif|webp|avif);base64,[a-z\d+/=\s]+$/i;
209
+ function pe(e) {
190
210
  let t = 0, n = "", r = 0, i = [];
191
211
  for (let a = 0; a < e.length; a += 1) {
192
212
  let o = e[a];
@@ -194,23 +214,23 @@ function M(e) {
194
214
  }
195
215
  return i.push(e.slice(r).trim()), i;
196
216
  }
197
- function N(e) {
217
+ function me(e) {
198
218
  let t = [];
199
219
  if (e.backgroundImage !== "none") {
200
- for (let t of M(e.backgroundImage)) {
220
+ for (let t of pe(e.backgroundImage)) {
201
221
  if (/^(?:repeating-)?(?:linear|radial|conic)-gradient\(/i.test(t)) continue;
202
222
  let e = t.match(/^url\(\s*(["']?)(.*?)\1\s*\)$/i)?.[2];
203
- if (!e || !j.test(e)) throw new b("UNSUPPORTED_CSS", "Unsupported CSS: automatic raster backgrounds require gradients or inline bitmap URLs.");
223
+ if (!e || !fe.test(e)) throw new w("UNSUPPORTED_CSS", "Unsupported CSS: automatic raster backgrounds require gradients or inline bitmap URLs.");
204
224
  }
205
225
  t.push("background-image");
206
226
  }
207
227
  if (e.borderRadius !== "0px" && t.push("border-radius"), e.boxShadow !== "none") {
208
- if (M(e.boxShadow).some((e) => !/\binset\b/.test(e))) throw new b("UNSUPPORTED_CSS", "Unsupported CSS: outer shadows require expanded capture bounds; only inset shadows can fall back automatically.");
228
+ if (pe(e.boxShadow).some((e) => !/\binset\b/.test(e))) throw new w("UNSUPPORTED_CSS", "Unsupported CSS: outer shadows require expanded capture bounds; only inset shadows can fall back automatically.");
209
229
  t.push("inset box-shadow");
210
230
  }
211
231
  return e.opacity !== "1" && t.push("opacity"), t.length ? t.join(", ") : void 0;
212
232
  }
213
- function ce(e, t) {
233
+ function he(e, t) {
214
234
  if (!(e instanceof HTMLImageElement) && ![
215
235
  "block",
216
236
  "flow-root",
@@ -219,18 +239,278 @@ function ce(e, t) {
219
239
  "inline-flex",
220
240
  "grid",
221
241
  "inline-grid"
222
- ].includes(t.display)) throw new b("UNSUPPORTED_CSS", "Automatic raster fallback requires a rectangular block, flex, grid, inline-block, or image boundary; wrap inline or table-internal effects in a supported block.");
223
- if (Array.from(e.getClientRects()).filter((e) => e.width > 0 && e.height > 0).length !== 1) throw new b("UNSUPPORTED_CSS", "Automatic raster fallback cannot capture fragmented inline or multi-column regions.");
242
+ ].includes(t.display)) throw new w("UNSUPPORTED_CSS", "Automatic raster fallback requires a rectangular block, flex, grid, inline-block, or image boundary; wrap inline or table-internal effects in a supported block.");
243
+ if (Array.from(e.getClientRects()).filter((e) => e.width > 0 && e.height > 0).length !== 1) throw new w("UNSUPPORTED_CSS", "Automatic raster fallback cannot capture fragmented inline or multi-column regions.");
244
+ }
245
+ // src/table-borders.ts
246
+ var j = .001;
247
+ function M(e, t) {
248
+ let n = (e) => e.style === "hidden" ? 2 : +(e.style === "solid" && e.width > 0);
249
+ return n(e) - n(t) || e.width - t.width || Number(e.owner === "cell") - Number(t.owner === "cell") || (e.direction === t.direction ? e.direction === "vertical" ? t.column - e.column || t.row - e.row : t.row - e.row || t.column - e.column : Number(e.direction === "horizontal") - Number(t.direction === "horizontal"));
250
+ }
251
+ function ge(e) {
252
+ let t = /* @__PURE__ */ new Map();
253
+ for (let n of e) {
254
+ if (![
255
+ n.position,
256
+ n.start,
257
+ n.end,
258
+ n.width
259
+ ].every(Number.isFinite) || n.width < 0 || n.end <= n.start) throw new w("INVALID_SOURCE", "Collapsed border geometry must be finite and positive.");
260
+ let e = `${n.direction}:${n.position.toFixed(4)}`, r = t.get(e) ?? [];
261
+ r.push(n), t.set(e, r);
262
+ }
263
+ let n = [];
264
+ for (let e of t.values()) {
265
+ let t = /* @__PURE__ */ new Map();
266
+ for (let n of e) {
267
+ let e = t.get(n.start) ?? {
268
+ starts: [],
269
+ ends: []
270
+ };
271
+ e.starts.push(n), t.set(n.start, e);
272
+ let r = t.get(n.end) ?? {
273
+ starts: [],
274
+ ends: []
275
+ };
276
+ r.ends.push(n), t.set(n.end, r);
277
+ }
278
+ let r = [...t.keys()].sort((e, t) => e - t), i = /* @__PURE__ */ new Set();
279
+ for (let e = 0; e < r.length - 1; e += 1) {
280
+ let a = r[e], o = r[e + 1], s = t.get(a);
281
+ for (let e of s.ends) i.delete(e);
282
+ for (let e of s.starts) i.add(e);
283
+ if (o - a <= j) continue;
284
+ let c;
285
+ for (let e of i) (!c || M(e, c) > 0) && (c = e);
286
+ c?.style === "solid" && c.width > 0 && n.push({
287
+ ...c,
288
+ start: a,
289
+ end: o
290
+ });
291
+ }
292
+ }
293
+ return n;
294
+ }
295
+ function _e(e, t) {
296
+ let n = Math.max(e.x, t.x), r = Math.min(e.x + e.width, t.x + t.width), i = Math.max(e.y, t.y), a = Math.min(e.y + e.height, t.y + t.height);
297
+ return r <= n + j || a <= i + j ? [e] : [
298
+ {
299
+ x: e.x,
300
+ y: e.y,
301
+ width: e.width,
302
+ height: i - e.y
303
+ },
304
+ {
305
+ x: e.x,
306
+ y: a,
307
+ width: e.width,
308
+ height: e.y + e.height - a
309
+ },
310
+ {
311
+ x: e.x,
312
+ y: i,
313
+ width: n - e.x,
314
+ height: a - i
315
+ },
316
+ {
317
+ x: r,
318
+ y: i,
319
+ width: e.x + e.width - r,
320
+ height: a - i
321
+ }
322
+ ].filter((e) => e.width > j && e.height > j);
323
+ }
324
+ function ve(e) {
325
+ let t = [], n = /* @__PURE__ */ new Map();
326
+ for (let t of e) {
327
+ let e = `${t.direction}:${t.position.toFixed(4)}`, r = n.get(e) ?? [];
328
+ r.push(t), n.set(e, r);
329
+ }
330
+ for (let e of n.values()) e.sort((e, t) => e.start - t.start);
331
+ let r = /* @__PURE__ */ new Map(), i = (e) => {
332
+ let t = [];
333
+ for (let n = Math.floor(e.x / 64); n <= Math.floor((e.x + e.width) / 64); n += 1) for (let r = Math.floor(e.y / 64); r <= Math.floor((e.y + e.height) / 64); r += 1) t.push(`${n}:${r}`);
334
+ return t;
335
+ }, a = [...e].sort((e, t) => M(t, e));
336
+ for (let e of a) {
337
+ let a = (t) => {
338
+ let r = e.direction === "horizontal" ? "vertical" : "horizontal", i = n.get(`${r}:${t.toFixed(4)}`) ?? [], a = 0, o = i.length;
339
+ for (; a < o;) {
340
+ let t = a + o >>> 1;
341
+ i[t].start <= e.position + j ? a = t + 1 : o = t;
342
+ }
343
+ let s = 0;
344
+ for (let t = a - 1; t >= 0; --t) {
345
+ let n = i[t];
346
+ if (n.end < e.position - j) break;
347
+ s = Math.max(s, n.width / 2);
348
+ }
349
+ return s;
350
+ }, o = e.start - a(e.start), s = e.end + a(e.end), c = [e.direction === "horizontal" ? {
351
+ x: o,
352
+ y: e.position - e.width / 2,
353
+ width: s - o,
354
+ height: e.width
355
+ } : {
356
+ x: e.position - e.width / 2,
357
+ y: o,
358
+ width: e.width,
359
+ height: s - o
360
+ }], l = new Set(i(c[0]).flatMap((e) => r.get(e) ?? []));
361
+ for (let e of l) if (c = c.flatMap((t) => _e(t, e.bounds)), c.length === 0) break;
362
+ for (let n of c) {
363
+ let a = {
364
+ kind: "border",
365
+ bounds: n,
366
+ color: e.color,
367
+ width: e.width,
368
+ direction: e.direction
369
+ };
370
+ t.push(a);
371
+ for (let e of i(n)) {
372
+ let t = r.get(e) ?? [];
373
+ t.push(a), r.set(e, t);
374
+ }
375
+ }
376
+ }
377
+ return t;
378
+ }
379
+ function ye(e, t) {
380
+ if (![
381
+ "TABLE",
382
+ "THEAD",
383
+ "TBODY",
384
+ "TFOOT",
385
+ "TR",
386
+ "TD",
387
+ "TH",
388
+ "COL",
389
+ "COLGROUP"
390
+ ].includes(e.tagName)) return;
391
+ let n = e instanceof HTMLTableElement ? e : e.closest("table");
392
+ return n && (e === n ? t : getComputedStyle(n)).borderCollapse === "collapse" ? n : void 0;
393
+ }
394
+ function be(e, t) {
395
+ if (!{
396
+ TABLE: ["table", "inline-table"],
397
+ THEAD: ["table-header-group"],
398
+ TBODY: ["table-row-group"],
399
+ TFOOT: ["table-footer-group"],
400
+ TR: ["table-row"],
401
+ TD: ["table-cell"],
402
+ TH: ["table-cell"],
403
+ COL: ["table-column"],
404
+ COLGROUP: ["table-column-group"]
405
+ }[e.tagName]?.includes(t.display)) throw new w("UNSUPPORTED_CSS", "Collapsed table parts require their native table-cell, table-row and table-group display modes.");
406
+ for (let n of [
407
+ "Top",
408
+ "Right",
409
+ "Bottom",
410
+ "Left"
411
+ ]) {
412
+ let r = t[`border${n}Style`], i = Number.parseFloat(t[`border${n}Width`]);
413
+ if (![
414
+ "solid",
415
+ "none",
416
+ "hidden"
417
+ ].includes(r)) throw new w("UNSUPPORTED_CSS", "Collapsed tables support only solid, none and hidden border styles.");
418
+ if (![
419
+ "TABLE",
420
+ "TD",
421
+ "TH"
422
+ ].includes(e.tagName) && (r === "hidden" || i > 0)) throw new w("UNSUPPORTED_CSS", "Collapsed row, row-group, column and column-group border contributions are unsupported; put borders on cells or the table.");
423
+ }
424
+ }
425
+ function xe(e, t) {
426
+ if (e.parentElement?.closest("table") || e.querySelector("table") || e.caption || !["table", "inline-table"].includes(getComputedStyle(e).display)) throw new w("UNSUPPORTED_ELEMENT", "Native collapsed borders require a table without nested tables or captions.");
427
+ let n = [], r = Array.from(e.rows).flatMap((e) => Array.from(e.cells).filter((e) => {
428
+ let t = e.getBoundingClientRect();
429
+ return t.width > 0 && t.height > 0;
430
+ }));
431
+ if (r.length === 0) return {
432
+ operations: [],
433
+ repeatSafe: !0
434
+ };
435
+ let i = r.map((e) => e.getBoundingClientRect()), a = i.reduce((e, t) => ({
436
+ left: Math.min(e.left, t.left),
437
+ right: Math.max(e.right, t.right),
438
+ top: Math.min(e.top, t.top),
439
+ bottom: Math.max(e.bottom, t.bottom)
440
+ }), {
441
+ left: Infinity,
442
+ right: -Infinity,
443
+ top: Infinity,
444
+ bottom: -Infinity
445
+ }), o = (r, i) => {
446
+ let a = getComputedStyle(r), o = r === e ? "table" : "cell";
447
+ for (let e of [
448
+ "Top",
449
+ "Right",
450
+ "Bottom",
451
+ "Left"
452
+ ]) {
453
+ let r = e === "Top" || e === "Bottom";
454
+ n.push({
455
+ direction: r ? "horizontal" : "vertical",
456
+ position: (e === "Top" ? i.top : e === "Bottom" ? i.bottom : e === "Left" ? i.left : i.right) - (r ? t.top : t.left),
457
+ start: (r ? i.left : i.top) - (r ? t.left : t.top),
458
+ end: (r ? i.right : i.bottom) - (r ? t.left : t.top),
459
+ width: Number.parseFloat(a[`border${e}Width`]),
460
+ style: a[`border${e}Style`],
461
+ color: a[`border${e}Color`],
462
+ owner: o,
463
+ row: i.top,
464
+ column: i.left
465
+ });
466
+ }
467
+ };
468
+ o(e, a), r.forEach((e, t) => o(e, i[t]));
469
+ let s = ge(n), c = ve(s), l = e.tHead?.getBoundingClientRect();
470
+ if (!l || l.height <= 0) return {
471
+ operations: c,
472
+ repeatSafe: !0
473
+ };
474
+ let u = l.bottom - t.top, d = Array.from(e.tBodies).flatMap((e) => Array.from(e.rows)), f = d.at(-1)?.getBoundingClientRect().bottom ?? l.bottom, p = [.../* @__PURE__ */ new Set([u, ...d.map((e) => e.getBoundingClientRect().top - t.top).filter((e) => e < f - t.top - j)])], m = /* @__PURE__ */ new Map();
475
+ for (let e of s) {
476
+ if (e.direction !== "horizontal") continue;
477
+ let t = e.position.toFixed(4), n = m.get(t) ?? [];
478
+ n.push(e), m.set(t, n);
479
+ }
480
+ let h = (e) => JSON.stringify((m.get(e.toFixed(4)) ?? []).sort((e, t) => e.start - t.start).map(({ start: e, end: t, width: n, color: r }) => ({
481
+ start: e,
482
+ end: t,
483
+ width: n,
484
+ color: r
485
+ }))), g = h(u), _ = p.every((e) => h(e) === g), v = c.filter((e) => e.bounds.y < u + j && e.bounds.y + e.bounds.height > l.top - t.top - j).reduce((e, { bounds: t }) => ({
486
+ left: Math.min(e.left, t.x),
487
+ right: Math.max(e.right, t.x + t.width),
488
+ top: Math.min(e.top, t.y)
489
+ }), {
490
+ left: l.left - t.left,
491
+ right: l.right - t.left,
492
+ top: l.top - t.top
493
+ });
494
+ return {
495
+ operations: c,
496
+ repeatSafe: _,
497
+ headerBounds: {
498
+ x: v.left,
499
+ y: v.top,
500
+ width: v.right - v.left,
501
+ height: u - v.top
502
+ }
503
+ };
224
504
  }
225
505
  // src/layout.ts
226
- var le = new Intl.Segmenter("en", { granularity: "grapheme" });
227
- function P(e, t) {
506
+ var Se = new Intl.Segmenter("en", { granularity: "grapheme" });
507
+ function N(e, t) {
228
508
  if (![
229
509
  e.x,
230
510
  e.y,
231
511
  e.width,
232
512
  e.height
233
- ].every(Number.isFinite)) throw new b("INVALID_SOURCE", "Browser geometry must contain only finite CSS pixel values.");
513
+ ].every(Number.isFinite)) throw new w("INVALID_SOURCE", "Browser geometry must contain only finite CSS pixel values.");
234
514
  return {
235
515
  x: e.left - t.left,
236
516
  y: e.top - t.top,
@@ -238,12 +518,12 @@ function P(e, t) {
238
518
  height: e.height
239
519
  };
240
520
  }
241
- function F(e) {
521
+ function P(e) {
242
522
  let t = e.getRootNode();
243
523
  return e.parentElement ?? (t instanceof ShadowRoot && t.host instanceof HTMLElement ? t.host : null);
244
524
  }
245
- function I(e, t, n = !1) {
246
- let r = e instanceof HTMLImageElement, i = [
525
+ function F(e, t, n = !1, r = !1) {
526
+ let i = e instanceof HTMLImageElement, a = [
247
527
  t.position !== "static" && `position: ${t.position}`,
248
528
  t.zIndex !== "auto" && `z-index: ${t.zIndex}`,
249
529
  t.transform !== "none" && "transform",
@@ -256,8 +536,8 @@ function I(e, t, n = !1) {
256
536
  t.backgroundImage !== "none" && !n && "background-image",
257
537
  t.boxShadow !== "none" && !n && "box-shadow",
258
538
  t.borderRadius !== "0px" && !n && "border-radius",
259
- t.overflowX !== "visible" && !(r && t.overflowX === "clip") && "overflow-x",
260
- t.overflowY !== "visible" && !(r && t.overflowY === "clip") && "overflow-y",
539
+ t.overflowX !== "visible" && !(i && t.overflowX === "clip") && "overflow-x",
540
+ t.overflowY !== "visible" && !(i && t.overflowY === "clip") && "overflow-y",
261
541
  t.cssFloat !== "none" && "float",
262
542
  t.order !== "0" && "order",
263
543
  t.direction !== "ltr" && "direction",
@@ -282,10 +562,10 @@ function I(e, t, n = !1) {
282
562
  ].filter(Boolean);
283
563
  for (let t of ["::before", "::after"]) {
284
564
  let n = getComputedStyle(e, t).content;
285
- n !== "none" && n !== "normal" && i.push(`generated content ${t}`);
565
+ n !== "none" && n !== "normal" && a.push(`generated content ${t}`);
286
566
  }
287
- if (i.length > 0) throw new b("UNSUPPORTED_CSS", `Unsupported CSS on ${e.tagName}: ${i.join(", ")}. Use a bounded data-raster subtree when safe.`);
288
- if (e instanceof HTMLTableElement && t.borderCollapse !== "separate") throw new b("UNSUPPORTED_CSS", "Native table snapshots require separate borders; collapsed borders are unsupported.");
567
+ if (a.length > 0) throw new w("UNSUPPORTED_CSS", `Unsupported CSS on ${e.tagName}: ${a.join(", ")}. Use a bounded data-raster subtree when safe.`);
568
+ if (e instanceof HTMLTableElement && !["separate", "collapse"].includes(t.borderCollapse)) throw new w("UNSUPPORTED_CSS", "Native table snapshots require separate or collapsed borders.");
289
569
  if ([
290
570
  "THEAD",
291
571
  "TBODY",
@@ -293,17 +573,17 @@ function I(e, t, n = !1) {
293
573
  "TR",
294
574
  "COL",
295
575
  "COLGROUP"
296
- ].includes(e.tagName) && t.backgroundColor !== "rgba(0, 0, 0, 0)") throw new b("UNSUPPORTED_CSS", "Native table snapshots require transparent row-group, row, and column backgrounds.");
297
- let a = [
576
+ ].includes(e.tagName) && t.backgroundColor !== "rgba(0, 0, 0, 0)") throw new w("UNSUPPORTED_CSS", "Native table snapshots require transparent row-group, row, and column backgrounds.");
577
+ let o = [
298
578
  "Top",
299
579
  "Right",
300
580
  "Bottom",
301
581
  "Left"
302
- ], o = a.map((e) => t[`border${e}Width`]), s = a.map((e) => t[`border${e}Color`]), c = a.map((e) => t[`border${e}Style`]);
303
- if (o.some((e) => e !== o[0]) || o[0] !== "0px" && (s.some((e) => e !== s[0]) || c.some((e) => e !== "solid"))) throw new b("UNSUPPORTED_CSS", "Unsupported CSS: only uniform solid borders are supported.");
304
- if (!n && (t.backgroundColor !== "rgba(0, 0, 0, 0)" || o[0] !== "0px") && Array.from(e.getClientRects()).filter((e) => e.width > 0 && e.height > 0).length > 1) throw new b("UNSUPPORTED_CSS", "Fragmented inline backgrounds and borders require an explicit containing raster region.");
582
+ ], s = o.map((e) => t[`border${e}Width`]), c = o.map((e) => t[`border${e}Color`]), l = o.map((e) => t[`border${e}Style`]);
583
+ if (r && be(e, t), !r && (s.some((e) => e !== s[0]) || s[0] !== "0px" && (c.some((e) => e !== c[0]) || l.some((e) => e !== "solid")))) throw new w("UNSUPPORTED_CSS", "Unsupported CSS: only uniform solid borders are supported.");
584
+ if (!n && (t.backgroundColor !== "rgba(0, 0, 0, 0)" || s[0] !== "0px") && Array.from(e.getClientRects()).filter((e) => e.width > 0 && e.height > 0).length > 1) throw new w("UNSUPPORTED_CSS", "Fragmented inline backgrounds and borders require an explicit containing raster region.");
305
585
  }
306
- function L(e, t) {
586
+ function I(e, t) {
307
587
  if (!(e instanceof HTMLImageElement) && ![
308
588
  "block",
309
589
  "flow-root",
@@ -323,34 +603,34 @@ function L(e, t) {
323
603
  "table-cell",
324
604
  "table-column-group",
325
605
  "table-column"
326
- ].includes(t.display)) throw new b("UNSUPPORTED_CSS", `Unsupported CSS display: ${t.display}.`);
606
+ ].includes(t.display)) throw new w("UNSUPPORTED_CSS", `Unsupported CSS display: ${t.display}.`);
327
607
  }
328
- function R(e, t, n, r, i) {
608
+ function Ce(e, t, n, r, i) {
329
609
  let a = e.parentElement;
330
610
  if (!a || !e.data.replace(/[\t\n\r\f ]+/g, "")) return [];
331
611
  let o = getComputedStyle(a), s = o.fontFamily.split(",")[0]?.trim().replace(/^['"]|['"]$/g, ""), c = Number(o.fontWeight), l = n.find((e) => e.family.toLowerCase() === s?.toLowerCase() && e.weight === c && e.style === o.fontStyle);
332
- if (!l) throw new b("MISSING_FONT", `No exact registered font face for ${o.fontFamily} ${o.fontWeight} ${o.fontStyle}.`);
333
- if (o.direction !== "ltr" || o.writingMode !== "horizontal-tb" || o.textTransform !== "none" || o.whiteSpace !== "normal" || e.data.includes("­") || o.hyphens === "auto" || o.letterSpacing !== "normal" && o.letterSpacing !== "0px" || o.wordSpacing !== "normal" && o.wordSpacing !== "0px" || o.fontVariantCaps !== "normal" || o.fontVariantAlternates !== "normal" || o.fontVariantLigatures !== "normal" || o.fontVariantNumeric !== "normal" || o.fontVariantEastAsian !== "normal" || o.fontVariantPosition !== "normal" || o.fontFeatureSettings !== "normal" || o.fontVariationSettings !== "normal" || o.fontStretch !== "100%" && o.fontStretch !== "normal" || o.fontSizeAdjust !== "none" && o.fontSizeAdjust !== void 0 || !["auto", "geometricPrecision"].includes(o.textRendering)) throw new b("UNSUPPORTED_CSS", "Unsupported CSS: text requires horizontal LTR layout with normal whitespace, spacing, font variants and OpenType features, without automatic hyphenation or soft hyphens.");
612
+ if (!l) throw new w("MISSING_FONT", `No exact registered font face for ${o.fontFamily} ${o.fontWeight} ${o.fontStyle}.`);
613
+ if (o.direction !== "ltr" || o.writingMode !== "horizontal-tb" || o.textTransform !== "none" || o.whiteSpace !== "normal" || e.data.includes("­") || o.hyphens === "auto" || o.letterSpacing !== "normal" && o.letterSpacing !== "0px" || o.wordSpacing !== "normal" && o.wordSpacing !== "0px" || o.fontVariantCaps !== "normal" || o.fontVariantAlternates !== "normal" || o.fontVariantLigatures !== "normal" || o.fontVariantNumeric !== "normal" || o.fontVariantEastAsian !== "normal" || o.fontVariantPosition !== "normal" || o.fontFeatureSettings !== "normal" || o.fontVariationSettings !== "normal" || o.fontStretch !== "100%" && o.fontStretch !== "normal" || o.fontSizeAdjust !== "none" && o.fontSizeAdjust !== void 0 || !["auto", "geometricPrecision"].includes(o.textRendering)) throw new w("UNSUPPORTED_CSS", "Unsupported CSS: text requires horizontal LTR layout with normal whitespace, spacing, font variants and OpenType features, without automatic hyphenation or soft hyphens.");
334
614
  let u = Number.parseFloat(o.fontSize), d = `${l.style} ${l.weight} ${u}px "${l.family}"`, f = r.fonts.get(d);
335
615
  if (!f) {
336
616
  if (!Array.from(document.fonts).some((e) => {
337
617
  let t = e.weight === "normal" ? 400 : e.weight === "bold" ? 700 : Number(e.weight);
338
618
  return e.family.replace(/^['"]|['"]$/g, "").toLowerCase() === l.family.toLowerCase() && t === l.weight && e.style === l.style && e.status === "loaded";
339
- }) || !document.fonts.check(d, e.data)) throw new b("MISSING_FONT", `Browser font is not loaded: ${d}.`);
619
+ }) || !document.fonts.check(d, e.data)) throw new w("MISSING_FONT", `Browser font is not loaded: ${d}.`);
340
620
  r.context.font = d;
341
621
  let t = r.context.measureText(e.data), n = t.fontBoundingBoxAscent, i = t.fontBoundingBoxDescent;
342
- if (!Number.isFinite(n) || !Number.isFinite(i)) throw new b("UNSUPPORTED_CSS", "Font bounding metrics are unavailable in this browser.");
622
+ if (!Number.isFinite(n) || !Number.isFinite(i)) throw new w("UNSUPPORTED_CSS", "Font bounding metrics are unavailable in this browser.");
343
623
  f = {
344
624
  ascent: n,
345
625
  descent: i
346
626
  }, r.fonts.set(d, f);
347
627
  }
348
- let { ascent: p, descent: m } = f, h = le.segment(e.data), g = document.createRange(), _ = [];
628
+ let { ascent: p, descent: m } = f, h = Se.segment(e.data), g = document.createRange(), _ = [];
349
629
  for (let t of h) {
350
630
  g.setStart(e, t.index), g.setEnd(e, t.index + t.segment.length);
351
631
  let n = Array.from(g.getClientRects()), r = n.find((e) => e.width > 0 && e.height > 0);
352
632
  if (!r) {
353
- if (/[^\t\n\r\f ]/.test(t.segment) && n.some((e) => e.height > 0 && e.width === 0)) throw new b("UNSUPPORTED_CSS", "Text shaping crosses an inline boundary or uses an unsupported zero-width glyph. Keep each shaped word in one text node.");
633
+ if (/[^\t\n\r\f ]/.test(t.segment) && n.some((e) => e.height > 0 && e.width === 0)) throw new w("UNSUPPORTED_CSS", "Text shaping crosses an inline boundary or uses an unsupported zero-width glyph. Keep each shaped word in one text node.");
354
634
  continue;
355
635
  }
356
636
  let i = _.at(-1);
@@ -366,14 +646,14 @@ function R(e, t, n, r, i) {
366
646
  for (; a > r && /[\t\n\r\f ]/.test(e.data[a - 1]);) --a;
367
647
  if (r === a) return [];
368
648
  g.setStart(e, r), g.setEnd(e, a);
369
- let s = e.data.slice(r, a), c = s.replace(/[\t\n\r\f ]+/g, " "), d = P(g.getBoundingClientRect(), t);
649
+ let s = e.data.slice(r, a), c = s.replace(/[\t\n\r\f ]+/g, " "), d = N(g.getBoundingClientRect(), t);
370
650
  return [{
371
651
  kind: "text",
372
652
  bounds: d,
373
653
  text: c,
374
654
  runs: Array.from(s.matchAll(/[^\t\n\r\f ]+/g), (n) => {
375
655
  g.setStart(e, r + n.index), g.setEnd(e, r + n.index + n[0].length);
376
- let i = P(g.getBoundingClientRect(), t);
656
+ let i = N(g.getBoundingClientRect(), t);
377
657
  return {
378
658
  text: n[0],
379
659
  x: i.x,
@@ -391,147 +671,210 @@ function R(e, t, n, r, i) {
391
671
  }];
392
672
  });
393
673
  }
394
- function z(e, t, n) {
395
- if (n !== "auto" && n !== "strict") throw new b("INVALID_OPTIONS", "Raster fallback must be auto or strict.");
396
- if (!(e instanceof HTMLElement) || e.ownerDocument !== document || !e.isConnected || t.length === 0) throw new b("INVALID_SOURCE", "A connected source root and registered fonts are required.");
397
- if (new Set(t.map((e) => `${e.family.toLowerCase()}:${e.weight}:${e.style}`)).size !== t.length) throw new b("INVALID_OPTIONS", "Registered fonts require unique family/weight/style descriptors.");
398
- for (let t = F(e); t; t = F(t)) {
674
+ function we(e, t, n, r = !1) {
675
+ if (n !== "auto" && n !== "strict") throw new w("INVALID_OPTIONS", "Raster fallback must be auto or strict.");
676
+ if (typeof r != "boolean") throw new w("INVALID_OPTIONS", "Table header repetition must be a boolean.");
677
+ if (!(e instanceof HTMLElement) || e.ownerDocument !== document || !e.isConnected || t.length === 0) throw new w("INVALID_SOURCE", "A connected source root and registered fonts are required.");
678
+ if (new Set(t.map((e) => `${e.family.toLowerCase()}:${e.weight}:${e.style}`)).size !== t.length) throw new w("INVALID_OPTIONS", "Registered fonts require unique family/weight/style descriptors.");
679
+ for (let t = P(e); t; t = P(t)) {
399
680
  let e = getComputedStyle(t);
400
- if (e.transform !== "none" || e.filter !== "none" || e.opacity !== "1" || e.clipPath !== "none" || e.maskImage !== "none" || e.mixBlendMode !== "normal" || e.zoom !== "1" || e.overflowX !== "visible" || e.overflowY !== "visible") throw new b("UNSUPPORTED_CSS", "Unsupported CSS: source ancestors must not transform, clip, blend, or fade the source.");
681
+ if (e.transform !== "none" || e.filter !== "none" || e.opacity !== "1" || e.clipPath !== "none" || e.maskImage !== "none" || e.mixBlendMode !== "normal" || e.zoom !== "1" || e.overflowX !== "visible" || e.overflowY !== "visible") throw new w("UNSUPPORTED_CSS", "Unsupported CSS: source ancestors must not transform, clip, blend, or fade the source.");
682
+ }
683
+ }
684
+ function Te(e, t, n, r, i, a) {
685
+ let o = Array.from(e.querySelectorAll("table"));
686
+ e instanceof HTMLTableElement && o.unshift(e);
687
+ let s = [];
688
+ for (let c of o) {
689
+ let o = c.tHead;
690
+ if (!o || getComputedStyle(o).display === "none") continue;
691
+ let l = a.get(c), u = l?.headerBounds ?? N(o.getBoundingClientRect(), t);
692
+ if (u.width <= 0 || u.height <= 0) continue;
693
+ let d = Array.from(c.tBodies).flatMap((e) => Array.from(e.rows).filter((e) => e.getBoundingClientRect().height > 0));
694
+ if (d.length === 0) continue;
695
+ if (l && !l.repeatSafe) throw new w("UNSUPPORTED_CSS", "Repeated collapsed headers require matching horizontal border winners at the header/body boundary and all body row boundaries.");
696
+ if (c.parentElement?.closest("table") || c.querySelector("table") || Array.from(c.children).filter((e) => e.tagName === "THEAD").length !== 1 || getComputedStyle(c).display !== "table" || getComputedStyle(o).display !== "table-header-group") throw new w("UNSUPPORTED_ELEMENT", "Repeated headers require a standalone native table with one THEAD and no nested tables.");
697
+ if (Array.from(o.rows).some((e) => Array.from(e.cells).some((e) => e.rowSpan !== 1)) || n.some((e) => e.kind === "raster" && (e.element.contains(o) || o.contains(e.element)))) throw new w("UNSUPPORTED_ELEMENT", "Repeated table headers do not support header rowspans or rasterized headers and ancestors.");
698
+ let f = N(d[0].getBoundingClientRect(), t).y, p = N(d.at(-1).getBoundingClientRect(), t), m = p.y + p.height, h = r.get(o);
699
+ if (!h || f < u.y + u.height - .1 || m <= f || h.some((e) => e.kind !== "border" && (e.bounds.y < u.y - .1 || e.bounds.y + e.bounds.height > u.y + u.height + .1 || e.bounds.x < u.x - .1 || e.bounds.x + e.bounds.width > u.x + u.width + .1)) || d.some((e) => Array.from(e.cells).some((e) => {
700
+ let n = N(e.getBoundingClientRect(), t);
701
+ return n.y < f - .1 || n.y + n.height > m + .1;
702
+ }))) throw new w("UNSUPPORTED_CSS", "Repeated table headers require a separate, ordered header band without overflowing paint or cells spanning header/body boundaries.");
703
+ if (s.some((e) => Math.min(e.bodyEnd, m) - Math.max(e.bounds.y, u.y) > .01)) throw new w("UNSUPPORTED_CSS", "Repeated headers do not support tables whose vertical continuation intervals overlap.");
704
+ let g = new Set(h);
705
+ for (let t = o.parentElement; t; t = t.parentElement) {
706
+ let n = i.get(t);
707
+ if (n && g.add(n), t === e) break;
708
+ }
709
+ s.push({
710
+ bounds: u,
711
+ bodyStart: f,
712
+ bodyEnd: m,
713
+ operations: n.filter((e) => g.has(e))
714
+ });
401
715
  }
716
+ return s;
402
717
  }
403
- function B(e, t, n = {}) {
404
- let r = n.fallback ?? "auto";
405
- z(e, t, r);
406
- let i = e.getBoundingClientRect();
407
- if (i.width <= 0 || i.height <= 0) throw new b("INVALID_SOURCE", "The source root must have positive visible dimensions.");
408
- let a = document.createElement("canvas").getContext("2d");
409
- if (!a) throw new b("INVALID_SOURCE", "Canvas font measurement is unavailable.");
410
- let o = {
411
- context: a,
718
+ function Ee(e, t, n = {}) {
719
+ let r = n.fallback ?? "auto", i = n.repeatTableHeaders ?? !1;
720
+ we(e, t, r, i);
721
+ let a = e.getBoundingClientRect();
722
+ if (a.width <= 0 || a.height <= 0) throw new w("INVALID_SOURCE", "The source root must have positive visible dimensions.");
723
+ let o = document.createElement("canvas").getContext("2d");
724
+ if (!o) throw new w("INVALID_SOURCE", "Canvas font measurement is unavailable.");
725
+ let s = {
726
+ context: o,
412
727
  fonts: /* @__PURE__ */ new Map()
413
- }, s = [], c = [], l = [{
728
+ }, c = [], l = /* @__PURE__ */ new Map(), u = /* @__PURE__ */ new Map(), d = /* @__PURE__ */ new Map(), f = [], p = /* @__PURE__ */ new Map(), m = [{
414
729
  code: "TEXT_METRICS_APPROXIMATED",
415
730
  message: "Baselines are estimated from Canvas font metrics; browser and PDF shaping engines may still differ for unsupported scripts or font features."
416
731
  }];
417
- function u(e, n, a = !1) {
418
- let d = getComputedStyle(e);
419
- if (d.display === "none") return;
420
- if (d.visibility !== "visible") {
421
- if (!n || a) throw new b("UNSUPPORTED_CSS", "Unsupported CSS: visibility overrides are not supported.");
422
- l.push({
732
+ function h(e, n, o = !1) {
733
+ let g = c.length, _ = getComputedStyle(e);
734
+ if (_.display === "none") return;
735
+ let v = ye(e, _);
736
+ if (_.visibility !== "visible") {
737
+ if (!n || o) throw new w("UNSUPPORTED_CSS", "Unsupported CSS: visibility overrides are not supported.");
738
+ m.push({
423
739
  code: "RASTER_TEXT_OMITTED",
424
740
  message: `Raster text extraction omitted for visibility-controlled subtree ${e.tagName}.`
425
741
  });
426
742
  return;
427
743
  }
428
- if ((!n || a) && !(/* @__PURE__ */ "MAIN.SECTION.ARTICLE.DIV.SPAN.P.H1.H2.H3.H4.H5.H6.STRONG.EM.B.I.IMG.FONT.CENTER.BR.HR.TABLE.THEAD.TBODY.TFOOT.TR.TH.TD.CAPTION.COLGROUP.COL".split(".")).includes(e.tagName)) throw new b("UNSUPPORTED_ELEMENT", `Native snapshots do not support ${e.tagName}.`);
429
- let f = !n && e.hasAttribute("data-raster"), p = !n && !f && r === "auto" ? N(d) : void 0, m = a || p !== void 0;
430
- if (m && (a && N(d), I(e, d, !0), L(e, d)), n && (d.transform !== "none" || d.writingMode !== "horizontal-tb" || d.direction !== "ltr" || d.opacity === "0" || d.clipPath !== "none" || d.maskImage !== "none" || d.overflowX !== "visible" || d.overflowY !== "visible")) {
431
- l.push({
744
+ if ((!n || o) && !(/* @__PURE__ */ "MAIN.SECTION.ARTICLE.DIV.SPAN.P.H1.H2.H3.H4.H5.H6.STRONG.EM.B.I.IMG.FONT.CENTER.BR.HR.TABLE.THEAD.TBODY.TFOOT.TR.TH.TD.CAPTION.COLGROUP.COL".split(".")).includes(e.tagName)) throw new w("UNSUPPORTED_ELEMENT", `Native snapshots do not support ${e.tagName}.`);
745
+ let y = !n && e.hasAttribute("data-raster"), b = !n && !y && r === "auto" ? me(_) : void 0, x = o || b !== void 0;
746
+ if (x && (o && me(_), F(e, _, !0, v !== void 0), I(e, _)), n && (_.transform !== "none" || _.writingMode !== "horizontal-tb" || _.direction !== "ltr" || _.opacity === "0" || _.clipPath !== "none" || _.maskImage !== "none" || _.overflowX !== "visible" || _.overflowY !== "visible")) {
747
+ m.push({
432
748
  code: "RASTER_TEXT_OMITTED",
433
749
  message: `Raster text extraction omitted for transformed, clipped, hidden, masked, or non-LTR subtree ${e.tagName}.`
434
750
  });
435
751
  return;
436
752
  }
437
- let h = P(e.getBoundingClientRect(), i);
438
- if (!n && (h.x < -.1 || h.y < -.1 || h.x + h.width > i.width + .1 || h.y + h.height > i.height + .1)) throw new b("SOURCE_OVERFLOW", "Source content must fit within the root border box.");
439
- let g = f || p !== void 0;
440
- if (g) {
441
- if (d.position !== "static" || d.transform !== "none" || d.mixBlendMode !== "normal") throw new b("UNSUPPORTED_CSS", "A raster boundary must be static, untransformed, and independently compositable.");
442
- if (p !== void 0 && ce(e, d), s.push({
753
+ let S = N(e.getBoundingClientRect(), a);
754
+ if (!n && (S.x < -.1 || S.y < -.1 || S.x + S.width > a.width + .1 || S.y + S.height > a.height + .1)) throw new w("SOURCE_OVERFLOW", "Source content must fit within the root border box.");
755
+ let C = y || b !== void 0;
756
+ if (C && v && !(e instanceof HTMLTableElement)) throw new w("UNSUPPORTED_CSS", "Collapsed table cells and internal groups cannot be independently rasterized.");
757
+ if (C) {
758
+ if (_.position !== "static" || _.transform !== "none" || _.mixBlendMode !== "normal") throw new w("UNSUPPORTED_CSS", "A raster boundary must be static, untransformed, and independently compositable.");
759
+ if (b !== void 0 && he(e, _), c.push({
443
760
  kind: "raster",
444
- bounds: h,
761
+ bounds: S,
445
762
  element: e
446
- }), l.push({
763
+ }), m.push({
447
764
  code: "RASTER_FALLBACK",
448
- message: `${p === void 0 ? "An explicit raster subtree" : `Automatic raster fallback on ${e.tagName} for ${p}`} uses invisible PDF text for extraction where supported; generated content and clipped or transformed text are not represented.`
449
- }), d.overflowX !== "visible" || d.overflowY !== "visible" || d.clipPath !== "none" || d.maskImage !== "none" || d.opacity === "0") {
450
- l.push({
765
+ message: `${b === void 0 ? "An explicit raster subtree" : `Automatic raster fallback on ${e.tagName} for ${b}`} uses invisible PDF text for extraction where supported; generated content and clipped or transformed text are not represented.`
766
+ }), _.overflowX !== "visible" || _.overflowY !== "visible" || _.clipPath !== "none" || _.maskImage !== "none" || _.opacity === "0") {
767
+ m.push({
451
768
  code: "RASTER_TEXT_OMITTED",
452
769
  message: `Raster text extraction omitted for clipped, masked, or hidden boundary ${e.tagName}.`
453
770
  });
454
771
  return;
455
772
  }
456
773
  } else if (!n) {
457
- if (I(e, d), L(e, d), (e instanceof HTMLTableRowElement || e instanceof HTMLTableCellElement && e.rowSpan !== 1) && c.push(h), e instanceof HTMLImageElement) {
458
- if (d.objectFit !== "fill" || d.borderTopWidth !== "0px") throw new b("UNSUPPORTED_CSS", "Image boxes require object-fit: fill without borders.");
774
+ if (F(e, _, !1, v !== void 0), I(e, _), e instanceof HTMLTableRowElement || e instanceof HTMLTableCellElement && e.rowSpan !== 1) {
775
+ let t = { ...S };
776
+ f.push(t), e instanceof HTMLTableRowElement && p.set(e, t);
777
+ }
778
+ if (e instanceof HTMLImageElement) {
779
+ if (_.objectFit !== "fill" || _.borderTopWidth !== "0px") throw new w("UNSUPPORTED_CSS", "Image boxes require object-fit: fill without borders.");
459
780
  if ([
460
781
  "paddingTop",
461
782
  "paddingRight",
462
783
  "paddingBottom",
463
784
  "paddingLeft"
464
- ].some((e) => Number.parseFloat(d.getPropertyValue(e.replace(/[A-Z]/g, (e) => `-${e.toLowerCase()}`))) !== 0) || d.backgroundColor !== "rgba(0, 0, 0, 0)") throw new b("UNSUPPORTED_CSS", "Image boxes require zero padding and a transparent background.");
465
- s.push({
785
+ ].some((e) => Number.parseFloat(_.getPropertyValue(e.replace(/[A-Z]/g, (e) => `-${e.toLowerCase()}`))) !== 0) || _.backgroundColor !== "rgba(0, 0, 0, 0)") throw new w("UNSUPPORTED_CSS", "Image boxes require zero padding and a transparent background.");
786
+ c.push({
466
787
  kind: "image",
467
- bounds: h,
788
+ bounds: S,
468
789
  element: e
469
790
  });
470
- } else [
791
+ } else if (![
471
792
  "BR",
472
793
  "COL",
473
794
  "COLGROUP"
474
- ].includes(e.tagName) || s.push({
475
- kind: "box",
476
- bounds: h,
477
- background: d.backgroundColor,
478
- borderColor: d.borderTopColor,
479
- borderWidth: Number.parseFloat(d.borderTopWidth)
480
- });
795
+ ].includes(e.tagName)) {
796
+ let t = {
797
+ kind: "box",
798
+ bounds: S,
799
+ background: _.backgroundColor,
800
+ borderColor: _.borderTopColor,
801
+ borderWidth: v ? 0 : Number.parseFloat(_.borderTopWidth)
802
+ };
803
+ c.push(t), i && u.set(e, t);
804
+ }
481
805
  }
482
- let _ = n || g;
483
- if (!_ || m) {
484
- let t = Array.from(e.children).filter((e) => e instanceof HTMLElement && getComputedStyle(e).display !== "none" && getComputedStyle(e).visibility !== "hidden").map((e) => e.getBoundingClientRect());
806
+ let T = n || C;
807
+ if (!T || x) {
808
+ let t = Array.from(e.children).filter((e) => e instanceof HTMLElement && !(v && ["COL", "COLGROUP"].includes(e.tagName)) && getComputedStyle(e).display !== "none" && getComputedStyle(e).visibility !== "hidden").map((e) => e.getBoundingClientRect());
485
809
  for (let e = 0; e < t.length; e += 1) for (let n = e + 1; n < t.length; n += 1) {
486
810
  let r = t[e], i = t[n];
487
- if (Math.min(r.right, i.right) - Math.max(r.left, i.left) > .1 && Math.min(r.bottom, i.bottom) - Math.max(r.top, i.top) > .1) throw new b("UNSUPPORTED_CSS", "Overlapping sibling boxes are unsupported; isolate them in one raster subtree.");
811
+ if (Math.min(r.right, i.right) - Math.max(r.left, i.left) > .1 && Math.min(r.bottom, i.bottom) - Math.max(r.top, i.top) > .1) throw new w("UNSUPPORTED_CSS", "Overlapping sibling boxes are unsupported; isolate them in one raster subtree.");
488
812
  }
489
813
  }
490
- for (let n of e.childNodes) if (n instanceof Text) s.push(...R(n, i, t, o, _));
491
- else if (n instanceof HTMLElement) u(n, _, m);
814
+ for (let n of e.childNodes) if (n instanceof Text) c.push(...Ce(n, a, t, s, T));
815
+ else if (n instanceof HTMLElement) h(n, T, x);
492
816
  else if (n instanceof Element) {
493
- if (_) l.push({
817
+ if (T) m.push({
494
818
  code: "RASTER_TEXT_OMITTED",
495
819
  message: `Raster text extraction omitted for ${n.tagName}.`
496
820
  });
497
- else throw new b("UNSUPPORTED_ELEMENT", `Cannot snapshot ${n.tagName}; use a raster subtree.`);
821
+ else throw new w("UNSUPPORTED_ELEMENT", `Cannot snapshot ${n.tagName}; use a raster subtree.`);
822
+ }
823
+ if (i && e instanceof HTMLTableSectionElement && e.tagName === "THEAD" && l.set(e, c.slice(g)), !T && e instanceof HTMLTableElement && v) {
824
+ let t = xe(e, a);
825
+ d.set(e, t);
826
+ for (let e of t.operations) c.push(e);
827
+ let n = Array.from(e.rows).filter((e) => p.has(e)), r = n[0] ? p.get(n[0]) : void 0, o = n.at(-1) ? p.get(n.at(-1)) : void 0, s = t.operations.reduce((e, { bounds: t }) => ({
828
+ top: Math.min(e.top, t.y),
829
+ bottom: Math.max(e.bottom, t.y + t.height)
830
+ }), {
831
+ top: r?.y ?? 0,
832
+ bottom: o ? o.y + o.height : 0
833
+ });
834
+ r && s.top < r.y && (r.height += r.y - s.top, r.y = s.top), o && (o.height = Math.max(o.height, s.bottom - o.y));
835
+ let u = e.tHead;
836
+ if (i && u && t.headerBounds) {
837
+ let e = t.headerBounds, n = t.operations.filter((t) => t.bounds.y < e.y + e.height + .001 && t.bounds.y + t.bounds.height > e.y - .001);
838
+ l.set(u, [...l.get(u) ?? [], ...n]);
839
+ }
498
840
  }
499
841
  }
500
- u(e, !1);
501
- let d = [];
502
- for (let e of s) {
842
+ h(e, !1);
843
+ let g = [];
844
+ for (let e of c) {
503
845
  if (e.kind === "text") {
504
- e.invisible || d.push(e);
846
+ e.invisible || g.push(e);
505
847
  continue;
506
848
  }
507
849
  if (e.kind === "box" && (e.background.startsWith("rgba(") ? Number(e.background.slice(e.background.lastIndexOf(",") + 1, -1)) : 1) === 0 && e.borderWidth === 0) continue;
508
850
  let t = e.bounds;
509
- if (d.some((e) => Math.min(e.bounds.x + e.bounds.width, t.x + t.width) - Math.max(e.bounds.x, t.x) > .1 && Math.min(e.bounds.y + e.bounds.height, t.y + t.height) - Math.max(e.bounds.y, t.y) > .1)) throw new b("UNSUPPORTED_CSS", "Text overlaps later visible paint whose CSS paint phases cannot be represented safely; use an explicit containing raster region.");
851
+ if (g.some((e) => Math.min(e.bounds.x + e.bounds.width, t.x + t.width) - Math.max(e.bounds.x, t.x) > .1 && Math.min(e.bounds.y + e.bounds.height, t.y + t.height) - Math.max(e.bounds.y, t.y) > .1)) throw new w("UNSUPPORTED_CSS", "Text overlaps later visible paint whose CSS paint phases cannot be represented safely; use an explicit containing raster region.");
510
852
  }
511
- if (s.some((e) => {
853
+ if (c.some((e) => {
512
854
  if (e.kind === "text" && e.invisible) return !1;
513
855
  let t = e.bounds;
514
- return t.x < -.1 || t.y < -.1 || t.x + t.width > i.width + .1 || t.y + t.height > i.height + .1;
515
- })) throw new b("SOURCE_OVERFLOW", "Visible source text or paint overflows the root border box.");
856
+ return t.x < -.1 || t.y < -.1 || t.x + t.width > a.width + .1 || t.y + t.height > a.height + .1;
857
+ })) throw new w("SOURCE_OVERFLOW", "Visible source text or paint overflows the root border box.");
516
858
  return {
517
- width: i.width,
518
- height: i.height,
519
- operations: s,
520
- warnings: l,
521
- atomicBounds: c
859
+ width: a.width,
860
+ height: a.height,
861
+ operations: c,
862
+ warnings: m,
863
+ atomicBounds: f,
864
+ ...i ? { tableHeaders: Te(e, a, c, l, u, d) } : {}
522
865
  };
523
866
  }
524
867
  // src/page.ts
525
- function V(e) {
526
- if (!e || !Array.isArray(e.fonts) || e.fonts.length === 0) throw new b("INVALID_OPTIONS", "Provide at least one explicitly registered font.");
868
+ function L(e) {
869
+ if (!e || typeof e != "object" || Array.isArray(e)) throw new w("INVALID_OPTIONS", "PDF page options must be an object.");
527
870
  let t = {
528
871
  A4: [595.28, 841.89],
529
872
  A3: [841.89, 1190.55],
530
873
  Letter: [612, 792]
531
874
  }, n = e.format ?? "A4";
532
- if (typeof n != "object" && !Object.hasOwn(t, n)) throw new b("INVALID_OPTIONS", `Unsupported page format: ${n}.`);
875
+ if (typeof n != "object" && !Object.hasOwn(t, n)) throw new w("INVALID_OPTIONS", `Unsupported page format: ${n}.`);
533
876
  let r = e.orientation ?? "portrait";
534
- if (r !== "portrait" && r !== "landscape") throw new b("INVALID_OPTIONS", `Unsupported page orientation: ${r}.`);
877
+ if (r !== "portrait" && r !== "landscape") throw new w("INVALID_OPTIONS", `Unsupported page orientation: ${r}.`);
535
878
  let i = e.margin ?? 24, a = typeof i == "number" ? {
536
879
  top: i,
537
880
  right: i,
@@ -543,183 +886,250 @@ function V(e) {
543
886
  bottom: i?.bottom,
544
887
  left: i?.left
545
888
  };
546
- if (Object.values(a).some((e) => !Number.isFinite(e) || e < 0)) throw new b("INVALID_OPTIONS", "Every page margin must be finite and nonnegative.");
889
+ if (Object.values(a).some((e) => !Number.isFinite(e) || e < 0)) throw new w("INVALID_OPTIONS", "Every page margin must be finite and nonnegative.");
547
890
  let o = typeof n == "object" ? [n?.width, n?.height] : t[n].map((e) => e / .75);
548
- if (o.some((e) => !Number.isFinite(e) || e <= 0)) throw new b("INVALID_OPTIONS", "Custom page width and height must be finite positive CSS pixel values.");
891
+ if (o.some((e) => !Number.isFinite(e) || e <= 0)) throw new w("INVALID_OPTIONS", "Custom page width and height must be finite positive CSS pixel values.");
549
892
  let s = o[r === "portrait" ? 0 : 1], c = o[+(r === "portrait")];
550
- if (a.left + a.right >= s || a.top + a.bottom >= c) throw new b("INVALID_OPTIONS", "Page margins leave no positive content area.");
551
- if (e.rasterScale !== void 0 && (!Number.isFinite(e.rasterScale) || e.rasterScale <= 0)) throw new b("INVALID_OPTIONS", "Raster scale must be a finite positive number.");
552
- if (e.onWarning !== void 0 && typeof e.onWarning != "function") throw new b("INVALID_OPTIONS", "onWarning must be a function.");
553
- if (e.fallback !== void 0 && !["auto", "strict"].includes(e.fallback)) throw new b("INVALID_OPTIONS", "fallback must be auto or strict.");
554
- if (e.pageBreak !== void 0 && !["auto", "css"].includes(e.pageBreak)) throw new b("INVALID_OPTIONS", "pageBreak must be auto or css.");
555
- if (e.signal !== void 0 && !(e.signal instanceof AbortSignal)) throw new b("INVALID_OPTIONS", "signal must be an AbortSignal.");
893
+ if (a.left + a.right >= s || a.top + a.bottom >= c) throw new w("INVALID_OPTIONS", "Page margins leave no positive content area.");
556
894
  return {
557
895
  width: s,
558
896
  height: c,
559
897
  margin: a
560
898
  };
561
899
  }
900
+ function De(e) {
901
+ if (!e || !Array.isArray(e.fonts) || e.fonts.length === 0) throw new w("INVALID_OPTIONS", "Provide at least one explicitly registered font.");
902
+ let t = L(e);
903
+ if (e.rasterScale !== void 0 && (!Number.isFinite(e.rasterScale) || e.rasterScale <= 0)) throw new w("INVALID_OPTIONS", "Raster scale must be a finite positive number.");
904
+ if (e.onWarning !== void 0 && typeof e.onWarning != "function") throw new w("INVALID_OPTIONS", "onWarning must be a function.");
905
+ if (e.onProgress !== void 0 && typeof e.onProgress != "function") throw new w("INVALID_OPTIONS", "onProgress must be a function.");
906
+ if (e.fallback !== void 0 && !["auto", "strict"].includes(e.fallback)) throw new w("INVALID_OPTIONS", "fallback must be auto or strict.");
907
+ if (e.pageBreak !== void 0 && !["auto", "css"].includes(e.pageBreak)) throw new w("INVALID_OPTIONS", "pageBreak must be auto or css.");
908
+ if (e.repeatTableHeaders !== void 0 && typeof e.repeatTableHeaders != "boolean") throw new w("INVALID_OPTIONS", "repeatTableHeaders must be a boolean.");
909
+ if (e.signal !== void 0 && !(e.signal instanceof AbortSignal)) throw new w("INVALID_OPTIONS", "signal must be an AbortSignal.");
910
+ return t;
911
+ }
912
+ // src/page-bands.ts
913
+ function R(e, t) {
914
+ if (e === void 0) return;
915
+ if (!e || typeof e != "object" || typeof e.text != "string" || !Number.isFinite(e.height) || e.height <= 0 || /[\p{Cc}\p{Cf}\p{Zl}\p{Zp}]/u.test(e.text) || /[^\p{Script=Latin}\p{Script=Common}\p{Script=Inherited}]/u.test(e.text)) throw new w("INVALID_OPTIONS", "Page header/footer requires single-line Latin text and a finite positive reserved height.");
916
+ let n = e.font ?? t[0], r = e.fontSize ?? 12, i = e.align ?? "left";
917
+ if (!t.includes(n) || !Number.isFinite(r) || r <= 0 || ![
918
+ "left",
919
+ "center",
920
+ "right"
921
+ ].includes(i)) throw new w("INVALID_OPTIONS", "Page header/footer needs a font included in fonts, a positive fontSize and left/center/right alignment.");
922
+ return Object.freeze({
923
+ text: e.text,
924
+ height: e.height,
925
+ font: n,
926
+ fontSize: r,
927
+ align: i
928
+ });
929
+ }
930
+ function Oe(e, t) {
931
+ let n = R(t.header, t.fonts), r = R(t.footer, t.fonts), i = {
932
+ ...e,
933
+ margin: {
934
+ ...e.margin,
935
+ top: e.margin.top + (n?.height ?? 0),
936
+ bottom: e.margin.bottom + (r?.height ?? 0)
937
+ }
938
+ };
939
+ if (i.margin.top + i.margin.bottom >= i.height) throw new w("INVALID_OPTIONS", "Page header/footer heights and margins leave no positive source content area.");
940
+ return {
941
+ content: i,
942
+ header: n,
943
+ footer: r
944
+ };
945
+ }
562
946
  // src/pagination.ts
563
- var H = .01;
564
- function U(e, t, n) {
947
+ var z = .01;
948
+ function B(e, t, n) {
565
949
  if (![
566
950
  e.x,
567
951
  e.y,
568
952
  e.width,
569
953
  e.height
570
- ].every(Number.isFinite) || e.width < 0 || e.height < 0 || e.y < -.01 || e.y + e.height > t + H) throw new b("PAGINATION_FAILED", `${n} geometry must be finite, nonnegative and within the source height.`);
954
+ ].every(Number.isFinite) || e.width < 0 || e.height < 0 || e.y < -.01 || e.y + e.height > t + z) throw new w("PAGINATION_FAILED", `${n} geometry must be finite, nonnegative and within the source height.`);
571
955
  }
572
- function W(e, t) {
573
- return e.y < t - H && e.y + e.height > t + H;
956
+ function V(e, t) {
957
+ return e.y < t - z && e.y + e.height > t + z;
574
958
  }
575
- function ue(e, t, n) {
959
+ function ke(e, t, n) {
576
960
  let r = e;
577
961
  for (;;) {
578
- let e = n.reduce((e, t) => W(t, r) ? Math.min(e, t.y) : e, r);
962
+ let e = n.reduce((e, t) => V(t, r) ? Math.min(e, t.y) : e, r);
579
963
  if (e === r) return r;
580
- if (e <= t + H) throw new b("PAGINATION_FAILED", "Overlapping atomic items cannot fit on one page.");
964
+ if (e <= t + z) throw new w("PAGINATION_FAILED", "Overlapping atomic items cannot fit on one page.");
581
965
  r = e;
582
966
  }
583
967
  }
584
- function de(e, t, n) {
585
- if (!Number.isFinite(t) || t <= 0) throw new b("PAGINATION_FAILED", "Page content height must be finite and positive.");
586
- if (!Number.isFinite(e.height) || e.height <= 0) throw new b("PAGINATION_FAILED", "Source height must be finite and positive.");
587
- let r = [], i = [...e.operations.filter((e) => e.kind !== "box").map((e) => e.bounds), ...e.atomicBounds ?? []];
588
- for (let n of i) if (U(n, e.height, "Atomic"), n.height > t + H) throw new b("PAGINATION_FAILED", "An atomic item is taller than one page; this renderer cannot split it.");
589
- let a = e.forcedBreaks ?? [];
590
- for (let t of a) if (!Number.isFinite(t) || t < 0 || t > e.height) throw new b("PAGINATION_FAILED", "Forced page breaks must lie within the finite source height.");
591
- let o = [...new Set(a)].filter((t) => t > H && t < e.height - H).sort((e, t) => e - t).filter((e, t, n) => t === 0 || e - n[t - 1] > H);
592
- for (let e of o) if (i.some((t) => W(t, e))) throw new b("PAGINATION_FAILED", "A forced CSS page break crosses an atomic text, image, raster, table row or spanning cell.");
593
- let s = !1;
594
- function c() {
595
- s || (s = !0, n?.({
968
+ function Ae(e, t, n) {
969
+ if (!Number.isFinite(t) || t <= 0) throw new w("PAGINATION_FAILED", "Page content height must be finite and positive.");
970
+ if (!Number.isFinite(e.height) || e.height <= 0) throw new w("PAGINATION_FAILED", "Source height must be finite and positive.");
971
+ let r = [], i = [...e.operations.filter((e) => e.kind !== "box" && e.kind !== "border").map((e) => e.bounds), ...e.atomicBounds ?? []], a = e.tableHeaders ?? [];
972
+ for (let [n, r] of a.entries()) {
973
+ if (B(r.bounds, e.height, "Table header"), r.bounds.width <= 0 || r.bounds.height <= 0 || !Number.isFinite(r.bodyStart) || !Number.isFinite(r.bodyEnd) || r.bodyStart < r.bounds.y + r.bounds.height - z || r.bodyEnd <= r.bodyStart || r.bodyEnd > e.height + z) throw new w("PAGINATION_FAILED", "Repeated table headers require a positive header followed by finite body row bounds.");
974
+ if (a.slice(0, n).some((e) => Math.min(e.bodyEnd, r.bodyEnd) - Math.max(e.bounds.y, r.bounds.y) > z)) throw new w("PAGINATION_FAILED", "Repeated table header continuation intervals must not overlap.");
975
+ let o = r.bodyStart;
976
+ for (;;) {
977
+ let e = i.reduce((e, t) => t.y >= r.bodyStart - z && t.y < r.bodyEnd - z && (t.y <= r.bodyStart + z || t.y < o - z) ? Math.max(e, t.y + t.height) : e, o);
978
+ if (e === o) break;
979
+ o = e;
980
+ }
981
+ if (o <= r.bodyStart + z) throw new w("PAGINATION_FAILED", "Repeated table headers require a measured atomic first body row.");
982
+ if (o - r.bounds.y > t + z) throw new w("PAGINATION_FAILED", "A table header and its first atomic body row cannot fit on one page.");
983
+ i.push({
984
+ ...r.bounds,
985
+ height: o - r.bounds.y
986
+ });
987
+ }
988
+ for (let n of i) if (B(n, e.height, "Atomic"), n.height > t + z) throw new w("PAGINATION_FAILED", "An atomic item is taller than one page; this renderer cannot split it.");
989
+ let o = e.forcedBreaks ?? [];
990
+ for (let t of o) if (!Number.isFinite(t) || t < 0 || t > e.height) throw new w("PAGINATION_FAILED", "Forced page breaks must lie within the finite source height.");
991
+ let s = [...new Set(o)].filter((t) => t > z && t < e.height - z).sort((e, t) => e - t).filter((e, t, n) => t === 0 || e - n[t - 1] > z);
992
+ for (let e of s) if (i.some((t) => V(t, e))) throw new w("PAGINATION_FAILED", "A forced CSS page break crosses an atomic text, image, raster, table row or spanning cell.");
993
+ let c = !1;
994
+ function l() {
995
+ c || (c = !0, n?.({
596
996
  code: "BREAK_AVOID_RELAXED",
597
997
  message: "A CSS break-avoid preference could not fit on one page and was relaxed; native text, images and table rows remain indivisible."
598
998
  }));
599
999
  }
600
- let l = [];
601
- for (let n of e.avoidBreakBounds ?? []) U(n, e.height, "Preferred break-avoid"), !o.some((e) => W(n, e)) && (n.height > t + H ? c() : l.push(n));
602
- let u = [...i, ...l], d = 0, f = 0;
603
- for (; d < e.height;) {
604
- for (; o[f] !== void 0 && o[f] <= d + H;) f += 1;
605
- let n = o[f] ?? e.height, a = Math.min(d + t, n, e.height), s = ue(a, d, i), l = s;
606
- if (Math.abs(a - n) > H) for (;;) {
607
- let e = u.reduce((e, t) => W(t, l) ? Math.min(e, t.y) : e, l);
608
- if (e === l) break;
609
- if (e <= d + H) {
610
- c(), l = s;
1000
+ let u = [];
1001
+ for (let n of e.avoidBreakBounds ?? []) B(n, e.height, "Preferred break-avoid"), !s.some((e) => V(n, e)) && (n.height > t + z ? l() : u.push(n));
1002
+ let d = [...i, ...u], f = 0, p = 0;
1003
+ for (; f < e.height;) {
1004
+ let n = a.find((e) => f >= e.bodyStart - z && f < e.bodyEnd - z), o = n ? (e.atomicBounds ?? []).reduce((e, t) => t.y >= f - z && t.y < n.bodyEnd - z ? Math.min(e, t.y) : e, Infinity) : f, c = Number.isFinite(o) ? Math.max(0, o - f) : 0, u = n ? Math.max(n.bounds.height, n.bodyStart - n.bounds.y - c) : 0, m = t - u;
1005
+ if (m <= z) throw new w("PAGINATION_FAILED", "A repeated table header leaves no space for its body rows.");
1006
+ for (; s[p] !== void 0 && s[p] <= f + z;) p += 1;
1007
+ let h = s[p] ?? e.height, g = Math.min(f + m, h, e.height);
1008
+ if (n && i.some((e) => e.y <= f + z && V(e, f + m))) throw new w("PAGINATION_FAILED", "A repeated table header and its atomic body row cannot fit on one page.");
1009
+ let _ = ke(g, f, i), v = _;
1010
+ if (Math.abs(g - h) > z) for (;;) {
1011
+ let e = d.reduce((e, t) => V(t, v) ? Math.min(e, t.y) : e, v);
1012
+ if (e === v) break;
1013
+ if (e <= f + z) {
1014
+ l(), v = _;
611
1015
  break;
612
1016
  }
613
- l = e;
1017
+ v = e;
614
1018
  }
1019
+ if (v <= f + z) throw new w("PAGINATION_FAILED", "Page planning must advance through the source.");
1020
+ if (n && !(e.atomicBounds ?? []).some((e) => e.y >= f - z && e.y < n.bodyEnd - z && e.y + e.height <= v + z && e.y + e.height > f + z)) throw new w("PAGINATION_FAILED", "A repeated table header must share its page with a complete atomic body row.");
615
1021
  r.push({
616
- start: d,
617
- end: l
618
- }), d = l;
1022
+ start: f,
1023
+ end: v,
1024
+ ...n ? {
1025
+ header: n,
1026
+ headerHeight: u
1027
+ } : {}
1028
+ }), f = v;
619
1029
  }
620
1030
  return r;
621
1031
  }
622
1032
  // src/images.ts
623
- function G() {
624
- throw new b("RESOURCE_LOAD_FAILED", "JPEG EXIF orientation must be 1 with valid metadata; normalize the image pixels and remove orientation metadata before exporting.");
1033
+ function H() {
1034
+ throw new w("RESOURCE_LOAD_FAILED", "JPEG EXIF orientation must be 1 with valid metadata; normalize the image pixels and remove orientation metadata before exporting.");
625
1035
  }
626
- function fe(e, t, n) {
1036
+ function je(e, t, n) {
627
1037
  let r = new DataView(e.buffer, e.byteOffset + t, n - t);
628
- r.byteLength < 8 && G();
1038
+ r.byteLength < 8 && H();
629
1039
  let i = r.getUint16(0);
630
- i !== 18761 && i !== 19789 && G();
1040
+ i !== 18761 && i !== 19789 && H();
631
1041
  let a = i === 18761;
632
- r.getUint16(2, a) !== 42 && G();
1042
+ r.getUint16(2, a) !== 42 && H();
633
1043
  let o = r.getUint32(4, a);
634
- (o < 8 || o + 2 > r.byteLength) && G();
1044
+ (o < 8 || o + 2 > r.byteLength) && H();
635
1045
  let s = r.getUint16(o, a), c = o + 2 + s * 12;
636
- c + 4 > r.byteLength && G();
1046
+ c + 4 > r.byteLength && H();
637
1047
  let l = !1;
638
1048
  for (let e = 0; e < s; e += 1) {
639
1049
  let t = o + 2 + e * 12;
640
- r.getUint16(t, a) === 274 && ((l || r.getUint16(t + 2, a) !== 3 || r.getUint32(t + 4, a) !== 1 || r.getUint16(t + 8, a) !== 1) && G(), l = !0);
1050
+ r.getUint16(t, a) === 274 && ((l || r.getUint16(t + 2, a) !== 3 || r.getUint32(t + 4, a) !== 1 || r.getUint16(t + 8, a) !== 1) && H(), l = !0);
641
1051
  }
642
1052
  let u = r.getUint32(c, a);
643
- u !== 0 && (u < 8 || u + 2 > r.byteLength) && G();
1053
+ u !== 0 && (u < 8 || u + 2 > r.byteLength) && H();
644
1054
  }
645
- function pe(e) {
1055
+ function Me(e) {
646
1056
  if (e[0] === 137 && e[1] === 80 && e[2] === 78 && e[3] === 71 && e[4] === 13 && e[5] === 10 && e[6] === 26 && e[7] === 10) return "png";
647
- if (e[0] !== 255 || e[1] !== 216) throw new b("RESOURCE_LOAD_FAILED", "Native PDF images require PNG or JPEG bytes.");
1057
+ if (e[0] !== 255 || e[1] !== 216) throw new w("RESOURCE_LOAD_FAILED", "Native PDF images require PNG or JPEG bytes.");
648
1058
  let t = 2;
649
1059
  for (; t < e.length;) {
650
- for (e[t] !== 255 && G(); e[t] === 255;) t += 1;
1060
+ for (e[t] !== 255 && H(); e[t] === 255;) t += 1;
651
1061
  let n = e[t];
652
- if ((n === void 0 || n === 0) && G(), t += 1, n === 217) return "jpeg";
1062
+ if ((n === void 0 || n === 0) && H(), t += 1, n === 217) return "jpeg";
653
1063
  if (n === 1 || n >= 208 && n <= 215) continue;
654
- t + 2 > e.length && G();
1064
+ t + 2 > e.length && H();
655
1065
  let r = e[t] * 256 + e[t + 1], i = t + r;
656
- if ((r < 2 || i > e.length) && G(), n === 218) return "jpeg";
1066
+ if ((r < 2 || i > e.length) && H(), n === 218) return "jpeg";
657
1067
  let a = t + 2;
658
- n === 225 && i - a >= 4 && e[a] === 69 && e[a + 1] === 120 && e[a + 2] === 105 && e[a + 3] === 102 && ((i - a < 6 || e[a + 4] !== 0 || e[a + 5] !== 0) && G(), fe(e, a + 6, i)), t = i;
1068
+ n === 225 && i - a >= 4 && e[a] === 69 && e[a + 1] === 120 && e[a + 2] === 105 && e[a + 3] === 102 && ((i - a < 6 || e[a + 4] !== 0 || e[a + 5] !== 0) && H(), je(e, a + 6, i)), t = i;
659
1069
  }
660
- G();
1070
+ H();
661
1071
  }
662
1072
  // src/raster.ts
663
- var me = 16777216, K = 8192, q = /^data:image\/(?:png|jpeg|gif|webp|avif);base64,[a-z\d+/=\s]+$/i;
664
- function he(e) {
1073
+ var Ne = 16777216, Pe = 8192, Fe = /^data:image\/(?:png|jpeg|gif|webp|avif);base64,[a-z\d+/=\s]+$/i;
1074
+ function Ie(e) {
665
1075
  let t = e.bytes;
666
- if (!/^[\w -]+$/.test(e.family) || t.byteLength === 0) throw new b("RASTER_FAILED", "Raster capture requires nonempty registered fonts with plain CSS family names.");
1076
+ if (!/^[\w -]+$/.test(e.family) || t.byteLength === 0) throw new w("RASTER_FAILED", "Raster capture requires nonempty registered fonts with plain CSS family names.");
667
1077
  let n = "";
668
1078
  for (let e = 0; e < t.length; e += 8192) n += String.fromCharCode(...t.subarray(e, e + 8192));
669
1079
  let r = t[0] === 79 && t[1] === 84 && t[2] === 84 && t[3] === 79;
670
1080
  return `@font-face { font-family: "${e.family}"; font-weight: ${e.weight}; font-style: ${e.style}; src: url("data:font/${r ? "otf" : "ttf"};base64,${btoa(n)}") format("${r ? "opentype" : "truetype"}"); }`;
671
1081
  }
672
- function ge(e) {
1082
+ function Le(e) {
673
1083
  for (let t of e) {
674
1084
  let n = e.getPropertyValue(t);
675
- for (let e of n.matchAll(/url\(\s*(["']?)(.*?)\1\s*\)/gi)) if (!q.test(e[2] ?? "")) throw new b("RASTER_FAILED", `Raster capture requires inline bitmap resources (${t}).`);
1085
+ for (let e of n.matchAll(/url\(\s*(["']?)(.*?)\1\s*\)/gi)) if (!Fe.test(e[2] ?? "")) throw new w("RASTER_FAILED", `Raster capture requires inline bitmap resources (${t}).`);
676
1086
  }
677
1087
  }
678
- function _e(e, t) {
1088
+ function Re(e, t) {
679
1089
  let n = e.getBoundingClientRect(), r = [e, ...e.querySelectorAll("*")];
680
1090
  for (let i of r) {
681
- if (!(i instanceof HTMLElement)) throw new b("RASTER_FAILED", "Raster capture supports HTML subtrees only; inline SVG needs a separate experiment.");
682
- if (i.shadowRoot || i.tagName.includes("-") || i.tagName === "SLOT") throw new b("RASTER_FAILED", "Raster capture does not inspect shadow trees or distributed slot content in supported renderer.");
683
- if (/^(CANVAS|IFRAME|OBJECT|EMBED|VIDEO|AUDIO|INPUT|TEXTAREA|SELECT|SCRIPT|STYLE|LINK)$/.test(i.tagName)) throw new b("RASTER_FAILED", `Raster capture does not support ${i.tagName.toLowerCase()} elements.`);
1091
+ if (!(i instanceof HTMLElement)) throw new w("RASTER_FAILED", "Raster capture supports HTML subtrees only; inline SVG needs a separate experiment.");
1092
+ if (i.shadowRoot || i.tagName.includes("-") || i.tagName === "SLOT") throw new w("RASTER_FAILED", "Raster capture does not inspect shadow trees or distributed slot content in supported renderer.");
1093
+ if (/^(CANVAS|IFRAME|OBJECT|EMBED|VIDEO|AUDIO|INPUT|TEXTAREA|SELECT|SCRIPT|STYLE|LINK)$/.test(i.tagName)) throw new w("RASTER_FAILED", `Raster capture does not support ${i.tagName.toLowerCase()} elements.`);
684
1094
  let r = getComputedStyle(i);
685
- if (ge(r), r.mixBlendMode !== "normal" || r.backdropFilter !== "none") throw new b("RASTER_FAILED", "Raster capture cannot reproduce blending with pixels outside its subtree.");
686
- if (r.position === "fixed" || r.position === "sticky") throw new b("RASTER_FAILED", "Raster capture does not reproduce viewport-dependent positioning.");
687
- if (i !== e && r.position === "absolute") throw new b("RASTER_FAILED", "Raster capture does not normalize external containing blocks for positioned descendants.");
1095
+ if (Le(r), r.mixBlendMode !== "normal" || r.backdropFilter !== "none") throw new w("RASTER_FAILED", "Raster capture cannot reproduce blending with pixels outside its subtree.");
1096
+ if (r.position === "fixed" || r.position === "sticky") throw new w("RASTER_FAILED", "Raster capture does not reproduce viewport-dependent positioning.");
1097
+ if (i !== e && r.position === "absolute") throw new w("RASTER_FAILED", "Raster capture does not normalize external containing blocks for positioned descendants.");
688
1098
  let a = r.boxShadow.split(/,(?![^(]*\))/);
689
- if (r.filter !== "none" || r.boxShadow !== "none" && a.some((e) => !e.includes("inset"))) throw new b("RASTER_FAILED", "Raster capture requires effects contained in the border box; outer shadows and filters need expanded bounds.");
690
- if (r.textShadow !== "none" || r.outlineStyle !== "none" && Number.parseFloat(r.outlineWidth) > 0) throw new b("RASTER_FAILED", "Raster capture requires expanded bounds before capturing text shadows or outlines.");
691
- if (r.backgroundAttachment.includes("fixed")) throw new b("RASTER_FAILED", "Raster capture does not preserve viewport-dependent background attachment.");
692
- if (i === e && r.transform !== "none") throw new b("RASTER_FAILED", "Raster capture does not normalize transformed region coordinates.");
693
- if (r.display === "list-item") throw new b("RASTER_FAILED", "Raster capture does not preserve generated list markers in supported renderer.");
1099
+ if (r.filter !== "none" || r.boxShadow !== "none" && a.some((e) => !e.includes("inset"))) throw new w("RASTER_FAILED", "Raster capture requires effects contained in the border box; outer shadows and filters need expanded bounds.");
1100
+ if (r.textShadow !== "none" || r.outlineStyle !== "none" && Number.parseFloat(r.outlineWidth) > 0) throw new w("RASTER_FAILED", "Raster capture requires expanded bounds before capturing text shadows or outlines.");
1101
+ if (r.backgroundAttachment.includes("fixed")) throw new w("RASTER_FAILED", "Raster capture does not preserve viewport-dependent background attachment.");
1102
+ if (i === e && r.transform !== "none") throw new w("RASTER_FAILED", "Raster capture does not normalize transformed region coordinates.");
1103
+ if (r.display === "list-item") throw new w("RASTER_FAILED", "Raster capture does not preserve generated list markers in supported renderer.");
694
1104
  for (let e of ["::before", "::after"]) {
695
1105
  let t = getComputedStyle(i, e).content;
696
- if (t !== "none" && t !== "normal") throw new b("RASTER_FAILED", "Raster capture does not support generated pseudo-element content in supported renderer.");
1106
+ if (t !== "none" && t !== "normal") throw new w("RASTER_FAILED", "Raster capture does not support generated pseudo-element content in supported renderer.");
697
1107
  }
698
1108
  if (i instanceof HTMLImageElement) {
699
- if (i.srcset || !q.test(i.currentSrc || i.src)) throw new b("RASTER_FAILED", "Raster capture requires inline bitmap images without srcset.");
700
- if (!i.complete || i.naturalWidth === 0) throw new b("RASTER_FAILED", "Raster capture requires successfully decoded images.");
1109
+ if (i.srcset || !Fe.test(i.currentSrc || i.src)) throw new w("RASTER_FAILED", "Raster capture requires inline bitmap images without srcset.");
1110
+ if (!i.complete || i.naturalWidth === 0) throw new w("RASTER_FAILED", "Raster capture requires successfully decoded images.");
701
1111
  }
702
1112
  if ([...i.childNodes].some((e) => e.nodeType === Node.TEXT_NODE && !!e.textContent?.trim())) {
703
- if (!r.font) throw new b("RASTER_FAILED", "Raster capture requires a serializable computed font shorthand to preserve exact font sizes.");
1113
+ if (!r.font) throw new w("RASTER_FAILED", "Raster capture requires a serializable computed font shorthand to preserve exact font sizes.");
704
1114
  let e = r.fontFamily.split(",")[0]?.trim().replace(/^["']|["']$/g, "");
705
- if (!t.some((t) => t.family.toLowerCase() === e?.toLowerCase() && t.weight === Number(r.fontWeight) && t.style === r.fontStyle)) throw new b("RASTER_FAILED", "Raster capture text requires an explicitly registered matching font face.");
1115
+ if (!t.some((t) => t.family.toLowerCase() === e?.toLowerCase() && t.weight === Number(r.fontWeight) && t.style === r.fontStyle)) throw new w("RASTER_FAILED", "Raster capture text requires an explicitly registered matching font face.");
706
1116
  }
707
1117
  let o = i.getBoundingClientRect();
708
- if (o.width > 0 && o.height > 0 && (o.left < n.left - .5 || o.top < n.top - .5 || o.right > n.right + .5 || o.bottom > n.bottom + .5)) throw new b("RASTER_FAILED", "Raster capture descendants must stay inside the selected border box.");
1118
+ if (o.width > 0 && o.height > 0 && (o.left < n.left - .5 || o.top < n.top - .5 || o.right > n.right + .5 || o.bottom > n.bottom + .5)) throw new w("RASTER_FAILED", "Raster capture descendants must stay inside the selected border box.");
709
1119
  }
710
1120
  }
711
- async function ve(e, t, n, r = {
1121
+ async function ze(e, t, n, r = {
712
1122
  x: 0,
713
1123
  y: 0
714
1124
  }) {
715
- if (!e.isConnected || e.ownerDocument !== document) throw new b("RASTER_FAILED", "Raster capture requires a connected element in the current document.");
716
- if (!Number.isFinite(n) || n <= 0) throw new b("RASTER_FAILED", "Raster scale must be a finite positive number.");
717
- if (!r || ![r.x, r.y].every((e) => Number.isFinite(e) && e >= 0 && e < 1)) throw new b("RASTER_FAILED", "Raster offsets must be finite CSS pixel fractions from zero inclusive to one exclusive.");
1125
+ if (!e.isConnected || e.ownerDocument !== document) throw new w("RASTER_FAILED", "Raster capture requires a connected element in the current document.");
1126
+ if (!Number.isFinite(n) || n <= 0) throw new w("RASTER_FAILED", "Raster scale must be a finite positive number.");
1127
+ if (!r || ![r.x, r.y].every((e) => Number.isFinite(e) && e >= 0 && e < 1)) throw new w("RASTER_FAILED", "Raster offsets must be finite CSS pixel fractions from zero inclusive to one exclusive.");
718
1128
  await document.fonts.ready;
719
1129
  let { width: i, height: a } = e.getBoundingClientRect(), o = Math.ceil(i + r.x), s = Math.ceil(a + r.y), c = Math.ceil(o * n), l = Math.ceil(s * n);
720
- if (i <= 0 || a <= 0 || c < 1 || l < 1 || c > K || l > K || c * l > me) throw new b("RASTER_FAILED", "Raster region exceeds the supported renderer canvas budget or has empty bounds.");
721
- _e(e, t);
722
- let u = t.map(he).join("\n"), d = ["font", ...Array.from(getComputedStyle(e)).filter((e) => e !== "font-size")], f = (await y(e, {
1130
+ if (i <= 0 || a <= 0 || c < 1 || l < 1 || c > Pe || l > Pe || c * l > Ne) throw new w("RASTER_FAILED", "Raster region exceeds the supported renderer canvas budget or has empty bounds.");
1131
+ Re(e, t);
1132
+ let u = t.map(Ie).join("\n"), d = ["font", ...Array.from(getComputedStyle(e)).filter((e) => e !== "font-size")], f = (await C(e, {
723
1133
  width: o,
724
1134
  height: s,
725
1135
  pixelRatio: n,
@@ -738,12 +1148,12 @@ async function ve(e, t, n, r = {
738
1148
  transform: `translate(${r.x}px, ${r.y}px)`
739
1149
  }
740
1150
  })).match(/^data:image\/png;base64,(.+)$/)?.[1];
741
- if (!f) throw new b("RASTER_FAILED", "Raster capture produced an empty or invalid PNG.");
1151
+ if (!f) throw new w("RASTER_FAILED", "Raster capture produced an empty or invalid PNG.");
742
1152
  let p = atob(f);
743
1153
  return Uint8Array.from(p, (e) => e.charCodeAt(0));
744
1154
  }
745
1155
  // src/text.ts
746
- var ye = class {
1156
+ var Be = class {
747
1157
  font;
748
1158
  program;
749
1159
  words = /* @__PURE__ */ new Map();
@@ -757,11 +1167,11 @@ var ye = class {
757
1167
  let n = `${t ? "1" : "0"}:${e}`, r = this.words.get(n);
758
1168
  if (r) return r;
759
1169
  let a = this.program.layout(e, { kern: t }), o = this.font.encodeText(e).asString();
760
- if (o.length !== a.glyphs.length * 4) throw new b("PDF_WRITE_FAILED", "PDF font encoding disagrees with font shaping.");
1170
+ if (o.length !== a.glyphs.length * 4) throw new w("PDF_WRITE_FAILED", "PDF font encoding disagrees with font shaping.");
761
1171
  let s = 0, c = 0, l = a.glyphs.map((e, t) => {
762
1172
  let n = o.slice(t * 4, t * 4 + 4);
763
- if (Number.parseInt(n, 16) !== e.id) throw new b("PDF_WRITE_FAILED", "PDF glyph identifiers disagree with shaping.");
764
- if (e.codePoints.length === 0) throw new b("PDF_WRITE_FAILED", "The font produced a shaped glyph without a Unicode source mapping.");
1173
+ if (Number.parseInt(n, 16) !== e.id) throw new w("PDF_WRITE_FAILED", "PDF glyph identifiers disagree with shaping.");
1174
+ if (e.codePoints.length === 0) throw new w("PDF_WRITE_FAILED", "The font produced a shaped glyph without a Unicode source mapping.");
765
1175
  let r = a.positions[t];
766
1176
  this.unicode.has(n) || this.unicode.set(n, [...e.codePoints]), this.widths.set(n, e.advanceWidth * 1e3 / this.unitsPerEm);
767
1177
  let l = {
@@ -773,6 +1183,19 @@ var ye = class {
773
1183
  });
774
1184
  return this.words.set(n, l), l;
775
1185
  }
1186
+ measure(e) {
1187
+ let t = this.program.layout(e), n = 0, r = 0, i = 0, a = 0, o = this.program.ascent, s = -this.program.descent;
1188
+ for (let e = 0; e < t.glyphs.length; e += 1) {
1189
+ let c = t.glyphs[e], l = t.positions[e];
1190
+ i = Math.min(i, n + l.xOffset + c.bbox.minX), a = Math.max(a, n + l.xOffset + c.bbox.maxX), o = Math.max(o, r + l.yOffset + c.bbox.maxY), s = Math.max(s, -(r + l.yOffset + c.bbox.minY)), n += l.xAdvance, r += l.yAdvance;
1191
+ }
1192
+ return {
1193
+ left: i,
1194
+ right: Math.max(a, n),
1195
+ ascent: o,
1196
+ descent: s
1197
+ };
1198
+ }
776
1199
  async embedUnicode(e) {
777
1200
  if (await this.font.embed(), this.unicode.size === 0) return;
778
1201
  let r = [...this.unicode].map(([e, t]) => {
@@ -804,39 +1227,66 @@ var ye = class {
804
1227
  let c = s.lookup(a.of("DescendantFonts"), t).lookup(0, n), l = [...this.widths].sort(([e], [t]) => Number.parseInt(e, 16) - Number.parseInt(t, 16)).flatMap(([e, t]) => [Number.parseInt(e, 16), [t]]);
805
1228
  c.set(a.of("W"), e.context.obj(l));
806
1229
  }
807
- }, J = .75;
808
- function Y(e) {
1230
+ }, U = .75;
1231
+ function W(e) {
809
1232
  let t = e.match(/^rgba?\(\s*([\d.]+)[, ]+([\d.]+)[, ]+([\d.]+)(?:\s*[,/]\s*([\d.]+))?\s*\)$/);
810
- if (!t) throw new b("UNSUPPORTED_CSS", `Unsupported computed color: ${e}`);
1233
+ if (!t) throw new w("UNSUPPORTED_CSS", `Unsupported computed color: ${e}`);
811
1234
  return {
812
- color: m(Number(t[1]) / 255, Number(t[2]) / 255, Number(t[3]) / 255),
1235
+ color: g(Number(t[1]) / 255, Number(t[2]) / 255, Number(t[3]) / 255),
813
1236
  opacity: Number(t[4] ?? 1)
814
1237
  };
815
1238
  }
816
- function X(e, t) {
1239
+ function G(e, t) {
817
1240
  let n = t.find(({ definition: t }) => t.family.toLowerCase() === e.family.toLowerCase() && t.weight === e.weight && t.style === e.style);
818
- if (!n) throw new b("MISSING_FONT", `Unregistered font face: ${e.family} ${e.weight} ${e.style}`);
819
- for (let t of e.text) if (!n.characters.has(t.codePointAt(0))) throw new b("MISSING_GLYPH", `Missing glyph: ${t} (${e.family} ${e.weight} ${e.style})`);
1241
+ if (!n) throw new w("MISSING_FONT", `Unregistered font face: ${e.family} ${e.weight} ${e.style}`);
1242
+ for (let t of e.text) if (!n.characters.has(t.codePointAt(0))) throw new w("MISSING_GLYPH", `Missing glyph: ${t} (${e.family} ${e.weight} ${e.style})`);
820
1243
  return n;
821
1244
  }
822
- function be(e, t, n, r, i, a) {
823
- let c = Y(t.color), l = a.fonts.get(n.font);
824
- if (l || (l = e.node.newFontDictionary(n.font.name, n.font.ref), a.fonts.set(n.font, l)), e.pushOperators(f(), h(c.color)), c.opacity !== 1) {
1245
+ function K(e, t, n, r, i, a) {
1246
+ let c = W(t.color), l = a.fonts.get(n.font);
1247
+ if (l || (l = e.node.newFontDictionary(n.font.name, n.font.ref), a.fonts.set(n.font, l)), e.pushOperators(m(), _(c.color)), c.opacity !== 1) {
825
1248
  let t = a.opacity.get(c.opacity);
826
1249
  t || (t = e.node.newExtGState("TextOpacity", e.doc.context.obj({
827
1250
  Type: "ExtGState",
828
1251
  ca: c.opacity
829
- })), a.opacity.set(c.opacity, t)), e.pushOperators(_(t));
1252
+ })), a.opacity.set(c.opacity, t)), e.pushOperators(y(t));
830
1253
  }
831
- e.pushOperators(s(), g(l, t.fontSize * J), ee(t.invisible ? o.Invisible : o.Fill));
832
- let p = t.fontSize * J / n.positioned.unitsPerEm, m = (i.height - i.margin.top - (t.baseline - r)) * J;
1254
+ e.pushOperators(s(), v(l, t.fontSize * U), x(t.invisible ? o.Invisible : o.Fill));
1255
+ let u = t.fontSize * U / n.positioned.unitsPerEm, d = (i.height - i.margin.top - (t.baseline - r)) * U;
833
1256
  for (let r of t.runs) {
834
- let a = (i.margin.left + r.x) * J;
835
- for (let i of n.positioned.shape(r.text, t.kerning ?? !0)) e.pushOperators(v(1, 0, 0, 1, a + i.x * p, m + i.y * p), te(i.encoded));
1257
+ let a = (i.margin.left + r.x) * U;
1258
+ for (let i of n.positioned.shape(r.text, t.kerning ?? !0)) e.pushOperators(b(1, 0, 0, 1, a + i.x * u, d + i.y * u), S(i.encoded));
836
1259
  }
837
- e.pushOperators(u(), d());
1260
+ e.pushOperators(f(), p());
838
1261
  }
839
- function Z(e) {
1262
+ function Ve(e, t, n, r, i, a) {
1263
+ let o = e.text.replaceAll("{pageNumber}", String(t)).replaceAll("{pageCount}", String(n)), s = a.find((t) => t.definition === e.font), c = e.fontSize / s.positioned.unitsPerEm, l = s.positioned.measure(o), u = (l.right - l.left) * c, d = (l.ascent + l.descent) * c, f = i.width - i.margin.left - i.margin.right;
1264
+ if (u > f + .01 || d > e.height + .01) throw new w("SOURCE_OVERFLOW", "Page header/footer text does not fit its reserved height or page content width; increase its area or reduce fontSize.");
1265
+ let p = e.align === "right" ? f - u : e.align === "center" ? (f - u) / 2 : 0, m = p - l.left * c, h = r - i.margin.top + (e.height - d) / 2;
1266
+ return {
1267
+ kind: "text",
1268
+ bounds: {
1269
+ x: p,
1270
+ y: h,
1271
+ width: u,
1272
+ height: d
1273
+ },
1274
+ text: o,
1275
+ runs: [{
1276
+ text: o,
1277
+ x: m,
1278
+ width: u
1279
+ }],
1280
+ baseline: h + l.ascent * c,
1281
+ fontSize: e.fontSize,
1282
+ family: e.font.family,
1283
+ weight: e.font.weight,
1284
+ style: e.font.style,
1285
+ color: "rgb(0, 0, 0)",
1286
+ invisible: !1
1287
+ };
1288
+ }
1289
+ function He(e) {
840
1290
  let t = Math.floor(e.x), n = Math.floor(e.y);
841
1291
  return {
842
1292
  x: t,
@@ -845,10 +1295,10 @@ function Z(e) {
845
1295
  height: Math.ceil(e.y + e.height) - n
846
1296
  };
847
1297
  }
848
- async function xe(e, t, n) {
1298
+ async function Ue(e, t, n) {
849
1299
  let r = t.currentSrc || t.src;
850
1300
  try {
851
- x(n);
1301
+ T(n);
852
1302
  let t = await fetch(r, {
853
1303
  mode: "cors",
854
1304
  credentials: "same-origin",
@@ -856,109 +1306,182 @@ async function xe(e, t, n) {
856
1306
  });
857
1307
  if (!t.ok) throw Error(`Image request returned HTTP ${t.status}.`);
858
1308
  let i = new Uint8Array(await t.arrayBuffer());
859
- return x(n), pe(i) === "png" ? await e.embedPng(i) : await e.embedJpg(i);
1309
+ return T(n), Me(i) === "png" ? await e.embedPng(i) : await e.embedJpg(i);
860
1310
  } catch (e) {
861
- throw x(n), e instanceof b ? e : new b("RESOURCE_LOAD_FAILED", "The source image could not be embedded; provide a readable PNG/JPEG with same-origin or CORS access.", { cause: e });
1311
+ throw T(n), e instanceof w ? e : new w("RESOURCE_LOAD_FAILED", "The source image could not be embedded; provide a readable PNG/JPEG with same-origin or CORS access.", { cause: e });
862
1312
  }
863
1313
  }
864
- async function Q(t, n, i, a, o = {}) {
1314
+ async function We(t, n, i, a, o = {}) {
865
1315
  try {
866
- if (x(o.signal), t.width > i.width - i.margin.left - i.margin.right) throw new b("SOURCE_OVERFLOW", "Source is wider than the PDF content area.");
1316
+ if (T(o.signal), t.width > i.width - i.margin.left - i.margin.right) throw new w("SOURCE_OVERFLOW", "Source is wider than the PDF content area.");
867
1317
  let s = await r.create();
868
- x(o.signal), s.registerFontkit(e), s.setTitle("RenderPDF.js");
869
- let u = [];
1318
+ T(o.signal), s.registerFontkit(e), oe(s, o.metadata);
1319
+ let l = o.reportProgress ? n.length + t.operations.filter((e) => e.kind === "image" || e.kind === "raster").length : 0, u = 0;
1320
+ o.reportProgress && await o.reportProgress({
1321
+ stage: "resources",
1322
+ completed: 0,
1323
+ total: l
1324
+ });
1325
+ let f = [];
870
1326
  for (let e of n) {
871
- x(o.signal);
1327
+ T(o.signal);
872
1328
  let t = await s.embedFont(e.bytes);
873
- u.push({
1329
+ f.push({
874
1330
  definition: e,
875
1331
  font: t,
876
1332
  characters: new Set(t.getCharacterSet()),
877
- positioned: new ye(t, e.bytes)
1333
+ positioned: new Be(t, e.bytes)
1334
+ }), u += 1, o.reportProgress && await o.reportProgress({
1335
+ stage: "resources",
1336
+ completed: u,
1337
+ total: l
878
1338
  });
879
1339
  }
880
- for (let e of t.operations) e.kind !== "text" || e.invisible && o.includeRasterText === !1 || X(e, u);
881
- let m = /* @__PURE__ */ new Map(), h = /* @__PURE__ */ new Map();
1340
+ for (let e of t.operations) e.kind !== "text" || e.invisible && o.includeRasterText === !1 || G(e, f);
1341
+ let g = a.map((e, t) => {
1342
+ let n = [];
1343
+ o.header && n.push(Ve(o.header, t + 1, a.length, i.margin.top, i, f)), o.footer && n.push(Ve(o.footer, t + 1, a.length, i.height - i.margin.bottom - o.footer.height, i, f));
1344
+ for (let e of n) G(e, f);
1345
+ return n;
1346
+ }), _ = /* @__PURE__ */ new Map(), v = /* @__PURE__ */ new Map();
882
1347
  for (let e of t.operations) {
883
- if (x(o.signal), e.kind === "raster") {
884
- let t = Z(e.bounds);
1348
+ if (T(o.signal), e.kind === "raster") {
1349
+ let t = He(e.bounds);
885
1350
  try {
886
- let r = await ve(e.element, n, o.rasterScale ?? 2, {
1351
+ let r = await ze(e.element, n, o.rasterScale ?? 2, {
887
1352
  x: e.bounds.x - t.x,
888
1353
  y: e.bounds.y - t.y
889
1354
  });
890
- x(o.signal), m.set(e, await s.embedPng(r));
1355
+ T(o.signal), _.set(e, await s.embedPng(r));
891
1356
  } catch (e) {
892
- throw x(o.signal), e instanceof b ? e : new b("RASTER_FAILED", "The selected local raster region could not be captured.", { cause: e });
1357
+ throw T(o.signal), e instanceof w ? e : new w("RASTER_FAILED", "The selected local raster region could not be captured.", { cause: e });
893
1358
  }
894
1359
  }
895
1360
  if (e.kind === "image") {
896
- let t = e.element.currentSrc || e.element.src, n = h.get(t);
897
- n || (n = await xe(s, e.element, o.signal), h.set(t, n)), m.set(e, n);
1361
+ let t = e.element.currentSrc || e.element.src, n = v.get(t);
1362
+ n || (n = await Ue(s, e.element, o.signal), v.set(t, n)), _.set(e, n);
898
1363
  }
1364
+ (e.kind === "image" || e.kind === "raster") && (u += 1, o.reportProgress && await o.reportProgress({
1365
+ stage: "resources",
1366
+ completed: u,
1367
+ total: l
1368
+ }));
899
1369
  }
900
- for (let e of a) {
901
- x(o.signal);
902
- let n = s.addPage([i.width * J, i.height * J]), r = {
1370
+ o.reportProgress && await o.reportProgress({
1371
+ stage: "pages",
1372
+ completed: 0,
1373
+ total: a.length
1374
+ });
1375
+ for (let [e, n] of a.entries()) {
1376
+ T(o.signal);
1377
+ let r = s.addPage([i.width * U, i.height * U]), l = {
903
1378
  fonts: /* @__PURE__ */ new Map(),
904
1379
  opacity: /* @__PURE__ */ new Map()
905
1380
  };
906
- n.pushOperators(f(), p(i.margin.left * J, (i.height - i.margin.top - (e.end - e.start)) * J, (i.width - i.margin.left - i.margin.right) * J, (e.end - e.start) * J), c(), l());
907
- for (let a of t.operations) {
908
- let t = a.bounds;
909
- if (t.y >= e.end - .01 || t.y + t.height <= e.start + .01) continue;
910
- if (a.kind === "text") {
911
- a.invisible && o.includeRasterText === !1 || be(n, a, X(a, u), e.start, i, r);
912
- continue;
913
- }
914
- let s = a.kind === "raster" ? Z(t) : t, c = {
915
- x: (i.margin.left + s.x) * J,
916
- y: (i.height - i.margin.top - (s.y - e.start) - s.height) * J,
917
- width: s.width * J,
918
- height: s.height * J
1381
+ function u(e, t, n) {
1382
+ let a = {
1383
+ ...i,
1384
+ margin: {
1385
+ ...i.margin,
1386
+ top: n.y
1387
+ }
919
1388
  };
920
- if (a.kind === "box") {
921
- let e = Y(a.background), t = Y(a.borderColor);
922
- if (n.drawRectangle({
923
- ...c,
924
- color: e.color,
925
- opacity: e.opacity
926
- }), a.borderWidth > 0) {
927
- let e = a.borderWidth * J / 2;
928
- n.drawRectangle({
929
- x: c.x + e,
930
- y: c.y + e,
931
- width: c.width - e * 2,
932
- height: c.height - e * 2,
933
- borderWidth: a.borderWidth * J,
934
- borderColor: t.color,
935
- borderOpacity: t.opacity
936
- });
1389
+ r.pushOperators(m(), h(n.x * U, (i.height - n.y - n.height) * U, n.width * U, n.height * U), c(), d());
1390
+ for (let s of e) {
1391
+ let e = s.bounds;
1392
+ if (e.y >= t + n.height - .01 || e.y + e.height <= t + .01) continue;
1393
+ if (s.kind === "text") {
1394
+ s.invisible && o.includeRasterText === !1 || K(r, s, G(s, f), t, a, l);
1395
+ continue;
937
1396
  }
938
- } else n.drawImage(m.get(a), c);
1397
+ let c = s.kind === "raster" ? He(e) : e, u = {
1398
+ x: (i.margin.left + c.x) * U,
1399
+ y: (i.height - n.y - (c.y - t) - c.height) * U,
1400
+ width: c.width * U,
1401
+ height: c.height * U
1402
+ };
1403
+ if (s.kind === "box") {
1404
+ let e = W(s.background), t = W(s.borderColor);
1405
+ if (r.drawRectangle({
1406
+ ...u,
1407
+ color: e.color,
1408
+ opacity: e.opacity
1409
+ }), s.borderWidth > 0) {
1410
+ let e = s.borderWidth * U / 2;
1411
+ r.drawRectangle({
1412
+ x: u.x + e,
1413
+ y: u.y + e,
1414
+ width: u.width - e * 2,
1415
+ height: u.height - e * 2,
1416
+ borderWidth: s.borderWidth * U,
1417
+ borderColor: t.color,
1418
+ borderOpacity: t.opacity
1419
+ });
1420
+ }
1421
+ } else if (s.kind === "border") {
1422
+ let e = W(s.color);
1423
+ r.drawRectangle({
1424
+ ...u,
1425
+ color: e.color,
1426
+ opacity: e.opacity
1427
+ });
1428
+ } else r.drawImage(_.get(s), u);
1429
+ }
1430
+ r.pushOperators(p());
1431
+ }
1432
+ let v = i.margin.top + (o.header?.height ?? 0), y = n.headerHeight ?? (n.header ? n.header.bodyStart - n.header.bounds.y : 0);
1433
+ if (o.header) {
1434
+ let t = g[e][0];
1435
+ K(r, t, G(t, f), 0, i, l);
1436
+ }
1437
+ if (n.header && u(n.header.operations, n.header.bounds.y, {
1438
+ x: i.margin.left + n.header.bounds.x,
1439
+ y: v,
1440
+ width: n.header.bounds.width,
1441
+ height: y
1442
+ }), u(t.operations, n.start, {
1443
+ x: i.margin.left,
1444
+ y: v + y,
1445
+ width: i.width - i.margin.left - i.margin.right,
1446
+ height: n.end - n.start
1447
+ }), o.footer) {
1448
+ let t = g[e].at(-1);
1449
+ K(r, t, G(t, f), 0, i, l);
939
1450
  }
940
- n.pushOperators(d());
1451
+ o.reportProgress && await o.reportProgress({
1452
+ stage: "pages",
1453
+ completed: e + 1,
1454
+ total: a.length
1455
+ });
941
1456
  }
942
- x(o.signal);
943
- for (let e of u) await e.positioned.embedUnicode(s), x(o.signal);
944
- let g = Uint8Array.from(await s.save());
945
- return x(o.signal), {
946
- bytes: g,
1457
+ T(o.signal), o.reportProgress && await o.reportProgress({
1458
+ stage: "saving",
1459
+ completed: 0,
1460
+ total: 1
1461
+ });
1462
+ for (let e of f) await e.positioned.embedUnicode(s), T(o.signal);
1463
+ let y = Uint8Array.from(await s.save());
1464
+ return T(o.signal), o.reportProgress && await o.reportProgress({
1465
+ stage: "saving",
1466
+ completed: 1,
1467
+ total: 1
1468
+ }), {
1469
+ bytes: y,
947
1470
  slices: a
948
1471
  };
949
1472
  } catch (e) {
950
- throw x(o.signal), e instanceof b ? e : new b("PDF_WRITE_FAILED", "The measured content could not be serialized as a PDF.", { cause: e });
1473
+ throw T(o.signal), e instanceof w ? e : new w("PDF_WRITE_FAILED", "The measured content could not be serialized as a PDF.", { cause: e });
951
1474
  }
952
1475
  }
953
1476
  // src/prepare.ts
954
- async function $(e, t) {
955
- return x(t), t ? new Promise((n, r) => {
1477
+ async function q(e, t) {
1478
+ return T(t), t ? new Promise((n, r) => {
956
1479
  let i = () => r(new DOMException("PDF rendering was cancelled.", "AbortError"));
957
1480
  t.addEventListener("abort", i, { once: !0 }), e.then(n, r).finally(() => t.removeEventListener("abort", i));
958
1481
  }) : e;
959
1482
  }
960
- function Se(e, t) {
961
- if (x(t), typeof document > "u" || !(e instanceof HTMLElement) || !e.isConnected || e.ownerDocument !== document) throw new b("INVALID_SOURCE", "Provide a connected HTMLElement in the current document.");
1483
+ function Ge(e, t) {
1484
+ if (T(t), typeof document > "u" || !(e instanceof HTMLElement) || !e.isConnected || e.ownerDocument !== document) throw new w("INVALID_SOURCE", "Provide a connected HTMLElement in the current document.");
962
1485
  let n = !1, r, i = /* @__PURE__ */ new Map(), a = new MutationObserver(() => {
963
1486
  n = !0;
964
1487
  }), o = [];
@@ -1008,18 +1531,18 @@ function Se(e, t) {
1008
1531
  return `${t >>> 0}:${n >>> 0}`;
1009
1532
  }
1010
1533
  function d() {
1011
- if (x(t), a.takeRecords().length && (n = !0), !e.isConnected || n || r !== void 0 && u() !== r) throw new b("SOURCE_CHANGED", "The source or its styles changed during PDF rendering. Retry with a stable source.");
1534
+ if (T(t), a.takeRecords().length && (n = !0), !e.isConnected || n || r !== void 0 && u() !== r) throw new w("SOURCE_CHANGED", "The source or its styles changed during PDF rendering. Retry with a stable source.");
1012
1535
  }
1013
1536
  function f() {
1014
- if (d(), [...e.getAnimations({ subtree: !0 }), ...o.flatMap((e) => e.getAnimations())].some((e) => e.playState === "running")) throw new b("UNSUPPORTED_CSS", "Pause source animations before rendering a PDF.");
1537
+ if (d(), [...e.getAnimations({ subtree: !0 }), ...o.flatMap((e) => e.getAnimations())].some((e) => e.playState === "running")) throw new w("UNSUPPORTED_CSS", "Pause source animations before rendering a PDF.");
1015
1538
  r = u();
1016
1539
  }
1017
1540
  async function p() {
1018
- await $(document.fonts.ready, t);
1541
+ await q(document.fonts.ready, t);
1019
1542
  try {
1020
- await $(Promise.all([...e.querySelectorAll("img"), ...e instanceof HTMLImageElement ? [e] : []].map((e) => e.decode())), t);
1543
+ await q(Promise.all([...e.querySelectorAll("img"), ...e instanceof HTMLImageElement ? [e] : []].map((e) => e.decode())), t);
1021
1544
  } catch (e) {
1022
- throw x(t), new b("RESOURCE_LOAD_FAILED", "A source image could not be decoded.", { cause: e });
1545
+ throw T(t), new w("RESOURCE_LOAD_FAILED", "A source image could not be decoded.", { cause: e });
1023
1546
  }
1024
1547
  d();
1025
1548
  }
@@ -1030,8 +1553,26 @@ function Se(e, t) {
1030
1553
  dispose: () => a.disconnect()
1031
1554
  };
1032
1555
  }
1556
+ // src/progress.ts
1557
+ function Ke(e, t) {
1558
+ let n, r = 0;
1559
+ return {
1560
+ report: e ? async (i) => {
1561
+ T(t);
1562
+ try {
1563
+ e(Object.freeze({ ...i }));
1564
+ } catch (e) {
1565
+ throw n = { error: e }, e;
1566
+ }
1567
+ T(t), i.stage !== "complete" && (i.completed === 0 || i.completed === i.total || performance.now() - r >= 32) && (await new Promise((e) => setTimeout(e, 0)), r = performance.now(), T(t));
1568
+ } : void 0,
1569
+ rethrow(e) {
1570
+ throw n ? n.error : (T(t), e);
1571
+ }
1572
+ };
1573
+ }
1033
1574
  // src/result.ts
1034
- function Ce(e, t, n) {
1575
+ function qe(e, t, n) {
1035
1576
  let r = Uint8Array.from(e), i = Object.freeze(n.map((e) => Object.freeze({ ...e })));
1036
1577
  return Object.freeze({
1037
1578
  pageCount: t,
@@ -1043,7 +1584,7 @@ function Ce(e, t, n) {
1043
1584
  return new Blob([r], { type: "application/pdf" });
1044
1585
  },
1045
1586
  async download(e = "document.pdf") {
1046
- if (typeof e != "string" || !e.trim() || /[/\\]/.test(e) || [...e].some((e) => e.codePointAt(0) < 32)) throw new b("INVALID_OPTIONS", "Provide a nonempty PDF filename without path separators or control characters.");
1587
+ if (typeof e != "string" || !e.trim() || /[/\\]/.test(e) || [...e].some((e) => e.codePointAt(0) < 32)) throw new w("INVALID_OPTIONS", "Provide a nonempty PDF filename without path separators or control characters.");
1047
1588
  let t = URL.createObjectURL(new Blob([r], { type: "application/pdf" }));
1048
1589
  try {
1049
1590
  let n = document.createElement("a");
@@ -1055,38 +1596,793 @@ function Ce(e, t, n) {
1055
1596
  });
1056
1597
  }
1057
1598
  // src/render-pdf.ts
1058
- async function we(e, t, n = !0) {
1059
- let r = V(t), i = [...t.fonts];
1060
- A(i);
1061
- let { signal: a, onWarning: o, rasterScale: s = 2, fallback: c = "auto", pageBreak: l = "css" } = t, u = Se(e, a);
1599
+ async function Je(e, t, n = !0) {
1600
+ let r = De(t), i = Oe(r, t), a = ae(t.metadata), o = [...t.fonts];
1601
+ A(o);
1602
+ let { signal: s, onWarning: c, onProgress: l, rasterScale: u = 2, fallback: d = "auto", pageBreak: f = "css", repeatTableHeaders: p = !1 } = t, m = Ge(e, s), h = Ke(l, s), g = h.report;
1062
1603
  try {
1063
- await u.ready(), u.assertUnchanged();
1064
- let t = B(e, i, { fallback: c });
1065
- if (u.captureBaseline(), t.width > r.width - r.margin.left - r.margin.right) throw new b("SOURCE_OVERFLOW", "Source is wider than the configured page content area. Set the source width explicitly; no implicit scaling is applied.");
1066
- l === "css" && Object.assign(t, ae(e, t));
1067
- let d = [], f = de(t, r.height - r.margin.top - r.margin.bottom, (e) => d.push(e)), p = [...t.warnings, ...d];
1068
- t.operations.some((e) => e.kind === "text") && !p.some((e) => e.code === "TEXT_METRICS_APPROXIMATED") && p.unshift({
1604
+ g && await g({
1605
+ stage: "preparing",
1606
+ completed: 0,
1607
+ total: 1
1608
+ }), await m.ready(), g && m.captureBaseline(), g && await g({
1609
+ stage: "preparing",
1610
+ completed: 1,
1611
+ total: 1
1612
+ }), g && await g({
1613
+ stage: "layout",
1614
+ completed: 0,
1615
+ total: 1
1616
+ }), m.assertUnchanged();
1617
+ let t = Ee(e, o, {
1618
+ fallback: d,
1619
+ repeatTableHeaders: p
1620
+ });
1621
+ if (g || m.captureBaseline(), g && await g({
1622
+ stage: "layout",
1623
+ completed: 1,
1624
+ total: 1
1625
+ }), g && await g({
1626
+ stage: "pagination",
1627
+ completed: 0,
1628
+ total: 1
1629
+ }), g && m.assertUnchanged(), t.width > r.width - r.margin.left - r.margin.right) throw new w("SOURCE_OVERFLOW", "Source is wider than the configured page content area. Set the source width explicitly; no implicit scaling is applied.");
1630
+ f === "css" && Object.assign(t, ie(e, t));
1631
+ let l = [], h = Ae(t, i.content.height - i.content.margin.top - i.content.margin.bottom, (e) => l.push(e));
1632
+ g && await g({
1633
+ stage: "pagination",
1634
+ completed: 1,
1635
+ total: 1
1636
+ });
1637
+ let _ = [...t.warnings, ...l];
1638
+ t.operations.some((e) => e.kind === "text") && !_.some((e) => e.code === "TEXT_METRICS_APPROXIMATED") && _.unshift({
1069
1639
  code: "TEXT_METRICS_APPROXIMATED",
1070
1640
  message: "PDF text uses browser word origins and approximate baseline/kerning metrics; full glyph shaping fidelity is not guaranteed."
1071
1641
  });
1072
- for (let e of p) o?.(Object.freeze(e));
1073
- o && u.assertUnchanged();
1074
- let m = await $(Q(t, i, r, f, {
1075
- rasterScale: s,
1642
+ for (let e of _) c?.(Object.freeze(e));
1643
+ (c || g) && m.assertUnchanged();
1644
+ let v = await q(We(t, o, r, h, {
1645
+ rasterScale: u,
1076
1646
  includeRasterText: n,
1077
- ...a ? { signal: a } : {}
1078
- }), a);
1079
- return u.assertUnchanged(), A(i), x(a), {
1080
- ...m,
1647
+ ...i.header ? { header: i.header } : {},
1648
+ ...i.footer ? { footer: i.footer } : {},
1649
+ ...a ? { metadata: a } : {},
1650
+ ...g ? { reportProgress: g } : {},
1651
+ ...s ? { signal: s } : {}
1652
+ }), s);
1653
+ return m.assertUnchanged(), A(o), T(s), g && (await g({
1654
+ stage: "complete",
1655
+ completed: 1,
1656
+ total: 1
1657
+ }), m.assertUnchanged(), A(o)), {
1658
+ ...v,
1081
1659
  snapshot: t,
1082
- warnings: p
1660
+ warnings: _
1083
1661
  };
1662
+ } catch (e) {
1663
+ return h.rethrow(e);
1084
1664
  } finally {
1085
- u.dispose();
1665
+ m.dispose();
1666
+ }
1667
+ }
1668
+ async function Ye(e, t) {
1669
+ let n = await Je(e, t);
1670
+ return qe(n.bytes, n.slices.length, n.warnings);
1671
+ }
1672
+ // src/image-geometry.ts
1673
+ var Xe = 4e7;
1674
+ function J(e, t) {
1675
+ if (!Number.isSafeInteger(e) || !Number.isSafeInteger(t) || e <= 0 || t <= 0) throw new w("INVALID_SOURCE", "Image dimensions must be positive safe integers.");
1676
+ if (e > 4e7 / t) throw new w("UNSUPPORTED_IMAGE", `Each image frame must contain at most ${Xe} pixels.`);
1677
+ }
1678
+ function Ze(e, t, n, r) {
1679
+ if (J(e, t), !Number.isInteger(n) || n < 1 || n > 8) throw new w("INVALID_SOURCE", "Image orientation must be an integer from 1 through 8.");
1680
+ let i = n >= 5 ? t : e, a = n >= 5 ? e : t, o = r.width - r.margin.left - r.margin.right, s = r.height - r.margin.top - r.margin.bottom, c = Math.min(o / i, s / a), l = i * c, u = a * c, d = r.margin.left + (o - l) / 2, f = r.margin.bottom + (s - u) / 2, [p, m, h, g, _, v] = [
1681
+ [
1682
+ 1,
1683
+ 0,
1684
+ 0,
1685
+ 1,
1686
+ 0,
1687
+ 0
1688
+ ],
1689
+ [
1690
+ -1,
1691
+ 0,
1692
+ 0,
1693
+ 1,
1694
+ 1,
1695
+ 0
1696
+ ],
1697
+ [
1698
+ -1,
1699
+ 0,
1700
+ 0,
1701
+ -1,
1702
+ 1,
1703
+ 1
1704
+ ],
1705
+ [
1706
+ 1,
1707
+ 0,
1708
+ 0,
1709
+ -1,
1710
+ 0,
1711
+ 1
1712
+ ],
1713
+ [
1714
+ 0,
1715
+ -1,
1716
+ -1,
1717
+ 0,
1718
+ 1,
1719
+ 1
1720
+ ],
1721
+ [
1722
+ 0,
1723
+ -1,
1724
+ 1,
1725
+ 0,
1726
+ 0,
1727
+ 1
1728
+ ],
1729
+ [
1730
+ 0,
1731
+ 1,
1732
+ 1,
1733
+ 0,
1734
+ 0,
1735
+ 0
1736
+ ],
1737
+ [
1738
+ 0,
1739
+ 1,
1740
+ -1,
1741
+ 0,
1742
+ 1,
1743
+ 0
1744
+ ]
1745
+ ][n - 1];
1746
+ return [
1747
+ p * l,
1748
+ m * u,
1749
+ h * l,
1750
+ g * u,
1751
+ d + _ * l,
1752
+ f + v * u
1753
+ ];
1754
+ }
1755
+ // src/image-input.ts
1756
+ function Y(e) {
1757
+ throw new w("INVALID_SOURCE", e);
1758
+ }
1759
+ var Qe = Uint32Array.from({ length: 256 }, (e, t) => {
1760
+ let n = t;
1761
+ for (let e = 0; e < 8; e += 1) n = n >>> 1 ^ (n & 1 ? 3988292384 : 0);
1762
+ return n >>> 0;
1763
+ });
1764
+ function $e(e, t, n) {
1765
+ let r = 4294967295;
1766
+ for (let i = t; i < n; i += 1) r = Qe[(r ^ e[i]) & 255] ^ r >>> 8;
1767
+ return (r ^ 4294967295) >>> 0;
1768
+ }
1769
+ function et(e) {
1770
+ let t = new DataView(e.buffer, e.byteOffset, e.byteLength), n = 8, r = 0, i = 0, a = -1, o = 0, s = 0, c = !1, l = !1, u = 1, d = !1, f = !1, p = 0;
1771
+ for (; n < e.length;) {
1772
+ n + 12 > e.length && Y("PNG contains a truncated chunk header.");
1773
+ let m = t.getUint32(n), h = n + 12 + m;
1774
+ h > e.length && Y("PNG chunk data extends beyond the input.");
1775
+ let g = e.subarray(n + 4, n + 8);
1776
+ g.some((e) => !(e >= 65 && e <= 90 || e >= 97 && e <= 122)) && Y("PNG chunk names must contain four ASCII letters.");
1777
+ let _ = String.fromCharCode(...g);
1778
+ $e(e, n + 4, h - 4) !== t.getUint32(h - 4) && Y(`PNG ${_} chunk checksum is invalid.`), n === 8 && _ !== "IHDR" && Y("PNG must begin with its IHDR chunk.");
1779
+ let v = n + 8;
1780
+ if (_ === "IHDR") (n !== 8 || m !== 13) && Y("PNG requires exactly one 13-byte IHDR chunk."), r = t.getUint32(v), i = t.getUint32(v + 4), J(r, i), o = e[v + 8], a = e[v + 9], {
1781
+ 0: [
1782
+ 1,
1783
+ 2,
1784
+ 4,
1785
+ 8,
1786
+ 16
1787
+ ],
1788
+ 2: [8, 16],
1789
+ 3: [
1790
+ 1,
1791
+ 2,
1792
+ 4,
1793
+ 8
1794
+ ],
1795
+ 4: [8, 16],
1796
+ 6: [8, 16]
1797
+ }[a]?.includes(o) || Y("PNG bit depth and color type are incompatible."), (e[v + 10] !== 0 || e[v + 11] !== 0 || e[v + 12] > 1) && Y("PNG compression, filtering or interlace settings are invalid.");
1798
+ else if (_ === "acTL" || _ === "fcTL" || _ === "fdAT") throw new w("UNSUPPORTED_IMAGE", "Animated PNG is unsupported; provide a static PNG or a multipage TIFF.");
1799
+ else if (_ === "PLTE") (s || d || m === 0 || m % 3 != 0 || m > 768 || a === 0 || a === 4) && Y("PNG palette is invalid or appears after image data."), s = m / 3, a === 3 && s > 2 ** o && Y("PNG palette exceeds its indexed bit depth.");
1800
+ else if (_ === "tRNS") (c || d || !(a === 0 && m === 2 || a === 2 && m === 6 || a === 3 && m > 0 && m <= s)) && Y("PNG transparency is incompatible with its color type or palette."), c = !0;
1801
+ else if (_ === "eXIf") l && Y("PNG requires at most one eXIf chunk."), u = X(e, v, h - 4), l = !0;
1802
+ else if (_ === "IDAT") (f || a === 3 && s === 0) && Y("PNG image data must be contiguous and follow its palette."), d = !0, p += m;
1803
+ else if (_ === "IEND") return m === 0 && d && p !== 0 && h === e.length || Y("PNG must end with IEND after nonempty image data."), {
1804
+ format: "png",
1805
+ width: r,
1806
+ height: i,
1807
+ orientation: u
1808
+ };
1809
+ else if (g[0] < 97) throw new w("UNSUPPORTED_IMAGE", `Unsupported critical PNG chunk: ${_}.`);
1810
+ d && _ !== "IDAT" && (f = !0), n = h;
1811
+ }
1812
+ Y("PNG is missing its final IEND chunk.");
1813
+ }
1814
+ function X(e, t, n) {
1815
+ let r = new DataView(e.buffer, e.byteOffset + t, n - t);
1816
+ r.byteLength < 8 && Y("Image EXIF TIFF header is truncated.");
1817
+ let i = r.getUint16(0);
1818
+ i !== 18761 && i !== 19789 && Y("Image EXIF byte order is invalid.");
1819
+ let a = i === 18761;
1820
+ r.getUint16(2, a) !== 42 && Y("Image EXIF requires a classic TIFF header.");
1821
+ let o = r.getUint32(4, a);
1822
+ (o < 8 || o + 2 > r.byteLength) && Y("Image EXIF IFD0 offset is outside its metadata segment.");
1823
+ let s = r.getUint16(o, a), c = o + 2 + s * 12;
1824
+ c + 4 > r.byteLength && Y("Image EXIF directory extends beyond its metadata segment.");
1825
+ let l = 1, u = !1;
1826
+ for (let e = 0; e < s; e += 1) {
1827
+ let t = o + 2 + e * 12;
1828
+ if (r.getUint16(t, a) !== 274) continue;
1829
+ let n = r.getUint16(t + 8, a);
1830
+ (u || r.getUint16(t + 2, a) !== 3 || r.getUint32(t + 4, a) !== 1 || n < 1 || n > 8) && Y("Image EXIF orientation must be one unambiguous SHORT value from 1 to 8."), l = n, u = !0;
1086
1831
  }
1832
+ let d = r.getUint32(c, a);
1833
+ return d !== 0 && (d < 8 || d + 2 > r.byteLength) && Y("Image EXIF thumbnail directory is outside its metadata segment."), l;
1087
1834
  }
1088
- async function Te(e, t) {
1089
- let n = await we(e, t);
1090
- return Ce(n.bytes, n.slices.length, n.warnings);
1835
+ function tt(e) {
1836
+ let t = new DataView(e.buffer, e.byteOffset, e.byteLength), n = 2, r = 0, i = 0, a = 1, o = !1, s = 0, c = !1, l = /* @__PURE__ */ new Set();
1837
+ for (; n < e.length;) {
1838
+ for (e[n] !== 255 && Y("JPEG marker structure is invalid."), n += 1; e[n] === 255;) n += 1;
1839
+ let u = e[n++];
1840
+ if ((u === void 0 || u === 0 || u === 216) && Y("JPEG contains an invalid or truncated marker."), u === 217) return s && c && n === e.length || Y("JPEG must end after a complete frame and scan."), {
1841
+ format: "jpeg",
1842
+ width: r,
1843
+ height: i,
1844
+ orientation: a
1845
+ };
1846
+ (u === 1 || u >= 208 && u <= 215) && Y("JPEG standalone markers are only supported inside scan data."), n + 2 > e.length && Y("JPEG segment length is truncated.");
1847
+ let d = t.getUint16(n), f = n + d;
1848
+ (d < 2 || f > e.length) && Y("JPEG segment extends beyond the input.");
1849
+ let p = n + 2;
1850
+ if (u >= 192 && u <= 207 && ![
1851
+ 196,
1852
+ 200,
1853
+ 204
1854
+ ].includes(u)) {
1855
+ if (![
1856
+ 192,
1857
+ 193,
1858
+ 194
1859
+ ].includes(u)) throw new w("UNSUPPORTED_IMAGE", "JPEG requires baseline, extended sequential or progressive Huffman encoding.");
1860
+ (s || d < 8) && Y("JPEG contains an invalid or duplicate frame header.");
1861
+ let n = e[p + 5];
1862
+ if (d !== 8 + n * 3 && Y("JPEG frame components are truncated."), e[p] !== 8 || ![
1863
+ 1,
1864
+ 3,
1865
+ 4
1866
+ ].includes(n)) throw new w("UNSUPPORTED_IMAGE", "JPEG requires 8-bit grayscale, RGB or CMYK pixels.");
1867
+ i = t.getUint16(p + 1), r = t.getUint16(p + 3), J(r, i);
1868
+ for (let t = 0; t < n; t += 1) {
1869
+ let n = p + 6 + t * 3, r = e[n], i = e[n + 1];
1870
+ (l.has(r) || !(i >> 4) || i >> 4 > 4 || !(i & 15) || (i & 15) > 4 || e[n + 2] > 3) && Y("JPEG frame component identifiers or sampling settings are invalid."), l.add(r);
1871
+ }
1872
+ s = u;
1873
+ } else if (u === 225 && d >= 6 && e[p] === 69 && e[p + 1] === 120 && e[p + 2] === 105 && e[p + 3] === 102) (o || d < 8 || e[p + 4] !== 0 || e[p + 5] !== 0) && Y("JPEG contains ambiguous or malformed EXIF APP1 metadata."), a = X(e, p + 6, f), o = !0;
1874
+ else if (u === 218) {
1875
+ let t = e[p];
1876
+ (!s || t === void 0 || t < 1 || t > l.size || d !== 6 + t * 2) && Y("JPEG scan header must follow a complete frame and select its components.");
1877
+ let r = /* @__PURE__ */ new Set();
1878
+ for (let n = 0; n < t; n += 1) {
1879
+ let t = e[p + 1 + n * 2], i = e[p + 2 + n * 2];
1880
+ (!l.has(t) || r.has(t) || i >> 4 > 3 || (i & 15) > 3) && Y("JPEG scan components or Huffman table selectors are invalid."), r.add(t);
1881
+ }
1882
+ let i = p + 1 + t * 2;
1883
+ (e[i] > e[i + 1] || e[i + 1] > 63 || e[i + 2] >> 4 > 13 || (e[i + 2] & 15) > 13 || s !== 194 && (e[i] !== 0 || e[i + 1] !== 63 || e[i + 2] !== 0)) && Y("JPEG scan spectral settings are invalid."), c = !0, n = f;
1884
+ let a = 0;
1885
+ for (; n < e.length;) {
1886
+ if (e[n] !== 255) {
1887
+ a += 1, n += 1;
1888
+ continue;
1889
+ }
1890
+ let t = n;
1891
+ for (n += 1; e[n] === 255;) n += 1;
1892
+ let r = e[n];
1893
+ if (r === 0 || r !== void 0 && r >= 208 && r <= 215) r === 0 && (a += 1), n += 1;
1894
+ else {
1895
+ n = t;
1896
+ break;
1897
+ }
1898
+ }
1899
+ a === 0 && Y("JPEG scan data is empty or truncated.");
1900
+ continue;
1901
+ } else if ([
1902
+ 220,
1903
+ 222,
1904
+ 223
1905
+ ].includes(u)) throw new w("UNSUPPORTED_IMAGE", "JPEG deferred-height and hierarchical frames are unsupported.");
1906
+ n = f;
1907
+ }
1908
+ Y("JPEG is missing its final EOI marker.");
1909
+ }
1910
+ function nt(e) {
1911
+ if (e.length >= 8 && [
1912
+ 137,
1913
+ 80,
1914
+ 78,
1915
+ 71,
1916
+ 13,
1917
+ 10,
1918
+ 26,
1919
+ 10
1920
+ ].every((t, n) => e[n] === t)) return et(e);
1921
+ if (e[0] === 255 && e[1] === 216) return tt(e);
1922
+ throw new w("UNSUPPORTED_IMAGE", "Provide PNG, JPEG or classic TIFF image bytes.");
1923
+ }
1924
+ async function rt(e, t) {
1925
+ T(t);
1926
+ let n = e.buffer instanceof ArrayBuffer ? new Uint8Array(e.buffer, e.byteOffset, e.byteLength) : Uint8Array.from(e), r = nt(n);
1927
+ if (r.format !== "png" && Y("PNG data validation requires PNG bytes."), typeof DecompressionStream != "function") throw new w("UNSUPPORTED_IMAGE", "PNG validation requires DecompressionStream support.");
1928
+ let i = {
1929
+ 0: 1,
1930
+ 2: 3,
1931
+ 3: 1,
1932
+ 4: 2,
1933
+ 6: 4
1934
+ }[n[25]] * n[24], a = (n[28] === 0 ? [[
1935
+ 0,
1936
+ 0,
1937
+ 1,
1938
+ 1
1939
+ ]] : [
1940
+ [
1941
+ 0,
1942
+ 0,
1943
+ 8,
1944
+ 8
1945
+ ],
1946
+ [
1947
+ 4,
1948
+ 0,
1949
+ 8,
1950
+ 8
1951
+ ],
1952
+ [
1953
+ 0,
1954
+ 4,
1955
+ 4,
1956
+ 8
1957
+ ],
1958
+ [
1959
+ 2,
1960
+ 0,
1961
+ 4,
1962
+ 4
1963
+ ],
1964
+ [
1965
+ 0,
1966
+ 2,
1967
+ 2,
1968
+ 4
1969
+ ],
1970
+ [
1971
+ 1,
1972
+ 0,
1973
+ 2,
1974
+ 2
1975
+ ],
1976
+ [
1977
+ 0,
1978
+ 1,
1979
+ 1,
1980
+ 2
1981
+ ]
1982
+ ]).flatMap(([e = 0, t = 0, n = 1, a = 1]) => {
1983
+ let o = Math.max(0, Math.ceil((r.width - e) / n)), s = Math.max(0, Math.ceil((r.height - t) / a));
1984
+ return o && s ? [{
1985
+ rows: s,
1986
+ stride: Math.ceil(o * i / 8) + 1
1987
+ }] : [];
1988
+ }), o = a.reduce((e, t) => e + t.rows * t.stride, 0), s = new DataView(n.buffer, n.byteOffset, n.byteLength), c = 8, l = 0, u = 0, d = new ReadableStream({ pull(e) {
1989
+ for (; l === u && c < n.length;) {
1990
+ let e = s.getUint32(c), t = c + 8, r = n[c + 4] === 73 && n[c + 5] === 68 && n[c + 6] === 65 && n[c + 7] === 84;
1991
+ c += e + 12, r && e > 0 && (l = t, u = t + e);
1992
+ }
1993
+ if (l === u) {
1994
+ e.close();
1995
+ return;
1996
+ }
1997
+ let t = Math.min(l + 8192, u);
1998
+ e.enqueue(n.subarray(l, t)), l = t;
1999
+ } }).pipeThrough(new DecompressionStream("deflate")).getReader(), f = () => {
2000
+ d.cancel().catch(() => {});
2001
+ };
2002
+ t?.addEventListener("abort", f, { once: !0 });
2003
+ let p = !1, m = 0, h = 0, g = a[0]?.rows ?? 0, _ = 0;
2004
+ try {
2005
+ for (;;) {
2006
+ T(t);
2007
+ let e = await d.read();
2008
+ if (T(t), e.done) break;
2009
+ m += e.value.length, m > o && Y("PNG decompressed data exceeds its declared scanline dimensions.");
2010
+ let n = 0;
2011
+ for (; n < e.value.length;) {
2012
+ let t = a[h];
2013
+ t || Y("PNG decompressed data contains unexpected image rows."), _ === 0 && (e.value[n] > 4 && Y("PNG scanline filter must be between zero and four."), n += 1, _ = 1);
2014
+ let r = Math.min(e.value.length - n, t.stride - _);
2015
+ n += r, _ += r, _ === t.stride && (_ = 0, --g, g === 0 && (h += 1, g = a[h]?.rows ?? 0));
2016
+ }
2017
+ }
2018
+ (m !== o || h !== a.length || _ !== 0) && Y("PNG decompressed data is shorter than its declared scanline dimensions."), p = !0;
2019
+ } catch (e) {
2020
+ throw T(t), e instanceof w ? e : new w("INVALID_SOURCE", "PNG compressed data or zlib checksum is invalid.", { cause: e });
2021
+ } finally {
2022
+ t?.removeEventListener("abort", f), p || await d.cancel().catch(() => {}), d.releaseLock();
2023
+ }
2024
+ }
2025
+ // src/tiff.ts
2026
+ var it = 1e3, at = [
2027
+ 0,
2028
+ 1,
2029
+ 1,
2030
+ 2,
2031
+ 4,
2032
+ 8,
2033
+ 1,
2034
+ 1,
2035
+ 2,
2036
+ 4,
2037
+ 8,
2038
+ 4,
2039
+ 8,
2040
+ 4
2041
+ ], ot = /* @__PURE__ */ new Set([
2042
+ 256,
2043
+ 257,
2044
+ 258,
2045
+ 259,
2046
+ 262,
2047
+ 266,
2048
+ 273,
2049
+ 274,
2050
+ 277,
2051
+ 278,
2052
+ 279,
2053
+ 284,
2054
+ 292,
2055
+ 293,
2056
+ 317,
2057
+ 320,
2058
+ 322,
2059
+ 323,
2060
+ 324,
2061
+ 325,
2062
+ 330,
2063
+ 338,
2064
+ 339
2065
+ ]);
2066
+ function Z(e) {
2067
+ throw new w("INVALID_SOURCE", `Invalid TIFF: ${e}`);
2068
+ }
2069
+ function Q(e) {
2070
+ throw new w("UNSUPPORTED_IMAGE", `Unsupported TIFF: ${e}`);
2071
+ }
2072
+ function $(e, t, n) {
2073
+ let r = e[`t${t}`];
2074
+ return !r && n !== void 0 ? n : (r && r.length === 1 && Number.isSafeInteger(r[0]) || Z(`tag ${t} requires one integer value.`), r[0]);
2075
+ }
2076
+ function st(e, t) {
2077
+ let n = $(e, 256), r = $(e, 257);
2078
+ J(n, r);
2079
+ let i = $(e, 274, 1);
2080
+ (i < 1 || i > 8) && Z("orientation must be between 1 and 8.");
2081
+ let a = $(e, 259, 1);
2082
+ [
2083
+ 1,
2084
+ 2,
2085
+ 3,
2086
+ 4,
2087
+ 5,
2088
+ 8,
2089
+ 32946,
2090
+ 32773
2091
+ ].includes(a) || Q(`compression ${a}; use uncompressed, CCITT, LZW, Deflate or PackBits strips.`), [
2092
+ 322,
2093
+ 323,
2094
+ 324,
2095
+ 325,
2096
+ 330
2097
+ ].some((t) => e[`t${t}`]) && Q("tiled images and SubIFDs are not supported."), $(e, 284, 1) !== 1 && Q("separate planar samples are not supported.");
2098
+ let o = $(e, 277, 1), s = e.t258 ?? [1], c = s[0], l = e.t339 ?? [1];
2099
+ (![1, o].includes(s.length) || s.some((e) => e !== c) || ![1, o].includes(l.length) || l.some((e) => e !== 1)) && Q("sample depths must match and samples must be unsigned integers.");
2100
+ let u = $(e, 262), d = o === 1 && (u === 0 && [
2101
+ 1,
2102
+ 4,
2103
+ 8
2104
+ ].includes(c) || u === 1 && [
2105
+ 1,
2106
+ 2,
2107
+ 8
2108
+ ].includes(c)), f = u === 2 && c === 8 && [3, 4].includes(o), p = u === 3 && o === 1 && [
2109
+ 1,
2110
+ 2,
2111
+ 4,
2112
+ 8
2113
+ ].includes(c);
2114
+ d || f || p || Q("photometric interpretation, channels or bit depth.");
2115
+ let m = e.t338 ?? [];
2116
+ o !== 4 || m.length === 1 && [1, 2].includes(m[0]) || Q("four-channel RGB requires an explicit associated or unassociated alpha sample."), o !== 4 && m.length > 0 && Q("extra samples are unsupported in this pixel mode."), p && e.t320?.length !== 3 * (1 << c) && Z("the palette has an invalid size."), p && e.t320.some((e) => e > 65535) && Z("palette entries must be sixteen-bit values.");
2117
+ let h = $(e, 266, 1), g = [
2118
+ 2,
2119
+ 3,
2120
+ 4
2121
+ ].includes(a);
2122
+ [1, 2].includes(h) || Z("fill order must be 1 or 2."), h === 2 && !g && Q("reversed fill order is supported only for CCITT scans."), g && (!d || c !== 1 || o !== 1) && Q("CCITT compression requires bilevel grayscale."), a === 3 && ![0, 1].includes($(e, 292, 0)) && Q("unsupported Group 3 options."), a === 4 && $(e, 293, 0) !== 0 && Q("uncompressed Group 4 extension mode.");
2123
+ let _ = $(e, 317, 1);
2124
+ _ !== 1 && !(_ === 2 && c === 8 && [
2125
+ 1,
2126
+ 5,
2127
+ 8,
2128
+ 32946,
2129
+ 32773
2130
+ ].includes(a)) && Q("only normal and eight-bit horizontal differencing predictors are supported.");
2131
+ let v = Math.min($(e, 278, r), r);
2132
+ v <= 0 && Z("RowsPerStrip must be positive.");
2133
+ let y = e.t273, b = e.t279;
2134
+ y && b && y.length === b.length && y.length === Math.ceil(r / v) || Z("strip offsets and byte counts must cover exactly the declared rows.");
2135
+ for (let e = 0; e < y.length; e += 1) {
2136
+ let n = y[e], r = b[e];
2137
+ (n < 8 || r <= 0 || n > t - r) && Z("a strip extends outside the input.");
2138
+ }
2139
+ return {
2140
+ tags: e,
2141
+ width: n,
2142
+ height: r,
2143
+ orientation: i,
2144
+ bits: c,
2145
+ samples: o,
2146
+ compression: a,
2147
+ rowsPerStrip: v,
2148
+ offsets: y,
2149
+ counts: b,
2150
+ associatedAlpha: o === 4 && m[0] === 1
2151
+ };
2152
+ }
2153
+ function ct(e) {
2154
+ e.byteLength < 8 && Z("the header is truncated.");
2155
+ let t = e[0] === 73 && e[1] === 73;
2156
+ t || e[0] === 77 && e[1] === 77 || Z("byte order must be II or MM.");
2157
+ let n = new DataView(e.buffer, e.byteOffset, e.byteLength), r = n.getUint16(2, t);
2158
+ r === 43 && Q("BigTIFF is not supported."), r !== 42 && Z("the classic TIFF signature is missing.");
2159
+ let i = n.getUint32(4, t), a = /* @__PURE__ */ new Set(), o = [];
2160
+ for (; i !== 0;) {
2161
+ a.has(i) && Z("the main IFD chain contains a cycle."), o.length >= it && Q("the file exceeds the 1000 frame limit."), a.add(i), (i < 8 || i > e.length - 2) && Z("an IFD offset is outside the input.");
2162
+ let r = n.getUint16(i, t), s = i + 2 + r * 12;
2163
+ s > e.length - 4 && Z("an IFD directory is truncated.");
2164
+ let c = {};
2165
+ for (let a = 0; a < r; a += 1) {
2166
+ let r = i + 2 + a * 12, o = n.getUint16(r, t), s = n.getUint16(r + 2, t), l = n.getUint32(r + 4, t), u = at[s];
2167
+ u && l !== 0 || Z("an IFD field has an invalid type or count.");
2168
+ let d = u * l, f = d <= 4 ? r + 8 : n.getUint32(r + 8, t);
2169
+ if (f > e.length - d && Z("an IFD value extends outside the input."), !ot.has(o)) continue;
2170
+ [3, 4].includes(s) || Q(`rendering tag ${o} must use SHORT or LONG values.`), c[`t${o}`] && Z(`rendering tag ${o} occurs more than once.`), l > 1e6 && Q("a rendering tag contains too many values.");
2171
+ let p = [];
2172
+ for (let e = 0; e < l; e += 1) p.push(s === 3 ? n.getUint16(f + e * 2, t) : n.getUint32(f + e * 4, t));
2173
+ c[`t${o}`] = p;
2174
+ }
2175
+ o.push(st(c, e.length)), i = n.getUint32(s, t);
2176
+ }
2177
+ return o.length === 0 && Z("no main image IFD exists."), o;
2178
+ }
2179
+ function lt(e, t) {
2180
+ let n = 0, r = 0;
2181
+ for (; n < e.length;) {
2182
+ let i = e[n++];
2183
+ if (i <= 127) {
2184
+ let t = i + 1;
2185
+ n + t > e.length && Z("a PackBits literal is truncated."), n += t, r += t;
2186
+ } else i !== 128 && (n >= e.length && Z("a PackBits repeat is truncated."), n += 1, r += 257 - i);
2187
+ r > t && Z("PackBits output exceeds the declared strip size.");
2188
+ }
2189
+ r !== t && Z("PackBits output does not cover the declared strip.");
2190
+ }
2191
+ function ut(e, t) {
2192
+ let n = /* @__PURE__ */ new Uint32Array(4096);
2193
+ for (let e = 0; e < 256; e += 1) n[e] = 1;
2194
+ let r = 0, i = 9, a = 258, o = 0, s = 0, c = !1;
2195
+ for (; r + i <= e.length * 8;) {
2196
+ let l = 0;
2197
+ for (let t = 0; t < i; t += 1) l = l << 1 | e[r >>> 3] >>> 7 - (r & 7) & 1, r += 1;
2198
+ if (l === 256) {
2199
+ i = 9, a = 258, o = 0, c = !0;
2200
+ continue;
2201
+ }
2202
+ if (c || Z("an LZW strip must start with its clear code."), l === 257) {
2203
+ s !== t && Z("LZW output does not cover the declared strip.");
2204
+ return;
2205
+ }
2206
+ let u = l < a ? n[l] : l === a && o > 0 ? o + 1 : 0;
2207
+ u || Z("an LZW strip contains an invalid dictionary reference."), s += u, s > t && Z("LZW output exceeds the declared strip size."), o > 0 && a < 4096 && (n[a++] = o + 1, i < 12 && a === (1 << i) - 1 && (i += 1)), o = u;
2208
+ }
2209
+ Z("an LZW strip is missing its end code.");
2210
+ }
2211
+ async function dt(e, t, n) {
2212
+ typeof DecompressionStream != "function" && Q("Deflate requires DecompressionStream support.");
2213
+ let r = new Blob([Uint8Array.from(e)]).stream().pipeThrough(new DecompressionStream("deflate")).getReader(), i = 0;
2214
+ try {
2215
+ for (;;) {
2216
+ T(n);
2217
+ let { value: e, done: a } = await r.read();
2218
+ if (a) break;
2219
+ i += e.byteLength, i > t && Z("Deflate output exceeds the declared strip size.");
2220
+ }
2221
+ i !== t && Z("Deflate output does not cover the declared strip.");
2222
+ } catch (e) {
2223
+ throw await r.cancel().catch(() => void 0), T(n), e instanceof w ? e : new w("INVALID_SOURCE", "Invalid TIFF: a Deflate strip is corrupt or truncated.", { cause: e });
2224
+ } finally {
2225
+ r.releaseLock();
2226
+ }
2227
+ }
2228
+ async function ft(e, t) {
2229
+ T(t);
2230
+ let n = Uint8Array.from(e), r = ct(n);
2231
+ return T(t), {
2232
+ pageCount: r.length,
2233
+ async decodeFrame(e) {
2234
+ T(t), (!Number.isSafeInteger(e) || e < 0 || e >= r.length) && Z("frame index is outside the document.");
2235
+ let i = r[e], a = Math.ceil(i.width * i.samples * i.bits / 8);
2236
+ for (let e = 0; e < i.offsets.length; e += 1) {
2237
+ let r = Math.min(i.rowsPerStrip, i.height - e * i.rowsPerStrip) * a, o = i.offsets[e], s = i.counts[e];
2238
+ i.compression === 1 && s !== r && Z("an uncompressed strip has the wrong byte length."), i.compression === 5 && ut(n.subarray(o, o + s), r), i.compression === 32773 && lt(n.subarray(o, o + s), r), [8, 32946].includes(i.compression) && await dt(n.subarray(o, o + s), r, t);
2239
+ }
2240
+ let o = Object.fromEntries(Object.entries(i.tags).map(([e, t]) => [e, [...t]])), s;
2241
+ try {
2242
+ let e = (await import("utif2")).default;
2243
+ T(t), e.decodeImage(n.buffer, o), o.data instanceof Uint8Array && o.data.length === a * i.height || Z("the decoder returned an incomplete raster."), s = e.toRGBA8(o);
2244
+ } catch (e) {
2245
+ throw T(t), e instanceof w ? e : new w("RESOURCE_LOAD_FAILED", "The TIFF frame could not be decoded.", { cause: e });
2246
+ }
2247
+ if (s.length !== i.width * i.height * 4 && Z("the decoder returned an invalid RGBA raster."), i.associatedAlpha) for (let e = 0; e < s.length; e += 4) {
2248
+ let t = s[e + 3];
2249
+ for (let n = 0; n < 3; n += 1) s[e + n] = t === 0 ? 0 : Math.min(255, Math.round(s[e + n] * 255 / t));
2250
+ }
2251
+ return T(t), {
2252
+ width: i.width,
2253
+ height: i.height,
2254
+ rgba: s,
2255
+ orientation: i.orientation
2256
+ };
2257
+ }
2258
+ };
2259
+ }
2260
+ // src/image-pdf.ts
2261
+ function pt(e, t) {
2262
+ let { width: n, height: r, rgba: i, orientation: a } = t;
2263
+ J(n, r);
2264
+ let o = n * r;
2265
+ if (i.length !== o * 4) throw new w("RESOURCE_LOAD_FAILED", "Decoded TIFF pixels do not match the frame dimensions.");
2266
+ let s = new Uint8Array(o * 3), c;
2267
+ for (let e = 0; e < o; e += 1) {
2268
+ s[e * 3] = i[e * 4], s[e * 3 + 1] = i[e * 4 + 1], s[e * 3 + 2] = i[e * 4 + 2];
2269
+ let t = i[e * 4 + 3];
2270
+ t !== 255 && !c && (c = new Uint8Array(o).fill(255)), c && (c[e] = t);
2271
+ }
2272
+ let l = c ? e.context.register(e.context.flateStream(c, {
2273
+ Type: "XObject",
2274
+ Subtype: "Image",
2275
+ Width: n,
2276
+ Height: r,
2277
+ ColorSpace: "DeviceGray",
2278
+ BitsPerComponent: 8
2279
+ })) : void 0;
2280
+ return {
2281
+ ref: e.context.register(e.context.flateStream(s, {
2282
+ Type: "XObject",
2283
+ Subtype: "Image",
2284
+ Width: n,
2285
+ Height: r,
2286
+ ColorSpace: "DeviceRGB",
2287
+ BitsPerComponent: 8,
2288
+ ...l ? { SMask: l } : {}
2289
+ })),
2290
+ width: n,
2291
+ height: r,
2292
+ orientation: a
2293
+ };
2294
+ }
2295
+ async function mt(e, t) {
2296
+ try {
2297
+ return new Uint8Array(await q(e.arrayBuffer(), t));
2298
+ } catch (e) {
2299
+ throw T(t), new w("RESOURCE_LOAD_FAILED", "The source image file could not be read.", { cause: e });
2300
+ }
2301
+ }
2302
+ async function ht(e, t = {}) {
2303
+ let n = L(t), i = ae(t.metadata), { signal: a, onProgress: o } = t;
2304
+ if (o !== void 0 && typeof o != "function") throw new w("INVALID_OPTIONS", "onProgress must be a function.");
2305
+ if (a !== void 0 && !(a instanceof AbortSignal)) throw new w("INVALID_OPTIONS", "signal must be an AbortSignal.");
2306
+ T(a);
2307
+ let s = e instanceof Uint8Array ? Uint8Array.from(e) : e instanceof ArrayBuffer ? new Uint8Array(e.slice(0)) : void 0, c = typeof Blob < "u" && e instanceof Blob ? e : void 0;
2308
+ if (!s && !c) throw new w("INVALID_SOURCE", "Provide image bytes, an ArrayBuffer, or a File/Blob.");
2309
+ let d = Ke(o, a), f = d.report;
2310
+ try {
2311
+ f && await f({
2312
+ stage: "preparing",
2313
+ completed: 0,
2314
+ total: 1
2315
+ });
2316
+ let e = s ?? await mt(c, a);
2317
+ if (e.length === 0) throw new w("INVALID_SOURCE", "The source image is empty.");
2318
+ let t = e.length >= 4 && (e[0] === 73 && e[1] === 73 && (e[2] === 42 || e[2] === 43) && e[3] === 0 || e[0] === 77 && e[1] === 77 && e[2] === 0 && (e[3] === 42 || e[3] === 43)) ? await q(ft(e, a), a) : void 0, o = t ? void 0 : nt(e), d = t?.pageCount ?? 1;
2319
+ f && await f({
2320
+ stage: "preparing",
2321
+ completed: 1,
2322
+ total: 1
2323
+ });
2324
+ let h = await r.create();
2325
+ oe(h, i);
2326
+ let g = [];
2327
+ f && await f({
2328
+ stage: "resources",
2329
+ completed: 0,
2330
+ total: d
2331
+ });
2332
+ for (let n = 0; n < d; n += 1) {
2333
+ if (T(a), t) {
2334
+ let e = await q(t.decodeFrame(n), a);
2335
+ g.push(pt(h, e));
2336
+ } else try {
2337
+ o.format === "png" && await rt(e, a);
2338
+ let t = o.format === "png" ? await h.embedPng(e) : await h.embedJpg(e);
2339
+ if (t.width !== o.width || t.height !== o.height) throw new w("INVALID_SOURCE", "Decoded image dimensions disagree with its header.");
2340
+ g.push({
2341
+ ref: t.ref,
2342
+ width: t.width,
2343
+ height: t.height,
2344
+ orientation: o.orientation
2345
+ });
2346
+ } catch (e) {
2347
+ throw e instanceof w ? e : new w("RESOURCE_LOAD_FAILED", "The PNG/JPEG image could not be embedded.", { cause: e });
2348
+ }
2349
+ f && await f({
2350
+ stage: "resources",
2351
+ completed: n + 1,
2352
+ total: d
2353
+ });
2354
+ }
2355
+ f && await f({
2356
+ stage: "pages",
2357
+ completed: 0,
2358
+ total: d
2359
+ });
2360
+ for (let [e, t] of g.entries()) {
2361
+ T(a);
2362
+ let r = h.addPage([n.width * .75, n.height * .75]), i = Ze(t.width, t.height, t.orientation, n), o = r.node.newXObject("Image", t.ref);
2363
+ r.pushOperators(m(), l(i[0] * .75, i[1] * .75, i[2] * .75, i[3] * .75, i[4] * .75, i[5] * .75), u(o), p()), f && await f({
2364
+ stage: "pages",
2365
+ completed: e + 1,
2366
+ total: d
2367
+ });
2368
+ }
2369
+ f && await f({
2370
+ stage: "saving",
2371
+ completed: 0,
2372
+ total: 1
2373
+ });
2374
+ let _ = await q(h.save(), a);
2375
+ return T(a), f && await f({
2376
+ stage: "saving",
2377
+ completed: 1,
2378
+ total: 1
2379
+ }), f && await f({
2380
+ stage: "complete",
2381
+ completed: 1,
2382
+ total: 1
2383
+ }), T(a), qe(_, d, []);
2384
+ } catch (e) {
2385
+ return d.rethrow(e instanceof w ? e : new w("PDF_WRITE_FAILED", "The standalone image could not be written as PDF.", { cause: e }));
2386
+ }
1091
2387
  }
1092
- export { b as RenderPDFError, se as registerFont, Te as renderPDF };
2388
+ export { w as RenderPDFError, ht as imageToPDF, de as registerFont, Ye as renderPDF };