mirror of
https://github.com/emilk/egui.git
synced 2026-06-27 07:03:14 -04:00
* 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>
100 lines
3.0 KiB
TOML
100 lines
3.0 KiB
TOML
[package]
|
|
name = "epaint"
|
|
version.workspace = true
|
|
authors = ["Emil Ernerfeldt <emil.ernerfeldt@gmail.com>"]
|
|
description = "Minimal 2D graphics library for GUI work"
|
|
edition.workspace = true
|
|
rust-version.workspace = true
|
|
homepage = "https://github.com/emilk/egui/tree/main/crates/epaint"
|
|
license = "MIT OR Apache-2.0"
|
|
readme = "README.md"
|
|
repository = "https://github.com/emilk/egui/tree/main/crates/epaint"
|
|
categories = ["graphics", "gui"]
|
|
keywords = ["graphics", "gui", "egui"]
|
|
include = ["../../LICENSE-APACHE", "../../LICENSE-MIT", "**/*.rs", "Cargo.toml"]
|
|
|
|
[lints]
|
|
workspace = true
|
|
|
|
[package.metadata.docs.rs]
|
|
all-features = true
|
|
rustdoc-args = ["--generate-link-to-definition"]
|
|
|
|
[lib]
|
|
|
|
|
|
[features]
|
|
default = ["default_fonts"]
|
|
|
|
## [`bytemuck`](https://docs.rs/bytemuck) enables you to cast [`Vertex`] to `&[u8]`.
|
|
bytemuck = ["dep:bytemuck", "emath/bytemuck", "ecolor/bytemuck"]
|
|
|
|
## [`cint`](https://docs.rs/cint) enables interoperability with other color libraries.
|
|
cint = ["ecolor/cint"]
|
|
|
|
## Enable the [`hex_color`] macro.
|
|
color-hex = ["ecolor/color-hex"]
|
|
|
|
## If set, epaint will use `include_bytes!` to bundle some fonts.
|
|
## If you plan on specifying your own fonts you may disable this feature.
|
|
default_fonts = ["epaint_default_fonts"]
|
|
|
|
## [`mint`](https://docs.rs/mint) enables interoperability with other math libraries such as [`glam`](https://docs.rs/glam) and [`nalgebra`](https://docs.rs/nalgebra).
|
|
mint = ["emath/mint"]
|
|
|
|
## Enable parallel tessellation using [`rayon`](https://docs.rs/rayon).
|
|
##
|
|
## This can help performance for graphics-intense applications.
|
|
rayon = ["dep:rayon"]
|
|
|
|
## Allow serialization using [`serde`](https://docs.rs/serde).
|
|
serde = ["dep:serde", "ahash/serde", "emath/serde", "ecolor/serde", "font-types/serde", "smallvec/serde"]
|
|
|
|
## Change Vertex layout to be compatible with unity
|
|
unity = []
|
|
|
|
## Override and disable the unity feature
|
|
## This exists, so that when testing with --all-features, snapshots render correctly.
|
|
_override_unity = []
|
|
|
|
[dependencies]
|
|
emath.workspace = true
|
|
ecolor.workspace = true
|
|
|
|
ahash.workspace = true
|
|
font-types.workspace = true
|
|
harfrust.workspace = true
|
|
log.workspace = true
|
|
nohash-hasher.workspace = true
|
|
parking_lot.workspace = true # Using parking_lot over std::sync::Mutex gives 50% speedups in some real-world scenarios.
|
|
profiling.workspace = true
|
|
self_cell.workspace = true
|
|
skrifa.workspace = true
|
|
smallvec.workspace = true
|
|
unicode-general-category.workspace = true
|
|
unicode-segmentation.workspace = true
|
|
vello_cpu.workspace = true
|
|
|
|
#! ### Optional dependencies
|
|
bytemuck = { workspace = true, optional = true, features = ["derive"] }
|
|
|
|
## Enable this when generating docs.
|
|
document-features = { workspace = true, optional = true }
|
|
|
|
rayon = { workspace = true, optional = true }
|
|
|
|
## Allow serialization using [`serde`](https://docs.rs/serde) .
|
|
serde = { workspace = true, optional = true, features = ["derive", "rc"] }
|
|
|
|
epaint_default_fonts = { workspace = true, optional = true }
|
|
|
|
[dev-dependencies]
|
|
criterion.workspace = true
|
|
mimalloc.workspace = true
|
|
similar-asserts.workspace = true
|
|
|
|
|
|
[[bench]]
|
|
name = "benchmark"
|
|
harness = false
|