Skip to content

Biblioteca Office para Rust — Inicio Rápido

office_oxide es una crate en Rust puro para analizar, convertir y editar documentos de Office: DOCX, XLSX, PPTX, junto con sus predecesores binarios DOC, XLS y PPT. Una sola crate, un handle Document unificado, cero dependencias nativas.

Instalación

[dependencies]
office_oxide = "0.1.0"

Features opcionales:

office_oxide = { version = "0.1.0", features = ["mmap"] }       # apertura mmap para OOXML grandes
office_oxide = { version = "0.1.0", features = ["parallel"] }   # ayudantes de parseo paralelo basados en rayon

Leer un documento

use office_oxide::Document;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let doc = Document::open("report.docx")?;
    println!("{}", doc.plain_text());
    Ok(())
}

O el helper de una sola llamada:

let text = office_oxide::extract_text("report.docx")?;

API principal

El handle Document se comporta igual en todos los formatos — detección por extensión más verificación con magic bytes.

use office_oxide::{Document, DocumentFormat};

let doc = Document::open("file.xlsx")?;
assert_eq!(doc.format(), DocumentFormat::Xlsx);

let plain = doc.plain_text();
let md    = doc.to_markdown();
let html  = doc.to_html();
let ir    = doc.to_ir();             // IR independiente del formato

doc.save_as("file.docx")?;            // heredado → OOXML también funciona

Document::open acepta AsRef<Path>; Document::from_reader toma Read + Seek + Send + 'static con un DocumentFormat explícito.

Atajos a nivel de módulo:

let text = office_oxide::extract_text("file.docx")?;
let md   = office_oxide::to_markdown("file.pptx")?;
let html = office_oxide::to_html("file.xlsx")?;

Acceso específico al formato

Cuando necesites datos propios del formato — hojas, diapositivas, celdas de tabla — desempaqueta el documento interno:

if let Some(xlsx) = doc.as_xlsx() {
    for sheet in xlsx.sheets() {
        println!("sheet: {}", sheet.name());
    }
}

El mismo patrón aplica a as_docx, as_pptx, as_doc, as_xls y as_ppt.

Edición

EditableDocument ejecuta read-modify-write y conserva todas las partes OPC sin tocar (imágenes, gráficos, estilos, relaciones). Solo DOCX, XLSX y PPTX.

use office_oxide::edit::EditableDocument;

let mut doc = EditableDocument::open("template.docx")?;
let n = doc.replace_text("{{name}}", "Alice");
println!("{n} reemplazos");
doc.save("out.docx")?;

replace_text recorre <w:t> en DOCX y <a:t> en PPTX, devolviendo el número de reemplazos (XLSX devuelve 0 — usa set_cell).

Establecer celdas XLSX

use office_oxide::edit::EditableDocument;
use office_oxide::xlsx::edit::CellValue;

let mut wb = EditableDocument::open("budget.xlsx")?;
wb.set_cell(0, "B2", CellValue::Number(42.0))?;
wb.set_cell(0, "A1", CellValue::String("Total".into()))?;
wb.set_cell(0, "C1", CellValue::Boolean(true))?;
wb.set_cell(0, "D1", CellValue::Empty)?;
wb.save("budget.xlsx")?;

Los índices de hoja empiezan en cero; las referencias de celda usan la notación estándar (A1, AA12).

IR independiente del formato

DocumentIR es el puente estructural entre formatos — soporta to_html, save_as y la conversión de formatos heredados. Implementa Serialize / Deserialize, así que puede emitir JSON para herramientas posteriores.

let legacy = Document::open("old.doc")?;
legacy.save_as("migrated.docx")?;     // CFB → OOXML en una línea

Abrir desde bytes

use std::io::Cursor;
use office_oxide::{Document, DocumentFormat};

let bytes = std::fs::read("file.pptx")?;
let doc = Document::from_reader(Cursor::new(bytes), DocumentFormat::Pptx)?;

Apertura mediante memory map

Con la feature mmap, Document::open_mmap evita copiar archivos OOXML grandes al heap:

let doc = Document::open_mmap("huge.xlsx")?;

Solo DOCX/XLSX/PPTX admiten mmap; los parsers CFB heredados requieren búferes propios.

Errores

Cada punto de entrada con posibles fallos devuelve office_oxide::Result<T>, es decir, Result<T, OfficeError>. El enum cubre IO, parseo, formato no soportado y fallos de extracción.

use office_oxide::{Document, OfficeError};

match Document::open("weird.file") {
    Ok(doc) => println!("{}", doc.plain_text()),
    Err(OfficeError::UnsupportedFormat(ext)) => eprintln!("no se puede abrir .{ext}"),
    Err(e) => eprintln!("falló: {e}"),
}

Solución de problemas

Síntoma Causa probable
UnsupportedFormat("(none)") Ruta sin extensión — abre con from_reader y DocumentFormat explícito.
Texto DOC ilegible El archivo puede estar cifrado o usar una piece-table inusual. Verifica el magic CFB D0 CF 11 E0.
Hyperlinks ausentes en DOCX Los hyperlinks se resuelven vía w:rels. Comprueba que el .rels esté dentro del ZIP.
Stack overflow en hilos de pila pequeña office_oxide lanza un hilo de parseo de 16 MB cuando RLIMIT_STACK < 12 MB. En pools propios usa Builder::stack_size(16 * 1024 * 1024).

Véase también