0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

tosaken1116Advent Calendar 2024

Day 2

typstを使って卒論の環境構築をする

Last updated at Posted at 2024-12-02

はじめに

tosaken1116 Advent Calendar 2024 2日目担当の土佐犬です

絶賛大学四年生でめちゃくちゃ忙しい日々を過ごしてる中記事を書く暇がありません

今回は卒論の環境構築をします

卒論の環境構築ってなんやって話ですが、大学生は4年生になると卒業するために卒業研究をし、その論文を書かなくてはなりません 理系は特に、

でその卒論というのは基本的にMicrosoft Word or Latexで書きます 

今回の話は前者Microsoft Wordで書く人には当てはまりません

私が所属する研究室ももれなくLatexで書くようですが、去年の今頃typstなるものをチラリと見かけていて使ってみたいなぁと思い、教授に直談判したところ「いいんじゃない?」ということで今回はtypstを使った卒論環境構築を作ろうと思います

ちなみに私はLatexも書いたことがありません

のでtypstでもLatexでもハードルは同じくらいです

今回作成したディレクトリはこちら

この記事で扱う話 扱わない話

扱う話
環境構築したよって話

扱わない話
typstの文法の詳細

typstとは

Latexより早くて簡単なドキュメントカキコツールだぜ!!って書いてありました

要件定義

要件としては以下のようなものを考えています

  • 手元でコンパイルできる
  • コンパイル済みファイルを外部に共有できる
  • バージョン管理ができる

バージョン管理に関してはgitでどうにでもなります

手元でコンパイルに関してもtypst cliでできそうです

コンパイル済みファイルを外部に共有することが今回の環境構築の主な内容になりそうです

環境構築

typstファイルを書く

typstはbrewで入れられます

brew install typst
typst --version
typst 0.12.0

まだベータ版なのかメジャーバージョンは0です

typstはinitコマンドを使ってテンプレートを呼び出して作成することができますが今回は1から構築していきます

ディレクトリ構成ですが以下のようになっています

.
├── .cz-config.js // コミットフォーマット
├── .czrc         // コミットフォーマット
├── .github         
│   └── workflows
│       └── build.yaml
├── .gitignore
├── README.md
├── Taskfile.yml
├── docs                       // ユーザーが書き込む部分
│   ├── abstract.typ
│   └── chapters
│       └── chapter1.typ
├── env.typ            // 環境変数
├── init.sh                    // 環境構築スクリプト
├── lefthook.yml               // lefthook用(pre commit)
├── libs                       // 文書のフォーマット定義
│   ├── abstract.typ
│   ├── config.typ
│   ├── cover.typ
│   ├── index.typ
│   └── template.typ
└── main.typ           // main

まずはmain.typを作ります

#import "libs/cover.typ": cover
#import "libs/abstract.typ": abstract
#import "libs/config.typ": view
#import "libs/index.typ": index
#show: view.with()

// 表紙
#cover()
#counter(page).update(1)
#set page(numbering: "i")

// アブストラクト
#abstract()

// 目次
#index()

#set page(numbering: "1")
#counter(page).update(1)


#let chapters = ("docs/chapters/chapter1.typ",)

#for chapter in (chapters) {
  pagebreak()
  include (chapter)
}

typstは#importを使って他のtypstファイルを参照できます

ここではlibsディレクトリのcover, abstract config indexを呼び出しています

それぞれのファイルには以下が定義されています

  • cover: 表紙テンプレート
  • abstract: アブストラクトテンプレート
  • config: 文書全体に適用する設定
  • index: 目次テンプレート

#show文は関数を受け取りその関数を用いて文書を表示します

ここではconfigを用いて続く文書を表示するようにしています

config.typは次のように書いています

#let view(doc) = {
  set text(font: "IPAMincho", 12pt) // テキストの設定
  set par(                          // 文章コンテンツの設定
    first-line-indent: 1em,
    spacing: 0.65em,
    justify: true,
  )
  set heading(numbering: (..nums) => { // 章のフォーマット
    let numbers = nums.pos()
    if numbers.len() == 1 {
      numbering("第1章 ", ..numbers)
    } else {
      numbering("1.1 ", ..numbers)
    }
  })

  show heading.where(level: 1): set block(above: 40pt, below: 50pt) // 章のマージン
  show heading.where(level: 2): set block(above: 30pt, below: 15pt)
  show heading.where(level: 3): set block(above: 30pt, below: 15pt)
  show heading.where(level: 1): set text(size: 24pt) // 章のテキストサイズ
  show heading.where(level: 2): set text(size: 16pt)
  show heading.where(level: 3): set text(size: 12pt)
 
  set page(  // 全ページ共通の設定
    paper: "a4",
    margin: (
      top: 40mm,
      bottom: 40mm,
      left: 35mm,
      right: 20mm,
    ),
  )

  doc
}

まだ中身は最低限のものしか書いていませんが今後必要になったら足していこうと思います

続いてcover関数の呼び出しを行っています

cover.typは次のように記述しています


#import "../env.typ": env

#let cover() = {
  set align(center)
  set text(18pt)

  v(20mm)
  [#text(size: 20pt)[卒~~業~~論~~文]]

  v(15mm)
  box(width: 145mm)[
    #align(center)[#env.thesis_title]
  ]
  v(40mm)
  stack(
    dir: ttb,
    spacing: 3mm,
    text(size: 18pt)[#env.college_name #env.faculty_name],
    text(size: 18pt)[#env.department_name],
  )
  v(15mm)
  [#env.student_name]
  v(25mm)
  [#env.year_of_grad 年度]
  v(10mm)
  [指導教員:#env.supervisor_name]
  pagebreak()
}

env.typから特定の値を呼び出し表紙として表示する関数です

その次のページ数の表示をしています

表紙にはページ番号をつけたくないため表紙を表示した後に関数を実行し表示するようにしています

#counter(page).update(1)
#set page(numbering: "i")

またこの時のページ番号はローマ数字表記にしています

次にabstractとindexです

abstractとはその論文の概要を示すセクションです

// アブストラクト
#abstract()

// 目次
#index()

abstract.typではdocsディレクトリの中のabstract.typファイルから文章を読み出し、それを特定のフォーマットに基づいて表示するようにしています

とはいってもそこまで大きな設定はないので冗長といえば冗長です

includeを使ってtypstファイルから文書全体を取り出して表示しています

libs/abstract.typ
#let abstract() = {
  align( //文書を真ん中寄せで表示
    center,
    text(size: 12pt)[概~要],
  )
  v(4mm)
  [#include "../docs/abstract.typ"] // docs/abstract.typをinclude
  pagebreak() // 次の文書が次のページに来るようにする
}

index.typではそれ以降に表示する文書の目次を表示する関数を定義しています

libs/index.typ
#let index() = {
  show outline.entry.where(level: 1): it => { // 最も大きい章の表示設定
    v(12pt)
    text(size: 12pt, it)
  }

  show outline.entry.where(level: 2): it => { // 2番目に大きい章の表示設定
    text(size: 12pt, it)
  }

  v(10mm)
  outline( // 目次の表示
    indent: 2em,
    title: box([
      #text(size: 24pt)[目~~次]
      #v(10mm)
    ]),
  )
}

目次を表示したらもう一度ページ番号をリセットします

#set page(numbering: "1")
#counter(page).update(1)

これは目次の次のページからページ番号を始めるためです

なおこの時の数字表記はアラビア数字を用いています

そして最後にchapterを表示しています

chapters配列にインポートしたいファイルを定義してそれをfor文を用いて表示しています


#let chapters = ("docs/chapters/chapter1.typ",)

#for chapter in (chapters) {
  pagebreak()
  include (chapter)
}

最後にユーザーが文書を書く部分ですがenv.typではユーザーに関するデータなどを定義します

env.typ
#let student_name = "著者の名前"
#let student_id = "00000000"
#let supervisor_name = "指導教員の名前"
#let thesis_title = "卒論のタイトル"
#let year_of_grad = "2023"
#let college_name = "大学名"
#let faculty_name = "学部名"
#let department_name = "学科名"

#let env = (
  student_name: student_name,
  student_id: student_id,
  supervisor_name: supervisor_name,
  thesis_title: thesis_title,
  year_of_grad: year_of_grad,
  college_name: college_name,
  faculty_name: faculty_name,
  department_name: department_name,
)

またdocsディレクトリ内のchapterやabstract.typでは次のように書くだけで文書が表示されます

abstract.typ
アブストラクト書くとこ
chapters/chapter1.typ
= はじめに // typstでは = を用いて章を書きます

これはテスト用のドキュメントです

これで一通りのtypstファイルの定義を作ったのでコンパイルします

typst compile main.typ

また文書が変わったら動的にコンパイルするwatchコマンドもあります

typst watch main.typ

そしてコンパイルされた文書が以下です

image.png

image.png

image.png

image.png

いい感じにできました

とはいっても私はまだ卒論を書いたことがないのでこれはサークルの先輩に見せていただいた論文を模したものです

周りのツールなど

typstコマンドを毎回全部打つのは面倒くさいのでtaskfileを作ります

taskfileはgo製のツールTaskのコマンド定義ファイルです

といってもここでは詳しくは触れません

公式サイト
https://taskfile.dev/

taskfile.yml
version: "3"

tasks:
  default:
    desc: Run the default task
    cmds:
      - task -l

  compile:
    desc: Builds the typst document
    deps:
      - fmt
    cmds:
      - mkdir -p exports && typst compile main.typ exports/main.pdf
  compile:watch:
    desc: Builds the typst document on change
    cmds:
      - mkdir -p exports && typst watch main.typ exports/main.pdf
  commit:
    cmds:
      - cz
  fmt:
    desc: Format all typ files
    cmds:
      - typstyle format-all libs
      - typstyle format-all main.typ
      - typstyle format-all env.typ
      - typstyle format-all docs
    silent: true

今回はcompile compile:watch commit fmtの四つのコマンドを定義しました

commitにはcommitizenという特定のフォーマットに基づいたコミットができるツールを使っています

またtypstにはデフォルトではフォーマットコマンドがないので有志のパッケージtypstyleを用いています

typstyle
https://github.com/Enter-tainer/typstyle

github actionsの設定

最後にgithub actionsの設定です

一度最初に作った時に出てきたpdfが以下のようなものです

image.png

豆腐がたくさんありますね

github actionsで使用されるubuntuにはデフォルトでは日本語フォントが入っていないためこのような表記になります

ということでそれを加味したactionが以下です

.github/workflows/build.yaml
name: Build Typst document
on:
  push:
    branches:
      - main
    paths:
      - docs/**/*
      - main.typ
      - env.typ

env:
  GOOGLE_DRIVE_UPLOAD_DIRECTORY: /research/report
  TZ: Asia/Tokyo

jobs:
  build_typst_documents:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v3

      - name: Cache Typst binary
        id: cache
        uses: actions/cache@v3
        with:
          path: ~/.cargo/bin/typst
          key: ${{ runner.os }}-typst-bin-${{ hashFiles('**/Cargo.lock') }}
          restore-keys: |
            ${{ runner.os }}-typst-bin-

      - name: Set up Typst
        if: steps.cache.outputs.cache-hit != 'true'
        run: |
          sudo apt update -y
          sudo apt install -y fonts-ipafont
          cargo install --git https://github.com/typst/typst --locked typst-cli

      - name: Verify Typst Installation
        run: typst --version

      - name: Build Typst Document
        run: typst compile main.typ

      - name: Get current date in JST
        id: date
        run: |
          export TZ=$TZ
          echo "DATE=$(date +%Y-%m-%d-%H:%M)" >> $GITHUB_ENV

      - name: Rename file
        run: mkdir exports && mv main.pdf exports/${{ env.DATE }}.pdf

      - name: Upload to Google Drive
        uses: adityak74/google-drive-upload-git-action@main
        with:
          credentials: ${{ secrets.GOOGLE_DRIVE_SECRET }}
          filename: "exports/*.pdf"
          folderId: ${{ secrets.GOOGLE_DRIVE_FOLDER_ID }}

ubuntuからtypstを入れるにはRust経由が良さそうだったのでRustをインストールしそこからtypstをインストールしています

またtypstでコンパイル後google driveにアップロードして他のユーザーからも参照できるようにします

なおgoogle driveにアップロードするためにはGoogle Cloudでプロジェクトを作りgoogle drive apiにアクセス可能なサービスアカウントを作り、自分のフォルダにそのサービスアカウントを招待することが必要です

ちょっとめんどくさいです

image.png

ということでできました

リポジトリにpushするだけでpdfができるactionsの完成です

終わりに

ということで今回はtypstでテンプレートを作った話でした

色々忙しくて卒論を書くのはちょっと大変ですが、卒業できるように頑張ります...

0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?