use std::path::PathBuf;
use core_foundation::{
base::{CFRange, TCFType},
dictionary::CFDictionary,
number::CFNumber,
string::{CFString, CFStringRef},
};
use core_text::{
font::{new_from_descriptor, CTFont, CTFontRef},
font_descriptor::{
kCTFontBoldTrait, kCTFontCondensedTrait, kCTFontExpandedTrait, kCTFontFamilyNameAttribute, kCTFontItalicTrait,
kCTFontSymbolicTrait, kCTFontTraitsAttribute, kCTFontWeightTrait, kCTFontWidthTrait, new_from_attributes,
},
};
pub fn font_for_text(name: &str, text: &str, weight: u16, stretch: u16, italic: bool) -> Option<(String, PathBuf)> {
let mut symbolic = 0u32;
if weight >= 700 {
symbolic |= kCTFontBoldTrait;
}
if italic {
symbolic |= kCTFontItalicTrait;
}
if stretch < 1000 {
symbolic |= kCTFontCondensedTrait;
} else if stretch > 1000 {
symbolic |= kCTFontExpandedTrait;
}
let weight = if weight < 150 {
-0.80
} else if weight < 250 {
-0.60
} else if weight < 350 {
-0.40
} else if weight < 450 {
0.0
} else if weight < 550 {
0.23
} else if weight < 650 {
0.30
} else if weight < 750 {
0.40
} else if weight < 850 {
0.56
} else if weight < 925 {
0.62
} else {
1.00
};
let stretch = if stretch < (500 + 625) / 2 {
-0.5
} else if stretch < (625 + 750) / 2 {
-0.37
} else if stretch < (750 + 875) / 2 {
-0.25
} else if stretch < (875 + 1000) / 2 {
-0.13
} else if stretch < (1000 + 1125) / 2 {
0.0
} else if stretch < (1125 + 1250) / 2 {
0.13
} else if stretch < (1250 + 1375) / 2 {
0.25
} else if stretch < (1375 + 1500) / 2 {
0.37
} else {
0.5
};
let attributes = CFDictionary::from_CFType_pairs(&[
(
unsafe { CFString::wrap_under_get_rule(kCTFontFamilyNameAttribute) },
CFString::new(name).as_CFType(),
),
(
unsafe { CFString::wrap_under_get_rule(kCTFontTraitsAttribute) },
CFDictionary::from_CFType_pairs(&[
(
unsafe { CFString::wrap_under_get_rule(kCTFontWeightTrait) },
CFNumber::from(weight).as_CFType(),
),
(
unsafe { CFString::wrap_under_get_rule(kCTFontWidthTrait) },
CFNumber::from(stretch).as_CFType(),
),
(
unsafe { CFString::wrap_under_get_rule(kCTFontSymbolicTrait) },
CFNumber::from(symbolic as i32).as_CFType(),
),
])
.as_CFType(),
),
]);
let desc = new_from_attributes(&attributes);
if text.is_empty() {
return Some((desc.family_name(), desc.font_path()?));
}
let font = new_from_descriptor(&desc, 0.0);
let text = CFString::new(text);
let font = unsafe {
CTFont::wrap_under_create_rule(CTFontCreateForString(
font.as_concrete_TypeRef(),
text.as_concrete_TypeRef(),
CFRange::init(0, text.char_len()),
))
};
let desc = font.copy_descriptor();
Some((desc.family_name(), desc.font_path()?))
}
#[link(kind = "framework", name = "CoreText")]
extern "C" {
pub fn CTFontCreateForString(currentFont: CTFontRef, string: CFStringRef, range: CFRange) -> CTFontRef;
}