zpl_forge/engine/
font.rs

1use std::collections::HashMap;
2
3use ab_glyph::FontArc;
4use font_loader::system_fonts;
5
6/// List of valid ZPL font identifiers (A-Z and 0-9).
7const FONT_MAP: &[char] = &[
8    'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S',
9    'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
10];
11
12/// Manages fonts and their mapping to ZPL font identifiers.
13///
14/// This structure tracks registered fonts and maps them to the single-character
15/// identifiers used in ZPL commands (e.g., '^A0', '^AA').
16#[derive(Debug, Clone)]
17pub struct FontManager {
18    /// Maps ZPL font identifiers (as Strings) to internal font names.
19    font_map: HashMap<String, String>,
20    /// Stores the actual font data indexed by internal font names.
21    font_index: HashMap<String, FontArc>,
22}
23
24impl Default for FontManager {
25    /// Creates a `FontManager` with a default system font registered for all identifiers.
26    ///
27    /// It attempts to load common sans-serif fonts available on the system.
28    fn default() -> Self {
29        let mut current = Self {
30            font_map: HashMap::new(),
31            font_index: HashMap::new(),
32        };
33
34        let families = [
35            "Swiss 721",
36            "Helvetica",
37            "Roboto",
38            "Liberation Sans",
39            "DejaVu Sans",
40            "Arial",
41        ];
42
43        for family in families {
44            let prop = system_fonts::FontPropertyBuilder::new()
45                .family(family)
46                .build();
47            if let Some((data, _)) = system_fonts::get(&prop) {
48                let _ = current.register_font(family, &data, 'A', '9');
49                break;
50            }
51        }
52
53        current
54    }
55}
56
57impl FontManager {
58    /// Retrieves a font by its ZPL identifier.
59    ///
60    /// # Arguments
61    /// * `name` - The ZPL font identifier (e.g., "0", "A").
62    pub fn get_font(&self, name: &str) -> Option<&FontArc> {
63        let font_name = self.font_map.get(name);
64        if let Some(font_name) = font_name {
65            self.font_index.get(font_name)
66        } else {
67            None
68        }
69    }
70
71    /// Registers a new font and maps it to a range of ZPL identifiers.
72    ///
73    /// # Arguments
74    /// * `name` - An internal name for the font.
75    /// * `bytes` - The raw TrueType/OpenType font data.
76    /// * `from` - The starting ZPL identifier in the range.
77    /// * `to` - The ending ZPL identifier in the range.
78    ///
79    /// # Errors
80    /// Returns an error if the font data is invalid.
81    pub fn register_font(
82        &mut self,
83        name: &str,
84        bytes: &[u8],
85        from: char,
86        to: char,
87    ) -> Result<(), String> {
88        let font = FontArc::try_from_vec(bytes.to_vec()).map_err(|_| "Invalid font".to_string())?;
89        self.font_index.insert(name.to_string(), font);
90        self.assign_font(name, from, to);
91        Ok(())
92    }
93
94    /// Internal helper to assign a registered font to a range of ZPL identifiers.
95    fn assign_font(&mut self, name: &str, from: char, to: char) {
96        let from_idx = FONT_MAP.iter().position(|&x| x == from);
97        let to_idx = FONT_MAP.iter().position(|&x| x == to);
98
99        if from_idx.is_none() || to_idx.is_none() {
100            return;
101        }
102
103        if let (Some(start), Some(end)) = (from_idx, to_idx) {
104            if start <= end {
105                for key in &FONT_MAP[start..=end] {
106                    self.font_map.insert(key.to_string(), name.to_string());
107                }
108            }
109        }
110    }
111}