@stafyniaksacha/facturx
Version:
Factur-X and Order-X generation library for European e-invoicing standard
94 lines (90 loc) • 3 kB
JavaScript
import { Buffer } from 'node:buffer';
import { r as resolvePdf, F as FACTURX_FILENAME, O as ORDERX_FILENAME, Z as ZUGFERD_FILENAMES, a as resolveXml, c as check } from './facturx.Cloc1W3i.mjs';
import { PDFName, PDFDict, PDFStream, decodePDFRawStream, PDFArray } from 'pdf-lib';
function extractAttachments(pdfDoc) {
const rawAttachments = extractRawAttachments(pdfDoc);
return rawAttachments.map(({ fileName, fileSpec }) => {
const stream = fileSpec.lookup(PDFName.of("EF"), PDFDict).lookup(PDFName.of("F"), PDFStream);
return {
name: fileName.decodeText(),
data: decodePDFRawStream(stream).decode()
};
});
}
function extractRawAttachments(pdfDoc) {
if (!pdfDoc.catalog.has(PDFName.of("Names")))
return [];
const Names = pdfDoc.catalog.lookup(PDFName.of("Names"), PDFDict);
if (!Names.has(PDFName.of("EmbeddedFiles")))
return [];
const EmbeddedFiles = Names.lookup(PDFName.of("EmbeddedFiles"), PDFDict);
if (!EmbeddedFiles.has(PDFName.of("Names")))
return [];
const EFNames = EmbeddedFiles.lookup(PDFName.of("Names"), PDFArray);
const rawAttachments = [];
for (let idx = 0, len = EFNames.size(); idx < len; idx += 2) {
const fileName = EFNames.lookup(idx);
const fileSpec = EFNames.lookup(idx + 1, PDFDict);
rawAttachments.push({ fileName, fileSpec });
}
return rawAttachments;
}
async function extract(options) {
let file = null;
const pdf = await resolvePdf(options.pdf);
let flavor = options.flavor;
const level = options.level;
const attachments = extractAttachments(pdf);
if (attachments?.length) {
for (const attachment of attachments) {
if (attachment.name === FACTURX_FILENAME) {
if (!options.flavor || options.flavor === "facturx") {
flavor = "facturx";
} else {
throw new Error(`Invalid flavor, expected ${options.flavor} but found facturx`);
}
file = attachment;
break;
}
if (attachment.name === ORDERX_FILENAME) {
if (!options.flavor || options.flavor === "orderx") {
flavor = "orderx";
} else {
throw new Error(`Invalid flavor, expected ${options.flavor} but found orderx`);
}
file = attachment;
break;
}
if (ZUGFERD_FILENAMES.includes(attachment.name)) {
if (!options.flavor || options.flavor === "zugferd") {
flavor = "zugferd";
} else {
throw new Error(`Invalid flavor, expected ${options.flavor} but found zugferd`);
}
file = attachment;
break;
}
}
}
if (!file) {
throw new Error("No attachment found");
}
const xml = await resolveXml(Buffer.from(file.data));
if (options.check === true) {
const result = await check({
xml,
flavor,
level
});
if (!result.valid) {
throw new Error("Invalid XML");
}
}
return {
filename: file.name,
xml: xml.toString(),
flavor,
level
};
}
export { extract as e };