Puuttuva glyfi PDF:ssä ei ole virhe. Tuottaja pyytää merkkiä, jota valittu fontti ei voi kartoittaa, fontti palauttaa glyfi-indeksin nolla, ja tuloksena syntyvä tiedosto on rakenteellisesti pätevä, avautuu kaikkialla ja näyttää tyhjän laatikon siellä, missä nimen tai summan pitäisi olla. Kukaan tuottavassa putkessa ei saa tietää. Vastaanottaja saa. HotPDF sulkee tuon silmukan ominaisuudella TrackUnresolvedGlyphs: kytke se päälle, ja tekstipiirtopolku tallentaa jokaisen koodipisteen, jonka glyfihaku ratkeaa indeksiin nolla, laukaisemalla OnUnresolvedGlyph-tapahtuman kerran jokaista uniikkia löydöstä kohden koodipisteellä, fontilla, jolla se epäonnistui, skriptillä, johon se kuuluu, ja ehdotuksella fontteja, jotka kattaisivat sen
Havaitseminen on puolet vastauksesta. Toinen puoli on SetFontFallbackChain, joka rekisteröi järjestetyn fonttiluettelon skriptiä kohden, joten tavalliset tapaukset ratkeavat itsestään ja vain aidot aukot päätyvät käsittelijääsi. Yhdessä ne muuntavat virheluokan, josta asiakkaat raportoivat aiemmin, käännösaikaiseksi tarkistukseksi
Miksi puuttuva glyfi ei heitä mitään?
Koska ISO 32000 ei aseta tuottajalle velvollisuutta varmistaa kattavuutta, ja glyfi-indeksi nolla on laillinen glyfi. Se on .notdef, jonka ääriviivan fonttisuunnittelija valitsee: yleensä tyhjä tai ontto suorakulmio, joskus ei mitään. Katselin, joka piirtää sen, käyttäytyy oikein. Tekstiuutto voi jopa palauttaa oikeat merkit, koska /ToUnicode-kartoitus kirjoitetaan lähdetekstistä ääriviivojen sijaan, joten automatisoitu kierrostarkistus läpäisee mielellään dokumentin, jonka näkyvässä tekstissä on aukkoja
Käytännön seuraus on, että kattavuus on tarkistettava piirtämisen hetkellä, kun kirjasto tietää yhä, mitä koodipistettä pyydettiin ja minkä glyfin fontti oikeasti tarjosi. Sen jälkeen tieto on poissa
Havaitsejan on tarkkailtava subset-tilaa, ei laitekontekstia
Tässä ensimmäinen toteutus meni väärin, ja syy kannattaa ymmärtää, koska se pätee mihin tahansa tekstiputkeen kiinnitettyyn kattavuustarkistukseen. HotPDF:llä on kaksi tekstipolkua. Ensimmäinen emittoi rekisteröidyn Unicode TrueType -fontin kautta muistiin rakennetulla merkistökartalla, joka rakennettiin rekisteröintihetkellä. Toinen on perinteinen GDI-polku, joka luo tuoreen laitekontekstin ja fonttikahvan jokaista merkkiajoa kohden
Kattavuuden arviointi GDI-polusta on toivotonta. Sen kartoitus ei ole se kartoitus, joka päätyy emittoituun sisältövirtaan, eikä nämä kaksi ole synkronoituja, joten havaitseja, joka lukee GDI-tuloksia, raportoi koko tulostettavan ASCII-alueen ratkaisemattomaksi. Auktoritatiivinen vastaus asuu rekisteröidyssä fontissa: merkistökartassa, jonka RegisterUnicodeTTF jäsentää ja jota kysytään funktiolla GetUnicodeGlyphForCodepoint. Havaitseja on siksi portitettu subset-valmiin tilan mukaan, ei mihinkään GDI-ehtoon, eikä se yksinkertaisesti aja dokumenteissa, jotka eivät koskaan rekisteröineet Unicode-fonttia, mikä on oikein, koska kyseiset dokumentit ovat joka tapauksessa rajoitettuja vakioenkoodauksiin
Toinen ansa istuu sen vieressä. Fontin GDI-perhenimi ja rekisteröintihetkellä fonttibinääristä poimittu PostScript-nimi ovat eri merkkijonoja, eikä tavalla, jota voi normalisoida: perhe nimeltä Arial Unicode MS kantaa PostScript-nimeä ArialMT. Mikä tahansa portti, kirjoitettuna muodossa ”onko juuri valittu fontti se, jonka rekisteröimme”, verrattuna nimellä, on kuollutta koodia, joka ei koskaan laukea. Portittaa tilan mukaan, ei koskaan fonttinimien mukaan
Älä testaa glyfihavaitsejaa emojilla
Ilmeinen testitapaus on hymiönaama, ja se vakuuttaa sinut siitä, että havaitseja on rikki. Yleiset emojikoodipisteet astral-tasoissa ratkeavat yksityiskäytön synteesipolun kautta, joka kartoittaa ne suoraan glyfi-indeksiin, joten ne eivät koskaan saavuta yleistä kattavuushaaraa. Havaitseja käyttäytyy oikein, ja testi mittaa väärää polkua
Käytä sen sijaan osoittamatonta koodipistettä. U+0378 on pysyvästi varaamatta Unicode-datassa, joten mikään fontti ei voi laillisesti kartoittaa sitä, ja se harjoittaa täsmälleen sen haaran, jonka haluat varmentaa. Tuo ero sen välillä, onko ”ominaisuus rikki” vai ”testi valitsi syötteen, joka ohittaa ominaisuuden”, maksaa oikeita tunteja, ja osoittamattomat koodipisteet ovat halvin tapa välttää se
type
TCoverageAudit = class
private
FFindings: TStringList;
public
procedure Handle(Sender: TObject;
const Info: THPDFUnresolvedGlyphInfo);
property Findings: TStringList read FFindings;
end;
procedure TCoverageAudit.Handle(Sender: TObject;
const Info: THPDFUnresolvedGlyphInfo);
begin
// Laukeaa kerran per uniikki koodipiste, ei kerran per esiintymä
FFindings.Add(Format('U+%.4X missing in %s (script %d), try: %s',
[Info.CodePoint, String(Info.FontName), Ord(Info.Script),
String(Info.SuggestedFonts)]));
end;
// Kytkeminen tuottavaan työhön
Pdf := THotPDF.Create(nil);
try
Pdf.TrackUnresolvedGlyphs := True;
Pdf.OnUnresolvedGlyph := Audit.Handle;
Pdf.RegisterUnicodeTTF('C:\Windows\Fonts\arial.ttf');
Pdf.BeginDoc;
Pdf.CurrentPage.SetFont('Arial', [], 11);
Pdf.CurrentPage.TextOut(50, 720, 0, CustomerName);
Pdf.EndDoc;
if Audit.Findings.Count > 0 then
// Epäonnista työ sen sijaan että toimittaisit sivun laatikoilla
raise Exception.Create(Audit.Findings.Text);
finally
Pdf.Free;
end;
Fallback-ketjut ovat skriptiä kohden, ei fonttia kohden
Syy sille, että fallback rajataan skriptin eikä lähdefontin mukaan, on se, että kattavuusaukot kasautuvat kirjoitusjärjestelmän mukaan. Latinalaisesta tekstifontista puuttuvat devanagari, thai, han ja emoji, kaikki yhtä aikaa, ja jokaisen korvaaja on eri fontti. Yhden ketjun julistaminen skriptiä kohden kuvaa siksi todellista käyttöönottoa: yksi latinalainen fontti runkotekstiin, yksi CJK-fontti, yksi emoji-fontti, yksi yleisvarmuus
// THPDFFontScript kattaa arvot hfsCommon, hfsLatin, hfsGreek, hfsCyrillic,
// hfsHebrew, hfsArabic, hfsIndic, hfsSoutheastAsian, hfsCJK, hfsKana,
// hfsHangul, hfsEmoji ja hfsOther
Pdf.SetFontFallbackChain(hfsCJK,
['Microsoft YaHei', 'SimSun', 'Yu Gothic']);
Pdf.SetFontFallbackChain(hfsArabic, ['Segoe UI', 'Arial']);
Pdf.SetFontFallbackChain(hfsEmoji, ['Segoe UI Emoji']);
Pdf.SetFontFallbackChain(hfsOther, ['Arial Unicode MS']);
Fallback ja havaitseminen ovat toisiaan täydentäviä, eivät vaihtoehtoisia. Ketjut käsittelevät kattavuuden, jonka ennakoit; havaitseja raportoi kattavuuden, jota et ennakoinut, mikä järjestelmässä, joka käsittelee mielivaltaista asiakasdataa, on kiinnostava puolisko. Huomaa, että fontin korvaaminen muuttaa metriikoita, joten kappale, joka putoaa varalle, voi kierrättää rivinsä uudelleen; jos asettelu merkitsee, kannattaa perehtyä korvatun fontin sulkemis- ja subsettokäyttäytymiseen artikkelissa fonttisubsetin sulkemisesta, ja uudelleenjärjestystä tai yhdistämistä tarvitsevat skriptit käsitellään muotoiluvaiheessa, josta kerrotaan artikkelissa monimutkaisten skriptien tekstimuotoilu
Miten jälkiasentaa käyttäytyminen vaarantamatta olemassa olevaa polkua
Sama julkaisu lisäsi perinteisen kern-taulukon fallbackin parivälistykseen, ja tapa, jolla se rajattiin, on malli, joka kannattaa kopioida. Sen sijaan että olisi lisätty uusi päätöspiste kernauslogiikkaan, fallback roikkuu aikaisen poistumisen haarassa, joka oli jo olemassa fonteille ilman GPOS-taulukkoa. Moderni fontti GPOS:illa ei koskaan päädy siihen, joten sen käyttäytyminen on muuttumatonta rakenteen eikä testaamisen kautta. Polut, jotka eivät rekisteröi Unicode-fonttia, tuottavat kaksi nollasiirtymää, joten nekin ovat muuttumattomia
Tuo on matalan riskin jälkiasennuksen yleinen muoto kypsässä renderöintikirjastossa: löydä haara, joka tällä hetkellä ei tuota mitään, ja laita uusi käyttäytyminen sinne. Se muuntaa väitteen ”uskomme, ettei tämä heikentänyt mitään” väitteeksi ”tämä ei ole voinut heikentää mitään”, mikä on paljon parempi asia sanoa tekstikoneesta, jonka kautta toisten laskut kulkevat
Tee siitä portti, ei lokia
Kattavuuslöydökset ovat hyödyllisiä vain, jos jokin epäonnistuu niiden vuoksi. Dokumentteja tuottavassa palvelussa tuottava järjestely on pitää seuranta päällä yöllisessä regressiotyössä todellisten asiakkaiden nimien, osoitteiden ja tuotekuvausten korpuusta vastaan ja epäonnistaa työ mistä tahansa löydöksestä. Koska tapahtuma laukeaa kerran per uniikki koodipiste eikä kerran per esiintymä, tuloste pysyy tarpeeksi pienenä luettavaksi, vaikka kokonainen skripti puuttuisi
Tuotannossa samaa käsittelijää on parempi käyttää telemetriana: tallenna koodipiste ja fontti, jatka dokumentin palvelemista ja anna aggregaatin kertoa, minkä skriptin lisäät seuraavaksi jakelufonttijoukkoon. Upotettujen ja korvattujen fonttien renderöintikäyttäytyminen käsitellään tarkemmin artikkelissa upotettujen fonttien glyfien renderöinti, ja koko ominaisuusluettelo, mukaan lukien TrackUnresolvedGlyphs, on dokumentoitu HotPDF Delphi PDF -komponentin tuotesivulla