Add metrics to attributes
This commit is contained in:
parent
89503b254f
commit
8638ec29bb
4 changed files with 84 additions and 26 deletions
|
|
@ -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)),
|
||||
|
|
|
|||
39
src/attrs.rs
39
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<Metrics> 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<CacheMetrics> 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<CacheMetrics>,
|
||||
}
|
||||
|
||||
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<CacheMetrics>,
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
54
src/shape.rs
54
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<Color>,
|
||||
pub metadata: usize,
|
||||
pub cache_key_flags: CacheKeyFlags,
|
||||
pub metrics_opt: Option<Metrics>,
|
||||
}
|
||||
|
||||
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<ShapeGlyph>,
|
||||
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,
|
||||
});
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue