Technischer Artikel

Vertikaler japanischer und chinesischer Text in PDF mit Delphi

PDFlibPas zeichnet japanischen und chinesischen Text von oben nach unten auf die Seite. SetVerticalWritingMode schaltet vertikales Schreiben ein, gewöhnliche DrawText-Aufrufe laufen daraufhin nach unten, und GetVerticalWritingMode meldet den aktuellen Zustand. Bevor es das gab, bedeutete vertikales Textsetzen, jedes Zeichen per Hand zu platzieren und zu hoffen, dass der Abstand stimmig wirkte

Vertikales Schreiben ist kein um neunzig Grad gedrehter horizontaler Text. Die Zeichen bleiben aufrecht, die Vorwärtsbewegung läuft nach unten statt quer, und eine Reihe von Zeichen ändert ihre Form vollständig — und das ist der Teil, der ein Dokument, das sich natürlich liest, von einem trennt, das ein japanischer Leser sofort als maschinell erzeugt erkennt

Was sich innerhalb des PDF ändert

Auf diese Weise gezeichneter Text läuft über eine Type0-Schrift im vertikalen Schreibmodus, die die vertikalen Metriken der Schrift selbst mitführt. Das ist in zwei Richtungen von Bedeutung. Ein Reader schreitet jedes Zeichen um jene Distanz fort, die sein Designer vorgesehen hat, statt um einen einheitlichen Schritt, sodass die Spalte den Rhythmus aufweist, für den die Schrift gezeichnet wurde. Und das Herauskopieren des Textes liefert die ursprünglichen Zeichen zurück, weil der vertikale Lauf nach wie vor echter Text mit korrektem Mapping ist statt eine Folge positionierter Glyphen

Eine Schrift, die keine eigenen vertikalen Metriken mitführt, schreitet pro Zeichen um ein Em fort, was auch ein Reader mit dem Standard tun würde. Diesen Fallback zu kennen lohnt sich, denn er ist genau der Unterschied, der sichtbar wird, wenn ein Dokument mit einer korrekten CJK-Schrift richtig rendert und mit einer lateinischen Schrift, die zufällig einige Kana enthält, mechanisch abständiert aussieht

var
  Lib: TPDFlib;
  H: Double;
begin
  Lib := TPDFlib.Create;
  try
    Lib.SetOrigin(1);
    Lib.AddTrueTypeFont('MS Mincho', 1);      // 1 = embed the face
    Lib.SetTextSize(12);
    Lib.SetVerticalWritingMode(1);            // ordinary DrawText now runs down
    H := Lib.GetVerticalTextHeight('MS Mincho', 12, '第三章 保守点検');
    Lib.DrawText(480, 72, '第三章 保守点検');
    Lib.SetVerticalWritingMode(0);            // back to horizontal
    Lib.DrawText(72, 72 + H, 'Chapter 3');
    Lib.SaveToFile('manual-ja.pdf');
  finally
    Lib.Free;
  end;
end;

Warum wirken Klammern in vertikalem Text falsch?

Weil eine Klammer zwei Formen hat und nur eine davon in eine Spalte gehört. Klammern, das lange Vokalzeichen und die kleinen Kana werden anders gezeichnet, wenn Text nach unten läuft — eine runde Klammer dreht sich so, dass sie entlang der Spalte liegt statt quer dazu, und das lange Vokalzeichen wird zu einem senkrechten Strich. Zeichnet man die horizontalen Formen in eine vertikale Spalte, liegt jedes einzelne davon auf der Seite

PDFlibPas nimmt die Formen aus dem vertical-Feature der Schrift selbst, sodass jede Schrift das liefert, was ihr Designer gezeichnet hat, statt einer Substitution, die aus dem Zeichen erraten wurde. Diese Unterscheidung ist für die Korrektheit wichtig: Eine erratene Substitutionstabelle liegt bei den häufigen Fällen richtig und bei den Schriften falsch, die ein Zeichen anders behandeln, und eine Schrift, die keine vertikalen Formen benennt, wird exakt wie bisher gezeichnet statt durch eine Tabelle gezwungen, die sie nie verlangt hat

GetVerticalTextHeight misst die Formen, die tatsächlich gezeichnet werden, sodass eine Spalte, deren Zeichen die Form ändern, trotzdem korrekt gemessen wird. Die horizontalen Formen zu messen und die vertikalen zu zeichnen ist die klassische Quelle für Spalten, die um einige Zeichen über ihren Rahmen hinauslaufen

Einen Lauf zeichnen, ohne den Modus zu wechseln

DrawVerticalText zeichnet einen einzelnen Lauf vertikal, nimmt Position, Schriftname, Größe und Text entgegen und belässt den Schreibmodus, wie er ist. Einsetzen lässt sich das für die vertikale Ausnahme innerhalb eines horizontalen Dokuments — ein Rücken-Etikett, ein Stempel, eine einzelne Namensspalte —, wo das Umschalten eines globalen Modus um jeden Aufruf herum mehr Zustand ist, als die Aufgabe verdient

Die horizontalen und vertikalen Formen einer Schrift werden intern getrennt gehalten, sodass eine Seite beide führen kann, ohne dass eine die andere stört. Das ist es, was eine gemischte Seite praktikabel macht: eine japanische Buchseite mit vertikalem Fließtext und horizontalen Kopfzeilen oder ein chinesisches Zertifikat mit einem vertikalen Titel über horizontalen Detailzeilen

// One vertical run inside an otherwise horizontal page
Lib.DrawVerticalText(520, 96, 'MS Mincho', 14, '保守点検記録');
// The horizontal text around it is unaffected
Lib.DrawText(72, 96, 'Maintenance inspection record');

Die Schrift vor allem anderen in Ordnung bringen

Vertikales Schreiben hängt vollständig von der Schrift ab. Eine CJK-Schrift mit korrekten vertikalen Metriken und einem vertical-Feature erzeugt ohne weitere Arbeit eine korrekte Ausgabe; eine Schrift ohne diese erzeugt aufrechte Zeichen, die sich jeweils um ein Em fortbewegen, ohne jede Formänderung. Wenn vertikaler Text subtil falsch wirkt, sollte man die Schrift prüfen, bevor man den Code prüft

Das Einbetten folgt den gewohnten Regeln und Kosten. Eine vollständige CJK-Schrift ist groß, sodass Subsetting für Dokumente, die irgendwohin gehen, nicht optional ist — die Hinweise zur PDF-Dateigrößenoptimierung und zum Font-Subsetting behandeln, was zu erwarten ist, und die Erläuterung zum Einbetten fehlender Schriften in ein bestehendes PDF behandelt den Reparaturfall, in dem ein vertikales Dokument ohne seine Schriften ankommt

Wo vertikaler Text weiterhin eine Layout-Entscheidung verlangt

Die Spaltenreihenfolge. Japanischer vertikaler Text läuft von Spalte zu Spalte von rechts nach links, sodass eine zweispaltige Seite am rechten Rand beginnt, und keine Schreibmodus-Einstellung kann das aus dem Text erschließen. Dasselbe gilt für die Seitenreihenfolge in einem Dokument, das als Ganzes von rechts nach links liest, und dafür, wo Furigana, Fußnoten und Abbildungsunterschriften sitzen

Was die Bibliothek garantiert, ist, dass jeder Lauf korrekt gesetzt wird: korrekte Formen, korrekte Vorwärtsbewegungen, extrahierbarer Text. Wohin die Läufe auf der Seite gelangen, ist ein Layout-Problem, und für Dokumente, die aus Daten zusammengestellt werden, ist die Erläuterung zur Textsuche und Seitenelement-Aufzählung nützlich, um danach zu verifizieren, dass das, was auf der Seite gelandet ist, dem entspricht, was beabsichtigt war

PDFlibPas ist eine native Pascal-PDF-Bibliothek für Delphi, C++Builder und Lazarus, und vertikales CJK-Schreiben ist Teil der Zeichen-API statt eines Add-ons — siehe die PDFlibPas-Produktseite für die Text- und Schriftfunktionsliste