diff --git a/profile-designspace/Cargo.toml b/profile-designspace/Cargo.toml index e8edf9d7..a4755818 100644 --- a/profile-designspace/Cargo.toml +++ b/profile-designspace/Cargo.toml @@ -9,7 +9,7 @@ crate-type = ["cdylib"] [dependencies] fontspector-checkapi = { path = "../fontspector-checkapi" } -norad = "0.16.0" +norad = "0.17.0" serde_json = { workspace = true } quick-xml = { version = "0.38.0", features = ["serialize"] } diff --git a/profile-fontwerk/src/checks/fontwerk/mod.rs b/profile-fontwerk/src/checks/fontwerk/mod.rs index ccd5aa04..be8468bc 100644 --- a/profile-fontwerk/src/checks/fontwerk/mod.rs +++ b/profile-fontwerk/src/checks/fontwerk/mod.rs @@ -2,7 +2,6 @@ mod names; pub use names::name_consistency; pub use names::name_entries; -pub use names::required_name_ids; mod fstype; pub use fstype::fstype; mod glyph_coverage; diff --git a/profile-fontwerk/src/checks/fontwerk/names.rs b/profile-fontwerk/src/checks/fontwerk/names.rs index 9af101d8..80a6637b 100644 --- a/profile-fontwerk/src/checks/fontwerk/names.rs +++ b/profile-fontwerk/src/checks/fontwerk/names.rs @@ -86,75 +86,6 @@ fn name_entries(f: &Testable, context: &Context) -> CheckFnResult { }) } -#[check( - id = "fontwerk/required_name_ids", - rationale = " - Required names for Fontwerk fonts: - - Copyright (0) - - Family Name (1) - - Subfamily Name (2) - - Unique ID (3) - - Full Name (4) - - Version String (5) - - PostScript Name (6) - - Trademark (7) - - Manufacturer (8) - - Designer(s) (9) - - Description (10) - - Vendor URL (11) - - Designer URL (12) - - License Description (13) - - License URL (14) - - Typographic Family Name (16) - - Typographic Subfamily Name (17) - - Variations PostScript Name Prefix (25) (if variable font) - ", - title = "Required name ids in name table" -)] -fn required_name_ids(t: &Testable, context: &Context) -> CheckFnResult { - let font = testfont!(t); - if !font.has_table(b"name") { - return Ok(Status::just_one_fail("lacks-table", "No name table.")); - } - let mut bad_names: Vec = vec![]; - - let name_PEL_codes = get_name_PEL_codes(font.font()); - for code in name_PEL_codes { - let mut missing_name_ids: Vec<_> = vec![]; - for id in [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 25] { - let name_id = StringId::from(id); - if let Some(_name_string) = - get_name_entry_string(&font.font(), code.0, code.1, code.2, name_id) - { - continue; - } else { - if id == 25 && !font.is_variable_font() { - // Skip Variations PostScript Name Prefix if not a variable font - continue; - } - missing_name_ids.push(id); - } - } - if !missing_name_ids.is_empty() { - bad_names.push(format!( - "Missing required name IDs {missing_name_ids:?} for {code:?}.", - )); - } - } - - Ok(if bad_names.is_empty() { - Status::just_one_pass() - } else { - Status::just_one_fail( - "bad-name-table-entries", - &format!( - "The following issues have been found:\n\n{}", - bullet_list(context, bad_names) - ), - ) - }) -} - #[check( id = "fontwerk/name_consistency", rationale = " @@ -519,21 +450,4 @@ mod tests { assert_eq!(result.severity, *expected_severity); } } - - #[test] - fn test_required_name_ids() { - let contents = include_bytes!( - "../../../../fontspector-py/data/test/montserrat/Montserrat-Regular.ttf" - ); - let testable = Testable::new_with_contents("demo.ttf", contents.to_vec()); - let context = Context { - ..Default::default() - }; - let result = required_name_ids_impl(&testable, &context) - .unwrap() - .next() - .unwrap(); - let expected_message = "The following issues have been found:\n\n* Missing required name IDs [7, 10, 16, 17] for (1, 0, 0).\n* Missing required name IDs [7, 10, 16, 17] for (3, 1, 1033)."; - assert_eq!(result.message, Some(expected_message.to_string())); - } } diff --git a/profile-fontwerk/src/lib.rs b/profile-fontwerk/src/lib.rs index 581644fa..9ed2e5ee 100644 --- a/profile-fontwerk/src/lib.rs +++ b/profile-fontwerk/src/lib.rs @@ -38,12 +38,18 @@ impl fontspector_checkapi::Plugin for Fontwerk { .add_section("Fontwerk Checks") .add_and_register_check(checks::fontwerk::name_entries) .add_and_register_check(checks::fontwerk::name_consistency) - .add_and_register_check(checks::fontwerk::required_name_ids) .add_and_register_check(checks::fontwerk::fstype) .add_and_register_check(checks::fontwerk::glyph_coverage) .add_and_register_check(checks::fontwerk::weightclass) // TODO: implement other Fontwerk checks // .add_and_register_check("fontwerk/names_match_default_fvar") + .include_profile("universal") + .with_configuration_defaults( + "universal/required_name_ids", + HashMap::from([ + ("required_name_ids".to_string(), json!([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 25])), + ]), + ) .with_configuration_defaults( "opentype/vendor_id", HashMap::from([ diff --git a/profile-googlefonts/src/checks/googlefonts/metadata/validate.rs b/profile-googlefonts/src/checks/googlefonts/metadata/validate.rs index 8332018f..0ce46b6b 100644 --- a/profile-googlefonts/src/checks/googlefonts/metadata/validate.rs +++ b/profile-googlefonts/src/checks/googlefonts/metadata/validate.rs @@ -42,6 +42,8 @@ fn category_hints(family_name: &str) -> Option<&'static str> { } None } +const VALID_CLASSIFICATIONS: &[&str] = &["Display", "Handwriting", "Monospace", "Symbols"]; +const VALID_STROKES: &[&str] = &["Serif", "Slab Serif", "Sans Serif"]; fn clean_url(url: &str) -> String { let mut cleaned = url.trim().to_string(); @@ -243,6 +245,40 @@ fn validate(c: &Testable, _context: &Context) -> CheckFnResult { )); } + // The METADATA.pb file can only contain specific predefined values for the + // 'stroke' and 'classifications' fields: + + // Valid stroke values: Serif, Slab Serif, Sans Serif + // Valid classifications values: Display, Handwriting, Monospace, Symbols + + // Any other values are invalid and will cause issues with the Google Fonts API. + + if let Some(stroke) = msg.stroke.as_ref() { + if !stroke.is_empty() && !VALID_STROKES.contains(&stroke.as_str()) { + problems.push(Status::fail( + "invalid-stroke", + &format!( + "METADATA.pb stroke field contains invalid value '{}'. Valid values are: {}", + stroke, + VALID_STROKES.join(", ") + ), + )); + } + } + + for classification in &msg.classifications { + if !VALID_CLASSIFICATIONS.contains(&classification.as_str()) { + problems.push(Status::fail( + "invalid-classification", + &format!( + "METADATA.pb classifications field contains invalid value '{}'. Valid values are: {}", + classification, + VALID_CLASSIFICATIONS.join(", ") + ), + )); + } + } + return_result(problems) } diff --git a/profile-opentype/src/checks/opentype/STAT/ital_axis.rs b/profile-opentype/src/checks/opentype/STAT/ital_axis.rs index 24fbdb06..d770ecb1 100644 --- a/profile-opentype/src/checks/opentype/STAT/ital_axis.rs +++ b/profile-opentype/src/checks/opentype/STAT/ital_axis.rs @@ -1,29 +1,39 @@ -use fontations::skrifa::raw::{ - tables::stat::{AxisValue, AxisValueTableFlags}, - ReadError, TableProvider, +use fontations::skrifa::MetadataProvider; +use fontations::{ + skrifa::raw::{ + tables::stat::{AxisValue, AxisValueTableFlags}, + ReadError, TableProvider, + }, + types::NameId, }; use fontspector_checkapi::{prelude::*, FileTypeConvert, TestFont}; -fn segment_vf_collection(fonts: Vec) -> Vec<(Option, Option)> { +fn segment_collection(fonts: Vec) -> Vec<(Option, Option)> { let mut roman_italic = vec![]; let (italics, mut non_italics): (Vec<_>, Vec<_>) = fonts .into_iter() - .partition(|f| f.filename.to_str().unwrap_or_default().contains("-Italic[")); + .partition(|f| f.filename.to_str().unwrap_or_default().contains("Italic")); for italic in italics.into_iter() { // Find a matching roman - let suspected_roman = italic - .filename - .to_str() - .unwrap_or_default() - .replace("-Italic[", "["); - if let Some(index) = non_italics - .iter() - .position(|f| f.filename.to_str().unwrap_or_default() == suspected_roman) + if let Some(name) = italic + .font() + .localized_strings(NameId::FAMILY_NAME) + .english_or_first() { - let roman = non_italics.swap_remove(index); - roman_italic.push((Some(roman), Some(italic))); - } else { - roman_italic.push((None, Some(italic))); + let suspected_roman_family_name = name.to_string(); + if let Some(index) = non_italics.iter().position(|f| { + f.font() + .localized_strings(NameId::FAMILY_NAME) + .english_or_first() + .expect("No Family Name") + .to_string() + == suspected_roman_family_name + }) { + let roman = non_italics.swap_remove(index); + roman_italic.push((Some(roman), Some(italic))); + } else { + roman_italic.push((None, Some(italic))); + } } } // Now add all the remaining non-italic fonts @@ -190,7 +200,7 @@ fn check_ital_is_binary_and_last(t: &TestFont, is_italic: bool) -> Result CheckFnResult { let fonts = TTF.from_collection(c); + for font in fonts.iter() { + if font.has_table(b"gvar") && !font.has_table(b"STAT") { + // variable font must have a STAT table + return Ok(Status::just_one_fail( + "no-stat-table", + &format!("Variable font is missing the 'STAT' table."), + )); + } else if !font.has_table(b"gvar") && !font.has_table(b"STAT") { + // static font is recommend to have a STAT table + return Ok(Status::just_one_warn( + "no-stat-table", + &format!("Static font is missing the 'STAT' table."), + )); + } + } let mut problems = vec![]; - for pair in segment_vf_collection(fonts).into_iter() { + for pair in segment_collection(fonts).into_iter() { + print!("Pair: {:?}\n", pair); match pair { (Some(roman), Some(italic)) => { // These should definitely both have an ital axis @@ -241,3 +267,97 @@ fn ital_axis(c: &TestableCollection, _context: &Context) -> CheckFnResult { } return_result(problems) } + +#[cfg(test)] +mod tests { + #![allow(clippy::unwrap_used)] + + use super::*; + use fontspector_checkapi::StatusCode; + use fontspector_checkapi::{Testable, TestableType}; + + use fontspector_checkapi::codetesting::{ + assert_results_contain, run_check_with_config, test_able, + }; + use std::collections::HashMap; + + #[test] + fn test_segment_collection_var() { + let testable_1 = test_able("notosans/NotoSans-VariableFont_wdth,wght.ttf"); + let testable_2 = test_able("notosans/NotoSans-Italic-VariableFont_wdth,wght.ttf"); + let testables: Vec = vec![testable_1, testable_2]; + let collection = TestableCollection { + testables, + directory: "".to_string(), + }; + let fonts = TTF.from_collection(&collection); + let pairs = segment_collection(fonts); + assert_eq!(pairs.len(), 1); + for (roman, italic) in pairs.into_iter() { + assert!(roman.is_some()); + assert!(italic.is_some()); + } + } + + #[test] + fn test_segment_collection_static() { + let testable_1 = test_able("montserrat/Montserrat-Regular.ttf"); + let testable_2 = test_able("montserrat/Montserrat-Italic.ttf"); + let testable_3 = test_able("montserrat/Montserrat-Bold.ttf"); + let testable_4 = test_able("montserrat/Montserrat-BoldItalic.ttf"); + let testable_5 = test_able("montserrat/Montserrat-Light.ttf"); + let testable_6 = test_able("montserrat/Montserrat-LightItalic.ttf"); + let testables: Vec = vec![ + testable_1, testable_2, testable_3, testable_4, testable_5, testable_6, + ]; + let collection = TestableCollection { + testables, + directory: "".to_string(), + }; + let fonts = TTF.from_collection(&collection); + let pairs = segment_collection(fonts); + assert_eq!(pairs.len(), 3); + for (roman, italic) in pairs.into_iter() { + assert!(roman.is_some()); + assert!(italic.is_some()); + } + } + + #[test] + fn test_ital_axis_static_fonts_missing_stat() { + let testable_1 = test_able("montserrat/Montserrat-Regular.ttf"); + let testable_2 = test_able("montserrat/Montserrat-Italic.ttf"); + let testables: Vec = vec![testable_1, testable_2]; + let collection = TestableCollection { + testables, + directory: "".to_string(), + }; + let results = run_check_with_config( + ital_axis, + TestableType::Collection(&collection), + HashMap::new(), + ); + assert_results_contain( + &results, + StatusCode::Warn, + Some("no-stat-table".to_string()), + ); + } + + #[test] + fn test_ital_axis_skip_static_fonts() { + let testable_1 = test_able("notosans/static/NotoSans-Black.ttf"); + let testable_2 = test_able("notosans/static/NotoSans-BlackItalic.ttf"); + let testables: Vec = vec![testable_1, testable_2]; + let collection = TestableCollection { + testables, + directory: "".to_string(), + }; + let results = run_check_with_config( + ital_axis, + TestableType::Collection(&collection), + HashMap::new(), + ); + assert_results_contain(&results, StatusCode::Pass, None); + } +} diff --git a/profile-universal/src/checks/mod.rs b/profile-universal/src/checks/mod.rs index 7991a49a..c378d149 100644 --- a/profile-universal/src/checks/mod.rs +++ b/profile-universal/src/checks/mod.rs @@ -44,6 +44,7 @@ mod missing_small_caps_glyphs; mod nested_components; mod no_mac_entries; mod os2_metrics_match_hhea; +mod required_name_ids; mod required_tables; mod rupee; mod sfnt_version; @@ -114,6 +115,7 @@ pub use missing_small_caps_glyphs::missing_small_caps_glyphs; pub use nested_components::nested_components; pub use no_mac_entries::no_mac_entries; pub use os2_metrics_match_hhea::os2_metrics_match_hhea; +pub use required_name_ids::required_name_ids; pub use required_tables::required_tables; pub use rupee::rupee; pub use sfnt_version::sfnt_version; diff --git a/profile-universal/src/checks/required_name_ids.rs b/profile-universal/src/checks/required_name_ids.rs new file mode 100644 index 00000000..93ce0d7e --- /dev/null +++ b/profile-universal/src/checks/required_name_ids.rs @@ -0,0 +1,129 @@ +use fontations::skrifa::string::StringId; +use fontspector_checkapi::{ + get_name_entry_string, get_name_platform_tuples, prelude::*, testfont, FileTypeConvert, + PlatformSelector, +}; + +use std::vec; + +#[check( + id = "universal/required_name_ids", + rationale = " + Check required name ids based on given list. + ", + title = "Required name ids in name table" +)] +fn required_name_ids(t: &Testable, context: &Context) -> CheckFnResult { + print!("required_name_ids check running..."); + let font = testfont!(t); + if !font.has_table(b"name") { + return Ok(Status::just_one_fail("lacks-table", "No name table.")); + } + + let config = context.local_config("universal/required_name_ids"); + let required_ids: Vec = config + .get("required_name_ids") + .ok_or(FontspectorError::skip( + "no-required-name-ids", + "Add the `required_name_ids` key to a `fontspector.toml` file.", + ))? + .as_array() + .ok_or(FontspectorError::skip( + "invalid-required-name-ids", + "The `required_name_ids` key in the configuration file must be an array.", + ))? + .iter() + .filter_map(|v| v.as_u64().map(|n| n as u16)) + .collect(); + + let mut bad_names: Vec = vec![]; + + let platform_tuples = get_name_platform_tuples(font.font()); + for platform_tuple in platform_tuples { + let mut missing_name_ids: Vec<_> = vec![]; + for id in required_ids.clone().into_iter() { + let selector = PlatformSelector { + platform_id: platform_tuple.0, + encoding_id: platform_tuple.1, + language_id: platform_tuple.2, + }; + let name_id = StringId::from(id); + if let Some(_name_string) = get_name_entry_string(&font.font(), selector, name_id) { + continue; + } else { + if id == 25 && !font.is_variable_font() { + // Skip Variations PostScript Name Prefix if not a variable font + continue; + } + missing_name_ids.push(id); + } + } + if !missing_name_ids.is_empty() { + bad_names.push(format!( + "Missing required name IDs {missing_name_ids:?} for {platform_tuple:?}.", + )); + } + } + + Ok(if bad_names.is_empty() { + Status::just_one_pass() + } else { + Status::just_one_fail( + "missing-name-table-ids", + &format!( + "The following issues have been found:\n\n{}", + bullet_list(context, bad_names) + ), + ) + }) +} + +#[cfg(test)] +mod tests { + #![allow(clippy::unwrap_used, clippy::expect_used)] + + use super::*; + use fontspector_checkapi::StatusCode; + use serde_json::json; + use std::collections::HashMap; + + use fontspector_checkapi::codetesting::{ + assert_messages_contain, assert_pass, assert_results_contain, run_check_with_config, + test_able, + }; + + #[test] + fn test_required_name_ids_fail() { + let conf = HashMap::from([( + "required_name_ids".to_string(), + serde_json::json!(json!([0, 1, 1234])), + )]); + let testable = test_able("varfont/inter/Inter[slnt,wght].ttf"); + let results = run_check_with_config( + super::required_name_ids, + TestableType::Single(&testable), + conf, + ); + assert_messages_contain(&results, "Missing required name IDs [1234]"); + assert_results_contain( + &results, + StatusCode::Fail, + Some("missing-name-table-ids".to_string()), + ); + } + + #[test] + fn test_required_name_ids_pass() { + let conf = HashMap::from([( + "required_name_ids".to_string(), + serde_json::json!(json!([260])), // name ID exists in all platforms + )]); + let testable = test_able("varfont/inter/Inter[slnt,wght].ttf"); + let results = run_check_with_config( + super::required_name_ids, + TestableType::Single(&testable), + conf, + ); + assert_pass(&results); + } +} diff --git a/profile-universal/src/lib.rs b/profile-universal/src/lib.rs index 0ea3a062..37c99f42 100644 --- a/profile-universal/src/lib.rs +++ b/profile-universal/src/lib.rs @@ -97,6 +97,7 @@ impl fontspector_checkapi::Plugin for Universal { .add_and_register_check(checks::has_features) .add_and_register_check(checks::has_glyphs) .add_and_register_check(checks::has_unicodes) + .add_and_register_check(checks::required_name_ids) .build("universal", cr) // Checks which don't make sense any more diff --git a/resources/test/notosans/NotoSans-Italic-VariableFont_wdth,wght.ttf b/resources/test/notosans/NotoSans-Italic-VariableFont_wdth,wght.ttf new file mode 100644 index 00000000..6245ba01 Binary files /dev/null and b/resources/test/notosans/NotoSans-Italic-VariableFont_wdth,wght.ttf differ diff --git a/resources/test/notosans/NotoSans-VariableFont_wdth,wght.ttf b/resources/test/notosans/NotoSans-VariableFont_wdth,wght.ttf new file mode 100644 index 00000000..9530d84d Binary files /dev/null and b/resources/test/notosans/NotoSans-VariableFont_wdth,wght.ttf differ diff --git a/resources/test/notosans/static/NotoSans-Black.ttf b/resources/test/notosans/static/NotoSans-Black.ttf new file mode 100644 index 00000000..d5a6e0d1 Binary files /dev/null and b/resources/test/notosans/static/NotoSans-Black.ttf differ diff --git a/resources/test/notosans/static/NotoSans-BlackItalic.ttf b/resources/test/notosans/static/NotoSans-BlackItalic.ttf new file mode 100644 index 00000000..dfc640c3 Binary files /dev/null and b/resources/test/notosans/static/NotoSans-BlackItalic.ttf differ diff --git a/resources/test/notosans/static/NotoSans-Bold.ttf b/resources/test/notosans/static/NotoSans-Bold.ttf new file mode 100644 index 00000000..506f7d84 Binary files /dev/null and b/resources/test/notosans/static/NotoSans-Bold.ttf differ diff --git a/resources/test/notosans/static/NotoSans-BoldItalic.ttf b/resources/test/notosans/static/NotoSans-BoldItalic.ttf new file mode 100644 index 00000000..0e8fa4bd Binary files /dev/null and b/resources/test/notosans/static/NotoSans-BoldItalic.ttf differ diff --git a/resources/test/notosans/static/NotoSans-ExtraBold.ttf b/resources/test/notosans/static/NotoSans-ExtraBold.ttf new file mode 100644 index 00000000..58684467 Binary files /dev/null and b/resources/test/notosans/static/NotoSans-ExtraBold.ttf differ diff --git a/resources/test/notosans/static/NotoSans-ExtraBoldItalic.ttf b/resources/test/notosans/static/NotoSans-ExtraBoldItalic.ttf new file mode 100644 index 00000000..68abd4c0 Binary files /dev/null and b/resources/test/notosans/static/NotoSans-ExtraBoldItalic.ttf differ diff --git a/resources/test/notosans/static/NotoSans-ExtraLight.ttf b/resources/test/notosans/static/NotoSans-ExtraLight.ttf new file mode 100644 index 00000000..078f8dc8 Binary files /dev/null and b/resources/test/notosans/static/NotoSans-ExtraLight.ttf differ diff --git a/resources/test/notosans/static/NotoSans-ExtraLightItalic.ttf b/resources/test/notosans/static/NotoSans-ExtraLightItalic.ttf new file mode 100644 index 00000000..acaa4664 Binary files /dev/null and b/resources/test/notosans/static/NotoSans-ExtraLightItalic.ttf differ diff --git a/resources/test/notosans/static/NotoSans-Italic.ttf b/resources/test/notosans/static/NotoSans-Italic.ttf new file mode 100644 index 00000000..d9b9e148 Binary files /dev/null and b/resources/test/notosans/static/NotoSans-Italic.ttf differ diff --git a/resources/test/notosans/static/NotoSans-Light.ttf b/resources/test/notosans/static/NotoSans-Light.ttf new file mode 100644 index 00000000..8d8a6789 Binary files /dev/null and b/resources/test/notosans/static/NotoSans-Light.ttf differ diff --git a/resources/test/notosans/static/NotoSans-LightItalic.ttf b/resources/test/notosans/static/NotoSans-LightItalic.ttf new file mode 100644 index 00000000..0ab65c0d Binary files /dev/null and b/resources/test/notosans/static/NotoSans-LightItalic.ttf differ diff --git a/resources/test/notosans/static/NotoSans-Medium.ttf b/resources/test/notosans/static/NotoSans-Medium.ttf new file mode 100644 index 00000000..a44124bb Binary files /dev/null and b/resources/test/notosans/static/NotoSans-Medium.ttf differ diff --git a/resources/test/notosans/static/NotoSans-MediumItalic.ttf b/resources/test/notosans/static/NotoSans-MediumItalic.ttf new file mode 100644 index 00000000..467af1b3 Binary files /dev/null and b/resources/test/notosans/static/NotoSans-MediumItalic.ttf differ diff --git a/resources/test/notosans/static/NotoSans-Regular.ttf b/resources/test/notosans/static/NotoSans-Regular.ttf new file mode 100644 index 00000000..4bac02f2 Binary files /dev/null and b/resources/test/notosans/static/NotoSans-Regular.ttf differ diff --git a/resources/test/notosans/static/NotoSans-SemiBold.ttf b/resources/test/notosans/static/NotoSans-SemiBold.ttf new file mode 100644 index 00000000..e8467490 Binary files /dev/null and b/resources/test/notosans/static/NotoSans-SemiBold.ttf differ diff --git a/resources/test/notosans/static/NotoSans-SemiBoldItalic.ttf b/resources/test/notosans/static/NotoSans-SemiBoldItalic.ttf new file mode 100644 index 00000000..cacc7ece Binary files /dev/null and b/resources/test/notosans/static/NotoSans-SemiBoldItalic.ttf differ diff --git a/resources/test/notosans/static/NotoSans-Thin.ttf b/resources/test/notosans/static/NotoSans-Thin.ttf new file mode 100644 index 00000000..04335a5b Binary files /dev/null and b/resources/test/notosans/static/NotoSans-Thin.ttf differ diff --git a/resources/test/notosans/static/NotoSans-ThinItalic.ttf b/resources/test/notosans/static/NotoSans-ThinItalic.ttf new file mode 100644 index 00000000..910dfc74 Binary files /dev/null and b/resources/test/notosans/static/NotoSans-ThinItalic.ttf differ diff --git a/resources/test/notosans/static/NotoSans_Condensed-Black.ttf b/resources/test/notosans/static/NotoSans_Condensed-Black.ttf new file mode 100644 index 00000000..31866995 Binary files /dev/null and b/resources/test/notosans/static/NotoSans_Condensed-Black.ttf differ diff --git a/resources/test/notosans/static/NotoSans_Condensed-BlackItalic.ttf b/resources/test/notosans/static/NotoSans_Condensed-BlackItalic.ttf new file mode 100644 index 00000000..d4b19bc1 Binary files /dev/null and b/resources/test/notosans/static/NotoSans_Condensed-BlackItalic.ttf differ diff --git a/resources/test/notosans/static/NotoSans_Condensed-Bold.ttf b/resources/test/notosans/static/NotoSans_Condensed-Bold.ttf new file mode 100644 index 00000000..1ce44bce Binary files /dev/null and b/resources/test/notosans/static/NotoSans_Condensed-Bold.ttf differ diff --git a/resources/test/notosans/static/NotoSans_Condensed-BoldItalic.ttf b/resources/test/notosans/static/NotoSans_Condensed-BoldItalic.ttf new file mode 100644 index 00000000..1960e68a Binary files /dev/null and b/resources/test/notosans/static/NotoSans_Condensed-BoldItalic.ttf differ diff --git a/resources/test/notosans/static/NotoSans_Condensed-ExtraBold.ttf b/resources/test/notosans/static/NotoSans_Condensed-ExtraBold.ttf new file mode 100644 index 00000000..cb36919a Binary files /dev/null and b/resources/test/notosans/static/NotoSans_Condensed-ExtraBold.ttf differ diff --git a/resources/test/notosans/static/NotoSans_Condensed-ExtraBoldItalic.ttf b/resources/test/notosans/static/NotoSans_Condensed-ExtraBoldItalic.ttf new file mode 100644 index 00000000..7bbea17b Binary files /dev/null and b/resources/test/notosans/static/NotoSans_Condensed-ExtraBoldItalic.ttf differ diff --git a/resources/test/notosans/static/NotoSans_Condensed-ExtraLight.ttf b/resources/test/notosans/static/NotoSans_Condensed-ExtraLight.ttf new file mode 100644 index 00000000..29a77513 Binary files /dev/null and b/resources/test/notosans/static/NotoSans_Condensed-ExtraLight.ttf differ diff --git a/resources/test/notosans/static/NotoSans_Condensed-ExtraLightItalic.ttf b/resources/test/notosans/static/NotoSans_Condensed-ExtraLightItalic.ttf new file mode 100644 index 00000000..983b81ab Binary files /dev/null and b/resources/test/notosans/static/NotoSans_Condensed-ExtraLightItalic.ttf differ diff --git a/resources/test/notosans/static/NotoSans_Condensed-Italic.ttf b/resources/test/notosans/static/NotoSans_Condensed-Italic.ttf new file mode 100644 index 00000000..8e2d1f83 Binary files /dev/null and b/resources/test/notosans/static/NotoSans_Condensed-Italic.ttf differ diff --git a/resources/test/notosans/static/NotoSans_Condensed-Light.ttf b/resources/test/notosans/static/NotoSans_Condensed-Light.ttf new file mode 100644 index 00000000..32c58a58 Binary files /dev/null and b/resources/test/notosans/static/NotoSans_Condensed-Light.ttf differ diff --git a/resources/test/notosans/static/NotoSans_Condensed-LightItalic.ttf b/resources/test/notosans/static/NotoSans_Condensed-LightItalic.ttf new file mode 100644 index 00000000..c5d1b451 Binary files /dev/null and b/resources/test/notosans/static/NotoSans_Condensed-LightItalic.ttf differ diff --git a/resources/test/notosans/static/NotoSans_Condensed-Medium.ttf b/resources/test/notosans/static/NotoSans_Condensed-Medium.ttf new file mode 100644 index 00000000..45f8ea45 Binary files /dev/null and b/resources/test/notosans/static/NotoSans_Condensed-Medium.ttf differ diff --git a/resources/test/notosans/static/NotoSans_Condensed-MediumItalic.ttf b/resources/test/notosans/static/NotoSans_Condensed-MediumItalic.ttf new file mode 100644 index 00000000..92cd88a5 Binary files /dev/null and b/resources/test/notosans/static/NotoSans_Condensed-MediumItalic.ttf differ diff --git a/resources/test/notosans/static/NotoSans_Condensed-Regular.ttf b/resources/test/notosans/static/NotoSans_Condensed-Regular.ttf new file mode 100644 index 00000000..3ad9a1b8 Binary files /dev/null and b/resources/test/notosans/static/NotoSans_Condensed-Regular.ttf differ diff --git a/resources/test/notosans/static/NotoSans_Condensed-SemiBold.ttf b/resources/test/notosans/static/NotoSans_Condensed-SemiBold.ttf new file mode 100644 index 00000000..2f20a216 Binary files /dev/null and b/resources/test/notosans/static/NotoSans_Condensed-SemiBold.ttf differ diff --git a/resources/test/notosans/static/NotoSans_Condensed-SemiBoldItalic.ttf b/resources/test/notosans/static/NotoSans_Condensed-SemiBoldItalic.ttf new file mode 100644 index 00000000..b28147d3 Binary files /dev/null and b/resources/test/notosans/static/NotoSans_Condensed-SemiBoldItalic.ttf differ diff --git a/resources/test/notosans/static/NotoSans_Condensed-Thin.ttf b/resources/test/notosans/static/NotoSans_Condensed-Thin.ttf new file mode 100644 index 00000000..d5b50b57 Binary files /dev/null and b/resources/test/notosans/static/NotoSans_Condensed-Thin.ttf differ diff --git a/resources/test/notosans/static/NotoSans_Condensed-ThinItalic.ttf b/resources/test/notosans/static/NotoSans_Condensed-ThinItalic.ttf new file mode 100644 index 00000000..00d93157 Binary files /dev/null and b/resources/test/notosans/static/NotoSans_Condensed-ThinItalic.ttf differ diff --git a/resources/test/notosans/static/NotoSans_ExtraCondensed-Black.ttf b/resources/test/notosans/static/NotoSans_ExtraCondensed-Black.ttf new file mode 100644 index 00000000..619c4f8f Binary files /dev/null and b/resources/test/notosans/static/NotoSans_ExtraCondensed-Black.ttf differ diff --git a/resources/test/notosans/static/NotoSans_ExtraCondensed-BlackItalic.ttf b/resources/test/notosans/static/NotoSans_ExtraCondensed-BlackItalic.ttf new file mode 100644 index 00000000..f1246272 Binary files /dev/null and b/resources/test/notosans/static/NotoSans_ExtraCondensed-BlackItalic.ttf differ diff --git a/resources/test/notosans/static/NotoSans_ExtraCondensed-Bold.ttf b/resources/test/notosans/static/NotoSans_ExtraCondensed-Bold.ttf new file mode 100644 index 00000000..11e46997 Binary files /dev/null and b/resources/test/notosans/static/NotoSans_ExtraCondensed-Bold.ttf differ diff --git a/resources/test/notosans/static/NotoSans_ExtraCondensed-BoldItalic.ttf b/resources/test/notosans/static/NotoSans_ExtraCondensed-BoldItalic.ttf new file mode 100644 index 00000000..81ec21fa Binary files /dev/null and b/resources/test/notosans/static/NotoSans_ExtraCondensed-BoldItalic.ttf differ diff --git a/resources/test/notosans/static/NotoSans_ExtraCondensed-ExtraBold.ttf b/resources/test/notosans/static/NotoSans_ExtraCondensed-ExtraBold.ttf new file mode 100644 index 00000000..2ce3cb31 Binary files /dev/null and b/resources/test/notosans/static/NotoSans_ExtraCondensed-ExtraBold.ttf differ diff --git a/resources/test/notosans/static/NotoSans_ExtraCondensed-ExtraBoldItalic.ttf b/resources/test/notosans/static/NotoSans_ExtraCondensed-ExtraBoldItalic.ttf new file mode 100644 index 00000000..98929675 Binary files /dev/null and b/resources/test/notosans/static/NotoSans_ExtraCondensed-ExtraBoldItalic.ttf differ diff --git a/resources/test/notosans/static/NotoSans_ExtraCondensed-ExtraLight.ttf b/resources/test/notosans/static/NotoSans_ExtraCondensed-ExtraLight.ttf new file mode 100644 index 00000000..ce67cb11 Binary files /dev/null and b/resources/test/notosans/static/NotoSans_ExtraCondensed-ExtraLight.ttf differ diff --git a/resources/test/notosans/static/NotoSans_ExtraCondensed-ExtraLightItalic.ttf b/resources/test/notosans/static/NotoSans_ExtraCondensed-ExtraLightItalic.ttf new file mode 100644 index 00000000..45726c35 Binary files /dev/null and b/resources/test/notosans/static/NotoSans_ExtraCondensed-ExtraLightItalic.ttf differ diff --git a/resources/test/notosans/static/NotoSans_ExtraCondensed-Italic.ttf b/resources/test/notosans/static/NotoSans_ExtraCondensed-Italic.ttf new file mode 100644 index 00000000..e6b1a73e Binary files /dev/null and b/resources/test/notosans/static/NotoSans_ExtraCondensed-Italic.ttf differ diff --git a/resources/test/notosans/static/NotoSans_ExtraCondensed-Light.ttf b/resources/test/notosans/static/NotoSans_ExtraCondensed-Light.ttf new file mode 100644 index 00000000..5e9fef86 Binary files /dev/null and b/resources/test/notosans/static/NotoSans_ExtraCondensed-Light.ttf differ diff --git a/resources/test/notosans/static/NotoSans_ExtraCondensed-LightItalic.ttf b/resources/test/notosans/static/NotoSans_ExtraCondensed-LightItalic.ttf new file mode 100644 index 00000000..500c9196 Binary files /dev/null and b/resources/test/notosans/static/NotoSans_ExtraCondensed-LightItalic.ttf differ diff --git a/resources/test/notosans/static/NotoSans_ExtraCondensed-Medium.ttf b/resources/test/notosans/static/NotoSans_ExtraCondensed-Medium.ttf new file mode 100644 index 00000000..c78465e7 Binary files /dev/null and b/resources/test/notosans/static/NotoSans_ExtraCondensed-Medium.ttf differ diff --git a/resources/test/notosans/static/NotoSans_ExtraCondensed-MediumItalic.ttf b/resources/test/notosans/static/NotoSans_ExtraCondensed-MediumItalic.ttf new file mode 100644 index 00000000..527291a3 Binary files /dev/null and b/resources/test/notosans/static/NotoSans_ExtraCondensed-MediumItalic.ttf differ diff --git a/resources/test/notosans/static/NotoSans_ExtraCondensed-Regular.ttf b/resources/test/notosans/static/NotoSans_ExtraCondensed-Regular.ttf new file mode 100644 index 00000000..8921daa1 Binary files /dev/null and b/resources/test/notosans/static/NotoSans_ExtraCondensed-Regular.ttf differ diff --git a/resources/test/notosans/static/NotoSans_ExtraCondensed-SemiBold.ttf b/resources/test/notosans/static/NotoSans_ExtraCondensed-SemiBold.ttf new file mode 100644 index 00000000..83b98b27 Binary files /dev/null and b/resources/test/notosans/static/NotoSans_ExtraCondensed-SemiBold.ttf differ diff --git a/resources/test/notosans/static/NotoSans_ExtraCondensed-SemiBoldItalic.ttf b/resources/test/notosans/static/NotoSans_ExtraCondensed-SemiBoldItalic.ttf new file mode 100644 index 00000000..9dedf3ef Binary files /dev/null and b/resources/test/notosans/static/NotoSans_ExtraCondensed-SemiBoldItalic.ttf differ diff --git a/resources/test/notosans/static/NotoSans_ExtraCondensed-Thin.ttf b/resources/test/notosans/static/NotoSans_ExtraCondensed-Thin.ttf new file mode 100644 index 00000000..81e2bf94 Binary files /dev/null and b/resources/test/notosans/static/NotoSans_ExtraCondensed-Thin.ttf differ diff --git a/resources/test/notosans/static/NotoSans_ExtraCondensed-ThinItalic.ttf b/resources/test/notosans/static/NotoSans_ExtraCondensed-ThinItalic.ttf new file mode 100644 index 00000000..17b43b19 Binary files /dev/null and b/resources/test/notosans/static/NotoSans_ExtraCondensed-ThinItalic.ttf differ diff --git a/resources/test/notosans/static/NotoSans_SemiCondensed-Black.ttf b/resources/test/notosans/static/NotoSans_SemiCondensed-Black.ttf new file mode 100644 index 00000000..5a141da4 Binary files /dev/null and b/resources/test/notosans/static/NotoSans_SemiCondensed-Black.ttf differ diff --git a/resources/test/notosans/static/NotoSans_SemiCondensed-BlackItalic.ttf b/resources/test/notosans/static/NotoSans_SemiCondensed-BlackItalic.ttf new file mode 100644 index 00000000..538888da Binary files /dev/null and b/resources/test/notosans/static/NotoSans_SemiCondensed-BlackItalic.ttf differ diff --git a/resources/test/notosans/static/NotoSans_SemiCondensed-Bold.ttf b/resources/test/notosans/static/NotoSans_SemiCondensed-Bold.ttf new file mode 100644 index 00000000..9cff468e Binary files /dev/null and b/resources/test/notosans/static/NotoSans_SemiCondensed-Bold.ttf differ diff --git a/resources/test/notosans/static/NotoSans_SemiCondensed-BoldItalic.ttf b/resources/test/notosans/static/NotoSans_SemiCondensed-BoldItalic.ttf new file mode 100644 index 00000000..314024cd Binary files /dev/null and b/resources/test/notosans/static/NotoSans_SemiCondensed-BoldItalic.ttf differ diff --git a/resources/test/notosans/static/NotoSans_SemiCondensed-ExtraBold.ttf b/resources/test/notosans/static/NotoSans_SemiCondensed-ExtraBold.ttf new file mode 100644 index 00000000..c50c0814 Binary files /dev/null and b/resources/test/notosans/static/NotoSans_SemiCondensed-ExtraBold.ttf differ diff --git a/resources/test/notosans/static/NotoSans_SemiCondensed-ExtraBoldItalic.ttf b/resources/test/notosans/static/NotoSans_SemiCondensed-ExtraBoldItalic.ttf new file mode 100644 index 00000000..b8b053e8 Binary files /dev/null and b/resources/test/notosans/static/NotoSans_SemiCondensed-ExtraBoldItalic.ttf differ diff --git a/resources/test/notosans/static/NotoSans_SemiCondensed-ExtraLight.ttf b/resources/test/notosans/static/NotoSans_SemiCondensed-ExtraLight.ttf new file mode 100644 index 00000000..6450a04b Binary files /dev/null and b/resources/test/notosans/static/NotoSans_SemiCondensed-ExtraLight.ttf differ diff --git a/resources/test/notosans/static/NotoSans_SemiCondensed-ExtraLightItalic.ttf b/resources/test/notosans/static/NotoSans_SemiCondensed-ExtraLightItalic.ttf new file mode 100644 index 00000000..a655d1e7 Binary files /dev/null and b/resources/test/notosans/static/NotoSans_SemiCondensed-ExtraLightItalic.ttf differ diff --git a/resources/test/notosans/static/NotoSans_SemiCondensed-Italic.ttf b/resources/test/notosans/static/NotoSans_SemiCondensed-Italic.ttf new file mode 100644 index 00000000..67c7a2f7 Binary files /dev/null and b/resources/test/notosans/static/NotoSans_SemiCondensed-Italic.ttf differ diff --git a/resources/test/notosans/static/NotoSans_SemiCondensed-Light.ttf b/resources/test/notosans/static/NotoSans_SemiCondensed-Light.ttf new file mode 100644 index 00000000..f9221c3d Binary files /dev/null and b/resources/test/notosans/static/NotoSans_SemiCondensed-Light.ttf differ diff --git a/resources/test/notosans/static/NotoSans_SemiCondensed-LightItalic.ttf b/resources/test/notosans/static/NotoSans_SemiCondensed-LightItalic.ttf new file mode 100644 index 00000000..9a722003 Binary files /dev/null and b/resources/test/notosans/static/NotoSans_SemiCondensed-LightItalic.ttf differ diff --git a/resources/test/notosans/static/NotoSans_SemiCondensed-Medium.ttf b/resources/test/notosans/static/NotoSans_SemiCondensed-Medium.ttf new file mode 100644 index 00000000..e2c825c4 Binary files /dev/null and b/resources/test/notosans/static/NotoSans_SemiCondensed-Medium.ttf differ diff --git a/resources/test/notosans/static/NotoSans_SemiCondensed-MediumItalic.ttf b/resources/test/notosans/static/NotoSans_SemiCondensed-MediumItalic.ttf new file mode 100644 index 00000000..6be577a4 Binary files /dev/null and b/resources/test/notosans/static/NotoSans_SemiCondensed-MediumItalic.ttf differ diff --git a/resources/test/notosans/static/NotoSans_SemiCondensed-Regular.ttf b/resources/test/notosans/static/NotoSans_SemiCondensed-Regular.ttf new file mode 100644 index 00000000..06a2982d Binary files /dev/null and b/resources/test/notosans/static/NotoSans_SemiCondensed-Regular.ttf differ diff --git a/resources/test/notosans/static/NotoSans_SemiCondensed-SemiBold.ttf b/resources/test/notosans/static/NotoSans_SemiCondensed-SemiBold.ttf new file mode 100644 index 00000000..8c8f313f Binary files /dev/null and b/resources/test/notosans/static/NotoSans_SemiCondensed-SemiBold.ttf differ diff --git a/resources/test/notosans/static/NotoSans_SemiCondensed-SemiBoldItalic.ttf b/resources/test/notosans/static/NotoSans_SemiCondensed-SemiBoldItalic.ttf new file mode 100644 index 00000000..59093a98 Binary files /dev/null and b/resources/test/notosans/static/NotoSans_SemiCondensed-SemiBoldItalic.ttf differ diff --git a/resources/test/notosans/static/NotoSans_SemiCondensed-Thin.ttf b/resources/test/notosans/static/NotoSans_SemiCondensed-Thin.ttf new file mode 100644 index 00000000..7d7ef338 Binary files /dev/null and b/resources/test/notosans/static/NotoSans_SemiCondensed-Thin.ttf differ diff --git a/resources/test/notosans/static/NotoSans_SemiCondensed-ThinItalic.ttf b/resources/test/notosans/static/NotoSans_SemiCondensed-ThinItalic.ttf new file mode 100644 index 00000000..44084d99 Binary files /dev/null and b/resources/test/notosans/static/NotoSans_SemiCondensed-ThinItalic.ttf differ