Zet 「コード」 naar beneden over een pagina door het teken voor teken te tekenen, elke aanroep één em lager dan de vorige, en drie van de vijf glyphs komen er verkeerd uit. De twee hoekhaakjes staan in de kolom zoals ze op een horizontale regel staan, zijwaarts open, en het lange klinkerteken ー is een plat streepje dat dwars door de kolom snijdt waar een Japanse lezer een korte verticale streep verwacht. Alleen コ en ド, de twee gewone katakana, zien eruit zoals een lettersetter ze zou zetten
De hele regel negentig graden draaien maakt de haakjes en het lange klinkerteken min of meer bij toeval goed en breekt al het andere: elke kanji en kana ligt nu op zijn zij. Verticaal schrijven doet drie dingen tegelijk. Tekens blijven overeind staan, het voorschot loopt de pagina af in plaats van eroverheen, en een handvol tekens wisselt naar andere tekeningen. Alle drie goed krijgen vereist een font dat verticale vormen en verticale metrics meedraagt, en een PDF-font dat is ingericht om ze te gebruiken
Vertical writing aanzetten in PDF Library for Delphi
PDF Library for Delphi behandelt verticaal schrijven als een tekenmodus. SetVerticalWritingMode(1) zet hem aan, elke DrawText-aanroep daarna loopt vanaf het punt dat u opgeeft de pagina af, en SetVerticalWritingMode(0) haalt horizontale tekst terug. De setter geeft de modus terug die vóór de aanroep gold en GetVerticalWritingMode leest de huidige, dus een hulproutine kan de modus omschakelen en later precies terugzetten wat hij aantrof
De twee coördinaten betekenen iets anders dan bij horizontale tekst. De x-waarde benoemt de middenlijn van de kolom: elke glyph hangt met zijn horizontale midden op die lijn, dus een smal leesteken en een volle-breedte-kanji blijven op dezelfde as uitgelijnd. De y-waarde is de bovenkant van de eerste tekencel, en de kolom groeit vanaf daar naar beneden. Met SetOrigin(1), dat y = 0 op de bovenrand van de pagina zet, leest de rekenkunde zoals u de pagina op papier zou indelen
var
Lib: TPDFlib;
H: Double;
begin
Lib := TPDFlib.Create;
try
Lib.SetOrigin(1);
Lib.AddTrueTypeFont('MS Mincho', 1); // 1 = de face insluiten
Lib.SetTextSize(12);
Lib.SetVerticalWritingMode(1); // gewone DrawText loopt nu naar beneden
H := Lib.GetVerticalTextHeight('MS Mincho', 12, '第三章 保守点検');
Lib.DrawText(480, 72, '第三章 保守点検'); // kolom begint op y = 72
Lib.SetVerticalWritingMode(0); // terug naar horizontaal
// Origin 1 zet y = 0 bovenaan, dus 72 + H is de voet van de kolom;
// het horizontale bijschrift staat er één regel onder
Lib.DrawText(456, 72 + H + 18, 'Chapter 3');
Lib.SaveToFile('manual-ja.pdf');
finally
Lib.Free;
end;
end;
GetVerticalTextHeight neemt de facenaam, de grootte en de tekst, en geeft de lengte van de kolom in punten terug. In het voorbeeld is 72 + H de voet van de kolom en gaat het Engelse bijschrift 18 punten daaronder, beginnend 24 punten links van de middenlijn van de kolom, zodat de korte Latijnse regel grofweg onder de kolom staat. De functie geeft 0 terug wanneer hij geen face onder die naam kan openen, en een indeling die op een nulhoogte bouwt zet het bijschrift bovenop de kolom, dus test daarop voordat u de waarde gebruikt
Waarom liggen haakjes op hun zij in een verticale kolom?
Omdat het font twee tekeningen van elk zo'n teken vasthoudt en de horizontale de standaard is. OpenType-faces zetten de alternatieven in een GSUB-feature die vert heet, of vrt2 in faces die glyphs meeleveren die al gedraaid zijn voor verticale opmaak. Het shapen van een tekstregel geeft de horizontale glyphs terug, en iets moet daarna de verticale feature toepassen, één glyph voor één glyph. Hoekhaakjes, haakjes en het lange klinkerteken draaien om langs de kolom te lopen; kleine kana en leestekens zoals 、 en 。 verhuizen naar een andere hoek van hun vierkant
PDF Library for Delphi leest die feature uit de face waarmee hij tekent en wisselt elke glyph voor de verticale vorm die het font benoemt. Draagt een face beide features, dan wint vrt2 en wordt vert genegeerd. De specificatie bedoelt vrt2 als vervanger van vert, en beide lezen kan één teken twee verschillende verticale glyphs geven, afhankelijk van welke lookup het eerst antwoordt. Een face dat geen van beide features heeft, houdt zijn gewone glyphs. We verkiezen die uitkomst boven een ingebouwde substitutietabel: een geraden tabel krijgt de gangbare tekens goed en overschrijft stilletjes de faces waarvan de ontwerpers een teken anders behandelden
Dumpt u de GSUB-tabellen van de kopieën die Windows meelevert, dan lopen de aantallen ver uiteen: de verticale feature van MS Gothic mapt 979 glyphs, Yu Gothic 2.491, SimSun 72, en Arial helemaal geen (de exacte aantallen verschuiven met fontversies). Een Latijns face dat toevallig wat kana bevat, tekent een kolom van overeind staande tekens waarbij elk haakje en elk lang klinkerteken in zijn horizontale vorm staat, want er is niets in het font om te substitueren
Substitutie verandert ook de meting. Een verticale vorm kan zijn eigen voorschot hebben, anders dan de horizontale glyph die hij vervangt, dus GetVerticalTextHeight past dezelfde substitutie toe voordat hij de voorschotten optelt. Meet de horizontale vormen en teken de verticale, en de kolom loopt de door u gereserveerde box enkele punten per haakje voorbij
Niets van dit alles raakt de tekst zelf. Kopieer de kolom uit een viewer en u krijgt 「コード」 terug, want elke verticale glyph mapt op het teken dat u binnenstuurde. Zoeken en tekstextractie zien uw brontekenreeks, nooit de gedraaide presentatievormen
Hoe de verticale metrics in de PDF belanden
Elk teken in een verticale run schuift op over de afstand die de eigen verticale metrics van het font geven. Open de uitvoer in een PDF-inspector en u vindt een Type0-font met Identity-V-encoding waarvan de CIDFont voor elke gebruikte glyph een W2-entry draagt: het verticale voorschot uit de vmtx-tabel van de face, de halve glyphbreedte als offset naar de middenlijn van de kolom, en de verticale oorsprong. ISO 32000-1 §9.7.4.3 definieert die drie getallen. Voor een face zonder verticale metrics is er geen W2-array, en de DW2-standaard schuift elk teken precies één em op
Over de verticale oorsprong zijn externe fonts het oneens. De vhea-tabel heeft ascender- en descendervelden die eruitzien als de voor de hand liggende bron, maar de tekenconventie van de descender is niet gedeeld over faces. Lees het veld uit de Windows-kopieën met een willekeurige fonttabelviewer en het komt uit als +140 bij MS Gothic en +120 bij Yu Gothic, tegenover −144 bij SimSun en −500 bij MingLiU, en de waarden kunnen met fontversies meebewegen. Elke positie die uit dat veld is afgeleid, drijft per face over een andere afstand weg. PDF Library for Delphi neemt de oorsprong uit de verticale ascender, die de hierboven genoemde CJK-faces consistent rapporteren, en valt terug op 880 units — de standaard die de PDF-specificatie zelf voor DW2 geeft — wanneer een face niets geloofwaardigs rapporteert
Verticale glyphs met de hand positioneren heeft een eigen valkuil. Horizontale tekst fixeert tussenruimte vaak met getallen in een TJ-array. Onder een vertical writing mode past ISO 32000-1 §9.4.4 die getallen langs de kolom toe, dus een correctie die voor horizontale tussenruimte is berekend, duwt elk teken verder de pagina af. Daarom is een verticale run in de content stream één kale reeks glyphcodes zonder enige aanpassing, en komt alle tussenruimte uit W2
Als u de uitvoer controleert, kijk dan naar de inkt in plaats van naar de woordenboeken. /Identity-V in het font vinden bewijst dat de encoding is gedeclareerd; het zegt niets over de richting waarin de tekst werkelijk loopt. De pagina renderen en de bounding box van de getekende pixels meten doet dat wel: een run van zeven tekens hoort ongeveer één teken breed en zeven tekens hoog uit te komen
Eén verticale run op een horizontale pagina
DrawVerticalText tekent één enkele run verticaal en laat de schrijfmodus met rust. Hij neemt de positie, de fontnaam, de grootte en de tekst, dus ook de huidige fontselectie blijft onaangetast. Ruggenlabels, zegelstempels, één kolom met namen naast een tabel: dit zijn de verticale uitzonderingen binnen een horizontaal document, en rond elk ervan een globale modus aan en uit zetten is meer toestand dan de klus verdient
// Eén verticale run in een verder horizontale pagina
if Lib.DrawVerticalText(520, 96, 'MS Mincho', 14, '保守点検記録') = 0 then
begin
// De face kon niet worden geopend of kon deze tekst niet zetten.
// LogLayoutProblem is uw eigen routine, geen onderdeel van de bibliotheek
LogLayoutProblem('vertical label on page ' + IntToStr(Lib.SelectedPage));
end;
// De horizontale tekst eromheen blijft onaangetast
Lib.DrawText(72, 96, 'Maintenance inspection record');
Een pagina die beide richtingen mengt, draagt de face tweemaal in zijn resources, één keer met horizontale encoding en één keer met Identity-V, gebouwd uit hetzelfde fontprogramma. Elke richting houdt zijn eigen metrics, dus een Japanse boekpagina met verticale lopende tekst en horizontale doorlopende koppen, of een Chinees certificaat met een verticale titel boven horizontale detailregels, komt eruit met beide richtingen correct verdeeld
De returnwaarde is de reden dat we ook in documenten die vooral verticaal zijn, naar DrawVerticalText grijpen. Bij tekenen op modus wordt een run die niet verticaal gezet kan worden, op de gewone horizontale manier getekend in plaats van weggegooid, en DrawText meldt succes in beide gevallen. Voor lopende tekst is dat de betere standaard, want een verdwaalde horizontale regel valt in een drukproef makkelijk op en een ontbrekende alinea niet. Voor een label waarvan de richting het hele punt is, gebruikt u DrawVerticalText en behandelt u een 0 als een indelingsfout
Kolommen van rechts naar links indelen
De kolomvolgorde is aan u. Verticale Japanse en traditioneel Chinese tekst leest kolom voor kolom van rechts naar links, dus een pagina begint bij zijn rechtermarge en elke nieuwe kolom staat links van de vorige. Geen instelling van de schrijfmodus kan dat uit de tekst afleiden, en hetzelfde geldt voor paginavolgorde in een rechts gebonden boek en voor waar furigana, voetnoten en bijschriften thuishoren
De routine hieronder breekt een tekenreeks op in kolommen die in een vaste hoogte passen, meet met GetVerticalTextHeight terwijl hij gaat en stapt per kolom anderhalve em naar 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
// Voorgestelde bewaking, geen bibliotheekcontrole: een hoogte van nul betekent dat de face
// niet kon worden geopend, en de lus hieronder zou nooit een kolom afbreken
if Lib.GetVerticalTextHeight(Face, Size, Text) <= 0 then
Exit;
X := Right; // eerste kolom op de rechtermarge
Col := '';
for I := 1 to Length(Text) do // gaat uit van geen surrogate pairs
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; // volgende kolom naar links
Col := Text[I];
end
else
Col := Trial;
end;
if Col <> '' then
Lib.DrawVerticalText(X, Top, Face, Size, Col);
end;
De proeftekenreeks bij elk teken opnieuw meten is trager dan voorschotten optellen, maar hij meet precies wat er getekend wordt, substituties inbegrepen. Wat de lus niet doet, is kinsoku-regels toepassen. Een sluitend haakje of 。 bovenaan een kolom, of een openingshaakje dat onderaan is achtergebleven, is voor een Japanse lezer een zetfout, en repareren betekent een teken over de breuk heen trekken of duwen voordat u tekent. Surrogate pairs vragen dezelfde zorg: indexeren met Text[I] splijt een teken buiten het Basic Multilingual Plane in tweeën, dus stap per codepunt als uw tekst er een kan bevatten
Voor documenten die uit data worden samengesteld, laat de doorloop van tekstzoeken en pagina-elementenumeratie zien hoe u terugleest wat op elke pagina terechtkwam, een snelle manier om te bevestigen dat elke kolom de tekens draagt die u bedoelde
Welk font hoort een verticaal document te gebruiken?
Een CJK-face met eigen verticale metrics en een vert- of vrt2-feature. Zonder die twee krijgt u overeind staande tekens die per keer één em opschuiven en geen vormveranderingen. Als verticale tekst subtiel verkeerd oogt, onderzoek dan het font voordat u de code onderzoekt. Een tabelviewer die de GSUB-features van een face en zijn vhea- en vmtx-tabellen oplijst, vertelt u binnen een minuut of de face de klus aankan
DrawVerticalText en GetVerticalTextHeight openen een geïnstalleerde face bij naam via Windows. In Lazarus-builds voor andere platforms loopt verticaal schrijven via SetVerticalWritingMode en DrawText, die werken vanuit het programma van het font dat u laadde en selecteerde, dus dezelfde verticale vormen en metrics gelden zolang dat font de CJK-face is die u bedoelde
Insluiten volgt de gebruikelijke regels en de gebruikelijke kosten. Een volledige CJK-face loopt op tot vele megabytes, dus subsetting is de verstandige standaard voor elk document dat uw machine verlaat. De notities over PDF-bestandsgrootte-optimalisatie en font-subsetting dekken wat u kunt verwachten, en de doorloop van ontbrekende fonts insluiten in een bestaande PDF dekt het herstelgeval waarin een verticaal document zonder zijn fonts aankomt
Verticaal CJK-schrijven zit in dezelfde teken-API als de rest van de tekstfuncties van PDF Library for Delphi, een native Pascal-PDF-bibliotheek voor Delphi, C++Builder en Lazarus. De productpagina van PDF Library for Delphi lijst de volledige set font- en tekstfeatures ernaast op