Problematische Schriften

TrueType Collections

Diese Dateien enthalten typischerweise zwei oder mehr Schriften, die viele gemeinsame Zeichen haben. Dabei können enthaltene Blöcke von mehreren Schriften gemeinsam genutzt werden, was Speicherplatz spart. Mit PDF ist es aber leider inkompatibel.

Um eine Schrift einer Collection in PDF nutzen zu können, müssen wir ermitteln, welche Blöcke die Schrift verwendet, und daraus eine normale TrueType-Datei zusammenstellen. Das klingt schlimmer als es ist.

TrueType Collections verwenden die Big-Endian Anordnung. Die Datei beginnt folgendermassen:

PostionGrösseTyp Wert
04 Bytechar Signatur
42 Byteuint16Version
62 Byteuint16Unterversion
84 Byteuint32Anzahl Schriften

Die Signatur muss der ASCII-String „ttcf“ sein. Die Version ist egal: Es gibt derzeit zwei, aber diese sind zueinander kompatibel.

Daraufhin folgt eine Liste mit folgendem Eintrag für jede Schrift:

PostionGrösseTyp Wert
04 Byteuint32Position Blockverzeichnis

Die Position ist gerechnet ab dem Anfang der Datei.

Das Verzeichnis ist identisch wie bei normalen TrueType Schriften. Die Positionen darin sind ebenfalls ab dem Dateianfang der TrueType Collection gerechnet. Um jetzt eine gültige TrueType-Einzelschrift zu erstellen, müssen wir folgendermassen vorgehen:

  1. An den Anfang der Schrift kommt der Verzeichnisblock.
  2. Für jeden von der Schrift verwendeten Block machen wir folgendes:
    1. Der Block wird an die Schrift angehängt.
    2. Wenn nötig wird mit 0-Bytes aufgefüllt, um eine durch 4 teilbare Schriftlänge zu erhalten.
    3. Im Verzeichnisblock wird die Position des angehängten Blocks korrigiert.
  3. Wenn alles zusammengestellt ist, wird die Prüfsummenjustierung angepasst.

Punkt 2b ist wichtig: Laut Standard muss jeder Block an einer durch 4 teilbaren Position sein, und die Länge der gesamten Datei muss ebenfalls durch 4 teilbar sein.

Die Prüfsummenjustierung ist folgendes Feld im „head“-Block, welches wir bisher nur am Rande erwähnt haben:

PostionGrösseTyp Wert
84 Byteuint32Prüfsummenjustierung

Für die Anpassung müssen wir folgendermassen vorgehen:

  1. Die Prüfsummenjustierung wird auf 0 gesetzt.
  2. Die Prüfsumme über die gesamte Schrift wird nach der bekannten Methode ermittelt.
  3. Vom Wert 0xB1B0AFBA wird die Prüfsumme abgezogen (nicht umgekehrt).
  4. Ist das Resultat kleiner 0, so wird 0x100000000 addiert.
  5. Das endgültige Resultat wird als Prüfsummenjustierung eingetragen.

Danach sollte die Prüfsumme über die gesamte Schrift wieder 0xB1B0AFBA ergeben. Manche Programme verweigern die Verwendung einer Schrift, wo dies nicht der Fall ist.

DFonts

Diese Dateien enthalten typischerweise 4 Schriften derselben Schriftart, nämlich eine normale, eine fette, eine kursive und eine fettkursive Variante. Anders als TrueType Collections enthalten DFonts dabei für jede Einzelschrift einen Block, dessen Inhalt eine gültige TrueType Schrift ist, die wir direkt in PDF verwenden können. An diesen Block heranzukommen ist allerdings etwas umständlich.

Hinweis: Die offizielle Spezifikation des DFont Formats ist nicht öffentlich. Die nachfolgenden Informationen basieren auf Tipps von Dritten und Analysen der existierenden Dateien.

Die Datei verwendet die Big-Endian Anordnung. Sie beginnt mit folgenden Informationen:

PostionGrösseTyp Wert
04 Byteuint32Startposition Blocks
44 Byteuint32Position Verzeichnis

Die Positionen sind gerechnet ab dem Anfang der Datei. Betrachten wir nun das Verzeichnis. Dieses beginnt folgendermassen:

PostionGrösse Typ Wert
024 Byte* unbekannt
242 Byte uint16Position Typenliste
262 Byte uint16Startposition Namen

Die Positionen sind gerechnet ab dem Anfang des Verzeichnisses. Als nächstes müssen wir uns die Typenliste vornehmen. Diese beginnt folgendermassen:

PostionGrösseTyp Wert
02 Byteuint16Anzahl Typen - 1

Wenn hier also eine 2 steht, so haben wir 3 Typen abgelegt. Es folgt nun eine Liste mit folgendem Eintrag für jeden Typ:

PostionGrösseTyp Wert
04 Bytechar Typ
42 Byteuint16Anzahl Blöcke - 1
62 Byteuint16Position Blockliste

Die Position ist gerechnet ab dem Anfang der Typenliste. Der Typ, den wir suchen, ist mit dem ASCII-String „sfnt“ gekennzeichnet. Die Blöcke dieses Typs sind vollständige, eigenständige TrueType Schriften.

Die Blockliste beginnt direkt mit der eigentlichen Liste. Für jeden Block (meistens 4) ist folgender Eintrag hinterlegt:

PostionGrösseTyp Wert
02 Byte* unbekannt
22 Byteuint16Position Namen
41 Byte* unbekannt
53 Byteuint24Position Block
84 Byte* unbekannt

Es muss nicht unbedingt ein Blockname abgelegt sein. Wenn dieser fehlt, so ist als Position 65535 abgelegt. Ansonsten ist die Position gerechnet ab der Startposition Namen. Wir brauchen den Namen nicht wirklich, aber wer will, kann ihn auslesen. Es handelt sich um einen String im Pascalformat. Das heisst, das erste Byte ist ein uint8 mit der Länge in Byte (ohne dieses erste Byte), und der Rest ist der eigentliche String. Die Kodierung ist wahrscheinlich MacRoman.

Die Position des Blocks ist gerechnet ab der Startposition Blocks. An der beschriebenen Position steht ein uint32 mit der Länge des Blocks (ohne diese vier Byte). Darauf folgen die eigentlichen Blockdaten mit der TrueType Schrift.

reine Apple Schriften

TrueType Schriften, welche nur den Anforderungen von Apple genügen (aber nicht jenen von Microsoft), unterscheiden sich in den Blöcken „name“, „OS/2“ und „cmap“, von denjenigen, die wir bisher behandelt haben. Im Falle von „name“ und „OS/2“ ist dies lediglich ärgerlich, aber im Falle von „cmap“ haben wir ein echtes Problem. Laut PDF Standard wird nämlich ein Unterblock mit Plattform-ID 3 und Format 4 erwartet, der eine Tabelle von Unicode auf die GIDs enthält. Dieser Unterblock fehlt in der Regel.

Aus Kompatibilitätsgründen haben aber Schriften, die für lateinischen Schriftsatz geeignet sind, einen Unterblock mit Plattform 1 und Kodierung 0. Laut TrueType Standard definiert dieser Unterblock eine Tabelle von MacRoman auf die GIDs. Der Unterblock ist auch in symbolischen Schriften vorhanden, und definiert in diesem Fall eine Tabelle von der schrifteigenen Kodierung auf die GIDs. Laut PDF Standard wird dieser Unterblock als Fallback verwendet. Um Probleme zu vermeiden sollte aber die MacRoman Kodierung anstelle der „Windows westlich“ Kodierung verwendet werden. Ansonsten muss das PDF-Programm erst den Text nach MacRoman konvertieren, und da können subtile Differenzen zu unterschiedlichen Ausgaben führen.

Schriften, die für MacOS 8.5 oder neuer konzipiert wurden, enthalten in der Regel auch noch einen Unterblock für Unicode. Dieser hat aber andere Codes für Plattform und Kodierung, und oft auch ein anderes Format. Für PDF Programme, die nur die Unterblocks nach PDF Standard betrachten, ist dieser Unterblock daher nutzlos. Wir können ihn aber selber auslesen, und die Schrift über das Kompositschriftsystem einbinden. Bei diesem System stützt sich das PDF Programm bekanntlich auf unsere Angaben, statt auf den cmap Block.

Es gibt noch einen Punkt betreffend der Kompatibilität, den wir beachten sollten: Der MacRoman-Unterblock kann im Format 6 sein (siehe unten), und dieses Format wird von manchen PDF Programmen nicht verstanden. Will man solche Schriften verwenden, ist es besser, auf das Kompositschriftsystem auszuweichen.

Hier nun die konkreten Unterschiede, und wie wir an die Daten kommen:

der "name" Block

Grundlagen "name" Block

Das Format des Blocks ist identisch, aber die Codes im Verzeichnis, und die Zeichenkodierung der Texte sind unterschiedlich. Folgende Codes werden verwendet:

PlattformKodierungSpracheTypInhalt
1 0 0 1Schriftart
1 0 0 4Schriftname (OS)
1 * * 6Schriftname (Postscript)

Die Kodierung ist jeweils MacRoman (wobei für den Schriftnamen nach Postscript nur ASCII zulässig ist).

der "OS/2" Block

Dieser Block fehlt in der Regel, und wenn er vorhanden ist, ist er häufig in einem inkompatiblen (und für uns unbrauchbaren) Format. Dieses Format ist daran erkennbar, dass der Block nur 68 Byte lang ist.

Fehlt der Block, oder ist er im inkompatiblen Format, so dürfen wir annehmen, dass es keine Einschränkung für die Einbettung gibt, und für die Hoch- und Tiefstellung können wir einfach die Standardwerte verwenden. Was Oberlänge, Unterlänge, Zeilendurchschuss und Versalhöhe betrifft, so müssen wir diese statt dessen aus dem „hhea“ Block holen.

der "hhea" Block

Grundlagen "hhea" Block

Dieser Block ist identisch. Er enthält aber einige Informationen, die wir bisher ignoriert haben.

PositionGrösseTyp Wert
42 Byteint16 Oberlänge (Apple)
62 Byteint16 Unterlänge (Apple)
82 Byteint16 Zeilendurchschuss (Apple)
342 Byteuint16Anzahl GIDs mit expliziter Zeichenbreite

Die Oberlänge und Unterlänge haben dasselbe Format wie jene im „OS/2“ Block. Der Unterschied ist, dass hier die Apple-Definition verwendet wird. Im Unterschied zur Adobe-Definition erlaubt diese keine oben oder unten überhängenden Zeichen, was den Gestaltungsspielraum des Designers etwas einschränkt. Darum sollte man wenn möglich die Werte aus dem „OS/2“ Block verwenden, kann aber wenn nötig auf jene aus dem „hhea“ Block ausweichen.

Die Oberlänge kann als Versalhöhe eingesetzt werden (so wie bei „OS/2“ Blöcken ohne Versalhöhe).

Der Zeilendurchschuss ist ein wenig anders definiert, als bei Adobe. Um den Zeilenabstand zu bekommen, rechnet man folgendermassen:

Zeilenabstand = Zeilendurchschuss + Oberlänge - Unterlänge

Nicht vergessen: Die Unterlänge ist ein negativer Wert, und vergrössert somit den Zeilenabstand.

der "cmap" Block

Grundlagen "cmap" Block
Unterblock Format 12

Der Block an sich ist im selben Format. Es werden aber andere Codes im Verzeichnis verwendet, und die Unterblocks können in anderen Formaten sein. Folgende Codes werden verwendet:

Kodierung 0 0 - 4
PlattformTyp
1MacRoman / symbolische Schrift
0Unicode

Der Unterblock für MacRoman bzw. symbolische Schriften ist in einem der Formate 0, 4 oder 6.

Der Unterblock für Unicode ist in einem der Formate 4, 6, 8, 10 oder 12. Es können mehrere Unterblocks vorhanden sein. In diesem Fall wählt man am Besten jenen mit der grössten Formatnummer.

Die Formate 4 und 12 kennen wir schon. Hier die Rest:

Format 0 Unterblock

Dieser Unterblock kann nur Zeichen aus 8-Bit Kodierungen aufnehmen. Er beginnt folgendermassen:

PositionGrösseTyp Inhalt
02 Byteuint16Format (0)
22 Byteuint16Grösse
42 Byteuint16Sprache

Darauf folgt ein Eintrag für jeden der möglichen Zeichencodes von MacRoman bzw. der symbolischen Schrift von 0 bis 255:

PositionGrösseTyp Inhalt
01 Byteuint8GID

Format 6 Unterblock

Dieser Unterblock kann, ähnlich wie der Format 4 Unterblock, Unicode-Zeichen bis 65535 aufnehmen. Vom Aufbau her ähnelt er aber eher dem Format 0 Unterblock. Er beginnt folgendermassen:

PositionGrösseTyp Inhalt
02 Byteuint16Format (6)
22 Byteuint16Grösse
42 Byteuint16Sprache
62 Byteuint16Startcode
82 Byteuint16Anzahl Zeichen

Darauf folgt ein Liste von „Anzahl Zeichen“ Länge, welches die möglichen Unicode-Nummern vom Startcode an hochgezählt abdeckt:

PositionGrösseTyp Inhalt
02 Byteuint16GID

Format 8 Unterblock

Dieser Unterblock wurde spezifisch für UTF-16 mit Surrogaten definiert. Er ist mir in freier Wildbahn noch nie begegnet, aber man kann ja nie wissen. Der Block ähnelt dem Format 12 Unterblock, und beginnt folgendermassen:

PositionGrösse Typ Inhalt
02 Byte uint16Format (8)
22 Byte uint16unbenutzt
44 Byte uint32Grösse
84 Byte uint32Sprache
128192 Byte* Codeflags
82044 Byte uint32Anzahl Segmente

Die Codeflags definieren, welche Codes ein Surrogat-Paar einleiten. Für uns sind sie uninteressant.

Darauf folgt eine Liste mit folgendem Eintrag für jedes Segment:

PositionGrösseTyp Inhalt
04 Byteuint32Startcode
44 Byteuint32Endcode
84 Byteuint32erste GID

Der Startcode erhält die „erste GID“, für die restlichen Codes wird die GID hochgezählt.

Codes mit Wert von 0 bis 65535 sind Unicode-Nummern. Grössere Codes sind Strings in der Kodierung UTF-16BE. Um aus ihnen die Unicode-Nummer zu ermitteln, kann man arithmetisch so vorgehen:

unicode = (floor(utf16 / 0x10000) modulo 0xd800) * 0x400 + (utf16 modulo 0x10000 modulo 0xdc00) + 0x10000

Etwas einfacher geht es mit bitweisen Operatoren:

unicode = (((utf16 >> 16) & 0x3ff) << 10) + (utf16 & 0x3ff) + 0x10000

Format 10 Unterblock

Dieser Unterblock kann, ähnlich wie der Format 12 Unterblock, den vollen Zeichenumfang von Unicode aufnehmen. Er ähnelt dem Format 0 oder Format 6 Unterblock, und beginnt folgendermassen:

PositionGrösseTyp Inhalt
02 Byteuint16Format (10)
22 Byteuint16unbenutzt
44 Byteuint32Grösse
84 Byteuint32Startcode
124 Byteuint32Anzahl Zeichen

Darauf folgt ein Liste von „Anzahl Zeichen“ Länge, welches die möglichen Unicode-Nummern vom Startcode an hochgezählt abdeckt:

PositionGrösseTyp Inhalt
02 Byteuint16GID