Delphi ja Lazarus kääntävät samaa Object Pascalia, ja juuri tuo pinnan samankaltaisuus tekee katseluohjelman siirtämisestä niiden välillä petollista. Työkaluketjut eroavat toisistaan kolmessa PDF-työn kannalta olennaisessa kohdassa: natiivi string-tyyppi on UTF-16 Delphissä ja UTF-8 LCL-sovelluksessa; VCL ja LCL ovat eri visuaalisia kehyksiä, joilla on omat kontrollinsa, dialoginsa ja lomakkeiden tallennusmuotonsa; ja Delphi-binääri kohdistuu Windowsiin, kun taas FPC-binääri voi olla matkalla Linuxiin tai macOS:ään. Mikään näistä eroista ei paljastu käännösaikana. PDFium Componentin päälle rakennettu katseluohjelma — komponentti toimitetaan VCL- ja LCL-editioina yhdestä lähdekoodipuusta — kääntyy puhtaasti Lazaruksen alla kourallisen yksikkönimien vaihtoja ja muutaman {$IFDEF FPC}-lohkon jälkeen. Viat tulevat esiin myöhemmin, kun oikea data ja oikea käyttöönotto paljastavat ne oletukset, joita Delphi-käännös oli hiljaa tehnyt
Suurimman osan menetetystä ajasta selittää neljä näistä oletuksista: tekstin koodaus käyttöliittymän rajalla, kiusaus ylläpitää lomakkeesta kahta kopiota, tapa jolla natiivi moottoribinääri ratkeaa ajonaikaisesti ja hetki, jolloin tekstistä puheeksi -ominaisuudelta loppuu alusta SAPI:n kadotessa. Kunkin käsittely on halpaa, jos sen tulemisen tietää, ja jäljittäminen kallista, jos ei tiedä
Sama Pascal, erilaiset merkkijonojen hyötykuormat
Delphin natiivi string on ollut UTF-16 vuodesta 2009 lähtien. Lazarus ja Free Pascal käyttävät oletuksena UTF-8:aa LCL-sovelluksissa. Komponentin tekstiä käsittelevät API:t puhuvat UTF-16:ta WString-tyypin kautta, jonka FPC-käännös aliasoi tyypiksi WideString, joten jokainen raja, jolla teksti liikkuu LCL-käyttöliittymäsi ja PDF-moottorin välillä, on muunnospiste
Muunnokset tapahtuvat automaattisesti suoraviivaisissa sijoituksissa, eikä useimpien ohjelmien tarvitse koskaan miettiä niitä. Kaksi tapaa pitää koodausviat loitolla. Välitä teksti sellaisenaan ilman tavutason käsittelyä: koodi, joka pilkkoo hakutermin tavusiirtymän mukaan, toimii Delphissä, jossa yksi Char on yksi UTF-16-yksikkö, mutta korruptoi monitavuisen UTF-8:n LCL:ssä. Ja testaa ei-ASCII-datalla ensimmäisestä ajosta lähtien. Saksalainen tiedostonimi, kyrillinen hakutermi, aksentoitu tekijänimi asiakirjan metadatassa: puhtaasti ASCII-testidata kätkee jokaisen koodausvian, koska ASCII on se ainoa alue, jossa UTF-8 ja UTF-16 vastaavat toisiaan merkkiä kohden tavu kerrallaan. Vika on todellinen koko ajan; ASCII vain pitää sen näkymättömänä, kunnes asiakas Münchenissä avaa tiedoston, jota et koskaan kokeillut
Yksi ehtolohko, ei haarautumista per IDE
Ensimmäisen tusinan IFDEF:n jälkeen koodikanta alkaa tuntua kahdelta projektilta samassa arkistossa, ja sen haarauttaminen per IDE näyttää houkuttelevalta. Se on väärä liike. Todelliset erovat supistuvat yhdeksi jaetuksi esittelylohkoksi, ja haarautuminen kaksinkertaistaisi jokaisen vikakorjauksen hinnan siitä eteenpäin. Pidä ehtokerros näin pienenä:
{$IFDEF FPC}
uses
LCLType, Forms, Graphics, Controls;
type
WString = WideString; // komponentin teksti-API:t ovat UTF-16
TBytes = array of Byte;
{$ELSE}
uses
Winapi.Windows, Vcl.Forms, Vcl.Graphics, Vcl.Controls;
{$ENDIF}
Kaikki tuon lohkon alla oleva kääntyy identtisesti molemmissa IDE:ssä. Asiakirjojen käsittely, sivulla liikkuminen, renderöintikutsut: TPdf ja TPdfView tarjoavat saman rajapinnan VCL- ja LCL-editioissa, joten katseluohjelmasta suurin osa ei koskaan kohtaa kääntäjädirektiiviä. Sen pitäminen sellaisenaan on rakenteellista kuria eikä nokkela temppu. Yhteinen PDF-logiikka elää yksiköissä, jotka eivät vedä mukaansa kehyskohtaisia dialogeja tai paneeleja. Se harvojen asioiden joukko, jotka aidosti eroavat — kuten tulostusdialogit ja alustakäytäntöjensä mukaiset tiedostovalitsimet — kätkeytyy ohuen rajapinnan taakse, joka toteutetaan kerran per kehys. IFDEF-lohkosta tulee se yksi paikka, johon tulevat alustaerot saavat laskeutua, sen sijaan että kääntäjädirektiivit vuotaisivat neljänkymmenen yksikön yli
Rakenna lomake koodissa, ei kahdessa suunnittelijassa
Lomakkeiden tallennus on kohta, jossa kahden IDE:n projektit mätänevät hiljalleen. .dfm ja .lfm, jotka väittävät kuvaavansa samaa lomaketta, ajautuvat erilleen ominaisuus kerrallaan, kunnes molemmat käännökset käyttäytyvät eri tavalla syistä, joita kukaan ei pysty vertailemaan, koska kyseiset kaksi tiedostoa eivät edes ole samassa muodossa. Katseluohjelman rakentaminen ajonaikaisesti kiertää koko ongelman. Siinä on yksi konstruktorisekvenssi, versionhallittuna tavallisena koodina, ja se näyttää samalta molemmilla alustoilla:
procedure TViewerForm.FormCreate(Sender: TObject);
begin
Pdf := TPdf.Create(Self);
PdfView := TPdfView.Create(Self);
PdfView.Parent := Self;
PdfView.Align := alClient;
PdfView.Pdf := Pdf;
PdfView.FitMode := pfmFitWidth;
if ParamCount > 0 then
begin
Pdf.FileName := ParamStr(1);
Pdf.Active := True; // avaa asiakirjan; PageCount on käytössä tämän jälkeen
end;
end;
Noiden sijoitusten tarkalla järjestyksellä on vähemmän merkitystä kuin sillä yhdellä rivillä, joka tekee varsinaisen työn. PdfView.Pdf := Pdf sitoo visuaalisen kontrollin asiakirjakomponenttiin, ja siitä lähtien sivulla liikkuminen PageNumber-ominaisuuden kautta ja sovituskäyttäytyminen FitMode-ominaisuuden kautta toimivat identtisesti VCL:n ja LCL:n alla. Yksi kehyksienvälinen erikoisuus on hyvä tietää ennen kuin käyttäjä raportoi sen vikana: Zoom-arvon asettaminen käsin palauttaa FitModen arvoon pfmNone molemmissa kehyksissä. Jos työkalupalkkisi siis kohtelee "sovita leveyteen" -asetusta pysyvänä mieltymyksenä, sinun on asetettava sovitustila uudelleen jokaisen ohjelmallisen zoomauksen jälkeen, tai asetus lakkaa hiljaa pätemästä ensimmäisellä kerralla, kun koodi koskee zoomaustasoon
Binääri, josta IDE ei koskaan varoittanut
Komponentti kietoo ympärilleen PDFium-moottorin, joka toimitetaan natiivina alustabinäärinä, ja juuri tuo binääri on lähde lähes jokaiseen "toimii IDE:ssä, kaatuu asennetusta pikakuvakkeesta" -raporttiin. Kolme sääntöä selittää suurimman osan niistä. Bittisyyden on täsmättävä tarkalleen. 32-bittinen suoritettava tiedosto ei voi ladata 64-bittistä pdfium-kirjastoa, ja käyttöjärjestelmän palauttama viesti ("moduulia ei löydy" joissakin Windows-versioissa) johdattaa aktiivisesti harhaan, koska tiedosto makaa aivan suoritettavan tiedoston vieressä. Ratkaise kirjaston polku suoritettavaan tiedostoon suhteutettuna, älä koskaan työhakemistoon; IDE-käynnistys ja komentorivikäynnistys eroavat juuri tässä suhteessa, minkä vuoksi vika piilottelee kehityksen aikana. Ja ota epäonnistunut lataus kiinni ennen ensimmäisen asiakirjan avaamista ja raportoi se odotettu polku ja arkkitehtuuri erikseen mainiten. Tukipyyntö, jossa lukee "PDFium 64-bit binary missing at <path>", sulkeutuu minuuteissa. Se, jossa lukee "katseluohjelma kaatuu käynnistyksessä", muuttuu viikon edestakaiseksi selittelyksi
Versioi moottoribinääri suoritettavan tiedoston rinnalla samalla kertaa. PDFium kehittyy nopeasti, ja asennusohjelma, joka päivittää sovelluksen mutta jättää vanhentuneen kirjaston levylle, tuottaa kaatumisia, joita kukaan toimistossanne ei pysty toistamaan, yksinkertaisesti siksi, että jokaisella koneella toimistossanne sattuu olemaan sopiva pari. Kohtele kirjastoa osana käännösartefaktia, samalla asennusohjelmalla, samalla versioleimalla ja samalla paluupolulla kuin se suoritettava tiedosto, jonka se lataa
Komponenttien rekisteröinti Lazarus IDE:ssä
Ajonaikainen rakentaminen ei tarvitse lainkaan suunnitteluaikaisia rekisteröintejä, mikä on puhtain asetelma katseluohjelmalle, joka rakentaa oman käyttöliittymänsä koodissa. Kun haluat komponentit Lazarus-paletille suunnitteluaikaista työskentelyä varten, asenna paketti ja anna sen omalle rekisteröintiyksikölle, PDFiumLazReg tiedostossa Lib/FPC/PDFiumLaz.lpk, hoitaa asia. Tuo yksikkö on merkitty tarkoituksella suunnitteluaikaiseksi: se viittaa IDE:n ominaisuuseditorien rajapintoihin, joita ei saa koskaan linkittää toimitettavaan suoritettavaan tiedostoon
Jos tämä menee pieleen, oireena on sovellus, joka riippuu käsittämättömästi IDE-paketeista, mikä näkyy käyttöönotto-ongelmana ensimmäisellä asiakaskoneella, johon Lazarusta ei ole koskaan asennettu
Puhe ja ruudunlukijat Windowsin ulkopuolella
Tekstistä puheeksi on se yksi ominaisuus, jossa alustarajat hajoavat, ja ne hajoavat käyttöjärjestelmässä, eivät komponentissa. SAPI, tavallinen TTS-tausta Windowsissa, on olemassa vain Windowsissa. Lazarus-käännös, joka kohdistuu yhä Windowsiin, säilyttää täyden SAPI-tulosteen ja saman NVDA-yhteensopivan käyttäytymisen kuin Delphi-alkuperäinenkin, joten Windowsista Windowsiin -siirto ei menetä tässä mitään, eikä NVDA-käyttäjä erota näitä kahta käännöstä toisistaan
Linux- tai macOS-kohde on eri juttu. SAPIa ei ole kutsuttavana, joten äänilähtö on kytkettävä uudelleen natiiviin puhepalveluun, kun taas sen yläpuolella olevat lukemisrajapinnat pysyvät paikoillaan. Juuri tämä kahtiajako on peruste sille, miksi puhe kannattaa pistää rajapinnan taakse ensimmäisestä commitista lähtien: lukujärjestysanalyysi ja sanoja seuraava kohdistin ovat alustaneutraaleja ja siirtyvät koskemattomina, ja vain se ohut kerros, joka oikeasti tuottaa äänen, on muutettava alustakohtaisesti. Artikkeli saavutettavasta lukijasta käsittelee tuota lukukoneistoa perusteellisesti
Yhtäpitävyystarkistuslista ennen kuin julistat siirron valmiiksi
Seuraava läpikäynti on saanut kiinni oikeita regressioita; kohdat on lueteltu suunnilleen siinä järjestyksessä, jossa viat tapaavat tulla esiin. Avaa asiakirja, jonka polku sisältää ei-ASCII-merkkejä. Hae termi, jossa on ei-ASCII-merkkejä, ja varmista, että osumat korostuvat siellä, missä niiden pitääkin. Kokeile hiiren rullan vieritystä, raahaamalla valitsemista ja näppäimistöllä sivulta toiselle siirtymistä jokaisessa toimittamassasi widget-sarjassa, koska kohdistuksen käsittely ja rullan käyttäytyminen ovat LCL:n widget-sarjasta eniten riippuvia nurkkia. Tarkista renderöinti 100 prosentin, 150 prosentin ja 200 prosentin näytön skaalauksella. Aja lopuksi asennettu käännös, ei IDE-käännös, koneella, jolla ei ole koskaan ollut IDE:tä, koska vain se testi koettelee binäärin ratkaisun rehellisesti. Kaikki muu voi mennä läpi, vaikka toi yksi epäonnistuu hiljaisesti
Renderöinnin läpimeno siirtyy editioiden välillä muuttumattomana, joten artikkelissa renderöintivälimuistista ja zoomauksen suorituskyvystä esitetty lähestymistapa pätee LCL-katseluohjelmaan täsmälleen siinä muodossa kuin se on kirjoitettu VCL-katseluohjelmalle
Mikään tästä ei tee LCL-editiosta toisenluokkaista. Ydinrajapinta on identtinen molemmin puolin: TPdf, TPdfView, renderöinti, lomakkeet, tekstin poiminta ja saavutettavuusrajapinnat käyttäytyvät samalla tavalla riippumatta siitä, millä IDE:llä ne on käännetty. Jokainen seurannan arvoinen ero on alustasidonnainen, ei editiosidonnainen. SAPI-puhe on vain Windowsissa, dialogit noudattavat kunkin kehyksen käytäntöjä, ja binäärin on vastattava sitä arkkitehtuuria, johon se ladataan. Kun koodausrajat, ajonaikainen lomake ja binäärin ratkaisu ovat kunnossa, loput siirrosta on sitä mekaanista työtä, jonka kääntäjä jo hoiti puolestasi
Tässä kuvatut VCL- ja LCL-editiot toimitetaan yhdessä PDFium Component -pakettina, lähdekoodeineen ja identtisine julkisine API:neineen Delphille, C++Builderille ja Lazarus/FPC:lle