From 3562ef41867a6748a376a0bb800e74aafcd994eb Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Thu, 6 Jun 2024 14:40:35 -0600 Subject: [PATCH 1/6] Add metrics to attributes --- examples/rich-text/src/main.rs | 7 +++++ src/attrs.rs | 39 +++++++++++++++++++++++- src/buffer.rs | 10 +++++++ src/shape.rs | 54 ++++++++++++++++++---------------- 4 files changed, 84 insertions(+), 26 deletions(-) diff --git a/examples/rich-text/src/main.rs b/examples/rich-text/src/main.rs index 671fa96032..4762e43606 100644 --- a/examples/rich-text/src/main.rs +++ b/examples/rich-text/src/main.rs @@ -26,6 +26,13 @@ fn set_buffer_text<'a>(buffer: &mut BorrowedWithFontSystem<'a, Buffer>) { let comic_attrs = attrs.family(Family::Name("Comic Neue")); let spans: &[(&str, Attrs)] = &[ + ("Font size 8 ", attrs.metrics(Metrics::relative(8.0, 1.2))), + ("Font size 20 ", attrs.metrics(Metrics::relative(20.0, 1.2))), + ("Font size 14 ", attrs.metrics(Metrics::relative(14.0, 1.2))), + ( + "Font size 48\n", + attrs.metrics(Metrics::relative(48.0, 1.2)), + ), ("B", attrs.weight(Weight::BOLD)), ("old ", attrs), ("I", attrs.style(Style::Italic)), diff --git a/src/attrs.rs b/src/attrs.rs index 16949200f4..d241fe4f1a 100644 --- a/src/attrs.rs +++ b/src/attrs.rs @@ -8,7 +8,7 @@ use alloc::{ use core::ops::Range; use rangemap::RangeMap; -use crate::CacheKeyFlags; +use crate::{CacheKeyFlags, Metrics}; pub use fontdb::{Family, Stretch, Style, Weight}; @@ -101,6 +101,32 @@ impl FamilyOwned { } } +/// Metrics, but implementing Eq and Hash using u32 representation of f32 +//TODO: what are the edge cases of this? +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub struct CacheMetrics { + pub font_size_bits: u32, + pub line_height_bits: u32, +} + +impl From for CacheMetrics { + fn from(metrics: Metrics) -> Self { + Self { + font_size_bits: metrics.font_size.to_bits(), + line_height_bits: metrics.line_height.to_bits(), + } + } +} + +impl From for Metrics { + fn from(metrics: CacheMetrics) -> Self { + Self { + font_size: f32::from_bits(metrics.font_size_bits), + line_height: f32::from_bits(metrics.line_height_bits), + } + } +} + /// Text attributes #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub struct Attrs<'a> { @@ -112,6 +138,7 @@ pub struct Attrs<'a> { pub weight: Weight, pub metadata: usize, pub cache_key_flags: CacheKeyFlags, + pub metrics_opt: Option, } impl<'a> Attrs<'a> { @@ -127,6 +154,7 @@ impl<'a> Attrs<'a> { weight: Weight::NORMAL, metadata: 0, cache_key_flags: CacheKeyFlags::empty(), + metrics_opt: None, } } @@ -172,6 +200,12 @@ impl<'a> Attrs<'a> { self } + /// Set [`Metrics`], overriding values in buffer + pub fn metrics(mut self, metrics: Metrics) -> Self { + self.metrics_opt = Some(metrics.into()); + self + } + /// Check if font matches pub fn matches(&self, face: &fontdb::FaceInfo) -> bool { //TODO: smarter way of including emoji @@ -219,6 +253,7 @@ pub struct AttrsOwned { pub weight: Weight, pub metadata: usize, pub cache_key_flags: CacheKeyFlags, + pub metrics_opt: Option, } impl AttrsOwned { @@ -231,6 +266,7 @@ impl AttrsOwned { weight: attrs.weight, metadata: attrs.metadata, cache_key_flags: attrs.cache_key_flags, + metrics_opt: attrs.metrics_opt, } } @@ -243,6 +279,7 @@ impl AttrsOwned { weight: self.weight, metadata: self.metadata, cache_key_flags: self.cache_key_flags, + metrics_opt: self.metrics_opt, } } } diff --git a/src/buffer.rs b/src/buffer.rs index 31e370c4ef..8da5942c9a 100644 --- a/src/buffer.rs +++ b/src/buffer.rs @@ -199,6 +199,7 @@ pub struct Metrics { } impl Metrics { + /// Create metrics with given font size and line height pub const fn new(font_size: f32, line_height: f32) -> Self { Self { font_size, @@ -206,6 +207,15 @@ impl Metrics { } } + /// Create metrics with given font size and calculate line height using relative scale + pub fn relative(font_size: f32, line_height_scale: f32) -> Self { + Self { + font_size, + line_height: font_size * line_height_scale, + } + } + + /// Scale font size and line height pub fn scale(self, scale: f32) -> Self { Self { font_size: self.font_size * scale, diff --git a/src/shape.rs b/src/shape.rs index ac28287792..75ab7f0ec1 100644 --- a/src/shape.rs +++ b/src/shape.rs @@ -14,7 +14,7 @@ use unicode_segmentation::UnicodeSegmentation; use crate::fallback::FontFallbackIter; use crate::{ math, Align, AttrsList, CacheKeyFlags, Color, Font, FontSystem, LayoutGlyph, LayoutLine, - ShapePlanCache, Wrap, + Metrics, ShapePlanCache, Wrap, }; /// The shaping strategy of some text. @@ -163,6 +163,7 @@ fn shape_fallback( color_opt: attrs.color_opt, metadata: attrs.metadata, cache_key_flags: attrs.cache_key_flags, + metrics_opt: attrs.metrics_opt.map(|x| x.into()), }); } @@ -463,6 +464,7 @@ fn shape_skip( color_opt: attrs.color_opt, metadata: attrs.metadata, cache_key_flags: attrs.cache_key_flags, + metrics_opt: attrs.metrics_opt.map(|x| x.into()), } }), ); @@ -485,6 +487,7 @@ pub struct ShapeGlyph { pub color_opt: Option, pub metadata: usize, pub cache_key_flags: CacheKeyFlags, + pub metrics_opt: Option, } impl ShapeGlyph { @@ -513,6 +516,10 @@ impl ShapeGlyph { cache_key_flags: self.cache_key_flags, } } + + pub fn width(&self, font_size: f32) -> f32 { + self.metrics_opt.map_or(font_size, |x| x.font_size) * self.x_advance + } } /// A shaped word (for word wrapping) @@ -520,8 +527,6 @@ impl ShapeGlyph { pub struct ShapeWord { pub blank: bool, pub glyphs: Vec, - pub x_advance: f32, - pub y_advance: f32, } impl ShapeWord { @@ -603,19 +608,15 @@ impl ShapeWord { ); } - let mut x_advance = 0.0; - let mut y_advance = 0.0; - for glyph in &glyphs { - x_advance += glyph.x_advance; - y_advance += glyph.y_advance; - } + Self { blank, glyphs } + } - Self { - blank, - glyphs, - x_advance, - y_advance, + pub fn width(&self, font_size: f32) -> f32 { + let mut width = 0.0; + for glyph in self.glyphs.iter() { + width += glyph.width(font_size); } + width } } @@ -1025,7 +1026,7 @@ impl ShapeLine { let mut word_range_width = 0.; let mut number_of_blanks: u32 = 0; for word in span.words.iter() { - let word_width = font_size * word.x_advance; + let word_width = word.width(font_size); word_range_width += word_width; if word.blank { number_of_blanks += 1; @@ -1051,7 +1052,7 @@ impl ShapeLine { // incongruent directions let mut fitting_start = (span.words.len(), 0); for (i, word) in span.words.iter().enumerate().rev() { - let word_width = font_size * word.x_advance; + let word_width = word.width(font_size); // Addition in the same order used to compute the final width, so that // relayouts with that width as the `line_width` will produce the same @@ -1098,7 +1099,7 @@ impl ShapeLine { } for (glyph_i, glyph) in word.glyphs.iter().enumerate().rev() { - let glyph_width = font_size * glyph.x_advance; + let glyph_width = glyph.width(font_size); if current_visual_line.w + (word_range_width + glyph_width) <= line_width { @@ -1179,7 +1180,7 @@ impl ShapeLine { // congruent direction let mut fitting_start = (0, 0); for (i, word) in span.words.iter().enumerate() { - let word_width = font_size * word.x_advance; + let word_width = word.width(font_size); if current_visual_line.w + (word_range_width + word_width) <= line_width // Include one blank word over the width limit since it won't be @@ -1222,7 +1223,7 @@ impl ShapeLine { } for (glyph_i, glyph) in word.glyphs.iter().enumerate() { - let glyph_width = font_size * glyph.x_advance; + let glyph_width = glyph.width(font_size); if current_visual_line.w + (word_range_width + glyph_width) <= line_width { @@ -1383,9 +1384,12 @@ impl ShapeLine { (true, true) => &word.glyphs[starting_glyph..ending_glyph], }; - let match_mono_em_width = match_mono_width.map(|w| w / font_size); - for glyph in included_glyphs { + // Use overridden font size + let font_size = glyph.metrics_opt.map_or(font_size, |x| x.font_size); + + let match_mono_em_width = match_mono_width.map(|w| w / font_size); + let glyph_font_size = match ( match_mono_em_width, glyph.font_monospace_em_width, @@ -1419,8 +1423,8 @@ impl ShapeLine { x += x_advance; } y += y_advance; - max_ascent = max_ascent.max(glyph.ascent); - max_descent = max_descent.max(glyph.descent); + max_ascent = max_ascent.max(glyph_font_size * glyph.ascent); + max_descent = max_descent.max(glyph_font_size * glyph.descent); } } } @@ -1445,8 +1449,8 @@ impl ShapeLine { } else { x }, - max_ascent: max_ascent * font_size, - max_descent: max_descent * font_size, + max_ascent, + max_descent, glyphs, }); } From 95fe88a0d97a4e4e04b5ae4e7f523938e96ce3d6 Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Thu, 6 Jun 2024 15:21:44 -0600 Subject: [PATCH 2/6] Use line height from attrs --- examples/rich-text/src/main.rs | 1 + src/buffer.rs | 86 ++++++++++++---------------------- src/edit/editor.rs | 2 +- src/edit/vi.rs | 2 +- src/layout.rs | 2 + src/shape.rs | 11 ++++- 6 files changed, 44 insertions(+), 60 deletions(-) diff --git a/examples/rich-text/src/main.rs b/examples/rich-text/src/main.rs index 4762e43606..2c42b73eb2 100644 --- a/examples/rich-text/src/main.rs +++ b/examples/rich-text/src/main.rs @@ -26,6 +26,7 @@ fn set_buffer_text<'a>(buffer: &mut BorrowedWithFontSystem<'a, Buffer>) { let comic_attrs = attrs.family(Family::Name("Comic Neue")); let spans: &[(&str, Attrs)] = &[ + ("Font size 64 ", attrs.metrics(Metrics::relative(64.0, 1.2))), ("Font size 8 ", attrs.metrics(Metrics::relative(8.0, 1.2))), ("Font size 20 ", attrs.metrics(Metrics::relative(20.0, 1.2))), ("Font size 14 ", attrs.metrics(Metrics::relative(14.0, 1.2))), diff --git a/src/buffer.rs b/src/buffer.rs index 8da5942c9a..46f70a5df8 100644 --- a/src/buffer.rs +++ b/src/buffer.rs @@ -26,6 +26,8 @@ pub struct LayoutRun<'a> { pub line_y: f32, /// Y offset to top of line pub line_top: f32, + /// Y offset to next line + pub line_height: f32, /// Width of line pub line_w: f32, } @@ -92,43 +94,18 @@ pub struct LayoutRunIter<'b> { buffer: &'b Buffer, line_i: usize, layout_i: usize, - remaining_len: usize, total_layout: i32, + line_top: f32, } impl<'b> LayoutRunIter<'b> { pub fn new(buffer: &'b Buffer) -> Self { - let total_layout_lines: usize = buffer - .lines - .iter() - .skip(buffer.scroll.line) - .map(|line| { - line.layout_opt() - .as_ref() - .map(|layout| layout.len()) - .unwrap_or_default() - }) - .sum(); - let top_cropped_layout_lines = - total_layout_lines.saturating_sub(buffer.scroll.layout.try_into().unwrap_or_default()); - let maximum_lines = if buffer.metrics.line_height == 0.0 { - 0 - } else { - (buffer.height / buffer.metrics.line_height) as i32 - }; - let bottom_cropped_layout_lines = - if top_cropped_layout_lines > maximum_lines.try_into().unwrap_or_default() { - maximum_lines.try_into().unwrap_or_default() - } else { - top_cropped_layout_lines - }; - Self { buffer, line_i: buffer.scroll.line, layout_i: 0, - remaining_len: bottom_cropped_layout_lines, total_layout: 0, + line_top: 0.0, } } } @@ -136,10 +113,6 @@ impl<'b> LayoutRunIter<'b> { impl<'b> Iterator for LayoutRunIter<'b> { type Item = LayoutRun<'b>; - fn size_hint(&self) -> (usize, Option) { - (self.remaining_len, Some(self.remaining_len)) - } - fn next(&mut self) -> Option { while let Some(line) = self.buffer.lines.get(self.line_i) { let shape = line.shape_opt().as_ref()?; @@ -153,30 +126,33 @@ impl<'b> Iterator for LayoutRunIter<'b> { continue; } - let line_top = self - .total_layout - .saturating_sub(self.buffer.scroll.layout) - .saturating_sub(1) as f32 - * self.buffer.metrics.line_height; + let mut line_height = self.buffer.metrics.line_height; + for glyph in layout_line.glyphs.iter() { + if let Some(glyph_line_height) = glyph.line_height_opt { + line_height = line_height.max(glyph_line_height); + } + } + + let line_top = self.line_top; let glyph_height = layout_line.max_ascent + layout_line.max_descent; - let centering_offset = (self.buffer.metrics.line_height - glyph_height) / 2.0; + let centering_offset = (line_height - glyph_height) / 2.0; let line_y = line_top + centering_offset + layout_line.max_ascent; if line_top + centering_offset > self.buffer.height { return None; } - return self.remaining_len.checked_sub(1).map(|num| { - self.remaining_len = num; - LayoutRun { - line_i: self.line_i, - text: line.text(), - rtl: shape.rtl, - glyphs: &layout_line.glyphs, - line_y, - line_top, - line_w: layout_line.w, - } + self.line_top += line_height; + + return Some(LayoutRun { + line_i: self.line_i, + text: line.text(), + rtl: shape.rtl, + glyphs: &layout_line.glyphs, + line_y, + line_top, + line_height, + line_w: layout_line.w, }); } self.line_i += 1; @@ -187,8 +163,6 @@ impl<'b> Iterator for LayoutRunIter<'b> { } } -impl<'b> ExactSizeIterator for LayoutRunIter<'b> {} - /// Metrics of text #[derive(Clone, Copy, Debug, Default, PartialEq)] pub struct Metrics { @@ -798,21 +772,19 @@ impl Buffer { #[cfg(all(feature = "std", not(target_arch = "wasm32")))] let instant = std::time::Instant::now(); - let font_size = self.metrics.font_size; - let line_height = self.metrics.line_height; - let mut new_cursor_opt = None; let mut runs = self.layout_runs().peekable(); let mut first_run = true; while let Some(run) = runs.next() { - let line_y = run.line_y; + let line_top = run.line_top; + let line_height = run.line_height; - if first_run && y < line_y - font_size { + if first_run && y < line_top { first_run = false; let new_cursor = Cursor::new(run.line_i, 0); new_cursor_opt = Some(new_cursor); - } else if y >= line_y - font_size && y < line_y - font_size + line_height { + } else if y >= line_top && y < line_top + line_height { let mut new_cursor_glyph = run.glyphs.len(); let mut new_cursor_char = 0; let mut new_cursor_affinity = Affinity::After; @@ -1128,7 +1100,7 @@ impl Buffer { )?; } Motion::Vertical(px) => { - // TODO more efficient + // TODO more efficient, use layout run line height let lines = px / self.metrics().line_height as i32; match lines.cmp(&0) { cmp::Ordering::Less => { diff --git a/src/edit/editor.rs b/src/edit/editor.rs index cf7690159b..c7784d92bb 100644 --- a/src/edit/editor.rs +++ b/src/edit/editor.rs @@ -57,11 +57,11 @@ impl<'buffer> Editor<'buffer> { F: FnMut(i32, i32, u32, u32, Color), { self.with_buffer(|buffer| { - let line_height = buffer.metrics().line_height; for run in buffer.layout_runs() { let line_i = run.line_i; let line_y = run.line_y; let line_top = run.line_top; + let line_height = run.line_height; let cursor_glyph_opt = |cursor: &Cursor| -> Option<(usize, f32)> { if cursor.line == line_i { diff --git a/src/edit/vi.rs b/src/edit/vi.rs index e1913ccd2f..92b0480cc2 100644 --- a/src/edit/vi.rs +++ b/src/edit/vi.rs @@ -312,11 +312,11 @@ impl<'syntax_system, 'buffer> ViEditor<'syntax_system, 'buffer> { let size = buffer.size(); f(0, 0, size.0 as u32, size.1 as u32, background_color); let font_size = buffer.metrics().font_size; - let line_height = buffer.metrics().line_height; for run in buffer.layout_runs() { let line_i = run.line_i; let line_y = run.line_y; let line_top = run.line_top; + let line_height = run.line_height; let cursor_glyph_opt = |cursor: &Cursor| -> Option<(usize, f32, f32)> { //TODO: better calculation of width diff --git a/src/layout.rs b/src/layout.rs index 99a78de088..64226b520a 100644 --- a/src/layout.rs +++ b/src/layout.rs @@ -16,6 +16,8 @@ pub struct LayoutGlyph { pub end: usize, /// Font size of the glyph pub font_size: f32, + /// Line height of the glyph, will override buffer setting + pub line_height_opt: Option, /// Font id of the glyph pub font_id: fontdb::ID, /// Font id of the glyph diff --git a/src/shape.rs b/src/shape.rs index 75ab7f0ec1..ef834d84c1 100644 --- a/src/shape.rs +++ b/src/shape.rs @@ -494,6 +494,7 @@ impl ShapeGlyph { fn layout( &self, font_size: f32, + line_height_opt: Option, x: f32, y: f32, w: f32, @@ -503,6 +504,7 @@ impl ShapeGlyph { start: self.start, end: self.end, font_size, + line_height_opt, font_id: self.font_id, glyph_id: self.glyph_id, x, @@ -1418,7 +1420,14 @@ impl ShapeLine { x -= x_advance; } let y_advance = glyph_font_size * glyph.y_advance; - glyphs.push(glyph.layout(glyph_font_size, x, y, x_advance, span.level)); + glyphs.push(glyph.layout( + glyph_font_size, + glyph.metrics_opt.map(|x| x.line_height), + x, + y, + x_advance, + span.level, + )); if !self.rtl { x += x_advance; } From 5794a681977da0660250cd8cff810437f8568447 Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Thu, 6 Jun 2024 15:31:31 -0600 Subject: [PATCH 3/6] Embed font for wrap_word_fallback test --- fonts/Inter-LICENSE | 93 +++++++++++++++++++++++++++++++++++++ fonts/Inter-Regular.ttf | 3 ++ tests/wrap_word_fallback.rs | 5 +- 3 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 fonts/Inter-LICENSE create mode 100644 fonts/Inter-Regular.ttf diff --git a/fonts/Inter-LICENSE b/fonts/Inter-LICENSE new file mode 100644 index 0000000000..d05ec4b38c --- /dev/null +++ b/fonts/Inter-LICENSE @@ -0,0 +1,93 @@ +Copyright 2020 The Inter Project Authors (https://github.com/rsms/inter) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +https://openfontlicense.org + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/fonts/Inter-Regular.ttf b/fonts/Inter-Regular.ttf new file mode 100644 index 0000000000..b9750f3137 --- /dev/null +++ b/fonts/Inter-Regular.ttf @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3127f0b873387ee37e2040135a06e9e9c05030f509eb63689529becf28b50384 +size 310252 diff --git a/tests/wrap_word_fallback.rs b/tests/wrap_word_fallback.rs index c3a824b7aa..b613fe2e95 100644 --- a/tests/wrap_word_fallback.rs +++ b/tests/wrap_word_fallback.rs @@ -4,7 +4,10 @@ use cosmic_text::{Attrs, Buffer, FontSystem, Metrics, Shaping, Wrap}; // No line should ever overflow the buffer size. #[test] fn wrap_word_fallback() { - let mut font_system = FontSystem::new(); + let mut font_system = + FontSystem::new_with_locale_and_db("en-US".into(), fontdb::Database::new()); + let font = std::fs::read("fonts/Inter-Regular.ttf").unwrap(); + font_system.db_mut().load_font_data(font); let metrics = Metrics::new(14.0, 20.0); let mut buffer = Buffer::new(&mut font_system, metrics); From 268f5079702a223c23530d83996d5145bc3be465 Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Thu, 6 Jun 2024 15:37:07 -0600 Subject: [PATCH 4/6] Address review --- src/attrs.rs | 4 ++-- src/shape.rs | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/attrs.rs b/src/attrs.rs index d241fe4f1a..ea8b671376 100644 --- a/src/attrs.rs +++ b/src/attrs.rs @@ -105,8 +105,8 @@ impl FamilyOwned { //TODO: what are the edge cases of this? #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub struct CacheMetrics { - pub font_size_bits: u32, - pub line_height_bits: u32, + font_size_bits: u32, + line_height_bits: u32, } impl From for CacheMetrics { diff --git a/src/shape.rs b/src/shape.rs index ef834d84c1..e631f16cef 100644 --- a/src/shape.rs +++ b/src/shape.rs @@ -519,6 +519,8 @@ impl ShapeGlyph { } } + /// Get the width of the [`ShapeGlyph`] in pixels, either using the provided font size + /// or the [`ShapeGlyph::metrics_opt`] override. pub fn width(&self, font_size: f32) -> f32 { self.metrics_opt.map_or(font_size, |x| x.font_size) * self.x_advance } @@ -613,6 +615,7 @@ impl ShapeWord { Self { blank, glyphs } } + /// Get the width of the [`ShapeWord`] in pixels, using the [`ShapeGlyph::width`] function. pub fn width(&self, font_size: f32) -> f32 { let mut width = 0.0; for glyph in self.glyphs.iter() { From 8e1db4ba36bb33055fab3f09725a79cacef73140 Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Thu, 6 Jun 2024 15:52:52 -0600 Subject: [PATCH 5/6] Fall back to buffer line height only if no glyphs found --- src/buffer.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/buffer.rs b/src/buffer.rs index 46f70a5df8..fec3c31011 100644 --- a/src/buffer.rs +++ b/src/buffer.rs @@ -126,13 +126,17 @@ impl<'b> Iterator for LayoutRunIter<'b> { continue; } - let mut line_height = self.buffer.metrics.line_height; + let mut line_height_opt: Option = None; for glyph in layout_line.glyphs.iter() { if let Some(glyph_line_height) = glyph.line_height_opt { - line_height = line_height.max(glyph_line_height); + line_height_opt = match line_height_opt { + Some(line_height) => Some(line_height.max(glyph_line_height)), + None => Some(glyph_line_height) + }; } } + let line_height = line_height_opt.unwrap_or(self.buffer.metrics.line_height); let line_top = self.line_top; let glyph_height = layout_line.max_ascent + layout_line.max_descent; let centering_offset = (line_height - glyph_height) / 2.0; From 2e089c6d90c815686de31b136c65edd1eaac78f3 Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Thu, 6 Jun 2024 15:54:04 -0600 Subject: [PATCH 6/6] Format --- src/buffer.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/buffer.rs b/src/buffer.rs index fec3c31011..9b844e576d 100644 --- a/src/buffer.rs +++ b/src/buffer.rs @@ -131,7 +131,7 @@ impl<'b> Iterator for LayoutRunIter<'b> { if let Some(glyph_line_height) = glyph.line_height_opt { line_height_opt = match line_height_opt { Some(line_height) => Some(line_height.max(glyph_line_height)), - None => Some(glyph_line_height) + None => Some(glyph_line_height), }; } }