中文书稿 ggplot 出图规范

纵轴竖排、宋体主题、ragg 设备、图表单元顺序与源图交付

R
ggplot2
Quarto
书稿
Windows
Author

胡华平

Published

October 28, 2026

摘要

中文书稿用 knitr + ggplot2 出图,交给出版社时会反复踩到同一批问题:纵轴题侧躺或单位混在竖排串里;Windows 默认 png() 遇上宋体出全白图;Cursor 终端预览报字体或视口错误,被误当成几何图层写错;图注圈号的 $ 漏进成品;源图格式、文件名与书稿图号对不上。本包把 report-tech2026 已经跑通的做法收到 harness scripts/book-plot/。

核心结论:

  1. 纵轴题满足「自上而下、正向竖排、单位外置」三条。单图用 labs(y = stack_ylab(...)) + theme_ylab_stack,分面用 geom_text(size.unit = "pt"),翻转图底轴横排,热力图 y = NULL。
  2. knitr 全局 dev = "ragg_png",不开 showtext_auto()。渲完用 check-fig-png.py 扫 figure 目录,近白 PNG 视为失败。
  3. 交互 print(ggplot) 报错先用 ggsave(..., device = ragg::agg_png) 隔离;能出图就不要改分析脚本。
  4. Word 图题、表题与图号收紧不在本包,见 Quarto 中文书稿 docx 滤镜。

1. 两套资产的分工

flowchart LR
  rscript[分析脚本 plot 对象] --> bookPlot[book-plot 主题与轴题]
  bookPlot --> knitr[knitr ragg_png]
  knitr --> pngCheck[check-fig-png 门禁]
  knitr --> quartoDocx[quarto-docx Lua 滤镜]
  quartoDocx --> wordOut[docx 成品]

问题 资产
纵轴题竖排、单位顶置、宋体 11pt scripts/book-plot/R/ylab-stack.R
无网格、内向刻度、图例靠下的出版主题 scripts/book-plot/R/theme-tech.R
Windows 空白图 / 中文丢失 scripts/book-plot/R/knitr-ragg.R + py/check-fig-png.py
Cursor httpgd 首帧 depth(NULL) scripts/book-plot/R/httpgd-warmup.R
「图2- 13」、题注在表内、列宽 scripts/quarto-docx/(另一套)

2. 前提

  • R ≥ 4.1,ggplot2 ≥ 3.5(geom_text 的 size.unit),ragg;字体栈 systemfonts ≥ 1.3 并与 textshaping / ragg 配套(见 httpgd 笔记)。
  • 系统已装宋体,族名 simsun(Windows 自带)。macOS / Linux 需自行安装或在 source() 之后改写 ylab_text_family 与 theme_tech 的 family。
  • check-fig-png.py 需 Python 3.8+ 与 Pillow。

3. 复用步骤

3.1 安装

.\scripts\powershell\Install-Harness.ps1 -InstallProfile book-plot -ProjectRoot "D:\path\to\your-repo"

文件落在业务仓 scripts/book-plot/。安装不改 _quarto.yml、章 YAML 与 .Rprofile。

3.2 章 setup

source(here::here("scripts/book-plot/R/knitr-ragg.R"))
source(here::here("scripts/book-plot/R/theme-tech.R"))
source(here::here("scripts/book-plot/R/ylab-stack.R"))

分析脚本若可能被单独 source,在脚本顶部写 if (!exists("stack_ylab")) source(...),与章 setup 共用同一份文件。

3.3 .Rprofile(可选,仅 Cursor / vscode-R 开 httpgd)

if (interactive() && file.exists("scripts/book-plot/R/httpgd-warmup.R")) {
  source("scripts/book-plot/R/httpgd-warmup.R")
}

放在 renv/activate.R 之后。


4. 纵轴题

规范与四种布局写在 纵轴题笔记。要点:

  • 末尾括号视为单位,split_ylab_unit() 拆出,放在纵轴正上方。
  • stack_ylab() 先保护 R&D、RD、AI 等缩写,再按字竖排。项目缩写追加到 ylab_keep。
  • 叠加顺序 + theme_tech + theme_ylab_stack,反过来会被 -90 盖掉。
  • 主题在分析脚本里加完,qmd 只 p <- plot_*。

5. 图表单元与源图

5.1 单元顺序

每张图在章 qmd 里按下列顺序,图号才会与导出文件名一致:

  1. source() 该图的分析脚本(若有)。
  2. num_fig <- num_fig + 1(表用 num_tbl)。
  3. fig_cap:中文图题 + <br> + Figure 2-{num_fig}(章号随章)。
  4. 绘图 chunk:#| label: fig-...,#| fig-cap: !expr fig_cap,对象赋给 p。
  5. 图注与资料来源。
  6. 源图导出 chunk(eval: save_xlsx),文件名取当前 chunk label。

num_fig 必须在同一个 knitr 会话里累加,所以不要把一章拆成可独立 render 的子 qmd。正文引用写 见 @fig-...(@ 两侧留空格),图号收紧交给 quarto-docx 的 crossref-tight-num.lua。

5.2 源图交付

  • 出版社主格式是 xlsx(可编辑矢量,report-tech2026 用 techme::gg2xlsx;其他项目可换成 rvg + officer 的等价导出)。png 可选,不要再导 svg:Windows 上 base svg() 常无法启动。
  • 文件名:Figure{章}-{num_fig}-{chunk label}.xlsx,例如 Figure2-1-fig-bar-budget-province.xlsx。names_chunk <- knitr::opts_current$get()$label,必须与 #| label: 一致。
  • 章 YAML 用参数开关(如 save_xlsx: TRUE)只在交稿时打开,交稿后改回,避免日常误写盘。
  • 交稿核对:导出目录文件序号与书稿「图2-N」一致;num_fig 等于 fig- label 个数。

6. 图注圈号与行内 $

图注圈号写成 $\U2460$ 时,Pandoc 把一对 $...$ 当行内公式。收尾 $ 后面不能紧跟阿拉伯数字,否则圈号不成对,美元符漏进 html/docx(成品出现 $④$2025年)。

宜写($ 后空格或汉字):

"$\U2460$ 2018年及以后未统计……"
"$\U2463$在2025年主要为申报……"

不宜写:

"$\U2460$2025年为第一批……"

glue() 里 {year} 紧接收尾 $ 时,展开后同样是数字。真公式(如 $\\times$)也不要紧贴阿拉伯数字。


7. 验收

层级 命令 平台
L0 切词 Rscript scripts/book-plot/test/Rscript/test-ylab-stack.R 任意
L1 出图 Rscript scripts/book-plot/test/Rscript/run-l1-ragg.R Windows(需宋体)
业务仓渲完 python scripts/book-plot/py/check-fig-png.py <figure-dir> ... 任意

L1 在独立 R 进程里画单图与分面各一张,经 ragg::agg_png 输出到 test/output/,再跑近白检查。轴题位置与单位顶置仍需目视。

theme_tech 置空了 plot.background。手工 ggsave() 不写 bg = "white" 会得到透明底;check-fig-png.py 把透明像素占比达到 5% 的 PNG 判为失败(Pillow 把透明底合成黑色,不能只靠「是不是近白」)。单独出图写 ggsave(..., device = ragg::agg_png, bg = "white")。knitr 的 ragg_png 本身是白底。沙盒规程见 test/README.md,通用流程见 配置资产验证工作流。


8. 不要做

  • showtext_auto() 与 ragg_png 叠用。
  • 用 suppressWarnings() 或空 filter 画出空白 ggplot 交差;缺数据应 stop() / warning()。
  • 为交互预览报错改 geom_*。
  • 为无量纲图编造单位;翻转图左侧再竖排数值题。
  • 一次改完全部旧脚本;迁移时一脚本一提交,每个脚本单独 ggsave(ragg) 目视。