Tekninen artikkeli

Liitettävä tekstimuotoilu: Uniscribe ja HarfBuzz Delphissä

Tekstimuotoilu PDFium-komponentissa kulkee yhden asennettavan objektin kautta. ConfigureTextShaper asentaa muotoilijan, jonka kautta jokainen muotoilusisääntulopiste ohjautuu, korvaten ja vapauttaen sen, mitä siellä oli; ActiveTextShaper palauttaa asennetun ja luo alustan oletuksen ensimmäisellä käytöllä; ActiveTextShaperName raportoi, mikä tausta on käynnissä; ClearTextShaper pudottaa asennuksen ja antaa oletuksen luoda uudelleen. Windowsilla oletus on TPdfUniscribeTextShaper. Free Pascalilla on TPdfHarfBuzzTextShaper, joka sitoo libharfbuzzin ajonaikaisesti, joten puuttuva kirjasto on raportoitu tila eikä latausvika

Liitettävän tekstimuotoilun arkkitehtuuri PDFium Delphi -komponentissa: ConfigureTextShaper, ActiveTextShaper ja ClearTextShaper hallitsevat yhtä asennettua taustaa, Uniscribe Windowsilla ja ajonaikaisesti sidottu HarfBuzz Free Pascalilla
Jokainen muotoilukutsu ohjautuu yhden asennetun muotoilijaobjektin kautta, alustan oletuksella jokaisella kohteella

Yksi rajapinta, kaksi taustaa, jotka jakavat työn täysin eri tavalla. Tuo asymmetria ymmärtäminen on se, mikä estää kannettavaa polkua tuottamasta tekstiä, joka on muotoiltu oikein ja sijoitettu väärin

Miksi Windows-tausta on yksi luokka ja kannettava kolme osaa?

Koska Uniscribe on neljä API:a, jotka teeskentelevät olevansa yksi. ScriptItemize jakaa merkkijonon segmentteihin skriptin mukaan ja ratkaisee kaksisuuntaiset tasot; ScriptShape kartoittaa merkit glyfeiksi; ScriptPlace laskee edut ja siirtymät; ScriptLayout laittaa tuloksena syntyneet ajot visuaaliseen järjestykseen. Siihen rakennetulla taustalla ei siksi ole mitään lisättävää, mistä syystä Windows-muotoilija on yksi luokka yhdellä metodilla

HarfBuzz kattaa kaksi keskimmäistä. Se muotoilee ja sijoittaa ajon, jonka suunnan ja skriptin kutsuja on jo päättänyt, eikä sillä ole mielipidettä siitä, miten kappale jakautuu ajoiksi eikä missä järjestyksessä kyseiset ajot ilmestyvät. Kannettava tausta toimittaa siksi loput: kaksisuuntainen algoritmi ratkaisee upotustasot, HarfBuzzin Unicode-funktiot jakavat tekstin skriptin mukaan, ja ajot asetetaan visuaaliseen järjestykseen, jonka UAX #9 -sääntö L2 tuottaa. Kaksisuuntainen puolisko on tarpeeksi merkittävä ollakseen oma yksikkönsä, kuvattuna artikkelissa UAX #9 -upotustasot

Muotoiluputken vertailu PDF-tekstille: Uniscribe toimittaa ScriptItemize-, ScriptShape-, ScriptPlace- ja ScriptLayout-vaiheet yhden luokan sisällä, kun taas HarfBuzz kattaa vain muotoilun ja sijoittelun komponentin omien UAX #9 -vaiheiden ympärillä
Uniscribe kattaa kaikki neljä vaihetta; kannettavan polun on toimitettava itemisointi ja visuaalinen järjestys itse

Muotoilija ei ratkaise fontteja, ja se on tahallista

Uniscribe lukee fonttibinäärin GDI-laitekontekstista. Sille ei ole kannettavaa vastaista, ja sellaisen keksiminen muotoiluyksikön sisällä tarkoittaisi päättämistä jokaisen sovelluksen puolesta, tulevatko fontit fontconfigista, CoreTextistä, sovelluksen fonttikansiosta vai tietokannasta. HarfBuzz-tausta ottaa siksi ratkaisijan: callbackin, joka kartoittaa fonttinimen TrueType- tai OpenType-tavuihin. Arvon False palauttaminen epäonnistaa muotoilupyynnön samalla tavalla kuin lukukelvoton GDI-fontti epäonnistaa sen Windowsilla

uses
  FPdfTextShaping
{$IFDEF FPC}
  , FPdfTextShapingHb
{$ENDIF}
  ;

function TFontCatalogue.Resolve(const FontName: WideString;
  out FontData: TBytes): Boolean;
var
  Path: string;
begin
  // Politiikkasi: fontconfig, CoreText, sovelluksen fonttikansio, tietokanta
  Result := FLookup.TryGetValue(LowerCase(FontName), Path);
  if Result then
    FontData := TFile.ReadAllBytes(Path);
end;

procedure InstallShaper(Catalogue: TFontCatalogue);
begin
{$IFDEF FPC}
  // Omistusoikeus siirtyy yksikölle; kutsu kerran käynnistyksessä,
  // ennen kuin mikään muotoilee tekstiä
  ConfigureTextShaper(TPdfHarfBuzzTextShaper.Create(Catalogue.Resolve));
{$ENDIF}
  // Delphillä alustan oletus (Uniscribe) luodaan tarpeen mukaan,
  // joten asennusta ei tarvita lainkaan
  LogInfo('shaping backend: ' + ActiveTextShaperName);
end;

Fonttien löytämisen pitäminen muotoilijan ulkopuolella tarjoaa toisen edun, joka näkyy palvelimissa: sama prosessi voi muotoilla upotetulla fonttijoukolla, jolla ei ole mitään tekemistä sen kanssa, mitä koneelle on asennettu, mikä on sitä, mitä haluat, kun tulosteen on oltava tavuilta toistettava isäntien välillä. Komponentti paljastaa myös isäntäjärjestelmän fonttitoimittajan tapauksille, joissa haluatkin asennetut fontit, mistä kerrotaan artikkelissa järjestelmän fonttitoimittaja

Tulostietue on taustaneutraali, ja klusterit ovat syy

Molemmat taustat täyttävät saman TPdfShapedTextin: lähdetekstin, fonttinimen, koon, fonttitavut, ajojen taulukon, kokonaisleveyden, glyfien määrän ja loogisten merkkien määrän. Jokainen TPdfShapedRun kantaa jaksonsa lähdetekstissä, visuaalisen X-sijaintinsa, leveytensä, kaksisuuntaisen tasonsa ja oikealta vasemmalle -lipun sekä glyfinsä. Jokainen TPdfShapedGlyph kantaa glyfitunnisteen, edun, X- ja Y-siirtymät ja klusterin, johon se kuuluu, alku- ja pituusarvona lähdetekstissä

Kyseiset klusterikentät ovat se, mikä tekee tietueesta käytettävän eikä ainoastaan informatiivisen. Muotoilu ei ole yksi yhteen -kartoitus: devanagarin tavu tulee yhdeksi glyfiksi neljästä merkistä, arabialainen ligatuuri yhdistää kaksi, ja yksittäinen merkki voi tuottaa useita diakriitteja. Ilman klusterijaksoja et voi sijoittaa kursoria, tehdä osumatestiä napsautukselle tai korostaa valintaa, koska et voi sanoa, mihin merkkeihin glyfi kuuluu. Niiden kanssa aritmetiikka on lokaalia ja sama koodi toimii molemmilla taustoilla

Glyfiklusterijaksot kohteessa TPdfShapedText: yksi devanagarin tavu glyfi neljästä merkistä, arabialainen ligatuuri kahdesta ja perusmerkki plus diakriitti yhdestä merkistä, jokainen kartoitettuna takaisin arvojen ClusterStart ja ClusterLength kautta
Klusterijaksot kartoittavat jokaisen glyfin takaisin sen lähdemerkkeihin, joten kursorit, osumatestit ja valinnat toimivat
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
      // Ajot saapuvat jo visuaalisessa järjestyksessä VisualX täytettynä
      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;

Budjetit kuuluvat asetustietueeseen

TPdfTextShapingOptions kantaa suunnan plus kolme kattoa: enimmäismerkit, enimmäisglyfit ja enimmäisajot, ja luokkafunktio Default täyttää järkevät arvot. Katot eivät ole paranoiaa vääristynyttä syötettä kohtaan; ne ovat aritmetiikkaa. Muotoilu laajenee: fontti, jolla on aggressiivinen kontekstuaalinen korvaus, voi emittoida enemmän glyfejä kuin syötemerkkejä, ja kappale, joka vaihtelee skriptejä muutaman merkin välein, tuottaa ajon per vaihto. Dokumentti, joka on koottu maksimoimaan molemmat, muuntaa vaatimattoman merkkijonon suureksi varaukseksi, ja palvelu, joka muotoilee tekstiä luotamattomista PDF:istä, tarvitsee rajan, jonka se valitsi, eikä rajaa, jonka kone määrää

Suunnan asettaminen eksplisiittisesti sen jättämisen sijaan automaattiseksi kannattaa tehdä aina, kun tiedät sen jo. Automaattinen soveltaa kappaleen suunnan sääntöjä arvatakseen ensimmäisen vahvan merkin perusteella, mikä on oikein vapaalle tekstille ja väärin lomakekentälle, jonka suunta on kentän ominaisuus eikä sen arvon, jonka joku kirjoitti siihen

Ajonaikainen sidonta, ei käännösaikainen riippuvuus

HarfBuzz-tausta lataa kirjaston dynaamisesti. Se on jakelupäätös todellisilla seurauksilla: yksi binääri toimii koneella, jossa on HarfBuzz, ja koneella, jossa ei, raportoiden pienennetyn kyvykkyyden jälkimmäisessä tapauksessa käynnistymisen epäonnistumisen sijaan. Muille kehittäjille toimitettavalle kirjastolle se on ainoa toimiva järjestely, koska et voi vaatia jokaisen PDF-komponentin kuluttajan hankkivan ja versiota täsmäävää muotoilukirjastoa, jota he eivät välttämättä tarvitse

Vastaava sääntö kutsujille on tarkistaa. ActiveTextShaper palauttaa arvon nil, kun alustalla ei ole oletusta eikä mitään konfiguroitu, ja muotoilusisääntulopiste raportoi sen ei-saatavissa olevana muotoilijana eikä muotoiluvikana. Nuo ovat eri ongelmia ja ansaitsevat eri viestit: toinen on jakeluaukko, toinen on fontti- tai tekstiongelma

Asenna kerran, ennen kuin mikään muotoilee

Asennus korvaa ja vapauttaa aiemman muotoilijan, joten sen kutsuminen toistuvasti on turvallista mutta turhaa, ja sen kutsuminen sillä aikaa, kun toinen säie muotoilee, ei ole lainkaan turvallista. Tee se käynnistyksen aikana. Jos sinun on palattava alustan oletukseen myöhemmin, välitä arvo nil, mikä on myös tapa purkaa testikaksoisolion testin lopussa

Kun tausta on asennettu, mittaus ja rivitys käyttäytyvät samalla tavalla molemmilla alustoilla, koska ne kuluttavat ajo- ja glyfimetriikat sen sijaan että kutsuisivat alustaa suoraan; rivitustapa on kuvattu artikkelissa tekstimittaus ja sanorivitätys. Komponentin tuetut alustat ja työkaluketjut on lueteltu PDFium Delphi -komponentin tuotesivulla