Krátká odpověď na ten support ticket zní ano, s limity. HotPDF 2.730.0 staví na Free Pascal 3.2.2 a Lazarus 4.6 pro Win64 a cesty jádra — vytvoření, načtení a uložení — fungují. Co nepatří mezi ně, je všechno, co spočívá na staticky linkovaném nativním objektu kodeku nebo na anonymních metodách Delphi
Dotaz obvykle přichází stejně: tým standardizuje na Lazarus kvůli multiplatformnímu nástroji, nebo zdědí codebase Free Pascal a chce tutéž PDF komponentu, kterou už licence pro Delphi. Přenos vyzrálé knihovny Delphi je zřídka otázkou syntaxe. Zajímavé je, co přenos prozradí o tom, kde se knihovna tiše svázala s jedním toolchainem, a v tomto případě to svázání sedí na dvou velmi konkrétních místech: objektovém ABI přibalených kodeků a kompilátorových funkcích ukrytých za verzovým symbolem
Co Free Pascal 3.2.2 potřebuje, než HPDFDoc zkompiluje
HotPDF kompiluje pod Free Pascal jen v režimu Delphi a jen když jsou adresáře unit Lazarus LCL na hledací cestě. Ani jedno se nepřipouští k jednání. HotPDF.inc přepne kompilátor pomocí {$MODE DELPHI} a {$H+} uvnitř bloku {$IFDEF FPC} a čímkoli starším odmítá s {$FATAL}, když je FPC_FULLVERSION pod 30202, takže instalace 3.0.x selže nahlas místo vyrobení rozbité unity. Lazarus runtime balíček HotPDFLaz.lpk zakóduje zbytek: LCL jako vyžadovaný balíček a -Mdelphi jako vlastní volbu
Požadavek LCL překvapí lidi, kteří chtějí jen konzolový výstup, ale je strukturální. HPDFFPCCompat dodává typy Delphi VCL, pro něž Free Pascal nemá ekvivalent, mapuje TMetafile a TMetafileCanvas na bitmapové a canvas třídy LCL a aliashuje TRichEdit na TMemo, zatímco HPDFDoc aliashuje TPNGObject na Graphics.TPortableNetworkGraphic. Berte to jako kompilační shims, ne jako paritu funkcí: metafile třída opřená o bitmapu udrží unitu v kompilaci, nedělá z cest metafile chování, jaké mají na Delphi. I test bez GUI si táhne Interfaces a build skript podává -Fu pro lcl\units\x86_64-win64 a výstupní adresář lazutils
Proč D2009+ nemůže sloužit zároveň jako verzová brána
Je lákavé brát build Free Pascal jako moderní kompilátor a prostě definovat nejnovější symbol funkcí Delphi. HotPDF to nedělá a důvod stojí za vyslovení nahlas: D2009+ neznamená samotné Unicode řetězce, zároveň braní unity, jejichž veřejné API je vyjádřeno anonymními metodami. Free Pascal 3.2.2 nepodporuje anonymní metody Delphi ani tato API, takže vypůjčení symbolu by vleklo kód, který se nezkompiluje. Uses klauzule HPDFDoc proto nese dvě oddělené podmíněné patky a jejich překryv je záměrný, ne náhodný
uses
// ...
HPDFJavaScript,
HPDFFormCalcGraph
{$IFDEF FPC}
, HPDFFPCCodecStubs,
HPDFCMS,
HPDFWinCertSigner
{$ENDIF}
{$IFDEF D2009+}
, HPDFXFARuntime,
HPDFCMS,
HPDFWinCertSigner,
HPDFSignVerify,
HPDFSignatureBatch
{$ENDIF};
Proč se nativní kodeky zastaví u linkeru?
Protože jsou to Win64 COFF objekty emitované jedním konkrétním toolchainem a ani jeden z obou Free Pascal linkerů na Win64 je nespolkne: ani interní linker, ani externí cesta GNU ld. To je problém ABI objektových souborů, ne problém Pascalu, a žádná hromada podmíněného zdrojáku to neopraví. Knihovna jde jedinou poctivou cestou, která zbyla. Každá direktiva {$L}, která vtahuje statický objekt kodeku, je obalena v {$IFNDEF FPC}, takže build Free Pascal je prostě vynechá, a HPDFFPCCodecStubs pak dodá každý chybějící externí symbol jako stub, který vyhazuje místo vracení
// 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 tabulka stubů je dlouhá a čtení z ní vyčtete přesně, které schopnosti jsou dnes jen Delphi: vstupní body deflate zlib-ng a zopfli, komprese a dekomprese libjpeg, kodek OpenJPEG JPEG 2000, libtiff a jeho inicializátory na kompresi, kódování i dekódování JBIG2, vstupní body barevné transformace Little-CMS a primitivy AES. Volba designu za stuby má větší váhu než seznam. Chybějící symbol při linkování vám dává zeň nedefinovaných referencí z unity, které jste nikdy nesáhli; stub, který vyhazuje ENotSupportedException, vám dává build, který běží, hlášení pojmenovávající důvod a stack trace ukazující na místo volání. Znamená to také, že build Free Pascal nikdy tiše nevyrobí špatné bajty tam, kde by build Delphi vyrobil správné. Všimněte si také efektu druhého řádu: spouštění nedůvěryhodných obrazových kodeků v izolovaném procesu je rozhodnutí, které se rodí jen na buildu Delphi, protože build Free Pascal nemá v procesu žádný nativní dekodér, který by šlo sandboxovat
Komprese: první řádek, který změnit, je cmNone
Ještě před přenosem čehokoli jiného nastavte Compression na cmNone. THPDFCompressionMethod nabízí přesně dvě hodnoty, cmNone a cmFlateDecode, a ta druhá míří rovnou do vstupních bodů deflate, které jsou ve buildu Free Pascal stuby. Nejdřív ověřte jádro objektového modelu s kompresí vypnutou, pak rozhodněte, co dalšího potřebujete. Toto je pořadí, které používá dodaný smoke test: vytvoří jednostránkový nekomprimovaný dokument, znovu ho načte a tvrdí, že počet stránek vrátil jedničku. Nekomprimovaný výstup je větší a je to stále naprosto validní 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 sahá na stubovaný 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.
Co se stane s paralelním renderováním stránek?
Zkompiluje se dál, vrací dál správné bitmapy a přestane být paralelní. THotPDF.RenderLoadedPagesParallel a THotPDF.RenderLoadedPagesParallelOrdered staví na TThread.CreateAnonymousThread s inline uzávěrou procedure, kterou Free Pascal 3.2.2 nedokáže vyjádřit, takže větev Free Pascal běží deterministickým sériovým fallbackem: projde indexy stránek v pořadí, pro každý zavolá RenderLoadedPageToBitmap a počítá úspěchy. Tvar API, návratová hodnota i výstupní pole zůstávají beze změny, což dovolí jediné codebase stavět oběma směry
var
Bitmaps: THPDFBitmapArray;
Info: THPDFParallelRenderPipelineInfo;
Rendered: Integer;
begin
Rendered := Pdf.RenderLoadedPagesParallel([0, 1, 2, 3], 150, 4,
Bitmaps, Info);
// Delphi: Info.WorkerCount je, co dovolil paměťový rozpočet
// Free Pascal: Info.WorkerCount je vždy 1, stránky v pořadí indexů
if Info.WorkerCount = 1 then
LogSerialFallback(Rendered, Info.RequestedWorkerCount);
Fallback není tichý a právě to je kus, který stojí za navržení okolo. Vyplňuje THPDFParallelRenderPipelineInfo poctivě: PageCount z požadavku, RequestedWorkerCount odrážející, o co jste požádali, WorkerCount nastavený na 1 a počty dokončených a doručených odpovídající tomu, co skutečně dorazilo. Kód, který už prohlíží Info, aby rozměrnil progress bar nebo paměťový rozpočet, funguje dál a čte pravdu místo předpokladu. Pokud váš plán propustnosti závisí na paralelní renderovací pipeline a jejím modelu backpressure, je ten plán plán pro Delphi; na Free Pascal rozpočítejte jednovláknovou cenu renderování stránky do bitmapy krát počet stránek
Který build skutečně expedovat?
Vybírejte podle schopností, ne podle preference. Pokud vaše workflow je skládání dokumentů, kreslení textu a vektorů, naplňování formulářů, načítání a ukládání, pokryje to build Free Pascal na Win64 a měli byste validovat s kompresí vypnutou, než cokoli zapnete. Pokud to zahrnuje obrázky JPEG, JPEG 2000, TIFF nebo JBIG2, barevné transformace ICC, komprimovaný výstup nebo propustnost závislou na mnoha jádrech, zůstaňte zatím na Delphi nebo C++Builder. Hranice je vytyčena ABI objektových souborů a chybějící jazykovou funkcí, obojí je vidět ve zdrojáku místo pohřbení v support matici a obojí selhává pojmenovanou chybou místo špatného výsledku
Balíček Free Pascal a Lazarus putuje ve stejném rozdělení jako unity Delphi a C++Builder, takže licence pokrývá obojí a Lazarus cestu můžete testovat proti vlastním dokumentům, než se ji zavážete; produktová stránka HotPDF Delphi PDF Component nese aktuální matici podpory kompilátorů a kompletní referenci API