diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/list_fonts.rs | 141 |
1 files changed, 119 insertions, 22 deletions
diff --git a/src/list_fonts.rs b/src/list_fonts.rs index 9b15ea3d..c418bc02 100644 --- a/src/list_fonts.rs +++ b/src/list_fonts.rs @@ -1,16 +1,24 @@ -use std::ffi::CStr; +use std::ffi::{CStr, CString}; +use std::path::PathBuf; use std::ptr; -use std::str; +use std::str::from_utf8; use libc::{c_char, c_int}; use fontconfig::fontconfig::{FcConfigGetCurrent, FcConfigGetFonts, FcSetSystem}; -use fontconfig::fontconfig::{FcPatternGetString}; -use fontconfig::fontconfig::{FcResultMatch}; +use fontconfig::fontconfig::{FcPatternGetString, FcPatternCreate, FcPatternAddString}; +use fontconfig::fontconfig::{FcPatternGetInteger}; +use fontconfig::fontconfig::{FcObjectSetCreate, FcObjectSetAdd}; +use fontconfig::fontconfig::{FcResultMatch, FcFontSetList}; use fontconfig::fontconfig::{FcChar8}; +use fontconfig::fontconfig::{FcFontSetDestroy, FcPatternDestroy, FcObjectSetDestroy}; -pub fn list_font_names() -> Vec<String> { - let mut fonts = Vec::new(); +unsafe fn fc_char8_to_string(fc_str: *mut FcChar8) -> String { + from_utf8(CStr::from_ptr(fc_str as *const c_char).to_bytes()).unwrap().to_owned() +} + +fn list_families() -> Vec<String> { + let mut families = Vec::new(); unsafe { // https://www.freedesktop.org/software/fontconfig/fontconfig-devel/fcconfiggetcurrent.html let config = FcConfigGetCurrent(); // *mut FcConfig @@ -22,36 +30,125 @@ pub fn list_font_names() -> Vec<String> { for i in 0..nfont { let font = (*font_set).fonts.offset(i); // *mut FcPattern let id = 0 as c_int; - let mut fullname: *mut FcChar8 = ptr::null_mut(); + let mut family: *mut FcChar8 = ptr::null_mut(); + let mut format: *mut FcChar8 = ptr::null_mut(); - // The second parameter here (fullname) is from the "FONT PROPERTIES" table: - // https://www.freedesktop.org/software/fontconfig/fontconfig-devel/x19.html let result = FcPatternGetString(*font, - b"fullname\0".as_ptr() as *mut c_char, + b"fontformat\0".as_ptr() as *mut c_char, id, - &mut fullname); + &mut format); + if result != FcResultMatch { continue; } - let s = str::from_utf8(CStr::from_ptr(fullname as *const c_char).to_bytes()) - .unwrap().to_owned(); - fonts.push(s); + let format = fc_char8_to_string(format); + + if format != "TrueType" && format != "CFF" { + continue + } + + let mut id = 0; + while FcPatternGetString(*font, b"family\0".as_ptr() as *mut c_char, id, &mut family) == FcResultMatch { + let safe_family = fc_char8_to_string(family); + id += 1; + families.push(safe_family); + } } } - fonts + families.sort(); + families.dedup(); + families +} + +#[derive(Debug)] +pub struct Variant { + style: String, + file: PathBuf, + index: usize, +} + +#[derive(Debug)] +pub struct Family { + name: String, + variants: Vec<Variant>, +} + +static FILE: &'static [u8] = b"file\0"; +static FAMILY: &'static [u8] = b"family\0"; +static INDEX: &'static [u8] = b"index\0"; +static STYLE: &'static [u8] = b"style\0"; + +pub fn get_family_info(family: String) -> Family { + + let mut members = Vec::new(); + + unsafe { + let config = FcConfigGetCurrent(); // *mut FcConfig + let mut font_set = FcConfigGetFonts(config, FcSetSystem); // *mut FcFontSet + + let pattern = FcPatternCreate(); + let family_name = CString::new(&family[..]).unwrap(); + let family_name = family_name.as_ptr(); + + // Add family name to pattern. Use this for searching. + FcPatternAddString(pattern, FAMILY.as_ptr() as *mut c_char, family_name as *mut FcChar8); + + // Request filename, style, and index for each variant in family + let object_set = FcObjectSetCreate(); // *mut FcObjectSet + FcObjectSetAdd(object_set, FILE.as_ptr() as *mut c_char); + FcObjectSetAdd(object_set, INDEX.as_ptr() as *mut c_char); + FcObjectSetAdd(object_set, STYLE.as_ptr() as *mut c_char); + + let variants = FcFontSetList(config, &mut font_set, 1 /* nsets */, pattern, object_set); + let num_variant = (*variants).nfont as isize; + + for i in 0..num_variant { + let font = (*variants).fonts.offset(i); + let mut file: *mut FcChar8 = ptr::null_mut(); + assert_eq!(FcPatternGetString(*font, FILE.as_ptr() as *mut c_char, 0, &mut file), + FcResultMatch); + let file = fc_char8_to_string(file); + + let mut style: *mut FcChar8 = ptr::null_mut(); + assert_eq!(FcPatternGetString(*font, STYLE.as_ptr() as *mut c_char, 0, &mut style), + FcResultMatch); + let style = fc_char8_to_string(style); + + let mut index = 0 as c_int; + assert_eq!(FcPatternGetInteger(*font, INDEX.as_ptr() as *mut c_char, 0, &mut index), + FcResultMatch); + + members.push(Variant { + style: style, + file: PathBuf::from(file), + index: index as usize, + }); + } + + FcFontSetDestroy(variants); + FcPatternDestroy(pattern); + FcObjectSetDestroy(object_set); + } + + Family { + name: family, + variants: members + } +} + +pub fn get_font_families() -> Vec<Family> { + list_families().into_iter() + .map(|family| get_family_info(family)) + .collect() } #[cfg(test)] mod tests { - use super::list_font_names; - #[test] - fn list_fonts() { - let fonts = list_font_names(); - assert!(!fonts.is_empty()); - - println!("fonts: {:?}", fonts); + fn get_font_families() { + let families = super::get_font_families(); + assert!(!families.is_empty()); } } |