"""坐标回写:在 PII bbox 处黑条遮盖,输出脱敏后 PDF bytes。 两种方案: A) add_redact_annot + apply_redactions(官方推荐,真正删除文字内容) B) draw_rect fill=black(只画黑条,文字仍在文本层,可被复制提取) 默认用 A(删得最干净);fill 参数可同时画出黑底。测试验证 A 能让 get_text 中 PII 消失。 """ import io from typing import Iterable, List import fitz # PyMuPDF def redact_pdf(pdf_bytes: bytes, hits: List[dict], method: str = "redact") -> bytes: """按 hits([{page, bbox}, ...])脱敏,返回新 PDF bytes。 method: 'redact' → add_redact_annot + apply_redactions(删除内容,推荐) 'draw' → 仅画黑矩形(文字仍在) """ by_page: dict = {} for h in hits: by_page.setdefault(h["page"], []).append(h["bbox"]) src = fitz.open(stream=pdf_bytes, filetype="pdf") for pno, page in enumerate(src): boxes = by_page.get(pno, []) if method == "redact": for bbox in boxes: page.add_redact_annot(bbox, fill=(0, 0, 0)) if boxes: page.apply_redactions() elif method == "draw": for bbox in boxes: page.draw_rect(bbox, color=None, fill=(0, 0, 0)) else: raise ValueError(f"unknown method: {method}") # 直接保存原文档本身(apply_redactions 已就地修改页对象)。 # 不用 insert_pdf 搬运到新文档——部分 PyMuPDF 版本会拷贝未脱敏的原始页。 buf = io.BytesIO() src.save(buf, garbage=3, deflate=True) src.close() return buf.getvalue()