Dokument PDF, ki na vašem računalniku izgleda odlično, na računalniku nekoga drugega pa se prikaže kot vrsta praznih kvadratkov, je najpogostejša napaka s pisavo v programski opremi za dokumente, pri čemer to skoraj nikoli ne pomeni, da je besedilo napačno. Znaki so nepoškodovani, kodiranje je pravilno, le samih glifov (oblik znakov) ni. Razlika med računalnikoma je v pisavah, ki jih ima nameščen operacijski sistem. Razkorak med prenosljivo in občutljivo datoteko pa izhaja iz ene same odločitve ob zapisu strani: ali je pisava potovala znotraj datoteke PDF ali pa se je predvidevalo, da je že prisotna pri prejemniku.
Za razumevanje, zakaj se to dogaja in zakaj ločena napaka povzroči, da se besedilo, ki ga je mogoče iskati, prekopira kot nerazumljive smeti, si moramo ogledati, kako PDF shranjuje besedilo. PDF ne shranjuje stavkov. Shranjuje kode glifov skupaj s programom pisave in tabelami, ki jih povezujejo. Vsaka napaka pri izrisu ali ekstrakciji živi v razkoraku med temi tremi elementom. V nadaljevanju je opisano to delovanje po standardu ISO 32000, skupaj s ključi za nadzor v Delphiju.
Znaki, kode in glifi so tri različne stvari
Izrazoslovje pogosto povzroča zmedo, saj v vsakdanjem govoru tri različne koncepte združimo v besedo "črka". Znak (character) je abstraktna enota pisanja, na primer velika črka A, ki je v standardu Unicode določena kot U+0041. Glif (glyph) je izrisana oblika, obris s krivuljami in črtami, ki ga določena pisava uporablja za prikaz tega znaka. Med njima pa se nahaja koda (code): bajt ali bajti v toku vsebine, ki pregledovalniku povedo, kateri glif v trenutni pisavi naj izriše.
PDF deluje s kodami. Ko tok vsebine prikaže niz, so ti bajti indeksi v aktivni pisavi in ne vrednosti Unicode. Kodiranje pisave določa, da koda 65 pomeni "izriši glif, shranjen pod številko 65", pri čemer sam postopek ne ve, da je rezultat za človeka videti kot črka A. Zaradi tega se PDF povsod izriše enako, če le najde glife. To je tudi razlog, zakaj je ekstrahiranje besedila ločena težava od samega prikaza: risanje potrebuje le preslikavo koda-glif, branje pa preslikavo koda-Unicode. Gre za dve različni tabeli, ki se lahko razlikujeta ali pa neodvisno manjkata.
Tipi pisav, s katerimi se boste dejansko srečali
Standard ISO 32000 opredeljuje več vrst slovarjev pisav, v praksi pa dokument, ki ga prejmete ali ustvarite, uporablja enega izmed treh tipov. Poznavanje teh tipov pojasni večino napak.
Type 1 je Adobejev izvirni PostScript format obrisov, zgrajen iz kubičnih Bezierjevih krivulj. Štirinajst standardnih pisav, ki jih mora zagotoviti vsak skladen bralnik (družine Helvetica, Times, Courier, Symbol in ZapfDingbats), so tipa Type 1. Slovar pisave, ki poimenuje eno izmed njih, lahko zakonito izpusti program pisave. To je edini primer, ko je ne-vgrajevanje pisave varno po sami specifikaciji in ne le zaradi sreče. Za katero koli drugo pisavo Type 1 mora biti program vgrajen, sicer pregledovalnik uporabi nadomestno pisavo, ki je običajno metrično podobna, a vizualno drugačna.
TrueType uporablja kvadratne krivulje in izvira iz okolij Apple ter Microsoft. To je večina sistemskih pisav in tiste, ki jih boste najpogosteje vgrajevali. Enostavna pisava TrueType v PDF-ju je omejena na enobajtne kode, kar pomeni, da lahko hkrati naslovi največ 256 glifov. Ta omejitev je strukturni razlog, zakaj pisave CJK (kitajske, japonske, korejske) in drugi obsežni nabori znakov ne lahko delujejo kot enostavne pisave.
Type 0 (sestavljena ali s CID-ključi označena pisava) je odgovor na to omejitev. Uporablja večbajtne kode in tabelo CMap za njihovo usmerjanje skozi podrejeno pisavo CIDFont, katere obrisi so prav tako TrueType ali CFF/Type 1. To je edini tip pisave, ki lahko vsebuje na tisoče glifov, zato vsak PDF s kitajskim, japonskim, korejskim ali mešanim večjezičnim besedilom uporablja Type 0, ne glede na to, ali se je avtor tega zavedal ali ne. Kompromis pa je zahtevnost: več gibljivih delov, ki morajo biti vsi pravilno nastavljeni tako za risanje kot za ekstrahiranje besedila.

Ena izmed podrobnosti na tej sliki vpliva na velikost datoteke. Pisava je knjižnica obrisov in ne slikovnih pik fiksne velikosti, zato isti vgrajeni program služi vsem velikostim točk na strani. Sprememba velikosti je transformacija, ki se izvede ob risanju. Zato naslov in osrednje besedilo uporabljata isto vgrajeno pisavo, strošek vgrajevanja pa se obračuna na posamezno pisavo in ne na njeno velikost.
Vgrajevanje je razlika med prenosljivostjo in občutljivostjo
Vgrajevanje (embedding) pomeni, da so dejanski podatki o obrisih pisave zapisani v PDF kot tok. Bralnik na računalniku, ki ne pozna vaše pisave, prebere te obrise neposredno iz datoteke in natančno izriše glife. Če vgrajevanje izpustite, stavite na to, da ima prejemnik nameščeno pisavo z enakim imenom; če je nima, bo bralnik uporabil nadomestno pisavo. Pri standardnih štirinajstih pisavah je to nadomeščanje določeno in neopazno. Pri vseh ostalih pa se lahko giblje od majhnih razlik v izgledu do praznih kvadratkov, če nobena nadomestna pisava ne podpira teh znakov.
V knjižnici HotPDF to nadzoruje ena sama lastnost, ki jo je treba nastaviti pred odpiranjem dokumenta. Lastnost FontEmbedding naroči knjižnici, naj pisave, s katerimi riše, zapakira v datoteko:
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;
Vrstni red ni le kozmetični popravek. Klic BeginDoc je točka, kjer HotPDF potrdi strukturo dokumenta, zato mora biti lastnost FontEmbedding nastavljena na true pred tem klicem. Če jo dodelite kasneje, ne bo napak ali opozoril, datoteka pa bo preprosto poslana brez pisav. To je najslabša vrsta napake: uspešno prestane vse teste na razvijalčevem računalniku, kjer je pisava nameščena, pojavi pa se šele pri stranki, ki pisave nima.
Vgrajevanje je tudi stičišče licenciranja in programiranja. Program pisave vsebuje oznake, ki določajo, ali se lahko vgradi prosto, le za predogled ali pa sploh ne. Spoštovanje teh omejitev je vaša odgovornost in ne odgovornost pregledovalnika; dejstvo, da je zadeva tehnično delovala, še ne pomeni, da je licenčno dovoljena.
Delni uvoz (Subsetting): vgrajevanje le uporabljenih glifov
Polno vgrajevanje zapiše celoten program pisave v datoteko. Obsežna pisava TrueType CJK lahko meri več megabajtov, njeno celotno vgrajevanje za prikaz le nekaj znakov pa je neučinkovito, kar se pri večstranskih dokumentih še stopnjuje. Delni uvoz (subsetting) to rešuje tako, da zapiše le tiste glife, na katere se dokument sklicuje, nato pa pisavo preimenuje s šestčrkovno oznako in znakom plus, na primer v obliko ABCDEF+Calibri, tako da bralnik delne pisave nikoli ne zameša s polno sistemsko pisavo z enakim imenom.
Za večino generiranih dokumentov je delni uvoz pravilna privzeta nastavitev. Velikost datoteke ohranja sorazmerno z vsebino in ne z velikostjo izvorne pisave, kar je najbolj pomembno pri velikih večjezičnih pisavah, ki bi sicer prevladovale v datoteki. Edino opozorilo je, da delni uvoz vsebuje le znake, uporabljene ob ustvarjanju dokumenta. Če kasnejši proces poskuša dodati besedilo v to pisavo, potrebnih glifov morda ne bo v datoteki, kar predstavlja resno omejitev pri naknadnem urejanju tujih datotek PDF.
Pisave Unicode in težava s kvadratki pri CJK
Ko besedilo ne vsebuje le latinice, preprosta pot s sistemskimi pisavami odpove. Rešitev je eksplicitna registracija pisave s podporo za Unicode, s čimer HotPDF iz nje zgradi pisavo Type 0. Funkcija RegisterUnicodeTTF naloži datoteko TrueType prek njene poti, nato pa je registrirano ime uporabno v metodi SetFont kot katero koli 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;
Za uspeh sta pomembni dve stvari. Pisava mora vsebovati znake, ki so v nizu: pisava TrueType, ki podpira le latinico, ne bo čudežno dobila kitajskih znakov le zato, ker ste jo to prosili, rezultat pa bodo znova prazni kvadratki, saj ti glifi v tej pisavi sploh ne obstajajo. Pravno pa mora ostati vključeno vgrajevanje pisave, saj je pisava Type 0, sestavljena iz registrirane pisave TTF, neuporabna za bralnik, ki ne more dostopati do njenih obrisov. Za mešano vsebino so najboljša izbira široko podprte pisave, kot sta družini Noto ali Arial Unicode MS, ki jih vgradite z delnim uvozom.
Pisave z desne proti levi in kompleksne pisave dodajajo še plast oblikovanja poleg same podpore znakom. Knjižnica HotPDF ponuja metodo RtLTextOut za arabščino in hebrejščino, ki upravlja spreminjanje smeri besedila, tako da vi podate logični vrstni red, knjižnica pa poskrbi za izris. Pravilen prikaz arabščine zahteva podporo znakom, oblikovanje ter pravilno smer; to so tri ločene stvari, napaka pri kateri koli izmed njih pa se prikaže kot prazen kvadratek.
Tabela ToUnicode: kje živi funkcija kopiraj-prilepi
Vse zgoraj opisano se nanaša na risanje. Ekstrahiranje besedila pa je zrcalna slika in odpove iz lastnih razlogov. Pregledovalnik izriše stran z uporabo preslikave koda-glif, ko pa uporabnik označi in kopira besedilo, mora pregledovalnik te kode pretvoriti nazaj v Unicode. Ta obratna preslikava je tabela ToUnicode CMap, ki je neobvezen tok, pripet k pisavi.
Ko je ta tabela prisotna in pravilna, se kopirano besedilo prikaže s pravilnimi znaki. Če pa manjka ali pa je napačna (ali pa je bila pisava uvožena delno s prilagojenimi kodami glifov brez tabele ToUnicode), bo stran videti odlično, v odložišče pa se bodo prekopirale neuporabne smeti: kode glifov se berejo, kot da bi bile Unicode znaki, kar pri prilagojeni pisavi niso. To je razlog, zakaj je po skeniranem dokumentu s prepoznano plastjo OCR mogoče brskati, po elektronsko ustvarjenem PDF-ju iz nepazljivega generatorja pa ne. Risanje in uvoz besedila uporabljata različni tabeli, zato lahko datoteka ustreza eni in odpove pri drugi. Če je branje besedila pomembno za vaš izhod, določite pravilno tabelo ToUnicode kot obvezno zahtevo in jo preverite s kopiranjem besedila iz vzorca.
Kako hitro diagnosticirati težavo s pisavo
Način odpovedi vam pove, kje iskati težavo. Prazni kvadratki na drugem računalniku skoraj vedno pomenijo, da pisava ni bila vgrajena, zato najprej preverite vgrajevanje, nato pa pokritost znakov. Kvadratki, ki se prikažejo že na vašem računalniku, kažejo na pokritost znakov: pisava ne vsebuje tega nabora znakov, ne glede na vgrajevanje. Besedilo, ki se izriše pravilno, a se prekopira kot nesmisel, je težava tabele ToUnicode in ne risanja, spreminjanje nastavitev pisav ali vgrajevanja pa tega ne bo rešilo, saj risanje ni bilo poškodovano. Če želite preveriti končano datoteko, jo odprite v programu Acrobat in si oglejte Lastnosti dokumenta, Pisave (Document Properties, Fonts): urejen vnos prikazuje tip pisave, navaja "Embedded" (vgrajeno) ali "Embedded Subset" (delno vgrajeno) ter poimenuje kodiranje. Pisava, ki bi morala biti vgrajena, pa ni, se bo tam razkrila veliko prej kot se bo pritožila stranka.
Nič od tega ni nenavadno, ko je ločnica med znakom, kodo in glifom jasna. Vgradite pisave, s katerimi rišete, uvozite le dele obsežnih pisav, uporabite pisavo s podporo za Unicode in funkcijo RegisterUnicodeTTF, ko besedilo zapusti latinico, ter ohranite pravilno tabelo ToUnicode, če bo kdor koli kopiral besedilo. S temi nastavitvami bodo kvadratki izginili. Za spoznavanje ozadja si lahko preberete članek o anatomiji minimalnega dokumenta PDF, ki prikazuje mesto slovarja pisave v drevesu objektov, ter članek o strukturi dokumentov, ki opisuje deljenje virov med stranmi.
Klici SetFont, FontEmbedding in RegisterUnicodeTTF, ki so prikazani tukaj, so del knjižnice HotPDF Component za Delphi in C++Builder.