PDF koji izgleda savršeno na vašem računaru, a na tuđem se prikazuje kao niz praznih kvadratića, najčešći je defekt sa fontovima u softveru za dokumente, i to skoro nikada ne znači da je tekst pogrešan. Karakteri su netaknuti, kodiranje je u redu, ali glifovi jednostavno nisu tu. Ono što se promenilo između dva računara jeste koji su fontovi instalirani u operativnom sistemu, a razlika između prenosive i krhke datoteke leži u jednoj odluci donetoj prilikom kreiranja stranice: da li je font putovao unutar PDF-a ili se pretpostavljalo da je prisutan na odredištu.
Razumevanje zašto se to dešava, kao i zašto poseban problem stvara tekst koji izgleda pretraživo ali se kopira kao besmislica, zahteva sagledavanje načina na koji PDF čuva tekst. On ne čuva rečenice. On čuva kodove glifova plus program fonta plus tabele koje ih povezuju, a svaka greška pri prikazivanju ili ekstrakciji leži u jazu između ta tri dela. Ono što sledi jeste obilazak tog mehanizma, zasnovan na standardu ISO 32000, sa Delphi pozivima koji ga kontrolišu tamo gde je to važno.
Karakteri, kodovi i glifovi su tri različite stvari
Terminologija često zbunjuje ljude jer svakodnevni govor spaja tri različite ideje u reč "slovo". Karakter je apstraktna jedinica pisma, ideja o velikom slovu A, identifikovana u Unicode-u kao U+0041. Glif je nacrtani oblik, kontura sačinjena od krivih i linija koju određeni font koristi za prikaz tog karaktera. Između njih se nalazi kod: bajt ili bajtovi u toku sadržaja (content stream) koji čitaču govore koji glif u trenutnom fontu treba iscrtati.
PDF radi sa kodovima. Kada tok sadržaja prikazuje string, ti bajtovi su indeksi u aktivnom fontu, a ne Unicode. Kodiranje fonta određuje da kod 65 znači "nacrtaj glif pod brojem 65", i ništa u toj operaciji ne zna da rezultat čoveku izgleda kao slovo A. To je ono što omogućava da se PDF prikazuje identično svuda gde može da pronađe glifove, i to je takođe razlog zašto je ekstrakcija odvojen problem od prikaza: crtanju je potrebna samo mapa kod-u-glif, dok je čitanju potrebna mapa kod-u-Unicode, a to su dve različite tabele koje mogu nezavisno da se ne slažu ili da nedostaju.
Tipovi fontova sa kojima ćete se zaista sresti
ISO 32000 definiše nekoliko tipova rečnika fontova, a u praksi dokument koji primite ili generišete koristi jedan od tri. Poznavanje onoga što gledate objašnjava većinu stvari koje mogu poći po zlu.
Type 1 je Adobe-ov originalni PostScript format kontura, izgrađen od kubnih Bezijeovih krivih. Četrnaest standardnih fontova koje svaki usklađeni čitač mora da obezbedi, porodice Helvetica, Times, Courier, Symbol i ZapfDingbats, su Type 1, i rečnik fonta koji imenuje jedan od njih može legalno izostaviti program fonta. To je jedini slučaj u kojem je neugrađivanje fonta bezbedno po specifikaciji, a ne zahvaljujući sreći. Za bilo koji drugi Type 1 font, program mora biti ugrađen ili će čitač zameniti font nečim drugim, obično metrički sličnim ali vizuelno drugačijim fontom.
TrueType koristi kvadratne krive i potiče iz Apple i Microsoft sveta. To je ono što je većina sistemskih fontova i ono što ćete najčešće ugrađivati. Jednostavan TrueType font u PDF-u je ograničen na jednobajtne kodove, tako da jedan takav font može adresirati najviše 256 glifova odjednom. Ovo ograničenje je strukturalni razlog zašto CJK i druga velika pisma ne mogu da se koriste u jednostavnom fontu.
Type 0, kompozitni ili CID font, predstavlja odgovor na to ograničenje. Koristi višebajtne kodove i CMap za njihovo usmeravanje kroz podređeni CIDFont, čije su konture same po sebi ili TrueType ili CFF/Type 1. Ovo je jedini tip fonta koji može da sadrži hiljade glifova, tako da svaki PDF koji sadrži kineski, japanski, korejski ili široku višejezičnu mešavinu koristi Type 0, bez obzira na to da li je autor o tome razmišljao ili ne. Cena toga je složenost: više pokretnih delova, od kojih više njih mora biti ispravno i za prikazivanje i za ekstrakciju.

Jedan detalj iza te slike utiče na veličinu datoteke. Font je biblioteka kontura, a ne bitmapa fiksne veličine, tako da isti ugrađeni program služi za svaku veličinu tačke na stranici. Skaliranje je transformacija koja se primenjuje u vreme crtanja, zbog čega naslov i njegov glavni tekst dele isti ugrađeni font i zašto je trošak ugradnje po fontu, a ne po veličini.
Ugradnja je razlika između prenosivog i krhkog
Ugradnja (embedding) znači da je program fonta, odnosno stvarni podaci o konturama, upisan u PDF kao tok podataka (stream). Čitač na računaru koji nikada nije čuo za vaš font čita te konture direktno iz datoteke i iscrtava tačne glifove. Ako preskočite ugradnju, kladite se da odredište ima font istog imena; kada ga nema, čitač se vraća na zamenski font. Za standardnih četrnaest fontova ta zamena je definisana i bezopasna. Za sve ostalo, to se kreće od male razlike u drugom fontu do praznih kvadratića kada nijedan zamenski font uopšte ne pokriva to pismo.
U HotPDF-u, kontrola se vrši preko jednog svojstva koje se podešava pre otvaranja dokumenta. FontEmbedding govori biblioteci da spakuje fontove koje koristi za crtanje u datoteku:
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;
Redosled nije estetske prirode. BeginDoc je mesto gde HotPDF upisuje strukturu dokumenta, tako da FontEmbedding mora biti postavljen na true pre tog poziva. Ako ga dodelite nakon toga, neće biti greške ni upozorenja, već će se samo dobiti datoteka koja je tiho otišla bez svojih fontova. To je najgora vrsta greške: prolazi sve testove na računaru programera, gde je font slučajno instaliran, a pojavljuje se tek kod klijenta, gde nije.
Ugradnja je takođe mesto gde se licenciranje susreće sa inženjeringom. Program fonta nosi oznake koje opisuju da li se može slobodno ugrađivati, samo za pregled ili se uopšte ne sme ugrađivati. Poštovanje tih oznaka je vaša odgovornost, a ne odgovornost čitača, i "radilo je" nije isto što i "bilo je dozvoljeno".
Podskupovi (subsetting): ugradite samo glifove koje ste koristili
Potpuna ugradnja upisuje ceo program fonta u datoteku. Veliki CJK TrueType font može imati nekoliko megabajta, a njegovo ugrađivanje u celosti za prikazivanje desetak karaktera je rasipanje koje se akumulira u višestranom dokumentu. Pravljenje podskupa (subsetting) rešava ovo upisivanjem samo onih glifova koje dokument referencira, a zatim preimenovanjem fonta pomoću oznake od šest slova i znaka plus, u obliku ABCDEF+Calibri na listi fontova svakog PDF-a sa podskupom, kako čitač nikada ne bi pomešao delimični font sa punim sistemskim fontom istog naziva.
Za većinu generisanih dokumenata, pravljenje podskupa je ispravan podrazumevani izbor. Ono održava veličinu datoteke proporcionalnom sadržaju, a ne izvornom fontu, što je najvažnije za velike višejezične fontove koji bi inače dominirali datotekom. Jedino upozorenje je da podskup sadrži samo ono što je korišćeno u vreme kreiranja. Ako kasniji proces pokuša da doda tekst koristeći podskup fonta, glifovi koji su mu potrebni možda neće biti u datoteci, što predstavlja realno ograničenje za inkrementalno uređivanje tuđeg PDF-a.
Unicode fontovi i problem CJK kvadratića
Kada tekst nije čisti latinica, put jednostavnih fontova se završava, a rešenje je eksplicitna registracija Unicode-kompatibilnog fonta i prepuštanje HotPDF-u da od njega izgradi Type 0 font. RegisterUnicodeTTF učitava TrueType datoteku preko njene putanje; nakon toga je registrovano ime upotrebljivo 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;
Dve stvari odlučuju o uspehu ovoga. Font mora da pokriva pisma u stringu: TrueType koji sadrži samo latinicu neće razviti kineske glifove zato što ste to tražili, i rezultat će ponovo biti prazni kvadratići, ovog puta zato što glif zaista ne postoji u tom fontu. Takođe, ugradnja mora ostati uključena, jer je Type 0 font sastavljen od registrovanog TTF-a besmislen za čitač koji ne može da pronađe konture. Za mešoviti sadržaj, siguran izbor je font sa širokom pokrivenošću, pri čemu su porodice Noto i Arial Unicode MS uobičajeni odgovori, ugrađeni i sa izdvojenim podskupom.
Pisma sa desna na levo i složena pisma dodaju sloj oblikovanja (shaping) povrh pokrivenosti. HotPDF nudi funkciju RtLTextOut za arapski i hebrejski jezik, koja upravlja usmerenim ređanjem, tako da vi prosleđujete logički redosled, a biblioteka ga sama raspoređuje. Ispravan prikaz arapskog jezika zahteva pokrivenost plus oblikovanje plus smer pisanja, što su tri odvojene stvari, i kvadratić tamo može značiti da je bilo koja od njih otkazala.
Tabela ToUnicode: gde živi kopiranje i lepljenje
Sve gore navedeno odnosi se na crtanje. Ekstrakcija je slika u ogledalu i ne uspeva iz sopstvenih razloga. Čitač prikazuje stranicu koristeći mapiranje kod-u-glif, ali kada korisnik označi tekst i kopira ga, čitač mora da te iste kodove vrati nazad u Unicode. To obrnuto mapiranje je ToUnicode CMap, opcioni tok podataka koji je pridružen fontu.
Kada je ona prisutna i ispravna, kopirani tekst se dobija u vidu pravih karaktera. Kada je odsutna ili pogrešna, ili je font napravljen kao podskup sa prilagođenim kodovima glifova bez upisivanja ToUnicode tabele, stranica izgleda savršeno, a privremena memorija (clipboard) se puni smećem: kodovi glifova se čitaju kao da su Unicode, što za prilagođeno kodiran podskup oni nisu. Zbog toga skenirani dokument sa OCR tekstualnim slojem može biti pretraživ, dok digitalno rođeni PDF koji je kreirao nepažljiv generator to nije. Prikazivanje i ekstrakcija se oslanjaju na različite tabele, pa datoteka može da zadovolji jednu, a da ne uspe na drugoj. Ako vam je ekstrakcija važna za izlaz, tretirajte ispravnu ToUnicode mapu kao zahtev i proverite je kopiranjem teksta iz uzorka, umesto da verujete da je tu.
Kako brzo dijagnostikovati grešku sa fontom
Način na koji dolazi do greške govori vam gde da gledate. Prazni kvadratići na drugom računaru skoro uvek znače da font nije ugrađen, pa prvo proverite ugradnju, a zatim pokrivenost glifovima. Kvadratići koji se pojavljuju čak i na vašem računaru ukazuju na pokrivenost: font ne sadrži to pismo, bez obzira na ugradnju. Tekst koji se prikazuje ispravno, ali se kopira kao besmislica, predstavlja ToUnicode problem, a ne problem sa prikazivanjem, i igranje sa fontovima ili ugradnjom to neće rešiti jer crtanje nikada nije bilo pokvareno. Da biste pročitali gotovu datoteku, otvorite je u Acrobat-u i pogledajte Document Properties, pa Fonts: ispravan unos prikazuje tip, navovi "Embedded" ili "Embedded Subset", i imenuje kodiranje. Font koji je trebalo da bude ugrađen, a nije, tamo se najavljuje pre nego što to klijent učini.
Ništa od ovoga nije neobično kada je jasna podela između karaktera, koda i glifa. Ugradite fontove kojima crtate, napravite podskupove za velike, posegnite za Unicode fontom i iskoristite RegisterUnicodeTTF čim tekst napusti latinicu, i sačuvajte ispravnu ToUnicode mapu ako će neko ekstrahovati tekst. Uradite to kako treba i kvadratići će prestati da se pojavljuju. Za prateće mehanizme, anatomija minimalnog PDF-a pokazuje gde se rečnik fontova nalazi u stablu objekata, dok vodič kroz strukturu dokumenta pokriva kako se resursi dele na stranicama.
Pozivi SetFont, FontEmbedding i RegisterUnicodeTTF koji su ovde prikazani deo su HotPDF komponente za Delphi i C++Builder.