- convert.py:轉換失敗改印例外型別、批次尾端彙總、--traceback 旗標; 空白轉換結果示警(避免純圖片 docx 等假成功靜默入庫) - from_legacy.py(B):.doc/.xls/.ppt 經本地 LibreOffice headless 升版為 現代格式後再走既有轉換器;raw/originals 仍保存真正的舊格式原始檔 - to_image.py + from_vision.py(A):needs_ocr 的掃描/圖片型 PDF 逐頁 render → 本地 vision 模型轉錄(--vision);:cloud/未設定防護, 模型設定走 config/models.yaml 的 tasks.vision(預設不啟用) - kb.ollama_chat 加 images 參數(向後相容);models.yaml 加 vision 範例 - selfcheck 補 legacy 路由、to_image 真測、from_vision 注入測、 --vision 端到端,全數 ALL PASS Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
49 lines
1.6 KiB
Python
49 lines
1.6 KiB
Python
"""PDF 頁 → PNG 影像(pymupdf,deterministic、全程本地)。
|
||
|
||
供 vision fallback 使用:文字抽取失敗(needs_ocr)的掃描/圖片型 PDF 逐頁 render
|
||
成影像後,交給 from_vision.py 以本地 vision 模型判讀。render 本身不含任何 LLM,
|
||
維持「deterministic 工具做結構、LLM 只做語意」的分工(AGENTS.md §3 稽核原則)。
|
||
|
||
可獨立執行:python tools/convert/to_image.py <file.pdf> --out DIR [--dpi 150]
|
||
"""
|
||
import argparse
|
||
import pathlib
|
||
|
||
import fitz # pymupdf
|
||
|
||
# ponytail: 預設 150 DPI 是「可讀性 vs 影像大小/推論成本」的折衷;掃描件文字偏小時
|
||
# 可調高。升級路徑:依頁面尺寸/內容自適應 DPI。
|
||
DEFAULT_DPI = 150
|
||
|
||
|
||
def render_pdf_pages(src, out_dir, dpi=DEFAULT_DPI, prefix="page"):
|
||
"""將 src 每頁 render 成 PNG,回傳影像路徑列表(依頁序)。"""
|
||
src = pathlib.Path(src)
|
||
out_dir = pathlib.Path(out_dir)
|
||
out_dir.mkdir(parents=True, exist_ok=True)
|
||
paths = []
|
||
doc = fitz.open(str(src))
|
||
try:
|
||
for i, page in enumerate(doc, 1):
|
||
pix = page.get_pixmap(dpi=dpi)
|
||
p = out_dir / f"{prefix}{i}.png"
|
||
pix.save(str(p))
|
||
paths.append(p)
|
||
finally:
|
||
doc.close()
|
||
return paths
|
||
|
||
|
||
def main(argv=None):
|
||
ap = argparse.ArgumentParser(description=__doc__)
|
||
ap.add_argument("src")
|
||
ap.add_argument("--out", required=True, help="影像輸出目錄")
|
||
ap.add_argument("--dpi", type=int, default=DEFAULT_DPI)
|
||
a = ap.parse_args(argv)
|
||
paths = render_pdf_pages(a.src, a.out, a.dpi)
|
||
print(f"{len(paths)} 頁 → {a.out}")
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main()
|