HotPDF se kompajlira i izvršava pod Free Pascal 3.2.2 sa Lazarus-om, i pošten sažetak tog porta su dve rečenice. Stvaranje dokumenta, učitavanje, čuvanje, kompresija, dekompresija, šifrovanje i dešifrovanje svi rade na isključivo Pascal backend-ima, pa Lazarus aplikacija može proizvoditi i trošiti pravi PDF bez ikakve C zavisnosti. Opcioni nativni kodeci slika ne rade, jer prethodno izgrađeni Win64 objekti koriste COFF varijantu koju nijedan Free Pascal linker ne može konzumirati, pa na tom toolchain-u ulazne tačke rešavaju se u stub-ove koji otkazuju zatvoreno
Dolazak od "se kompajlira" do "radi" uzeo je specifičan skup popravki, i svaka od njih je zamka koja će naći svaku drugu Delphi bazu koda koja se seli na Free Pascal. Vredi ih zapisati u redosledu po kojem su bolele
Zašto kompajlirana jedinica ništa ne dokazuje?
Zato što Pascal jedinica može referencirati simbol koji nikad neće učiniti ništa korisno, a i dalje zadovoljiti kompajler. U trenutku kada se svih 113 jedinica biblioteke čisto izgradilo pod Free Pascal-om, rukovaoci arhiv kontejnera iskreno su radili, provereno smoke testom koji je otvorio CBZ i pretvorio ga u PDF. XFA izravnavanje obrazaca uopšte nije radilo, jer izravnavanje mora inflate-ovati komprimovani /XFA tok paketa, a deflate ulazna tačka bila je još stub. Ništa u izlazu izgradnje nije razlikovalo ta dva slučaja
Pravilo koje je iz toga izašlo kratko je. Pre pisanja u napomenu o izdanju da funkcija radi na novom toolchain-u, napišite runtime sondu koja vežba funkciju od kraja do kraja na tom toolchain-u. Pokrivenost kompajliranjem je preduslov, nikada dokaz. Šira slika onoga što port pokriva nalazi se u napomenama o Win64 podršci Free Pascal-a i Lazarus-a
Izuzetak unutar cdecl stub-a ne stiže do pozivaoca
Ovo zaslužuje sopstveni odeljak jer je simptom toliko obmanjujući. Stub jedinice izlažu C ulazne tačke na način na koji bi statička biblioteka, pa stub izgleda ovako
// Deluje razumno. Nije.
function inflate(Strm: Pointer; Flush: Integer): PtrUInt; cdecl;
public name 'inflate';
begin
raise ENotSupportedException.Create('codec unavailable');
end;
Na Free Pascal-u za Win64 taj izuzetak ne propagira do pozivaoca. Nema try..except rukovaoca koji ga vidi, jer odmotavanje preko cdecl granice deklarisane ovako ne nosi Pascal okvir izuzetka; proces se okončava sa izlaznim kodom 217. Sa strane aplikacije nema greške, nema poruke i nema log linije, samo program koji nestane. To je strogo gore od pogrešnog odgovora, jer se pogrešnim odgovorom može rukovati
Primamljiva popravka je učiniti da stub vrati kod neuspeha umesto toga, i za inflate to je ispravno jer zlib ima dobro definisan povratak greške. Opšte je pogrešno: stub za jpeg_read_header koji vraća nulu govori pozivaocu da nastavi sa strukturom koju niko nije inicijalizovao. Trajna popravka je kapija na Pascal ulaznoj tački umesto unutar C-oblikovanog stub-a, koristeći koju god konvenciju neuspeha taj API već ima
function TryDecodeJPEG(const Data: TBytes; out Bitmap: TBitmap): Boolean;
begin
{$IFDEF FPC}
// Odbijte pre nego što se stub uopšte dostigne, sa sopstvenom
// konvencijom neuspeha ovog API-ja umesto izuzetkom preko cdecl
Bitmap := nil;
Result := False;
Exit;
{$ENDIF}
Result := DecodeJPEGNative(Data, Bitmap);
end;
paszlib nije zlib, i razlika su dve klase dokumenata
Pascal deflate implementacija dostupna na Free Pascal-u rukuje dva uokvirivanja: zlib omotačem i sirovim deflate. Ne rukuje gzip uokvirivanjem, koje zlib bira kroz windowBits vrednosti od 16 do 31, i ne rukuje režimom automatske detekcije koji biraju vrednosti 32 do 47. HotPDF traži oba. Bezbedan put SVG uvoza traži 31, a učitavač ima lestvicu rezervnih rešenja koja traži 47 kada je uokvirivanje toka dvosmisleno. Preskočite bilo koje i cela familija dokumenata prestaje se otvarati, sa greškom dekodiranja koja ukazuje na tok umesto na nedostajuće uokvirivanje
Postoji druga, oštrija nekompatibilnost. z_stream zapis koji paszlib deklariše nema isti memorijski raspored kao C-ov: njegovo polje msg je kratak niz znakova umesto pokazivača, a total_in i total_out su 64-bitni gde C ABI ima mašinske reči. Zapis pozivaoca dakle ne može se proslediti pravo. Funkcionišući aranžman je držati paszlib stanje iza state pokazivača koji javni zapis već rezerviše, i kopirati javna polja unutra i napolju oko svakog poziva. gzip CRC i osmobajtni trailer dužine obuhvaćeni su u tom istom shim sloju, što je prirodno mesto za njih jer on već poseduje odluku o uokvirivanju
Prosleđivanje dinamičkog niza netipiziranom var parametru
Ovo je greška koja najverovatnije upravo sedi u vašem kodu. Kada prosledite dinamički niz netipiziranom var parametru, ono što primalac dobija je adresa promenljive niza, koja je adresa pokazivača, a ne adresa sadržaja. Pa čitanje u njega prepisuje samu promenljivu i ono što sedi pored nje
var
FBuffer: TBytes;
begin
SetLength(FBuffer, 65536);
// Pogrešno: predaje adresu promenljive FBuffer
FStream.Read(FBuffer, Length(FBuffer));
// Ispravno: predaje adresu prvog bajta sadržaja
FStream.Read(FBuffer[0], Length(FBuffer));
end;
Na Delphi-ju pogrešan oblik često deluje kao da radi, jer ono što oštećuje je susedan slot steka koji ništa kasnije ne čita. Na Free Pascal-u ista linija pravi segmentacionu grešku pri prvoj upotrebi. Ono što ga čini tako teškim za uočavanje okom jeste što statički nizovi nemaju takav problem, pošto je promenljiva statičkog niza svoj vlastiti sadržaj, pa su oba pravopisa ispravna u istoj datoteci zavisno od deklaracije nekoliko stotina linija dalje
ZIP kontejneri bez System.Zip
Free Pascal nema ekvivalent RTL zip jedinici, a dostupna alternativa ima i drugačiju API površinu i bez podrške za nasleđeno šifrovanje koje stariji formati kontejnera i dalje koriste, pa se mali čitač unutar biblioteke ispostavio kraćim od prilagođavanja njoj. Dva detalja formata koštala su vremena i lako je pogrešiti o njima
Prvi je bajt provere šifrovskog zaglavlja. Njegov dvanaesti bajt obično je gornji bajt CRC-a, ali kada je postavljen bit 3 opštenamenske zastavice, što znači da veličine žive u pratećem data deskriptoru i da CRC još nije poznat, bajt provere dolazi iz gornjeg bajta vremena izmene umesto toga. Implementirajte samo CRC oblik i svaki arhiv pisan u streaming režimu odbacuje ispravnu lozinku. Drugi je ZIP64 extra polje: njegova tri 64-bitna polja pojavljuju se u fiksnom redosledu ali se pišu samo kada je odgovarajuće 32-bitno polje zasićeno, pa njihovo čitanje na fiksnim ofsetima radi na arhivima koje ste testirali i otkazuje na sledećem. Rastavite ih poziciono u odnosu na to koja su 32-bitna polja zasićena
Jedna pogodnost vredna znanja: Free Pascal tok dekompresije prima drugi argument konstruktora koji preskače zlib zaglavlje, što je tačno ono što ZIP unosi traže pošto čuvaju sirovi deflate. Taj put uopšte ne dira zlib shim biblioteke, pa nije pogođen nedostajućim C backend-om
Prozirnost obojenih glifova pod LCL-om
Čitanje alfa kanala rasterizovanog obojenog glifa jedini je grafički detalj bez direktne translacije. LCL PNG klasa nema scanline pristupnik koji izlaže alfu, a dodela PNG-a bitmapama je odbacuje, pa obojeni emotikon stiže potpuno neprovidan i kompozituje sa crnom kutijom iza sebe. Funkcionišući put je interfejska slika: stvorite je iz PNG-a, pa čitajte piksele kroz pristupnik boje, pamteći da su njegove komponente 16-bitne i treba ih pomeriti naniže za osam da postanu bajtovi. Ta površina takođe koristi prirodni redosled redova odozgo nadole, pa Height - 1 - Y inverziju koju VCL scanline kod traži treba ukloniti, a ne preneti
Dve napomene o sistemu izgradnje pre nego što prijavite grešku
Kompletna ponovna izgradnja povremeno otkazuje sa nedefinisanim simbolom čije se ime završava na $crc sufiks i heksadecimalnu vrednost. Taj sufiks računa se iz tipova parametara, i otkazuje poklapanje kada jedna izgradnja kompajlira jedinicu naspram dve različite verzije interfejsa u istom prolazu. Ponovno pokretanje izgradnje to čisti; potpis nije pogrešan
Drugo, Free Pascal 3.2.2 nema anonimne metode, pa svuda gde je biblioteka koristila closure-e da poveže paralelni pipeline Free Pascal izgradnja umesto toga uzima deterministički serijski rezervni put. Izlaz je identičan, propusnost nije; ako zavisite od paralelnog renderovanja stranica, to je razlog da ostanete na Delphi-ju za sada, i dizajn pipeline-a opisan je u članku o paralelnom render pipeline-u. Situacija sa kodecima slika je drugo mesto gde izbor toolchain-a menja mogućnosti, a ne samo brzinu, pa Lazarus raspoređivanje treba da planira svoje formate slika u skladu sa tim; trenutna matrica po toolchain-u nalazi se na stranici proizvoda HotPDF Delphi PDF component