1use crate::fontface::FontFace;
4use crate::fontreader;
5#[cfg(not(target_arch = "wasm32"))]
6use std::io::Read;
7#[cfg(not(target_arch = "wasm32"))]
8use std::io::Write;
9use std::io::{Error, ErrorKind};
10#[cfg(not(target_arch = "wasm32"))]
11use std::net::TcpStream;
12use std::path::Path;
13
14pub enum FontSource<'a> {
16 File(&'a Path),
18 Buffer(&'a [u8]),
20}
21
22#[derive(Debug, Clone)]
24pub struct FontFile {
25 font: fontreader::Font,
26}
27
28impl FontFile {
29 pub fn from_file(path: impl AsRef<Path>) -> Result<Self, Error> {
31 let font = fontreader::Font::get_font_from_file(&path.as_ref().to_path_buf())?;
32 Ok(Self { font })
33 }
34
35 pub fn from_buffer(buffer: &[u8]) -> Result<Self, Error> {
37 let font = fontreader::Font::get_font_from_buffer(buffer)?;
38 Ok(Self { font })
39 }
40
41 pub fn from_source(source: FontSource<'_>) -> Result<Self, Error> {
43 match source {
44 FontSource::File(path) => Self::from_file(path),
45 FontSource::Buffer(buffer) => Self::from_buffer(buffer),
46 }
47 }
48
49 pub fn from_net(url: &str) -> Result<Self, Error> {
53 #[cfg(target_arch = "wasm32")]
54 {
55 let _ = url;
56 return Err(Error::new(
57 ErrorKind::Unsupported,
58 "network font loading is not supported on wasm32",
59 ));
60 }
61
62 #[cfg(not(target_arch = "wasm32"))]
63 {
64 let bytes = fetch_http_font(url)?;
65 Self::from_buffer(&bytes)
66 }
67 }
68
69 pub fn face_count(&self) -> usize {
71 self.font.get_font_count()
72 }
73
74 pub fn face(&self, index: usize) -> Result<FontFace, Error> {
76 let mut font = self.font.clone();
77 font.set_font(index)
78 .map_err(|message| Error::new(ErrorKind::InvalidInput, message))?;
79 Ok(FontFace::from_font(font))
80 }
81
82 pub fn current_face(&self) -> Result<FontFace, Error> {
84 self.face(self.font.get_font_number())
85 }
86
87 pub fn faces(&self) -> Result<Vec<FontFace>, Error> {
89 let mut faces = Vec::with_capacity(self.face_count());
90 for index in 0..self.face_count() {
91 faces.push(self.face(index)?);
92 }
93 Ok(faces)
94 }
95
96 pub fn dump(&self) -> String {
98 format!(
99 "FontFile\nface_count: {}\ncurrent_face: {}\nformat: {}",
100 self.face_count(),
101 self.font.get_font_number(),
102 self.font.font_type.to_string()
103 )
104 }
105
106 #[cfg(feature = "raw")]
107 pub fn raw_font(&self) -> &crate::fontreader::Font {
109 &self.font
110 }
111}
112
113pub fn open_font_from_file(path: impl AsRef<Path>) -> Result<FontFile, Error> {
115 FontFile::from_file(path)
116}
117
118pub fn open_font_from_buffer(buffer: &[u8]) -> Result<FontFile, Error> {
120 FontFile::from_buffer(buffer)
121}
122
123pub fn open_font_from_net(url: &str) -> Result<FontFile, Error> {
125 FontFile::from_net(url)
126}
127
128pub fn open_font(source: FontSource<'_>) -> Result<FontFile, Error> {
130 FontFile::from_source(source)
131}
132
133pub fn load_font_from_file(path: impl AsRef<Path>) -> Result<FontFace, Error> {
135 FontFile::from_file(path)?.current_face()
136}
137
138pub fn load_font_from_buffer(buffer: &[u8]) -> Result<FontFace, Error> {
140 FontFile::from_buffer(buffer)?.current_face()
141}
142
143pub fn load_font_from_net(url: &str) -> Result<FontFace, Error> {
145 FontFile::from_net(url)?.current_face()
146}
147
148pub fn load_font(source: FontSource<'_>) -> Result<FontFace, Error> {
150 FontFile::from_source(source)?.current_face()
151}
152
153#[cfg(feature = "raw")]
154#[deprecated(note = "use `load_font_from_file()` instead")]
155pub fn fontload_file(path: impl AsRef<Path>) -> Result<FontFace, Error> {
156 load_font_from_file(path)
157}
158
159#[cfg(feature = "raw")]
160#[deprecated(note = "use `load_font_from_buffer()` instead")]
161pub fn fontload_buffer(buffer: &[u8]) -> Result<FontFace, Error> {
162 load_font_from_buffer(buffer)
163}
164
165#[cfg(feature = "raw")]
166#[deprecated(note = "use `load_font_from_net()` instead")]
167pub fn fontload_net(url: &str) -> Result<FontFace, Error> {
168 load_font_from_net(url)
169}
170
171#[cfg(feature = "raw")]
172#[deprecated(note = "use `load_font()` instead")]
173pub fn fontload(source: FontSource<'_>) -> Result<FontFace, Error> {
174 load_font(source)
175}
176
177pub struct ChunkedFontBuffer {
179 total_size: usize,
180 data: Vec<u8>,
181 filled: Vec<bool>,
182 filled_len: usize,
183}
184
185impl ChunkedFontBuffer {
186 pub fn new(total_size: usize) -> Result<Self, Error> {
188 if total_size == 0 {
189 return Err(Error::new(
190 ErrorKind::InvalidInput,
191 "chunked font buffer size must be greater than zero",
192 ));
193 }
194
195 Ok(Self {
196 total_size,
197 data: vec![0; total_size],
198 filled: vec![false; total_size],
199 filled_len: 0,
200 })
201 }
202
203 pub fn total_size(&self) -> usize {
205 self.total_size
206 }
207
208 pub fn filled_len(&self) -> usize {
210 self.filled_len
211 }
212
213 pub fn is_complete(&self) -> bool {
215 self.filled_len == self.total_size
216 }
217
218 pub fn append(&mut self, offset: usize, bytes: &[u8]) -> Result<(), Error> {
220 if bytes.is_empty() {
221 return Ok(());
222 }
223
224 let end = offset
225 .checked_add(bytes.len())
226 .ok_or_else(|| Error::new(ErrorKind::InvalidInput, "chunk offset overflow"))?;
227 if end > self.total_size {
228 return Err(Error::new(
229 ErrorKind::InvalidInput,
230 "chunk is out of range for the target font buffer",
231 ));
232 }
233
234 for (index, byte) in bytes.iter().copied().enumerate() {
235 let position = offset + index;
236 if self.filled[position] {
237 if self.data[position] != byte {
238 return Err(Error::new(
239 ErrorKind::InvalidData,
240 "conflicting chunk data for the same byte range",
241 ));
242 }
243 continue;
244 }
245
246 self.data[position] = byte;
247 self.filled[position] = true;
248 self.filled_len += 1;
249 }
250
251 Ok(())
252 }
253
254 pub fn missing_ranges(&self) -> Vec<(usize, usize)> {
256 let mut ranges = Vec::new();
257 let mut start = None;
258
259 for (index, filled) in self.filled.iter().copied().enumerate() {
260 match (start, filled) {
261 (None, false) => start = Some(index),
262 (Some(range_start), true) => {
263 ranges.push((range_start, index));
264 start = None;
265 }
266 _ => {}
267 }
268 }
269
270 if let Some(range_start) = start {
271 ranges.push((range_start, self.total_size));
272 }
273
274 ranges
275 }
276
277 pub fn to_vec(&self) -> Result<Vec<u8>, Error> {
279 if !self.is_complete() {
280 return Err(Error::new(
281 ErrorKind::WouldBlock,
282 "font buffer is incomplete; append all chunks before decoding",
283 ));
284 }
285
286 Ok(self.data.clone())
287 }
288
289 pub fn load_font_file(&self) -> Result<FontFile, Error> {
291 let bytes = self.to_vec()?;
292 open_font_from_buffer(&bytes)
293 }
294
295 pub fn load_font_face(&self) -> Result<FontFace, Error> {
297 self.load_font_file()?.current_face()
298 }
299
300 #[cfg(feature = "raw")]
301 pub fn load_font(&self) -> Result<FontFace, Error> {
302 self.load_font_face()
303 }
304
305 pub fn into_font_file(self) -> Result<FontFile, Error> {
307 if !self.is_complete() {
308 return Err(Error::new(
309 ErrorKind::WouldBlock,
310 "font buffer is incomplete; append all chunks before decoding",
311 ));
312 }
313
314 open_font_from_buffer(&self.data)
315 }
316
317 pub fn into_font_face(self) -> Result<FontFace, Error> {
319 self.into_font_file()?.current_face()
320 }
321
322 #[cfg(feature = "raw")]
323 pub fn into_loaded_font(self) -> Result<FontFace, Error> {
324 self.into_font_face()
325 }
326}
327
328#[cfg(not(target_arch = "wasm32"))]
329fn fetch_http_font(url: &str) -> Result<Vec<u8>, Error> {
330 let url = url.strip_prefix("http://").ok_or_else(|| {
331 Error::new(
332 ErrorKind::InvalidInput,
333 "only http:// URLs are supported for font loading",
334 )
335 })?;
336
337 let (authority, path) = match url.split_once('/') {
338 Some((authority, path)) => (authority, format!("/{}", path)),
339 None => (url, "/".to_string()),
340 };
341
342 let (host, port) = match authority.rsplit_once(':') {
343 Some((host, port)) if !host.is_empty() && !port.is_empty() => {
344 let port = port
345 .parse::<u16>()
346 .map_err(|_| Error::new(ErrorKind::InvalidInput, "invalid port in http URL"))?;
347 (host.to_string(), port)
348 }
349 _ => (authority.to_string(), 80),
350 };
351
352 let mut stream = TcpStream::connect((host.as_str(), port))?;
353 let host_header = if port == 80 {
354 host.clone()
355 } else {
356 format!("{}:{}", host, port)
357 };
358 let request = format!(
359 "GET {} HTTP/1.1\r\nHost: {}\r\nConnection: close\r\nAccept: */*\r\n\r\n",
360 path, host_header
361 );
362 stream.write_all(request.as_bytes())?;
363
364 let mut response = Vec::new();
365 stream.read_to_end(&mut response)?;
366
367 let header_end = response
368 .windows(4)
369 .position(|window| window == b"\r\n\r\n")
370 .ok_or_else(|| Error::new(ErrorKind::InvalidData, "invalid http response"))?
371 + 4;
372
373 let header = std::str::from_utf8(&response[..header_end])
374 .map_err(|_| Error::new(ErrorKind::InvalidData, "invalid http header"))?;
375 if !(header.starts_with("HTTP/1.1 200") || header.starts_with("HTTP/1.0 200")) {
376 return Err(Error::new(
377 ErrorKind::InvalidData,
378 format!(
379 "unexpected http status: {}",
380 header.lines().next().unwrap_or("")
381 ),
382 ));
383 }
384
385 Ok(response[header_end..].to_vec())
386}