@casoon/invoice-generator
Version:
Generate PDF invoices, XRechnung XML, and ZUGFeRD PDF/A-3 from JSON data with TypeScript support
104 lines (89 loc) • 3.19 kB
text/typescript
import PDFDocument from 'pdfkit';
import fs from 'fs';
import { Invoice, PDFOptions } from '../types/invoice.types';
import {
HeaderSection,
SalutationSection,
ItemsTableSection,
ClosingSection,
FooterSection
} from './sections';
export class PDFGenerator {
private doc: PDFKit.PDFDocument;
private options: PDFOptions;
private currentY: number = 50;
constructor(options: PDFOptions = {}) {
this.options = {
template: 'default',
language: 'de',
includeLogo: false,
logoPath: undefined,
...options
};
this.doc = new PDFDocument({
size: 'A4',
margin: 50,
compress: false,
bufferPages: true,
info: {
Title: 'Invoice',
Author: 'Invoice Generator',
Subject: 'Invoice Document',
Creator: 'PDFKit',
Producer: 'PDFKit'
}
});
this.doc.font('Helvetica').fontSize(10);
}
public async generateInvoice(invoice: Invoice): Promise<Buffer> {
return new Promise((resolve, reject) => {
const chunks: Buffer[] = [];
this.doc.on('data', (chunk: Buffer) => chunks.push(chunk));
this.doc.on('end', () => resolve(Buffer.concat(chunks)));
this.doc.on('error', reject);
this.generateInvoiceContent(invoice);
this.doc.end();
});
}
public async generateInvoiceToFile(invoice: Invoice, outputPath: string): Promise<void> {
return new Promise((resolve, reject) => {
const writeStream = fs.createWriteStream(outputPath);
this.doc.pipe(writeStream);
writeStream.on('finish', resolve);
writeStream.on('error', reject);
this.doc.on('error', reject);
this.generateInvoiceContent(invoice);
this.doc.end();
});
}
private generateInvoiceContent(invoice: Invoice): void {
// Reset Y-Position
this.currentY = 50;
// Bereich 1: Header mit Adressen und Rechnungsdaten
const headerSection = new HeaderSection(this.doc, this.currentY, this.options.logoPath);
this.currentY = headerSection.draw(invoice);
// Bereich 2: Ansprache zur Rechnung
const salutationSection = new SalutationSection(this.doc, this.currentY);
this.currentY = salutationSection.draw(invoice);
// Bereich 3: Tabelle mit Rechnungspositionen
const itemsTableSection = new ItemsTableSection(this.doc, this.currentY);
this.currentY = itemsTableSection.draw(invoice);
// Bereich 4: Grußformel
const closingSection = new ClosingSection(this.doc, this.currentY);
this.currentY = closingSection.draw(invoice);
// Wiederkehrender Footer
const footerSection = new FooterSection(this.doc, this.currentY);
this.currentY = footerSection.draw(invoice);
}
}
/**
* Convenience-Funktionen für einfache Verwendung
*/
export async function generateInvoicePDF(invoice: Invoice, options?: PDFOptions): Promise<Buffer> {
const generator = new PDFGenerator(options);
return generator.generateInvoice(invoice);
}
export async function generateInvoicePDFToFile(invoice: Invoice, outputPath: string, options?: PDFOptions): Promise<void> {
const generator = new PDFGenerator(options);
return generator.generateInvoiceToFile(invoice, outputPath);
}