Typst学习笔记
本文的简介部分有一个更新、更简洁的版本:体验Typst
Typst是一种对标LATEX的桌面出版系统,包括同名的排版语言、引擎和编辑器支持工具等,主要用Rust语言写成,是typ(e+ru)st。很受关注,据说它的GitHub代码库在开源第一天即达到了5千多星。
Typst要解决的问题#
目前大多数基于文本的排版系统(Text-based typesetting systems,主要指TeX系,特别对标LATEX),每次改动排版内容,不管改动多小,都要整个儿重新编译,无法实现所见即所得(WYSIWYG,What You See Is What You Get)的效果,不能即时预览。Typst在解析环节使用一种增量解析算法(a incremental parsing algorithm),在版面编排(layouting)环节使用一种有约束的版面缓存方案(a constrained layout caching scheme),从而实现资源重用,而只做局部改动。
在基于一组典型文档和编辑动作的评估中,我们确定本论文的贡献使Typst的速度提高了4.5到91倍。Typst编译一个编辑动作的速度是LATEX的3.4到9895倍。2
其增量编译算法类似于C#、Swift和Rust开发工具rust-analyzer所使用的Red-Green Trees机制。
运行效率#
从解析到排版阶段的编译效率很高。下面是三种类型出版物每次编辑动作导致的解析、排版平均耗时和标准差:2
Typst生成和导出PDF阶段没有这么绝对的优势,处理小文件时表现很好;但在导出多卷本文集(Tome)的PDF文件时,效率还略低于pdfTEX,只略高于XETEX:
运行平台#
主要目标平台是WASM,也就是说在用户的浏览器中本地运行,离线编译文档。也发布成多种主流PC系统上能运行的命令行界面(CLI)工具,略大于8MB。
含有编辑支持工具,能实现文档增量解析和代码自动补全。
官方建有在线编辑平台,其代码编辑器有比较丰富的辅助功能,即时预览是通过浏览器画布上渲染的,与下载得到PDF文件一致。在一般用例上编辑,预览刷新极快,实现了“所见即所得”的效果。 可能还有不少高级功能,比如代码补全的建议项能显示实时的值:
另有非官方的VScode Typst语言支持插件,无需额外安装命令行工具;目前在初始阶段。
Typst语言#
Typst是一种由纯函数组成的、有完整编程环境的语言。
在Typst语言中,纯文本、文本标记(markup)和程序代码是无缝连接的,不是在一种标记文本中嵌入另一种程序代码。(这是诸TeX+LuaTeX的基本问题, 比如宏展开、交互传值等机制非常复杂难解。)3
文本标记#
详见文档:syntax
Typst文档,最上层使用非常类似于markdown的文本标记语法,目的是使文档本身即是人类可读的,并尽可能模拟最终输出的格式。
数学公式的写法比TeX系简洁。
还支持行注释和块注释。
当然,最核心的是支持在文档中直接写Typst代码。详见文档:scripting
实际上,文本标记是它对应的函数的语法糖,也就是说,你也可以通过调用对应函数的方式来使用这些功能,或者改变这些函数。 如下:
而且从编程语言的角度看,整个文档是一个顶级的内容域[content]
,其中可以有纯文本、各种带文本标记的语句(包括代码语句#...
)、代码域{...}
、子内容域[content]
;子域中又可以嵌入子域,不限种类和深度。变量、函数调用的作用域即遵循这样的结构。
脚本代码#
Typst代码是一种函数式的、动态类型的脚本语言,不像TeX系主要是基于宏。
它跟当前的主流编程语言很像,比如提供字符串、数组、字典等数据类型,内建数据类型有自己的方法;支持模块;有常用的数学计数函数、构建和转换数据类型的函数、加载CSV、JSON、Plain text、XML等数据文件的函数,以及查看变量类型、处理错误信息等基础函数。
针对出版提供了支持:
为排版定制的类型系统。比如支持数据类型auto(默认属性)、length、relative length(相对长度)、angle(角度)、ratio(比率)、fraction(份,类似TeX的fill)、color(色值)、content等类型。详见文档:types。其中,content类型处于Typst的中心位置,表示正文档、文档片段,可以内嵌所有类型的内容;以结点树的形式呈现,包含由set
和show
加入的样式。
语境敏感。比如不同位置的默认值(如页码)会不同。它同时也支持闭包,即在定义函数时即固定某些变量的值。
编译器带有一套标准的常量和函数,可以通过它们访问内部的排版功能,或者用作便捷的辅助工具。所有文件都会加载这个库。它包括这几大类:Text、Math、Layout、Visualize(目前只有几种图形绘制函数,暂缺数据可视化函数)、Meta、Symbols(符号常量,包括emoji)。
基于属性和转换的可组合的弹性样式系统;样式与内容紧密结合,以免冲突。比如,你可以通过set
和show
关键字(可视作高阶函数或者宏)控制所有样式函数,生成新的样式规则(rule)。详见文档:styling。1如果要对本域中插入点后所有内容应用一种样式(比如一种杂志的样式),可以用show: rest =>
把后面的内容包裹到变量中,再传递给一个样式函数。
支持结构内省操作。通过Meta类函数控制整个文档的元数据和结构,比如章节标题、书目、交叉引用、计数器;查询和展示特定内容,如通过locate
函数获取排版元素最终的页码和页面坐标。
中文支持#
通过少量观察,目前能正确断行。官方的在线编辑器内置“Noto * CJK SC”系列的字体(需要临时下载)。其余支持似乎没有。
我打算把我做的ConTeXt+LuaTeX(LuaMetaTeX)中文标点、竖排、双行夹注模块移植过来。这里更新进展情况:
一些技术细节#
编译过程#
编译分成4个阶段:
- 解析,生成抽象语法树(绿树);
- 求值(evaluate,或lift),生成内容模型树(Content Model),其结点可能是终点(leaf,如文本、断行;其间会对它们执行替换操作)、模块和内联结点、样式结点(包含一个由
set
或show
生成的子项)、序列结点(包含多个子项,由joint生成)、本地结点(可内省的节点)。结点实际样式会受到从根到结点之间的所有样式结点的影响,在下一个阶段会生成样式链。 - 排版(layout),生成框架树,分行和分页发生在这个阶段;
- 调用stack或grid函数会生成版面结点。大多数版面节点可以包含子节点。每个版面结点都提供一个函数,在给定上下文和可用空间的情况下返回框架(frame,相当于TeX的box)。可用空间可以是一个矩形,也可以是不同尺寸的矩形序列,可能在不同的页面。
- 框架是有固定尺寸的容器,盛放最小化的可视元素,如有固定位置的字模、图形和图像;
- 另有块(block,与段落同级)和内联(inline,段落的子节点)层次的框架结点;
- 最终包含:原始内容(字模、几何图形、图像、内部链接、外部超链接)、线性变换后的框架、定位器。
- 导出,由后端生成PDF、图像,或像官方在线编辑器那样渲染到HTML Canvas。
四个阶段不是线性进行的,可能会交替或循环。
定位器#
定位器(locator)可以放在内容中,与周围的内容一起排版。通过一个变量对定位器进行引用,或在实际排版后获得它在页面上的绝对位置,或所在的页码;在实际排版前则会获得默认值。用户可以根据获得值来运行代码,这就产生了循环依赖关系。Typst通过循环运行求值和版面编排来解决这个循环,直到收敛(值不变),或超过编译预算(一般是4次)。
表达式与拼接#
所有代码结构都是表达式,会生成输出值(可能是none)。这些值会被拼接在语境中。
变量#
变量没有引用类型,都是值类型,即每次引用都获得一个拷贝,不会受到其他引用的影响。
排版#
段落包含文字、图像等水平元素,方向与所选语言有关。 流(flow)负责从上到下垂直地管理块级元素(段落等)。 段落和流是自动生成的。
堆叠(stack)和网格(grid)是组合基本元素的最基本的方法。 堆叠沿着一条轴线放置条目,条目间放置绝对或相对的空格。 网格按行列排布。
元素可以应用仿射(affine)变换函数:move、scale、rotate。
查看和更改数据#
1 2 3 4 5 6 7 8 9 10 |
|
show
省略输入对象(冒号左侧的内容)时会捕获后面的所有内容;如果放在文档头部,则是全文档的内容。这方便编写作用于全文的库。
1 2 3 4 5 6 7 8 |
|
资源#
- 官方网站
- 代码库
- 官方的VS Code插件
- 作者Martin E. Haug的硕士论文:fast typesetting incremental compilation
- 作者Laurenz Mädje的硕士论文:Typst: programmable markup language for typesetting
- VS Code语言支持插件
- 讨论群:Discord server
- 资源列表awesome-typst
- 代码库变化情况的可视化(YouTube视频):typst/typst - Gource visualisation
- typst-examples-book
-
这里的情况似乎有些复杂,有待于根据源码确认:尽管作者声称Typst不是基于宏的(The Typst language is Turing-complete, dynamically typed, and not macro-based.2),但它的样式管理系统、应用样式的主要方法——
set rules
和show rules
,不管从语法结构、行为方式还是命名,都有Rust基于抽象语法树的宏系统的影子,比如Rust定义宏的关键字是macro_rules!
。 ↩ -
fast typesetting incremental compilation, by Martin E. Haug, June 2022 (Master’s thesis, Technische Universität Berlin, Faculty IV – Electrical Engineering and Computer Science) ↩↩↩
-
Typst: programmable markup language for typesetting, by Laurenz Mädje, September 2022 (Master’s thesis, Technische Universität Berlin, Faculty IV – Electrical Engineering and Computer Science) ↩