Exemple #1
0
def test_check_fsselection_matches_macstyle():
    """Check if OS/2 fsSelection matches head macStyle bold and italic bits."""
    from fontbakery.profiles.os2 import \
      com_adobe_fonts_check_fsselection_matches_macstyle as check
    from fontbakery.constants import FsSelection

    test_font_path = TEST_FILE("nunito/Nunito-Regular.ttf")

    # try a regular (not bold, not italic) font
    test_font = TTFont(test_font_path)
    assert_PASS(check(test_font))

    # now turn on bold in OS/2.fsSelection, but not in head.macStyle
    test_font['OS/2'].fsSelection |= FsSelection.BOLD
    message = assert_results_contain(
        check(test_font), FAIL, None)  # FIXME: This needs a message keyword!
    assert 'bold' in message

    # now turn off bold in OS/2.fsSelection so we can focus on italic
    test_font['OS/2'].fsSelection &= ~FsSelection.BOLD

    # now turn on italic in OS/2.fsSelection, but not in head.macStyle
    test_font['OS/2'].fsSelection |= FsSelection.ITALIC
    message = assert_results_contain(
        check(test_font), FAIL, None)  # FIXME: This needs a message keyword!
    assert 'italic' in message
Exemple #2
0
def test_check_name_postscript_vs_cff():
    from fontbakery.profiles.name import com_adobe_fonts_check_name_postscript_vs_cff as check
    test_font = TTFont()
    test_font['CFF '] = fontTools.ttLib.newTable('CFF ')
    test_font['CFF '].cff.fontNames = ['SomeFontName']
    test_font['name'] = fontTools.ttLib.newTable('name')

    test_font['name'].setName(
        'SomeOtherFontName',
        NameID.POSTSCRIPT_NAME,
        PlatformID.WINDOWS,
        WindowsEncodingID.UNICODE_BMP,
        WindowsLanguageID.ENGLISH_USA
    )
    assert_results_contain(check(test_font),
                           FAIL, 'mismatch')

    test_font['name'].setName(
        'SomeFontName',
        NameID.POSTSCRIPT_NAME,
        PlatformID.WINDOWS,
        WindowsEncodingID.UNICODE_BMP,
        WindowsLanguageID.ENGLISH_USA
    )
    assert_PASS(check(test_font))
Exemple #3
0
def test_check_name_postscript_name_consistency():
    from fontbakery.profiles.name import \
        com_adobe_fonts_check_name_postscript_name_consistency as check

    base_path = portable_path("data/test/source-sans-pro/TTF")
    font_path = os.path.join(base_path, 'SourceSansPro-Regular.ttf')
    test_font = TTFont(font_path)

    # SourceSansPro-Regular only has one name ID 6 entry (for Windows),
    # let's add another one for Mac that matches the Windows entry:
    test_font['name'].setName(
        'SourceSansPro-Regular',
        NameID.POSTSCRIPT_NAME,
        PlatformID.MACINTOSH,
        MacintoshEncodingID.ROMAN,
        MacintoshLanguageID.ENGLISH
    )
    assert_PASS(check(test_font))

    # ...now let's change the Mac name ID 6 entry to something else:
    test_font['name'].setName(
        'YetAnotherFontName',
        NameID.POSTSCRIPT_NAME,
        PlatformID.MACINTOSH,
        MacintoshEncodingID.ROMAN,
        MacintoshLanguageID.ENGLISH
    )
    assert_results_contain(check(test_font),
                           FAIL, 'inconsistency')
def test_check_varfont_regular_ital_coord():
    """ The variable font 'ital' (Italic) axis coordinate
        must be zero on the 'Regular' instance. """
    from fontbakery.profiles.fvar import com_google_fonts_check_varfont_regular_ital_coord as check
    from fontbakery.profiles.shared_conditions import regular_ital_coord
    from fontTools.ttLib.tables._f_v_a_r import Axis

    # Our reference varfont, CabinVFBeta.ttf, lacks an 'ital' variation axis.
    ttFont = TTFont("data/test/cabinvfbeta/CabinVFBeta.ttf")

    # So we add one:
    new_axis = Axis()
    new_axis.axisTag = "ital"
    ttFont["fvar"].axes.append(new_axis)

    # and specify a bad coordinate for the Regular:
    ttFont["fvar"].instances[0].coordinates["ital"] = 123
    # Note: I know the correct instance index for this hotfix because
    # I inspected the our reference CabinVF using ttx

    # then we test the code of the regular_ital_coord condition:
    regular_italic_coord = regular_ital_coord(ttFont)

    # So it must FAIL the test
    assert_results_contain(check(ttFont,
                                 regular_italic_coord), FAIL, 'non-zero',
                           'with a bad Regular:ital coordinate (123)...')

    # We then fix the Regular:ital coordinate:
    regular_italic_coord = 0

    # and now this should PASS the test:
    assert_PASS(check(ttFont, regular_italic_coord),
                'with a good Regular:ital coordinate (zero)...')
Exemple #5
0
def test_check_fsselection_matches_macstyle():
    """Check if OS/2 fsSelection matches head macStyle bold and italic bits."""
    check = CheckTester(opentype_profile,
                        "com.adobe.fonts/check/fsselection_matches_macstyle")
    from fontbakery.constants import FsSelection

    test_font_path = TEST_FILE("nunito/Nunito-Regular.ttf")

    # try a regular (not bold, not italic) font
    test_font = TTFont(test_font_path)
    assert_PASS(check(test_font))

    # now turn on bold in OS/2.fsSelection, but not in head.macStyle
    test_font['OS/2'].fsSelection |= FsSelection.BOLD
    message = assert_results_contain(check(test_font),
                                     FAIL, "fsselection-macstyle-bold")
    assert 'bold' in message

    # now turn off bold in OS/2.fsSelection so we can focus on italic
    test_font['OS/2'].fsSelection &= ~FsSelection.BOLD

    # now turn on italic in OS/2.fsSelection, but not in head.macStyle
    test_font['OS/2'].fsSelection |= FsSelection.ITALIC
    message = assert_results_contain(check(test_font),
                                     FAIL, "fsselection-macstyle-italic")
    assert 'italic' in message
def test_check_varfont_wdth_valid_range():
    """ The variable font 'wdth' (Width) axis coordinate
        must be within spec range of 1 to 1000 on all instances. """
    from fontbakery.profiles.fvar import com_google_fonts_check_varfont_wdth_valid_range as check

    # Our reference varfont, CabinVFBeta.ttf, has
    # all instances within the 1-1000 range
    ttFont = TTFont("data/test/cabinvfbeta/CabinVFBeta.ttf")

    # so it must PASS the test:
    assert_PASS(check(ttFont), 'with a good varfont...')

    # We then introduce a bad value:
    ttFont["fvar"].instances[0].coordinates["wdth"] = 0

    # And it must FAIL the test
    assert_results_contain(check(ttFont), FAIL, 'out-of-range',
                           'with wght=0...')

    # And yet another bad value:
    ttFont["fvar"].instances[0].coordinates["wdth"] = 1001

    # Should also FAIL:
    assert_results_contain(check(ttFont), FAIL, 'out-of-range',
                           'with wght=1001...')
def test_check_varfont_regular_wght_coord():
    """ The variable font 'wght' (Weight) axis coordinate
        must be 400 on the 'Regular' instance. """
    from fontbakery.profiles.fvar import com_google_fonts_check_varfont_regular_wght_coord as check
    from fontbakery.profiles.shared_conditions import regular_wght_coord

    # Our reference varfont, CabinVFBeta.ttf, has
    # a good Regular:wght coordinate
    ttFont = TTFont("data/test/cabinvfbeta/CabinVFBeta.ttf")
    regular_weight_coord = regular_wght_coord(ttFont)

    # So it must PASS the test
    assert_PASS(check(ttFont, regular_weight_coord),
                'with a good Regular:wght coordinate...')

    # We then change the value so it must FAIL:
    ttFont["fvar"].instances[0].coordinates["wght"] = 500

    # Then re-read the coord:
    regular_weight_coord = regular_wght_coord(ttFont)

    # and now this should FAIL the test:
    assert_results_contain(check(ttFont,
                                 regular_weight_coord), FAIL, 'not-400',
                           'with a bad Regular:wght coordinate (500)...')
Exemple #8
0
def test_check_unwanted_tables():
    """ Are there unwanted tables ? """
    from fontbakery.profiles.universal import com_google_fonts_check_unwanted_tables as check

    unwanted_tables = [
        "FFTM",  # FontForge
        "TTFA",  # TTFAutohint
        "TSI0",  # TSI* = VTT
        "TSI1",
        "TSI2",
        "TSI3",
        "TSI5",
        "prop",  # FIXME: Why is this one unwanted?
        "MVAR",  # Bugs in DirectWrite
    ]
    # Our reference Mada Regular font is good here:
    ttFont = TTFont(TEST_FILE("mada/Mada-Regular.ttf"))

    # So it must PASS the check:
    assert_PASS(check(ttFont), 'with a good font...')

    # We now add unwanted tables one-by-one to validate the FAIL code-path:
    for unwanted in unwanted_tables:
        ttFont = TTFont(TEST_FILE("mada/Mada-Regular.ttf"))
        ttFont.reader.tables[unwanted] = "foo"
        assert_results_contain(
            check(ttFont),
            FAIL,
            None,  # FIXME: This needs a message keyword
            f'with unwanted table {unwanted} ...')
Exemple #9
0
def test_check_whitespace_glyphs():
    """ Font contains glyphs for whitespace characters ? """
    from fontbakery.profiles.universal import com_google_fonts_check_whitespace_glyphs as check
    from fontbakery.profiles.shared_conditions import missing_whitespace_chars

    # Our reference Mada Regular font is good here:
    ttFont = TTFont(TEST_FILE("mada/Mada-Regular.ttf"))
    missing = missing_whitespace_chars(ttFont)

    # So it must PASS the check:
    assert_PASS(check(ttFont, missing), 'with a good font...')

    # Then we remove the nbsp char (0x00A0) so that we get a FAIL:
    for table in ttFont['cmap'].tables:
        if 0x00A0 in table.cmap:
            del table.cmap[0x00A0]

    missing = missing_whitespace_chars(ttFont)
    assert_results_contain(check(ttFont, missing), FAIL,
                           'missing-whitespace-glyph-0x00A0',
                           'with a font lacking a nbsp (0x00A0)...')

    # restore original Mada Regular font:
    ttFont = TTFont(TEST_FILE("mada/Mada-Regular.ttf"))

    # And finally remove the space character (0x0020) to get another FAIL:
    for table in ttFont['cmap'].tables:
        if 0x0020 in table.cmap:
            del table.cmap[0x0020]

    missing = missing_whitespace_chars(ttFont)
    assert_results_contain(check(ttFont, missing), FAIL,
                           'missing-whitespace-glyph-0x0020',
                           'with a font lacking a space (0x0020)...')
Exemple #10
0
def test_check_unique_glyphnames():
    """ Font contains unique glyph names? """
    import io
    from fontbakery.profiles.universal import com_google_fonts_check_unique_glyphnames as check

    test_font_path = TEST_FILE("nunito/Nunito-Regular.ttf")
    test_font = TTFont(test_font_path)
    assert_PASS(check(test_font))

    # Fonttools renames duplicate glyphs with #1, #2, ... on load.
    # Code snippet from https://github.com/fonttools/fonttools/issues/149.
    glyph_names = test_font.getGlyphOrder()
    glyph_names[2] = glyph_names[3]
    # Load again, we changed the font directly.
    test_font = TTFont(test_font_path)
    test_font.setGlyphOrder(glyph_names)
    test_font['post']  # Just access the data to make fonttools generate it.
    test_file = io.BytesIO()
    test_font.save(test_file)
    test_font = TTFont(test_file)
    message = assert_results_contain(
        check(test_font), FAIL, None)  # FIXME: This needs a message keyword
    assert "space" in message

    # Upgrade to post format 3.0 and roundtrip data to update TTF object.
    test_font = TTFont(test_font_path)
    test_font.setGlyphOrder(glyph_names)
    test_font["post"].formatType = 3.0
    test_file = io.BytesIO()
    test_font.save(test_file)
    test_font = TTFont(test_file)
    assert_SKIP(check(test_font))
Exemple #11
0
def test_check_family_bold_italic_unique_for_nameid1():
    """Check that OS/2.fsSelection bold/italic settings are unique within each
    Compatible Family group (i.e. group of up to 4 with same NameID1)"""
    from fontbakery.profiles.os2 import \
      com_adobe_fonts_check_family_bold_italic_unique_for_nameid1 as check
    from fontbakery.constants import FsSelection

    base_path = portable_path("data/test/source-sans-pro/OTF")

    # these fonts have the same NameID1
    font_names = [
        'SourceSansPro-Regular.otf', 'SourceSansPro-Bold.otf',
        'SourceSansPro-It.otf', 'SourceSansPro-BoldIt.otf'
    ]

    font_paths = [os.path.join(base_path, n) for n in font_names]
    test_fonts = [TTFont(x) for x in font_paths]

    # the family should be correctly constructed
    assert_PASS(check(test_fonts))

    # now hack the italic font to also have the bold bit set
    test_fonts[2]['OS/2'].fsSelection |= FsSelection.BOLD

    # we should get a failure due to two fonts with both bold & italic set
    message = assert_results_contain(
        check(test_fonts), FAIL, None)  # FIXME: This needs a message keyword!
    assert message == ("Family 'Source Sans Pro' has 2 fonts (should be no"
                       " more than 1) with the same OS/2.fsSelection"
                       " bold & italic settings: Bold=True, Italic=True")
Exemple #12
0
def test_check_varfont_regular_opsz_coord():
    """ The variable font 'opsz' (Optical Size) axis coordinate
        should be between 9 and 13 on the 'Regular' instance. """
    check = CheckTester(opentype_profile,
                        "com.google.fonts/check/varfont/regular_opsz_coord")
    from fontTools.ttLib.tables._f_v_a_r import Axis

    # Our reference varfont, CabinVFBeta.ttf, lacks an 'opsz' variation axis.
    ttFont = TTFont("data/test/cabinvfbeta/CabinVFBeta.ttf")

    # So we add one:
    new_axis = Axis()
    new_axis.axisTag = "opsz"
    ttFont["fvar"].axes.append(new_axis)

    # and specify a bad coordinate for the Regular:
    ttFont["fvar"].instances[0].coordinates["opsz"] = 8
    # Note: I know the correct instance index for this hotfix because
    # I inspected the our reference CabinVF using ttx

    # Then we ensure the problem is detected:
    assert_results_contain(check(ttFont),
                           WARN, 'out-of-range',
                           'with a bad Regular:opsz coordinate (8)...')

    # We try yet another bad value
    # and the check should detect the problem:
    assert_results_contain(check(ttFont, {"regular_opsz_coord": 14}),
                           WARN, 'out-of-range',
                           'with another bad Regular:opsz value (14)...')

    # We then test with good default opsz values:
    for value in [9, 10, 11, 12, 13]:
        assert_PASS(check(ttFont, {"regular_opsz_coord": value}),
                    f'with a good Regular:opsz coordinate ({value})...')
Exemple #13
0
def test_check_family_underline_thickness(mada_ttFonts):
    """ Fonts have consistent underline thickness ? """
    check = CheckTester(opentype_profile,
                        "com.google.fonts/check/family/underline_thickness")

    # We start with our reference Mada font family,
    # which we know has the same value of post.underlineThickness
    # across all of its font files, based on our inspection
    # of the file contents using TTX.
    #
    # So the check should PASS in this case:
    assert_PASS(check(mada_ttFonts), 'with a good family.')

    # Then we introduce the issue by setting a
    # different underlineThickness value in just
    # one of the font files:
    value = mada_ttFonts[0]['post'].underlineThickness
    incorrect_value = value + 1
    mada_ttFonts[0]['post'].underlineThickness = incorrect_value

    # And now re-running the check on the modified
    # family should result in a FAIL:
    assert_results_contain(check(mada_ttFonts), FAIL,
                           "inconsistent-underline-thickness",
                           'with an inconsistent family.')
Exemple #14
0
def test_check_ttx_roundtrip():
    """ Checking with fontTools.ttx """
    check = CheckTester(universal_profile,
                        "com.google.fonts/check/ttx-roundtrip")

    font = TEST_FILE("mada/Mada-Regular.ttf")
    assert_PASS(check(font))
Exemple #15
0
def test_check_glyf_unused_data():
    """ Is there any unused data at the end of the glyf table? """
    check = CheckTester(opentype_profile,
                        "com.google.fonts/check/glyf_unused_data")

    font = TEST_FILE("nunito/Nunito-Regular.ttf")
    ttFont = TTFont(font)
    assert_PASS(check(ttFont))

    # Always start with a fresh copy, as fT works lazily. Accessing certain data
    # can prevent the test from working because we rely on uninitialized
    # behavior.
    ttFont = TTFont(font)
    ttFont["loca"].locations.pop()
    _file = io.BytesIO()
    ttFont.save(_file)
    ttFont = TTFont(_file)
    ttFont.reader.file.name = font
    assert_results_contain(check(ttFont),
                           FAIL, 'unreachable-data')

    ttFont = TTFont(font)
    ttFont["loca"].locations.append(50000)
    _file = io.BytesIO()
    ttFont.save(_file)
    ttFont = TTFont(_file)
    ttFont.reader.file.name = font
    assert_results_contain(check(ttFont),
                           FAIL, 'missing-data')
Exemple #16
0
def test_check_family_max_4_fonts_per_family_name():
    from fontbakery.profiles.name import \
        com_adobe_fonts_check_family_max_4_fonts_per_family_name as check

    base_path = portable_path("data/test/source-sans-pro/OTF")

    font_names = [
        'SourceSansPro-Black.otf', 'SourceSansPro-BlackIt.otf',
        'SourceSansPro-Bold.otf', 'SourceSansPro-BoldIt.otf',
        'SourceSansPro-ExtraLight.otf', 'SourceSansPro-ExtraLightIt.otf',
        'SourceSansPro-It.otf', 'SourceSansPro-Light.otf',
        'SourceSansPro-LightIt.otf', 'SourceSansPro-Regular.otf',
        'SourceSansPro-Semibold.otf', 'SourceSansPro-SemiboldIt.otf'
    ]

    font_paths = [os.path.join(base_path, n) for n in font_names]

    test_fonts = [TTFont(x) for x in font_paths]

    # try fonts with correct family name grouping
    assert_PASS(check(test_fonts))

    # now set 5 of the fonts to have the same family name
    for font in test_fonts[:5]:
        name_records = font['name'].names
        for name_record in name_records:
            if name_record.nameID == 1:
                # print(repr(name_record.string))
                name_record.string = 'foobar'.encode('utf-16be')

    assert_results_contain(check(test_fonts), FAIL, 'too-many')
Exemple #17
0
def test_check_unique_glyphnames():
    """ Font contains unique glyph names? """
    check = CheckTester(universal_profile,
                        "com.google.fonts/check/unique_glyphnames")

    ttFont = TTFont(TEST_FILE("nunito/Nunito-Regular.ttf"))
    assert_PASS(check(ttFont))

    # Fonttools renames duplicate glyphs with #1, #2, ... on load.
    # Code snippet from https://github.com/fonttools/fonttools/issues/149.
    glyph_names = ttFont.getGlyphOrder()
    glyph_names[2] = glyph_names[3]

    # Load again, we changed the font directly.
    ttFont = TTFont(TEST_FILE("nunito/Nunito-Regular.ttf"))
    ttFont.setGlyphOrder(glyph_names)
    ttFont['post']  # Just access the data to make fonttools generate it.
    _file = io.BytesIO()
    _file.name = ttFont.reader.file.name
    ttFont.save(_file)
    ttFont = TTFont(_file)
    message = assert_results_contain(check(ttFont), FAIL,
                                     "duplicated-glyph-names")
    assert "space" in message

    # Upgrade to post format 3.0 and roundtrip data to update TTF object.
    ttFont = TTFont(TEST_FILE("nunito/Nunito-Regular.ttf"))
    ttFont.setGlyphOrder(glyph_names)
    ttFont["post"].formatType = 3.0
    _file = io.BytesIO()
    _file.name = ttFont.reader.file.name
    ttFont.save(_file)
    ttFont = TTFont(_file)
    assert_SKIP(check(ttFont))
Exemple #18
0
def test_check_name_match_familyname_fullfont():
    """ Does full font name begin with the font family name? """
    from fontbakery.profiles.name import com_google_fonts_check_name_match_familyname_fullfont as check
    # Our reference Mada Regular is known to be good
    ttFont = TTFont(TEST_FILE("mada/Mada-Regular.ttf"))

    # So it must PASS the check:
    assert_PASS(check(ttFont), 'with a good font...')

    # alter the full-font-name prepending a bad prefix:
    for i, name in enumerate(ttFont["name"].names):
        if name.nameID == NameID.FULL_FONT_NAME:
            ttFont["name"].names[i].string = "bad-prefix".encode(
                name.getEncoding())

    # and make sure the check FAILs:
    assert_results_contain(
        check(ttFont), FAIL, 'does-not', 'with a font in which the family name'
        ' begins with a digit...')

    ttFont = TTFont(TEST_FILE("mada/Mada-Regular.ttf"))
    for i, name in enumerate(ttFont["name"].names):
        if name.nameID == NameID.FULL_FONT_NAME:
            del ttFont["name"].names[i]
    assert_results_contain(check(ttFont), FAIL, 'no-full-font-name',
                           'with no FULL_FONT_NAME entries...')

    ttFont = TTFont(TEST_FILE("mada/Mada-Regular.ttf"))
    for i, name in enumerate(ttFont["name"].names):
        if name.nameID == NameID.FONT_FAMILY_NAME:
            del ttFont["name"].names[i]
    assert_results_contain(check(ttFont), FAIL, 'no-font-family-name',
                           'with no FONT_FAMILY_NAME entries...')
Exemple #19
0
def test_check_varfont_regular_ital_coord():
    """ The variable font 'ital' (Italic) axis coordinate
        must be zero on the 'Regular' instance. """
    check = CheckTester(opentype_profile,
                        "com.google.fonts/check/varfont/regular_ital_coord")
    from fontTools.ttLib.tables._f_v_a_r import Axis

    # Our reference varfont, CabinVFBeta.ttf, lacks an 'ital' variation axis.
    ttFont = TTFont("data/test/cabinvfbeta/CabinVFBeta.ttf")

    # So we add one:
    new_axis = Axis()
    new_axis.axisTag = "ital"
    ttFont["fvar"].axes.append(new_axis)

    # and specify a bad coordinate for the Regular:
    ttFont["fvar"].instances[0].coordinates["ital"] = 123
    # Note: I know the correct instance index for this hotfix because
    # I inspected the our reference CabinVF using ttx

    # And with this the check must detect the problem:
    assert_results_contain(check(ttFont),
                           FAIL, 'non-zero',
                           'with a bad Regular:ital coordinate (123)...')

    # but with zero it must PASS the check:
    assert_PASS(check(ttFont, {"regular_ital_coord": 0}),
                'with a good Regular:ital coordinate (zero)...')
Exemple #20
0
def test_check_family_underline_thickness(mada_ttFonts):
    """ Fonts have consistent underline thickness ? """
    from fontbakery.profiles.post import com_google_fonts_check_family_underline_thickness as check

    # We start with our reference Mada font family,
    # which we know has the same value of post.underlineThickness
    # across all of its font files, based on our inspection
    # of the file contents using TTX.
    #
    # So the check should PASS in this case:
    assert_PASS(check(mada_ttFonts), 'with a good family.')

    # Then we introduce the issue by setting a
    # different underlineThickness value in just
    # one of the font files:
    value = mada_ttFonts[0]['post'].underlineThickness
    incorrect_value = value + 1
    mada_ttFonts[0]['post'].underlineThickness = incorrect_value

    # And now re-running the check on the modified
    # family should result in a FAIL:
    assert_results_contain(
        check(mada_ttFonts),
        FAIL,
        None,  # FIXME: This needs a message keyword!
        'with an inconsistent family.')
Exemple #21
0
def test_check_whitespace_ink():
    """ Whitespace glyphs have ink? """
    check = CheckTester(universal_profile,
                        "com.google.fonts/check/whitespace_ink")

    test_font = TTFont(TEST_FILE("nunito/Nunito-Regular.ttf"))
    assert_PASS(check(test_font))

    test_font["cmap"].tables[0].cmap[0x0020] = "uni1E17"
    assert_results_contain(
        check(test_font), FAIL, 'has-ink',
        'for whitespace character having composites (with ink).')

    test_font["cmap"].tables[0].cmap[0x0020] = "scedilla"
    assert_results_contain(
        check(test_font), FAIL, 'has-ink',
        'for whitespace character having outlines (with ink).')

    import fontTools.pens.ttGlyphPen
    pen = fontTools.pens.ttGlyphPen.TTGlyphPen(test_font.getGlyphSet())
    pen.addComponent("space", (1, 0, 0, 1, 0, 0))
    test_font["glyf"].glyphs["uni200B"] = pen.glyph()
    assert_results_contain(
        check(test_font),
        FAIL,
        'has-ink',  # should we give is a separate keyword? This looks wrong.
        'for whitespace character having composites (without ink).')
Exemple #22
0
def test_check_whitespace_glyphs():
    """ Font contains glyphs for whitespace characters? """
    check = CheckTester(universal_profile,
                        "com.google.fonts/check/whitespace_glyphs")

    # Our reference Mada Regular font is good here:
    ttFont = TTFont(TEST_FILE("mada/Mada-Regular.ttf"))
    assert_PASS(check(ttFont), 'with a good font...')

    # We remove the nbsp char (0x00A0)
    for table in ttFont['cmap'].tables:
        if 0x00A0 in table.cmap:
            del table.cmap[0x00A0]

    # And make sure the problem is detected:
    assert_results_contain(check(ttFont), FAIL,
                           'missing-whitespace-glyph-0x00A0',
                           'with a font lacking a nbsp (0x00A0)...')

    # restore original Mada Regular font:
    ttFont = TTFont(TEST_FILE("mada/Mada-Regular.ttf"))

    # And finally do the same with the space character (0x0020):
    for table in ttFont['cmap'].tables:
        if 0x0020 in table.cmap:
            del table.cmap[0x0020]
    assert_results_contain(check(ttFont), FAIL,
                           'missing-whitespace-glyph-0x0020',
                           'with a font lacking a space (0x0020)...')
Exemple #23
0
def test_check_glyf_unused_data():
    """ Is there any unused data at the end of the glyf table? """
    from fontbakery.profiles.glyf import com_google_fonts_check_glyf_unused_data as check

    test_font_path = TEST_FILE("nunito/Nunito-Regular.ttf")

    test_font = TTFont(test_font_path)
    assert_PASS(check(test_font))

    # Always start with a fresh copy, as fT works lazily. Accessing certain data
    # can prevent the test from working because we rely on uninitialized
    # behavior.
    test_font = TTFont(test_font_path)
    test_font["loca"].locations.pop()
    test_file = io.BytesIO()
    test_font.save(test_file)
    test_font = TTFont(test_file)
    assert_results_contain(check(test_font), FAIL, 'unreachable-data')

    test_font = TTFont(test_font_path)
    test_font["loca"].locations.append(50000)
    test_file = io.BytesIO()
    test_font.save(test_file)
    test_font = TTFont(test_file)
    assert_results_contain(check(test_font), FAIL, 'missing-data')
Exemple #24
0
def test_check_unitsperem():
    """ Checking unitsPerEm value is reasonable. """
    from fontbakery.profiles.head import com_google_fonts_check_unitsperem as check

    # In this test we'll forge several known-good and known-bad values.
    # We'll use Mada Regular to start with:
    ttFont = TTFont(TEST_FILE("mada/Mada-Regular.ttf"))

    for good_value in [16, 32, 64, 128, 256, 512, 1000,
                       1024, 2000, 2048, 4096, 8192, 16384]:
        ttFont['head'].unitsPerEm = good_value
        assert_PASS(check(ttFont),
                    f'with a good value of unitsPerEm = {good_value} ...')

    for warn_value in [20, 50, 100, 500, 4000]:
        ttFont['head'].unitsPerEm = warn_value
        assert_results_contain(check(ttFont),
                               WARN, 'suboptimal',
                               f'with a value of unitsPerEm = {warn_value} ...')

    # These are arbitrarily chosen bad values:
    for bad_value in [0, 1, 2, 4, 8, 10, 15, 16385, 32768]:
        ttFont['head'].unitsPerEm = bad_value
        assert_results_contain(check(ttFont),
                               FAIL, 'out-of-range',
                               f'with a bad value of unitsPerEm = {bad_value} ...')
Exemple #25
0
def test_check_rupee():
    """ Ensure indic fonts have the Indian Rupee Sign glyph. """
    check = CheckTester(universal_profile, "com.google.fonts/check/rupee")

    # FIXME: This should be possible:
    #          The `assert_SKIP` helper should be able to detect when a
    #          check skips due to unmet @conditions.
    #          For now it only detects explicit SKIP messages instead.
    #
    # non_indic = TTFont(TEST_FILE("mada/Mada-Regular.ttf"))
    # assert_SKIP(check(non_indic),
    #             "with a non-indic font.")
    #
    #        But for now we have to do this:
    #
    from fontbakery.profiles.shared_conditions import is_indic_font
    print("Ensure the check will SKIP when dealing with a non-indic font...")
    non_indic = TTFont(TEST_FILE("mada/Mada-Regular.ttf"))
    assert is_indic_font(non_indic) == False

    # This one is good:
    ttFont = TTFont(
        TEST_FILE(
            "indic-font-with-rupee-sign/NotoSerifDevanagari-Regular.ttf"))
    assert_PASS(check(ttFont),
                "with a sample font that has the Indian Rupee Sign.")

    # But this one lacks the glyph:
    ttFont = TTFont(
        TEST_FILE("indic-font-without-rupee-sign/NotoSansOlChiki-Regular.ttf"))
    assert_results_contain(check(ttFont), FAIL, "missing-rupee",
                           "with a sample font missing it.")
Exemple #26
0
def test_check_family_win_ascent_and_descent(mada_ttFonts):
    """ Checking OS/2 usWinAscent & usWinDescent. """
    check = CheckTester(
        universal_profile,
        "com.google.fonts/check/family/win_ascent_and_descent")
    from fontbakery.profiles.shared_conditions import vmetrics

    # Our reference Mada Regular is know to be bad here.
    ttFont = TTFont(TEST_FILE("mada/Mada-Regular.ttf"))

    # But we fix it first to test the PASS code-path:
    vm = vmetrics(mada_ttFonts)
    ttFont['OS/2'].usWinAscent = vm['ymax']
    ttFont['OS/2'].usWinDescent = abs(vm['ymin'])
    assert_PASS(check(ttFont), 'with a good font...')

    # Then we break it:
    ttFont[
        'OS/2'].usWinAscent = 0  # FIXME: this should be bad as well: vm['ymax'] - 1
    ttFont['OS/2'].usWinDescent = abs(vm['ymin'])
    assert_results_contain(check(ttFont), FAIL, 'ascent',
                           'with a bad OS/2.usWinAscent...')

    # and also this other way of breaking it:
    ttFont['OS/2'].usWinAscent = vm['ymax']
    ttFont[
        'OS/2'].usWinDescent = 0  # FIXME: this should be bad as well: abs(vm['ymin']) - 1
    assert_results_contain(check(ttFont), FAIL, 'descent',
                           'with a bad OS/2.usWinDescent...')
Exemple #27
0
def test_check_unwanted_tables():
    """ Are there unwanted tables ? """
    check = CheckTester(universal_profile,
                        "com.google.fonts/check/unwanted_tables")

    unwanted_tables = [
        "FFTM",  # FontForge
        "TTFA",  # TTFAutohint
        "TSI0",  # TSI* = VTT
        "TSI1",
        "TSI2",
        "TSI3",
        "TSI5",
        "prop",  # FIXME: Why is this one unwanted?
        "MVAR",  # Bugs in DirectWrite
    ]
    # Our reference Mada Regular font is good here:
    ttFont = TTFont(TEST_FILE("mada/Mada-Regular.ttf"))

    # So it must PASS the check:
    assert_PASS(check(ttFont), 'with a good font...')

    # We now add unwanted tables one-by-one to validate the FAIL code-path:
    for unwanted in unwanted_tables:
        ttFont = TTFont(TEST_FILE("mada/Mada-Regular.ttf"))
        ttFont.reader.tables[unwanted] = "foo"
        assert_results_contain(check(ttFont), FAIL, "unwanted-tables",
                               f'with unwanted table {unwanted} ...')
Exemple #28
0
def test_check_required_tables():
    """ Font contains all required tables ? """
    check = CheckTester(universal_profile,
                        "com.google.fonts/check/required_tables")

    required_tables = [
        "cmap", "head", "hhea", "hmtx", "maxp", "name", "OS/2", "post"
    ]
    optional_tables = [
        "cvt ", "fpgm", "loca", "prep", "VORG", "EBDT", "EBLC", "EBSC", "BASE",
        "GPOS", "GSUB", "JSTF", "DSIG", "gasp", "hdmx", "kern", "LTSH", "PCLT",
        "VDMX", "vhea", "vmtx"
    ]
    # Our reference Mada Regular font is good here
    ttFont = TTFont(TEST_FILE("mada/Mada-Regular.ttf"))

    # So it must PASS the check:
    assert_PASS(check(ttFont), 'with a good font...')

    # Now we test the special cases for variable fonts:
    #
    # Note: A TTF with an fvar table but no STAT table
    #       is probably a GX font. For now we're OK with
    #       rejecting those by emitting a FAIL in this case.
    #
    # TODO: Maybe we could someday emit a more explicit
    #       message to the users regarding that...
    ttFont.reader.tables["fvar"] = "foo"
    assert_results_contain(check(ttFont), FAIL, "required-tables",
                           'with fvar but no STAT...')

    del ttFont.reader.tables["fvar"]
    ttFont.reader.tables["STAT"] = "foo"
    assert_PASS(check(ttFont), 'with STAT on a non-variable font...')

    # and finally remove what we've just added:
    del ttFont.reader.tables["STAT"]
    # Now we remove required tables one-by-one to validate the FAIL code-path:
    for required in required_tables:
        ttFont = TTFont(TEST_FILE("mada/Mada-Regular.ttf"))
        if required in ttFont.reader.tables:
            del ttFont.reader.tables[required]
        assert_results_contain(check(ttFont), FAIL, "required-tables",
                               f'with missing mandatory table {required} ...')
    # Then, in preparation for the next step, we make sure
    # there's no optional table (by removing them all):
    for optional in optional_tables:
        if optional in ttFont.reader.tables:
            del ttFont.reader.tables[optional]

    # Then re-insert them one by one to validate the INFO code-path:
    for optional in optional_tables:
        ttFont.reader.tables[optional] = "foo"
        # and ensure that the second to last logged message is an
        # INFO status informing the user about it:
        assert_results_contain(check(ttFont), INFO, "required-tables",
                               f'with optional table {required} ...')
        # remove the one we've just inserted before trying the next one:
        del ttFont.reader.tables[optional]
Exemple #29
0
def test_check_xavgcharwidth():
    """ Check if OS/2 xAvgCharWidth is correct. """
    from fontbakery.profiles.os2 import com_google_fonts_check_xavgcharwidth as check

    test_font_path = TEST_FILE("nunito/Nunito-Regular.ttf")

    test_font = TTFont(test_font_path)
    assert_PASS(check(test_font))

    test_font['OS/2'].xAvgCharWidth = 556
    assert_results_contain(check(test_font), INFO,
                           None)  # FIXME: This needs a message keyword!

    test_font['OS/2'].xAvgCharWidth = 500
    assert_results_contain(check(test_font), WARN,
                           None)  # FIXME: This needs a message keyword

    test_font = TTFont()
    test_font['OS/2'] = fontTools.ttLib.newTable('OS/2')
    test_font['OS/2'].version = 4
    test_font['OS/2'].xAvgCharWidth = 1000
    test_font['glyf'] = fontTools.ttLib.newTable('glyf')
    test_font['glyf'].glyphs = {}
    test_font['hmtx'] = fontTools.ttLib.newTable('hmtx')
    test_font['hmtx'].metrics = {}
    assert_results_contain(check(test_font), FAIL, 'missing-glyphs')

    test_font = TTFont(test_font_path)
    subsetter = fontTools.subset.Subsetter()
    subsetter.populate(glyphs=[
        'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
        'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'space'
    ])
    subsetter.subset(test_font)
    test_font['OS/2'].xAvgCharWidth = 447
    test_font['OS/2'].version = 2
    temp_file = io.BytesIO()
    test_font.save(temp_file)
    test_font = TTFont(temp_file)
    assert_PASS(check(test_font))

    test_font['OS/2'].xAvgCharWidth = 450
    assert_results_contain(check(test_font), INFO,
                           None)  # FIXME: This needs a message keyword

    test_font['OS/2'].xAvgCharWidth = 500
    assert_results_contain(check(test_font), WARN,
                           None)  # FIXME: This needs a message keyword

    test_font = TTFont(temp_file)
    subsetter = fontTools.subset.Subsetter()
    subsetter.populate(glyphs=[
        'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
        'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'space'
    ])
    subsetter.subset(test_font)
    assert_results_contain(check(test_font), FAIL, 'missing-glyphs')
Exemple #30
0
def test_check_STAT_strings():
    check = CheckTester(universal_profile,
                        "com.google.fonts/check/STAT_strings")

    good = TTFont(TEST_FILE("ibmplexsans-vf/IBMPlexSansVar-Roman.ttf"))
    assert_PASS(check(good))

    bad = TTFont(TEST_FILE("ibmplexsans-vf/IBMPlexSansVar-Italic.ttf"))
    assert_results_contain(check(bad), FAIL, "bad-italic")