@stackforge-eu/factur-x 1.0.7 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +42 -0
- package/dist/index.cjs +33 -5
- package/dist/index.d.cts +22 -5
- package/dist/index.d.ts +22 -5
- package/dist/index.js +34 -6
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,46 @@ and this project adheres to [Semantic Versioning](https://semver.org/).
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [1.1.0] — 2026-05-27
|
|
11
|
+
|
|
12
|
+
- **BREAKING:** Every `ram:GlobalID` and the legal-organization `ram:ID` now
|
|
13
|
+
carry the `@schemeID` attribute required by EN 16931. The XSD-canonical CII
|
|
14
|
+
mapping makes the scheme mandatory on `ram:GlobalID` (BT-29-1 / BT-46-1 for
|
|
15
|
+
parties, BT-157-1 for line items) and conditional on
|
|
16
|
+
`ram:SpecifiedLegalOrganization/ram:ID` (BT-30-1 / BT-47-1); without these
|
|
17
|
+
attributes Mustang / KoSIT / veraPDF's Factur-X profile reject the invoice
|
|
18
|
+
even though the embedded CII looked superficially correct. Three input
|
|
19
|
+
fields changed:
|
|
20
|
+
- `TradePartyInput.globalId` is now `IdentifierWithSchemeInput`
|
|
21
|
+
(`{ value: string; schemeID: string }`) instead of a bare `string`
|
|
22
|
+
- `InvoiceLineInput.standardIdentifier` is now `IdentifierWithSchemeInput`
|
|
23
|
+
instead of a bare `string` (sibling fix on top of #4 — the line-item
|
|
24
|
+
`ram:GlobalID` had the same defect as the party one)
|
|
25
|
+
- `LegalOrganizationInput.schemeID?: string` added (optional, mirrors the
|
|
26
|
+
conditional spec rule)
|
|
27
|
+
Migration: `globalId: "4000001000005"` →
|
|
28
|
+
`globalId: { value: "4000001000005", schemeID: "0088" }` (and the analogous
|
|
29
|
+
rewrite for `standardIdentifier`). The new `IdentifierWithSchemeInput` type
|
|
30
|
+
is exported from `src/index.ts`. Regression test added in
|
|
31
|
+
`tests/xsd-validator.test.ts` that drives all three schemed identifiers
|
|
32
|
+
through the EN16931 XSD in one go (thanks @sco-indy, #4).
|
|
33
|
+
|
|
34
|
+
## [1.0.8] — 2026-05-27
|
|
35
|
+
|
|
36
|
+
- **PDF trailer was missing the `/ID` entry required by PDF/A-3.** ISO 19005-3
|
|
37
|
+
(and PDF 1.7 §14.4) require the trailer dictionary to carry an `/ID` array of
|
|
38
|
+
two byte strings: the first immutable identifier of the original document,
|
|
39
|
+
the second updated on every modification. `pdf-lib` initializes
|
|
40
|
+
`trailerInfo = {}` and never writes one on save, so veraPDF and the FNFE-MPE
|
|
41
|
+
Factur-X profile rejected every PDF this library produced as non-conformant.
|
|
42
|
+
Added `ensureIdTrailer` in `src/core/embed.ts` which runs after the first
|
|
43
|
+
`pdfDoc.save()` when `addPdfA3Metadata` is on: it computes an MD5 over the
|
|
44
|
+
serialized bytes (per PDF 1.7 §14.4 — uniqueness, not cryptographic strength,
|
|
45
|
+
is what matters), preserves any pre-existing first identifier, and writes the
|
|
46
|
+
pair before re-saving. Regression tests added in `tests/embed.test.ts`
|
|
47
|
+
covering both the no-existing-trailer and existing-trailer paths (thanks
|
|
48
|
+
@sco-indy, #3).
|
|
49
|
+
|
|
10
50
|
## [1.0.7] — 2026-05-27
|
|
11
51
|
|
|
12
52
|
- **`fx:ConformanceLevel` in the XMP metadata used the raw `Profile` enum
|
|
@@ -113,6 +153,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/).
|
|
|
113
153
|
- Deno compatibility test suite
|
|
114
154
|
|
|
115
155
|
[Unreleased]: https://github.com/StackForge-EU/factur-x/commits/main
|
|
156
|
+
[1.1.0]: https://github.com/StackForge-EU/factur-x/compare/v1.0.8...v1.1.0
|
|
157
|
+
[1.0.8]: https://github.com/StackForge-EU/factur-x/compare/v1.0.7...v1.0.8
|
|
116
158
|
[1.0.7]: https://github.com/StackForge-EU/factur-x/compare/v1.0.6...v1.0.7
|
|
117
159
|
[1.0.6]: https://github.com/StackForge-EU/factur-x/compare/v1.0.5...v1.0.6
|
|
118
160
|
[1.0.5]: https://github.com/StackForge-EU/factur-x/compare/v1.0.4...v1.0.5
|
package/dist/index.cjs
CHANGED
|
@@ -4,6 +4,7 @@ var fs = require('fs');
|
|
|
4
4
|
var path = require('path');
|
|
5
5
|
var url = require('url');
|
|
6
6
|
var console = require('console');
|
|
7
|
+
var crypto = require('crypto');
|
|
7
8
|
var pdfLib = require('pdf-lib');
|
|
8
9
|
|
|
9
10
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
@@ -339,13 +340,21 @@ function buildParty(p, profile) {
|
|
|
339
340
|
const ids = Array.isArray(p.id) ? p.id : [p.id];
|
|
340
341
|
for (const id of ids) x += tag("ram:ID", escapeXml(id));
|
|
341
342
|
}
|
|
342
|
-
if (p.globalId)
|
|
343
|
+
if (p.globalId) {
|
|
344
|
+
x += tag("ram:GlobalID", escapeXml(p.globalId.value), { schemeID: p.globalId.schemeID });
|
|
345
|
+
}
|
|
343
346
|
x += tag("ram:Name", escapeXml(p.name));
|
|
344
347
|
if (p.description && atLeast(profile, "EN16931" /* EN16931 */))
|
|
345
348
|
x += tag("ram:Description", escapeXml(p.description));
|
|
346
349
|
if (p.legalOrganization) {
|
|
347
350
|
let lo = "";
|
|
348
|
-
if (p.legalOrganization.id)
|
|
351
|
+
if (p.legalOrganization.id) {
|
|
352
|
+
lo += tag(
|
|
353
|
+
"ram:ID",
|
|
354
|
+
escapeXml(p.legalOrganization.id),
|
|
355
|
+
p.legalOrganization.schemeID ? { schemeID: p.legalOrganization.schemeID } : void 0
|
|
356
|
+
);
|
|
357
|
+
}
|
|
349
358
|
if (p.legalOrganization.tradingName)
|
|
350
359
|
lo += tag("ram:TradingBusinessName", escapeXml(p.legalOrganization.tradingName));
|
|
351
360
|
x += tag("ram:SpecifiedLegalOrganization", lo);
|
|
@@ -373,7 +382,11 @@ function buildLineItem(line, profile) {
|
|
|
373
382
|
let doc = tag("ram:LineID", escapeXml(line.id));
|
|
374
383
|
if (line.note) doc += tag("ram:IncludedNote", tag("ram:Content", escapeXml(line.note)));
|
|
375
384
|
let prod = "";
|
|
376
|
-
if (line.standardIdentifier)
|
|
385
|
+
if (line.standardIdentifier) {
|
|
386
|
+
prod += tag("ram:GlobalID", escapeXml(line.standardIdentifier.value), {
|
|
387
|
+
schemeID: line.standardIdentifier.schemeID
|
|
388
|
+
});
|
|
389
|
+
}
|
|
377
390
|
if (line.sellerAssignedId) prod += tag("ram:SellerAssignedID", escapeXml(line.sellerAssignedId));
|
|
378
391
|
if (line.buyerAssignedId) prod += tag("ram:BuyerAssignedID", escapeXml(line.buyerAssignedId));
|
|
379
392
|
prod += tag("ram:Name", escapeXml(line.name));
|
|
@@ -1122,6 +1135,17 @@ function resolveXmpConformanceLevel(profile, flavor) {
|
|
|
1122
1135
|
if (flavor === "xrechnung" /* XRECHNUNG */) return "XRECHNUNG";
|
|
1123
1136
|
return XMP_CONFORMANCE_LEVEL[profile];
|
|
1124
1137
|
}
|
|
1138
|
+
function ensureIdTrailer(pdfDoc, pdfContentBytes) {
|
|
1139
|
+
const existingIdTrailer = pdfDoc.context.trailerInfo.ID;
|
|
1140
|
+
const currentContentHash = pdfLib.PDFHexString.of(crypto.createHash("md5").update(pdfContentBytes).digest("hex"));
|
|
1141
|
+
const hasValidIdTrailer = existingIdTrailer instanceof pdfLib.PDFArray && existingIdTrailer.size() === 2;
|
|
1142
|
+
if (hasValidIdTrailer) {
|
|
1143
|
+
const existingFirstId = existingIdTrailer.lookup(0);
|
|
1144
|
+
pdfDoc.context.trailerInfo.ID = pdfDoc.context.obj([existingFirstId, currentContentHash]);
|
|
1145
|
+
} else {
|
|
1146
|
+
pdfDoc.context.trailerInfo.ID = pdfDoc.context.obj([currentContentHash, currentContentHash]);
|
|
1147
|
+
}
|
|
1148
|
+
}
|
|
1125
1149
|
function ensureOutputIntent(pdfDoc, iccBytes, identifier) {
|
|
1126
1150
|
const existing = pdfDoc.catalog.lookup(pdfLib.PDFName.of("OutputIntents"));
|
|
1127
1151
|
if (existing instanceof pdfLib.PDFArray && existing.size() > 0) return;
|
|
@@ -1397,9 +1421,13 @@ async function embedFacturX(options) {
|
|
|
1397
1421
|
const metadataRef = pdfDoc.context.register(metadataStream);
|
|
1398
1422
|
pdfDoc.catalog.set(pdfLib.PDFName.of("Metadata"), metadataRef);
|
|
1399
1423
|
}
|
|
1400
|
-
|
|
1424
|
+
let pdfContentBytes = await pdfDoc.save();
|
|
1425
|
+
if (addPdfA3Metadata) {
|
|
1426
|
+
ensureIdTrailer(pdfDoc, pdfContentBytes);
|
|
1427
|
+
pdfContentBytes = await pdfDoc.save();
|
|
1428
|
+
}
|
|
1401
1429
|
return {
|
|
1402
|
-
pdf:
|
|
1430
|
+
pdf: pdfContentBytes,
|
|
1403
1431
|
xml,
|
|
1404
1432
|
...validation ? { validation } : {},
|
|
1405
1433
|
...xsdValidation ? { xsdValidation } : {}
|
package/dist/index.d.cts
CHANGED
|
@@ -448,9 +448,18 @@ interface TaxRegistrationInput {
|
|
|
448
448
|
interface LegalOrganizationInput {
|
|
449
449
|
/** Registration identifier */
|
|
450
450
|
id?: string;
|
|
451
|
+
/** Registration identifier scheme (ICD code list value, e.g. `"0002"` for SIRENE) */
|
|
452
|
+
schemeID?: string;
|
|
451
453
|
/** Trading business name */
|
|
452
454
|
tradingName?: string;
|
|
453
455
|
}
|
|
456
|
+
/**
|
|
457
|
+
* Identifier value with its scheme identifier.
|
|
458
|
+
*/
|
|
459
|
+
interface IdentifierWithSchemeInput {
|
|
460
|
+
value: string;
|
|
461
|
+
schemeID: string;
|
|
462
|
+
}
|
|
454
463
|
/**
|
|
455
464
|
* Trade party (seller, buyer, payee, etc.).
|
|
456
465
|
*
|
|
@@ -468,10 +477,14 @@ interface TradePartyInput {
|
|
|
468
477
|
*/
|
|
469
478
|
id?: string | string[];
|
|
470
479
|
/**
|
|
471
|
-
* Global identifier with scheme (e.g. GLN `"
|
|
480
|
+
* Global identifier with scheme (e.g. GLN value `"4000001000005"` and
|
|
481
|
+
* ICD scheme `"0088"`).
|
|
482
|
+
*
|
|
483
|
+
* `schemeID` is required by EN 16931 for `ram:GlobalID`.
|
|
484
|
+
*
|
|
472
485
|
* @see EN 16931 BT-29-1 / BT-46-1
|
|
473
486
|
*/
|
|
474
|
-
globalId?:
|
|
487
|
+
globalId?: IdentifierWithSchemeInput;
|
|
475
488
|
/** Postal address — mandatory for BASIC_WL and above */
|
|
476
489
|
address?: AddressInput;
|
|
477
490
|
/**
|
|
@@ -581,10 +594,14 @@ interface InvoiceLineInput {
|
|
|
581
594
|
*/
|
|
582
595
|
description?: string;
|
|
583
596
|
/**
|
|
584
|
-
* Item standard identifier (e.g. GTIN
|
|
597
|
+
* Item standard identifier (e.g. GTIN value `"4012345678901"` and ICD
|
|
598
|
+
* scheme `"0160"` for GS1).
|
|
599
|
+
*
|
|
600
|
+
* `schemeID` is required by EN 16931 for `ram:GlobalID` (BT-157-1).
|
|
601
|
+
*
|
|
585
602
|
* @see EN 16931 BT-157
|
|
586
603
|
*/
|
|
587
|
-
standardIdentifier?:
|
|
604
|
+
standardIdentifier?: IdentifierWithSchemeInput;
|
|
588
605
|
/**
|
|
589
606
|
* Seller-assigned item identifier.
|
|
590
607
|
* @see EN 16931 BT-155
|
|
@@ -1435,4 +1452,4 @@ interface XRechnungResult {
|
|
|
1435
1452
|
*/
|
|
1436
1453
|
declare function toXRechnung(input: FacturXInvoiceInput, options?: XRechnungOptions): XRechnungResult;
|
|
1437
1454
|
|
|
1438
|
-
export { type AddressInput, type AllowanceChargeInput, type BillingPeriodInput, type ContactInput, type DeliveryInput, type DocumentReferenceInput, DocumentTypeCode, type EmbedOptions, type EmbedResult, type ExtractOptions, type ExtractResult, FLAVOR_CONFIGS, FLAVOR_DEFAULT_TYPE_CODES, type FacturXInvoiceInput, Flavor, type FlavorConfig, type InvoiceDocumentInput, type InvoiceLineInput, type InvoiceTotalsInput, type LegalOrganizationInput, type NoteInput, PROFILE_MAIN_XSD, PROFILE_SCHEMA_DIRS, PROFILE_URNS, type PaymentInput, Profile, type TaxRegistrationInput, type TradePartyInput, UnitCode, type ValidationError, type ValidationResult, type VatBreakdownInput, VatCategoryCode, XRECHNUNG_PROFILE_URN, type XRechnungOptions, type XRechnungResult, type XsdValidateOptions, type XsdValidationError, type XsdValidationResult, buildXml, embedFacturX, escapeXml, extractXml, getFlavorConfig, resolveBusinessProcessUrn, resolveTypeCode, toXRechnung, validateFlavorProfile, validateInput, validateXsd };
|
|
1455
|
+
export { type AddressInput, type AllowanceChargeInput, type BillingPeriodInput, type ContactInput, type DeliveryInput, type DocumentReferenceInput, DocumentTypeCode, type EmbedOptions, type EmbedResult, type ExtractOptions, type ExtractResult, FLAVOR_CONFIGS, FLAVOR_DEFAULT_TYPE_CODES, type FacturXInvoiceInput, Flavor, type FlavorConfig, type IdentifierWithSchemeInput, type InvoiceDocumentInput, type InvoiceLineInput, type InvoiceTotalsInput, type LegalOrganizationInput, type NoteInput, PROFILE_MAIN_XSD, PROFILE_SCHEMA_DIRS, PROFILE_URNS, type PaymentInput, Profile, type TaxRegistrationInput, type TradePartyInput, UnitCode, type ValidationError, type ValidationResult, type VatBreakdownInput, VatCategoryCode, XRECHNUNG_PROFILE_URN, type XRechnungOptions, type XRechnungResult, type XsdValidateOptions, type XsdValidationError, type XsdValidationResult, buildXml, embedFacturX, escapeXml, extractXml, getFlavorConfig, resolveBusinessProcessUrn, resolveTypeCode, toXRechnung, validateFlavorProfile, validateInput, validateXsd };
|
package/dist/index.d.ts
CHANGED
|
@@ -448,9 +448,18 @@ interface TaxRegistrationInput {
|
|
|
448
448
|
interface LegalOrganizationInput {
|
|
449
449
|
/** Registration identifier */
|
|
450
450
|
id?: string;
|
|
451
|
+
/** Registration identifier scheme (ICD code list value, e.g. `"0002"` for SIRENE) */
|
|
452
|
+
schemeID?: string;
|
|
451
453
|
/** Trading business name */
|
|
452
454
|
tradingName?: string;
|
|
453
455
|
}
|
|
456
|
+
/**
|
|
457
|
+
* Identifier value with its scheme identifier.
|
|
458
|
+
*/
|
|
459
|
+
interface IdentifierWithSchemeInput {
|
|
460
|
+
value: string;
|
|
461
|
+
schemeID: string;
|
|
462
|
+
}
|
|
454
463
|
/**
|
|
455
464
|
* Trade party (seller, buyer, payee, etc.).
|
|
456
465
|
*
|
|
@@ -468,10 +477,14 @@ interface TradePartyInput {
|
|
|
468
477
|
*/
|
|
469
478
|
id?: string | string[];
|
|
470
479
|
/**
|
|
471
|
-
* Global identifier with scheme (e.g. GLN `"
|
|
480
|
+
* Global identifier with scheme (e.g. GLN value `"4000001000005"` and
|
|
481
|
+
* ICD scheme `"0088"`).
|
|
482
|
+
*
|
|
483
|
+
* `schemeID` is required by EN 16931 for `ram:GlobalID`.
|
|
484
|
+
*
|
|
472
485
|
* @see EN 16931 BT-29-1 / BT-46-1
|
|
473
486
|
*/
|
|
474
|
-
globalId?:
|
|
487
|
+
globalId?: IdentifierWithSchemeInput;
|
|
475
488
|
/** Postal address — mandatory for BASIC_WL and above */
|
|
476
489
|
address?: AddressInput;
|
|
477
490
|
/**
|
|
@@ -581,10 +594,14 @@ interface InvoiceLineInput {
|
|
|
581
594
|
*/
|
|
582
595
|
description?: string;
|
|
583
596
|
/**
|
|
584
|
-
* Item standard identifier (e.g. GTIN
|
|
597
|
+
* Item standard identifier (e.g. GTIN value `"4012345678901"` and ICD
|
|
598
|
+
* scheme `"0160"` for GS1).
|
|
599
|
+
*
|
|
600
|
+
* `schemeID` is required by EN 16931 for `ram:GlobalID` (BT-157-1).
|
|
601
|
+
*
|
|
585
602
|
* @see EN 16931 BT-157
|
|
586
603
|
*/
|
|
587
|
-
standardIdentifier?:
|
|
604
|
+
standardIdentifier?: IdentifierWithSchemeInput;
|
|
588
605
|
/**
|
|
589
606
|
* Seller-assigned item identifier.
|
|
590
607
|
* @see EN 16931 BT-155
|
|
@@ -1435,4 +1452,4 @@ interface XRechnungResult {
|
|
|
1435
1452
|
*/
|
|
1436
1453
|
declare function toXRechnung(input: FacturXInvoiceInput, options?: XRechnungOptions): XRechnungResult;
|
|
1437
1454
|
|
|
1438
|
-
export { type AddressInput, type AllowanceChargeInput, type BillingPeriodInput, type ContactInput, type DeliveryInput, type DocumentReferenceInput, DocumentTypeCode, type EmbedOptions, type EmbedResult, type ExtractOptions, type ExtractResult, FLAVOR_CONFIGS, FLAVOR_DEFAULT_TYPE_CODES, type FacturXInvoiceInput, Flavor, type FlavorConfig, type InvoiceDocumentInput, type InvoiceLineInput, type InvoiceTotalsInput, type LegalOrganizationInput, type NoteInput, PROFILE_MAIN_XSD, PROFILE_SCHEMA_DIRS, PROFILE_URNS, type PaymentInput, Profile, type TaxRegistrationInput, type TradePartyInput, UnitCode, type ValidationError, type ValidationResult, type VatBreakdownInput, VatCategoryCode, XRECHNUNG_PROFILE_URN, type XRechnungOptions, type XRechnungResult, type XsdValidateOptions, type XsdValidationError, type XsdValidationResult, buildXml, embedFacturX, escapeXml, extractXml, getFlavorConfig, resolveBusinessProcessUrn, resolveTypeCode, toXRechnung, validateFlavorProfile, validateInput, validateXsd };
|
|
1455
|
+
export { type AddressInput, type AllowanceChargeInput, type BillingPeriodInput, type ContactInput, type DeliveryInput, type DocumentReferenceInput, DocumentTypeCode, type EmbedOptions, type EmbedResult, type ExtractOptions, type ExtractResult, FLAVOR_CONFIGS, FLAVOR_DEFAULT_TYPE_CODES, type FacturXInvoiceInput, Flavor, type FlavorConfig, type IdentifierWithSchemeInput, type InvoiceDocumentInput, type InvoiceLineInput, type InvoiceTotalsInput, type LegalOrganizationInput, type NoteInput, PROFILE_MAIN_XSD, PROFILE_SCHEMA_DIRS, PROFILE_URNS, type PaymentInput, Profile, type TaxRegistrationInput, type TradePartyInput, UnitCode, type ValidationError, type ValidationResult, type VatBreakdownInput, VatCategoryCode, XRECHNUNG_PROFILE_URN, type XRechnungOptions, type XRechnungResult, type XsdValidateOptions, type XsdValidationError, type XsdValidationResult, buildXml, embedFacturX, escapeXml, extractXml, getFlavorConfig, resolveBusinessProcessUrn, resolveTypeCode, toXRechnung, validateFlavorProfile, validateInput, validateXsd };
|
package/dist/index.js
CHANGED
|
@@ -2,7 +2,8 @@ import * as fs from 'fs';
|
|
|
2
2
|
import * as path from 'path';
|
|
3
3
|
import { fileURLToPath } from 'url';
|
|
4
4
|
import console from 'console';
|
|
5
|
-
import {
|
|
5
|
+
import { createHash } from 'crypto';
|
|
6
|
+
import { AFRelationship, PDFDocument, PDFName, PDFArray, PDFDict, PDFStream, PDFRawStream, decodePDFRawStream, PDFString, PDFHexString, PDFRef } from 'pdf-lib';
|
|
6
7
|
|
|
7
8
|
// src/types/input.ts
|
|
8
9
|
var DocumentTypeCode = /* @__PURE__ */ ((DocumentTypeCode2) => {
|
|
@@ -309,13 +310,21 @@ function buildParty(p, profile) {
|
|
|
309
310
|
const ids = Array.isArray(p.id) ? p.id : [p.id];
|
|
310
311
|
for (const id of ids) x += tag("ram:ID", escapeXml(id));
|
|
311
312
|
}
|
|
312
|
-
if (p.globalId)
|
|
313
|
+
if (p.globalId) {
|
|
314
|
+
x += tag("ram:GlobalID", escapeXml(p.globalId.value), { schemeID: p.globalId.schemeID });
|
|
315
|
+
}
|
|
313
316
|
x += tag("ram:Name", escapeXml(p.name));
|
|
314
317
|
if (p.description && atLeast(profile, "EN16931" /* EN16931 */))
|
|
315
318
|
x += tag("ram:Description", escapeXml(p.description));
|
|
316
319
|
if (p.legalOrganization) {
|
|
317
320
|
let lo = "";
|
|
318
|
-
if (p.legalOrganization.id)
|
|
321
|
+
if (p.legalOrganization.id) {
|
|
322
|
+
lo += tag(
|
|
323
|
+
"ram:ID",
|
|
324
|
+
escapeXml(p.legalOrganization.id),
|
|
325
|
+
p.legalOrganization.schemeID ? { schemeID: p.legalOrganization.schemeID } : void 0
|
|
326
|
+
);
|
|
327
|
+
}
|
|
319
328
|
if (p.legalOrganization.tradingName)
|
|
320
329
|
lo += tag("ram:TradingBusinessName", escapeXml(p.legalOrganization.tradingName));
|
|
321
330
|
x += tag("ram:SpecifiedLegalOrganization", lo);
|
|
@@ -343,7 +352,11 @@ function buildLineItem(line, profile) {
|
|
|
343
352
|
let doc = tag("ram:LineID", escapeXml(line.id));
|
|
344
353
|
if (line.note) doc += tag("ram:IncludedNote", tag("ram:Content", escapeXml(line.note)));
|
|
345
354
|
let prod = "";
|
|
346
|
-
if (line.standardIdentifier)
|
|
355
|
+
if (line.standardIdentifier) {
|
|
356
|
+
prod += tag("ram:GlobalID", escapeXml(line.standardIdentifier.value), {
|
|
357
|
+
schemeID: line.standardIdentifier.schemeID
|
|
358
|
+
});
|
|
359
|
+
}
|
|
347
360
|
if (line.sellerAssignedId) prod += tag("ram:SellerAssignedID", escapeXml(line.sellerAssignedId));
|
|
348
361
|
if (line.buyerAssignedId) prod += tag("ram:BuyerAssignedID", escapeXml(line.buyerAssignedId));
|
|
349
362
|
prod += tag("ram:Name", escapeXml(line.name));
|
|
@@ -1092,6 +1105,17 @@ function resolveXmpConformanceLevel(profile, flavor) {
|
|
|
1092
1105
|
if (flavor === "xrechnung" /* XRECHNUNG */) return "XRECHNUNG";
|
|
1093
1106
|
return XMP_CONFORMANCE_LEVEL[profile];
|
|
1094
1107
|
}
|
|
1108
|
+
function ensureIdTrailer(pdfDoc, pdfContentBytes) {
|
|
1109
|
+
const existingIdTrailer = pdfDoc.context.trailerInfo.ID;
|
|
1110
|
+
const currentContentHash = PDFHexString.of(createHash("md5").update(pdfContentBytes).digest("hex"));
|
|
1111
|
+
const hasValidIdTrailer = existingIdTrailer instanceof PDFArray && existingIdTrailer.size() === 2;
|
|
1112
|
+
if (hasValidIdTrailer) {
|
|
1113
|
+
const existingFirstId = existingIdTrailer.lookup(0);
|
|
1114
|
+
pdfDoc.context.trailerInfo.ID = pdfDoc.context.obj([existingFirstId, currentContentHash]);
|
|
1115
|
+
} else {
|
|
1116
|
+
pdfDoc.context.trailerInfo.ID = pdfDoc.context.obj([currentContentHash, currentContentHash]);
|
|
1117
|
+
}
|
|
1118
|
+
}
|
|
1095
1119
|
function ensureOutputIntent(pdfDoc, iccBytes, identifier) {
|
|
1096
1120
|
const existing = pdfDoc.catalog.lookup(PDFName.of("OutputIntents"));
|
|
1097
1121
|
if (existing instanceof PDFArray && existing.size() > 0) return;
|
|
@@ -1367,9 +1391,13 @@ async function embedFacturX(options) {
|
|
|
1367
1391
|
const metadataRef = pdfDoc.context.register(metadataStream);
|
|
1368
1392
|
pdfDoc.catalog.set(PDFName.of("Metadata"), metadataRef);
|
|
1369
1393
|
}
|
|
1370
|
-
|
|
1394
|
+
let pdfContentBytes = await pdfDoc.save();
|
|
1395
|
+
if (addPdfA3Metadata) {
|
|
1396
|
+
ensureIdTrailer(pdfDoc, pdfContentBytes);
|
|
1397
|
+
pdfContentBytes = await pdfDoc.save();
|
|
1398
|
+
}
|
|
1371
1399
|
return {
|
|
1372
|
-
pdf:
|
|
1400
|
+
pdf: pdfContentBytes,
|
|
1373
1401
|
xml,
|
|
1374
1402
|
...validation ? { validation } : {},
|
|
1375
1403
|
...xsdValidation ? { xsdValidation } : {}
|