La respuesta corta a ese ticket de soporte es sí, con límites. HotPDF 2.730.0 compila en Free Pascal 3.2.2 y Lazarus 4.6 para Win64, y los caminos núcleo de crear, cargar y guardar funcionan. Lo que no sigue son las cosas que descansen sobre un objeto de códec nativo enlazado estáticamente o sobre los métodos anónimos de Delphi
La pregunta suele llegar de la misma manera: un equipo estandariza en Lazarus para una herramienta multiplataforma, o hereda una base de código en Free Pascal, y quiere el mismo componente PDF que ya licencia para Delphi. Portar una biblioteca Delphi madura rara vez es cuestión de sintaxis. Lo interesante es lo que el porte revela sobre dónde la biblioteca quedó silenciosamente acoplada a una sola cadena de herramientas, y en este caso el acoplamiento está en dos lugares muy concretos: el ABI de archivos objeto de los códecs incluidos, y las características del compilador escondidas detrás de 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. Ninguna de las dos cosas es negociable. HotPDF.inc conmuta 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 es menor que 30202, así que una instalación 3.0.x falla con estruendo en lugar de producir una unidad rota. El paquete runtime de Lazarus HotPDFLaz.lpk codifica el resto: LCL como paquete requerido y -Mdelphi como opción personalizada
El requisito de LCL sorprende a quienes solo quieren 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 poniendo TRichEdit como alias de TMemo, mientras que HPDFDoc pone TPNGObject como alias de Graphics.TPortableNetworkGraphic. Trátenlos como shims de compilación, no como paridad de características: una clase metafile respaldada por un bitmap mantiene la unidad compilando, no hace que los caminos de metafile se comporten como en Delphi. Incluso la prueba de humo sin GUI incorpora 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 de compuerta de versión
Es tentador tratar la compilación Free Pascal como un compilador moderno y simplemente definir el símbolo de características Delphi más nuevo. HotPDF no lo hace, y la razón vale enunciarla sin rodeos: D2009+ no significa solo cadenas Unicode, también da paso a 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 tomar prestado el símbolo arrastraría código que no puede compilar. Por eso la cláusula uses de HPDFDoc lleva dos colas condicionales separadas, y el traslape 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 linker?
Porque son objetos COFF Win64 emitidos por una cadena de herramientas en particular, y ninguno de los dos linkers de Free Pascal en Win64 los consumirá: ni el linker interno, ni el camino externo de GNU ld. Este es un problema de ABI de archivos objeto, no un problema de Pascal, y ninguna cantidad de código condicional lo arregla. La biblioteca toma el único camino honesto disponible. Cada directiva {$L} que incorpora un objeto de códec estático va envuelta en {$IFNDEF FPC}, así que la compilación Free Pascal simplemente los omite, y HPDFFPCCodecStubs luego suministra cada símbolo externo faltante 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 les dice exactamente qué capacidades son solo de Delphi hoy: 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 faltante en tiempo de enlace les da una pared de referencias indefinidas desde una unidad que nunca tocaron; un stub que lanza ENotSupportedException les da una compilación que corre, un mensaje que nombra la razón y un stack trace que apunta al sitio de la llamada. También significa que una compilación Free Pascal nunca produce bytes equivocados en silencio donde una compilación Delphi produciría los correctos. Fíjense 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 compilación Delphi, porque una compilación Free Pascal no tiene decodificador nativo en proceso que aislar en primer lugar
Compresión: la primera línea a cambiar es cmNone
Antes de portar cualquier otra cosa, pongan 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 compilación Free Pascal. Verifiquen primero el modelo de objetos núcleo con la compresión apagada, y luego decidan qué más necesitan. Ese es el orden que usa la prueba de humo incluida: crear un documento de una página sin comprimir, recargarlo y verificar que el conteo de páginas volvió 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 de páginas en paralelo?
Sigue compilando, sigue devolviendo bitmaps correctos y deja de ser paralelo. THotPDF.RenderLoadedPagesParallel y THotPDF.RenderLoadedPagesParallelOrdered están construidos sobre TThread.CreateAnonymousThread con un closure procedure en línea, que Free Pascal 3.2.2 no puede expresar, así que la rama Free Pascal corre 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 arreglo de salida no cambian, que es lo que permite que una sola base de código compile de ambas maneras
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 el presupuesto de memoria permitió
// Free Pascal: Info.WorkerCount siempre es 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 vale la pena tener en cuenta al diseñar. Llena THPDFParallelRenderPipelineInfo con honestidad: PageCount de la petición, RequestedWorkerCount reflejando lo que pidieron, WorkerCount en 1, y los conteos de completadas y entregadas coincidiendo con lo que realmente 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 su plan de throughput depende de el pipeline de renderizado paralelo y su modelo de backpressure, ese plan es un plan Delphi; en Free Pascal, presupuesten el costo de un solo hilo de renderizar una página a un bitmap multiplicado por el conteo de páginas
¿Qué compilación debería enviar realmente?
Elijan por capacidad, no por preferencia. Si su flujo de trabajo es ensamblaje de documentos, dibujo de texto y vectores, llenado de formularios, carga y guardado, la compilación Free Pascal en Win64 lo cubre, y deberían validar con la compresión apagada antes de activar cualquier cosa. Si involucra imágenes JPEG o JPEG 2000 o TIFF o JBIG2, transformaciones de color ICC, salida comprimida o throughput que depende de muchos núcleos, quédense 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 código fuente en lugar de enterrados en una matriz de soporte, y ambos fallan con un error con nombre en lugar de 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 pueden probar el camino Lazarus contra sus propios documentos antes de comprometerse con él; la página de producto de HotPDF Delphi PDF Component lleva la matriz de soporte de compiladores actual y la referencia completa de la API