The short answer to that support ticket is yes, with limits. HotPDF 2.730.0 builds on Free Pascal 3.2.2 and Lazarus 4.6 for Win64, and the core create, load and save paths work. What does not follow is anything resting on a statically linked native codec object or on Delphi anonymous methods
The question usually arrives the same way: a team standardises on Lazarus for a cross-platform tool, or inherits a Free Pascal codebase, and wants the same PDF component they already license for Delphi. Porting a mature Delphi library is rarely a matter of syntax. The interesting part is what the port exposes about where the library was quietly coupled to one toolchain, and in this case the coupling sits in two very specific places: the object-file ABI of the bundled codecs, and the compiler features hiding behind a version symbol
What Free Pascal 3.2.2 needs before HPDFDoc compiles
HotPDF compiles under Free Pascal only in Delphi mode, and only when the Lazarus LCL unit directories are on the search path. Neither is negotiable. HotPDF.inc switches the compiler with {$MODE DELPHI} and {$H+} inside its {$IFDEF FPC} block and refuses anything older with a {$FATAL} when FPC_FULLVERSION is below 30202, so a 3.0.x installation fails loudly rather than producing a broken unit. The Lazarus runtime package HotPDFLaz.lpk encodes the rest: LCL as a required package and -Mdelphi as a custom option
The LCL requirement surprises people who only want console output, but it is structural. HPDFFPCCompat supplies the Delphi VCL types that Free Pascal has no equivalent for, mapping TMetafile and TMetafileCanvas onto LCL bitmap and canvas classes and aliasing TRichEdit to TMemo, while HPDFDoc aliases TPNGObject to Graphics.TPortableNetworkGraphic. Treat those as compile-time shims, not feature parity: a metafile class backed by a bitmap keeps the unit compiling, it does not make the metafile paths behave the way they do on Delphi. Even the non-GUI smoke test pulls in Interfaces, and the build script passes -Fu for lcl\units\x86_64-win64 and the lazutils output directory
Why D2009+ cannot double as the version gate
It is tempting to treat the Free Pascal build as a modern compiler and simply define the newest Delphi feature symbol. HotPDF does not, and the reason is worth stating plainly: D2009+ does not mean Unicode strings alone, it also gates units whose public API is expressed with anonymous methods. Free Pascal 3.2.2 supports neither Delphi anonymous methods nor those APIs, so borrowing the symbol would drag in code that cannot compile. The uses clause of HPDFDoc therefore carries two separate conditional tails, and the overlap between them is deliberate rather than accidental
uses
// ...
HPDFJavaScript,
HPDFFormCalcGraph
{$IFDEF FPC}
, HPDFFPCCodecStubs,
HPDFCMS,
HPDFWinCertSigner
{$ENDIF}
{$IFDEF D2009+}
, HPDFXFARuntime,
HPDFCMS,
HPDFWinCertSigner,
HPDFSignVerify,
HPDFSignatureBatch
{$ENDIF};
Why do the native codecs stop at the linker?
Because they are Win64 COFF objects emitted by one particular toolchain, and neither Free Pascal linker on Win64 will consume them: not the internal linker, not the external GNU ld path. This is an object-file ABI problem, not a Pascal problem, and no amount of conditional source fixes it. The library takes the only honest route available. Every {$L} directive that pulls in a static codec object is wrapped in {$IFNDEF FPC}, so the Free Pascal build simply omits them, and HPDFFPCCodecStubs then supplies each missing external symbol as a stub that raises instead of returning
// 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;
That stub table is long, and reading it tells you exactly which capabilities are Delphi-only today: the zlib-ng and zopfli deflate entry points, libjpeg compression and decompression, the OpenJPEG JPEG 2000 codec, libtiff and its per-compression initialisers, JBIG2 encode and decode, the Little-CMS colour transform entry points, and the AES primitives. The design choice behind the stubs matters more than the list. A missing symbol at link time gives you a wall of undefined references from a unit you never touched; a stub that raises ENotSupportedException gives you a build that runs, a message naming the reason, and a stack trace pointing at the call site. It also means a Free Pascal build never silently produces wrong bytes where a Delphi build would produce correct ones. Note the second-order effect too: running untrusted image codecs in an isolated process is a decision that only arises on the Delphi build, because a Free Pascal build has no in-process native decoder to sandbox in the first place
Compression: the first line to change is cmNone
Before you port anything else, set Compression to cmNone. THPDFCompressionMethod offers exactly two values, cmNone and cmFlateDecode, and the second one routes straight into the deflate entry points that are stubs in a Free Pascal build. Verify the core object model first with compression off, then decide what else you need. That is the order the shipped smoke test uses: create a one-page uncompressed document, reload it, and assert the page count came back as one. Uncompressed output is larger, and it is still a perfectly valid 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 reaches a stubbed 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.
What happens to parallel page rendering?
It still compiles, still returns correct bitmaps, and stops being parallel. THotPDF.RenderLoadedPagesParallel and THotPDF.RenderLoadedPagesParallelOrdered are built on TThread.CreateAnonymousThread with an inline procedure closure, which Free Pascal 3.2.2 cannot express, so the Free Pascal branch runs a deterministic serial fallback: it walks the page indices in order, calls RenderLoadedPageToBitmap for each one, and counts the successes. The API shape, the return value and the output array are unchanged, which is what lets a single codebase build both ways
var
Bitmaps: THPDFBitmapArray;
Info: THPDFParallelRenderPipelineInfo;
Rendered: Integer;
begin
Rendered := Pdf.RenderLoadedPagesParallel([0, 1, 2, 3], 150, 4,
Bitmaps, Info);
// Delphi: Info.WorkerCount is whatever the memory budget allowed
// Free Pascal: Info.WorkerCount is always 1, pages in index order
if Info.WorkerCount = 1 then
LogSerialFallback(Rendered, Info.RequestedWorkerCount);
The fallback is not silent, which is the part worth designing around. It fills THPDFParallelRenderPipelineInfo honestly: PageCount from the request, RequestedWorkerCount echoing what you asked for, WorkerCount set to 1, and the completed and delivered counts matching what actually came back. Code that already inspects Info to size a progress bar or a memory budget keeps working and reads the truth rather than an assumption. If your throughput plan depends on the parallel render pipeline and its backpressure model, that plan is a Delphi plan; on Free Pascal, budget for the single-threaded cost of rendering a page to a bitmap multiplied by the page count
Which build should you actually ship?
Pick by capability, not by preference. If your workflow is document assembly, text and vector drawing, form population, loading and saving, the Free Pascal build on Win64 covers it, and you should validate with compression off before switching anything on. If it involves JPEG or JPEG 2000 or TIFF or JBIG2 images, ICC colour transforms, compressed output, or throughput that depends on many cores, stay on Delphi or C++Builder for now. The boundary is drawn by an object-file ABI and a missing language feature, both of which are visible in the source rather than buried in a support matrix, and both of which fail with a named error rather than a wrong result
The Free Pascal and Lazarus package ships in the same distribution as the Delphi and C++Builder units, so a licence covers both and you can test the Lazarus path against your own documents before committing to it; the HotPDF Delphi PDF Component product page carries the current compiler support matrix and the full API reference