Skip to main content

graphitepdf_kit/
font.rs

1#[cfg(feature = "fonts-engine")]
2use crate::error::{GraphitePdfKitError, Result};
3use graphitepdf_font::{LoadedFont, StandardFont};
4use std::collections::HashMap;
5
6#[derive(Clone, Debug)]
7pub struct Font {
8    pub name: String,
9    pub data: Vec<u8>,
10    standard_font: Option<StandardFont>,
11}
12
13impl Font {
14    #[cfg(feature = "fonts-engine")]
15    pub fn from_bytes(name: impl Into<String>, data: Vec<u8>) -> Result<Self> {
16        // Verify font data with ttf-parser
17        let _ = ttf_parser::Face::parse(&data, 0)
18            .map_err(|e| GraphitePdfKitError::FontError(format!("Invalid font data: {:?}", e)))?;
19
20        Ok(Self {
21            name: name.into(),
22            data,
23            standard_font: None,
24        })
25    }
26
27    pub fn standard(name: StandardFont) -> Self {
28        Self {
29            name: name.as_str().to_string(),
30            data: Vec::new(),
31            standard_font: Some(name),
32        }
33    }
34
35    pub fn name(&self) -> &str {
36        &self.name
37    }
38
39    pub fn data(&self) -> &[u8] {
40        &self.data
41    }
42
43    pub const fn standard_font(&self) -> Option<StandardFont> {
44        self.standard_font
45    }
46
47    pub fn base_font_name(&self) -> &str {
48        match self.standard_font {
49            Some(font) => font.as_str(),
50            None => self.name.as_str(),
51        }
52    }
53
54    #[cfg(feature = "fonts-engine")]
55    pub fn measure_text_width(&self, text: &str, font_size: f64) -> Result<f64> {
56        if self.data.is_empty() {
57            // For standard fonts, use rough estimate
58            Ok(text.len() as f64 * font_size * 0.6)
59        } else {
60            let face = ttf_parser::Face::parse(&self.data, 0).map_err(|e| {
61                GraphitePdfKitError::FontError(format!("Failed to parse font: {:?}", e))
62            })?;
63
64            let units_per_em = face.units_per_em() as f64;
65            let scale = font_size / units_per_em;
66
67            let mut width = 0.0;
68            for c in text.chars() {
69                if let Some(glyph_id) = face.glyph_index(c) {
70                    if let Some(advance) = face.glyph_hor_advance(glyph_id) {
71                        width += advance as f64 * scale;
72                    }
73                }
74            }
75            Ok(width)
76        }
77    }
78}
79
80impl From<StandardFont> for Font {
81    fn from(value: StandardFont) -> Self {
82        Self::standard(value)
83    }
84}
85
86impl From<&LoadedFont> for Font {
87    fn from(value: &LoadedFont) -> Self {
88        if let Some(font) = value.standard_font() {
89            return Self::standard(font);
90        }
91
92        Self {
93            name: value.descriptor().family().to_string(),
94            data: value.bytes().map(ToOwned::to_owned).unwrap_or_default(),
95            standard_font: None,
96        }
97    }
98}
99
100impl From<LoadedFont> for Font {
101    fn from(value: LoadedFont) -> Self {
102        Self::from(&value)
103    }
104}
105
106#[derive(Clone, Debug, Default)]
107pub struct FontRegistry {
108    pub fonts: HashMap<String, (Font, u64)>,
109    next_id: u64,
110}
111
112impl FontRegistry {
113    pub fn new() -> Self {
114        Self {
115            fonts: HashMap::new(),
116            next_id: 1,
117        }
118    }
119
120    pub fn with_default_font() -> Self {
121        let mut registry = Self::new();
122        registry.register(Font::standard(StandardFont::Helvetica));
123        registry
124    }
125
126    pub fn register(&mut self, font: impl Into<Font>) -> String {
127        let font = font.into();
128        let id = self.next_id;
129        let name = format!("F{}", id);
130        self.next_id += 1;
131        self.fonts.insert(name.clone(), (font, id));
132        name
133    }
134
135    pub fn get(&self, name: &str) -> Option<&Font> {
136        self.fonts.get(name).map(|(font, _)| font)
137    }
138}