PDF on the fly mit Javascript erzeugen: jsPDF

PDF vom Inhalt der Webseite erzeugen mit Javascript jsPDF

jsPDF erzeugt PDFs, z.B. für Tickets, Angebote, Präsentationen, Stundenpläne und Anmeldebestätigungen. Die Javascript-Library wird in den Seitenkopf eingebunden und schon kann ein PDF erzeugt und vom Benutzer heruntergeladen werden.

Library einbinden

Die Library wird von github.com/MrRio/jsPDF geladen. Nur eine Datei in dem Paket jsPDF-master ist wichtig: jsPDF-master/dist/jsPDF.min.js (~ 300 KB). Wie meist: Am besten in den Seitenfuß, vor dem schließenden body-Tag.

<script type="text/javascript" src="jspdf.min.js"></script>

PDF-Objekt erzeugen

Den Anfang machen das Erzeugen des PDF-Objekts und das Festlegen des Formats (p = Porträt, mm = Maßeinheit, a4 = Seitengröße)

var pdf = new jsPDF("p", "mm", "a4");
oder
var pdf = new jsPDF ( {
   orientation: "landscape",
   unit: "mm",
   format: [297,210]
});

Ausgaben erzeugen

Ab jetzt ist Rechnen gefragt, denn jede Ausgabe muss platziert werden. Den Text Hallo Welt auf Postion 20mm von links, 30 mm von rechts ausgeben:

pdf.text ("ESTESTAS SEMPER LOREM", 20, 30);

Eine zweite Seite anhängen mit addPage

pdf.addPage();
pdf.text ("Hallo Universum!", 20, 30);

Das PDF muss nur noch gespeichert werden, allerdings am besten mit einem Link oder Button, denn sonst wird das PDF direkt beim Aufruf der Seite geladen.

<button id="laden">PDF laden</button>

document.querySelector("#laden").onclick = function () {
    pdf.save ("hallowelt.pdf");
}

Text, Linien, Rechtecke und Kreise

jsPDF gibt Text, Bilder, Rechtecke, Kreise und Linien aus und lehnt sich eng an Javascript Canvas an.

pdf.setTextColor(110,120,82);                 // Textfarbe als RGB-Werte
pdf.setFontSize(42);                          // Schriftgröße
pdf.setFont("times")
pdf.setFontType("bold")
pdf.text ("EGESTAS SEMPER LOREM", 50, 20);    // Text platzieren

pdf.setDrawColor(110,120,82);                 // Farbe für Linien als RGB-Werte
pdf.setLineWidth(1);                          // Breite der Linie in mm
pdf.line(25, 26, 270, 26);                    // Linie von 25 bis 270 mm

Rechteck zeichnen

pdf.setLineWidth(0.5);
pdf.setDrawColor(220,220,220);
pdf.rect (25, 30, 247, 170);                  // Viereck auf 80, 100 mit Seiten 20mm 20mm

Kreis zeichnen

pdf.circle( X, Y, radius, style);            // style: S (default) für stroke, F für fill, DF oder FD Fill und Stroke

Bilder sind so’ne Sache

Bilder können data-URL-kodiert in das PDF gesetzt werden, dann liegen die Bilddaten vor, sobald die Seite geladen ist. Im Internet gibt es zahlreiche Seiten, die diese Umwandlung online durchführen, z.B. Convert your images to base64. Für Logos und kleine Bilder ist das die einfachste Lösung.

let imgData = "data:image/jpeg;base64,/9j/4QAYRXhpZgAASUkqAAgAAAAAAAAAAAAAA … /9k=";
doc.addImage(imgData, 'JPEG', 20, 40, 40, 54);

Bye Bye Pixel – PDFs brauchen Zentimeter oder Millimeter. Durch die mm-Angaben von Breite und Höhe wird der Bereich für das Bild festgelegt. So könnte ein Bild auch mit 300dpi eingebracht werden – es muss nur im Bildbearbeitungsprogramm auf die entsprechende Größe berechnet werden.

screenshot-photoshop-bild-berechnen
120 dpi reichen in diesem Beispiel für das Bild im PDF.

Sind es mehrere und große Bilder oder kann der Benutzer Bilder auswählen, würden die langatmigen data-URLs die Seite überfrachten.

Screenshot PDF mit Javascript Seite 1
Zu groß, um als data-URL vorab geladen zu werden.
let img = new Image();
img.addEventListener("load", function() {
    pdf.addImage(img, 'png', 20, 40, 40, 54);
                              |   |   |   |
         Position von links --+   |   |   |
              Position von oben --+   |   |
                                      |   |
                       Breite in mm --+   |
                             Höhe in mm --+  
});
img.src = "image.png";

Dabei darf man eines nicht außer Acht lassen: Das Script wird fortgeführt, während der Event Listener auf das load-Event wartet. Folgt ein Seitenwechsel, würde das Bild auf der folgenden Seite landen. In so einem Fall springt z.B. eine Callback-Funktion ein.

/** https://davidwalsh.name/convert-image-data-uri-javascript **/
function getDataUri(url, callback) {
    let image = new Image();
    image.onload = function () {
        callback();
    };
    image.src = url;
}

let url = "image.jpg";

getDataUri(url, function(dataUri) {
    pdf.addImage(url,'JPEG', 20, 40, 40, 54);
    pdf.addPage();                     // Um sicher zu gehen, dass das Bild
                                       // ausgegeben wird.
});

Weitere Seiten hinzufügen

pdf.addPage() fügt dem PDF weitere Seiten hinzu.

Mehrseitiges PDF mit jsPDF

Das sieht einfach aus, ist auch einfach.

„pdf.internal.getNumberOfPages()

Fließtext

Fließtext ist genauso wie beim HTML-Canvas eben kein Fließtext. Einen Zeilenbruch oder Text zentrieren gibt es so erstmal nicht.

Allerdings es wäre viel zu mühsam, den Text Zeile für Zeile zu formatieren und zu setzen, und wenn der Text vom Benutzer erzeugt wird, muss er auf jeden Fall automatisch umgebrochen werden.

pdf.text ("Lorem ipsum es un texto pseudo-latino usedo en el diseño web, tipografía, layout, e", 25, 178);
pdf.text ("imprenta a la vez del español para darle emphasís al diseño sobre el contenido.", 25, 183);

Für die Berechnung von Textabsätzen stellt jsPDF splitTextToSize bereit.

var loremipsum = "Lorem ipsum dolor adipiscing elit. … \n\nProin feugiat augue in augue rhoncus eu cursus tellus laoreet.";

var lines = pdf.setFontSize(11)
               .splitTextToSize(loremipsum, 100);
                                             |
                      Breite des Absatz   –––+

//      left  vertical margin
//        |      |
pdf.text(170,    33 +           11 / 100, lines);
                                 |     |
                    font size –––+     |
                       Absatzbreite –––+

\n erzeugt einen Zeilenumbruch, \n\n den Durchschuss zwischen zwei Absätzen.

Woher kommt das PDF?

Am Ende nicht vergessen, dem PDF die Adresse mitzugeben, von der es kam …

pdf.setLineWidth(1);
pdf.line(25, 190, 270, 190);
pdf.text ("PDF mit Javascript erzeugen: https://www.mediaevent.de/?p=5575", 25, 192);

Einen klickbaren Link setzen kann jsPDF nicht … jammerschade. Stattdessen gibt es Metadaten für das PDF und einen sprechenden Dateinamen.

pdf.setProperties({
	title: 'PDF mit jsPDF erstellen',
	subject: 'PDF mit Javascript online zusammenstellen',
	author: 'U. Häßler',
	keywords: 'generated, javascript,jspdf',
	creator: 'Javascript jsPDF'
});

pdf.save ("mediaevent.de-pdf-mit-javascipt.pdf");