Skip to content

Видобування тексту

PDF Oxide надає кілька рівнів видобування тексту: повний текст сторінки, стилізовані span-и з метаданими шрифту та окремі символи з точним позиціонуванням. Використовуйте extract_text() для швидкого отримання вмісту, extract_spans() коли потрібні дані про шрифт і позицію, та extract_chars() для посимвольного аналізу — наприклад, у власних движках верстки або при постобробці OCR.

Для тегованих PDF видобування тексту автоматично слідує дереву структури документа, щоб забезпечити правильний порядок читання. Для нетегованих PDF використовується порядок вмісту сторінки з інтелектуальним визначенням переносів рядків — включаючи захист одноколонкового макету, що запобігає фрагментації основного тексту в документах у стилі RFC та дисертацій.

Підтримка порядку читання

Конвеєр визначення порядку читання забезпечує коректний вивід для різних систем письма та макетів:

  • Latin — стандартний порядок зліва направо, зверху вниз із визначенням колонок.
  • Arabic — обертання попередньо сформованих span-ів (Pass 0) розташовує символи в логічному порядку читання замість візуального.
  • CJK — просторовий детектор таблиць зберігає колонки з мітками rowspan; 3-точкова квантизація по осі Y запобігає змішуванню табличного вмісту з текстом.
  • Повернуті / згенеровані dvips PDF — відкидання викидів на основі медіани при визначенні колонок обробляє вироджені CTM-координати.
  • Багатоколонкові наукові статті — одноколонковий захист XYCut усуває фрагментацію; рядково-усвідомлена сортировка span-ів обробляє табличний вміст усередині текстових блоків.

Сегментація слів і рядків

extract_words() і extract_text_lines() приймають необов’язкові іменовані параметри для налаштування порогів розбиття на слова та рядки:

Параметр За замовчуванням Опис
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 надають 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 і використовує дерево структури для визначення порядку читання, якщо воно доступне. Вставляє переноси рядків і пробіли на основі вертикальних і горизонтальних проміжків між span-ами.

Параметр Тип Опис
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]

Видобуває текст у вигляді span-ів — безперервних фрагментів тексту з однаковим шрифтом і стилем. Кожен span містить текстовий вміст, обмежувальний прямокутник, назву шрифту, розмір шрифту, насиченість, прапор курсиву та колір. Це рекомендований підхід для більшості завдань видобування, що потребують інформації про макет або шрифт.

Параметр Тип Опис
page_index int / usize Індекс сторінки (починаючи з нуля)

Повертає: Список/вектор об’єктів TextSpan.

Поля TextSpan

Поле Тип Опис
text str Текстовий вміст span-у
bbox Rect Обмежувальний прямокутник (x, y, ширина, висота)
font_name str Назва/сімейство шрифту (наприклад, “Helvetica”, “TimesNewRoman”)
font_size f32 Розмір шрифту в пунктах
font_weight FontWeight Насиченість: Normal, Bold, Light, SemiBold тощо
is_italic bool Чи є span курсивним
color Color RGB-колір (r, g, b) зі значеннями від 0,0 до 1,0
mcid Option<u32> Ідентифікатор позначеного вмісту для тегованих PDF
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>

Видобуває span-и з користувацькою конфігурацією злиття. Використовуйте цей метод, коли стандартна поведінка злиття дає неправильні межі слів для вашого документа.

Параметр Тип Опис
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% швидше, ніж видобування span-ів, оскільки пропускає етап групування та злиття тексту.

Параметр Тип Опис
page_index int / usize Індекс сторінки (починаючи з нуля)

Повертає: Список/вектор об’єктів TextChar.

Поля TextChar

Поле Тип Опис
char char Символ
bbox Rect Обмежувальний прямокутник (x, y, ширина, висота)
font_name str Назва/сімейство шрифту
font_size f32 Розмір шрифту в пунктах
font_weight FontWeight Насиченість (Normal, Bold тощо)
is_italic bool Прапор курсиву
color Color RGB-колір (від 0,0 до 1,0 на канал)
mcid Option<u32> Ідентифікатор позначеного вмісту
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

Отримує span-и, символи та розміри сторінки за один прохід видобування. Ефективніше, ніж окремі виклики extract_spans() + extract_chars(), оскільки потік вмісту сторінки аналізується лише один раз.

Параметр Тип Опис
page_index int / usize Індекс сторінки (починаючи з нуля)

Повертає: Об’єкт PageText (dict у Python / object у JS) з полями: spans, chars, page_width, page_height, text.

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);
    }
}

Розширені приклади

Побудова таблиці частотності слів із span-ів

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}")

Виявлення жирних заголовків за метаданими span-ів

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

Пов’язані сторінки