Skip to main content

honzo_chunks/data/
font.rs

1use honzo_core::{FontEmbedding, HonzoError};
2
3pub const FONT_TAG: [u8; 4] = *b"FONT";
4
5pub fn is_font_tag(tag: &[u8; 4]) -> bool {
6    *tag == FONT_TAG
7}
8
9/// Validate font bytes by checking known magic bytes.
10pub fn validate_font(bytes: &[u8]) -> Result<&[u8], HonzoError> {
11    if guess_font_format(bytes).is_none() {
12        return Err(HonzoError::Truncated);
13    }
14    Ok(bytes)
15}
16
17/// Validate font bytes and map error to u8 code for FFI.
18pub fn validate_font_bytes(bytes: &[u8]) -> Result<(), u8> {
19    match validate_font(bytes) {
20        Ok(..) => Ok(()),
21        Err(HonzoError::Truncated) => Err(7),
22        Err(..) => Err(255),
23    }
24}
25
26/// Guess the font format from magic bytes.
27pub fn guess_font_format(bytes: &[u8]) -> Option<&'static str> {
28    if bytes.len() < 4 {
29        return None;
30    }
31    if &bytes[..4] == b"wOFF" {
32        return Some("font/woff");
33    }
34    if &bytes[..4] == b"wOF2" {
35        return Some("font/woff2");
36    }
37    if &bytes[..4] == b"OTTO" {
38        return Some("font/otf");
39    }
40    if bytes.starts_with(&[0x00, 0x01, 0x00, 0x00])
41        || bytes.starts_with(&[0x00, 0x01, 0x00, 0x01])
42        || bytes.starts_with(b"true")
43        || bytes.starts_with(b"typ1")
44    {
45        return Some("font/ttf");
46    }
47    None
48}
49
50/// Parse font_embedding and font_license_url from a TOC entry at cursor.
51/// Advances cursor past the consumed bytes.
52pub fn read_font_toc_fields<'a>(
53    buf: &'a [u8],
54    cursor: &mut usize,
55) -> Result<(FontEmbedding, Option<&'a str>), HonzoError> {
56    if *cursor + 3 > buf.len() {
57        return Err(HonzoError::Truncated);
58    }
59    let embedding = FontEmbedding::from_u8(buf[*cursor])?;
60    *cursor += 1;
61    let url_len = u16::from_le_bytes([buf[*cursor], buf[*cursor + 1]]) as usize;
62    *cursor += 2;
63    let license_url = if url_len > 0 {
64        let end = *cursor + url_len;
65        if end > buf.len() {
66            return Err(HonzoError::Truncated);
67        }
68        let s = core::str::from_utf8(&buf[*cursor..end]).map_err(|_| HonzoError::Truncated)?;
69        *cursor = end;
70        Some(s)
71    } else {
72        None
73    };
74    Ok((embedding, license_url))
75}
76
77/// Serialize font_embedding and font_license_url into the output buffer.
78pub fn write_font_toc_fields(
79    out: &mut Vec<u8>,
80    embedding: FontEmbedding,
81    license_url: Option<&str>,
82) {
83    out.push(embedding as u8);
84    if let Some(url) = license_url {
85        out.extend_from_slice(&(url.len() as u16).to_le_bytes());
86        out.extend_from_slice(url.as_bytes());
87    } else {
88        out.extend_from_slice(&0u16.to_le_bytes());
89    }
90}
91
92pub fn chunk_name() -> &'static str {
93    "font"
94}