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<read_fonts::tables::meta::Metadata<'_>> for Metadata {
100 fn from_obj_ref(from: &read_fonts::tables::meta::Metadata<'_>, _: FontData) -> Self {
101 match from {
102 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 read_fonts::tables::meta::Metadata::Other(bytes) => Self::Other(bytes.to_vec()),
114 }
115 }
116}
117
118impl FromTableRef<read_fonts::tables::meta::Metadata<'_>> for Metadata {}
119
120impl Default for Metadata {
123 fn default() -> Self {
124 Metadata::ScriptLangTags(Vec::new())
125 }
126}
127
128impl FromObjRef<read_fonts::tables::meta::DataMapRecord> for DataMapRecord {
129 fn from_obj_ref(obj: &read_fonts::tables::meta::DataMapRecord, offset_data: FontData) -> Self {
130 let data = obj
131 .data(offset_data)
132 .map(|meta| meta.to_owned_table())
133 .unwrap_or_else(|_| match obj.tag() {
134 DLNG | SLNG => Metadata::ScriptLangTags(Vec::new()),
135 _ => Metadata::Other(Vec::new()),
136 });
137 DataMapRecord {
138 tag: obj.tag(),
139 data: OffsetMarker::new(data),
140 }
141 }
142}
143
144#[cfg(test)]
145mod tests {
146
147 use super::*;
148 use font_test_data::meta as test_data;
149
150 #[test]
151 fn convert_from_read() {
152 let table = Meta::read(test_data::SIMPLE_META_TABLE.into()).unwrap();
153 let rec1 = &table.data_maps[0];
154 assert_eq!(
155 rec1.data.as_ref(),
156 &Metadata::ScriptLangTags(vec![
157 ScriptLangTag::new("en-latn".into()).unwrap(),
158 ScriptLangTag::new("latn".into()).unwrap()
159 ])
160 );
161
162 let round_trip = crate::dump_table(&table).unwrap();
163 let read_back = Meta::read(round_trip.as_slice().into()).unwrap();
164 let readr = read_fonts::tables::meta::Meta::read(round_trip.as_slice().into()).unwrap();
165 dbg!(readr);
166
167 assert_eq!(table, read_back);
170 }
171}