@stackforge-eu/factur-x 1.2.0 → 1.3.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 +34 -7
- package/dist/index.cjs +40 -23
- package/dist/index.d.cts +14 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +41 -24
- package/package.json +20 -2
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,31 @@ and this project adheres to [Semantic Versioning](https://semver.org/).
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [1.3.0] — 2026-09-01
|
|
11
|
+
|
|
12
|
+
- **Added item price base quantity (BT-149/BT-150).** New optional
|
|
13
|
+
`basisQuantity` and `basisQuantityUnitCode` fields on `InvoiceLineInput`
|
|
14
|
+
emit `ram:BasisQuantity` inside the net (and, when present, gross) price
|
|
15
|
+
element, so a unit price can state the quantity it refers to — e.g.
|
|
16
|
+
`12.50` per 10 m². The unit code defaults to the line's `unitCode`, as
|
|
17
|
+
EN 16931 requires BT-150 to match BT-130. Omitting the field keeps the
|
|
18
|
+
output byte-for-byte identical (#12).
|
|
19
|
+
- CI now requires every PR to add a `CHANGELOG.md` entry (or carry the
|
|
20
|
+
`skip-changelog` label), so contributor fixes always reach the release notes.
|
|
21
|
+
|
|
22
|
+
## [1.2.1] — 2026-08-27
|
|
23
|
+
|
|
24
|
+
- **Fixed XSD validation on Windows.** Schema `<xsd:import>` resolution built a
|
|
25
|
+
malformed `file://` URL from backslash paths and keyed the schema lookup by
|
|
26
|
+
a percent-encoded href that libxml2 normalises differently. Now uses
|
|
27
|
+
`pathToFileURL()` and matches imported schemas by filename. Windows added
|
|
28
|
+
to the CI matrix (thanks @muratkorkmaztr-cmyk, #6).
|
|
29
|
+
- **Embedded `factur-x.xml` now uses the `application/xml` MIME type** instead
|
|
30
|
+
of the deprecated `text/xml` (RFC 7303), which current Factur-X conformance
|
|
31
|
+
checkers flag (thanks @muratkorkmaztr-cmyk, #7).
|
|
32
|
+
- Expanded npm `keywords` (e.g. `e-rechnung`, `pdfa`, `chorus-pro`, `un-cefact`,
|
|
33
|
+
spelling variants) so the package is easier to find on npm (#8).
|
|
34
|
+
|
|
10
35
|
## [1.2.0] — 2026-07-11
|
|
11
36
|
|
|
12
37
|
- **Updated to Factur-X 1.09 / ZUGFeRD 2.5** (spec dated 2026-06-10). Factur-X
|
|
@@ -70,12 +95,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/).
|
|
|
70
95
|
`ram:GlobalID` had the same defect as the party one)
|
|
71
96
|
- `LegalOrganizationInput.schemeID?: string` added (optional, mirrors the
|
|
72
97
|
conditional spec rule)
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
98
|
+
Migration: `globalId: "4000001000005"` →
|
|
99
|
+
`globalId: { value: "4000001000005", schemeID: "0088" }` (and the analogous
|
|
100
|
+
rewrite for `standardIdentifier`). The new `IdentifierWithSchemeInput` type
|
|
101
|
+
is exported from `src/index.ts`. Regression test added in
|
|
102
|
+
`tests/xsd-validator.test.ts` that drives all three schemed identifiers
|
|
103
|
+
through the EN16931 XSD in one go (thanks @sco-indy, #4).
|
|
79
104
|
|
|
80
105
|
## [1.0.8] — 2026-05-27
|
|
81
106
|
|
|
@@ -122,7 +147,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/).
|
|
|
122
147
|
debtor account last. Any invoice combining a SEPA direct debit
|
|
123
148
|
(`payment.meansCode = "59"`) with `payment.debtorIban` plus a payee
|
|
124
149
|
IBAN/BIC failed XSD validation with `Element 'PayerPartyDebtorFinancialAccount':
|
|
125
|
-
|
|
150
|
+
This element is not expected`. Reordered the three blocks in
|
|
126
151
|
`src/core/xml-builder.ts`; regression test added in
|
|
127
152
|
`tests/xml-builder.test.ts`.
|
|
128
153
|
|
|
@@ -199,6 +224,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/).
|
|
|
199
224
|
- Deno compatibility test suite
|
|
200
225
|
|
|
201
226
|
[Unreleased]: https://github.com/StackForge-EU/factur-x/commits/main
|
|
227
|
+
[1.3.0]: https://github.com/StackForge-EU/factur-x/compare/v1.2.1...v1.3.0
|
|
228
|
+
[1.2.1]: https://github.com/StackForge-EU/factur-x/compare/v1.2.0...v1.2.1
|
|
202
229
|
[1.2.0]: https://github.com/StackForge-EU/factur-x/compare/v1.1.0...v1.2.0
|
|
203
230
|
[1.1.0]: https://github.com/StackForge-EU/factur-x/compare/v1.0.8...v1.1.0
|
|
204
231
|
[1.0.8]: https://github.com/StackForge-EU/factur-x/compare/v1.0.7...v1.0.8
|
package/dist/index.cjs
CHANGED
|
@@ -402,13 +402,19 @@ function buildLineItem(line, profile) {
|
|
|
402
402
|
"ram:BuyerOrderReferencedDocument",
|
|
403
403
|
tag("ram:LineID", escapeXml(line.buyerOrderLineId))
|
|
404
404
|
);
|
|
405
|
+
const unitCode = line.unitCode ?? "C62" /* UNIT */;
|
|
406
|
+
const basisQuantity = line.basisQuantity !== void 0 ? tag("ram:BasisQuantity", line.basisQuantity.toString(), {
|
|
407
|
+
unitCode: line.basisQuantityUnitCode ?? unitCode
|
|
408
|
+
}) : "";
|
|
405
409
|
if (line.grossUnitPrice !== void 0)
|
|
406
410
|
agree += tag(
|
|
407
411
|
"ram:GrossPriceProductTradePrice",
|
|
408
|
-
tag("ram:ChargeAmount", fmtAmt(line.grossUnitPrice))
|
|
412
|
+
tag("ram:ChargeAmount", fmtAmt(line.grossUnitPrice)) + basisQuantity
|
|
409
413
|
);
|
|
410
|
-
agree += tag(
|
|
411
|
-
|
|
414
|
+
agree += tag(
|
|
415
|
+
"ram:NetPriceProductTradePrice",
|
|
416
|
+
tag("ram:ChargeAmount", fmtAmt(line.unitPrice)) + basisQuantity
|
|
417
|
+
);
|
|
412
418
|
const delivery = tag("ram:BilledQuantity", line.quantity.toString(), {
|
|
413
419
|
unitCode
|
|
414
420
|
});
|
|
@@ -509,10 +515,7 @@ function buildXml(input, profile, flavor) {
|
|
|
509
515
|
tag("ram:ID", escapeXml(doc.businessProcessId))
|
|
510
516
|
);
|
|
511
517
|
const profileUrn = fl === "xrechnung" /* XRECHNUNG */ ? XRECHNUNG_PROFILE_URN : PROFILE_URNS[profile];
|
|
512
|
-
ctx += tag(
|
|
513
|
-
"ram:GuidelineSpecifiedDocumentContextParameter",
|
|
514
|
-
tag("ram:ID", profileUrn)
|
|
515
|
-
);
|
|
518
|
+
ctx += tag("ram:GuidelineSpecifiedDocumentContextParameter", tag("ram:ID", profileUrn));
|
|
516
519
|
let edoc = tag("ram:ID", escapeXml(doc.id));
|
|
517
520
|
if (doc.name && atLeast(profile, "EXTENDED" /* EXTENDED */)) edoc += tag("ram:Name", escapeXml(doc.name));
|
|
518
521
|
edoc += tag("ram:TypeCode", typeCode);
|
|
@@ -1100,26 +1103,42 @@ async function validateXsd(xml, profile, options) {
|
|
|
1100
1103
|
XmlValidateError,
|
|
1101
1104
|
xmlRegisterInputProvider,
|
|
1102
1105
|
xmlCleanupInputProvider,
|
|
1103
|
-
|
|
1106
|
+
openBuffer,
|
|
1107
|
+
readBuffer,
|
|
1108
|
+
closeBuffer
|
|
1104
1109
|
} = await import('libxml2-wasm');
|
|
1105
1110
|
const schemaDir = path__namespace.dirname(xsdPath);
|
|
1106
1111
|
const files = fs__namespace.readdirSync(schemaDir).filter((f) => f.endsWith(".xsd"));
|
|
1107
1112
|
const buffers = {};
|
|
1108
1113
|
for (const file of files) {
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
+
buffers[file] = fs__namespace.readFileSync(path__namespace.join(schemaDir, file));
|
|
1115
|
+
}
|
|
1116
|
+
const schemaFileFor = (requested) => {
|
|
1117
|
+
const start = Math.max(requested.lastIndexOf("/"), requested.lastIndexOf("\\")) + 1;
|
|
1118
|
+
const segment = requested.slice(start);
|
|
1119
|
+
let name = segment;
|
|
1120
|
+
try {
|
|
1121
|
+
name = decodeURIComponent(segment);
|
|
1122
|
+
} catch {
|
|
1123
|
+
}
|
|
1124
|
+
return files.includes(name) ? name : void 0;
|
|
1125
|
+
};
|
|
1114
1126
|
xmlCleanupInputProvider();
|
|
1115
|
-
|
|
1116
|
-
|
|
1127
|
+
xmlRegisterInputProvider({
|
|
1128
|
+
match: (filename) => schemaFileFor(filename) !== void 0,
|
|
1129
|
+
open: (filename) => openBuffer(buffers[schemaFileFor(filename)]),
|
|
1130
|
+
read: readBuffer,
|
|
1131
|
+
close: (fd) => {
|
|
1132
|
+
closeBuffer(fd);
|
|
1133
|
+
return true;
|
|
1134
|
+
}
|
|
1135
|
+
});
|
|
1117
1136
|
let xsdDoc;
|
|
1118
1137
|
let validator;
|
|
1119
1138
|
let xmlDoc;
|
|
1120
1139
|
try {
|
|
1121
1140
|
const xsdContent = fs__namespace.readFileSync(xsdPath, "utf-8");
|
|
1122
|
-
xsdDoc = XmlDocument.fromString(xsdContent, { url:
|
|
1141
|
+
xsdDoc = XmlDocument.fromString(xsdContent, { url: url.pathToFileURL(xsdPath).href });
|
|
1123
1142
|
validator = XsdValidator.fromDoc(xsdDoc);
|
|
1124
1143
|
xmlDoc = XmlDocument.fromString(xml);
|
|
1125
1144
|
validator.validate(xmlDoc);
|
|
@@ -1162,7 +1181,9 @@ function resolveXmpConformanceLevel(profile, flavor) {
|
|
|
1162
1181
|
}
|
|
1163
1182
|
function ensureIdTrailer(pdfDoc, pdfContentBytes) {
|
|
1164
1183
|
const existingIdTrailer = pdfDoc.context.trailerInfo.ID;
|
|
1165
|
-
const currentContentHash = pdfLib.PDFHexString.of(
|
|
1184
|
+
const currentContentHash = pdfLib.PDFHexString.of(
|
|
1185
|
+
crypto.createHash("md5").update(pdfContentBytes).digest("hex")
|
|
1186
|
+
);
|
|
1166
1187
|
const hasValidIdTrailer = existingIdTrailer instanceof pdfLib.PDFArray && existingIdTrailer.size() === 2;
|
|
1167
1188
|
if (hasValidIdTrailer) {
|
|
1168
1189
|
const existingFirstId = existingIdTrailer.lookup(0);
|
|
@@ -1191,11 +1212,7 @@ function ensureOutputIntent(pdfDoc, iccBytes, identifier) {
|
|
|
1191
1212
|
function findUnembeddedFonts(pdfDoc) {
|
|
1192
1213
|
const seen = /* @__PURE__ */ new Set();
|
|
1193
1214
|
const unembedded = /* @__PURE__ */ new Set();
|
|
1194
|
-
const FONT_FILE_KEYS = [
|
|
1195
|
-
pdfLib.PDFName.of("FontFile"),
|
|
1196
|
-
pdfLib.PDFName.of("FontFile2"),
|
|
1197
|
-
pdfLib.PDFName.of("FontFile3")
|
|
1198
|
-
];
|
|
1215
|
+
const FONT_FILE_KEYS = [pdfLib.PDFName.of("FontFile"), pdfLib.PDFName.of("FontFile2"), pdfLib.PDFName.of("FontFile3")];
|
|
1199
1216
|
const inspectFontDict = (font) => {
|
|
1200
1217
|
const subtype = font.lookup(pdfLib.PDFName.of("Subtype"));
|
|
1201
1218
|
const baseFont = font.lookup(pdfLib.PDFName.of("BaseFont"));
|
|
@@ -1391,7 +1408,7 @@ async function embedFacturX(options) {
|
|
|
1391
1408
|
const xmlBytes = new TextEncoder().encode(xml);
|
|
1392
1409
|
const afRel = resolveAfRelationship(profile, options.afRelationship);
|
|
1393
1410
|
await pdfDoc.attach(xmlBytes, flavorConfig.attachmentFilename, {
|
|
1394
|
-
mimeType: "
|
|
1411
|
+
mimeType: "application/xml",
|
|
1395
1412
|
afRelationship: afRel,
|
|
1396
1413
|
description: "Factur-X Invoice"
|
|
1397
1414
|
});
|
package/dist/index.d.cts
CHANGED
|
@@ -573,6 +573,20 @@ interface InvoiceLineInput {
|
|
|
573
573
|
* @see EN 16931 BT-148
|
|
574
574
|
*/
|
|
575
575
|
grossUnitPrice?: number;
|
|
576
|
+
/**
|
|
577
|
+
* Item price base quantity — the quantity the unit price (BT-146)
|
|
578
|
+
* applies to, e.g. a price per 10 square metres or per 1000 prints.
|
|
579
|
+
* @see EN 16931 BT-149
|
|
580
|
+
*/
|
|
581
|
+
basisQuantity?: number;
|
|
582
|
+
/**
|
|
583
|
+
* Unit of measure for `basisQuantity` (UN/CEFACT Rec 20).
|
|
584
|
+
*
|
|
585
|
+
* @default the line's `unitCode` when omitted, as EN 16931 requires
|
|
586
|
+
* BT-150 to match the invoiced quantity's unit (BT-130)
|
|
587
|
+
* @see EN 16931 BT-150
|
|
588
|
+
*/
|
|
589
|
+
basisQuantityUnitCode?: UnitCode | (string & Record<never, never>);
|
|
576
590
|
/**
|
|
577
591
|
* Line total (net) — can be derived from `quantity * unitPrice`.
|
|
578
592
|
* @see EN 16931 BT-131
|
package/dist/index.d.ts
CHANGED
|
@@ -573,6 +573,20 @@ interface InvoiceLineInput {
|
|
|
573
573
|
* @see EN 16931 BT-148
|
|
574
574
|
*/
|
|
575
575
|
grossUnitPrice?: number;
|
|
576
|
+
/**
|
|
577
|
+
* Item price base quantity — the quantity the unit price (BT-146)
|
|
578
|
+
* applies to, e.g. a price per 10 square metres or per 1000 prints.
|
|
579
|
+
* @see EN 16931 BT-149
|
|
580
|
+
*/
|
|
581
|
+
basisQuantity?: number;
|
|
582
|
+
/**
|
|
583
|
+
* Unit of measure for `basisQuantity` (UN/CEFACT Rec 20).
|
|
584
|
+
*
|
|
585
|
+
* @default the line's `unitCode` when omitted, as EN 16931 requires
|
|
586
|
+
* BT-150 to match the invoiced quantity's unit (BT-130)
|
|
587
|
+
* @see EN 16931 BT-150
|
|
588
|
+
*/
|
|
589
|
+
basisQuantityUnitCode?: UnitCode | (string & Record<never, never>);
|
|
576
590
|
/**
|
|
577
591
|
* Line total (net) — can be derived from `quantity * unitPrice`.
|
|
578
592
|
* @see EN 16931 BT-131
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as fs from 'fs';
|
|
2
2
|
import * as path from 'path';
|
|
3
|
-
import { fileURLToPath } from 'url';
|
|
3
|
+
import { pathToFileURL, fileURLToPath } from 'url';
|
|
4
4
|
import console from 'console';
|
|
5
5
|
import { createHash } from 'crypto';
|
|
6
6
|
import { AFRelationship, PDFDocument, PDFName, PDFArray, PDFDict, PDFStream, PDFRawStream, decodePDFRawStream, PDFString, PDFHexString, PDFRef } from 'pdf-lib';
|
|
@@ -372,13 +372,19 @@ function buildLineItem(line, profile) {
|
|
|
372
372
|
"ram:BuyerOrderReferencedDocument",
|
|
373
373
|
tag("ram:LineID", escapeXml(line.buyerOrderLineId))
|
|
374
374
|
);
|
|
375
|
+
const unitCode = line.unitCode ?? "C62" /* UNIT */;
|
|
376
|
+
const basisQuantity = line.basisQuantity !== void 0 ? tag("ram:BasisQuantity", line.basisQuantity.toString(), {
|
|
377
|
+
unitCode: line.basisQuantityUnitCode ?? unitCode
|
|
378
|
+
}) : "";
|
|
375
379
|
if (line.grossUnitPrice !== void 0)
|
|
376
380
|
agree += tag(
|
|
377
381
|
"ram:GrossPriceProductTradePrice",
|
|
378
|
-
tag("ram:ChargeAmount", fmtAmt(line.grossUnitPrice))
|
|
382
|
+
tag("ram:ChargeAmount", fmtAmt(line.grossUnitPrice)) + basisQuantity
|
|
379
383
|
);
|
|
380
|
-
agree += tag(
|
|
381
|
-
|
|
384
|
+
agree += tag(
|
|
385
|
+
"ram:NetPriceProductTradePrice",
|
|
386
|
+
tag("ram:ChargeAmount", fmtAmt(line.unitPrice)) + basisQuantity
|
|
387
|
+
);
|
|
382
388
|
const delivery = tag("ram:BilledQuantity", line.quantity.toString(), {
|
|
383
389
|
unitCode
|
|
384
390
|
});
|
|
@@ -479,10 +485,7 @@ function buildXml(input, profile, flavor) {
|
|
|
479
485
|
tag("ram:ID", escapeXml(doc.businessProcessId))
|
|
480
486
|
);
|
|
481
487
|
const profileUrn = fl === "xrechnung" /* XRECHNUNG */ ? XRECHNUNG_PROFILE_URN : PROFILE_URNS[profile];
|
|
482
|
-
ctx += tag(
|
|
483
|
-
"ram:GuidelineSpecifiedDocumentContextParameter",
|
|
484
|
-
tag("ram:ID", profileUrn)
|
|
485
|
-
);
|
|
488
|
+
ctx += tag("ram:GuidelineSpecifiedDocumentContextParameter", tag("ram:ID", profileUrn));
|
|
486
489
|
let edoc = tag("ram:ID", escapeXml(doc.id));
|
|
487
490
|
if (doc.name && atLeast(profile, "EXTENDED" /* EXTENDED */)) edoc += tag("ram:Name", escapeXml(doc.name));
|
|
488
491
|
edoc += tag("ram:TypeCode", typeCode);
|
|
@@ -1070,26 +1073,42 @@ async function validateXsd(xml, profile, options) {
|
|
|
1070
1073
|
XmlValidateError,
|
|
1071
1074
|
xmlRegisterInputProvider,
|
|
1072
1075
|
xmlCleanupInputProvider,
|
|
1073
|
-
|
|
1076
|
+
openBuffer,
|
|
1077
|
+
readBuffer,
|
|
1078
|
+
closeBuffer
|
|
1074
1079
|
} = await import('libxml2-wasm');
|
|
1075
1080
|
const schemaDir = path.dirname(xsdPath);
|
|
1076
1081
|
const files = fs.readdirSync(schemaDir).filter((f) => f.endsWith(".xsd"));
|
|
1077
1082
|
const buffers = {};
|
|
1078
1083
|
for (const file of files) {
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
+
buffers[file] = fs.readFileSync(path.join(schemaDir, file));
|
|
1085
|
+
}
|
|
1086
|
+
const schemaFileFor = (requested) => {
|
|
1087
|
+
const start = Math.max(requested.lastIndexOf("/"), requested.lastIndexOf("\\")) + 1;
|
|
1088
|
+
const segment = requested.slice(start);
|
|
1089
|
+
let name = segment;
|
|
1090
|
+
try {
|
|
1091
|
+
name = decodeURIComponent(segment);
|
|
1092
|
+
} catch {
|
|
1093
|
+
}
|
|
1094
|
+
return files.includes(name) ? name : void 0;
|
|
1095
|
+
};
|
|
1084
1096
|
xmlCleanupInputProvider();
|
|
1085
|
-
|
|
1086
|
-
|
|
1097
|
+
xmlRegisterInputProvider({
|
|
1098
|
+
match: (filename) => schemaFileFor(filename) !== void 0,
|
|
1099
|
+
open: (filename) => openBuffer(buffers[schemaFileFor(filename)]),
|
|
1100
|
+
read: readBuffer,
|
|
1101
|
+
close: (fd) => {
|
|
1102
|
+
closeBuffer(fd);
|
|
1103
|
+
return true;
|
|
1104
|
+
}
|
|
1105
|
+
});
|
|
1087
1106
|
let xsdDoc;
|
|
1088
1107
|
let validator;
|
|
1089
1108
|
let xmlDoc;
|
|
1090
1109
|
try {
|
|
1091
1110
|
const xsdContent = fs.readFileSync(xsdPath, "utf-8");
|
|
1092
|
-
xsdDoc = XmlDocument.fromString(xsdContent, { url:
|
|
1111
|
+
xsdDoc = XmlDocument.fromString(xsdContent, { url: pathToFileURL(xsdPath).href });
|
|
1093
1112
|
validator = XsdValidator.fromDoc(xsdDoc);
|
|
1094
1113
|
xmlDoc = XmlDocument.fromString(xml);
|
|
1095
1114
|
validator.validate(xmlDoc);
|
|
@@ -1132,7 +1151,9 @@ function resolveXmpConformanceLevel(profile, flavor) {
|
|
|
1132
1151
|
}
|
|
1133
1152
|
function ensureIdTrailer(pdfDoc, pdfContentBytes) {
|
|
1134
1153
|
const existingIdTrailer = pdfDoc.context.trailerInfo.ID;
|
|
1135
|
-
const currentContentHash = PDFHexString.of(
|
|
1154
|
+
const currentContentHash = PDFHexString.of(
|
|
1155
|
+
createHash("md5").update(pdfContentBytes).digest("hex")
|
|
1156
|
+
);
|
|
1136
1157
|
const hasValidIdTrailer = existingIdTrailer instanceof PDFArray && existingIdTrailer.size() === 2;
|
|
1137
1158
|
if (hasValidIdTrailer) {
|
|
1138
1159
|
const existingFirstId = existingIdTrailer.lookup(0);
|
|
@@ -1161,11 +1182,7 @@ function ensureOutputIntent(pdfDoc, iccBytes, identifier) {
|
|
|
1161
1182
|
function findUnembeddedFonts(pdfDoc) {
|
|
1162
1183
|
const seen = /* @__PURE__ */ new Set();
|
|
1163
1184
|
const unembedded = /* @__PURE__ */ new Set();
|
|
1164
|
-
const FONT_FILE_KEYS = [
|
|
1165
|
-
PDFName.of("FontFile"),
|
|
1166
|
-
PDFName.of("FontFile2"),
|
|
1167
|
-
PDFName.of("FontFile3")
|
|
1168
|
-
];
|
|
1185
|
+
const FONT_FILE_KEYS = [PDFName.of("FontFile"), PDFName.of("FontFile2"), PDFName.of("FontFile3")];
|
|
1169
1186
|
const inspectFontDict = (font) => {
|
|
1170
1187
|
const subtype = font.lookup(PDFName.of("Subtype"));
|
|
1171
1188
|
const baseFont = font.lookup(PDFName.of("BaseFont"));
|
|
@@ -1361,7 +1378,7 @@ async function embedFacturX(options) {
|
|
|
1361
1378
|
const xmlBytes = new TextEncoder().encode(xml);
|
|
1362
1379
|
const afRel = resolveAfRelationship(profile, options.afRelationship);
|
|
1363
1380
|
await pdfDoc.attach(xmlBytes, flavorConfig.attachmentFilename, {
|
|
1364
|
-
mimeType: "
|
|
1381
|
+
mimeType: "application/xml",
|
|
1365
1382
|
afRelationship: afRel,
|
|
1366
1383
|
description: "Factur-X Invoice"
|
|
1367
1384
|
});
|
package/package.json
CHANGED
|
@@ -1,21 +1,39 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stackforge-eu/factur-x",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Embed Factur-X, ZUGFeRD, and XRechnung structured invoice XML into PDF/A-3 documents",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"factur-x",
|
|
8
|
+
"facturx",
|
|
8
9
|
"zugferd",
|
|
9
10
|
"xrechnung",
|
|
11
|
+
"e-rechnung",
|
|
12
|
+
"chorus-pro",
|
|
10
13
|
"en16931",
|
|
14
|
+
"en-16931",
|
|
11
15
|
"cii",
|
|
16
|
+
"un-cefact",
|
|
17
|
+
"cross-industry-invoice",
|
|
12
18
|
"pdf",
|
|
19
|
+
"pdfa",
|
|
20
|
+
"pdf-a",
|
|
13
21
|
"pdf-a3",
|
|
22
|
+
"pdfa3",
|
|
23
|
+
"xmp",
|
|
14
24
|
"invoice",
|
|
15
25
|
"e-invoice",
|
|
16
26
|
"e-invoicing",
|
|
27
|
+
"einvoice",
|
|
28
|
+
"e-invoice-eu",
|
|
29
|
+
"electronic-invoice",
|
|
30
|
+
"b2g",
|
|
17
31
|
"eu",
|
|
18
|
-
"
|
|
32
|
+
"germany",
|
|
33
|
+
"france",
|
|
34
|
+
"xml",
|
|
35
|
+
"typescript",
|
|
36
|
+
"deno"
|
|
19
37
|
],
|
|
20
38
|
"license": "EUPL-1.2",
|
|
21
39
|
"author": "StackForge UG",
|