「コード」 setzte man eine Seite hinunter, Zeichen für Zeichen gezeichnet, jeder Aufruf ein em tiefer als der vorherige, und drei der fünf Glyphen kommen falsch heraus. Die beiden Eckklammern sitzen in der Spalte so, wie sie auf einer horizontalen Linie sitzen, seitlich öffnend, und das lange Vokalzeichen ー ist ein flacher Strich, der quer durch die Spalte läuft, wo ein japanischer Leser einen kurzen vertikalen Strich erwartet. Nur コ und ド, die beiden gewöhnlichen Katakana, sehen so aus, wie ein Setzer sie setzen würde
Die ganze Zeile um neunzig Grad zu drehen repariert Klammern und langes Vokalzeichen mehr oder weniger zufällig und zerstört alles andere: Jedes Kanji und Kana liegt jetzt auf der Seite. Vertikales Schreiben tut drei Dinge gleichzeitig. Die Zeichen bleiben aufrecht, der Vorschub läuft die Seite hinunter statt quer über sie, und eine Handvoll Zeichen wechselt zu anderen Zeichnungen. Alle drei richtig zu bekommen braucht eine Schrift, die vertikale Formen und vertikale Metriken mitbringt, und eine PDF-Schrift, die darauf eingerichtet ist, sie zu verwenden
Vertikales Schreiben in PDF Library for Delphi einschalten
PDF Library for Delphi behandelt vertikales Schreiben als Zeichenmodus. SetVerticalWritingMode(1) schaltet ihn ein, jeder DrawText-Aufruf danach läuft ab dem angegebenen Punkt die Seite hinunter, und SetVerticalWritingMode(0) bringt horizontalen Text zurück. Der Setter gibt den Modus zurück, der vor dem Aufruf galt, und GetVerticalWritingMode liest den aktuellen, also kann eine Hilfsroutine den Modus umschalten und später exakt das wieder einsetzen, was sie vorfand
Die beiden Koordinaten bedeuten etwas anderes als bei horizontalem Text. Der x-Wert nennt die Mittellinie der Spalte: Jede Glyphe hängt mit ihrer horizontalen Mitte an dieser Linie, also bleiben ein schmales Satzzeichen und ein breites Kanji auf derselben Achse ausgerichtet. Der y-Wert ist die Oberkante der ersten Zeichenzelle, und die Spalte wächst von dort nach unten. Mit SetOrigin(1), das y = 0 an die obere Seitenkante legt, liest sich die Geometrie so, wie man die Seite auf Papier anordnen würde
var
Lib: TPDFlib;
H: Double;
begin
Lib := TPDFlib.Create;
try
Lib.SetOrigin(1);
Lib.AddTrueTypeFont('MS Mincho', 1); // 1 = die Schriftart einbetten
Lib.SetTextSize(12);
Lib.SetVerticalWritingMode(1); // gewöhnliches DrawText läuft nun nach unten
H := Lib.GetVerticalTextHeight('MS Mincho', 12, '第三章 保守点検');
Lib.DrawText(480, 72, '第三章 保守点検'); // die Spalte beginnt bei y = 72
Lib.SetVerticalWritingMode(0); // zurück zu horizontal
// Origin 1 legt y = 0 an den oberen Rand, also ist 72 + H der Fuß der Spalte;
// die horizontale Beschriftung sitzt eine Zeile darunter
Lib.DrawText(456, 72 + H + 18, 'Chapter 3');
Lib.SaveToFile('manual-ja.pdf');
finally
Lib.Free;
end;
end;
GetVerticalTextHeight nimmt den Schnitt-Namen, die Größe und den Text entgegen und liefert die Länge der Spalte in Punkten zurück. Im Beispiel ist 72 + H der Fuß der Spalte, und die englische Beschriftung geht 18 Punkte darunter, beginnend 24 Punkte links der Mittellinie der Spalte, sodass die kurze lateinische Zeile ungefähr unter der Spalte sitzt. Die Funktion gibt 0 zurück, wenn sie keinen Schnitt dieses Namens öffnen kann, und ein Layout, das auf einer Null-Höhe aufbaut, legt die Beschriftung auf die Spalte, also darauf testen, bevor man den Wert verwendet
Warum liegen Klammern seitlich in einer vertikalen Spalte?
Weil die Schrift zwei Zeichnungen eines jeden solchen Zeichens hält und die horizontale die Voreinstellung ist. OpenType-Schnitte listen die Alternativen in einem GSUB-Feature namens vert, oder vrt2 in Schnitten, die bereits für vertikalen Satz gedrehte Glyphen mitbringen. Eine Textzeile zu shapen liefert die horizontalen Glyphen, und irgend etwas muss das vertikale Feature nachträglich anwenden, eine Glyphe für eine Glyphe. Eckklammern, Parenthesen und das lange Vokalzeichen drehen sich, um der Spalte zu folgen; kleine Kana und Satzzeichen wie 、 und 。 wechseln in eine andere Ecke ihres Kästchens
PDF Library for Delphi liest dieses Feature aus dem Schnitt, mit dem gezeichnet wird, und tauscht jede Glyphe gegen die vertikale Form, die die Schrift nennt. Trägt ein Schnitt beide Features, gewinnt vrt2 und vert wird ignoriert. Die Spezifikation sieht vrt2 als Ersatz für vert, und beide zu lesen kann einem Zeichen zwei verschiedene vertikale Glyphen geben, je nachdem, welche Lookup zuerst antwortet. Ein Schnitt ohne beide Features behält seine gewöhnlichen Glyphen. Wir ziehen dieses Ergebnis einer eingebauten Substitutionstabelle vor: Eine geratene Tabelle trifft die häufigen Zeichen und überschreibt still die Schnitte, deren Entwürfe ein Zeichen anders behandelt haben
Wirft man einen Blick auf die GSUB-Tabellen der Kopien, die Windows mitbringt, liegen die Zähler weit auseinander: Das vertikale Feature von MS Gothic mappt 979 Glyphen, Yu Gothic 2.491, SimSun 72, und Arial gar keine (die exakten Zahlen wandern mit Font-Versionen). Ein lateinischer Schnitt, der zufällig etwas Kana enthält, zeichnet eine aufrechte Spalte mit jeder Klammer und jedem langen Vokalzeichen in horizontaler Form, weil es in der Schrift nichts zu substituieren gibt
Die Substitution ändert auch die Messung. Eine vertikale Form kann einen eigenen Vorschub haben, verschieden von der horizontalen Glyphe, die sie ersetzt, also wendet GetVerticalTextHeight dieselbe Substitution an, bevor es die Vorschübe aufsummiert. Die horizontalen Formen zu messen und die vertikalen zu zeichnen lässt die Spalte das reservierte Feld um ein paar Punkte je Klammer überschreiten
Keins davon berührt den Text selbst. Kopiert man die Spalte aus einem Viewer heraus, bekommt man 「コード」 zurück, denn jede vertikale Glyphe mappt auf das Zeichen, das man hineingereicht hat. Suche und Textextraktion sehen Ihren Quell-String, nie die gedrehten Präsentationsformen
Wie die vertikalen Metriken ins PDF gelangen
Jedes Zeichen in einem vertikalen Lauf schreitet um die Distanz voran, die die eigenen vertikalen Metriken der Schrift angeben. Öffnet man die Ausgabe in einem PDF-Inspektor, findet man eine Type0-Schrift mit Identity-V-Kodierung, deren CIDFont für jede verwendete Glyphe einen W2-Eintrag trägt: den vertikalen Vorschub aus der vmtx-Tabelle des Schnitts, die halbe Glyphenbreite als Versatz zur Mittellinie der Spalte und den vertikalen Ursprung. ISO 32000-1 §9.7.4.3 definiert diese drei Zahlen. Für einen Schnitt ohne vertikale Metriken gibt es kein W2-Array, und der DW2-Standard schreitet jedes Zeichen um genau ein em voran
Beim vertikalen Ursprung widersprechen sich Dritt-Schriften gegenseitig. Die vhea-Tabelle hat Ascender- und Descender-Felder, die wie die naheliegende Quelle aussehen, aber die Vorzeichen-Konvention des Descenders ist nicht über Schnitte hinweg geteilt. Liest man das Feld aus den Windows-Kopien mit einem beliebigen Font-Tabellen-Viewer, kommt +140 in MS Gothic heraus und +120 in Yu Gothic, gegen −144 in SimSun und −500 in MingLiU, und die Werte können mit Font-Versionen wechseln. Jede aus diesem Feld abgeleitete Position driftet je Schnitt um einen anderen Betrag. PDF Library for Delphi nimmt den Ursprung aus dem vertikalen Ascender, den die genannten CJK-Schnitte konsistent melden, und fällt auf 880 Einheiten zurück — den Standard, den die PDF-Spezifikation selbst für DW2 angibt —, wenn ein Schnitt nichts Plausibles meldet
Vertikale Glyphen von Hand zu positionieren hat seine eigene Falle. Horizontaler Text fixiert Abstände oft mit Zahlen in einem TJ-Array. Unter einem vertikalen Schreibmodus wendet ISO 32000-1 §9.4.4 diese Zahlen entlang der Spalte an, also schiebt eine für horizontale Abstände berechnete Korrektur jedes Zeichen weiter die Seite hinunter. Deshalb ist ein vertikaler Lauf im Content-Stream ein schlichter String von Glyphen-Codes ohne irgendeine Korrektur, und jeder Abstand kommt aus W2
Wenn Sie die Ausgabe prüfen, sehen Sie auf die Tinte statt auf die Dictionaries. /Identity-V im Font zu finden beweist, dass die Kodierung deklariert wurde; es sagt nichts darüber, in welche Richtung der Text tatsächlich läuft. Die Seite zu rendern und die Bounding-Box der gezeichneten Pixel zu messen sagt es: Ein Lauf mit sieben Zeichen sollte etwa ein Zeichen breit und sieben Zeichen hoch herauskommen
Ein vertikaler Lauf auf einer horizontalen Seite
DrawVerticalText zeichnet einen einzelnen Lauf vertikal und lässt den Schreibmodus in Ruhe. Es nimmt Position, Font-Namen, Größe und Text, also bleibt auch die aktuelle Schriftauswahl unangetastet. Rücken-Etiketten, Siegel-Stempel, eine einzelne Namensspalte neben einer Tabelle: Das sind die vertikalen Ausnahmen in einem horizontalen Dokument, und einen globalen Modus um jedes einzelne herum an- und auszuschalten ist mehr Zustand, als der Job verdient
// Ein vertikaler Lauf in einer ansonsten horizontalen Seite
if Lib.DrawVerticalText(520, 96, 'MS Mincho', 14, '保守点検記録') = 0 then
begin
// Der Schnitt ließ sich nicht öffnen oder konnte diesen Text nicht setzen.
// LogLayoutProblem ist Ihre eigene Routine, kein Teil der Bibliothek
LogLayoutProblem('vertical label on page ' + IntToStr(Lib.SelectedPage));
end;
// Der horizontale Text darum herum bleibt unberührt
Lib.DrawText(72, 96, 'Maintenance inspection record');
Eine Seite, die beide Richtungen mischt, führt den Schnitt doppelt in ihren Ressourcen, einmal mit horizontaler Kodierung und einmal mit Identity-V, gebaut aus demselben Font-Programm. Jede Richtung behält ihre eigenen Metriken, also kommt eine japanische Buchseite mit vertikalem Fließtext und horizontalen Kolumnentiteln, oder ein chinesisches Zertifikat mit vertikalem Titel über horizontalen Detailzeilen, in beiden Richtungen korrekt gesperrt heraus
Der Rückgabewert ist der Grund, warum wir zu DrawVerticalText greifen, selbst in Dokumenten, die überwiegend vertikal sind. Im modusbasierten Zeichnen wird ein Lauf, der sich nicht vertikal setzen lässt, auf die gewöhnlich horizontale Weise gezeichnet statt fallengelassen, und DrawText meldet Erfolg in beiden Fällen. Für Fließtext ist das der bessere Standard, denn eine abseits liegende horizontale Zeile fällt im Andruck leicht auf, ein fehlender Absatz nicht. Für ein Etikett, dessen Richtung der ganze Punkt ist, DrawVerticalText nehmen und eine 0 als Layout-Fehlschlag behandeln
Spalten von rechts nach links anordnen
Die Spaltenreihenfolge liegt bei Ihnen. Japanischer und traditioneller chinesischer vertikaler Text liest sich Spalte für Spalte von rechts nach links, also beginnt eine Seite an ihrem rechten Rand und jede neue Spalte sitzt links der vorherigen. Keine Schreibmodus-Einstellung kann das aus dem Text ableiten, und dasselbe gilt für die Seitenreihenfolge eines rechts gebundenen Buchs und dafür, wo Furigana, Fußnoten und Bildunterschriften hingehören
Die Routine unten zerlegt einen String in Spalten, die in eine feste Höhe passen, misst unterwegs mit GetVerticalTextHeight und rückt je Spalte um anderthalb em nach links:
procedure DrawColumns(Lib: TPDFlib; const Face: WideString; Size: Double;
const Text: WideString; Right, Top, BoxH: Double);
var
Col, Trial: WideString;
I: Integer;
X: Double;
begin
// Empfohlener Guard, keine Bibliotheksprüfung: eine Höhe von null bedeutet, dass sich der Schnitt
// nicht öffnen ließ, und die Schleife darunter würde nie eine Spalte umbrechen
if Lib.GetVerticalTextHeight(Face, Size, Text) <= 0 then
Exit;
X := Right; // erste Spalte am rechten Rand
Col := '';
for I := 1 to Length(Text) do // nimmt keine Surrogat-Paare an
begin
Trial := Col + Text[I];
if (Col <> '') and (Lib.GetVerticalTextHeight(Face, Size, Trial) > BoxH) then
begin
Lib.DrawVerticalText(X, Top, Face, Size, Col);
X := X - Size * 1.5; // nächste Spalte links
Col := Text[I];
end
else
Col := Trial;
end;
if Col <> '' then
Lib.DrawVerticalText(X, Top, Face, Size, Col);
end;
Den Probe-String bei jedem Zeichen neu zu messen ist langsamer als Vorschübe zu summieren, aber es misst exakt das, was gezeichnet wird, Substitutionen eingeschlossen. Was die Schleife nicht tut, sind Kinsoku-Regeln. Eine schließende Klammer oder 。 am Kopf einer Spalte, oder eine öffnende Klammer, verwaist am Fuß einer Spalte, ist für einen japanischen Leser ein Satzfehler, und sie zu reparieren bedeutet, ein Zeichen über den Umbruch zu ziehen oder zu schieben, bevor man zeichnet. Surrogat-Paare brauchen dieselbe Sorgfalt: Text[I] zu indizieren zerschneidet ein Zeichen außerhalb der Basic Multilingual Plane in der Mitte, also in Codepunkten schreiten, wenn Ihr Text einen enthalten kann
Für aus Daten zusammengestellte Dokumente zeigt der Durchgang durch Textsuche und Seiten-Element-Enumeration, wie man zurückliest, was auf jeder Seite gelandet ist, ein schneller Weg zu bestätigen, dass jede Spalte die Zeichen trägt, für die sie gedacht war
Welche Schrift sollte ein vertikales Dokument verwenden?
Ein CJK-Schnitt mit eigenen vertikalen Metriken und einem vert- oder vrt2-Feature. Ohne sie bekommt man aufrechte Zeichen, die um je ein em weiterschreiten, und keine Formwechsel. Wenn vertikaler Text subtil falsch aussieht, untersuchen Sie die Schrift, bevor Sie den Code untersuchen. Ein Tabellen-Viewer, der die GSUB-Features eines Schnitts und seine vhea- und vmtx-Tabellen auflistet, verrät in einer Minute, ob der Schnitt den Job kann
DrawVerticalText und GetVerticalTextHeight öffnen einen installierten Schnitt über Windows bei Namen. In Lazarus-Builds für andere Plattformen läuft vertikales Schreiben über SetVerticalWritingMode und DrawText, die aus dem Programm der geladenen und ausgewählten Schrift arbeiten, also gelten dieselben vertikalen Formen und Metriken, solange diese Schrift der CJK-Schnitt ist, den Sie meinen
Einbetten folgt den üblichen Regeln und den üblichen Kosten. Ein vollständiger CJK-Schnitt läuft auf viele Megabyte hinaus, also ist Subsetting der vernünftige Standard für jedes Dokument, das Ihre Maschine verlässt. Die Hinweise zu PDF-Dateigrößen-Optimierung und Font-Subsetting decken, was zu erwarten ist, und der Durchgang durch fehlende Schriften in ein bestehendes PDF einbetten behandelt den Reparaturfall, in dem ein vertikales Dokument ohne seine Schriften ankommt
Vertikales CJK-Schreiben sitzt in derselben Zeichen-API wie der Rest der Textfunktionen in PDF Library for Delphi, einer nativen Pascal-PDF-Bibliothek für Delphi, C++Builder und Lazarus. Die Produktseite von PDF Library for Delphi listet den vollständigen Satz der Schrift- und Text-Features daneben auf