summaryrefslogtreecommitdiffstats
path: root/lib/silfont/fbtests/silnotcjk.py
blob: 9ebdafdcfcd303005bb632a96dc93c482ec4d86c (plain)
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
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
#!/usr/bin/env python
'''These are copies of checks that have the "not is_cjk" condition, but these versions have that condition removed.
The is_cjk condition was being matched by multiple fonts that are not cjk fonts - but do have some cjk punctuation characters.
These checks based on based on examples from Font Bakery, copyright 2017 The Font Bakery Authors, licensed under the Apache 2.0 license'''
__url__ = 'http://github.com/silnrsi/pysilfont'
__copyright__ = 'Copyright (c) 2022 SIL International (http://www.sil.org)'
__license__ = 'Released under the MIT License (http://opensource.org/licenses/MIT)'
__author__ = 'David Raymond'

from fontbakery.checkrunner import Section, PASS, FAIL, WARN, ERROR, INFO, SKIP
from fontbakery.callable import condition, check, disable
from fontbakery.message import Message
from fontbakery.profiles.shared_conditions import typo_metrics_enabled
import os
from fontbakery.constants import NameID, PlatformID, WindowsEncodingID

@check(
    id = 'org.sil/check/family/win_ascent_and_descent',
    conditions = ['vmetrics'],
    rationale = """
        Based on com.google.fonts/check/family/win_ascent_and_descent but with the 'not is_cjk' condition removed
    """
)
def org_sil_check_family_win_ascent_and_descent(ttFont, vmetrics):
    """Checking OS/2 usWinAscent & usWinDescent."""

    if "OS/2" not in ttFont:
        yield FAIL,\
              Message("lacks-OS/2",
                      "Font file lacks OS/2 table")
        return

    failed = False
    os2_table = ttFont['OS/2']
    win_ascent = os2_table.usWinAscent
    win_descent = os2_table.usWinDescent
    y_max = vmetrics['ymax']
    y_min = vmetrics['ymin']

    # OS/2 usWinAscent:
    if win_ascent < y_max:
        failed = True
        yield FAIL,\
              Message("ascent",
                      f"OS/2.usWinAscent value should be"
                      f" equal or greater than {y_max},"
                      f" but got {win_ascent} instead")
    if win_ascent > y_max * 2:
        failed = True
        yield FAIL,\
              Message("ascent",
                      f"OS/2.usWinAscent value"
                      f" {win_ascent} is too large."
                      f" It should be less than double the yMax."
                      f" Current yMax value is {y_max}")
    # OS/2 usWinDescent:
    if win_descent < abs(y_min):
        failed = True
        yield FAIL,\
              Message("descent",
                      f"OS/2.usWinDescent value should be equal or"
                      f" greater than {abs(y_min)}, but got"
                      f" {win_descent} instead.")

    if win_descent > abs(y_min) * 2:
        failed = True
        yield FAIL,\
              Message("descent",
                      f"OS/2.usWinDescent value"
                      f" {win_descent} is too large."
                      f" It should be less than double the yMin."
                      f" Current absolute yMin value is {abs(y_min)}")
    if not failed:
        yield PASS, "OS/2 usWinAscent & usWinDescent values look good!"


@check(
    id = 'org.sil/check/os2_metrics_match_hhea',
    rationale="""
            Based on com.google.fonts/check/os2_metrics_match_hhea but with the 'not is_cjk' condition removed
        """
)
def org_sil_check_os2_metrics_match_hhea(ttFont):
    """Checking OS/2 Metrics match hhea Metrics."""

    filename = os.path.basename(ttFont.reader.file.name)

    # Check both OS/2 and hhea are present.
    missing_tables = False

    required = ["OS/2", "hhea"]
    for key in required:
        if key not in ttFont:
            missing_tables = True
            yield FAIL,\
                  Message(f'lacks-{key}',
                          f"{filename} lacks a '{key}' table.")

    if missing_tables:
        return

    # OS/2 sTypoAscender and sTypoDescender match hhea ascent and descent
    if ttFont["OS/2"].sTypoAscender != ttFont["hhea"].ascent:
        yield FAIL,\
              Message("ascender",
                      f"OS/2 sTypoAscender ({ttFont['OS/2'].sTypoAscender})"
                      f" and hhea ascent ({ttFont['hhea'].ascent})"
                      f" must be equal.")
    elif ttFont["OS/2"].sTypoDescender != ttFont["hhea"].descent:
        yield FAIL,\
              Message("descender",
                      f"OS/2 sTypoDescender ({ttFont['OS/2'].sTypoDescender})"
                      f" and hhea descent ({ttFont['hhea'].descent})"
                      f" must be equal.")
    elif ttFont["OS/2"].sTypoLineGap != ttFont["hhea"].lineGap:
        yield FAIL,\
              Message("lineGap",
                      f"OS/2 sTypoLineGap ({ttFont['OS/2'].sTypoLineGap})"
                      f" and hhea lineGap ({ttFont['hhea'].lineGap})"
                      f" must be equal.")
    else:
        yield PASS, ("OS/2.sTypoAscender/Descender values"
                     " match hhea.ascent/descent.")

@check(
    id = "org.sil/check/os2/use_typo_metrics",
    rationale="""
            Based on com.google.fonts/check/os2/use_typo_metrics but with the 'not is_cjk' condition removed
        """
    )
def corg_sil_check_os2_fsselectionbit7(ttFonts):
    """OS/2.fsSelection bit 7 (USE_TYPO_METRICS) is set in all fonts."""

    bad_fonts = []
    for ttFont in ttFonts:
        if not ttFont["OS/2"].fsSelection & (1 << 7):
            bad_fonts.append(ttFont.reader.file.name)

    if bad_fonts:
        yield FAIL,\
              Message('missing-os2-fsselection-bit7',
                      f"OS/2.fsSelection bit 7 (USE_TYPO_METRICS) was"
                      f"NOT set in the following fonts: {bad_fonts}.")
    else:
        yield PASS, "OK"


@check(
    id = 'org.sil/check/vertical_metrics',
#    conditions = ['not remote_styles'],
    rationale="""
            Based on com.google.fonts/check/vertical_metrics but with the 'not is_cjk' condition removed
        """
)
def org_sil_check_vertical_metrics(ttFont):
    """Check font follows the Google Fonts vertical metric schema"""
    filename = os.path.basename(ttFont.reader.file.name)

    # Check necessary tables are present.
    missing_tables = False
    required = ["OS/2", "hhea", "head"]
    for key in required:
        if key not in ttFont:
            missing_tables = True
            yield FAIL,\
                  Message(f'lacks-{key}',
                          f"{filename} lacks a '{key}' table.")

    if missing_tables:
        return

    font_upm = ttFont['head'].unitsPerEm
    font_metrics = {
        'OS/2.sTypoAscender': ttFont['OS/2'].sTypoAscender,
        'OS/2.sTypoDescender': ttFont['OS/2'].sTypoDescender,
        'OS/2.sTypoLineGap': ttFont['OS/2'].sTypoLineGap,
        'hhea.ascent': ttFont['hhea'].ascent,
        'hhea.descent': ttFont['hhea'].descent,
        'hhea.lineGap': ttFont['hhea'].lineGap,
        'OS/2.usWinAscent': ttFont['OS/2'].usWinAscent,
        'OS/2.usWinDescent': ttFont['OS/2'].usWinDescent
    }
    expected_metrics = {
        'OS/2.sTypoLineGap': 0,
        'hhea.lineGap': 0,
    }

    failed = False
    warn = False

    # Check typo metrics and hhea lineGap match our expected values
    for k in expected_metrics:
        if font_metrics[k] != expected_metrics[k]:
            failed = True
            yield FAIL,\
                  Message(f'bad-{k}',
                          f'{k} is "{font_metrics[k]}" it should be {expected_metrics[k]}')

    hhea_sum = (font_metrics['hhea.ascent'] +
                abs(font_metrics['hhea.descent']) +
                font_metrics['hhea.lineGap']) / font_upm

    # Check the sum of the hhea metrics is not below 1.2
    # (120% of upm or 1200 units for 1000 upm font)
    if hhea_sum < 1.2:
        failed = True
        yield FAIL,\
            Message('bad-hhea-range',
                'The sum of hhea.ascender+abs(hhea.descender)+hhea.lineGap '
                f'is {int(hhea_sum*font_upm)} when it should be at least {int(font_upm*1.2)}')

    # Check the sum of the hhea metrics is below 2.0
    elif hhea_sum > 2.0:
        failed = True
        yield FAIL,\
            Message('bad-hhea-range',
                'The sum of hhea.ascender+abs(hhea.descender)+hhea.lineGap '
                f'is {int(hhea_sum*font_upm)} when it should be at most {int(font_upm*2.0)}')

    # Check the sum of the hhea metrics is between 1.1-1.5x of the font's upm
    elif hhea_sum > 1.5:
        warn = True
        yield WARN,\
            Message('bad-hhea-range',
                "We recommend the absolute sum of the hhea metrics should be"
                f" between 1.2-1.5x of the font's upm. This font has {hhea_sum}x ({int(hhea_sum*font_upm)})")

    if not failed and not warn:
        yield PASS, 'Vertical metrics are good'