提示:本教程存在过时问题,更详细教程请阅读 小蓝书Typst 中文社区导航 FAQ
注意:该中文文档为社区驱动的非官网文档,可能存在错译、漏译或过时等问题,请以官网文档 Documentation 为准,如发现错漏,也欢迎 您的贡献镜像)。Typst 非官方中文交流 QQ 群:793548390
Warning: the Chinese document is a community-driven non-official document, there may be mistranslation, omission or outdated problems, please refer to the official website documentation.
Typst 中文文档

制作模板

在本教程的前三章中,您学习了如何在 Typst 中编写文档、应用基本样式, 以及深入自定义其外观以符合出版社的样式规范。 因为你在上一章写的论文取得了巨大的成功,所以你被要求为同一个会议写一篇后续文章。 这一次,您希望使用在上一章中创建的样式,并将其转换为可复用的模板。 在本章中,您将学习如何为您和您的团队创建只需一个 Show 规则即可使用的模板。 让我们开始吧!

玩具模板

在 Typst 中,模板是一个可以包装整个文档的函数。 要学习如何做到这一点,让我们首先回顾一下如何编写自己的函数。 函数可以做任何你想让他们做的事情,所以为什么不做得疯狂一点呢?

#let amazed(term) = box[#term]

You are #amazed[beautiful]!
Preview

此函数采用单个参数 term,并返回一个内容块,其中 term 被被一朵朵小火花包围着。 我们还把整个东西放在一个 box 里,这样我们的 term 与它的火花就不会换行符分开。

Typst 附带的许多函数都有可选的命名参数,我们的函数也可以实现这一点。 让我们向函数添加一个参数,其选择文本的颜色。 我们需要提供默认颜色,以防用户没有给出参数。

#let amazed(term, color: blue) = {
  text(color, box[#term])
}

You are #amazed[beautiful]!
I am #amazed(color: purple)[amazed]!
Preview

模板现在可以通过 “所有内容” Show 规则来使用,该 Show 规则将自定义函数应用于我们的整个文档。 让我们使用我们的 amazed 函数来实现它。

#show: amazed
I choose to focus on the good
in my life and let go of any
negative thoughts or beliefs.
In fact, I am amazing!
Preview

我们的整个文档现在将被传递给 amazed 函数,就好像我们把它包裹在 amazed 函数里面一样。 这对于这个特定函数并不是特别有用,但是当 Set 规则和命名参数结合使用时,它可以非常强大。

嵌入的 Set 和 Show 规则

要将一些 Set 和 Show 规则应用于我们的模板,我们可以在函数的内容块中使用 setshow,然后将文档插入到该内容块中。

#let template(doc) = [
  #set text(font: "Inria Serif")
  #show "something cool": [Typst]
  #doc
]

#show: template
I am learning something cool today.
It's going great so far!
Preview

就像我们在上一章中已经发现的那样,Set 规则将应用于其内容块中的所有内容。 由于 “所有内容” Show 规则将我们的整个文档传递给 template 函数, 因此模板中的 text Set 规则和字符串 Show 规则将应用于整个文档。 让我们利用这些知识来创建一个模板,以复现我们在上一章中编写的论文的正文风格。

#let conf(title, doc) = {
  set page(
    paper: "us-letter",
    header: align(
      right + horizon,
      title
    ),
    ...
  )
  set par(justify: true)
  set text(
    font: "Linux Libertine",
    size: 11pt,
  )

  // Heading show rules.
  ...

  columns(2, doc)
}

#show: doc => conf(
  [Paper title],
  doc,
)

= Introduction
#lorem(90)

...
Preview

我们复制粘贴了上一章中的大部分代码。 唯一的两个区别是,我们将所有内容都包装在函数 conf 中, 并直接在 doc 参数上调用 columns 函数,因为 doc 对应着整个文档的内容。 此外,我们使用大括号代码块而不是内容块。 这样,我们就不需要为所有的 Set 规则和函数调用加上 # 前缀。 与之相对的,我们也不再能再直接在里面写标记文本了。

还要注意标题的来源:我们以前把它放在变量中,而现在我们将其作为模板函数的第一个参数接收。 因此,我们必须在调用模板的 Show 规则中指定它。

具有命名参数的模板

我们在上一章的论文有一个标题和一个作者列表,让我们将这些内容添加到我们的模板中。 除了标题之外,我们还希望我们的模板接受作者及其单位构成的列表,以及一个论文摘要。 为了保持可读性,我们将添加这些参数作为命名参数。最后,我们希望它像这样工作:

#show: doc => conf(
  title: [Towards Improved Modelling],
  authors: (
    (
      name: "Theresa Tungsten",
      affiliation: "Artos Institute",
      email: "tung@artos.edu",
    ),
    (
      name: "Eugene Deklan",
      affiliation: "Honduras State",
      email: "e.deklan@hstate.hn",
    ),
  ),
  abstract: lorem(80),
  doc,
)

...

让我们构建这个新的模板函数。 首先,我们为 title 参数添加一个默认值。 这样,我们可以在不指定标题的情况下调用模板。 我们还添加了具有空默认值的命名参数 authorsabstract。 接下来,我们将上一章中生成标题、摘要和作者列表的代码复制到模板中,并用参数替换其中的固定值。

新的 authors 参数接收一个由 字典数组,其中带有键 nameaffiliationemail。 因为我们可以输入任意数量的作者,所以我们需要动态地确定作者列表是需要一列、两列还是三列。 首先,我们在 authors 数组上使用 .len() 方法确定作者的数量。 然后,我们将列数设置为作者数量和 3 之间的最小值,以便我们永远不会创建超过三个列。 如果作者超过三个,则将插入一个新行。为此,我们还在 grid 函数中添加了一个 row-gending 参数。 否则,这些行将会靠得太近。为了从字典中提取有关作者的详细信息,我们使用 字段访问语法

我们仍然必须为每个作者对应的网格提供一个参数:这就是数组的 map 方法 派上用场的地方。 它将一个函数作为参数,该函数与数组的每个项一起调用。 我们给它传递一个函数,该函数会格式化每个作者的详细信息,并返回一个包含内容值的新数组。 现在,我们有一个值数组,我们希望将其用作网格的多个参数。 我们可以通过使用 spread 操作符 来做到这一点。 它接受一个数组,并将其数组里的每个项目作为单独的参数应用于函数中。

生成的模板函数如下所示:

#let conf(
  title: none,
  authors: (),
  abstract: [],
  doc,
) = {
  // Set and show rules from before.
  ...

  set align(center)
  text(17pt, title)

  let count = authors.len()
  let ncols = calc.min(count, 3)
  grid(
    columns: (1fr,) * ncols,
    row-gutter: 24pt,
    ..authors.map(author => [
      #author.name \
      #author.affiliation \
      #link("mailto:" + author.email)
    ]),
  )

  par(justify: false)[
    *Abstract* \
    #abstract
  ]

  set align(left)
  columns(2, doc)
}

单独的模板文件

大多数情况下,模板应该在不同的文件中定义,然后导入到文档中。 这样,您编写的主文件就可以保持整洁,并且您的模板可以轻松地复用。 在文件面板中通过单击加号按钮创建一个新的文本文件,并将其命名为 conf.typ。 将 conf 函数定义移到该新文件内。 现在,您可以通过在显示规则之前进行导入来从主文件访问它。 在 import 关键字和冒号之间指定文件的路径,然后指明你要导入的函数。

#import "conf.typ": conf
#show: doc => conf(
  title: [
    Towards Improved Modelling
  ],
  authors: (
    (
      name: "Theresa Tungsten",
      affiliation: "Artos Institute",
      email: "tung@artos.edu",
    ),
    (
      name: "Eugene Deklan",
      affiliation: "Honduras State",
      email: "e.deklan@hstate.hn",
    ),
  ),
  abstract: lorem(80),
  doc,
)

= Introduction
#lorem(90)

== Motivation
#lorem(140)

== Problem Statement
#lorem(50)

= Related Work
#lorem(200)
Preview

我们现在已经成功将会议论文转换为该会议的可重复使用模板! 快来 Typst 的 Discord 服务器 分享它, 以便其他人也可以使用它吧!

回顾

恭喜,您已完成 Typst 的教程! 在本节中,您学习了如何定义自己的函数,以及如何创建和应用这个定义了可复用文档样式的模板。 你已经走了很远,学到了很多东西。您现在可以使用 Typst 编写自己的文档并与他人共享。

我们仍然是一个非常年轻的项目,正在寻求您的反馈。 如果您有任何问题,建议或发现错误,请在 Typst 的 Discord 服务器,我们的 contact form社交媒体 上告诉我们。

那还在等什么呢?快 注册 一个账户并写点有趣的东西吧!