swiss-pdf は何をしますか?
将 md / docx / txt / html 一键转换为瑞士国际排版风格的 A4 PDF。
白底黑字、极细分割线、大号章节数字、等宽数字、唯一彩色红 #E5484D。
设计令牌源自一套瑞士国际主义版式的精装手册(大号章节数字、极细分割线、单一强调色), 改为单流自动分页,因此任意长度的文档都能直接处理。
使用方法
方式一:拖拽(推荐)
将文件拖放到 swiss-pdf.bat 上 → 同一目录生成同名 .pdf,同时保留一份 xxx.pdf-src.html(可供复查的 HTML 源)。
方式二:命令行
python make.py 输入文件 # 输出 PDF
python make.py 输入文件 --html # 仅输出 HTML(保存为 xxx.html,浏览器打开可手动打印)
python make.py 输入文件 -o 输出.pdf
python make.py a.md b.docx c.txt # 批量处理
python make.py 输入文件 --no-cover --no-toc
python make.py 输入文件 --title "标题" --kick "眉注" --date 2026.11.09
支持的输入格式
| 格式 | 转换方式 | 说明 |
|---|---|---|
.md | pandoc | 支持 YAML front-matter、GFM 表格、列表、代码块、图片 |
.docx | pandoc | 标题样式 → 章节标题;表格 → 样式表;图片自动提取 |
.txt | 内置解析 | 空行分段;短行带编号(一、/ 1. / 第X章)→ 章节标题 |
.html | 内置解析 | 提取 <body>,移除 script/style;<title> 作为文档标题 |
封面:YAML front-matter
写在 md 文件最顶部即可(其他格式使用 --title 等参数):
---
title: 城市骑行安全白皮书
subtitle: 基于三城抽样调查的观察与建议
kick: 交通治理研究
en: Urban Cycling Safety White Paper
slug: 城市骑行安全
nature: 公开版
project: 城市慢行系统研究计划
party_a: 委托方名称
party_b: 受托方名称
stage: 调研 → 撰写 → 定稿
source: 01_观测记录 02_访谈纪要 03_方案
date: 2026.11.10
version: v2.1
badges: 公开; !重点; 数据支撑
---
badges使用;分隔,前缀!的为黑底反白。- 未填写
date时自动使用当天日期。 - 不加 front-matter 也能运行,封面仅显示标题 + 日期 + 版本占位。
正文:六种风格块
在 markdown 中使用 ::: 包裹(块内每行 = 一个条目):
::: note
补充说明、口径提醒。灰底。
:::
::: quote
关键结论引文,左侧 3pt 竖线。
:::
::: inverse
黑底反白块,放置最重要的一句话。
:::
::: warn
红框警示,唯一允许使用彩色的位置。
:::
::: stat
4,280 | 观测样本总数 || 24 | 观测点位 || 3 | 覆盖城市
:::
::: check
早高峰通勤路口(需 7:30 前到位)
夜间照明路段(需补光灯)
:::
::: kv
页数 | A 方案 20 页 / C 方案 32 页
推荐 | 方案 C,但需补足第二城数据
:::
stat 使用 数字 | 说明 组成一格,|| 分隔。kv 使用 键 | 值。
自动行为
| 行为 | 规则 |
|---|---|
| 章节编号 | # 一级标题 → 大号数字 00/01/02…;标题已包含「01 」「一、」编号时沿用 |
| 目录 | 章节 + ## 小节合计 ≥ 5 条时才生成,否则省略该页;章节标题带锚点,PDF 内可点击跳转 |
| 表格 | 列数 ≥ 6 自动缩小字号(table.wide);纯数字单元格右对齐 + 等宽字体 |
| 页眉页脚 | 封面页不排版;正文页眉左侧运行标题、页脚右侧「第 i 页 / 共 N 页」 |
| 打印 | @page A4,边距 14/15/13mm;标题不与正文分离,表格行不跨页断开 |
HTML 源的额外处理(实测补充)
当源码是「自带设计的网页」时,本 skill 会自动进行这些归一化,避免重排后出现游离编号或重复标题:
| 源码结构 | 处理 |
|---|---|
<div class="sec-head"><span class="sec-num">3</span><h2>题</h2></div> | 转换为模板的 .sec 章节头部(编号取自 span,标题取自 h2) |
<h1>03 题</h1> / <h1>题</h1> | 同上;无编号则按出现顺序自动递增 |
单独成段的纯数字 <p>0</p> | 丢弃(常见于将章节号作为装饰数字的写法) |
<header class="masthead"> / <nav> | 丢弃(封面已另起,重复的 kicker/大标题/落地目录会污染正文) |
<section> 分节 | 保留为分节容器,标题仍需 sec-head 或 h1 才能进入目录 |
<div id="xxx" class="chart"> + echarts | 使用无头浏览器截图为 PNG 后嵌入;截图前等待 networkidle + 2.5s |
图表截图前提:echarts 通过 CDN 加载时需要能联网;离线环境请在源码中内联 echarts.min.js。
字体与标点
字体:仓库内置思源黑体,@font-face 内联
fonts/ 目录随仓库附带两个文件,make.py 在生成 HTML 时将它们的 base64 内联进 <style>,
注册为字体族 Swiss Sans SC:
| 文件 | 字重 |
|---|---|
SourceHanSansCN-Regular.subset.ttf | 400 |
SourceHanSansCN-Bold.subset.ttf | 700 |
子集范围约 7100 字符(ASCII + 中英标点符号 + GB2312 全部 6763 汉字),字符集详见 fonts/charset.txt。
优势:在未安装思源字体的机器上,输出完全一致,无需用户安装字体。
用户可自行更换或下载字体,三种方法(详见 fonts/README.md):
- 替换文件(推荐):将自己的 TTF 改名为上述两个文件名并覆盖。先进行子集化,否则 HTML 会带上完整的 10 MB:
pyftsubset 你的字体-Regular.ttf --text-file=fonts/charset.txt --output-file=fonts/SourceHanSansCN-Regular.subset.ttf - 删除
fonts/目录:make.py检测不到文件时会跳过@font-face,回退到 CSS 中的系统字体栈 (思源黑体 SC/CN → Noto Sans SC → Noto Sans CJK SC → 梦源黑体 CN → PingFang SC → Segoe UI Symbol)。 - 仅修改 CSS:修改
templates/swiss.css顶部的--sans第一个字体名。
⚠️ 必须使用 .ttf(TrueType 轮廓),不能使用 .otf(CFF)。 Chromium 在打印 PDF 时会将 CFF 的
Web 字体降级为 Type3 字形指令(每个字形存储一段绘图路径),导致体积膨胀数倍。同一份 4 页文档:
OTF → 328 KB / 205 个 Type3;TTF → 79 KB / 1 个 Type3。
微软雅黑在 Chromium 下缺少 ·(U+00B7)、→ ↓ ≤ ≥ 等符号字形,会呈现为方框,因此已从主字体栈移除。
引号:全文强制使用国标弯引号
convert.normalize_quotes() 在转换的最后一步统一处理,覆盖标题、封面、页眉、目录、表格、风格块:
| 输入 | 输出 |
|---|---|
「」 | “” |
『』 | ‘’ |
代码中的直引号 " ' | markdown 通过 pandoc +smart 自动转换为弯引号;代码块 <code> / <pre> 内保持不变 |
实现仅替换 4 个码位(U+300C/D/E/F → U+201C/D/8/9),不影响 HTML 标签属性与 CSS/JS。
已验证的实跑记录
| 输入 | 规模 | 页数 | 耗时 |
|---|---|---|---|
| md | 29 KB | 15 | ~6s |
| html | 105 KB + 2 个 echarts 图 | 21 | ~15s |
| docx | 标题样式 + 表格 | 2 | ~6s |
txt(samples/) | 1.2 KB | 2 | ~5s |
| md 长文档回归 | 71 KB | 21 | ~24s |
环境依赖
| 依赖 | 本机状态 | 缺失时 |
|---|---|---|
| pandoc(≥2.19) | 运行 pandoc -v 确认 | md/docx 无法转换 |
| playwright + chromium | 或系统自带 Chrome / Edge | 无法渲染 PDF |
| python-docx(可选) | 仅辅助解析 | 不影响主流程 |
指定浏览器:set SWISS_PDF_CHROME=<chrome.exe 的路径>
文件结构
swiss-pdf/
├── SKILL.md 本文件
├── swiss-pdf.bat 拖拽入口
├── make.py 主流程:转换 → 组装 → 渲染
├── convert.py 各格式 → HTML 片段 + 块级语法
├── templates/
│ └── swiss.css 全部样式(设计令牌 + 打印规则)
└── samples/ 三种格式的对照样例
边界(无法实现的功能)
- 精装册式的手工分页(每页内容精心摆放、跨页对开设计)模板无法实现,仍需手动编写 HTML。 本 skill 的定位是「常规文档快速成稿」。
- 插图排版依赖原文档的图片尺寸,不做裁切与出血。
- 目录右侧印刷页码尚未实现(页内元素无法获取页码);章节标题可点击跳转,页脚有「第 i 页 / 共 N 页」。
- 过短的表格块可能被整块推到下一页(为避免键值表被拆分,
.kv块设置为不可跨页)。 - 复杂 Word 样式(文本框、艺术字、多级编号列表)在 pandoc 转换时会降级。