|
| 1 | +use fontspector_checkapi::{prelude::*, testfont, FileTypeConvert}; |
| 2 | +use google_fonts_glyphsets::{get_glyphset_coverage, languages_per_glyphset}; |
| 3 | +use hashbrown::HashMap; |
| 4 | +use markdown_table::MarkdownTable; |
| 5 | +use shaperglot::{Checker, Languages, ResultCode}; |
| 6 | + |
| 7 | +fn table_of_results( |
| 8 | + context: &Context, |
| 9 | + _title: &str, |
| 10 | + results: &HashMap<String, Vec<String>>, |
| 11 | +) -> Result<String, CheckError> { |
| 12 | + let table = MarkdownTable::new( |
| 13 | + results |
| 14 | + .iter() |
| 15 | + .map(|(message, languages)| vec![message.to_string(), bullet_list(context, languages)]) |
| 16 | + .collect(), |
| 17 | + ); |
| 18 | + table |
| 19 | + .as_markdown() |
| 20 | + .map_err(|_| CheckError::Error("Can't happen (table creation failed)".to_string())) |
| 21 | +} |
| 22 | +#[check( |
| 23 | + id = "googlefonts/glyphsets/shape_languages", |
| 24 | + rationale = " |
| 25 | + This check uses a heuristic to determine which GF glyphsets a font supports. |
| 26 | + Then it checks the font for correct shaping behaviour for all languages in |
| 27 | + those glyphsets. |
| 28 | + ", |
| 29 | + proposal = "https://github.com/googlefonts/fontbakery/issues/4147", |
| 30 | + title = "Shapes languages in all GF glyphsets." |
| 31 | +)] |
| 32 | +fn shape_languages(t: &Testable, context: &Context) -> CheckFnResult { |
| 33 | + let f = testfont!(t); |
| 34 | + let checker = Checker::new(&t.contents).map_err(|e| CheckError::Error(e.to_string()))?; |
| 35 | + let languages = Languages::new(); |
| 36 | + let codepoints = f.codepoints(Some(context)); |
| 37 | + let mut warns = HashMap::new(); |
| 38 | + let mut fails = HashMap::new(); |
| 39 | + let mut any_glyphset_supported = false; |
| 40 | + for (glyphset, coverage) in get_glyphset_coverage(&codepoints).iter() { |
| 41 | + if coverage.fraction > 0.8 { |
| 42 | + any_glyphset_supported = true; |
| 43 | + for language_code in languages_per_glyphset(glyphset)?.iter() { |
| 44 | + if let Some(language) = languages.get_language(language_code) { |
| 45 | + let reporter = checker.check(language); |
| 46 | + let name = language.name(); |
| 47 | + let language_string = format!("{} ({})", language_code, name); |
| 48 | + for result in reporter.iter() { |
| 49 | + let message = result.to_string(); |
| 50 | + if result.status == ResultCode::Warn { |
| 51 | + warns |
| 52 | + .entry(message) |
| 53 | + .or_insert(vec![]) |
| 54 | + .push(language_string.clone()); |
| 55 | + } else if result.status == ResultCode::Fail { |
| 56 | + fails |
| 57 | + .entry(message) |
| 58 | + .or_insert(vec![]) |
| 59 | + .push(language_string.clone()); |
| 60 | + } |
| 61 | + } |
| 62 | + } |
| 63 | + } |
| 64 | + } |
| 65 | + } |
| 66 | + |
| 67 | + let mut problems = vec![]; |
| 68 | + if !fails.is_empty() { |
| 69 | + problems.push(Status::fail( |
| 70 | + "failed-language-shaping", |
| 71 | + &format!( |
| 72 | + "Failed language shaping:\n{}", |
| 73 | + table_of_results(context, "FAIL", &fails)? |
| 74 | + ), |
| 75 | + )); |
| 76 | + } |
| 77 | + if !warns.is_empty() { |
| 78 | + problems.push(Status::warn( |
| 79 | + "warning-language-shaping", |
| 80 | + &format!( |
| 81 | + "Warning language shaping:\n{}", |
| 82 | + table_of_results(context, "WARN", &warns)? |
| 83 | + ), |
| 84 | + )); |
| 85 | + } |
| 86 | + if !any_glyphset_supported { |
| 87 | + problems.push(Status::fail("no-glyphset-supported", |
| 88 | + "No GF glyphset was found to be supported >80%, so language shaping support couldn't get checked.", |
| 89 | + )); |
| 90 | + } |
| 91 | + |
| 92 | + return_result(problems) |
| 93 | +} |
0 commit comments