oxidize_pdf/fonts/
loader.rs1use crate::error::PdfError;
4use crate::Result;
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8pub enum FontFormat {
9 TrueType,
11 OpenType,
13}
14
15impl FontFormat {
16 pub fn detect(data: &[u8]) -> Result<Self> {
18 if data.len() < 4 {
19 return Err(PdfError::FontError("Font data too small".into()));
20 }
21
22 match &data[0..4] {
24 [0x00, 0x01, 0x00, 0x00] => Ok(FontFormat::TrueType),
26 [0x4F, 0x54, 0x54, 0x4F] => Ok(FontFormat::OpenType),
28 [0x74, 0x72, 0x75, 0x65] => Ok(FontFormat::TrueType),
30 _ => Err(PdfError::FontError("Unknown font format".into())),
31 }
32 }
33}
34
35#[derive(Debug, Clone)]
37pub struct FontData {
38 pub bytes: Vec<u8>,
40 pub format: FontFormat,
42}
43
44pub struct FontLoader;
46
47impl FontLoader {
48 pub fn load_from_file(path: impl AsRef<std::path::Path>) -> Result<FontData> {
50 let bytes = std::fs::read(path)?;
51 Self::load_from_bytes(bytes)
52 }
53
54 pub fn load_from_bytes(bytes: Vec<u8>) -> Result<FontData> {
56 let format = FontFormat::detect(&bytes)?;
57 Ok(FontData { bytes, format })
58 }
59
60 pub fn validate(data: &FontData) -> Result<()> {
62 if data.bytes.len() < 12 {
63 return Err(PdfError::FontError("Font file too small".into()));
64 }
65
66 match data.format {
68 FontFormat::TrueType => Self::validate_ttf(&data.bytes),
69 FontFormat::OpenType => Self::validate_otf(&data.bytes),
70 }
71 }
72
73 fn validate_ttf(data: &[u8]) -> Result<()> {
74 if data.len() < 12 {
76 return Err(PdfError::FontError("Invalid TTF structure".into()));
77 }
78 Ok(())
79 }
80
81 fn validate_otf(data: &[u8]) -> Result<()> {
82 if data.len() < 12 {
84 return Err(PdfError::FontError("Invalid OTF structure".into()));
85 }
86 Ok(())
87 }
88}
89
90#[cfg(test)]
91mod tests {
92 use super::*;
93
94 #[test]
95 fn test_font_format_detection() {
96 let ttf_header = vec![0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
98 let ttf_data = FontLoader::load_from_bytes(ttf_header).unwrap();
99 assert_eq!(ttf_data.format, FontFormat::TrueType);
100
101 let otf_header = vec![0x4F, 0x54, 0x54, 0x4F, 0x00, 0x00, 0x00, 0x00];
103 let otf_data = FontLoader::load_from_bytes(otf_header).unwrap();
104 assert_eq!(otf_data.format, FontFormat::OpenType);
105 }
106
107 #[test]
108 fn test_invalid_font_data() {
109 let small_data = vec![0x00, 0x01];
111 assert!(FontLoader::load_from_bytes(small_data).is_err());
112
113 let invalid_data = vec![0xFF, 0xFF, 0xFF, 0xFF];
115 assert!(FontLoader::load_from_bytes(invalid_data).is_err());
116 }
117
118 #[test]
119 fn test_font_validation() {
120 let mut ttf_data = vec![0x00, 0x01, 0x00, 0x00];
122 ttf_data.extend_from_slice(&[0x00; 20]); let font_data = FontLoader::load_from_bytes(ttf_data).unwrap();
124 assert!(FontLoader::validate(&font_data).is_ok());
125
126 let small_font = FontData {
128 bytes: vec![0x00; 10],
129 format: FontFormat::TrueType,
130 };
131 assert!(FontLoader::validate(&small_font).is_err());
132 }
133
134 #[test]
135 fn test_ttf_true_tag_detection() {
136 let true_header = vec![0x74, 0x72, 0x75, 0x65, 0x00, 0x00, 0x00, 0x00];
138 let true_data = FontLoader::load_from_bytes(true_header).unwrap();
139 assert_eq!(true_data.format, FontFormat::TrueType);
140 }
141
142 #[test]
143 fn test_font_data_clone() {
144 let font_data = FontData {
145 bytes: vec![1, 2, 3, 4],
146 format: FontFormat::TrueType,
147 };
148
149 let cloned = font_data.clone();
150 assert_eq!(cloned.bytes, font_data.bytes);
151 assert_eq!(cloned.format, font_data.format);
152 }
153
154 #[test]
155 fn test_font_format_equality() {
156 assert_eq!(FontFormat::TrueType, FontFormat::TrueType);
157 assert_eq!(FontFormat::OpenType, FontFormat::OpenType);
158 assert_ne!(FontFormat::TrueType, FontFormat::OpenType);
159 }
160
161 #[test]
162 fn test_otf_validation() {
163 let mut otf_data = vec![0x4F, 0x54, 0x54, 0x4F];
165 otf_data.extend_from_slice(&[0x00; 20]); let font_data = FontLoader::load_from_bytes(otf_data).unwrap();
167 assert!(FontLoader::validate(&font_data).is_ok());
168
169 let small_otf = FontData {
171 bytes: vec![0x4F, 0x54, 0x54, 0x4F],
172 format: FontFormat::OpenType,
173 };
174 assert!(FontLoader::validate(&small_otf).is_err());
175 }
176
177 #[test]
178 fn test_empty_font_data() {
179 let empty_data = vec![];
180 let result = FontLoader::load_from_bytes(empty_data);
181 assert!(result.is_err());
182 if let Err(PdfError::FontError(msg)) = result {
183 assert!(msg.contains("too small"));
184 }
185 }
186
187 #[test]
188 fn test_font_format_detect_edge_cases() {
189 let exact_ttf = vec![0x00, 0x01, 0x00, 0x00];
191 assert_eq!(
192 FontFormat::detect(&exact_ttf).unwrap(),
193 FontFormat::TrueType
194 );
195
196 let too_small = vec![0x00, 0x01, 0x00];
198 assert!(FontFormat::detect(&too_small).is_err());
199 }
200}