1pub mod context;
3mod gdef;
4pub mod jsondiff;
5mod layout;
6pub mod monkeypatching;
7pub mod namemap;
8mod serializefont;
9
10use crate::{jsondiff::diff, serializefont::ToValue};
11use context::SerializationContext;
12use namemap::NameMap;
13use read_fonts::{traversal::SomeTable, FontRef, TableProvider};
14use serde_json::{Map, Value};
15use skrifa::{charmap::Charmap, string::StringId, MetadataProvider};
16
17pub use layout::gpos::just_kerns;
18
19fn serialize_name_table<'a>(font: &(impl MetadataProvider<'a> + TableProvider<'a>)) -> Value {
20 let mut map = Map::new();
21 if let Ok(name) = font.name() {
22 let mut ids: Vec<StringId> = name.name_record().iter().map(|x| x.name_id()).collect();
23 ids.sort_by_key(|id| id.to_u16());
24 for id in ids {
25 let strings = font.localized_strings(id);
26 if strings.clone().next().is_some() {
27 let mut localized = Map::new();
28 for string in font.localized_strings(id) {
29 localized.insert(
30 string.language().unwrap_or("default").to_string(),
31 Value::String(string.to_string()),
32 );
33 }
34 map.insert(id.to_string(), Value::Object(localized));
35 }
36 }
37 }
38 Value::Object(map)
39}
40
41fn serialize_cmap_table<'a>(font: &FontRef<'a>, names: &NameMap) -> Value {
42 let charmap = Charmap::new(font);
43 let mut map: Map<String, Value> = Map::new();
44 for (codepoint, gid) in charmap.mappings() {
45 let name = names.get(gid);
46 map.insert(format!("U+{:04X}", codepoint), Value::String(name));
47 }
48 Value::Object(map)
49}
50
51fn serialize_hmtx_table<'a>(font: &impl TableProvider<'a>, names: &NameMap) -> Value {
52 let mut map = Map::new();
53 if let Ok(hmtx) = font.hmtx() {
54 let widths = hmtx.h_metrics();
55 let long_metrics = widths.len();
56 for gid in 0..font.maxp().unwrap().num_glyphs() {
57 let name = names.get(gid);
58 if gid < (long_metrics as u16) {
59 if let Some((width, lsb)) = widths
60 .get(gid as usize)
61 .map(|lm| (lm.advance(), lm.side_bearing()))
62 {
63 map.insert(
64 name,
65 Value::Object(
66 vec![
67 ("width".to_string(), Value::Number(width.into())),
68 ("lsb".to_string(), Value::Number(lsb.into())),
69 ]
70 .into_iter()
71 .collect(),
72 ),
73 );
74 }
75 } else {
76 }
78 }
79 }
80 Value::Object(map)
81}
82
83pub fn font_to_json(font: &FontRef, glyphmap: Option<&NameMap>) -> Value {
93 let glyphmap = if let Some(glyphmap) = glyphmap {
94 glyphmap
95 } else {
96 &NameMap::new(font)
97 };
98 let mut map = Map::new();
99 let context = SerializationContext::new(font, glyphmap.clone()).unwrap_or_else(|_| {
101 panic!("Could not create serialization context for font");
102 });
103
104 for table in font.table_directory.table_records().iter() {
108 let key = table.tag().to_string();
109 let value = match table.tag().into_bytes().as_ref() {
110 b"head" => font.head().map(|t| <dyn SomeTable>::serialize(&t)),
111 b"hhea" => font.hhea().map(|t| <dyn SomeTable>::serialize(&t)),
112 b"vhea" => font.vhea().map(|t| <dyn SomeTable>::serialize(&t)),
113 b"vmtx" => font.vmtx().map(|t| <dyn SomeTable>::serialize(&t)),
114 b"fvar" => font.fvar().map(|t| <dyn SomeTable>::serialize(&t)),
115 b"avar" => font.avar().map(|t| <dyn SomeTable>::serialize(&t)),
116 b"HVAR" => font.hvar().map(|t| <dyn SomeTable>::serialize(&t)),
117 b"VVAR" => font.vvar().map(|t| <dyn SomeTable>::serialize(&t)),
118 b"MVAR" => font.mvar().map(|t| <dyn SomeTable>::serialize(&t)),
119 b"maxp" => font.maxp().map(|t| <dyn SomeTable>::serialize(&t)),
120 b"OS/2" => font.os2().map(|t| <dyn SomeTable>::serialize(&t)),
121 b"post" => font.post().map(|t| <dyn SomeTable>::serialize(&t)),
122 b"loca" => font.loca(None).map(|t| <dyn SomeTable>::serialize(&t)),
123 b"glyf" => font.glyf().map(|t| <dyn SomeTable>::serialize(&t)),
124 b"gvar" => font.gvar().map(|t| <dyn SomeTable>::serialize(&t)),
125 b"COLR" => font.colr().map(|t| <dyn SomeTable>::serialize(&t)),
126 b"CPAL" => font.cpal().map(|t| <dyn SomeTable>::serialize(&t)),
127 b"STAT" => font.stat().map(|t| <dyn SomeTable>::serialize(&t)),
128 _ => font.expect_data_for_tag(table.tag()).map(|tabledata| {
129 Value::Array(
130 tabledata
131 .as_ref()
132 .iter()
133 .map(|&x| Value::Number(x.into()))
134 .collect(),
135 )
136 }),
137 };
138 map.insert(
139 key,
140 value.unwrap_or_else(|_| Value::String("Could not parse".to_string())),
141 );
142 }
143
144 map.insert("name".to_string(), serialize_name_table(font));
146 map.insert("cmap".to_string(), serialize_cmap_table(font, glyphmap));
147 map.insert("hmtx".to_string(), serialize_hmtx_table(font, glyphmap));
148 map.insert("GDEF".to_string(), gdef::serialize_gdef_table(&context));
149 map.insert("GPOS".to_string(), layout::serialize_gpos_table(&context));
150 map.insert("GSUB".to_string(), layout::serialize_gsub_table(&context));
151 Value::Object(map)
152}
153
154pub fn table_diff(font_a: &FontRef, font_b: &FontRef, max_changes: usize, no_match: bool) -> Value {
166 let glyphmap_a = NameMap::new(font_a);
167 let glyphmap_b = NameMap::new(font_b);
168 let big_difference = !no_match && !glyphmap_a.compatible(&glyphmap_b);
169
170 #[cfg(not(target_family = "wasm"))]
171 if big_difference {
172 log::info!("Glyph names differ dramatically between fonts, using font names from font A");
173 }
174
175 let mut font_a_json = font_to_json(font_a, Some(&glyphmap_a));
176 let mut font_b_json = font_to_json(
177 font_b,
178 Some(if big_difference {
179 &glyphmap_a
180 } else {
181 &glyphmap_b
182 }),
183 );
184
185 font_a_json.as_object_mut().unwrap().remove("glyf");
187 font_b_json.as_object_mut().unwrap().remove("glyf");
188 font_a_json.as_object_mut().unwrap().remove("loca");
189 font_b_json.as_object_mut().unwrap().remove("loca");
190
191 diff(&font_a_json, &font_b_json, max_changes)
192}
193
194pub fn kern_diff(font_a: &FontRef, font_b: &FontRef, max_changes: usize, no_match: bool) -> Value {
203 let glyphmap_a = NameMap::new(font_a);
204 let glyphmap_b = NameMap::new(font_b);
205 let big_difference = !no_match && !glyphmap_a.compatible(&glyphmap_b);
206
207 #[cfg(not(target_family = "wasm"))]
208 if big_difference {
209 log::info!("Glyph names differ dramatically between fonts, using font names from font A");
210 }
211
212 let kerns_a = just_kerns(font_to_json(font_a, None));
213 let kerns_b = just_kerns(font_to_json(
215 font_b,
216 Some(if big_difference {
217 &glyphmap_a
218 } else {
219 &glyphmap_b
220 }),
221 ));
222 diff(&kerns_a, &kerns_b, max_changes)
225}