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?

自作 Typst ライブラリのすすめ ~手に馴染む最強のレポートテンプレを考える~

Posted at

この記事は #UEC25アドベントカレンダー2025 その4 の 2日目です。
さまざまな電通大25生が参加するアドカレですので、その1~3 と併せてお楽しみください。

 1日目は AI さんの「今日は〇〇〇⚬〇〇の誕生日」、Narachi さんの「」(インフルお大事に)、しおり🔖 さんの「大学生になって携帯代を“自分で払うようになった人”へ」でしたね。

この記事では Typst というマークアップ言語についてお話します。

Typst を知っていて、テクニックを学びたい人は、この緑のブロックだけでも眺めていってください。

はじめに

 こんにちは、Ⅲ類の t5ugu (つぐ, @t5ugu_)と申します。
普段は技術系サークルの MMA や、非電源ゲーム研究会にて活動しています。Twitter は見る専です。

 後期中間試験に向けての勉強に追われている皆さんには申し上げにくいですが、試験が終われば冬タームが始まります。
この1年間の心労をねぎらうのも、冬タームに向けての予習も、並行して進めていきましょう!

 加えて、冬タームに基礎科学実験A を控えている方々は、夏タームでもすでに完遂されたかと思いますが、いろいろと大変でしたね。これから再び、と考えるとちょっと憂鬱ですよね。




ところで……




$\LaTeX$、使い続けますか?

 もちろん、半年以上付き添った仲でしょうから、それなりのサンクコストもあり、別のツールに移行しないほどには腰は重いでしょう。

 ただ、この記事を読んで、世の中には $\LaTeX$ しかない わけではない ことを、知ってほしいです。
しいては、お手元の Windows OS や iOS すら、義務じゃないことに気付ける日が来るかも。


 4月くらいに「基礎科学実験A なる科目があるらしい」「その科目では、パソコンで理系レポートを書くらしい」「レポートを書くためのツールとして、$\LaTeX$ を先輩から薦められた」という流れで、なんとなく使い始めた読者が多いのではないでしょうか。
逆に、宗教上の理由で $\LaTeX$ を使っている方は少ないと思います。

 そこで提案したいのが Typst [taɪpst] です。
冗長な記述が少なく、拡張性も十分で、加えてプレビューやPDF化がとても速いです。

 この機会に Typst ライフを始めてみませんか?

Typst 概論

始め方

 Typst の Playground や、いくつかのエディタ用の Typst 拡張機能 Tinymist などを使って、実際に手元で動かしながら、Typst を書くことをおすすめします。
詳しい誘導は省きますが、書き始めるまでに カップラーメンを作れないくらい、素早くセットアップできるでしょう。
必要に応じて、「Typst 入門」などで検索して、それっぽい 理解の助けになる記事を読むのもよいでしょう。

基本的な使い方

 おおむね、マークダウン記法に慣れているなら理解しやすい文法です。
知らないことは使っているうちに調べて学べばよいので、一部のみを抜粋して紹介します。

見出し

 \section{見出し1}\subsection{小見出し2} と書かなくても、= 見出し1== 小見出し2 で見出し(heading) が記述できます。

数字ブロック

 $x^2 + y^2 = 1$ と1行中に収めれば $x^2 + y^2 = 1$ のようにインラインになります。また、

$
  integral 1/x dif x = log x + C
$

のように、$ と内容の間に空白や改行があれば、

$$
\int \frac{1}{x} {\mathrm d} x = \log x + C
$$

というふうにブロックとして表示されます。
バックスラッシュ \ で始まる暗号、特に分数マーカー \frac が無いので直感的ですね。

テーブル

#table(columns: 3)[1][2][3][4][5][6]

とすれば、
image.png
のような表が書けます。
なお、デフォルトでは枠が付いてしまうので、後続セクションでカスタマイズしましょう。

大括弧で包んだ中身 [ ここ ] には content という型が割り当てられ、どのように描画されるべきかの情報が含まれています。

参照

 コンテンツに対して <TAG> のようにタグ付けし、本文中で @TAG と書けば、参照が可能です。

// 数式ブロックのタグ付け例
$
  ...
$<tag_math>

// テーブル要素のタグ付け例
#table()<tag_table>

文章内で@tag_math や@tag_table のように参照できます。

なお、標準では「式 1」と書かれてほしいのが「Equation 1」となってしまうので、ご愛嬌 練習問題として委ねます。

え?ちゃんと参照されるまでに 2 回コンパイルしなきゃいけないソフトがあるんですか?

その他の推しポイント

 編集とプレビューが同時におこなえます。
コンパイルボタンを押したり、コマンドを実行したりしなくても、目線を移動させるだけなのでうれしい。
Incremental compilation という、差分のみをコンパイルする仕様が、高速化を実現しています。

 また、プレビュー画面でトークン(≒文字)をクリックすると、ファイルの中のその文章がある場所に飛ぶことができます。
長い文章でも、プレビュー画面を見ながら推敲できるのは QoL 爆上げに違いありません。
※ 本文を描画するまでにユーザーによる処理が入る場合、うまく飛べないことがあります。かなしい。

自分好みにカスタマイズ

 この記事のタイトルは「自作 Typst ライブラリのすすめ」でしたね。
自分用のライブラリや理想のレポートの構想を膨らませるため、まずは Typst を $\LaTeX$ 製のレポートに 紛れ込ませる 方法を伝授いたします。
 その後、作ったものを別のファイルにまとめて、読み込むだけでテンプレとして機能する ライブラリを目指します。
目指した先、完成させるのはこの記事を読んだ画面の前のあなたです。

 まずは 温故知新 として、$\LaTeX$ っぽい PDF を出力できるようになりましょう。

用紙設定

 今はまだ おまじない で、後々効いてくるか、もしくは二度と編集しない可能性がある部分について、先に設定しましょう。
ファイルの先頭に次を書いてください。

// ページ数表示の凡例と、用紙
#set page(numbering: "1", paper: "a4")
// パラグラフのインデント
#set par(first-line-indent: (amount: 1em, all: true))
// 数字ブロックは基本的に番号を振る
#set math.equation(numbering: "(1)")

// 全角の句読点を、カンマ・ピリオドとして描画する
#show "、": ","
#show "。": "."

// figure の中身に応じてキャプション位置を変える
#show figure.where(kind: table): set figure.caption(position: top)
#show figure.where(kind: image): set figure.caption(position: bottom)

#set 文で、以降その関数を呼ぶときのデフォルトの引数を設定しています。リファレンスに Settable と書かれているパラメーターに対して設定できます。

#show A : B 文にはいくつかの使い方がありますが、今回は「A であるときには B と考える」の意味で使っています。
この記事の最後の方にもう一度登場します。

where 句でオブジェクトの特定のフィールドに対しての条件を指定できます。
例えば #figure() は説明している内容(図なのか表なのか)を、kind フィールドに持っています。

フォント

 何といってもフォントを似せないと始まりませんよね。
だいたいの Windows PC には Times New Roman や MS P明朝 というフォントが入っていると思うので、それらが 10pt で表示されるようにしてみます。
また、日本語を使うので lang: 'ja' も書いておきます。
Windows を使っていない人や、Web 版で試している人は、それっぽい選択可能なフォントを適時用いてください。
ファイルの初めに以下を書いてみましょう。

#set text(10pt, font: ("Times New Roman", "MS PMincho"), lang: "ja")

 使うフォントは、環境が読み込んでいるものなら自由に設定できます。
日本語のフォントもアラビア語のフォントも、英文字で管理されているので、探す際は注意しましょう。

このフォントの並び順は、先頭からフォントを探し、無ければ次のフォントを探し ...... それでも無ければ、利用可能なフォントを片っ端から探します。

英語と日本語のみなど、使う文字が限られているなら、どんなフォントが使われるかを自分で設定しておくと、扱いやすいですよ。

 もっと $\LaTeX$ 環境に寄せたいなら、

のような記事から、フォント名が何か調べるのもよいでしょう。

text() のリファレンス は、あなたのような知識欲にあふれた人を歓迎します

小話: 小かっこ

ただ小括弧で包んだ (Obj)Obj というように括弧が外れて解釈されますが、(Obj,)(Obj1, Obj2) などは配列オブジェクトとして解釈されます。
大括弧 [] が配列になるわけではないので、慣れるまでは気を付けてください。

maketitle のようなもの

 レポートのタイトル部分は定型文なので、変える必要がない場所は考えなくてよいようにするべきです。
ファイル上部にこんなのを追記してみてはどうでしょうか。

#let maketitle(タイトル, 作成: datetime.today(offset: 9), 更新: none) = {
  let 日付パターン = "[year]年 [month padding:none]月 [day padding:none]日"
  align(center)[
    #set text(font: ("Times New Roman", "MS UI Gothic"))
    #text(18pt)[#タイトル]
    
    #text(12pt)[電気通信大学 #math.space 0類\
      0123456 電通 太郎]

    #text(12pt)[#作成.display(日付パターン) 作成]\
    #if 更新 != none {
      text(12pt)[#更新.display(日付パターン) 更新]
    } else {linebreak()}
  ]
}

#maketitle("謁見テンプレ", 作成: datetime(year: 2025, month: 12, day: 2))

フォントや細部は #let} の部分だけを始めの一度だけ編集すれば、あとは必要箇所に一番下の行だけ書けばタイトルとして機能します。

image.png

#let は一連の処理をひとつにまとめる構文です。
今回はパラメーターありで定義しましたが、{} の中でやっているように、なしでも定義することができます。

また、その返り値は、いちばん最後の関数の返り値です。Ruby とかと同じですね。

マウスホバーで型のようなものが表示されますが、明示的に指定することはできない[要検証]です。

パラメーターに : ... があるものは、デフォルトの引数が設定されていて、その部分は呼び出す際に引数名を添えて代入しなければなりません。(例: 作成: datetime(...) と書いている)
逆に、無いものは呼び出しが簡潔に書けますが、保証がないということでもあります。(例: タイトルは タイトル: "謁見テンプレ" とは書かずに済んでいます)

# を先頭に付けている #let と、つけていないただの let があるように見えます。
これは、content 内では、ふつうの文章と区別するために # が必要なためです。
プレビューで余計なものが見えるときは、# のミスを疑うとよいです。

if cnd { thn } else if { thn2 } else { els } 構文にも少しだけ慣れが必要です。
いかなる場合も中括弧が必須です。

 LMS で事前学習したときに、表のフォーマットは口酸っぱく叩き込まれたことでしょう。
テンプレートを名乗るなら、表は書きやすいべきですよね。


(必要な要素を思い出す時間)


 表には必ずしも縦棒は要らず、1 行目は値と単位、2 行目以降はデータが並んでいるのでした。
完成品を見てみましょう。

#import "@preview/unify:0.7.1": unit

#let 表(caption, symbols, ..children) = {
  let column = symbols.map(it => if it.at(1) == "" { it.at(0) } else { $#it.at(0) space slash#unit(it.at(1))$ })
  figure(table(columns: column.len(), stroke: none, align: center,
    table.hline(stroke: 0.8pt),
    ..column,
    table.hline(stroke: 0.8pt),
    ..children,
    table.hline(stroke: 0.8pt)
  ), caption: caption)
}

#表("なんらか", (($x$, "mm"),($v$, "m/s^2")), [2.0], [1.0], [3.0], [2.0])

忘れないようにキャプションは先頭においてみました。
また、(記号, 単位) のペア(だと思い込んだもの)の配列 symbols を、column という 1 行目扱いの部分に変換しています。
「単位が空文字列なら単位を書かなくてよく、そうでないなら、書くべきだ」というルールについては、関数を呼ぶときに忘れていたらエラーになって教えてくれます。

table() のいくつかの機能を紹介します。
columns は列の数を指定します。
stroke は各セルに対する枠線を指定できますが、今回は none (何もなし)としています。
align: center は、各セルの要素がなるべく中央寄りになるように設定しています。
table.hline() は、「今埋まっているセルのうち、最後のセルの下側」に、テーブルを横断するような線を引きます。

標準でかなりたくさんの外部ライブラリが組み込まれています。

unify もそのひとつで、単位を簡単に書けるものです。
使い方は調べてください。

また、ほかにもいろいろあるので、ぜひ探して、試してみてください。

ライブラリ化

 ここまで準備してきた、いくつかの「$\LaTeX$ っぽい」上に自分が扱いやすい関数たちを、まとめて自分用ライブラリにしてしまいましょう。
新しく lib.typ みたいなファイルを作って、今までのを好きなように配置します。(エラーに怒られたら、指示に従ったり、置き換えたりしてください)
念のためプレビューしてみると、何も映っていないはずです。

 そして、それらすべてを #let load_lib(body) = {body } で囲ってしまいます。

lib.typ内

#import "@preview/unify:0.7.1": qty, unit

#let 表(caption, symbols, ..children) = {
  let column = symbols.map(it => if it.at(1) == "" { it.at(0) } else { $#it.at(0) space slash#unit(it.at(1))$ })
  figure(table(columns: column.len(), stroke: none, align: center,
    table.hline(stroke: 0.8pt),
    ..column,
    table.hline(stroke: 0.8pt),
    ..children,
    table.hline(stroke: 0.8pt)
  ), caption: caption)
}

#let maketitle(タイトル, 作成: datetime.today(offset: 9), 更新: none) = {
  let 日付パターン = "[year]年 [month padding:none]月 [day padding:none]日"
  align(center)[
    #set text(font: ("Times New Roman", "MS UI Gothic"))
    #text(18pt)[#タイトル]
    
    #text(12pt)[電気通信大学 #math.space 0類\
      0123456 電通 太郎]

    #text(12pt)[#作成.display(日付パターン) 作成]\
    #if 更新 != none {
      text(12pt)[#更新.display(日付パターン) 更新]
    } else {linebreak()}
  ]
}

#let load_lib(body) = {
  set page(numbering: "1", paper: "a4")
  set par(first-line-indent: (amount: 1em, all: true))
  set math.equation(numbering: "(1)")

  show "、": ","
  show "。": "."

  show figure.where(kind: table): set figure.caption(position: top)
  show figure.where(kind: image): set figure.caption(position: bottom)

  set text(10pt, font: ("Times New Roman", "MS PMincho"), lang: "ja")
  
  body
}

ここまで来たら、あとは読み込むだけです。
元のファイルに戻って、先頭行に

#import "lib.typ" : *
#show : load_lib

と書けば、はじめて Typst と会ったときのように白いながらも、何かレポートを書き始められる気がしませんか?
まず #maketitle() より始めよ!

今後できること

ここまで読んだ賢い読者なら、ノリでもっと便利にできるでしょう。

  • 不確かさを含む値を簡単に書けるようにしたり
  • 表の小数点をそろえたり1
  • 参照すると「図 1.4」や「式 (3)」と書かれるようにしたり2
  • データを入力するだけで平均値を取得できるようにしたり3
  • 数式ブロックのラベルが notag の時は通し番号をつけないようにしたり4

おわりに

 筆者は、MMA の「新入生向け $\LaTeX$ 講習会」にて、インストール待機中に Typst を知ってからずっと使い続けています。
そのせいで、コンリテのレポートを書いたり、MMA で部誌を書いたりする際に余計に勉強させていただきました……。

 この記事を読んで、何らかの発見を得られたなら幸いです。たとえそれが「$\LaTeX$ でも自作ライブラリ作れそう」であっても。

 #UEC25アドベントカレンダー2025 はまだまだ続きます。
並行して公開される 2日目の記事も、明日の 3日目の記事もぜひ読んでくださいね。
加えて、12/21 に公開予定の MMA Advent Calendar 2025 の記事もぜひ読んでいただけると幸いです。

最後まで読んでいただき、ありがとうございました。

文責: t5ugu

  1. #h() を使うことで、横方向にスペーシングが、#v() で縦方向にスペーシングができます

  2. refit.elementcontent 型であり、しかもその func() (構成子)が特定のものの場合... といったくらいに、重めです……

  3. #eval が利用できます

  4. counter への知見が必要です

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?