mirror of
https://github.com/emilk/egui.git
synced 2026-09-02 23:00:04 -04:00
Integrate harfrust for text shaping (#8031)
* Related to #56 (Improve text — tracking issue) ## Summary This PR integrates [harfrust](https://crates.io/crates/harfrust) (a pure-Rust port of HarfBuzz) into epaint's text layout pipeline, replacing the character-by-character glyph positioning with proper OpenType text shaping. ### What this enables - **GPOS kerning**: most modern fonts only ship kerning in GPOS tables (not the legacy `kern` table). Pairs like "AV", "VA", "AT" are now properly tightened. - **GSUB substitutions**: ligatures (fi, fl), contextual alternates, and other OpenType features. - **Combining marks**: diacritics (e.g. ɔ̃) are positioned via anchor tables instead of being rendered as standalone replacement glyphs. ### Before/After #### Kerning, etc. <img width="838" height="726" alt="before_main" src="https://github.com/user-attachments/assets/f0f26d5f-b117-43a6-b39c-ea40d2e73836" /> <img width="838" height="726" alt="after_harfrust" src="https://github.com/user-attachments/assets/d983e5da-486c-4f39-bd4f-5782a90c6b39" /> #### Ligatures <img width="1117" height="698" alt="before_closeup" src="https://github.com/user-attachments/assets/7a3b08b4-cf6f-45b7-98ba-07c473cd3b02" /> <img width="1117" height="698" alt="after_closeup" src="https://github.com/user-attachments/assets/6cfc5f21-d32f-4f09-be0c-59c8c553d44f" /> ### Architecture The shaping integrates into the existing pipeline without changing the public API: 1. **`Font::segment_into_runs`** — segments text into contiguous runs by font face (grapheme-cluster aware, never splits combining sequences) 2. **`FontFace::shape_text`** — calls harfrust to shape each run, returning glyph IDs + positioned advances/offsets 3. **`layout_shaped_run`** — emits `Glyph` structs from the shaping output, with NOTDEF fallback to other font faces for missing glyphs 4. **Buffer recycling** — `FontsImpl` pools a `harfrust::UnicodeBuffer` to avoid per-layout allocations ### Disclaimer I'm far from being a good Rust programmer. Claude Code did most of the heavy lifting here. I did my best and used my limited knowledge to avoid making too many mistakes. If this PR isn't up to quality standards, please don't hesitate to close it. ## Test plan - [x] `cargo test -p epaint` — all 18 text tests pass, including 6 new ones - [x] `cargo clippy -p epaint --all-features` — clean - [x] `cargo fmt` — clean - [ ] Snapshot tests need regeneration (expected: shaping changes glyph positions) - New tests added: - `test_gpos_kerning` — verifies GPOS kerning tightens "AV", "VA", "AT" pairs - `test_combining_diacritics` — combining tilde doesn't add extra width - `test_shaping_basic_latin` — sanity check for Latin text - `test_shaping_empty_string` — empty input doesn't panic - `test_shaping_multiple_newlines` — newline splitting works correctly - `test_shaping_mixed_font_fallback` — Latin + emoji in same string --------- Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>
This commit is contained in:
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:00fb02e0cc2c1454d3a3dc0635be24086234c2bc5e2c9fd73741b179622e16d6
|
||||
size 4514
|
||||
oid sha256:698d1e9ecf490e04fb58ebf81cf9c633be29712abe172a6e6adfdc7fe442732d
|
||||
size 4517
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:d8757e2db9a3892d9347495ad59f14d2bd9164a9ba258375a53c9faf8176b597
|
||||
size 8016
|
||||
oid sha256:84e857521c20ea2458d9f9d724578e0495c06e75571c25104bf64a0be0be617d
|
||||
size 8028
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:38ee4acc23d9c66f127d377ac8a0dd3b683a1465ca319fba092f6d3cdff8c266
|
||||
size 11166
|
||||
oid sha256:aac8c973f26d03c25155f0454185dc3ab27fffae1c63443e36f07c49fb0828bf
|
||||
size 11183
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:ac1941f5eab71bfad020132eae47e1995efa17410b7861aa9f260032e5b0472c
|
||||
size 21785
|
||||
oid sha256:8dd0d91d0866848e7965e88223ff3ddae2231728197fbcd3fb5b1a6b034e179d
|
||||
size 21804
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:b1f1a4dd9de1d8405c527c7f8f04b42ed9d403d0ec507bb3ff650a6896f28df0
|
||||
size 28628
|
||||
oid sha256:11d0081c272986e6fbd00cde05c0931321bc9670d6a0fa3a650323cd7b56d795
|
||||
size 28667
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:af05a9b66340e0c128d823d3935a23bcf17cfeac02a822e7277234a9c8eb26e0
|
||||
size 33393
|
||||
oid sha256:6fc08e05c0ab0e167503324fb7c82777afd5866384ae8bbacb9b933f717feb56
|
||||
size 33412
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:1dd1f5013587463f002b1becac1560876c462295dbe5dfbb1a9dbce58991e53d
|
||||
size 2209
|
||||
oid sha256:57211d5ac8021223ed288b9fd6f651ed901ec91599ed67d54ac540f3a2037937
|
||||
size 2205
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:cfc03625c268f0ae067d2f4521a8668b47e4bc8525350d77a480840a09cd5083
|
||||
size 2046
|
||||
oid sha256:4ae52d6761a4d0b95b38ca4ca34c10751eef1ab2a54a62786c55bee3a3522ba1
|
||||
size 2050
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:be0bd449166878ced27eff4966d1741731e926f9baabe8b590375c20103036dd
|
||||
size 5527
|
||||
oid sha256:7ddac0bff10e6699c0ab2d9ea3679d5047d6099b7ba3540d1ebc144361f07d86
|
||||
size 5517
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:6154c8bb550575bcb9fa0bba06da4d47079a00dffc5754b62ef2a6e7529e2090
|
||||
size 7489
|
||||
oid sha256:59939d3347679ff27c1de500a559cdc0e6387f633c86ec7ce51f2a36cd92547b
|
||||
size 7511
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:888f8a4d995d718a9a158e563d8ac1434775660b33aebb5f34feea54ffd12600
|
||||
size 2830
|
||||
oid sha256:db6f35e7dabe8f6d3766df022e5643f8461c3c2476959682622b793d59bf7c40
|
||||
size 2833
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:888f8a4d995d718a9a158e563d8ac1434775660b33aebb5f34feea54ffd12600
|
||||
size 2830
|
||||
oid sha256:db6f35e7dabe8f6d3766df022e5643f8461c3c2476959682622b793d59bf7c40
|
||||
size 2833
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:037f3e356d32e1a2c32767460399f919452bff0933e1db7aa113e7e2bdb083f0
|
||||
size 4927
|
||||
oid sha256:d785e2073627f33a2e7a23a47e0e0b8ca9570f45d4de3d8b545b473c1a9e75b5
|
||||
size 4940
|
||||
|
||||
Reference in New Issue
Block a user