1
0
mirror of https://github.com/emilk/egui.git synced 2026-09-03 07:10: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:
Gautier Cailly
2026-04-06 14:25:04 +02:00
committed by GitHub
parent 33e89e33be
commit 16cad760a5
158 changed files with 998 additions and 502 deletions

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:bff16d453b960bb9abcdd9f72dab73f8f25da6339a0e1c310ed352f57080db93
size 32426
oid sha256:10d64e017d1d0eba736a4471d28b1602a0cb69d8e2ab53f4ee604b01c9343116
size 32475

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:24f4a9745c60c0353ece5f8fc48200671dcb185f4f0b964bbe66bf4a2fe71d7a
size 27067
oid sha256:ab0d730ce0cf5f1d79947601def4f60c0a015e23a5dcd780df65c7ddc7ae7156
size 27194

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:75a9cd9a3315b236c23a53e890de1a821d39c3327813d06df85ba86d2ed50cc7
size 26887
oid sha256:6fccbda741a056ae30a7bfd7497f7b0adfb337bdb885d247fbdfef43cc24b54c
size 26948

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:b849adf0ff9a06e1f7bfa22ef32b13224c7e62429cf675286110729156434d12
size 76531
oid sha256:26d247655398bae33c724ad3c3bdcab330d194093b07442708d5069e256f636b
size 76542

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:d4e33c7f817100d8414bba245ee7886354b86109f383d59e87a197e39501f0a0
size 62604
oid sha256:e3389491f9f5c54cfc2e295abe76ad53f223c31e9489e1d07a8323cf14fcf37d
size 62628

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:93fcc271831167cb077f3de0a9f0e27037f9e5a2ce94e056bd6f1ede9890cb7e
size 27818
oid sha256:6e42e801758bb9e6130a4e94bd0857d6f254e68a7dedebec5af5cb7f7d896068
size 27822

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:d194db1705b36c1ee6189878a323a591555493c9319b7c2ddfc0cb0541055aca
size 20948
oid sha256:41e68fd3a679e2a6e3dd81129de5aa1ded3a8f24cc7b1f2ba0b876240d309d9b
size 21019

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:62af1b6d248b731b0930c51e481118ae01a5d8c08f27697ff121a47fefea83bf
size 10795
oid sha256:3db502ec416b322e0f98e9737faa52d5d2fd308d47649710fffa0b0bc5996f52
size 10783

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:deff441cd1d9142352f8759dff4b759f4572f0ddf93752349314da77abe4b254
size 115028
oid sha256:c39ae3420fe01d696a032d8d052c405c5623a9208fc673f5cf09188b1e6a539b
size 115447

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:20ea4f93ee50c7a3585aef74c66d7700083ac1c16519b0704b70387849d9d2bc
size 25057
oid sha256:19322248e8335301b2ce31fc1f1352993a374dda945d227784fecea2ee831761
size 25088

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:1b72a4c0e6d441190a7a156b8bba709e81b6c1fe7b0eacedc1ee7a3bfcf881f6
size 99297
oid sha256:98f8865d866a6f28ae3e3a16c815770ed691a031b1d06f7d3662a7e94f564606
size 99318

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:08c40934d4bd2a239bdcc1928d1e5eba56bac03fdded2c85cf47b020d669f07e
size 18281
oid sha256:39208c3c2c95f68fb37880b011f866bedd8dbccee33d163a725cb2a5cc6bb1b3
size 18290

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:82878e4150e38fdc4b2e78203c8c661c2d9e716ab32595c298392faf6ba96105
size 113803
oid sha256:d251ffd6c34e4ffa7b5de7fe5ae57a2d8f48ba7c2e03711da2c3f1a3fd84648c
size 113998

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:72d49d9a35e16e7413158ba14ce9cab762925f5f5e52fbbe16292de499a177f1
size 25791
oid sha256:4d959e17ee5c8a32534ab98b6f08db884b2fbf25476d8dc2dc90edc79bd87ea4
size 25821

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:bb7e3887cf12bd00ea09b297ac361562a961f64c15b28360bc87f72f270a4065
size 51649
oid sha256:c923f523cc77d678929f294b360f60b9f546ddec66d5317ad0eb44bd61a5f927
size 51733

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:58cd3aba4392332a45f57c7dd90a9b5da386cb396c0c6319e7a7dae71e03ff30
size 22563
oid sha256:c9c98d7bfa08e22e217dd9e7031cc49e4b4486f1a9fdd223bd122be07af72365
size 22550

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:26ffcf6b71108b82ce15d4cf3f9dd0ce9fe0b9563f02725fef1b74f40e749439
size 47281
oid sha256:68afa93605427e12fe527f3ca9613095664b4983f1f585a60f14bc2370c0a1f2
size 47224

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:faedf9631149e231d510165215c24fccec50502d58000d5f893aa047a637a68f
size 23148
oid sha256:33959851f1bd2386fcca8fb5700133d14d90db5a8f783fbfaa9f3aebb7b0d5b2
size 23119

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:b6b4c2e55c02fa4caf5f9f8bd2d8c0311cc4cbcf1fc2f568fe112e8e6125c675
size 65308
oid sha256:7b918565d66594fe53ed62bb14e357d3489335f3d037ccb088f198d944eb367d
size 65307

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:3a65927cd8bd8d24e3ffbea8eb421eb22849b27dc77d36f8acd82bf5d5e63959
size 33469
oid sha256:f8c3c2912a3a11892e65f94792c77c79400a81d8c913109d39e8ce12f5b095c6
size 33503

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:115398e2b4b459afc2f1c49c4e60ef3f63e562650f05581372002c93030da632
size 38374
oid sha256:0c2c7b0d4913b59ef932f6d6349ddab5cc8619b2e8e9a8b5eb3e055a62e6ed60
size 38382

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:9c595ee9b7ada33780178a6a35e26a98055a707f2ff99f6bb36e8db4ed819791
size 18242
oid sha256:9567f56f2dd030608798347e1ab755d95730ba5c5dd1721f1c61147be7216e87
size 18304

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:a46457b23b7b32694564d03b42bccac2f017a756225bc54b508bb6fe2ad8ee7b
size 249548
oid sha256:d19d694e6a70ab6acb45668b391751e82f7beab19f9918e23821c667e8cc9cdb
size 249733

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:c218115d305dfa6c9ab883ac6f3a21584b4840b3ba273ea765c8a8381d78935f
size 57181
oid sha256:9e1c73e28020371429b3e03d540f72dbf886fd40f0ba08bb194e868bbb3c95ff
size 57230

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:fadea24444c402695db6cbc9e03aef8a0ed3c5db487a324fb255d38c14f73dce
size 19804
oid sha256:c2facd3881e6b107a0dcce9d6e00008a3c9b0f31ad270c35357a87e487180f56
size 19814

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:f6105c95470d1342f9003ab03e71243b5e18a6f225261aee94b15f8f0501572c
size 33542
oid sha256:0ad81eb762150360368a97858ef30bb0d5aff72e71743fa40c3fd4d70ec84cdc
size 33400

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:f2ce9062c5d1f0b0861d5df49ae64e56ba0e6501e8bd3f8a92c53aea748be78b
size 23629
oid sha256:c22130891755ac095d73e0494f11ee8e89d0fd0c31a321d3afb969648ece11ef
size 23675

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:927a497e8b6f9ce3b71dcb67086f477e19d327c163b2b8ad868af10009c2faf2
size 172981
oid sha256:465f33e53ebe15b776e2d6d0710f13f2453f00bab1f6ef4319b3491f1d1d3a26
size 173487

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:6ffba8bb50b42e47f855f62682f6d5ec10bf67b01d3aa2e843f6bf787f150d0d
size 118562
oid sha256:001f7a1310ddf37be4d9a7f56a95a3079f713b741824a348544561bb16c291fa
size 118614

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:9d8d87e4cb944def0dc28e3515243a8c1b07b9b0f88e802924d4381c1cda74db
size 26763
oid sha256:8105f7c2b519716cc0a45dffd5f08980f53f35c6c6b788592e1d82506cdacccc
size 26665

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:db4c0cf1c4cdae3d416afce5c58efd1cc382be86431e547fa66bcc95a0a17ddb
size 76364
oid sha256:a9e83d5e83e3003830f7f719b02dd93273733a9b72d388aa42083387d02c1a20
size 76310

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:57bf5220ae8f47485a07e9117abaaad36924d8c6c0f9e278cb05c455f342bff6
size 70250
oid sha256:3d46c87417c49c8462fac6c488e46b1482bbc75f70fe8f7af8391f0d5d28dac3
size 70271

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:7c964d07a39ad286a562b53cdfe514d568d91955e6c1ca06a0cb5e45dbe3977e
size 60947
oid sha256:4c47faa75a002577220a92efceab5c0ccee1ddaa17c2d2f3d6d1429dbf8fe718
size 60887

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:f5e9d645e1decf7624bc8f031b5e28213e64fc5f568dd3eeb1768a57fcb988c3
size 21814
oid sha256:9c09529c3a1c26c8f28c00fc15cc5f495842862276870c24b5ee0713954f97fc
size 21916

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:57018beba5e4fb4f1e6de9c58bf898560b3a7669159d5bad91a4e2382ef57ce0
size 64004
oid sha256:9d69b9a12e777ee559a481aec012935a5bfb2ca8b0d48725ecd33e7f0880b2b8
size 64273

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:99fa5a5cb10c7d277eafb258af6019eda24a3c96075a50db321f52a521dede92
size 13700
oid sha256:f6c020860fe9cb7503cea548afbf298ebb9dc620133870b528fe7508d04150c8
size 13691

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:1cc61413bcce62cc8e0a55460a974bb56ac40936cd2e5512c4a0e0c521eaaae4
size 35874
oid sha256:d033935ca18ebee7e3c35629233cc3e3a73766ac8c0627fcdd8a12660eed703c
size 35873

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:999f9cc006302b8951d97b510a02f1209969c376ecc7909ed5d7b46da27c0637
size 483753
oid sha256:77c9058b036770a644f1bfcf9ed1ba7a29a7c98107b1823474c55ccc2880e9e7
size 485880