dear_imgui/fonts/
atlas.rs

1//! Font atlas management for Dear ImGui v1.92+
2//!
3//! This module provides a modern, type-safe interface to Dear ImGui's dynamic font system.
4//! Key features:
5//! - Dynamic glyph loading (no need to pre-specify glyph ranges)
6//! - Runtime font size adjustment
7//! - Custom font loaders
8//! - Incremental texture updates
9
10use crate::fonts::Font;
11use crate::sys;
12use std::ffi::CString;
13use std::marker::PhantomData;
14use std::ptr;
15use std::rc::Rc;
16
17/// Font atlas that manages multiple fonts and their texture data
18///
19/// The font atlas is responsible for:
20/// - Loading and managing multiple fonts
21/// - Packing font glyphs into texture atlases
22/// - Providing texture data for rendering
23#[derive(Debug)]
24pub struct FontAtlas {
25    raw: *mut sys::ImFontAtlas,
26    owned: bool,
27    _phantom: PhantomData<*mut sys::ImFontAtlas>,
28}
29
30/// A font identifier
31#[derive(Copy, Clone, Debug, Eq, PartialEq)]
32pub struct FontId(pub(crate) *const sys::ImFont);
33
34/// Font loader interface for custom font backends
35///
36/// This provides a safe Rust interface to Dear ImGui's ImFontLoader system,
37/// allowing custom font loading implementations.
38pub struct FontLoader {
39    raw: sys::ImFontLoader,
40    name: CString,
41}
42
43impl FontLoader {
44    /// Creates a new font loader with the given name
45    pub fn new(name: &str) -> Result<Self, std::ffi::NulError> {
46        let name_cstring = CString::new(name)?;
47        let mut raw = sys::ImFontLoader::default();
48        raw.Name = name_cstring.as_ptr();
49
50        Ok(Self {
51            raw,
52            name: name_cstring,
53        })
54    }
55
56    /// Returns a pointer to the raw ImFontLoader
57    pub(crate) fn as_ptr(&self) -> *const sys::ImFontLoader {
58        &self.raw
59    }
60
61    /// Sets the loader initialization callback
62    pub fn with_loader_init<F>(self, _callback: F) -> Self
63    where
64        F: Fn(&mut FontAtlas) -> bool + 'static,
65    {
66        // Note: For now, we'll use the default STB TrueType loader
67        // Custom callbacks would require more complex lifetime management
68        self
69    }
70}
71
72/// Font loader flags for controlling font loading behavior
73#[derive(Debug, Clone, Copy, PartialEq, Eq)]
74pub struct FontLoaderFlags(pub u32);
75
76impl FontLoaderFlags {
77    /// No special flags
78    pub const NONE: Self = Self(0);
79
80    /// Load color glyphs (requires FreeType backend)
81    pub const LOAD_COLOR: Self = Self(1 << 0);
82
83    /// Force auto-hinting
84    pub const FORCE_AUTOHINT: Self = Self(1 << 1);
85
86    /// Disable hinting
87    pub const NO_HINTING: Self = Self(1 << 2);
88
89    /// Disable auto-hinting
90    pub const NO_AUTOHINT: Self = Self(1 << 3);
91}
92
93impl std::ops::BitOr for FontLoaderFlags {
94    type Output = Self;
95    fn bitor(self, rhs: Self) -> Self::Output {
96        Self(self.0 | rhs.0)
97    }
98}
99
100impl std::ops::BitOrAssign for FontLoaderFlags {
101    fn bitor_assign(&mut self, rhs: Self) {
102        self.0 |= rhs.0;
103    }
104}
105
106/// A shared font atlas that can be used across multiple contexts
107///
108/// This allows multiple ImGui contexts to share the same font atlas,
109/// which is useful for applications with multiple windows or contexts.
110#[derive(Debug, Clone)]
111pub struct SharedFontAtlas(pub(crate) Rc<*mut sys::ImFontAtlas>);
112
113impl SharedFontAtlas {
114    /// Creates a new shared font atlas
115    pub fn create() -> SharedFontAtlas {
116        unsafe {
117            let raw_atlas = sys::ImFontAtlas_ImFontAtlas();
118            SharedFontAtlas(Rc::new(raw_atlas))
119        }
120    }
121
122    /// Returns a raw pointer to the underlying ImFontAtlas
123    pub(crate) fn as_ptr(&self) -> *const sys::ImFontAtlas {
124        *self.0
125    }
126
127    /// Returns a mutable raw pointer to the underlying ImFontAtlas
128    pub(crate) fn as_ptr_mut(&mut self) -> *mut sys::ImFontAtlas {
129        *self.0
130    }
131}
132
133impl Drop for SharedFontAtlas {
134    fn drop(&mut self) {
135        // Only drop if this is the last reference
136        if Rc::strong_count(&self.0) == 1 {
137            unsafe {
138                let atlas_ptr = *self.0;
139                if !atlas_ptr.is_null() {
140                    sys::ImFontAtlas_destroy(atlas_ptr);
141                }
142            }
143        }
144    }
145}
146
147impl FontAtlas {
148    /// Creates a new font atlas with default settings
149    pub fn new() -> Self {
150        unsafe {
151            let raw = sys::ImFontAtlas_ImFontAtlas();
152            Self {
153                raw,
154                owned: true,
155                _phantom: PhantomData,
156            }
157        }
158    }
159
160    /// Creates a new font atlas with a custom font loader
161    pub fn with_font_loader(loader: &FontLoader) -> Self {
162        let mut atlas = Self::new();
163        atlas.set_font_loader(loader);
164        atlas
165    }
166
167    /// Creates a FontAtlas wrapper from a raw ImFontAtlas pointer
168    ///
169    /// # Safety
170    /// The caller must ensure that the pointer is valid and points to a valid ImFontAtlas
171    pub(crate) unsafe fn from_raw(raw: *mut sys::ImFontAtlas) -> Self {
172        Self {
173            raw,
174            owned: false,
175            _phantom: PhantomData,
176        }
177    }
178
179    /// Returns the raw ImFontAtlas pointer
180    pub fn raw(&self) -> *mut sys::ImFontAtlas {
181        self.raw
182    }
183
184    /// Sets the font loader for this atlas
185    ///
186    /// This allows using custom font backends like FreeType with additional features.
187    /// Must be called before adding any fonts.
188    pub fn set_font_loader(&mut self, loader: &FontLoader) {
189        unsafe {
190            sys::ImFontAtlas_SetFontLoader(self.raw, loader.as_ptr());
191        }
192    }
193
194    /// Sets global font loader flags
195    ///
196    /// These flags apply to all fonts loaded with this atlas unless overridden
197    /// in individual FontConfig instances.
198    pub fn set_font_loader_flags(&mut self, flags: FontLoaderFlags) {
199        unsafe {
200            (*self.raw).FontLoaderFlags = flags.0;
201        }
202    }
203
204    /// Gets the current font loader flags
205    pub fn font_loader_flags(&self) -> FontLoaderFlags {
206        unsafe { FontLoaderFlags((*self.raw).FontLoaderFlags) }
207    }
208
209    /// Add a font to the atlas using FontSource
210    #[doc(alias = "AddFont")]
211    pub fn add_font(&mut self, font_sources: &[FontSource<'_>]) -> crate::fonts::FontId {
212        let (head, tail) = font_sources.split_first().unwrap();
213        let font_id = self.add_font_internal(head, false);
214        for font in tail {
215            self.add_font_internal(font, true);
216        }
217        font_id
218    }
219
220    fn add_font_internal(
221        &mut self,
222        font_source: &FontSource<'_>,
223        _merge_mode: bool,
224    ) -> crate::fonts::FontId {
225        match font_source {
226            FontSource::DefaultFontData {
227                size_pixels,
228                config,
229            } => {
230                // For v1.92+, we can use dynamic sizing by passing 0.0
231                let size = size_pixels.unwrap_or(0.0);
232                let mut cfg = config.clone().unwrap_or_default();
233                if size > 0.0 {
234                    cfg = cfg.size_pixels(size);
235                }
236                let font = self.add_font_default(Some(&cfg));
237                font.id()
238            }
239            FontSource::TtfData {
240                data,
241                size_pixels,
242                config,
243            } => {
244                let size = size_pixels.unwrap_or(0.0);
245                let mut cfg = config.clone().unwrap_or_default();
246                if size > 0.0 {
247                    cfg = cfg.size_pixels(size);
248                }
249                let font = self
250                    .add_font_from_memory_ttf(data, size, Some(&cfg), None)
251                    .expect("Failed to add TTF font from memory");
252                font.id()
253            }
254            FontSource::TtfFile {
255                path,
256                size_pixels,
257                config,
258            } => {
259                let size = size_pixels.unwrap_or(0.0);
260                let mut cfg = config.clone().unwrap_or_default();
261                if size > 0.0 {
262                    cfg = cfg.size_pixels(size);
263                }
264                let font = self
265                    .add_font_from_file_ttf(path, size, Some(&cfg), None)
266                    .expect("Failed to add TTF font from file");
267                font.id()
268            }
269        }
270    }
271
272    /// Add a font to the atlas using FontConfig
273    #[doc(alias = "AddFont")]
274    pub fn add_font_with_config(&mut self, font_cfg: &FontConfig) -> &mut Font {
275        unsafe {
276            let font_ptr = sys::ImFontAtlas_AddFont(self.raw, font_cfg.raw());
277            Font::from_raw_mut(font_ptr)
278        }
279    }
280
281    /// Add the default font to the atlas
282    #[doc(alias = "AddFontDefault")]
283    pub fn add_font_default(&mut self, font_cfg: Option<&FontConfig>) -> &mut Font {
284        unsafe {
285            let cfg_ptr = font_cfg.map_or(ptr::null(), |cfg| cfg.raw());
286            let font_ptr = sys::ImFontAtlas_AddFontDefault(self.raw, cfg_ptr);
287            Font::from_raw_mut(font_ptr)
288        }
289    }
290
291    /// Add a font from a TTF file
292    #[doc(alias = "AddFontFromFileTTF")]
293    pub fn add_font_from_file_ttf(
294        &mut self,
295        filename: &str,
296        size_pixels: f32,
297        font_cfg: Option<&FontConfig>,
298        glyph_ranges: Option<&[sys::ImWchar]>,
299    ) -> Option<&mut Font> {
300        unsafe {
301            let filename_cstr = std::ffi::CString::new(filename).ok()?;
302            let cfg_ptr = font_cfg.map_or(ptr::null(), |cfg| cfg.raw());
303            let ranges_ptr = glyph_ranges.map_or(ptr::null(), |ranges| ranges.as_ptr());
304
305            let font_ptr = sys::ImFontAtlas_AddFontFromFileTTF(
306                self.raw,
307                filename_cstr.as_ptr(),
308                size_pixels,
309                cfg_ptr,
310                ranges_ptr,
311            );
312
313            if font_ptr.is_null() {
314                None
315            } else {
316                Some(Font::from_raw_mut(font_ptr))
317            }
318        }
319    }
320
321    /// Add a font from memory (TTF data)
322    #[doc(alias = "AddFontFromMemoryTTF")]
323    pub fn add_font_from_memory_ttf(
324        &mut self,
325        font_data: &[u8],
326        size_pixels: f32,
327        font_cfg: Option<&FontConfig>,
328        glyph_ranges: Option<&[sys::ImWchar]>,
329    ) -> Option<&mut Font> {
330        unsafe {
331            let cfg_ptr = font_cfg.map_or(ptr::null(), |cfg| cfg.raw());
332            let ranges_ptr = glyph_ranges.map_or(ptr::null(), |ranges| ranges.as_ptr());
333
334            let font_ptr = sys::ImFontAtlas_AddFontFromMemoryTTF(
335                self.raw,
336                font_data.as_ptr() as *mut std::os::raw::c_void,
337                font_data.len() as i32,
338                size_pixels,
339                cfg_ptr,
340                ranges_ptr,
341            );
342
343            if font_ptr.is_null() {
344                None
345            } else {
346                Some(Font::from_raw_mut(font_ptr))
347            }
348        }
349    }
350
351    /// Remove a font from the atlas
352    #[doc(alias = "RemoveFont")]
353    pub fn remove_font(&mut self, font: &mut Font) {
354        unsafe { sys::ImFontAtlas_RemoveFont(self.raw, font.raw()) }
355    }
356
357    /// Clear all fonts and texture data
358    #[doc(alias = "Clear")]
359    pub fn clear(&mut self) {
360        unsafe { sys::ImFontAtlas_Clear(self.raw) }
361    }
362
363    /// Clear only the fonts (keep texture data)
364    #[doc(alias = "ClearFonts")]
365    pub fn clear_fonts(&mut self) {
366        unsafe { sys::ImFontAtlas_ClearFonts(self.raw) }
367    }
368
369    /// Clear only the texture data (keep fonts)
370    #[doc(alias = "ClearTexData")]
371    pub fn clear_tex_data(&mut self) {
372        unsafe { sys::ImFontAtlas_ClearTexData(self.raw) }
373    }
374
375    /// Get default glyph ranges (Basic Latin + Latin Supplement)
376    #[doc(alias = "GetGlyphRangesDefault")]
377    pub fn get_glyph_ranges_default(&self) -> &[sys::ImWchar] {
378        unsafe {
379            let ptr = sys::ImFontAtlas_GetGlyphRangesDefault(self.raw);
380            if ptr.is_null() {
381                &[]
382            } else {
383                // Count the ranges (terminated by 0)
384                let mut len = 0;
385                while *ptr.add(len) != 0 {
386                    len += 1;
387                }
388                std::slice::from_raw_parts(ptr, len)
389            }
390        }
391    }
392
393    /// Build the font atlas texture
394    ///
395    /// This is a simplified build process. For more control, use the individual build functions.
396    #[doc(alias = "Build")]
397    pub fn build(&mut self) -> bool {
398        if self.raw.is_null() {
399            return false;
400        }
401        unsafe {
402            // Initialize the build process
403            sys::igImFontAtlasBuildInit(self.raw);
404
405            // Perform the main build
406            sys::igImFontAtlasBuildMain(self.raw);
407
408            // Update pointers
409            sys::igImFontAtlasBuildUpdatePointers(self.raw);
410
411            // Check if build was successful
412            (*self.raw).TexIsBuilt
413        }
414    }
415
416    /// Check if the texture is built
417    pub fn is_built(&self) -> bool {
418        if self.raw.is_null() {
419            return false;
420        }
421        unsafe { (*self.raw).TexIsBuilt }
422    }
423
424    /// Get texture data information
425    ///
426    /// Returns (min_width, min_height) if texture is built
427    /// Note: Our Dear ImGui version uses a different texture management system
428    pub fn get_tex_data_info(&self) -> Option<(u32, u32)> {
429        if self.raw.is_null() {
430            return None;
431        }
432        unsafe {
433            if (*self.raw).TexIsBuilt {
434                let min_width = (*self.raw).TexMinWidth as u32;
435                let min_height = (*self.raw).TexMinHeight as u32;
436                Some((min_width, min_height))
437            } else {
438                None
439            }
440        }
441    }
442
443    /// Get raw texture data pointer and dimensions
444    ///
445    /// # Safety
446    /// The returned pointer is only valid while the FontAtlas exists and the texture is built.
447    /// The caller must ensure proper lifetime management.
448    pub unsafe fn get_tex_data_ptr(&self) -> Option<(*const u8, u32, u32)> {
449        if self.raw.is_null() {
450            return None;
451        }
452        unsafe {
453            if (*self.raw).TexIsBuilt {
454                let tex_data = (*self.raw).TexData;
455                if !tex_data.is_null() {
456                    let width = (*tex_data).Width as u32;
457                    let height = (*tex_data).Height as u32;
458                    let pixels = (*tex_data).Pixels;
459                    if !pixels.is_null() {
460                        Some((pixels, width, height))
461                    } else {
462                        None
463                    }
464                } else {
465                    None
466                }
467            } else {
468                None
469            }
470        }
471    }
472
473    /// Get texture reference for the font atlas
474    ///
475    /// Note: Our Dear ImGui version uses ImTextureRef instead of a simple texture ID
476    pub fn get_tex_ref(&self) -> sys::ImTextureRef {
477        unsafe { (*self.raw).TexRef }
478    }
479
480    /// Set texture reference for the font atlas
481    pub fn set_tex_ref(&mut self, tex_ref: sys::ImTextureRef) {
482        unsafe {
483            (*self.raw).TexRef = tex_ref;
484        }
485    }
486
487    /// Get a mutable view of the atlas texture data, if available
488    pub fn tex_data_mut(&mut self) -> Option<&mut crate::texture::TextureData> {
489        let ptr = unsafe { (*self.raw).TexData };
490        if ptr.is_null() {
491            None
492        } else {
493            Some(unsafe { crate::texture::TextureData::from_raw(ptr) })
494        }
495    }
496
497    /// Convenience: set atlas texture id and mark status OK
498    /// Also updates TexRef so draw commands use this texture id.
499    pub fn set_texture_id(&mut self, tex_id: crate::texture::TextureId) {
500        // Update TexRef used by draw commands
501        let tex_ref = sys::ImTextureRef {
502            _TexData: std::ptr::null_mut(),
503            _TexID: tex_id.id() as sys::ImTextureID,
504        };
505        self.set_tex_ref(tex_ref);
506
507        // Update ImTextureData (if present)
508        if let Some(td) = self.tex_data_mut() {
509            td.set_tex_id(tex_id);
510            td.set_status(crate::texture::TextureStatus::OK);
511        }
512    }
513
514    /// Get texture data pointer
515    ///
516    /// Returns the current texture data used by the atlas
517    pub fn get_tex_data(&self) -> *mut sys::ImTextureData {
518        unsafe { (*self.raw).TexData }
519    }
520
521    /// Get texture UV scale
522    pub fn get_tex_uv_scale(&self) -> [f32; 2] {
523        unsafe {
524            let scale = (*self.raw).TexUvScale;
525            [scale.x, scale.y]
526        }
527    }
528
529    /// Get texture UV white pixel coordinates
530    pub fn get_tex_uv_white_pixel(&self) -> [f32; 2] {
531        unsafe {
532            let pixel = (*self.raw).TexUvWhitePixel;
533            [pixel.x, pixel.y]
534        }
535    }
536}
537
538impl Default for FontAtlas {
539    fn default() -> Self {
540        Self::new()
541    }
542}
543
544impl Drop for FontAtlas {
545    fn drop(&mut self) {
546        if self.owned && !self.raw.is_null() {
547            unsafe {
548                sys::ImFontAtlas_destroy(self.raw);
549            }
550        }
551    }
552}
553
554// NOTE: Do not mark FontAtlas as Send/Sync. It wraps pointers owned by the
555// ImGui context and is not thread-safe to move/share across threads.
556
557/// Font configuration for loading fonts with v1.92+ features
558#[derive(Debug, Clone)]
559pub struct FontConfig {
560    raw: sys::ImFontConfig,
561}
562
563impl FontConfig {
564    /// Creates a new font configuration with default settings
565    pub fn new() -> Self {
566        Self {
567            raw: Default::default(),
568        }
569    }
570
571    /// Returns the raw ImFontConfig pointer
572    pub(crate) fn raw(&self) -> *const sys::ImFontConfig {
573        &self.raw
574    }
575
576    /// Set the font size in pixels
577    ///
578    /// Note: With v1.92+ dynamic fonts, size can be 0.0 to use default sizing
579    pub fn size_pixels(mut self, size: f32) -> Self {
580        self.raw.SizePixels = size;
581        self
582    }
583
584    /// Set whether to merge this font with the previous one
585    pub fn merge_mode(mut self, merge: bool) -> Self {
586        self.raw.MergeMode = merge;
587        self
588    }
589
590    /// Set font loader flags for this specific font
591    ///
592    /// These flags override the global atlas flags for this font.
593    pub fn font_loader_flags(mut self, flags: FontLoaderFlags) -> Self {
594        self.raw.FontLoaderFlags = flags.0;
595        self
596    }
597
598    /// Set glyph ranges to exclude from this font
599    ///
600    /// Useful when merging fonts to avoid overlapping glyphs.
601    pub fn glyph_exclude_ranges(mut self, ranges: &[u32]) -> Self {
602        self.raw.GlyphExcludeRanges = ranges.as_ptr() as *const sys::ImWchar;
603        self
604    }
605
606    /// Set a custom font loader for this font
607    pub fn font_loader(mut self, loader: &FontLoader) -> Self {
608        self.raw.FontLoader = loader.as_ptr();
609        self
610    }
611
612    /// Set the font name for debugging
613    pub fn name(mut self, name: &str) -> Self {
614        let name_bytes = name.as_bytes();
615        let copy_len = std::cmp::min(name_bytes.len(), self.raw.Name.len() - 1);
616
617        // Clear the array first
618        for i in 0..self.raw.Name.len() {
619            self.raw.Name[i] = 0;
620        }
621
622        // Copy the name
623        for (i, &byte) in name_bytes.iter().take(copy_len).enumerate() {
624            self.raw.Name[i] = byte as i8;
625        }
626
627        self
628    }
629
630    /// Set glyph offset for this font
631    pub fn glyph_offset(mut self, offset: [f32; 2]) -> Self {
632        self.raw.GlyphOffset.x = offset[0];
633        self.raw.GlyphOffset.y = offset[1];
634        self
635    }
636
637    /// Set minimum advance X for glyphs
638    pub fn glyph_min_advance_x(mut self, advance: f32) -> Self {
639        self.raw.GlyphMinAdvanceX = advance;
640        self
641    }
642
643    /// Set maximum advance X for glyphs
644    pub fn glyph_max_advance_x(mut self, advance: f32) -> Self {
645        self.raw.GlyphMaxAdvanceX = advance;
646        self
647    }
648
649    /// Set extra advance X for glyphs (spacing between characters)
650    pub fn glyph_extra_advance_x(mut self, advance: f32) -> Self {
651        self.raw.GlyphExtraAdvanceX = advance;
652        self
653    }
654
655    /// Set rasterizer multiply factor
656    pub fn rasterizer_multiply(mut self, multiply: f32) -> Self {
657        self.raw.RasterizerMultiply = multiply;
658        self
659    }
660
661    /// Set rasterizer density for DPI scaling
662    pub fn rasterizer_density(mut self, density: f32) -> Self {
663        self.raw.RasterizerDensity = density;
664        self
665    }
666
667    /// Set pixel snap horizontally
668    pub fn pixel_snap_h(mut self, snap: bool) -> Self {
669        self.raw.PixelSnapH = snap;
670        self
671    }
672
673    /// Set pixel snap vertically
674    pub fn pixel_snap_v(mut self, snap: bool) -> Self {
675        self.raw.PixelSnapV = snap;
676        self
677    }
678
679    /// Set horizontal oversampling
680    pub fn oversample_h(mut self, oversample: i8) -> Self {
681        self.raw.OversampleH = oversample;
682        self
683    }
684
685    /// Set vertical oversampling
686    pub fn oversample_v(mut self, oversample: i8) -> Self {
687        self.raw.OversampleV = oversample;
688        self
689    }
690}
691
692impl Default for FontConfig {
693    fn default() -> Self {
694        Self::new()
695    }
696}
697
698/// A source for font data with v1.92+ dynamic font support
699#[derive(Clone, Debug)]
700pub enum FontSource<'a> {
701    /// Default font included with the library (ProggyClean.ttf)
702    ///
703    /// With v1.92+, size_pixels can be 0.0 for dynamic sizing
704    DefaultFontData {
705        size_pixels: Option<f32>,
706        config: Option<FontConfig>,
707    },
708
709    /// Binary TTF/OTF font data
710    ///
711    /// With v1.92+, size_pixels can be 0.0 for dynamic sizing
712    TtfData {
713        data: &'a [u8],
714        size_pixels: Option<f32>,
715        config: Option<FontConfig>,
716    },
717
718    /// Font from file path
719    ///
720    /// With v1.92+, size_pixels can be 0.0 for dynamic sizing
721    TtfFile {
722        path: &'a str,
723        size_pixels: Option<f32>,
724        config: Option<FontConfig>,
725    },
726}
727
728impl<'a> FontSource<'a> {
729    /// Creates a default font source with dynamic sizing
730    pub fn default_font() -> Self {
731        Self::DefaultFontData {
732            size_pixels: None,
733            config: None,
734        }
735    }
736
737    /// Creates a default font source with specific size
738    pub fn default_font_with_size(size: f32) -> Self {
739        Self::DefaultFontData {
740            size_pixels: Some(size),
741            config: None,
742        }
743    }
744
745    /// Creates a TTF data source with dynamic sizing
746    pub fn ttf_data(data: &'a [u8]) -> Self {
747        Self::TtfData {
748            data,
749            size_pixels: None,
750            config: None,
751        }
752    }
753
754    /// Creates a TTF data source with specific size
755    pub fn ttf_data_with_size(data: &'a [u8], size: f32) -> Self {
756        Self::TtfData {
757            data,
758            size_pixels: Some(size),
759            config: None,
760        }
761    }
762
763    /// Creates a TTF file source with dynamic sizing
764    pub fn ttf_file(path: &'a str) -> Self {
765        Self::TtfFile {
766            path,
767            size_pixels: None,
768            config: None,
769        }
770    }
771
772    /// Creates a TTF file source with specific size
773    pub fn ttf_file_with_size(path: &'a str, size: f32) -> Self {
774        Self::TtfFile {
775            path,
776            size_pixels: Some(size),
777            config: None,
778        }
779    }
780
781    /// Sets the font configuration for this source
782    pub fn with_config(mut self, config: FontConfig) -> Self {
783        match &mut self {
784            Self::DefaultFontData { config: cfg, .. } => *cfg = Some(config),
785            Self::TtfData { config: cfg, .. } => *cfg = Some(config),
786            Self::TtfFile { config: cfg, .. } => *cfg = Some(config),
787        }
788        self
789    }
790}
791
792/// Handle to a font atlas texture
793#[derive(Clone, Debug)]
794pub struct FontAtlasTexture<'a> {
795    /// Texture width (in pixels)
796    pub width: u32,
797    /// Texture height (in pixels)
798    pub height: u32,
799    /// Raw texture data (in bytes).
800    ///
801    /// The format depends on which function was called to obtain this data:
802    /// - For RGBA32: 4 bytes per pixel (R, G, B, A)
803    /// - For Alpha8: 1 byte per pixel (Alpha only)
804    pub data: &'a [u8],
805}