Von jqplot Diagrammen zu einer PDF Datei

ich sags gleich, ein steiniger Weg.

Die Anforderung

Zum Zweck der analogen Archivierung musste ich für ein aktuelles Projekt (CHES) eine moderne HTML-Seite (mit mehreren Canvas-Elementen) in eine druckbare Form bringen. Mit CSS-Anweisungen ist es mir nicht gelungen, nur in zwei modernen Browsern ein halbwegs vergleichbares Layout zu Papier zu bringen. Mit viel Javascript ging es eher in diese Richtung, aber im dritten Browser war dann auch da wieder alles anders.

Eine PDF-Datei war die schlüssige Entscheidung. Diese kann dann auch digital archiviert werden, was bei CHES, einem Projekt im Medizinumfeld, durchaus sinnvoll scheint. Jetzt wäre der einfachste Weg folgender: Verwenden Sie Google-Chrome, drücken Sie auf diesen Button und wählen Sie im anschließenden Dialog „Speichern als PDF“. Aber das wäre ja zu einfach …

Das Ausgangsmaterial

CHES Longitudinal Report demo

Mit serverseitig-erzeugten PDFs habe ich schon mehrfach Erfahrung: mit PHP, wkhtmltopdf, und batik gab es immer brauchbare Ergebnisse. Bei CHES verwenden wir die jqplot-Bibliothek von Chris Leonello, die (mehr oder weniger) aufwändige Diagramme mit Hilfe von HTML und canvas im Browser anzeigt. Die Darstellung kann clientseitig angepasst werden, soweit so gut. Serverseitig kann ich diese Bibliothek aber nicht verwenden und um die veränderte Seite durch wkhtmltopdf zu jagen, müsste ich alle Veränderungen mitgeben und … ach, zu kompliziert. Also versuchen wir das PDF am Client zu erzeugen: jspdf.com.

jspdf.com

jspdf ist noch ein recht junges Projekt und vom Funktionsumfang überschaubar. Für die Ansprüche im aktuellen Projekt schien es möglich, einen Versuch damit zu starten. Mit canvas kann jspdf (noch) nicht umgehen, darum werden die Diagramme zuerst in Grafiken konvertiert. Der Qualitätsverlust ist schmerzlich, wo jspdf obendrein nur mit JPEGs und nicht mit PNG arbeiten will.

var doc = new jsPDF();
$(jquery_selector_fuer_jqplots_hier).each(function() {
  // hier werden xoff, yoff, w, h berechnet
  // jqplot muss ge-patcht werden, damit nicht image/png sondern image/jpeg kommt
  var img = $(this).jqplotToImageStr();
  doc.addImage(img, 'JPEG', xoff, yoff, w, h);
});
doc.save("ches_report_"+what+"_"+CHES.patientWithoutPrefix+".pdf");

Und hier das Resultat, not too bad (Firefox, Linux):

CHES Report PDF mit jspdf

Wunderbar, und sehr übersichtlicher Code. Funktioniert auch in Firefox, Chrome, Safari (sogar am iPad), you name it. Ach ja, der IE, da geht das aller leider nicht. Mehrere Problem:

  • IE8: Kennt das canvas Element nicht. Damit man im IE8 überhaupt jqplot Diagramme sehen kann lädt man die excanvas Bibliothek. Alles wunderbar, sehr langsam, aber immerhin, 100% Funktionalität. Fast, die toDataURL()-Methode ist nicht unterstützt und nach meinen ausführlichen google-Recherchen wird das im IE8 auch nie möglich sein. Die Folge: Der IE8 kann diese Seiten weder drucken noch in PDFs konvertieren.
  • IE9: Hmm, pdfjs funktioniert wunderbar, allein das PDF kann weder angezeigt noch gespeichert werden. Klingt absurd, ist aber so. Der workaround ist noch aberwitziger: Man lade eine kleine Flash-Bibliothek, die einen Button anzeigt, der den PDF-String herunterladen lässt. Unglaublich aber wahr, hier der Code (vollständiges Beispiel bei github.com):
if (badIE === true) {
      var docString = doc.output('datauristring');
      $("#ieWarning").show();
        Downloadify.create('downloadify',{
          filename: "ches_report_"+what+"_"+CHES.patientWithoutPrefix+".pdf",
          data: docString,
          onComplete: function(){ $("#ieWarning").hide(); },
          onCancel: function(){ $("#ieWarning").hide();  },
          onError: function(){ $("#ieWarning").hide(); },
          dataType: 'base64',
          transparent: false,
          swf: 'js/jspdf/libs/Downloadify/media/downloadify.swf',
          downloadImage: 'js/jspdf/libs/Downloadify/images/download.png',
          width: 100,
          height: 30,
          append: false
        });
    }
  • IE10: soll das alles ohne schmutzige Tricks können, konnte ich aber noch nicht testen.

tl;dr — Fazit

Das sehr einfache PDF-Dokument kann mit jspdf gut erstellt werden. Der Ausschluss des IE8 ist mit der mangelnden Unterstützung des canvas-Elements begründet und nicht mit jspdf an sich. Der IE9 workaround ist aberwitzig, aber kann vermutlich bald entfernt werden (der IE9-Marktanteil ist eh rückläufig). Alle anderen, aktuellen Browser können gut bedient werden und erzeugen eine druckbare PDF Datei, die plattformübergreifend gleich aussieht.

See also