// 워드형 수량산출서(TFRMMULWORD, WPTools 문서) 읽기와 표 — spec_word. // 문단(
| ]*>([\s\S]*?)<\/td>/gi)].map((td) => { const c = convert(td[1], true), mark = c.names.find((n) => tag(n) === "\x12"); return { text: c.body.replace(/[\x0e\x12\x13\x16]/g, "").trim(), ...(mark && { mark }), ...(c.names.some((n) => /^N\d+$/.test(n)) && { name: true }), ...(c.names.some((n) => /^S\d+$/.test(n)) && { size: true }), }; }), ); if (table.length) rows.push({ LineData: "", markers: [], table }); continue; } const c = convert(match[1]); if (c.body.trim()) rows.push({ LineData: c.body, markers: c.markers, ...(c.totals.length && { totals: c.totals }), }); } return { Code: code, Name: name, rows, items, options }; } // ── 표 계산 ── const L = (n) => { let s = ""; for (n += 1; n > 0; n = Math.floor((n - 1) / 26)) s = String.fromCharCode(65 + ((n - 1) % 26)) + s; return s; }; const numeric = (t) => /^[+-]?[\d,]*\.?\d+$/.test(String(t).trim()); const value = (t) => (numeric(t) ? Number(String(t).replaceAll(",", "")) : 0); const show = (v) => v.toLocaleString("en-US", { maximumFractionDigits: 9 }); function recalc(table) { const errors = []; table.forEach((row) => row.forEach((cell) => { if (!cell.formula) return; try { const X = typeof EstExcelSheet !== "undefined" ? EstExcelSheet : require("./native-excel-sheet.cjs"); const v = X.evaluate(cell.formula, (r, c) => value(table[r]?.[c]?.text ?? "")); cell.text = show(typeof v === "number" ? v : Number(v) || 0); } catch (error) { errors.push(error.message); } }), ); return errors; } // 항목 수량: 수량 표지 칸의 수 function values(sheet) { const out = new Map(); for (const row of sheet.rows || []) { if (!row.table) continue; recalc(row.table); for (const r of row.table) for (const c of r) if (c.mark && !c.vmerge) out.set(c.mark, value(c.text)); } return out; } function sumFormula(table, r, c, dir) { const [dr, dc] = { right: [0, -1], left: [0, 1], up: [1, 0], down: [-1, 0] }[dir] || []; if (dr == null) throw Error("방향을 고르세요."); // 방향 = 결과 칸이 있는 쪽(원본 Ctrl+← 는 오른쪽 칸들을 더함) let rr = r + dr, cc = c + dc, last = null; const first = [rr, cc]; while (table[rr]?.[cc]) { const cell = table[rr][cc]; if (cell.name || cell.size) break; if (!cell.text.trim() || numeric(cell.text)) last = [rr, cc]; rr += dr; cc += dc; } if (!table[first[0]]?.[first[1]]) throw Error("더할 셀이 없습니다."); if (!last) last = dir === "right" ? [r, 0] : first; const a = [Math.min(first[0], last[0]), Math.min(first[1], last[1])], b = [Math.max(first[0], last[0]), Math.max(first[1], last[1])]; return `SUM(${L(a[1])}${a[0] + 1}:${L(b[1])}${b[0] + 1})`; } function vmerge(table, r1, r2, c) { if (r2 <= r1) throw Error("세로로 여러 칸을 고르세요."); const top = table[r1][c]; for (let r = r1 + 1; r <= r2; r++) { const cell = table[r][c]; if (cell.text.trim()) top.text = `${top.text} ${cell.text}`.trim(); cell.text = ""; cell.vmerge = true; } top.vtop = r2 - r1 + 1; } function vsplit(table, r, c) { const top = table[r][c]; for (let i = r + 1; i < r + (top.vtop || 1); i++) if (table[i]?.[c]) delete table[i][c].vmerge; delete top.vtop; } function create(rows, cols) { if (!(rows >= 1 && rows <= 100 && cols >= 1 && cols <= 30)) throw Error("줄 1~100, 칸 1~30 입니다."); return Array.from({ length: rows }, () => Array.from({ length: cols }, () => ({ text: "" }))); } return { entities, fromWpt, recalc, values, sumFormula, vmerge, vsplit, create, letters: L }; })(); if (typeof module !== "undefined" && module.exports) module.exports = EstQuantityWpt; |