PDF koji na vašem računalu izgleda savršeno, a na tuđem se prikazuje kao niz praznih kvadratića najčešći je nedostatak s fontovima u softveru za dokumente, i to gotovo nikada ne znači da je tekst pogrešan. Znakovi su netaknuti, kodiranje je u redu, ali glifovi jednostavno nisu prisutni. Ono što se promijenilo između dvaju računala jest to koje je fontove operacijski sustav imao instalirane, a razlika između prijenosne i krhke datoteke leži u jednoj odluci donesenoj pri pisanju stranice: je li font putovao unutar PDF-a ili se pretpostavljalo da je prisutan na odredištu.
Razumijevanje zašto se to događa, te zašto zaseban problem proizvodi tekst koji izgleda pretraživo ali se kopira kao besmislica, zahtijeva uvid u to kako PDF pohranjuje tekst. On ne pohranjuje rečenice. Pohranjuje kodove glifova plus program fonta plus tablice koje ih međusobno povezuju, a svaki bug pri iscrtavanju ili izdvajanju živi u jazu između to troje. Ono što slijedi jest pregled tog mehanizma, utemeljen na ISO 32000 standardu, uz Delphi pozive koji ga kontroliraju tamo gdje je to važno.
Znakovi, kodovi i glifovi tri su različite stvari
Rječnik često zbunjuje ljude jer svakodnevni govor spaja tri različite ideje u riječ "slovo". Znak (character) je apstraktna jedinica pisanja, ideja velikog slova A, identificirana u Unicodeu kao U+0041. Glif (glyph) je nacrtani oblik, obris krivulja i linija koji određeni font koristi za prikaz tog znaka. Između njih nalazi se kod (code): bajt ili bajtovi u toku sadržaja koji pregledniku govore koji glif u trenutnom fontu treba iscrtati.
PDF radi s kodovima. Kada tok sadržaja prikazuje niz znakova, ti su bajtovi indeksi u aktivnom fontu, a ne Unicode. Kodiranje fonta odlučuje da kod 65 znači "nacrtaj glif pod brojem 65", i ništa u toj operaciji ne zna da rezultat čovjeku izgleda kao slovo A. To je ono što omogućuje da se PDF prikazuje identično svugdje gdje može pronaći glifove, a to je ujedno i razlog zašto je izdvajanje teksta zaseban problem od prikaza: crtanje zahtijeva samo mapiranje koda u glif (code-to-glyph), dok čitanje zahtijeva mapiranje koda u Unicode (code-to-Unicode), a to su dvije različite tablice koje se mogu razlikovati ili neovisno nedostajati.
Vrste fontova s kojima ćete se susresti
ISO 32000 definira nekoliko vrsta rječnika fontova, a u praksi dokument koji primite ili generirate koristi jedan od tri. Poznavanje onoga što gledate objašnjava većinu stvari koje mogu poći po zlu.
Type 1 je Adobeov izvorni PostScript format obrisa, izgrađen od kubičnih Bezierovih krivulja. Četrnaest standardnih fontova koje svaki usklađeni preglednik mora osigurati, obitelji Helvetica, Times, Courier, Symbol i ZapfDingbats, pripadaju vrsti Type 1, a rječnik fonta koji imenuje jedan od njih može izostaviti program fonta. To je jedini slučaj u kojem je ostavljanje fonta neugrađenim sigurno prema specifikaciji, a ne pukom srećom. Za bilo koji drugi Type 1 font program mora biti ugrađen ili će preglednik upotrijebiti zamjenu, obično metrički sličan, ali vizualno različit font.
TrueType koristi kvadratne krivulje i potječe iz Appleova i Microsoftova svijeta. To je ono što je većina sistemskih fontova i ono što ćete najčešće ugrađivati. Jednostavan TrueType font u PDF-u ograničen je na jednobajtne kodove, pa takav font može odjednom adresirati najviše 256 glifova. To ograničenje je strukturalni razlog zašto CJK i druga velika pisma ne mogu koristiti jednostavan font.
Type 0, kompozitni ili CID-keyed font, rješenje je za to ograničenje. Koristi višebajtne kodove i CMap za njihovo usmjeravanje kroz podređeni CIDFont, čiji su obrisi sami po sebi TrueType ili CFF/Type 1. Ovo je jedina vrsta fonta koja može sadržavati tisuće glifova, pa svaki PDF koji sadrži kineski, japanski, korejski ili širi višejezični sadržaj koristi Type 0, bez obzira na to je li autor o tome razmišljao. Cijena toga je složenost: više pokretnih dijelova, od kojih više njih mora biti ispravno i za iscrtavanje i za izdvajanje teksta.

Jedan detalj iza te slike utječe na veličinu datoteke. Font je knjižnica obrisa, a ne bitmapa fiksne veličine, tako da isti ugrađeni program služi za svaku veličinu točke na stranici. Skaliranje je transformacija koja se primjenjuje u trenutku crtanja, zbog čega naslov i tekst u tijelu dijele isti ugrađeni font te zašto se trošak ugradnje računa po fontu, a ne po veličini.
Ugradnja je razlika između prijenosnog i krhkog
Ugradnja (embedding) znači da je program fonta, stvarni podaci o obrisu, zapisan u PDF kao tok podataka. Preglednik na računalu koje nikada nije čulo za vaš font čita te obrise izravno iz datoteke i crta točne glifove. Ako preskočite ugradnju, kladite se da odredište ima font istog naziva; kada ga nema, preglednik koristi zamjenu. Za standardnih četrnaest ta je zamjena definirana i bezopasna. Za sve ostalo to varira od male razlike u drugom fontu do praznih kvadratića kada nijedna zamjena uopće ne pokriva to pismo.
S komponentom HotPDF kontrola se vrši preko jednog svojstva, postavljenog prije otvaranja dokumenta. FontEmbedding govori knjižnici da u datoteku pakira fontove s kojima crta:
var
Pdf: THotPDF;
begin
Pdf := THotPDF.Create(nil);
try
Pdf.FileName := 'report.pdf';
Pdf.Compression := cmFlateDecode;
Pdf.FontEmbedding := True; // outlines travel inside the file
Pdf.BeginDoc;
Pdf.CurrentPage.SetFont('Calibri', [], 11);
Pdf.CurrentPage.TextOut(72, 760, 0, 'This renders the same on a machine without Calibri.');
Pdf.EndDoc;
finally
Pdf.Free;
end;
end;
Redoslijed nije estetske naravi. BeginDoc je mjesto gdje HotPDF potvrđuje strukturu dokumenta, pa FontEmbedding mora biti postavljen na true prije tog poziva. Ako ga dodijelite nakon toga, neće biti pogreške ni upozorenja, već samo datoteka koja je tiho isporučena bez svojih fontova. To je najgora vrsta buga: prolazi sve testove na razvojnom računalu na kojem je font instaliran, a pojavljuje se tek kod klijenta gdje tog fonta nema.
Ugradnja je također mjesto gdje se licenciranje susreće s inženjerstvom. Program fonta nosi oznake koje opisuju smije li se ugraditi slobodno, samo za pregled ili se uopće ne smije ugraditi. Poštivanje tih oznaka vaša je odgovornost, a ne odgovornost preglednika, i "radilo je" nije isto što i "bilo je dopušteno".
Podskupovi (subsetting): Ugradite samo glifove koje ste koristili
Potpuna ugradnja zapisuje cijeli program fonta u datoteku. Veliki CJK TrueType font može težiti nekoliko megabajta, a njegovo ugrađivanje u cijelosti za prikaz desetak znakova je rastrošno na način koji se akumulira kroz dokument s više stranica. Izrada podskupa (subsetting) rješava to pisanjem samo onih glifova na koje se dokument referira, a zatim preimenovanjem fonta s oznakom od šest slova i znakom plus, npr. u obliku ABCDEF+Calibri u popisu fontova svakog PDF-a s podskupom, kako preglednik nikada ne bi pomiješao djelomični font s punim sistemskim fontom istog naziva.
Za većinu generiranih dokumenata izrada podskupova je ispravna zadana postavka. Ona održava veličinu datoteke proporcionalnom sadržaju, a ne izvornom fontu, što je najvažnije kod velikih višejezičnih fontova koji bi inače dominirali datotekom. Jedino upozorenje je da podskup sadrži samo ono što je korišteno u trenutku izrade. Ako neki kasniji proces pokuša dodati tekst koristeći taj podskup fonta, glifovi koji su mu potrebni možda neće biti u datoteci, što predstavlja stvarno ograničenje za inkrementalno uređivanje tuđeg PDF-a.
Unicode fontovi i problem s CJK kvadratićima
Kada tekst nije obična latinica, jednostavni fontovi više nisu dovoljni, a rješenje je izričito registrirati font s podrškom za Unicode i prepustiti HotPDF-u da iz njega izgradi Type 0 font. RegisterUnicodeTTF učitava TrueType datoteku prema putanji; nakon toga se registrirano ime može koristiti u SetFont kao i bilo koje drugo:
Pdf.FontEmbedding := True;
Pdf.RegisterUnicodeTTF('C:\Fonts\NotoSansCJKsc-Regular.ttf');
Pdf.BeginDoc;
Pdf.CurrentPage.SetFont('NotoSansCJKsc-Regular', [], 14);
Pdf.CurrentPage.TextOut(72, 720, 0, '你好,世界 こんにちは 안녕하세요');
Pdf.EndDoc;
Dvije stvari određuju uspjeh ovoga. Font mora pokrivati pisma u nizu znakova: TrueType font koji podržava samo latinicu neće razviti kineske glifove zato što ste vi to zatražili, pa će rezultat ponovno biti prazni kvadratići, ovaj put zato što glif doista ne postoji u tom fontu. Također, ugradnja mora ostati uključena, jer je Type 0 font sastavljen od registriranog TTF-a besmislen za preglednik koji ne može pronaći obrise. Za mješoviti sadržaj trajan izbor je font sa širokom pokrivenošću, pri čemu su obitelji Noto i Arial Unicode MS uobičajeni odgovori, ugrađeni i s podskupovima.
Pisma zdesna nalijevo i složena pisma dodaju sloj oblikovanja povrh pokrivenosti. HotPDF nudi RtLTextOut za arapski i hebrejski, koji upravlja promjenom smjera pa vi prosljeđujete logički redoslijed, a knjižnica ga raspoređuje. Ispravan prikaz arapskog jezika zahtijeva pokrivenost plus oblikovanje plus smjer, što su tri zasebne stvari, a kvadratić na tom mjestu može značiti da je bilo koja od njih zakazala.
Tablica ToUnicode: gdje živi kopiranje i lijepljenje
Sve gore navedeno odnosi se na crtanje. Izdvajanje teksta je zrcalna slika i ne uspijeva iz vlastitih razloga. Preglednik prikazuje stranicu koristeći mapiranje koda u glif, ali kada korisnik označi tekst i kopira ga, preglednik mora te iste kodove vratiti u Unicode. To obrnuto mapiranje je ToUnicode CMap, neobavezni tok podataka priložen uz font.
Kada je prisutna i ispravna, kopirani tekst izlazi kao točni znakovi. Kada nedostaje ili je neispravna, ili je font podskup s prilagođenim kodovima glifova bez zapisanog ToUnicode mapiranja, stranica izgleda savršeno, ali se međuspremnik puni smećem: kodovi glifova čitaju se kao da su Unicode, što za prilagođeni podskup nisu. Zato skenirani dokument sa slojem OCR teksta može biti pretraživ, dok digitalno stvoreni PDF iz neopreznog generatora to nije. Iscrtavanje i izdvajanje koriste različite tablice, pa datoteka može zadovoljiti jedno, a zakazati na drugom. Ako je izdvajanje važno za vaš izlaz, tretirajte ispravnu ToUnicode tablicu kao zahtjev i provjerite je kopiranjem teksta iz uzorka umjesto da vjerujete da je tu.
Kako brzo dijagnosticirati problem s fontom
Način na koji stvar ne uspijeva govori vam gdje trebate gledati. Prazni kvadratići na drugom računalu gotovo uvijek znače da font nije ugrađen, pa najprije provjerite ugradnju, a potom pokrivenost glifovima. Kvadratići koji se pojavljuju čak i na vašem vlastitom računalu upućuju na pokrivenost: font ne sadrži to pismo, bez obzira na ugradnju. Tekst koji se ispravno iscrtava, ali se kopira kao besmislica, predstavlja ToUnicode problem, a ne problem s iscrtavanjem, i poigravanje s fontovima ili ugradnjom to neće popraviti jer crtanje nikada nije bilo pokvareno. Da biste pročitali gotovu datoteku, otvorite je u Acrobatu i pogledajte Document Properties, Fonts: ispravan unos prikazuje vrstu, navodi Embedded ili Embedded Subset i imenuje kodiranje. Font koji bi trebao biti ugrađen, a nije, tamo se sam otkriva prije nego što to učini kupac.
Ništa od ovoga nije neobično kada je jasna podjela na znak, kod i glif. Ugradite fontove s kojima crtate, napravite podskupove za velike, posegnite za Unicode fontom i upotrijebite RegisterUnicodeTTF čim tekst napusti latinicu te zadržite ispravnu ToUnicode tablicu ako će itko izdvajati tekst. Učinite to kako treba i kvadratići se više neće pojavljivati. Za prateće mehanizme, anatomija minimalnog PDF-a pokazuje gdje se rječnik fonta nalazi u stablu objekata, a vodič kroz strukturu dokumenta pokriva kako se resursi dijele među stranicama.
Pozivi SetFont, FontEmbedding i RegisterUnicodeTTF koji su ovdje prikazani dio su komponente HotPDF Component za Delphi i C++Builder.