@@ -4,6 +4,14 @@ include!(concat!(env!("OUT_DIR"), "/protos/mod.rs"));
4
4
use chrono:: prelude:: * ;
5
5
use fonts_public:: FamilyProto ;
6
6
use fontspector_checkapi:: prelude:: * ;
7
+ use fontspector_checkapi:: { skip, testfont, FileTypeConvert } ;
8
+
9
+ fn family_proto ( t : & Testable ) -> Result < FamilyProto , CheckError > {
10
+ let mdpb = std:: str:: from_utf8 ( & t. contents )
11
+ . map_err ( |_| CheckError :: Error ( "METADATA.pb is not valid UTF-8" . to_string ( ) ) ) ?;
12
+ protobuf:: text_format:: parse_from_str :: < FamilyProto > ( mdpb)
13
+ . map_err ( |e| CheckError :: Error ( format ! ( "Error parsing METADATA.pb: {}" , e) ) )
14
+ }
7
15
8
16
#[ check(
9
17
id = "com.google.fonts/check/metadata/parses" ,
@@ -16,10 +24,7 @@ use fontspector_checkapi::prelude::*;
16
24
applies_to = "MDPB"
17
25
) ]
18
26
fn validate_metadatapb ( c : & Testable , _context : & Context ) -> CheckFnResult {
19
- let mdpb = std:: str:: from_utf8 ( & c. contents )
20
- . map_err ( |_| CheckError :: Error ( "METADATA.pb is not valid UTF-8" . to_string ( ) ) ) ?;
21
- let msg = protobuf:: text_format:: parse_from_str :: < FamilyProto > ( mdpb)
22
- . map_err ( |e| CheckError :: Error ( format ! ( "Error parsing METADATA.pb: {}" , e) ) ) ?;
27
+ let msg = family_proto ( c) ?;
23
28
let mut problems = vec ! [ ] ;
24
29
if let Some ( designer) = msg. designer . as_ref ( ) {
25
30
if designer. contains ( '/' ) {
@@ -50,3 +55,74 @@ fn validate_metadatapb(c: &Testable, _context: &Context) -> CheckFnResult {
50
55
}
51
56
return_result ( problems)
52
57
}
58
+
59
+ #[ check(
60
+ id = "com.google.fonts/check/metadata/can_render_samples" ,
61
+ title = "Check samples can be rendered" ,
62
+ rationale = "
63
+ In order to prevent tofu from being seen on fonts.google.com, this check
64
+ verifies that all samples specified by METADATA.pb can be properly
65
+ rendered by the font.
66
+ " ,
67
+ proposal = "https://github.com/fonttools/fontbakery/issues/3419" ,
68
+ applies_to = "MDPB" ,
69
+ implementation = "all"
70
+ ) ]
71
+ fn can_render_samples ( c : & TestableCollection , _context : & Context ) -> CheckFnResult {
72
+ let mdpb = c
73
+ . get_file ( "METADATA.pb" )
74
+ . ok_or_else ( || CheckError :: skip ( "no-mdpb" , "No METADATA.pb file found" ) ) ?;
75
+ let msg = family_proto ( mdpb) ?;
76
+ let languages = msg. languages ;
77
+ if languages. is_empty ( ) {
78
+ skip ! ( "no-languages" , "No languages specified in METADATA.pb" ) ;
79
+ }
80
+ let fonts = msg
81
+ . fonts
82
+ . iter ( )
83
+ . flat_map ( |f| f. filename . as_ref ( ) )
84
+ . flat_map ( |f| c. get_file ( f) )
85
+ . collect :: < Vec < & Testable > > ( ) ;
86
+ if fonts. is_empty ( ) {
87
+ skip ! ( "no-fonts" , "No font files found in METADATA.pb" ) ;
88
+ }
89
+ let mut samples: Vec < ( & str , String ) > = vec ! [ ] ;
90
+ for language in languages
91
+ . iter ( )
92
+ . flat_map ( |l| google_fonts_languages:: LANGUAGES . get ( l. as_str ( ) ) )
93
+ {
94
+ if let Some ( st) = language. sample_text . as_ref ( ) {
95
+ if let Some ( tester) = st. tester . as_ref ( ) {
96
+ let tester = tester
97
+ . to_string ( )
98
+ . replace ( "\n " , " " )
99
+ . replace ( "\u{200b} " , "" ) ;
100
+ if let Some ( name) = & language. name {
101
+ samples. push ( ( name, tester) ) ;
102
+ }
103
+ }
104
+ }
105
+ }
106
+ let mut problems = vec ! [ ] ;
107
+ for font in fonts {
108
+ // We could get all clever and use harfruzz here, but to honest,
109
+ // the only way you get a .notdef is if you can't use cmap to map
110
+ // the character to a glyph, so we'll just use that.
111
+ let ttf = testfont ! ( font) ;
112
+ let codepoints = ttf. codepoints ( ) ;
113
+ for ( langid, sample) in samples. iter ( ) {
114
+ if sample. chars ( ) . any ( |c| !codepoints. contains ( & ( c as u32 ) ) ) {
115
+ problems. push ( Status :: fail (
116
+ "tofu" ,
117
+ & format ! (
118
+ "Font {} cannot render {} sample text: {}" ,
119
+ font. basename( ) . unwrap_or_default( ) ,
120
+ langid,
121
+ sample
122
+ ) ,
123
+ ) ) ;
124
+ }
125
+ }
126
+ }
127
+ return_result ( problems)
128
+ }
0 commit comments