🔁Updates, kompakte Streams und schnelles Laden
Drei Erweiterungen der Dateistruktur: Änderungen anhängen statt überschreiben, Objekte und xref-Tabelle komprimieren (PDF 1.5) und die Datei so anordnen, dass Seite 1 erscheint, bevor der Download fertig ist.
➕Inkrementelles Update
4 0 obj << /Length 40 >> stream BT /F1 24 Tf 72 72 Td (Hallo Welt) Tj ET endstream endobj 6 0 obj << /Title (Hallo Welt) /Producer (VisualPdf) >> endobj xref 4 1 0000000690 00000 n 6 1 0000000780 00000 n trailer << /Size 7 /Root 1 0 R /Info 6 0 R /Prev 475 >> startxref 843 %%EOF
Dieselbe Objektnummer 4 wird ans Ende angehängt, mit neuem Inhalt „Hallo Welt“. Das alte Objekt 4 bleibt als Bytes stehen, wird aber nicht mehr benutzt.
Auch der Titel ändert sich – wieder als neue Version am Dateiende.
Enthält nur die geänderten bzw. neuen Objekte. Für alle anderen gilt weiterhin die ältere Tabelle.
/Prev 475 verweist auf die vorherige xref-Tabelle. Ein Leser folgt dieser Kette rückwärts; der jüngste Eintrag gewinnt.
Zeigt jetzt auf die neue Tabelle bei Byte 843. Die ursprünglichen Bytes davor bleiben unverändert – deshalb überleben digitale Signaturen ein Update.
startxref 843
└─► xref (neu): Objekte 4, 6
└─ /Prev 475 ─► xref (alt): Objekte 0–6
Objekt 4 → neue Version (Byte 690)
Objekt 3 → alte Tabelle (Byte 121)🔬 Im Byte-Explorer ansehen (Tab „Nach inkrementellem Update“)qpdf in.pdf out.pdf) entfernt ihn.🗜️Object Streams (PDF 1.5)
<< /Filter /FlateDecode /First 26 /Length 215 /N 5 /Type /ObjStm >> stream 2 0 3 34 4 78 5 191 6 246 << /Pages 3 0 R /Type /Catalog >> << /Count 1 /Kids [ 4 0 R ] /Type /Pages >> << /Contents 7 0 R /MediaBox [ 0 0 300 144 ] /Parent 3 0 R /Resources << /Font << /F1 5 0 R >> >> /Type /Page >> << /BaseFont /Helvetica /Subtype /Type1 /Type /Font >> << /Producer (VisualPdf) /Title (Hello World) >> endstream
Der Kopf 2 0 3 34 4 78 5 191 6 246 besteht aus /N 5 Paaren „Objektnummer, Offset“. Offsets zählen ab /First 26:
| Objekt | Offset | Position | Inhalt |
|---|---|---|---|
| 2 | 0 | 26 | << /Pages 3 0 R /Type /Catalog >> |
| 3 | 34 | 60 | << /Count 1 /Kids [ 4 0 R ] /Type /Pages >> |
| 4 | 78 | 104 | << /Contents 7 0 R /MediaBox [ 0 0 300 144 ] /Pa… |
| 5 | 191 | 217 | << /BaseFont /Helvetica /Subtype /Type1 /Type /F… |
| 6 | 246 | 272 | << /Producer (VisualPdf) /Title (Hello World) >> |
Streams dürfen nicht in Object Streams liegen – der Content-Stream bleibt ein eigenes Objekt. Auch die Generationsnummer ist dort immer 0.
📇Cross-Reference-Streams (PDF 1.5)
<< /DecodeParms << /Columns 4 /Predictor 12 >> /Filter /FlateDecode /ID [ <53b8…> <53b8…> ] /Info 6 0 R /Length 32 /Root 2 0 R /Size 9 /Type /XRef /W [ 1 2 1 ] >>
| Obj. | gespeichert (Filter + 4 Bytes) | dekodiert | Typ | Feld 2 | Feld 3 |
|---|---|---|---|---|---|
| 0 | 02 00 00 00 00 | 00 00 00 00 | 0 | 0 | 0 |
| 1 | 02 01 00 0F 00 | 01 00 0F 00 | 1 | 15 | 0 |
| 2 | 02 01 00 F2 00 | 02 00 01 00 | 2 | 1 | 0 |
| 3 | 02 00 00 00 01 | 02 00 01 01 | 2 | 1 | 1 |
| 4 | 02 00 00 00 01 | 02 00 01 02 | 2 | 1 | 2 |
| 5 | 02 00 00 00 01 | 02 00 01 03 | 2 | 1 | 3 |
| 6 | 02 00 00 00 01 | 02 00 01 04 | 2 | 1 | 4 |
| 7 | 02 FF 01 49 FC | 01 01 4A 00 | 1 | 330 | 0 |
| 8 | 02 00 00 75 00 | 01 01 BF 00 | 1 | 447 | 0 |
Filterbyte 02 = PNG-Filter „Up“: jedes Byte wird zum Byte darüber addiert (modulo 256).
2/0: compressed; stream = 1, index = 0
Diese Zeile ist Byte für Byte identisch mit der Ausgabe von qpdf --show-xref.
/W [1 2 1]: 1 Byte Typ, 2 Bytes Feld 2 (Offset bzw. Nummer des Object Streams), 1 Byte Feld 3 (Generation bzw. Index im Object Stream), Big Endian. Der Trailer steht jetzt im Stream-Dictionary (/Root, /Size, /Info), das Schlüsselwort trailer entfällt.
⚡Linearisierung („Fast Web View“)
Byte Inhalt von lin.pdf (qpdf --linearize hello.pdf lin.pdf)
0 %PDF-1.7 + Binärkommentar
15 3 0 obj << /Linearized 1 /L 1305 /H [561 120] /O 6 /E 996 /N 1 /T 1127 >>
xref 3 6 … trailer << … /Prev 1119 >> ◄ xref der ersten Seite
512 4 0 obj Catalog
561 5 0 obj Hint-Stream (FlateDecode) ◄ /H
681 6 0 obj Page (erste Seite) ◄ /O
809 7 0 obj Content-Stream
926 8 0 obj Font Helvetica
───── /E 996 ── bis hier: Seite 1 komplett ────────────────────
996 1 0 obj Pages
1055 2 0 obj Info
1119 xref 0 3 … trailer ◄ Haupt-xref (/T 1127)
1305 Dateiende ◄ /L| Eintrag | Bedeutung |
|---|---|
| /Linearized 1 | Version der Linearisierung |
| /L 1305 | Dateilänge in Bytes – stimmt sie nicht, wurde die Datei nachträglich (inkrementell) geändert und gilt nicht mehr als linearisiert |
| /H [561 120] | Offset und Länge des primären Hint-Streams (Objekt 5) |
| /O 6 | Objektnummer des Page-Objekts der ersten Seite |
| /E 996 | Offset des Endes der ersten Seite – bis hierher reicht alles, was zum Anzeigen von Seite 1 nötig ist |
| /N 1 | Anzahl der Seiten |
| /T 1127 | Offset des Leerzeichens vor dem ersten Eintrag der Haupt-xref-Tabelle |
Prüfen: qpdf --check-linearization lin.pdf → „no linearization errors“; pdfinfo lin.pdf meldet Optimized: yes. Das Linearisierungs-Dictionary muss vollständig in den ersten 1024 Bytes stehen.