C / FFI Office Library — Quick Start
The office_oxide C API is a stable, thin ABI over the Rust core. All higher-level bindings (Go, C#, Node.js native) call these same entry points, so if your language doesn’t have a ready-made wrapper you can always link the C library directly.
The authoritative surface lives in include/office_oxide_c/office_oxide.h; this guide walks through the same API.
Install
Option 1 — build from Rust source
git clone https://github.com/yfedoseev/office_oxide
cd office_oxide
cargo build --release --lib
The shared library lands under target/release/:
| OS | File |
|---|---|
| Linux | liboffice_oxide.so |
| macOS | liboffice_oxide.dylib |
| Windows | office_oxide.dll (+ .lib import library) |
The header lives at include/office_oxide_c/office_oxide.h.
Option 2 — install a prebuilt library
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
Read a document
#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;
}
Build and run:
cc quickstart.c -I/usr/local/include -L/usr/local/lib -loffice_oxide -o quickstart
LD_LIBRARY_PATH=/usr/local/lib ./quickstart
Core API
Library info
const char *version = office_oxide_version(); // "0.1.0" — don't free
const char *fmt = office_oxide_detect_format("f"); // "docx"/... or NULL
Document (read-only)
int err = 0;
OfficeDocumentHandle *doc = office_document_open("file.xlsx", &err);
if (!doc) { /* handle err */ }
const char *fmt = office_document_format(doc); // "xlsx" — don't 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) {
/* handle 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);
Open from an in-memory buffer:
uint8_t *data = ...;
size_t len = ...;
OfficeDocumentHandle *doc =
office_document_open_from_bytes(data, len, "docx", &err);
format must be one of the six strings "docx" | "xlsx" | "pptx" | "doc" | "xls" | "ppt".
EditableDocument
Editing preserves every unmodified OPC part on save. DOCX, XLSX, and PPTX only.
int err = 0;
OfficeEditableHandle *ed = office_editable_open("template.docx", &err);
if (!ed) { /* handle err */ }
int64_t n = office_editable_replace_text(ed, "{{name}}", "Alice", &err);
if (n < 0) { /* handle err */ }
printf("%lld replacements\n", (long long)n);
if (office_editable_save(ed, "out.docx", &err) != 0) {
/* handle err */
}
office_editable_free(ed);
Set XLSX cells:
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);
For booleans, non-zero value_num = true.
Serialize to a heap buffer:
size_t out_len = 0;
uint8_t *buf = office_editable_save_to_bytes(ed, &out_len, &err);
if (!buf) { /* handle err */ }
/* upload or stream buf[0..out_len] */
office_oxide_free_bytes(buf, out_len);
One-shot helpers
char *text = office_extract_text("file.docx", &err);
char *md = office_to_markdown("file.pptx", &err);
char *html = office_to_html("file.xlsx", &err);
/* free each with office_oxide_free_string */
Memory rules
- Strings returned as
char*fromoffice_document_*,office_editable_*, and the one-shot helpers must be freed withoffice_oxide_free_string(ptr). - Byte buffers returned as
uint8_t*(with anout_lenout-parameter) must be freed withoffice_oxide_free_bytes(ptr, len). Thelenmust match the value the API wrote toout_len. - Opaque handles must be freed with their matching
*_free()call. const char*values fromoffice_oxide_version,office_oxide_detect_format, andoffice_document_formatare static — do not free.
Errors
Every fallible call takes an int *error_code out-parameter:
int err = 0;
OfficeDocumentHandle *doc = office_document_open("missing.docx", &err);
if (!doc) {
switch (err) {
case OFFICE_ERR_IO: fputs("io error\n", stderr); break;
case OFFICE_ERR_PARSE: fputs("parse error\n", stderr); break;
/* ... */
}
}
| Macro | Value | Meaning |
|---|---|---|
OFFICE_OK |
0 | success |
OFFICE_ERR_INVALID_ARG |
1 | nil pointer / unknown format string |
OFFICE_ERR_IO |
2 | filesystem error |
OFFICE_ERR_PARSE |
3 | malformed document |
OFFICE_ERR_EXTRACTION |
4 | parsed but rendering failed |
OFFICE_ERR_INTERNAL |
5 | bug — please file an issue |
OFFICE_ERR_UNSUPPORTED |
6 | extension / feature not supported |
Cell value constants
| Macro | Value |
|---|---|
OFFICE_CELL_EMPTY |
0 |
OFFICE_CELL_STRING |
1 |
OFFICE_CELL_NUMBER |
2 |
OFFICE_CELL_BOOLEAN |
3 |
Legacy formats
Open with office_document_open — extension drives format detection, magic bytes verify it. office_document_save_as converts legacy → OOXML transparently:
OfficeDocumentHandle *doc = office_document_open("old.xls", &err);
office_document_save_as(doc, "modern.xlsx", &err);
office_document_free(doc);
Thread safety
Each handle is owned by the caller — do not share a single handle between threads. Different handles can be used in parallel; the library itself is reentrant.
C++
The header is wrapped in extern "C" guards, so it drops straight into a C++ translation unit:
#include "office_oxide.h"
Pair with RAII wrappers (std::unique_ptr<OfficeDocumentHandle, decltype(&office_document_free)>) to make cleanup exception-safe.
Troubleshooting
| Symptom | Fix |
|---|---|
Linker: undefined reference to office_document_open |
Add -loffice_oxide and a matching -L flag; ensure the library was built --release --lib. |
Runtime: error while loading shared libraries: liboffice_oxide.so |
Add the library directory to LD_LIBRARY_PATH (Linux), DYLD_LIBRARY_PATH (macOS), or next to the exe (Windows). |
OFFICE_ERR_INVALID_ARG on open_from_bytes |
format must be exactly `“docx” |
| Double-free or heap corruption | Free each char* with office_oxide_free_string, each byte buffer with office_oxide_free_bytes(ptr, len) using the original out_len. |
office_editable_replace_text returns 0 on XLSX |
Expected — use office_editable_set_cell for spreadsheet edits. |
See also
- Rust crate — the underlying core
- Go binding — uses this same C ABI via cgo
- C# binding — uses this same C ABI via P/Invoke