Skip to main content

rocketsplash_formats/
font_atlas.rs

1// <FILE>crates/rocketsplash-formats/src/font_atlas.rs</FILE>
2// <DESC>Font atlas format definitions and validation</DESC>
3// <VERS>VERSION: 1.0.0</VERS>
4// <WCTX>Runtime library implementation</WCTX>
5// <CLOG>Define FontAtlas, GlyphData, and validation helpers</CLOG>
6
7use std::collections::BTreeMap;
8
9use serde::{Deserialize, Serialize};
10
11use crate::{validate_dimensions, RenderMode, StyleFlags};
12
13/// Format version constants for compatibility checking.
14pub const FONT_ATLAS_VERSION: u8 = 1;
15pub const FONT_ATLAS_MIN_SUPPORTED: u8 = 1;
16
17/// Complete font atlas for runtime text rendering.
18#[derive(Clone, Debug, Serialize, Deserialize)]
19pub struct FontAtlas {
20    pub version: u8,
21    pub meta: FontMeta,
22    pub glyphs: BTreeMap<char, GlyphVariants>,
23    pub line_height: u32,
24    pub mode: RenderMode,
25    pub available_styles: StyleFlags,
26}
27
28#[derive(Clone, Debug, Serialize, Deserialize)]
29pub struct FontMeta {
30    pub font_name: String,
31    pub font_size: f32,
32    pub created_at: u64,
33    pub editor_version: String,
34}
35
36/// All style variants for a single character.
37#[derive(Clone, Debug, Serialize, Deserialize)]
38pub struct GlyphVariants {
39    pub base: GlyphData,
40    pub bold: Option<GlyphData>,
41    pub italic: Option<GlyphData>,
42    pub bold_italic: Option<GlyphData>,
43    pub reverse: Option<GlyphData>,
44}
45
46impl GlyphVariants {
47    /// Validate base and all optional variants.
48    pub fn validate(&self) -> Result<(), String> {
49        self.base.validate()?;
50        if let Some(glyph) = &self.bold {
51            glyph.validate()?;
52        }
53        if let Some(glyph) = &self.italic {
54            glyph.validate()?;
55        }
56        if let Some(glyph) = &self.bold_italic {
57            glyph.validate()?;
58        }
59        if let Some(glyph) = &self.reverse {
60            glyph.validate()?;
61        }
62        Ok(())
63    }
64
65    /// Select the best available variant given style flags.
66    pub fn select(&self, bold: bool, italic: bool, reverse: bool) -> &GlyphData {
67        if reverse {
68            if let Some(glyph) = &self.reverse {
69                return glyph;
70            }
71        }
72        if bold && italic {
73            if let Some(glyph) = &self.bold_italic {
74                return glyph;
75            }
76        }
77        if bold {
78            if let Some(glyph) = &self.bold {
79                return glyph;
80            }
81        }
82        if italic {
83            if let Some(glyph) = &self.italic {
84                return glyph;
85            }
86        }
87        &self.base
88    }
89}
90
91/// Serializable glyph data (flat chars + optional opacity).
92#[derive(Clone, Debug, Serialize, Deserialize)]
93pub struct GlyphData {
94    pub chars: String,
95    pub width: u32,
96    pub height: u32,
97    pub opacity: Option<Vec<u8>>,
98}
99
100impl GlyphData {
101    pub fn validate(&self) -> Result<(), String> {
102        let cells = validate_dimensions(self.width, self.height)?;
103        let char_count = self.chars.chars().count();
104        if char_count != cells {
105            return Err(format!(
106                "Glyph char count {} does not match {}x{}",
107                char_count, self.width, self.height
108            ));
109        }
110        if let Some(opacity) = &self.opacity {
111            if opacity.len() != cells {
112                return Err(format!(
113                    "Glyph opacity length {} does not match {}x{}",
114                    opacity.len(),
115                    self.width,
116                    self.height
117                ));
118            }
119        }
120        Ok(())
121    }
122}
123
124// <FILE>crates/rocketsplash-formats/src/font_atlas.rs</FILE>
125// <VERS>END OF VERSION: 1.0.0</VERS>