Skip to content

Primeros pasos con PDF Oxide (Kotlin)

PDF Oxide es la librería de PDF más rápida para la JVM con extracción de texto integrada: 0,8 ms de media y 100 % de éxito en 3.830 PDF. El binding de Kotlin es una fachada idiomática y lista para Android sobre el binding de Java: añade use { } a los handles que se pueden cerrar y convierte los retornos Optional<T> de Java en T? anulables. Una sola librería para extraer, crear y editar PDF. Licencia MIT, construida sobre un núcleo en Rust.

Instalación

Añade el binding de Kotlin a tu build.gradle.kts. Este arrastra de forma transitiva el binding de Java, que es el propietario del puente nativo JNI:

dependencies {
    implementation("fyi.oxide:pdf-oxide-kotlin:0.3.69")
}

Requisitos: JDK 17 o superior. En Android, distribuye la librería nativa libpdf_oxide_jni.so en jniLibs/<abi>/; en la JVM de escritorio el cargador la encuentra automáticamente (puedes anular este comportamiento con -Dfyi.oxide.pdf.lib.path=<path> cuando lo necesites).

Guía rápida

Construye un PDF a partir de Markdown, ábrelo y vuelve a leer el texto. Los handles Pdf y PdfDocument son AutoCloseable, así que envuélvelos en use { }:

import fyi.oxide.pdf.Pdf
import fyi.oxide.pdf.PdfDocument
import fyi.oxide.pdf.producerOrNull

Pdf.fromMarkdown("# Hello pdf_oxide\n\nThis is a **Kotlin** binding.\n").use { pdf ->
    PdfDocument.open(pdf.save()).use { doc ->
        println("pages:    ${doc.pageCount()}")
        println("producer: ${doc.producerOrNull() ?: "(none)"}")
        println(doc.extractText(0))
    }
}

Pdf.fromMarkdown(String) devuelve un constructor Pdf que se puede cerrar; pdf.save() lo serializa a un ByteArray. PdfDocument.open(ByteArray) abre esos bytes para su lectura.

Abrir un PDF

Abre un documento existente a partir de bytes e inspecciona sus metadatos. producerOrNull() y creatorOrNull() son las vistas anulables de Kotlin sobre los getters Optional de Java:

import fyi.oxide.pdf.PdfDocument
import fyi.oxide.pdf.producerOrNull
import fyi.oxide.pdf.creatorOrNull

PdfDocument.open(pdfBytes).use { doc ->
    println("open:     ${doc.isOpen}")
    println("pages:    ${doc.pageCount()}")
    println("producer: ${doc.producerOrNull() ?: "(none)"}")
    println("creator:  ${doc.creatorOrNull() ?: "(none)"}")
}

Extracción de texto

Extrae texto plano de cualquier página por su índice (empezando en cero), o recorre todas las páginas:

import fyi.oxide.pdf.PdfDocument

PdfDocument.open(pdfBytes).use { doc ->
    // una sola página
    println(doc.extractText(0))

    // todas las páginas
    for (i in 0 until doc.pageCount()) {
        println("--- Page ${i + 1} ---")
        println(doc.extractText(i))
    }
}

Elementos de la página

doc.page(i) devuelve un PdfPage que expone la geometría estructurada: palabras, líneas, caracteres, tablas, imágenes y anotaciones. Cada palabra incluye su texto y un cuadro delimitador:

import fyi.oxide.pdf.PdfDocument

PdfDocument.open(pdfBytes).use { doc ->
    val page = doc.page(0)
    println("size: ${page.width()} x ${page.height()}")

    page.words().take(8).forEach { word ->
        println("${word.text()} @ ${word.bbox()}")
    }

    println("lines:       ${page.lines().size}")
    println("chars:       ${page.chars().size}")
    println("tables:      ${page.tables().size}")
    println("images:      ${page.images().size}")
    println("annotations: ${page.annotations().size}")
}

El bbox() de una palabra es un BBox con utilidades como width() y height().

Conversión a Markdown y HTML

Convierte todo el documento a Markdown, o renderiza una página a HTML:

import fyi.oxide.pdf.PdfDocument

PdfDocument.open(pdfBytes).use { doc ->
    val markdown = doc.toMarkdown()  // todas las páginas
    println(markdown)

    val html = doc.toHtml()
    println(html)
}

Búsqueda

Busca texto en todo el documento. Cada coincidencia expone su texto mediante text():

import fyi.oxide.pdf.PdfDocument

PdfDocument.open(pdfBytes).use { doc ->
    val matches = doc.search("configuration")
    matches.forEach { m ->
        println("match: ${m.text()}")
    }
}

Extracción automática

AutoExtractor ejecuta toda la canalización de extracción en una sola llamada y devuelve un AutoResult con el texto, más renderizados opcionales en Markdown/HTML. Las extensiones markdownOrNull() / htmlOrNull() convierten los retornos Optional de Java en valores anulables:

import fyi.oxide.pdf.PdfDocument
import fyi.oxide.pdf.AutoExtractor
import fyi.oxide.pdf.markdownOrNull
import fyi.oxide.pdf.htmlOrNull

PdfDocument.open(pdfBytes).use { doc ->
    val result = AutoExtractor.of(doc).extractDocument()
    println(result.text())
    result.markdownOrNull()?.let { println(it) }
    result.htmlOrNull()?.let { println(it) }
}

Edición

DocumentEditor abre un PDF para realizar ediciones estructurales —por ejemplo, depurar los metadatos antes de compartirlo— y luego serializa el resultado de vuelta a bytes:

import fyi.oxide.pdf.DocumentEditor

DocumentEditor.open(pdfBytes).use { editor ->
    editor.scrubMetadata()
    val cleaned: ByteArray = editor.save()
    println("cleaned: ${cleaned.size} bytes")
}

Próximos pasos