Postscript Type1

Dieses Format war einst weit verbreitet, vor allem im professionellen Sektor. Mittlerweile ist es weitgehend durch TrueType und OpenType ersetzt. Trotzdem findet man es aber noch, und es kann relativ leicht eingebunden werden.

die Dateien

Eine Postscript Type1 Schrift besteht aus mehreren Dateien. Zumindest gibt es eine AFM Datei mit den Metadaten, und eine PFB Datei mit den eigentlichen Schriftdaten. Oft sind noch eine PFM und eine INF Datei vorhanden, welche vom Adobe Type Manager benötigt werden.

Wir benötigen lediglich die AFM Datei, um an die Metadaten für das Schriftobjekt und das Schriftdeskriptorobjekt zu kommen, sowie die PFB Datei für das Streamobjekt. Manchmal gibt es statt der PFB Datei eine PFA Datei. Das ist ein leicht anderes Format, welches von PDF nicht unterstützt wird. Zum Glück kann PFA aber leicht in PFB umgewandelt werden. Vorsicht: PFA Dateien können ausser Type1 Schriften auch die nicht unterstützten Type3 oder Type42 Schriften enthalten.

Falls die AFM-Datei fehlt, so kann sie z.b. mit dem mit Ghostscript mitgelieferten Utility pfb2afm rekonstruiert werden. Viele der Metadaten in einer so erstellten AFM-Datei enthalten aber nur Abschätzungen, und natürlich fehlen jegliche Feinjustierungen durch den Schriftdesigner.

Vorsicht Postscript Umsteiger:

Das „ISOLatin1Encoding“ von Postscript setzt den Code 0x2D auf das Zeichen „minus“. Das „WinAnsiEncoding“ von PDF setzt diesen Code hingegen auf „hyphen“. Beide stellen einen einfachen, horizontalen Strich dar, aber „hyphen“ ist in vielen Schriften schmaler als „minus“.

Metadaten

Wie gesagt sind die Metadaten in der AFM Datei enthalten. Dabei handelt es sich um eine Textdatei. Normalerweise haben diese Zeilenumbrüche nach Microsoft-Norm (<0D 0A>), gelegentlich aber auch nach Unix-Norm (<0A>). Solche nach der pre Mac OS X Norm (<0D>) sollten mittlerweile ausgestorben sein. Die meisten Programmiersprachen können alle Varianten lesen. Falls aber Schwierigkeiten auftreten, hilft es unter Umständen, die Datei vorgängig in die passende Variante zu konvertieren.

Die Informationen sind zeilenweise abgelegt, wobei grundsätzlich eine Information pro Zeile steht. Das erste Wort der Zeile benennt den Typ der Information, der Rest ist die Information selbst. Worte und Werte sind durch ein oder mehrere Leerschläge oder Tabulatoren getrennt.

Die erste Zeile ist immer StartFontMetrics v, wobei v für eine Versionsnummer steht. Die letzte Zeile ist immer EndFontMetrics.

allgemeine Werte

Von den in der Datei angegebenen Werten interessieren uns zunächst jene, welche mit folgenden Worten gekennzeichnet sind:

FontName Schriftname
Ascender Oberlänge
Descender Unterlänge
CapHeight Versalhöhe
FontBBox Zeichenumfang
ItalicAngle Schrägung
StdVW Stammdicke
IsFixedPitch Fixbreitenschrift
CharWidth Zeichenbreite
EncodingSchemedie interne Schriftkodierung

FontName muss in ein Namenobjekt umgewandelt werden, und kann danach für /BaseFont im Schriftobjekt und für /FontName im Schriftdeskriptorobjekt eingesetzt werden. Ascender, Descender, CapHeight, ItalicAngle und StdVW können 1:1 in /Ascent, /Descent, /CapHeight, /ItalicAngle und /StemV des Schriftdeskriptorobjekts übernommen werden. FontBBox hat als Wert 4 durch Leerschläge und/oder Tabulatoren getrennte Zahlen. Diese müssen noch mit eckigen Klammern umschlossen werden, um ein Array zu bilden, und können danach für /FontBBox des Schriftdeskriptorobjekts eingesetzt werden.

Für die Flags gilt folgendes:

  • Kommt IsFixedPitch vor, und ist als Wert true angegeben, so ist es eine Fixbreitenschrift, sonst nicht.
  • Kommt EncodingScheme vor, und ist als Wert FontSpecific angegeben, so ist es eine symbolische Schrift, sonst eine nichtsymbolische Schrift.
  • Kommt ItalicAngle vor, und ist der Wert nicht 0, so ist es eine Schrägschrift, sonst nicht.

CharWidth kann bei Fixbreitenschriften vorkommen. Falls vorhanden, hat es als Wert zwei durch Leerschläge und/oder Tabulatoren getrennte Zahlen, von denen die erste Zahl die Zeichenbreite für alle Zeichen dieser Schrift ist.

Schreibrichtungsblöcke

Gewisse Angaben können für horizontale und vertikale Schreibrichtung unterschiedlich sein. Für diesen Fall werden in der Datei Abschnitte definiert, die nur für eine Schreibrichtung gültig sind.

Ein Schreibrichtungsblock beginnt mit einer Zeile StartDirection d, wobei d für die Schreibrichtung steht. 0 ist horizontal, 1 ist vertikal, 2 ist beides. Der Schreibrichtungsblock endet mit einer Zeile EndDirection.

Da wir nur die horizontale Schreibrichtung verwenden, können wir es uns einfach machen: Wenn immer wir eine Zeile StartDirection 1 finden, ignorieren wir einfach alle weiteren Zeilen, bis wir eine mit EndDirection finden.

Zeichenbreitenblock

Falls wir eine Fixbreitenschrift haben, und CharWidth definiert wurde, können wir die dort angegebene Breite für alle Zeichen übernehmen. Ansonsten müssen wir sie aus dem Zeichenbreitenblock holen.

Der Zeichenbreitenblock beginnt mit einer Zeile StartCharMetrics x, wobei x für die Anzahl Zeichen steht. Der Block endet mit einer Zeile EndCharMetrics. Die Zeilen innerhalb des Blocks haben ein spezielles Format.

Jedes Zeichen hat seine eigene Zeile. Jede Zeile besteht aus verschiedenen Informationen, die mit Strichpunkten voneinander getrennt sind. Die einzelnen Informationen bestehen aus einer Buchstabenkombination, die den Typ festlegt, und einem oder mehreren Werten. Die Typen, Werte und Strichpunkte sind durch ein oder mehrere Leerschläge und/oder Tabulatoren getrennt.

Beispiel:

C 36 ; WX 556 ; N dollar ; B 32 -126 518 770 ;

Von den Informationen interessieren uns folgende:

N Name des Zeichens
C Nummer des Zeichens
WX oder W0X Breite des Zeichens
W oder W0 Breite und Höhe des Zeichens

Im Falle von WX oder W0X haben wir genau eine Zahl, welche die Zeichenbreite ist. Bei W oder W0 haben wir zwei Zahlen, von denen die Erste die Zeichenbreite ist. In beiden Fällen ist die Zeichenbreite in Promillen der Schriftgrösse, also schon im richtigen Format.

Um bei symbolischen Schriften die Zeichenbreiten zu erhalten, suchen wir für alle möglichen Codes von 0 bis 255 einen Eintrag mit entsprechender Zeichennummer.

Um bei normalen Schriften die Zeichenbreiten zu erhalten, ermitteln wir für alle möglichen Codes von 0 bis 255 den passenden Zeichennamen, und suchen anschliessend einen Eintrag mit diesem Namen. Im Anhang ist eine Tabelle mit den Zeichennamen für die Zeichen in „Windows westlich“.

Bei Zeichen, die keinen Namen haben, oder für die wir keinen Eintrag finden, nehmen wir als Breite 0.

Die so ermittelten Breiten stellen wir zu einer Liste zusammen. Diese können wir nun für /Widths im Schriftobjekt verwenden.

Kerningblock

Die Datei kann einen Kerningblock enthalten. Dieser enthält Angaben zur Unterschneidung. Wir brauchen diese Angaben nicht, müssen aber wissen, wie der Block erkannt und überlesen werden kann.

Der Kerningblock, soweit vorhanden, beginnt mit einer Zeile StartKernData, und endet mit einer Zeile EndKernData. Diesen Bereich müssen wir überlesen.

falls was fehlt

Die meisten Angaben sind optional. Wir müssen sie ersetzen, falls sie nicht in der Datei sind.

  • Falls Ascender fehlt, können wir statt dessen die vierte Zahl von FontBBox verwenden.
  • Falls Descender fehlt, können wir statt dessen die zweite Zahl von FontBBox verwenden.
  • Falls CapHeight fehlt, können wir statt dessen Ascender verwenden.
  • Falls ItalicAngle oder StdVW fehlt, können wir als Wert 0 annehmen.
  • Falls IsFixedPitch fehlt, können wir als Wert false annehmen.
  • Falls EncodingScheme fehlt, können wir als Wert AdobeStandardEncoding annehmen.

die Schriftdaten

Die eigentlichen Schriftdaten sind in einer PFB Datei. Dabei handelt es sich um eine binäre Datei. Zahlen, die mehr als 1 Byte benötigen, sind in Little-Endian Anordnung. Es kommen folgende Datentypen zur Anwendung:

uint8 vorzeichenlose 8-bit Zahl
uint32vorzeichenlose 32-bit Zahl

Der Aufbau ist ähnlich wie bei JPEG: Die Datei besteht aus mehreren aufeinanderfolgenden Blöcken, die mit einem 2 Byte langen Marker und einer 4 Byte langen Längenangabe beginnen. In diesem Fall sind es immer exakt drei Blöcke.

PositionGrösseTyp Inhalt
01 Byteuint8 immer 0x80
11 Byteuint8 Inhaltstyp
24 Byteuint32 Datenlänge
6Datenlänge* Daten

Der Marker hat als erstes Byte 0x80, und als zweites Byte den Wert 1 oder 2, was den Inhaltstyp festlegt. Der erste und dritte Block sind immer Typ 1, der zweite Block ist immer Typ 2. Auf den Marker folgt ein uint32 Wert mit der Länge des Blocks. Die Längenangabe ist dabei nur für die eigentlichen Blockdaten, ohne die 6 Bytes für Marker und Längenangabe selbst. Nach dem dritten Block folgt ein Marker <80 03> ohne Längenangabe, welcher das Ende der Datei markiert.

Wir müssen den Inhalt nicht weiter interpretieren. Wir müssen lediglich die Daten der einzelnen Blöcke extrahieren und aneinanderhängen (ohne Marker und Längenangaben). Diese aneinandergehängten Daten werden als Inhalt des Streamobjekts verwendet. Die drei Längenangaben müssen wir als /Length1, /Length2 und /Length3 in das Dictionary des Streams eintragen.

Der dritte Block einer PFB Datei ist immer gleich. Darum ist es uns erlaubt diesen wegzulassen. In dem Fall muss /Length3 einfach auf 0 gesetzt werden.

Beispiel:

<80 01 B9 12 00 00> (4793 Bytes) <80 02 68 75 00 00> (30056 Bytes) <80 01 14 02 00 00> (532 Bytes) <80 03>

Der erste Block ist 0x12B9 (4793) Bytes lang, der zweite 0x7568 (30056) Bytes, und der letzte wie eigentlich immer 0x214 (532) Bytes. Wenn wir nur mit den ersten beiden Blocks arbeiten, sollte das Streamobjekt also etwa so aussehen:

6 0 obj
<<
/Length1 4793
/Length2 30056
/Length3 0
/Length 44145
/Filter /ASCII85Decode
>>
stream
...~>
endstream
endobj

PFA verarbeiten

Falls wir die Datei im PFA Format haben, können wir sie folgendermassen verwenden:

PFA Dateien sind Textdateien, nicht binäre Dateien. Auch sie bestehen grundsätzlich aus denselben drei Blöcken. Deren Abgrenzung ist aber nicht mit Blockmarkern festgelegt, sondern ergibt sich aus dem Kontext.

Der erste Block geht vom Anfang der Datei bis zum Schlüsselwort eexec1). Er endet nach dem auf eexec folgenden Zeilenumbruch.

Der dritte Block ist am Dateiende. Er umfasst 512 Nullen, die von Leerschlägen, Tabulatoren und Zeilenumbrüchen unterbrochen sein können2), gefolgt vom Schlüsselwort cleartomark.

Der zweite Block geht exakt vom Ende des ersten Blocks zum Anfang des dritten Blocks. Es handelt sich um binäre Daten in Hexadezimalschreibweise. Um diesen Block verwenden zu können, müssen wir die binären Daten rekonstruieren. Die meisten Programmiersprachen liefern hierfür eine fertige Funktion mit.

Nun hängen wir den ersten Block und den konvertierten zweiten Block aneinander, und verwenden dies als Inhalt des Streamobjekts. /Length1 setzen wir auf die Länge des ersten Blocks, /Length2 auf die Länge des konvertierten zweiten Blocks, und /Length3 auf 0.

1) Falls kein Schlüsselwort eexec vorhanden ist, ist es keine Type1 Schrift.
2) eigentlich 256 0-Bytes in Hexadezimalschreibweise