1
0
Fork 0
mirror of https://github.com/alacritty/alacritty.git synced 2024-11-18 13:55:23 -05:00

Rework font loading

This work started because we wanted to be able to simply say "monospace"
on Linux and have it give us some sort of font. The config format for
fonts changed to accomodate this new paradigm. As a result, italic and
bold can have different families from the normal (roman) face.

The fontconfig based font resolution probably works a lot better than
the CoreText version at this point. With CoreText, we simply iterate
over fonts and check it they match the requested properties. What's
worse is that the CoreText version requires a valid family. With
fontconfig, it will just provide the closest matching thing and use it
(unless a specific style is requested).
This commit is contained in:
Joe Wilm 2017-01-02 18:52:41 -08:00
parent 04d69e3c29
commit 8630185639
7 changed files with 522 additions and 219 deletions

View file

@ -9,14 +9,29 @@ dpi:
# Display tabs using this many cells (changes require restart) # Display tabs using this many cells (changes require restart)
tabspaces: 8 tabspaces: 8
# When true, bold text is drawn using the bright variant of colors.
draw_bold_text_with_bright_colors: true draw_bold_text_with_bright_colors: true
# Font configuration (changes require restart) # Font configuration (changes require restart)
font: font:
family: DejaVu Sans Mono # The normal (roman) font face to use.
style: Book normal:
bold_style: Bold family: monospace # should be "Menlo" or something on macOS.
italic_style: Oblique # 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 # Point size of the font
size: 11.0 size: 11.0
# Offset is the extra space around each character. offset.y can be thought of # Offset is the extra space around each character. offset.y can be thought of

View file

@ -19,6 +19,8 @@
use std::collections::HashMap; use std::collections::HashMap;
use std::ptr; use std::ptr;
use ::{Slant, Weight, Style};
use core_foundation::base::TCFType; use core_foundation::base::TCFType;
use core_foundation::string::{CFString, CFStringRef}; use core_foundation::string::{CFString, CFStringRef};
use core_foundation::array::CFIndex; 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::kCTFontHorizontalOrientation;
use core_text::font_descriptor::kCTFontVerticalOrientation; use core_text::font_descriptor::kCTFontVerticalOrientation;
use core_text::font_descriptor::{CTFontDescriptor, CTFontDescriptorRef, CTFontOrientation}; use core_text::font_descriptor::{CTFontDescriptor, CTFontDescriptorRef, CTFontOrientation};
use core_text::font_descriptor::SymbolicTraitAccessors;
use libc::{size_t, c_int}; use libc::{size_t, c_int};
@ -166,10 +169,15 @@ impl ::Rasterize for Rasterizer {
} }
impl 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[..]); let descriptors = descriptors_for_family(&desc.name[..]);
for descriptor in descriptors { for descriptor in descriptors {
if descriptor.style_name == desc.style { if descriptor.style_name == style {
// Found the font we want // Found the font we want
let scaled_size = size.as_f32_pts() as f64 * self.device_pixel_ratio as f64; let scaled_size = size.as_f32_pts() as f64 * self.device_pixel_ratio as f64;
let font = descriptor.to_font(scaled_size); let font = descriptor.to_font(scaled_size);
@ -179,6 +187,44 @@ impl Rasterizer {
Err(Error::MissingFont(desc.to_owned())) 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 /// 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 { fn glyph_advance(&self, character: char) -> f64 {
let index = self.glyph_index(character).unwrap(); let index = self.glyph_index(character).unwrap();
@ -539,12 +593,9 @@ mod tests {
.collect::<Vec<_>>(); .collect::<Vec<_>>();
for font in fonts { for font in fonts {
// Check deref
println!("family: {}", font.family_name());
// Get a glyph // Get a glyph
for c in &['a', 'b', 'c', 'd'] { 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 // Debug the glyph.. sigh
for row in 0..glyph.height { for row in 0..glyph.height {

View file

@ -12,28 +12,31 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
// //
use std::collections::HashMap; pub mod fc {
use std::fmt;
use std::path::PathBuf;
mod fc {
use std::ptr; use std::ptr;
use std::ffi::{CStr, CString}; use std::ffi::{CStr, CString};
use std::str; use std::str;
use std::ops::Deref; use std::ops::Deref;
use std::path::PathBuf;
use ffi_util::ForeignTypeRef; use ffi_util::{ForeignType, ForeignTypeRef};
use libc::{c_char, c_int}; use libc::{c_char, c_int};
use fontconfig::fontconfig as ffi; use fontconfig::fontconfig as ffi;
use self::ffi::{FcConfigGetCurrent, FcConfigGetFonts, FcSetSystem, FcSetApplication}; use self::ffi::{FcConfigGetCurrent, FcConfigGetFonts, FcSetSystem, FcSetApplication};
use self::ffi::{FcPatternGetString, FcPatternCreate, FcPatternAddString}; use self::ffi::{FcPatternGetString, FcPatternCreate, FcPatternAddString};
use self::ffi::{FcPatternGetInteger}; use self::ffi::{FcPatternGetInteger, FcPatternAddInteger};
use self::ffi::{FcObjectSetCreate, FcObjectSetAdd}; use self::ffi::{FcObjectSetCreate, FcObjectSetAdd};
use self::ffi::{FcResultMatch, FcFontSetList}; use self::ffi::{FcResultMatch, FcResultNoMatch, FcFontSetList};
use self::ffi::{FcChar8, FcConfig, FcPattern, FcFontSet, FcObjectSet}; use self::ffi::{FcChar8, FcConfig, FcPattern, FcFontSet, FcObjectSet, FcCharSet};
use self::ffi::{FcFontSetDestroy, FcPatternDestroy, FcObjectSetDestroy, FcConfigDestroy}; 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 /// Iterator over a font set
pub struct FontSetIter<'a> { pub struct FontSetIter<'a> {
@ -48,6 +51,7 @@ mod fc {
ffi_type!(FontSet, FontSetRef, FcFontSet, FcFontSetDestroy); ffi_type!(FontSet, FontSetRef, FcFontSet, FcFontSetDestroy);
impl ObjectSet { impl ObjectSet {
#[allow(dead_code)]
pub fn new() -> ObjectSet { pub fn new() -> ObjectSet {
ObjectSet(unsafe { ObjectSet(unsafe {
FcObjectSetCreate() 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 { impl ObjectSetRef {
fn add(&mut self, property: &[u8]) { fn add(&mut self, property: &[u8]) {
unsafe { unsafe {
@ -79,13 +166,28 @@ mod fc {
} }
macro_rules! pattern_add_string { macro_rules! pattern_add_string {
($name:ident => $object:expr) => { ($($name:ident => $object:expr),*) => {
#[inline] $(
pub fn $name(&mut self, value: &str) -> bool { #[inline]
unsafe { pub fn $name(&mut self, value: &str) -> bool {
self.add_string($object, value) 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, 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 { 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() 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> { pub fn $method(&self, id: isize) -> Option<String> {
unsafe { 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(), self.as_ptr(),
$property.as_ptr() as *mut c_char, $property.as_ptr() as *mut c_char,
id as c_int, int as c_int,
&mut format &mut index
); ) == 1
if result == FcResultMatch {
Some(char8_to_string(format))
} else {
None
}
} }
} }
)+ )+
@ -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 { impl PatternRef {
/// Add a string value to the pattern /// Add a string value to the pattern
/// ///
@ -175,20 +311,75 @@ mod fc {
) == 1 ) == 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! { 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! { pattern_get_string! {
fontformat() => b"fontformat\0", fontformat() => b"fontformat\0",
family() => b"family\0", family() => b"family\0",
file() => b"file\0",
style() => b"style\0" style() => b"style\0"
} }
pattern_get_integer! { pattern_get_integer! {
index() => b"index\0" 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 { impl<'a> IntoIterator for &'a FontSet {
@ -199,6 +390,8 @@ mod fc {
(*self.as_ptr()).nfont as isize (*self.as_ptr()).nfont as isize
}; };
println!("num fonts = {}", num_fonts);
FontSetIter { FontSetIter {
font_set: self.deref(), font_set: self.deref(),
num_fonts: num_fonts as _, num_fonts: num_fonts as _,
@ -215,6 +408,8 @@ mod fc {
(*self.as_ptr()).nfont as isize (*self.as_ptr()).nfont as isize
}; };
println!("num fonts = {}", num_fonts);
FontSetIter { FontSetIter {
font_set: self, font_set: self,
num_fonts: num_fonts as _, 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>,
}
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)?;
}
Ok(())
}
}
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,
});
}
}
}
}
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)] #[cfg(test)]
mod tests { mod tests {
use super::fc;
#[test] #[test]
fn get_font_families() { fn font_match() {
let families = super::get_font_families(); let mut pattern = fc::Pattern::new();
assert!(!families.is_empty()); 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;
}
}
println!("");
}
#[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!("");
}
} }
} }

View file

@ -17,16 +17,16 @@ use std::collections::HashMap;
use freetype::{self, Library, Face}; use freetype::{self, Library, Face};
mod list_fonts; mod list_fonts;
use self::list_fonts::{Family, get_font_families}; use self::list_fonts::fc;
use super::{FontDesc, RasterizedGlyph, Metrics, Size, FontKey, GlyphKey}; use super::{FontDesc, RasterizedGlyph, Metrics, Size, FontKey, GlyphKey, Weight, Slant, Style};
/// Rasterizes glyphs for a single font face. /// Rasterizes glyphs for a single font face.
pub struct FreeTypeRasterizer { pub struct FreeTypeRasterizer {
faces: HashMap<FontKey, Face<'static>>, faces: HashMap<FontKey, Face<'static>>,
library: Library, library: Library,
system_fonts: HashMap<String, Family>,
keys: HashMap<FontDesc, FontKey>, keys: HashMap<FontDesc, FontKey>,
dpi_x: u32, dpi_x: u32,
dpi_y: u32, dpi_y: u32,
@ -45,7 +45,6 @@ impl ::Rasterize for FreeTypeRasterizer {
let library = Library::init()?; let library = Library::init()?;
Ok(FreeTypeRasterizer { Ok(FreeTypeRasterizer {
system_fonts: get_font_families(),
faces: HashMap::new(), faces: HashMap::new(),
keys: HashMap::new(), keys: HashMap::new(),
library: library, 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 { impl FreeTypeRasterizer {
/// Load a font face accoring to `FontDesc`
fn get_face(&mut self, desc: &FontDesc) -> Result<Face<'static>, Error> { fn get_face(&mut self, desc: &FontDesc) -> Result<Face<'static>, Error> {
self.system_fonts match desc.style {
.get(&desc.name[..]) Style::Description { slant, weight } => {
.and_then(|font| font.variants().get(&desc.style[..])) // Match nearest font
.ok_or_else(|| Error::MissingFont(desc.to_owned())) self.get_matching_face(&desc, slant, weight)
.and_then(|variant| Ok(self.library.new_face(variant.path(), variant.index())?)) }
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 {} unsafe impl Send for FreeTypeRasterizer {}
#[cfg(test)]
mod tests {
use ::FontDesc;
fn font_desc() -> FontDesc {
FontDesc::new("Ubuntu Mono", "Regular")
}
}

View file

@ -57,16 +57,47 @@ pub use darwin::*;
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct FontDesc { pub struct FontDesc {
name: String, 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 { impl FontDesc {
pub fn new<S>(name: S, style: S) -> FontDesc pub fn new<S>(name: S, style: Style) -> FontDesc
where S: Into<String> where S: Into<String>
{ {
FontDesc { FontDesc {
name: name.into(), name: name.into(),
style: style.into() style: style
} }
} }
} }

View file

@ -977,16 +977,13 @@ impl DeserializeFromF32 for Size {
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
pub struct Font { pub struct Font {
/// Font family /// Font family
family: String, pub normal: FontDescription,
/// Font style #[serde(default="default_italic_desc")]
style: String, pub italic: FontDescription,
/// Bold font style #[serde(default="default_bold_desc")]
bold_style: Option<String>, pub bold: FontDescription,
/// Italic font style
italic_style: Option<String>,
// Font size in points // Font size in points
#[serde(deserialize_with="DeserializeFromF32::deserialize_from_f32")] #[serde(deserialize_with="DeserializeFromF32::deserialize_from_f32")]
@ -996,35 +993,31 @@ pub struct Font {
offset: FontOffset, offset: FontOffset,
} }
fn default_bold_desc() -> FontDescription {
Font::default().bold
}
fn default_italic_desc() -> FontDescription {
Font::default().italic
}
/// Description of a single font
#[derive(Debug, Deserialize)]
pub struct FontDescription {
pub family: String,
pub style: Option<String>,
}
impl FontDescription {
fn new_with_family<S: Into<String>>(family: S) -> FontDescription {
FontDescription {
family: family.into(),
style: None,
}
}
}
impl Font { impl Font {
/// Get the font family
#[inline]
pub fn family(&self) -> &str {
&self.family[..]
}
/// Get the font style
#[inline]
pub fn style(&self) -> &str {
&self.style[..]
}
/// 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())
}
/// 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())
}
/// Get the font size in points /// Get the font size in points
#[inline] #[inline]
pub fn size(&self) -> Size { pub fn size(&self) -> Size {
@ -1042,11 +1035,10 @@ impl Font {
impl Default for Font { impl Default for Font {
fn default() -> Font { fn default() -> Font {
Font { Font {
family: String::from("Menlo"), normal: FontDescription::new_with_family("Menlo"),
style: String::from("Regular"), bold: FontDescription::new_with_family("Menlo"),
italic: FontDescription::new_with_family("Menlo"),
size: Size::new(11.0), size: Size::new(11.0),
bold_style: Some(String::from("Bold")),
italic_style: Some(String::from("Italic")),
offset: FontOffset { offset: FontOffset {
x: 0.0, x: 0.0,
y: 0.0 y: 0.0
@ -1059,11 +1051,10 @@ impl Default for Font {
impl Default for Font { impl Default for Font {
fn default() -> Font { fn default() -> Font {
Font { Font {
family: String::from("DejaVu Sans Mono"), normal: FontDescription::new_with_family("monospace"),
style: String::from("Book"), bold: FontDescription::new_with_family("monospace"),
italic: FontDescription::new_with_family("monospace"),
size: Size::new(11.0), size: Size::new(11.0),
bold_style: Some(String::from("Bold")),
italic_style: Some(String::from("Italic")),
offset: FontOffset { offset: FontOffset {
// TODO should improve freetype metrics... shouldn't need such // TODO should improve freetype metrics... shouldn't need such
// drastic offsets for the default! // drastic offsets for the default!

View file

@ -166,13 +166,29 @@ impl GlyphCache {
let size = font.size(); let size = font.size();
// Load regular font // 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 let regular = rasterizer
.load_font(&regular_desc, size)?; .load_font(&regular_desc, size)?;
// Load bold font // Load bold font
let bold_style = font.bold_style().unwrap_or("Bold"); let bold_desc = if let Some(ref style) = font.bold.style {
let bold_desc = FontDesc::new(font.family(), bold_style); FontDesc::new(&font.bold.family[..], font::Style::Specific(style.to_owned()))
} else {
let style = font::Style::Description {
slant: font::Slant::Normal,
weight: font::Weight::Bold
};
FontDesc::new(&font.bold.family[..], style)
};
let bold = if bold_desc == regular_desc { let bold = if bold_desc == regular_desc {
regular regular
@ -181,8 +197,15 @@ impl GlyphCache {
}; };
// Load italic font // Load italic font
let italic_style = font.italic_style().unwrap_or("Italic"); let italic_desc = if let Some(ref style) = font.italic.style {
let italic_desc = FontDesc::new(font.family(), italic_style); FontDesc::new(&font.italic.family[..], font::Style::Specific(style.to_owned()))
} else {
let style = font::Style::Description {
slant: font::Slant::Italic,
weight: font::Weight::Normal
};
FontDesc::new(&font.italic.family[..], style)
};
let italic = if italic_desc == regular_desc { let italic = if italic_desc == regular_desc {
regular regular