cranpose-text 0.22.2

Text styles, fonts, shaping and paragraph layout for Cranpose
Documentation
// What a plan found for the characters it met, so plain text reads each
// character with one load and each pair of characters with another, as
// main's ASCII table did, for any script: per character its glyph, its
// advance and whether it is plain, direct mapped by the character's low
// bits, so a script block of up to `SLOTS` characters fills without
// collisions; per pair of slots the kerning between their glyphs, or that a
// ligature joins them, in rows made for the characters that precede others.
// A cell names its kerning among the few values a font kerns by.
pub(crate) struct PlainChars {
    slots: Box<[u64; SLOTS]>,
    // For each slot, the number of its row in `rows`, from 1; 0 for a slot
    // with no row yet.
    row_of: Box<[u8; SLOTS]>,
    rows: Vec<[u8; SLOTS]>,
    kerns: Vec<i16>,
    // Whether a character given since `take_inked` has a glyph whose ink
    // leaves its advance or the band of its typeface's ASCII ink.
    inked: bool,
}

// A plain character's glyph, its advance, whether its glyph may kern with
// or join the glyph after it, and its slot.
#[derive(Clone, Copy)]
pub(crate) struct PlainChar {
    pub(crate) glyph: u16,
    pub(crate) advance: i16,
    pub(crate) pairs: bool,
    slot: u8,
}

impl PlainChar {
    // The character slot `slot` holds as `packed`.
    fn of(packed: u64, slot: u8) -> Self {
        Self {
            glyph: (packed >> 16) as u16,
            advance: packed as u16 as i16,
            pairs: packed & PAIRS != 0,
            slot,
        }
    }
}

// What a font reads for a plain character: its glyph, its advance,
// whether its glyph may kern with or join the glyph after it, and whether
// its ink leaves its advance or the band of its typeface's ASCII ink.
pub(crate) struct CharGlyph {
    pub(crate) glyph: u32,
    pub(crate) advance: i32,
    pub(crate) pairs: bool,
    pub(crate) inks_out: bool,
}

// What stands between the glyphs of two characters side by side.
#[derive(Clone, Copy, PartialEq, Eq)]
pub(crate) enum PairFit {
    // The kerning the first takes, in font units.
    Kern(i32),
    // A ligature joins them: the pair is not plain.
    Joins,
}

const SLOT_BITS: u32 = 7;
const SLOTS: usize = 1 << SLOT_BITS;
// A slot holds its character in bits 32 to 52, the character's glyph in
// bits 16 to 31 and its advance in the low 16. `FILLED` marks a slot that
// holds a character, `PLAIN` one whose character is plain, `PAIRS` one
// whose glyph may kern with or join the next and `INKS_OUT` one whose
// glyph's ink leaves its advance or the band of its typeface's ASCII ink.
const FILLED: u64 = 1 << 63;
const PLAIN: u64 = 1 << 62;
const PAIRS: u64 = 1 << 61;
const INKS_OUT: u64 = 1 << 60;
// A pair cell not read yet, one a ligature joins and one with no kerning;
// a cell from `KERNED` on holds the kerning `kerns` holds at its distance
// from `KERNED`.
const UNREAD: u8 = 0;
const JOINS: u8 = 1;
const UNKERNED: u8 = 2;
const KERNED: u8 = 3;
const MOST_KERNS: usize = (u8::MAX - KERNED) as usize + 1;

impl Default for PlainChars {
    fn default() -> Self {
        Self {
            slots: Box::new([0; SLOTS]),
            row_of: Box::new([0; SLOTS]),
            rows: Vec::new(),
            kerns: Vec::new(),
            inked: false,
        }
    }
}

impl PlainChars {
    // The glyph of `ch` and its advance when `ch` is plain, from its slot,
    // or from `read` of `ch` the first time. A glyph or an advance a slot
    // cannot hold is not plain.
    pub(crate) fn char(
        &mut self,
        ch: char,
        read: &impl Fn(char) -> Option<CharGlyph>,
    ) -> Option<PlainChar> {
        let code = u32::from(ch);
        let slot = (code & (SLOTS as u32 - 1)) as u8;
        let packed = self.slots[usize::from(slot)];
        let known = (FILLED >> 32) | u64::from(code);
        if (packed >> 32) & !(PAIRS >> 32) == known | (PLAIN >> 32) {
            return Some(PlainChar::of(packed, slot));
        }
        self.uncommon_char(slot, (packed, known), ch, read)
    }

    // Whether a character given since the last call has a glyph whose ink
    // leaves its advance or the band of its typeface's ASCII ink.
    pub(crate) fn take_inked(&mut self) -> bool {
        std::mem::take(&mut self.inked)
    }

    // `char` of a character its slot holds as plain with a glyph whose ink
    // reaches out, holds as not plain, or does not hold, `packed` holding
    // another or none: `read` reads it. Out of line, as text meets each
    // character once and few glyphs ink out.
    #[inline(never)]
    fn uncommon_char(
        &mut self,
        slot: u8,
        (packed, known): (u64, u64),
        ch: char,
        read: &impl Fn(char) -> Option<CharGlyph>,
    ) -> Option<PlainChar> {
        if (packed >> 32) & !((PAIRS | INKS_OUT) >> 32) == known | (PLAIN >> 32) {
            self.inked = true;
            return Some(PlainChar::of(packed, slot));
        }
        if packed >> 32 == known {
            return None;
        }
        let read = read(ch);
        let inks_out = read.as_ref().is_some_and(|read| read.inks_out);
        let plain = read.and_then(|read| {
            Some(PlainChar {
                glyph: u16::try_from(read.glyph).ok()?,
                advance: i16::try_from(read.advance).ok()?,
                pairs: read.pairs,
                slot,
            })
        });
        let inks_out = plain.is_some() && inks_out;
        self.inked |= inks_out;
        if packed & FILLED != 0 {
            self.forget(slot);
        }
        self.slots[usize::from(slot)] = known << 32
            | plain.map_or(0, |plain| {
                let pairs = if plain.pairs { PAIRS } else { 0 };
                let inks_out = if inks_out { INKS_OUT } else { 0 };
                PLAIN
                    | pairs
                    | inks_out
                    | u64::from(plain.glyph) << 16
                    | u64::from(plain.advance as u16)
            });
        plain
    }

    // What stands between the glyphs of `first` and `second`, from their
    // cell, or from `read` the first time.
    pub(crate) fn pair(
        &mut self,
        first: PlainChar,
        second: PlainChar,
        read: impl FnOnce() -> PairFit,
    ) -> PairFit {
        let number = self.row_of[usize::from(first.slot)];
        if number != 0 {
            match self.rows[usize::from(number - 1)][usize::from(second.slot)] {
                UNREAD => {}
                JOINS => return PairFit::Joins,
                UNKERNED => return PairFit::Kern(0),
                kerned => {
                    let kern = self.kerns[usize::from(kerned - KERNED)];
                    return PairFit::Kern(i32::from(kern));
                }
            }
        }
        self.read_pair(first, second, read)
    }

    // `pair` of a pair whose cell is not read yet, or whose first
    // character has no row yet: `read` reads it. Out of line, as text meets
    // each pair once.
    #[inline(never)]
    fn read_pair(
        &mut self,
        first: PlainChar,
        second: PlainChar,
        read: impl FnOnce() -> PairFit,
    ) -> PairFit {
        let number = &mut self.row_of[usize::from(first.slot)];
        if *number == 0 {
            // A plan meets a few rows' worth of characters; room for more
            // goes unused.
            self.rows.reserve_exact(1);
            self.rows.push([UNREAD; SLOTS]);
            *number = u8::try_from(self.rows.len()).unwrap_or(u8::MAX);
        }
        let row = usize::from(*number - 1);
        let fit = read();
        // A cell holds what stands between the glyphs its slots hold now:
        // `second` took the slot of `first` from it when they share one.
        if first.slot == second.slot && first.glyph != second.glyph {
            return fit;
        }
        let cell = &mut self.rows[row][usize::from(second.slot)];
        *cell = match fit {
            PairFit::Joins => JOINS,
            PairFit::Kern(0) => UNKERNED,
            PairFit::Kern(kern) => {
                let Some(name) = kern_name(&mut self.kerns, kern) else {
                    return fit;
                };
                name
            }
        };
        fit
    }

    // Drops what pairs knew of the character leaving `slot`, keeping its
    // row for the next character in it; the kerning values stay for the
    // cells that name them.
    fn forget(&mut self, slot: u8) {
        if let Some(number) = self.row_of[usize::from(slot)].checked_sub(1) {
            self.rows[usize::from(number)] = [UNREAD; SLOTS];
        }
        for row in &mut self.rows {
            row[usize::from(slot)] = UNREAD;
        }
    }
}

// The cell that names `kern` among `kerns`, which it joins when new; `None`
// when `kern` is not an `i16` or the cells name as many values as they can.
fn kern_name(kerns: &mut Vec<i16>, kern: i32) -> Option<u8> {
    let kern = i16::try_from(kern).ok()?;
    let index = match kerns.iter().position(|&known| known == kern) {
        Some(index) => index,
        None if kerns.len() < MOST_KERNS => {
            kerns.push(kern);
            kerns.len() - 1
        }
        None => return None,
    };
    u8::try_from(index).ok().map(|index| index + KERNED)
}