URL dieses Artikels:

zu Ausgabe: 05.2004
C++ + PDF = ClibPDF!
Mit ClibPDF PDF-Dateien direkt aus Windows-C++-Anwendungen heraus erstellen
von Tobias Wassermann
Jeder möchte sie heute haben - denn sie sind universell: PDF-Dokumente. Aber man möchte sich als Entwickler nicht gerade selbst eine Library zum Erstellen von PDF-Dateien schreiben (was ja durchaus verständlich ist). Und außerdem: Warum das Rad neu erfinden, wenn es mit ClibPDF so einfach geht? Hier gibt es einen kleinen Einstieg in die ClibPDF.

Es gibt natürlich auch andere Methoden, um ein PDF aus einer Anwendung heraus zu erzeugen. Man kann zum einen ein externes Tool aufrufen, das die Eingabedaten für die zu erstellende PDF-Datei als XML-Datei erwartet oder man nutzt einen PDF-Drucker - meist mittels Ghostscript und Port-Umleitungen oder einem Umweg über einen Samba-Server umgesetzt. Nur haben diese Methoden einen Nachteil, der gleichzeitig auch ihr Vorteil ist: Sie sind extern. Das ist sicher ein klarer Vorteil - denn soll der PDF-Drucker (oder die dahinter stehende Ghostscript-Version) aktualisiert werden, muss man nicht die Anwendung neu übersetzen und verteilen. Andererseits muss man bei der direkten Integration der PDF-Generierung in die eigene Applikation nichts auf dem Client-Rechner zusätzlich installieren - man kann also immer vom Funktionieren dieser Ausgabeart ausgehen.

Doch indem man auf das direkte Erstellen dieses ultimativen, plattformübergreifenden Formats direkt aus der Anwendung heraus verzichtet, entsagt man auch Features, die man gern nutzen würde, die einem mit einem PDF-Drucker (bei diesem steht nur die Druck-API zur Verfügung) oder einem XML-basierten PDF-Compiler nicht zur Verfügung stehen (etwa Verlinkungen im PDF-Dokument).

Für und Wider
Die ClibPDF-Library zum Erstellen von PDF-Dateien aus C- und C++-Anwendungen heraus dürfte auch kaufmännische Vorgesetzte freuen: Es gibt für die kommerzielle Nutzung einen Pauschalpreis von 1000 US-Dollar - unabhängig davon, wie oft eine Anwendung, die diese Bibliothek nutzt, verkauft wird (es ist also keine volumen- sondern eine produktabhängige Software, für Webserver zahlt man pro IP). Selbstverständlich kann man das ganze vorher 30 Tage testen, es soll ja niemand die Katze im Sack kaufen. Für Privatanwender, non-profit, wissenschaftliche und staatliche Organisationen sowie Bildungsträger fällt keine Lizenzgebühr an.

Nun noch zu den unumgänglichen Nachteilen: ClibPDF kann vieles, ist aber dennoch keine eierlegende Wollmilchsau. Es können nur PDF-Dateien erstellt werden. Es ist weder möglich, bestehende PDF-Dateien zu ändern, noch einzulesen oder gar zwei Dateien zu einem Dokument zusammenzufügen. Auch das Koordinatensystem - mathematisch äußerst korrekt - ist (wenn man hier den Maßstab z.B. des Systems beim Zeichnen von Fenstern anlegt), etwas gewöhnungsbedürftig. Mehr Hinweise zum Umgang mit dem Koordinatensystem finden Sie im Kasten Etwas eigenwillig - das Koordinatensystem.

Los geht's
Das gute an dieser Library: Man braucht weder auf dem Entwicklungsrechner noch auf dem Client Software oder gar Treiber zu installieren. Lediglich eine Anwendung für das Betrachten von PDF-Dateien (etwa der Adobe Acrobat Reader) sollte vorhanden sein, falls man die generierten PDF-Dokumente von ClibPDF nach dem Erzeugen mittels der Funktion cpdf_launchPreview gleich anzeigen lassen möchte. Vor der ersten Implementierung muss die ClibPDF allerdings einmalig kompiliert und übersetzt werden. Mehr Informationen hierzu sind im Kasten Download und Start zu finden.

Bevor wir nun so richtig starten noch etwas vorweg: ClibPDF verwendet grundsätzlich als Maßeinheit ein eigenes System. Der Umrechnungsfaktor liegt hier bei ca. 2,83 Punkten pro Millimeter. Eine X-Koordinate, die einen Zentimeter vom linken Rand entfernt ist, entspricht also 28,3 Punkten. Doch diese Umrechnung kann man sich sparen - man gewöhnt sich recht schnell an diese Maßeinheit. Zudem hat es sich bewährt, nicht etwa absolute Werte für die X- und Y-Koordinaten zu verwenden, sondern auf prozentuale Werte zu setzen. Das bedeutet, dass beispielsweise 1% von der Dokumentbreite als X-Koordinate verwendet wird. Der Vorteil ist hier, dass das Layout auch bei einem kleineren oder größerem Seitenformat (falls ein Wechsel einmal notwendig ist) fast 1:1 erhalten bleibt. Es sind nur geringe Anpassungsarbeiten notwendig (diese stehen und fallen auch mit der Genauigkeit, mit der die Koordinaten prozentual von der Dokumentbreite und Höhe berechnet werden). Und für alle, die doch mit einer bekannten Einheit arbeiten möchten, wird Zoll genügen müssen, da dies im Gegensatz zum metrischen System von ClibPDF direkt unterstützt wird. Hierzu gibt es stets eigene Funktionen, die um den Namensteil raw erweitert worden sind (cpdf_rawLineto ist die zollfähige cpdf_lineto-Variante). Soviel nun zur trockenen Theorie - nun geht es endlich in die Praxis über.

Der weitere Einstieg gestaltet sich dann auch recht einfach. Die Standardwerte zur Initialisierung (etwa die maximale Seitenzahl für die Ausgabedatei) sind schon recht gut - und sollten nur bei Bedarf geändert werden. Falls beispielsweise mehr als 100 Verlinkungen (zu anderen Dokumenten oder innerhalb des Dokumentes) eingefügt werden sollen, müssen die Standards angegriffen werden. Von vornherein werden stets immer nur eine bestimmte Anzahl Objekte pro PDF von ClibPDF unterstützt. Diese Vorgaben sind für kleinere PDF-Dateien gedacht. Wichtig ist: Standards müssen vor dem cpdf_open-Aufruf gesetzt werden. Man kann diese Limits entweder als einzelne Werte mittels cpdf_setGlobalDocumentLimits (unmittelbar nach cpdf_open)definieren, oder man nutzt hierzu die Struktur CPDFdocLimits, die als Argument dann an die Funktion cpdf_open übergeben werden kann. Ist dies getan, muss die Funktion cpdf_open(int mode, CPDFdocLimits *docLimits) aufgerufen werden. Der Parameter mode wird in der aktuellen Version der Library nicht unterstützt, sollte also immer auf 0 gesetzt sein; der zweite Parameter kann NULL sein oder zeigt entsprechend auf die notwendige Struktur. Die Struktur enthält folgende Mitglieder (alle vom Typ int, in eckigen Klammern ist der Vorgabewert angegeben): nMaxPages [100], nMaxFonts [180], nMaxImages [100], nMaxAnnots [100], nMaxObjects [5000]. Möchte man also nun ein Dokument erstellen, das beispielsweise 200 Seiten unterstützt, so muss nMaxPages auf 200 gesetzt werden - sollen alle anderen Werte beim Standard belassen werden, kann dort -1 angegeben werden. In diesem Fall können bis zu 200 Seiten erzeugt werden. Falls man also auf Nummer sicher gehen möchte, ohne vor dem Erstellen des PDF's im Quelltext auszurechnen, wie viele Seiten denn benötigt werden, kann man den Wert generell etwas höher setzen. Von dieser Funktion wird schließlich ein Pointer vom Typ CPDFdoc zurückgegeben. Diese initialisierte Dokument-Referenz muss bei 90 % der Funktionen übergeben werden. Das ermöglicht es, in einer Anwendung - etwa in mehreren parallel laufenden Threads - zeitgleich mehrere PDF-Dokumente zu erzeugen. Die Library ist seit Version 2.0 Thread-Safe. Man sollte allerdings auf keinen Fall versuchen, ein CPDFdoc aus mehreren Threads gleichzeitig zu bearbeiten, da die Abhängigkeiten in der Befehlsreihenfolge teilweise sehr wichtig sind.

Nach dem cpdf_open-Aufruf, der das Dokument initialisiert hat, muss stets der benutzte Memorystream initialisiert werden (in diesen werden alle Daten zwischengespeichert, bis das PDF fertig ist und auf der Festplatte abgelegt wird). Dies geschieht mittels cpdf_init(CPDFdoc *pdf) - hier wird nur die Referenz aus cpdf_open übergeben. Wer nun nach der Erwähnung eines Memorystreams Angst vor intensiven Speicherverbrauch hat, sei beruhigt, denn der hält sich in Grenzen und kann sogar durch den Aufruf einiger Funktionen während der Speicherung im RAM noch reduziert werden, indem beispielsweise einzelne Seiten mittels cpdf_finalizePage abgeschlossen werden. Falls man auch auf dem Datenträger Platz sparen möchte, kann man vor cpdf_init noch cpdf_enableCompression(CPDFdoc *pdf, int compressON) nutzen (dabei wird für compressON YES übergeben), um die PDF-Datei mittels ZLIB zu komprimieren.

Seitenweise
Bevor man nun Text- und Grafikfunktionen nutzen kann, muss erst einmal eine Seite initialisiert werden. Alle Text- und Grafikfunktionen nutzen die aktuell gesetzte Seite als Ziel. cpdf_pageInit ist also unbedingt notwendig,. Die Parameter sind das PDF-Handle, die Seitennummer (diese sollte 1 oder höher sein), gefolgt von der Formatangabe PORTRAIT oder LANDSCAPE (Seite ist hochkant oder quer), gefolgt vom mediaboxstr und dem cropboxstr - die beide die Größe der Seite angeben, hierfür können für Standardgrößen Konstanten wie etwa A4 oder LETTER übergeben werden. Für andere, benutzerdefinierte Formate muss ein entsprechender Formatstring übergeben werden (vordefinierte Formate sind in CPDFlib.h zu finden). ClibPDf hat in dieser Hinsicht auch ein interessantes Feature zu bieten: Jede Seite kann ein eigenes Format haben, es müssen nicht alle Seiten das gleiche Format besitzen. Falls eine Seite abgeschlossen werden kann, sollte cpdf_finalizePage(CPDFdoc *pdf, int page) genutzt werden. Danach wird eine Seite bereits im Speicher gepackt, vorausgesetzt, die Kompression wurde aktiviert.

Ähnlich verhält es sich mit Textoperationen. Diese greifen erst, nachdem die Funktion cpdf_beginText aufgerufen wurde. Zum Zeichnen mittels cpdf_stroke und cpdf_fill kommt es bei Text erst nach cpdf_endText(CPDFdoc *pdf).
Es gibt allein fünf Funktionen in ClibPDF um Text auszugeben (zuzüglich der raw-Varianten): Entweder der Text wird einfach nur so in das PDF projiziert, ausgerichtet ausgegeben, von einer Textbox begrenzt oder gar in eine Textbox eingepasst. Dabei kann der Text in den Textboxen auch noch gedreht werden. Wir werden uns nun einmal an die drei gebräuchlichsten Funktionen machen. Um nun nicht jeden mit endlos langen Parameterdefinitionen zu quälen, sollte man - wenn man sich denn dann genauer für einen Befehl interessiert - die Dokumentation konsultieren. Wir sehen uns folgende Funktionen einmal etwas genauer an: cpdf_textAligned, cpdf_textBox und cpdf_textBoxFit. Mit dem ersten Aufruf kommt man am einfachsten klar - denn das Ergebnis ist in jeder Textverarbeitung sichtbar. Es geht darum, einen Text um einen Punkt vertikal und horizontal auszurichten, man kann damit Text zentrieren, linksseitig oder rechtsseitig ausrichten. Dabei ist die horizontale nicht an die vertikale Ausrichtung gebunden. Will heißen: Ein Text ist horizontal zentriert, kann aber vertikal linksseitig stehen. cpdf_textAligned setzt im Gegensatz zu den cpdf_textBox-Funktionen ein cpdf_setFont zum Setzen der Schriftart und -größe voraus. Welche Schriften und Gestaltungsmöglichkeiten es gibt, ist in der Dokumentation ausführlich beschrieben. Dort findet sich sogar der Verweis auf eine Barcode-Schrift, mit der man dann beispielsweise auf einfache Weise Barcodes in PDF-Dateien integrieren kann.
Nun zu den Textbox-Funktionen, bei denen es zwei Möglichkeiten gibt, um Text darin zu platzieren: Zum einen schreibt man Text in eine Textbox, so wie er hineinpasst, was dort nicht mehr untergebracht werden kann, muss wo anders hin. Zum anderen kann man allerdings die Library auch anweisen, den gegebenen Text solange von der Schriftart her und dem Zeilenabstand anzupassen, bis der Text in die vorgegebene Box passt.
Zu erst das Einfachere: Wir zeigen dem Text nun einmal Grenzen auf. Dazu muss man allerdings eines vorweg wissen: Text, der diesen Funktionen übergeben wird, muss unbedingt mit zwei Zeilenschaltungen (\n\n) enden, da sonst die ganze Aktion fehlschlägt. cpdf_textBox ermöglicht es, Text innerhalb vorgegebener Grenzen anzuzeigen, definiert durch eine Ecke mit den Koordinaten X und Y und die Breite sowie Höhe. Diese Kombination bildet ein Rechteck: die Textbox. Ein Pointer auf den Beginn des überschüssigen Textes, also Text, der nicht mehr in diese Begrenzung hinein gepasst hat, ist das Rückgabeergebnis dieser Funktion. Damit kann man dann beispielsweise mehrseitige PDF-Dateien erzeugen, in denen kein Text verloren geht. Die Textbox-Befehle ermöglichen auch verschiedenste Gestaltungsmöglichkeiten, wie etwa das Drehen des Textes in jedem beliebigen Winkel, das Definieren des Abstandes zwischen Absätzen, das automatische Einfügen von Zeilenumbrüchen und das Ausrichten in den üblichen Variationen (linksbündig, zentriert, rechtsbündig oder als blockbündig).

cpdf_textBoxFit geht nun einen etwas anderen Weg. Es wird hier nicht die Stelle des Textes zurückgegeben, ab der die Grenzen der Vorgabeflächen überschritten würden, sondern es wird versucht, den Text entsprechend einzupassen. Hierzu werden in erster Linie der Linespace (Abstand zwischen zwei Textzeilen) und die Schriftgröße schrittweise herabgesetzt. Die Schrittstärke wird beim Funktionsaufruf definiert. Ein Hinweis: Hier sollte für den Parameter fsdecrement auf keinen Fall 0 übergeben werden, da dies eventuell zu einer Endlosschleife führt. Bewährt haben sich Werte zwischen 0,2 und 0,3 sowie die in der Dokumentation erwähnten 0,5 und 1,0.

Ecken, Kanten und etwas Farbe
Von Text allein wird niemand glücklich. Hierfür gibt es in ClibPDF sehr gute Grafikfunktionen. Dies geht von einfachen Funktionen wie dem cpdf_lineTo (Zeichnen einer Line vom aktuellen Punkt aus) oder cpdf_rect bis hin zu gehobenen Funktionen wie cpdf_curveto zum Zeichnen eines kubischen Kurvensegmentes. Interessant in diesem Zusammenhang sind auch die Pfad-Funktionen, mit denen in Verbindung mit anderen Grafikfunktionen und der Abschluss-Funktion cpdf_closepath eine geschlossene Fläche erzeugt werden kann.
Das Zeichnen von geometrischen Objekten hat etwas für sich. Doch wenn man danach ein PDF abschließt und es sich ansieht, wird man nichts sehen, denn es fehlt die Farbe im Spiel. Es muss den letzten Zeichnungsoperationen explizit noch Farbe gegeben werden. Dies geschieht mit den Funktionen cpdf_fill, cpdf_stroke und cpdf_fillAndStroke. Diese Funktionen Füllen das Innere der letzten Operationen bzw. markieren die Kanten mit der vorgegebenen Farbe, die durch cpdf_setrgbcolor, cpdf_setgray, cpdf_setgrayFill, cpdf_setrgbFill, cpdf_setcmykFill, cpdf_setgrayStroke, cpdf_setrgbcolorStrole oder cpdf_setcmykStroke angegeben wurde. Hierbei müssen Farbewerte als Dezimalwert zwischen null und eins angegeben werden. Dies ist wohl auch etwas gewöhnungsbedürftig, da sonst Farbanteile meist zwischen null und 255 definiert sind. Der Aufruf der Fill- oder Stroke-Funktion setzt auch gleich einen neuen Pfad. Mit dem nächsten Fill- oder Stroke-Aufruf werden nur die Objekte gefüllt oder markiert, die neu dazu gekommen sind.

Besondere Features
Nun gibt es noch einige Features, die bei dieser Library sehr interessant sind. Zum einen gibt es - was definitiv ein Muss-Merkmal ist - die Möglichkeit, Bilder einzubinden. Dies geschieht mit der Funktion cpdf_importImage. Die Public-Version unterstützt allerdings nur JPG als Importformat. In der Premium-Version kommen dann noch TIFF und PDFIMG (ein speziell optimiertes Bildformat) dazu.
Ein weiteres Feature sind die Achsen-Funktionen, mit denen man sogar gezielt Zeit-Achsen erstellen kann; zu guter letzt seien auch noch die Annotations erwähnt, mit denen man Aktionen ausführen kann (so kann man zum Beispiel Links im PDF-Dokument setzen).
Alles in allem bietet ClibPDF eine komfortable Möglichkeit, mittels C und C++ PDF-Dokumente zu erstellen. Dabei ist es so einfach wie möglich gehalten, bietet aber im Ergebnsis dennoch eine möglichst große Komplexibilität.

© 2004 Software & Support Verlag GmbH. Vervielfältigung nur mit Genehmigung des Verlags. Fragen?