Det korta svaret på det supportärendet är ja, med gränser. HotPDF 2.730.0 bygger på Free Pascal 3.2.2 och Lazarus 4.6 för Win64, och kärnvägarna för att skapa, läsa in och spara fungerar. Det som inte följer med är allt som vilar på ett statiskt länkat nativt codec-objekt eller på Delphis anonyma metoder
Frågan kommer nästan alltid på samma sätt: ett team standardiserar på Lazarus för ett plattformsoberoende verktyg, eller ärver en Free Pascal-kodbas, och vill ha samma PDF-komponent som de redan licensierar för Delphi. Att portera ett moget Delphi-bibliotek är sällan en fråga om syntax. Det intressanta är vad porteringen blottlägger om var biblioteket i tysthet var kopplat till en enda verktygskedja, och i det här fallet sitter kopplingen på två mycket bestämda ställen: objektfilens ABI hos de medföljande codecerna, och kompilatorfunktionerna som gömmer sig bakom en versionssymbol
Vad Free Pascal 3.2.2 behöver innan HPDFDoc kompilerar
HotPDF kompilerar under Free Pascal bara i Delphi-läge, och bara när Lazarus LCL-enhetskataloger ligger på sökvägen. Inget av det är förhandlingsbart. HotPDF.inc ställer om kompilatorn med {$MODE DELPHI} och {$H+} inne i sitt {$IFDEF FPC}-block och vägrar allt äldre med ett {$FATAL} när FPC_FULLVERSION ligger under 30202, så en 3.0.x-installation havererar högljutt i stället för att producera en trasig enhet. Lazarus runtime-paket HotPDFLaz.lpk kodar in resten: LCL som ett obligatoriskt paket och -Mdelphi som ett eget kompilatoralternativ
LCL-kravet överraskar dem som bara vill ha konsolutdata, men det är strukturellt. HPDFFPCCompat levererar de Delphi-VCL-typer som Free Pascal saknar motsvarighet till: den mappar TMetafile och TMetafileCanvas på LCL:s bitmap- och canvasklasser och aliasar TRichEdit till TMemo, medan HPDFDoc aliasar TPNGObject till Graphics.TPortableNetworkGraphic. Behandla dem som shims för kompileringstid, inte som funktionsparitet: en metafilklass som backas av en bitmapp håller enheten kompilerbar, den får inte metafilvägarna att bete sig som de gör på Delphi. Till och med smoke-testet utan GUI drar in Interfaces, och byggskriptet skickar -Fu för lcl\units\x86_64-win64 och för utdatakatalogen för lazutils
Varför D2009+ inte kan tjäna som versionsgrind
Det är frestande att behandla Free Pascal-bygget som en modern kompilator och helt enkelt definiera den nyaste Delphi-funktionssymbolen. HotPDF gör inte det, och skälet är värt att säga rent ut: D2009+ betyder inte bara Unicode-strängar, det grindar också enheter vars publika API uttrycks med anonyma metoder. Free Pascal 3.2.2 stödjer varken Delphis anonyma metoder eller de API:erna, så att låna symbolen skulle dra in kod som inte går att kompilera. Uses-satsen i HPDFDoc bär därför två separata villkorliga svansar, och överlappningen mellan dem är avsiktlig snarare än en olyckshändelse
uses
// ...
HPDFJavaScript,
HPDFFormCalcGraph
{$IFDEF FPC}
, HPDFFPCCodecStubs,
HPDFCMS,
HPDFWinCertSigner
{$ENDIF}
{$IFDEF D2009+}
, HPDFXFARuntime,
HPDFCMS,
HPDFWinCertSigner,
HPDFSignVerify,
HPDFSignatureBatch
{$ENDIF};
Varför stannar de nativa codecerna hos länkaren?
Därför att de är Win64-COFF-objekt som emitterats av en enda bestämd verktygskedja, och ingen av Free Pascals länkare på Win64 vill svälja dem: varken den interna länkaren eller vägen via extern GNU ld. Det är ett ABI-problem på objektfilsnivå, inte ett Pascal-problem, och ingen mängd villkorlig källkod löser det. Biblioteket väljer den enda ärliga väg som står till buds. Varje {$L}-direktiv som drar in ett statiskt codec-objekt är inlindat i {$IFNDEF FPC}, så Free Pascal-bygget utelämnar dem helt enkelt, och HPDFFPCCodecStubs levererar sedan varje saknad extern symbol som en stub som kastar i stället för att returnera
// 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;
Den stubbtabellen är lång, och att läsa den talar om exakt vilka funktioner som är Delphi-exklusiva i dag: ingångspunkterna för deflate i zlib-ng och zopfli, komprimering och dekomprimering med libjpeg, JPEG 2000-codecen i OpenJPEG, libtiff med sina initierare per komprimeringsmetod, kodning och avkodning av JBIG2, ingångspunkterna för färgtransformer i Little-CMS samt AES-primitiverna. Designvalet bakom stubbarna betyder mer än listan. En saknad symbol vid länkningen ger dig en vägg av odefinierade referenser från en enhet du aldrig rört; en stub som kastar ENotSupportedException ger dig ett bygge som kör, ett meddelande som namnger orsaken och en stackspårning som pekar på anropsstället. Det betyder också att ett Free Pascal-bygge aldrig i tysthet producerar fel bytes där ett Delphi-bygge skulle producera rätt. Notera andraordningseffekten också: att köra obetrodda bildcodecer i en isolerad process är ett beslut som bara uppstår i Delphi-bygget, eftersom ett Free Pascal-bygge över huvud taget inte har någon nativ avkodare i processen att sandlådeisolera
Komprimering: den första raden att ändra är cmNone
Innan du portar något annat, sätt Compression till cmNone. THPDFCompressionMethod erbjuder exakt två värden, cmNone och cmFlateDecode, och det andra går rakt in i de deflate-ingångspunkter som är stubbar i ett Free Pascal-bygge. Verifiera kärnobjektmodellen först med komprimeringen avstängd, och bestäm sedan vad du mer behöver. Det är den ordning som det medföljande smoke-testet använder: skapa ett okomprimerat dokument på en sida, läs in det igen och kontrollera att sidantalet kom tillbaka som ett. Okomprimerad utdata är större, och den är fortfarande en fullt giltig 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 når en stubbad symbol
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.
Vad händer med parallell sidrendering?
Den kompilerar fortfarande, returnerar fortfarande korrekta bitmappar och slutar vara parallell. THotPDF.RenderLoadedPagesParallel och THotPDF.RenderLoadedPagesParallelOrdered är byggda på TThread.CreateAnonymousThread med en inline-procedure som stängning, vilket Free Pascal 3.2.2 inte kan uttrycka, så Free Pascal-grenen kör ett deterministiskt seriellt reservspår: den går igenom sidindexen i ordning, anropar RenderLoadedPageToBitmap för varje och räknar de lyckade. API-formen, returvärdet och utdatavektorn är oförändrade, vilket är det som låter en enda kodbas byggas åt båda hållen
var
Bitmaps: THPDFBitmapArray;
Info: THPDFParallelRenderPipelineInfo;
Rendered: Integer;
begin
Rendered := Pdf.RenderLoadedPagesParallel([0, 1, 2, 3], 150, 4,
Bitmaps, Info);
// Delphi: Info.WorkerCount är vad minnesbudgeten tillät
// Free Pascal: Info.WorkerCount är alltid 1, sidor i indexordning
if Info.WorkerCount = 1 then
LogSerialFallback(Rendered, Info.RequestedWorkerCount);
Reservspåret är inte tyst, och det är den del som är värd att designa kring. Det fyller i THPDFParallelRenderPipelineInfo ärligt: PageCount från begäran, RequestedWorkerCount som ekar det du bad om, WorkerCount satt till 1, och räknarna för färdiga och levererade sidor matchar det som faktiskt kom tillbaka. Kod som redan inspekterar Info för att dimensionera en förloppsindikator eller en minnesbudget fortsätter att fungera och läser sanningen i stället för ett antagande. Om din genomströmningsplan hänger på den parallella renderingspipelinen och dess mottrycksmodell är den planen en Delphi-plan; på Free Pascal får du budgetera för den enkeltrådade kostnaden för att rendera en sida till en bitmapp multiplicerad med sidantalet
Vilket bygge bör du faktiskt leverera?
Välj efter kapacitet, inte efter preferens. Om ditt arbetsflöde är dokumentmontering, text- och vektorritning, ifyllning av formulär, inläsning och sparande, så täcker Free Pascal-bygget på Win64 det, och du bör validera med komprimeringen avstängd innan du slår på något. Om det handlar om bilder i JPEG, JPEG 2000, TIFF eller JBIG2, ICC-färgtransformer, komprimerad utdata eller genomströmning som hänger på många kärnor, håll dig till Delphi eller C++Builder tills vidare. Gränsen dras av ett objektfils-ABI och en saknad språkfunktion, som båda syns i källkoden i stället för att ligga begravda i en stödmatris, och som båda havererar med ett namngivet fel i stället för ett felaktigt resultat
Paketet för Free Pascal och Lazarus levereras i samma distribution som enheterna för Delphi och C++Builder, så en licens täcker båda och du kan testa Lazarus-vägen mot dina egna dokument innan du binder dig; produktsidan för HotPDF Delphi PDF Component bär den aktuella stödmatrisen för kompilatorer och den fullständiga API-referensen