use std::collections::HashMap; use list_fonts::get_font_families; use freetype::Library; use freetype::Face; use freetype; /// Rasterizes glyphs for a single font face. pub struct Rasterizer { faces: HashMap>, library: Library, system_fonts: HashMap, dpi_x: u32, dpi_y: u32, dpr: f32, } #[inline] fn to_freetype_26_6(f: f32) -> isize { ((1i32 << 6) as f32 * f) as isize } #[derive(Clone, PartialEq, Eq, Hash)] pub struct FontDesc { name: String, style: String, } impl FontDesc { pub fn new(name: S, style: S) -> FontDesc where S: Into { FontDesc { name: name.into(), style: style.into() } } } impl Rasterizer { pub fn new(dpi_x: f32, dpi_y: f32, device_pixel_ratio: f32) -> Rasterizer { let library = Library::init().unwrap(); Rasterizer { system_fonts: get_font_families(), faces: HashMap::new(), library: library, dpi_x: dpi_x as u32, dpi_y: dpi_y as u32, dpr: device_pixel_ratio, } } pub fn box_size_for_font(&mut self, desc: &FontDesc, size: f32) -> (u32, u32) { let face = self.get_face(&desc).unwrap(); let scale_size = self.dpr * size; let em_size = face.em_size() as f32; let w = face.max_advance_width() as f32; let h = face.height() as f32; let w_scale = w / em_size; let h_scale = h / em_size; ((w_scale * scale_size) as u32, (h_scale * scale_size) as u32) } pub fn get_face(&mut self, desc: &FontDesc) -> Option> { if let Some(face) = self.faces.get(desc) { return Some(face.clone()); } if let Some(font) = self.system_fonts.get(&desc.name[..]) { if let Some(variant) = font.variants().get(&desc.style[..]) { let face = self.library.new_face(variant.path(), variant.index()) .expect("TODO handle new_face error"); self.faces.insert(desc.to_owned(), face); return Some(self.faces.get(desc).unwrap().clone()); } } None } pub fn get_glyph(&mut self, desc: &FontDesc, size: f32, c: char) -> RasterizedGlyph { let face = self.get_face(desc).expect("TODO handle get_face error"); face.set_char_size(to_freetype_26_6(size * self.dpr), 0, self.dpi_x, self.dpi_y).unwrap(); face.load_char(c as usize, freetype::face::TARGET_LIGHT).unwrap(); let glyph = face.glyph(); glyph.render_glyph(freetype::render_mode::RenderMode::Lcd).unwrap(); unsafe { let ft_lib = self.library.raw(); freetype::ffi::FT_Library_SetLcdFilter(ft_lib, freetype::ffi::FT_LCD_FILTER_DEFAULT); } let bitmap = glyph.bitmap(); let buf = bitmap.buffer(); let pitch = bitmap.pitch() as usize; let mut packed = Vec::with_capacity((bitmap.rows() * bitmap.width()) as usize); for i in 0..bitmap.rows() { let start = (i as usize) * pitch; let stop = start + bitmap.width() as usize; packed.extend_from_slice(&buf[start..stop]); } RasterizedGlyph { c: c, top: glyph.bitmap_top(), left: glyph.bitmap_left(), width: glyph.bitmap().width() / 3, height: glyph.bitmap().rows(), buf: packed, } } } #[derive(Debug)] pub struct RasterizedGlyph { pub c: char, pub width: i32, pub height: i32, pub top: i32, pub left: i32, pub buf: Vec, } #[cfg(test)] mod tests { use super::{Rasterizer, FontDesc}; #[cfg(target_os = "linux")] fn font_desc() -> FontDesc { FontDesc::new("Ubuntu Mono", "Regular") } }