Skip to content

Commit a44685e

Browse files
Merge pull request #5 from Awesome-Embedded-Learning-Studio/feat/pdf-export
feat(pdf): add pandoc + WeasyPrint whole-site tutorial PDF export
2 parents 73f6e58 + 1881605 commit a44685e

5 files changed

Lines changed: 434 additions & 1 deletion

File tree

.github/workflows/pdf-export.yml

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
name: Export site PDF
2+
3+
# 把整站教程 markdown 渲染成单本 PDF(纯 Python:pandoc + WeasyPrint)。
4+
# 手动触发,与发版 Release / Pages 部署完全解耦(不挂 push/tag/release)。
5+
# 产物:CI artifact(14 天质检)+ 独立轻量 PDF Release asset。
6+
# 读者稳定下载链接(产物名固定):https://github.com/Awesome-Embedded-Learning-Studio/rk-forge/releases/latest/download/rk-forge.pdf
7+
on:
8+
workflow_dispatch:
9+
inputs:
10+
ref:
11+
description: '按分支/tag 构建(留空用默认分支)'
12+
required: false
13+
default: ''
14+
15+
permissions:
16+
contents: write # gh release create 需要
17+
18+
concurrency:
19+
group: pdf-export
20+
cancel-in-progress: false
21+
22+
jobs:
23+
pdf:
24+
runs-on: ubuntu-latest
25+
timeout-minutes: 30
26+
steps:
27+
- name: Checkout
28+
uses: actions/checkout@v4
29+
with:
30+
ref: ${{ inputs.ref }}
31+
fetch-depth: 0
32+
33+
- name: Install system deps (WeasyPrint libs + Chinese fonts)
34+
run: |
35+
sudo apt-get update
36+
sudo apt-get install -y \
37+
libpango-1.0-0 libpangoft2-1.0-0 libharfbuzz0b \
38+
libcairo2 libgdk-pixbuf-2.0-0 \
39+
fonts-noto-cjk
40+
41+
- name: Setup uv
42+
uses: astral-sh/setup-uv@v3
43+
44+
- name: Build PDF
45+
run: ./scripts/build-pdf.sh
46+
47+
- name: Upload PDF artifact (14-day QA)
48+
uses: actions/upload-artifact@v4
49+
with:
50+
name: rk-forge-pdf
51+
path: dist-pdf/*.pdf
52+
retention-days: 14
53+
if-no-files-found: error
54+
55+
# 与发版语义无关的独立轻量 release;asset 名固定,供 /releases/latest/download/ 稳定指向。
56+
- name: Publish as a lightweight PDF release (decoupled from version releases)
57+
run: |
58+
TAG="pdf/$(date -u +%Y-%m-%d-%H%M)"
59+
gh release create "$TAG" dist-pdf/rk-forge.pdf \
60+
--title "整站教程 PDF @ $(date -u +%F)" \
61+
--notes "手动触发(pandoc + WeasyPrint)生成的整站教程 PDF,与发版 Release 解耦。" \
62+
--target ${{ inputs.ref || github.ref }}
63+
env:
64+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

.gitignore

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,4 +82,11 @@ node_modules/
8282
site/.vitepress/dist/
8383
site/.vitepress/cache/
8484
site/.vitepress/.temp/
85-
*.tgz
85+
*.tgz
86+
87+
# --- 整站 PDF 导出产物(pandoc + WeasyPrint 输出;走 Release asset 分发,不提交进仓库) ---
88+
dist-pdf/
89+
90+
# --- Python ---
91+
__pycache__/
92+
*.pyc

scripts/book.css

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
/* rk-forge 教程 PDF 书版样式(WeasyPrint / CSS Paged Media) */
2+
3+
/* ── 页面: A4、页边距、底部页码(首页/封面不显示)────────────────── */
4+
@page {
5+
size: A4;
6+
margin: 22mm 18mm 18mm 18mm;
7+
@bottom-center {
8+
content: counter(page);
9+
font-family: "Noto Sans CJK SC", sans-serif;
10+
font-size: 9pt;
11+
color: #9aa0a6;
12+
}
13+
}
14+
@page :first {
15+
@bottom-center { content: ''; }
16+
}
17+
18+
/* ── 封面(第一页,满版居中,无页码)────────────────────────────── */
19+
.cover {
20+
break-after: page;
21+
display: flex;
22+
flex-direction: column;
23+
justify-content: center;
24+
align-items: center;
25+
min-height: 245mm;
26+
text-align: center;
27+
padding: 0 12mm;
28+
}
29+
.cover-eyebrow {
30+
font-family: "Noto Sans CJK SC", sans-serif;
31+
font-size: 11pt;
32+
letter-spacing: 0.25em;
33+
color: #2c7be5;
34+
margin-bottom: 1.6em;
35+
}
36+
.cover-title {
37+
font-family: "Noto Sans CJK SC", sans-serif;
38+
font-size: 34pt;
39+
font-weight: 700;
40+
color: #1f2d3d;
41+
line-height: 1.25;
42+
margin: 0 0 0.45em;
43+
padding: 0;
44+
border: none;
45+
}
46+
.cover-sub {
47+
font-family: "Noto Serif CJK SC", serif;
48+
font-size: 13pt;
49+
color: #5a6675;
50+
max-width: 130mm;
51+
line-height: 1.7;
52+
}
53+
.cover-rule {
54+
width: 60mm;
55+
height: 3px;
56+
background: #2c7be5;
57+
border-radius: 2px;
58+
margin: 2.2em 0;
59+
}
60+
.cover-meta {
61+
font-family: "Noto Sans CJK SC", sans-serif;
62+
font-size: 10.5pt;
63+
color: #8a94a3;
64+
line-height: 1.9;
65+
}
66+
.cover-date { font-size: 9.5pt; }
67+
68+
/* ── 正文 ──────────────────────────────────────────────────────── */
69+
html {
70+
font-family: "Noto Serif CJK SC", "Noto Sans CJK SC", serif;
71+
font-size: 11pt;
72+
line-height: 1.75;
73+
color: #24292f;
74+
}
75+
body { margin: 0; }
76+
77+
/* ── 标题 ──────────────────────────────────────────────────────── */
78+
h1, h2, h3, h4 {
79+
font-family: "Noto Sans CJK SC", sans-serif;
80+
break-after: avoid;
81+
line-height: 1.35;
82+
}
83+
h1 {
84+
font-size: 20pt;
85+
margin: 0 0 0.4em;
86+
padding-bottom: 0.2em;
87+
border-bottom: 2px solid #d0d7de;
88+
}
89+
h2 {
90+
font-size: 15pt;
91+
margin: 1.4em 0 0.5em;
92+
padding-left: 0.4em;
93+
border-left: 5px solid #2c7be5;
94+
}
95+
h3 { font-size: 12.5pt; margin: 1.1em 0 0.4em; }
96+
h4 { font-size: 11pt; margin: 1em 0 0.3em; }
97+
98+
p { margin: 0.6em 0; }
99+
100+
/* ── 代码 ──────────────────────────────────────────────────────── */
101+
code, pre, pre code {
102+
font-family: "Liberation Mono", "DejaVu Sans Mono", "Noto Sans Mono CJK SC", monospace;
103+
}
104+
pre {
105+
background: #f6f8fa;
106+
border: 1px solid #e6e8eb;
107+
border-radius: 5px;
108+
padding: 9px 12px;
109+
font-size: 8.8pt;
110+
line-height: 1.5;
111+
white-space: pre-wrap;
112+
overflow-wrap: anywhere;
113+
break-inside: avoid;
114+
}
115+
code { font-size: 9.5pt; }
116+
:not(pre) > code {
117+
background: #eef0f2;
118+
padding: 1px 5px;
119+
border-radius: 3px;
120+
}
121+
122+
/* ── 表格 ──────────────────────────────────────────────────────── */
123+
table {
124+
border-collapse: collapse;
125+
width: 100%;
126+
font-size: 9.8pt;
127+
break-inside: avoid;
128+
margin: 1em 0;
129+
}
130+
th, td {
131+
border: 1px solid #d0d7de;
132+
padding: 5px 9px;
133+
text-align: left;
134+
vertical-align: top;
135+
}
136+
th { background: #f3f5f7; }
137+
138+
/* ── 引用 / 提示容器(::: tip 等 → pandoc fenced_divs → div.tip)── */
139+
blockquote {
140+
margin: 1em 0;
141+
padding: 0.5em 1em;
142+
border-left: 3px solid #4a90d9;
143+
background: #f6f9fd;
144+
color: #444;
145+
}
146+
div.tip, div.warning, div.info, div.danger {
147+
margin: 1em 0;
148+
padding: 0.6em 1em;
149+
border-radius: 5px;
150+
break-inside: avoid;
151+
}
152+
div.tip { background: #eef7ff; border-left: 4px solid #2c7be5; }
153+
div.warning { background: #fff8e1; border-left: 4px solid #f0ad4e; }
154+
div.danger { background: #fdecea; border-left: 4px solid #d64545; }
155+
156+
/* ── 图片 ──────────────────────────────────────────────────────── */
157+
img { max-width: 100%; break-inside: avoid; }
158+
159+
/* ── 链接 ──────────────────────────────────────────────────────── */
160+
a { color: #1a6fb0; text-decoration: none; }
161+
162+
/* ── 章节分页(每篇/每卷另起一页)──────────────────────────────── */
163+
.chapter-break, .part-break { break-before: page; }
164+
165+
/* ── 目录(pandoc 生成 nav#TOC;WeasyPrint 用 target-counter 补页码)── */
166+
nav#TOC > ul {
167+
list-style: none;
168+
padding-left: 0;
169+
}
170+
nav#TOC ul { list-style: none; padding-left: 1.2em; }
171+
nav#TOC a {
172+
color: #24292f;
173+
text-decoration: none;
174+
}
175+
nav#TOC a::after {
176+
content: leader('.') target-counter(attr(href), page);
177+
color: #9aa0a6;
178+
font-family: "Noto Sans CJK SC", sans-serif;
179+
}
180+
181+
/* 列表间距 */
182+
ul, ol { margin: 0.5em 0; padding-left: 1.6em; }
183+
li { margin: 0.2em 0; }
184+
185+
hr { border: none; border-top: 1px solid #e6e8eb; margin: 1.4em 0; }

scripts/build-pdf.sh

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#!/usr/bin/env bash
2+
# 固化入口:一键生成 rk-forge 整站教程 PDF(纯 Python:pandoc + WeasyPrint)。
3+
# 依赖由 uv 按需拉取(pypandoc-binary 内含 pandoc;weasyprint 需系统库 pango/cairo,
4+
# 本机已装;CI 里由 workflow 的 apt 步骤安装)。
5+
#
6+
# 用法: ./scripts/build-pdf.sh
7+
set -euo pipefail
8+
cd "$(dirname "$0")/.."
9+
exec uv run --no-project --with pypandoc-binary --with weasyprint python3 scripts/build_pdf.py "$@"

0 commit comments

Comments
 (0)