Tehnični članak

HotPDF na Free Pascal in Lazarus: meje podpore Win64

Kratek odgovor na tisto prijavo podpore je da, a z mejami. HotPDF 2.730.0 deluje na Free Pascal 3.2.2 in Lazarus 4.6 za Win64, osnovne poti ustvarjanja, nalaganja in shranjevanja pa delujejo. Kar ne sledi, je vse, kar počiva na statično povezani izvorni predmetni datoteki kodeka ali na anonimnih metodah Delphi

Vprašanje običajno pride enako: ekipa standardizira na Lazarus za večplatformsko orodje ali podeduje izvorno kodo Free Pascal in želi isto komponento PDF, za katero že ima licenco za Delphi. Prestavljanje zrele knjižnice Delphi je redko vprašanje sintakse. Zanimiv del je, česa prestavitev pokaže o tem, kje se je knjižnica tiho povezala z enim naborom orodij; v tem primeru povezanost leži na dveh zelo konkretnih mestih: ABI predmetnih datotek priloženih kodekov in zmožnosti prevajalnika, skrite za simbolom različice

Matrika zmožnosti, ki primerja izgradnjo HotPDF za Delphi z izgradnjo za Free Pascal 3.2.2 in Lazarus 4.6 Win64 in pokazuje, katere dokumentne poti so skupne ter kateri API-ji kodekov, stiskanja, vzporednega izrisa in anonimnih metod dosežejo odlomek, ki sproži izjemo
Osnovne poti ustvarjanja, nalaganja in shranjevanja so v obeh izgradnjah enake, vrzel pa leži v celoti v statično povezanih kodekih in API-jih anonimnih metod

Kaj Free Pascal 3.2.2 potrebuje, preden se HPDFDoc prevede

HotPDF se pod Free Pascal prevede le v načinu Delphi in le, ko so imeniki enot LCL Lazarusa na iskalni poti. Nič od tega ni pogajljivo. HotPDF.inc preklopi prevajalnik s {$MODE DELPHI} in {$H+} znotraj svojega bloka {$IFDEF FPC} in s {$FATAL} zavrne vse starejše, ko je FPC_FULLVERSION pod 30202, zato namestitev 3.0.x glasno odpove, namesto da bi naredila pokvarjeno enoto. Paket izvajalnega okolja Lazarus HotPDFLaz.lpk zapiše preostanek: LCL kot zahtevan paket in -Mdelphi kot možnost po meri

Zahteva po LCL preseneti tiste, ki želijo le izpis v konzolo, a je strukturna. HPDFFPCCompat dobavlja tipe VCL iz Delphija, za katere Free Pascal nima ustreznika: preslika TMetafile in TMetafileCanvas na bitne slike in razrede platna LCL ter TRichEdit na TMemo, HPDFDoc pa preslika TPNGObject na Graphics.TPortableNetworkGraphic. Obravnavajte jih kot opore ob prevajanju in ne kot pariteto zmožnosti: razred metafile, ki počiva na bitni sliki, ohranja enoto v prevajanju, ne poskrbi pa, da bi se poti metafile obnašale kot na Delphiju. Že tudi dimni test brez GUI vključi Interfaces, gradbeni skript pa podaja -Fu za lcl\units\x86_64-win64 in izhodni imenik lazutils

Zakaj D2009+ ne more služiti kot vrata različice

Mamljivo je obravnavati izgradnjo Free Pascal kot sodobnega prevajalnika in preprosto definirati najnovejši simbol zmožnosti Delphi. HotPDF tega ne stori, razlog pa je vredna jasnega izreka: D2009+ ne pomeni samo nizov Unicode, ampak tudi vrata enot, katerih javni API je izražen z anonimnimi metodami. Free Pascal 3.2.2 ne podpira niti anonimnih metod Delphi niti teh API-jev, izposoja si simbola pa bi povlekla kodo, ki se ne more prevesti. Stavek uses v HPDFDoc zato nosi dva ločena pogojna repa, prekrivanje med njima pa je namerno in ne naključno

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

Zakaj se izvorni kodeki ustavijo pri povezovalniku?

Ker so predmeti COFF za Win64, izdani s strani enega konkretnega nabora orodij, in jih noben povezovalnik Free Pascal na Win64 ne bo sprejel: ne notranji povezovalnik, ne zunanja pot GNU ld. To je težava ABI predmetnih datotek in ne težava Pascala, pogojna izvorna koda je ne popravi v nobeni količini. Knjižnica vzame edino pošteno pot, ki je na voljo. Vsako direktivo {$L}, ki vleče statično predmetno datoteko kodeka, ovije {$IFNDEF FPC}, zato jih izgradnja Free Pascal preprosto izpusti, HPDFFPCCodecStubs pa nato vsak manjkajoči zunanji simbol dobavi kot odlomek, ki sproži izjemo, namesto da bi vračal

// 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 tabela odlomkov je dolga, in branje pove točno, katere zmožnosti so danes le za Delphi: vstopne točke deflate zlib-ng in zopfli, stiskanje in razstiskanje libjpeg, kodek OpenJPEG JPEG 2000, libtiff in njegovi inicializatorji po stiskanju, kodiranje in dekodiranje JBIG2, vstopne točke barvnih pretvorb Little-CMS in primitivi AES. Oblikovna odločitev za odlomki je pomembnejša od seznama. Manjkajoč simbol ob povezovanju vam da steno nedefiniranih referenc iz enote, ki se je niste nikoli dotaknili; odlomek, ki sproži ENotSupportedException, pa vam da izgradnjo, ki teče, sporočilo z imenom razloga in sled sklada, ki kaže na mesto klica. Pomeni tudi, da izgradnja Free Pascal nikoli tiho ne naredi napačnih bajtov tam, kjer bi jih izgradnja Delphi naredila prave. Upoštevajte še učinek drugega reda: zaganjanje kodekov nezaupanih slik v izoliranem procesu je odločitev, ki se pojavi le pri izgradnji Delphi, saj izgradnja Free Pascal sploh nima izvornega dekodirnika v procesu, ki bi ga dala v peskovnik

Pod Delphijem se statične predmetne datoteke kodekov HotPDF povežejo in tečejo izvorno, izgradnja Free Pascal Win64 pa preskoči direktive povezovanja in vsak manjkajoči zunanji simbol usmeri v odlomek, ki na mestu klica sproži poimenovano izjemo
Preskok direktiv povezovanja in odlomek za vsak zunanji simbol spremenita steno nedefiniranih referenc v izgradnjo, ki teče in poimenuje svoje meje

Stiskanje: prva vrstica za spremembo je cmNone

Preden prestavite karkoli drugega, nastavite Compression na cmNone. THPDFCompressionMethod ponuja točno dve vrednosti, cmNone in cmFlateDecode, druga pa vodi naravnost v vstopne točke deflate, ki so v izgradnji Free Pascal odlomki. Najprej preverite osnovni predmetni model s stiskanjem izklopljenim, nato pa se odločite, kaj še potrebujete. To je vrstni red, ki ga uporablja priloženi dimni test: ustvari enostransko nestisnjen dokument, ga znova naloži in trdi, da se je število strani vrnilo kot ena. Nestisnjen izhod je večji in je še vedno popolnoma veljaven 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 doseže odlomljen simbol
    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.

Kaj se zgodi z vzporednim izrisom strani?

Še vedno se prevede, še vedno vrača prave bitne slike in preneha biti vzporeden. THotPDF.RenderLoadedPagesParallel in THotPDF.RenderLoadedPagesParallelOrdered sta zgrajena na TThread.CreateAnonymousThread z vgrajeno zaprtjo procedure, ki je Free Pascal 3.2.2 ne zna izraziti, zato veja Free Pascal teče po deterministični serijski rezervi: indeksi strani gredo po vrsti, za vsakega se pokliče RenderLoadedPageToBitmap, uspehi pa se preštejejo. Oblika API-ja, vračilna vrednost in izhodna tabela ostanejo nespremenjeni, kar je tisto, ki eni sami izvorni kodi dovoli gradnjo v obeh smereh

Isti klic vzporednega izrisa HotPDF teče na prekrivajočih delovnih nitih pod Delphijem in pod Free Pascal serijsko obhodi indeksi strani, zapis informacij o cevovodu pa poroča število delavcev ena, namesto da bi skrival rezervo
Veja Free Pascal ohrani obliko API-ja in izhodno tabelo, poroča pa število delavcev ena, tako da koda, ki že bere zapis informacij, vidi resnico
var
  Bitmaps: THPDFBitmapArray;
  Info: THPDFParallelRenderPipelineInfo;
  Rendered: Integer;
begin
  Rendered := Pdf.RenderLoadedPagesParallel([0, 1, 2, 3], 150, 4,
    Bitmaps, Info);
  // Delphi: Info.WorkerCount je, kar je dovolil proračun pomnilnika
  // Free Pascal: Info.WorkerCount je vedno 1, strani v vrstnem redu indeksov
  if Info.WorkerCount = 1 then
    LogSerialFallback(Rendered, Info.RequestedWorkerCount);

Rezerva ni tiha, kar je del, vreden oblikovanja okoli njega. THPDFParallelRenderPipelineInfo zapolni pošteno: PageCount iz zahteve, RequestedWorkerCount kot odmev tega, kar ste zahtevali, WorkerCount nastavljen na 1 ter števca dokončanih in dostavljenih v skladu s tem, kar je res prišlo nazaj. Koda, ki že pregleduje Info, da dimenzionira vrstico napredka ali proračun pomnilnika, deluje naprej in bere resnico in ne predpostavke. Če vaš načrt prepustnosti sloni na cevovodu vzporednega izrisa in njegovem modelu povratnega tlaka, je ta načrt načrt za Delphi; pod Free Pascal proračunajte enonitni strošek izrisa strani v bitno sliko, pomnožen s številom strani

Katero izgradnjo naj sploh dobavite?

Izberite po zmožnostih in ne po preferenci. Če vaš potek dela je sestavljanje dokumentov, risanje besedila in vektorjev, polnjenje obrazcev, nalaganje in shranjevanje, to pokrije izgradnja Free Pascal na Win64, in preveriti bi jo bilo s stiskanjem izklopljenim, preden karkoli vklopite. Če gre za slike JPEG ali JPEG 2000 ali TIFF ali JBIG2, barvne pretvorbe ICC, stisnjen izhod ali prepustnost, odvisno od mnogih jeder, za zdaj ostanite pri Delphi ali C++Builder. Mejo potegneta ABI predmetnih datotek in manjkajoča jezikovna zmožnost; obe sta vidni v izvorni kodi in ne zakopani v matriki podpore, obe pa odpoveta s poimenovano napako in ne z napačnim rezultatom

Paket Free Pascal in Lazarus prihaja v isti distribuciji kot enote Delphi in C++Builder, zato licenca krije oboje in lahko pot Lazarus preizkusite na svojih dokumentih, preden se zavežete; stran izdelka HotPDF Delphi PDF Component nosi trenutno matriko podpore prevajalnikov in celotno referenco API