PDFlibPas razrješava znakove koje odabrani font ne može nacrtati tako da pretražuje fallback lanac instaliranih fontova, klaster po klaster, uz očuvanje oblikovanja i redoslijeda dvosmjernih nizova. Uključujete ga pomoću SetAutomaticFontFallback, lanac proširujete pomoću AddFontFallback, a u datoteku se ugrađuju samo oni fallback fontovi koji su stvarno korišteni za izlaz
Problem koji rješava jest onaj s kojim se svaki generator dokumenata suoči prvi put kada stigne ime kupca napisano pismom koje predloženi font nikada nije predvidio. Kvar je tih, i upravo je to ono što ga čini skupim
Zašto nepodržani tekst nestane umjesto da izazove pogrešku?
Zato što PDF nema koncept fonta koji ne može nacrtati znak. Jednostavan font mapira bajt kodove u imena glifova putem kodiranja; složeni font mapira kodove putem CMap-a u indekse glifova. Zatražite glif koji font ne sadrži i dobit ćete indeks glifa nula, .notdef, koji većina fontova crta kao ništa ili kao praznu kutiju. Datoteka je strukturno valjana, operator teksta ispravno je oblikovan, a stranica se renderira. Samo je prazna ondje gdje bi trebalo biti ime
Ništa u ISO 32000-1 ne zahtijeva da proizvođač to primijeti. Generator koji piše tekst bez provjere pokrivenosti proizvodi tehnički sukladan PDF koji je tiho izgubio sadržaj, a gubitak se pojavi na kupčevu zaslonu tek tjednima kasnije. Zato značajka fallback fontova i izvještaj o nedostajućim glifovima dolaze zajedno: razrješavanje onoga što se može razriješiti samo je pola posla, a prijavljivanje onoga što se nije moglo razriješiti druga je polovica
Fallback se odvija po klasteru, a ne po kodnoj točki
Granularnost je detalj koji razdvaja funkcionalnu implementaciju od uvjerljive. Tekst nije niz nezavisnih znakova. Devanagari slog, emoji s modifikatorom tona kože, osnovno slovo s dijakritičkim znakovima: svaki je od njih jedan klaster koji mora nacrtati jedan font, jer odluke o oblikovanju unutar njega ovise o tablicama tog fonta
PDFlibPas razrješava klastere, pa klaster koji pokriva fallback font u cijelosti crta taj font. Razdvajanje usred klastera i crtanje pola iz primarnog fonta, a pola iz fallback fonta proizvelo bi tehnički prisutan, ali vizualno slomljen rezultat, što je gotovo sigurno gore od praznine s kojom ste započeli. Redoslijed nizova također se čuva, pa fallback unutar niza s desna na lijevo ne mijenja redoslijed okolnog teksta; isti mehanizam stoji iza okomitog rasporeda opisanog u članku okomito pisanje za japanski i kineski
var
Lib: TPDFlib;
begin
Lib := TPDFlib.Create;
try
Lib.SetOrigin(1);
Lib.SetAutomaticFontFallback(1);
// Redoslijed pretrage: prvo podudaranje pobjeđuje, pa najšire fontove stavite na kraj
Lib.AddFontFallback('Microsoft YaHei'); // pojednostavljeni kineski
Lib.AddFontFallback('Meiryo'); // japanski
Lib.AddFontFallback('Segoe UI Symbol');
Lib.AddFontFallback('Segoe UI Emoji');
Lib.SetMissingGlyphPolicy(PDF_MISSING_GLYPH_REPORT);
Lib.AddTrueTypeFont('Arial', 1); // 1 = ugradi font
Lib.SetTextSize(11);
Lib.DrawText(72, 720, 'Invoice for 北京示例科技有限公司');
Lib.DrawText(72, 700, 'Delivery status: on time');
Lib.SaveToFile('invoice.pdf');
finally
Lib.Free;
end;
end;
Lanac slažite namjerno. Razrješavanje uzima prvi font koji pokriva klaster, pa će široki pan-Unicode font stavljen na prvo mjesto pobijediti gotovo sve, a vaše pažljivo odabrane fontove specifične za pojedino pismo nikada neće biti konzultirani. Specifične fontove stavite prve, a univerzalni na kraj
Prijaviti ili prekinuti: koji kvar želite?
SetMissingGlyphPolicy prima PDF_MISSING_GLYPH_REPORT, kompatibilnu zadanu vrijednost, ili PDF_MISSING_GLYPH_ABORT. Kod politike izvještavanja operacija teksta nastavlja se, nerazrješive kodne točke izostavljaju se kao i prije, a svaka se zabilježi. Kod politike prekida operacija teksta odbacuje se prije nego što se zapiše ikakav sadržaj, a LastErrorCode postavlja se na 521
Birajte prema tome čemu dokument služi. Skup internih izvještaja trebao bi nastaviti s renderiranjem i bilježiti praznine, jer je danas malo nepotpun izvještaj bolji od nikakvog izvještaja. Pravno obvezujući ugovor, račun, ili bilo što s nečijim imenom na sebi trebalo bi prekinuti obradu, jer je tiho izostavljen znak u imenu stranke greška koju želite otkriti u vlastitom procesu, a ne u sporu. Politika prekida ne uspijeva prije pisanja, pa iza sebe ne ostavlja polunapisan tok sadržaja
var
Lib: TPDFlib;
Report: WideString;
begin
Lib := TPDFlib.Create;
try
Lib.SetMissingGlyphPolicy(PDF_MISSING_GLYPH_ABORT);
// ... izgradite dokument ...
if Lib.DrawText(72, 660, CustomerName) <> 1 then
if Lib.LastErrorCode = PDFLIB_ERROR_MISSING_GLYPH then
begin
Report := Lib.GetMissingGlyphReportJSON;
// {"valid":false,"policy":1,"eventCount":1,"events":[
// {"sequence":1,"documentIndex":0,"page":1,"utf16Index":12,
// "codePoint":21271,"unicode":"U+5317","fontName":"Arial",
// "fontType":"TrueType","operation":"DrawText"}]}
EscalateToOperator(Report);
end;
finally
Lib.Free;
end;
end;
Izvještaj je namjerno strojno čitljiv i ograničen. Svaki događaj nosi stranicu, UTF-16 indeks unutar niza, kodnu točku u brojčanom obliku i obliku U+XXXX, odabrani font, njegov tip i operaciju kod koje je nastao problem, tako da prijava podrške može navesti točan znak umjesto da opisuje simptom. Tracker čuva posljednjih 256 događaja, što je dovoljno za dijagnosticiranje dokumenta, a dovoljno malo da patološko izvođenje ne pretvori dijagnostiku u problem s memorijom
Mjerenje i crtanje moraju se slagati
Mjerenje širine koristi iste odluke fallback mehanizma svjesnog klastera kao i crtanje. To zvuči očito, a upravo je to ono što najviše kućnih fallback slojeva pogrešno rade: zakrpaju putanju crtanja, a mjerenje ostave na primarnom fontu, pa svaki tekstualni okvir, desno poravnanje i stupac tablice na kraju bude izračunat iz širina koje se ne podudaraju s onim što je zapravo renderirano
Budući da obje putanje dijele isto razrješavanje, niz izmjeren prije crtanja zauzima točno onu širinu na kojoj je izmjeren, uključujući fallback dijelove. Upravo to čini sigurnim globalno uključivanje fallback mehanizma, umjesto samo na mjestima koja ste ručno provjerili
Ugrađuje se samo ono što ste stvarno koristili
Fallback fontovi ugrađuju se lijeno: font u lancu koji nikada nije razriješio nijedan klaster ne pridonosi ničim izlazu. Dokument koji sadrži jedan kineski znak i 5000 latiničnih ne nosi cijeli CJK font; nosi samo ono što je prolaz izrade podskupa proizveo za taj jedan glif, što je ponašanje opisano u članku optimizacija veličine PDF datoteke i izrada podskupova fontova
Ta lijenost čini jeftinim konfiguriranje širokog lanca. Registrirajte fontove koji bi mogli trebati vašem skupu dokumenata za svaku lokalizaciju koju opslužujete, a svaki pojedinačni PDF plaća samo ono što je stvarno koristio. Za dokumente koje niste sami generirali, gdje su nedostajući fontovi već unutar postojeće datoteke, put popravka je drugačiji i obrađen je u članku ugrađivanje nedostajućih fontova u postojeći PDF
Jedno upozorenje vezano uz implementaciju vrijedi jasno izreći: fallback se razrješava u odnosu na fontove instalirane na stroju koji izvršava kod. Poslužitelj bez instaliranih CJK fontova nema na što pasti, a izvještaj će vam to reći već kod prvog dokumenta, a ne tek nakon prve pritužbe. Isporučite fontove o kojima ovisite i potvrdite licenciranje za njihovu ugradnju
PDFlibPas je PDF biblioteka za Delphi, C++Builder i Lazarus s pripadajućim DLL i ActiveX sučeljima, pa su API-ji za fallback i nedostajuće glifove dostupni i pozivateljima izvan Pascala. Potpuna dokumentacija nalazi se na stranici PDFlibPas Delphi PDF biblioteke