@formfeed/engine 0.3.20 → 0.3.22

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.cjs CHANGED
@@ -47,10 +47,12 @@ __export(index_exports, {
47
47
  DefaultHelperRegistry: () => DefaultHelperRegistry,
48
48
  EINVOICE_FLAVOURS: () => EINVOICE_FLAVOURS,
49
49
  EINVOICE_FLAVOUR_NAMES: () => EINVOICE_FLAVOUR_NAMES,
50
+ EINVOICE_HEADER_LENGTH: () => EINVOICE_HEADER_LENGTH,
50
51
  EINVOICE_MAX_MESSAGES: () => EINVOICE_MAX_MESSAGES,
51
52
  EINVOICE_PROFILES: () => EINVOICE_PROFILES,
52
53
  EINVOICE_PROFILE_NAMES: () => EINVOICE_PROFILE_NAMES,
53
54
  EINVOICE_SPEC_VERSIONS: () => EINVOICE_SPEC_VERSIONS,
55
+ EINVOICE_TEST_NOTE: () => EINVOICE_TEST_NOTE,
54
56
  EINVOICE_XML_NAME: () => EINVOICE_XML_NAME,
55
57
  EN16931_TERMS: () => EN16931_TERMS,
56
58
  EPC_MAX_BYTES: () => EPC_MAX_BYTES,
@@ -58,6 +60,12 @@ __export(index_exports, {
58
60
  INVOICE_DATA_KEY: () => INVOICE_DATA_KEY,
59
61
  OFFICE_FILLED_MAX_BYTES: () => OFFICE_FILLED_MAX_BYTES,
60
62
  OfficeTemplateError: () => OfficeTemplateError,
63
+ PDFA_DEFAULT_LEVEL: () => PDFA_DEFAULT_LEVEL,
64
+ PDFA_HEADER_LENGTH: () => PDFA_HEADER_LENGTH,
65
+ PDFA_LEVELS: () => PDFA_LEVELS,
66
+ PDFA_MAX_MESSAGES: () => PDFA_MAX_MESSAGES,
67
+ PDFA_REFUSALS: () => PDFA_REFUSALS,
68
+ PDFA_STANDARDS: () => PDFA_STANDARDS,
61
69
  PDFUA_STANDARD: () => PDFUA_STANDARD,
62
70
  RenderError: () => RenderError,
63
71
  RenderLimitError: () => RenderLimitError,
@@ -106,6 +114,7 @@ __export(index_exports, {
106
114
  editAttribute: () => editAttribute,
107
115
  editableRegion: () => editableRegion,
108
116
  editableRegionAt: () => editableRegionAt,
117
+ einvoiceHeaderValue: () => einvoiceHeaderValue,
109
118
  einvoiceOptionsSchema: () => einvoiceOptionsSchema,
110
119
  einvoiceResult: () => einvoiceResult,
111
120
  einvoiceXml: () => einvoiceXml,
@@ -149,6 +158,8 @@ __export(index_exports, {
149
158
  isApitemplateRegion: () => isApitemplateRegion,
150
159
  isOfficeKind: () => isOfficeKind,
151
160
  isOutputFormat: () => isOutputFormat,
161
+ isPdfaResult: () => isPdfaResult,
162
+ isPdfaSetting: () => isPdfaSetting,
152
163
  isScss: () => isScss,
153
164
  isUaSetting: () => isUaSetting,
154
165
  jinja2Engine: () => jinja2Engine,
@@ -181,6 +192,10 @@ __export(index_exports, {
181
192
  parseDesign: () => parseDesign,
182
193
  parseSourceAttribute: () => parseSourceAttribute,
183
194
  partialInclude: () => partialInclude,
195
+ pdfaFromEinvoice: () => pdfaFromEinvoice,
196
+ pdfaHeaderValue: () => pdfaHeaderValue,
197
+ pdfaResult: () => pdfaResult,
198
+ pdfaStandard: () => pdfaStandard,
184
199
  printReset: () => printReset,
185
200
  qrSvg: () => qrSvg,
186
201
  readJsreportExport: () => readJsreportExport,
@@ -191,6 +206,7 @@ __export(index_exports, {
191
206
  renderVersion: () => renderVersion,
192
207
  resolveEinvoiceOptions: () => resolveEinvoiceOptions,
193
208
  resolvePath: () => resolvePath,
209
+ resolvePdfa: () => resolvePdfa,
194
210
  resolveTranslation: () => resolveTranslation,
195
211
  resolveUa: () => resolveUa,
196
212
  scanBlocks: () => scanBlocks,
@@ -321,7 +337,7 @@ function toNumber(value) {
321
337
  // packages/engine/src/lib/codes/epc.ts
322
338
  function epcPayload(input) {
323
339
  const amount2 = input.amount === void 0 || input.amount === "" ? "" : `EUR${toNumber(input.amount).toFixed(2)}`;
324
- return [
340
+ const elements = [
325
341
  "BCD",
326
342
  "002",
327
343
  "1",
@@ -333,8 +349,12 @@ function epcPayload(input) {
333
349
  (input.purpose ?? "").slice(0, 4),
334
350
  (input.reference ?? "").slice(0, 35),
335
351
  input.reference ? "" : (input.text ?? "").slice(0, 140),
352
+ // the beneficiary to originator information, which a payment here has no field for
336
353
  ""
337
- ].join("\n");
354
+ ];
355
+ while (elements.length > 0 && elements[elements.length - 1] === "")
356
+ elements.pop();
357
+ return elements.join("\n");
338
358
  }
339
359
  var IBAN_LENGTHS = {
340
360
  AD: 24,
@@ -391,34 +411,73 @@ function validateEpc(input) {
391
411
  const problems = [];
392
412
  const name = String(input.name ?? "").trim();
393
413
  if (!name) problems.push({ field: "name", code: "required" });
394
- else if (name.length > 70) problems.push({ field: "name", code: "too_long", max: 70, actual: name.length });
414
+ else if (name.length > 70)
415
+ problems.push({
416
+ field: "name",
417
+ code: "too_long",
418
+ max: 70,
419
+ actual: name.length
420
+ });
395
421
  const iban2 = normaliseIban(String(input.iban ?? ""));
396
422
  if (!iban2) problems.push({ field: "iban", code: "required" });
397
- else if (!/^[A-Z]{2}[0-9]{2}[A-Z0-9]{11,30}$/.test(iban2)) problems.push({ field: "iban", code: "iban_characters" });
423
+ else if (!/^[A-Z]{2}[0-9]{2}[A-Z0-9]{11,30}$/.test(iban2))
424
+ problems.push({ field: "iban", code: "iban_characters" });
398
425
  else {
399
426
  const expected = IBAN_LENGTHS[iban2.slice(0, 2)];
400
427
  if (expected !== void 0 && iban2.length !== expected)
401
- problems.push({ field: "iban", code: "iban_country_length", max: expected, actual: iban2.length });
402
- else if (!ibanChecksum(iban2)) problems.push({ field: "iban", code: "iban_checksum" });
428
+ problems.push({
429
+ field: "iban",
430
+ code: "iban_country_length",
431
+ max: expected,
432
+ actual: iban2.length
433
+ });
434
+ else if (!ibanChecksum(iban2))
435
+ problems.push({ field: "iban", code: "iban_checksum" });
403
436
  }
404
437
  const bic = String(input.bic ?? "").replace(/\s/g, "").toUpperCase();
405
- if (bic && !/^[A-Z]{6}[A-Z0-9]{2}([A-Z0-9]{3})?$/.test(bic)) problems.push({ field: "bic", code: "bic_format" });
438
+ if (bic && !/^[A-Z]{6}[A-Z0-9]{2}([A-Z0-9]{3})?$/.test(bic))
439
+ problems.push({ field: "bic", code: "bic_format" });
406
440
  if (input.amount !== void 0 && input.amount !== "") {
407
441
  const amount2 = toNumber(input.amount);
408
- if (Number.isNaN(amount2)) problems.push({ field: "amount", code: "not_a_number" });
409
- else if (amount2 < 0.01 || amount2 > 99999999999e-2) problems.push({ field: "amount", code: "out_of_range" });
442
+ if (Number.isNaN(amount2))
443
+ problems.push({ field: "amount", code: "not_a_number" });
444
+ else if (amount2 < 0.01 || amount2 > 99999999999e-2)
445
+ problems.push({ field: "amount", code: "out_of_range" });
410
446
  }
411
447
  const purpose = String(input.purpose ?? "");
412
- if (purpose.length > 4) problems.push({ field: "purpose", code: "too_long", max: 4, actual: purpose.length });
448
+ if (purpose.length > 4)
449
+ problems.push({
450
+ field: "purpose",
451
+ code: "too_long",
452
+ max: 4,
453
+ actual: purpose.length
454
+ });
413
455
  const reference = String(input.reference ?? "").trim();
414
456
  const text4 = String(input.text ?? "").trim();
415
- if (reference && text4) problems.push({ field: "reference", code: "reference_or_text" });
457
+ if (reference && text4)
458
+ problems.push({ field: "reference", code: "reference_or_text" });
416
459
  if (reference.length > 35)
417
- problems.push({ field: "reference", code: "too_long", max: 35, actual: reference.length });
418
- if (text4.length > 140) problems.push({ field: "text", code: "too_long", max: 140, actual: text4.length });
460
+ problems.push({
461
+ field: "reference",
462
+ code: "too_long",
463
+ max: 35,
464
+ actual: reference.length
465
+ });
466
+ if (text4.length > 140)
467
+ problems.push({
468
+ field: "text",
469
+ code: "too_long",
470
+ max: 140,
471
+ actual: text4.length
472
+ });
419
473
  const bytes = byteLength(epcPayload(input));
420
474
  if (bytes > EPC_MAX_BYTES)
421
- problems.push({ field: "payload", code: "too_long", max: EPC_MAX_BYTES, actual: bytes });
475
+ problems.push({
476
+ field: "payload",
477
+ code: "too_long",
478
+ max: EPC_MAX_BYTES,
479
+ actual: bytes
480
+ });
422
481
  return problems;
423
482
  }
424
483
 
@@ -9785,6 +9844,7 @@ var CII_GUIDELINES = {
9785
9844
  en16931: "urn:cen.eu:en16931:2017"
9786
9845
  };
9787
9846
  var EINVOICE_XML_NAME = "factur-x.xml";
9847
+ var EINVOICE_TEST_NOTE = "TEST \u2013 keine Rechnung / test render, not an invoice";
9788
9848
  function leaf(name, text4, attributes) {
9789
9849
  if (text4 === void 0 || text4 === "") return void 0;
9790
9850
  const kept = Object.entries(attributes ?? {}).filter(
@@ -10030,6 +10090,7 @@ function invoiceToCii(invoice, options) {
10030
10090
  leaf("ram:ID", invoice.number),
10031
10091
  leaf("ram:TypeCode", invoice.type_code),
10032
10092
  date("ram:IssueDateTime", invoice.issue_date),
10093
+ options.test ? group2("ram:IncludedNote", [leaf("ram:Content", EINVOICE_TEST_NOTE)]) : void 0,
10033
10094
  ...invoice.notes.map(
10034
10095
  (note2) => group2("ram:IncludedNote", [
10035
10096
  leaf("ram:Content", note2.text),
@@ -10943,6 +11004,33 @@ function einvoiceResult(options, specVersion, validation, display) {
10943
11004
  display: display ? { checked: display.checked, missing: display.missing } : null
10944
11005
  };
10945
11006
  }
11007
+ var EINVOICE_HEADER_LENGTH = 8e3;
11008
+ function einvoiceHeaderValue(result, limit = EINVOICE_HEADER_LENGTH) {
11009
+ const encode = (report) => encodeURIComponent(JSON.stringify(report));
11010
+ const full = encode(result);
11011
+ const validation = result.validation;
11012
+ if (full.length <= limit || !validation) return full;
11013
+ let messages = validation.messages.map((message) => ({
11014
+ ...message,
11015
+ location: null
11016
+ }));
11017
+ const cut = () => ({
11018
+ ...result,
11019
+ validation: { ...validation, messages, truncated: true }
11020
+ });
11021
+ let value = encode(cut());
11022
+ while (value.length > limit && messages.length > 0) {
11023
+ let last = messages.length - 1;
11024
+ for (let i = messages.length - 1; i >= 0; i--)
11025
+ if (messages[i].severity !== "error") {
11026
+ last = i;
11027
+ break;
11028
+ }
11029
+ messages = messages.filter((_, i) => i !== last);
11030
+ value = encode(cut());
11031
+ }
11032
+ return value;
11033
+ }
10946
11034
  var EINVOICE_FLAVOUR_NAMES = {
10947
11035
  "factur-x": "Factur-X",
10948
11036
  zugferd: "ZUGFeRD"
@@ -11146,7 +11234,7 @@ function einvoiceXml(data, options) {
11146
11234
  }
11147
11235
  ]
11148
11236
  };
11149
- const check = checkInvoice(block, options);
11237
+ const check = checkInvoice(block, { profile: options.profile });
11150
11238
  if (!check.ok) return { ok: false, problems: check.problems };
11151
11239
  return {
11152
11240
  ok: true,
@@ -11469,6 +11557,89 @@ function staticAudit(input) {
11469
11557
  return findings;
11470
11558
  }
11471
11559
 
11560
+ // packages/engine/src/lib/pdfa/index.ts
11561
+ var PDFA_LEVELS = ["2b", "3b"];
11562
+ var PDFA_DEFAULT_LEVEL = "3b";
11563
+ var PDFA_STANDARDS = {
11564
+ "2b": "PDF/A-2b",
11565
+ "3b": "PDF/A-3b"
11566
+ };
11567
+ function resolvePdfa(setting) {
11568
+ if (setting === true) return PDFA_DEFAULT_LEVEL;
11569
+ return typeof setting === "string" && PDFA_LEVELS.includes(setting) ? setting : null;
11570
+ }
11571
+ function isPdfaSetting(value) {
11572
+ return value === null || typeof value === "boolean" || typeof value === "string" && PDFA_LEVELS.includes(value);
11573
+ }
11574
+ function pdfaStandard(level) {
11575
+ return PDFA_STANDARDS[level];
11576
+ }
11577
+ var PDFA_REFUSALS = {
11578
+ password: "pdf.pdfa cannot be combined with post.password: PDF/A does not allow encryption",
11579
+ einvoice: 'pdf.pdfa "2b" cannot be combined with an e-invoice: the invoice XML is an attachment, which PDF/A-2 does not allow. An e-invoice is a PDF/A-3b already; ask for "3b" or leave pdf.pdfa out',
11580
+ invalid: 'settings.pdf.pdfa is "2b", "3b", true, false or null'
11581
+ };
11582
+ var PDFA_MAX_MESSAGES = 50;
11583
+ var asMessage = (message) => ({
11584
+ severity: message.severity === "warning" ? "warning" : "error",
11585
+ rule: typeof message.rule === "string" ? message.rule : null,
11586
+ message: typeof message.message === "string" ? message.message : "",
11587
+ location: typeof message.location === "string" ? message.location : null
11588
+ });
11589
+ var capped = (messages, truncated) => {
11590
+ const sorted = [
11591
+ ...messages.filter((message) => message.severity === "error"),
11592
+ ...messages.filter((message) => message.severity !== "error")
11593
+ ];
11594
+ return sorted.length > PDFA_MAX_MESSAGES || truncated ? { messages: sorted.slice(0, PDFA_MAX_MESSAGES), truncated: true } : { messages: sorted };
11595
+ };
11596
+ function pdfaResult(level, report, pdfSha256) {
11597
+ return {
11598
+ standard: PDFA_STANDARDS[level],
11599
+ conformant: report.valid === true,
11600
+ validator: report.validator,
11601
+ id: report.id,
11602
+ versions: { ...report.versions ?? {} },
11603
+ pdf_sha256: pdfSha256,
11604
+ ...capped(
11605
+ (Array.isArray(report.messages) ? report.messages : []).map(asMessage),
11606
+ report.truncated === true
11607
+ )
11608
+ };
11609
+ }
11610
+ function pdfaFromEinvoice(validation) {
11611
+ if (!validation || validation.pdfa == null) return null;
11612
+ const messages = validation.messages.filter((message) => message.part === "pdf").map((message) => ({
11613
+ severity: message.severity,
11614
+ rule: message.rule,
11615
+ message: message.message,
11616
+ location: message.location
11617
+ }));
11618
+ const versions = { ...validation.versions ?? {} };
11619
+ return {
11620
+ standard: PDFA_STANDARDS["3b"],
11621
+ conformant: !messages.some((message) => message.severity === "error"),
11622
+ validator: `veraPDF ${versions["verapdf"] ?? "unknown"}`,
11623
+ id: validation.id ?? "",
11624
+ versions,
11625
+ pdf_sha256: validation.pdf_sha256 ?? null,
11626
+ ...capped(messages, false)
11627
+ };
11628
+ }
11629
+ function isPdfaResult(value) {
11630
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
11631
+ const report = value;
11632
+ return typeof report.standard === "string" && Object.values(PDFA_STANDARDS).includes(report.standard) && typeof report.conformant === "boolean";
11633
+ }
11634
+ var PDFA_HEADER_LENGTH = 2e3;
11635
+ function pdfaHeaderValue(result, limit = PDFA_HEADER_LENGTH) {
11636
+ const value = encodeURIComponent(JSON.stringify(result));
11637
+ if (value.length <= limit) return value;
11638
+ return encodeURIComponent(
11639
+ JSON.stringify({ ...result, messages: [], truncated: true })
11640
+ );
11641
+ }
11642
+
11472
11643
  // packages/engine/src/lib/office/errors.ts
11473
11644
  var OfficeError = class extends Error {
11474
11645
  constructor(code, message, details = {}) {
package/index.js CHANGED
@@ -101,7 +101,7 @@ function toNumber(value) {
101
101
  // packages/engine/src/lib/codes/epc.ts
102
102
  function epcPayload(input) {
103
103
  const amount2 = input.amount === void 0 || input.amount === "" ? "" : `EUR${toNumber(input.amount).toFixed(2)}`;
104
- return [
104
+ const elements = [
105
105
  "BCD",
106
106
  "002",
107
107
  "1",
@@ -113,8 +113,12 @@ function epcPayload(input) {
113
113
  (input.purpose ?? "").slice(0, 4),
114
114
  (input.reference ?? "").slice(0, 35),
115
115
  input.reference ? "" : (input.text ?? "").slice(0, 140),
116
+ // the beneficiary to originator information, which a payment here has no field for
116
117
  ""
117
- ].join("\n");
118
+ ];
119
+ while (elements.length > 0 && elements[elements.length - 1] === "")
120
+ elements.pop();
121
+ return elements.join("\n");
118
122
  }
119
123
  var IBAN_LENGTHS = {
120
124
  AD: 24,
@@ -171,34 +175,73 @@ function validateEpc(input) {
171
175
  const problems = [];
172
176
  const name = String(input.name ?? "").trim();
173
177
  if (!name) problems.push({ field: "name", code: "required" });
174
- else if (name.length > 70) problems.push({ field: "name", code: "too_long", max: 70, actual: name.length });
178
+ else if (name.length > 70)
179
+ problems.push({
180
+ field: "name",
181
+ code: "too_long",
182
+ max: 70,
183
+ actual: name.length
184
+ });
175
185
  const iban2 = normaliseIban(String(input.iban ?? ""));
176
186
  if (!iban2) problems.push({ field: "iban", code: "required" });
177
- else if (!/^[A-Z]{2}[0-9]{2}[A-Z0-9]{11,30}$/.test(iban2)) problems.push({ field: "iban", code: "iban_characters" });
187
+ else if (!/^[A-Z]{2}[0-9]{2}[A-Z0-9]{11,30}$/.test(iban2))
188
+ problems.push({ field: "iban", code: "iban_characters" });
178
189
  else {
179
190
  const expected = IBAN_LENGTHS[iban2.slice(0, 2)];
180
191
  if (expected !== void 0 && iban2.length !== expected)
181
- problems.push({ field: "iban", code: "iban_country_length", max: expected, actual: iban2.length });
182
- else if (!ibanChecksum(iban2)) problems.push({ field: "iban", code: "iban_checksum" });
192
+ problems.push({
193
+ field: "iban",
194
+ code: "iban_country_length",
195
+ max: expected,
196
+ actual: iban2.length
197
+ });
198
+ else if (!ibanChecksum(iban2))
199
+ problems.push({ field: "iban", code: "iban_checksum" });
183
200
  }
184
201
  const bic = String(input.bic ?? "").replace(/\s/g, "").toUpperCase();
185
- if (bic && !/^[A-Z]{6}[A-Z0-9]{2}([A-Z0-9]{3})?$/.test(bic)) problems.push({ field: "bic", code: "bic_format" });
202
+ if (bic && !/^[A-Z]{6}[A-Z0-9]{2}([A-Z0-9]{3})?$/.test(bic))
203
+ problems.push({ field: "bic", code: "bic_format" });
186
204
  if (input.amount !== void 0 && input.amount !== "") {
187
205
  const amount2 = toNumber(input.amount);
188
- if (Number.isNaN(amount2)) problems.push({ field: "amount", code: "not_a_number" });
189
- else if (amount2 < 0.01 || amount2 > 99999999999e-2) problems.push({ field: "amount", code: "out_of_range" });
206
+ if (Number.isNaN(amount2))
207
+ problems.push({ field: "amount", code: "not_a_number" });
208
+ else if (amount2 < 0.01 || amount2 > 99999999999e-2)
209
+ problems.push({ field: "amount", code: "out_of_range" });
190
210
  }
191
211
  const purpose = String(input.purpose ?? "");
192
- if (purpose.length > 4) problems.push({ field: "purpose", code: "too_long", max: 4, actual: purpose.length });
212
+ if (purpose.length > 4)
213
+ problems.push({
214
+ field: "purpose",
215
+ code: "too_long",
216
+ max: 4,
217
+ actual: purpose.length
218
+ });
193
219
  const reference = String(input.reference ?? "").trim();
194
220
  const text4 = String(input.text ?? "").trim();
195
- if (reference && text4) problems.push({ field: "reference", code: "reference_or_text" });
221
+ if (reference && text4)
222
+ problems.push({ field: "reference", code: "reference_or_text" });
196
223
  if (reference.length > 35)
197
- problems.push({ field: "reference", code: "too_long", max: 35, actual: reference.length });
198
- if (text4.length > 140) problems.push({ field: "text", code: "too_long", max: 140, actual: text4.length });
224
+ problems.push({
225
+ field: "reference",
226
+ code: "too_long",
227
+ max: 35,
228
+ actual: reference.length
229
+ });
230
+ if (text4.length > 140)
231
+ problems.push({
232
+ field: "text",
233
+ code: "too_long",
234
+ max: 140,
235
+ actual: text4.length
236
+ });
199
237
  const bytes = byteLength(epcPayload(input));
200
238
  if (bytes > EPC_MAX_BYTES)
201
- problems.push({ field: "payload", code: "too_long", max: EPC_MAX_BYTES, actual: bytes });
239
+ problems.push({
240
+ field: "payload",
241
+ code: "too_long",
242
+ max: EPC_MAX_BYTES,
243
+ actual: bytes
244
+ });
202
245
  return problems;
203
246
  }
204
247
 
@@ -9565,6 +9608,7 @@ var CII_GUIDELINES = {
9565
9608
  en16931: "urn:cen.eu:en16931:2017"
9566
9609
  };
9567
9610
  var EINVOICE_XML_NAME = "factur-x.xml";
9611
+ var EINVOICE_TEST_NOTE = "TEST \u2013 keine Rechnung / test render, not an invoice";
9568
9612
  function leaf(name, text4, attributes) {
9569
9613
  if (text4 === void 0 || text4 === "") return void 0;
9570
9614
  const kept = Object.entries(attributes ?? {}).filter(
@@ -9810,6 +9854,7 @@ function invoiceToCii(invoice, options) {
9810
9854
  leaf("ram:ID", invoice.number),
9811
9855
  leaf("ram:TypeCode", invoice.type_code),
9812
9856
  date("ram:IssueDateTime", invoice.issue_date),
9857
+ options.test ? group2("ram:IncludedNote", [leaf("ram:Content", EINVOICE_TEST_NOTE)]) : void 0,
9813
9858
  ...invoice.notes.map(
9814
9859
  (note2) => group2("ram:IncludedNote", [
9815
9860
  leaf("ram:Content", note2.text),
@@ -10723,6 +10768,33 @@ function einvoiceResult(options, specVersion, validation, display) {
10723
10768
  display: display ? { checked: display.checked, missing: display.missing } : null
10724
10769
  };
10725
10770
  }
10771
+ var EINVOICE_HEADER_LENGTH = 8e3;
10772
+ function einvoiceHeaderValue(result, limit = EINVOICE_HEADER_LENGTH) {
10773
+ const encode = (report) => encodeURIComponent(JSON.stringify(report));
10774
+ const full = encode(result);
10775
+ const validation = result.validation;
10776
+ if (full.length <= limit || !validation) return full;
10777
+ let messages = validation.messages.map((message) => ({
10778
+ ...message,
10779
+ location: null
10780
+ }));
10781
+ const cut = () => ({
10782
+ ...result,
10783
+ validation: { ...validation, messages, truncated: true }
10784
+ });
10785
+ let value = encode(cut());
10786
+ while (value.length > limit && messages.length > 0) {
10787
+ let last = messages.length - 1;
10788
+ for (let i = messages.length - 1; i >= 0; i--)
10789
+ if (messages[i].severity !== "error") {
10790
+ last = i;
10791
+ break;
10792
+ }
10793
+ messages = messages.filter((_, i) => i !== last);
10794
+ value = encode(cut());
10795
+ }
10796
+ return value;
10797
+ }
10726
10798
  var EINVOICE_FLAVOUR_NAMES = {
10727
10799
  "factur-x": "Factur-X",
10728
10800
  zugferd: "ZUGFeRD"
@@ -10926,7 +10998,7 @@ function einvoiceXml(data, options) {
10926
10998
  }
10927
10999
  ]
10928
11000
  };
10929
- const check = checkInvoice(block, options);
11001
+ const check = checkInvoice(block, { profile: options.profile });
10930
11002
  if (!check.ok) return { ok: false, problems: check.problems };
10931
11003
  return {
10932
11004
  ok: true,
@@ -11249,6 +11321,89 @@ function staticAudit(input) {
11249
11321
  return findings;
11250
11322
  }
11251
11323
 
11324
+ // packages/engine/src/lib/pdfa/index.ts
11325
+ var PDFA_LEVELS = ["2b", "3b"];
11326
+ var PDFA_DEFAULT_LEVEL = "3b";
11327
+ var PDFA_STANDARDS = {
11328
+ "2b": "PDF/A-2b",
11329
+ "3b": "PDF/A-3b"
11330
+ };
11331
+ function resolvePdfa(setting) {
11332
+ if (setting === true) return PDFA_DEFAULT_LEVEL;
11333
+ return typeof setting === "string" && PDFA_LEVELS.includes(setting) ? setting : null;
11334
+ }
11335
+ function isPdfaSetting(value) {
11336
+ return value === null || typeof value === "boolean" || typeof value === "string" && PDFA_LEVELS.includes(value);
11337
+ }
11338
+ function pdfaStandard(level) {
11339
+ return PDFA_STANDARDS[level];
11340
+ }
11341
+ var PDFA_REFUSALS = {
11342
+ password: "pdf.pdfa cannot be combined with post.password: PDF/A does not allow encryption",
11343
+ einvoice: 'pdf.pdfa "2b" cannot be combined with an e-invoice: the invoice XML is an attachment, which PDF/A-2 does not allow. An e-invoice is a PDF/A-3b already; ask for "3b" or leave pdf.pdfa out',
11344
+ invalid: 'settings.pdf.pdfa is "2b", "3b", true, false or null'
11345
+ };
11346
+ var PDFA_MAX_MESSAGES = 50;
11347
+ var asMessage = (message) => ({
11348
+ severity: message.severity === "warning" ? "warning" : "error",
11349
+ rule: typeof message.rule === "string" ? message.rule : null,
11350
+ message: typeof message.message === "string" ? message.message : "",
11351
+ location: typeof message.location === "string" ? message.location : null
11352
+ });
11353
+ var capped = (messages, truncated) => {
11354
+ const sorted = [
11355
+ ...messages.filter((message) => message.severity === "error"),
11356
+ ...messages.filter((message) => message.severity !== "error")
11357
+ ];
11358
+ return sorted.length > PDFA_MAX_MESSAGES || truncated ? { messages: sorted.slice(0, PDFA_MAX_MESSAGES), truncated: true } : { messages: sorted };
11359
+ };
11360
+ function pdfaResult(level, report, pdfSha256) {
11361
+ return {
11362
+ standard: PDFA_STANDARDS[level],
11363
+ conformant: report.valid === true,
11364
+ validator: report.validator,
11365
+ id: report.id,
11366
+ versions: { ...report.versions ?? {} },
11367
+ pdf_sha256: pdfSha256,
11368
+ ...capped(
11369
+ (Array.isArray(report.messages) ? report.messages : []).map(asMessage),
11370
+ report.truncated === true
11371
+ )
11372
+ };
11373
+ }
11374
+ function pdfaFromEinvoice(validation) {
11375
+ if (!validation || validation.pdfa == null) return null;
11376
+ const messages = validation.messages.filter((message) => message.part === "pdf").map((message) => ({
11377
+ severity: message.severity,
11378
+ rule: message.rule,
11379
+ message: message.message,
11380
+ location: message.location
11381
+ }));
11382
+ const versions = { ...validation.versions ?? {} };
11383
+ return {
11384
+ standard: PDFA_STANDARDS["3b"],
11385
+ conformant: !messages.some((message) => message.severity === "error"),
11386
+ validator: `veraPDF ${versions["verapdf"] ?? "unknown"}`,
11387
+ id: validation.id ?? "",
11388
+ versions,
11389
+ pdf_sha256: validation.pdf_sha256 ?? null,
11390
+ ...capped(messages, false)
11391
+ };
11392
+ }
11393
+ function isPdfaResult(value) {
11394
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
11395
+ const report = value;
11396
+ return typeof report.standard === "string" && Object.values(PDFA_STANDARDS).includes(report.standard) && typeof report.conformant === "boolean";
11397
+ }
11398
+ var PDFA_HEADER_LENGTH = 2e3;
11399
+ function pdfaHeaderValue(result, limit = PDFA_HEADER_LENGTH) {
11400
+ const value = encodeURIComponent(JSON.stringify(result));
11401
+ if (value.length <= limit) return value;
11402
+ return encodeURIComponent(
11403
+ JSON.stringify({ ...result, messages: [], truncated: true })
11404
+ );
11405
+ }
11406
+
11252
11407
  // packages/engine/src/lib/office/errors.ts
11253
11408
  var OfficeError = class extends Error {
11254
11409
  constructor(code, message, details = {}) {
@@ -14159,10 +14314,12 @@ export {
14159
14314
  DefaultHelperRegistry,
14160
14315
  EINVOICE_FLAVOURS,
14161
14316
  EINVOICE_FLAVOUR_NAMES,
14317
+ EINVOICE_HEADER_LENGTH,
14162
14318
  EINVOICE_MAX_MESSAGES,
14163
14319
  EINVOICE_PROFILES,
14164
14320
  EINVOICE_PROFILE_NAMES,
14165
14321
  EINVOICE_SPEC_VERSIONS,
14322
+ EINVOICE_TEST_NOTE,
14166
14323
  EINVOICE_XML_NAME,
14167
14324
  EN16931_TERMS,
14168
14325
  EPC_MAX_BYTES,
@@ -14170,6 +14327,12 @@ export {
14170
14327
  INVOICE_DATA_KEY,
14171
14328
  OFFICE_FILLED_MAX_BYTES,
14172
14329
  OfficeTemplateError,
14330
+ PDFA_DEFAULT_LEVEL,
14331
+ PDFA_HEADER_LENGTH,
14332
+ PDFA_LEVELS,
14333
+ PDFA_MAX_MESSAGES,
14334
+ PDFA_REFUSALS,
14335
+ PDFA_STANDARDS,
14173
14336
  PDFUA_STANDARD,
14174
14337
  RenderError,
14175
14338
  RenderLimitError,
@@ -14218,6 +14381,7 @@ export {
14218
14381
  editAttribute,
14219
14382
  editableRegion,
14220
14383
  editableRegionAt,
14384
+ einvoiceHeaderValue,
14221
14385
  einvoiceOptionsSchema,
14222
14386
  einvoiceResult,
14223
14387
  einvoiceXml,
@@ -14261,6 +14425,8 @@ export {
14261
14425
  isApitemplateRegion,
14262
14426
  isOfficeKind,
14263
14427
  isOutputFormat,
14428
+ isPdfaResult,
14429
+ isPdfaSetting,
14264
14430
  isScss,
14265
14431
  isUaSetting,
14266
14432
  jinja2Engine,
@@ -14293,6 +14459,10 @@ export {
14293
14459
  parseDesign,
14294
14460
  parseSourceAttribute,
14295
14461
  partialInclude,
14462
+ pdfaFromEinvoice,
14463
+ pdfaHeaderValue,
14464
+ pdfaResult,
14465
+ pdfaStandard,
14296
14466
  printReset,
14297
14467
  qrSvg,
14298
14468
  readJsreportExport,
@@ -14303,6 +14473,7 @@ export {
14303
14473
  renderVersion,
14304
14474
  resolveEinvoiceOptions,
14305
14475
  resolvePath,
14476
+ resolvePdfa,
14306
14477
  resolveTranslation,
14307
14478
  resolveUa,
14308
14479
  scanBlocks,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@formfeed/engine",
3
- "version": "0.3.20",
3
+ "version": "0.3.22",
4
4
  "description": "The Formfeed template engine (Jinja2, Liquid, Handlebars) for the browser and Node.",
5
5
  "license": "MIT",
6
6
  "type": "module",
package/types/index.d.cts CHANGED
@@ -30,11 +30,13 @@ export type { ApplyResult, ChipKind, EditableRegion, EditItem, RegionChip, Regio
30
30
  export { resolveTranslation, translationTarget } from './lib/translation.cjs';
31
31
  export { DESIGN_LIMITS, DESIGN_VERSION, dataPathPattern, defaultLayer, designBytes, designOutputHash, designStarters, emitDesign, emptyDesign, genericFamily, googleFont, googleFonts, googleFontsUrl, imageIsEmpty, layerIdPattern, nearestWeight, nextLayerId, parseDesign, } from './lib/design/index.cjs';
32
32
  export type { BarcodeLayer, BarcodeSymbology, Binding, CanvasSize, DesignDocument, DesignLayer, DesignStarter, EmittedDesign, FontChoice, GoogleFont, ImageLayer, ImageSource, LayerBase, LayerType, Paint, ParsedDesign, QrLayer, Shadow, ShapeLayer, Stroke, TextFormat, TextItem, TextLayer, TextStyle, } from './lib/design/index.cjs';
33
- export { CII_GUIDELINES, CODE_LIST_RELEASE, DISPLAY_TERMS, EINVOICE_FLAVOURS, EINVOICE_FLAVOUR_NAMES, EINVOICE_MAX_MESSAGES, EINVOICE_PROFILES, EINVOICE_PROFILE_NAMES, EINVOICE_SPEC_VERSIONS, EINVOICE_XML_NAME, EN16931_TERMS, INVOICE_DATA_KEY, VAT_CATEGORIES, capMessages, carriesTerm, checkInvoice, defaultEinvoiceOptions, displayCheck, displayMissingText, einvoiceOptionsSchema, einvoiceResult, einvoiceXml, htmlText, invoiceAmountText, invoiceLineOf, invoicePathSegments, invoiceSchema, invoiceSkeleton, invoiceToCii, locateJsonPath, resolveEinvoiceOptions, shownAmounts, } from './lib/einvoice/index.cjs';
33
+ export { CII_GUIDELINES, CODE_LIST_RELEASE, DISPLAY_TERMS, EINVOICE_FLAVOURS, EINVOICE_FLAVOUR_NAMES, EINVOICE_HEADER_LENGTH, EINVOICE_MAX_MESSAGES, EINVOICE_PROFILES, EINVOICE_PROFILE_NAMES, EINVOICE_SPEC_VERSIONS, EINVOICE_TEST_NOTE, EINVOICE_XML_NAME, EN16931_TERMS, INVOICE_DATA_KEY, VAT_CATEGORIES, capMessages, carriesTerm, checkInvoice, defaultEinvoiceOptions, displayCheck, displayMissingText, einvoiceHeaderValue, einvoiceOptionsSchema, einvoiceResult, einvoiceXml, htmlText, invoiceAmountText, invoiceLineOf, invoicePathSegments, invoiceSchema, invoiceSkeleton, invoiceToCii, locateJsonPath, resolveEinvoiceOptions, shownAmounts, } from './lib/einvoice/index.cjs';
34
34
  export type { CheckOptions, DisplayCheck, DisplayValue, EinvoiceDisplay, EinvoiceFlavour, EinvoiceMessage, EinvoiceOptions, EinvoiceOptionsInput, EinvoiceProfile, EinvoiceResult, EinvoiceTerm, EinvoiceValidation, EinvoiceXml, Identifier, Invoice, InvoiceAddress, InvoiceAllowance, InvoiceBreakdown, InvoiceCheck, InvoiceInput, InvoiceLine, InvoiceParty, InvoiceProblem, InvoiceProblemCode, InvoiceTax, JsonLocation, ResolvedEinvoice, VatCategory, } from './lib/einvoice/index.cjs';
35
35
  export { ACCESSIBILITY_HEADER_LENGTH, ACCESSIBILITY_MAX_ELEMENTS, ACCESSIBILITY_MAX_FAILURES, AUDIT_CODES, AUDIT_RULES, AUDIT_SEVERITY, AUDIT_SNIPPET_LENGTH, AUDIT_TITLES, PDFUA_STANDARD, UA_CHECKS, UA_REFUSALS, accessibilityHeader, accessibilityHeaderValue, accessibilityResult, auditMessage, isUaSetting, resolveUa, } from './lib/accessibility/index.cjs';
36
36
  export type { AccessibilityFailure, AccessibilityResult, AccessibilityWarning, AuditArgs, AuditCode, AuditFinding, AuditLink, AuditOptions, AuditReport, AuditSeverity, UaCheck, UaOptions, UaSetting, ValidatorFailure, ValidatorVerdict, } from './lib/accessibility/index.cjs';
37
37
  export { staticAudit, staticAuditDiagnostics, } from './lib/accessibility/static.cjs';
38
+ export { PDFA_DEFAULT_LEVEL, PDFA_HEADER_LENGTH, PDFA_LEVELS, PDFA_MAX_MESSAGES, PDFA_REFUSALS, PDFA_STANDARDS, isPdfaResult, isPdfaSetting, pdfaFromEinvoice, pdfaHeaderValue, pdfaResult, pdfaStandard, resolvePdfa, } from './lib/pdfa/index.cjs';
39
+ export type { PdfaLevel, PdfaMessage, PdfaResult, PdfaSetting, PdfaStandard, PdfaValidatorReport, } from './lib/pdfa/index.cjs';
38
40
  export type { StaticAuditDiagnostic, StaticAuditInput, } from './lib/accessibility/static.cjs';
39
41
  export { auditRuntime } from './lib/generated/frames.cjs';
40
42
  export { encodeTemplateText } from './lib/source-edit.cjs';
package/types/index.d.ts CHANGED
@@ -30,11 +30,13 @@ export type { ApplyResult, ChipKind, EditableRegion, EditItem, RegionChip, Regio
30
30
  export { resolveTranslation, translationTarget } from './lib/translation.js';
31
31
  export { DESIGN_LIMITS, DESIGN_VERSION, dataPathPattern, defaultLayer, designBytes, designOutputHash, designStarters, emitDesign, emptyDesign, genericFamily, googleFont, googleFonts, googleFontsUrl, imageIsEmpty, layerIdPattern, nearestWeight, nextLayerId, parseDesign, } from './lib/design/index.js';
32
32
  export type { BarcodeLayer, BarcodeSymbology, Binding, CanvasSize, DesignDocument, DesignLayer, DesignStarter, EmittedDesign, FontChoice, GoogleFont, ImageLayer, ImageSource, LayerBase, LayerType, Paint, ParsedDesign, QrLayer, Shadow, ShapeLayer, Stroke, TextFormat, TextItem, TextLayer, TextStyle, } from './lib/design/index.js';
33
- export { CII_GUIDELINES, CODE_LIST_RELEASE, DISPLAY_TERMS, EINVOICE_FLAVOURS, EINVOICE_FLAVOUR_NAMES, EINVOICE_MAX_MESSAGES, EINVOICE_PROFILES, EINVOICE_PROFILE_NAMES, EINVOICE_SPEC_VERSIONS, EINVOICE_XML_NAME, EN16931_TERMS, INVOICE_DATA_KEY, VAT_CATEGORIES, capMessages, carriesTerm, checkInvoice, defaultEinvoiceOptions, displayCheck, displayMissingText, einvoiceOptionsSchema, einvoiceResult, einvoiceXml, htmlText, invoiceAmountText, invoiceLineOf, invoicePathSegments, invoiceSchema, invoiceSkeleton, invoiceToCii, locateJsonPath, resolveEinvoiceOptions, shownAmounts, } from './lib/einvoice/index.js';
33
+ export { CII_GUIDELINES, CODE_LIST_RELEASE, DISPLAY_TERMS, EINVOICE_FLAVOURS, EINVOICE_FLAVOUR_NAMES, EINVOICE_HEADER_LENGTH, EINVOICE_MAX_MESSAGES, EINVOICE_PROFILES, EINVOICE_PROFILE_NAMES, EINVOICE_SPEC_VERSIONS, EINVOICE_TEST_NOTE, EINVOICE_XML_NAME, EN16931_TERMS, INVOICE_DATA_KEY, VAT_CATEGORIES, capMessages, carriesTerm, checkInvoice, defaultEinvoiceOptions, displayCheck, displayMissingText, einvoiceHeaderValue, einvoiceOptionsSchema, einvoiceResult, einvoiceXml, htmlText, invoiceAmountText, invoiceLineOf, invoicePathSegments, invoiceSchema, invoiceSkeleton, invoiceToCii, locateJsonPath, resolveEinvoiceOptions, shownAmounts, } from './lib/einvoice/index.js';
34
34
  export type { CheckOptions, DisplayCheck, DisplayValue, EinvoiceDisplay, EinvoiceFlavour, EinvoiceMessage, EinvoiceOptions, EinvoiceOptionsInput, EinvoiceProfile, EinvoiceResult, EinvoiceTerm, EinvoiceValidation, EinvoiceXml, Identifier, Invoice, InvoiceAddress, InvoiceAllowance, InvoiceBreakdown, InvoiceCheck, InvoiceInput, InvoiceLine, InvoiceParty, InvoiceProblem, InvoiceProblemCode, InvoiceTax, JsonLocation, ResolvedEinvoice, VatCategory, } from './lib/einvoice/index.js';
35
35
  export { ACCESSIBILITY_HEADER_LENGTH, ACCESSIBILITY_MAX_ELEMENTS, ACCESSIBILITY_MAX_FAILURES, AUDIT_CODES, AUDIT_RULES, AUDIT_SEVERITY, AUDIT_SNIPPET_LENGTH, AUDIT_TITLES, PDFUA_STANDARD, UA_CHECKS, UA_REFUSALS, accessibilityHeader, accessibilityHeaderValue, accessibilityResult, auditMessage, isUaSetting, resolveUa, } from './lib/accessibility/index.js';
36
36
  export type { AccessibilityFailure, AccessibilityResult, AccessibilityWarning, AuditArgs, AuditCode, AuditFinding, AuditLink, AuditOptions, AuditReport, AuditSeverity, UaCheck, UaOptions, UaSetting, ValidatorFailure, ValidatorVerdict, } from './lib/accessibility/index.js';
37
37
  export { staticAudit, staticAuditDiagnostics, } from './lib/accessibility/static.js';
38
+ export { PDFA_DEFAULT_LEVEL, PDFA_HEADER_LENGTH, PDFA_LEVELS, PDFA_MAX_MESSAGES, PDFA_REFUSALS, PDFA_STANDARDS, isPdfaResult, isPdfaSetting, pdfaFromEinvoice, pdfaHeaderValue, pdfaResult, pdfaStandard, resolvePdfa, } from './lib/pdfa/index.js';
39
+ export type { PdfaLevel, PdfaMessage, PdfaResult, PdfaSetting, PdfaStandard, PdfaValidatorReport, } from './lib/pdfa/index.js';
38
40
  export type { StaticAuditDiagnostic, StaticAuditInput, } from './lib/accessibility/static.js';
39
41
  export { auditRuntime } from './lib/generated/frames.js';
40
42
  export { encodeTemplateText } from './lib/source-edit.js';
@@ -1,4 +1,5 @@
1
1
  import type { UaSetting } from './accessibility/index.cjs';
2
+ import type { PdfaSetting } from './pdfa/index.cjs';
2
3
  import type { BrandContext } from './brand.cjs';
3
4
  import type { EinvoiceOptionsInput } from './einvoice/schema.cjs';
4
5
  import type { EngineId, RenderContext } from './types.cjs';
@@ -56,6 +57,13 @@ export interface TemplateSettings {
56
57
  * implies `tagged`. Read it through `resolveUa`.
57
58
  */
58
59
  ua?: UaSetting;
60
+ /**
61
+ * PDF/A (plan 26): the finished PDF is converted to PDF/A-2b or PDF/A-3b and held to it by the
62
+ * validator; a file that fails fails the render, there is no mode that delivers it. `true` is
63
+ * `'3b'`. An e-invoice is a PDF/A-3b already, so `'2b'` beside one is refused. Read it through
64
+ * `resolvePdfa`.
65
+ */
66
+ pdfa?: PdfaSetting;
59
67
  metadata?: {
60
68
  title?: string;
61
69
  author?: string;
@@ -1,4 +1,5 @@
1
1
  import type { UaSetting } from './accessibility/index.js';
2
+ import type { PdfaSetting } from './pdfa/index.js';
2
3
  import type { BrandContext } from './brand.js';
3
4
  import type { EinvoiceOptionsInput } from './einvoice/schema.js';
4
5
  import type { EngineId, RenderContext } from './types.js';
@@ -56,6 +57,13 @@ export interface TemplateSettings {
56
57
  * implies `tagged`. Read it through `resolveUa`.
57
58
  */
58
59
  ua?: UaSetting;
60
+ /**
61
+ * PDF/A (plan 26): the finished PDF is converted to PDF/A-2b or PDF/A-3b and held to it by the
62
+ * validator; a file that fails fails the render, there is no mode that delivers it. `true` is
63
+ * `'3b'`. An e-invoice is a PDF/A-3b already, so `'2b'` beside one is refused. Read it through
64
+ * `resolvePdfa`.
65
+ */
66
+ pdfa?: PdfaSetting;
59
67
  metadata?: {
60
68
  title?: string;
61
69
  author?: string;
@@ -29,7 +29,11 @@ export interface EpcPayment {
29
29
  }
30
30
  /** What a caller actually hands over: the same fields, none of them guaranteed to be there. */
31
31
  export type EpcInput = Partial<EpcPayment>;
32
- /** EPC QR (GiroCode) payload, version 002, UTF-8, SCT. */
32
+ /**
33
+ * EPC QR (GiroCode) payload, version 002, UTF-8, SCT. It ends with the last populated element:
34
+ * "The last populated element is not followed by any character or element separator" (EPC069-12
35
+ * v3.1 §2.2), so the empty elements after it are left out, line breaks and all.
36
+ */
33
37
  export declare function epcPayload(input: EpcInput): string;
34
38
  /**
35
39
  * What is wrong with a payment, as a code rather than a sentence: the engine has no locale, and the
@@ -29,7 +29,11 @@ export interface EpcPayment {
29
29
  }
30
30
  /** What a caller actually hands over: the same fields, none of them guaranteed to be there. */
31
31
  export type EpcInput = Partial<EpcPayment>;
32
- /** EPC QR (GiroCode) payload, version 002, UTF-8, SCT. */
32
+ /**
33
+ * EPC QR (GiroCode) payload, version 002, UTF-8, SCT. It ends with the last populated element:
34
+ * "The last populated element is not followed by any character or element separator" (EPC069-12
35
+ * v3.1 §2.2), so the empty elements after it are left out, line breaks and all.
36
+ */
33
37
  export declare function epcPayload(input: EpcInput): string;
34
38
  /**
35
39
  * What is wrong with a payment, as a code rather than a sentence: the engine has no locale, and the
@@ -13,6 +13,15 @@ import type { EinvoiceProfile } from './schema.cjs';
13
13
  export declare const CII_GUIDELINES: Record<EinvoiceProfile, string>;
14
14
  /** The name the XML has inside the PDF, under either of the specification's two names. */
15
15
  export declare const EINVOICE_XML_NAME = "factur-x.xml";
16
+ /**
17
+ * The first note (BT-22) of a test render's invoice, on every plan (plan 25 §5.2): an XML carries
18
+ * no page mark, so the invoice says it of itself. No subject code: France's rules, which hold the
19
+ * subject codes of an invoice between French businesses, accept the note without one (measured
20
+ * with the sidecar on 2026-10-27). The wording waits for the Steuerberater (plan 25 §5.5).
21
+ */
22
+ export declare const EINVOICE_TEST_NOTE = "TEST \u2013 keine Rechnung / test render, not an invoice";
16
23
  export declare function invoiceToCii(invoice: Invoice, options: {
17
24
  profile: EinvoiceProfile;
25
+ /** A test render's invoice: its first note says it is none (`EINVOICE_TEST_NOTE`). */
26
+ test?: boolean;
18
27
  }): string;
@@ -13,6 +13,15 @@ import type { EinvoiceProfile } from './schema.js';
13
13
  export declare const CII_GUIDELINES: Record<EinvoiceProfile, string>;
14
14
  /** The name the XML has inside the PDF, under either of the specification's two names. */
15
15
  export declare const EINVOICE_XML_NAME = "factur-x.xml";
16
+ /**
17
+ * The first note (BT-22) of a test render's invoice, on every plan (plan 25 §5.2): an XML carries
18
+ * no page mark, so the invoice says it of itself. No subject code: France's rules, which hold the
19
+ * subject codes of an invoice between French businesses, accept the note without one (measured
20
+ * with the sidecar on 2026-10-27). The wording waits for the Steuerberater (plan 25 §5.5).
21
+ */
22
+ export declare const EINVOICE_TEST_NOTE = "TEST \u2013 keine Rechnung / test render, not an invoice";
16
23
  export declare function invoiceToCii(invoice: Invoice, options: {
17
24
  profile: EinvoiceProfile;
25
+ /** A test render's invoice: its first note says it is none (`EINVOICE_TEST_NOTE`). */
26
+ test?: boolean;
18
27
  }): string;
@@ -7,13 +7,13 @@
7
7
  import { type Invoice, type InvoiceProblem } from './check.cjs';
8
8
  import type { EinvoiceFlavour, EinvoiceProfile } from './schema.cjs';
9
9
  export { checkInvoice, type CheckOptions, type Identifier, type Invoice, type InvoiceAddress, type InvoiceAllowance, type InvoiceBreakdown, type InvoiceCheck, type InvoiceLine, type InvoiceParty, type InvoiceProblem, type InvoiceProblemCode, type InvoiceTax, } from './check.cjs';
10
- export { CII_GUIDELINES, EINVOICE_XML_NAME, invoiceToCii } from './cii.cjs';
10
+ export { CII_GUIDELINES, EINVOICE_TEST_NOTE, EINVOICE_XML_NAME, invoiceToCii, } from './cii.cjs';
11
11
  export { EN16931_TERMS, carriesTerm, type EinvoiceTerm } from './coverage.cjs';
12
12
  export { CODE_LIST_RELEASE } from './codes.generated.cjs';
13
13
  export { DISPLAY_TERMS, displayCheck, displayMissingText, shownAmounts, type DisplayCheck, type DisplayValue, } from './display.cjs';
14
14
  export { EINVOICE_FLAVOURS, EINVOICE_PROFILES, VAT_CATEGORIES, defaultEinvoiceOptions, einvoiceOptionsSchema, invoiceSchema, resolveEinvoiceOptions, type EinvoiceFlavour, type EinvoiceOptions, type EinvoiceOptionsInput, type EinvoiceProfile, type InvoiceInput, type ResolvedEinvoice, type VatCategory, } from './schema.cjs';
15
15
  export { invoicePathSegments, locateJsonPath, type JsonLocation, } from './locate.cjs';
16
- export { EINVOICE_FLAVOUR_NAMES, EINVOICE_MAX_MESSAGES, EINVOICE_PROFILE_NAMES, capMessages, einvoiceResult, invoiceLineOf, type EinvoiceDisplay, type EinvoiceMessage, type EinvoiceResult, type EinvoiceValidation, } from './report.cjs';
16
+ export { EINVOICE_FLAVOUR_NAMES, EINVOICE_HEADER_LENGTH, EINVOICE_MAX_MESSAGES, EINVOICE_PROFILE_NAMES, capMessages, einvoiceHeaderValue, einvoiceResult, invoiceLineOf, type EinvoiceDisplay, type EinvoiceMessage, type EinvoiceResult, type EinvoiceValidation, } from './report.cjs';
17
17
  export { htmlText } from './html-text.cjs';
18
18
  export { invoiceSkeleton } from './skeleton.cjs';
19
19
  /** Where the invoice sits in the render data. */
@@ -43,4 +43,6 @@ export type EinvoiceXml = {
43
43
  */
44
44
  export declare function einvoiceXml(data: unknown, options: {
45
45
  profile: EinvoiceProfile;
46
+ /** A test render's invoice, whose first note says it is none (`EINVOICE_TEST_NOTE`). */
47
+ test?: boolean;
46
48
  }): EinvoiceXml;
@@ -7,13 +7,13 @@
7
7
  import { type Invoice, type InvoiceProblem } from './check.js';
8
8
  import type { EinvoiceFlavour, EinvoiceProfile } from './schema.js';
9
9
  export { checkInvoice, type CheckOptions, type Identifier, type Invoice, type InvoiceAddress, type InvoiceAllowance, type InvoiceBreakdown, type InvoiceCheck, type InvoiceLine, type InvoiceParty, type InvoiceProblem, type InvoiceProblemCode, type InvoiceTax, } from './check.js';
10
- export { CII_GUIDELINES, EINVOICE_XML_NAME, invoiceToCii } from './cii.js';
10
+ export { CII_GUIDELINES, EINVOICE_TEST_NOTE, EINVOICE_XML_NAME, invoiceToCii, } from './cii.js';
11
11
  export { EN16931_TERMS, carriesTerm, type EinvoiceTerm } from './coverage.js';
12
12
  export { CODE_LIST_RELEASE } from './codes.generated.js';
13
13
  export { DISPLAY_TERMS, displayCheck, displayMissingText, shownAmounts, type DisplayCheck, type DisplayValue, } from './display.js';
14
14
  export { EINVOICE_FLAVOURS, EINVOICE_PROFILES, VAT_CATEGORIES, defaultEinvoiceOptions, einvoiceOptionsSchema, invoiceSchema, resolveEinvoiceOptions, type EinvoiceFlavour, type EinvoiceOptions, type EinvoiceOptionsInput, type EinvoiceProfile, type InvoiceInput, type ResolvedEinvoice, type VatCategory, } from './schema.js';
15
15
  export { invoicePathSegments, locateJsonPath, type JsonLocation, } from './locate.js';
16
- export { EINVOICE_FLAVOUR_NAMES, EINVOICE_MAX_MESSAGES, EINVOICE_PROFILE_NAMES, capMessages, einvoiceResult, invoiceLineOf, type EinvoiceDisplay, type EinvoiceMessage, type EinvoiceResult, type EinvoiceValidation, } from './report.js';
16
+ export { EINVOICE_FLAVOUR_NAMES, EINVOICE_HEADER_LENGTH, EINVOICE_MAX_MESSAGES, EINVOICE_PROFILE_NAMES, capMessages, einvoiceHeaderValue, einvoiceResult, invoiceLineOf, type EinvoiceDisplay, type EinvoiceMessage, type EinvoiceResult, type EinvoiceValidation, } from './report.js';
17
17
  export { htmlText } from './html-text.js';
18
18
  export { invoiceSkeleton } from './skeleton.js';
19
19
  /** Where the invoice sits in the render data. */
@@ -43,4 +43,6 @@ export type EinvoiceXml = {
43
43
  */
44
44
  export declare function einvoiceXml(data: unknown, options: {
45
45
  profile: EinvoiceProfile;
46
+ /** A test render's invoice, whose first note says it is none (`EINVOICE_TEST_NOTE`). */
47
+ test?: boolean;
46
48
  }): EinvoiceXml;
@@ -62,6 +62,21 @@ export declare const EINVOICE_MAX_MESSAGES = 50;
62
62
  export declare function capMessages(messages: readonly EinvoiceMessage[], max?: number): Pick<EinvoiceValidation, 'messages' | 'truncated'>;
63
63
  /** The result of a render that got as far as `validation` and `display` say. */
64
64
  export declare function einvoiceResult(options: Pick<EinvoiceOptions, 'profile' | 'flavour'>, specVersion: string, validation: EinvoiceValidation | null, display: DisplayCheck | null): EinvoiceResult;
65
+ /**
66
+ * How long the report may be as a header value. A proxy answers a response whose headers grow past a
67
+ * few kilobytes with 502, and the e-invoice's report shares this room with a PDF/UA verdict
68
+ * (`ACCESSIBILITY_HEADER_LENGTH`) where a render made both.
69
+ */
70
+ export declare const EINVOICE_HEADER_LENGTH = 8000;
71
+ /**
72
+ * The report as a response header carries it: the editor's true render reads the file from the body
73
+ * and the report from `X-Formfeed-Einvoice`, percent-encoded because a validator's message is not
74
+ * always ASCII. A report too long for `limit` loses, in this order, every finding's place, then the
75
+ * findings from the last, warnings before errors, and says so in `truncated` (plan 25 §5.5). What
76
+ * makes it a verdict always arrives: whether the file is valid, the rule set, the validator's id and
77
+ * versions, the hashes of the bytes it judged, and the display check.
78
+ */
79
+ export declare function einvoiceHeaderValue(result: EinvoiceResult, limit?: number): string;
65
80
  /** The names a person reads: `Factur-X` and `EN 16931` for `factur-x` and `en16931`. */
66
81
  export declare const EINVOICE_FLAVOUR_NAMES: Record<EinvoiceFlavour, string>;
67
82
  export declare const EINVOICE_PROFILE_NAMES: Record<EinvoiceProfile, string>;
@@ -62,6 +62,21 @@ export declare const EINVOICE_MAX_MESSAGES = 50;
62
62
  export declare function capMessages(messages: readonly EinvoiceMessage[], max?: number): Pick<EinvoiceValidation, 'messages' | 'truncated'>;
63
63
  /** The result of a render that got as far as `validation` and `display` say. */
64
64
  export declare function einvoiceResult(options: Pick<EinvoiceOptions, 'profile' | 'flavour'>, specVersion: string, validation: EinvoiceValidation | null, display: DisplayCheck | null): EinvoiceResult;
65
+ /**
66
+ * How long the report may be as a header value. A proxy answers a response whose headers grow past a
67
+ * few kilobytes with 502, and the e-invoice's report shares this room with a PDF/UA verdict
68
+ * (`ACCESSIBILITY_HEADER_LENGTH`) where a render made both.
69
+ */
70
+ export declare const EINVOICE_HEADER_LENGTH = 8000;
71
+ /**
72
+ * The report as a response header carries it: the editor's true render reads the file from the body
73
+ * and the report from `X-Formfeed-Einvoice`, percent-encoded because a validator's message is not
74
+ * always ASCII. A report too long for `limit` loses, in this order, every finding's place, then the
75
+ * findings from the last, warnings before errors, and says so in `truncated` (plan 25 §5.5). What
76
+ * makes it a verdict always arrives: whether the file is valid, the rule set, the validator's id and
77
+ * versions, the hashes of the bytes it judged, and the display check.
78
+ */
79
+ export declare function einvoiceHeaderValue(result: EinvoiceResult, limit?: number): string;
65
80
  /** The names a person reads: `Factur-X` and `EN 16931` for `factur-x` and `en16931`. */
66
81
  export declare const EINVOICE_FLAVOUR_NAMES: Record<EinvoiceFlavour, string>;
67
82
  export declare const EINVOICE_PROFILE_NAMES: Record<EinvoiceProfile, string>;
@@ -0,0 +1,112 @@
1
+ import type { EinvoiceValidation } from '../einvoice/report.cjs';
2
+ /**
3
+ * `@formfeed/engine/pdfa` (plan 26): what `pdf.pdfa` asks for, what it cannot go with, and the
4
+ * report a render keeps of what the validator said. No template engine is imported here and nothing
5
+ * of Node, so the gateway Worker, the render-worker, the editor and the CLI all read the same names.
6
+ *
7
+ * PDF/A is ISO 19005, a file format for keeping documents: the fonts embedded, the colours defined,
8
+ * nothing that needs the network or a password. Whether an archive keeps them as a law asks (the
9
+ * GoBD, for one) is a matter of how the archive is run, not of the file, so nothing here or on any
10
+ * page says "revisionssicher", "GoBD-konform", "certified" or "archive-compliant": the report says
11
+ * which part and level a file was held to, and by which validator.
12
+ */
13
+ /**
14
+ * The levels a render can ask for. 3b is the default and what an e-invoice is; 2b is for archives
15
+ * that take PDF/A-2 and not PDF/A-3, which may carry any file as an attachment. PDF/A-1 forbids the
16
+ * transparency Chromium writes, so it is not offered.
17
+ */
18
+ export declare const PDFA_LEVELS: readonly ["2b", "3b"];
19
+ export type PdfaLevel = (typeof PDFA_LEVELS)[number];
20
+ /** What `true` stands for. */
21
+ export declare const PDFA_DEFAULT_LEVEL: PdfaLevel;
22
+ /** The standard a level names, as the report and the `X-Formfeed-PDF-A` header write it. */
23
+ export declare const PDFA_STANDARDS: {
24
+ readonly '2b': "PDF/A-2b";
25
+ readonly '3b': "PDF/A-3b";
26
+ };
27
+ export type PdfaStandard = (typeof PDFA_STANDARDS)[PdfaLevel];
28
+ /** `pdf.pdfa` as a template's settings or a request write it. */
29
+ export type PdfaSetting = PdfaLevel | boolean | null;
30
+ /**
31
+ * `pdf.pdfa` as a render takes it: a level, `true` for the default level, and anything else off.
32
+ * There is no check to choose (plan 26, decision 2): a file that fails PDF/A fails the render.
33
+ */
34
+ export declare function resolvePdfa(setting: unknown): PdfaLevel | null;
35
+ /** Whether a `pdf.pdfa` value is one the API takes: a level, a boolean, or null. */
36
+ export declare function isPdfaSetting(value: unknown): value is PdfaSetting;
37
+ export declare function pdfaStandard(level: PdfaLevel): PdfaStandard;
38
+ /**
39
+ * What `pdf.pdfa` cannot go with, in the words every place that refuses it uses (the gateway before
40
+ * a render costs anything, the worker for whoever calls it directly, `formfeed validate`, the API's
41
+ * template validation). A merge, a watermark and Word and PowerPoint templates go with it, since all
42
+ * of them come before the conversion.
43
+ */
44
+ export declare const PDFA_REFUSALS: {
45
+ readonly password: "pdf.pdfa cannot be combined with post.password: PDF/A does not allow encryption";
46
+ readonly einvoice: "pdf.pdfa \"2b\" cannot be combined with an e-invoice: the invoice XML is an attachment, which PDF/A-2 does not allow. An e-invoice is a PDF/A-3b already; ask for \"3b\" or leave pdf.pdfa out";
47
+ readonly invalid: "settings.pdf.pdfa is \"2b\", \"3b\", true, false or null";
48
+ };
49
+ /** How many findings a render keeps; a render row travels in every list and webhook. */
50
+ export declare const PDFA_MAX_MESSAGES = 50;
51
+ /** One rule of PDF/A the file failed, as the validator reports it. */
52
+ export interface PdfaMessage {
53
+ severity: 'error' | 'warning';
54
+ /** A clause of ISO 19005 and veraPDF's test number, `6.2.11.4.1-1`. */
55
+ rule: string | null;
56
+ message: string;
57
+ /** veraPDF's path to the object of the PDF. */
58
+ location: string | null;
59
+ }
60
+ /**
61
+ * `pdfa` on a render (plan 26): `null` unless the render asked for PDF/A or is an e-invoice with
62
+ * `pdf.pdfa` beside it. `conformant` is the validator's word on the file it judged; a render that
63
+ * succeeded delivered exactly that file, and one that failed delivered nothing.
64
+ */
65
+ export interface PdfaResult {
66
+ standard: PdfaStandard;
67
+ conformant: boolean;
68
+ /** The validator and its version: its rule set moves between releases. */
69
+ validator: string;
70
+ /** The validator's own id for this report, which its log names too. */
71
+ id: string;
72
+ /** The versions of what converted and judged the file: `verapdf`, `pdfbox` (and `mustang` for an e-invoice). */
73
+ versions: Record<string, string>;
74
+ /** SHA-256 of the PDF that was judged: the delivered file when the render succeeded. */
75
+ pdf_sha256: string | null;
76
+ messages: PdfaMessage[];
77
+ /** More findings than the render keeps. */
78
+ truncated?: boolean;
79
+ }
80
+ /** The sidecar's report of `POST /pdf/pdfa` (`ArchiveReport` in `apps/conformance`). */
81
+ export interface PdfaValidatorReport {
82
+ id: string;
83
+ valid: boolean;
84
+ pdfa: string;
85
+ validator: string;
86
+ messages: Array<{
87
+ severity?: string;
88
+ rule?: string | null;
89
+ message?: string;
90
+ location?: string | null;
91
+ }>;
92
+ truncated?: boolean;
93
+ versions?: Record<string, string>;
94
+ }
95
+ /** The report a render keeps, from the sidecar's, with the hash of the file it judged. */
96
+ export declare function pdfaResult(level: PdfaLevel, report: PdfaValidatorReport, pdfSha256: string | null): PdfaResult;
97
+ /**
98
+ * The `pdfa` block of an e-invoice that asked for PDF/A too (plan 26, decision 8): an e-invoice is
99
+ * a PDF/A-3b, converted and judged once, so the block is made from the e-invoice's own validation
100
+ * and its PDF findings. Null while that validation ended at the XML, before there was a file.
101
+ */
102
+ export declare function pdfaFromEinvoice(validation: EinvoiceValidation | null | undefined): PdfaResult | null;
103
+ /** Whether a stored value is a report: the API shows it, and null otherwise. */
104
+ export declare function isPdfaResult(value: unknown): value is PdfaResult;
105
+ /** How long the report may be as a header value: proxies refuse a response whose headers grow past a few kilobytes. */
106
+ export declare const PDFA_HEADER_LENGTH = 2000;
107
+ /**
108
+ * The report as the worker's binary answer carries it in `x-formfeed-pdfa`, percent-encoded: the
109
+ * gateway records it with the render and `preview-render` shows it. A file that was delivered
110
+ * passed, so its report is short; one that is too long anyway loses its findings and says so.
111
+ */
112
+ export declare function pdfaHeaderValue(result: PdfaResult, limit?: number): string;
@@ -0,0 +1,112 @@
1
+ import type { EinvoiceValidation } from '../einvoice/report.js';
2
+ /**
3
+ * `@formfeed/engine/pdfa` (plan 26): what `pdf.pdfa` asks for, what it cannot go with, and the
4
+ * report a render keeps of what the validator said. No template engine is imported here and nothing
5
+ * of Node, so the gateway Worker, the render-worker, the editor and the CLI all read the same names.
6
+ *
7
+ * PDF/A is ISO 19005, a file format for keeping documents: the fonts embedded, the colours defined,
8
+ * nothing that needs the network or a password. Whether an archive keeps them as a law asks (the
9
+ * GoBD, for one) is a matter of how the archive is run, not of the file, so nothing here or on any
10
+ * page says "revisionssicher", "GoBD-konform", "certified" or "archive-compliant": the report says
11
+ * which part and level a file was held to, and by which validator.
12
+ */
13
+ /**
14
+ * The levels a render can ask for. 3b is the default and what an e-invoice is; 2b is for archives
15
+ * that take PDF/A-2 and not PDF/A-3, which may carry any file as an attachment. PDF/A-1 forbids the
16
+ * transparency Chromium writes, so it is not offered.
17
+ */
18
+ export declare const PDFA_LEVELS: readonly ["2b", "3b"];
19
+ export type PdfaLevel = (typeof PDFA_LEVELS)[number];
20
+ /** What `true` stands for. */
21
+ export declare const PDFA_DEFAULT_LEVEL: PdfaLevel;
22
+ /** The standard a level names, as the report and the `X-Formfeed-PDF-A` header write it. */
23
+ export declare const PDFA_STANDARDS: {
24
+ readonly '2b': "PDF/A-2b";
25
+ readonly '3b': "PDF/A-3b";
26
+ };
27
+ export type PdfaStandard = (typeof PDFA_STANDARDS)[PdfaLevel];
28
+ /** `pdf.pdfa` as a template's settings or a request write it. */
29
+ export type PdfaSetting = PdfaLevel | boolean | null;
30
+ /**
31
+ * `pdf.pdfa` as a render takes it: a level, `true` for the default level, and anything else off.
32
+ * There is no check to choose (plan 26, decision 2): a file that fails PDF/A fails the render.
33
+ */
34
+ export declare function resolvePdfa(setting: unknown): PdfaLevel | null;
35
+ /** Whether a `pdf.pdfa` value is one the API takes: a level, a boolean, or null. */
36
+ export declare function isPdfaSetting(value: unknown): value is PdfaSetting;
37
+ export declare function pdfaStandard(level: PdfaLevel): PdfaStandard;
38
+ /**
39
+ * What `pdf.pdfa` cannot go with, in the words every place that refuses it uses (the gateway before
40
+ * a render costs anything, the worker for whoever calls it directly, `formfeed validate`, the API's
41
+ * template validation). A merge, a watermark and Word and PowerPoint templates go with it, since all
42
+ * of them come before the conversion.
43
+ */
44
+ export declare const PDFA_REFUSALS: {
45
+ readonly password: "pdf.pdfa cannot be combined with post.password: PDF/A does not allow encryption";
46
+ readonly einvoice: "pdf.pdfa \"2b\" cannot be combined with an e-invoice: the invoice XML is an attachment, which PDF/A-2 does not allow. An e-invoice is a PDF/A-3b already; ask for \"3b\" or leave pdf.pdfa out";
47
+ readonly invalid: "settings.pdf.pdfa is \"2b\", \"3b\", true, false or null";
48
+ };
49
+ /** How many findings a render keeps; a render row travels in every list and webhook. */
50
+ export declare const PDFA_MAX_MESSAGES = 50;
51
+ /** One rule of PDF/A the file failed, as the validator reports it. */
52
+ export interface PdfaMessage {
53
+ severity: 'error' | 'warning';
54
+ /** A clause of ISO 19005 and veraPDF's test number, `6.2.11.4.1-1`. */
55
+ rule: string | null;
56
+ message: string;
57
+ /** veraPDF's path to the object of the PDF. */
58
+ location: string | null;
59
+ }
60
+ /**
61
+ * `pdfa` on a render (plan 26): `null` unless the render asked for PDF/A or is an e-invoice with
62
+ * `pdf.pdfa` beside it. `conformant` is the validator's word on the file it judged; a render that
63
+ * succeeded delivered exactly that file, and one that failed delivered nothing.
64
+ */
65
+ export interface PdfaResult {
66
+ standard: PdfaStandard;
67
+ conformant: boolean;
68
+ /** The validator and its version: its rule set moves between releases. */
69
+ validator: string;
70
+ /** The validator's own id for this report, which its log names too. */
71
+ id: string;
72
+ /** The versions of what converted and judged the file: `verapdf`, `pdfbox` (and `mustang` for an e-invoice). */
73
+ versions: Record<string, string>;
74
+ /** SHA-256 of the PDF that was judged: the delivered file when the render succeeded. */
75
+ pdf_sha256: string | null;
76
+ messages: PdfaMessage[];
77
+ /** More findings than the render keeps. */
78
+ truncated?: boolean;
79
+ }
80
+ /** The sidecar's report of `POST /pdf/pdfa` (`ArchiveReport` in `apps/conformance`). */
81
+ export interface PdfaValidatorReport {
82
+ id: string;
83
+ valid: boolean;
84
+ pdfa: string;
85
+ validator: string;
86
+ messages: Array<{
87
+ severity?: string;
88
+ rule?: string | null;
89
+ message?: string;
90
+ location?: string | null;
91
+ }>;
92
+ truncated?: boolean;
93
+ versions?: Record<string, string>;
94
+ }
95
+ /** The report a render keeps, from the sidecar's, with the hash of the file it judged. */
96
+ export declare function pdfaResult(level: PdfaLevel, report: PdfaValidatorReport, pdfSha256: string | null): PdfaResult;
97
+ /**
98
+ * The `pdfa` block of an e-invoice that asked for PDF/A too (plan 26, decision 8): an e-invoice is
99
+ * a PDF/A-3b, converted and judged once, so the block is made from the e-invoice's own validation
100
+ * and its PDF findings. Null while that validation ended at the XML, before there was a file.
101
+ */
102
+ export declare function pdfaFromEinvoice(validation: EinvoiceValidation | null | undefined): PdfaResult | null;
103
+ /** Whether a stored value is a report: the API shows it, and null otherwise. */
104
+ export declare function isPdfaResult(value: unknown): value is PdfaResult;
105
+ /** How long the report may be as a header value: proxies refuse a response whose headers grow past a few kilobytes. */
106
+ export declare const PDFA_HEADER_LENGTH = 2000;
107
+ /**
108
+ * The report as the worker's binary answer carries it in `x-formfeed-pdfa`, percent-encoded: the
109
+ * gateway records it with the render and `preview-render` shows it. A file that was delivered
110
+ * passed, so its report is short; one that is too long anyway loses its findings and says so.
111
+ */
112
+ export declare function pdfaHeaderValue(result: PdfaResult, limit?: number): string;