OpenType CID

CID Schriften sind im Grund lediglich ein Spezialfall der Type1 Schriften. Die OpenType Datei verwendet ebenfalls „OTTO“ als Signatur, die Zeichenbeschreibungen sind ebenfalls in einem „CFF “ Block, und das Format der Zeichenbeschreibungen ist ebenfall identisch. Nur der Zugriff auf die Zeichenbeschreibungen funktioniert anders.

Konkret kann auf die Zeichen in einer CID Schrift nur über einen 2-Byte Code zugegriffen werden. Darum ist es nicht möglich, die Schriften über das einfache Schriftsystem zu verwenden. Desweiteren ist der /CIDToGIDMap Eintrag nur bei TrueType Schriften erlaubt, weswegen wir auch die Unicode-Methode nicht verwenden können. Die Direktzugriffsmethode hingegen funktioniert einwandfrei.

Mehr braucht man im Grunde nicht zu wissen. Der Rest dieses Kapitels lohnt sich aber trotzdem, wenn man mit CID Schriften arbeiten will. Gewisse Missverständnisse und Probleme lassen sich so vermeiden.

das CID System

Das System stammt aus einer Zeit, als TrueType brandneu und Unicode noch in den Kinderschuhen war. Es war Adobes Versuch, das Problem mit Schriften zu lösen, die eine grosse Anzahl Zeichen enthalten sollten. Die Grundidee war, dass von der im jeweiligen Land üblichen Kodierung in eine von mehreren Standardkodierungen, und von dort über eine schriftinterne Tabelle in die GID umgewandelt werden sollte. Für die Standardkodierungen gab Adobe eine Auswahl vor. Zudem konnte jeder eine eigene Kodierung entwerfen, solange er eine passende Konversionsdefinition von einem landesüblichen Zeichensatz in die eigene Kodierung veröffentlichte. Die Zwischenkodierungen nannte man Character Collection, die einzelnen Einträge darin Character-ID oder kurz CID. Daher der Name des Systems.

Bei OpenType CID sind Überbleibsel davon erhalten geblieben. Der cmap-Block ermittelt bei diesen Schriften eigentlich nicht die GID, sondern die CID zu den Unicode-Nummern. Die Tabelle für die eigentlichen GIDs ist im CFF-Block. Das ist für uns aber kein Problem: Die anderen Blocks (wie z.B. der hmtx-Block) arbeiten ebenfalls mit der CID, und das /W Array benötigt die Schriftbreiten bei CID Schriften nach CID sortiert. Kurz: Dort, wo wir bei anderen Schriften eine GID erhalten, bekommen wir statt dessen eine CID, und dort, wo wir sonst eine GID benötigen, brauchen wir statt dessen die CID. Für uns ändert sich rein gar nichts. Nur der PDF Interpreter muss den Unterschied kennen.

CIDSystemInfo

Dieser Eintrag gibt an, welche Zwischenkodierung (Character Collection) verwendet wird. Es ist immer ein Dictionary mit den Einträgen /Registry, /Ordering, und /Supplement, und wird daher auch als ROS abgekürzt. In unserem Fall haben wir die Werte dieses Dictionaries immer auf Adobe-Identity-0 gesetzt. Das ist ein Standardwert, welcher bedeutet, dass die Zwischenkodierung unbekannt ist. TrueType und Type1 haben keine Zwischenkodierung, folglich ist dieser Wert immer korrekt. Viele moderne CID Schriften verwenden ebenfalls keine Zwischenkodierung mehr (bzw. diese ist identisch mit den GIDs), wodurch dieser Eintrag ebenfalls korrekt ist. Manche stützen sich aber noch auf eine der klassischen Kodierungen.

Gemäss PDF Standard sollten wir die richtige Kodierung angeben. Es ist nicht zwingend nötig, aber guter Stil, und erspart uns unter Umständen Probleme mit mancher Software. Die Information ist innerhalb des CFF-Blocks abgelegt. Type1 Schriften haben diese Information natürlich nicht. Somit lassen sich auch Type1 und CID Schriften voneinander unterscheiden.

der "CFF " Block

Dieser Block enthält, wie schon erwähnt, eine gültige Datei im CFF Format. Es handelt sich dabei im Grunde um eine modernisierte Variante des PFB Formats. CFF wurde aber auf möglichst geringen Platzverbrauch getrimmt. Es ist ein rein binäres Format (mit Big-Endian Anordnung), und leider nicht ganz einfach zu dekodieren. Folgende Datentypen kommen zur Anwendung:

char String mit 8-Bit Kodierung
uint8 vorzeichenlose 8-Bit Zahl
int16 vorzeichenbehaftete 16-Bit Zahl
uint16vorzeichenlose 16-Bit Zahl
uint24vorzeichenlose 24-Bit Zahl
int32 vorzeichenbehaftete 32-Bit Zahl
uint32vorzeichenlose 32-Bit Zahl

Dateiaufbau

Der Einfachheit halber betrachte ich im Folgenden den CFF-Block als eigenständige Datei. Diese besteht aus mehreren Blocks, wovon uns die ersten vier interessieren:

  1. der Dateiheader
  2. der Namenindex
  3. der Top-Dict Index
  4. der Stringindex

der Dateiheader

Von diesem Block interessiert uns eigentlich nur seine Länge, damit wir den nächsten Block finden können. Der Header kann laut Standard theoretisch unterschiedlich aufgebaut sein, aber die ersten drei Einträge lauten immer wie folgt:

PositionLänge Typ Inhalt
01 Byteuint8Version
11 Byteuint8Unterversion
21 Byteuint8Blocklänge

Die Blocklänge ist einschliesslich dieser 3 Bytes. Sie ist gleichzeitig die Position des Namenindex, wenn man das erste Byte der Datei als Position 0 betrachtet.

das Indexformat

Die drei folgenden Blocks haben alle grundsätzlich den gleichen Aufbau:

PositionLänge Typ Inhalt
02 Byteuint16Anzahl Einträge
21 Byteuint8 Positionsgrösse
3* * Positionenliste
** * Datenliste

Ein Index kann ein oder mehrere Einträge haben. Wieviele es sind, ist unter „Anzahl Einträge“ vermerkt. Die „Positionsgrösse“ ist ein Wert von 1 bis 4, und gibt an, wieviele Bytes jeder Eintrag in der Positionenliste hat. Die Datenliste enthält die eigentlichen Daten der Einträge, die Positionenliste die Position der Einträge innerhalb der Datenliste.

Die Zahlen in der Positionenliste sind je nach Positionsgrösse im Format uint8, uint16, uint24 oder uint32. Es hat dabei eine Position mehr, als Dateneinträge. Diese letzte Position zeigt auf das erste Byte nach der Datenliste. Das ist gleichzeitig das erste Byte des nächsten Blocks. Alle Positionen sind ab dem letzten Byte der Positionenliste gerechnet. Das erste Byte der Datenliste hat somit immer die Position 1.

Die Einträge in der Datenliste stehen direkt hintereinander. Ihre Länge lässt sich folglich aus ihrer Position und der nachfolgenden Position berechnen.

der Namenindex

Dieser Block ist in bei OpenType immer ein Index mit genau einem Eintrag. Dieser Eintrag ist der Postscript-Name der Schrift in Latin-1 Kodierung. Wir sollten den Schriftnamen schon aus dem name-Block kennen, brauchen diesen Block also eigentlich nicht.

der Top-Dict Index

Auch dieser Block ist bei OpenType immer ein Index mit genau einem Eintrag. Der Inhalt dieses Eintrags ist in diesem Fall ein CFF Dictionary. Ähnlich wie die PDF Dictionaries dient auch dieses dazu, gewissen Schlüsseln gewisse Werte zuzuordnen. Anders als bei den PDF Dictionaries sind Schlüssel und Werte aber alles Zahlen in verschiedenen Kodierungen, die Werte werden vor den Schlüsseln geschrieben, und es ist möglich, einem Schlüssel mehrere Werte zuzuordnen.

Wert- und Schlüsselkodierung

Die Werte und Schlüssel sind direkt hintereinander geschrieben. Ob es sich jeweils um einen Wert oder einen Schlüssel handelt, und welche Kodierung verwendet wird, ergibt sich jeweils aus dem ersten Byte. Dieses sollte als uint8 gelesen werden.

Hat das erste Byte einen Wert im Bereich 1 - 11 oder 13 - 21, so ist es ein einfacher Schlüssel. Er besteht nur aus diesem einen Byte.

Hat das erste Byte den Wert 12 so ist es ein erweiterter Schlüssel. Dieser besteht aus diesem und dem nächsten Byte. Das zweite Byte ist ein uint8, und kann einen beliebigen Wert haben.

Hat das erste Byte den Wert 28, so handelt es sich um einen int16 Wert. Dieser ist in den nächsten 2 Bytes abgelegt.

Hat das erste Byte den Wert 29, so handelt es sich um einen int32 Wert. Dieser ist in den nächsten 4 Bytes abgelegt.

Hat das erste Byte den Wert 30, so handelt es sich um einen Dezimalbruchwert. Der Wert ist in den folgenden Bytes in BCD-Notation abgelegt. Wir brauchen das nicht zu dekodieren, aber wir müssen das letzte Byte finden. Dieses (und nur dieses) hat im niederwertigen Halbbyte den Wert 15 abgelegt. Wir müssen also ein Byte suchen, welches als uint8 gelesen und modulo 16 gerechnet den Wert 15 ergibt.

Hat das erste Byte einen Wert im Bereich 32 - 246, so handelt es sich um einen einfachen Wert. Er besteht nur aus diesem Byte. Um den eigentlichen Wert zu erhalten, zieht man von der Zahl 139 ab, so dass man eine Zahl im Bereich -107 - 107 erhält.

Hat das erste Byte einen Wert im Bereich 247 - 250, so handelt es sich um einen erweiterten, positiven Wert. Er besteht aus diesem und dem nachfolgenden Byte. Ist B1 das erste Byte, und B2 das nachfolgende Byte, so errechnet sich der eigentliche Wert wie folgt:

(B1 - 247) * 256 + B2 + 108

Dies ergibt einen Wert von 108 - 1131.

Hat das erste Byte einen Wert im Bereich 251 - 254, so handelt es sich um einen erweiterten, negativen Wert. Er besteht aus diesem und dem nachfolgenden Byte. Der eigentliche Wert errechnet sich wie folgt:

0 - ((B1 - 251) * 256 + B2 + 108)

Dies ergibt einen Wert von -1131 - -108.

Registry, Ordering und Supplement

Mit dieser Information können wir nun die eigentlichen Werte und Schlüssel bestimmen. Insgesamt interessiert uns nur der Schlüssel 12/30. Diesem müssen drei Werte vorangehen, nämlich nacheinander eine Zahl für Registry, eine Zahl für Ordering und eine Zahl für Supplement. Die Zahlen für Registry und Ordering sind String-IDs. Mit diesen können wir den entsprechenden String im Stringindex suchen. Die Zahl für Supplement hingegen können wir so übernehmen. Normalerweise handelt es sich um eine Zahl von 0 bis 6.

Ist der Schlüssel 12/30 nicht vorhanden, so handelt es sich nicht um eine CID Schrift.

Beispiel:

<F8 1B F8 1C 91 0C 1E>

Diese Bytes jeweils als uint8 gelesen ergeben folgende Zahlen:

248 27 248 28 145 12 30

Gemäss der oben beschriebenen Formeln stehen die Zahlen 248 und 27 für den Wert 391, die Zahlen 248 und 28 für den Wert 392, die Zahl 145 für den Wert 6. Die Zahlen 12 und 30 ergeben den erweiterten Schlüssel. Wir haben also den gesuchten Schlüssel 12/30 mit drei Werten 391, 392 und 6 (was recht typische Werte sind).

der Stringindex

Den String-IDs 0 bis 390 sind im Standard fixe Strings zugeordnet. String-IDs ab 391 sind als Einträge im Stringindex abgelegt. Der erste Eintrag des Stringindexes ist somit der ID 391 zugeordnet, der zweite der ID 392, und so weiter. Die String sind jeweils Latin-1 kodiert.

Für Registry und Ordering werden immer String-IDs ab 391 verwendet. Dies, weil keine hierfür passenden Standardstrings definiert wurden. Die Registry ist normalerweise „Adobe“, das Ordering normalerweise einer der Strings „Identity“, „CNS1“, „GB1“, „Japan1“ oder „Korea1“.