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> FontRead<'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 ReadArgs for ScriptLangTag<'_> {
73 type Args = ();
74}
75
76impl<'a> FontRead<'a> for ScriptLangTag<'a> {
77 fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
78 std::str::from_utf8(data.as_bytes())
79 .map_err(|_| ReadError::MalformedData("LangScriptTag must be utf8"))
80 .map(|s| ScriptLangTag(s.trim_matches(|c| c == ' ' || c == ',')))
81 }
82}
83
84#[cfg(test)]
85mod tests {
86 use super::*;
87 use font_test_data::meta as test_data;
88
89 impl PartialEq<&str> for ScriptLangTag<'_> {
90 fn eq(&self, other: &&str) -> bool {
91 self.as_ref() == *other
92 }
93 }
94
95 fn expect_script_lang_tags(table: Metadata, expected: &[&str]) -> bool {
96 let Metadata::ScriptLangTags(langs) = table else {
97 panic!("wrong metadata");
98 };
99 let result = langs.iter().map(|x| x.unwrap()).collect::<Vec<_>>();
100 result == expected
101 }
102
103 #[test]
104 fn parse_simple() {
105 let table = Meta::read(test_data::SIMPLE_META_TABLE.into()).unwrap();
106 let rec1 = table.data_maps()[0];
107 let rec2 = table.data_maps()[1];
108
109 assert_eq!(rec1.tag(), Tag::new(b"dlng"));
110 assert_eq!(rec2.tag(), Tag::new(b"slng"));
111 assert!(expect_script_lang_tags(
112 rec1.data(table.offset_data()).unwrap(),
113 &["en-latn", "latn"]
114 ));
115 assert!(expect_script_lang_tags(
116 rec2.data(table.offset_data()).unwrap(),
117 &["latn"]
118 ));
119 }
120}