1use crate::{v2, Family as FamilyV1, Font as FontV1, FontsManifest as FontsManifestV1};
8use anyhow::{format_err, Error};
9use itertools::Itertools;
10use std::path::{Path, PathBuf};
11
12impl TryFrom<FontsManifestV1> for v2::FontsManifest {
13 type Error = Error;
14
15 fn try_from(old: FontsManifestV1) -> Result<v2::FontsManifest, Error> {
19 let families_and_fallbacks: Result<Vec<(v2::Family, bool)>, _> = old
23 .families
24 .iter()
25 .map(|v1_family| {
26 v2::Family::try_from(v1_family).map(|v2_family| (v2_family, v1_family.fallback))
27 })
28 .collect();
29 let families_and_fallbacks = families_and_fallbacks?;
30 let fallback_chain: Vec<v2::TypefaceId> = families_and_fallbacks
33 .iter()
34 .filter(|(_, is_fallback)| *is_fallback)
35 .flat_map(|(family, _)| {
36 family.assets.iter().flat_map(|asset| {
37 asset.typefaces.iter().map(move |typeface| v2::TypefaceId {
38 file_name: asset.file_name.clone(),
39 index: typeface.index,
40 })
41 })
42 })
43 .collect();
44 let families = families_and_fallbacks.into_iter().map(|(family, _)| family).collect();
45 Ok(v2::FontsManifest { families, fallback_chain, settings: v2::Settings::default() })
46 }
47}
48
49impl TryFrom<&FamilyV1> for v2::Family {
50 type Error = Error;
51
52 fn try_from(old: &FamilyV1) -> Result<v2::Family, Error> {
56 let assets: Result<Vec<v2::Asset>, _> = old
57 .fonts
58 .iter()
59 .group_by(|font| &font.asset)
60 .into_iter()
61 .map(|(asset_path, font_group)| group_fonts_into_assets(asset_path, font_group))
62 .collect();
63
64 let aliases = match &old.aliases {
66 None => vec![],
67 Some(aliases) => vec![v2::FontFamilyAliasSet::without_overrides(aliases)?],
68 };
69
70 Ok(v2::Family {
71 name: old.family.clone(),
72 aliases,
73 generic_family: old.generic_family.clone(),
74 assets: assets?,
75 })
76 }
77}
78
79fn group_fonts_into_assets<'a>(
86 asset_path: &PathBuf,
87 font_group: impl Iterator<Item = &'a FontV1>,
88) -> Result<v2::Asset, Error> {
89 let file_name: String = asset_path
92 .file_name()
93 .ok_or_else(|| format_err!("Invalid path: {:?}", asset_path))?
94 .to_str()
95 .ok_or_else(|| format_err!("Invalid path: {:?}", asset_path))?
96 .to_string();
97 let directory: PathBuf =
99 asset_path.parent().map_or_else(|| PathBuf::from(""), Path::to_path_buf);
100 Ok(v2::Asset {
102 file_name,
103 location: v2::AssetLocation::LocalFile(v2::LocalFileLocator { directory }),
104 typefaces: font_group.map(font_to_typeface).collect(),
105 })
106}
107
108fn font_to_typeface(font: &FontV1) -> v2::Typeface {
110 v2::Typeface {
111 index: font.index,
112 languages: font.languages.clone(),
113 style: v2::Style { slant: font.slant, weight: font.weight, width: font.width },
114 code_points: font.code_points.clone(),
115 postscript_name: None,
116 full_name: None,
117 }
118}
119
120#[cfg(test)]
121mod tests {
122 use super::*;
123 use char_set::CharSet;
124 use fidl_fuchsia_fonts::{GenericFontFamily, Slant, Width};
125
126 #[test]
127 fn test_v1_to_v2() -> Result<(), Error> {
128 let old = FontsManifestV1 {
129 families: vec![
130 FamilyV1 {
131 family: "FamilyA".to_string(),
132 aliases: Some(vec!["Family A".to_string(), "A Family".to_string()]),
133 fonts: vec![
134 FontV1 {
135 asset: PathBuf::from("path/to/FamilyA-ExtraBold-Condensed.ttf"),
136 index: 0,
137 slant: Slant::Upright,
138 weight: 800,
139 width: Width::Condensed,
140 languages: vec!["en-US".to_string()],
141 package: None,
142 code_points: CharSet::new(vec![0x1, 0x2, 0x3, 0x7, 0x8, 0x9, 0x100]),
143 },
144 FontV1 {
145 asset: PathBuf::from("path/to/FamilyA-ExtraLight.ttf"),
146 index: 0,
147 slant: Slant::Upright,
148 weight: 200,
149 width: Width::Normal,
150 languages: vec!["en-US".to_string()],
151 package: None,
152 code_points: CharSet::new(vec![
153 0x11, 0x12, 0x13, 0x17, 0x18, 0x19, 0x100,
154 ]),
155 },
156 ],
157 fallback: true,
158 generic_family: Some(GenericFontFamily::SansSerif),
159 },
160 FamilyV1 {
161 family: "FamilyB".to_string(),
162 aliases: Some(vec!["Family B".to_string(), "B Family".to_string()]),
163 fonts: vec![
164 FontV1 {
165 asset: PathBuf::from("FamilyB.ttc"),
166 index: 0,
167 slant: Slant::Upright,
168 weight: 800,
169 width: Width::Condensed,
170 languages: vec!["en-US".to_string()],
171 package: None,
172 code_points: CharSet::new(vec![0x1, 0x2, 0x3, 0x7, 0x8, 0x9, 0x100]),
173 },
174 FontV1 {
175 asset: PathBuf::from("FamilyB.ttc"),
176 index: 1,
177 slant: Slant::Upright,
178 weight: 200,
179 width: Width::Normal,
180 languages: vec!["zh-Hant".to_string()],
181 package: None,
182 code_points: CharSet::new(vec![
183 0x11, 0x12, 0x13, 0x17, 0x18, 0x19, 0x100,
184 ]),
185 },
186 ],
187 fallback: false,
188 generic_family: None,
189 },
190 ],
191 };
192
193 let expected = v2::FontsManifest {
194 families: vec![
195 v2::Family {
196 name: "FamilyA".to_string(),
197 aliases: vec![v2::FontFamilyAliasSet::without_overrides(vec![
198 "Family A", "A Family",
199 ])?],
200 generic_family: Some(GenericFontFamily::SansSerif),
201 assets: vec![
202 v2::Asset {
203 file_name: "FamilyA-ExtraBold-Condensed.ttf".to_string(),
204 location: v2::AssetLocation::LocalFile(v2::LocalFileLocator {
205 directory: PathBuf::from("path/to"),
206 }),
207 typefaces: vec![v2::Typeface {
208 index: 0,
209 languages: vec!["en-US".to_string()],
210 style: v2::Style {
211 slant: Slant::Upright,
212 weight: 800,
213 width: Width::Condensed,
214 },
215 code_points: CharSet::new(vec![
216 0x1, 0x2, 0x3, 0x7, 0x8, 0x9, 0x100,
217 ]),
218 postscript_name: None,
219 full_name: None,
220 }],
221 },
222 v2::Asset {
223 file_name: "FamilyA-ExtraLight.ttf".to_string(),
224 location: v2::AssetLocation::LocalFile(v2::LocalFileLocator {
225 directory: PathBuf::from("path/to"),
226 }),
227 typefaces: vec![v2::Typeface {
228 index: 0,
229 languages: vec!["en-US".to_string()],
230 style: v2::Style {
231 slant: Slant::Upright,
232 weight: 200,
233 width: Width::Normal,
234 },
235 code_points: CharSet::new(vec![
236 0x11, 0x12, 0x13, 0x17, 0x18, 0x19, 0x100,
237 ]),
238 postscript_name: None,
239 full_name: None,
240 }],
241 },
242 ],
243 },
244 v2::Family {
245 name: "FamilyB".to_string(),
246 aliases: vec![v2::FontFamilyAliasSet::without_overrides(vec![
247 "Family B", "B Family",
248 ])?],
249 generic_family: None,
250 assets: vec![v2::Asset {
251 file_name: "FamilyB.ttc".to_string(),
252 location: v2::AssetLocation::LocalFile(v2::LocalFileLocator {
253 directory: PathBuf::from(""),
254 }),
255 typefaces: vec![
256 v2::Typeface {
257 index: 0,
258 languages: vec!["en-US".to_string()],
259 style: v2::Style {
260 slant: Slant::Upright,
261 weight: 800,
262 width: Width::Condensed,
263 },
264 code_points: CharSet::new(vec![
265 0x1, 0x2, 0x3, 0x7, 0x8, 0x9, 0x100,
266 ]),
267 postscript_name: None,
268 full_name: None,
269 },
270 v2::Typeface {
271 index: 1,
272 languages: vec!["zh-Hant".to_string()],
273 style: v2::Style {
274 slant: Slant::Upright,
275 weight: 200,
276 width: Width::Normal,
277 },
278 code_points: CharSet::new(vec![
279 0x11, 0x12, 0x13, 0x17, 0x18, 0x19, 0x100,
280 ]),
281 postscript_name: None,
282 full_name: None,
283 },
284 ],
285 }],
286 },
287 ],
288 fallback_chain: vec![
289 v2::TypefaceId {
290 file_name: "FamilyA-ExtraBold-Condensed.ttf".to_string(),
291 index: 0,
292 },
293 v2::TypefaceId { file_name: "FamilyA-ExtraLight.ttf".to_string(), index: 0 },
294 ],
295 settings: v2::Settings::default(),
296 };
297
298 assert_eq!(v2::FontsManifest::try_from(old)?, expected);
299 Ok(())
300 }
301}