PDFlibPas pretvori izboljšane metafile (enhanced metafiles) v pravo vsebino strani PDF zapis po zapisu, namesto da jih rastrira, kar je tisto, kar ohranja uvoženi grafikon ali CAD risanje ostro pri kateri koli povečavi. Ta pretvornik ima približno 6500 vrstic in je bil napisan proti VCL, zato je bil, ko je knjižnica pridobila cilj Free Pascal, razvrščen kot neprenosljiv in nadomeščen s štubom (stub). Ta razvrstitev je bila napačna, in način, kako je bila napačna, je koristna lekcija o tem, kako revizirati odvisnost, preden se odločite za prepis okoli nje
Dejanska površina VCL teh 6500 vrstic se je izkazala za majhno: razred bitne slike (bitmap), uporabljen za svojo slikovno obliko, shranjevanje v tok, ročaj (handle), platno in vrstice prekrivanja (scanlines); razred metafile, uporabljen za svojo širino, višino in ročaj; in barvni tip z dvema konstantama. Vsakega od teh je že zagotavljala lastna grafična enota knjižnice, ki obstaja ravno zato, da ima gradnja brez VCL ustrežnike. Pretvornik sploh ni bil blokiran na VCL. Bil je blokiran na enoti Windows Free Pascala
Ločite po osi, od katere koda dejansko je odvisna
Sprememba torej ni bila ponovna implementacija. Bila je en pogoj: iz »prevedi štub, ko se gradi brez VCL« v »prevedi štub, ko se ne gradi za Windows«. To je prava os, in izjava o tem, zakaj, naredi razliko očitno. Izboljšani metafile je vsebnik Windows. Pretvornik je razčlenjevalnik zapisov Windows GDI od vrha do dna. Ali gostujoča aplikacija uporablja VCL, drug nabor gradnikov (widget set) ali pa sploh nobenega, nima nobene zveze z tem, ali se ti zapisi lahko interpretirajo; ali je cilj Windows, pa ima z tem vse
Posledice izbire prave osi padejo ven zastonj. Gradnje C++Builder, ki oddefinirajo simbol platforme Windows v tej knjižnici, obdržijo metajoči štub in se obnašajo natanko kot prej. macOS obdrži štub, pravilno, ker tam ni zapisov GDI za razčlenjevanje. Gradnje Delphi VCL so nedotaknjene. In gradnja Windows z ne-VCL naborom gradnikov pridobi vektorski uvoz EMF kot stranski učinek, ki ga nihče ni moral implementirati. Pogoj, poravnan z resnično odvisnostjo, spremeni delo na platformi v spremembo ene vrstice; pogoj, poravnan z napačno, pa ga spremeni v prepis, ki se nikoli ne razporedi
Vrzel Free Pascala so bile deklaracije, ne logika
Tisto, kar je dejansko manjkalo, so bile deklaracije Win32, ki jih zagotavlja enota Windows Delphija in jih tista Free Pascal ne. Zbiranje v eno samo združljivostno enoto namesto raztresanja pogojev skozi pretvornik je ohranilo razčlenjevalnik berljiv. Seznam je poučen, ker pokaže, kako neenakomerno je pokritje glav med dvema RTL: 113 konstant tipov zapisov metafile, dve zastavici razširjenega besedilnega izhoda, tri konstante načina polnjenja z gradientom, tip kazalca tabele ročajev, vzdevki za zapise oglišč gradienta in primitivov ter trije tipi zapisov, ki jih Free Pascal sploh ne deklarira, ki pokrivajo mešanje alfa, prosojno blitting in način upravljanja barv
Nič od tega ni zanimivo posamezno. Vse skupaj mora biti pravilno, preden se razčlenjevalnik prevede, združljivostna enota pa je naravni dom, ker jo je mogoče primerjati z dokumentacijo glav kot enoto
Tista, ki tiho nariše napačno sliko
Dve od teh deklaracij nita samo manjkajoči, sta prisotni in napačni za ta namen, in to je del, vreden spomina, tudi če se metafila nikoli ne dotaknete
Free Pascal deklarira zapis ustvarjanja čopiča z vanjo vgrajeno strukturo čopiča ob izvajanju in zapis razširjenega peresa z vanjo vgrajeno strukturo peresa ob izvajanju. Obe strukturi ob izvajanju deklarirata svojega člana hatch kot celo število velikosti kazalca, ker v živem klicu GDI lahko ta član nosi ročaj. Metafile pa vedno shranjuje obliko 32 bitov, ker je razporeditev zapisa del serializirane oblike datoteke in se ne spremeni z bitnostjo procesa
Na gradnjah 32 bitov se ta dve ujemata in nič se ne zgodi. Na Win64 je član velikosti kazalca osem bajtov, kjer ima datoteka štiri, zato je vsako polje za članom hatch brano z napačnega odmika. Ni izjeme, ni napake razčlenjevanja in ni opozorila. Metafile se samo napačno upodobi: barve iz napačnih bajtov, širine peres iz napačnih bajtov in slika, ki izgleda kot hrošč upodabljanja in ne kot hrošč razporeditve struktur. Delphi izkazno dostavi izrecno 32-bitne različice obeh struktur ravno iz tega razloga, združljivostna enota pa jih ponovno deklarira na enak način
// Napačno na Win64: Hatch je velikosti kazalca, datoteka shranjuje
// 32 bitov, in vsako naslednje polje se zamakne za štiri bajte brez napake
type
TLogBrushRuntime = record
lbStyle: UINT;
lbColor: COLORREF;
lbHatch: ULONG_PTR; // 8 bajtov v 64-bitnem procesu
end;
// Pravilno: serializirana razporeditev, fiksna širina ne glede na bitnost
type
TLogBrush32 = record
lbStyle: UINT;
lbColor: COLORREF;
lbHatch: DWORD; // vedno 4 bajti, kot je shranjeno v metafilu
end;
Splošno pravilo: vsaka struktura, ki se pojavlja tako kot argument API-ja ob izvajanju kot razporeditev polj serializacije, potrebuje dve deklaraciji, serializirana pa mora povsod uporabljati tipe fiksne širine. Člani velikosti kazalca v obliki datoteke so vedno hrošč, ki čaka na 64-bitno gradnjo
Razlike podpisov pripadajo v ovijalnik, ne na vsako mesto klica
Preostale razlike so bile običajna neskladja podpisov, način za njihovo absorbiranje pa je posredujoči ovijalnik namesto pogoja na vsakem od mest klica. Funkcija za kombiniranje transformacij vzame kazalce pod Free Pascalom, kjer Delphi vzame referenčne parametre, zato ovijalnik vzame reference in posreduje naslove. Prav tako najprej skopira oba izvorna argumenta v lokalne spremenljivke, ker ima pretvornik mesta klica, kjer je ciljna matrika hkrati eden od virov, in podajanje istega naslova dvakrat funkciji, ki piše, ko bere, proizvede transformacijo, ki je subtilno napačna na način, ki se pokaže samo na zasukani vsebini
function CombineTransformCompat(var Dest: TXForm;
const A, B: TXForm): BOOL;
var
SrcA, SrcB: TXForm;
begin
// Najprej kopiraj: klicatelji upravičeno posredujejo Dest kot A ali B
SrcA := A;
SrcB := B;
{$IFDEF FPC}
Result := Windows.CombineTransform(@Dest, @SrcA, @SrcB);
{$ELSE}
Result := Windows.CombineTransform(Dest, SrcA, SrcB);
{$ENDIF}
end;
Tipa pravokotnika in točke sta drugi primer. Free Pascal obravnava zapise pravokotnika in točke metafila kot ločene tipe od splošnih grafičnih, zato je osem mest dodelitve potrebovalo izrecno pretvorbo (cast) med zapisi enake razporeditve. Oba prevajalnika sprejemata obliko pretvorbe, zato ta mesta sploh ne nosijo pogoja, kar je vredno malce grdobije
Kaj to spremeni za namestitev Free Pascala
Vektorski uvoz EMF deluje na Windows pod Free Pascalom in proizvaja enako vsebino strani kot gradnja Delphi: poti kot poti, gradienti kot vsebina vzorca, besedilo kot besedilo. Zunaj Windows ostane rastrska pot odgovor in to je omejitev oblike, ne prenosa. Stanje koordinat in izrezovanja (clipping), v katerega se pretvornik napaja, je opisano v članku o sledilniku CTM in izrezovanja toka vsebine, vektorski primitivi, ki jih izda, pa so pokriti v vektorski grafiki, senčnikih in gradientih
Če revizirate svojo lastno kodno bazo za isto priložnost, je koristna vaja tista, ki je to začela: naštejte člane, ki jih dejansko uporabljate iz ogrodja, od katerega menite, da ste odvisni. Odgovor je pogosto veliko krajši, kot nakazuje uvozni seznam, prava omejitev pa je običajno povsem drugje. Poti uvoza na osnovi konteksta naprave (device context) na splošno so opisane v članku o predogledu tiskanja in kontekstu naprave, pokritost platform in orodjarn pa je navedena na strani produkta losLab PDF Developer Library