Przejdź do głównej zawartości
getnextpdf.com

Anatomia pliku PDF

Otwórz dowolny PDF w zwykłym edytorze tekstu, a pierwsze, co zobaczysz, jest uspokajające: nagłówek %PDF-1.x lub %PDF-2.0. Ostatnie, co zobaczysz, to %%EOF. Wszystko między tymi dwoma wierszami to schludna mała maszyna do odnajdywania obiektów po numerze. Ta strona to sekcja. Otwieramy plik, nazywamy każdy organ i pokazujemy, jak się łączą.

To strukturalny towarzysz dwóch sąsiadów. Czym naprawdę jest PDF traktuje plik jako graf obiektów; aktualizacje przyrostowe omawiają, jak rośnie on w czasie. Ta strona trzyma się blisko bajtów — fizycznych regionów, przez które przechodzi parser, w kolejności, w jakiej leżą na dysku.

Niemal nigdy nie potrzebujesz tego, by użyć PDF. Potrzebujesz tego w dniu, w którym jeden idzie nie tak. Plik otwiera się w jednej przeglądarce, a w innej nie; walidator zgłasza „uszkodzoną tablicę odwołań krzyżowych”; podpisany dokument nagle nie przechodzi weryfikacji. Żadna z tych rzeczy nie jest tajemnicą, gdy potrafisz czytać anatomię. To liczba, która już nie pasuje do pozycji, region w niewłaściwym miejscu lub ogon, który wskazuje na nic.

Znajomość układu zamienia „PDF jest uszkodzony” w diagnozę, na którą możesz zareagować. To różnica między wzruszeniem ramion nad czarną skrzynką a wskazaniem dokładnego bajta, który kłamie.

Zgodny PDF ma cztery fizyczne części, w tej kolejności w pliku (Spec: ISO 32000-2, §7.5.1):

  1. Nagłówek — jeden wiersz, %PDF-2.0, nazywający wersję.
  2. Treść — większość pliku: ciąg numerowanych obiektów pośrednich.
  3. Sekcja odwołań krzyżowych — indeks od numeru obiektu do przesunięcia bajtowego, gdzie ten obiekt mieszka. Klasyczne PDF-y używają tekstowej tablicy; PDF 2.0 używa skompresowanego strumienia xref.
  4. Trailer — niewielki słownik nazywający punkt wejścia, po którym następuje startxref, przesunięcie i %%EOF.

Zwrot akcji: czytnik nie zaczyna od góry. Zaczyna od dołu, odczytuje startxref, by znaleźć indeks, i używa tego indeksu, by dotrzeć do dowolnego obiektu bezpośrednio. Plik jest zapisywany od początku do końca, ale czytany od końca do początku.

Przejdźmy przez cztery regiony po kolei, z bajtami przed nami.

Nagłówek to jeden wiersz. NextPDF zapisuje %PDF-2.0, a umownie drugi wiersz komentarza z bajtami o wysokim bicie, aby naiwne narzędzia transferu traktowały plik jak binarny, a nie tekstowy. Ten drugi wiersz jest powodem, dla którego PDF otwarty jako zwykły tekst pokazuje niewielki bełkot zaraz po wersji.

Treść to miejsce, gdzie mieszka dokument. Każdy obiekt pośredni to numer, generacja, słowo kluczowe obj, wartość i endobj (Spec: ISO 32000-2, §7.3.10). Wartość ma jeden z kilku kształtów — ale dwa niosą niemal cały ciężar:

  • Słownik, << /Key value … >>, to odwzorowanie nazw na wartości. Drzewo stron, katalog, deskryptory czcionek: same słowniki.
  • Strumień to słownik, po którym następuje stream, blok dowolnych bajtów i endstream. Treść strony, osadzone czcionki i obrazy to strumienie, niemal zawsze skompresowane. (Ich filtry to osobna opowieść, opisana w strumieniach i filtrach.)

Obiekty wskazują na siebie nawzajem przez odwołanie pośrednie2 0 R oznacza „obiekt 2, generacja 0”. To okablowanie, które zamienia płaską listę obiektów w graf.

Sekcja odwołań krzyżowych to część, której większość ludzi nigdy nie wyobraża sobie poprawnie. W formie klasycznej to zwykły tekst: słowo kluczowe xref, następnie podsekcje wierszy o stałej szerokości 20 bajtów (Spec: ISO 32000-2, §7.5.4). Każdy wiersz to dziesięciocyfrowe przesunięcie bajtowe, pięciocyfrowa generacja i pojedyncza flaga — n dla w użyciu, f dla wolnego — dopełniona do dokładnie dwudziestu bajtów, aby czytnik mógł doskoczyć do dowolnego wpisu samą arytmetyką. PDF 2.0 zastępuje to strumieniem odwołań krzyżowych: tym samym indeksem, ale binarnym i skompresowanym wewnątrz obiektu strumieniowego oznaczonego /Type /XRef (Spec: ISO 32000-2, §7.5.8). Mniejszym i zdolnym opisać obiekty upakowane wewnątrz strumieni obiektów.

Trailer to spis treści pliku (Spec: ISO 32000-2, §7.5.5). Nazywa /Root — katalog dokumentu, jedyny obiekt, od którego wszystko inne zwisa — oraz /Size, liczbę obiektów. Potem następuje uścisk dłoni, który umożliwia czytanie od tyłu: startxref, przesunięcie bajtowe w osobnym wierszu i %%EOF. Czytnik doskakuje do końca, odczytuje to przesunięcie, skacze prosto do sekcji odwołań krzyżowych i już działa.

  1. HeaderOne line, %PDF-2.0, naming the version. A binary-marker comment usually follows.
  2. BodyNumbered indirect objects — dictionaries and streams — referenced by N G R.
  3. Cross-reference sectionA text table of 20-byte entries, or a compressed /Type /XRef stream in PDF 2.0.
  4. TrailerNames /Root and /Size, then startxref + offset + %%EOF.
  5. Read orderA reader starts at %%EOF, follows startxref to the index, then reaches each object directly.
Cztery fizyczne regiony PDF w kolejności w pliku oraz ścieżka, którą czytnik faktycznie przez nie przechodzi — zaczynając od trailera i posuwając się do środka przez sekcję odwołań krzyżowych.

Jest piąty region, który zapuszcza długowieczny plik: aktualizacja przyrostowa. Zmiana nie jest zapisywana w miejscu. Zmienione obiekty, świeża sekcja odwołań krzyżowych i nowy trailer są dołączane po pierwszym %%EOF, a ten nowy trailer niesie /Prev — przesunięcie poprzedniej sekcji odwołań krzyżowych (Spec: ISO 32000-2, §7.5.6). Sekcje tworzą łańcuch wstecz; dla dowolnego numeru obiektu wygrywa najnowszy wpis. Ponieważ oryginalne bajty nigdy się nie przesuwają, kryptograficzne sprawdzenie zakresu bajtów, który podpis faktycznie obejmuje, nadal się trzyma po aktualizacji. Późniejsza aktualizacja przyrostowa może nadal zmienić to, co walidator raportuje o dokumencie jako całości — czy zmiany po podpisie są dozwolone i co podpis ma certyfikować — ale nie może zmienić samych podpisanych bajtów. Ta właściwość to cały temat aktualizacji przyrostowych.

Oto cała anatomia w jednym minimalnym pliku. Liczby pod xref to przesunięcia bajtowe i muszą być dokładne — wskaż jeden znak za miejsce, gdzie zaczyna się obiekt, a rygorystyczny czytnik się poddaje.

%PDF-2.0
1 0 obj
<< /Type /Catalog /Pages 2 0 R >>
endobj
2 0 obj
<< /Type /Pages /Kids [3 0 R] /Count 1 >>
endobj
3 0 obj
<< /Type /Page /Parent 2 0 R /MediaBox [0 0 612 792] >>
endobj
xref
0 4
0000000000 65535 f
0000000009 00000 n
0000000058 00000 n
0000000115 00000 n
trailer
<< /Size 4 /Root 1 0 R >>
startxref
186
%%EOF

Czytaj go tak, jak czyta parser. Ostatni wiersz: %%EOF. Nad nim: startxref 186, więc doskocz do bajta 186, gdzie zaczyna się xref. Tablica mówi, że obiekt 1 mieszka pod bajtem 9. /Root 1 0 R trailera wskazuje tam — katalog — a z katalogu przechodzisz przez /Pages do drzewa stron i odnajdujesz pojedynczą stronę. Obiekt 0 jest zawsze początkiem listy wolnych wpisów z generacją 65535, skamieliną z pierwszego projektu formatu, której każdy czytnik nadal oczekuje.

Pułapką jest czytanie PDF jak opowieści — od góry do dołu, po kolei. To nie jest opowieść; to indeks ze wskaźnikiem wstecz. Numery obiektów nie muszą być sekwencyjne w pliku, obiekty mogą występować w dowolnej fizycznej kolejności, a czytnik nigdy nie polega na ich pozycji. Jedyną miarodajną mapą jest sekcja odwołań krzyżowych, a jedyną drogą do odnalezienia tej mapy jest przesunięcie startxref na samym końcu.

Konsekwencja zaskakuje ludzi. PDF z bezbłędną treścią i jedną błędną cyfrą w startxref jest nieczytelny — czytnik nie może znaleźć indeksu. PDF z obiektami w wymieszanej kolejności, ale z poprawną sekcją odwołań krzyżowych, jest zupełnie w porządku. Fizyczna pozycja nie niesie żadnego znaczenia. Zapisana pozycja niesie całe.

Ta strona opisuje strukturę fizyczną, a nie zawartość strony. Jak znaki trafiają na stronę — operatory strumienia zawartości, wyświetlanie tekstu, stan grafiki — to osobny temat. Opisuje też plik dobrze uformowany. Rzeczywiste PDF-y bywają często odrobinę uszkodzone i przeżywają tylko dlatego, że wyrozumiałe przeglądarki odbudowują tablicę odwołań krzyżowych, skanując w poszukiwaniu słów kluczowych obj. To ratowanie to zachowanie przeglądarki, a nie coś, co gwarantuje format.

Reading and repairing arbitrary third-party PDFs — edition availability
EditionAvailability
CoreNextPDF jest modułem zapisującym. Zapisuje każde przesunięcie z bufora wyjściowego w chwili emitowania każdego obiektu, więc pliki, które wytwarza, mają sekcję odwołań krzyżowych zgodną z treścią z konstrukcji.
ProParsowanie, rekonstrukcja czy naprawa uszkodzonej tablicy odwołań krzyżowych w pliku, którego NextPDF nie zapisał, jest poza zakresem we wszystkich edycjach.
EnterpriseDo inspekcji struktury istniejącego pliku użyj dedykowanego parsera lub walidatora; NextPDF gwarantuje poprawność dla tego, co zapisuje, a nie dla tego, co odczytuje.

Dlaczego PDF ma wiersz znacznika binarnego po nagłówku? Niektóre starsze narzędzia transferu kaleczyły plik, który uznały za zwykły tekst. Komentarz o wysokim bicie sprawia, że plik wygląda jednoznacznie binarnie, więc przeżywa podróż bez zmian.

Czy strumień xref to po prostu mniejsza tablica? W większości, z jedną dodatkową mocą. Poza byciem skompresowanym, strumień xref może opisać obiekty przechowywane wewnątrz strumieni obiektów — wpisy, których klasyczna 20-bajtowa tablica tekstowa nie potrafi wyrazić.

Czy mogę mieć w jednym pliku zarówno tablicę, jak i strumień? Pojedyncza rewizja używa jednego albo drugiego. Ale plik hybrydowy może łączyć klasyczną tablicę dla starych czytników ze strumieniem odwołań krzyżowych dla nowych, aby każdy rodzaj czytnika odnalazł indeks, który rozumie.

  • Nagłówek — pierwszy wiersz, %PDF-2.0, nazywający wersję; zwykle po nim następuje komentarz znacznika binarnego.
  • Obiekt pośredni — numerowany obiekt w treści, zapisany jako N G obj … endobj, gdzie N to numer obiektu, a G to generacja.
  • Słownik — odwzorowanie nazw na wartości << /Key value … >>; najbardziej powszechny kształt obiektu.
  • Strumień — słownik plus blok bajtów między stream a endstream, używany do treści, czcionek i obrazów.
  • Tablica odwołań krzyżowych (xref) — indeks od numeru obiektu do przesunięcia bajtowego; klasycznie tekstowa tablica po 20 bajtów na wpis, strumień /Type /XRef w PDF 2.0.
  • Trailer — słownik nazywający /Root i /Size, odnajdywany za pośrednictwem przesunięcia startxref na końcu pliku.
  • Aktualizacja przyrostowa — zmienione obiekty, nowa sekcja odwołań krzyżowych i nowy trailer dołączone po %%EOF, z /Prev łączącym wstecz do poprzedniej sekcji.