| name | feishu-wiki-to-xhs |
| description | 从飞书Wiki/文档下载PDF并按逻辑内容块切分为小红书友好格式的竖版图片。触发场景:用户要把飞书文档发小红书、切图、做学习卡片、把文档拆成图片。当用户说'发小红书''切图''把文档切成图片''学习卡片'或提供飞书链接要求切分时使用。 |
| description_zh | 飞书文档→小红书切图 |
| description_en | Feishu doc to XHS images |
| disable | false |
| agent_created | true |
feishu-wiki-to-xhs
When to use
用户要把飞书 Wiki/云文档转成小红书风格的竖版切图。触发词:
- "发小红书""切图""切成图片""学习卡片"
- 提供飞书链接 + 要求切分/导出图片
- "把这个文档切成小红书格式"
Prerequisites
- lark-cli 已登录(
lark-cli auth status)
- 系统 Python 有 PyMuPDF + Pillow(
/usr/bin/python3 -c "import fitz; import PIL")
- 若缺依赖:
/usr/bin/python3 -m pip install PyMuPDF Pillow
- ⚠️ 不要用 managed Python(
~/.workbuddy/binaries/python/),原生 .so/.dylib 因 Team ID 签名不匹配会 ImportError,必须用系统 Python /usr/bin/python3
Steps
Step 1: 解包 Wiki URL 获取真实 token
Wiki 链接不能直接当 document token 用,必须先 inspect:
lark-cli drive +inspect "<wiki_url>"
从输出中获取 obj_token 和 obj_type(通常是 docx 或 sheet)。
Step 2: 导出 PDF
lark-cli doc +export --token <obj_token> --type pdf
等待导出完成,记录返回的 ticket 或 file_token。
Step 3: 下载 PDF
cd <目标目录>
lark-cli doc +export-download --token <obj_token> --type pdf --output-dir '.'
⚠️ +export-download 不接受绝对路径,必须先 cd 再用 --output-dir '.'。
Step 4: 分析 PDF 结构
在运行切图脚本前,先用系统 Python 快速查看 PDF 内容结构,确认能正确识别章节标题:
/usr/bin/python3 -c "
import fitz
doc = fitz.open('<pdf_path>')
print(f'Pages: {len(doc)}, Size: {doc[0].rect.width:.0f}x{doc[0].rect.height:.0f}pt')
for i in range(len(doc)):
page = doc[i]
td = page.get_text('dict')
for b in td.get('blocks', []):
if b.get('type') != 0: continue
for line in b.get('lines', []):
spans = line.get('spans', [])
if not spans: continue
max_fs = max(s.get('size', 0) for s in spans)
text = ''.join(s.get('text','') for s in spans).strip()
if max_fs >= 14 and text:
print(f' p{i+1} y={line[\"bbox\"][1]:.0f} fs={max_fs:.1f} | {text}')
"
fs >= 14 的行被视为章节标题/分割点
- 如果自动检测结果不理想,调整
--min-split-font 参数
Step 5: 运行切图脚本
/usr/bin/python3 ~/.workbuddy/skills/feishu-wiki-to-xhs/scripts/split_pdf_for_xhs.py \
"<pdf_path>" \
"<output_dir>" \
--dpi 200 \
--min-split-font 14
参数说明:
pdf_path: Step 3 下载的 PDF 文件路径
output_dir: 输出目录(可选,默认在 PDF 同目录下创建 <文件名>_xhs/)
--dpi: 渲染分辨率,200 适合小红书(默认 200)
--min-split-font: 最小标题字号,>=此值的行作为分块起点(默认 14)
Step 6: 检查结果
ls -lh "<output_dir>/"
确认:
- 每张图是一个完整逻辑块,没有段落被截断
- 跨页内容已纵向拼接
- 图片宽度一致(默认 1656px @200DPI)
Automatic vs Manual mode
脚本会自动检测章节标题并分块。如果自动分块效果不理想(如标题字号太小未被识别、某些块太大或太小),可以:
- 调小
--min-split-font:识别更多分割点(适合字号较小的文档)
- 手动定义分块:直接写 Python 代码,用
clip 参数精确指定 y 坐标
手动分块模板(见 @references/manual-split-template.py):
import fitz
from PIL import Image
from pathlib import Path
pdf_path = Path('<pdf_path>')
out_dir = Path('<output_dir>')
out_dir.mkdir(parents=True, exist_ok=True)
doc = fitz.open(str(pdf_path))
dpi = 200
scale = dpi / 72
page_w = doc[0].rect.width
blocks = [
("01_封面", [(0, 0, 633)]),
("02_第一章", [(0, 640, 842), (1, 0, 600)]),
]
for label, segments in blocks:
segment_images = []
for page_idx, y_top, y_bot in segments:
page = doc[page_idx]
clip = fitz.Rect(0, y_top, page_w, y_bot)
mat = fitz.Matrix(scale, scale)
pix = page.get_pixmap(matrix=mat, clip=clip, alpha=False)
img = Image.frombytes("RGB", [pix.width, pix.height], pix.samples)
segment_images.append(img)
total_w = max(img.width for img in segment_images)
total_h = sum(img.height for img in segment_images)
combined = Image.new("RGB", (total_w, total_h), (255, 255, 255))
y_offset = 0
for img in segment_images:
combined.paste(img, (0, y_offset))
y_offset += img.height
out_path = out_dir / f"{label}.png"
combined.save(str(out_path), optimize=True)
Pitfalls
- managed Python 原生库签名冲突:
~/.workbuddy/binaries/python/ 下 PyMuPDF/pypdfium2 的 .so 因 Team ID 不匹配会 ImportError,必须用 /usr/bin/python3
- lark-cli 绝对路径限制:
+export-download 的 --output-dir 只接受相对路径,先 cd 到目标目录
- Wiki URL ≠ document token:必须先
+inspect 解包,不能直接用 Wiki URL 里的 token
- 字号检测偏差:飞书导出 PDF 的标题字号取决于原文档排版,如果自动检测不准,用 Step 4 先检查实际字号再调
--min-split-font
- 小红书图片限制:单张图建议不超过 20MB;宽度 1242px 或 1656px 均可;200 DPI 下 A4 页宽约 1656px
Verification
- 确认输出目录有 N 张 PNG(N = 逻辑块数量)
- 用
sips 或 file 检查图片尺寸:sips -g pixelWidth -g pixelHeight <img>
- 目视确认无段落截断、跨页拼接无缝