Umístění THotPDF na formulář v době návrhu je v pořádku pro rychlý prototyp, ale váže komponentu k životnímu cyklu formuláře, což produkční kód zřídkakdy vyžaduje. Generátor sestav, který se spouští jedním kliknutím na tlačítko, vlákno služby, které dávkově zpracovává noční exporty, pomocná třída, která vůbec nemá formulář: v každé z těchto situací chcete, aby komponenta existovala přesně po dobu jedné úlohy PDF a poté zmizela. To znamená alokaci za běhu, a to mění dvě věci, kterým je dobré porozumět, než napíšete první řádek: kdo vlastní objekt a jak probíhá čištění, když se něco pokazí
Sémantika vlastníka ve VCL
Konstruktor každé komponenty VCL přijímá parametr Owner typu TComponent*. Předání this (formuláře) zaregistruje nový objekt do seznamu vlastněných komponent formuláře, takže pokud je formulář zničen, zatímco je komponenta stále živá, VCL ji automaticky uvolní. Předání nullptr znamená, že objekt nemá vlastníka: přebíráte výhradní odpovědnost za ukazatel a nic jej za vás nevyčistí, pokud výjimka rozvine zásobník před vaším explicitním delete
Pro jednorázový export, který se dokončí v rámci jediné funkce, funguje obojí, ale tyto dvě volby mají různé způsoby selhání. S this jako vlastníkem je únik nemožný, pokud se formulář nakonec zavře; s nullptr musí ukazatel dosáhnout bloku __finally. V praxi je vzor nullptr plus __finally pro objekty s krátkou životností o něco čistší, protože na první pohled zviditelňuje hranici životnosti a zabraňuje tomu, aby formulář shromažďoval vlastněné objekty, které měly být dočasné
Struktura bezpečná proti výjimkám
Generování PDF může selhat z důvodů, které nemají nic společného s API: výstupní adresář je pouze pro čtení, chybí soubor s písmem, stream se předčasně vyprázdní nebo data dodaná volajícím narazí na limit délky. Ať už je příčina jakákoli, musí se spustit cesta pro čištění. Způsob, jak to idiomaticky zaručit v C++Builderu, je pomocí 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;
}
}
Stojí za to upozornit na několik věcí v tomto výpisu. Vlastník je nullptr, což činí životnost explicitní. Compression a FontEmbedding jsou nastaveny před BeginDoc: obojí jsou možnosti na úrovni dokumentu, které HotPDF potvrzuje při otevření dokumentu, a jejich následné přiřazení nemá žádný vliv. TextOut přijímá souřadnice v bodech měřených od levého dolního rohu stránky, přičemž Y roste směrem nahoru; dvojice 72, 720 umístí text blízko levého horního rohu stránky velikosti Letter s levým okrajem jeden palec. Příkaz delete Pdf v bloku __finally se spustí bez ohledu na to, zda BeginDoc, vykreslování nebo EndDoc vyvolaly výjimku či nikoliv
Vyvarujte se volání jakékoli metody na objektu Pdf po zavolání delete. Pokud je ukazatel uložen v členské proměnné, nastavte jej bezprostředně po smazání na nullptr, aby jakýkoli náhodný pozdější přístup způsobil čistý pád spíše než tiché poškození
Konfigurace projektu
C++Builder vyhledává THotPDF pomocí kombinace cest k hlavičkovým souborům (include paths), cest ke knihovnám a direktivy pragma. Vygenerovaný hlavičkový soubor se nachází vedle HPDFDoc.pas ve zdrojovém adresáři HotPDF; přidejte tento adresář do Project > Options > C++ Compiler > Include path. Direktiva #pragma link "HPDFDoc" říká linkeru, aby zatáhl zkompilovanou jednotku, aniž byste ji museli ručně uvádět v souboru projektu. Pokud používáte běhový balíček namísto statického linkování, nainstalujte nejprve návrhové a běhové balíčky HotPDF; direktiva pragma stále platí
Ponechte název jednotky HPDFDoc nezměněný. C++Builder odvozuje název hlavičkového souboru z názvu Pascal jednotky, takže přejmenování souboru nebo použití aliasu cesty v direktivě pragma tiše rozbije vyhledávání
Rozsah platnosti (scoping) a úlohy s více dokumenty
Pro jeden export spuštěný akcí uživatele je lokální proměnná s rozsahem platnosti v obslužné rutině tlačítka tou správnou volbou: je vytvořena, použita a zničena v rámci jednoho volání a její záměr je zřejmý komukoli, kdo kód čte později. Alternativa v době návrhu je oprávněná v případě, že stejný formulář řídí nepřetržitý pracovní tok, jako je panel náhledu tisku, který znovu sestaví dokument, kdykoli uživatel změní nastavení; v takovém případě je udržování komponenty při životě a opakované volání BeginDoc/EndDoc méně rušivé než opakované alokování a uvolňování objektů na haldě
U dávkových úloh, které postupně produkují mnoho dokumentů, se vytvoření jedné instance THotPDF na každý dokument vyplatí i přes režii s alokací. Stav se mezi dokumenty nepřenáší, pokud neexistuje objekt, který by ho přenášel, a to je třída občasných chyb, které už nikdy nebudete muset ladit. Alokovat, generovat, smazat, opakovat
Jednou z vlastností, která se objevuje v několika ukázkách HotPDF, je AutoLaunch, která ihned po dokončení EndDoc otevře vygenerovaný soubor v systémovém prohlížeči PDF. To je užitečné při psaní prvního návrhu rozložení. V produkčním prostředí ji přeskočte: otevřete výstupní cestu explicitně, ověřte, že soubor existuje a má nenulovou velikost, zaznamenejte výsledek a nechte volající proces rozhodnout, zda je prohlížeč relevantní. U dávkových úloh spouští AutoLaunch jedno okno prohlížeče pro každý dokument a na některých systémech zablokuje proces čekáním na zavření prohlížeče
Komponenta THotPDF a všechna zde uvedená volání vykreslování jsou součástí HotPDF Component pro Delphi a C++Builder