PDFlibPas patobulintuosius metafailus (enhanced metafile) paverčia tikru PDF puslapio turiniu įrašas po įrašo, o ne jų rastravimu — būtent todėl importuota diagrama ar CAD brėžinys bet kuriame mastelyje lieka aštrus. Tas konverteris yra apie 6500 eilučių ir buvo rašytas prieš VCL, todėl kai biblioteka įgijo Free Pascal paskirtį, jis buvo priskirtas prie neperkeliamų ir pakeistas tuščia iškarpa (stub). Tas priskyrimas buvo klaidingas, o būdas, kuriuo jis buvo klaidingas, yra naudinga pamoka apie tai, kaip audituoti priklausomybę prieš apsispręsdavimą rašyti aplink ją
Tų 6500 eilučių faktinis VCL paviršius pasirodė esąs mažas: taškinės grafikos (bitmap) klasė, naudota dėl pikselių formato, srauto įrašymo, rankenėlės (handle), drobės ir eilučių nuskaitymo; metafailo klasė, naudota dėl pločio, aukščio ir rankenėlės; ir spalvos tipas su dviem konstantomis. Kiekvieną iš jų jau teikė pačios bibliotekos grafikos modulis, kuris egzistuoja būtent tam, kad nebūsiantis VCL variantas turėtų atitikmenis. Konverteris apskritai nebuvo užblokuotas dėl VCL. Jis buvo užblokuotas dėl Free Pascal Windows modulio
Skaidykite ašimi, kurios kodas iš tikrųjų laikosi
Tai pokytis nebuvo nauja įgyvendinimas. Tai buvo viena sąlyga: iš „kompiliuoti iškarpą, kai statoma be VCL“ į „kompiliuoti iškarpą, kai nestatoma Windows aplinkai“. Tai teisinga ašis, o pasakius priežastį, skirtumas tampa akivaizdus. Patobulintasis metafailas yra Windows konteineris. Konverteris yra Windows GDI įrašų analizatorius nuo pradžios iki galo. Ar pagrindinė programa naudoja VCL, kitą valdiklių rinkinį ar apskritai jokio valdiklių rinkinio, neturi nieko bendro su tuo, ar tuos įrašus galima interpretuoti; o ar paskirtis yra Windows, turi su tuo viską
Teisingos ašies pasekmės atkrenta pačios. C++Builder versijos, kuriose šioje bibliotekoje nepaisyta Windows platformos simbolio, lieka su išimties metančia iškarpa ir elgiasi lygiai kaip anksčiau. macOS lieka su iškarpa, teisingai, nes ten nėra GDI įrašų, kuriuos analizuoti. Delphi VCL versijos nepaliestos. O Windows versija su nebūsiančiu VCL valdiklių rinkiniu kaip šalutinis poveikis gauna vektorinį EMF importą, kurio niekam nereikėjo įgyvendinti. Su tikra priklausomybe sutampanti sąlyga platformos darbą paverčia vienos eilutės pakeitimu; su neteisinga sutampanti — perrašymu, kuris niekada nepatenka į planą
Free Pascal spraga buvo deklaracijos, o ne logika
Ko iš tikrųjų trūko, buvo Win32 deklaracijos, kurias teikia Delphi Windows modulis ir kurių neteikia Free Pascal modulis. Jų surinkimas į vieną suderinamumo modulį, o ne sąlygų išbarstymas po konverterį, paliko analizatorių skaitomą. Sąrašas yra instruktyvus, nes parodo, kiek nevienodai dvi RTL aplinkos aprašo antraštes: 113 metafailo įrašų tipų konstantų, dvi išplėstinės teksto išvesties vėliavėlės, trys gradientinio užpildymo režimo konstantos, rankenėlių lentelės rodyklės tipas, gradientinių viršūnių ir pirminių įrašų sinonimai bei trys įrašų tipai, kurių Free Pascal apskritai nedeklaruoja, dengiantys alfa maišymą, skaidrųjį blit ir spalvų valdymo režimą
Nė viena iš tų detalių atskirai neįdomi. Visos jos turi būti teisingos, kad analizatorius sukompiliuotų, o suderinamumo modulis yra natūralios jos namai, nes galima palyginti su antraštės dokumentacija kaip vieną visumą
Ta, kuri tyloje nupiešia neteisingą paveikslą
Dvi iš tų deklaracijų ne tik trūksta — jos yra, bet šiam tikslui neteisingos, ir tai dalis, verta atminties, net jeigu su metafailu niekada nesusiliesite
Free Pascal deklaruoja teptuko kūrimo įrašą su jam įmontuota vykdymo laiko teptuko struktūra, o išplėstinio pieštuko įrašą — su įmontuota vykdymo laiko pieštuko struktūra. Abi tos vykdymo laiko struktūros savo hatch narį deklaruoja kaip rodyklės dydžio sveikąjį skaičių, nes gyvame GDI iškvietime tas narys gali nešti rankenėlę. Metafailas vis dėlto visada saugo 32 bitų formą, nes įrašo išdėstymas yra serijizuoto failo formato dalis ir nesikeičia pagal proceso bitumą
32 bitų versijose abi sutampa ir nieko nevyksta. Win64 rodyklės dydžio narys yra aštuoni baitai ten, kur faile yra keturi, todėl kiekvienas laukas po hatch nario skaitomas iš neteisingo poslinkio. Jokios išimties, jokios analizės klaidos ir jokio įspėjimo. Metafailas tiesiog atvaizduojamas neteisingai: spalvos iš neteisingų baitų, pieštukų storiai iš neteisingų baitų, o paveikslas atrodo kaip atvaizdavimo, o ne struktūros išdėstymo klaida. Delphi būtent dėl šios priežasties tiekia aiškiai 32 bitų abiejų struktūrų variantus, ir suderinamumo modulis juos padeklaruoja taip pat
// Netinka Win64: Hatch yra rodyklės dydžio, faile saugoma 32 bitai,
// ir kiekvienas tolesnis laukas pasislenka keturiais baitais be jokios klaidos
type
TLogBrushRuntime = record
lbStyle: UINT;
lbColor: COLORREF;
lbHatch: ULONG_PTR; // 8 baitai 64 bitų procese
end;
// Teisingai: serijizuotas išdėstymas, fiksuotas plotis nepriklausomai nuo bitumo
type
TLogBrush32 = record
lbStyle: UINT;
lbColor: COLORREF;
lbHatch: DWORD; // visada 4 baitai, kaip saugoma metafaile
end;
Bendroji taisyklė: kiekviena struktūra, kuri pasirodo ir kaip vykdymo laiko API argumentas, ir kaip serijizuoto laukų išdėstymo forma, reikalauja dviejų deklaracijų, o serijizuotoji turi naudoti fiksuoto pločio tipus nuo galo iki galo. Rodyklės dydžio nariai failo formate yra visada klaida, laukianti 64 bitų versijos
Parašo skirtumai priklauso suderinamumo įvyniojimui, o ne kiekvienai iškvietimo vietai
Likę skirtumai buvo įprasti parašo nesutapimai, ir būdas juos sugerti yra persiuntimo įvyniojimas, o ne sąlyga prie kiekvienos iškvietimo vietos. Transformacijų jungimo funkcija Free Pascal aplinkoje priima rodykles ten, kur Delphi priima nuorodinius parametrus, todėl įvyniojimas priima nuorodas ir perduoda adresus. Jis taip pat pirmiausia nukopijuoja abu šaltinius į vietinius kintamuosius, nes konverteris turi iškvietimo vietų, kur paskirties matrica kartu yra vienas iš šaltinių, o to paties adreso perdavimas du kartus funkcijai, kuri rašo skaitydama, duoda transformaciją, kuri subtiliai neteisinga ir to nepastebima, kol neatvaizduojamas pasuktas turinys
function CombineTransformCompat(var Dest: TXForm;
const A, B: TXForm): BOOL;
var
SrcA, SrcB: TXForm;
begin
// Pirma kopijuoti: iškviečiantieji teisėtai perduoda Dest kaip A arba B
SrcA := A;
SrcB := B;
{$IFDEF FPC}
Result := Windows.CombineTransform(@Dest, @SrcA, @SrcB);
{$ELSE}
Result := Windows.CombineTransform(Dest, SrcA, SrcB);
{$ENDIF}
end;
Stačiakampio ir taško tipai yra kitas atvejis. Free Pascal metafailo stačiakampio ir taško įrašus traktuoja kaip atskirus tipus, skirtingus nuo bendrųjų grafikos tipų, todėl aštuonios priskyrimo vietos reikalavo aiškaus atvedimo tarp identišką išdėstymą turinčių įrašų. Abi kompiliatoriaus versijos priima tokią atvedimo formą, todėl tos vietos neša jokios sąlygos — verta šiek tiek negražumo
Ką tai keičia Free Pascal diegimui
Vektorinis EMF importas veikia Windows aplinkoje su Free Pascal ir duoda tą patį puslapio turinį kaip Delphi versija: kreives kaip kreives, gradientus kaip rašto turinį, tekstą kaip tekstą. Už Windows ribų sprendimas lieka rastrinis kelias, ir tai formato, o ne perkeliamumo, ribojimas. Koordinates ir iškarpos būseną, į kurią maitinasi konverteris, aprašo turinio srauto CTM ir iškarpos būsenos sekimo straipsnis, o jo skleidžiami vektoriniai pirminiai elementai aptarti vektorinėje grafikoje, šešėliuose (shaders) ir gradientuose
Jei audituojate savo paties kodų bazę dėl tokios pat galimybės, naudingas pratimas yra tas, nuo kurio viskas prasidėjo: išsirašykite narį, kuriuos iš tikrųjų naudojate iš sistemos, kurią, jūsų manymu, priklauso. Atsakymas dažnai gerokai trumpesnis, nei rodo importo sąrašas, o tikrasis apribojimas dažniausiai būna visai kitoje vietoje. Įrenginio kontekstu paremti importo keliai apskritai aprašyti spaudinio peržiūros ir įrenginio konteksto straipsnyje, o platformų ir įrankių grandinės dengimas išvardytas losLab PDF Developer Library produkto puslapyje