Delphi og Lazarus kompilerer den samme Object Pascal, og den overflatiske likheten er nettopp det som gjør det bedragersk å portere en fremviser mellom dem. De to verktøykjedene divergerer på tre steder som betyr noe for PDF-arbeid: den native string-typen er UTF-16 i Delphi og UTF-8 i en LCL-applikasjon; VCL og LCL er forskjellige visuelle rammeverk med sine egne kontroller, dialoger og skjema-strømmeformater; og en Delphi-binærfil retter seg mot Windows mens en FPC-binærfil kan være på vei mot Linux eller macOS. Ingen av disse forskjellene viser seg ved kompileringstidspunktet. En fremviser bygget på PDFium Component, som leverer VCL- og LCL-utgaver fra ett enkelt kildetre, vil kompilere rent under Lazarus etter en håndfull enhetsnavn-bytter og noen få {$IFDEF FPC}-blokker. Feilene ankommer senere, når ekte data og en ekte utrulling avslører antagelsene Delphi-builden stille gjorde
Fire av disse antagelsene står for mesteparten av den tapte tiden: tekstkoding ved UI-grensen, fristelsen til å vedlikeholde to kopier av skjemaet, måten en native motor-binærfil løses opp ved kjøretid på, og øyeblikket tekst-til-tale går tom for plattform når SAPI er borte. Hver av dem er billig å håndtere hvis du vet den kommer, og dyr å jage etter hvis du ikke gjør det
Samme Pascal, forskjellige strenglaster
Delphis native string har vært UTF-16 siden 2009. Lazarus og Free Pascal bruker UTF-8 som standard i LCL-applikasjoner. Komponentens tekstvendte API-er snakker UTF-16 gjennom WString-typen, som FPC-builden aliaser til WideString, så hver grense hvor tekst krysser mellom LCL-UI-et ditt og PDF-motoren, er et konverteringspunkt
Konverteringene skjer automatisk i grei tilordninger, og det meste av koden trenger aldri å tenke på dem. To vaner holder kodingsfeilene unna. Send tekst rett gjennom uten manipulasjon på bytenivå: kode som skjærer opp et søkeord etter byteforskyvning, fungerer i Delphi, hvor én Char er én UTF-16-enhet, og ødelegger multi-byte UTF-8 i LCL. Og test med ikke-ASCII-data fra første kjøring. Et tysk filnavn, et kyrillisk søkeord, et forfatternavn med aksenttegn i dokumentmetadataen: rent ASCII-testdata skjuler hver eneste kodingsfeil, fordi ASCII er det ene området hvor UTF-8 og UTF-16 er enige byte for tegn. Feilen er reell hele tiden; ASCII holder den bare usynlig helt til en kunde i München åpner en fil du aldri prøvde
Én betinget blokk, ikke en fork per IDE
Etter det første dusinet IFDEF-er begynner kodebasen å føles som to prosjekter i ett repository, og å forke den per IDE ser fristende ut. Det er feil grep. De genuine forskjellene faller sammen til én delt deklarasjonsblokk, og en fork dobler kostnaden av hver feilfiks fra da av. Hold det betingede laget så lite som dette:
{$IFDEF FPC}
uses
LCLType, Forms, Graphics, Controls;
type
WString = WideString; // komponentens tekst-API-er er UTF-16
TBytes = array of Byte;
{$ELSE}
uses
Winapi.Windows, Vcl.Forms, Vcl.Graphics, Vcl.Controls;
{$ENDIF}
Alt under den blokken kompilerer identisk i begge IDE-ene. Dokumenthåndtering, sidenavigering, rendringskall: TPdf og TPdfView eksponerer den samme overflaten i VCL- og LCL-utgavene, så hoveddelen av fremviseren ser aldri en kompilatorbetingelse. Å holde det slik er strukturell disiplin fremfor et smart triks. Delt PDF-logikk bor i enheter som ikke drar inn noen rammeverk-spesifikke dialoger eller paneler. Den håndfullen ting som genuint skiller seg, som utskriftsdialoger og filvelgere med sine plattformkonvensjoner, gjemmer seg bak et tynt grensesnitt implementert én gang per rammeverk. IFDEF-blokken blir det ene stedet fremtidig plattformdivergens har lov til å lande, i stedet for å lekke kompilatordirektiver over førti enheter
Bygg skjemaet i kode, ikke i to designere
Skjema-strømming er der dobbelt-IDE-prosjekter stille råtner. En .dfm og en .lfm som hevder å beskrive det samme skjemaet, driver fra hverandre egenskap for egenskap helt til de to buildene oppfører seg forskjellig av grunner ingen kan diffe, fordi de to filene ikke engang er i samme format. Å konstruere fremviseren ved kjøretid går utenom hele problemet. Det finnes én konstruktørsekvens, versjonskontrollert som vanlig kode, og den leses likt på begge plattformer:
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; // åpner dokumentet; PageCount gyldig etter dette
end;
end;
Den eksakte rekkefølgen til de tilordningene betyr mindre enn den ene linjen som gjør det reelle arbeidet. PdfView.Pdf := Pdf binder det visuelle kontrollelementet til dokumentkomponenten, og fra det punktet svarer sidenavigering gjennom PageNumber og tilpasningsatferd gjennom FitMode identisk under VCL og LCL. Ett kvirk på tvers av rammeverk er verdt å kjenne til før en bruker rapporterer det som en feil: å tilordne Zoom for hånd smeller FitMode tilbake til pfmNone på begge rammeverk. Så hvis verktøylinjen din behandler "tilpass bredde" som en klebrig preferanse, må du tilordne tilpasningsmodusen på nytt etter enhver programmatisk zoom, ellers slutter preferansen stille å henge ved første gang koden rører zoom-nivået
Binærfilen IDE-en aldri advarte deg om
Komponenten pakker inn PDFium-motoren, som leveres som en native plattform-binærfil, og den binærfilen er kilden til nesten hver eneste "fungerer i IDE-en, feiler fra den installerte snarveien"-rapport. Tre regler står for de fleste av dem. Bit-bredden må matche eksakt. En 32-bits kjørbar fil kan ikke laste et 64-bits pdfium-bibliotek, og meldingen OS-et gir tilbake ("modul ikke funnet" på enkelte Windows-versjoner) villeder aktivt, fordi filen sitter rett der ved siden av den kjørbare filen. Løs bibliotekstien relativt til den kjørbare filen, aldri arbeidsmappen; en IDE-oppstart og en skalloppstart skiller seg akkurat på det punktet, og det er derfor feilen gjemmer seg under utvikling. Og fang en mislykket lasting før det første dokumentet åpnes, og rapporter den så med forventet sti og arkitektur stavet ut. En supportsak som lyder "PDFium 64-bits binærfil mangler ved <sti>" lukkes på minutter. Én som lyder "fremviser krasjer ved oppstart" blir til en uke med frem og tilbake
Versjonér motor-binærfilen sammen med den kjørbare filen mens du først er i gang. PDFium beveger seg raskt, og et installasjonsprogram som oppdaterer applikasjonen, men lar et foreldet bibliotek ligge igjen på disk, produserer krasj ingen på kontoret ditt kan gjenskape, av den enkle grunn at hver maskin på kontoret ditt tilfeldigvis holder det matchende paret. Behandle biblioteket som en del av byggeartefakten, med samme installasjonsprogram, samme versjonsstempel og samme tilbakerullingssti som den kjørbare filen den lastes inn i
Å registrere komponenter i Lazarus-IDE-en
Kjøretidskonstruksjon trenger ingen designtidsregistrering i det hele tatt, noe som er det reneste oppsettet for en fremviser som bygger sitt eget UI i kode. Når du faktisk vil ha komponentene på Lazarus-paletten for designtidsarbeid, installer pakken og la dens dedikerte registreringsenhet, PDFiumLazReg i Lib/FPC/PDFiumLaz.lpk, ta seg av det. Den enheten er merket designtid med hensikt: den refererer IDE-egenskapsredigerer-grensesnitt som aldri må lenkes inn i den kjørbare filen du sender ut
Gjør dette feil, og symptomet er en applikasjon som uforklarlig avhenger av IDE-pakker, noe som viser seg som en utrullingsfeil på den første kundemaskinen som aldri har hatt Lazarus installert
Tale og skjermlesere utenfor Windows
Tekst-til-tale er den ene funksjonen hvor plattformuavhengigheten bryter sammen, og den bryter sammen ved operativsystemet, ikke ved komponenten. SAPI, den vanlige TTS-bakenden på Windows, eksisterer bare på Windows. En Lazarus-build som fortsatt retter seg mot Windows, beholder full SAPI-utdata og den samme NVDA-kompatible atferden Delphi-originalen hadde, så en Windows-til-Windows-portering mister ingenting her, og en NVDA-bruker kan ikke skille de to buildene fra hverandre
Et Linux- eller macOS-mål er en annen sak. Det finnes ingen SAPI å kalle, så lydutdataen må kobles om til en native taletjeneste mens lese-API-ene over den blir stående. Det skillet er argumentet for å plassere tale bak et grensesnitt fra første commit: analysen av leserekkefølge og ordsporingsmarkøren er plattformnøytrale og bæres over urørt, og bare det tynne laget som faktisk produserer lyd, må endres per plattform. Artikkelen om tilgjengelig leser dekker det lesemaskineriet i dybden
En paritetssjekkliste før du erklærer porteringen ferdig
Følgende gjennomgang har fanget reelle regresjoner, listet omtrent i den rekkefølgen feil har en tendens til å dukke opp i. Åpne et dokument hvis sti inneholder ikke-ASCII-tegn. Søk etter et begrep med ikke-ASCII-tegn og bekreft at treffene uthever der de skal. Utøv musehjul-rulling, dra-utvalg og tastaturs sidenavigering på hvert kontrollelement-sett du sender, fordi fokushåndtering og hjulatferd er de mest kontrollelement-sett-avhengige hjørnene av LCL-en. Sjekk rendring ved 100 %, 150 % og 200 % skjermskalering. Til slutt, kjør den installerte builden, ikke IDE-builden, på en maskin som aldri har hatt IDE-en på seg, fordi det er den eneste testen som utøver binærfilløsing ærlig. Alt annet kan bestå mens akkurat den stille feiler
Rendringsgjennomstrømning bæres over mellom de to utgavene uendret, så bufringstilnærmingen fra artikkelen om rendringsbuffer og zoom-ytelse gjelder for LCL-fremviseren nøyaktig slik den er skrevet for VCL-en
Ingenting av dette gjør LCL-utgaven til en dårligere en. Kjerneoverflaten er identisk på begge sider: TPdf, TPdfView, rendring, skjemaer, tekstuthenting, og tilgjengelighets-API-ene oppfører seg likt uansett hvilken IDE som kompilerte dem. Hver forskjell verdt å spore, er plattformbundet fremfor utgavebundet. SAPI-tale er bare for Windows, dialoger følger hvert rammeverks konvensjoner, og binærfilen må matche arkitekturen den lastes inn i. Få kodingsgrensene, kjøretidsskjemaet og binærfilløsingen riktig, og resten av porteringen er det mekaniske arbeidet kompilatoren allerede har tatt seg av for deg
VCL- og LCL-utgavene beskrevet her, leveres sammen som PDFium Component, med kildekode og identiske offentlige API-er for Delphi, C++Builder og Lazarus/FPC