Tehnički članak

HotPDF na Free Pascalu i Lazarusu: granice Win64 podrške

Kratki odgovor na taj zahtjev za podrškom glasi da, uz ograničenja. HotPDF 2.730.0 gradi se na Free Pascalu 3.2.2 i Lazarusu 4.6 za Win64, a osnovni putevi stvaranja, učitavanja i spremanja rade. Ono što ne slijedi je sve što počiva na statički povezanom objektu nativnog kodeka ili na Delphi anonimnim metodama

Pitanje obično stiže na isti način: tim se standardizira na Lazarusu za alat na više platformi, ili nasljeđuje Free Pascal bazu koda, i želi istu PDF komponentu koju već licencira za Delphi. Prenošenje zrele Delphi knjižnice rijetko je pitanje sintakse. Zanimljiv je dio što prijenos otkriva o mjestima gdje je knjižnica tiho bila vezana uz jedan alatni lanac, a u ovom je slučaju ta veza na dva vrlo konkretna mjesta: ABI objektnih datoteka priloženih kodeka i jezične mogućnosti skrivene iza verzijske oznake

Matrica mogućnosti koja uspoređuje Delphi izgradnju HotPDF-a s Free Pascal 3.2.2 i Lazarus 4.6 Win64 izgradnjom, pokazujući koji se putevi dokumenata dijele, dok se kodek, kompresija, paralelno renderiranje i API-ji anonimnih metoda završavaju na stubu koji podiže iznimku
Osnovni putevi stvaranja, učitavanja i spremanja identični su u obje izgradnje, a razlika leži u cijelosti u statički povezanim kodecima i API-jima anonimnih metoda

Što Free Pascal 3.2.2 treba prije nego što se HPDFDoc kompilira

HotPDF se pod Free Pascalom kompilira samo u Delphi načinu rada, i to samo kada su direktoriji jedinica Lazarus LCL-a na putu pretraživanja. Ništa od toga nije podložno pregovaranju. HotPDF.inc prebacuje kompilator s {$MODE DELPHI} i {$H+} unutar svog bloka {$IFDEF FPC} i odbija sve starije s {$FATAL} kada je FPC_FULLVERSION ispod 30202, pa instalacija 3.0.x glasno propada umjesto da proizvede neispravnu jedinicu. Runtime paket za Lazarus HotPDFLaz.lpk kodira ostalo: LCL kao obavezan paket i -Mdelphi kao prilagođenu opciju

Zahtjev za LCL iznenađuje one koji žele samo konzolni izlaz, ali on je strukturan. HPDFFPCCompat dostavlja Delphi VCL tipove za koje Free Pascal nema ekvivalent, preslikavajući TMetafile i TMetafileCanvas na LCL klase bitmapa i platna te aliasirajući TRichEdit na TMemo, dok HPDFDoc aliasira TPNGObject na Graphics.TPortableNetworkGraphic. Tretirajte to kao kompilacijske podloške, a ne kao paritet mogućnosti: metafile klasa koju nosi bitmapa održava jedinicu kompilabilnom, ali ne čini da se putevi metafilea ponašaju kako se ponašaju na Delphiju. Čak i negrafički smoke test povlači Interfaces, a skripta za gradnju predaje -Fu za lcl\units\x86_64-win64 i za izlazni direktorij lazutils

Zašto D2009+ ne može biti i granica verzije

Primamljivo je tretirati Free Pascal izgradnju kao moderni kompilator i jednostavno definirati najnoviji Delphi simbol mogućnosti. HotPDF to ne čini, a razlog vrijedi izreći jasno: D2009+ ne znači samo Unicode nizove, on je također kapija jedinicama čiji je javni API izražen anonimnim metodama. Free Pascal 3.2.2 ne podržava ni Delphi anonimne metode ni te API-je, pa bi posuđivanje simbola uvlaklo kod koji se ne može kompilirati. Uses klauzula HPDFDoc-a stoga nosi dva odvojena uvjetna repa, a njihovo preklapanje namjerno je, a ne slučajno

uses
  // ...
  HPDFJavaScript,
  HPDFFormCalcGraph
{$IFDEF FPC}
  , HPDFFPCCodecStubs,
  HPDFCMS,
  HPDFWinCertSigner
{$ENDIF}
{$IFDEF D2009+}
  , HPDFXFARuntime,
  HPDFCMS,
  HPDFWinCertSigner,
  HPDFSignVerify,
  HPDFSignatureBatch
{$ENDIF};

Zašto se nativni kodeci zaustavljaju na linkaru?

Jer su to Win64 COFF objekti koje je emitirao jedan konkretan alatni lanac, a nijedan Free Pascal linkar na Win64 neće ih konzumirati: ni interni linkar, ni vanjski put s GNU ld. To je ABI problem objektnih datoteka, a ne Pascal problem, i nijedna količina uvjetnog koda to ne rješava. Knjižnica bira jedini pošten dostupan put. Svaka direktiva {$L} koja uvlači statički objekt kodeka obavijena je s {$IFNDEF FPC}, pa Free Pascal izgradnja jednostavno izostavlja te objekte, a HPDFFPCCodecStubs zatim svaki nedostajući vanjski simbol dostavlja kao stub koji podiže iznimku umjesto da vrati

// HPDFFPCCodecStubs.pas
function HPDFFPCNativeCodecUnavailable: PtrUInt;
begin
  raise ENotSupportedException.Create(
    'This native codec is not available in the Free Pascal build');
end;

function HPDFFPCStub_deflate: PtrUInt; cdecl;
  public name 'deflate';
begin
  Result := HPDFFPCNativeCodecUnavailable;
end;

Ta je tablica stubova dugačka, a njezino čitanje točno vam kaže koje su mogućnosti danas samo za Delphi: ulazne točke deflate od zlib-ng i zopfli, kompresija i dekompresija libjpega, OpenJPEG JPEG 2000 kodek, libtiff s njegovim inicijalizatorima po kompresiji, JBIG2 kodiranje i dekodiranje, ulazne točke pretvorbi boja Little-CMS-a i AES primitivi. Dizajnerska odluka iza stubova važnija je od popisa. Nedostajući simbol u trenutku povezivanja donosi zid nedefiniranih referenci iz jedinice koju nikada niste dirnuli; stub koji podiže ENotSupportedException donosi izgradnju koja radi, poruku koja imenuje razlog i stack trace koji pokazuje na mjesto poziva. To također znači da Free Pascal izgradnja nikada tiho ne proizvodi pogrešne bajtove tamo gdje bi Delphi izgradnja proizvela ispravne. Uočite i učinak drugog reda: izvođenje nepouzdanih kodeka slika u izoliranom procesu odluka je koja se javlja samo na Delphi izgradnji, jer Free Pascal izgradnja nema in-process nativni dekoder koji bi se uopće mogao izolirati

Na Delphiju se statički objekti kodeka HotPDF-a povežu i izvode nativno, dok Free Pascal Win64 izgradnja izostavlja direktive povezivanja i svaki nedostajući vanjski simbol usmjerava na stub koji na mjestu poziva podiže imenovanu iznimku
Izostavljanje direktiva povezivanja i zamjena svakog vanjskog simbola stubom pretvara zid nedefiniranih referenci u izgradnju koja radi i koja sama imenuje svoja ograničenja

Kompresija: prvi red koji treba promijeniti je cmNone

Prije nego što prenesete bilo što drugo, postavite Compression na cmNone. THPDFCompressionMethod nudi točno dvije vrijednosti, cmNone i cmFlateDecode, a druga vodi ravno u ulazne točke deflate koje su u Free Pascal izgradnji stubovi. Prvo provjerite temeljni objektni model uz isključenu kompresiju, a zatim odlučite što još trebate. To je redoslijed koji koristi priloženi smoke test: stvorite jednostranični nekompresirani dokument, ponovno ga učitajte i provjerite da se broj stranica vratio kao jedan. Nekompresirani izlaz je veći, a i dalje je sasvim valjani PDF

program HotPDFLazarusSmoke;

{$mode delphi}
{$H+}

uses
  Interfaces, SysUtils, HPDFDoc;

var
  Pdf, Reloaded: THotPDF;
  OutputFile: string;
  PageCount: Integer;
begin
  OutputFile := IncludeTrailingPathDelimiter(GetTempDir) +
    'HotPDF-FPC-Smoke.pdf';
  Pdf := THotPDF.Create(nil);
  try
    Pdf.FileName := OutputFile;
    Pdf.Compression := cmNone;   // cmFlateDecode završava na stubiranom simbolu
    Pdf.BeginDoc;
    Pdf.CurrentPage.SetFont('Arial', [], 12);
    Pdf.CurrentPage.TextOut(72, 72, 0, 'HotPDF Free Pascal smoke test');
    Pdf.EndDoc;
  finally
    Pdf.Free;
  end;

  Reloaded := THotPDF.Create(nil);
  try
    PageCount := Reloaded.LoadFromFile(OutputFile);
    if PageCount <> 1 then
      raise Exception.CreateFmt('Expected one page, got %d', [PageCount]);
  finally
    Reloaded.Free;
  end;
end.

Što se događa s paralelnim renderiranjem stranica?

I dalje se kompilira, i dalje vraća ispravne bitmape, i prestaje biti paralelno. THotPDF.RenderLoadedPagesParallel i THotPDF.RenderLoadedPagesParallelOrdered građeni su na TThread.CreateAnonymousThread s inline procedure zatvaranjem koje Free Pascal 3.2.2 ne može izraziti, pa Free Pascal grana izvodi deterministički serijski fallback: prolazi indekse stranica redom, poziva RenderLoadedPageToBitmap za svaki i broji uspjehe. Oblik API-ja, povratna vrijednost i izlazno polje nepromijenjeni su, što omogućuje da jedna baza koda gradi u oba smjera

Isti poziv paralelnog renderiranja HotPDF-a izvodi se na preklapajućim radnim nitima pod Delphijem i prolazi indekse stranica serijski pod Free Pascalom, pri čemu zapis info cjevovoda izvještava broj radnika od jedan umjesto da sakrije fallback
Free Pascal grana zadržava oblik API-ja i izlazno polje dok izvještava broj radnika od jedan, pa kod koji već čita info zapis vidi istinu
var
  Bitmaps: THPDFBitmapArray;
  Info: THPDFParallelRenderPipelineInfo;
  Rendered: Integer;
begin
  Rendered := Pdf.RenderLoadedPagesParallel([0, 1, 2, 3], 150, 4,
    Bitmaps, Info);
  // Delphi: Info.WorkerCount je ono što je budžet memorije dopustio
  // Free Pascal: Info.WorkerCount je uvijek 1, stranice u redoslijedu indeksa
  if Info.WorkerCount = 1 then
    LogSerialFallback(Rendered, Info.RequestedWorkerCount);

Fallback nije tih, i to je dio vrijedan oblikovanja oko njega. THPDFParallelRenderPipelineInfo popunjava pošteno: PageCount iz zahtjeva, RequestedWorkerCount koji odjekuje ono što ste tražili, WorkerCount postavljen na 1, te brojače dovršenih i dostavljenih koji odgovaraju onome što je stvarno stiglo. Kod koji već pregledava Info da dimenzionira traku napretka ili budžet memorije i dalje radi i čita istinu umjesto pretpostavke. Ako vaš plan propusnosti ovisi o paralelnoj rendererskoj cjevovodnoj liniji i njezinu modelu povratnog tlaka, taj plan je Delphi plan; na Free Pascalu računajte trošak renderiranja stranice u bitmapu u jednoj niti pomnožen brojem stranica

Koju izgradnju biste stvarno isporučili?

Birajte prema mogućnostima, ne prema sklonostima. Ako je vaš tok rada sastavljanje dokumenata, crtanje teksta i vektora, popunjavanje obrazaca, učitavanje i spremanje, Free Pascal izgradnja na Win64 to pokriva, i trebali biste provjeriti uz isključenu kompresiju prije nego što išta uključite. Ako uključuje JPEG ili JPEG 2000 ili TIFF ili JBIG2 slike, ICC pretvorbe boja, kompresirani izlaz ili propusnost koja ovisi o mnogim jezgrama, za sada ostanite na Delphiju ili C++Builderu. Granicu crtaju ABI objektnih datoteka i nedostajuća jezična mogućnost, obje vidljive u izvornom kodu umjesto zakopane u matricu podrške, i obje propadaju s imenovanom pogreškom umjesto pogrešnim rezultatom

Free Pascal i Lazarus paket isporučuje se u istoj distribuciji kao Delphi i C++Builder jedinice, pa jedna licenca pokriva oboje i možete isprobati Lazarus put na svojim dokumentima prije nego što mu se obvežete; stranica proizvoda HotPDF Delphi PDF Component nosi trenutnu matricu podrške kompilatora i potpunu API referencu