diff options
| author | Laurenz <laurmaedje@gmail.com> | 2021-03-19 17:57:31 +0100 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2021-03-19 17:57:31 +0100 |
| commit | 264a7dedd42e27cd9e604037640cf0594b2ec46b (patch) | |
| tree | d26feea399d54bb86bd44878f40293983bf5251d /src/shaping.rs | |
| parent | ca3df70e2a5069832d7d2135967674c34a155442 (diff) | |
Scheduled maintenance 🔨
- New naming scheme
- TextNode instead of NodeText
- CallExpr instead of ExprCall
- ...
- Less glob imports
- Removes Value::Args variant
- Removes prelude
- Renames Layouted to Fragment
- Moves font into env
- Moves shaping into layout
- Moves frame into separate module
Diffstat (limited to 'src/shaping.rs')
| -rw-r--r-- | src/shaping.rs | 198 |
1 files changed, 0 insertions, 198 deletions
diff --git a/src/shaping.rs b/src/shaping.rs deleted file mode 100644 index 41119639..00000000 --- a/src/shaping.rs +++ /dev/null @@ -1,198 +0,0 @@ -//! Super-basic text shaping. -//! -//! This is really only suited for simple Latin text. It picks the most suitable -//! font for each individual character. When the direction is right-to-left, the -//! word is spelled backwards. Vertical shaping is not supported. - -use std::fmt::{self, Debug, Display, Formatter}; - -use fontdock::{FaceId, FaceQuery, FallbackTree, FontVariant}; -use ttf_parser::{Face, GlyphId}; - -use crate::font::FontLoader; -use crate::geom::{Dir, Length, Point, Size}; -use crate::layout::{Element, Frame}; - -/// A shaped run of text. -#[derive(Clone, PartialEq)] -pub struct Shaped { - /// The shaped text. - pub text: String, - /// The font face the text was shaped with. - pub face: FaceId, - /// The shaped glyphs. - pub glyphs: Vec<GlyphId>, - /// The horizontal offsets of the glyphs. This is indexed parallel to - /// `glyphs`. Vertical offsets are not yet supported. - pub offsets: Vec<Length>, - /// The font size. - pub font_size: Length, -} - -impl Shaped { - /// Create a new shape run with empty `text`, `glyphs` and `offsets`. - pub fn new(face: FaceId, font_size: Length) -> Self { - Self { - text: String::new(), - face, - glyphs: vec![], - offsets: vec![], - font_size, - } - } - - /// Encode the glyph ids into a big-endian byte buffer. - pub fn encode_glyphs_be(&self) -> Vec<u8> { - let mut bytes = Vec::with_capacity(2 * self.glyphs.len()); - for &GlyphId(g) in &self.glyphs { - bytes.push((g >> 8) as u8); - bytes.push((g & 0xff) as u8); - } - bytes - } -} - -impl Debug for Shaped { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - write!(f, "Shaped({})", self.text) - } -} - -/// Identifies a vertical metric of a font. -#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)] -pub enum VerticalFontMetric { - /// The distance from the baseline to the typographic ascender. - /// - /// Corresponds to the typographic ascender from the `OS/2` table if present - /// and falls back to the ascender from the `hhea` table otherwise. - Ascender, - /// The approximate height of uppercase letters. - CapHeight, - /// The approximate height of non-ascending lowercase letters. - XHeight, - /// The baseline on which the letters rest. - Baseline, - /// The distance from the baseline to the typographic descender. - /// - /// Corresponds to the typographic descender from the `OS/2` table if - /// present and falls back to the descender from the `hhea` table otherwise. - Descender, -} - -impl Display for VerticalFontMetric { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - f.pad(match self { - Self::Ascender => "ascender", - Self::CapHeight => "cap-height", - Self::XHeight => "x-height", - Self::Baseline => "baseline", - Self::Descender => "descender", - }) - } -} - -/// Shape text into a frame containing [`Shaped`] runs. -pub fn shape( - text: &str, - dir: Dir, - fallback: &FallbackTree, - variant: FontVariant, - font_size: Length, - top_edge: VerticalFontMetric, - bottom_edge: VerticalFontMetric, - loader: &mut FontLoader, -) -> Frame { - let mut frame = Frame::new(Size::new(Length::ZERO, Length::ZERO)); - let mut shaped = Shaped::new(FaceId::MAX, font_size); - let mut width = Length::ZERO; - let mut top = Length::ZERO; - let mut bottom = Length::ZERO; - - // 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) = loader.query(query) { - let face = loader.face(id).get(); - let (glyph, glyph_width) = match lookup_glyph(face, c) { - Some(v) => v, - None => continue, - }; - - let units_per_em = f64::from(face.units_per_em().unwrap_or(1000)); - let convert = |units| units / units_per_em * font_size; - - // Flush the buffer and reset the metrics if we use a new font face. - if shaped.face != id { - place(&mut frame, shaped, width, top, bottom); - - shaped = Shaped::new(id, font_size); - width = Length::ZERO; - top = convert(f64::from(lookup_metric(face, top_edge))); - bottom = convert(f64::from(lookup_metric(face, bottom_edge))); - } - - shaped.text.push(c); - shaped.glyphs.push(glyph); - shaped.offsets.push(width); - width += convert(f64::from(glyph_width)); - } - } - - place(&mut frame, shaped, width, top, bottom); - - frame -} - -/// Place shaped text into a frame. -fn place(frame: &mut Frame, shaped: Shaped, width: Length, top: Length, bottom: Length) { - if !shaped.text.is_empty() { - frame.push(Point::new(frame.size.width, top), Element::Text(shaped)); - frame.size.width += width; - frame.size.height = frame.size.height.max(top - bottom); - } -} - -/// Look up the glyph for `c` and returns its index alongside its advance width. -fn lookup_glyph(face: &Face, c: char) -> Option<(GlyphId, u16)> { - let glyph = face.glyph_index(c)?; - let width = face.glyph_hor_advance(glyph)?; - Some((glyph, width)) -} - -/// Look up a vertical metric. -fn lookup_metric(face: &Face, metric: VerticalFontMetric) -> i16 { - match metric { - VerticalFontMetric::Ascender => lookup_ascender(face), - VerticalFontMetric::CapHeight => face - .capital_height() - .filter(|&h| h > 0) - .unwrap_or_else(|| lookup_ascender(face)), - VerticalFontMetric::XHeight => face - .x_height() - .filter(|&h| h > 0) - .unwrap_or_else(|| lookup_ascender(face)), - VerticalFontMetric::Baseline => 0, - VerticalFontMetric::Descender => lookup_descender(face), - } -} - -/// The ascender of the face. -fn lookup_ascender(face: &Face) -> i16 { - // We prefer the typographic ascender over the Windows ascender because - // it can be overly large if the font has large glyphs. - face.typographic_ascender().unwrap_or_else(|| face.ascender()) -} - -/// The descender of the face. -fn lookup_descender(face: &Face) -> i16 { - // See `lookup_ascender` for reason. - face.typographic_descender().unwrap_or_else(|| face.descender()) -} |
