Nedostajući glif u PDF-u nije greška. Proizvođač traži znak koji izabrani font ne može mapirati, font vraća indeks glifa nula, i datoteka koja izađe strukturno je valjana, otvara se svuda, i prikazuje praznu kutiju gde bi ime ili iznos trebalo biti. Niko u pipeline-u generisanja ne saznaje. Primalac saznaje. HotPDF zatvara tu petlju sa TrackUnresolvedGlyphs: uključite je i put crtanja teksta beleži svaku kodnu tačku čije se traženje glifa razrešava u indeks nula, podižući OnUnresolvedGlyph jednom po jedinstvenom nalazu sa kodnom tačkom, fontom na kojem je otkazala, pismu kojem pripada, i sugestijom fontova koji bi je pokrili
Detekcija je pola odgovora. Druga polovina je SetFontFallbackChain, koja registruje uređen popis fontova po pismu, pa se uobičajeni slučajevi sami razrešavaju i samo pravi jazovi stižu do vašeg rukovaoca. Zajedno pretvaraju klasu defekta koju su kupci ranije prijavljivali u proveru u vreme izgradnje
Zašto nedostajući glif ništa ne podiže?
Zato što ISO 32000 ne postavlja obavezu proizvođaču da verifikuje pokrivenost, i indeks glifa nula je legitimni glif. To je .notdef, čiju konturu bira dizajner fonta: obično prazan ili šuplji pravougaonik, ponekad ništa. Prikazivač koji ga crta ponaša se ispravno. Izdvajanje teksta može čak vratiti ispravne znakove, jer se /ToUnicode mapiranje piše iz izvornog teksta, a ne iz kontura, pa automatska provera povratnog puta rado prolazi dokument čiji vidljivi tekst ima rupe
Praktična posledica je da pokrivenost mora biti proverena u trenutku crtanja, kada biblioteka još zna koja je kodna tačka tražena i koji glif je font zapravo ponudio. Posle je informacija nestala
Detektor mora gledati stanje podskupa, ne device kontekst
Ovde je prva implementacija pogrešila, i razlog vredi razumeti jer se primenjuje na svaku proveru pokrivenosti pričvršćenu na tekst pipeline. HotPDF ima dva puta teksta. Jedan emituje kroz registrovani Unicode TrueType font sa mapom znakova u memoriji izgrađenom u trenutku registracije. Drugi je nasleđeni GDI put koji stvara svež device kontekst i handle fonta po nizu znakova
Ocena pokrivenosti iz GDI puta je beznadežna. Njegovo mapiranje nije mapiranje koje završi u emitovanom toku sadržaja, i dva nisu sinhronizovana, pa detektor koji čita GDI rezultate izveštava ceo štampani ASCII opseg kao nerazrešen. Ovlašćeni odgovor živi u registrovanom fontu: mapi znakova koju RegisterUnicodeTTF raščlanjuje, ispitivanoj kroz GetUnicodeGlyphForCodepoint. Detektor je dakle kapijaran na stanje spremnosti podskupa, ne na bilo koji GDI uslov, i jednostavno ne radi na dokumentima koji nikad nisu registrovali Unicode font, što je ispravno jer su ti dokumenti ionako ograničeni na standardna kodiranja
Druga zamka sedi pored nje. GDI ime familije fonta i PostScript ime izvučeno iz font binarne datoteke u registraciji različiti su nizovi znakova, i ne na način koji možete normalizovati: familija zvana Arial Unicode MS nosi PostScript ime ArialMT. Svaka kapija napisana kao "da li je trenutno izabrani font onaj koji smo registrovali", poređena po imenu, mrtav je kod koji nikad ne opali. Kapija na stanje, nikada na imena fontova
Ne testirajte detektor glifova emotikonima
Očigledan test slučaj je nasmejano lice, i ubediće vas da je detektor pokvaren. Česte kodne tačke emotikona u astralnim ravnima razrešavaju se privatnom-upotrebnom sinteznom putanjom koja ih mapira u indeks glifa direktno, pa nikad ne stižu do opšte grane pokrivenosti. Detektor se ponaša ispravno i test meri pogrešan put
Koristite nedodeljenu kodnu tačku umesto toga. U+0378 trajno je nedodeljen u Unicode-u, pa nijedan font ne može legitimno mapirati, i vežba tačno granu koju želite verifikovati. Ova razlika između "funkcija je pokvarena" i "test je izabrao ulaz koji zaobilazi funkciju" košta pravih sati, i nedodeljene kodne tačke najjeftiniji su način da se to izbegne
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
// Opali jednom po jedinstvenoj kodnoj tački, ne jednom po pojavi
FFindings.Add(Format('U+%.4X missing in %s (script %d), try: %s',
[Info.CodePoint, String(Info.FontName), Ord(Info.Script),
String(Info.SuggestedFonts)]));
end;
// Povezivanje u posao generisanja
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
// Odbijte posao umesto isporučivanja stranice sa kutijama na njoj
raise Exception.Create(Audit.Findings.Text);
finally
Pdf.Free;
end;
Lanci rezervnih rešenja su po pismu, ne po fontu
Razlog što je rezerva obuhvaćena pismom umesto izvornim fontom jeste što se jazovi pokrivenosti grupišu po sistemu pisanja. Latin tekstualnom fontu nedostaju Devanagari, Thai, Han i emotikoni, svi odjednom, i zamena za svaki je drugi font. Izjavljivanje jednog lanca po pismu dakle opisuje stvarno raspoređivanje: jedan Latin font za tekst, jedan CJK font, jedan font emotikona, jedan za sve
// THPDFFontScript pokriva hfsCommon, hfsLatin, hfsGreek, hfsCyrillic,
// hfsHebrew, hfsArabic, hfsIndic, hfsSoutheastAsian, hfsCJK, hfsKana,
// hfsHangul, hfsEmoji i 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']);
Rezerva i detekcija komplementarne su umesto alternativne. Lanci rukuju pokrivenošću koju ste predvideli; detektor izveštava pokrivenost koju niste, što na sistemu koji obrađuje proizvoljne podatke kupaca jeste zanimljiva polovina. Obratite pažnju da zamena fonta menja metrike, pa pasus koji padne na rezervu može se prelomiti ponovo; ako raspored važi, vredi proučiti ponašanje zatvaranja i podskupova zamenjenog fonta u članku o zatvaranju podskupa fonta, a pisma koja traže preuređivanje ili spajanje rukuje etapa oblikovanja opisana u oblikovanju teksta složenog pisma
Kako uneti ponašanje unazad bez rizika za postojeći put
Isto izdanje dodalo je nasleđenu rezervu tabele kern za razmak parova, i način na koji je obuhvaćena obrazac je vredan kopiranja. Umesto dodavanja nove tačke odluke logici kerninga, rezerva visi na rano-izlaznoj grani koja je već postojala za fontove bez GPOS tabele. Moderni font sa GPOS-om nikad do nje ne stiže, pa je njegovo ponašanje nepromenjeno konstrukcijom umesto testiranjem. Putovi koji ne registruju Unicode font proizvode dva nulta ofseta, pa su i oni nepromenjeni
To je opšti oblik niskorizičnog unosa unazad u zreloj biblioteci prikaza: pronađite granu koja trenutno ne proizvodi ništa i stavite novo ponašanje tamo. Pretvara "verujemo da ovo nije unelo regresiju" u "ovo ne može biti unelo regresiju", što je mnogo bolja stvar za reći o motoru teksta kroz koji prolaze tuđe fakture
Učinite to kapiju, ne log
Nalazi pokrivenosti korisni su samo ako nešto otkazuje na njima. U servisu generisanja dokumenata plodan aranžman je držati praćenje uključenim u noćnom regresionom poslu nad korpusom stvarnih imena kupaca, adresa i opisa proizvoda, i odbiti posao na svaki nalaz. Pošto događaj opali jednom po jedinstvenoj kodnoj tački umesto jednom po pojavi, izlaz ostaje dovoljno mali za čitanje i kada celo pismo nedostaje
U produkciji isti rukovaoc bolje je koristiti kao telemetriju: zabeležite kodnu tačku i font, nastavite serviranje dokumenta, i pustite zbir da vam kaže koje pismo dodati sledeće u skup fontova raspoređivanja. Ponašanje prikaza za ugrađene i zamenjene fontove dalje je pokriveno u prikazu ugrađenih glifova fonta, a potpun popis svojstava uključujući TrackUnresolvedGlyphs dokumentovan je na stranici proizvoda HotPDF Delphi PDF component