def test_build_ttf(tmpdir):
    outPath = os.path.join(str(tmpdir), "test.ttf")

    fb, advanceWidths, nameStrings = _setupFontBuilder(True)

    pen = TTGlyphPen(None)
    drawTestGlyph(pen)
    glyph = pen.glyph()
    glyphs = {".notdef": glyph, "A": glyph, "a": glyph, ".null": glyph}
    fb.setupGlyf(glyphs)
    metrics = {}
    glyphTable = fb.font["glyf"]
    for gn, advanceWidth in advanceWidths.items():
        metrics[gn] = (advanceWidth, glyphTable[gn].xMin)
    fb.setupHorizontalMetrics(metrics)

    fb.setupHorizontalHeader(ascent=824, descent=200)
    fb.setupNameTable(nameStrings)
    fb.setupOS2()
    fb.setupPost()
    fb.setupDummyDSIG()

    fb.save(outPath)

    _verifyOutput(outPath)
Exemple #2
0
    def setupTable_glyf(self):
        """Make the glyf table."""

        allGlyphs = self.allGlyphs
        if self.convertCubics:
            from cu2qu.pens import Cu2QuPen
            allGlyphs = {}
            for name, glyph in self.allGlyphs.items():
                if isinstance(glyph, StubGlyph):
                    allGlyphs[name] = glyph
                    continue
                newGlyph = glyph.__class__()
                glyph.draw(
                    Cu2QuPen(newGlyph.getPen(),
                             self.cubicConversionError,
                             reverse_direction=True))
                # the width is needed for autoUseMyMetrics method below
                newGlyph.width = glyph.width
                allGlyphs[name] = newGlyph

        self.otf["loca"] = newTable("loca")
        self.otf["glyf"] = glyf = newTable("glyf")
        glyf.glyphs = {}
        glyf.glyphOrder = self.glyphOrder

        for name in self.glyphOrder:
            glyph = allGlyphs[name]
            pen = TTGlyphPen(allGlyphs)
            glyph.draw(pen)
            ttGlyph = pen.glyph()
            if ttGlyph.isComposite() and self.autoUseMyMetrics:
                self.autoUseMyMetrics(ttGlyph, glyph.width, allGlyphs)
            glyf[name] = ttGlyph
Exemple #3
0
    def test_trim_remove_hinting_composite_glyph(self):
        glyphSet = {"dummy": TTGlyphPen(None).glyph()}

        pen = TTGlyphPen(glyphSet)
        pen.addComponent("dummy", (1, 0, 0, 1, 0, 0))
        composite = pen.glyph()
        p = ttProgram.Program()
        p.fromAssembly(['SVTCA[0]'])
        composite.program = p
        glyphSet["composite"] = composite

        glyfTable = newTable("glyf")
        glyfTable.glyphs = glyphSet
        glyfTable.glyphOrder = sorted(glyphSet)

        composite.compact(glyfTable)

        self.assertTrue(hasattr(composite, "data"))

        # remove hinting from the compacted composite glyph, without expanding it
        composite.trim(remove_hinting=True)

        # check that, after expanding the glyph, we have no instructions
        composite.expand(glyfTable)
        self.assertFalse(hasattr(composite, "program"))

        # now remove hinting from expanded composite glyph
        composite.program = p
        composite.trim(remove_hinting=True)

        # check we have no instructions
        self.assertFalse(hasattr(composite, "program"))

        composite.compact(glyfTable)
Exemple #4
0
def test_build_ttf(tmpdir):
    outPath = os.path.join(str(tmpdir), "test.ttf")

    fb, advanceWidths, nameStrings = _setupFontBuilder(True)

    pen = TTGlyphPen(None)
    drawTestGlyph(pen)
    glyph = pen.glyph()
    glyphs = {".notdef": glyph, "A": glyph, "a": glyph, ".null": glyph}
    fb.setupGlyf(glyphs)
    metrics = {}
    glyphTable = fb.font["glyf"]
    for gn, advanceWidth in advanceWidths.items():
        metrics[gn] = (advanceWidth, glyphTable[gn].xMin)
    fb.setupHorizontalMetrics(metrics)

    fb.setupHorizontalHeader(ascent=824, descent=200)
    fb.setupNameTable(nameStrings)
    fb.setupOS2()
    fb.setupPost()
    fb.setupDummyDSIG()

    fb.save(outPath)

    f = TTFont(outPath)
    f.saveXML(outPath + ".ttx")
    with open(outPath + ".ttx") as f:
        testData = strip_VariableItems(f.read())
    refData = strip_VariableItems(getTestData("test.ttf.ttx"))
    assert refData == testData
Exemple #5
0
def test_build_ttf(tmpdir):
    outPath = os.path.join(str(tmpdir), "test.ttf")

    fb, advanceWidths, nameStrings = _setupFontBuilder(True)

    pen = TTGlyphPen(None)
    drawTestGlyph(pen)
    glyph = pen.glyph()
    glyphs = {".notdef": glyph, "A": glyph, "a": glyph, ".null": glyph}
    fb.setupGlyf(glyphs)
    metrics = {}
    glyphTable = fb.font["glyf"]
    for gn, advanceWidth in advanceWidths.items():
        metrics[gn] = (advanceWidth, glyphTable[gn].xMin)
    fb.setupHorizontalMetrics(metrics)

    fb.setupHorizontalHeader(ascent=824, descent=200)
    fb.setupNameTable(nameStrings)
    fb.setupOS2()
    fb.setupPost()
    fb.setupDummyDSIG()

    fb.save(outPath)

    _verifyOutput(outPath)
 def test_closePath_ignoresAnchors(self):
     pen = TTGlyphPen(None)
     pen.moveTo((0, 0))
     pen.closePath()
     self.assertFalse(pen.points)
     self.assertFalse(pen.types)
     self.assertFalse(pen.endPts)
    def setupTable_glyf(self):
        """Make the glyf table."""

        allGlyphs = self.allGlyphs
        if self.convertCubics:
            from cu2qu.pens import Cu2QuPen
            allGlyphs = {}
            for name, glyph in self.allGlyphs.items():
                if isinstance(glyph, StubGlyph):
                    allGlyphs[name] = glyph
                    continue
                newGlyph = glyph.__class__()
                glyph.draw(Cu2QuPen(
                    newGlyph.getPen(), self.cubicConversionError,
                    reverse_direction=True))
                allGlyphs[name] = newGlyph

        self.otf["loca"] = newTable("loca")
        self.otf["glyf"] = glyf = newTable("glyf")
        glyf.glyphs = {}
        glyf.glyphOrder = self.glyphOrder

        for name in self.glyphOrder:
            pen = TTGlyphPen(allGlyphs)
            allGlyphs[name].draw(pen)
            glyf[name] = pen.glyph()
    def setupTable_glyf(self):
        """Make the glyf table."""

        allGlyphs = self.allGlyphs
        if self.convertCubics:
            from cu2qu.pens import Cu2QuPen
            allGlyphs = {}
            for name, glyph in self.allGlyphs.items():
                if isinstance(glyph, StubGlyph):
                    allGlyphs[name] = glyph
                    continue
                newGlyph = glyph.__class__()
                glyph.draw(
                    Cu2QuPen(newGlyph.getPen(),
                             self.cubicConversionError,
                             reverse_direction=True))
                allGlyphs[name] = newGlyph

        self.otf["loca"] = newTable("loca")
        self.otf["glyf"] = glyf = newTable("glyf")
        glyf.glyphs = {}
        glyf.glyphOrder = self.glyphOrder

        for name in self.glyphOrder:
            pen = TTGlyphPen(allGlyphs)
            allGlyphs[name].draw(pen)
            glyf[name] = pen.glyph()
Exemple #9
0
def test_build_ttf(tmpdir):
    outPath = os.path.join(str(tmpdir), "test.ttf")

    fb, advanceWidths, nameStrings = _setupFontBuilder(True)

    pen = TTGlyphPen(None)
    drawTestGlyph(pen)
    glyph = pen.glyph()
    glyphs = {".notdef": glyph, "A": glyph, "a": glyph, ".null": glyph}
    fb.setupGlyf(glyphs)
    metrics = {}
    glyphTable = fb.font["glyf"]
    for gn, advanceWidth in advanceWidths.items():
        metrics[gn] = (advanceWidth, glyphTable[gn].xMin)
    fb.setupHorizontalMetrics(metrics)

    fb.setupHorizontalHeader(ascent=824, descent=200)
    fb.setupNameTable(nameStrings)
    fb.setupOS2()
    fb.setupPost()
    fb.setupDummyDSIG()

    fb.save(outPath)

    f = TTFont(outPath)
    f.saveXML(outPath + ".ttx")
    with open(outPath + ".ttx") as f:
        testData = strip_VariableItems(f.read())
    refData = strip_VariableItems(getTestData("test.ttf.ttx"))
    assert refData == testData
 def test_remove_extra_move_points(self):
     pen = TTGlyphPen(None)
     pen.moveTo((0, 0))
     pen.lineTo((100, 0))
     pen.qCurveTo((100, 50), (50, 100), (0, 0))
     pen.closePath()
     assert len(pen.points) == 4
     assert pen.points[0] == (0, 0)
Exemple #11
0
 def test_remove_extra_move_points(self):
     pen = TTGlyphPen(None)
     pen.moveTo((0, 0))
     pen.lineTo((100, 0))
     pen.qCurveTo((100, 50), (50, 100), (0, 0))
     pen.closePath()
     self.assertEqual(len(pen.points), 4)
     self.assertEqual(pen.points[0], (0, 0))
Exemple #12
0
def glyphs_to_quadratic(glyphs, max_err, **kwargs):
    quadGlyphs = {}
    for gname in glyphs.keys():
        glyph = glyphs[gname]
        ttPen = TTGlyphPen(glyphs)
        cu2quPen = Cu2QuPen(ttPen, max_err, **kwargs)
        glyph.draw(cu2quPen)
        quadGlyphs[gname] = ttPen.glyph()
    return quadGlyphs
 def test_keep_move_point(self):
     pen = TTGlyphPen(None)
     pen.moveTo((0, 0))
     pen.lineTo((100, 0))
     pen.qCurveTo((100, 50), (50, 100), (30, 30))
     # when last and move pts are different, closePath() implies a lineTo
     pen.closePath()
     assert len(pen.points) == 5
     assert pen.points[0] == (0, 0)
def ttfGlyphFromSkPath(path: pathops.Path) -> _g_l_y_f.Glyph:
    # Skia paths have no 'components', no need for glyphSet
    ttPen = TTGlyphPen(glyphSet=None)
    path.draw(ttPen)
    glyph = ttPen.glyph()
    assert not glyph.isComposite()
    # compute glyph.xMin (glyfTable parameter unused for non composites)
    glyph.recalcBounds(glyfTable=None)
    return glyph
Exemple #15
0
def skia_path_to_ttfont_glyph(skia_path: pathops.Path) -> _g_l_y_f.Glyph:
    """
    Converts a pathops.Path object to a fontTools.ttLib._g_l_y_f.Glyph
    object
    """
    tt_pen = TTGlyphPen(glyphSet=None)
    skia_path.draw(tt_pen)
    glyph = tt_pen.glyph()
    glyph.recalcBounds(glyfTable=None)
    return glyph
Exemple #16
0
def glyphs_to_quadratic(
        glyphs, max_err=MAX_ERR, reverse_direction=REVERSE_DIRECTION):
    quadGlyphs = {}
    for gname in glyphs.keys():
        glyph = glyphs[gname]
        ttPen = TTGlyphPen(glyphs)
        cu2quPen = Cu2QuPen(ttPen, max_err,
                            reverse_direction=reverse_direction)
        glyph.draw(cu2quPen)
        quadGlyphs[gname] = ttPen.glyph()
    return quadGlyphs
Exemple #17
0
def glyphs_to_quadratic(
        glyphs, max_err=MAX_ERR, reverse_direction=REVERSE_DIRECTION):
    quadGlyphs = {}
    for gname in glyphs.keys():
        glyph = glyphs[gname]
        ttPen = TTGlyphPen(glyphs)
        cu2quPen = Cu2QuPen(ttPen, max_err,
                            reverse_direction=reverse_direction)
        glyph.draw(cu2quPen)
        quadGlyphs[gname] = ttPen.glyph()
    return quadGlyphs
 def test_remove_extra_move_points(self):
     pen = TTGlyphPen(None)
     pen.moveTo((0, 0))
     pen.lineTo((100, 0))
     pen.qCurveTo((100, 50), (50, 100), (0, 0))
     pen.closePath()
     self.assertEqual(len(pen.points), 4)
     self.assertEqual(pen.points[0], (0, 0))
Exemple #19
0
    def setupTable_glyf(self):
        """Make the glyf table."""

        self.otf["loca"] = newTable("loca")
        self.otf["glyf"] = glyf = newTable("glyf")
        glyf.glyphs = {}
        glyf.glyphOrder = self.glyphOrder

        for glyph in self.ufo:
            pen = TTGlyphPen(self.ufo)
            glyph.draw(pen)
            glyf[glyph.name] = pen.glyph()
Exemple #20
0
    def setupTable_glyf(self):
        """Make the glyf table."""

        self.otf["loca"] = newTable("loca")
        self.otf["glyf"] = glyf = newTable("glyf")
        glyf.glyphs = {}
        glyf.glyphOrder = self.glyphOrder

        for name in self.glyphOrder:
            pen = TTGlyphPen(self.allGlyphs)
            self.allGlyphs[name].draw(pen)
            glyf[name] = pen.glyph()
 def test_keep_move_point(self):
     pen = TTGlyphPen(None)
     pen.moveTo((0, 0))
     pen.lineTo((100, 0))
     pen.qCurveTo((100, 50), (50, 100), (30, 30))
     # when last and move pts are different, closePath() implies a lineTo
     pen.closePath()
     self.assertEqual(len(pen.points), 5)
     self.assertEqual(pen.points[0], (0, 0))
Exemple #22
0
    def __init__(self,
                 path=None,
                 lazy=False,
                 size=1500,
                 ft_load_glyph_flags=FTHintMode.UNHINTED):
        self.path = path
        self.ttfont = TTFont(self.path)

        has_outlines = self.ttfont.has_key("glyf") or self.ttfont.has_key(
            "CFF ")
        if not has_outlines:
            # Create faux empty glyf table with empty glyphs to make
            # it a valid font, e.g. for old-style CBDT/CBLC fonts
            logger.warning(
                "No outlines present, treating {} as bitmap font".format(
                    self.path))
            self.ttfont["glyf"] = newTable("glyf")
            self.ttfont["glyf"].glyphs = {}
            pen = TTGlyphPen({})
            for name in self.ttfont.getGlyphOrder():
                self.ttfont["glyf"].glyphs[name] = pen.glyph()

        self._src_ttfont = TTFont(self.path)
        self.glyphset = None
        self.recalc_glyphset()
        self.axis_order = None
        self.instance_coordinates = self._get_dflt_instance_coordinates()
        self.instances_coordinates = self._get_instances_coordinates()
        self.glyphs = self.marks = self.mkmks = self.kerns = \
            self.glyph_metrics = self.names = self.attribs = None

        self.ftfont = freetype.Face(self.path)
        self.ftslot = self.ftfont.glyph
        self.ft_load_glyph_flags = ft_load_glyph_flags

        self.size = size
        if self.ftfont.is_scalable:
            self.ftfont.set_char_size(self.size)

        with open(self.path, 'rb') as fontfile:
            self._fontdata = fontfile.read()
        self.hbface = hb.Face.create(self._fontdata)
        self.hbfont = hb.Font.create(self.hbface)

        self.hbfont.scale = (self.size, self.size)

        if not lazy:
            self.recalc_tables()
Exemple #23
0
 def test_recursiveComponent(self):
     glyphSet = {}
     pen_dummy = TTGlyphPen(glyphSet)
     glyph_dummy = pen_dummy.glyph()
     glyphSet["A"] = glyph_dummy
     glyphSet["B"] = glyph_dummy
     pen_A = TTGlyphPen(glyphSet)
     pen_A.addComponent("B", (1, 0, 0, 1, 0, 0))
     pen_B = TTGlyphPen(glyphSet)
     pen_B.addComponent("A", (1, 0, 0, 1, 0, 0))
     glyph_A = pen_A.glyph()
     glyph_B = pen_B.glyph()
     glyphSet["A"] = glyph_A
     glyphSet["B"] = glyph_B
     with self.assertRaisesRegex(TTLibError, "glyph '.' contains a recursive component reference"):
         glyph_A.getCoordinates(glyphSet)
Exemple #24
0
 def process_glyf(self) -> None:
     """
     Processes glyf table
     """
     if self.ttf_components:
         glyf = self.font["glyf"]
         gs = self.font.getGlyphSet()
         for glyph_name in self.font.glyphOrder:
             if glyph_name in self.keep_g_names:
                 continue
             pen = TTGlyphPen(gs)
             pen.addComponent(self.replacer, (1, 0, 0, 1, 0, 0))
             glyf[glyph_name] = pen.glyph()
     else:
         self._process_base(self.font["glyf"])
     return None
Exemple #25
0
def glyphs_to_quadratic(glyphs,
                        max_err=MAX_ERR,
                        reverse_direction=REVERSE_DIRECTION):
    try:
        from cu2qu.pens import Cu2QuPen
        from fontTools.pens.ttGlyphPen import TTGlyphPen
    except ImportError:
        raise ValueError("cannot convert glyphs due to missing libs")
    quadGlyphs = {}
    for gname in glyphs.keys():
        glyph = glyphs[gname]
        ttPen = TTGlyphPen(glyphs)
        cu2quPen = Cu2QuPen(ttPen,
                            max_err,
                            reverse_direction=reverse_direction)
        glyph.draw(cu2quPen)
        quadGlyphs[gname] = ttPen.glyph()
    return quadGlyphs
Exemple #26
0
def decomposeAndRemoveOverlap(font, glyphName):

    glyfTable = font["glyf"]
    glyphSet = font.getGlyphSet()

    # record TTGlyph outlines without components
    dcPen = DecomposingRecordingPen(glyphSet)
    glyphSet[glyphName].draw(dcPen)

    # replay recording onto a skia-pathops Path
    path = pathops.Path()
    pathPen = path.getPen()
    dcPen.replay(pathPen)

    # remove overlaps
    path.simplify()

    # create new TTGlyph from Path
    ttPen = TTGlyphPen(None)
    path.draw(ttPen)
    glyfTable[glyphName] = ttPen.glyph()
Exemple #27
0
    def test_trim_remove_hinting_composite_glyph(self):
        glyphSet = {"dummy": TTGlyphPen(None).glyph()}

        pen = TTGlyphPen(glyphSet)
        pen.addComponent("dummy", (1, 0, 0, 1, 0, 0))
        composite = pen.glyph()
        p = ttProgram.Program()
        p.fromAssembly(['SVTCA[0]'])
        composite.program = p
        glyphSet["composite"] = composite

        glyfTable = newTable("glyf")
        glyfTable.glyphs = glyphSet
        glyfTable.glyphOrder = sorted(glyphSet)

        composite.compact(glyfTable)

        self.assertTrue(hasattr(composite, "data"))

        # remove hinting from the compacted composite glyph, without expanding it
        composite.trim(remove_hinting=True)

        # check that, after expanding the glyph, we have no instructions
        composite.expand(glyfTable)
        self.assertFalse(hasattr(composite, "program"))

        # now remove hinting from expanded composite glyph
        composite.program = p
        composite.trim(remove_hinting=True)

        # check we have no instructions
        self.assertFalse(hasattr(composite, "program"))

        composite.compact(glyfTable)
 def test_closePath_ignoresAnchors(self):
     pen = TTGlyphPen(None)
     pen.moveTo((0, 0))
     pen.closePath()
     assert not pen.points
     assert not pen.types
     assert not pen.endPts
Exemple #29
0
 def test_closePath_ignoresAnchors(self):
     pen = TTGlyphPen(None)
     pen.moveTo((0, 0))
     pen.closePath()
     self.assertFalse(pen.points)
     self.assertFalse(pen.types)
     self.assertFalse(pen.endPts)
    def runEndToEnd(self, filename):
        font = ttLib.TTFont()
        ttx_path = os.path.join(
            os.path.abspath(os.path.dirname(os.path.realpath(__file__))),
            '..', 'ttLib', 'data', filename)
        font.importXML(ttx_path)

        glyphSet = font.getGlyphSet()
        glyfTable = font['glyf']
        pen = TTGlyphPen(font.getGlyphSet())

        for name in font.getGlyphOrder():
            oldGlyph = glyphSet[name]
            oldGlyph.draw(pen)
            oldGlyph = oldGlyph._glyph
            newGlyph = pen.glyph()

            if hasattr(oldGlyph, 'program'):
                newGlyph.program = oldGlyph.program

            self.assertEqual(
                oldGlyph.compile(glyfTable), newGlyph.compile(glyfTable))
Exemple #31
0
def test_unicodeVariationSequences(tmpdir):
    familyName = "UVSTestFont"
    styleName = "Regular"
    nameStrings = dict(familyName=familyName, styleName=styleName)
    nameStrings['psName'] = familyName + "-" + styleName
    glyphOrder = [".notdef", "space", "zero", "zero.slash"]
    cmap = {ord(" "): "space", ord("0"): "zero"}
    uvs = [
        (0x0030, 0xFE00, "zero.slash"),
        (0x0030, 0xFE01, None),  # not an official sequence, just testing
    ]
    metrics = {gn: (600, 0) for gn in glyphOrder}
    pen = TTGlyphPen(None)
    glyph = pen.glyph()  # empty placeholder
    glyphs = {gn: glyph for gn in glyphOrder}

    fb = FontBuilder(1024, isTTF=True)
    fb.setupGlyphOrder(glyphOrder)
    fb.setupCharacterMap(cmap, uvs)
    fb.setupGlyf(glyphs)
    fb.setupHorizontalMetrics(metrics)
    fb.setupHorizontalHeader(ascent=824, descent=200)
    fb.setupNameTable(nameStrings)
    fb.setupOS2()
    fb.setupPost()

    outPath = os.path.join(str(tmpdir), "test_uvs.ttf")
    fb.save(outPath)
    _verifyOutput(outPath, tables=["cmap"])

    uvs = [
        (0x0030, 0xFE00, "zero.slash"),
        (
            0x0030, 0xFE01, "zero"
        ),  # should result in the exact same subtable data, due to cmap[0x0030] == "zero"
    ]
    fb.setupCharacterMap(cmap, uvs)
    fb.save(outPath)
    _verifyOutput(outPath, tables=["cmap"])
Exemple #32
0
    def runEndToEnd(self, filename):
        font = ttLib.TTFont()
        ttx_path = os.path.join(
            os.path.abspath(os.path.dirname(os.path.realpath(__file__))), '..',
            'ttLib', 'testdata', filename)
        font.importXML(ttx_path, quiet=True)

        glyphSet = font.getGlyphSet()
        glyfTable = font['glyf']
        pen = TTGlyphPen(font.getGlyphSet())

        for name in font.getGlyphOrder():
            oldGlyph = glyphSet[name]
            oldGlyph.draw(pen)
            oldGlyph = oldGlyph._glyph
            newGlyph = pen.glyph()

            if hasattr(oldGlyph, 'program'):
                newGlyph.program = oldGlyph.program

            self.assertEqual(oldGlyph.compile(glyfTable),
                             newGlyph.compile(glyfTable))
Exemple #33
0
    def setupTable_glyf(self):
        """Make the glyf table."""

        self.otf["loca"] = newTable("loca")
        self.otf["glyf"] = glyf = newTable("glyf")
        glyf.glyphs = {}
        glyf.glyphOrder = self.glyphOrder

        allGlyphs = self.allGlyphs
        for name in self.glyphOrder:
            glyph = allGlyphs[name]
            pen = TTGlyphPen(allGlyphs)
            try:
                glyph.draw(pen)
            except NotImplementedError:
                logger.error("%r has invalid curve format; skipped", name)
                ttGlyph = Glyph()
            else:
                ttGlyph = pen.glyph()
                if ttGlyph.isComposite() and self.autoUseMyMetrics:
                    self.autoUseMyMetrics(ttGlyph, glyph.width, allGlyphs)
            glyf[name] = ttGlyph
def test_unicodeVariationSequences(tmpdir):
    familyName = "UVSTestFont"
    styleName = "Regular"
    nameStrings = dict(familyName=familyName, styleName=styleName)
    nameStrings['psName'] = familyName + "-" + styleName
    glyphOrder = [".notdef", "space", "zero", "zero.slash"]
    cmap = {ord(" "): "space", ord("0"): "zero"}
    uvs = [
        (0x0030, 0xFE00, "zero.slash"),
        (0x0030, 0xFE01, None),  # not an official sequence, just testing
    ]
    metrics = {gn: (600, 0) for gn in glyphOrder}
    pen = TTGlyphPen(None)
    glyph = pen.glyph()  # empty placeholder
    glyphs = {gn: glyph for gn in glyphOrder}

    fb = FontBuilder(1024, isTTF=True)
    fb.setupGlyphOrder(glyphOrder)
    fb.setupCharacterMap(cmap, uvs)
    fb.setupGlyf(glyphs)
    fb.setupHorizontalMetrics(metrics)
    fb.setupHorizontalHeader(ascent=824, descent=200)
    fb.setupNameTable(nameStrings)
    fb.setupOS2()
    fb.setupPost()

    outPath = os.path.join(str(tmpdir), "test_uvs.ttf")
    fb.save(outPath)
    _verifyOutput(outPath, tables=["cmap"])

    uvs = [
        (0x0030, 0xFE00, "zero.slash"),
        (0x0030, 0xFE01, "zero"),  # should result in the exact same subtable data, due to cmap[0x0030] == "zero"
    ]
    fb.setupCharacterMap(cmap, uvs)
    fb.save(outPath)
    _verifyOutput(outPath, tables=["cmap"])
def process_font(input_path, output_path=None, verbose=False):
    """
    De-componentize a single font at input_path, saving to output_path (or
    input_path if None)
    """
    font = TTFont(input_path)
    output_path = output_path or input_path
    gs = font.getGlyphSet()
    drpen = DecomposingRecordingPen(gs)
    ttgpen = TTGlyphPen(gs)

    count = 0
    for glyphname in font.glyphOrder:
        glyph = font["glyf"][glyphname]
        if not glyph.isComposite():
            continue

        if verbose:
            print(f"    decomposing '{glyphname}'")

        # reset the pens
        drpen.value = []
        ttgpen.init()

        # draw the composite glyph into the decomposing pen
        glyph.draw(drpen, font["glyf"])
        # replay the recorded decomposed glyph into the TTGlyphPen
        drpen.replay(ttgpen)
        # store the decomposed glyph in the 'glyf' table
        font["glyf"][glyphname] = ttgpen.glyph()

        count += 1

    font.save(output_path)

    return count
    def test_no_handle_overflowing_transform(self):
        componentName = 'a'
        glyphSet = {}
        pen = TTGlyphPen(glyphSet, handleOverflowingTransforms=False)

        pen.moveTo((0, 0))
        pen.lineTo((0, 1))
        pen.lineTo((1, 0))
        pen.closePath()
        baseGlyph = pen.glyph()
        glyphSet[componentName] = _TestGlyph(baseGlyph)

        pen.addComponent(componentName, (3, 0, 0, 1, 0, 0))
        compositeGlyph = pen.glyph()

        self.assertEqual(compositeGlyph.components[0].transform,
                         ((3, 0), (0, 1)))

        with self.assertRaises(struct.error):
            compositeGlyph.compile({'a': baseGlyph})
Exemple #37
0
 def draw(self, ppp: float):
     pen = TTGlyphPen(None)
     for y, row in enumerate(reversed(self.bits)):
         for x, col in enumerate(row):
             if col:
                 pen.moveTo(
                     ((x + self.bbx.x) * ppp, (y + self.bbx.y) * ppp))
                 pen.lineTo(
                     ((x + self.bbx.x + 1) * ppp, (y + self.bbx.y) * ppp))
                 pen.lineTo((
                     (x + self.bbx.x + 1) * ppp,
                     (y + self.bbx.y + 1) * ppp,
                 ))
                 pen.lineTo(
                     ((x + self.bbx.x) * ppp, (y + self.bbx.y + 1) * ppp))
                 pen.lineTo(
                     ((x + self.bbx.x) * ppp, (y + self.bbx.y) * ppp))
                 pen.closePath()
     return pen.glyph()
Exemple #38
0
def test_build_var(tmpdir):
    outPath = os.path.join(str(tmpdir), "test_var.ttf")

    fb = FontBuilder(1024, isTTF=True)
    fb.setupGlyphOrder([".notdef", ".null", "A", "a"])
    fb.setupCharacterMap({65: "A", 97: "a"})

    advanceWidths = {".notdef": 600, "A": 600, "a": 600, ".null": 600}

    familyName = "HelloTestFont"
    styleName = "TotallyNormal"
    nameStrings = dict(familyName=dict(en="HelloTestFont", nl="HalloTestFont"),
                       styleName=dict(en="TotallyNormal", nl="TotaalNormaal"))
    nameStrings['psName'] = familyName + "-" + styleName

    pen = TTGlyphPen(None)
    pen.moveTo((100, 0))
    pen.lineTo((100, 400))
    pen.lineTo((500, 400))
    pen.lineTo((500, 000))
    pen.closePath()

    glyph = pen.glyph()

    pen = TTGlyphPen(None)
    emptyGlyph = pen.glyph()

    glyphs = {
        ".notdef": emptyGlyph,
        "A": glyph,
        "a": glyph,
        ".null": emptyGlyph
    }
    fb.setupGlyf(glyphs)
    metrics = {}
    glyphTable = fb.font["glyf"]
    for gn, advanceWidth in advanceWidths.items():
        metrics[gn] = (advanceWidth, glyphTable[gn].xMin)
    fb.setupHorizontalMetrics(metrics)

    fb.setupHorizontalHeader(ascent=824, descent=200)
    fb.setupNameTable(nameStrings)

    axes = [
        ('LEFT', 0, 0, 100, "Left"),
        ('RGHT', 0, 0, 100, "Right"),
        ('UPPP', 0, 0, 100, "Up"),
        ('DOWN', 0, 0, 100, "Down"),
    ]
    instances = [
        dict(location=dict(LEFT=0, RGHT=0, UPPP=0, DOWN=0),
             stylename="TotallyNormal"),
        dict(location=dict(LEFT=0, RGHT=100, UPPP=100, DOWN=0),
             stylename="Right Up"),
    ]
    fb.setupFvar(axes, instances)
    variations = {}
    # Four (x, y) pairs and four phantom points:
    leftDeltas = [(-200, 0), (-200, 0), (0, 0), (0, 0), None, None, None, None]
    rightDeltas = [(0, 0), (0, 0), (200, 0), (200, 0), None, None, None, None]
    upDeltas = [(0, 0), (0, 200), (0, 200), (0, 0), None, None, None, None]
    downDeltas = [(0, -200), (0, 0), (0, 0), (0, -200), None, None, None, None]
    variations['a'] = [
        TupleVariation(dict(RGHT=(0, 1, 1)), rightDeltas),
        TupleVariation(dict(LEFT=(0, 1, 1)), leftDeltas),
        TupleVariation(dict(UPPP=(0, 1, 1)), upDeltas),
        TupleVariation(dict(DOWN=(0, 1, 1)), downDeltas),
    ]
    fb.setupGvar(variations)

    fb.setupOS2()
    fb.setupPost()
    fb.setupDummyDSIG()

    fb.save(outPath)

    _verifyOutput(outPath)
def makeGlyfBBox1(calcBBoxes=True, composite=False):
    font = getTTFont(sfntTTFSourcePath, recalcBBoxes=calcBBoxes)
    glyf = font["glyf"]
    hmtx = font["hmtx"]
    for name in ("bbox1", "bbox2"):
        pen = TTGlyphPen(None)
        if name == "bbox1":
            pen.moveTo((0, 0))
            pen.lineTo((0, 1000))
            pen.lineTo((1000, 1000))
            pen.lineTo((1000, 0))
            pen.closePath()
        else:
            pen.moveTo((0, 0))
            pen.qCurveTo((500, 750), (600, 500), (500, 250), (0, 0))
            pen.closePath()
        glyph = pen.glyph()
        if not calcBBoxes:
            glyph.recalcBounds(glyf)
            glyph.xMax -= 100
        glyf.glyphs[name] = glyph
        hmtx.metrics[name] = (0, 0)
        glyf.glyphOrder.append(name)

    if composite:
        name = "bbox3"
        pen = TTGlyphPen(glyf.glyphOrder)
        pen.addComponent("bbox1", [1, 0, 0, 1, 0, 0])
        pen.addComponent("bbox2", [1, 0, 0, 1, 1000, 0])
        glyph = pen.glyph()
        glyph.recalcBounds(glyf)
        glyf.glyphs[name] = glyph
        hmtx.metrics[name] = (0, 0)
        glyf.glyphOrder.append(name)

    tableData = getSFNTData(font)[0]
    font.close()
    del font
    header, directory, tableData = defaultSFNTTestData(tableData=tableData, flavor="TTF")
    data = packSFNT(header, directory, tableData, flavor="TTF")
    return data
    def test_within_range_component_transform(self):
        componentName = 'a'
        glyphSet = {}
        pen = TTGlyphPen(glyphSet)

        pen.moveTo((0, 0))
        pen.lineTo((0, 1))
        pen.lineTo((1, 0))
        pen.closePath()
        glyphSet[componentName] = _TestGlyph(pen.glyph())

        pen.addComponent(componentName, (1.5, 0, 0, 1, 0, 0))
        pen.addComponent(componentName, (1, 0, 0, -1.5, 0, 0))
        compositeGlyph = pen.glyph()

        pen.addComponent(componentName, (1.5, 0, 0, 1, 0, 0))
        pen.addComponent(componentName, (1, 0, 0, -1.5, 0, 0))
        expectedGlyph = pen.glyph()

        self.assertEqual(expectedGlyph, compositeGlyph)
    def test_glyph_decomposes(self):
        componentName = 'a'
        glyphSet = {}
        pen = TTGlyphPen(glyphSet)

        pen.moveTo((0, 0))
        pen.lineTo((0, 1))
        pen.lineTo((1, 0))
        pen.closePath()
        glyphSet[componentName] = _TestGlyph(pen.glyph())

        pen.moveTo((0, 0))
        pen.lineTo((0, 1))
        pen.lineTo((1, 0))
        pen.closePath()
        pen.addComponent(componentName, (1, 0, 0, 1, 2, 0))
        compositeGlyph = pen.glyph()

        pen.moveTo((0, 0))
        pen.lineTo((0, 1))
        pen.lineTo((1, 0))
        pen.closePath()
        pen.moveTo((2, 0))
        pen.lineTo((2, 1))
        pen.lineTo((3, 0))
        pen.closePath()
        plainGlyph = pen.glyph()

        self.assertEqual(plainGlyph, compositeGlyph)
def makeCollectionHmtxTransform2():
    fonts = [getTTFont(sfntTTFSourcePath), getTTFont(sfntTTFSourcePath)]
    for i, font in enumerate(fonts):
        glyf = font["glyf"]
        hmtx = font["hmtx"]
        maxp = font["maxp"]
        for name in glyf.glyphs:
            glyph = glyf.glyphs[name]
            glyph.expand(glyf)
            if hasattr(glyph, "xMin"):
                metrics = hmtx.metrics[name]
                if i == 0:
                    # Move the glyph so that xMin is 0
                    pen = TTGlyphPen(None)
                    glyph.draw(pen, glyf, -glyph.xMin)
                    glyph = pen.glyph()
                    glyph.recalcBounds(glyf)
                    assert glyph.xMin == 0
                    glyf.glyphs[name] = glyph
                hmtx.metrics[name] = (metrics[0], 0)

        # Build a unique glyph for each font, but with the same advance and LSB
        name = "box"
        pen = TTGlyphPen(None)
        pen.moveTo([0, 0])
        pen.lineTo([0, 1000])
        if i > 0:
            pen.lineTo([0, 2000])
            pen.lineTo([1000, 2000])
        pen.lineTo([1000, 1000])
        pen.lineTo([1000, 0])
        pen.closePath()
        glyph = pen.glyph()
        glyph.recalcBounds(glyf)
        glyf.glyphs[name] = glyph
        hmtx.metrics[name] = (glyph.xMax, glyph.xMin)
        glyf.glyphOrder.append(name)
        maxp.recalc(font)
        data = hmtx.compile(font)
        hmtx.decompile(data, font)

    data = getSFNTCollectionData(fonts, shared=["hmtx"])

    return data
    def test_clamp_to_almost_2_component_transform(self):
        componentName = 'a'
        glyphSet = {}
        pen = TTGlyphPen(glyphSet)

        pen.moveTo((0, 0))
        pen.lineTo((0, 1))
        pen.lineTo((1, 0))
        pen.closePath()
        glyphSet[componentName] = _TestGlyph(pen.glyph())

        pen.addComponent(componentName, (1.99999, 0, 0, 1, 0, 0))
        pen.addComponent(componentName, (1, 2, 0, 1, 0, 0))
        pen.addComponent(componentName, (1, 0, 2, 1, 0, 0))
        pen.addComponent(componentName, (1, 0, 0, 2, 0, 0))
        pen.addComponent(componentName, (-2, 0, 0, -2, 0, 0))
        compositeGlyph = pen.glyph()

        almost2 = MAX_F2DOT14  # 0b1.11111111111111
        pen.addComponent(componentName, (almost2, 0, 0, 1, 0, 0))
        pen.addComponent(componentName, (1, almost2, 0, 1, 0, 0))
        pen.addComponent(componentName, (1, 0, almost2, 1, 0, 0))
        pen.addComponent(componentName, (1, 0, 0, almost2, 0, 0))
        pen.addComponent(componentName, (-2, 0, 0, -2, 0, 0))
        expectedGlyph = pen.glyph()

        self.assertEqual(expectedGlyph, compositeGlyph)
 def test_keep_duplicate_end_point(self):
     pen = TTGlyphPen(None)
     pen.moveTo((0, 0))
     pen.lineTo((100, 0))
     pen.qCurveTo((100, 50), (50, 100), (0, 0))
     pen.lineTo((0, 0))  # the duplicate point is not removed
     pen.closePath()
     self.assertEqual(len(pen.points), 5)
     self.assertEqual(pen.points[0], (0, 0))
Exemple #45
0
def make_font(feature_source, fea_type='fea'):
    """Return font with GSUB compiled from given source.

    Adds a bunch of filler tables so the font can be saved if needed, for
    debugging purposes.
    """

    # copied from fontTools' feaLib/builder_test.
    glyphs = """
        .notdef space slash fraction semicolon period comma ampersand
        quotedblleft quotedblright quoteleft quoteright
        zero one two three four five six seven eight nine
        zero.oldstyle one.oldstyle two.oldstyle three.oldstyle
        four.oldstyle five.oldstyle six.oldstyle seven.oldstyle
        eight.oldstyle nine.oldstyle onequarter onehalf threequarters
        onesuperior twosuperior threesuperior ordfeminine ordmasculine
        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
        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
        A.sc B.sc C.sc D.sc E.sc F.sc G.sc H.sc I.sc J.sc K.sc L.sc M.sc
        N.sc O.sc P.sc Q.sc R.sc S.sc T.sc U.sc V.sc W.sc X.sc Y.sc Z.sc
        A.alt1 A.alt2 A.alt3 B.alt1 B.alt2 B.alt3 C.alt1 C.alt2 C.alt3
        a.alt1 a.alt2 a.alt3 a.end b.alt c.mid d.alt d.mid
        e.begin e.mid e.end m.begin n.end s.end z.end
        Eng Eng.alt1 Eng.alt2 Eng.alt3
        A.swash B.swash C.swash D.swash E.swash F.swash G.swash H.swash
        I.swash J.swash K.swash L.swash M.swash N.swash O.swash P.swash
        Q.swash R.swash S.swash T.swash U.swash V.swash W.swash X.swash
        Y.swash Z.swash
        f_l c_h c_k c_s c_t f_f f_f_i f_f_l f_i o_f_f_i s_t f_i.begin
        a_n_d T_h T_h.swash germandbls ydieresis yacute breve
        grave acute dieresis macron circumflex cedilla umlaut ogonek caron
        damma hamza sukun kasratan lam_meem_jeem noon.final noon.initial
        by feature lookup sub table
    """.split()
    font = TTFont()
    font.setGlyphOrder(glyphs)
    glyph_order = font.getGlyphOrder()

    font['cmap'] = cmap = newTable('cmap')
    table = cmap_format_4(4)
    table.platformID = 3
    table.platEncID = 1
    table.language = 0
    table.cmap = {AGL2UV[n]: n for n in glyph_order if n in AGL2UV}
    cmap.tableVersion = 0
    cmap.tables = [table]

    font['glyf'] = glyf = newTable('glyf')
    glyf.glyphs = {}
    glyf.glyphOrder = glyph_order
    for name in glyph_order:
        pen = TTGlyphPen(None)
        glyf[name] = pen.glyph()

    font['head'] = head = newTable('head')
    head.tableVersion = 1.0
    head.fontRevision = 1.0
    head.flags = head.checkSumAdjustment = head.magicNumber =\
        head.created = head.modified = head.macStyle = head.lowestRecPPEM =\
        head.fontDirectionHint = head.indexToLocFormat =\
        head.glyphDataFormat =\
        head.xMin = head.xMax = head.yMin = head.yMax = 0
    head.unitsPerEm = 1000

    font['hhea'] = hhea = newTable('hhea')
    hhea.tableVersion = 0x00010000
    hhea.ascent = hhea.descent = hhea.lineGap =\
        hhea.caretSlopeRise = hhea.caretSlopeRun = hhea.caretOffset =\
        hhea.reserved0 = hhea.reserved1 = hhea.reserved2 = hhea.reserved3 =\
        hhea.metricDataFormat = hhea.advanceWidthMax = hhea.xMaxExtent =\
        hhea.minLeftSideBearing = hhea.minRightSideBearing =\
        hhea.numberOfHMetrics = 0

    font['hmtx'] = hmtx = newTable('hmtx')
    hmtx.metrics = {}
    for name in glyph_order:
        hmtx[name] = (600, 50)

    font['loca'] = newTable('loca')

    font['maxp'] = maxp = newTable('maxp')
    maxp.tableVersion = 0x00005000
    maxp.numGlyphs = 0

    font['post'] = post = newTable('post')
    post.formatType = 2.0
    post.extraNames = []
    post.mapping = {}
    post.glyphOrder = glyph_order
    post.italicAngle = post.underlinePosition = post.underlineThickness =\
        post.isFixedPitch = post.minMemType42 = post.maxMemType42 =\
        post.minMemType1 = post.maxMemType1 = 0

    if fea_type == 'fea':
        addOpenTypeFeaturesFromString(font, feature_source)
    elif fea_type == 'mti':
        font['GSUB'] = mtiLib.build(UnicodeIO(feature_source), font)

    return font
 def test_moveTo_errorWithinContour(self):
     pen = TTGlyphPen(None)
     pen.moveTo((0, 0))
     with self.assertRaises(AssertionError):
         pen.moveTo((1, 0))
    def test_endPath_sameAsClosePath(self):
        pen = TTGlyphPen(None)

        pen.moveTo((0, 0))
        pen.lineTo((0, 1))
        pen.lineTo((1, 0))
        pen.closePath()
        closePathGlyph = pen.glyph()

        pen.moveTo((0, 0))
        pen.lineTo((0, 1))
        pen.lineTo((1, 0))
        pen.endPath()
        endPathGlyph = pen.glyph()

        self.assertEqual(closePathGlyph, endPathGlyph)
 def test_glyph_errorOnUnendedContour(self):
     pen = TTGlyphPen(None)
     pen.moveTo((0, 0))
     with self.assertRaises(AssertionError):
         pen.glyph()
    def test_out_of_range_transform_decomposed(self):
        componentName = 'a'
        glyphSet = {}
        pen = TTGlyphPen(glyphSet)

        pen.moveTo((0, 0))
        pen.lineTo((0, 1))
        pen.lineTo((1, 0))
        pen.closePath()
        glyphSet[componentName] = _TestGlyph(pen.glyph())

        pen.addComponent(componentName, (3, 0, 0, 2, 0, 0))
        pen.addComponent(componentName, (1, 0, 0, 1, -1, 2))
        pen.addComponent(componentName, (2, 0, 0, -3, 0, 0))
        compositeGlyph = pen.glyph()

        pen.moveTo((0, 0))
        pen.lineTo((0, 2))
        pen.lineTo((3, 0))
        pen.closePath()
        pen.moveTo((-1, 2))
        pen.lineTo((-1, 3))
        pen.lineTo((0, 2))
        pen.closePath()
        pen.moveTo((0, 0))
        pen.lineTo((0, -3))
        pen.lineTo((2, 0))
        pen.closePath()
        expectedGlyph = pen.glyph()

        self.assertEqual(expectedGlyph, compositeGlyph)
Exemple #50
0
    def test_endPath_sameAsClosePath(self):
        pen = TTGlyphPen(None)

        pen.moveTo((0, 0))
        pen.lineTo((0, 1))
        pen.lineTo((1, 0))
        pen.closePath()
        closePathGlyph = pen.glyph()

        pen.moveTo((0, 0))
        pen.lineTo((0, 1))
        pen.lineTo((1, 0))
        pen.endPath()
        endPathGlyph = pen.glyph()

        endPathGlyph.program = closePathGlyph.program
        self.assertEqual(closePathGlyph, endPathGlyph)
Exemple #51
0
src = sys.argv[1]
dst = sys.argv[2]

with TTFont(src) as f:
    glyfTable = f["glyf"]
    glyphSet = f.getGlyphSet()

    for glyphName in glyphSet.keys():
        if not glyfTable[glyphName].isComposite():
            continue

        # record TTGlyph outlines without components
        dcPen = DecomposingRecordingPen(glyphSet)
        glyphSet[glyphName].draw(dcPen)

        # replay recording onto a skia-pathops Path
        path = pathops.Path()
        pathPen = path.getPen()
        dcPen.replay(pathPen)

        # remove overlaps
        path.simplify()

        # create new TTGlyph from Path
        ttPen = TTGlyphPen(None)
        path.draw(ttPen)
        glyfTable[glyphName] = ttPen.glyph()

    f.save(dst)
Exemple #52
0
 def test_glyph_errorOnUnendedContour(self):
     pen = TTGlyphPen(None)
     pen.moveTo((0, 0))
     with self.assertRaises(AssertionError):
         pen.glyph()
Exemple #53
0
 def test_recursiveComponent(self):
     glyphSet = {}
     pen_dummy = TTGlyphPen(glyphSet)
     glyph_dummy = pen_dummy.glyph()
     glyphSet["A"] = glyph_dummy
     glyphSet["B"] = glyph_dummy
     pen_A = TTGlyphPen(glyphSet)
     pen_A.addComponent("B", (1, 0, 0, 1, 0, 0))
     pen_B = TTGlyphPen(glyphSet)
     pen_B.addComponent("A", (1, 0, 0, 1, 0, 0))
     glyph_A = pen_A.glyph()
     glyph_B = pen_B.glyph()
     glyphSet["A"] = glyph_A
     glyphSet["B"] = glyph_B
     with self.assertRaisesRegex(
             TTLibError,
             "glyph '.' contains a recursive component reference"):
         glyph_A.getCoordinates(glyphSet)
Exemple #54
0
    def test_glyph_decomposes(self):
        componentName = 'a'
        glyphSet = {}
        pen = TTGlyphPen(glyphSet)

        pen.moveTo((0, 0))
        pen.lineTo((0, 1))
        pen.lineTo((1, 0))
        pen.closePath()
        glyphSet[componentName] = _TestGlyph(pen.glyph())

        pen.moveTo((0, 0))
        pen.lineTo((0, 1))
        pen.lineTo((1, 0))
        pen.closePath()
        pen.addComponent(componentName, (1, 0, 0, 1, 2, 0))
        compositeGlyph = pen.glyph()

        pen.moveTo((0, 0))
        pen.lineTo((0, 1))
        pen.lineTo((1, 0))
        pen.closePath()
        pen.moveTo((2, 0))
        pen.lineTo((2, 1))
        pen.lineTo((3, 0))
        pen.closePath()
        plainGlyph = pen.glyph()

        plainGlyph.program = compositeGlyph.program
        self.assertEqual(plainGlyph, compositeGlyph)
def fontfile():

	class Glyph(object):
		def __init__(self, empty=False, **kwargs):
			if not empty:
				self.draw = partial(self.drawRect, **kwargs)
			else:
				self.draw = lambda pen: None

		@staticmethod
		def drawRect(pen, xMin, xMax):
			pen.moveTo((xMin, 0))
			pen.lineTo((xMin, 1000))
			pen.lineTo((xMax, 1000))
			pen.lineTo((xMax, 0))
			pen.closePath()

	class CompositeGlyph(object):
		def __init__(self, components):
			self.components = components

		def draw(self, pen):
			for baseGlyph, (offsetX, offsetY) in self.components:
				pen.addComponent(baseGlyph, (1, 0, 0, 1, offsetX, offsetY))

	fb = fontBuilder.FontBuilder(unitsPerEm=1000, isTTF=True)
	fb.setupGlyphOrder(
		[".notdef", "space", "A", "acutecomb", "Aacute", "zero", "one", "two"]
	)
	fb.setupCharacterMap(
		{
			0x20: "space",
			0x41: "A",
			0x0301: "acutecomb",
			0xC1: "Aacute",
			0x30: "zero",
			0x31: "one",
			0x32: "two",
		}
	)
	fb.setupHorizontalMetrics(
		{
			".notdef": (500, 50),
			"space": (600, 0),
			"A": (550, 40),
			"acutecomb": (0, -40),
			"Aacute": (550, 40),
			"zero": (500, 30),
			"one": (500, 50),
			"two": (500, 40),
		}
	)
	fb.setupHorizontalHeader(ascent=1000, descent=-200)

	srcGlyphs = {
		".notdef": Glyph(xMin=50, xMax=450),
		"space": Glyph(empty=True),
		"A": Glyph(xMin=40, xMax=510),
		"acutecomb": Glyph(xMin=-40, xMax=60),
		"Aacute": CompositeGlyph([("A", (0, 0)), ("acutecomb", (200, 0))]),
		"zero": Glyph(xMin=30, xMax=470),
		"one": Glyph(xMin=50, xMax=450),
		"two": Glyph(xMin=40, xMax=460),
	}
	pen = TTGlyphPen(srcGlyphs)
	glyphSet = {}
	for glyphName, glyph in srcGlyphs.items():
		glyph.draw(pen)
		glyphSet[glyphName] = pen.glyph()
	fb.setupGlyf(glyphSet)

	fb.setupNameTable(
		{
			"familyName": "TestWOFF2",
			"styleName": "Regular",
			"uniqueFontIdentifier": "TestWOFF2 Regular; Version 1.000; ABCD",
			"fullName": "TestWOFF2 Regular",
			"version": "Version 1.000",
			"psName": "TestWOFF2-Regular",
		}
	)
	fb.setupOS2()
	fb.setupPost()

	buf = BytesIO()
	fb.save(buf)
	buf.seek(0)

	assert fb.font["maxp"].numGlyphs == 8
	assert fb.font["hhea"].numberOfHMetrics == 6
	for glyphName in fb.font.getGlyphOrder():
		xMin = getattr(fb.font["glyf"][glyphName], "xMin", 0)
		assert xMin == fb.font["hmtx"][glyphName][1]

	return buf
def test_build_var(tmpdir):
    outPath = os.path.join(str(tmpdir), "test_var.ttf")

    fb = FontBuilder(1024, isTTF=True)
    fb.setupGlyphOrder([".notdef", ".null", "A", "a"])
    fb.setupCharacterMap({65: "A", 97: "a"})

    advanceWidths = {".notdef": 600, "A": 600, "a": 600, ".null": 600}

    familyName = "HelloTestFont"
    styleName = "TotallyNormal"
    nameStrings = dict(familyName=dict(en="HelloTestFont", nl="HalloTestFont"),
                       styleName=dict(en="TotallyNormal", nl="TotaalNormaal"))
    nameStrings['psName'] = familyName + "-" + styleName

    pen = TTGlyphPen(None)
    pen.moveTo((100, 0))
    pen.lineTo((100, 400))
    pen.lineTo((500, 400))
    pen.lineTo((500, 000))
    pen.closePath()

    glyph = pen.glyph()

    pen = TTGlyphPen(None)
    emptyGlyph = pen.glyph()

    glyphs = {".notdef": emptyGlyph, "A": glyph, "a": glyph, ".null": emptyGlyph}
    fb.setupGlyf(glyphs)
    metrics = {}
    glyphTable = fb.font["glyf"]
    for gn, advanceWidth in advanceWidths.items():
        metrics[gn] = (advanceWidth, glyphTable[gn].xMin)
    fb.setupHorizontalMetrics(metrics)

    fb.setupHorizontalHeader(ascent=824, descent=200)
    fb.setupNameTable(nameStrings)

    axes = [
        ('LEFT', 0, 0, 100, "Left"),
        ('RGHT', 0, 0, 100, "Right"),
        ('UPPP', 0, 0, 100, "Up"),
        ('DOWN', 0, 0, 100, "Down"),
    ]
    instances = [
        dict(location=dict(LEFT=0, RGHT=0, UPPP=0, DOWN=0), stylename="TotallyNormal"),
        dict(location=dict(LEFT=0, RGHT=100, UPPP=100, DOWN=0), stylename="Right Up"),
    ]
    fb.setupFvar(axes, instances)
    variations = {}
    # Four (x, y) pairs and four phantom points:
    leftDeltas = [(-200, 0), (-200, 0), (0, 0), (0, 0), None, None, None, None]
    rightDeltas = [(0, 0), (0, 0), (200, 0), (200, 0), None, None, None, None]
    upDeltas = [(0, 0), (0, 200), (0, 200), (0, 0), None, None, None, None]
    downDeltas = [(0, -200), (0, 0), (0, 0), (0, -200), None, None, None, None]
    variations['a'] = [
        TupleVariation(dict(RGHT=(0, 1, 1)), rightDeltas),
        TupleVariation(dict(LEFT=(0, 1, 1)), leftDeltas),
        TupleVariation(dict(UPPP=(0, 1, 1)), upDeltas),
        TupleVariation(dict(DOWN=(0, 1, 1)), downDeltas),
    ]
    fb.setupGvar(variations)

    fb.setupOS2()
    fb.setupPost()
    fb.setupDummyDSIG()

    fb.save(outPath)

    _verifyOutput(outPath)