PDFlibPas prevádza enhanced metafile na skutočný obsah stránky PDF záznam po zázname, namiesto toho, aby ich rastroval, čo je presne to, čo udržiava importovaný graf alebo CAD kresbu ostrú pri ľubovoľnom priblížení. Ten konvertor má približne 6500 riadkov a bol napísaný proti VCL, takže keď knižnica získala cieľ Free Pascal, bol klasifikovaný ako neprenositeľný a nahradený stubom. Tá klasifikácia bola chybná a spôsob, akým bola chybná, je poučná lekcia o tom, ako audítovať závislosť skôr, než sa rozhodnete prepisovať okolo nej
Skutočný VCL povrch týchto 6500 riadkov sa ukázal byť malý: trieda bitmapy používaná pre svoj pixel formát, ukladanie streamu, handle, canvas a scanlines; trieda metafilu používaná pre svoju šírku, výšku a handle; a typ farby s dvoma konštami. Každú z nich už poskytovala vlastná grafická jednotka knižnice, ktorá existuje presne preto, aby non-VCL build mal ekvivalenty. Konvertor neblokovala VCL vôbec. Blokovala ho jednotka Windows vo Free Pascale
Rozdeľte podľa osi, na ktorej kód skutočne závisí
Zmena teda nebola reimplementáciou. Bola jednou podmienkou: z „kompiluj stub, keď sa zostavuje bez VCL“ na „kompiluj stub, keď sa nezostavuje pre Windows“. Toto je správna os a vysvetlenie prečo robí rozdiel zjavným. Enhanced metafile je Windows kontajner. Konvertor je parser Windows GDI záznamov od začiatku do konca. To, či hostiteľská aplikácia používa VCL, inú sadu widgetov alebo žiadnu, nemá nič spoločné s tým, či sa tie záznamy dajú interpretovať; to, či cieľom je Windows, má so všetkým
Dôsledky správnej voľby osi pripadnú zadarmo. Buildy C++Builder, ktoré v tejto knižnici nedefinujú symbol Windows platformy, ponechajú stub vyhadzujúci výnimku a správajú sa presne ako doteraz. macOS ponechá stub, správne, pretože tam nie sú žiadne GDI záznamy na parsovanie. Delphi VCL buildy zostanú nedotknuté. A Windows build s non-VCL sadou widgetov získa vektorový import EMF ako vedľajší efekt, čo nikto nemusel implementovať. Podmienka zarovnaná so skutočnou závislosťou mení platformovú prácu na jednoradkovú zmenu; podmienka zarovnaná so zlou z nej robí prepis, ktorý sa nikdy nedostane do plánu
Medzera vo Free Pascale boli deklarácie, nie logika
To, čo skutočne chýbalo, boli Win32 deklarácie, ktoré poskytuje jednotka Windows v Delphi a neposkytuje tá vo Free Pascale. Sústrediť ich do jedinej jednotky kompatibility namiesto rozhadzovania podmienok konvertorom udržalo parser čitateľný. Zoznam je poučný, pretože ukazuje, aké nerovnomerné pokrytie hlavičiek je medzi oboma RTL: 113 konšt typov záznamov metafilu, dva príznaky rozšíreného textového výstupu, tri konšty režimu gradient fill, typ ukazovateľa handle tabuľky, aliasy pre záznamy gradient vertex a primitive a tri typy záznamov, ktoré Free Pascal nedeklaruje vôbec, pokrývajúce alpha blending, transparent blitting a režim správy farieb
Žiadna z týchto položiek nie je sama o sebe zaujímavá. Všetko musí byť správne skôr, než parser skompiluje, a jednotka kompatibility je prirodzený domov, pretože sa dá diffovať voči hlavičkovej dokumentácii ako celok
Tá jediná, ktorá potichu nakreslí nesprávny obrázok
Dve z týchto deklarácií nie sú len chýbajúce, sú prítomné a pre tento účel chybné, a toto je časť, ktorú stojí za to zapamätať si, aj keď sa metafilu nikdy nedotknete
Free Pascal deklaruje záznam na vytváranie štetcov so vloženou run-time štruktúrou štetca a záznam rozšíreného pera so vloženou run-time štruktúrou pera. Obe run-time štruktúry deklarujú svoj člen hatch ako celé číslo veľkosti ukazovateľa, pretože v živom GDI volaní môže tento člen niesť handle. Metafil však vždy uchováva 32-bitovú formu, pretože rozloženie záznamu je súčasťou serializovaného formátu súboru a nemení sa s bitovosťou procesu
Na 32-bitových buildoch sa oba zhodujú a nič sa nedeje. Na Win64 má člen veľkosti ukazovateľa osem bajtov tam, kde súbor má štyri, takže každé pole za členom hatch sa číta z nesprávneho offsetu. Žiadna výnimka, žiadna chyba parsovania a žiadne varovanie. Metafil sa len vykreslí nesprávne: farby z nesprávnych bajtov, šírky pera z nesprávnych bajtov a obrázok, ktorý vyzerá ako chyba renderovania, nie ako chyba rozloženia štruktúry. Delphi dodáva explicitne 32-bitové varianty oboch štruktúr presne z tohto dôvodu a jednotka kompatibility ich redeklaruje rovnakým spôsobom
// Na Win64 zle: Hatch má veľkosť ukazovateľa, súbor uchováva 32 bitov
// a každé nasledujúce pole sa posunie o štyri bajty bez chyby
type
TLogBrushRuntime = record
lbStyle: UINT;
lbColor: COLORREF;
lbHatch: ULONG_PTR; // 8 bajtov v 64-bitovom procese
end;
// Správne: serializované rozloženie, pevná šírka bez ohľadu na bitovosť
type
TLogBrush32 = record
lbStyle: UINT;
lbColor: COLORREF;
lbHatch: DWORD; // vždy 4 bajty, ako ich uchováva metafil
end;
Všeobecné pravidlo: každá štruktúra, ktorá sa objavuje ako argument run-time API a zároveň ako serializované rozloženie polí, potrebuje dve deklarácie a tá serializovaná musí používať typy s pevnou šírkou od začiatku do konca. Členy veľkosti ukazovateľa vo formáte súboru sú vždy chyba čakajúca na 64-bitový build
Rozdiely v signatúrach patria do wrapperu, nie na každé miesto volania
Zostávajúce rozdiely boli obyčajné nezrovnalosti signatúr a spôsob, ako ich pohlásiť, je forwardovací wrapper namiesto podmienky na každom mieste volania. Funkcia kombinujúca transformácie berie pod Free Pascalom ukazovatele tam, kde Delphi berie referenčné parametre, takže wrapper berie referencie a odovzdáva adresy. Najprv tiež skopíruje oba zdrojové argumenty do lokálnych premenných, pretože konvertor má miesta volania, kde cieľová matica je zároveň jedným zo zdrojov, a odovzdanie tej istej adresy dvakrát funkcii, ktorá zapisuje počas čítania, produkuje transformáciu jemne chybnú spôsobom, ktorý sa prejaví len na rotovanom obsahu
function CombineTransformCompat(var Dest: TXForm;
const A, B: TXForm): BOOL;
var
SrcA, SrcB: TXForm;
begin
// Najprv kópia: volajúci oprávnene odovzdávajú Dest ako A alebo B
SrcA := A;
SrcB := B;
{$IFDEF FPC}
Result := Windows.CombineTransform(@Dest, @SrcA, @SrcB);
{$ELSE}
Result := Windows.CombineTransform(Dest, SrcA, SrcB);
{$ENDIF}
end;
Typy rectangle a point sú druhý prípad. Free Pascal považuje záznamy rectangle a point metafilu za odlišné typy od všeobecných grafických, takže osem miest priradenia potrebovalo explicitný cast medzi záznamami identického rozloženia. Obe kompilátory akceptujú formu s castom, takže tieto miesta nenesú nijakú podmienku, čo stojí za trochu škaredosti
Čo to mení pre nasadenie vo Free Pascale
Vektorový import EMF funguje na Windows pod Free Pascalom a produkuje rovnaký obsah stránky ako build Delphi: cesty ako cesty, gradienty ako pattern content, text ako text. Mimo Windows zostáva riešením rastrová cesta a to je obmedzenie formátu, nie portu. Súradnicový a clipping stav, do ktorého konvertor zapisuje, popisuje článok o sledovaní CTM a clipovania v content stream a vektorové primitívy, ktoré vysiela, pokrýva vektorová grafika, shadery a gradienty
Ak audítujete vlastnú kódovú bázu za tou istou príležitosťou, užitočné cvičenie je to, ktoré to celé odštartovalo: vypíšte členov, ktoré skutočne používate z frameworku, od ktorého si myslíte, že závisíte. Odpoveď je často oveľa kratšia, než naznačuje zoznam importov, a skutočné obmedzenie býva úplne inde. Cesty importu založené na device context všeobecne popisuje článok o print preview a device context a pokrytie platforiem a toolchainov je uvedené na produktovej stránke losLab PDF Developer Library