Trascinare un THotPDF su un form in fase di progettazione va bene per un prototipo rapido, ma lega il componente alla durata di vita del form, che raramente è ciò che il codice di produzione desidera. Un generatore di report che viene eseguito a ogni clic su un pulsante, un thread di servizio che elabora esportazioni notturne in batch, una classe helper che non ha alcun form: in ciascuna di queste situazioni si vuole che il componente esista esattamente per la durata di un singolo job PDF e poi scompaia. Ciò significa allocazione a runtime, e cambia due cose che vale la pena comprendere prima di scrivere la prima riga: chi possiede l'oggetto e come viene eseguita la pulizia quando qualcosa va storto
Semantica dell'owner nella VCL
Ogni costruttore di componenti VCL accetta un parametro Owner di tipo TComponent*. Passare this (il form) registra il nuovo oggetto nella lista dei componenti posseduti dal form, quindi se il form viene distrutto mentre il componente è ancora vivo, la VCL lo libera automaticamente. Passare nullptr significa nessun owner: vi assumete la piena responsabilità del puntatore, e nulla lo pulirà al posto vostro se un'eccezione svolge lo stack prima del vostro delete esplicito
Per un'esportazione una tantum che si completa all'interno di una singola funzione, entrambe le scelte funzionano, ma le due hanno modalità di errore diverse. Con this come owner, un leak è impossibile finché il form prima o poi si chiude; con nullptr, il puntatore deve raggiungere un blocco __finally. In pratica, il pattern nullptr più __finally è leggermente più pulito per gli oggetti a vita breve, perché rende visibile a colpo d'occhio il confine del ciclo di vita ed evita che il form accumuli oggetti posseduti che erano pensati per essere temporanei
Struttura a prova di eccezioni
La generazione di PDF può fallire per motivi che non hanno nulla a che fare con l'API: la directory di output è in sola lettura, manca un file di font, uno stream si svuota prematuramente o i dati forniti dal chiamante superano un limite di lunghezza. Qualunque sia la causa, il percorso di pulizia deve essere eseguito. Il modo idiomatico in C++Builder per garantirlo è 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;
}
}
Alcuni aspetti di quel listato meritano di essere evidenziati. L'owner è nullptr, il che rende esplicito il ciclo di vita. Compression e FontEmbedding vengono impostati prima di BeginDoc: entrambe sono opzioni a livello di documento che HotPDF applica all'apertura del documento, e assegnarle in seguito non ha alcun effetto. TextOut accetta coordinate in punti misurate dall'angolo inferiore sinistro della pagina, con Y crescente verso l'alto; la coppia 72, 720 posiziona il testo vicino all'angolo superiore sinistro di una pagina formato letter con un margine sinistro di un pollice. Il delete Pdf nel blocco __finally viene eseguito indipendentemente dal fatto che BeginDoc, il disegno o EndDoc abbiano sollevato o meno un'eccezione
Evitate di chiamare qualsiasi metodo su Pdf dopo delete. Se il puntatore è memorizzato in una variabile membro, impostatelo a nullptr subito dopo l'eliminazione, in modo che qualsiasi accesso accidentale successivo produca un crash pulito anziché una corruzione silenziosa
Configurazione del progetto
C++Builder individua THotPDF attraverso una combinazione di percorsi di include, percorsi di libreria e una direttiva pragma. L'header generato risiede accanto a HPDFDoc.pas nella directory dei sorgenti di HotPDF; aggiungete quella directory a Progetto > Opzioni > Compilatore C++ > Percorso di include. La direttiva #pragma link "HPDFDoc" dice al linker di includere l'unità compilata senza doverla elencare manualmente nel file di progetto. Se utilizzate il package runtime invece del linking statico, installate prima i package di design e runtime di HotPDF; il pragma resta comunque valido
Mantenete invariato il nome dell'unità HPDFDoc. C++Builder deriva il nome dell'header dal nome dell'unità Pascal, quindi rinominare il file o usare un alias di percorso nel pragma rompe silenziosamente la risoluzione
Ambito di visibilità e job multi-documento
Per una singola esportazione attivata da un'azione dell'utente, una variabile locale con ambito limitato al gestore del pulsante è la risposta giusta: viene creata, usata e distrutta all'interno di un unico frame di chiamata, e l'intento è ovvio per chiunque legga il codice in seguito. L'alternativa in fase di progettazione è giustificata quando lo stesso form guida un flusso di lavoro continuo, come un pannello di anteprima di stampa che ricostruisce il documento ogni volta che l'utente cambia un'impostazione; in quel caso, mantenere vivo il componente e chiamare ripetutamente BeginDoc/EndDoc è meno invasivo che allocare e rilasciare ripetutamente oggetti sull'heap
Per i job batch che producono molti documenti in sequenza, dedicare un THotPDF per documento vale il costo di allocazione. Lo stato non si trascina tra i documenti se non c'è alcun oggetto a trascinarlo, e questa è una classe di bug intermittenti che non dovrete mai debuggare. Allocare, generare, eliminare, ripetere
Una proprietà che compare in diverse demo di HotPDF è AutoLaunch, che apre il file generato nel visualizzatore PDF di sistema subito dopo EndDoc. È utile mentre si scrive la prima bozza di un layout. In produzione, evitatela: aprite esplicitamente il percorso di output, verificate che il file esista e abbia una dimensione diversa da zero, registrate il risultato nei log e lasciate che sia il flusso di lavoro chiamante a decidere se un visualizzatore è pertinente. In un job batch, AutoLaunch avvia una finestra del visualizzatore per ogni documento e su alcuni sistemi bloccherà il processo in attesa che il visualizzatore venga chiuso
Il componente THotPDF e tutte le chiamate di disegno mostrate qui fanno parte del HotPDF Component per Delphi e C++Builder