Klapring

Typst 使用经验记录

更新日志

警告:本文主要编写于 Typst 0.12.0 版本,部分内容未考虑新版本更新内容,可能已经过时。

如未特别注明,本文中的代码均基于 Typst 0.12.0 版本。

怎么找解决方案

多翻翻别人趟过的坑:

排版经验

不同层级标题使用不同序号格式

其实是如何为每一级标题指定不同的编号格式?-常见问题| Typst 中文社区导航的重复造轮子,但是造轮子的时候没看到这个东西,造都造了就发出来算了。(用 Typst 非官方中文群群友的说法,在 numbly 包出来之前,人人都有自己的 numbly 轮子(笑))

#let diff_numbering(..schemes) = {
(..nums) => {
let nums_arr = nums.pos()
let schemes_arr = schemes.pos()
if nums_arr.len() >= schemes_arr.len() {
numbering(schemes_arr.at(-1), ..nums)
} else {
numbering(schemes_arr.at(nums_arr.len() - 1), ..nums)
}
}
}
// 使用
#set heading(numbering: diff_numbering("第一章", "1.1", "1-1-1"))
= 一个章节
== 一个小节
=== 一个小节的小节
= 第二个章节
== 第二个章节的小节
=== 第二个章节的小节的小节

效果:

排版效果截图:多级目录示例,各层标题字重与缩进不同,条目如「第一章 一个章节」「1.1 一个小节」。
图 1 效果

使用 numbly 包可以达到更好的效果:

#import "@preview/numbly:0.1.0": numbly
#set heading(numbering: numbly("一|{1:一}章", "a{1:1}.{2:1}", "1?{1:1}-{2:1}.{3:1}"))
= #lorem(1)
== #lorem(2)
=== #lorem(3)
== #lorem(2)
=== #lorem(3)
=== #lorem(3)
= #lorem(1)
== #lorem(2)
=== #lorem(3)

填空栏

#let grid_blanks(cell: grid.cell, blank-width: 2em, line-width: 0.05em, colon: [: ], ..args) = grid(
..args.named(),
..args.pos().map(label => (cell([#label#colon#box(width: blank-width, stroke: (bottom: line-width))])))
)
// 使用
#grid_blanks(
columns: (auto, auto),
align: right,
row-gutter: 1.5em,
column-gutter: 1em,
blank-width: 3cm,
"姓名",
"班级",
"学号",
"学校",
)
#grid_blanks(
columns: (auto, auto),
align: right,
row-gutter: 1.5em,
column-gutter: 1em,
blank-width: 3cm,
colon: [ $==> integral_0^1 pi - 1$ ],
"Name",
"Class",
"Id",
"Grade",
)

效果:

排版效果截图:四组中文填空栏,「姓名」「班级」「学号」「学校」后各接一条下划线。
排版效果截图:英文填空栏,Name、Class、Id、Grade 后是双箭头与下划线。

页眉使用当前页面标题

使用 hydra 包。需要其他设置可以参考hydra 包的文档

#import "@preview/hydra:0.5.1": hydra
#set page(
header: context align(
{
if calc.odd(here().page()) {
right
} else {
left
}
},
emph(hydra(1, skip-starting: false)),
),
)

效果:

两页文档截图:页眉显示当前章节标题「第一章 全纯函数」,正文为复欧氏空间的数学笔记。
备注:效果演示使用的是我的多复变函数论笔记的 Typst 版本。

目录不同层级标题使用不同样式

简易版

#show outline.entry: it => {
if it.level == 1 {
strong(it)
} else if it.level >= 3 {
emph(it)
} else {
it
}
}

效果如下:

排版效果截图:目录中各级标题样式不同,章名加粗、节名常规、小节名倾斜,并带页码点线。
图 5 效果

可以发现,页码也变得歪七倒八的。如果不介意,那么用这个简易版即可;反之,可以看下面的复杂版

复杂版

该代码适用于 Typst 0.13 版本。

#show outline.entry: it => {
// 获取 entry 原本的内容
let prefix = outline.entry.prefix(it)
let body = outline.entry.body(it)
let page-number = outline.entry.page(it)
// 修改 body
body = if it.level == 1 {
strong(body)
} else if it.level >= 3 {
emph(body)
} else {
body
}
// 重新组装 inner
// 保持和 `outline.entry.inner(it)` 获取的结构一致
let inner = {
body
sym.space
box(width: 1fr, it.fill)
sym.space
page-number
}
let indented = outline.entry.indented(it, prefix, inner)
link(it.element.location(), indented)
}
警告: Typst 0.13 大幅修改了 outline.entry 的接口,以下代码只适用于 Typst 0.12 版本。
#show outline.entry: it => {
let loc = it.element.location()
link(
loc,
if it.level == 1 {
strong(it.body)
} else if it.level >= 3 {
emph(it.body)
} else {
it.body
},
)
sym.space
box(width: 1fr, it.fill)
sym.space
link(loc, it.page)
}

复杂版是直接照着 Typst 的 outline 函数 源码 写的,只有标题的文字部分会修改样式。

结合 showybox 与 ctheorems 包制作美观定理环境

提示:目前有开箱即用的包 theorion,推荐使用这个。

本节内容参考了 showybox and ctheorems? · Issue #15 · sahasatvik/typst-theorems 的内容并进行了部分修改。

// --------- 定义函数 ---------
#import "@preview/ctheorems:1.1.2": *
#import "@preview/showybox:2.0.1": showybox
#let showy-thm(
identifier,
head,
color: blue,
..showy-args,
supplement: auto,
base: "heading",
base_level: none,
) = {
let showy-fmt(name, number, body, ..args) = {
showybox(
title: {
head
number
if name != none {
[(#name)]
}
},
frame: (
border-color: color,
title-color: color.lighten(30%),
body-color: color.lighten(95%),
footer-color: color.lighten(80%),
radius: (top-left: 7pt, bottom-right: 7pt, rest: 2pt),
),
..args.named(),
body,
)
}
if supplement == auto {
supplement = head
}
thmenv(
identifier,
"heading",
none,
(name, number, body) => showy-fmt(name, number, body, ..showy-args),
).with(supplement: supplement)
}
#let theorem = showy-thm(
"theorem",
"定理",
title-style: (
weight: "bold",
boxed-style: (
anchor: (x: left, y: horizon),
offset: (x: 0pt, t: 0pt),
radius: (top-left: 7pt, bottom-right: 7pt, rest: 2pt),
),
),
).with(numbering: "1.1")
#let lemma = showy-thm("lemma", "引理", color: green.darken(25%)).with(numbering: "1.1")
#let proof = thmproof("proof", "证明", inset: (x: 0em, top: 0em))
// --------- 定义函数 ---------
// --------- 使用演示 ---------
#show: thmrules
#theorem[
对任何子集 $Omega subset CC^n$$cal(O)(Omega)$ 在逐点加法和数乘意义下封闭。
任一关于 $z_1, dots.c, z_n$ 的复系数多项式在 $CC^n$ 上是全纯的,从而在 $cal(O)(Omega)$ 里。
$f, g in cal(O)(Omega)$,且 $g(z) eq.not 0, forall z in Omega$,则 $f slash g in cal(O)(Omega)$
]
#theorem("F. Hartogs, 1906", numbering: none)[
$D subset CC^n$ 为一区域,$f: D -> CC$。若 $f$ 分别关于每一单复变量 $z_j (1 <= j <= n)$
全纯,则 $f in cal(O)(D)$
]
#lemma("Osgood", numbering: none)[
$D subset CC$ 为一区域,$f: D -> CC$。若 $f in C(D)$,且 $f$
分别关于每一单复变量 $z_j (1 <= j <= n)$ 全纯,则 $f in cal(O)(D)$
]
// --------- 使用演示 ---------

效果:

排版效果截图:蓝、绿圆角框包裹的「定理」「引理」环境,标题块着色,正文为数学内容。

在文本中使用美观的大括号分类

#let my-cases = (..items) => box(
baseline: 50% - 0.5em,
width: 1fr,
math.cases(
..items.pos().map(item => math.display(block(item))),
),
)
*TEST*: #my-cases(
lorem(20),
lorem(20),
lorem(20),
)

效果:

排版效果截图:左侧「TEST:」后接一个大括号,把三段 Lorem ipsum 文本括在一起。

在 Typst 中绘制 LaTeX 符号

Typst Universe 上有一个现成的包 metalogo,提供了各种 TeX徽标的绘制函数

metalogo 文档页面:展示 LaTeX、XeTeX、LuaTeX 等徽标的渲染效果与配置小节。
图 8 metalogo manual

其他

转换为 Markdown

我的一部分文章使用 Pandoc 将 Typst 转换为 Markdown 文件,方便放在博客。

提示:如果你愿意折腾,可以尝试使用 hexo-renderer-typst 直接在 Hexo 上渲染 Typst 文档。

由于 Typst 0.13 版本已有实验性的 HTML 导出,也可以尝试通过 Typst.ts 进行 HTML 导出。对于 Astro,有可以直接使用 HTML 导出功能渲染为网页的 astro-typst,以及 tinymist作者编写的 基于 Typst 的 Astro 博客模板

此外,除去 Pandoc,你也可以尝试使用目前集成于 tinymist 内的 typlite 来将 typst 文档转换为Markdown。

基础操作

下载 Pandoc 的最新版本,配置相应环境变量(如果有需要的话),然后就可以开始了。

pandoc --from=typst --to=markdown --output=path/to/your/blog/source/_draft/post_name.md --wrap=preserve "path/to/your/typst_file.typ"

不能转换的情形

警告: 以下内容基于 pandoc 3.4 说明

可能有更多转换失败的情形,欢迎在本文评论区留言告诉我。

转换可能出现的错误

可能有更多转换错误的情形,欢迎在本文评论区留言告诉我。

实用工具