Artykuł techniczny

Struktura pliku PDF: Jak ten format w rzeczywistości działa

PDF nie jest formatem dokumentu w takim sensie jak Word czy RTF. Formaty te przechowują sekwencję treści, którą mechanizm renderujący interpretuje w momencie wyświetlania, więc wynik zależy od tego, jakie czcionki i silnik układu są akurat dostępne. PDF przechowuje wynik tego procesu: precyzyjne instrukcje renderowania, programy czcionek, skompresowane strumienie obrazów oraz graf obiektów, który łączy je w samowystarczalny opis każdej strony. Plik niesie wystarczającą ilość informacji, aby odtworzyć każdą stronę identycznie na dowolnym zgodnym mechanizmie renderującym, co jest jego głównym celem projektowym i źródłem większości złożoności, jaką napotykasz, próbując go programowo wygenerować, przeanalizować lub zmodyfikować

Model obiektowy

Każdy PDF to zbiór ponumerowanych obiektów. Obiektem może być wartość logiczna, liczba całkowita, liczba rzeczywista, nazwa, ciąg znaków, tablica, słownik, strumień lub wartość pusta (null). Prawie wszystko, co interesujące, to słownik, czyli zbiór par klucz-wartość, gdzie klucze są nazwami, a wartości dowolnym innym typem obiektu, łącznie z odniesieniami do innych obiektów poprzez numer i licznik generacji. Strumień to słownik, po którym następuje sekwencja bajtów, zazwyczaj skompresowana

Słownik katalogu (catalog) jest korzeniem (root). Wskazuje na drzewo stron (page tree), które organizuje słowniki stron w strukturze zrównoważonego drzewa zamiast płaskiej listy, dzięki czemu nawigacja do 5000. strony w dokumencie liczącym 10000 stron nie wymaga przechodzenia przez każdy wcześniejszy deskryptor strony. Każdy słownik strony odnosi się do swoich strumieni zawartości (jednej lub więcej sekwencji operatorów opisu strony), swojego słownika zasobów (który z kolei odnosi się do deskryptorów czcionek, przestrzeni barw i obiektów obrazów XObjects) oraz jej pudełka nośnika (media box), czyli przestrzeni współrzędnych, w której istnieje strona. Początek układu współrzędnych znajduje się w lewym dolnym rogu, a dodatnia oś Y skierowana jest ku górze, w jednostkach równych 1/72 cala

Na końcu pliku znajduje się tabela odsyłaczy (cross-reference table), która mapuje numer każdego obiektu na jego przesunięcie w bajtach w pliku. To właśnie umożliwia dostęp swobodny: przeglądarka najpierw czyta tabelę odsyłaczy, a następnie bezpośrednio przechodzi do wymaganych obiektów. W wersji PDF 1.5 wprowadzono strumienie odsyłaczy (cross-reference streams), które kompresują tabelę do obiektu strumienia i pakują powiązane obiekty w strumienie obiektów, zauważalnie zmniejszając rozmiar pliku dla dokumentów zawierających wiele małych obiektów

Strumienie zawartości i model graficzny

Treść wizualna strony mieści się w jednym lub więcej strumieniach zawartości. Każdy strumień to sekwencja operatorów PDF przeplatana ich operandami. Operator tekstowy BT rozpoczyna obiekt tekstowy, Tf wybiera czcionkę i rozmiar ze słownika zasobów, Td pozycjonuje kursor tekstowy, Tj lub TJ maluje ciąg znaków, a ET zamyka obiekt tekstowy. Grafika wektorowa podąża za podobnym schematem: m ustawia punkt początkowy ścieżki, l dołącza segment linii, c dołącza krzywą Beziera, a f lub S wypełnia lub rysuje ścieżkę

Stan graficzny (graphics state) zarządza wszystkim, co dzieje się między operatorami: bieżącą macierzą transformacji, szerokością linii, przestrzenią barw, kolorem wypełnienia, kolorem obrysu i ścieżką odcinania. Operatory takie jak q i Q odkładają i zdejmują stan graficzny na stos (stack), w ten sposób PDF implementuje lokalne transformacje współrzędnych i tymczasowe nadpisania stanu bez wpływania na kontekst wokół nich. Obiekty Form XObject uogólniają to: jest to samowystarczalny strumień zawartości z własnym słownikiem zasobów, który można malować na stronie w dowolnych pozycjach i skalach za pomocą pojedynczego operatora Do

Osadzanie czcionek i wyodrębnianie tekstu

PDF może odwoływać się do czcionek po nazwie i polegać na przeglądarce, że czymś je zastąpi, ale w praktyce każdy dokument, który zamierzasz udostępnić, musi zawierać osadzone dane czcionek. Czcionka Type 1 lub TrueType/OpenType osadzona w PDF przenosi słownik deskryptora czcionki wskazujący na strumień pliku czcionki. Dla czcionek TrueType ten strumień zawiera binarny program czcionki; dla Type 1 są to dane PFB. Podzbiory (subsetting), co robi każdy poważny generator PDF, usuwa glify nieużywane w dokumencie, co pozwala zachować rozsądne rozmiary plików nawet dla dużych czcionek Unicode

Wyodrębnianie tekstu to moment, w którym osadzanie czcionek daje o sobie znać z drugiej strony. Wizualna reprezentacja znaku jest zdeterminowana przez glif w osadzonym programie czcionki. Wartość Unicode tego znaku jest zdeterminowana przez strumień ToUnicode CMap dołączony do słownika czcionki. Kiedy brakuje mapy ToUnicode CMap lub jest ona nieprawidłowa, przeglądarka PDF może czytelnie wyrenderować tekst, ale nie może go wyodrębnić jako użyteczny ciąg Unicode, co jest powodem, dla którego kopiowanie-wklejanie z niektórych plików PDF tworzy zniekształcony tekst. Tagged PDF (ISO 32000 §14.8) dodaje drugą warstwę: drzewo struktury logicznej, które mapuje treść strony na role semantyczne w dokumencie, takie jak akapity, nagłówki i komórki tabeli. Czytniki ekranu i silniki przeformatowujące (reflow) używają drzewa struktury, a nie surowej kolejności ze strumienia zawartości, co wyjaśnia, dlaczego wizualnie dobrze ułożony PDF wciąż może być niedostępny, jeśli tagowania brakuje lub jest nieprawidłowe

Przyrostowe aktualizacje i podpisy cyfrowe

Kiedy zapisujesz zmiany do istniejącego pliku PDF bez przepisywania go od podstaw, nowe obiekty są dołączane po oryginalnym ciele pliku, razem z nową sekcją odsyłaczy i nowym słownikiem końcowym (trailer dictionary). Zaktualizowany słownik końcowy wskazuje na nowe dane odsyłaczy, a zastąpione obiekty pozostają w pliku, ale po prostu nie są powiązane przez nowy łańcuch odsyłaczy. To jest aktualizacja przyrostowa (incremental update) i ma dwie istotne konsekwencje

Po pierwsze, plik rośnie przy każdym cyklu zapisu. Dokument wielokrotnie edytowany i zapisywany gromadzi warstwy przestarzałych obiektów. Narzędzia takie jak QPDF mogą zlinearyzować lub skompresować-i-przepisać plik, aby odzyskać tę przestrzeń, ale wartością domyślną jest akumulacja. Po drugie, podpisy cyfrowe opierają się na przyrostowych aktualizacjach dla ich modelu integralności. Podpis ISO 32000 obejmuje zakres bajtów pliku, zazwyczaj wszystko oprócz samego miejsca (placeholder) na wartość podpisu. Wszelkie zmiany po złożeniu podpisu, które pojawiają się jako dodatkowe aktualizacje przyrostowe, są widoczne dla walidującego czytnika jako modyfikacje wykonane po podpisaniu, co stanowi dokładnie taki ślad audytu (audit trail), jakiego oczekujesz. Oznacza to również, że pewne modyfikacje, takie jak dodanie podpisu zatwierdzającego lub wypełnienie pól formularza, są wyraźnie dozwolone przez standard bez unieważniania oryginalnego podpisu, pod warunkiem, że zmiany te są zgodne z ustawieniami uprawnień dokumentu (ISO 32000-2 §12.7.6). Modyfikacja wykraczająca poza te uprawnienia jest oznaczana jako nieautoryzowana. Prawidłowe uchwycenie tego rozróżnienia ma znaczenie, gdy generujesz dokumenty, które będą kontrasygnowane (countersigned) na dalszych etapach

Poziomy zgodności i linia ISO 32000

PDF zaczął się jako zamknięty format firmy Adobe w 1993 roku, wchłonął model obrazowania języka PostScript i w ciągu piętnastu wersji nagromadził nowe funkcje: szyfrowanie w wersji 1.1, interaktywne formularze w 1.2, podpisy cyfrowe i strukturę logiczną w 1.3, przezroczystość w 1.4, strumienie obiektów w 1.5, szyfrowanie AES w 1.6. Adobe przekazało plik PDF 1.7 do ISO w 2007 roku, a wynikiem było ISO 32000-1:2008. ISO 32000-2:2020 obejmuje PDF 2.0, który zacieśnił kilka niedookreślonych obszarów, zrewidował derywację klucza AES-256 (wersja 6 zastępująca wersję 5) oraz dodał jawne wsparcie dla powiązanych plików i multimediów

Substandardy wywodzą się z tej samej bazy. PDF/A (ISO 19005) zamienia funkcje na stabilność archiwalną: brak szyfrowania, brak zależności treści z zewnątrz, wszystkie czcionki osadzone, przestrzenie barw niezależne od urządzeń, wymagane metadane XMP. PDF/A-1 oparty jest na PDF 1.4, PDF/A-2 na PDF 1.7, a PDF/A-3 dopuszcza osadzanie plików w dowolnym formacie. PDF/X (ISO 15930) to podzbiór dla produkcji druku: zamiary wyjściowe (output intents), spady i przycięcia, brak przezroczystości w starszych poziomach zgodności. PDF/UA (ISO 14289) wymusza strukturę tagowaną, mapowania Unicode i metadane językowe w celach dostępności. Nie są to konkurencyjne formaty; są to zestawy dodatkowych ograniczeń nałożonych na podstawowy PDF i pojedynczy plik może być jednocześnie zgodny z więcej niż jednym, o ile ograniczenia te ze sobą nie kolidują

Dla każdego piszącego kod, który generuje lub przetwarza dokumenty PDF, praktyczną bazą odniesienia jest ISO 32000-2, ze zwróceniem szczególnej uwagi na sekcje opisujące model odsyłaczy (§7.5), stan graficzny (§8.4), operatory stanu tekstu (§9.3), deskryptory czcionek i ToUnicode (§9.6 i §9.10), formularze interaktywne (§12.7) oraz podpisy cyfrowe (§12.8). Standard jest długi, ale większość programistycznej pracy nad formatem PDF cyklicznie dotyka jego wąskiego wycinka. Zrozumienie modelu obiektowego i mechanizmu odsyłaczy jest punktem wyjścia; wszystko inne to już tylko specjalizacja