La respuesta corta a ese ticket de soporte es sí, con límites. HotPDF 2.730.0 compila con Free Pascal 3.2.2 y Lazarus 4.6 para Win64, y las rutas centrales de creación, carga y guardado funcionan. Lo que no viene de la mano es cualquier cosa que se apoye en un objeto de códec nativo enlazado estáticamente o en métodos anónimos de Delphi
La pregunta suele llegar siempre igual: un equipo estandariza en Lazarus para una herramienta multiplataforma, o hereda una base de código Free Pascal, y quiere el mismo componente PDF para el que ya paga licencia en Delphi. Portar una biblioteca Delphi madura rara vez es cuestión de sintaxis. Lo interesante es lo que el port revela sobre dónde estaba la biblioteca acoplada calladamente a una sola cadena de herramientas, y en este caso el acoplamiento se sienta en dos lugares muy concretos: el ABI de archivos objeto de los códecs incluidos y las características del compilador escondidas tras un símbolo de versión
Qué necesita Free Pascal 3.2.2 antes de que HPDFDoc compile
HotPDF compila bajo Free Pascal solo en modo Delphi, y solo cuando los directorios de unidades LCL de Lazarus están en la ruta de búsqueda. Nada de eso es negociable. HotPDF.inc cambia el compilador con {$MODE DELPHI} y {$H+} dentro de su bloque {$IFDEF FPC} y rechaza cualquier versión anterior con un {$FATAL} cuando FPC_FULLVERSION está por debajo de 30202, de modo que una instalación 3.0.x falla a los gritos en lugar de producir una unidad rota. El paquete de runtime de Lazarus HotPDFLaz.lpk codifica el resto: LCL como paquete requerido y -Mdelphi como opción personalizada
El requisito de LCL sorprende a quien solo quiere salida de consola, pero es estructural. HPDFFPCCompat suministra los tipos VCL de Delphi para los que Free Pascal no tiene equivalente, mapeando TMetafile y TMetafileCanvas sobre clases de bitmap y canvas de LCL y aliasando TRichEdit a TMemo, mientras que HPDFDoc aliasa TPNGObject a Graphics.TPortableNetworkGraphic. Tómalos como cuñas de compilación, no como paridad de características: una clase metafile apoyada en un bitmap mantiene la unidad compilando, no hace que las rutas de metafile se comporten como en Delphi. Hasta la prueba de humo sin GUI tira de Interfaces, y el script de build pasa -Fu para lcl\units\x86_64-win64 y el directorio de salida de lazutils
Por qué D2009+ no puede hacer también de gate de versión
Es tentador tratar la build Free Pascal como un compilador moderno y definir sin más el símbolo de la característica Delphi más nueva. HotPDF no lo hace, y el motivo conviene decirlo sin adornos: D2009+ no significa solo cadenas Unicode, también hace de gate para unidades cuya API pública se expresa con métodos anónimos. Free Pascal 3.2.2 no soporta ni los métodos anónimos de Delphi ni esas APIs, así que pedir prestado el símbolo arrastraría código que no puede compilar. La cláusula uses de HPDFDoc lleva por eso dos colas condicionales separadas, y el solape entre ellas es deliberado y no accidental
uses
// ...
HPDFJavaScript,
HPDFFormCalcGraph
{$IFDEF FPC}
, HPDFFPCCodecStubs,
HPDFCMS,
HPDFWinCertSigner
{$ENDIF}
{$IFDEF D2009+}
, HPDFXFARuntime,
HPDFCMS,
HPDFWinCertSigner,
HPDFSignVerify,
HPDFSignatureBatch
{$ENDIF};
¿Por qué los códecs nativos se detienen en el enlazador?
Porque son objetos COFF Win64 emitidos por una cadena de herramientas concreta, y ningún enlazador de Free Pascal en Win64 los consume: ni el enlazador interno, ni la ruta externa de GNU ld. Es un problema de ABI de archivos objeto, no un problema Pascal, y ninguna cantidad de código condicional lo arregla. La biblioteca toma la única ruta honesta disponible. Cada directiva {$L} que trae un objeto de códec estático va envuelta en {$IFNDEF FPC}, así que la build Free Pascal simplemente los omite, y HPDFFPCCodecStubs suministra entonces cada símbolo externo ausente como un stub que lanza en lugar de devolver
// 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;
Esa tabla de stubs es larga, y leerla te dice exactamente qué capacidades son hoy exclusivas de Delphi: los puntos de entrada deflate de zlib-ng y zopfli, la compresión y descompresión de libjpeg, el códec JPEG 2000 de OpenJPEG, libtiff y sus inicializadores por compresión, la codificación y decodificación JBIG2, los puntos de entrada de transformación de color de Little-CMS y las primitivas AES. La decisión de diseño detrás de los stubs importa más que la lista. Un símbolo ausente en tiempo de enlace te da un muro de referencias sin definir desde una unidad que jamás tocaste; un stub que lanza ENotSupportedException te da una build que corre, un mensaje que nombra el motivo y un stack trace que apunta al punto de llamada. También significa que una build Free Pascal nunca produce bytes equivocados en silencio donde una build Delphi produciría los correctos. Fíjate también en el efecto de segundo orden: ejecutar códecs de imágenes no confiables en un proceso aislado es una decisión que solo surge en la build Delphi, porque una build Free Pascal no tiene decodificador nativo en proceso que encerrar en un sandbox, para empezar
Compresión: la primera línea que cambiar es cmNone
Antes de portar cualquier otra cosa, pon Compression en cmNone. THPDFCompressionMethod ofrece exactamente dos valores, cmNone y cmFlateDecode, y el segundo va directo a los puntos de entrada deflate que son stubs en una build Free Pascal. Verifica primero el modelo de objetos central con la compresión apagada y decide luego qué más necesitas. Ese es el orden que usa la prueba de humo incluida: crear un documento de una página sin comprimir, recargarlo y comprobar que el recuento de páginas vuelve como uno. La salida sin comprimir es más grande, y sigue siendo un PDF perfectamente válido
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 llega a un símbolo con 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.
¿Qué pasa con el renderizado paralelo de páginas?
Sigue compilando, sigue devolviendo bitmaps correctos y deja de ser paralelo. THotPDF.RenderLoadedPagesParallel y THotPDF.RenderLoadedPagesParallelOrdered se apoyan en TThread.CreateAnonymousThread con un closure procedure inline, que Free Pascal 3.2.2 no sabe expresar, así que la rama Free Pascal ejecuta un fallback serial determinista: recorre los índices de página en orden, llama a RenderLoadedPageToBitmap por cada uno y cuenta los éxitos. La forma de la API, el valor de retorno y el array de salida no cambian, que es lo que permite que una sola base de código compile por ambos caminos
var
Bitmaps: THPDFBitmapArray;
Info: THPDFParallelRenderPipelineInfo;
Rendered: Integer;
begin
Rendered := Pdf.RenderLoadedPagesParallel([0, 1, 2, 3], 150, 4,
Bitmaps, Info);
// Delphi: Info.WorkerCount es lo que dejó el presupuesto de memoria
// Free Pascal: Info.WorkerCount es siempre 1, páginas en orden de índice
if Info.WorkerCount = 1 then
LogSerialFallback(Rendered, Info.RequestedWorkerCount);
El fallback no es silencioso, que es la parte que merece diseñar alrededor. Llena THPDFParallelRenderPipelineInfo con honestidad: PageCount de la petición, RequestedWorkerCount haciendo eco de lo que pediste, WorkerCount puesto en 1, y los recuentos de completadas y entregadas coincidiendo con lo que de verdad volvió. El código que ya inspecciona Info para dimensionar una barra de progreso o un presupuesto de memoria sigue funcionando y lee la verdad en lugar de una suposición. Si tu plan de rendimiento depende de el pipeline de renderizado paralelo y su modelo de contrapresión, ese plan es un plan Delphi; en Free Pascal, presupuesta el coste monothread de renderizar una página a bitmap multiplicado por el recuento de páginas
Qué build deberías enviar de verdad
Elige por capacidad, no por preferencia. Si tu flujo de trabajo es ensamblaje de documentos, dibujo de texto y vectores, relleno de formularios, carga y guardado, la build Free Pascal en Win64 lo cubre, y deberías validar con la compresión apagada antes de encender nada. Si implica imágenes JPEG o JPEG 2000 o TIFF o JBIG2, transformaciones de color ICC, salida comprimida o rendimiento que depende de muchos núcleos, quédate en Delphi o C++Builder por ahora. La frontera la trazan un ABI de archivos objeto y una característica del lenguaje ausente, ambos visibles en el fuente en lugar de enterrados en una matriz de soporte, y ambos fallan con un error con nombre y no con un resultado equivocado
El paquete Free Pascal y Lazarus se entrega en la misma distribución que las unidades Delphi y C++Builder, así que una licencia cubre ambas y puedes probar la ruta Lazarus contra tus propios documentos antes de comprometerte; la página de producto del HotPDF Delphi PDF Component lleva la matriz de soporte de compiladores actual y la referencia completa de la API