use fcft::{Canvas, Color, Font, GlyphFormat, Subpixel};
const OPEN_SANS: &str = "Open Sans:pixelsize=24";
#[test]
fn capabilities_include_text_run_shaping() {
let caps = fcft::capabilities();
assert!(caps.text_run_shaping());
assert!(caps.grapheme_shaping());
}
#[test]
fn nonsense_family_still_matches() {
let font = Font::new(&["No Such Font Family Exists:pixelsize=16"], None);
assert!(font.is_ok());
}
#[test]
fn metrics_are_positive() {
let font = Font::new(&[OPEN_SANS], None).unwrap();
assert!(font.height() > 0);
assert!(font.ascent() > 0);
assert!(font.descent() > 0);
assert!(font.max_advance().0 > 0);
}
#[test]
fn shaping_yields_expected_glyphs() {
let font = Font::new(&[OPEN_SANS], None).unwrap();
let run = font.shape("Hello 123", Subpixel::None);
assert_eq!(run.len(), 9);
for g in run.glyphs() {
assert!(g.advance.0 > 0);
}
assert!(run.width() > 0);
}
#[test]
fn measuring_is_monotonic() {
let font = Font::new(&[OPEN_SANS], None).unwrap();
let text = "Hailing frequencies open";
let mut previous = 0;
for end in text.char_indices().map(|(i, _)| i).chain([text.len()]) {
let width = font.shape(&text[..end], Subpixel::None).width();
assert!(width >= previous, "width shrank at {:?}", &text[..end]);
previous = width;
}
}
#[test]
fn truncation_fits_max_width() {
let font = Font::new(&[OPEN_SANS], None).unwrap();
let text = "Hailing frequencies open";
let full_width = font.shape(text, Subpixel::None).width();
let run = font.shape_truncated(text, Subpixel::None, full_width);
assert_eq!(run.width(), full_width);
let max = full_width / 2;
let run = font.shape_truncated(text, Subpixel::None, max);
assert!(run.width() <= max);
assert!(run.len() > 2);
let run = font.shape_truncated(text, Subpixel::None, 1);
assert!(run.width() <= 1);
}
#[test]
fn drawing_covers_expected_pixels() {
const WIDTH: usize = 200;
const HEIGHT: usize = 40;
const STRIDE: usize = WIDTH * 4;
let font = Font::new(&["Open Sans:pixelsize=20"], None).unwrap();
let run = font.shape("Hi", Subpixel::None);
let text_width = run.width();
assert!(text_width > 0 && text_width < 60);
let mut buf = vec![0u8; STRIDE * HEIGHT];
let mut canvas = Canvas::from_xrgb8888(&mut buf, WIDTH as u32, HEIGHT as u32, STRIDE as u32);
let baseline = font.ascent();
let (pen_x, _) = canvas.draw(&run, 10, baseline, Color::from_argb8888(0xffffffff));
assert_eq!(pen_x, 10 + text_width);
drop(canvas);
let painted = |x: usize, y: usize| -> bool {
let p = y * STRIDE + x * 4;
buf[p] != 0 || buf[p + 1] != 0 || buf[p + 2] != 0
};
let painted_in = |x0: usize, x1: usize, y0: usize, y1: usize| -> usize {
(y0..y1)
.flat_map(|y| (x0..x1).map(move |x| (x, y)))
.filter(|&(x, y)| painted(x, y))
.count()
};
assert!(painted_in(10, 10 + text_width as usize, 0, HEIGHT) > 0);
assert_eq!(painted_in(0, 9, 0, HEIGHT), 0);
assert_eq!(
painted_in(10 + text_width as usize + 2, WIDTH, 0, HEIGHT),
0
);
}
#[test]
fn color_emoji_is_a_color_glyph() {
let font = Font::new(&["Twemoji:pixelsize=24"], None).unwrap();
let glyph = font.glyph('😀', Subpixel::None).unwrap();
assert!(glyph.is_color_glyph());
assert_eq!(glyph.format(), GlyphFormat::A8R8G8B8);
let font = Font::new(&[OPEN_SANS], None).unwrap();
let glyph = font.glyph('A', Subpixel::None).unwrap();
assert!(!glyph.is_color_glyph());
assert_eq!(glyph.format(), GlyphFormat::A8);
}
#[test]
fn kerning_is_queryable() {
let font = Font::new(&[OPEN_SANS], None).unwrap();
let _ = font.kerning('A', 'V');
}