est_render/font/
mod.rs

1use std::{
2    collections::HashMap,
3    hash::{Hash, Hasher},
4    io::{Read, Write},
5};
6
7use byteorder_lite::{LittleEndian, ReadBytesExt, WriteBytesExt};
8use flate2::bufread::ZlibDecoder;
9
10use crate::{
11    gpu::{
12        GPU,
13        GPUInner,
14        texture::{Texture, TextureBuilder, TextureError, TextureFormat, TextureUsage},
15    },
16    math::{Point2, Vector2},
17    utils::ArcRef,
18};
19
20
21/// Creates a new [FontManager] instance.
22///
23/// This is useful for loading and managing fonts for text rendering.
24pub fn new() -> FontManager {
25    FontManager::new()
26}
27
28mod system;
29
30#[derive(Clone, Copy, Debug)]
31pub struct FontStyle(u8);
32
33bitflags::bitflags! {
34    impl FontStyle: u8 {
35        /// The font is bold.
36        const BOLD = 0b00000001;
37        /// The font is italic.
38        const ITALIC = 0b00000010;
39    }
40}
41
42#[derive(Clone, Debug)]
43pub struct FontInfo {
44    pub name: String,
45    pub path: std::path::PathBuf,
46    pub style: FontStyle,
47}
48
49#[derive(Clone, Debug)]
50pub struct FontInner {
51    pub info: FontInfo,
52    pub glyphs: HashMap<u32, Glyph>,
53    pub texture_buffer: Vec<u8>,
54    pub texture_width: u32,
55    pub texture_height: u32,
56    pub ascender: f32,
57    pub descender: f32,
58    pub line_height: f32,
59    pub space_width: f32,
60}
61
62#[derive(Clone, Debug)]
63pub struct Font {
64    pub(crate) inner: ArcRef<FontInner>,
65}
66
67const FONT_CACHE_MAGIC: [u8; 5] = *b"eFONT";
68const MAX_ATLAS_SIZE: usize = 2048; // 2048x2048
69
70fn power_of_two(n: usize) -> usize {
71    let mut power = 1;
72    while power < n {
73        power *= 2;
74    }
75    power
76}
77
78pub enum FontBakeFormat {
79    GrayScale,
80    Rgba,
81}
82
83pub enum FontError {
84    InvalidFontData(String),
85    GlyphNotFound(u32),
86    IoError(std::io::Error),
87}
88
89impl Font {
90    pub(crate) fn new(info: FontInfo, size: f32, glyph_range: &[(u32, u32)]) -> Self {
91        let data = std::fs::read(&info.path).expect("Failed to read font file");
92        let font = fontdue::Font::from_bytes(data, fontdue::FontSettings::default())
93            .expect("Failed to parse font file");
94
95        let line_metrics = font.horizontal_line_metrics(size);
96        let pixel_gap = 2usize; // Add a pixel gap to avoid artifacts
97
98        #[cfg(any(debug_assertions, feature = "enable-release-validation"))]
99        if line_metrics.is_none() {
100            panic!(
101                "Failed to get line metrics for font: {}",
102                info.path.display()
103            );
104        }
105
106        let line_metrics = line_metrics.unwrap();
107
108        let ascender = line_metrics.ascent;
109        let descender = line_metrics.descent;
110        let line_height = line_metrics.ascent - line_metrics.descent + line_metrics.line_gap;
111        let space_metrics = font.metrics(' ', size);
112
113        // Calculate texture estimated width based on glyph range
114        // to avoid very WIDE font atlas
115        let tex_width = {
116            let mut total_area = 0;
117            
118            for &(start, end) in glyph_range {
119                for codepoint in start..=end {
120                    let codepoint_char = std::char::from_u32(codepoint).unwrap_or_default();
121                    let metrics = font.metrics(codepoint_char, size);
122
123                    total_area += ((metrics.width + pixel_gap) * (metrics.height + pixel_gap)) as usize;
124                }
125            }
126
127            power_of_two((total_area as f32).sqrt().ceil() as usize) as i32
128        };
129
130        if tex_width > MAX_ATLAS_SIZE as i32 {
131            panic!(
132                "Calculated texture area {} exceeds maximum atlas size {}",
133                tex_width, MAX_ATLAS_SIZE
134            );
135        }
136
137        let rect_config = rect_packer::Config {
138            width: tex_width,
139            height: tex_width,
140            border_padding: 0,
141            rectangle_padding: pixel_gap as i32,
142        };
143
144        let mut packer = rect_packer::Packer::new(rect_config);
145        let mut raw_glyphs = Vec::new();
146        let mut max_size = Point2::new(0, 0);
147
148        for &(start, end) in glyph_range {
149            for codepoint in start..=end {
150                let codepoint_char = std::char::from_u32(codepoint).unwrap_or_default();
151                let (metrics, bitmap) = font.rasterize(codepoint_char, size);
152                if bitmap.is_empty() {
153                    continue;
154                }
155
156                if let Some(rect) = packer.pack(metrics.width as i32, metrics.height as i32, false) {
157                    raw_glyphs.push(
158                        (rect, codepoint, metrics, bitmap)
159                    );
160
161                    max_size.x = max_size.x.max(rect.x + rect.width);
162                    max_size.y = max_size.y.max(rect.y + rect.height);
163                } else {
164                    #[cfg(any(debug_assertions, feature = "enable-release-validation"))]
165                    panic!(
166                        "Failed to pack glyph: {} ({}x{}) with atlas size {}x{}",
167                        codepoint_char,
168                        metrics.width,
169                        metrics.height,
170                        tex_width,
171                        tex_width
172                    );
173                }
174            }
175        }
176
177        let mut texture_buffer = vec![0; (max_size.x * max_size.y) as usize];
178        let mut glyphs = HashMap::new();
179
180        for (rect, codepoint, metrics, bitmap) in raw_glyphs {
181            let advance = metrics.advance_width as f32;
182            let glyph_width = metrics.width as usize;
183            let glyph_height = metrics.height as usize;
184
185            for j in 0..glyph_height {
186                for i in 0..glyph_width {
187                    let src_index = j * glyph_width + i;
188                    let dest_x = rect.x as usize + i;
189                    let dest_y = rect.y as usize + j;
190                    let dest_index = dest_y * max_size.x as usize + dest_x;
191
192                    if dest_index < texture_buffer.len() && src_index < bitmap.len() {
193                        texture_buffer[dest_index] = bitmap[src_index];
194                    }
195                }
196            }
197
198            let start_offset = Vector2::new(rect.x as f32, rect.y as f32);
199            let end_offset = Vector2::new(
200                rect.x + glyph_width as i32,
201                rect.y + glyph_height as i32,
202            );
203
204            let glyph = Glyph {
205                codepoint,
206                advance,
207                atlas_start_offset: start_offset,
208                atlas_end_offset: end_offset,
209
210                width: glyph_width as f32,
211                height: glyph_height as f32,
212                bearing_x: metrics.xmin as f32,
213                bearing_y: metrics.ymin as f32,
214                advance_x: metrics.advance_width as f32,
215                advance_y: metrics.advance_height as f32,
216                ascender: -metrics.bounds.ymin.max(0.0) as f32,
217                descender: (metrics.bounds.ymin + metrics.bounds.height) as f32,
218            };
219
220            glyphs.insert(codepoint, glyph);
221        }
222
223        let inner = FontInner {
224            info,
225            glyphs,
226            texture_buffer,
227            texture_width: max_size.x as u32,
228            texture_height: max_size.y as u32,
229            ascender,
230            descender,
231            line_height,
232            space_width: space_metrics.advance_width as f32,
233        };
234
235        let inner = ArcRef::new(inner);
236        
237        Font {
238            inner,
239        }
240    }
241
242    pub fn line_height(&self) -> f32 {
243        self.inner.borrow().line_height
244    }
245
246    pub fn ascender(&self) -> f32 {
247        self.inner.borrow().ascender
248    }
249
250    pub fn descender(&self) -> f32 {
251        self.inner.borrow().descender
252    }
253
254    pub fn space_width(&self) -> f32 {
255        self.inner.borrow().space_width
256    }
257
258    pub fn texture_size(&self) -> Point2 {
259        let inner = self.inner.borrow();
260        Point2::new(inner.texture_width as i32, inner.texture_height as i32)
261    }
262
263    pub fn calculate_text_size(&self, text: &str) -> Vector2 {
264        let inner = self.inner.borrow();
265
266        let mut width = 0.0f32;
267        let mut height = inner.line_height;
268
269        let mut pen_x = 0.0;
270
271        for c in text.chars() {
272            let codepoint = c as u32;
273            if codepoint == '\n' as u32 {
274                width = width.max(pen_x);
275                pen_x = 0.0;
276                height += inner.line_height;
277                continue;
278            }
279
280            if codepoint == ' ' as u32 {
281                pen_x += inner.space_width;
282                continue;
283            }
284
285            if let Some(glyph) = inner.glyphs.get(&codepoint) {
286                pen_x += glyph.advance_x;
287            }
288        }
289
290        width = width.max(pen_x);
291
292        Vector2::new(width, height)
293    }
294
295    /// Bakes the text into a texture data buffer.
296    ///
297    /// This is useful for rendering static text without needing to render each glyph individually.
298    pub fn create_baked_text_raw(
299        &self,
300        text: &str,
301        format: FontBakeFormat,
302    ) -> Result<(Vec<u8>, u32, u32), String> {
303        let inner = self.inner.borrow();
304
305        let mut pen = Vector2::new(0.0, 0.0);
306
307        // Track bounding box
308        let mut min_x = f32::MAX;
309        let mut min_y = f32::MAX;
310        let mut max_x = f32::MIN;
311        let mut max_y = f32::MIN;
312
313        // let mut max_bearing_y = f32::MIN;
314
315        for c in text.chars() {
316            let codepoint = c as u32;
317            if codepoint == '\n' as u32 {
318                pen.x = 0.0;
319                pen.y += inner.line_height as f32;
320                continue;
321            }
322
323            if codepoint == ' ' as u32 {
324                pen.x += inner.space_width;
325                continue;
326            }
327
328            if let Some(glyph) = inner.glyphs.get(&codepoint) {
329                let x0 = pen.x + glyph.bearing_x;
330                let y0 = pen.y + inner.ascender - (glyph.height + glyph.bearing_y);
331                let x1 = x0 + glyph.width;
332                let y1 = y0 + glyph.height;
333
334                min_x = min_x.min(x0);
335                min_y = min_y.min(y0);
336                max_x = max_x.max(x1);
337                max_y = max_y.max(y1);
338
339                pen.x += glyph.advance_x;
340            }
341        }
342
343        // If no glyphs, return empty buffer
344        if min_x == f32::MAX || min_y == f32::MAX {
345            return Err("No glyphs found".to_string());
346        }
347
348        let width = (max_x - min_x).ceil().max(1.0) as usize;
349        let height = (max_y - min_y).ceil().max(1.0) as usize;
350        let mut buffer = vec![0; width * height];
351
352        let mut pen2 = Vector2::new(0.0, 0.0);
353
354        for c in text.chars() {
355            let codepoint = c as u32;
356            if codepoint == '\n' as u32 {
357                pen2.x = 0.0;
358                pen2.y += inner.line_height as f32;
359                continue;
360            }
361
362            if codepoint == ' ' as u32 {
363                pen2.x += inner.space_width;
364                continue;
365            }
366
367            if let Some(glyph) = inner.glyphs.get(&codepoint) {
368                let x0 = pen2.x + glyph.bearing_x - min_x;
369                let y0 = pen2.y + inner.ascender - (glyph.height + glyph.bearing_y) - min_y;
370
371                let atlas_offset_x = glyph.atlas_start_offset.x as usize;
372                let atlas_offset_y = glyph.atlas_start_offset.y as usize;
373                let atlas_width = inner.texture_width as usize;
374                let atlas_height = inner.texture_height as usize;
375
376                for y in 0..glyph.height as usize {
377                    let src_start = (atlas_offset_y + y) * atlas_width + atlas_offset_x;
378                    let dest_start = (y0 as usize + y) * width + x0 as usize;
379
380                    for x in 0..glyph.width as usize {
381                        let src_index = src_start + x;
382                        let dest_index = dest_start + x;
383
384                        if src_index < atlas_width * atlas_height && dest_index < buffer.len() {
385                            buffer[dest_index] = inner.texture_buffer[src_index];
386                        }
387                    }
388                }
389
390                pen2.x += glyph.advance_x;
391            }
392        }
393
394        match format {
395            FontBakeFormat::GrayScale => Ok((buffer, width as u32, height as u32)),
396            FontBakeFormat::Rgba => {
397                let mut rgba_buffer = Vec::with_capacity(width * height * 4);
398                for byte in buffer.iter() {
399                    let is_transparent = *byte == 0;
400
401                    rgba_buffer.push(*byte);
402                    rgba_buffer.push(*byte);
403                    rgba_buffer.push(*byte);
404                    rgba_buffer.push(if is_transparent { 0 } else { 255 });
405                }
406
407                Ok((rgba_buffer, width as u32, height as u32))
408            }
409        }
410    }
411
412    pub(crate) fn new_cached(path: &str) -> Result<Self, std::io::Error> {
413        let data = std::fs::read(path)?;
414        let mut reader = std::io::Cursor::new(data);
415
416        let mut magic = [0; 5];
417        reader.read_exact(&mut magic)?;
418        if magic != FONT_CACHE_MAGIC {
419            return Err(std::io::Error::new(
420                std::io::ErrorKind::InvalidData,
421                "Invalid font cache file",
422            ));
423        }
424
425        let compressed_size = reader.read_u32::<LittleEndian>()?;
426        let uncompressed_size = reader.read_u32::<LittleEndian>()?;
427
428        let mut compressed_data = vec![0; compressed_size as usize];
429        reader.read_exact(&mut compressed_data)?;
430
431        let mut decoder = ZlibDecoder::new(&compressed_data[..]);
432        let mut decompressed_data = Vec::with_capacity(uncompressed_size as usize);
433        decoder.read_to_end(&mut decompressed_data)?;
434
435        let mut reader = std::io::Cursor::new(decompressed_data);
436
437        let font_family_name_len = reader.read_u32::<LittleEndian>()?;
438        let mut font_family_name = vec![0; font_family_name_len as usize];
439        reader.read_exact(&mut font_family_name)?;
440        let font_family_name = String::from_utf8(font_family_name).map_err(|_| {
441            std::io::Error::new(
442                std::io::ErrorKind::InvalidData,
443                "Invalid UTF-8 in font family name",
444            )
445        })?;
446        let font_style = reader.read_u8()?;
447        let font_style = FontStyle::from_bits(font_style).ok_or_else(|| {
448            std::io::Error::new(std::io::ErrorKind::InvalidData, "Invalid font style")
449        })?;
450
451        let info = FontInfo {
452            name: font_family_name,
453            path: std::path::PathBuf::from(path),
454            style: font_style,
455        };
456
457        let num_glyphs = reader.read_u32::<LittleEndian>()?;
458        let mut glyphs = HashMap::new();
459        for _ in 0..num_glyphs {
460            let codepoint = reader.read_u32::<LittleEndian>()?;
461            let advance = reader.read_f32::<LittleEndian>()?;
462            let atlas_start_offset = Vector2::new(
463                reader.read_f32::<LittleEndian>()?,
464                reader.read_f32::<LittleEndian>()?,
465            );
466            let atlas_end_offset = Vector2::new(
467                reader.read_f32::<LittleEndian>()?,
468                reader.read_f32::<LittleEndian>()?,
469            );
470            let width = reader.read_f32::<LittleEndian>()?;
471            let height = reader.read_f32::<LittleEndian>()?;
472            let bearing_x = reader.read_f32::<LittleEndian>()?;
473            let bearing_y = reader.read_f32::<LittleEndian>()?;
474            let advance_x = reader.read_f32::<LittleEndian>()?;
475            let advance_y = reader.read_f32::<LittleEndian>()?;
476            let ascender = reader.read_f32::<LittleEndian>()?;
477            let descender = reader.read_f32::<LittleEndian>()?;
478
479            let glyph = Glyph {
480                codepoint,
481                advance,
482                atlas_start_offset,
483                atlas_end_offset,
484                width,
485                height,
486                bearing_x,
487                bearing_y,
488                advance_x,
489                advance_y,
490                ascender,
491                descender,
492            };
493
494            glyphs.insert(codepoint, glyph);
495        }
496
497        let texture_buffer_width = reader.read_u32::<LittleEndian>()?;
498        let texture_buffer_height = reader.read_u32::<LittleEndian>()?;
499
500        if texture_buffer_width > MAX_ATLAS_SIZE as u32
501            || texture_buffer_height > MAX_ATLAS_SIZE as u32
502        {
503            return Err(std::io::Error::new(
504                std::io::ErrorKind::InvalidData,
505                "Invalid texture buffer size",
506            ));
507        }
508
509        let mut texture_buffer = vec![0; (texture_buffer_width * texture_buffer_height) as usize];
510        reader.read_exact(&mut texture_buffer)?;
511
512        let ascender = reader.read_f32::<LittleEndian>()?;
513        let descender = reader.read_f32::<LittleEndian>()?;
514        let line_height = reader.read_f32::<LittleEndian>()?;
515        let space_width = reader.read_f32::<LittleEndian>()?;
516
517        let inner = FontInner {
518            info,
519            glyphs,
520            texture_buffer,
521            texture_width: texture_buffer_width,
522            texture_height: texture_buffer_height,
523            ascender,
524            descender,
525            line_height,
526            space_width,
527        };
528
529        let inner = ArcRef::new(inner);
530
531        Ok(Font {
532            inner: ArcRef::clone(&inner),
533        })
534    }
535
536    /// Saves the font cache to a file.
537    ///
538    /// This will create a binary file that can be loaded later using [FontManager::load_font_cached].
539    pub fn save_font_cache(&self, path: &str) -> Result<(), std::io::Error> {
540        let mut writer = std::fs::File::create(path)?;
541        writer.write_all(&FONT_CACHE_MAGIC)?;
542
543        let inner = self.inner.borrow();
544
545        let mut writer2 = std::io::Cursor::new(Vec::<u8>::new());
546
547        writer2.write_u32::<LittleEndian>(inner.info.name.len() as u32)?;
548        writer2.write_all(inner.info.name.as_bytes())?;
549        writer2.write_u8(inner.info.style.bits())?;
550
551        writer2.write_u32::<LittleEndian>(inner.glyphs.len() as u32)?;
552        for (_index, glyph) in inner.glyphs.iter() {
553            writer2.write_u32::<LittleEndian>(glyph.codepoint)?;
554            writer2.write_f32::<LittleEndian>(glyph.advance)?;
555            writer2.write_f32::<LittleEndian>(glyph.atlas_start_offset.x)?;
556            writer2.write_f32::<LittleEndian>(glyph.atlas_start_offset.y)?;
557            writer2.write_f32::<LittleEndian>(glyph.atlas_end_offset.x)?;
558            writer2.write_f32::<LittleEndian>(glyph.atlas_end_offset.y)?;
559            writer2.write_f32::<LittleEndian>(glyph.width)?;
560            writer2.write_f32::<LittleEndian>(glyph.height)?;
561            writer2.write_f32::<LittleEndian>(glyph.bearing_x)?;
562            writer2.write_f32::<LittleEndian>(glyph.bearing_y)?;
563            writer2.write_f32::<LittleEndian>(glyph.advance_x)?;
564            writer2.write_f32::<LittleEndian>(glyph.advance_y)?;
565            writer2.write_f32::<LittleEndian>(glyph.ascender)?;
566            writer2.write_f32::<LittleEndian>(glyph.descender)?;
567        }
568
569        writer2.write_u32::<LittleEndian>(inner.texture_width)?;
570        writer2.write_u32::<LittleEndian>(inner.texture_height)?;
571        writer2.write_all(&inner.texture_buffer)?;
572
573        writer2.write_f32::<LittleEndian>(inner.ascender)?;
574        writer2.write_f32::<LittleEndian>(inner.descender)?;
575        writer2.write_f32::<LittleEndian>(inner.line_height)?;
576        writer2.write_f32::<LittleEndian>(inner.space_width)?;
577
578        let uncompressed_data: Vec<u8> = writer2.into_inner();
579        let uncompressed_size = uncompressed_data.len() as u32;
580
581        let mut compressed_data =
582            flate2::write::ZlibEncoder::new(Vec::new(), flate2::Compression::default());
583        compressed_data.write_all(&uncompressed_data)?;
584
585        let compressed_data = compressed_data.finish()?;
586
587        writer.write_u32::<LittleEndian>(compressed_data.len() as u32)?;
588        writer.write_u32::<LittleEndian>(uncompressed_size as u32)?;
589        writer.write_all(&compressed_data)?;
590
591        Ok(())
592    }
593
594    /// Returns the image data of the font texture atlas.
595    pub fn get_image_data(&self) -> (Vec<u8>, u32, u32) {
596        let inner = self.inner.borrow();
597        (
598            inner.texture_buffer.clone(),
599            inner.texture_width,
600            inner.texture_height,
601        )
602    }
603
604    /// Returns the font's glyph for the given codepoint.
605    pub fn get_glyph(&self, codepoint: u32) -> Result<Glyph, FontError> {
606        let inner = self.inner.borrow();
607
608        inner
609            .glyphs
610            .get(&codepoint)
611            .cloned()
612            .ok_or(FontError::GlyphNotFound(codepoint))
613    }
614
615    /// Create a texture from the baked text.
616    /// 
617    /// This is useful for rendering static text without needing to render each glyph individually.
618    pub fn create_baked_text(
619        &self,
620        gpu: &mut GPU,
621        text: &str,
622    ) -> Result<Texture, TextureError> {
623        let (image_data, width, height) = self.create_baked_text_raw(text, FontBakeFormat::Rgba)
624            .map_err(|_| TextureError::InvalidTextureData)?;
625
626        let format = {
627            let gpu_inner = gpu.inner.borrow();
628
629            if gpu_inner.is_srgb() {
630                TextureFormat::Bgra8UnormSrgb
631            } else {
632                TextureFormat::Bgra8Unorm
633            }
634        };
635
636        let texture = gpu
637            .create_texture()
638            .set_raw_image(&image_data, Point2::new(width as i32, height as i32), format)
639            .set_usage(TextureUsage::Sampler)
640            .build()?;
641
642        Ok(texture)
643    }
644
645    /// Creates a texture from the font's glyph atlas.
646    pub fn create_texture(&self, gpu: &mut GPU) -> Result<Texture, TextureError> {
647        let gpu_inner = &gpu.inner;
648
649        self.create_texture_inner(&gpu_inner)
650    }
651
652    pub(crate) fn create_texture_inner(
653        &self,
654        gpu: &ArcRef<GPUInner>,
655    ) -> Result<Texture, TextureError> {
656        let (image_data, width, height) = self.get_image_data();
657
658        let format = {
659            let gpu_inner = gpu.borrow();
660
661            if gpu_inner.is_srgb() {
662                TextureFormat::Bgra8UnormSrgb
663            } else {
664                TextureFormat::Bgra8Unorm
665            }
666        };
667
668        let image_data = {
669            let mut data = Vec::with_capacity(image_data.len() * 4);
670            for &pixel in &image_data {
671                let is_transparent_pixel = pixel == 0;
672                data.push(pixel);
673                data.push(pixel);
674                data.push(pixel);
675                data.push(if is_transparent_pixel { 0 } else { 255 });
676            }
677
678            data
679        };
680
681        let texture = TextureBuilder::new(ArcRef::clone(gpu))
682            .set_raw_image(
683                &image_data,
684                Point2::new(width as i32, height as i32),
685                format,
686            )
687            .set_usage(TextureUsage::Sampler)
688            .build()?;
689
690        Ok(texture)
691    }
692}
693
694#[derive(Clone, Debug)]
695pub struct Glyph {
696    pub codepoint: u32,
697    pub advance: f32,
698    pub atlas_start_offset: Vector2,
699    pub atlas_end_offset: Vector2,
700
701    // Metrics
702    pub width: f32,
703    pub height: f32,
704    pub bearing_x: f32,
705    pub bearing_y: f32,
706    pub advance_x: f32,
707    pub advance_y: f32,
708    pub ascender: f32,
709    pub descender: f32,
710}
711
712impl Eq for Glyph {}
713
714impl PartialEq for Glyph {
715    fn eq(&self, other: &Self) -> bool {
716        self.codepoint == other.codepoint
717    }
718}
719
720#[derive(Clone, Debug)]
721pub struct FontManager {
722    fonts: Vec<FontInfo>,
723    cached_font: HashMap<u64, Font>,
724}
725
726const DEFAULT_GLYPH_RANGE: [(u32, u32); 1] = [(0x20, 0x7E)]; // ASCII range
727
728impl FontManager {
729    /// Creates a new FontManager instance.
730    /// 
731    /// This will search for system fonts and cache them.
732    /// It will also initialize an empty cache for loaded fonts.
733    pub fn new() -> Self {
734        let fonts = system::search_system_font();
735        FontManager {
736            fonts,
737            cached_font: HashMap::new(),
738        }
739    }
740
741    /// Loads a font by name and size, optionally specifying a glyph range.
742    ///
743    /// If the font is already cached, it will return the cached version.
744    /// If the font is not found, it will return `None`.
745    pub fn load_font(
746        &mut self,
747        font_name: &str,
748        glyph_range: Option<&[(u32, u32)]>,
749        size: f32,
750    ) -> Option<Font> {
751        let glyph_range = glyph_range.unwrap_or(&DEFAULT_GLYPH_RANGE);
752
753        let hashed_name = {
754            let mut hasher = std::collections::hash_map::DefaultHasher::new();
755            font_name.hash(&mut hasher);
756            for (start, end) in glyph_range {
757                start.hash(&mut hasher);
758                end.hash(&mut hasher);
759            }
760            size.to_bits().hash(&mut hasher);
761            hasher.finish()
762        };
763
764        if self.cached_font.contains_key(&hashed_name) {
765            return self.cached_font.get(&hashed_name).cloned();
766        }
767
768        if std::path::Path::new(font_name).exists() {
769            let path = std::path::Path::new(font_name);
770
771            let font_info = system::get_font_info(path);
772            if font_info.is_none() {
773                return None;
774            }
775
776            let font_info = font_info.unwrap();
777            let font = Font::new(font_info, size, glyph_range);
778
779            self.cached_font.insert(hashed_name, font.clone());
780
781            return Some(font);
782        } else {
783            for font in &self.fonts {
784                if font.name == font_name {
785                    let font = Font::new(font.clone(), size, glyph_range);
786                    self.cached_font.insert(hashed_name, font.clone());
787                    return Some(font);
788                }
789            }
790        }
791
792        None
793    }
794
795    /// Loads a font from a cached file.
796    ///
797    /// This will load the font from a binary file created by [Font::save_font_cache].
798    /// If the font is already cached, it will return the cached version.
799    pub fn load_font_cached(&mut self, path: &str) -> Option<Font> {
800        let hash_id = {
801            let mut hasher = std::collections::hash_map::DefaultHasher::new();
802            path.hash(&mut hasher);
803            hasher.finish()
804        };
805
806        if self.cached_font.contains_key(&hash_id) {
807            return self.cached_font.get(&hash_id).cloned();
808        }
809
810        match Font::new_cached(path) {
811            Ok(font) => {
812                self.cached_font.insert(hash_id, font.clone());
813                Some(font)
814            }
815            Err(_) => None,
816        }
817    }
818}