Typst における関数とルール

そろそろ Typst の関数とルール設定について,そろそろちゃんと調べようと思って。 本当は「Typst に関する雑多な話」で軽く触れるだけにするつもりだったが,思ったより量があったので記事を立てることにした。

関数の定義と呼び出し

まずは一番簡単な関数を考えてみる。

#let fnc(it) = {
  it
};

これは引数をそのまま返すだけの関数である。 この関数の引数に "Hello" を渡して呼び出すと

#fnc("Hello")

引数の内容がそのまま出力される。

関数の定義と呼び出し (1)

引数には(出力可能なものであれば)なんでも渡せる。 数値を入れることもできるし,何なら関数を入れ子にしてもよい。

#fnc(fnc("Hello"))

もうひとつ。 Typst の関数呼び出しではコンテントブロック(角括弧 [ ... ] で囲まれる領域)を関数呼び出しの後ろに付けることができる。 これをコンテント引数(content argument)と呼ぶ。

#fnc[Hello]

コンテント引数(content argument)は呼び出された側では content 型の引数としてセットされる。 つまり #fnc("Hello")#fnc[Hello] は(strcontent の型の違い以外は)ほぼ同じ機能である。

ついでの話として,呼び出す側はコンテント引数を複数並べることができる。

#fnc2[Hello][world]

この場合,呼び出される側は以下のように定義する。

#let fnc2(..it) = {
  it
}

#fnc2[Hello][world]

このときの出力結果は以下の通り。

関数の定義と呼び出し (2)

itarguments 型で受けているのが分かる。 arguments 型は pos メソッドで array 型に変換できる。

名前付き引数

次は文字列を色付きで出力することを考えてみる。 関数定義は以下の通り。

#let colorText(color: red, it) = {
  text(fill: color)[#it]
}

color: red は名前付き引数(named parameter)と呼ばれる。 名前付き引数には既定値がつく(上述のコードでは red が既定値)。 この関数の呼び出しは以下のように書く。

#colorText[Hello] #colorText(color: blue)[world]

名前付き引数は省略可能で省略した場合は既定値になる。 もちろん

#colorText("Hello") #colorText(color: blue, "world")

などと書くこともできる。 出力結果は同じで以下の通り:

関数の定義と呼び出し (2)

Show ルール

show キーワードを使って指定した対象に対し show ルールを設定できる。

#let colorText(color: red, it) = {
  text(fill: color)[#it]
}

#show heading: it => colorText(color: blue, it)

= Heading 1

== Heading 1.1

上のコードの itshow キーワードで指定した対象を無名関数のコンテント引数として渡したもの。 全ての関数で共通に用意されている with メソッドを使うことで以下のようにコンテント引数を省略できる。

#let colorText(color: red, it) = {
  text(fill: color)[#it]
}

#show heading: colorText.with(color: blue)

= Heading 1

== Heading 1.1

出力結果はいずれも同じでこんな感じ。

Show ルール設定 (1)

Show ルールの対象は(出力可能なものなら)なんでもよくて,たとえば

#let colorText(color: red, it) = {
  text(fill: color)[#it]
}

#show "Hello": colorText

Hello World

みたいな記述もできる。 なお,コンテント引数以外に引数がない(または省略できる)場合は,上のように関数名だけを指定できる。

このコードの出力結果は以下の通り:

Show ルール設定 (2)

Show ルールの対象には selector も指定できる。 たとえば where メソッドを使って以下のように記述できる。

#let colorText(color: red, it) = {
  text(fill: color)[#it]
}

#show heading.where(level: 1).or(heading.where(level: 2)): colorText

= Heading 1

== Heading 1.1

=== Heading 1.1.1

出力結果は以下の通り:

Show ルール設定 (3)

Show ルールの対象がない場合,文書全体が対象となる。

#let colorText(color: red, it) = {
  text(fill: color)[#it]
}

#show: colorText.with(color: blue)

#lorem(40)

ちなみに lorem は指定した単語数のランダムな文章(欧文1)を生成する関数である。

出力結果は以下の通り:

Show ルール設定 (4)

Show ルールは逐次処理されるのか

Show ルールがどのように効いてくるか試してみた。

まずはこれ。

#let colorText(color: red, it) = {
  text(fill: color)[#it]
}

= Heading 1

== Heading 1.1

#show heading: colorText

= Heading 2

== Heading 2.1

出力結果は以下の通り:

Show ルール設定 (1b)

まぁ,これは予想通り。

次はこれ。

#let colorText(color: red, it) = {
  text(fill: color)[#it]
}

= Heading 1

== Heading 1.1

#show heading: colorText

= Heading 2

== Heading 2.1

#show heading: it => colorText(color: blue, it)

= Heading 3

== Heading 3.1

希望としては 黒 → 赤 → 青 の順で変わってほしいのだが…

Show ルール設定 (1c)

んー。 ひょっとして show ルールって遡って適用されるのだろうか。 外部パッケージを import したときに show ルールが衝突したら面倒なことになりそう。

Show ルールの設計はちょっと気をつけないといけないかもしれない。

Set ルール

textimage あるいは headingpar などドキュメント要素に紐づく組込み関数は「要素関数(element function)」と言うそうな。 要素関数は set キーワードを使い set ルールで名前付き引数の既定値を変更することができる。

Some functions are associated with elements like headings or tables. When called, these create an element of their respective kind. In contrast to normal functions, they can further be used in set rules, show rules, and selectors.

ドキュメントのルートで set ルールを指定すると,逐次処理で指定位置以降に適用される。

#set text(font: "Noto Serif CJK JP", lang: "ja")

明朝体

#set text(font: "Noto Sans CJK JP", lang: "ja")

ゴシック体

出力結果は以下の通り:

Set ルール設定 (1)

Show ルールの中で set ルールを指定した場合,その show ルールの中でのみ set ルールが適用される。

#set text(font: "Noto Serif CJK JP", lang: "ja")
#show heading: it => {
  set text(font: "Noto Sans CJK JP")
  it
}

= ゴシック体

明朝体

set キーワードのみの記述ならコンテント引数を省略して

#set text(font: "Noto Serif CJK JP", lang: "ja")
#show heading: set text(font: "Noto Sans CJK JP")

= ゴシック体

明朝体

などと記述することもできる。

出力結果はいずれも同じでこんな感じ。

Set ルール設定 (2)

なお let キーワードで定義したユーザ関数は要素関数ではないので set ルールは使えない

文書ファイルを分割した際のルール設定

文書ファイルを分割して importinclude で読み込む場合,子ドキュメントで設定した set および show ルールは親ドキュメントには適用されないので注意(親ドキュメントから子ドキュメントへはルールが継承される)。 子ドキュメントで記述した set および show ルールを親ドキュメントに適用させるには(テンプレートで使う手法)ルールを記述した関数を import で読み込み,親ドキュメントの show ルールを使って関数を呼び出す。

#let initFonts(font-name: "New Computer Modern", body) = {
  set text(
    lang: "ja",
    font: font-name,
    size: 10pt,
  )
  body
}
子ドキュメント
#import "child.typ": initFonts

#show: it => initFonts(
  font-name: "Noto Sans CJK JP",
  it
)

こんにちわ、世界!
親ドキュメント (1)

または with メソッドを使って

#import "child.typ": initFonts

#show: initFonts.with(
  font-name: "Noto Sans CJK JP",
)

こんにちわ、世界!
親ドキュメント (2)

などとコンテント引数を省略できる。

ブックマーク

ブックマークは「Typst に関するブックマーク」にてまとめています。

参考文献

photo
Typst完全入門: LaTeXより簡単、Markdownより強力、美しいドキュメント作成術
doitsu (著)
2024-12-08 (Release 2024-12-08)
Kindle版
B0DPXBNTRS (ASIN)
評価     

マークアップ言語および組版ツールである Typst についての解説。 Kindle 版のみの提供。固定レイアウトではないためレイアウトが崩れまくって読みにくい。この手の技術解説書は固定レイアウトの Kindle 版か,いっそ PDF で出してほしい。でも Typst についてまとまった解説のある日本語の本は他に見当たらなかったのでありがたい。

reviewed by Spiegel on 2025-02-20 (powered by PA-APIv5)


  1. 日本語の文を生成する roremu パッケージを公開されている方もいる。感謝! ↩︎