mcpField
O
oxidize-pdf MCP Server

oxidize-pdf MCP Server

本地服务
github.com/bzsanti生产力63 天前更新

关于 oxidize-pdf MCP Server

oxidize-pdf MCP Server 通过本地 stdio 服务向 AI Agent 提供 PDF 创建、读取和编辑能力。工具涵盖文档分析与处理,并支持工作区路径限制和可配置的资源上限。

主要功能

  1. 01读取 PDF 元数据并提取文本
  2. 02创建和编辑 PDF 文档
  3. 03拆分、合并、旋转和重排页面
  4. 04管理表单、批注和安全设置
  5. 05分析结构并提取实体
  6. 06工作区沙箱与资源限制

适用场景

  1. 01提取 PDF 文本和元数据
  2. 02合并或拆分文档页面
  3. 03填写或检查 PDF 表单
  4. 04检查文档结构与合规性

MCP 相关

该服务通过 stdio 向 MCP 客户端提供 12 个 PDF 工具、资源和提示词。工具处理配置工作区内的文件,并受路径限制和资源上限约束。

配置

本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。

mcp.json · 通用配置
{
"mcpServers": {
"bzsanti-oxidize-pdf": {
"command": "uvx",
"args": [
"oxidize-pdf"
]
}
}
}
启动命令uvx oxidize-pdf

本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。

安装包
pypioxidize-pdf0.23.0

工具

12

收录的工具列表 · 更新于 3 天前

参数类型必填说明
pathstring是PDF 文件相对于已配置工作区的路径。
passwordany否用于解锁加密 PDF 的用户密码。未加密文件请省略;若加密文件未提供密码,工具会将其报告为锁定,而不是报错。
include_page_detailsboolean否为 true 时,还会返回每页的宽度、高度(以 PDF 点为单位)和旋转角度。默认关闭,以保持响应较小。

详细介绍

oxidize-pdf

Rust 核心版本固定为 5.5.0。参见兼容性与验证。

PyPI 版本 CI 许可证:MIT Python 类型标注 MCP

oxidize-python MCP 服务

由 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_MB100拒绝磁盘上大小超过此值的输入 PDF。
OXIDIZE_MAX_PAGES10000在进行任何提取操作之前,拒绝页数超过此值的文档。
OXIDIZE_MAX_OUTPUT_BYTES10485760限制工具 JSON 响应的序列化大小(10 MB)。
OXIDIZE_MAX_SESSIONS10并发有状态 PDF 创建会话的最大数量。
OXIDIZE_MAX_SESSION_BYTES10485760限制单个会话可累积的内容大小(10 MB)。
OXIDIZE_SESSION_TIMEOUT3600会话过期时间,单位为秒。

资源上限(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 会原样提取每个嵌入图像(例如,DCTDecode JPEG 会逐字节写入)。不支持图像预处理,包括自动旋转校正、对比度增强、降噪、放大和强制灰度,因为构建时排除了上游的 external-images 功能(及其 image crate 依赖)。这样可确保提取结果忠实且无损;不会悄悄返回空结果或占位结果。
  • 仅支持 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() 构建器。 所有字段均可通过属性读取。

消费者生效约定
StreamingDocumentbuffer_size 用于配置输入缓冲。page_cache_size 限制保留的页面数;memory_limit 限制保留的已解码内容和 UTF-8 文本字节数。任一缓存上限设为零都会禁用保留。memory_usage() 仅报告这些载荷。max_content_stream_size 限制解码后的页面内容总量(包括各流之间的分隔符);超出时会引发 PDF 错误,且不会推进游标。
LazyDocumentlazy_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 则允许这些注释操作。对于这些已认证的输入,读取和空批次仍可用;加密输入会被拒绝。此权限检查不会验证签名真实性。这些编辑器会保留或输出原生注释字典;但不保证生成外观流或在所有查看器中呈现相同的注释,也不会施加全局内存限制。

评论

支持 Markdown

第一次接触 MCP?

从协议是什么,到在客户端里装上第一个服务,这几条回答覆盖了大多数入门问题。

一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。