PDF 工具箱
成品文件公式:用户给 PDF(或图片)→ 直接拿到成品文件(压好的 PDF / 一批图片 / 合成好的 PDF / 加了水印或页码的 PDF),不是教程、不是参数建议。核心刚需:报名/上传系统卡"PDF 不超过 X MB"、把 PDF 内容导成图片发群、把手机拍的多张图拼成一个 PDF 上传、给文件打"机密/内部资料"水印防盗传、给长文档补页码。全程本地处理(PyMuPDF + Pillow),无需网络与 API Key。
> ⚠️ 本技能不做合并不做拆分。合并/拆分 PDF 请使用平台内置的 PDF 处理能力——那里已覆盖。本技能只负责"压缩 / 转图片 / 图片合成 / 水印 / 页码"。
功能
| 子命令 | 用途 | 典型场景 |
|---|---|---|
compress |
压缩 PDF 到指定大小或按档位 | 报名系统要求 PDF ≤ 2MB;扫描件太大发不出 |
to-images |
PDF 转 PNG/JPG(可指定页范围与 dpi) | 把 PDF 某几页导成图片发微信/做封面 |
from-images |
多张图片合成一个 PDF(按文件名排序) | 手机拍的身份证/合同/作业拼成一个 PDF 上传 |
watermark |
加文字水印(斜向平铺) | 给"机密/内部资料/样稿"打防盗传水印 |
pagenum |
加页码(四角 + 底部居中) | 打印前给文档补页码 |
脚本内置策略(agent 无需操心):
- 压缩双路径:①图片降采样(优先,保留文字可选中)——用 PyMuPDF 重存
garbage=4 + deflate,再把 PDF 里的图片对象按缩放比例 + JPEG 质量逐级压低;②整页重绘为图片(降级兜底,文字变图片不可选中)——按 dpi + 质量把每页渲成 JPEG 重组 PDF。脚本从轻到重依次尝试,自动选到达标的最轻方案。 - 达标即停:指定
--target时,一旦某档位压到目标以内立即停止,不浪费多余压缩。 - 安全兜底:若压缩结果反而变大,自动保留原文件并说明,不产生"越压越大"的坑。
- 页范围解析:
to-images --pages支持1-3/1,3,5/all/2。 - 文件名自然排序:
from-images按1,2,10(而非1,10,2)排序,避免合并错序。 - 透明保留:合成 PDF 时,RGBA/透明 PNG 保留透明通道,不铺白底破坏 PNG。
- 中文支持:水印、页码使用 PyMuPDF 内置简体中文字体
china-s,中文不出方块。
工作流程(SOP)
- 拿三要素:①输入文件路径(PDF 或图片)②想要的结果(压到多大 / 转什么格式 / 加什么水印 / 页码位置)③输出路径(不给就用默认名)。
- 一律用绝对路径执行命令(脚本路径固定):
bash
SKILL_DIR="$HOME/.workbuddy/skills/pdf-toolbox"
[ -f "$SKILL_DIR/scripts/pdf_toolbox.py" ] || SKILL_DIR="$PWD/.workbuddy/skills/pdf-toolbox"
[ -f "$SKILL_DIR/scripts/pdf_toolbox.py" ] || SKILL_DIR="/sandbox/workspace/skills/pdf-toolbox"
S="$SKILL_DIR/scripts/pdf_toolbox.py"
python3 $S compress /path/in.pdf --target 2MB -o /path/out.pdf
- 交付:命令会打印
原始 X → 压缩后 Y(减小 N%)等结果行,把输出文件的绝对路径 + 实际大小报给用户即可,无需解释技术细节。 - 出现
⚠️ 未达到目标时(exit code=1):如实告知用户已压到该档位极限,给两个选项——(a)放宽目标 / (b)提高档位(--level high)允许"文字变图片"换取更小体积。 - 不确定压到多少合适:先按用户给的目标跑;只给"压小点"时用
--level med起步。 - 合并 / 拆分请求 → 转交内置 PDF 技能:如果用户要"合并两个 PDF / 拆分某几页",先说明"合并拆分由平台内置 PDF 技能处理",不要用本技能硬做。
命令参考
S="$SKILL_DIR/scripts/pdf_toolbox.py"
# 1) 压缩到指定大小(核心命令)
python3 $S compress in.pdf --target 1MB -o out.pdf
# --target 目标大小:1MB / 500KB / 2(纯数字按 MB;1KB=1024B 口径)
# --level low/med/high(不指定目标时默认 low;指定 --target 时默认 high)
# -o 输出路径(默认 原名_compressed.pdf)
# --password 加密 PDF 的密码
# 2) 按档位压缩(不设目标,只要"尽量小")
python3 $S compress in.pdf --level high -o out.pdf
# 3) PDF 转图片
python3 $S to-images in.pdf --dpi 150 --pages 1-3 --format png -d ./imgs
# --dpi 分辨率,默认 150
# --pages 1-3 / 1,3,5 / all(默认 all)
# --format png(默认,支持透明)/ jpg
# --quality JPG 质量,默认 88
# -d 输出目录(默认 原名_images)
# 4) 多张图片 → 一个 PDF
python3 $S from-images ./imgs --page-size a4 -o merged.pdf
# 输入可为多个文件或目录(可混用),按文件名自然排序
# --page-size a4/a3/a5/letter/legal/original(默认 a4 自适应居中)
# --margin 页边距(pt),默认 18
# -o 输出(默认 merged.pdf)
# 5) 加文字水印(斜向平铺)
python3 $S watermark in.pdf --text "机密" --opacity 0.15 --angle 45 -o wm.pdf
# --text 水印文字(必填)
# --opacity 不透明度 0-1,默认 0.15
# --angle 旋转角度(度),默认 45
# --fontsize 字号(默认按页宽自适应)
# -o 输出(默认 原名_watermark.pdf)
# 6) 加页码
python3 $S pagenum in.pdf --position bottom-center -o numbered.pdf
# --position bottom-right(默认)/bottom-center/bottom-left/top-right/top-center/top-left
# --start 起始页码,默认 1
# --format 页码格式,{n}=当前页 {total}=总页数(默认 "第 {n} 页 / 共 {total} 页")
# --fontsize 字号,默认 10 ; --margin 边距,默认 28
示例对话
例 1:PDF 压到 1MB
> 用户:这个 PDF 2.6MB,报名系统要 1MB 以内,帮我压一下
> Agent:(执行 compress 报名表.pdf --target 1MB -o 报名表_compressed.pdf)
> "压缩完成:原始 2.6MB → 771KB(减小 71%),文字仍可选中。文件在 ./报名表_compressed.pdf,可以直接上传。"
例 2:PDF 导出成图片
> 用户:把这份 PDF 前 3 页导成高清图片发我
> Agent:(执行 to-images 文件.pdf --dpi 150 --pages 1-3 -d ./导出图)
> "已导出 3 张 PNG(150dpi)到 ./导出图/,分别是 文件_page001.png ~ page003.png。"
例 3:多张图拼成一个 PDF
> 用户:这几张身份证照片,帮我拼成一个 PDF 上传
> Agent:(执行 from-images ./证件照 --page-size a4 -o 证件合集.pdf)
> "已把 3 张图合成为 PDF(按文件名顺序):./证件合集.pdf,每张一页、A4 居中。"
例 4:加"机密"水印
> 用户:这份合同加上"机密"水印,别太显眼
> Agent:(执行 watermark 合同.pdf --text "机密" --opacity 0.15 --angle 45)
> "已在每页加上斜向平铺的「机密」水印(淡灰、15% 不透明度):./合同_watermark.pdf。"
例 5:合并请求 → 转交
> 用户:帮我把这两个 PDF 合并成一个
> Agent:"合并 PDF 请用平台内置的 PDF 处理能力哦;本工具箱负责的是压缩/转图片/图片合成/水印/页码。需要的话我也可以帮你把这两个文件先压缩或用图片方式合成。"
运行环境
- 脚本:
scripts/pdf_toolbox.py(相对技能根目录) - Python:3.9+
- 第三方库:
Pillow(pip install Pillow)、PyMuPDF(pip install PyMuPDF)
开工前先跑一次自检,缺什么当场补,别等跑到一半报错:
python3 -c "import PIL" 2>/dev/null || echo "缺 Pillow,请执行 pip install Pillow"
python3 -c "import fitz" 2>/dev/null || echo "缺 PyMuPDF,请执行 pip install PyMuPDF"
python3 -c "import pymupdf" 2>/dev/null || echo "缺 PyMuPDF,请执行 pip install PyMuPDF"
> 缺依赖时用中文告诉用户缺什么、装完就能用,不要把 Python 的英文 traceback 原样贴给用户。
注意事项
- 只用绝对路径;脚本路径固定为
<技能目录>/scripts/pdf_toolbox.py。 - 错误清晰:文件不存在、不是有效 PDF、加密 PDF 无密码(或密码错误)都会给出明确中文报错并退出(exit code=2),不会静默产出一个坏文件。
- 加密 PDF:需通过
--password提供密码;本工具不做暴力破解。 - 压缩取舍:优先走"图片降采样"(文字可选中、画质保留较好);只有当指定目标非常小、图片路径压不到时,才降级为"整页重绘为图片",此时文字将无法选中、也不可搜索——脚本会在输出里明确标注"文字将无法选中"。
- 未达标会警告:极小的目标(如把扫描件压到几十 KB)可能受档位限制仍不达标,此时 exit code=1 并打印
⚠️,agent 应如实告知用户,不要静默交付。 - 不做的事:合并 / 拆分(用内置 PDF 技能)、PDF 编辑正文、OCR、加密解密、签名、PDF↔Word;如需这些请换用对应工具。
- 依赖:PyMuPDF(fitz,环境自带 1.28.2)+ Pillow(11.3.0);无需 qpdf、无需 pikepdf、无需网络与 API Key。
- 支持输入:compress/watermark/pagenum/to-images 仅接受 PDF;from-images 接受 png/jpg/jpeg/bmp/tif/tiff/webp/gif。