Skip to content

형식 무관 IR

DocumentIR은 Office Oxide가 형식 간에 놓는 구조적 다리입니다. .docx, .xlsx, 레거시 .ppt를 열어도 같은 형태가 돌아옵니다 — 섹션 목록과, 각 섹션 내부의 타입 있는 요소(제목, 단락, 표, 목록, 이미지) 시퀀스.

IR은 to_html, save_as, 레거시 → OOXML 변환을 받칩니다. 다운스트림 파이프라인 — 검색 인덱스, RAG 청커, 커스텀 렌더러 — 에도 가장 적합한 접점입니다. 형식별로 6개의 스키마가 아닌 하나의 스키마만 처리하면 되니까요.

IR 읽기

Rust

use office_oxide::Document;

let doc = Document::open("report.docx")?;
let ir = doc.to_ir();

for section in &ir.sections {
    println!("{:?}", section.title);
    for el in &section.elements {
        // el은 Element enum — Heading, Paragraph, Table, List, Image, ...
    }
}

Python

from office_oxide import Document

with Document.open("report.docx") as doc:
    ir = doc.to_ir()

for section in ir["sections"]:
    print(section.get("title"))
    for el in section["elements"]:
        kind = el["kind"]   # "Heading" | "Paragraph" | "Table" | "List" | "Image"

JavaScript

using doc = Document.open('report.docx');
const ir = doc.toIr();

for (const section of ir.sections) {
  for (const el of section.elements) {
    // el.kind: "Heading" | "Paragraph" | "Table" | "List" | "Image"
  }
}

Go

import "encoding/json"

irJSON, _ := doc.ToIRJSON()

var ir struct {
    Sections []struct {
        Title    *string           `json:"title"`
        Elements []json.RawMessage `json:"elements"`
    } `json:"sections"`
}
_ = json.Unmarshal([]byte(irJSON), &ir)

C#

using System.Text.Json;

using var doc = Document.Open("report.docx");
using var ir = JsonDocument.Parse(doc.ToIrJson());

foreach (var section in ir.RootElement.GetProperty("sections").EnumerateArray())
{
    // ...
}

스키마

형태는 의도적으로 작고 안정적입니다.

{
  "sections": [
    {
      "title": "선택적 섹션 제목",     // string | null
      "elements": [
        { "kind": "Heading", "level": 1, "text": "..." },
        { "kind": "Paragraph", "runs": [
            { "text": "Hello ", "bold": false, "italic": false },
            { "text": "world", "bold": true,  "italic": false }
        ] },
        { "kind": "List", "ordered": true, "items": ["one", "two"] },
        { "kind": "Table", "rows": [
            ["A1", "B1"],
            ["A2", "B2"]
        ] },
        { "kind": "Image", "filename": "image1.png", "data": "<base64>" }
      ]
    }
  ]
}

형식별 매핑:

형식 섹션 경계 비고
DOCX <w:sectPr>마다 한 섹션(없으면 본문 전체) 제목은 w:pStyle로 키잉
XLSX 워크시트마다 한 섹션 title = 시트 이름; 사용 범위마다 Table 요소 한 개
PPTX 슬라이드마다 한 섹션 title = 슬라이드 제목 자리표시자; 노트는 마지막 단락으로 부착
DOC / XLS / PPT OOXML 대응품과 같은 형태 레거시 CFB 파이프라인을 통해 파싱

IR을 쓰는 이유

  • 한 번 빌드하고 여러 번 렌더. DOCX, XLSX, PPTX를 같은 형태로 변환하고 단일 검색/청킹 파이프라인으로 처리.
  • 형식 변경을 견디는 LLM 컨텍스트. 소스가 .doc에서 .docx로 옮겨가도 스키마는 흔들리지 않습니다.
  • save_as로 라운드트립. IR을 편집하고 어떤 지원 형식으로든 새 문서를 씁니다.
use office_oxide::create::create_from_ir;
use office_oxide::DocumentFormat;

create_from_ir(&ir, DocumentFormat::Docx, "out.docx")?;

직렬화

Rust의 DocumentIRSerialize / Deserialize(serde 경유)를 derive합니다. Python의 to_ir()은 일반 dict을 반환합니다(이미 JSON 직렬화 가능). Node, Go, C#, C 바인딩은 to_ir_json() / ToIRJSON() / ToIrJson()으로 JSON 문자열을 노출합니다.

더 보기