diff --git a/docs/images/sample-test-2026-10-19/01-korean-2after.jpg b/docs/images/sample-test-2026-10-19/01-korean-2after.jpg new file mode 100644 index 0000000..5021a50 Binary files /dev/null and b/docs/images/sample-test-2026-10-19/01-korean-2after.jpg differ diff --git a/docs/images/sample-test-2026-10-19/01-korean-3current.jpg b/docs/images/sample-test-2026-10-19/01-korean-3current.jpg new file mode 100644 index 0000000..0f6d96d Binary files /dev/null and b/docs/images/sample-test-2026-10-19/01-korean-3current.jpg differ diff --git a/docs/images/sample-test-2026-10-19/02-model-paper-1before.jpg b/docs/images/sample-test-2026-10-19/02-model-paper-1before.jpg new file mode 100644 index 0000000..95021b6 Binary files /dev/null and b/docs/images/sample-test-2026-10-19/02-model-paper-1before.jpg differ diff --git a/docs/images/sample-test-2026-10-19/02-model-paper-2after.jpg b/docs/images/sample-test-2026-10-19/02-model-paper-2after.jpg new file mode 100644 index 0000000..6d239d2 Binary files /dev/null and b/docs/images/sample-test-2026-10-19/02-model-paper-2after.jpg differ diff --git a/docs/images/sample-test-2026-10-19/02-model-paper-3current.jpg b/docs/images/sample-test-2026-10-19/02-model-paper-3current.jpg new file mode 100644 index 0000000..9368da8 Binary files /dev/null and b/docs/images/sample-test-2026-10-19/02-model-paper-3current.jpg differ diff --git a/docs/images/sample-test-2026-10-19/03-large-hatch-1before.jpg b/docs/images/sample-test-2026-10-19/03-large-hatch-1before.jpg new file mode 100644 index 0000000..8c81e38 Binary files /dev/null and b/docs/images/sample-test-2026-10-19/03-large-hatch-1before.jpg differ diff --git a/docs/images/sample-test-2026-10-19/03-large-hatch-2after.jpg b/docs/images/sample-test-2026-10-19/03-large-hatch-2after.jpg new file mode 100644 index 0000000..605f139 Binary files /dev/null and b/docs/images/sample-test-2026-10-19/03-large-hatch-2after.jpg differ diff --git a/docs/images/sample-test-2026-10-19/03-large-hatch-3current.jpg b/docs/images/sample-test-2026-10-19/03-large-hatch-3current.jpg new file mode 100644 index 0000000..f06cf4c Binary files /dev/null and b/docs/images/sample-test-2026-10-19/03-large-hatch-3current.jpg differ diff --git a/docs/images/sample-test-2026-10-19/04-slug-text-1before.jpg b/docs/images/sample-test-2026-10-19/04-slug-text-1before.jpg new file mode 100644 index 0000000..670f5ca Binary files /dev/null and b/docs/images/sample-test-2026-10-19/04-slug-text-1before.jpg differ diff --git a/docs/images/sample-test-2026-10-19/04-slug-text-2after.jpg b/docs/images/sample-test-2026-10-19/04-slug-text-2after.jpg new file mode 100644 index 0000000..52fe39d Binary files /dev/null and b/docs/images/sample-test-2026-10-19/04-slug-text-2after.jpg differ diff --git a/docs/images/sample-test-2026-10-19/04-slug-text-3current.jpg b/docs/images/sample-test-2026-10-19/04-slug-text-3current.jpg new file mode 100644 index 0000000..5bb29c3 Binary files /dev/null and b/docs/images/sample-test-2026-10-19/04-slug-text-3current.jpg differ diff --git a/docs/images/sample-test-2026-10-19/05-mesh-1before.jpg b/docs/images/sample-test-2026-10-19/05-mesh-1before.jpg new file mode 100644 index 0000000..397ebaf Binary files /dev/null and b/docs/images/sample-test-2026-10-19/05-mesh-1before.jpg differ diff --git a/docs/images/sample-test-2026-10-19/05-mesh-2after.jpg b/docs/images/sample-test-2026-10-19/05-mesh-2after.jpg new file mode 100644 index 0000000..9fd90bd Binary files /dev/null and b/docs/images/sample-test-2026-10-19/05-mesh-2after.jpg differ diff --git a/docs/images/sample-test-2026-10-19/05-mesh-3current.jpg b/docs/images/sample-test-2026-10-19/05-mesh-3current.jpg new file mode 100644 index 0000000..98c7822 Binary files /dev/null and b/docs/images/sample-test-2026-10-19/05-mesh-3current.jpg differ diff --git a/docs/images/sample-test-2026-10-19/06-lineweight-1before.jpg b/docs/images/sample-test-2026-10-19/06-lineweight-1before.jpg new file mode 100644 index 0000000..8753f62 Binary files /dev/null and b/docs/images/sample-test-2026-10-19/06-lineweight-1before.jpg differ diff --git a/docs/images/sample-test-2026-10-19/06-lineweight-2after.jpg b/docs/images/sample-test-2026-10-19/06-lineweight-2after.jpg new file mode 100644 index 0000000..8336584 Binary files /dev/null and b/docs/images/sample-test-2026-10-19/06-lineweight-2after.jpg differ diff --git a/docs/images/sample-test-2026-10-19/06-lineweight-3current.jpg b/docs/images/sample-test-2026-10-19/06-lineweight-3current.jpg new file mode 100644 index 0000000..f136c3a Binary files /dev/null and b/docs/images/sample-test-2026-10-19/06-lineweight-3current.jpg differ diff --git a/docs/images/sample-test-2026-10-19/n1-percent-3current.jpg b/docs/images/sample-test-2026-10-19/n1-percent-3current.jpg new file mode 100644 index 0000000..3df0765 Binary files /dev/null and b/docs/images/sample-test-2026-10-19/n1-percent-3current.jpg differ diff --git a/docs/images/sample-test-2026-10-19/n2-cp949-symbol-3current.jpg b/docs/images/sample-test-2026-10-19/n2-cp949-symbol-3current.jpg new file mode 100644 index 0000000..b86dbb1 Binary files /dev/null and b/docs/images/sample-test-2026-10-19/n2-cp949-symbol-3current.jpg differ diff --git a/docs/images/sample-test-2026-10-19/n3-fit-crop-3current.jpg b/docs/images/sample-test-2026-10-19/n3-fit-crop-3current.jpg new file mode 100644 index 0000000..94494c9 Binary files /dev/null and b/docs/images/sample-test-2026-10-19/n3-fit-crop-3current.jpg differ diff --git a/docs/images/sample-test-2026-10-19/results.json b/docs/images/sample-test-2026-10-19/results.json new file mode 100644 index 0000000..7bbabc4 --- /dev/null +++ b/docs/images/sample-test-2026-10-19/results.json @@ -0,0 +1,414 @@ +{ + "01-korean": { + "1before": { + "snap": "95fa6a4", + "file": "civil.dwg", + "loadMs": 5230, + "status": "✓ civil.dwg · 17807 entities", + "ok": true, + "space": "n/a (no spaces API)", + "zoom": { + "camX": 600800, + "camY": 478560, + "width": 7000, + "up": [ + 0, + 1 + ] + }, + "image": "01-korean-1before.jpg", + "errors": [] + }, + "2after": { + "snap": "be8d52f", + "file": "civil.dwg", + "loadMs": 5650, + "status": "✓ civil.dwg · 17810 entities · Layout", + "ok": true, + "space": "model 1f", + "zoom": { + "camX": 598950, + "camY": 478462, + "width": 90, + "up": [ + 0, + 1 + ] + }, + "image": "01-korean-2after.jpg", + "errors": [] + }, + "3current": { + "snap": "current", + "file": "civil.dwg", + "loadMs": 775, + "status": "✓ civil.dwg · 17810 entities · Layout", + "ok": true, + "space": "model 1f", + "zoom": { + "camX": 598950, + "camY": 478462, + "width": 90, + "up": [ + 0, + 1 + ] + }, + "image": "01-korean-3current.jpg", + "errors": [] + } + }, + "02-model-paper": { + "1before": { + "snap": "95fa6a4", + "file": "civil.dwg", + "loadMs": 5041, + "status": "✓ civil.dwg · 17807 entities", + "ok": true, + "image": "02-model-paper-1before.jpg", + "errors": [] + }, + "2after": { + "snap": "be8d52f", + "file": "civil.dwg", + "loadMs": 5012, + "status": "✓ civil.dwg · 17810 entities · Layout", + "ok": true, + "image": "02-model-paper-2after.jpg", + "errors": [] + }, + "3current": { + "snap": "current", + "file": "civil.dwg", + "loadMs": 797, + "status": "✓ civil.dwg · 17810 entities · Layout", + "ok": true, + "image": "02-model-paper-3current.jpg", + "errors": [] + } + }, + "03-large-hatch": { + "3current": { + "snap": "current", + "file": "dwg_18.3mb.dwg", + "loadMs": 3693, + "status": "✓ dwg_18.3mb.dwg · 82393 entities · Model", + "ok": true, + "image": "03-large-hatch-3current.jpg", + "errors": [] + }, + "1before": { + "snap": "7a6edd4", + "file": "dwg_18.3mb.dwg", + "loadMs": 213769, + "status": "✓ dwg_18.3mb.dwg · 82393 entities · Model", + "ok": true, + "image": "03-large-hatch-1before.jpg", + "errors": [] + }, + "2after": { + "snap": "b047dc4", + "file": "dwg_18.3mb.dwg", + "loadMs": 3597, + "status": "✓ dwg_18.3mb.dwg · 82393 entities · Model", + "ok": true, + "image": "03-large-hatch-2after.jpg", + "errors": [] + } + }, + "04-slug-text": { + "3current": { + "snap": "current", + "file": "BasicSample.dwg", + "loadMs": 529, + "status": "✓ BasicSample.dwg · 7309 entities · Model", + "ok": true, + "space": "model 2", + "zoom": { + "camX": 2900, + "camY": 1900, + "width": 1100, + "up": [ + 0, + 1 + ] + }, + "image": "04-slug-text-3current.jpg", + "errors": [] + }, + "1before": { + "snap": "b047dc4", + "file": "BasicSample.dwg", + "loadMs": 524, + "status": "✓ BasicSample.dwg · 7309 entities · Model", + "ok": true, + "space": "model 2", + "zoom": { + "camX": 2900, + "camY": 1900, + "width": 1100, + "up": [ + 0, + 1 + ] + }, + "image": "04-slug-text-1before.jpg", + "errors": [] + }, + "2after": { + "snap": "fb8f7ca", + "file": "BasicSample.dwg", + "loadMs": 531, + "status": "✓ BasicSample.dwg · 7309 entities · Model", + "ok": true, + "space": "model 2", + "zoom": { + "camX": 2900, + "camY": 1900, + "width": 1100, + "up": [ + 0, + 1 + ] + }, + "image": "04-slug-text-2after.jpg", + "errors": [] + } + }, + "05-mesh": { + "3current": { + "snap": "current", + "file": "대산당진 2공구_노면_20260804_143854.dwg", + "loadMs": 5300, + "status": "✓ 대산당진 2공구_노면_20260804_143854.dwg · 65495 entities · Model", + "ok": true, + "space": "model 70", + "zoom": { + "camX": 156542.00000000003, + "camY": 479804.00000000006, + "width": 160, + "up": [ + 0, + 1 + ] + }, + "image": "05-mesh-3current.jpg", + "errors": [] + }, + "1before": { + "snap": "6f200ef", + "file": "대산당진 2공구_노면_20260804_143854.dwg", + "loadMs": 5030, + "status": "✓ 대산당진 2공구_노면_20260804_143854.dwg · 65486 entities · Model", + "ok": true, + "space": "model 70", + "zoom": { + "camX": 156542.00000000003, + "camY": 479804.00000000006, + "width": 160, + "up": [ + 0, + 1 + ] + }, + "image": "05-mesh-1before.jpg", + "errors": [] + }, + "2after": { + "snap": "c67a803", + "file": "대산당진 2공구_노면_20260804_143854.dwg", + "loadMs": 5279, + "status": "✓ 대산당진 2공구_노면_20260804_143854.dwg · 65495 entities · Model", + "ok": true, + "space": "model 70", + "zoom": { + "camX": 156542.00000000003, + "camY": 479804.00000000006, + "width": 160, + "up": [ + 0, + 1 + ] + }, + "image": "05-mesh-2after.jpg", + "errors": [] + } + }, + "06-lineweight": { + "3current": { + "snap": "current", + "file": "대산당진 2공구_노면_20260804_143854.dwg", + "loadMs": 5347, + "status": "✓ 대산당진 2공구_노면_20260804_143854.dwg · 65495 entities · Model", + "ok": true, + "space": "model 70", + "zoom": { + "camX": 156542.00000000003, + "camY": 479804.00000000006, + "width": 160, + "up": [ + 0, + 1 + ] + }, + "image": "06-lineweight-3current.jpg", + "errors": [] + }, + "1before": { + "snap": "eec3382", + "file": "대산당진 2공구_노면_20260804_143854.dwg", + "loadMs": 5068, + "status": "✓ 대산당진 2공구_노면_20260804_143854.dwg · 65495 entities · Model", + "ok": true, + "space": "model 70", + "zoom": { + "camX": 156542.00000000003, + "camY": 479804.00000000006, + "width": 160, + "up": [ + 0, + 1 + ] + }, + "image": "06-lineweight-1before.jpg", + "errors": [] + }, + "2after": { + "snap": "1f80442", + "file": "대산당진 2공구_노면_20260804_143854.dwg", + "loadMs": 5220, + "status": "✓ 대산당진 2공구_노면_20260804_143854.dwg · 65495 entities · Model", + "ok": true, + "space": "model 70", + "zoom": { + "camX": 156542.00000000003, + "camY": 479804.00000000006, + "width": 160, + "up": [ + 0, + 1 + ] + }, + "image": "06-lineweight-2after.jpg", + "errors": [] + } + }, + "07-layer-toggle": { + "3current": { + "snap": "current", + "file": "dwg_18.3mb.dwg", + "loadMs": 3698, + "status": "✓ dwg_18.3mb.dwg · 82393 entities · Model", + "ok": true, + "toggle": { + "layer": "CF-PATT", + "hideMs": [ + 21, + 18, + 13 + ], + "showMs": [ + 35, + 5, + 5 + ] + }, + "errors": [] + }, + "1before": { + "snap": "fb8f7ca", + "file": "dwg_18.3mb.dwg", + "loadMs": 3703, + "status": "✓ dwg_18.3mb.dwg · 82393 entities · Model", + "ok": true, + "toggle": { + "layer": "CF-PATT", + "hideMs": [ + 565, + 454, + 512 + ], + "showMs": [ + 501, + 622, + 609 + ] + }, + "errors": [] + }, + "2after": { + "snap": "3480945", + "file": "dwg_18.3mb.dwg", + "loadMs": 3603, + "status": "✓ dwg_18.3mb.dwg · 82393 entities · Model", + "ok": true, + "toggle": { + "layer": "CF-PATT", + "hideMs": [ + 18, + 17, + 13 + ], + "showMs": [ + 26, + 5, + 6 + ] + }, + "errors": [] + } + }, + "n1-percent": { + "3current": { + "snap": "current", + "file": "C0060203-001-표준단면도(2차로,자연환기300m이상).dwg", + "loadMs": 528, + "status": "✓ C0060203-001-표준단면도(2차로,자연환기300m이상).dwg · 2068 entities · Model", + "ok": true, + "space": "model 2", + "zoom": { + "camX": 12700, + "camY": 16740, + "width": 2600, + "up": [ + 0, + 1 + ] + }, + "image": "n1-percent-3current.jpg", + "errors": [] + } + }, + "n2-cp949-symbol": { + "3current": { + "snap": "current", + "file": "civil.dwg", + "loadMs": 820, + "status": "✓ civil.dwg · 17810 entities · Layout", + "ok": true, + "space": "model 1f", + "zoom": { + "camX": 162366.6, + "camY": 475780, + "width": 9, + "up": [ + 0, + 1 + ] + }, + "image": "n2-cp949-symbol-3current.jpg", + "errors": [] + } + }, + "n3-fit-crop": { + "3current": { + "snap": "current", + "file": "산외교- ELEVATION 산출근거도 (울산방향).dwg", + "loadMs": 738, + "status": "✓ 산외교- ELEVATION 산출근거도 (울산방향).dwg · 11262 entities · Model", + "ok": true, + "image": "n3-fit-crop-3current.jpg", + "errors": [] + } + } +} \ No newline at end of file diff --git a/docs/sample-test-report-2026-10-19.html b/docs/sample-test-report-2026-10-19.html new file mode 100644 index 0000000..fd02a4b --- /dev/null +++ b/docs/sample-test-report-2026-10-19.html @@ -0,0 +1,1238 @@ + + + + + +샘플 테스트 보고서 + + + +
+
+

샘플 테스트·오류 처리 현황 보고서 (2026-10-19)

+
+

대상: dwg-dxf-viewer-sample (HEAD 1f80442 + 미커밋 작업 트리) +근거: 이번 재테스트 실측, git 이력 20커밋, docs/*.md 10건, hmwebviewer/PROGRESS.md +상태 표기: 원인 확정 · 수정 완료·검증됨 · 수정 완료·미검증 +이 보고서에는 검증된 결과만 싣는다. 검증하지 못한 항목은 §8 목록에만 적는다.

+
+
+

1. 핵심

+ +

2. 권장 액션

+
    +
  1. 라이선스.
      +
    • MPL 패치를 다시 만든다. 원본 0.4.1 과 실제 vendored 트리의 전체 diff 로 만들고, 원본에 적용해서 성공하는지 확인한다.
    • +
    • 고지 문서 4곳의 "한 파일만 수정", "unmodified" 문구를 고친다.
    • +
    • ICAD_LIN 은 IntelliCAD(ITC) 계약 범위를 확인한다. 외부 배포가 허용되지 않으면 제거한다.
    • +
    +
  2. +
  3. 미커밋 작업 정리.
      +
    • 2.5D 보기, VIEWPORT 채움 변환, Z 붕괴 수정을 커밋하거나 분리한다.
    • +
    • 루트의 untracked 대용량 파일(C0060202.json 31.7MB, SAN01.json 16.6MB, C0060202.zip)은 .gitignore 에 넣거나 삭제한다.
    • +
    +
  4. +
  5. e2e 를 실제 테스트로.
      +
    • linParser.ts 의 확장자 없는 import 를 고친다.
    • +
    • 샘플 경로를 dist/ 에서 public/samples/ 로 바꾼다.
    • +
    • assert 를 추가한다.
    • +
    • 이번에 쓴 브라우저 e2e 와 3단 비교 스크립트를 레포에 넣는다.
    • +
    +
  6. +
  7. Fit 잘림 수정 (§3.4-①).
  8. +
  9. 텍스트 후처리 2건 수정.
      +
    • stripMText 에 %%%, %%nnn, %%o, %%u 처리를 추가한다.
    • +
    • fixCp949Mojibake 가 한글이 없는 CP949 기호도 복원하게 한다.
    • +
    +
  10. +
+
+

3. 이번 재테스트 결과

+

3.1 환경

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
항목값
코드HEAD 1f80442 + 미커밋 변경(2.5D 보기, _bakeFillXf, _trimmedContentBox Z 수정)
Nodev24.11.1
브라우저시스템 Chrome headless, playwright-core@1.56.1
GPUANGLE D3D11 / NVIDIA RTX 3060 (실제 GPU)
타입체크npm run typecheck 통과 (컴파일 통과일 뿐, 동작 검증은 아님)
+

3.2 샘플별 결과

+

열 설명:

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
파일MB버전엔티티Node 파싱 msRSS MB브라우저 로드 msJS heap MB콘솔 에러notif결과
BasicSample.dwg0.4R20187,309142147553471¹4통과
civil.dwg²5.4R200017,8103892647999802통과
C0060203-001-표준단면도(…).dwg0.4R20072,0689312656757078통과
bb.dwg0.7R2018438751095794604통과
simple.dxf0.0DXF6——74340—통과
04manhole1-simplify.dwg0.4R20187,0351181385755404통과
dwg_18.3mb.dwg18.4R201882,3931,9881,0874,23237204통과
test.dwg³18.4R201882,3932,1561,0883,91437104통과
대산당진 2공구_노면_….dwg37.1R201865,4953,0171,3735,45478204통과
a. 거더배치 삽도0.2R20181,43311813456813905통과
a. 교대배치 삽도0.4R20182,138581025655904통과
a. 교량시종점 삽도0.4R20184,432841055425604통과
a. 교량폭원 삽도0.5R20182,824641065754504통과
a. 교면포장 삽도0.2R20181,304541045704404통과
a. 중앙분리대 삽도0.4R20182,159631035374404통과
a. 횡단경사 삽도0.5R20185,6351261385694404통과
산외교- ELEVATION 산출근거도0.6R200711,262162181937111044통과
산외교-받침배치도0.4R20186,2401061446356304통과
산외교_파이형_교각일반도(울산P11-꺾임)1.0R201825,9273712198105604통과
산외교_파이형_교각일반도(울산P9)1.0R201826,2073972207445704통과
+

¹ 404 리소스 1건, 첫 페이지에서만 발생. /favicon.ico 가 404 를 반환하는 것은 확인했다. +² civil.dwg 와 C0060202-001-평면및종단면도(…).dwg 는 md5 가 같다. 같은 파일이다. +³ test.dwg 는 dwg_18.3mb.dwg 와 바이트는 다르지만, 파싱 결과의 엔티티 수와 텍스트 통계가 같다.

+

"통과" 의 기준: 상태바에 ✓ <그 파일명> · … entities 가 표시되고, .err 가 없고, 페이지 crash 가 없을 것.

+

3.3 기존 e2e (e2e_lin_test.cjs)

+
+ + + + + + + + + + + + + + +
실행 방식결과
node e2e_lin_test.cjs (그대로)실패, exit 1. 4단계에서 ERR_MODULE_NOT_FOUND: …/src/viewer2d/linData
resolve hook 추가끝까지 실행, exit 0. H-DICHL06/06R/02R/01R/05 패턴 5개 출력
+

원인 확정: linParser.ts 가 ./linData 를 확장자 없이 import 한다. Vite 는 이것을 해석하지만 Node ESM 은 해석하지 못한다.

+

그 밖의 문제:

+ +

3.4 이번에 새로 확인한 문제

+

① Fit 이 도면 가장자리를 잘라냄. 재현 확인.

+ +

② AutoCAD 제어코드 %%% 등이 변환되지 않음. 원인 확정.

+ +

③ 기호만 있는 CP949 문자열이 깨진 채 남음. 원인 확정.

+ +

④ e2e 스크립트 고장. §3.3 참고.

+

기타 (경미)

+ +
+

4. 처리된 오류 이력

+

표기: [M] 커밋 메시지 · [D] diff 로 확인 · [Doc] 문서 기록. +Rust 쪽 수정(dwg-wasm, acadrust)은 hmwebviewer/rust/dwg-wasm 에 있다.

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
#시기증상재현 샘플원인조치커밋이번 검증
107-30구형 한글 TEXT/MTEXT 깨짐civil 외CP949 바이트가 Latin-1 로 올라옴 [Doc]fixDwgKoreanText.ts (뷰어)be8d52f수정 완료·검증됨 (§4.1-①)
207-30Model 과 Paper 동시 렌더, Layout 비어 보임, Fit/좌표, 글자 거대, VP 순서 (6건)C0060202VIEWPORT 미투영, minTextH 등 [Doc]cadSpaces.ts, Viewer2D (뷰어)be8d52f수정 완료·검증됨 (§4.1-②)
307-30Brave/Firefox 에서 OLE BMP 미표시—<img> 가 data:image/bmp 미지원 [M]순수 JS BMP 디코더 [D]963a903§8
407-30OLE2FRAME 이 하드코딩된 좌표에 고정됨—정답 좌표 하드코딩 [D]파서가 준 ul/lr 사용76f3e20§8
508-03Slug 텍스트 cap-height 약 26% 작음BasicSample3d3e052 의 회귀 [D]되돌림fb8f7ca수정 완료·검증됨 (§4.1-④)
608-0319MB DWG 로딩 시 탭 프리즈dwg_18.3mbwasm 힙 O(n²) [M]parse_dwg_json 스트리밍b047dc4수정 완료·검증됨 (§4.1-③)
708-03도면 위아래 거대 회색 원dwg_18.3mbsample_arc 각도 swap 등 [Doc]손상 호 감지 후 생략 (파서)b047dc4수정 완료·검증됨 (§4.1-③)
808-04레이어 토글 지연dwg_18.3mb토글마다 load() 재빌드 [M]setDrawRange, .visible 토글3480945수정 완료·검증됨 (§4.1-⑦)
908-04노면/부체도로면(MESH) 미표시대산당진 노면MESH 직렬화 누락, read_mesh 100k 상한 [M]acadrust 패치 + 직렬화 + _meshSegseec3382, c67a803수정 완료·검증됨 (§4.1-⑤)
1008-04모든 선이 1px대산당진 노면lineWeight 누락, WebGL linewidth 무시 [M]LineSegments2 버킷c67a803수정 완료·검증됨 (§4.1-⑥)
1108-04~05선가중치 배율 (8 → 20 → 11 px/mm)대산당진 노면배율 [M]기본값 11, 옵션 패널cf12987, a924b1e, 1f80442굵기 적용 확인 (§4.1-⑥). 배율 일치는 §8
1208-04MPL-2.0 고지 누락—고지/원문/소스 경로 없음 [M]NOTICES, public/licenses/ce3859c조치 불충분: 패치가 원본에 적용되지 않음 (§5 A-1)
1308-04vite build 가 dist2 테스트 파일을 삭제—emptyOutDir 기본값 [M]--emptyOutDir false 절차 문서화6f200ef—
+

분리 전 hmwebviewer 에서 처리한 것 (07-10~14, PROGRESS.md):

+ +

미커밋 수정 2건 (수정 완료·미검증):

+ +

4.1 수정 전 / 수정 후 / 현재 비교 캡처

+

방법

+ +

① 한글 깨짐 (civil.dwg)

+

수정 전 버전의 wasm 이 실제로 내보낸 문자열과, 수정 후 화면을 함께 비교했다:

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
handle수정 전 95fa6a4 wasm 출력수정 후 be8d52f
3232403Ãø Á¡측 점
3232405°èȹ°í계획고
3232408Áö¹Ý°í지반고
3240601õÀÇ1Áö±¸ °èȹ¼±천의1지구 계획선
3241973(»ç¾÷ ÁøÇà ¿¹Á¤)(사업 진행 예정)
1009°Ç¼³´Ü°è건설단계
+
+ + + + + + + + + + +
수정 후 be8d52f현재
01-korean-2after.jpg01-korean-3current.jpg
+

② Model/Paper 겹침, Layout 비어 보임 (civil.dwg, 로드 직후 기본 화면)

+
+ + + + + + + + + + + + + + + + + +
수정 전 95fa6a4수정 후 be8d52f현재
02-model-paper-1before.jpg02-model-paper-2after.jpg02-model-paper-3current.jpg
점 3개만 보임. Model(x≈600k·162k)과 Paper(원점)를 한꺼번에 Fit 함Layout 이 열리고 VIEWPORT 내용이 투영됨같음 (밝은 종이 배경)
+

③ 대용량 로딩 + 거대 회색 원 (dwg_18.3mb.dwg, 로드 직후 기본 화면)

+
+ + + + + + + + + + + + + + + + + +
수정 전 7a6edd4수정 후 b047dc4현재
03-large-hatch-1before.jpg03-large-hatch-2after.jpg03-large-hatch-3current.jpg
로드 213.8초. 회색 SOLID HATCH 가 도면 아래쪽 절반을 덮음3.6초, 회색 채움 없음3.7초
+

④ Slug 텍스트 크기 (BasicSample.dwg, "정면도" h=140 주변 고정 화면)

+
+ + + + + + + + + + + + + + + + + +
수정 전 b047dc4수정 후 fb8f7ca현재
04-slug-text-1before.jpg04-slug-text-2after.jpg04-slug-text-3current.jpg
글자 높이 150px202px202px
+ +

⑤ SubDMesh(MESH) 미표시 (노면 도면, 연결부 (156542, 479804), 폭 160)

+
+ + + + + + + + + + + + + + + + + +
수정 전 6f200ef수정 후 c67a803현재
05-mesh-1before.jpg05-mesh-2after.jpg05-mesh-3current.jpg
삼각망 없음도로면·부체도로면 삼각망 표시같음
+

c67a803 에는 선가중치 수정도 함께 들어 있다.

+

⑥ 선가중치 (⑤와 같은 화면)

+
+ + + + + + + + + + + + + + + + + +
수정 전 eec3382수정 후 1f80442 (11 px/mm)현재
06-lineweight-1before.jpg06-lineweight-2after.jpg06-lineweight-3current.jpg
0.50mm 도로마킹 포함 모든 선 1px (메쉬 렌더러 이전 버전)굵게 표시같음
+

⑦ 레이어 토글 속도 (dwg_18.3mb.dwg, setHiddenLayers(CF-PATT) 숨김→표시 3회, 동기 실행 시간)

+
+ + + + + + + + + + + + + + + + + + + + +
수정 전 fb8f7ca수정 후 3480945현재
숨김 ms565 / 454 / 51218 / 17 / 1321 / 18 / 13
표시 ms501 / 622 / 60926 / 5 / 635 / 5 / 5
+

새로 확인한 문제 (현재 버전)

+
+ + + + + + + + + + + + + + + + + +
%%% 미변환 (C0060203)CP949 기호 잔존 (civil)Fit 잘림 (ELEVATION)
n1-percent-3current.jpgn2-cp949-symbol-3current.jpgn3-fit-crop-3current.jpg
-2 %%% (정답 -2 %)500¡¿350 (정답 500×350)왼쪽 잘림, 오른쪽 약 470px 공백
+
+

5. 남은 과제

+

A. 우선 처리

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
#과제근거
A-1MPL 패치가 실제 소스를 재현하지 못함 (원인 확정)
- public/licenses/acadrust-0.4.1-read_mesh.patch 를 원본 acadrust 0.4.1 crate 에 git apply --check 하면 entities.rs:2350(read_ole2frame)에서 실패한다.
- 고지의 "One file differs", "exact Source Code Form" 문구와 acadrust-dwg/package.json 의 "used unmodified" 가 사실과 다르다.
이번 실행, THIRD-PARTY-NOTICES.md
A-2Visio 저작권 데이터가 번들됨
- linData.ts 의 ICAD_LIN 헤더가 "Copyright (c) 1998 by Visio Corporation. All rights reserved" 다.
- linParser.ts:228 이 import 될 때 등록하므로 배포본에 반드시 들어간다.
- IntelliCAD(ITC) 계약이 자사 웹 뷰어로의 외부 배포를 허용하는지 확인이 필요하다.
이번 확인
A-3자체 LICENSE 파일 없음, NanumGothic OFL 원문 미동봉§6
A-4자동 판정 테스트 없음, e2e 고장§3.3
A-5Fit 이 가장자리를 잘라냄§3.4-①
A-6미커밋 WIP 정리, src/topProgress.ts 어디서도 import 되지 않음, 루트 대용량 untracked 파일git status
A-7원본 hmwebviewer 에 Model/Paper 수정 미이식 (cadSpaces.ts 없음)model-paper-space.md §9
+

B. 기능·품질

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
#과제근거
B-1해치 손상 호의 근본 원인 미확인. 현재 수정은 감지 후 생략. spurious_r 5배 임계값은 경험값hatch-boundary-arc-artifact.md §7
B-2파싱 중 메인 스레드 블로킹. 37MB 도면 로드 5.5초 동안 UI 정지. Web Worker 미적용이번 측정
B-3메쉬 레이어에 굵기가 지정되면 fat 버킷 상한(40만) 124.5% 초과 → 버킷 전체 헤어라인 폴백improvement-results §5-3
B-4%%% 등 제어코드, CP949 기호§3.4-②③
B-5tables.styles 비어 있음 → 폰트 매핑 불가한글 문서 §8, 이번 e2e
B-6한글 처리가 JS 휴리스틱. 932/936/950 미지원한글 문서 §8
B-7Layout 탭 캡션이 내부명이번 실행
B-8acadrust notification 내용 비노출이번 실행
B-940만 상한 초과 시 사용자 알림 없음 (console.warn 만)lineweight-rendering.md §6
B-10wasm32 4GB 한계. 37MB 도면에서 Node RSS 1.37GB, 브라우저 JS heap 782MB이번 측정
+

C. 나중에

+ +

D. 배포·문서 정리

+ +
+

6. 라이브러리 배포 가능성

+

6.1 결론: 조건부 가능

+ +

6.2 차단 요소

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
#문제위치영향
L1MPL 패치로 소스 재현 불가§5 A-1법적 의무 미충족
L2Visio 저작권 lin 데이터linData.ts:2재배포 권리 확인 필요
L3자체 LICENSE, package.json license 필드 없음. Viewer2D.js:3 에 "Ported from webviewer/…"루트사용 권리 불명확
L4NanumGothic OFL 원문 미동봉public/licenses/폰트 동봉 시 위반
B1?url import 가 Vite 전용acadrustParser.ts:6Node·webpack·SSR 에서 실패. Vite lib 모드로 빌드하면 wasm 이 base64 로 두 번 인라인됨 (실측 2.37MB)
B2확장자 없는 상대 importdwgParser.ts:16, parseDxf.ts:7, Viewer2D.js:16-18 외Node ESM 에서 ERR_MODULE_NOT_FOUND (§3.3 과 같은 원인)
B3폰트 경로 /fonts/… 하드코딩, 전역 싱글톤slugText.ts:32-49, Viewer2D.js:127호스트에 그 경로가 없으면 404
B4dispose() 없음Viewer2D.js:159,164,4527익명 window 리스너와 rAF 루프를 해제할 수 없음. F 키 리스너는 입력란에서도 Fit 실행
B5타입 불완전Viewer2D.d.tsload(result: unknown), 일부 메서드 누락, 기본값 주석 20 ≠ 실제 11
B6패키지 설정 없음package.jsonprivate: true, exports/files/peerDependencies 없음
B7소스 정본이 여러 곳이 레포 / hmwebviewer/src/viewer2d / hmwebviewer/packages/dwg-dxf-viewerwasm 바이너리 3개가 모두 다름. Viewer2D.js 는 hmwebviewer 와 1,217줄 차이
+

6.3 권장 구조: 패키지 2개

+
@scope/cad-parser   (headless, three 없음)        license: "MIT AND MPL-2.0"
+  initDwgParser({ wasm?: URL | bytes | Module })
+  parseDwg(bytes) / parseDxf(buf) / parseCad(buf, nameHint?)
+  listCadSpaces, pickDefaultSpace, isDwg/isDxf, CadParseResult 타입
+  ./worker  (선택: 메인 스레드 비블로킹)
+
+@scope/viewer2d     (Viewer2D, slugText, linParser)   license: "MIT"
+  peerDependencies: three ^0.185.0   ← peer + external
+  dependencies:     @scope/cad-parser, opentype.js
+
+

파서 패키지 package.json 핵심:

+
{
+  "type": "module",
+  "license": "MIT AND MPL-2.0",
+  "exports": {
+    ".": { "types": "./dist/index.d.ts", "node": "./dist/node.js", "default": "./dist/browser.js" },
+    "./worker": "./dist/worker.js",
+    "./acadrust_dwg_bg.wasm": "./dist/acadrust_dwg_bg.wasm"
+  },
+  "files": ["dist", "licenses", "THIRD-PARTY-NOTICES.md", "LICENSE"],
+  "sideEffects": false
+}
+
+

wasm 로딩:

+
    +
  1. 기본: glue 의 new URL('./acadrust_dwg_bg.wasm', import.meta.url) 패턴을 유지한다.
  2. +
  3. 탈출구: init({ wasm }) 로 URL, bytes, Module 을 직접 넘길 수 있게 한다.
  4. +
  5. Node: initSync({ module: readFileSync(...) }). scripts/parse-dwg.mjs 에서 동작을 확인했다.
  6. +
+

6.4 작업 순서

+
    +
  1. 라이선스 L1~L4 해결
  2. +
  3. 정본 레포 통일, rust/dwg-wasm 과 vendored acadrust 이전, rust-toolchain.toml 로 툴체인 고정
  4. +
  5. 파서 분리: ?url 제거, init({ wasm }) 추가, Node 진입점, parseCad(), 상대 import 에 .js
  6. +
  7. Viewer2D: 생성자 옵션(fontUrl, keyboard 등), dispose(), ResizeObserver, localStorage 잔재 제거, 콜백 해제 함수
  8. +
  9. 타입: 수기 Viewer2D.d.ts 보완, CadParseResult 구체화
  10. +
  11. 빌드: tsup/rollup/tsc 로 ESM, wasm 은 파일로 복사, opentype.js 는 default import (Node ESM 에서 named import 실패 실측)
  12. +
  13. 검증: 소비 예제(Vite, webpack 5, Node)를 CI 로 돌리고, §3 의 샘플 20개 e2e 를 회귀 테스트로
  14. +
+

6.5 유의할 점

+ +
+

7. 재현 방법

+
# 헤드리스 파싱 (1개 파일)
+node scripts/parse-dwg.mjs "<file.dwg>" [out.json]
+
+# 기존 e2e (현재는 실패, §3.3)
+node e2e_lin_test.cjs
+
+

배치 파서, 브라우저 e2e, 3단 비교 캡처 스크립트는 세션 임시 폴더에 있다. 레포 반영은 §2-3. 측정 원본은 images/sample-test-2026-10-19/results.json 에 있다.

+

8. 검증 불가 목록

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
항목이유
OLE BMP(#3), OLE2FRAME(#4)OLE2FRAME 엔티티가 있는 샘플이 없음
선가중치 배율(#11)이 AutoCAD 와 일치하는지같은 위치의 AutoCAD 정답 캡처가 없음
한글 깨짐(#1) 수정 전 화면같은 커밋에서 고친 '글자 거대' 버그 때문에 판독 불가. 문자열 비교로 대신 검증함
수정 전 수치와 문서 기록의 차이 (로드 213.8s vs 문서 354s, 토글 약 0.5s vs 커밋 "5초 이상")당시 측정 환경과 방법을 알 수 없음
Fit 잘림의 원인재현만 확인, 원인 미확정
H-DICHL06 의 patternLen(1) 과 요소 합(1.588) 중 어느 쪽이 AutoCAD 기준인지AutoCAD 대조 수단 없음
문서상 한글 재현 도면 C0060203-002-굴착및시공순서도.dwg레포에 없음
7~8월 테스트 당시의 증상 원문세션 기록이 남아 있지 않음 (가장 오래된 기록이 09-16)
+ +
+ + + + diff --git a/docs/sample-test-report-2026-10-19.md b/docs/sample-test-report-2026-10-19.md new file mode 100644 index 0000000..f96f163 --- /dev/null +++ b/docs/sample-test-report-2026-10-19.md @@ -0,0 +1,418 @@ +# 샘플 테스트·오류 처리 현황 보고서 (2026-10-19) + +> 대상: `dwg-dxf-viewer-sample` (HEAD `1f80442` + 미커밋 작업 트리) +> 근거: 이번 재테스트 실측, git 이력 20커밋, `docs/*.md` 10건, `hmwebviewer/PROGRESS.md` +> 상태 표기: `원인 확정` · `수정 완료·검증됨` · `수정 완료·미검증` +> 이 보고서에는 검증된 결과만 싣는다. 검증하지 못한 항목은 §8 목록에만 적는다. + +--- + +## 1. 핵심 + +- **고유 샘플 20개(DWG 19 + DXF 1)가 모두 브라우저 뷰어에서 로드에 성공했다.** 콘솔 에러는 404 리소스 1건뿐이다. Node 헤드리스 파싱도 19/19 성공했다. +- **기존 e2e(`e2e_lin_test.cjs`)는 지금 상태 그대로는 실패한다** (`ERR_MODULE_NOT_FOUND`). 끝까지 돌려도 assert 가 없어서 결과를 판정하지 못한다. +- **과거에 처리한 오류 13건 중 8건을 검증했다** (§4.1). 해당 커밋의 수정 전 버전과 수정 후 버전을 실제로 띄워서, 현재 버전까지 3단으로 캡처하고 측정했다. + - 대용량 로드: 213.8초 → 3.6초 + - Slug 글자 높이: 150px → 202px (비율 0.743, 커밋 주장 0.744) + - 레이어 토글: 454~622ms → 13~35ms + - 그 외 회색 원 제거, 메쉬 표시, 선가중치 적용, Model/Paper 분리, 한글 복원 +- **이번에 새로 찾은 문제는 4건이다.** + - Fit 이 도면 가장자리를 잘라냄 + - `%%%` 제어코드가 변환되지 않음 + - 기호만 있는 CP949 문자열이 깨진 채 남음 + - e2e 스크립트가 고장 나 있음 +- **라이선스 2건 (`원인 확정`)** + - 공개한 MPL 패치를 원본 acadrust 0.4.1 에 적용하면 실패한다. 실제 wasm 의 소스를 재현할 수 없다는 뜻이다. + - Visio 저작권("All rights reserved") 라인타입 데이터가 번들에 들어 있다. + - Gitea 와 dist2 는 사내 전용이다. **외부 반출(고객 납품·외부 웹 공개·npm) 전에 해결해야 한다.** 라이선스 고지 버튼 자체는 형식이 맞다. 그 버튼이 가리키는 패치 내용이 틀렸다. +- **라이브러리 배포는 조건부로 가능하다** (§6). 위 라이선스 2건, Vite 전용 코드, Viewer2D 에 `dispose()` 가 없는 문제를 먼저 해결해야 한다. + +## 2. 권장 액션 + +1. **라이선스.** + - MPL 패치를 다시 만든다. 원본 0.4.1 과 실제 vendored 트리의 전체 diff 로 만들고, 원본에 적용해서 성공하는지 확인한다. + - 고지 문서 4곳의 "한 파일만 수정", "unmodified" 문구를 고친다. + - `ICAD_LIN` 은 IntelliCAD(ITC) 계약 범위를 확인한다. 외부 배포가 허용되지 않으면 제거한다. +2. **미커밋 작업 정리.** + - 2.5D 보기, VIEWPORT 채움 변환, Z 붕괴 수정을 커밋하거나 분리한다. + - 루트의 untracked 대용량 파일(`C0060202.json` 31.7MB, `SAN01.json` 16.6MB, `C0060202.zip`)은 `.gitignore` 에 넣거나 삭제한다. +3. **e2e 를 실제 테스트로.** + - `linParser.ts` 의 확장자 없는 import 를 고친다. + - 샘플 경로를 `dist/` 에서 `public/samples/` 로 바꾼다. + - assert 를 추가한다. + - 이번에 쓴 브라우저 e2e 와 3단 비교 스크립트를 레포에 넣는다. +4. **Fit 잘림 수정** (§3.4-①). +5. **텍스트 후처리 2건 수정.** + - `stripMText` 에 `%%%`, `%%nnn`, `%%o`, `%%u` 처리를 추가한다. + - `fixCp949Mojibake` 가 한글이 없는 CP949 기호도 복원하게 한다. + +--- + +## 3. 이번 재테스트 결과 + +### 3.1 환경 + +| 항목 | 값 | +|---|---| +| 코드 | HEAD `1f80442` + 미커밋 변경(2.5D 보기, `_bakeFillXf`, `_trimmedContentBox` Z 수정) | +| Node | v24.11.1 | +| 브라우저 | 시스템 Chrome headless, `playwright-core@1.56.1` | +| GPU | ANGLE D3D11 / NVIDIA RTX 3060 (실제 GPU) | +| 타입체크 | `npm run typecheck` 통과 (컴파일 통과일 뿐, 동작 검증은 아님) | + +### 3.2 샘플별 결과 + +열 설명: +- **Node 파싱**: `parse_dwg_json` + `fixDwgKoreanText` 실행 시간. +- **RSS**: 프로세스 전체 메모리. +- **브라우저 로드**: 파일을 선택한 뒤 상태바에 `✓ <파일명>` 이 뜨기까지의 시간. +- **JS heap**: `performance.memory`. +- **notif**: acadrust notification 개수. + +| 파일 | MB | 버전 | 엔티티 | Node 파싱 ms | RSS MB | 브라우저 로드 ms | JS heap MB | 콘솔 에러 | notif | 결과 | +|---|---|---|---|---|---|---|---|---|---|---| +| BasicSample.dwg | 0.4 | R2018 | 7,309 | 142 | 147 | 553 | 47 | 1¹ | 4 | 통과 | +| civil.dwg² | 5.4 | R2000 | 17,810 | 389 | 264 | 799 | 98 | 0 | 2 | 통과 | +| C0060203-001-표준단면도(…).dwg | 0.4 | R2007 | 2,068 | 93 | 126 | 567 | 57 | 0 | **78** | 통과 | +| bb.dwg | 0.7 | R2018 | 438 | 75 | 109 | 579 | 46 | 0 | 4 | 통과 | +| simple.dxf | 0.0 | DXF | 6 | — | — | 74 | 34 | 0 | — | 통과 | +| 04manhole1-simplify.dwg | 0.4 | R2018 | 7,035 | 118 | 138 | 575 | 54 | 0 | 4 | 통과 | +| dwg_18.3mb.dwg | 18.4 | R2018 | 82,393 | 1,988 | 1,087 | **4,232** | 372 | 0 | 4 | 통과 | +| test.dwg³ | 18.4 | R2018 | 82,393 | 2,156 | 1,088 | 3,914 | 371 | 0 | 4 | 통과 | +| 대산당진 2공구_노면_….dwg | 37.1 | R2018 | 65,495 | 3,017 | **1,373** | **5,454** | **782** | 0 | 4 | 통과 | +| a. 거더배치 삽도 | 0.2 | R2018 | 1,433 | 118 | 134 | 568 | 139 | 0 | 5 | 통과 | +| a. 교대배치 삽도 | 0.4 | R2018 | 2,138 | 58 | 102 | 565 | 59 | 0 | 4 | 통과 | +| a. 교량시종점 삽도 | 0.4 | R2018 | 4,432 | 84 | 105 | 542 | 56 | 0 | 4 | 통과 | +| a. 교량폭원 삽도 | 0.5 | R2018 | 2,824 | 64 | 106 | 575 | 45 | 0 | 4 | 통과 | +| a. 교면포장 삽도 | 0.2 | R2018 | 1,304 | 54 | 104 | 570 | 44 | 0 | 4 | 통과 | +| a. 중앙분리대 삽도 | 0.4 | R2018 | 2,159 | 63 | 103 | 537 | 44 | 0 | 4 | 통과 | +| a. 횡단경사 삽도 | 0.5 | R2018 | 5,635 | 126 | 138 | 569 | 44 | 0 | 4 | 통과 | +| 산외교- ELEVATION 산출근거도 | 0.6 | R2007 | 11,262 | 162 | 181 | 937 | 111 | 0 | **44** | 통과 | +| 산외교-받침배치도 | 0.4 | R2018 | 6,240 | 106 | 144 | 635 | 63 | 0 | 4 | 통과 | +| 산외교_파이형_교각일반도(울산P11-꺾임) | 1.0 | R2018 | 25,927 | 371 | 219 | 810 | 56 | 0 | 4 | 통과 | +| 산외교_파이형_교각일반도(울산P9) | 1.0 | R2018 | 26,207 | 397 | 220 | 744 | 57 | 0 | 4 | 통과 | + +¹ 404 리소스 1건, 첫 페이지에서만 발생. `/favicon.ico` 가 404 를 반환하는 것은 확인했다. +² `civil.dwg` 와 `C0060202-001-평면및종단면도(…).dwg` 는 md5 가 같다. 같은 파일이다. +³ `test.dwg` 는 `dwg_18.3mb.dwg` 와 바이트는 다르지만, 파싱 결과의 엔티티 수와 텍스트 통계가 같다. + +"통과" 의 기준: 상태바에 `✓ <그 파일명> · … entities` 가 표시되고, `.err` 가 없고, 페이지 crash 가 없을 것. + +### 3.3 기존 e2e (`e2e_lin_test.cjs`) + +| 실행 방식 | 결과 | +|---|---| +| `node e2e_lin_test.cjs` (그대로) | **실패, exit 1**. 4단계에서 `ERR_MODULE_NOT_FOUND: …/src/viewer2d/linData` | +| resolve hook 추가 | 끝까지 실행, exit 0. H-DICHL06/06R/02R/01R/05 패턴 5개 출력 | + +`원인 확정`: `linParser.ts` 가 `./linData` 를 확장자 없이 import 한다. Vite 는 이것을 해석하지만 Node ESM 은 해석하지 못한다. + +그 밖의 문제: +- **assert 가 없다.** 패턴을 못 찾아도 `console.error` 만 찍고, 마지막에 항상 `COMPLETED SUCCESSFULLY` 를 출력한다. +- 샘플 경로가 gitignore 된 `./dist/samples/…` 다. clean checkout 에서는 실패한다. +- 느린 경로인 `parse_dwg` 를 쓰고 `fixDwgKoreanText` 를 적용하지 않는다. 그래서 출력되는 라인타입 설명이 `LÇüÃø±¸` 처럼 깨진다. +- `Found 0 text styles` 가 출력된다. `tables.styles` 가 비어 있다는 문서상 과제가 C0060202 에서도 재현된다. + +### 3.4 이번에 새로 확인한 문제 + +**① Fit 이 도면 가장자리를 잘라냄.** 재현 확인. +- ELEVATION 도면에서 Fit 을 하면 도곽 왼쪽이 화면 밖으로 나가고, 오른쪽에는 약 470px 가 비어 있다 (§4.1 캡처). +- 자동 Fit, Fit 버튼, 창 크기 변경 후 Fit 모두 같은 결과다. +- HEAD(미커밋 변경 제외)에서도 똑같다. WIP 회귀가 아니다. + +**② AutoCAD 제어코드 `%%%` 등이 변환되지 않음.** `원인 확정`. +- C0060203 에 `-2 %%%`, civil Layout 에 `S=0.0000%%%` 가 그대로 표시된다. +- [`Viewer2D.js:64`](../src/viewer2d/Viewer2D.js) 의 `stripMText` 는 `%%d`, `%%p`, `%%c` 만 처리한다. + +**③ 기호만 있는 CP949 문자열이 깨진 채 남음.** `원인 확정`. +- civil 에 `500¡¿350` 이 표시된다. CP949 로 디코딩하면 `500×350` 이다. +- [`fixDwgKoreanText.ts:64-67`](../src/viewer2d/fixDwgKoreanText.ts) 는 디코딩 결과에 한글이 생길 때만 변환을 받아들인다. + +**④ e2e 스크립트 고장.** §3.3 참고. + +**기타 (경미)** +- Layout 탭 이름이 `Layout (Paper_Space0)` 처럼 내부 블록명으로 나온다. dwg2model 삽도 7개 전부가 그렇다. +- Layout 탭이 많으면 툴바가 줄바꿈되면서 상태바가 속성 패널과 겹친다. +- `parse_dwg_json` 은 acadrust notification 의 개수만 돌려주고 내용은 돌려주지 않는다 (C0060203 78건, ELEVATION 44건). + +--- + +## 4. 처리된 오류 이력 + +표기: [M] 커밋 메시지 · [D] diff 로 확인 · [Doc] 문서 기록. +Rust 쪽 수정(dwg-wasm, acadrust)은 `hmwebviewer/rust/dwg-wasm` 에 있다. + +| # | 시기 | 증상 | 재현 샘플 | 원인 | 조치 | 커밋 | 이번 검증 | +|---|---|---|---|---|---|---|---| +| 1 | 07-30 | 구형 한글 TEXT/MTEXT 깨짐 | civil 외 | CP949 바이트가 Latin-1 로 올라옴 [Doc] | `fixDwgKoreanText.ts` (뷰어) | be8d52f | **`수정 완료·검증됨`** (§4.1-①) | +| 2 | 07-30 | Model 과 Paper 동시 렌더, Layout 비어 보임, Fit/좌표, 글자 거대, VP 순서 (6건) | C0060202 | VIEWPORT 미투영, minTextH 등 [Doc] | `cadSpaces.ts`, Viewer2D (뷰어) | be8d52f | **`수정 완료·검증됨`** (§4.1-②) | +| 3 | 07-30 | Brave/Firefox 에서 OLE BMP 미표시 | — | `` 가 data:image/bmp 미지원 [M] | 순수 JS BMP 디코더 [D] | 963a903 | §8 | +| 4 | 07-30 | OLE2FRAME 이 하드코딩된 좌표에 고정됨 | — | 정답 좌표 하드코딩 [D] | 파서가 준 ul/lr 사용 | 76f3e20 | §8 | +| 5 | 08-03 | Slug 텍스트 cap-height 약 26% 작음 | BasicSample | 3d3e052 의 회귀 [D] | 되돌림 | fb8f7ca | **`수정 완료·검증됨`** (§4.1-④) | +| 6 | 08-03 | 19MB DWG 로딩 시 탭 프리즈 | dwg_18.3mb | wasm 힙 O(n²) [M] | `parse_dwg_json` 스트리밍 | b047dc4 | **`수정 완료·검증됨`** (§4.1-③) | +| 7 | 08-03 | 도면 위아래 거대 회색 원 | dwg_18.3mb | `sample_arc` 각도 swap 등 [Doc] | 손상 호 감지 후 생략 (파서) | b047dc4 | **`수정 완료·검증됨`** (§4.1-③) | +| 8 | 08-04 | 레이어 토글 지연 | dwg_18.3mb | 토글마다 `load()` 재빌드 [M] | setDrawRange, `.visible` 토글 | 3480945 | **`수정 완료·검증됨`** (§4.1-⑦) | +| 9 | 08-04 | 노면/부체도로면(MESH) 미표시 | 대산당진 노면 | MESH 직렬화 누락, read_mesh 100k 상한 [M] | acadrust 패치 + 직렬화 + `_meshSegs` | eec3382, c67a803 | **`수정 완료·검증됨`** (§4.1-⑤) | +| 10 | 08-04 | 모든 선이 1px | 대산당진 노면 | lineWeight 누락, WebGL linewidth 무시 [M] | LineSegments2 버킷 | c67a803 | **`수정 완료·검증됨`** (§4.1-⑥) | +| 11 | 08-04~05 | 선가중치 배율 (8 → 20 → 11 px/mm) | 대산당진 노면 | 배율 [M] | 기본값 11, 옵션 패널 | cf12987, a924b1e, 1f80442 | 굵기 적용 확인 (§4.1-⑥). 배율 일치는 §8 | +| 12 | 08-04 | MPL-2.0 고지 누락 | — | 고지/원문/소스 경로 없음 [M] | NOTICES, `public/licenses/` | ce3859c | **조치 불충분**: 패치가 원본에 적용되지 않음 (§5 A-1) | +| 13 | 08-04 | `vite build` 가 dist2 테스트 파일을 삭제 | — | `emptyOutDir` 기본값 [M] | `--emptyOutDir false` 절차 문서화 | 6f200ef | — | + +분리 전 hmwebviewer 에서 처리한 것 (07-10~14, `PROGRESS.md`): +- solid HATCH 구멍, HIDDEN/CENTER 파선 무시 +- MAX_TEXTS 3000 상한 +- 회전 도곽 viewTwist, 로고 해치, 폴리라인 폭, MTEXT 배경 + +**미커밋 수정 2건** (`수정 완료·미검증`): +- VIEWPORT 안 SOLID/HATCH 채움에 model→paper 변환 추가 (`_fillXf`/`_bakeFillXf`) +- `_trimmedContentBox` 의 Z 붕괴 수정 + +### 4.1 수정 전 / 수정 후 / 현재 비교 캡처 + +**방법** +- 각 버전은 `git archive <커밋>` 으로 풀어서 실행했다. wasm 이 커밋마다 함께 커밋돼 있어서, 그 시점의 파서와 뷰어가 같이 재현된다. +- 계측용으로 두 가지만 덧붙였다. 둘 다 동작에는 영향이 없다. + - `main.ts` 에 `window.__viewer = viewer` 1줄 + - vite 캐시 경로 분리 +- "현재" 는 오늘의 작업 트리다. +- 로드 판정은 상태바가 `✓ <그 파일명> ·` 으로 바뀌었는지로 했다. +- 확대 화면은 모든 버전에서 같은 WCS 좌표와 화면 폭으로 카메라를 고정했다. 파일럿 캡처로 목표 객체가 화면 중앙에 오는 것을 먼저 확인했다. +- UI 패널은 숨기고 캔버스만 찍었다. 해상도 1280×800, RTX 3060. +- 24회 실행 모두 로드 성공, 콘솔 에러 0건. + +**① 한글 깨짐** (`civil.dwg`) + +수정 전 버전의 wasm 이 실제로 내보낸 문자열과, 수정 후 화면을 함께 비교했다: + +| handle | 수정 전 `95fa6a4` wasm 출력 | 수정 후 `be8d52f` | +|---|---|---| +| 3232403 | `Ãø Á¡` | `측 점` | +| 3232405 | `°èȹ°í` | `계획고` | +| 3232408 | `Áö¹Ý°í` | `지반고` | +| 3240601 | `õÀÇ1Áö±¸ °èȹ¼±` | `천의1지구 계획선` | +| 3241973 | `(»ç¾÷ ÁøÇà ¿¹Á¤)` | `(사업 진행 예정)` | +| 1009 | `°Ç¼³´Ü°è` | `건설단계` | + +| 수정 후 `be8d52f` | 현재 | +|---|---| +| ![](images/sample-test-2026-10-19/01-korean-2after.jpg) | ![](images/sample-test-2026-10-19/01-korean-3current.jpg) | + +**② Model/Paper 겹침, Layout 비어 보임** (`civil.dwg`, 로드 직후 기본 화면) + +| 수정 전 `95fa6a4` | 수정 후 `be8d52f` | 현재 | +|---|---|---| +| ![](images/sample-test-2026-10-19/02-model-paper-1before.jpg) | ![](images/sample-test-2026-10-19/02-model-paper-2after.jpg) | ![](images/sample-test-2026-10-19/02-model-paper-3current.jpg) | +| 점 3개만 보임. Model(x≈600k·162k)과 Paper(원점)를 한꺼번에 Fit 함 | Layout 이 열리고 VIEWPORT 내용이 투영됨 | 같음 (밝은 종이 배경) | + +**③ 대용량 로딩 + 거대 회색 원** (`dwg_18.3mb.dwg`, 로드 직후 기본 화면) + +| 수정 전 `7a6edd4` | 수정 후 `b047dc4` | 현재 | +|---|---|---| +| ![](images/sample-test-2026-10-19/03-large-hatch-1before.jpg) | ![](images/sample-test-2026-10-19/03-large-hatch-2after.jpg) | ![](images/sample-test-2026-10-19/03-large-hatch-3current.jpg) | +| 로드 **213.8초**. 회색 SOLID HATCH 가 도면 아래쪽 절반을 덮음 | **3.6초**, 회색 채움 없음 | **3.7초** | + +**④ Slug 텍스트 크기** (`BasicSample.dwg`, "정면도" h=140 주변 고정 화면) + +| 수정 전 `b047dc4` | 수정 후 `fb8f7ca` | 현재 | +|---|---|---| +| ![](images/sample-test-2026-10-19/04-slug-text-1before.jpg) | ![](images/sample-test-2026-10-19/04-slug-text-2after.jpg) | ![](images/sample-test-2026-10-19/04-slug-text-3current.jpg) | +| 글자 높이 **150px** | **202px** | **202px** | + +- 자홍색 픽셀 bbox 높이를 쟀다. 비율 150/202 = **0.743** 으로, 커밋 메시지 "3.6 → 2.68 (0.744)" 와 일치한다. +- 주변 선과 치수의 위치는 세 장 모두 같다. + +**⑤ SubDMesh(MESH) 미표시** (노면 도면, 연결부 (156542, 479804), 폭 160) + +| 수정 전 `6f200ef` | 수정 후 `c67a803` | 현재 | +|---|---|---| +| ![](images/sample-test-2026-10-19/05-mesh-1before.jpg) | ![](images/sample-test-2026-10-19/05-mesh-2after.jpg) | ![](images/sample-test-2026-10-19/05-mesh-3current.jpg) | +| 삼각망 없음 | 도로면·부체도로면 삼각망 표시 | 같음 | + +c67a803 에는 선가중치 수정도 함께 들어 있다. + +**⑥ 선가중치** (⑤와 같은 화면) + +| 수정 전 `eec3382` | 수정 후 `1f80442` (11 px/mm) | 현재 | +|---|---|---| +| ![](images/sample-test-2026-10-19/06-lineweight-1before.jpg) | ![](images/sample-test-2026-10-19/06-lineweight-2after.jpg) | ![](images/sample-test-2026-10-19/06-lineweight-3current.jpg) | +| 0.50mm 도로마킹 포함 모든 선 1px (메쉬 렌더러 이전 버전) | 굵게 표시 | 같음 | + +**⑦ 레이어 토글 속도** (`dwg_18.3mb.dwg`, `setHiddenLayers(CF-PATT)` 숨김→표시 3회, 동기 실행 시간) + +| | 수정 전 `fb8f7ca` | 수정 후 `3480945` | 현재 | +|---|---|---|---| +| 숨김 ms | 565 / 454 / 512 | 18 / 17 / 13 | 21 / 18 / 13 | +| 표시 ms | 501 / 622 / 609 | 26 / 5 / 6 | 35 / 5 / 5 | + +**새로 확인한 문제 (현재 버전)** + +| `%%%` 미변환 (C0060203) | CP949 기호 잔존 (civil) | Fit 잘림 (ELEVATION) | +|---|---|---| +| ![](images/sample-test-2026-10-19/n1-percent-3current.jpg) | ![](images/sample-test-2026-10-19/n2-cp949-symbol-3current.jpg) | ![](images/sample-test-2026-10-19/n3-fit-crop-3current.jpg) | +| `-2 %%%` (정답 `-2 %`) | `500¡¿350` (정답 `500×350`) | 왼쪽 잘림, 오른쪽 약 470px 공백 | + +--- + +## 5. 남은 과제 + +### A. 우선 처리 + +| # | 과제 | 근거 | +|---|---|---| +| A-1 | **MPL 패치가 실제 소스를 재현하지 못함** (`원인 확정`)
- `public/licenses/acadrust-0.4.1-read_mesh.patch` 를 원본 acadrust 0.4.1 crate 에 `git apply --check` 하면 `entities.rs:2350`(read_ole2frame)에서 실패한다.
- 고지의 "One file differs", "exact Source Code Form" 문구와 `acadrust-dwg/package.json` 의 "used unmodified" 가 사실과 다르다. | 이번 실행, `THIRD-PARTY-NOTICES.md` | +| A-2 | **Visio 저작권 데이터가 번들됨**
- `linData.ts` 의 `ICAD_LIN` 헤더가 "Copyright (c) 1998 by Visio Corporation. All rights reserved" 다.
- `linParser.ts:228` 이 import 될 때 등록하므로 배포본에 반드시 들어간다.
- IntelliCAD(ITC) 계약이 자사 웹 뷰어로의 외부 배포를 허용하는지 확인이 필요하다. | 이번 확인 | +| A-3 | 자체 `LICENSE` 파일 없음, NanumGothic OFL 원문 미동봉 | §6 | +| A-4 | 자동 판정 테스트 없음, e2e 고장 | §3.3 | +| A-5 | Fit 이 가장자리를 잘라냄 | §3.4-① | +| A-6 | 미커밋 WIP 정리, `src/topProgress.ts` 어디서도 import 되지 않음, 루트 대용량 untracked 파일 | `git status` | +| A-7 | 원본 hmwebviewer 에 Model/Paper 수정 미이식 (`cadSpaces.ts` 없음) | `model-paper-space.md` §9 | + +### B. 기능·품질 + +| # | 과제 | 근거 | +|---|---|---| +| B-1 | 해치 손상 호의 근본 원인 미확인. 현재 수정은 감지 후 생략. `spurious_r` 5배 임계값은 경험값 | `hatch-boundary-arc-artifact.md` §7 | +| B-2 | 파싱 중 메인 스레드 블로킹. 37MB 도면 로드 5.5초 동안 UI 정지. Web Worker 미적용 | 이번 측정 | +| B-3 | 메쉬 레이어에 굵기가 지정되면 fat 버킷 상한(40만) 124.5% 초과 → 버킷 전체 헤어라인 폴백 | `improvement-results` §5-3 | +| B-4 | `%%%` 등 제어코드, CP949 기호 | §3.4-②③ | +| B-5 | `tables.styles` 비어 있음 → 폰트 매핑 불가 | 한글 문서 §8, 이번 e2e | +| B-6 | 한글 처리가 JS 휴리스틱. 932/936/950 미지원 | 한글 문서 §8 | +| B-7 | Layout 탭 캡션이 내부명 | 이번 실행 | +| B-8 | acadrust notification 내용 비노출 | 이번 실행 | +| B-9 | 40만 상한 초과 시 사용자 알림 없음 (`console.warn` 만) | `lineweight-rendering.md` §6 | +| B-10 | wasm32 4GB 한계. 37MB 도면에서 Node RSS 1.37GB, 브라우저 JS heap 782MB | 이번 측정 | + +### C. 나중에 + +- 선가중치: 중첩 ByBlock 미추적, DXF LAYER 코드 370 무시, `celweight` 가 DWG 인덱스 그대로 나감 +- MESH: Catmull-Clark(`subdivisionLevel > 0`), POLYFACE/POLYGON MESH, shaded 패스 +- VIEWPORT: 솔리드 HATCH 근사, 비직교 VP, TILEMODE/CTAB 휴리스틱 +- OLE BMP 가 24/32bpp 만 지원 +- `parse_dwg` export 미정리 +- CXGLOGO-EX흑백.dwg 의 얇은 원 (hmwebviewer 시절부터) + +### D. 배포·문서 정리 + +- `build:dist2` 스크립트 없음, `dist2/assets` 에 예전 해시 번들 누적, 테스트 도면이 `dist2/samples` 에 섞여 있음 +- 문서 상태 표기가 낡음: + - `subdmesh-rendering.md`: "렌더러 미커밋" + - `lineweight-rendering.md`, `improvement-results`: "미푸시" + - 여러 문서: "acadrust 미수정" +- `Viewer2D.d.ts:24`, `Viewer2D.js:1389` 주석은 "Default 20" 인데 실제 기본값은 11 +- README 이슈 표에 hatch, large-dwg 문서 누락 + +--- + +## 6. 라이브러리 배포 가능성 + +### 6.1 결론: 조건부 가능 + +- 파서는 DOM 없이 동작한다. Node 24 에서 `initSync` 로 19개 도면을 파싱해 실측했다. +- Viewer2D 가 DOM 에 닿는 곳은 생성자로 받는 `container` 하나뿐이다. +- 다만 아래 차단 요소를 먼저 풀어야 한다. + +### 6.2 차단 요소 + +| # | 문제 | 위치 | 영향 | +|---|---|---|---| +| L1 | MPL 패치로 소스 재현 불가 | §5 A-1 | 법적 의무 미충족 | +| L2 | Visio 저작권 lin 데이터 | `linData.ts:2` | 재배포 권리 확인 필요 | +| L3 | 자체 `LICENSE`, `package.json` `license` 필드 없음. `Viewer2D.js:3` 에 "Ported from webviewer/…" | 루트 | 사용 권리 불명확 | +| L4 | NanumGothic OFL 원문 미동봉 | `public/licenses/` | 폰트 동봉 시 위반 | +| B1 | `?url` import 가 Vite 전용 | `acadrustParser.ts:6` | Node·webpack·SSR 에서 실패. Vite lib 모드로 빌드하면 wasm 이 base64 로 두 번 인라인됨 (실측 2.37MB) | +| B2 | 확장자 없는 상대 import | `dwgParser.ts:16`, `parseDxf.ts:7`, `Viewer2D.js:16-18` 외 | Node ESM 에서 `ERR_MODULE_NOT_FOUND` (§3.3 과 같은 원인) | +| B3 | 폰트 경로 `/fonts/…` 하드코딩, 전역 싱글톤 | `slugText.ts:32-49`, `Viewer2D.js:127` | 호스트에 그 경로가 없으면 404 | +| B4 | `dispose()` 없음 | `Viewer2D.js:159,164,4527` | 익명 window 리스너와 rAF 루프를 해제할 수 없음. F 키 리스너는 입력란에서도 Fit 실행 | +| B5 | 타입 불완전 | `Viewer2D.d.ts` | `load(result: unknown)`, 일부 메서드 누락, 기본값 주석 20 ≠ 실제 11 | +| B6 | 패키지 설정 없음 | `package.json` | `private: true`, `exports`/`files`/`peerDependencies` 없음 | +| B7 | 소스 정본이 여러 곳 | 이 레포 / `hmwebviewer/src/viewer2d` / `hmwebviewer/packages/dwg-dxf-viewer` | wasm 바이너리 3개가 모두 다름. `Viewer2D.js` 는 hmwebviewer 와 1,217줄 차이 | + +### 6.3 권장 구조: 패키지 2개 + +``` +@scope/cad-parser (headless, three 없음) license: "MIT AND MPL-2.0" + initDwgParser({ wasm?: URL | bytes | Module }) + parseDwg(bytes) / parseDxf(buf) / parseCad(buf, nameHint?) + listCadSpaces, pickDefaultSpace, isDwg/isDxf, CadParseResult 타입 + ./worker (선택: 메인 스레드 비블로킹) + +@scope/viewer2d (Viewer2D, slugText, linParser) license: "MIT" + peerDependencies: three ^0.185.0 ← peer + external + dependencies: @scope/cad-parser, opentype.js +``` + +파서 패키지 `package.json` 핵심: + +```json +{ + "type": "module", + "license": "MIT AND MPL-2.0", + "exports": { + ".": { "types": "./dist/index.d.ts", "node": "./dist/node.js", "default": "./dist/browser.js" }, + "./worker": "./dist/worker.js", + "./acadrust_dwg_bg.wasm": "./dist/acadrust_dwg_bg.wasm" + }, + "files": ["dist", "licenses", "THIRD-PARTY-NOTICES.md", "LICENSE"], + "sideEffects": false +} +``` + +wasm 로딩: +1. **기본**: glue 의 `new URL('./acadrust_dwg_bg.wasm', import.meta.url)` 패턴을 유지한다. +2. **탈출구**: `init({ wasm })` 로 URL, bytes, Module 을 직접 넘길 수 있게 한다. +3. **Node**: `initSync({ module: readFileSync(...) })`. `scripts/parse-dwg.mjs` 에서 동작을 확인했다. + +### 6.4 작업 순서 + +1. 라이선스 L1~L4 해결 +2. 정본 레포 통일, `rust/dwg-wasm` 과 vendored acadrust 이전, `rust-toolchain.toml` 로 툴체인 고정 +3. 파서 분리: `?url` 제거, `init({ wasm })` 추가, Node 진입점, `parseCad()`, 상대 import 에 `.js` +4. Viewer2D: 생성자 옵션(`fontUrl`, `keyboard` 등), `dispose()`, `ResizeObserver`, `localStorage` 잔재 제거, 콜백 해제 함수 +5. 타입: 수기 `Viewer2D.d.ts` 보완, `CadParseResult` 구체화 +6. 빌드: tsup/rollup/tsc 로 ESM, wasm 은 파일로 복사, `opentype.js` 는 default import (Node ESM 에서 named import 실패 실측) +7. 검증: 소비 예제(Vite, webpack 5, Node)를 CI 로 돌리고, §3 의 샘플 20개 e2e 를 회귀 테스트로 + +### 6.5 유의할 점 + +- **크기** + + | 파일 | raw | brotli | + |---|---|---| + | wasm | 884KB | 301KB | + | 폰트 | 2.05MB | 432KB | + + 폰트는 번들하지 말고 `fontUrl` 옵션으로 받는다. +- **메모리**: 37MB 도면에서 Node RSS 1.37GB, 브라우저 JS heap 782MB. wasm32 상한은 4GB 다. +- **런타임**: CSP 에 `'wasm-unsafe-eval'` 이 필요하다. `application/wasm` MIME 이 아니면 느린 폴백으로 떨어진다. +- **한글**: `cp949`/`euc-kr` TextDecoder 가 없는 런타임에서는 `fixDwgKoreanText` 가 아무 표시 없이 보정을 건너뛴다. +- **버전 정책**: 두 패키지는 lockstep 으로 올린다. `CadParseResult` 필드 추가는 minor, 삭제나 변경은 major. +- **upstream drift**: README 의 "본가에서 다시 복사" 절차를 따르면 이 레포의 기능(lineweight, 2.5D, Model/Layout)이 지워진다. +- **acadrust 버전**: 상류는 0.6.x 다. 0.4.1 fork 유지 여부를 결정해야 한다. +- **릴리스**: 깨끗한 태그에서만 배포한다. + +--- + +## 7. 재현 방법 + +```powershell +# 헤드리스 파싱 (1개 파일) +node scripts/parse-dwg.mjs "" [out.json] + +# 기존 e2e (현재는 실패, §3.3) +node e2e_lin_test.cjs +``` + +배치 파서, 브라우저 e2e, 3단 비교 캡처 스크립트는 세션 임시 폴더에 있다. 레포 반영은 §2-3. 측정 원본은 `images/sample-test-2026-10-19/results.json` 에 있다. + +## 8. 검증 불가 목록 + +| 항목 | 이유 | +|---|---| +| OLE BMP(#3), OLE2FRAME(#4) | OLE2FRAME 엔티티가 있는 샘플이 없음 | +| 선가중치 배율(#11)이 AutoCAD 와 일치하는지 | 같은 위치의 AutoCAD 정답 캡처가 없음 | +| 한글 깨짐(#1) 수정 전 화면 | 같은 커밋에서 고친 '글자 거대' 버그 때문에 판독 불가. 문자열 비교로 대신 검증함 | +| 수정 전 수치와 문서 기록의 차이 (로드 213.8s vs 문서 354s, 토글 약 0.5s vs 커밋 "5초 이상") | 당시 측정 환경과 방법을 알 수 없음 | +| Fit 잘림의 원인 | 재현만 확인, 원인 미확정 | +| H-DICHL06 의 `patternLen`(1) 과 요소 합(1.588) 중 어느 쪽이 AutoCAD 기준인지 | AutoCAD 대조 수단 없음 | +| 문서상 한글 재현 도면 `C0060203-002-굴착및시공순서도.dwg` | 레포에 없음 | +| 7~8월 테스트 당시의 증상 원문 | 세션 기록이 남아 있지 않음 (가장 오래된 기록이 09-16) | diff --git a/scripts/parse-dwg.mjs b/scripts/parse-dwg.mjs new file mode 100644 index 0000000..c042e4a --- /dev/null +++ b/scripts/parse-dwg.mjs @@ -0,0 +1,44 @@ +#!/usr/bin/env node +/** + * Parse a DWG in Node — no browser, no Viewer2D. Same acadrust wasm and + * Korean-text fixup as src/viewer2d/dwgParser.ts. + * + * Usage: + * node scripts/parse-dwg.mjs # print summary + * node scripts/parse-dwg.mjs out.json # also write full parseResult + * + * Needs Node >= 22.18 (imports fixDwgKoreanText.ts via built-in type stripping). + */ +import { readFileSync, writeFileSync } from 'node:fs'; +import { initSync, parse_dwg_json } from '../src/viewer2d/acadrust-dwg/acadrust_dwg.js'; +import { fixDwgKoreanText } from '../src/viewer2d/fixDwgKoreanText.ts'; + +const [inPath, outPath] = process.argv.slice(2); +if (!inPath) { + console.error('usage: node scripts/parse-dwg.mjs [out.json]'); + process.exit(1); +} + +const wasmUrl = new URL('../src/viewer2d/acadrust-dwg/acadrust_dwg_bg.wasm', import.meta.url); +initSync({ module: readFileSync(wasmUrl) }); + +const t0 = performance.now(); +const result = fixDwgKoreanText(JSON.parse(parse_dwg_json(readFileSync(inPath)))); +const ms = performance.now() - t0; + +const entities = result.entities ?? []; +const byType = {}; +for (const e of entities) byType[e.type] = (byType[e.type] ?? 0) + 1; + +console.log(`file : ${inPath}`); +console.log(`version : ${result.version}`); +console.log(`parse : ${ms.toFixed(0)} ms`); +console.log(`entities : ${entities.length}`); +console.table(Object.entries(byType).sort((a, b) => b[1] - a[1]).map(([type, count]) => ({ type, count }))); +console.log(`tables : ${Object.keys(result.tables ?? {}).join(', ')}`); +if (result.stats) console.log('stats :', result.stats); + +if (outPath) { + writeFileSync(outPath, JSON.stringify(result, null, 2)); + console.log(`wrote : ${outPath}`); +}