
Svg Book Illustrator
- 38 installs
- 543 repo stars
- Updated August 5, 2026
- cat-xierluo/legal-skills
Generates clean, professional static SVG technical figures such as architecture, flow, and hierarchy diagrams for book chapters and formal articles.
About
Generates concise professional static SVG technical illustrations focused on architecture, flow, and hierarchy diagrams. Developers use it to add embedded figures to book chapters or formal articles suitable for print.
- Static SVG embedded directly in markdown
- Transparent background, print-friendly diagram style
Svg Book Illustrator by the numbers
- 38 all-time installs (skills.sh)
- Ranked #933 of 1,335 Generative Media skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/cat-xierluo/legal-skills --skill svg-book-illustratorAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 38 |
|---|---|
| repo stars | ★ 543 |
| Last updated | August 5, 2026 |
| Repository | cat-xierluo/legal-skills ↗ |
What it does
Generates clean, professional static SVG technical figures such as architecture, flow, and hierarchy diagrams for book chapters and formal articles.
Files
SVG Book Illustrator
为书籍章节和正式文章生成简洁专业的 SVG 技术配图。
本 Skill 生成静态 SVG,直接嵌入 Markdown 文件(<svg> 标签),风格为简洁专业、透明背景 + 内部模块多色柔和区分(v1.5.0),适合纸质出版。快速开始
/svg-book-illustrator @path/to/chapter.md核心工作流程
第一阶段:分析章节,规划插图
1. 读取章节 Markdown 文件 2. 如果 references/diagram-catalog.md 有当前章节的预定义插图,匹配之 3. 扫描章节内容,识别适合配图的位置:
- 架构描述处("X 层"、"体系"、"架构"等)
- 流程描述处("步骤"、"流程"、"阶段"等)
- 对比描述处("vs"、"对比"、"前后"等)
- 层次描述处("层级"、"分类"、"金字塔"等)
- 循环描述处("循环"、"迭代"、"闭环"等)
- 生态/关系描述处("生态"、"要素"、"关系"等)
4. 在合适位置插入占位符 [[FIG:N:简要描述]](N 从 1 开始编号) 5. 列出所有规划的插图(类型、位置、描述),除非用户明确要求先确认,否则继续生成 SVG
插图密度:每章「一节一张」为基准,数万字章节 6-8 张,宁精勿滥;纯 walkthrough / 总结节可省图避免冗余。密度指标:图/节 ≥ 0.7、图/万字 ≥ 0.8,低于判「偏少」并提示补图位置(详见第四阶段 + references/review-checklist.md)。
第二阶段:生成 SVG
完成插图规划后,逐张生成:
1. 根据插图描述选择布局模板(flow / layer / matrix / hub / tree / cycle,或组合模板) 2. 读取 references/layout-templates.md 获取模板规范 3. 按 references/style-guide.md 的设计规范生成 SVG 代码 4. 将 <svg> 标签嵌入 Markdown,替换对应占位符 5. 在 SVG 下方添加图注:**图 N-X:图标题**
第三阶段:归档
生成完成后,提取所有 SVG 到独立文件:
python scripts/extract_svgs.py path/to/chapter.md --output output/figures/第四阶段:审查与验收(必须过三道门禁)
生成 + 嵌入后,逐章过审查门禁,详见 references/review-checklist.md:
① 配图密度审查:图/节 ≥ 0.7、图/万字 ≥ 0.8;低于判「偏少」,列出可补图的小节。跨章均衡——相邻章密度不宜骤变(认知/入门篇图密度不应远低于方法/实战篇)。
② 图-正文论点一致性审查:每张图回溯所在小节,核对节点数 / 层级名 / 流程方向 / 对比维度 与正文表述一致;替换 mermaid / ASCII 图时原信息(节点、关系、标注)不丢失;图注准确概括图内容,不夸大不遗漏。
③ 视觉目检(多模态渲染后眼检):SVG 渲染为 PNG(scripts/svg2png.js 或 rsvg-convert -w 720 -h 400)后,用多模态模型逐张查——文字不溢出容器、框不重叠(间距 ≥24px)、箭头落位方向正确、字号可读(节点≥16px 副≥12px)、黑白可辨、整体美观留白合理。发现问题回改 SVG 坐标,复检直到目检通过。
多模态生产提示:若环境支持图像理解,③ 必须真正"看"渲染图,不能只靠 xmllint / rsvg 无警告间接验证——语法通过 ≠ 布局美观,溢出/重叠/箭头错位只有肉眼(或多模态模型)能发现。
---
布局模板
8 种布局模板(6 种基础 + 2 种组合),详见 references/layout-templates.md。
| 模板 | 适用场景 | 典型元素数 |
|---|---|---|
| flow | 流程图、步骤图、管道图 | 3-5 个节点(水平≤4) |
| layer | 层次架构、分层堆叠 | 3-4 层 |
| matrix | 前后对比、并排比较 | 2 列 |
| hub | 中心辐射、生态关系 | 1 核心 + 4-8 外围 |
| tree | 层级结构、组织图、金字塔 | 3 层 |
| cycle | 循环流程、迭代闭环 | 4-6 个节点 |
| flow+matrix | 递进流程附带阶段对比 | 3-4 阶段 + 对比区 |
| flow+hub | 编排流程中节点展开 | 主流程 + 展开节点 |
---
设计规范
详见 references/style-guide.md。核心要点:
- 画布:720x400,透明背景(硬约束)——不画背景矩形、不设底色;安全边距 40px(基于 16开 115mm 通栏物理尺寸推算)。底色由书页/排版提供。
- 风格:简洁、专业、静态(无动画、无渐变、无滤镜、无 emoji)
- 颜色(v1.5.0 透明背景 + 内部模块多色版):新生成图透明底,配色用于 SVG 内部不同模块 / 分支 / 方向 / 层级之间的多色柔和区分——颜色尽量多样(一图 4-6 种甚至更多柔和模块色)。从 8 组预定义调色板(P1 雾蓝系 / P2 浅青系 / P3 嫩绿系 / P4 暖米系 / P5 浅紫系 / P6 浅粉系 / P7 暖灰系 / P8 混合柔和系)选 1 组,组内 5-6 个柔和模块色按"模块 1 取色 1、模块 2 取色 2…"依次分配,相邻模块不同色。文字色统一深灰
#2D3436/#636E72保证可读。打印友好约束(文字 vs 所在模块填充色对比 ≥4.5:1 WCAG AA、相邻模块区分度 ≥10%、模块色明度 L≥80、禁高饱和荧光、CMYK 不偏色、灰度差≥10%)。仅新生成图用新配色,main 上既有 34 张白底单色 SVG 保持稳定不回改*。 - 颜色语法硬约束:颜色只用
fill/stroke属性内联——禁 `<style>` 块定义颜色、禁 `<svg>` 开标签写 font-family、禁 CSS 变量/class、禁画背景矩形(已验证的 Obsidian 渲染 + 透明背景硬约束,详见 style-guide.md §5.4)。 - 文字:继承渲染环境默认无衬线字体(PingFang SC / Microsoft YaHei 落在系统层),节点标签 18px 起(16开 115mm 通栏下物理 2.88mm = 8.2pt)
- 形状:圆角矩形(rx="6")、简洁箭头、最小 24px 间距
- 印刷:黑白可辨,颜色不是唯一区分手段(黑白降级仍可辨是硬约束)
---
插图目录
references/diagram-catalog.md 定义插图目录格式和创建方法。
---
PNG 导出
出版社通常需要位图版本。使用 scripts/svg2png.js 将 SVG 转为高分辨率 PNG:
# 单张转换(默认 600 DPI)
node scripts/svg2png.js input.svg
# 指定输出文件和 DPI
node scripts/svg2png.js input.svg output.png 300
# 批量转换目录下所有 SVG
find figures/ -name "*.svg" -exec node scripts/svg2png.js {} \;依赖:PNG 导出功能需要 Puppeteer 和 Chrome/Chromium。首次使用前运行:npm install puppeteer
印刷 DPI 建议:
- 300 DPI:最低印刷要求
- 600 DPI:推荐,清晰锐利
- 1200 DPI:线条图最高质量
---
成功标准
- 每张图只表达 1-2 个核心概念
- 架构图层次清晰,流程图逻辑通顺
- 风格简洁专业,无装饰性元素
- SVG 在 Markdown 预览中正确渲染(语法门禁:开标签 viewBox 720×400、无 font-family、无
<style>、颜色只用fill/stroke属性内联、无背景矩形、xmllint well-formed、rsvg 无警告) - 图注格式统一:图 N-X:标题
- 配图密度达标:图/节 ≥ 0.7、图/万字 ≥ 0.8
- 图-正文一致:节点/层级/方向/维度与正文论点吻合,替换 mermaid/ASCII 图信息无损
- 视觉目检通过:渲染后无溢出/重叠/箭头错位,字号可读、黑白可辨、美观
- 印刷友好:16开 115mm 通栏下文字 ≥8pt 清晰可读,黑白打印可辨,文字对比度 ≥4.5:1(WCAG AA),CMYK 转换不偏色
- 配色合规(v1.5.0):新生成图透明背景(无画布底色矩形);从调色板 8 组选 1 组,组内模块色用于内部模块多色柔和区分(一图 4-6 色)、相邻模块不同色;文字色统一深灰;颜色只用
fill/stroke属性内联;既有 34 张白底单色图保持稳定不回改
语法门禁(xmllint/rsvg)只是必要不充分条件——保证 SVG 合法,不保证图正确美观。密度 + 一致性 + 目检三道审查才是验收依据(references/review-checklist.md)。CHANGELOG
[v1.5.0] - 2026-06-20
新增(全彩印刷配色,方向经作者 2026-06-20 纠正后定稿)
- 透明背景 + 内部模块多色柔和区分(supersede DEC-002 纯白底 / DEC-003 单一强调色):所有新生成图透明背景、不加任何画布底色;颜色用于 SVG 内部不同模块 / 不同方向之间的多色区分,颜色尽量多样但柔和(去饱和)。
references/style-guide.md§5.2 预定义 8 组柔和模块色调色板(P1 雾蓝 / P2 浅青 / P3 嫩绿 / P4 暖米 / P5 浅紫 / P6 浅粉 / P7 暖灰 / P8 混合柔和系),每组 5-6 个模块色用于内部多模块区分;文字统一深灰#2D3436。- §5.3 打印友好:文字色 vs 所在模块填充色对比 ≥ 4.5:1(WCAG AA)、相邻模块区分度 ≥10%、模块填充明度 L*≥80、禁高饱和荧光、CMYK 不偏色、灰度可辨。
references/review-checklist.md③ 视觉目检:新增"透明背景(grep 源码无画布底矩形)" + "内部模块多色(一图 4-6 色、相邻不同色)"检查项;对比度口径改为文字 vs 模块填充色。
优化
- 颜色仅用
fill/stroke属性内联(不引入 `<style>` 块 / 不在 `<svg>` 开标签写 font-family / 不画背景矩形 / 不用 class·CSS 变量),保持 xmllint well-formed + rsvg 无警告 + Obsidian 渲染三重兼容(沿用feedback_svg_embed_syntax硬约束)。 references/layout-templates.md5 个模板骨架删除背景矩形 +<style>块,节点填充改用同色组不同模块色。
边界
- 仅新生成图采用透明底 + 内部多色;main 既有 34 张白底单色 SVG 保持稳定不回改(作者待确认)。透明底与白底是两种不同做法,老图白底属历史兼容。
[v1.4.0] - 2026-06-17
新增
- 第四阶段:审查与验收(三道门禁):生成 + 嵌入后逐章过审查,作为配图验收依据
- ① 配图密度审查:图/节 ≥ 0.7、图/万字 ≥ 0.8,低于判「偏少」并定位可补图小节;纯 walkthrough / 总结节可省;跨章密度比 < 2×
- ② 图-正文论点一致性审查:逐图回溯所在小节,核对节点数 / 层级名 / 流程方向 / 对比维度与正文一致,替换 mermaid/ASCII 图信息无损
- ③ 视觉目检:SVG 渲染为 PNG 后多模态逐图眼检,查文字溢出 / 框重叠 / 箭头错位 / 字号可读 / 黑白可辨
references/review-checklist.md:三道审查的量化指标、判定表、逐图核对清单与多模态目检 prompt 模板(含 legal-ai-skill-book 2026-06-17 实测密度参考)
优化
- 插图密度基准从「每章 3-8 张」改为「一节一张为基准,数万字章节 6-8 张」+ 量化指标(图/节 ≥ 0.7、图/万字 ≥ 0.8)
- 强调语法门禁(xmllint well-formed / rsvg 无警告 / 无 font-family / 无
<style>)只是必要不充分条件——只保证 SVG 合法,不保证图正确美观;密度 + 一致性 + 目检三道审查才是验收依据
[v1.3.0] - 2026-05-17
新增
scripts/svg2png.js:SVG → PNG 高分辨率转换(由 svg-article-illustrator 简化而来)- 支持单文件和目录批量转换
- 默认 600 DPI,支持 72–2400 DPI
- SKILL.md 新增 PNG 导出使用说明
- 新增
LICENSE.txt,补齐 MIT 许可证全文
修复
- 修复
scripts/svg2png.js使用networkidle0导致简单 SVG 转换超时的问题,改为domcontentloaded并增加 SVG 加载等待 - 修复 PNG 转换失败时浏览器进程可能未关闭的问题,使用
finally兜底关闭 - 修复水平
flow模板 4 节点尺寸不一致的问题,统一为 140px 节点并重算坐标
优化
- SKILL.md 精简:去掉"通用性"、"per-book 配置"等冗余说明,以功能说话
文档完善
- 将技能级任务跟踪文件从
ROADMAP.md更正为TASKS.md,符合本仓库 Skill 文档约定 - 调整第一阶段流程描述:默认插入占位符并继续生成,仅在用户明确要求时等待确认
[v1.2.0] - 2026-05-17
重大变更:从物理尺寸反推所有参数
- 字号全面校准:基于 16开 115mm 通栏印刷宽度推算
- 节点标签:14px → 18px(物理 2.88mm = 8.2pt,过中文印刷 8pt 下限)
- 子标签:12px → 16px(物理 2.56mm = 7.3pt,仅限简短补充)
- 层标签:14px → 20px(物理 3.20mm = 9.1pt)
- 图标题:16px → 22px(物理 3.52mm = 10pt)
- 标签字数限制收紧:18px 下每节点最多 8 个汉字(原 14px 下 12 字)
- 元素密度下调:水平 flow 最多 4 节点(原 5),hub 最多 5 外围(原 6)
- 间距放大:最小间距 20px → 24px,水平间距 24px → 28px
- 新增完整印刷推算章节:style-guide.md 第二节,含中国开本尺寸表、pt 换算公式、不同开本的最低字号表
其他
- layout-templates.md 所有 SVG 骨架的字号、节点尺寸、坐标同步更新
- 新增大32开适配说明(大32开建议缩小 viewBox 或放大字号)
[v1.1.0] - 2026-05-17
新增与优化
- 组合模板、印刷黑白兼容、通用化
- 去掉书籍绑定,diagram-catalog.md 改为纯格式模板
- 场景覆盖分析
v1.0.0 (2026-05-17)
由 svg-article-illustrator(公众号文章配图 Skill)演化而来。针对印刷出版场景重新设计:去掉 SMIL 动画/emoji/非白底等微信适配特性,画布改为 720×400(书籍版面比例),字号和间距按物理尺寸反推,扩展为 6 种通用布局模板。
初始包含:SKILL.md、style-guide.md、layout-templates.md、diagram-catalog.md、extract_svgs.py
MIT License
Copyright (c) 2025 杨卫薪律师(微信ywxlaw)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
插图目录模板(diagram-catalog)
本文件是 per-book 配置。使用本 skill 前,先按以下格式创建你的书籍插图目录。
工作方式:Phase 1 分析章节时读取此文件,匹配当前章节的预定义插图,结合章节内容识别额外配图机会。
---
格式定义
每条插图包含以下字段:
| 字段 | 必填 | 说明 | 示例 |
|---|---|---|---|
| # | 是 | 全书统一编号 | 1、G1(全书级) |
| 位置 | 是 | 章节小节编号或描述 | 6.1、附录 |
| 类型 | 是 | 布局模板名(见 layout-templates.md) | flow、layer、matrix、hub、tree、cycle,或组合如 flow+matrix |
| 描述 | 是 | 插图要表达的核心内容 | "识别→梳理→编写→验证→迭代的五步流程" |
| 优先级 | 是 | P0 核心 / P1 重要 / P2 辅助 | P0 |
| 备注 | 否 | 生成注意事项 | "需突出数据对比"、"参见大纲论证链" |
优先级说明
| 级别 | 含义 | 典型密度 |
|---|---|---|
| P0 | 核心插图,必须完成。表达全书关键概念或方法论。 | 每章 0-2 张 |
| P1 | 重要插图,建议完成。覆盖关键对比、流程、架构。 | 每章 1-3 张 |
| P2 | 辅助插图,按需完成。补充说明或装饰性。 | 按需 |
组合模板
标注为 flow+matrix 等组合类型的插图,参见 layout-templates.md 的"组合模板"章节。
---
如何创建插图目录
步骤 1:通读书籍大纲
阅读全书大纲或目录,标记以下位置:
- 架构描述("X 层"、"体系"、"架构")
- 流程描述("步骤"、"流程"、"阶段")
- 对比描述("vs"、"对比"、"前后")
- 层次描述("层级"、"分类"、"金字塔")
- 循环描述("循环"、"迭代"、"闭环")
- 生态/关系描述("生态"、"关系"、"要素")
步骤 2:按章填写表格
为每个标记的位置创建一条插图记录。按以下格式:
### chXX 章节名
| # | 位置 | 类型 | 描述 | 优先级 |
|---|------|------|------|--------|
| 1 | X.1 子节名 | flow | 简要描述图要表达什么 | P0 |
| 2 | X.2 子节名 | matrix | 简要描述 | P1 |步骤 3:标注优先级
- 方法论核心图、全书贯穿概念图 → P0
- 重要流程、对比、架构 → P1
- 补充说明、装饰性 → P2
步骤 4:控制密度
每章 3-8 张。如果某章超过 8 张,将优先级最低的降级或合并。
---
模板示例
以下是一个虚构的技术书籍插图目录片段,展示完整格式:
### ch03 系统架构设计
| # | 位置 | 类型 | 描述 | 优先级 | 备注 |
|---|------|------|------|--------|------|
| 9 | 3.1 架构概览 | layer | 表现层→服务层→数据层的三层架构 | P0 | 本章核心图 |
| 10 | 3.2 微服务通信方式 | matrix | 同步 vs 异步通信对比 | P1 | |
| 11 | 3.3 服务注册发现流程 | flow | 注册→心跳→发现→调用的线性流程 | P1 | |
| 12 | 3.4 全链路监控闭环 | cycle | 采集→聚合→告警→处理的闭环 | P2 | |布局模板定义
6 种基础布局 + 2 种组合模板。基于 16开(115mm 通栏) 物理尺寸推算。
所有字号已按印刷可读性校准:节点标签 18px(物理 2.88mm = 8.2pt)。
详见 style-guide.md 的物理尺寸推算。
配色说明(v1.5.0):以下骨架示例是透明背景——不画任何背景矩形,直接画模块。模块填充色用 P1-P8 调色板的内部模块色(详见 style-guide.md §5.2)。骨架中示例统一用 P1 雾蓝系的几个模块色(#D6E4F0#C5D9E8#B8CFE0等)占位演示——照搬时把相邻模块换成同组不同色,让一图内部 4-6 种柔和色区分。文字色统一深灰#2D3436/#636E72。
>
字体说明:骨架中不再出现<style>text { font-family... }</style>块——字体由渲染环境继承默认无衬线。这是已验证的 Obsidian 渲染硬约束(memoryfeedback_svg_embed_syntax)。若个别环境需强制字体,在每个<text>上单独写font-family,绝不在<svg>开标签或<style>块统一设置。
---
1. flow(流程图)
适用:步骤流程、管道图、工作流、分叉路径
结构特征
- 节点水平或垂直排列,箭头连接
- 起始节点用强调色,终止节点加深边框
- 分支用分叉箭头
布局参考
水平流程(最多 4 节点):
3 节点:宽 150px,高 48px
4 节点:宽 140px,高 48px
y 居中:176
3 节点 rect x = 40, 285, 530(中心 x = 115, 360, 605)
4 节点 rect x = 40, 207, 373, 540(中心 x = 110, 277, 443, 610)垂直流程(最多 5 节点):
节点尺寸:宽 200px,高 48px
x 居中:260(偏左,右侧可放注释)
3 节点:y = 60, 170, 300(间距 110)
4 节点:y = 45, 130, 215, 300(间距 85)
5 节点:y = 30, 105, 180, 255, 330(间距 75)分叉流程:
主干水平 3 节点,第二个节点向下扇出 2-3 条分支
分支 y = 主干 y + 90
分支间距 140pxSVG 骨架
<svg viewBox="0 0 720 400">
<defs><marker id="arrow" viewBox="0 0 10 10" refX="10" refY="5" markerWidth="8" markerHeight="8" orient="auto"><path d="M 0 0 L 10 5 L 0 10 z" fill="#2D3436"/></marker></defs>
<!-- 注意:无背景矩形,透明底 -->
<!-- 节点 1(起始,P1 雾蓝系模块色 1) -->
<rect x="40" y="176" width="140" height="48" rx="6" fill="#D6E4F0" stroke="#2D3436" stroke-width="2"/>
<text x="110" y="205" text-anchor="middle" font-size="18" fill="#2D3436">识别场景</text>
<!-- 箭头 -->
<line x1="184" y1="200" x2="199" y2="200" stroke="#2D3436" stroke-width="2" marker-end="url(#arrow)"/>
<!-- 节点 2(模块色 2,与节点 1 不同色) -->
<rect x="207" y="176" width="140" height="48" rx="6" fill="#C5D9E8" stroke="#2D3436" stroke-width="2"/>
<text x="277" y="205" text-anchor="middle" font-size="18" fill="#2D3436">梳理流程</text>
<!-- 箭头 -->
<line x1="351" y1="200" x2="365" y2="200" stroke="#2D3436" stroke-width="2" marker-end="url(#arrow)"/>
<!-- 节点 3(模块色 3) -->
<rect x="373" y="176" width="140" height="48" rx="6" fill="#B8CFE0" stroke="#2D3436" stroke-width="2"/>
<text x="443" y="205" text-anchor="middle" font-size="18" fill="#2D3436">编写</text>
<!-- 箭头 -->
<line x1="517" y1="200" x2="532" y2="200" stroke="#2D3436" stroke-width="2" marker-end="url(#arrow)"/>
<!-- 节点 4(终止,模块色 4,深边框强调) -->
<rect x="540" y="176" width="140" height="48" rx="6" fill="#DCE8F2" stroke="#2D3436" stroke-width="3"/>
<text x="610" y="205" text-anchor="middle" font-size="18" fill="#2D3436">验证</text>
</svg>---
2. layer(层次图)
适用:分层架构、堆叠结构、层级依赖
布局参考
3 层布局:
层 1:y = 80, 高度 70px(强调色)
层 2:y = 170, 高度 70px
层 3:y = 260, 高度 70px
4 层布局:
层 1:y = 45, 高度 56px
层 2:y = 121, 高度 56px
层 3:y = 197, 高度 56px
层 4:y = 273, 高度 56px
每层宽度:580px(x: 70–650)
层间距:20px
层标签居中:20px, font-weight 600SVG 骨架
<svg viewBox="0 0 720 400">
<!-- 无背景矩形,透明底 -->
<!-- 层 1(顶层,P1 雾蓝系模块色 1) -->
<rect x="70" y="80" width="580" height="70" rx="6" fill="#D6E4F0" stroke="#2D3436" stroke-width="2"/>
<text x="360" y="122" text-anchor="middle" font-size="20" font-weight="600" fill="#2D3436">应用层</text>
<!-- 层 2(模块色 2,相邻层不同色) -->
<rect x="70" y="170" width="580" height="70" rx="6" fill="#C5D9E8" stroke="#2D3436" stroke-width="2"/>
<text x="360" y="212" text-anchor="middle" font-size="20" font-weight="600" fill="#2D3436">能力层</text>
<!-- 层 3(底层,模块色 3) -->
<rect x="70" y="260" width="580" height="70" rx="6" fill="#B8CFE0" stroke="#2D3436" stroke-width="2"/>
<text x="360" y="302" text-anchor="middle" font-size="20" font-weight="600" fill="#2D3436">基础层</text>
</svg>---
3. matrix(对比图)
适用:前后对比、方案对比、四象限
布局参考
2 列对比:
左列:x = 40–340,宽 300px
右列:x = 380–680,宽 300px
列间距:40px
列标题:y = 50,高度 48px
行单元:y 从 118 开始,每行高度 60px,间距 12px
3 行:y = 118, 190, 262SVG 骨架
<svg viewBox="0 0 720 400">
<!-- 无背景矩形,透明底 -->
<!-- 左列标题(P8 混合系暖米色) -->
<rect x="40" y="50" width="300" height="48" rx="6" fill="#E8D8C0" stroke="#2D3436" stroke-width="2"/>
<text x="190" y="80" text-anchor="middle" font-size="18" font-weight="600" fill="#2D3436">方案 A</text>
<!-- 右列标题(P8 混合系雾蓝色,与左列不同色相) -->
<rect x="380" y="50" width="300" height="48" rx="6" fill="#D6E4F0" stroke="#2D3436" stroke-width="2"/>
<text x="530" y="80" text-anchor="middle" font-size="18" font-weight="600" fill="#2D3436">方案 B</text>
<!-- 行单元(同列内浅一档模块色) -->
<rect x="40" y="118" width="300" height="60" rx="6" fill="#EDDFC8" stroke="#2D3436" stroke-width="1.5"/>
<text x="190" y="153" text-anchor="middle" font-size="18" fill="#2D3436">特点 1</text>
<!-- ... -->
</svg>---
4. hub(中心辐射图)
适用:生态关系、核心概念+关联要素
布局参考
核心节点:中心 (360, 200),尺寸 140×52
外围节点:半径 130px 的圆上均匀分布,尺寸 120×44
4 个外围(十字形):
上(360, 65) 右(505, 200) 下(360, 335) 左(215, 200)
5 个外围:
(360, 65) (483, 140) (440, 300) (280, 300) (237, 140)SVG 骨架
<svg viewBox="0 0 720 400">
<!-- 无背景矩形,透明底 -->
<!-- 连线(底层) -->
<line x1="360" y1="174" x2="360" y2="87" stroke="#2D3436" stroke-width="1.5"/>
<!-- ... -->
<!-- 核心节点(P1 雾蓝系模块色 1,深边框强调) -->
<rect x="290" y="174" width="140" height="52" rx="6" fill="#D6E4F0" stroke="#2D3436" stroke-width="2.5"/>
<text x="360" y="206" text-anchor="middle" font-size="18" font-weight="600" fill="#2D3436">核心概念</text>
<!-- 外围节点(每个用同组不同模块色,避免色块连片) -->
<rect x="300" y="50" width="120" height="44" rx="6" fill="#C5D9E8" stroke="#2D3436" stroke-width="1.5"/>
<text x="360" y="78" text-anchor="middle" font-size="18" fill="#2D3436">要素 A</text>
<!-- ... -->
</svg>---
5. tree(层级/树形图)
适用:组织结构、分类体系、金字塔
布局参考
2 层(1→3-5):
根:x=360, y=100, 宽 160px, 高 48px
子节点:y=260, 宽 140px, 高 48px
3 子:x = 120, 360, 600
5 子:x = 50, 190, 360, 530, 660
3 层(1→3→5):
根:y=40, 宽 160px
分支:y=180, 宽 140px, x = 120, 360, 600
叶子:y=320, 宽 120px, 每分支下 1-2 个
金字塔(3 层):
层 1:宽 200px, 居中, y=70, 高 64px
层 2:宽 400px, 居中, y=170, 高 64px
层 3:宽 580px, 居中, y=270, 高 64pxSVG 骨架
<svg viewBox="0 0 720 400">
<!-- 无背景矩形,透明底 -->
<!-- 根节点(P1 雾蓝系模块色 1) -->
<rect x="280" y="100" width="160" height="48" rx="6" fill="#D6E4F0" stroke="#2D3436" stroke-width="2"/>
<text x="360" y="130" text-anchor="middle" font-size="18" font-weight="600" fill="#2D3436">根概念</text>
<!-- 连线 -->
<line x1="310" y1="148" x2="190" y2="260" stroke="#2D3436" stroke-width="1.5"/>
<line x1="360" y1="148" x2="360" y2="260" stroke="#2D3436" stroke-width="1.5"/>
<line x1="410" y1="148" x2="530" y2="260" stroke="#2D3436" stroke-width="1.5"/>
<!-- 子节点(每个分支不同模块色) -->
<rect x="120" y="260" width="140" height="48" rx="6" fill="#C5D9E8" stroke="#2D3436" stroke-width="2"/>
<text x="190" y="289" text-anchor="middle" font-size="18" fill="#2D3436">分支 A</text>
<!-- ... -->
</svg>---
6. cycle(循环图)
适用:闭环流程、迭代循环
布局参考
4 节点(矩形排列):
上:(305, 50), 140×48
右:(520, 176), 140×48
下:(305, 302), 140×48
左:(60, 176), 140×48
中心标签:x=360, y=205, 18px
5 节点(圆上均布,半径 120px):
中心 (360, 200)---
7. 组合模板
flow+matrix
上下分区:
上半部分(y: 50–150):3-4 个水平阶段节点,宽 130px,高 48px
下半部分(y: 170–380):每阶段下方的对比区域flow+hub
主流程 + 关键节点展开:
主流程 y 上移至 100,节点宽 130px, 高 48px
关键节点下方展开 3-4 个子节点(y: 230–340)
虚线框圈出展开区域---
模板选择决策树
需要表达什么关系?
├── 线性步骤/顺序 → flow
│ └── 有分叉? → flow(带分支箭头)
├── 上下分层/依赖 → layer
├── 并排对比/差异 → matrix
├── 中心+辐射 → hub
├── 层级/分类/金字塔 → tree
│ └── 下层更宽? → tree(金字塔变体)
├── 闭环/循环 → cycle
└── 混合关系?
├── 递进+数据对比 → flow+matrix
└── 流程+节点展开 → flow+hub---
场景覆盖分析
已覆盖
| 场景 | 模板 |
|---|---|
| 多步骤工作流 | flow |
| 带分支的流程 | flow(分叉) |
| 系统分层架构 | layer |
| 前后/方案对比 | matrix |
| 核心概念与要素 | hub |
| 组织/分类体系 | tree |
| 金字塔层级 | tree(金字塔变体) |
| 循环/迭代过程 | cycle |
| 递进效果+数据 | flow+matrix |
| 流程+关键展开 | flow+hub |
可变通
| 场景 | 变通方式 | 局限 |
|---|---|---|
| 时间线 | flow(水平+时间标注) | 无刻度标记 |
| 多角色并行 | flow(按角色分行) | 非严格泳道 |
| 韦恩/交叉 | hub(中心为交集) | 无真正重叠圆 |
| 简单数据对比 | matrix(矩形高度代表值) | 非正式图表 |
不在范围内
- 复杂数据可视化(柱状图、折线图、饼图)
- 地图/空间布局
- 交互原型
配图审查清单(Review Checklist)
SVG 配图生成 + 嵌入后的三道审查门禁。多模态模型 / 审查者照此逐章执行。
配套 SKILL.md「第四阶段:审查与验收」。
>
核心原则:语法门禁(xmllint / rsvg / font-family / <style>)是必要不充分条件——它只保证 SVG 合法,不保证图正确美观。下面三道审查(尤其 ③ 目检)才是验收依据。---
① 配图密度审查(整章统计,~5 分钟)
指标(按章节统计):
- 图/节 = SVG 数 ÷ 节数(
## 第N节计数) - 图/万字 = SVG 数 ÷ (字数 / 10000)
判定:
| 情况 | 判定 | 动作 |
|---|---|---|
| 图/节 ≥ 0.7 且 图/万字 ≥ 0.8 | ✅ 适中 | 通过 |
| 任一低于 | ⚠️ 偏少 | 列出缺图小节,按下方「补图定位」补 |
| 纯 walkthrough / 总结节 | ➖ 可省 | 不计偏少 |
补图定位:扫描缺图小节,找「架构 / 流程 / 对比 / 层次 / 循环 / 关系」描述处(SKILL 第一阶段识别法),补占位 [[FIG:N:描述]]。
跨章均衡:相邻章密度比 < 2×;认知 / 入门篇(如 ch01-03)图密度不应远低于方法 / 实战篇。
实测参考(legal-ai-skill-book,2026-06-17):
- 充足(1.3-1.6/万字):ch07/08/11/12/13
- 适中(1.0-1.5):ch01/09/10
- 偏少待补:ch03(1 张/7 节,0.6)、ch05(2/6,0.5)、ch02(3/7,0.9)
---
② 图-正文论点一致性审查(逐图对照正文,每图 2-3 分钟)
每张图,回溯所在小节原文,逐项核对:
- [ ] 节点数一致:图的节点 / 框数 = 正文描述的要素数("四层"画 4 层、"七节点"画 7 个、"六对象"画 6 个)
- [ ] 层级 / 节点名一致:用正文术语,不擅自改写(如"思维内核层"不写成"思维层"、"MCP 工具层"不写成"工具层"——全称层名见各书总纲)
- [ ] 流程方向一致:箭头方向 = 正文叙述顺序(A→B 还是 B→A;自上而下 / 自下而上 / 蛇形)
- [ ] 对比维度一致:matrix 的列 / 行 = 正文对比维度(甲方/乙方、前后、Chatbot/Agent)
- [ ] 信息无损:替换 mermaid / ASCII 图时,原节点、关系、虚线标注、脚注不丢失
- [ ] 图注准确:「图 N-X:标题」概括图内容,不夸大(非"全景"除非真是)、不遗漏核心要素
- [ ] 正文锚点:图嵌入位置紧贴相关论述段,前后文有"如下图 / 这张图"类承接(避免孤图悬空)
发现问题:回 SVG 改节点 / 方向 / 图注,或调整嵌入位置;改后重跑一致性核对。
---
③ 视觉目检(多模态渲染后眼检,每图 1-2 分钟)
前置:SVG 渲染为 PNG。
rsvg-convert -w 720 -h 400 in.svg -o out.png # 快
# 或
node scripts/svg2png.js in.svg out.png 300 # 300 DPI多模态模型逐张查(看渲染 PNG,不看 SVG 源码):
- [ ] 文字不溢出:所有文字落在所属容器 / 框内,不被边缘截断
- [ ] 无重叠:框 / 节点 / 文字互不遮挡,间距 ≥ 24px
- [ ] 箭头正确:箭头起止落在节点边缘,方向正确,marker 不偏移
- [ ] 连线清晰:连线不穿越文字,交叉处可辨
- [ ] 字号可读:节点标签 ≥ 16px、副说明 ≥ 12px、标题 19-20px(16 开 115mm 通栏下 ≥ 8pt)
- [ ] 黑白可辨:去色后仍能区分层级 / 强调(颜色不是唯一区分手段)
- [ ] 配色对比度(v1.5.0 新增):文字色(
#2D3436/#636E72)与所在模块填充色对比 ≥ 4.5:1(WCAG AA,标题/≥18px 大文本 ≥ 3:1);无高饱和荧光色;红绿不并置做区分 - [ ] 透明背景(v1.5.0 新增):SVG 无背景矩形、无画布底色(grep 源码不应出现
<rect width="720" height="400" fill="..."/>当画布底;模块矩形除外);底色由书页提供 - [ ] 内部模块多色(v1.5.0 新增):SVG 内部不同模块 / 分支 / 方向 / 层级用同色组(P1-P8)不同模块色区分,一图 4-6 种柔和色;相邻模块不同色(亮度差 ≥10%、色相可辨);颜色尽量多样但不刺眼
- [ ] 配色规范(v1.5.0 新增):新生成图从 8 组调色板选 1 组、不混色组;颜色只用
fill/stroke属性内联(无<style>块定义色、无<svg>开标签 font-family、无 class/CSS 变量引色、无背景矩形) - [ ] 灰度降级可辨(v1.5.0 新增):
rsvg-convert+convert -colorspace Gray后预览,文字与模块填充灰度差 ≥ 15%、相邻模块灰度差 ≥ 10% - [ ] 视觉重心稳:布局均衡,无头重脚轻或左右失衡
- [ ] 留白合理:不拥挤不空旷,安全边距 40px
多模态目检 prompt 模板(供生产用):
这是一张书籍配图(720×400 SVG 的 PNG 渲染,v1.5.0 透明背景 + 内部模块多色版)。请逐项检查,只报问题:
1. 是否有文字溢出容器 / 被截断?
2. 是否有框、节点、文字重叠(间距应 ≥24px)?
3. 箭头是否落位正确、方向无误?
4. 字号是否可读(节点≥16px)?
5. 黑白打印能否分辨层级(颜色不是唯一区分手段)?
6. 文字与所在模块填充色对比度是否 ≥ 4.5:1(标题大字 ≥ 3:1)?是否有高饱和荧光色?红绿是否并置做区分?
7. 内部模块之间是否多色柔和区分(一图 4-6 色),相邻模块不同色?配色是否来自单一调色板组(P1-P8)、不混色组?
8. SVG 是否透明背景(PNG 渲染下若叠在白底应看不出自带的画布底色矩形)?
9. 整体布局是否美观、留白合理?
若无问题回答"通过";有问题逐条指出(位置 + 现象 + 修改建议)。发现问题:回 SVG 改坐标(文字 x/y、框 w/h、连线端点、marker refX/refY),重新渲染复检,直到目检"通过"。
---
审查流程小结
1. 密度审查(整章统计)→ 偏少则补图 2. 一致性审查(逐图对照正文)→ 不符则改图 / 图注 / 位置 3. 视觉目检(多模态逐图)→ 不美则改坐标 4. 三道全过 → 验收通过;任一未过 → 回改 + 复检
与写作审稿(writing-reviewer)的边界:本清单审配图本身(密度 / 一致性 / 视觉);writing-reviewer 审正文文字(口吻 / 去 AI 化 / 术语)。图注若需审贴切性 / 口吻,可补跑 writing-reviewer。
SVG 书籍配图设计规范
书籍/正式文章 SVG 配图的视觉设计系统。所有生成的 SVG 必须遵循本规范。
---
一、画布
- viewBox:720 × 400(宽高比 1.8:1)
- 背景:透明背景(硬约束)——新生成图不画任何背景矩形、不设
background、不在<svg>根设底色。视觉区分由内部模块填充色承担(详见 §5)。 - 安全边距:40px(上下左右)
- 有效绘图区:640 × 320(x: 40–680, y: 40–360)
关于老图:main 上既有 34 张白底单色 SVG 是历史产物(<rect width="720" height="400" fill="#FFFFFF"/>),保持稳定不回改(作者 2026-06-20 确认)。新规则只管新生成图:透明底 + 内部模块多色柔和区分。透明底与白底是两种不同做法——老图不强制改透明。---
二、印刷物理尺寸推算
SVG viewBox 是虚拟坐标,实际印刷尺寸由排版决定。本节从物理尺寸反推所有参数。
中国常见书籍开本
| 开本 | 成品尺寸 | 版心宽度 | 版心高度 | 通栏配图宽 | 典型用途 |
|---|---|---|---|---|---|
| 16开 | 170×240mm | ~115mm | ~190mm | 115mm | 技术书、教材(本 skill 主目标) |
| 大32开 | 140×203mm | ~95mm | ~160mm | 95mm | 通俗读物、商业书 |
| 32开 | 130×184mm | ~85mm | ~150mm | 85mm | 口袋书 |
| A5 | 148×210mm | ~100mm | ~170mm | 100mm | 国际标准 |
换算公式
物理尺寸(mm) = SVG单位 × (印刷宽度mm / 720)
物理尺寸(pt) = 物理尺寸(mm) / 0.3528以主目标 16开(115mm 通栏)为例:1 SVG单位 ≈ 0.160mm
中文印刷可读性标准
| 等级 | 字号 | 物理尺寸 | 用途 |
|---|---|---|---|
| 正文 | 五号 10.5pt | 3.70mm | 书籍正文 |
| 图注 | 小五号 8pt | 2.82mm | 图注、标签最低线 |
| 辅助 | 7pt | 2.47mm | 仅限纯英文/数字,中文不建议 |
| 危险 | <7pt | <2.47mm | 中文不可辨认 |
关键换算表(16开 115mm 通栏)
1 单位 = 0.160mm
| SVG字号 | 物理尺寸 | pt值 | 可读性 |
|---|---|---|---|
| 24px | 3.84mm | 10.9pt | 舒适,适合图标题 |
| 22px | 3.52mm | 10.0pt | 舒适,适合层标签 |
| 20px | 3.20mm | 9.1pt | 良好 |
| 18px | 2.88mm | 8.2pt | 刚好过 8pt 下限 → 节点标签最低字号 |
| 16px | 2.56mm | 7.3pt | 偏小,仅限英文/数字子标签 |
| 14px | 2.24mm | 6.4pt | 中文不可读 |
| 12px | 1.92mm | 5.4pt | 完全不可读 |
不同开本的标签最低字号
| 开本 | 印刷宽度 | 达到 8pt(2.82mm) 所需 | 达到 9pt(3.17mm) 所需 |
|---|---|---|---|
| 16开 | 115mm | 18px | 20px |
| 大32开 | 95mm | 22px | 24px |
| 32开 | 85mm | 24px | 27px |
结论:16开下 18px 是节点标签的硬底线。大32开需要 22px。
---
三、字体规格(基于 16开 115mm 推算)
| 用途 | SVG字号 | 物理(16开) | pt值 | 字重 | 颜色 |
|---|---|---|---|---|---|
| 图标题 | 22px | 3.52mm | 10pt | 600 | #2D3436 |
| 层标签(layer 模板) | 20px | 3.20mm | 9.1pt | 600 | #2D3436 |
| 节点标签 | 18px | 2.88mm | 8.2pt | 400 | #2D3436 |
| 子标签/第二行 | 16px | 2.56mm | 7.3pt | 400 | #636E72 |
字体族:-apple-system, "PingFang SC", "Microsoft YaHei", "Noto Sans SC", sans-serif
标签长度限制(18px 标签,汉字宽度约为字号的 1.05 倍):
- 18px 标签:最多 8 个汉字(约 151px)或 10 个英文字符
- 16px 子标签:最多 10 个汉字(约 168px)
- 超出时缩写或拆成子标签
中文换行:使用多个 <text> 元素:
<!-- 主标签 + 子标签 -->
<text x="360" y="195" text-anchor="middle" font-size="18" fill="#2D3436">法律评注</text>
<text x="360" y="214" text-anchor="middle" font-size="16" fill="#636E72">知识库最佳载体</text>行间距:两行文字 y 坐标差 18-20px。
---
四、节点尺寸(基于 18px 标签推算)
18px 单行中文标签 + 上下内边距:
节点高度 = 18px(文字) + 10px(上内边距) + 12px(下内边距) = 40px 最低
推荐 44px(留余量给视觉呼吸)
两行标签节点 = 第一行 18px + 间距 18px + 第二行 16px + 上下内边距 = 约 56px| 元素类型 | 宽度 | 高度 | 能放多少字 |
|---|---|---|---|
| 标准节点 | 140–160px | 48px | 8 字单行(18px) |
| 宽节点(layer 内) | 200–240px | 48px | 12 字单行 |
| 紧凑节点(hub 外围) | 120px | 44px | 6 字 |
| 两行节点 | 140–160px | 56px | 8字+8字 |
物理尺寸验证(16开 115mm):
- 标准节点 160×48px → 物理 25.6×7.7mm → 高度 7.7mm 看起来合适
- 紧凑节点 120×44px → 物理 19.2×7.0mm → OK
- 4 个标准节点水平排列:4×140 + 3×26.7 = 640px → 正好占满 640px 有效宽度
节点尺寸下限
任何节点不得小于:
- 宽 100px(物理 16mm,放 5 个 18px 汉字)
- 高 40px(物理 6.4mm)
---
五、颜色系统
5.0 配色总则(透明背景 + 内部模块多色,v1.5.0 起生效)
适用范围:本节适用于 v1.5.0 及之后新生成的图。main 上既有 34 张白底单色 SVG 是历史产物,保持稳定不回改。新生成图采用本节的"透明背景 + 内部模块多色"配色方案。
三条总原则: 1. 透明背景,绝不加底色:SVG 根标签和内容里不画任何背景矩形、不设 `background`、不写画布底色。视觉空间由排版(纸/页面)提供,配图叠上去就是透明底。 2. 颜色用在内部模块之间,尽量多样:所谓"配色"是指 SVG 内部不同模块 / 不同分支 / 不同方向 / 不同层级之间 的多色柔和区分——颜色要尽量丰富(一图 4-6 种甚至更多柔和色),用调色板把内部模块彼此分开,而不是"一图一种主色"。 3. 颜色不是唯一区分手段:仍用形状、位置、线型、边框粗细、填充深浅协同区分元素——黑白打印或 CMYK 偏色下仍可辨(继承自旧规范)。
5.1 基础中性色(文字 / 边框 / 连线)
| 用途 | 色值 | 说明 |
|---|---|---|
| 深灰文字 / 边框 / 连线 | #2D3436 | 主文字色,对任何柔和模块填充色对比 ≥ 7:1 |
| 中灰子标签 | #636E72 | 辅助文字 |
| 浅灰辅助线 | #B2BEC3 | 分隔线、弱连接 |
文字色统一用深色(#2D3436/#636E72)保证在任意柔和模块填充色上都清晰可读。不要把文字写成柔和色(浅蓝/浅绿等)——文字可读性优先。
5.2 预定义柔和模块色调色板(8 组,新生成图选用)
每组 = 一组柔和的「模块色」(5-6 个去饱和、低饱和、纸面友好的色),用于区分 SVG 内部不同模块 / 分支 / 方向 / 层级。一组内所有色都是柔和(去饱和)的同明度档(避免某个模块过亮/过暗抢视觉重心),色相彼此拉开但都压住饱和度。
关键概念转变:上一版"调色板"是「画布浅底 + 强调色」二元结构(已被废弃)。本版调色板是「内部模块多色」结构——一组给 5-6 个模块色,不再有"画布底"这一列。
| 编号 | 色组名 | 模块色(用于区分内部模块,去饱和柔和) | 适用主题 |
|---|---|---|---|
| P1 | 雾蓝系 | #D6E4F0 #C5D9E8 #B8CFE0 #DCE8F2 #C9DCEC #E0EBF4 | 科技、AI、数据、系统架构 |
| P2 | 浅青系 | #C0E8E0 #B5DDD4 #A8D2C9 #CFEDE5 #BCDFD6 #D3EFE9 | 工具、流水线、协作 |
| P3 | 嫩绿系 | #C8EBC8 #BDDFBD #B0D2B0 #D4EED4 #C3E3C3 #D8F0D8 | 成长、流程闭环、正向结果 |
| P4 | 暖米系 | #E8D8C0 #DCCAB0 #D0BEA0 #EDDFC8 #E0D0B5 #F0E2CC | 法律、正式、风控、合规 |
| P5 | 浅紫系 | #DCC8E8 #CFBDDC #C2B2D0 #E2D0EC #D5C3DF #E7D6F0 | 思考、概念、抽象、方法论 |
| P6 | 浅粉系 | #F5D8E0 #E8CBD2 #DCBEC6 #F8DFE5 #EFD2D8 #FAE4EA | 情感、用户视角、体验 |
| P7 | 暖灰系 | #E8DFD0 #DCD3C4 #D0C7B8 #EDE5D7 #E0D8C9 #F0E9DC | 叙事、随笔、文化 |
| P8 | 混合柔和系 | #D6E4F0(蓝) #C8EBC8(绿) #E8D8C0(米) #DCC8E8(紫) #F5D8E0(粉) | 多方向/多分支对比(一图内天然多色,无需另选) |
使用约定:
- 每张图选 1 组(P1-P7)或用 P8 混合系;同图内部模块按"模块 1 取色 1、模块 2 取色 2…"依次分配,让相邻模块不同色。
- 模块色用于内部矩形 / 卡片 / 区块的 `fill`:
<rect fill="{模块色}" stroke="#2D3436" stroke-width="2"/>。 - 文字色始终
#2D3436/#636E72,不写浅色。 - 绝对不画背景矩形:不要
<rect width="720" height="400" fill="..."/>。SVG 直接画模块(透明底由页面/纸面提供)。 - 模块色应覆盖内部所有矩形/区块;连线/箭头/marker 用深灰
#2D3436(或取该模块对应的更深一档色做箭头),不抢戏。
5.3 打印友好约束(CMYK / 纸面可读硬约束)
透明背景下的对比度口径:对比度是 文字色 vs 所在模块的填充色(不是 vs 画布底——没有画布底)。因此模块填充色必须足够浅(去饱和),让深色文字落在上面仍 ≥ 4.5:1。
| 约束 | 阈值/规则 | 验证方法 |
|---|---|---|
| 文字对比度 | 文字色(#2D3436)与所在模块填充色亮度对比 ≥ 4.5:1(WCAG AA 正常文本);标题/节点标签(≥18px)≥ 3:1(AA 大文本) | WebAIM Contrast Checker(输入文字色 vs 模块填充色) |
| 相邻模块区分度 | 相邻模块(同行/同列/直接连接的两个矩形)填充色之间亮度差 ≥ 10% 且色相差可辨;非相邻可同色 | 肉眼:相邻模块一眼能分开 |
| 模块填充明度上限 | 所有模块色明度 L* ≥ 80(约 #E0E0E0 以上偏浅),保证深色文字对比 | 调色板内 8 组已统一标定到柔和档 |
| 禁高饱和荧光 | 禁 #FF0000/#00FF00/#FFFF00/#FF00FF 等纯三原色高饱和;调色板内已统一去饱和 | 不使用调色板外的色 |
| 红绿不并置做区分 | 红绿对比在色盲+黑白下都失效;若必须红绿,靠填充深浅+边框粗细补区分 | 避免纯红与纯绿相邻做唯一区分手段 |
| CMYK 转换不偏色 | 所有色值在 sRGB 空间,转 CMYK 时不出现明显色相漂移(蓝→紫、黄→棕) | 出片前用 Photoshop/Ghostscript 转 CMYK 预览 |
| 黑白降级可辨 | 去色(grayscale)后,相邻模块灰度差 ≥ 10%;文字与模块填充灰度差 ≥ 15% | convert in.svg -colorspace Gray out.png 预览 |
打印友好自检脚本(生成后必跑一次,灰度预览 + 目检):
rsvg-convert -w 720 -h 400 in.svg -o out.png
# 灰度预览(模拟黑白印刷)
convert out.png -colorspace Gray gray.png
# 肉眼看 gray.png:模块彼此可分、文字清晰 = 通过5.4 颜色语法约束(与既有语法门禁兼容,必读)
硬约束:颜色只能通过fill/stroke属性内联到具体元素上。严禁以下写法:
<!-- ❌ 禁止 1:用 <style> 块定义颜色类 -->
<style>
.node-blue { fill: #D6E4F0; stroke: #2C5282; }
</style>
<!-- ❌ 禁止 2:在 <svg> 开标签上写 font-family -->
<svg viewBox="0 0 720 400" font-family="...">
<!-- ❌ 禁止 3:用 CSS 变量 / currentColor / class 引用色 -->
<rect class="node-blue"/>
<!-- ❌ 禁止 4:画背景矩形当画布底(透明背景硬约束) -->
<rect width="720" height="400" fill="#F0F4F8"/>正确写法:每个元素的 fill/stroke 直接写色值;不画任何背景。
<!-- ✅ 正确:透明背景 + 属性内联模块色 -->
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 720 400" width="720" height="400">
<!-- 注意:没有背景矩形!直接画模块 -->
<rect x="40" y="176" width="140" height="48" rx="6" fill="#D6E4F0" stroke="#2D3436" stroke-width="2"/>
<text x="110" y="205" text-anchor="middle" font-size="18" fill="#2D3436">识别场景</text>
<!-- 相邻模块用同组不同色 -->
<rect x="207" y="176" width="140" height="48" rx="6" fill="#C5D9E8" stroke="#2D3436" stroke-width="2"/>
<text x="277" y="205" text-anchor="middle" font-size="18" fill="#2D3436">梳理流程</text>
</svg>为何如此:
- 透明背景:作者 2026-06-20 明确要求——配图叠在排版纸面上,底色由书页提供,不应自带底色。
- 属性内联(不用
<style>/ 不写<svg>font-family):Obsidian/Markdown 内嵌渲染时存在已知不兼容(嵌套引号、样式作用域——见 memoryfeedback_svg_embed_syntax)。颜色同样遵守"属性内联、不用<style>块、不引类名"的硬约束,保证 xmllint well-formed + rsvg 无警告 + Obsidian 渲染三重兼容。
---
六、形状规范
圆角矩形
模块填充色从 §5.2 调色板取值(以下示例用 P1 雾蓝系的两个相邻模块色;边框统一深灰):
<!-- 模块 1 -->
<rect x="40" y="40" width="150" height="48" rx="6" fill="#D6E4F0" stroke="#2D3436" stroke-width="2"/>
<!-- 相邻模块 2,同组不同色 -->
<rect x="207" y="40" width="150" height="48" rx="6" fill="#C5D9E8" stroke="#2D3436" stroke-width="2"/>- 圆角
rx="6" - 边框
2px(普通)、3px(强调根节点/终止节点) - 线宽
2px在 16开下物理 0.32mm,印刷可见 - 相邻模块同色组取不同色,避免色块连成一片
箭头
<marker id="arrow" viewBox="0 0 10 10" refX="10" refY="5"
markerWidth="8" markerHeight="8" orient="auto">
<path d="M 0 0 L 10 5 L 0 10 z" fill="#2D3436"/>
</marker>连线
- 实线:关键关系
- 虚线
stroke-dasharray="6,4":辅助/可选关系
---
七、间距
| 规则 | 数值 | 物理(16开) | 理由 |
|---|---|---|---|
| 元素最小间距 | 24px | 3.8mm | 印刷下不糊在一起 |
| 层间垂直间距 | 40px | 6.4mm | 层次清晰 |
| 同层水平间距 | 28px | 4.5mm | 节点不挨 |
| 节点内边距 | 16px 水平, 12px 垂直 | 2.6mm / 1.9mm | 文字不贴边 |
---
八、印刷黑白兼容
1. 颜色不是唯一区分手段:用形状、位置、线型区分 2. 避免红绿对比(色盲+黑白都不行) 3. 关键连线实线,辅助虚线 4. 节点区分靠填充深浅 + 边框粗细,不仅靠颜色
---
九、元素密度上限
基于 680px 有效宽度和 48px 节点高度:
| 模板 | 最大节点 | 计算 | 最大字数/节点 |
|---|---|---|---|
| flow(水平) | 4 个 | 4×140+3×26.7=640 | 7-8 字 |
| flow(垂直) | 5 个 | 高度足够 | 12 字 |
| layer | 4 层 | 4×56+3×24=296 | 12 字 |
| matrix | 2列×3行 | 行高 60px×3+间距 | 8 字/格 |
| hub | 1+5 外围 | 半径 130px | 6 字 |
| tree | 1→3→5 | 叶子 5×120+4×20=680 | 6 字 |
| cycle | 4 个 | 矩形排列 | 8 字 |
超过上限时:拆成两张图,不要硬塞。
---
十、大32开适配说明
如果目标是大32开(95mm 通栏),1 单位 ≈ 0.132mm,所有尺寸需放大:
| 参数 | 16开值 | 大32开调整 |
|---|---|---|
| 节点标签 | 18px | 22px |
| 子标签 | 16px | 18px |
| 图标题 | 22px | 26px |
| 节点高度 | 48px | 56px |
| 最大 flow 节点 | 4 个 | 3 个 |
| 最大 hub 外围 | 5 个 | 4 个 |
建议大32开书籍直接缩小 viewBox(如 520×290),这样 18px 标签在 95mm 下达到 3.34mm = 9.5pt,舒适可读。
---
十一、图注格式
**图 N-X:图标题**---
十二、SVG 代码模板
注意:模板去掉了<style>块与<svg>属性上的font-family——这是已验证的 Obsidian 渲染硬约束(见 §5.4)。字体由 Markdown 渲染环境继承默认无衬线字体。若个别环境需要强制字体,应在每个<text>元素上单独写font-family属性,绝不在 `<svg>` 开标签或 `<style>` 块统一设置。
模板 A:透明背景 + 内部多色(新生成图默认,示例用 P1 雾蓝系)
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 720 400" width="720" height="400">
<defs>
<marker id="arrow" viewBox="0 0 10 10" refX="10" refY="5"
markerWidth="8" markerHeight="8" orient="auto">
<path d="M 0 0 L 10 5 L 0 10 z" fill="#2D3436"/>
</marker>
</defs>
<!-- 注意:无背景矩形!透明底由书页提供 -->
<!-- 模块 1 -->
<rect x="40" y="176" width="140" height="48" rx="6" fill="#D6E4F0" stroke="#2D3436" stroke-width="2"/>
<text x="110" y="205" text-anchor="middle" font-size="18" fill="#2D3436">识别场景</text>
<!-- 模块 2,同色组相邻模块用不同色 -->
<rect x="207" y="176" width="140" height="48" rx="6" fill="#C5D9E8" stroke="#2D3436" stroke-width="2"/>
<text x="277" y="205" text-anchor="middle" font-size="18" fill="#2D3436">梳理流程</text>
<!-- 模块 3 -->
<rect x="373" y="176" width="140" height="48" rx="6" fill="#B8CFE0" stroke="#2D3436" stroke-width="2"/>
<text x="443" y="205" text-anchor="middle" font-size="18" fill="#2D3436">编写</text>
</svg>模板 B:旧极简白底单色(仅 main 上 34 张历史图兼容,新图不再用)
历史产物保留:既有 34 张图带 <rect width="720" height="400" fill="#FFFFFF"/> 白底,保持稳定不回改。新生成图一律走模板 A(透明底 + 内部多色)。<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 720 400" width="720" height="400">
<defs>
<marker id="arrow" viewBox="0 0 10 10" refX="10" refY="5"
markerWidth="8" markerHeight="8" orient="auto">
<path d="M 0 0 L 10 5 L 0 10 z" fill="#2D3436"/>
</marker>
</defs>
<rect width="720" height="400" fill="#FFFFFF"/>
<!-- 内容 -->
</svg>#!/usr/bin/env python3
"""从 Markdown 文件中提取内联 SVG 并保存为独立文件。
用法:
python extract_svgs.py <markdown_file> [--output <output_dir>]
示例:
python extract_svgs.py manuscript/方法篇/ch06.md --output manuscript/方法篇/figures/
"""
import argparse
import os
import re
import sys
from pathlib import Path
SVG_OPEN = re.compile(r'<svg\b[^>]*>')
SVG_CLOSE = re.compile(r'</svg>')
FIG_CAPTION = re.compile(r'^\*\*图\s+(\d+)-(\d+)[::]\s*(.+?)\*\*')
PLACEHOLDER = re.compile(r'\[\[FIG:(\d+):([^\]]+)\]\]')
def find_svgs(content):
"""找到所有 <svg>...</svg> 块及其起止位置。"""
blocks = []
pos = 0
while True:
m_open = SVG_OPEN.search(content, pos)
if not m_open:
break
m_close = SVG_CLOSE.search(content, m_open.end())
if not m_close:
break
end = m_close.end()
blocks.append((m_open.start(), end, content[m_open.start():end]))
pos = end
return blocks
def find_caption(content, svg_end):
"""找到 SVG 块紧随的图注行,返回 (章节号, 序号, 标题) 或 None。"""
after = content[svg_end:].lstrip('\n')
first_line = after.split('\n', 1)[0].strip()
m = FIG_CAPTION.match(first_line)
if m:
return m.group(1), m.group(2), m.group(3)
return None
def slugify(text, max_len=40):
text = re.sub(r'[\\/:*?"<>|#\s]+', '-', text.strip())
text = re.sub(r'-{2,}', '-', text)
text = text.strip('-')
return text[:max_len]
def extract(source_path, output_dir, dry_run=False):
content = Path(source_path).read_text(encoding='utf-8')
blocks = find_svgs(content)
if not blocks:
print('未找到 SVG 块。')
return []
os.makedirs(output_dir, exist_ok=True)
results = []
for idx, (start, end, svg_code) in enumerate(blocks, 1):
cap = find_caption(content, end)
if cap:
ch, num, title = cap
filename = f'ch{ch}-fig{num}-{slugify(title)}.svg'
else:
stem = Path(source_path).stem
filename = f'{stem}-fig{idx:02d}.svg'
out_path = os.path.join(output_dir, filename)
if dry_run:
print(f' [{idx}] → {out_path} ({len(svg_code)} chars)')
else:
with open(out_path, 'w', encoding='utf-8') as f:
f.write(svg_code)
f.write('\n')
print(f' ✓ {out_path}')
results.append(out_path)
return results
def main():
parser = argparse.ArgumentParser(description='从 Markdown 提取 SVG 到独立文件')
parser.add_argument('source', help='Markdown 文件路径')
parser.add_argument('--output', '-o', default='.', help='输出目录(默认当前目录)')
parser.add_argument('--dry-run', action='store_true', help='只显示将提取的文件,不实际写入')
args = parser.parse_args()
if not os.path.isfile(args.source):
print(f'文件不存在: {args.source}', file=sys.stderr)
sys.exit(1)
results = extract(args.source, args.output, args.dry_run)
print(f'\n共提取 {len(results)} 个 SVG 文件到 {args.output}/')
if __name__ == '__main__':
main()
/**
* svg2png.js
* SVG → PNG 高分辨率转换,适用于书籍印刷。
*
* 由 svg-article-illustrator/scripts/svg2png.js 简化而来:
* - 去掉"强制输出到 SVG 源目录"限制,允许指定任意输出路径
* - 默认 DPI 改为 600(印刷推荐)
*
* 依赖:npm install puppeteer(需要 Chrome/Chromium)
*
* 用法:
* node svg2png.js input.svg # 同目录输出,600 DPI
* node svg2png.js input.svg output.png 300 # 指定输出和 DPI
* node svg2png.js input/ output/ # 批量转换目录
*/
import fs from "fs";
import path from "path";
const DEFAULT_DPI = 600;
const MIN_DPI = 72;
const MAX_DPI = 2400;
async function loadPuppeteer() {
try {
const module = await import("puppeteer");
return module.default;
} catch {
throw new Error(
"缺少依赖: puppeteer\n请先运行: npm install puppeteer"
);
}
}
function findChrome() {
const paths = [
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
"/Applications/Chromium.app/Contents/MacOS/Chromium",
"/usr/bin/google-chrome-stable",
"/usr/bin/chromium-browser",
"/usr/bin/google-chrome",
];
for (const p of paths) {
if (fs.existsSync(p)) return p;
}
return undefined;
}
async function svgToPng(inputPath, outputPath, dpi = DEFAULT_DPI) {
if (!fs.existsSync(inputPath)) {
throw new Error(`文件不存在: ${inputPath}`);
}
const puppeteer = await loadPuppeteer();
const svgContent = fs.readFileSync(inputPath, "utf8");
const html = `<!doctype html>
<html>
<head>
<meta charset="utf-8">
<style>
html, body { margin: 0; padding: 0; background: transparent; }
svg { display: block; }
</style>
</head>
<body>${svgContent}</body>
</html>`;
let browser;
try {
browser = await puppeteer.launch({
headless: "new",
args: [
"--no-sandbox",
"--disable-setuid-sandbox",
"--disable-dev-shm-usage",
"--disable-gpu",
"--no-first-run",
"--no-zygote",
],
executablePath: findChrome(),
});
const page = await browser.newPage();
await page.setContent(html, { waitUntil: "domcontentloaded", timeout: 10000 });
await page.waitForSelector("svg", { timeout: 10000 });
const dimensions = await page.evaluate(() => {
const svg = document.querySelector("svg");
if (!svg) throw new Error("未找到 <svg> 元素");
const vb = svg.viewBox.baseVal;
const w = svg.getAttribute("width");
const h = svg.getAttribute("height");
return {
width: w ? parseFloat(w) : vb.width || 720,
height: h ? parseFloat(h) : vb.height || 400,
};
});
const scale = dpi / 96;
await page.setViewport({
width: Math.round(dimensions.width),
height: Math.round(dimensions.height),
deviceScaleFactor: scale,
});
const element = await page.$("svg");
if (!element) throw new Error("SVG 元素未加载");
fs.mkdirSync(path.dirname(outputPath), { recursive: true });
await element.screenshot({ path: outputPath, omitBackground: true });
const pxW = Math.round(dimensions.width * scale);
const pxH = Math.round(dimensions.height * scale);
console.log(` ${dpi}DPI ${pxW}×${pxH}px → ${outputPath}`);
} finally {
if (browser) {
await browser.close().catch(() => {});
}
}
}
async function convertFile(input, output, dpi) {
if (output) {
const outDir = path.dirname(output);
if (outDir) fs.mkdirSync(outDir, { recursive: true });
} else {
output = input.replace(/\.svg$/i, ".png");
}
await svgToPng(input, output, dpi);
}
async function convertDir(inputDir, outputDir, dpi) {
const files = fs.readdirSync(inputDir).filter((f) => f.endsWith(".svg"));
if (files.length === 0) {
console.log(`目录中无 SVG 文件: ${inputDir}`);
return;
}
fs.mkdirSync(outputDir, { recursive: true });
console.log(`批量转换 ${files.length} 个文件 (${dpi} DPI)...`);
for (const f of files) {
await convertFile(
path.join(inputDir, f),
path.join(outputDir, f.replace(/\.svg$/i, ".png")),
dpi
);
}
console.log(`完成: ${files.length} 张 PNG → ${outputDir}`);
}
// CLI
const [,, arg1, arg2, arg3] = process.argv;
if (!arg1) {
console.log("SVG → PNG 转换(书籍印刷用)");
console.log("");
console.log("用法:");
console.log(" node svg2png.js <input.svg> [output.png] [dpi]");
console.log(" node svg2png.js <input-dir> <output-dir> [dpi]");
console.log("");
console.log("示例:");
console.log(" node svg2png.js fig1.svg # 同目录,600 DPI");
console.log(" node svg2png.js fig1.svg fig1.png 300 # 300 DPI");
console.log(" node svg2png.js figures/ output/ 600 # 批量转换");
console.log("");
console.log(`DPI 范围: ${MIN_DPI}–${MAX_DPI},默认 ${DEFAULT_DPI}`);
process.exit(0);
}
if (!fs.existsSync(arg1)) {
console.error(`路径不存在: ${arg1}`);
process.exit(1);
}
const dpi = parseInt(arg3 || arg2) || DEFAULT_DPI;
if (dpi < MIN_DPI || dpi > MAX_DPI) {
console.error(`DPI 应在 ${MIN_DPI}–${MAX_DPI} 之间`);
process.exit(1);
}
const stat = fs.statSync(arg1);
if (stat.isDirectory()) {
const outDir = arg2 || path.join(arg1, "png");
convertDir(arg1, outDir, dpi).catch((err) => {
console.error("批量转换失败:", err.message);
process.exit(1);
});
} else {
const output = arg2 && !arg2.match(/^\d+$/) ? arg2 : undefined;
convertFile(arg1, output, dpi).catch((err) => {
console.error("转换失败:", err.message);
process.exit(1);
});
}