Rich Document Foundation — Phase 0 완료
LUTable의 Document Surface가 진짜 리치 텍스트를 다루려면, 캔버스 위에서 글자 한 자 한 자를 직접 배치해야 합니다. 그 토대가 되는 텍스트 엔진을 단일 레이아웃 월드로 통합한 이야기입니다.
왜 텍스트가 어려운가
일반 웹 앱이라면 텍스트는 브라우저가 알아서 그립니다. 하지만 LUTable의 Document와 Canvas는 WebGPU 표면 위에 직접 그려집니다. 그 말은 줄바꿈, 글자 간격, 폰트 fallback, 결합 문자, 양방향 텍스트까지 전부 우리가 계산해야 한다는 뜻입니다.
초기에는 Canvas의 텍스트 객체와 Document의 본문 텍스트가 서로 다른 코드 경로로 레이아웃되고 있었습니다. 같은 "글자를 배치한다"는 문제를 두 번 푸는 셈이라, 버그도 두 배, 일관성도 깨졌습니다.
단일 layout_world
Phase 0의 목표는 명확했습니다. 모든 텍스트 레이아웃을 하나의 layout_world로 통일한다. Canvas의 텍스트 객체든 Document의 문단이든, 결국 같은 입력(스타일이 적용된 글자 run의 시퀀스)을 받아 같은 출력(배치된 글리프 위치)을 내놓습니다.
- styled-run 레이아웃 — 한 문단 안에서 run마다 폰트, 색, 크기, 줄 높이가 다를 수 있습니다. 레이아웃 엔진은 run 경계를 인식하면서 한 줄로 흐르게 합니다.
- per-codepoint 폰트 fallback — 한글·라틴·이모지·CJK가 한 줄에 섞여도, 각 코드포인트가 표시 가능한 폰트를 찾아 떨어집니다. 두부(□)가 뜨지 않게.
- per-line height — 줄마다 가장 큰 글자에 맞춰 줄 높이가 정해집니다. 큰 제목과 작은 주석이 한 문단에 있어도 안정적입니다.
성능: LRU 캐시
가벼움은 LUTable의 전제입니다. 텍스트 레이아웃은 매 프레임 다시 계산하면 비싸므로, 레이아웃 결과를 LRU 캐시에 담습니다. 같은 문단이 바뀌지 않으면 캐시 히트. 편집 중인 줄만 다시 계산합니다.
여기에 CRLF strip, 빈 줄 처리, 탭 정규화 같은 자잘한 입력 정리도 한 곳으로 모았습니다. 입력이 한 곳에서 정리되니, 위에서 도는 모든 기능이 같은 가정 위에 설 수 있습니다.
다음은
Phase 0은 토대입니다. 이 위에 Phase 1에서 인라인 스타일 편집(굵게/기울임/링크), 블록 구조(헤딩/리스트/인용), 그리고 셀이 Document 본문을 읽고 변형하는 명령 경로가 올라갑니다. 텍스트가 한 엔진으로 모였으니, 이제 그 위에 기능을 쌓는 일은 훨씬 가벼워졌습니다.