use rusttype::{point, FontCollection, PositionedGlyph, Scale}; use std::io::Write; // Add an allocator that lets us keep track of the system extern crate stats_alloc; use stats_alloc::{StatsAlloc, Region, INSTRUMENTED_SYSTEM}; use std::alloc::System; #[global_allocator] static GLOBAL: &StatsAlloc = &INSTRUMENTED_SYSTEM; fn main() { let font = include_bytes!("../resources/WenQuanYiMicroHei-01.ttf"); test_rusttype(font); test_fontdue(font); } fn test_fontdue(font: &[u8]) { let reg = Region::new(&GLOBAL); println!("start: {:#?}", reg.change()); // Parse it into the font type. let mut font = fontdue::Font::from_bytes(font).unwrap(); println!("from_bytes: {:#?}", reg.change()); // Rasterize and get the layout metrics for the letter 'g' at 17px. let (metrics, bitmap) = font.rasterize('欢', 17.0); println!("font.rasterize: {:#?}", reg.change()); // Used here to ensure that the value is not // dropped before we check the statistics ::std::mem::size_of_val(&metrics); ::std::mem::size_of_val(&bitmap); ::std::mem::size_of_val(&font); } fn test_rusttype(font: &[u8]) { let reg = Region::new(&GLOBAL); let collection = FontCollection::from_bytes(font).unwrap_or_else(|e| { panic!("error constructing a FontCollection from bytes: {}", e); }); println!("collection: {:#?}", reg.change()); let font = collection .into_font() // only succeeds if collection consists of one font .unwrap_or_else(|e| { panic!("error turning FontCollection into a Font: {}", e); }); // Desired font pixel height let height: f32 = 20.0; // to get 80 chars across (fits most terminals); adjust as desired let pixel_height = height.ceil() as usize; // 2x scale in x direction to counter the aspect ratio of monospace characters. let scale = Scale { x: height * 2.0, y: height, }; // The origin of a line of text is at the baseline (roughly where // non-descending letters sit). We don't want to clip the text, so we shift // it down with an offset when laying it out. v_metrics.ascent is the // distance between the baseline and the highest edge of any glyph in // the font. That's enough to guarantee that there's no clipping. let v_metrics = font.v_metrics(scale); let offset = point(0.0, v_metrics.ascent); println!("metrics: {:#?}", reg.change()); // Glyphs to draw for "RustType". Feel free to try other strings. let glyphs: Vec> = font.layout("Xous:發啊你好", scale, offset).collect(); println!("glyphs: {:#?}", reg.change()); // Find the most visually pleasing width to display let width = glyphs .iter() .rev() .map(|g| g.position().x as f32 + g.unpositioned().h_metrics().advance_width) .next() .unwrap_or(0.0) .ceil() as usize; println!("width: {}, height: {}", width, pixel_height); // Rasterise directly into ASCII art. let mut pixel_data = vec![b'@'; width * pixel_height]; let mapping = b"@%#x+=:-. "; // The approximation of greyscale let mapping_scale = (mapping.len() - 1) as f32; for g in glyphs { if let Some(bb) = g.pixel_bounding_box() { g.draw(|x, y, v| { // v should be in the range 0.0 to 1.0 let i = (v * mapping_scale + 0.5) as usize; // so something's wrong if you get $ in the output. let c = mapping.get(i).cloned().unwrap_or(b'$'); let x = x as i32 + bb.min.x; let y = y as i32 + bb.min.y; // There's still a possibility that the glyph clips the boundaries of the bitmap if x >= 0 && x < width as i32 && y >= 0 && y < pixel_height as i32 { let x = x as usize; let y = y as usize; pixel_data[(x + y * width)] = c; } }) } } println!("rasterization: {:#?}", reg.change()); // Print it out let stdout = ::std::io::stdout(); let mut handle = stdout.lock(); for j in 0..pixel_height { handle .write_all(&pixel_data[j * width..(j + 1) * width]) .unwrap(); handle.write_all(b"\n").unwrap(); } }