def com_google_fonts_check_046(ttFont): """Font contains the first few mandatory glyphs (.null or NULL, CR and space)?""" from fontbakery.utils import get_glyph_name # It would be good to also check # for .notdef (codepoint = unspecified) null = get_glyph_name(ttFont, 0x0000) CR = get_glyph_name(ttFont, 0x000D) space = get_glyph_name(ttFont, 0x0020) missing = [] if null is None: missing.append("0x0000") if CR is None: missing.append("0x000D") if space is None: missing.append("0x0020") if missing != []: yield WARN, ("Font is missing glyphs for" " the following mandatory codepoints:" " {}.").format(", ".join(missing)) else: yield PASS, ("Font contains the first few mandatory glyphs" " (.null or NULL, CR and space).")
def com_google_fonts_check_046(ttFont): """Font contains the first few mandatory glyphs (.null or NULL, CR and space)?""" from fontbakery.utils import get_glyph_name # It would be good to also check # for .notdef (codepoint = unspecified) null = get_glyph_name(ttFont, 0x0000) CR = get_glyph_name(ttFont, 0x000D) space = get_glyph_name(ttFont, 0x0020) missing = [] if null is None: missing.append("0x0000") if CR is None: missing.append("0x000D") if space is None: missing.append("0x0020") if missing != []: yield WARN, ("Font is missing glyphs for" " the following mandatory codepoints:" " {}.").format(", ".join(missing)) else: yield PASS, ("Font contains the first few mandatory glyphs" " (.null or NULL, CR and space).")
def com_google_fonts_check_whitespace_widths(ttFont): """Space and non-breaking space have the same width?""" from fontbakery.utils import get_glyph_name space_name = get_glyph_name(ttFont, 0x0020) nbsp_name = get_glyph_name(ttFont, 0x00A0) space_width = ttFont['hmtx'][space_name][0] nbsp_width = ttFont['hmtx'][nbsp_name][0] if space_width > 0 and space_width == nbsp_width: yield PASS, "Space and non-breaking space have the same width." else: yield FAIL,\ Message("different-widths", f"Space and non-breaking space have differing width:" f" The space glyph named {space_name}" f" is {space_width} font units wide," f" non-breaking space named ({nbsp_name})" f" is {nbsp_width} font units wide, and" f" both should be positive and the same." f" GlyphsApp has \"Sidebearing arithmetic\"" f" (https://glyphsapp.com/tutorials/spacing)" f" which allows you to set the non-breaking" f" space width to always equal the space width.")
def missing_whitespace_chars(ttFont): from fontbakery.utils import get_glyph_name space = get_glyph_name(ttFont, 0x0020) nbsp = get_glyph_name(ttFont, 0x00A0) # tab = get_glyph_name(ttFont, 0x0009) missing = [] if space is None: missing.append("0x0020") if nbsp is None: missing.append("0x00A0") # fonts probably don't need an actual tab char # if tab is None: missing.append("0x0009") return missing
def missing_whitespace_chars(ttFont): from fontbakery.utils import get_glyph_name space = get_glyph_name(ttFont, 0x0020) nbsp = get_glyph_name(ttFont, 0x00A0) # tab = get_glyph_name(ttFont, 0x0009) missing = [] if space is None: missing.append("0x0020") if nbsp is None: missing.append("0x00A0") # fonts probably don't need an actual tab char # if tab is None: missing.append("0x0009") return missing
def com_google_fonts_check_whitespace_ink(ttFont): """Whitespace glyphs have ink?""" from fontbakery.utils import (get_glyph_name, glyph_has_ink) # This checks that certain glyphs are empty. # Some, but not all, are Unicode whitespace. # code-points for all Unicode whitespace chars # (according to Unicode 11.0 property list): WHITESPACE_CHARACTERS = { 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x0020, 0x0085, 0x00A0, 0x1680, 0x2000, 0x2001, 0x2002, 0x2003, 0x2004, 0x2005, 0x2006, 0x2007, 0x2008, 0x2009, 0x200A, 0x2028, 0x2029, 0x202F, 0x205F, 0x3000 } # Code-points that do not have whitespace property, but # should not have a drawing. EXTRA_NON_DRAWING = {0x180E, 0x200B, 0x2060, 0xFEFF} # Make the set of non drawing characters. # OGHAM SPACE MARK U+1680 is removed as it is # a whitespace that should have a drawing. NON_DRAWING = WHITESPACE_CHARACTERS | EXTRA_NON_DRAWING - {0x1680} passed = True for codepoint in sorted(NON_DRAWING): g = get_glyph_name(ttFont, codepoint) if g is not None and glyph_has_ink(ttFont, g): passed = False yield FAIL,\ Message('has-ink', f'Glyph "{g}" has ink.' f' It needs to be replaced by an empty glyph.') if passed: yield PASS, "There is no whitespace glyph with ink."
def com_google_fonts_check_050(ttFont): """Whitespace and non-breaking space have the same width?""" from fontbakery.utils import get_glyph_name space_name = get_glyph_name(ttFont, 0x0020) nbsp_name = get_glyph_name(ttFont, 0x00A0) space_width = ttFont['hmtx'][space_name][0] nbsp_width = ttFont['hmtx'][nbsp_name][0] if space_width > 0 and space_width == nbsp_width: yield PASS, "Whitespace and non-breaking space have the same width." else: yield FAIL, ( "Whitespace and non-breaking space have differing width:" " Whitespace ({}) is {} font units wide, non-breaking space" " ({}) is {} font units wide. Both should be positive and the" " same.").format(space_name, space_width, nbsp_name, nbsp_width)
def com_google_fonts_check_whitespace_widths(ttFont): """Whitespace and non-breaking space have the same width?""" from fontbakery.utils import get_glyph_name space_name = get_glyph_name(ttFont, 0x0020) nbsp_name = get_glyph_name(ttFont, 0x00A0) space_width = ttFont['hmtx'][space_name][0] nbsp_width = ttFont['hmtx'][nbsp_name][0] if space_width > 0 and space_width == nbsp_width: yield PASS, "Whitespace and non-breaking space have the same width." else: yield FAIL, ("Whitespace and non-breaking space have differing width:" " Whitespace ({}) is {} font units wide, non-breaking space" " ({}) is {} font units wide. Both should be positive and the" " same.").format(space_name, space_width, nbsp_name, nbsp_width)
def com_google_fonts_check_048(ttFont): """Font has **proper** whitespace glyph names?""" from fontbakery.utils import get_glyph_name def getGlyphEncodings(font, names): result = set() for subtable in font['cmap'].tables: if subtable.isUnicode(): for codepoint, name in subtable.cmap.items(): if name in names: result.add(codepoint) return result if ttFont['post'].formatType == 3.0: yield SKIP, "Font has version 3 post table." else: failed = False space_enc = getGlyphEncodings(ttFont, ["uni0020", "space"]) nbsp_enc = getGlyphEncodings( ttFont, ["uni00A0", "nonbreakingspace", "nbspace", "nbsp"]) space = get_glyph_name(ttFont, 0x0020) if 0x0020 not in space_enc: failed = True yield FAIL, Message("bad20", ("Glyph 0x0020 is called \"{}\":" " Change to \"space\"" " or \"uni0020\"").format(space)) nbsp = get_glyph_name(ttFont, 0x00A0) if 0x00A0 not in nbsp_enc: if 0x00A0 in space_enc: # This is OK. # Some fonts use the same glyph for both space and nbsp. pass else: failed = True yield FAIL, Message("badA0", ("Glyph 0x00A0 is called \"{}\":" " Change to \"nbsp\"" " or \"uni00A0\"").format(nbsp)) if failed is False: yield PASS, "Font has **proper** whitespace glyph names."
def com_google_fonts_check_048(ttFont): """Font has **proper** whitespace glyph names?""" from fontbakery.utils import get_glyph_name def getGlyphEncodings(font, names): result = set() for subtable in font['cmap'].tables: if subtable.isUnicode(): for codepoint, name in subtable.cmap.items(): if name in names: result.add(codepoint) return result if ttFont['post'].formatType == 3.0: yield SKIP, "Font has version 3 post table." else: failed = False space_enc = getGlyphEncodings(ttFont, ["uni0020", "space"]) nbsp_enc = getGlyphEncodings( ttFont, ["uni00A0", "nonbreakingspace", "nbspace", "nbsp"]) space = get_glyph_name(ttFont, 0x0020) if 0x0020 not in space_enc: failed = True yield FAIL, Message("bad20", ("Glyph 0x0020 is called \"{}\":" " Change to \"space\"" " or \"uni0020\"").format(space)) nbsp = get_glyph_name(ttFont, 0x00A0) if 0x00A0 not in nbsp_enc: if 0x00A0 in space_enc: # This is OK. # Some fonts use the same glyph for both space and nbsp. pass else: failed = True yield FAIL, Message("badA0", ("Glyph 0x00A0 is called \"{}\":" " Change to \"nbsp\"" " or \"uni00A0\"").format(nbsp)) if failed is False: yield PASS, "Font has **proper** whitespace glyph names."
def com_google_fonts_check_whitespace_widths(ttFont): """Whitespace and non-breaking space have the same width?""" from fontbakery.utils import get_glyph_name space_name = get_glyph_name(ttFont, 0x0020) nbsp_name = get_glyph_name(ttFont, 0x00A0) space_width = ttFont['hmtx'][space_name][0] nbsp_width = ttFont['hmtx'][nbsp_name][0] if space_width > 0 and space_width == nbsp_width: yield PASS, "Whitespace and non-breaking space have the same width." else: yield FAIL,\ Message("different-widths", f"Whitespace and non-breaking space have differing width:" f" Whitespace ({space_name})" f" is {space_width} font units wide," f" non-breaking space ({nbsp_name})" f" is {nbsp_width} font units wide." f" Both should be positive and the same.")
def com_google_fonts_check_049(ttFont): """Whitespace glyphs have ink?""" from fontbakery.utils import get_glyph_name def glyphHasInk(font, name): """Checks if specified glyph has any ink. That is, that it has at least one defined contour associated. Composites are considered to have ink if any of their components have ink. Args: font: the font glyph_name: The name of the glyph to check for ink. Returns: True if the font has at least one contour associated with it. """ glyph = font['glyf'].glyphs[name] glyph.expand(font['glyf']) if not glyph.isComposite(): if glyph.numberOfContours == 0: return False (coords, _, _) = glyph.getCoordinates(font['glyf']) # you need at least 3 points to draw return len(coords) > 2 # composite is blank if composed of blanks # if you setup a font with cycles you are just a bad person # Dave: lol, bad people exist, so put a recursion in this recursion for glyph_name in glyph.getComponentNames(glyph.components): if glyphHasInk(font, glyph_name): return True return False # code-points for all "whitespace" chars: WHITESPACE_CHARACTERS = [ 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x0020, 0x0085, 0x00A0, 0x1680, 0x2000, 0x2001, 0x2002, 0x2003, 0x2004, 0x2005, 0x2006, 0x2007, 0x2008, 0x2009, 0x200A, 0x2028, 0x2029, 0x202F, 0x205F, 0x3000, 0x180E, 0x200B, 0x2060, 0xFEFF ] failed = False for codepoint in WHITESPACE_CHARACTERS: g = get_glyph_name(ttFont, codepoint) if g is not None and glyphHasInk(ttFont, g): failed = True yield FAIL, ("Glyph \"{}\" has ink." " It needs to be replaced by" " an empty glyph.").format(g) if not failed: yield PASS, "There is no whitespace glyph with ink."
def com_google_fonts_check_049(ttFont): """Whitespace glyphs have ink?""" from fontbakery.utils import get_glyph_name def glyphHasInk(font, name): """Checks if specified glyph has any ink. That is, that it has at least one defined contour associated. Composites are considered to have ink if any of their components have ink. Args: font: the font glyph_name: The name of the glyph to check for ink. Returns: True if the font has at least one contour associated with it. """ glyph = font['glyf'].glyphs[name] glyph.expand(font['glyf']) if not glyph.isComposite(): if glyph.numberOfContours == 0: return False (coords, _, _) = glyph.getCoordinates(font['glyf']) # you need at least 3 points to draw return len(coords) > 2 # composite is blank if composed of blanks # if you setup a font with cycles you are just a bad person # Dave: lol, bad people exist, so put a recursion in this recursion for glyph_name in glyph.getComponentNames(glyph.components): if glyphHasInk(font, glyph_name): return True return False # code-points for all "whitespace" chars: WHITESPACE_CHARACTERS = [ 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x0020, 0x0085, 0x00A0, 0x1680, 0x2000, 0x2001, 0x2002, 0x2003, 0x2004, 0x2005, 0x2006, 0x2007, 0x2008, 0x2009, 0x200A, 0x2028, 0x2029, 0x202F, 0x205F, 0x3000, 0x180E, 0x200B, 0x2060, 0xFEFF ] failed = False for codepoint in WHITESPACE_CHARACTERS: g = get_glyph_name(ttFont, codepoint) if g is not None and glyphHasInk(ttFont, g): failed = True yield FAIL, ("Glyph \"{}\" has ink." " It needs to be replaced by" " an empty glyph.").format(g) if not failed: yield PASS, "There is no whitespace glyph with ink."
def com_google_fonts_check_049(ttFont): """Whitespace glyphs have ink?""" from fontbakery.utils import get_glyph_name, glyph_has_ink # code-points for all "whitespace" chars: WHITESPACE_CHARACTERS = [ 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x0020, 0x0085, 0x00A0, 0x1680, 0x2000, 0x2001, 0x2002, 0x2003, 0x2004, 0x2005, 0x2006, 0x2007, 0x2008, 0x2009, 0x200A, 0x2028, 0x2029, 0x202F, 0x205F, 0x3000, 0x180E, 0x200B, 0x2060, 0xFEFF ] failed = False for codepoint in WHITESPACE_CHARACTERS: g = get_glyph_name(ttFont, codepoint) if g is not None and glyph_has_ink(ttFont, g): failed = True yield FAIL, ("Glyph \"{}\" has ink." " It needs to be replaced by" " an empty glyph.").format(g) if not failed: yield PASS, "There is no whitespace glyph with ink."
def com_google_fonts_check_whitespace_ink(ttFont): """Whitespace glyphs have ink?""" from fontbakery.utils import get_glyph_name, glyph_has_ink # code-points for all "whitespace" chars: WHITESPACE_CHARACTERS = [ 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x0020, 0x0085, 0x00A0, 0x1680, 0x2000, 0x2001, 0x2002, 0x2003, 0x2004, 0x2005, 0x2006, 0x2007, 0x2008, 0x2009, 0x200A, 0x2028, 0x2029, 0x202F, 0x205F, 0x3000, 0x180E, 0x200B, 0x2060, 0xFEFF ] failed = False for codepoint in WHITESPACE_CHARACTERS: g = get_glyph_name(ttFont, codepoint) if g is not None and glyph_has_ink(ttFont, g): failed = True yield FAIL, ("Glyph \"{}\" has ink." " It needs to be replaced by" " an empty glyph.").format(g) if not failed: yield PASS, "There is no whitespace glyph with ink."
def com_google_fonts_check_whitespace_glyphnames(ttFont): """Font has **proper** whitespace glyph names?""" from fontbakery.utils import get_glyph_name # AGL recommended names, according to Adobe Glyph List for new fonts: AGL_RECOMMENDED_0020 = {'space'} AGL_RECOMMENDED_00A0 = {"uni00A0", "space"} # "space" is in this set because some fonts # use the same glyph for U+0020 and U+00A0 # Including it here also removes a warning # when U+0020 is wrong, but U+00A0 is okay. # AGL compliant names, but not recommended for new fonts: AGL_COMPLIANT_BUT_NOT_RECOMMENDED_0020 = {'uni0020', 'u0020', 'u00020', 'u000020'} AGL_COMPLIANT_BUT_NOT_RECOMMENDED_00A0 = {'nonbreakingspace', 'nbspace', 'u00A0', 'u000A0', 'u0000A0'} if ttFont['post'].formatType == 3.0: yield SKIP, "Font has version 3 post table." else: passed = True space = get_glyph_name(ttFont, 0x0020) if not space: passed = False yield FAIL,\ Message('missing-0020', 'Glyph 0x0020 is missing a glyph name!') elif space in AGL_RECOMMENDED_0020: pass elif space in AGL_COMPLIANT_BUT_NOT_RECOMMENDED_0020: passed = False yield WARN,\ Message('not-recommended-0020', f'Glyph 0x0020 is called "{space}":' f' Change to "space"') else: passed = False yield FAIL,\ Message('non-compliant-0020', f'Glyph 0x0020 is called "{space}":' f' Change to "space"') nbsp = get_glyph_name(ttFont, 0x00A0) if not nbsp: yield FAIL,\ Message('missing-00a0', 'Glyph 0x00A0 is missing a glyph name!') elif nbsp == space: # This is OK. # Some fonts use the same glyph for both space and nbsp. pass elif nbsp in AGL_RECOMMENDED_00A0: pass elif nbsp in AGL_COMPLIANT_BUT_NOT_RECOMMENDED_00A0: passed = False yield WARN,\ Message('not-recommended-00a0', f'Glyph 0x00A0 is called "{nbsp}":' f' Change to "uni00A0"') else: passed = False yield FAIL,\ Message('non-compliant-00a0', f'Glyph 0x00A0 is called "{nbsp}":' f' Change to "uni00A0"') if passed: yield PASS, "Font has **AGL recommended** names for whitespace glyphs."