尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

pypdf annotations 模块完全指南:为 PDF 添加与读取文本、链接、高亮等注释

pypdf annotations 模块完全指南:为 PDF 添加与读取文本、链接、高亮等注释 pypdf annotations 模块完全指南为 PDF 添加与读取文本、链接、高亮等注释【免费下载链接】pypdfA pure-python PDF library capable of splitting, merging, cropping, and transforming the pages of PDF files项目地址: https://gitcode.com/GitHub_Trending/py/pypdf导读本文以 pypdf 仓库中 annotations.rst 所自动生成的pypdf.annotations模块为核心系统讲解 pypdf 提供的全部注释Annotation类型及其底层实现包括 Text、FreeText、Line、PolyLine、Rectangle、Ellipse、Polygon、Highlight 等标记类注释以及 Link、Popup 两类非标记注释。你将掌握通过PdfWriter.add_annotation()为指定页面写入各类注释、通过page[/Annots]读取已有注释的完整方案并理解每个注释对象本质上是 PDFDictionaryObject这一设计带来的可扩展性。模块概览pypdf.annotations 提供了什么pypdf.annotations是 pypdf 为创建 PDF 注释提供的独立子包其入口在 pypdf/annotations/init.py对外导出了以下符号基类与常量AnnotationDictionary、MarkupAnnotation、NO_FLAGS标记类注释Text、FreeText、Line、PolyLine、Polygon、Rectangle、Ellipse、Highlight非标记类注释Link、Popup该模块的文档说明中特别强调了两点设计原则理解它们有助于正确使用 API注释类名与 PDF 规范名称并不总是一一对应。例如 PDF 标准中的Square注释实际上并不要求是正方形因此 pypdf 将其命名为Rectangle同理Circle被命名为Ellipse。这与 docs/user/adding-pdf-annotations.md 中Rectangle 方法使用 PDF 格式的 square 注释类型的说明是一致的。所有注释类型的核心都是DictionaryObject。也就是说每个注释本质上就是一个 PDF 字典对象如果 pypdf 尚未实现某个特性用户可以像操作普通字典一样直接扩展其功能例如手动写入/C颜色条目。注释的基类AnnotationDictionary 与 flags 属性所有注释都继承自 pypdf/annotations/_base.py 中的AnnotationDictionary它本身继承自DictionaryObject。基类构造函数只做两件事写入/Type条目为/Annot声明这是一个注释对象不设置任何 flags——这是刻意为之文档注释明确说明大多数用户不需要修改默认值若需要可用flags属性自行设置默认值为 0。flags属性读写的是 PDF 字典中的/F条目类型为AnnotationFlag定义于 pypdf/constants.py它是一个IntFlag枚举取值包括标志值含义INVISIBLE1注释不可见除非激活HIDDEN2不显示也不打印PRINT4打印时显示NO_ZOOM8视图缩放时不变形NO_ROTATE16页面旋转时不随之旋转NO_VIEW32屏幕上不显示READ_ONLY64用户不可交互修改LOCKED128锁定用户不可删除或修改TOGGLE_NO_VIEW256与 NO_VIEW 组合可切换显示LOCKED_CONTENTS512内容锁定但可移动注释本身模块还导出了NO_FLAGS AnnotationFlag(0)作为无任何标志的默认值。MarkupAnnotation标记类注释的公共基类Text、FreeText、Line、PolyLine、Polygon、Rectangle、Ellipse、Highlight均继承自 pypdf/annotations/_markup_annotations.py 中的MarkupAnnotation。它定义了所有标记类注释共享的关键字参数title_bar显示在注释标题栏中的文本按惯例是作者名对应 PDF 条目/Tin_reply_to本条注释回复的注释PDF 1.5 起可传已通过writer.add_annotation()添加过的注释对象或其间接引用对应条目/IRTreply_type与in_reply_to的关系取R回复默认或Group与父注释分组对应/RTannotation_name唯一标识该注释的文本对应/NM当设置了in_reply_to而未提供名字时会自动用uuid.uuid4()生成。从源码可以看出几个严格校验规则annotation_name仅在设置in_reply_to时允许传入否则抛出ValueErrorreply_type非默认值同理。另外若in_reply_to传入的是普通字典对象而非已注册注释会抛出ValueError提示必须先通过writer.add_annotation()注册。各类注释详解与实战示例下文所有示例均沿用 docs/user/adding-pdf-annotations.md 的标准流程先用PdfReader读取页面加入PdfWriter构造注释对象再调用writer.add_annotation(page_number..., annotation...)最后writer.write(...)输出。Text文本注释便签Text注释显示为可点击的图标点击后展开文本内容对应 PDF/Text子类型。构造参数rect[xLL, yLL, xUR, yUR]四个数值指定可点击矩形区域text要加入文档的文本open是否默认展开默认Falseflags注释标志默认NO_FLAGS。from pypdf import PdfReader, PdfWriter from pypdf.annotations import Text from pypdf.constants import AnnotationFlag reader PdfReader(crazyones.pdf) page reader.pages[0] writer PdfWriter() writer.add_page(page) annotation Text( textHello World\nThis is the second line!, rect(50, 550, 200, 650), openTrue, ) annotation.flags AnnotationFlag.PRINT # 标记为可打印 writer.add_annotation(page_number0, annotationannotation) writer.write(out-text.pdf)FreeText自由文本注释FreeText用于在页面上的矩形框中直接显示富文本适合批注、说明等场景效果见 free-text-annotation.png。它的参数最为丰富text、rect必填含义同上font字体名默认Helveticabold/italic是否加粗 / 斜体font_size字号字符串默认14ptfont_color字体颜色十六进制字符串如00ff00默认000000黑border_color边框颜色传None则无边框/BS宽度置 0默认000000background_color背景颜色默认ffffff白对应/C条目。源码实现中font_*参数会按 PDF 1.7 参考的 CSS2 style attributes used in rich text strings 拼接成/DS富文本样式字符串border_color则通过hex_to_rgb()转为默认外观字符串写入/DA。完整示例from pypdf import PdfReader, PdfWriter from pypdf.annotations import FreeText from pypdf.constants import AnnotationFlag reader PdfReader(crazyones.pdf) page reader.pages[0] writer PdfWriter() writer.add_page(page) annotation FreeText( textHello World\nThis is the second line!, rect(50, 550, 200, 650), fontArial, boldTrue, italicTrue, font_size20pt, font_color00ff00, border_color0000ff, background_colorcdcdcd, ) annotation.flags AnnotationFlag.PRINT writer.add_annotation(page_number0, annotationannotation) writer.write(out-free-text.pdf)Line直线注释Line绘制一条从p1到p2的直线效果见 annotation-line.png同时支持text附加说明。内部写入/L端点坐标数组、/LE两端线帽默认/None以及/IC内部颜色默认灰 0.5,0.5,0.5。from pypdf import PdfReader, PdfWriter from pypdf.annotations import Line reader PdfReader(crazyones.pdf) writer PdfWriter() writer.add_page(reader.pages[0]) annotation Line( textHello World\nLine2, rect(50, 550, 200, 650), p1(50, 550), p2(200, 650), ) writer.add_annotation(page_number0, annotationannotation) writer.write(out-line.pdf)PolyLine折线注释PolyLine接受顶点列表vertices[(x, y), ...]自动计算包围盒作为/Rect空列表会抛出ValueError。注意默认折线是透明的无颜色必须显式设置颜色才会可见。用户文档给出的做法是直接给字典写入/C条目数组元素取值范围 0.0~1.0一个元素为灰度、三个为 RGB、四个为 CMYKfrom pypdf import PdfReader, PdfWriter from pypdf.annotations import PolyLine from pypdf.generic import ArrayObject, FloatObject, NameObject reader PdfReader(crazyones.pdf) writer PdfWriter() writer.add_page(reader.pages[0]) annotation PolyLine( vertices[(50, 550), (200, 650), (70, 750), (50, 700)], ) annotation[NameObject(/C)] ArrayObject( [FloatObject(0.9), FloatObject(0.1), FloatObject(0)] ) writer.add_annotation(page_number0, annotationannotation) writer.write(out-polyline.pdf)Rectangle 与 Ellipse矩形与椭圆圆注释Rectangle对应 PDF 的/Square子类型Ellipse对应/Circle。两者都支持interior_color十六进制填充色对应/IC不传则只画边框from pypdf import PdfReader, PdfWriter from pypdf.annotations import Rectangle, Ellipse reader PdfReader(crazyones.pdf) writer PdfWriter() writer.add_page(reader.pages[0]) rect_annot Rectangle(rect(50, 550, 200, 650), interior_colorff0000) ellipse_annot Ellipse(rect(250, 550, 400, 650)) writer.add_annotation(page_number0, annotationrect_annot) writer.add_annotation(page_number0, annotationellipse_annot) writer.write(out-rect-ellipse.pdf)效果分别见 annotation-square.png 与 annotation-circle.png。Polygon多边形注释Polygon接受顶点列表写入/Vertices、包围盒/Rect并设置/IT为/PolygonCloud云状边框意图。空顶点列表同样抛出ValueError。示例from pypdf import PdfReader, PdfWriter from pypdf.annotations import Polygon reader PdfReader(crazyones.pdf) writer PdfWriter() writer.add_page(reader.pages[0]) annotation Polygon( vertices[(50, 550), (200, 650), (70, 750), (50, 700)], ) writer.add_annotation(page_number0, annotationannotation) writer.write(out-polygon.pdf)效果见 annotation-polygon.png。Highlight文本高亮注释Highlight属于文本标记注释需要精确指定文本所在位置的Quad points四边形点。quad_points是一个ArrayObject按顺序给出四边形四个角的坐标[x1, y1, x2, y2, x3, y3, x4, y4]highlight_color默认ff0000红printingTrue时自动设置AnnotationFlag.PRINT。from pypdf import PdfReader, PdfWriter from pypdf.annotations import Highlight from pypdf.generic import ArrayObject, FloatObject reader PdfReader(crazyones.pdf) writer PdfWriter() writer.add_page(reader.pages[0]) rect (50, 550, 200, 650) quad_points [rect[0], rect[1], rect[2], rect[1], rect[0], rect[3], rect[2], rect[3]] annotation Highlight( rectrect, quad_pointsArrayObject([FloatObject(qp) for qp in quad_points]), ) writer.add_annotation(page_number0, annotationannotation) writer.write(out-highlight.pdf)效果见 annotation-highlight.png。非标记注释Link 与 Popup这两类注释定义在 pypdf/annotations/_non_markup_annotations.py它们不继承MarkupAnnotation而直接继承AnnotationDictionary。Link外部与内部链接Link构造参数为rect、可选的border边框前 3 项为线宽等第 4 项可为虚线模式数组、url外部链接或target_page_index内部目标页以及fit目标页显示方式类型为Fit默认DEFAULT_FIT。源码强制二选一url与target_page_index必须且只能提供其一否则抛出ValueError。外部链接会生成带/S/URI、/Type/Action、/URI的动作字典写入/Afrom pypdf import PdfReader, PdfWriter from pypdf.annotations import Link reader PdfReader(crazyones.pdf) writer PdfWriter() writer.add_page(reader.pages[0]) annotation Link( rect(50, 550, 200, 650), urlhttps://martin-thoma.com/, ) writer.add_annotation(page_number0, annotationannotation) writer.write(out-link.pdf)内部链接使用target_page_index加fit指定跳转方式例如Fit(fit_type/FitH, fit_args(123,))表示以指定纵向坐标适配from pypdf import PdfReader, PdfWriter from pypdf.annotations import Link from pypdf.generic import Fit reader PdfReader(crazyones.pdf) writer PdfWriter() writer.add_page(reader.pages[0]) annotation Link( rect(50, 550, 200, 650), target_page_index3, fitFit(fit_type/FitH, fit_args(123,)), ) writer.add_annotation(page_number0, annotationannotation) writer.write(out-internal-link.pdf)注意内部链接的/Dest在构造时是延迟解析的占位字典真正的目标数组由add_annotation()内部完成转换详见下文源码解析。Popup弹出窗口Popup用于为标记注释管理弹出窗口效果见 annotation-popup.png构造参数为rect、parent父注释对象和open是否默认展开。关键用法必须使用writer.add_annotation()的返回值作为parent因为该返回值才是已注册带间接引用的父注释对象Popup内部通过parent.indirect_reference写入/Parent若父对象未注册则会记录一条警告日志 Unregistered Parent object : No Parent field set。from pypdf import PdfWriter from pypdf.annotations import Popup, Text writer PdfWriter() writer.append(crazyones.pdf, [0]) text_annotation writer.add_annotation( 0, Text( textHello World\nThis is the second line!, rect(50, 550, 200, 650), openTrue, ), ) popup_annotation Popup( rect(50, 550, 200, 650), openTrue, parenttext_annotation, # 必须是 add_annotation 的返回值 ) writer.add_annotation(0, popup_annotation) writer.write(out-popup.pdf)add_annotation还做了额外处理当新增的是Popup且包含/Parent时会把 popup 的间接引用写回父注释的/Popup条目从而建立双向关联。写入注释的底层机制PdfWriter.add_annotation所有注释最终都通过 pypdf/_writer.py 的add_annotation(page_number, annotation)写入文档其流程如下接受页码整数或PageObject其他类型抛出TypeError将注释字典对象化并把当前页的间接引用写入注释的/P条目若页面还没有/Annots数组则创建随后把注释对象追加进去对内部链接的特殊处理若/Subtype为/Link且含/Dest会把构造时的占位字典转换为Destination生成真正可用的目标数组返回被插入的对象如上述 Popup 场景需要用到。该方法返回的注释对象已注册到 writer拥有indirect_reference因此可作为in_reply_to、Popup.parent等需要间接引用的参数。该方法位于 pypdf/_writer.py。读取已有注释遍历 /Annots读取注释不需要pypdf.annotations模块直接遍历页面的/Annots数组即可参考 docs/user/reading-pdf-annotations.md。PDF 2.0 定义的注释类型包括 Text、Link、FreeText、Line、Square、Circle、Polygon、PolyLine、Highlight、Underline、Squiggly、StrikeOut、Caret、Stamp、Ink、Popup、FileAttachment、Sound、Movie、Screen、Widget、PrinterMark、TrapNet、Watermark、3D、Redact、Projection、RichMedia 等读取时通过/Subtype区分from pypdf import PdfReader reader PdfReader(example.pdf) for page in reader.pages: if /Annots in page: for annotation in page[/Annots]: obj annotation.get_object() print({subtype: obj[/Subtype], location: obj[/Rect]})针对常见类型的定向读取# 读取 Text 注释内容 for page in reader.pages: if /Annots in page: for annotation in page[/Annots]: subtype annotation.get_object()[/Subtype] if subtype /Text: print(annotation.get_object()[/Contents]) # 读取 Highlight 的 QuadPoints 坐标 for page in reader.pages: if /Annots in page: for annotation in page[/Annots]: subtype annotation.get_object()[/Subtype] if subtype /Highlight: coords annotation.get_object()[/QuadPoints] x1, y1, x2, y2, x3, y3, x4, y4 coords # 读取 FileAttachment 附件 attachments {} for page in reader.pages: if /Annots in page: for annotation in page[/Annots]: subtype annotation.get_object()[/Subtype] if subtype /FileAttachment: fileobj annotation.get_object()[/FS] attachments[fileobj[/F]] fileobj[/EF][/F].get_data()注意/Annots中的条目通常是间接引用需先调用get_object()解引用再取条目。扩展性所有注释都是字典对象由于所有注释都继承自DictionaryObject你可以像操作字典一样自由扩展。最典型的场景就是 PolyLine 默认透明、需要手动设置/C颜色同理对任何注释都可以直接写入 PDF 规范支持但 pypdf 未封装的条目例如用annotation[NameObject(/C)] ArrayObject([...])统一上色或按 docs/user/adding-pdf-annotations.md 顶部的说明设置灰度 / RGB / CMYK 三种形式的颜色值。这种规范条目 字典直写的组合让pypdf.annotations在覆盖常见需求之外仍保留了对 PDF 标准的完整可触及性。小结创建注释统一走构造pypdf.annotations类 →writer.add_annotation()→ 写出三步流程需要间接引用的参数in_reply_to、Popup.parent必须使用add_annotation()的返回值注释标志通过flags属性设置AnnotationFlag枚举覆盖了 PDF 规范中的全部十种标志读取注释通过遍历页面/Annots并按/Subtype分发处理所有注释本质是字典对象超出封装范围的条目可直接写入实现深度定制。相关参考模块入口 pypdf/annotations/init.py、基类 pypdf/annotations/_base.py、标记类实现 pypdf/annotations/_markup_annotations.py、非标记类实现 pypdf/annotations/_non_markup_annotations.py、标志枚举 pypdf/constants.py、写入入口 pypdf/_writer.py。【免费下载链接】pypdfA pure-python PDF library capable of splitting, merging, cropping, and transforming the pages of PDF files项目地址: https://gitcode.com/GitHub_Trending/py/pypdf创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表