14  Typst pdf写作

本文档总结了使用 Quarto 结合 Typst 引擎制作国际会议双语(英文/中文)邀请信的技术要点。重点记录了从 PDF 模板复刻到 Typst 代码实现过程中的布局技巧、字体处理及 Quarto 交互细节。

14.1 核心工作流

  1. 数据源 (.qmd): 撰写内容,通过 YAML header 指定输出格式。
  2. 样式模板 (.typ): 定义页面尺寸、页眉页脚、字体规范、自定义函数。
  3. 编译引擎: Quarto 调用 Pandoc 将 Markdown 转换为 Typst 代码,再由 Typst 编译器生成 PDF。

14.2 Typst 模板开发关键技术

14.2.1 页面布局与背景水印 (Page & Background)

在公文排版中,红头文件和盖章效果至关重要。

  • 背景印章定位:使用 place 函数结合 dx/dy 进行绝对定位。
  • 透明度处理opacity 是一个函数包裹内容,而非 image 的参数。
#set page(
  paper: "a4",
  margin: (top: 6cm, bottom: 3cm, left: 2cm, right: 2cm), // 留出头部给红头
  background: [
    #place(
      bottom + left, // 基准位置
      dx: 2.8cm,     // 水平偏移
      dy: -3.5cm,    // 垂直偏移(向上)
      // 正确的透明度写法
      opacity(0.85, image("../../pic/logo/logo-seal.png", width: 4.8cm))
    )
  ]
)

14.2.2 复杂页眉设计 (Complex Header)

英文版页眉需要“左侧校徽 + 间隔 + 右侧校名/Logo”的组合,且要求高度对齐。

  • Grid 布局:使用 #grid 实现分列布局。
  • Stack 堆叠:在某一列中使用 #stack 实现垂直排列(如图片 + 文字)。
  • 微调间距:使用 v(-0.3cm) 等负值来强制拉近元素间距,解决图片自带留白过大的问题。
header: [
  #grid(
    columns: (auto, auto, 1fr), // 三列:空白占位 | 图标 | 文字堆叠
    gutter: 0.8em,
    box(width: 0.2cm)[ ], // 强制左侧留白
    image("logo.png", height: 3.0cm),
    align(center)[
      #stack(
        dir: ttb,
        spacing: 0em,
        image("name.png", height: 3.0cm),
        v(-0.3cm), // 负边距,消除图片底部空白
        text(weight: "bold")[Northwest A&F University]
      )
    ]
  )
  #line(length: 100%, stroke: 0.75pt + blue) // 贯穿线
]

14.3 中文排版难点与解决方案

14.3.1 中文字体回退机制 (Font Fallback)

如果没有相应字体(如方正小标宋),则可以考虑首先下载字体到本地。对于windows用户,把下载后的字体文件复制到 C:\Windows\Fonts 目录下完成字体安装。然后就使用 typst fonts 命令查看字体名称。例如,方正小标宋的PostScript名称是 FZXiaoBiaoSong-B05S

Typst 对中文字体的识别依赖于系统字体的英文名称PostScript名称。为保证兼容性,应使用字体数组。

// 优先级:方正小标宋 -> 方正粗黑宋 -> 系统宋体
#text(
  font: ("FZXiaoBiaoSong-B05S", "FZXBSJW--GB1-0", "FZCuHeiSongS-B-GB", "SimSun"), 
  size: 34pt, 
  fill: red
)[西北农林科技大学]

14.3.2 伪粗体实现 (Fake Bold)

许多中文字体(如仿宋 FangSong)本身不包含 Bold 字重。直接设置 weight: "bold" 无效(会回退到宋体或不显示粗体)。

  • 解决方案:使用 stroke (描边) 来模拟加粗效果。
// 仿宋伪粗体:描边 0.5pt
#text(font: "FangSong_GB2312", size: 22pt, stroke: 0.5pt)[邀请函]

14.3.3 首行缩进与例外处理

中文正文通常需要首行缩进 2 字符,但“尊敬的专家:”等称呼语需要顶格。

  • 全局设置
#set par(first-line-indent: 2em)
  • 例外处理(自定义函数): 在 .typ 中定义 helper 函数:
#let indent(enable, content) = {
    if not enable {
        set par(first-line-indent: 0em)
        content
    } else {
        content
    }
}

14.4 Quarto 与 Typst 的交互

14.4.1 嵌入原生 Typst 代码 (Raw Typst)

当 Markdown 语法不足以表达特定的排版需求(如调用上面的 indent 函数或复杂的落款布局)时,需要在 .qmd 中使用 ```{=typst} 块。

<!-- 在 qmd 文件中 -->
```{=typst}
#indent(false)[尊敬的专家:]
```

14.4.2 落款样式的封装

为了让 .qmd 专注于内容,将复杂的落款布局(右对齐容器+内部居中)封装在 .typ 模板的函数中。

**Template (.typ):**
```{=typst}
#let sign(content) = {
  v(1em)
  align(right)[
    #block(width: 50%)[ // 限制宽度块
      #set align(center) // 块内文字居中
      #set par(first-line-indent: 0em)
      #text(size: 16pt)[#content]
    ]
  ]
}
```

Quarto (.qmd):

```{=typst}
#sign[
西北农林科技大学\
2025年10月30日
]
```

14.5 遇到的坑 (Pitfalls)

  1. Typst 包版本依赖

    • 尝试使用 @preview/ctyp 优化中文排版,但 Quarto 内置的 Typst 版本(如 0.13.0)可能滞后于包的最低要求(如 0.13.1)。
    • 对策:暂时手动处理字体和缩进,或手动管理 Typst 编译器版本。
  2. Markdown 解析干扰

    • 直接在 qmd 写 #func[] 可能会被视为普通文本。必须包裹在 ```{=typst} 中。
  3. 字体名称不匹配

    • Windows 下“方正小标宋”可能识别为 FZXBSJW--GB1-0,但也可能是 FZXiaoBiaoSong-B05S。使用 typst fonts 命令检查本地实际名称至关重要。

14.6 应用案例

  • 中俄中心2026CRC会议邀请信(英文版/中文版)的Typst实现。

14.7 方法总结

通过 Quarto + Typst,我们成功实现了:

  1. 样式分离:所有视觉噪音(字号、颜色、间距)保留在 .typ 文件中。
  2. 内容专注.qmd 文件仅包含文本内容,便于维护。
  3. 高保真输出:通过 Typst 强大的布局能力,完美复刻了 Word/PDF 模板的红头文件格式。