Műszaki cikk

HotPDF Free Pascal és Lazarus alatt: Win64 korlátok

A rövid válasz arra a támogatási jegyre igen, korlátokkal. A HotPDF 2.730.0 Free Pascal 3.2.2 és Lazarus 4.6 alatt épül Win64-re, és a létrehozás, betöltés és mentés alapútvonalai működnek. Ami nem következik belőle: minden, ami statikusan linkelt natív kodek objektumon vagy Delphi névtelen metódusokon nyugszik

A kérdés általában ugyanúgy érkezik: egy csapat Lazarusra vált egy keresztplatformos eszköz miatt, vagy Free Pascal kódbázist örököl, és ugyanazt a PDF komponenst akarja, amelyet Delphihez már licencelnek. Egy érett Delphi könyvtár portolása ritkán szintaxis kérdése. Az érdekes rész az, amit a port felfed: hol volt a könyvtár csendesen egyetlen eszközlánchoz kötve, és itt a kötés két nagyon konkrét helyen ül: a csomagolt kodekek objektumfájl-ABI-jában és a verziószimbólum mögé bújt fordítójellemzőkben

Képességmátrix, amely a HotPDF Delphi buildjét veti össze a Free Pascal 3.2.2 és Lazarus 4.6 Win64 buildjével: mely dokumentumútvonalak közösek, és mely kodek, tömörítés, párhuzamos renderelés és névtelen metódus API-k érik el a kivételt dobó stubot
A létrehozás, betöltés és mentés alapútvonalai mindkét builden azonosak, és a szakadék egészen a statikusan linkelt kodekekben és a névtelen metódus API-kban ül

Mit kér a Free Pascal 3.2.2, mielőtt az HPDFDoc lefordul

A HotPDF Free Pascal alatt csak Delphi módban fordul, és csak akkor, ha a Lazarus LCL egységkönyvtárak a keresési útvonalon vannak. Egyik sem alku tárgya. A HotPDF.inc a {$IFDEF FPC} blokkjában {$MODE DELPHI}-re és {$H+}-ra állítja a fordítót, és {$FATAL}-lal utasít el minden régebbit, ha az FPC_FULLVERSION 30202 alatt van, így egy 3.0.x telepítés hangosan bukik meg, nem törött egységet termel. A Lazarus futásidejű csomag, a HotPDFLaz.lpk a többit kódolja: LCL kötelező csomagként és -Mdelphi egyéni opcióként

Az LCL követelmény meglepti azokat, akik csak konzolkimenetet akarnak, de strukturális. A HPDFFPCCompat szolgáltatja azokat a Delphi VCL típusokat, amelyeknek a Free Pascalban nincs megfelelője: a TMetafile-t és a TMetafileCanvas-t LCL bitmap és canvas osztályokra képezi le, a TRichEdit-et TMemo-ra aliasálja, az HPDFDoc pedig a TPNGObject-et a Graphics.TPortableNetworkGraphic-ra aliasálja. Kezelje ezeket fordítási idejű ékeként, nem funkcióparitásként: bitmapra támaszkodó metafájl-osztály fordulni tartja az egységet, de nem teszi a metafájl-útvonalakat Delphi-módra működővé. Még a nem GUI füstteszt is behúzza az Interfaces-t, és a build szkript -Fu-t ad át a lcl\units\x86_64-win64 és a lazutils kimeneti könyvtárra

Miért nem lehet a D2009+ a verziókapu is

Kísértés a Free Pascal buildet modern fordítóként kezelni és egyszerűen a legújabb Delphi funkciószimbólumot definiálni. A HotPDF nem így tesz, és az ok megér egy mondatot: a D2009+ nem egyedül a Unicode sztringeket jelenti, hanem kapuként áll azok előtt az egységek előtt is, amelyek nyilvános API-ját névtelen metódusok fejezik ki. A Free Pascal 3.2.2 sem a Delphi névtelen metódusokat, sem azokat az API-kat nem támogatja, így a szimbólum elkölcsönzése lefordíthatatlan kódot húzna be. Az HPDFDoc uses záradéka ezért két külön feltételes farokvit, és a köztük lévő átfedés szándékos, nem véletlen

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

Miért állnak meg a natív kodekek a linkernél?

Mert egyetlen konkrét eszközlánc által kibocsátott Win64 COFF objektumok, és a Free Pascal egyik Win64 linkere sem fogyasztja őket: sem a belső linker, sem a külső GNU ld útvonal. Ez objektumfájl-ABI probléma, nem Pascal probléma, és kondicionális forrásból mennyiség sem javítja. A könyvtár az egyetlen becsületes utat választja. Minden statikus kodek objektumot behúzó {$L} direktíva {$IFNDEF FPC}-be van csomagolva, így a Free Pascal build egyszerűen kihagyja őket, az HPDFFPCCodecStubs pedig minden hiányzó külső szimbólumot olyan stubként szolgáltat, amely visszatérés helyett kivételt dob

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

A stubtábla hosszú, és elolvasva pontosan megmutatja, mely képességek Delphi-exkluzívak ma: a zlib-ng és zopfli deflate belépőpontok, a libjpeg tömörítés és kicsomagolás, az OpenJPEG JPEG 2000 kodek, a libtiff és a tömörítésenkénti inicializálóik, a JBIG2 kódolás és dekódolás, a Little-CMS színtranszformáció belépőpontjai és az AES primitívek. A stubok mögötti tervezési döntés fontosabb, mint a lista. A linkerben hiányzó szimbólum egy falnyi definiálatlan hivatkozást ad egy egységtől, amelyet sosem érintett; a ENotSupportedException-t dobó stub olyan buildet ad, amely fut, üzenetet ad az okról, és veremkiírást a hívási helyre. Azt is jelenti, hogy a Free Pascal build sosem termel csendesen rossz bájtokat ott, ahol a Delphi build helyeset termelne. Jegyezze meg a másodrendű hatást is: a nem megbízható képkodekek futtatása izolált folyamatban döntés csak a Delphi builden merül fel, mert a Free Pascal buildnek eleve nincs folyamaton belüli natív dekóderje, amit homokozóba lehetne zárni

Delphi alatt a HotPDF statikus kodek objektumai befordulnak és natívan futnak, a Free Pascal Win64 build viszont kihagyja a linkdirektívákat, és minden hiányzó külső szimbólumot olyan stubra terel, amely néven nevezett kivételt dob a hívási helyen
A linkdirektívák kihagyása és minden külső szimbólum stubra cserélése a definiálatlan hivatkozások falát futó buildre váltja, amely megnevezi a saját korlátait

Tömörítés: az első változtatandó sor a cmNone

Mielőtt bármi mást portolna, állítsa a Compression-t cmNone-ra. A THPDFCompressionMethod pontosan két értéket kínál, cmNone és cmFlateDecode, és a második egyenesen a deflate belépőpontokba fut, amelyek stubok a Free Pascal builden. Először a tömörítés kikapcsolásával ellenőrizze az alapobjektummodellt, majd döntse el, mi kell még. Ebben a sorrendben dolgozik a szállított füstteszt: létrehoz egy egyoldalas tömörítetlen dokumentumot, újratölti, és ellenőrzi, hogy az oldalszám eggyel tért vissza. A tömörítetlen kimenet nagyobb, és továbbra is teljesen érvényes 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;   // a cmFlateDecode stubolt szimbólumot ér el
    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.

Mi lesz a párhuzamos oldalrendereléssel?

Továbbra is lefordul, továbbra is helyes bitmapokat ad vissza, és megszűnik párhuzamos lenni. A THotPDF.RenderLoadedPagesParallel és a THotPDF.RenderLoadedPagesParallelOrdered inline procedure zárványos TThread.CreateAnonymousThread-re épül, amelyet a Free Pascal 3.2.2 nem tud kifejezni, így a Free Pascal ág determinisztikus soros tartalékot futtat: sorban járja az oldalindexeket, mindegyikre meghívja a RenderLoadedPageToBitmap-ot, és megszámolja a sikereket. Az API alakja, a visszatérési érték és a kimeneti tömb változatlan, ami lehetővé teszi, hogy egyetlen kódbázis mindkét módon buildeljen

Ugyanaz a HotPDF párhuzamos render hívás átfedő munkaszálakon fut Delphi alatt, és sorban járja az oldalindexeket Free Pascal alatt, miközben a pipeline infórekord eggyel jelentett munkaszámot ad, nem rejti el a tartalékot
A Free Pascal ág megtartja az API alakját és a kimeneti tömböt, eggyel jelentett munkaszámmal, így az infórekordot már olvasó kód az igazságot látja
var
  Bitmaps: THPDFBitmapArray;
  Info: THPDFParallelRenderPipelineInfo;
  Rendered: Integer;
begin
  Rendered := Pdf.RenderLoadedPagesParallel([0, 1, 2, 3], 150, 4,
    Bitmaps, Info);
  // Delphi: az Info.WorkerCount az, amit a memóriakeret engedett
  // Free Pascal: az Info.WorkerCount mindig 1, oldalak indexsorrendben
  if Info.WorkerCount = 1 then
    LogSerialFallback(Rendered, Info.RequestedWorkerCount);

A tartalék nem csendes, és épp ez a megtervezésre érdemes rész. Őszintén tölti a THPDFParallelRenderPipelineInfo-t: PageCount a kérésből, RequestedWorkerCount visszhangzza, amit kért, WorkerCount 1-re állítva, a teljesített és kézbesített számok pedig azt tükrözik, ami tényleg visszaérkezett. Az Info-t már vizsgáló kód, amelyik folyamatjelzőt vagy memóriakeretet méretez, tovább működik, és tényt olvas feltevés helyett. Ha az áteresztőképességi terve a párhuzamos render pipeline-ra és annak visszanyomás-modelljére épül, az a terv Delphi-terv; Free Pascal alatt a oldal bitmapra renderelésének egyszálas költségét szorozza meg az oldalszámmal, és arra költségvetésezz

Melyik buildet érdemes valójában szállítani?

Válasszon képesség szerint, nem preferencia szerint. Ha a munkafolyamata dokumentum-összeállítás, szöveg- és vektorrajz, űrlapkitöltés, betöltés és mentés, a Free Pascal build Win64-en lefedi, és tömörítés kikapcsolásával érdemes validálni, mielőtt bármit bekapcsolna. Ha JPEG vagy JPEG 2000 vagy TIFF vagy JBIG2 képeket, ICC színtranszformációkat, tömörített kimenetet vagy sok magon nyugvó áteresztőképességet érint, egyelőre maradjon Delphi vagy C++Builder mellett. A határt egy objektumfájl-ABI és egy hiányzó nyelvi funkció húzza meg, mindkettő látható a forrásban, nem temetve egy támogatási mátrixba, és mindkettő néven nevezett hibával bukik meg, nem rossz eredménnyel

A Free Pascal és Lazarus csomag ugyanabban a disztribúcióban szállul, mint a Delphi és C++Builder egységek, így egy licenc mindkettőt lefedi, és a Lazarus útvonalat a saját dokumentumain tesztelheti, mielőtt elkövetné maga mellette; a HotPDF Delphi PDF Component terméklapján van a jelenlegi fordítótámogatási mátrix és a teljes API-referencia