qrsimple-cli 1.2.0

Command line tool to generate QR codes.
Documentation
pub use inner::test_fonts;

#[cfg(all(unix, feature = "fontconfig"))]
mod inner {
    use fc::{FcChar32, FcCharSet, FcConfig, FcPattern, FcResultMatch, FcSetSystem};
    use fontconfig::fontconfig as fc;
    use std::collections::HashSet;
    use std::ptr;

    pub fn test_fonts(
        sextants: &mut HashSet<char>,
        octants: &mut HashSet<char>,
    ) -> Option<()> {
        let config = load_config()?;
        let (count, patterns) = get_patterns(config)?;
        for index in 0..count {
            let pattern = match get_pattern(patterns, index) {
                Some(pattern) => pattern,
                None => continue,
            };
            let charset = match get_charset(pattern) {
                Some(charset) => charset,
                None => continue,
            };
            test_charset(sextants, octants, charset);
            if octants.is_empty() {
                break;
            }
        }
        Some(())
    }

    fn load_config() -> Option<*mut FcConfig> {
        let result = unsafe { fc::FcInit() };
        if result != 0 {
            let config = unsafe { fc::FcInitLoadConfigAndFonts() };
            if !config.is_null() {
                return Some(config);
            }
        }
        None
    }

    fn get_patterns(config: *mut FcConfig) -> Option<(isize, *mut *mut FcPattern)> {
        let fonts = unsafe { fc::FcConfigGetFonts(config, FcSetSystem) };
        if !fonts.is_null() {
            let count = unsafe { (*fonts).nfont as isize };
            let patterns = unsafe { (*fonts).fonts };
            Some((count, patterns))
        } else {
            None
        }
    }

    fn get_pattern(patterns: *mut *mut FcPattern, index: isize) -> Option<*mut FcPattern> {
        let pattern = unsafe { *patterns.offset(index) };
        if !pattern.is_null() {
            Some(pattern)
        } else {
            None
        }
    }

    fn get_charset(pattern: *mut FcPattern) -> Option<*mut FcCharSet> {
        let mut charset: *mut FcCharSet = ptr::null_mut();
        let object = b"charset\0".as_ptr() as *const _;
        let result = unsafe { fc::FcPatternGetCharSet(pattern, object, 0, &mut charset) };
        if result == FcResultMatch && !charset.is_null() {
            Some(charset)
        } else {
            None
        }
    }

    fn test_charset(
        sextants: &mut HashSet<char>,
        octants: &mut HashSet<char>,
        charset: *mut FcCharSet,
    ) {
        const MAP_SIZE: usize = 256 / 32; // 8
        let mut map: [FcChar32; MAP_SIZE] = [0; MAP_SIZE];
        let mut next: FcChar32 = 0;
        let mut page = unsafe { fc::FcCharSetFirstPage(charset, map.as_mut_ptr(), &mut next as *mut _) };
        while page != !0_u32 {
            for (index, &chunk) in map.iter().enumerate() {
                let bits = chunk as u32;
                for bit in 0..32_u32 {
                    if bits & (1_u32 << bit) != 0 {
                        let cp = page.wrapping_add((index as u32) * 32 + bit);
                        if let Some(char) = char::from_u32(cp) {
                            sextants.remove(&char);
                            octants.remove(&char);
                        }
                    }
                }
            }
            page = unsafe { fc::FcCharSetNextPage(charset, map.as_mut_ptr(), &mut next as *mut _) };
        }
    }
}

#[cfg(not(all(unix, feature = "fontconfig")))]
mod inner {
    use std::collections::HashSet;

    pub fn test_fonts(
        _sextants: &mut HashSet<char>,
        _octants: &mut HashSet<char>,
    ) -> Option<()> {
        None
    }
}