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이 0이 아니면 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*는 정적입니다 — 해제하지 마세요.
오류
실패 가능한 모든 호출은 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 | 버그 — 이슈를 등록해 주세요 |
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 사용)으로 해제했는지 확인. |
XLSX에서 office_editable_replace_text가 0 반환 |
정상 동작 — 스프레드시트 편집은 office_editable_set_cell 사용. |