//! TrueType outline types. use std::mem::size_of; use super::super::{ path::{to_path, ToPathError}, pen::PathStyle, DrawError, Hinting, OutlinePen, }; use raw::tables::glyf::PointCoord; use read_fonts::{ tables::glyf::{Glyph, PointFlags}, types::{F26Dot6, Fixed, GlyphId, Point}, }; /// Maximum number of points we support in a single outline including /// composites. /// /// TrueType uses a 16 bit integer to store contour end points so /// we must keep the total count within this value. /// /// The maxp /// table encodes `maxCompositePoints` as a `uint16` so the spec enforces /// this limit. const MAX_POINTS: usize = u16::MAX as usize; /// Represents the information necessary to scale a glyph outline. /// /// Contains a reference to the glyph data itself as well as metrics that /// can be used to compute the memory requirements for scaling the glyph. #[derive(Clone, Default)] pub struct Outline<'a> { pub glyph_id: GlyphId, /// The associated top-level glyph for the outline. pub glyph: Option>, /// Sum of the point counts of all simple glyphs in an outline. pub points: usize, /// Sum of the contour counts of all simple glyphs in an outline. pub contours: usize, /// Maximum number of points in a single simple glyph. pub max_simple_points: usize, /// "Other" points are the unscaled or original scaled points. /// /// The size of these buffer is the same and this value tracks the size /// for one (not both) of the buffers. This is the maximum of /// `max_simple_points` and the total number of points for all component /// glyphs in a single composite glyph. pub max_other_points: usize, /// Maximum size of the component delta stack. /// /// For composite glyphs in variable fonts, delta values are computed /// for each component. This tracks the maximum stack depth necessary /// to store those values during processing. pub max_component_delta_stack: usize, /// Number of entries in the hinting value stack. pub max_stack: usize, /// Number of CVT entries for copy-on-write support. pub cvt_count: usize, /// Number of storage area entries for copy-on-write support. pub storage_count: usize, /// Maximum number of points in the twilight zone for hinting. pub max_twilight_points: usize, /// True if any component of a glyph has bytecode instructions. pub has_hinting: bool, /// True if the glyph requires variation delta processing. pub has_variations: bool, /// True if the glyph contains any simple or compound overlap flags. pub has_overlaps: bool, } impl Outline<'_> { /// Returns the minimum size in bytes required to scale an outline based /// on the computed sizes. pub fn required_buffer_size(&self, hinting: Hinting) -> usize { let mut size = 0; let hinting = self.has_hinting && hinting == Hinting::Embedded; // Scaled, unscaled and (for hinting) original scaled points size += self.points * size_of::>(); // Unscaled and (if hinted) original scaled points size += self.max_other_points * size_of::>() * if hinting { 2 } else { 1 }; // Contour end points size += self.contours * size_of::(); // Point flags size += self.points * size_of::(); if self.has_variations { // Interpolation buffer for delta IUP size += self.max_simple_points * size_of::>(); // Delta buffer for points size += self.max_simple_points * size_of::>(); // Delta buffer for composite components size += self.max_component_delta_stack * size_of::>(); } if hinting { // Hinting value stack size += self.max_stack * size_of::(); // CVT and storage area copy-on-write buffers size += (self.cvt_count + self.storage_count) * size_of::(); // Twilight zone storage. Two point buffers plus one point flags buffer size += self.max_twilight_points * (size_of::>() * 2 + size_of::()); } if size != 0 { // If we're given a buffer that is not aligned, we'll need to // adjust, so add our maximum alignment requirement in bytes. size += std::mem::align_of::(); } size } pub(super) fn ensure_point_count_limit(&self) -> Result<(), DrawError> { if self.points > MAX_POINTS { Err(DrawError::TooManyPoints(self.glyph_id)) } else { Ok(()) } } } #[derive(Debug)] pub struct ScaledOutline<'a, C> where C: PointCoord, { pub points: &'a mut [Point], pub flags: &'a mut [PointFlags], pub contours: &'a mut [u16], pub phantom_points: [Point; 4], pub hdmx_width: Option, } impl<'a, C> ScaledOutline<'a, C> where C: PointCoord, { pub(crate) fn new( points: &'a mut [Point], phantom_points: [Point; 4], flags: &'a mut [PointFlags], contours: &'a mut [u16], hdmx_width: Option, ) -> Self { let x_shift = phantom_points[0].x; if x_shift != C::zeroed() { for point in points.iter_mut() { point.x = point.x - x_shift; } } Self { points, flags, contours, phantom_points, hdmx_width, } } pub fn adjusted_lsb(&self) -> C { self.phantom_points[0].x } pub fn adjusted_advance_width(&self) -> C { // Prefer widths from hdmx, otherwise take difference between first // two phantom points // if let Some(hdmx_width) = self.hdmx_width { C::from_i32(hdmx_width as i32) } else { self.phantom_points[1].x - self.phantom_points[0].x } } pub fn to_path( &self, path_style: PathStyle, pen: &mut impl OutlinePen, ) -> Result<(), ToPathError> { to_path(self.points, self.flags, self.contours, path_style, pen) } }