Files
dwg-dxf-viewer-sample/docs/korean-dwg-text-encoding.md

297 lines
12 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 한글 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+AC00U+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)