Czym naprawdę jest PDF
ISO 32000-2 §7 Evidence: Standard-backed
W skrócie
Dział zatytułowany „W skrócie”PDF nie jest opisem strony, który przypadkiem trafił do pliku. To niewielka grafowa baza danych z dołączoną drukarką. Ta strona opisuje cztery części, z których składa się każdy PDF — nagłówek, treść, tablicę odwołań krzyżowych i trailer — oraz sposób, w jaki NextPDF zapisuje je, aby czytnik mógł znaleźć każdy obiekt bez zgadywania.
Dlaczego to ma znaczenie
Dział zatytułowany „Dlaczego to ma znaczenie”Większość błędów w plikach PDF nie dotyczy renderowania. Dotyczy struktury: przesunięcia bajtowego wskazującego o jeden znak dalej niż obiekt, na który powinno wskazywać, trailera wskazującego niewłaściwy obiekt główny, wpisu w tablicy odwołań krzyżowych niezgodnego z faktycznym położeniem obiektu. Żaden z takich błędów nie zmienia wyglądu strony, dopóki czytnik nie wybierze innej ścieżki przez plik i nie wyjdzie poza jego koniec.
Jeśli traktujesz PDF jak czarną skrzynkę, te awarie wyglądają na przypadkowe. Jeśli znasz model obiektowy, widać dokładnie, czym są: liczbą, która nie pasuje do pozycji. Czytanie formatu pozwala odróżnić „plik PDF jest uszkodzony” od „przesunięcie obiektu 14 jest nieaktualne, ponieważ moduł zapisujący zmierzył je przed sfinalizowaniem długości strumienia.”
Wersja skrócona
Dział zatytułowany „Wersja skrócona”PDF składa się z czterech części, w kolejności występowania w pliku:
- Nagłówek — jeden wiersz określający wersję (
%PDF-2.0). - Treść — sekwencja numerowanych obiektów pośrednich: słowników, strumieni, tablic, liczb, ciągów znaków, nazw.
- Tablica odwołań krzyżowych (lub, w PDF 2.0, strumień odwołań krzyżowych) — odwzorowanie numeru obiektu na przesunięcie bajtowe, tak aby każdy obiekt można było osiągnąć bez skanowania pliku.
- Trailer — niewielki słownik wskazujący obiekt główny dokumentu i miejsce, w którym zaczyna się sekcja odwołań krzyżowych.
Czytnik nie czyta pliku PDF sekwencyjnie od początku do końca. Najpierw odczytuje ostatni wiersz, znajduje startxref, przeskakuje do sekcji odwołań krzyżowych i używa jej jako indeksu do treści. Format zaprojektowano do czytania od tyłu. Już sam ten fakt wyjaśnia większość założeń projektowych.
Jak podchodzi do tego NextPDF
Dział zatytułowany „Jak podchodzi do tego NextPDF”NextPDF buduje plik PDF zgodnie z tym, jak format jest czytany: najpierw obiekt, potem odnotowane przesunięcie, na końcu zapisana tablica.
Za numer każdego obiektu pośredniego odpowiada pojedynczy rejestr (src/Core/ObjectRegistry.php). Rejestr przydziela kolejne numery metodą allocate(), a po zapisaniu bajtów obiektu do bufora wyjściowego zapisuje przesunięcie bajtowe metodą register(). Przesunięcia nigdy nie są odgadywane z wyprzedzeniem. Są odczytywane z BinaryBuffer::getOffset() w chwili emitowania nagłówka obiektu. Dlatego wpis odwołania krzyżowego w NextPDF nie może rozminąć się z obiektem, który opisuje: przesunięcie jest faktyczną pozycją bufora.
Gdy treść jest kompletna, właściwa dla danej wersji strategia serializacji (src/Writer/PdfSerializationStrategy.php) zapisuje sekcję odwołań krzyżowych oraz trailer:
Pdf20StreamStrategyemituje skompresowany strumień odwołań krzyżowych (/Type /XRef) — domyślny mechanizm w PDF 2.0.Pdf17TableStrategyorazPdf14TableStrategyemitują tradycyjną 20-bajtową tablicę odwołań krzyżowych oraz odrębny słownik trailera — wymagane w profilach PDF/A, które narzucają starszą strukturę pliku.
Strategię wybiera profil wyjściowy; nie jest ona wnioskowana. Niezależnie od tego, którą wybrano, końcowe bajty mają ten sam układ: sekcja odwołań krzyżowych, następnie startxref, następnie przesunięcie bajtowe, a potem %%EOF. To właśnie tę końcówkę czytnik znajduje jako pierwszą.
- Step 1 of 4: ISO 32000-2 §7.5.5 — %%EOF and startxref at the file end
- Step 2 of 4: ISO 32000-2 §7.5.4 / §7.5.8 — the cross-reference section maps object number to offset
- Step 3 of 4: ISO 32000-2 §7.5.5 — the trailer names /Root, the document catalog
- Step 4 of 4: ISO 32000-2 §7.3.10 — each indirect object is reached at its recorded offset
Co mówią dowody
Dział zatytułowany „Co mówią dowody”Czteroczęściowa struktura nie jest konwencją NextPDF; wynika z klauzuli dotyczącej struktury pliku w Spec: ISO 32000-2, §7.5 ISO 32000-2 §7.5 . Norma definiuje PDF jako nagłówek, treść złożoną z obiektów, tablicę odwołań krzyżowych i trailer oraz stwierdza, że czytnik powinien przetwarzać go od końca pliku. Ostatni wiersz to %%EOF, a dwa poprzedzające go wiersze zawierają słowo kluczowe startxref oraz przesunięcie bajtowe do sekcji odwołań krzyżowych.
Każdy obiekt pośredni jest zdefiniowany przez numer obiektu i numer generacji, oddzielone białym znakiem, po których następuje wartość obiektu ujęta między słowami kluczowymi obj i endobj. Połączenie numeru obiektu i numeru generacji jednoznacznie identyfikuje obiekt; odwołanie pośrednie do niego zapisuje się jako numer obiektu, numer generacji oraz słowo kluczowe R. Komponent ObjectRegistry w NextPDF odzwierciedla to dokładnie: kolejny numer, generację 0 dla nowo zapisywanych obiektów i zapisane przesunięcie.
Od PDF 1.5 obiekty mogą również znajdować się wewnątrz strumienia obiektów, gdzie są przechowywane bez słów kluczowych obj/endobj i muszą mieć generację zero. Strumień odwołań krzyżowych (/Type /XRef,
Spec: ISO 32000-2, §7.5.8 ISO 32000-2 §7.5.8 ) jest mechanizmem PDF 2.0
indeksującym zarówno zwykłe obiekty, jak i te skompresowane.
Komponent CrossReferenceStream w NextPDF tworzy go z tablicą szerokości pól /W oraz
kompresją FlateDecode.
Praktyczny przykład
Dział zatytułowany „Praktyczny przykład”Tak wygląda treść minimalnego pliku PDF i jego trailer. Liczby w sekcji odwołań krzyżowych to przesunięcia bajtowe. Muszą być poprawne co do bajtu, dlatego NextPDF odczytuje je z bufora, zamiast je obliczać.
%PDF-2.01 0 obj<< /Type /Catalog /Pages 2 0 R >>endobj2 0 obj<< /Type /Pages /Kids [3 0 R] /Count 1 >>endobj3 0 obj<< /Type /Page /Parent 2 0 R /MediaBox [0 0 612 792] >>endobjxref0 40000000000 65535 f0000000009 00000 n0000000058 00000 n0000000122 00000 ntrailer<< /Size 4 /Root 1 0 R >>startxref196%%EOFCzytnik zaczyna od dołu: %%EOF, następnie startxref 196, potem przechodzi do bajtu 196, gdzie zaczyna się xref, odczytuje, że obiekt 1 znajduje się w bajcie 9, podąża za /Root 1 0 R do katalogu i stamtąd przechodzi przez drzewo stron. Obiekt 0 jest zawsze początkiem listy wolnych wpisów z generacją 65535 — to osobliwość odziedziczona po najwcześniejszym projekcie formatu, wiernie odtwarzana, ponieważ czytniki tego oczekują.
Częste nieporozumienie
Dział zatytułowany „Częste nieporozumienie”Pułapką jest przekonanie, że PDF czyta się od góry do dołu jak kod źródłowy. Tak nie jest. W treści obiekty mogą występować w dowolnej kolejności. Numery obiektów nie muszą pojawiać się w pliku po kolei, a czytnik nigdy nie zakłada, że tak jest. Jedynym miarodajnym indeksem jest sekcja odwołań krzyżowych, a jedyną drogą do jej odnalezienia jest trailer na końcu. PDF z całkowicie poprawną treścią i jedną błędną liczbą w startxref jest nieczytelny. PDF z obiektami zapisanymi w wymieszanej kolejności, ale z poprawną tablicą odwołań krzyżowych, jest poprawny. Fizyczna kolejność nie ma znaczenia; liczy się zapisana pozycja.
Ograniczenia i granice
Dział zatytułowany „Ograniczenia i granice”Ta strona opisuje strukturę pliku, a nie zawartość strony. Sposób, w jaki znaki trafiają na stronę — strumienie zawartości, operatory graficzne, wyświetlanie tekstu — to odrębny temat. Nie obejmuje też tego, co dzieje się, gdy plik zostaje zmieniony po zapisaniu. To domena aktualizacji przyrostowych, w których moduł zapisujący dołącza drugą sekcję odwołań krzyżowych, a trailer łączy się wstecz w łańcuch.
NextPDF jest modułem zapisującym. Opisane tutaj zachowanie dotyczy sposobu, w jaki serializuje dokument, który sam zbudował. Nie jest uniwersalnym parserem ani narzędziem do naprawy plików PDF. Nie obiecuje odczytu, rekonstrukcji ani ratowania dowolnego pliku z zewnętrznego źródła, który ma uszkodzoną tablicę odwołań krzyżowych. Gwarancja jest wąska i celowa. Pliki, które NextPDF zapisuje, mają zgodne przesunięcia, ponieważ zostały zmierzone, a nie przewidziane.
Mini-FAQ
Dział zatytułowany „Mini-FAQ”Po co numery generacji, skoro nowe pliki zawsze używają 0? Numery generacji istnieją po to, by ponownie wykorzystywać obiekty w kolejnych aktualizacjach. W świeżo zapisanym pliku każdy obiekt ma generację 0. Niezerowe generacje pojawiają się tylko wtedy, gdy plik został zaktualizowany przyrostowo, a numer obiektu został ponownie wykorzystany.
Czy dwa obiekty mogą mieć ten sam numer? W pojedynczej sekcji odwołań krzyżowych nie. W toku aktualizacji przyrostowych plik może fizycznie zawierać kilka kopii tego samego numeru obiektu. Obowiązuje najnowszy wpis odwołania krzyżowego. To temat następnej strony.
Czy kolejność obiektów w pliku ma znaczenie dla danych wyjściowych? Nie. NextPDF zapisuje obiekty w deterministycznej kolejności, aby kompilacje były powtarzalne, ale czytnik rozwiązuje wszystko za pośrednictwem sekcji odwołań krzyżowych, więc kolejność fizyczna nie ma znaczenia semantycznego.
Powiązana dokumentacja
Dział zatytułowany „Powiązana dokumentacja”- Aktualizacje przyrostowe i dlaczego mają znaczenie — co się dzieje, gdy zapisany PDF zostaje zmieniony: dołączone sekcje i trailer połączony w łańcuch.
- Strumienie i filtry — jak strumienie zawartości są kompresowane i kodowane.
- PDF 2.0: co się zmieniło — czym różni się struktura pliku między 1.7 a bazą 2.0 docelową dla NextPDF.
Słowniczek
Dział zatytułowany „Słowniczek”- Obiekt pośredni — numerowany obiekt w treści, zapisany jako
N G obj … endobj, gdzieNto numer obiektu, aGto numer generacji. - Odwołanie pośrednie — wskaźnik do obiektu pośredniego, zapisywany jako
N G R. - Tablica odwołań krzyżowych (xref) — indeks odwzorowujący numer obiektu na przesunięcie bajtowe. W PDF 2.0 jest to zwykle strumień odwołań krzyżowych (
/Type /XRef) zamiast klasycznej tekstowej tablicy z wpisami po 20 bajtów. - Trailer — słownik na końcu sekcji odwołań krzyżowych, który wskazuje
/Root(katalog dokumentu) oraz/Sizei jest odnajdywany za pośrednictwem przesunięciastartxref. - Strumień obiektów — obiekt strumieniowy, który sam zawiera inne obiekty pośrednie (skompresowane razem); jego elementy nie mają
obj/endobji mają generację zero. - Katalog dokumentu — obiekt wskazywany przez
/Root; punkt wejścia do drzewa stron i reszty dokumentu.