diff options
| author | eunhhu <[email protected]> | 2026-09-28 14:49:05 +0100 |
|---|---|---|
| committer | eunhhu <[email protected]> | 2026-09-28 14:49:05 +0100 |
| commit | 60a3c0232bbca8d5d8c1107ab8197d272212ef42 (patch) | |
| tree | ba7b4bad98b79ad1abd40cdb038c493d50df0c19 /docs | |
| parent | c23b79f1cb599ee7766002ad23756643e72113e5 (diff) | |
| download | vega-60a3c0232bbca8d5d8c1107ab8197d272212ef42.tar.gz vega-60a3c0232bbca8d5d8c1107ab8197d272212ef42.zip | |
Shape the nine Indic scripts as HarfBuzz does
The Indic shaper, from HarfBuzz 10.2's source: syllables found by the
Indic machine; each syllable's base consonant, its pre-base matras and
reph moved around it and masked for the basic features, applied one at
a time within the syllable; then matras, reph and pre-base-reordering
consonants placed again by what the features made. Vowel constraints,
Indic decompositions and compositions, dotted circles for broken
syllables, and per-syllable matching come with it.
Plans carry GSUB pauses (steps where the shaper does its part) and the
stages of the features the shaper asks whether they would substitute;
the runtime answers with each subtable's would_apply, as HarfBuzz does.
Masks are now the common ones and each shaper's own.
text/syllabic.py reads HarfBuzz's Ragel grammars (Indic, Khmer,
Myanmar) and compiles each to a minimal DFA with Ragel's scanner
semantics; beside the machines Ragel generated from the same grammars,
100,000 random runs each find no syllable that differs. It also reads
HarfBuzz's Indic table (Unicode 16) cell for cell.
Every installed font shapes as HarfBuzz does (213 fonts, 1,688 plans);
the Indic ones at 20,000 runs a plan (FreeSerif's eight scripts,
FreeSans, Unifont's nine without GSUB), and nine new-spec Noto Sans
fonts at 5,000.
Diffstat (limited to 'docs')
| -rw-r--r-- | docs/design/004-text.md | 39 |
1 files changed, 38 insertions, 1 deletions
diff --git a/docs/design/004-text.md b/docs/design/004-text.md index 6c8e09e..065c4db 100644 --- a/docs/design/004-text.md +++ b/docs/design/004-text.md @@ -129,11 +129,48 @@ HarfBuzz와 다른 파일은 만들어지지 않는다. `vega-symbols build --sh 않았다. Unifont의 히브리는 HarfBuzz보다 2.3배 빠르다(문자당 116 대 272 ns). HarfBuzz는 부호마다 CFF 외곽을 풀어 상자를 재고, Vega는 빌드할 때 잰 상자를 읽는다. +### 인도계 문자(데바나가리, 벵골, 구르무키, 구자라트, 오리야, 타밀, 텔루구, 칸나다, 말라얄람) + +HarfBuzz의 인도계 셰이퍼는 음절 단위로 일한다. 음절을 찾고(Ragel로 짠 문법), 음절마다 기저 자음을 찾아 그 앞뒤로 모음 부호와 +reph를 옮기고, 기본 기능(nukt, akhn, rphf, rkrf, pref, blwf, abvf, half, pstf, vatu, cjct)을 하나씩 음절 안에서만 적용한 뒤, +그 결과를 보고 모음 부호와 reph와 앞으로 가는 자음을 다시 옮긴다. 그 사이사이가 GSUB의 멈춤(pause)이다. + +- 계획: 멈춤은 조회 목록 안의 표지(`VEGA_STEP_PAUSE`와 멈춤 번호)로 싣는다. 셰이퍼가 "이 기능이 이 글리프들을 바꾸겠는가"를 + 묻는 기능(rphf, pref, blwf, pstf, vatu)은 그 단계의 조회 범위를 계획에 싣고, 실행 중에 조회의 하위 표를 문맥 없이 대어 본다 + (`would_apply`, 형식마다 HarfBuzz의 판단 그대로). 옛 규격(deva)과 새 규격(dev2)은 글꼴이 고른 문자 체계 태그로 가른다. +- 음절 기계: [text/syllabic.py](../../text/syllabic.py)가 HarfBuzz의 `.rl` 문법(인도계, 크메르, 미얀마)을 그대로 읽어 정규식에서 + NFA, DFA를 만들고 최소화한다. Ragel 스캐너의 뜻(각 위치에서 가장 긴 일치, 같으면 먼저 적은 패턴)을 따른다. 같은 문법으로 + Ragel이 만든 HarfBuzz의 기계(`hb-ot-shaper-*-machine.hh`)를 해석해 무작위 범주열 10만 개씩 대 보면 세 기계 모두 음절 경계와 + 종류가 하나도 다르지 않다(`--check`). 문자마다의 범주와 자리는 HarfBuzz 10.2의 `hb-ot-shaper-indic-table.cc`를 칸마다 읽는다 + (유니코드 16 기준이라 여기의 15.1 UCD에서 만들지 않는다). +- 실행: 모음 제약(다른 모음처럼 읽히는 모음 연쇄에 점선 원), 인도계 분해·합성(몇 글자는 분해하지 않고, 쪼갠 모음 부호는 다시 + 합치지 않는다), 음절 찾기, 초기 재배치(기저 찾기, 자리순 안정 정렬, 마스크), 깨진 음절의 점선 원, 음절 안에서만 맞추는 + 조회(`F_PER_SYLLABLE`), 최종 재배치(pref가 만든 기저, 말라얄람의 아래 형태, 모음 부호와 reph와 pref 자음의 자리, 단어 + 첫머리의 init). + +HarfBuzz 10.2의 원본을 기준으로 삼았다. 처음에는 rustybuzz 0.20(HarfBuzz 10.0.1을 옮긴 Rust)을 보고 옮겼는데, 설치된 +HarfBuzz 10.2와 대 보니 넷이 달랐다. rustybuzz가 틀린 것 둘: 새 규격에서 would-substitute를 문맥 없이 묻는 조건이 뒤집혀 +있고, 기저를 찾을 때 미얀마의 자음 집합을 쓴다. 10.0.1 뒤에 HarfBuzz가 바꾼 것 둘: 위첨자 2·3(U+00B2·00B3)이 SMPst라는 새 +범주가 됐고(문법에 `sm = SM | SMPst`), 텔루구 길이 부호의 수정 결합 등급이 0이 아니라 4·5다. 넷 모두 HarfBuzz 원본대로 고쳤다. +다섯째는 계획기에 있었다. HarfBuzz는 단계마다 조회를 번호로 정렬한 뒤 같은 조회를 합치는데, 합칠 때 음절 제한과 무작위 +깃발은 앞선 쪽의 것을 남긴다. 그 정렬이 자체 퀵정렬(hb_qsort)이라 안정적이지 않고, 앞뒤는 그 알고리즘이 정한다. Noto Sans +Malayalam은 psts와 rlig가 한 조회를 나눠 쓰는데, 안정 정렬로는 psts 쪽(음절 안에서만)이 남아 음절을 넘는 합자(ma와 결합 +점)가 생기지 않았다. 계획기가 같은 퀵정렬을 그대로 쓰게 했다. + +결과(Pi 5): + +- 설치된 글꼴 213벌, 계획 1,688개에 각 1천 실행: 다른 실행 0(글자 모양이 비트맵뿐인 Noto Color Emoji는 심볼 단계에서 + 빠진다). 인도계가 있는 글꼴은 따로 계획마다 2만 실행: FreeSerif(여덟 문자), FreeSans, 굵은 꼴들, FreeMono, Unifont(아홉 + 문자, GSUB 없음) 130개 계획 260만 실행, 다른 실행 0. +- 새 규격 글꼴(dev2 등): 인터넷에서 받은 Noto Sans 아홉 벌(데바나가리부터 말라얄람까지), 계획마다 5천 실행, 다른 실행 0. +- 속도: HarfBuzz의 1.25–1.3배 시간(Noto Sans Devanagari 문자당 1,614 대 1,235 ns, Tamil 674 대 526 ns). + 받지 않는 것(빌드 도구가 이유를 말하고 거부한다): - `kern` 표의 상태 기계(Apple 형식 1), 등급 형식(2, 3), 교차 방향 커닝. 설치된 글꼴의 `kern` 표는 모두 짝 형식(0)이다. - AAT 표(morx, kerx, trak), 늘이기(stch), 아랍 형태가 없어 HarfBuzz가 표시형으로 대체 셰이핑을 하는 글꼴(Unifont의 아랍). -- 기본·아랍·히브리·한글 셰이퍼가 아닌 문자 체계: 인도계, 태국·라오, 크메르, 미얀마, USE 문자 체계들, 시리아. +- 기본·아랍·히브리·한글·인도계 셰이퍼가 아닌 문자 체계: 태국·라오, 크메르, 미얀마, USE 문자 체계들(싱할라, 티베트, 자바 + 등), 시리아. - 이체 선택자(cmap 형식 14)는 싣지 않는다. 선택자는 숨기고 앞 글자는 기본 글리프로 둔다. ## 비례폭 글줄 |
