Informationen über Unterschneidung finden sich beim Schriftfeaturesystem in einem „GPOS“ Block. Dieser Block kann noch andere Informationen zur Zeichenplatzierung enthalten. Die Strukturen sind darum allgemeiner ausgerichtet, als dies im „kern“ Block der Fall ist.
Der „GPOS“ Block beginnt immer mit folgenden Angaben:
Position | Grösse | Typ | Inhalt |
---|---|---|---|
0 | 4 Byte | uint32 | Version |
4 | 2 Byte | uint16 | Position Schrift/Sprachtabelle |
6 | 2 Byte | uint16 | Position Featuretabelle |
8 | 2 Byte | uint16 | Position Lookuptabelle |
Damit lassen sich mit dem unter Das Schriftfeaturesystem erwähnten Verfahren die Position des Unterblocks bzw. der Unterblöcke ermitteln. Die gesuchte Feature-ID ist dabei „kern“.
Der Inhalt der Unterblocks hängt von der Typnummer im Lookupeintrag ab. Für „kern“ sind die Nummern 2, 8 und 9 erlaubt.
2 steht für normale Unterschneidung.
8 steht für kontextabhängige Unterschneidung. Das ist eine recht komplexe und selten gebrauchte Art der Unterschneidung, bei der die Buchstaben vor und nach dem Buchstabenpaar eine Rolle spielen. Ich gehe hier nicht darauf ein.
9 ist ein Spezialfall. Dieser Unterblock wird verwendet, wenn der uint16 im Lookupeintrag nicht ausreicht, um die eigentliche Position des Unterblocks abzubilden (bei grossen „GPOS“ Blöcken).
Dieser Unterblock verweist lediglich auf den eigentlichen Unterblock. Er ist folgendermassen aufgebaut:
Position | Grösse | Typ | Inhalt |
---|---|---|---|
0 | 2 Byte | uint16 | Version |
2 | 2 Byte | uint16 | Typ |
4 | 4 Byte | uint32 | Position |
Die Position ist ab dem Anfang des Unterblocks gerechnet.
Dieser Unterblock enthält die Unterschneidungsinformationen. Dabei ist er flexibler als der „kern“ Block (zumindest flexibler als der allgemein unterstützte Minimalstandard des „kern“ Blocks).
Zum einen unterstützt er neben der normalen Unterschneidung auf Basis von Zeichenpaaren auch solche auf Basis von Zeichenklassenpaaren. Dabei werden die Zeichen in Zeichenklassen eingeteilt, und dann Unterschneidungswerte festgelegt für Kombinationen von bestimmten Zeichenklassen. Dieser Wert gilt dann für alle Kombinationen eines Zeichens aus der linken Klasse mit einem Zeichen aus der rechten Klasse. Auf diese Weise lässt sich Unterschneidung für grosse Zeichensätze definieren, wo die Kombinationsmöglichkeiten sonst schnell in die Milliarden gehen würden.
Zum anderen ist es möglich, mehrere Unterblocks zu definieren. Die Werte der Unterblocks wirken dabei kumulativ. Das heisst, falls sich für ein Zeichenpaar in mehreren Unterblocks Unterschneidungswerte ergeben, so müssen diese addiert werden. Dies wird vor allem in Zusammenhang mit Zeichenklassen verwendet.
Je nachdem, ob die Unterschneidung auf Zeichenpaaren oder Zeichenklassenpaaren basiert, wird mit einem anderen Format gearbeitet. Die Formatnummer ist in einem uint16 am Anfang des Unterblocks festgehalten.
Dieses Format kommt für einfache Unterschneidung auf Basis von Zeichenpaaren zur Anwendung. Der Unterblock beginnt folgendermassen:
Position | Grösse | Typ | Inhalt |
---|---|---|---|
0 | 2 Byte | uint16 | Format (immer 1) |
2 | 2 Byte | uint16 | Position Zeichenliste |
4 | 2 Byte | uint16 | Werteformat 1 |
6 | 2 Byte | uint16 | Werteformat 2 |
8 | 2 Byte | uint16 | Anzahl Paarlisten |
Die Zeichenliste enthält eine Liste der möglichen Zeichen links. Die Position ist ab dem Beginn des Unterblocks gerechnet.
Die beiden Einträge für das Werteformat bestimmen, wie die Werteeinträge aufgebaut sind. Für klassische Unterschneidung muss das Werteformat 1 auf 4 oder 68 stehen, das Werteformat 2 auf 0. Steht etwas anderes, haben wir es wahrscheinlich mit komplexer Unterschneidung zu tun, welche Vertikalversatz enthält. Ähnlich wie bei kontextabhängiger Unterschneidung ist auch dies selten gebraucht und mühsam umzusetzen, weshalb ich nicht darauf eingehe.
Nun kommen wir zu den Paarlisten. Für jedes mögliche Zeichen links gibt es eine Paarliste, welche die dazu passenden möglichen Zeichen rechts und den jeweiligen Unterschneidungswert enthält. Das heisst, die Anzahl Paarlisten sollte identisch sein mit der Anzahl Zeichen in der Zeichenliste.
Auf die erwähnten Angaben folgt zunächst für jeder Paarliste folgender Eintrag:
Position | Grösse | Typ | Inhalt |
---|---|---|---|
0 | 2 Byte | uint16 | Position Paarliste |
Die Position ist jeweils ab dem Anfang des Unterblocks gerechnet. An der Position findet sich dann die eigentliche Liste. Sie beginnt folgendermassen:
Position | Grösse | Typ | Inhalt |
---|---|---|---|
0 | 2 Byte | uint16 | Anzahl Einträge |
Daraufhin folgen die Einträge. Ist das Werteformat 1 auf 4 gesetzt, sehen sie folgendermassen aus:
Position | Grösse | Typ | Inhalt |
---|---|---|---|
0 | 2 Byte | uint16 | GID rechts |
2 | 2 Byte | int16 | Unterschneidung |
Ist das Werteformat 1 auf 68 gesetzt, sehen die Einträge so aus:
Position | Grösse | Typ | Inhalt |
---|---|---|---|
0 | 2 Byte | uint16 | GID rechts |
2 | 2 Byte | int16 | Unterschneidung |
4 | 2 Byte | uint16 | Position Hintingtabelle |
Die Unterschneidung muss, wie im „kern“ Block, in Promille der Schriftgrösse umgerechnet, und negiert werden, damit die Zahl für den Einsatz in PDF brauchbar ist.
Die Hintingtabelle ist für dein Einsatz in PDF uninteressant. Wir müssen den Eintrag aber natürlich überlesen.
Wie man sieht, ist die GID links nicht explizit aufgeführt. Sie ergibt sich aus der Zeichenliste. Die erste Paarliste deckt das erste Zeichen der Zeichenliste ab, die zweite Paarliste das zweite Zeichen der Zeichenliste, und so weiter.
Dieses Format kommt für Unterschneidung auf der Basis von Zeichenklassen zur Anwendung. Es ist folglich ein klein wenig komplexer. Der Unterblock beginnt folgendermassen:
Position | Grösse | Typ | Inhalt |
---|---|---|---|
0 | 2 Byte | uint16 | Format (immer 2) |
2 | 2 Byte | uint16 | Position Zeichenliste |
4 | 2 Byte | uint16 | Werteformat 1 |
6 | 2 Byte | uint16 | Werteformat 2 |
8 | 2 Byte | uint16 | Position Klassendefinition links |
10 | 2 Byte | uint16 | Position Klassendefinition rechts |
12 | 2 Byte | uint16 | Anzahl Klassen links |
14 | 2 Byte | uint16 | Anzahl Klassen rechts |
Für die Zeichenliste und die Werteformate gilt hier grundsätzlich das gleiche, wie im Format 1. Allerdings benötigen wir die Zeichenliste nicht, da die Einträge hier nicht danach geordnet sind.
Die Positionen der Klassendefinitionen sind ab dem Beginn des Unterblocks gerechnet. Ähnlich wie bei den Zeichenlisten gibt es auch bei der Klassendefinition zwei Formate, die sich anhand eines uint16 am Anfang unterscheiden lassen:
Dieses Format beginnt folgendermassen:
Position | Grösse | Typ | Inhalt |
---|---|---|---|
0 | 2 Byte | uint16 | Format (immer 1) |
2 | 2 Byte | uint16 | erste GID |
4 | 2 Byte | uint16 | Anzahl GIDs |
Danach folgt für jede GID ein Eintrag:
Position | Grösse | Typ | Inhalt |
---|---|---|---|
0 | 2 Byte | uint16 | Klassennummer |
Der erste Eintrag steht dabei für die „erste GID“, der nächste für die erste GID plus 1, und so weiter. Alle nicht aufgeführten GIDs sind in der Klasse 0.
Dieses Format beginnt folgendermassen:
Position | Grösse | Typ | Inhalt |
---|---|---|---|
0 | 2 Byte | uint16 | Format (immer 2) |
2 | 2 Byte | uint16 | Anzahl GID-Bereiche |
Danach folgt für jeden GID-Bereich ein Eintrag:
Position | Grösse | Typ | Inhalt |
---|---|---|---|
0 | 2 Byte | uint16 | erste GID |
2 | 2 Byte | uint16 | letzte GID |
4 | 2 Byte | uint16 | Klassennummer |
Alle GIDs von der ersten bis und mit der letzten GID werden dabei der jeweiligen Klassennummer zugeteilt. Die Bereiche dürfen sich dabei nicht überschneiden (ein Zeichen ist immer in genau einer Klasse). Alle GIDs, die in keinem der aufgeführten Bereiche sind, werden der Klasse 0 zugeteilt.
Nachdem wir nun wissen, welche Zeichen in welcher Klasse sind, können wir die Klassenpaare auslesen. Die „Anzahl Klassen links“ und „Anzahl Klassen rechts“ vom Anfang des Blocks sollte dabei mit der jeweiligen Anzahl definierter Klassen übereinstimmen (einschliesslich der Klasse 0).
Die Tabelle beginnt direkt nach den Angaben am Beginn des Unterblocks (also an Position 16 des Unterblocks). Dabei hat es nacheinander einen Eintrag für jede mögliche Kombination der Klassen links und rechts in folgender Reihenfolge: Zunächst je ein Eintrag für die Kombination der Klasse 0 links mit jeder der Klassen rechts von 0 an aufsteigend. Dann je ein Eintrag für die Klasse 1 links mit jeder der Klassen rechts, und so weiter.
Die Einträge sind wiederum vom Werteformat 1 abhängig. Bei Format 4 sehen sie folgendermassen aus:
Position | Grösse | Typ | Inhalt |
---|---|---|---|
0 | 2 Byte | int16 | Unterschneidung |
Bei Format 68 folgendermassen:
Position | Grösse | Typ | Inhalt |
---|---|---|---|
0 | 2 Byte | int16 | Unterschneidung |
2 | 2 Byte | uint16 | Position Hintingtabelle |
Auch hier muss die Unterschneidung in Promille der Schriftgrösse umgerechnet sowie negiert werden, und auch hier können wir die Hintingtabelle ignorieren.
Diskussion