跳到主要内容
  1. Skills/
  2. typst 专栏/

使用 pandoc 和 typst 将 markdown 转换为 pdf

·字数 4499·9 分钟

你是否有使用 markdown 编写文档,但又不想使用繁琐的 latex 转换成 pdf 的烦恼?那么你可以试试 pandoc 和 typst。这是一款文档模板程序,可以将 markdown 转换为漂亮的 pdf 文件,同时支持强调(斜体粗体)、无序列表、有序列表、任务列表、表情符号 emoji、链接、图片、脚注、定义列表、数学公式、代码高亮 fenced code block、表格、表格宽度与列宽、引用 quote|blocktext、alerts、水平线、标题等多种格式。想要查看文档效果?请点击 用户帮助手册 ,它也是由本文档模板程序生成。支持多种代码高亮颜色主题,

大约十年前,有悟就喜欢使用 markdown 编写文档,原因只是 microsoft office 或 wps 过于笨重,后来也渐渐发现,日常的技术文档、小组工作计划等小体裁文档,使用 office word 或者 wps 确实有点杀鸡用牛刀,常用的格式无非是标题、列表、表格、文本段落,并不需要使用老旧的电脑来运行庞大的文档编辑器程序。有悟有一定的编程基础,也知道文字内容与格式分离,可更有利于专注写作。

但是,在使用 markdown 编写完内容后,要将其分发时,一直遇到这样的困难,使用 pandoc 转换成 pdf,需要安装庞大的 latex 引擎。使用 pandoc 将 markdown 文件转换为 docx ,然后在 wps 或 microsoft office 打开再导出 pdf,操作繁琐,不符合文档写作工作流简易性要求。

直到 2024年,发现已经开源了一段时间的 typst,它非常本人习惯,将文档内容的 markdown 文件,转换成 pdf,仅需要安装几十M的 typst,仅消耗秒级的时间,即可生产质量非常高的 pdf 文档 或者 png 图片。非常感谢 tyspt 团队的贡献。

有悟将原来基于 pandoc markdown 转 docx 转 pdf 的工作流,在 pandoc 和 typst 加持下,简化为 markdown 转 pdf 或 png。当然,也花费了两个多月的时间来学习 typst 的编程语法,并编制文档模板程序。

文档模板的设计初衷,是满足本人日常工作的文档写作要求,非常自以为是,这些文档的主要类型的有:

  • 技术方案说明
  • 工作计划
  • 总结
  • 日志

书籍出版、 学术论文,暂不在考虑的范围。

从哪里获得本文档模板程序,见 如何获得本模板

模板中支持的格式有 #

文档中支持的格式类型有:

模板中的颜色设定 #

文档有暗黑、浅色两个版本。文档页面分为 A4 与适用手机浏览的长宽比 650/300 两种尺寸。

代码高亮颜色主题有:

  • ayu :
    • ayu-mirage
    • ayu-dark
    • ayu-light
  • Catppuccin
    • Catppuccin Frappe
    • Catppuccin Latte
    • Catppuccin Macchiato
    • Catppuccin Mocha

模板程序中已经包含以上所有情况的组合。

效果示例 #

可直接下载 userguide.dark.ayu-dark.pdf 浏览效果。

强调 #

markdown 内容如下:


强调,又称斜体,使用 *星号*_下划线_
强调,又称粗体,使用 **星号**__下划线__
结合使用 **星号和 _下划线_** 来强调。

删除线使用两个波浪号。 ~~划掉这个。~~

**这是粗体文本**

__这是粗体文本__

*这是斜体文本*

_这是斜体文本_

~~删除线~~

渲染成:

强调(粗体与斜体)示例
强调(粗体与斜体)示例


无序列表 #

markdown 内容如下:


* 无序列表可以使用星号(`*`)
- 或减号(`-`)
+ 或加号(`+`)

+ 通过在行开头使用 `+`、`-` 或 `*` 创建列表
+ 通过缩进 2 个空格来创建子列表: 
  - 标记字符更改强制开始新列表: 
    * Ac tristique libero volutpat at
    + Facilisis in pretium nisl aliquet
    - Nulla volutpat aliquam velit
+ 非常简单!

+ 无序列表也可以
+ 与序列列表混合
  1. 缩进并创建一个序号列表项
  2. 再来一个序号项
+ 另一个无充列表项

渲染成:

无序列表示例
无序列表示例


有序列表 #

markdown 内容如下:


1. 第一个有序列表项
2. 另一个项
  * 无序列表
1. 实际数字无关紧要,只要它是一个数字
  1. 序号会重新编排
4. 另一个项。
   你还可以在列表项中正确缩进段落。
5. 多空一行
  
   除了可以正确缩进段落外,在段落前多空一行,还可以形成一个新的缩进段落,首行缩进。

   除了可以正确缩进段落外,在段落前多空一行,还可以形成一个新的缩进段落,首行缩进。
7. 再来一个项

1. 进行更改
  1. 修复错误
  2. 改进格式
    - 使标题更大
2. 将我的提交推送到 GitHub
3. 打开拉取请求
  * 描述我的更改
  * 提及我团队的所有成员
    * 征求反馈
    * 

渲染成:

有序列表示例
有序列表示例


任务列表 #

markdown 内容如下:

- [x] 完成我的更改
- [ ] 将我的提交推送到 GitHub
- [ ] 打开拉取请求
- [x] 支持 \@mentions、#refs、[links]()、**formatting** 和 <del>tags</del>
- [x] 需要列表语法(支持任何无序或有序列表)
- [ ] 这是一个完整的项目
- [ ] 这是一个不完整的项目
- ✅ 这一行不是 task list 项,但也属于列表项

渲染成:

任务列表示例
任务列表示例


emoji #

markdown 内容如下:


*emoji 标记*: 😄 😡 😓 👇

*emoji 符号*: 😀😃😄😁🐶🐱🐭🐹
🍏🍐🍉🍇🍓
⚽️🏐🏈🏀
🚗🚕🚙🚌
⌚️📱📲💻⌨️
🩷❤️🧡💛🉐㊙️㊗️🈴🉑☢️☣️
🏳️🏴🏴‍☠️🏁
⚠️🌟🔥👍✅

渲染成:

表情emoji示例
表情emoji示例


链接 #

markdown 内容如下:


[我是内联样式链接](https://www.google.com)

[我是带有标题的内联样式链接](https://www.google.com "Google 的主页")

[我是引用样式链接][任意不区分大小写的引用文本]

[我是存储库文件的相对引用](../blob/master/LICENSE)

[您可以使用数字进行引用样式链接定义][1]

或者将其留空并使用 [链接文本本身]。

URL 和尖括号中的 URL 将自动转换为链接。http://www.example.com 或 <http://www.example.com>,有时是 example.com(但不是 Github 上的)。

一些文字表明参考链接可以稍后跟进。

[任意不区分大小写的引用文本]: https://www.mozilla.org
[1]: http://slashdot.org
[链接文本本身]: http://www.reddit.com

渲染成:

链接示例
链接示例

图片 #

markdown 内容如下:


这是徽标: 

内联样式: 
![替代文本](./images/icon48.png "徽标标题文本 1")

引用样式: 
![替代文本][徽标]

[徽标]: ./images/icon48.png "徽标标题文本2"

图片下方没有题注,并且图片紧靠着文字:
![Minion](./images/minion.png){width=20%}
![Stormtroopocat](./images/stormtroopocat.png "The Stormtroopocat"){width=20%}

还可以在图片下方添加题注,需要单独成为段落,即前后各空一行:

![图片标题,来点说明][id]

在文档后面的引用中定义图片位置: 

[id]: ./images/dojocat.jpg "The Dojocat"

还可以指定图片大小: 

![Minion](./images/minion.png){width=5%} ![Minion](./images/minion.png){width=10%}
![Minion](./images/minion.png){width=20%}![Minion](./images/minion.png){width=30%}

渲染成:

图片示例
图片示例

脚注 #

markdown 内容如下:


脚注 1 链接[^1]。

脚注 2 链接[^2]。

~内联脚注^[内联脚注的文本] 定义。

重复的脚注引用[^2]。

[^1]: 脚注 **可以有标记** 和多个段落。

[^2]: 脚注文本。

渲染成:

脚注示例
脚注示例


定义列表 #

markdown 内容如下:


术语
: 术语名称独立一行,第二行以冒号定格(`: `)开头,紧接是术语定义的描述

又一个术语
: 还是以冒号定格(`: `)开头,紧接还是术语定义的描述。*定义列表*经常用在文档,对专业术语、业务术语、名词进行统一的定义。

术语条目换行
: 与 markdown 的换行解释有关,如果是 `hard_linkbreak`,即条目间需要空出一行。
这不是一个定义
: 定义名称没有与上个条目空行分隔,无法成为新的条目。

术语定义段落
: 术语内容作为整体段落,向右缩进。

渲染成:

定义列表示例
定义列表示例


数学公式 #

markdown 内容如下:


单独行显示
: $$
e=mc^2
$$


内联
: 这是一个小兔子,紧接一个公式,$e=mc^2$。紧接着后面还可以有文字。

渲染成:

数据公式示例
数据公式示例


代码高亮 #

markdown 内容如下:



```
内联 `代码` 周围有 `反引号`。
```

内联 `代码` 周围有 `反引号`。

代码块使用一对 三反引号 ```


示例代码:

````
```css
@font-face {
  font-family: Chunkfive; src: url('Chunkfive.otf');
}

body, .usertext {
  color: #F0F0F0; background: #600;
  font-family: Chunkfive, sans;
}

@import url(print.css);
@media print {
  a[href^=http]::after {
    content: attr(href)
  }
}
```
````

代码高亮效果: 

```css
@font-face {
  font-family: Chunkfive; src: url('Chunkfive.otf');
}

body, .usertext {
  color: #F0F0F0; background: #600;
  font-family: Chunkfive, sans;
}

@import url(print.css);
@media print {
  a[href^=http]::after {
    content: attr(href)
  }
}
```

示例代码:

````
```javascript
function $initHighlight(block, cls) {
  try {
    if (cls.search(/\bno\-highlight\b/) != -1)
      return process(block, true, 0x0F) +
             ` class="${cls}"`;
  } catch (e) {
    /* handle exception */
  }
  for (var i = 0 / 2; i < classes.length; i++) {
    if (checkCondition(classes[i]) === undefined)
      console.log('undefined');
  }
}

export  $initHighlight;
```
````

代码高亮效果: 

```javascript
function $initHighlight(block, cls) {
  try {
    if (cls.search(/\bno\-highlight\b/) != -1)
      return process(block, true, 0x0F) +
             ` class="${cls}"`;
  } catch (e) {
    /* handle exception */
  }
  for (var i = 0 / 2; i < classes.length; i++) {
    if (checkCondition(classes[i]) === undefined)
      console.log('undefined');
  }
}

export  $initHighlight;
```

渲染成:

代码高亮示例
代码高亮示例
代码高亮示例
代码高亮示例


表格 #

markdown 内容如下:


冒号可用于对齐列。


| 表格          | 很            | 酷    |
| ------------- |:-------------:| -----:|
| 列 3 是       | 右对齐        | $1600 |
| 列 2 是       | 居中          |   $12 |
| 斑马条纹      | 整齐          |    $1 |

每个标题单元格之间必须至少有 3 个破折号(`-`) 。
外部管道 (`|`) 是可选的,您不需要让
原始 Markdown 排列得很漂亮。您还可以使用内联 Markdown。

表格示例,外部管道符(|)可省略:

Markdown | 少也 | 漂亮
--- | --- | ---
*仍然* | `呈现` | **很好**
1 | 2 | 3

| 第一个标题    | 第二个标题    |
| ------------- | ------------- |
| 内容单元格    | 内容单元格    |
| 内容单元格    | 内容单元格    |

表格示例,单元格内容可以标记样式:

| 命令 | 描述 |
| --- | --- |
| git status | 列出所有新的或修改过的文件 |
| git diff | 显示尚未暂存的文件差异 |


| 命令 | 说明 |
| --- | --- |
| `git status` | 列出所有 *新的或修改过的* 文件 |
| `git diff` | 显示**尚未**暂存的文件差异 |

表格示例,表格各列可自由对齐:

| 左对齐     | 居中对齐   | 右对齐     |
| :---       | :---:      | ---:       |
| git status | git status | git status |
| git diff   | git diff   | git diff   |

表格示例,单元格中的特殊符号:

| 名称   | 字符 |
| ---    | ---  |
| 反引号 | `    |
| 管道符 | \|   |

渲染成:

表格示例
表格示例
表格示例
表格示例


表格宽度与列宽 #

pandoc markdown 解析器,支持两格可以控制表格宽度和列宽的表格格式,multiline tablesgrid tables。默认行 72个 scii 字符宽度则可占满页面正文宽度。

grid_stables 为例:


: 2 列 72 字符数

+-----------------------------------+----------------------------------+
| Fruit           Price             | Advantages                       |
+===================================+:=================================+
| Oranges         $2.10             | - cures scurvy                   |
|                                   | - tasty                          |
+-----------------------------------+----------------------------------+

: 3 列 72 字符数

+---------------+-------------------+----------------------------------+
| Fruit         | Price             | Advantages                       |
+===============+===================+:=================================+
| Oranges       | $2.10             | - cures scurvy                   |
|               |                   | - tasty                          |
+---------------+-------------------+----------------------------------+

: 2 列 37 字符数

+---------------+-------------------+
| Fruit         | Price             |
+===============+===================+
| Oranges       | $2.10             |
|               |                   |
+---------------+-------------------+

渲染成:

grid table表格宽度示例
grid table表格宽度示例


multiline_tables 为例:


: 4列,72 字符数

------------------------ 
 Centered  Default           Right Left
  Header   Aligned         Aligned Aligned
---------- ------- --------------- -------------------------------------
   First   row                12.0 Example of a row that
                                   spans multiple lines.
------------------------ 


: 3列,34 字符数

------------------------ 
 Centered  Default           Right
  Header   Aligned         Aligned
---------- ------- ---------------
   First   row                12.0
                                  
------------------------ 


: 2列 72 字符数

------------------------ 
Centered   Default     Right        Left
Header    Aligned   Aligned         Aligned
-----------------------------       ------------------------------------
   First    row          12.0       Example of a row that
                                    spans multiple lines.
------------------------ 

渲染成:

multiline table表格宽度示例
multiline table表格宽度示例


引用 #

markdown 内容如下:


> 文本区块引用在电子邮件中非常方便,可以模拟回复文本。
> 此行是同一引文的一部分。

引文分隔符。

> 这是一行非常长的行,换行时仍会正确引用。哦,让我们继续写,以确保它足够长,实际上可以为每个人换行。哦,你可以*将* **Markdown** 放入区块引用中。

> 块引用也可以嵌套...
>> ...通过使用紧挨着彼此的附加大于号...
> > > ...或者在箭头之间留有空格。

渲染成:

blockquotes 文本块示例
blockquotes 文本块示例


gfm alerts #

alerts 是 GitHub-Flavored markdown 中专用的 html css 样式。本模板做了兼容性处理,支持除了 gfm 本身所规定的 note、tip、important、caution, warning 外,还支持 idea、error、success、goal、notification,大小写均可。

markdown 内容如下:


> [!NOTE]
> 这是一个 note

> [!TIP]
> 这是一个提示

> [!IMPORTANT]
> 这很重要

> [!CAUTION]
> 要小心这里所描述的问题

> [!WARNING]
> 警告
>


>[!Idea]
> 这是一个想法
>
>

> [!Error]
> 这里错误了
>

> [!Success]
> 成功的时候

> [!Goal]
> 这是目标
>

> [!Notification]
> 这里有一个通知
>

渲染成:

gfm alerts 示例
gfm alerts 示例


水平线 #

markdown 内容如下:


三个符号或更多

三个连字符 `---` 

---

三个星号 `***`

***


三个下划线 `___`

___

渲染成:

水平线示例
水平线示例


标题 #

markdown 内容如下:


# (示例需要,请忽略) h1 标题
## h2 标题
### h3 标题
#### h4 标题
##### h5 标题
###### h6 标题

或者,对于 H1 和 H2,使用下划线样式: 

(示例需要,请忽略) Alt-H1
======

Alt-H2
------

渲染成:

章节标题示例
章节标题示例


使用方法 #

将本模板安装为 typst local package,即复制或链接到用户本地目录下 {data_dir}/typst/packages/local 下,使在使用 pandoc 转换 markdown 为 pdf 时,指定 --template 参数为模板文件夹 themekit/{版本号}/pandoc/ 中的某一个模板。

  • {data_dir} 与操作系统有关

具体详细操作,见模板程序的 《用户帮助与说明手册》

如何获得本模板 #

若上面演示的效果满足你日常使用要求,或者你有需求自己开发维护一套这样的程序但又不想从零开始的话,可以参考本模板。

本markdown 转 pdf 的 pandoc typst 模板程序,暂没有开源的计划。你可以从以下的链接中获取授权版本。