如何用PyPDF2和ReportLab自动为PDF地图目标位置添加唯一递增编号?
我明白你手动给PDF里的每个"A"加编号有多头疼——PyPDF2确实在文本定位这块能力有限,要搞定这个自动化任务,咱们得换个更趁手的工具:PyMuPDF(也就是fitz库),它能精准提取每个文本字符的位置,再结合它本身的PDF编辑功能,就能轻松完成编号添加。
下面给你两种可行的方案,先从最简单的纯PyMuPDF方案开始:
方案1:纯PyMuPDF实现(推荐,更简洁)
PyMuPDF不仅能提取文本位置,还能直接在PDF上添加文本,一步到位。
步骤&代码示例:
首先安装依赖:
pip install pymupdf
然后运行以下代码:
import fitz # 导入PyMuPDF # 配置参数 INPUT_PDF = "你的原始图纸.pdf" OUTPUT_PDF = "带编号的图纸.pdf" START_NUMBER = 101 # 编号的位置偏移:比如在"A"的右侧20单位,垂直居中(可根据实际调整) OFFSET_X = 20 OFFSET_Y = 0 # 编号的字体样式(尽量匹配原文档,避免违和) FONT_SIZE = 12 FONT_NAME = "helv" # 常用字体:helv(黑体), times(宋体), courier(等宽) def add_unique_numbers(): doc = fitz.open(INPUT_PDF) current_num = START_NUMBER for page in doc: # 搜索页面中所有"A"字符,获取每个的位置矩形 text_instances = page.search_for("A") for rect in text_instances: # 计算编号的位置:基于"A"的矩形右上角偏移 # PyMuPDF坐标原点在左下角,rect的x0,y0是左下角,x1,y1是右上角 num_x = rect.x1 + OFFSET_X num_y = rect.y0 + (rect.y1 - rect.y0)/2 + OFFSET_Y # 垂直居中 # 添加编号文本 page.insert_text( (num_x, num_y), str(current_num), fontsize=FONT_SIZE, fontname=FONT_NAME, color=(0, 0, 0) # 黑色,可改为(1,0,0)红色等 ) current_num += 1 # 保存修改后的PDF doc.save(OUTPUT_PDF) doc.close() print(f"已完成!编号从{START_NUMBER}开始,保存至{OUTPUT_PDF}") if __name__ == "__main__": add_unique_numbers()
关键说明:
page.search_for("A")会返回每个"A"所在的矩形区域,包含精确的坐标信息,这是PyPDF2做不到的- 你可以调整
OFFSET_X和OFFSET_Y来改变编号相对于"A"的位置,比如想放在下方就调大OFFSET_Y(注意坐标原点在左下角,y值越大越靠上) - 如果原文档的"A"是特殊字体,可能需要调整
FONT_NAME或者加载自定义字体(PyMuPDF支持加载本地字体文件)
方案2:PyMuPDF提取位置 + ReportLab生成叠加层 + PyPDF2合并
如果你坚持要用ReportLab,这个方案也可行:先提取所有"A"的位置,用ReportLab生成一个空白的PDF(只包含编号),再和原PDF合并。
步骤&代码示例:
先安装所有依赖:
pip install pymupdf reportlab PyPDF2
代码分为三部分:提取位置、生成叠加层、合并PDF:
import fitz from reportlab.pdfgen import canvas from PyPDF2 import PdfMerger, PdfReader # 配置参数 INPUT_PDF = "你的原始图纸.pdf" OVERLAY_PDF = "编号叠加层.pdf" OUTPUT_PDF = "带编号的图纸.pdf" START_NUMBER = 101 OFFSET_X = 20 OFFSET_Y = 0 FONT_SIZE = 12 # 第一步:提取所有"A"的位置,按页面存储 def get_a_positions(): doc = fitz.open(INPUT_PDF) positions = {} # key: 页码(从0开始), value: [(x,y), ...] for page_num in range(len(doc)): page = doc[page_num] text_instances = page.search_for("A") page_positions = [] for rect in text_instances: # 转换坐标到ReportLab的系统(和PyMuPDF一致,左下角为原点) num_x = rect.x1 + OFFSET_X num_y = rect.y0 + (rect.y1 - rect.y0)/2 + OFFSET_Y page_positions.append((num_x, num_y)) positions[page_num] = page_positions doc.close() return positions # 第二步:用ReportLab生成叠加层PDF def generate_overlay(positions): reader = PdfReader(INPUT_PDF) c = canvas.Canvas(OVERLAY_PDF) current_num = START_NUMBER for page_num in range(len(reader.pages)): # 设置画布大小和原页面一致 page = reader.pages[page_num] width = float(page.mediabox.width) height = float(page.mediabox.height) c.setPageSize((width, height)) # 绘制当前页面的所有编号 for (x, y) in positions.get(page_num, []): c.setFont("Helvetica", FONT_SIZE) c.drawString(x, y, str(current_num)) current_num += 1 c.showPage() # 切换到下一页 c.save() # 第三步:合并原PDF和叠加层 def merge_pdfs(): merger = PdfMerger() merger.append(INPUT_PDF) merger.append(OVERLAY_PDF, pages=(0, len(PdfReader(INPUT_PDF).pages))) merger.write(OUTPUT_PDF) merger.close() if __name__ == "__main__": positions = get_a_positions() generate_overlay(positions) merge_pdfs() print(f"已完成!保存至{OUTPUT_PDF}")
注意事项:
- 这个方案需要确保叠加层的页面大小和原PDF完全一致,否则编号会错位
- ReportLab的字体设置可能和原文档有差异,需要微调字体和大小
两种方案都能解决你的问题,优先推荐方案1,因为代码更简洁,不需要额外的合并步骤,出错概率更低。你可以先拿一页测试,调整偏移量和字体参数,确保编号位置完美后再批量处理。
内容的提问来源于stack exchange,提问作者Dat_guy_who_hangs_out
相关产品推荐
相关产品推荐

