Tekninen artikkeli

HotPDF Free Pascalilla ja Lazarusilla: Win64-tuen rajat

Lyhyt vastaus siihen tukipyyntöön on kyllä, rajoituksin. HotPDF 2.730.0 toimii Free Pascal 3.2.2:lla ja Lazarus 4.6:lla Win64:llä, ja ydinluonti-, lataus- ja tallennuspolut toimivat. Mikä ei kulje mukana, on kaikki, mikä nojaa staattisesti linkitettyyn natiiviin kodekkiobjektiin tai Delphin anonyymeihin metodeihin

Kysymys saapuu yleensä samalla tavalla: tiimi vakioituu Lazaruksiin alustariippumattoman työkalun vuoksi tai perii Free Pascal -koodikannan ja haluaa saman PDF-komponentin, jota se jo lisensoi Delphille. Kypsän Delphi-kirjaston siirtäminen on harvoin syntaksiasia. Kiinnostava osa on se, mitä siirto paljastaa siitä, missä kirjasto oli hiljaa kytketty yhteen työkaluketjuun, ja tässä tapauksessa kytkös istuu kahdessa hyvin tarkassa paikassa: mukana tulevien kodekkien objektitiedosto-ABI:ssa ja versiosymbolin taakse kätkeytyneissä kääntäjäominaisuuksissa

Ominaisuusmatriisi, joka vertaa HotPDF:n Delphi-rakennusta Free Pascal 3.2.2:n ja Lazarus 4.6:n Win64-rakennukseen ja osoittaa, mitkä dokumenttipolut ovat yhteisiä ja mitkä kodekki-, pakkaus-, rinnakkaispiirto- ja anonyymimetodi-API:t päätyvät poikkeuksen nostavaan stubiin
Ydinluonti-, lataus- ja tallennuspolut ovat identtiset kummassakin rakennuksessa, ja ero istuu kokonaan staattisesti linkitetyissä kodekeissa ja anonyymimetodi-API:issa

Mitä Free Pascal 3.2.2 vaatii, ennen kuin HPDFDoc kääntyy

HotPDF kääntyy Free Pascalilla vain Delphi-tilassa, ja vain silloin, kun Lazarusin LCL-yksikköhakemistot ovat hakupolussa. Kumpikaan ei ole neuvoteltavissa. HotPDF.inc vaihtaa kääntäjän tilan {$MODE DELPHI}llä ja {$H+}llä {$IFDEF FPC}-lohkonsa sisällä ja kieltäytyy kaikesta vanhemmasta {$FATAL}illa, kun FPC_FULLVERSION on alle 30202, joten 3.0.x-asennus epäonnistuu äänekkäästi eikä tuota rikkinäistä yksikköä. Lazarusin ajopaketti HotPDFLaz.lpk koodaa loput: LCL vaadittavana pakettina ja -Mdelphi mukautettuna valintana

LCL-vaatimus yllättää ne, jotka haluavat vain konsolitulosteen, mutta se on rakenteellinen. HPDFFPCCompat toimittaa Delphin VCL-tyypit, joilla ei ole Free Pascal -vastinetta, yhdistämällä TMetafilen ja TMetafileCanvasin LCL:n bittikartta- ja piirtoalustaluokkiin ja aliasoimalla TRichEditin TMemoksi, kun taas HPDFDoc aliasoi TPNGObjectin Graphics.TPortableNetworkGraphicksi. Käsittele niitä käännösaikaisina shimmeinä, ei ominaisuuspariteettina: bittikarttaan tukeutuva metafile-luokka pitää yksikön kääntyvänä, se ei tee metafile-poluista Delphin kaltaisia. Jo ei-GUI-smoketestaus vetää Interfacesin mukaan, ja koontiskripti antaa -Fulle arvot lcl\units\x86_64-win64 ja lazutils-tulostehakemisto

Miksi D2009+ ei voi toimia myös versioporttina

On houkuttelevaa käsitellä Free Pascal -rakennusta nykyaikaisena kääntäjänä ja yksinkertaisesti määritellä uusin Delphi-ominaisuussymboli. HotPDF ei tee niin, ja syy kannattaa sanoa suoraan: D2009+ ei tarkoita pelkkää Unicode-merkkijonoja, se portittaa myös yksiköt, joiden julkinen API on ilmaistu anonyymeillä metodeilla. Free Pascal 3.2.2 ei tue Delphin anonyymejä metodeja eikä niitä API:ja, joten symbolin lainaaminen raahaisi mukaan koodia, joka ei voi kääntyä. HPDFDocin uses-lause kantaa siksi kahta erillistä ehdollista häntää, ja niiden välinen päällekkäisyys on tahallista, ei sattumaa

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

Miksi natiivit kodekit pysähtyvät linkittimeen?

Koska ne ovat yhden tietyn työkaluketjun emittoimia Win64-COFF-objekteja, eikä kumpikaan Free Pascal -linkittäjä kuluta niitä Win64:llä: ei sisäinen linkittäjä, ei ulkoinen GNU ld -polku. Tämä on objektitiedoston ABI-ongelma, ei Pascal-ongelma, eikä mikään määrä ehdollista lähdekoodia korjaa sitä. Kirjasto ottaa ainoan rehellisen käytettävissä olevan reitin. Jokainen staattisen kodekkiobjektin mukaan vetävä {$L}-direktiivi on kääritty {$IFNDEF FPC}iin, joten Free Pascal -rakennus jättää ne yksinkertaisesti pois, ja HPDFFPCCodecStubs toimittaa sitten jokaisen puuttuvan ulkoisen symbolin stubina, joka nostaa poikkeuksen palauttamisen sijaan

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

Tuo stubitaulukko on pitkä, ja sen lukeminen kertoo tarkalleen, mitkä ominaisuudet ovat tänään vain Delphille: zlib-ng:n ja zopflin deflate-aloituspisteet, libjpegin pakkaus ja purku, OpenJPEG:n JPEG 2000 -kodekki, libtiff ja sen pakkauskohtaiset alustajat, JBIG2-koodaus ja -purku, Little-CMS:n värimuunnoksen aloituspisteet ja AES-primitiivit. Stubien takana oleva suunnitteluratkaisu merkitsee enemmän kuin lista. Puuttuva symboli linkityshetkellä antaa sinulle seinän täynnä määrittelemättömiä viittauksia yksiköstä, jota et koskaan koskenut; poikkeuksen nostava ENotSupportedException-stub antaa sinulle rakennuksen, joka toimii, viestin, joka nimeää syyn, ja pinolistausen, joka osoittaa kutsupaikkaan. Se tarkoittaa myös, ettei Free Pascal -rakennus koskaan tuota hiljaa vääriä tavuja, joissa Delphi-rakennus tuottaisi oikeat. Huomaa myös toisen asteen vaikutus: epäluotettavien kuvakodekkien ajaminen eristetyssä prosessissa on päätös, joka herää vain Delphi-rakennuksessa, koska Free Pascal -rakennuksella ei ole prosessin sisäistä natiivipurkajaa, joka voitaisiin eristää hiekkalaatikkoon

Delphissä HotPDF:n staattiset kodekkiobjektit linkittyvät mukaan ja toimivat natiivisti, kun taas Free Pascalin Win64-rakennus ohittaa linkitysdirektiivit ja ohjaa jokaisen puuttuvan ulkoisen symbolin stubille, joka nostaa nimetyn poikkeuksen kutsupaikassa
Linkitysdirektiivien ohittaminen ja jokaisen ulkoisen symbolin stubaaminen muuttaa määrittelemättömien viittausten seinän rakennukseksi, joka toimii ja nimeää omat rajansa

Pakkaus: ensimmäinen muutettava rivi on cmNone

Ennen kuin siirrät mitään muuta, aseta Compression arvoon cmNone. THPDFCompressionMethod tarjoaa täsmälleen kaksi arvoa, cmNone ja cmFlateDecode, ja jälkimmäinen ohjautuu suoraan niihin deflate-aloituspisteisiin, jotka ovat stubit Free Pascal -rakennuksessa. Varmista ydinobjektimalli ensin pakkauksen ollessa pois päältä ja päätä sitten, mitä muuta tarvitset. Se on järjestys, jota mukana toimitettu smoketestaus noudattaa: luo yhden sivun pakkaamaton dokumentti, lataa se uudelleen ja varmista, että sivumäärä palasi ykkösenä. Pakkaamaton tuloste on suurempi, ja se on silti täysin kelvollinen 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 päätyy stubattuun symboliin
    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.

Mitä tapahtuu rinnakkaiselle sivunpiirrolle?

Se kääntyy yhä, palauttaa yhä oikeita bittikarttoja ja lakkaa olemasta rinnakkainen. THotPDF.RenderLoadedPagesParallel ja THotPDF.RenderLoadedPagesParallelOrdered rakentuvat TThread.CreateAnonymousThreadin päälle sisäänrakennetulla procedure-sulkeumalla, jota Free Pascal 3.2.2 ei osaa ilmaista, joten Free Pascal -haara ajaa deterministisen sarjallisen varatoiminnon: se kulkee sivuindeksit järjestyksessä, kutsuu RenderLoadedPageToBitmapia jokaiselle ja laskee onnistumiset. API:n muoto, paluuarvo ja tulostetaulukko ovat muuttumattomia, mikä mahdollistaa saman koodikannan rakentamisen molemmilla tavoilla

Sama HotPDF-rinnakkaispiirtokutsu toimii limittyvillä työsäikeillä Delphissä ja kulkee sivuindeksit sarjallisesti Free Pascalilla, ja putken tietotietue ilmoittaa työsäikeiden määräksi yhden sen sijaan, että kätkisi varatoiminnon
Free Pascal -haara pitää API:n muodon ja tulostetaulukon ilmoittaen työsäikeiden määräksi yhden, joten koodi, joka jo lukee tietotietueen, näkee totuuden
var
  Bitmaps: THPDFBitmapArray;
  Info: THPDFParallelRenderPipelineInfo;
  Rendered: Integer;
begin
  Rendered := Pdf.RenderLoadedPagesParallel([0, 1, 2, 3], 150, 4,
    Bitmaps, Info);
  // Delphi: Info.WorkerCount on se, minkä muistibudjetti salli
  // Free Pascal: Info.WorkerCount on aina 1, sivut indeksijärjestyksessä
  if Info.WorkerCount = 1 then
    LogSerialFallback(Rendered, Info.RequestedWorkerCount);

Varatoiminto ei ole hiljainen, ja juuri se on osa, joka kannattaa ottaa suunnittelun lähtökohdaksi. Se täyttää THPDFParallelRenderPipelineInfon rehellisesti: PageCountin pyynnöstä, RequestedWorkerCountin kaikuna siitä, mitä pyysit, WorkerCountin arvolla 1 sekä valmistuneiden ja toimitettujen lukumäärät täsmäämässä sen kanssa, mikä oikeasti palasi. Koodi, joka jo tarkastelee Infoa mitoittaakseen edistymispalkin tai muistibudjetin, jatkaa toimimistaan ja lukee totuuden oletuksen sijaan. Jos läpivirtaussuunnitelmasi nojaa rinnakkaispiirtoputkeen ja sen takapainemalliin, kyseinen suunnitelma on Delphi-suunnitelma; Free Pascalilla budjetoi sivun piirtämisen bittikartaksi yksisäikeisenä kustannuksena kerrottuna sivumäärällä

Mitä rakennusta sinun oikeasti kannattaa toimittaa?

Valitse ominaisuuksien, eivät mieltymysten, perusteella. Jos työnkulkusi on dokumenttien kokoamista, tekstin ja vektorien piirtämistä, lomakkeiden täyttöä, lataamista ja tallentamista, Free Pascal -rakennus Win64:llä kattaa sen, ja sinun kannattaa validoida pakkaus pois päältä ennen kuin kytket mitään päälle. Jos siihen kuuluu JPEG-, JPEG 2000-, TIFF- tai JBIG2-kuvia, ICC-värimuunnoksia, pakattua tulostetta tai useisiin ytimiin nojaavaa läpivirtausta, pysy toistaiseksi Delphissä tai C++Builderissa. Rajan vetää objektitiedoston ABI ja puuttuva kielen ominaisuus, molemmat näkyvissä lähdekoodissa sen sijaan, että ne olisi haudattu tukimatriisiin, ja molemmat epäonnistuvat nimetyllä virheellä väärän tuloksen sijaan

Free Pascal- ja Lazarus-paketti toimitetaan samassa jakelussa kuin Delphi- ja C++Builder-yksiköt, joten lisenssi kattaa molemmat ja voit testata Lazarus-polun omilla dokumenteillasi ennen kuin sitoudut siihen; tuotesivu HotPDF Delphi PDF Component kantaa ajantasaisen kääntäjätukimatriisin ja täyden API-referenssin