tinymist_world/font/
profile.rs

1use serde::{Deserialize, Serialize};
2use sha2::Digest;
3use std::{collections::HashMap, time::SystemTime};
4use typst::text::{Coverage, FontInfo};
5
6type FontMetaDict = HashMap<String, String>;
7
8#[derive(Debug, Clone, Serialize, Deserialize)]
9pub struct FontInfoItem {
10    /// customized profile data
11    pub meta: FontMetaDict,
12    /// The informatioin of the font
13    pub info: FontInfo,
14}
15
16impl FontInfoItem {
17    pub fn new(info: FontInfo) -> Self {
18        Self {
19            meta: Default::default(),
20            info,
21        }
22    }
23
24    pub fn index(&self) -> Option<u32> {
25        self.meta.get("index").and_then(|v| v.parse::<u32>().ok())
26    }
27
28    pub fn set_index(&mut self, v: u32) {
29        self.meta.insert("index".to_owned(), v.to_string());
30    }
31
32    pub fn coverage_hash(&self) -> Option<&String> {
33        self.meta.get("coverage_hash")
34    }
35
36    pub fn set_coverage_hash(&mut self, v: String) {
37        self.meta.insert("coverage_hash".to_owned(), v);
38    }
39
40    pub fn meta(&self) -> &FontMetaDict {
41        &self.meta
42    }
43
44    pub fn info(&self) -> &FontInfo {
45        &self.info
46    }
47}
48
49#[derive(Debug, Clone, Serialize, Deserialize)]
50pub struct FontProfileItem {
51    /// The hash of the file
52    pub hash: String,
53    /// customized profile data
54    pub meta: FontMetaDict,
55    /// The informatioin of the font
56    pub info: Vec<FontInfoItem>,
57}
58
59fn to_micro_lossy(t: SystemTime) -> u128 {
60    t.duration_since(SystemTime::UNIX_EPOCH)
61        .unwrap()
62        .as_micros()
63}
64
65impl FontProfileItem {
66    pub fn new(kind: &str, hash: String) -> Self {
67        let mut meta: FontMetaDict = Default::default();
68        meta.insert("kind".to_owned(), kind.to_string());
69
70        Self {
71            hash,
72            meta,
73            info: Default::default(),
74        }
75    }
76
77    pub fn path(&self) -> Option<&String> {
78        self.meta.get("path")
79    }
80
81    pub fn mtime(&self) -> Option<SystemTime> {
82        self.meta.get("mtime").and_then(|v| {
83            let v = v.parse::<u64>().ok();
84            v.map(|v| SystemTime::UNIX_EPOCH + std::time::Duration::from_micros(v))
85        })
86    }
87
88    pub fn mtime_is_exact(&self, t: SystemTime) -> bool {
89        self.mtime()
90            .map(|s| {
91                let s = to_micro_lossy(s);
92                let t = to_micro_lossy(t);
93                s == t
94            })
95            .unwrap_or_default()
96    }
97
98    pub fn set_path(&mut self, v: String) {
99        self.meta.insert("path".to_owned(), v);
100    }
101
102    pub fn set_mtime(&mut self, v: SystemTime) {
103        self.meta
104            .insert("mtime".to_owned(), to_micro_lossy(v).to_string());
105    }
106
107    pub fn hash(&self) -> &str {
108        &self.hash
109    }
110
111    pub fn meta(&self) -> &FontMetaDict {
112        &self.meta
113    }
114
115    pub fn info(&self) -> &[FontInfoItem] {
116        &self.info
117    }
118
119    pub fn add_info(&mut self, info: FontInfoItem) {
120        self.info.push(info);
121    }
122}
123
124#[derive(Default, Debug, Clone, Serialize, Deserialize)]
125pub struct FontProfile {
126    pub version: String,
127    pub build_info: String,
128    pub items: Vec<FontProfileItem>,
129}
130
131pub fn get_font_coverage_hash(coverage: &Coverage) -> String {
132    let mut coverage_hash = sha2::Sha256::new();
133    coverage
134        .iter()
135        .for_each(|c| coverage_hash.update(c.to_le_bytes()));
136    let coverage_hash = coverage_hash.finalize();
137    format!("sha256:{coverage_hash:x}")
138}