프로젝트 소개
Pretext는 여러 줄 텍스트를 측정하고 줄 레이아웃을 계산하기 위한 순수 JavaScript/TypeScript 라이브러리입니다. `getBoundingClientRect`나 `offsetHeight`처럼 레이아웃 리플로우를 강제하는 DOM 측정 API를 피하고, 대신 브라우저의 canvas `measureText`를 기준값으로 삼아 자체 측정을 수행합니다. 따라서 가상화, 사용자 정의 레이아웃 엔진, canvas/SVG 렌더링, 개발 시점의 오버플로 검사에 유용합니다.
## 설치
```sh
npm install @chenglou/pretext
```
## 주요 사용 사례
### 1. DOM을 건드리지 않고 문단 높이 측정
```ts
import { prepare, layout } from '@chenglou/pretext'
const prepared = prepare('AGI 春天到了. بدأت الرحلة 🚀', '16px Inter')
const { height, lineCount } = layout(prepared, 320, 20)
```
`prepare()`는 일회성 작업을 수행합니다: 공백 정규화, 텍스트 분할, glue 규칙 적용, canvas 기반 측정입니다. 그런 다음 `layout()`은 캐시된 너비에 대한 저렴한 순수 산술 연산이므로, 다시 prepare하지 않고도 리사이즈 시 재실행할 수 있습니다.
`prepare()`의 옵션에는 textarea와 유사한 동작을 위한 `whiteSpace: 'pre-wrap'`, CSS의 `word-break: keep-all`과 유사한 `wordBreak: 'keep-all'`, CSS `letter-spacing`을 위한 `letterSpacing`이 있습니다.
### 2. 수동 줄 레이아웃
`prepareWithSegments()`는 사용자 정의 레이아웃을 위한 더 풍부한 구조를 반환합니다. 다음 API를 사용할 수 있습니다:
- `layoutWithLines()` — 고정된 최대 너비에서 모든 줄을 반환하며, 텍스트와 측정된 너비를 포함합니다.
- `walkLineRanges()` — 줄 문자열을 만들지 않고, 너비와 시작/끝 커서를 사용해 줄마다 콜백을 호출합니다.
- `measureLineStats()` — 할당 없이 줄 수와 가장 넓은 줄 너비를 반환합니다.
- `measureNaturalWidth()` — 너비가 줄바꿈의 원인이 아닐 때 가장 넓은 강제 줄을 반환합니다.
- `layoutNextLine()` / `layoutNextLineRange()` — 잠재적으로 서로 다른 너비로 한 번에 한 줄씩 레이아웃하기 위한 iterator 스타일 API로, float 주변으로 텍스트를 흘리거나 동적 컨테이너에 유용합니다.
- `materializeLineRange()` — 레이아웃 범위를 다시 전체 줄 문자열로 변환합니다.
이를 통해 Canvas, SVG, WebGL, 그리고 향후 서버 측 환경으로 렌더링할 수 있습니다. 데모는 저장소와 chenglou.me/pretext에 포함되어 있습니다.
## 리치 인라인 헬퍼
`@chenglou/pretext/rich-inline`의 별도 헬퍼는 혼합 폰트, 원자적 항목(예: 칩과 멘션), pill chrome을 위한 호출자 소유의 추가 너비를 갖는 기본 리치 텍스트 인라인 흐름을 지원합니다. 의도적으로 범위가 좁습니다: 인라인 전용, `white-space: normal`만 지원하며, 일반적인 CSS 인라인 포매팅 엔진이 아닙니다.
## API 용어집 하이라이트
- `PreparedText`는 불투명한 빠른 경로 핸들이고, `PreparedTextWithSegments`는 더 풍부한 수동 레이아웃 핸들입니다.
- `LayoutCursor`는 원시 문자열 오프셋이 아니라 세그먼트/그래핌 인덱스를 사용합니다.
- 빈 문자열에 대한 `layout()`은 `{ lineCount: 0, height: 0 }`을 반환합니다. 브라우저는 빈 블록을 하나의 `line-height`로 크기 지정하므로, 호출자가 clamp하고 싶을 수 있습니다.
- 소프트 하이픈이 지원됩니다: 선택적 줄바꿈 지점으로 작동하며, 선택되면 후행 `-`로 materialize됩니다.
- `clearCache()`는 공유 내부 캐시를 지우고, `setLocale()`은 향후 prepare 호출의 로케일을 설정합니다.
- 더 풍부한 핸들은 사용자 정의 bidi 인식 렌더링을 위한 대략적인 `segLevels`를 포함하지만, Pretext는 전체 Unicode Bidirectional Algorithm을 구현하지 않습니다.
## 주의사항
Pretext는 완전한 폰트 렌더링 엔진이 아닙니다. 일반적인 CSS 텍스트 설정을 대상으로 합니다:
- `white-space: normal` 및 `pre-wrap`
- `word-break: normal` 및 `keep-all`
- `overflow-wrap: break-word`
- `line-break: auto`
- 숫자 픽셀 값으로서의 `letter-spacing`
- 탭은 기본 `tab-size: 8`을 따릅니다
주목할 만한 제한사항:
- `system-ui`와 `-apple-system`은 macOS에서 `layout()` 정확도에 안전하지 않습니다. 명명된 폰트를 권장합니다.
- 구두점 옆의 이모지는 브라우저와 다르게 줄바꿈될 수 있습니다.
- Shantell Sans 같은 일부 폰트는 긴 단어에서 다른 줄바꿈을 만들 수 있습니다.
- 런타임에는 `Intl.Segmenter`, Canvas 2D 텍스트 측정, Unicode property escapes가 필요합니다.
- canvas `font` 단축 속성 밖의 CSS 폰트 기능은 별도로 모델링되지 않습니다.
전체적으로 Pretext는 여러 줄 텍스트 측정을 위한 집중된 라이브러리로 자리매김하며, 웹 개발자에게 브라우저 레이아웃 쿼리에 대한 빠르고 결정론적인 대안을 제공합니다.
Comments
0 Rating appears after 10 ratings
Sign in to join the discussion.