-
Notifications
You must be signed in to change notification settings - Fork 103
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
split out typenetwork checks into individual files
- Loading branch information
1 parent
2a5225f
commit 9f49523
Showing
21 changed files
with
1,328 additions
and
1,320 deletions.
There are no files selected for viewing
1,320 changes: 0 additions & 1,320 deletions
1,320
Lib/fontbakery/checks/vendorspecific/typenetwork.py
This file was deleted.
Oops, something went wrong.
50 changes: 50 additions & 0 deletions
50
Lib/fontbakery/checks/vendorspecific/typenetwork/PUA_encoded_glyphs.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
from fontbakery.prelude import check, Message, PASS, WARN | ||
from fontbakery.utils import bullet_list | ||
|
||
|
||
def in_PUA_range(codepoint): | ||
""" | ||
Three private use areas are defined: | ||
one in the Basic Multilingual Plane (U+E000–U+F8FF), | ||
and one each in, and nearly covering, planes 15 and 16 | ||
(U+F0000–U+FFFFD, U+100000–U+10FFFD). | ||
""" | ||
return ( | ||
(codepoint >= 0xE000 and codepoint <= 0xF8FF) | ||
or (codepoint >= 0xF0000 and codepoint <= 0xFFFFD) | ||
or (codepoint >= 0x100000 and codepoint <= 0x10FFFD) | ||
) | ||
|
||
|
||
@check( | ||
id="typenetwork/PUA_encoded_glyphs", | ||
rationale=""" | ||
Using Private Use Area (PUA) encodings is not recommended. They are | ||
defined by users and are not standardized. That said, PUA are font | ||
specific so they will break if the user tries to copy/paste, | ||
search/replace, or change the font. Using PUA to encode small caps, | ||
for example, is not recommended as small caps can and should be | ||
accessible via Open Type substitution instead. | ||
If you must encode your characters in the Private Use Area (PUA), | ||
do so with great caution. | ||
""", | ||
proposal=["https://github.com/fonttools/fontbakery/pull/4260"], | ||
) | ||
def check_PUA_encoded_glyphs(ttFont, config): | ||
"""Check if font has PUA encoded glyphs.""" | ||
|
||
pua_encoded_glyphs = [] | ||
|
||
for cp, glyphName in ttFont.getBestCmap().items(): | ||
if in_PUA_range(cp) and cp != 0xF8FF: | ||
pua_encoded_glyphs.append(glyphName + f" U+{cp:02x}".upper()) | ||
|
||
if pua_encoded_glyphs: | ||
yield WARN, Message( | ||
"pua-encoded", | ||
f"Glyphs with PUA codepoints:\n\n" | ||
f"{bullet_list(config, pua_encoded_glyphs)}", | ||
) | ||
else: | ||
yield PASS, "No PUA encoded glyphs." |
Empty file.
34 changes: 34 additions & 0 deletions
34
Lib/fontbakery/checks/vendorspecific/typenetwork/composite_glyphs.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
import string | ||
|
||
from fontbakery.prelude import check, Message, PASS, WARN | ||
|
||
|
||
@check( | ||
id="typenetwork/composite_glyphs", | ||
rationale=""" | ||
For performance reasons, it is recommended that TTF fonts use composite glyphs. | ||
""", | ||
conditions=["is_ttf"], | ||
proposal=["https://github.com/fonttools/fontbakery/pull/4260"], | ||
) | ||
def check_composite_glyphs(ttFont): | ||
"""Check if TTF font uses composite glyphs.""" | ||
baseGlyphs = [*string.printable] | ||
failed = [] | ||
|
||
numberOfGlyphs = ttFont["maxp"].numGlyphs | ||
for glyph_name in ttFont["glyf"].keys(): | ||
glyph = ttFont["glyf"][glyph_name] | ||
if glyph_name not in baseGlyphs and glyph.isComposite() is False: | ||
failed.append(glyph_name) | ||
|
||
percentageOfNotCompositeGlyphs = round(len(failed) * 100 / numberOfGlyphs) | ||
if percentageOfNotCompositeGlyphs > 50: | ||
yield WARN, Message( | ||
"low-composites", | ||
f"{percentageOfNotCompositeGlyphs}% of the glyphs are not composites.", | ||
) | ||
else: | ||
yield PASS, ( | ||
f"{100-percentageOfNotCompositeGlyphs}% of the glyphs are composites." | ||
) |
Empty file.
95 changes: 95 additions & 0 deletions
95
Lib/fontbakery/checks/vendorspecific/typenetwork/family/duplicated_names.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
from fontbakery.prelude import check, Message, PASS, FAIL | ||
from fontbakery.constants import ( | ||
PlatformID, | ||
WindowsEncodingID, | ||
WindowsLanguageID, | ||
) | ||
|
||
|
||
@check( | ||
id="typenetwork/family/duplicated_names", | ||
rationale=""" | ||
Having duplicated name records can produce several issues like not all fonts | ||
being listed on design apps or incorrect automatic creation of CSS classes | ||
and @font-face rules. | ||
""", | ||
proposal=[ | ||
"https://github.com/fonttools/fontbakery/pull/4260", | ||
# "https://github.com/TypeNetwork/fontQA/issues/25", # Currently private repo. | ||
], | ||
) | ||
def check_family_duplicated_names(ttFonts): | ||
"""Check if font doesn’t have duplicated names within a family.""" | ||
duplicate_subfamilyNames = set() | ||
seen_fullNames = set() | ||
duplicate_fullNames = set() | ||
seen_postscriptNames = set() | ||
duplicate_postscriptNames = set() | ||
|
||
PLAT_ID = PlatformID.WINDOWS | ||
ENC_ID = WindowsEncodingID.UNICODE_BMP | ||
LANG_ID = WindowsLanguageID.ENGLISH_USA | ||
|
||
for ttFont in list(ttFonts): | ||
# # Subfamily name | ||
# if ttFont["name"].getName(17, PLAT_ID, ENC_ID, LANG_ID): | ||
# subfamName = ttFont["name"].getName(17, PLAT_ID, ENC_ID, LANG_ID) | ||
# else: | ||
# subfamName = ttFont["name"].getName(2, PLAT_ID, ENC_ID, LANG_ID) | ||
|
||
# if subfamName: | ||
# subfamName = subfamName.toUnicode() | ||
# if subfamName in seen_subfamilyNames: | ||
# duplicate_subfamilyNames.add(subfamName) | ||
# else: | ||
# seen_subfamilyNames.add(subfamName) | ||
|
||
# FullName name | ||
fullName = ttFont["name"].getName(4, PLAT_ID, ENC_ID, LANG_ID) | ||
|
||
if fullName: | ||
fullName = fullName.toUnicode() | ||
if fullName in seen_fullNames: | ||
duplicate_fullNames.add(fullName) | ||
else: | ||
seen_fullNames.add(fullName) | ||
|
||
# Postscript name | ||
postscriptName = ttFont["name"].getName(6, PLAT_ID, ENC_ID, LANG_ID) | ||
if postscriptName: | ||
postscriptName = postscriptName.toUnicode() | ||
if postscriptName in seen_postscriptNames: | ||
duplicate_subfamilyNames.add(postscriptName) | ||
else: | ||
seen_postscriptNames.add(postscriptName) | ||
|
||
# if duplicate_subfamilyNames: | ||
# duplicate_subfamilyNamesString = \ | ||
# "".join(f"* {inst}\n" for inst in sorted(duplicate_subfamilyNames)) | ||
# yield FAIL, Message( | ||
# "duplicate-subfamily-names", | ||
# "Following subfamily names are duplicate:\n\n" | ||
# f"{duplicate_subfamilyNamesString}", | ||
# ) | ||
|
||
if duplicate_fullNames: | ||
duplicate_fullNamesString = "".join( | ||
f"* {inst}\n" for inst in sorted(duplicate_fullNames) | ||
) | ||
yield FAIL, Message( | ||
"duplicate-full-names", | ||
"Following full names are duplicate:\n\n" f"{duplicate_fullNamesString}", | ||
) | ||
|
||
if duplicate_postscriptNames: | ||
duplicate_postscriptNamesString = "".join( | ||
f"* {inst}\n" for inst in sorted(duplicate_postscriptNames) | ||
) | ||
yield FAIL, Message( | ||
"duplicate-postscript-names", | ||
"Following postscript names are duplicate:\n\n" | ||
f"{duplicate_postscriptNamesString}", | ||
) | ||
|
||
if not duplicate_fullNames and not duplicate_postscriptNames: | ||
yield PASS, "All names are unique" |
81 changes: 81 additions & 0 deletions
81
Lib/fontbakery/checks/vendorspecific/typenetwork/family/equal_numbers_of_glyphs.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
from fontbakery.testable import CheckRunContext | ||
from fontbakery.prelude import check, condition, Message, PASS, WARN | ||
|
||
|
||
@condition(CheckRunContext) | ||
def roman_ttFonts(context): | ||
return [font.ttFont for font in context.fonts if not font.is_italic] | ||
|
||
|
||
@condition(CheckRunContext) | ||
def italic_ttFonts(context): | ||
return [font.ttFont for font in context.fonts if font.is_italic] | ||
|
||
|
||
@check( | ||
id="typenetwork/family/equal_numbers_of_glyphs", | ||
rationale=""" | ||
Check if all fonts in a family have the same number of glyphs. | ||
""", | ||
conditions=["roman_ttFonts", "italic_ttFonts"], | ||
proposal=["https://github.com/fonttools/fontbakery/pull/4260"], | ||
) | ||
def equal_numbers_of_glyphs(roman_ttFonts, italic_ttFonts): | ||
"""Equal number of glyphs""" | ||
max_roman_count = 0 | ||
max_roman_font = None | ||
roman_failed_fonts = {} | ||
|
||
# Checks roman | ||
for ttFont in list(roman_ttFonts): | ||
fontname = ttFont.reader.file.name | ||
this_count = ttFont["maxp"].numGlyphs | ||
if this_count > max_roman_count: | ||
max_roman_count = this_count | ||
max_roman_font = fontname | ||
|
||
for ttFont in list(roman_ttFonts): | ||
this_count = ttFont["maxp"].numGlyphs | ||
fontname = ttFont.reader.file.name | ||
if this_count != max_roman_count: | ||
roman_failed_fonts[fontname] = this_count | ||
|
||
max_italic_count = 0 | ||
max_italic_font = None | ||
italic_failed_fonts = {} | ||
|
||
# Checks Italics | ||
for ttFont in list(italic_ttFonts): | ||
fontname = ttFont.reader.file.name | ||
this_count = ttFont["maxp"].numGlyphs | ||
if this_count > max_italic_count: | ||
max_italic_count = this_count | ||
max_italic_font = fontname | ||
|
||
for ttFont in list(italic_ttFonts): | ||
this_count = ttFont["maxp"].numGlyphs | ||
fontname = ttFont.reader.file.name | ||
if this_count != max_italic_count: | ||
italic_failed_fonts[fontname] = this_count | ||
|
||
if len(roman_failed_fonts) > 0: | ||
yield WARN, Message( | ||
"roman-different-number-of-glyphs", | ||
f"Romans doesn’t have the same number of glyphs" | ||
f"{max_roman_font} has {max_roman_count} and \n\t{roman_failed_fonts}", | ||
) | ||
else: | ||
yield PASS, ( | ||
"All roman files in this family have an equal total ammount of glyphs." | ||
) | ||
|
||
if len(italic_failed_fonts) > 0: | ||
yield WARN, Message( | ||
"italic-different-number-of-glyphs", | ||
f"Italics doesn’t have the same number of glyphs" | ||
f"{max_italic_font} has {max_italic_count} and \n\t{italic_failed_fonts}", | ||
) | ||
else: | ||
yield PASS, ( | ||
"All italics files in this family have an equal total ammount of glyphs." | ||
) |
78 changes: 78 additions & 0 deletions
78
Lib/fontbakery/checks/vendorspecific/typenetwork/family/tnum_horizontal_metrics.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
from fontbakery.prelude import check, Message, PASS, WARN, INFO | ||
from fontbakery.utils import ( | ||
bullet_list, | ||
pretty_print_list, | ||
) | ||
|
||
|
||
@check( | ||
id="typenetwork/family/tnum_horizontal_metrics", | ||
rationale=""" | ||
Tabular figures need to have the same metrics in all styles in order to allow | ||
tables to be set with proper typographic control, but to maintain the placement | ||
of decimals and numeric columns between rows. | ||
""", | ||
proposal=["https://github.com/fonttools/fontbakery/pull/4260"], | ||
) | ||
def check_family_tnum_horizontal_metrics(ttFonts, config): | ||
"""All tabular figures must have the same width across the family.""" | ||
tnum_widths = {} | ||
half_width_glyphs = {} | ||
for ttFont in list(ttFonts): | ||
glyphs = ttFont.getGlyphSet() | ||
|
||
tabular_suffixes = (".tnum", ".tf", ".tosf", ".tsc", ".tab", ".tabular") | ||
tnum_glyphs = [ | ||
(glyph_id, glyphs[glyph_id]) | ||
for glyph_id in glyphs.keys() | ||
if any(suffix in glyph_id for suffix in tabular_suffixes) | ||
] | ||
|
||
for glyph_id, glyph in tnum_glyphs: | ||
if glyph.width not in tnum_widths: | ||
tnum_widths[glyph.width] = [glyph_id] | ||
else: | ||
tnum_widths[glyph.width].append(glyph_id) | ||
|
||
max_num = 0 | ||
most_common_width = None | ||
half_width = None | ||
|
||
# Get most common width | ||
for width, glyphs in tnum_widths.items(): | ||
if len(glyphs) > max_num: | ||
max_num = len(glyphs) | ||
most_common_width = width | ||
if most_common_width: | ||
del tnum_widths[most_common_width] | ||
|
||
# Get Half width | ||
for width, glyphs in tnum_widths.items(): | ||
if round(most_common_width / 2) == width: | ||
half_width = width | ||
half_width_glyphs = glyphs | ||
|
||
if half_width: | ||
del tnum_widths[half_width] | ||
|
||
if half_width: | ||
yield INFO, Message( | ||
"half-widths", | ||
f"The are other glyphs with half of the width ({half_width}) of the" | ||
f" most common width such as the following ones:\n\n" | ||
f"{bullet_list(config, half_width_glyphs)}.", | ||
) | ||
|
||
if len(tnum_widths.keys()): | ||
# prepare string to display | ||
tnumWidthsString = "" | ||
for width, glyphs in tnum_widths.items(): | ||
tnumWidthsString += f"{width}: {pretty_print_list(config, glyphs)}\n\n" | ||
yield WARN, Message( | ||
"inconsistent-widths", | ||
f"The most common tabular glyph width is {most_common_width}." | ||
f" But there are other tabular glyphs with different widths" | ||
f" such as the following ones:\n\n{tnumWidthsString}.", | ||
) | ||
else: | ||
yield PASS, "OK" |
20 changes: 20 additions & 0 deletions
20
Lib/fontbakery/checks/vendorspecific/typenetwork/family/valid_strikeout.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
from fontbakery.prelude import check, Message, FAIL | ||
|
||
|
||
@check( | ||
id="typenetwork/family/valid_strikeout", | ||
rationale=""" | ||
If strikeout size is not set, nothing gets rendered on Figma. | ||
""", | ||
proposal=["https://github.com/fonttools/fontbakery/pull/4260"], | ||
misc_metadata={"affects": [("Figma", "unspecified")]}, | ||
) | ||
def check_family_valid_strikeout(ttFont): | ||
"""Font has a value strikeout size?""" | ||
|
||
strikeoutSize = ttFont["OS/2"].yStrikeoutSize | ||
if strikeoutSize is None or strikeoutSize == 0: | ||
yield FAIL, Message( | ||
"invalid-strikeout-size", | ||
f"Size of the strikeout is {strikeoutSize} which is not valid.", | ||
) |
20 changes: 20 additions & 0 deletions
20
Lib/fontbakery/checks/vendorspecific/typenetwork/family/valid_underline.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
from fontbakery.prelude import check, Message, FAIL | ||
|
||
|
||
@check( | ||
id="typenetwork/family/valid_underline", | ||
rationale=""" | ||
If underline thickness is not set nothing gets rendered on Figma. | ||
""", | ||
proposal=["https://github.com/fonttools/fontbakery/pull/4260"], | ||
misc_metadata={"affects": [("Figma", "unspecified")]}, | ||
) | ||
def check_family_valid_underline(ttFont): | ||
"""Font has a valid underline thickness?""" | ||
|
||
underlineThickness = ttFont["post"].underlineThickness | ||
if underlineThickness is None or underlineThickness == 0: | ||
yield FAIL, Message( | ||
"invalid-underline-thickness", | ||
f"Thickness of the underline is {underlineThickness} which is not valid.", | ||
) |
Oops, something went wrong.