Műszaki cikk

CJK függőleges szöveg PDF-ben Delphi és HotPDF segítségével

Tegyünk egy japán regényt az oldalra, és az első dolog, amit észreveszünk, hogy a szöveg lefelé halad az oszlopban, nem pedig keresztbe a soron, és az oszlopok a lap jobb szélétől balra haladnak. Egy ezen nevelkedett olvasó a vízszintes szöveget halványan klinikai jellegűnek találja. A mérnöki probléma az, hogy a PDF, mint szinte minden digitális szöveges rendszer, egy vízszintes alapvonal köré épült, amely balról jobbra nő, és egy tartalomfolyamnak nincs fogalma arról, hogy "ezt a bekezdést inkább lefelé írjuk". Így amikor egy Delphi alkalmazásnak igazolást, verset, feliratot vagy hagyományos formátumú jogi dokumentumot kell készítenie egy tajvani, japán vagy koreai olvasó számára, az elrendezést kézzel kell összeállítani: egyik karaktert a másik alá, egyik oszlopot az előzőtől balra

A HotPDF ad egy kapcsolót, amely elvégzi helyetted a karakterenkénti könyvelést. A beállított betűtípus hordoz egy IsVertical jelzőt, és miután ez be van kapcsolva, egyetlen TextOut hívás az egész karakterláncot függőleges oszlopba rakja ahelyett, hogy egy alapvonal mentén futtatná. Az oszlop elhelyezése, a jobbról balra tartó sorrend és egy csendben fontos glifahelyettesítés az, amit ez az oldal a továbbiakban feldolgoz

A4 PDF page produced by HotPDF showing Chinese, Japanese, and Korean text stacked into vertical columns that read from right to left
Egyetlen A4-es oldal kínai, japán és koreai szöveggel függőleges oszlopokban, egyetlen Delphi eljárásból generálva.

A kapcsoló a SetFont-ban él

A függőleges elrendezés nem az oldal vagy a dokumentum tulajdonsága. Ez a rajzoláshoz használt betűtípus tulajdonsága, és a SetFont ötödik argumentumával kapcsolható be:

// SetFont(FontName, FontStyle, Size, FontCharset, IsVertical)
// The 5th argument flips the current font into vertical mode.
Pdf.CurrentPage.SetFont('Arial Unicode MS', [], 12, DEFAULT_CHARSET, False); // horizontal
Pdf.CurrentPage.SetFont('Arial Unicode MS', [], 12, DEFAULT_CHARSET, True);  // vertical

Mivel a jelző a betűtípushoz tartozik, a vízszintes és függőleges írás között egyszerűen a SetFont ismételt meghívásával válthatunk, egy eltérő utolsó argumentummal. Egy oldal tartalmazhat egy vízszintes címet felül és alatta egy függőleges törzsszöveget, és a HotPDF a két módot betűtípus objektumonként tartja külön, nem pedig oldalanként. Ez teszi lehetővé a vegyes elrendezéseket anélkül, hogy a részedről bármilyen különleges módkezelésre lenne szükség: egy függőleges SetFont utáni minden TextOut egymásra rakódik, egy vízszintes utáni minden TextOut az alapvonal mentén fut, és az utolsó SetFont nyer

A negyedik argumentum a Windows karakterkészlet, ugyanaz, amit egy vízszintes hívás is elfogad. A DEFAULT_CHARSET átadása lehetővé teszi a rendszer számára, hogy karakterlánconként oldja fel a glifákat, ami itt azért számít, mert egy függőleges dokumentum gyakran keveri az írásmódokat. A betűtípussal kapcsolatban minden más továbbra is érvényes: telepítve kell lennie a build gépen, és szinte mindig a FontEmbedding := True beállítást akarod használni, hogy a fájl ugyanazokat a CJK glifákat jelenítse meg egy olyan olvasón, amelynek soha nem volt Arial Unicode MS-e

Egy TextOut hívás egy oszlop

Aktív függőleges betűtípus esetén egy TextOut hívás többé nem terjeszti ki a karakterláncát oldalirányban az adott pontból. Az első karaktert felülre ülteti, a többit pedig egyenesen lefelé viszi, minden egyes glifánál a betűtípus sormagasságával haladva előre. Az átadott X rögzíti az oszlopot; az átadott Y rögzíti, hogy hol kezdődik az oszlop teteje. Egy valódi szövegrész elrendezéséhez ezért oszloponként egy TextOut hívást adsz ki, és az X-et balra lépteted a hívások között, mivel a CJK oszlopokat jobbról balra olvassák

var
  Pdf: THotPDF;
const
  ColTop = 760;   // y of the first glyph in every column (points)
  ColGap = 28;    // horizontal distance between columns
begin
  Pdf := THotPDF.Create(nil);
  try
    Pdf.FileName := 'VerticalText.pdf';
    Pdf.FontEmbedding := True;       // embed the CJK face for portable rendering
    Pdf.BeginDoc;
    Pdf.CurrentPage.Size := psA4;

    // A horizontal heading first, in the ordinary writing mode.
    Pdf.CurrentPage.SetFont('Arial Unicode MS', [], 16, DEFAULT_CHARSET, False);
    Pdf.CurrentPage.TextOut(60, 800, 0, 'Tang poem, vertical layout');

    // Switch the font into vertical mode; every TextOut below now stacks.
    Pdf.CurrentPage.SetFont('Arial Unicode MS', [], 18, DEFAULT_CHARSET, True);

    // Columns advance right to left, so X decreases with each call.
    Pdf.CurrentPage.TextOut(520, ColTop, 0, '床前明月光');
    Pdf.CurrentPage.TextOut(520 - ColGap,     ColTop, 0, '疑是地上霜');
    Pdf.CurrentPage.TextOut(520 - ColGap * 2, ColTop, 0, '舉頭望明月');
    Pdf.CurrentPage.TextOut(520 - ColGap * 3, ColTop, 0, '低頭思故鄉');

    Pdf.EndDoc;
  finally
    Pdf.Free;
  end;
end;

A vers úgy olvasható, ahogy kell: először a jobb szélső oszlop, fentről lefelé, majd a szem balra ugrik a következő oszlopra. Magában a karakterláncban semmi sem kódolja ezt a sorrendet. Ezt te kódolod az X koordinátákban, hívásonként csökkentve azokat. Ha elrontod az irányt, a vers fordítva jön ki, ami a leggyakoribb hiba, amikor egy vízszintes elrendezést függőlegesre portolnak

Koordináták: lefelé az oldalon, jobbról balra

Két tengely van játékban, és ellentétes irányba húznak, ezért érdemes pontosnak lenni. A HotPDF az oldal bal alsó sarkától mér, az Y pedig felfelé növekszik pontokban. Egy függőleges oszlop ezért egy magas Y értéknél kezdődik (közel a lap tetejéhez), és a karakterek onnan ereszkednek le, ahogy a HotPDF mindegyiknél levon egy sormagasságot. Ezt a kezdő Y-t oszloponként egyszer állítod be, és a komponens kezeli a leereszkedést

A vízszintes tengely az, amit te magad hajtasz meg. Minden oszlop a saját X koordinátáján ül, és az egymást követő oszlopok egyre kisebb X értékekre lépnek, mivel az olvasási sorrend jobbról balra tart. Ésszerű ritmus a jobb szélső oszlop X koordinátájának kiválasztása, majd minden további hívásnál egy rögzített oszloptávolság levonása, ahogy a példa is teszi a ColGap segítségével. A távolságot te választhatod meg; ha túl szűk, a szomszédos oszlopok összeérnek, ha túl laza, a blokk ritkának tűnik. 12-18 pontos törzsszöveg esetén a betűméretnél valamivel nagyobb távolság kényelmesen olvasható

Alakot változtató glifák

Néhány karakter nem csupán vízszintes énjük elforgatott másolata; helyettesíteni kell őket, amikor a szöveg függőlegesre fordul. Az, amit a HotPDF kezel számodra, a japán elnyújtott hang jele (U+30FC), a hosszú magánhangzó vonal, amely olyan katakana szavakban jelenik meg, mint a コーヒー. Vízszintesen megrajzolva ez egy rövid vonás az alapvonalon. Ha ugyanezt a glifát egy oszlopba raknád, laposan feküdne az oszlopon keresztül, ami helytelen: a függőleges japán nyelvben a jel egy függőleges vonássá válik, amely összeköti a két karaktert, amely között ül. A HotPDF észleli az U+30FC-t a függőleges útvonalon, és függőleges vonalként (U+007C) jeleníti meg, így a hosszú magánhangzó jele mindenféle munkád nélkül a megfelelő irányba mutat

Ez az egyetlen helyettesítés lefedi azt az esetet, amely a legtöbb naiv implementációt megtöri, de érdemes tudni, hogy az általános probléma hol megy mélyebbre. A teljes függőleges tipográfia kilencven fokkal el is forgatja a latin betűket és a nyugati írásjeleket, eltolja a kis kanákat, és a zárójeleket és vesszőket a függőleges formájukra helyezi át, és ennek teljes implementációja egy betűtípus OpenType függőleges funkcióiban él, nem pedig rögzített szabályokban. A HotPDF ki tudja aknázni ezeket a funkciókat, ha a betűtípus hordozza őket: a függőleges alternatívák (a vert és a vrt2 GSUB funkciók) és a függőleges alávágás (a vkrn és a vpal GPOS keresések) választhatók, és csak akkor alkalmazódnak a függőleges útvonal mentén, ha az aktív betűtípus ténylegesen definiálja őket. A vegyes CJK szöveghez egyetlen, széles lefedettségű betűtípusban, mint az Arial Unicode MS, a beépített U+30FC kezelés plusz az egyenletes sormagasság-lépés elegendő a helyes, olvasható oszlopok létrehozásához; az OpenType funkciók akkor számítanak, ha egy finom függőleges szedésre tervezett betűtípusra térsz át, és annak natív kana pozicionálását és glifák közötti térközét szeretnéd

Írásmódok és tájolások keverése egy oldalon

A valódi dokumentumok ritkán tiszták. Egy függőleges japán oldal hordozhat egy vízszintes angol feliratot, egy oldalszámot az alján, vagy egy koreai blokkot függőlegesen a japán mellett. Mivel a függőleges jelző egy betűtípus-szintű kapcsoló, ezeket a SetFont hívások váltogatásával állíthatod össze ahelyett, hogy valamilyen oldalszintű állapotot kezelnél. Állíts be egy vízszintes betűtípust, írd meg a futófejlécet és a lapszámot, állíts be egy függőleges betűtípust, rakd le az oszlopokat, majd állíts be ismét egy vízszintes betűtípust a lábléchez. Minden régió a legutóbbi SetFont módját veszi fel, így az egyetlen szükséges fegyelem az, hogy meghívd azt, amikor csak irányt változtatsz

Egy részlet, amire tervezni kell, amikor az írásmódok keverednek: a kínai, japán és koreai ideogrammák közel négyzet alakúak, és egyenletes lépésekben rakódnak egymásra, de a függőleges oszlopba ágyazott latin sorozatoknak nincs ilyen egyenletes előrehaladásuk. Ha néhány latin szóra van szükséged az egyébként függőleges szövegben, szándékosan döntsd el, hogy el kell-e forgatni őket, hogy lefelé fussanak az oszlopon, vagy egyenesen, rövid vízszintes betétként kell elhelyezni őket, és azt a töredéket a saját TextOut-jával pozicionáld, ahelyett, hogy hagynád meglovagolni az ideogrammákhoz szánt függőleges lépést. A vegyes sorozat saját elhelyezési problémaként való kezelése érintetlenül hagyja az oszlopritmust

A mintától a gyártásig

A darabok kicsik és kiszámíthatóan épülnek fel. Kapcsold be a függőleges jelzőt a SetFont-ban, adj ki egy TextOut hívást oszloponként egy rögzített felső Y-ból, és csökkentsd az X-et a hívások során, hogy az oszlopok jobbról balra legyenek olvashatók. Ágyazd be a betűtípust, hogy a CJK glifák túléljék az utat az olvasó gépére, és hagyd, hogy a komponens kezelje az U+30FC hosszú magánhangzó jelet és, ahol a betűtípus támogatja őket, az OpenType függőleges funkciókat. Innentől kezdve az elrendezés termelésbe állítása nagyrészt aritmetika: az oszlopok X pozícióinak levezetése a mért blokkszélességből, a hosszú szövegrészek oszlopokra bontása, amelyek illeszkednek az oldalmagassághoz, valamint a hely fenntartása a vízszintes fejlécek és lapszámok számára

Azokra a szélesebb szöveg- és betűtípus-felületekre vonatkozóan, amelyekre ez épül, beleértve a vízszintes TextOut konvenciókat és a Unicode betűtípus-regisztrációt, lásd a többnyelvű Hello World példát és a TextOut mintát. Az itt bemutatott SetFont függőleges kapcsoló és a TextOut hívások a Delphihez és C++Builderhez készült HotPDF komponens részét képezik