Coding · Content Stream

Text- und Grafik-Operatoren im PDF-Content-Stream

Jede Linie, jedes Wort und jedes Bild auf einer PDF-Seite wird durch eine kleine, klar definierte Operator-Sprache erzeugt. Dieser Artikel geht die zentralen Befehle durch – die Text-Operatoren, die Grafik-Operatoren und die Transformationen, die alles in Position bringen – mit Schaubild zur Transformationsmatrix und konkreten Code-Beispielen.

  • 13 Minuten Lesezeit
  • Stand: Mai 2026

Was ein Content Stream ist

Ein Content Stream ist ein Bytefolge im Body einer PDF-Datei, die beschreibt, was auf einer Seite zu sehen ist. Eine Seite ( /Page ) verweist über /Contents auf einen oder mehrere solcher Streams. Der Inhalt eines Content Streams besteht aus einer Folge sehr kurzer Operatoren – einer kleinen, klar spezifizierten Befehlssprache, die im wesentlichen seit den frühen Tagen von PDF stabil ist und in ISO 32000-2 Abschnitt 7.8 definiert wird.

Wer einen Content Stream das erste Mal sieht, erkennt vier Eigenschaften sehr schnell: Befehle sind kurz (oft nur ein oder zwei Buchstaben), sie stehen nach ihren Argumenten (Postfix-Notation), jeder Befehl steht auf einer eigenen Zeile , und Streams sind in der Datei in der Regel komprimiert ( FlateDecode ) – sichtbar werden sie erst nach Dekompression, z. B. mit qpdf --qdf .

Operator-Syntax: Postfix in Kleinbuchstaben

Die Operator-Sprache erinnert an PostScript, ist aber deutlich kleiner und deklarativ: Es gibt keine Schleifen, keine if-Konstrukte, keine Variablen. Alles ist eine Folge von Befehlen , jeder Befehl konsumiert Argumente, die unmittelbar davor stehen.

Postfix: Argumente vor Operator
% Rechteck zeichnen: x y breite höhe re 100 200 300 50 re % gefüllt darstellen f

Eine Konvention der Spezifikation: Operatoren sind durchgängig kleingeschrieben , wenn sie auf Strokes(Konturen) wirken, und großgeschrieben für die Fill-Variante. s schließt einen Pfad und zeichnet ihn nach; S zeichnet nur die Kontur ohne Schließen; f füllt einen Pfad; B macht beides. Diese kleine Konvention vereinfacht das Lesen.

Der Graphics-State und seine Stack-Operatoren (q/Q)

Operatoren wirken nicht direkt auf die Seite, sondern auf einen Graphics-State: einen Zustand mit Eigenschaften wie aktuelle Schrift, Strichbreite, Farbe, Text-Matrix und Transformationsmatrix. Die Spec spricht von CTM – der Current Transformation Matrix.

Zwei Operatoren machen den Graphics-State stack-fähig:

  • q (für „save" – kommt vom englischen quit-restoring , ist aber faktisch eine Push-Operation): sichert den aktuellen Graphics-State auf einem internen Stack.
  • Q (für „restore"): stellt den zuletzt gesicherten Zustand wieder her und entfernt ihn vom Stack.

Das Muster ist immer dasselbe: lokale Änderungen zwischen q und Q einklammern, damit sie nichts Nachfolgendes beeinflussen.

q/Q als Schutz-Klammer
q                           % State sichern   1 0 0 1 100 200 cm         % Verschiebung   0.5 0.0 0.0 RG             % Strichfarbe Karmin   0 0 50 50 re S             % Rechteck mit Kontur Q                           % State zurücksetzen

Text-Operatoren: BT, Tf, Td, Tj, Tm

Text wird auf einer PDF-Seite ausschließlich innerhalb von Text-Objekten gezeichnet, eingeleitet durch BT ( Begin Text ) und beendet durch ET ( End Text ). Dazwischen gilt eine zusätzliche Zustands-Schicht: die Text-Matrix und der Text-State.

Minimal-Text-Block
BT   /F1 12 Tf              % Schrift F1, 12 pt   72 720 Td              % Textposition (72, 720)   (Hallo Welt) Tj        % Zeichenkette zeichnen ET

Die wichtigsten Text-Operatoren im Überblick

Kern-Operatoren für Text
Operator Argumente Wirkung
BT · ET Beginn / Ende eines Text-Objekts. Außerhalb dieser Klammer sind Text-Operatoren ungültig.
Tf name size Schrift wählen (über Resource Name /F1 u. a.) und Größe in Punkt setzen.
Td tx ty Textposition relativ zur Zeilenstart-Position verschieben.
Tm a b c d e f Text-Matrix setzen – legt Drehung, Scherung, Skalierung und Position des Texts fest.
Tj (string) Zeichenkette zeichnen.
TJ [array] Array aus Strings und Zahlen; Zahlen erzeugen Kerning-Verschiebungen.
T* In die nächste Zeile springen (auf Basis des Leading-Werts).

Grafik-Operatoren: m, l, c, re, S, f

Linien, Kurven und Flächen werden über Pfad-Konstruktion und anschließendes Painten erzeugt. Pfad-Operatoren bauen einen Pfad auf; Paint-Operatoren realisieren ihn als sichtbares Stroke, Fill oder beides.

Kern-Operatoren für Grafik
Operator Argumente Wirkung
m x y Pfad-Startpunkt setzen ( moveto ).
l x y Linie vom aktuellen Punkt zu (x, y) ziehen ( lineto ).
c x1 y1 x2 y2 x3 y3 Kubische Bézier-Kurve mit zwei Kontrollpunkten zu (x3, y3).
re x y w h Rechteck als geschlossenen Sub-Pfad anlegen (linke untere Ecke + Größe).
h Aktuellen Sub-Pfad schließen.
S Pfad als Kontur zeichnen (Stroke).
f Pfad füllen (Fill).
B Pfad füllen und Kontur zeichnen.
n Pfad ohne Zeichnen abschließen (z. B. für Clipping).
Dreieck mit Kontur
q   100 100 m              % Startpunkt   200 100 l              % Linie nach rechts   150 180 l              % Linie nach oben Mitte    h                      % Pfad schließen    S                      % Kontur zeichnen Q

Transformationen und die Matrix cm

Die wichtigste Brücke zwischen Operatoren und Seiten-Geometrie ist die Current Transformation Matrix, kurz CTM. Sie legt fest, wie Koordinaten der nachfolgenden Operatoren in den Seitenraum übersetzt werden. Der Operator cm erweitert die CTM um eine neue Transformation ( concatenate matrix ).

In PDF wird jede Transformation als 2D-Affintransformation in einer 3×3-Matrix dargestellt – die letzte Spalte ist immer fixiert. Sechs Zahlen a b c d e f reichen, um sie vollständig zu beschreiben:

Transformationsmatrix und Rotations-Beispiel Links die allgemeine 3 mal 3 Affintransformations-Matrix mit den Einträgen a, b, c, d, e, f und der fixen letzten Spalte 0, 0, 1. In der Mitte die für eine Rotation um 30 Grad eingesetzten Werte: a gleich Kosinus 30 Grad gleich 0,866, b gleich Sinus 30 Grad gleich 0,5, c gleich minus Sinus 30 Grad gleich minus 0,5, d gleich Kosinus 30 Grad gleich 0,866. Translation e und f stehen auf 0. Rechts das optische Ergebnis: ein zunächst aufrecht stehendes Rechteck mit dem Buchstaben A erscheint nach der Rotation um den Ursprung um 30 Grad gegen den Uhrzeigersinn gekippt. Allgemeine 2D-Affin-Matrix a b 0 c d 0 e f 1 a, b, c, d – Skalierung und Rotation e, f – Translation (Verschiebung) Rotation um 30° 0.866 0.500 0 -0.500 0.866 0 0 0 1 a = cos 30°, b = sin 30° c = −sin 30°, d = cos 30° 0.866 0.5 -0.5 0.866 0 0 cm PDF-Operator: sechs Zahlen, dann cm – CTM wird um diese Rotation erweitert. Ergebnis A vorher A nachher
Eine 2D-Affintransformation wird in PDF durch sechs Werte a b c d e f beschrieben – die siebte Spalte ist fest. Eine Rotation um 30° gegen den Uhrzeigersinn ergibt 0.866 0.5 -0.5 0.866 0 0 cm . Translation in derselben Matrix erfolgt über e und f.

Wer eine Drehung mit Verschiebung kombinieren möchte, fügt Translation über e und f direkt in dieselbe Matrix ein. Wer mehrere Transformationen hintereinander braucht, ruft mehrfach cm auf – jeder Aufruf verkettet die neue Transformation mit der bestehenden CTM (Matrix-Multiplikation, von rechts).

Verschiebung + Rotation
q   1 0 0 1 200 400 cm          % Translation: Ursprung verschieben   0.866 0.5 -0.5 0.866 0 0 cm    % danach Rotation um 30°    % ab hier zeichnen alle Operatoren um den neuen Ursprung,    % gedreht um 30° gegen den Uhrzeigersinn Q

Marked Content: die Brücke zur Barrierefreiheit

Inhalte werden barrierefrei nutzbar, indem ihre Bedeutung über den Struktur-Baum einer PDF erschlossen wird. Verknüpft werden Struktur-Baum und Content Stream über Marked-Content-Sequenzen. Im Content Stream gibt es dafür drei Operatoren:

  • BDCBegin marked-content with Dictionary : eröffnet einen markierten Bereich mit Tag (z. B. /P ) und einer Eigenschaft, die typischerweise eine MCID enthält.
  • EMCEnd Marked Content : schließt den zuletzt geöffneten Bereich.
  • BMC – wie BDC , aber ohne Properties-Dictionary. Wird selten für Tagging verwendet.
Marked Content mit MCID
/P << /MCID 0 >> BDC    BT     /F1 12 Tf     72 720 Td     (Hallo Welt) Tj    ET EMC

Der Struktur-Baum verweist über die MCID auf genau diese Sequenz und gibt ihr eine semantische Rolle (hier: einen Absatz, /P ). Ohne diese Verbindung würden Reader und Assistive-Technologie zwar sehen, dass dort Text gezeichnet wird – aber nicht, was er bedeutet. Damit liegt die strukturelle Erklärung von Barrierefreiheit im Format: Operator-Schicht zeichnet, Tag-Schicht bedeutet, Marked Content verbindet.

Mythen-Prüfung

Drei Missverständnisse über den Content Stream

„Text in einer PDF ist Unicode-Text." In der Regel nicht. Tj und TJ zeichnen Glyphen , deren Bedeutung über die Schrift-Ressource festgelegt ist. Erst der /ToUnicode -Eintrag der Schrift bringt eine zuverlässige Rückabbildung auf Unicode-Zeichen. Fehlt dieser Eintrag, ist Text-Extraktion und Vorlesen unzuverlässig – auch wenn die Datei optisch perfekt aussieht.

„Eine Drehung erreicht man mit einem einzigen Operator." Es gibt keinen dedizierten Rotations-Operator. Drehung wird ausschließlich über die Matrix cm (oder für Text über Tm ) erreicht – mit den Werten cos α · sin α · -sin α · cos α · 0 · 0 .

„Tagging passiert nach dem Zeichnen, also nachträglich." Korrekt ist: Tagging passiert parallel zum Zeichnen – im selben Content Stream, durch BDC / EMC . Nachträgliche Tagging-Tools rekonstruieren diese Klammerung rückwärts, was selten so sauber gelingt wie eine gleich richtig getaggte Erstausgabe.

Was du daraus mitnimmst

Drei Punkte, die das Lesen jedes Content Streams erleichtern:

  • Postfix-Notation, kleine Operator-Sprache, kein Kontrollfluss. Jeder Befehl verbraucht die unmittelbar davor liegenden Argumente, endet die Zeile – und ändert entweder den Graphics-State oder zeichnet etwas.
  • Alles, was kein Text ist, ist Pfad-Konstruktion + Painten. Linien, Rechtecke und Kurven werden mit m , l , c oder re aufgebaut und mit S , f oder B realisiert.
  • Transformationen sind sechs Zahlen. Wer a b c d e f cm versteht, versteht Position, Skalierung, Rotation und Scherung in PDF. Translation steckt im letzten Zahlenpaar, Drehung im Rest.

Wer diese Schicht beherrscht, kann nicht nur PDF-Inhalte lesen, sondern auch gezielt schreiben – und versteht, was beim Tagging strukturell passieren muss, damit eine PDF aus Coding-Sicht tatsächlich barrierefrei wird.