#!/usr/bin/env bash # 设计稿构建:把整页 HTML 按顶层 .page 拆成单页 → html2poster 逐页渲染 → 合并为 PDF # 用法:bash build.sh [src ...] # src 为相对 mockup 的 html 路径(如 components.html 或 design-system/01-colors.html) # 默认:历史 mockup(course-shelf/components/color-system)+ design-system 四部分文档 set -euo pipefail cd "$(dirname "$0")" PDF_SKILL_DIR="${PDF_SKILL_DIR:-$HOME/.zcode/cli/plugins/cache/zcode-plugins-official/pdf/0.1.7/skills/pdf}" export PDF_SKILL_DIR # 本地渲染器(固定 headless-shell 规避系统 Chrome 占用;TMPDIR 重定向规避 /tmp 磁盘配额) HTML2POSTER="$PWD/tools/html2poster.js" H2P_CHROMIUM="${H2P_CHROMIUM:-$HOME/.cache/ms-playwright/chromium_headless_shell-1234/chrome-headless-shell-linux64/chrome-headless-shell}" export HTML2POSTER H2P_CHROMIUM mkdir -p "$HOME/.cache/h2p-tmp" export TMPDIR="$HOME/.cache/h2p-tmp" # build_one build_one() { local SRC="$1" PDF_OUT="$2" local STEM; STEM=$(basename "${SRC%.html}") local OUT_DIR="single-${STEM}" mkdir -p "$OUT_DIR" "$(dirname "$PDF_OUT")" # 1. 按顶层 .page 拆单页(固定布局必须走 html2poster,不能用 html2pdf-next) python3 - "$SRC" "$OUT_DIR" <<'EOF' import re, sys src, out_dir = sys.argv[1], sys.argv[2] html = open(src, encoding="utf-8").read() head, rest = html.split("", 1) body_inner = rest.rsplit("", 1)[0] pat = re.compile(r'(?=
)') chunks = [c for c in pat.split(body_inner) if c.lstrip().startswith('
\n" + page_html + "\n\n") print(f"[{src}] split {len(chunks)} pages") EOF # 2. 单页素材(img 相对路径以单页目录为基准) [ -d assets ] && cp -r assets "$OUT_DIR/assets" # 3. 逐页渲染 python3 - "$OUT_DIR" <<'EOF' import glob, os, re, subprocess, sys out_dir = sys.argv[1] pages = sorted(glob.glob(f"{out_dir}/page-*.html"), key=lambda p: int(re.search(r"(\d+)", os.path.basename(p)).group(1))) for p in pages: out = p.replace(".html", ".pdf") subprocess.run(["node", os.environ['HTML2POSTER'], p, "--output", out, "--width", "1280px"], check=True, capture_output=True) print(f"[{out_dir}] rendered {len(pages)} pages") EOF # 4. 合并 + 元数据(只取第 1 页:Chromium 打印偶发产生一个空白尾页) python3 - "$OUT_DIR" "$PDF_OUT" <<'EOF' import glob, os, re, sys from pypdf import PdfWriter out_dir, pdf_out = sys.argv[1], sys.argv[2] pdfs = sorted(glob.glob(f"{out_dir}/page-*.pdf"), key=lambda p: int(re.search(r"(\d+)", os.path.basename(p)).group(1))) w = PdfWriter() for p in pdfs: w.append(p, pages=(0, 1)) w.add_metadata({"/Title": f"设计系统 · {os.path.splitext(os.path.basename(pdf_out))[0]} · stack idealjs", "/Author": "Z.ai", "/Creator": "Z.ai"}) with open(pdf_out, "wb") as f: w.write(f) print(f"merged {len(pdfs)} pages -> {pdf_out}") EOF echo "done: $PDF_OUT" } SRCS=("$@") if [ ${#SRCS[@]} -eq 0 ]; then SRCS=(course-shelf.html components.html color-system.html design-system/01-colors.html design-system/02-symbols.html design-system/03-components.html design-system/04-examples.html) fi for SRC in "${SRCS[@]}"; do [ -f "$SRC" ] || { echo "!! 缺少 $SRC,跳过"; continue; } build_one "$SRC" "${SRC%.html}.pdf" done