oxidize-pdf MCP Server について
oxidize-pdf MCP Serverは、ローカルのstdioサーバーを通じて、AIエージェントにPDFの作成、読み取り、編集機能を提供します。文書分析や加工のツールに加え、ワークスペースのパス制限と設定可能なリソース上限を備えています。
主な機能
- 01PDFのメタデータ読み取りとテキスト抽出
- 02PDF文書の作成と編集
- 03ページの分割、結合、回転、並べ替え
- 04フォーム、注釈、セキュリティの管理
- 05構造分析とエンティティ抽出
- 06ワークスペースのサンドボックスとリソース上限
活用シーン
- 01PDFからテキストとメタデータを抽出
- 02文書ページの結合や分割
- 03PDFフォームの入力や確認
- 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 エージェントはすぐに 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 の所有権モデル | GC に依存 | 手動管理/GC |
| 型スタブ | 公開 API 一覧 + develop での厳格な mypy チェック | さまざま | さまざま |
| AI 対応(MCP) | 組み込み | なし | なし |
MCP サーバー
対応する PDF ツールを AI エージェントに公開します。
oxidize-mcp
組み込みのModel Context Protocolサーバーは、stdio 経由で 12 個のツール、5 個の静的リソースとセッションリソーステンプレート、5 個のプロンプトを公開します。依存関係を含めるには oxidize-pdf[mcp] をインストールしてください。基本ライブラリでは MCP はオプションです。
バージョン 0.20 では FastMCP 4 と MCP Python SDK 2 を使用し、新旧両方の stdio クライアントに対応しています。バージョンとインストール方法については、移行ガイドを参照してください。
古いバージョンをインストールしていて tools/list が空の配列を返す場合(issue #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 を送信して応答を待ち、その後 tools/list の前に notifications/initialized を送信してください。改行区切りの JSON-RPC を stdout から読み取り、stderr は分けてください。起動時のバナーは stderr に書き込まれます。
Claude Desktop との連携
claude_desktop_config.json に追加します。
{
"mcpServers": {
"oxidize-pdf": {
"command": "oxidize-mcp",
"env": {
"OXIDIZE_WORKSPACE": "/path/to/your/pdfs"
}
}
}
}
GitHub Copilot(VS Code)との統合
Copilot のエージェントモードは 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 経由でサーバーを起動し、そのツールをエージェントに公開します。
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 ウェブにはリモート接続が必要です。開発者モードでのテストには、ローカルの stdio サーバーと Secure MCP Tunnel を使用することもできます。この移行によって 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 | 1 つのセッションに蓄積できるコンテンツ量を制限します(10 MB)。 |
OXIDIZE_SESSION_TIMEOUT | 3600 | セッションの有効期限(秒)です。 |
リソース上限(OXIDIZE_MAX_*)は、大きな PDF や悪意のある 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 には、Claude などの AI アシスタントに PDF の機能を提供する MCP サーバーが含まれています。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クレート依存関係)をビルドから除外しているため、画像の前処理(自動回転補正、コントラスト強調、ノイズ除去、アップスケーリング、強制グレースケール)は利用できません。これにより、忠実性と可逆性を保った抽出が可能になり、結果を暗黙的に空にしたり、スタブにしたりすることはありません。 - 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コンテナーも含まれます。そのため、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ビットのコンポーネントを使用します。
3つのコンストラクターすべてで、正の寸法と正確なバッファー長が必要です。サイズ/深度が無効な場合はValueError、符号なし引数の範囲外の値はOverflowErrorが発生します。RGBA/グレーのコンストラクターは、公開済みコアの寸法演算を呼び出す前に、積がu32::MAXを超えていないかを確認します。検証はピクセルバッファーのコピー前に行われます。
data、format、bits_per_component、alpha_data、soft_maskは、所有されたスナップショットを公開します。ImageFormatはJpeg、Png、Tiff、Rawを区別します。データはコアの画像ペイロードです。JPEGはエンコードされたままですが、PNG/TIFFのコンストラクターはデコード済みのピクセルデータを公開します。RGBAではRGBデータとアルファが分離され、ソフトマスクは個別に検査できます。これらのAPIは、external-imagesや新たな依存関係を使わず、既存のコアを使用します。マスクの明示的な作成/適用と、writerオブジェクトへのエクスポートは、引き続き今回の変更の対象外です。
新しいページのマーク付きコンテンツ
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の論理的な置換テキストです。コピー/抽出が機能するかどうかは、ビューアーがこれを尊重するかによります。タグ付き構造では、返されたIDをStructureElement.add_mcid(page_index, mcid)で関連付け、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 を適用します。値は、特異行列や非有限行列を含め、コアの浮動小数点数のセマンティクスを維持します。CTM を PDF で使用する前に、値を検証してください。
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 は、保存されている座標系のスナップショットを返します。コア 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) メソッドは、現在の座標系と高さを使用します。行列はコンストラクターの座標系と高さから初期化されます。後からフィールドを代入しても再計算されず、変換でも読み取られません。これはコア 5.4.2 の動作を維持するためです。ゲッターは独立した値のスナップショットを返します。保存されている行列を明示的に適用する場合は、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 は、読み取り専用の kind とコピーされた coordinates を持つ、所有権付きの ClippingPathCommand スナップショットを返します。move/line は [x, y]、3 次ベジェ曲線は 2 つの制御点と終点 [x1, y1, x2, y2, x3, y3]、矩形は [x, y, width, height] を使用し、close の座標はありません。座標は PDF ユーザー空間の値です。呼び出し側で、有効かつ有限のジオメトリを指定してください。to_pdf_operations() は、W/W* と n を含む、コアのクリッピング演算子を出力します。
既存の rect、circle、ellipse ファクトリーも引き続き使用できます。
この API は、パスの交差、テキストのクリッピング、クリッピングスタックを公開しません。
線のダッシュパターン
LineDashPattern.dashed(dash_length, gap_length) と
LineDashPattern.dotted(dot_size, gap_size) は、位相がゼロの 2 要素パターンを作成します。どちらも同じ PDF ダッシュ表現を使用します。点の見え方は長さと線端の形状によって決まり、ファクトリーは丸い線端を選択しません。パターンを page.set_dash_pattern(pattern) に渡してください。連続線に戻すには solid() を使用します。
array はコピーされたリストを返し、phase は読み取り専用です。to_pdf_string() はコアの表現を返します(d 演算子は含みません)。小数点以下 2 桁に丸められます。コアは、非有限成分をゼロとして、空の配列を格納された位相に関係なく [] 0 としてシリアライズします。負の値は変更されません。この API は、任意のパターンが有効な PDF 形式かどうかを検証しません。長さには非負の値を使用し、空でないパターンでは少なくとも 1 つの長さを正の値にしてください。位相も非負にしてください。
ブレンドモード
公開済みのコア 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 入力を含め、変換後の 4 つの成分を返します。既存の c、m、y、k プロパティは、CMYK 以外の色の場合、引き続き 0 を返します。is_rgb()、is_cmyk()、is_gray() は格納されているバリアントを判定し、color_space_name() は DeviceRGB、DeviceCMYK、または DeviceGray を返します。既存の color_space プロパティでは、引き続き短い名前が使われます。
変換には、公開済みのコア 5.5.0 の近似式が使用されます。ICC プロファイルやキャリブレーション済みの色管理は使用されません。Gray は、RGB ではすべて同じ値の成分に、CMYK ではブラックのみの色に変換されます。RGB の黒は (0, 0, 0, 1) に変換されます。コンストラクターは、有限範囲外の値と無限大をクランプします。NaN は変換後も伝播することがあります。予測可能な色を得るには、有限の成分を指定してください。
ストリーミングと遅延読み込みドキュメントのオプション
Core 5.5.0 では、不変のオプションと設定スナップショットを持つ、独立した2つのコンシューマーを公開しています。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 が発生します。どちらのコンシューマーも、暗号化されたドキュメントには対応していません。必要に応じて、リーダー API を通じてロックを解除してください。
タグ付き構造のインクリメンタル編集
IncrementalTaggedPdfEditor(pdf) は不変のバイトスナップショットを保持します。
TaggedPdfMutation は、create_element、set_element_attribute(値を省略すると削除します)、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 には、現在のハイライト一式がページと ID の順に含まれます。ネイティブ 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 には、PDF の QuadPoints の順序に従った、ちょうど 4 つの (x, y) ペアを指定します。1 辺の始点と終点に続けて、反対側の辺の始点と終点を指定します。コアは、有限でない値、重複、交差、凹型、退化したジオメトリを拒否します。
HighlightColor には、[0, 1] の範囲内にある、有限値の DeviceRGB 成分をちょうど 3 つ指定します。HighlightOpacity には、[0, 1] の範囲内にある有限値を指定します。これらのプロパティは所有する値を返します。ハイライトには少なくとも 1 つの四辺形が必要です。オプションのコンテンツでは、None と空文字列が区別されます。opacity の省略と明示的なゼロも異なる値として扱われます。
ページインデックスは 0 から始まります。座標は、回転前の PDF 空間におけるページの有効な CropBox(存在しない場合は MediaBox)の範囲内でなければなりません。継承された境界も考慮されます。ID は、オブジェクト番号と世代番号を持つ TextNoteId/HighlightId です。別のサブタイプ、存在しない ID、またはバッチ内で 2 回指定された対象を指定すると、PDF エラーが発生します。明確で安定した ID を持たないため、直接指定された対象アノテーション辞書と、複数ページで共有されるアノテーションには対応していません。
エディターは不変のソーススナップショットを所有します。正常に完了した空でないバッチでは、ソースが完全なバイト列のプレフィックスとして保持され、リビジョンが 1 つ追加されます。失敗したバッチでは、ソースとエディターは変更されません。空のバッチではバイト列が完全に保持されます。ノート、ハイライト、変更内容、結果スナップショットのプロパティは読み取り専用です。これらの API はメモリ内でリビジョンを追加します。削除すると参照が切り離されますが、過去のアノテーションのバイト列はファイル内に残ります。これらの API は取り消し不能な墨消しには対応していません。
Core 5.5.0 では、空でないバッチに対して DocMDP が適用されます。P1/P2 では編集が拒否され、P3 ではこれらのアノテーション操作が許可されます。これらの証明済み入力では読み取りと空のバッチを引き続き利用できます。暗号化された入力は拒否されます。この権限チェックでは、署名の真正性は検証されません。これらのエディターはネイティブのアノテーション辞書を保持または出力しますが、外観ストリームの生成や、すべてのビューアーで同一に表示されることは保証しません。また、メモリ使用量に全体的な上限は設けません。
コメント
AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。