Das Ablegen eines THotPDF auf einem Formular zur Entwurfszeit ist für einen schnellen Prototyp in Ordnung, bindet die Komponente jedoch an die Lebensdauer des Formulars, was selten das ist, was Produktionscode erfordert. Ein Berichtsgenerator, der einmal pro Klick auf eine Schaltfläche ausgeführt wird, ein Dienst-Thread, der nächtliche Exporte stapelweise verarbeitet, eine Hilfsklasse, die überhaupt kein Formular hat: In all diesen Situationen möchten Sie, dass die Komponente genau für die Dauer eines PDF-Auftrags existiert und dann verschwindet. Das bedeutet Laufzeitzuweisung, und es ändert zwei Dinge, die es wert sind, verstanden zu werden, bevor man die erste Zeile schreibt: wem das Objekt gehört und wie die Bereinigung abläuft, wenn etwas schief geht
Besitzer-Semantik in der VCL
Jeder VCL-Komponenten-Konstruktor akzeptiert einen Owner-Parameter vom Typ TComponent*. Die Übergabe von this (dem Formular) registriert das neue Objekt in der Liste der eigenen Komponenten des Formulars. Wenn das Formular zerstört wird, während die Komponente noch aktiv ist, gibt die VCL sie automatisch frei. Die Übergabe von nullptr bedeutet, dass es keinen Besitzer gibt: Sie übernehmen die alleinige Verantwortung für den Zeiger, und nichts bereinigt ihn für Sie, wenn eine Ausnahme den Stack abwickelt, bevor Sie Ihr explizites delete aufrufen
Für einen einmaligen Export, der innerhalb einer einzigen Funktion abgeschlossen wird, funktioniert jede der beiden Möglichkeiten, aber die beiden haben unterschiedliche Fehlermodi. Mit this als Besitzer ist ein Speicherleck unmöglich, solange das Formular schließlich geschlossen wird; mit nullptr muss der Zeiger einen __finally-Block erreichen. In der Praxis ist das Muster nullptr plus __finally für kurzlebige Objekte etwas sauberer, da es die Lebensdauergrenze auf einen Blick sichtbar macht und vermeidet, dass sich im Formular eigene Objekte ansammeln, die als temporär gedacht waren
Ausnahmesichere Struktur
Die PDF-Generierung kann aus Gründen fehlschlagen, die nichts mit der API zu tun haben: Das Ausgabeverzeichnis ist schreibgeschützt, eine Schriftartdatei fehlt, ein Stream wird vorzeitig geleert oder vom Aufrufer bereitgestellte Daten erreichen ein Längenlimit. Was auch immer die Ursache ist, der Bereinigungspfad muss ausgeführt werden. Der idiomatische Weg in C++Builder, dies zu garantieren, ist try/__finally:
#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
#pragma package(smart_init)
#pragma link "HPDFDoc"
#pragma resource "*.dfm"
TForm1 *Form1;
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
void __fastcall TForm1::Button1Click(TObject *Sender)
{
THotPDF* Pdf = new THotPDF(nullptr);
try
{
Pdf->FileName = "output.pdf";
Pdf->Compression = cmFlateDecode;
Pdf->FontEmbedding = true;
Pdf->BeginDoc();
Pdf->CurrentPage->SetFont("Arial", TFontStyles(), 12);
Pdf->CurrentPage->TextOut(72, 720, 0, L"Hello from C++Builder");
Pdf->EndDoc();
}
__finally
{
delete Pdf;
}
}
Einige Dinge in dieser Auflistung sind erwähnenswert. Der Besitzer ist nullptr, was die Lebensdauer explizit macht. Compression und FontEmbedding werden vor BeginDoc gesetzt: Beides sind Optionen auf Dokumentebene, die HotPDF übernimmt, wenn das Dokument geöffnet wird, und eine nachträgliche Zuweisung hat keine Auswirkung. TextOut akzeptiert Koordinaten in Punkten, gemessen von der unteren linken Ecke der Seite, wobei Y nach oben zunimmt; das Paar 72, 720 platziert Text in der Nähe der oberen linken Ecke einer Seite im Letter-Format mit einem linken Rand von einem Zoll. Das delete Pdf im __finally-Block wird ausgeführt, unabhängig davon, ob BeginDoc, das Zeichnen oder EndDoc eine Ausnahme ausgelöst haben oder nicht
Vermeiden Sie den Aufruf von Methoden für Pdf nach delete. Wenn der Zeiger in einer Elementvariablen gespeichert ist, setzen Sie ihn sofort nach dem Löschen auf nullptr, damit ein versehentlicher späterer Zugriff einen sauberen Absturz anstelle einer stillen Beschädigung verursacht
Projektkonfiguration
C++Builder findet THotPDF über eine Kombination aus Include-Pfaden, Bibliothekspfaden und einer Pragma-Direktive. Der generierte Header befindet sich zusammen mit HPDFDoc.pas im HotPDF-Quellverzeichnis; fügen Sie dieses Verzeichnis zu Project > Options > C++ Compiler > Include path hinzu. Die Direktive #pragma link "HPDFDoc" weist den Linker an, die kompilierte Unit einzuziehen, ohne sie manuell in der Projektdatei aufzuführen. Wenn Sie das Laufzeitpaket anstelle von statischem Linken verwenden, installieren Sie zuerst die Design- und Laufzeitpakete von HotPDF; das Pragma gilt weiterhin
Belassen Sie den Unit-Namen HPDFDoc unverändert. C++Builder leitet den Header-Namen vom Pascal-Unit-Namen ab, sodass das Umbenennen der Datei oder die Verwendung eines Pfad-Alias im Pragma das Nachschlagen stillschweigend unterbricht
Geltungsbereich und Aufträge für mehrere Dokumente
Für einen einzelnen Export, der durch eine Benutzeraktion ausgelöst wird, ist eine auf den Schaltflächen-Handler beschränkte lokale Variable die richtige Antwort: Sie wird innerhalb eines Aufrufrahmens erstellt, verwendet und zerstört, und die Absicht ist für jeden offensichtlich, der den Code später liest. Die Entwurfszeitalternative ist gerechtfertigt, wenn dasselbe Formular einen kontinuierlichen Workflow steuert, wie z. B. ein Seitenansichts-Bereich, der das Dokument immer dann neu aufbaut, wenn der Benutzer eine Einstellung ändert; in diesem Fall ist es weniger störend, die Komponente am Leben zu erhalten und wiederholt BeginDoc/EndDoc aufzurufen, als Heap-Objekte wiederholt zuzuweisen und freizugeben
Bei Batch-Aufträgen, die viele Dokumente nacheinander erstellen, ist es den Zuweisungsaufwand wert, ein THotPDF pro Dokument festzulegen. Der Zustand wird nicht zwischen Dokumenten übertragen, wenn es kein Objekt gibt, das ihn überträgt, und das ist eine Klasse von zeitweiligen Fehlern, die Sie nie beheben müssen. Zuweisen, generieren, löschen, wiederholen
Eine Eigenschaft, die in mehreren HotPDF-Demos vorkommt, ist AutoLaunch, die die generierte Datei sofort nach EndDoc im System-PDF-Viewer öffnet. Das ist nützlich, während man den ersten Entwurf eines Layouts schreibt. In der Produktion sollten Sie darauf verzichten: Öffnen Sie den Ausgabepfad explizit, stellen Sie sicher, dass die Datei existiert und eine Größe ungleich Null hat, protokollieren Sie das Ergebnis und lassen Sie den aufrufenden Workflow entscheiden, ob ein Viewer relevant ist. Bei einem Batch-Auftrag startet AutoLaunch ein Viewer-Fenster pro Dokument und blockiert den Prozess auf einigen Systemen, während auf das Schließen des Viewers gewartet wird
Die THotPDF-Komponente und alle hier gezeigten Zeichenaufrufe sind Teil der HotPDF-Komponente für Delphi und C++Builder