@formfeed/engine 0.3.12 → 0.3.13

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/index.js CHANGED
@@ -182,11 +182,11 @@ function validateEpc(input) {
182
182
  const purpose = String(input.purpose ?? "");
183
183
  if (purpose.length > 4) problems.push({ field: "purpose", code: "too_long", max: 4, actual: purpose.length });
184
184
  const reference = String(input.reference ?? "").trim();
185
- const text3 = String(input.text ?? "").trim();
186
- if (reference && text3) problems.push({ field: "reference", code: "reference_or_text" });
185
+ const text4 = String(input.text ?? "").trim();
186
+ if (reference && text4) problems.push({ field: "reference", code: "reference_or_text" });
187
187
  if (reference.length > 35)
188
188
  problems.push({ field: "reference", code: "too_long", max: 35, actual: reference.length });
189
- if (text3.length > 140) problems.push({ field: "text", code: "too_long", max: 140, actual: text3.length });
189
+ if (text4.length > 140) problems.push({ field: "text", code: "too_long", max: 140, actual: text4.length });
190
190
  const bytes = byteLength(epcPayload(input));
191
191
  if (bytes > EPC_MAX_BYTES)
192
192
  problems.push({ field: "payload", code: "too_long", max: EPC_MAX_BYTES, actual: bytes });
@@ -224,9 +224,12 @@ var barcodeTypes = {
224
224
  };
225
225
  var ownTextLayout = /* @__PURE__ */ new Set(["ean13", "ean8", "upca"]);
226
226
  function codeText(helper, value) {
227
- const text3 = value === void 0 || value === null ? "" : String(value);
228
- if (text3 === "") throw new Error(`${helper}: nothing to encode, the value is empty or missing from the data`);
229
- return text3;
227
+ const text4 = value === void 0 || value === null ? "" : String(value);
228
+ if (text4 === "")
229
+ throw new Error(
230
+ `${helper}: nothing to encode, the value is empty or missing from the data`
231
+ );
232
+ return text4;
230
233
  }
231
234
  var codeHelpers = [
232
235
  {
@@ -234,18 +237,25 @@ var codeHelpers = [
234
237
  aliases: ["qr"],
235
238
  doc: {
236
239
  signature: "qrcode(value, { size = 160, ecc = 'M', margin = 1, color, background })",
237
- description: "QR code as an SVG data URI for an <img> src.",
240
+ description: "QR code as an SVG data URI for an <img> src. Give the image an `alt` that says where the code leads: a screen reader cannot scan it.",
238
241
  examples: {
239
- jinja2: '<img src="{{ qrcode(order.url, { size: 120 }) }}">',
240
- liquid: '<img src="{{ order.url | qrcode: size: 120 }}">',
241
- handlebars: '<img src="{{qrcode order.url size=120}}">'
242
+ jinja2: '<img src="{{ qrcode(order.url, { size: 120 }) }}" alt="QR code: {{ order.url }}">',
243
+ liquid: '<img src="{{ order.url | qrcode: size: 120 }}" alt="QR code: {{ order.url }}">',
244
+ handlebars: '<img src="{{qrcode order.url size=120}}" alt="QR code: {{order.url}}">'
242
245
  },
243
246
  category: "code"
244
247
  },
245
248
  fn: (ctx, value, options) => {
246
249
  const o = options ?? {};
247
250
  const svg = qrSvg(codeText("qrcode", value), o);
248
- if (ctx.drawing) return ctx.drawing({ kind: "svg", svg, width: o.size, height: o.size, alt: "QR code" });
251
+ if (ctx.drawing)
252
+ return ctx.drawing({
253
+ kind: "svg",
254
+ svg,
255
+ width: o.size,
256
+ height: o.size,
257
+ alt: "QR code"
258
+ });
249
259
  return svgDataUri(svg);
250
260
  }
251
261
  },
@@ -253,28 +263,29 @@ var codeHelpers = [
253
263
  name: "barcode",
254
264
  doc: {
255
265
  signature: "barcode(value, { type = 'code128', height = 12, width, text = true, scale = 2 })",
256
- description: "Barcode (code128, ean13, ean8, upc, itf14, code39, datamatrix, pdf417) as an SVG data URI.",
266
+ description: "Barcode (code128, ean13, ean8, upc, itf14, code39, datamatrix, pdf417) as an SVG data URI. Give the image an `alt` with the value it encodes.",
257
267
  examples: {
258
- jinja2: `<img src="{{ barcode(item.sku, { type: 'code128' }) }}">`,
259
- liquid: `<img src="{{ item.sku | barcode: type: 'code128' }}">`,
260
- handlebars: `<img src="{{barcode item.sku type='code128'}}">`
268
+ jinja2: `<img src="{{ barcode(item.sku, { type: 'code128' }) }}" alt="Barcode {{ item.sku }}">`,
269
+ liquid: `<img src="{{ item.sku | barcode: type: 'code128' }}" alt="Barcode {{ item.sku }}">`,
270
+ handlebars: `<img src="{{barcode item.sku type='code128'}}" alt="Barcode {{item.sku}}">`
261
271
  },
262
272
  category: "code"
263
273
  },
264
274
  fn: (ctx, value, options) => {
265
275
  const o = options ?? {};
266
276
  const bcid = barcodeTypes[String(o.type ?? "code128").toLowerCase()] ?? "code128";
267
- const text3 = codeText("barcode", value);
277
+ const text4 = codeText("barcode", value);
268
278
  const svg = toSVG({
269
279
  bcid,
270
- text: text3,
280
+ text: text4,
271
281
  height: o.height ?? 12,
272
282
  ...o.width === void 0 ? {} : { width: o.width },
273
283
  scale: o.scale ?? 2,
274
284
  includetext: o.text !== false,
275
285
  ...ownTextLayout.has(bcid) ? {} : { textxalign: "center" }
276
286
  });
277
- if (ctx.drawing) return ctx.drawing({ kind: "svg", svg, alt: `Barcode ${text3}` });
287
+ if (ctx.drawing)
288
+ return ctx.drawing({ kind: "svg", svg, alt: `Barcode ${text4}` });
278
289
  return svgDataUri(svg);
279
290
  }
280
291
  },
@@ -283,11 +294,11 @@ var codeHelpers = [
283
294
  aliases: ["epc_qr", "girocode"],
284
295
  doc: {
285
296
  signature: "epcQr({ name, iban, bic?, amount?, reference?, text? }, { size })",
286
- description: "SEPA credit transfer QR code (EPC069-12 / GiroCode) that banking apps scan to prefill a payment.",
297
+ description: "SEPA credit transfer QR code (EPC069-12 / GiroCode) that banking apps scan to prefill a payment. Give the image an `alt`, and print the bank details as text as well: the code is a picture.",
287
298
  examples: {
288
- jinja2: '<img src="{{ epcQr({ name: company.name, iban: company.iban, amount: invoice.total, reference: invoice.number }) }}">',
289
- liquid: '<img src="{{ company.iban | epcQr: name: company.name, amount: invoice.total, reference: invoice.number }}">',
290
- handlebars: '<img src="{{epcQr name=company.name iban=company.iban amount=invoice.total reference=invoice.number}}">'
299
+ jinja2: '<img src="{{ epcQr({ name: company.name, iban: company.iban, amount: invoice.total, reference: invoice.number }) }}" alt="QR code to pay invoice {{ invoice.number }}">',
300
+ liquid: '<img src="{{ company.iban | epcQr: name: company.name, amount: invoice.total, reference: invoice.number }}" alt="QR code to pay invoice {{ invoice.number }}">',
301
+ handlebars: '<img src="{{epcQr name=company.name iban=company.iban amount=invoice.total reference=invoice.number}}" alt="QR code to pay invoice {{invoice.number}}">'
291
302
  },
292
303
  category: "code"
293
304
  },
@@ -306,12 +317,27 @@ var codeHelpers = [
306
317
  (EPC_FIELDS.has(key) ? payment2 : qr)[key] = value;
307
318
  const o = { ecc: "M", ...qr };
308
319
  const svg = qrSvg(epcPayload(payment2), o);
309
- if (ctx.drawing) return ctx.drawing({ kind: "svg", svg, width: o.size, height: o.size, alt: "GiroCode" });
320
+ if (ctx.drawing)
321
+ return ctx.drawing({
322
+ kind: "svg",
323
+ svg,
324
+ width: o.size,
325
+ height: o.size,
326
+ alt: "GiroCode"
327
+ });
310
328
  return svgDataUri(svg);
311
329
  }
312
330
  }
313
331
  ];
314
- var EPC_FIELDS = /* @__PURE__ */ new Set(["name", "iban", "bic", "amount", "reference", "text", "purpose"]);
332
+ var EPC_FIELDS = /* @__PURE__ */ new Set([
333
+ "name",
334
+ "iban",
335
+ "bic",
336
+ "amount",
337
+ "reference",
338
+ "text",
339
+ "purpose"
340
+ ]);
315
341
 
316
342
  // packages/engine/src/lib/helpers/format.ts
317
343
  import { TZDate } from "@date-fns/tz";
@@ -329,8 +355,8 @@ import { toCardinal as nlWords } from "n2words/nl";
329
355
  function resolveTranslation(dictionaries, locale, key) {
330
356
  const lang = locale.split("-")[0] ?? locale;
331
357
  for (const candidate of [locale, lang, "en"]) {
332
- const text3 = dictionaries?.[candidate]?.[key];
333
- if (typeof text3 === "string") return { text: text3, from: candidate };
358
+ const text4 = dictionaries?.[candidate]?.[key];
359
+ if (typeof text4 === "string") return { text: text4, from: candidate };
334
360
  }
335
361
  return { text: key, from: null };
336
362
  }
@@ -810,9 +836,9 @@ var formatHelpers = [
810
836
  category: "text"
811
837
  },
812
838
  fn: (ctx, key, params) => {
813
- const { text: text3 } = resolveTranslation(ctx.i18n, ctx.locale, str(key));
839
+ const { text: text4 } = resolveTranslation(ctx.i18n, ctx.locale, str(key));
814
840
  const p = params ?? {};
815
- return text3.replace(/\{(\w+)\}/g, (_, name) => str(p[name]));
841
+ return text4.replace(/\{(\w+)\}/g, (_, name) => str(p[name]));
816
842
  }
817
843
  },
818
844
  {
@@ -1083,7 +1109,16 @@ var collectionHelpers = [
1083
1109
  ];
1084
1110
 
1085
1111
  // packages/engine/src/lib/helpers/document.ts
1086
- var CHART_PALETTE = ["#3E63DD", "#12A594", "#E5484D", "#F76B15", "#8E4EC6", "#FFC53D", "#0090FF", "#978365"];
1112
+ var CHART_PALETTE = [
1113
+ "#3E63DD",
1114
+ "#12A594",
1115
+ "#E5484D",
1116
+ "#F76B15",
1117
+ "#8E4EC6",
1118
+ "#FFC53D",
1119
+ "#0090FF",
1120
+ "#978365"
1121
+ ];
1087
1122
  var SLICED = /* @__PURE__ */ new Set(["pie", "doughnut", "polarArea"]);
1088
1123
  var colour = (value) => typeof value === "string" && value ? value : Array.isArray(value) && value.length ? value.map(String) : void 0;
1089
1124
  function chartDataFromPlain(spec) {
@@ -1097,8 +1132,13 @@ function chartDataFromPlain(spec) {
1097
1132
  });
1098
1133
  const own = colour(s.color);
1099
1134
  const fill = SLICED.has(type) ? own ?? labels.map((_, i) => CHART_PALETTE[i % CHART_PALETTE.length]) : own ?? CHART_PALETTE[index % CHART_PALETTE.length];
1100
- const dataset = { data, backgroundColor: fill, borderColor: fill };
1101
- if (s.label !== void 0 && s.label !== null && s.label !== "") dataset["label"] = String(s.label);
1135
+ const dataset = {
1136
+ data,
1137
+ backgroundColor: fill,
1138
+ borderColor: fill
1139
+ };
1140
+ if (s.label !== void 0 && s.label !== null && s.label !== "")
1141
+ dataset["label"] = String(s.label);
1102
1142
  if (type === "line") dataset["fill"] = false;
1103
1143
  return dataset;
1104
1144
  });
@@ -1115,7 +1155,8 @@ function chartPayload(spec) {
1115
1155
  const options = { ...spec.options ?? {} };
1116
1156
  options["animation"] = false;
1117
1157
  options["responsive"] = false;
1118
- if (!isPlain(spec)) return { type: spec.type ?? "bar", data: spec.data ?? {}, options };
1158
+ if (!isPlain(spec))
1159
+ return { type: spec.type ?? "bar", data: spec.data ?? {}, options };
1119
1160
  const data = chartDataFromPlain(spec);
1120
1161
  const unnamed = data.datasets.length === 1 && data.datasets[0]?.["label"] === void 0;
1121
1162
  const plugins = asObject(options["plugins"]);
@@ -1128,7 +1169,8 @@ function chartMarkup(spec, id) {
1128
1169
  const height = toNumber(spec.height ?? 240) || 240;
1129
1170
  const payload = escapeHtml(JSON.stringify(chartPayload(spec)));
1130
1171
  const idAttr = id ? ` id="${escapeHtml(id)}"` : "";
1131
- return `<canvas class="ff-chart"${idAttr} width="${width}" height="${height}" style="width:${width}px;height:${height}px" data-ff-chart="${payload}"></canvas>`;
1172
+ const alt = typeof spec.alt === "string" && spec.alt.trim() ? ` role="img" aria-label="${escapeHtml(spec.alt.trim())}"` : "";
1173
+ return `<canvas class="ff-chart"${idAttr}${alt} width="${width}" height="${height}" style="width:${width}px;height:${height}px" data-ff-chart="${payload}"></canvas>`;
1132
1174
  }
1133
1175
  function imageMarkup(url, options = {}) {
1134
1176
  const styles = [
@@ -1149,18 +1191,21 @@ var documentHelpers = [
1149
1191
  name: "chart",
1150
1192
  html: true,
1151
1193
  doc: {
1152
- signature: "chart({ type, labels, values, label, color } | { type, labels, series } | { type, data, options }, { width, height })",
1153
- description: "Draws a Chart.js chart (bar, line, pie, doughnut, radar, \u2026) from plain data, `labels` with `values` or several `series` of `{ label, values, color }`, or from a full Chart.js `data` and `options`; animations are disabled so PDF and preview match.",
1194
+ signature: "chart({ type, labels, values, label, color } | { type, labels, series } | { type, data, options }, { width, height, alt })",
1195
+ description: "Draws a Chart.js chart (bar, line, pie, doughnut, radar, \u2026) from plain data, `labels` with `values` or several `series` of `{ label, values, color }`, or from a full Chart.js `data` and `options`; animations are disabled so PDF and preview match. `alt` says in words what the chart shows: a screen reader reads it in place of the picture, and a tagged PDF needs it.",
1154
1196
  examples: {
1155
- jinja2: "{{ chart({ type: 'line', labels: sales.labels, values: sales.quarters, width: 480 }) }}",
1156
- liquid: "{{ '' | chart: type: 'line', labels: sales.labels, values: sales.quarters, width: 480 }}",
1157
- handlebars: "{{chart type='line' labels=sales.labels values=sales.quarters width=480}}"
1197
+ jinja2: "{{ chart({ type: 'line', labels: sales.labels, values: sales.quarters, width: 480, alt: 'Sales by quarter' }) }}",
1198
+ liquid: "{{ '' | chart: type: 'line', labels: sales.labels, values: sales.quarters, width: 480, alt: 'Sales by quarter' }}",
1199
+ handlebars: "{{chart type='line' labels=sales.labels values=sales.quarters width=480 alt='Sales by quarter'}}"
1158
1200
  },
1159
1201
  category: "document"
1160
1202
  },
1161
1203
  fn: (ctx, spec, extra) => {
1162
1204
  if (ctx.mode === "office") throw new Error(officeUnsupported("chart"));
1163
- return chartMarkup({ ...asObject(spec), ...asObject(extra) });
1205
+ return chartMarkup({
1206
+ ...asObject(spec),
1207
+ ...asObject(extra)
1208
+ });
1164
1209
  }
1165
1210
  },
1166
1211
  {
@@ -1168,18 +1213,24 @@ var documentHelpers = [
1168
1213
  html: true,
1169
1214
  doc: {
1170
1215
  signature: "image(url, { width, height, fit, alt })",
1171
- description: "Places an image with fixed size and crop mode (`cover`, `contain`, `fill`); the render-worker fetches it through its cache and blocks private hosts.",
1216
+ description: "Places an image with fixed size and crop mode (`cover`, `contain`, `fill`); the render-worker fetches it through its cache and blocks private hosts. `alt` says what it shows; without one the image counts as decoration and a screen reader passes over it.",
1172
1217
  examples: {
1173
- jinja2: "{{ image(product.photo, { width: 120, height: 80, fit: 'cover' }) }}",
1174
- liquid: "{{ product.photo | image: width: 120, height: 80, fit: 'cover' }}",
1175
- handlebars: "{{image product.photo width=120 height=80 fit='cover'}}"
1218
+ jinja2: "{{ image(product.photo, { width: 120, height: 80, fit: 'cover', alt: product.name }) }}",
1219
+ liquid: "{{ product.photo | image: width: 120, height: 80, fit: 'cover', alt: product.name }}",
1220
+ handlebars: "{{image product.photo width=120 height=80 fit='cover' alt=product.name}}"
1176
1221
  },
1177
1222
  category: "document"
1178
1223
  },
1179
1224
  fn: (ctx, url, options) => {
1180
1225
  const o = asObject(options);
1181
1226
  if (ctx.drawing)
1182
- return ctx.drawing({ kind: "url", url: resolveAsset(ctx.assetBaseUrl, String(url ?? "")), width: o.width, height: o.height, alt: o.alt });
1227
+ return ctx.drawing({
1228
+ kind: "url",
1229
+ url: resolveAsset(ctx.assetBaseUrl, String(url ?? "")),
1230
+ width: o.width,
1231
+ height: o.height,
1232
+ alt: o.alt
1233
+ });
1183
1234
  return imageMarkup(String(url ?? ""), o);
1184
1235
  }
1185
1236
  }
@@ -1919,8 +1970,8 @@ var SPEC = /^(?:([\s\S])?([<>=^]))?([+\- ])?(#)?(0)?(\d+)?([,_])?(?:\.(\d+))?([b
1919
1970
  function group(digits, separator) {
1920
1971
  return digits.replace(/\B(?=(\d{3})+(?!\d))/g, separator);
1921
1972
  }
1922
- function pythonExponent(text3) {
1923
- return text3.replace(/e([+-])(\d)$/i, (m, sign, digit) => `${m[0]}${sign}0${digit}`);
1973
+ function pythonExponent(text4) {
1974
+ return text4.replace(/e([+-])(\d)$/i, (m, sign, digit) => `${m[0]}${sign}0${digit}`);
1924
1975
  }
1925
1976
  function formatNumber(value, type, precision, alternate) {
1926
1977
  if (!Number.isFinite(value)) return Number.isNaN(value) ? "nan" : "inf";
@@ -1933,20 +1984,20 @@ function formatNumber(value, type, precision, alternate) {
1933
1984
  return `${(abs2 * 100).toFixed(precision ?? 6)}%`;
1934
1985
  case "e":
1935
1986
  case "E": {
1936
- const text3 = pythonExponent(abs2.toExponential(precision ?? 6));
1937
- return type === "E" ? text3.toUpperCase() : text3;
1987
+ const text4 = pythonExponent(abs2.toExponential(precision ?? 6));
1988
+ return type === "E" ? text4.toUpperCase() : text4;
1938
1989
  }
1939
1990
  case "g":
1940
1991
  case "G": {
1941
1992
  const p = precision === 0 ? 1 : precision ?? 6;
1942
1993
  const exp = abs2 === 0 ? 0 : Math.floor(Math.log10(abs2));
1943
- let text3 = exp < -4 || exp >= p ? pythonExponent(abs2.toExponential(p - 1)) : abs2.toFixed(Math.max(0, p - 1 - exp));
1994
+ let text4 = exp < -4 || exp >= p ? pythonExponent(abs2.toExponential(p - 1)) : abs2.toFixed(Math.max(0, p - 1 - exp));
1944
1995
  if (!alternate) {
1945
- const [mantissa = "", exponent] = text3.split("e");
1996
+ const [mantissa = "", exponent] = text4.split("e");
1946
1997
  const trimmed = mantissa.includes(".") ? mantissa.replace(/0+$/, "").replace(/\.$/, "") : mantissa;
1947
- text3 = exponent === void 0 ? trimmed : `${trimmed}e${exponent}`;
1998
+ text4 = exponent === void 0 ? trimmed : `${trimmed}e${exponent}`;
1948
1999
  }
1949
- return type === "G" ? text3.toUpperCase() : text3;
2000
+ return type === "G" ? text4.toUpperCase() : text4;
1950
2001
  }
1951
2002
  case "d":
1952
2003
  case "n":
@@ -2015,35 +2066,35 @@ function lookupField(name, positional, named, next) {
2015
2066
  function pythonFormat(template, positional, named = {}) {
2016
2067
  let auto = 0;
2017
2068
  const next = () => auto++;
2018
- const render = (text3) => {
2069
+ const render = (text4) => {
2019
2070
  let out = "";
2020
- for (let i = 0; i < text3.length; i++) {
2021
- const ch = text3[i];
2071
+ for (let i = 0; i < text4.length; i++) {
2072
+ const ch = text4[i];
2022
2073
  if (ch === "}") {
2023
2074
  out += "}";
2024
- if (text3[i + 1] === "}") i++;
2075
+ if (text4[i + 1] === "}") i++;
2025
2076
  continue;
2026
2077
  }
2027
2078
  if (ch !== "{") {
2028
2079
  out += ch;
2029
2080
  continue;
2030
2081
  }
2031
- if (text3[i + 1] === "{") {
2082
+ if (text4[i + 1] === "{") {
2032
2083
  out += "{";
2033
2084
  i++;
2034
2085
  continue;
2035
2086
  }
2036
2087
  let depth = 1;
2037
2088
  let j = i + 1;
2038
- for (; j < text3.length && depth > 0; j++) {
2039
- if (text3[j] === "{") depth++;
2040
- else if (text3[j] === "}") depth--;
2089
+ for (; j < text4.length && depth > 0; j++) {
2090
+ if (text4[j] === "{") depth++;
2091
+ else if (text4[j] === "}") depth--;
2041
2092
  }
2042
2093
  if (depth > 0) {
2043
- out += text3.slice(i);
2094
+ out += text4.slice(i);
2044
2095
  break;
2045
2096
  }
2046
- const field = text3.slice(i + 1, j - 1);
2097
+ const field = text4.slice(i + 1, j - 1);
2047
2098
  const colon = field.indexOf(":");
2048
2099
  const head = colon < 0 ? field : field.slice(0, colon);
2049
2100
  const name = head.replace(/![rsa]$/, "");
@@ -4188,9 +4239,9 @@ function pick(input, ...keys) {
4188
4239
  function toLength(value) {
4189
4240
  if (value === void 0 || value === null || value === "") return void 0;
4190
4241
  if (typeof value === "number") return `${value}mm`;
4191
- const text3 = String(value).trim();
4192
- if (/^-?\d+(\.\d+)?$/.test(text3)) return `${text3}mm`;
4193
- if (/^-?\d+(\.\d+)?(mm|cm|in|px|pt)$/i.test(text3)) return text3.toLowerCase();
4242
+ const text4 = String(value).trim();
4243
+ if (/^-?\d+(\.\d+)?$/.test(text4)) return `${text4}mm`;
4244
+ if (/^-?\d+(\.\d+)?(mm|cm|in|px|pt)$/i.test(text4)) return text4.toLowerCase();
4194
4245
  return void 0;
4195
4246
  }
4196
4247
  function toMarginLength(value) {
@@ -4210,7 +4261,7 @@ function convertHeaderFooter(html) {
4210
4261
  if (typeof html !== "string" || !html.trim()) return void 0;
4211
4262
  return chromePlaceholders.reduce((out, [pattern, span]) => out.replace(pattern, span), html);
4212
4263
  }
4213
- var escapeText = (text3) => text3.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
4264
+ var escapeText = (text4) => text4.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
4214
4265
  var visibleMarkup = (html) => html.replace(/<style[\s\S]*?<\/style>/gi, "").trim();
4215
4266
  function apitemplateChrome(take, kind) {
4216
4267
  const custom = take(`custom_${kind}`);
@@ -4224,7 +4275,7 @@ function apitemplateChrome(take, kind) {
4224
4275
  if (!slots.some(Boolean)) return { fromSlots: false };
4225
4276
  const sizeText = size === void 0 ? "" : String(size).trim();
4226
4277
  const fontSize2 = /^\d+(\.\d+)?$/.test(sizeText) ? `${sizeText}px` : /^\d+(\.\d+)?(px|pt|mm)$/.test(sizeText) ? sizeText : "9px";
4227
- const cells = slots.map((text3, i) => `<span style="flex:1;text-align:${["left", "center", "right"][i]}">${escapeText(text3)}</span>`).join("");
4278
+ const cells = slots.map((text4, i) => `<span style="flex:1;text-align:${["left", "center", "right"][i]}">${escapeText(text4)}</span>`).join("");
4228
4279
  const styles = customHtml.match(/<style[\s\S]*?<\/style>/gi)?.join("") ?? "";
4229
4280
  return {
4230
4281
  html: convertHeaderFooter(`${styles}<div style="display:flex;gap:4mm;font-size:${fontSize2}">${cells}</div>`),
@@ -4385,9 +4436,9 @@ function checkTemplate(engineId, html, sampleData, name, docs) {
4385
4436
  function lengthMm(value) {
4386
4437
  if (value === void 0 || value === null || value === "") return void 0;
4387
4438
  if (typeof value === "number" && Number.isFinite(value)) return `${value}mm`;
4388
- const text3 = String(value).trim();
4389
- if (/^-?\d+(\.\d+)?$/.test(text3)) return `${text3}mm`;
4390
- if (/^-?\d+(\.\d+)?(mm|cm|in|px|pt)$/i.test(text3)) return text3.toLowerCase();
4439
+ const text4 = String(value).trim();
4440
+ if (/^-?\d+(\.\d+)?$/.test(text4)) return `${text4}mm`;
4441
+ if (/^-?\d+(\.\d+)?(mm|cm|in|px|pt)$/i.test(text4)) return text4.toLowerCase();
4391
4442
  return void 0;
4392
4443
  }
4393
4444
  function toBase64(value) {
@@ -4904,11 +4955,11 @@ function inlineIncludes(source, blocks, errors, changes) {
4904
4955
  }
4905
4956
  var cssPaperFormats = { a3: "A3", a4: "A4", a5: "A5", letter: "Letter" };
4906
4957
  var isoSizes = { a0: [841, 1189], a1: [594, 841], a2: [420, 594], a6: [105, 148] };
4907
- var escapeHtml2 = (text3) => text3.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
4958
+ var escapeHtml2 = (text4) => text4.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
4908
4959
  function simpleHeaderFooter(part, margin) {
4909
4960
  const cells = ["left", "center", "right"].map((side) => part[side] ?? "");
4910
4961
  if (!cells.some((c) => c.trim())) return void 0;
4911
- const cell3 = (text3, align) => `<div style="flex: 1; text-align: ${align}">${escapeHtml2(text3).replace(/\[page\]/g, '<span class="pageNumber"></span>').replace(/\[topage\]/g, '<span class="totalPages"></span>')}</div>`;
4962
+ const cell3 = (text4, align) => `<div style="flex: 1; text-align: ${align}">${escapeHtml2(text4).replace(/\[page\]/g, '<span class="pageNumber"></span>').replace(/\[topage\]/g, '<span class="totalPages"></span>')}</div>`;
4912
4963
  return `<div style="display: flex; width: 100%; font-size: 10px; padding: 0 ${margin.right} 0 ${margin.left}">${cell3(cells[0] ?? "", "left")}${cell3(cells[1] ?? "", "center")}${cell3(cells[2] ?? "", "right")}</div>`;
4913
4964
  }
4914
4965
  function mapSettings(input, kind, warnings, changes) {
@@ -5077,11 +5128,11 @@ var DOCS3 = "/migrate/jsreport";
5077
5128
  var COLLECTIONS = ["templates", "data", "assets", "components", "scripts", "folders"];
5078
5129
  function readJsreportExport(files) {
5079
5130
  const bundle = { templates: [], data: [], assets: [], components: [], scripts: [], folders: [] };
5080
- for (const [path, text3] of Object.entries(files)) {
5131
+ for (const [path, text4] of Object.entries(files)) {
5081
5132
  const normalized = path.replace(/\\/g, "/").replace(/^\.?\//, "");
5082
5133
  if (normalized === "metadata.json") {
5083
5134
  try {
5084
- bundle.version = String(JSON.parse(text3).reporterVersion ?? "");
5135
+ bundle.version = String(JSON.parse(text4).reporterVersion ?? "");
5085
5136
  } catch {
5086
5137
  }
5087
5138
  continue;
@@ -5089,7 +5140,7 @@ function readJsreportExport(files) {
5089
5140
  const collection = normalized.split("/")[0];
5090
5141
  if (!COLLECTIONS.includes(collection) || !normalized.endsWith(".json")) continue;
5091
5142
  try {
5092
- bundle[collection].push(JSON.parse(text3));
5143
+ bundle[collection].push(JSON.parse(text4));
5093
5144
  } catch {
5094
5145
  }
5095
5146
  }
@@ -5131,7 +5182,7 @@ function assetBase64(content) {
5131
5182
  }
5132
5183
  return toBase64(content);
5133
5184
  }
5134
- var escapeMustaches = (text3) => text3.replace(/\{\{/g, "\\{{");
5185
+ var escapeMustaches = (text4) => text4.replace(/\{\{/g, "\\{{");
5135
5186
  function resolveAssets(source, bundle, options, warnings, changes) {
5136
5187
  const v3 = /\{\{\{?\s*asset\s+(["'])([^"']+)\1(?:\s+(["'])([^"']+)\3)?\s*\}?\}\}/g;
5137
5188
  const v2 = /\{#asset\s+([^\s}]+)(?:\s+@encoding=(\w+))?\s*\}/g;
@@ -5372,6 +5423,7 @@ function importJsreport(bundle, ref, options = {}) {
5372
5423
  var textEditRuntime = '"use strict";(()=>{var O=`\nhtml.ff-edit-mode [data-ff-edit] { cursor: text; }\nhtml.ff-edit-mode [data-ff-edit]:hover { outline: 1px dashed rgba(37,99,235,.85); outline-offset: 2px; }\nhtml.ff-edit-mode [data-ff-edit="t"]:hover { outline-color: rgba(147,51,234,.9); }\n.ff-editing { outline: 2px solid #2563eb !important; outline-offset: 2px; cursor: text; }\n.ff-editing .ff-chip { background: rgba(37,99,235,.14) !important; box-shadow: inset 0 0 0 1px rgba(37,99,235,.45) !important; border-radius: 3px !important; padding: 0 1px !important; cursor: default; }\n.ff-edit-badge { position: absolute; z-index: 2147483647; font: 500 11px/1.4 system-ui, sans-serif !important; color: #fff !important; background: #2563eb !important; padding: 1px 6px !important; border-radius: 4px !important; pointer-events: none; white-space: nowrap; }\n`;function F(l,d={}){let m=d.clicks!==!1,g=d.live===!0,o=l.document,i=null,s=null,f=e=>{try{l.parent.postMessage(e,"*")}catch{}},v=m?o.querySelector(\'meta[name="formfeed:document"]\')?.getAttribute("content")??null:null,h=!1;l.addEventListener("blur",()=>{h=!0}),l.addEventListener("focus",()=>{h=!1});let y=o.createElement("style");y.setAttribute("data-formfeed","edit"),y.setAttribute("data-pagedjs-ignore",""),y.textContent=O,(o.head??o.documentElement).appendChild(y),m&&o.documentElement.classList.add("ff-edit-mode");let M=e=>(e&&typeof e.closest=="function"?e:null)?.closest("[data-ff-edit]")??null;o.addEventListener("click",e=>{if(!m)return;if(i&&!i.done&&i.element.contains(e.target)){e.stopImmediatePropagation();return}let t=M(e.target);t&&(e.preventDefault(),e.stopImmediatePropagation(),C(t,{x:e.clientX,y:e.clientY}))},!0);function C(e,t){i&&!i.done&&x();let n=e.getAttribute("data-ff-src")??"",r=e.getBoundingClientRect(),u=Array.from(o.querySelectorAll(`[data-ff-src="${n.replace(/"/g,"")}"]`));s={src:n,element:e,point:t},f({type:"formfeed:edit-request",src:n,doc:v,edit:e.getAttribute("data-ff-edit"),text:e.textContent??"",elements:e.childElementCount,count:u.length,index:Math.max(0,u.indexOf(e)),split:e.hasAttribute("data-split-from")||e.hasAttribute("data-split-to"),rect:{left:r.left,top:r.top,width:r.width,height:r.height}})}o.addEventListener("keydown",e=>{if(!m||i&&!i.done||!(e.ctrlKey||e.metaKey)||e.altKey)return;let t=e.key.toLowerCase();(t==="z"||t==="y")&&(e.preventDefault(),f({type:t==="y"||e.shiftKey?"formfeed:redo":"formfeed:undo"}))}),l.addEventListener("message",e=>{if(e.source!==l.parent)return;let t=e.data;!t||typeof t.type!="string"||(t.type==="formfeed:edit-begin"?P(t):t.type==="formfeed:edit-refuse"||t.type==="formfeed:edit-end"?(s=null,i&&!i.done&&E(!1)):t.type==="formfeed:edit-open"&&B(t))});function B(e){if(!m||typeof e.src!="string"||i&&!i.done)return;let t=Array.from(o.querySelectorAll(`[data-ff-src="${e.src.replace(/"/g,"")}"][data-ff-edit]`)),n=Number.isInteger(e.index)?Number(e.index):0,r=t[n]??t[0];r&&C(r,null)}if(m){let e=()=>f({type:"formfeed:edit-ready",doc:v}),t=l;t.PagedConfig?t.formfeedEditReady=e:o.readyState==="loading"?o.addEventListener("DOMContentLoaded",e):e()}function P(e){i&&!i.done&&E(!1);let t=e.layer?o.querySelector(`[data-ff-layer="${String(e.layer).replace(/"/g,"")}"] > .ff-text`):s&&s.src===e.src?s.element:o.querySelector(`[data-ff-src="${String(e.src).replace(/"/g,"")}"]`),n=e.at,r=e.layer?n&&Number.isFinite(n.x)&&Number.isFinite(n.y)?{x:Number(n.x),y:Number(n.y)}:null:s&&s.src===e.src?s.point:null;s=null;let u=h&&!o.hasFocus();if(!t||!Array.isArray(e.items)||u){f({type:"formfeed:edit-cancel",token:String(e.token)});return}let c=Array.from(t.childNodes),b=o.createDocumentFragment();for(let a of e.items)if(a.kind==="text")b.appendChild(o.createTextNode(String(a.text)));else if(a.kind==="break")b.appendChild(o.createElement("br"));else if(a.kind==="chip"){let p=o.createElement("span");p.className="ff-chip",p.setAttribute("data-ff-chip",String(a.index)),p.setAttribute("contenteditable","false"),a.title&&(p.title=String(a.title)),p.textContent=String(a.text),b.appendChild(p)}for(let a of c)t.removeChild(a);t.appendChild(b),t.contentEditable="true",t.classList.add("ff-editing"),t.setAttribute("role","textbox"),e.label&&t.setAttribute("aria-label",String(e.label)),t.spellcheck=!0,i={token:String(e.token),element:t,saved:c,allowBreaks:e.allowBreaks===!0,badge:e.note?H(t,String(e.note)):null,done:!1},t.addEventListener("keydown",A),t.addEventListener("beforeinput",T),t.addEventListener("paste",S),t.addEventListener("drop",N),t.addEventListener("focusout",R),g&&t.addEventListener("input",k),t.focus(),e.select==="all"?I(t):K(t,r),f({type:"formfeed:edit-started",token:i.token}),typeof e.insert=="string"&&e.insert&&L(e.insert.replace(/\\r\\n?|\\n/g," "))}function L(e){if(typeof o.execCommand=="function"&&o.execCommand("insertText",!1,e))return;let t=l.getSelection(),n=t&&t.rangeCount?t.getRangeAt(0):null;n&&(n.deleteContents(),n.insertNode(o.createTextNode(e)),n.collapse(!1),g&&k())}function k(){i&&!i.done&&f({type:"formfeed:edit-input",token:i.token,items:D(i.element)})}function H(e,t){let n=o.createElement("div");n.className="ff-edit-badge",n.textContent=t;let r=e.getBoundingClientRect();return n.style.left=`${r.left+l.scrollX}px`,n.style.top=`${Math.max(0,r.top+l.scrollY-22)}px`,o.body.appendChild(n),n}function I(e){let t=l.getSelection();if(!t)return;let n=o.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}function K(e,t){let n=l.getSelection();if(!n)return;let r=null,u=o;if(t){if(u.caretRangeFromPoint)r=u.caretRangeFromPoint(t.x,t.y);else if(u.caretPositionFromPoint){let c=u.caretPositionFromPoint(t.x,t.y);c&&(r=o.createRange(),r.setStart(c.offsetNode,c.offset))}}if(!r||!e.contains(r.startContainer))r=o.createRange(),r.selectNodeContents(e),r.collapse(!1);else if(t){let c=r.startContainer,a=(c.nodeType===1?c:c.parentElement)?.closest("[data-ff-chip]")??null;if(a&&a!==e&&e.contains(a)){let p=a.getBoundingClientRect();r=o.createRange(),t.x<p.left+p.width/2?r.setStartBefore(a):r.setStartAfter(a),r.collapse(!0)}}n.removeAllRanges(),n.addRange(r)}function A(e){let t=e.key;t==="Escape"?(e.preventDefault(),E(!0)):t==="Enter"&&!e.shiftKey?(e.preventDefault(),x()):t==="Enter"?(e.preventDefault(),i?.allowBreaks&&q()):t==="Tab"?(e.preventDefault(),x()):(e.ctrlKey||e.metaKey)&&["b","i","u"].includes(t.toLowerCase())&&(e.preventDefault(),g&&i&&!e.altKey&&f({type:"formfeed:edit-style",token:i.token,key:t.toLowerCase()}))}function T(e){let t=e.inputType??"";(t.startsWith("format")||t==="insertFromDrop"||t==="insertParagraph"||t==="insertLineBreak")&&e.preventDefault()}function S(e){e.preventDefault(),L((e.clipboardData?.getData("text/plain")??"").replace(/\\r\\n?|\\n/g," "))}function N(e){e.preventDefault()}function R(){l.setTimeout(()=>{i&&!i.done&&(!o.hasFocus()||!i.element.contains(o.activeElement))&&x()},0)}function q(){let e=l.getSelection();if(!e||!e.rangeCount)return;let t=e.getRangeAt(0);t.deleteContents();let n=o.createElement("br");t.insertNode(n),n.nextSibling||n.parentNode?.appendChild(o.createElement("br"));let r=o.createRange();r.setStartAfter(n),r.collapse(!0),e.removeAllRanges(),e.addRange(r)}function w(e){e.done=!0;let t=e.element;t.removeEventListener("keydown",A),t.removeEventListener("beforeinput",T),t.removeEventListener("paste",S),t.removeEventListener("drop",N),t.removeEventListener("focusout",R),t.removeEventListener("input",k),t.removeAttribute("contenteditable"),t.removeAttribute("role"),t.removeAttribute("aria-label"),t.classList.remove("ff-editing"),e.badge?.remove(),i===e&&(i=null)}function x(){let e=i;if(!e||e.done)return;let t=D(e.element);w(e),f({type:"formfeed:edit-commit",token:e.token,items:t})}function E(e){let t=i;if(!t||t.done)return;let n=t.element;for(;n.firstChild;)n.removeChild(n.firstChild);for(let r of t.saved)n.appendChild(r);w(t),e&&f({type:"formfeed:edit-cancel",token:t.token})}}function D(l){let d=[],m=o=>{let i=d[d.length-1];i?.kind==="text"?i.text+=o:o&&d.push({kind:"text",text:o})},g=o=>{for(let i of Array.from(o.childNodes))if(i.nodeType===3)m(i.data);else if(i.nodeType===1){let s=i,f=s.getAttribute("data-ff-chip");f!==null?d.push({kind:"chip",index:Number(f)}):s.tagName==="BR"?d.push({kind:"break"}):((s.tagName==="DIV"||s.tagName==="P")&&d.length&&d.push({kind:"break"}),g(s))}};for(g(l);d[d.length-1]?.kind==="break";)d.pop();return d}F(window);})();';
5373
5424
  var fitRuntime = '"use strict";(()=>{function a(t,e,n){if(e===t)return t.scrollHeight<=t.clientHeight+.5&&t.scrollWidth<=t.clientWidth+.5;let o=n.getComputedStyle(t),i=t.clientHeight-(parseFloat(o.paddingTop)||0)-(parseFloat(o.paddingBottom)||0);return e.scrollHeight<=i+.5&&e.scrollWidth<=e.clientWidth+.5}function m(t,e){let n=t.firstElementChild??t;n.style.fontSize="";let o=parseFloat(e.getComputedStyle(n).fontSize)||16,i=Math.min(o,parseFloat(t.getAttribute("data-ff-fit-min")??"")||o/2);if(a(t,n,e))return o;let l=i,s=o;for(let d=0;d<14;d++){let r=(l+s)/2;n.style.fontSize=`${r}px`,a(t,n,e)?l=r:s=r}let f=Math.floor(l*100)/100;return n.style.fontSize=`${f}px`,f}function c(t){let e=()=>{for(let o of Array.from(t.document.querySelectorAll(\'[data-ff-fit="shrink"]\')))m(o,t);t.document.documentElement.setAttribute("data-ff-fit","done")};t.formfeedFit=e;let n=t.document.fonts;n?n.ready.then(e):t.addEventListener("load",e)}c(window);})();';
5374
5425
  var stageRuntime = '"use strict";(()=>{var W=`\nhtml.ff-edit-mode [data-ff-edit] { cursor: text; }\nhtml.ff-edit-mode [data-ff-edit]:hover { outline: 1px dashed rgba(37,99,235,.85); outline-offset: 2px; }\nhtml.ff-edit-mode [data-ff-edit="t"]:hover { outline-color: rgba(147,51,234,.9); }\n.ff-editing { outline: 2px solid #2563eb !important; outline-offset: 2px; cursor: text; }\n.ff-editing .ff-chip { background: rgba(37,99,235,.14) !important; box-shadow: inset 0 0 0 1px rgba(37,99,235,.45) !important; border-radius: 3px !important; padding: 0 1px !important; cursor: default; }\n.ff-edit-badge { position: absolute; z-index: 2147483647; font: 500 11px/1.4 system-ui, sans-serif !important; color: #fff !important; background: #2563eb !important; padding: 1px 6px !important; border-radius: 4px !important; pointer-events: none; white-space: nowrap; }\n`;function H(a,l={}){let f=l.clicks!==!1,p=l.live===!0,n=a.document,o=null,d=null,u=e=>{try{a.parent.postMessage(e,"*")}catch{}},m=f?n.querySelector(\'meta[name="formfeed:document"]\')?.getAttribute("content")??null:null,s=!1;a.addEventListener("blur",()=>{s=!0}),a.addEventListener("focus",()=>{s=!1});let g=n.createElement("style");g.setAttribute("data-formfeed","edit"),g.setAttribute("data-pagedjs-ignore",""),g.textContent=W,(n.head??n.documentElement).appendChild(g),f&&n.documentElement.classList.add("ff-edit-mode");let x=e=>(e&&typeof e.closest=="function"?e:null)?.closest("[data-ff-edit]")??null;n.addEventListener("click",e=>{if(!f)return;if(o&&!o.done&&o.element.contains(e.target)){e.stopImmediatePropagation();return}let t=x(e.target);t&&(e.preventDefault(),e.stopImmediatePropagation(),v(t,{x:e.clientX,y:e.clientY}))},!0);function v(e,t){o&&!o.done&&L();let r=e.getAttribute("data-ff-src")??"",i=e.getBoundingClientRect(),b=Array.from(n.querySelectorAll(`[data-ff-src="${r.replace(/"/g,"")}"]`));d={src:r,element:e,point:t},u({type:"formfeed:edit-request",src:r,doc:m,edit:e.getAttribute("data-ff-edit"),text:e.textContent??"",elements:e.childElementCount,count:b.length,index:Math.max(0,b.indexOf(e)),split:e.hasAttribute("data-split-from")||e.hasAttribute("data-split-to"),rect:{left:i.left,top:i.top,width:i.width,height:i.height}})}n.addEventListener("keydown",e=>{if(!f||o&&!o.done||!(e.ctrlKey||e.metaKey)||e.altKey)return;let t=e.key.toLowerCase();(t==="z"||t==="y")&&(e.preventDefault(),u({type:t==="y"||e.shiftKey?"formfeed:redo":"formfeed:undo"}))}),a.addEventListener("message",e=>{if(e.source!==a.parent)return;let t=e.data;!t||typeof t.type!="string"||(t.type==="formfeed:edit-begin"?B(t):t.type==="formfeed:edit-refuse"||t.type==="formfeed:edit-end"?(d=null,o&&!o.done&&S(!1)):t.type==="formfeed:edit-open"&&E(t))});function E(e){if(!f||typeof e.src!="string"||o&&!o.done)return;let t=Array.from(n.querySelectorAll(`[data-ff-src="${e.src.replace(/"/g,"")}"][data-ff-edit]`)),r=Number.isInteger(e.index)?Number(e.index):0,i=t[r]??t[0];i&&v(i,null)}if(f){let e=()=>u({type:"formfeed:edit-ready",doc:m}),t=a;t.PagedConfig?t.formfeedEditReady=e:n.readyState==="loading"?n.addEventListener("DOMContentLoaded",e):e()}function B(e){o&&!o.done&&S(!1);let t=e.layer?n.querySelector(`[data-ff-layer="${String(e.layer).replace(/"/g,"")}"] > .ff-text`):d&&d.src===e.src?d.element:n.querySelector(`[data-ff-src="${String(e.src).replace(/"/g,"")}"]`),r=e.at,i=e.layer?r&&Number.isFinite(r.x)&&Number.isFinite(r.y)?{x:Number(r.x),y:Number(r.y)}:null:d&&d.src===e.src?d.point:null;d=null;let b=s&&!n.hasFocus();if(!t||!Array.isArray(e.items)||b){u({type:"formfeed:edit-cancel",token:String(e.token)});return}let y=Array.from(t.childNodes),k=n.createDocumentFragment();for(let c of e.items)if(c.kind==="text")k.appendChild(n.createTextNode(String(c.text)));else if(c.kind==="break")k.appendChild(n.createElement("br"));else if(c.kind==="chip"){let h=n.createElement("span");h.className="ff-chip",h.setAttribute("data-ff-chip",String(c.index)),h.setAttribute("contenteditable","false"),c.title&&(h.title=String(c.title)),h.textContent=String(c.text),k.appendChild(h)}for(let c of y)t.removeChild(c);t.appendChild(k),t.contentEditable="true",t.classList.add("ff-editing"),t.setAttribute("role","textbox"),e.label&&t.setAttribute("aria-label",String(e.label)),t.spellcheck=!0,o={token:String(e.token),element:t,saved:y,allowBreaks:e.allowBreaks===!0,badge:e.note?I(t,String(e.note)):null,done:!1},t.addEventListener("keydown",T),t.addEventListener("beforeinput",N),t.addEventListener("paste",w),t.addEventListener("drop",R),t.addEventListener("focusout",F),p&&t.addEventListener("input",C),t.focus(),e.select==="all"?q(t):K(t,i),u({type:"formfeed:edit-started",token:o.token}),typeof e.insert=="string"&&e.insert&&A(e.insert.replace(/\\r\\n?|\\n/g," "))}function A(e){if(typeof n.execCommand=="function"&&n.execCommand("insertText",!1,e))return;let t=a.getSelection(),r=t&&t.rangeCount?t.getRangeAt(0):null;r&&(r.deleteContents(),r.insertNode(n.createTextNode(e)),r.collapse(!1),p&&C())}function C(){o&&!o.done&&u({type:"formfeed:edit-input",token:o.token,items:D(o.element)})}function I(e,t){let r=n.createElement("div");r.className="ff-edit-badge",r.textContent=t;let i=e.getBoundingClientRect();return r.style.left=`${i.left+a.scrollX}px`,r.style.top=`${Math.max(0,i.top+a.scrollY-22)}px`,n.body.appendChild(r),r}function q(e){let t=a.getSelection();if(!t)return;let r=n.createRange();r.selectNodeContents(e),t.removeAllRanges(),t.addRange(r)}function K(e,t){let r=a.getSelection();if(!r)return;let i=null,b=n;if(t){if(b.caretRangeFromPoint)i=b.caretRangeFromPoint(t.x,t.y);else if(b.caretPositionFromPoint){let y=b.caretPositionFromPoint(t.x,t.y);y&&(i=n.createRange(),i.setStart(y.offsetNode,y.offset))}}if(!i||!e.contains(i.startContainer))i=n.createRange(),i.selectNodeContents(e),i.collapse(!1);else if(t){let y=i.startContainer,c=(y.nodeType===1?y:y.parentElement)?.closest("[data-ff-chip]")??null;if(c&&c!==e&&e.contains(c)){let h=c.getBoundingClientRect();i=n.createRange(),t.x<h.left+h.width/2?i.setStartBefore(c):i.setStartAfter(c),i.collapse(!0)}}r.removeAllRanges(),r.addRange(i)}function T(e){let t=e.key;t==="Escape"?(e.preventDefault(),S(!0)):t==="Enter"&&!e.shiftKey?(e.preventDefault(),L()):t==="Enter"?(e.preventDefault(),o?.allowBreaks&&$()):t==="Tab"?(e.preventDefault(),L()):(e.ctrlKey||e.metaKey)&&["b","i","u"].includes(t.toLowerCase())&&(e.preventDefault(),p&&o&&!e.altKey&&u({type:"formfeed:edit-style",token:o.token,key:t.toLowerCase()}))}function N(e){let t=e.inputType??"";(t.startsWith("format")||t==="insertFromDrop"||t==="insertParagraph"||t==="insertLineBreak")&&e.preventDefault()}function w(e){e.preventDefault(),A((e.clipboardData?.getData("text/plain")??"").replace(/\\r\\n?|\\n/g," "))}function R(e){e.preventDefault()}function F(){a.setTimeout(()=>{o&&!o.done&&(!n.hasFocus()||!o.element.contains(n.activeElement))&&L()},0)}function $(){let e=a.getSelection();if(!e||!e.rangeCount)return;let t=e.getRangeAt(0);t.deleteContents();let r=n.createElement("br");t.insertNode(r),r.nextSibling||r.parentNode?.appendChild(n.createElement("br"));let i=n.createRange();i.setStartAfter(r),i.collapse(!0),e.removeAllRanges(),e.addRange(i)}function M(e){e.done=!0;let t=e.element;t.removeEventListener("keydown",T),t.removeEventListener("beforeinput",N),t.removeEventListener("paste",w),t.removeEventListener("drop",R),t.removeEventListener("focusout",F),t.removeEventListener("input",C),t.removeAttribute("contenteditable"),t.removeAttribute("role"),t.removeAttribute("aria-label"),t.classList.remove("ff-editing"),e.badge?.remove(),o===e&&(o=null)}function L(){let e=o;if(!e||e.done)return;let t=D(e.element);M(e),u({type:"formfeed:edit-commit",token:e.token,items:t})}function S(e){let t=o;if(!t||t.done)return;let r=t.element;for(;r.firstChild;)r.removeChild(r.firstChild);for(let i of t.saved)r.appendChild(i);M(t),e&&u({type:"formfeed:edit-cancel",token:t.token})}}function D(a){let l=[],f=n=>{let o=l[l.length-1];o?.kind==="text"?o.text+=n:n&&l.push({kind:"text",text:n})},p=n=>{for(let o of Array.from(n.childNodes))if(o.nodeType===3)f(o.data);else if(o.nodeType===1){let d=o,u=d.getAttribute("data-ff-chip");u!==null?l.push({kind:"chip",index:Number(u)}):d.tagName==="BR"?l.push({kind:"break"}):((d.tagName==="DIV"||d.tagName==="P")&&l.length&&l.push({kind:"break"}),p(d))}};for(p(a);l[l.length-1]?.kind==="break";)l.pop();return l}function O(a,l){return Array.from(a.querySelectorAll("[data-ff-layer]")).map(f=>{let p=f.querySelector(":scope > .ff-text"),n=!1;if(p&&f.style.height!=="auto"){let o=l.getComputedStyle(f),d=f.clientHeight-(parseFloat(o.paddingTop)||0)-(parseFloat(o.paddingBottom)||0);n=o.overflow==="hidden"&&(p.scrollHeight>d+1||p.scrollWidth>p.clientWidth+1)}return{id:f.getAttribute("data-ff-layer")??"",width:f.offsetWidth,height:f.offsetHeight,overflow:n}})}function P(a){let l=a.document,f=m=>{try{a.parent.postMessage(m,"*")}catch{}},p=m=>typeof m=="string"?l.querySelector(`[data-ff-layer="${m.replace(/"/g,"")}"]`):null,n=()=>{for(let m of Array.from(l.querySelectorAll("[data-ff-layer]")))for(let s of["left","top","width","height","transform"])m.style.removeProperty(s)},o=()=>{a.formfeedFit?.(),f({type:"formfeed:ready",layers:O(l,a)})};a.addEventListener("message",m=>{if(m.source!==a.parent)return;let s=m.data;if(!(!s||typeof s.type!="string")){if(s.type==="formfeed:layer-style"){let g=p(s.id);if(!g)return;let x=(v,E)=>{typeof E=="number"&&Number.isFinite(E)&&g.style.setProperty(v,`${E}px`)};x("left",s.left),x("top",s.top),x("width",s.width),x("height",s.height),typeof s.rotation=="number"&&Number.isFinite(s.rotation)&&g.style.setProperty("transform",s.rotation?`rotate(${s.rotation}deg)`:"none")}else if(s.type==="formfeed:layer-style-clear")n();else if(s.type==="formfeed:css"&&typeof s.css=="string"){let g=l.querySelector(\'style[data-formfeed="template"]\');g&&(g.textContent=s.css),n(),o()}}}),H(a,{clicks:!1,live:!0});let d=l.fonts,u=()=>d?void d.ready.then(o):o();l.readyState==="complete"?u():a.addEventListener("load",u)}P(window);})();';
5426
+ var auditRuntime = '"use strict";(()=>{var I={"document-title":"error","image-alt":"error","figure-name":"error","heading-order":"error","table-rows":"error","table-cell-empty":"error","masked-text":"error","filtered-text":"warning","document-language":"warning","table-headers":"warning","link-name":"warning","generated-text":"warning","fixed-text":"warning","running-text":"warning"};var S=120;var O=".formfeed-chrome, .ff-running-header, .ff-running-footer",_=".pageNumber, .totalPages, .date, .title, .url, .ff-page-no, .ff-page-total",k=new Set(["script","style","template","noscript","head"]),j=2e4,q=10,B=new Set(["page","pages","of","seite","seiten","von","p","s"]),u=t=>(t??"").replace(/\\s+/g," ").trim(),D=t=>t.replace(/\\s+/g,"").toLowerCase(),$=t=>/[\\p{L}\\p{N}]/u.test(t),Y=/^data-(ref|id|split-(from|to|original)|(previous-|next-)?break-(before|after|inside)|(align-)?last-split-element|nth-of-type|counter-.+|page(-number)?|undisplayed|after-page|following|has-notes|children|item-num)$/;function X(t){let n=t.cloneNode(!1);n.removeAttribute("data-ff-src"),n.removeAttribute("data-ff-edit");let i=t.closest(".pagedjs_page")!==null;for(let s of Array.from(n.attributes))i&&Y.test(s.name)?n.removeAttribute(s.name):s.value.length>48&&n.setAttribute(s.name,`${s.value.slice(0,40)}\\u2026`);let o=n.outerHTML.replace(new RegExp(`</${t.tagName}>$`,"i"),"");return o.length>S?`${o.slice(0,S-1)}\\u2026`:o}function F(t,n={}){let i=t.document,o=i.body,s=[],d=new Set,a=n.findings!==!1,c=(e,r,l={})=>{if(!a)return;let m=r?.closest("[data-ff-src]")?.getAttribute("data-ff-src")??null,h=r?X(r):"",g=`${e}|${m??h}|${JSON.stringify(l)}`;d.has(g)||(d.add(g),s.push({code:e,severity:I[e],src:m,snippet:h,args:l}))},f=new WeakMap,A=e=>{let r=f.get(e);if(r!==void 0)return r;let l=t.getComputedStyle(e),m=l.display==="none"||l.visibility==="hidden"||l.visibility==="collapse"||e.parentElement!==null&&e.parentElement!==i.documentElement&&A(e.parentElement);return f.set(e,m),m},p=e=>e.closest(\'[aria-hidden="true"]\')!==null,E=e=>/^(presentation|none)$/.test(e.getAttribute("role")??""),H=e=>e.closest(O)!==null,y=e=>$(e.textContent??""),x=e=>{let r=u(e.getAttribute("aria-label"));return r||u((e.getAttribute("aria-labelledby")??"").split(/\\s+/).filter(Boolean).map(l=>e.ownerDocument.getElementById(l)?.textContent??"").join(" "))},P=e=>x(e)||u(e.textContent)||u(Array.from(e.querySelectorAll("img[alt]")).map(r=>r.getAttribute("alt")).join(" "))||u(e.getAttribute("title")),T=e=>{let r=e.getAttribute("href")??"";return{href:r.startsWith("#")?r:e.href??r,name:P(e)}};u(i.title)||c("document-title",null),!u(i.documentElement.getAttribute("lang"))&&!n.locale&&c("document-language",null);let M=n.pages??(n.pageHeight&&n.pageHeight>0?Math.ceil(i.documentElement.scrollHeight/n.pageHeight):1),C=[],v=[],R={"masked-text":[],"filtered-text":[],"fixed-text":[]},w=(e,r)=>{R[e].some(l=>l.contains(r))||(R[e].push(r),c(e,r))},V=a&&ne(i),G=o?Array.from(o.querySelectorAll("*")).slice(0,j):[];for(let e of G){let r=e.tagName.toLowerCase();if(k.has(r)||H(e)||A(e))continue;if(r==="a"&&e.hasAttribute("href")){let g=T(e);C.push(g),!g.name&&!p(e)&&c("link-name",e)}if(!a)continue;let l=t.getComputedStyle(e),m=l.getPropertyValue("mask-image")||l.getPropertyValue("-webkit-mask-image");if(m&&m!=="none"&&y(e)&&w("masked-text",e),l.filter&&l.filter!=="none"&&y(e)&&w("filtered-text",e),l.position==="fixed"&&M>1&&y(e)&&w("fixed-text",e),V)for(let g of["::before","::after"]){let U=re(t.getComputedStyle(e,g).content);U&&c("generated-text",e,{text:U})}if(p(e))continue;r==="img"&&!E(e)?e.hasAttribute("alt")||x(e)||u(e.getAttribute("title"))||c("image-alt",e):(r==="canvas"||e.getAttribute("role")==="img"&&r!=="img")&&(x(e)||u(e.getAttribute("title"))||u(e.querySelector(":scope > title")?.textContent)||c("figure-name",e,{tag:r}));let h=/^h[1-6]$/.test(r)?Number(r[1]):e.getAttribute("role")==="heading"?Number(e.getAttribute("aria-level"))||2:0;h&&v.push({element:e,level:h}),r==="table"&&!E(e)&&Q(e,c)}let b=0;for(let{element:e,level:r}of v)(b===0?r!==1:r>b+1)&&c("heading-order",e,{level:r,previous:b}),b=r;let N=[],W=a&&o?D(J(o)):"",L=0;for(let e of["header","footer"])for(let r of z(i,e,n[e])){for(let l of Array.from(r.querySelectorAll("a[href]")))N.push(T(l));if(a)for(let{text:l,element:m}of K(r)){if(L>=q||W.includes(D(l)))continue;let h=s.length;c("running-text",m,{text:l.slice(0,80),part:e}),s.length>h&&L++}}return{findings:s,links:C,templateLinks:N}}function z(t,n,i){if(i!==void 0){if(!i.trim())return[];let o=new DOMParser().parseFromString(`<!doctype html><body>${i}</body>`,"text/html");return o.body?[o.body]:[]}return Array.from(t.querySelectorAll(`.ff-running-${n}, .formfeed-chrome[data-ff-chrome="${n}"]`))}function J(t){let n=[],i=o=>{if(o.nodeType===3){n.push(o.nodeValue??"");return}if(o.nodeType!==1)return;let s=o;if(!(k.has(s.tagName.toLowerCase())||s.matches(O)))for(let d of Array.from(o.childNodes))i(d)};return i(t),n.join(" ")}function K(t){let n=[],i=o=>{let s=o.tagName.toLowerCase();if(k.has(s)||o.matches(_))return;let d=Array.from(o.children).some(a=>a.matches(_));for(let a of Array.from(o.childNodes))if(a.nodeType===1)i(a);else if(a.nodeType===3&&!d)for(let c of(a.nodeValue??"").split(/\\s*[\xB7\u2022|]\\s*|\\s{2,}|\\n/)){let f=u(c);(f.toLowerCase().match(/[\\p{L}\\p{N}]+/gu)??[]).some(p=>!B.has(p))&&n.push({text:f,element:o})}};return i(t),n}function Q(t,n){let i=Array.from(t.rows);if(!(t.querySelector(\'th, [role="columnheader"], [role="rowheader"]\')!==null)){i.length>=2&&n("table-headers",t);return}let s=te(i),d=s.find(c=>c!==s[0]);d!==void 0&&n("table-rows",t,{first:s[0]??0,other:d});let a=i.flatMap(c=>Array.from(c.cells).filter(f=>f.tagName==="TD"&&ee(f)));a[0]&&n("table-cell-empty",a[0],{count:a.length})}var Z=\'img[alt]:not([alt=""]), svg, canvas, input, select, textarea, button, meter, progress\';function ee(t){return!/[^ \\t\\n\\r\\f]/.test(t.textContent??"")&&t.querySelector(Z)===null}function te(t){let n=[];return t.map(i=>{let o=[],s=0,d=()=>{for(;(n[s]??0)>0;)o[s]=n[s]-1,s++};for(let a of Array.from(i.cells)){d();let c=Math.max(1,a.rowSpan||1)-1;for(let f=Math.max(1,a.colSpan||1);f>0;f--)o[s++]=c}d();for(let a=s;a<n.length;a++)(n[a]??0)>0&&(o[a]=n[a]-1,s=a+1);return n=o,s})}function ne(t){let n=i=>{for(let o of Array.from(i??[])){let s=o;if(s.selectorText&&/::?(before|after)/.test(s.selectorText)&&s.style?.content||o.cssRules&&n(o.cssRules))return!0}return!1};for(let i of Array.from(t.styleSheets))try{if(n(i.cssRules))return!0}catch{return!0}return!1}function re(t){if(!t||t==="none"||t==="normal")return"";let n=(t.match(/"(?:[^"\\\\]|\\\\.)*"/g)??[]).map(o=>o.slice(1,-1).replace(/\\\\(.)/g,"$1"));if(/\\bcounters?\\(/.test(t))return u(t).slice(0,80);let i=u(n.join(""));return $(i)?i.slice(0,80):""}window.formfeedAudit=t=>F(window,t);})();';
5375
5427
 
5376
5428
  // packages/engine/src/lib/assemble.ts
5377
5429
  var DEFAULT_CHROME_PADDING = "0 10mm";
@@ -5626,6 +5678,23 @@ document.addEventListener('click', function (event) {
5626
5678
  }, true);
5627
5679
  </script>`;
5628
5680
  var textEditScript = `<script>${textEditRuntime.replace(/<\/script/gi, "<\\/script")}</script>`;
5681
+ function auditScript(mode) {
5682
+ return `<script>${auditRuntime.replace(/<\/script/gi, "<\\/script")}</script><script>
5683
+ (function () {
5684
+ window.formfeedRunAudit = function (pages) {
5685
+ try {
5686
+ var report = window.formfeedAudit({
5687
+ locale: true,
5688
+ pages: pages,
5689
+ pageHeight: parseFloat(getComputedStyle(document.body).minHeight) || 0,
5690
+ });
5691
+ parent.postMessage({ type: 'formfeed:audit', findings: report.findings }, '*');
5692
+ } catch (e) {}
5693
+ };
5694
+ ${mode === "flow" ? "window.addEventListener('load', function () { (document.fonts ? document.fonts.ready : Promise.resolve()).then(function () { window.formfeedRunAudit(); }); });" : ""}
5695
+ })();
5696
+ </script>`;
5697
+ }
5629
5698
  function documentMeta(options) {
5630
5699
  const name = (options.document ?? "").replace(/[^A-Za-z0-9_-]/g, "");
5631
5700
  return name ? `<meta name="formfeed:document" content="${name}">` : "";
@@ -5737,6 +5806,15 @@ function intoHead(document, markup) {
5737
5806
  ${markup}`
5738
5807
  );
5739
5808
  }
5809
+ function intoBody(document, atStart, atEnd = "") {
5810
+ const own = document.indexOf('<body class="formfeed-body');
5811
+ const open2 = own >= 0 ? own : document.search(/<body[^>]*>/);
5812
+ if (open2 < 0) return document;
5813
+ const content = document.indexOf(">", open2) + 1;
5814
+ const close = document.lastIndexOf("</body>");
5815
+ const end = close >= content ? close : document.length;
5816
+ return `${document.slice(0, content)}${atStart}${document.slice(content, end)}${atEnd}${document.slice(end)}`;
5817
+ }
5740
5818
  function chromeBoxStyle(settings, which) {
5741
5819
  const margin = settings.margin ?? {};
5742
5820
  const left = margin.left ?? "0px";
@@ -5760,12 +5838,16 @@ html { background: #e5e7eb; }
5760
5838
  body.formfeed-preview { position: relative; box-sizing: border-box; width: ${paper.width}; min-height: ${paper.height}; margin: 24px auto; background: #fff; box-shadow: 0 1px 3px rgba(0,0,0,.2), 0 8px 24px rgba(0,0,0,.08); padding: ${margin.top ?? "0"} ${margin.right ?? "0"} ${margin.bottom ?? "0"} ${margin.left ?? "0"}; }
5761
5839
  .formfeed-chrome { color: #6b7280; font: 10px/1.3 system-ui, sans-serif; }
5762
5840
  </style>` : `<style data-formfeed="preview">html { background: #e5e7eb; } body.formfeed-preview { margin: 24px auto; background: #fff; box-shadow: 0 1px 3px rgba(0,0,0,.2); }</style>`;
5763
- const header = headerHtml ? `<div class="formfeed-chrome" style="${pdf ? flowChromeStyle(settings, "header") : `${chromeBoxStyle(settings, "header")}margin-bottom:8px`}">${headerHtml}</div>` : "";
5764
- const footer = footerHtml ? `<div class="formfeed-chrome" style="${pdf ? flowChromeStyle(settings, "footer") : `${chromeBoxStyle(settings, "footer")}margin-top:8px`}">${pageNumberSpans(footerHtml, "1", "1")}</div>` : "";
5765
- return intoHead(
5766
- draft.document,
5767
- `${documentMeta(options)}${chrome}${options.editing ? textEditScript : ""}${inspectScript}${zoomGestureScript}${pdf ? overflowScript("flow") : ""}`
5768
- ).replace(/(<body[^>]*>)/, `$1${header}`).replace("</body>", `${footer}</body>`);
5841
+ const header = headerHtml ? `<div class="formfeed-chrome" data-ff-chrome="header" style="${pdf ? flowChromeStyle(settings, "header") : `${chromeBoxStyle(settings, "header")}margin-bottom:8px`}">${headerHtml}</div>` : "";
5842
+ const footer = footerHtml ? `<div class="formfeed-chrome" data-ff-chrome="footer" style="${pdf ? flowChromeStyle(settings, "footer") : `${chromeBoxStyle(settings, "footer")}margin-top:8px`}">${pageNumberSpans(footerHtml, "1", "1")}</div>` : "";
5843
+ return intoBody(
5844
+ intoHead(
5845
+ draft.document,
5846
+ `${documentMeta(options)}${chrome}${options.editing ? textEditScript : ""}${inspectScript}${zoomGestureScript}${pdf ? overflowScript("flow") : ""}${pdf && options.audit ? auditScript("flow") : ""}`
5847
+ ),
5848
+ header,
5849
+ footer
5850
+ );
5769
5851
  }
5770
5852
  function canvasDocument(draft) {
5771
5853
  const chrome = `<style data-formfeed="stage">html, body { background: transparent !important; margin: 0 !important; }</style>`;
@@ -5792,14 +5874,17 @@ html { background: #e5e7eb; }
5792
5874
  `;
5793
5875
  const selectorGuard = `<script>(function(){var q=DocumentFragment.prototype.querySelectorAll;DocumentFragment.prototype.querySelectorAll=function(s){try{return q.call(this,s);}catch(e){console.warn('formfeed paged preview: skipped a selector Paged.js could not use:',s);return q.call(this,':not(*)');}};})();</script>`;
5794
5876
  const boxSizingGuard = `<script>(function(){var rule='.pagedjs_pagebox *';var o=new MutationObserver(function(ms){ms.forEach(function(m){m.addedNodes.forEach(function(n){if(n.nodeName==='STYLE'&&n.textContent&&n.textContent.indexOf(rule)>=0){n.textContent=n.textContent.split(rule).join('.pagedjs_pagebox [class*="pagedjs_"]');o.disconnect();}});});});o.observe(document.documentElement,{childList:true,subtree:true});})();</script>`;
5795
- const config = `<script>window.PagedConfig = { auto: true, after: function (flow) { try { if (window.formfeedDrawCharts) window.formfeedDrawCharts(); } catch (e) {} try { if (window.formfeedFit) window.formfeedFit(); } catch (e) {} try { parent.postMessage({ type: 'formfeed:pages', pages: flow.total }, '*'); } catch (e) {} if (window.formfeedCheckOverflow) window.formfeedCheckOverflow(); try { if (window.formfeedEditReady) window.formfeedEditReady(); } catch (e) {} } };</script>`;
5877
+ const config = `<script>window.PagedConfig = { auto: true, after: function (flow) { try { if (window.formfeedDrawCharts) window.formfeedDrawCharts(); } catch (e) {} try { if (window.formfeedFit) window.formfeedFit(); } catch (e) {} try { parent.postMessage({ type: 'formfeed:pages', pages: flow.total }, '*'); } catch (e) {} if (window.formfeedCheckOverflow) window.formfeedCheckOverflow(); if (window.formfeedRunAudit) window.formfeedRunAudit(flow.total); try { if (window.formfeedEditReady) window.formfeedEditReady(); } catch (e) {} } };</script>`;
5796
5878
  const script = `<script src="${options.pagedScriptUrl}"></script>`;
5797
5879
  const header = headerHtml ? `<div class="ff-running-header">${pageNumberSpans(headerHtml, '<span class="ff-page-no"></span>', '<span class="ff-page-total"></span>')}</div>` : "";
5798
5880
  const footer = footerHtml ? `<div class="ff-running-footer">${pageNumberSpans(footerHtml, '<span class="ff-page-no"></span>', '<span class="ff-page-total"></span>')}</div>` : "";
5799
- return intoHead(
5800
- draft.document,
5801
- `${documentMeta(options)}<style data-formfeed="paged">${runningCss}</style>${selectorGuard}${boxSizingGuard}${overflowScript("paged")}${config}${script}${options.editing ? textEditScript : ""}${inspectScript}${pageNavScript}${zoomGestureScript}`
5802
- ).replace(/(<body[^>]*>)/, `$1${header}${footer}`);
5881
+ return intoBody(
5882
+ intoHead(
5883
+ draft.document,
5884
+ `${documentMeta(options)}<style data-formfeed="paged">${runningCss}</style>${selectorGuard}${boxSizingGuard}${overflowScript("paged")}${options.audit ? auditScript("paged") : ""}${config}${script}${options.editing ? textEditScript : ""}${inspectScript}${pageNavScript}${zoomGestureScript}`
5885
+ ),
5886
+ `${header}${footer}`
5887
+ );
5803
5888
  }
5804
5889
 
5805
5890
  // packages/engine/src/lib/source-scan.ts
@@ -6289,18 +6374,18 @@ function applyRegionEdit(source, engine, region, items) {
6289
6374
  const rewritten = source.slice(0, region.start) + whole + source.slice(region.end);
6290
6375
  return readsBack(rewritten, engine, region.tagStart, expected) ? { source: rewritten } : { error: "invalid" };
6291
6376
  }
6292
- function translationItems(text3) {
6377
+ function translationItems(text4) {
6293
6378
  const items = [];
6294
6379
  const names = [];
6295
6380
  let last = 0;
6296
- for (const match of text3.matchAll(/\{(\w+)\}/g)) {
6381
+ for (const match of text4.matchAll(/\{(\w+)\}/g)) {
6297
6382
  if (match.index > last)
6298
- items.push({ kind: "text", text: text3.slice(last, match.index) });
6383
+ items.push({ kind: "text", text: text4.slice(last, match.index) });
6299
6384
  items.push({ kind: "chip", index: names.length });
6300
6385
  names.push(match[1] ?? "");
6301
6386
  last = match.index + match[0].length;
6302
6387
  }
6303
- if (last < text3.length) items.push({ kind: "text", text: text3.slice(last) });
6388
+ if (last < text4.length) items.push({ kind: "text", text: text4.slice(last) });
6304
6389
  return { items, names };
6305
6390
  }
6306
6391
  function translationText(items, names) {
@@ -6544,9 +6629,9 @@ function translationCall(engine, inner) {
6544
6629
  );
6545
6630
  return match ? { key: match[2] ?? "", params: parseParams(match[3] ?? "", "=", " ") } : null;
6546
6631
  }
6547
- function parseParams(text3, assign, separator) {
6632
+ function parseParams(text4, assign, separator) {
6548
6633
  const params = {};
6549
- for (const piece of splitTopLevel(text3, separator)) {
6634
+ for (const piece of splitTopLevel(text4, separator)) {
6550
6635
  const at = piece.indexOf(assign);
6551
6636
  if (at <= 0) continue;
6552
6637
  const name = piece.slice(0, at).trim().replace(/^['"]|['"]$/g, "");
@@ -6554,12 +6639,12 @@ function parseParams(text3, assign, separator) {
6554
6639
  }
6555
6640
  return params;
6556
6641
  }
6557
- function splitTopLevel(text3, separator) {
6642
+ function splitTopLevel(text4, separator) {
6558
6643
  const pieces = [];
6559
6644
  let depth = 0;
6560
6645
  let quote2 = null;
6561
6646
  let current = "";
6562
- for (const ch of text3) {
6647
+ for (const ch of text4) {
6563
6648
  if (quote2) {
6564
6649
  if (ch === quote2) quote2 = null;
6565
6650
  } else if (ch === '"' || ch === "'") quote2 = ch;
@@ -6584,7 +6669,7 @@ function entityChar(reference) {
6584
6669
  return code === void 0 ? null : String.fromCharCode(code);
6585
6670
  }
6586
6671
  function decodeText2(raw) {
6587
- let text3 = "";
6672
+ let text4 = "";
6588
6673
  const map = [];
6589
6674
  let i = 0;
6590
6675
  while (i < raw.length) {
@@ -6594,23 +6679,23 @@ function decodeText2(raw) {
6594
6679
  const decoded = match ? entityChar(match[1] ?? "") : null;
6595
6680
  if (match && decoded !== null) {
6596
6681
  for (let u = 0; u < decoded.length; u++) map.push(i);
6597
- text3 += decoded;
6682
+ text4 += decoded;
6598
6683
  i += match[0].length;
6599
6684
  continue;
6600
6685
  }
6601
6686
  }
6602
- text3 += raw[i];
6687
+ text4 += raw[i];
6603
6688
  map.push(i);
6604
6689
  i++;
6605
6690
  }
6606
6691
  map.push(raw.length);
6607
- return { text: text3, map };
6692
+ return { text: text4, map };
6608
6693
  }
6609
- function encodeText(text3, keepNbsp, allBraces = false) {
6694
+ function encodeText(text4, keepNbsp, allBraces = false) {
6610
6695
  let out = "";
6611
- for (let k = 0; k < text3.length; k++) {
6612
- const ch = text3[k] ?? "";
6613
- const next = text3[k + 1];
6696
+ for (let k = 0; k < text4.length; k++) {
6697
+ const ch = text4[k] ?? "";
6698
+ const next = text4[k + 1];
6614
6699
  if (ch === "&") out += "&amp;";
6615
6700
  else if (ch === "<") out += "&lt;";
6616
6701
  else if (ch === ">") out += "&gt;";
@@ -6622,8 +6707,8 @@ function encodeText(text3, keepNbsp, allBraces = false) {
6622
6707
  }
6623
6708
  return out;
6624
6709
  }
6625
- function encodeTemplateText(text3) {
6626
- return encodeText(text3, true);
6710
+ function encodeTemplateText(text4) {
6711
+ return encodeText(text4, true);
6627
6712
  }
6628
6713
  function spanOf(part) {
6629
6714
  return part ? [part.start, part.end] : [0, 0];
@@ -6657,7 +6742,7 @@ function rewriteSegment(source, segStart, segEnd, parts, next, breakSpelling) {
6657
6742
  const to = map[old.length - suffix] ?? segEnd;
6658
6743
  const middle = next.slice(prefix, next.length - suffix);
6659
6744
  const keepNbsp = old.slice(prefix, old.length - suffix).includes(NBSP);
6660
- const written = middle.split(BREAK).map((text3) => encodeText(text3, keepNbsp)).join(breakSpelling);
6745
+ const written = middle.split(BREAK).map((text4) => encodeText(text4, keepNbsp)).join(breakSpelling);
6661
6746
  return source.slice(segStart, from) + written + source.slice(to, segEnd);
6662
6747
  }
6663
6748
  var isHighSurrogate = (code) => code >= 55296 && code <= 56319;
@@ -7412,11 +7497,11 @@ function googleFontsUrl(uses) {
7412
7497
 
7413
7498
  // packages/engine/src/lib/design/hash.ts
7414
7499
  function designOutputHash(html, css2) {
7415
- const text3 = `${html}\0${css2}`;
7500
+ const text4 = `${html}\0${css2}`;
7416
7501
  let a = 2166136261;
7417
7502
  let b = 16777619 ^ 2654435769;
7418
- for (let i = 0; i < text3.length; i++) {
7419
- const code = text3.charCodeAt(i);
7503
+ for (let i = 0; i < text4.length; i++) {
7504
+ const code = text4.charCodeAt(i);
7420
7505
  if (code === 13) continue;
7421
7506
  a = Math.imul(a ^ code, 16777619) >>> 0;
7422
7507
  b = Math.imul(b ^ code, 16777619) >>> 0;
@@ -7643,32 +7728,32 @@ function imageSource(source, engine) {
7643
7728
  return `{{ brand.logo.${source.brandLogo} }}`;
7644
7729
  }
7645
7730
  function layerCss(layer) {
7646
- const text3 = layer.type === "text" ? layer : null;
7731
+ const text4 = layer.type === "text" ? layer : null;
7647
7732
  const box = {
7648
7733
  left: px(layer.x),
7649
7734
  top: px(layer.y),
7650
7735
  width: px(layer.width),
7651
- height: text3?.sizing === "grow" ? void 0 : px(layer.height),
7736
+ height: text4?.sizing === "grow" ? void 0 : px(layer.height),
7652
7737
  opacity: layer.opacity < 1 ? num(layer.opacity) : void 0,
7653
7738
  transform: layer.rotation ? `rotate(${num(layer.rotation)}deg)` : void 0,
7654
7739
  "border-radius": layer.type === "shape" && layer.shape === "ellipse" ? "50%" : layer.radius ? px(layer.radius) : void 0,
7655
7740
  border: layer.stroke && layer.stroke.width > 0 ? `${px(layer.stroke.width)} solid ${paint(layer.stroke.paint)}` : void 0,
7656
- "box-shadow": layer.shadow && !text3 ? shadow(layer.shadow) : void 0
7741
+ "box-shadow": layer.shadow && !text4 ? shadow(layer.shadow) : void 0
7657
7742
  };
7658
- if (text3) {
7743
+ if (text4) {
7659
7744
  Object.assign(box, {
7660
7745
  display: "flex",
7661
7746
  "flex-direction": "column",
7662
- "justify-content": text3.valign === "middle" ? "center" : text3.valign === "bottom" ? "flex-end" : "flex-start",
7663
- overflow: text3.sizing === "grow" ? void 0 : "hidden",
7664
- padding: text3.padding ? px(text3.padding) : void 0,
7665
- background: text3.fill ? paint(text3.fill) : void 0
7747
+ "justify-content": text4.valign === "middle" ? "center" : text4.valign === "bottom" ? "flex-end" : "flex-start",
7748
+ overflow: text4.sizing === "grow" ? void 0 : "hidden",
7749
+ padding: text4.padding ? px(text4.padding) : void 0,
7750
+ background: text4.fill ? paint(text4.fill) : void 0
7666
7751
  });
7667
- const s = text3.style;
7668
- const clamp = text3.maxLines ? {
7752
+ const s = text4.style;
7753
+ const clamp = text4.maxLines ? {
7669
7754
  display: "-webkit-box",
7670
7755
  "-webkit-box-orient": "vertical",
7671
- "-webkit-line-clamp": String(text3.maxLines),
7756
+ "-webkit-line-clamp": String(text4.maxLines),
7672
7757
  overflow: "hidden"
7673
7758
  } : {};
7674
7759
  const inner = decl({
@@ -7684,7 +7769,7 @@ function layerCss(layer) {
7684
7769
  "font-style": s.italic ? "italic" : void 0,
7685
7770
  "text-transform": s.uppercase ? "uppercase" : void 0,
7686
7771
  "text-decoration": [s.underline && "underline", s.strike && "line-through"].filter(Boolean).join(" ") || void 0,
7687
- "text-shadow": text3.shadow ? shadow(text3.shadow) : void 0,
7772
+ "text-shadow": text4.shadow ? shadow(text4.shadow) : void 0,
7688
7773
  "overflow-wrap": "break-word",
7689
7774
  ...clamp
7690
7775
  });
@@ -8048,24 +8133,24 @@ var paymentMeansCodes = list2(
8048
8133
  // packages/engine/src/lib/einvoice/decimal.ts
8049
8134
  function decimalText(value) {
8050
8135
  if (typeof value === "string") {
8051
- const text4 = value.trim();
8052
- return /^-?\d+(\.\d+)?$/.test(text4) ? trimZeros(text4) : null;
8136
+ const text5 = value.trim();
8137
+ return /^-?\d+(\.\d+)?$/.test(text5) ? trimZeros(text5) : null;
8053
8138
  }
8054
8139
  if (typeof value !== "number" || !Number.isFinite(value)) return null;
8055
- const text3 = value.toPrecision(15);
8056
- return /^-?\d+(\.\d+)?$/.test(text3) ? trimZeros(text3) : null;
8140
+ const text4 = value.toPrecision(15);
8141
+ return /^-?\d+(\.\d+)?$/.test(text4) ? trimZeros(text4) : null;
8057
8142
  }
8058
- function trimZeros(text3) {
8059
- const trimmed = text3.includes(".") ? text3.replace(/0+$/, "").replace(/\.$/, "") : text3;
8143
+ function trimZeros(text4) {
8144
+ const trimmed = text4.includes(".") ? text4.replace(/0+$/, "").replace(/\.$/, "") : text4;
8060
8145
  return trimmed === "-0" ? "0" : trimmed;
8061
8146
  }
8062
- function decimalPlaces(text3) {
8063
- const dot = text3.indexOf(".");
8064
- return dot === -1 ? 0 : text3.length - dot - 1;
8147
+ function decimalPlaces(text4) {
8148
+ const dot = text4.indexOf(".");
8149
+ return dot === -1 ? 0 : text4.length - dot - 1;
8065
8150
  }
8066
- function toUnits(text3, scale) {
8067
- const negative = text3.startsWith("-");
8068
- const [whole = "0", fraction = ""] = (negative ? text3.slice(1) : text3).split(
8151
+ function toUnits(text4, scale) {
8152
+ const negative = text4.startsWith("-");
8153
+ const [whole = "0", fraction = ""] = (negative ? text4.slice(1) : text4).split(
8069
8154
  "."
8070
8155
  );
8071
8156
  const units = BigInt(whole + fraction.padEnd(scale, "0").slice(0, scale));
@@ -8076,13 +8161,13 @@ function fromUnits(units, scale, places = scale) {
8076
8161
  const digits = (negative ? -units : units).toString().padStart(scale + 1, "0");
8077
8162
  const whole = digits.slice(0, digits.length - scale);
8078
8163
  const fraction = digits.slice(digits.length - scale).padEnd(places, "0");
8079
- const text3 = places > 0 ? `${whole}.${fraction.slice(0, places)}` : whole;
8080
- return negative && /[1-9]/.test(text3) ? `-${text3}` : text3;
8164
+ const text4 = places > 0 ? `${whole}.${fraction.slice(0, places)}` : whole;
8165
+ return negative && /[1-9]/.test(text4) ? `-${text4}` : text4;
8081
8166
  }
8082
- function withPlaces(text3, min) {
8083
- const places = decimalPlaces(text3);
8084
- if (places >= min) return text3;
8085
- return places === 0 ? `${text3}.${"0".repeat(min)}` : text3 + "0".repeat(min - places);
8167
+ function withPlaces(text4, min) {
8168
+ const places = decimalPlaces(text4);
8169
+ if (places >= min) return text4;
8170
+ return places === 0 ? `${text4}.${"0".repeat(min)}` : text4 + "0".repeat(min - places);
8086
8171
  }
8087
8172
  function divideRounded(value, divisor) {
8088
8173
  const negative = value < 0n !== divisor < 0n;
@@ -8504,37 +8589,37 @@ function valueAt(data, path) {
8504
8589
  }
8505
8590
  function normalise(input, add2, options) {
8506
8591
  const amount2 = (value, path) => {
8507
- const text3 = decimalText(value);
8508
- if (text3 === null) {
8592
+ const text4 = decimalText(value);
8593
+ if (text4 === null) {
8509
8594
  add2("not_a_number", path, `_invoice.${path} is not a number`);
8510
8595
  return "0";
8511
8596
  }
8512
- if (decimalPlaces(text3) > 2) {
8597
+ if (decimalPlaces(text4) > 2) {
8513
8598
  add2(
8514
8599
  "decimals",
8515
8600
  path,
8516
8601
  `_invoice.${path} has more than 2 decimals; an amount of the invoice is exact to the cent`,
8517
8602
  {
8518
- args: { max: 2, value: text3 }
8603
+ args: { max: 2, value: text4 }
8519
8604
  }
8520
8605
  );
8521
8606
  return "0";
8522
8607
  }
8523
- return text3;
8608
+ return text4;
8524
8609
  };
8525
8610
  const number = (value, path, max2) => {
8526
- const text3 = decimalText(value);
8527
- if (text3 === null) {
8611
+ const text4 = decimalText(value);
8612
+ if (text4 === null) {
8528
8613
  add2("not_a_number", path, `_invoice.${path} is not a number`);
8529
8614
  return "0";
8530
8615
  }
8531
- if (decimalPlaces(text3) > max2) {
8616
+ if (decimalPlaces(text4) > max2) {
8532
8617
  add2("decimals", path, `_invoice.${path} has more than ${max2} decimals`, {
8533
- args: { max: max2, value: text3 }
8618
+ args: { max: max2, value: text4 }
8534
8619
  });
8535
8620
  return "0";
8536
8621
  }
8537
- return text3;
8622
+ return text4;
8538
8623
  };
8539
8624
  const optionalAmount = (value, path) => value === void 0 ? void 0 : amount2(value, path);
8540
8625
  const date2 = (value, path) => {
@@ -8912,7 +8997,7 @@ function defined(value) {
8912
8997
  Object.entries(value).filter(([, v]) => v !== void 0)
8913
8998
  );
8914
8999
  }
8915
- var cents = (text3) => text3 === void 0 ? 0n : toUnits(text3, 2);
9000
+ var cents = (text4) => text4 === void 0 ? 0n : toUnits(text4, 2);
8916
9001
  var money = (value) => fromUnits(value, 2);
8917
9002
  var rateKey = (tax) => `${tax.category}|${tax.rate ?? ""}`;
8918
9003
  function rules(invoice, add2) {
@@ -9372,14 +9457,14 @@ var CII_GUIDELINES = {
9372
9457
  en16931: "urn:cen.eu:en16931:2017"
9373
9458
  };
9374
9459
  var EINVOICE_XML_NAME = "factur-x.xml";
9375
- function leaf(name, text3, attributes) {
9376
- if (text3 === void 0 || text3 === "") return void 0;
9460
+ function leaf(name, text4, attributes) {
9461
+ if (text4 === void 0 || text4 === "") return void 0;
9377
9462
  const kept = Object.entries(attributes ?? {}).filter(
9378
9463
  (entry) => entry[1] !== void 0
9379
9464
  );
9380
9465
  return {
9381
9466
  name,
9382
- text: text3,
9467
+ text: text4,
9383
9468
  ...kept.length > 0 ? { attributes: Object.fromEntries(kept) } : {}
9384
9469
  };
9385
9470
  }
@@ -9636,14 +9721,14 @@ function invoiceToCii(invoice, options) {
9636
9721
  ${serialise(root, 0)}`;
9637
9722
  }
9638
9723
  var xmlCharacter = (code) => code === 9 || code === 10 || code === 13 || code >= 32 && !(code >= 55296 && code <= 57343) && code !== 65534 && code !== 65535;
9639
- function carried(text3) {
9724
+ function carried(text4) {
9640
9725
  let out = "";
9641
- for (const character of text3)
9726
+ for (const character of text4)
9642
9727
  if (xmlCharacter(character.codePointAt(0) ?? 0)) out += character;
9643
9728
  return out;
9644
9729
  }
9645
- var escapeText2 = (text3) => carried(text3).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
9646
- var escapeAttribute2 = (text3) => escapeText2(text3).replace(/"/g, "&quot;");
9730
+ var escapeText2 = (text4) => carried(text4).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
9731
+ var escapeAttribute2 = (text4) => escapeText2(text4).replace(/"/g, "&quot;");
9647
9732
  function serialise(element, depth) {
9648
9733
  const indent = " ".repeat(depth);
9649
9734
  const attributes = Object.entries(element.attributes ?? {}).map(([name, value]) => ` ${name}="${escapeAttribute2(value)}"`).join("");
@@ -9677,7 +9762,7 @@ var DASHES = /* @__PURE__ */ new Set([45, 8208, 8209, 8210, 8211, 8722]);
9677
9762
  var INVISIBLE = /* @__PURE__ */ new Set([173, 8203, 8204, 8205, 8288, 65279]);
9678
9763
  var isDigit = (code) => code >= DIGIT_0 && code <= DIGIT_9;
9679
9764
  var MAX_GROUPS = 6;
9680
- function shownAmounts(text3) {
9765
+ function shownAmounts(text4) {
9681
9766
  const found = /* @__PURE__ */ new Set();
9682
9767
  let run2 = "";
9683
9768
  const flush = () => {
@@ -9701,7 +9786,7 @@ function shownAmounts(text3) {
9701
9786
  }
9702
9787
  }
9703
9788
  };
9704
- for (const character of text3) {
9789
+ for (const character of text4) {
9705
9790
  const code = character.codePointAt(0) ?? 0;
9706
9791
  if (INVISIBLE.has(code)) continue;
9707
9792
  if (isDigit(code)) run2 += character;
@@ -9741,9 +9826,9 @@ function toCents(whole, fraction) {
9741
9826
  if (whole.length > 18 || /[1-9]/.test(fraction.slice(2))) return null;
9742
9827
  return BigInt(whole) * 100n + BigInt((fraction + "00").slice(0, 2));
9743
9828
  }
9744
- function squeeze(text3) {
9829
+ function squeeze(text4) {
9745
9830
  let out = "";
9746
- for (const character of text3) {
9831
+ for (const character of text4) {
9747
9832
  const code = character.codePointAt(0) ?? 0;
9748
9833
  if (INVISIBLE.has(code) || /\s/.test(character) || SPACES.has(code))
9749
9834
  continue;
@@ -9751,8 +9836,8 @@ function squeeze(text3) {
9751
9836
  }
9752
9837
  return out;
9753
9838
  }
9754
- function displayCheck(text3, invoice) {
9755
- const whole = typeof text3 === "string" ? text3 : text3.join("\n");
9839
+ function displayCheck(text4, invoice) {
9840
+ const whole = typeof text4 === "string" ? text4 : text4.join("\n");
9756
9841
  const amounts = shownAmounts(whole);
9757
9842
  const squeezed = squeeze(whole);
9758
9843
  const values = [];
@@ -9818,97 +9903,97 @@ function invoicePathSegments(path) {
9818
9903
  }
9819
9904
  return segments;
9820
9905
  }
9821
- function locateJsonPath(text3, path) {
9822
- let offset = skipSpace(text3, 0);
9823
- if (offset >= text3.length) return null;
9906
+ function locateJsonPath(text4, path) {
9907
+ let offset = skipSpace(text4, 0);
9908
+ if (offset >= text4.length) return null;
9824
9909
  let best = { offset, depth: 0 };
9825
9910
  try {
9826
9911
  for (let depth = 0; depth < path.length; depth += 1) {
9827
9912
  const segment = path[depth];
9828
- const found = typeof segment === "number" ? arrayItem(text3, offset, segment) : objectMember(text3, offset, String(segment));
9913
+ const found = typeof segment === "number" ? arrayItem(text4, offset, segment) : objectMember(text4, offset, String(segment));
9829
9914
  if (!found) break;
9830
9915
  best = { offset: found.at, depth: depth + 1 };
9831
9916
  offset = found.value;
9832
9917
  }
9833
9918
  } catch {
9834
9919
  }
9835
- return { ...positionAt(text3, best.offset), depth: best.depth };
9920
+ return { ...positionAt(text4, best.offset), depth: best.depth };
9836
9921
  }
9837
- function positionAt(text3, offset) {
9922
+ function positionAt(text4, offset) {
9838
9923
  let line2 = 1;
9839
9924
  let lineStart = 0;
9840
9925
  for (let i = 0; i < offset; i += 1)
9841
- if (text3.charCodeAt(i) === 10) {
9926
+ if (text4.charCodeAt(i) === 10) {
9842
9927
  line2 += 1;
9843
9928
  lineStart = i + 1;
9844
9929
  }
9845
9930
  return { line: line2, column: offset - lineStart + 1 };
9846
9931
  }
9847
- function skipSpace(text3, from) {
9932
+ function skipSpace(text4, from) {
9848
9933
  let i = from;
9849
- while (i < text3.length && /\s/.test(text3[i] ?? "")) i += 1;
9934
+ while (i < text4.length && /\s/.test(text4[i] ?? "")) i += 1;
9850
9935
  return i;
9851
9936
  }
9852
- function stringEnd(text3, from) {
9853
- if (text3[from] !== '"') throw new Error("string");
9854
- for (let i = from + 1; i < text3.length; i += 1) {
9855
- if (text3[i] === "\\") i += 1;
9856
- else if (text3[i] === '"') return i + 1;
9937
+ function stringEnd(text4, from) {
9938
+ if (text4[from] !== '"') throw new Error("string");
9939
+ for (let i = from + 1; i < text4.length; i += 1) {
9940
+ if (text4[i] === "\\") i += 1;
9941
+ else if (text4[i] === '"') return i + 1;
9857
9942
  }
9858
9943
  throw new Error("string");
9859
9944
  }
9860
- function valueEnd(text3, from) {
9861
- const start = skipSpace(text3, from);
9862
- const first = text3[start];
9863
- if (first === '"') return stringEnd(text3, start);
9945
+ function valueEnd(text4, from) {
9946
+ const start = skipSpace(text4, from);
9947
+ const first = text4[start];
9948
+ if (first === '"') return stringEnd(text4, start);
9864
9949
  if (first === "{" || first === "[") {
9865
9950
  const close = first === "{" ? "}" : "]";
9866
- let i2 = skipSpace(text3, start + 1);
9867
- if (text3[i2] === close) return i2 + 1;
9951
+ let i2 = skipSpace(text4, start + 1);
9952
+ if (text4[i2] === close) return i2 + 1;
9868
9953
  for (; ; ) {
9869
9954
  if (first === "{") {
9870
- i2 = skipSpace(text3, stringEnd(text3, skipSpace(text3, i2)));
9871
- if (text3[i2] !== ":") throw new Error("colon");
9955
+ i2 = skipSpace(text4, stringEnd(text4, skipSpace(text4, i2)));
9956
+ if (text4[i2] !== ":") throw new Error("colon");
9872
9957
  i2 += 1;
9873
9958
  }
9874
- i2 = skipSpace(text3, valueEnd(text3, i2));
9875
- if (text3[i2] === close) return i2 + 1;
9876
- if (text3[i2] !== ",") throw new Error("comma");
9959
+ i2 = skipSpace(text4, valueEnd(text4, i2));
9960
+ if (text4[i2] === close) return i2 + 1;
9961
+ if (text4[i2] !== ",") throw new Error("comma");
9877
9962
  i2 += 1;
9878
9963
  }
9879
9964
  }
9880
9965
  let i = start;
9881
- while (i < text3.length && !/[\s,\]}]/.test(text3[i] ?? "")) i += 1;
9966
+ while (i < text4.length && !/[\s,\]}]/.test(text4[i] ?? "")) i += 1;
9882
9967
  if (i === start) throw new Error("value");
9883
9968
  return i;
9884
9969
  }
9885
- function objectMember(text3, from, key) {
9886
- const start = skipSpace(text3, from);
9887
- if (text3[start] !== "{") return null;
9888
- let i = skipSpace(text3, start + 1);
9889
- while (text3[i] !== "}") {
9890
- const at = skipSpace(text3, i);
9891
- const end = stringEnd(text3, at);
9892
- const colon = skipSpace(text3, end);
9893
- if (text3[colon] !== ":") throw new Error("colon");
9894
- const value = skipSpace(text3, colon + 1);
9895
- if (JSON.parse(text3.slice(at, end)) === key) return { at, value };
9896
- i = skipSpace(text3, valueEnd(text3, value));
9897
- if (text3[i] === ",") i = skipSpace(text3, i + 1);
9898
- else if (text3[i] !== "}") throw new Error("comma");
9970
+ function objectMember(text4, from, key) {
9971
+ const start = skipSpace(text4, from);
9972
+ if (text4[start] !== "{") return null;
9973
+ let i = skipSpace(text4, start + 1);
9974
+ while (text4[i] !== "}") {
9975
+ const at = skipSpace(text4, i);
9976
+ const end = stringEnd(text4, at);
9977
+ const colon = skipSpace(text4, end);
9978
+ if (text4[colon] !== ":") throw new Error("colon");
9979
+ const value = skipSpace(text4, colon + 1);
9980
+ if (JSON.parse(text4.slice(at, end)) === key) return { at, value };
9981
+ i = skipSpace(text4, valueEnd(text4, value));
9982
+ if (text4[i] === ",") i = skipSpace(text4, i + 1);
9983
+ else if (text4[i] !== "}") throw new Error("comma");
9899
9984
  }
9900
9985
  return null;
9901
9986
  }
9902
- function arrayItem(text3, from, index) {
9903
- const start = skipSpace(text3, from);
9904
- if (text3[start] !== "[") return null;
9905
- let i = skipSpace(text3, start + 1);
9906
- for (let n = 0; text3[i] !== "]"; n += 1) {
9907
- const at = skipSpace(text3, i);
9987
+ function arrayItem(text4, from, index) {
9988
+ const start = skipSpace(text4, from);
9989
+ if (text4[start] !== "[") return null;
9990
+ let i = skipSpace(text4, start + 1);
9991
+ for (let n = 0; text4[i] !== "]"; n += 1) {
9992
+ const at = skipSpace(text4, i);
9908
9993
  if (n === index) return { at, value: at };
9909
- i = skipSpace(text3, valueEnd(text3, at));
9910
- if (text3[i] === ",") i = skipSpace(text3, i + 1);
9911
- else if (text3[i] !== "]") throw new Error("comma");
9994
+ i = skipSpace(text4, valueEnd(text4, at));
9995
+ if (text4[i] === ",") i = skipSpace(text4, i + 1);
9996
+ else if (text4[i] !== "]") throw new Error("comma");
9912
9997
  }
9913
9998
  return null;
9914
9999
  }
@@ -10021,8 +10106,8 @@ var ENTITIES = {
10021
10106
  agrave: 224,
10022
10107
  ccedil: 231
10023
10108
  };
10024
- function decodeEntities(text3) {
10025
- return text3.replace(
10109
+ function decodeEntities(text4) {
10110
+ return text4.replace(
10026
10111
  /&(#x[0-9a-f]+|#\d+|[a-z][a-z0-9]*);/gi,
10027
10112
  (whole, reference) => {
10028
10113
  const code = reference.startsWith("#") ? reference[1] === "x" || reference[1] === "X" ? Number.parseInt(reference.slice(2), 16) : Number.parseInt(reference.slice(1), 10) : ENTITIES[reference];
@@ -10148,6 +10233,320 @@ function einvoiceXml(data, options) {
10148
10233
  };
10149
10234
  }
10150
10235
 
10236
+ // packages/engine/src/lib/accessibility/index.ts
10237
+ var PDFUA_STANDARD = "PDF/UA-1";
10238
+ var UA_CHECKS = ["strict", "report"];
10239
+ function resolveUa(setting) {
10240
+ if (setting === true) return { check: "strict" };
10241
+ if (!setting || typeof setting !== "object" || Array.isArray(setting))
10242
+ return null;
10243
+ const check = setting.check;
10244
+ return { check: check === "report" ? "report" : "strict" };
10245
+ }
10246
+ var UA_REFUSALS = {
10247
+ office: "pdf.ua is not available for Word and PowerPoint templates yet",
10248
+ merge: "pdf.ua cannot be combined with post.merge_after: the tags of merged documents do not survive the merge"
10249
+ };
10250
+ function isUaSetting(value) {
10251
+ if (value === null || typeof value === "boolean") return true;
10252
+ if (typeof value !== "object" || Array.isArray(value)) return false;
10253
+ const entries = Object.entries(value);
10254
+ return entries.every(
10255
+ ([key, entry]) => key === "check" && UA_CHECKS.includes(entry)
10256
+ );
10257
+ }
10258
+ var AUDIT_CODES = [
10259
+ "document-title",
10260
+ "image-alt",
10261
+ "figure-name",
10262
+ "heading-order",
10263
+ "table-rows",
10264
+ "table-cell-empty",
10265
+ "masked-text",
10266
+ "filtered-text",
10267
+ "document-language",
10268
+ "table-headers",
10269
+ "link-name",
10270
+ "generated-text",
10271
+ "fixed-text",
10272
+ "running-text"
10273
+ ];
10274
+ var AUDIT_SEVERITY = {
10275
+ "document-title": "error",
10276
+ "image-alt": "error",
10277
+ "figure-name": "error",
10278
+ "heading-order": "error",
10279
+ "table-rows": "error",
10280
+ "table-cell-empty": "error",
10281
+ "masked-text": "error",
10282
+ "filtered-text": "warning",
10283
+ "document-language": "warning",
10284
+ "table-headers": "warning",
10285
+ "link-name": "warning",
10286
+ "generated-text": "warning",
10287
+ "fixed-text": "warning",
10288
+ "running-text": "warning"
10289
+ };
10290
+ var AUDIT_RULES = {
10291
+ "document-title": ["7.1-9", "7.1-10"],
10292
+ "image-alt": ["7.3-1"],
10293
+ "figure-name": ["7.3-1"],
10294
+ "heading-order": ["7.4.2-1"],
10295
+ "table-rows": ["7.2-43"],
10296
+ "table-cell-empty": ["7.2-43"],
10297
+ "masked-text": ["7.20-2"],
10298
+ "filtered-text": [],
10299
+ "document-language": [],
10300
+ "table-headers": [],
10301
+ "link-name": [],
10302
+ "generated-text": [],
10303
+ "fixed-text": [],
10304
+ "running-text": []
10305
+ };
10306
+ var AUDIT_TITLES = {
10307
+ "document-title": "No document title",
10308
+ "image-alt": "Image without an alt attribute",
10309
+ "figure-name": "Figure without a name",
10310
+ "heading-order": "Heading level skipped",
10311
+ "table-rows": "Table rows of different lengths",
10312
+ "table-cell-empty": "Empty table cell",
10313
+ "masked-text": "Text under a CSS mask",
10314
+ "filtered-text": "Text under a CSS filter",
10315
+ "document-language": "No language stated",
10316
+ "table-headers": "Table without header cells",
10317
+ "link-name": "Link without a name",
10318
+ "generated-text": "Text generated by CSS",
10319
+ "fixed-text": "Fixed text on every page",
10320
+ "running-text": "Only in the header or footer"
10321
+ };
10322
+ var AUDIT_SNIPPET_LENGTH = 120;
10323
+ var text3 = (value) => String(value ?? "");
10324
+ function auditMessage(finding2) {
10325
+ const args = finding2.args;
10326
+ switch (finding2.code) {
10327
+ case "document-title":
10328
+ return "The document has no title. A PDF viewer shows the title in its title bar and a screen reader says it first: set pdf.metadata.title, or give the page a <title>.";
10329
+ case "image-alt":
10330
+ return 'The image has no alt attribute. Say what it shows, or write alt="" if it is decoration.';
10331
+ case "figure-name":
10332
+ return `The ${text3(args["tag"])} has no text alternative. Give it an aria-label that says what it shows; the chart helper takes alt.`;
10333
+ case "heading-order":
10334
+ return args["previous"] === 0 ? `The first heading is an h${text3(args["level"])}. Start with an h1 and size it with CSS.` : `This h${text3(args["level"])} follows an h${text3(args["previous"])}, so a heading level is skipped. Make it an h${Number(args["previous"]) + 1} and size it with CSS.`;
10335
+ case "table-rows":
10336
+ return `The rows of this table span different numbers of columns (${text3(args["first"])} and ${text3(args["other"])}). Even them out with colspan.`;
10337
+ case "table-cell-empty":
10338
+ return Number(args["count"]) > 1 ? `${text3(args["count"])} cells of this table are empty. Chromium writes no cell for an empty td, so their rows come out shorter than the others: put a non-breaking space (&nbsp;) or a dash into each.` : "This table cell is empty. Chromium writes no cell for an empty td, so its row comes out shorter than the others: put a non-breaking space (&nbsp;) or a dash into it.";
10339
+ case "masked-text":
10340
+ return "This element has a CSS mask and holds text. Chromium then writes the text in a way PDF/UA-1 does not allow, and no repair reaches it: mask an element without text instead.";
10341
+ case "filtered-text":
10342
+ return "This element has a CSS filter and holds text. The filter turns the text into a picture, which nobody can have read out or select.";
10343
+ case "document-language":
10344
+ return "The document names no language. Set lang on the html element, or the locale in the settings.";
10345
+ case "table-headers":
10346
+ return "The table has no header cells. Use th for the titles of its columns or rows, or a screen reader says the cells without them.";
10347
+ case "link-name":
10348
+ return "The link has no text. A screen reader says its address instead: give it text, an aria-label, or an image with alt.";
10349
+ case "generated-text":
10350
+ return `\u201C${text3(args["text"])}\u201D comes from CSS (::before or ::after). Generated text is not read out: write it into the template.`;
10351
+ case "fixed-text":
10352
+ return "This element is position: fixed, so it is printed on every page and read once per page, one after the other.";
10353
+ case "running-text":
10354
+ return `\u201C${text3(args["text"])}\u201D stands only in the ${text3(args["part"])}. A screen reader skips page headers and footers, so say it in the body too.`;
10355
+ }
10356
+ }
10357
+ var ACCESSIBILITY_MAX_FAILURES = 20;
10358
+ var ACCESSIBILITY_MAX_ELEMENTS = 5;
10359
+ var elementOf = (finding2) => finding2.code === "running-text" || finding2.code === "generated-text" ? text3(finding2.args["text"]) : finding2.snippet;
10360
+ function accessibilityResult(options, verdict, findings) {
10361
+ const failures = verdict.failures.map((failure) => {
10362
+ const about = findings.filter(
10363
+ (finding2) => AUDIT_RULES[finding2.code].includes(failure.rule)
10364
+ );
10365
+ const first = about[0];
10366
+ return {
10367
+ rule: failure.rule,
10368
+ ...first ? { code: first.code } : {},
10369
+ count: failure.checks,
10370
+ message: first ? auditMessage(first) : failure.description.replace(/\s+/g, " ").trim(),
10371
+ elements: [...new Set(about.map(elementOf).filter(Boolean))].slice(
10372
+ 0,
10373
+ ACCESSIBILITY_MAX_ELEMENTS
10374
+ )
10375
+ };
10376
+ });
10377
+ const warnings = [];
10378
+ for (const finding2 of findings) {
10379
+ if (finding2.severity !== "warning") continue;
10380
+ let warning = warnings.find((entry) => entry.code === finding2.code);
10381
+ if (!warning)
10382
+ warnings.push(warning = { code: finding2.code, count: 0, elements: [] });
10383
+ warning.count++;
10384
+ const element = elementOf(finding2);
10385
+ if (element && warning.elements.length < ACCESSIBILITY_MAX_ELEMENTS && !warning.elements.includes(element))
10386
+ warning.elements.push(element);
10387
+ }
10388
+ return {
10389
+ standard: PDFUA_STANDARD,
10390
+ check: options.check,
10391
+ conformant: verdict.compliant,
10392
+ validator: verdict.validator,
10393
+ rules: verdict.rules,
10394
+ failures: failures.slice(0, ACCESSIBILITY_MAX_FAILURES),
10395
+ warnings,
10396
+ ...failures.length > ACCESSIBILITY_MAX_FAILURES ? { truncated: true } : {}
10397
+ };
10398
+ }
10399
+ function accessibilityHeader(result) {
10400
+ return result.conformant ? "conformant" : "not-conformant";
10401
+ }
10402
+ var ACCESSIBILITY_HEADER_LENGTH = 8e3;
10403
+ function accessibilityHeaderValue(result, limit = ACCESSIBILITY_HEADER_LENGTH) {
10404
+ const encode = (report2) => encodeURIComponent(JSON.stringify(report2));
10405
+ const keep = (report2, elements) => ({
10406
+ ...report2,
10407
+ truncated: true,
10408
+ failures: report2.failures.map((failure) => ({
10409
+ ...failure,
10410
+ elements: failure.elements.slice(0, elements)
10411
+ })),
10412
+ warnings: report2.warnings.map((warning) => ({
10413
+ ...warning,
10414
+ elements: warning.elements.slice(0, elements)
10415
+ }))
10416
+ });
10417
+ let value = encode(result);
10418
+ if (value.length <= limit) return value;
10419
+ for (const elements of [1, 0]) {
10420
+ value = encode(keep(result, elements));
10421
+ if (value.length <= limit) return value;
10422
+ }
10423
+ let report = keep(result, 0);
10424
+ while (report.failures.length + report.warnings.length > 0) {
10425
+ report = report.warnings.length ? { ...report, warnings: report.warnings.slice(0, -1) } : { ...report, failures: report.failures.slice(0, -1) };
10426
+ value = encode(report);
10427
+ if (value.length <= limit) return value;
10428
+ }
10429
+ return value;
10430
+ }
10431
+
10432
+ // packages/engine/src/lib/accessibility/static.ts
10433
+ var finding = (code, src, snippet) => ({
10434
+ code,
10435
+ severity: AUDIT_SEVERITY[code],
10436
+ src,
10437
+ snippet,
10438
+ args: {}
10439
+ });
10440
+ var NAMING = /* @__PURE__ */ new Set(["alt", "aria-label", "aria-labelledby", "title"]);
10441
+ function imageIsNamed(attributes, scanner, base) {
10442
+ const n = attributes.length;
10443
+ let i = 0;
10444
+ let named = false;
10445
+ while (i < n) {
10446
+ const ch = attributes[i];
10447
+ if (/[\s/>]/.test(ch)) {
10448
+ i++;
10449
+ continue;
10450
+ }
10451
+ if (ch === "{" && scanner.skipTemplate(base + i) !== null) return null;
10452
+ const start = i;
10453
+ while (i < n && !/[\s=/>]/.test(attributes[i])) i++;
10454
+ const name = attributes.slice(start, i).toLowerCase();
10455
+ while (i < n && /\s/.test(attributes[i])) i++;
10456
+ let value = "";
10457
+ if (attributes[i] === "=") {
10458
+ i++;
10459
+ while (i < n && /\s/.test(attributes[i])) i++;
10460
+ const quote2 = attributes[i];
10461
+ if (quote2 === '"' || quote2 === "'") {
10462
+ const end = scanner.skipTo(base + i + 1, quote2) - base;
10463
+ value = attributes.slice(i + 1, Math.max(i + 1, end - 1));
10464
+ i = end;
10465
+ } else {
10466
+ const from = i;
10467
+ while (i < n && !/[\s>]/.test(attributes[i])) i++;
10468
+ value = attributes.slice(from, i);
10469
+ }
10470
+ }
10471
+ if (NAMING.has(name)) named = true;
10472
+ else if (name === "aria-hidden" && value.trim() !== "false") named = true;
10473
+ else if (name === "role" && /^(presentation|none)$/.test(value.trim()))
10474
+ named = true;
10475
+ }
10476
+ return named;
10477
+ }
10478
+ function staticAuditDiagnostics(input, ua) {
10479
+ return staticAudit(input).map((found) => {
10480
+ const at = /^body:(\d+):(\d+)$/.exec(found.src ?? "");
10481
+ return {
10482
+ severity: found.severity === "error" && ua.check === "strict" ? "error" : "warning",
10483
+ code: `accessibility-${found.code}`,
10484
+ message: auditMessage(found),
10485
+ ...at ? { location: { line: Number(at[1]), column: Number(at[2]) } } : {}
10486
+ };
10487
+ });
10488
+ }
10489
+ function staticAudit(input) {
10490
+ const findings = [];
10491
+ if (!input.title?.trim() && !/<title[\s>]/i.test(input.head ?? ""))
10492
+ findings.push(finding("document-title", null, ""));
10493
+ const source = input.html;
10494
+ const scanner = new Scanner(source, input.engine);
10495
+ const lines = lineStarts(source);
10496
+ const n = source.length;
10497
+ let i = 0;
10498
+ while (i < n) {
10499
+ const ch = source[i];
10500
+ if (ch === "{") {
10501
+ i = scanner.skipTemplate(i) ?? i + 1;
10502
+ continue;
10503
+ }
10504
+ if (ch !== "<") {
10505
+ i++;
10506
+ continue;
10507
+ }
10508
+ if (source.startsWith("<!--", i)) {
10509
+ i = scanner.skipTo(i + 4, "-->");
10510
+ continue;
10511
+ }
10512
+ const next = source[i + 1];
10513
+ if (next === "!" || next === "?") {
10514
+ i = scanner.tagEnd(i + 2);
10515
+ continue;
10516
+ }
10517
+ if (next === "/") {
10518
+ i += 2;
10519
+ continue;
10520
+ }
10521
+ tagName.lastIndex = i + 1;
10522
+ const name = tagName.exec(source)?.[0];
10523
+ if (!name) {
10524
+ i++;
10525
+ continue;
10526
+ }
10527
+ const nameEnd = i + 1 + name.length;
10528
+ const end = scanner.tagEnd(nameEnd);
10529
+ if (name.toLowerCase() === "img") {
10530
+ const named = imageIsNamed(source.slice(nameEnd, end), scanner, nameEnd);
10531
+ if (named === false) {
10532
+ const { line: line2, column } = locate(lines, i);
10533
+ const tag = source.slice(i, end).replace(/\s+/g, " ");
10534
+ findings.push(
10535
+ finding(
10536
+ "image-alt",
10537
+ `body:${line2}:${column}`,
10538
+ tag.length > AUDIT_SNIPPET_LENGTH ? `${tag.slice(0, AUDIT_SNIPPET_LENGTH - 1)}\u2026` : tag
10539
+ )
10540
+ );
10541
+ }
10542
+ }
10543
+ i = end;
10544
+ if (rawTextElements.has(name.toLowerCase()) && source[end - 1] === ">")
10545
+ i = scanner.skipToClosingTag(end, name);
10546
+ }
10547
+ return findings;
10548
+ }
10549
+
10151
10550
  // packages/engine/src/lib/office/errors.ts
10152
10551
  var OfficeError = class extends Error {
10153
10552
  constructor(code, message, details = {}) {
@@ -11467,12 +11866,12 @@ function assertWellFormed(tokens, part) {
11467
11866
  }
11468
11867
  var FORBIDDEN = /[\u0000-\u0008\u000B\u000C\u000E-\u001F\uFFFE\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/;
11469
11868
  var ENTITY = /&(?:#x[0-9A-Fa-f]+|#\d+|amp|lt|gt|quot|apos);/y;
11470
- function checkText(text3, part) {
11471
- if (FORBIDDEN.test(text3)) throw invalid2("a character XML does not allow", part);
11472
- if (text3.includes("<")) throw invalid2("unescaped < in text", part);
11473
- for (let i = text3.indexOf("&"); i !== -1; i = text3.indexOf("&", i + 1)) {
11869
+ function checkText(text4, part) {
11870
+ if (FORBIDDEN.test(text4)) throw invalid2("a character XML does not allow", part);
11871
+ if (text4.includes("<")) throw invalid2("unescaped < in text", part);
11872
+ for (let i = text4.indexOf("&"); i !== -1; i = text4.indexOf("&", i + 1)) {
11474
11873
  ENTITY.lastIndex = i;
11475
- if (!ENTITY.test(text3)) throw invalid2(`invalid entity reference near "${text3.slice(i, i + 12)}"`, part);
11874
+ if (!ENTITY.test(text4)) throw invalid2(`invalid entity reference near "${text4.slice(i, i + 12)}"`, part);
11476
11875
  }
11477
11876
  }
11478
11877
  function checkAttributes(raw, part) {
@@ -11507,13 +11906,13 @@ function decodeText3(raw) {
11507
11906
  }
11508
11907
  });
11509
11908
  }
11510
- function escapeText3(text3) {
11511
- return text3.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
11909
+ function escapeText3(text4) {
11910
+ return text4.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
11512
11911
  }
11513
11912
  var FORBIDDEN_GLOBAL = /[\u0000-\u0008\u000B\u000C\u000E-\u001F\uFFFE\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g;
11514
- function stripForbidden(text3) {
11913
+ function stripForbidden(text4) {
11515
11914
  let removed = 0;
11516
- const out = text3.replace(FORBIDDEN_GLOBAL, () => {
11915
+ const out = text4.replace(FORBIDDEN_GLOBAL, () => {
11517
11916
  removed++;
11518
11917
  return "";
11519
11918
  });
@@ -11764,13 +12163,13 @@ function placeDrawings(xml2, pattern, markupFor) {
11764
12163
  pattern.lastIndex = 0;
11765
12164
  if (pattern.test(t.raw)) {
11766
12165
  const segments = t.raw.split(pattern);
11767
- let text3 = segments[0];
12166
+ let text4 = segments[0];
11768
12167
  for (let s = 1; s < segments.length; s += 2) {
11769
12168
  const index = Number(segments[s]);
11770
- text3 += `</w:t></w:r><w:r>${runProps}${markupFor(index)}</w:r>${runOpen}${runProps}<w:t xml:space="preserve">` + segments[s + 1];
12169
+ text4 += `</w:t></w:r><w:r>${runProps}${markupFor(index)}</w:r>${runOpen}${runProps}<w:t xml:space="preserve">` + segments[s + 1];
11771
12170
  }
11772
12171
  out[textOpen] = withPreserve(out[textOpen]);
11773
- out.push(text3);
12172
+ out.push(text4);
11774
12173
  continue;
11775
12174
  }
11776
12175
  }
@@ -12025,11 +12424,11 @@ function fromTemplateOutput(output, template, part) {
12025
12424
  let last = 0;
12026
12425
  let inText = false;
12027
12426
  const emit = (segment) => {
12028
- const { text: text3, removed } = stripForbidden(segment);
12427
+ const { text: text4, removed } = stripForbidden(segment);
12029
12428
  removedCharacters += removed;
12030
- if (!inText && text3.trim() !== "")
12031
- throw new OfficeError("office_document_invalid", `${part ?? "part"}: text outside a text element: "${text3.trim().slice(0, 40)}"`);
12032
- xml2 += escapeText3(text3);
12429
+ if (!inText && text4.trim() !== "")
12430
+ throw new OfficeError("office_document_invalid", `${part ?? "part"}: text outside a text element: "${text4.trim().slice(0, 40)}"`);
12431
+ xml2 += escapeText3(text4);
12033
12432
  };
12034
12433
  for (const m of output.matchAll(placeholder)) {
12035
12434
  emit(output.slice(last, m.index));
@@ -12071,15 +12470,15 @@ function layoutText(xml2, flavour, part) {
12071
12470
  out.push(token2.raw);
12072
12471
  continue;
12073
12472
  }
12074
- const text3 = token2.raw.replace(/\r\n?/g, "\n");
12473
+ const text4 = token2.raw.replace(/\r\n?/g, "\n");
12075
12474
  if (flavour === "wordprocessing") {
12076
- const pieces = text3.split(/(\n|\t)/);
12077
- if (pieces.length > 1 || /^\s|\s$/.test(text3)) out[textOpenIndex] = preserveSpace(out[textOpenIndex]);
12475
+ const pieces = text4.split(/(\n|\t)/);
12476
+ if (pieces.length > 1 || /^\s|\s$/.test(text4)) out[textOpenIndex] = preserveSpace(out[textOpenIndex]);
12078
12477
  out.push(
12079
12478
  pieces.map((p) => p === "\n" ? '</w:t><w:br/><w:t xml:space="preserve">' : p === " " ? '</w:t><w:tab/><w:t xml:space="preserve">' : p).join("")
12080
12479
  );
12081
12480
  } else {
12082
- out.push(text3.split("\n").join(`</a:t></a:r><a:br>${runProperties}</a:br><a:r>${runProperties}<a:t>`));
12481
+ out.push(text4.split("\n").join(`</a:t></a:r><a:br>${runProperties}</a:br><a:r>${runProperties}<a:t>`));
12083
12482
  }
12084
12483
  }
12085
12484
  return out.join("");
@@ -12112,17 +12511,17 @@ var AUTOCORRECTED = [
12112
12511
  [/\u200B|\u200C|\u200D|\uFEFF/g, ""]
12113
12512
  ];
12114
12513
  function normaliseTags(template, part) {
12115
- const { text: text3, origin, placeholders } = stripPlaceholders(template);
12514
+ const { text: text4, origin, placeholders } = stripPlaceholders(template);
12116
12515
  const sourceRange = (from, to) => [
12117
12516
  origin[from] ?? template.source.length,
12118
12517
  (origin[to - 1] ?? template.source.length - 1) + 1
12119
12518
  ];
12120
12519
  const diagnostics = [];
12121
12520
  const edits = [];
12122
- for (const tag of findTags(text3)) {
12521
+ for (const tag of findTags(text4)) {
12123
12522
  const inside = placeholders.filter((p) => p.at > tag.start && p.at < tag.end);
12124
12523
  const paragraph2 = paragraphOf(template, placeholders, tag.start);
12125
- const raw = text3.slice(tag.start, tag.end);
12524
+ const raw = text4.slice(tag.start, tag.end);
12126
12525
  const unsafe = inside.filter((p) => {
12127
12526
  const piece = template.markup[p.index];
12128
12527
  return !piece || piece.paragraphBoundary || piece.containerBoundary;
@@ -12176,8 +12575,8 @@ function normaliseTags(template, part) {
12176
12575
  return { template: { ...template, source: applyEdits(template.source, edits) }, diagnostics };
12177
12576
  }
12178
12577
  var ENTITIES2 = { amp: "&", lt: "<", gt: ">", quot: '"', apos: "'" };
12179
- function decodeEntities2(text3) {
12180
- return text3.replace(/&(#x[0-9a-f]+|#\d+|amp|lt|gt|quot|apos);/gi, (whole, name) => {
12578
+ function decodeEntities2(text4) {
12579
+ return text4.replace(/&(#x[0-9a-f]+|#\d+|amp|lt|gt|quot|apos);/gi, (whole, name) => {
12181
12580
  if (name[0] !== "#") return ENTITIES2[name.toLowerCase()] ?? whole;
12182
12581
  const code = name[1] === "x" || name[1] === "X" ? parseInt(name.slice(2), 16) : parseInt(name.slice(1), 10);
12183
12582
  return Number.isFinite(code) ? String.fromCodePoint(code) : whole;
@@ -12185,28 +12584,28 @@ function decodeEntities2(text3) {
12185
12584
  }
12186
12585
  function stripPlaceholders(template) {
12187
12586
  const pattern = placeholderPattern(template.nonce);
12188
- let text3 = "";
12587
+ let text4 = "";
12189
12588
  const origin = [];
12190
12589
  const placeholders = [];
12191
12590
  let last = 0;
12192
12591
  for (const m of template.source.matchAll(pattern)) {
12193
12592
  for (let i = last; i < (m.index ?? 0); i++) {
12194
- text3 += template.source[i];
12593
+ text4 += template.source[i];
12195
12594
  origin.push(i);
12196
12595
  }
12197
- placeholders.push({ index: Number(m[1]), at: text3.length, from: m.index ?? 0, to: (m.index ?? 0) + m[0].length });
12596
+ placeholders.push({ index: Number(m[1]), at: text4.length, from: m.index ?? 0, to: (m.index ?? 0) + m[0].length });
12198
12597
  last = (m.index ?? 0) + m[0].length;
12199
12598
  }
12200
12599
  for (let i = last; i < template.source.length; i++) {
12201
- text3 += template.source[i];
12600
+ text4 += template.source[i];
12202
12601
  origin.push(i);
12203
12602
  }
12204
- return { text: text3, origin, placeholders };
12603
+ return { text: text4, origin, placeholders };
12205
12604
  }
12206
12605
  function listTags(template, part) {
12207
- const { text: text3, placeholders } = stripPlaceholders(template);
12208
- return findTags(text3).map((tag) => {
12209
- const raw = text3.slice(tag.start, tag.end);
12606
+ const { text: text4, placeholders } = stripPlaceholders(template);
12607
+ return findTags(text4).map((tag) => {
12608
+ const raw = text4.slice(tag.start, tag.end);
12210
12609
  const kind = raw.startsWith("{#") ? "comment" : raw.startsWith("{%") || /^\{\{~?\s*[#/^]/.test(raw) || /^\{\{~?\s*else\b/.test(raw) ? "block" : "output";
12211
12610
  return { part, paragraph: paragraphOf(template, placeholders, tag.start), text: raw.replace(/\s+/g, " ").trim(), kind };
12212
12611
  });
@@ -12227,15 +12626,15 @@ function undoAutocorrect(tag) {
12227
12626
  for (const [pattern, replacement] of AUTOCORRECTED) out = out.replace(pattern, replacement);
12228
12627
  return out;
12229
12628
  }
12230
- function findTags(text3) {
12629
+ function findTags(text4) {
12231
12630
  const tags = [];
12232
- for (let i = 0; i < text3.length - 1; i++) {
12233
- const opener = OPENERS.find((o) => text3.startsWith(o, i));
12631
+ for (let i = 0; i < text4.length - 1; i++) {
12632
+ const opener = OPENERS.find((o) => text4.startsWith(o, i));
12234
12633
  if (!opener) continue;
12235
12634
  const closer = CLOSERS[opener];
12236
- const end = text3.indexOf(closer, i + opener.length);
12635
+ const end = text4.indexOf(closer, i + opener.length);
12237
12636
  if (end === -1) continue;
12238
- const stop = end + closer.length + (opener === "{{" && text3[end + closer.length] === "}" ? 1 : 0);
12637
+ const stop = end + closer.length + (opener === "{{" && text4[end + closer.length] === "}" ? 1 : 0);
12239
12638
  tags.push({ start: i, end: stop });
12240
12639
  i = stop - 1;
12241
12640
  }
@@ -12265,8 +12664,8 @@ var CONTENT = /* @__PURE__ */ new Set(["w:drawing", "w:pict", "w:object", "w:fld
12265
12664
  var JINJA_OPEN = /^(for|if|unless|case|capture|raw|with|macro|call|filter|block|tablerow|comment|autoescape)\b/;
12266
12665
  var JINJA_CLOSE = /^end(for|if|unless|case|capture|raw|with|macro|call|filter|block|tablerow|comment|autoescape)\b/;
12267
12666
  var JINJA_MIDDLE = /^(else|elif|elsif|when)\b/;
12268
- function blockRole(text3) {
12269
- const t = text3.trim();
12667
+ function blockRole(text4) {
12668
+ const t = text4.trim();
12270
12669
  let m = /^\{%-?\s*([\s\S]*?)\s*-?%\}$/.exec(t);
12271
12670
  if (m && !t.slice(2, -2).includes("%}")) {
12272
12671
  const body = m[1] ?? "";
@@ -12298,14 +12697,14 @@ function dropTaggedFallbacks(xml2, part) {
12298
12697
  if (t.kind === "open" && t.name === "mc:Fallback" && !t.selfClosing) {
12299
12698
  let depth = 1;
12300
12699
  let j = i + 1;
12301
- let text3 = "";
12700
+ let text4 = "";
12302
12701
  for (; j < tokens.length && depth > 0; j++) {
12303
12702
  const u = tokens[j];
12304
12703
  if (u.kind === "open" && u.name === "mc:Fallback" && !u.selfClosing) depth++;
12305
12704
  else if (u.kind === "close" && u.name === "mc:Fallback") depth--;
12306
- else if (u.kind === "text") text3 += decodeText3(u.raw);
12705
+ else if (u.kind === "text") text4 += decodeText3(u.raw);
12307
12706
  }
12308
- if (/\{\{|\{%|\{#/.test(text3)) {
12707
+ if (/\{\{|\{%|\{#/.test(text4)) {
12309
12708
  i = j - 1;
12310
12709
  continue;
12311
12710
  }
@@ -12378,9 +12777,9 @@ function applyStructure(xml2, part) {
12378
12777
  for (const [span, s] of structural) {
12379
12778
  if (rowOf.has(span)) continue;
12380
12779
  const keep = s.info.sectionProperties;
12381
- let text3 = element(s.tag);
12382
- if (keep) text3 = s.role === "open" ? `<w:p>${keep}</w:p>${text3}` : `${text3}<w:p>${keep}</w:p>`;
12383
- replacements.push({ start: span.start, end: span.end, text: text3 });
12780
+ let text4 = element(s.tag);
12781
+ if (keep) text4 = s.role === "open" ? `<w:p>${keep}</w:p>${text4}` : `${text4}<w:p>${keep}</w:p>`;
12782
+ replacements.push({ start: span.start, end: span.end, text: text4 });
12384
12783
  const holder = span.parent && BODIES.has(span.parent.name) ? span.parent : null;
12385
12784
  if (holder && !keep) cellsEmptied.set(holder, (cellsEmptied.get(holder) ?? 0) + 1);
12386
12785
  }
@@ -12434,7 +12833,7 @@ function collectParagraphs(tokens, root) {
12434
12833
  const out = [];
12435
12834
  const walk3 = (span) => {
12436
12835
  if (PARAGRAPH.has(span.name)) {
12437
- let text3 = "";
12836
+ let text4 = "";
12438
12837
  let hasContent = false;
12439
12838
  let sectionProperties = null;
12440
12839
  let inText = false;
@@ -12445,10 +12844,10 @@ function collectParagraphs(tokens, root) {
12445
12844
  else if (CONTENT.has(t.name)) hasContent = true;
12446
12845
  else if (t.name === "w:sectPr") sectionProperties = "";
12447
12846
  } else if (t.kind === "close" && TEXT.has(t.name)) inText = false;
12448
- else if (t.kind === "text" && inText) text3 += decodeText3(t.raw);
12847
+ else if (t.kind === "text" && inText) text4 += decodeText3(t.raw);
12449
12848
  }
12450
12849
  if (sectionProperties !== null) sectionProperties = propertiesOf(tokens, span);
12451
- out.push({ span, number: out.length + 1, text: text3, hasContent, sectionProperties });
12850
+ out.push({ span, number: out.length + 1, text: text4, hasContent, sectionProperties });
12452
12851
  }
12453
12852
  for (const child2 of span.children) walk3(child2);
12454
12853
  };
@@ -12556,7 +12955,7 @@ function placeSlidePictures(xml2, pattern, drawingFor, pictureMarkup, slide) {
12556
12955
  for (const container of containers) {
12557
12956
  const indexes = [];
12558
12957
  const edits = [];
12559
- let text3 = "";
12958
+ let text4 = "";
12560
12959
  let inText = false;
12561
12960
  for (let i = container.start + 1; i < container.end; i++) {
12562
12961
  const t = tokens[i];
@@ -12565,7 +12964,7 @@ function placeSlidePictures(xml2, pattern, drawingFor, pictureMarkup, slide) {
12565
12964
  else if (t.kind === "text" && inText) {
12566
12965
  pattern.lastIndex = 0;
12567
12966
  if (!pattern.test(t.raw)) {
12568
- text3 += decodeText3(t.raw);
12967
+ text4 += decodeText3(t.raw);
12569
12968
  continue;
12570
12969
  }
12571
12970
  const segments = t.raw.split(pattern);
@@ -12574,7 +12973,7 @@ function placeSlidePictures(xml2, pattern, drawingFor, pictureMarkup, slide) {
12574
12973
  if (s % 2 === 1) indexes.push(Number(segments[s]));
12575
12974
  else kept += segments[s];
12576
12975
  }
12577
- text3 += decodeText3(kept);
12976
+ text4 += decodeText3(kept);
12578
12977
  edits.push({ at: i, raw: kept });
12579
12978
  }
12580
12979
  }
@@ -12593,7 +12992,7 @@ function placeSlidePictures(xml2, pattern, drawingFor, pictureMarkup, slide) {
12593
12992
  }
12594
12993
  const autoFit = tokens.slice(container.start, container.end).some((t) => t.kind === "open" && t.name === "a:spAutoFit");
12595
12994
  const placed = layoutPictures(pictures, box, autoFit).map((b, i) => pictureMarkup(pictures[i], b)).join("");
12596
- const onlyPictures = container.name === "p:sp" && text3.trim() === "";
12995
+ const onlyPictures = container.name === "p:sp" && text4.trim() === "";
12597
12996
  if (onlyPictures) replacements.push({ start: container.start, end: container.end, text: placed });
12598
12997
  else {
12599
12998
  for (const edit of edits) replacements.push({ start: edit.at, end: edit.at, text: edit.raw });
@@ -12866,8 +13265,8 @@ function starterTags(engine) {
12866
13265
  codeFit: engine === "liquid" ? "{{ offer.url | qrcode }}" : "{{ qrcode(offer.url) }}"
12867
13266
  };
12868
13267
  }
12869
- var paragraph = (text3, style2, bold = false) => `<w:p>${style2 ? `<w:pPr><w:pStyle w:val="${style2}"/></w:pPr>` : ""}<w:r>${bold ? "<w:rPr><w:b/></w:rPr>" : ""}<w:t xml:space="preserve">${escapeText3(text3)}</w:t></w:r></w:p>`;
12870
- var cell = (text3, width, bold = false) => `<w:tc><w:tcPr><w:tcW w:w="${width}" w:type="dxa"/></w:tcPr>${paragraph(text3, void 0, bold)}</w:tc>`;
13268
+ var paragraph = (text4, style2, bold = false) => `<w:p>${style2 ? `<w:pPr><w:pStyle w:val="${style2}"/></w:pPr>` : ""}<w:r>${bold ? "<w:rPr><w:b/></w:rPr>" : ""}<w:t xml:space="preserve">${escapeText3(text4)}</w:t></w:r></w:p>`;
13269
+ var cell = (text4, width, bold = false) => `<w:tc><w:tcPr><w:tcW w:w="${width}" w:type="dxa"/></w:tcPr>${paragraph(text4, void 0, bold)}</w:tc>`;
12871
13270
  var row = (cells) => `<w:tr>${cells}</w:tr>`;
12872
13271
  function starterDocument(engine) {
12873
13272
  const t = starterTags(engine);
@@ -12910,7 +13309,7 @@ function starterDocument(engine) {
12910
13309
  ["word/document.xml", `<?xml version="1.0" encoding="UTF-8" standalone="yes"?><w:document xmlns:w="${W}"><w:body>${body}</w:body></w:document>`]
12911
13310
  ];
12912
13311
  return {
12913
- bytes: writeZip(files.map(([name, text3]) => ({ name, data: encoder2.encode(text3) }))),
13312
+ bytes: writeZip(files.map(([name, text4]) => ({ name, data: encoder2.encode(text4) }))),
12914
13313
  sampleData: starterSampleData()
12915
13314
  };
12916
13315
  }
@@ -12938,15 +13337,15 @@ var CM = 36e4;
12938
13337
  var FONT = "Carlito";
12939
13338
  var ACCENT = "0F766E";
12940
13339
  var emptyTree = '<p:nvGrpSpPr><p:cNvPr id="1" name=""/><p:cNvGrpSpPr/><p:nvPr/></p:nvGrpSpPr><p:grpSpPr/>';
12941
- var run = (text3, size, options = {}) => `<a:r><a:rPr lang="en-GB" sz="${size * 100}"${options.bold ? ' b="1"' : ""} dirty="0">${options.color ? `<a:solidFill><a:srgbClr val="${options.color}"/></a:solidFill>` : ""}<a:latin typeface="${FONT}"/></a:rPr><a:t>${escapeText3(text3)}</a:t></a:r>`;
12942
- var para = (text3, size, options = {}) => `<a:p>${run(text3, size, options)}</a:p>`;
13340
+ var run = (text4, size, options = {}) => `<a:r><a:rPr lang="en-GB" sz="${size * 100}"${options.bold ? ' b="1"' : ""} dirty="0">${options.color ? `<a:solidFill><a:srgbClr val="${options.color}"/></a:solidFill>` : ""}<a:latin typeface="${FONT}"/></a:rPr><a:t>${escapeText3(text4)}</a:t></a:r>`;
13341
+ var para = (text4, size, options = {}) => `<a:p>${run(text4, size, options)}</a:p>`;
12943
13342
  function textBox(id, name, x, y, cx, cy, paragraphs) {
12944
13343
  return `<p:sp><p:nvSpPr><p:cNvPr id="${id}" name="${name}"/><p:cNvSpPr txBox="1"/><p:nvPr/></p:nvSpPr><p:spPr><a:xfrm><a:off x="${x}" y="${y}"/><a:ext cx="${cx}" cy="${cy}"/></a:xfrm><a:prstGeom prst="rect"><a:avLst/></a:prstGeom><a:noFill/></p:spPr><p:txBody><a:bodyPr wrap="square" rtlCol="0"><a:noAutofit/></a:bodyPr><a:lstStyle/>${paragraphs}</p:txBody></p:sp>`;
12945
13344
  }
12946
- function cell2(text3, header) {
13345
+ function cell2(text4, header) {
12947
13346
  const fill = header ? `<a:solidFill><a:srgbClr val="${ACCENT}"/></a:solidFill>` : "<a:noFill/>";
12948
13347
  const line2 = `<a:lnB w="6350"><a:solidFill><a:srgbClr val="BFBFBF"/></a:solidFill></a:lnB>`;
12949
- return `<a:tc><a:txBody><a:bodyPr/><a:lstStyle/>${para(text3, 14, header ? { bold: true, color: "FFFFFF" } : {})}</a:txBody><a:tcPr marL="91440" marR="91440" marT="45720" marB="45720">${line2}${fill}</a:tcPr></a:tc>`;
13348
+ return `<a:tc><a:txBody><a:bodyPr/><a:lstStyle/>${para(text4, 14, header ? { bold: true, color: "FFFFFF" } : {})}</a:txBody><a:tcPr marL="91440" marR="91440" marT="45720" marB="45720">${line2}${fill}</a:tcPr></a:tc>`;
12950
13349
  }
12951
13350
  var tableRow = (a, b, header = false) => `<a:tr h="${Math.round(0.9 * CM)}">${cell2(a, header)}${cell2(b, header)}</a:tr>`;
12952
13351
  function table2(id, x, y, rows) {
@@ -13036,11 +13435,19 @@ function starterPresentation(engine) {
13036
13435
  ];
13037
13436
  const encoder2 = new TextEncoder();
13038
13437
  return {
13039
- bytes: writeZip(files.map(([name, text3]) => ({ name, data: encoder2.encode(text3) }))),
13438
+ bytes: writeZip(files.map(([name, text4]) => ({ name, data: encoder2.encode(text4) }))),
13040
13439
  sampleData: starterSampleData()
13041
13440
  };
13042
13441
  }
13043
13442
  export {
13443
+ ACCESSIBILITY_HEADER_LENGTH,
13444
+ ACCESSIBILITY_MAX_ELEMENTS,
13445
+ ACCESSIBILITY_MAX_FAILURES,
13446
+ AUDIT_CODES,
13447
+ AUDIT_RULES,
13448
+ AUDIT_SEVERITY,
13449
+ AUDIT_SNIPPET_LENGTH,
13450
+ AUDIT_TITLES,
13044
13451
  CII_GUIDELINES,
13045
13452
  CODE_LIST_RELEASE,
13046
13453
  DEFAULT_CHROME_PADDING,
@@ -13060,9 +13467,15 @@ export {
13060
13467
  INVOICE_DATA_KEY,
13061
13468
  OFFICE_FILLED_MAX_BYTES,
13062
13469
  OfficeTemplateError,
13470
+ PDFUA_STANDARD,
13063
13471
  RenderError,
13064
13472
  RenderLimitError,
13473
+ UA_CHECKS,
13474
+ UA_REFUSALS,
13065
13475
  VAT_CATEGORIES,
13476
+ accessibilityHeader,
13477
+ accessibilityHeaderValue,
13478
+ accessibilityResult,
13066
13479
  analyzeHandlebars,
13067
13480
  analyzeJinja2,
13068
13481
  analyzeLiquid,
@@ -13071,6 +13484,8 @@ export {
13071
13484
  apitemplateRegions,
13072
13485
  applyRegionEdit,
13073
13486
  assembleDocument,
13487
+ auditMessage,
13488
+ auditRuntime,
13074
13489
  brandCss,
13075
13490
  builtinHelpers,
13076
13491
  canvasDocument,
@@ -13143,6 +13558,7 @@ export {
13143
13558
  isOfficeKind,
13144
13559
  isOutputFormat,
13145
13560
  isScss,
13561
+ isUaSetting,
13146
13562
  jinja2Engine,
13147
13563
  jinjaTags,
13148
13564
  jsreportTemplates,
@@ -13184,6 +13600,7 @@ export {
13184
13600
  resolveEinvoiceOptions,
13185
13601
  resolvePath,
13186
13602
  resolveTranslation,
13603
+ resolveUa,
13187
13604
  scanBlocks,
13188
13605
  schemaPaths,
13189
13606
  shownAmounts,
@@ -13195,6 +13612,8 @@ export {
13195
13612
  splitApitemplateCss,
13196
13613
  starterDocument,
13197
13614
  starterPresentation,
13615
+ staticAudit,
13616
+ staticAuditDiagnostics,
13198
13617
  strftimeToDateFns,
13199
13618
  svgDataUri,
13200
13619
  toDate,