コード例 #1
0
    def test_arabic_numerals(self):
        ufo = Font()
        for name, code in [("four-ar", 0x664), ("seven-ar", 0x667)]:
            glyph = ufo.newGlyph(name)
            glyph.unicode = code
        ufo.kerning.update({
            ('four-ar', 'seven-ar'): -30,
        })
        ufo.features.text = dedent("""
            languagesystem DFLT dflt;
            languagesystem arab dflt;
        """)

        writer = KernFeatureWriter()
        kern = writer.write(ufo)

        assert kern == dedent("""
            lookup kern_ltr {
                lookupflag IgnoreMarks;
                pos four-ar seven-ar -30;
            } kern_ltr;

            feature kern {
                lookup kern_ltr;
            } kern;""")
コード例 #2
0
    def test_getKerningClasses(self, FontClass):
        font = FontClass()
        for i in range(65, 65 + 6):  # A..F
            font.newGlyph(chr(i))
        font.groups.update({
            "public.kern1.A": ["A", "B"],
            "public.kern2.C": ["C", "D"]
        })
        # simulate a name clash between pre-existing class definitions in
        # feature file, and those generated by the feature writer
        font.features.text = "@kern1.A = [E F];"

        writer = KernFeatureWriter()
        feaFile = parseLayoutFeatures(font)
        side1Classes, side2Classes = KernFeatureWriter.getKerningClasses(
            font, feaFile)

        assert "public.kern1.A" in side1Classes
        # the new class gets a unique name
        assert side1Classes["public.kern1.A"].name == "kern1.A_1"
        assert getGlyphs(side1Classes["public.kern1.A"]) == ["A", "B"]

        assert "public.kern2.C" in side2Classes
        assert side2Classes["public.kern2.C"].name == "kern2.C"
        assert getGlyphs(side2Classes["public.kern2.C"]) == ["C", "D"]
コード例 #3
0
    def test_mode(self):
        class MockTTFont:
            def getReverseGlyphMap(self):
                return {"one": 0, "four": 1, "six": 2, "seven": 3}

        outline = MockTTFont()

        ufo = Font()
        for name in ("one", "four", "six", "seven"):
            ufo.newGlyph(name)
        existing = dedent("""
            feature kern {
                pos one four' -50 six;
            } kern;
            """)
        ufo.features.text = existing
        ufo.kerning.update({
            ('seven', 'six'): 25.0,
        })

        writer = KernFeatureWriter()  # default mode="skip"
        compiler = FeatureCompiler(ufo, outline, featureWriters=[writer])
        compiler.setupFile_features()

        assert compiler.features == existing

        writer = KernFeatureWriter(mode="append")
        compiler = FeatureCompiler(ufo, outline, featureWriters=[writer])
        compiler.setupFile_features()

        assert compiler.features == existing + dedent("""


            lookup kern_ltr {
                lookupflag IgnoreMarks;
                pos seven six 25;
            } kern_ltr;

            feature kern {
                lookup kern_ltr;
            } kern;""")

        writer = KernFeatureWriter(mode="prepend")
        compiler = FeatureCompiler(ufo, outline, featureWriters=[writer])
        compiler.setupFile_features()

        assert compiler.features == dedent("""
            lookup kern_ltr {
                lookupflag IgnoreMarks;
                pos seven six 25;
            } kern_ltr;

            feature kern {
                lookup kern_ltr;
            } kern;

            """) + existing
コード例 #4
0
    def test_insert_comment_after(self, FontClass):
        ufo = FontClass()
        for name in ("one", "four", "six", "seven"):
            ufo.newGlyph(name)
        existing = dedent("""\
            feature kern {
                pos one four' -50 six;
                #
                # Automatic Code
                #
            } kern;
            """)
        ufo.features.text = existing
        ufo.kerning.update({("seven", "six"): 25.0})

        writer = KernFeatureWriter()
        feaFile = parseLayoutFeatures(ufo)
        assert writer.write(ufo, feaFile)

        expected = dedent("""\
            feature kern {
                pos one four' -50 six;
                #
                #
            } kern;


            lookup kern_ltr {
                lookupflag IgnoreMarks;
                pos seven six 25;
            } kern_ltr;

            feature kern {
                lookup kern_ltr;
            } kern;
            """)

        assert str(feaFile) == expected

        # test append mode ignores insert marker
        generated = self.writeFeatures(ufo, mode="append")
        assert str(generated) == dedent("""
            lookup kern_ltr {
                lookupflag IgnoreMarks;
                pos seven six 25;
            } kern_ltr;

            feature kern {
                lookup kern_ltr;
            } kern;
            """)
コード例 #5
0
    def test_getKerningPairs(self, FontClass):
        font = FontClass()
        for i in range(65, 65 + 8):  # A..H
            font.newGlyph(chr(i))
        font.groups.update({
            "public.kern1.foo": ["A", "B"],
            "public.kern2.bar": ["C", "D"],
            "public.kern1.baz": ["E", "F"],
            "public.kern2.nul": ["G", "H"],
        })
        font.kerning.update({
            ("public.kern1.foo", "public.kern2.bar"):
            10,
            ("public.kern1.baz", "public.kern2.bar"):
            -10,
            ("public.kern1.foo", "D"):
            15,
            ("A", "public.kern2.bar"):
            5,
            ("G", "H"):
            -5,
            # class-class zero-value pairs are skipped
            ("public.kern1.foo", "public.kern2.nul"):
            0,
        })

        s1c, s2c = KernFeatureWriter.getKerningClasses(font)
        pairs = KernFeatureWriter.getKerningPairs(font, s1c, s2c)
        assert len(pairs) == 5

        assert "G H -5" in repr(pairs[0])
        assert (pairs[0].firstIsClass, pairs[0].secondIsClass) == (False,
                                                                   False)
        assert pairs[0].glyphs == {"G", "H"}

        assert "A @kern2.bar 5" in repr(pairs[1])
        assert (pairs[1].firstIsClass, pairs[1].secondIsClass) == (False, True)
        assert pairs[1].glyphs == {"A", "C", "D"}

        assert "@kern1.foo D 15" in repr(pairs[2])
        assert (pairs[2].firstIsClass, pairs[2].secondIsClass) == (True, False)
        assert pairs[2].glyphs == {"A", "B", "D"}

        assert "@kern1.baz @kern2.bar -10" in repr(pairs[3])
        assert (pairs[3].firstIsClass, pairs[3].secondIsClass) == (True, True)
        assert pairs[3].glyphs == {"C", "D", "E", "F"}

        assert "@kern1.foo @kern2.bar 10" in repr(pairs[4])
        assert (pairs[4].firstIsClass, pairs[4].secondIsClass) == (True, True)
        assert pairs[4].glyphs == {"A", "B", "C", "D"}
コード例 #6
0
    def test_correct_invalid_class_names(self, FontClass):
        font = FontClass()
        for i in range(65, 65 + 12):  # A..L
            font.newGlyph(chr(i))
        font.groups.update(
            {
                "public.kern1.foo$": ["A", "B", "C"],
                "public.kern1.foo@": ["D", "E", "F"],
                "@public.kern2.bar": ["G", "H", "I"],
                "public.kern2.bar&": ["J", "K", "L"],
            }
        )
        font.kerning.update(
            {
                ("public.kern1.foo$", "@public.kern2.bar"): 10,
                ("public.kern1.foo@", "public.kern2.bar&"): -10,
            }
        )

        side1Classes, side2Classes = KernFeatureWriter.getKerningClasses(font)

        assert side1Classes["public.kern1.foo$"].name == "kern1.foo"
        assert side1Classes["public.kern1.foo@"].name == "kern1.foo_1"
        # no valid 'public.kern{1,2}.' prefix, skipped
        assert "@public.kern2.bar" not in side2Classes
        assert side2Classes["public.kern2.bar&"].name == "kern2.bar"
コード例 #7
0
    def test__groupScriptsByTagAndDirection(self, FontClass):
        font = FontClass()
        font.features.text = dedent("""
            languagesystem DFLT dflt;
            languagesystem latn dflt;
            languagesystem latn TRK;
            languagesystem arab dflt;
            languagesystem arab URD;
            languagesystem deva dflt;
            languagesystem dev2 dflt;
            """)

        feaFile = parseLayoutFeatures(font)
        scripts = ast.getScriptLanguageSystems(feaFile)
        scriptGroups = KernFeatureWriter._groupScriptsByTagAndDirection(
            scripts)

        assert "kern" in scriptGroups
        assert list(scriptGroups["kern"]["LTR"]) == [("latn", ["dflt",
                                                               "TRK "])]
        assert list(scriptGroups["kern"]["RTL"]) == [("arab", ["dflt",
                                                               "URD "])]

        assert "dist" in scriptGroups
        assert list(scriptGroups["dist"]["LTR"]) == [
            ("deva", ["dflt"]),
            ("dev2", ["dflt"]),
        ]
コード例 #8
0
    def test_quantize(self, FontClass):
        font = FontClass()
        for name in ("one", "four", "six"):
            font.newGlyph(name)
        font.kerning.update({("four", "six"): -57.0, ("one", "six"): -24.0})
        writer = KernFeatureWriter(quantization=5)
        feaFile = ast.FeatureFile()
        assert writer.write(font, feaFile)

        assert str(feaFile) == dedent("""\
            lookup kern_ltr {
                lookupflag IgnoreMarks;
                pos four six -55;
                pos one six -25;
            } kern_ltr;

            feature kern {
                lookup kern_ltr;
            } kern;
            """)
コード例 #9
0
    def test_mode(self, FontClass):
        ufo = FontClass()
        for name in ("one", "four", "six", "seven"):
            ufo.newGlyph(name)
        existing = dedent(
            """\
            feature kern {
                pos one four' -50 six;
            } kern;
            """
        )
        ufo.features.text = existing
        ufo.kerning.update({("seven", "six"): 25.0})

        writer = KernFeatureWriter()  # default mode="skip"
        feaFile = parseLayoutFeatures(ufo)
        assert not writer.write(ufo, feaFile)

        assert str(feaFile) == existing

        # pass optional "append" mode
        writer = KernFeatureWriter(mode="append")
        feaFile = parseLayoutFeatures(ufo)
        assert writer.write(ufo, feaFile)

        expected = existing + dedent(
            """

            lookup kern_ltr {
                lookupflag IgnoreMarks;
                pos seven six 25;
            } kern_ltr;

            feature kern {
                lookup kern_ltr;
            } kern;
            """
        )
        assert str(feaFile) == expected

        # pass "skip" mode explicitly
        writer = KernFeatureWriter(mode="skip")
        feaFile = parseLayoutFeatures(ufo)
        assert not writer.write(ufo, feaFile)

        assert str(feaFile) == existing
コード例 #10
0
    def test_ignoreMarks(self):
        font = Font()
        for name in ("one", "four", "six"):
            font.newGlyph(name)
        font.kerning.update({
            ('four', 'six'): -55.0,
            ('one', 'six'): -30.0,
        })
        # default is ignoreMarks=True
        writer = KernFeatureWriter()
        kern = writer.write(font)

        assert kern == dedent("""
            lookup kern_ltr {
                lookupflag IgnoreMarks;
                pos four six -55;
                pos one six -30;
            } kern_ltr;

            feature kern {
                lookup kern_ltr;
            } kern;""")

        writer = KernFeatureWriter(ignoreMarks=False)
        kern = writer.write(font)

        assert kern == dedent("""
            lookup kern_ltr {
                pos four six -55;
                pos one six -30;
            } kern_ltr;

            feature kern {
                lookup kern_ltr;
            } kern;""")
コード例 #11
0
    def test_cleanup_missing_glyphs(self, FontClass):
        groups = {
            "public.kern1.A": ["A", "Aacute", "Abreve", "Acircumflex"],
            "public.kern2.B": ["B", "D", "E", "F"],
            "public.kern1.C": ["foobar"],
        }
        kerning = {
            ("public.kern1.A", "public.kern2.B"): 10,
            ("public.kern1.A", "baz"): -25,
            ("baz", "public.kern2.B"): -20,
            ("public.kern1.C", "public.kern2.B"): 20,
        }
        ufo = FontClass()
        exclude = {"Abreve", "D", "foobar"}
        for glyphs in groups.values():
            for glyph in glyphs:
                if glyph in exclude:
                    continue
                ufo.newGlyph(glyph)
        ufo.groups.update(groups)
        ufo.kerning.update(kerning)

        writer = KernFeatureWriter()
        feaFile = parseLayoutFeatures(ufo)
        writer.write(ufo, feaFile)

        classDefs = getClassDefs(feaFile)
        assert len(classDefs) == 2
        assert classDefs[0].name == "kern1.A"
        assert classDefs[1].name == "kern2.B"
        assert getGlyphs(classDefs[0]) == ["A", "Aacute", "Acircumflex"]
        assert getGlyphs(classDefs[1]) == ["B", "E", "F"]

        lookups = getLookups(feaFile)
        assert len(lookups) == 1
        kern_ltr = lookups[0]
        assert kern_ltr.name == "kern_ltr"
        rules = getPairPosRules(kern_ltr)
        assert len(rules) == 1
        assert str(rules[0]) == "pos @kern1.A @kern2.B 10;"
コード例 #12
0
    def test_featureWriters_empty(self, FontClass):
        kernWriter = KernFeatureWriter(ignoreMarks=False)
        ufo = FontClass()
        ufo.newGlyph("a")
        ufo.newGlyph("v")
        ufo.kerning.update({("a", "v"): -40})
        compiler = FeatureCompiler(ufo, featureWriters=[kernWriter])
        ttFont1 = compiler.compile()
        assert "GPOS" in ttFont1

        compiler = FeatureCompiler(ufo, featureWriters=[])
        ttFont2 = compiler.compile()
        assert "GPOS" not in ttFont2
コード例 #13
0
 def designSpace2Var(self):
     ds = self.designSpace
     family = os.path.basename(self.familyPath)
     print("\n>>> Load the {} designspace".format(family))
     print("    Load " + family + " files")
     ds.loadSourceFonts(Font)
     print("    Start to build Variable Tables")
     feature_Writers = [KernFeatureWriter(mode="append"), MarkFeatureWriter]
     font, _, _ = varLib.build(compileInterpolatableTTFsFromDS(
         ds, featureWriters=feature_Writers),
                               optimize=False)
     font.save(os.path.join(self.destination, family + "-VF.ttf"))
     print("    " + family + " Variable Font generated\n")
コード例 #14
0
def ufo2font(directory, ufolist, output, fromInstances=False):
    path = os.path.abspath(os.path.join(os.pardir, "src", directory))
    for i in ufolist:
        ufoSource = os.path.join(path, i)
        destination = ""
        ufo = Font(ufoSource)
        folder = os.path.join(path, "fonts")
        if fromInstances is True:
            folder = os.path.abspath(os.path.join(path, os.pardir, "fonts"))
        featureWriters = [KernFeatureWriter(mode="append"), MarkFeatureWriter]
        if "otf" in output:
            destination = os.path.join(folder, "OTF")
            if not os.path.exists(destination):
                os.makedirs(destination)
            otf = compileOTF(ufo,
                             removeOverlaps=True,
                             useProductionNames=False,
                             featureWriters=featureWriters)
            otf.save(os.path.join(destination, i[:-4] + ".otf"))
            print(destination + i[:-4] + ".otf saved")
        if "ttf" in output:
            destination = os.path.join(folder, "TTF")
            if not os.path.exists(destination):
                os.makedirs(destination)
            ttf = compileTTF(ufo,
                             removeOverlaps=True,
                             useProductionNames=False,
                             featureWriters=featureWriters)
            ttf.save(os.path.join(destination, i[:-4] + ".ttf"))
        if "woff2" in output:
            destination = os.path.join(folder, "WOFF2")
            if not os.path.exists(destination):
                os.makedirs(destination)
            if "ttf" not in output:
                ttf = compileTTF(ufo,
                                 removeOverlaps=True,
                                 useProductionNames=False,
                                 featureWriters=featureWriters)
            ttf.flavor = "woff2"
            ttf.save(os.path.join(destination, i[:-4] + ".woff2"))
        if "woff" in output:
            destination = os.path.join(folder, "WOFF")
            if not os.path.exists(destination):
                os.makedirs(destination)
            if "ttf" not in output and "woff2" not in output:
                ttf = compileTTF(ufo,
                                 removeOverlaps=True,
                                 useProductionNames=False,
                                 featureWriters=featureWriters)
            ttf.flavor = "woff"
            ttf.save(os.path.join(destination, i[:-4] + ".woff"))
コード例 #15
0
    def test_insert_comment_middle(self, FontClass):
        ufo = FontClass()
        for name in ("one", "four", "six", "seven"):
            ufo.newGlyph(name)
        existing = dedent("""\
            feature kern {
                pos one four' -50 six;
                #
                # Automatic Code
                #
                pos one six' -50 six;
            } kern;
            """)
        ufo.features.text = existing
        ufo.kerning.update({("seven", "six"): 25.0})

        writer = KernFeatureWriter()
        feaFile = parseLayoutFeatures(ufo)

        with pytest.raises(
                InvalidFeaturesData,
                match="Insert marker has rules before and after, feature kern "
                "cannot be inserted.",
        ):
            writer.write(ufo, feaFile)

        # test append mode ignores insert marker
        generated = self.writeFeatures(ufo, mode="append")
        assert str(generated) == dedent("""
            lookup kern_ltr {
                lookupflag IgnoreMarks;
                pos seven six 25;
            } kern_ltr;

            feature kern {
                lookup kern_ltr;
            } kern;
            """)
コード例 #16
0
 def makeOneMaster(self, ufoSource):
     ufo = Font(ufoSource)
     ttf = compileTTF(ufo,
                      removeOverlaps=True,
                      useProductionNames=False,
                      featureWriters=[
                          KernFeatureWriter(mode="append"),
                          MarkFeatureWriter
                      ])
     ttf.save(
         os.path.join(self.staticPath,
                      os.path.basename(ufoSource)[:-4] + ".ttf"))
     print("    " + os.path.basename(ufoSource)[:-4] +
           " has been generated.\n")
コード例 #17
0
    def test_insert_comment_before_extended(self, FontClass):
        ufo = FontClass()
        for name in ("one", "four", "six", "seven"):
            ufo.newGlyph(name)
        existing = dedent("""\
            feature kern {
                #
                # Automatic Code End
                #
                pos one four' -50 six;
            } kern;
            """)
        ufo.features.text = existing
        ufo.kerning.update({("seven", "six"): 25.0})

        writer = KernFeatureWriter()
        feaFile = parseLayoutFeatures(ufo)
        assert writer.write(ufo, feaFile)

        expected = dedent("""\
            lookup kern_ltr {
                lookupflag IgnoreMarks;
                pos seven six 25;
            } kern_ltr;

            feature kern {
                lookup kern_ltr;
            } kern;

            feature kern {
                #
                #
                pos one four' -50 six;
            } kern;
            """)

        assert str(feaFile).strip() == expected.strip()
コード例 #18
0
    def test_collect_fea_classes(self):
        text = '@MMK_L_v = [v w y];'
        expected = {'@MMK_L_v': ['v', 'w', 'y']}

        ufo = Font()
        ufo.features.text = text
        writer = KernFeatureWriter()
        writer.set_context(ufo)
        writer._collectFeaClasses()
        self.assertEquals(writer.context.leftFeaClasses, expected)
コード例 #19
0
    def makeVarFont(self):
        (minimum, maximum), tag2name = self.findRegularBoldDefaultLocation()
        # check if defautl exist
        otherTags = [
            a.tag for a in self.designSpaceDocument.axes if a.tag != "wght"
        ]
        print("\tLoad {}.designspace".format(self.familyName))
        self.designSpaceDocument.loadSourceFonts(defcon.Font)
        vfont, _, _ = varLib.build(compileInterpolatableTTFsFromDS(
            self.designSpaceDocument,
            featureWriters=[
                KernFeatureWriter(mode="append"),
                MarkFeatureWriter()
            ]),
                                   optimize=False)

        fullfontsFolder = os.path.join(self.dirpath, "fonts/VAR")
        if not os.path.exists(fullfontsFolder):
            os.makedirs(fullfontsFolder)
        path = os.path.join(fullfontsFolder, self.familyName + "-VF.ttf")
        vfont.save(path)
        vfont = TTFont(path)

        tags = {"wght": (minimum, maximum)}
        if len(otherTags) == 0:
            slimft = instancer.instantiateVariableFont(vfont, tags)
        else:
            for t in otherTags:
                tags[t] = None
            slimft = instancer.instantiateVariableFont(vfont, tags)

        for namerecord in slimft['name'].names:
            namerecord.string = namerecord.toUnicode()
            if namerecord.nameID == 3:
                unicID = namerecord.string.split(";")
                newID = "4.444" + ";" + unicID[1] + ";" + unicID[2]
                print("\tTagging the font as a 'slim' one:", newID)
                namerecord.string = newID
            if namerecord.nameID == 5:
                namerecord.string = "Version 4.444"
        print("\tSaving " + self.familyName + "Slim-VF.ttf\n")
        slimFontFolder = os.path.join(fullfontsFolder + "/SlimVF")
        if not os.path.exists(slimFontFolder):
            os.makedirs(slimFontFolder)
        slimft.save(
            os.path.join(slimFontFolder, "%sSlim-VF.ttf" % self.familyName))
def designSpace2Var(family):
    print(">>> Load the {} designspace".format(family))
    path, folder = getFile(".designspace", family)
    designSpace = openDesignSpace(path)
    print("    Load " + family + " files")
    designSpace.loadSourceFonts(Font)
    print("    Start to build Variable Tables")
    feature_Writers = [KernFeatureWriter(mode="append"), MarkFeatureWriter]

    font, _, _ = varLib.build(compileInterpolatableTTFsFromDS(
        designSpace, featureWriters=feature_Writers),
                              optimize=False)
    destination = folder + "/fonts/VAR"
    if not os.path.exists(destination):
        os.makedirs(destination)
    font.save(os.path.join(destination, family + "-VF.ttf"))
    print("    " + family + " Variable Font generated\n")
コード例 #21
0
    def test_ignoreMarks(self, FontClass):
        font = FontClass()
        for name in ("one", "four", "six"):
            font.newGlyph(name)
        font.kerning.update({("four", "six"): -55.0, ("one", "six"): -30.0})
        # default is ignoreMarks=True
        writer = KernFeatureWriter()
        feaFile = ast.FeatureFile()
        assert writer.write(font, feaFile)

        assert str(feaFile) == dedent(
            """\
            lookup kern_ltr {
                lookupflag IgnoreMarks;
                pos four six -55;
                pos one six -30;
            } kern_ltr;

            feature kern {
                lookup kern_ltr;
            } kern;
            """
        )

        writer = KernFeatureWriter(ignoreMarks=False)
        feaFile = ast.FeatureFile()
        assert writer.write(font, feaFile)

        assert str(feaFile) == dedent(
            """\
            lookup kern_ltr {
                pos four six -55;
                pos one six -30;
            } kern_ltr;

            feature kern {
                lookup kern_ltr;
            } kern;
            """
        )
コード例 #22
0
    def test__cleanupMissingGlyphs(self):
        groups = {
            "public.kern1.A": ["A", "Aacute", "Abreve", "Acircumflex"],
            "public.kern2.B": ["B", "D", "E", "F"],
        }
        ufo = Font()
        for glyphs in groups.values():
            for glyph in glyphs:
                ufo.newGlyph(glyph)
        ufo.groups.update(groups)
        del ufo["Abreve"]
        del ufo["D"]

        writer = KernFeatureWriter()
        writer.set_context(ufo)
        self.assertEquals(writer.context.groups, groups)

        writer._cleanupMissingGlyphs()
        self.assertEquals(
            writer.context.groups, {
                "public.kern1.A": ["A", "Aacute", "Acircumflex"],
                "public.kern2.B": ["B", "E", "F"]
            })