La réponse courte à ce ticket de support est oui, avec des limites. HotPDF 2.730.0 se construit avec Free Pascal 3.2.2 et Lazarus 4.6 pour Win64, et les chemins fondamentaux de création, de chargement et de sauvegarde fonctionnent. Ce qui ne suit pas, c’est tout ce qui repose sur un objet codec natif à édition de liens statique ou sur les méthodes anonymes de Delphi
La question arrive généralement de la même manière : une équipe standardise Lazarus pour un outil multiplateforme, ou hérite d’une base de code Free Pascal, et veut le même composant PDF qu’elle licencie déjà pour Delphi. Porter une bibliothèque Delphi mature est rarement une affaire de syntaxe. La partie intéressante est ce que le portage révèle sur les endroits où la bibliothèque était discrètement couplée à une seule chaîne d’outils, et en l’occurrence ce couplage se trouve à deux endroits très précis : l’ABI des fichiers objets des codecs embarqués, et les fonctionnalités du compilateur cachées derrière un symbole de version
Ce dont Free Pascal 3.2.2 a besoin avant que HPDFDoc ne compile
HotPDF compile sous Free Pascal uniquement en mode Delphi, et uniquement quand les répertoires d’unités LCL de Lazarus sont sur le chemin de recherche. Rien de tout cela n’est négociable. HotPDF.inc bascule le compilateur avec {$MODE DELPHI} et {$H+} à l’intérieur de son bloc {$IFDEF FPC} et refuse tout compilateur plus ancien avec un {$FATAL} quand FPC_FULLVERSION est inférieure à 30202, si bien qu’une installation 3.0.x échoue bruyamment au lieu de produire une unité cassée. Le paquet d’exécution Lazarus HotPDFLaz.lpk encode le reste : LCL comme paquet requis et -Mdelphi comme option personnalisée
L’exigence LCL surprend celles et ceux qui ne veulent qu’une sortie console, mais elle est structurelle. HPDFFPCCompat fournit les types VCL de Delphi pour lesquels Free Pascal n’a pas d’équivalent, en mappant TMetafile et TMetafileCanvas sur les classes bitmap et canvas de la LCL et en faisant un alias de TRichEdit vers TMemo, tandis que HPDFDoc crée un alias de TPNGObject vers Graphics.TPortableNetworkGraphic. Traitez-les comme des shims de compilation, pas comme une parité de fonctionnalités : une classe métafichier adossée à un bitmap garde l’unité compilable, elle ne rend pas les chemins métafichier identiques à leur comportement sur Delphi. Même le test de fumée sans GUI ajoute Interfaces, et le script de construction passe -Fu pour lcl\units\x86_64-win64 et le répertoire de sortie de lazutils
Pourquoi D2009+ ne peut pas servir de verrou de version
Il est tentant de considérer la construction Free Pascal comme celle d’un compilateur moderne et de définir simplement le symbole de fonctionnalité Delphi le plus récent. HotPDF ne le fait pas, et la raison mérite d’être énoncée clairement : D2009+ ne signifie pas uniquement les chaînes Unicode, il verrouille aussi les unités dont l’API publique est exprimée avec des méthodes anonymes. Free Pascal 3.2.2 ne prend en charge ni les méthodes anonymes de Delphi ni ces API, donc emprunter le symbole entraînerait du code qui ne peut pas compiler. La clause uses de HPDFDoc porte donc deux queues conditionnelles distinctes, et leur chevauchement est délibéré plutôt qu’accidentel
uses
// ...
HPDFJavaScript,
HPDFFormCalcGraph
{$IFDEF FPC}
, HPDFFPCCodecStubs,
HPDFCMS,
HPDFWinCertSigner
{$ENDIF}
{$IFDEF D2009+}
, HPDFXFARuntime,
HPDFCMS,
HPDFWinCertSigner,
HPDFSignVerify,
HPDFSignatureBatch
{$ENDIF};
Pourquoi les codecs natifs s’arrêtent-ils au lieur ?
Parce que ce sont des objets COFF Win64 émis par une chaîne d’outils bien précise, et qu’aucun des deux lieurs de Free Pascal en Win64 ne les consommera : ni le lieur interne, ni la voie externe GNU ld. C’est un problème d’ABI de fichiers objets, pas un problème Pascal, et aucune quantité de source conditionnelle ne le corrige. La bibliothèque emprunte la seule voie honnête disponible. Chaque directive {$L} qui tire un objet codec statique est enveloppée dans {$IFNDEF FPC}, si bien que la construction Free Pascal les omet simplement, et HPDFFPCCodecStubs fournit alors chaque symbole externe manquant sous forme de stub qui lève au lieu de renvoyer
// 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;
Cette table de stubs est longue, et sa lecture vous dit exactement quelles capacités sont réservées à Delphi aujourd’hui : les points d’entrée deflate de zlib-ng et zopfli, la compression et la décompression libjpeg, le codec JPEG 2000 OpenJPEG, libtiff et ses initialiseurs par compression, l’encodage et le décodage JBIG2, les points d’entrée de transformation colorimétrique Little-CMS, et les primitives AES. Le choix de conception derrière les stubs compte plus que la liste. Un symbole manquant à l’édition de liens vous donne un mur de références indéfinies venant d’une unité que vous n’avez jamais touchée ; un stub qui lève ENotSupportedException vous donne une construction qui s’exécute, un message nommant la raison, et une trace de pile pointant vers le site d’appel. Cela signifie aussi qu’une construction Free Pascal ne produit jamais silencieusement des octets faux là où une construction Delphi produirait des octets corrects. Notez aussi l’effet de second ordre : exécuter les codecs d’images non fiables dans un processus isolé est une décision qui ne se pose que sur la construction Delphi, car une construction Free Pascal n’a aucun décodeur natif intra-processus à isoler au départ
Compression : la première ligne à changer est cmNone
Avant de porter quoi que ce soit d’autre, mettez Compression à cmNone. THPDFCompressionMethod offre exactement deux valeurs, cmNone et cmFlateDecode, et la seconde mène directement aux points d’entrée deflate qui sont des stubs dans une construction Free Pascal. Vérifiez d’abord le modèle d’objets fondamental avec la compression désactivée, puis décidez de ce dont vous avez besoin d’autre. C’est l’ordre que suit le test de fumée livré : créer un document d’une page non compressé, le recharger, et vérifier que le nombre de pages revient bien à un. Une sortie non compressée est plus volumineuse, et elle reste un PDF parfaitement valide
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 atteint un symbole remplacé par un stub
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.
Que devient le rendu parallèle des pages ?
Il compile toujours, renvoie toujours des bitmaps corrects, et cesse d’être parallèle. THotPDF.RenderLoadedPagesParallel et THotPDF.RenderLoadedPagesParallelOrdered sont bâties sur TThread.CreateAnonymousThread avec une fermeture procedure en ligne, ce que Free Pascal 3.2.2 ne sait pas exprimer, si bien que la branche Free Pascal exécute un repli séquentiel déterministe : elle parcourt les indices de pages dans l’ordre, appelle RenderLoadedPageToBitmap pour chacune, et compte les succès. La forme de l’API, la valeur de retour et le tableau de sortie restent inchangés, et c’est ce qui permet à une seule base de code de se construire des deux manières
var
Bitmaps: THPDFBitmapArray;
Info: THPDFParallelRenderPipelineInfo;
Rendered: Integer;
begin
Rendered := Pdf.RenderLoadedPagesParallel([0, 1, 2, 3], 150, 4,
Bitmaps, Info);
// Delphi : Info.WorkerCount vaut ce que le budget mémoire a permis
// Free Pascal : Info.WorkerCount vaut toujours 1, pages dans l'ordre des indices
if Info.WorkerCount = 1 then
LogSerialFallback(Rendered, Info.RequestedWorkerCount);
Le repli n’est pas silencieux, et c’est la partie qui mérite d’être conçue autour. Il remplit THPDFParallelRenderPipelineInfo honnêtement : PageCount issu de la requête, RequestedWorkerCount faisant écho à ce que vous avez demandé, WorkerCount mis à 1, et les compteurs de pages complétées et livrées correspondant à ce qui est réellement revenu. Le code qui inspecte déjà Info pour dimensionner une barre de progression ou un budget mémoire continue de fonctionner et lit la vérité plutôt qu’une hypothèse. Si votre plan de débit dépend du pipeline de rendu parallèle et de son modèle de contre-pression, ce plan est un plan Delphi ; sur Free Pascal, budgétez le coût monothread de rendu d’une page en bitmap multiplié par le nombre de pages
Quelle construction faut-il réellement livrer ?
Choisissez selon les capacités, pas selon les préférences. Si votre flux de travail est l’assemblage de documents, le dessin de texte et de vecteurs, le remplissage de formulaires, le chargement et la sauvegarde, la construction Free Pascal en Win64 le couvre, et vous devriez valider avec la compression désactivée avant d’activer quoi que ce soit. Si cela implique des images JPEG ou JPEG 2000 ou TIFF ou JBIG2, des transformations colorimétriques ICC, une sortie compressée, ou un débit qui dépend de nombreux cœurs, restez pour l’instant sur Delphi ou C++Builder. La frontière est tracée par une ABI de fichiers objets et une fonctionnalité de langage manquante, toutes deux visibles dans la source plutôt qu’enfouies dans une matrice de support, et toutes deux en échec avec une erreur nommée plutôt qu’un résultat faux
Le paquet Free Pascal et Lazarus est livré dans la même distribution que les unités Delphi et C++Builder, si bien qu’une licence couvre les deux et que vous pouvez tester le chemin Lazarus contre vos propres documents avant de vous y engager ; la page produit du composant PDF HotPDF pour Delphi porte la matrice de support des compilateurs actuelle et la référence API complète