Typst 0.10.0 では縦書きをサポートしていません。しかしながら、部分的に縦書きを使いたいときもあります。
そこで、疑似的に縦書きを可能にする方法を作ってみました。
縦書きに見えるように出力しますが、縦書きそのものではありません。あくまで疑似的です。
今回実装した方法では、少なくとも以下のような問題を抱えています。
- 自動改行できない
- 句読点の位置が奇妙
- ゃ・ゅ・ょ・っ等の配置が奇妙
- カーニングできない
縦書きにする方針
split
関数と clusters
関数を利用することで文章を 1 文字ずつに分割し、stack
関数で下方向に向かって並べるようにします。
具体的には以下のようなステップを踏みます。
-
split
関数を使って改行 (\n
) で分割し配列として取得- 配列を左方向に積み上げる (
stack
)
- 配列を左方向に積み上げる (
-
split
関数を使って空白 (Zs) で分割し配列として取得- 配列を下方向に積み上げる (
stack
)
- 配列を下方向に積み上げる (
-
clusters
関数を使って 1 文字ずつの配列として取得- 特定の文字の場合は 90 度左回転
- 1 文字ずつを下方向に積み上げる (
stack
)
文字を並べているだけなので、カーニングのような小難しいことはまったく出来ません。古の組版 (?) を感じてください。(ただし、1 文字毎の間隔を指定することは出来ます)
縦書き内のラテン文字
縦書きの中のラテン文字は、左側が下になるような向き(90 度左回転)になります。これにも上手く対応します。
このような縦書き内で向きを変える必要のある文字は、正規表現を使って以下のように指定しておきます。
#let rotateChar = "[ー\u{FF5E}\u{301C}\p{Open_Punctuation}\p{Close_Punctuation}a-zA-Z0-9]"
この正規表現では次の文字を指定してます。
- 長音符
- 波ダッシュ・全角チルダ
- 開き括弧・閉じ括弧
- a-z、A-Z
- 0-9
また、これらはこれかこれ以外かを単純に if-else
文で判別します。
行間隔・単語間隔
改行や空白がある場合、これに対応して縦書きの改行や単語間に空白を設けるようにします。
実際、これは stack
関数の spacing
を利用しているだけです。
コード
これで完成したコードは次のようになります。
コード(折りたたみ)
#let rotateChar = "[ー\u{FF5E}\u{301C}\p{Open_Punctuation}\p{Close_Punctuation}a-zA-Z0-9]"
#let vw( body, size: 13.5pt, font: "Harano Aji Gothic", weight: "regular",
kerning: 0.3em, leading: 0.5em, spacing: 1em, ) = {
set text(size: size, font: font, weight: weight)
set align(right)
stack( dir: rtl, spacing: leading,
..for s in body.split(regex("[\n]")) {
// "s"
( // 1 ->
stack(dir: ttb, spacing: spacing,
..for t in s.split(regex("[\p{Zs}]")) {
// "t"
( // 2 ->
stack( dir: ttb, spacing: kerning,
..for l in t.clusters() {
// "l"
( // 3 ->
if regex(rotateChar) in l {
set align(center)
rotate(90deg, l)
} else {
set align(center)
l
},
) // <- 3
}
),
) // <- 2
}
),
) // <- 1
}
)
}
Typst のドキュメント風にパラメータの説明をすると、以下のようになります。
vw(
size: `length`
font: `str`, `array`
weight: `int`, `str`
kerning: `none`, `relative`, `fraction`
leading: `none`, `relative`, `fraction`
spacing: `none`, `relative`, `fraction`
`str`
)
パラメータ | 説明 | デフォルト |
---|---|---|
size |
文字の大きさ | 13.5pt |
font |
フォント | Harano Aji Gothic |
weight |
フォントのウェイト | regular |
kerning |
文字と文字の縦間隔 | 0.3em |
leading |
行間隔 | 0.5em |
spacing |
空白の大きさ | 1em |
利用方法に必要な注意として、split
関数は文字列しか受け付けません。コンテンツを受け付けないため、途中の文章を装飾することは出来ません。
利用例
以下の利用例では上のコードを vw.typ として保存し、import
で導入します。
複数の文章を並べる場合、縦書きのように左側に並ばないため、table
関数で表形式を利用します。(文章は 青空文庫 より)
#import "vw.typ": *
#set page(width: 15cm, height: 10cm, margin: 1cm)
#set align(center)
#let title = "雨ニモマケズ"
#let author = "宮澤 賢治"
#let main = "雨ニモマケズ
風ニモマケズ
雪ニモ夏ノ暑サニモマケヌ
丈夫ナカラダヲモチ
慾ハナク
決シテ瞋ラズ
イツモシヅカニワラッテヰル
一日ニ玄米四合ト
味噌ト少シノ野菜ヲタベ
アラユルコトヲ
ジブンヲカンジョウニ入レズニ
ヨクミキキシワカリ
ソシテワスレズ"
#table(
columns: (auto, 4em, 3em),
align: (right, right+bottom, right),
stroke: none,
vw(main),
vw(author),
vw(title, kerning: 0.6em),
)
句読点やラテン文字が入る良い感じの例が見つからないので、実際に試してみてください。
新年のご挨拶
Typst で疑似的な縦書きを実装してみました。
これを使って新年のご挨拶をしたいと思います。新年のご挨拶と言えば年賀状ですね。
と言うことで、“Typst で年賀状” を作ってみました。
年賀状(折りたたみ)
挿入されている辰の絵は いらすとや から取得しました。
#import "vw.typ": *
#let bgColor = rgb("#f7edc6")
#let backCard = rect( width: 100mm-10mm, height: 148mm-10mm, fill: bgColor )
#set page(
width: 100mm, height: 148mm, margin: 20pt,
background: backCard,
)
#show image: set align(center)
#let stamp = {
box(
fill: gradient.linear(red, red, red, bgColor, angle: 120deg),
width: 23pt, height: 20pt,
outset: 5pt, inset: 2.5pt,
radius: 5pt,
)[#text(bgColor, size: 20pt)[*辰*]]
}
#let greeding = "謹賀新年"
#let letter = "昨年は大変お世話になりました
本年もそれなりに頑張りますので
何卒よろしくお願いします
皆さまも幸多き年となりますよう
心よりお祈り申し上げます"
#let date = "令和六年 元日 "
#image("dragon_cloud.png", height: 150pt)
#table(
align: (left+bottom, center+bottom, center, right),
columns: (20pt, 20pt, 140pt, 62pt),
stroke: none,
stamp,
vw(date, spacing: 1.5em, font: "Noto Serif CJK JP"),
vw(letter),
vw(greeding, size: 40pt, kerning: 0.74em, font: "Noto Serif CJK JP", weight: "bold"),
)
こだわりポイントは「辰」印がちょっとかすれているところです()
参考
Typst で縦書きをしている人がとても少ないようでした。
Twitter 上で Typst の縦書きをテーブルレイアウトで再現している人 ⧉ がいましたが、その他 ⧉ は見つけることが出来ませんでした。
まとめ
新年のご挨拶を兼ねた Typst でも縦書きしたいでした。
2024 年もよろしくお願いいたします。