Tehnički članak

EMF vektorski uvoz na Free Pascalu s PDFlibPasom

PDFlibPas pretvara enhanced metafile u stvarni sadržaj PDF stranice zapis po zapis, umjesto da ih pretvara u rastrovu sliku, što je upravo ono što uvezeni grafikon ili CAD crtež drži oštrim pri bilo kojem zumiranju. Taj pretvarač ima oko 6500 redaka i pisan je u odnosu na VCL, pa je kada je knjižnica dobila Free Pascal cilj klasificiran kao neprenosiv i začepčen. Ta je klasifikacija bila pogrešna, a način na koji je bila pogrešna korisna je lekcija o tome kako pregledati ovisnost prije nego što odlučite pisati oko nje iznova

Stvarna VCL površina tih 6500 redaka pokazala se malom: bitmap klasa korištena zbog svog pikselnog formata, spremanja u tok, handlea, canvasa i scanlineova; metafile klasa korištena zbog svoje širine, visine i handlea; te tip boje s dvije konstante. Svaki od njih već je pružala vlastita grafička jedinica knjižnice, koja postoji upravo zato da ne-VCL izgradnja ima ekvivalente. Pretvarač uopće nije bio blokiran na VCL-u. Bio je blokiran na Free Pascal jedinici Windows

Podijelite po osi na kojoj kôd stvarno ovisi

Promjena dakle nije bila ponovna implementacija. Bio je to jedan uvjetni izraz: iz "kompajliraj začepčenu verziju kada se gradi bez VCL-a" u "kompajliraj začepčenu verziju kada se ne gradi za Windows". To je ispravna os, a iznošenje razloga čini razliku očiglednom. Enhanced metafile je Windows spremnik. Pretvarač je parser za Windows GDI zapise od vrha do dna. Hoće li host aplikacija koristiti VCL, drugi skup widgeta ili nikakav skup widgeta nema nikakve veze s time mogu li se ti zapisi tumačiti; je li cilj Windows ima sve s tim veze

Posljedice odabira ispravne osi dobivaju se besplatno. C++Builder izgradnje, koje poništavaju simbol Windows platforme u ovoj knjižnici, zadržavaju začepčenu verziju koja baca iznimku i ponašaju se točno kao i prije. macOS zadržava začepčenu verziju, ispravno, jer tamo nema GDI zapisa za raščlanjivanje. Delphi VCL izgradnje nisu dirane. A Windows izgradnja s ne-VCL skupom widgeta dobiva vektorski EMF uvoz kao nusprodukt, što nitko nije morao implementirati. Uvjet usklađen sa stvarnom ovisnošću pretvara platformski rad u promjenu od jednog retka; uvjet usklađen s pogrešnom pretvara ga u prepisivanje koje nikad ne dođe na red

EMF uvozni uvjet preusmjeren s pripadnosti VCL-u na Windows platformu, zadržava začepčene verzije drugdje i daje ne-VCL Windows izgradnjama vektorski uvoz
Preusmjeravanje uvjeta začepčene verzije na Windows platformu zadržava ponašanje svih postojećih izgradnji i daje ne-VCL Windows ciljevima EMF vektorski uvoz besplatno

Razmak na Free Pascalu bili su deklaracije, ne logika

Ono što je stvarno nedostajalo bile su Win32 deklaracije koje pruža Delphi jedinica Windows, a ne pruža ona Free Pascalova. Njihovo sabiranje u jednu jedinicu za kompatibilnost umjesto rasipanja uvjetnih izraza kroz pretvarač zadržalo je parser čitljivim. Popis je poučan jer pokazuje koliko je nejednaka pokrivenost zaglavlja između dva RTL-a: 113 konstanti tipova metafile zapisa, dvije zastavice proširenog tekstualnog izlaza, tri konstante načina gradient ispune, tip pokazivača na tablicu handlea, aliasi za gradient vršne i primitivne zapise te tri tipa zapisa koje Free Pascal uopće ne deklarira, pokrivajući alfa miješanje, prozirno blitanje i način upravljanja bojama

Ništa od toga nije zanimljivo pojedinačno. Sve to mora biti ispravno prije nego se parser kompajlira, a jedinica za kompatibilnost prirodni je dom jer se može usporediti s dokumentacijom zaglavlja kao cjelina

Win32 deklaracije kojih nema u Free Pascal jedinici Windows, sabrane u jednu jedinicu za kompatibilnost za EMF u PDF vektorski pretvarač
Konstante zapisa, zastavice, aliasi i tri izostala tipa zapisa žive u jednoj jedinici za kompatibilnost koja se može usporediti s dokumentacijom zaglavlja

Onaj koji tiho crta pogrešnu sliku

Dvije od tih deklaracija nisu samo izostale, one su prisutne i pogrešne za ovu svrhu, a to je dio vrijedan pamćenja čak i ako nikad ne dirate metafile

Free Pascal deklarira zapis stvaranja kista s ugrađenom strukturom kista iz vremena izvođenja, a zapis proširene olovke s ugrađenom strukturom olovke iz vremena izvođenja. Obje te strukture iz vremena izvođenja deklariraju svoj član hatch kao cijeli broj veličine pokazivača, jer u živom GDI pozivu taj član može nositi handle. Metafile, međutim, uvijek sprema 32-bitni oblik, jer je raspored zapisa dio serijaliziranog formata datoteke i ne mijenja se s bitnošću procesa

Na 32-bitnim izgradnjama ta se dva slažu i ništa se ne događa. Na Win64 član veličine pokazivača ima osam bajtova gdje datoteka ima četiri, pa se svako polje nakon člana hatch čita s pogrešnog pomaka. Nema iznimke, nema pogreške raščlambe i nema upozorenja. Metafile se jednostavno pogrešno prikazuje: boje iz pogrešnih bajtova, širine olovaka iz pogrešnih bajtova i slika koja izgleda kao greška prikazivanja, a ne greška rasporeda strukture. Delphi isporučuje izričito 32-bitne varijante obiju struktura upravo iz tog razloga, a jedinica za kompatibilnost ih ponovno deklarira na isti način

Raspored bajtova EMF zapisa kista pokazuje da pokazivački veličinsko hatch polje pomjera kasnija polja za četiri bajta na Win64 u odnosu na fiksni 32-bitni raspored
Serijalizirani zapis uvijek sprema 4-bajtni hatch, pa struktura iz vremena izvođenja veličine pokazivača tiho pogrešno čita svako kasnije polje na Win64
// Pogrešno na Win64: Hatch je veličine pokazivača, datoteka sprema
// 32 bita, a svako sljedeće polje pomakne se za četiri bajta bez
// ijedne pogreške
type
  TLogBrushRuntime = record
    lbStyle: UINT;
    lbColor: COLORREF;
    lbHatch: ULONG_PTR;      // 8 bajtova u 64-bitnom procesu
  end;

// Ispravno: serijalizirani raspored, fiksna širina bez obzira na bitnost
type
  TLogBrush32 = record
    lbStyle: UINT;
    lbColor: COLORREF;
    lbHatch: DWORD;          // uvijek 4 bajta, kako je spremljeno u metafileu
  end;

Opće pravilo: svaka struktura koja se pojavljuje i kao argument API-ja u vremenu izvođenja i kao raspored polja serijalizirane datoteke treba dvije deklaracije, a serijalizirana mora koristiti tipove fiksne širine od početka do kraja. Članovi veličine pokazivača u formatu datoteke uvijek su greška koja čeka 64-bitnu izgradnju

Razlike u potpisima pripadaju omotu, ne svakom mjestu poziva

Preostale razlike bile su obične nepodudarnosti potpisa, a način da ih se upije jest prosljeđujući omot umjesto uvjetnog izraza na svakom od mjesta poziva. Funkcija kombiniranja transformacija uzima pokazivače pod Free Pascalom gdje Delphi uzima referentne parametre, pa omot uzima reference i prosljeđuje adrese. On također najprije kopira oba izvorna argumenta u lokalne varijable, jer pretvarač ima mjesta poziva gdje je odredišna matrica istodobno jedan od izvora, a prosljeđivanje iste adrese dvaput funkciji koja piše dok čita proizvodi transformaciju koja je suptilno pogrešna na način koji se pokazuje samo na rotiranom sadržaju

function CombineTransformCompat(var Dest: TXForm;
  const A, B: TXForm): BOOL;
var
  SrcA, SrcB: TXForm;
begin
  // Najprije kopiraj: pozivatelji legitimno prosljeđuju Dest kao A ili B
  SrcA := A;
  SrcB := B;
{$IFDEF FPC}
  Result := Windows.CombineTransform(@Dest, @SrcA, @SrcB);
{$ELSE}
  Result := Windows.CombineTransform(Dest, SrcA, SrcB);
{$ENDIF}
end;

Tipovi pravokutnika i točke su drugi slučaj. Free Pascal tretira metafile zapise pravokutnika i točke kao odvojene tipove od općih grafičkih, pa je osam mjesta dodjele trebalo eksplicitnu pretvorbu između zapisa istog rasporeda. Oba kompajlera prihvaćaju oblik s pretvorbom, pa ta mjesta ne nose nikakav uvjetni izraz, što vrijedi malo ružnoće

Što ovo mijenja za raspoređivanje na Free Pascalu

Vektorski EMF uvoz radi na Windowsu pod Free Pascalom, proizvodeći isti sadržaj stranice kao Delphi izgradnja: staze kao staze, gradijente kao sadržaj uzorka, tekst kao tekst. Izvan Windowsa rastrovani put ostaje odgovor, a to je ograničenje formata, ne prijenosa. Koordinatno i izrezno stanje u koje pretvarač utječe opisano je u članku o praćenju CTM i izrezivanja toka sadržaja, a vektorske primitivne koje emitira pokrivene su u vektorskoj grafici, shaderima i gradijentima

Ako pregledavate vlastitu bazu kôda za istu priliku, korisna vježba je ona koja je ovo pokrenula: napravite popis članova koje stvarno koristite iz okvira od kojega mislite da ovisite. Odgovor je često znatno kraći nego što to sugerira popis uvoza, a stvarno ograničenje obično je negdje sasvim drugdje. Putovi uvoza temeljeni na kontekstu uređaja općenito opisani su u članku o pretpregledu ispisa i kontekstu uređaja, a pokrivenost platformi i alatnim lancima navedena je na stranici proizvoda losLab PDF Developer Library