Răspunsul scurt la tichetul de suport acela este da, cu limite. HotPDF 2.730.0 se construiește pe Free Pascal 3.2.2 și Lazarus 4.6 pentru Win64, iar căile de bază de creare, încărcare și salvare funcționează. Ce nu urmează este orice se sprijină pe un obiect codec nativ legat static sau pe metode anonime Delphi
Întrebarea sosește de obicei la fel: o echipă standardizează pe Lazarus pentru un instrument multiplatform, sau moștenește o bază de cod Free Pascal, și vrea aceeași componentă PDF pe care o licențiază deja pentru Delphi. Portarea unei biblioteci Delphi mature rareori este o chestiune de sintaxă. Partea interesantă este ce expune portarea despre locurile în care biblioteca era cuplată în tăcere la un singur toolchain, iar în acest caz cuplarea stă în două locuri foarte precise: ABI-ul de fișier obiect al codecurilor incluse și funcțiile compilatorului ascunse în spatele unui simbol de versiune
Ce îi trebuie lui Free Pascal 3.2.2 până să compileze HPDFDoc
HotPDF compilează sub Free Pascal doar în modul Delphi, și doar când directoarele de unități LCL Lazarus sunt pe calea de căutare. Niciunul nu este negociabil. HotPDF.inc comută compilatorul cu {$MODE DELPHI} și {$H+} în interiorul blocului său {$IFDEF FPC} și refuză orice mai vechi cu un {$FATAL} când FPC_FULLVERSION este sub 30202, astfel încât o instalare 3.0.x eșuează zgomotos în loc să producă o unitate stricată. Pachetul de runtime Lazarus HotPDFLaz.lpk codifică restul: LCL ca pachet necesar și -Mdelphi ca opțiune personalizată
Cerința LCL îi surprinde pe cei care vor doar ieșire în consolă, dar este structurală. HPDFFPCCompat furnizează tipurile VCL Delphi pentru care Free Pascal nu are echivalent, mapând TMetafile și TMetafileCanvas pe clasele LCL de bitmap și canvas și aliasând TRichEdit la TMemo, în timp ce HPDFDoc aliasiază TPNGObject la Graphics.TPortableNetworkGraphic. Tratați-le ca shims de timp de compilare, nu ca paritate de funcționalități: o clasă metafile susținută de un bitmap ține unitatea compilând, nu face căile metafile să se comporte cum se comportă pe Delphi. Chiar și smoke testul non-GUI aduce Interfaces, iar scriptul de build trece -Fu pentru lcl\units\x86_64-win64 și directorul de ieșire lazutils
De ce D2009+ nu poate servi și ca poartă de versiune
E tentant să tratați build-ul Free Pascal ca un compilator modern și să definiți pur și simplu cel mai nou simbol de funcționalitate Delphi. HotPDF nu face asta, iar motivul merită enunțat limpede: D2009+ nu înseamnă doar șiruri Unicode, ci poartă și unitățile a căror API public este exprimat cu metode anonime. Free Pascal 3.2.2 nu suportă nici metodele anonime Delphi, nici acele API-uri, deci împrumutarea simbolului ar trage în cod care nu poate compila. Clauza uses a lui HPDFDoc poartă de aceea două cozi condiționale separate, iar suprapunerea dintre ele este deliberată, nu accidentală
uses
// ...
HPDFJavaScript,
HPDFFormCalcGraph
{$IFDEF FPC}
, HPDFFPCCodecStubs,
HPDFCMS,
HPDFWinCertSigner
{$ENDIF}
{$IFDEF D2009+}
, HPDFXFARuntime,
HPDFCMS,
HPDFWinCertSigner,
HPDFSignVerify,
HPDFSignatureBatch
{$ENDIF};
De ce se opresc codecurile native la linker?
Pentru că sunt obiecte Win64 COFF emise de un anumit toolchain, și niciunul dintre linker-ele Free Pascal pe Win64 nu le va consuma: nici linkerul intern, nici calea externă GNU ld. Aceasta este o problemă de ABI de fișier obiect, nu o problemă Pascal, și nicio cantitate de sursă condițională nu o repară. Biblioteca ia singura cale onestă disponibilă. Fiecare directivă {$L} care trage un obiect codec static este învelită în {$IFNDEF FPC}, astfel încât build-ul Free Pascal pur și simplu le omite, iar HPDFFPCCodecStubs furnizează apoi fiecare simbol extern lipsă ca un stub care ridică excepție în loc să returneze
// 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;
Tabelul acela de stuburi este lung, iar citirea lui vă spune exact care capabilități sunt doar-Delphi astăzi: punctele de intrare deflate zlib-ng și zopfli, compresia și decompresia libjpeg, codecul OpenJPEG JPEG 2000, libtiff cu inițializatoarele sale per comprimare, codificarea și decodarea JBIG2, punctele de intrare de transformare de culori Little-CMS și primitivele AES. Alegerea de design din spatele stuburilor contează mai mult decât lista. Un simbol lipsă la timpul de legare vă dă un zid de referințe nedefinite dintr-o unitate pe care nu ați atins-o niciodată; un stub care ridică ENotSupportedException vă dă un build care rulează, un mesaj care numește motivul și o urmă de stivă îndreptată spre locul apelului. Înseamnă și că un build Free Pascal nu produce niciodată în tăcere octeți greșiți acolo unde un build Delphi ar produce octeți corecți. Notați și efectul de ordinul doi: rularea codecurilor de imagini de neîncredere într-un proces izolat este o decizie care apare doar pe build-ul Delphi, pentru că un build Free Pascal nu are deloc un decodor nativ în proces de izolat
Comprimare: prima linie de schimbat este cmNone
Înainte să portați orice altceva, setați Compression la cmNone. THPDFCompressionMethod oferă exact două valori, cmNone și cmFlateDecode, iar a doua merge drept în punctele de intrare deflate care sunt stuburi într-un build Free Pascal. Verificați mai întâi modelul de obiect de bază cu comprimarea oprită, apoi decideți ce mai aveți nevoie. Aceasta este ordinea pe care o folosește smoke testul livrat: creați un document necomprimat de o pagină, reîncărcați-l și verificați că numărul de pagini s-a întors ca unu. Ieșirea necomprimată este mai mare, și este tot un PDF perfect valid
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 ajunge la un simbol stubuit
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.
Ce se întâmplă cu randarea paralelă de pagini?
Încă compilează, încă returnează bitmapuri corecte și nu mai este paralel. THotPDF.RenderLoadedPagesParallel și THotPDF.RenderLoadedPagesParallelOrdered sunt construite pe TThread.CreateAnonymousThread cu o închidere procedure inline, pe care Free Pascal 3.2.2 nu o poate exprima, astfel încât ramura Free Pascal rulează un fallback serial determinist: parcurge indicii de pagini în ordine, apelează RenderLoadedPageToBitmap pentru fiecare și numără succesele. Forma API, valoarea returnată și tabloul de ieșire sunt neschimbate, ceea ce permite unei singure baze de cod să construiască în ambele feluri
var
Bitmaps: THPDFBitmapArray;
Info: THPDFParallelRenderPipelineInfo;
Rendered: Integer;
begin
Rendered := Pdf.RenderLoadedPagesParallel([0, 1, 2, 3], 150, 4,
Bitmaps, Info);
// Delphi: Info.WorkerCount este cât a permis bugetul de memorie
// Free Pascal: Info.WorkerCount este întotdeauna 1, pagini în ordinea indicilor
if Info.WorkerCount = 1 then
LogSerialFallback(Rendered, Info.RequestedWorkerCount);
Fallback-ul nu este tăcut, și tocmai asta merită proiectat în jur. Umple THPDFParallelRenderPipelineInfo onest: PageCount din cerere, RequestedWorkerCount făcând ecou la ce ați cerut, WorkerCount setat la 1, iar contoarele de finalizat și livrat corespunzând a ceea ce a venit efectiv înapoi. Codul care inspectează deja Info pentru a dimensiona o bară de progres sau un buget de memorie continuă să funcționeze și citește adevărul, nu o presupunere. Dacă planul dumneavoastră de debit depinde de pipeline-ul de randare paralelă și modelul său de contrapresiune, acel plan este un plan Delphi; pe Free Pascal, bugetați costul single-threaded al randării unei pagini într-un bitmap înmulțit cu numărul de pagini
Ce build ar trebui să livrați de fapt?
Alegeți după capabilități, nu după preferință. Dacă fluxul vostru de lucru este asamblare de documente, desen text și vector, populare de formulare, încărcare și salvare, build-ul Free Pascal pe Win64 o acoperă, și ar trebui să validați cu comprimarea oprită înainte să activați ceva. Dacă implică imagini JPEG sau JPEG 2000 sau TIFF sau JBIG2, transformări de culori ICC, ieșire comprimată sau debit care depinde de multe nuclee, rămâneți pentru moment pe Delphi sau C++Builder. Granița este trasată de un ABI de fișier obiect și o funcționalitate de limbaj lipsă, ambele vizibile în sursă în loc să fie îngropate într-o matrice de suport, și ambele eșuează cu o eroare numită, nu cu un rezultat greșit
Pachetul Free Pascal și Lazarus vine în aceeași distribuție ca unitățile Delphi și C++Builder, astfel încât o licență acoperă ambele și puteți testa calea Lazarus pe documentele voastre înainte să vă angajați la ea; pagina de produs HotPDF Delphi PDF Component poartă matricea actuală de suport de compilatoare și referința completă a API-ului