116 lines
3.5 KiB
TypeScript
116 lines
3.5 KiB
TypeScript
|
import * as plugins from './plugins.js';
|
||
|
import * as interfaces from './interfaces.js';
|
||
|
import { PDFDocument, PDFDict, PDFName, PDFRawStream } from 'pdf-lib';
|
||
|
|
||
|
export class XInvoice {
|
||
|
public pdfUint8Array: Uint8Array;
|
||
|
|
||
|
constructor(pdfBuffer: Uint8Array | Buffer) {
|
||
|
this.pdfUint8Array = Uint8Array.from(pdfBuffer);
|
||
|
}
|
||
|
|
||
|
public async embedXml(xmlString: string): Promise<void> {
|
||
|
try {
|
||
|
|
||
|
const pdfDoc = await PDFDocument.load(this.pdfUint8Array);
|
||
|
|
||
|
const xmlBuffer = new TextEncoder().encode(xmlString);
|
||
|
pdfDoc.attach(xmlBuffer, plugins.path.basename('invoice.xml'), {
|
||
|
mimeType: 'application/xml',
|
||
|
description: 'XRechnung XML Invoice',
|
||
|
});
|
||
|
|
||
|
const modifiedPdfBytes = await pdfDoc.save();
|
||
|
this.pdfUint8Array = modifiedPdfBytes;
|
||
|
console.log(`PDF Buffer updated with new XML attachment!`);
|
||
|
} catch (error) {
|
||
|
console.error('Error embedding XML into PDF:', error);
|
||
|
throw error;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* reads the xml part and returns parsed structured data
|
||
|
* @returns {Promise<interfaces.IXInvoice>} Parsed XML data
|
||
|
*/
|
||
|
public async getParsedXmlData(): Promise<interfaces.IXInvoice> {
|
||
|
try {
|
||
|
const pdfBuffer = this.pdfUint8Array;
|
||
|
const pdfDoc = await PDFDocument.load(pdfBuffer);
|
||
|
|
||
|
const namesDict = pdfDoc.catalog.get(PDFName.of('Names')) as PDFDict;
|
||
|
if (!namesDict) throw new Error('No Names dictionary found in PDF!');
|
||
|
|
||
|
const embeddedFilesDict = namesDict.get(PDFName.of('EmbeddedFiles')) as PDFDict;
|
||
|
if (!embeddedFilesDict) throw new Error('No EmbeddedFiles dictionary found!');
|
||
|
|
||
|
const filesSpecDict = embeddedFilesDict.get(PDFName.of('Names')) as PDFDict;
|
||
|
if (!filesSpecDict) throw new Error('No files specified in EmbeddedFiles dictionary!');
|
||
|
|
||
|
let xmlFile: PDFRawStream | undefined = undefined;
|
||
|
const entries = filesSpecDict.entries();
|
||
|
for (const [key, fileSpecValue] of entries) {
|
||
|
const fileSpec = fileSpecValue as PDFDict;
|
||
|
const efDict = fileSpec.get(PDFName.of('EF')) as PDFDict;
|
||
|
xmlFile = efDict.get(PDFName.of('F')) as PDFRawStream;
|
||
|
if (xmlFile) break;
|
||
|
}
|
||
|
|
||
|
if (!xmlFile) throw new Error('XML file stream not found!');
|
||
|
|
||
|
const xmlBytes = xmlFile.getContents();
|
||
|
const xmlContent = new TextDecoder().decode(xmlBytes);
|
||
|
console.log(`Read embedded XML: ${xmlContent}`);
|
||
|
|
||
|
return this.parseXmlToInvoice(xmlContent);
|
||
|
} catch (error) {
|
||
|
console.error('Error extracting or parsing embedded XML from PDF:', error);
|
||
|
throw error;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private parseXmlToInvoice(xmlContent: string): interfaces.IXInvoice {
|
||
|
// Implement XML parsing logic here
|
||
|
// Placeholder return, replace with actual parsing result
|
||
|
return {
|
||
|
InvoiceNumber: '12345',
|
||
|
DateIssued: '2023-04-01',
|
||
|
Seller: {
|
||
|
Name: 'Seller Co',
|
||
|
Address: {
|
||
|
Street: '1234 Market St',
|
||
|
City: 'Sample City',
|
||
|
PostalCode: '12345',
|
||
|
Country: 'DE',
|
||
|
},
|
||
|
Contact: {
|
||
|
Email: 'contact@sellerco.com',
|
||
|
Phone: '123-456-7890',
|
||
|
},
|
||
|
},
|
||
|
Buyer: {
|
||
|
Name: 'Buyer Inc',
|
||
|
Address: {
|
||
|
Street: '5678 Trade Rd',
|
||
|
City: 'Trade City',
|
||
|
PostalCode: '67890',
|
||
|
Country: 'DE',
|
||
|
},
|
||
|
Contact: {
|
||
|
Email: 'info@buyerinc.com',
|
||
|
Phone: '987-654-3210',
|
||
|
},
|
||
|
},
|
||
|
Items: [
|
||
|
{
|
||
|
Description: 'Item 1',
|
||
|
Quantity: 10,
|
||
|
UnitPrice: 9.99,
|
||
|
TotalPrice: 99.9,
|
||
|
},
|
||
|
],
|
||
|
TotalAmount: 99.9,
|
||
|
};
|
||
|
}
|
||
|
}
|