如何用borb、pypdf或reportlab添加图片/二维码作为PDF注释
以注释形式添加图片到PDF的实现方案
当然可行,PDF规范支持将图片作为注释的外观,核心是通过注释字典中的/AP(外观字典)指定图片资源。以下是不同库的实现示例及PDF规范的关键字典说明:
1. 使用borb实现
borb提供了高层API直接支持自定义图片注释,无需手动操作底层PDF对象:
先安装依赖:
pip install borb
示例代码:
from borb.pdf import Document, Page, PDF from borb.pdf.canvas.geometry.rectangle import Rectangle from borb.pdf.canvas.layout.image.image import Image from borb.pdf.annotation.custom_annotation import CustomAnnotation # 新建PDF文档 doc = Document() page = Page() doc.add_page(page) # 加载目标图片(支持本地路径或字节流) image = Image("qrcode.png", width=100, height=100) # 定义注释的位置与尺寸(左下坐标x=100, y=600,宽高各100) annot_rect = Rectangle(100, 600, 100, 100) # 创建自定义注释并绑定图片外观 image_annotation = CustomAnnotation( rect=annot_rect, appearance_state="Normal", normal_appearance=image ) # 将注释添加到页面 page.add_annotation(image_annotation) # 保存PDF with open("borb_image_annot.pdf", "wb") as output_file: PDF.dumps(output_file, doc)
2. 使用PyPDF实现
PyPDF没有直接的高层API支持图片注释,需要手动构建PDF对象和注释字典:
先安装依赖:
pip install pypdf pillow
示例代码:
from pypdf import PdfWriter, PdfReader from pypdf.generic import DictionaryObject, NameObject, RectangleObject, StreamObject from PIL import Image import io # 读取现有PDF reader = PdfReader("input.pdf") writer = PdfWriter() page = reader.pages[0] # 加载图片并转换为字节流 img = Image.open("qrcode.png") img_bytes = io.BytesIO() img.save(img_bytes, format="PNG") img_bytes.seek(0) # 创建图片XObject(PDF中的图片资源) img_xobj = StreamObject() img_xobj.set_data(img_bytes.getvalue()) img_xobj.update({ NameObject("/Type"): NameObject("/XObject"), NameObject("/Subtype"): NameObject("/Image"), NameObject("/Width"): img.width, NameObject("/Height"): img.height, NameObject("/ColorSpace"): NameObject("/DeviceRGB"), NameObject("/BitsPerComponent"): 8, }) # 将图片XObject添加到页面资源 if "/Resources" not in page: page[NameObject("/Resources")] = DictionaryObject() if "/XObject" not in page["/Resources"]: page["/Resources"][NameObject("/XObject")] = DictionaryObject() img_name = NameObject("/ImgAnnot") page["/Resources"]["/XObject"][img_name] = img_xobj # 构建注释字典 annot = DictionaryObject() annot[NameObject("/Type")] = NameObject("/Annot") annot[NameObject("/Subtype")] = NameObject("/Stamp") # 用Stamp类型保证阅读器兼容性 annot[NameObject("/Rect")] = RectangleObject([100, 600, 200, 700]) # 注释边界:左下(100,600)到右上(200,700) annot[NameObject("/Contents")] = "图片注释" # 构建注释的外观字典(Normal状态) ap_dict = DictionaryObject() form_stream = StreamObject() # PDF绘图指令:保存状态→设置变换矩阵→绘制图片→恢复状态 form_stream.set_data(""" q 100 0 0 100 100 600 cm /ImgAnnot Do Q """.encode("utf-8")) form_stream.update({ NameObject("/Type"): NameObject("/XObject"), NameObject("/Subtype"): NameObject("/Form"), NameObject("/BBox"): RectangleObject([0, 0, 100, 100]), NameObject("/Resources"): page["/Resources"], }) ap_dict[NameObject("/N")] = form_stream annot[NameObject("/AP")] = ap_dict # 将注释添加到页面 if "/Annots" not in page: page[NameObject("/Annots")] = [] page["/Annots"].append(annot) # 保存输出PDF writer.add_page(page) with open("pypdf_image_annot.pdf", "wb") as output_file: writer.write(output_file)
3. PDF规范核心字典说明
如果手动构建注释,需关注以下关键键值:
/Type:/Annot— 标识对象为注释/Subtype: 可选/Stamp(兼容多数阅读器)或/Custom(自定义类型)/Rect: 注释边界矩形,格式为[x1, y1, x2, y2](PDF坐标:左下为原点)/AP: 外观字典,包含不同状态的外观资源:/N: 必选,Normal状态下的外观,可直接绑定Image XObject,或用Form XObject包裹绘图指令
- Image XObject需包含的属性:
/Type=/XObject、/Subtype=/Image、/Width、/Height、/ColorSpace、/BitsPerComponent
内容的提问来源于stack exchange,提问作者SheikStack
相关产品推荐
相关产品推荐

