Műszaki cikk

HotPDF komponens dinamikus létrehozása és felszabadítása C++Builderben

A THotPDF elhelyezése egy űrlapon tervezési időben (design time) megfelel egy gyors prototípushoz, de a komponenst az űrlap élettartamához köti, amire a termelési (production) kódnak ritkán van szüksége. Egy jelentésgenerátor, amely gombnyomásonként egyszer fut le, egy szolgáltatásszál (service thread), amely kötegeli (batch) az éjszakai exportálásokat, egy segédosztály, amelynek egyáltalán nincs űrlapja: mindezekben a helyzetekben azt szeretné, hogy a komponens pontosan egy PDF feladat idejéig létezzen, majd eltűnjön. Ez futásidejű lefoglalást (runtime allocation) jelent, ami két olyan dolgot változtat meg, amelyet érdemes megérteni az első sor megírása előtt: ki a tulajdonosa az objektumnak, és hogyan fut le a takarítás (cleanup), ha valami elromlik

Tulajdonosi szemantika a VCL-ben

Minden VCL komponens konstruktora kap egy TComponent* típusú Owner paramétert. A this (az űrlap) átadása regisztrálja az új objektumot az űrlap tulajdonában lévő komponensek listáján, így ha az űrlap megsemmisül, miközben a komponens még él, a VCL automatikusan felszabadítja. A nullptr átadása azt jelenti, hogy nincs tulajdonos: Ön vállalja a kizárólagos felelősséget a mutatóért, és semmi sem fogja azt letakarítani Ön helyett, ha egy kivétel (exception) felgöngyölíti a vermet az explicit delete utasítás előtt

Egy egyszeri (one-shot) exportáláshoz, amely egyetlen függvényen belül befejeződik, mindkét választás működik, de a kettőnek eltérő hibaállapotai (failure modes) vannak. Ha a this a tulajdonos, szivárgás lehetetlen, amíg az űrlap végül bezárul; a nullptr esetén a mutatónak el kell érnie egy __finally blokkot. A gyakorlatban a nullptr plusz __finally minta valamivel tisztább a rövid életű objektumok számára, mert egy pillantással láthatóvá teszi az élettartam határát, és elkerüli, hogy az űrlap felhalmozza azokat a tulajdonolt objektumokat, amelyeket ideiglenesnek szántak

Kivételbiztos struktúra

A PDF generálás olyan okokból is meghiúsulhat, amelyeknek semmi közük az API-hoz: a kimeneti könyvtár csak olvasható, hiányzik egy betűtípus-fájl, egy adatfolyam idő előtt kiürül (flushes prematurely), vagy a hívó által biztosított adat elér egy hosszkorlátot. Bármi is az ok, a takarítási útvonalnak (cleanup path) le kell futnia. Ennek garantálására a megszokott (idiomatic) C++Builder módszer a 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;
    }
}

Érdemes kiemelni néhány dolgot abban a kódrészletben. A tulajdonos nullptr, ami explicitté teszi az élettartamot. A Compression és a FontEmbedding a BeginDoc előtt vannak beállítva: mindkettő dokumentumszintű beállítás, amelyeket a HotPDF a dokumentum megnyitásakor rögzít, és későbbi hozzárendelésüknek nincs hatása. A TextOut a koordinátákat pontokban (points) kapja meg, az oldal bal alsó sarkától mérve, ahol az Y felfelé növekszik; a 72, 720 pár a szöveget egy letter méretű oldal bal felső része közelébe helyezi, egyhüvelykes (one-inch) bal margóval. A delete Pdf a __finally blokkban mindenképpen lefut, függetlenül attól, hogy a BeginDoc, a rajzolás vagy az EndDoc kivételt dobott-e vagy sem

Kerülje a Pdf bármely metódusának meghívását a delete után. Ha a mutató egy tagváltozóban van tárolva, törlés után azonnal állítsa nullptr-re, így minden véletlen későbbi hozzáférés tiszta összeomlást eredményez, nem pedig csendes adatsérülést

Projekt konfiguráció

A C++Builder a THotPDF-et include útvonalak, könyvtárútvonalak és egy pragma direktíva kombinációján keresztül találja meg. A generált fejléc a HPDFDoc.pas mellett található a HotPDF forráskönyvtárában; adja hozzá ezt a könyvtárat a Project > Options > C++ Compiler > Include path (Projekt > Beállítások > C++ fordító > Include útvonal) menüponthoz. A #pragma link "HPDFDoc" direktíva utasítja a linkert, hogy húzza be a lefordított unitot anélkül, hogy manuálisan fel kellene sorolni a projektfájlban. Ha a futásidejű csomagot (runtime package) használja statikus szerkesztés (static linking) helyett, először telepítse a HotPDF design és runtime csomagokat; a pragma továbbra is érvényes

Hagyja változatlanul a HPDFDoc unit nevet. A C++Builder a fejléc nevét a Pascal unit nevéből származtatja, így a fájl átnevezése vagy egy útvonal-álnév használata a pragmában csendben tönkreteszi a keresést

Hatókör és többdokumentumos feladatok

Egy felhasználói művelet által kiváltott egyszeri exportálás esetén a gombkezelőre korlátozott (scoped) helyi változó a megfelelő válasz: egyetlen hívási kereten belül jön létre, használják és semmisül meg, és a szándék nyilvánvaló mindenki számára, aki később olvassa a kódot. A tervezési idejű alternatíva akkor indokolt, ha ugyanaz az űrlap egy folyamatos munkafolyamatot hajt végre, például egy nyomtatási előnézet panelt, amely újraépíti a dokumentumot, valahányszor a felhasználó megváltoztat egy beállítást; ebben az esetben a komponens életben tartása és a BeginDoc/EndDoc ismételt meghívása kevésbé zavaró, mint a heap objektumok ismételt lefoglalása és felszabadítása

Sorozatban sok dokumentumot előállító kötegelt feladatok (batch jobs) esetén megéri a lefoglalási többletköltséget (allocation overhead), ha dokumentumonként egy THotPDF-re korlátozzuk a hatókört. Az állapot nem vivődik át a dokumentumok között, ha nincs objektum, amely átvigye, és ez egy olyan időszakos hibatípus, amelyet soha nem kell hibakereséssel (debug) megoldania. Foglalja le, generálja, törölje, ismételje meg

A HotPDF számos demójában megjelenő egyik tulajdonság az AutoLaunch, amely a generált fájlt a rendszer PDF-nézegetőjében nyitja meg közvetlenül az EndDoc után. Hasznos egy elrendezés első vázlatának megírásakor. A termelésben (production) hagyja ki: nyissa meg a kimeneti útvonalat explicit módon, ellenőrizze, hogy a fájl létezik és nem nulla méretű, naplózza az eredményt, és hagyja, hogy a hívó munkafolyamat eldöntse, releváns-e egy nézegető. Egy kötegelt feladatnál (batch job) az AutoLaunch dokumentumonként egy nézegetőablakot indít, és egyes rendszereken blokkolja a folyamatot, várva a nézegető bezáródására

A THotPDF komponens és az itt bemutatott összes rajzolási hívás a HotPDF Component része Delphi-hez és C++Builder-hez