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:7389e319d9153af313cc113d97b57d462da00feb0d5f99da211552af3ac7e18a
|
||||
size 6704
|
||||
oid sha256:19c9ae55906e0ad18a9507898884ec31ef785c498fb8d10267ee848bd3f17186
|
||||
size 6705
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:d4480dd34ed36c6bdbc2084843dd136448b3934c22b3df3e40314ba6324b5b39
|
||||
size 10306
|
||||
oid sha256:6f13116cb76e52620221b41c8dbd37b74a0da513402cbb2cb7e70c89027f5e31
|
||||
size 10313
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:624bfa884431c35cc5d852b96653f13da17e60f8545471f9fb1c3bb85b40ffc8
|
||||
size 16555
|
||||
oid sha256:6869b180f2d1dcfd9fcbb215ed3febfbe548f30035560272a4e11e465f20c592
|
||||
size 16567
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:bf665389ef43524e097a7ae4eec0aa01bb788bdbb306144f20f9133f74a64b2c
|
||||
size 6941
|
||||
oid sha256:132f060b2e20b4cc0c55062367381f47856a5cc10310b903dbddc3790f475581
|
||||
size 6942
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:a2480d0f49a929993de70612572b321457b2507c149a25112064cfc27840e6ee
|
||||
size 11005
|
||||
oid sha256:e5ec63c5e2f5bb5c0048ee67df5b821a30f89ba35dbedf72e7bc91e04a5f8359
|
||||
size 11007
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:1e0013b499934f47370a3a20b3d3a19f8a8c6db360752a35a3fb1d676d122263
|
||||
size 18068
|
||||
oid sha256:491bb98b22cf56e50d3ed10a10649db15aaffb93d7e045dfecfa26e98f537c35
|
||||
size 18070
|
||||
|
||||
Reference in New Issue
Block a user