diff options
-rw-r--r-- | alacritty.yml | 23 | ||||
-rw-r--r-- | font/src/darwin/mod.rs | 63 | ||||
-rw-r--r-- | font/src/ft/list_fonts.rs | 396 | ||||
-rw-r--r-- | font/src/ft/mod.rs | 102 | ||||
-rw-r--r-- | font/src/lib.rs | 37 | ||||
-rw-r--r-- | src/config.rs | 71 | ||||
-rw-r--r-- | src/renderer/mod.rs | 33 |
7 files changed, 514 insertions, 211 deletions
diff --git a/alacritty.yml b/alacritty.yml index 1fbcb63f..0d907ea0 100644 --- a/alacritty.yml +++ b/alacritty.yml @@ -9,14 +9,29 @@ dpi: # Display tabs using this many cells (changes require restart) tabspaces: 8 +# When true, bold text is drawn using the bright variant of colors. draw_bold_text_with_bright_colors: true # Font configuration (changes require restart) font: - family: DejaVu Sans Mono - style: Book - bold_style: Bold - italic_style: Oblique + # The normal (roman) font face to use. + normal: + family: monospace # should be "Menlo" or something on macOS. + # Style can be specified to pick a specific face. + # style: Regular + + # The bold font face + bold: + family: monospace # should be "Menlo" or something on macOS. + # Style can be specified to pick a specific face. + # style: Bold + + # The italic font face + italic: + family: monospace # should be "Menlo" or something on macOS. + # Style can be specified to pick a specific face. + # style: Italic + # Point size of the font size: 11.0 # Offset is the extra space around each character. offset.y can be thought of diff --git a/font/src/darwin/mod.rs b/font/src/darwin/mod.rs index 99f080ab..35336df9 100644 --- a/font/src/darwin/mod.rs +++ b/font/src/darwin/mod.rs @@ -19,6 +19,8 @@ use std::collections::HashMap; use std::ptr; +use ::{Slant, Weight, Style}; + use core_foundation::base::TCFType; use core_foundation::string::{CFString, CFStringRef}; use core_foundation::array::CFIndex; @@ -36,6 +38,7 @@ use core_text::font_descriptor::kCTFontDefaultOrientation; use core_text::font_descriptor::kCTFontHorizontalOrientation; use core_text::font_descriptor::kCTFontVerticalOrientation; use core_text::font_descriptor::{CTFontDescriptor, CTFontDescriptorRef, CTFontOrientation}; +use core_text::font_descriptor::SymbolicTraitAccessors; use libc::{size_t, c_int}; @@ -166,10 +169,15 @@ impl ::Rasterize for Rasterizer { } impl Rasterizer { - fn get_font(&mut self, desc: &FontDesc, size: Size) -> Result<Font, Error> { + fn get_specific_face( + &mut self, + desc: &FontDesc, + style: &str, + size: Size + ) -> Result<Font, Error> { let descriptors = descriptors_for_family(&desc.name[..]); for descriptor in descriptors { - if descriptor.style_name == desc.style { + if descriptor.style_name == style { // Found the font we want let scaled_size = size.as_f32_pts() as f64 * self.device_pixel_ratio as f64; let font = descriptor.to_font(scaled_size); @@ -179,6 +187,44 @@ impl Rasterizer { Err(Error::MissingFont(desc.to_owned())) } + + fn get_matching_face( + &mut self, + desc: &FontDesc, + slant: Slant, + weight: Weight, + size: Size + ) -> Result<Font, Error> { + let bold = match weight { + Weight::Bold => true, + _ => false + }; + let italic = match slant { + Slant::Normal => false, + _ => true, + }; + let scaled_size = size.as_f32_pts() as f64 * self.device_pixel_ratio as f64; + + let descriptors = descriptors_for_family(&desc.name[..]); + for descriptor in descriptors { + let font = descriptor.to_font(scaled_size); + if font.is_bold() == bold && font.is_italic() == italic { + // Found the font we want + return Ok(font); + } + } + + Err(Error::MissingFont(desc.to_owned())) + } + + fn get_font(&mut self, desc: &FontDesc, size: Size) -> Result<Font, Error> { + match desc.style { + Style::Specific(ref style) => self.get_specific_face(desc, style, size), + Style::Description { slant, weight } => { + self.get_matching_face(desc, slant, weight, size) + }, + } + } } /// Specifies the intended rendering orientation of the font for obtaining glyph metrics @@ -290,6 +336,14 @@ impl Font { } } + pub fn is_bold(&self) -> bool { + self.ct_font.symbolic_traits().is_bold() + } + + pub fn is_italic(&self) -> bool { + self.ct_font.symbolic_traits().is_italic() + } + fn glyph_advance(&self, character: char) -> f64 { let index = self.glyph_index(character).unwrap(); @@ -539,12 +593,9 @@ mod tests { .collect::<Vec<_>>(); for font in fonts { - // Check deref - println!("family: {}", font.family_name()); - // Get a glyph for c in &['a', 'b', 'c', 'd'] { - let glyph = font.get_glyph(*c, 72.); + let glyph = font.get_glyph(*c, 72.).unwrap(); // Debug the glyph.. sigh for row in 0..glyph.height { diff --git a/font/src/ft/list_fonts.rs b/font/src/ft/list_fonts.rs index 0fbc7231..29912a8f 100644 --- a/font/src/ft/list_fonts.rs +++ b/font/src/ft/list_fonts.rs @@ -12,28 +12,31 @@ // See the License for the specific language governing permissions and // limitations under the License. // -use std::collections::HashMap; -use std::fmt; -use std::path::PathBuf; - -mod fc { +pub mod fc { use std::ptr; use std::ffi::{CStr, CString}; use std::str; use std::ops::Deref; + use std::path::PathBuf; - use ffi_util::ForeignTypeRef; + use ffi_util::{ForeignType, ForeignTypeRef}; use libc::{c_char, c_int}; use fontconfig::fontconfig as ffi; use self::ffi::{FcConfigGetCurrent, FcConfigGetFonts, FcSetSystem, FcSetApplication}; use self::ffi::{FcPatternGetString, FcPatternCreate, FcPatternAddString}; - use self::ffi::{FcPatternGetInteger}; + use self::ffi::{FcPatternGetInteger, FcPatternAddInteger}; use self::ffi::{FcObjectSetCreate, FcObjectSetAdd}; - use self::ffi::{FcResultMatch, FcFontSetList}; - use self::ffi::{FcChar8, FcConfig, FcPattern, FcFontSet, FcObjectSet}; + use self::ffi::{FcResultMatch, FcResultNoMatch, FcFontSetList}; + use self::ffi::{FcChar8, FcConfig, FcPattern, FcFontSet, FcObjectSet, FcCharSet}; use self::ffi::{FcFontSetDestroy, FcPatternDestroy, FcObjectSetDestroy, FcConfigDestroy}; + use self::ffi::{FcFontMatch, FcFontList, FcFontSort, FcConfigSubstitute, FcDefaultSubstitute}; + use self::ffi::{FcMatchFont, FcMatchPattern, FcMatchScan, FC_SLANT_ITALIC, FC_SLANT_ROMAN}; + use self::ffi::{FC_SLANT_OBLIQUE}; + use self::ffi::{FC_WEIGHT_THIN, FC_WEIGHT_EXTRALIGHT, FC_WEIGHT_LIGHT}; + use self::ffi::{FC_WEIGHT_BOOK, FC_WEIGHT_REGULAR, FC_WEIGHT_MEDIUM, FC_WEIGHT_SEMIBOLD}; + use self::ffi::{FC_WEIGHT_BOLD, FC_WEIGHT_EXTRABOLD, FC_WEIGHT_BLACK, FC_WEIGHT_EXTRABLACK}; /// Iterator over a font set pub struct FontSetIter<'a> { @@ -48,6 +51,7 @@ mod fc { ffi_type!(FontSet, FontSetRef, FcFontSet, FcFontSetDestroy); impl ObjectSet { + #[allow(dead_code)] pub fn new() -> ObjectSet { ObjectSet(unsafe { FcObjectSetCreate() @@ -55,6 +59,89 @@ mod fc { } } + /// Find the font closest matching the provided pattern. + #[allow(dead_code)] + pub fn font_match( + config: &ConfigRef, + pattern: &mut PatternRef, + ) -> Option<Pattern> { + pattern.config_subsitute(config, MatchKind::Pattern); + pattern.default_substitute(); + + unsafe { + // What is this result actually used for? Seems redundant with + // return type. + let mut result = FcResultNoMatch; + let ptr = FcFontMatch( + config.as_ptr(), + pattern.as_ptr(), + &mut result, + ); + + if ptr.is_null() { + None + } else { + Some(Pattern::from_ptr(ptr)) + } + } + } + + /// list fonts by closeness to the pattern + pub fn font_sort( + config: &ConfigRef, + pattern: &mut PatternRef, + ) -> Option<FontSet> { + pattern.config_subsitute(config, MatchKind::Pattern); + pattern.default_substitute(); + + unsafe { + // What is this result actually used for? Seems redundant with + // return type. + let mut result = FcResultNoMatch; + + let mut charsets: *mut FcCharSet = ptr::null_mut(); + + let ptr = FcFontSort( + config.as_ptr(), + pattern.as_ptr(), + 0, // false + &mut charsets, + &mut result, + ); + + if ptr.is_null() { + None + } else { + Some(FontSet::from_ptr(ptr)) + } + } + } + + /// List fonts matching pattern + #[allow(dead_code)] + pub fn font_list( + config: &ConfigRef, + pattern: &mut PatternRef, + objects: &ObjectSetRef, + ) -> Option<FontSet> { + pattern.config_subsitute(config, MatchKind::Pattern); + pattern.default_substitute(); + + unsafe { + let ptr = FcFontList( + config.as_ptr(), + pattern.as_ptr(), + objects.as_ptr(), + ); + + if ptr.is_null() { + None + } else { + Some(FontSet::from_ptr(ptr)) + } + } + } + impl ObjectSetRef { fn add(&mut self, property: &[u8]) { unsafe { @@ -79,13 +166,28 @@ mod fc { } macro_rules! pattern_add_string { - ($name:ident => $object:expr) => { - #[inline] - pub fn $name(&mut self, value: &str) -> bool { - unsafe { - self.add_string($object, value) + ($($name:ident => $object:expr),*) => { + $( + #[inline] + pub fn $name(&mut self, value: &str) -> bool { + unsafe { + self.add_string($object, value) + } } - } + )* + } + } + + macro_rules! pattern_add_int { + ($($name:ident => $object:expr),*) => { + $( + #[inline] + pub fn $name(&mut self, value: &str) -> bool { + unsafe { + self.add_string($object, value) + } + } + )* } } @@ -102,6 +204,14 @@ mod fc { Application = FcSetApplication as isize, } + /// When matching, how to match + #[derive(Debug, Copy, Clone)] + pub enum MatchKind { + Font = FcMatchFont as isize, + Pattern = FcMatchPattern as isize, + Scan = FcMatchScan as isize, + } + pub unsafe fn char8_to_string(fc_str: *mut FcChar8) -> String { str::from_utf8(CStr::from_ptr(fc_str as *const c_char).to_bytes()).unwrap().to_owned() } @@ -111,20 +221,24 @@ mod fc { $( pub fn $method(&self, id: isize) -> Option<String> { unsafe { - let mut format: *mut FcChar8 = ptr::null_mut(); + self.get_string($property, id) + } + } + )+ + }; + } - let result = FcPatternGetString( + macro_rules! pattern_add_integer { + ($($method:ident() => $property:expr),+) => { + $( + pub fn $method(&self, int: isize) -> bool { + unsafe { + FcPatternAddInteger( self.as_ptr(), $property.as_ptr() as *mut c_char, - id as c_int, - &mut format - ); - - if result == FcResultMatch { - Some(char8_to_string(format)) - } else { - None - } + int as c_int, + &mut index + ) == 1 } } )+ @@ -155,6 +269,28 @@ mod fc { }; } + #[derive(Debug, Copy, Clone)] + pub enum Slant { + Italic = FC_SLANT_ITALIC as isize, + Oblique = FC_SLANT_OBLIQUE as isize, + Roman = FC_SLANT_ROMAN as isize, + } + + #[derive(Debug, Copy, Clone)] + pub enum Weight { + Thin = FC_WEIGHT_THIN as isize, + Extralight = FC_WEIGHT_EXTRALIGHT as isize, + Light = FC_WEIGHT_LIGHT as isize, + Book = FC_WEIGHT_BOOK as isize, + Regular = FC_WEIGHT_REGULAR as isize, + Medium = FC_WEIGHT_MEDIUM as isize, + Semibold = FC_WEIGHT_SEMIBOLD as isize, + Bold = FC_WEIGHT_BOLD as isize, + Extrabold = FC_WEIGHT_EXTRABOLD as isize, + Black = FC_WEIGHT_BLACK as isize, + Extrablack = FC_WEIGHT_EXTRABLACK as isize, + } + impl PatternRef { /// Add a string value to the pattern /// @@ -175,20 +311,75 @@ mod fc { ) == 1 } + unsafe fn add_integer(&self, object: &[u8], int: isize) -> bool { + FcPatternAddInteger( + self.as_ptr(), + object.as_ptr() as *mut c_char, + int as c_int + ) == 1 + } + + unsafe fn get_string(&self, object: &[u8], index: isize) -> Option<String> { + let mut format: *mut FcChar8 = ptr::null_mut(); + + let result = FcPatternGetString( + self.as_ptr(), + object.as_ptr() as *mut c_char, + index as c_int, + &mut format + ); + + if result == FcResultMatch { + Some(char8_to_string(format)) + } else { + None + } + } + pattern_add_string! { - add_family => b"family\0" + add_family => b"family\0", + add_style => b"style\0" + } + + pub fn set_slant(&mut self, slant: Slant) -> bool { + unsafe { + self.add_integer(b"slant\0", slant as isize) + } + } + + pub fn set_weight(&mut self, weight: Weight) -> bool { + unsafe { + self.add_integer(b"weight\0", weight as isize) + } + } + + pub fn file(&self, index: isize) -> Option<PathBuf> { + unsafe { + self.get_string(b"file\0", index) + }.map(From::from) } pattern_get_string! { fontformat() => b"fontformat\0", family() => b"family\0", - file() => b"file\0", style() => b"style\0" } pattern_get_integer! { index() => b"index\0" } + + pub fn config_subsitute(&mut self, config: &ConfigRef, kind: MatchKind) { + unsafe { + FcConfigSubstitute(config.as_ptr(), self.as_ptr(), kind as u32); + } + } + + pub fn default_substitute(&mut self) { + unsafe { + FcDefaultSubstitute(self.as_ptr()); + } + } } impl<'a> IntoIterator for &'a FontSet { @@ -199,6 +390,8 @@ mod fc { (*self.as_ptr()).nfont as isize }; + println!("num fonts = {}", num_fonts); + FontSetIter { font_set: self.deref(), num_fonts: num_fonts as _, @@ -215,6 +408,8 @@ mod fc { (*self.as_ptr()).nfont as isize }; + println!("num fonts = {}", num_fonts); + FontSetIter { font_set: self, num_fonts: num_fonts as _, @@ -282,119 +477,50 @@ mod fc { } } -fn list_families() -> Vec<String> { - let mut families = Vec::new(); - - let config = fc::Config::get_current(); - let font_set = config.get_fonts(fc::SetName::System); - for font in font_set { - if let Some(format) = font.fontformat(0) { - if format == "TrueType" || format == "CFF" { - for id in 0.. { - match font.family(id) { - Some(family) => families.push(family), - None => break, - } - } - } - } - } - - families.sort(); - families.dedup(); - families -} - -#[derive(Debug)] -pub struct Variant { - style: String, - file: PathBuf, - index: isize, -} - -impl Variant { - #[inline] - pub fn path(&self) -> &::std::path::Path { - self.file.as_path() - } - - #[inline] - pub fn index(&self) -> isize { - self.index - } -} - -#[derive(Debug)] -pub struct Family { - name: String, - variants: HashMap<String, Variant>, -} +#[cfg(test)] +mod tests { + use super::fc; -impl fmt::Display for Family { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}: ", self.name)?; - for (k, _v) in &self.variants { - write!(f, "{}, ", k)?; + #[test] + fn font_match() { + let mut pattern = fc::Pattern::new(); + pattern.add_family("monospace"); + pattern.add_style("regular"); + + let config = fc::Config::get_current(); + let font = fc::font_match(config, &mut pattern).expect("match font monospace"); + + print!("family={:?}", font.family(0)); + for i in 0.. { + if let Some(style) = font.style(i) { + print!(", style={:?}, ", style); + } else { + break; + } } - - Ok(()) + println!(""); } -} - -impl Family { - #[inline] - pub fn variants(&self) -> &HashMap<String, Variant> { - &self.variants - } -} -#[allow(mutable_transmutes)] -pub fn get_family_info(family: String) -> Family { - let mut members = Vec::new(); - let config = fc::Config::get_current(); - let font_set = config.get_fonts(fc::SetName::System); - - let mut pattern = fc::Pattern::new(); - pattern.add_family(&family); - - let mut objects = fc::ObjectSet::new(); - objects.add_file(); - objects.add_index(); - objects.add_style(); - - let variants = fc::FontSet::list(&config, unsafe { ::std::mem::transmute(font_set) }, &pattern, &objects); - for variant in &variants { - if let Some(file) = variant.file(0) { - if let Some(style) = variant.style(0) { - if let Some(index) = variant.index(0) { - members.push(Variant { - style: style, - file: PathBuf::from(file), - index: index as isize, - }); + #[test] + fn font_sort() { + let mut pattern = fc::Pattern::new(); + pattern.add_family("monospace"); + pattern.set_slant(fc::Slant::Italic); + + let config = fc::Config::get_current(); + let fonts = fc::font_sort(config, &mut pattern) + .expect("sort font monospace"); + + for font in fonts.into_iter().take(10) { + print!("family={:?}", font.family(0)); + for i in 0.. { + if let Some(style) = font.style(i) { + print!(", style={:?}", style); + } else { + break; } } + println!(""); } } - - Family { - name: family, - variants: members.into_iter().map(|v| (v.style.clone(), v)).collect() - } -} - -pub fn get_font_families() -> HashMap<String, Family> { - list_families() - .into_iter() - .map(|family| (family.clone(), get_family_info(family))) - .collect() -} - -#[cfg(test)] -mod tests { - #[test] - fn get_font_families() { - let families = super::get_font_families(); - assert!(!families.is_empty()); - } } diff --git a/font/src/ft/mod.rs b/font/src/ft/mod.rs index 8e653816..0ffecf03 100644 --- a/font/src/ft/mod.rs +++ b/font/src/ft/mod.rs @@ -17,16 +17,16 @@ use std::collections::HashMap; use freetype::{self, Library, Face}; + mod list_fonts; -use self::list_fonts::{Family, get_font_families}; -use super::{FontDesc, RasterizedGlyph, Metrics, Size, FontKey, GlyphKey}; +use self::list_fonts::fc; +use super::{FontDesc, RasterizedGlyph, Metrics, Size, FontKey, GlyphKey, Weight, Slant, Style}; /// Rasterizes glyphs for a single font face. pub struct FreeTypeRasterizer { faces: HashMap<FontKey, Face<'static>>, library: Library, - system_fonts: HashMap<String, Family>, keys: HashMap<FontDesc, FontKey>, dpi_x: u32, dpi_y: u32, @@ -45,7 +45,6 @@ impl ::Rasterize for FreeTypeRasterizer { let library = Library::init()?; Ok(FreeTypeRasterizer { - system_fonts: get_font_families(), faces: HashMap::new(), keys: HashMap::new(), library: library, @@ -130,13 +129,89 @@ impl ::Rasterize for FreeTypeRasterizer { } } +pub trait IntoFontconfigType { + type FcType; + fn into_fontconfig_type(&self) -> Self::FcType; +} + +impl IntoFontconfigType for Slant { + type FcType = fc::Slant; + fn into_fontconfig_type(&self) -> Self::FcType { + match *self { + Slant::Normal => fc::Slant::Roman, + Slant::Italic => fc::Slant::Italic, + Slant::Oblique => fc::Slant::Oblique, + } + } +} + +impl IntoFontconfigType for Weight { + type FcType = fc::Weight; + + fn into_fontconfig_type(&self) -> Self::FcType { + match *self { + Weight::Normal => fc::Weight::Regular, + Weight::Bold => fc::Weight::Bold, + } + } +} + impl FreeTypeRasterizer { + /// Load a font face accoring to `FontDesc` fn get_face(&mut self, desc: &FontDesc) -> Result<Face<'static>, Error> { - self.system_fonts - .get(&desc.name[..]) - .and_then(|font| font.variants().get(&desc.style[..])) - .ok_or_else(|| Error::MissingFont(desc.to_owned())) - .and_then(|variant| Ok(self.library.new_face(variant.path(), variant.index())?)) + match desc.style { + Style::Description { slant, weight } => { + // Match nearest font + self.get_matching_face(&desc, slant, weight) + } + Style::Specific(ref style) => { + // If a name was specified, try and load specifically that font. + self.get_specific_face(&desc, &style) + } + } + } + + fn get_matching_face( + &mut self, + desc: &FontDesc, + slant: Slant, + weight: Weight + ) -> Result<Face<'static>, Error> { + 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()); + + let fonts = fc::font_sort(fc::Config::get_current(), &mut pattern) + .ok_or_else(|| Error::MissingFont(desc.to_owned()))?; + + // Take first font that has a path + for font in &fonts { + if let (Some(path), Some(index)) = (font.file(0), font.index(0)) { + return Ok(self.library.new_face(path, index)?); + } + } + + Err(Error::MissingFont(desc.to_owned())) + } + + fn get_specific_face( + &mut self, + desc: &FontDesc, + style: &str + ) -> Result<Face<'static>, Error> { + let mut pattern = fc::Pattern::new(); + pattern.add_family(&desc.name); + pattern.add_style(style); + + let font = fc::font_match(fc::Config::get_current(), &mut pattern) + .ok_or_else(|| Error::MissingFont(desc.to_owned()))?; + if let (Some(path), Some(index)) = (font.file(0), font.index(0)) { + println!("got font path={:?}", path); + return Ok(self.library.new_face(path, index)?); + } else { + Err(Error::MissingFont(desc.to_owned())) + } } } @@ -193,12 +268,3 @@ impl From<freetype::Error> for Error { } unsafe impl Send for FreeTypeRasterizer {} - -#[cfg(test)] -mod tests { - use ::FontDesc; - - fn font_desc() -> FontDesc { - FontDesc::new("Ubuntu Mono", "Regular") - } -} diff --git a/font/src/lib.rs b/font/src/lib.rs index afe2b94f..bc4a2006 100644 --- a/font/src/lib.rs +++ b/font/src/lib.rs @@ -57,16 +57,47 @@ pub use darwin::*; #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct FontDesc { name: String, - style: String, + style: Style, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum Slant { + Normal, + Italic, + Oblique, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum Weight { + Normal, + Bold +} + +/// Style of font +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum Style { + Specific(String), + Description { slant: Slant, weight: Weight } +} + +impl fmt::Display for Style { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + Style::Specific(ref s) => f.write_str(&s), + Style::Description { slant, weight } => { + write!(f, "slant={:?}, weight={:?}", slant, weight) + }, + } + } } impl FontDesc { - pub fn new<S>(name: S, style: S) -> FontDesc + pub fn new<S>(name: S, style: Style) -> FontDesc where S: Into<String> { FontDesc { name: name.into(), - style: style.into() + style: style } } } diff --git a/src/config.rs b/src/config.rs index abf0bdf5..59b9f5ea 100644 --- a/src/config.rs +++ b/src/config.rs @@ -977,16 +977,13 @@ impl DeserializeFromF32 for Size { #[derive(Debug, Deserialize)] pub struct Font { /// Font family - family: String, + pub normal: FontDescription, - /// Font style - style: String, + #[serde(default="default_italic_desc")] + pub italic: FontDescription, - /// Bold font style - bold_style: Option<String>, - - /// Italic font style - italic_style: Option<String>, + #[serde(default="default_bold_desc")] + pub bold: FontDescription, // Font size in points #[serde(deserialize_with="DeserializeFromF32::deserialize_from_f32")] @@ -996,35 +993,31 @@ pub struct Font { offset: FontOffset, } -impl Font { - /// Get the font family - #[inline] - pub fn family(&self) -> &str { - &self.family[..] - } +fn default_bold_desc() -> FontDescription { + Font::default().bold +} - /// Get the font style - #[inline] - pub fn style(&self) -> &str { - &self.style[..] - } +fn default_italic_desc() -> FontDescription { + Font::default().italic +} - /// Get italic font style; assumes same family - #[inline] - pub fn italic_style(&self) -> Option<&str> { - self.italic_style - .as_ref() - .map(|s| s.as_str()) - } +/// Description of a single font +#[derive(Debug, Deserialize)] +pub struct FontDescription { + pub family: String, + pub style: Option<String>, +} - /// Get bold font style; assumes same family - #[inline] - pub fn bold_style(&self) -> Option<&str> { - self.bold_style - .as_ref() - .map(|s| s.as_str()) +impl FontDescription { + fn new_with_family<S: Into<String>>(family: S) -> FontDescription { + FontDescription { + family: family.into(), + style: None, + } } +} +impl Font { /// Get the font size in points #[inline] pub fn size(&self) -> Size { @@ -1042,11 +1035,10 @@ impl Font { impl Default for Font { fn default() -> Font { Font { - family: String::from("Menlo"), - style: String::from("Regular"), + normal: FontDescription::new_with_family("Menlo"), + bold: FontDescription::new_with_family("Menlo"), + italic: FontDescription::new_with_family("Menlo"), size: Size::new(11.0), - bold_style: Some(String::from("Bold")), - italic_style: Some(String::from("Italic")), offset: FontOffset { x: 0.0, y: 0.0 @@ -1059,11 +1051,10 @@ impl Default for Font { impl Default for Font { fn default() -> Font { Font { - family: String::from("DejaVu Sans Mono"), - style: String::from("Book"), + normal: FontDescription::new_with_family("monospace"), + bold: FontDescription::new_with_family("monospace"), + italic: FontDescription::new_with_family("monospace"), size: Size::new(11.0), - bold_style: Some(String::from("Bold")), - italic_style: Some(String::from("Italic")), offset: FontOffset { // TODO should improve freetype metrics... shouldn't need such // drastic offsets for the default! diff --git a/src/renderer/mod.rs b/src/renderer/mod.rs index d800a1b5..81ac7fd4 100644 --- a/src/renderer/mod.rs +++ b/src/renderer/mod.rs @@ -166,13 +166,29 @@ impl GlyphCache { let size = font.size(); // Load regular font - let regular_desc = FontDesc::new(font.family(), font.style()); + let regular_desc = if let Some(ref style) = font.normal.style { + FontDesc::new(&font.normal.family[..], font::Style::Specific(style.to_owned())) + } else { + let style = font::Style::Description { + slant: font::Slant::Normal, + weight: font::Weight::Normal + }; + FontDesc::new(&font.normal.family[..], style) + }; + let regular = rasterizer .load_font(®ular_desc, size)?; // Load bold font - let bold_style = font.bold_style().unwrap_or("Bold"); - let bold_desc = FontDesc::new(font.family(), bold_style); + let bold_desc = if let Some(ref style) = font.bold.style { + FontDesc::new(&font.bold.family[..], font::Style::Speci |