mp4_atom/moov/trak/mdia/minf/stbl/stsd/
ftab.rs

1use crate::*;
2
3/// Font table for tx3g
4///
5/// 3GPP TS 26.245 or ETSI TS 126 245 Section 5.16
6/// See https://www.etsi.org/deliver/etsi_ts/126200_126299/126245/18.00.00_60/ts_126245v180000p.pdf
7
8#[derive(Debug, Clone, PartialEq, Eq, Default)]
9#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
10pub struct FontEntry {
11    pub font_id: u16,
12    pub font: String,
13}
14
15#[derive(Debug, Clone, PartialEq, Eq, Default)]
16#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
17pub struct Ftab {
18    pub font_entries: Vec<FontEntry>,
19}
20
21impl Atom for Ftab {
22    const KIND: FourCC = FourCC::new(b"ftab");
23
24    fn decode_body<B: Buf>(buf: &mut B) -> Result<Self> {
25        let entry_count = u16::decode(buf)?;
26        let mut font_entries = Vec::with_capacity(std::cmp::min(8, entry_count as usize));
27        for _ in 0..entry_count {
28            let font_id = u16::decode(buf)?;
29            let font_name_length = u8::decode(buf)?;
30            let font_bytes = Vec::decode_exact(buf, font_name_length as usize)?;
31            let font = if font_bytes.len() >= 4 && font_bytes[0] == 0xFF && font_bytes[1] == 0xFE {
32                // UTF-16 little endian
33                if font_name_length % 2 != 0 {
34                    return Err(Error::InvalidSize);
35                }
36                let utf_16_len: usize = ((font_name_length - 2) / 2) as usize;
37                let mut utf_16 = Vec::with_capacity(utf_16_len);
38                for i in 1..=utf_16_len {
39                    let bytes = [font_bytes[i * 2], font_bytes[i * 2 + 1]];
40                    utf_16.push(u16::from_le_bytes(bytes));
41                }
42                String::from_utf16(&utf_16).map_err(|_| {
43                    Error::InvalidString("Failed to parse UTF-16 LE font id in ftab".into())
44                })?
45            } else if font_bytes.len() >= 4 && font_bytes[0] == 0xFE && font_bytes[1] == 0xFF {
46                // UTF-16 big endian
47                if font_name_length % 2 != 0 {
48                    return Err(Error::InvalidSize);
49                }
50                let utf_16_len: usize = ((font_name_length - 2) / 2) as usize;
51                let mut utf_16 = Vec::with_capacity(utf_16_len);
52                for i in 1..=utf_16_len {
53                    let bytes = [font_bytes[i * 2], font_bytes[i * 2 + 1]];
54                    utf_16.push(u16::from_be_bytes(bytes));
55                }
56                String::from_utf16(&utf_16).map_err(|_| {
57                    Error::InvalidString("Failed to parse UTF-16 BE font id in ftab".into())
58                })?
59            } else {
60                String::from_utf8(font_bytes)
61                    .map_err(|_| Error::InvalidString("Failed to parse font id in ftab".into()))?
62            };
63            let font_entry = FontEntry { font_id, font };
64            font_entries.push(font_entry);
65        }
66        Ok(Ftab { font_entries })
67    }
68
69    fn encode_body<B: BufMut>(&self, buf: &mut B) -> Result<()> {
70        let entry_count: u16 = self
71            .font_entries
72            .len()
73            .try_into()
74            .map_err(|_| Error::TooLarge(Self::KIND))?;
75        entry_count.encode(buf)?;
76        for font_entry in &self.font_entries {
77            font_entry.font_id.encode(buf)?;
78            // We always encode as UTF-8, so there won't be round tripping of UTF-16. That is OK.
79            let font_bytes = font_entry.font.as_bytes();
80            let font_name_length: u8 = font_bytes
81                .len()
82                .try_into()
83                .map_err(|_| Error::TooLarge(Self::KIND))?;
84            font_name_length.encode(buf)?;
85            font_bytes.encode(buf)?;
86        }
87
88        Ok(())
89    }
90}