Oblikovanje teksta u PDFium komponenti ide kroz jedan instalabilan objekat. ConfigureTextShaper instalira oblikovača kroz koji svaka ulazna tačka oblikovanja ide, zamenjujući i oslobađajući šta god je tamo bilo; ActiveTextShaper vraća instaliranog i stvara platformski podrazumevani pri prvoj upotrebi; ActiveTextShaperName izveštava koji backend je živ; ClearTextShaper ispušta instalaciju i dopušta da se podrazumevani ponovo stvori. Na Windows-u podrazumevani je TPdfUniscribeTextShaper. Pod Free Pascal-om postoji TPdfHarfBuzzTextShaper, koji vezuje libharfbuzz u vremenu izvršavanja pa je nedostajuća biblioteka prijavljeno stanje umesto neuspeha učitavanja
Jedno sučelje, dva backend-a koja dele posao potpuno različito. Razumevanje te asimetrije ono je što sprečava prenosiv put da proizvodi tekst oblikovan ispravno a pozicioniran pogrešno
Zašto je Windows backend jedna klasa a prenosivi tri dela?
Zato je Uniscribe četiri API-ja koji se pretvaraju da su jedan. ScriptItemize segmenira niz znakova po pismu i razrešuje dvosmjerne nivoe; ScriptShape mapira znakove u glifove; ScriptPlace računa napredovanja i ofsete; ScriptLayout stavlja rezultujuće nizove u vizuelni redosled. Backend izgrađen na njemu dakle nema više šta dodati, što je razlog što je Windows oblikovač jedna klasa sa jednom metodom
HarfBuzz pokriva srednja dva. Oblikuje i postavlja niz čije su smer i pismo koje je pozivalac već odlučio, i nema mišljenje o tome kako se pasus cepa u nizove ni u kojem redosledu se ti nizovi pojavljuju. Pa prenosivi backend snabdeva ostalo: dvosmjerni algoritam razrešuje nivoe ugrađivanja, HarfBuzz Unicode funkcije segmentiraju tekst po pismu, i nizovi se slažu u vizuelnom redosledu koji pravilo L2 UAX #9 proizvodi. Dvosmjerna polovina dovoljno je obimna da bude sopstvena jedinica, opisana u članku o UAX #9 nivoima ugrađivanja
Oblikovač ne razrešuje fontove, i to je namerno
Uniscribe čita font binarnu datoteku iz GDI device konteksta. Nema prenosivog ekvivalenta tome, i izmišljanje jednog unutar jedinice oblikovanja značilo bi odlučivati, u ime svake aplikacije, da li fontovi dolaze iz fontconfig-a, iz CoreText-a, iz fascikle fontova aplikacije, ili iz baze podataka. Pa HarfBuzz backend prima razrešivača: callback koji mapira ime fonta u TrueType ili OpenType bajtove. Vraćanje False otkazuje zahtev oblikovanja istim načinom na koji ga nečitljiv GDI font otkazuje na Windows-u
uses
FPdfTextShaping
{$IFDEF FPC}
, FPdfTextShapingHb
{$ENDIF}
;
function TFontCatalogue.Resolve(const FontName: WideString;
out FontData: TBytes): Boolean;
var
Path: string;
begin
// Vaša politika: fontconfig, CoreText, fascikla fontova aplikacije, baza podataka
Result := FLookup.TryGetValue(LowerCase(FontName), Path);
if Result then
FontData := TFile.ReadAllBytes(Path);
end;
procedure InstallShaper(Catalogue: TFontCatalogue);
begin
{$IFDEF FPC}
// Vlasništvo prelazi na jedinicu; pozovite jednom tokom pokretanja,
// pre nego što bilo šta oblikuje tekst
ConfigureTextShaper(TPdfHarfBuzzTextShaper.Create(Catalogue.Resolve));
{$ENDIF}
// Na Delphi-ju platformski podrazumevani (Uniscribe) stvara se na zahtev,
// pa instalacija uopšte nije potrebna
LogInfo('shaping backend: ' + ActiveTextShaperName);
end;
Držanje otkrivanja fontova van oblikovača ima drugu korist koja se pokazuje na serverima: isti proces može oblikovati sa ugrađenim skupom fontova koji nema nikakve veze sa onim što je instalirano na mašini, što je ono što želite kada izlaz mora biti bajt-reprodukući preko domaćina. Komponenta takođe izlaže pružaoca fontova host sistema za slučajeve gde ipak želite instalirane fontove, pokriveno u članku o pružaocu sistemskih fontova
Zapis rezultata je backend-neutralan, i klasteri su razlog
Oba backend-a pune isti TPdfShapedText: izvorni tekst, ime fonta, veličinu, bajtove fonta, niz nizova, ukupnu širinu, broj glifova i broj logičkih znakova. Svaki TPdfShapedRun nosi svoj deo u izvornom tekstu, svoju vizuelnu X poziciju, svoju širinu, svoj dvosmjerni nivo i zastavicu sdesna-ulevo, plus svoje glifove. Svaki TPdfShapedGlyph nosi identifikator glifa, napredovanje, X i Y ofsete, i klaster kojem pripada kao početak i dužinu u izvornom tekstu
Ta polja klastera ono su što čini zapis upotrebljivim umesto samo informativnim. Oblikovanje nije preslikavanje jedan-u-jedan: devanagarski slog postaje jedan glif iz četiri znaka, arapska ligatura spaja dva, i jedan znak može proizvesti nekoliko dijakritika. Bez delova klastera ne možete postaviti kursor, ispitati pogodak klikom, ni istaći izbor, jer ne možete reći kojim znakovima glif pripada. Sa njima, aritmetika je lokalna i isti kod radi za oba backend-a
var
Shaped: TPdfShapedText;
R, G: Integer;
begin
if ShapePdfText(Line, 'Noto Sans Arabic', 14, ptdAuto, Shaped) then
for R := 0 to High(Shaped.Runs) do
begin
// Nizovi već stižu u vizuelnom redosledu sa popunjenim VisualX
X := Shaped.Runs[R].VisualX;
for G := 0 to High(Shaped.Runs[R].Glyphs) do
begin
EmitGlyph(Shaped.Runs[R].Glyphs[G].GlyphID,
X + Shaped.Runs[R].Glyphs[G].OffsetX,
Shaped.Runs[R].Glyphs[G].OffsetY);
X := X + Shaped.Runs[R].Glyphs[G].Advance;
end;
end;
end;
Budžeti pripadaju zapisu opcija
TPdfTextShapingOptions nosi smer plus tri granice: najviše znakova, najviše glifova i najviše nizova, sa klasnom funkcijom Default koja puni razumne vrednosti. Granice nisu paranoja oko deformisanog ulaza; one su aritmetika. Oblikovanje se širi: font sa agresivnom kontekstualnom zamenom može emitovati više glifova nego ulaznih znakova, i pasus koji naizmenično menja pisma na svakih nekoliko znakova proizvodi niz po promeni. Dokument sklopljen da uveća oba pretvara skromni niz znakova u veliko alociranje, i servis koji oblikuje tekst iz nepoverljivih PDF-ova traži granicu koju je izabrao umesto granice koju mašina nameće
Postavljanje smera eksplicitno umesto ostavljanja na automatskom vredi raditi kad god to već znate. Automatski primenjuje pravila smera pasusa da pogodi iz prvog jakog znaka, što je ispravno za slobodan tekst i pogrešno za polje obrasca čiji je smer svojstvo polja umesto vrednosti koju je neko ukucao u njega
Vezivanje u vremenu izvršavanja, ne zavisnost izgradnje
HarfBuzz backend učitava biblioteku dinamički. To je odluka raspoređivanja sa stvarnim posledicama: jedna binarna datoteka radi na mašini sa HarfBuzz-om i na mašini bez njega, izveštavajući smanjenu mogućnost u drugom slučaju umesto da otkazuje pokretanje. Za biblioteku isporučenu drugim programerima to je jedini funkcionišući aranžman, jer ne možete zahtevati da svaki potrošač PDF komponente pribavi i uskladi verziju biblioteke oblikovanja koja mu možda ne treba
Odgovarajuće pravilo za pozivaoce je proveriti. ActiveTextShaper vraća nil kada platforma nema podrazumevani i nijedan nije konfigurisan, i ulazna tačka oblikovanja izveštava to kao nedostupnog oblikovača umesto kao neuspeh oblikovanja. To su različiti problemi i zaslužuju različite poruke: jedan je jaz raspoređivanja, drugi je problem fonta ili teksta
Instalirajte jednom, pre nego što bilo šta oblikuje
Instalacija zamenjuje i oslobađa prethodnog oblikovača, pa njeno ponavljanje bezbedno je ali beskorisno, i pozivanje dok druga nit oblikuje nikako nije bezbedno. Uradite to tokom pokretanja. Ako treba pasti nazad na platformski podrazumevani kasnije, prosledite nil, što je i način da poništite test dvojnika na kraju testa
Kada je backend jednom instaliran, merenje i prelamanje ponašaju se isto na obe platforme, pošto troše metrike nizova i glifova umesto da zovu platformu direktno; model prelamanja opisan je u članku o merenju teksta i prelamanju reči. Podržane platforme i toolchain-i za komponentu navedeni su na stranici proizvoda PDFium Delphi component