PDF 在线放映器(仅阅读 · 防下载)
把一份 PDF 渲染成逐页图片,生成一个零依赖的网页放映器:canvas 逐页绘制、
左右翻页、全屏放映、进度条,并通过多层手段禁止下载/复制/打印。
适用场景
- 课件、内部文档、培训材料想发给学员/同事「在线看」,但不想让人直接拿到 PDF 源文件。
- 需要左右翻页的网页版「放映」体验,而非浏览器原生 PDF 阅读器。
铁律
- 原始 PDF 绝不进入产出目录。 只产出逐页图片 + 网页,从源头消除「另存为 PDF」。
- 不要为了「更保险」去加下载/打印按钮或导出功能。 本 skill 的卖点就是只读。
- 防下载是「提高门槛、劝退随手保存」的客户端手段,无法 100% 防住技术用户;
交付时如实说明这一点,不要承诺「绝对无法下载」。
前置依赖
脚本用 PyMuPDF(fitz)渲染页面。首次运行若缺失,先装:
pip install PyMuPDF
# 或本机隔离环境:
# <managed_python> -m venv <venv> && <venv>/Scripts/pip install PyMuPDF
执行流程
Step 1 确认输入
向用户要 PDF 路径;可选确认:标题(页头/网页标题)、水印文字、渲染清晰度(DPI)。
Step 2 运行构建脚本
python <skill_dir>/scripts/build.py \
--pdf "<PDF路径>" \
--out "<产出目录>" \
--title "我的课件" \
--watermark "内部资料 · 仅供学习" \
--dpi 144 \
--quality 85
参数说明:
- --pdf:输入 PDF(必填)
- --out:产出目录(默认 ./dist),里面会生成 index.html / manifest.js / assets/
- --title:网页标题与页头显示名
- --watermark:画布内 + 全屏覆盖水印文字
- --dpi:渲染分辨率,默认 144(16:9 页 => 1920×1080);追求清晰可提到 160–200,体积会变大
- --quality:JPG 质量 1–100,默认 85
- --pad:页码补零位数(默认 3 => 001);--ext 可改 png
脚本会打印 OK total=... sizeMB=...,确认无报错。
Step 3 验证渲染
起一个本地静态服务,用无头浏览器截图确认首屏正常(无头 Chrome 复用本机 ms-playwright 缓存):
# 1) 起服务
<managed_python> -m http.server 8123 --bind 127.0.0.1 # 在 --out 目录下
# 2) 截图(路径带空格要用绝对路径)
<ms-playwright-chrome> --headless --no-sandbox --disable-gpu \
--window-size=1280,800 --virtual-time-budget=8000 \
--screenshot="<绝对路径>/_test.png" "http://127.0.0.1:8123/index.html"
# 3) 用 Read 工具看 _test.png,确认首屏、水印、控件正常,再删掉 _test.png
Step 4 交付 / 部署
- 本地离线:直接把整个
--out目录发给用户,双击index.html即可(无需服务器,manifest.js 已内联配置,file:// 可用)。 - 在线托管:把
--out目录部署到静态服务。例如 WorkBuddy 的 CloudStudio:
用workbuddy_cloudstudio_deploy工具,目录指向--out,entry 填index.html。 - 验证在线链接可打开后再交付。
放映器能力(产出网页自带)
- 翻页:底部按钮、键盘 ← → / PageUp/Down / 空格 / Home/End、触屏左右滑动、点击页面左右两侧热区。
- 跳页:底部输入框跳到指定页;进度条显示位置;「⛶ 放映」进入全屏。
- 防下载:禁用右键菜单、拖拽、文字选中、复制/剪切、Ctrl+S/P/U、F12、打印;
画布内斜向平铺水印 + 全屏 CSS 水印层(截图/录屏也带水印)。 - 响应式:窗口缩放自动重绘适配;高 DPI 屏清晰。
自定义进阶
- 换配色:改
template.html顶部:root的 CSS 变量(背景/强调色)。 - 加缩略图导航条、自动轮播等:在
template.html的 JS 里扩展,改完把新版
放回<skill_dir>/scripts/template.html,后续构建自动套用。 - 多份课件:每份 PDF 各自
--out到不同目录;想做「上一课/下一课」入口,在外层再加一个索引页即可。