关于 oxidize-pdf MCP Server
oxidize-pdf MCP Server 通过本地 stdio 服务向 AI Agent 提供 PDF 创建、读取和编辑能力。工具涵盖文档分析与处理,并支持工作区路径限制和可配置的资源上限。
主要功能
- 01读取 PDF 元数据并提取文本
- 02创建和编辑 PDF 文档
- 03拆分、合并、旋转和重排页面
- 04管理表单、批注和安全设置
- 05分析结构并提取实体
- 06工作区沙箱与资源限制
适用场景
- 01提取 PDF 文本和元数据
- 02合并或拆分文档页面
- 03填写或检查 PDF 表单
- 04检查文档结构与合规性
MCP 相关
该服务通过 stdio 向 MCP 客户端提供 12 个 PDF 工具、资源和提示词。工具处理配置工作区内的文件,并受路径限制和资源上限约束。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
工具
12收录的工具列表 · 更新于 3 天前
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| path | string | 是 | PDF 文件相对于已配置工作区的路径。 |
| password | any | 否 | 用于解锁加密 PDF 的用户密码。未加密文件请省略;若加密文件未提供密码,工具会将其报告为锁定,而不是报错。 |
| include_page_details | boolean | 否 | 为 true 时,还会返回每页的宽度、高度(以 PDF 点为单位)和旋转角度。默认关闭,以保持响应较小。 |
详细介绍
oxidize-pdf
Rust 核心版本固定为 5.5.0。参见兼容性与验证。
由 Rust 驱动的 Python PDF 库。 以原生性能生成、解析、拆分、合并和处理 PDF。内置 MCP 服务,让 AI Agent 开箱即用地处理 PDF。
无 C 依赖。无需 Java。无需调用子进程。
安装
pip install oxidize-pdf # Core library
pip install "oxidize-pdf[mcp]" # + MCP server for AI agents
平台: Linux(x86_64、aarch64)| macOS(x86_64、Apple Silicon)| Windows(x86_64) 要求: Python 3.10+
版本 0.23.0 固定使用 Rust 核心 5.5.0,并采用基于 MIT 许可的 oxidize-webpki
RustCrypto 提供程序进行证书验证。支持的构建和限制请参阅
依赖策略。
新增 API 包括显式文本恢复、不间断空格规范化、
构建标识控制和增量签名槽。请参阅
API 指南与验证。
当前 develop 分支包含发布后的兼容性工作。有关经审计的子集、缺失的 绑定和继承自核心的限制,请参阅当前功能清单和 Python/.NET 对比。核心版本和类型存根都不能证明实现了完整的功能兼容性;我们不宣称任何全局覆盖率。
为什么选择 oxidize-pdf?
| oxidize-pdf | 纯 Python 库 | C/Java 封装 | |
|---|---|---|---|
| 性能 | 原生(编译后的 Rust) | 解释执行 | 原生,但较重 |
| 依赖项 | 无 | 因库而异 | Poppler、Java、Ghostscript |
| 内存安全 | Rust 所有权模型 | 依赖垃圾回收 | 手动管理 / 垃圾回收 |
| 类型存根 | 公共 API 清单 + develop 分支上的严格 mypy 检查 | 因库而异 | 因库而异 |
| 支持 AI(MCP) | 内置 | 否 | 否 |
MCP 服务
向 AI Agent 提供受支持的 PDF 工具:
oxidize-mcp
内置的 Model Context Protocol 服务通过标准输入/输出提供 12 个工具、5 个静态资源和一个会话资源模板以及 5 个提示词。安装 oxidize-pdf[mcp] 以包含其依赖项;基础库将 MCP 保持为可选项。
0.20 版本使用 FastMCP 4 和 MCP Python SDK 2,同时支持新版和旧版标准输入/输出客户端。版本信息和安装方式请参阅迁移指南。
如果较旧版本安装后的 tools/list 返回空数组(问题 #198),请升级到 0.20.0 或更高版本。这些版本修复了直接启动 server.py 时的工具注册问题。在 Windows 上,安装和启动时请使用相同的 Python 环境:
python -m pip install --upgrade "oxidize-pdf[mcp]"
python -m oxidize_pdf.mcp.server
对于自定义 JSON-RPC 客户端,请发送 initialize,等待其响应,然后再发送 notifications/initialized,之后才能发送 tools/list。从标准输出读取以换行符分隔的 JSON-RPC,并将标准错误输出单独处理;启动横幅会写入标准错误输出。
Claude Desktop 集成
将以下内容添加到你的 claude_desktop_config.json:
{
"mcpServers": {
"oxidize-pdf": {
"command": "oxidize-mcp",
"env": {
"OXIDIZE_WORKSPACE": "/path/to/your/pdfs"
}
}
}
}
GitHub Copilot(VS Code)集成
Copilot 的 Agent 模式支持 MCP。在工作区中添加 .vscode/mcp.json:
{
"servers": {
"oxidize-pdf": {
"command": "oxidize-mcp",
"env": {
"OXIDIZE_WORKSPACE": "/path/to/your/pdfs"
}
}
}
}
打开“聊天”视图,切换到 Agent 模式,工具选择器中就会出现 12 个 PDF 工具。(如果想全局安装,也可以将同一配置块放在用户的 settings.json 中的 mcp.servers 键下。)
OpenAI Agents SDK 集成
OpenAI Agents SDK 会通过 stdio 启动服务,并向 Agent 提供其工具:
from agents import Agent, Runner
from agents.mcp import MCPServerStdio
async with MCPServerStdio(
params={"command": "oxidize-mcp", "env": {"OXIDIZE_WORKSPACE": "/path/to/your/pdfs"}},
cache_tools_list=True,
) as server:
agent = Agent(
name="PDF assistant",
instructions="Use the oxidize-pdf tools to inspect and manipulate PDFs.",
mcp_servers=[server],
)
result = await Runner.run(agent, "How many pages does report.pdf have?")
print(result.final_output)
可运行的示例位于 examples/openai_agents_quickstart.py。
两种集成都通过 stdio 在本地运行服务,因此其工具会操作已配置工作区目录中的 PDF。此软件包不提供托管的 HTTP 端点。ChatGPT 网页版需要远程连接;开发者模式测试也可以通过 Secure MCP Tunnel 连接本地 stdio 服务。本次迁移未发布 ChatGPT 插件。
可用工具
| 工具 | 功能 |
|---|---|
read_pdf | 读取元数据——页数、版本、加密状态、标题、作者 |
extract_text | 提取所有页面或指定页面的文本 |
convert_pdf | 转换为 Markdown、分块内容或针对 RAG 优化的格式 |
create_pdf | 创建新的 PDF,可选设置元数据 |
save_pdf | 将会话保存到磁盘,可选加密 |
add_content | 向会话添加页面、文本和图形 |
annotate_pdf | 添加文本注释和高亮 |
manipulate_pdf | 拆分、合并、旋转、提取页面、反转、叠加 |
manage_forms | 创建、填写、读取和验证表单字段 |
secure_pdf | 加密、检查权限、验证签名 |
extract_entities | 从页面提取结构化实体 |
analyze_pdf | 验证结构、检测损坏、检查是否符合 PDF/A 标准 |
该服务还提供资源(会话数据、功能、版本信息)和提示词(用于摘要、数据提取、填写表单等的引导式工作流)。
配置
OXIDIZE_WORKSPACE=/path/to/pdfs oxidize-mcp
服务完全通过环境变量进行配置:
| 变量 | 默认值 | 用途 |
|---|---|---|
OXIDIZE_WORKSPACE | ~/Documents/oxidize-mcp | 沙盒根目录;所有路径都必须解析到此目录内。 |
OXIDIZE_ALLOWED_PATHS | (none) | 允许在工作区之外访问的额外目录,多个目录以逗号分隔。 |
OXIDIZE_MAX_FILE_SIZE_MB | 100 | 拒绝磁盘上大小超过此值的输入 PDF。 |
OXIDIZE_MAX_PAGES | 10000 | 在进行任何提取操作之前,拒绝页数超过此值的文档。 |
OXIDIZE_MAX_OUTPUT_BYTES | 10485760 | 限制工具 JSON 响应的序列化大小(10 MB)。 |
OXIDIZE_MAX_SESSIONS | 10 | 并发有状态 PDF 创建会话的最大数量。 |
OXIDIZE_MAX_SESSION_BYTES | 10485760 | 限制单个会话可累积的内容大小(10 MB)。 |
OXIDIZE_SESSION_TIMEOUT | 3600 | 会话过期时间,单位为秒。 |
资源上限(OXIDIZE_MAX_*)可保护服务免受大型或恶意 PDF 的影响:超大文档会在处理开始前被拒绝,工具响应也会受到限制,而不会无限制地序列化。超出上限时会返回错误,错误代码为 RESOURCE_LIMIT。
或者以编程方式启动:
from oxidize_pdf.mcp.server import run
run()
Python API
创建 PDF
from oxidize_pdf import Document, Page, Font, Color
doc = Document()
doc.set_title("My Document")
doc.set_author("Jane Doe")
page = Page.a4()
page.set_font(Font.HELVETICA, 24.0)
page.set_text_color(Color.black())
page.text_at(72.0, 750.0, "Hello from oxidize-pdf!")
page.set_font(Font.TIMES_ROMAN, 12.0)
page.text_at(72.0, 700.0, "Generated with Python + Rust.")
doc.add_page(page)
doc.save("output.pdf")
解析现有 PDF
from oxidize_pdf import PdfReader
reader = PdfReader.open("document.pdf")
print(f"Pages: {reader.page_count}, Version: {reader.version}")
for i, text in enumerate(reader.extract_text()):
print(f"--- Page {i + 1} ---")
print(text)
检查带标签的 PDF 结构
from pathlib import Path
from oxidize_pdf import TaggedPdfLimits, TaggedPdfValidationOptions, validate_tagged_pdf
report = validate_tagged_pdf(
Path("input.pdf").read_bytes(),
TaggedPdfValidationOptions(TaggedPdfLimits(max_depth=64)),
)
for finding in report.findings:
print(finding.severity, finding.code, finding.path, finding.message)
报告包含结构元素、伪影、角色/类别映射、父级数字树和内容 MCID。原始字典值仍保留为带类型的 PdfValue 对象,包括未知属性和引用代数。结果为不可变快照;返回的列表和字典均为独立副本。
valid 表示核心验证器未发现错误级别的问题。它不是 PDF/UA 认证:未加标签的 PDF 可能会有 tagged=False、valid=True,并附带 MissingStructureTree 警告。格式错误的输入会引发 PdfParseError,受锁定的输入会引发 PdfEncryptionError,超出验证预算则会引发 PdfError。
TaggedPdfLimits 限制已访问对象数、遍历深度、条目数和解码后的内容字节数(默认值:100,000;256;1,000,000;256 MiB)。这些是核心验证器的预算,并非进程内存或运行时间限制:输入复制和初始 PDF 解析发生在遍历之前。不会修改任何 PDF 字节。
操作
from oxidize_pdf import split_pdf, merge_pdfs, rotate_pdf, extract_pages
split_pdf("input.pdf", "output_dir/") # Split into individual pages
merge_pdfs(["part1.pdf", "part2.pdf"], "merged.pdf") # Merge multiple PDFs
rotate_pdf("input.pdf", "rotated.pdf", 90) # Rotate all pages
extract_pages("input.pdf", "subset.pdf", [0, 2, 4]) # Extract specific pages
图形
from oxidize_pdf import Document, Page, Color
doc = Document()
page = Page.a4()
page.set_fill_color(Color.hex("#3498db"))
page.draw_rect(72.0, 700.0, 200.0, 100.0)
page.fill()
page.set_stroke_color(Color.red())
page.set_line_width(2.0)
page.draw_circle(300.0, 500.0, 50.0)
page.stroke()
doc.add_page(page)
doc.save("graphics.pdf")
类型
from oxidize_pdf import Color, Point, Rectangle, Margins, Font
# Colors
Color.rgb(1.0, 0.0, 0.0) # RGB
Color.hex("#ff6600") # Hex
Color.cmyk(0.0, 1.0, 1.0, 0.0) # CMYK
# Geometry
Point(72.0, 720.0)
Rectangle.from_xywh(72.0, 72.0, 468.0, 648.0)
Margins.uniform(72.0)
# Fonts — all 14 standard PDF fonts
Font.HELVETICA # Font.HELVETICA_BOLD
Font.TIMES_ROMAN # Font.TIMES_BOLD
Font.COURIER # Font.COURIER_BOLD
错误处理
from oxidize_pdf import PdfReader, PdfError, PdfIoError, PdfParseError
try:
reader = PdfReader.open("missing.pdf")
except PdfIoError as e:
print(f"I/O error: {e}")
except PdfParseError as e:
print(f"Parse error: {e}")
except PdfError as e:
print(f"PDF error: {e}")
异常层级:PdfError > PdfIoError、PdfParseError、PdfEncryptionError、PdfPermissionError
MCP 服务
oxidize-pdf 包含一个 MCP 服务,可向 Claude 等 AI 助手提供 PDF 功能。使用 mcp 附加组件安装:
pip install oxidize-pdf[mcp]
Claude Desktop
将以下内容添加到 claude_desktop_config.json:
{
"mcpServers": {
"oxidize-pdf": {
"command": "uvx",
"args": ["--from", "oxidize-pdf[mcp]", "oxidize-mcp"]
}
}
}
Claude Code
claude mcp add oxidize-pdf -- uvx --from "oxidize-pdf[mcp]" oxidize-mcp
可用工具
| 工具 | 描述 |
|---|---|
read_pdf | 打开 PDF 并获取元数据(页数、版本、加密信息) |
extract_text | 从 PDF 页面提取文本内容 |
convert_pdf | 转换 PDF 版本 |
analyze_pdf | 分析结构、字体、图像和合规性 |
extract_entities | 提取图像和数字签名 |
manipulate_pdf | 拆分、合并、旋转、提取和重新排序页面 |
annotate_pdf | 添加文本注释、高亮和印章 |
manage_forms | 创建、填写和读取 PDF 表单字段 |
secure_pdf | 加密、解密并设置文档权限 |
create_pdf | 创建包含页面的新 PDF 文档 |
add_pdf_content | 向页面添加文本、形状和图像 |
save_pdf | 将文档保存到文件或字节数据 |
资源
oxidize://fonts— 可用的内置 PDF 字体oxidize://page-sizes— 标准页面尺寸及其规格oxidize://capabilities— 服务功能和工具列表oxidize://version— 版本信息oxidize://workspace— 工作区目录中的 PDF 文件oxidize://session/{id}— 按 ID 查询会话数据
已知限制
- 加密写入支持:
Document.encrypt()会配置加密参数,但底层 Rust 库尚不能将加密字典序列化到 PDF 输出中。读取加密 PDF 可正常工作。 - 图像提取会返回嵌入的原始流:
extract_images_from_pdf会原样提取每个嵌入图像(例如,DCTDecodeJPEG 会逐字节写入)。不支持图像预处理,包括自动旋转校正、对比度增强、降噪、放大和强制灰度,因为构建时排除了上游的external-images功能(及其imagecrate 依赖)。这样可确保提取结果忠实且无损;不会悄悄返回空结果或占位结果。 - 仅支持 CPython:不支持 PyPy 和 GraalPy。
许可证
MIT — 详情请参阅 LICENSE。
使用修订历史进行语义比较
from oxidize_pdf import (
compare_pdfs_semantically, SemanticComparisonLimits, SemanticComparisonOptions,
)
options = SemanticComparisonOptions(SemanticComparisonLimits(
max_objects=100_000, max_depth=256,
max_decoded_stream_bytes=256 * 1024 * 1024,
max_canonical_bytes=256 * 1024 * 1024,
max_extracted_text_bytes=64 * 1024 * 1024,
max_unreachable_objects=10_000, max_revisions=1024,
))
result = compare_pdfs_semantically(original_bytes, revised_bytes, options)
for difference in result.differences:
print(difference.path, difference.class_, difference.description)
for revision in result.right_revisions:
print(revision.index, revision.xref_offset, revision.semantic_fingerprint.hex())
for change in revision.object_changes:
print(change.object_number, change.generation, change.kind)
不可变结果会公开两份修订历史、不可达对象标识,以及语义等价的对象对。集合属性会返回独立快照。修订指纹为 32 字节哈希,修订按从旧到新的顺序排列。class_ 是 Rust 字段 class 在 Python 中的写法。差异类别包括 Visual、Textual、Structural、Metadata、Security 和 SerializationOnly。如果唯一差异是 SerializationOnly,semantically_equal 仍可能为 true;它会纳入受支持的历史语义,因此恢复到最新状态并不会抹去期间发生的语义变化。
每份文档分别受限额约束。解码/规范化限额统计重复遍历中的工作量,而不只是唯一输入字节数。初始输入复制、解析和部分提取分配发生在这些检查之前;这些选项并非进程级内存/时间限额。输入格式错误、超出限额和不支持的解码方式都会引发 PDF 错误;加密输入会引发 PdfPermissionError,必须先解密才能进行比较。
此 API 比较核心支持的对象/文本范围,不比较渲染像素,也不会认证签名或不可逆涂黑。Core 5.4.2 会将 Root/Info 无法到达的对象中的物理 ObjStm/XRef 容器也纳入。因而,将 PDF 转换为对象流的 qpdf 操作,即使文本和页面内容相同,也可能在 /UnreachableObjects 和 /Revisions 处报告 Structural 差异。绑定会保留这一保守结果;它不保证所有 PDF 布局之间都等价。现有的 compare_pdfs API 仍然可用。
从像素缓冲区创建图像
from oxidize_pdf import Image, ImageColorSpace
rgb = Image.from_raw_data(b'\xff\x00\x00', 1, 1, ImageColorSpace.DeviceRGB)
gray = Image.from_gray_data(b'\x80', 1, 1)
rgba = Image.from_rgba_data(b'\xff\x00\x00\x80', 1, 1)
assert rgba.alpha_data == b'\x80'
assert rgba.soft_mask.data == b'\x80'
from_raw_data(data, width, height, color_space, bits_per_component=8) 支持 DeviceGray、DeviceRGB 和 DeviceCMYK。分量按像素顺序打包,每个分量的最高有效位在前,每条扫描线填充至整字节。支持的分量位深为 1、2、4、8 和 16;16 位分量采用大端字节序。RGBA 和灰度便捷构造函数使用 8 位分量。
这三个构造函数都要求尺寸为正数,且缓冲区长度必须精确匹配。无效尺寸/位深会引发 ValueError;超出无符号参数范围的值会引发 OverflowError。RGBA/灰度构造函数会在调用已发布核心的尺寸运算之前,拒绝乘积超过 u32::MAX 的情况。验证会在复制像素缓冲区之前进行。
data、format、bits_per_component、alpha_data 和 soft_mask 会公开自有快照。ImageFormat 用于区分 Jpeg、Png、Tiff 和 Raw。Data 是核心的图像载荷:JPEG 会保留编码形式,而 PNG/TIFF 构造函数会公开解码后的像素数据。RGBA 会将 RGB 数据与 alpha 分开;可以单独检查其软掩码。这些 API 使用现有核心,不需要 external-images 或新增依赖。本次更新暂不包含显式创建/应用掩码以及导出写入器对象。
新页面上的标记内容
Page.begin_marked_content(tag) 和
Page.begin_marked_content_with_actual_text(tag, actual_text) 会返回页面局部的 MCID。每次 begin 都必须配对调用 Page.end_marked_content();可通过 next_mcid() 和 marked_content_depth() 检查状态。标签必须是非空字面名称(UTF-8 字节会按 PDF 名称规则转义,因此应传入 "Span",而不是 "/Span")。空标签会引发 ValueError,且不会改变状态;结束空栈会引发 PdfError。
page = Page.a4()
page.set_font(Font.HELVETICA, 12)
mcid = page.begin_marked_content_with_actual_text("Span", "café 😀")
page.text_at(40, 700, "visible glyphs")
page.end_marked_content()
ActualText 是 Unicode 逻辑替代文本;复制/提取效果取决于查看器是否支持它。对于带标签的结构,请使用 StructureElement.add_mcid(page_index, mcid) 附加返回的 ID,并通过 Document.set_struct_tree(tree) 安装结构树。仅执行这些操作并不能认证 PDF/UA。核心会在文本上下文中写入这些标记;它们适用于文本操作,不适用于围绕单独缓冲的图形内容建立通用作用域。
坐标变换
TransformMatrix(a, b, c, d, e, f) 提供不可变的仿射分量、
IDENTITY、translate、scale、rotate(弧度)、flip_y、multiply、
transform_point(Point) 和 to_pdf_ctm()。组合操作 a.multiply(b) 会先应用
b,再应用 a。数值保留核心的浮点语义,包括奇异矩阵和非有限矩阵;在 PDF 中
使用 CTM 前,请先验证这些值。
from oxidize_pdf import CoordinateSystem, Point, TransformMatrix
matrix = TransformMatrix.translate(10, 20).multiply(TransformMatrix.scale(2, 3))
point = matrix.transform_point(Point(4, 5)) # Point(18, 35)
custom = CoordinateSystem.custom(matrix)
pdf_point = custom.to_pdf_standard(Point(4, 5), 800) # Point(18, 35)
CoordinateSystem 支持将点、仅 Y 坐标和矩阵转换为 PDF 坐标,并提供
grows_upward()。对于自定义坐标系,仅 Y 坐标转换时使用 x=0,方向测试使用
d > 0,且忽略页面高度。Page.coordinate_system 返回已存储系统的快照。在
core 5.4.2 中,通过 set_coordinate_system 设置此属性不会自动变换页面内容;
请显式转换位置。LayoutManager 仍不属于此次绑定增量的范围。
RenderContext(coordinate_system, page_width, page_height) 存储可变的
coordinate_system、page_width、page_height 和 current_transform 字段。
其 to_pdf_standard(Point) 和 y_to_pdf(y) 方法使用当前坐标系和高度。矩阵根据
构造函数传入的坐标系和高度初始化;之后对字段的赋值不会重新计算矩阵,转换操作
也不会读取矩阵。此行为与 core 5.4.2 保持一致。Getter 返回独立的值快照。若要
显式应用已存储的矩阵,请使用
context.current_transform.transform_point(point)。数值字段保留核心的 f64 语义。
Page.content_width 和 Page.content_height 会减去页面边距。
Page.content_area 返回未旋转 PDF 坐标中的 (left, bottom, right, top) 边界,
而非位置和尺寸。这些只读属性反映当前边距;当边距超出页面尺寸时,值可能为负数
或边界顺序颠倒。Page.effective_width 和 Page.effective_height 会反映旋转:
90°/270° 时宽度和高度互换。它们使用页面完整尺寸,不减去边距。设置坐标系不会
改变这些几何属性。
自定义裁剪路径
from oxidize_pdf import ClippingPath, WindingRule
clip = (ClippingPath().add_rect(10, 10, 80, 80)
.add_polygon([(30, 30), (70, 30), (50, 70)])
.with_winding_rule(WindingRule.EvenOdd))
page.set_clipping_path(clip)
move_to、line_to、curve_to、close_path、add_rect、add_circle、
add_ellipse、add_rounded_rect 和 add_polygon 会修改并返回同一路径。
with_winding_rule 返回独立副本;默认规则为 NonZero。clear() 会移除命令,
但保留规则。is_empty 用于报告是否存在命令。Page.set_clipping_path 会保留
副本。
commands 返回由调用方持有的 ClippingPathCommand 快照,其 kind 为只读,
coordinates 为副本:移动和直线命令使用 [x, y],三次曲线使用两个控制点和一个
终点 [x1, y1, x2, y2, x3, y3],矩形使用 [x, y, width, height],关闭命令
不包含坐标。坐标为 PDF 用户空间中的值;调用方需提供有效的有限几何数据。
to_pdf_operations() 会输出核心的裁剪运算符,包括 W/W* 和 n。
现有的 rect、circle 和 ellipse 工厂仍可使用。
此 API 不支持路径求交、文本裁剪或裁剪堆栈。
线条虚线样式
LineDashPattern.dashed(dash_length, gap_length) 和
LineDashPattern.dotted(dot_size, gap_size) 会创建相位为零的双长度样式。两者
使用相同的 PDF 虚线表示;点的外观取决于长度和线帽,工厂不会选择圆形线帽。将
样式传递给 page.set_dash_pattern(pattern);使用 solid() 可重置为连续线条。
array 返回列表副本,phase 为只读。to_pdf_string() 返回核心表示形式
(不含 d 运算符),并四舍五入到两位小数。核心会将非有限分量序列化为零,
并将空数组序列化为 [] 0,而不考虑存储的相位。负值保持不变;此 API 不会验证
任意样式是否合法的 PDF。请使用非负长度;对于非空样式,至少一个长度必须为正,
且相位必须非负。
混合模式
已发布的 core 5.4.2 中的全部 16 种模式均可通过 BlendMode 使用。
除了现有模式外,还可以在绘制前使用 page.set_blend_mode(mode) 设置 DIFFERENCE、EXCLUSION、HUE、
SATURATION、COLOR 或 LUMINOSITY。mode.pdf_name() 会返回 PDF 名称(例如 "Color"),而
repr(mode) 使用 Python 常量名称。这些不可变值用于配置页面的扩展图形状态;使用 BlendMode.NORMAL 重置混合模式。
颜色转换与检查
color.to_rgb() 和 color.to_cmyk() 会返回独立的 Color 值。
cmyk_components() 会返回四个转换后的分量,包括输入为 RGB/Gray 时的情况。对于非 CMYK
颜色,现有的 c、m、y、k 属性仍会返回零。is_rgb()、is_cmyk()、is_gray() 用于检查存储的变体,而
color_space_name() 会返回 DeviceRGB、DeviceCMYK 或 DeviceGray。
现有的 color_space 属性仍使用较短的名称。
转换使用已发布的 core 5.5.0 中的近似公式,不使用 ICC
配置文件或校准后的色彩管理。灰度会转换为各分量相等的 RGB,或仅含黑色油墨的
CMYK。RGB 黑色会转换为 (0, 0, 0, 1)。构造函数会将有限的超出范围值和无穷值钳制到范围内;NaN 可能会在转换过程中传播。
为确保颜色可预测,调用方应提供有限的分量。
流式处理和延迟文档选项
Core 5.5.0 提供了两个独立的消费者,并支持不可变选项和配置快照。
现有调用(例如 LazyDocument.open(path))仍然有效。
from oxidize_pdf import LazyDocument, MemoryOptions, StreamingDocument, StreamingOptions
options = StreamingOptions(page_cache_size=2, memory_limit=2 * 1024 * 1024,
max_content_stream_size=1024 * 1024)
with StreamingDocument.open("input.pdf", options) as document:
configuration = document.configuration()
while (page := document.next_page()) is not None:
print(page.number, page.extract_text_streaming())
print(document.cached_pages(), document.memory_usage())
document.clear_cache()
with LazyDocument.open("input.pdf", MemoryOptions(cache_size=20)) as document:
page = document.get_page(0)
print(page.width, document.memory_stats().cached_objects)
print(document.configuration().unsupported)
StreamingOptions 接受 buffer_size、page_cache_size、
max_content_stream_size、progressive_hints 和 memory_limit 作为关键字
参数。其 minimal_memory() 和 fast_processing() 预设,以及
with_buffer_size()、with_page_cache_size() 和 with_memory_limit() 构建器
都会返回独立的值。MemoryOptions 接受 lazy_loading、
memory_mapping、cache_size、streaming、buffer_size 和 mmap_threshold;
可使用 small_file()/large_file(),或其 with_lazy_loading()、
with_memory_mapping()、with_cache_size() 和 with_streaming() 构建器。
所有字段均可通过属性读取。
| 消费者 | 生效约定 |
|---|---|
StreamingDocument | buffer_size 用于配置输入缓冲。page_cache_size 限制保留的页面数;memory_limit 限制保留的已解码内容和 UTF-8 文本字节数。任一缓存上限设为零都会禁用保留。memory_usage() 仅报告这些载荷。max_content_stream_size 限制解码后的页面内容总量(包括各流之间的分隔符);超出时会引发 PDF 错误,且不会推进游标。 |
LazyDocument | lazy_loading=False 会预先读取已索引对象。cache_size 限制适配器 LRU 缓存中的对象数;设为零会禁用缓存。get_object(number, generation=0)、get_page(index) 和 preload_page(index) 使用原生消费者。buffer_size 用于配置输入缓冲。 |
configuration() 返回自有的 requested 和 effective 快照。零缓冲区大小
会规范化为 1 字节。流式处理的渐进式提示为预留选项:
effective.progressive_hints 为 false,且如果请求了该提示,
progressive_hints_unsupported 会报告这一情况。此适配器不支持延迟内存映射和流式处理:
unsupported 会列出 UnsupportedMemoryOption.MemoryMapping 和/或 .Streaming,
有效标志为 false,且有效映射阈值为平台的最大 usize。这些标志不会启用相应模式。
这些限制不会限制总 RAM、解析器缓存、提取过程工作内存、
调用方保留的值或峰值分配。延迟文档的 memory_stats() 会报告适配器缓存的命中数、
未命中数和保留的对象数;allocated_bytes、
lazy_loads 和 mapped_regions 是未经检测的计数器,并非进程内存的测量值。clear_cache() 会清除适配器保留的数据,不会清除调用方持有的值。
原生 StreamingDocument 页面使用未旋转的 MediaBox 几何信息。现有
PageStreamer 保留旋转后的几何信息和寻址行为;其选项属于不同的 API。LazyDocument.get_page() 返回带有效旋转信息的 ParsedPage。关闭任一文档后,页面和快照仍可使用;
需要访问已关闭文档的操作会引发 ValueError。两个消费者都不接受加密文档;
必要时请通过 reader API 解锁。
增量式标记结构编辑
IncrementalTaggedPdfEditor(pdf) 持有不可变的字节快照。
TaggedPdfMutation 支持 create_element、set_element_attribute(省略
value 即可移除属性)、reparent_element、associate_mcid 和
set_parent_tree_entry。输入使用 TaggedPdfObjectRef(编号和代次)
以及无损的 PdfValue 对象。plan(mutations) 返回生效的变更和已更改的对象标识;
apply(mutations) 返回 pdf_bytes、计划以及修订前后的验证报告。两者都会按顺序
执行变更。先更改再恢复的操作会保留这两项生效操作。
from oxidize_pdf import IncrementalTaggedPdfEditor, TaggedPdfMutation, TaggedPdfObjectRef, PdfValue
editor = IncrementalTaggedPdfEditor(source_bytes)
mutation = TaggedPdfMutation.set_element_attribute(
TaggedPdfObjectRef(6, 7), "T", PdfValue.string(b"New title"))
result = editor.apply([mutation])
assert result.pdf_bytes.startswith(source_bytes)
对象引用必须指向实际源 PDF 中的结构元素。 编辑器和返回的快照彼此独立;批次失败不会更改编辑器。 空的/无操作的普通编辑会原样保留源字节。应用的修订会保留完全一致的源前缀,包括 xref 流源文件。
with_validation_options() 接受 TaggedPdfValidationOptions 及其限制。
preflight() 会为检查进行明确无歧义的派生索引恢复;
不会准备或修改编辑器。with_recovered_indexes() 会准备一个具有已恢复索引/页面键的独立编辑器。with_document_language()
会补充缺失的语言;空白值和已有的非空语言会被拒绝。恢复操作不会凭空生成无障碍描述或语言。
每次操作都会按顺序重放准备构建器。
preparation_objects() 会单独列出它们的更改(包括 xref 流对象),与 plan().changed_objects 区分。
只有在完整结果通过验证(包括空变更批次)时,已准备的编辑器才会返回字节。
必须明确提供缺失的描述;准备过程会拒绝覆盖已有描述。
普通编辑器会返回诊断报告,也可能返回结构无效的结果。报告不代表 PDF/UA 合规认证。
核心会在规划、应用和准备过程中强制执行加密输入和 DocMDP P1/P2/P3 限制。验证限制只限制其所描述的验证工作,不限制所有解析、复制或准备工作。此编辑器不代表会进行签名验证、OCR 识别,也不设定全局内存上限。
增量文本便笺和高亮
IncrementalTextNoteEditor(pdf) 会读取 notes(),并应用由
TextNoteMutation.add(page_index, position, contents)、.update(id, position, contents) 和 .remove(id) 构成的批次。position 是 PDF 坐标中的 (x, y) 对。
新便笺的矩形为 20 × 20 点;更新会保留现有矩形的宽度、高度和其他字典键。内容必须包含非空白文本,并支持 Unicode。TextNoteUpdate.added_notes 仅包含新添加的便笺,顺序与请求一致;要获取完整便笺集,请重新读取结果。
IncrementalHighlightEditor(pdf) 会读取 highlights(),并接受
HighlightMutation.add(page_index, quadrilaterals, color, opacity=None, contents=None) 和 .remove(id)。HighlightUpdate.highlights 包含按页面和标识排序的完整当前集合。原生 API 不支持更新高亮。
from oxidize_pdf import (
IncrementalTextNoteEditor, TextNoteMutation,
IncrementalHighlightEditor, HighlightMutation,
HighlightQuad, HighlightColor, HighlightOpacity,
)
notes = IncrementalTextNoteEditor(source_bytes).apply([
TextNoteMutation.add(0, (40., 50.), "Revisar: café 世界"),
])
quad = HighlightQuad([(40., 90.), (100., 90.), (40., 70.), (100., 70.)])
result = IncrementalHighlightEditor(notes.pdf_bytes).apply([
HighlightMutation.add(0, [quad], HighlightColor([1., 1., 0.]),
HighlightOpacity(.5), "Revisar"),
])
output_bytes = result.pdf_bytes
HighlightQuad 必须恰好包含 4 个 (x, y) 对,顺序遵循 PDF QuadPoints:先是一个边的起点和终点,再是对边的起点和终点。核心会拒绝非有限、重复、交叉、凹形和退化的几何形状。HighlightColor 必须恰好接受 3 个有限的 DeviceRGB 分量,且值在 [0, 1] 范围内;HighlightOpacity 接受 [0, 1] 范围内的有限值。其属性会返回自有值。高亮至少需要一个四边形。可选内容会保留 None 与空字符串的区别;省略透明度与显式设为零也有所不同。
页面索引从零开始。坐标必须位于页面的有效 CropBox(若不存在,则为 MediaBox)范围内,且使用未旋转的 PDF 坐标;继承的边界也会得到遵守。标识使用 TextNoteId/HighlightId,包含对象号和代号。在批次中,若目标属于另一子类型、标识不存在,或同一目标被指定两次,则会引发 PDF 错误。直接作为目标的注释字典以及跨页面共享的注释不受支持,因为它们缺乏明确且稳定的标识。
编辑器拥有不可变的源快照。对于成功的非空批次,结果会将源内容保留为精确的字节前缀,并追加一个修订版;失败的批次不会更改源或编辑器。空批次会精确保留原始字节。便笺、高亮、变更和结果快照均提供只读属性。这些 API 会在内存中追加修订版;移除操作会解除引用,因此历史注释字节仍会保留在文件中。它们不提供不可逆的涂黑功能。
Core 5.5.0 会对非空批次强制执行 DocMDP:P1/P2 会拒绝编辑,P3 则允许这些注释操作。对于这些已认证的输入,读取和空批次仍可用;加密输入会被拒绝。此权限检查不会验证签名真实性。这些编辑器会保留或输出原生注释字典;但不保证生成外观流或在所有查看器中呈现相同的注释,也不会施加全局内存限制。
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。