git.lucas.co / cce-fonts
font browser

src/pages.rs (2.4K)

 1 use std::process::Command;
 2 
 3 #[derive(Debug, Clone)]
 4 pub struct FontEntry {
 5     pub family: String,
 6     pub style: String,
 7     pub file: String,
 8 }
 9 
10 pub fn fetch_fonts() -> Vec<FontEntry> {
11     let output = match Command::new("fc-list")
12         .arg("--format=%{family}\\t%{style}\\t%{file}\\n")
13         .output()
14     {
15         Ok(o) => String::from_utf8_lossy(&o.stdout).into_owned(),
16         Err(_) => return Vec::new(),
17     };
18 
19     let mut fonts: Vec<FontEntry> = output
20         .lines()
21         .filter_map(|line| {
22             let parts: Vec<&str> = line.splitn(3, '\t').collect();
23             if parts.len() == 3 {
24                 let family = parts[0].split(',').next().unwrap_or(parts[0]).trim().to_string();
25                 let style = parts[1].split(',').next().unwrap_or(parts[1]).trim().to_string();
26                 Some(FontEntry {
27                     family,
28                     style,
29                     file: parts[2].trim().to_string(),
30                 })
31             } else {
32                 None
33             }
34         })
35         .collect();
36 
37     fonts.sort_by(|a, b| a.family.to_lowercase().cmp(&b.family.to_lowercase()));
38     fonts.dedup_by(|a, b| a.family == b.family && a.style == b.style);
39     fonts
40 }
41 
42 pub fn is_user_font(file: &str) -> bool {
43     file.starts_with("/home/") || file.contains(".local/share/fonts") || file.contains(".fonts")
44 }
45 
46 pub fn count_chars(file: &str) -> usize {
47     let output = Command::new("fc-query")
48         .arg("--format=%{charset}")
49         .arg(file)
50         .output()
51         .ok();
52 
53     match output {
54         Some(o) => {
55             let s = String::from_utf8_lossy(&o.stdout);
56             let mut count = 0usize;
57             for range in s.split_whitespace() {
58                 if let Some((start, end)) = range.split_once('-') {
59                     if let (Ok(s_val), Ok(e_val)) = (u32::from_str_radix(start, 16), u32::from_str_radix(end, 16)) {
60                         // Guard against malformed/reversed ranges (end < start) — an unsigned
61                         // subtraction there panics and crashes the whole app.
62                         if e_val >= s_val {
63                             count += (e_val - s_val + 1) as usize;
64                         }
65                     }
66                 } else if let Ok(_) = u32::from_str_radix(range, 16) {
67                     count += 1;
68                 }
69             }
70             count
71         }
72         None => 0,
73     }
74 }