Dingęs glifas PDF nėra klaida. Gamintojas prašo simbolio, kurio pasirinktas šriftas negali atvaizduoti, šriftas grąžina glifo indeksą nulis, ir failas, kuris išeina, yra struktūriškai teisėtas, atsidaro visur ir rodo tuščią dėžę ten, kur turėtų būti vardas arba suma. Niekas generuojančiame konvejeryje to nesužino. Gavėjas — sužino. HotPDF uždaro tą ratą su TrackUnresolvedGlyphs: įjunkite tai, ir teksto braižymo kelias užrašo kiekvieną kodinį tašką, kurio glifo paieška išsiskleidžia į indeksą nulis, pakeldamas OnUnresolvedGlyph kartą per unikalų radinį su kodiniu tašku, šriftu, kuriame nepavyko, rašto sistema, kuriai jis priklauso, ir šriftų, kurie jį dengtų, pasiūlymu
Aptikimas yra pusė atsakymo. Kita pusė yra SetFontFallbackChain, registruojanti surūšiuotą šriftų sąrašą pagal rašto sistemą, todėl įprasti atvejai išsprendžia savi, o tikros spragos pasiekia jūsų apdorotoją. Kartu jie paverčia defektų klasę, kurią anksčiau pranešdavo klientai, statymo laiko patikra
Kodėl dingęs glifas nepakelia nieko?
Nes ISO 32000 nenumato jokios gamintojo pareigos tikrinti dengimą, o glifo indeksas nulis yra teisėtas glifas. Tai .notdef, kurio kontūrą pasirenka šrifto dizaineris: dažniausiai tuščias arba tuščiaviduris stačiakampis, kartais niekas apskritai. Peržiūros programa, jį piešianti, elgiasi teisingai. Teksto ištrauka gali net grąžinti teisingus simbolius, nes /ToUnicode atvaizdis rašomas iš šaltinio teksto, o ne iš kontūrų, todėl automatinė abipusė patikra linksmai išlaikys dokumentą, kurio matomas tekstas turi skylių
Praktinė pasekmė ta, kad dengimą reikia tikrinti piešimo akimirką, kai biblioteka dar žino, kuris kodinis taškas prašytas ir kurį glifą šriftas iš tikrųjų pateikė. Vėliau ta informacija dingo
Aptikėjas turi stebėti poaibio būseną, o ne įrenginio kontekstą
Čia pirmasis įgyvendinimas suklydo, ir priežastis verta suprasti, nes ji taikoma bet kuriai dengimo patikrai, prisi kalbtai prie teksto konvejerio. HotPDF turi du teksto kelius. Vienas skleidžia per registruotą Unicode TrueType šriftą su atmintyje sukurtu simbolių atvaizdu, pastatytu registracijos metu. Kitas yra senasis GDI kelias, kuriantis naują įrenginio kontekstą ir šrifto rankenėlę kiekvienam simbolių etapui
Dengimo vertinimas iš GDI kelio yra beviltiškas. Jo atvaizdis nėra tas atvaizdis, kuris atsiduria skleidžiamame turinio sraute, ir abu nesinchronizuoti, todėl aptikėjas, skaitantis GDI rezultatus, praneša visą spausdinamą ASCII intervalą kaip neišspręstą. Autoritetingas atsakymas gyvena registruotame šrifte: simbolių atvaizdyje, kurį išanalizuoja RegisterUnicodeTTF, teikiamame per GetUnicodeGlyphForCodepoint. Aptikėjas todėl užtvertas pagal poaibiui paruoštą būseną, o ne pagal bet kokią GDI sąlygą, ir jis tiesiog nevyksta dokumentuose, kurie niekada neregistravo Unicode šrifto, — kas teisinga, nes tie dokumentai vis tiek apriboti standartinėmis koduotėmis
Antri spąstai sėdi greta. Šrifto GDI šeimos vardas ir iš šrifto dvejetainės failo registracijos metu ištrauktas PostScript vardas yra skirtingos eilutės, ir ne tokiu būdu, kurį galima normalizuoti: šeima, vadinama Arial Unicode MS, neša PostScript vardą ArialMT. Bet kuri užtvara, parašyta kaip „ar dabar pasirinktas šriftas yra tas, kurį registravome“, lyginama pagal vardą, yra negyvas kodas, niekada nepažystantis. Užtverkite pagal būseną, niekada pagal šriftų vardus
Negalvokite tikrinti glifų aptikėjo su emoji
Akivaizdus testinis atvejis yra besišypsantis veidas, ir jis įtikins jus, kad aptikėjas sugedęs. Įprasti astralinių sričių emoji kodiniai taškai išsiskleidžia per privataus naudojimo sintezės kelią, juos tiesiogiai atvaizduojant į glifo indeksą, todėl jie niekada nepasiekia bendrosios dengimo šakos. Aptikėjas elgiasi teisingai, o testas matuoja neteisingą kelią
Vietoj to naudokite nepriskirtą kodinį tašką. U+0378 yra visam laikui nepaskirtas Unicode, todėl joks šriftas teisėtai negali jo atvaizduoti, ir jis pratina tiksliai tą šaką, kurią norite patikrinti. Ta skirtis tarp „savybė sugedusi“ ir „testas parinko įvečią, apėjantį savybę“ kainuoja tikras valandas, o nepriskirti kodiniai taškai yra pigiausias būdas to išvengti
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
// Pakyla kartą per unikalų kodinį tašką, o ne kartą per pasirodymą
FFindings.Add(Format('U+%.4X missing in %s (script %d), try: %s',
[Info.CodePoint, String(Info.FontName), Ord(Info.Script),
String(Info.SuggestedFonts)]));
end;
// Sujungimas į generuojančią užduotį
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
// Sustabdykite užduotį, vietoj išsiuntimo puslapio su dėžėmis
raise Exception.Create(Audit.Findings.Text);
finally
Pdf.Free;
end;
Atsargos grandinės yra pagal rašto sistemą, o ne pagal šriftą
Priežastis, kodėl atsarga apibrėžiama pagal rašto sistemą, o ne pagal šaltinio šriftą, yra ta, kad dengimo spragos kaupiasi pagal rašto sistemą. Lotyniškam teksto šriftui trūksta Devanagari, thai, Han ir emoji — visko iš karto, o atitikmuo kiekvienam yra kitoks šriftas. Vienos grandinės deklaravimas pagal rašto sistemą todėl aprašo tikrą diegimą: vienas lotynų šriftas pagrindiniam tekstui, vienas CJK šriftas, vienas emoji šriftas, vienas viską gaunantis
// THPDFFontScript covers hfsCommon, hfsLatin, hfsGreek, hfsCyrillic,
// hfsHebrew, hfsArabic, hfsIndic, hfsSoutheastAsian, hfsCJK, hfsKana,
// hfsHangul, hfsEmoji and 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']);
Atsarga ir aptikimas yra papildantys, o ne alternatyvūs. Grandinės tvarko dengimą, kurio tikėtės; aptikėjas praneša dengimą, kurio netikėtės — sistemoje, apdorojančioje bet kokius klientų duomenis, tai įdomioji pusė. Atkreipkite dėmesį, kad šrifto pakeitimas keičia metrikas, todėl pastraipa, nukritusi į atsargą, gali persimainyti; jeigu išdėstymas svarbus, verta pasidomėti pakeisto šrifto uždarymo ir poaibių elgsena šrifto poaibio uždarymo straipsnyje, o rašto sistemos, kurioms reikia perkėlimo ar sujungimo, tvarko formavimo (shaping) pakopa, aprašyta sudėtingų rašto sistemų teksto formavime
Kaip atnaujinti elgseną, nerizikuojant esamu keliu
Tas pats leidimas pridėjo senojo kern lentelės atsargą porų tarpetams, ir būdas, kuriuo ji buvo apibrėžta, yra modelis, vertas nukopijuoti. Vietoje naujo sprendimų taško pridėjimo prie porinio tarpančio (kerning) logikos, atsarga kabo ant ankstyvo išėjimo šakos, kuri jau egzistavo šriftams be GPOS lentelės. Modernus šriftas su GPOS niekada jos nepasiekia, todėl jo elgsena nepakitusi dėl paties sandėlio, o ne dėl testavimo. Keliai, neregistruojantys Unicode šrifto, duoda du nulinius tarpetus, todėl irgi nepakitę
Tai bendroji mažos rizikos atnaujinimo forma subrendusioje atvaizdavimo bibliotekoje: suraskite šaką, kuri dabar negamina nieko, ir padėkite naują elgseną ten. Tai paverčia „tikime, kad tai nesugrąžino nieko“ į „tai negalėjo sugrąžinti nieko“, kas yra gerokai geresnis dalykas, sakomas apie teksto variklį, per kurį eina svetimų žmonių sąskaitos
Padarykite tai užtvara, o ne žurnalu
Dengimo radiniai naudingi tik tada, jei dėl jų kažkas nepavyksta. Dokumentus generuojančioje tarnyboje vaisingas išdėstymas — laikyti sekimą įjungtą naktiniame regresijos darbe prieš tikrų klientų vardų, adresų ir produktų aprašymų korpusą, ir sustabdyti darbą dėl bet kurio radinio. Kadangi įvykis pakyla kartą per unikalų kodinį tašką, o ne kartą per pasirodymą, išvestis lieka pakankamai maža skaityti net tada, kai dingusi visa rašto sistema
Gamyboje tas pats apdorotojas geriau naudotinas kaip telemetrija: užrašykite kodinį tašką ir šriftą, toliau aptarnaukite dokumentą ir leiskite suvestinei pasakyti, kurią rašto sistemą kitą pridėti prie diegimo šriftų rinkinio. Įterptų ir pakeistų šriftų atvaizdavimo elgsena plačiau dengiama įterptų šriftų glifų atvaizdavime, o visas savybių sąrašas, įskaitant TrackUnresolvedGlyphs, dokumentuotas HotPDF Delphi PDF component produkto puslapyje