Angular
HTML & CSS
React
Vue & Svelte

PDFs mit Node.js erstellen

14. Jul 2023

Zu den wohl schwierigsten Problemen in der Webentwicklung gehört unbestritten das Zentrieren eines div-Elements im Browser. Doch direkt danach folgt das Erstellen von PDFs. Wobei es hier weniger um die technische Herausforderung geht, sondern vielmehr darum, dass das Ergebnis halbwegs gut aussieht.

Bevor wir uns in den Code stürzen, zunächst die Frage, ob PDFs heutzutage noch ihre Daseinsberechtigung haben: Die Antwort ist zum Leidwesen aller, die sie erzeugen müssen: ja. Wenn es um Rechnungen, Tickets und weitere statische Dokumente geht, die Ihre Benutzer:innen benötigen, führt oft kein Weg an diesem Dateiformat vorbei. Nehmen wir an, dass wir an der Implementierung eines Webshops arbeiten und mit der Aufgabe betraut sind, für unsere Kund:innen Rechnungen im PDF-Format zum Download bereitzustellen. Das Problem ist dann, dass solche Dokumente nicht nur aus statischen, sondern auch aus vielen dynamischen Elementen bestehen. Es reicht also nicht, wenn Sie das Dokument einmal generieren und es auf einem Webserver zum Download anbieten. Es muss eine dynamische Lösung her, doch wo findet die Implementierung statt? Serverseitig oder clientseitig oder gibt es sogar die Option für beides?

Auswahl der passenden Bibliothek

Gehen Sie jetzt davon aus, dass es genau eine Bibliothek für das Erstellen von PDFs gibt, muss ich Sie leider enttäuschen. Hier gilt, wie so oft in JavaScript, dass es eine Vielzahl von Lösungen gibt, aus denen Sie sich die passende herauspicken müssen. Ein Blick in die Suchmaschine der Wahl und eine anschließende Prüfung auf www.xpmtrends.com liefert das in Abbildung 1 Gezeigte.

Abb. 1: Auswahl der PDF-Bibliotheken [1]Abb. 1: Auswahl der PDF-Bibliotheken [1]

In der engeren Auswahl stehen die Bibliotheken jspdf, pdfkit und pdfmake. Der Langzeittrend zeigt, dass bis Ende 2021 das mittlerweile zwölf Jahre alte pdfkit das Mittel der Wahl war, zumindest wenn es um die wöchentlichen Downloads geht. Zum Jahreswechsel 2022 haben dann jspdf und pdfmake die Führung übernommen. Das letzte Update von jspdf liegt aber schon einige Zeit zurück, also fällt unsere Wahl auf pdfmake. Für unser Beispiel erzeugen wir das PDF serverseitig, was beide Bibliotheken, sowohl jspdf als auch pdfmake unterstützen.

Installation und erstes PDF

Die Installation von pdfmake läuft ab wie bei nahezu jedem anderen JavaScript-Paket. Mit npm install pdfmake installieren Sie das Paket in Ihrer Applikation. Nutzen Sie TypeScript, können Sie die zugehörigen Typdefinitionen mit npm install @types/pdfmake installieren. Aktuell installieren Sie mit diesem Kommando die Version 0.2.7. Die Version 0.3 steht allerdings schon in den Startlöchern und kann mit npm install pdfmake@0.3.0-beta.1 installiert werden.

LUST AUF NOCH MEHR JAVASCRIPT?

Entdecken Sie Workshops vom 21. - 24. Oktober 2024

 

Für das Erzeugen eines sehr einfachen PDFs müssen Sie nicht viel tun: Sie definieren, welche Schriftarten und Stile Sie verwenden möchten, beschreiben Ihr Dokument, erzeugen aus dieser Definition das PDF und schreiben es ins Dateisystem (Listing 1).

Listing 1: Erstellen eines PDF

import pdfmake from ‚pdfmake‘;
 
const fonts = {
  Helvetica: {
    normal: 'Helvetica',
    bold: 'Helvetica-Bold',
    italics: 'Helvetica-Oblique',
    bolditalics: 'Helvetica-BoldOblique'
  },
};
 
pdfmake.addFonts(fonts);
 
const docDefinition = {
  content: [
    'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor',
    'invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam'
  ], defaultStyle: {
    font: 'Helvetica'
  }
};
 
const pdf = pdfmake.createPdf(docDefinition);
pdf.write('document.pdf').then(() => {
  console.log('pdf generated')
}, err => {
  console.error(err);
});

Um die Definition der Schriften kommen Sie nicht herum. Sie können hier entweder eine der Standardschriftarten wie Times, Helvetica oder Courier nutzen oder zusätzliche Schriftarten in Form von .ttf-Dateien einbinden. Diese können Sie übrigens auch über npm-Pakete installieren. Die Roboto-Schriftart, die pdf-make als Standard verwenden möchte, installieren Sie mit npm install @fontsource/roboto. Im Beispiel nutzen wir Helvetica, was wir pdfmake mit der defaultStyle-Eigenschaft auch mitteilen. Das eigentlich Spannende am PDF, den Inhalt, definieren Sie mit der content-Eigenschaft. Im einfachsten Fall übergeben Sie hier eine Reihe von Zeichenketten, die für die Absätze in Ihrem Dokument stehen. Unser generiertes PDF besteht also aus zwei Absätzen formschönen Lorem-ipsum-Texts. Ist der Text zu breit für eine Seite, wird er umgebrochen und in der nächsten Zeile fortgesetzt. Mit diesen Definitionen können Sie mit Hilfe der createPdf-Methode, der Sie die Dokumentendefinition übergeben, Ihr PDF erzeugen. Die write-Funktion schreibt das Ergebnis unter dem angegebenen Namen im aktuellen Verzeichnis ins Dateisystem und liefert Ihnen ein Promise-Objekt zurück.

Anspruchsvolleres Layout

Das Ausgeben von unformatiertem Text eignet sich vielleicht gerade noch für einfachen Fließtext. Optisch ansprechend ist das Ergebnis aber nicht. Doch auch hierfür hat pdfmake eine Lösung. Sie können innerhalb der content-Eigenschaft nicht nur einfache Zeichenketten angeben, sondern auch komplexere Objekte. Möchten wir z. B. eine Rechnung schreiben, beginnen wir also mit dem Kopf des Dokuments: Er besteht aus unserer Adresse, der Adresse des Rechnungsempfängers und dem Titel Rechnung. Natürlich können Sie all diese Informationen direkt in den Content schreiben. Nachdem Sie den Inhalt des PDF aber als ganz gewöhnliche Objektstruktur beschreiben, können Sie auch Hilfsfunktionen definieren, die bestimmte Teile des Dokuments für Sie erzeugen. Das führt dazu, dass der Code sprechender wird und Sie das Dokument deutlich besser aufteilen können. In Listing 2 sehen Sie, wie Sie diese Elemente in Ihrem PDF definieren können.

Lisiting 2: Weiteren Inhalt definieren

function createDocumentHead(recipient) {
  return [
    {
      text: [recipient.name, recipient.street, recipient.city].join('\n'),
      absolutePosition: { x: 100, y: 200 }
    },
    {
      text: [
        'ACME',
        'Fairfield, New Jersey'
      ].join('\n'),
      absolutePosition: { x: 400, y: 80 }
    },
    {
      text: 'Rechnung', fontSize: 22, bold: true, absolutePosition: { x: 100, y: 300 }
    }
  ];
}
 
const docDefinition = {
  content: [
    ...createDocumentHead({ name: 'Glover Inc.', street: '1853 Stefanie Bridge', city: 'Carrolton, GA 30118' })
  ], defaultStyle: {
    font: ‚Helvetica‘
  }
};

Doch damit ist noch nicht genug. Was unserer Rechnung jetzt noch fehlt, ist eine Auflistung der bestellten Produkte, und was eignet sich dafür besser als eine Tabelle? Aber auch dafür hat unsere PDF-Bibliothek eine Lösung parat. Zusätzlich zur Texteigenschaft wie zuvor können Sie mit table eine Tabelle definieren. Hier legen Sie fest, wie viele der definierten Zeilen der Kopf der Tabelle sein sollen. Ein nettes Zusatzfeature von pdfmake ist, dass dieser Tabellenheader bei einem Seitenumbruch automatisch wiederholt wird. Sie können außerdem die gewünschte Breite der Spalten angeben und schließlich mit der body-Eigenschaft in Form eines Arrays mit Objekten den eigentlich Inhalt der Tabelle definieren. In Listing 3 sehen Sie die Implementierung der Funktion createTable, der Sie einen Warenkorb übergeben können und die daraus eine Tabellendarstellung macht. Wie Sie sehen, lassen sich mit pdfmake die verschiedenen Features miteinander kombinieren, z. B. die Tabelle absolut oder relativ positionieren und innerhalb der Tabelle die Formatierung der einzelnen Zellen anpassen.

Listing 3: Tabelle erstellen

function createDocumentHead(recipient) {... }
 
function createTable(cart) {
  const tableRows = cart.map((item, index) => {
    return [index + 1, item.title, item.amount, item.price, item.amount * item.price]
  });
 
  return {
    layout: 'lightHorizontalLines',
    table: {
      headerRows: 1,
      widths: [30, 200, 30, 30, 50],
      body: [
        ['Pos', 'Titel', 'Stück', 'Preis', 'Gesamt'],
        ...tableRows,
        ['', { text: 'Gesamtsumme:', bold: true }, '', '', tableRows.reduce((prev, curr) => { return prev + curr[4] }, 0)]]
    },
    absolutePosition: { x: 100, y: 350 }
  }
}
 
const docDefinition = {
  content: [
    ...createDocumentHead({ name: 'Glover Inc.', street: '1853 Stefanie Bridge', city: 'Carrolton, GA 30118' }),
    createTable(items)
  ], defaultStyle: {
    font: 'Helvetica'
  }
};

Weitere interessante Features von pdfmake

Doch pdfmake kann noch deutlich mehr, als ich Ihnen bisher gezeigt habe. So können Sie Ihre eigenen Styles definieren und auf Texte anwenden. Dieses Feature erinnert in seinen Grundzügen im weitesten Sinn an CSS. Sie definieren einen Style z. B. bestehend aus einer Schriftgröße und schräggestellter Schrift einmal und wenden ihn dann an verschiedenen Stellen in Ihrer Dokumentdefintion an. Weitere Funktionalität, die Ihnen pdfmake darüber hinaus bietet, ist beispielsweise das Einbinden von Bildern, die Definition von Kopf- und Fußbereichen eines Dokuments oder die Erzeugung eines QR-Codes.

Bei all ihrem Funktionsumfang kann die Bibliothek aber auch nicht alles, was man sich bei der PDF-Erstellung vorstellen kann. So ist es nicht möglich, interaktive Formulare zu implementieren. Jedoch gibt es schon einen Feature-Request im GitHub-Repo des Projekts, in dem diese Funktionalität gewünscht wird.


 

 

Und was lernen wir daraus?

Das Erstellen von PDFs macht nicht unbedingt jedem Spaß; das liegt aber weniger an den verfügbaren Bibliotheken, als eher daran, wie das Dateiformat und die Positionierung von Elementen generell funktioniert. Projekte wie pdfmake machen es uns Entwickler:innen verhältnismäßig angenehm, solche Dateien zu erzeugen. Auch wenn sich die Bibliothek noch in der Version 0.2 befindet, können Sie sie bedenkenlos nutzen. Bei der Beta der Version 0.3 wäre ich noch etwas zurückhaltend, was den Produktiveinsatz angeht. Diese Version verfügt jedoch über einige Verbesserungen und zeigt, wohin die Reise der Bibliothek gehen wird. Das pdfmake-Team hat vor allem die Schnittstellen vereinheitlicht und arbeitet nun generell mit Promises.

Falls Sie also das Vergnügen haben, ein PDF erzeugen zu dürfen, geben Sie vielleicht pdfmake eine Chance.

Top Articles About JavaScript

Bleiben Sie auf dem Laufenden!

mit wöchentlichen Event-& Branchen-Updates