Skip to content

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 사용.

더 보기