Tehnički članak

Otkrivanje nedostajućih PDF glifova pri crtanju u Delphi-ju

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

Dijagram zašto nedostajući PDF glif ostaje tih dok glif nula crta praznu kutiju a ToUnicode izdvajanje prolazi provere povratnog puta
Glif nula je legitimni .notdef odgovor i /ToUnicode piše se iz izvornog teksta, pa niko u pipeline-u ne saznaje za jaz

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

Tok detekcije nerazrešenih glifova HotPDF prikazuje kapiju stanja podskupa, traženje GetUnicodeGlyphForCodepoint i povezivanje događaja OnUnresolvedGlyph
Pokrivenost se ocenjuje iz mape registrovanog Unicode fonta umesto GDI, i svaka jedinstvena kodna tačka podiže jedan događaj sa sugestijom fonta

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

Dijagram rezervnih fontova po pismu mapira pisma hfsCJK, hfsArabic, hfsEmoji i hfsOther u uređene lance zamenskih fontova u HotPDF-u
Svako pismo dobija svoj uređeni lanac, pa Latin font teksta bez Han, arapskog ili emotikona pada na font koji ih pokriva
// 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