Műszaki cikk

EMF vektorimport Free Pascal alatt a PDFlibPas-szal

A PDFlibPas a továbbfejlesztett metafájlokat rekordonként valódi PDF oldaltartalommá alakítja, raszterizálás helyett, és ez az, ami az importált diagramot vagy CAD-rajzot bármilyen nagyításnál élesnek tartja. Az átalakító körülbelül 6500 sor, és a VCL-re íródott, így amikor a könyvtár Free Pascal célt kapott, nem hordozhatónak minősítették, és csonkra cserélték. Az a minősítés téves volt, és az, ahogyan téves volt, hasznos tanulság arról, hogyan kell függőséget auditálni, mielőtt úgy dönt, hogy körülötte ír újra mindent

Annak a 6500 sornak a tényleges VCL-felülete kicsinek bizonyult: egy bittérkép osztály, amelyet a pixelformátuma, a folyam mentése, a handle, a vászon és a pásztázásvonalak miatt használtak; egy metafájl osztály, amelyet a szélessége, a magassága és a handle miatt; valamint a színtípus két konstanssal. Mindegyiket már szolgáltatta a könyvtár saját grafikai egysége, amely pontosan azért létezik, hogy a nem VCL-es buildnek legyenek megfelelői. Az átalakítót egyáltalán nem a VCL blokkolta. A Free Pascal Windows egysége blokkolta

Ossza ketté azon a tengelyen, amelyen a kód ténylegesen függ

Tehát a változtatás nem újraimplementáció volt. Egyetlen feltételes fordítás volt: a „csonk fordítása, ha VCL nélkül épül” helyett „csonk fordítása, ha nem Windowsra épül”. Ez a helyes tengely, és az ok kimondása nyilvánvalóvá teszi a különbséget. Egy továbbfejlesztett metafájl Windows-tároló. Az átalakító felülről lefelé Windows GDI rekordok elemzője. Az, hogy a gazdaalkalmazás a VCL-t használja, más widgetkészletet vagy egyáltalán semmilyet, semmi köze ahhoz, hogy azok a rekordok értelmezhetők-e; az viszont mindent eldönt, hogy a cél Windows-e

A helyes tengely megválasztásának következményei maguktól jönnek. A C++Builder buildek, amelyek ebben a könyvtárban kivenni a Windows platformszimbólumot, megtartják a kivételt dobó csonkot, és pontosan ugyanúgy viselkednek, mint korábban. A macOS megtartja a csonkot, helyesen, mert ott nincsenek feldolgozandó GDI rekordok. A Delphi VCL buildek érintetlenek. És egy Windows build nem VCL-es widgetkészlettel mellékesen megkapja a vektoros EMF importot, amelyet senkinek nem kellett megvalósítania. A valódi függőséggel összhangban lévő feltételes fordítás a platformmunkát egysoros változtatássá teszi; a rossz tengelyhez igazított pedig soha be nem ütemezett átírássá

Az EMF import feltétele a VCL-tagságról a Windows platform tengelyére áll át, máshol megtartja a csonkot, és a nem VCL-es Windows buildek megkapják a vektorimportot
A csonkfeltétel Windows platformra átállítása megtartja minden meglévő build viselkedését, és a nem VCL-es Windows céloknak adja ingyen az EMF vektorimportot

A Free Pascal hiánya deklarációk voltak, nem logika

Ami ténylegesen hiányzott, azok a Win32 deklarációk voltak, amelyeket a Delphi Windows egysége szolgáltat, a Free Pascalé pedig nem. Azok egyetlen kompatibilitási egységbe gyűjtése, a feltételes fordítások szétszórása helyett az átalakítóba, olvashatóvá tartotta az elemzőt. A lista tanulságos, mert megmutatja, milyen egyenetlen a fejléclefedettség a két RTL között: 113 metafájlrekord-típuskonstans, két kibővített szövegkiírási jelző, három gradienskitöltési módkonstans, egy handletábla-mutatótípus, álnévek a gradienscsúcs- és primitívrekordokhoz, valamint három rekordtípus, amelyet a Free Pascal egyáltalán nem deklarál, és amelyek az alfakeveredést, az átlátszó blittelést és a színkezelési módot fedik le

Egyik sem érdekes egyenként. Mindeneknek helyesnek kell lennie, mielőtt az elemző lefordul, és a kompatibilitási egység a természetes otthon, mert egységként differálható a fejlécdokumentációval szemben

Win32 deklarációk, amelyek hiányoznak a Free Pascal Windows egységéből, egyetlen kompatibilitási egységbe gyűjtve az EMF-PDF vektorátalakítóhoz
Rekordkonstansok, jelzők, álnévek és három hiányzó rekordtípus mind egyetlen kompatibilitási egységben laknak, amely a fejlécdokumentációval differálható

Az, amely csendben rossz képet rajzol

E deklarációk közül kettő nem csupán hiányzik: jelen vannak, és erre a célra rosszak, és ez az a rész, amelyet érdemes megjegyezni akkor is, ha soha nem nyúl metafájlhoz

A Free Pascal az ecsetlétrehozási rekordot a futásidejű ecsetstruktúra beágyazásával deklarálja, a kiterjesztett toll rekordját pedig a futásidejű tollstruktúra beágyazásával. Mindkét futásidejű struktúra a hatch tagját mutatóméretű egészként deklarálja, mert egy élő GDI hívásban az a tag handle-t hordozhat. Egy metafájl viszont mindig a 32 bites formát tárolja, mert a rekordelrendezés a szerializált fájlformátum része, és nem változik a folyamat bitességével

32 bites buildeknél a kettő egyetért, és semmi nem történik. Win64-en a mutatóméretű tag nyolc bájt ott, ahol a fájl négyet tartalmaz, így a hatch tag utáni minden mező rossz offsetről olvasódik. Nincs kivétel, nincs elemzési hiba, nincs figyelmeztetés. A metafájl egyszerűen rosszul jelenik meg: színek a rossz bájtokból, tollvastagságok a rossz bájtokból, és egy kép, amely renderelési hibának látszik, nem struktúraelrendezési hibának. A Delphi pontosan ezért szállít mindkét struktúra explicit 32 bites változatát, és a kompatibilitási egység ugyanígy deklarálja újra őket

Az EMF ecsetrekord bájtelrendezése: a mutatóméretű hatch mező négy bájttal eltolja a későbbi mezőket Win64-en a rögzített 32 bites elrendezéshez képest
A szerializált rekord mindig 4 bájtos hatchet tárol, így a mutatóméretű futásidejű struktúra csendben rosszul olvas minden későbbi mezőt Win64-en
// Rossz Win64-en: a Hatch mutatóméretű, a fájl 32 bitet tárol,
// és minden következő mező négy bájttal tolódik, hiba nélkül
type
  TLogBrushRuntime = record
    lbStyle: UINT;
    lbColor: COLORREF;
    lbHatch: ULONG_PTR;      // 8 bájt egy 64 bites folyamatban
  end;

// Helyes: a szerializált elrendezés, rögzített szélesség bitességtől függetlenül
type
  TLogBrush32 = record
    lbStyle: UINT;
    lbColor: COLORREF;
    lbHatch: DWORD;          // mindig 4 bájt, ahogyan a metafájl tárolja
  end;

Az általános szabály: minden olyan struktúra, amely futásidejű API-argumentumként és szerializált mezőelrendezésként is megjelenik, két deklarációt igényel, és a szerializáltnak végig rögzített szélességű típusokat kell használnia. A fájlformátumokban a mutatóméretű tag mindig olyan hiba, amely egy 64 bites buildre vár

A szignatúrakülönbségek burkolóba valók, nem minden hívási helyre

A maradék különbségek közönséges szignatúra-eltérések voltak, és elnyelésük módja egy továbbító burkoló, nem pedig feltételes fordítás minden egyes hívási helyen. A transzformációt kombináló függvény Free Pascal alatt pointereket vár ott, ahol a Delphi referencia-paramétereket, tehát a burkoló referenciákat vesz át, és címeket ad át. Emellett előbb lokálisokba másolja mindkét forrásargumentumot, mert az átalakítónak vannak olyan hívási helyei, ahol a célmátrix egyidejűleg az egyik forrás, és ugyanazon cím kétszeri átadása egy olyan függvénynek, amely olvasás közben ír, olyan transzformációt eredményez, amely finoman rossz, és csak elforgatott tartalmon mutatkozik meg

function CombineTransformCompat(var Dest: TXForm;
  const A, B: TXForm): BOOL;
var
  SrcA, SrcB: TXForm;
begin
  // Előbb másolás: a hívók jogszerűen átadják a Dest-et A-ként vagy B-ként
  SrcA := A;
  SrcB := B;
{$IFDEF FPC}
  Result := Windows.CombineTransform(@Dest, @SrcA, @SrcB);
{$ELSE}
  Result := Windows.CombineTransform(Dest, SrcA, SrcB);
{$ENDIF}
end;

A téglalap- és ponttípusok a másik eset. A Free Pascal a metafájl téglalap- és pontrekordjait a fő grafikaiaktól különböző típusokként kezeli, így nyolc hozzárendelési helynek azonos elrendezésű rekordok között explicit típuskonverzióra volt szüksége. Mindkét fordító elfogadja a konverziós formát, tehát ezek a helyek egyáltalán nem hordoznak feltételes fordítást, ami megér némi csúnyágot

Mit változtat ez egy Free Pascal telepítésben

A vektoros EMF import Windows alatt Free Pascal-lel működik, és ugyanazt az oldaltartalmat állítja elő, mint a Delphi build: útvonalak útvonalként, gradiensek mintázattartalomként, szöveg szövegként. Windowson kívül a raszteres út marad a válasz, és ez a formátum, nem pedig a portolás korlátja. A koordináta- és a kivágási állapot, amelybe az átalakító táplál, a tartalomfolyam CTM- és kivágáskövető cikkében van leírva, és az általa kibocsátott vektorprimitíveket a vektorgrafika, shaderek és gradiensek tárgyalja

Ha saját kódbázisát auditálja ugyanez a lehetőség után, a hasznos gyakorlat az, amellyel ez az egész kezdődött: sorolja fel azokat a tagokat, amelyeket ténylegesen használ abból a keretrendszerből, amelytől úgy gondolja, hogy függ. A válasz gyakran jóval rövidebb, mint amire az importlista utal, és a valódi korlát általában teljesen máshol van. Az eszközkontextuson alapuló importutakat általában a nyomtatási előnézeti és eszközkontextus cikk írja le, a platform- és eszközlánc-lefedettség pedig a losLab PDF Developer Library terméklapon található