# 한글 DWG 텍스트 깨짐 — 원인 분석 및 원본 반영 가이드

| 항목 | 내용 |
|------|------|
| 작성 목적 | 샘플에서 재현·수정한 이슈를 **원본(`hmwebviewer`)** 에 반영하기 위한 기술 메모 |
| 샘플 저장소 | `dwg-dxf-viewer-sample` (본 문서 위치) |
| 원본 저장소 | `hmwebviewer` (`src/viewer2d/` 동기화 대상) |
| 재현 도면 | `C0060203-002-굴착및시공순서도(2차로,자연환기300m이상).dwg` 등 구형 한글 CAD 도면 |
| 대조 도면 | `BasicSample.dwg` (이미 정상 유니코드 한글) |
| 샘플 수정일 | 2026-07-29 |
| 상태 | 샘플 패치 완료 / 원본 미반영 |

---

## 1. 현상

웹 2D 뷰어에서 일부 DWG를 열면 **도형·치수 기하, 영문 TEXT는 정상**인데 **한글 TEXT/MTEXT·타이틀블록 문구·NOTE** 등이 다음과 같이 깨져 보인다.

| 화면/파서 출력 (깨짐) | 정상 의미 |
|----------------------|-----------|
| `½Ã°ø½Ã` | 시공시 |
| `·Ïº¼Æ®` | 록볼트 |
| `¼ôÅ©¸®Æ®` | 숏크리트 |
| `±¼Âø ¹× ½Ã°ø¼ø¼­µµ` | 굴착 및 시공순서도 |
| `Áß½É¼±(ÀÏ¹Ý)` (라인타입 설명) | 중심선(일반) |

외형상 “폰트가 없다 / 글리프가 없다”와 혼동하기 쉽지만, **글리프 부재(tofu □)** 가 아니라 **잘못된 유니코드 코드포인트가 그대로 그려진 것(mojibake)** 이다.

### 자주 하는 오진

| 가설 | 실제 |
|------|------|
| `dist/fonts` / `public/fonts` 에 돋움이 없어서 | 폰트 폴더에는 `NanumGothic-Regular.ttf`(한글 포함)가 있음 |
| Style `Standard` + 폰트 `돋움` 미매핑 | 뷰어는 스타일→폰트 매핑 없이 **단일 TTF** 로 그림. 돋움 없어도 코드포인트만 맞으면 한글 표시 가능 |
| slugText / opentype 버그 | 동일 파이프라인에서 `BasicSample.dwg` 한글은 정상 |

---

## 2. 파이프라인 위치

```
DWG bytes
  → acadrust WASM (parse_dwg)     ← 여기서 문자열이 이미 깨지거나 정상으로 나옴
  → CadParseResult (entities/tables/…)
  → [샘플 패치] fixDwgKoreanText   ← 복원 계층 (원본에는 아직 없음)
  → Viewer2D.load()
  → slugText (NanumGothic TTF)
  → WebGL
```

- **DXF 경로**는 텍스트 파일 디코딩 단계에서 코드페이지를 고려한다.  
  - 샘플: `src/viewer2d/decodeDxf.ts` (`$DWGCODEPAGE` / 949 → `euc-kr` 등)  
  - 원본: `hmwebviewer/src/viewerController.ts` 내 유사 `decodeDxf`
- **DWG 경로**는 바이너리 → WASM → JSON 문자열이며, 파싱 직후 한글 코드페이지 보정 계층이 없었다.

관련 모듈:

| 역할 | 샘플 경로 | 원본 경로 |
|------|-----------|-----------|
| DWG 파서 진입점 | `src/viewer2d/dwgParser.ts` | `hmwebviewer/src/viewer2d/dwgParser.ts` |
| WASM 래퍼 | `src/viewer2d/acadrustParser.ts` | 동일 |
| 텍스트 렌더 | `src/viewer2d/slugText.ts` | 동일 |
| 한글 복원 (샘플만) | `src/viewer2d/fixDwgKoreanText.ts` | **없음 → 이식 대상** |

---

## 3. 원인 분석

### 3.1 근본 원인 (Root cause)

**구형/한글 CAD DWG에 저장된 CP949(Windows-949) 계열 바이트 시퀀스를, acadrust 파서(또는 그 문자열 경계)가 Latin-1/바이트-단위 유니코드로 승격해 버린 것.**

즉:

```
원본 바이트 (CP949)  --잘못된 해석-->  U+00xx Latin-1 문자들  --렌더-->  깨진 화면
예: EC 8B 9C ... (시) 가  ½ Ã ° ø … 같은 문자로 보임
```

복원 공식 (검증됨):

```text
broken.encode('latin1')  →  bytes
bytes.decode('cp949' | 'euc-kr' | 'windows-949')  →  정상 한글
```

브라우저/Node `TextDecoder` 라벨 우선순위 (샘플 구현):

1. `cp949`
2. `windows-949`
3. `euc-kr`

### 3.2 왜 도면마다 다른가

| 도면 | 파서 직후 TEXT | 원인 해석 |
|------|----------------|-----------|
| `BasicSample.dwg` | `설계사`, `시공사` 등 **정상 한글 음절** | 이미 유니코드/UTF 계열로 저장·추출됨 |
| `C0060203-002-….dwg` | 한글 음절 **0건**, Latin-1 고위 문자 다수 | CP949 레거시 저장 + 오해독 |

동일 뷰어·동일 폰트에서 결과가 갈리므로 **렌더러/폰트 문제가 아님**이 확정된다.

### 3.3 영향 범위 (문자열 필드 전반)

TEXT/MTEXT 본문뿐 아니라 같은 인코딩으로 들어간 메타 문자열도 깨진다.

- `entities[].text` (TEXT / MTEXT / ATTRIB 등)
- `tables.lineTypes[].description` (예: 중심선, 파선 한글 설명)
- `tables.blocks[].name` (예: `CXGLOGO-EX흑백` → `CXGLOGO-EXÈæ¹é`)
- 기타 파서가 문자열로 넘기는 테이블/속성 필드

→ **필드 단위 패치보다 parseResult 트리 전체 문자열 walk** 가 안전하다.

### 3.4 부수 관찰 (이번 깨짐의 주원인은 아님)

| 관찰 | 의미 |
|------|------|
| `tables.styles` 가 빈 배열 | 스타일/폰트명(돋움) 정보를 뷰어가 쓰지 못함. 장기 개선 항목 |
| 뷰어 고정 폰트 `NanumGothic-Regular.ttf` | 한글 글리프 보유. 코드포인트만 맞으면 표시됨 |
| UI 파일명·OS 경로 한글 | OS 유니코드 경로라 정상일 수 있음. DWG 내부 텍스트와 무관 |

---

## 4. 증거 (재현 절차)

### 4.1 파서만 돌려 TEXT 통계

WASM 초기화 후 `parse_dwg` 결과에서:

```js
// TEXT/MTEXT 중 한글 음절 포함 여부
/[\uAC00-\uD7A3]/.test(entity.text)
```

문제 도면 예 (패치 전):

- hangul: **0**
- 깨진 Latin: 다수 (`½Ã°ø…` 패턴)

패치 후:

- hangul: 다수 (예: 130)
- NOTE 샘플: `2. 시공시 막장관찰, 계측분석결과, …`
- 라인타입: `중심선(일반) Center …`
- 블록: `CXGLOGO-EX흑백`

### 4.2 단위 변환 검증

| 입력 | `latin1` 바이트 재해석 → CP949 |
|------|-------------------------------|
| `½Ã°ø½Ã` | `시공시` |
| `·Ïº¼Æ®` | `록볼트` |
| `NOTE` | `NOTE` (변경 없음) |
| `설계사` | 휴리스틱상 **스킵** (이미 한글) |

### 4.3 음성 대조

`BasicSample.dwg` 에 동일 복원기를 적용해도 `설계사` / `시 공 사` 등이 **변하지 않아야** 한다.

---

## 5. 샘플에 적용한 처리 방법

### 5.1 신규 모듈

`src/viewer2d/fixDwgKoreanText.ts`

- `fixCp949Mojibake(s: string): string`  
  단일 문자열 복원
- `fixDwgKoreanText<T>(root: T): T`  
  parseResult 딥 워크, in-place 수정 후 동일 참조 반환

### 5.2 휴리스틱 (중요 — 정상 도면 보호)

다음이면 **원문 유지**:

1. 빈 문자열
2. 이미 한글 음절(`U+AC00–U+D7A3`) 또는 자모 포함
3. 순수 ASCII (모든 코드포인트 ≤ 127)
4. Latin-1 범위를 넘는 코드포인트 포함 (진짜 유니코드)
5. 한국 코드페이지 디코더 없음 / 디코드 실패
6. 디코드 결과에 한글이 **없거나** `U+FFFD` 포함

다음일 때만 **교체**:

- 고위 Latin-1(>127) 포함 **그리고**
- CP949/EUC-KR 디코드 성공 **그리고**
- 결과에 한글 음절/자모 포함 **그리고**
- 치환 문자 없음

### 5.3 삽입 지점

`src/viewer2d/dwgParser.ts`:

```ts
import { fixDwgKoreanText } from './fixDwgKoreanText';

export async function parseDwgBuffer(bytes: Uint8Array): Promise<CadParseResult> {
  const { initAcadrustParser, parseDwgAcadrust } = await import('./acadrustParser');
  await initAcadrustParser();
  return fixDwgKoreanText(parseDwgAcadrust(bytes));
}
```

- Viewer2D / slugText / 폰트 설정 변경 **불필요**
- DXF 경로 변경 **불필요** (이미 별도 디코드)

### 5.4 왜 WASM 내부 수정이 아닌가 (1차 선택 이유)

| 접근 | 장점 | 단점 |
|------|------|------|
| **JS 후처리 (채택)** | 배포 단순, acadrust 크레이트 재빌드 없음, 샘플↔원본 이식 쉬움 | 근본 수정은 아님. 휴리스틱 오탐 가능성(낮음) |
| acadrust/코드페이지 수정 | 파서 정합 | MPL·Rust/WASM 빌드 체인, 검증 범위 큼 |
| 스타일→돋움 TTF 매핑 | WYSIWYG 개선 | **이번 mojibake를 해결하지 못함** |

중장기적으로는 WASM/파서가 DWG 코드페이지(또는 문자열 유니코드 플래그)를 올바르게 반영하는 것이 이상적이다.  
그 전 단계의 **제품 적용 가능한 완화책**이 JS 후처리다.

---

## 6. 원본(`hmwebviewer`) 반영 절차

원본 `src/viewer2d/dwgParser.ts` 는 샘플 패치 전과 동일하게 `parseDwgAcadrust` 결과를 그대로 반환한다.

### 6.1 최소 이식 (권장 1차)

1. 샘플에서 복사  
   - `dwg-dxf-viewer-sample/src/viewer2d/fixDwgKoreanText.ts`  
   - → `hmwebviewer/src/viewer2d/fixDwgKoreanText.ts`
2. `hmwebviewer/src/viewer2d/dwgParser.ts` 에 샘플과 동일하게  
   `fixDwgKoreanText(parseDwgAcadrust(bytes))` 연결
3. `parseDwgBuffer` 를 거치지 않는 다른 진입점이 있으면 **동일 후처리** 적용  
   - 예: 직접 `parseDwgAcadrust` / `parse_dwg` 호출부 검색
4. 검증  
   - 문제 DWG: NOTE·공정 문구 한글  
   - `BasicSample` 또는 기존 유니코드 한글 DWG: 회귀 없음  
   - 영문·숫자·`Sealing` 등 ASCII 유지
5. (선택) 단위 테스트  
   - `fixCp949Mojibake('½Ã°ø½Ã') === '시공시'`  
   - `fixCp949Mojibake('설계사') === '설계사'`  
   - `fixCp949Mojibake('NOTE') === 'NOTE'`

### 6.2 동기화 정책

README 기준: `hmwebviewer/src/viewer2d` 가 본가이고 샘플은 형제 복사본이다.  
이번 이슈는 **샘플에서 먼저 수정**했으므로, 원본 반영 후 필요 시 샘플과 다시 맞춘다.

### 6.3 반영 체크리스트

- [ ] `fixDwgKoreanText.ts` 원본 트리에 추가
- [ ] `dwgParser.ts` 후처리 연결
- [ ] 직접 WASM 호출 경로 없음 확인
- [ ] 문제 한글 DWG 수동 확인
- [ ] 정상 유니코드 한글 DWG 회귀 확인
- [ ] (선택) DXF ANSI_949 도면도 기존 `decodeDxf` 동작 유지 확인
- [ ] (선택) `tables.styles` / 폰트 매핑은 **별 이슈**로 분리

---

## 7. 하지 말아야 할 것

| 비권장 | 이유 |
|--------|------|
| 돋움 TTF만 추가하고 끝 | 문자열이 깨진 상태면 돋움으로도 동일 mojibake |
| 모든 문자열을 무조건 CP949 디코드 | 정상 유니코드·라틴 확장 도면 손상 |
| 렌더러에서 글리프 폴백만 강화 | 코드포인트 자체가 틀림 |
| `dist` 만 수동 패치 | 소스는 `src/`; 빌드 산출물에만 고치면 재빌드 시 소실 |

---

## 8. 잔여 이슈 / 후속 과제

1. **파서 본가 수정**  
   acadrust(또는 문자열 추출 경로)에서 DWG 코드페이지·유니코드 플래그 반영 → JS 휴리스틱 제거 가능 여부 검토
2. **STYLE 테이블**  
   현재 `tables.styles` 가 비어 스타일/폰트명 미노출. 돋움 등 CAD 지정 폰트 매핑은 별 과제
3. **다른 코드페이지**  
   일본(932)/중국(936)/대만(950) 도면에 동일 패턴이 있으면 라벨 확장 + 문자 체계 휴리스틱 필요 (현 구현은 **한글 음절 출현** 조건으로 한국 도면에 한정)
4. **ATTRIB / 치수 상주 텍스트 등**  
   딥 워크로 대부분 커버되나, 바이너리/비문자열 필드는 대상 외

---

## 9. 한 줄 요약

> **폰트 폴더 문제가 아니라, 구형 한글 DWG의 CP949 바이트가 Latin-1 문자열로 올라온 인코딩 오해독이다.  
> `parse_dwg` 직후 문자열 트리를 CP949로 재해석(이미 한글인 값은 스킵)하면 해결되며,  
> 원본 `hmwebviewer` 에는 샘플의 `fixDwgKoreanText.ts` + `dwgParser.ts` 연결을 그대로 이식하면 된다.**

---

## 10. 참고 파일 (샘플)

- 복원 구현: [`../src/viewer2d/fixDwgKoreanText.ts`](../src/viewer2d/fixDwgKoreanText.ts)
- 연결부: [`../src/viewer2d/dwgParser.ts`](../src/viewer2d/dwgParser.ts)
- DXF 쪽 코드페이지 처리(대조): [`../src/viewer2d/decodeDxf.ts`](../src/viewer2d/decodeDxf.ts)
- 모듈 지도: [`modules-dwg-dxf.html`](./modules-dwg-dxf.html)
