/*! Shaper that partitions by font using trait based per-cluster selection. */ use super::{Direction, ShapeContext, Shaper}; use crate::text::cluster::{CharCluster, Parser, Token}; use crate::text::{Codepoint as _, Language, Script}; use crate::{FontRef, Setting, Synthesis}; use core::iter::Copied; use core::slice; /// Trait for types that can select appropriate fonts for character clusters. pub trait Selector { /// Type of font returned by the provider. type SelectedFont: SelectedFont; /// Selects a font for the specified character cluster. fn select_font(&mut self, cluster: &mut CharCluster) -> Option; } /// Trait for a font provided by a font selector. pub trait SelectedFont: PartialEq { /// Returns a reference to the underlying font. fn font(&self) -> FontRef; fn id_override(&self) -> Option<[u64; 2]> { None } /// Returns an optional synthesis state for the font. fn synthesis(&self) -> Option { None } } /// Trait for types that specify shaping options. pub trait ShapeOptions { /// Iterator over the feature settings for a fragment. type Features: Iterator>; /// Iterator over the variation settings for a fragment. type Variations: Iterator>; /// Returns the script of the text in the fragment. fn script(&self) -> Script { Script::Latin } /// Returns the language of the text in the fragment. fn language(&self) -> Option { None } /// Returns the direction for the fragment. fn direction(&self) -> Direction { Direction::LeftToRight } /// Returns the font size for the fragment. fn size(&self) -> f32 { 0. } /// Returns an iterator over the feature settings for the fragment. fn features(&self) -> Self::Features; /// Returns an iterator over the variation settings for the fragment. fn variations(&self) -> Self::Variations; /// Returns true if the shaper should insert dotted circles for broken /// clusters in the fragment. fn insert_dotted_circles(&self) -> bool { false } } /// Simple implementation of the shape options trait. #[derive(Copy, Clone)] pub struct SimpleShapeOptions<'a> { /// Script for the fragment. pub script: Script, /// Language for the fragment. pub language: Option, /// Text direction of the fragment. pub direction: Direction, /// Font size for the fragment. pub size: f32, /// Feature settings for the fragment. pub features: &'a [Setting], /// Variation settings for the fragment. pub variations: &'a [Setting], /// True if the shaper should insert dotted circles for broken clusters. pub insert_dotted_circles: bool, } impl Default for SimpleShapeOptions<'_> { fn default() -> Self { Self { script: Script::Latin, language: None, direction: Direction::LeftToRight, size: 0., features: &[], variations: &[], insert_dotted_circles: false, } } } impl<'a> ShapeOptions for SimpleShapeOptions<'a> { type Features = Copied>>; type Variations = Copied>>; fn script(&self) -> Script { self.script } fn language(&self) -> Option { self.language } fn direction(&self) -> Direction { self.direction } fn size(&self) -> f32 { self.size } fn features(&self) -> Self::Features { self.features.iter().copied() } fn variations(&self) -> Self::Variations { self.variations.iter().copied() } fn insert_dotted_circles(&self) -> bool { self.insert_dotted_circles } } /// Shapes a run of text (provided as a [`Token`] iterator) using the specified [`Selector`] to assign /// per-cluster fonts. Invokes the specified closure `f` for each contiguous fragment with the chosen font /// and a prepared [`Shaper`] that contains the cluster content of the fragment. pub fn shape( context: &mut ShapeContext, selector: &mut S, options: &impl ShapeOptions, tokens: T, mut f: impl FnMut(&S::SelectedFont, Shaper), ) where S: Selector, T: IntoIterator, T::IntoIter: Clone, { let mut cluster = CharCluster::new(); if options.direction() == Direction::RightToLeft { let mut parser = Parser::new( options.script(), tokens.into_iter().map(|mut t| { t.ch = t.ch.mirror().unwrap_or(t.ch); t }), ); if !parser.next(&mut cluster) { return; } let mut font = selector.select_font(&mut cluster); while shape_clusters( context, selector, options, &mut parser, &mut cluster, &mut font, &mut f, ) {} } else { let mut parser = Parser::new(options.script(), tokens.into_iter()); if !parser.next(&mut cluster) { return; } let mut font = selector.select_font(&mut cluster); while shape_clusters( context, selector, options, &mut parser, &mut cluster, &mut font, &mut f, ) {} } } fn shape_clusters( context: &mut ShapeContext, selector: &mut S, options: &impl ShapeOptions, parser: &mut Parser, cluster: &mut CharCluster, current_font: &mut Option, f: &mut impl FnMut(&S::SelectedFont, Shaper), ) -> bool where S: Selector, T: Iterator + Clone, { if current_font.is_none() { return false; } let font = current_font.as_ref().unwrap(); let font_ref = font.font(); let id = font .id_override() .unwrap_or([font_ref.key.value(), u64::MAX]); let mut shaper = context .builder_with_id(font.font(), id) .script(options.script()) .language(options.language()) .direction(options.direction()) .size(options.size()) .features(options.features()) .variations( font.synthesis() .as_ref() .map(|s| s.variations()) .unwrap_or(&[]) .iter() .copied(), ) .variations(options.variations()) .insert_dotted_circles(options.insert_dotted_circles()) .build(); loop { shaper.add_cluster(cluster); if !parser.next(cluster) { f(font, shaper); return false; } if let Some(next_font) = selector.select_font(cluster) { if next_font != *font { f(font, shaper); *current_font = Some(next_font); return true; } } else { return false; } } }