Primeros pasos con PDF Oxide (Clojure)
PDF Oxide es el toolkit de PDF más rápido con extracción de texto integrada: 0,8 ms de media y 100 % de aciertos en 3.830 PDFs. El binding de Clojure es un wrapper idiomático y ligero sobre el maduro binding de Java fyi.oxide:pdf-oxide, que es el dueño del único puente nativo JNI. No añade nada de código nativo: invoca las clases de Java por interop y devuelve valores cómodos para Clojure (java.util.List → vector, java.util.Optional → valor o nil).
Instalación
Añade el binding de Java a tu deps.edn. El namespace de Clojure (pdf_oxide.core) vive en tu árbol de fuentes y lo envuelve:
{:deps {fyi.oxide/pdf-oxide {:mvn/version "0.3.69"}}}
Los tipos de handle (Pdf, PdfDocument, DocumentEditor) son AutoCloseable, así que usa with-open para una limpieza determinista.
Guía rápida
Construye un PDF a partir de Markdown, vuelve a abrirlo y extrae su texto. Cada paso devuelve valores Clojure normales.
(require '[pdf-oxide.core :as pdf])
(with-open [p (pdf/from-markdown "# Hello\n\nbody\n")
d (pdf/open (pdf/save p))]
(println "pages: " (pdf/page-count d))
(println "producer:" (or (pdf/producer d) "(none)"))
(println (pdf/extract-text d 0)))
Abrir un PDF
pdf/open acepta tanto un array de bytes como una cadena con la ruta del sistema de archivos, con una contraseña opcional para documentos cifrados.
(require '[pdf-oxide.core :as pdf])
;; Desde una ruta
(with-open [d (pdf/open "research-paper.pdf")]
(println "pages:" (pdf/page-count d)))
;; Desde bytes (p. ej. descargado de S3 o HTTP)
(with-open [d (pdf/open pdf-bytes)]
(println (pdf/extract-text d 0)))
;; Documento cifrado
(with-open [d (pdf/open "confidential.pdf" "secret")]
(println (pdf/extract-text d 0)))
También puedes autenticarte después de abrir:
(with-open [d (pdf/open "confidential.pdf")]
(when (pdf/authenticate d "secret")
(println (pdf/extract-text d 0))))
Extracción de texto
Extrae texto plano de cualquier página por su índice (basado en cero).
(require '[pdf-oxide.core :as pdf])
(with-open [d (pdf/open "report.pdf")]
;; Una sola página
(println (pdf/extract-text d 0))
;; Todas las páginas
(doseq [i (range (pdf/page-count d))]
(println "--- Page" (inc i) "---")
(println (pdf/extract-text d i))))
Elementos de página
pdf/page devuelve un PdfPage. A partir de él puedes obtener palabras, líneas, caracteres, tablas, imágenes y anotaciones, cada uno como un vector de Clojure. Los objetos de palabra, línea y carácter exponen .text y .bbox por interop.
(require '[pdf-oxide.core :as pdf])
(with-open [d (pdf/open "paper.pdf")]
(let [pg (pdf/page d 0)]
(println "page width:" (.width pg))
;; Palabras con sus bounding boxes
(doseq [w (take 8 (pdf/words pg))]
(println " " (.text w) "@" (.bbox w)))
;; Otros vectores de elementos
(println "lines: " (count (pdf/lines pg)))
(println "chars: " (count (pdf/chars pg)))
(println "tables: " (count (pdf/tables pg)))
(println "images: " (count (pdf/images pg)))
(println "annotations:" (count (pdf/annotations pg)))
;; Texto plano de toda la página, o de una región recortada (BBox)
(println (pdf/page-text pg))))
Para recortar la extracción a una región, pasa un fyi.oxide.pdf.geometry.BBox:
(import '[fyi.oxide.pdf.geometry BBox])
(with-open [d (pdf/open "paper.pdf")]
(let [pg (pdf/page d 0)]
(println (pdf/page-text pg (BBox. 0.0 0.0 1000.0 1000.0)))))
Conversión a Markdown y HTML
Convierte el documento entero o una sola página a Markdown o HTML.
(require '[pdf-oxide.core :as pdf])
(with-open [d (pdf/open "paper.pdf")]
;; Documento entero
(println (pdf/to-markdown d))
(println (pdf/to-html d))
;; Una sola página (basada en cero)
(println (pdf/to-markdown d 0))
(println (pdf/to-html d 0)))
Para una estructura más rica, pdf/extract-structured devuelve el árbol estructurado de elementos de una página:
(with-open [d (pdf/open "paper.pdf")]
(println (pdf/extract-structured d 0)))
Búsqueda
pdf/search recorre el documento entero y devuelve un vector de objetos de coincidencia. Cada coincidencia expone .text por interop.
(require '[pdf-oxide.core :as pdf])
(with-open [d (pdf/open "manual.pdf")]
(doseq [m (pdf/search d "configuration")]
(println (.text m))))
Renderizado
Renderiza una página a un array de bytes PNG, opcionalmente a un DPI elegido.
(require '[pdf-oxide.core :as pdf]
'[clojure.java.io :as io])
(with-open [d (pdf/open "paper.pdf")]
;; DPI por defecto
(io/copy (pdf/render d 0) (io/file "page-0.png"))
;; DPI explícito
(io/copy (pdf/render d 0 150) (io/file "page-0@150.png")))
Creación
El tipo Pdf proporciona funciones de fábrica. pdf/save serializa un Pdf ya construido a un array de bytes.
(require '[pdf-oxide.core :as pdf]
'[clojure.java.io :as io])
;; Desde Markdown
(with-open [p (pdf/from-markdown "# Hello World\n\nThis is a PDF.")]
(io/copy (pdf/save p) (io/file "output.pdf")))
;; Desde HTML
(with-open [p (pdf/from-html "<h1>Invoice</h1><p>Amount: $42</p>")]
(io/copy (pdf/save p) (io/file "invoice.pdf")))
Edición y redacción
pdf/editor abre un DocumentEditor (desde un array de bytes o una ruta) para ediciones estructurales. Depura los metadatos, marca regiones para redacción y aplícalas de forma destructiva; luego serializa con pdf/editor-save.
(require '[pdf-oxide.core :as pdf]
'[clojure.java.io :as io])
(import '[fyi.oxide.pdf.geometry BBox])
(with-open [ed (pdf/editor "form.pdf")]
(pdf/scrub-metadata ed)
(pdf/add-redaction ed 0 (BBox. 10.0 10.0 50.0 20.0))
(pdf/apply-redactions ed)
(io/copy (pdf/editor-save ed) (io/file "redacted.pdf")))
Metadatos y ciclo de vida
pdf/producer y pdf/creator devuelven los metadatos del documento como un valor, o nil cuando no están presentes (el java.util.Optional se desempaqueta por ti). Prefiere with-open; pdf/close y pdf/open? son válvulas de escape para la gestión manual del ciclo de vida.
(require '[pdf-oxide.core :as pdf])
(let [d (pdf/open "paper.pdf")]
(println "open? " (pdf/open? d))
(println "producer:" (or (pdf/producer d) "(none)"))
(println "creator: " (or (pdf/creator d) "(none)"))
(pdf/close d)
(println "open? " (pdf/open? d)))
Próximos pasos
- Primeros pasos con Java – el binding de Java sobre el que se apoya este wrapper
- Extracción de texto – opciones de extracción detalladas y recetas
- Creación de PDF – creación avanzada con metadatos y estilos
- Edición – modificación de PDFs existentes, anotaciones y redacción