Was ein PDF wirklich ist
ISO 32000-2 §7 Evidence: Standard-backed
Auf einen Blick
Abschnitt betitelt „Auf einen Blick“Ein PDF ist keine Seitenbeschreibung, die zufällig in einer Datei liegt. Es ist eine kleine Graphdatenbank mit angeschlossenem Drucker. Diese Seite beschreibt die vier Teile, aus denen jedes PDF besteht – Header, Rumpf, Querverweistabelle, Trailer – und wie NextPDF sie schreibt, damit ein Reader jedes Objekt findet, ohne raten zu müssen.
Warum das wichtig ist
Abschnitt betitelt „Warum das wichtig ist“Die meisten PDF-Fehler sind keine Rendering-Fehler. Es sind Strukturfehler: ein Byte-Offset, der ein Zeichen hinter das Objekt zeigt, auf das er zeigen sollte, ein Trailer, der die falsche Wurzel benennt, ein Querverweiseintrag, der nicht mit der tatsächlichen Position des Objekts übereinstimmt. Keiner dieser Fehler verändert das Aussehen einer Seite, bis ein Reader einen anderen Weg durch die Datei nimmt und an deren Ende ins Leere läuft.
Wenn Sie ein PDF als undurchsichtig behandeln, wirken diese Fehler zufällig. Wenn Sie das Objektmodell kennen, sehen Sie sie genau als das, was sie sind: eine Zahl, die nicht zu einer Position passt. Das Format lesen zu können, ist der Unterschied zwischen „das PDF ist beschädigt“ und „der Offset von Objekt 14 ist veraltet, weil der Writer ihn gemessen hat, bevor die Stream-Länge feststand“.
Die Kurzfassung
Abschnitt betitelt „Die Kurzfassung“Ein PDF hat vier Teile, in Dateireihenfolge:
- Einen Header – eine Zeile, die die Version benennt (
%PDF-2.0). - Einen Rumpf – eine Folge nummerierter indirekter Objekte: Dictionaries, Streams, Arrays, Zahlen, Strings, Namen.
- Eine Querverweistabelle (oder, in PDF 2.0, einen Querverweis-Stream) – eine Zuordnung von Objektnummer zu Byte-Offset, damit jedes Objekt erreichbar ist, ohne die Datei zu durchsuchen.
- Einen Trailer – ein kleines Dictionary, das das Wurzelobjekt des Dokuments benennt und auf den Beginn des Querverweisabschnitts zeigt.
Ein Reader liest ein PDF nicht von vorne nach hinten. Er liest zuerst die letzte Zeile, findet startxref, springt zum Querverweisabschnitt und verwendet ihn als Index für den Rumpf. Das Format ist darauf ausgelegt, rückwärts gelesen zu werden. Diese eine Tatsache erklärt den größten Teil seines Designs.
Wie NextPDF es angeht
Abschnitt betitelt „Wie NextPDF es angeht“NextPDF baut ein PDF so auf, wie das Format gelesen wird: Es schreibt zuerst das Objekt, zeichnet danach den Offset auf und schreibt die Tabelle zuletzt.
Jedes indirekte Objekt erhält von einer einzigen Registry eine Nummer (src/Core/ObjectRegistry.php). Die Registry vergibt fortlaufende Nummern über allocate() und zeichnet, nachdem die Bytes eines Objekts in den Ausgabepuffer geschrieben wurden, den Byte-Offset über register() auf. Offsets werden niemals im Voraus geraten. Sie werden in dem Moment, in dem der Objekt-Header ausgegeben wird, aus BinaryBuffer::getOffset() abgelesen. Deshalb kann ein NextPDF-Querverweiseintrag nicht von dem Objekt abweichen, das er beschreibt: Der Offset ist genau das, was die Position des Puffers tatsächlich war.
Wenn der Rumpf vollständig ist, schreibt eine versionsspezifische Serialisierungsstrategie (src/Writer/PdfSerializationStrategy.php) den Querverweisabschnitt und den Trailer:
Pdf20StreamStrategygibt einen komprimierten Querverweis-Stream aus (/Type /XRef) – der PDF-2.0-Standard.Pdf17TableStrategyundPdf14TableStrategygeben eine herkömmliche 20-Byte-Querverweis-tabelle sowie ein separates Trailer-Dictionary aus – erforderlich für die PDF/A-Profile, die eine ältere Dateistruktur vorschreiben.
Die Strategie wird vom Ausgabeprofil bestimmt, nicht abgeleitet. Unabhängig davon, welche Strategie verwendet wird, haben die letzten Bytes dieselbe Form: der Querverweisabschnitt, dann startxref, dann der Byte-Offset, dann %%EOF. Dieser Abschluss ist das, was ein Reader zuerst findet.
- 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
Was die Belege sagen
Abschnitt betitelt „Was die Belege sagen“Die vierteilige Struktur ist keine NextPDF-Konvention; sie ist die Dateistrukturklausel von Spec: ISO 32000-2, §7.5 ISO 32000-2 §7.5 . Der Standard definiert ein PDF als einen Header, einen Rumpf aus Objekten, eine Querverweistabelle und einen Trailer und legt fest, dass ein Reader vom Ende der Datei aus parsen sollte. Die letzte Zeile ist %%EOF, und die beiden Zeilen davor sind das Schlüsselwort startxref und der Byte-Offset zum Querverweisabschnitt.
Ein indirektes Objekt wird als Objektnummer und Generationsnummer definiert, durch Leerraum getrennt, gefolgt vom Wert des Objekts und eingeschlossen zwischen den Schlüsselwörtern obj und endobj. Die Kombination aus Objektnummer und Generationsnummer identifiziert das Objekt eindeutig; eine indirekte Referenz darauf wird als Objektnummer, Generationsnummer und das Schlüsselwort R geschrieben. Die ObjectRegistry von NextPDF bildet dies exakt nach: eine fortlaufende Nummer, Generation 0 für neu geschriebene Objekte und einen aufgezeichneten Offset.
Ab PDF 1.5 dürfen Objekte auch innerhalb eines Objekt-Streams liegen, wo sie ohne die Schlüsselwörter obj/endobj gespeichert werden und Generation null haben müssen. Der Querverweis-Stream (/Type /XRef,
Spec: ISO 32000-2, §7.5.8 ISO 32000-2 §7.5.8 ) ist der PDF-2.0-
Mechanismus, der sowohl gewöhnliche Objekte als auch diese komprimierten Objekte indiziert.
Der CrossReferenceStream von NextPDF baut ihn mit einem /W-Feldbreiten-Array und
FlateDecode-Komprimierung auf.
Praktisches Beispiel
Abschnitt betitelt „Praktisches Beispiel“So ist ein minimaler PDF-Rumpf samt Trailer aufgebaut. Die Zahlen im Querverweisabschnitt sind Byte-Offsets. Sie müssen exakt stimmen, weshalb NextPDF sie aus dem Puffer aufzeichnet, statt sie zu berechnen.
%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%%EOFEin Reader arbeitet diese Datei von unten her auf: %%EOF, dann startxref 196, dann springt er zu Byte 196, wo xref beginnt, liest, dass Objekt 1 bei Byte 9 liegt, folgt /Root 1 0 R zum Katalog und durchläuft von dort den Seitenbaum. Objekt 0 ist immer der Kopf der Freiliste mit Generation 65535 – eine Eigenart, die aus dem frühen Design des Formats übernommen und getreu reproduziert wird, weil Reader sie erwarten.
Verbreitetes Missverständnis
Abschnitt betitelt „Verbreitetes Missverständnis“Der Irrtum besteht darin zu glauben, ein PDF werde wie Quellcode von oben nach unten gelesen. Das ist nicht der Fall. Der Rumpf kann in beliebiger Objektreihenfolge vorliegen. Objektnummern müssen in der Datei nicht fortlaufend sein, und ein Reader verlässt sich nie darauf, dass sie es sind. Der einzige maßgebliche Index ist der Querverweisabschnitt, und der einzige Weg, ihn zu finden, ist der Trailer am Ende. Ein PDF mit einem vollständig gültigen Rumpf und einer einzigen falschen Zahl in startxref ist unlesbar. Ein PDF mit Objekten, die in einer ungeordneten Reihenfolge geschrieben sind, aber mit einer korrekten Querverweistabelle, ist in Ordnung. Die physische Position allein ist bedeutungslos; die aufgezeichnete Position ist entscheidend.
Grenzen und Abgrenzungen
Abschnitt betitelt „Grenzen und Abgrenzungen“Diese Seite beschreibt die Dateistruktur, nicht den Seiteninhalt. Wie Zeichen und Grafiken auf eine Seite gelangen – Content-Streams, Grafikoperatoren, Textausgabe – ist ein eigenes Thema. Sie behandelt auch nicht, was geschieht, wenn eine Datei nach dem Schreiben geändert wird. Das ist Aufgabe inkrementeller Aktualisierungen, bei denen ein zweiter Querverweisabschnitt angehängt wird und der Trailer rückwärts verkettet.
NextPDF ist ein Writer. Das hier beschriebene Verhalten zeigt, wie es ein selbst erstelltes Dokument serialisiert. Es ist kein universeller PDF-Parser und kein Reparaturwerkzeug. Es verspricht nicht, eine beliebige Drittanbieterdatei mit beschädigter Querverweistabelle zu lesen, zu rekonstruieren oder zu retten. Die Garantie ist eng gefasst und bewusst gewählt. Die Dateien, die NextPDF schreibt, haben korrekte Offsets, weil sie gemessen und nicht vorhergesagt werden.
Mini-FAQ
Abschnitt betitelt „Mini-FAQ“Warum Generationsnummern, wenn neue Dateien immer 0 verwenden? Generationsnummern existieren für die Wiederverwendung von Objekten über Aktualisierungen hinweg. In einer frisch geschriebenen Datei liegt jedes Objekt auf Generation 0. Generationen ungleich null treten nur auf, wenn eine Datei inkrementell aktualisiert und eine Objektnummer wiederverwendet wurde.
Können zwei Objekte dieselbe Nummer haben? In einem einzelnen Querverweisabschnitt: nein. Über inkrementelle Aktualisierungen hinweg kann eine Datei physisch mehrere Kopien derselben Objektnummer enthalten. Der jüngste Querverweiseintrag gewinnt. Das ist Thema der nächsten Seite.
Spielt die Objektreihenfolge in der Datei für die Ausgabe eine Rolle? Nein. NextPDF schreibt Objekte in einer deterministischen Reihenfolge für reproduzierbare Builds, aber ein Reader löst alles über den Querverweisabschnitt auf, sodass die physische Reihenfolge semantisch nicht bedeutsam ist.
Verwandte Dokumente
Abschnitt betitelt „Verwandte Dokumente“- Inkrementelle Aktualisierungen und warum sie wichtig sind – was geschieht, wenn ein geschriebenes PDF geändert wird: angehängte Abschnitte und ein verketteter Trailer.
- Streams und Filter – wie Stream-Objekte im Rumpf komprimiert und kodiert werden.
- PDF 2.0: Was sich geändert hat – wie sich die Dateistruktur zwischen 1.7 und der 2.0-Baseline unterscheidet, auf die NextPDF abzielt.
Glossar
Abschnitt betitelt „Glossar“- Indirektes Objekt – ein nummeriertes Objekt im Rumpf, geschrieben als
N G obj … endobj, wobeiNdie Objektnummer undGdie Generationsnummer ist. - Indirekte Referenz – ein Zeiger auf ein indirektes Objekt, geschrieben als
N G R. - Querverweistabelle (xref) – der Index von Objektnummer zu Byte-Offset. In PDF 2.0 ist dies üblicherweise ein Querverweis-Stream (
/Type /XRef) anstelle der klassischen Texttabelle mit 20 Byte pro Eintrag. - Trailer – das Dictionary am Ende eines Querverweisabschnitts, das
/Root(den Dokumentkatalog) und/Sizebenennt und über denstartxref-Offset gefunden wird. - Objekt-Stream – ein Stream-Objekt, das selbst andere indirekte Objekte enthält (gemeinsam komprimiert); seine Mitglieder haben kein
obj/endobjund Generation null. - Dokumentkatalog – das von
/Rootbenannte Objekt; der Einstiegspunkt in den Seitenbaum und alle weiteren Dokumentbestandteile.