4
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

TypstAdvent Calendar 2024

Day 16

Typstで和文と数式の間の空きをどうにかしたい話

Last updated at Posted at 2024-12-16

本記事では、Typstにおける有名な未解決問題である​「和文と数式の間の空き」​について、問題の回避策について検討していきます。

やりたいやつ

伝統的な和文組版においては「和文と欧文の間に小さい水平空きを入れる」という規則になっています。以下ではこの空きのことを​「和欧文間空白」​と呼ぶことにします。

image-1.png

伝統的な“全角組”においては和欧文間空白の標準的な幅は「全角の1/4」(四分)となっています1。一般的な欧文フォントでは欧文空白の幅は全角の1/3~1/4程度であることが多いため、フォントによっては和欧文間空白は欧文空白よりも小さくなります。

image-2.png

“プロポーショナル組”の場合の和欧文間空白は四分よりも小さい値が好まれ「空きを入れない」という選択もあるようです。

和文文書においては数式も“欧文の一種”であるため、和文と数式の間にも和欧文間空白を入れる必要があります。

image-4.png

和文側の文字種によっては和欧文間空白が入らない場合もあります。例えば、括弧の内側や句読点の直前には空きを入れてはいけないため、この位置に欧文や数式があっても和欧文間空白は入りません2

image-3.png

以上が伝統的な和文組版における和欧文間空白の規則です。

以降では伝統的な“全角組”を行うことを前提にします。

おやくそくのやつ

以降ではTypstにおける和欧文間空白の扱いについて述べますが、そこでの出力例にでは和文用の最低限のレイアウト設定として次のモジュールを使用します。

layout.typ
// 和文用の最低限のレイアウト設定
#let doc(body) = {
  set page(paper: "a5")
  set text(
    lang: "ja",
    font: ("New Computer Modern", "Harano Aji Mincho"),
    weight: 450,
    top-edge: 0.88em,
  )
  body
}

この設定では和文書体に「原ノ味明朝」、欧文書体に「New Computer Modern」のBookウェイト3を指定しています。この欧文フォントの欧文空白の幅は0.333em(ほぼ三分)なので和欧文間空白との違いが顕著になります。(実はこのフォント設定を前節の例でも使っています。)

従って、以下の例を実際にコンパイルするには「原ノ味明朝」と「New Computer Moder Math」のBookウェイト4がTypstで使用可能になっている必要があります。

現状のTypstで和欧文間空白を自動挿入するやつ

Typstは日本語組版もサポート範囲に含めているため、和欧文間空白の自動挿入の機能をもっています。和欧文間空白の自動挿入はtextエレメントのcjk-latin-spacingパラメタで制御できます。

  • cjk-latin-spacing[none|auto;既定値=auto]: 和欧文間空白の自動挿入の状態。
    • none: 和欧文間空白を挿入しない。
    • auto: “全角組”の規則に従って、文書ソース上で和文と欧文が隣接している箇所について必要に応じて四分(0.25em)の空きを自動挿入する5

既定値がautoなので、ソース上で和文と欧文が隣接している部分には和欧文間空白が適切に自動挿入されます。もし何らかの理由で自動挿入を止めたいという場合は、set規則等を利用してtextエレメントのcjk-latin-spacingnoneを指定すればよいわけです。

example-1.typ
// おやくそくのやつ
#import "layout.typ": *
#show: doc

// 既定で和欧文間空白の自動挿入は有効
お客様の中にTypst芸人はおられませんか。

// 自動挿入を無効にする
#set text(cjk-latin-spacing: none)
お客様の中にTypst芸人はおられませんか。

image-5.png

現状のTypstで和欧文間空白を自動挿入できないやつ

ところが現状のTypstでは、和欧文間空白の自動挿入機能にはまだまだ不完全なところがあります。和文と欧文の間には空白が入るのですが、和文と数式の間には空白が入らないのです。

example-2.typ
#import "layout.typ": *
#show: doc

// 数式との間いには空白が入らない!
お客様の中に$T_y (p_s dot tau)$芸人はおられませんか。

image-6.png

これは結構厄介な問題です。「自動で入らないなら手動で入れよう」と思っても、正しい四分の空きを入れる“イイカンジの”方法がなかなか見つからないからです。もちろん、単純にASCIIの空白文字を入れてしまうと欧文空白(今の設定では三分)が入ってしまうので正しい結果になりません。

次に示す例では差異を明確にするため、数式の先頭の“T”を欧文の例に合わせて立体upright(T)にしています。

example-3.typ
#import "layout.typ": *
#show: doc

お客様の中にTypst芸人は…

お客様の中に $upright(T)_y (p_s dot tau)$ 芸人は…

image-7.png

もっとも「こんな僅かな差はほとんどの人は気にしない:blush:」のも確かでしょうし、この問題が将来のバージョンで解決される見込みも高い6ので、今のところはこれで妥協しておく:upside_down:という方策も考えられるでしょう。

しかし一方で日本語LaTeXでは和文と数式の間の空白自動挿入はしっかりサポートしています。よって「(日本語文書について)TypstがLaTeXの代わりになる:muscle:」と主張したい人にとっては、現状でも何らかの回避策が必要なのは間違いありません。

現状のTypstの問題を回避するやつ

というわけで、以降では「和文と数式の間に和欧文間空白が入らない」問題に対する回避策を検討していきます。自動で入らないのを解決するのは困難そうなので、まずは手動で入れることとし、以下の要件を定めます。

  • 正しい幅である四分の空きが入ること。
  • ソース上の記述が煩雑になることをできるだけ避けること。

四分空きのモノを作るやつ

正しい幅の空きを入れること自体は難しくありません。水平空きを生成するh()関数を利用して、マークアップモードで#h(0.25em)と書けば所望の四分空きが出力されます。

example-4.typ
#import "layout.typ": *
#show: doc

// "#h(0.25em)"で四分の空きが入る
お客様の中に#h(0.25em)$T_y (p_s dot tau)$#h(0.25em)芸人は…

image-8.png

求める出力は得られましたが、明らかにこの記述は煩雑すぎます。

h(0.25em)の結果を変数に保存しておくと毎回この式を書く必要がなくなります。できるだけ簡潔にしたいので、変数名も1文字qにしておきましょう。

example-5.typ
#import "layout.typ": *
#show: doc

// 四分の空きのコンテンツ
#let q = h(0.25em)

お客様の中に#q$T_y (p_s dot tau)$#q;芸人は…

※出力はexample-4.typと同じです。

数式の後ろの#q;;は、#で始まるスクリプトモードの式を強制的に終結させるためのものです。もし;を除いて#q芸人は…と書くとq芸人はという未定義の変数を参照しようとしてエラーになります。

かなりマシにはなりましたが、それでも「数式の前後のほぼ全てに#qが挟まっている」のはどうにも煩雑な感じがします。

ここで#q$a$#q;と#q$b$#q;を#q$n$#q;以下の自然数とする。

これはやはり実用には耐えられないですね:tired_face:

LaTeXでは\>のような「記号からなる命令名」が使うことで煩雑さを抑えられます7。また分割禁止欧文空白を表す~も(TeXレベルでは)再定義可能なので、CJKパッケージ8では「~を和欧文間空白のために再割当する」という“荒業”を用いています。残念ながらTypstではこういう芸当が使えないので「超簡潔に書く」というのが難しいのです。

四分空きの文字を書くやつ

先述の通り、Typstでは「1文字の記号にユーザ定義を割り当てる」ことはできないわけですが、でもよく考えてみると、Unicode文字の中には最初から​「四分の空き」という意味の文字が存在します。それがU+2005“FOUR-PER-EM SPACE”という文字です。

Typstでは任意のUnicode文字が使えて、しかもこのU+2005は“普通の文字”なのでそのまま出力されます。つまりU+2005の1文字で求める「四分の空き」が出せるわけです:slight_smile:

example-6.typ
#import "layout.typ": *
#show: doc

// 数式の前後にはU+2005が入っている
お客様の中に $T_y (p_s dot tau)$ 芸人は…

※出力はexample-4.typと同じです。

この方法の難点は「U+2005の文字の取扱が煩わしい」ということでしょう。実用的に利用するためには、使用するエディタにおいてU+2005が“イイカンジ”に入力できてかつ“イイカンジ”に表示されている必要がありそうです。でも条件が揃えば有望な方策になるかもしれません。

欧文空白が四分なフォントを使うやつ【オススメ①】

手動で空白を入れるという前提の下では「ASCIIの空白文字を入れる」というのが最も簡潔な記述であることは間違いありません。これが使えない原因は「欧文空白が四分ではないから」でした。

でも改めて考えると「欧文空白の幅」はフォントによって違います。だったら「欧文空白が四分であるフォント」を指定しておけばいいのでは?:thinking:

でも、そんなうまい欧文フォントがあるでしょうか……、実はあります。なんと、Typstの既定のフォント9である「Libertinus Serif」がまさに条件を満たしています:astonished:

「Libertinus Serif」を欧文フォントとして指定しておけば「ASCIIの空白文字を入れる」という簡潔な記述が通用するようになります。

example-7.typ
// "おやくそく"以外のレイアウト設定
#set page(paper: "a5")
#set text(
  lang: "ja",
  // 欧文フォントを"Libertinus Serif"にした
  font: ("Libertinus Serif", "Harano Aji Mincho"),
  top-edge: 0.88em,
)

// 再び欧文と数式を比較する

お客様の中にTypst芸人は…

// 数式は"New Computer Modern Math"なので書体は異なる
お客様の中に $upright(T)_y (p_s dot tau)$ 芸人は…

image-9.png

既定フォントである「Libertinus Serif」はTypstに内蔵されているため、Typstにおいて貴重な「必ず使えるフォント」の1つになっています。このため日本語文書でもこのフォントを使っている人は多いでしょう。実はその“よくある設定”の場合は今まで散々議論してきた「和欧文間空白の幅」の問題はそもそも生じていないのでした:upside_down::upside_down::upside_down:

欧文空白を無理やり四分にするやつ【オススメ②】

「欧文空白が四分であれば嬉しい」という考え方は「Libertinus Serif」(などの欧文空白が四分であるフォント)以外を使う場合にも応用できます。実は、Typstには「欧文空白の幅を変更する」機能が用意されています。textエレメントのspacingパラメタです。

  • spacing:[relative10;既定値=100%+0pt] 欧文空白の幅を表す。値は「現在フォントの属性で定められる標準の欧文空白幅」を基としたrelative値で指定する。

つまりspacingパラメタに0.25em(=0%+0.25em)という値を指定すると、フォントの欧文空白の幅(これが100%)が何であっても欧文空白の幅を0.25em(四分)に変更できます。

「New Computer Modern」フォントを用いた“おやくそく”の設定にこのspacing指定を適用してみましょう。

example-8.typ
// "おやくそく"の設定
#import "layout.typ": *
#show: doc
// さらにコレ
#set text(spacing: 0.25em)

// 再び欧文と数式を比較する

お客様の中にTypst芸人は…

お客様の中に $upright(T)_y (p_s dot tau)$ 芸人は…

image-10.png

欧文フォントが何であっても、「ASCIIの空白文字を入れる」という簡潔な記述が使えるようになりました:smiley:

それでも自動で和欧文間空白を入れたいやつ

前節では空白を手動で入れることを前提にして、正しい結果を得ることに注力してきました。しかし、最も理想的な解決はやはり「数式の前後に何も(空白文字も)書かなくても和欧文間空白が自動挿入される」ことでしょう。本節ではこれの実現可能性について検討してみます。

あかんやつ

和欧文間空白を自動で挿入する方法としてネット上で時折見かけるのが「数式(math.equationエレメント)に対して、前後に和欧文間空白を入れるようなshow規則を設定する」というものです。

example-9.typ
#import "layout.typ": *
#show: doc

// あかんやつ
#show math.equation.where(block: false): it => {
  h(0.25em); it; h(0.25em)
}

お客様の中に$T_y (p_s dot tau)$芸人はおられませんか。

image-11.png

この例では正しい結果が得られているように見えます。

しかしこの方法は結局うまくいきません:no_good: 何故かというと、この文書の最初で述べたように、数式の前後に必ず和欧文間空白が入るとは限らないからです。

example-10.typ
#import "layout.typ": *
#show: doc

// あかんやつ
#show math.equation.where(block: false): it => {
  h(0.25em); it; h(0.25em)
}

// 和欧文間空白が入らないはずの箇所に入ってしまう!
アレ($T_y (p_s dot tau)$)、つまり$T_y (p_s dot tau)$。

image-12.png

この例の出力結果は明らかに妥当ではありません。

補足

この問題に対する解決策として「現実の運用では和欧文間空白が入る場合が圧倒的で入らない場合は極めて少ないはずだから、自動で空きを入れるshow規則を適用した上で、入らない場合にその空きの効果を打ち消すための何らかの記述を書く規則にする」というアイデアが考えられます。しかしTypstで「空きの効果を打ち消す」のは困難だと思われます。

空きを消すこと自体はh(-0.25em)でできます。しかし数式の前後が元々行分割禁止の箇所になっていた場合、空きを入れることでその行分割禁止が破れてしまうことになり、これを防ぐ手立てがありません。(そもそも和欧文間空白が入らないパターンは「括弧の内側」など行分割禁止が同時に適用されていることが多いことに注意してください。)

結局、空きをが入らない箇所では「自動で入る空きを最初から抑止する」必要があるのですが、Typstでそういう処理を実装するのは、introspectionを駆使したかなり複雑なコードを実装する必要があると考えています。

やっぱりあかんやつ

どうやら和欧文間空白の自動挿入の処理をTypstのコードで“実装”するのはかなり無謀そうです。「Typstの既存の処理をうまく活用する」方策を考えると、「数式の部分を無理やり欧文であると認識させる」という方針が思いつきます。これは日本語LaTeXの世界では有名なトピックであり、解決策として「ゴースト挿入」という技法がよく知られています。

ゴースト」というのは「何らかの望ましい性質をもつ幅ゼロの見えない*文字」を指します。例えば「欧文ゴースト」は「和欧文混植処理において欧文文字のように振る舞うゼロ幅不可視文字」のことです。日本語LaTeXの世界ではU+200B“ZERO WIDTH SPACE”(に相当する文字)が欧文ゴーストとしてよく用いられます。

欧文ゴースト挿入をTypstでやってみましょう。数式に対するshow規則において、先ほどは「四分空きを両端に付ける」処理をしていましたが、今度は代わりに「U+200Bの文字を両端に付ける」処理を行います。

example-11.typ
#import "layout.typ": *
#show: doc

// いけるかもしれないやつ
#show math.equation.where(block: false): it => {
  let ghost = "\u{200B}" // 欧文ゴースト
  ghost; it; ghost
}

お客様の中に$T_y (p_s dot tau)$芸人はおられませんか。

早速コンパイルしてみましょう。

image-13.png

うまくいきません:sob:

これが失敗する理由は「U+200Bは​“欧文”の文字ではない」(制御文字の一種)だからです。TypstはUnicodeのマトモな知識を持っていて、現状の仕様では「ラテン文字・ギリシャ文字・キリル文字・算用数字の何れか11に該当する文字」を欧文文字と見なしているようです。

Unicodeのゼロ幅不可視文字はU+200Bの他にもありますが、そういう文字は当然特定の用字系には属しません。残念ながら、Typstでは欧文ゴーストとして使える文字は存在しません。

超絶アレなやつ

絶望的な状況:sob:になってしまいましたが、もう少し考えてみましょう。Typstは組版ソフトウェアなので欧文ゴーストの文字を出力する際に好きなフォントを選択できます。ということは「Typstが欧文文字と見なす文字」がゼロ幅不可視になっている“特別なフォント”を用意すればいいのでは?

……ということはコレの出番ですね:wink:

「Adobe Blank」はAdobeがOSSとして提供しているフォントで​「全ての文字がゼロ幅不可視」​というトッテモ素敵:upside_down:なデザインを持っています。これを使えば目的の「欧文ゴースト」が実装できそうです。

先ほどの欧文ゴースト挿入の実装で「U+200Bの文字」の代わりに「Adobe BlankのU+0375の文字」に変えてみます。

U+0375“͵”(GREEK LOWER NUMERAL MARK)はギリシャ語で使われる記号の一種です。欧文ゴーストの文字はPDFからコピペした場合に混入してしまうため、できるだけ目立たない記号である(かつ数式中で使われることがない)文字を選びました。

example-12.typ
#import "layout.typ": *
#show: doc

// 超絶アレなやつ
#show math.equation.where(block: false): it => {
  let ghost = text(font: "Adobe Blank", "\u{375}") // 欧文ゴースト
  ghost; it; ghost
}

お客様の中に$T_y (p_s dot tau)$芸人はおられませんか。

// 四分空きが入らないパターンも試してみる
アレ($T_y (p_s dot tau)$)、つまり$T_y (p_s dot tau)$。

この例をコンパイルするには「Adobe Blank」のフォントがTypstで使用可能になっている必要があります。(自分はTTF班のAdobeBlank.ttfを使いました。)

image-14.png

うまくいきました!:heart_eyes: やっぱり「Adobe Blank」の力は絶大(えっ:astonished:

まとめ

和文と数式の間の和欧文間空白を実現する、現状でオススメの方法は次の2つです。

  • 方法①:欧文空白の幅が四分である欧文フォント(Typstの既定の「Libertinus Serif」でよい)を使った上で空白文字を手動で入れる。
  • 方法②#set text(spacing: 0.25em)で欧文空白の幅を四分に変更した上で空白文字を手動で入れる。

Typstで和欧文間空白が完全にサポートされる日が早く来るといいですね:smiley:

  1. そのため、印刷業界では和欧文間空白のことを指して​「四分空き」​と呼びます。

  2. 括弧の外側については少し事情が複雑なのですが、組版的には“既に空きが入っている”ことになっているため、ここにも和欧文間空白は入りません。

  3. Typstの既定の数式フォントが「New Computer Moder Math」のBookウェイトであり、これと同じ書体・ウェイトのテキストフォントを選んでいます。ちなみに、この設定は奥村晴彦氏による記事「Typst入門」で紹介されているものです。

  4. テキスト用の「New Computer Modern」のRegularとBoldウェイトはTypstに内蔵されていますが、Bookはされていないので別途入手する必要があります。ファイル名でいうとNewCM10-Book.otfNewCM10-BookItalic.otfです。

  5. 現状ではcjk-latin-spacingの値はnone(空き無し)かauto(四分)の2通りしかないため、“プロポーショナル組”で「四分より小さい和欧文間空白を自動挿入する」という要求はサポートされていません。一方で「和欧文間空白を空けない」という選択はnoneを指定することで実現できて、しかもこの記事のメインの話題である「数式で和欧文間空白が自動挿入されない」という性質はそもそも問題にならなくなります。

  6. Typstの開発側でこの問題は既に認識されています。

  7. 実際にBXjsclsバンドルの文書クラスでは\>を「手動で和欧文間空白を入れる命令」と定義しています。

  8. pdfLaTeXでCJK言語を扱うためのパッケージです。和欧文間空白については、自動挿入する機能はないので~で手動で挿入するという規則になっています。

  9. Typstの既定の(初期状態で指定されている)フォントは0.12.0版において「Linux Libertine」から「Libertinus Serif」に変更されました。ただし後者は前者の派生フォントであるため書体としては同じです。そして欧文空白の幅についても両者ともに四分(0.25em)です。

  10. relative型はratio値とlength値の和として表される値を表します。例えば50%+10pt0%+2em等で、後者の値は2emとも書けます。つまり、ratio値やlength値はそのままrelative値として使用可能です。

  11. 実際には若干の記号を追加しています。

4
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?