diff --git a/resources/master_template/report/stab_report.json b/resources/master_template/report/stab_report.json
new file mode 100644
index 000000000..9ded03dad
--- /dev/null
+++ b/resources/master_template/report/stab_report.json
@@ -0,0 +1,2606 @@
+{
+ "종류": "통합문서",
+ "판": 1,
+ "열": "stab_report",
+ "기본": {
+ "글꼴": "맑은 고딕",
+ "크기": 10,
+ "행높이": 18
+ },
+ "서식": [
+ {},
+ {
+ "크기": 18,
+ "굵게": true,
+ "가로": "center",
+ "세로": "center"
+ },
+ {
+ "크기": 10,
+ "글자색": "#555555",
+ "가로": "center",
+ "세로": "center",
+ "줄바꿈": true
+ },
+ {
+ "크기": 11,
+ "굵게": true,
+ "세로": "center",
+ "테두리": {
+ "아래": {
+ "선": "medium"
+ }
+ }
+ },
+ {
+ "굵게": true,
+ "채움": "#F2F2F2",
+ "가로": "center",
+ "세로": "center",
+ "줄바꿈": true,
+ "테두리": {
+ "위": {
+ "선": "thin"
+ },
+ "아래": {
+ "선": "thin"
+ },
+ "왼": {
+ "선": "thin"
+ },
+ "오른": {
+ "선": "thin"
+ }
+ }
+ },
+ {
+ "가로": "left",
+ "세로": "center",
+ "줄바꿈": true,
+ "테두리": {
+ "위": {
+ "선": "thin"
+ },
+ "아래": {
+ "선": "thin"
+ },
+ "왼": {
+ "선": "thin"
+ },
+ "오른": {
+ "선": "thin"
+ }
+ }
+ },
+ {
+ "굵게": true,
+ "채움": "#DDEBF7",
+ "가로": "center",
+ "세로": "center",
+ "줄바꿈": true,
+ "테두리": {
+ "위": {
+ "선": "thin"
+ },
+ "아래": {
+ "선": "thin"
+ },
+ "왼": {
+ "선": "thin"
+ },
+ "오른": {
+ "선": "thin"
+ }
+ }
+ },
+ {
+ "가로": "center",
+ "세로": "center",
+ "테두리": {
+ "위": {
+ "선": "thin"
+ },
+ "아래": {
+ "선": "thin"
+ },
+ "왼": {
+ "선": "thin"
+ },
+ "오른": {
+ "선": "thin"
+ }
+ }
+ },
+ {
+ "가로": "center",
+ "세로": "center",
+ "형식": "0.00",
+ "테두리": {
+ "위": {
+ "선": "thin"
+ },
+ "아래": {
+ "선": "thin"
+ },
+ "왼": {
+ "선": "thin"
+ },
+ "오른": {
+ "선": "thin"
+ }
+ }
+ },
+ {
+ "가로": "center",
+ "세로": "center",
+ "형식": "0",
+ "테두리": {
+ "위": {
+ "선": "thin"
+ },
+ "아래": {
+ "선": "thin"
+ },
+ "왼": {
+ "선": "thin"
+ },
+ "오른": {
+ "선": "thin"
+ }
+ }
+ },
+ {
+ "굵게": true,
+ "글자색": "#C00000",
+ "채움": "#FFC7CE",
+ "가로": "center",
+ "세로": "center",
+ "테두리": {
+ "위": {
+ "선": "thin"
+ },
+ "아래": {
+ "선": "thin"
+ },
+ "왼": {
+ "선": "thin"
+ },
+ "오른": {
+ "선": "thin"
+ }
+ }
+ },
+ {
+ "굵게": true,
+ "글자색": "#006100",
+ "채움": "#E2EFDA",
+ "가로": "center",
+ "세로": "center",
+ "테두리": {
+ "위": {
+ "선": "thin"
+ },
+ "아래": {
+ "선": "thin"
+ },
+ "왼": {
+ "선": "thin"
+ },
+ "오른": {
+ "선": "thin"
+ }
+ }
+ },
+ {
+ "크기": 12,
+ "굵게": true,
+ "글자색": "#C00000",
+ "가로": "center",
+ "세로": "center",
+ "줄바꿈": true
+ },
+ {
+ "가로": "center",
+ "세로": "center",
+ "형식": "#,##0.0",
+ "테두리": {
+ "위": {
+ "선": "thin"
+ },
+ "아래": {
+ "선": "thin"
+ },
+ "왼": {
+ "선": "thin"
+ },
+ "오른": {
+ "선": "thin"
+ }
+ }
+ },
+ {
+ "가로": "center",
+ "세로": "center",
+ "형식": "#,##0",
+ "테두리": {
+ "위": {
+ "선": "thin"
+ },
+ "아래": {
+ "선": "thin"
+ },
+ "왼": {
+ "선": "thin"
+ },
+ "오른": {
+ "선": "thin"
+ }
+ }
+ },
+ {
+ "가로": "left",
+ "세로": "top",
+ "줄바꿈": true,
+ "테두리": {
+ "위": {
+ "선": "thin"
+ },
+ "아래": {
+ "선": "thin"
+ },
+ "왼": {
+ "선": "thin"
+ },
+ "오른": {
+ "선": "thin"
+ }
+ }
+ },
+ {
+ "가로": "center",
+ "세로": "center",
+ "형식": "0.0",
+ "테두리": {
+ "위": {
+ "선": "thin"
+ },
+ "아래": {
+ "선": "thin"
+ },
+ "왼": {
+ "선": "thin"
+ },
+ "오른": {
+ "선": "thin"
+ }
+ }
+ },
+ {
+ "가로": "center",
+ "세로": "center",
+ "형식": "#,##0.00",
+ "테두리": {
+ "위": {
+ "선": "thin"
+ },
+ "아래": {
+ "선": "thin"
+ },
+ "왼": {
+ "선": "thin"
+ },
+ "오른": {
+ "선": "thin"
+ }
+ }
+ }
+ ],
+ "시트": [
+ {
+ "id": "s1",
+ "이름": "표지",
+ "칸": {
+ "B4": {
+ "값": "비탈면 안정성 검토 보고서",
+ "서식": 1
+ },
+ "C4": {
+ "서식": 1
+ },
+ "D4": {
+ "서식": 1
+ },
+ "E4": {
+ "서식": 1
+ },
+ "B5": {
+ "값": "임도 노선 비탈면 안정해석 결과",
+ "서식": 2
+ },
+ "C5": {
+ "서식": 2
+ },
+ "D5": {
+ "서식": 2
+ },
+ "E5": {
+ "서식": 2
+ },
+ "B8": {
+ "값": "사업명",
+ "서식": 4
+ },
+ "C8": {
+ "서식": 5
+ },
+ "D8": {
+ "서식": 5
+ },
+ "E8": {
+ "서식": 5
+ },
+ "B9": {
+ "값": "노선명",
+ "서식": 4
+ },
+ "C9": {
+ "서식": 5
+ },
+ "D9": {
+ "서식": 5
+ },
+ "E9": {
+ "서식": 5
+ },
+ "B10": {
+ "값": "발주처",
+ "서식": 4
+ },
+ "C10": {
+ "서식": 5
+ },
+ "D10": {
+ "서식": 5
+ },
+ "E10": {
+ "서식": 5
+ },
+ "B11": {
+ "값": "작성일",
+ "서식": 4
+ },
+ "C11": {
+ "서식": 5
+ },
+ "D11": {
+ "서식": 5
+ },
+ "E11": {
+ "서식": 5
+ },
+ "B12": {
+ "값": "작성자",
+ "서식": 4
+ },
+ "C12": {
+ "서식": 5
+ },
+ "D12": {
+ "서식": 5
+ },
+ "E12": {
+ "서식": 5
+ },
+ "B14": {
+ "값": "문헌 추정치 · 현장 시험 없음",
+ "서식": 12
+ },
+ "C14": {
+ "서식": 12
+ },
+ "D14": {
+ "서식": 12
+ },
+ "E14": {
+ "서식": 12
+ },
+ "B15": {
+ "값": "※ 지반 물성치는 문헌 · 기준값을 적용한 추정치이며 현장 조사 · 시험 성과를 반영하지 않았음.",
+ "서식": 2
+ },
+ "C15": {
+ "서식": 2
+ },
+ "D15": {
+ "서식": 2
+ },
+ "E15": {
+ "서식": 2
+ }
+ },
+ "병합": [
+ "B4:E4",
+ "B5:E5",
+ "C8:E8",
+ "C9:E9",
+ "C10:E10",
+ "C11:E11",
+ "C12:E12",
+ "B14:E14",
+ "B15:E15"
+ ],
+ "열": {
+ "A": {
+ "폭": 4
+ },
+ "B": {
+ "폭": 18
+ },
+ "C": {
+ "폭": 22
+ },
+ "D": {
+ "폭": 22
+ },
+ "E": {
+ "폭": 22
+ },
+ "F": {
+ "폭": 4
+ }
+ },
+ "행": {
+ "4": {
+ "높이": 60
+ },
+ "5": {
+ "높이": 24
+ },
+ "14": {
+ "높이": 30
+ },
+ "15": {
+ "높이": 30
+ },
+ "8": {
+ "높이": 30
+ },
+ "9": {
+ "높이": 30
+ },
+ "10": {
+ "높이": 30
+ },
+ "11": {
+ "높이": 30
+ },
+ "12": {
+ "높이": 30
+ }
+ },
+ "보기": {
+ "눈금선": false
+ },
+ "인쇄": {
+ "용지": "A4",
+ "방향": "portrait",
+ "맞춤": {
+ "폭": 1,
+ "높이": 0
+ },
+ "여백": {
+ "위": 25,
+ "아래": 20,
+ "왼": 20,
+ "오른": 20
+ }
+ }
+ },
+ {
+ "id": "s2",
+ "이름": "개요",
+ "칸": {
+ "A1": {
+ "값": "개요 및 판정 요약",
+ "서식": 1
+ },
+ "B1": {
+ "서식": 1
+ },
+ "C1": {
+ "서식": 1
+ },
+ "D1": {
+ "서식": 1
+ },
+ "E1": {
+ "서식": 1
+ },
+ "F1": {
+ "서식": 1
+ },
+ "A2": {
+ "값": "수치는 「측점표」 시트를 따라감 — 해석이 바뀌면 [보고서 갱신] 뒤 새 값으로 바뀜",
+ "서식": 2
+ },
+ "B2": {
+ "서식": 2
+ },
+ "C2": {
+ "서식": 2
+ },
+ "D2": {
+ "서식": 2
+ },
+ "E2": {
+ "서식": 2
+ },
+ "F2": {
+ "서식": 2
+ },
+ "A4": {
+ "값": "1. 해석 대상",
+ "서식": 3
+ },
+ "B4": {
+ "서식": 3
+ },
+ "C4": {
+ "서식": 3
+ },
+ "D4": {
+ "서식": 3
+ },
+ "E4": {
+ "서식": 3
+ },
+ "F4": {
+ "서식": 3
+ },
+ "A5": {
+ "값": "대상 측점 수",
+ "서식": 4
+ },
+ "B5": {
+ "식": "COUNTA(측점표!A6:A20000)",
+ "서식": 9
+ },
+ "C5": {
+ "값": "개소",
+ "서식": 7
+ },
+ "A6": {
+ "값": "해석 구간 연장",
+ "서식": 4
+ },
+ "B6": {
+ "식": "IF(B5=0,0,MAX(측점표!B6:B20000)-MIN(측점표!B6:B20000))",
+ "서식": 13
+ },
+ "C6": {
+ "값": "m",
+ "서식": 7
+ },
+ "A8": {
+ "값": "2. 조건별 최소 안전율",
+ "서식": 3
+ },
+ "B8": {
+ "서식": 3
+ },
+ "C8": {
+ "서식": 3
+ },
+ "D8": {
+ "서식": 3
+ },
+ "E8": {
+ "서식": 3
+ },
+ "F8": {
+ "서식": 3
+ },
+ "A9": {
+ "값": "조건",
+ "서식": 6
+ },
+ "B9": {
+ "값": "최소 안전율",
+ "서식": 6
+ },
+ "C9": {
+ "값": "기준 안전율",
+ "서식": 6
+ },
+ "D9": {
+ "값": "판정",
+ "서식": 6
+ },
+ "E9": {
+ "값": "최소 안전율 측점",
+ "서식": 6
+ },
+ "F9": {
+ "서식": 6
+ },
+ "A10": {
+ "값": "건기",
+ "서식": 4
+ },
+ "B10": {
+ "식": "IF(COUNT(측점표!D6:D20000)=0,\"-\",MIN(측점표!D6:D20000))",
+ "서식": 8
+ },
+ "C10": {
+ "식": "입력조건!B12",
+ "서식": 8
+ },
+ "D10": {
+ "식": "IF(B10=\"-\",\"-\",IF(B10>=C10,\"O.K\",\"N.G\"))",
+ "서식": 7
+ },
+ "E10": {
+ "식": "IF(B10=\"-\",\"-\",INDEX(측점표!A6:A20000,MATCH(B10,측점표!D6:D20000,0)))",
+ "서식": 7
+ },
+ "F10": {
+ "서식": 7
+ },
+ "A11": {
+ "값": "우기",
+ "서식": 4
+ },
+ "B11": {
+ "식": "IF(COUNT(측점표!E6:E20000)=0,\"-\",MIN(측점표!E6:E20000))",
+ "서식": 8
+ },
+ "C11": {
+ "식": "입력조건!B13",
+ "서식": 8
+ },
+ "D11": {
+ "식": "IF(B11=\"-\",\"-\",IF(B11>=C11,\"O.K\",\"N.G\"))",
+ "서식": 7
+ },
+ "E11": {
+ "식": "IF(B11=\"-\",\"-\",INDEX(측점표!A6:A20000,MATCH(B11,측점표!E6:E20000,0)))",
+ "서식": 7
+ },
+ "F11": {
+ "서식": 7
+ },
+ "A12": {
+ "값": "지진",
+ "서식": 4
+ },
+ "B12": {
+ "식": "IF(COUNT(측점표!F6:F20000)=0,\"-\",MIN(측점표!F6:F20000))",
+ "서식": 8
+ },
+ "C12": {
+ "식": "입력조건!B14",
+ "서식": 8
+ },
+ "D12": {
+ "식": "IF(B12=\"-\",\"-\",IF(B12>=C12,\"O.K\",\"N.G\"))",
+ "서식": 7
+ },
+ "E12": {
+ "식": "IF(B12=\"-\",\"-\",INDEX(측점표!A6:A20000,MATCH(B12,측점표!F6:F20000,0)))",
+ "서식": 7
+ },
+ "F12": {
+ "서식": 7
+ },
+ "A14": {
+ "값": "3. 종합 판정",
+ "서식": 3
+ },
+ "B14": {
+ "서식": 3
+ },
+ "C14": {
+ "서식": 3
+ },
+ "D14": {
+ "서식": 3
+ },
+ "E14": {
+ "서식": 3
+ },
+ "F14": {
+ "서식": 3
+ },
+ "A15": {
+ "값": "기준 미달(N.G) 측점 수",
+ "서식": 4
+ },
+ "B15": {
+ "식": "COUNTIF(측점표!J6:J20000,\"N.G\")",
+ "서식": 9
+ },
+ "C15": {
+ "값": "개소",
+ "서식": 7
+ },
+ "A16": {
+ "값": "종합 판정",
+ "서식": 4
+ },
+ "B16": {
+ "식": "IF(B5=0,\"-\",IF(B15=0,\"O.K\",\"N.G\"))",
+ "서식": 7
+ },
+ "A17": {
+ "값": "판정 글",
+ "서식": 4
+ },
+ "B17": {
+ "식": "IF(B5=0,\"해석 결과 없음\",\"대상 측점 \"&B5&\"개소 중 \"&B15&\"개소가 기준 안전율에 미달함. \"&IF(B15=0,\"전 구간이 기준을 만족함.\",\"미달 구간은 보강 대책 검토가 필요함.\"))",
+ "서식": 15
+ },
+ "C17": {
+ "서식": 15
+ },
+ "D17": {
+ "서식": 15
+ },
+ "E17": {
+ "서식": 15
+ },
+ "F17": {
+ "서식": 15
+ },
+ "B18": {
+ "서식": 15
+ },
+ "C18": {
+ "서식": 15
+ },
+ "D18": {
+ "서식": 15
+ },
+ "E18": {
+ "서식": 15
+ },
+ "F18": {
+ "서식": 15
+ },
+ "B19": {
+ "서식": 15
+ },
+ "C19": {
+ "서식": 15
+ },
+ "D19": {
+ "서식": 15
+ },
+ "E19": {
+ "서식": 15
+ },
+ "F19": {
+ "서식": 15
+ },
+ "A18": {
+ "서식": 4
+ },
+ "A19": {
+ "서식": 4
+ },
+ "A21": {
+ "값": "4. 의견",
+ "서식": 3
+ },
+ "B21": {
+ "서식": 3
+ },
+ "C21": {
+ "서식": 3
+ },
+ "D21": {
+ "서식": 3
+ },
+ "E21": {
+ "서식": 3
+ },
+ "F21": {
+ "서식": 3
+ },
+ "A22": {
+ "서식": 15
+ },
+ "B22": {
+ "서식": 15
+ },
+ "C22": {
+ "서식": 15
+ },
+ "D22": {
+ "서식": 15
+ },
+ "E22": {
+ "서식": 15
+ },
+ "F22": {
+ "서식": 15
+ },
+ "A23": {
+ "서식": 15
+ },
+ "B23": {
+ "서식": 15
+ },
+ "C23": {
+ "서식": 15
+ },
+ "D23": {
+ "서식": 15
+ },
+ "E23": {
+ "서식": 15
+ },
+ "F23": {
+ "서식": 15
+ },
+ "A24": {
+ "서식": 15
+ },
+ "B24": {
+ "서식": 15
+ },
+ "C24": {
+ "서식": 15
+ },
+ "D24": {
+ "서식": 15
+ },
+ "E24": {
+ "서식": 15
+ },
+ "F24": {
+ "서식": 15
+ },
+ "A25": {
+ "서식": 15
+ },
+ "B25": {
+ "서식": 15
+ },
+ "C25": {
+ "서식": 15
+ },
+ "D25": {
+ "서식": 15
+ },
+ "E25": {
+ "서식": 15
+ },
+ "F25": {
+ "서식": 15
+ },
+ "A26": {
+ "서식": 15
+ },
+ "B26": {
+ "서식": 15
+ },
+ "C26": {
+ "서식": 15
+ },
+ "D26": {
+ "서식": 15
+ },
+ "E26": {
+ "서식": 15
+ },
+ "F26": {
+ "서식": 15
+ },
+ "A27": {
+ "서식": 15
+ },
+ "B27": {
+ "서식": 15
+ },
+ "C27": {
+ "서식": 15
+ },
+ "D27": {
+ "서식": 15
+ },
+ "E27": {
+ "서식": 15
+ },
+ "F27": {
+ "서식": 15
+ },
+ "A28": {
+ "서식": 15
+ },
+ "B28": {
+ "서식": 15
+ },
+ "C28": {
+ "서식": 15
+ },
+ "D28": {
+ "서식": 15
+ },
+ "E28": {
+ "서식": 15
+ },
+ "F28": {
+ "서식": 15
+ },
+ "A29": {
+ "서식": 15
+ },
+ "B29": {
+ "서식": 15
+ },
+ "C29": {
+ "서식": 15
+ },
+ "D29": {
+ "서식": 15
+ },
+ "E29": {
+ "서식": 15
+ },
+ "F29": {
+ "서식": 15
+ },
+ "A30": {
+ "서식": 15
+ },
+ "B30": {
+ "서식": 15
+ },
+ "C30": {
+ "서식": 15
+ },
+ "D30": {
+ "서식": 15
+ },
+ "E30": {
+ "서식": 15
+ },
+ "F30": {
+ "서식": 15
+ }
+ },
+ "병합": [
+ "A1:F1",
+ "A2:F2",
+ "A4:F4",
+ "A8:F8",
+ "E9:F9",
+ "E10:F10",
+ "E11:F11",
+ "E12:F12",
+ "A14:F14",
+ "B17:F19",
+ "A17:A19",
+ "A21:F21",
+ "A22:F30"
+ ],
+ "열": {
+ "A": {
+ "폭": 20
+ },
+ "B": {
+ "폭": 16
+ },
+ "C": {
+ "폭": 16
+ },
+ "D": {
+ "폭": 14
+ },
+ "E": {
+ "폭": 16
+ },
+ "F": {
+ "폭": 16
+ }
+ },
+ "행": {
+ "1": {
+ "높이": 36
+ },
+ "2": {
+ "높이": 20
+ },
+ "9": {
+ "높이": 22
+ },
+ "5": {
+ "높이": 22
+ },
+ "6": {
+ "높이": 22
+ },
+ "10": {
+ "높이": 22
+ },
+ "11": {
+ "높이": 22
+ },
+ "12": {
+ "높이": 22
+ },
+ "15": {
+ "높이": 22
+ },
+ "16": {
+ "높이": 22
+ },
+ "17": {
+ "높이": 22
+ },
+ "18": {
+ "높이": 22
+ },
+ "19": {
+ "높이": 22
+ }
+ },
+ "보기": {
+ "눈금선": false
+ },
+ "인쇄": {
+ "용지": "A4",
+ "방향": "portrait",
+ "맞춤": {
+ "폭": 1,
+ "높이": 0
+ },
+ "여백": {
+ "위": 20,
+ "아래": 15,
+ "왼": 15,
+ "오른": 15
+ }
+ },
+ "조건부서식": [
+ {
+ "id": "ng_d",
+ "범위": [
+ {
+ "r0": 9,
+ "c0": 3,
+ "r1": 11,
+ "c1": 3
+ }
+ ],
+ "조건": {
+ "종류": "글포함",
+ "글": "N.G"
+ },
+ "스타일": {
+ "채움": "#FFC7CE",
+ "글자색": "#C00000",
+ "굵게": true
+ }
+ },
+ {
+ "id": "ng_b16",
+ "범위": [
+ {
+ "r0": 15,
+ "c0": 1,
+ "r1": 15,
+ "c1": 1
+ }
+ ],
+ "조건": {
+ "종류": "글포함",
+ "글": "N.G"
+ },
+ "스타일": {
+ "채움": "#FFC7CE",
+ "글자색": "#C00000",
+ "굵게": true
+ }
+ }
+ ]
+ },
+ {
+ "id": "s3",
+ "이름": "입력조건",
+ "칸": {
+ "A1": {
+ "값": "입력 조건",
+ "서식": 1
+ },
+ "B1": {
+ "서식": 1
+ },
+ "C1": {
+ "서식": 1
+ },
+ "D1": {
+ "서식": 1
+ },
+ "E1": {
+ "서식": 1
+ },
+ "F1": {
+ "서식": 1
+ },
+ "G1": {
+ "서식": 1
+ },
+ "A2": {
+ "값": "문헌 추정치 · 현장 시험 없음 — 지반 물성치는 문헌 · 기준값 적용",
+ "서식": 2
+ },
+ "B2": {
+ "서식": 2
+ },
+ "C2": {
+ "서식": 2
+ },
+ "D2": {
+ "서식": 2
+ },
+ "E2": {
+ "서식": 2
+ },
+ "F2": {
+ "서식": 2
+ },
+ "G2": {
+ "서식": 2
+ },
+ "A4": {
+ "값": "1. 해석 방법",
+ "서식": 3
+ },
+ "B4": {
+ "서식": 3
+ },
+ "C4": {
+ "서식": 3
+ },
+ "D4": {
+ "서식": 3
+ },
+ "E4": {
+ "서식": 3
+ },
+ "F4": {
+ "서식": 3
+ },
+ "G4": {
+ "서식": 3
+ },
+ "A5": {
+ "값": "해석 기법",
+ "서식": 4
+ },
+ "B5": {
+ "서식": 5
+ },
+ "C5": {
+ "서식": 5
+ },
+ "D5": {
+ "서식": 5
+ },
+ "E5": {
+ "서식": 5
+ },
+ "F5": {
+ "서식": 5
+ },
+ "G5": {
+ "서식": 5
+ },
+ "A6": {
+ "값": "활동면 탐색",
+ "서식": 4
+ },
+ "B6": {
+ "서식": 5
+ },
+ "C6": {
+ "서식": 5
+ },
+ "D6": {
+ "서식": 5
+ },
+ "E6": {
+ "서식": 5
+ },
+ "F6": {
+ "서식": 5
+ },
+ "G6": {
+ "서식": 5
+ },
+ "A7": {
+ "값": "3D 해석",
+ "서식": 4
+ },
+ "B7": {
+ "서식": 5
+ },
+ "C7": {
+ "서식": 5
+ },
+ "D7": {
+ "서식": 5
+ },
+ "E7": {
+ "서식": 5
+ },
+ "F7": {
+ "서식": 5
+ },
+ "G7": {
+ "서식": 5
+ },
+ "A8": {
+ "값": "적용 기준",
+ "서식": 4
+ },
+ "B8": {
+ "서식": 5
+ },
+ "C8": {
+ "서식": 5
+ },
+ "D8": {
+ "서식": 5
+ },
+ "E8": {
+ "서식": 5
+ },
+ "F8": {
+ "서식": 5
+ },
+ "G8": {
+ "서식": 5
+ },
+ "A10": {
+ "값": "2. 기준 안전율",
+ "서식": 3
+ },
+ "B10": {
+ "서식": 3
+ },
+ "C10": {
+ "서식": 3
+ },
+ "D10": {
+ "서식": 3
+ },
+ "E10": {
+ "서식": 3
+ },
+ "F10": {
+ "서식": 3
+ },
+ "G10": {
+ "서식": 3
+ },
+ "A11": {
+ "값": "조건",
+ "서식": 6
+ },
+ "B11": {
+ "값": "기준 안전율",
+ "서식": 6
+ },
+ "C11": {
+ "값": "비고",
+ "서식": 6
+ },
+ "D11": {
+ "서식": 6
+ },
+ "E11": {
+ "서식": 6
+ },
+ "F11": {
+ "서식": 6
+ },
+ "G11": {
+ "서식": 6
+ },
+ "A12": {
+ "값": "건기",
+ "서식": 4
+ },
+ "B12": {
+ "서식": 8
+ },
+ "C12": {
+ "서식": 5
+ },
+ "D12": {
+ "서식": 5
+ },
+ "E12": {
+ "서식": 5
+ },
+ "F12": {
+ "서식": 5
+ },
+ "G12": {
+ "서식": 5
+ },
+ "A13": {
+ "값": "우기",
+ "서식": 4
+ },
+ "B13": {
+ "서식": 8
+ },
+ "C13": {
+ "서식": 5
+ },
+ "D13": {
+ "서식": 5
+ },
+ "E13": {
+ "서식": 5
+ },
+ "F13": {
+ "서식": 5
+ },
+ "G13": {
+ "서식": 5
+ },
+ "A14": {
+ "값": "지진",
+ "서식": 4
+ },
+ "B14": {
+ "서식": 8
+ },
+ "C14": {
+ "서식": 5
+ },
+ "D14": {
+ "서식": 5
+ },
+ "E14": {
+ "서식": 5
+ },
+ "F14": {
+ "서식": 5
+ },
+ "G14": {
+ "서식": 5
+ },
+ "A16": {
+ "값": "3. 지하수 규칙",
+ "서식": 3
+ },
+ "B16": {
+ "서식": 3
+ },
+ "C16": {
+ "서식": 3
+ },
+ "D16": {
+ "서식": 3
+ },
+ "E16": {
+ "서식": 3
+ },
+ "F16": {
+ "서식": 3
+ },
+ "G16": {
+ "서식": 3
+ },
+ "A17": {
+ "값": "건기",
+ "서식": 4
+ },
+ "B17": {
+ "서식": 5
+ },
+ "C17": {
+ "서식": 5
+ },
+ "D17": {
+ "서식": 5
+ },
+ "E17": {
+ "서식": 5
+ },
+ "F17": {
+ "서식": 5
+ },
+ "G17": {
+ "서식": 5
+ },
+ "A18": {
+ "값": "우기",
+ "서식": 4
+ },
+ "B18": {
+ "서식": 5
+ },
+ "C18": {
+ "서식": 5
+ },
+ "D18": {
+ "서식": 5
+ },
+ "E18": {
+ "서식": 5
+ },
+ "F18": {
+ "서식": 5
+ },
+ "G18": {
+ "서식": 5
+ },
+ "A19": {
+ "값": "지진 시",
+ "서식": 4
+ },
+ "B19": {
+ "서식": 5
+ },
+ "C19": {
+ "서식": 5
+ },
+ "D19": {
+ "서식": 5
+ },
+ "E19": {
+ "서식": 5
+ },
+ "F19": {
+ "서식": 5
+ },
+ "G19": {
+ "서식": 5
+ },
+ "A21": {
+ "값": "4. 노면 하중",
+ "서식": 3
+ },
+ "B21": {
+ "서식": 3
+ },
+ "C21": {
+ "서식": 3
+ },
+ "D21": {
+ "서식": 3
+ },
+ "E21": {
+ "서식": 3
+ },
+ "F21": {
+ "서식": 3
+ },
+ "G21": {
+ "서식": 3
+ },
+ "A22": {
+ "값": "노면 하중",
+ "서식": 4
+ },
+ "B22": {
+ "서식": 16
+ },
+ "C22": {
+ "값": "kPa",
+ "서식": 7
+ },
+ "A24": {
+ "값": "5. 지반 물성치",
+ "서식": 3
+ },
+ "B24": {
+ "서식": 3
+ },
+ "C24": {
+ "서식": 3
+ },
+ "D24": {
+ "서식": 3
+ },
+ "E24": {
+ "서식": 3
+ },
+ "F24": {
+ "서식": 3
+ },
+ "G24": {
+ "서식": 3
+ },
+ "A25": {
+ "값": "지층",
+ "서식": 6
+ },
+ "B25": {
+ "값": "적용 구간",
+ "서식": 6
+ },
+ "C25": {
+ "값": "γt (kN/m³)",
+ "서식": 6
+ },
+ "D25": {
+ "값": "γsat (kN/m³)",
+ "서식": 6
+ },
+ "E25": {
+ "값": "c (kPa)",
+ "서식": 6
+ },
+ "F25": {
+ "값": "φ (°)",
+ "서식": 6
+ },
+ "G25": {
+ "값": "출처",
+ "서식": 6
+ },
+ "A26": {
+ "서식": 7
+ },
+ "B26": {
+ "서식": 7
+ },
+ "C26": {
+ "서식": 16
+ },
+ "D26": {
+ "서식": 16
+ },
+ "E26": {
+ "서식": 16
+ },
+ "F26": {
+ "서식": 16
+ },
+ "G26": {
+ "서식": 5
+ }
+ },
+ "병합": [
+ "A1:G1",
+ "A2:G2",
+ "A4:G4",
+ "B5:G5",
+ "B6:G6",
+ "B7:G7",
+ "B8:G8",
+ "A10:G10",
+ "C11:G11",
+ "C12:G12",
+ "C13:G13",
+ "C14:G14",
+ "A16:G16",
+ "B17:G17",
+ "B18:G18",
+ "B19:G19",
+ "A21:G21",
+ "A24:G24"
+ ],
+ "열": {
+ "A": {
+ "폭": 18
+ },
+ "B": {
+ "폭": 16
+ },
+ "C": {
+ "폭": 12
+ },
+ "D": {
+ "폭": 12
+ },
+ "E": {
+ "폭": 12
+ },
+ "F": {
+ "폭": 12
+ },
+ "G": {
+ "폭": 28
+ }
+ },
+ "행": {
+ "1": {
+ "높이": 36
+ },
+ "2": {
+ "높이": 20
+ },
+ "25": {
+ "높이": 32
+ },
+ "5": {
+ "높이": 22
+ },
+ "6": {
+ "높이": 22
+ },
+ "7": {
+ "높이": 22
+ },
+ "8": {
+ "높이": 22
+ },
+ "12": {
+ "높이": 22
+ },
+ "13": {
+ "높이": 22
+ },
+ "14": {
+ "높이": 22
+ },
+ "17": {
+ "높이": 22
+ },
+ "18": {
+ "높이": 22
+ },
+ "19": {
+ "높이": 22
+ }
+ },
+ "보기": {
+ "눈금선": false
+ },
+ "인쇄": {
+ "용지": "A4",
+ "방향": "portrait",
+ "맞춤": {
+ "폭": 1,
+ "높이": 0
+ },
+ "여백": {
+ "위": 15,
+ "아래": 15,
+ "왼": 10,
+ "오른": 10
+ },
+ "머리행": [
+ 25,
+ 25
+ ]
+ }
+ },
+ {
+ "id": "s4",
+ "이름": "측점표",
+ "칸": {
+ "A1": {
+ "값": "측점별 안전율",
+ "서식": 1
+ },
+ "B1": {
+ "서식": 1
+ },
+ "C1": {
+ "서식": 1
+ },
+ "D1": {
+ "서식": 1
+ },
+ "E1": {
+ "서식": 1
+ },
+ "F1": {
+ "서식": 1
+ },
+ "G1": {
+ "서식": 1
+ },
+ "H1": {
+ "서식": 1
+ },
+ "I1": {
+ "서식": 1
+ },
+ "J1": {
+ "서식": 1
+ },
+ "A2": {
+ "값": "판정 — 모든 조건의 안전율이 기준 이상이면 O.K · 하나라도 미달이면 N.G",
+ "서식": 2
+ },
+ "B2": {
+ "서식": 2
+ },
+ "C2": {
+ "서식": 2
+ },
+ "D2": {
+ "서식": 2
+ },
+ "E2": {
+ "서식": 2
+ },
+ "F2": {
+ "서식": 2
+ },
+ "G2": {
+ "서식": 2
+ },
+ "H2": {
+ "서식": 2
+ },
+ "I2": {
+ "서식": 2
+ },
+ "J2": {
+ "서식": 2
+ },
+ "A4": {
+ "값": "측점",
+ "서식": 6
+ },
+ "A5": {
+ "서식": 6
+ },
+ "B4": {
+ "값": "누가거리 (m)",
+ "서식": 6
+ },
+ "B5": {
+ "서식": 6
+ },
+ "C4": {
+ "값": "구분",
+ "서식": 6
+ },
+ "C5": {
+ "서식": 6
+ },
+ "D4": {
+ "값": "산정 안전율 Fs",
+ "서식": 6
+ },
+ "E4": {
+ "서식": 6
+ },
+ "F4": {
+ "서식": 6
+ },
+ "G4": {
+ "값": "기준 안전율",
+ "서식": 6
+ },
+ "H4": {
+ "서식": 6
+ },
+ "I4": {
+ "서식": 6
+ },
+ "J4": {
+ "값": "판정",
+ "서식": 6
+ },
+ "J5": {
+ "서식": 6
+ },
+ "D5": {
+ "값": "건기",
+ "서식": 6
+ },
+ "E5": {
+ "값": "우기",
+ "서식": 6
+ },
+ "F5": {
+ "값": "지진",
+ "서식": 6
+ },
+ "G5": {
+ "값": "건기",
+ "서식": 6
+ },
+ "H5": {
+ "값": "우기",
+ "서식": 6
+ },
+ "I5": {
+ "값": "지진",
+ "서식": 6
+ },
+ "A6": {
+ "서식": 7
+ },
+ "B6": {
+ "서식": 17
+ },
+ "C6": {
+ "서식": 7
+ },
+ "D6": {
+ "서식": 8
+ },
+ "E6": {
+ "서식": 8
+ },
+ "F6": {
+ "서식": 8
+ },
+ "G6": {
+ "서식": 8
+ },
+ "H6": {
+ "서식": 8
+ },
+ "I6": {
+ "서식": 8
+ },
+ "J6": {
+ "서식": 7
+ }
+ },
+ "병합": [
+ "A1:J1",
+ "A2:J2",
+ "A4:A5",
+ "B4:B5",
+ "C4:C5",
+ "D4:F4",
+ "G4:I4",
+ "J4:J5"
+ ],
+ "열": {
+ "A": {
+ "폭": 16
+ },
+ "B": {
+ "폭": 13
+ },
+ "C": {
+ "폭": 9
+ },
+ "D": {
+ "폭": 9
+ },
+ "E": {
+ "폭": 9
+ },
+ "F": {
+ "폭": 9
+ },
+ "G": {
+ "폭": 9
+ },
+ "H": {
+ "폭": 9
+ },
+ "I": {
+ "폭": 9
+ },
+ "J": {
+ "폭": 9
+ }
+ },
+ "행": {
+ "1": {
+ "높이": 36
+ },
+ "2": {
+ "높이": 20
+ },
+ "3": {
+ "높이": 8
+ },
+ "4": {
+ "높이": 22
+ },
+ "5": {
+ "높이": 22
+ }
+ },
+ "틀고정": {
+ "행": 5
+ },
+ "보기": {
+ "눈금선": false
+ },
+ "인쇄": {
+ "용지": "A4",
+ "방향": "landscape",
+ "맞춤": {
+ "폭": 1,
+ "높이": 0
+ },
+ "여백": {
+ "위": 15,
+ "아래": 15,
+ "왼": 10,
+ "오른": 10
+ },
+ "머리행": [
+ 4,
+ 5
+ ]
+ },
+ "조건부서식": [
+ {
+ "id": "ng_j",
+ "범위": [
+ {
+ "r0": 5,
+ "c0": 9,
+ "r1": 19999,
+ "c1": 9
+ }
+ ],
+ "조건": {
+ "종류": "글포함",
+ "글": "N.G"
+ },
+ "스타일": {
+ "채움": "#FFC7CE",
+ "글자색": "#C00000",
+ "굵게": true
+ }
+ }
+ ]
+ },
+ {
+ "id": "s5",
+ "이름": "대표단면",
+ "칸": {
+ "A1": {
+ "값": "대표 단면 ①",
+ "서식": 3
+ },
+ "B1": {
+ "서식": 3
+ },
+ "C1": {
+ "서식": 3
+ },
+ "D1": {
+ "서식": 3
+ },
+ "E1": {
+ "서식": 3
+ },
+ "F1": {
+ "서식": 3
+ },
+ "G1": {
+ "서식": 3
+ },
+ "H1": {
+ "서식": 3
+ },
+ "A2": {
+ "값": "선정 사유",
+ "서식": 4
+ },
+ "B2": {
+ "서식": 5
+ },
+ "C2": {
+ "서식": 5
+ },
+ "D2": {
+ "서식": 5
+ },
+ "E2": {
+ "서식": 5
+ },
+ "F2": {
+ "서식": 5
+ },
+ "G2": {
+ "서식": 5
+ },
+ "H2": {
+ "서식": 5
+ },
+ "A23": {
+ "값": "조건",
+ "서식": 6
+ },
+ "B23": {
+ "값": "해석 방법",
+ "서식": 6
+ },
+ "C23": {
+ "값": "안전율 Fs",
+ "서식": 6
+ },
+ "D23": {
+ "값": "기준",
+ "서식": 6
+ },
+ "E23": {
+ "값": "판정",
+ "서식": 6
+ },
+ "F23": {
+ "값": "활동면 반경 R (m)",
+ "서식": 6
+ },
+ "G23": {
+ "값": "중심 x (m)",
+ "서식": 6
+ },
+ "H23": {
+ "값": "중심 y (m)",
+ "서식": 6
+ },
+ "A24": {
+ "값": "건기",
+ "서식": 4
+ },
+ "B24": {
+ "서식": 7
+ },
+ "C24": {
+ "서식": 8
+ },
+ "D24": {
+ "서식": 8
+ },
+ "E24": {
+ "서식": 7
+ },
+ "F24": {
+ "서식": 13
+ },
+ "G24": {
+ "서식": 13
+ },
+ "H24": {
+ "서식": 13
+ },
+ "A25": {
+ "값": "우기",
+ "서식": 4
+ },
+ "B25": {
+ "서식": 7
+ },
+ "C25": {
+ "서식": 8
+ },
+ "D25": {
+ "서식": 8
+ },
+ "E25": {
+ "서식": 7
+ },
+ "F25": {
+ "서식": 13
+ },
+ "G25": {
+ "서식": 13
+ },
+ "H25": {
+ "서식": 13
+ },
+ "A26": {
+ "값": "지진",
+ "서식": 4
+ },
+ "B26": {
+ "서식": 7
+ },
+ "C26": {
+ "서식": 8
+ },
+ "D26": {
+ "서식": 8
+ },
+ "E26": {
+ "서식": 7
+ },
+ "F26": {
+ "서식": 13
+ },
+ "G26": {
+ "서식": 13
+ },
+ "H26": {
+ "서식": 13
+ },
+ "A27": {
+ "값": "비고",
+ "서식": 4
+ },
+ "B27": {
+ "서식": 5
+ },
+ "C27": {
+ "서식": 5
+ },
+ "D27": {
+ "서식": 5
+ },
+ "E27": {
+ "서식": 5
+ },
+ "F27": {
+ "서식": 5
+ },
+ "G27": {
+ "서식": 5
+ },
+ "H27": {
+ "서식": 5
+ }
+ },
+ "병합": [
+ "A1:H1",
+ "B2:H2",
+ "B27:H27"
+ ],
+ "열": {
+ "A": {
+ "폭": 12
+ },
+ "B": {
+ "폭": 12
+ },
+ "C": {
+ "폭": 12
+ },
+ "D": {
+ "폭": 12
+ },
+ "E": {
+ "폭": 12
+ },
+ "F": {
+ "폭": 12
+ },
+ "G": {
+ "폭": 12
+ },
+ "H": {
+ "폭": 12
+ }
+ },
+ "행": {
+ "1": {
+ "높이": 26
+ },
+ "2": {
+ "높이": 22
+ },
+ "3": {
+ "높이": 20
+ },
+ "4": {
+ "높이": 20
+ },
+ "5": {
+ "높이": 20
+ },
+ "6": {
+ "높이": 20
+ },
+ "7": {
+ "높이": 20
+ },
+ "8": {
+ "높이": 20
+ },
+ "9": {
+ "높이": 20
+ },
+ "10": {
+ "높이": 20
+ },
+ "11": {
+ "높이": 20
+ },
+ "12": {
+ "높이": 20
+ },
+ "13": {
+ "높이": 20
+ },
+ "14": {
+ "높이": 20
+ },
+ "15": {
+ "높이": 20
+ },
+ "16": {
+ "높이": 20
+ },
+ "17": {
+ "높이": 20
+ },
+ "18": {
+ "높이": 20
+ },
+ "19": {
+ "높이": 20
+ },
+ "20": {
+ "높이": 20
+ },
+ "21": {
+ "높이": 20
+ },
+ "22": {
+ "높이": 20
+ },
+ "23": {
+ "높이": 32
+ },
+ "24": {
+ "높이": 22
+ },
+ "25": {
+ "높이": 22
+ },
+ "26": {
+ "높이": 22
+ },
+ "27": {
+ "높이": 22
+ },
+ "28": {
+ "높이": 6
+ }
+ },
+ "그림": [
+ {
+ "열쇠": "fig/{측점키}.png",
+ "닻": "A3:H22",
+ "이름": "대표 단면 그림"
+ }
+ ],
+ "보기": {
+ "눈금선": false
+ },
+ "인쇄": {
+ "용지": "A4",
+ "방향": "portrait",
+ "맞춤": {
+ "폭": 1,
+ "높이": 0
+ },
+ "여백": {
+ "위": 15,
+ "아래": 15,
+ "왼": 10,
+ "오른": 10
+ },
+ "쪽나눔": [
+ 28
+ ]
+ },
+ "조건부서식": [
+ {
+ "id": "ng_e",
+ "범위": [
+ {
+ "r0": 23,
+ "c0": 4,
+ "r1": 25,
+ "c1": 4
+ }
+ ],
+ "조건": {
+ "종류": "글포함",
+ "글": "N.G"
+ },
+ "스타일": {
+ "채움": "#FFC7CE",
+ "글자색": "#C00000",
+ "굵게": true
+ }
+ }
+ ]
+ },
+ {
+ "id": "s6",
+ "이름": "3D요약",
+ "칸": {
+ "A1": {
+ "값": "3D 결과 요약",
+ "서식": 1
+ },
+ "B1": {
+ "서식": 1
+ },
+ "C1": {
+ "서식": 1
+ },
+ "D1": {
+ "서식": 1
+ },
+ "E1": {
+ "서식": 1
+ },
+ "F1": {
+ "서식": 1
+ },
+ "G1": {
+ "서식": 1
+ },
+ "H1": {
+ "서식": 1
+ },
+ "A2": {
+ "값": "3D 최소 안전율은 2D 결과와 대조 — 3D/2D 비가 1 보다 크면 2D 가 안전 쪽",
+ "서식": 2
+ },
+ "B2": {
+ "서식": 2
+ },
+ "C2": {
+ "서식": 2
+ },
+ "D2": {
+ "서식": 2
+ },
+ "E2": {
+ "서식": 2
+ },
+ "F2": {
+ "서식": 2
+ },
+ "G2": {
+ "서식": 2
+ },
+ "H2": {
+ "서식": 2
+ },
+ "A4": {
+ "값": "1. 평면 위험 띠",
+ "서식": 3
+ },
+ "B4": {
+ "서식": 3
+ },
+ "C4": {
+ "서식": 3
+ },
+ "D4": {
+ "서식": 3
+ },
+ "E4": {
+ "서식": 3
+ },
+ "F4": {
+ "서식": 3
+ },
+ "G4": {
+ "서식": 3
+ },
+ "H4": {
+ "서식": 3
+ },
+ "A18": {
+ "값": "2. 구간별 3D 안전율",
+ "서식": 3
+ },
+ "B18": {
+ "서식": 3
+ },
+ "C18": {
+ "서식": 3
+ },
+ "D18": {
+ "서식": 3
+ },
+ "E18": {
+ "서식": 3
+ },
+ "F18": {
+ "서식": 3
+ },
+ "G18": {
+ "서식": 3
+ },
+ "H18": {
+ "서식": 3
+ },
+ "A19": {
+ "값": "구간",
+ "서식": 6
+ },
+ "B19": {
+ "값": "시작 측점",
+ "서식": 6
+ },
+ "C19": {
+ "값": "끝 측점",
+ "서식": 6
+ },
+ "D19": {
+ "값": "3D 최소 Fs",
+ "서식": 6
+ },
+ "E19": {
+ "값": "2D 최소 Fs",
+ "서식": 6
+ },
+ "F19": {
+ "값": "3D/2D 비",
+ "서식": 6
+ },
+ "G19": {
+ "값": "임계 체적 (m³)",
+ "서식": 6
+ },
+ "H19": {
+ "값": "판정",
+ "서식": 6
+ },
+ "A20": {
+ "서식": 7
+ },
+ "B20": {
+ "서식": 7
+ },
+ "C20": {
+ "서식": 7
+ },
+ "D20": {
+ "서식": 8
+ },
+ "E20": {
+ "서식": 8
+ },
+ "F20": {
+ "식": "IF(E20>0,D20/E20,\"\")",
+ "서식": 8
+ },
+ "G20": {
+ "서식": 14
+ },
+ "H20": {
+ "서식": 7
+ }
+ },
+ "병합": [
+ "A1:H1",
+ "A2:H2",
+ "A4:H4",
+ "A18:H18"
+ ],
+ "열": {
+ "A": {
+ "폭": 12
+ },
+ "B": {
+ "폭": 13
+ },
+ "C": {
+ "폭": 13
+ },
+ "D": {
+ "폭": 11
+ },
+ "E": {
+ "폭": 11
+ },
+ "F": {
+ "폭": 10
+ },
+ "G": {
+ "폭": 13
+ },
+ "H": {
+ "폭": 9
+ }
+ },
+ "행": {
+ "1": {
+ "높이": 36
+ },
+ "2": {
+ "높이": 20
+ },
+ "4": {
+ "높이": 22
+ },
+ "18": {
+ "높이": 22
+ },
+ "19": {
+ "높이": 32
+ },
+ "5": {
+ "높이": 20
+ },
+ "6": {
+ "높이": 20
+ },
+ "7": {
+ "높이": 20
+ },
+ "8": {
+ "높이": 20
+ },
+ "9": {
+ "높이": 20
+ },
+ "10": {
+ "높이": 20
+ },
+ "11": {
+ "높이": 20
+ },
+ "12": {
+ "높이": 20
+ },
+ "13": {
+ "높이": 20
+ },
+ "14": {
+ "높이": 20
+ },
+ "15": {
+ "높이": 20
+ },
+ "16": {
+ "높이": 20
+ }
+ },
+ "그림": [
+ {
+ "열쇠": "fig/3d_band.png",
+ "닻": "A5:H16",
+ "이름": "평면 위험 띠"
+ }
+ ],
+ "보기": {
+ "눈금선": false
+ },
+ "인쇄": {
+ "용지": "A4",
+ "방향": "portrait",
+ "맞춤": {
+ "폭": 1,
+ "높이": 0
+ },
+ "여백": {
+ "위": 15,
+ "아래": 15,
+ "왼": 10,
+ "오른": 10
+ },
+ "머리행": [
+ 19,
+ 19
+ ]
+ },
+ "조건부서식": [
+ {
+ "id": "ng_h",
+ "범위": [
+ {
+ "r0": 19,
+ "c0": 7,
+ "r1": 19999,
+ "c1": 7
+ }
+ ],
+ "조건": {
+ "종류": "글포함",
+ "글": "N.G"
+ },
+ "스타일": {
+ "채움": "#FFC7CE",
+ "글자색": "#C00000",
+ "굵게": true
+ }
+ }
+ ]
+ }
+ ],
+ "활성": "s1"
+}
diff --git a/resources/master_template/report/stab_report_slots.md b/resources/master_template/report/stab_report_slots.md
new file mode 100644
index 000000000..3fb11f4c7
--- /dev/null
+++ b/resources/master_template/report/stab_report_slots.md
@@ -0,0 +1,81 @@
+# E05 보고서 양식 자리표 — `stab_report.json`
+
+빌더(534-3 뒤쪽)가 읽는 표. 시트 id · 칸 주소 · 서식 번호는 양식 그대로 쓰고 바꾸지 않음.
+
+## 공통
+
+- 몫: 사용자 몫(표지 · 개요) = 처음 만들 때만 채움 · 다시 만들 때(보고서 갱신) 안 건드림. 프로그램 몫(입력조건 · 측점표 · 대표단면 · 3D요약) = 갱신 때 통째 다시 씀.
+- 서식 번호(`서식` 표): 1 title · 2 sub · 3 section · 4 label · 5 text · 6 head · 7 body(가운데 글) · 8 fs(0.00) · 9 int(0) · 10 ng(N.G 빨강 채움) · 11 ok(O.K 초록) · 12 notice · 13 num1(#,##0.0) · 14 num0(#,##0) · 15 para(위 맞춤 긴 글) · 16 prop(0.0) · 17 chain(#,##0.00).
+- 판정 글(`O.K` · `N.G`) 칸 = 엔진 판정을 값으로 씀 · N.G 칸은 서식 10 · O.K 칸은 서식 11 을 직접 붙임 — 인쇄 HTML · xlsx 는 `조건부서식` 을 안 먹이므로(화면만). 양식의 `조건부서식`(글포함 `N.G` → 빨강)은 사용자가 화면에서 고친 판정 글용.
+- 수식이 보는 끝 행 = 20000(측점표 · 3D요약). 측점이 이보다 많으면 개요 수식의 `20000` 을 같이 늘림.
+- 빈 값은 칸을 안 적거나 `값` 을 안 둠(빈 글 `""` 금지).
+- 그림 열쇠 = `fig/<측점 키>.png`(측점 키 = `common_util_station_key`) · 3D 띠 = `fig/3d_band.png`. 양식의 `fig/{측점키}.png` 는 자리 표시일 뿐 — 빌더가 목록을 새로 씀.
+
+## s1 표지 (사용자 몫)
+
+| 칸 | 내용 |
+| --- | --- |
+| B4:E4 | 제목(고정 글 · 서식 1) |
+| C8:E8 · C9:E9 · C10:E10 · C11:E11 · C12:E12 | 사업명 · 노선명 · 발주처 · 작성일 · 작성자 값(병합 · 서식 5) — 왼위 칸 `C8` … `C12` 에 값 |
+| B14:E14 | 「문헌 추정치 · 현장 시험 없음」(고정 글 · 건드리지 않음) |
+
+## s2 개요 (사용자 몫 · 수식은 양식에 들어 있음)
+
+- 빌더가 쓸 것: 없음(수식이 `측점표` · `입력조건` 을 가리킴). 의견 칸 `A22:F30`(병합 · 서식 15) = 사용자 글.
+- 수식 칸: `B5` 대상 측점 수 · `B6` 연장 · `B10:B12` 조건별 최소 Fs · `C10:C12` = `입력조건!B12:B14` · `D10:D12` 판정 · `E10`·`E11`·`E12` 최소 측점 · `B15` N.G 수 · `B16` 종합 판정 · `B17`(병합 B17:F19) 판정 글.
+- 지진을 안 쓰면 `측점표!F` 열을 비움 → 개요 지진 행은 `-`.
+
+## s3 입력조건 (프로그램 몫)
+
+| 구역 | 칸 | 내용 |
+| --- | --- | --- |
+| 해석 방법 | `B5` `B6` `B7` `B8` (각 B:G 병합) | 해석 기법 · 활동면 탐색 · 3D 해석 · 적용 기준 글 |
+| 기준 안전율 | `B12` `B13` `B14` | 건기 · 우기 · 지진 기준 값(수 · 서식 8) · 지진 끔 = 글 `-` |
+| | `C12` `C13` `C14` (C:G 병합) | 비고 글 |
+| 지하수 규칙 | `B17` `B18` `B19` (B:G 병합) | 건기 · 우기 · 지진 시 규칙 글 |
+| 노면 하중 | `B22` | 수(kPa · 서식 16) |
+| 지반 물성치 | **26 행부터 한 줄씩** · 머리 25 행(머리 줄 반복) | A 지층 · B 적용 구간(전체 또는 구간 글) · C γt · D γsat · E c · F φ(서식 16) · G 출처(서식 5) · A·B = 서식 7 |
+
+- 지반 물성치가 표의 맨 끝이라 줄 수가 늘어도 다른 칸이 안 밀림. 구간별 덮어쓰기 줄 = 같은 표에 줄 추가(B 열에 구간).
+
+## s4 측점표 (프로그램 몫 · 머리 줄 4~5 행 쪽마다 반복 · 가로 · 폭 1쪽)
+
+- 자료는 **6 행부터 측점 하나 = 한 줄** · 서식은 6 행 서식을 줄마다 그대로.
+
+| 열 | 내용 | 서식 |
+| --- | --- | --- |
+| A | 측점 이름 | 7 |
+| B | 누가거리(m · 수) | 17 |
+| C | 구분(`깎기` · `쌓기`) | 7 |
+| D · E · F | 건기 · 우기 · 지진 안전율 Fs(지진 끔 = 칸 없음) | 8 |
+| G · H · I | 건기 · 우기 · 지진 기준 안전율 | 8 |
+| J | 판정 `O.K` · `N.G`(엔진 판정 값) | N.G = 10 · O.K = 11 |
+
+- 개요 · 3D 수식이 `A6:A20000` · `B` · `D` · `E` · `F` · `J` 열을 읽음 → 이 열 배치 바꾸지 않음.
+- 줄 수 = 측점 수(시험: 측점 수 = 줄 수). 쪽 나눔은 자동(`인쇄.쪽나눔` 안 씀).
+
+## s5 대표단면 (프로그램 몫 · 단면 하나 = 28 행 덩이 = 한 쪽)
+
+덩이 `k`(0 부터)의 시작 오프셋 `dr = 28 × k` — 양식의 1~28 행 덩이를 `dr` 만큼 아래로 복사해 단면 수만큼 만듦(칸 · 병합 · 행 높이 · `조건부서식` 범위 포함).
+
+| 덩이 안 행 | 칸 | 내용 |
+| --- | --- | --- |
+| 1 | `A1:H1` 병합 | 단면 제목(`대표 단면 ① — 측점 · 구분 · 높이`) |
+| 2 | `B2:H2` 병합 | 선정 사유 글 |
+| 3~22 | 그림 `A3:H22` | 그림 한 장 — 그림 칸 크기 약 712 × 533 px(가로 : 세로 = 4 : 3) · 열쇠 `fig/<측점 키>.png` · 닻은 `dr` 만큼 내림 |
+| 23 | 머리(고정 글) | 조건 · 해석 방법 · 안전율 · 기준 · 판정 · 활동면 반경 R · 중심 x · 중심 y |
+| 24 · 25 · 26 | A = 건기 · 우기 · 지진(고정 글) | B 해석 방법 글(서식 7) · C Fs(8) · D 기준(8) · E 판정(7→ N.G 10 · O.K 11) · F 반경 · G 중심 x · H 중심 y(13) |
+| 27 | `B27:H27` 병합 | 비고 글 |
+| 28 | 빈 줄 | 쪽 끝 |
+
+- `인쇄.쪽나눔` = `[28, 56, …, 28 × 단면 수]`(마지막 덩이 끝 쪽 나눔은 있어도 빈 쪽이 안 생김).
+- 지진을 안 쓰면 26 행(덩이 안) 칸을 비움.
+
+## s6 3D요약 (프로그램 몫 · 머리 줄 19 행 반복)
+
+| 칸 | 내용 |
+| --- | --- |
+| 그림 `A5:H16` | 평면 위험 띠 — 그림 칸 약 684 × 320 px(2.1 : 1) · 열쇠 `fig/3d_band.png` |
+| 20 행부터 구간 하나 = 한 줄 | A 구간 · B 시작 측점 · C 끝 측점(서식 7) · D 3D 최소 Fs · E 2D 최소 Fs(서식 8) · **F 3D/2D 비 = 수식 `IF(E{r}>0,D{r}/E{r},"")`**(서식 8) · G 임계 체적 m³(서식 14) · H 판정(서식 7 → N.G 10 · O.K 11) |
+
+- 20 행 서식을 줄마다 복사.
diff --git a/resources/tester/test_534_3_stab_report_form.py b/resources/tester/test_534_3_stab_report_form.py
new file mode 100644
index 000000000..89ac63e32
--- /dev/null
+++ b/resources/tester/test_534_3_stab_report_form.py
@@ -0,0 +1,252 @@
+"""534-3(양식) — E05 안정성평가 보고서 양식 `resources/master_template/report/stab_report.json`.
+
+계약 검사(키 · 서식 번호 · A1 · 병합 · 쪽 나눔) + 가짜 값 사본으로 수식 · 인쇄 HTML · PDF 쪽 수 · xlsx 되읽기.
+"""
+
+import copy
+import io
+import json
+import re
+import shutil
+from pathlib import Path
+
+import pytest
+from openpyxl import load_workbook
+
+from common_util import common_util_spreadsheet as spreadsheet
+from common_util.common_util_print_pdf import PrintError
+from common_util.common_util_svg_png import svgs_to_pngs
+from common_util.common_util_workbook_xlsx import workbook_to_xlsx
+
+ROOT = Path(__file__).resolve().parents[2]
+FORM = ROOT / "resources/master_template/report/stab_report.json"
+BLOCK = 28 # 대표단면 한 쪽 = 28 행
+SVG = (
+ ''
+)
+BOOK_KEYS = {"종류", "판", "열", "기본", "서식", "시트", "활성", "계산값"}
+SHEET_KEYS = {
+ "id", "이름", "칸", "병합", "열", "행", "틀고정", "보기", "기본", "comments", "필터",
+ "조건부서식", "유효성", "이름정의", "숨김", "탭색", "연결", "인쇄", "그림",
+} # fmt: skip
+STYLE_KEYS = {
+ "글꼴", "크기", "굵게", "기울임", "밑줄", "취소선", "글자색", "채움", "가로", "세로", "줄바꿈",
+ "테두리", "형식",
+} # fmt: skip
+PRINT_KEYS = {"용지", "방향", "배율", "맞춤", "여백", "머리행", "쪽나눔"}
+A1 = re.compile(r"^([A-Z]{1,3})(\d+)$")
+NAMES = ["표지", "개요", "입력조건", "측점표", "대표단면", "3D요약"]
+
+needs_node = pytest.mark.skipif(shutil.which("node") is None, reason="node 가 없음")
+
+
+def _form() -> dict:
+ return json.loads(FORM.read_text(encoding="utf-8"))
+
+
+def _rc(a1: str) -> tuple[int, int]:
+ m = A1.match(a1)
+ assert m, a1
+ col = 0
+ for ch in m.group(1):
+ col = col * 26 + ord(ch) - 64
+ return int(m.group(2)), col
+
+
+def _span(text: str) -> tuple[int, int, int, int]:
+ a, _, b = text.partition(":")
+ (r0, c0), (r1, c1) = _rc(a), _rc(b or a)
+ assert r0 <= r1 and c0 <= c1, text
+ return r0, c0, r1, c1
+
+
+def _contract(book: dict) -> None:
+ assert set(book) <= BOOK_KEYS and book["종류"] == "통합문서" and book["판"] == 1
+ assert book["서식"][0] == {}
+ for st in book["서식"]:
+ assert set(st) <= STYLE_KEYS
+ ids = [s["id"] for s in book["시트"]]
+ assert len(set(ids)) == len(ids) and book["활성"] in ids
+ for sheet in book["시트"]:
+ assert set(sheet) <= SHEET_KEYS
+ for a1, cell in sheet["칸"].items():
+ _rc(a1)
+ assert set(cell) <= {"값", "식", "서식"} and not ({"값", "식"} <= set(cell))
+ assert 0 <= cell.get("서식", 0) < len(book["서식"])
+ used: set[tuple[int, int]] = set()
+ for m in sheet.get("병합", []):
+ r0, c0, r1, c1 = _span(m)
+ box = {(r, c) for r in range(r0, r1 + 1) for c in range(c0, c1 + 1)}
+ assert len(box) > 1 and not (used & box), m
+ used |= box
+ pr = sheet.get("인쇄", {})
+ assert set(pr) <= PRINT_KEYS
+ assert pr.get("쪽나눔", []) == sorted(set(pr.get("쪽나눔", [])))
+ if "머리행" in pr:
+ assert 1 <= pr["머리행"][0] <= pr["머리행"][1]
+ for fig in sheet.get("그림", []):
+ _span(fig["닻"])
+ assert fig["열쇠"]
+ for rule in sheet.get("조건부서식", []):
+ assert rule["조건"]["종류"] == "글포함" and rule["범위"]
+
+
+# ── 가짜 값 사본 ─────────────────────────────────────────────────────────────
+def _shift(a1: str, dr: int) -> str:
+ m = A1.match(a1)
+ return f"{m.group(1)}{int(m.group(2)) + dr}"
+
+
+def _fill(book: dict, stations: int, sections: int) -> tuple[dict, int]:
+ """측점표 `stations` 줄 · 대표단면 `sections` 쪽 · 3D 두 줄을 채운 사본 · N.G 수."""
+ book = copy.deepcopy(book)
+ by = {s["이름"]: s for s in book["시트"]}
+ s3, s4, s5, s6 = by["입력조건"], by["측점표"], by["대표단면"], by["3D요약"]
+ for r, v in ((12, 1.5), (13, 1.2), (14, 1.1)):
+ s3["칸"][f"B{r}"]["값"] = v
+ ng = 0
+ for i in range(stations):
+ r = 6 + i
+ bad = i % 7 == 3
+ ng += bad
+ fs = [1.32 if bad else 1.8 + i / 100, 1.5 + i / 100, 1.3 + i / 100]
+ for letter, v, st in (
+ ("A", f"No.{i}", 7), ("B", 20.0 * i, 17), ("C", "깎기" if i % 2 else "쌓기", 7),
+ ("D", fs[0], 8), ("E", fs[1], 8), ("F", fs[2], 8),
+ ("G", 1.5, 8), ("H", 1.2, 8), ("I", 1.1, 8), ("J", "N.G" if bad else "O.K", 7),
+ ): # fmt: skip
+ s4["칸"][f"{letter}{r}"] = {"값": v, "서식": st}
+ for k in range(1, sections): # 첫 덩이를 아래로 복사
+ dr = BLOCK * k
+ for a1, cell in list(s5["칸"].items()):
+ if int(A1.match(a1).group(2)) <= BLOCK:
+ s5["칸"][_shift(a1, dr)] = copy.deepcopy(cell)
+ for m in list(s5["병합"]):
+ if int(re.search(r"\d+", m).group()) <= BLOCK:
+ s5["병합"].append(":".join(_shift(p, dr) for p in m.split(":")))
+ for r, info in list(s5["행"].items()):
+ if int(r) <= BLOCK:
+ s5["행"][str(int(r) + dr)] = dict(info)
+ a, _, b = s5["그림"][0]["닻"].partition(":")
+ s5["그림"].append({"열쇠": f"fig/s{k}.png", "닻": f"{_shift(a, dr)}:{_shift(b, dr)}"})
+ s5["인쇄"]["쪽나눔"].append(BLOCK * (k + 1))
+ s5["그림"][0]["열쇠"] = "fig/s0.png"
+ for r, row in (
+ (20, ("1구간", "No.0", "No.9", 1.9, 1.7, 1200)),
+ (21, ("2구간", "No.9", "No.19", 1.4, 1.5, 800)),
+ ):
+ for letter, v in zip("ABCDEG", row):
+ s6["칸"][f"{letter}{r}"] = {"값": v, "서식": s6["칸"][f"{letter}20"]["서식"]}
+ s6["칸"][f"F{r}"] = {"식": f'IF(E{r}>0,D{r}/E{r},"")', "서식": s6["칸"]["F20"]["서식"]}
+ s6["칸"][f"H{r}"] = {"값": "O.K" if r == 20 else "N.G", "서식": s6["칸"]["H20"]["서식"]}
+ return book, ng
+
+
+# ── 시험 ─────────────────────────────────────────────────────────────────────
+def test_양식_계약():
+ book = _form()
+ _contract(book)
+ assert [s["이름"] for s in book["시트"]] == NAMES
+ assert [s["id"] for s in book["시트"]] == ["s1", "s2", "s3", "s4", "s5", "s6"]
+
+
+def test_양식_인쇄_쪽나눔_머리행_그림():
+ by = {s["이름"]: s for s in _form()["시트"]}
+ for sheet in by.values():
+ pr = sheet["인쇄"]
+ assert pr["용지"] == "A4" and pr["맞춤"]["폭"] == 1 and pr["여백"]
+ assert by["측점표"]["인쇄"]["방향"] == "landscape" and by["측점표"]["인쇄"]["머리행"] == [4, 5]
+ assert by["대표단면"]["인쇄"]["쪽나눔"] == [BLOCK]
+ figs = {n: len(s.get("그림", [])) for n, s in by.items()}
+ assert figs == {"표지": 0, "개요": 0, "입력조건": 0, "측점표": 0, "대표단면": 1, "3D요약": 1}
+ # N.G 빨강 조건부 서식 — 측점표 판정 열 · 서식 표에 N.G 칸 서식(빌더가 인쇄 · xlsx 용으로 직접 씀)
+ assert by["측점표"]["조건부서식"][0]["범위"][0]["c0"] == 9
+ assert any(st.get("글자색") == "#C00000" and st.get("채움") for st in _form()["서식"])
+ # 안전율 칸 숫자 형식 0.00
+ assert _form()["서식"][by["측점표"]["칸"]["D6"]["서식"]]["형식"] == "0.00"
+
+
+@needs_node
+def test_빈_양식도_풀림():
+ book = _form()
+ vals = spreadsheet.recalc_workbooks([book])[0]
+ assert vals["s2"]["B5"] == {"수": "0"}
+ assert vals["s2"]["B10"] == {"글": "-"}
+ assert vals["s2"]["B16"] == {"글": "-"}
+ assert vals["s2"]["B17"] == {"글": "해석 결과 없음"}
+ assert not [v for s in vals.values() for v in s.values() if "오류" in v]
+
+
+@needs_node
+def test_채운_양식_수식():
+ book, ng = _fill(_form(), 40, 2)
+ s2 = spreadsheet.recalc_workbooks([book])[0]["s2"]
+ assert s2["B5"] == {"수": "40"} and s2["B6"] == {"수": "780"}
+ assert s2["B15"] == {"수": str(ng)} and ng == 6
+ assert (
+ s2["B10"] == {"수": "1.32"} and s2["D10"] == {"글": "N.G"} and s2["E10"] == {"글": "No.3"}
+ )
+ assert s2["D11"] == {"글": "O.K"} and s2["B11"] == {"수": "1.5"}
+ assert s2["B16"] == {"글": "N.G"}
+ assert s2["B17"]["글"].startswith("대상 측점 40개소 중 6개소가")
+ s6 = spreadsheet.recalc_workbooks([book])[0]["s6"]
+ assert s6["F21"] == {"수": "0.933333333333333"} or s6["F21"]["수"].startswith("0.93333")
+
+
+@pytest.fixture(scope="module")
+def png() -> bytes:
+ try:
+ return svgs_to_pngs([SVG])[0]
+ except PrintError as error:
+ pytest.skip(f"브라우저 없음 — {error}")
+
+
+@needs_node
+def test_견본_pdf_쪽수_머리행(png):
+ book, _ = _fill(_form(), 40, 2)
+ figs = {"fig/s0.png": png, "fig/s1.png": png, "fig/3d_band.png": png}
+ try:
+ pdf = spreadsheet.workbook_pdf(book, figs)
+ except PrintError as error:
+ pytest.skip(f"브라우저 없음 — {error}")
+ from pypdf import PdfReader
+
+ pages = PdfReader(io.BytesIO(pdf)).pages
+ texts = [p.extract_text() for p in pages]
+ stab = [i for i, t in enumerate(texts) if "측점별 안전율" in t or "산정 안전율" in t]
+ assert len(stab) >= 2 and all("산정 안전율" in texts[i] for i in stab) # 머리 줄 쪽마다
+ assert (
+ texts[0].count("비탈면 안정성 검토 보고서") == 1
+ and "문헌 추정치 · 현장 시험 없음" in texts[0]
+ )
+ sections = [i for i, t in enumerate(texts) if "대표 단면" in t and "안전율 Fs" in t]
+ assert len(sections) == 2 # 단면마다 한 쪽
+ # 표지 1 + 개요 1 + 입력조건 1 + 측점표 n + 대표단면 2 + 3D 1
+ assert len(pages) == 1 + 1 + 1 + len(stab) + 2 + 1
+ assert all(float(pages[i].mediabox.width) > float(pages[i].mediabox.height) for i in stab)
+ html = spreadsheet.print_htmls([{"book": book, "figures": {k: png for k in figs}}])[0]
+ assert html.count("![]()