Technical Article

Dinaminis HotPDF komponento sukūrimas ir atlaisvinimas C++Builder aplinkoje

THotPDF komponento užmetimas ant formos projektavimo metu (design time) tinka greitam prototipų kūrimui, tačiau tai susieja komponento gyvavimo ciklą su pačia forma, o gamybiniame kode to retai kada norima. Ataskaitų generatorius, paleidžiamas vienu mygtuko paspaudimu, paslaugos gija (service thread), atliekanti naktinį eksportavimą partijomis, arba pagalbinė klasė, kuri išvis netri formos: kiekvienoje iš šių situacijų norite, kad komponentas egzistuotų tiksliai vienos PDF užduoties atlikimo laikotarpiu ir po to išnyktų. Tai reiškia objekto išskyrimą vykdymo metu (runtime allocation), o tai pakeičia du dalykus, kuriuos verta suprasti prieš rašant pirmąją kodo eilutę: kas valdo objektą ir kaip atliekamas išvalymas įvykus klaidai.

Savininko (owner) semantika VCL bibliotekoje

Kiekvieno VCL komponento konstruktorius priima Owner parametrą, kurio tipas yra TComponent*. Perduodant this (formą), naujas objektas užregistruojamas formos valdomų komponentų sąraše, todėl jei forma sunaikinama komponentui tebesant gyvam, VCL jį atlaisvina automatiškai. Perduodant nullptr, savininko nėra: prisiimate visą atsakomybę už rodyklę, ir niekas jos už jus neišvalys, jei išimtis atlaisvins steką prieš jūsų aiškiai nurodytą delete.

Vienkartiniam eksportui, kuris užbaigiamas vienoje funkcijoje, tinka abu variantai, tačiau jie turi skirtingus elgsenos scenarijus klaidos atveju. Naudojant this kaip savininką, atminties nutekėjimas neįmanomas, kol forma galiausiai užsidaro; naudojant nullptr, rodyklė turi pasiekti __finally bloką. Praktikoje nullptr ir __finally šablonas yra šiek tiek švaresnis trumpalaikiams objektams, nes jis iškart parodo gyvavimo ciklo ribas ir neleidžia formoje kauptis valdomiems objektams, kurie buvo suplanuoti kaip laikinai naudojami.

Išimtims saugi struktūra

PDF generavimas gali nepavykti dėl priežasčių, nesusijusių su pačia API: išvesties katalogas yra skirtas tik skaitymui, trūksta šrifto failo, srautas išvalomas per anksti arba iškvietėjo pateikti duomenys viršija ilgio ribą. Kad ir kokia būtų priežastis, išvalymo kelias privalo suveikti. Įprastas „C++Builder“ būdas tai garantuoti yra try/__finally konstrukcija:

#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;
    }
}

Verta atkreipti dėmesį į kelis dalykus šiame kode. Savininkas yra nullptr, todėl objekto gyvavimo ciklas yra aiškus. Compression ir FontEmbedding nustatomi prieš BeginDoc: abi parinktys yra dokumento lygio nustatymai, kuriuos „HotPDF“ pritaiko atidarant dokumentą, o jų keitimas vėliau neturės jokios įtakos. TextOut priima koordinates taškais (points), matuojant nuo apatinio kairiojo puslapio kampo, teigiamai Y ašiai einant į viršų; 72 ir 720 koordinačių pora talpina tekstą netoli laiško (letter) dydžio puslapio viršutinio kairiojo kampo su vieno colio kairiąja parašte. Kodas delete Pdf bloke __finally bus paleistas nepriklausomai nuo to, ar BeginDoc, braižymas, ar EndDoc išmetė klaidą.

Venkite bet kokių Pdf metodų iškvietimų po to, kai atlikote delete. Jei rodyklė yra saugoma klasės narių kintamajame, iškart po ištrynimo nustatykite ją į nullptr, kad bet kokia atsitiktinė vėlesnė prieiga sukeltų aiškų programos lūžimą, o ne tylų atminties sugadinimą.

Projekto konfigūravimas

„C++Builder“ randa THotPDF pagal įtraukimo (include) ir bibliotekų (library) kelius bei pragma direktyvą. Sukurta antraštė yra šalia HPDFDoc.pas failo „HotPDF“ pirminio kodo kataloge; pridėkite šį katalogą prie Project > Options > C++ Compiler > Include path. Direktyva #pragma link "HPDFDoc" nurodo saistytojui (linker) įtraukti sukompiliuotą modulį rankiniu būdu neįtraukiant jo į projekto failą. Jei vietoj statinio susiejimo naudojate vykdymo laiko paketą (runtime package), pirmiausia įdiekite „HotPDF“ projektavimo ir vykdymo laiko paketus; pragma direktyva vis tiek išlieka taikoma.

Palikite modulio pavadinimą HPDFDoc nepakeistą. „C++Builder“ išveda antraštės failo pavadinimą iš „Pascal“ modulio pavadinimo, todėl failo pervadinimas arba kelio pseudonimų naudojimas pragma direktyvoje tyliai sugadins paiešką.

Matomumo sritis ir kelių dokumentų užduotys

Vienkartiniam eksportui, kurį inicijuoja vartotojo veiksmas, geriausias sprendimas yra vietinis kintamasis mygtuko paspaudimo įvykio rėmuose: jis sukuriamas, panaudojamas ir sunaikinamas vieno iškvietimo metu, o ketinimas yra visiškai aiškus visiems, kas vėliau skaitys kodą. Projektavimo laiko alternatyva yra pagrįsta tada, kai ta pati forma atlieka nuolatinį darbą, pavyzdžiui, spaudinio peržiūros skydelyje, kuris perkuria dokumentą kaskart vartotojui pakeitus nustatymą; tokiu atveju palaikyti komponentą gyvą ir pakartotinai kviesti BeginDoc/EndDoc yra efektyviau nei nuolat išskirti ir atlaisvinti objektus atminties krūvoje.

Vykdant paketus, kurie sukuria daug dokumentų iš eilės, verta naudoti po vieną THotPDF egzempliorių kiekvienam dokumentui, nepaisant išskyrimo sąnaudų. Būsena nebus perkeliama tarp dokumentų, jei nebus paties objekto jai saugoti, o tai leidžia visiškai išvengti periodiškai pasirodančių klaidų, kurias būtų sunku derinti. Išskirkite atmintį, sugeneruokite, ištrinkite ir pakartokite.

Viena iš savybių, rodomų keliuose „HotPDF“ demonstraciniuose pavyzdžiuose, yra AutoLaunch, kuri atidaro sugeneruotą failą sistemos PDF peržiūros programoje iškart po EndDoc. Tai naudinga maketo derinimo metu. Gamybiniame kode to venkite: atidarykite išvesties kelią patys, patikrinkite, ar failas egzistuoja ir jo dydis nėra nulinis, užregistruokite rezultatą žurnale ir leiskite iškvietimo procesui nuspręsti, ar peržiūros programa išvis reikalinga. Vykdant paketines užduotis, AutoLaunch atidarys po vieną peržiūros programos langą kiekvienam dokumentui ir kai kuriose sistemose užblokuos procesą, kol peržiūros programa bus uždaryta.

Šiame straipsnyje parodytas THotPDF komponentas ir visi braižymo iškvietimai yra dalis HotPDF Component rinkinio, skirto „Delphi“ ir „C++Builder“.