Skip to main content

qrsimple_cli/
font.rs

1pub use inner::test_fonts;
2
3#[cfg(all(unix, feature = "fontconfig"))]
4mod inner {
5    use fc::{FcChar32, FcCharSet, FcConfig, FcPattern, FcResultMatch, FcSetSystem};
6    use fontconfig::fontconfig as fc;
7    use std::collections::HashSet;
8    use std::ptr;
9
10    pub fn test_fonts(
11        sextants: &mut HashSet<char>,
12        octants: &mut HashSet<char>,
13    ) -> Option<()> {
14        let config = load_config()?;
15        let (count, patterns) = get_patterns(config)?;
16        for index in 0..count {
17            let pattern = match get_pattern(patterns, index) {
18                Some(pattern) => pattern,
19                None => continue,
20            };
21            let charset = match get_charset(pattern) {
22                Some(charset) => charset,
23                None => continue,
24            };
25            test_charset(sextants, octants, charset);
26            if octants.is_empty() {
27                break;
28            }
29        }
30        Some(())
31    }
32
33    fn load_config() -> Option<*mut FcConfig> {
34        let result = unsafe { fc::FcInit() };
35        if result != 0 {
36            let config = unsafe { fc::FcInitLoadConfigAndFonts() };
37            if !config.is_null() {
38                return Some(config);
39            }
40        }
41        None
42    }
43
44    fn get_patterns(config: *mut FcConfig) -> Option<(isize, *mut *mut FcPattern)> {
45        let fonts = unsafe { fc::FcConfigGetFonts(config, FcSetSystem) };
46        if !fonts.is_null() {
47            let count = unsafe { (*fonts).nfont as isize };
48            let patterns = unsafe { (*fonts).fonts };
49            Some((count, patterns))
50        } else {
51            None
52        }
53    }
54
55    fn get_pattern(patterns: *mut *mut FcPattern, index: isize) -> Option<*mut FcPattern> {
56        let pattern = unsafe { *patterns.offset(index) };
57        if !pattern.is_null() {
58            Some(pattern)
59        } else {
60            None
61        }
62    }
63
64    fn get_charset(pattern: *mut FcPattern) -> Option<*mut FcCharSet> {
65        let mut charset: *mut FcCharSet = ptr::null_mut();
66        let object = b"charset\0".as_ptr() as *const _;
67        let result = unsafe { fc::FcPatternGetCharSet(pattern, object, 0, &mut charset) };
68        if result == FcResultMatch && !charset.is_null() {
69            Some(charset)
70        } else {
71            None
72        }
73    }
74
75    fn test_charset(
76        sextants: &mut HashSet<char>,
77        octants: &mut HashSet<char>,
78        charset: *mut FcCharSet,
79    ) {
80        const MAP_SIZE: usize = 256 / 32; // 8
81        let mut map: [FcChar32; MAP_SIZE] = [0; MAP_SIZE];
82        let mut next: FcChar32 = 0;
83        let mut page = unsafe { fc::FcCharSetFirstPage(charset, map.as_mut_ptr(), &mut next as *mut _) };
84        while page != !0_u32 {
85            for (index, &chunk) in map.iter().enumerate() {
86                let bits = chunk as u32;
87                for bit in 0..32_u32 {
88                    if bits & (1_u32 << bit) != 0 {
89                        let cp = page.wrapping_add((index as u32) * 32 + bit);
90                        if let Some(char) = char::from_u32(cp) {
91                            sextants.remove(&char);
92                            octants.remove(&char);
93                        }
94                    }
95                }
96            }
97            page = unsafe { fc::FcCharSetNextPage(charset, map.as_mut_ptr(), &mut next as *mut _) };
98        }
99    }
100}
101
102#[cfg(not(all(unix, feature = "fontconfig")))]
103mod inner {
104    use std::collections::HashSet;
105
106    pub fn test_fonts(
107        _sextants: &mut HashSet<char>,
108        _octants: &mut HashSet<char>,
109    ) -> Option<()> {
110        None
111    }
112}