Extracción por ámbito — Obtén contenido de una región específica
Cuando procesas facturas, extractos bancarios, declaraciones de impuestos o cualquier maquetación con plantilla, normalmente ya sabes dónde están los campos. En lugar de extraer la página completa y buscar el valor, apunta PDF Oxide al rectángulo exacto y obtén solo lo que hay ahí.
La API fluida within(page, rect) devuelve una región con ámbito acotado sobre la que puedes encadenar métodos de extracción: extract_text(), extract_words(), extract_chars(), extract_tables().
Cobertura de bindings.
within(page, rect)está disponible en Python, Rust y WASM. Go y C# exponen los helpers de bajo nivel equivalentes (ExtractTextInRect,ExtractWordsInRect,ExtractImagesInRect) — ver más abajo. La familia in-rect completa (texto, palabras, líneas, tablas, imágenes) está disponible de extremo a extremo en Rust, el C ABI y el wrapper Swift; consulta Variantes de extracción in-rect para ver qué ofrece cada binding.
Ejemplo rápido
rect es (x, y, width, height) en puntos PDF, con el origen en la esquina inferior izquierda de la página. Las páginas Letter son de 612 × 792 puntos.
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 bajo nivel, mismo efecto)
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 bajo nivel)
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 en formato de esquinas (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)
Extracción encadenada desde una región
La forma fluida within() en Python / Rust / WASM te permite llamar a cualquier método de extracción sobre la misma región acotada sin especificar de nuevo el rectángulo:
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++ (sin encadenamiento fluido — llama a cada helper in-rect con el mismo rectá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 habituales
Extracción de campos de factura
Una factura suele tener la dirección del proveedor, el número de factura y la tabla de líneas en zonas fijas. Define los rectángulos una vez por plantilla:
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"]))
Líneas de movimientos de extracto bancario
La mayoría de los extractos tienen una franja estrecha de “transacciones”. Recorta esa franja y llama a extract_words() para obtener cada línea en orden de lectura con su 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})")
Eliminación de encabezado y pie de página
Si solo indexas el contenido del cuerpo, recorta la parte superior e inferior 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` …
}
Detección de región de tabla
Cuando ya sabes que una página contiene una tabla y dónde está, acota el rectángulo de la tabla y deja que extract_tables() se centre solo en esa región:
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"]])
¿Qué variantes de extracción acotada por rectángulo existen? {#what-rect-scoped-extraction-variants-exist}
Además de extract_text(), extract_words() y extract_chars(), hay dos variantes más con ámbito rectangular que devuelven resultados con conciencia geométrica desde un único rectángulo: líneas en el rectángulo y tablas en el rectángulo. Ambas filtran una extracción de página completa para quedarse solo con las regiones cuyo bounding box intersecta tu rectángulo, de modo que las coordenadas y el orden de lectura devueltos son los mismos que en una llamada de página completa — solo recortados.
Extraer líneas de texto en una región (extract_lines_in_rect)
Devuelve los registros a nivel de línea (cada uno con su texto, bounding box y recuento de palabras) que caen dentro del rectángulo. Úsalo cuando necesites líneas completas en orden de lectura en lugar de palabras individuales — por ejemplo, bloques de dirección, totales multilínea o una sola fila de extracto.
La firma del C ABI es la referencia 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>> en 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 — la región fluida expone líneas mediante 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:) devuelve [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) devuelve 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) devuelve 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: devuelve 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#. El punto de entrada C
extract_lines_in_rectexiste, pero los wrappers de Go y C# aún no lo exponen. En esos lenguajes, extrae las líneas de la página completa y filtra por los bounding boxes devueltos, o usaExtractWordsInRect(Go) y agrupa las palabras en líneas tú mismo.
Extraer tablas en una región (extract_tables_in_rect)
Acota la detección de tablas a un único rectángulo — solo se devuelven las tablas cuyo bounding box intersecta el rectángulo. Esta es la contraparte con conciencia geométrica del fluido within(...).extract_tables() mostrado arriba.
Firma del 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>> (la variante ..._with_config acepta una TableDetectionConfig personalizada):
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 — a través de la región fluida:
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:) devuelve [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) devuelve 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) devuelve 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: devuelve 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#. Al igual que con las líneas, el punto de entrada C
extract_tables_in_rectexiste pero aún no está envuelto en Go o C#. Llama aExtractTables(page)para la página completa y conserva las tablas cuyo bounding box caiga dentro de tu rectángulo.
¿Cómo extraigo una página automáticamente sin elegir entre texto y OCR?
Cuando no sabes si una página tiene texto digital, es un escaneo o una mezcla, extract_page_auto hace el enrutamiento por ti. Ejecuta el AutoExtractor — enrutamiento texto vs. OCR por región, con retroceso nativo elegante (nunca expone un error opaco de OCR) — y devuelve un PageExtraction JSON: un kind de página, el text montado en orden de lectura, una confidence, un reason tipado, un flag ocr_used y un array regions[] donde cada región lleva bbox, kind, text, confidence, source y reason (el bbox y el reason están presentes incluso cuando el texto de una región está vacío, para que el orden de lectura nunca se corrompa silenciosamente).
Es tolerante a {}: pasa un JSON de opciones vacío/null para los valores por defecto, o proporciona un objeto AutoExtractOptions. Los campos reconocidos (serializados en snake_case) son:
| Campo | Tipo | Por defecto | Significado |
|---|---|---|---|
mode |
"text_only" | "auto" | "force_ocr" |
"auto" |
Estrategia de enrutamiento texto vs. OCR |
reconstruct_image_tables |
bool | true |
Reconstruir tablas solo de imagen mediante el detector espacial sobre spans de OCR |
emit_placeholders |
bool | true |
Emitir marcadores de posición Figure/Table en el flujo de texto |
ocr_languages |
string[] | [] |
Pistas de idioma para OCR (ej.: ["english","chinese"]) |
min_text_confidence |
float | null | null |
Umbral de confianza para la decisión automática |
table_confidence |
float | null | null |
Umbral de reconstrucción de tabla de imagen |
force_ocr_pages |
int[] | [] |
Índices de página (base 0) para forzar OCR |
Activador de la función OCR. El OCR solo se ejecuta de verdad cuando la biblioteca se compila con la feature
ocr; de lo contrario,extract_page_autocae de vuelta a la capa de texto nativa (sin errores). El punto de entrada automático está disponible en Python, Go, C#, Swift, WASM y el C ABI. En Rust es la API de bibliotecaAutoExtractor, no un método de una línea dePdfDocument— ver más abajo.
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) (devuelve JSON; configura mediante opciones funcionales):
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 (por defecto "{}"):
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 — el camino automático es la API de biblioteca AutoExtractor. Construye AutoExtractOptions (presets fast(), balanced(), high_fidelity() o el builder fluido) y llama a extract_page, que devuelve un PageExtraction tipado (sin 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 = "") devuelve el sobre 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]) devuelve el sobre 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) devuelve 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 = "{}") devuelve 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) devuelve bytes JSON:
const json = try doc.extractPageAuto(a, 0, null); // free json
Objective-C — extractPageAuto:optionsJson:error: devuelve el sobre JSON:
NSString *json = [doc extractPageAuto:0 optionsJson:@"{}" error:&err];
Elixir — extract_page_auto(doc, page, options_json \\ "") devuelve JSON:
{:ok, json} = PdfOxide.extract_page_auto(doc, 0)
page = Jason.decode!(json)
IO.inspect({page["kind"], page["confidence"], page["ocr_used"]})
Java — el camino automático es la API AutoExtractor (extractPage → resultado tipado; extractTextForPage para texto plano):
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 — el rico sobre JSON está disponible mediante 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) devuelve el sobre analizado fusionado en un 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
¿Cómo obtengo regiones tipadas estructuradas como JSON?
Para una vista estructurada de toda la página — encabezados, bloques de cuerpo, cabeceras/pies de página, números de página y orden de columnas — usa el punto de entrada de extracción estructurada. Devuelve un StructuredPage: page_index, page_width, page_height y un array regions[] donde cada región lleva kind (rol semántico), text, bbox, spans y column_index (para el orden de lectura en múltiples columnas). Los kind de región incluyen bloques de cuerpo, encabezados estructurales (H1–H6), etiquetas marginales, cabeceras/pies de página recurrentes, números de página y artefactos.
La mayoría de los bindings devuelven esto como una cadena JSON (el C ABI serializa una vez y los bindings deserializan a tipos nativos); Rust devuelve el StructuredPage tipado directamente.
Firma del 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; deserializa con 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) (devuelve una cadena JSON con claves en 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> devuelve regiones tipadas directamente (sin round-trip JSON). Una variante extract_structured_with_column_mode permite forzar ColumnMode::Two/Single para maquetaciones complicadas:
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) devuelve la cadena JSON:
auto json = doc.extract_structured_json(0);
Dart — extractStructuredJson(page) devuelve la cadena 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) devuelve JSON:
library(jsonlite)
page <- fromJSON(pdf_extract_structured_json(doc, 0))
print(page$page_width)
Julia — extract_structured_json(doc, page) devuelve 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) devuelve bytes JSON:
const json = try doc.extractStructuredJson(a, 0); // free json
Objective-C — extractStructuredJson:error: devuelve la cadena JSON:
NSString *json = [doc extractStructuredJson:0 error:&err];
Elixir — extract_structured_json(doc, page) devuelve JSON:
{:ok, json} = PdfOxide.extract_structured_json(doc, 0)
page = Jason.decode!(json)
Java — extractStructured(page) devuelve la cadena 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) devuelve la cadena 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) devuelve el Hash StructuredPage analizado:
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) devuelve el array asociativo deserializado:
$doc = PdfOxide\PdfDocument::open('report.pdf');
$page = $doc->extractStructured(0);
foreach ($page['regions'] as $region) {
echo $region['kind'], ' ', $region['column_index'], "\n";
}
Referencia de coordenadas
PDF usa un origen en la esquina inferior izquierda, medido en puntos (1 pt = 1/72 pulgada). Una página Letter es (0, 0, 612, 792). Para apuntar a la franja superior de 1 pulgada, escribe:
(x, y, w, h) = (0, 792 - 72, 612, 72)
= (0, 720, 612, 72)
Si vienes de un sistema de coordenadas de imagen (origen arriba a la izquierda), invierte y en consecuencia.
Para obtener el MediaBox real de una 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() devuelve un 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++ — mediante el 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
Aunque Go y C# todavía no exponen la cadena fluida within(), los métodos de bajo nivel subyacentes son los mismos:
| Método | Go | C# |
|---|---|---|
| Texto en el rectángulo | doc.ExtractTextInRect(page, x, y, w, h) |
doc.ExtractTextInRect(page, x, y, w, h) |
| Palabras en el rectángulo | doc.ExtractWordsInRect(page, x, y, w, h) |
(aún no envuelto) |
| Imágenes en el rectángulo | doc.ExtractImagesInRect(page, x, y, w, h) |
(aún no envuelto) |
Para patrones que necesitan múltiples tipos de extracción sobre el mismo rectángulo en Go o C#, guarda el rectángulo en variables y llama a los helpers de forma secuencial. La interfaz fluida llegará cuando la API del editor se estabilice.
Preguntas frecuentes
¿Cuál es la diferencia entre extract_words() y extract_lines_in_rect() en una región?
extract_words() devuelve un registro por palabra; extract_lines_in_rect() devuelve un registro por línea (texto, bounding box y recuento de palabras) para las líneas cuyo bounding box intersecta el rectángulo. Usa líneas cuando necesites filas completas en orden de lectura — bloques de dirección, filas de extracto, totales multilínea — sin tener que reagrupar palabras tú mismo.
¿extract_page_auto siempre ejecuta OCR?
No. El enrutamiento es por región. En el modo "auto" predeterminado, solo escala a OCR donde la capa de texto nativa está ausente o es sospechosa, y el OCR solo se ejecuta de verdad cuando la biblioteca se compila con la feature ocr. Sin esa feature, cae de vuelta a la capa de texto nativa sin generar un error opaco de OCR.
¿Qué bindings exponen las variantes lines-in-rect y tables-in-rect?
Rust, el C ABI y Swift exponen extract_lines_in_rect / extract_tables_in_rect directamente. Python alcanza los mismos resultados a través de la región fluida (within(...).extract_text_lines() y within(...).extract_tables()). Go y C# todavía no envuelven los puntos de entrada in-rect de líneas/tablas — extrae para la página completa y filtra por los bounding boxes devueltos.
¿Qué tan rápida es la extracción por ámbito? El acotamiento no añade sobrecarga medible respecto a la extracción de página completa — PDF Oxide extrae con una media de 0,8 ms (tasa de aprobación del 100 % en el corpus de referencia), y una llamada in-rect filtra ese mismo resultado por bounding box.
Páginas relacionadas
- Extracción de texto — extracción de página completa
- Extraer tablas de PDF — tablas estructuradas
- Búsqueda de texto — resultados de búsqueda y serialización
search_results_to_json - Perfiles de extracción — ajuste de extracción por documento
- Referencia de la API de página — iterar y acotar desde un objeto
Page(page.region(rect))