read_fonts/tables/
meta.rs1include!("../../generated/generated_meta.rs");
4
5pub const DLNG: Tag = Tag::new(b"dlng");
6pub const SLNG: Tag = Tag::new(b"slng");
7
8pub enum Metadata<'a> {
10 ScriptLangTags(VarLenArray<'a, ScriptLangTag<'a>>),
12 Other(&'a [u8]),
14}
15
16impl ReadArgs for Metadata<'_> {
17 type Args = (Tag, u32);
18}
19
20impl<'a> FontReadWithArgs<'a> for Metadata<'a> {
21 fn read_with_args(data: FontData<'a>, args: &Self::Args) -> Result<Self, ReadError> {
22 let (tag, len) = *args;
23 let data = data.slice(0..len as usize).ok_or(ReadError::OutOfBounds)?;
24 if [DLNG, SLNG].contains(&tag) {
25 VarLenArray::read(data).map(Metadata::ScriptLangTags)
26 } else {
27 Ok(Metadata::Other(data.as_bytes()))
28 }
29 }
30}
31
32#[derive(Clone, Debug)]
33pub struct ScriptLangTag<'a>(&'a str);
34
35impl<'a> ScriptLangTag<'a> {
36 pub fn as_str(&self) -> &'a str {
37 self.0
38 }
39}
40
41impl AsRef<str> for ScriptLangTag<'_> {
42 fn as_ref(&self) -> &str {
43 self.0
44 }
45}
46
47#[cfg(feature = "std")]
48impl From<ScriptLangTag<'_>> for String {
49 fn from(value: ScriptLangTag<'_>) -> Self {
50 value.0.into()
51 }
52}
53
54impl VarSize for ScriptLangTag<'_> {
55 type Size = u32;
56
57 fn read_len_at(data: FontData, pos: usize) -> Option<usize> {
58 let bytes = data.split_off(pos)?.as_bytes();
59 if bytes.is_empty() {
60 return None;
61 }
62 let end = data
63 .as_bytes()
64 .iter()
65 .position(|b| *b == b',')
66 .map(|pos| pos + 1) .unwrap_or(bytes.len());
68 Some(end)
69 }
70}
71
72impl<'a> FontRead<'a> for ScriptLangTag<'a> {
73 fn read(data: FontData<'a>) -> Result<Self, ReadError> {
74 std::str::from_utf8(data.as_bytes())
75 .map_err(|_| ReadError::MalformedData("LangScriptTag must be utf8"))
76 .map(|s| ScriptLangTag(s.trim_matches([' ', ','])))
77 }
78}
79
80#[cfg(test)]
81mod tests {
82 use super::*;
83 use font_test_data::meta as test_data;
84
85 impl PartialEq<&str> for ScriptLangTag<'_> {
86 fn eq(&self, other: &&str) -> bool {
87 self.as_ref() == *other
88 }
89 }
90
91 fn expect_script_lang_tags(table: Metadata, expected: &[&str]) -> bool {
92 let Metadata::ScriptLangTags(langs) = table else {
93 panic!("wrong metadata");
94 };
95 let result = langs.iter().map(|x| x.unwrap()).collect::<Vec<_>>();
96 result == expected
97 }
98
99 #[test]
100 fn parse_simple() {
101 let table = Meta::read(test_data::SIMPLE_META_TABLE.into()).unwrap();
102 let rec1 = table.data_maps()[0];
103 let rec2 = table.data_maps()[1];
104
105 assert_eq!(rec1.tag(), Tag::new(b"dlng"));
106 assert_eq!(rec2.tag(), Tag::new(b"slng"));
107 assert!(expect_script_lang_tags(
108 rec1.data(table.offset_data()).unwrap(),
109 &["en-latn", "latn"]
110 ));
111 assert!(expect_script_lang_tags(
112 rec2.data(table.offset_data()).unwrap(),
113 &["latn"]
114 ));
115 }
116}