@reportwright/cli 0.11.0 → 0.12.1

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/pw.mjs CHANGED
@@ -3849,9 +3849,23 @@ function nodeRequest(url, init, trusted) {
3849
3849
  for (let i = 0; i < res.rawHeaders.length; i += 2) pairs2.push([res.rawHeaders[i], res.rawHeaders[i + 1]]);
3850
3850
  const none = [101, 204, 205, 304].includes(res.statusCode) || (init.method || "GET").toUpperCase() === "HEAD";
3851
3851
  if (none) res.resume();
3852
- resolve(new Response(none ? null : stream.Readable.toWeb(res), { status: res.statusCode, statusText: res.statusMessage, headers: (
3852
+ const enc = String(res.headers["content-encoding"] || "").trim().toLowerCase();
3853
+ let body = res, decoded = false;
3854
+ if (!none && enc && enc !== "identity") {
3855
+ const zlib = nodeMod("node:zlib");
3856
+ const dec2 = enc === "gzip" || enc === "x-gzip" ? zlib.createGunzip() : enc === "deflate" ? zlib.createInflate() : enc === "br" ? zlib.createBrotliDecompress() : null;
3857
+ if (!dec2) {
3858
+ res.resume();
3859
+ return reject(Object.assign(new Error(`the response is encoded as "${enc}", which the data fetch cannot decode`), { status: 415 }));
3860
+ }
3861
+ body = stream.pipeline(res, dec2, () => {
3862
+ });
3863
+ decoded = true;
3864
+ }
3865
+ const kept = decoded ? pairs2.filter(([k]) => !/^(content-encoding|content-length)$/i.test(k)) : pairs2;
3866
+ resolve(new Response(none ? null : stream.Readable.toWeb(body), { status: res.statusCode, statusText: res.statusMessage, headers: (
3853
3867
  /** @type {any} */
3854
- pairs2
3868
+ kept
3855
3869
  ) }));
3856
3870
  });
3857
3871
  req.on("error", reject);
@@ -3869,7 +3883,7 @@ function redirectRequest(from, to, req, status) {
3869
3883
  }
3870
3884
  function guardedFetch(opt, base) {
3871
3885
  const allow = (opt.allowHosts || []).map((h) => String(h).toLowerCase().replace(/^\[|\]$/g, ""));
3872
- const node2 = inNode();
3886
+ const node2 = !opt.viaBase && inNode();
3873
3887
  return (
3874
3888
  /** @type {any} */
3875
3889
  (async (input, init = {}) => {
@@ -4802,7 +4816,10 @@ function fetcherFor(opt) {
4802
4816
  return boundedFetch(f4, opt);
4803
4817
  }
4804
4818
  function checkHostSource(src, url, opt) {
4805
- if (opt.fetch || opt.unsafeFetch) return;
4819
+ const rule = opt.fetch ? opt.fetch.hostRule : opt.unsafeFetch ? null : opt;
4820
+ if (!rule) return;
4821
+ const scheme = /^([a-z][a-z0-9+.-]*):/i.exec(String(url).trim());
4822
+ if (scheme && !/^https?$/i.test(scheme[1])) throw Object.assign(new Error(`Only http and https URLs can be fetched, not ${scheme[1].toLowerCase()}:`), { status: 400 });
4806
4823
  const t = String(src.url ?? "").trim();
4807
4824
  if (/^\/(?![/\\])/.test(t)) return;
4808
4825
  const m = /^https?:\/\/([^/?#\\]*)/i.exec(t);
@@ -4813,7 +4830,7 @@ function checkHostSource(src, url, opt) {
4813
4830
  host = new URL(url).hostname.replace(/^\[|\]$/g, "").toLowerCase();
4814
4831
  } catch {
4815
4832
  }
4816
- if (whole && host && (opt.allowHosts || []).map((h) => String(h).toLowerCase()).includes(host)) return;
4833
+ if (whole && host && (rule.allowHosts || []).map((h) => String(h).toLowerCase()).includes(host)) return;
4817
4834
  throw Object.assign(new Error("its host is made by a parameter or an expression: write the scheme and host out (parameters may fill the path and query), or make the whole URL a parameter and list its host in allowHosts"), { status: 403 });
4818
4835
  }
4819
4836
  async function loadSources(def, opt) {
@@ -11348,7 +11365,8 @@ function toRdlx3(root, notes, reportName) {
11348
11365
  const on = prop(b, "aggregateOn") ?? simpleList(b, "aggregateOn")[0]?.text;
11349
11366
  const gc = { ...c, group: on && on !== "All" ? on : void 0 };
11350
11367
  if (fn3) {
11351
- const arg = listProp(b, "arguments").map((x) => prop(x, "value")).find((x) => x != null);
11368
+ const arg = listProp(b, "arguments").map((x) => prop(x, "value")).find((x) => x != null) ?? prop(b, "expression");
11369
+ if (arg == null && fn3 !== "count") throw new Untranslatable2(`the aggregate ${fn3.toUpperCase()} with no expression`);
11352
11370
  const inner2 = arg != null ? vb2(arg, { ...c, inSet: false }, depth) : "1";
11353
11371
  if (prop(b, "filterExpr")) throw new Untranslatable2(`the bound column "${prop(b, "name")}", an aggregate with a filter`);
11354
11372
  const all = gc.group ?? c.region;
@@ -12126,6 +12144,7 @@ function special(cp) {
12126
12144
  if (cp >= 8192 && cp <= 8303) return cp >= 8203 && cp <= 8207 || cp >= 8234 && cp <= 8238 || cp >= 8288;
12127
12145
  return cp >= 11904 && cp <= 42191 || cp >= 43056 && cp <= 44031 || cp >= 44032 && cp <= 55215 || cp >= 63744 && cp <= 64255 || cp >= 64285 && cp <= 65023 || cp >= 65024 && cp <= 65039 || cp >= 65072 && cp <= 65103 || cp >= 65136 && cp <= 65278 || cp >= 65280 && cp <= 65519 || cp >= 65536;
12128
12146
  }
12147
+ var FORMAT_CHAR = new RegExp("^\\p{Cf}$", "u");
12129
12148
  var cjk = (cp) => cp >= 11904 && cp <= 40959 || cp >= 44032 && cp <= 55215 || cp >= 63744 && cp <= 64255 || cp >= 65280 && cp <= 65519 || cp >= 131072 && cp <= 262143;
12130
12149
  var NO_START = new Set("、。,.・:;?!ー)」』】〕〉》〗〙〟’”}⦆ぁぃぅぇぉっゃゅょゎゕゖァィゥェォッャュョヮヵヶ々〻‐゠–〜~…‥,.!?:;)]}%ゝゞヽヾ゛゜ㇰㇱㇲㇳㇴㇵㇶㇷㇸㇹㇺㇻㇼㇽㇾㇿ。」、・ー%]⦆");
12131
12150
  var NO_END = new Set("(「『【〔〈《〖〘〝‘“{⦅([{[「$¥£");
@@ -12521,7 +12540,7 @@ var TextMeasurer = class {
12521
12540
  }
12522
12541
  /** The font for one character: the chosen one, else the bundled fallback for its script, else the one before. */
12523
12542
  pick(cp, key, prev) {
12524
- if (cp < 32 || this.has(key, cp)) return key;
12543
+ if (cp < 32 || this.has(key, cp) || FORMAT_CHAR.test(String.fromCodePoint(cp))) return key;
12525
12544
  const fb = fallbackKey(cp, key);
12526
12545
  if (fb && !this.store.failed.has(fb)) {
12527
12546
  if (!this.store.has(fb)) {
@@ -13348,8 +13367,8 @@ function rgbOf(c) {
13348
13367
  if (h.length === 3) h = h.replace(/./g, (x) => x + x);
13349
13368
  return [0, 2, 4].map((i) => parseInt(h.slice(i, i + 2), 16) / 255);
13350
13369
  }
13351
- var lum = (rgb3) => {
13352
- const [r, g, b] = rgb3.map((v) => v <= 0.03928 ? v / 12.92 : ((v + 0.055) / 1.055) ** 2.4);
13370
+ var lum = (rgb4) => {
13371
+ const [r, g, b] = rgb4.map((v) => v <= 0.03928 ? v / 12.92 : ((v + 0.055) / 1.055) ** 2.4);
13353
13372
  return 0.2126 * r + 0.7152 * g + 0.0722 * b;
13354
13373
  };
13355
13374
  function contrast(a, b) {
@@ -13377,6 +13396,11 @@ function niceScale(min, max, ticks = 5) {
13377
13396
  const step3 = niceNum(range / Math.max(1, ticks - 1), true);
13378
13397
  return { lo: Math.floor(min / step3) * step3, hi: Math.ceil(max / step3) * step3, step: step3 };
13379
13398
  }
13399
+ function niceScaleSpan(min, max, ticks = 5) {
13400
+ if (!Number.isFinite(min) || !Number.isFinite(max) || !(max > min)) return niceScale(min, max, ticks);
13401
+ const step3 = niceNum((max - min) / Math.max(1, ticks - 1), true);
13402
+ return { lo: Math.floor(min / step3) * step3, hi: Math.ceil(max / step3) * step3, step: step3 };
13403
+ }
13380
13404
  function niceNum(x, round) {
13381
13405
  const e2 = Math.floor(Math.log10(x));
13382
13406
  const f4 = x / 10 ** e2;
@@ -14334,7 +14358,7 @@ var byName = (lc, pal) => (name) => {
14334
14358
  };
14335
14359
  function chartData(item, ctx, lc, pal = paletteOf(item.palette)) {
14336
14360
  let rows = item.dataSet ? related(lc.dataSets[item.dataSet] || [], ctx) : ctx.aggRows || [];
14337
- if (item.dataSet && !lc.dataSets[item.dataSet]) lc.warn(`${item.name}: the data set "${item.dataSet}" does not exist`);
14361
+ if (item.dataSet && !lc.dataSets[item.dataSet]) lc.warn(`${item.name || item.type}: the data set "${item.dataSet}" does not exist`);
14338
14362
  if (item.filters?.length) rows = applyFilters(rows, item.filters, ctx);
14339
14363
  if (item.sort?.length) rows = applySort(rows, item.sort, ctx);
14340
14364
  const who = item.name || "Chart";
@@ -14622,13 +14646,15 @@ function valueAxis(spec, lo, hi, minPos, { includeZero = true, len = 100, fs: fs
14622
14646
  lo = lo < 0 ? -1 : 0;
14623
14647
  hi = 1;
14624
14648
  }
14649
+ let padded = false;
14625
14650
  if (pad2 > 0 && hi > lo) {
14626
14651
  const d = (hi - lo) * pad2;
14627
- if (smin == null) lo -= d;
14628
- if (smax == null) hi += d;
14652
+ if (smin == null) lo = lo >= 0 && lo - d < 0 ? 0 : lo - d;
14653
+ if (smax == null) hi = hi <= 0 && hi + d > 0 ? 0 : hi + d;
14654
+ padded = true;
14629
14655
  }
14630
14656
  const want = len < fs2 * 6 ? 3 : 5;
14631
- const sc = niceScale(smin ?? lo, smax ?? hi, want);
14657
+ const sc = padded && smin == null && smax == null && (lo >= 0 || hi <= 0) ? niceScaleSpan(lo, hi, want) : niceScale(smin ?? lo, smax ?? hi, want);
14632
14658
  let a = sc.lo, b = sc.hi, ticks;
14633
14659
  if (smin != null || smax != null) {
14634
14660
  a = smin ?? sc.lo;
@@ -15056,31 +15082,27 @@ function rowPoints(type, g) {
15056
15082
  rows = rows.slice(0, cap2);
15057
15083
  }
15058
15084
  const blank2 = (v) => v == null || v === "";
15059
- const missing2 = /* @__PURE__ */ new Set();
15085
+ const drawn = /* @__PURE__ */ new Set();
15060
15086
  const sets = data.series.map((s, si) => {
15061
15087
  const pts2 = [];
15062
15088
  rows.forEach((r, ri) => {
15063
15089
  c.fields = r;
15064
15090
  const rx = safe2(item.xValue, c, lc, who), ry = safe2(s.def.value || "=0", { ...c, aggRows: [r] }, lc, who);
15065
- if (blank2(rx) || blank2(ry)) {
15066
- missing2.add(ri);
15067
- return;
15068
- }
15091
+ if (blank2(rx) || blank2(ry)) return;
15069
15092
  const x = Number(rx), y = Number(ry);
15070
- if (!Number.isFinite(x) || !Number.isFinite(y)) {
15071
- missing2.add(ri);
15072
- return;
15073
- }
15093
+ if (!Number.isFinite(x) || !Number.isFinite(y)) return;
15074
15094
  let size = 1;
15075
15095
  if (type === "bubble") {
15076
15096
  size = Number(safe2(s.def.size || "=1", { ...c, aggRows: [r] }, lc, who));
15077
15097
  if (!Number.isFinite(size) || size <= 0) return;
15078
15098
  }
15079
15099
  pts2.push({ x, y, size });
15100
+ drawn.add(ri);
15080
15101
  });
15081
15102
  return { s, pts: pts2, i: si };
15082
15103
  });
15083
- if (missing2.size) lc.warn(`${who}: ${missing2.size} of ${rows.length} rows have no x or y value and are not drawn`);
15104
+ const missing2 = rows.length - drawn.size;
15105
+ if (missing2 > 0) lc.warn(`${who}: ${missing2} of ${rows.length} rows have no x or y value and are not drawn`);
15084
15106
  return sets;
15085
15107
  }
15086
15108
  function paintXY(type, g) {
@@ -17512,7 +17534,7 @@ function hidden(item, ctx, lc) {
17512
17534
  const r = evalSafe(item.hidden, ctx, lc, item.name || item.type);
17513
17535
  h = !r.err && truthy(r.v);
17514
17536
  }
17515
- return item.name && lc.state?.toggles?.[`item:${item.name}`] ? !h : h;
17537
+ return item.name && lc.state?.toggles?.[`item:${item.name || item.type}`] ? !h : h;
17516
17538
  }
17517
17539
  var SHRINK_DEFAULTS = { minFontSize: 4, step: 0.5 };
17518
17540
  function rotationOf(item) {
@@ -17848,7 +17870,7 @@ register("table", {
17848
17870
  else {
17849
17871
  rows = item.dataSet && lc.dataSets[item.dataSet] || [];
17850
17872
  rows = related(rows, tctx);
17851
- if (item.dataSet && !lc.dataSets[item.dataSet]) lc.warn(`${item.name || "Table"}: the data set "${item.dataSet}" does not exist`);
17873
+ if (item.dataSet && !lc.dataSets[item.dataSet]) lc.warn(`${item.name || item.type}: the data set "${item.dataSet}" does not exist`);
17852
17874
  if (item.filters?.length) rows = applyFilters(rows, item.filters, tctx);
17853
17875
  const sort = lc.state?.sort?.[item.name] || item.sort;
17854
17876
  if (sort?.length) rows = applySort(rows, sort, tctx);
@@ -17864,11 +17886,11 @@ register("table", {
17864
17886
  if (overflow === "shrink" && room > 0) {
17865
17887
  const k = room / designedW;
17866
17888
  cols = cols.map((c) => ({ ...c, width: (Number(c.width) || 0) * k }));
17867
- lc.warn(`${item.name || "Table"}: its columns are ${Math.round(designedW)}pt wide, wider than the page (${Math.round(room)}pt), so they are narrowed to fit`);
17889
+ lc.warn(`${item.name || item.type}: its columns are ${Math.round(designedW)}pt wide, wider than the page (${Math.round(room)}pt), so they are narrowed to fit`);
17868
17890
  } else if (overflow === "clip" || overflow === "paginate" && lc.fixed) {
17869
- lc.warn(`${item.name || "Table"}: its columns are ${Math.round(designedW)}pt wide, wider than the page (${Math.round(room)}pt), so the columns past the page edge are cut`, true);
17891
+ lc.warn(`${item.name || item.type}: its columns are ${Math.round(designedW)}pt wide, wider than the page (${Math.round(room)}pt), so the columns past the page edge are cut`, true);
17870
17892
  } else if (overflow === "paginate" && item.overflow !== "paginate") {
17871
- lc.warn(`${item.name || "Table"}: its columns are ${Math.round(designedW)}pt wide, wider than the page (${Math.round(room)}pt), so they continue on the next pages`);
17893
+ lc.warn(`${item.name || item.type}: its columns are ${Math.round(designedW)}pt wide, wider than the page (${Math.round(room)}pt), so they continue on the next pages`);
17872
17894
  }
17873
17895
  const colX = [];
17874
17896
  let acc = item.x;
@@ -17878,13 +17900,14 @@ register("table", {
17878
17900
  }
17879
17901
  const width = acc - item.x;
17880
17902
  const region = { name: item.name, columns: cols.map((c) => c.width), rows: [] };
17903
+ if (lc.tablesSeen) lc.tablesSeen.n++;
17881
17904
  const tableScope = { rows, index: -1, run: [] };
17882
17905
  const scopes = { ...tctx.scopes || {}, [item.name]: tableScope };
17883
17906
  const groupChains = {};
17884
17907
  (item.groups || []).forEach((g, i) => {
17885
17908
  if (g.name) groupChains[g.name] = { rows, keys: item.groups.slice(0, i + 1).map((x) => x.expr) };
17886
17909
  });
17887
- const base = { ...tctx, aggRows: rows, aggIndex: void 0, fields: rows[0] || null, scopes, region: tableScope, group: void 0, groupChains: { ...tctx.groupChains || {}, ...groupChains } };
17910
+ const base = { ...tctx, aggRows: rows, aggIndex: void 0, fields: lc.win?.first || rows[0] || null, scopes, region: tableScope, group: void 0, groupChains: { ...tctx.groupChains || {}, ...groupChains } };
17888
17911
  const units = [];
17889
17912
  let y = 0;
17890
17913
  let repeatH = 0;
@@ -17901,7 +17924,7 @@ register("table", {
17901
17924
  const covered = (ci) => cover[ci] !== null;
17902
17925
  const cellsAt = /* @__PURE__ */ new WeakMap();
17903
17926
  const layRow = (row, ctx, kind, extra) => {
17904
- if (row.hidden != null && row.hidden !== false && row.hidden !== "" && hidden({ hidden: row.hidden, name: `${item.name || "Table"} row` }, ctx, lc)) {
17927
+ if (row.hidden != null && row.hidden !== false && row.hidden !== "" && hidden({ hidden: row.hidden, name: `${item.name || item.type} row` }, ctx, lc)) {
17905
17928
  if (openSpans.length) {
17906
17929
  for (const s of openSpans) s.left--;
17907
17930
  for (const s of openSpans) if (s.left <= 0) closeSpan(s);
@@ -17935,7 +17958,7 @@ register("table", {
17935
17958
  else try {
17936
17959
  v = evalValue(cell.value ?? "", cctx);
17937
17960
  } catch (e2) {
17938
- lc.warn(`${item.name || "Table"} cell: ${e2.message}`);
17961
+ lc.warn(`${item.name || item.type} cell: ${e2.message}`);
17939
17962
  v = "#Error";
17940
17963
  }
17941
17964
  let text2 = textOf(v, st, cctx, lc);
@@ -17978,7 +18001,7 @@ register("table", {
17978
18001
  cells.forEach((c, k) => {
17979
18002
  if (c.cell.visual?.type) {
17980
18003
  items.push(...paintBox({ x: c.x, y: 0, w: c.w, h, st: c.st, tl: null, m: lc.m }));
17981
- if (first) items.push(...paintVisual(c.cell.visual, { x: c.x, y: 0, w: c.w, h }, c.ctx, lc, c.st, `${item.name} cell`, { cell: true }));
18004
+ if (first) items.push(...paintVisual(c.cell.visual, { x: c.x, y: 0, w: c.w, h }, c.ctx, lc, c.st, `${item.name || item.type} cell`, { cell: true }));
17982
18005
  return;
17983
18006
  }
17984
18007
  if (c.nested) {
@@ -18033,7 +18056,7 @@ register("table", {
18033
18056
  };
18034
18057
  const layoutInCell = (inner2, x, cw, st, cctx, row) => {
18035
18058
  const pd = padding(st);
18036
- const it = { ...inner2, name: inner2.name || `${item.name}_cell`, x: x + pd[3], y: 0, w: Math.max(1, cw - pd[1] - pd[3]), h: Number(inner2.h) || Math.max(1, (row.height || 18) - pd[0] - pd[2]) };
18059
+ const it = { ...inner2, name: inner2.name || `${item.name || item.type}_cell`, x: x + pd[3], y: 0, w: Math.max(1, cw - pd[1] - pd[3]), h: Number(inner2.h) || Math.max(1, (row.height || 18) - pd[0] - pd[2]) };
18037
18060
  const scopeRows2 = cctx.aggIndex != null ? [cctx.fields] : cctx.aggRows || [];
18038
18061
  const current = item.dataSet ? { ...cctx.current || {}, [item.dataSet]: scopeRows2 } : cctx.current;
18039
18062
  const child = {
@@ -18053,7 +18076,7 @@ register("table", {
18053
18076
  const reads = (it.dataSet && it.dataSet !== item.dataSet ? child.ctxFor(it).aggRows?.length : scopeRows2.length) || 1;
18054
18077
  if (budget.left < reads) {
18055
18078
  budget.left = 0;
18056
- lc.warn(`${item.name}: more than ${CELL_ITEM_UNITS} rows of items inside cells; the rest are left empty`);
18079
+ lc.warn(`${item.name || item.type}: more than ${CELL_ITEM_UNITS} rows of items inside cells; the rest are left empty`);
18057
18080
  return { items: [], height: 0 };
18058
18081
  }
18059
18082
  budget.left -= reads;
@@ -18097,7 +18120,7 @@ register("table", {
18097
18120
  u.items.splice(at2 < 0 ? u.items.length : at2, 0, ...add2);
18098
18121
  });
18099
18122
  if (s.tl.needed > s.units[0].height + GROW_TOLERANCE || s.st.verticalAlign && s.st.verticalAlign !== "top") {
18100
- const id = `${item.name}:span:${lc.nextId()}`;
18123
+ const id = `${item.name || item.type}:span:${lc.nextId()}`;
18101
18124
  for (const u of s.units) u.block ||= id;
18102
18125
  } else {
18103
18126
  let above = s.units[0].height;
@@ -18122,7 +18145,8 @@ register("table", {
18122
18145
  openSpans = [];
18123
18146
  };
18124
18147
  const fl = base.bandRows === "first-last";
18125
- const headerUnits = (item.header || []).map((r) => layRow(r, fl && rows.length ? { ...base, pendingRows: [rows[0]] } : base, "header")).filter(Boolean);
18148
+ const first0 = lc.win?.first || rows[0];
18149
+ const headerUnits = (item.header || []).map((r) => layRow(r, fl && rows.length ? { ...base, pendingRows: [first0] } : base, "header")).filter(Boolean);
18126
18150
  endBand();
18127
18151
  const headerHeight = headerUnits.reduce((s, u) => s + u.height, 0);
18128
18152
  if (item.repeatHeader !== false) repeatH = headerHeight;
@@ -18136,9 +18160,9 @@ register("table", {
18136
18160
  y += tl.needed;
18137
18161
  }
18138
18162
  const groups = item.groups || [];
18139
- let rowNo = 0;
18163
+ let rowNo = lc.win?.rowOffset || 0;
18140
18164
  const groupTables = [];
18141
- const tableKey = `${item.name}#${lc.nextId()}`;
18165
+ const tableKey = `${item.name || item.type}#${lc.nextId()}`;
18142
18166
  const emit2 = (level, grows, ctx, chain, keyPath) => {
18143
18167
  if (level === groups.length) {
18144
18168
  for (let i = 0; i < grows.length; i++) {
@@ -18156,9 +18180,16 @@ register("table", {
18156
18180
  const dctx = { ...ctx, fields: grows[i], aggRows: grows, aggIndex: i, rowNumber: rowNo };
18157
18181
  const u0 = units.length;
18158
18182
  for (const r of item.detail || []) layRow(r, dctx, "detail", { level: groups.length });
18159
- if (groups.length && item.widowControl !== false) for (let k = u0; k < units.length; k++) units[k].grp = tableKey + keyPath;
18183
+ if (lc.win && units[u0]) {
18184
+ units[u0].rowHead = true;
18185
+ for (let k = u0; k < units.length; k++) units[k].dataRow = rowNo;
18186
+ }
18187
+ if (groups.length && item.widowControl !== false) for (let k = u0; k < units.length; k++) {
18188
+ units[k].grp = tableKey + keyPath;
18189
+ if (ctx.__before) units[k].grpBefore = ctx.__before;
18190
+ }
18160
18191
  const every = Math.trunc(Number(item.pageBreakEvery)) || 0;
18161
- if (every > 0 && rowNo > 1 && (rowNo - 1) % every === 0 && units[u0]) units[u0].breakBefore = true;
18192
+ if (every > 0 && rowNo > (lc.win?.rowOffset || 0) + 1 && (rowNo - 1) % every === 0 && units[u0]) units[u0].breakBefore = true;
18162
18193
  endBand();
18163
18194
  }
18164
18195
  return;
@@ -18193,7 +18224,7 @@ register("table", {
18193
18224
  kids = /* @__PURE__ */ new Map();
18194
18225
  roots = [];
18195
18226
  for (const b of buckets) {
18196
- const pk = evalSafe(g.parent, { ...ctx, fields: b.rows[0], aggRows: b.rows, aggIndex: void 0 }, lc, `${item.name} group parent`).v;
18227
+ const pk = evalSafe(g.parent, { ...ctx, fields: b.rows[0], aggRows: b.rows, aggIndex: void 0 }, lc, `${item.name || item.type} group parent`).v;
18197
18228
  const pr = pk == null || pk === "" ? null : byText.get(pk instanceof Date ? String(pk.getTime()) : String(pk));
18198
18229
  if (!pr || pr === b) roots.push(b);
18199
18230
  else {
@@ -18218,20 +18249,26 @@ register("table", {
18218
18249
  if (lc.rowBudget?.cut || shown2.has(bk)) return;
18219
18250
  shown2.add(bk);
18220
18251
  const sc = { rows: bk.rows, index: -1, run: [], number: bi + 1, prevRows: bi > 0 ? sibs[bi - 1].rows : null, siblings: sibRows(sibs), ...kids ? { level: depth, recRows: recOf.get(bk) } : {} };
18221
- const gctx = { ...ctx, fields: bk.rows[0], aggRows: bk.rows, aggIndex: void 0, scopes: { ...ctx.scopes, [gname]: sc }, groupKey: bk.key, group: sc };
18252
+ const cont = bi === 0 && (level === 0 || ctx.__cont) ? lc.win?.cont?.[level] : null;
18253
+ const gctx = { ...ctx, fields: cont ? cont.first : bk.rows[0], aggRows: bk.rows, aggIndex: void 0, scopes: { ...ctx.scopes, [gname]: sc }, groupKey: bk.key, group: sc, __cont: !!cont, __before: cont && level === groups.length - 1 ? cont.before : 0 };
18222
18254
  if (kids) {
18223
18255
  gctx.level = depth;
18224
18256
  if (g.indent) gctx.indent = (ctx.indent || 0) + depth * Number(g.indent);
18225
18257
  }
18226
18258
  const path2 = `${parentPath}/${bk.text}`;
18227
- const tkey = `${item.name}|${level}|${path2}`;
18259
+ const tkey = `${item.name || item.type}|${level}|${path2}`;
18228
18260
  const dflt = kids && g.expandLevels != null ? depth < Number(g.expandLevels) : !g.initiallyCollapsed;
18229
18261
  const open = !g.collapsible ? true : lc.state?.toggles?.[tkey] ?? dflt;
18230
18262
  const start = units.length;
18231
- const hctx = g.header?.length ? { ...gctx, pendingRows: fl ? [bk.rows[0]] : recOf.get(bk) || bk.rows } : gctx;
18232
- const hu = (g.header || []).map((r, ri) => layRow(r, hctx, "groupHeader", { level, ...ri === 0 && g.collapsible ? { toggle: { key: tkey, open } } : {} })).filter(Boolean);
18263
+ const hctx = g.header?.length ? { ...gctx, pendingRows: fl ? [cont ? cont.first : bk.rows[0]] : recOf.get(bk) || bk.rows } : gctx;
18264
+ const hu = (cont && !g.repeatHeader ? [] : g.header || []).map((r, ri) => layRow(r, hctx, "groupHeader", { level, ...ri === 0 && g.collapsible ? { toggle: { key: tkey, open } } : {} })).filter(Boolean);
18233
18265
  endBand();
18234
18266
  const before = units.length;
18267
+ if (lc.win && !cont && before > start) {
18268
+ for (let k = start; k < before; k++) units[k].dataRow = rowNo + 1;
18269
+ units[start].rowHead = true;
18270
+ units[start].headLevel = level;
18271
+ }
18235
18272
  const rep = g.repeatHeader && before > start && !lc.fixed ? [...ctx.__rep || [], (() => {
18236
18273
  const hdr = units.slice(start, before), h0 = hdr[0].top, its = [];
18237
18274
  for (const u of hdr) for (const it of shiftLocal(u.items.filter((i) => i.t !== "link" && i.t !== "bookmark"), u.top - h0)) its.push(it);
@@ -18241,7 +18278,7 @@ register("table", {
18241
18278
  const ch = kids?.get(bk) || [];
18242
18279
  if (open) {
18243
18280
  emit2(level + 1, bk.rows, gctx, [...chain, sc], path2);
18244
- if (ch.length && depth + 1 >= MAX_TREE_DEPTH) lc.warn(`${item.name}: the group "${gname}" is more than ${MAX_TREE_DEPTH} levels deep; deeper rows are listed at the top level`);
18281
+ if (ch.length && depth + 1 >= MAX_TREE_DEPTH) lc.warn(`${item.name || item.type}: the group "${gname}" is more than ${MAX_TREE_DEPTH} levels deep; deeper rows are listed at the top level`);
18245
18282
  else ch.forEach((c, ci) => node2(c, ci, ch, depth + 1, path2));
18246
18283
  } else {
18247
18284
  const rs = recOf.get(bk) || bk.rows;
@@ -18270,8 +18307,11 @@ register("table", {
18270
18307
  if (g.newSection) units[start].sectionStart = true;
18271
18308
  if (g.pageBreak === "after" && bi < sibs.length - 1) units[units.length - 1].breakAfter = true;
18272
18309
  if (g.keepTogether) {
18273
- const id = `${item.name}:g${level}:${path2}:${lc.nextId()}`;
18274
- for (let k = start; k < units.length; k++) units[k].block ||= id;
18310
+ const id = `${item.name || item.type}:g${level}:${path2}:${lc.nextId()}`;
18311
+ for (let k = start; k < units.length; k++) if (!units[k].block) {
18312
+ units[k].block = id;
18313
+ if (cont) units[k].blockOpen = true;
18314
+ }
18275
18315
  }
18276
18316
  }
18277
18317
  };
@@ -18288,7 +18328,7 @@ register("table", {
18288
18328
  if (kids) {
18289
18329
  const rest2 = buckets.filter((b) => !shown2.has(b));
18290
18330
  if (rest2.length) {
18291
- lc.warn(`${item.name}: ${rest2.length} rows of the group "${gname}" have no root (a cycle or too deep); they are listed at the top level`);
18331
+ lc.warn(`${item.name || item.type}: ${rest2.length} rows of the group "${gname}" have no root (a cycle or too deep); they are listed at the top level`);
18292
18332
  rest2.forEach((bk, bi) => node2(bk, bi, rest2, 0, keyPath));
18293
18333
  }
18294
18334
  }
@@ -18316,7 +18356,7 @@ register("table", {
18316
18356
  units[units.length - 1].breakAfter = true;
18317
18357
  }
18318
18358
  if (item.bookmark && units.length) {
18319
- const b = evalSafe(item.bookmark, base, lc, `${item.name || "Table"} bookmark`).v;
18359
+ const b = evalSafe(item.bookmark, base, lc, `${item.name || item.type} bookmark`).v;
18320
18360
  if (b != null && b !== "" && b !== "#Error") units[0].items.unshift({ t: "bookmark", label: String(b), x: item.x, y: 0, level: item.bookmarkLevel ?? 0 });
18321
18361
  }
18322
18362
  if (!lc.fixed) lc.regions?.push(region);
@@ -18411,7 +18451,7 @@ register("container", {
18411
18451
  const ctx = lc.ctxFor(item);
18412
18452
  if (hidden(item, ctx, lc)) return { height: 0, units: [] };
18413
18453
  const res = lc.layoutItems(item.items, lc, { dx: item.x });
18414
- return frame2(item, lc, ctx, res, item.keepTogether !== false ? `c:${item.name}:${lc.nextId()}` : null);
18454
+ return frame2(item, lc, ctx, res, item.keepTogether !== false ? `c:${item.name || item.type}:${lc.nextId()}` : null);
18415
18455
  }
18416
18456
  });
18417
18457
  function frame2(item, lc, ctx, res, blockId0) {
@@ -18468,7 +18508,7 @@ register("list", {
18468
18508
  let rows = item.dataSet && lc.dataSets[item.dataSet] || [];
18469
18509
  if (item.inheritRows && item.dataSet && ctx.current?.[item.dataSet]) rows = ctx.current[item.dataSet];
18470
18510
  else rows = related(rows, ctx);
18471
- if (item.dataSet && !lc.dataSets[item.dataSet]) lc.warn(`${item.name}: the data set "${item.dataSet}" does not exist`);
18511
+ if (item.dataSet && !lc.dataSets[item.dataSet]) lc.warn(`${item.name || item.type}: the data set "${item.dataSet}" does not exist`);
18472
18512
  if (item.filters?.length) rows = applyFilters(rows, item.filters, ctx);
18473
18513
  if (item.sort?.length) rows = applySort(rows, item.sort, ctx);
18474
18514
  const reps = item.groupBy ? groupRows(rows, item.groupBy, ctx, lc, item.name, item.groupAdjacent === true).map((b) => b.rows) : rows.map((r) => [r]);
@@ -18497,7 +18537,7 @@ register("list", {
18497
18537
  } };
18498
18538
  const k = i % ncol;
18499
18539
  const res = lc.layoutItems(item.items, child, { dx: item.x + k * (item.w + gap) });
18500
- const fr = frame2({ ...item, x: item.x + k * (item.w + gap), name: `${item.name}[${i + 1}]` }, child, rctx, res, item.keepTogether !== false ? `l:${item.name}:${i}:${lc.nextId()}` : null);
18540
+ const fr = frame2({ ...item, x: item.x + k * (item.w + gap), name: `${item.name || item.type}[${i + 1}]` }, child, rctx, res, item.keepTogether !== false ? `l:${item.name || item.type}:${i}:${lc.nextId()}` : null);
18501
18541
  if (ncol > 1) {
18502
18542
  if (k === 0) {
18503
18543
  cells = { top: y, height: 0, items: [], x0: item.x, x1: item.x + ncol * item.w + (ncol - 1) * gap, block: void 0 };
@@ -18968,7 +19008,7 @@ register("subreport", {
18968
19008
  if (hidden(item, ctx, lc)) return { height: 0, units: [] };
18969
19009
  const params = {};
18970
19010
  for (const [k, v] of Object.entries(item.params || {})) {
18971
- const r = evalSafe(v, ctx, lc, `${item.name} parameter ${k}`);
19011
+ const r = evalSafe(v, ctx, lc, `${item.name || item.type} parameter ${k}`);
18972
19012
  params[k] = r.v instanceof Date ? isoDate(r.v, ctx.timeZone) : r.v;
18973
19013
  }
18974
19014
  const key = `${item.report}|${JSON.stringify(params)}`;
@@ -18987,10 +19027,10 @@ register("subreport", {
18987
19027
  const res = sub.results.get(key);
18988
19028
  if (!res) return placeholder(`Subreport "${item.report}" did not load`);
18989
19029
  if (res.error) {
18990
- lc.warn(`${item.name}: ${res.error}`);
19030
+ lc.warn(`${item.name || item.type}: ${res.error}`);
18991
19031
  return placeholder(`Subreport "${item.report}": ${res.error}`);
18992
19032
  }
18993
- const block = item.keepTogether ? `s:${item.name}:${lc.nextId()}` : void 0;
19033
+ const block = item.keepTogether ? `s:${item.name || item.type}:${lc.nextId()}` : void 0;
18994
19034
  const units = res.units.map((u) => ({ ...u, items: shiftItems(u.items, item.x, 0), ...u.cont ? { cont: shiftItems(u.cont, item.x, 0) } : {}, x0: (u.x0 ?? 0) + item.x, x1: (u.x1 ?? item.w) + item.x, block: u.block || block, tableId: void 0 }));
18995
19035
  const tables = res.tables.map((t) => ({ ...t, x0: t.x0 + item.x, x1: t.x1 + item.x, headerItems: shiftItems(t.headerItems, item.x, 0), id: `sub${lc.nextId()}` }));
18996
19036
  for (const r of res.regions || []) lc.regions?.push(r);
@@ -19161,7 +19201,8 @@ function paginate(units, tables, pageH, warn, stats) {
19161
19201
  if (b) {
19162
19202
  b.top = Math.min(b.top, u.top);
19163
19203
  b.bottom = Math.max(b.bottom, u.top + u.height);
19164
- } else blockMap.set(u.block, { top: u.top, bottom: u.top + u.height });
19204
+ if (u.blockOpen) b.open = true;
19205
+ } else blockMap.set(u.block, { top: u.top, bottom: u.top + u.height, open: !!u.blockOpen });
19165
19206
  }
19166
19207
  const blockUnits = /* @__PURE__ */ new Map();
19167
19208
  for (const u of units) if (u.block) {
@@ -19169,18 +19210,18 @@ function paginate(units, tables, pageH, warn, stats) {
19169
19210
  if (!l) blockUnits.set(u.block, l = []);
19170
19211
  l.push(u);
19171
19212
  }
19172
- const blocks = [...blockMap.values()].filter((b) => b.bottom - b.top <= pageH + EPS2);
19213
+ const blocks = [...blockMap.values()].filter((b) => !b.open && b.bottom - b.top <= pageH + EPS2);
19173
19214
  for (const b of blocks) cand.push(b.top, b.bottom);
19174
19215
  cand.sort((a, b) => a - b);
19175
19216
  const grpRows = /* @__PURE__ */ new Map();
19176
19217
  for (const u of units) if (u.grp) {
19177
19218
  let g = grpRows.get(u.grp);
19178
- if (!g) grpRows.set(u.grp, g = { tops: /* @__PURE__ */ new Set(), first: u.top, last: 0, block: u.block || null });
19219
+ if (!g) grpRows.set(u.grp, g = { tops: /* @__PURE__ */ new Set(), first: u.top, last: 0, block: u.block || null, before: u.grpBefore || 0 });
19179
19220
  g.tops.add(u.top);
19180
19221
  g.last = Math.max(g.last, u.top + u.height);
19181
19222
  if (g.block !== (u.block || null)) g.block = void 0;
19182
19223
  }
19183
- const widowGroups = [...grpRows.values()].map((g) => ({ first: g.first, last: g.last, tops: [...g.tops].sort((a, b) => a - b), block: g.block })).filter((g) => g.tops.length >= 4 || g.tops.length >= 2 && g.last - g.first <= pageH + EPS2).sort((a, b) => a.first - b.first);
19224
+ const widowGroups = [...grpRows.values()].map((g) => ({ first: g.first, last: g.last, tops: [...g.tops].sort((a, b) => a - b), block: g.block, before: g.before, n: g.tops.size + g.before })).filter((g) => g.n >= 4 || !g.before && g.n >= 2 && g.last - g.first <= pageH + EPS2).sort((a, b) => a.first - b.first);
19184
19225
  const wFirst = widowGroups.map((g) => g.first);
19185
19226
  const wMaxLast = [];
19186
19227
  widowGroups.forEach((g, i) => wMaxLast.push(Math.max(i ? wMaxLast[i - 1] : -Infinity, g.last)));
@@ -19207,9 +19248,9 @@ function paginate(units, tables, pageH, warn, stats) {
19207
19248
  tick(1);
19208
19249
  const g = widowGroups[i];
19209
19250
  if (g.last <= y + EPS2 || g.block && !withBlocks) continue;
19210
- if (g.tops.length < 4) return false;
19211
- const above = rowsAbove(g.tops, y);
19212
- if (above < 2 || g.tops.length - above < 2) return false;
19251
+ if (g.n < 4) return false;
19252
+ const above = g.before + rowsAbove(g.tops, y);
19253
+ if (above < 2 || g.n - above < 2) return false;
19213
19254
  }
19214
19255
  return true;
19215
19256
  };
@@ -19264,7 +19305,7 @@ function paginate(units, tables, pageH, warn, stats) {
19264
19305
  if (u.block) {
19265
19306
  const bl = blockMap.get(u.block);
19266
19307
  if (movedBlocks.has(bl)) continue;
19267
- if (!bl || bl.bottom - bl.top > room + EPS2 || bl.top <= top2 + EPS2) return null;
19308
+ if (!bl || bl.open || bl.bottom - bl.top > room + EPS2 || bl.top <= top2 + EPS2) return null;
19268
19309
  movedBlocks.add(bl);
19269
19310
  for (const m of blockUnits.get(u.block)) if (!placed.has(m)) moved.push(m);
19270
19311
  continue;
@@ -22009,6 +22050,7 @@ var SCHEMA_ALIAS = "reportwright/report@1";
22009
22050
  var ITEM_KEYS = new Set("id type name x y w h style styleName items hidden layer locked label alt value dataSet canGrow canShrink action bookmark tooltip keepTogether pageBreakBefore pageBreakAfter pageBreakBetween newSection filters sort groups groupBy groupAdjacent inheritRows columns header detail footer repeatHeader widowControl noRowsText pageBreakEvery cells rows rowGroup rowGroups columnGro\
22010
22051
  up columnGroups values value valueFormat rowSort columnSort rowTotals columnTotals cellStyle headerStyle totalStyle corner partProps library part properties overflow overflowTo src fit headingLevel rotate shrinkToFit minFontSize shrinkStep tocDots tocLink tocPage format chartType series seriesGroup category title legend palette stacked valueAxis xTitle maxCategories outliers categorySort categoryF\
22011
22052
  ormat showValues labelPosition symbology quietZone report params text toggle initiallyCollapsed collapsible expandLevels repeatColumns columnGap rowHeaderWidth anim animation visual comment notes description".split(" "));
22053
+ var NO_SERIES_CHARTS = /* @__PURE__ */ new Set(["gantt"]);
22012
22054
  function looksLikeLink(it) {
22013
22055
  if (it.type !== "textbox" || it.action && it.action.type && it.action.type !== "none") return false;
22014
22056
  const st = it.style && typeof it.style === "object" ? it.style : {};
@@ -22137,6 +22179,9 @@ function validate(def) {
22137
22179
  if (p?.hidden && p.required && (p.default == null || p.default === "") && !p.nullable) {
22138
22180
  warnings.push(`Parameter "${p.name}" is hidden and required but has no default: only a URL, the API or a drill-through can set it`);
22139
22181
  }
22182
+ if (p?.required && p.nullable && !p.multi && (p.default == null || p.default === "")) {
22183
+ warnings.push(`Parameter "${p.name}" is required and nullable with no default: when no value is given it is null and the report can draw blank; give it a default, or drop "nullable" to require a value`);
22184
+ }
22140
22185
  if (p?.type === "date" && typeof p.default === "string" && p.default.trim() && !p.default.startsWith("=") && !toDate(p.default)) {
22141
22186
  warnings.push(`Parameter "${p.name}": its default "${p.default}" is not a date; write it as 2026-12-31 (year-month-day)`);
22142
22187
  }
@@ -22217,7 +22262,7 @@ function validate(def) {
22217
22262
  warnings.push(`${label}: there is no named style "${it.styleName}"${g ? ` (did you mean "${g}"?)` : ""}; it is ignored`);
22218
22263
  }
22219
22264
  if (Array.isArray(it.sort)) sortIssues(it.sort, label, warnings);
22220
- if (it.type === "chart" && Array.isArray(it.series) && !it.series.length) warnings.push(`${label}: the chart has no series, so it draws nothing; add one, e.g. series: [{ "name": "Value", "value": "=Sum(Fields.Amount)" }]`);
22265
+ if (it.type === "chart" && !NO_SERIES_CHARTS.has(it.chartType) && !(Array.isArray(it.series) && it.series.length)) warnings.push(`${label}: the chart has no series, so it plots a count of its rows; add one, e.g. series: [{ "name": "Value", "value": "=Sum(Fields.Amount)" }]`);
22221
22266
  if (it.type === "image" && typeof it.src === "string" && !it.src.startsWith("=") && LOCAL_IMAGE.test(it.src.trim())) warnings.push(`${label}: "${it.src}" is a local file path, which a browser cannot load; embed the image (images: { name: "data:image/png;base64,..." } and src: "embedded:name") or use an https:// address`);
22222
22267
  if (it.style != null && (typeof it.style !== "object" || Array.isArray(it.style))) warnings.push(`${label}: "style" must be an object of style properties, such as { "fontWeight": "bold" }; ${JSON.stringify(it.style).slice(0, 40)} is ignored`);
22223
22268
  const own = ownKeys(it.type);
@@ -22260,6 +22305,9 @@ function validate(def) {
22260
22305
  const n5 = Array.isArray(it.columns) ? it.columns.length : 0;
22261
22306
  const rowsOf = [["header", it.header], ["detail", it.detail], ["footer", it.footer], ...(it.groups || []).flatMap((g, gi) => [[`group ${gi + 1} header`, g?.header], [`group ${gi + 1} footer`, g?.footer]])];
22262
22307
  for (const [part2, rows] of rowsOf) (Array.isArray(rows) ? rows : []).forEach((row, ri) => {
22308
+ const cells = Array.isArray(row?.cells) ? row.cells : [];
22309
+ const placed = cells.length < n5 && cells.some((c) => Number(c?.colSpan) > 1) ? cells.reduce((s, c) => s + Math.max(1, Math.floor(Number(c?.colSpan)) || 1), 0) : cells.length;
22310
+ if (placed < n5) warnings.push(`${label} ${part2} row ${ri + 1}: the row has ${placed} of ${n5} cells and no colSpan covers the other ${n5 - placed}; add the cells or a colSpan`);
22263
22311
  for (const [k, j] of rowCells(row?.cells, n5).covered) warnings.push(`${label} ${part2} row ${ri + 1}: cell ${k} is covered by colSpan of cell ${j}, so it is not drawn (leave it null, or omit the spanned cells: a row with fewer cells than columns places each cell after the previous one's span)`);
22264
22312
  });
22265
22313
  }
@@ -22675,7 +22723,7 @@ function* textsOf(def, dataSets) {
22675
22723
  }
22676
22724
  }
22677
22725
  }
22678
- var NO_CJK_FONTS = "Text in Japanese/Chinese/Korean needs the optional CJK fonts: npm run fonts:cjk";
22726
+ var NO_CJK_FONTS = "Text in Japanese/Chinese/Korean needs the optional CJK fonts: npm i @reportwright/fonts-cjk, then npx reportwright-fonts-cjk (npm); in the app, npm run fonts:cjk";
22679
22727
  async function render(def0, opts = {}) {
22680
22728
  const t0 = Date.now();
22681
22729
  const reuseWarnings = [];
@@ -22746,6 +22794,7 @@ async function render(def0, opts = {}) {
22746
22794
  const base = { ...BASE_STYLE, ...def.styles?.base || {} };
22747
22795
  let idSeq = 0;
22748
22796
  const regions = [];
22797
+ const tablesSeen = { n: 0 };
22749
22798
  const imageBudget = opts.imageBudget || { seen: /* @__PURE__ */ new Set(), bytes: 0 };
22750
22799
  const makeLc = (globals, fixed2) => {
22751
22800
  const lc = {
@@ -22756,6 +22805,7 @@ async function render(def0, opts = {}) {
22756
22805
  fixed: fixed2,
22757
22806
  maxUnitHeight: contentH,
22758
22807
  state: opts.state || {},
22808
+ tablesSeen,
22759
22809
  regions: opts.exportData || opts.dataOnly ? regions : null,
22760
22810
  dataOnly: !!opts.dataOnly,
22761
22811
  // exportData: true: table data and cell tags (Excel, CSV, Word, HTML, accessible PDF)
@@ -22775,14 +22825,16 @@ async function render(def0, opts = {}) {
22775
22825
  ctxFor: (item) => {
22776
22826
  const ds = item.dataSet || defaultDs;
22777
22827
  const rows = ds && dataSets[ds] || [];
22778
- return { ...baseCtx, globals, fields: rows[0] || null, aggRows: rows };
22828
+ return { ...baseCtx, globals, fields: (opts.pageWindow?.first && ds === defaultDs ? opts.pageWindow.first : rows[0]) || null, aggRows: rows };
22779
22829
  },
22780
22830
  subreports: null,
22781
22831
  bodyWidth: opts.bodyWidth ?? page.bodyWidth,
22782
22832
  images: def.images || {},
22783
22833
  imageBudget,
22784
- cellItems: { left: 1e5 }
22834
+ cellItems: { left: 1e5 },
22785
22835
  // rows of items inside table cells, for the whole render (items/index.js)
22836
+ win: opts.pageWindow || null
22837
+ // paged.js: units carry their row number (dataRow, rowHead), rows and groups continue
22786
22838
  };
22787
22839
  return lc;
22788
22840
  };
@@ -22871,8 +22923,8 @@ async function render(def0, opts = {}) {
22871
22923
  u.breakBefore = false;
22872
22924
  u.breakAfter = false;
22873
22925
  }
22874
- const slices = paginate(body.units, body.tables, g.cH, warn);
22875
- parts2.push({ body, slices, g });
22926
+ const slices2 = paginate(body.units, body.tables, g.cH, warn);
22927
+ parts2.push({ body, slices: slices2, g });
22876
22928
  if (lc.rowBudget?.cut) break sections;
22877
22929
  if (!overflow.more) break;
22878
22930
  if (rep + 1 >= REUSE_LIMITS.repeats) {
@@ -22913,19 +22965,21 @@ async function render(def0, opts = {}) {
22913
22965
  const headings = [];
22914
22966
  const pages2 = [];
22915
22967
  list2.forEach(({ part: part2, k, last }, n5) => {
22916
- const { body, slices, g } = part2;
22968
+ const { body, slices: slices2, g } = part2;
22917
22969
  const { pg, colCount, colW, colGap, cH } = g;
22918
22970
  const ph = parts2.length === 1 ? pageH : pg.height;
22919
22971
  const ml = pg.margins.left, mt = pg.margins.top;
22920
22972
  const bodyTop = mt + g.hH;
22921
22973
  const items = [];
22922
22974
  const cut = lc.rowBudget?.cut;
22923
- const globals = { ...baseCtx.globals, PageNumber: n5 - secStart[n5] + 1, TotalPages: cut ? "…" : secLen.get(secStart[n5]), OverallPageNumber: n5 + 1, OverallTotalPages: cut ? "…" : total };
22975
+ const win = opts.pageWindow, off = win ? win.offset : 0, all = win ? win.total ?? "…" : null;
22976
+ const globals = { ...baseCtx.globals, PageNumber: n5 - secStart[n5] + 1 + (secStart[n5] ? 0 : off), TotalPages: cut ? "…" : all ?? secLen.get(secStart[n5]), OverallPageNumber: n5 + 1 + off, OverallTotalPages: cut ? "…" : all ?? total };
22977
+ const isFirst = n5 + off === 0, isLast = n5 === total - 1 && (!win || win.final);
22924
22978
  const plc = makeLc(globals, true);
22925
- const band = (section, y0, isFirst, isLast) => {
22979
+ const band = (section, y0, isFirst2, isLast2) => {
22926
22980
  if (!section) return;
22927
- if (isFirst && section.printOnFirstPage === false) return;
22928
- if (isLast && section.printOnLastPage === false) return;
22981
+ if (isFirst2 && section.printOnFirstPage === false) return;
22982
+ if (isLast2 && section.printOnLastPage === false) return;
22929
22983
  const capped2 = () => warn(`The page headers and footers would draw more than ${BAND_ITEMS.toLocaleString("en-US")} items in this report; they are left out of the later pages. Make them lighter, or the report shorter.`);
22930
22984
  if (bandWork.left <= 0) {
22931
22985
  capped2();
@@ -22946,40 +23000,48 @@ async function render(def0, opts = {}) {
22946
23000
  for (const u of r.units) if (c.paged) placeItems(u.items, ml, y0 + u.top, items);
22947
23001
  else for (const it of shiftItems(u.items, ml, y0 + u.top)) items.push(it);
22948
23002
  };
22949
- band(g.hdr, mt, g.own.h ? k === 0 : n5 === 0, g.own.h ? last : n5 === total - 1);
23003
+ band(g.hdr, mt, g.own.h ? k === 0 : isFirst, g.own.h ? last : isLast);
22950
23004
  const frozen = [];
22951
23005
  for (let c = 0; c < colCount; c++) {
22952
- const p = slices[k * colCount + c];
23006
+ const p = slices2[k * colCount + c];
22953
23007
  if (!p) break;
22954
23008
  const cx = ml + c * (colW + colGap);
22955
23009
  for (const t of p.repeat) placeItems(t.headerItems, cx, bodyTop, items);
22956
23010
  for (const t of body.tables) {
22957
23011
  if (!(t.top < p.bottom - EPS4 && t.bottom > p.top + EPS4)) continue;
22958
23012
  const cont = p.repeat.includes(t);
22959
- let off = 0;
23013
+ let off2 = 0;
22960
23014
  if (!cont) {
22961
- for (const r of p.repeat) if (t.x0 < r.x1 - EPS4 && r.x0 < t.x1 - EPS4) off = Math.max(off, r.headerHeight);
23015
+ for (const r of p.repeat) if (t.x0 < r.x1 - EPS4 && r.x0 < t.x1 - EPS4) off2 = Math.max(off2, r.headerHeight);
22962
23016
  }
22963
- const y = cont ? bodyTop : bodyTop + off + t.top - p.top;
22964
- frozen.push({ x: cx + t.x0, y, w: t.x1 - t.x0, h: t.headerHeight, end: bodyTop + (cont ? t.headerHeight : off) + Math.min(t.bottom, p.bottom) - p.top });
23017
+ const y = cont ? bodyTop : bodyTop + off2 + t.top - p.top;
23018
+ frozen.push({ x: cx + t.x0, y, w: t.x1 - t.x0, h: t.headerHeight, end: bodyTop + (cont ? t.headerHeight : off2) + Math.min(t.bottom, p.bottom) - p.top });
22965
23019
  }
22966
23020
  for (const u of p.units) {
22967
- let off = 0;
22968
- for (const t of p.repeat) if (u.x0 < t.x1 - EPS4 && t.x0 < u.x1 - EPS4) off = Math.max(off, t.headerHeight);
22969
- if (u.atBottom) off += Math.max(0, cH - off - (p.bottom - p.top));
22970
- if (u.cont && u.top <= p.top + EPS4) placeItems(u.cont, cx, bodyTop + off, items);
22971
- placeItems(u.items, cx, bodyTop + off + (u.top - p.top), items);
23021
+ let off2 = 0;
23022
+ for (const t of p.repeat) if (u.x0 < t.x1 - EPS4 && t.x0 < u.x1 - EPS4) off2 = Math.max(off2, t.headerHeight);
23023
+ if (u.atBottom) off2 += Math.max(0, cH - off2 - (p.bottom - p.top));
23024
+ if (u.cont && u.top <= p.top + EPS4) placeItems(u.cont, cx, bodyTop + off2, items);
23025
+ placeItems(u.items, cx, bodyTop + off2 + (u.top - p.top), items);
22972
23026
  }
22973
23027
  }
22974
- band(g.ftr, ph - pg.margins.bottom - g.fH, g.own.f ? k === 0 : n5 === 0, g.own.f ? last : n5 === total - 1);
23028
+ band(g.ftr, ph - pg.margins.bottom - g.fH, g.own.f ? k === 0 : isFirst, g.own.f ? last : isLast);
22975
23029
  let j = 0;
22976
23030
  for (let i = 0; i < items.length; i++) {
22977
23031
  const it = items[i];
22978
- if (it.t === "bookmark") (it.heading ? headings : bookmarks2).push({ label: it.label, page: n5 + 1, y: it.y, level: it.level || 0, ...secStart[n5] ? { shown: n5 - secStart[n5] + 1 } : {} });
23032
+ if (it.t === "bookmark") (it.heading ? headings : bookmarks2).push({ label: it.label, page: n5 + 1 + off, y: it.y, level: it.level || 0, ...secStart[n5] ? { shown: n5 - secStart[n5] + 1 } : {} });
22979
23033
  else items[j++] = it;
22980
23034
  }
22981
23035
  items.length = j;
22982
23036
  const out = frozen.length ? { items, frozen } : { items };
23037
+ if (win) {
23038
+ const u = slices2[k * colCount]?.units[0];
23039
+ if (u?.dataRow != null) {
23040
+ out.firstRow = u.dataRow;
23041
+ out.rowHead = !!u.rowHead;
23042
+ if (u.headLevel != null) out.headLevel = u.headLevel;
23043
+ }
23044
+ }
22983
23045
  if (pg.width !== first.width || ph !== pageH) {
22984
23046
  out.width = pg.width;
22985
23047
  out.height = ph;
@@ -23047,7 +23109,7 @@ async function render(def0, opts = {}) {
23047
23109
  if (m.noGlyph.size) {
23048
23110
  const cjk2 = [...m.noGlyph].some((c) => /[\u1100-\u11ff\u2600-\u27bf\u2b00-\u2bff\u3000-\u9fff\uac00-\ud7af\uf900-\ufaff\uff00-\uffef]|[\u{1f000}-\u{1faff}]/u.test(c));
23049
23111
  const list2 = [...m.noGlyph].slice(0, 20).join(" ");
23050
- warn(cjk2 ? `${NO_CJK_FONTS} (the app), or npm i @reportwright/fonts-cjk then npx reportwright-fonts-cjk (the npm engine). Until then these characters show as boxes: ${list2}.` : `No font has these characters, so they show as boxes: ${list2}. Upload a font that has them and use it for this text.`);
23112
+ warn(cjk2 ? `${NO_CJK_FONTS}. Until then these characters show as boxes: ${list2}.` : `No font has these characters, so they show as boxes: ${list2}. Upload a font that has them and use it for this text.`);
23051
23113
  }
23052
23114
  if (opts.bodyOnly) return { units: result.body.units, tables: result.body.tables, height: result.body.height, regions, warnings, clipped: clipped() };
23053
23115
  if (opts.dataOnly) return {
@@ -23087,6 +23149,7 @@ async function render(def0, opts = {}) {
23087
23149
  headings: result.headings || [],
23088
23150
  pageStarts: result.pageStarts || [],
23089
23151
  regions: opts.exportData ? regions : null,
23152
+ hasTables: tablesSeen.n > 0,
23090
23153
  name: def.name || "",
23091
23154
  partial: cutRows(),
23092
23155
  timeZone: timeZone || null,
@@ -23378,36 +23441,9 @@ function pdfEncryptOptions(pdf) {
23378
23441
  }
23379
23442
 
23380
23443
  // src/exporters/pdf.js
23381
- import { PDFDocument, PDFName as PDFName3, PDFString as PDFString4, PDFHexString as PDFHexString4, PDFRawStream as PDFRawStream2, rgb as rgb2 } from "pdf-lib";
23444
+ import { PDFDocument, PDFName as PDFName3, PDFString as PDFString4, PDFHexString as PDFHexString5, PDFRawStream as PDFRawStream2, rgb as rgb3 } from "pdf-lib";
23382
23445
  import fontkitModule2 from "@pdf-lib/fontkit";
23383
23446
 
23384
- // src/exporters/color.js
23385
- var NAMED2 = { black: "#000000", white: "#ffffff", red: "#ff0000", green: "#008000", blue: "#0000ff", gray: "#808080", grey: "#808080", orange: "#ffa500", yellow: "#ffff00" };
23386
- function parseColor(c) {
23387
- if (!c || c === "none" || c === "transparent") return null;
23388
- let s = String(c).trim().toLowerCase();
23389
- if (NAMED2[s]) s = NAMED2[s];
23390
- let m = /^#([0-9a-f]{3,8})$/.exec(s);
23391
- if (m) {
23392
- let h = m[1];
23393
- if (h.length === 3 || h.length === 4) h = [...h].map((x) => x + x).join("");
23394
- const n5 = parseInt(h.slice(0, 6), 16);
23395
- const a = h.length === 8 ? parseInt(h.slice(6, 8), 16) / 255 : 1;
23396
- return { r: (n5 >> 16 & 255) / 255, g: (n5 >> 8 & 255) / 255, b: (n5 & 255) / 255, a };
23397
- }
23398
- m = /^rgba?\(([^)]+)\)$/.exec(s);
23399
- if (m) {
23400
- const [r, g, b, a = 1] = m[1].split(/[\s,/]+/).map(Number);
23401
- return { r: r / 255, g: g / 255, b: b / 255, a };
23402
- }
23403
- return null;
23404
- }
23405
- function cssColor(c) {
23406
- const p = parseColor(c);
23407
- if (!p) return "none";
23408
- return `rgba(${Math.round(p.r * 255)},${Math.round(p.g * 255)},${Math.round(p.b * 255)},${p.a})`;
23409
- }
23410
-
23411
23447
  // src/exporters/pdfa.js
23412
23448
  import { PDFName, PDFNumber as PDFNumber2, PDFString as PDFString2, PDFHexString as PDFHexString2, PDFArray as PDFArray2, PDFDict as PDFDict2 } from "pdf-lib";
23413
23449
  var xml = (s) => String(s).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
@@ -23481,6 +23517,35 @@ function setUaMetadata(doc, { title: title2 = "Report", author = void 0, created
23481
23517
 
23482
23518
  // src/exporters/svgimage.js
23483
23519
  import { pushGraphicsState, popGraphicsState, concatTransformationMatrix, rgb } from "pdf-lib";
23520
+
23521
+ // src/exporters/color.js
23522
+ var NAMED2 = { black: "#000000", white: "#ffffff", red: "#ff0000", green: "#008000", blue: "#0000ff", gray: "#808080", grey: "#808080", orange: "#ffa500", yellow: "#ffff00" };
23523
+ function parseColor(c) {
23524
+ if (!c || c === "none" || c === "transparent") return null;
23525
+ let s = String(c).trim().toLowerCase();
23526
+ if (NAMED2[s]) s = NAMED2[s];
23527
+ let m = /^#([0-9a-f]{3,8})$/.exec(s);
23528
+ if (m) {
23529
+ let h = m[1];
23530
+ if (h.length === 3 || h.length === 4) h = [...h].map((x) => x + x).join("");
23531
+ const n5 = parseInt(h.slice(0, 6), 16);
23532
+ const a = h.length === 8 ? parseInt(h.slice(6, 8), 16) / 255 : 1;
23533
+ return { r: (n5 >> 16 & 255) / 255, g: (n5 >> 8 & 255) / 255, b: (n5 & 255) / 255, a };
23534
+ }
23535
+ m = /^rgba?\(([^)]+)\)$/.exec(s);
23536
+ if (m) {
23537
+ const [r, g, b, a = 1] = m[1].split(/[\s,/]+/).map(Number);
23538
+ return { r: r / 255, g: g / 255, b: b / 255, a };
23539
+ }
23540
+ return null;
23541
+ }
23542
+ function cssColor(c) {
23543
+ const p = parseColor(c);
23544
+ if (!p) return "none";
23545
+ return `rgba(${Math.round(p.r * 255)},${Math.round(p.g * 255)},${Math.round(p.b * 255)},${p.a})`;
23546
+ }
23547
+
23548
+ // src/exporters/svgimage.js
23484
23549
  var mul = (a, b) => [a[0] * b[0] + a[2] * b[1], a[1] * b[0] + a[3] * b[1], a[0] * b[2] + a[2] * b[3], a[1] * b[2] + a[3] * b[3], a[0] * b[4] + a[2] * b[5] + a[4], a[1] * b[4] + a[3] * b[5] + a[5]];
23485
23550
  var I = [1, 0, 0, 1, 0, 0];
23486
23551
  var TRANSFORMS = /* @__PURE__ */ new Set(["matrix", "translate", "scale", "rotate", "skewX", "skewY"]);
@@ -24112,7 +24177,7 @@ function cellVisuals(items, used, tables) {
24112
24177
 
24113
24178
  // src/exporters/regions.js
24114
24179
  function needsTableData(model, what) {
24115
- if (model && model.regions === null) throw Object.assign(new Error(`${what} needs the report's table data: render it with { exportData: true }`), { code: "NO_EXPORT_DATA" });
24180
+ if (model && model.regions === null && model.hasTables !== false) throw Object.assign(new Error(`${what} needs the report's table data: render it with { exportData: true }`), { code: "NO_EXPORT_DATA" });
24116
24181
  }
24117
24182
  function mergedRegions(model) {
24118
24183
  const map = /* @__PURE__ */ new Map();
@@ -24547,11 +24612,8 @@ function pngProblem(b) {
24547
24612
  return null;
24548
24613
  }
24549
24614
 
24550
- // src/exporters/pdf.js
24551
- var fontkit2 = (
24552
- /** @type {any} */
24553
- fontkitModule2.default || fontkitModule2
24554
- );
24615
+ // src/exporters/pdfpaint.js
24616
+ import { PDFHexString as PDFHexString4, rgb as rgb2 } from "pdf-lib";
24555
24617
  var colors = /* @__PURE__ */ new Map();
24556
24618
  var col = (c) => {
24557
24619
  if (colors.has(c)) return colors.get(c);
@@ -24566,138 +24628,460 @@ var n4 = (v) => Math.round(v * 1e4) / 1e4;
24566
24628
  var hex4 = (id) => id.toString(16).padStart(4, "0");
24567
24629
  var KAPPA = 4 * ((Math.SQRT2 - 1) / 3);
24568
24630
  var cleanText = (t) => t.replace(/\t|\u0085|\u2028|\u2029/g, " ").replace(/[\b\v]/g, "");
24569
- function exportPdf(model, fontStore2, opt = {}) {
24570
- return withDeadline(
24571
- pdfOf(model, fontStore2, opt),
24572
- /** @type {any} */
24573
- opt.timeoutMs
24574
- );
24575
- }
24576
- async function pdfOf(model, fontStore2, opt) {
24577
- const fixed2 = fixedDate(opt);
24578
- const when = fixed2 ?? /* @__PURE__ */ new Date();
24579
- const fileId = fixed2 ? await contentId(model, opt, when) : void 0;
24580
- const doc = await PDFDocument.create({ updateMetadata: false });
24581
- doc.registerFontkit(fontkit2);
24582
- doc.setTitle(opt.title || "Report");
24583
- doc.setProducer("ReportWright");
24584
- doc.setCreator("ReportWright");
24585
- if (opt.author) doc.setAuthor(opt.author);
24586
- setInfoDates(doc, when);
24587
- if (fileId) doc.context.trailerInfo.ID = doc.context.obj([PDFHexString4.of(fileId), PDFHexString4.of(fileId)]);
24588
- const used = /* @__PURE__ */ new Map();
24589
- const shaped = /* @__PURE__ */ new Map();
24590
- const spans2 = /* @__PURE__ */ new WeakMap();
24591
- const chars = (key) => {
24592
- let c = used.get(key);
24593
- if (!c) {
24594
- c = /* @__PURE__ */ new Set([32]);
24595
- used.set(key, c);
24596
- }
24597
- return c;
24631
+ var formatAt = (run, cl) => {
24632
+ const cp = run.text.codePointAt(cl);
24633
+ return cp !== void 0 && FORMAT_CHAR.test(String.fromCodePoint(cp));
24634
+ };
24635
+ function drawRun(c, run, x, y, size, pf, color2, spans2, texts) {
24636
+ const f4 = pf.embedder.font;
24637
+ const per = 1e3 / size;
24638
+ const span = (text2) => `/Span <</ActualText ${PDFHexString4.fromText(text2).toString()}>> BDC
24639
+ `;
24640
+ c.et();
24641
+ c.paint(color2, null);
24642
+ c.font(pf, size);
24643
+ if (!run.rtl) c.s += span(run.text);
24644
+ c.s += `BT ${n2(x)} ${n2(y)} Td
24645
+ `;
24646
+ let arr = "";
24647
+ let pen = 0, rise = 0;
24648
+ const flush = () => {
24649
+ if (arr) c.s += `[${arr}] TJ
24650
+ `;
24651
+ arr = "";
24598
24652
  };
24599
- for (const pg of model.pages) for (const it of pg.items.flatMap((i) => i.t === "field" ? i.draw.filter((d) => d.t === "text") : i.t === "text" ? [i] : [])) {
24600
- for (const l of it.lines) {
24601
- if (!l.runs) {
24602
- const c = chars(it.font);
24603
- for (const ch of l.text) c.add(
24604
- /** @type {number} */
24605
- ch.codePointAt(0)
24606
- );
24607
- continue;
24608
- }
24609
- for (const r of l.runs) {
24610
- const c = chars(r.font);
24611
- for (const ch of r.text) c.add(
24612
- /** @type {number} */
24613
- ch.codePointAt(0)
24614
- );
24615
- let g = shaped.get(r.font);
24616
- if (!g) {
24617
- g = /* @__PURE__ */ new Map();
24618
- shaped.set(r.font, g);
24619
- }
24620
- glyphText(r, fontStore2.get(r.font), g, spans2);
24621
- }
24653
+ const all = run.glyphs;
24654
+ const adv = (id) => f4.getGlyph(id).advanceWidth;
24655
+ let g = all, quietAt = -1;
24656
+ if (run.rtl) {
24657
+ const hasBase = /* @__PURE__ */ new Set();
24658
+ for (let i = 0; i < all.length; i += 4) if (adv(all[i]) > 0) hasBase.add(all[i + 3]);
24659
+ const quiet = (i) => spans2?.has(all[i + 3]) ? adv(all[i]) === 0 && hasBase.has(all[i + 3]) : texts?.get(all[i]) === "";
24660
+ g = [];
24661
+ for (const late of [false, true]) {
24662
+ if (late) quietAt = g.length;
24663
+ for (let i = 0; i < all.length; i += 4) if (quiet(i) === late) g.push(all[i], all[i + 1], all[i + 2], all[i + 3]);
24622
24664
  }
24665
+ if (quietAt === g.length) quietAt = -1;
24623
24666
  }
24624
- const fieldFonts = /* @__PURE__ */ new Set();
24625
- for (const pg of model.pages) for (const it of pg.items) if (it.t === "field") fieldFonts.add(fieldFont(it));
24626
- const fonts = /* @__PURE__ */ new Map();
24627
- const font = async (key) => {
24628
- if (!fonts.has(key)) {
24629
- const full = fontStore2.bytes.get(key);
24630
- let bytes = full;
24631
- if (!bytes) throw new Error(`Font "${key}" is not loaded`);
24632
- const glyphs = shaped.get(key);
24633
- if (opt.subset && !fieldFonts.has(key) && !fontStore2.get(key)["OS/2"]?.fsType?.noSubsetting) {
24634
- const cps = used.get(key) || /* @__PURE__ */ new Set([32]);
24635
- try {
24636
- const sub = withPadding(await opt.subset(bytes, cps, glyphs ? { glyphs: glyphs.keys() } : void 0));
24637
- const f4 = fontkit2.create(sub);
24638
- for (const cp of cps) void f4.glyphForCodePoint(cp).advanceWidth;
24639
- for (const id of glyphs?.keys() || []) void f4.getGlyph(id).advanceWidth;
24640
- bytes = sub;
24641
- } catch {
24642
- }
24643
- }
24644
- const pf = await doc.embedFont(bytes, { subset: false, customName: bytes === full ? void 0 : `${subsetTag(bytes)}+${fontStore2.get(key).postscriptName || "Font"}` });
24645
- plainEncoding(pf);
24646
- if (glyphs) addGlyphs(pf, glyphs, !!opt.tagged);
24647
- const file = bytes, e2 = (
24648
- /** @type {any} */
24649
- pf.embedder
24650
- );
24651
- if (!e2.isCFF()) e2.embedFontStream = async (c) => c.register(PDFRawStream2.of(c.obj({ Filter: "FlateDecode", Length1: file.length }), await deflate(file)));
24652
- fonts.set(key, pf);
24667
+ const starts = run.rtl ? [...new Set(all.filter((_, i) => i % 4 === 3))].sort((a, b) => a - b) : [];
24668
+ let inSpan = false;
24669
+ for (let i = 0; i < g.length; i += 4) {
24670
+ const id = g[i], gx = g[i + 1], gy = g[i + 2], cl = g[i + 3];
24671
+ if (i === quietAt) {
24672
+ flush();
24673
+ c.s += span("");
24674
+ inSpan = true;
24675
+ } else if (run.rtl && spans2?.has(cl) && (quietAt < 0 || i < quietAt) && (i === 0 || g[i - 1] !== cl)) {
24676
+ flush();
24677
+ c.s += span(run.text.slice(cl, starts[starts.indexOf(cl) + 1] ?? run.text.length));
24678
+ inSpan = true;
24653
24679
  }
24654
- return fonts.get(key);
24655
- };
24656
- const images = /* @__PURE__ */ new Map();
24657
- const image2 = async (src) => {
24658
- if (images.has(src)) return images.get(src);
24659
- const out = await loadImage(src).catch((e2) => ({ img: null, why: `it could not be embedded (${e2?.message || e2})` }));
24660
- images.set(src, out);
24661
- return out;
24662
- };
24663
- async function loadImage(src) {
24664
- const local = /^data:/i.test(src);
24665
- if (local && dataUriBytes(src) > IMAGE_LIMITS.bytes) return { img: null, why: "it is larger than the image size limit" };
24666
- if (!local && !opt.fetchImage) return { img: null, why: "it is a URL, and no fetchImage was given to load it" };
24667
- const bytes = local ? dataUriBytesOf(src) : await opt.fetchImage(src);
24668
- if (!bytes?.length) return { img: null, why: "it could not be loaded" };
24669
- if (bytes.length > IMAGE_LIMITS.bytes) return { img: null, why: "it is larger than the image size limit" };
24670
- const info = imageInfoOf(bytes);
24671
- if (info?.type === "svg") {
24672
- const svg = parseSvg(new TextDecoder().decode(bytes));
24673
- return svg ? { img: { svg, width: svg.w, height: svg.h } } : { img: null, why: "the SVG could not be read" };
24680
+ if (gy !== rise) {
24681
+ flush();
24682
+ c.s += `${n4(gy)} Ts
24683
+ `;
24684
+ rise = gy;
24674
24685
  }
24675
- if (info?.type === "png") {
24676
- const why = "w" in info && !/** @type {any} */
24677
- info.suspect && info.w * info.h <= IMAGE_LIMITS.pixels ? pngProblem(bytes) : "too large or animated";
24678
- return why ? { img: null, why: `the PNG is not usable (${why})` } : { img: await doc.embedPng(bytes) };
24686
+ const adj = Math.round((pen - gx) * per * 1e3) / 1e3;
24687
+ if (id === 0 && (c.tagged || formatAt(run, cl))) pen = gx;
24688
+ else {
24689
+ arr += (adj ? `${adj}` : "") + `<${hex4(id)}>`;
24690
+ pen = gx + adv(id) * size / f4.unitsPerEm;
24679
24691
  }
24680
- if (info?.type === "jpeg") return { img: await doc.embedJpg(bytes) };
24681
- return { img: null, why: info ? `${info.type.toUpperCase()} is not embedded (PNG, JPEG and SVG are)` : "it is not a PNG, JPEG or SVG" };
24682
- }
24683
- const ctx = doc.context;
24684
- const res = ctx.obj({ Font: {}, ExtGState: {}, XObject: {} });
24685
- const resRef = ctx.register(res);
24686
- const names = /* @__PURE__ */ new Map();
24687
- const nameOf = (kind, key, value) => {
24688
- let n5 = names.get(key);
24689
- if (!n5) {
24690
- n5 = `${kind[0]}${names.size + 1}`;
24691
- names.set(key, n5);
24692
- res.get(PDFName3.of(kind)).set(PDFName3.of(n5), value());
24692
+ if (inSpan && (i + 4 >= g.length || (quietAt < 0 || i + 4 < quietAt) && g[i + 7] !== cl || i + 4 === quietAt)) {
24693
+ flush();
24694
+ c.s += "EMC\n";
24695
+ inSpan = false;
24693
24696
  }
24694
- return n5;
24695
- };
24696
- const shared = {
24697
- font: (pf) => nameOf("Font", pf, () => pf.ref),
24698
- gs: (ca, CA) => nameOf("ExtGState", `gs ${ca} ${CA}`, () => ctx.obj({ Type: "ExtGState", ca, CA })),
24699
- image: (img) => nameOf("XObject", img, () => img.ref)
24700
- };
24697
+ }
24698
+ flush();
24699
+ if (rise) c.s += "0 Ts\n";
24700
+ c.s += "ET\n";
24701
+ if (!run.rtl) c.s += "EMC\n";
24702
+ }
24703
+ function glyphText(run, font, out, spans2) {
24704
+ const g = run.glyphs;
24705
+ const starts = [...new Set(g.filter((_, i) => i % 4 === 3))].sort((a, b) => a - b);
24706
+ for (let k = 0; k < starts.length; k++) {
24707
+ const chars = [...run.text.slice(starts[k], starts[k + 1] ?? run.text.length)];
24708
+ const ids = [];
24709
+ for (let i = 0; i < g.length; i += 4) if (g[i + 3] === starts[k]) ids.push(g[i]);
24710
+ const own = new Map(chars.map((ch) => [font.glyphForCodePoint(ch.codePointAt(0)).id, ch]));
24711
+ const rest2 = chars.filter((ch) => !ids.includes(font.glyphForCodePoint(ch.codePointAt(0)).id)).join("");
24712
+ const carrier = rest2 ? ids.find((id) => !own.has(id) && font.getGlyph(id).advanceWidth > 0) ?? ids.find((id) => !own.has(id)) : void 0;
24713
+ let ok = !rest2 || carrier !== void 0;
24714
+ const want = /* @__PURE__ */ new Map();
24715
+ for (const id of ids) {
24716
+ const t = own.get(id) ?? (id === carrier ? rest2 : "");
24717
+ if (want.has(id) && want.get(id) !== t) ok = false;
24718
+ want.set(id, t);
24719
+ }
24720
+ for (const [id, t] of want) {
24721
+ if (!out.has(id)) out.set(id, t);
24722
+ else if (out.get(id) !== t) ok = false;
24723
+ }
24724
+ if (!ok && run.rtl && spans2) {
24725
+ let s = spans2.get(run);
24726
+ if (!s) spans2.set(run, s = /* @__PURE__ */ new Set());
24727
+ s.add(starts[k]);
24728
+ }
24729
+ }
24730
+ }
24731
+ function insideClip(it, m) {
24732
+ const k = it.clip, asc = m.asc * it.size, desc = m.desc * it.size;
24733
+ return it.lines.every((l) => l.w != null && !l.runs && l.x >= k.x && l.x + l.w <= k.x + k.w && l.y - asc >= k.y && l.y + desc <= k.y + k.h);
24734
+ }
24735
+ var Content = class {
24736
+ constructor(shared) {
24737
+ this.shared = shared;
24738
+ this._s = "";
24739
+ this.bt = false;
24740
+ this.tx = 0;
24741
+ this.ty = 0;
24742
+ this.st = { fill: "0 0 0", stroke: "0 0 0", lw: 1, dash: "[] 0", ca: 1, CA: 1, font: "", size: 0 };
24743
+ this.saved = [];
24744
+ this.lines = null;
24745
+ this.tagged = false;
24746
+ this.artLines = false;
24747
+ }
24748
+ /**
24749
+ * The operators so far. Any other drawing reads or appends to it, so the lines waiting are drawn first: the text
24750
+ * of a table's cells stays in one text object (relative moves, which compress to little) instead of one per cell.
24751
+ */
24752
+ get s() {
24753
+ this.flushLines();
24754
+ return this._s;
24755
+ }
24756
+ set s(v) {
24757
+ this._s = v;
24758
+ }
24759
+ /**
24760
+ * A straight line. Untagged, it waits until something other than text is drawn (table borders between cell
24761
+ * texts); collinear segments that touch, in the same stroke, become one (a row's border is one line).
24762
+ * ponytail: lines move after the text drawn just after them; both are thin and do not cover each other.
24763
+ */
24764
+ line(stroke, w, dash, x1, y1, x2, y2) {
24765
+ if (this.tagged && !this.artLines) {
24766
+ this.paint(null, stroke, w, dash);
24767
+ this._s += `${n2(x1)} ${n2(y1)} m ${n2(x2)} ${n2(y2)} l S
24768
+ `;
24769
+ return;
24770
+ }
24771
+ const key = `${stroke.rgb}|${stroke.opacity}|${w}|${(dash || []).join(" ")}`;
24772
+ if (this.lines && this.lines.key !== key) this.flushLines();
24773
+ this.lines ||= { key, stroke, w, dash, segs: [] };
24774
+ const segs = this.lines.segs, last = segs[segs.length - 1];
24775
+ const eq = (a, b) => Math.abs(a - b) < 5e-3;
24776
+ if (last && eq(last[1], y1) && eq(last[3], y2) && eq(y1, y2) && eq(last[2], x1)) last[2] = x2;
24777
+ else if (last && eq(last[0], x1) && eq(last[2], x2) && eq(x1, x2) && eq(last[3], y1)) last[3] = y2;
24778
+ else segs.push([x1, y1, x2, y2]);
24779
+ }
24780
+ flushLines() {
24781
+ const L = this.lines;
24782
+ if (!L) return;
24783
+ this.lines = null;
24784
+ this.et();
24785
+ const end = this.artLines ? "EMC\n" : "";
24786
+ if (end) this._s += "/Artifact BMC\n";
24787
+ this.paint(null, L.stroke, L.w, L.dash);
24788
+ const g = L.segs;
24789
+ const dy = g.length > 2 ? Math.round((g[1][1] - g[0][1]) * 100) : 0;
24790
+ if (dy && g.every((q3, i) => q3[1] === q3[3] && n2(q3[0]) === n2(g[0][0]) && n2(q3[2]) === n2(g[0][2]) && Math.abs(q3[1] - (g[0][1] + i * dy / 100)) < 0.01)) {
24791
+ const one = `${n2(g[0][0])} ${n2(g[0][1])} m ${n2(g[0][2])} ${n2(g[0][1])} l S
24792
+ `;
24793
+ this._s += `q
24794
+ ${one}${`1 0 0 1 0 ${dy / 100} cm ${one}`.repeat(g.length - 1)}Q
24795
+ ${end}`;
24796
+ return;
24797
+ }
24798
+ this._s += g.map(([a, b, c, d]) => `${n2(a)} ${n2(b)} m ${n2(c)} ${n2(d)} l`).join(" ") + " S\n" + end;
24799
+ }
24800
+ q() {
24801
+ this.et();
24802
+ this._s += "q\n";
24803
+ this.saved.push({ ...this.st });
24804
+ }
24805
+ Q() {
24806
+ this.et();
24807
+ this._s += "Q\n";
24808
+ this.st = this.saved.pop();
24809
+ }
24810
+ /**
24811
+ * A line of plain text at (x, y). Lines follow one another in one text object, each placed relative to the
24812
+ * last (in whole hundredths, so nothing drifts): most of a table page is these.
24813
+ * @param {string} str the glyphs, as a literal string's escaped body (array: a TJ array's body)
24814
+ */
24815
+ text(pf, size, color2, x, y, str, array = false) {
24816
+ this.setPaint(color2, null);
24817
+ this.setFont(pf, size);
24818
+ const X = Math.round(x * 10) * 10, Y = Math.round(y * 10) * 10;
24819
+ if (!this.bt) {
24820
+ this._s += "BT\n";
24821
+ this.bt = true;
24822
+ this.tx = 0;
24823
+ this.ty = 0;
24824
+ }
24825
+ this._s += array ? `${(X - this.tx) / 100} ${(Y - this.ty) / 100} Td [${str}] TJ
24826
+ ` : `${(X - this.tx) / 100} ${(Y - this.ty) / 100} Td (${str}) Tj
24827
+ `;
24828
+ this.tx = X;
24829
+ this.ty = Y;
24830
+ }
24831
+ /** end the open text object, if any: anything but text needs that (and draws the lines waiting) */
24832
+ et() {
24833
+ if (this.bt) {
24834
+ this._s += "ET\n";
24835
+ this.bt = false;
24836
+ }
24837
+ if (this.lines) this.flushLines();
24838
+ }
24839
+ lineWidth(w) {
24840
+ if (this.lines) this.flushLines();
24841
+ this.setLineWidth(w);
24842
+ }
24843
+ setLineWidth(w) {
24844
+ if (this.st.lw !== w) {
24845
+ this._s += `${n3(w)} w
24846
+ `;
24847
+ this.st.lw = w;
24848
+ }
24849
+ }
24850
+ /** the graphics state that sets these opacities, or undefined when they are set already */
24851
+ gsFor(ca, CA) {
24852
+ return ca === this.st.ca && CA === this.st.CA ? void 0 : this.shared.gs(ca, CA);
24853
+ }
24854
+ alpha(ca, CA) {
24855
+ if (this.lines) this.flushLines();
24856
+ this.setAlpha(ca, CA);
24857
+ }
24858
+ setAlpha(ca, CA) {
24859
+ const g = this.gsFor(ca, CA);
24860
+ if (g) {
24861
+ this._s += `/${g} gs
24862
+ `;
24863
+ this.st.ca = ca;
24864
+ this.st.CA = CA;
24865
+ }
24866
+ }
24867
+ /** set up to fill with fill and/or stroke with stroke (width w, dash); the lines waiting are drawn first */
24868
+ paint(fill3, stroke, w = 1, dash = null) {
24869
+ if (this.lines) this.flushLines();
24870
+ this.setPaint(fill3, stroke, w, dash);
24871
+ }
24872
+ setPaint(fill3, stroke, w = 1, dash = null) {
24873
+ const st = this.st;
24874
+ if (fill3 && fill3.rgb !== st.fill) {
24875
+ this._s += `${fill3.rgb} rg
24876
+ `;
24877
+ st.fill = fill3.rgb;
24878
+ }
24879
+ if (stroke) {
24880
+ if (stroke.rgb !== st.stroke) {
24881
+ this._s += `${stroke.rgb} RG
24882
+ `;
24883
+ st.stroke = stroke.rgb;
24884
+ }
24885
+ this.setLineWidth(w);
24886
+ const d = `[${(dash || []).map(n3).join(" ")}] 0`;
24887
+ if (d !== st.dash) {
24888
+ this._s += `${d} d
24889
+ `;
24890
+ st.dash = d;
24891
+ }
24892
+ }
24893
+ this.setAlpha(fill3 ? fill3.opacity : st.ca, stroke ? stroke.opacity : st.CA);
24894
+ }
24895
+ font(pf, size) {
24896
+ if (this.lines) this.flushLines();
24897
+ this.setFont(pf, size);
24898
+ }
24899
+ setFont(pf, size) {
24900
+ const name = this.shared.font(pf);
24901
+ if (name !== this.st.font || size !== this.st.size) {
24902
+ this._s += `/${name} ${n3(size)} Tf
24903
+ `;
24904
+ this.st.font = name;
24905
+ this.st.size = size;
24906
+ }
24907
+ }
24908
+ };
24909
+ function latin1(s) {
24910
+ const B = (
24911
+ /** @type {any} */
24912
+ globalThis.Buffer
24913
+ );
24914
+ if (B) return B.from(s, "latin1");
24915
+ const out = new Uint8Array(s.length);
24916
+ for (let i = 0; i < s.length; i++) out[i] = s.charCodeAt(i);
24917
+ return out;
24918
+ }
24919
+ async function deflate(bytes) {
24920
+ if (zslots.free > 0) zslots.free--;
24921
+ else await new Promise((ok) => zslots.wait.push(
24922
+ /** @type {() => void} */
24923
+ ok
24924
+ ));
24925
+ try {
24926
+ return await deflateNow(bytes);
24927
+ } finally {
24928
+ const next = zslots.wait.shift();
24929
+ if (next) next();
24930
+ else zslots.free++;
24931
+ }
24932
+ }
24933
+ var zslots = { free: 4, wait: (
24934
+ /** @type {(() => void)[]} */
24935
+ []
24936
+ ) };
24937
+ var NODE_ZLIB = (
24938
+ /** @type {any} */
24939
+ globalThis.process?.getBuiltinModule?.("node:zlib")
24940
+ );
24941
+ async function deflateNow(bytes) {
24942
+ const zlib = NODE_ZLIB;
24943
+ if (zlib) return new Promise((ok, fail2) => zlib.deflate(bytes, { level: 9 }, (e2, z) => e2 ? fail2(e2) : ok(z)));
24944
+ if (typeof CompressionStream === "undefined") return (await import("fflate")).zlibSync(bytes);
24945
+ return new Uint8Array(await new Response(new Blob([bytes]).stream().pipeThrough(new CompressionStream("deflate"))).arrayBuffer());
24946
+ }
24947
+
24948
+ // src/exporters/pdf.js
24949
+ var fontkit2 = (
24950
+ /** @type {any} */
24951
+ fontkitModule2.default || fontkitModule2
24952
+ );
24953
+ function exportPdf(model, fontStore2, opt = {}) {
24954
+ return withDeadline(
24955
+ pdfOf(model, fontStore2, opt),
24956
+ /** @type {any} */
24957
+ opt.timeoutMs
24958
+ );
24959
+ }
24960
+ async function pdfOf(model, fontStore2, opt) {
24961
+ const fixed2 = fixedDate(opt);
24962
+ const when = fixed2 ?? /* @__PURE__ */ new Date();
24963
+ const fileId = fixed2 ? await contentId(model, opt, when) : void 0;
24964
+ const doc = await PDFDocument.create({ updateMetadata: false });
24965
+ doc.registerFontkit(fontkit2);
24966
+ doc.setTitle(opt.title || "Report");
24967
+ doc.setProducer("ReportWright");
24968
+ doc.setCreator("ReportWright");
24969
+ if (opt.author) doc.setAuthor(opt.author);
24970
+ setInfoDates(doc, when);
24971
+ if (fileId) doc.context.trailerInfo.ID = doc.context.obj([PDFHexString5.of(fileId), PDFHexString5.of(fileId)]);
24972
+ const used = /* @__PURE__ */ new Map();
24973
+ const shaped = /* @__PURE__ */ new Map();
24974
+ const spans2 = /* @__PURE__ */ new WeakMap();
24975
+ const chars = (key) => {
24976
+ let c = used.get(key);
24977
+ if (!c) {
24978
+ c = /* @__PURE__ */ new Set([32]);
24979
+ used.set(key, c);
24980
+ }
24981
+ return c;
24982
+ };
24983
+ for (const pg of model.pages) for (const it of pg.items.flatMap((i) => i.t === "field" ? i.draw.filter((d) => d.t === "text") : i.t === "text" ? [i] : [])) {
24984
+ for (const l of it.lines) {
24985
+ if (!l.runs) {
24986
+ const c = chars(it.font);
24987
+ for (const ch of l.text) c.add(
24988
+ /** @type {number} */
24989
+ ch.codePointAt(0)
24990
+ );
24991
+ continue;
24992
+ }
24993
+ for (const r of l.runs) {
24994
+ const c = chars(r.font);
24995
+ for (const ch of r.text) c.add(
24996
+ /** @type {number} */
24997
+ ch.codePointAt(0)
24998
+ );
24999
+ let g = shaped.get(r.font);
25000
+ if (!g) {
25001
+ g = /* @__PURE__ */ new Map();
25002
+ shaped.set(r.font, g);
25003
+ }
25004
+ glyphText(r, fontStore2.get(r.font), g, spans2);
25005
+ }
25006
+ }
25007
+ }
25008
+ const fieldFonts = /* @__PURE__ */ new Set();
25009
+ for (const pg of model.pages) for (const it of pg.items) if (it.t === "field") fieldFonts.add(fieldFont(it));
25010
+ const fonts = /* @__PURE__ */ new Map();
25011
+ const font = async (key) => {
25012
+ if (!fonts.has(key)) {
25013
+ const full = fontStore2.bytes.get(key);
25014
+ let bytes = full;
25015
+ if (!bytes) throw new Error(`Font "${key}" is not loaded`);
25016
+ const glyphs = shaped.get(key);
25017
+ if (opt.subset && !fieldFonts.has(key) && !fontStore2.get(key)["OS/2"]?.fsType?.noSubsetting) {
25018
+ const cps = used.get(key) || /* @__PURE__ */ new Set([32]);
25019
+ try {
25020
+ const sub = withPadding(await opt.subset(bytes, cps, glyphs ? { glyphs: glyphs.keys() } : void 0));
25021
+ const f4 = fontkit2.create(sub);
25022
+ for (const cp of cps) void f4.glyphForCodePoint(cp).advanceWidth;
25023
+ for (const id of glyphs?.keys() || []) void f4.getGlyph(id).advanceWidth;
25024
+ bytes = sub;
25025
+ } catch {
25026
+ }
25027
+ }
25028
+ const pf = await doc.embedFont(bytes, { subset: false, customName: bytes === full ? void 0 : `${subsetTag(bytes)}+${fontStore2.get(key).postscriptName || "Font"}` });
25029
+ plainEncoding(pf);
25030
+ if (glyphs) addGlyphs(pf, glyphs, !!opt.tagged);
25031
+ const file = bytes, e2 = (
25032
+ /** @type {any} */
25033
+ pf.embedder
25034
+ );
25035
+ if (!e2.isCFF()) e2.embedFontStream = async (c) => c.register(PDFRawStream2.of(c.obj({ Filter: "FlateDecode", Length1: file.length }), await deflate(file)));
25036
+ fonts.set(key, pf);
25037
+ }
25038
+ return fonts.get(key);
25039
+ };
25040
+ const images = /* @__PURE__ */ new Map();
25041
+ const image2 = async (src) => {
25042
+ if (images.has(src)) return images.get(src);
25043
+ const out = await loadImage(src).catch((e2) => ({ img: null, why: `it could not be embedded (${e2?.message || e2})` }));
25044
+ images.set(src, out);
25045
+ return out;
25046
+ };
25047
+ async function loadImage(src) {
25048
+ const local = /^data:/i.test(src);
25049
+ if (local && dataUriBytes(src) > IMAGE_LIMITS.bytes) return { img: null, why: "it is larger than the image size limit" };
25050
+ if (!local && !opt.fetchImage) return { img: null, why: "it is a URL, and no fetchImage was given to load it" };
25051
+ const bytes = local ? dataUriBytesOf(src) : await opt.fetchImage(src);
25052
+ if (!bytes?.length) return { img: null, why: "it could not be loaded" };
25053
+ if (bytes.length > IMAGE_LIMITS.bytes) return { img: null, why: "it is larger than the image size limit" };
25054
+ const info = imageInfoOf(bytes);
25055
+ if (info?.type === "svg") {
25056
+ const svg = parseSvg(new TextDecoder().decode(bytes));
25057
+ return svg ? { img: { svg, width: svg.w, height: svg.h } } : { img: null, why: "the SVG could not be read" };
25058
+ }
25059
+ if (info?.type === "png") {
25060
+ const why = "w" in info && !/** @type {any} */
25061
+ info.suspect && info.w * info.h <= IMAGE_LIMITS.pixels ? pngProblem(bytes) : "too large or animated";
25062
+ return why ? { img: null, why: `the PNG is not usable (${why})` } : { img: await doc.embedPng(bytes) };
25063
+ }
25064
+ if (info?.type === "jpeg") return { img: await doc.embedJpg(bytes) };
25065
+ return { img: null, why: info ? `${info.type.toUpperCase()} is not embedded (PNG, JPEG and SVG are)` : "it is not a PNG, JPEG or SVG" };
25066
+ }
25067
+ const ctx = doc.context;
25068
+ const res = ctx.obj({ Font: {}, ExtGState: {}, XObject: {} });
25069
+ const resRef = ctx.register(res);
25070
+ const names = /* @__PURE__ */ new Map();
25071
+ const nameOf = (kind, key, value) => {
25072
+ let n5 = names.get(key);
25073
+ if (!n5) {
25074
+ n5 = `${kind[0]}${names.size + 1}`;
25075
+ names.set(key, n5);
25076
+ res.get(PDFName3.of(kind)).set(PDFName3.of(n5), value());
25077
+ }
25078
+ return n5;
25079
+ };
25080
+ const shared = {
25081
+ font: (pf) => nameOf("Font", pf, () => pf.ref),
25082
+ gs: (ca, CA) => nameOf("ExtGState", `gs ${ca} ${CA}`, () => ctx.obj({ Type: "ExtGState", ca, CA })),
25083
+ image: (img) => nameOf("XObject", img, () => img.ref)
25084
+ };
24701
25085
  const links = [];
24702
25086
  const taken = /* @__PURE__ */ new Set();
24703
25087
  const uniqueName = (n5) => {
@@ -24831,7 +25215,7 @@ async function pdfOf(model, fontStore2, opt) {
24831
25215
  const form = doc.getForm();
24832
25216
  const f4 = await font(fieldFont(it));
24833
25217
  const name = uniqueName(it.name);
24834
- const box = { x: it.x, y: H - it.y - it.h, width: it.w, height: it.h, borderColor: rgb2(0.61, 0.64, 0.69), borderWidth: 0.75, backgroundColor: rgb2(1, 1, 1) };
25218
+ const box = { x: it.x, y: H - it.y - it.h, width: it.w, height: it.h, borderColor: rgb3(0.61, 0.64, 0.69), borderWidth: 0.75, backgroundColor: rgb3(1, 1, 1) };
24835
25219
  let field;
24836
25220
  if (it.kind === "checkbox") {
24837
25221
  field = form.createCheckBox(name);
@@ -24888,7 +25272,7 @@ async function pdfOf(model, fontStore2, opt) {
24888
25272
  if (it.required) field.enableRequired();
24889
25273
  if (it.readOnly) field.enableReadOnly();
24890
25274
  if (tags) {
24891
- field.acroField.dict.set(PDFName3.of("TU"), PDFHexString4.fromText(it.label || it.name || name));
25275
+ field.acroField.dict.set(PDFName3.of("TU"), PDFHexString5.fromText(it.label || it.name || name));
24892
25276
  const w = field.acroField.getWidgets()[0];
24893
25277
  const ref3 = w && doc.context.getObjectRef(w.dict);
24894
25278
  if (ref3) tags.annot(pi, ii, ref3, "field");
@@ -24962,7 +25346,7 @@ async function pdfOf(model, fontStore2, opt) {
24962
25346
  const to = l.href ? { A: { Type: "Action", S: "URI", URI: pdfLiteral(asciiUrl(l.href)) } } : { Dest: dest(l.to) };
24963
25347
  if (!l.href && !to.Dest) continue;
24964
25348
  const annot = doc.context.obj({ Type: "Annot", Subtype: "Link", F: 4, Rect: l.rect, Border: [0, 0, 0], ...to });
24965
- if (tags) annot.set(PDFName3.of("Contents"), PDFHexString4.fromText(String(l.text || l.tip || l.href || "Link")));
25349
+ if (tags) annot.set(PDFName3.of("Contents"), PDFHexString5.fromText(String(l.text || l.tip || l.href || "Link")));
24966
25350
  const ref2 = doc.context.register(annot);
24967
25351
  if (tags) tags.annot(l.pi, l.ii, ref2, "link");
24968
25352
  const annots = l.page.node.lookup(PDFName3.of("Annots"));
@@ -25001,110 +25385,14 @@ function fixedDate(opt) {
25001
25385
  }
25002
25386
  async function contentId(model, opt, when) {
25003
25387
  const bytes = new TextEncoder().encode(JSON.stringify([{ ...model, stats: void 0 }, opt.title || "Report", opt.author || "", when.toISOString()]));
25004
- const digest = new Uint8Array(await globalThis.crypto.subtle.digest("SHA-256", bytes));
25005
- return Array.from(digest.subarray(0, 16), (b) => b.toString(16).padStart(2, "0")).join("").toUpperCase();
25006
- }
25007
- function fieldFont(it) {
25008
- for (const d of it.draw) if (d.t === "text") {
25009
- for (const l of d.lines) for (const r of l.runs || []) if (r.font !== it.font && r.text.trim()) return r.font;
25010
- }
25011
- return it.font;
25012
- }
25013
- function drawRun(c, run, x, y, size, pf, color2, spans2, texts) {
25014
- const f4 = pf.embedder.font;
25015
- const per = 1e3 / size;
25016
- const span = (text2) => `/Span <</ActualText ${PDFHexString4.fromText(text2).toString()}>> BDC
25017
- `;
25018
- c.et();
25019
- c.paint(color2, null);
25020
- c.font(pf, size);
25021
- if (!run.rtl) c.s += span(run.text);
25022
- c.s += `BT ${n2(x)} ${n2(y)} Td
25023
- `;
25024
- let arr = "";
25025
- let pen = 0, rise = 0;
25026
- const flush = () => {
25027
- if (arr) c.s += `[${arr}] TJ
25028
- `;
25029
- arr = "";
25030
- };
25031
- const all = run.glyphs;
25032
- const adv = (id) => f4.getGlyph(id).advanceWidth;
25033
- let g = all, quietAt = -1;
25034
- if (run.rtl) {
25035
- const hasBase = /* @__PURE__ */ new Set();
25036
- for (let i = 0; i < all.length; i += 4) if (adv(all[i]) > 0) hasBase.add(all[i + 3]);
25037
- const quiet = (i) => spans2?.has(all[i + 3]) ? adv(all[i]) === 0 && hasBase.has(all[i + 3]) : texts?.get(all[i]) === "";
25038
- g = [];
25039
- for (const late of [false, true]) {
25040
- if (late) quietAt = g.length;
25041
- for (let i = 0; i < all.length; i += 4) if (quiet(i) === late) g.push(all[i], all[i + 1], all[i + 2], all[i + 3]);
25042
- }
25043
- if (quietAt === g.length) quietAt = -1;
25044
- }
25045
- const starts = run.rtl ? [...new Set(all.filter((_, i) => i % 4 === 3))].sort((a, b) => a - b) : [];
25046
- let inSpan = false;
25047
- for (let i = 0; i < g.length; i += 4) {
25048
- const id = g[i], gx = g[i + 1], gy = g[i + 2], cl = g[i + 3];
25049
- if (i === quietAt) {
25050
- flush();
25051
- c.s += span("");
25052
- inSpan = true;
25053
- } else if (run.rtl && spans2?.has(cl) && (quietAt < 0 || i < quietAt) && (i === 0 || g[i - 1] !== cl)) {
25054
- flush();
25055
- c.s += span(run.text.slice(cl, starts[starts.indexOf(cl) + 1] ?? run.text.length));
25056
- inSpan = true;
25057
- }
25058
- if (gy !== rise) {
25059
- flush();
25060
- c.s += `${n4(gy)} Ts
25061
- `;
25062
- rise = gy;
25063
- }
25064
- const adj = Math.round((pen - gx) * per * 1e3) / 1e3;
25065
- if (id === 0 && c.tagged) pen = gx;
25066
- else {
25067
- arr += (adj ? `${adj}` : "") + `<${hex4(id)}>`;
25068
- pen = gx + adv(id) * size / f4.unitsPerEm;
25069
- }
25070
- if (inSpan && (i + 4 >= g.length || (quietAt < 0 || i + 4 < quietAt) && g[i + 7] !== cl || i + 4 === quietAt)) {
25071
- flush();
25072
- c.s += "EMC\n";
25073
- inSpan = false;
25074
- }
25075
- }
25076
- flush();
25077
- if (rise) c.s += "0 Ts\n";
25078
- c.s += "ET\n";
25079
- if (!run.rtl) c.s += "EMC\n";
25388
+ const digest = new Uint8Array(await globalThis.crypto.subtle.digest("SHA-256", bytes));
25389
+ return Array.from(digest.subarray(0, 16), (b) => b.toString(16).padStart(2, "0")).join("").toUpperCase();
25080
25390
  }
25081
- function glyphText(run, font, out, spans2) {
25082
- const g = run.glyphs;
25083
- const starts = [...new Set(g.filter((_, i) => i % 4 === 3))].sort((a, b) => a - b);
25084
- for (let k = 0; k < starts.length; k++) {
25085
- const chars = [...run.text.slice(starts[k], starts[k + 1] ?? run.text.length)];
25086
- const ids = [];
25087
- for (let i = 0; i < g.length; i += 4) if (g[i + 3] === starts[k]) ids.push(g[i]);
25088
- const own = new Map(chars.map((ch) => [font.glyphForCodePoint(ch.codePointAt(0)).id, ch]));
25089
- const rest2 = chars.filter((ch) => !ids.includes(font.glyphForCodePoint(ch.codePointAt(0)).id)).join("");
25090
- const carrier = rest2 ? ids.find((id) => !own.has(id) && font.getGlyph(id).advanceWidth > 0) ?? ids.find((id) => !own.has(id)) : void 0;
25091
- let ok = !rest2 || carrier !== void 0;
25092
- const want = /* @__PURE__ */ new Map();
25093
- for (const id of ids) {
25094
- const t = own.get(id) ?? (id === carrier ? rest2 : "");
25095
- if (want.has(id) && want.get(id) !== t) ok = false;
25096
- want.set(id, t);
25097
- }
25098
- for (const [id, t] of want) {
25099
- if (!out.has(id)) out.set(id, t);
25100
- else if (out.get(id) !== t) ok = false;
25101
- }
25102
- if (!ok && run.rtl && spans2) {
25103
- let s = spans2.get(run);
25104
- if (!s) spans2.set(run, s = /* @__PURE__ */ new Set());
25105
- s.add(starts[k]);
25106
- }
25391
+ function fieldFont(it) {
25392
+ for (const d of it.draw) if (d.t === "text") {
25393
+ for (const l of d.lines) for (const r of l.runs || []) if (r.font !== it.font && r.text.trim()) return r.font;
25107
25394
  }
25395
+ return it.font;
25108
25396
  }
25109
25397
  var pdfLiteral = (s) => PDFString4.of(String(s).replace(/[\\()]/g, "\\$&"));
25110
25398
  function plainEncoding(pf) {
@@ -25122,7 +25410,7 @@ function plainEncoding(pf) {
25122
25410
  }
25123
25411
  return hex6;
25124
25412
  };
25125
- e2.encodeText = (text2) => PDFHexString4.of(e2.hexOf(text2));
25413
+ e2.encodeText = (text2) => PDFHexString5.of(e2.hexOf(text2));
25126
25414
  e2.widthOfTextAtSize = (text2, size) => {
25127
25415
  let w = 0;
25128
25416
  for (const ch of text2) w += f4.glyphForCodePoint(
@@ -25213,7 +25501,7 @@ function addOutline(doc, bookmarks, dest) {
25213
25501
  if (!Dest) continue;
25214
25502
  const level = Math.max(0, Math.min(9, Number(b.level) || 0));
25215
25503
  while (stack2[stack2.length - 1].level >= level) stack2.pop();
25216
- const node2 = { ref: ctx.nextRef(), level, kids: [], Title: PDFHexString4.fromText(b.label), Dest };
25504
+ const node2 = { ref: ctx.nextRef(), level, kids: [], Title: PDFHexString5.fromText(b.label), Dest };
25217
25505
  stack2[stack2.length - 1].kids.push(node2);
25218
25506
  stack2.push(node2);
25219
25507
  }
@@ -25244,216 +25532,6 @@ function subsetTag(bytes) {
25244
25532
  }
25245
25533
  return tag4;
25246
25534
  }
25247
- function insideClip(it, m) {
25248
- const k = it.clip, asc = m.asc * it.size, desc = m.desc * it.size;
25249
- return it.lines.every((l) => l.w != null && !l.runs && l.x >= k.x && l.x + l.w <= k.x + k.w && l.y - asc >= k.y && l.y + desc <= k.y + k.h);
25250
- }
25251
- var Content = class {
25252
- constructor(shared) {
25253
- this.shared = shared;
25254
- this._s = "";
25255
- this.bt = false;
25256
- this.tx = 0;
25257
- this.ty = 0;
25258
- this.st = { fill: "0 0 0", stroke: "0 0 0", lw: 1, dash: "[] 0", ca: 1, CA: 1, font: "", size: 0 };
25259
- this.saved = [];
25260
- this.lines = null;
25261
- this.tagged = false;
25262
- }
25263
- /**
25264
- * The operators so far. Any other drawing reads or appends to it, so the lines waiting are drawn first: the text
25265
- * of a table's cells stays in one text object (relative moves, which compress to little) instead of one per cell.
25266
- */
25267
- get s() {
25268
- this.flushLines();
25269
- return this._s;
25270
- }
25271
- set s(v) {
25272
- this._s = v;
25273
- }
25274
- /**
25275
- * A straight line. Untagged, it waits until something other than text is drawn (table borders between cell
25276
- * texts); collinear segments that touch, in the same stroke, become one (a row's border is one line).
25277
- * ponytail: lines move after the text drawn just after them; both are thin and do not cover each other.
25278
- */
25279
- line(stroke, w, dash, x1, y1, x2, y2) {
25280
- if (this.tagged) {
25281
- this.paint(null, stroke, w, dash);
25282
- this._s += `${n2(x1)} ${n2(y1)} m ${n2(x2)} ${n2(y2)} l S
25283
- `;
25284
- return;
25285
- }
25286
- const key = `${stroke.rgb}|${stroke.opacity}|${w}|${(dash || []).join(" ")}`;
25287
- if (this.lines && this.lines.key !== key) this.flushLines();
25288
- this.lines ||= { key, stroke, w, dash, segs: [] };
25289
- const segs = this.lines.segs, last = segs[segs.length - 1];
25290
- const eq = (a, b) => Math.abs(a - b) < 5e-3;
25291
- if (last && eq(last[1], y1) && eq(last[3], y2) && eq(y1, y2) && eq(last[2], x1)) last[2] = x2;
25292
- else if (last && eq(last[0], x1) && eq(last[2], x2) && eq(x1, x2) && eq(last[3], y1)) last[3] = y2;
25293
- else segs.push([x1, y1, x2, y2]);
25294
- }
25295
- flushLines() {
25296
- const L = this.lines;
25297
- if (!L) return;
25298
- this.lines = null;
25299
- this.et();
25300
- this.paint(null, L.stroke, L.w, L.dash);
25301
- const g = L.segs;
25302
- const dy = g.length > 2 ? Math.round((g[1][1] - g[0][1]) * 100) : 0;
25303
- if (dy && g.every((q3, i) => q3[1] === q3[3] && n2(q3[0]) === n2(g[0][0]) && n2(q3[2]) === n2(g[0][2]) && Math.abs(q3[1] - (g[0][1] + i * dy / 100)) < 0.01)) {
25304
- const one = `${n2(g[0][0])} ${n2(g[0][1])} m ${n2(g[0][2])} ${n2(g[0][1])} l S
25305
- `;
25306
- this._s += `q
25307
- ${one}${`1 0 0 1 0 ${dy / 100} cm ${one}`.repeat(g.length - 1)}Q
25308
- `;
25309
- return;
25310
- }
25311
- this._s += g.map(([a, b, c, d]) => `${n2(a)} ${n2(b)} m ${n2(c)} ${n2(d)} l`).join(" ") + " S\n";
25312
- }
25313
- q() {
25314
- this.et();
25315
- this._s += "q\n";
25316
- this.saved.push({ ...this.st });
25317
- }
25318
- Q() {
25319
- this.et();
25320
- this._s += "Q\n";
25321
- this.st = this.saved.pop();
25322
- }
25323
- /**
25324
- * A line of plain text at (x, y). Lines follow one another in one text object, each placed relative to the
25325
- * last (in whole hundredths, so nothing drifts): most of a table page is these.
25326
- * @param {string} str the glyphs, as a literal string's escaped body (array: a TJ array's body)
25327
- */
25328
- text(pf, size, color2, x, y, str, array = false) {
25329
- this.setPaint(color2, null);
25330
- this.setFont(pf, size);
25331
- const X = Math.round(x * 10) * 10, Y = Math.round(y * 10) * 10;
25332
- if (!this.bt) {
25333
- this._s += "BT\n";
25334
- this.bt = true;
25335
- this.tx = 0;
25336
- this.ty = 0;
25337
- }
25338
- this._s += array ? `${(X - this.tx) / 100} ${(Y - this.ty) / 100} Td [${str}] TJ
25339
- ` : `${(X - this.tx) / 100} ${(Y - this.ty) / 100} Td (${str}) Tj
25340
- `;
25341
- this.tx = X;
25342
- this.ty = Y;
25343
- }
25344
- /** end the open text object, if any: anything but text needs that (and draws the lines waiting) */
25345
- et() {
25346
- if (this.bt) {
25347
- this._s += "ET\n";
25348
- this.bt = false;
25349
- }
25350
- if (this.lines) this.flushLines();
25351
- }
25352
- lineWidth(w) {
25353
- if (this.lines) this.flushLines();
25354
- this.setLineWidth(w);
25355
- }
25356
- setLineWidth(w) {
25357
- if (this.st.lw !== w) {
25358
- this._s += `${n3(w)} w
25359
- `;
25360
- this.st.lw = w;
25361
- }
25362
- }
25363
- /** the graphics state that sets these opacities, or undefined when they are set already */
25364
- gsFor(ca, CA) {
25365
- return ca === this.st.ca && CA === this.st.CA ? void 0 : this.shared.gs(ca, CA);
25366
- }
25367
- alpha(ca, CA) {
25368
- if (this.lines) this.flushLines();
25369
- this.setAlpha(ca, CA);
25370
- }
25371
- setAlpha(ca, CA) {
25372
- const g = this.gsFor(ca, CA);
25373
- if (g) {
25374
- this._s += `/${g} gs
25375
- `;
25376
- this.st.ca = ca;
25377
- this.st.CA = CA;
25378
- }
25379
- }
25380
- /** set up to fill with fill and/or stroke with stroke (width w, dash); the lines waiting are drawn first */
25381
- paint(fill3, stroke, w = 1, dash = null) {
25382
- if (this.lines) this.flushLines();
25383
- this.setPaint(fill3, stroke, w, dash);
25384
- }
25385
- setPaint(fill3, stroke, w = 1, dash = null) {
25386
- const st = this.st;
25387
- if (fill3 && fill3.rgb !== st.fill) {
25388
- this._s += `${fill3.rgb} rg
25389
- `;
25390
- st.fill = fill3.rgb;
25391
- }
25392
- if (stroke) {
25393
- if (stroke.rgb !== st.stroke) {
25394
- this._s += `${stroke.rgb} RG
25395
- `;
25396
- st.stroke = stroke.rgb;
25397
- }
25398
- this.setLineWidth(w);
25399
- const d = `[${(dash || []).map(n3).join(" ")}] 0`;
25400
- if (d !== st.dash) {
25401
- this._s += `${d} d
25402
- `;
25403
- st.dash = d;
25404
- }
25405
- }
25406
- this.setAlpha(fill3 ? fill3.opacity : st.ca, stroke ? stroke.opacity : st.CA);
25407
- }
25408
- font(pf, size) {
25409
- if (this.lines) this.flushLines();
25410
- this.setFont(pf, size);
25411
- }
25412
- setFont(pf, size) {
25413
- const name = this.shared.font(pf);
25414
- if (name !== this.st.font || size !== this.st.size) {
25415
- this._s += `/${name} ${n3(size)} Tf
25416
- `;
25417
- this.st.font = name;
25418
- this.st.size = size;
25419
- }
25420
- }
25421
- };
25422
- function latin1(s) {
25423
- const B = (
25424
- /** @type {any} */
25425
- globalThis.Buffer
25426
- );
25427
- if (B) return B.from(s, "latin1");
25428
- const out = new Uint8Array(s.length);
25429
- for (let i = 0; i < s.length; i++) out[i] = s.charCodeAt(i);
25430
- return out;
25431
- }
25432
- async function deflate(bytes) {
25433
- if (zslots.free > 0) zslots.free--;
25434
- else await new Promise((ok) => zslots.wait.push(ok));
25435
- try {
25436
- return await deflateNow(bytes);
25437
- } finally {
25438
- const next = zslots.wait.shift();
25439
- if (next) next();
25440
- else zslots.free++;
25441
- }
25442
- }
25443
- var zslots = { free: 4, wait: (
25444
- /** @type {(() => void)[]} */
25445
- []
25446
- ) };
25447
- var NODE_ZLIB = (
25448
- /** @type {any} */
25449
- globalThis.process?.getBuiltinModule?.("node:zlib")
25450
- );
25451
- async function deflateNow(bytes) {
25452
- const zlib = NODE_ZLIB;
25453
- if (zlib) return new Promise((ok, fail2) => zlib.deflate(bytes, { level: 9 }, (e2, z) => e2 ? fail2(e2) : ok(z)));
25454
- if (typeof CompressionStream === "undefined") return (await import("fflate")).zlibSync(bytes);
25455
- return new Uint8Array(await new Response(new Blob([bytes]).stream().pipeThrough(new CompressionStream("deflate"))).arrayBuffer());
25456
- }
25457
25535
  function roundRect(x, y, w, h, r) {
25458
25536
  r = Math.min(r, w / 2, h / 2);
25459
25537
  return `M ${x + r} ${y} H ${x + w - r} A ${r} ${r} 0 0 1 ${x + w} ${y + r} V ${y + h - r} A ${r} ${r} 0 0 1 ${x + w - r} ${y + h} H ${x + r} A ${r} ${r} 0 0 1 ${x} ${y + h - r} V ${y + r} A ${r} ${r} 0 0 1 ${x + r} ${y} Z`;
@@ -25503,6 +25581,7 @@ function createSubsetter(loadWasm) {
25503
25581
  for (const g of opt.glyphs) x.hb_set_add(gs, g);
25504
25582
  }
25505
25583
  x.hb_subset_input_pin_all_axes_to_default(input, face);
25584
+ x.hb_subset_input_pin_axis_location(input, face, tag3("wght"), opt.weight ?? 400);
25506
25585
  const sub = x.hb_subset_or_fail(face, input);
25507
25586
  if (!sub) throw new Error("HarfBuzz: subsetting failed");
25508
25587
  const res = x.hb_face_reference_blob(sub);
@@ -25523,6 +25602,7 @@ function createSubsetter(loadWasm) {
25523
25602
 
25524
25603
  // src/exporters/htmldata.js
25525
25604
  init_format();
25605
+ var dateKey = (d, tz) => tz ? toWall(d, tz) : Date.UTC(d.getFullYear(), d.getMonth(), d.getDate(), d.getHours(), d.getMinutes(), d.getSeconds(), d.getMilliseconds());
25526
25606
  var MAX_DATA_ROWS = 2e4;
25527
25607
  var DATA_STRINGS = { pages: "Pages", tables: "Tables", all: "All", filter: "Filter", toggle: "Show or hide the rows of this group", sortAsc: "Sort ascending", sortDesc: "Sort descending" };
25528
25608
  function tablesData(model, o = {}) {
@@ -25545,7 +25625,7 @@ function tablesData(model, o = {}) {
25545
25625
  rows: r.rows.map((row) => ({
25546
25626
  k: row.kind,
25547
25627
  l: row.level || 0,
25548
- c: row.cells.filter((c) => c.colIndex >= 0).map((c) => [c.colIndex, String(show(c) ?? ""), c.value instanceof Date ? c.value.getTime() : typeof c.value === "number" ? c.value : String(c.value ?? "").toLowerCase(), c.span || 1])
25628
+ c: row.cells.filter((c) => c.colIndex >= 0).map((c) => [c.colIndex, String(show(c) ?? ""), c.value instanceof Date ? dateKey(c.value, model.timeZone || void 0) : typeof c.value === "number" ? c.value : String(c.value ?? "").toLowerCase(), c.span || 1])
25549
25629
  }))
25550
25630
  }));
25551
25631
  }
@@ -25677,13 +25757,29 @@ async function cspHash(text2) {
25677
25757
  }
25678
25758
 
25679
25759
  // src/exporters/html.js
25680
- function exportHtml(model, fontStore2, opt = {}) {
25760
+ function exportHtml(model, options, legacyOpt) {
25761
+ const isStore = (x) => !!x && typeof x.load === "function" && typeof x.get === "function" && x.bytes instanceof Map;
25762
+ let fontStore2, opt;
25763
+ if (isStore(options)) {
25764
+ if (!warnedLegacy) {
25765
+ warnedLegacy = true;
25766
+ console.warn("[reportwright] DEPRECATED: exportHtml(model, fontStore, options) is replaced by exportHtml(model, { fonts: fontStore, ...options }); the old form is removed in 0.9");
25767
+ }
25768
+ fontStore2 = options;
25769
+ opt = legacyOpt || {};
25770
+ } else {
25771
+ opt = /** @type {any} */
25772
+ options || {};
25773
+ fontStore2 = opt.fonts || opt.fontStore;
25774
+ if (!isStore(fontStore2)) throw new TypeError("exportHtml(model, { fonts }): fonts must be a FontStore (defaultFontStore() or new FontStore(loader)), got " + (fontStore2 === void 0 ? "nothing" : typeof fontStore2));
25775
+ }
25681
25776
  return withDeadline(
25682
25777
  htmlOf(model, fontStore2, opt),
25683
25778
  /** @type {any} */
25684
25779
  opt.timeoutMs
25685
25780
  );
25686
25781
  }
25782
+ var warnedLegacy = false;
25687
25783
  async function htmlOf(model, fontStore2, opt) {
25688
25784
  const used = /* @__PURE__ */ new Map();
25689
25785
  const layout = /* @__PURE__ */ new Set();
@@ -26435,9 +26531,22 @@ async function xlsxOf(model, opt) {
26435
26531
  }
26436
26532
 
26437
26533
  // src/exporters/docx.js
26438
- import { zipSync as zipSync3, strToU8 as strToU83 } from "fflate";
26534
+ import { Zip, ZipDeflate, strToU8 as strToU83 } from "fflate";
26439
26535
  var TW = 20;
26440
26536
  var EMU = 12700;
26537
+ var XML_CHUNK = 4 * 1024 * 1024;
26538
+ function slices(s, max) {
26539
+ const out = [];
26540
+ let i = 0;
26541
+ while (s.length - i > max) {
26542
+ const j = s.indexOf(">", i + max);
26543
+ if (j < 0) break;
26544
+ out.push(s.slice(i, j + 1));
26545
+ i = j + 1;
26546
+ }
26547
+ out.push(s.slice(i));
26548
+ return out;
26549
+ }
26441
26550
  var esc3 = (s) => String(s).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
26442
26551
  var hex5 = (c) => {
26443
26552
  const m = /^#?([0-9a-f]{6})$/i.exec(String(c || "").trim());
@@ -26581,9 +26690,9 @@ async function docxOf(model, opt) {
26581
26690
  const margin = 18;
26582
26691
  const media = [];
26583
26692
  let docPr = 0;
26584
- const addMedia = (bytes, ext) => {
26693
+ const addMedia = (bytes2, ext) => {
26585
26694
  const rid = `rIdm${media.length + 1}`;
26586
- media.push({ name: `image${media.length + 1}.${ext}`, bytes, rid });
26695
+ media.push({ name: `image${media.length + 1}.${ext}`, bytes: bytes2, rid });
26587
26696
  return rid;
26588
26697
  };
26589
26698
  const pictureRun = (pic, alt) => {
@@ -26647,7 +26756,7 @@ async function docxOf(model, opt) {
26647
26756
  }
26648
26757
  for (const f4 of figs) if (!f4.used) blocks.push({ kind: "fig", y: f4.box.y, x: f4.box.x, fig: f4 });
26649
26758
  blocks.sort((a, b) => a.y - b.y || a.x - b.x);
26650
- let xml3 = "";
26759
+ let xml2 = "";
26651
26760
  for (let bi = 0; bi < blocks.length; bi++) {
26652
26761
  const b = blocks[bi];
26653
26762
  if (b.kind === "fig") {
@@ -26662,7 +26771,7 @@ async function docxOf(model, opt) {
26662
26771
  right = pic.x + pic.w;
26663
26772
  }
26664
26773
  const first = row[0];
26665
- xml3 += `<w:p><w:pPr><w:spacing w:before="0" w:after="80"/>${Math.abs(first.x - margin) > 0.5 ? `<w:ind w:left="${Math.round(Math.max(-margin, first.x - margin) * TW)}"/>` : ""}</w:pPr>${runs}</w:p>`;
26774
+ xml2 += `<w:p><w:pPr><w:spacing w:before="0" w:after="80"/>${Math.abs(first.x - margin) > 0.5 ? `<w:ind w:left="${Math.round(Math.max(-margin, first.x - margin) * TW)}"/>` : ""}</w:pPr>${runs}</w:p>`;
26666
26775
  } else if (b.kind === "table") {
26667
26776
  const pics = /* @__PURE__ */ new Map();
26668
26777
  for (const [k, v] of visuals.get(b.key) || []) {
@@ -26670,19 +26779,19 @@ async function docxOf(model, opt) {
26670
26779
  const svg = figureSvg({ items: v.items, box: v.box }, opt.fontStore);
26671
26780
  pics.set(k, pictureRun({ svg, png: await rasterize(svg, v.box.w, v.box.h, picOpt), w: v.box.w, h: v.box.h }, "Chart in a table cell"));
26672
26781
  }
26673
- xml3 += table(b.cells, b.x - margin, pics);
26674
- } else if (b.kind === "rich") xml3 += richPara(b.items, b.x - margin);
26782
+ xml2 += table(b.cells, b.x - margin, pics);
26783
+ } else if (b.kind === "rich") xml2 += richPara(b.items, b.x - margin);
26675
26784
  else {
26676
26785
  const row = [b];
26677
26786
  const top = (t) => t.it.clip?.y ?? t.y - t.it.size;
26678
26787
  while (blocks[bi + 1]?.kind === "text" && Math.abs(top(blocks[bi + 1]) - top(b)) < 3 && blocks[bi + 1].it.clip && b.it.clip) row.push(blocks[++bi]);
26679
26788
  row.sort((p, q3) => p.it.clip.x - q3.it.clip.x);
26680
26789
  const apart = row.every((r, k) => k === 0 || r.it.clip.x >= row[k - 1].it.clip.x + row[k - 1].it.clip.w - 0.5);
26681
- if (row.length > 1 && apart) xml3 += layoutRow(row, margin);
26682
- else for (const r of row) xml3 += para(r.it, (r.it.clip?.x ?? r.x) - margin, r.decor);
26790
+ if (row.length > 1 && apart) xml2 += layoutRow(row, margin);
26791
+ else for (const r of row) xml2 += para(r.it, (r.it.clip?.x ?? r.x) - margin, r.decor);
26683
26792
  }
26684
26793
  }
26685
- body.push(xml3);
26794
+ body.push(xml2);
26686
26795
  }
26687
26796
  const M = margin * TW, V = 36 * TW;
26688
26797
  const sectPr = (pg) => {
@@ -26690,17 +26799,39 @@ async function docxOf(model, opt) {
26690
26799
  return `<w:sectPr><w:pgSz w:w="${W2}" w:h="${H}"${W2 > H ? ' w:orient="landscape"' : ""}/><w:pgMar w:top="${V}" w:right="${M}" w:bottom="${V}" w:left="${M}" w:header="0" w:footer="0" w:gutter="0"/></w:sectPr>`;
26691
26800
  };
26692
26801
  const sizeOf = (pg) => `${pg.width ?? model.width}x${pg.height ?? model.height}`;
26693
- let xml2 = "";
26802
+ const zipOut = [];
26803
+ const zip = new Zip((err, data) => {
26804
+ if (err) throw err;
26805
+ zipOut.push(data);
26806
+ });
26807
+ const put = (name, bytes2) => {
26808
+ const e2 = new ZipDeflate(name, { level: 6 });
26809
+ zip.add(e2);
26810
+ e2.push(bytes2, true);
26811
+ };
26812
+ const parts = [];
26813
+ const max = opt.xmlChunk || XML_CHUNK;
26814
+ let buf = "";
26815
+ const write = (str) => {
26816
+ for (const p of slices(str, max)) {
26817
+ if (buf.length + p.length > max) {
26818
+ parts.push(buf);
26819
+ buf = "";
26820
+ }
26821
+ buf += p;
26822
+ }
26823
+ };
26824
+ write(`<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
26825
+ <w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:wp="http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing" xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" xmlns:pic="http://schemas.openxmlformats.org/drawingml/2006/picture"><w:body>`);
26694
26826
  model.pages.forEach((pg, i) => {
26695
- xml2 += body[i];
26827
+ write(body[i]);
26696
26828
  const next = model.pages[i + 1];
26697
26829
  if (!next) return;
26698
- xml2 += sizeOf(next) !== sizeOf(pg) ? `<w:p><w:pPr>${sectPr(pg)}</w:pPr></w:p>` : '<w:p><w:pPr><w:pageBreakBefore/><w:spacing w:before="0" w:after="0" w:line="20" w:lineRule="exact"/><w:rPr><w:sz w:val="2"/></w:rPr></w:pPr></w:p>';
26830
+ write(sizeOf(next) !== sizeOf(pg) ? `<w:p><w:pPr>${sectPr(pg)}</w:pPr></w:p>` : '<w:p><w:pPr><w:pageBreakBefore/><w:spacing w:before="0" w:after="0" w:line="20" w:lineRule="exact"/><w:rPr><w:sz w:val="2"/></w:rPr></w:pPr></w:p>');
26699
26831
  });
26700
26832
  const last = model.pages[model.pages.length - 1] || {};
26701
- const document2 = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
26702
- <w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:wp="http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing" xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" xmlns:pic="http://schemas.openxmlformats.org/drawingml/2006/picture"><w:body>${xml2}${sectPr(
26703
- last)}</w:body></w:document>`;
26833
+ write(`${sectPr(last)}</w:body></w:document>`);
26834
+ parts.push(buf);
26704
26835
  const lang = `<w:lang w:val="${esc3(opt.lang || model.locale || "en-US")}"/>`;
26705
26836
  const headings = [28, 24, 22, 20, 18, 18].map((sz, i) => `<w:style w:type="paragraph" w:styleId="Heading${i + 1}"><w:name w:val="heading ${i + 1}"/><w:basedOn w:val="Normal"/><w:next w:val="Normal"/><w:qFormat/><w:pPr><w:keepNext/><w:outlineLvl w:val="${i}"/></w:pPr><w:rPr><w:b/><w:sz w:val="${sz}"/></w:rPr></w:style>`).join("");
26706
26837
  const styles = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
@@ -26710,24 +26841,32 @@ name w:val="Normal"/></w:style>${headings}</w:styles>`;
26710
26841
  const core = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
26711
26842
  <cp:coreProperties xmlns:cp="http://schemas.openxmlformats.org/package/2006/metadata/core-properties" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><dc:title>${esc3(opt.title || model.name || "Report")}</dc:title>${opt.author ? `<dc:creator>${esc3(opt.author)}</dc:creator>` : ""}<dcterms:created xsi:type=\
26712
26843
  "dcterms:W3CDTF">${now}</dcterms:created></cp:coreProperties>`;
26713
- const files = {
26714
- "[Content_Types].xml": strToU83(`<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
26844
+ put("[Content_Types].xml", strToU83(`<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
26715
26845
  <Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types"><Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/><Default Extension="xml" ContentType="application/xml"/><Default Extension="png" ContentType="image/png"/><Default Extension="jpeg" ContentType="image/jpeg"/><Default Extension="svg" ContentType="image/svg+xml"/><Override Part\
26716
- Name="/word/document.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"/><Override PartName="/word/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml"/><Override PartName="/docProps/core.xml" ContentType="application/vnd.openxmlformats-package.core-properties+xml"/></Types>`),
26717
- "_rels/.rels": strToU83(`<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
26718
- <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="word/document.xml"/><Relationship Id="rId2" Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties" Target="docProps/core.xml"/></Relationships>`),
26719
- "word/_rels/document.xml.rels": strToU83(`<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
26720
- <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml"/>${media.map((m) => `<Relationship Id="${m.rid}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/image" Target="media/${m.name}"/>`).join("")}</Relationships>`),
26721
- "word/document.xml": strToU83(document2),
26722
- "word/styles.xml": strToU83(styles),
26723
- "docProps/core.xml": strToU83(core)
26724
- };
26725
- for (const m of media) files[`word/media/${m.name}`] = m.bytes;
26726
- return zipSync3(files, { level: 6 });
26846
+ Name="/word/document.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"/><Override PartName="/word/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml"/><Override PartName="/docProps/core.xml" ContentType="application/vnd.openxmlformats-package.core-properties+xml"/></Types>`));
26847
+ put("_rels/.rels", strToU83(`<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
26848
+ <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="word/document.xml"/><Relationship Id="rId2" Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties" Target="docProps/core.xml"/></Relationships>`));
26849
+ put("word/_rels/document.xml.rels", strToU83(`<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
26850
+ <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml"/>${media.map((m) => `<Relationship Id="${m.rid}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/image" Target="media/${m.name}"/>`).join("")}</Relationships>`));
26851
+ const doc = new ZipDeflate("word/document.xml", { level: 6 });
26852
+ zip.add(doc);
26853
+ for (const p of parts) doc.push(strToU83(p), false);
26854
+ doc.push(new Uint8Array(0), true);
26855
+ put("word/styles.xml", strToU83(styles));
26856
+ put("docProps/core.xml", strToU83(core));
26857
+ for (const m of media) put(`word/media/${m.name}`, m.bytes);
26858
+ zip.end();
26859
+ const total = zipOut.reduce((n5, c) => n5 + c.length, 0), bytes = new Uint8Array(total);
26860
+ let at2 = 0;
26861
+ for (const c of zipOut) {
26862
+ bytes.set(c, at2);
26863
+ at2 += c.length;
26864
+ }
26865
+ return bytes;
26727
26866
  }
26728
26867
 
26729
26868
  // src/exporters/pptx.js
26730
- import { zipSync as zipSync4, strToU8 as strToU84 } from "fflate";
26869
+ import { zipSync as zipSync3, strToU8 as strToU84 } from "fflate";
26731
26870
  var EMU2 = 12700;
26732
26871
  var e = (v) => Math.round(v * EMU2);
26733
26872
  var esc4 = (s) => String(s).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f￾￿]/g, "");
@@ -26910,7 +27049,7 @@ rops/core.xml"/></Relationships>`);
26910
27049
  ="svg" ContentType="image/svg+xml"/><Override PartName="/ppt/presentation.xml" ContentType="application/vnd.openxmlformats-officedocument.presentationml.presentation.main+xml"/><Override PartName="/ppt/slideMasters/slideMaster1.xml" ContentType="application/vnd.openxmlformats-officedocument.presentationml.slideMaster+xml"/><Override PartName="/ppt/slideLayouts/slideLayout1.xml" ContentType="applic\
26911
27050
  ation/vnd.openxmlformats-officedocument.presentationml.slideLayout+xml"/><Override PartName="/ppt/theme/theme1.xml" ContentType="application/vnd.openxmlformats-officedocument.theme+xml"/>${slides.map((n5) => `<Override PartName="/ppt/slides/slide${n5}.xml" ContentType="application/vnd.openxmlformats-officedocument.presentationml.slide+xml"/>`).join("")}<Override PartName="/docProps/core.xml" Conte\
26912
27051
  ntType="application/vnd.openxmlformats-package.core-properties+xml"/></Types>`);
26913
- return zipSync4(files, { level: 6 });
27052
+ return zipSync3(files, { level: 6 });
26914
27053
  }
26915
27054
  var THEME = `${XML2}<a:theme xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" name="ReportWright"><a:themeElements><a:clrScheme name="ReportWright"><a:dk1><a:srgbClr val="000000"/></a:dk1><a:lt1><a:srgbClr val="FFFFFF"/></a:lt1><a:dk2><a:srgbClr val="1F2328"/></a:dk2><a:lt2><a:srgbClr val="E7E6E6"/></a:lt2><a:accent1><a:srgbClr val="2563EB"/></a:accent1><a:accent2><a:srgbClr val="F5\
26916
27055
  9E0B"/></a:accent2><a:accent3><a:srgbClr val="16A34A"/></a:accent3><a:accent4><a:srgbClr val="DC2626"/></a:accent4><a:accent5><a:srgbClr val="7C3AED"/></a:accent5><a:accent6><a:srgbClr val="0891B2"/></a:accent6><a:hlink><a:srgbClr val="0563C1"/></a:hlink><a:folHlink><a:srgbClr val="954F72"/></a:folHlink></a:clrScheme><a:fontScheme name="ReportWright"><a:majorFont><a:latin typeface="Inter"/><a:ea t\
@@ -26930,6 +27069,7 @@ pw test [folder] [--update] [--layout]
26930
27069
  render every <folder>/__tests__/*.test.json case and compare it with its snapshot
26931
27070
  (the words; --layout: the layout, see the CLI README)
26932
27071
  pw diff <old.pw.json> <new.pw.json> [-p name=value]… [-d source=data.json]… [--base URL]
27072
+ SERVER ONLY (the self-hosted app, run in its folder; the npm CLI does not include these):
26933
27073
  pw audit verify [--from YYYY-MM-DD] [--to YYYY-MM-DD] [--json] [--public-key file.pem]
26934
27074
  check the server's audit log (hash chain, signed checkpoints); run it in the app
26935
27075
  folder with the server's PW_STORAGE and PW_AUDIT_KEY / PW_SESSION_SECRET
@@ -26946,7 +27086,7 @@ pw audit rekey --old-secret S | --old-key file.pem
26946
27086
  --ua an accessible PDF (PDF/UA-1; with --pdfa, PDF/A-2a) --values Excel totals as values, not formulas
26947
27087
  --layout CSV of the printed layout (every table, group lines, totals), not just the data
26948
27088
  --static an HTML file without its Tables view (sort, groups, filter)
26949
- pw migrate-storage --to <url> [--from file|<url>]
27089
+ pw migrate-storage --to <url> [--from file|<url>] (SERVER ONLY, as above)
26950
27090
  copy everything (reports and their versions, users, grants, folders, schedules,
26951
27091
  jobs, fonts, settings, blobs, the audit log) from one storage to another, e.g. the
26952
27092
  files in this folder to sqlite:/srv/pw.db or postgres://…; stop the server first
@@ -26992,7 +27132,9 @@ var baseHost = (base, internal) => {
26992
27132
  };
26993
27133
  async function renderModel(def, { parameters = {}, data, base, internal = false, now, state, file, exportData = true, dataOnly = false } = {}) {
26994
27134
  try {
26995
- return await render(withData(def, data), { fontStore, parameters, exportData, dataOnly, baseUrl: base, allowHosts: baseHost(base, internal), now: now ? new Date(now) : void 0, ...state ? { state } : {}, ...file ? { loadReport: siblingLoader(file) } : {} });
27135
+ const m = await render(withData(def, data), { fontStore, parameters, exportData, dataOnly, baseUrl: base, allowHosts: baseHost(base, internal), now: now ? new Date(now) : void 0, ...state ? { state } : {}, ...file ? { loadReport: siblingLoader(file) } : {} });
27136
+ const seen = /* @__PURE__ */ new Set();
27137
+ return { ...m, warnings: m.warnings.filter((w) => !seen.has(w.toLowerCase()) && seen.add(w.toLowerCase())) };
26996
27138
  } catch (e2) {
26997
27139
  const relative = /fetch|URL|Invalid URL/i.test(e2.message) && !base;
26998
27140
  fail(`${e2.message}${relative ? "\n A data source needs a server: pass --base http://localhost:3000, or push the data with -d source=file.json." : ""}`);
@@ -27057,7 +27199,7 @@ async function cmdRender(args) {
27057
27199
  if (encrypt && values.pdfa) usage("a PDF/A file cannot have a password");
27058
27200
  if (values.ua && fmt2 !== "pdf") usage("--ua applies to -f pdf only");
27059
27201
  if (fmt2 === "pdf") bytes = await exportPdf(model, fontStore, { title: def.name, subset, encrypt, drillBase: values.base, pdfa: values.pdfa ? { icc: fs.readFileSync(path.join(PUB, "icc", "sRGB-v2-magic.icc")) } : void 0, tagged: values.ua ? { lang: def.locale || "en" } : void 0 });
27060
- else if (fmt2 === "html") bytes = await exportHtml(model, fontStore, { title: def.name, subset, static: !!values.static, locale: def.locale, currency: def.currency });
27202
+ else if (fmt2 === "html") bytes = await exportHtml(model, { fonts: fontStore, title: def.name, subset, static: !!values.static, locale: def.locale, currency: def.currency });
27061
27203
  else if (fmt2 === "csv") bytes = exportCsv(model, { locale: def.locale, currency: def.currency, layout: !!values.layout });
27062
27204
  else if (fmt2 === "xlsx") bytes = await exportXlsx(model, { currency: def.currency, title: def.name, formulas: !values.values, fontStore });
27063
27205
  else if (fmt2 === "docx") bytes = await exportDocx(model, { title: def.name, lang: def.locale, fontStore });