swash 0.1.6

Font introspection, complex text shaping and glyph rendering.
Documentation
/*!

PostScript outlines.

*/

#[allow(clippy::module_inception)]
mod cff;
mod hint;

pub use cff::{Cff, CffProxy, Glyph, GlyphSink};
pub use hint::HinterState;

use super::{internal, TRACE};

use crate::font::FontRef;

pub struct Scaler {
    entries: Vec<Entry>,
    max_entries: usize,
    epoch: u64,
}

impl Scaler {
    pub fn new(max_entries: usize) -> Self {
        Self {
            entries: Vec::new(),
            max_entries,
            epoch: 0,
        }
    }

    pub fn scale(
        &mut self,
        font: &FontRef,
        id: u64,
        coords: &[i16],
        proxy: &CffProxy,
        scale: f32,
        hint: bool,
        glyph_id: u16,
        sink: &mut impl GlyphSink,
    ) -> Option<()> {
        let cff = proxy.materialize(font);
        let glyph = cff.get(glyph_id)?;
        if hint {
            let dict = glyph.subfont_index();
            let state = self.entry(id, dict, coords, scale, &glyph);
            if glyph.path(scale, coords, Some(state), sink) {
                Some(())
            } else {
                None
            }
        } else if glyph.path(scale, coords, None, sink) {
            Some(())
        } else {
            None
        }
    }

    fn entry(
        &mut self,
        id: u64,
        dict: u16,
        coords: &[i16],
        scale: f32,
        glyph: &Glyph,
    ) -> &HinterState {
        let epoch = self.epoch;
        let (found, index) = self.find_entry(id, dict, coords, scale);
        if found {
            let entry = &mut self.entries[index];
            entry.epoch = epoch;
            &entry.state
        } else {
            self.epoch += 1;
            let state = HinterState::new(glyph, scale, coords);
            if index == self.entries.len() {
                self.entries.push(Entry {
                    epoch,
                    id,
                    dict,
                    state,
                    coords: Vec::from(coords),
                    scale,
                });
                &self.entries[index].state
            } else {
                let entry = &mut self.entries[index];
                entry.epoch = epoch;
                entry.id = id;
                entry.dict = dict;
                entry.state = state;
                entry.coords.clear();
                entry.coords.extend_from_slice(coords);
                entry.scale = scale;
                &entry.state
            }
        }
    }

    fn find_entry(&self, id: u64, dict: u16, coords: &[i16], scale: f32) -> (bool, usize) {
        let mut lowest_epoch = self.epoch;
        let mut lowest_index = 0;
        let vary = !coords.is_empty();
        for (i, entry) in self.entries.iter().enumerate() {
            if entry.id == id
                && entry.dict == dict
                && entry.scale == scale
                && (!vary || (coords == &entry.coords[..]))
            {
                return (true, i);
            }
            if entry.epoch < lowest_epoch {
                lowest_epoch = entry.epoch;
                lowest_index = i;
            }
        }
        if self.entries.len() < self.max_entries {
            lowest_index = self.entries.len();
        }
        (false, lowest_index)
    }
}

struct Entry {
    epoch: u64,
    id: u64,
    dict: u16,
    state: HinterState,
    coords: Vec<i16>,
    scale: f32,
}