// extract_hwp.mjs — HWP/HWPX 파일을 텍스트로 추출 (rhwp WASM 사용) // 사용: node extract_hwp.mjs // 출력: stdout JSON {ok, text, paragraphs, sections, error?} import init, { HwpDocument } from "./rhwp.js"; import { readFile } from "node:fs/promises"; async function main() { const inputPath = process.argv[2]; if (!inputPath) { console.error(JSON.stringify({ ok: false, error: "missing arg: input path" })); process.exit(1); } try { // WASM 초기화 — Node.js fs로 직접 로드 const { readFileSync } = await import("node:fs"); const wasmPath = new URL("./rhwp_bg.wasm", import.meta.url); const wasmBytes = readFileSync(wasmPath); await init({ module_or_path: wasmBytes }); // HWP 파일 로드 const fileBytes = await readFile(inputPath); const doc = new HwpDocument(new Uint8Array(fileBytes)); // Method 1: getTextRange로 모든 section/paragraph 순회 const allParas = []; let sectionCount = 0; try { sectionCount = doc.getSectionCount(); } catch (e) { sectionCount = 1; } for (let s = 0; s < sectionCount; s++) { let paraCount = 0; try { paraCount = doc.getParagraphCount(s); } catch (e) { continue; } for (let p = 0; p < paraCount; p++) { let paraLen = 0; try { paraLen = doc.getParagraphLength(s, p); } catch (e) { continue; } if (paraLen === 0) { allParas.push(""); continue; } try { const text = doc.getTextRange(s, p, 0, paraLen); allParas.push(text || ""); } catch (e) { allParas.push(`[para ${s}.${p} 추출 실패]`); } } } const finalText = allParas.filter(p => p && p.trim()).join("\n"); // free try { doc.free(); } catch (e) {} console.log(JSON.stringify({ ok: true, text: finalText, paragraphs: allParas.length, sections: sectionCount, char_count: finalText.length, }, null, 0)); } catch (e) { console.error(JSON.stringify({ ok: false, error: `${e.name || "Error"}: ${e.message || e}`, stack: (e.stack || "").split("\n").slice(0, 5).join(" | "), })); process.exit(2); } } main();