summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2020-10-05 15:35:55 +0200
committerLaurenz <laurmaedje@gmail.com>2020-10-05 15:35:55 +0200
commit2df8b964d0e6f40451183c207a20d0273907a524 (patch)
treed5cbf003dd3fc5665be43ccfc749b6754494f950 /src
parentd1c07260c0b67098a1b778262c76e6f31c5a5240 (diff)
Simplify shaping 🌱
Diffstat (limited to 'src')
-rw-r--r--src/layout/tree.rs6
-rw-r--r--src/shaping.rs170
2 files changed, 63 insertions, 113 deletions
diff --git a/src/layout/tree.rs b/src/layout/tree.rs
index fde7833e..c5b6609b 100644
--- a/src/layout/tree.rs
+++ b/src/layout/tree.rs
@@ -116,11 +116,11 @@ impl<'a> TreeLayouter<'a> {
let boxed = shaping::shape(
text,
- self.ctx.state.sys.primary,
self.ctx.state.text.font_size(),
- variant,
- &self.ctx.state.text.fallback,
+ self.ctx.state.sys.primary,
&mut self.ctx.loader.borrow_mut(),
+ &self.ctx.state.text.fallback,
+ variant,
)
.await;
diff --git a/src/shaping.rs b/src/shaping.rs
index 7c60e0d2..c620f872 100644
--- a/src/shaping.rs
+++ b/src/shaping.rs
@@ -7,24 +7,12 @@
use std::fmt::{self, Debug, Formatter};
use fontdock::{FaceId, FaceQuery, FallbackTree, FontVariant};
-use ttf_parser::GlyphId;
+use ttf_parser::{Face, GlyphId};
use crate::font::FontLoader;
use crate::geom::{Point, Size};
use crate::layout::{BoxLayout, Dir, LayoutElement};
-/// Shape text into a box containing shaped runs.
-pub async fn shape(
- text: &str,
- dir: Dir,
- size: f64,
- variant: FontVariant,
- fallback: &FallbackTree,
- loader: &mut FontLoader,
-) -> BoxLayout {
- Shaper::new(text, dir, size, variant, fallback, loader).shape().await
-}
-
/// A shaped run of text.
#[derive(Clone, PartialEq)]
pub struct Shaped {
@@ -70,113 +58,75 @@ impl Debug for Shaped {
}
}
-/// Performs super-basic text shaping.
-struct Shaper<'a> {
- text: &'a str,
+/// Shape text into a box containing [`Shaped`] runs.
+///
+/// [`Shaped`]: struct.Shaped.html
+pub async fn shape(
+ text: &str,
+ size: f64,
dir: Dir,
+ loader: &mut FontLoader,
+ fallback: &FallbackTree,
variant: FontVariant,
- fallback: &'a FallbackTree,
- loader: &'a mut FontLoader,
- shaped: Shaped,
- layout: BoxLayout,
- offset: f64,
-}
-
-impl<'a> Shaper<'a> {
- fn new(
- text: &'a str,
- dir: Dir,
- size: f64,
- variant: FontVariant,
- fallback: &'a FallbackTree,
- loader: &'a mut FontLoader,
- ) -> Self {
- Self {
- text,
- dir,
- variant,
- fallback,
- loader,
- shaped: Shaped::new(FaceId::MAX, size),
- layout: BoxLayout::new(Size::new(0.0, size)),
- offset: 0.0,
- }
- }
-
- async fn shape(mut self) -> BoxLayout {
- // If the primary axis is negative, we layout the characters reversed.
- if self.dir.is_positive() {
- for c in self.text.chars() {
- self.shape_char(c).await;
- }
- } else {
- for c in self.text.chars().rev() {
- self.shape_char(c).await;
+) -> BoxLayout {
+ let mut layout = BoxLayout::new(Size::new(0.0, size));
+ let mut shaped = Shaped::new(FaceId::MAX, size);
+ let mut offset = 0.0;
+
+ // Create an iterator with conditional direction.
+ let mut forwards = text.chars();
+ let mut backwards = text.chars().rev();
+ let chars: &mut dyn Iterator<Item = char> = if dir.is_positive() {
+ &mut forwards
+ } else {
+ &mut backwards
+ };
+
+ for c in chars {
+ let query = FaceQuery { fallback: fallback.iter(), variant, c };
+ if let Some((id, owned_face)) = loader.query(query).await {
+ let face = owned_face.get();
+ let (glyph, width) = match lookup_glyph(face, c, size) {
+ Some(v) => v,
+ None => continue,
+ };
+
+ // Flush the buffer if we change the font face.
+ if shaped.face != id && !shaped.text.is_empty() {
+ let pos = Point::new(layout.size.width, 0.0);
+ layout.push(pos, LayoutElement::Text(shaped));
+ layout.size.width += offset;
+ shaped = Shaped::new(FaceId::MAX, size);
+ offset = 0.0;
}
- }
- // Flush the last buffered parts of the word.
- if !self.shaped.text.is_empty() {
- let pos = Point::new(self.offset, 0.0);
- self.layout.push(pos, LayoutElement::Text(self.shaped));
+ shaped.face = id;
+ shaped.text.push(c);
+ shaped.glyphs.push(glyph);
+ shaped.offsets.push(offset);
+ offset += width;
}
-
- self.layout
}
- async fn shape_char(&mut self, c: char) {
- let (index, glyph, char_width) = match self.select_font(c).await {
- Some(selected) => selected,
- // TODO: Issue warning about missing character.
- None => return,
- };
-
- // Flush the buffer and issue a font setting action if the font differs
- // from the last character's one.
- if self.shaped.face != index {
- if !self.shaped.text.is_empty() {
- let shaped = std::mem::replace(
- &mut self.shaped,
- Shaped::new(FaceId::MAX, self.layout.size.height),
- );
-
- let pos = Point::new(self.offset, 0.0);
- self.layout.push(pos, LayoutElement::Text(shaped));
- self.offset = self.layout.size.width;
- }
-
- self.shaped.face = index;
- }
-
- self.shaped.text.push(c);
- self.shaped.glyphs.push(glyph);
- self.shaped.offsets.push(self.layout.size.width - self.offset);
-
- self.layout.size.width += char_width;
+ // Flush the last buffered parts of the word.
+ if !shaped.text.is_empty() {
+ let pos = Point::new(layout.size.width, 0.0);
+ layout.push(pos, LayoutElement::Text(shaped));
+ layout.size.width += offset;
}
- async fn select_font(&mut self, c: char) -> Option<(FaceId, GlyphId, f64)> {
- let query = FaceQuery {
- fallback: self.fallback.iter(),
- variant: self.variant,
- c,
- };
-
- if let Some((id, owned_face)) = self.loader.query(query).await {
- let face = owned_face.get();
+ layout
+}
- let units_per_em = face.units_per_em().unwrap_or(1000) as f64;
- let ratio = 1.0 / units_per_em;
- let font_size = self.layout.size.height;
- let to_raw = |x| ratio * x as f64 * font_size;
+/// Looks up the glyph for `c` and returns its index alongside its width at the
+/// given `size`.
+fn lookup_glyph(face: &Face, c: char, size: f64) -> Option<(GlyphId, f64)> {
+ let glyph = face.glyph_index(c)?;
- // Determine the width of the char.
- let glyph = face.glyph_index(c)?;
- let glyph_width = to_raw(face.glyph_hor_advance(glyph)? as i32);
+ // Determine the width of the char.
+ let units_per_em = face.units_per_em().unwrap_or(1000) as f64;
+ let width_units = face.glyph_hor_advance(glyph)? as f64;
+ let width = width_units / units_per_em * size;
- Some((id, glyph, glyph_width))
- } else {
- None
- }
- }
+ Some((glyph, width))
}