Przejdź do głównej zawartości

Czcionki: ta trudna część

Evidence: Mixed evidence

Czcionki to obszar, w którym plik PDF może wyglądać zupełnie poprawnie, a mimo to mieć cichą usterkę. Strona może renderować właściwe glify, a jednocześnie nie dać się przeszukać ani skopiować jako tekst i być niezgodna z profilem archiwizacyjnym. Wszystko to może wystąpić naraz, bez żadnego widocznego ostrzeżenia. Ta strona omawia trzy rzeczy, które muszą zostać wykonane poprawnie — osadzanie, tworzenie podzbiorów i kodowanie — oraz to, co robi dla każdej z nich NextPDF.

„Wygląda dobrze” to najgroźniejsze zdanie w pracy z plikami PDF, a czcionki to obszar, w którym wyrządza ono największe szkody. Muszą być spełnione trzy niezależne warunki:

  1. Osadzanie — program czcionki jest przenoszony razem z plikiem, dzięki czemu dokument renderuje się tak samo na komputerze, który nie ma zainstalowanej tej czcionki.
  2. Tworzenie podzbiorów — do pliku trafiają tylko faktycznie używane glify, więc czcionka CJK o rozmiarze 20 MB nie powiększa każdego dokumentu.
  3. Kodowanie — istnieje poprawne odwzorowanie kodów znaków użytych na stronie z powrotem na Unicode, dzięki czemu tekst można wyszukiwać, kopiować, indeksować oraz odczytywać za pomocą technologii wspomagających.

Renderowanie wizualne potwierdza co najwyżej część pierwszego warunku. Dokument może pokazywać idealne glify, a mimo to całkowicie nie spełniać trzeciego — tekst jest obrazem słów, a nie słowami. To właśnie ta usterka przechodzi każdą recenzję typu „wygląda dobrze”, a potem kończy się niepowodzeniem przy audycie zgodności lub żądaniu ujawnienia dowodów.

  • Czcionka w pliku PDF to zwykle słownik oraz strumień osadzonego programu czcionki.
  • Tworzenie podzbiorów przepisuje ten program tak, aby zawierał tylko używane glify. Nazwa czcionki będącej podzbiorem otrzymuje znacznik z sześciu wielkich liter oraz +, aby czytniki traktowały ją jako odrębną.
  • Kodowanie to osobny problem odwzorowania kodów znaków na Unicode. CMap /ToUnicode sprawia, że tekst da się wyszukiwać i kopiować — i jest to niezależne od tego, czy glify wyglądają poprawnie.
  • Tekst wyglądający poprawnie bez /ToUnicode (lub z błędnym) to klasyczna cicha usterka: idealny na ekranie, w praktyce niemożliwy do wyszukania.
  • NextPDF tworzy podzbiory czcionek TrueType, zachowuje tożsamość glifów dla poprawnego renderowania oraz emituje CMap /ToUnicode, dzięki czemu ekstrakcja działa — i potrafi wymuszać regułę osadzania z PDF 2.0, a nie tylko ostrzegać.

Tworzenie podzbiorów. FontSubsetter (src/Typography/FontSubsetter.php) analizuje pierwotny katalog tablic TrueType i odczytuje cmap, aby odwzorować punkty kodowe Unicode na identyfikatory glifów. Obsługuje zarówno format 4 dla BMP, jak i format 12 dla pełnego Unicode, którego wymaga CJK. Następnie wykonuje krok, który pomijają naiwne narzędzia do tworzenia podzbiorów: rozwiązuje zależności glifów złożonych przez domknięcie przechodnie. Glif z akcentem, zbudowany z litery bazowej oraz znaku łączącego, odwołuje się do innych glifów jako komponentów. Jeśli te komponenty zostaną pominięte, glif renderuje się błędnie. Narzędzie do tworzenia podzbiorów przechodzi przez ten graf, dopóki nie pojawi się żaden nowy komponent, z zabezpieczeniem przed cyklami, aby błędnie sformowana czcionka nie mogła zapętlić się w nieskończoność.

Dwie decyzje inżynierskie w tym pliku warto wymienić z nazwy. Po pierwsze, identyfikatory glifów są zachowywane, a nie przemapowywane — nieużywane sloty są wypełniane zerami w glyf/loca, dzięki czemu pierwotne indeksy glifów w strumieniu zawartości pozostają poprawne przy CIDToGIDMap /Identity. Przemapowanie dałoby mniejszy rozmiar, ale wymagałoby przepisania każdego odwołania do glifu. Zachowanie tożsamości zapewnia poprawność już na poziomie konstrukcji. Po drugie, przechodzenie jest posortowane (rosnąco według gid), dzięki czemu podzbiór jest deterministyczny bajtowo — ta sama czcionka i te same używane glify dają te same bajty podzbioru, czego wymagają powtarzalne kompilacje. Jeśli tworzenie podzbioru zaoszczędziłoby mniej niż około 10% pliku, zwracana jest niezmieniona oryginalna czcionka. Narzut nie jest wart marginalnego zysku.

Osadzanie. Jawne zasady decydują o tym, czy program czcionki w ogóle trafia do pliku — nigdy nie ma tu zgadywania. Pdf20FontEmbeddingPolicy (src/Writer/Pdf20FontEmbeddingPolicy.php) ma dwa tryby. W profilu PDF 2.0 Strict odrzuca odwołanie do nieosadzonej standardowej czcionki Type 1 („Base14”) typowanym wyjątkiem — co jest zachowaniem zgodnym z wymaganiami. AllowBase14 zachowuje historyczny tryb doradczy. W okresie migracji emituje minimalny deskryptor czcionki, którego standard wciąż wymaga, i wysyła ostrzeżenie, zamiast zgłaszać wyjątek. Kod wywołujący wybiera go jawnie na poziomie dokumentu; nigdy nie jest on wnioskowany z czcionki.

Kodowanie. Dla czcionek złożonych (Type 0) EmbeddedTtfFontDictBuilder (src/Writer/EmbeddedTtfFontDictBuilder.php) emituje potomka CIDFontType2, rodzica Type0 oraz strumień CMap /ToUnicode, dzięki czemu kody znaków można odwzorować z powrotem na Unicode. Strumień /ToUnicode jest zasadnie nieobecny tylko w jednym przypadku: gdy samoopisująca się predefiniowana CMap CJK już daje czytnikowi odwzorowanie znaków na Unicode. Tam CMap jest kodowaniem, więc zwykły profil pomija nadmiarowy strumień /ToUnicode, aby zaoszczędzić bajty. Poza tym przypadkiem to strumień /ToUnicode utrzymuje tekst jako tekst.

ZagadnienieCo gwarantujeCzego nie gwarantujeCicha usterka, gdy jest błędne
OsadzanieTakie samo renderowanie bez zainstalowanej czcionkiŻe tekst da się wyszukaćPodstawiona czcionka; błędne metryki na innym komputerze
Tworzenie podzbiorówMały plik; tylko używane glifyCokolwiek dotyczącego kodowaniaBrakujące komponenty złożone → uszkodzone glify z akcentami
Kodowanie (/ToUnicode)Tekst, który da się wyszukać, skopiować i jest dostępnyŻe glify renderują się poprawnieIdealnie wyglądająca strona, niemożliwa do wyszukania / zniekształcony tekst przy kopiowaniu

Trzy zagadnienia dotyczące czcionek są niezależne. Osadzanie i tworzenie podzbiorów dotyczą wyglądu i rozmiaru; kodowanie dotyczy znaczenia. Strona może przejść pierwsze dwa i nie spełnić trzeciego, bez żadnego widocznego sygnału.

Reguła nazewnictwa podzbiorów czcionek jest normatywna i precyzyjna. Spec: ISO 32000-2, §9.9.2 wymaga, aby nazwa PostScript podzbioru czcionki — BaseFont oraz FontName w deskryptorze — zaczynała się od znacznika z dokładnie sześciu wielkich liter, następnie znaku plus, a potem pierwotnej nazwy PostScript czcionki. Wymaga również, aby różne podzbiory tej samej czcionki w jednym pliku używały różnych znaczników. To właśnie ta reguła pozwala czytnikowi odróżnić od siebie dwa podzbiory i poprawnie scalać dokumenty. Evidence: Standard-backed

Kodowanie jest opisane niezależnie od renderowania. Spec: ISO 32000-2, §9.10.3 definiuje /ToUnicode jako strumień zawierający CMap, która odwzorowuje kody znaków na wartości Unicode, a procedura ekstrakcji tekstu w Spec: ISO 32000-2, §9.10.2 używa tej CMap do konwersji kodów znaków na Unicode na potrzeby wyszukiwania i indeksowania. Nic w mechanizmie malowania glifów nie odwołuje się do /ToUnicode — i właśnie dlatego tekst może wyglądać poprawnie, a przy ekstrakcji dawać błędny wynik.

W kwestii osadzania standard stwierdza, że większość słowników czcionek zawiera deskryptor czcionki, którego strumień osadzonego pliku czcionki jest opcjonalny, lecz zdecydowanie zalecany. PDF 2.0 zaostrza to konkretnie dla czternastu standardowych czcionek Type 1. Zasady Strict w NextPDF to zgodna z wymaganiami interpretacja tego zaostrzenia. AllowBase14 to jawna, opcjonalna furtka zgodności wstecznej — silnik nigdy po cichu nie obniża rygoru.

Strict PDF 2.0 font-embedding enforcement — edition availability
Edition Availability
Core

Dostępne. Tworzenie podzbiorów, emisja /ToUnicode oraz jawne zasady osadzania Strict / AllowBase14 to podstawowe zachowanie silnika.

Pro

Dodaje głębsze egzekwowanie zgodności oraz raportowanie dotyczące osadzania czcionek na poziomie profilu.

Enterprise

Dodaje to samo egzekwowanie zgodności w ramach warstwy operacyjnej dla przedsiębiorstw.

Oto dwie części poprawnie osadzonej czcionki złożonej po utworzeniu podzbioru, dzięki której tekst da się wyszukać. Znacznik podzbioru jest zgodny ze standardową regułą sześciu liter; odwołanie /ToUnicode sprawia, że tekst nadal nadaje się do ekstrakcji.

% The Type 0 (composite) font dictionary
20 0 obj
<< /Type /Font /Subtype /Type0
/BaseFont /ABCDEF+NotoSans % six-letter subset tag + '+'
/Encoding /Identity-H
/DescendantFonts [21 0 R]
/ToUnicode 23 0 R >> % the map that makes text searchable
endobj
% The descendant CIDFontType2 (carries the subsetted program)
21 0 obj
<< /Type /Font /Subtype /CIDFontType2
/BaseFont /ABCDEF+NotoSans
/CIDToGIDMap /Identity % glyph IDs preserved, not remapped
/FontDescriptor 22 0 R >>
endobj

Wpis /ToUnicode 23 0 R w obiekcie 20 to różnica między dokumentem, który da się wyszukać, a jego obrazem. Pomiń go (poza przypadkiem predefiniowanej CMap), a każdy glif wciąż narysuje się idealnie, lecz wyszukanie dowolnego słowa na stronie nie znajdzie niczego.

Pułapka, mówiąc wprost: poprawne renderowanie glifów nie mówi nic o tym, czy tekst jest tekstem. Renderowanie idzie ścieżką od kodowania do glifu. Wyszukiwanie i kopiowanie idą ścieżką od kodu do Unicode (/ToUnicode). To różne mechanizmy, które odczytują różne części słownika czcionki. Dokument może zatem mieć nienaganny obraz wizualny oraz brakujący lub błędny /ToUnicode. W efekcie powstaje strona, która wygląda wiarygodnie, a funkcjonalnie nie da się jej przeszukać — usterka, która przetrwa każdą recenzję wizualną, bo z definicji nie ma nic do zobaczenia.

Pokrewna pułapka: założenie „czcionka jest osadzona, więc archiwizacja jest w porządku”. Osadzanie jest konieczne, ale niewystarczające. Profil taki jak PDF/A oczekuje również podzbiorów z nazwami zgodnymi z regułą sześciu liter oraz poprawnego kodowania. Dokument z osadzoną czcionką, ale bez możliwości wyszukiwania tekstu, wciąż nie spełnia wymagań.

Narzędzie do tworzenia podzbiorów w NextPDF obsługuje konkretnie czcionki TrueType. Wymaga niezbędnych tablic TrueType i zwraca niezmienioną oryginalną czcionkę, gdy ich brakuje lub gdy zysk jest poniżej progu około 10%. Tworzenie podzbiorów oraz CMap /ToUnicode umożliwiają ekstrakcję tekstu, ale nie potrafią uratować czcionki źródłowej, której brakuje informacji do odwzorowania glifu z powrotem na sensowny znak. Tam, gdzie nie da się ustalić wartości Unicode, sama emisja CMap jej nie wymyśli.

Ta strona dotyczy tworzenia poprawnej struktury czcionek w dokumentach, które NextPDF zapisuje. Nie jest to mechanizm naprawy czcionek w dowolnych przychodzących plikach PDF. A emisja zgodnego podzbioru i zgodnego kodowania sama z siebie nie certyfikuje dokumentu względem pełnego profilu archiwizacyjnego — to osobne, szersze sprawdzenie.

Dlaczego znacznik z sześciu liter — czemu nie nazwa czcionki? Aby czytnik mógł odróżnić od siebie dwa różne podzbiory tej samej czcionki i scalać dokumenty bez kolizji ich zestawów glifów. Różne podzbiory oznaczają różne znaczniki — taka jest reguła.

Kiedy dopuszczalny jest brak /ToUnicode? Gdy samoopisująca się predefiniowana CMap CJK już zapewnia odwzorowanie znaków na Unicode. Tam CMap jest kodowaniem. Osobny /ToUnicode byłby nadmiarowy. Poza tym jego brak jest usterką.

Czy tworzenie podzbiorów kiedykolwiek szkodzi? Tylko wtedy, gdy jest wykonane błędnie. Pominięcie komponentów glifów złożonych uszkadza glify z akcentami. Przemapowanie identyfikatorów glifów bez przepisania odwołań uszkadza renderowanie. NextPDF unika obu problemów, wyznaczając domknięcie komponentów i zachowując tożsamość glifów.

  • Strumienie i filtry — osadzone programy czcionek to filtrowane obiekty strumieniowe z własnym kontraktem dekodowania.
  • Czym właściwie jest PDF — model obiektowy, w którym żyją słowniki czcionek i strumienie programów czcionek.
  • PDF 2.0: co się zmieniło — w tym zaostrzone oczekiwania dotyczące osadzania czcionek w standardzie PDF 2.0.
  • Osadzony program czcionki — faktyczny plik czcionki (TrueType/CFF/Type 1) przenoszony wewnątrz pliku PDF jako strumień, dzięki czemu renderowanie nie zależy od czcionek zainstalowanych u czytnika.
  • Tworzenie podzbiorów — przepisanie programu czcionki tak, aby zawierał tylko glify używane w dokumencie, w celu zmniejszenia rozmiaru.
  • Znacznik podzbioru — obowiązkowy przedrostek z sześciu wielkich liter plus + w nazwie czcionki będącej podzbiorem (na przykład ABCDEF+NotoSans).
  • /ToUnicode — strumień CMap odwzorowujący kody znaków na wartości Unicode; mechanizm, który sprawia, że tekst PDF da się wyszukiwać, kopiować i jest dostępny.
  • Glif złożony — glif zbudowany z odwołań do innych glifów jako komponentów; jego komponenty muszą zostać zachowane podczas tworzenia podzbioru.
  • CIDToGIDMap /Identity — tryb, w którym indeksy glifów w strumieniu zawartości są niezmienionymi identyfikatorami glifów z samej czcionki; NextPDF zachowuje tożsamość glifów, aby zachować tę właściwość.
  • Base14 — czternaście standardowych czcionek Type 1; PDF 2.0 oczekuje, że czcionki będą osadzane, a nie przywoływane po nazwie.