⌨️

Typstで日本語文字と英数字のフォントを別々に指定する

2024/01/08に公開

日本語とアルファベットを同じテキストで用いること(和欧混植)は現代の組版では一般的な要件になっています。その際には、特に学術論文などにおいて、本文がどちらも明朝体・セリフ体であるのに対し、題名・章節の見出しでは日本語を太いゴシック体、欧文を太いセリフ体にする習慣があります。本稿では、新興組版エンジンTypstにおいての、漢字かなと英数字をそれぞれ分けて別々にフォントの指定を実装する方法を説明します。

実装方法

Typstでは正規表現が用意されているため、その実装はかなり簡単です。日本語のテキストに用いられている日本語を表記するための文字は、漢字、ひらがな、カタカナだけなので、それを全部選択して、フォントを指定させればよいわけです。

基本実装

#set text(font: "Noto Serif CJK JP") // 全体フォント指定(明朝体=セリフ体)
#show regex("[\p{scx:Han}\p{scx:Hira}\p{scx:Kana}]"): set text(font: "Noto Sans CJK JP") // 漢字かなカナのみ指定(ゴシック体=サンセリフ体)
*Typstにおける和欧混植のフォント設定法* // 太字にする

主要な部分はこれだけです。正規表現では、Unicode Script Propertyが提供するScript_Extension(略称scx)を用いることで、漢字・ひらがな・カタカナを選択しています。これを正規表現の中で使える理由は、TypstがRustで実装されており、正規表現エンジンとしてregexのクレート(パッケージ)が利用されています。ここで\pの文法がサポートされています#showルールとregex()関数を組み合わせることで、正規表現にマッチする文字のみのフォントをtext()関数によって指定することができます。

題名・見出し・柱

題名(タイトル、title)や見出し(heading)柱(ヘッダー/ランニングタイトル、header/running head)に関しては、それぞれにルールを与えれば良いのですが、Typstの内蔵機能としてタイトルを特別視しておらず、ご利用のテンプレートやパッケージのタイトル設定に挿入するか、テンプレートを利用せず自分で定義するかの二択です。また、ヘッダーはpage()関数の引数であるため、その引数専用の物を指定する必要があります。

#set text(font: "Noto Serif CJK JP") // 本文のフォント指定

#let mixed(body) = {
  set text(weight: "extrabold")
  show regex("[\p{scx:Han}\p{scx:Hira}\p{scx:Kana}]"): set text(font: "Noto Sans CJK JP", weight: "bold")
  body
} // 和欧混植のフォント別々指定

#set page(header: mixed[頁Header]) // 柱へ応用

#let title(body) = {
  set align(center)
  set text(size: 2em)

  v(1em)
  mixed(body) // タイトルへ応用
} // タイトルのスタイル設定
#title[Typstにおける和欧混植のフォント設定法] // タイトルの呼び出し

#v(1em)
#align(center)[#text(size: 1.25em)[混植 太郎]]
#v(1em)

#show heading: mixed // 見出しへ応用

= Typstのショールールを利用する // 見出しの呼び出し

#set par(justify: true)
#lorem(100)

参考文献

追加:環境によって異なるフォントを指定する場合

(2024/10/21 追加)

Typstでは、条件によって正規表現で二回以上置き換えることは正しい結果が得られない場合があるので、@hitsujikaip の質問に対し、以下のような回答を行いました。簡単に言えば、グローバルステートを設置し、必要な環境だけオンして、フォント設定する際にステートによってフォントを変えることによって回避することができます。

https://zenn.dev/saito_atsushi/articles/2b56f58c4fe3ca

#import "@preview/roremu:0.1.0": roremu

#let is-in-bold-environment = state("in-bold-environment", false)

#let default-font(body) = (
  context {
    set text(
      font: if is-in-bold-environment.get() {
        "Arial"
      } else {
        "Times New Roman"
      },
      weight: if is-in-bold-environment.get() {
        "regular"
      } else {
        "regular"
      },
      fill: if is-in-bold-environment.get() {
        blue
      } else {
        red
      },
    )
    body
  }
)

#let japanese(body) = (
  context {
    set text(font: if is-in-bold-environment.get() {
      "Noto Sans CJK JP"
    } else {
      "Noto Serif CJK JP"
    })
    body
  }
)

#show: context {
  show par: default-font
  show regex("[\p{scx:Han}\p{scx:Hira}\p{scx:Kana}]"): japanese
  body
}
#show heading: it => {
  is-in-bold-environment.update(true)
  it
  is-in-bold-environment.update(false)
}
#show strong: it => {
  is-in-bold-environment.update(true)
  it
  is-in-bold-environment.update(false)
}

= #lorem(1) #roremu(1)

#lorem(1) #roremu(1)

#strong[#lorem(1) #roremu(1)]

コミュニティ

「くみはんクラブ」というコミュニティを2024年1月に創設し、そこでTypstを始めとする様々な組版の話について議論されています。何かTypstなどを使う上で質問や不明点があれば、ぜひいらしてください。また、そのメンバーが中心となって、Typst Japan Communityを結成し、公式の認可を得てドキュメントの非公式日本語翻訳プロジェクトも始動しました。

https://discord.gg/dHRaAsBeRY

https://x.com/mkpoli/status/1751193383085973670

https://x.com/mkpoli/status/1827242838880408036

https://x.com/mkpoli/status/1858311758517326002

Discussion