Búsqueda de Texto
PDF Oxide ofrece búsqueda de texto completo en documentos PDF con soporte de expresiones regulares, coincidencia sin distinción de mayúsculas, modo de palabra completa y cuadros delimitadores por coincidencia. Los resultados incluyen número de página, texto encontrado y coordenadas precisas de cada ocurrencia, lo que facilita la creación de flujos de trabajo de búsqueda y resaltado.
Use TextSearcher::search() para consultas de múltiples páginas con opciones personalizadas, o los métodos convenientes de Pdf (search(), search_page(), highlight_matches()) para los casos de uso más habituales.
Ejemplo Rápido
Python
from pdf_oxide import PdfDocument
doc = PdfDocument("report.pdf")
results = doc.search("conclusion", case_insensitive=True)
for r in results:
print(f"Page {r['page']}: '{r['text']}' at ({r['x']:.1f}, {r['y']:.1f})")
Node.js
const { PdfDocument } = require("pdf-oxide");
const doc = new PdfDocument("report.pdf");
const results = doc.searchAll("conclusion", { caseSensitive: false });
for (const r of results) {
console.log(`Page ${r.page}: '${r.text}' at (${r.x.toFixed(1)}, ${r.y.toFixed(1)})`);
}
doc.close();
Go
import pdfoxide "github.com/yfedoseev/pdf_oxide/go"
doc, _ := pdfoxide.Open("report.pdf")
defer doc.Close()
results, _ := doc.SearchAll("conclusion", false)
for _, r := range results {
fmt.Printf("Page %d: '%s' at (%.1f, %.1f)\n", r.Page, r.Text, r.X, r.Y)
}
C#
using PdfOxide.Core;
using var doc = PdfDocument.Open("report.pdf");
var results = doc.SearchAll("conclusion");
foreach (var r in results)
{
Console.WriteLine($"Page {r.Page}: '{r.Text}' at ({r.X:F1}, {r.Y:F1})");
}
WASM
const doc = new WasmPdfDocument(bytes);
const results = doc.search("conclusion");
for (const r of results) {
console.log(`Page ${r.page}: '${r.text}' at (${r.x.toFixed(1)}, ${r.y.toFixed(1)})`);
}
Rust
use pdf_oxide::api::Pdf;
let mut pdf = Pdf::open("report.pdf")?;
let results = pdf.search("conclusion")?;
for r in &results {
println!("Page {}: '{}' at ({:.1}, {:.1})", r.page, r.text, r.bbox.x, r.bbox.y);
}
Java
import fyi.oxide.pdf.PdfDocument;
import fyi.oxide.pdf.search.SearchMatch;
import java.nio.file.Path;
import java.util.List;
try (PdfDocument doc = PdfDocument.open(Path.of("report.pdf"))) {
List<SearchMatch> results = doc.search("conclusion", true, false, 0);
for (SearchMatch m : results) {
System.out.printf("Page %d: '%s' at (%.1f, %.1f)%n",
m.pageIndex(), m.text(), m.bbox().x0(), m.bbox().y0());
}
}
Kotlin
import fyi.oxide.pdf.PdfDocument
import java.nio.file.Path
PdfDocument.open(Path.of("report.pdf")).use { doc ->
val results = doc.search("conclusion", true, false, 0)
for (m in results) {
println("Page ${m.pageIndex()}: '${m.text()}' at (${m.bbox().x0()}, ${m.bbox().y0()})")
}
}
Scala
import fyi.oxide.pdf.{PdfDocument, searchSeq}
import scala.util.Using
Using.resource(PdfDocument.open("report.pdf")) { doc =>
val results = doc.searchSeq("conclusion")
for (m <- results)
println(f"Page ${m.pageIndex}: '${m.text}' at (${m.bbox.x0}%.1f, ${m.bbox.y0}%.1f)")
}
Clojure
(require '[pdf-oxide.core :as pdf])
(with-open [doc (pdf/open "report.pdf")]
(doseq [m (pdf/search doc "conclusion")]
(printf "Page %d: '%s' at (%.1f, %.1f)%n"
(.pageIndex m) (.text m) (.x0 (.bbox m)) (.y0 (.bbox m)))))
Ruby
require 'pdf_oxide'
PdfOxide::PdfDocument.open('report.pdf') do |doc|
doc.search('conclusion', case_sensitive: false).each do |r|
bbox = r[:bbox]
printf("Page %d: '%s' at (%.1f, %.1f)\n", r[:page], r[:text], bbox[:x], bbox[:y])
end
end
C++
#include <pdf_oxide/pdf_oxide.hpp>
#include <cstdio>
auto doc = pdf_oxide::Document::open("report.pdf");
auto results = doc.search_all("conclusion", /*case_sensitive=*/false);
for (const auto& r : results) {
std::printf("Page %d: '%s' at (%.1f, %.1f)\n",
r.page, r.text.c_str(), r.bbox.x, r.bbox.y);
}
Swift
import PdfOxide
let doc = try Document.open("report.pdf")
let results = try doc.searchAll("conclusion", false)
for r in results {
print("Page \(r.page): '\(r.text)' at (\(r.bbox.x), \(r.bbox.y))")
}
Dart
import 'package:pdf_oxide/pdf_oxide.dart';
final doc = PdfDocument.open('report.pdf');
final results = doc.searchAll('conclusion', false);
for (final r in results) {
print("Page ${r.page}: '${r.text}' at (${r.bbox.x}, ${r.bbox.y})");
}
doc.close();
R
library(pdfoxide)
doc <- pdf_open("report.pdf")
results <- pdf_search_all(doc, "conclusion", case_sensitive = FALSE)
for (r in results) {
cat(sprintf("Page %d: '%s' at (%.1f, %.1f)\n",
r$page, r$text, r$bbox$x, r$bbox$y))
}
Julia
using PdfOxide
doc = open_document("report.pdf")
results = search_all(doc, "conclusion", false)
for r in results
println("Page $(r.page): '$(r.text)' at ($(r.bbox.x), $(r.bbox.y))")
end
Zig
const pdf_oxide = @import("pdf_oxide");
const a = std.heap.page_allocator;
var doc = try pdf_oxide.Document.open("report.pdf");
const results = try doc.searchAll(a, "conclusion", false);
defer doc.freeSearchResults(a, results);
for (results) |r| {
std.debug.print("Page {d}: '{s}' at ({d:.1}, {d:.1})\n", .{ r.page, r.text, r.bbox.x, r.bbox.y });
}
Objective-C
#import "POXPdfOxide.h"
NSError *err = nil;
POXDocument *doc = [POXDocument openPath:@"report.pdf" error:&err];
NSArray<POXSearchResult*> *results = [doc searchAll:@"conclusion" caseSensitive:NO error:&err];
for (POXSearchResult *r in results) {
NSLog(@"Page %ld: '%@' at (%.1f, %.1f)", (long)r.page, r.text, r.bbox.x, r.bbox.y);
}
Elixir
{:ok, doc} = PdfOxide.open("report.pdf")
{:ok, results} = PdfOxide.search_all(doc, "conclusion", false)
for r <- results do
IO.puts("Page #{r.page}: '#{r.text}' at (#{r.bbox.x}, #{r.bbox.y})")
end
Referencia de API
TextSearcher::search(doc, pattern, options) -> Vec<SearchResult>
Busca texto en múltiples páginas de un documento PDF. El patrón se compila como expresión regular a menos que el modo literal esté activado.
| Parámetro | Tipo | Descripción |
|---|---|---|
doc |
&mut PdfDocument |
El documento PDF a buscar |
pattern |
&str |
Patrón de expresión regular (o texto literal si literal está definido) |
options |
&SearchOptions |
Configuración de búsqueda |
Devuelve: Un vector de objetos SearchResult, ordenados por página y posición.
Rust
use pdf_oxide::PdfDocument;
use pdf_oxide::search::{TextSearcher, SearchOptions};
let mut doc = PdfDocument::open("report.pdf")?;
let options = SearchOptions::new()
.with_case_insensitive(true)
.with_max_results(50);
let results = TextSearcher::search(&mut doc, "error|warning", &options)?;
for r in &results {
println!("Page {}: '{}'", r.page, r.text);
}
TextSearcher::search_page(doc, page, regex, options) -> Vec<SearchResult>
Busca texto en una página específica usando una expresión regular precompilada.
| Parámetro | Tipo | Descripción |
|---|---|---|
doc |
&mut PdfDocument |
El documento PDF |
page |
usize |
Índice de página con base cero |
regex |
&Regex |
Patrón de expresión regular precompilado |
options |
&SearchOptions |
Configuración de búsqueda |
Devuelve: Un vector de objetos SearchResult para la página especificada.
Rust
use pdf_oxide::PdfDocument;
use pdf_oxide::search::{TextSearcher, SearchOptions};
use regex::Regex;
let mut doc = PdfDocument::open("report.pdf")?;
let regex = Regex::new(r"\d{4}-\d{2}-\d{2}")?; // Date pattern
let options = SearchOptions::default();
let results = TextSearcher::search_page(&mut doc, 0, ®ex, &options)?;
for r in &results {
println!("Date found: '{}' at ({:.1}, {:.1})", r.text, r.bbox.x, r.bbox.y);
}
SearchOptions
Configuración del comportamiento de búsqueda de texto. Utiliza el patrón constructor para una construcción ergonómica.
| Campo | Tipo | Predeterminado | Descripción |
|---|---|---|---|
case_insensitive |
bool |
false |
Ignorar mayúsculas/minúsculas en la coincidencia |
literal |
bool |
false |
Tratar el patrón como texto literal (escapar metacaracteres de regex) |
whole_word |
bool |
false |
Coincidir solo palabras completas (envuelve el patrón con \b...\b) |
max_results |
usize |
0 |
Número máximo de resultados a devolver (0 = ilimitado) |
page_range |
Option<(usize, usize)> |
None |
Rango de páginas a buscar (inicio y fin inclusivos) |
Métodos del Constructor
let options = SearchOptions::new()
.with_case_insensitive(true)
.with_literal(true)
.with_whole_word(true)
.with_max_results(100)
.with_page_range(0, 9);
Constructor de Conveniencia
// Quick case-insensitive search
let options = SearchOptions::case_insensitive();
SearchResult
Una única coincidencia de búsqueda con información de posición.
| Campo | Tipo | Descripción |
|---|---|---|
page |
usize |
Número de página (índice 0) |
text |
String |
El texto coincidente |
bbox |
Rect |
Cuadro delimitador combinado de la coincidencia |
start_index |
usize |
Índice inicial en el texto extraído de la página |
end_index |
usize |
Índice final en el texto extraído de la página |
span_boxes |
Vec<Rect> |
Cuadros delimitadores individuales para cada tramo de la coincidencia (útil para coincidencias de varias líneas) |
Python: En la API Python, los resultados de búsqueda se devuelven como diccionarios:
{
"page": 0,
"text": "conclusion",
"x": 72.0,
"y": 650.5,
"width": 85.3,
"height": 12.0,
}
Métodos de Conveniencia de Pdf
La API de alto nivel Pdf proporciona métodos abreviados para operaciones de búsqueda comunes.
search(pattern) -> Vec<SearchResult>
Busca en todo el documento con las opciones predeterminadas.
let mut pdf = Pdf::open("report.pdf")?;
let results = pdf.search("important")?;
search_with_options(pattern, options) -> Vec<SearchResult>
Búsqueda con opciones personalizadas.
let options = SearchOptions::case_insensitive()
.with_whole_word(true)
.with_page_range(0, 5);
let results = pdf.search_with_options("abstract", options)?;
search_page(page, pattern) -> Vec<SearchResult>
Busca en una sola página con las opciones predeterminadas.
let results = pdf.search_page(0, r"\d+\.\d+")?; // Find decimal numbers
highlight_matches(results, color) -> Result<()>
Crea anotaciones de resaltado para los resultados de búsqueda. Cada resultado recibe una anotación de resaltado amarilla (o de color personalizado) en su página.
| Parámetro | Tipo | Descripción |
|---|---|---|
results |
&[SearchResult] |
Resultados de búsqueda a resaltar |
color |
[f32; 3] |
Color RGB (0.0–1.0 por componente) |
let mut pdf = Pdf::open("report.pdf")?;
let results = pdf.search("important")?;
pdf.highlight_matches(&results, [1.0, 1.0, 0.0])?; // Yellow
pdf.save("highlighted.pdf")?;
API de Búsqueda Python
La clase Python PdfDocument expone la búsqueda directamente.
doc.search(pattern, ...) -> list[dict]
doc.search(
pattern: str,
case_insensitive: bool = False,
literal: bool = False,
whole_word: bool = False,
max_results: int = 0,
) -> list[dict]
doc.search_page(page, pattern, ...) -> list[dict]
doc.search_page(
page: int,
pattern: str,
case_insensitive: bool = False,
literal: bool = False,
whole_word: bool = False,
max_results: int = 0,
) -> list[dict]
API de Búsqueda JavaScript
La clase WasmPdfDocument expone la misma funcionalidad de búsqueda.
doc.search(pattern, ...) -> Array
doc.search(pattern, caseInsensitive?, literal?, wholeWord?, maxResults?) -> Array
doc.searchPage(pageIndex, pattern, ...) -> Array
doc.searchPage(pageIndex, pattern, caseInsensitive?, literal?, wholeWord?, maxResults?) -> Array
Ejemplo:
const doc = new WasmPdfDocument(bytes);
// Search all pages, case-insensitive
const results = doc.search("error|warning", true);
for (const r of results) {
console.log(`Page ${r.page}: '${r.text}'`);
}
// Search a single page with whole-word matching
const pageResults = doc.searchPage(0, "abstract", true, false, true);
doc.free();
¿Cómo serializo los resultados de búsqueda a JSON?
Varios bindings exponen un serializador de un solo paso que convierte la lista de resultados de búsqueda de una página en un array JSON en un único cruce de frontera FFI — Rust serializa la lista completa y el binding la decodifica, en lugar de pasar cada campo de cada coincidencia individualmente. Este es el mismo mecanismo que usan internamente los métodos SearchPage de Go y C# para decodificar sus resultados.
La firma C ABI es la definición de referencia:
char *pdf_oxide_search_results_to_json(
const FfiSearchResults *results,
int32_t *error_code);
Recibe el manejador de resultados opaco devuelto por pdf_document_search_page(...) y devuelve una cadena JSON UTF-8 asignada con malloc (libérela con pdf_free_string). Cada elemento contiene el page, el text y el cuadro delimitador (x, y, width, height) de la coincidencia.
Swift — el wrapper agrupa la búsqueda y la serialización en una sola llamada, searchResultsToJson(_:_:caseSensitive:):
import PdfOxide
let doc = try PdfDocument(path: "report.pdf")
// Search page 0 for "conclusion" and get the matches as a JSON string
let json = try doc.searchResultsToJson(0, "conclusion", caseSensitive: false)
print(json)
// [{"page":0,"text":"conclusion","x":72.0,"y":650.5,"width":85.3,"height":12.0}, ...]
Go / C#. Estos bindings llaman a
pdf_oxide_search_results_to_jsoninternamente y te entregan registros nativos ya decodificados, por lo que no necesitas invocar el serializador directamente. Usadoc.SearchPage(...)(Go:doc.SearchPage(page, text, caseSensitive); C#:doc.SearchPage(pageIndex, text, caseSensitive)) y obtendrás resultados fuertemente tipados. Para obtener JSON en esos lenguajes, serializa los registros devueltos con la biblioteca JSON estándar (encoding/json/System.Text.Json).
Python / Rust. Los métodos Python
doc.search(...)/doc.search_page(...)ya devuelven registros nativoslist[dict](serialízalos directamente conjson.dumps), y Rust devuelveVec<SearchResult>tipado que puedes serializar conserde_json. Ninguno de los dos necesita el serializador C-ABI.
Ejemplos Avanzados
Buscar y resaltar con color personalizado
use pdf_oxide::api::Pdf;
use pdf_oxide::search::SearchOptions;
let mut pdf = Pdf::open("contract.pdf")?;
// Find all dollar amounts
let options = SearchOptions::new()
.with_literal(false); // regex mode
let results = pdf.search_with_options(r"\$[\d,]+\.?\d*", options)?;
println!("Found {} dollar amounts", results.len());
for r in &results {
println!(" Page {}: {}", r.page + 1, r.text);
}
// Highlight them in green
pdf.highlight_matches(&results, [0.6, 1.0, 0.6])?;
pdf.save("highlighted_amounts.pdf")?;
Búsqueda con restricción de rango de páginas
from pdf_oxide import PdfDocument
doc = PdfDocument("book.pdf")
# Search only the first 10 pages
results = doc.search(
"introduction",
case_insensitive=True,
whole_word=True,
max_results=5,
)
for r in results:
print(f"Found on page {r['page'] + 1}")
Construir un índice de búsqueda en múltiples PDFs
use pdf_oxide::PdfDocument;
use pdf_oxide::search::{TextSearcher, SearchOptions};
use std::collections::HashMap;
let files = vec!["paper_a.pdf", "paper_b.pdf", "paper_c.pdf"];
let query = "machine learning";
let options = SearchOptions::case_insensitive();
let mut index: HashMap<String, Vec<(usize, String)>> = HashMap::new();
for file in &files {
let mut doc = PdfDocument::open(file)?;
let results = TextSearcher::search(&mut doc, query, &options)?;
for r in results {
index.entry(file.to_string())
.or_default()
.push((r.page, r.text));
}
}
for (file, matches) in &index {
println!("{}: {} matches", file, matches.len());
for (page, text) in matches {
println!(" Page {}: '{}'", page + 1, text);
}
}
Extraer contexto alrededor de las coincidencias
use pdf_oxide::PdfDocument;
use pdf_oxide::search::{TextSearcher, SearchOptions};
let mut doc = PdfDocument::open("report.pdf")?;
let options = SearchOptions::new().with_case_insensitive(true);
let results = TextSearcher::search(&mut doc, "error", &options)?;
for r in &results {
// Extract full page text for context
let page_text = doc.extract_text(r.page)?;
// Show 50 chars before and after the match
let start = r.start_index.saturating_sub(50);
let end = (r.end_index + 50).min(page_text.len());
let context = &page_text[start..end];
println!("Page {} match: ...{}...", r.page + 1, context.trim());
}
Preguntas Frecuentes
¿Cómo obtengo los resultados de búsqueda como JSON? En Swift, llama a doc.searchResultsToJson(page, term, caseSensitive:), que ejecuta la búsqueda en la página y devuelve un array JSON de coincidencias en una sola llamada. En Python y Rust, la búsqueda devuelve registros nativos (list[dict] / Vec<SearchResult>) que se serializan con json.dumps / serde_json. Go y C# devuelven registros tipados que se serializan con encoding/json / System.Text.Json.
¿Qué contiene cada coincidencia JSON? El page de la coincidencia (índice 0), el text coincidente y el cuadro delimitador combinado: x, y, width, height (puntos PDF, origen en la esquina inferior izquierda).
¿La búsqueda usa regex o texto literal por defecto? Los patrones se compilan como expresiones regulares a menos que actives el modo literal (with_literal(true) / literal=True), que escapa los metacaracteres de regex y hace coincidir el texto tal cual.
¿La búsqueda admite coincidencia sin distinción de mayúsculas y búsqueda de palabras completas? Sí — establece case_insensitive y whole_word en SearchOptions (Rust) o pásalos como argumentos de palabra clave (Python) / opciones (otros bindings).
Páginas Relacionadas
- Extracción de Texto – La extracción de texto sobre la que opera la búsqueda
- Extracción con Ámbito – Extracción por región y JSON de región estructurado
- Extracción de Anotaciones – Anotaciones creadas por highlight_matches
- Conversión a Markdown – Convertir el contexto de resultados de búsqueda a Markdown