Techninis straipsnis

EMF vektorių importas Free Pascal su PDFlibPas

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ą

EMF importo sąlyga perkelta ašimi nuo VCL narystės prie Windows platformos, iškarpos paliktos kitur, o nebūsiančio VCL Windows versijos gavo vektorinį importą
Iškarpos sąlygos perkėlimas prie Windows platformos išlaiko visą esamą versijų elgseną ir nebūsiančio VCL Windows paskirtims nemokamai duoda EMF vektorinį importą

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ą

Win32 deklaracijos, kurių nėra Free Pascal Windows modulyje, surinktos į vieną suderinamumo modulį EMF į PDF vektoriniam konverteriui
Įrašų konstantos, vėliavėlės, sinonimai ir trys nebūstantys įrašų tipai visi gyvena viename suderinamumo modulyje, kurį galima palyginti su antraštės dokumentacija

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

EMF teptuko įrašo baitų išdėstymas, kuriame rodyklės dydžio hatch laukas Win64 vėlesnius laukus paslenka keturiais baitais, priešingai nei fiksuotas 32 bitų išdėstymas
Serijizuotas įrašas visada saugo 4 baitų hatch, todėl rodyklės dydžio vykdymo laiko struktūra Win64 tyloje neteisingai perskaito visus vėlesnius laukus
// 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