Das eigene PDF

Einführung in die Zend_Pdf-Komponente des Zend Frameworks
Kommentare

Mit Zend_Pdf kann jeder Anwender des Zend Frameworks PDF-Dokumente neu erstellen bzw. vorhandene bearbeiten. Zend_Pdf bietet dabei verschiedene Funktionen, um Seiten anzulegen, Texte einzufügen, grafische Elemente zu zeichnen oder Grafiken einzubinden. Zudem bietet die Komponente Unterstützung für das Arbeiten mit Styles oder das Ändern der Metadaten eines PDF-Dokuments.

Bei Zend_Pdf handelt es sich um eine der ältesten Komponenten des Zend Frameworks. Bereits in der ersten frühen Pre-Alpha-Version 0.1.1 war Zend_Pdf vertreten. Schon damals konnte man recht einfach PDF-Dokumente mit PHP erstellen. Zend_Pdf wurde seitdem konsequent weiterentwickelt und hat seit kurzem auch die lang ersehnte UTF-8-Unterstützung spendiert bekommen.

Dieser Artikel basiert auf dem Release 1.6.0 und setzt einige Kenntnisse im Aufbau einer Anwendung mithilfe einer Bootstrap-Datei sowie Zend_Controller und Zend_View voraus. Jedes Beispiel besteht aus einer Action im IndexController sowie aus einem View-Skript. Falls das Zend Framework für Sie komplettes Neuland ist, empfehle ich die Lektüre der Schnellanleitung im Zend Framework Manual.

Alle Beispiele sollten in der Regel auch mit späteren Versionen des Zend Frameworks laufen. Sie müssen lediglich die Verzeichnisse in der Bootstrap-Datei und in den View-Skripten anpassen, die aktuelle Version des Zend Frameworks in das Verzeichnis /library/Zend kopieren und schon können Sie die Anwendung starten (Abbildung 1).

Abb. 1: Menü

Als erste Einführung empfiehlt sich ein Blick in das Manual zu Zend_Pdf, das die verschiedenen Funktionen der Komponente vorstellt und auch ein komplettes Beispiel enthält. Zudem bietet auch das Tutorial auf der Zend Developer Zone eine gute Einführung in die grundlegenden Möglichkeiten von Zend_Pdf.

PDF-Dokument speichern

Wir beginnen mit einem einfachen Beispiel. Es soll ein PDF-Dokument erstellt und gespeichert werden. In einer HTML-Seite soll dann ein direkter Link auf dieses erstellte PDF-Dokument eingebunden werden. Somit kann der Nutzer die Datei direkt anzeigen oder auf seinem Rechner speichern.

Listing 1 zeigt die Action-Methode zum Erstellen und Speichern eines einfachen PDF-Dokuments. Zuerst wird eine neue Instanz von Zend_Pdf instantiiert. Danach wird eine neue Seite im A4-Format erstellt und dem PDF-Dokument hinzugefügt. Als Nächstes werden eine Überschrift und zwei Textzeilen ergänzt. Dabei muss vor dem ersten Zeichnen eines Texts immer zuerst ein Zeichensatz ausgewählt werden, weil ansonsten eine Exception geworfen wird (hier eine Übersicht über die Zeichensätze). Danach wird noch ein Dateiname festgelegt und die Datei gespeichert. Bitte achten Sie darauf, dass beim Ausprobieren der Beispiele Ihr Webserver auch Schreibrechte im Verzeichnis /public/pdf/ hat. Ansonsten wird nämlich ebenfalls eine Exception geworfen. Zu guter Letzt werden noch einige Template-Variablen an das View-Skript übergeben und der Name des View-Skripts geändert.

Bei den Aufrufen der drawText()-Methoden sind Ihnen sicherlich die Zahlenwerte aufgefallen. Zum Zeichnen eines Texts müssen immer die Koordinaten angegeben werden, bei denen der Text eingefügt werden soll. Der erste Wert stellt dabei den Abstand vom linken Rand der Seite und der zweite Wert den Abstand vom unteren (!) Rand der Seite dar. Das ist zu Beginn etwas gewöhnungsbedürftig, aber mit der Zeit kommt man mit der Vorgabe ganz gut klar. Die Koordinaten werden in Points/Punkten gemessen, was 1/72 eines Inches/Zolls entspricht. Eine DIN-A4-Seite hat übrigens die Maße von 595 Punkten Breite und 842 Punkten Höhe.

Das entsprechende View-Skript ist in Listing 2 zu sehen. Es besteht aus einer simplen HTML-Seite, in der ein Link zu dem erstellten PDF-Dokument zu finden ist. Wenn Sie auf diesen Link klicken, sollte Ihnen Ihr Webbrowser die Datei zur Anzeige oder zum Speichern anbieten. Zudem sollte im Verzeichnis /public/pdf/ nun die Datei pdf1.pdf erstellt worden sein.

[ header = Seite 2: PDF-Dokument direkt anzeigen ]

PDF-Dokument direkt anzeigen

Das zweite Beispiel widmet sich dem Problem, dass das PDF-Dokument nicht gespeichert, sondern direkt an den Webbrowser gesendet werden soll. Der Nutzer soll dann selbst entscheiden können, ob er das PDF-Dokument anzeigen oder speichern möchte. Listing 3 zeigt die Action-Methode für dieses Beispiel. Die ersten Zeilen zum Erstellen des neuen PDF-Dokuments, zum Anlegen der Seite und zum Schreiben der Texte sind ähnlich wie beim ersten Beispiel. Lediglich das Zeichnen der zweiten Textzeile demonstriert die Verwendung von UTF-8-kodierten Texten. Sie müssen bei der Verwendung der drawText()-Methode als vierten Parameter lediglich das Encoding angeben, in unserem Fall ist das UTF-8. Als Nächstes wird das gesamte PDF-Dokument an das View-Skript übergeben sowie wieder der Name des View-Skriptes geändert.

Wichtig sind die beiden letzten Zeilen, die zwei Header setzen, damit die Datei als Inline-Dokument an den Webbrowser gesendet und als PDF-Dokument erkannt wird. Somit kann der Nutzer selbst entscheiden, ob er das Dokument direkt anzeigen oder lieber speichern möchte. Das korrespondierende View-Skript in Listing 4 ist denkbar einfach gestrickt: es gibt lediglich das PDF-Dokument aus, indem es dieses in einen String umwandelt. Wenn Sie das zweite Beispiel aufrufen und sich das PDF-Dokument anzeigen lassen, sollten die Umlaute in der ersten Textzeile fehlen und in der zweiten vorhanden sein.

Mehrere Seiten in einem Dokument

Im nächsten Beispiel sehen Sie, wie einfach es ist, ein PDF-Dokument mit mehreren Seiten zu erstellen. Listing 5 zeigt, wie Sie dabei vorgehen müssen. Wichtig ist dabei die Eigenschaft pages desZend_Pdf-Objektes, in dem alle Seiten vorgehalten werden. In dem Beispiel wird zuerst die erste Seite erstellt und mit einer Überschrift sowie einem Text versehen. Danach wird dann die zweite Seite hinzugefügt und ebenfalls mit Überschrift und einem Text ausgestattet. Der restliche Ablauf ist identisch mit dem vorherigen Beispiel. Wenn Sie es aufrufen, wird Ihnen ein PDF-Dokument angezeigt, das tatsächlich zwei Seiten enthält.

Einen langen Text umbrechen

Das vierte Beispiel ist etwas komplexer und löst ein häufig vorkommendes Problem. Zend_Pdf bietet von Haus aus nämlich keine Möglichkeit, einen längeren Text umzubrechen und bei Bedarf auf mehrere Seiten aufzuteilen. Listing 6 zeigt das komplette Beispiel. Der Beginn ist wieder gleich. Es wird ein Dokument erstellt, eine neue Seite hinzugefügt und mit einer Überschrift versehen. Danach wird eine einfache Textdatei geladen, in der ein längerer „Lorem ipsum“-Fülltext enthalten ist. Der Text wird auf eine Zeilenlänge von maximal 70 Zeichen umgebrochen und in ein Array konvertiert. Dann wird die Position der ersten Zeile festgelegt und die Vorbereitungen sind abgeschlossen.

Nun wird das Array mit den Textzeilen durchlaufen. Zuerst wird die Zeilenposition geprüft. Wenn das Seitenende erreicht ist, wird automatisch eine neue Seite erstellt und an das PDF-Dokument angehängt. Danach wird der Zeichensatz für die Seite und die neue Zeilenposition festgelegt. Zum Ende jedes Durchlaufs wird dann der Text gezeichnet und die Zeilenposition verändert.

Der Rest des Beispiels sollte wieder selbsterklärend sein. Wenn Sie das vierte Beispiel aufrufen, sollte Ihnen ein PDF-Dokument mit drei Seiten angezeigt werden. Sie sehen, dass der Text entsprechend umgebrochen wurde und von der ersten Seite auf allen folgenden fortgeführt wurde. Das Konvertieren von redaktionellen Artikeln aus einem CMS in ein PDF-Dokument sollte somit leicht möglich sein.

Linien, Kreise, Rechtecke zeichnen

Das folgende Beispiel widmet sich dem Zeichnen geometrischer Figuren wie Linien, Kreisen, Ellipsen und Rechtecken. Listing 7 soll hierzu verschiedene Möglichkeiten demonstrieren. Als Erstes wird ein Rechteck gezeichnet, für das die Art der Linie, die Linienfarbe und die Füllfarbe des Rechtecks gesetzt wird. Die Farben werden hierbei in Graustufen mithilfe von Zend_Pdf_Color_GrayScalegesetzt. Hier mehr Informationen zum Verwenden von Farben.

Nachdem diese Vorbereitungen getroffen wurden, kann das Rechteck mit der drawRectangle()-Methode gezeichnet werden. Diese Methode benötigt vier Angaben für die Koordinaten. Die ersten beiden Parameter geben die Koordinaten der ersten Ecke (in unserem Fall der Ecke oben links) und die nächsten beiden Parameter die Koordinaten der zweiten Ecke (in unserem Fall der Ecke unten rechts) an. Natürlich können Sie das Rechteck auch von unten links nach oben rechts zeichnen; Sie sollten lediglich darauf achten, dass die linke untere Ecke der Seite quasi der Koordinate 1/1 entspricht. Als Nächstes wird ein Kreis gezeichnet. Bei der Art der Linie verwenden wir ein eigenes Muster, das die Linie in unterschiedlichen Abständen unterbricht. Die Farben werden zudem in RGB-Werten mithilfe von Zend_Pdf_Color_Rgb angegeben. Beim Zeichnen eines Kreises mithilfe der drawCircle()-Methode müssen Sie darauf achten, dass die ersten beiden Parameter den Mittelpunkt des Kreises angeben und der dritte Parameter den Radius in Punkten angibt. Im folgenden Schritt werden drei Linien gezeichnet. Wir ändern wieder die Art der Linie und die Farbe. Bei der Verwendung der drawLine()-Methode stellen die ersten beiden Parameter die Koordinaten für den Startpunkt der Linie und die nächsten beiden Parameter die Koordinaten für den Endpunkt der Linie dar.

Jetzt wird es ein wenig komplizierter im Vergleich zu den vorangegangenen Beispielen. Wir zeichnen einen Kreis, der aus mehreren Sektoren besteht. Dies eignet sich z.B. sehr gut für Diagramme. Für das Setzen der Farbe verwenden wir dieses Mal Zend_Pdf_Color_Html, bei dem sowohl Farben im Hexcode als auch in den Farbnamen für Grundfarben angegeben werden können. Beim Zeichnen der Sektoren ist darauf zu achten, dass mithilfe der Parameter Vier und Fünf bei der drawCircle()-Methode der Startwinkel und der Endwinkel angegeben werden. Diese können mithilfe der Konstante M_PI angegeben werden. Nach jedem Zeichnen eines Sektors wird die Farbe für den nächsten Sektor geändert, sodass ein mehrfarbiges Kuchendiagramm entsteht. Als letztes Beispiel wird dann noch eine Ellipse gezeichnet. Die Methode drawEllipse() arbeitet dabei ähnlich wie drawRectangle(). Als Parameter werden also in unserem Beispiel die Koordinaten für die obere linke Ecke und für die untere rechte Ecke übergeben und Zend_Pdf füllt dieses imaginäre Rechteck dann mit der Ellipse.

Wenn Sie dieses fünfte Beispiel aufrufen, fällt Ihnen sicherlich auf, dass die drei Linien vor dem ersten Kreis, aber hinter dem zweiten Kreis und der Ellipse gezeichnet wurden. Daran erkennen Sie, dass die Reihenfolge, in der die einzelnen geometrischen Elemente gezeichnet werden, darüber entscheidet, welches Element vorne steht und welche Elemente gegebenenfalls überdeckt werden.

Noch ein letzter Hinweis zu den geometrischen Formen: Das Tutorial von Alexander Veremyev enthält auch ein weiteres Beispiel, wie Sie mit Polygonen und ein wenig Geometrie auch einen schönen Stern zeichnen können.

[ header = Seite 3: Mit Styles arbeiten ]

Mit Styles arbeiten

Das sechste Beispiel in Listing 8 ähnelt dem vorherigen stark. Es zeichnet dieselben geometrischen Elemente und wird um eine farbige Textzeile ergänzt. Dabei werden wir dieses Mal jedoch mit den so genannten Styles arbeiten. Die Klasse Zend_Pdf_Style bietet die Möglichkeit, Stilangaben für Farben, Linienarten, Linienbreiten, Zeichensätze und Zeichensatzgrößen gesammelt für die Wiederverwendung zu speichern. Hier weitere Infos zu den Styles.

Im oberen Abschnitt des Beispiels definieren wir die diversen Styles für Überschrift, Textzeile, Rechteck, Kreis, Linien, Sektoren und Ellipse. Bei der Definition der Styles für die Sektoren 2 und 3 verwenden wir clone, um ein eigenes Zend_Pdf_Style-Objekt zu erhalten, bei dem sich nur die Füllfarbe von Sektor 1 unterscheidet. Beim Zeichnen der einzelnen Texte und geometrischen Elemente ändern wir dann jeweils den aktuellen Style der Seite mithilfe der setStyle()-Methode. Diese Vorgehensweise erleichtert die Wiederverwendung der Styles besonders dann, wenn verschiedene Styles im Fluss der PDF-Erstellung immer wieder verwendet werden sollen, z.B. beim Wechsel von Überschriften und normalem Text.

Einbinden von Grafiken

Das Zeichnen der geometrischen Elemente ist zwar ganz brauchbar – doch möchten Sie sicherlich auch mal fertige Grafiken in Ihr PDF integrieren, oder? Das siebte Beispiel in Listing 9 demonstriert die einfache Vorgehensweise für diese Anforderung. Mithilfe der Methode Zend_Pdf_Image::imageWithPath() wird eine Grafik geladen und in ein Zend_Pdf_Image-Objekt umgewandelt. Das Zeichnen übernimmt dann die drawImage()-Methode. Hierbei wird als erster Parameter das Zend_Pdf_Image-Objekt angegeben. Danach folgen vier Parameter für die Koordinaten für zwei Ecken. In unserem Beispiel sind das die linke untere Ecke und die rechte obere Ecke. Wenn Sie zuerst die linke obere Ecke und danach die rechte untere Ecke angeben, wird die Grafik gespiegelt. Das Verzerren einer Grafik ist auch problemlos möglich, wie Sie bei der zweiten Einbindung der Grafik erkennen können. Für das Einbinden von Grafiken werden derzeit die Formate JPG, PNG und TIFF unterstützt, wobei JPG-Grafiken die PHP-GD-Extension und PNG-Grafiken die ZLIB-Extension von PHP voraussetzen.

Arbeiten mit Metadaten

Zend_Pdf unterstützt auch das Ändern der so genannten Metadaten. Dies sind dokumentspezifische Angaben über Autor, Thema, Schlüsselwörter oder Änderungsdatum. Diese Angaben werden im Adobe Reader z.B. über das Menü Datei|Eigenschaften angezeigt. Unser achtes Beispiel demonstriert diese Möglichkeiten (Listing 10). Hier werden zuerst die Angaben zu den Maßen der DIN-A4-Seite in Textform ausgegeben. Zudem werden die Koordinaten für die linke obere Ecke und die rechte untere Ecke der Seite ermittelt und dort wird ein entsprechender Text platziert (abzüglich einiger Punkte, um den Text ausgeben zu können).

Entscheidend für die Angabe der Metadaten ist die Eigenschaft properties des Zend_Pdf-Objekts. Hier können Sie Angaben für Titel, Autor, Thema, Schlüsselwörter, Erstellungsdatum, Änderungsdatum usw. machen. Wenn Sie das Beispiel acht aufrufen und sich im Adobe Reader die Eigenschaften anzeigen lassen, sehen Sie, wie die einzelnen Felder durch Zend_Pdf gefüllt worden sind. Vergleichen Sie dieses PDF-Dokument einfach mit einem der vorherigen Beispiele, bei denen keine Änderungen in den Metadaten durch Zend_Pdf vorgenommen worden sind.

Eine PDF-Vorlage erstellen

In unserem nächsten Beispiel geht es nun ans Eingemachte. Eigentlich besteht das Beispiel aus zwei Beispielen. Zend_Pdf unterstützt nämlich nicht nur die komplette Neuerstellung eines PDF-Dokuments, sondern auch das Verändern eines bereits vorhandenen. Im Prinzip können Sie damit jedes PDF-Dokument der Version PDF V1.4 (Acrobat 5) mit Zend_Pdf laden und verändern. Die Dokumente müssen somit nicht zwingend mit Zend_Pdf erstellt worden sein. Im Folgenden nenne ich die PDF-Dokumente, die geladen werden, der Einfachheit halber nur noch PDF-Vorlagen. Ich empfehle dennoch, soweit es möglich ist, diese PDF-Vorlagen auch mit Zend_Pdf zu erstellen. In der Regel werden die PDF-Vorlagen verwendet, um an bestimmten Stellen im Dokument variable Inhalte einzufügen. Da Zend_Pdf intern mit Punkten (1/72 eines Inches) arbeitet, kann die Platzierung von Texten mit PDF-Vorlagen aus anderen Quellen durchaus problematisch werden.

Ich habe dies einmal mit einer PDF-Vorlage umsetzen müssen, welche die zu füllenden Textfelder nicht nach Punkten, sondern eher wahllos auf der Seite verteilt hatte. Es kann schon bei einer Vorlage, die nur aus einer DIN-A4-Seite besteht, aber 20 bis 30 Felder hat, die es zu befüllen gilt, ein paar Stunden dauern, bis man alle Texte einigermaßen gut platziert hat. Der BegriffPixelschubser bekommt da eine ganz neue Bedeutung. Deshalb wird im Beispiel neun, das Sie in Listing 11 finden, nun zuerst eine PDF-Vorlage erstellt. In dem Beispiel werden viele der bisher bekannten Möglichkeiten verwendet. Unter anderem werden Texte eingefügt, ein Rechteck und mehrere Linien gezeichnet, eine Grafik integriert und die Metadaten verändert. Die erstellte PDF-Vorlage wird dann gespeichert.

Wenn Sie das neunte Beispiel einmal starten, erhalten Sie ähnlich wie beim ersten Beispiel eine HTML-Seite mit einem Link zum generierten PDF-Dokument. Im Verzeichnis /public/pdf/ sollte danach die Datei pdfvorlage.pdf erstellt worden sein. Wenn Sie sich dieses PDF-Dokument anzeigen lassen, sehen Sie die fertige Vorlage, die im nächsten Beispiel nun mit Daten befüllt werden soll.

Eine Vorlage befüllen

In unserem zehnten und letzten Beispiel geht es nun darum, die PDF-Vorlage aus dem vorherigen Beispiel mit Daten zu befüllen. In Listing 12 sehen Sie die Actionmethode. Ihnen fällt sicherlich sofort auf, dass die eigentliche Generierung des PDFs nicht mehr in der Actionmethode des Controllers vorhanden ist. Hier werden lediglich der Dateiname der PDF-Vorlage sowie die zu verwendenden Daten an das View-Skript übergeben. Zudem werden wie gewohnt die entsprechenden Header gesetzt. Das heißt also, dass sich in diesem Beispiel nicht mehr der Controller, sondern das View-Skript um die Ausgabe der Daten kümmert. Dies entspricht meiner Meinung nach mehr dem Model-View-Controller-Konzept, ist aber vielleicht auch Geschmackssache.

Listing 13 zeigt nun das View-Skript, das für die Befüllung der PDF-Vorlage zuständig ist. Zuerst wird die PDF-Vorlage geladen, was mithilfe derZend_Pdf::load()-Methode recht einfach vonstatten geht. Als Nächstes wird die zu verändernde Seite ermittelt (die PDF-Vorlage hat nur eine einzelne Seite). Danach beginnt die Ausgabe der Mitgliedsdaten an den passenden Stellen im Formular. Bei dem Beschreibungstext besteht noch das Problem, dass der Text eventuell etwas länger ist, als es der Platz für die Beschreibung eigentlich zulässt. Hierbei bedienen wir uns also der Vorgehensweise für das Umbrechen von langen Texten, die Sie schon aus dem vierten Beispiel kennen. Der Unterschied ist nur, dass wir prüfen, ob der Platz verbraucht ist, und den Text bei Bedarf abkürzen. Als Nächstes wird noch das Foto des Mitglieds platziert, wobei der Rahmen stehen gelassen wird. Das Foto wird mit der drawImage()-Methode passend gemacht. Zu guter Letzt wird noch das Änderungsdatum des PDF-Dokuments in den Metadaten überschrieben bzw. auf der Seite ausgegeben.

Mit diesem Prinzip des Befüllens von PDF-Vorlagen sollte es ohne weiteres möglich sein, dynamisch PDF-Dokumente mit individuellen Daten zu befüllen. Dies könnten z.B. Rechnungen, Auftragsbestätigungen, Druckversionen von Produktblättern oder redaktionellen Artikeln u.v.m. sein. Speziell bei Rechnungen sollten Sie im Sinne Ihrer Kunden jedoch darauf achten, dass Zend_Pdf meines Wissens nach derzeit nicht das Hinzufügen von digitalen Signaturen unterstützt. PDF-Rechnungen ohne digitale Signatur können den Vorsteuerabzugsanspruch Ihrer Kunden gefährden. Aber dieses Thema würde den Rahmen dieses Artikels sprengen. Dennoch sollten Sie diesen Umstand im Hinterkopf behalten. Für Vorabrechnungen eignet sich die Vorgehensweise der mit Zend_Pdf befüllten Rechnungen aber auf jeden Fall.

Fazit

Mit Zend_Pdf bietet das Zend Framework ein sehr brauchbares Werkzeug, um PDF-Dokumente neu zu erstellen bzw. vorhandene PDF-Dokumente zu bearbeiten, um z.B. eine PDF-Vorlage dynamisch mit Daten zu befüllen. Zend_Pdf bietet neben den hier besprochenen Möglichkeiten auch noch weitere Funktionen, wie das Einbinden und Extrahieren von weiteren Zeichensätzen, Rotationen, Transparenz oder die Einschränkung des Zeichenbereichs. Weitere Informationen dazu finden Sie im Manual zu Zend_Pdf.

Unsere Redaktion empfiehlt:

Relevante Beiträge

Meinungen zu diesem Beitrag

X
- Gib Deinen Standort ein -
- or -