Teknisk artikel

HotPDF på Free Pascal og Lazarus: Win64-grænser

Det korte svar på den supportsag er ja, med grænser. HotPDF 2.730.0 bygges på Free Pascal 3.2.2 og Lazarus 4.6 til Win64, og kerneoprettelses-, indlæsnings- og lagringsstierne virker. Det, der ikke følger med, er alt, der hviler på et statisk linket native codec-objekt eller på Delphi anonymous methods

Spørgsmålet ankommer normalt på samme måde: et team standardiserer på Lazarus til et cross-platform-værktøj, eller arver en Free Pascal-kodebase og vil have samme PDF-komponent, de allerede licenserer til Delphi. At portere et modent Delphi-bibliotek er sjældent et spørgsmål om syntaks. Den interessante del er, hvad porteringen afslører om, hvor biblioteket stiltiende var koblet til én toolchain, og i dette tilfælde sidder koblingen to helt specifikke steder: objektfil-ABI'en for de medfølgende codecs og compilerfunktionerne, der gemmer sig bag et versionssymbol

En evnematrice, der sammenligner HotPDF Delphi-builden med Free Pascal 3.2.2- og Lazarus 4.6-Win64-builden og viser, hvilke dokumentstier der deles, og hvilke codec-, komprimerings-, parallel-renderings- og anonymous-method-API'er når en rejsende stub
Kerneoprettelses-, indlæsnings- og lagringsstierne er identiske på begge builds, og hullet sidder udelukkende i de statisk linkede codecs og anonymous-method-API'erne

Hvad Free Pascal 3.2.2 behøver, før HPDFDoc kompilerer

HotPDF kompilerer under Free Pascal kun i Delphi mode, og kun når Lazarus LCL-unit-mapperne er på søgestien. Ingen af delene kan forhandles. HotPDF.inc skifter compileren med {$MODE DELPHI} og {$H+} inde i sin {$IFDEF FPC}-blok og nægter alt ældre med en {$FATAL}, når FPC_FULLVERSION er under 30202, så en 3.0.x-installation fejler højt i stedet for at producere en ødelagt unit. Lazarus runtime-pakken HotPDFLaz.lpk koder resten: LCL som en påkrævet pakke og -Mdelphi som en tilpasset indstilling

LCL-kravet overrasker folk, der kun vil have konsoloutput, men det er strukturelt. HPDFFPCCompat leverer Delphi VCL-typerne, som Free Pascal ikke har nogen ækvivalent til, mapper TMetafile og TMetafileCanvas på LCL bitmap- og canvas-klasser og aliaser TRichEdit til TMemo, mens HPDFDoc aliaser TPNGObject til Graphics.TPortableNetworkGraphic. Behandl dem som compile-time-shims, ikke feature-paritet: en metafile-klasse understøttet af en bitmap holder uniten kompilerende, den gør ikke metafile-stierne til at opføre sig, som de gør på Delphi. Selv den ikke-GUI smoke test trækker Interfaces ind, og build-scriptet giver -Fu for lcl\units\x86_64-win64 og lazutils-outputmappen

Hvorfor D2009+ ikke kan fungere som versionsport

Det er fristende at behandle Free Pascal-builden som en moderne compiler og blot definere det nyeste Delphi-featuresymbol. HotPDF gør ikke, og årsagen er værd at sige rent ud: D2009+ betyder ikke kun Unicode-strenge, den gælder også for units, hvis offentlige API er udtrykt med anonymous methods. Free Pascal 3.2.2 understøtter hverken Delphi anonymous methods eller de API'er, så at låne symbolet ville trække kode ind, der ikke kan kompile. Uses-clausen i HPDFDoc bærer derfor to separate betingede haler, og overlapningen mellem dem er bevidst snarere end tilfældig

uses
  // ...
  HPDFJavaScript,
  HPDFFormCalcGraph
{$IFDEF FPC}
  , HPDFFPCCodecStubs,
  HPDFCMS,
  HPDFWinCertSigner
{$ENDIF}
{$IFDEF D2009+}
  , HPDFXFARuntime,
  HPDFCMS,
  HPDFWinCertSigner,
  HPDFSignVerify,
  HPDFSignatureBatch
{$ENDIF};

Hvorfor stopper de native codecs ved linkeren?

Fordi de er Win64 COFF-objekter udsendt af én bestemt toolchain, og ingen af Free Pascal-linkerne på Win64 vil indtage dem: hverken den interne linker eller den eksterne GNU ld-sti. Dette er et objektfil-ABI-problem, ikke et Pascal-problem, og ingen mængde betinget kildekode fikser det. Biblioteket tager den eneste ærlige rute til rådighed. Hvert {$L}-direktiv, der trækker et statisk codec-objekt ind, er pakket ind i {$IFNDEF FPC}, så Free Pascal-builden blot udelader dem, og HPDFFPCCodecStubs leverer derefter hvert manglende eksternt symbol som en stub, der rejser i stedet for at returnere

// 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;

Den stub-tabel er lang, og at læse den fortæller dig præcis, hvilke evner der er Delphi-only i dag: zlib-ng- og zopfli-deflate-indgangspunkterne, libjpeg-komprimering og -dekomprimering, OpenJPEG JPEG 2000-codecet, libtiff og dets per-komprimering-initialiserere, JBIG2 encode og decode, Little-CMS-farvetransformationsindgangspunkterne og AES-primitiverne. Designvalget bag stubberne betyder mere end listen. Et manglende symbol ved link-tid giver dig en væg af undefined references fra en unit, du aldrig rørte; en stub, der rejser ENotSupportedException, giver dig en build, der kører, en besked, der navngiver årsagen, og et stack trace, der peger på kaldstedet. Det betyder også, at en Free Pascal-build aldrig stiltiende producerer forkerte bytes, hvor en Delphi-build ville producere korrekte. Bemærk også andenordenseffekten: kørsel af ikke-betroede image-codecs i en isoleret proces er en beslutning, der kun opstår på Delphi-builden, fordi en Free Pascal-build slet ikke har nogen in-process native decoder at sandboxe

På Delphi linkes HotPDFs statiske codec-objekter ind og kører nativt, mens Free Pascal Win64-builden springer link-direktiverne over og ruter hvert manglende eksternt symbol til en stub, der rejser en navngiven undtagelse ved kaldstedet
At springe link-direktiverne over og stubbe hvert eksternt symbol omdanner en væg af undefined references til en build, der kører og navngiver sine egne grænser

Komprimering: den første linje at ændre er cmNone

Før du porterer noget andet, sæt Compression til cmNone. THPDFCompressionMethod tilbyder præcis to værdier, cmNone og cmFlateDecode, og den anden ruter direkte ind i deflate-indgangspunkterne, der er stubber i en Free Pascal-build. Verificér først kerneobjektmodellen med komprimering slået fra, og beslut derefter, hvad du ellers behøver. Det er den orden, den medfølgende smoke test bruger: opret et en-sides ukomprimeret dokument, genindlæs det og assert, at sideantallet kom tilbage som ét. Ukomprimeret output er større, og det er stadig en helt gyldig 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 rammer et stubbet 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.

Hvad sker der med parallel side-rendering?

Den kompilerer stadig, returnerer stadig korrekte bitmaps og holder op med at være parallel. THotPDF.RenderLoadedPagesParallel og THotPDF.RenderLoadedPagesParallelOrdered er bygget på TThread.CreateAnonymousThread med en inline procedure-closure, som Free Pascal 3.2.2 ikke kan udtrykke, så Free Pascal-grenen kører en deterministisk seriell fallback: den går sideindekserne i orden igennem, kalder RenderLoadedPageToBitmap for hver og tæller succeserne. API-formen, returværdien og output-arrayet er uændret, hvilket er det, der lader en enkelt kodebase bygge begge veje

Samme HotPDF parallel-render-kald kører på overlappende workertråde under Delphi og går sideindekserne serielt igennem under Free Pascal, med pipeline-info-recorden rapporterende et worker-antal på én snarere end at skjule fallbacken
Free Pascal-grenen bevarer API-formen og output-arrayet, mens den rapporterer et worker-antal på ét, så kode, der allerede læser info-recorden, ser sandheden
var
  Bitmaps: THPDFBitmapArray;
  Info: THPDFParallelRenderPipelineInfo;
  Rendered: Integer;
begin
  Rendered := Pdf.RenderLoadedPagesParallel([0, 1, 2, 3], 150, 4,
    Bitmaps, Info);
  // Delphi: Info.WorkerCount er, hvad hukommelsesbudgettet tillod
  // Free Pascal: Info.WorkerCount er altid 1, sider i indeksorden
  if Info.WorkerCount = 1 then
    LogSerialFallback(Rendered, Info.RequestedWorkerCount);

Fallbacken er ikke stille, hvilket er den del, der er værd at designe omkring. Den fylder THPDFParallelRenderPipelineInfo ærligt: PageCount fra anmodningen, RequestedWorkerCount ekkoende hvad du bad om, WorkerCount sat til 1, og de fuldførte og leverede tællere matchende det, der reelt kom tilbage. Kode, der allerede inspicerer Info for at størrelsesbestemme en progressbar eller et hukommelsesbudget, fortsætter med at virke og læser sandheden snarere end en antagelse. Hvis din gennemløbsplan afhænger af parallel-render-pipelinen og dens backpressure-model, er den plan en Delphi-plan; på Free Pascal, budgetter for den enkelttrådede omkostning ved rendering af en side til en bitmap ganget med sideantallet

Hvilken build bør du reelt skibe?

Vælg efter evne, ikke efter præference. Hvis dit workflow er dokumentsamling, tekst- og vektortegning, formularudfyldning, indlæsning og lagring, dækker Free Pascal-builden på Win64 det, og du bør validere med komprimering slået fra, før du slår noget til. Hvis det involverer JPEG-, JPEG 2000-, TIFF- eller JBIG2-billeder, ICC-farvetransformationer, komprimeret output eller gennemløb, der afhænger af mange kerner, bliv på Delphi eller C++Builder indtil videre. Grænsen trækkes af en objektfil-ABI og en manglende sprogfeature, som begge er synlige i kilden snarere end begravet i en supportmatrix, og som begge fejler med en navngiven fejl snarere end et forkert resultat

Free Pascal- og Lazarus-pakken leveres i samme distribution som Delphi- og C++Builder-units, så en licens dækker begge, og du kan teste Lazarus-stien mod dine egne dokumenter, før du binder dig; HotPDF Delphi PDF Component-produktsiden bærer den aktuelle compiler-supportmatrix og den fulde API-reference