Ustaw 「コード」 w dół strony, rysując znak po znaku, każde wywołanie o jedno em niżej niż poprzednie, a trzy z pięciu glifów wyjdą źle. Dwa narożne nawiasy siedzą w kolumnie tak, jak siedzą na poziomym wierszu — otwierają się na boki, a długi znak samogłoski ー to płaski kreskowiec przecinający kolumnę tam, gdzie japoński czytelnik oczekuje krótkiej pionowej kreski. Tylko コ i ド, dwie zwykłe katakany, wyglądają tak, jak ustawiłby je zecer
Obrót całego wiersza o dziewięćdziesiąt stopni naprawia nawiasy i długi znak samogłoski mniej więcej przez przypadek — i psuje wszystko inne: każdy kanji i kana leży teraz na boku. Pismo pionowe robi trzy rzeczy naraz. Znaki stoją prosto, postęp biegnie w dół strony zamiast w poprzek, a garstka znaków przełącza się na inne rysunki. Trafienie we wszystkie trzy wymaga kroju, który wozi pionowe formy i pionowe metryki, i fontu PDF ustawionego tak, żeby ich używać
Włączanie pisma pionowego w PDF Library for Delphi
PDF Library for Delphi traktuje pismo pionowe jako tryb rysowania. SetVerticalWritingMode(1) włącza je, każde wywołanie DrawText po tym biegnie w dół strony od podanego punktu, a SetVerticalWritingMode(0) przywraca tekst poziomy. Setter zwraca tryb obowiązujący przed wywołaniem, a GetVerticalWritingMode odczytuje bieżący, więc rutyna pomocnicza może przełączyć tryb i później odłożyć dokładnie to, co zastała
Obie współrzędne znaczą coś nieco innego niż przy tekście poziomym. Wartość x nazywa linię środkową kolumny: każdy glif wisi swoją poziomą środkową na tej linii, więc wąski znak interpunkcyjny i pełnej szerokości kanji trzymają się jednej osi. Wartość y to górna krawędź pierwszej komórki znakowej, a kolumna rośnie stamtąd w dół. Przy SetOrigin(1), które kładzie y = 0 na górnej krawędzi strony, arytmetyka czyta się tak, jak układałbyś stronę na papierze
var
Lib: TPDFlib;
H: Double;
begin
Lib := TPDFlib.Create;
try
Lib.SetOrigin(1);
Lib.AddTrueTypeFont('MS Mincho', 1); // 1 = osadź krój
Lib.SetTextSize(12);
Lib.SetVerticalWritingMode(1); // zwykłe DrawText teraz biegnie w dół
H := Lib.GetVerticalTextHeight('MS Mincho', 12, '第三章 保守点検');
Lib.DrawText(480, 72, '第三章 保守点検'); // kolumna startuje na y = 72
Lib.SetVerticalWritingMode(0); // powrót do poziomego
// Origin 1 kładzie y = 0 u góry, więc 72 + H to stopka kolumny;
// poziomy podpis siedzi o jeden wiersz niżej
Lib.DrawText(456, 72 + H + 18, 'Chapter 3');
Lib.SaveToFile('manual-ja.pdf');
finally
Lib.Free;
end;
end;
GetVerticalTextHeight bierze nazwę kroju, rozmiar i tekst, a zwraca długość kolumny w punktach. W przykładzie 72 + H to stopka kolumny i angielski podpis idzie 18 punktów pod nią, startując 24 punkty na lewo od linii środkowej kolumny, żeby krótki łaciński wiersz siedział mniej więcej pod kolumną. Funkcja zwraca 0, gdy nie może otworzyć kroju o tej nazwie, a układ zbudowany na zerowej wysokości położy podpis na czubku kolumny, więc sprawdź to, zanim użyjesz wartości
Dlaczego nawiasy leżą na boku w pionowej kolumnie?
Ponieważ font trzyma dwa rysunki każdego takiego znaku, a poziomy jest domyślny. Kroje OpenType wypisują alternatywy w cesze GSUB zwanej vert, albo vrt2 w krojach, które wożą glify już obrócone do składu pionowego. Kształtowanie wiersza tekstu zwraca glify poziome i coś musi nałożyć cesę pionową po fakcie, glif za glif. Narożne nawiasy, nawiasy okrągłe i długi znak samogłoski obracają się, by biec wzdłuż kolumny; małe kana i interpunkcja, jak 、 i 。, przesuwają się do innego narożnika swojej kwadratowej komórki
PDF Library for Delphi czyta tę cesę z kroju, którym rysuje, i podmienia każdy glif na pionową formę nazwaną przez font. Gdy krój wozi obie cesy, vrt2 wygrywa, a vert jest ignorowane. Specyfikacja chce, by vrt2 zastępowało vert, a czytanie obu może wręczyć jednemu znakowi dwa różne pionowe glify, zależnie od tego, która tabela odpowiedziałaby pierwsza. Krój bez żadnej z nich zostawia zwykłe glify. Wolimy taki wynik niż wbudowaną tabelę podstawień: zgadywana tabela trafi w zwykłe znaki i po cichu nadpisze kroje, których projektanci potraktowali znak inaczej
Zrzuć tabele GSUB egzemplarzy dołączonych do Windows, a liczby rozjadą się daleko: cesa pionowa MS Gothic mapuje 979 glifów, Yu Gothic 2 491, SimSun 72, a Arial ani jednego (dokładne liczby wędrują wraz z wersjami fontów). Krój łaciński, który przy okazji zawiera trochę kany, narysuje prostą kolumnę z każdym nawiasem i długim znakiem samogłoski w formie poziomej, bo w foncie nie ma czego podstawiać
Podstawienie zmienia też pomiar. Pionowa forma może mieć własny postęp, różny od poziomego glifu, którego zastępuje, więc GetVerticalTextHeight nakłada to samo podstawienie, zanim zsumuje postępy. Zmierz formy poziome, narysuj pionowe, a kolumna przecieśnie rezerwację o kilka punktów na każdy nawias
Nic z tego nie dotyka samego tekstu. Skopiuj kolumnę z przeglądarki, a dostaniesz z powrotem 「コード」, bo każdy pionowy glif mapuje się na znak, który podałeś. Wyszukiwanie i ekstrakcja tekstu widzą twój łańcuch źródłowy, nigdy obrócone formy prezentacyjne
Jak metryki pionowe trafiają do PDF
Każdy znak w pionowym przebiegu postępuje o odległość, którą dają mu własne metryki pionowe fontu. Otwórz wyjście w inspektorze PDF, a znajdziesz font Type0 z kodowaniem Identity-V, którego CIDFont niesie wpis W2 dla każdego użytego glifu: pionowy postęp z tabeli vmtx kroju, połowę szerokości glifu jako offset do linii środkowej kolumny i pionowy początek. Te trzy liczby definiuje ISO 32000-1 §9.7.4.3. Dla kroju bez metryk pionowych nie ma tablicy W2 i domyślne DW2 postępuje każdy znak dokładnie o jedno em
O pionowym początku trzecie fonty spierają się między sobą. Tabela vhea ma pola ascender i descender wyglądające na oczywiste źródło, ale konwencja znaku descendera nie jest wspólna dla krojów. Przeczytaj pole z egzemplarzy z Windows dowolną przeglądarką tabel fontów i wyszły +140 w MS Gothic i +120 w Yu Gothic, wobec −144 w SimSun i −500 w MingLiU, a wartości mogą się zmieniać z wersjami fontów. Każda pozycja wyprowadzona z tego pola dryfuje o inną wielkość dla każdego kroju. PDF Library for Delphi bierze początek z pionowego ascendera, który wymienione wyżej kroje CJK raportują zgodnie, i pada na 880 jednostek — domyślną wartość, którą sama specyfikacja PDF daje DW2 — gdy krój nie raportuje niczego sensownego
Ręczne pozycjonowanie pionowych glifów ma własną pułapkę. Tekst poziomy często ustala odstępy liczbami wewnątrz tablicy TJ. Pod trybem pisma pionowego ISO 32000-1 §9.4.4 nakłada te liczby wzdłuż kolumny, więc korekta policzona dla odstępów poziomych zepchnie każdy znak głębiej w dół strony. Dlatego pionowy przebieg w strumieniu treści to jeden goły łańcuch kodów glifów bez jakichkolwiek korekt, a cały odstęp pochodzi z W2
Kontrolując wyjście, patrz na tusz, nie na słowniki. Obecność /Identity-V w foncie dowodzi deklaracji kodowania; nie mówi nic o tym, w którą stronę tekst faktycznie biegnie. Wyrenderowanie strony i zmierzenie ramki narysowanych pikseli mówi: przebieg siedmiu znaków powinien wyjść około jeden znak szeroki i siedem znaków wysoki
Jeden pionowy przebieg na poziomej stronie
DrawVerticalText rysuje pojedynczy przebieg pionowo i zostawia tryb pisania w spokoju. Bierze pozycję, nazwę fontu, rozmiar i tekst, więc bieżący wybór czcionki też zostaje nietknięty. Etykiety na grzbietach, pieczęcie, jedna kolumna nazwisk obok tabeli — to pionowe wyjątki wewnątrz poziomego dokumentu, a przełączanie globalnego trybu wokół każdego z nich to więcej stanu, niż zasługuje ta robota
// Jeden pionowy przebieg wewnątrz poza tym poziomej strony
if Lib.DrawVerticalText(520, 96, 'MS Mincho', 14, '保守点検記録') = 0 then
begin
// Krój nie dał się otworzyć albo nie dał się ustawić dla tego tekstu.
// LogLayoutProblem to twoja własna rutyna, nie część biblioteki
LogLayoutProblem('vertical label on page ' + IntToStr(Lib.SelectedPage));
end;
// Poziomy tekst wokół niego pozostaje niezmieniony
Lib.DrawText(72, 96, 'Maintenance inspection record');
Strona mieszająca oba kierunki wozi krój w swoich zasobach dwa razy — raz z kodowaniem poziomym, raz z Identity-V — zbudowany z tego samego programu fontu. Każdy kierunek trzyma własne metryki, więc strona japońskiej książki z pionowym tekstem głównym i poziomymi żywymi nagłówkami albo chiński certyfikat z pionowym tytułem nad poziomymi liniami szczegółów wychodzi z oboma kierunkami rozstawionymi poprawnie
Wartością zwracaną jest powód, dla którego sięgamy po DrawVerticalText nawet w dokumentach w większości pionowych. W rysowaniu opartym na trybie przebieg, którego nie da się złożyć pionowo, jest rysowany zwykłą drogą poziomą zamiast być porzucony, a DrawText raportuje sukces tak czy owak. Dla tekstu głównego to lepszy domyślny wybór, bo zbłąkany poziomy wiersz łatwo wychwycić na próbce wydruku, a brakujący akapit nie. Dla etykiety, której kierunek jest całym sensem, użyj DrawVerticalText i traktuj 0 jako porażkę składu
Układanie kolumn z prawej na lewo
Kolejność kolumn jest twoja do rozstrzygnięcia. Japoński i tradycyjny chiński w pionie czytają kolumna po kolumnie od prawej do lewej, więc strona startuje od prawego marginesu i każda nowa kolumna siedzi na lewo od poprzedniej. Żadne ustawienie trybu pisania nie wywnioskuje tego z tekstu, i to samo dotyczy kolejności stron w książce oprawionej od prawej oraz miejsc furigany, przypisów i podpisów rysunków
Rutyna poniżej łamie łańcuch na kolumny mieszczące się w stałej wysokości, mierząc po drodze przez GetVerticalTextHeight i stając w lewo o półtora em na kolumnę:
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
// Straż jako sugestia, nie kontrola biblioteki: zerowa wysokość znaczy, że krój
// nie dał się otworzyć, a pętla poniżej nigdy nie złamałaby kolumny
if Lib.GetVerticalTextHeight(Face, Size, Text) <= 0 then
Exit;
X := Right; // pierwsza kolumna przy prawym marginesie
Col := '';
for I := 1 to Length(Text) do // zakłada brak par surrogate
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; // następna kolumna w lewo
Col := Text[I];
end
else
Col := Trial;
end;
if Col <> '' then
Lib.DrawVerticalText(X, Top, Face, Size, Col);
end;
Ponowne mierzenie próbnego łańcucha przy każdym znaku jest wolniejsze od sumowania postępów, ale mierzy dokładnie to, co zostanie narysowane, z podstawieniami włącznie. Czego pętla nie robi, to zasad kinsoku. Zamykający nawias albo 。 na szczycie kolumny albo otwierający nawias u jej stopy to dla japońskiego czytelnika błąd składu, a naprawa oznacza przeciągnięcie lub odepchnięcie znaku przez łamanie, zanim narysujesz. Pary surrogate potrzebują tej samej troski: indeksowanie Text[I] przecina znak z poza Basic Multilingual Plane na pół, więc krokuj po punkcie kodu, jeśli twój tekst może taki zawierać
Dla dokumentów składanych z danych przejście przez artykuł przeszukiwanie tekstu i wyliczanie elementów strony pokazuje, jak odczytać z powrotem to, co wylądowało na każdej stronie — szybki sposób, by potwierdzić, że każda kolumna niesie znaki, które miała nieść
Którego fontu powinien używać dokument pionowy?
Kroju CJK z własnymi metrykami pionowymi i cesą vert albo vrt2. Bez nich dostajesz stojące prosto znaki postępujące po jednym em i żadnych zmian kształtów. Gdy tekst pionowy wygląda subtelnie nie tak, obejrzyj font, zanim obejrzysz kod. Przeglądarka tabel wypisująca cesy GSUB kroju i jego tabele vhea oraz vmtx powie ci w minutę, czy krój da radę
DrawVerticalText i GetVerticalTextHeight otwierają zainstalowany krój po nazwie przez Windows. W kompilacjach Lazarus dla innych platform pismo pionowe idzie przez SetVerticalWritingMode i DrawText, które pracują z programu fontu, który wczytałeś i wybrałeś — te same pionowe formy i metryki obowiązują, dopóki tym fontem jest krój CJK, o który ci chodziło
Osadzanie idzie zwykłymi regułami i zwykłymi kosztami. Pełny krój CJK ciągnie wiele megabajtów, więc subsetowanie to rozsądny domyślny wybór dla każdego dokumentu, który opuści twoją maszynę. Notki o artykule optymalizacja rozmiaru PDF i subsetowanie fontów mówią, czego się spodziewać, a przejście przez artykuł osadzanie brakujących fontów w istniejącym PDF pokrywa przypadek naprawczy, gdy dokument pionowy przychodzi bez swoich fontów
Pionowe pismo CJK siedzi w tym samym API rysowania co reszta funkcji tekstowych PDF Library for Delphi, natywnej biblioteki PDF w Pascalu dla Delphi, C++Builder i Lazarus. Pełny zestaw cech fontowych i tekstowych wypisuje obok niej strona produktu PDF Library for Delphi