Skip to content

Rust-бібліотека для Office — швидкий старт

office_oxide — це чисто-Rust крейт для розбору, конвертації та редагування документів Office: DOCX, XLSX, PPTX і їхніх legacy-попередників DOC, XLS та PPT. Один крейт, єдиний Document-handle, жодних нативних залежностей.

Встановлення

[dependencies]
office_oxide = "0.1.0"

Опційні фічі:

office_oxide = { version = "0.1.0", features = ["mmap"] }       # mmap-відкриття великих OOXML
office_oxide = { version = "0.1.0", features = ["parallel"] }   # хелпери паралельного парсингу на rayon

Прочитати документ

use office_oxide::Document;

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

Або одноразовий помічник:

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

Базовий API

Document-handle поводиться однаково для всіх форматів — визначення за розширенням плюс перевірка magic-байтами.

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

doc.save_as("file.docx")?;            // legacy → OOXML теж працює

Document::open приймає AsRef<Path>; Document::from_reader бере Read + Seek + Send + 'static з явним DocumentFormat.

Скорочення модульного рівня:

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

Доступ до специфіки формату

Коли потрібні специфічні дані формату — листи, слайди, клітинки — розпакуйте внутрішній документ:

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

Те саме працює для as_docx, as_pptx, as_doc, as_xls, as_ppt.

Редагування

EditableDocument виконує read-modify-write, зберігаючи дослівно всі незмінені OPC-частини (зображення, діаграми, стилі, зв’язки). Підтримуються лише DOCX, XLSX і PPTX.

use office_oxide::edit::EditableDocument;

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

replace_text обходить <w:t> у DOCX та <a:t> у PPTX, повертає кількість замін (для XLSX поверне 0 — використовуйте set_cell).

Запис клітинок 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")?;

Індекси листів нумеруються з нуля; адреси клітинок — стандартна нотація (A1, AA12).

Формат-незалежний IR

DocumentIR — структурний міст між форматами. На ньому тримаються to_html, save_as та legacy-конверсія. Реалізує Serialize / Deserialize, тож можна емітити JSON для подальшого тулінгу.

let legacy = Document::open("old.doc")?;
legacy.save_as("migrated.docx")?;     // CFB → OOXML одним рядком

Відкриття з байтів

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)?;

Memory-mapped відкриття

З фічею mmap функція Document::open_mmap не копіює великі OOXML-файли в купу:

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

mmap доступний лише для DOCX/XLSX/PPTX; legacy-парсери CFB вимагають owned-буферів.

Помилки

Усі помилки повертаються через office_oxide::Result<T>, тобто Result<T, OfficeError>. Перелік охоплює IO, парсинг, непідтримуваний формат і провали видобування.

use office_oxide::{Document, OfficeError};

match Document::open("weird.file") {
    Ok(doc) => println!("{}", doc.plain_text()),
    Err(OfficeError::UnsupportedFormat(ext)) => eprintln!("не вийде відкрити .{ext}"),
    Err(e) => eprintln!("помилка: {e}"),
}

Діагностика

Симптом Причина
UnsupportedFormat("(none)") У шляху немає розширення — відкривайте через from_reader з явним DocumentFormat.
Каша в тексті DOC Файл може бути зашифрованим або використовувати рідкісну кодування piece-table. Перевірте magic CFB D0 CF 11 E0.
Гіперпосилання відсутні в DOCX Посилання резолвляться через w:rels. Перевірте, що .rels-сусід є в ZIP.
Stack overflow на маленьких стеках потоків office_oxide піднімає 16-МБ парс-потік, якщо RLIMIT_STACK < 12 МБ. У власних пулах задавайте Builder::stack_size(16 * 1024 * 1024).

Дивіться також