Syntax

Wie bei allen Textformaten haben auch die Daten in einem PDF eine bestimmte Syntax. Das sind bestimmte Regeln, wie die Daten in die Datei geschrieben werden. Bevor wir den Aufbau der Datei genauer anschauen, müssen wir erst mal diese Regeln kennen.

Alles im eigentlichen Dokumentkörper ist in „Objekten“ organisiert. Im Hallo Welt ist der Dokumentkörper der Teil zwischen %PDF-1.4 und xref (nicht einschliesslich dieser Zeilen). Ein „Objekt“ im Sinne von PDF ist irgend eine Information. Es hat also nicht direkt mit objektorientierter Programmierung zu tun.

Von diesen Objekten gibt es verschiedene Typen, die verschiedene Informationen aufnehmen können, und verschieden in die Datei geschrieben werden. Es gibt aber eine universelle Regel: Zwei hintereinander stehende Objekte müssen durch ein oder mehrere Leerräume getrennt sein, ausser wenn der Beginn des nächsten Objekts von der Syntax her nicht ohnehin klar ist.

Eine generelle Regel muss noch beachtet werden: Es wird überall zwischen Gross- und Kleinschreibung unterschieden. Darum muss auch die Schreibweise immer exakt eingehalten werden.

Leerraum

Als Leerraum gelten der Leerschlag (ASCII 0x20), der Tabulator (ASCII 0X09), und der Zeilenumbruch. Was letzteren betrifft, so ist sowohl der von Windows verwendete IBM-Zeilenumbruch (ASCII 0x0D gefolgt von 0x0A), der unter anderem von Linux und Mac OS X verwendete Unix-Zeilenumbruch (ASCII 0x0A) und der bis Mac OS 9 verwendete Apple-Zeilenumbruch (ASCII 0X0D) erlaubt.

Im Grunde ebenfalls erlaubt sind das NUL-Zeichen (ASCII 0x00) und der Seitenvorschub (ASCII 0x0C). Diese Zeichen sind in der Praxis aber nicht verbreitet.

Alle Objekte sollten durch Leerräume getrennt sein. Art und Anzahl ist nicht relevant.

Zahlen

Zahlen können als Ganzzahlen oder Dezimalbrüche geschrieben werden. Ganzzahlen schreibt man einfach mit den Ziffern 0-9, bei negativen Zahlen mit einem vorangestellten Minus. Andere Zeichen sind nicht erlaubt, insbesondere keine Leerschläge oder Tausendertrenner.

Richtig:

1234 -1234

Falsch:

1'234 1234-

Bei Dezimalzahlen muss noch ein Dezimalpunkt an der richtigen Stelle gesetzt werden muss. Das Komma ist nicht erlaubt, ebensowenig Exponentialnotation.

Richtig:

123.4 0.1234

Falsch:

123,4 1.234e2

Namen

Namen sind kurze Texte, die dazu dienen, dokumentinterne Dinge zu bezeichnen. Sie werden dargestellt durch einen Schrägstrich gefolgt vom Text. Als Zeichen im Text sind alle druckbaren ASCII-Zeichen erlaubt (Bereich 0x20 – 0x7E) ausser Leerräumen, sowie den Zeichen #,(,),<,>,[,],{,},/ und %.

/Type
/MediaBox

Manchmal muss man Namen setzen, die Leerschläge oder andere problematische Zeichen enthalten. Diese können dargestellt werden, indem eine Raute gefolgt vom Hexcode des Zeichens verwendet wird. Die Kodierung sollte ASCII oder UTF-8 sein, zumal Programme, welche Namenobjekte anzeigen, normalerweise diese beiden Kodierungen erwarten.

Louis Grand/Louis#20Grand
Seite#34 /Seite#2334
Höflinger /H#C3#B6flinger

Als weitere Einschränkung gilt: Der Code für das NUL-Zeichen (#00) darf nicht vorkommen, und die übrigen Codes für Leerräume (#09, #0A, #0C, #0D, #20) dürfen nicht unmittelbar auf den einleitenden Schrägstrich folgen.

Strings

Strings sind Texte, die zur Ausgabe in irgend einer Form gedacht sind. Sie werden einfach in runde Klammern eingeschlossen. Der Text selbst darf ohne weiteres runde Klammern enthalten, solange geöffnete Klammern mit nachfolgenden schliessenden Klammern ein Paar bilden (jede Klammer darf nur zu einem Paar gehören). Überzähligen Klammern muss ein Backslash vorangestellt werden. Backslashes, die im Text selbst enthalten sind, müssen verdoppelt werden.

Hallo Welt (Hallo Welt)
Text (mit Klammern) drinn (Text (mit Klammern) drinn)
Text (mit (unausgeglichenen Klammern) drinn (Text \(mit (unausgeglichenen Klammern) drinn)
Text \mit \Backslashes (Text \\mit \\Backslashes)

Die Zeichenkodierung ist ein Knackpunkt. Strings für Metadaten müssen in PDFDoc kodiert sein. Das ist eine Variante von ISO Latin-1 bei der manche Steuerzeichen durch zusätzliche, druckbare Zeichen ersetzt wurden. Ebenfalls ersetzt wurde das Zeichen mit dem Code 0xA0. Dieses steht in ISO Latin-1 für einen Abstand innerhalb eines Wortes, in PDFDoc hingegen f??r das Eurozeichen.

Strings für Texte auf dem „Papier“ hingegen sind in der Kodierung der jeweiligen Schrift. Das kann uns ein heilloses Kodierungsdurcheinander bescheren. Zum Glück gibt es einen Trick. Im Hallo Welt haben wir die Kodierung des Fonts mit /Encoding /WinAnsiEncoding auf WinAnsi eingestellt. Das ist Adobes Name für die Kodierung „Windows westlich“.

Windows westlich ist wie PDFDoc eine Variante von ISO Latin-1, bei der einige Steuerzeichen durch druckbare Zeichen ersetzt wurden. Im Unterschied zu PDFDoc hat man dabei die Finger von bestehenden, druckbaren Zeichen gelassen. Wenn wir uns also festlegen, dass wir alle Fonts auf Windows westlich stellen, und uns in Metadatenstrings auf druckbare ISO Latin-1 Zeichen (ausser Code 0xA0) beschränken, dann können wir die Datei einfach als in Windows westlich kodiert betrachten.

Hexstrings

Manchmal müssen wir Zeichenketten einfügen, die nicht einem Text nach ASCII, ISO Latin-1, PDFDoc oder Windows westlich entsprechen. Für solche Fälle gibt es die Hexstrings. Diese werden in spitze Klammern eingeschlossen (gemeint ist das „kleiner als“ und das „grösser als“ Zeichen), und enthalten pro Byte zwei Hexadezimalziffern. Leerräume werden ignoriert. Hier ein paar Varianten von „Hallo Welt“ als Hexstring:

<48 61 6C 6C 6F 20 57 65 6C 74>

<48616C6C6F2057656C74>

<48616C6C6F20
57656C74>

null, true und false

Dies sind drei wenig gebrauchte Spezialobjekte.

Arrays

Arrayobjekte sind Sammlungen zusammengehöriger Objekte. Dazu werden die enthaltenen Objekte einfach – wie üblich durch Leerräume getrennt – hintereinander geschrieben. Die gesamte Liste wird mit eckigen Klammern umschlossen. Da das Array selbst auch ein Objekt ist, kann es ebenfalls in Arrays enthalten sein.

[1 2 3]
[/Eins /Zwei /Drei]
[/Ein /Array [/mit /verschachteltem] /Array]

Dictionaries

Dictionaryobjekte sind ebenfalls Sammlungen zusammengehöriger Objekte. Im Unterschied zu Arrays bekommt aber jedes enthaltene Objekt einen Bezeichner. Die Reihenfolge der Objekte ist dabei egal (bei Arrays ist dies in der Regel nicht der Fall).

Dictionaries beginnen mit zwei offenen, spitzen Klammern, und enden mit zwei geschlossenen, spitzen Klammern. Innerhalb der Klammern notiert man abwechselnd einen Bezeichner und das bezeichnete Objekt (wiederum mit Leerräumen getrennt). Die Bezeichner müssen Namenobjekte sein. Das bezeichnete Objekt darf von einem beliebigen Typ ausser null sein. Ist ein null Objekt vorhanden, so gilt der entsprechende Eintrag als nichtexistent.

<< /Title (Hallo Welt) /Author (Peter Muster) >>

<<
/Title (Hallo Welt)
/Author (Peter Muster)
>>

Wie auch bei Arrays gilt: Dictionaries sind selbst Objekte, und können darum in anderen Dictionaries oder Arrays auftauchen. Ebenso können Arrays in Dictionaries enthalten sein.

Streams

Streams dienen dazu, grössere Datenblöcken zu Objekten zusammenzufassen. Sie kommen hauptsächlich für die Seitenbeschreibung sowie für eingebettete Schriften und Bilder zum Einsatz.

Ein Stream beginnt mit einem Dictionary. Dieses muss zumindest einen Eintrag /Length haben, der die Grösse der eigentlichen Daten in Bytes beschreibt. Auf das Dictionary folgt das Schlüsselwort stream, gefolgt von einem Zeilenumbruch und den eigentlichen Daten. Nach den Daten folgt ein Zeilenumbruch und das Schlüsselwort endstream.

Zwischen stream und den Daten muss genau ein Zeilenumbruch nach Windows- oder Unix-Norm stehen. Dies, weil bei einem Zeilenumbruch nach der alten Apple-Norm oder anderen Leerräumen unter Umständen nicht klar wäre, wo der Leerraum endet und wo die Daten beginnen.

<<
/Length 51
>>
stream
Dies ist ein kurzer Stream, der als Beispiel dient.
endstream

indirekte Objekte

Ein indirektes Objekt ist ein Objekt, dem eine eindeutige Nummer zugewiesen wurde. Das ist mit jedem Objekt möglich, üblich ist es allerdings nur für Dictionaries und Streams. Streams sind nur als indirekte Objekte erlaubt.

Einem indirekten Objekt wird die Objektnummer, die Generationennummer und das Schlüsselwort obj vorangestellt. Nach dem Objekt muss das Schlüsselwort endobj stehen. Die Objektnummer ist eine fortlaufende Zahl (es dürfen keine Zahlen übersprungen werden). Die Generationennummer ist normalerweise 0. Sie wird nur bei Nachbearbeitungen gebraucht für Objektnummern, die gelöscht und später neu vergeben wurden.

1 0 obj
<<
/Title (Hallo Welt)
/Author (Peter Muster)
>>
endobj

2 0 obj
(Auch andere Objekte können indirekte Objekte sein.)
endobj

Referenzen

Referenzen sind eigentlich keine Objekte, sondern Platzhalter. Sie werden eingesetzt, um auf ein indirektes Objekt zu verweisen. Dabei kann auf ein und dasselbe indirekte Objekt auch mehrfach verwiesen werden.

Referenzen bestehen aus der Objektnummer und der Generationennummer des gewünschten indirekten Objekts, gefolgt vom Grossbuchstaben R.

1 0 obj
(indirekter Text)
endobj

2 0 obj
<<
/Direkt (direkter Text)
/Indirekt 1 0 R
>>
endobj

Referenzen in Arrays wirken etwas verwirrend, da sie auf den ersten Blick wie drei separate Objekte aussehen. Man muss manchmal schon genau hinschauen.

Dies sind 9 Zahlen:

[1 0 8 2 0 8 3 0 8]

Dies hingegen sind 3 Referenzen:

[1 0 R 2 0 R 3 0 R]

Anweisungen

In Seitenbeschreibungen finden sich noch Anweisungen. Dabei handelt es sich um festgelegte Schlüsselworte, die verschiede Aktionen auslösen. So etwa die Platzierung und das Schreiben eines Texts.

Wie in Postscript, so sind auch in PDF die Anweisungen in Postfixnotation. Das heisst, dass Parameter vor dem Schlüsselwort in die Datei geschrieben werden.

72 746 Td (Hallo Welt) Tj

Hier wird erst die Anweisung Td aufgerufen. Sie nimmt die Zahlen 72 und 746, und legt fest, dass der Text an diesen Koordinaten platziert wird. Danach wird die Anweisung Tj aufgerufen. Diese nimmt den String „Hallo Welt“, und schreibt ihn auf das virtuelle Papier.

Bei Anweisungen muss man ganz besonders auf die Gross- und Kleinschreibung achten, da hier besonders leicht Verwechslungen auftreten können. So haben etwa die Anweisungen S und s leicht unterschiedliche Bedeutungen, wie wir in Kürze sehen werden.