modern-pdf-lib 0.30.0 → 0.32.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -546,7 +546,7 @@ const NS_PDFAID = "http://www.aiim.org/pdfa/ns/id/";
546
546
  * Escape special characters for XML text content.
547
547
  * @internal
548
548
  */
549
- function escapeXml$4(str) {
549
+ function escapeXml$5(str) {
550
550
  const parts = [];
551
551
  for (let i = 0; i < str.length; i++) {
552
552
  const c = str.charCodeAt(i);
@@ -594,14 +594,14 @@ function buildXmpMetadata(meta) {
594
594
  if (meta.title !== void 0) {
595
595
  lines.push(" <dc:title>");
596
596
  lines.push(" <rdf:Alt>");
597
- lines.push(` <rdf:li xml:lang="x-default">${escapeXml$4(meta.title)}</rdf:li>`);
597
+ lines.push(` <rdf:li xml:lang="x-default">${escapeXml$5(meta.title)}</rdf:li>`);
598
598
  lines.push(" </rdf:Alt>");
599
599
  lines.push(" </dc:title>");
600
600
  }
601
601
  if (meta.author !== void 0) {
602
602
  lines.push(" <dc:creator>");
603
603
  lines.push(" <rdf:Seq>");
604
- lines.push(` <rdf:li>${escapeXml$4(meta.author)}</rdf:li>`);
604
+ lines.push(` <rdf:li>${escapeXml$5(meta.author)}</rdf:li>`);
605
605
  lines.push(" </rdf:Seq>");
606
606
  lines.push(" </dc:creator>");
607
607
  }
@@ -611,7 +611,7 @@ function buildXmpMetadata(meta) {
611
611
  lines.push(" <rdf:Bag>");
612
612
  for (const kw of keywords) {
613
613
  const trimmed = kw.trim();
614
- if (trimmed.length > 0) lines.push(` <rdf:li>${escapeXml$4(trimmed)}</rdf:li>`);
614
+ if (trimmed.length > 0) lines.push(` <rdf:li>${escapeXml$5(trimmed)}</rdf:li>`);
615
615
  }
616
616
  lines.push(" </rdf:Bag>");
617
617
  lines.push(" </dc:subject>");
@@ -619,15 +619,15 @@ function buildXmpMetadata(meta) {
619
619
  if (meta.subject !== void 0) {
620
620
  lines.push(" <dc:description>");
621
621
  lines.push(" <rdf:Alt>");
622
- lines.push(` <rdf:li xml:lang="x-default">${escapeXml$4(meta.subject)}</rdf:li>`);
622
+ lines.push(` <rdf:li xml:lang="x-default">${escapeXml$5(meta.subject)}</rdf:li>`);
623
623
  lines.push(" </rdf:Alt>");
624
624
  lines.push(" </dc:description>");
625
625
  }
626
- if (meta.creator !== void 0) lines.push(` <xmp:CreatorTool>${escapeXml$4(meta.creator)}</xmp:CreatorTool>`);
626
+ if (meta.creator !== void 0) lines.push(` <xmp:CreatorTool>${escapeXml$5(meta.creator)}</xmp:CreatorTool>`);
627
627
  if (meta.creationDate !== void 0) lines.push(` <xmp:CreateDate>${formatXmpDate(meta.creationDate)}</xmp:CreateDate>`);
628
628
  if (meta.modDate !== void 0) lines.push(` <xmp:ModifyDate>${formatXmpDate(meta.modDate)}</xmp:ModifyDate>`);
629
- if (meta.producer !== void 0) lines.push(` <pdf:Producer>${escapeXml$4(meta.producer)}</pdf:Producer>`);
630
- if (meta.keywords !== void 0) lines.push(` <pdf:Keywords>${escapeXml$4(meta.keywords)}</pdf:Keywords>`);
629
+ if (meta.producer !== void 0) lines.push(` <pdf:Producer>${escapeXml$5(meta.producer)}</pdf:Producer>`);
630
+ if (meta.keywords !== void 0) lines.push(` <pdf:Keywords>${escapeXml$5(meta.keywords)}</pdf:Keywords>`);
631
631
  lines.push("</rdf:Description>");
632
632
  lines.push("</rdf:RDF>");
633
633
  lines.push("</x:xmpmeta>");
@@ -7019,7 +7019,7 @@ function countMatches(text, regex) {
7019
7019
  //#region src/compliance/xmpGenerator.ts
7020
7020
  const encoder$2 = new TextEncoder();
7021
7021
  /** Escape XML special characters in a string. */
7022
- function escapeXml$3(str) {
7022
+ function escapeXml$4(str) {
7023
7023
  return str.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll("\"", "&quot;").replaceAll("'", "&apos;");
7024
7024
  }
7025
7025
  /**
@@ -7053,35 +7053,35 @@ function generatePdfAXmp(options) {
7053
7053
  if (options.title) {
7054
7054
  xmp += " <dc:title>\n";
7055
7055
  xmp += " <rdf:Alt>\n";
7056
- xmp += ` <rdf:li xml:lang="${lang}">${escapeXml$3(options.title)}</rdf:li>\n`;
7056
+ xmp += ` <rdf:li xml:lang="${lang}">${escapeXml$4(options.title)}</rdf:li>\n`;
7057
7057
  xmp += " </rdf:Alt>\n";
7058
7058
  xmp += " </dc:title>\n";
7059
7059
  }
7060
7060
  if (options.author) {
7061
7061
  xmp += " <dc:creator>\n";
7062
7062
  xmp += " <rdf:Seq>\n";
7063
- xmp += ` <rdf:li>${escapeXml$3(options.author)}</rdf:li>\n`;
7063
+ xmp += ` <rdf:li>${escapeXml$4(options.author)}</rdf:li>\n`;
7064
7064
  xmp += " </rdf:Seq>\n";
7065
7065
  xmp += " </dc:creator>\n";
7066
7066
  }
7067
7067
  if (options.subject) {
7068
7068
  xmp += " <dc:description>\n";
7069
7069
  xmp += " <rdf:Alt>\n";
7070
- xmp += ` <rdf:li xml:lang="${lang}">${escapeXml$3(options.subject)}</rdf:li>\n`;
7070
+ xmp += ` <rdf:li xml:lang="${lang}">${escapeXml$4(options.subject)}</rdf:li>\n`;
7071
7071
  xmp += " </rdf:Alt>\n";
7072
7072
  xmp += " </dc:description>\n";
7073
7073
  }
7074
7074
  xmp += " </rdf:Description>\n";
7075
7075
  xmp += " <rdf:Description rdf:about=\"\"\n";
7076
7076
  xmp += " xmlns:xmp=\"http://ns.adobe.com/xap/1.0/\">\n";
7077
- xmp += ` <xmp:CreatorTool>${escapeXml$3(creatorTool)}</xmp:CreatorTool>\n`;
7077
+ xmp += ` <xmp:CreatorTool>${escapeXml$4(creatorTool)}</xmp:CreatorTool>\n`;
7078
7078
  xmp += ` <xmp:CreateDate>${createDate}</xmp:CreateDate>\n`;
7079
7079
  xmp += ` <xmp:ModifyDate>${modifyDate}</xmp:ModifyDate>\n`;
7080
7080
  xmp += " </rdf:Description>\n";
7081
7081
  xmp += " <rdf:Description rdf:about=\"\"\n";
7082
7082
  xmp += " xmlns:pdf=\"http://ns.adobe.com/pdf/1.3/\">\n";
7083
- xmp += ` <pdf:Producer>${escapeXml$3(producer)}</pdf:Producer>\n`;
7084
- if (options.keywords) xmp += ` <pdf:Keywords>${escapeXml$3(options.keywords)}</pdf:Keywords>\n`;
7083
+ xmp += ` <pdf:Producer>${escapeXml$4(producer)}</pdf:Producer>\n`;
7084
+ if (options.keywords) xmp += ` <pdf:Keywords>${escapeXml$4(options.keywords)}</pdf:Keywords>\n`;
7085
7085
  xmp += " </rdf:Description>\n";
7086
7086
  xmp += " </rdf:RDF>\n";
7087
7087
  xmp += "</x:xmpmeta>\n";
@@ -17060,7 +17060,7 @@ const SECTION_TYPES = /* @__PURE__ */ new Set([
17060
17060
  "BlockQuote"
17061
17061
  ]);
17062
17062
  /** Illustration types that require alt text per PDF/UA. */
17063
- const ILLUSTRATION_TYPES = /* @__PURE__ */ new Set([
17063
+ const ILLUSTRATION_TYPES$1 = /* @__PURE__ */ new Set([
17064
17064
  "Figure",
17065
17065
  "Formula",
17066
17066
  "Form"
@@ -17403,7 +17403,7 @@ function findPreviousHeadingInSection(current, sectionHeadings) {
17403
17403
  */
17404
17404
  function checkAltText(elements, errors) {
17405
17405
  for (const elem of elements) {
17406
- if (!ILLUSTRATION_TYPES.has(elem.type)) continue;
17406
+ if (!ILLUSTRATION_TYPES$1.has(elem.type)) continue;
17407
17407
  if (elem.options.artifact) continue;
17408
17408
  if (elem.options.altText === void 0 && elem.options.actualText === void 0) errors.push({
17409
17409
  code: "UA-STRUCT-005",
@@ -20875,7 +20875,7 @@ const VAT_CATEGORY_STANDARD$1 = "S";
20875
20875
  /** Unit code C62 = "one" (unitless piece, UN/ECE Rec 20). */
20876
20876
  const UNIT_CODE_ONE$1 = "C62";
20877
20877
  /** Escape XML special characters in a text value. */
20878
- function escapeXml$2(str) {
20878
+ function escapeXml$3(str) {
20879
20879
  return str.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll("\"", "&quot;").replaceAll("'", "&apos;");
20880
20880
  }
20881
20881
  /** Format a numeric monetary/quantity value with two decimal places. */
@@ -20933,13 +20933,13 @@ function computeTotals$1(lines) {
20933
20933
  /** Render a single trade party block (Seller or Buyer). */
20934
20934
  function renderParty$1(party, indent) {
20935
20935
  const lines = [];
20936
- lines.push(`${indent}<ram:Name>${escapeXml$2(party.name)}</ram:Name>`);
20936
+ lines.push(`${indent}<ram:Name>${escapeXml$3(party.name)}</ram:Name>`);
20937
20937
  lines.push(`${indent}<ram:PostalTradeAddress>`);
20938
- lines.push(`${indent} <ram:CountryID>${escapeXml$2(party.countryCode)}</ram:CountryID>`);
20938
+ lines.push(`${indent} <ram:CountryID>${escapeXml$3(party.countryCode)}</ram:CountryID>`);
20939
20939
  lines.push(`${indent}</ram:PostalTradeAddress>`);
20940
20940
  if (party.vatId !== void 0 && party.vatId !== "") {
20941
20941
  lines.push(`${indent}<ram:SpecifiedTaxRegistration>`);
20942
- lines.push(`${indent} <ram:ID schemeID="VA">${escapeXml$2(party.vatId)}</ram:ID>`);
20942
+ lines.push(`${indent} <ram:ID schemeID="VA">${escapeXml$3(party.vatId)}</ram:ID>`);
20943
20943
  lines.push(`${indent}</ram:SpecifiedTaxRegistration>`);
20944
20944
  }
20945
20945
  return lines.join("\n");
@@ -20953,7 +20953,7 @@ function renderLine$2(line, index, currency) {
20953
20953
  out.push(` <ram:LineID>${index}</ram:LineID>`);
20954
20954
  out.push(" </ram:AssociatedDocumentLineDocument>");
20955
20955
  out.push(" <ram:SpecifiedTradeProduct>");
20956
- out.push(` <ram:Name>${escapeXml$2(line.description)}</ram:Name>`);
20956
+ out.push(` <ram:Name>${escapeXml$3(line.description)}</ram:Name>`);
20957
20957
  out.push(" </ram:SpecifiedTradeProduct>");
20958
20958
  out.push(" <ram:SpecifiedLineTradeAgreement>");
20959
20959
  out.push(" <ram:NetPriceProductTradePrice>");
@@ -20970,7 +20970,7 @@ function renderLine$2(line, index, currency) {
20970
20970
  out.push(` <ram:RateApplicablePercent>${formatPercent$1(line.taxPercent)}</ram:RateApplicablePercent>`);
20971
20971
  out.push(" </ram:ApplicableTradeTax>");
20972
20972
  out.push(" <ram:SpecifiedTradeSettlementLineMonetarySummation>");
20973
- out.push(` <ram:LineTotalAmount currencyID="${escapeXml$2(currency)}">${formatAmount$1(net)}</ram:LineTotalAmount>`);
20973
+ out.push(` <ram:LineTotalAmount currencyID="${escapeXml$3(currency)}">${formatAmount$1(net)}</ram:LineTotalAmount>`);
20974
20974
  out.push(" </ram:SpecifiedTradeSettlementLineMonetarySummation>");
20975
20975
  out.push(" </ram:SpecifiedLineTradeSettlement>");
20976
20976
  out.push(" </ram:IncludedSupplyChainTradeLineItem>");
@@ -20980,9 +20980,9 @@ function renderLine$2(line, index, currency) {
20980
20980
  function renderTaxGroup$1(group, currency) {
20981
20981
  const out = [];
20982
20982
  out.push(" <ram:ApplicableTradeTax>");
20983
- out.push(` <ram:CalculatedAmount currencyID="${escapeXml$2(currency)}">${formatAmount$1(group.taxAmount)}</ram:CalculatedAmount>`);
20983
+ out.push(` <ram:CalculatedAmount currencyID="${escapeXml$3(currency)}">${formatAmount$1(group.taxAmount)}</ram:CalculatedAmount>`);
20984
20984
  out.push(" <ram:TypeCode>VAT</ram:TypeCode>");
20985
- out.push(` <ram:BasisAmount currencyID="${escapeXml$2(currency)}">${formatAmount$1(group.basisAmount)}</ram:BasisAmount>`);
20985
+ out.push(` <ram:BasisAmount currencyID="${escapeXml$3(currency)}">${formatAmount$1(group.basisAmount)}</ram:BasisAmount>`);
20986
20986
  out.push(` <ram:CategoryCode>${VAT_CATEGORY_STANDARD$1}</ram:CategoryCode>`);
20987
20987
  out.push(` <ram:RateApplicablePercent>${formatPercent$1(group.taxPercent)}</ram:RateApplicablePercent>`);
20988
20988
  out.push(" </ram:ApplicableTradeTax>");
@@ -21010,11 +21010,11 @@ function generateCiiXml(invoice, profile = "EN16931") {
21010
21010
  out.push("<rsm:CrossIndustryInvoice xmlns:rsm=\"urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100\" xmlns:ram=\"urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100\" xmlns:udt=\"urn:un:unece:uncefact:data:standard:UnqualifiedDataType:100\">");
21011
21011
  out.push(" <rsm:ExchangedDocumentContext>");
21012
21012
  out.push(" <ram:GuidelineSpecifiedDocumentContextParameter>");
21013
- out.push(` <ram:ID>${escapeXml$2(guidelineUrn)}</ram:ID>`);
21013
+ out.push(` <ram:ID>${escapeXml$3(guidelineUrn)}</ram:ID>`);
21014
21014
  out.push(" </ram:GuidelineSpecifiedDocumentContextParameter>");
21015
21015
  out.push(" </rsm:ExchangedDocumentContext>");
21016
21016
  out.push(" <rsm:ExchangedDocument>");
21017
- out.push(` <ram:ID>${escapeXml$2(invoice.invoiceNumber)}</ram:ID>`);
21017
+ out.push(` <ram:ID>${escapeXml$3(invoice.invoiceNumber)}</ram:ID>`);
21018
21018
  out.push(` <ram:TypeCode>${COMMERCIAL_INVOICE_TYPE_CODE$1}</ram:TypeCode>`);
21019
21019
  out.push(" <ram:IssueDateTime>");
21020
21020
  out.push(` <udt:DateTimeString format="102">${toDateString102$1(invoice.issueDate)}</udt:DateTimeString>`);
@@ -21032,14 +21032,14 @@ function generateCiiXml(invoice, profile = "EN16931") {
21032
21032
  out.push(" </ram:ApplicableHeaderTradeAgreement>");
21033
21033
  out.push(" <ram:ApplicableHeaderTradeDelivery/>");
21034
21034
  out.push(" <ram:ApplicableHeaderTradeSettlement>");
21035
- out.push(` <ram:InvoiceCurrencyCode>${escapeXml$2(currency)}</ram:InvoiceCurrencyCode>`);
21035
+ out.push(` <ram:InvoiceCurrencyCode>${escapeXml$3(currency)}</ram:InvoiceCurrencyCode>`);
21036
21036
  for (const group of totals.taxGroups) out.push(renderTaxGroup$1(group, currency));
21037
21037
  out.push(" <ram:SpecifiedTradeSettlementHeaderMonetarySummation>");
21038
- out.push(` <ram:LineTotalAmount currencyID="${escapeXml$2(currency)}">${formatAmount$1(totals.lineTotal)}</ram:LineTotalAmount>`);
21039
- out.push(` <ram:TaxBasisTotalAmount currencyID="${escapeXml$2(currency)}">${formatAmount$1(totals.lineTotal)}</ram:TaxBasisTotalAmount>`);
21040
- out.push(` <ram:TaxTotalAmount currencyID="${escapeXml$2(currency)}">${formatAmount$1(totals.taxTotal)}</ram:TaxTotalAmount>`);
21041
- out.push(` <ram:GrandTotalAmount currencyID="${escapeXml$2(currency)}">${formatAmount$1(totals.grandTotal)}</ram:GrandTotalAmount>`);
21042
- out.push(` <ram:DuePayableAmount currencyID="${escapeXml$2(currency)}">${formatAmount$1(totals.duePayable)}</ram:DuePayableAmount>`);
21038
+ out.push(` <ram:LineTotalAmount currencyID="${escapeXml$3(currency)}">${formatAmount$1(totals.lineTotal)}</ram:LineTotalAmount>`);
21039
+ out.push(` <ram:TaxBasisTotalAmount currencyID="${escapeXml$3(currency)}">${formatAmount$1(totals.lineTotal)}</ram:TaxBasisTotalAmount>`);
21040
+ out.push(` <ram:TaxTotalAmount currencyID="${escapeXml$3(currency)}">${formatAmount$1(totals.taxTotal)}</ram:TaxTotalAmount>`);
21041
+ out.push(` <ram:GrandTotalAmount currencyID="${escapeXml$3(currency)}">${formatAmount$1(totals.grandTotal)}</ram:GrandTotalAmount>`);
21042
+ out.push(` <ram:DuePayableAmount currencyID="${escapeXml$3(currency)}">${formatAmount$1(totals.duePayable)}</ram:DuePayableAmount>`);
21043
21043
  out.push(" </ram:SpecifiedTradeSettlementHeaderMonetarySummation>");
21044
21044
  out.push(" </ram:ApplicableHeaderTradeSettlement>");
21045
21045
  out.push(" </rsm:SupplyChainTradeTransaction>");
@@ -21261,7 +21261,7 @@ const PDFA4_PART = 4;
21261
21261
  /** PDF/A-4 revision year (`pdfaid:rev`). */
21262
21262
  const PDFA4_REV = "2020";
21263
21263
  /** Escape XML special characters in a string. */
21264
- function escapeXml$1(str) {
21264
+ function escapeXml$2(str) {
21265
21265
  return str.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll("\"", "&quot;").replaceAll("'", "&apos;");
21266
21266
  }
21267
21267
  /**
@@ -21279,17 +21279,17 @@ function conformanceFor(level) {
21279
21279
  function renderExtensionSchema(s) {
21280
21280
  let out = "";
21281
21281
  out += " <rdf:li rdf:parseType=\"Resource\">\n";
21282
- out += ` <pdfaSchema:schema>${escapeXml$1(s.schema)}</pdfaSchema:schema>\n`;
21283
- out += ` <pdfaSchema:namespaceURI>${escapeXml$1(s.namespaceUri)}</pdfaSchema:namespaceURI>\n`;
21284
- out += ` <pdfaSchema:prefix>${escapeXml$1(s.prefix)}</pdfaSchema:prefix>\n`;
21282
+ out += ` <pdfaSchema:schema>${escapeXml$2(s.schema)}</pdfaSchema:schema>\n`;
21283
+ out += ` <pdfaSchema:namespaceURI>${escapeXml$2(s.namespaceUri)}</pdfaSchema:namespaceURI>\n`;
21284
+ out += ` <pdfaSchema:prefix>${escapeXml$2(s.prefix)}</pdfaSchema:prefix>\n`;
21285
21285
  out += " <pdfaSchema:property>\n";
21286
21286
  out += " <rdf:Seq>\n";
21287
21287
  for (const p of s.properties) {
21288
21288
  out += " <rdf:li rdf:parseType=\"Resource\">\n";
21289
- out += ` <pdfaProperty:name>${escapeXml$1(p.name)}</pdfaProperty:name>\n`;
21290
- out += ` <pdfaProperty:valueType>${escapeXml$1(p.valueType)}</pdfaProperty:valueType>\n`;
21289
+ out += ` <pdfaProperty:name>${escapeXml$2(p.name)}</pdfaProperty:name>\n`;
21290
+ out += ` <pdfaProperty:valueType>${escapeXml$2(p.valueType)}</pdfaProperty:valueType>\n`;
21291
21291
  out += ` <pdfaProperty:category>${p.category}</pdfaProperty:category>\n`;
21292
- out += ` <pdfaProperty:description>${escapeXml$1(p.description)}</pdfaProperty:description>\n`;
21292
+ out += ` <pdfaProperty:description>${escapeXml$2(p.description)}</pdfaProperty:description>\n`;
21293
21293
  out += " </rdf:li>\n";
21294
21294
  }
21295
21295
  out += " </rdf:Seq>\n";
@@ -21326,7 +21326,7 @@ function buildPdfA4Xmp(options) {
21326
21326
  xmp += " xmlns:dc=\"http://purl.org/dc/elements/1.1/\">\n";
21327
21327
  xmp += " <dc:title>\n";
21328
21328
  xmp += " <rdf:Alt>\n";
21329
- xmp += ` <rdf:li xml:lang="x-default">${escapeXml$1(title)}</rdf:li>\n`;
21329
+ xmp += ` <rdf:li xml:lang="x-default">${escapeXml$2(title)}</rdf:li>\n`;
21330
21330
  xmp += " </rdf:Alt>\n";
21331
21331
  xmp += " </dc:title>\n";
21332
21332
  xmp += " </rdf:Description>\n";
@@ -21400,7 +21400,7 @@ const ORDER_X_TYPE_CODES = {
21400
21400
  OrderResponse: ORDER_RESPONSE_TYPE_CODE
21401
21401
  };
21402
21402
  /** Escape XML special characters in a text value. */
21403
- function escapeXml(str) {
21403
+ function escapeXml$1(str) {
21404
21404
  return str.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll("\"", "&quot;").replaceAll("'", "&apos;");
21405
21405
  }
21406
21406
  /** Format a numeric monetary/quantity value with two decimal places. */
@@ -21457,13 +21457,13 @@ function computeTotals(lines) {
21457
21457
  /** Render a single trade party block (Seller or Buyer). */
21458
21458
  function renderParty(party, indent) {
21459
21459
  const out = [];
21460
- out.push(`${indent}<ram:Name>${escapeXml(party.name)}</ram:Name>`);
21460
+ out.push(`${indent}<ram:Name>${escapeXml$1(party.name)}</ram:Name>`);
21461
21461
  out.push(`${indent}<ram:PostalTradeAddress>`);
21462
- out.push(`${indent} <ram:CountryID>${escapeXml(party.countryCode)}</ram:CountryID>`);
21462
+ out.push(`${indent} <ram:CountryID>${escapeXml$1(party.countryCode)}</ram:CountryID>`);
21463
21463
  out.push(`${indent}</ram:PostalTradeAddress>`);
21464
21464
  if (party.vatId !== void 0 && party.vatId !== "") {
21465
21465
  out.push(`${indent}<ram:SpecifiedTaxRegistration>`);
21466
- out.push(`${indent} <ram:ID schemeID="VA">${escapeXml(party.vatId)}</ram:ID>`);
21466
+ out.push(`${indent} <ram:ID schemeID="VA">${escapeXml$1(party.vatId)}</ram:ID>`);
21467
21467
  out.push(`${indent}</ram:SpecifiedTaxRegistration>`);
21468
21468
  }
21469
21469
  return out.join("\n");
@@ -21477,7 +21477,7 @@ function renderLine$1(line, index, currency) {
21477
21477
  out.push(` <ram:LineID>${index}</ram:LineID>`);
21478
21478
  out.push(" </ram:AssociatedDocumentLineDocument>");
21479
21479
  out.push(" <ram:SpecifiedTradeProduct>");
21480
- out.push(` <ram:Name>${escapeXml(line.description)}</ram:Name>`);
21480
+ out.push(` <ram:Name>${escapeXml$1(line.description)}</ram:Name>`);
21481
21481
  out.push(" </ram:SpecifiedTradeProduct>");
21482
21482
  out.push(" <ram:SpecifiedLineTradeAgreement>");
21483
21483
  out.push(" <ram:NetPriceProductTradePrice>");
@@ -21494,7 +21494,7 @@ function renderLine$1(line, index, currency) {
21494
21494
  out.push(` <ram:RateApplicablePercent>${formatPercent(line.taxPercent)}</ram:RateApplicablePercent>`);
21495
21495
  out.push(" </ram:ApplicableTradeTax>");
21496
21496
  out.push(" <ram:SpecifiedTradeSettlementLineMonetarySummation>");
21497
- out.push(` <ram:LineTotalAmount currencyID="${escapeXml(currency)}">${formatAmount(net)}</ram:LineTotalAmount>`);
21497
+ out.push(` <ram:LineTotalAmount currencyID="${escapeXml$1(currency)}">${formatAmount(net)}</ram:LineTotalAmount>`);
21498
21498
  out.push(" </ram:SpecifiedTradeSettlementLineMonetarySummation>");
21499
21499
  out.push(" </ram:SpecifiedLineTradeSettlement>");
21500
21500
  out.push(" </ram:IncludedSupplyChainTradeLineItem>");
@@ -21504,9 +21504,9 @@ function renderLine$1(line, index, currency) {
21504
21504
  function renderTaxGroup(group, currency) {
21505
21505
  const out = [];
21506
21506
  out.push(" <ram:ApplicableTradeTax>");
21507
- out.push(` <ram:CalculatedAmount currencyID="${escapeXml(currency)}">${formatAmount(group.taxAmount)}</ram:CalculatedAmount>`);
21507
+ out.push(` <ram:CalculatedAmount currencyID="${escapeXml$1(currency)}">${formatAmount(group.taxAmount)}</ram:CalculatedAmount>`);
21508
21508
  out.push(" <ram:TypeCode>VAT</ram:TypeCode>");
21509
- out.push(` <ram:BasisAmount currencyID="${escapeXml(currency)}">${formatAmount(group.basisAmount)}</ram:BasisAmount>`);
21509
+ out.push(` <ram:BasisAmount currencyID="${escapeXml$1(currency)}">${formatAmount(group.basisAmount)}</ram:BasisAmount>`);
21510
21510
  out.push(` <ram:CategoryCode>${VAT_CATEGORY_STANDARD}</ram:CategoryCode>`);
21511
21511
  out.push(` <ram:RateApplicablePercent>${formatPercent(group.taxPercent)}</ram:RateApplicablePercent>`);
21512
21512
  out.push(" </ram:ApplicableTradeTax>");
@@ -21516,11 +21516,11 @@ function renderTaxGroup(group, currency) {
21516
21516
  function renderMonetarySummation(totals, currency) {
21517
21517
  const out = [];
21518
21518
  out.push(" <ram:SpecifiedTradeSettlementHeaderMonetarySummation>");
21519
- out.push(` <ram:LineTotalAmount currencyID="${escapeXml(currency)}">${formatAmount(totals.lineTotal)}</ram:LineTotalAmount>`);
21520
- out.push(` <ram:TaxBasisTotalAmount currencyID="${escapeXml(currency)}">${formatAmount(totals.lineTotal)}</ram:TaxBasisTotalAmount>`);
21521
- out.push(` <ram:TaxTotalAmount currencyID="${escapeXml(currency)}">${formatAmount(totals.taxTotal)}</ram:TaxTotalAmount>`);
21522
- out.push(` <ram:GrandTotalAmount currencyID="${escapeXml(currency)}">${formatAmount(totals.grandTotal)}</ram:GrandTotalAmount>`);
21523
- out.push(` <ram:DuePayableAmount currencyID="${escapeXml(currency)}">${formatAmount(totals.duePayable)}</ram:DuePayableAmount>`);
21519
+ out.push(` <ram:LineTotalAmount currencyID="${escapeXml$1(currency)}">${formatAmount(totals.lineTotal)}</ram:LineTotalAmount>`);
21520
+ out.push(` <ram:TaxBasisTotalAmount currencyID="${escapeXml$1(currency)}">${formatAmount(totals.lineTotal)}</ram:TaxBasisTotalAmount>`);
21521
+ out.push(` <ram:TaxTotalAmount currencyID="${escapeXml$1(currency)}">${formatAmount(totals.taxTotal)}</ram:TaxTotalAmount>`);
21522
+ out.push(` <ram:GrandTotalAmount currencyID="${escapeXml$1(currency)}">${formatAmount(totals.grandTotal)}</ram:GrandTotalAmount>`);
21523
+ out.push(` <ram:DuePayableAmount currencyID="${escapeXml$1(currency)}">${formatAmount(totals.duePayable)}</ram:DuePayableAmount>`);
21524
21524
  out.push(" </ram:SpecifiedTradeSettlementHeaderMonetarySummation>");
21525
21525
  return out.join("\n");
21526
21526
  }
@@ -21544,11 +21544,11 @@ function generateXRechnungCii(invoice, options = {}) {
21544
21544
  out.push("<rsm:CrossIndustryInvoice xmlns:rsm=\"urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100\" xmlns:ram=\"urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100\" xmlns:udt=\"urn:un:unece:uncefact:data:standard:UnqualifiedDataType:100\">");
21545
21545
  out.push(" <rsm:ExchangedDocumentContext>");
21546
21546
  out.push(" <ram:GuidelineSpecifiedDocumentContextParameter>");
21547
- out.push(` <ram:ID>${escapeXml(XRECHNUNG_GUIDELINE_URN)}</ram:ID>`);
21547
+ out.push(` <ram:ID>${escapeXml$1(XRECHNUNG_GUIDELINE_URN)}</ram:ID>`);
21548
21548
  out.push(" </ram:GuidelineSpecifiedDocumentContextParameter>");
21549
21549
  out.push(" </rsm:ExchangedDocumentContext>");
21550
21550
  out.push(" <rsm:ExchangedDocument>");
21551
- out.push(` <ram:ID>${escapeXml(invoice.invoiceNumber)}</ram:ID>`);
21551
+ out.push(` <ram:ID>${escapeXml$1(invoice.invoiceNumber)}</ram:ID>`);
21552
21552
  out.push(` <ram:TypeCode>${COMMERCIAL_INVOICE_TYPE_CODE}</ram:TypeCode>`);
21553
21553
  out.push(" <ram:IssueDateTime>");
21554
21554
  out.push(` <udt:DateTimeString format="102">${toDateString102(invoice.issueDate)}</udt:DateTimeString>`);
@@ -21557,7 +21557,7 @@ function generateXRechnungCii(invoice, options = {}) {
21557
21557
  out.push(" <rsm:SupplyChainTradeTransaction>");
21558
21558
  for (const [i, line] of invoice.lines.entries()) out.push(renderLine$1(line, i + 1, currency));
21559
21559
  out.push(" <ram:ApplicableHeaderTradeAgreement>");
21560
- if (buyerReference !== void 0 && buyerReference !== "") out.push(` <ram:BuyerReference>${escapeXml(buyerReference)}</ram:BuyerReference>`);
21560
+ if (buyerReference !== void 0 && buyerReference !== "") out.push(` <ram:BuyerReference>${escapeXml$1(buyerReference)}</ram:BuyerReference>`);
21561
21561
  out.push(" <ram:SellerTradeParty>");
21562
21562
  out.push(renderParty(invoice.seller, " "));
21563
21563
  out.push(" </ram:SellerTradeParty>");
@@ -21567,7 +21567,7 @@ function generateXRechnungCii(invoice, options = {}) {
21567
21567
  out.push(" </ram:ApplicableHeaderTradeAgreement>");
21568
21568
  out.push(" <ram:ApplicableHeaderTradeDelivery/>");
21569
21569
  out.push(" <ram:ApplicableHeaderTradeSettlement>");
21570
- out.push(` <ram:InvoiceCurrencyCode>${escapeXml(currency)}</ram:InvoiceCurrencyCode>`);
21570
+ out.push(` <ram:InvoiceCurrencyCode>${escapeXml$1(currency)}</ram:InvoiceCurrencyCode>`);
21571
21571
  for (const group of totals.taxGroups) out.push(renderTaxGroup(group, currency));
21572
21572
  out.push(renderMonetarySummation(totals, currency));
21573
21573
  out.push(" </ram:ApplicableHeaderTradeSettlement>");
@@ -21598,11 +21598,11 @@ function generateOrderX(invoice, orderType) {
21598
21598
  out.push("<rsm:SCRDMCCBDACIOMessageStructure xmlns:rsm=\"urn:un:unece:uncefact:data:standard:SCRDMCCBDACIOMessageStructure:100\" xmlns:ram=\"urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:128\" xmlns:udt=\"urn:un:unece:uncefact:data:standard:UnqualifiedDataType:128\">");
21599
21599
  out.push(" <rsm:ExchangedDocumentContext>");
21600
21600
  out.push(" <ram:GuidelineSpecifiedDocumentContextParameter>");
21601
- out.push(` <ram:ID>${escapeXml(ORDER_X_GUIDELINE_URN)}</ram:ID>`);
21601
+ out.push(` <ram:ID>${escapeXml$1(ORDER_X_GUIDELINE_URN)}</ram:ID>`);
21602
21602
  out.push(" </ram:GuidelineSpecifiedDocumentContextParameter>");
21603
21603
  out.push(" </rsm:ExchangedDocumentContext>");
21604
21604
  out.push(" <rsm:ExchangedDocument>");
21605
- out.push(` <ram:ID>${escapeXml(invoice.invoiceNumber)}</ram:ID>`);
21605
+ out.push(` <ram:ID>${escapeXml$1(invoice.invoiceNumber)}</ram:ID>`);
21606
21606
  out.push(` <ram:TypeCode>${typeCode}</ram:TypeCode>`);
21607
21607
  out.push(" <ram:IssueDateTime>");
21608
21608
  out.push(` <udt:DateTimeString format="102">${toDateString102(invoice.issueDate)}</udt:DateTimeString>`);
@@ -21620,7 +21620,7 @@ function generateOrderX(invoice, orderType) {
21620
21620
  out.push(" </ram:ApplicableHeaderTradeAgreement>");
21621
21621
  out.push(" <ram:ApplicableHeaderTradeDelivery/>");
21622
21622
  out.push(" <ram:ApplicableHeaderTradeSettlement>");
21623
- out.push(` <ram:OrderCurrencyCode>${escapeXml(currency)}</ram:OrderCurrencyCode>`);
21623
+ out.push(` <ram:OrderCurrencyCode>${escapeXml$1(currency)}</ram:OrderCurrencyCode>`);
21624
21624
  for (const group of totals.taxGroups) out.push(renderTaxGroup(group, currency));
21625
21625
  out.push(renderMonetarySummation(totals, currency));
21626
21626
  out.push(" </ram:ApplicableHeaderTradeSettlement>");
@@ -30416,6 +30416,823 @@ function buildBlackPointCompensationExtGState(mode) {
30416
30416
  return gs;
30417
30417
  }
30418
30418
  //#endregion
30419
+ //#region src/accessibility/taggingHelpers.ts
30420
+ /**
30421
+ * The property key under which {@link tagList} records the chosen
30422
+ * {@link ListNumbering} on a list element's `options` object.
30423
+ *
30424
+ * `StructureElementOptions` has no dedicated `listNumbering` field, so
30425
+ * the value is stored as an extra (string-keyed) property on the same
30426
+ * options object. When the structure tree is serialized this is the
30427
+ * value an attribute writer should emit as the list's `/ListNumbering`
30428
+ * entry inside an `/A` attribute dictionary whose owner is `/List`
30429
+ * (ISO 32000, Table 384):
30430
+ *
30431
+ * ```
30432
+ * /A << /O /List /ListNumbering /Decimal >>
30433
+ * ```
30434
+ *
30435
+ * Using a well-known string key (rather than a `Symbol`) keeps the
30436
+ * value plain-serializable and inspectable from tests.
30437
+ */
30438
+ const LIST_NUMBERING_KEY = "__listNumbering";
30439
+ /**
30440
+ * Heading level → structure-type mapping for {@link tagHeading}.
30441
+ *
30442
+ * Indexed by the numeric heading level (`1`..`6`); the result is the
30443
+ * corresponding standard heading structure type (`'H1'`..`'H6'`).
30444
+ */
30445
+ const HEADING_TYPE = {
30446
+ 1: "H1",
30447
+ 2: "H2",
30448
+ 3: "H3",
30449
+ 4: "H4",
30450
+ 5: "H5",
30451
+ 6: "H6"
30452
+ };
30453
+ /**
30454
+ * Tag a heading element of the given level.
30455
+ *
30456
+ * @param tree The structure tree to add the element to.
30457
+ * @param parent The parent element, or `null` to add under the root
30458
+ * `Document` element.
30459
+ * @param level The heading level (`1`..`6`); maps to `H1`..`H6`.
30460
+ * @param options Optional attributes (title, language, id, …).
30461
+ * @returns The newly created heading element (type `H1`..`H6`).
30462
+ *
30463
+ * @example
30464
+ * ```ts
30465
+ * const tree = doc.createStructureTree();
30466
+ * const h2 = tagHeading(tree, null, 2, { title: 'Background' });
30467
+ * // h2.type === 'H2'
30468
+ * ```
30469
+ */
30470
+ function tagHeading(tree, parent, level, options) {
30471
+ return tree.addElement(parent, HEADING_TYPE[level], options);
30472
+ }
30473
+ /**
30474
+ * Tag a paragraph (`P`) element.
30475
+ *
30476
+ * @param tree The structure tree to add the element to.
30477
+ * @param parent The parent element, or `null` for the root.
30478
+ * @param options Optional attributes.
30479
+ * @returns The newly created `P` element.
30480
+ */
30481
+ function tagParagraph(tree, parent, options) {
30482
+ return tree.addElement(parent, "P", options);
30483
+ }
30484
+ /**
30485
+ * Tag a figure (`Figure`) element with alternative text.
30486
+ *
30487
+ * The `altText` argument is required because PDF/UA mandates
30488
+ * alternative text for illustration elements. If the caller also
30489
+ * supplies `altText` in `options`, that explicit value takes
30490
+ * precedence over the positional argument.
30491
+ *
30492
+ * @param tree The structure tree to add the element to.
30493
+ * @param parent The parent element, or `null` for the root.
30494
+ * @param altText Alternative text describing the figure.
30495
+ * @param options Optional additional attributes.
30496
+ * @returns The newly created `Figure` element with `/Alt` set.
30497
+ */
30498
+ function tagFigure(tree, parent, altText, options) {
30499
+ const merged = {
30500
+ altText,
30501
+ ...options
30502
+ };
30503
+ return tree.addElement(parent, "Figure", merged);
30504
+ }
30505
+ /**
30506
+ * Tag a link (`Link`) element.
30507
+ *
30508
+ * @param tree The structure tree to add the element to.
30509
+ * @param parent The parent element, or `null` for the root.
30510
+ * @param options Optional attributes.
30511
+ * @returns The newly created `Link` element.
30512
+ */
30513
+ function tagLink(tree, parent, options) {
30514
+ return tree.addElement(parent, "Link", options);
30515
+ }
30516
+ /**
30517
+ * Tag a list (`L`) element and record its numbering style.
30518
+ *
30519
+ * The chosen {@link ListNumbering} is stored on the returned element's
30520
+ * `options` object under {@link LIST_NUMBERING_KEY} (since
30521
+ * `StructureElementOptions` has no dedicated field). A serializer can
30522
+ * emit it as the list's `/ListNumbering` attribute (ISO 32000,
30523
+ * Table 384) inside an `/A` dictionary owned by `/List`.
30524
+ *
30525
+ * @param tree The structure tree to add the element to.
30526
+ * @param parent The parent element, or `null` for the root.
30527
+ * @param numbering The list numbering style (default `'None'`).
30528
+ * @param options Optional additional attributes.
30529
+ * @returns The newly created `L` element.
30530
+ */
30531
+ function tagList(tree, parent, numbering = "None", options) {
30532
+ const list = tree.addElement(parent, "L", options);
30533
+ list.options[LIST_NUMBERING_KEY] = numbering;
30534
+ return list;
30535
+ }
30536
+ /**
30537
+ * Tag a list item under a list, building the conventional
30538
+ * `LI` → (`Lbl`, `LBody`) substructure.
30539
+ *
30540
+ * @param tree The structure tree to add the elements to.
30541
+ * @param list The parent `L` (list) element.
30542
+ * @param options Optional attributes for the `LI` element.
30543
+ * @returns The created `item` (`LI`), `label` (`Lbl`) and
30544
+ * `body` (`LBody`) elements.
30545
+ *
30546
+ * @example
30547
+ * ```ts
30548
+ * const list = tagList(tree, null, 'Decimal');
30549
+ * const { item, label, body } = tagListItem(tree, list);
30550
+ * // item.type === 'LI', label.type === 'Lbl', body.type === 'LBody'
30551
+ * ```
30552
+ */
30553
+ function tagListItem(tree, list, options) {
30554
+ const item = tree.addElement(list, "LI", options);
30555
+ return {
30556
+ item,
30557
+ label: tree.addElement(item, "Lbl"),
30558
+ body: tree.addElement(item, "LBody")
30559
+ };
30560
+ }
30561
+ /**
30562
+ * Tag a table (`Table`) element.
30563
+ *
30564
+ * @param tree The structure tree to add the element to.
30565
+ * @param parent The parent element, or `null` for the root.
30566
+ * @param options Optional attributes.
30567
+ * @returns The newly created `Table` element.
30568
+ */
30569
+ function tagTable(tree, parent, options) {
30570
+ return tree.addElement(parent, "Table", options);
30571
+ }
30572
+ /**
30573
+ * Tag a table row (`TR`) under a table.
30574
+ *
30575
+ * @param tree The structure tree to add the element to.
30576
+ * @param table The parent `Table` element.
30577
+ * @returns The newly created `TR` element.
30578
+ */
30579
+ function tagTableRow(tree, table) {
30580
+ return tree.addElement(table, "TR");
30581
+ }
30582
+ /**
30583
+ * Tag a table header cell (`TH`) under a row, recording its scope.
30584
+ *
30585
+ * The `scope` is stored in the element's options (`/Scope` attribute,
30586
+ * one of `Row`, `Column`, or `Both`) and is used by table-header
30587
+ * validation to associate header cells with the cells they describe.
30588
+ *
30589
+ * @param tree The structure tree to add the element to.
30590
+ * @param row The parent `TR` element.
30591
+ * @param scope The header scope (default `'Column'`).
30592
+ * @param options Optional additional attributes (e.g. colSpan/rowSpan).
30593
+ * @returns The newly created `TH` element with `scope` set.
30594
+ */
30595
+ function tagTableHeaderCell(tree, row, scope = "Column", options) {
30596
+ const merged = {
30597
+ ...options,
30598
+ scope
30599
+ };
30600
+ return tree.addElement(row, "TH", merged);
30601
+ }
30602
+ /**
30603
+ * Tag a table data cell (`TD`) under a row.
30604
+ *
30605
+ * @param tree The structure tree to add the element to.
30606
+ * @param row The parent `TR` element.
30607
+ * @param options Optional attributes (e.g. colSpan/rowSpan).
30608
+ * @returns The newly created `TD` element.
30609
+ */
30610
+ function tagTableDataCell(tree, row, options) {
30611
+ return tree.addElement(row, "TD", options);
30612
+ }
30613
+ //#endregion
30614
+ //#region src/accessibility/pdfUa2.ts
30615
+ /** The AIIM PDF/UA identification namespace URI (used in the XMP packet). */
30616
+ const PDFUA_ID_NS = "http://www.aiim.org/pdfua/ns/id/";
30617
+ /** The PDF/UA part identified by this module's XMP. */
30618
+ const PDFUA2_PART = 2;
30619
+ /**
30620
+ * The PDF/UA-2 revision year. ISO 14289-2 was published in 2024, which the
30621
+ * AIIM `pdfuaid:rev` field records as a four-digit year.
30622
+ */
30623
+ const PDFUA2_REV = 2024;
30624
+ /** Illustration types that require alternative text under PDF/UA-2. */
30625
+ const ILLUSTRATION_TYPES = /* @__PURE__ */ new Set([
30626
+ "Figure",
30627
+ "Formula",
30628
+ "Form"
30629
+ ]);
30630
+ /**
30631
+ * Validate a PDF document against PDF/UA-2 (ISO 14289-2) requirements.
30632
+ *
30633
+ * The check reuses {@link validatePdfUa} for the shared tagging, language,
30634
+ * MarkInfo, and metadata requirements, then layers the PDF 2.0 / UA-2
30635
+ * specific requirements on top:
30636
+ *
30637
+ * - **UA2-STRUCT-001** — a structure tree must exist (tagged PDF).
30638
+ * - **UA2-NS-001** — the structure tree must declare structure
30639
+ * `/Namespaces` (PDF/UA-2 builds on PDF 2.0 namespaces).
30640
+ * - **UA2-LANG-001** — the document must declare a natural language
30641
+ * (`/Lang`).
30642
+ * - **UA2-FIG-001** — every figure must carry alternative text.
30643
+ *
30644
+ * Each failure is mapped to an ISO 14289-2 clause string. The returned
30645
+ * result is `conformant` only when there are no issues.
30646
+ *
30647
+ * @param doc The PDF document to validate.
30648
+ * @returns A {@link PdfUa2Result} describing conformance and issues.
30649
+ *
30650
+ * @example
30651
+ * ```ts
30652
+ * import { createPdf } from 'modern-pdf-lib';
30653
+ * import { validatePdfUa2 } from 'modern-pdf-lib/accessibility';
30654
+ *
30655
+ * const doc = createPdf();
30656
+ * const result = validatePdfUa2(doc);
30657
+ * if (!result.conformant) {
30658
+ * for (const issue of result.issues) {
30659
+ * console.error(`[${issue.code}] ${issue.message} (§${issue.clause})`);
30660
+ * }
30661
+ * }
30662
+ * ```
30663
+ */
30664
+ function validatePdfUa2(doc) {
30665
+ const issues = [];
30666
+ const tree = doc.getStructureTree();
30667
+ if (!tree) issues.push({
30668
+ code: "UA2-STRUCT-001",
30669
+ message: "Document has no structure tree (/StructTreeRoot). PDF/UA-2 requires a fully tagged PDF 2.0 document with logical structure.",
30670
+ clause: "8.2"
30671
+ });
30672
+ if (!hasDeclaredNamespaces(tree)) issues.push({
30673
+ code: "UA2-NS-001",
30674
+ message: "Structure tree does not declare structure /Namespaces. PDF/UA-2 builds on PDF 2.0 structure namespaces (ISO 32000-2 §14.7.4); the StructTreeRoot must declare its namespaces.",
30675
+ clause: "8.2.2"
30676
+ });
30677
+ const lang = doc.getLanguage();
30678
+ if (lang === void 0 || lang.trim().length === 0) issues.push({
30679
+ code: "UA2-LANG-001",
30680
+ message: "Document has no natural language (/Lang). PDF/UA-2 requires the document catalog to specify a default natural language.",
30681
+ clause: "7.2"
30682
+ });
30683
+ if (tree) for (const elem of tree.getAllElements()) {
30684
+ if (!ILLUSTRATION_TYPES.has(elem.type)) continue;
30685
+ if (elem.options.artifact) continue;
30686
+ const alt = elem.options.altText;
30687
+ const actual = elem.options.actualText;
30688
+ const hasAlt = alt !== void 0 && alt.trim().length > 0;
30689
+ const hasActual = actual !== void 0 && actual.trim().length > 0;
30690
+ if (!hasAlt && !hasActual) issues.push({
30691
+ code: "UA2-FIG-001",
30692
+ message: `${elem.type} element has no /Alt or /ActualText. PDF/UA-2 requires alternative text for all non-decorative illustration elements.`,
30693
+ clause: "7.3"
30694
+ });
30695
+ }
30696
+ const ua1 = validatePdfUa(doc);
30697
+ for (const err of ua1.errors) {
30698
+ if (isCoveredByUa2(err.code)) continue;
30699
+ issues.push({
30700
+ code: `UA2-${err.code}`,
30701
+ message: err.message,
30702
+ clause: mapUa1Clause(err.clause)
30703
+ });
30704
+ }
30705
+ return {
30706
+ conformant: issues.length === 0,
30707
+ issues
30708
+ };
30709
+ }
30710
+ /**
30711
+ * Build an XMP packet (RDF/XML) that identifies a document as PDF/UA-2.
30712
+ *
30713
+ * The packet declares the AIIM PDF/UA identification schema
30714
+ * (`pdfuaid`, namespace `http://www.aiim.org/pdfua/ns/id/`) with:
30715
+ *
30716
+ * - `pdfuaid:part` = `2` — the PDF/UA part (ISO 14289-2);
30717
+ * - `pdfuaid:rev` = the revision year of the standard.
30718
+ *
30719
+ * The result is a serialized, packet-wrapped XMP string suitable for
30720
+ * embedding as the document's `/Metadata` stream.
30721
+ *
30722
+ * @returns The serialized PDF/UA-2 identification XMP packet.
30723
+ *
30724
+ * @example
30725
+ * ```ts
30726
+ * import { buildPdfUa2Xmp } from 'modern-pdf-lib/accessibility';
30727
+ *
30728
+ * doc.setXmpMetadata(buildPdfUa2Xmp());
30729
+ * ```
30730
+ */
30731
+ function buildPdfUa2Xmp() {
30732
+ return [
30733
+ "<?xpacket begin=\"\" id=\"W5M0MpCehiHzreSzNTczkc9d\"?>",
30734
+ "<x:xmpmeta xmlns:x=\"adobe:ns:meta/\">",
30735
+ " <rdf:RDF xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\">",
30736
+ ` <rdf:Description rdf:about=""`,
30737
+ ` xmlns:pdfuaid="${PDFUA_ID_NS}">`,
30738
+ ` <pdfuaid:part>${PDFUA2_PART}</pdfuaid:part>`,
30739
+ ` <pdfuaid:rev>${PDFUA2_REV}</pdfuaid:rev>`,
30740
+ " </rdf:Description>",
30741
+ " </rdf:RDF>",
30742
+ "</x:xmpmeta>",
30743
+ "<?xpacket end=\"w\"?>"
30744
+ ].join("\n");
30745
+ }
30746
+ /**
30747
+ * Determine whether a structure tree declares one or more structure
30748
+ * namespaces.
30749
+ *
30750
+ * The in-memory {@link PdfStructureTree} model does not yet carry a
30751
+ * first-class namespaces field, so callers declare namespaces by attaching
30752
+ * a `namespaces` array (of namespace URI strings, or namespace descriptor
30753
+ * objects) to the tree. This helper reads that field defensively and also
30754
+ * recognizes a per-element `namespace` option as evidence of namespace use.
30755
+ *
30756
+ * @internal
30757
+ */
30758
+ function hasDeclaredNamespaces(tree) {
30759
+ if (!tree) return false;
30760
+ const declared = tree.namespaces;
30761
+ if (Array.isArray(declared) && declared.length > 0) return true;
30762
+ for (const elem of tree.getAllElements()) {
30763
+ const ns = elem.options.namespace;
30764
+ if (typeof ns === "string" && ns.length > 0) return true;
30765
+ }
30766
+ return false;
30767
+ }
30768
+ /**
30769
+ * Whether a PDF/UA-1 error code is already represented by a dedicated
30770
+ * UA-2 issue produced above (to avoid duplicate structure-tree / language
30771
+ * findings).
30772
+ *
30773
+ * @internal
30774
+ */
30775
+ function isCoveredByUa2(ua1Code) {
30776
+ return ua1Code === "UA-STRUCT-001" || ua1Code === "UA-STRUCT-002" || ua1Code === "UA-META-001" || ua1Code === "UA-META-002" || ua1Code === "UA-META-003";
30777
+ }
30778
+ /**
30779
+ * Map a PDF/UA-1 (ISO 14289-1) clause reference onto the corresponding
30780
+ * PDF/UA-2 (ISO 14289-2) clause. The two standards share most clause
30781
+ * numbering; when a UA-1 clause is absent we fall back to the general
30782
+ * UA-2 conformance clause.
30783
+ *
30784
+ * @internal
30785
+ */
30786
+ function mapUa1Clause(ua1Clause) {
30787
+ if (ua1Clause === void 0 || ua1Clause.length === 0) return "5";
30788
+ return ua1Clause;
30789
+ }
30790
+ //#endregion
30791
+ //#region src/accessibility/autoTag.ts
30792
+ /** Default heading-detection scale relative to body text size. */
30793
+ const DEFAULT_HEADING_SCALE = 1.2;
30794
+ /**
30795
+ * Maximum vertical gap (in page-space units, as a fraction of the run's
30796
+ * font size) within which two text runs are considered to belong to the
30797
+ * same visual line. Runs whose baselines are within this fraction of an
30798
+ * em are merged onto one line.
30799
+ */
30800
+ const SAME_LINE_FRACTION = .5;
30801
+ /** Highest heading level tag supported. */
30802
+ const MAX_HEADING_LEVEL = 6;
30803
+ /**
30804
+ * Infer a coarse logical structure for a single untagged page and add the
30805
+ * inferred `H1`..`H6` and `P` elements to the document's structure tree.
30806
+ *
30807
+ * The procedure:
30808
+ * 1. Interpret the page into positioned text runs.
30809
+ * 2. Group runs into visual lines by their baseline y-position.
30810
+ * 3. Determine the body font size as the median run font size.
30811
+ * 4. Order lines top-to-bottom (reading order on a y-up page).
30812
+ * 5. Classify each line as a heading (font size `>= headingScale × body`)
30813
+ * or body text; map distinct heading sizes to `H1`..`H6`
30814
+ * (largest → `H1`).
30815
+ * 6. Emit one heading element per heading line and one `P` element per
30816
+ * contiguous block of body lines.
30817
+ *
30818
+ * Never throws on an empty (or text-free) page — it returns all-zero
30819
+ * counts and leaves the structure tree untouched.
30820
+ *
30821
+ * @param doc The document containing the page.
30822
+ * @param pageIndex Zero-based index of the page to tag.
30823
+ * @param options Optional heuristic tuning ({@link AutoTagOptions}).
30824
+ * @returns Counts of the elements that were added.
30825
+ */
30826
+ function autoTagPage(doc, pageIndex, options) {
30827
+ const headingScale = options?.headingScale ?? DEFAULT_HEADING_SCALE;
30828
+ const textItems = collectTextItems(doc, pageIndex);
30829
+ if (textItems.length === 0) return {
30830
+ headings: 0,
30831
+ paragraphs: 0,
30832
+ elements: 0
30833
+ };
30834
+ const lines = groupIntoLines(textItems);
30835
+ if (lines.length === 0) return {
30836
+ headings: 0,
30837
+ paragraphs: 0,
30838
+ elements: 0
30839
+ };
30840
+ const bodySize = medianFontSize(textItems);
30841
+ const ordered = [...lines].sort((a, b) => b.y - a.y);
30842
+ const headingThreshold = bodySize * headingScale;
30843
+ const headingLevelForSize = buildHeadingLevelMap(ordered, headingThreshold);
30844
+ const tree = doc.createStructureTree();
30845
+ let headings = 0;
30846
+ let paragraphs = 0;
30847
+ let paragraphOpen = false;
30848
+ for (const line of ordered) if (line.fontSize >= headingThreshold) {
30849
+ paragraphOpen = false;
30850
+ const type = `H${headingLevelForSize.get(line.fontSize) ?? 1}`;
30851
+ tree.addElement(null, type, { title: line.text });
30852
+ headings++;
30853
+ } else if (!paragraphOpen) {
30854
+ tree.addElement(null, "P");
30855
+ paragraphs++;
30856
+ paragraphOpen = true;
30857
+ }
30858
+ return {
30859
+ headings,
30860
+ paragraphs,
30861
+ elements: headings + paragraphs
30862
+ };
30863
+ }
30864
+ /**
30865
+ * Interpret the page and return only its text runs. Returns an empty
30866
+ * array (never throws) if the page cannot be interpreted.
30867
+ *
30868
+ * @internal
30869
+ */
30870
+ function collectTextItems(doc, pageIndex) {
30871
+ const pageCount = doc.getPageCount();
30872
+ if (pageIndex < 0 || pageIndex >= pageCount) return [];
30873
+ let items = [];
30874
+ try {
30875
+ items = interpretPage(doc.getPage(pageIndex)).items.filter((it) => it.type === "text" && it.text.trim().length > 0);
30876
+ } catch {
30877
+ return [];
30878
+ }
30879
+ return [...items];
30880
+ }
30881
+ /**
30882
+ * Group text runs into visual lines by their baseline y-position.
30883
+ *
30884
+ * The baseline is taken from the run's text-space → page-space transform
30885
+ * (`transform[5]`, the `f` translation component). Runs whose baselines
30886
+ * fall within {@link SAME_LINE_FRACTION} of an em of an existing line are
30887
+ * merged onto that line; the line's font size is the maximum run size.
30888
+ *
30889
+ * @internal
30890
+ */
30891
+ function groupIntoLines(items) {
30892
+ const sorted = [...items].sort((a, b) => b.transform[5] - a.transform[5]);
30893
+ const lines = [];
30894
+ for (const item of sorted) {
30895
+ const y = item.transform[5];
30896
+ const size = item.fontSize > 0 ? item.fontSize : Math.abs(item.transform[3]) || 1;
30897
+ const last = lines[lines.length - 1];
30898
+ const tolerance = size * SAME_LINE_FRACTION;
30899
+ if (last && Math.abs(last.y - y) <= tolerance) {
30900
+ last.text = `${last.text} ${item.text}`.trim();
30901
+ if (size > last.fontSize) last.fontSize = size;
30902
+ } else lines.push({
30903
+ y,
30904
+ fontSize: size,
30905
+ text: item.text.trim()
30906
+ });
30907
+ }
30908
+ return lines.map((l) => ({
30909
+ y: l.y,
30910
+ fontSize: l.fontSize,
30911
+ text: l.text
30912
+ }));
30913
+ }
30914
+ /**
30915
+ * Compute the median font size across all text runs. The median is more
30916
+ * robust than the mean against a handful of oversized heading runs, so it
30917
+ * gives a reliable estimate of the body text size.
30918
+ *
30919
+ * @internal
30920
+ */
30921
+ function medianFontSize(items) {
30922
+ const sizes = items.map((it) => it.fontSize > 0 ? it.fontSize : Math.abs(it.transform[3])).filter((s) => s > 0).sort((a, b) => a - b);
30923
+ if (sizes.length === 0) return 1;
30924
+ const mid = sizes.length >> 1;
30925
+ if (sizes.length % 2 === 1) return sizes[mid];
30926
+ return (sizes[mid - 1] + sizes[mid]) / 2;
30927
+ }
30928
+ /**
30929
+ * Build a map from each distinct heading font size to a heading level.
30930
+ * The largest heading size becomes `H1`, the next-largest `H2`, and so on,
30931
+ * capped at {@link MAX_HEADING_LEVEL} (`H6`).
30932
+ *
30933
+ * @internal
30934
+ */
30935
+ function buildHeadingLevelMap(lines, headingThreshold) {
30936
+ const distinct = [...new Set(lines.filter((l) => l.fontSize >= headingThreshold).map((l) => l.fontSize))].sort((a, b) => b - a);
30937
+ const map = /* @__PURE__ */ new Map();
30938
+ distinct.forEach((size, index) => {
30939
+ map.set(size, Math.min(index + 1, MAX_HEADING_LEVEL));
30940
+ });
30941
+ return map;
30942
+ }
30943
+ //#endregion
30944
+ //#region src/compliance/profileConvert.ts
30945
+ /** Canonical PDF/A identification namespace URI (`pdfaid`). */
30946
+ const PDFAID_NS = "http://www.aiim.org/pdfa/ns/id/";
30947
+ /**
30948
+ * Run a synchronous, pre-save PDF/A preflight over an in-memory document.
30949
+ *
30950
+ * See the module documentation for the (intentional) scope and limitations of
30951
+ * this check relative to the byte-level `validatePdfA` validator.
30952
+ *
30953
+ * @param doc - The in-memory document to inspect. Never mutated.
30954
+ * @param level - Optional target level (e.g. `'2b'`, `'3u'`, `'PDF/A-4'`).
30955
+ * Only used to refine messages; all checks here are
30956
+ * level-agnostic prerequisites common to every PDF/A part.
30957
+ * @returns A (possibly empty) list of preflight issues. Never throws.
30958
+ */
30959
+ function preflightPdfA(doc, level) {
30960
+ const issues = [];
30961
+ const target = level !== void 0 && level.length > 0 ? ` (${level})` : "";
30962
+ let xmp;
30963
+ try {
30964
+ xmp = doc.getXmpMetadata();
30965
+ } catch {
30966
+ xmp = void 0;
30967
+ }
30968
+ if (xmp === void 0 || xmp.length === 0) issues.push({
30969
+ code: "PDFA-001",
30970
+ message: `XMP metadata stream is required but not set on the document${target}.`,
30971
+ severity: "error",
30972
+ fixable: true,
30973
+ clause: "ISO 19005 (XMP metadata)"
30974
+ });
30975
+ else if (!xmp.includes("pdfaid:part")) issues.push({
30976
+ code: "PDFA-002",
30977
+ message: "XMP metadata does not declare PDF/A identification (pdfaid:part).",
30978
+ severity: "error",
30979
+ fixable: true,
30980
+ clause: "ISO 19005 (PDF/A identification schema)"
30981
+ });
30982
+ if (!documentHasOutputIntent(doc)) issues.push({
30983
+ code: "PDFA-012",
30984
+ message: "A PDF/A output intent (/OutputIntents) with an ICC destination profile is required (none detected on the in-memory document).",
30985
+ severity: "error",
30986
+ fixable: true,
30987
+ clause: "ISO 19005 (OutputIntents)"
30988
+ });
30989
+ let lang;
30990
+ try {
30991
+ lang = doc.getLanguage();
30992
+ } catch {
30993
+ lang = void 0;
30994
+ }
30995
+ if (lang === void 0 || lang.length === 0) issues.push({
30996
+ code: "PDFA-011",
30997
+ message: "The document language (/Lang) is not set; PDF/A recommends a BCP 47 language tag.",
30998
+ severity: "warning",
30999
+ fixable: true,
31000
+ clause: "ISO 19005 (document language)"
31001
+ });
31002
+ let title;
31003
+ try {
31004
+ title = doc.getTitle();
31005
+ } catch {
31006
+ title = void 0;
31007
+ }
31008
+ if (title === void 0 || title.length === 0) issues.push({
31009
+ code: "PDFA-014",
31010
+ message: "The document title is not set; PDF/A requires a dc:title.",
31011
+ severity: "warning",
31012
+ fixable: true,
31013
+ clause: "ISO 19005 (descriptive metadata)"
31014
+ });
31015
+ let pageCount = 0;
31016
+ try {
31017
+ pageCount = doc.getPageCount();
31018
+ } catch {
31019
+ pageCount = 0;
31020
+ }
31021
+ if (pageCount === 0) issues.push({
31022
+ code: "PDFA-015",
31023
+ message: "The document has no pages; a PDF/A file must contain at least one page.",
31024
+ severity: "error",
31025
+ fixable: false,
31026
+ clause: "ISO 32000 (page tree)"
31027
+ });
31028
+ return issues;
31029
+ }
31030
+ /**
31031
+ * Best-effort, pre-save detection of an output intent on a document.
31032
+ *
31033
+ * The public {@link PdfDocument} surface exposes no output-intent accessor, so
31034
+ * this currently always returns `false` for an in-memory document. It is
31035
+ * factored out so a future output-intent API can be wired in without touching
31036
+ * the issue-mapping logic.
31037
+ */
31038
+ function documentHasOutputIntent(_doc) {
31039
+ return false;
31040
+ }
31041
+ /** Map a numeric part to its decimal string. */
31042
+ function partString(part) {
31043
+ return String(part);
31044
+ }
31045
+ /** Map a conformance letter to the upper-case form stored in XMP. */
31046
+ function conformanceString(conformance) {
31047
+ return conformance.toUpperCase();
31048
+ }
31049
+ /**
31050
+ * Remap the PDF/A identification (`pdfaid:part`, and optionally
31051
+ * `pdfaid:conformance`) inside an existing XMP packet.
31052
+ *
31053
+ * Both the **attribute** form (`pdfaid:part="3"`) and the **element** form
31054
+ * (`<pdfaid:part>3</pdfaid:part>`) are handled. When no `pdfaid:part` is
31055
+ * present, identification is injected into the first `rdf:Description` element
31056
+ * (declaring the `pdfaid` namespace on it if necessary). All other content of
31057
+ * the packet is preserved verbatim.
31058
+ *
31059
+ * This is a pure string transform; it does not parse or re-serialize the XML.
31060
+ *
31061
+ * @param xmp - The source XMP packet string.
31062
+ * @param toPart - Target PDF/A part (1, 2, 3 or 4).
31063
+ * @param toConformance - Optional target conformance level (`a`/`b`/`u`). When
31064
+ * omitted, any existing conformance is left untouched.
31065
+ * @returns The transformed XMP packet string.
31066
+ */
31067
+ function convertPdfAConformanceXmp(xmp, toPart, toConformance) {
31068
+ const partVal = partString(toPart);
31069
+ let result = xmp;
31070
+ const partAttr = /pdfaid:part\s*=\s*"[^"]*"/;
31071
+ const partElem = /<pdfaid:part>[^<]*<\/pdfaid:part>/;
31072
+ if (partAttr.test(result)) result = result.replace(partAttr, `pdfaid:part="${partVal}"`);
31073
+ else if (partElem.test(result)) result = result.replace(partElem, `<pdfaid:part>${partVal}</pdfaid:part>`);
31074
+ else {
31075
+ result = injectIdentification(result, partVal, toConformance);
31076
+ return result;
31077
+ }
31078
+ if (toConformance !== void 0) {
31079
+ const confVal = conformanceString(toConformance);
31080
+ const confAttr = /pdfaid:conformance\s*=\s*"[^"]*"/;
31081
+ const confElem = /<pdfaid:conformance>[^<]*<\/pdfaid:conformance>/;
31082
+ if (confAttr.test(result)) result = result.replace(confAttr, `pdfaid:conformance="${confVal}"`);
31083
+ else if (confElem.test(result)) result = result.replace(confElem, `<pdfaid:conformance>${confVal}</pdfaid:conformance>`);
31084
+ else result = addConformanceBesidePart(result, confVal);
31085
+ }
31086
+ return result;
31087
+ }
31088
+ /**
31089
+ * Inject a fresh `pdfaid` identification block into the first `rdf:Description`
31090
+ * element. Used when the source packet has no `pdfaid:part` at all.
31091
+ */
31092
+ function injectIdentification(xmp, partVal, toConformance) {
31093
+ const nsDecl = `xmlns:pdfaid="${PDFAID_NS}"`;
31094
+ const hasNs = xmp.includes(PDFAID_NS);
31095
+ let frag = `<pdfaid:part>${partVal}</pdfaid:part>`;
31096
+ if (toConformance !== void 0) frag += `<pdfaid:conformance>${conformanceString(toConformance)}</pdfaid:conformance>`;
31097
+ const selfClosing = /<rdf:Description\b([^>]*?)\/>/;
31098
+ const scMatch = selfClosing.exec(xmp);
31099
+ if (scMatch) {
31100
+ const replacement = `<rdf:Description${scMatch[1] ?? ""}${hasNs ? "" : ` ${nsDecl}`}>${frag}</rdf:Description>`;
31101
+ return xmp.replace(selfClosing, replacement);
31102
+ }
31103
+ const open = /<rdf:Description\b([^>]*)>/;
31104
+ const openMatch = open.exec(xmp);
31105
+ if (openMatch) {
31106
+ const replacement = `<rdf:Description${openMatch[1] ?? ""}${hasNs ? "" : ` ${nsDecl}`}>${frag}`;
31107
+ return xmp.replace(open, replacement);
31108
+ }
31109
+ return xmp;
31110
+ }
31111
+ /**
31112
+ * Add a `pdfaid:conformance` next to an already-present `pdfaid:part`,
31113
+ * mirroring the part's representation (attribute vs element form).
31114
+ */
31115
+ function addConformanceBesidePart(xmp, confVal) {
31116
+ const partElem = /(<pdfaid:part>[^<]*<\/pdfaid:part>)/;
31117
+ if (partElem.test(xmp)) return xmp.replace(partElem, `$1<pdfaid:conformance>${confVal}</pdfaid:conformance>`);
31118
+ const partAttr = /(pdfaid:part\s*=\s*"[^"]*")/;
31119
+ if (partAttr.test(xmp)) return xmp.replace(partAttr, `$1 pdfaid:conformance="${confVal}"`);
31120
+ return xmp;
31121
+ }
31122
+ //#endregion
31123
+ //#region src/compliance/rasterProfile.ts
31124
+ /**
31125
+ * VERIFIED: PDF Association "PDF Declarations" namespace URI / prefix.
31126
+ * (Some published examples use the `https://` scheme; the `http://` form
31127
+ * is the one most commonly cited and is used here for stability.)
31128
+ */
31129
+ const PDFD_NS = "http://pdfa.org/declarations/";
31130
+ /**
31131
+ * PROVISIONAL: WTPDF declaration target URIs, keyed by conformance level.
31132
+ * The fragment form follows the PDF Association's corrected pattern
31133
+ * (`…/declarations/wtpdf#<level>1.0`) but is acknowledged as unsettled
31134
+ * (pdf-issues #395). Treat as identification hint, not normative match.
31135
+ */
31136
+ const WTPDF_CONFORMS_TO_BASE = "http://pdfa.org/declarations/wtpdf#";
31137
+ /**
31138
+ * PROVISIONAL: PDF/R declaration target URI. ISO 23504 / PDF/Raster has
31139
+ * no published PDF Declarations URI; this follows the same pattern and is
31140
+ * non-normative.
31141
+ */
31142
+ const PDFR_CONFORMS_TO = "http://pdfa.org/declarations/pdfr#1.0";
31143
+ /** Escape XML special characters in a string. */
31144
+ function escapeXml(str) {
31145
+ return str.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll("\"", "&quot;").replaceAll("'", "&apos;");
31146
+ }
31147
+ /**
31148
+ * Build an XMP packet carrying a single PDF Declaration plus a plain
31149
+ * `part`/`conformance` identification block.
31150
+ *
31151
+ * The packet is intentionally minimal: it declares the verified `pdfd`
31152
+ * PDF Declarations namespace and structure, embeds the (provisional)
31153
+ * `conformsTo` target, and additionally renders the supplied `part` and
31154
+ * `conformance` values under a clearly-named, profile-local namespace so
31155
+ * that the identification facts are machine-readable even though no ISO
31156
+ * identification namespace exists for these profiles.
31157
+ */
31158
+ function buildIdentificationXmp(identity) {
31159
+ const { profileName, conformsTo, part, conformance } = identity;
31160
+ let xmp = "<?xpacket begin=\"\" id=\"W5M0MpCehiHzreSzNTczkc9d\"?>\n";
31161
+ xmp += "<x:xmpmeta xmlns:x=\"adobe:ns:meta/\">\n";
31162
+ xmp += ` <!-- ${escapeXml(profileName)} identification marker (non-normative). -->\n`;
31163
+ xmp += " <rdf:RDF xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\">\n";
31164
+ xmp += " <rdf:Description rdf:about=\"\"\n";
31165
+ xmp += ` xmlns:pdfd="${PDFD_NS}">\n`;
31166
+ xmp += " <pdfd:declarations>\n";
31167
+ xmp += " <rdf:Bag>\n";
31168
+ xmp += " <rdf:li rdf:parseType=\"Resource\">\n";
31169
+ xmp += ` <pdfd:conformsTo>${escapeXml(conformsTo)}</pdfd:conformsTo>\n`;
31170
+ xmp += " </rdf:li>\n";
31171
+ xmp += " </rdf:Bag>\n";
31172
+ xmp += " </pdfd:declarations>\n";
31173
+ xmp += " </rdf:Description>\n";
31174
+ xmp += " <rdf:Description rdf:about=\"\"\n";
31175
+ xmp += " xmlns:pid=\"http://modern-pdf-lib/ns/profile-id/\">\n";
31176
+ xmp += ` <pid:part>${part}</pid:part>\n`;
31177
+ if (conformance !== void 0) xmp += ` <pid:conformance>${escapeXml(conformance)}</pid:conformance>\n`;
31178
+ xmp += " </rdf:Description>\n";
31179
+ xmp += " </rdf:RDF>\n";
31180
+ xmp += "</x:xmpmeta>\n";
31181
+ xmp += "<?xpacket end=\"w\"?>";
31182
+ return xmp;
31183
+ }
31184
+ /**
31185
+ * Build a WTPDF ("Well-Tagged PDF") identification XMP packet.
31186
+ *
31187
+ * WTPDF conformance is asserted through the PDF Association's PDF
31188
+ * Declarations mechanism (VERIFIED). The `conformsTo` target URI is
31189
+ * PROVISIONAL — see the module doc comment and pdf-issues #395 for the
31190
+ * acknowledged inconsistency in its exact form. This builder produces an
31191
+ * identification marker only and does not assert or validate WTPDF
31192
+ * conformance.
31193
+ *
31194
+ * @param options - Optional part (default `1`, i.e. WTPDF 1.0) and a
31195
+ * conformance level (WTPDF defines `'reuse'` and `'accessibility'`).
31196
+ * @returns A serialized XMP/RDF identification packet.
31197
+ */
31198
+ function buildWtpdfIdentificationXmp(options) {
31199
+ const part = options?.part ?? 1;
31200
+ const conformance = options?.conformance;
31201
+ let conformsTo = `${WTPDF_CONFORMS_TO_BASE}1.0`;
31202
+ if (conformance === "reuse" || conformance === "accessibility") conformsTo = `${WTPDF_CONFORMS_TO_BASE}${conformance}1.0`;
31203
+ return buildIdentificationXmp({
31204
+ profileName: "WTPDF (Well-Tagged PDF) 1.0",
31205
+ conformsTo,
31206
+ part,
31207
+ conformance
31208
+ });
31209
+ }
31210
+ /**
31211
+ * Build a PDF/R (ISO 23504 / PDF/Raster) identification XMP packet.
31212
+ *
31213
+ * IMPORTANT: PDF/R is normatively identified by a comment marker before
31214
+ * the final `startxref` in the file trailer, NOT by XMP. There is no
31215
+ * ISO-defined XMP identification namespace for PDF/R, so this XMP marker
31216
+ * is non-normative: the `pdfd:conformsTo` target is PROVISIONAL. Use this
31217
+ * only as a supplementary, machine-readable hint alongside the required
31218
+ * trailer comment marker; it does not assert or validate PDF/R
31219
+ * conformance.
31220
+ *
31221
+ * @param options - Optional part (default `1`, i.e. ISO 23504-1 / PDF/R-1)
31222
+ * and a conformance token.
31223
+ * @returns A serialized XMP/RDF identification packet.
31224
+ */
31225
+ function buildPdfRIdentificationXmp(options) {
31226
+ const part = options?.part ?? 1;
31227
+ const conformance = options?.conformance;
31228
+ return buildIdentificationXmp({
31229
+ profileName: "PDF/R (ISO 23504, raster image transport and storage)",
31230
+ conformsTo: PDFR_CONFORMS_TO,
31231
+ part,
31232
+ conformance
31233
+ });
31234
+ }
31235
+ //#endregion
30419
31236
  //#region src/index.ts
30420
31237
  /** Whether initWasm has already completed successfully. */
30421
31238
  let wasmInitialized = false;
@@ -30569,6 +31386,12 @@ Object.defineProperty(exports, "InvalidPageSizeError", {
30569
31386
  return InvalidPageSizeError;
30570
31387
  }
30571
31388
  });
31389
+ Object.defineProperty(exports, "LIST_NUMBERING_KEY", {
31390
+ enumerable: true,
31391
+ get: function() {
31392
+ return LIST_NUMBERING_KEY;
31393
+ }
31394
+ });
30572
31395
  Object.defineProperty(exports, "MATHML_NAMESPACE", {
30573
31396
  enumerable: true,
30574
31397
  get: function() {
@@ -30893,6 +31716,12 @@ Object.defineProperty(exports, "attachOutputIntents", {
30893
31716
  return attachOutputIntents;
30894
31717
  }
30895
31718
  });
31719
+ Object.defineProperty(exports, "autoTagPage", {
31720
+ enumerable: true,
31721
+ get: function() {
31722
+ return autoTagPage;
31723
+ }
31724
+ });
30896
31725
  Object.defineProperty(exports, "batchFlatten", {
30897
31726
  enumerable: true,
30898
31727
  get: function() {
@@ -31055,6 +31884,18 @@ Object.defineProperty(exports, "buildPdfA4Xmp", {
31055
31884
  return buildPdfA4Xmp;
31056
31885
  }
31057
31886
  });
31887
+ Object.defineProperty(exports, "buildPdfRIdentificationXmp", {
31888
+ enumerable: true,
31889
+ get: function() {
31890
+ return buildPdfRIdentificationXmp;
31891
+ }
31892
+ });
31893
+ Object.defineProperty(exports, "buildPdfUa2Xmp", {
31894
+ enumerable: true,
31895
+ get: function() {
31896
+ return buildPdfUa2Xmp;
31897
+ }
31898
+ });
31058
31899
  Object.defineProperty(exports, "buildPdfVtDParts", {
31059
31900
  enumerable: true,
31060
31901
  get: function() {
@@ -31157,6 +31998,12 @@ Object.defineProperty(exports, "buildVtDpm", {
31157
31998
  return buildVtDpm;
31158
31999
  }
31159
32000
  });
32001
+ Object.defineProperty(exports, "buildWtpdfIdentificationXmp", {
32002
+ enumerable: true,
32003
+ get: function() {
32004
+ return buildWtpdfIdentificationXmp;
32005
+ }
32006
+ });
31160
32007
  Object.defineProperty(exports, "buildXmpMetadata", {
31161
32008
  enumerable: true,
31162
32009
  get: function() {
@@ -31235,6 +32082,12 @@ Object.defineProperty(exports, "computeTileGrid", {
31235
32082
  return computeTileGrid;
31236
32083
  }
31237
32084
  });
32085
+ Object.defineProperty(exports, "convertPdfAConformanceXmp", {
32086
+ enumerable: true,
32087
+ get: function() {
32088
+ return convertPdfAConformanceXmp;
32089
+ }
32090
+ });
31238
32091
  Object.defineProperty(exports, "convertTiffCmykToRgb", {
31239
32092
  enumerable: true,
31240
32093
  get: function() {
@@ -31991,6 +32844,12 @@ Object.defineProperty(exports, "pdfA4Rules", {
31991
32844
  return pdfA4Rules;
31992
32845
  }
31993
32846
  });
32847
+ Object.defineProperty(exports, "preflightPdfA", {
32848
+ enumerable: true,
32849
+ get: function() {
32850
+ return preflightPdfA;
32851
+ }
32852
+ });
31994
32853
  Object.defineProperty(exports, "processBatch", {
31995
32854
  enumerable: true,
31996
32855
  get: function() {
@@ -32255,6 +33114,66 @@ Object.defineProperty(exports, "tableToJson", {
32255
33114
  return tableToJson;
32256
33115
  }
32257
33116
  });
33117
+ Object.defineProperty(exports, "tagFigure", {
33118
+ enumerable: true,
33119
+ get: function() {
33120
+ return tagFigure;
33121
+ }
33122
+ });
33123
+ Object.defineProperty(exports, "tagHeading", {
33124
+ enumerable: true,
33125
+ get: function() {
33126
+ return tagHeading;
33127
+ }
33128
+ });
33129
+ Object.defineProperty(exports, "tagLink", {
33130
+ enumerable: true,
33131
+ get: function() {
33132
+ return tagLink;
33133
+ }
33134
+ });
33135
+ Object.defineProperty(exports, "tagList", {
33136
+ enumerable: true,
33137
+ get: function() {
33138
+ return tagList;
33139
+ }
33140
+ });
33141
+ Object.defineProperty(exports, "tagListItem", {
33142
+ enumerable: true,
33143
+ get: function() {
33144
+ return tagListItem;
33145
+ }
33146
+ });
33147
+ Object.defineProperty(exports, "tagParagraph", {
33148
+ enumerable: true,
33149
+ get: function() {
33150
+ return tagParagraph;
33151
+ }
33152
+ });
33153
+ Object.defineProperty(exports, "tagTable", {
33154
+ enumerable: true,
33155
+ get: function() {
33156
+ return tagTable;
33157
+ }
33158
+ });
33159
+ Object.defineProperty(exports, "tagTableDataCell", {
33160
+ enumerable: true,
33161
+ get: function() {
33162
+ return tagTableDataCell;
33163
+ }
33164
+ });
33165
+ Object.defineProperty(exports, "tagTableHeaderCell", {
33166
+ enumerable: true,
33167
+ get: function() {
33168
+ return tagTableHeaderCell;
33169
+ }
33170
+ });
33171
+ Object.defineProperty(exports, "tagTableRow", {
33172
+ enumerable: true,
33173
+ get: function() {
33174
+ return tagTableRow;
33175
+ }
33176
+ });
32258
33177
  Object.defineProperty(exports, "timestampPlugin", {
32259
33178
  enumerable: true,
32260
33179
  get: function() {
@@ -32357,6 +33276,12 @@ Object.defineProperty(exports, "validatePdfUa", {
32357
33276
  return validatePdfUa;
32358
33277
  }
32359
33278
  });
33279
+ Object.defineProperty(exports, "validatePdfUa2", {
33280
+ enumerable: true,
33281
+ get: function() {
33282
+ return validatePdfUa2;
33283
+ }
33284
+ });
32360
33285
  Object.defineProperty(exports, "validatePdfX", {
32361
33286
  enumerable: true,
32362
33287
  get: function() {
@@ -32412,4 +33337,4 @@ Object.defineProperty(exports, "wrapText", {
32412
33337
  }
32413
33338
  });
32414
33339
 
32415
- //# sourceMappingURL=src-Tb7wHOKz.cjs.map
33340
+ //# sourceMappingURL=src-CeKIKI1W.cjs.map