C / FFI Office 库 — 快速上手
office_oxide 的 C API 是 Rust 内核之上一层稳定且轻薄的 ABI。所有上层绑定(Go、C#、Node.js 原生)都调用同一组入口点。如果某个生态没有现成的封装,你随时可以直接链接 C 库。
权威接口在 include/office_oxide_c/office_oxide.h。本指南介绍同一 API。
安装
方式 1 — 从 Rust 源码构建
git clone https://github.com/yfedoseev/office_oxide
cd office_oxide
cargo build --release --lib
共享库会输出到 target/release/:
| OS | 文件 |
|---|---|
| Linux | liboffice_oxide.so |
| macOS | liboffice_oxide.dylib |
| Windows | office_oxide.dll(+ .lib 导入库) |
头文件位于 include/office_oxide_c/office_oxide.h。
方式 2 — 安装预编译库
install -Dm644 include/office_oxide_c/office_oxide.h /usr/local/include/office_oxide.h
install -Dm755 target/release/liboffice_oxide.so /usr/local/lib/liboffice_oxide.so
读取文档
#include <stdio.h>
#include <stdlib.h>
#include "office_oxide.h"
int main(void) {
int err = 0;
char *text = office_extract_text("report.docx", &err);
if (!text) {
fprintf(stderr, "office_oxide failed: code=%d\n", err);
return 1;
}
printf("%s\n", text);
office_oxide_free_string(text);
return 0;
}
构建并运行:
cc quickstart.c -I/usr/local/include -L/usr/local/lib -loffice_oxide -o quickstart
LD_LIBRARY_PATH=/usr/local/lib ./quickstart
核心 API
库信息
const char *version = office_oxide_version(); // "0.1.0" — 不要 free
const char *fmt = office_oxide_detect_format("f"); // "docx"/... 或 NULL
Document(只读)
int err = 0;
OfficeDocumentHandle *doc = office_document_open("file.xlsx", &err);
if (!doc) { /* 处理 err */ }
const char *fmt = office_document_format(doc); // "xlsx" — 不要 free
char *text = office_document_plain_text(doc, &err);
char *md = office_document_to_markdown(doc, &err);
char *html = office_document_to_html(doc, &err);
char *ir = office_document_to_ir_json(doc, &err);
if (office_document_save_as(doc, "file.docx", &err) != 0) { /* 处理 err */ }
office_oxide_free_string(text);
office_oxide_free_string(md);
office_oxide_free_string(html);
office_oxide_free_string(ir);
office_document_free(doc);
从内存缓冲打开:
uint8_t *data = ...;
size_t len = ...;
OfficeDocumentHandle *doc =
office_document_open_from_bytes(data, len, "docx", &err);
format 必须是 "docx" | "xlsx" | "pptx" | "doc" | "xls" | "ppt" 中的一个。
EditableDocument
编辑会保留所有未修改的 OPC 部件。仅支持 DOCX、XLSX、PPTX。
int err = 0;
OfficeEditableHandle *ed = office_editable_open("template.docx", &err);
if (!ed) { /* 处理 err */ }
int64_t n = office_editable_replace_text(ed, "{{name}}", "Alice", &err);
if (n < 0) { /* 处理 err */ }
printf("%lld 处替换\n", (long long)n);
if (office_editable_save(ed, "out.docx", &err) != 0) { /* 处理 err */ }
office_editable_free(ed);
设置 XLSX 单元格:
OfficeEditableHandle *wb = office_editable_open("budget.xlsx", &err);
office_editable_set_cell(wb, 0, "A1", OFFICE_CELL_STRING, "Total", 0.0, &err);
office_editable_set_cell(wb, 0, "B1", OFFICE_CELL_NUMBER, NULL, 42.5, &err);
office_editable_set_cell(wb, 0, "C1", OFFICE_CELL_BOOLEAN, NULL, 1.0, &err);
office_editable_set_cell(wb, 0, "D1", OFFICE_CELL_EMPTY, NULL, 0.0, &err);
office_editable_save(wb, "budget.xlsx", &err);
office_editable_free(wb);
布尔值: value_num 非零即为 true。
序列化到堆缓冲:
size_t out_len = 0;
uint8_t *buf = office_editable_save_to_bytes(ed, &out_len, &err);
if (!buf) { /* 处理 err */ }
/* 上传或流式 buf[0..out_len] */
office_oxide_free_bytes(buf, out_len);
一次性辅助
char *text = office_extract_text("file.docx", &err);
char *md = office_to_markdown("file.pptx", &err);
char *html = office_to_html("file.xlsx", &err);
/* 每个都用 office_oxide_free_string 释放 */
内存规则
- 从
office_document_*、office_editable_*以及一次性辅助函数返回的char*必须用office_oxide_free_string(ptr)释放。 - 配合
out_len输出参数返回的uint8_t*必须用office_oxide_free_bytes(ptr, len)释放,len与 API 写入out_len的值一致。 - 不透明句柄要用对应的
*_free()释放。 office_oxide_version、office_oxide_detect_format、office_document_format返回的const char*是静态的 — 不要 free。
错误
每个可能失败的调用都接收 int *error_code 出参:
int err = 0;
OfficeDocumentHandle *doc = office_document_open("missing.docx", &err);
if (!doc) {
switch (err) {
case OFFICE_ERR_IO: fputs("io 错误\n", stderr); break;
case OFFICE_ERR_PARSE: fputs("解析错误\n", stderr); break;
/* ... */
}
}
| 宏 | 值 | 含义 |
|---|---|---|
OFFICE_OK |
0 | 成功 |
OFFICE_ERR_INVALID_ARG |
1 | nil 指针 / 未知 format 字符串 |
OFFICE_ERR_IO |
2 | 文件系统错误 |
OFFICE_ERR_PARSE |
3 | 文档损坏 |
OFFICE_ERR_EXTRACTION |
4 | 解析成功但渲染失败 |
OFFICE_ERR_INTERNAL |
5 | bug — 请提 issue |
OFFICE_ERR_UNSUPPORTED |
6 | 扩展名/特性不支持 |
单元格值常量
| 宏 | 值 |
|---|---|
OFFICE_CELL_EMPTY |
0 |
OFFICE_CELL_STRING |
1 |
OFFICE_CELL_NUMBER |
2 |
OFFICE_CELL_BOOLEAN |
3 |
旧版格式
用 office_document_open 打开 — 通过扩展名识别,魔术字节验证。office_document_save_as 透明地完成旧版格式 → OOXML 转换:
OfficeDocumentHandle *doc = office_document_open("old.xls", &err);
office_document_save_as(doc, "modern.xlsx", &err);
office_document_free(doc);
线程安全
每个句柄属于调用者 — 不要在线程间共享同一句柄。不同句柄可以并行使用,库本身是可重入的。
C++
头文件用 extern "C" 守卫包装,可直接放进 C++ 翻译单元:
#include "office_oxide.h"
配合 RAII 包装(如 std::unique_ptr<OfficeDocumentHandle, decltype(&office_document_free)>)让清理具备异常安全性。
故障排查
| 症状 | 解决 |
|---|---|
链接器: undefined reference to office_document_open |
加上 -loffice_oxide 和对应 -L;确认库以 --release --lib 构建。 |
运行时: error while loading shared libraries: liboffice_oxide.so |
把库目录加入 LD_LIBRARY_PATH(Linux)、DYLD_LIBRARY_PATH(macOS),或放到 exe 旁(Windows)。 |
open_from_bytes 报 OFFICE_ERR_INVALID_ARG |
format 必须严格是 `“docx” |
| 双重释放或堆损坏 | 检查每个 char* 用 office_oxide_free_string 释放,每个字节缓冲用 office_oxide_free_bytes(ptr, len)(用原始 out_len)。 |
office_editable_replace_text 在 XLSX 上返回 0 |
这是预期 — 表格编辑请用 office_editable_set_cell。 |
相关链接
- Rust crate — 底层内核
- Go 绑定 — 经 cgo 使用同一 C ABI
- C# 绑定 — 经 P/Invoke 使用同一 C ABI