1实现原理 · 为什么它能做到
管线是 pandoc(md→HTML) + CSS 主题 + weasyprint/headless-Chrome 渲染 + poppler 自检,脚本自动补 CJK 字体栈与排版补丁——核心在 md_to_pdf.py(813 行)。
result = subprocess.run( ["pandoc", "-f", "markdown", "-t", "html"], input=md_content, capture_output=True,
后端选择按『内容×主题字体栈』自动路由,不是按是否中文:default/cjk-auto(Songti/Heiti CID TrueType)走 weasyprint,PingFang 类主题走 Chrome,因为 weasyprint 会把 PingFang SC 子集嵌入成 CID Type 0C,macOS Preview/Adobe 读不了。
**CJK + a Songti/Heiti theme** (`default`, `cjk-auto`) → **weasyprint**. These themes embed CID TrueType, which every reader renders, so Chrome buys nothing and costs the clip described below.
主题=独立 CSS 文件(themes/*.css),5 个内置主题覆盖正式/培训/手机阅读;新主题=复制 default.css 改名。
To create a new theme: copy `themes/default.css`, modify, save as `themes/your-theme.css`.
CJK 排版双层防御:Layer 1 自动注入 CSS 补丁(table-layout: fixed、keep-all、th nowrap 等,不改用户源文件);Layer 2 渲染后用 pdftotext -layout 按『中文文案排版指北』扫孤儿字符/破括号等反模式并警告。
The patch: - `table { table-layout: fixed; width: 100% }` — equal column widths prevent weasyprint auto-layout from squeezing one column to ~10% width when an adjacent column has 5x more content
Chrome 裁剪缺陷被研究到像素级并配双形态检查:Chrome 的 @page clip path 会切掉越过内容框的表格右边框(538.90pt vs 545.18pt),且对象仍在 PDF 里、光看预览/坐标都发现不了——check_table_borders.py 提供 ink 检查 + --reference 对照两种形态,只有后者对 Chrome 渲染是有效裁决。
It takes two forms, and for a Chrome-rendered PDF only the second one is a verdict.
视觉自检强制化:每次生成自动 pdftoppm 出每页 PNG(放系统临时目录,绝不落工作树),打印 checklist 提醒逐页 Read;表格文件须跑 border check 才放行。
Converts each page to PNG via `pdftoppm` (poppler-utils) into a `<pdf-name>/` subdirectory under the **system temp dir** (NOT next to the PDF — previews are a throwaway self-check artifact and must never linger in your working tree / git repo).
2核心能力
3外部依赖
| 类型 | 依赖 |
|---|---|
| cli | pandoc(markdown→HTML) |
| package | weasyprint(uv run --with weasyprint / pip install) |
| cli | Google Chrome(headless --print-to-pdf 后端) |
| cli | poppler:pdftoppm(预览 PNG)/ pdftotext / pdfinfo(排版 lint) |
| package | pdfplumber / pillow / numpy(check_table_borders,uv --with) |
4风险提醒 风险提醒:蓝色 · 知晓即可
- Chrome 裁剪是结构性缺陷,边界案例仍可能漏 — 双形态检查只对『本 skill 产出的 PDF』校准;外部工具生成的 PDF 上 pdfplumber 可能把装饰误组装成表格产生假阳性(SKILL 自述),此时失败应视为『去人眼看』而非裁决。
- 字体/系统依赖面宽 — 要 Songti/PingFang 字体 + pandoc + weasyprint 或 Chrome + poppler;缺字体时中文出方框,缺 weasyprint 时自动降级 Chrome(带警告)。
- 验证义务重 — 逐页 Read + 表格 border check 是硬要求;--no-preview 批量场景要求另行跑 check,流程上易被跳过(SKILL 已反复强调)。
- 预置网络 — 首次使用需 PyPI 拉包;无网/受限环境需预先安装 weasyprint/pdfplumber。
5第二遍独立确认
- [ok] pandoc→HTML→渲染器管线 — md_to_pdf.py subprocess.run(["pandoc","-f","markdown","-t","html"]) + _render_weasyprint/_render_chrome 均存在。
- [ok] 后端按内容×主题路由 — _detect_backend 中 _WEASYPRINT_SAFE_CJK_THEMES={'default','cjk-auto'} 与 SKILL 路由表一致;test_backend_routing.py 存在。
- [ok] Chrome clip 缺陷数字 — SKILL 与 check_table_borders.py docstring 均载 538.90pt/545.18pt 与 '5/5 promised rules painted — PASS' 反例,跨文件一致。
- [ok] 预览进 temp dir 而非工作树 — 代码 preview_dir = Path(tempfile.gettempdir()) / 'pdf-creator-previews' / pdf_path.stem;SKILL 同述。
- [ok] Layer1 CSS 补丁 / Layer2 lint 不隐改源 — _TYPOGRAPHY_CSS_PATCH 注入与 pdftotext -layout 扫描代码均在;无对用户 md/css 的写回路径。
- [ok] 无网络无凭证 — 代码扫描零 curl/requests/API key;env 读取仅 DYLD_LIBRARY_PATH(进程内)。
- [ok] 元数据 — GitHub API:MIT / 1385 stars / pushed 2026-09-09T12:33:29Z;本地 HEAD==pin d5c4678。
6结论
77a0f5b23369e727…d5c4678cb5