Skip to content

PythonとRust、GoでPDFを結合・分割する

バインディングの対応状況。 PDFの結合はPython、Rust、Goで利用可能です。extract_pagesによるファイルへの分割はPythonとRustに対応しており、インメモリ版のextract_pages_to_bytesはSwiftとC ABI(WASMを除く)も加わります。ブックマーク基準の分割計画(plan_split_by_bookmarks)はPython、Rust、Swift、WASM、C ABIで利用できます。ファイル分割バインディングについては、C#バインディングはまだこれらのエディタ操作を公開していません。回避策としてRust CLI(pdf-oxide mergepdf-oxide split)を使用するか、サポートされているバインディングのいずれかを経由して呼び出してください。

2つのPDFを1つに結合する:

Python

from pdf_oxide import PdfDocument

doc = PdfDocument("main.pdf")
doc.merge_from("appendix.pdf")
doc.save("combined.pdf")

WASM

import { WasmPdfDocument } from "pdf-oxide-wasm";

// Load both PDFs as Uint8Array
const mainDoc = new WasmPdfDocument(mainBytes);
const appendixDoc = new WasmPdfDocument(appendixBytes);
// Extract text from both and process as needed
const allText = mainDoc.extractAllText() + "\n" + appendixDoc.extractAllText();
mainDoc.free();
appendixDoc.free();

Rust

use pdf_oxide::editor::DocumentEditor;

let mut editor = DocumentEditor::open("main.pdf")?;
editor.merge_from("appendix.pdf")?;
editor.save("combined.pdf")?;

Go

package main

import (
    "log"
    pdfoxide "github.com/yfedoseev/pdf_oxide/go"
)

func main() {
    editor, err := pdfoxide.OpenEditor("main.pdf")
    if err != nil { log.Fatal(err) }
    defer editor.Close()

    if _, err := editor.MergeFrom("appendix.pdf"); err != nil { log.Fatal(err) }
    if err := editor.Save("combined.pdf"); err != nil { log.Fatal(err) }
}

C++

#include <pdf_oxide/pdf_oxide.hpp>

auto editor = pdf_oxide::DocumentEditor::open("main.pdf");
editor.merge_from("appendix.pdf");
editor.save("combined.pdf");

Swift

import PdfOxide

let editor = try DocumentEditor.open("main.pdf")
try editor.mergeFrom("appendix.pdf")
try editor.save("combined.pdf")

Dart

import 'package:pdf_oxide/pdf_oxide.dart';

final editor = DocumentEditor.open('main.pdf');
editor.mergeFrom('appendix.pdf');
editor.save('combined.pdf');

R

library(pdfoxide)

editor <- pdf_editor_open("main.pdf")
pdf_editor_merge_from(editor, "appendix.pdf")
pdf_editor_save(editor, "combined.pdf")

Julia

using PdfOxide

editor = open_editor("main.pdf")
merge_from(editor, "appendix.pdf")
save(editor, "combined.pdf")

Zig

const pdf_oxide = @import("pdf_oxide");

var editor = try pdf_oxide.DocumentEditor.openEditor("main.pdf");
defer editor.deinit();
try editor.mergeFrom("appendix.pdf");
try editor.save("combined.pdf");

Objective-C

#import "POXPdfOxide.h"
NSError *err = nil;

POXDocumentEditor *editor = [POXDocumentEditor openEditor:@"main.pdf" error:&err];
[editor mergeFrom:@"appendix.pdf" error:&err];
[editor saveToPath:@"combined.pdf" error:&err];

Elixir

{:ok, editor} = PdfOxide.open_editor("main.pdf")
:ok = PdfOxide.merge_from(editor, "appendix.pdf")
:ok = PdfOxide.editor_save(editor, "combined.pdf")

PDF OxideはPDFオブジェクトレベルでページを結合するため、フォント・画像・注釈がドキュメント間で正しく保持されます。

インストール

pip install pdf_oxide

PDFの結合

すべてのページを結合する

2つ目のPDFの全ページを1つ目のPDFに追記する:

Python

from pdf_oxide import PdfDocument

doc = PdfDocument("report.pdf")
doc.merge_from("charts.pdf")
doc.save("full-report.pdf")

WASM

// WASM API: load and process multiple documents
const report = new WasmPdfDocument(reportBytes);
const charts = new WasmPdfDocument(chartsBytes);
// Process both documents together
const fullText = report.extractAllText() + "\n" + charts.extractAllText();
report.free();
charts.free();

Rust

let mut editor = DocumentEditor::open("report.pdf")?;
let pages_added = editor.merge_from("charts.pdf")?;
println!("Added {} pages", pages_added);
editor.save("full-report.pdf")?;

Go

editor, _ := pdfoxide.OpenEditor("report.pdf")
defer editor.Close()

added, _ := editor.MergeFrom("charts.pdf")
fmt.Printf("Added %d pages\n", added)
_ = editor.Save("full-report.pdf")

C++

auto editor = pdf_oxide::DocumentEditor::open("report.pdf");
editor.merge_from("charts.pdf");
editor.save("full-report.pdf");

Swift

let editor = try DocumentEditor.open("report.pdf")
try editor.mergeFrom("charts.pdf")
try editor.save("full-report.pdf")

Dart

final editor = DocumentEditor.open('report.pdf');
editor.mergeFrom('charts.pdf');
editor.save('full-report.pdf');

R

editor <- pdf_editor_open("report.pdf")
pdf_editor_merge_from(editor, "charts.pdf")
pdf_editor_save(editor, "full-report.pdf")

Julia

editor = open_editor("report.pdf")
merge_from(editor, "charts.pdf")
save(editor, "full-report.pdf")

Zig

var editor = try pdf_oxide.DocumentEditor.openEditor("report.pdf");
defer editor.deinit();
try editor.mergeFrom("charts.pdf");
try editor.save("full-report.pdf");

Objective-C

POXDocumentEditor *editor = [POXDocumentEditor openEditor:@"report.pdf" error:&err];
[editor mergeFrom:@"charts.pdf" error:&err];
[editor saveToPath:@"full-report.pdf" error:&err];

Elixir

{:ok, editor} = PdfOxide.open_editor("report.pdf")
:ok = PdfOxide.merge_from(editor, "charts.pdf")
:ok = PdfOxide.editor_save(editor, "full-report.pdf")

複数ファイルを結合する

静的メソッドPdf.merge()を使って複数のPDFを一度に結合する:

Python

from pdf_oxide import Pdf

pdf = Pdf.merge(["intro.pdf", "chapter1.pdf", "chapter2.pdf", "appendix.pdf"])
pdf.save("book.pdf")

既存のドキュメントにmerge_from()を連鎖させることもできます:

from pdf_oxide import PdfDocument

doc = PdfDocument("intro.pdf")
for f in ["chapter1.pdf", "chapter2.pdf", "appendix.pdf"]:
    doc.merge_from(f)
doc.save("book.pdf")

WASM

// Load and process multiple PDFs sequentially
const files = [introBytes, ch1Bytes, ch2Bytes, appendixBytes];
const allText = [];
for (const bytes of files) {
    const doc = new WasmPdfDocument(bytes);
    allText.push(doc.extractAllText());
    doc.free();
}
console.log(allText.join("\n"));

Rust

let files = ["intro.pdf", "chapter1.pdf", "chapter2.pdf", "appendix.pdf"];
let mut editor = DocumentEditor::open(files[0])?;
for f in &files[1..] {
    editor.merge_from(f)?;
}
editor.save("book.pdf")?;

Go

// Top-level Merge returns the combined PDF bytes in one call
bytes, err := pdfoxide.Merge([]string{
    "intro.pdf", "chapter1.pdf", "chapter2.pdf", "appendix.pdf",
})
if err != nil { log.Fatal(err) }
_ = os.WriteFile("book.pdf", bytes, 0644)

C++

// Top-level merge returns the combined PDF bytes in one call
auto bytes = pdf_oxide::merge({"intro.pdf", "chapter1.pdf", "chapter2.pdf", "appendix.pdf"});
std::ofstream("book.pdf", std::ios::binary)
    .write(reinterpret_cast<const char*>(bytes.data()), bytes.size());

Swift

// Top-level merge returns the combined PDF bytes in one call
let bytes = try merge(["intro.pdf", "chapter1.pdf", "chapter2.pdf", "appendix.pdf"])
try Data(bytes).write(to: URL(fileURLWithPath: "book.pdf"))

Dart

// Top-level pdfMerge returns the combined PDF bytes in one call
final bytes = pdfMerge(['intro.pdf', 'chapter1.pdf', 'chapter2.pdf', 'appendix.pdf']);
File('book.pdf').writeAsBytesSync(bytes);

R

# Top-level pdf_merge returns the combined PDF bytes in one call
bytes <- pdf_merge(c("intro.pdf", "chapter1.pdf", "chapter2.pdf", "appendix.pdf"))
writeBin(bytes, "book.pdf")

Julia

# Top-level merge_pdfs returns the combined PDF bytes in one call
bytes = merge_pdfs(["intro.pdf", "chapter1.pdf", "chapter2.pdf", "appendix.pdf"])
write("book.pdf", bytes)

Zig

const a = std.heap.page_allocator;
const paths = [_][*:0]const u8{ "intro.pdf", "chapter1.pdf", "chapter2.pdf", "appendix.pdf" };
const bytes = try pdf_oxide.merge(a, &paths); // combined PDF bytes
defer a.free(bytes);
const out = try std.fs.cwd().createFile("book.pdf", .{});
defer out.close();
try out.writeAll(bytes);

Objective-C

// Top-level merge returns the combined PDF bytes in one call
NSData *bytes = [POXTools merge:@[@"intro.pdf", @"chapter1.pdf", @"chapter2.pdf", @"appendix.pdf"]
                          error:&err];
[bytes writeToFile:@"book.pdf" atomically:YES];

Elixir

# Top-level merge returns the combined PDF bytes in one call
{:ok, bytes} = PdfOxide.merge(["intro.pdf", "chapter1.pdf", "chapter2.pdf", "appendix.pdf"])
File.write!("book.pdf", bytes)

特定のページのみ結合する

ソースドキュメントから結合するページを選択する:

Python

from pdf_oxide import PdfDocument

doc = PdfDocument("main.pdf")
# Merge only pages 0, 2, and 4 from source
doc.merge_pages_from("source.pdf", [0, 2, 4])
doc.save("selected.pdf")

Rust

let mut editor = DocumentEditor::open("main.pdf")?;
editor.merge_pages_from("source.pdf", &[0, 2, 4])?;
editor.save("selected.pdf")?;

PDFの分割

特定ページを新しいファイルに抽出する

大きなドキュメントから特定ページを取り出す:

Python

from pdf_oxide import PdfDocument

doc = PdfDocument("book.pdf")
doc.extract_pages([0, 1, 2, 3, 4], "chapter1.pdf")

WASM

// Extract text from specific pages
const doc = new WasmPdfDocument(bytes);
const pages = [0, 1, 2, 3, 4];
for (const i of pages) {
    const text = doc.extractText(i);
    console.log(`Page ${i + 1}: ${text.slice(0, 80)}...`);
}
doc.free();

Rust

let mut editor = DocumentEditor::open("book.pdf")?;
editor.extract_pages(&[0, 1, 2, 3, 4], "chapter1.pdf")?;

1ページずつ個別ファイルに分割する

各ページを独立したファイルとして保存する:

Python

from pdf_oxide import PdfDocument

doc = PdfDocument("document.pdf")
for i in range(doc.page_count()):
    doc.extract_pages([i], f"page_{i + 1}.pdf")

Rust

let mut editor = DocumentEditor::open("document.pdf")?;
let page_count = editor.page_count()?;
for i in 0..page_count {
    editor.extract_pages(&[i], &format!("page_{}.pdf", i + 1))?;
}

チャンクに分割する

大きなPDFをNページずつの小さなファイルに分割する:

Python

from pdf_oxide import PdfDocument

doc = PdfDocument("large.pdf")
chunk_size = 10

for start in range(0, doc.page_count(), chunk_size):
    end = min(start + chunk_size, doc.page_count())
    pages = list(range(start, end))
    doc.extract_pages(pages, f"chunk_{start // chunk_size + 1}.pdf")

Rust

let mut editor = DocumentEditor::open("large.pdf")?;
let page_count = editor.page_count()?;
let chunk_size = 10;

for start in (0..page_count).step_by(chunk_size) {
    let end = (start + chunk_size).min(page_count);
    let pages: Vec<usize> = (start..end).collect();
    editor.extract_pages(&pages, &format!("chunk_{}.pdf", start / chunk_size + 1))?;
}

インメモリバイトへの分割(一時ファイル不要)

分割したチャンクをそのままS3やHTTPレスポンス、あるいはプロセス内の次のステップに渡す場合は、extract_pages_to_bytesでディスクへの書き込みを省略できます。新しいPDFをバイトとして返し、元のドキュメントは変更しません。

Python

from pdf_oxide import PdfDocument

doc = PdfDocument("large.pdf")
chunk_size = 10
chunks = []

for start in range(0, doc.page_count(), chunk_size):
    end = min(start + chunk_size, doc.page_count())
    pages = list(range(start, end))
    chunk_bytes = doc.extract_pages_to_bytes(pages)  # bytes, not a file
    chunks.append(chunk_bytes)

print(f"Produced {len(chunks)} in-memory chunks")

Rust

let mut editor = DocumentEditor::open("large.pdf")?;
let page_count = editor.page_count()?;
let chunk_size = 10;
let mut chunks: Vec<Vec<u8>> = Vec::new();

for start in (0..page_count).step_by(chunk_size) {
    let end = (start + chunk_size).min(page_count);
    let pages: Vec<usize> = (start..end).collect();
    chunks.push(editor.extract_pages_to_bytes(&pages)?); // Vec<u8>, no file written
}

Swift

let editor = try DocumentEditor(path: "large.pdf")
let pageCount = try editor.pageCount()
let chunkSize = 10
var chunks: [[UInt8]] = []

for start in stride(from: 0, to: pageCount, by: chunkSize) {
    let end = min(start + chunkSize, pageCount)
    chunks.append(try editor.extractPagesToBytes(Array(start..<end)))
}

C++

auto editor = pdf_oxide::DocumentEditor::open("large.pdf");
int page_count = editor.page_count();
const int chunk_size = 10;
std::vector<std::vector<std::uint8_t>> chunks;

for (int start = 0; start < page_count; start += chunk_size) {
    int end = std::min(start + chunk_size, page_count);
    std::vector<int32_t> pages;
    for (int i = start; i < end; ++i) pages.push_back(i);
    chunks.push_back(editor.extract_pages_to_bytes(pages)); // bytes, no file written
}

Dart

final editor = DocumentEditor.open('large.pdf');
final pageCount = editor.pageCount;
const chunkSize = 10;
final chunks = <Uint8List>[];

for (var start = 0; start < pageCount; start += chunkSize) {
  final end = (start + chunkSize).clamp(0, pageCount);
  final pages = [for (var i = start; i < end; i++) i];
  chunks.add(editor.extractPagesToBytes(pages)); // bytes, no file written
}

R

editor <- pdf_editor_open("large.pdf")
page_count <- pdf_editor_page_count(editor)
chunk_size <- 10
chunks <- list()

for (start in seq(0, page_count - 1, by = chunk_size)) {
  end <- min(start + chunk_size, page_count)
  pages <- seq(start, end - 1)
  chunks[[length(chunks) + 1]] <- pdf_editor_extract_pages_to_bytes(editor, pages)
}

Julia

editor = open_editor("large.pdf")
n = page_count(editor)
chunk_size = 10
chunks = Vector{Vector{UInt8}}()

for start in 0:chunk_size:(n - 1)
    stop = min(start + chunk_size, n)
    pages = collect(start:(stop - 1))
    push!(chunks, extract_pages_to_bytes(editor, pages))  # bytes, no file written
end

Zig

const a = std.heap.page_allocator;
var editor = try pdf_oxide.DocumentEditor.openEditor("large.pdf");
defer editor.deinit();
const page_count = try editor.pageCount();
const chunk_size: i32 = 10;

var start: i32 = 0;
while (start < page_count) : (start += chunk_size) {
    const end = @min(start + chunk_size, page_count);
    var pages = std.ArrayList(i32).init(a);
    defer pages.deinit();
    var i = start;
    while (i < end) : (i += 1) try pages.append(i);
    const chunk = try editor.extractPagesToBytes(a, pages.items); // bytes, no file written
    a.free(chunk);
}

Objective-C

POXDocumentEditor *editor = [POXDocumentEditor openEditor:@"large.pdf" error:&err];
NSInteger pageCount = [editor pageCountError:&err];
NSInteger chunkSize = 10;
NSMutableArray<NSData*> *chunks = [NSMutableArray array];

for (NSInteger start = 0; start < pageCount; start += chunkSize) {
    NSInteger end = MIN(start + chunkSize, pageCount);
    NSMutableArray<NSNumber*> *pages = [NSMutableArray array];
    for (NSInteger i = start; i < end; i++) [pages addObject:@(i)];
    [chunks addObject:[editor extractPagesToBytes:pages error:&err]]; // bytes, no file written
}

Elixir

{:ok, editor} = PdfOxide.open_editor("large.pdf")
{:ok, n} = PdfOxide.editor_page_count(editor)
chunk_size = 10

chunks =
  0..(n - 1)
  |> Enum.take_every(chunk_size)
  |> Enum.map(fn start ->
    stop = min(start + chunk_size, n)
    {:ok, bytes} = PdfOxide.extract_pages_to_bytes(editor, Enum.to_list(start..(stop - 1)))
    bytes
  end)

extract_pages_to_bytesはPython、Rust、Swift、C++、Dart、R、Julia、Zig、Objective-C、Elixir、およびC ABIで利用可能です。WASMビルドでは公開されていません。

ブックマークで分割する

アウトライン(目次)を持つドキュメントの場合、ページ範囲を手動で計算することなく、ブックマーク境界での分割計画を立てられます。たとえば、最上位の章ごとに1つのPDFを作るといった使い方が可能です。plan_split_by_bookmarksドライランプランナーです。セグメント計画(ページ範囲、タイトル、ファイルシステムに安全なファイルステム)を返すだけで、PDFバイトは一切生成しません。出力を書き込む前に、内容を確認・フィルタリング・再ターゲット設定できます。

分割計画を立てる(ドライラン)

Python

import pdf_oxide

with open("manual.pdf", "rb") as f:
    src = f.read()

# level=1 -> split at top-level bookmarks only (0 = every depth, n = up to depth n)
segments = pdf_oxide.plan_split_by_bookmarks(src, level=1)

for seg in segments:
    # keys: index, start_page, end_page, title, file_stem, page_label
    print(f"#{seg['index']}: pages {seg['start_page']}-{seg['end_page'] - 1} "
          f"=> {seg['file_stem']}.pdf  ({seg['title']})")

Rust

use pdf_oxide::PdfDocument;
use pdf_oxide::split_bookmarks::{plan_split_by_bookmarks, SplitByBookmarksOptions, BookmarkLevel};

let doc = PdfDocument::open("manual.pdf")?;

let opts = SplitByBookmarksOptions {
    level: BookmarkLevel::TopLevel,  // top-level bookmarks only
    ..Default::default()
};

// Cheap: returns Vec<BookmarkSegment>, no PDF bytes produced
let segments = plan_split_by_bookmarks(&doc, &opts)?;
for seg in &segments {
    println!("#{}: pages {}..{} => {}.pdf ({:?})",
        seg.index, seg.start_page, seg.end_page, seg.file_stem, seg.title);
}

Swift

import PdfOxide

let doc = try PdfDocument(path: "manual.pdf")

// Returns a JSON array of segment objects (index, startPage, endPage, ...)
let planJson = try doc.planSplitByBookmarks(optionsJson: #"{"level": 1}"#)
print(planJson)

WASM

import { planSplitByBookmarks } from "pdf-oxide-wasm";

// level 1 = top-level bookmarks; returns an array of segment objects
const segments = planSplitByBookmarks(bytes, null, false, 1, true);
for (const seg of segments) {
  console.log(`#${seg.index}: ${seg.startPage}-${seg.endPage} => ${seg.fileStem}.pdf`);
}

Java

import fyi.oxide.pdf.Pdf;
import java.nio.file.*;

byte[] src = Files.readAllBytes(Path.of("manual.pdf"));

// level 1 = top-level bookmarks; returns the number of segments the split would produce
int segmentCount = Pdf.planSplitByBookmarksCount(src, 1);
System.out.println(segmentCount + " segments");

Ruby

require 'pdf_oxide'

src = File.binread('manual.pdf')

# level 1 = top-level bookmarks; returns the number of segments the split would produce
segment_count = PdfOxide::Pdf.plan_split_by_bookmarks_count(src, 1)
puts "#{segment_count} segments"

C++

auto doc = pdf_oxide::Document::open("manual.pdf");

// Returns a JSON array of segment objects (index, start_page, end_page, ...)
std::string planJson = doc.plan_split_by_bookmarks(R"({"level": 1})");
std::cout << planJson << "\n";

Dart

final doc = PdfDocument.open('manual.pdf');

// Returns a JSON array of segment objects (index, startPage, endPage, ...)
final planJson = doc.planSplitByBookmarks('{"level": 1}');
print(planJson);

R

doc <- pdf_open("manual.pdf")

# Returns a JSON array of segment objects (index, start_page, end_page, ...)
plan_json <- pdf_plan_split_by_bookmarks(doc, '{"level": 1}')
cat(plan_json, "\n")

Julia

doc = open_document("manual.pdf")

# Returns a JSON array of segment objects (index, start_page, end_page, ...)
plan_json = plan_split_by_bookmarks(doc, """{"level": 1}""")
println(plan_json)

Zig

const a = std.heap.page_allocator;
var doc = try pdf_oxide.Document.open("manual.pdf");
defer doc.deinit();

// Returns a JSON array of segment objects (index, start_page, end_page, ...)
const plan_json = try doc.planSplitByBookmarks(a, "{\"level\": 1}");
defer a.free(plan_json);
std.debug.print("{s}\n", .{plan_json});

Objective-C

POXDocument *doc = [POXDocument openPath:@"manual.pdf" error:&err];

// Returns a JSON array of segment objects (index, start_page, end_page, ...)
NSString *planJson = [doc planSplitByBookmarks:@"{\"level\": 1}" error:&err];
NSLog(@"%@", planJson);

Elixir

{:ok, doc} = PdfOxide.open("manual.pdf")

# Returns a JSON array of segment objects (index, start_page, end_page, ...)
{:ok, plan_json} = PdfOxide.plan_split_by_bookmarks(doc, ~s({"level": 1}))
IO.puts(plan_json)

各セグメントには次の情報が含まれます:index(1始まりの順序番号)、start_page(0始まり・包含)、end_page(0始まり・排他的。範囲はstart_page..end_page)、title(ソースブックマークのタイトル。前置フロントマターセグメントの場合はnull)、file_stem(重複排除済み・ファイルシステム安全なステム、拡張子なし)、page_label

分割オプション

オプション デフォルト 説明
title_prefix なし このプレフィックスで始まるタイトルのブックマークのみで分割
ignore_case false プレフィックス一致で大文字・小文字を無視する
level 1(最上位) 0 = 全深度、1 = 最上位のみ、n = 深度n以下
include_front_matter true 最初の分割点より前のページを先頭セグメントとして出力する

計画を立ててから抽出する

計画はページ範囲に過ぎないので、各セグメントをそのままインメモリ抽出器に渡せます。境界を再計算する必要はありません。

import pdf_oxide
from pdf_oxide import PdfDocument

with open("manual.pdf", "rb") as f:
    src = f.read()

doc = PdfDocument.from_bytes(src)
for seg in pdf_oxide.plan_split_by_bookmarks(src, level=1):
    pages = list(range(seg["start_page"], seg["end_page"]))
    chunk = doc.extract_pages_to_bytes(pages)
    with open(f"{seg['file_stem']}.pdf", "wb") as out:
        out.write(chunk)

バインディングの対応状況。 plan_split_by_bookmarksPython(モジュールレベル関数pdf_oxide.plan_split_by_bookmarks)、Rustpdf_oxide::split_bookmarks::plan_split_by_bookmarks)、SwiftplanSplitByBookmarks)、WASMplanSplitByBookmarks)、C ABIpdf_document_plan_split_by_bookmarks)で公開されています。ドキュメントにアウトラインがない場合はエラーが発生します(PythonではRuntimeError)。その場合はページ単位またはチャンク単位の分割を使用してください。

よくある質問

extract_pagesextract_pages_to_bytesの違いは何ですか? extract_pages(pages, output)は結果をファイルパスに書き込みます。extract_pages_to_bytes(pages)は新しいPDFをメモリ内のバイトとして返します。どちらも0始まりのページインデックスを受け取り、ソースドキュメントは変更しません。出力をディスクに触れずにストリーミングまたは保存する場合はインメモリ版を選択してください。

plan_split_by_bookmarksはPDFファイルを作成しますか? いいえ。セグメントのメタデータ(ページ範囲、タイトル、ファイルステム)のみを返す、純粋で軽量なプランナーです。実際にチャンクを生成するにはextract_pages_to_bytesと組み合わせるか、セグメントとバイトのペアを返すワンショットヘルパーsplit_by_bookmarks(Python/Rust/WASM)を使用してください。

章ごとに1つのPDFに分割するにはどうすればいいですか? PDFにアウトラインがある場合、plan_split_by_bookmarks(src, level=1)を呼び出して最上位ブックマークごとに1つのセグメントを取得し、各セグメントのstart_page..end_pageの範囲をextract_pages_to_bytesで抽出します。level=0にするとすべてのアウトライン深度で分割します。

なぜ分割処理がこんなに速いのですか? 分割はPDF OxideのピュアRustコアのPDFオブジェクトレベルで動作します。平均抽出速度0.8ms、合格率100%のベンチマーク結果を誇る同じエンジンです。分割計画はアウトラインとページ数にしか触れないため、大きなドキュメントでも事実上瞬時に完了します。

関連ページ