cranpose-render-common 0.9.23

Scene, renderer and hit-test contracts for Cranpose backends
Documentation
use super::*;

const NOTO: &[u8] = include_bytes!("../../assets/NotoSansMerged.ttf");

fn face() -> Face<'static> {
    Face::parse(NOTO, 0).expect("font")
}

fn glyph(face: &Face<'_>, ch: char) -> u16 {
    face.glyph_index(ch).expect("glyph").0
}

#[test]
fn embedded_font_has_no_truetype_kern_table() {
    assert!(face().raw_face().table(Tag::from_bytes(b"kern")).is_none());
}

#[test]
fn gpos_kerning_is_found_and_negative_for_a_kerning_pair() {
    let face = face();
    let kerning = GposKerning::parse(&face).expect("GPOS kerning");
    assert!(!kerning.is_empty());
    let value = kerning.kern_unscaled(glyph(&face, 'A'), glyph(&face, 'V'));
    assert!(value < 0.0, "AV should tuck, got {value}");
}

#[test]
fn unkerned_pairs_are_zero() {
    let face = face();
    let kerning = GposKerning::parse(&face).expect("GPOS kerning");
    assert_eq!(
        kerning.kern_unscaled(glyph(&face, 'n'), glyph(&face, 'n')),
        0.0
    );
}

#[test]
fn kerning_is_directional() {
    let face = face();
    let kerning = GposKerning::parse(&face).expect("GPOS kerning");
    let forward = kerning.kern_unscaled(glyph(&face, 'A'), glyph(&face, 'V'));
    let backward = kerning.kern_unscaled(glyph(&face, 'V'), glyph(&face, 'A'));
    assert!(forward < 0.0 && backward < 0.0);
}

#[test]
fn class_def_collapses_runs_and_defaults_to_zero() {
    let classes = ClassDef::from_pairs(vec![(4, 1), (5, 1), (6, 1), (9, 2), (12, 0)]);
    assert_eq!(classes.ranges, vec![(4, 6, 1), (9, 9, 2)]);
    assert_eq!(classes.class_of(5), 1);
    assert_eq!(classes.class_of(9), 2);
    assert_eq!(classes.class_of(12), 0);
    assert_eq!(classes.class_of(0), 0);
}

#[test]
fn value_size_counts_only_the_low_byte() {
    assert_eq!(value_size(0x0000), 0);
    assert_eq!(value_size(0x0004), 2);
    assert_eq!(value_size(0x0044), 4);
    assert_eq!(value_size(0x00FF), 16);
}

fn kerned_font() -> KernedFont {
    KernedFont::new(
        FontArc::try_from_slice(NOTO).expect("font"),
        KernedFont::read_kerning(NOTO, &[]),
    )
}

#[test]
fn a_face_without_feature_settings_keeps_its_cmap_glyphs() {
    let font = kerned_font();
    assert!(font.with_feature_settings(None).is_none());
    assert!(font.with_feature_settings(Some("\"zero\" 0")).is_none());
    assert!(font.with_feature_settings(Some("liga")).is_none());
    assert_eq!(font.feature_key(), 0);
    assert_eq!(font.glyph_id('0'), GlyphId(glyph(&face(), '0')));
}

#[test]
fn feature_settings_substitute_every_glyph_the_face_maps() {
    let base = kerned_font();
    let zero = base
        .with_feature_settings(Some("\"zero\""))
        .expect("zero substitutes");
    assert_eq!(zero.glyph_id('0'), GlyphId(2251));
    assert_eq!(zero.glyph_id('1'), base.glyph_id('1'));
    assert_eq!(zero.glyph_advance('0').0, GlyphId(2251));
    assert_eq!(base.glyph_advance('0').0, base.glyph_id('0'));
    assert_ne!(zero.feature_key(), 0);
}

#[test]
fn a_shaped_face_reshapes_from_its_cmap_glyphs() {
    let base = kerned_font();
    let zero = base
        .with_feature_settings(Some("zero"))
        .expect("zero substitutes");
    assert!(zero.with_feature_settings(Some("'zero' on")).is_none());
    let small_caps = zero
        .with_feature_settings(Some("smcp"))
        .expect("smcp substitutes");
    assert_eq!(small_caps.glyph_id('0'), base.glyph_id('0'));
    assert_eq!(small_caps.glyph_id('a'), GlyphId(1868));
    let unshaped = zero.with_feature_settings(None).expect("back to the cmap");
    assert_eq!(unshaped.glyph_id('0'), base.glyph_id('0'));
    assert_eq!(unshaped.feature_key(), 0);
    assert_eq!(unshaped.ascii_glyph('0'), base.ascii_glyph('0'));
}