fontcull_write_fonts/tables/
meta.rs1use std::fmt::Display;
4
5include!("../../generated/generated_meta.rs");
6
7pub const DLNG: Tag = Tag::new(b"dlng");
8pub const SLNG: Tag = Tag::new(b"slng");
9
10#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
12#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
13pub enum Metadata {
14 ScriptLangTags(Vec<ScriptLangTag>),
16 Other(Vec<u8>),
18}
19
20#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
27#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
28pub struct ScriptLangTag(String);
29
30#[derive(Clone, Debug)]
32#[non_exhaustive] pub struct InvalidScriptLangTag;
34
35impl ScriptLangTag {
36 pub fn new(raw: String) -> Result<Self, InvalidScriptLangTag> {
37 Ok(Self(raw))
38 }
39
40 pub fn as_str(&self) -> &str {
41 self.0.as_str()
42 }
43}
44
45impl Display for InvalidScriptLangTag {
46 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
47 f.write_str("ScriptLangTag was malformed")
48 }
49}
50
51impl std::error::Error for InvalidScriptLangTag {}
52
53impl DataMapRecord {
54 fn validate_data_type(&self, ctx: &mut ValidationCtx) {
55 if matches!(
56 (self.tag, self.data.as_ref()),
57 (SLNG | DLNG, Metadata::Other(_))
58 ) {
59 ctx.report("'slng' or 'dlng' tags use ScriptLangTag data");
60 }
61 }
62
63 fn compute_data_len(&self) -> usize {
64 match self.data.as_ref() {
65 Metadata::ScriptLangTags(items) => {
66 let sum_len: usize = items.iter().map(|tag| tag.as_str().len()).sum();
67 let toss_some_commas_in_there = items.len().saturating_sub(1);
68 sum_len + toss_some_commas_in_there
69 }
70 Metadata::Other(vec) => vec.len(),
71 }
72 }
73}
74
75impl FontWrite for Metadata {
76 fn write_into(&self, writer: &mut TableWriter) {
77 match self {
78 Metadata::ScriptLangTags(langs) => {
79 let mut first = true;
80 for lang in langs {
81 if !first {
82 b','.write_into(writer);
83 }
84 first = false;
85 lang.0.as_bytes().write_into(writer);
86 }
87 }
88 Metadata::Other(vec) => {
89 vec.write_into(writer);
90 }
91 };
92 }
93}
94
95impl Validate for Metadata {
96 fn validate_impl(&self, _ctx: &mut ValidationCtx) {}
97}
98
99impl FromObjRef<fontcull_read_fonts::tables::meta::Metadata<'_>> for Metadata {
100 fn from_obj_ref(from: &fontcull_read_fonts::tables::meta::Metadata<'_>, _: FontData) -> Self {
101 match from {
102 fontcull_read_fonts::tables::meta::Metadata::ScriptLangTags(var_len_array) => {
103 Self::ScriptLangTags(
104 var_len_array
105 .iter()
106 .flat_map(|x| {
107 x.ok()
108 .and_then(|x| ScriptLangTag::new(x.as_str().into()).ok())
109 })
110 .collect(),
111 )
112 }
113 fontcull_read_fonts::tables::meta::Metadata::Other(bytes) => {
114 Self::Other(bytes.to_vec())
115 }
116 }
117 }
118}
119
120impl FromTableRef<fontcull_read_fonts::tables::meta::Metadata<'_>> for Metadata {}
121
122impl Default for Metadata {
125 fn default() -> Self {
126 Metadata::ScriptLangTags(Vec::new())
127 }
128}
129
130impl FromObjRef<fontcull_read_fonts::tables::meta::DataMapRecord> for DataMapRecord {
131 fn from_obj_ref(
132 obj: &fontcull_read_fonts::tables::meta::DataMapRecord,
133 offset_data: FontData,
134 ) -> Self {
135 let data = obj
136 .data(offset_data)
137 .map(|meta| meta.to_owned_table())
138 .unwrap_or_else(|_| match obj.tag() {
139 DLNG | SLNG => Metadata::ScriptLangTags(Vec::new()),
140 _ => Metadata::Other(Vec::new()),
141 });
142 DataMapRecord {
143 tag: obj.tag(),
144 data: OffsetMarker::new(data),
145 }
146 }
147}
148
149#[cfg(test)]
150mod tests {
151
152 use super::*;
153 use fontcull_font_test_data::meta as test_data;
154
155 #[test]
156 fn convert_from_read() {
157 let table = Meta::read(test_data::SIMPLE_META_TABLE.into()).unwrap();
158 let rec1 = &table.data_maps[0];
159 assert_eq!(
160 rec1.data.as_ref(),
161 &Metadata::ScriptLangTags(vec![
162 ScriptLangTag::new("en-latn".into()).unwrap(),
163 ScriptLangTag::new("latn".into()).unwrap()
164 ])
165 );
166
167 let round_trip = crate::dump_table(&table).unwrap();
168 let read_back = Meta::read(round_trip.as_slice().into()).unwrap();
169 let readr =
170 fontcull_read_fonts::tables::meta::Meta::read(round_trip.as_slice().into()).unwrap();
171 dbg!(readr);
172
173 assert_eq!(table, read_back);
176 }
177}