Communitygithub.com

swiss-pdf

将 md / docx / txt / html 文档一键转换为瑞士国际排版风格的 A4 PDF,具备白底黑字、极细分割线、大号章节数字等特征。

swiss-pdf とは?

swiss-pdf 是一个 AI 代理排版工具,专门用于将 Markdown、Word、纯文本文档和 HTML 文件转换为带有瑞士国际主义风格的 A4 PDF。它通过 Python 脚本实现完整的转换流水线:首先使用 pandoc 或内置解析器将输入文件转为 HTML 片段,然后应用一套包含思源黑体子集字体、极细分割线、大号章节编号、等宽数字和唯一强调色(红 #E5484D)的 CSS 设计系统,最后利用 Playwright 驱动的 Chromium 浏览器渲染 PDF。该工具支持丰富的排版特性,包括 YAML front-matter 驱动的自动封面、六种设计区块(note/quote/inverse/warn/stat/check/kv)、智能目录生成、页眉页脚页码、以及表格和代码块的自动格式化。特别地,它会自动处理中文字体子集化(约 7100 字符)并内联为 base64,确保跨设备一致性;还能智能处理 echarts 图表截图和 HTML 源中的重复标题/游离编号等复杂情况。swiss-pdf 适用于需要快速生成规范、美观、可打印的 PDF 文档的场景,如报告、白皮书、技术文档等。

対応✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/echoBai83/swiss-pdf/tree/HEAD

お気に入りのAIに質問する

このエージェントスキルを事前に読み込んだ状態で新しいチャットを開きます。

ドキュメント

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

支持的输入格式

格式转换方式说明
.mdpandoc支持 YAML front-matter、GFM 表格、列表、代码块、图片
.docxpandoc标题样式 → 章节标题;表格 → 样式表;图片自动提取
.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.ttf400
SourceHanSansCN-Bold.subset.ttf700

子集范围约 7100 字符(ASCII + 中英标点符号 + GB2312 全部 6763 汉字),字符集详见 fonts/charset.txt。 优势:在未安装思源字体的机器上,输出完全一致,无需用户安装字体。

用户可自行更换或下载字体,三种方法(详见 fonts/README.md):

  1. 替换文件(推荐):将自己的 TTF 改名为上述两个文件名并覆盖。先进行子集化,否则 HTML 会带上完整的 10 MB:
    pyftsubset 你的字体-Regular.ttf --text-file=fonts/charset.txt --output-file=fonts/SourceHanSansCN-Regular.subset.ttf
    
  2. 删除 fonts/ 目录:make.py 检测不到文件时会跳过 @font-face,回退到 CSS 中的系统字体栈 (思源黑体 SC/CN → Noto Sans SC → Noto Sans CJK SC → 梦源黑体 CN → PingFang SC → Segoe UI Symbol)。
  3. 仅修改 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。

已验证的实跑记录

输入规模页数耗时
md29 KB15~6s
html105 KB + 2 个 echarts 图21~15s
docx标题样式 + 表格2~6s
txt(samples/)1.2 KB2~5s
md 长文档回归71 KB21~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 转换时会降级。

関連スキル