テキスト抽出
PDF Oxideは複数レベルのテキスト抽出を提供します。ページ全体のテキスト、フォントメタデータ付きのスタイル付きスパン、そして正確な位置情報を持つ個別文字の抽出です。素早いコンテンツ取得には extract_text() を、フォントや位置データが必要な場合は extract_spans() を、カスタムレイアウトエンジンやOCR後処理など文字単位の分析には extract_chars() を使用してください。
タグ付きPDFの場合、テキスト抽出は正しい読み順のためにドキュメントの構造ツリーを自動的に参照します。タグなしPDFの場合、ページコンテンツの順序をベースに、インテリジェントな改行検出を使用します。これにはRFCスタイルや論文スタイルの文書における本文テキストの断片化を防ぐシングルカラムガードも含まれます。
読み順サポート
読み順パイプラインはさまざまな文字体系とレイアウトで正確な出力を生成します:
- Latin — デフォルトの左から右、上から下へのカラム検出付き。
- Arabic — プリシェイプされたスパンの反転処理(Pass 0)により、文字を視覚的な順序ではなく論理的な読み順に配置。
- CJK — 空間的テーブル検出器によりrowspanラベルのカラムが保持され、3ptのYバンド量子化により表形式コンテンツが混在しないよう処理。
- 回転/dvips生成PDF — カラム検出における外れ値除去(中央値ベース)により、縮退したCTM座標を適切に処理。
- 多段組み学術論文 — XYCutシングルカラムガードで断片化を解消し、行対応スパンソートにより本文中の表形式コンテンツを処理。
単語・行セグメンテーション
extract_words() と extract_text_lines() は、単語および行の区切りしきい値を調整するオプションのkwargsを受け付けます:
| パラメータ | デフォルト | 説明 |
|---|---|---|
word_gap_threshold |
適応的 | 単語の区切りと判定する、隣接文字間の最小水平間隔(ポイント単位) |
line_gap_threshold |
適応的 | 行の区切りと判定する、ベースライン間の最小垂直間隔 |
profile |
"auto" |
"auto"、"dense"、"standard"、"sparse" のいずれか — 異なるレイアウトに合わせたプリセットを選択 |
適応的パラメータはページのフォントメトリクスから導出されます。計算値を確認するには page_layout_params() を、カスタムプロファイルを作成するには ExtractionProfile を使用してください。
Pythonのみの調整機能:
word_gap_threshold、line_gap_threshold、profile、page_layout_params()はPythonバインディングで公開されています。Node.js、JavaScript、Go、C#、WASMバインディングでは、kwargsなしで適応的デフォルトを使用したextractWords(pageIndex)/extractTextLines(pageIndex)が公開されています。これらの言語から調整する場合は、以下のRust APIを使用してください。
Python
from pdf_oxide import PdfDocument, ExtractionProfile
doc = PdfDocument("receipt.pdf")
params = doc.page_layout_params(0)
print(params.word_gap_threshold, params.line_gap_threshold)
words = doc.extract_words(0, word_gap_threshold=2.5, profile="dense")
lines = doc.extract_text_lines(0, profile=ExtractionProfile.DENSE)
Node.js
const { PdfDocument } = require("pdf-oxide");
const doc = new PdfDocument("receipt.pdf");
const words = doc.extractWords(0); // adaptive defaults
const lines = doc.extractTextLines(0);
doc.close();
JavaScript
const { PdfDocument } = require("pdf-oxide");
const doc = new PdfDocument("receipt.pdf");
const words = doc.extractWords(0);
const lines = doc.extractTextLines(0);
doc.close();
TypeScript
import { PdfDocument } from "pdf-oxide";
const doc: PdfDocument = new PdfDocument("receipt.pdf");
const words = doc.extractWords(0);
const lines = doc.extractTextLines(0);
doc.close();
Rust
use pdf_oxide::{PdfDocument, ExtractionProfile};
let mut doc = PdfDocument::open("receipt.pdf")?;
let params = doc.page_layout_params(0)?;
println!("{} {}", params.word_gap_threshold, params.line_gap_threshold);
let words = doc.extract_words_with_config(0, /* word_gap_threshold */ Some(2.5), ExtractionProfile::Dense)?;
let lines = doc.extract_text_lines_with_profile(0, ExtractionProfile::Dense)?;
Go
words, _ := doc.ExtractWords(0) // adaptive defaults
lines, _ := doc.ExtractTextLines(0)
C#
var words = doc.ExtractWords(0); // adaptive defaults
var lines = doc.ExtractTextLines(0);
WASM
const doc = new WasmPdfDocument(bytes);
const words = doc.extractWords(0);
const lines = doc.extractTextLines(0);
Java
try (PdfDocument doc = PdfDocument.open(Path.of("receipt.pdf"))) {
List<TextWord> words = doc.page(0).words(); // adaptive defaults
List<TextLine> lines = doc.page(0).lines();
}
C++
auto doc = pdf_oxide::Document::open("receipt.pdf");
auto words = doc.extract_words(0); // adaptive defaults
auto lines = doc.extract_text_lines(0);
Swift
let doc = try Document.open("receipt.pdf")
let words = try doc.extractWords(0) // adaptive defaults
let lines = try doc.extractTextLines(0)
Kotlin
PdfDocument.open(Path.of("receipt.pdf")).use { doc ->
val words = doc.page(0).words() // adaptive defaults
val lines = doc.page(0).lines()
}
Dart
final doc = PdfDocument.open('receipt.pdf');
final words = doc.extractWords(0); // adaptive defaults
final lines = doc.extractTextLines(0);
R
doc <- pdf_open("receipt.pdf")
words <- pdf_extract_words(doc, 0) # adaptive defaults
lines <- pdf_extract_text_lines(doc, 0)
Julia
doc = open_document("receipt.pdf")
words = extract_words(doc, 0) # adaptive defaults
lines = extract_text_lines(doc, 0)
Zig
var doc = try pdf_oxide.Document.open("receipt.pdf");
const words = try doc.extractWords(a, 0); // adaptive defaults
const lines = try doc.extractTextLines(a, 0);
Scala
Using.resource(PdfDocument.open("receipt.pdf")) { doc =>
val words = doc.page(0).wordsSeq // adaptive defaults
val lines = doc.page(0).linesSeq
}
Clojure
(with-open [doc (pdf/open "receipt.pdf")]
(pdf/words (pdf/page doc 0)) ; adaptive defaults
(pdf/lines (pdf/page doc 0)))
Objective-C
POXDocument *doc = [POXDocument openPath:@"receipt.pdf" error:&err];
NSArray<POXWord*> *words = [doc extractWords:0 error:&err]; // adaptive defaults
NSArray<POXTextLine*> *lines = [doc extractTextLines:0 error:&err];
Elixir
{:ok, doc} = PdfOxide.open("receipt.pdf")
{:ok, words} = PdfOxide.extract_words(doc, 0) # adaptive defaults
{:ok, lines} = PdfOxide.extract_text_lines(doc, 0)
クイックサンプル
Python
from pdf_oxide import PdfDocument
doc = PdfDocument("report.pdf")
text = doc.extract_text(0)
print(text)
Node.js
const { PdfDocument } = require("pdf-oxide");
const doc = new PdfDocument("report.pdf");
const text = doc.extractText(0);
console.log(text);
Go
import pdfoxide "github.com/yfedoseev/pdf_oxide/go"
doc, _ := pdfoxide.Open("report.pdf")
defer doc.Close()
text, _ := doc.ExtractText(0)
fmt.Println(text)
C#
using PdfOxide.Core;
using var doc = PdfDocument.Open("report.pdf");
string text = doc.ExtractText(0);
Console.WriteLine(text);
WASM
const doc = new WasmPdfDocument(bytes);
const text = doc.extractText(0);
console.log(text);
Rust
use pdf_oxide::PdfDocument;
let mut doc = PdfDocument::open("report.pdf")?;
let text = doc.extract_text(0)?;
println!("{}", text);
Java
import fyi.oxide.pdf.*;
import java.nio.file.Path;
try (PdfDocument doc = PdfDocument.open(Path.of("report.pdf"))) {
String text = doc.extractText(0);
System.out.println(text);
}
PHP
use PdfOxide\PdfDocument;
$doc = PdfDocument::open('report.pdf');
$text = $doc->extractText(0);
echo $text;
$doc->close();
Ruby
require 'pdf_oxide'
PdfOxide::PdfDocument.open('report.pdf') do |doc|
text = doc.extract_text(0)
puts text
end
C++
#include <pdf_oxide/pdf_oxide.hpp>
auto doc = pdf_oxide::Document::open("report.pdf");
auto text = doc.extract_text(0);
std::cout << text << "\n";
Swift
import PdfOxide
let doc = try Document.open("report.pdf")
let text = try doc.extractText(0)
print(text)
Kotlin
import fyi.oxide.pdf.*
PdfDocument.open(java.nio.file.Path.of("report.pdf")).use { doc ->
val text = doc.extractText(0)
println(text)
}
Dart
import 'package:pdf_oxide/pdf_oxide.dart';
final doc = PdfDocument.open('report.pdf');
final text = doc.extractText(0);
print(text);
R
library(pdfoxide)
doc <- pdf_open("report.pdf")
text <- pdf_extract_text(doc, 0)
cat(text)
Julia
using PdfOxide
doc = open_document("report.pdf")
text = extract_text(doc, 0)
println(text)
Zig
const pdf_oxide = @import("pdf_oxide");
var doc = try pdf_oxide.Document.open("report.pdf");
const text = try doc.extractText(a, 0);
std.debug.print("{s}\n", .{text});
Scala
import fyi.oxide.pdf.PdfDocument
import scala.util.Using
Using.resource(PdfDocument.open("report.pdf")) { doc =>
val text = doc.extractText(0)
println(text)
}
Clojure
(require '[pdf-oxide.core :as pdf])
(with-open [doc (pdf/open "report.pdf")]
(println (pdf/extract-text doc 0)))
Objective-C
#import "POXPdfOxide.h"
NSError *err = nil;
POXDocument *doc = [POXDocument openPath:@"report.pdf" error:&err];
NSString *text = [doc extractText:0 error:&err];
NSLog(@"%@", text);
Elixir
{:ok, doc} = PdfOxide.open("report.pdf")
{:ok, text} = PdfOxide.extract_text(doc, 0)
IO.puts(text)
APIリファレンス
extract_text(page_index) -> str
ページ全体のテキストを単一の文字列として抽出します。タグ付きPDFを自動検出し、利用可能な場合は構造ツリーを使用して読み順を決定します。スパン間の垂直・水平方向の間隔に基づいて改行とスペースを挿入します。
| パラメータ | 型 | 説明 |
|---|---|---|
page_index |
int / usize |
ゼロベースのページインデックス |
戻り値: ページの全テキストコンテンツ。
Python
doc = PdfDocument("report.pdf")
for i in range(doc.page_count()):
text = doc.extract_text(i)
print(f"--- Page {i + 1} ---")
print(text)
Node.js
const doc = new PdfDocument("report.pdf");
for (let i = 0; i < doc.getPageCount(); i++) {
const text = doc.extractText(i);
console.log(`--- Page ${i + 1} ---`);
console.log(text);
}
Go
doc, _ := pdfoxide.Open("report.pdf")
defer doc.Close()
count, _ := doc.PageCount()
for i := 0; i < count; i++ {
text, _ := doc.ExtractText(i)
fmt.Printf("--- Page %d ---\n", i+1)
fmt.Println(text)
}
C#
using var doc = PdfDocument.Open("report.pdf");
for (int i = 0; i < doc.PageCount; i++)
{
string text = doc.ExtractText(i);
Console.WriteLine($"--- Page {i + 1} ---");
Console.WriteLine(text);
}
WASM
const doc = new WasmPdfDocument(bytes);
for (let i = 0; i < doc.pageCount(); i++) {
const text = doc.extractText(i);
console.log(`--- Page ${i + 1} ---`);
console.log(text);
}
Rust
let mut doc = PdfDocument::open("report.pdf")?;
let page_count = doc.page_count()?;
for i in 0..page_count {
let text = doc.extract_text(i)?;
println!("--- Page {} ---", i + 1);
println!("{}", text);
}
Java
try (PdfDocument doc = PdfDocument.open(Path.of("report.pdf"))) {
int n = doc.pageCount();
for (int i = 0; i < n; i++) {
System.out.println("--- Page " + (i + 1) + " ---");
System.out.println(doc.extractText(i));
}
}
PHP
$doc = PdfDocument::open('report.pdf');
$n = $doc->pageCount();
for ($i = 0; $i < $n; $i++) {
echo "--- Page " . ($i + 1) . " ---\n";
echo $doc->extractText($i);
}
$doc->close();
Ruby
PdfOxide::PdfDocument.open('report.pdf') do |doc|
(0...doc.page_count).each do |i|
puts "--- Page #{i + 1} ---"
puts doc.extract_text(i)
end
end
C++
auto doc = pdf_oxide::Document::open("report.pdf");
int n = doc.page_count();
for (int i = 0; i < n; i++) {
std::cout << "--- Page " << (i + 1) << " ---\n";
std::cout << doc.extract_text(i) << "\n";
}
Swift
let doc = try Document.open("report.pdf")
let n = try doc.pageCount()
for i in 0..<n {
print("--- Page \(i + 1) ---")
print(try doc.extractText(i))
}
Kotlin
PdfDocument.open(java.nio.file.Path.of("report.pdf")).use { doc ->
for (i in 0 until doc.pageCount()) {
println("--- Page ${i + 1} ---")
println(doc.extractText(i))
}
}
Dart
final doc = PdfDocument.open('report.pdf');
for (var i = 0; i < doc.pageCount; i++) {
print('--- Page ${i + 1} ---');
print(doc.extractText(i));
}
R
doc <- pdf_open("report.pdf")
for (i in seq_len(pdf_page_count(doc)) - 1) {
cat(sprintf("--- Page %d ---\n", i + 1))
cat(pdf_extract_text(doc, i))
}
Julia
doc = open_document("report.pdf")
for i in 0:(page_count(doc) - 1)
println("--- Page $(i + 1) ---")
println(extract_text(doc, i))
end
Zig
var doc = try pdf_oxide.Document.open("report.pdf");
const n = try doc.pageCount();
var i: usize = 0;
while (i < n) : (i += 1) {
std.debug.print("--- Page {d} ---\n", .{i + 1});
const text = try doc.extractText(a, i);
std.debug.print("{s}\n", .{text});
}
Scala
Using.resource(PdfDocument.open("report.pdf")) { doc =>
for (i <- 0 until doc.pageCount()) {
println(s"--- Page ${i + 1} ---")
println(doc.extractText(i))
}
}
Clojure
(with-open [doc (pdf/open "report.pdf")]
(doseq [i (range (pdf/page-count doc))]
(println (str "--- Page " (inc i) " ---"))
(println (pdf/extract-text doc i))))
Objective-C
POXDocument *doc = [POXDocument openPath:@"report.pdf" error:&err];
NSInteger n = [doc pageCountError:&err];
for (NSInteger i = 0; i < n; i++) {
NSLog(@"--- Page %ld ---", (long)(i + 1));
NSLog(@"%@", [doc extractText:i error:&err]);
}
Elixir
{:ok, doc} = PdfOxide.open("report.pdf")
{:ok, n} = PdfOxide.page_count(doc)
Enum.each(0..(n - 1), fn i ->
IO.puts("--- Page #{i + 1} ---")
{:ok, text} = PdfOxide.extract_text(doc, i)
IO.puts(text)
end)
extract_spans(page_index) -> list[TextSpan]
テキストをスパンとして抽出します。スパンとは同じフォントとスタイルを持つ連続したテキストの塊です。各スパンにはテキストコンテンツ、バウンディングボックス、フォント名、フォントサイズ、ウェイト、イタリックフラグ、カラーが含まれます。レイアウトやフォント情報が必要な抽出タスクのほとんどで推奨されるアプローチです。
| パラメータ | 型 | 説明 |
|---|---|---|
page_index |
int / usize |
ゼロベースのページインデックス |
戻り値: TextSpan オブジェクトのリスト/ベクター。
TextSpanフィールド
| フィールド | 型 | 説明 |
|---|---|---|
text |
str |
スパンのテキストコンテンツ |
bbox |
Rect |
バウンディングボックス(x, y, width, height) |
font_name |
str |
フォント名/ファミリー(例:“Helvetica”、“TimesNewRoman”) |
font_size |
f32 |
フォントサイズ(ポイント単位) |
font_weight |
FontWeight |
ウェイト:Normal、Bold、Light、SemiBoldなど |
is_italic |
bool |
スパンがイタリック体かどうか |
color |
Color |
RGBカラー(r, g, b)、値の範囲は0.0〜1.0 |
mcid |
Option<u32> |
タグ付きPDF用のマークドコンテンツID |
sequence |
usize |
抽出順序(Y座標ソートのタイブレーカー) |
is_monospace |
bool |
フォントが等幅かどうか(Courier、Consolasなど) |
char_widths |
list[float] |
正確なバウンディングボックスのためのグリフ単位の送り幅 |
char_spacing |
f32 |
文字間隔(Tcパラメータ) |
word_spacing |
f32 |
単語間隔(Twパラメータ) |
horizontal_scaling |
f32 |
水平スケーリングパーセンテージ(Tz、デフォルト100.0) |
Rust
let mut doc = PdfDocument::open("paper.pdf")?;
let spans = doc.extract_spans(0)?;
for span in &spans {
println!(
"'{}' at ({:.1}, {:.1}) font={} size={:.1}pt bold={} italic={}",
span.text,
span.bbox.x, span.bbox.y,
span.font_name,
span.font_size,
span.font_weight == FontWeight::Bold,
span.is_italic,
);
}
extract_spans_with_config(page_index, config) -> Vec<TextSpan>
カスタムスパン結合設定でスパンを抽出します。デフォルトの結合処理がドキュメントの単語境界で正しく機能しない場合に使用します。
| パラメータ | 型 | 説明 |
|---|---|---|
page_index |
usize |
ゼロベースのページインデックス |
config |
SpanMergingConfig |
抽出パラメータを制御する設定 |
Rust
use pdf_oxide::extractors::SpanMergingConfig;
let mut doc = PdfDocument::open("report.pdf")?;
let config = SpanMergingConfig::adaptive();
let spans = doc.extract_spans_with_config(0, config)?;
extract_chars(page_index) -> list[TextChar]
正確なバウンディングボックス、フォントメタデータ、変換プロパティを持つ個別文字を抽出します。これは低レベルAPIです。ほとんどのユースケースでは extract_text() または extract_spans() を推奨します。文字抽出はテキストのグループ化と結合をスキップするため、スパン抽出より30〜50%高速です。
| パラメータ | 型 | 説明 |
|---|---|---|
page_index |
int / usize |
ゼロベースのページインデックス |
戻り値: TextChar オブジェクトのリスト/ベクター。
TextCharフィールド
| フィールド | 型 | 説明 |
|---|---|---|
char |
char |
文字 |
bbox |
Rect |
バウンディングボックス(x, y, width, height) |
font_name |
str |
フォント名/ファミリー |
font_size |
f32 |
フォントサイズ(ポイント単位) |
font_weight |
FontWeight |
ウェイト(Normal、Boldなど) |
is_italic |
bool |
イタリックフラグ |
color |
Color |
RGBカラー(各チャンネル0.0〜1.0) |
mcid |
Option<u32> |
マークドコンテンツID |
origin_x |
f32 |
ベースライン原点のX座標 |
origin_y |
f32 |
ベースライン原点のY座標 |
rotation_degrees |
f32 |
テキストの回転角度(0〜360、時計回り) |
advance_width |
f32 |
次の文字位置までの水平距離 |
matrix |
[f32; 6] |
完全な変換行列 [a, b, c, d, e, f] |
Python
doc = PdfDocument("report.pdf")
chars = doc.extract_chars(0)
for ch in chars:
print(f"'{ch.char}' at ({ch.bbox[0]:.1f}, {ch.bbox[1]:.1f}) "
f"font={ch.font_name} size={ch.font_size:.1f}")
<!-- Node.js: extractChars not yet in binding (js/src/index.ts) -->
Go
doc, _ := pdfoxide.Open("report.pdf")
defer doc.Close()
chars, _ := doc.ExtractChars(0)
for _, ch := range chars {
fmt.Printf("'%c' at (%.1f, %.1f) font=%s size=%.1f\n",
ch.Char, ch.X, ch.Y, ch.FontName, ch.FontSize)
}
C#
using var doc = PdfDocument.Open("report.pdf");
var chars = doc.ExtractChars(0);
foreach (var ch in chars)
{
Console.WriteLine($"'{ch.Char}' at ({ch.X:F1}, {ch.Y:F1}) {ch.W:F1}x{ch.H:F1}");
}
WASM
const doc = new WasmPdfDocument(bytes);
const chars = doc.extractChars(0);
for (const ch of chars) {
console.log(`'${ch.char}' at (${ch.bbox[0].toFixed(1)}, ${ch.bbox[1].toFixed(1)}) font=${ch.fontName} size=${ch.fontSize.toFixed(1)}`);
}
Rust
let mut doc = PdfDocument::open("report.pdf")?;
let chars = doc.extract_chars(0)?;
for ch in &chars {
println!(
"'{}' origin=({:.1}, {:.1}) rotation={:.0} advance={:.1}",
ch.char, ch.origin_x, ch.origin_y,
ch.rotation_degrees, ch.advance_width,
);
}
C++
auto doc = pdf_oxide::Document::open("report.pdf");
auto chars = doc.extract_chars(0);
for (const auto& ch : chars) {
std::printf("U+%04X at (%.1f, %.1f) font=%s size=%.1f\n",
ch.character, ch.bbox.x, ch.bbox.y,
ch.font_name.c_str(), ch.font_size);
}
Swift
let doc = try Document.open("report.pdf")
let chars = try doc.extractChars(0)
for ch in chars {
let scalar = String(UnicodeScalar(ch.character)!)
print("'\(scalar)' at (\(ch.bbox.x), \(ch.bbox.y)) font=\(ch.fontName) size=\(ch.fontSize)")
}
Dart
final doc = PdfDocument.open('report.pdf');
final chars = doc.extractChars(0);
for (final ch in chars) {
final glyph = String.fromCharCode(ch.character);
print("'$glyph' at (${ch.bbox.x}, ${ch.bbox.y}) font=${ch.fontName} size=${ch.fontSize}");
}
R
doc <- pdf_open("report.pdf")
chars <- pdf_extract_chars(doc, 0)
for (ch in chars) {
cat(sprintf("U+%04X at (%.1f, %.1f) font=%s size=%.1f\n",
ch$character, ch$bbox$x, ch$bbox$y, ch$font_name, ch$font_size))
}
Julia
doc = open_document("report.pdf")
chars = extract_chars(doc, 0)
for ch in chars
glyph = Char(ch.character)
println("'$glyph' at ($(ch.bbox.x), $(ch.bbox.y)) font=$(ch.font_name) size=$(ch.font_size)")
end
Zig
var doc = try pdf_oxide.Document.open("report.pdf");
const chars = try doc.extractChars(a, 0);
for (chars) |ch| {
std.debug.print("U+{X:0>4} at ({d:.1}, {d:.1}) font={s} size={d:.1}\n",
.{ ch.character, ch.bbox.x, ch.bbox.y, ch.fontName, ch.fontSize });
}
Objective-C
POXDocument *doc = [POXDocument openPath:@"report.pdf" error:&err];
NSArray<POXChar*> *chars = [doc extractChars:0 error:&err];
for (POXChar *ch in chars) {
NSLog(@"U+%04X at (%.1f, %.1f) font=%@ size=%.1f",
ch.character, ch.bbox.x, ch.bbox.y, ch.fontName, ch.fontSize);
}
Elixir
{:ok, doc} = PdfOxide.open("report.pdf")
{:ok, chars} = PdfOxide.extract_chars(doc, 0)
Enum.each(chars, fn ch ->
glyph = <<ch.character::utf8>>
IO.puts("'#{glyph}' at (#{ch.bbox.x}, #{ch.bbox.y}) font=#{ch.font_name} size=#{ch.font_size}")
end)
extract_page_text(page_index) -> PageText
単一の抽出パスからスパン、文字、ページ寸法を取得します。ページコンテンツストリームを一度だけ解析するため、extract_spans() と extract_chars() を個別に呼び出すより効率的です。
| パラメータ | 型 | 説明 |
|---|---|---|
page_index |
int / usize |
ゼロベースのページインデックス |
戻り値: フィールド spans、chars、page_width、page_height、text を持つ PageText オブジェクト(PythonではDict、JSではObject)。
Python
doc = PdfDocument("report.pdf")
result = doc.extract_page_text(0)
# result is a dict with: spans, chars, page_width, page_height, text
for span in result["spans"]:
print(f"'{span.text}' font={span.font_name} size={span.font_size}")
<!-- Node.js: extractPageText not yet in binding (js/src/index.ts) --> <!-- Go: ExtractPageText not yet in binding (go/pdf_oxide.go) --> <!-- C#: ExtractPageText not yet in binding (csharp/PdfOxide/Core/PdfDocument.cs) -->
WASM
const result = doc.extractPageText(0);
// result has: spans, chars, pageWidth, pageHeight, text
for (const span of result.spans) {
console.log(`'${span.text}' font=${span.fontName} size=${span.fontSize}`);
}
Rust
let mut doc = PdfDocument::open("report.pdf")?;
let result = doc.extract_page_text(0)?;
println!("Page is {}x{} pt", result.page_width, result.page_height);
for span in &result.spans {
println!("'{}' font={} size={:.1}", span.text, span.font_name, span.font_size);
}
カラム対応の読み順
多段組みPDF(研究論文、新聞など)では、カラム対応の読み順を使用することで、カラムをまたいで読むのではなく各カラムを独立して読み込めます:
Python
# Default: top-to-bottom (reads across columns)
spans = doc.extract_spans(0)
# Column-aware: reads each column separately
spans = doc.extract_spans(0, reading_order="column_aware")
<!-- Node.js: extractSpans not yet in binding (js/src/index.ts) --> <!-- Go: ExtractSpans not yet in binding (go/pdf_oxide.go) --> <!-- C#: ExtractSpans not yet in binding (csharp/PdfOxide/Core/PdfDocument.cs) -->
WASM
const spans = doc.extractSpans(0, undefined, "column_aware");
Rust
use pdf_oxide::extractors::ReadingOrder;
let spans = doc.extract_spans_with_reading_order(0, ReadingOrder::ColumnAware)?;
to_plain_text(page_index, options) -> str
単一ページをプレーンテキストに変換します。APIの一貫性のために変換オプションを受け付けますが、ほとんどのオプションは主にMarkdown/HTML出力に適用されます。
| パラメータ | 型 | デフォルト | 説明 |
|---|---|---|---|
page_index |
int / usize |
– | ゼロベースのページインデックス |
preserve_layout |
bool |
false |
視覚的レイアウトを保持 |
detect_headings |
bool |
true |
見出しを検出 |
include_images |
bool |
true |
画像を含める |
image_output_dir |
str / None |
None |
画像出力ディレクトリ |
Python
doc = PdfDocument("paper.pdf")
text = doc.to_plain_text(0)
Node.js
const doc = new PdfDocument("paper.pdf");
const text = doc.toPlainText(0);
Go
doc, _ := pdfoxide.Open("paper.pdf")
defer doc.Close()
text, _ := doc.ToPlainText(0)
C#
using var doc = PdfDocument.Open("paper.pdf");
string text = doc.ToPlainText(0);
WASM
const doc = new WasmPdfDocument(bytes);
const text = doc.extractText(0);
Rust
use pdf_oxide::converters::ConversionOptions;
let mut doc = PdfDocument::open("paper.pdf")?;
let options = ConversionOptions::default();
let text = doc.to_plain_text(0, &options)?;
C++
auto doc = pdf_oxide::Document::open("paper.pdf");
auto text = doc.to_plain_text(0);
Swift
let doc = try Document.open("paper.pdf")
let text = try doc.toPlainText(0)
Dart
final doc = PdfDocument.open('paper.pdf');
final text = doc.toPlainText(0);
R
doc <- pdf_open("paper.pdf")
text <- pdf_to_plain_text(doc, 0)
Julia
doc = open_document("paper.pdf")
text = to_plain_text(doc, 0)
Zig
var doc = try pdf_oxide.Document.open("paper.pdf");
const text = try doc.toPlainText(a, 0);
Objective-C
POXDocument *doc = [POXDocument openPath:@"paper.pdf" error:&err];
NSString *text = [doc toPlainText:0 error:&err];
Elixir
{:ok, doc} = PdfOxide.open("paper.pdf")
{:ok, text} = PdfOxide.to_plain_text(doc, 0)
extract_hierarchical_content(page_index) -> Option<StructureElement>
ページコンテンツを階層的な構造ツリーとして抽出します。タグなしPDFの場合は None を返します。タグ付きPDFの場合、ドキュメントの論理構造(見出し、段落、テーブル、図)を表す StructureElement ツリーを返します。
| パラメータ | 型 | 説明 |
|---|---|---|
page_index |
int / usize |
ゼロベースのページインデックス |
Rust
let mut doc = PdfDocument::open("tagged-report.pdf")?;
if let Some(root) = doc.extract_hierarchical_content(0)? {
println!("Structure type: {:?}", root.structure_type);
for child in &root.children {
println!(" Child: {:?}", child.structure_type);
}
}
応用例
スパンから単語頻度テーブルを作成する
from collections import Counter
from pdf_oxide import PdfDocument
doc = PdfDocument("report.pdf")
words = Counter()
for page in range(doc.page_count()):
text = doc.extract_text(page)
for word in text.split():
words[word.lower().strip(".,;:!?\"'()[]")] += 1
for word, count in words.most_common(20):
print(f"{word:20s} {count}")
スパンのメタデータを使って太字の見出しを検出する
use pdf_oxide::PdfDocument;
use pdf_oxide::layout::FontWeight;
let mut doc = PdfDocument::open("paper.pdf")?;
let spans = doc.extract_spans(0)?;
let headings: Vec<_> = spans.iter()
.filter(|s| s.font_weight == FontWeight::Bold && s.font_size > 14.0)
.collect();
for h in headings {
println!("Heading: '{}' ({}pt)", h.text, h.font_size);
}
文字データをCSVにエクスポートする
import csv
from pdf_oxide import PdfDocument
doc = PdfDocument("report.pdf")
chars = doc.extract_chars(0)
with open("characters.csv", "w", newline="") as f:
writer = csv.writer(f)
writer.writerow(["char", "x", "y", "width", "height", "font", "size"])
for ch in chars:
writer.writerow([
ch.char, ch.bbox[0], ch.bbox[1],
ch.bbox[2], ch.bbox[3],
ch.font_name, ch.font_size,
])
ベクターパスを抽出する
extract_paths() はページからベクターパスデータ(直線、曲線、矩形)を返します。表の罫線、区切り線、グラフィック要素の検出に役立ちます。
doc = PdfDocument("report.pdf")
paths = doc.extract_paths(0)
for path in paths:
for op in path["operations"]:
print(f"{op['type']}: {op.get('x', '')}, {op.get('y', '')}")
# types: move_to, line_to, curve_to, rectangle, close_path
関連ページ
- Markdown変換 – テキストを構造化されたMarkdownに変換
- HTML変換 – フォーマット付きのHTMLにテキストを変換
- テキスト検索 – 正規表現で抽出したテキストを検索
- メタデータ・XMP – ドキュメントレベルのメタデータを読み取る