PDF 注释

PDF 注释

Page.annotations 提供了一个 AnnotationCollection —— 一个可变的、类似序列的视图,覆盖页面上的每个注释。每个条目都是一个 Annotation(或 MarkupAnnotation / LinkAnnotation 子类),并且特定子类型的数据,如四点坐标、墨水列表或颜色,都是通过 get_property() / set_property() 读取和写入,而不是使用专门的属性。


添加注释

AnnotationCollection.add(subtype, rect, contents, title, appearance_normal, properties) 创建一个新的注释并将其追加到页面。subtype 接受纯字符串("Text"、"Square"、"Highlight")或 AnnotationType 枚举成员。

import aspose_pdf
from aspose_pdf import Document, AnnotationType

doc = Document()
doc.pages.add()
page = doc.pages[0]

# Plain string subtype
page.annotations.add("Text", (100, 100, 200, 200), "Hello")

# AnnotationType enum, with subtype-specific properties
page.annotations.add(
    AnnotationType.POLYGON,
    (0, 0, 10, 10),
    "",
    properties={"Vertices": [0, 0, 10, 0, 5, 10]},
)

读取和更新属性

get_property(name, default) 读取特定子类型的值;set_property(name, value) 写入该值,并且将属性设置为 None 会从注释的 properties 字典中移除它。

doc = Document()
doc.pages.add()
page = doc.pages[0]

ann = page.annotations.add(
    "Square", (0, 0, 50, 50), "x", properties={"C": [1, 0, 0]},
)
ann.set_property("IC", [0, 0, 1])
print(page.annotations[0].get_property("IC"))  # [0, 0, 1]

ann.set_property("C", None)  # removes the "C" entry entirely
print("C" in page.annotations[0].properties)  # False

使用 AnnotationName 为注释命名

PDF 名称(例如 Stamp 注释的 Name 条目)使用 AnnotationName 明显区别于普通字符串,它是从 aspose_pdf.engine.cos 派生的 str 子类。以这种方式存储的值仍然可以与普通字符串相等比较。

from aspose_pdf.engine.cos import AnnotationName

doc = Document()
doc.pages.add()
page = doc.pages[0]

page.annotations.add(
    "Stamp", (10, 10, 110, 60), "",
    properties={"Name": AnnotationName("Approved")},
)

插入、删除和清除

insert(index, subtype, rect, contents, title, appearance_normal, properties) 在特定位置放置一个新注释;delete(index) 按索引删除注释(对超出范围的索引会抛出 IndexError);clear() 删除页面上的所有注释。

doc = Document()
doc.pages.add()
page = doc.pages[0]

page.annotations.add("Text", (0, 0, 100, 100), "A")
page.annotations.add("Text", (200, 200, 300, 300), "C")
page.annotations.insert(1, "Text", (100, 100, 200, 200), "B")
# order is now: A, B, C

page.annotations.delete(1)   # removes "B"
page.annotations.clear()     # removes everything remaining

生成注释外观

Annotation.generate_appearance(force) 为单个注释构建 /AP /N 外观流,并在存在该子类型的渲染器时返回 True;AnnotationCollection.generate_appearances(force) 在一次调用中对页面上的每个注释执行相同操作,并返回实际生成的注释数量(不支持的子类型将被跳过)。

doc = Document()
doc.pages.add()
page = doc.pages[0]

page.annotations.add("Square", (0, 0, 50, 50), "")
page.annotations.add("Circle", (60, 0, 110, 50), "")
page.annotations.add("Text", (0, 60, 20, 80), "")  # unsupported subtype -> skipped

generated = page.annotations.generate_appearances()
print(generated)  # 2

注释子类型和标志

AnnotationType 列举了标准 PDF 32000-1:2008(表 169)子类型名称:TEXT, LINK, FREE_TEXT, LINE, SQUARE, CIRCLE, POLYGON, POLY_LINE, HIGHLIGHT, UNDERLINE, SQUIGGLY, STRIKE_OUT, STAMP, CARET, INK, POPUP, FILE_ATTACHMENT, SOUND, MOVIE, WIDGET, SCREEN, PRINTER_MARK, TRAP_NET, WATERMARK 和 REDACT。

AnnotationFlags 是一个 IntFlag,涵盖注释的显示/交互行为:DEFAULT, INVISIBLE, HIDDEN, PRINT, NO_ZOOM, NO_ROTATE, NO_VIEW, READ_ONLY, LOCKED 和 TOGGLE_NO_VIEW。


预发布:3D 注释

PDF3DAnnotation、PDF3DArtwork、PDF3DContent 和 PDF3DView 对页面附加的 3D 艺术作品进行建模——一个 PDF3DAnnotation 拥有一个 rect: Rectangle、一个 artwork: PDF3DArtwork,以及一个可选的 background_color: Color。PDF3DArtwork.add_view() 注册一个 PDF3DView,每个都携带一个 render_mode(PDF3DRenderMode:SOLID、WIREFRAME、TRANSPARENT)和一个 lighting_scheme(PDF3DLightingScheme:HEADLAMP、WHITE、GRAY、DARK、CUSTOM)。该库的文档字符串将 PDF3DAnnotation 标记为 “prerelease imports 的最小注释包装器”——将此表面视为早期阶段,而非完整的 3D 创作 API。


技巧与最佳实践

  • 在值需要在代码其他地方进行比较或分支时,优先使用 AnnotationType 枚举成员,而不是原始子类型字符串。
  • 调用 set_property(name, None) 完全删除属性,而不是保留过时的值——该条目会从 properties 中彻底消失。
  • 使用 AnnotationCollection.generate_appearances 批量生成外观,而不是对每个注释循环 generate_appearance();它返回实际生成的数量,便于检测被跳过或不受支持的子类型。
  • delete() 和对 page.annotations 的索引都是从 0 开始的;在调用 delete() 之前验证索引是否来自用户输入,因为超出范围的索引会引发 IndexError。
  • 将 PDF 名称值(例如 Stamp 的 Name)用 AnnotationName 包装,使其在往返时保持为 PDF 名称而不是普通文本字符串。

常见问题

问题原因修复
generate_appearances() 返回的数量少于已添加的注释数一个或多个子类型没有内置的外观渲染器将返回计数与 len(page.annotations) 进行比较;不支持的子类型会被静默跳过,而不会报错
delete(index) 抛出 IndexError索引为负或超出当前注释计数在调用 delete() 之前检查 len(page.annotations)
使用 set_property() 设置的属性在重新加载后未出现属性被设置为 None,这会删除它而不是存储当属性应持久化时,请使用真实值,而不是 None
插入的注释位于错误的位置insert(index, ...) 索引是从插入前的集合状态计数的在循环中每次调用 insert() 后重新检查索引

FAQ

如何添加纯文本评论注释?

调用 page.annotations.add("Text", (x0, y0, x1, y1), "comment text")。四元组表示页面上注释的矩形区域。

Annotation、MarkupAnnotation 和 LinkAnnotation 有何区别?

Annotation 是页面上任意注释返回的实时视图。MarkupAnnotation 是标记式子类型(高亮、文本注释、形状)的基础,而 LinkAnnotation 则用于链接式注释;两者目前都公开与 Annotation 相同的方法/属性界面。

我可以在不删除整个注释的情况下移除单个属性吗?

是的 — 调用 annotation.set_property(name, None);注释本身保持不变,仅从 properties 中移除该条目。

如果子类型不受支持,AnnotationCollection.generate_appearances 会失败吗?

不会。它会跳过没有内置外观渲染器的子类型,并返回实际生成外观的注释数量。

3D 注释类是否已准备好用于生产环境?

PDF3DAnnotation 及相关类型被记录为预发布导入的最小包装器——在将其用于生产 3D 内容之前,请在目标 PDF 查看器中验证其行为。


API Reference 摘要

类/方法描述
AnnotationCollection.add创建并将新注释追加到页面
AnnotationCollection.insert在特定索引处创建并插入一个新注释
AnnotationCollection.delete按索引删除注释(如果超出范围则抛出 IndexError)
AnnotationCollection.clear从页面中删除所有注释
AnnotationCollection.generate_appearances为页面上每个受支持的注释生成 /AP /N 外观流
Annotation.get_property / set_property读取或写入子类型特定的属性值
Annotation.update_properties在直接属性编辑后重新计算派生状态
Annotation.generate_appearance为单个注释生成 /AP /N 外观流
AnnotationType标准 PDF 注释子类型名称的枚举
AnnotationFlags注释显示/交互行为的 IntFlag
AnnotationNamestr 子类标记一个值,以序列化为 PDF 名称
PDF3DAnnotation / PDF3DArtwork / PDF3DContent / PDF3DView预发布的 3D 注释和艺术作品模型
PDF3DRenderMode / PDF3DLightingScheme用于 3D 视图渲染模式和光照方案的枚举

另请参阅

 中文