read-fonts 0.46.0

Reading OpenType font files.
Documentation
//! Path commands for outlines and sinks that receive them.

use alloc::{string::String, vec::Vec};
use core::fmt::{self, Write};
use types::{BoundingBox, Matrix};

/// Receives the path commands for an outline.
///
/// To receive the outline's points in font units, use
/// [`UnscaledOutlineSink`](super::UnscaledOutlineSink).
pub trait PathSink {
    /// Emit a command to begin a new subpath at (x, y).
    fn move_to(&mut self, x: f32, y: f32);

    /// Emit a line segment from the current point to (x, y).
    fn line_to(&mut self, x: f32, y: f32);

    /// Emit a quadratic bezier segment from the current point with a control
    /// point at (cx0, cy0) and ending at (x, y).
    fn quad_to(&mut self, cx0: f32, cy0: f32, x: f32, y: f32);

    /// Emit a cubic bezier segment from the current point with control
    /// points at (cx0, cy0) and (cx1, cy1) and ending at (x, y).
    fn curve_to(&mut self, cx0: f32, cy0: f32, cx1: f32, cy1: f32, x: f32, y: f32);

    /// Emit a command to close the current subpath.
    fn close(&mut self);
}

/// Single element of a path.
#[derive(Copy, Clone, PartialEq, PartialOrd, Debug)]
pub enum PathElement {
    /// Begin a new subpath at (x, y).
    MoveTo { x: f32, y: f32 },
    /// Draw a line from the current point to (x, y).
    LineTo { x: f32, y: f32 },
    /// Draw a quadratic bezier from the current point with a control point at
    /// (cx0, cy0) and ending at (x, y).
    QuadTo { cx0: f32, cy0: f32, x: f32, y: f32 },
    /// Draw a cubic bezier from the current point with control points at
    /// (cx0, cy0) and (cx1, cy1) and ending at (x, y).
    CurveTo {
        cx0: f32,
        cy0: f32,
        cx1: f32,
        cy1: f32,
        x: f32,
        y: f32,
    },
    /// Close the current subpath.
    Close,
}

impl PathSink for Vec<PathElement> {
    fn move_to(&mut self, x: f32, y: f32) {
        self.push(PathElement::MoveTo { x, y })
    }

    fn line_to(&mut self, x: f32, y: f32) {
        self.push(PathElement::LineTo { x, y })
    }

    fn quad_to(&mut self, cx0: f32, cy0: f32, x: f32, y: f32) {
        self.push(PathElement::QuadTo { cx0, cy0, x, y })
    }

    fn curve_to(&mut self, cx0: f32, cy0: f32, cx1: f32, cy1: f32, x: f32, y: f32) {
        self.push(PathElement::CurveTo {
            cx0,
            cy0,
            cx1,
            cy1,
            x,
            y,
        })
    }

    fn close(&mut self) {
        self.push(PathElement::Close)
    }
}

/// A sink that discards all drawing commands.
pub struct NullSink;

impl PathSink for NullSink {
    fn move_to(&mut self, _x: f32, _y: f32) {}
    fn line_to(&mut self, _x: f32, _y: f32) {}
    fn quad_to(&mut self, _cx0: f32, _cy0: f32, _x: f32, _y: f32) {}
    fn curve_to(&mut self, _cx0: f32, _cy0: f32, _cx1: f32, _cy1: f32, _x: f32, _y: f32) {}
    fn close(&mut self) {}
}

/// A sink that writes SVG style path data.
#[derive(Clone, Default, Debug)]
pub struct SvgSink(String, Option<usize>);

impl SvgSink {
    /// Creates a sink that formats floating point values with the
    /// standard behavior.
    pub fn new() -> Self {
        Self::default()
    }

    /// Creates a sink with the given precision (the number of digits
    /// that will be printed after the decimal).
    pub fn with_precision(precision: usize) -> Self {
        Self(String::default(), Some(precision))
    }

    /// Clears the content of the internal string.
    pub fn clear(&mut self) {
        self.0.clear();
    }

    fn maybe_push_space(&mut self) {
        if !self.0.is_empty() {
            self.0.push(' ');
        }
    }
}

impl core::ops::Deref for SvgSink {
    type Target = str;

    fn deref(&self) -> &Self::Target {
        self.0.as_str()
    }
}

impl PathSink for SvgSink {
    fn move_to(&mut self, x: f32, y: f32) {
        self.maybe_push_space();
        let _ = if let Some(prec) = self.1 {
            write!(self.0, "M{x:.0$},{y:.0$}", prec)
        } else {
            write!(self.0, "M{x},{y}")
        };
    }

    fn line_to(&mut self, x: f32, y: f32) {
        self.maybe_push_space();
        let _ = if let Some(prec) = self.1 {
            write!(self.0, "L{x:.0$},{y:.0$}", prec)
        } else {
            write!(self.0, "L{x},{y}")
        };
    }

    fn quad_to(&mut self, cx0: f32, cy0: f32, x: f32, y: f32) {
        self.maybe_push_space();
        let _ = if let Some(prec) = self.1 {
            write!(self.0, "Q{cx0:.0$},{cy0:.0$} {x:.0$},{y:.0$}", prec)
        } else {
            write!(self.0, "Q{cx0},{cy0} {x},{y}")
        };
    }

    fn curve_to(&mut self, cx0: f32, cy0: f32, cx1: f32, cy1: f32, x: f32, y: f32) {
        self.maybe_push_space();
        let _ = if let Some(prec) = self.1 {
            write!(
                self.0,
                "C{cx0:.0$},{cy0:.0$} {cx1:.0$},{cy1:.0$} {x:.0$},{y:.0$}",
                prec
            )
        } else {
            write!(self.0, "C{cx0},{cy0} {cx1},{cy1} {x},{y}")
        };
    }

    fn close(&mut self) {
        self.maybe_push_space();
        self.0.push('Z');
    }
}

impl AsRef<str> for SvgSink {
    fn as_ref(&self) -> &str {
        self.0.as_ref()
    }
}

impl From<String> for SvgSink {
    fn from(value: String) -> Self {
        Self(value, None)
    }
}

impl From<SvgSink> for String {
    fn from(value: SvgSink) -> Self {
        value.0
    }
}

impl fmt::Display for SvgSink {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.0)
    }
}

/// A sink that collects the control bounds of a glyph outline.
#[derive(Clone, Default, Debug)]
pub struct ControlBoundsSink(Option<BoundingBox<f32>>);

impl ControlBoundsSink {
    /// Creates an empty sink.
    pub fn new() -> Self {
        Self(None)
    }

    /// Returns the bounding box collected by this sink.
    pub fn bounding_box(&self) -> Option<BoundingBox<f32>> {
        self.0
    }

    fn update_bounds(&mut self, x: f32, y: f32) {
        if let Some(bb) = &mut self.0 {
            bb.x_min = bb.x_min.min(x);
            bb.y_min = bb.y_min.min(y);
            bb.x_max = bb.x_max.max(x);
            bb.y_max = bb.y_max.max(y);
        } else {
            self.0 = Some(BoundingBox {
                x_min: x,
                y_min: y,
                x_max: x,
                y_max: y,
            });
        }
    }
}

impl PathSink for ControlBoundsSink {
    fn move_to(&mut self, x: f32, y: f32) {
        self.update_bounds(x, y);
    }

    fn line_to(&mut self, x: f32, y: f32) {
        self.update_bounds(x, y);
    }

    fn quad_to(&mut self, cx0: f32, cy0: f32, x: f32, y: f32) {
        self.update_bounds(cx0, cy0);
        self.update_bounds(x, y);
    }

    fn curve_to(&mut self, cx0: f32, cy0: f32, cx1: f32, cy1: f32, x: f32, y: f32) {
        self.update_bounds(cx0, cy0);
        self.update_bounds(cx1, cy1);
        self.update_bounds(x, y);
    }

    fn close(&mut self) {}
}

/// A sink that applies an affine transform to every point and forwards the
/// result to another sink.
#[derive(Clone, Debug)]
pub struct TransformSink<S> {
    sink: S,
    transform: Matrix<f32>,
}

impl<S> TransformSink<S> {
    /// Creates a sink that applies `transform` to each point and forwards
    /// it to `sink`.
    pub fn new(sink: S, transform: Matrix<f32>) -> Self {
        Self { sink, transform }
    }

    /// Returns the transform applied to every point.
    pub fn transform(&self) -> Matrix<f32> {
        self.transform
    }

    /// Consumes this sink and returns the inner sink.
    pub fn into_inner(self) -> S {
        self.sink
    }
}

impl<S: PathSink> PathSink for TransformSink<S> {
    fn move_to(&mut self, x: f32, y: f32) {
        let (x, y) = self.transform.transform(x, y);
        self.sink.move_to(x, y);
    }

    fn line_to(&mut self, x: f32, y: f32) {
        let (x, y) = self.transform.transform(x, y);
        self.sink.line_to(x, y);
    }

    fn quad_to(&mut self, cx0: f32, cy0: f32, x: f32, y: f32) {
        let (cx0, cy0) = self.transform.transform(cx0, cy0);
        let (x, y) = self.transform.transform(x, y);
        self.sink.quad_to(cx0, cy0, x, y);
    }

    fn curve_to(&mut self, cx0: f32, cy0: f32, cx1: f32, cy1: f32, x: f32, y: f32) {
        let (cx0, cy0) = self.transform.transform(cx0, cy0);
        let (cx1, cy1) = self.transform.transform(cx1, cy1);
        let (x, y) = self.transform.transform(x, y);
        self.sink.curve_to(cx0, cy0, cx1, cy1, x, y);
    }

    fn close(&mut self) {
        self.sink.close();
    }
}

impl<P: PathSink + ?Sized> PathSink for &mut P {
    fn move_to(&mut self, x: f32, y: f32) {
        (**self).move_to(x, y)
    }

    fn line_to(&mut self, x: f32, y: f32) {
        (**self).line_to(x, y)
    }

    fn quad_to(&mut self, cx0: f32, cy0: f32, x: f32, y: f32) {
        (**self).quad_to(cx0, cy0, x, y)
    }

    fn curve_to(&mut self, cx0: f32, cy0: f32, cx1: f32, cy1: f32, x: f32, y: f32) {
        (**self).curve_to(cx0, cy0, cx1, cy1, x, y)
    }

    fn close(&mut self) {
        (**self).close()
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn transform_sink_moves_every_point() {
        // Scale x by 2, shear y by x, and move by (10, 20).
        let transform = Matrix::from_elements([2.0, 1.0, 0.0, 1.0, 10.0, 20.0]);
        let mut path = Vec::new();
        let mut sink = TransformSink::new(&mut path, transform);
        sink.move_to(1.0, 2.0);
        sink.line_to(3.0, 4.0);
        sink.quad_to(1.0, 0.0, 0.0, 1.0);
        sink.curve_to(1.0, 1.0, 2.0, 2.0, 3.0, 3.0);
        sink.close();
        assert_eq!(sink.transform(), transform);
        assert_eq!(
            path,
            [
                PathElement::MoveTo { x: 12.0, y: 23.0 },
                PathElement::LineTo { x: 16.0, y: 27.0 },
                PathElement::QuadTo {
                    cx0: 12.0,
                    cy0: 21.0,
                    x: 10.0,
                    y: 21.0
                },
                PathElement::CurveTo {
                    cx0: 12.0,
                    cy0: 22.0,
                    cx1: 14.0,
                    cy1: 24.0,
                    x: 16.0,
                    y: 26.0
                },
                PathElement::Close,
            ]
        );
    }

    #[test]
    fn svg_sink_precision() {
        let svg_data = [None, Some(1), Some(4)].map(|prec| {
            let mut pen = match prec {
                None => SvgSink::new(),
                Some(prec) => SvgSink::with_precision(prec),
            };
            pen.move_to(1.0, 2.45556);
            pen.line_to(1.2, 4.0);
            pen.quad_to(2.0345, 3.56789, -0.157, -425.07);
            pen.curve_to(-37.0010, 4.5, 2.0, 1.0, -0.5, -0.25);
            pen.close();
            pen.to_string()
        });
        let expected = [
            "M1,2.45556 L1.2,4 Q2.0345,3.56789 -0.157,-425.07 C-37.001,4.5 2,1 -0.5,-0.25 Z", 
            "M1.0,2.5 L1.2,4.0 Q2.0,3.6 -0.2,-425.1 C-37.0,4.5 2.0,1.0 -0.5,-0.2 Z", 
            "M1.0000,2.4556 L1.2000,4.0000 Q2.0345,3.5679 -0.1570,-425.0700 C-37.0010,4.5000 2.0000,1.0000 -0.5000,-0.2500 Z"
        ];
        for (result, expected) in svg_data.iter().zip(&expected) {
            assert_eq!(result, expected);
        }
    }
}