This repository has been archived by the owner on Oct 23, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 309
/
coverage_test.py
executable file
·158 lines (125 loc) · 5.25 KB
/
coverage_test.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
#!/usr/bin/env python
#
# Copyright 2015 Google Inc. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Routines for checking character coverage of Roboto fonts.
This scripts takes the name of the directory where the fonts are and checks
that they cover all characters required in the Roboto extension contract.
The data is in res/char_requirements.tsv.
"""
__author__ = (
"roozbeh@google.com (Roozbeh Pournader) and "
"cibu@google.com (Cibu Johny)")
import sys
import glob
from fontTools import ttLib
from nototools import coverage
from nototools import font_data
from nototools import unicode_data
def load_fonts():
"""Load all fonts built for Android."""
all_fonts = (glob.glob('out/RobotoTTF/*.ttf')
+ glob.glob('out/RobotoCondensedTTF/*.ttf'))
all_fonts = [ttLib.TTFont(font) for font in all_fonts]
return all_fonts
def _character_name(code):
"""Returns the printable name of a character."""
return unicode_data.name(unichr(code), '<Unassigned>')
def _print_char(code, additional_info=None):
"""Print a Unicode character as code and name and perhaps extra info."""
sys.stdout.write('U+%04X %s' % (code, _character_name(code)))
if additional_info is not None:
sys.stdout.write('\t' + additional_info)
sys.stdout.write('\n')
def _range_string_to_set(range_str):
"""Convert a range encoding in a string to a set."""
if '..' in range_str:
range_start, range_end = range_str.split('..')
range_start = int(range_start, 16)
range_end = int(range_end, 16)
return set(range(range_start, range_end+1))
else:
return {int(range_str, 16)}
def _multiple_range_string_to_set(ranges_str):
"""Convert a string of multiple ranges to a set."""
char_set = set()
for range_str in ranges_str.split(', '):
if range_str.startswith('and '):
range_str = range_str[4:] # drop the 'and '
char_set.update(_range_string_to_set(range_str))
return char_set
def _defined_characters_in_range(range_str):
"""Given a range string, returns defined Unicode characters in the range."""
characters = set()
for code in _range_string_to_set(range_str):
if unicode_data.is_defined(code) and unicode_data.age(code) is not None:
characters.add(code)
return characters
_EXCEPTION_STARTER = 'Everything except '
def _find_required_chars(block_range, full_coverage_required, exceptions):
"""Finds required coverage based on a row of the spreadsheet."""
chars_defined_in_block = _defined_characters_in_range(block_range)
if full_coverage_required:
return chars_defined_in_block
else:
if not exceptions:
return set()
if exceptions.startswith(_EXCEPTION_STARTER):
exceptions = exceptions[len(_EXCEPTION_STARTER):]
chars_to_exclude = _multiple_range_string_to_set(exceptions)
return chars_defined_in_block - chars_to_exclude
else:
chars_to_limit_to = _multiple_range_string_to_set(exceptions)
return chars_defined_in_block & chars_to_limit_to
def main():
"""Checkes the coverage of all Roboto fonts."""
with open('res/char_requirements.tsv') as char_reqs_file:
char_reqs_data = char_reqs_file.read()
# The format of the data to be parsed is like the following:
# General Punctuation\t2000..206F\t111\t35\t54\t0\tEverything except 2028..202E, 2060..2064, and 2066..206F
# Currency Symbols\t20A0..20CF\t29\t5\t24\t1\t
required_set = set()
for line in char_reqs_data.split('\n'):
if line.startswith('#'): # Skip comment lines
continue
line = line.split('\t')
if not line[0]:
continue # Skip the first line and empty lines
block_range = line[1]
full_coverage_required = (line[5] == '1')
exceptions = line[6]
required_set.update(
_find_required_chars(block_range,
full_coverage_required,
exceptions))
# Skip Unicode 8.0 characters
required_set = {ch for ch in required_set
if float(unicode_data.age(ch)) <= 7.0}
# Skip ASCII and C1 controls
required_set -= set(range(0, 0x20) + range(0x7F, 0xA0))
missing_char_found = False
for font in load_fonts():
font_coverage = coverage.character_set(font)
missing_chars = required_set - font_coverage
if missing_chars:
missing_char_found = True
font_name = font_data.font_name(font)
print 'Characters missing from %s:' % font_name
for char in sorted(missing_chars):
_print_char(char)
print
if missing_char_found:
sys.exit(1)
if __name__ == '__main__':
main()