rogerwang2008 f1888fbaf8 feat: PDF 导出脚本支持三页合一
逐页渲染 page1/page2/page3 并用 pypdf 合并为单个 PDF,默认输出 dist/三页demo.pdf;
--page 改为可多值的 --pages(保留旧名),每页各自校验四边 1em 与 Zpix 字体。
README 同步导出用法与各页说明。
2026-11-11 04:18:11 +08:00
2026-11-11 04:17:55 +08:00
2026-11-10 21:04:17 +08:00

《关于「甲子」的虚构故事》公众号推送 Demo

一个 28 列 × 44 行的汉字文字画 Demo。黑底、每格一个汉字,空白处由淡色「天」铺满;画面包含晚霞云、落日、飞鸟、天线与城市楼群,上方以整格扣出的黑色标题框居中呈现「关于「甲子」的虚构故事」。

项目规划为三张类似页面,当前实现第 1 页(page1.html)、第 2 页(page2.html)与第 3 页(page3.html)。

效果概述

  • 28 列 × 44 行,严格按字符网格排布,全页文字统一字号。
  • 空白格自动填充淡色「天」,形成低对比背景纹理。
  • 元素按层绘制:云 → 落日 → 飞鸟 → 天线 → 楼群。楼群最后绘制,保证太阳在无楼处仍保留完整圆弧。
  • 标题区域按整格对齐扣空,并叠放黑色面板,居中显示白色标题。
  • 使用 Zpix 像素字体;正文行通过负字距补偿,把单字步进压回 1em。

目录结构

.
├─ page1.html              # 第 1 页入口
├─ page2.html              # 第 2 页入口
├─ page3.html              # 第 3 页入口
├─ pyproject.toml          # uv 环境与导出依赖
├─ uv.lock                 # 依赖锁定
├─ scripts/export_pdf.py   # PDF 导出脚本
├─ assets/
│  ├─ css/
│  │  ├─ common.css        # 三页共用:字体、骨架、网格、标题框
│  │  ├─ page1.css         # 第 1 页配色
│  │  ├─ page2.css         # 第 2 页配色
│  │  └─ page3.css         # 第 3 页配色与前景标题
│  ├─ js/
│  │  ├─ text-scene.js     # 共享文字画渲染引擎
│  │  ├─ page1.js          # 第 1 页场景数据
│  │  ├─ page2.js          # 第 2 页场景数据
│  │  └─ page3.js          # 第 3 页场景数据
│  └─ fonts/zpix.ttf       # Zpix 像素字体
└─ references/a.html       # 原始参考页

运行

直接打开 page1.html 即可。若需通过本地服务器预览:

python -m http.server 8000
# 第 1 页 http://localhost:8000/page1.html
# 第 2 页 http://localhost:8000/page2.html
# 第 3 页 http://localhost:8000/page3.html

多页结构

现有页面:

  • page1.html:完整日落城市文字画,空白处铺淡色「天」。
  • page2.html:空白处铺淡色「墙」;底排「地」,其上以「桌」「椅」摆出桌子与椅子,桌面立「酒杯」「烛」与火,右下角「桶」包「酒」作酒桶,右上角「链」「笼」「灯」「穗」拼出灯笼,墙上以「框」包「图」作画框,前景为「酒水单」与两列曲目。
  • page3.html:空白处铺淡色「黑」的演职员表;含 36 个演职员 nametag、电线、麻雀、草、消防栓与下半屏火山,nametag 与「演职员表」四周的空白格不铺背景。

共用与专属:

  • common.css、text-scene.js:三页共用的页面骨架、网格、标题框与渲染引擎。
  • pageN.html、pageN.css、pageN.js:每一页的入口、配色与场景数据。

新增页面时复制 page1.* 三件套,修改 pageN.js 中的数据与 pageN.css 配色,并按顺序引入 common.css、pageN.css、text-scene.js、pageN.js。

导出 PDF

使用 Playwright(由 uv 管理)导出,页面尺寸为 30em × 46em:28 × 44 格的画面,四周各留恰好 1em。默认把三张页面合成一份三页 PDF。

uv sync
uv run python scripts/export_pdf.py                                   # 三页 → dist/三页demo.pdf
uv run python scripts/export_pdf.py --page page2.html -o dist/page2.pdf
uv run python scripts/export_pdf.py --pages page1.html page3.html -o dist/选页.pdf
uv run python scripts/export_pdf.py --em 16 -o dist/三页demo-16.pdf

--page / --pages 按给定顺序逐页渲染后用 pypdf 合并,只传一个页面即得到单页 PDF。

脚本优先调用本机 Edge / Chrome;若都未安装,可先下载 Playwright Chromium:

uv run playwright install chromium

导出页为黑底满幅,画面距上、下、左、右边界各 1em;脚本会在导出前逐页校验该边距。默认 --em 24 时每页输出 720 × 1104 px(约 190 × 292 mm)。

调整指南

构图(assets/js/page1.js)

数据 含义
clouds 云:三行 3/5/3 的云形,x/y 为最宽一行的左上角
sun 落日:逐行圆形切片,数组顺序对应顶部的红到底部的黄
birds 飞鸟:每只占 2 格(麻雀 / 喜鹊 / 乌鸦)
antennas 天线:单列「线」,从 y 向下叠 h 格
buildings 楼群:x/y/w/h,n 对应 .bld-n
title 标题文案、高亮词、起始行、行高、左右留格数

坐标为 0–27 列、0–43 行;越界会直接抛错提示。

样式

共用样式(assets/css/common.css):

  • 整体字号:.scene 的 font-size,所有元素统一继承。
  • 标题:.title-panel(黑色矩形)与 .title-panel .title(文字样式)。

页面样式(assets/css/page1.css):

  • 背景「天」:.sky。
  • 云:.cloud 的明度与 .cloud-* 的色相。
  • 落日:.sun 的整体明度(当前为原色 80%),.sun-t1 至 .sun-t17 为逐行色阶。
  • 飞鸟:.bird-sparrow、.bird-magpie、.bird-crow。
  • 天线:.ant(深色款为 .ant-dark)。
  • 楼群:.bld 与 .bld-1 至 .bld-8。

第 2 页(assets/js/page2.js)

block(x, y, w, h, text, cls) 按矩形铺字:桌面与桌腿、靠背/座面/椅腿、枪口/枪身/握把都由若干矩形拼出,坐标按格对齐;配色见 page2.css 的 .table、.chair、.gun、.ground。

第 3 页(assets/js/page3.js)

通过 glyphs 数组放置任意背景字:{ text, x, y, cls, vertical },坐标仍按格对齐。前景标题 .credits 由页面脚本追加,可在 page3.css 中调整字号与位置。

实现说明

  • text-scene.js 先建立 28×44 网格并全部填「天」,再按页面数据覆盖元素;同 class 的连续字符合并为单个 span 输出。
  • 标题区把对应格子清空,并在同一位置叠放绝对定位的黑色面板;边缘与字符整格对齐,不会出现半个字。
  • Zpix 单字步进为 13/12 em,因此 .line 使用 letter-spacing: -0.083333em 补偿,保证 28 列恰好落在 28em 宽内。

字体许可

assets/fonts/zpix.ttf 为第三方 Zpix 像素字体,版权归原作者所有;使用或分发前请遵循其原始许可说明。

S
Description
北京大学音乐剧社 Gala《关于「甲子」的虚构故事》公众号推送风格 Demo
Readme
3 MiB
0 Stars 1 Watchers 0 Forks
Languages
JavaScript 44.6%
HTML 23.4%
CSS 17.7%
Python 14.3%