完全指南:FreeText、图形、链接与高亮)
pypdf 添加 PDF 注释Annotations完全指南FreeText、图形、链接与高亮【免费下载链接】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 官方文档 docs/user/adding-pdf-annotations.md 为骨架系统讲解如何使用pypdf在 PDF 页面中创建附件、自由文本、线段、折线、矩形、椭圆、多边形、弹出窗、内外链接与文本高亮等注释。读完本文你将掌握pypdf.annotations模块中所有公开注释类的构造参数、PdfWriter.add_annotation()的调用方式以及颜色、字体、AnnotationFlag 等底层 PDF 对象的工作原理可直接在真实项目中落地使用。核心前置知识注释字典、颜色与可见性PDF 中的注释annotation本质上是一个字典对象Dictionary Object。pypdf 的pypdf/annotations/__init__.py明确说明所有注释类型的内核都是DictionaryObject因此如果 pypdf 没有实现某个特性你可以直接像操作字典一样扩展已生成的功能。所有注释类都继承自AnnotationDictionary见 pypdf/annotations/_base.py其构造函数自动写入/Type/Annot标识这是一个注释对象默认flags为 0即没有任何标记。构造函数刻意不添加 flags如果你需要改变默认行为请使用flags属性见下文AnnotationFlag小节。颜色/C条目为什么有些注释看不见官方文档特别提醒了一个常见陷阱默认情况下某些注释可能是不可见的例如 PolyLine折线因为默认颜色是transparent透明。解决办法是显式给注释添加/C条目它是一个数组每个元素取值在0.0到1.0之间1 个元素灰度值grayscale3 个元素RGB 颜色定义4 个元素CMYK 颜色定义。例如给折线设置橙红色RGB 0.9, 0.1, 0annotation[NameObject(/C)] ArrayObject( [FloatObject(0.9), FloatObject(0.1), FloatObject(0)] )从源码看pypdf/annotations/_markup_annotations.py中的hex_to_rgb工具函数负责把00ff00这类十六进制颜色字符串解析为 RGB 浮点数组每个分量除以 255最终写入/C或/IC条目因此多数注释类FreeText、Highlight、Rectangle、Ellipse的十六进制颜色参数在底层都会被转换为/C数组。AnnotationFlag控制注释的显示与交互AnnotationFlag定义在 pypdf/constants.py对应 PDF 规范 §12.5.3 Annotation Flags是一个IntFlag支持按位组合标志值含义INVISIBLE1不显示注释除非开启显示隐藏注释选项HIDDEN2不显示也不打印PRINT4打印时显示注释NO_ZOOM8页面缩放时不缩放注释外观NO_ROTATE16页面旋转时不旋转注释外观NO_VIEW32屏幕上不显示READ_ONLY64禁止用户交互LOCKED128锁定禁止删除或修改TOGGLE_NO_VIEW256反向切换 NO_VIEWLOCKED_CONTENTS512锁定注释内容典型用法把注释标记为可打印annotation.flags AnnotationFlag.PRINT或组合多个标志如AnnotationFlag.PRINT | AnnotationFlag.READ_ONLY。flags属性定义在AnnotationDictionary上见 pypdf/annotations/_base.py读取时返回AnnotationFlag类型写入时转换为/F数值条目。将注释写入 PDF 的统一入口add_annotation()无论创建哪种注释最终都通过PdfWriter.add_annotation()写入文档。其签名与行为见 pypdf/_writer.pywriter.add_annotation(page_number, annotation) - DictionaryObject要点page_number可以传**页面索引int**或PageObject注释必须新建不能复用/回收旧注释方法会为注释自动写入/P父页面引用、把注释追加到页面的/Annots数组并通过self._add_object()注册为间接对象返回被插入的注释对象——这个返回值很重要创建 Popup 弹出窗时必须使用它见下文内部链接注释/Subtype /Link且含/Dest会被自动转换为Destination目标数组供阅读器跳转Popup 注释会自动把自身引用写入父注释的/Popup条目。测试 tests/test_annotations.py 中所有用例test_free_text、test_link、test_popup等均验证了这一调用链路。Attachments把任意文件附加到 PDF附件注释FileAttachment是 PDF 文档中嵌入文件的通用方式。官方示例from pypdf import PdfWriter writer PdfWriter() writer.add_blank_page(width200, height200) data bany bytes - typically read from a file writer.add_attachment(smile.png, data) writer.write(out-attachment.pdf)add_attachment与注释的关系在于附件在 PDF 内部以 FileAttachment 形式关联读取侧可通过/Subtype /FileAttachment的注释取回文件数据见 docs/user/reading-pdf-annotations.md 中的读取示例。writer.add_blank_page(width200, height200)用于先创建一页空白页承载附件。FreeText在矩形框中添加自由文本FreeText 注释用于在页面上放置一段富文本效果见 free-text-annotation.png。构造参数源码见 pypdf/annotations/_markup_annotations.py参数默认值说明text必填注释文本内容支持\n换行rect必填矩形区域(xLL, yLL, xUR, yUR)fontHelvetica字体名称boldFalse是否加粗italicFalse是否斜体font_size14pt字号字符串如20ptfont_color000000字体颜色十六进制如00ff00border_color000000边框颜色传None则无边框写入/BS且宽度为 0background_colorffffff背景颜色传None则透明官方完整示例from pypdf import PdfReader, PdfWriter from pypdf.annotations import FreeText from pypdf.constants import AnnotationFlag # Fill the writer with the pages you want reader PdfReader(crazyones.pdf) page reader.pages[0] writer PdfWriter() writer.add_page(page) # Create the annotation and add it 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, ) # Mark the annotation as printable. # See AnnotationFlag for other options, e.g. hidden etc. annotation.flags AnnotationFlag.PRINT writer.add_annotation(page_number0, annotationannotation) # Write the annotated file to disk writer.write(out-free-text.pdf)底层原理FreeText 构造函数把italic/bold/font_size/font/font_color拼装成 CSS2 风格的富文本字符串写入/DS条目参照 PDF 1.7 规范 Table 225 CSS2 style attributes used in rich text strings测试 tests/test_annotations.py 直接断言了生成的/DS值例如italic bold 20pt Arial;text-align:left;color:#00ff00。边框颜色经hex_to_rgb转为r g b rg字符串写入/DA默认外观border_colorNone时写入/BS且宽度为 0即无边框背景颜色写入/C数组。Text经典文本注释Text 注释是 PDF 中最常见的便签式注释形如一个小图标点击后显示文本效果见 text-annotation.png。构造参数pypdf/annotations/_markup_annotations.pyrect可点击区域、text内容、open默认False是否默认展开、flags默认 0。示例from pypdf import PdfReader, PdfWriter from pypdf.annotations import Text reader PdfReader(crazyones.pdf) writer PdfWriter() writer.add_page(reader.pages[0]) annotation Text( textHello World\nThis is the second line!, rect(50, 550, 200, 650), openTrue, ) writer.add_annotation(page_number0, annotationannotation) writer.write(out-text.pdf)底层会写入/Subtype/Text、/Rect、/Contents、/Open与/Flags条目。Line两点连线Line 注释用于绘制一条直线效果见 annotation-line.png。关键参数p1、p2两个端点坐标以及rect包围矩形、text可选说明文字默认from pypdf import PdfReader, PdfWriter from pypdf.annotations import Line reader PdfReader(crazyones.pdf) page reader.pages[0] writer PdfWriter() writer.add_page(page) # Add the line 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)源码层面pypdf/annotations/_markup_annotations.pyLine 会额外写入/L端点数组[p1.x, p1.y, p2.x, p2.y]/LE两端线帽样式默认[/None, /None]无箭头/IC默认填充色[0.5, 0.5, 0.5]中灰色可选改/Contents说明文本。PolyLine多段折线PolyLine 绘制连续折线效果见 annotation-polyline.png。注意官方文档强调默认颜色是透明的必须显式设置/C颜色from pypdf import PdfReader, PdfWriter from pypdf.annotations import PolyLine from pypdf.generic import ArrayObject, FloatObject, NameObject reader PdfReader(crazyones.pdf) page reader.pages[0] writer PdfWriter() writer.add_page(page) # Add the polyline # By default, the line will be transparent. Set an explicit color. 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)源码要点pypdf/annotations/_markup_annotations.pyvertices为空列表时抛出ValueError/Vertices按x1, y1, x2, y2, ...扁平化写入/Rect自动由_get_bounding_rectangle()根据所有顶点计算包围盒因此无需手动传rect。Rectangle矩形框Rectangle 绘制矩形效果见 annotation-square.png。其 PDF 子类型实际是/Square——pypdf 故意命名为Rectangle是因为 PDF 规范中的 Square 注释并不要求是正方形见 pypdf/annotations/init.py 模块文档from pypdf import PdfReader, PdfWriter from pypdf.annotations import Rectangle reader PdfReader(crazyones.pdf) page reader.pages[0] writer PdfWriter() writer.add_page(page) # Add the rectangle annotation Rectangle( rect(50, 550, 200, 650), ) writer.add_annotation(page_number0, annotationannotation) writer.write(out-rectangle.pdf)如需填充使用interiour_colorff0000参数源码参数名为interior_color见 pypdf/annotations/_markup_annotations.py十六进制颜色会被转换为/IC数组annotation Rectangle( rect(50, 550, 200, 650), interior_colorff0000, )Ellipse椭圆与圆Ellipse 绘制椭圆圆是长宽相等的椭圆效果见 annotation-circle.png。其 PDF 子类型为/Circlefrom pypdf import PdfReader, PdfWriter from pypdf.annotations import Ellipse reader PdfReader(crazyones.pdf) page reader.pages[0] writer PdfWriter() writer.add_page(page) annotation Ellipse( rect(50, 550, 200, 650), ) writer.add_annotation(page_number0, annotationannotation) writer.write(out-ellipse.pdf)与 Rectangle 相同也支持interior_color...填充参数写入/IC。Polygon多边形Polygon 绘制闭合多边形效果见 annotation-polygon.pngfrom pypdf import PdfReader, PdfWriter from pypdf.annotations import Polygon reader PdfReader(crazyones.pdf) page reader.pages[0] writer PdfWriter() writer.add_page(page) # Add the line annotation Polygon( vertices[(50, 550), (200, 650), (70, 750), (50, 700)], ) writer.add_annotation(page_number0, annotationannotation) writer.write(out-polygon.pdf)源码pypdf/annotations/_markup_annotations.py中 Polygon 与 PolyLine 类似空vertices抛ValueError、自动计算/Rect包围盒并额外写入/IT /PolygonCloud标注该多边形可作为云朵形状。Polygon 与 PolyLine 的区别在于闭合与否以及/IT条目。Popup弹出窗Popup 注释用于管理标记注释的弹出窗口效果见 annotation-popup.png。关键要求必须使用add_annotation()的返回值作为parent因为返回的是已注册的父注释from pypdf import PdfWriter from pypdf.annotations import Popup, Text # Arrange writer PdfWriter() writer.append(crazyones.pdf, [0]) # Act 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, # use the output of add_annotation ) writer.write(out-popup.pdf)底层行为pypdf/_writer.py 与 pypdf/annotations/_non_markup_annotations.pyPopup 构造时通过parent.indirect_reference写入/Parent条目若 parent 未注册没有indirect_reference会触发logger_warning且不设置 Parent 字段add_annotation检测到/Subtype /Popup且含/Parent时会自动把 popup 的引用写回父注释的/Popup条目建立双向关联。Link外部链接与内部跳转Link 注释支持两类目标外部 URL与文档内部页面跳转效果与常规可点击链接一致。外部链接from pypdf import PdfReader, PdfWriter from pypdf.annotations import Link reader PdfReader(crazyones.pdf) page reader.pages[0] writer PdfWriter() writer.add_page(page) # Add the link 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控制跳转后的视图from pypdf import PdfReader, PdfWriter from pypdf.annotations import Link from pypdf.generic import Fit reader PdfReader(crazyones.pdf) page reader.pages[0] writer PdfWriter() writer.add_page(page) # Add the link 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)源码要点pypdf/annotations/_non_markup_annotations.pyurl与target_page_index必须且只能提供一个否则抛ValueError外部链接写入/Aaction 字典/S/URI、/Type/Action、/URI...内部链接先把目标暂存为延迟字典由add_annotation内部结合Fit转换为真正的Destination目标数组pypdf/_writer.pyFit类pypdf/generic/_fit.py支持多种视图方式Fit(fit_type/FitH, fit_args(123,))表示以纵坐标 123 为基准水平适配此外还提供Fit.xyz()、Fit.fit()、Fit.fit_horizontally()等便捷类方法fit_args中的None会被转换为NullObject。Text Markup Annotations高亮等文本标记文本标记注释Text Markup Annotations用于标记文档中的一段具体文本比上述注释更复杂你必须知道文本的确切位置——即所谓的QuadPoints四边形点。该类注释的 PDF 子类型包括 Highlight、Underline、Squiggly、StrikeOut 等pypdf 目前公开实现的是Highlight。Highlight文本高亮效果见 annotation-highlight.pngfrom pypdf import PdfReader, PdfWriter from pypdf.annotations import Highlight from pypdf.generic import ArrayObject, FloatObject reader PdfReader(crazyones.pdf) page reader.pages[0] writer PdfWriter() writer.add_page(page) rect (50, 550, 200, 650) quad_points [rect[0], rect[1], rect[2], rect[1], rect[0], rect[3], rect[2], rect[3]] # Add the highlight annotation Highlight( rectrect, quad_pointsArrayObject([FloatObject(quad_point) for quad_point in quad_points]), ) writer.add_annotation(page_number0, annotationannotation) writer.write(out-highlight.pdf)参数说明pypdf/annotations/_markup_annotations.pyrect高亮区域的包围矩形quad_pointsArrayObject一组四边形坐标。PDF 规范要求四边形按左上、右上、左下、右下顺序给出 8 个浮点数上例用rect的坐标顺序[x1,y1, x2,y1, x1,y2, x2,y2]构造highlight_color默认ff0000红色十六进制字符串会被转换为/C颜色数组printing默认False置True时自动设置AnnotationFlag.PRINT。与读取侧配合验证与闭环创建的注释可通过 docs/user/reading-pdf-annotations.md 中的通用读取方式验证。PDF 2.0 定义了 Text、Link、FreeText、Line、Square、Circle、Polygon、PolyLine、Highlight、Underline、Squiggly、StrikeOut、Popup、FileAttachment 等二十余种注释类型读取代码按/Annots遍历即可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]})创建侧与读取侧形成闭环例如 PolyLine 写入的/C颜色数组、FreeText 写入的/DS富文本、Link 写入的/Aaction、Highlight 写入的/QuadPoints都能在读取侧以同样的字典键访问obj[/Contents]、obj[/QuadPoints]、obj[/FS]等这也是AnnotationDictionary本质是DictionaryObject的实践体现。小结所有注释统一通过PdfWriter.add_annotation(page_number, annotation)写入返回的注释对象可用于 Popup 关联pypdf 提供Text、FreeText、Line、PolyLine、Rectangle、Ellipse、Polygon、Popup、Link、Highlight十种公开注释类见 pypdf/annotations/init.py 的__all__颜色统一使用十六进制字符串如ff0000底层经hex_to_rgb转为 0.0~1.0 的浮点数组写入/C或/ICPolyLine 等默认透明必须手动设置颜色交互与显示行为通过AnnotationFlag/F条目控制如PRINT、READ_ONLY、HIDDEN文本标记注释Highlight需要精确的 QuadPoints建议结合文本提取工具先定位目标文本坐标所有示例均可在 pypdf 仓库的 tests/test_annotations.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),仅供参考