Formats8 Min. Lesezeit

Wie das Bearbeiten von Text in einer PDF funktioniert: Content-Streams, Schriftkodierungen und warum immer nur eine Zeile geht

Eine PDF-Seite kennt keine Absätze und keine Textfelder, nur Anweisungen, Glyphen mit einer Schrift an einer Position zu zeichnen. Beim Bearbeiten einer Zeile werden die alten Anweisungen gelöscht und an derselben Stelle neue geschrieben, und daraus ergibt sich, was sich bearbeiten lässt und was nicht.

PDF123 · Updated 2026-10-14
Wie das Bearbeiten von Text in einer PDF funktioniert: Content-Streams, Schriftkodierungen und warum immer nur eine Zeile geht

Eine PDF-Seite hat keine Absätze und keine Textfelder. Sie hat eine Liste von Anweisungen: An dieser Position, mit dieser Schrift und dieser Größe, zeichne diese Glyphen. Text zu ändern heißt, die Anweisungen zu löschen, die diese Zeile zeichnen, und an derselben Position neue zu schreiben, mit derselben Schrift, Größe und Farbe. Deshalb bearbeitet PDF bearbeiten eine Zeile nach der anderen und kann Text nicht umbrechen: Das ergibt sich aus der Art, wie der Text gespeichert ist.

Wie eine Textzeile in der Datei aussieht

Der Inhalt einer Seite steckt in einem Content-Stream, einem kleinen Programm aus Operatoren. Vier Operatoren leisten bei Text die meiste Arbeit: BT und ET umschließen einen Textblock, Tf wählt Schrift und Größe, Td springt zur nächsten Position und Tj zeichnet eine Zeichenkette. Mit einer Standardschrift sieht der einfachste Fall so aus:

BT /F1 18 Tf 20 150 Td (Hello World) Tj ET

Das bedeutet: Wähle Schrift F1 mit 18 Punkt, gehe zu x 20, y 150 und zeichne Hello World. Eine aus Chrome gedruckte PDF sieht meist anders aus: In den Klammern stehen keine Zeichen mehr, sondern Glyphennummern der Schrift, und Tm setzt die Position einmalig. Unten steht die Zeile Hello World from Georgia aus einer von Chrome exportierten Seite, in der je vier Hexadezimalziffern eine Glyphennummer sind:

BT /F4 24 Tf 1 0 0 -1 30 52 Tm <002b0048004f004f00520003003a00520055004f0047000300490055005200500003002a004800520055004a004c0044> Tj ET

002b ist H, 0048 ist e, 004f ist l, 0003 ist ein Leerzeichen, 003a ist W und so weiter. Diese Nummern haben nur innerhalb dieser einen Schrift eine Bedeutung; es sind keine Unicode-Werte. Beim Kopieren des Textes übersetzt der Betrachter die Nummern über eine Nachschlagetabelle, die an anderer Stelle in der Datei liegt, wieder in Zeichen.

Daraus folgen zwei Dinge. Erstens steht im Content-Stream nirgends „dies ist ein Absatz“: Ein Zeilenumbruch ist nur ein weiteres Td oder Tm, das die Position auf die nächste Zeile setzt, und wie viele Zeilen ein Absatz hat, muss aus den Positionen erschlossen werden. Zweitens braucht man, um ein neues Zeichen in eine Zeile zu schreiben, die Nummer dieses Zeichens in dieser Schrift, und die Schrift enthält oft nur die Glyphen, die beim Schreiben der Datei verwendet wurden. Das ist das Ergebnis des Font-Subsettings.

Die Streams lassen sich mit qpdf entpacken:

qpdf --qdf --object-streams=disable in.pdf out.pdf

Öffne out.pdf in einem Texteditor und suche nach Tj, um jede auf der Seite gezeichnete Zeichenkette zu sehen. Was eine PDF sonst noch enthält, steht in Was in einer PDF steckt.

Wie der Editor aus Glyphen wieder eine „Zeile“ baut

Der Editor muss zuerst lose Glyphen zu einer Zeile zusammenfassen, die man anklicken kann. Glyphen mit gleicher Schrift, Größe und Farbe, deren Abstand zur vorherigen Glyphe höchstens eine Schriftgröße beträgt, gehören zum selben Textabschnitt, einer Gruppe benachbarter Glyphen, die der Editor als Einheit behandelt; ein Zeilenumbruch, eine neue Schrift oder eine neue Farbe beendet ihn. Text, der von rechts nach links oder senkrecht verläuft, wird nicht zusammengefasst und lässt sich nicht zum Bearbeiten öffnen.

Zwei Folgen lassen sich direkt beobachten. Eine Zeile mit zwei Schriften, etwa eine chinesische Zeile mit einem englischen Wort darin, wird in zwei benachbarte Textabschnitte geteilt, die einzeln angeklickt werden. Nach einer Bearbeitung passiert dasselbe: Verwendet der neue Text eine andere Schrift, wird die Zeile zu zwei Textabschnitten, und das Bearbeiten des einen lässt den anderen unverändert.

Eine Zeile bearbeiten: erst den alten Text löschen, dann den neuen schreiben

Das Löschen nutzt die reine Textform des Schwärzens: Die Glyphen im Bereich der Zeile werden aus dem Content-Stream entfernt, Bilder und Linien bleiben unberührt, und über den alten Text wird kein farbiges Feld gelegt, sodass Kopieren und Suchen ihn nicht mehr finden. Es ändert nur den Seiteninhalt; steht derselbe Text auch in einer Anmerkung oder in den Dokumenteigenschaften, bleiben diese unberührt.

Das Schreiben hängt eine neue Textanweisung ans Ende des Content-Streams der Seite an, ausgehend von der Grundlinienposition der ursprünglichen Zeile, mit deren Größe und Farbe. Nach dem Ändern der obigen Zeile in Hello World 2 wird am Ende der Seite Folgendes angehängt:

q 1 0 0 1 22.5 148.92 cm 0 0 0 rg
/Span <</ActualText <feff00480065006c006c006f00200057006f0072006c006400200032>>> BDC
BT /F4 18 Tf 0 0 Td <002b0048004f004f00520003003a00520055004f00470003> Tj ET
BT /F9 18 Tf 102.18 0 Td (2) Tj ET
EMC
Q

Das erste BT verwendet die ursprüngliche Schrift F4 und die ursprünglichen Glyphennummern, sodass Hello World genau so wiederverwendet wird, wie es war. Die „2“ steckt nicht im ursprünglichen Schrift-Subset, daher startet das zweite BT eine neue Standardschrift Helvetica (F9), um sie zu zeichnen. q und Q begrenzen die Koordinatentransformation und die Farbe dieses Blocks auf ihn selbst; der Editor setzt außerdem den ursprünglichen Inhalt in ein q/Q-Paar, damit ein am Ende der Originalseite unausgeglichen hinterlassener Zustand den neuen Text nicht beeinflussen kann. So sah ein Textabschnitt aus; die Zahlen unterscheiden sich von Datei zu Datei.

Das Anhängen am Ende hat drei Folgen:

  • Der neue Text wird zuletzt gezeichnet, also über allem anderen auf der Seite. Text, der vorher unter einem Bild oder einem farbigen Block lag, erscheint nach einer Bearbeitung darüber.
  • Ein Werkzeug, das Text in Content-Stream-Reihenfolge extrahiert (wie pdftotext -raw), liest die bearbeitete Zeile auf dieser Seite zuletzt, während die Extraktion nach Position (pdftotext -layout) keine Änderung zeigt. Die Seite sieht gleich aus, aber die Lesereihenfolge hat sich geändert.
  • Der Editor hängt dem neuen Text einen ActualText an, der einem Betrachter sagt, für welche echten Zeichen die Glyphen stehen. Eine Glyphe kann mehreren Zeichen entsprechen, und das Nachschlagen allein anhand der Glyphennummer liefert manchmal ein Kompatibilitätszeichen (zum Beispiel kommt für 文 das Kangxi-Radikal ⽂ heraus); ActualText stellt sicher, dass beim Kopieren das herauskommt, was du getippt hast.

Welche Schrift den neuen Text zeichnet

Die ursprüngliche Schrift weiterzuverwenden ist die sicherste Möglichkeit, weil Glyphenform, Strichstärke und Breite zu dem passen, was daneben steht. Ob sie sich verwenden lässt, hängt davon ab, ob der Editor feststellen kann, welchen Code ein Zeichen in dieser Schrift hat:

  • Eine zusammengesetzte Schrift wie die obige (Type0, Zwei-Byte-Glyphennummern, Kodierung Identity-H): das Zeichen in der Schrift nachschlagen, um seine Glyphennummer zu erhalten, und sie verwenden, wenn es gefunden wird. Wird es nicht gefunden, ist das Zeichen nicht im Subset.
  • Eine einbytige einfache Schrift (TrueType, Type1) mit der Kodierung WinAnsi oder MacRoman: Die Zuordnung von Zeichen zu Byte steht fest, und die Breiten stammen aus der Breitentabelle in der Datei.
  • Eine Schrift mit angepasster Kodierung (Differences) oder eine, die sich auf die eingebaute Kodierung der Schrift stützt: Es gibt keine verlässliche Zuordnung, daher rät der Editor nicht und greift auf eine Ersatzschrift zurück, weil der Text sonst unlesbar würde.

Fehlt ein Zeichen in der ursprünglichen Schrift, kommt eine Ersatzschrift zum Einsatz. Einfache lateinische Buchstaben verwenden die nächstliegende von Helvetica, Times und Courier, Standardschriften, die jeder Betrachter hat, sodass nichts eingebettet werden muss. Alles andere, etwa chinesische Zeichen, Kana, Hangul, Thai, lateinische Buchstaben mit Akzent, Kyrillisch und Griechisch, verwendet eine Open-Source-Schrift aus der Noto-Familie: Der Server nimmt die Zeichen aus der Anfrage und schneidet genau diese Glyphen mit der quelloffenen Subsetting-Bibliothek HarfBuzz aus der vollständigen Schrift heraus, meist wenige KB, und der Browser bettet sie in deine Datei ein. Die regionale Form eines chinesischen Zeichens folgt der Sprache der Oberfläche: vereinfacht, traditionell, japanisch oder koreanisch.

Die Glyphen werden eingebettet, statt nur einen Schriftnamen zu schreiben, weil eine allein per Name referenzierte Schrift von jedem Betrachter durch eine beliebige Ersatzschrift ersetzt wird und die Strichstärke zwischen Betrachtern abweichen kann; eingebettet zeigt jeder Betrachter dieselben Formen.

Warum immer nur eine Zeile

Der Editor hat keine Layout-Engine. Jede Glyphenposition steht in der Datei fest; der neue Text wird einfach vom Startpunkt aus nach rechts gesetzt, mit den Breiten der Schrift, ohne Logik für einen Umbruch am Rand, und nichts dahinter wird verschoben. Wird eine Zeile länger, laufen die zusätzlichen Zeichen nach rechts weiter, und was über den Seitenrand hinausgeht, ist nicht zu sehen. Einen Absatz umzuschreiben oder einer Tabelle Zeilen hinzuzufügen erfordert ein neues Layout, und das heißt: zurück zur Quelldatei, aus der die PDF erzeugt wurde.

Die Breiten sind außerdem nur die eigenen Vorschubbreiten der Schrift (wie weit es nach jeder Glyphe weitergeht), ohne Kerning und ohne Glyphenersetzung. Text, der kein Shaping braucht, also keine kontextabhängige Auswahl und Positionierung von Glyphen (Lateinisch, Kyrillisch, Griechisch und chinesische Zeichen), wird korrekt gesetzt. Thai-Tonzeichen brauchen Glyphenpositionierung, um an der richtigen Stelle zu sitzen, was der Editor nicht leistet, daher können sie versetzt sein. Arabisch braucht kontextabhängige Glyphenformen und Layout von rechts nach links, Hebräisch braucht Layout von rechts nach links, und indische Schriften brauchen Konsonanten und Vokalzeichen, die zu Glyphen verbunden werden; all das erfordert vollständiges Shaping, das der Editor derzeit nicht leistet, daher wird die Eingabe abgelehnt, statt falsche Zeichen zu erzeugen.

Zwei weitere Grenzen stammen aus der Datei selbst. Der Inhalt einer gescannten Seite ist ein einziges Bild ohne Textoperatoren, es gibt also nichts zu löschen oder zu ändern. Wird eine Datei mit digitaler Signatur bearbeitet, hat sich der ursprüngliche Inhalt, den die Signatur geschützt hat, geändert, und die Signatur ist nicht mehr gültig.

Selbst nachprüfen

Mit drei Befehlen siehst du die beschriebenen Änderungen in einer heruntergeladenen Datei:

pdftotext -raw edited.pdf -      # Extraktion in Content-Stream-Reihenfolge: der alte Text ist weg, die bearbeitete Zeile kommt zuletzt
pdffonts edited.pdf              # Schriftenliste: die ursprünglichen, wiederverwendeten Schriften und das neu eingebettete Schrift-Subset
qpdf --qdf --object-streams=disable edited.pdf edited.qdf   # Content-Stream entpacken; der angehängte BT ... ET-Block steht am Ende

Weiterlesen

Open tool
Process in the browser — no watermark, files removed after the job.
Open tool