HotPDF kääntyy ja toimii Free Pascal 3.2.2:lla Lazaruksen kanssa, ja tuon porttauksen rehellinen yhteenveto on kaksi virkettä. Dokumenttien luonti, lataus, tallennus, pakkaus, purku, salaus ja salauksen purku kaikki toimivat pelkästään Pascal-taustoilla, joten Lazarus-sovellus voi tuottaa ja kuluttaa aitoa PDF:ää ilman mitään C-riippuvuutta. Valinnaiset natiivit kuvakoodekit eivät, koska esikäännetyt Win64-objektit käyttävät COFF-muunnelmaa, jota kumpikaan Free Pascal -linkkeri ei voi nielaista, joten kyseisellä työkaluketjulla sisääntulopisteet ratkeavat stubeiksi, jotka epäonnistuvat suljettuina
Siirtyminen ”kääntyy” -tasolta ”toimii” -tasolle vaati tietyn joukon korjauksia, ja jokainen niistä on ansa, joka löytää minkä tahansa muun Delphi-koodikannan, joka siirtyy Free Pascalille. Ne kannattaa kirjata ylös siinä järjestyksessä, jossa ne satuttavat
Miksi yksikön kääntyminen ei todista mitään?
Koska Pascal-yksikkö voi viitata symboliin, joka ei koskaan tee mitään hyödyllistä, ja silti tyydyttää kääntäjän. Siinä vaiheessa, kun kaikki 113 kirjastoyksikköä kääntyivät puhtaasti Free Pascalilla, arkistosäiliön käsittelijät toimivat aidosti, varmennettuna savutestillä, joka avasi CBZ:n ja muunsi sen PDF:ksi. XFA-lomakkeiden litistys ei toiminut lainkaan, koska litistyksen on purettava pakattu /XFA-pakkavirta ja deflate-sisääntulopiste oli yhä stub. Mikään käännöstulosteessa ei erottanut näitä kahta tapausta
Siitä syntynyt sääntö on lyhyt. Ennen kuin kirjoitat julkaisumuistiinpanoon, että ominaisuus toimii uudella työkaluketjulla, kirjoita ajonaikainen koetin, joka kokeilee ominaisuutta päästä päähän kyseisellä työkaluketjulla. Kääntökatteisuus on edellytys, ei koskaan näyttö. Laajempi kuva siitä, mitä porttaus kattaa, on artikkelissa Free Pascal- ja Lazarus Win64 -tuen muistiinpanoissa
Heitto cdecl-stubin sisällä ei yllä kutsujaan
Tämä ansaitsee oman osionsa, koska oire on niin harhaanjohtava. Stub-yksiköt paljastavat C-sisääntulopisteet samalla tavalla kuin staattinen kirjasto, joten stub näyttää tältä
// Näyttää järkevältä. Ei ole.
function inflate(Strm: Pointer; Flush: Integer): PtrUInt; cdecl;
public name 'inflate';
begin
raise ENotSupportedException.Create('codec unavailable');
end;
Free Pascalilla Win64:lle tuo poikkeus ei etene kutsujalle. Ei ole try..except-käsittelijää, joka näkisi sen, koska purkautuminen tällä tavalla julistetun cdecl-rajan yli ei kanna Pascal-poikkeuskehystä; prosessi päättyy poistumiskoodilla 217. Sovelluksen näkökulmasta ei ole virhettä, ei viestiä eikä lokiriviä, vain ohjelma, joka katoaa. Se on ankarasti pahempaa kuin väärä vastaus, koska väärän vastauksen voi käsitellä
Kiusaava korjaus on saada stub palauttamaan vikakoodi sen sijaan, ja inflaten kohdalla se on oikein, koska zlibilla on hyvin määritelty virhepaluu. Se on väärin yleisesti: stub funktiolle jpeg_read_header, joka palauttaa nollan, käskee kutsujaa jatkamaan rakenteella, jota kukaan ei alustanut. Kestävä korjaus on portittaa Pascal-sisääntulopisteessä pikemminkin kuin C-muotoisen stubin sisällä käyttäen mitä tahansa vikakäytäntöä, jota kyseisellä API:lla jo on
function TryDecodeJPEG(const Data: TBytes; out Bitmap: TBitmap): Boolean;
begin
{$IFDEF FPC}
// Kieltäydy ennen kuin stubiin koskaan päädytään, tällä API:lla
// itsellään olevalla vikakäytännöllä poikkeuksen sijaan cdecl-rajan yli
Bitmap := nil;
Result := False;
Exit;
{$ENDIF}
Result := DecodeJPEGNative(Data, Bitmap);
end;
paszlib ei ole zlib, ja ero on kaksi dokumenttiluokkaa
Free Pascalilla saatavilla oleva Pascal-deflate-toteutus käsittelee kaksi kehystystä: zlib-kääreen ja raakan deflaten. Se ei käsittele gzip-kehystystä, jonka zlib valitsee windowBits-arvoilla 16:sta 31:een, eikä se käsittele automaattisen tunnistuksen tilaa, jonka arvot 32:sta 47:ään valitsevat. HotPDF tarvitsee molemmat. Turvallinen SVG-tuontipolku pyytää arvoa 31, ja lataajalla on varapolkutikapuu, joka pyytää arvoa 47, kun virran kehystys on moniselitteinen. Ohita jompikumpi, ja kokonainen dokumenttiperhe lakkaa avautumasta, dekoodausvirheellä, joka osoittaa virtaan eikä puuttuvaan kehystykseen
On toinen, terävämpi yhteensopimattomuus. z_stream-tietue, jonka paszlib julistaa, ei ole samassa muistiasettelussa kuin C:n: sen msg-kenttä on lyhyt merkkijono osoittimen sijaan, ja total_in ja total_out ovat 64-bittisiä siellä, missä C-ABI:ssä on konesanonia. Kutsujan tietuetta ei siksi voi välittää suoraan läpi. Toimiva järjestely on pitää paszlibin tila state-osoittimen takana, jonka julkinen tietue jo varaa, ja kopioida julkiset kentät sisään ja ulos jokaisen kutsun ympärillä. Gzipin CRC ja kahdeksan tavun pituusperä vaativat huomion samassa shim-kerroksessa, mikä on niille luonnollinen paikka, koska se jo omistaa kehystyspäätöksen
Dynaamisen taulukon välittäminen tyypittömälle var-parametrille
Tämä on virhe, joka todennäköisimmin istuu koodissasi juuri nyt. Kun välität dynaamisen taulukon tyypittömälle var-parametrille, se, mitä vastaanottava osapuoli saa, on taulukkomuuttujan osoite, joka on osoittimen osoite, ei payloadin osoite. Lukeminen siihen ylikirjoittaa siis muuttujan itsensä ja sen, mitä sen vieressä istuu
var
FBuffer: TBytes;
begin
SetLength(FBuffer, 65536);
// Väärin: luovuttaa FBuffer-muuttujan osoitteen
FStream.Read(FBuffer, Length(FBuffer));
// Oikein: luovuttaa ensimmäisen payload-tavun osoitteen
FStream.Read(FBuffer[0], Length(FBuffer));
end;
Delphillä väärä muoto vaikuttaa usein toimivan, koska se, mitä se korruptoi, on viereinen pinopaikka, jota mikään ei lue sen jälkeen. Free Pascalilla sama rivi aiheuttaa segmenttivirheen ensimmäisellä käytöllä. Sen tekee niin vaikeaksi havaita silmällä, että staattisilla taulukoilla ei ole tällaista ongelmaa, koska staattinen taulukkomuuttuja on oma payloadinsa, joten molemmat kirjoitusasut ovat oikein samassa tiedostossa riippuen muutaman sadan rivin päässä olevasta deklaraatiosta
ZIP-säiliöt ilman System.Zip-yksikköä
Free Pascalilla ei ole RTL:n zip-yksikön vastaista, ja saatavilla olevalla vaihtoehdolla on sekä erilainen API-pinta ettei sillä ole tukea perinnesalaukselle, jota vanhemmat säiliömuodot yhä käyttävät, joten pieni kirjaston sisäinen lukija osoittautui lyhyemmäksi kuin siihen sopeutuminen. Kaksi muotoyksityiskohtaa veivät aikaa ja ovat helppoja saada väärin
Ensimmäinen on salausotsikon tarkistustavu. Sen kahdestoista tavu on normaalisti CRC:n ylempi tavu, mutta kun yleistarkoituksisen lipun bitti 3 on asetettu, mikä tarkoittaa, että koot asuvat perässä olevassa datadeskriptorissa eikä CRC:tä vielä tiedetä, tarkistustavu tulee muutosajan ylemmästä tavusta sen sijaan. Toteuta vain CRC-muoto, ja jokainen suoratoistotilassa kirjoitettu arkisto hylkää oikean salasanan. Toinen on ZIP64-lisäkenttä: sen kolme 64-bittistä kenttää ilmestyvät kiinteässä järjestyksessä, mutta ne kirjoitetaan vain, kun vastaava 32-bittinen kenttä on kyllästynyt, joten niiden lukeminen kiinteillä siirtymillä toimii arkistoissa, jotka testasit, ja epäonnistuu seuraavassa. Jäsennä ne positionaalisesti sen mukaan, mitkä 32-bittiset kentät ovat kyllästyneet
Yksi kätevyys, joka kannattaa tuntea: Free Pascalin purkuvirta ottaa toisen konstruktoriargumentin, joka ohittaa zlib-otsikon, ja se on juuri se, mitä ZIP-merkinnät tarvitsevat, koska ne tallentavat raakan deflaten. Tuo polku ei kosketa kirjaston zlib-shimiä lainkaan, joten siihen ei vaikuta puuttuva C-tausta
Väriglyfien läpinäkyvyys LCL:n alla
Rasterisoidun väriglyfin alfakanavan lukeminen on se yksi grafiikkayksityiskohta, jolla ei ole suoraa käännöstä. LCL:n PNG-luokalla ei ole scanline-hakufunktiota, joka paljastaisi alfakanavan, ja PNG:n sijoittaminen bittikarttaan hylkää sen, joten väri-emoji saapuu täysin läpinäkymättömänä ja kompositoituu mustan laatikon kanssa takanaan. Toimiva reitti on rajapintakuva: luo se PNG:stä, lue sitten pikselit värihakufunktion kautta muistaen, että sen komponentit ovat 16-bittisiä ja ne pitää siirtää alas kahdeksalla tavuiksi. Tuo pinta käyttää myös luonnollista ylhäältä alas -rivijärjestystä, joten Height - 1 - Y-käänteisyys, jota VCL-scanline-koodi tarvitsee, on poistettava portattamisen sijaan
Kaksi käännösjärjestelmähuomiota ennen virheen raportoimista
Täysi uudelleenkäännös epäonnistuu joskus määrittelemättömään symboliin, jonka nimi päättyy $crc-päätteeseen ja heksaarvoon. Tuo pääte lasketaan parametrityypeistä, ja se ei täsmää, kun yksi käännös kääntää yksikön kahta eri rajapintaversiota vastaan samassa ajossa. Käännöksen ajaminen uudelleen poistaa sen; signatuuri ei ole väärä
Toiseksi, Free Pascal 3.2.2:lla ei ole anonyymejä metodeja, joten siellä missä kirjasto käytti sulkeumia rinnakkaisen putken kytkemiseen Free Pascal -käännös ottaa deterministisen sarjallisen varatien. Tuloste on identtinen, läpäisy ei ole; jos riipput rinnakkaisesta sivujen renderöinnistä, se on syy jäädä Delphiin toistaiseksi, ja putken suunnittelu on kuvattu artikkelissa rinnakkainen renderöintiputki ja takapaine. Kuvakoodekkien tilanne on toinen paikka, jossa työkaluketjun valinta muuttaa kyvykkyyttä eikä vain nopeutta, joten Lazarus-käyttöönoton kannattaa suunnitella kuvaformaatit sen mukaan; nykyinen työkaluketjukohtainen matriisi on HotPDF Delphi PDF -komponentin tuotesivulla