Tehnički članak

Ponovna upotreba (reuse) THotPDF instance kroz više dokumenata u Delphiju

Greška glasi Please load the document before using BeginDoc, i gotovo se uvijek pojavi u drugom pokušaju (the second time around). Prvi se dokument zapisuje u redu. Zatim se od te iste THotPDF instance traži da započne s drugim dokumentom, podiže (raises) se BeginDoc greška, a poruka ukazuje na učitavanje dokumenta, što je zapravo u suprotnosti s onim što kod pokušava učiniti. Nesklad (mismatch) između simptoma i poruke jest ono zbog čega ova poruka greške ostaje u sjećanju. Pravi je predmet ovdje životni ciklus komponente, i jednom kad to shvatite, pogreška prestaje biti misteriozna

THotPDF životni ciklus dokumenta koji prikazuje Create, BeginDoc, EndDoc i Free po izlaznoj datoteci
Jedna THotPDF instanca odgovara jednom dokumentu: Create, BeginDoc, crtanje (draw), EndDoc, Free

Jedna THotPDF instanca je jedan dokument, a ne tvornica dokumenata (document factory)

Primamljiv mentalni model je taj da je THotPDF servisni objekt (service object) kojeg jednom pokrenete (spin up) i potom u njega prosljeđujete (feed) dokumente, na isti način na koji biste držali vezu s bazom podataka otvorenom i kroz nju provlačili upit (query) za upitom. Ali on to nije. Instanca modelira jedan pojedinačni dokument u izgradnji, a njezin unutarnji automat stanja (state machine) nosi pretpostavku da taj put prelazi samo jednom: od praznog, kroz otvoreni dokument, pa sve do spremljene datoteke. BeginDoc otvara taj put te označava da je instanca dokument u procesu. EndDoc sve serijalizira u FileName te je time zatvara (closes it out). Ponovnim pozivanjem BeginDoc-a na istu gotovu instancu, zahtijeva se od nje da ponovno uđe u stanje iz kojeg nije izašla kako treba, a zaštita (guard) koja se tada pokreće jest ona čija poruka slučajno spominje učitavanje (loading), jer se interno stanja "spremno za početak" (ready to begin) i "ima učitani dokument" (has a loaded document) provjeravaju zajedno

Zato je poruka zavaravajuća (misleading), ali zaštita (guard) radi svoj posao. Odbija vam dozvoliti započinjanje s novim (fresh) dokumentom preko komponente koja još uvijek smatra da se nalazi usred izrade dokumenta. Rješenje (fix) nije zaobići (defeat) zaštitu, već u tome da se prestane s ponovnim korištenjem (reusing) iskorištene (spent) instance

Životni ciklus (lifecycle), točno onim redom kako se mora odvijati

Svaki dokument koji HotPDF kreira od nule prati ista četiri koraka (beats), a redoslijed ne podliježe promjenama. Create vrši alociranje komponente. BeginDoc otvara dokument i fiksira strukturne izbore, tako da se sve što utječe na cijelu datoteku (veličina stranice, kompresija, enkripcija, naziv izlazne datoteke) mora postaviti između funkcija Create i BeginDoc. Potom crtate (draw). Zatim EndDoc zapisuje bajtove na disk. Free otpušta instancu (releases the instance). Pozivi za crtanje (drawing calls) stavljeni prije poziva BeginDoc nemaju stranicu na kojoj bi se prikazali; postavke na razini cijelog dokumenta dodijeljene nakon ovoga se ignoriraju bez upozorenja (without complaint)

var
  Pdf: THotPDF;
begin
  Pdf := THotPDF.Create(nil);
  try
    Pdf.FileName := 'invoice.pdf';
    Pdf.BeginDoc;                        // otvara dokument
    Pdf.CurrentPage.SetFont('Arial', [], 11);
    Pdf.CurrentPage.TextOut(50, 760, 0, 'Invoice 2026-042');
    Pdf.EndDoc;                          // piše invoice.pdf, zatvara ga
  finally
    Pdf.Free;                            // jedna instanca, jedan dokument
  end;
end;

Shvatite to kao radnu jedinicu (unit of work). Jedan Create, jedan BeginDoc, jedan EndDoc, jedan Free, i jedna datoteka na disku. Onog trena kada poželite drugu datoteku, započinjete novu radnu jedinicu, što podrazumijeva stvaranje nove instance

Što bi "ponovna upotreba" (reuse) trebala značiti: po jedna nova (fresh) instanca za svaku datoteku

Verzija pri kojoj dolazi do prekida (version that breaks) nastoji biti štedljiva kod alokacije (frugal with allocation): instanciraj komponentu (build the component) jednom, vrti kroz masovnu obradu (loop over a batch), pa onda pozivaj BeginDoc i EndDoc unutar petlje (inside the loop). Druga iteracija (second iteration) diže grešku (throws). Verzija koja radi pravilno tretira svaki izlaz (output) kao zaseban objekt kratkog vijeka, a trošak (cost) alokacije stvaranja komponente beznačajan je (trivial) u usporedbi s trudom oblikovanja i serijalizacije PDF-a, tako da ne postoji razlog za zadržavanje (hoarding) instance

procedure WriteBatch(const Names: TArray<string>);
var
  I: Integer;
  Pdf: THotPDF;
begin
  for I := 0 to High(Names) do
  begin
    Pdf := THotPDF.Create(nil);         // nova instanca u svakom prolazu
    try
      Pdf.FileName := Names[I] + '.pdf';
      Pdf.BeginDoc;
      Pdf.CurrentPage.SetFont('Arial', [], 12);
      Pdf.CurrentPage.TextOut(50, 760, 0, 'Statement for ' + Names[I]);
      Pdf.EndDoc;
    finally
      Pdf.Free;
    end;
  end;
end;

Ono što valja zagovarati na kodu jest onaj dio koji obuhvaća try/finally konstrukciju unutar same petlje. Ukoliko BeginDoc funkcija ili bilo koji drugi poziv za crtanje dovede do upozorenja, odnosno do takozvanog podizanja, i to usred procesiranja jednog jedinog dokumenta, instanca koju upotrebljava određena iteracija i dalje će biti uredno i uspješno otpuštena (freed) prije negoli se pokrene prva naredna iteracija, što nadalje povlači da eventualni nevaljali unos (bad record) zapravo ne dovodi do problema s napola stvorenom (half-built) komponentom, odnosno do blokade i narušavanja obrade čitavog ostatka navedenog zadatka (rest of the run). Izbacite (pull) Create da bi se nalazio poviše (above) same petlje zbog navodne optimizacije ("optimize") koda i eto vas nanovo (back) kod onog istog izvornog programskog propusta (bug), s tim da on sad u novim uvjetima preuzima i nosi obličje takozvane serijske petlje (batch loop)

Modificiranje postojeće datoteke različita je točka ulaska (entry point)

Postoji i drugo razumijevanje "ponovne upotrebe" (reuse) koje je sasvim legitimno: ne želite prazan dokument, nego želite otvoriti postojeći PDF i unijeti izmjene. Taj put (path) se uopće ne odvija kroz BeginDoc, što je upravo razlog zašto poruka o pogrešci navodi učitavanje (loading). Učitajte datoteku, uredite je i spremite pod bilo kojim imenom koje odaberete

var
  Pdf: THotPDF;
  PageCount: Integer;
begin
  Pdf := THotPDF.Create(nil);
  try
    PageCount := Pdf.LoadFromFile('contract.pdf');
    if PageCount > 0 then
    begin
      Pdf.CurrentPage.SetFont('Arial', [fsBold], 10);
      Pdf.CurrentPage.TextOut(40, 30, 0, 'REVIEWED');
      Pdf.SaveLoadedDocument('contract-reviewed.pdf');
    end;
  finally
    Pdf.Free;
  end;
end;

Funkcija LoadFromFile vraća broj stranica, a vrijednost od nula ili manja znači da učitavanje (load) nije uspjelo, tako da je to preporučljivo provjeriti prije nego što dirnete CurrentPage. Uparivanje je bitno: dokument koji ste otvorili pomoću funkcije LoadFromFile sprema se upotrebom SaveLoadedDocument, a ne BeginDoc/EndDoc para (pair), koji pripada dokumentima koje iz temelja izrađujete sami. Miješanje ta dva (mixing the two) je najčešći način za zbunjivanje istog onog automata stanja (state machine) koji je i prouzročio prvotnu grešku. Držite ova dva procesa mentalno razdvojena: funkcija BeginDoc ... EndDoc stvara (creates), funkcija LoadFromFile ... SaveLoadedDocument uređuje (edits)

Problem zaključavanja datoteke (file-lock) je stvaran, a odgovor nije gašenje prozora (viewer windows)

Problem (error) s ponovnom upotrebom obično se pojavljuje sa sporednim simptomom, a oni se nerijetko međusobno izmiješaju zato što znaju isplivati na površinu iz istog tijeka operacija (workflow) oko ponovnog stvaranja te iste datoteke. Korisnik se koristi onim otvorenim PDF-om koji ste tek bili dobili kao izlazni zapis, i taj PDF biva netom poslije toga (subsequently) držan kao u cijelosti prikazan (leaves it open) u unutar (in) kakvom (Acrobat-u) čitaču, na primjer programu Acrobat ili na isti način rabljenom programu Foxit, te upravo u takvom (in such a state) trenutku navedeni isti taj takav pojedini (a particular) sudionik-izvor u istom navedenom programu pokrene izvršavanje obnavljanja pri čemu onda dalje istodobno zahtijeva odnosno daje uvjetovanje ponovnog građenja (triggers a rebuild). Metoda EndDoc se u spomenutoj prilici silno trudi provesti svoj zadaćeni (allocated) zadatak na način da obavi upis u onu potpuno i sasvim (exact same) jednu te istu odredišnu datoteku (path), pri čemu dolazi do slučaja odbijanja (refuses) koje provodi u funkciju aktivirani u podlozi onaj odgovarajući (the appropriate) aktivni operacijski sustav s obrazloženjem i to pod objašnjenjem čitač sam odnosno (namely) njegova tekuća nit rada drži datoteku preuzetom putem isključivosti dozvoljenja koja istu rezervira na način čitanja (read share), čime zapravo izravno dolazi do zaustavljanja izvršenja i to (blocks) pisaca usmjerenih (destined) u taj file uslijed čega (as a result) onda kao konkretnu povratnu (feedback) poruku za daljnju provjeru sustava kao i obavijest samom korisniku od iste te komponente dobijete obavijest odnosno upozorenje koja obznanjuje i priopćuje neuspješnost u djelovanju (failure) te pritom jasno sugerira problem oko dozvole pristupa samom odredišnom datotečnom direktoriju uz navođenje uzroka zbog i uslijed onemogućenosti pristupanja takozvanu (denied) istodobnu istom sadržaju koja u pravilu ima isključivu oznaku odnosno prirodu (nature) poznatijeg (well-known) događaja vezanog isključivo u domeni pod (under) nedostatkom dozvoljenja uslijed aktivirane zabrane s onim dobro poznatim (access-denied). Ovo spomenuto na kraju u svojoj naravi, iz razloga koji proizlaze iz arhitekture (architecture) samog navedenog operacijskog okruženja doista u cjelini (genuinely) označava istinsku (real) odnosno pravu, izvornu grešku koja obznanjuje prisutnost problema zaključavanja vezanog (locking issue) na razini sustava Windows i ujedno se očituje uslijed korištenja (usage) te isto tako zauzimanja datoteke (file-locking) pri čemu nipošto (rather than) ne obuhvaća ili uopće ukazuje odnosno označava ikakvu situaciju, manu pa ni samo puko postojanje stanja onih kakvim se bave same greške koje inače prate komponente s obzirom na grešku kakva se u normalnom ciklusu veže i odnosi direktno te pripada istom obuhvatu na onoj unutrašnjoj razini komponente, pa samim tim ujedno za ovaj ispad i nadasve (genuinely) svakako treba zatražiti traženje i pritom zaustaviti potragu sve do otkrivanja (finding) stvarnoga a po prirodi valjanog potpunog rješenja (real answer) koje staje neposredno namjesto posezanja i priklanjanja kakvom običnom i neuvjerljivom (workaround) pristupu na način rješavanja okolnim putem odnosno samo prividnom ili lažnom prilaženju navedenom propustu i situaciji nastaloj uz ovu vrstu odvijanja odnosno izvođenja same (running the) obrade datoteke (file processing)

Jedno rješenje (workaround) koje obično kruži kao obavijest okolo - gdje se na način vršenja i to konkretno preko onog popisa koji nabraja te u obzir donosi aktivnost prebrojavanja prisutnih najviših i gornjih formi odnosno otvorenih programskih cjelina poznatih pod (top-level windows), a onda u nastavku djelovanja slijedom obrade prosljeđuje te odašilje onaj takozvani WM_CLOSE (WM_CLOSE) zahtjev odnosno poziv svim onim raznoraznim naoko povezanim aplikacijama pod kojima naslov sam (whose title) donekle asocira, ali prije svega u prvom redu nalikuje na onaj preglednik otvoren upravo sa samo tom (looks like a) usko određenom, onoj na format PDF vezanoj (PDF viewer) odnosno istaknutoj datoteci samoj takvoj - jest postupak uz i za kojeg se nažalost neosporno da ustvrditi (assert) s onom sigurnošću kako nedvojbeno on kao takav u svojoj osnovi upućuje i zapravo služi isključivo k predstavljanju prilično krivog na sam čin postavljenog nagona (wrong instinct). Takav navedeni proboj svojim posezanjem prodire u onaj okruženi sustav s namjerom vršenja aktivnosti posezanja točno i usko unutar njega te u cjelokupnom prostoru prelazi kroz samu domenu a s njome prolazi direktno kroz granice na razini procesa (process boundaries), ne bi li posljedično tom zaobilaženju s namjerom direktnog dovođenja do izmjene potom prisilno išao s izvođenjem isključenja i posljedično samim zatvaranjem svih odreda te uslijed navedenog otvorenih prozora kakve međutim (windows) pritom pak (yet) vaš vlastiti izvorni program zaista unatoč i povrh svega nikako niti (neither) po (does not own) pravilu a niti po navici ni u kojem slučaju apsolutno ni izbliza (not own) ne posjeduje iz kojeg dotičnog razloga usto usput (furthermore) za taj daljnji nastavak pogađa a u isto vrijeme procjenjuje status o svim pojedinim istim tim otvorenim prikazima za pregled i izvođenje tek letimice onako iz daljine procjenjujući pa se uzda samo te jedino prema onom sadržajnom ispisu iz pročitanoga odabranom prepoznatom tekstu uočenom na pregledu na dotičnom mu postavljenom onom samom naslovu, te uz tako spomenuti odabir bez pitanja, provjere s odobrenjem posvema (quietly) može odbaciti i potom zanemarivanjem bespovratno izbrisati čitavu izmjenu s unesenim ali nespremljenim (unsaved annotations) promjenama svakoga istog onoga gore već dotičnog samog prisutnog uključenog te usput s onim istim radom nazočnog a u konačnici uključenog te isto navedenog dotičnog odnosno onog prisutnog korisnika navedene funkcije onako doslovce bez zaprimanja prethodnog priupitkivanja (without asking). Cjelokupni na navedeni način upregnuti sustav prilaženja po ovom postupku valja i to svakako stoga s pravom uistinu svrstati a u nastavku i sagledavati jedino te naprosto gledati kroz prizmu onoga što poznajemo te isto usput identificiramo sa samim onim sumnjivim oblikom koji sam po sebi predstavlja te se (smell) tako odnosi pod tim (is akin to a smell). U potpunosti ispravni te povrh onoga sveg usput i nadasve (reliable) pravi (fix) onaj ispravak greške počiva odnosno temelji se upravo u pristupu po kojem dotični postupak iziskuje isključivo te stoji pri onome prema kojemu se (to never write) zapravo navodi pa posljedično tome i usmjerava naređeno te nikako pa niti ikad zapravo u takvom onom istom odnosno onom jednom obliku te na ovakav dotičan način pri obavljanju ove odvijane namjerne procedure valja s točnim postupanjem osigurati nepostojanje upisivanja prema izlazu uz na to onom pridodanom pa s njime priloženom točno onom odredištu (path) ukoliko a pogotovo u onoj istoj i situaciji kad eventualno pa bilo u onom ili onakvom istom slučaju jedan potpuno izdvojen pa onda sasvim jedan za to odabran a usput od navedenog sasvim neki dotični u potpunosti s tim drugi takav navedeni započeti u svom istom obavljanju zaposleni proces odnosno isti taj rad u svom aktivnom izvođenju možda sam i ne bi posljedično tome sve navedeno držao pod blokadom odnosno takvo držanje provodio u svojoj pozadinskoj provedbi blokiranja sa samim odvijanjem iste (holding). Serijalizirajte sadržaj točno pa sasvim s istim povodom s onim ciljem izvođenja posrednog usmjeravanja u kakvu namjenski točno na tome onom takozvanom jednom posvema (temporary file) onom odnosno točno tako istom onom onakvom izrijekom tako usmjerenom privremenom (a temporary) usput pridruženom i istom takvom tom datotečnom dokumentu točno pri onom izrijekom (same directory) navedenom takvom i s tim navedenom usklađenom pridruženom odgovarajućem zajedničkom direktoriju. Zatim je potom zamijenite na željenom mjestu s pomoću nečeg takvog što usput u potpunosti osigurava (atomic rename) da je provedeno takozvano u cijelosti točno osigurano preimenovanje odmah čim isključivo to isto s onim neposrednim pokretanjem osigura (once) točno onda dok onaj za tu radnju upućeni EndDoc izvrši onaj zadatak i to (succeeds) sa svim ovim spomenutim ostvari puni uspjeh pri obavljanju. U slučaju (If) ukoliko bi i tada takozvani čitač sadržaja uzastopno s tim upućivao istovremeno te sa svog aspekta prema onoj spomenutoj za isti pregled upotrebljenoj dotadašnjoj isključivo ranijoj s podređenom na tom s istom takozvano u biti s onom dotada njome otvorenom onako (has the old file open), samo tada isto navedeno preimenovanje zapravo ili uistinu na taj jedan (either) isključivo u onom pravom (cleanly) odnosno i upravo onom (succeeds) s uspjehom (cleanly) odobrenom smislu iznijelo te iznijelo ostvarenje za tu na onaj obavljeni navedeni uspješno a i na način čisto uspješni izvršeni čin postizanja s rezultatom (either succeeds cleanly), ili bi na temelju onoga (fails loudly) u ovakvom a u biti pak točno (loudly) odnosno do toga uslijed na takav glasan pa onda posljedično nimalo suptilni nadasve izravni način i to sasvim s tim onako podbacio (fails loudly). Tako po tom navedenom pristupu tada pred nos istom (you surface a clear message) te pred njega istodobno to jest direktno onda usput korisniku postavite odnosno stavljate do izražaja sasvim bistru te time samim po onom prenošenju takvu razgovijetnu, do navedenog do kraja po svom sadržaju posvema te nedvojbeno takvu isto i jasnu priloženu u cijelosti ispisanu priloženu takozvanu onu nedvosmislenu pa tako istu tu poruku (message) a sve s time na koncu u navedenom navodu radije tako i onako naprosto u tu namjenu izbjegavajući i nadasve nikako (rather than) nego da posljedično vodite postupak onog daljnjega daljnjeg onog sasvim nepotrebnog (fighting) takozvanog direktnog izravnog ulaženja pa onda onog besmislenog upuštanja u onaj postupak bespotrebne provedbe uslijed beskompromisnog upuštanja (fighting) pod ulaskom sa samom borbom uz onom uistinu nazočnom pa tako istodobno i stvarnom zaključanošću (lock)

uses
  System.SysUtils, System.IOUtils;

procedure WritePdfAtomically(const FinalPath: string);
var
  Pdf: THotPDF;
  TempPath: string;
begin
  // Privremena datoteka u ISTOM direktoriju kao cilj: preimenovanje unutar jednog
  // NTFS volumena mijenja ime atomski, dok premještanje preko volumena
  // degradira na kopiranje-pa-brisanje i gubi to jamstvo
  TempPath := TPath.Combine(TPath.GetDirectoryName(FinalPath),
    TGUID.NewGuid.ToString + '.pdf.tmp');
  try
    Pdf := THotPDF.Create(nil);
    try
      Pdf.FileName := TempPath;
      Pdf.BeginDoc;
      Pdf.CurrentPage.SetFont('Arial', [], 11);
      Pdf.CurrentPage.TextOut(50, 760, 0, 'Invoice 2026-042');
      Pdf.EndDoc;                    // ovdje je privremena datoteka potpuna na disku
    finally
      Pdf.Free;
    end;

    // Zamijeni na mjestu. TFile.Move odbija prepisivanje, pa prvo očisti
    // zastarjeli cilj; ako preglednik i dalje drži staru datoteku, brisanje je
    // ono što glasno pada, prije nego što se dobri bajtovi dotaknu
    if TFile.Exists(FinalPath) then
      TFile.Delete(FinalPath);
    TFile.Move(TempPath, FinalPath); // ili: RenameFile(TempPath, FinalPath)
  except
    if TFile.Exists(TempPath) then
      TFile.Delete(TempPath);        // nikad ne ostavi napola napisanu privremenu datoteku
    raise;
  end;
end;

Dvije poštene napomene uz taj kod. TFile.Move i klasični RenameFile oba se svode na isto Windows preimenovanje, koje je atomarno samo kada su izvor i odredište na istom volumenu, i upravo je zato privremena datoteka u odredišnom direktoriju, a ne u TPath.GetTempPath. Uz to, par obriši-pa-premjesti sam po sebi nije jedan atomaran korak: postoji kratak prozor u kojem ne postoji nijedna od te dvije datoteke. Za desktop aplikaciju koja iznova generira izvještaj taj je prozor nebitan; onima kojima na istom volumenu treba čvršći ugovor mogu izravno pozvati Win32 ReplaceFile ili MoveFileEx s MOVEFILE_REPLACE_EXISTING, čime se zamjena sažima u jedan poziv

Za poslužitelj s velikim prometom koji dokumente generira neprekidno, čišća je disciplina svaki izlaz zapisati pod jedinstvenim imenom (vremenska oznaka ili identifikator posla) tako da se dva izvođenja nikada ne natječu za istu putanju, a uklanjanje starih datoteka prepustiti zasebnoj politici zadržavanja. Obrazac se svodi na jednu liniju discipline imenovanja po zahtjevu

// Jedna izlazna putanja po zahtjevu: dva istovremena posla nikad se ne mogu
// natjecati za isto ime, pa nema plesa preimenovanja niti brave koju bi izgubili
OutName := Format('statement-%s-%s.pdf',
  [CustomerId, TGUID.NewGuid.ToString.Trim(['{', '}'])]);
Pdf.FileName := TPath.Combine(OutputDir, OutName);

Identifikator zahtjeva ili posla radi jednako dobro kao GUID kada vam ga okolni framework ionako daje, a uz to besplatno čini ime datoteke sljedivim natrag do retka u logu. U oba slučaja načelo je isto: projektirajte tako da datoteka koju pišete u trenutku pisanja pripada samo vama. Zaključavanje nestaje ne zato što ste nasilno zatvorili tuđi prozor, nego zato što nitko drugi ne dira te bajtove

Oblik ispravka pogreške (shape of the fix)

Svedite (strip) ova dva problema natrag na njihove početke i shvatit ćete da oba govore o poštivanju granica. Greška sa stanjem-automata traži od vas da poštujete granicu instance: jedan THotPDF, jedan dokument, onda to otpustite (let it go) i napravite drugi. Greška sa zaključavanjem datoteke traži da poštujete granice same datoteke: vršite ispis onamo gdje ni jedan drugi dio sustava ne vrši isčitavanje (where nothing else is reading), a tada rezultat premjestite (move) na njegovo predodređeno mjesto. Ni jedan od tih propusta ne zaziva popravak unutar biblioteke komponente te niti dodatno prilagođavanje (scripting) korisničkog sučelja na radnoj površini. Sve spomenuto naprosto biva izravno (fall out) ispunjeno kroz jednostavni odnos odnosno preko tretiranja (treating) svakog postojećeg novog navedenog pojedinog spomenutog po isto spomenutoj obradi (each document) prema na taj način pod onom te prema onoj te prvenstveno na temelju iste (as a self-contained unit of work), koja biva pri dotičnom novome početku pokrenuta pa od navedenoga iznova na posve takozvani taj friški pa novi iznovice (created fresh), zapisana uz pridavanje poštivanja svih obavezujućih onako odnosno propisanih zahtjeva uz pravila pisanja da to prođe uz takozvani onaj potpuno bez mane zapis s upisom zvan onako besprijekornim pa uz navedeno do tog (written cleanly), i oslobođena po na istom izvedenom od onom nadasve izrijekom pa po (released), što sve skupa tvori taj pod onim što to doista na koncu tvori navedeno onaj jedan (which is the same pattern) po navedenom pod uz navedenu pretpostavku i od njega onog navedenog isti a usto takav te u konačnici navedeni takav potpuno s potpunom sviješću do navedenog stvarni i zaista do tog i nadalje jedinstveni uzorak s onom navedenom pretpostavkom za primjenu uz usklađen onaj uzorak prirode s na navedeni u tu namjenu primijenjeni onaj oblik iz onog modela posve istog usvojenog (same pattern) i po njemu od kojeg i iz svega onaj isti navedeni onaj postupak radi (that makes the rest of the component predictable) u potpunosti posve predvidiv te pri onome onaj (predictable) onako a da zapravo on takav uz navedeno iz priloženog stoji odnosno te s druge strane postavlja a s time onako pred (makes) predvidljiv (predictable)

Metode BeginDoc, EndDoc, LoadFromFile i pozivi funkcija za dokument s metodom SaveLoadedDocument koji su ovdje prikazani sastavni su dio HotPDF Delphi komponente namijenjene za Delphi i C++Builder