Extração por escopo — Extraia conteúdo de uma região específica
Ao processar notas fiscais, extratos bancários, declarações de imposto ou qualquer layout com template, geralmente você já sabe onde os campos estão. Em vez de extrair a página inteira e buscar o valor, aponte o PDF Oxide para o retângulo exato e receba somente o que está lá.
A API fluente within(page, rect) retorna uma região com escopo na qual você pode encadear métodos de extração: extract_text(), extract_words(), extract_chars(), extract_tables().
Cobertura de bindings.
within(page, rect)está disponível em Python, Rust e WASM. Go e C# expõem os helpers de baixo nível equivalentes (ExtractTextInRect,ExtractWordsInRect,ExtractImagesInRect) — veja abaixo. A família in-rect completa (texto, palavras, linhas, tabelas, imagens) está disponível de ponta a ponta em Rust, no C ABI e no wrapper Swift; veja Variantes de extração in-rect para saber o que cada binding oferece.
Exemplo rápido
rect é (x, y, width, height) em pontos PDF, com a origem no canto inferior esquerdo da página. Páginas Letter têm 612 × 792 pontos.
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 (helper de baixo nível, mesmo efeito)
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# (helper de baixo nível)
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 no formato de cantos (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)
Extração encadeada de uma região
A forma fluente within() em Python / Rust / WASM permite chamar qualquer método de extração na mesma região com escopo sem precisar especificar o retângulo novamente:
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++ (sem encadeamento fluente — chame cada helper in-rect com o mesmo retângulo)
// 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)
Casos de uso comuns
Extração de campos de nota fiscal
Uma nota fiscal geralmente tem o endereço do fornecedor, o número e a tabela de itens em zonas fixas. Defina os retângulos uma vez por 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"]))
Itens de linha de extrato bancário
A maioria dos extratos tem uma faixa estreita de “transações”. Recorte essa faixa e chame extract_words() para obter cada linha em ordem de leitura com seu bounding box:
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})")
Remoção de cabeçalho e rodapé
Se você está indexando apenas o conteúdo do corpo, recorte o topo e a base de cada página:
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` …
}
Detecção de região de tabela
Quando você já sabe que a página contém uma tabela e onde ela está, delimite o retângulo da tabela e deixe extract_tables() focar apenas nessa região:
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"]])
Quais variantes de extração com escopo retangular existem? {#what-rect-scoped-extraction-variants-exist}
Além de extract_text(), extract_words() e extract_chars(), há mais duas variantes com escopo retangular que retornam resultados com consciência geométrica de um único retângulo: linhas no retângulo e tabelas no retângulo. Ambas filtram uma extração de página completa para as regiões cujo bounding box intersecta seu retângulo, então as coordenadas e a ordem de leitura retornadas são as mesmas de uma chamada de página inteira — apenas recortadas.
Extrair linhas de texto em uma região (extract_lines_in_rect)
Retorna os registros no nível de linha (cada um com seu texto, bounding box e contagem de palavras) que estão dentro do retângulo. Use quando precisar de linhas inteiras em ordem de leitura em vez de palavras individuais — por exemplo, blocos de endereço, totais multilinha ou uma única linha de extrato.
A assinatura do C ABI é a referência autoritativa:
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);
Rust — extract_lines_in_rect(page_index, region) -> Result<Vec<PathContent>> em 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 — a região fluente expõe linhas via extract_text_lines():
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)
Swift — extractLinesInRect(_:x:y:w:h:) retorna [TextLine]:
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) retorna std::vector<TextLine>:
auto lines = doc.extract_lines_in_rect(0, 36, 72, 540, 628);
for (const auto& line : lines) {
std::cout << line.text << "\n";
}
Dart — extractLinesInRect(page, x, y, w, h) retorna List<TextLine>:
final lines = doc.extractLinesInRect(0, 36, 72, 540, 628);
for (final line in lines) {
print('${line.text} ${line.bbox}');
}
R — pdf_extract_lines_in_rect(doc, page, x, y, width, height):
lines <- pdf_extract_lines_in_rect(doc, 0, 36, 72, 540, 628)
Julia — extract_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
Zig — extractLinesInRect(allocator, page, x, y, w, h):
const lines = try doc.extractLinesInRect(a, 0, 36, 72, 540, 628); // freeTextLines
Objective-C — extractLinesInRect:x:y:w:h: retorna NSArray<POXTextLine*>:
NSArray<POXTextLine*> *lines = [doc extractLinesInRect:0 x:36 y:72 w:540 h:628 error:&err];
Elixir — extract_lines_in_rect(doc, page, x, y, w, h):
{:ok, lines} = PdfOxide.extract_lines_in_rect(doc, 0, 36, 72, 540, 628)
Go / C#. O ponto de entrada
extract_lines_in_rectdo C existe, mas os wrappers de Go e C# ainda não o expõem. Nessas linguagens, extraia linhas da página inteira e filtre pelos bounding boxes retornados, ou useExtractWordsInRect(Go) e agrupe as palavras em linhas manualmente.
Extrair tabelas em uma região (extract_tables_in_rect)
Delimita a detecção de tabelas a um único retângulo — somente tabelas cujo bounding box intersecta o retângulo são retornadas. Esta é a contraparte com consciência geométrica do fluente within(...).extract_tables() mostrado acima.
Assinatura do C ABI:
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);
Rust — extract_tables_in_rect(page_index, region) -> Result<Vec<Table>> (a variante ..._with_config aceita um TableDetectionConfig personalizado):
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 — via região fluente:
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"]])
Swift — extractTablesInRect(_:x:y:w:h:) retorna [Table]:
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) retorna std::vector<Table>:
auto tables = doc.extract_tables_in_rect(0, 50, 200, 500, 400);
for (const auto& table : tables) {
std::cout << table.rows.size() << " rows\n";
}
Dart — extractTablesInRect(page, x, y, w, h) retorna List<Table>:
final tables = doc.extractTablesInRect(0, 50, 200, 500, 400);
for (final table in tables) {
print('${table.rows.length} rows');
}
R — pdf_extract_tables_in_rect(doc, page, x, y, width, height):
tables <- pdf_extract_tables_in_rect(doc, 0, 50, 200, 500, 400)
Julia — extract_tables_in_rect(doc, page, x, y, w, h):
tables = extract_tables_in_rect(doc, 0, 50, 200, 500, 400)
Zig — extractTablesInRect(allocator, page, x, y, w, h):
const tables = try doc.extractTablesInRect(a, 0, 50, 200, 500, 400);
Objective-C — extractTablesInRect:x:y:w:h: retorna NSArray<POXTable*>:
NSArray<POXTable*> *tables = [doc extractTablesInRect:0 x:50 y:200 w:500 h:400 error:&err];
Elixir — extract_tables_in_rect(doc, page, x, y, w, h):
{:ok, tables} = PdfOxide.extract_tables_in_rect(doc, 0, 50, 200, 500, 400)
Go / C#. Assim como com as linhas, o ponto de entrada
extract_tables_in_rectdo C existe mas ainda não está encapsulado em Go ou C#. ChameExtractTables(page)para a página inteira e mantenha as tabelas cujo bounding box esteja dentro do seu retângulo.
Como extrair uma página automaticamente sem escolher entre texto e OCR?
Quando você não sabe se uma página tem texto digital, é um scan ou uma mistura, extract_page_auto faz o roteamento por você. Ele executa o AutoExtractor — roteamento texto vs OCR por região, com fallback nativo elegante (nunca expõe um erro opaco de OCR) — e retorna um PageExtraction JSON: um kind de página, o text montado em ordem de leitura, um confidence, um reason tipado, um flag ocr_used, e um array regions[] onde cada região traz bbox, kind, text, confidence, source e reason (o bbox e o reason estão presentes mesmo quando o texto da região está vazio, para que a ordem de leitura nunca seja silenciosamente corrompida).
É tolerante a {}: passe um JSON de opções vazio/null para os padrões, ou forneça um objeto AutoExtractOptions. Os campos reconhecidos (serializados em snake_case) são:
| Campo | Tipo | Padrão | Significado |
|---|---|---|---|
mode |
"text_only" | "auto" | "force_ocr" |
"auto" |
Estratégia de roteamento texto vs OCR |
reconstruct_image_tables |
bool | true |
Reconstruir tabelas apenas de imagem via detector espacial sobre spans de OCR |
emit_placeholders |
bool | true |
Emitir placeholders posicionados de Figure/Table no fluxo de texto |
ocr_languages |
string[] | [] |
Dicas de idioma para OCR (ex.: ["english","chinese"]) |
min_text_confidence |
float | null | null |
Limiar de confiança para decisão automática |
table_confidence |
float | null | null |
Limiar de reconstrução de tabela de imagem |
force_ocr_pages |
int[] | [] |
Índices de página (base 0) para forçar OCR |
Gate de funcionalidade OCR. O OCR só executa de fato quando a biblioteca é compilada com a feature
ocr; caso contrário,extract_page_autocai de volta para a camada de texto nativa (sem erros). O ponto de entrada automático está disponível em Python, Go, C#, Swift, WASM e no C ABI. Em Rust, é a API de bibliotecaAutoExtractor, e não um método de uma linha emPdfDocument— veja abaixo.
Python — extract_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))
Go — ExtractPageAuto(pageIndex, opts ...AutoOption) (string, error) (retorna JSON; configure via opções funcionais):
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);
Swift — extractPageAuto(_:optionsJson:) -> String (padrão "{}"):
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 — o caminho automático é a API de biblioteca AutoExtractor. Construa AutoExtractOptions (presets fast(), balanced(), high_fidelity(), ou o builder fluente) e chame extract_page, que retorna um PageExtraction tipado (sem round-trip JSON):
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 = "") retorna o envelope JSON:
#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"]})");
Dart — extractPageAuto(page, [optionsJson]) retorna o envelope JSON:
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();
R — pdf_extract_page_auto(doc, page, options_json = NULL) retorna JSON:
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")
Julia — extract_page_auto(doc, page, options = "{}") retorna JSON:
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"])
Zig — extractPageAuto(allocator, page, options_json) retorna bytes JSON:
const json = try doc.extractPageAuto(a, 0, null); // free json
Objective-C — extractPageAuto:optionsJson:error: retorna o envelope JSON:
NSString *json = [doc extractPageAuto:0 optionsJson:@"{}" error:&err];
Elixir — extract_page_auto(doc, page, options_json \\ "") retorna JSON:
{:ok, json} = PdfOxide.extract_page_auto(doc, 0)
page = Jason.decode!(json)
IO.inspect({page["kind"], page["confidence"], page["ocr_used"]})
Java — o caminho automático é a API AutoExtractor (extractPage → resultado tipado; extractTextForPage para texto simples):
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 — o envelope JSON rico está disponível via AutoExtractor::extractPageJson:
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'];
Ruby — auto_extractor.extract_page(page) retorna o envelope analisado como Hash:
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
Como obter regiões tipadas estruturadas em JSON?
Para uma visão estruturada da página inteira — títulos, blocos de corpo, cabeçalhos/rodapés, números de página e ordem de colunas — use o ponto de entrada de extração estruturada. Ele retorna um StructuredPage: page_index, page_width, page_height e um array regions[] onde cada região traz kind (papel semântico), text, bbox, spans e column_index (para ordem de leitura em múltiplas colunas). Os kinds de região incluem blocos de corpo, títulos estruturais (H1–H6), rótulos marginais, cabeçalhos/rodapés recorrentes, números de página e artefatos.
A maioria dos bindings retorna isso como uma string JSON (o C ABI serializa uma vez e os bindings desserializam para tipos nativos); Rust retorna o StructuredPage tipado diretamente.
Assinatura do C ABI:
char *pdf_document_extract_structured_to_json(
PdfDocument *handle,
int32_t page_index,
int32_t *error_code);
Python — extract_structured(page) -> str (JSON; desserialize com json.loads):
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])
Go — ExtractStructured(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"));
}
Swift — extractStructuredJson(_:) -> String:
let json = try doc.extractStructuredJson(0)
JavaScript (WASM) — extractStructured(pageIndex) (retorna uma string JSON com chaves em camelCase):
const page = JSON.parse(doc.extractStructured(0));
for (const region of page.regions) {
console.log(region.kind, region.columnIndex);
}
Rust — extract_structured(page_index) -> Result<StructuredPage> retorna regiões tipadas diretamente (sem round-trip JSON). Uma variante extract_structured_with_column_mode permite forçar ColumnMode::Two/Single para layouts teimosos:
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) retorna a string JSON:
auto json = doc.extract_structured_json(0);
Dart — extractStructuredJson(page) retorna a string JSON:
import 'dart:convert';
final page = jsonDecode(doc.extractStructuredJson(0));
for (final region in page['regions']) {
print('${region["kind"]} ${region["column_index"]}');
}
R — pdf_extract_structured_json(doc, page) retorna JSON:
library(jsonlite)
page <- fromJSON(pdf_extract_structured_json(doc, 0))
print(page$page_width)
Julia — extract_structured_json(doc, page) retorna JSON:
using JSON
page = JSON.parse(extract_structured_json(doc, 0))
for region in page["regions"]
println(region["kind"], " ", region["column_index"])
end
Zig — extractStructuredJson(allocator, page) retorna bytes JSON:
const json = try doc.extractStructuredJson(a, 0); // free json
Objective-C — extractStructuredJson:error: retorna a string JSON:
NSString *json = [doc extractStructuredJson:0 error:&err];
Elixir — extract_structured_json(doc, page) retorna JSON:
{:ok, json} = PdfOxide.extract_structured_json(doc, 0)
page = Jason.decode!(json)
Java — extractStructured(page) retorna a string JSON:
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) retorna a string JSON:
(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")))))
Ruby — extract_structured(page) retorna o Hash StructuredPage analisado:
PdfOxide::PdfDocument.open('report.pdf') do |doc|
page = doc.extract_structured(0)
page['regions'].each { |r| puts "#{r['kind']} #{r['column_index']}" }
end
PHP — extractStructured($page) retorna o array associativo desserializado:
$doc = PdfOxide\PdfDocument::open('report.pdf');
$page = $doc->extractStructured(0);
foreach ($page['regions'] as $region) {
echo $region['kind'], ' ', $region['column_index'], "\n";
}
Referência de coordenadas
O PDF usa uma origem no canto inferior esquerdo, medida em pontos (1 pt = 1/72 polegada). Uma página Letter é (0, 0, 612, 792). Para atingir a faixa superior de 1 polegada, escreva:
(x, y, w, h) = (0, 792 - 72, 612, 72)
= (0, 720, 612, 72)
Se você vem de um sistema de coordenadas de imagem (origem no canto superior esquerdo), inverta y adequadamente.
Para obter o MediaBox real de uma página antes de calcular:
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]
Java — page.mediaBox() retorna um BBox (x0, y0, x1, y1):
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++ — via 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# — helpers in-rect
Embora Go e C# ainda não exponham a cadeia fluente within(), os métodos de baixo nível subjacentes são os mesmos:
| Método | Go | C# |
|---|---|---|
| Texto no retângulo | doc.ExtractTextInRect(page, x, y, w, h) |
doc.ExtractTextInRect(page, x, y, w, h) |
| Palavras no retângulo | doc.ExtractWordsInRect(page, x, y, w, h) |
(ainda não encapsulado) |
| Imagens no retângulo | doc.ExtractImagesInRect(page, x, y, w, h) |
(ainda não encapsulado) |
Para padrões que precisam de múltiplos tipos de extração sobre o mesmo retângulo em Go ou C#, mantenha o retângulo em variáveis e chame os helpers sequencialmente. A interface fluente virá assim que a API do editor se estabilizar.
Perguntas frequentes
Qual é a diferença entre extract_words() e extract_lines_in_rect() em uma região?
extract_words() retorna um registro por palavra; extract_lines_in_rect() retorna um registro por linha (texto, bounding box e contagem de palavras) para as linhas cujo bounding box intersecta o retângulo. Use linhas quando precisar de linhas inteiras em ordem de leitura — blocos de endereço, linhas de extrato, totais multilinha — sem precisar reagrupar palavras manualmente.
extract_page_auto sempre executa OCR?
Não. O roteamento é por região. No modo padrão "auto", só escalona para OCR onde a camada de texto nativa está ausente ou suspeita, e o OCR só executa de fato quando a biblioteca é compilada com a feature ocr. Sem essa feature, cai de volta para a camada de texto nativa sem gerar um erro opaco de OCR.
Quais bindings expõem as variantes lines-in-rect e tables-in-rect?
Rust, o C ABI e Swift expõem extract_lines_in_rect / extract_tables_in_rect diretamente. Python alcança os mesmos resultados pela região fluente (within(...).extract_text_lines() e within(...).extract_tables()). Go e C# ainda não encapsulam os pontos de entrada in-rect de linhas/tabelas — extraia para a página inteira e filtre pelos bounding boxes retornados.
Quão rápida é a extração com escopo? O escopo não adiciona overhead mensurável sobre a extração de página inteira — PDF Oxide extrai com média de 0,8ms (taxa de aprovação de 100% no corpus de benchmark), e uma chamada in-rect filtra esse mesmo resultado por bounding box.
Páginas relacionadas
- Extração de texto — extração de página inteira
- Extrair tabelas de PDF — tabelas estruturadas
- Busca de texto — resultados de busca e serialização
search_results_to_json - Perfis de extração — ajuste de extração por documento
- Referência da API de página — iterar + delimitar a partir de um objeto
Page(page.region(rect))