diff --git a/font/src/ft/fc/pattern.rs b/font/src/ft/fc/pattern.rs index 55fbaef..e3c5261 100644 --- a/font/src/ft/fc/pattern.rs +++ b/font/src/ft/fc/pattern.rs @@ -18,15 +18,15 @@ use std::path::PathBuf; use std::str; use std::mem; -use libc::{c_char, c_int}; +use libc::{c_char, c_int, c_double}; use foreign_types::{ForeignType, ForeignTypeRef}; use super::ffi::FcResultMatch; use super::ffi::{FcPatternDestroy, FcPatternAddCharSet}; -use super::ffi::{FcPatternGetString, FcPatternCreate, FcPatternAddString}; +use super::ffi::{FcPatternGetString, FcPatternCreate, FcPatternAddString, FcPatternAddDouble}; use super::ffi::{FcPatternGetInteger, FcPatternAddInteger, FcPatternPrint}; use super::ffi::{FcChar8, FcPattern, FcDefaultSubstitute, FcConfigSubstitute}; -use super::ffi::{FcFontRenderPrepare, FcPatternGetBool, FcBool}; +use super::ffi::{FcFontRenderPrepare, FcPatternGetBool, FcBool, FcPatternGetDouble}; use super::{MatchKind, ConfigRef, CharSetRef, Weight, Slant, Width, Rgba, HintStyle, LcdFilter}; @@ -115,7 +115,6 @@ pub struct IntPropertyIter<'a> { index: usize } - impl<'a> IntPropertyIter<'a> { fn new<'b>(pattern: &'b PatternRef, object: &'b [u8]) -> IntPropertyIter<'b> { IntPropertyIter { @@ -227,6 +226,42 @@ impl<'a> LcdFilterPropertyIter<'a> { } } +/// Iterator over interger properties +pub struct DoublePropertyIter<'a> { + pattern: &'a PatternRef, + object: &'a [u8], + index: usize +} + +impl<'a> DoublePropertyIter<'a> { + fn new<'b>(pattern: &'b PatternRef, object: &'b [u8]) -> DoublePropertyIter<'b> { + DoublePropertyIter { + pattern: pattern, + object: object, + index: 0 + } + } + + fn get_value(&self, index: usize) -> Option { + let mut value = 0 as c_double; + + let result = unsafe { + FcPatternGetDouble( + self.pattern.as_ptr(), + self.object.as_ptr() as *mut c_char, + index as c_int, + &mut value + ) + }; + + if result == FcResultMatch { + Some(value as f64) + } else { + None + } + } +} + /// Implement debug for a property iterator macro_rules! impl_property_iter_debug { ($iter:ty => $item:ty) => { @@ -305,6 +340,7 @@ macro_rules! impl_derived_property_iter { impl_property_iter! { StringPropertyIter<'a> => &'a str, IntPropertyIter<'a> => isize, + DoublePropertyIter<'a> => f64, BooleanPropertyIter<'a> => bool } @@ -322,7 +358,7 @@ foreign_type! { pub struct PatternRef; } -macro_rules! pattern_string_accessors { +macro_rules! string_accessor { ($([$getter:ident, $setter:ident] => $object_name:expr),*) => { $( #[inline] @@ -372,6 +408,18 @@ macro_rules! boolean_getter { } } +macro_rules! double_getter { + ($($method:ident() => $property:expr),*) => { + $( + pub fn $method(&self) -> DoublePropertyIter { + unsafe { + self.get_double($property) + } + } + )* + } +} + impl PatternRef { // Prints the pattern to stdout // @@ -411,6 +459,14 @@ impl PatternRef { ) == 1 } + unsafe fn add_double(&self, object: &[u8], value: f64) -> bool { + FcPatternAddDouble( + self.as_ptr(), + object.as_ptr() as *mut c_char, + value as c_double + ) == 1 + } + unsafe fn get_string<'a>(&'a self, object: &'a [u8]) -> StringPropertyIter<'a> { StringPropertyIter::new(self, object) } @@ -419,6 +475,10 @@ impl PatternRef { IntPropertyIter::new(self, object) } + unsafe fn get_double<'a>(&'a self, object: &'a [u8]) -> DoublePropertyIter<'a> { + DoublePropertyIter::new(self, object) + } + unsafe fn get_boolean<'a>(&'a self, object: &'a [u8]) -> BooleanPropertyIter<'a> { BooleanPropertyIter::new(self, object) } @@ -446,7 +506,15 @@ impl PatternRef { decorative() => b"decorative\0" } - pattern_string_accessors! { + double_getter! { + size() => b"size\0", + aspect() => b"aspect\0", + pixelsize() => b"pixelsize\0", + scale() => b"scale\0", + dpi() => b"dpi\0" + } + + string_accessor! { [family, add_family] => b"family\0", [familylang, add_familylang] => b"familylang\0", [style, add_style] => b"style\0", @@ -467,6 +535,12 @@ impl PatternRef { } } + pub fn add_pixelsize(&mut self, size: f64) -> bool { + unsafe { + self.add_double(b"pixelsize\0", size) + } + } + pub fn set_weight(&mut self, weight: Weight) -> bool { unsafe { self.add_integer(b"weight\0", weight as isize) diff --git a/font/src/ft/mod.rs b/font/src/ft/mod.rs index 1905d99..f37a500 100644 --- a/font/src/ft/mod.rs +++ b/font/src/ft/mod.rs @@ -26,12 +26,17 @@ pub mod fc; use super::{FontDesc, RasterizedGlyph, Metrics, Size, FontKey, GlyphKey, Weight, Slant, Style}; +struct FixedSize { + pixelsize: f64, +} + struct Face { ft_face: freetype::Face<'static>, key: FontKey, load_flags: freetype::face::LoadFlag, render_mode: freetype::RenderMode, lcd_filter: c_uint, + non_scalable: Option } impl fmt::Debug for Face { @@ -103,8 +108,8 @@ impl ::Rasterize for FreeTypeRasterizer { }) } - fn load_font(&mut self, desc: &FontDesc, _size: Size) -> Result { - let face = self.get_face(desc)?; + fn load_font(&mut self, desc: &FontDesc, size: Size) -> Result { + let face = self.get_face(desc, size)?; let key = face.key; self.faces.insert(key, face); Ok(key) @@ -145,15 +150,19 @@ impl IntoFontconfigType for Weight { impl FreeTypeRasterizer { /// Load a font face accoring to `FontDesc` - fn get_face(&mut self, desc: &FontDesc) -> Result { + fn get_face(&mut self, desc: &FontDesc, size: Size) -> Result { + // Adjust for DPI + let scale = self.dpi_x as f32 / 72.; + let size = Size::new(size.as_f32_pts() * scale); + match desc.style { Style::Description { slant, weight } => { // Match nearest font - self.get_matching_face(&desc, slant, weight) + self.get_matching_face(&desc, slant, weight, size) } Style::Specific(ref style) => { // If a name was specified, try and load specifically that font. - self.get_specific_face(&desc, &style) + self.get_specific_face(&desc, &style, size) } } } @@ -162,12 +171,14 @@ impl FreeTypeRasterizer { &mut self, desc: &FontDesc, slant: Slant, - weight: Weight + weight: Weight, + size: Size, ) -> Result { let mut pattern = fc::Pattern::new(); pattern.add_family(&desc.name); pattern.set_weight(weight.into_fontconfig_type()); pattern.set_slant(slant.into_fontconfig_type()); + pattern.add_pixelsize(size.as_f32_pts() as _); let font = fc::font_match(fc::Config::get_current(), &mut pattern) .ok_or_else(|| Error::MissingFont(desc.to_owned()))?; @@ -183,11 +194,13 @@ impl FreeTypeRasterizer { fn get_specific_face( &mut self, desc: &FontDesc, - style: &str + style: &str, + size: Size, ) -> Result { let mut pattern = fc::Pattern::new(); pattern.add_family(&desc.name); pattern.add_style(style); + pattern.add_pixelsize(size.as_f32_pts() as _); let font = fc::font_match(fc::Config::get_current(), &mut pattern) .ok_or_else(|| Error::MissingFont(desc.to_owned()))?; @@ -203,16 +216,29 @@ impl FreeTypeRasterizer { if let (Some(path), Some(index)) = (pattern.file(0), pattern.index().nth(0)) { trace!("got font path={:?}", path); let ft_face = self.library.new_face(path, index)?; + + // Get available pixel sizes if font isn't scalable. + let non_scalable = if !pattern.scalable().next().unwrap_or(true) { + let mut pixelsize = pattern.pixelsize(); + debug!("pixelsizes: {:?}", pixelsize); + + Some(FixedSize { + pixelsize: pixelsize.next().expect("has 1+ pixelsize"), + }) + } else { + None + }; + let face = Face { ft_face: ft_face, key: FontKey::next(), load_flags: Self::ft_load_flags(pattern), render_mode: Self::ft_render_mode(pattern), lcd_filter: Self::ft_lcd_filter(pattern), + non_scalable: non_scalable, }; debug!("Loaded Face {:?}", face); - Ok(Some(face)) } else { Ok(None) @@ -220,14 +246,11 @@ impl FreeTypeRasterizer { } fn face_for_glyph(&mut self, glyph_key: &GlyphKey, have_recursed: bool) -> Result { - let size = glyph_key.size.as_f32_pts() * self.dpr; let c = glyph_key.c; let use_initial_face = if self.faces.contains_key(&glyph_key.font_key) { // Get face and unwrap since we just checked for presence. let face = self.faces.get(&glyph_key.font_key).unwrap(); - - face.ft_face.set_char_size(to_freetype_26_6(size), 0, self.dpi_x, self.dpi_y)?; let index = face.ft_face.get_char_index(c as usize); if index != 0 || have_recursed { @@ -253,6 +276,12 @@ impl FreeTypeRasterizer { let face = self.faces.get(&font_key).unwrap(); let index = face.ft_face.get_char_index(glyph_key.c as usize); + let size = face.non_scalable.as_ref() + .map(|v| v.pixelsize as f32) + .unwrap_or_else(|| glyph_key.size.as_f32_pts() * self.dpi_x as f32 / 72.); + + face.ft_face.set_char_size(to_freetype_26_6(size), 0, 0, 0)?; + unsafe { let ft_lib = self.library.raw(); freetype::ffi::FT_Library_SetLcdFilter(ft_lib, face.lcd_filter);