- Pretext는 DOM 접근 없이 멀티라인 텍스트의 높이와 줄 배치를 계산하는 순수 JavaScript/TypeScript 라이브러리로, 브라우저와 서버 환경 모두 지원
- getBoundingClientRect 같은 DOM 측정 API를 사용하지 않아 레이아웃 리플로우 비용을 제거하고, 폰트 엔진 기반 자체 측정 로직으로 정확도를 확보
- prepare() / layout() API를 통해 텍스트를 전처리하고, 캐시된 폭 데이터를 이용해 순수 산술 연산으로 빠른 높이 계산 수행
- 이모지, 혼합 방향 텍스트(bidi), 다양한 언어를 지원하며, Canvas·SVG·WebGL·서버 렌더링에서도 동일한 결과 제공
- 가상화 스크롤, 텍스트 오버플로 검증, 플로팅 텍스트 배치 등 정밀한 UI 레이아웃 구현에 활용 가능한 고성능 텍스트 엔진임
개요
- Pretext는 멀티라인 텍스트 측정과 레이아웃을 위한 순수 JavaScript/TypeScript 라이브러리로, DOM, Canvas, SVG, 그리고 서버 사이드 렌더링까지 지원
-
DOM 측정 API(
getBoundingClientRect,offsetHeight등)를 사용하지 않아 레이아웃 리플로우 비용을 제거함 - 브라우저의 폰트 엔진을 기준으로 한 자체 측정 로직을 통해 정확하고 빠른 성능을 제공
- 모든 언어, 이모지, 혼합 방향 텍스트(bidi) 를 지원하며, 브라우저별 차이도 처리
설치 및 데모
- 설치:
npm install @chenglou/pretext - 로컬 실행:
bun install후bun start로/demos디렉터리 열기 - 온라인 데모: chenglou.me/pretext 및 somnai-dreams.github.io/pretext-demos
주요 기능
- Pretext는 두 가지 주요 사용 방식을 제공
-
1. DOM 접근 없이 문단 높이 측정
-
prepare()는 텍스트를 전처리하고, 공백 정규화·세그먼트 분리·glue 규칙 적용·canvas 기반 측정을 수행해 불투명 핸들(opaque handle) 을 반환 -
layout()은 캐시된 폭 데이터를 이용해 순수 산술 연산으로 높이와 줄 수 계산 - 동일한 텍스트와 설정에서는
prepare()를 반복 호출하지 않고, 리사이즈 시에는layout()만 다시 실행 -
{ whiteSpace: 'pre-wrap' }옵션으로 공백, 탭(\t), 줄바꿈(\n)을 그대로 유지 - 벤치마크 결과:
prepare()약 19ms (500개 텍스트 기준),layout()약 0.09ms - 반환된 높이 값은 다음과 같은 UI 기능에 활용 가능
- 가상화 및 오클루전 처리에서 정확한 높이 계산
- JS 기반 레이아웃 시스템(예: masonry, flexbox 유사 구조)
- AI 기반 텍스트 오버플로 검증
- 텍스트 로드 시 스크롤 위치 유지
-
-
2. 수동 문단 레이아웃 구성
-
prepareWithSegments()로 세그먼트 단위 데이터 생성 -
layoutWithLines()는 고정 폭에서 각 줄의 텍스트와 폭 정보를 반환 -
walkLineRanges()는 텍스트 문자열을 만들지 않고 각 줄의 폭과 커서 범위를 순회- 예: 여러 폭을 테스트해 적절한 줄 수와 높이를 찾는 이진 탐색형 레이아웃 조정 가능
-
layoutNextLine()은 줄마다 폭이 달라지는 경우 한 줄씩 순차적으로 레이아웃- 예: 이미지 주위로 텍스트를 흐르게 하는 플로팅 텍스트 배치
- 이 방식은 Canvas, SVG, WebGL, 서버 사이드 렌더링에도 동일하게 적용 가능
-
API 요약
-
기본 측정용 API
-
prepare(text, font, options?): 텍스트 분석 및 측정,layout()에 전달할 핸들 반환 -
layout(prepared, maxWidth, lineHeight): 주어진 폭과 줄 높이에 따른 텍스트 높이와 줄 수 계산
-
-
수동 레이아웃용 API
-
prepareWithSegments(text, font, options?): 세그먼트 단위 데이터 반환 -
layoutWithLines(prepared, maxWidth, lineHeight): 각 줄의 텍스트, 폭, 커서 정보 포함 -
walkLineRanges(prepared, maxWidth, onLine): 각 줄의 폭과 커서 범위를 콜백으로 전달 -
layoutNextLine(prepared, start, maxWidth): 줄 단위 반복자 형태로 레이아웃 수행 -
LayoutLine,LayoutLineRange,LayoutCursor타입 정의 포함
-
-
기타 유틸리티
-
clearCache(): 내부 캐시 초기화 -
setLocale(locale?): 로케일 설정 및 캐시 초기화 (기존 상태에는 영향 없음)
-
제약 및 주의사항
- Pretext는 완전한 폰트 렌더링 엔진은 아님
- 기본 대상 CSS 속성
-
white-space: normal -
word-break: normal -
overflow-wrap: break-word -
line-break: auto
-
-
{ whiteSpace: 'pre-wrap' }사용 시 공백, 탭, 줄바꿈 유지하며tab-size: 8적용 - macOS에서
system-ui폰트는layout()정확도에 부적합하므로 명시적 폰트명 사용 권장 -
overflow-wrap: break-word로 인해 매우 좁은 폭에서는 단어 내부에서도 줄바꿈 가능, 단 문자 단위(grapheme) 기준으로만 분리
개발 관련
- 개발 환경 및 명령은
DEVELOPMENT.md참고
기여 및 배경
- Sebastian Markbage의 text-layout 프로젝트에서 아이디어를 이어받음
-
canvas
measureText기반 셰이핑, pdf.js의 bidi 처리, 스트리밍 라인 브레이킹 설계를 계승해 발전시킨 구조