Delphi og Lazarus kompilerer den samme Object Pascal, og den overfladiske lighed er præcis det, der gør det at porte en fremviser mellem dem vildledende. De to værktøjskæder divergerer på tre punkter, der betyder noget for PDF-arbejde: den indfødte string-type er UTF-16 i Delphi og UTF-8 i en LCL-applikation; VCL og LCL er forskellige visuelle frameworks med deres egne kontroller, dialoger og formular-streaming-formater; og en Delphi-binær sigter mod Windows, mens en FPC-binær kan være på vej mod Linux eller macOS. Ingen af de forskelle viser sig ved kompileringstid. En fremviser bygget på PDFium Component, som leverer VCL- og LCL-udgaver fra ét enkelt kildetræ, vil kompilere rent under Lazarus efter en håndfuld enhedsnavne-ombytninger og nogle få {$IFDEF FPC}-blokke. Fejlene ankommer senere, når reelle data og en reel udrulning afslører de antagelser, Delphi-builden stiltiende gjorde
Fire af de antagelser tegner sig for det meste af den tabte tid: tekstkodning ved UI-grænsen, fristelsen til at vedligeholde to kopier af formularen, den måde, en indfødt motor-binær opløses på ved kørselstid, og det øjeblik, tekst-til-tale løber tør for platform, når SAPI er væk. Hver er billig at håndtere, hvis du ved, den er på vej, og dyr at jagte, hvis du ikke gør
Samme Pascal, forskellige streng-payloads
Delphis indfødte string har været UTF-16 siden 2009. Lazarus og Free Pascal bruger som standard UTF-8 i LCL-applikationer. Komponentens tekstvendte API'er taler UTF-16 gennem typen WString, som FPC-builden aliaser til WideString, så hver grænse, hvor tekst krydser mellem dit LCL-UI og PDF-motoren, er et konverteringspunkt
Konverteringerne sker automatisk ved ligefremme tildelinger, og det meste kode behøver aldrig at tænke på dem. To vaner holder kodningsbugs ude. Send tekst lige igennem uden byte-niveau-manipulation: kode, der skærer et søgeord op ved byteoffset, virker i Delphi, hvor ét Char er én UTF-16-enhed, og korrumperer multi-byte UTF-8 i LCL'en. Og test med ikke-ASCII-data fra første kørsel. Et tysk filnavn, et kyrillisk søgeord, et accentueret forfatternavn i dokumentets metadata: rene ASCII-testdata skjuler hver eneste kodningsdefekt, fordi ASCII er det ene interval, hvor UTF-8 og UTF-16 stemmer overens byte for tegn. Bugen er reel hele tiden; ASCII holder den blot usynlig, indtil en kunde i München åbner en fil, du aldrig prøvede
Én betinget blok, ikke en fork pr. IDE
Efter det første dusin IFDEF'er begynder kodebasen at føles som to projekter i ét repository, og at forke det pr. IDE ser fristende ud. Det er det forkerte træk. De ægte forskelle kollapser til én delt deklarationsblok, og en fork fordobler prisen for hver bugfix fra det tidspunkt. Hold det betingede lag så lille:
{$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 blok kompilerer identisk i begge IDE'er. Dokumenthåndtering, sidenavigation, renderingskald: TPdf og TPdfView eksponerer den samme flade i VCL- og LCL-udgaverne, så størstedelen af fremviseren aldrig ser en kompilerbetingelse. At holde det sådan er en strukturel disciplin snarere end et smart trick. Delt PDF-logik bor i enheder, der ikke trækker framework-specifikke dialoger eller paneler med sig ind. Den håndfuld ting, der ægte er forskellige, såsom printdialoger og filvælgere med deres platformskonventioner, gemmer sig bag en tynd grænseflade implementeret én gang pr. framework. IFDEF-blokken bliver det ene sted, fremtidig platformsdivergens har lov til at lande, i stedet for at kompilerdirektiver lækker ud over fyrre enheder
Byg formularen i kode, ikke i to designere
Formular-streaming er der, hvor dual-IDE-projekter stille rådner. En .dfm og en .lfm, der påstår at beskrive den samme formular, driver fra hinanden egenskab for egenskab, indtil de to builds opfører sig forskelligt af grunde, ingen kan diffe, fordi de to filer end ikke er i samme format. At konstruere fremviseren ved kørselstid omgår hele problemet. Der er én konstruktørsekvens, versionsstyret som almindelig kode, og den læses ens på begge platforme:
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; // åbner dokumentet; PageCount gyldig herefter
end;
end;
Den præcise rækkefølge af de tildelinger betyder mindre end den ene linje, der udfører det reelle arbejde. PdfView.Pdf := Pdf binder den visuelle kontrol til dokumentkomponenten, og fra det punkt reagerer sidenavigation gennem PageNumber og fit-adfærd gennem FitMode identisk under VCL og LCL. Én særhed på tværs af frameworks er værd at kende, før en bruger rapporterer den som en bug: at tildele Zoom i hånden snapper FitMode tilbage til pfmNone på begge frameworks. Så hvis din værktøjslinje behandler "fit width" som en klæbende præference, skal du gentildele fit-tilstanden efter enhver programmatisk zoom, ellers holder præferencen stille op med at klæbe, første gang kode rører zoomniveauet
Den binære fil, IDE'en aldrig advarede dig om
Komponenten wrapper PDFium-motoren, som leveres som en indfødt platformsbinær, og den binære fil er kilden til stort set hver eneste "works in the IDE, fails from the installed shortcut"-rapport. Tre regler tegner sig for de fleste af dem. Bitness skal matche præcist. En 32-bit eksekverbar kan ikke indlæse et 64-bit pdfium-bibliotek, og beskeden, OS'et sender tilbage ("module not found" på nogle Windows-versioner), vildleder aktivt, fordi filen sidder lige der ved siden af den eksekverbare. Opløs biblioteksstien relativt til den eksekverbare, aldrig arbejdsmappen; en IDE-start og en shell-start adskiller sig præcis på det punkt, hvilket er grunden til, at bugen gemmer sig under udvikling. Og fang en mislykket indlæsning, før det første dokument åbnes, og rapportér den så med den forventede sti og arkitektur udskrevet. En supportsag, der lyder "PDFium 64-bit binary missing at <path>", lukkes på minutter. Én, der lyder "viewer crashes on startup", bliver til en uge frem og tilbage
Versionér motorens binære fil sammen med den eksekverbare, mens du er i gang. PDFium bevæger sig hurtigt, og et installationsprogram, der opdaterer applikationen, men efterlader et forældet bibliotek på disk, producerer nedbrud, ingen på dit kontor kan genskabe, af den simple grund, at hver maskine på dit kontor tilfældigvis rummer det matchende par. Behandl biblioteket som en del af build-artefaktet, med det samme installationsprogram, det samme versionsstempel og den samme rollback-sti som den eksekverbare, den indlæser
Registrering af komponenter i Lazarus IDE
Kørselstidskonstruktion kræver overhovedet ingen design-time-registrering, hvilket er den reneste opsætning for en fremviser, der bygger sit eget UI i kode. Når du rent faktisk vil have komponenterne på Lazarus-paletten til design-time-arbejde, installér pakken, og lad dens dedikerede registreringsenhed, PDFiumLazReg i Lib/FPC/PDFiumLaz.lpk, klare det. Den enhed er markeret design-time med vilje: den refererer til IDE-egenskabsredigerings-grænseflader, der aldrig må linkes ind i din leverede eksekverbare
Får du det galt, er symptomet en applikation, der uforklarligt afhænger af IDE-pakker, hvilket viser sig som en udrulningsfejl på den første kundemaskine, der aldrig har haft Lazarus installeret
Tale og skærmlæsere uden for Windows
Tekst-til-tale er den ene funktion, hvor cross-platform-historien går i stykker, og den går i stykker ved operativsystemet, ikke komponenten. SAPI, den sædvanlige TTS-backend på Windows, findes kun på Windows. En Lazarus-build, der stadig sigter mod Windows, beholder fuldt SAPI-output og den samme NVDA-kompatible adfærd, som Delphi-originalen havde, så en Windows-til-Windows-portering mister intet her, og en NVDA-bruger kan ikke kende forskel på de to builds
Et Linux- eller macOS-mål er en anden sag. Der er ingen SAPI at kalde, så lydoutputtet skal omkobles til en indfødt taletjeneste, mens læse-API'erne over den bliver stående. Den opsplitning er argumentet for at placere tale bag en grænseflade fra første commit: læserækkefølge-analysen og den ordsporende markør er platformsneutrale og overføres uændret, og kun det tynde lag, der rent faktisk producerer lyd, skal ændres pr. platform. Artiklen om den tilgængelige fremviser dækker den læsemekanik i dybden
En paritetstjekliste, før du kalder porteringen færdig
Den følgende gennemgang har fanget reelle regressioner, listet nogenlunde i den rækkefølge, fejl har en tendens til at dukke op i. Åbn et dokument, hvis sti indeholder ikke-ASCII-tegn. Søg efter et udtryk med ikke-ASCII-tegn, og bekræft, at træffene fremhæves, hvor de skal. Afprøv musehjul-scroll, træk-markering og tastaturets sidenavigation på hvert widget-sæt, du leverer, fordi fokushåndtering og hjul-adfærd er de mest widget-sæt-afhængige hjørner af LCL'en. Tjek rendering ved 100%, 150% og 200% skærmskalering. Til sidst, kør den installerede build, ikke IDE-builden, på en maskine, der aldrig har haft IDE'en på sig, fordi det er den eneste test, der afprøver binær opløsning ærligt. Alt andet kan bestå, mens netop den fejler i stilhed
Rendering-gennemstrømning overføres uændret mellem de to udgaver, så cache-tilgangen fra artiklen om render-cache og zoom-ydelse gælder for LCL-fremviseren nøjagtigt, som den er skrevet for VCL-udgaven
Intet af dette gør LCL-udgaven til en ringere én. Kernefladen er identisk på begge sider: TPdf, TPdfView, rendering, formularer, tekstudtræk og tilgængeligheds-API'erne opfører sig ens, uanset hvilken IDE der kompilerede dem. Hver forskel, der er værd at spore, er platformsbunden snarere end udgavebunden. SAPI-tale er kun til Windows, dialoger følger hvert frameworks konventioner, og den binære fil skal matche den arkitektur, den indlæses i. Få kodningsgrænserne, kørselstidsformularen og den binære opløsning rigtig, og resten af porteringen er det mekaniske arbejde, compileren allerede har klaret for dig
VCL- og LCL-udgaverne beskrevet her leveres sammen som PDFium Component, med kildekode og identiske offentlige API'er til Delphi, C++Builder og Lazarus/FPC