Teknisk artikel

Dynamisk oprettelse og frigivelse af en HotPDF-komponent i C++Builder

At placere en THotPDF på en formular på designtidspunktet fungerer fint til en hurtig prototype, men det binder komponenten til formularens levetid, hvilket sjældent er det, produktionskode ønsker. En rapportgenerator, der kører én gang pr. knapklik, en servicetråd, der batcher eksporter om natten, en hjælpeklasse, der slet ikke har en formular: i alle disse situationer ønsker man, at komponenten kun eksisterer i præcis varigheden af ét PDF-job og derefter forsvinder. Det betyder allokering ved kørselstid, og det ændrer to ting, der er værd at forstå, inden man skriver den første linje: hvem ejer objektet, og hvordan oprydning sker, når noget går galt

Ejerens semantik i VCL

Enhver VCL-komponentkonstruktør tager en Owner-parameter af typen TComponent*. At sende this (formularen) registrerer det nye objekt i formularens ejede-komponent-liste, så hvis formularen ødelægges, mens komponenten stadig lever, frigiver VCL den automatisk. At sende nullptr betyder ingen ejer: du har det fulde ansvar for markøren, og intet vil rydde op efter dig, hvis en undtagelse ruller stakken tilbage før dit eksplicitte delete

For en enkelt eksport, der afsluttes inden for én funktion, virker begge valg, men de to har forskellige fejlmodi. Med this som ejer er en lækage umulig, så længe formularen til sidst lukkes; med nullptr skal markøren nå en __finally-blok. I praksis er nullptr plus __finally-mønsteret lidt renere for kortlivede objekter, fordi det gør levetidsgrænsen synlig på et blik og undgår, at formularen akkumulerer ejede objekter, der var beregnet til at være midlertidige

Undtagelsessikker struktur

PDF-generering kan fejle af årsager, der ikke har noget med API'et at gøre: outputmappen er skrivebeskyttet, en skriftfil mangler, en strøm skylles for tidligt, eller afsender-leverede data rammer en længdegrænse. Uanset årsagen skal oprydningsstien køre. Den idiomatiske C++Builder-måde at garantere det på er 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;
    }
}

Et par ting i den liste er værd at fremhæve. Ejeren er nullptr, hvilket gør levetiden eksplicit. Compression og FontEmbedding sættes før BeginDoc: begge er dokumentniveau-indstillinger, som HotPDF binder, når dokumentet åbnes, og at tildele dem bagefter har ingen effekt. TextOut tager koordinater i punkter målt fra sidens nederste venstre hjørne, Y stigende opad; parret 72, 720 placerer tekst nær øverste venstre hjørne af en letter-størrelse side med en tommes venstre margen. delete Pdf i __finally-blokken kører, uanset om BeginDoc, tegning eller EndDoc rejste en undtagelse

Undgå at kalde nogen metode på Pdf efter delete. Hvis markøren er gemt i en medlemsvariabel, sæt den til nullptr umiddelbart efter sletning, så en eventuel utilsigtet efterfølgende adgang resulterer i et rent nedbrud frem for stille korruption

Projektkonfiguration

C++Builder finder THotPDF via en kombination af inkluderingstier, bibliotekstier og et pragma-direktiv. Den genererede header bor ved siden af HPDFDoc.pas i HotPDF-kildekataloget; tilføj det katalog til Project > Options > C++ Compiler > Include path. Direktivet #pragma link "HPDFDoc" fortæller linkeren at trække den kompilerede enhed ind uden at liste den manuelt i projektfilen. Hvis du bruger runtime-pakken i stedet for statisk linking, installér HotPDF-design- og runtime-pakkerne først; pragmaet gælder stadig

Behold enhedsnavnet HPDFDoc uændret. C++Builder udleder headernavnet fra Pascal-enhedsnavnet, så omdøbning af filen eller brug af et stialias i pragmaet bryder opslaget lydløst

Afgrænsning og jobs med flere dokumenter

For en enkelt eksport udløst af en brugerhandling er en lokal variabel afgrænset til knap-handleren det rigtige svar: den oprettes, bruges og ødelægges inden for én kaldramme, og hensigten er tydelig for alle, der læser koden senere. Design-tids-alternativet er berettiget, når den samme formular driver et kontinuerligt arbejdsforløb, f.eks. et forhåndsvisningspanel, der genopbygger dokumentet, hver gang brugeren ændrer en indstilling; i så fald er det mindre forstyrrende at holde komponenten i live og kalde BeginDoc/EndDoc gentagne gange frem for gentagne gange at allokere og frigive heap-objekter

For batchjobs, der producerer mange dokumenter i rækkefølge, er det besværet værd at afgrænse én THotPDF pr. dokument. Tilstand overføres ikke mellem dokumenter, hvis der ikke er noget objekt til at bære den, og det er én klasse af periodiske fejl, du aldrig behøver at debugge. Allokér, generér, slet, gentag

En egenskab, der optræder i flere HotPDF-demoer, er AutoLaunch, som åbner den genererede fil i systemets PDF-fremviser umiddelbart efter EndDoc. Den er nyttig, mens man skriver det første udkast til et layout. I produktion: spring den over, åbn outputstien eksplicit, bekræft at filen eksisterer og har en størrelse forskellig fra nul, log resultatet, og lad den kaldende arbejdsgang beslutte, om en fremviser er relevant. I et batchjob starter AutoLaunch ét fremviservindue pr. dokument og vil blokere processen på nogle systemer, mens den venter på, at fremviseren lukkes

THotPDF-komponenten og alle tegne­kald vist her er en del af HotPDF-komponenten til Delphi og C++Builder