image_renderer/
font.rs

1//! 字体处理模块
2//!
3//! 封装 font-kit 和 rusttype,提供字体加载和管理功能
4
5use crate::error::{FontError, FontResult};
6use font_kit::family_name::FamilyName;
7use font_kit::properties::{Properties, Style, Weight};
8use font_kit::source::SystemSource;
9use rusttype::{Font as RustTypeFont, Scale};
10use std::path::Path;
11use std::sync::Arc;
12
13/// 字体类型
14#[derive(Debug, Clone)]
15pub struct Font {
16    /// rusttype 字体实例
17    inner: Arc<RustTypeFont<'static>>,
18    /// 字体数据
19    data: Arc<Vec<u8>>,
20}
21
22impl Font {
23    /// 从文件加载字体
24    pub fn from_file<P: AsRef<Path>>(path: P) -> FontResult<Self> {
25        let data = std::fs::read(path.as_ref())
26            .map_err(|e| FontError::LoadError(format!("Failed to read font file: {}", e)))?;
27
28        Self::from_bytes(data)
29    }
30
31    /// 从字节数据加载字体
32    pub fn from_bytes(data: Vec<u8>) -> FontResult<Self> {
33        let font = RustTypeFont::try_from_vec(data.clone())
34            .ok_or_else(|| FontError::LoadError("Failed to parse font data".to_string()))?;
35
36        Ok(Self {
37            inner: Arc::new(font),
38            data: Arc::new(data),
39        })
40    }
41
42    /// 从系统字体加载
43    pub fn from_system(family_name: &str) -> FontResult<Self> {
44        let source = SystemSource::new();
45
46        let handle = source
47            .select_best_match(
48                &[FamilyName::Title(family_name.to_string())],
49                &Properties::new(),
50            )
51            .map_err(|e| FontError::LoadError(format!("Failed to find system font: {}", e)))?;
52
53        let font_data = handle
54            .load()
55            .map_err(|e| FontError::LoadError(format!("Failed to load system font: {}", e)))?
56            .copy_font_data()
57            .ok_or_else(|| FontError::LoadError("Failed to copy font data".to_string()))?;
58
59        Self::from_bytes(font_data.to_vec())
60    }
61
62    /// 从系统字体加载,支持样式和粗细
63    pub fn from_system_with_style(
64        family_name: &str,
65        weight: FontWeight,
66        style: FontStyle,
67    ) -> FontResult<Self> {
68        let source = SystemSource::new();
69
70        let properties = Properties {
71            weight: weight.to_font_kit_weight(),
72            style: style.to_font_kit_style(),
73            ..Properties::default()
74        };
75
76        let handle = source
77            .select_best_match(&[FamilyName::Title(family_name.to_string())], &properties)
78            .map_err(|e| FontError::LoadError(format!("Failed to find system font: {}", e)))?;
79
80        let font_data = handle
81            .load()
82            .map_err(|e| FontError::LoadError(format!("Failed to load system font: {}", e)))?
83            .copy_font_data()
84            .ok_or_else(|| FontError::LoadError("Failed to copy font data".to_string()))?;
85
86        Self::from_bytes(font_data.to_vec())
87    }
88
89    /// 获取默认系统字体
90    pub fn default_system_font() -> FontResult<Self> {
91        #[cfg(target_os = "macos")]
92        let default_font = "PingFang SC";
93        #[cfg(target_os = "windows")]
94        let default_font = "Arial";
95        #[cfg(target_os = "linux")]
96        #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "linux")))]
97        let default_font = "sans-serif";
98
99        Self::from_system(default_font)
100    }
101
102    /// 获取内部 rusttype 字体
103    pub(crate) fn inner(&self) -> &RustTypeFont<'static> {
104        &self.inner
105    }
106
107    /// 获取字体数据
108    pub fn data(&self) -> &[u8] {
109        &self.data
110    }
111}
112
113/// 字体粗细
114#[derive(Debug, Clone, Copy, PartialEq, Eq)]
115pub enum FontWeight {
116    Thin,
117    ExtraLight,
118    Light,
119    Normal,
120    Medium,
121    SemiBold,
122    Bold,
123    ExtraBold,
124    Black,
125}
126
127impl FontWeight {
128    fn to_font_kit_weight(self) -> Weight {
129        match self {
130            FontWeight::Thin => Weight::THIN,
131            FontWeight::ExtraLight => Weight::EXTRA_LIGHT,
132            FontWeight::Light => Weight::LIGHT,
133            FontWeight::Normal => Weight::NORMAL,
134            FontWeight::Medium => Weight::MEDIUM,
135            FontWeight::SemiBold => Weight::SEMIBOLD,
136            FontWeight::Bold => Weight::BOLD,
137            FontWeight::ExtraBold => Weight::EXTRA_BOLD,
138            FontWeight::Black => Weight::BLACK,
139        }
140    }
141}
142
143impl Default for FontWeight {
144    fn default() -> Self {
145        FontWeight::Normal
146    }
147}
148
149/// 字体样式
150#[derive(Debug, Clone, Copy, PartialEq, Eq)]
151pub enum FontStyle {
152    Normal,
153    Italic,
154    Oblique,
155}
156
157impl FontStyle {
158    fn to_font_kit_style(self) -> Style {
159        match self {
160            FontStyle::Normal => Style::Normal,
161            FontStyle::Italic => Style::Italic,
162            FontStyle::Oblique => Style::Oblique,
163        }
164    }
165}
166
167impl Default for FontStyle {
168    fn default() -> Self {
169        FontStyle::Normal
170    }
171}
172
173/// 文本样式配置
174#[derive(Debug, Clone)]
175pub struct TextStyle {
176    /// 字体
177    pub font: Font,
178    /// 字体大小
179    pub size: f32,
180    /// 字体粗细
181    pub weight: FontWeight,
182    /// 字体样式
183    pub style: FontStyle,
184}
185
186impl TextStyle {
187    /// 创建新的文本样式
188    pub fn new(font: Font, size: f32) -> Self {
189        Self {
190            font,
191            size,
192            weight: FontWeight::Normal,
193            style: FontStyle::Normal,
194        }
195    }
196
197    /// 设置字体大小
198    pub fn with_size(mut self, size: f32) -> Self {
199        self.size = size;
200        self
201    }
202
203    /// 设置字体粗细
204    pub fn with_weight(mut self, weight: FontWeight) -> Self {
205        self.weight = weight;
206        self
207    }
208
209    /// 设置字体样式
210    pub fn with_style(mut self, style: FontStyle) -> Self {
211        self.style = style;
212        self
213    }
214
215    /// 获取 rusttype Scale
216    pub(crate) fn scale(&self) -> Scale {
217        Scale::uniform(self.size)
218    }
219
220    /// 测量文本的尺寸
221    ///
222    /// 返回文本的宽度和高度(单位:像素)
223    ///
224    /// # 参数
225    ///
226    /// * `text` - 要测量的文本
227    ///
228    /// # 返回值
229    ///
230    /// 返回 `(width, height)` 元组,表示文本的宽度和高度
231    ///
232    /// # 示例
233    ///
234    /// ```no_run
235    /// use image_renderer::{Font, TextStyle};
236    ///
237    /// let font = Font::default_system_font().unwrap();
238    /// let style = TextStyle::new(font, 24.0);
239    /// let (width, height) = style.measure_text("Hello, World!");
240    /// println!("文本宽度: {}, 高度: {}", width, height);
241    /// ```
242    pub fn measure_text(&self, text: &str) -> (u32, u32) {
243        let scale = self.scale();
244        let font = self.font.inner();
245
246        // 计算文本的总宽度
247        let mut width = 0.0;
248        let mut last_glyph_id = None;
249
250        for c in text.chars() {
251            let glyph = font.glyph(c).scaled(scale);
252
253            // 添加字距调整(kerning)
254            if let Some(last_id) = last_glyph_id {
255                width += font.pair_kerning(scale, last_id, glyph.id());
256            }
257
258            // 添加字形的水平前进距离
259            width += glyph.h_metrics().advance_width;
260            last_glyph_id = Some(glyph.id());
261        }
262
263        // 计算文本的高度
264        // 使用字体的 v_metrics 来获取基线上方和下方的距离
265        let v_metrics = font.v_metrics(scale);
266        let height = (v_metrics.ascent - v_metrics.descent).ceil();
267
268        (width.ceil() as u32, height as u32)
269    }
270}