Skip to content

Commit 6264892

Browse files
committed
Implement three more checks
1 parent 4fdc7c5 commit 6264892

File tree

6 files changed

+263
-74
lines changed

6 files changed

+263
-74
lines changed

fontspector-checkapi/src/constants.rs

+21-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,24 @@
1-
// pub const RIBBI_STYLE_NAMES: [&str; 5] = ["Regular", "Italic", "Bold", "BoldItalic", "Bold Italic"];
1+
pub const RIBBI_STYLE_NAMES: [&str; 5] = ["Regular", "Italic", "Bold", "BoldItalic", "Bold Italic"];
2+
pub const STATIC_STYLE_NAMES: [&str; 18] = [
3+
"Thin",
4+
"ExtraLight",
5+
"Light",
6+
"Regular",
7+
"Medium",
8+
"SemiBold",
9+
"Bold",
10+
"ExtraBold",
11+
"Black",
12+
"Thin Italic",
13+
"ExtraLight Italic",
14+
"Light Italic",
15+
"Italic",
16+
"Medium Italic",
17+
"SemiBold Italic",
18+
"Bold Italic",
19+
"ExtraBold Italic",
20+
"Black Italic",
21+
];
222

323
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
424
pub enum GlyphClass {

fontspector-checkapi/src/font.rs

+51-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
use crate::{constants::GlyphClass, filetype::FileTypeConvert, CheckError, FileType, Testable};
1+
use crate::{
2+
constants::{GlyphClass, RIBBI_STYLE_NAMES, STATIC_STYLE_NAMES},
3+
filetype::FileTypeConvert,
4+
CheckError, FileType, Testable,
5+
};
26
use read_fonts::{
37
tables::cmap::Cmap,
48
tables::gdef::Gdef,
@@ -7,7 +11,6 @@ use read_fonts::{
711
TableProvider,
812
};
913
use skrifa::{
10-
charmap::Charmap,
1114
font::FontRef,
1215
string::{LocalizedStrings, StringId},
1316
GlyphId, MetadataProvider, Tag,
@@ -61,7 +64,33 @@ impl TestFont<'_> {
6164
}
6265

6366
pub fn style(&self) -> Option<&str> {
64-
Some("Regular")
67+
if let Some(default_location) = self.default_location() {
68+
if default_location.get("wght") == Some(&700.0) {
69+
if self.filename.to_str()?.contains("Italic") {
70+
return Some("BoldItalic");
71+
} else {
72+
return Some("Bold");
73+
}
74+
} else {
75+
if self.filename.to_str()?.contains("Italic") {
76+
return Some("Italic");
77+
}
78+
return Some("Regular");
79+
}
80+
}
81+
if let Some(style_part) = self.filename.file_stem()?.to_str()?.split('-').last() {
82+
for styles in STATIC_STYLE_NAMES.iter() {
83+
if style_part == styles.replace(" ", "") {
84+
return Some(style_part);
85+
}
86+
}
87+
}
88+
None
89+
}
90+
91+
pub fn is_ribbi(&self) -> bool {
92+
self.style()
93+
.map_or(false, |s| RIBBI_STYLE_NAMES.iter().any(|r| r == &s))
6594
}
6695

6796
pub fn has_table(&self, table: &[u8; 4]) -> bool {
@@ -150,8 +179,26 @@ impl TestFont<'_> {
150179
self.has_table(b"fvar")
151180
}
152181

182+
pub fn default_location(&self) -> Option<HashMap<String, f32>> {
183+
Some(
184+
self.font()
185+
.fvar()
186+
.ok()?
187+
.axes()
188+
.ok()?
189+
.iter()
190+
.map(|axis| {
191+
let tag = axis.axis_tag().to_string();
192+
let default = axis.default_value().to_f32();
193+
(tag, default)
194+
})
195+
.collect(),
196+
)
197+
}
198+
153199
pub fn codepoints(&self) -> HashSet<u32> {
154-
Charmap::new(&self.font())
200+
self.font()
201+
.charmap()
155202
.mappings()
156203
.map(|(u, _gid)| u)
157204
.collect()
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,48 @@
1-
// use std::collections::HashSet;
1+
use std::collections::HashSet;
22

3-
// use fontspector_checkapi::{return_result, Check, FontCollection, Status, StatusCode, StatusList};
4-
// use read_fonts::tables::os2::SelectionFlags;
5-
// use skrifa::string::StringId;
3+
use fontspector_checkapi::{prelude::*, FileTypeConvert};
4+
use read_fonts::tables::os2::SelectionFlags;
5+
use skrifa::string::StringId;
66

7-
// fn bold_italic_unique(c: &FontCollection) -> StatusList {
8-
// let ribbi = c.ribbi_fonts();
9-
// let mut problems = vec![];
10-
// let mut flags: HashSet<(bool, bool)> = HashSet::new();
11-
// for font in ribbi.iter() {
12-
// let _names_list = font.get_name_entry_strings(StringId::FAMILY_NAME);
13-
// match font.get_os2_fsselection() {
14-
// Ok(fsselection) => {
15-
// let val = (
16-
// fsselection.intersects(SelectionFlags::BOLD),
17-
// fsselection.intersects(SelectionFlags::ITALIC),
18-
// );
19-
// if flags.contains(&val) {
20-
// problems.push(Status {
21-
// message: Some(format!(
22-
// "Font {} has the same selection flags ({}{}{}) as another font",
23-
// font.filename,
24-
// if val.0 { "bold" } else { "" },
25-
// if val.0 && val.1 { " & " } else { "" },
26-
// if val.1 { "italic" } else { "" }
27-
// )),
28-
// code: StatusCode::Error,
29-
// });
30-
// } else {
31-
// flags.insert(val);
32-
// }
33-
// }
34-
// Err(_e) => problems.push(Status {
35-
// message: Some(format!("Font {} had no OS2 table", font.filename)),
36-
// code: StatusCode::Error,
37-
// }),
38-
// }
39-
// }
40-
// return_result(problems)
41-
// }
42-
// pub const BOLD_ITALIC_UNIQUE_CHECK: Check = Check {
43-
// id: "opentype/family/bold_italic_unique_for_nameid1",
44-
// title: "Check that OS/2.fsSelection bold & italic settings are unique for each NameID1",
45-
// rationale: None,
46-
// proposal: Some("https://github.com/googlefonts/fontbakery/pull/2388"),
47-
// check_all: Some(&bold_italic_unique),
48-
// check_one: None,
49-
// };
7+
#[check(
8+
id = "opentype/family/bold_italic_unique_for_nameid1",
9+
title = "Check that OS/2.fsSelection bold & italic settings are unique for each NameID1",
10+
rationale = "Per the OpenType spec: name ID 1 'is used in combination with Font Subfamily
11+
name (name ID 2), and should be shared among at most four fonts that differ
12+
only in weight or style.
13+
14+
This four-way distinction should also be reflected in the OS/2.fsSelection
15+
field, using bits 0 and 5.
16+
",
17+
proposal = "legacy:check/153",
18+
implementation = "all"
19+
)]
20+
fn bold_italic_unique(c: &TestableCollection, _context: &Context) -> CheckFnResult {
21+
let fonts = TTF.from_collection(c);
22+
let mut problems = vec![];
23+
let mut flags: HashSet<(bool, bool)> = HashSet::new();
24+
let ribbi = fonts.iter().filter(|f| f.is_ribbi());
25+
for font in ribbi {
26+
let _names_list = font.get_name_entry_strings(StringId::FAMILY_NAME);
27+
let fsselection = font.get_os2_fsselection()?;
28+
let val = (
29+
fsselection.intersects(SelectionFlags::BOLD),
30+
fsselection.intersects(SelectionFlags::ITALIC),
31+
);
32+
if flags.contains(&val) {
33+
problems.push(Status::fail(
34+
"unique-fsselection",
35+
&(format!(
36+
"Font {} has the same selection flags ({}{}{}) as another font",
37+
font.filename.to_string_lossy(),
38+
if val.0 { "bold" } else { "" },
39+
if val.0 && val.1 { " & " } else { "" },
40+
if val.1 { "italic" } else { "" }
41+
)),
42+
));
43+
} else {
44+
flags.insert(val);
45+
}
46+
}
47+
return_result(problems)
48+
}

profile-universal/src/checks/head.rs

+104
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
use font_types::NameId;
2+
use fontspector_checkapi::{prelude::*, testfont, FileTypeConvert};
3+
use read_fonts::{tables::head::MacStyle, TableProvider};
4+
use skrifa::MetadataProvider;
5+
6+
#[check(
7+
id = "font_version",
8+
proposal = "legacy:check/044",
9+
title = "Checking font version fields (head and name table).",
10+
rationale = "
11+
The OpenType specification provides for two fields which contain
12+
the version number of the font: fontRevision in the head table,
13+
and nameID 5 in the name table. If these fields do not match,
14+
different applications will report different version numbers for
15+
the font.
16+
"
17+
)]
18+
fn font_version(f: &Testable, _context: &Context) -> CheckFnResult {
19+
let font = testfont!(f);
20+
let head_version = font.font().head()?.font_revision().to_f32();
21+
let name_id_5_version_str = font
22+
.font()
23+
.localized_strings(NameId::VERSION_STRING)
24+
.english_or_first()
25+
.ok_or(CheckError::Error("No name ID 5".to_string()))?
26+
.chars()
27+
.skip_while(|c| !c.is_ascii_digit())
28+
.take_while(|c| c.is_ascii_digit() || *c == '.')
29+
.collect::<String>();
30+
if name_id_5_version_str.is_empty() {
31+
return Err(CheckError::Error(
32+
"No version string in name table".to_string(),
33+
));
34+
}
35+
let name_id_5_version = name_id_5_version_str.parse::<f32>().map_err(|e| {
36+
CheckError::Error(format!("Could not parse name ID 5 version as float: {}", e))
37+
})?;
38+
let warn_tolerance = 1.0 / (0x10000 as f32);
39+
let fail_tolerance = 1.0 / 2000.0;
40+
if (head_version - name_id_5_version).abs() > fail_tolerance {
41+
return Ok(Status::just_one_fail(
42+
"mismatch",
43+
&format!(
44+
"Font version mismatch: head table: {}, name table: {}",
45+
head_version, name_id_5_version
46+
),
47+
));
48+
}
49+
if (head_version - name_id_5_version).abs() > warn_tolerance {
50+
return Ok(Status::just_one_warn(
51+
"mismatch",
52+
&format!(
53+
"Font version mismatch: head table: {}, name table: {}",
54+
head_version, name_id_5_version
55+
),
56+
));
57+
}
58+
Ok(Status::just_one_pass())
59+
}
60+
61+
#[check(
62+
id = "opentype/mac_style",
63+
proposal = "legacy:check/031",
64+
title = "Checking head.macStyle value.",
65+
rationale = "
66+
The values of the flags on the macStyle entry on the 'head' OpenType table
67+
that describe whether a font is bold and/or italic must be coherent with the
68+
actual style of the font as inferred by its filename.
69+
"
70+
)]
71+
fn mac_style(f: &Testable, _context: &Context) -> CheckFnResult {
72+
let font = testfont!(f);
73+
let head = font.font().head()?;
74+
let style = font
75+
.style()
76+
.ok_or(CheckError::skip("no-style", "No style detected"))?;
77+
let bold = style == "Bold" || style == "BoldItalic";
78+
let italic = style.contains("Italic");
79+
let bits = head.mac_style();
80+
let bold_ok = bits.contains(MacStyle::BOLD) == bold;
81+
let italic_ok = bits.contains(MacStyle::ITALIC) == italic;
82+
let mut problems = vec![];
83+
if !bold_ok {
84+
problems.push(Status::warn(
85+
"bold-mismatch",
86+
&format!(
87+
"macStyle bold flag {} does not match font style {}",
88+
bits.contains(MacStyle::BOLD),
89+
style
90+
),
91+
));
92+
}
93+
if !italic_ok {
94+
problems.push(Status::warn(
95+
"italic-mismatch",
96+
&format!(
97+
"macStyle italic flag {} does not match font style {}",
98+
bits.contains(MacStyle::ITALIC),
99+
italic
100+
),
101+
));
102+
}
103+
return_result(problems)
104+
}

profile-universal/src/checks/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ pub mod arabic_spacing_symbols;
22
pub mod bold_italic_unique;
33
pub mod fvar;
44
pub mod glyphnames;
5+
pub mod head;
56
pub mod hhea;
67
pub mod name;
78
pub mod name_trailing_spaces;

0 commit comments

Comments
 (0)