Skip to content

Bereichsextraktion — Inhalte aus einem bestimmten Bereich abrufen

Bei der Verarbeitung von Rechnungen, Kontoauszügen, Steuerformularen oder beliebigen Template-Layouts wissen Sie meistens bereits, wo sich die Felder befinden. Statt die gesamte Seite zu extrahieren und nach dem Wert zu suchen, weisen Sie PDF Oxide einfach auf das exakte Rechteck hin und erhalten nur das, was dort steht.

Die fluente within(page, rect)-API gibt einen Bereich mit eingeschränktem Geltungsbereich zurück, auf dem Sie Extraktionsmethoden verketten können: extract_text(), extract_words(), extract_chars(), extract_tables().

Binding-Abdeckung. within(page, rect) ist in Python, Rust und WASM verfügbar. Go und C# bieten gleichwertige Low-Level-Hilfsfunktionen (ExtractTextInRect, ExtractWordsInRect, ExtractImagesInRect) — siehe unten. Die vollständige in-rect-Familie (Text, Wörter, Zeilen, Tabellen, Bilder) ist in Rust, dem C ABI und dem Swift-Wrapper vollständig verfügbar; welches Binding was unterstützt, entnehmen Sie Varianten der in-rect-Extraktion.

Schnellbeispiel

rect ist (x, y, width, height) in PDF-Punkten, mit dem Ursprung an der linken unteren Ecke der Seite. Letter-Seiten sind 612 × 792 Punkte groß.

Python

from pdf_oxide import PdfDocument

doc = PdfDocument("invoice.pdf")

# Top 92 points of page 0 — typical header band
header = doc.within(0, (0, 700, 612, 92)).extract_text()
print(header)

Rust

use pdf_oxide::PdfDocument;
use pdf_oxide::geometry::Rect;

let mut doc = PdfDocument::open("invoice.pdf")?;
let header = doc.within(0, Rect::new(0.0, 700.0, 612.0, 92.0)).extract_text()?;
println!("{}", header);

JavaScript (WASM)

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

const doc = new WasmPdfDocument(bytes);
const headerRegion = doc.within(0, [0, 700, 612, 92]);
console.log(headerRegion.extractText());
doc.free();

Go (Low-Level-Hilfsfunktion, gleicher Effekt)

package main

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

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

    // ExtractTextInRect(pageIndex, x, y, width, height)
    header, _ := doc.ExtractTextInRect(0, 0, 700, 612, 92)
    fmt.Println(header)
}

C# (Low-Level-Hilfsfunktion)

using PdfOxide;

using var doc = PdfDocument.Open("invoice.pdf");
string header = doc.ExtractTextInRect(0, 0, 700, 612, 92);
Console.WriteLine(header);

Java (page.text(region); BBox im Eckpunkt-Format (x0, y0, x1, y1))

import fyi.oxide.pdf.PdfDocument;
import fyi.oxide.pdf.geometry.BBox;

try (PdfDocument doc = PdfDocument.open(java.nio.file.Path.of("invoice.pdf"))) {
    // Top 92 points of page 0 → corners (0, 700) … (612, 792)
    String header = doc.page(0).text(new BBox(0, 700, 612, 792));
    System.out.println(header);
}

Kotlin

import fyi.oxide.pdf.PdfDocument
import fyi.oxide.pdf.geometry.BBox

PdfDocument.open(java.nio.file.Path.of("invoice.pdf")).use { doc ->
    val header = doc.page(0).text(BBox(0.0, 700.0, 612.0, 792.0))
    println(header)
}

Scala

import fyi.oxide.pdf.PdfDocument
import fyi.oxide.pdf.geometry.BBox
import scala.util.Using

Using.resource(PdfDocument.open("invoice.pdf")) { doc =>
  val header = doc.page(0).text(BBox(0, 700, 612, 792))
  println(header)
}

Clojure

(require '[pdf-oxide.core :as pdf])
(import '[fyi.oxide.pdf.geometry BBox])

(with-open [doc (pdf/open "invoice.pdf")]
  ;; Top 92 points of page 0 → corners (0 700) … (612 792)
  (println (pdf/page-text (pdf/page doc 0) (BBox. 0 700 612 792))))

C++

#include <pdf_oxide/pdf_oxide.hpp>

auto doc = pdf_oxide::Document::open("invoice.pdf");
// extract_text_in_rect(page, x, y, w, h)
auto header = doc.extract_text_in_rect(0, 0, 700, 612, 92);
std::cout << header << "\n";

Swift

import PdfOxide

let doc = try Document.open("invoice.pdf")
let header = try doc.extractTextInRect(0, x: 0, y: 700, w: 612, h: 92)
print(header)

Dart

import 'package:pdf_oxide/pdf_oxide.dart';

final doc = PdfDocument.open('invoice.pdf');
final header = doc.extractTextInRect(0, 0, 700, 612, 92);
print(header);
doc.close();

R

library(pdfoxide)

doc <- pdf_open("invoice.pdf")
# pdf_extract_text_in_rect(doc, page, x, y, width, height)
header <- pdf_extract_text_in_rect(doc, 0, 0, 700, 612, 92)
cat(header)

Julia

using PdfOxide

doc = open_document("invoice.pdf")
header = extract_text_in_rect(doc, 0, 0, 700, 612, 92)
println(header)

Zig

const pdf_oxide = @import("pdf_oxide");
const a = std.heap.page_allocator;

var doc = try pdf_oxide.Document.open("invoice.pdf");
const header = try doc.extractTextInRect(a, 0, 0, 700, 612, 92);  // free header
std.debug.print("{s}\n", .{header});

Objective-C

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

POXDocument *doc = [POXDocument openPath:@"invoice.pdf" error:&err];
NSString *header = [doc extractTextInRect:0 x:0 y:700 w:612 h:92 error:&err];
NSLog(@"%@", header);

Elixir

{:ok, doc} = PdfOxide.open("invoice.pdf")
# extract_text_in_rect(doc, page, x, y, w, h)
{:ok, header} = PdfOxide.extract_text_in_rect(doc, 0, 0, 700, 612, 92)
IO.puts(header)

Verkettete Extraktion aus einem Bereich

Die fluente within()-Form in Python / Rust / WASM ermöglicht es, beliebige Extraktionsmethoden auf demselben eingeschränkten Bereich aufzurufen, ohne das Rechteck erneut angeben zu müssen:

Python

doc = PdfDocument("invoice.pdf")
region = doc.within(0, (400, 100, 200, 200))   # bottom-right 200×200 box

total_text = region.extract_text()              # plain text
words      = region.extract_words()             # word-level records
chars      = region.extract_chars()             # character-level records

Rust

let region = doc.within(0, Rect::new(400.0, 100.0, 200.0, 200.0));
let text  = region.extract_text()?;
let words = region.extract_words()?;

C++ (keine fluente Verkettung — jede in-rect-Hilfsfunktion für dasselbe Rechteck einzeln aufrufen)

// bottom-right 200×200 box: x=400, y=100, w=200, h=200
auto text  = doc.extract_text_in_rect(0, 400, 100, 200, 200);
auto words = doc.extract_words_in_rect(0, 400, 100, 200, 200);
auto lines = doc.extract_lines_in_rect(0, 400, 100, 200, 200);

Swift

let text  = try doc.extractTextInRect(0, x: 400, y: 100, w: 200, h: 200)
let words = try doc.extractWordsInRect(0, x: 400, y: 100, w: 200, h: 200)

Dart

final text  = doc.extractTextInRect(0, 400, 100, 200, 200);
final words = doc.extractWordsInRect(0, 400, 100, 200, 200);

R

text  <- pdf_extract_text_in_rect(doc, 0, 400, 100, 200, 200)
words <- pdf_extract_words_in_rect(doc, 0, 400, 100, 200, 200)

Julia

text  = extract_text_in_rect(doc, 0, 400, 100, 200, 200)
words = extract_words_in_rect(doc, 0, 400, 100, 200, 200)

Zig

const text  = try doc.extractTextInRect(a, 0, 400, 100, 200, 200);
const words = try doc.extractWordsInRect(a, 0, 400, 100, 200, 200);  // freeWords

Objective-C

NSString *text = [doc extractTextInRect:0 x:400 y:100 w:200 h:200 error:&err];
NSArray<POXWord*> *words = [doc extractWordsInRect:0 x:400 y:100 w:200 h:200 error:&err];

Elixir

{:ok, text}  = PdfOxide.extract_text_in_rect(doc, 0, 400, 100, 200, 200)
{:ok, words} = PdfOxide.extract_words_in_rect(doc, 0, 400, 100, 200, 200)

Typische Anwendungsfälle

Rechnungsfelder extrahieren

Eine Rechnung enthält Lieferantenadresse, Rechnungsnummer und Positionstabelle meist in festen Zonen. Definieren Sie die Rechtecke einmal pro Template:

from pdf_oxide import PdfDocument

TEMPLATES = {
    "acme_v1": {
        "invoice_no":  (450, 720,  120,  20),
        "issue_date":  (450, 700,  120,  20),
        "vendor_name": ( 50, 740,  300,  40),
        "total":       (450, 100,  120,  24),
    },
}

def parse_invoice(path, template):
    doc = PdfDocument(path)
    out = {}
    for field, rect in template.items():
        out[field] = doc.within(0, rect).extract_text().strip()
    return out

print(parse_invoice("invoice-2025-04.pdf", TEMPLATES["acme_v1"]))

Kontoauszug-Buchungszeilen

Die meisten Kontoauszüge haben einen schmalen „Transaktions"-Streifen. Schneiden Sie diesen Bereich aus und rufen Sie extract_words() auf, um jede Zeile in Lesereihenfolge mit ihrem Bounding Box zu erhalten:

doc = PdfDocument("statement.pdf")
for page in range(doc.page_count()):
    txn_region = doc.within(page, (36, 72, 540, 650))   # skip header + footer
    for w in txn_region.extract_words():
        print(f"page {page}: {w.text} at ({w.x0:.0f},{w.y0:.0f})")

Kopf- und Fußzeilen entfernen

Wenn Sie nur den Fließtext indexieren möchten, schneiden Sie den oberen und unteren Rand jeder Seite weg:

Rust

let mut doc = PdfDocument::open("book.pdf")?;
for i in 0..doc.page_count()? {
    let body = doc.within(i, Rect::new(0.0, 100.0, 612.0, 600.0))
                  .extract_text()?;
    // index `body` …
}

Tabellenbereiche erkennen

Wenn Sie wissen, dass eine Seite eine Tabelle enthält und wo sie sich befindet, begrenzen Sie den Bereich auf das Tabellenrechteck und lassen Sie extract_tables() nur auf diesen Bereich fokussieren:

Python

tables = doc.within(0, (50, 200, 500, 400)).extract_tables()
for t in tables:
    for row in t["rows"]:
        print([c["text"] for c in row["cells"]])

Welche Varianten der rechteckbegrenzten Extraktion gibt es? {#what-rect-scoped-extraction-variants-exist}

Über extract_text(), extract_words() und extract_chars() hinaus gibt es zwei weitere rechteckbegrenzte Varianten, die geometriebewusste Ergebnisse aus einem einzelnen Rechteck liefern: Zeilen im Rechteck und Tabellen im Rechteck. Beide filtern eine Ganzseiten-Extraktion auf die Bereiche, deren Bounding Box mit Ihrem Rechteck überschneidet, sodass die zurückgegebenen Koordinaten und die Lesereihenfolge dieselben wie bei einem Ganzseiten-Aufruf sind — nur ausgeschnitten.

Textzeilen in einem Bereich extrahieren (extract_lines_in_rect)

Gibt die Datensätze auf Zeilenebene (jeweils mit Text, Bounding Box und Wortzahl) zurück, die innerhalb des Rechtecks liegen. Verwenden Sie diese Methode, wenn Sie ganze Zeilen in Lesereihenfolge benötigen statt einzelner Wörter — z. B. Adressblöcke, mehrzeilige Summen oder eine einzelne Kontoauszugszeile.

Die C-ABI-Signatur ist die maßgebliche Referenz:

FfiTextLineList *pdf_document_extract_lines_in_rect(
    PdfDocument *handle,
    int32_t page_index,
    float x, float y, float w, float h,
    int32_t *error_code);

Rustextract_lines_in_rect(page_index, region) -> Result<Vec<PathContent>> auf PdfDocument:

use pdf_oxide::PdfDocument;
use pdf_oxide::geometry::Rect;

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

// Transactions band: skip the header (top 92pt) and footer (bottom 72pt)
let region = Rect::new(36.0, 72.0, 540.0, 628.0);
let lines = doc.extract_lines_in_rect(0, region)?;
for line in &lines {
    println!("{:?}", line.bbox);
}

Python — der fluente Bereich stellt Zeilen über extract_text_lines() bereit:

from pdf_oxide import PdfDocument

doc = PdfDocument("statement.pdf")

# Same band as the Rust example above
region = doc.within(0, (36, 72, 540, 628))
for line in region.extract_text_lines():
    print(line.text, line.bbox)

SwiftextractLinesInRect(_:x:y:w:h:) gibt [TextLine] zurück:

import PdfOxide

let doc = try PdfDocument(path: "statement.pdf")
let lines = try doc.extractLinesInRect(0, x: 36, y: 72, w: 540, h: 628)
for line in lines {
    print(line.text, line.bbox, line.wordCount)
}

C++extract_lines_in_rect(page, x, y, w, h) gibt std::vector<TextLine> zurück:

auto lines = doc.extract_lines_in_rect(0, 36, 72, 540, 628);
for (const auto& line : lines) {
    std::cout << line.text << "\n";
}

DartextractLinesInRect(page, x, y, w, h) gibt List<TextLine> zurück:

final lines = doc.extractLinesInRect(0, 36, 72, 540, 628);
for (final line in lines) {
    print('${line.text} ${line.bbox}');
}

Rpdf_extract_lines_in_rect(doc, page, x, y, width, height):

lines <- pdf_extract_lines_in_rect(doc, 0, 36, 72, 540, 628)

Juliaextract_lines_in_rect(doc, page, x, y, w, h):

lines = extract_lines_in_rect(doc, 0, 36, 72, 540, 628)
for line in lines
    println(line.text, " ", line.bbox)
end

ZigextractLinesInRect(allocator, page, x, y, w, h):

const lines = try doc.extractLinesInRect(a, 0, 36, 72, 540, 628);  // freeTextLines

Objective-CextractLinesInRect:x:y:w:h: gibt NSArray<POXTextLine*> zurück:

NSArray<POXTextLine*> *lines = [doc extractLinesInRect:0 x:36 y:72 w:540 h:628 error:&err];

Elixirextract_lines_in_rect(doc, page, x, y, w, h):

{:ok, lines} = PdfOxide.extract_lines_in_rect(doc, 0, 36, 72, 540, 628)

Go / C#. Der C-Einstiegspunkt extract_lines_in_rect existiert, wird aber von den Go- und C#-Wrappern noch nicht bereitgestellt. Extrahieren Sie in diesen Sprachen die Zeilen für die gesamte Seite und filtern Sie nach den zurückgegebenen Bounding Boxes, oder verwenden Sie ExtractWordsInRect (Go) und gruppieren Sie die Wörter selbst zu Zeilen.

Tabellen in einem Bereich extrahieren (extract_tables_in_rect)

Begrenzt die Tabellenerkennung auf ein einzelnes Rechteck — es werden nur Tabellen zurückgegeben, deren Bounding Box das Rechteck schneidet. Dies ist die geometriebewusste Entsprechung des oben gezeigten fluenten within(...).extract_tables().

C-ABI-Signatur:

FfiTableList *pdf_document_extract_tables_in_rect(
    PdfDocument *handle,
    int32_t page_index,
    float x, float y, float w, float h,
    int32_t *error_code);

Rustextract_tables_in_rect(page_index, region) -> Result<Vec<Table>> (die Variante ..._with_config nimmt eine benutzerdefinierte TableDetectionConfig entgegen):

use pdf_oxide::PdfDocument;
use pdf_oxide::geometry::Rect;

let doc = PdfDocument::open("invoice.pdf")?;
let region = Rect::new(50.0, 200.0, 500.0, 400.0);
let tables = doc.extract_tables_in_rect(0, region)?;
for table in &tables {
    println!("{} rows × {} cols", table.rows.len(), table.col_count);
}

Python — über den fluenten Bereich:

from pdf_oxide import PdfDocument

doc = PdfDocument("invoice.pdf")
tables = doc.within(0, (50, 200, 500, 400)).extract_tables()
for t in tables:
    for row in t["rows"]:
        print([c["text"] for c in row["cells"]])

SwiftextractTablesInRect(_:x:y:w:h:) gibt [Table] zurück:

let tables = try doc.extractTablesInRect(0, x: 50, y: 200, w: 500, h: 400)
for table in tables {
    print("\(table.rowCount) rows, header: \(table.hasHeader)")
}

C++extract_tables_in_rect(page, x, y, w, h) gibt std::vector<Table> zurück:

auto tables = doc.extract_tables_in_rect(0, 50, 200, 500, 400);
for (const auto& table : tables) {
    std::cout << table.rows.size() << " rows\n";
}

DartextractTablesInRect(page, x, y, w, h) gibt List<Table> zurück:

final tables = doc.extractTablesInRect(0, 50, 200, 500, 400);
for (final table in tables) {
    print('${table.rows.length} rows');
}

Rpdf_extract_tables_in_rect(doc, page, x, y, width, height):

tables <- pdf_extract_tables_in_rect(doc, 0, 50, 200, 500, 400)

Juliaextract_tables_in_rect(doc, page, x, y, w, h):

tables = extract_tables_in_rect(doc, 0, 50, 200, 500, 400)

ZigextractTablesInRect(allocator, page, x, y, w, h):

const tables = try doc.extractTablesInRect(a, 0, 50, 200, 500, 400);

Objective-CextractTablesInRect:x:y:w:h: gibt NSArray<POXTable*> zurück:

NSArray<POXTable*> *tables = [doc extractTablesInRect:0 x:50 y:200 w:500 h:400 error:&err];

Elixirextract_tables_in_rect(doc, page, x, y, w, h):

{:ok, tables} = PdfOxide.extract_tables_in_rect(doc, 0, 50, 200, 500, 400)

Go / C#. Wie bei den Zeilen existiert der C-Einstiegspunkt extract_tables_in_rect, ist aber in Go oder C# noch nicht eingebunden. Rufen Sie ExtractTables(page) für die gesamte Seite auf und behalten Sie nur die Tabellen, deren Bounding Box in Ihr Rechteck fällt.

Wie extrahiere ich eine Seite automatisch, ohne Text oder OCR zu wählen?

Wenn Sie nicht wissen, ob eine Seite digitalen Text, einen Scan oder eine Mischung enthält, übernimmt extract_page_auto das Routing. Es führt den AutoExtractor aus — bereichsweises Text-vs.-OCR-Routing mit elegantem nativem Fallback (ohne undurchsichtige OCR-Fehler) — und gibt ein JSON-PageExtraction zurück: einen Seiten-kind, den in Lesereihenfolge zusammengestellten text, eine confidence, einen typisierten reason, ein ocr_used-Flag und ein regions[]-Array, in dem jeder Bereich bbox, kind, text, confidence, source und reason trägt (bbox und reason sind auch dann vorhanden, wenn der Text eines Bereichs leer ist, damit die Lesereihenfolge nie stillschweigend beschädigt wird).

Es ist {}-tolerant: Übergeben Sie ein leeres/null Options-JSON für die Standardwerte oder ein AutoExtractOptions-Objekt. Die erkannten Felder (serialisiert als snake_case) sind:

Feld Typ Standard Bedeutung
mode "text_only" | "auto" | "force_ocr" "auto" Text-vs.-OCR-Routing-Strategie
reconstruct_image_tables bool true Nur-Bild-Tabellen über den räumlichen Detektor auf OCR-Spans neu aufbauen
emit_placeholders bool true Positionierte Figure/Table-Platzhalter in den Textfluss einfügen
ocr_languages string[] [] OCR-Sprachhinweise (z. B. ["english","chinese"])
min_text_confidence float | null null Konfidenz-Schwellenwert für die automatische Entscheidung
table_confidence float | null null Schwellenwert für die Bild-Tabellen-Rekonstruktion
force_ocr_pages int[] [] 0-basierte Seitenindizes, für die OCR erzwungen wird

OCR-Feature-Gate. OCR läuft tatsächlich nur, wenn die Bibliothek mit der Feature ocr gebaut wurde; andernfalls fällt extract_page_auto auf die native Textschicht zurück (kein Fehler). Der automatische Einstiegspunkt ist in Python, Go, C#, Swift, WASM und dem C ABI verfügbar. In Rust ist es die Bibliotheks-API AutoExtractor statt einer einzeiligen PdfDocument-Methode — siehe unten.

Pythonextract_page_auto(page, options_json=None) -> str (JSON):

import json
from pdf_oxide import PdfDocument

doc = PdfDocument("mixed-scan.pdf")

# Defaults (balanced preset)
page = json.loads(doc.extract_page_auto(0))
print(page["kind"], page["confidence"], page["ocr_used"])
for region in page["regions"]:
    print(region["kind"], region["bbox"], region["reason"])

# With options
opts = json.dumps({"mode": "auto", "reconstruct_image_tables": True,
                   "ocr_languages": ["english"]})
page = json.loads(doc.extract_page_auto(0, opts))

GoExtractPageAuto(pageIndex, opts ...AutoOption) (string, error) (gibt JSON zurück; Konfiguration über funktionale Optionen):

package main

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

func main() {
    doc, err := pdfoxide.Open("mixed-scan.pdf")
    if err != nil { log.Fatal(err) }
    defer doc.Close()

    raw, err := doc.ExtractPageAuto(0)
    if err != nil { log.Fatal(err) }

    var page map[string]any
    json.Unmarshal([]byte(raw), &page)
    fmt.Println(page["kind"], page["confidence"], page["ocr_used"])
}

C#ExtractPageAuto(int pageIndex, string? optionsJson = null) -> string (JSON):

using System.Text.Json;
using PdfOxide.Core;

using var doc = PdfDocument.Open("mixed-scan.pdf");

// Defaults
string json = doc.ExtractPageAuto(0);
using var page = JsonDocument.Parse(json);
Console.WriteLine(page.RootElement.GetProperty("kind"));

// With options
string opts = """{"mode":"auto","ocr_languages":["english"]}""";
string json2 = doc.ExtractPageAuto(0, opts);

SwiftextractPageAuto(_:optionsJson:) -> String (Standard: "{}"):

let json = try doc.extractPageAuto(0, optionsJson: "{}")

JavaScript (WASM)extractPageAuto(pageIndex, optionsJson?):

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

const doc = new WasmPdfDocument(bytes);
const page = JSON.parse(doc.extractPageAuto(0));
console.log(page.kind, page.confidence, page.ocr_used);
doc.free();

Rust — der automatische Pfad ist die Bibliotheks-API AutoExtractor. Erstellen Sie AutoExtractOptions (Presets fast(), balanced(), high_fidelity() oder den fluenten Builder) und rufen Sie extract_page auf; es wird ein typisiertes PageExtraction zurückgegeben (kein JSON-Roundtrip):

use pdf_oxide::PdfDocument;
use pdf_oxide::extractors::auto::{AutoExtractor, AutoExtractOptions, ExtractMode};

let doc = PdfDocument::open("mixed-scan.pdf")?;

// Default (balanced) preset
let page = AutoExtractor::new().extract_page(&doc, 0)?;
println!("{:?} conf={} ocr={}", page.kind, page.confidence, page.ocr_used);

// Custom options via the builder
let opts = AutoExtractOptions::builder()
    .mode(ExtractMode::Auto)
    .reconstruct_image_tables(true)
    .ocr_languages(["english"])
    .build();
let page = AutoExtractor::with(opts).extract_page(&doc, 0)?;
for region in &page.regions {
    println!("{:?} {:?} {:?}", region.kind, region.bbox, region.reason);
}

C++extract_page_auto(page, options_json = "") gibt den JSON-Umschlag zurück:

#include <pdf_oxide/pdf_oxide.hpp>

auto doc = pdf_oxide::Document::open("mixed-scan.pdf");
auto json = doc.extract_page_auto(0);                                    // defaults
auto json2 = doc.extract_page_auto(0, R"({"mode":"auto","ocr_languages":["english"]})");

DartextractPageAuto(page, [optionsJson]) gibt den JSON-Umschlag zurück:

import 'dart:convert';
import 'package:pdf_oxide/pdf_oxide.dart';

final doc = PdfDocument.open('mixed-scan.pdf');
final page = jsonDecode(doc.extractPageAuto(0));
print('${page["kind"]} ${page["confidence"]} ${page["ocr_used"]}');
doc.close();

Rpdf_extract_page_auto(doc, page, options_json = NULL) gibt JSON zurück:

library(jsonlite)

doc  <- pdf_open("mixed-scan.pdf")
page <- fromJSON(pdf_extract_page_auto(doc, 0))
cat(page$kind, page$confidence, page$ocr_used, "\n")

Juliaextract_page_auto(doc, page, options = "{}") gibt JSON zurück:

using PdfOxide, JSON

doc  = open_document("mixed-scan.pdf")
page = JSON.parse(extract_page_auto(doc, 0))
println(page["kind"], " ", page["confidence"], " ", page["ocr_used"])

ZigextractPageAuto(allocator, page, options_json) gibt JSON-Bytes zurück:

const json = try doc.extractPageAuto(a, 0, null);  // free json

Objective-CextractPageAuto:optionsJson:error: gibt den JSON-Umschlag zurück:

NSString *json = [doc extractPageAuto:0 optionsJson:@"{}" error:&err];

Elixirextract_page_auto(doc, page, options_json \\ "") gibt JSON zurück:

{:ok, json} = PdfOxide.extract_page_auto(doc, 0)
page = Jason.decode!(json)
IO.inspect({page["kind"], page["confidence"], page["ocr_used"]})

Java — der automatische Pfad ist die AutoExtractor-API (extractPage → typisiertes Ergebnis; extractTextForPage für reinen Text):

import fyi.oxide.pdf.PdfDocument;
import fyi.oxide.pdf.AutoExtractor;

try (PdfDocument doc = PdfDocument.open(java.nio.file.Path.of("mixed-scan.pdf"))) {
    AutoExtractor ax = AutoExtractor.of(doc);             // or .fast/.balanced/.highFidelity
    String text = ax.extractTextForPage(0);               // graceful native/OCR routing
    System.out.println(text);
}

Kotlin

import fyi.oxide.pdf.PdfDocument
import fyi.oxide.pdf.AutoExtractor

PdfDocument.open(java.nio.file.Path.of("mixed-scan.pdf")).use { doc ->
    val ax = AutoExtractor.of(doc)
    println(ax.extractTextForPage(0))
}

Scala

import fyi.oxide.pdf.{PdfDocument, AutoExtractor}
import scala.util.Using

Using.resource(PdfDocument.open("mixed-scan.pdf")) { doc =>
  val ax = AutoExtractor.of(doc)
  println(ax.extractTextForPage(0))
}

PHP — der umfangreiche JSON-Umschlag ist über AutoExtractor::extractPageJson erreichbar:

use PdfOxide\PdfDocument;
use PdfOxide\AutoExtractor;

$doc = PdfDocument::open('mixed-scan.pdf');
$ax  = AutoExtractor::balanced($doc);
$page = json_decode($ax->extractPageJson(0), true);
echo $page['kind'], ' ', $page['confidence'], ' ', $page['ocr_used'];

Rubyauto_extractor.extract_page(page) gibt den geparsten Umschlag als Hash zurück:

require 'pdf_oxide'

PdfOxide::PdfDocument.open('mixed-scan.pdf') do |doc|
  result = doc.auto_extractor.extract_page(0)
  cls = result[:classification]            # full PageExtraction JSON as a Hash
  puts [cls['kind'], cls['confidence'], cls['ocr_used']].join(' ')
end

Wie erhalte ich strukturierte typisierte Bereiche als JSON?

Für eine seitenübergreifende strukturierte Ansicht — Überschriften, Fließtextblöcke, Kopf-/Fußzeilen, Seitenzahlen und Spaltenreihenfolge — verwenden Sie den Einstiegspunkt zur strukturierten Extraktion. Er gibt eine StructuredPage zurück: page_index, page_width, page_height und ein regions[]-Array, in dem jeder Bereich kind (semantische Rolle), text, bbox, spans und column_index (für mehrspaltige Lesereihenfolge) trägt. Die Bereichs-kinds umfassen Fließtextblöcke, Strukturüberschriften (H1H6), Randnotizen, laufende Kopf-/Fußzeilen, Seitenzahlen und Artefakte.

Die meisten Bindings geben dies als JSON-String zurück (das C ABI serialisiert einmalig, Bindings deserialisieren in native Typen); Rust gibt die typisierte StructuredPage direkt zurück.

C-ABI-Signatur:

char *pdf_document_extract_structured_to_json(
    PdfDocument *handle,
    int32_t page_index,
    int32_t *error_code);

Pythonextract_structured(page) -> str (JSON; mit json.loads deserialisieren):

import json
from pdf_oxide import PdfDocument

doc = PdfDocument("report.pdf")
page = json.loads(doc.extract_structured(0))

print(page["page_width"], page["page_height"])
for region in page["regions"]:
    print(region["kind"], region["column_index"], region["text"][:60])

GoExtractStructured(page) (string, error):

raw, err := doc.ExtractStructured(0)
if err != nil { log.Fatal(err) }

var page map[string]any
json.Unmarshal([]byte(raw), &page)
for _, r := range page["regions"].([]any) {
    region := r.(map[string]any)
    fmt.Println(region["kind"], region["text"])
}

C#ExtractStructured(int page) -> string:

using System.Text.Json;

string json = doc.ExtractStructured(0);
using var page = JsonDocument.Parse(json);
foreach (var region in page.RootElement.GetProperty("regions").EnumerateArray())
{
    Console.WriteLine(region.GetProperty("kind"));
}

SwiftextractStructuredJson(_:) -> String:

let json = try doc.extractStructuredJson(0)

JavaScript (WASM)extractStructured(pageIndex) (gibt einen JSON-String mit camelCase-Schlüsseln zurück):

const page = JSON.parse(doc.extractStructured(0));
for (const region of page.regions) {
    console.log(region.kind, region.columnIndex);
}

Rustextract_structured(page_index) -> Result<StructuredPage> gibt typisierte Bereiche direkt zurück (kein JSON-Roundtrip). Eine Variante extract_structured_with_column_mode ermöglicht es, ColumnMode::Two/Single für schwierige Layouts zu erzwingen:

use pdf_oxide::PdfDocument;

let doc = PdfDocument::open("report.pdf")?;
let page = doc.extract_structured(0)?;
for region in &page.regions {
    println!("{:?} col={:?}: {}", region.kind, region.column_index, region.text);
}

C++extract_structured_json(page) gibt den JSON-String zurück:

auto json = doc.extract_structured_json(0);

DartextractStructuredJson(page) gibt den JSON-String zurück:

import 'dart:convert';

final page = jsonDecode(doc.extractStructuredJson(0));
for (final region in page['regions']) {
    print('${region["kind"]} ${region["column_index"]}');
}

Rpdf_extract_structured_json(doc, page) gibt JSON zurück:

library(jsonlite)

page <- fromJSON(pdf_extract_structured_json(doc, 0))
print(page$page_width)

Juliaextract_structured_json(doc, page) gibt JSON zurück:

using JSON
page = JSON.parse(extract_structured_json(doc, 0))
for region in page["regions"]
    println(region["kind"], " ", region["column_index"])
end

ZigextractStructuredJson(allocator, page) gibt JSON-Bytes zurück:

const json = try doc.extractStructuredJson(a, 0);  // free json

Objective-CextractStructuredJson:error: gibt den JSON-String zurück:

NSString *json = [doc extractStructuredJson:0 error:&err];

Elixirextract_structured_json(doc, page) gibt JSON zurück:

{:ok, json} = PdfOxide.extract_structured_json(doc, 0)
page = Jason.decode!(json)

JavaextractStructured(page) gibt den JSON-String zurück:

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

String json = doc.extractStructured(0);
JsonNode page = new ObjectMapper().readTree(json);
for (JsonNode region : page.get("regions")) {
    System.out.println(region.get("kind").asText());
}

Kotlin

val json = doc.extractStructured(0)   // JSON string; parse with your library of choice

Scala

val json = doc.extractStructured(0)   // JSON string

Clojure(pdf/extract-structured doc page) gibt den JSON-String zurück:

(require '[clojure.data.json :as json])

(with-open [doc (pdf/open "report.pdf")]
  (let [page (json/read-str (pdf/extract-structured doc 0))]
    (doseq [region (get page "regions")]
      (println (get region "kind") (get region "column_index")))))

Rubyextract_structured(page) gibt den geparsten StructuredPage-Hash zurück:

PdfOxide::PdfDocument.open('report.pdf') do |doc|
  page = doc.extract_structured(0)
  page['regions'].each { |r| puts "#{r['kind']} #{r['column_index']}" }
end

PHPextractStructured($page) gibt das deserialisierte assoziative Array zurück:

$doc = PdfOxide\PdfDocument::open('report.pdf');
$page = $doc->extractStructured(0);
foreach ($page['regions'] as $region) {
    echo $region['kind'], ' ', $region['column_index'], "\n";
}

Koordinatenreferenz

PDF verwendet einen Ursprung in der unteren linken Ecke, gemessen in Punkten (1 Pt = 1/72 Zoll). Eine Letter-Seite ist (0, 0, 612, 792). Um den obersten 1-Zoll-Streifen anzusprechen, schreiben Sie:

(x, y, w, h) = (0, 792 - 72, 612, 72)
             = (0, 720,      612, 72)

Wenn Sie aus einer Bildkoordinaten-Welt (Ursprung oben links) kommen, drehen Sie y entsprechend um.

So ermitteln Sie die tatsächliche MediaBox einer Seite vor der Berechnung:

Python

doc = PdfDocument("doc.pdf")
mb = doc.page_media_box(0)       # (llx, lly, urx, ury)

Rust

let mb = editor.get_page_media_box(0)?;   // [f32; 4]

Javapage.mediaBox() gibt ein BBox (x0, y0, x1, y1) zurück:

import fyi.oxide.pdf.geometry.BBox;

BBox mb = doc.page(0).mediaBox();         // (x0, y0, x1, y1) in PDF user space
double w = mb.width(), h = mb.height();   // 612 × 792 for US Letter

Kotlin

val mb = doc.page(0).mediaBox()           // BBox(x0, y0, x1, y1)

Scala

val mb = doc.page(0).mediaBox             // BBox(x0, y0, x1, y1)

C++ — über den Editor: get_page_media_box(page):

auto editor = pdf_oxide::DocumentEditor::open("doc.pdf");
auto mb = editor.get_page_media_box(0);   // Bbox{x, y, width, height}

Swift

let editor = try DocumentEditor.open("doc.pdf")
let mb = try editor.getPageMediaBox(0)    // Bbox(x, y, width, height)

Dart

final editor = DocumentEditor.open('doc.pdf');
final mb = editor.getPageMediaBox(0);     // Bbox(x, y, width, height)

R

editor <- pdf_editor_open("doc.pdf")
mb <- pdf_editor_get_page_media_box(editor, 0)   # list(x=, y=, width=, height=)

Julia

editor = open_editor("doc.pdf")
mb = get_page_media_box(editor, 0)        # Bbox

Zig

var editor = try pdf_oxide.DocumentEditor.openEditor("doc.pdf");
const mb = try editor.getPageMediaBox(0);  // Bbox{ x, y, width, height }

Objective-C

POXDocumentEditor *editor = [POXDocumentEditor openEditor:@"doc.pdf" error:&err];
POXBbox mb = [editor pageMediaBox:0 error:&err];   // {x, y, width, height}

Elixir

{:ok, editor} = PdfOxide.open_editor("doc.pdf")
{:ok, mb} = PdfOxide.get_page_media_box(editor, 0)   # %Bbox{}

Go / C# — in-rect-Hilfsfunktionen

Go und C# bieten die fluente within()-Kette noch nicht, aber die zugrunde liegenden Low-Level-Methoden sind dieselben:

Methode Go C#
Text im Rechteck doc.ExtractTextInRect(page, x, y, w, h) doc.ExtractTextInRect(page, x, y, w, h)
Wörter im Rechteck doc.ExtractWordsInRect(page, x, y, w, h) (noch nicht eingebunden)
Bilder im Rechteck doc.ExtractImagesInRect(page, x, y, w, h) (noch nicht eingebunden)

Für Muster, die mehrere Extraktionstypen auf dasselbe Rechteck in Go oder C# anwenden müssen, halten Sie das Rechteck in Variablen und rufen Sie die Hilfsfunktionen nacheinander auf. Die fluente Oberfläche folgt, sobald die Editor-API stabil ist.

Häufig gestellte Fragen

Was ist der Unterschied zwischen extract_words() und extract_lines_in_rect() in einem Bereich? extract_words() gibt einen Datensatz pro Wort zurück; extract_lines_in_rect() gibt einen Datensatz pro Zeile (Text, Bounding Box und Wortzahl) für die Zeilen zurück, deren Bounding Box das Rechteck schneidet. Verwenden Sie Zeilen, wenn Sie ganze Zeilen in Lesereihenfolge benötigen — Adressblöcke, Kontoauszugszeilen, mehrzeilige Summen — ohne Wörter selbst gruppieren zu müssen.

Führt extract_page_auto immer OCR aus? Nein. Das Routing erfolgt bereichsweise. Im Standard-Modus "auto" wird OCR nur dann eskaliert, wenn die native Textschicht fehlt oder zweifelhaft ist, und OCR wird tatsächlich nur ausgeführt, wenn die Bibliothek mit der Feature ocr gebaut wurde. Ohne diese Feature fällt es auf die native Textschicht zurück und erzeugt keinen undurchsichtigen OCR-Fehler.

Welche Bindings bieten die Varianten lines-in-rect und tables-in-rect? Rust, das C ABI und Swift bieten extract_lines_in_rect / extract_tables_in_rect direkt. Python erreicht dieselben Ergebnisse über den fluenten Bereich (within(...).extract_text_lines() und within(...).extract_tables()). Go und C# binden die in-rect-Einstiegspunkte für Zeilen/Tabellen noch nicht ein — extrahieren Sie für die gesamte Seite und filtern Sie nach den zurückgegebenen Bounding Boxes.

Wie schnell ist die Bereichsextraktion? Die Bereichsbegrenzung fügt keinen messbaren Overhead gegenüber der Ganzseiten-Extraktion hinzu — PDF Oxide extrahiert mit einem Mittlewert von 0,8 ms (100 % Bestehensrate im Benchmark-Korpus), und ein in-rect-Aufruf filtert dasselbe Ergebnis lediglich nach Bounding Box.

Verwandte Seiten