diff --git a/Cargo.lock b/Cargo.lock
index fe02a86dde8e..bc17eaf6bfcb 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -21,6 +21,12 @@ version = "0.2.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ee2a4ec343196209d6594e19543ae87a39f96d5534d7174822a3ad825dd6ed7e"
 
+[[package]]
+name = "ahash"
+version = "0.3.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e8fd72866655d1904d6b0997d0b07ba561047d070fbe29de039031c641b61217"
+
 [[package]]
 name = "ahash"
 version = "0.4.5"
@@ -519,10 +525,10 @@ checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650"
 name = "egui"
 version = "0.2.0"
 dependencies = [
- "ahash",
+ "ahash 0.4.5",
  "criterion",
+ "fontdue",
  "parking_lot",
- "rusttype",
  "serde",
  "serde_json",
 ]
@@ -574,6 +580,15 @@ version = "1.0.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
 
+[[package]]
+name = "fontdue"
+version = "0.3.2"
+source = "git+https://github.com/mooman219/fontdue#3e2f51fe70fd7ab21ff70652ff8c2798f6e9fc65"
+dependencies = [
+ "hashbrown",
+ "ttf-parser 0.8.2",
+]
+
 [[package]]
 name = "foreign-types"
 version = "0.3.2"
@@ -716,6 +731,16 @@ version = "1.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d36fab90f82edc3c747f9d438e06cf0a491055896f2a279638bb5beed6c40177"
 
+[[package]]
+name = "hashbrown"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e91b62f79061a0bc2e046024cb7ba44b08419ed238ecbd9adbd787434b9e8c25"
+dependencies = [
+ "ahash 0.3.8",
+ "autocfg",
+]
+
 [[package]]
 name = "hermit-abi"
 version = "0.1.17"
@@ -1143,7 +1168,7 @@ version = "0.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9f923fb806c46266c02ab4a5b239735c144bdeda724a50ed058e5226f594cde3"
 dependencies = [
- "ttf-parser",
+ "ttf-parser 0.6.2",
 ]
 
 [[package]]
@@ -1525,6 +1550,12 @@ version = "0.6.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3e5d7cd7ab3e47dda6e56542f4bbf3824c15234958c6e1bd6aaa347e93499fdc"
 
+[[package]]
+name = "ttf-parser"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d973cfa0e6124166b50a1105a67c85de40bbc625082f35c0f56f84cb1fb0a827"
+
 [[package]]
 name = "unicode-width"
 version = "0.1.8"
diff --git a/egui/Cargo.toml b/egui/Cargo.toml
index 214eaf484cd4..92aa18ce8003 100644
--- a/egui/Cargo.toml
+++ b/egui/Cargo.toml
@@ -17,7 +17,7 @@ include = [ "**/*.rs", "Cargo.toml", "fonts/ProggyClean.ttf", "fonts/Comfortaa-R
 [dependencies]
 ahash = { version = "0.4", features = ["std"], default-features = false }
 parking_lot = "0.11"
-rusttype = "0.9"
+fontdue = { version = "0.3", git = "https://github.com/mooman219/fontdue" }
 serde = { version = "1", features = ["derive"], optional = true }
 serde_json = { version = "1", optional = true }
 
diff --git a/egui/src/containers/collapsing_header.rs b/egui/src/containers/collapsing_header.rs
index 978d170686a1..91e2b9b2a95a 100644
--- a/egui/src/containers/collapsing_header.rs
+++ b/egui/src/containers/collapsing_header.rs
@@ -169,8 +169,9 @@ struct Prepared {
 
 impl CollapsingHeader {
     fn begin(self, ui: &mut Ui) -> Prepared {
-        assert!(
-            ui.layout().dir() == Direction::Vertical,
+        assert_eq!(
+            ui.layout().dir(),
+            Direction::Vertical,
             "Horizontal collapsing is unimplemented"
         );
         let Self {
@@ -186,14 +187,14 @@ impl CollapsingHeader {
 
         let available = ui.available_finite();
         let text_pos = available.min + vec2(ui.style().spacing.indent, 0.0);
-        let galley = label.layout_width(ui, available.right() - text_pos.x);
-        let text_max_x = text_pos.x + galley.size.x;
+        let layout = label.layout_width(ui, available.right() - text_pos.x);
+        let text_max_x = text_pos.x + layout.size.x;
         let desired_width = text_max_x - available.left();
         let desired_width = desired_width.max(available.width());
 
         let mut desired_size = vec2(
             desired_width,
-            galley.size.y + 2.0 * ui.style().spacing.button_padding.y,
+            layout.size.y + 2.0 * ui.style().spacing.button_padding.y,
         );
         desired_size = desired_size.at_least(ui.style().spacing.interact_size);
         let rect = ui.allocate_space(desired_size);
@@ -201,7 +202,7 @@ impl CollapsingHeader {
         let header_response = ui.interact(rect, id, Sense::click());
         let text_pos = pos2(
             text_pos.x,
-            header_response.rect.center().y - galley.size.y / 2.0,
+            header_response.rect.center().y - layout.size.y / 2.0,
         );
 
         let mut state = State::from_memory_with_default_open(ui.ctx(), id, default_open);
@@ -226,9 +227,9 @@ impl CollapsingHeader {
         }
 
         let painter = ui.painter();
-        painter.galley(
+        painter.layout(
             text_pos,
-            galley,
+            layout,
             label.text_style_or_default(ui.style()),
             ui.style().interact(&header_response).text_color(),
         );
diff --git a/egui/src/containers/window.rs b/egui/src/containers/window.rs
index 77ce625807f5..0ae6af48ffcc 100644
--- a/egui/src/containers/window.rs
+++ b/egui/src/containers/window.rs
@@ -2,9 +2,7 @@
 
 use std::sync::Arc;
 
-use crate::{paint::*, widgets::*, *};
-
-use super::*;
+use crate::{paint::fonts::GlyphLayout, paint::*, widgets::*, *};
 
 /// Builder for a floating window which can be dragged, closed, collapsed, resized and scrolled (off by default).
 ///
@@ -219,7 +217,7 @@ impl<'open> Window<'open> {
         // First interact (move etc) to avoid frame delay:
         let last_frame_outer_rect = area.state().rect();
         let interaction = if possible.movable || possible.resizable {
-            let title_bar_height = title_label.font_height(ctx.fonts(), &ctx.style())
+            let title_bar_height = title_label.font_height(&ctx.fonts().lock(), &ctx.style())
                 + 1.0 * ctx.style().spacing.item_spacing.y; // this could be better
             let margins = 2.0 * frame.margin + vec2(0.0, title_bar_height);
 
@@ -599,7 +597,7 @@ fn paint_frame_interaction(
 
 struct TitleBar {
     title_label: Label,
-    title_galley: font::Galley,
+    title_layout: GlyphLayout,
     title_rect: Rect,
     rect: Rect,
 }
@@ -613,7 +611,7 @@ fn show_title_bar(
     collapsible: bool,
 ) -> TitleBar {
     let (title_bar, response) = ui.horizontal(|ui| {
-        ui.set_min_height(title_label.font_height(ui.fonts(), ui.style()));
+        ui.set_min_height(title_label.font_height(&ui.fonts().lock(), ui.style()));
 
         let item_spacing = ui.style().spacing.item_spacing;
         let button_size = ui.style().spacing.icon_width;
@@ -630,8 +628,8 @@ fn show_title_bar(
             collapsing_header::paint_icon(ui, openness, &collapse_button_response);
         }
 
-        let title_galley = title_label.layout(ui);
-        let title_rect = ui.allocate_space(title_galley.size);
+        let title_layout = title_label.layout(ui);
+        let title_rect = ui.allocate_space(title_layout.size);
 
         if show_close_button {
             // Reserve space for close button which will be added later (once we know our full width):
@@ -649,7 +647,7 @@ fn show_title_bar(
 
         TitleBar {
             title_label,
-            title_galley,
+            title_layout,
             title_rect,
             rect: Rect::invalid(), // Will be filled in later
         }
@@ -685,7 +683,7 @@ impl TitleBar {
 
         // TODO: pick style for title based on move interaction
         self.title_label
-            .paint_galley(ui, self.title_rect.min, self.title_galley);
+            .paint_layout(ui, self.title_rect.min, self.title_layout);
 
         if let Some(content_response) = &content_response {
             // paint separator between title and content:
diff --git a/egui/src/context.rs b/egui/src/context.rs
index 2463fa1c4130..64af66563e92 100644
--- a/egui/src/context.rs
+++ b/egui/src/context.rs
@@ -25,7 +25,7 @@ struct Options {
     /// Controls the tessellator.
     paint_options: paint::PaintOptions,
     /// Font sizes etc.
-    font_definitions: FontDefinitions,
+    font_configuration: FontConfiguration,
 }
 
 /// Thi is the first thing you need when working with Egui.
@@ -39,7 +39,7 @@ struct Options {
 pub struct Context {
     options: Mutex<Options>,
     /// None until first call to `begin_frame`.
-    fonts: Option<Arc<Fonts>>,
+    fonts: Option<Arc<Mutex<Fonts>>>,
     memory: Arc<Mutex<Memory>>,
     animation_manager: Arc<Mutex<AnimationManager>>,
 
@@ -110,24 +110,24 @@ impl Context {
 
     /// Not valid until first call to `begin_frame()`
     /// That's because since we don't know the proper `pixels_per_point` until then.
-    pub fn fonts(&self) -> &Fonts {
-        &*self
-            .fonts
+    pub fn fonts(&self) -> Arc<Mutex<Fonts>> {
+        self.fonts
             .as_ref()
             .expect("No fonts available until first call to Context::begin_frame()`")
+            .clone()
     }
 
     /// The Egui texture, containing font characters etc..
     /// Not valid until first call to `begin_frame()`
     /// That's because since we don't know the proper `pixels_per_point` until then.
     pub fn texture(&self) -> Arc<paint::Texture> {
-        self.fonts().texture()
+        self.fonts().lock().texture()
     }
 
     /// Will become active at the start of the next frame.
     /// `pixels_per_point` will be ignored (overwritten at start of each frame with the contents of input)
-    pub fn set_fonts(&self, font_definitions: FontDefinitions) {
-        lock(&self.options, "options").font_definitions = font_definitions;
+    pub fn set_fonts(&self, font_configuration: FontConfiguration) {
+        lock(&self.options, "options").font_configuration = font_configuration;
     }
 
     pub fn style(&self) -> Arc<Style> {
@@ -183,14 +183,16 @@ impl Context {
         self.used_ids.lock().clear();
 
         self.input = std::mem::take(&mut self.input).begin_frame(new_raw_input);
-        let mut font_definitions = lock(&self.options, "options").font_definitions.clone();
-        font_definitions.pixels_per_point = self.input.pixels_per_point();
+        let mut font_configuration = lock(&self.options, "options").font_configuration.clone();
+        font_configuration.pixels_per_point = self.input.pixels_per_point();
         let same_as_current = match &self.fonts {
             None => false,
-            Some(fonts) => *fonts.definitions() == font_definitions,
+            Some(fonts) => *fonts.lock().configuration() == font_configuration,
         };
         if !same_as_current {
-            self.fonts = Some(Arc::new(Fonts::from_definitions(font_definitions)));
+            self.fonts = Some(Arc::new(Mutex::new(Fonts::from_definitions(
+                font_configuration,
+            ))));
         }
     }
 
@@ -523,10 +525,11 @@ impl Context {
         CollapsingHeader::new("Fonts")
             .default_open(false)
             .show(ui, |ui| {
-                let mut font_definitions = self.fonts().definitions().clone();
-                font_definitions.ui(ui);
-                self.fonts().texture().ui(ui);
-                self.set_fonts(font_definitions);
+                let mut font_configuration = self.fonts().lock().configuration().clone();
+                font_configuration.ui(ui);
+                let texture = self.fonts().lock().texture();
+                texture.ui(ui);
+                self.set_fonts(font_configuration);
             });
 
         CollapsingHeader::new("Painting")
diff --git a/egui/src/demos/app.rs b/egui/src/demos/app.rs
index 6bc5c5f389f4..9373b6d38228 100644
--- a/egui/src/demos/app.rs
+++ b/egui/src/demos/app.rs
@@ -129,7 +129,7 @@ impl FrameHistory {
                     ui.fonts(),
                     pos2(rect.left(), y),
                     align::LEFT_BOTTOM,
-                    text,
+                    &text,
                     TextStyle::Monospace,
                     color::WHITE,
                 ));
diff --git a/egui/src/introspection.rs b/egui/src/introspection.rs
index e864d19ff42f..1895b266a0a8 100644
--- a/egui/src/introspection.rs
+++ b/egui/src/introspection.rs
@@ -54,18 +54,18 @@ impl Texture {
     }
 }
 
-impl paint::FontDefinitions {
+impl paint::FontConfiguration {
     pub fn ui(&mut self, ui: &mut Ui) {
-        for (text_style, (_family, size)) in self.fonts.iter_mut() {
+        for (text_style, definition) in self.definitions.iter_mut() {
             // TODO: radio button for family
             ui.add(
-                Slider::f32(size, 4.0..=40.0)
+                Slider::f32(&mut definition.scale_in_points, 4.0..=40.0)
                     .precision(0)
                     .text(format!("{:?}", text_style)),
             );
         }
         if ui.button("Reset fonts").clicked {
-            *self = paint::FontDefinitions::with_pixels_per_point(self.pixels_per_point);
+            *self = paint::FontConfiguration::with_pixels_per_point(self.pixels_per_point);
         }
     }
 }
diff --git a/egui/src/paint/command.rs b/egui/src/paint/command.rs
index 968b7bc35546..dc0fdb986bf7 100644
--- a/egui/src/paint/command.rs
+++ b/egui/src/paint/command.rs
@@ -1,8 +1,13 @@
+use std::sync::Arc;
+
+use parking_lot::Mutex;
+
 use {
-    super::{font::Galley, fonts::TextStyle, Fonts, Srgba, Triangles},
+    super::{fonts::TextStyle, Fonts, Srgba, Triangles},
     crate::{
         align::{anchor_rect, Align},
         math::{Pos2, Rect},
+        paint::fonts::GlyphLayout,
     },
 };
 
@@ -38,9 +43,8 @@ pub enum PaintCmd {
     Text {
         /// Top left corner of the first character.
         pos: Pos2,
-        /// The layed out text
-        galley: Galley,
-        text_style: TextStyle, // TODO: Font?
+        layout: GlyphLayout,
+        text_style: TextStyle,
         color: Srgba,
     },
     Triangles(Triangles),
@@ -91,19 +95,18 @@ impl PaintCmd {
     }
 
     pub fn text(
-        fonts: &Fonts,
+        fonts: Arc<Mutex<Fonts>>,
         pos: Pos2,
         anchor: (Align, Align),
-        text: impl Into<String>,
+        text: &str,
         text_style: TextStyle,
         color: Srgba,
     ) -> Self {
-        let font = &fonts[text_style];
-        let galley = font.layout_multiline(text.into(), f32::INFINITY);
-        let rect = anchor_rect(Rect::from_min_size(pos, galley.size), anchor);
+        let layout = fonts.lock().layout_multiline(text_style, text, None);
+        let rect = anchor_rect(Rect::from_min_size(pos, layout.size), anchor);
         Self::Text {
             pos: rect.min,
-            galley,
+            layout,
             text_style,
             color,
         }
diff --git a/egui/src/paint/font.rs b/egui/src/paint/font.rs
deleted file mode 100644
index 8e0f854767da..000000000000
--- a/egui/src/paint/font.rs
+++ /dev/null
@@ -1,531 +0,0 @@
-use std::sync::Arc;
-
-use {
-    ahash::AHashMap,
-    parking_lot::{Mutex, RwLock},
-    rusttype::{point, Scale},
-};
-
-use crate::math::{vec2, Vec2};
-
-use super::texture_atlas::TextureAtlas;
-
-#[derive(Clone, Copy, Debug, Default)]
-pub struct GalleyCursor {
-    /// character count in whole galley
-    pub char_idx: usize,
-    /// line number
-    pub line: usize,
-    /// character count on this line
-    pub column: usize,
-}
-
-/// A collection of text locked into place.
-#[derive(Clone, Debug, Default)]
-pub struct Galley {
-    /// The full text
-    pub text: String,
-
-    /// Lines of text, from top to bottom.
-    /// The number of chars in all lines sum up to text.chars().count()
-    pub lines: Vec<Line>,
-
-    // Optimization: calculate once and reuse.
-    pub size: Vec2,
-}
-
-/// A typeset piece of text on a single line.
-#[derive(Clone, Debug)]
-pub struct Line {
-    /// The start of each character, probably starting at zero.
-    /// The last element is the end of the last character.
-    /// x_offsets.len() == text.chars().count() + 1
-    /// This is never empty.
-    /// Unit: points.
-    pub x_offsets: Vec<f32>,
-
-    /// Top of the line, offset within the Galley.
-    /// Unit: points.
-    pub y_min: f32,
-
-    /// Bottom of the line, offset within the Galley.
-    /// Unit: points.
-    pub y_max: f32,
-
-    /// If true, the last char on this line is '\n'
-    pub ends_with_newline: bool,
-}
-
-impl Galley {
-    pub fn sanity_check(&self) {
-        let mut char_count = 0;
-        for line in &self.lines {
-            line.sanity_check();
-            char_count += line.char_count();
-        }
-        assert_eq!(char_count, self.text.chars().count());
-    }
-
-    /// If given a char index after the first line, the end of the last character is returned instead.
-    /// Returns a Vec2 rather than a Pos2 as this is an offset into the galley. *shrug*
-    pub fn char_start_pos(&self, char_idx: usize) -> Vec2 {
-        let mut char_count = 0;
-        for line in &self.lines {
-            let line_char_count = line.char_count();
-            if char_count <= char_idx && char_idx < char_count + line_char_count {
-                let line_char_offset = char_idx - char_count;
-                return vec2(line.x_offsets[line_char_offset], line.y_min);
-            }
-            char_count += line_char_count;
-        }
-
-        if let Some(last) = self.lines.last() {
-            vec2(last.max_x(), last.y_min)
-        } else {
-            // Empty galley
-            vec2(0.0, 0.0)
-        }
-    }
-
-    /// Character offset at the given position within the galley
-    pub fn char_at(&self, pos: Vec2) -> GalleyCursor {
-        let mut best_y_dist = f32::INFINITY;
-        let mut cursor = GalleyCursor::default();
-
-        let mut char_count = 0;
-        for (line_nr, line) in self.lines.iter().enumerate() {
-            let y_dist = (line.y_min - pos.y).abs().min((line.y_max - pos.y).abs());
-            if y_dist < best_y_dist {
-                best_y_dist = y_dist;
-                let mut column = line.char_at(pos.x);
-                if column == line.char_count() && line.ends_with_newline {
-                    // handle the case where line ends with a \n and we click after it.
-                    // We should return the position BEFORE the \n!
-                    column -= 1;
-                }
-                cursor = GalleyCursor {
-                    char_idx: char_count + column,
-                    line: line_nr,
-                    column,
-                }
-            }
-            char_count += line.char_count();
-        }
-        cursor
-    }
-}
-
-impl Line {
-    pub fn sanity_check(&self) {
-        assert!(!self.x_offsets.is_empty());
-    }
-
-    pub fn char_count(&self) -> usize {
-        assert!(!self.x_offsets.is_empty());
-        self.x_offsets.len() - 1
-    }
-
-    pub fn min_x(&self) -> f32 {
-        *self.x_offsets.first().unwrap()
-    }
-
-    pub fn max_x(&self) -> f32 {
-        *self.x_offsets.last().unwrap()
-    }
-
-    /// Closest char at the desired x coordinate. returns something in the range `[0, char_count()]`
-    pub fn char_at(&self, desired_x: f32) -> usize {
-        for (i, char_x_bounds) in self.x_offsets.windows(2).enumerate() {
-            let char_center_x = 0.5 * (char_x_bounds[0] + char_x_bounds[1]);
-            if desired_x < char_center_x {
-                return i;
-            }
-        }
-        self.char_count()
-    }
-}
-
-// ----------------------------------------------------------------------------
-
-// const REPLACEMENT_CHAR: char = '\u{25A1}'; // □ white square Replaces a missing or unsupported Unicode character.
-// const REPLACEMENT_CHAR: char = '\u{FFFD}'; // � REPLACEMENT CHARACTER
-const REPLACEMENT_CHAR: char = '?';
-
-#[derive(Clone, Copy, Debug)]
-pub struct UvRect {
-    /// X/Y offset for nice rendering (unit: points).
-    pub offset: Vec2,
-    pub size: Vec2,
-
-    /// Top left corner UV in texture.
-    pub min: (u16, u16),
-
-    /// Bottom right corner (exclusive).
-    pub max: (u16, u16),
-}
-
-#[derive(Clone, Copy, Debug)]
-pub struct GlyphInfo {
-    id: rusttype::GlyphId,
-
-    /// Unit: points.
-    pub advance_width: f32,
-
-    /// Texture coordinates. None for space.
-    pub uv_rect: Option<UvRect>,
-}
-
-/// The interface uses points as the unit for everything.
-pub struct Font {
-    font: rusttype::Font<'static>,
-    /// Maximum character height
-    scale_in_pixels: f32,
-    pixels_per_point: f32,
-    replacement_glyph_info: GlyphInfo,
-    glyph_infos: RwLock<AHashMap<char, GlyphInfo>>,
-    atlas: Arc<Mutex<TextureAtlas>>,
-}
-
-impl Font {
-    pub fn new(
-        atlas: Arc<Mutex<TextureAtlas>>,
-        font_data: &'static [u8],
-        scale_in_points: f32,
-        pixels_per_point: f32,
-    ) -> Font {
-        assert!(scale_in_points > 0.0);
-        assert!(pixels_per_point > 0.0);
-
-        let font = rusttype::Font::try_from_bytes(font_data).expect("Error constructing Font");
-        let scale_in_pixels = pixels_per_point * scale_in_points;
-
-        let replacement_glyph_info = allocate_glyph(
-            &mut atlas.lock(),
-            REPLACEMENT_CHAR,
-            &font,
-            scale_in_pixels,
-            pixels_per_point,
-        )
-        .unwrap_or_else(|| {
-            panic!(
-                "Failed to find replacement character {:?}",
-                REPLACEMENT_CHAR
-            )
-        });
-
-        let font = Font {
-            font,
-            scale_in_pixels,
-            pixels_per_point,
-            replacement_glyph_info,
-            glyph_infos: Default::default(),
-            atlas,
-        };
-
-        font.glyph_infos
-            .write()
-            .insert(REPLACEMENT_CHAR, font.replacement_glyph_info);
-
-        // Preload the printable ASCII characters [32, 126] (which excludes control codes):
-        const FIRST_ASCII: usize = 32; // 32 == space
-        const LAST_ASCII: usize = 126;
-        for c in (FIRST_ASCII..=LAST_ASCII).map(|c| c as u8 as char) {
-            font.glyph_info(c);
-        }
-        font.glyph_info('°');
-
-        font
-    }
-
-    pub fn round_to_pixel(&self, point: f32) -> f32 {
-        (point * self.pixels_per_point).round() / self.pixels_per_point
-    }
-
-    /// Height of one line of text. In points
-    /// TODO: rename height ?
-    pub fn line_spacing(&self) -> f32 {
-        self.scale_in_pixels / self.pixels_per_point
-    }
-    pub fn height(&self) -> f32 {
-        self.scale_in_pixels / self.pixels_per_point
-    }
-
-    pub fn uv_rect(&self, c: char) -> Option<UvRect> {
-        self.glyph_infos.read().get(&c).and_then(|gi| gi.uv_rect)
-    }
-
-    fn glyph_info(&self, c: char) -> GlyphInfo {
-        if c == '\n' {
-            // Hack: else we show '\n' as '?' (REPLACEMENT_CHAR)
-            return self.glyph_info(' ');
-        }
-
-        {
-            if let Some(glyph_info) = self.glyph_infos.read().get(&c) {
-                return *glyph_info;
-            }
-        }
-
-        // Add new character:
-        let glyph_info = allocate_glyph(
-            &mut self.atlas.lock(),
-            c,
-            &self.font,
-            self.scale_in_pixels,
-            self.pixels_per_point,
-        );
-        // debug_assert!(glyph_info.is_some(), "Failed to find {:?}", c);
-        let glyph_info = glyph_info.unwrap_or(self.replacement_glyph_info);
-        self.glyph_infos.write().insert(c, glyph_info);
-        glyph_info
-    }
-
-    /// Typeset the given text onto one line.
-    /// Assumes there are no \n in the text.
-    /// Always returns exactly one fragment.
-    pub fn layout_single_line(&self, text: String) -> Galley {
-        let x_offsets = self.layout_single_line_fragment(&text);
-        let line = Line {
-            x_offsets,
-            y_min: 0.0,
-            y_max: self.height(),
-            ends_with_newline: false,
-        };
-        let width = line.max_x();
-        let size = vec2(width, self.height());
-        let galley = Galley {
-            text,
-            lines: vec![line],
-            size,
-        };
-        galley.sanity_check();
-        galley
-    }
-
-    pub fn layout_multiline(&self, text: String, max_width_in_points: f32) -> Galley {
-        let line_spacing = self.line_spacing();
-        let mut cursor_y = 0.0;
-        let mut lines = Vec::new();
-
-        let mut paragraph_start = 0;
-
-        while paragraph_start < text.len() {
-            let next_newline = text[paragraph_start..].find('\n');
-            let paragraph_end = next_newline
-                .map(|newline| paragraph_start + newline + 1)
-                .unwrap_or_else(|| text.len());
-
-            assert!(paragraph_start < paragraph_end);
-            let paragraph_text = &text[paragraph_start..paragraph_end];
-            let mut paragraph_lines =
-                self.layout_paragraph_max_width(paragraph_text, max_width_in_points);
-            assert!(!paragraph_lines.is_empty());
-
-            for line in &mut paragraph_lines {
-                line.y_min += cursor_y;
-                line.y_max += cursor_y;
-            }
-            cursor_y = paragraph_lines.last().unwrap().y_max;
-            cursor_y += line_spacing * 0.4; // extra spacing between paragraphs. less hacky
-
-            lines.append(&mut paragraph_lines);
-
-            paragraph_start = paragraph_end;
-        }
-
-        if text.is_empty() || text.ends_with('\n') {
-            // Add an empty last line for correct visuals etc:
-            lines.push(Line {
-                x_offsets: vec![0.0],
-                y_min: cursor_y,
-                y_max: cursor_y + line_spacing,
-                ends_with_newline: text.ends_with('\n'),
-            });
-        }
-
-        let mut widest_line = 0.0;
-        for line in &lines {
-            widest_line = line.max_x().max(widest_line);
-        }
-        let size = vec2(widest_line, lines.last().unwrap().y_max);
-
-        let galley = Galley { text, lines, size };
-        galley.sanity_check();
-        galley
-    }
-
-    /// Typeset the given text onto one line.
-    /// Assumes there are no \n in the text.
-    /// Return `x_offsets`, one longer than the number of characters in the text.
-    fn layout_single_line_fragment(&self, text: &str) -> Vec<f32> {
-        let scale_in_pixels = Scale::uniform(self.scale_in_pixels);
-
-        let mut x_offsets = Vec::with_capacity(text.chars().count() + 1);
-        x_offsets.push(0.0);
-
-        let mut cursor_x_in_points = 0.0f32;
-        let mut last_glyph_id = None;
-
-        for c in text.chars() {
-            let glyph = self.glyph_info(c);
-
-            if let Some(last_glyph_id) = last_glyph_id {
-                cursor_x_in_points +=
-                    self.font
-                        .pair_kerning(scale_in_pixels, last_glyph_id, glyph.id)
-                        / self.pixels_per_point
-            }
-            cursor_x_in_points += glyph.advance_width;
-            cursor_x_in_points = self.round_to_pixel(cursor_x_in_points);
-            last_glyph_id = Some(glyph.id);
-
-            x_offsets.push(cursor_x_in_points);
-        }
-
-        x_offsets
-    }
-
-    /// A paragraph is text with no line break character in it.
-    /// The text will be linebreaked by the given `max_width_in_points`.
-    pub fn layout_paragraph_max_width(&self, text: &str, max_width_in_points: f32) -> Vec<Line> {
-        let full_x_offsets = self.layout_single_line_fragment(text);
-
-        let mut line_start_x = full_x_offsets[0];
-
-        {
-            #![allow(clippy::float_cmp)]
-            assert_eq!(line_start_x, 0.0);
-        }
-
-        let mut cursor_y = 0.0;
-        let mut line_start_idx = 0;
-
-        // start index of the last space. A candidate for a new line.
-        let mut last_space = None;
-
-        let mut out_lines = vec![];
-
-        for (i, (x, chr)) in full_x_offsets.iter().skip(1).zip(text.chars()).enumerate() {
-            let line_width = x - line_start_x;
-
-            if line_width > max_width_in_points {
-                if let Some(last_space_idx) = last_space {
-                    let include_trailing_space = true;
-                    let line = if include_trailing_space {
-                        Line {
-                            x_offsets: full_x_offsets[line_start_idx..=last_space_idx + 1]
-                                .iter()
-                                .map(|x| x - line_start_x)
-                                .collect(),
-                            y_min: cursor_y,
-                            y_max: cursor_y + self.height(),
-                            ends_with_newline: false, // we'll fix this later
-                        }
-                    } else {
-                        Line {
-                            x_offsets: full_x_offsets[line_start_idx..=last_space_idx]
-                                .iter()
-                                .map(|x| x - line_start_x)
-                                .collect(),
-                            y_min: cursor_y,
-                            y_max: cursor_y + self.height(),
-                            ends_with_newline: false, // we'll fix this later
-                        }
-                    };
-                    line.sanity_check();
-                    out_lines.push(line);
-
-                    line_start_idx = last_space_idx + 1;
-                    line_start_x = full_x_offsets[line_start_idx];
-                    last_space = None;
-                    cursor_y += self.line_spacing();
-                    cursor_y = self.round_to_pixel(cursor_y);
-                }
-            }
-
-            const NON_BREAKING_SPACE: char = '\u{A0}';
-            if chr.is_whitespace() && chr != NON_BREAKING_SPACE {
-                last_space = Some(i);
-            }
-        }
-
-        if line_start_idx + 1 < full_x_offsets.len() {
-            let line = Line {
-                x_offsets: full_x_offsets[line_start_idx..]
-                    .iter()
-                    .map(|x| x - line_start_x)
-                    .collect(),
-                y_min: cursor_y,
-                y_max: cursor_y + self.height(),
-                ends_with_newline: false, // we'll fix this later
-            };
-            line.sanity_check();
-            out_lines.push(line);
-        }
-
-        if text.ends_with('\n') {
-            out_lines.last_mut().unwrap().ends_with_newline = true;
-        }
-
-        out_lines
-    }
-}
-
-fn allocate_glyph(
-    atlas: &mut TextureAtlas,
-    c: char,
-    font: &rusttype::Font<'static>,
-    scale_in_pixels: f32,
-    pixels_per_point: f32,
-) -> Option<GlyphInfo> {
-    let glyph = font.glyph(c);
-    if glyph.id().0 == 0 {
-        return None; // Failed to find a glyph for the character
-    }
-
-    let glyph = glyph.scaled(Scale::uniform(scale_in_pixels));
-    let glyph = glyph.positioned(point(0.0, 0.0));
-
-    let uv_rect = if let Some(bb) = glyph.pixel_bounding_box() {
-        let glyph_width = bb.width() as usize;
-        let glyph_height = bb.height() as usize;
-        assert!(glyph_width >= 1);
-        assert!(glyph_height >= 1);
-
-        let glyph_pos = atlas.allocate((glyph_width, glyph_height));
-
-        let texture = atlas.texture_mut();
-        glyph.draw(|x, y, v| {
-            if v > 0.0 {
-                let px = glyph_pos.0 + x as usize;
-                let py = glyph_pos.1 + y as usize;
-                texture[(px, py)] = (v * 255.0).round() as u8;
-            }
-        });
-
-        let offset_y_in_pixels = scale_in_pixels as f32 + bb.min.y as f32 - 4.0 * pixels_per_point; // TODO: use font.v_metrics
-        Some(UvRect {
-            offset: vec2(
-                bb.min.x as f32 / pixels_per_point,
-                offset_y_in_pixels / pixels_per_point,
-            ),
-            size: vec2(glyph_width as f32, glyph_height as f32) / pixels_per_point,
-            min: (glyph_pos.0 as u16, glyph_pos.1 as u16),
-            max: (
-                (glyph_pos.0 + glyph_width) as u16,
-                (glyph_pos.1 + glyph_height) as u16,
-            ),
-        })
-    } else {
-        // No bounding box. Maybe a space?
-        None
-    };
-
-    let advance_width_in_points = glyph.unpositioned().h_metrics().advance_width / pixels_per_point;
-
-    Some(GlyphInfo {
-        id: glyph.id(),
-        advance_width: advance_width_in_points,
-        uv_rect,
-    })
-}
diff --git a/egui/src/paint/fonts.rs b/egui/src/paint/fonts.rs
index e03109e9f62e..58c74f4375ec 100644
--- a/egui/src/paint/fonts.rs
+++ b/egui/src/paint/fonts.rs
@@ -4,14 +4,17 @@ use std::{
     sync::Arc,
 };
 
+use ahash::AHashMap;
+use fontdue::{
+    layout::{CoordinateSystem, GlyphPosition, GlyphRasterConfig, LayoutSettings},
+    Font, FontSettings, Metrics,
+};
 use parking_lot::Mutex;
 
-use super::{
-    font::Font,
-    texture_atlas::{Texture, TextureAtlas},
-};
+use crate::math::{vec2, Vec2};
+
+use super::texture_atlas::{Texture, TextureAtlas};
 
-// TODO: rename
 #[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
 #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
 #[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
@@ -29,113 +32,240 @@ pub enum FontFamily {
     VariableWidth,
 }
 
+impl FontFamily {
+    /// Used as index for the font vector. The fonts need to be inserted in this order!
+    pub fn font_index(&self) -> usize {
+        match self {
+            FontFamily::Monospace => 0,
+            FontFamily::VariableWidth => 1,
+        }
+    }
+}
+
+#[derive(Clone, Debug, PartialEq)]
+pub struct FontDefinition {
+    pub family: FontFamily,
+    pub scale_in_points: f32,
+}
+
+/// Configured the typefaces that are used. This is a configuration and is not supposed to be changed while rendering.
 #[derive(Clone, Debug, PartialEq)]
-pub struct FontDefinitions {
+pub struct FontConfiguration {
     /// The dpi scale factor. Needed to get pixel perfect fonts.
     pub pixels_per_point: f32,
-
-    pub fonts: BTreeMap<TextStyle, (FontFamily, f32)>,
+    pub definitions: BTreeMap<TextStyle, FontDefinition>,
 }
 
-impl Default for FontDefinitions {
+impl Default for FontConfiguration {
     fn default() -> Self {
         Self::with_pixels_per_point(f32::NAN) // must be set later
     }
 }
 
-impl FontDefinitions {
+impl FontConfiguration {
     pub fn with_pixels_per_point(pixels_per_point: f32) -> Self {
-        let mut fonts = BTreeMap::new();
-        fonts.insert(TextStyle::Body, (FontFamily::VariableWidth, 14.0));
-        fonts.insert(TextStyle::Button, (FontFamily::VariableWidth, 16.0));
-        fonts.insert(TextStyle::Heading, (FontFamily::VariableWidth, 24.0));
-        fonts.insert(TextStyle::Monospace, (FontFamily::Monospace, 13.0));
-
+        let mut definitions = BTreeMap::new();
+        definitions.insert(
+            TextStyle::Body,
+            FontDefinition {
+                family: FontFamily::VariableWidth,
+                scale_in_points: 12.0,
+            },
+        );
+        definitions.insert(
+            TextStyle::Button,
+            FontDefinition {
+                family: FontFamily::VariableWidth,
+                scale_in_points: 13.0,
+            },
+        );
+        definitions.insert(
+            TextStyle::Heading,
+            FontDefinition {
+                family: FontFamily::VariableWidth,
+                scale_in_points: 20.0,
+            },
+        );
+        definitions.insert(
+            TextStyle::Monospace,
+            FontDefinition {
+                family: FontFamily::Monospace,
+                scale_in_points: 11.0,
+            },
+        );
         Self {
             pixels_per_point,
-            fonts,
+            definitions,
         }
     }
 }
 
-/// Note: the `default()` fonts are invalid (missing `pixels_per_point`).
-#[derive(Default)]
+#[derive(Clone, Copy, Debug, Default)]
+pub struct UvRect {
+    /// The size of the element in points.
+    pub size: Vec2,
+
+    /// Top left corner UV in texture.
+    pub min: (u16, u16),
+
+    /// Bottom right corner (exclusive).
+    pub max: (u16, u16),
+}
+
+#[derive(Clone, Copy, Debug, Default)]
+pub struct GlyphInfo {
+    /// Glyph metrics.
+    pub metrics: Metrics,
+
+    /// Texture coordinates.
+    pub uv_rect: UvRect,
+}
+
+/// Glyph layout information. Units are in physical pixel and need to be scaled back to points.
+#[derive(Clone, Debug, Default)]
+pub struct GlyphLayout {
+    pub size: Vec2,
+    pub glyph_positions: Vec<GlyphPosition>,
+}
+
+impl GlyphLayout {
+    pub fn with_capacity(capacity: usize) -> Self {
+        Self {
+            size: vec2(0.0, 0.0),
+            glyph_positions: Vec::with_capacity(capacity),
+        }
+    }
+}
+
+/// Font renderer.
 pub struct Fonts {
-    definitions: FontDefinitions,
-    fonts: BTreeMap<TextStyle, Font>,
+    configuration: FontConfiguration,
+    fonts: Vec<Font>,
+    layout_engine: fontdue::layout::Layout,
+    glyph_infos: AHashMap<GlyphRasterConfig, GlyphInfo>,
     atlas: Arc<Mutex<TextureAtlas>>,
     /// Copy of the texture in the texture atlas.
     /// This is so we can return a reference to it (the texture atlas is behind a lock).
     buffered_texture: Mutex<Arc<Texture>>,
+    /// Precalculated heights for the TextStyles.
+    heights: BTreeMap<TextStyle, f32>,
+    /// Precalculated heights for the TextStyles.
+    line_spacings: BTreeMap<TextStyle, f32>,
+}
+
+impl Default for Fonts {
+    fn default() -> Self {
+        Self {
+            configuration: Default::default(),
+            fonts: Default::default(),
+            layout_engine: fontdue::layout::Layout::new(CoordinateSystem::PositiveYDown),
+            glyph_infos: Default::default(),
+            atlas: Default::default(),
+            buffered_texture: Default::default(),
+            heights: Default::default(),
+            line_spacings: Default::default(),
+        }
+    }
 }
 
 impl Fonts {
-    pub fn from_definitions(definitions: FontDefinitions) -> Fonts {
-        let mut fonts = Self::default();
-        fonts.set_definitions(definitions);
+    pub fn from_definitions(configuration: FontConfiguration) -> Fonts {
+        let mut fonts = Fonts::default();
+        fonts.set_configuration(configuration);
         fonts
     }
 
-    pub fn definitions(&self) -> &FontDefinitions {
-        &self.definitions
+    pub fn configuration(&self) -> &FontConfiguration {
+        &self.configuration
     }
 
-    pub fn set_definitions(&mut self, definitions: FontDefinitions) {
-        if self.definitions == definitions {
+    pub fn set_configuration(&mut self, configuration: FontConfiguration) {
+        if self.configuration == configuration {
             return;
         }
 
-        let mut atlas = TextureAtlas::new(512, 16); // TODO: better default?
+        self.atlas = Arc::new(Mutex::new(TextureAtlas::new(512, 512)));
 
-        {
-            // Make the top left pixel fully white:
-            let pos = atlas.allocate((1, 1));
-            assert_eq!(pos, (0, 0));
-            atlas.texture_mut()[pos] = 255;
-        }
+        // Make the top left pixel fully white, since it's used for the UI rendering.
+        let pos = self.atlas.lock().allocate((1, 1));
+        self.atlas.lock().texture_mut()[pos] = 255;
+        debug_assert_eq!(pos, (0, 0));
 
-        let atlas = Arc::new(Mutex::new(atlas));
+        // FontFamily::Monospace (Use 13 for this. NOTHING ELSE).
+        let monospace_typeface_data: &[u8] = include_bytes!("../../fonts/ProggyClean.ttf");
+        // FontFamily::VariableWidth.
+        // FIXME: https://github.com/mooman219/fontdue/issues/38
+        // FIXME: https://github.com/RazrFalcon/ttf-parser/issues/43
+        let variable_typeface_data: &[u8] = include_bytes!("../../fonts/Comfortaa-Regular.ttf");
+        //let variable_typeface_data: &[u8] = include_bytes!("../../fonts/Roboto-Regular.ttf");
 
-        // TODO: figure out a way to make the WASM smaller despite including a font. Zip it?
-        let monospace_typeface_data = include_bytes!("../../fonts/ProggyClean.ttf"); // Use 13 for this. NOTHING ELSE.
+        // Fonts need to be added in the same order as defined in the `FontFamily.font_index()` method.
+        self.fonts.push(
+            Font::from_bytes(monospace_typeface_data, FontSettings::default())
+                .expect("error constructing Font"),
+        );
+        self.fonts.push(
+            Font::from_bytes(variable_typeface_data, FontSettings::default())
+                .expect("error constructing Font"),
+        );
 
-        // let monospace_typeface_data = include_bytes!("../../fonts/Roboto-Regular.ttf");
+        self.configuration = configuration;
 
-        let variable_typeface_data = include_bytes!("../../fonts/Comfortaa-Regular.ttf"); // Funny, hard to read
+        let pixels_per_points = self.configuration.pixels_per_point;
+        for (text_style, definition) in self.configuration.definitions.clone().iter() {
+            let font_index = definition.family.font_index();
+            let scale_in_pixels = definition.scale_in_points * pixels_per_points;
 
-        // let variable_typeface_data = include_bytes!("../../fonts/DejaVuSans.ttf"); // Basic, boring, takes up more space
+            // Preload the printable ASCII characters [33, 126] (which excludes control codes):
+            const FIRST_ASCII: usize = 33; // !
+            const LAST_ASCII: usize = 126; // ~
 
-        self.definitions = definitions.clone();
-        let FontDefinitions {
-            pixels_per_point,
-            fonts,
-        } = definitions;
-        self.fonts = fonts
-            .into_iter()
-            .map(|(text_style, (family, size))| {
-                let typeface_data: &[u8] = match family {
-                    FontFamily::Monospace => monospace_typeface_data,
-                    FontFamily::VariableWidth => variable_typeface_data,
+            for u in FIRST_ASCII..=LAST_ASCII {
+                let c = std::char::from_u32(u as u32)
+                    .unwrap_or_else(|| panic!("can't create char from u32: {}", u));
+                let key = GlyphRasterConfig {
+                    c,
+                    px: scale_in_pixels,
+                    font_index,
                 };
+                self.glyph_info(&key);
+            }
 
-                (
-                    text_style,
-                    Font::new(atlas.clone(), typeface_data, size, pixels_per_point),
-                )
-            })
-            .collect();
-
-        {
-            let mut atlas = atlas.lock();
-            let texture = atlas.texture_mut();
-            // Make sure we seed the texture version with something unique based on the default characters:
-            let mut hasher = ahash::AHasher::default();
-            texture.pixels.hash(&mut hasher);
-            texture.version = hasher.finish();
+            // Precalculate the line spacings and heights (in points)
+            let px = scale_in_pixels;
+            let font = &self.fonts[font_index];
+            let line_spacing = font
+                .horizontal_line_metrics(px)
+                .unwrap_or_else(|| panic!("font doesn't seem to support horizontal text layout"))
+                .new_line_size;
+            self.line_spacings
+                .insert(*text_style, line_spacing / pixels_per_points);
+            self.heights
+                .insert(*text_style, scale_in_pixels / pixels_per_points);
         }
 
-        self.buffered_texture = Default::default(); //atlas.lock().texture().clone();
-        self.atlas = atlas;
+        // Make sure we seed the texture version with something unique based on the default characters:
+        let mut atlas = self.atlas.lock();
+        let texture = atlas.texture_mut();
+        let mut hasher = ahash::AHasher::default();
+        texture.pixels.hash(&mut hasher);
+        texture.version = hasher.finish();
+
+        self.buffered_texture = Default::default();
+    }
+
+    /// Returns the `GlyphInfo` for the given `GlyphRasterConfig` key. Allocates a new Glyph if necessary.
+    pub fn glyph_info(&mut self, grc: &GlyphRasterConfig) -> GlyphInfo {
+        if let Some(glyph_info) = self.glyph_infos.get(grc) {
+            return *glyph_info;
+        }
+
+        let glyph_info = self.allocate_glyph(grc);
+
+        let glyph_info =
+            glyph_info.unwrap_or_else(|| panic!("couldn't render glyph: {:#?}", grc.c));
+        self.glyph_infos.insert(*grc, glyph_info);
+        glyph_info
     }
 
     pub fn texture(&self) -> Arc<Texture> {
@@ -147,12 +277,144 @@ impl Fonts {
 
         buffered_texture.clone()
     }
-}
 
-impl std::ops::Index<TextStyle> for Fonts {
-    type Output = Font;
+    /// Typeset the given text onto one line. Ignores hard wraps. Returns the dimension of the line.
+    pub fn layout_single_line(&mut self, style: TextStyle, text: &str) -> GlyphLayout {
+        let settings = LayoutSettings {
+            wrap_hard_breaks: false,
+            ..Default::default()
+        };
+
+        self.layout(style, text, settings)
+    }
+
+    // FIXME: https://github.com/mooman219/fontdue/issues/39
+    /// Typeset the given text onto multiple lines.
+    pub fn layout_multiline(
+        &mut self,
+        style: TextStyle,
+        text: &str,
+        max_width_in_points: Option<f32>,
+    ) -> GlyphLayout {
+        let settings = LayoutSettings {
+            max_width: max_width_in_points,
+            wrap_hard_breaks: true,
+            ..Default::default()
+        };
+
+        self.layout(style, text, settings)
+    }
+
+    fn layout(
+        &mut self,
+        style: TextStyle,
+        text: &str,
+        mut settings: LayoutSettings,
+    ) -> GlyphLayout {
+        // We calculate the layout in physical pixel and later scale back the metrics back to points.
+        // This is done to get a better looking layout.
+        let mut layout = GlyphLayout::with_capacity(text.len());
+
+        if text.is_empty() {
+            return layout;
+        }
+
+        // Convert points to pixel.
+        settings.max_width = if let Some(width) = settings.max_width {
+            Some(width * self.configuration.pixels_per_point)
+        } else {
+            None
+        };
+
+        let font_index = self.configuration.definitions[&style].family.font_index();
+        let pixels_per_point = self.configuration.pixels_per_point;
+        let height = self.heights[&style];
+        let px_height = self.heights[&style] * pixels_per_point;
+
+        let text_style = fontdue::layout::TextStyle {
+            text,
+            px: px_height,
+            font_index,
+        };
+
+        self.layout_engine.layout_horizontal(
+            &self.fonts,
+            &[&text_style],
+            &settings,
+            &mut layout.glyph_positions,
+        );
+
+        let mut min_y = f32::MAX;
+        let mut max_y = f32::MIN;
+        let mut min_x = f32::MAX;
+        let mut max_x = f32::MIN;
+
+        // Calculate logical points and max dimensions.
+        for pos in layout.glyph_positions.iter_mut() {
+            pos.width = (pos.width as f32 / pixels_per_point) as usize;
+            pos.height = (pos.height as f32 / pixels_per_point) as usize;
+            pos.x /= pixels_per_point;
+            pos.y /= pixels_per_point;
+
+            min_x = min_x.min(pos.x);
+            max_x = max_x.max(pos.x + pos.width as f32);
+            min_y = min_y.min(pos.y);
+            max_y = max_y.max(pos.y + pos.height as f32);
+        }
+
+        // Wait for fontdue do provide better line metrics to align the glyphs
+        // to the line with the current DPI setting.
+
+        // Add 20% height as margin to each side
+        let width = (max_x - min_x) + 0.4 * height;
+
+        // Add 20% height as margin to each side
+        let height = (max_y - min_y) + 0.4 * height;
+
+        layout.size = vec2(width, height);
+        layout
+    }
+
+    fn allocate_glyph(&mut self, grc: &GlyphRasterConfig) -> Option<GlyphInfo> {
+        let font = &self.fonts[grc.font_index];
+        let (metrics, glyph_data) = font.rasterize(grc.c, grc.px);
+
+        if glyph_data.is_empty() {
+            return None;
+        }
+
+        let mut atlas = self.atlas.lock();
+        let glyph_pos = atlas.allocate((metrics.width, metrics.height));
+        let texture = atlas.texture_mut();
+
+        for (i, v) in glyph_data.iter().enumerate() {
+            if *v > 0 {
+                let px = glyph_pos.0 + (i % metrics.width);
+                let py = glyph_pos.1 + (i / metrics.width);
+                texture[(px, py)] = *v;
+            }
+        }
+
+        let uv_rect = UvRect {
+            size: vec2(metrics.width as f32, metrics.height as f32)
+                / self.configuration.pixels_per_point,
+            min: (glyph_pos.0 as u16, glyph_pos.1 as u16),
+            max: (
+                (glyph_pos.0 + metrics.width) as u16,
+                (glyph_pos.1 + metrics.height) as u16,
+            ),
+        };
+
+        Some(GlyphInfo { metrics, uv_rect })
+    }
+
+    /// Returns the font height in points of the given `TextStyle`.
+    pub fn text_style_height(&self, text_style: TextStyle) -> f32 {
+        self.heights[&text_style]
+    }
 
-    fn index(&self, text_style: TextStyle) -> &Font {
-        &self.fonts[&text_style]
+    /// Returns the line spacing in points of the given `TextStyle`.
+    pub fn text_style_line_spacing(&self, text_style: TextStyle) -> f32 {
+        self.line_spacings[&text_style]
     }
 }
diff --git a/egui/src/paint/mod.rs b/egui/src/paint/mod.rs
index 383435312233..de783db3e477 100644
--- a/egui/src/paint/mod.rs
+++ b/egui/src/paint/mod.rs
@@ -4,7 +4,6 @@
 
 pub mod color;
 pub mod command;
-pub mod font;
 pub mod fonts;
 pub mod tessellator;
 mod texture_atlas;
@@ -12,7 +11,7 @@ mod texture_atlas;
 pub use {
     color::{Rgba, Srgba},
     command::{PaintCmd, Stroke},
-    fonts::{FontDefinitions, Fonts, TextStyle},
+    fonts::{FontConfiguration, Fonts, TextStyle},
     tessellator::{PaintJobs, PaintOptions, TextureId, Triangles, Vertex, WHITE_UV},
     texture_atlas::Texture,
 };
diff --git a/egui/src/paint/tessellator.rs b/egui/src/paint/tessellator.rs
index c940d827efa0..2a3b6650eaf4 100644
--- a/egui/src/paint/tessellator.rs
+++ b/egui/src/paint/tessellator.rs
@@ -5,6 +5,9 @@
 
 #![allow(clippy::identity_op)]
 
+use parking_lot::Mutex;
+use std::sync::Arc;
+
 use {
     super::{
         color::{self, srgba, Rgba, Srgba, TRANSPARENT},
@@ -655,7 +658,7 @@ fn tessellate_paint_command(
     clip_rect: Rect,
     command: PaintCmd,
     options: PaintOptions,
-    fonts: &Fonts,
+    fonts: Arc<Mutex<Fonts>>,
     out: &mut Triangles,
     scratchpad_points: &mut Vec<Pos2>,
     scratchpad_path: &mut Path,
@@ -744,59 +747,55 @@ fn tessellate_paint_command(
         }
         PaintCmd::Text {
             pos,
-            galley,
+            layout,
             text_style,
             color,
         } => {
             if color == TRANSPARENT {
                 return;
             }
-            galley.sanity_check();
-
-            let num_chars = galley.text.chars().count();
-            out.reserve_triangles(num_chars * 2);
-            out.reserve_vertices(num_chars * 4);
-
-            let tex_w = fonts.texture().width as f32;
-            let tex_h = fonts.texture().height as f32;
-
-            let text_offset = vec2(0.0, 1.0); // Eye-balled for buttons. TODO: why is this needed?
-
-            let clip_rect = clip_rect.expand(2.0); // Some fudge to handle letter slightly larger than expected.
-
-            let font = &fonts[text_style];
-            let mut chars = galley.text.chars();
-            for line in &galley.lines {
-                let line_min_y = pos.y + line.y_min + text_offset.x;
-                let line_max_y = line_min_y + font.height();
-                let is_line_visible =
-                    line_max_y >= clip_rect.min.y && line_min_y <= clip_rect.max.y;
-
-                for x_offset in line.x_offsets.iter().take(line.x_offsets.len() - 1) {
-                    let c = chars.next().unwrap();
-
-                    if options.coarse_tessellation_culling && !is_line_visible {
-                        // culling individual lines of text is important, since a single `PaintCmd::Text`
-                        // can span hundreds of lines.
-                        continue;
+            let num_glyphs = layout.glyph_positions.len();
+            out.reserve_triangles(num_glyphs * 2);
+            out.reserve_vertices(num_glyphs * 4);
+
+            let tex_w = fonts.lock().texture().width as f32;
+            let tex_h = fonts.lock().texture().height as f32;
+
+            let line_height = fonts.lock().text_style_line_spacing(text_style);
+
+            let mut was_visible = false;
+            for glyph in &layout.glyph_positions {
+                // Coarse culling the glyphs on the Y-axis.
+                // Could be optimized by only checking every n-th glyph.
+                if options.coarse_tessellation_culling {
+                    let glyph_pos_y = pos.y + glyph.y as f32;
+                    let is_glyph_visible = glyph_pos_y >= clip_rect.min.y - line_height
+                        && glyph_pos_y <= clip_rect.max.y;
+
+                    if !was_visible && is_glyph_visible {
+                        was_visible = true;
                     }
 
-                    if let Some(glyph) = font.uv_rect(c) {
-                        let mut left_top =
-                            pos + glyph.offset + vec2(*x_offset, line.y_min) + text_offset;
-                        left_top.x = font.round_to_pixel(left_top.x); // Pixel-perfection.
-                        left_top.y = font.round_to_pixel(left_top.y); // Pixel-perfection.
-
-                        let pos = Rect::from_min_max(left_top, left_top + glyph.size);
-                        let uv = Rect::from_min_max(
-                            pos2(glyph.min.0 as f32 / tex_w, glyph.min.1 as f32 / tex_h),
-                            pos2(glyph.max.0 as f32 / tex_w, glyph.max.1 as f32 / tex_h),
-                        );
-                        out.add_rect_with_uv(pos, uv, color);
+                    if !is_glyph_visible {
+                        if was_visible {
+                            break;
+                        }
+                        continue;
                     }
                 }
+
+                let glyph_info = fonts.lock().glyph_info(&glyph.key);
+                let uv_rect = glyph_info.uv_rect;
+                let glyph_pos = vec2(glyph.x, glyph.y);
+                let left_top = pos + glyph_pos;
+                let pos = Rect::from_min_max(left_top, left_top + uv_rect.size);
+                let uv = Rect::from_min_max(
+                    pos2(uv_rect.min.0 as f32 / tex_w, uv_rect.min.1 as f32 / tex_h),
+                    pos2(uv_rect.max.0 as f32 / tex_w, uv_rect.max.1 as f32 / tex_h),
+                );
+
+                out.add_rect_with_uv(pos, uv, color);
             }
-            assert_eq!(chars.next(), None);
         }
     }
 }
@@ -815,7 +814,7 @@ fn tessellate_paint_command(
 pub fn tessellate_paint_commands(
     commands: Vec<(Rect, PaintCmd)>,
     options: PaintOptions,
-    fonts: &Fonts,
+    fonts: Arc<Mutex<Fonts>>,
 ) -> Vec<(Rect, Triangles)> {
     let mut scratchpad_points = Vec::new();
     let mut scratchpad_path = Path::default();
@@ -842,7 +841,7 @@ pub fn tessellate_paint_commands(
             clip_rect,
             cmd,
             options,
-            fonts,
+            fonts.clone(),
             out,
             &mut scratchpad_points,
             &mut scratchpad_path,
@@ -860,7 +859,7 @@ pub fn tessellate_paint_commands(
                     stroke: Stroke::new(2.0, srgba(150, 255, 150, 255)),
                 },
                 options,
-                fonts,
+                fonts.clone(),
                 triangles,
                 &mut scratchpad_points,
                 &mut scratchpad_path,
diff --git a/egui/src/painter.rs b/egui/src/painter.rs
index 7154334f1d57..f26d27e5d4f5 100644
--- a/egui/src/painter.rs
+++ b/egui/src/painter.rs
@@ -1,11 +1,14 @@
 use std::sync::Arc;
 
+use parking_lot::Mutex;
+
 use crate::{
     align::{anchor_rect, Align, LEFT_TOP},
     color,
     layers::PaintCmdIdx,
     math::{Pos2, Rect, Vec2},
-    paint::{font, Fonts, PaintCmd, Stroke, TextStyle},
+    paint::fonts::GlyphLayout,
+    paint::{Fonts, PaintCmd, Stroke, TextStyle},
     Context, Layer, Srgba,
 };
 
@@ -48,7 +51,7 @@ impl Painter {
     }
 
     /// Available fonts
-    pub(crate) fn fonts(&self) -> &Fonts {
+    pub(crate) fn fonts(&self) -> Arc<Mutex<Fonts>> {
         self.ctx.fonts()
     }
 
@@ -115,25 +118,23 @@ impl Painter {
 
 /// ## Debug painting
 impl Painter {
-    pub fn debug_rect(&mut self, rect: Rect, color: Srgba, text: impl Into<String>) {
+    pub fn debug_rect(&mut self, rect: Rect, color: Srgba, text: &str) {
         self.rect_stroke(rect, 0.0, (1.0, color));
         let text_style = TextStyle::Monospace;
-        self.text(rect.min, LEFT_TOP, text.into(), text_style, color);
+        self.text(rect.min, LEFT_TOP, text, text_style, color);
     }
 
-    pub fn error(&self, pos: Pos2, text: impl Into<String>) {
-        let text = text.into();
+    pub fn error(&self, pos: Pos2, text: &str) {
         let text_style = TextStyle::Monospace;
-        let font = &self.fonts()[text_style];
-        let galley = font.layout_multiline(text, f32::INFINITY);
-        let rect = anchor_rect(Rect::from_min_size(pos, galley.size), LEFT_TOP);
+        let layout = self.fonts().lock().layout_multiline(text_style, text, None);
+        let rect = anchor_rect(Rect::from_min_size(pos, layout.size), LEFT_TOP);
         self.add(PaintCmd::Rect {
             rect: rect.expand(2.0),
             corner_radius: 0.0,
             fill: Srgba::black_alpha(240),
             stroke: Stroke::new(1.0, color::RED),
         });
-        self.galley(rect.min, galley, text_style, color::RED);
+        self.layout(rect.min, layout, text_style, color::RED);
     }
 
     pub fn debug_arrow(&self, origin: Pos2, dir: Vec2, stroke: Stroke) {
@@ -248,22 +249,21 @@ impl Painter {
         &self,
         pos: Pos2,
         anchor: (Align, Align),
-        text: impl Into<String>,
+        text: &str,
         text_style: TextStyle,
         text_color: Srgba,
     ) -> Rect {
-        let font = &self.fonts()[text_style];
-        let galley = font.layout_multiline(text.into(), f32::INFINITY);
-        let rect = anchor_rect(Rect::from_min_size(pos, galley.size), anchor);
-        self.galley(rect.min, galley, text_style, text_color);
+        let layout = self.fonts().lock().layout_multiline(text_style, text, None);
+        let rect = anchor_rect(Rect::from_min_size(pos, layout.size), anchor);
+        self.layout(rect.min, layout, text_style, text_color);
         rect
     }
 
-    /// Paint text that has already been layed out in a `Galley`.
-    pub fn galley(&self, pos: Pos2, galley: font::Galley, text_style: TextStyle, color: Srgba) {
+    /// Paint text that has already been laid out in a `GlyphLayout`.
+    pub fn layout(&self, pos: Pos2, layout: GlyphLayout, text_style: TextStyle, color: Srgba) {
         self.add(PaintCmd::Text {
             pos,
-            galley,
+            layout,
             text_style,
             color,
         });
diff --git a/egui/src/ui.rs b/egui/src/ui.rs
index 7bbb5f67d1d9..78fac776171a 100644
--- a/egui/src/ui.rs
+++ b/egui/src/ui.rs
@@ -3,6 +3,7 @@
 use std::{hash::Hash, sync::Arc};
 
 use crate::{color::*, containers::*, layout::*, paint::*, widgets::*, *};
+use parking_lot::Mutex;
 
 /// Represents a region of the screen
 /// with a type of layout (horizontal or vertical).
@@ -166,7 +167,7 @@ impl Ui {
 
     /// The `Fonts` of the `Context` associated with the `Ui`.
     /// Equivalent to `.ctx().fonts()`.
-    pub fn fonts(&self) -> &Fonts {
+    pub fn fonts(&self) -> Arc<Mutex<Fonts>> {
         self.ctx().fonts()
     }
 
diff --git a/egui/src/widgets/mod.rs b/egui/src/widgets/mod.rs
index 93a4917222f2..438ff9ad1084 100644
--- a/egui/src/widgets/mod.rs
+++ b/egui/src/widgets/mod.rs
@@ -6,7 +6,7 @@
 
 #![allow(clippy::new_without_default)]
 
-use crate::{layout::Direction, *};
+use crate::{layout::Direction, paint::fonts::GlyphLayout, paint::*, *};
 
 pub mod color_picker;
 mod drag_value;
@@ -16,8 +16,6 @@ pub(crate) mod text_edit;
 
 pub use {drag_value::DragValue, image::Image, slider::*, text_edit::*};
 
-use paint::*;
-
 // ----------------------------------------------------------------------------
 
 /// Anything implementing Widget can be added to a Ui with `Ui::add`
@@ -74,7 +72,7 @@ impl Label {
         self
     }
 
-    pub fn layout(&self, ui: &Ui) -> font::Galley {
+    pub fn layout(&self, ui: &Ui) -> GlyphLayout {
         let max_width = ui.available().width();
         // Prevent word-wrapping after a single letter, and other silly shit:
         // TODO: general "don't force labels and similar to wrap so early"
@@ -82,19 +80,20 @@ impl Label {
         self.layout_width(ui, max_width)
     }
 
-    pub fn layout_width(&self, ui: &Ui, max_width: f32) -> font::Galley {
+    pub fn layout_width(&self, ui: &Ui, max_width: f32) -> GlyphLayout {
         let text_style = self.text_style_or_default(ui.style());
-        let font = &ui.fonts()[text_style];
         if self.multiline {
-            font.layout_multiline(self.text.clone(), max_width) // TODO: avoid clone
+            ui.fonts()
+                .lock()
+                .layout_multiline(text_style, &self.text, Some(max_width))
         } else {
-            font.layout_single_line(self.text.clone()) // TODO: avoid clone
+            ui.fonts().lock().layout_single_line(text_style, &self.text)
         }
     }
 
     pub fn font_height(&self, fonts: &Fonts, style: &Style) -> f32 {
         let text_style = self.text_style_or_default(style);
-        fonts[text_style].height()
+        fonts.text_style_height(text_style)
     }
 
     // TODO: this should return a LabelLayout which has a paint method.
@@ -105,12 +104,12 @@ impl Label {
     // TODO: a paint method for painting anywhere in a ui.
     // This should be the easiest method of putting text anywhere.
 
-    pub fn paint_galley(&self, ui: &mut Ui, pos: Pos2, galley: font::Galley) {
+    pub fn paint_layout(&self, ui: &mut Ui, pos: Pos2, layout: GlyphLayout) {
         let text_style = self.text_style_or_default(ui.style());
         let text_color = self
             .text_color
             .unwrap_or_else(|| ui.style().visuals.text_color());
-        ui.painter().galley(pos, galley, text_style, text_color);
+        ui.painter().layout(pos, layout, text_style, text_color);
     }
 
     /// Read the text style, or get the default for the current style
@@ -130,9 +129,9 @@ macro_rules! label {
 
 impl Widget for Label {
     fn ui(self, ui: &mut Ui) -> Response {
-        let galley = self.layout(ui);
-        let rect = ui.allocate_space(galley.size);
-        self.paint_galley(ui, rect.min, galley);
+        let layout = self.layout(ui);
+        let rect = ui.allocate_space(layout.size);
+        self.paint_layout(ui, rect.min, layout);
         ui.interact_hover(rect)
     }
 }
@@ -182,13 +181,14 @@ impl Hyperlink {
 impl Widget for Hyperlink {
     fn ui(self, ui: &mut Ui) -> Response {
         let Hyperlink { url, text } = self;
-
         let color = color::LIGHT_BLUE;
         let text_style = ui.style().body_text_style;
         let id = ui.make_child_id(&url);
-        let font = &ui.fonts()[text_style];
-        let galley = font.layout_multiline(text, ui.available().width());
-        let rect = ui.allocate_space(galley.size);
+        let layout =
+            ui.fonts()
+                .lock()
+                .layout_multiline(text_style, &text, Some(ui.available().width()));
+        let rect = ui.allocate_space(layout.size);
         let response = ui.interact(rect, id, Sense::click());
         if response.hovered {
             ui.ctx().output().cursor_icon = CursorIcon::PointingHand;
@@ -197,10 +197,13 @@ impl Widget for Hyperlink {
             ui.ctx().output().open_url = Some(url.clone());
         }
 
+        // FIXME Render the underline (render under all glyphs?)
+        /*
         let visuals = ui.style().interact(&response);
-
+        // Render the underline
         if response.hovered {
-            // Underline:
+
+
             for line in &galley.lines {
                 let pos = response.rect.min;
                 let y = pos.y + line.y_max;
@@ -213,9 +216,10 @@ impl Widget for Hyperlink {
                 );
             }
         }
+        */
 
         ui.painter()
-            .galley(response.rect.min, galley, text_style, color);
+            .layout(response.rect.min, layout, text_style, color);
 
         response.on_hover_text(url)
     }
@@ -294,9 +298,11 @@ impl Widget for Button {
         let button_padding = ui.style().spacing.button_padding;
 
         let id = ui.make_position_id();
-        let font = &ui.fonts()[text_style];
-        let galley = font.layout_multiline(text, ui.available().width());
-        let mut desired_size = galley.size + 2.0 * button_padding;
+        let layout =
+            ui.fonts()
+                .lock()
+                .layout_multiline(text_style, &text, Some(ui.available().width()));
+        let mut desired_size = layout.size + 2.0 * button_padding;
         desired_size = desired_size.at_least(ui.style().spacing.interact_size);
         let rect = ui.allocate_space(desired_size);
 
@@ -305,7 +311,7 @@ impl Widget for Button {
         // let text_cursor = response.rect.center() - 0.5 * galley.size; // centered-centered (looks bad for justified drop-down menus
         let text_cursor = pos2(
             response.rect.left() + button_padding.x,
-            response.rect.center().y - 0.5 * galley.size.y,
+            response.rect.center().y - 0.5 * layout.size.y,
         ); // left-centered
         let fill = fill.unwrap_or(visuals.bg_fill);
         ui.painter().rect(
@@ -318,7 +324,7 @@ impl Widget for Button {
             .or(ui.style().visuals.override_text_color)
             .unwrap_or_else(|| visuals.text_color());
         ui.painter()
-            .galley(text_cursor, galley, text_style, text_color);
+            .layout(text_cursor, layout, text_style, text_color);
         response
     }
 }
@@ -359,15 +365,14 @@ impl<'a> Widget for Checkbox<'a> {
 
         let id = ui.make_position_id();
         let text_style = TextStyle::Button;
-        let font = &ui.fonts()[text_style];
-        let galley = font.layout_single_line(text);
+        let layout = ui.fonts().lock().layout_single_line(text_style, &text);
 
         let spacing = &ui.style().spacing;
         let icon_width = spacing.icon_width;
         let icon_spacing = ui.style().spacing.icon_spacing;
         let button_padding = spacing.button_padding;
         let mut desired_size =
-            button_padding + vec2(icon_width + icon_spacing, 0.0) + galley.size + button_padding;
+            button_padding + vec2(icon_width + icon_spacing, 0.0) + layout.size + button_padding;
         desired_size = desired_size.at_least(spacing.interact_size);
         desired_size.y = desired_size.y.max(icon_width);
         let rect = ui.allocate_space(desired_size);
@@ -380,7 +385,7 @@ impl<'a> Widget for Checkbox<'a> {
         let visuals = ui.style().interact(&response);
         let text_cursor = pos2(
             response.rect.min.x + button_padding.x + icon_width + icon_spacing,
-            response.rect.center().y - 0.5 * galley.size.y,
+            response.rect.center().y - 0.5 * layout.size.y,
         );
         let (small_icon_rect, big_icon_rect) = ui.style().spacing.icon_rectangles(response.rect);
         ui.painter().add(PaintCmd::Rect {
@@ -407,7 +412,7 @@ impl<'a> Widget for Checkbox<'a> {
             .or(ui.style().visuals.override_text_color)
             .unwrap_or_else(|| visuals.text_color());
         ui.painter()
-            .galley(text_cursor, galley, text_style, text_color);
+            .layout(text_cursor, layout, text_style, text_color);
         response
     }
 }
@@ -446,14 +451,16 @@ impl Widget for RadioButton {
         } = self;
         let id = ui.make_position_id();
         let text_style = TextStyle::Button;
-        let font = &ui.fonts()[text_style];
-        let galley = font.layout_multiline(text, ui.available().width());
+        let layout =
+            ui.fonts()
+                .lock()
+                .layout_multiline(text_style, &text, Some(ui.available().width()));
 
         let icon_width = ui.style().spacing.icon_width;
         let icon_spacing = ui.style().spacing.icon_spacing;
         let button_padding = ui.style().spacing.button_padding;
         let mut desired_size =
-            button_padding + vec2(icon_width + icon_spacing, 0.0) + galley.size + button_padding;
+            button_padding + vec2(icon_width + icon_spacing, 0.0) + layout.size + button_padding;
         desired_size = desired_size.at_least(ui.style().spacing.interact_size);
         desired_size.y = desired_size.y.max(icon_width);
         let rect = ui.allocate_space(desired_size);
@@ -462,7 +469,7 @@ impl Widget for RadioButton {
 
         let text_cursor = pos2(
             response.rect.min.x + button_padding.x + icon_width + icon_spacing,
-            response.rect.center().y - 0.5 * galley.size.y,
+            response.rect.center().y - 0.5 * layout.size.y,
         );
 
         let visuals = ui.style().interact(&response);
@@ -492,7 +499,7 @@ impl Widget for RadioButton {
         let text_color = text_color
             .or(ui.style().visuals.override_text_color)
             .unwrap_or_else(|| visuals.text_color());
-        painter.galley(text_cursor, galley, text_style, text_color);
+        painter.layout(text_cursor, layout, text_style, text_color);
         response
     }
 }
diff --git a/egui/src/widgets/slider.rs b/egui/src/widgets/slider.rs
index 6f4238245b89..d407abce1352 100644
--- a/egui/src/widgets/slider.rs
+++ b/egui/src/widgets/slider.rs
@@ -354,9 +354,10 @@ impl<'a> Slider<'a> {
 impl<'a> Widget for Slider<'a> {
     fn ui(mut self, ui: &mut Ui) -> Response {
         let text_style = TextStyle::Button;
-        let font = &ui.fonts()[text_style];
-        let height = font
-            .line_spacing()
+        let height = ui
+            .fonts()
+            .lock()
+            .text_style_line_spacing(text_style)
             .at_least(ui.style().spacing.interact_size.y);
 
         if self.text.is_some() {
diff --git a/egui/src/widgets/text_edit.rs b/egui/src/widgets/text_edit.rs
index 512cd6cad413..462f4c8eaa1a 100644
--- a/egui/src/widgets/text_edit.rs
+++ b/egui/src/widgets/text_edit.rs
@@ -96,17 +96,19 @@ impl<'t> Widget for TextEdit<'t> {
         let mut state = ui.memory().text_edit.get(&id).cloned().unwrap_or_default();
 
         let text_style = text_style.unwrap_or_else(|| ui.style().body_text_style);
-        let font = &ui.fonts()[text_style];
-        let line_spacing = font.line_spacing();
+
+        let line_spacing = ui.fonts().lock().text_style_line_spacing(text_style);
         let available_width = ui.available().width();
-        let mut galley = if multiline {
-            font.layout_multiline(text.clone(), available_width)
+        let layout = if multiline {
+            ui.fonts()
+                .lock()
+                .layout_multiline(text_style, text, Some(available_width))
         } else {
-            font.layout_single_line(text.clone())
+            ui.fonts().lock().layout_single_line(text_style, text)
         };
         let desired_size = vec2(
-            galley.size.x.max(desired_width.min(available_width)),
-            galley.size.y.max(line_spacing),
+            layout.size.x.max(desired_width.min(available_width)),
+            layout.size.y.max(line_spacing),
         );
         let rect = ui.allocate_space(desired_size);
         let sense = if enabled {
@@ -118,9 +120,11 @@ impl<'t> Widget for TextEdit<'t> {
 
         if response.clicked && enabled {
             ui.memory().request_kb_focus(id);
-            if let Some(mouse_pos) = ui.input().mouse.pos {
-                state.cursor = Some(galley.char_at(mouse_pos - response.rect.min).char_idx);
-            }
+        /* FIXME find the character under the cursor
+        if let Some(mouse_pos) = ui.input().mouse.pos {
+            state.cursor = Some(layout.char_at(mouse_pos - response.rect.min).char_idx);
+        }
+         */
         } else if ui.input().mouse.click || (ui.input().mouse.pressed && !response.hovered) {
             // User clicked somewhere else
             ui.memory().surrender_kb_focus(id);
@@ -172,14 +176,13 @@ impl<'t> Widget for TextEdit<'t> {
             state.cursor = Some(cursor);
 
             // layout again to avoid frame delay:
-            let font = &ui.fonts()[text_style];
-            galley = if multiline {
-                font.layout_multiline(text.clone(), available_width)
+            if multiline {
+                ui.fonts()
+                    .lock()
+                    .layout_multiline(text_style, text, Some(available_width));
             } else {
-                font.layout_single_line(text.clone())
+                ui.fonts().lock().layout_single_line(text_style, text);
             };
-
-            // dbg!(&galley);
         }
 
         let painter = ui.painter();
@@ -196,8 +199,10 @@ impl<'t> Widget for TextEdit<'t> {
             });
         }
 
+        /* FIXME Print the cursor
         if ui.memory().has_kb_focus(id) {
             let cursor_blink_hz = ui.style().visuals.cursor_blink_hz;
+
             let show_cursor = if 0.0 < cursor_blink_hz {
                 ui.ctx().request_repaint(); // TODO: only when cursor blinks on or off
                 (ui.input().time * cursor_blink_hz as f64 * 3.0).floor() as i64 % 3 != 0
@@ -205,6 +210,7 @@ impl<'t> Widget for TextEdit<'t> {
                 true
             };
 
+
             if show_cursor {
                 if let Some(cursor) = state.cursor {
                     let cursor_pos = response.rect.min + galley.char_start_pos(cursor);
@@ -215,11 +221,11 @@ impl<'t> Widget for TextEdit<'t> {
                 }
             }
         }
-
+        */
         let text_color = text_color
             .or(ui.style().visuals.override_text_color)
             .unwrap_or_else(|| visuals.text_color());
-        painter.galley(response.rect.min, galley, text_style, text_color);
+        painter.layout(response.rect.min, layout, text_style, text_color);
         ui.memory().text_edit.insert(id, state);
         response
     }