電気工学博士の雑記

電気工学の博士が徒然なるままに試した技術的なことをアウトプットする。

Typst 備忘録 -自作書式のサンプル-

概要

Typst で自作書式として、電気学会全国大会のテンプレートを作成してみた。 ここでは、Typstで記述した自作書式を紹介する。 Typst については下記のサイトが詳しい。

Typstで作成した電気学会全国大会のテンプレートの出力 (令和6年電気学会全国大会 | 一般社団法人 電気学会

背景

最近 Latex の代替として Typst が流行っているので、ブラウザ上のオンラインエディタ(https://typst.app)を触ってみた。 Latexよりも自作の書式が作りやすいと感じたため、Typst での自作書式の作り方を学習する目的で何かテンプレートを作ることにした。 いい題材が思い浮かばなかったので、電気学会全国大会のテンプレートを作成することとした。

コード

  • libs/ieej_annual_template.typ
#let paperBuilder(
  doc, 
  title: "", 
  authors: "", 
  etitle: "", 
  eauthors: "",
  font: "Liberation Sans",
  mathfont: "New Computer Modern Math"
) = {
  // ページ設定
  set page(
    paper: "a4",      // 用紙サイズ
    flipped: false,   // 縦用紙 
    numbering: none,  // ページ番号は不要
    margin: (         // 余白設定
      top: 30mm,
      bottom: 27mm,
      x: 18mm
    )
  )
  // 段落設定
  set par(
    justify: true,          // 行調整を有効
    leading: 5pt,           // 行間
    first-line-indent: 1em  // 行頭下げ
  )
  // 段落間距離の設定
  show par: set block(spacing: 0.65em)
  // テキスト設定
  set text(
    font: font,               // フォント
    size: 9pt,                // フォントサイズ
    lang: "jp"                // 言語
  )
  // 数式フォント
  show math.equation: set text(font: mathfont, style: "italic")
  // 表題番号の設定
  set heading(numbering: "1")
  show heading: it => {
    if it.level == 1 {        // 最上段の表題番号
      set text(size: 10pt, weight: "bold") 
      block(counter(heading).display() + sym.dot.basic + sym.space.quad + it.body, width: 100%, spacing: 0pt)
    }
    else if it.level == 2 {   // 2段目の表題番号
      set text(size: 9pt, weight: "regular")
      block(height: 0pt, spacing: 0em, above: 0em, below: 0.65em)  // 行頭下げを無効化するために挿入
      box(sym.lt + counter(heading).display("1・1") + sym.gt + it.body + sym.space.quad + sym.space.quad)
    }
    else {                    // 3段目以降の表題番号
      emph(it.body)
    }
  }
  // 数式番号の設定
  set math.equation(numbering: "(1)")
  // 図表リファレンスの設定
  set ref(supplement: it => {
    if it.kind == image {       // figureのcontentの種類がimageの場合
      "図"
    }
    else if it.kind == table {  // figureのcontentの種類がtableの場合
      "表"
    }
  })
  // 式リファレンスの設定
  show ref: it => {  // 詳細 https://typst.app/docs/reference/model/ref/
    let eq = math.equation
    let el = it.element
    if el != none and el.func() == eq {
      // Override equation references.
      numbering(
        el.numbering,
        ..counter(eq).at(el.location())
      )
    } else {
      // Other references as usual.
      it
    }
  }
  
  let titleBuilder(title, authors, etitle, eauthors) = {
    // 日本語タイトル
    align(center, 
      block(width: 134mm, height: auto, spacing: 28pt, 
      {
        text(title, size: 18pt)
      })
    )
    // 日本語著者名、英語タイトル、英語著者名
    align(center, 
      block(width: 100%, height: auto, spacing: 14pt, 
      {
        text(authors, size: 12pt) 
        linebreak() 
        linebreak()
        text(etitle, size: 9pt) 
        linebreak()
        text(eauthors, size: 9pt) 
        linebreak()
      })
    )
  }

  // タイトル等の作成
  titleBuilder(title, authors, etitle, eauthors)
  // 二段組の文章の作成
  columns(2, doc, gutter: 7mm)
}

// 2段キャプションの図表
#let twoCaptionFigure(fig, caption, ecaption) = {
  show figure.caption: none
  show figure.where(kind: table): (it) => {
    align(center)[
      表#counter(figure.where(kind: table)).display()#sym.space#caption \
      Table#counter(figure.where(kind: table)).display()#sym.space#ecaption \
    ]
    it
  }
  show figure.where(kind: image): (it) => {
    it
    align(center)[
      図#counter(figure.where(kind: image)).display()#sym.space#caption \
      Fig.#sym.space#counter(figure.where(kind: image)).display()#sym.space#ecaption \
    ]
  }
  
  fig
}
  • main.typ
#import "libs/ieej_annual_template.typ" as ieej_annual_tpl
#import "@preview/tablex:0.0.7": tablex, hlinex

// 文章に ieej_annual_tpl を適用
#show: doc => ieej_annual_tpl.paperBuilder(
  doc, 
  title: "電気学会全国大会講演論文の書き方", 
  authors: [研究 花子#sym.ast.basic,電気 太郎,学会 次郎 (○○○大学)],
  etitle: "Preparation of Papers for National Convention of I.E.E JAPAN",
  eauthors: "Hanako Kenkyu, Taro Denki, Jiro Gakkai (○○○University)"
)

= まえがき
発表論文原稿は、A4原寸で印刷されます。執筆の時は以下
の説明をよく読んだ上で、お使いのワードプロセッサ等で可
能な範囲で指示に従って原稿をお書きください。なお、この
説明書は、講演論文のレイアウトの見本になっていますので
、参考にしてください。

= 原稿提出について
- 15年大会からPDF投稿を可能としました。
- 紙面投稿の場合専用の原稿用紙はありません。お手持ちのA4判白色の上質紙に印刷してください。
- 提出いただいた原稿は、DVD-ROMを作成する際の原版として使用します。
- DVD-ROMでは,標準サイズで見えない文字等でも拡大により出現することがありますので,個人情報等に関するものの記載等の無い様,十分にご注意下さい。なお,電気学会著作権規程第6条には(著作者の責任)として,「本会が編集または発行する著作物の内容については,その著作者自身が責任を負うものとする。」としています。

= レイアウトと文字サイズ
== マージンとカラム幅
原稿用紙のマージンおよびカラム幅(全ページ共通)は、表1のとおりです。
特に上下左右のマージンは厳守してください。
2カラム(2段組)とし、各コラムの幅、カラム間マージンは表1のとおりです。
本文の字詰は、1行あたり26文字程度とします。

#let tbl1 = tablex(
  columns: (1fr, 1fr),
  align: (left, center),
  auto-lines: false,
  hlinex(), 
  [上マージン], [30mm],
  [下マージン], [27mm],
  [左右マージン], [18mm],
  [カラム間マージン], [7mm],
  [カラム幅], [83.5mm],
  hlinex()
)

#ieej_annual_tpl.twoCaptionFigure(
  [#figure(tbl1, kind: table) <table:table1>], 
  "マージン", 
  "Margins"
)

#let tbl2 = tablex(
  columns: (1fr, 20%, 20%),
  align: (left, center),
  auto-lines: false,
  hlinex(), 
  [], [サイズ], [行送り],
  [論文タイトル], [18pt], [28pt],
  [著者名], [12pt], [18pt],
  [英文タイトル著者所属名], [9pt], [14pt],
  [章タイトル], [10pt], [20pt],
  [本文], [9pt], [14pt],
  [参考文献], [8pt], [12pt],
  hlinex()
)

#ieej_annual_tpl.twoCaptionFigure(
  [#figure(tbl2, kind: table) <table:table2>], 
  "文字サイズ", 
  "Type sizes"
)

#colbreak()
分量は、図面、写真等を含めて1枚ないし2枚、シンポジウムは4枚以内です。

== 配置
表題等は、この見本に従って次の①?④の
順序で記載し、本文を書き始めてください。(2ページ目以
降は、①?③不要)文字サイズと行送りは、表2を参考にし
てください。
#[
  #set par(first-line-indent: 0pt)
  / ①: 表題:第1行中央に2カラム通しで書く(長ければ第2行も使う。第1行で済めば、第2行目は詰める)。表題1行目の左に、講演番号のスペースをあける。(テンプレートにおいては講演番号のスペースは設定してある)
  / ②: 著者名および勤務先:表題の下を1行あけて、次の行から中央に2カラム通しで書く。講演者名の右肩に「*」印を付ける。
  / ③: 英文表題、氏名(所属):著者名および勤務先の下を1行あけて、次の行から中央に2カラム通しで書く。
  / ④: 本文:英文による表題、氏名の下を1行あけて、次の行から書く。2ページは、上マージンに続いて第1行から本文を書く。
]

== 文献  
文献は本文末尾に通し番号を付けて一括記載し、本文中の該当個所に引用番号を付けてください。
文献の記載方法は、著者名、雑誌名、ページ、発行年の順序にしてください。

== 式および図  
式および図は、@fig:figure1 および以下の記載例を参考にしてください。
図面等を貼り付ける場合は、しわにならないように注意してください。
また、図や表の表題は和英両語で図表の下に記載してください。
図表中の説明は原則として英語としてください。

$ E = R I $ <eq:eq1>
$ V = R i + L (d i) / (d t) $ <eq:eq2>

#ieej_annual_tpl.twoCaptionFigure([
  #figure(
    image("figure.png", width: 70%), 
  )<fig:figure1>],
  "図面の例", 
  "An example of figures"
)

#colbreak()

= 表について
電気学会のテンプレートでは表両端のストロークや項目間のストロークがないです。
しかし現在のTypstのデフォルトでは表のストロークを詳細に制御できませんので、ここでは公式ドキュメントにしたがってtablexパッケージを使用して表を描画しています#link("https://typst.app/docs/reference/model/table/#:~:text=Note%3A%20Richer%20stroke%20customization%20for%20individual%20cells%20is%20not%20yet%20implemented%2C%20but%20will%20be%20in%20the%20future.%20In%20the%20meantime%2C%20you%20can%20use%20the%20third%2Dparty%20tablex%20library.")[#text(blue, "(Note: Richer stroke customization)")]。
tablexを使用するときは、キャプションの関係で、"\#figure(.., kind: table)"のようにkindをtableに指定してください。

デフォルトの"\#table"で記述した場合は下記のようになります。

#ieej_annual_tpl.twoCaptionFigure([
  #figure(
    table(
      columns: (1fr, 1fr), 
      align: (center, center),
      [上マージン], [30mm],
      [下マージン], [27mm],
      [左右マージン], [18mm],
      [カラム間マージン], [7mm],
      [カラム幅], [83.5mm],
    ), 
    caption:"マージン"
  ) <table:table3>], 
  "マージン", 
  "Margins"
)

#ieej_annual_tpl.twoCaptionFigure([
  #figure(
  table(columns: (1fr, 20%, 20%),
    [], [サイズ], [行送り],
    [論文タイトル], [18pt], [28pt],
    [著者名], [12pt], [18pt],
    [英文タイトル著者所属名], [9pt], [14pt],
    [章タイトル], [10pt], [20pt],
    [本文], [9pt], [14pt],
    [参考文献], [8pt], [12pt],
  ), 
  caption:"文字サイズ"
) <table:table4>], 
  "文字サイズ", 
  "Type sizes"
)

= 図表キャプション
日英キャプションについては"\#ieej_annual_tpl.twoCaptionFigure(\#figure(...))"のように、\#figure(...)を\#ieej_annual_tpl.twoCaptionFigure(...)でラッピングすると2行で記述されます。
このとき日本語キャプションを第二引数に、英語キャプションを第三引数に指定します。
\#figure内のcaption引数は無効にしています。

キャプション番号を参照することもできます。
図番号は"\#counter(figure.where(kind: image)).display()"、表番号は"\#counter(figure.where(kind: table)).display()"により表示できます。

= 参照
参照は"\@label"のように記述します。
式番号は @eq:eq1 のように表示されます。
図番号は @fig:figure1 のように表示されます。
表番号は @table:table1 のように表示されます。

= 脚注
脚注は"\#footnote[...]"と書きます。 #footnote[脚注はここに表示されます。]

= 引用
引用は"\@label"のように記述すると @lesk:1977 のように表示されます。
電気学会の引用スタイルに設定することが難しかったため、IEEEのスタイルを使用しています。
電気学会のcslファイルをお持ちの場合は"\#bibliography(style: "... .csl")"で指定してください。
引用文献は .bib ファイルを用意してください。

番号付きリストを用いて自分で記述する方法もありますが、文献番号を参照することが難しいため、文献番号を自分で記述する必要があります。
引用番号は"\#super([(1)])"と記述すると次のように表示されます。#super([(1)])

#line(length: 100%)
=== 参考文献(bibliography を使用する方法)
#bibliography("sample.bib", style: "ieee", title: none, full: true)

/* sample.bib
@inproceedings{lesk:1977,
  title={Computer Typesetting of Technical Journals on {UNIX}},
  author={Michael Lesk and Brian Kernighan},
  booktitle={Proceedings of American Federation of
             Information Processing Societies: 1977
             National Computer Conference},
  pages={879--888},
  year={1977},
  address={Dallas, Texas}
}
@article{amano:1975,
  title={電気学会全国大会講演論文の書き方},
  author={天野一夫 and 有竹次男 and 角替四郎},
  journal={電気学会全大}, 
  number={508}, 
  year={1975}
}
@article{abe:2005,
  title={論文の書き方と発表の仕方},
  author={阿部重夫},
  journal={電気学会論文誌C(電子・情報・システム部門誌)}, 
  volume={125},
  number={1},
  page={1--6},
  year={2005}
}
*/


#line(length: 100%)
=== 参考文献(引用番号も含めて自分で記述する方法)
#set enum(numbering: "(1)")
+ Michael Lesk and Brian Kernighan, "Computer Typesetting of Technical", #text(style: "italic")[Proceedings of American Federation of Information Processing Societies: 1977 National Computer Conference], pp. 879--888, 1977

+ 天野一夫・有竹次男・角替四郎:「電気学会全国大会講演論文の書き方」, 昭 50 電気学会全大, 1975, No. 508
+ 阿部重夫:「論文の書き方と発表の仕方」, 電気学会論文誌C(電子・情報・システム部門誌), 2005, 125 巻, 1 号, pp. 1--6

出力結果

上記コードを出力したときのPDFファイルは下記のリンクからダウンロードできる。 app.box.com

解説

Typst公式のチュートリアルなどで説明されている基本的な内容は省く。 これらは下記のサイトで説明されているので、参照してほしい。 typst.app

書式の適用

書式はpaperBuilder関数で定義している。

// libs/ieej_annual_template.typ
#let paperBuilder(
  doc, 
  title: "", 
  authors: "", 
  etitle: "", 
  eauthors: "",
  font: "Liberation Sans",
  mathfont: "New Computer Modern Math"
) = {...}

paperBuilder関数の引数は下記の通り。

  • doc: content, // documentコンテンツ
  • title: str, // 日本語タイトル
  • authors: str, // 日本語著者名
  • etitle: str, // 英語タイトル
  • eauthors: str, // 英語著者名
  • font: str, // 本文フォント
  • mathfont: str // 数式フォント

paperBuilderを適用する場合は本文中に下記のように記述する。

// main.typ
#show: doc => ieej_annual_tpl.paperBuilder(
  doc, 
  title: "電気学会全国大会講演論文の書き方", 
  authors: [研究 花子#sym.ast.basic,電気 太郎,学会 次郎 (○○○大学)],
  etitle: "Preparation of Papers for National Convention of I.E.E JAPAN",
  eauthors: "Hanako Kenkyu, Taro Denki, Jiro Gakkai (○○○University)"
)

言語設定

// libs/ieej_annual_template.typ
set text(
    ... , 
    lang: "jp"                // 言語
)

日本語を用いる場合はtext(lang: "jp")に設定する。

数式のフォント

// libs/ieej_annual_template.typ
// 数式フォント
show math.equation: set text(font: mathfont, style: "italic")
// 数式番号の設定
set math.equation(numbering: "(1)")

数式のフォントはshow math.equation: set text(font: fontname, style: "italic")で指定する。 数式番号はset math.equation(numbering: str)で指定する。 numberingの引数には使用する番号記号とプレフィックス(接頭語)とサフィックス(接尾語)を与える。 番号記号は 1, a, A, i, I, い, イ, ?, ?, ?, * が使用できる。 上記では番号記号を1、プレフィックスを ( 、サフィックスを ) としている。

表題番号

// libs/ieej_annual_template.typ
// 表題番号の設定
set heading(numbering: "1")
show heading: it => {
  if it.level == 1 {        // 最上段の表題番号
    set text(size: 10pt, weight: "bold") 
    block(counter(heading).display() + sym.dot.basic + sym.space.quad + it.body, width: 100%, spacing: 0pt)
  }
  else if it.level == 2 {   // 2段目の表題番号
    set text(size: 9pt, weight: "regular")
    block(height: 0pt, spacing: 0em, above: 0em, below: 0.65em)  // 行頭下げを無効化するために挿入
    box(sym.lt + counter(heading).display("1・1") + sym.gt + it.body + sym.space.quad + sym.space.quad)
  }
  else {                    // 3段目以降の表題番号
    emph(it.body)
  }
}

show heading: itのitには個々のheadingコンテンツが入る。 headingの階層を示すlevelプロパティにより、if文を用いて条件分岐している。 set関数はブロックの中でのみ有効であり、各条件文の中でフォントを設定している。 条件文内の各変数は主に下記のとおりである。

  • counter(heading).display():表題番号を表示する。
  • counter(heading).display("1・1"):表題番号を"1・1"スタイルで表示する。
  • sym:特殊記号(General Symbols – Typst Documentation)。
  • it.body:表題。

最上段の表題はフォントサイズを10pt、太字として、ブロックの中に表題を入れることで章を区切っている。

2段目の表題はフォントサイズを9ptとして、ボックスの中に表題を入れることでインラインテキストとしている。 ただし、ボックスだけだと、2段目の表題を含む段落が前章の段落とみなされ、行頭を下げられてしまう。 2段目の表題を含む段落が今の章の最初の段落とするために、高さ0ptのブロックを表題の上に設置している。

3段目以降の表題は表題番号なしの強調テキストで表示している。

リファレンス

// libs/ieej_annual_template.typ
// 図表リファレンスの設定
  set ref(supplement: it => {
    if it.kind == image {       // figureのcontentの種類がimageの場合
      "図"
    }
    else if it.kind == table {  // figureのcontentの種類がtableの場合
      "表"
    }
  })
  // 式リファレンスの設定
  show ref: it => {  // 詳細 https://typst.app/docs/reference/model/ref/
  let eq = math.equation
  let el = it.element
  if el != none and el.func() == eq {
    // Override equation references.
    numbering(
      el.numbering,
      ..counter(eq).at(el.location())
    )
  } else {
    // Other references as usual.
    it
  }
}

ref(supplyment: ...)は参照時の参照番号の前の文字を定義する。 set ref(supplement: it => ...)のitは参照元のコンテンツ(figureなど)である。 特にfigureコンテンツはtableコンテンツを格納しているときはkind: table、imageコンテンツを格納しているときはkind: imageである。 そこでit.kindがtableのときに「表1」、imageのときに「図1」と表示されるように参照番号の前の文字を設定した。

数式番号は下記にしたがって、括弧つき番号を表示するよう設定した。 typst.app

2段のキャプション

// libs/ieej_annual_template.typ
// 2段キャプションの図表
#let twoCaptionFigure(fig, caption, ecaption) = {
  show figure.caption: none
  show figure.where(kind: table): (it) => {
    align(center)[
      表#counter(figure.where(kind: table)).display()#sym.space#caption \
      Table#counter(figure.where(kind: table)).display()#sym.space#ecaption \
    ]
    it
  }
  show figure.where(kind: image): (it) => {
    it
    align(center)[
      図#counter(figure.where(kind: image)).display()#sym.space#caption \
      Fig.#sym.space#counter(figure.where(kind: image)).display()#sym.space#ecaption \
    ]
  }
  
  fig
}

ここでは2段のキャプションのfigureを設定するtwoCaptionFigureを定義している。 twoCaptionFigureの引数は下記の通り。

  • fig: content, // figureコンテンツ
  • caption: str, // 日本語キャプション
  • ecaption: str, // 英語キャプション

show関数はtwoCaptionFigure関数の中でのみ有効である。 twoCaptionFigureでは最初にshow figure.caption: noneによりfigureのキャプションを無効にしている。 show figure.where(kind: table): (it) => {...}はwhereメソッドにより、kindプロパティがtableのfigureコンテンツの動作を変更しており、itには該当するfigureコンテンツが格納される。 kindがtableの場合は、中央揃えで2段のキャプションを表示してからitを参照している。 counter(figure.where(kind: table)).display()は表番号を参照する。 kindがimageも場合もほぼ同様であるが、itを参照してから2段のキャプションを表示している。

結び

Latexと比較して、容易に自作の書式を実装することができた。 特にPythonJavascriptのプログラミングに近い感覚で実装できるのが楽だと感じた。