Example #1
0
    def glyph(self, componentFlags=0x4):
        assert self._isClosed(), "Didn't close last contour."

        components = []
        for glyphName, transformation in self.components:
            if self.points:
                # can't have both, so decompose the glyph
                tpen = TransformPen(self, transformation)
                self.glyphSet[glyphName].draw(tpen)
                continue

            component = GlyphComponent()
            component.glyphName = glyphName
            if transformation[:4] != (1, 0, 0, 1):
                component.transform = (transformation[:2], transformation[2:4])
            component.x, component.y = transformation[4:]
            component.flags = componentFlags
            components.append(component)

        glyph = Glyph()
        glyph.coordinates = GlyphCoordinates(self.points)
        glyph.endPtsOfContours = self.endPts
        glyph.flags = array("B", self.types)
        self.init()

        if components:
            glyph.components = components
            glyph.numberOfContours = -1
        else:
            glyph.numberOfContours = len(glyph.endPtsOfContours)
            glyph.program = ttProgram.Program()
            glyph.program.fromBytecode(b"")

        return glyph
Example #2
0
 def addComponent(self, glyphName, transformation):
     component = GlyphComponent()
     component.glyphName = glyphName
     component.transform = (transformation[:2], transformation[2:4])
     component.x, component.y = [int(n) for n in transformation[4:]]
     component.flags = 0
     self.components.append(component)
Example #3
0
    def _buildComponents(self, componentFlags):
        if self.handleOverflowingTransforms:
            # we can't encode transform values > 2 or < -2 in F2Dot14,
            # so we must decompose the glyph if any transform exceeds these
            overflowing = any(s > 2 or s < -2
                              for (glyphName, transformation) in self.components
                              for s in transformation[:4])
        components = []
        for glyphName, transformation in self.components:
            if glyphName not in self.glyphSet:
                self.log.warning(
                    "skipped non-existing component '%s'", glyphName
                )
                continue
            if (self.points or
                    (self.handleOverflowingTransforms and overflowing)):
                # can't have both coordinates and components, so decompose
                tpen = TransformPen(self, transformation)
                self.glyphSet[glyphName].draw(tpen)
                continue

            component = GlyphComponent()
            component.glyphName = glyphName
            component.x, component.y = transformation[4:]
            transformation = transformation[:4]
            if transformation != (1, 0, 0, 1):
                if (self.handleOverflowingTransforms and
                        any(MAX_F2DOT14 < s <= 2 for s in transformation)):
                    # clamp values ~= +2.0 so we can keep the component
                    transformation = tuple(MAX_F2DOT14 if MAX_F2DOT14 < s <= 2
                                           else s for s in transformation)
                component.transform = (transformation[:2], transformation[2:])
            component.flags = componentFlags
            components.append(component)
        return components
Example #4
0
    def glyph(self, componentFlags=0x4):
        assert self._isClosed(), "Didn't close last contour."

        if self.handleOverflowingTransforms:
            # we can't encode transform values > 2 or < -2 in F2Dot14,
            # so we must decompose the glyph if any transform exceeds these
            overflowing = any(s > 2 or s < -2
                              for (glyphName,
                                   transformation) in self.components
                              for s in transformation[:4])

        components = []
        for glyphName, transformation in self.components:
            if (self.points
                    or (self.handleOverflowingTransforms and overflowing)):
                # can't have both coordinates and components, so decompose
                try:
                    baseGlyph = self.glyphSet[glyphName]
                except KeyError:
                    self.log.debug(
                        "can't decompose non-existing component '%s'; skipped",
                        glyphName)
                    continue
                else:
                    tpen = TransformPen(self, transformation)
                    baseGlyph.draw(tpen)
                    continue

            component = GlyphComponent()
            component.glyphName = glyphName
            component.x, component.y = transformation[4:]
            transformation = transformation[:4]
            if transformation != (1, 0, 0, 1):
                if (self.handleOverflowingTransforms
                        and any(MAX_F2DOT14 < s <= 2 for s in transformation)):
                    # clamp values ~= +2.0 so we can keep the component
                    transformation = tuple(
                        MAX_F2DOT14 if MAX_F2DOT14 < s <= 2 else s
                        for s in transformation)
                component.transform = (transformation[:2], transformation[2:])
            component.flags = componentFlags
            components.append(component)

        glyph = Glyph()
        glyph.coordinates = GlyphCoordinates(self.points)
        glyph.endPtsOfContours = self.endPts
        glyph.flags = array("B", self.types)
        self.init()

        if components:
            glyph.components = components
            glyph.numberOfContours = -1
        else:
            glyph.numberOfContours = len(glyph.endPtsOfContours)
            glyph.program = ttProgram.Program()
            glyph.program.fromBytecode(b"")

        return glyph
Example #5
0
    def test_toXML_no_transform(self):
        comp = GlyphComponent()
        comp.glyphName = "a"
        comp.flags = ARGS_ARE_XY_VALUES
        comp.x, comp.y = 1, 2

        assert getXML(comp.toXML) == [
            '<component glyphName="a" x="1" y="2" flags="0x2"/>'
        ]
Example #6
0
    def test_fromXML_no_transform(self):
        comp = GlyphComponent()
        for name, attrs, content in parseXML(
            ['<component glyphName="a" x="1" y="2" flags="0x2"/>']):
            comp.fromXML(name, attrs, content, ttFont=None)

        assert comp.glyphName == "a"
        assert comp.flags & ARGS_ARE_XY_VALUES != 0
        assert (comp.x, comp.y) == (1, 2)
        assert not hasattr(comp, "transform")
Example #7
0
    def test_toXML_transform_scale(self):
        comp = GlyphComponent()
        comp.glyphName = "a"
        comp.flags = ARGS_ARE_XY_VALUES | WE_HAVE_A_SCALE
        comp.x, comp.y = 1, 2

        comp.transform = [[0.2999878, 0], [0, 0.2999878]]
        assert getXML(comp.toXML) == [
            '<component glyphName="a" x="1" y="2" scale="0.3" flags="0xa"/>'
        ]
Example #8
0
    def test_toXML_reference_points(self):
        comp = GlyphComponent()
        comp.glyphName = "a"
        comp.flags = 0
        comp.firstPt = 1
        comp.secondPt = 2

        assert getXML(comp.toXML) == [
            '<component glyphName="a" firstPt="1" secondPt="2" flags="0x0"/>'
        ]
Example #9
0
    def test_toXML_transform_2x2_scale(self):
        comp = GlyphComponent()
        comp.glyphName = "a"
        comp.flags = ARGS_ARE_XY_VALUES | WE_HAVE_A_TWO_BY_TWO
        comp.x, comp.y = 1, 2

        comp.transform = [[0.5999756, -0.2000122], [0.2000122, 0.2999878]]
        assert getXML(comp.toXML) == [
            '<component glyphName="a" x="1" y="2" scalex="0.6" scale01="-0.2" '
            'scale10="0.2" scaley="0.3" flags="0x82"/>'
        ]
Example #10
0
    def test_fromXML_reference_points(self):
        comp = GlyphComponent()
        for name, attrs, content in parseXML([
                '<component glyphName="a" firstPt="1" secondPt="2" flags="0x0"/>'
        ]):
            comp.fromXML(name, attrs, content, ttFont=None)

        assert comp.glyphName == "a"
        assert comp.flags == 0
        assert (comp.firstPt, comp.secondPt) == (1, 2)
        assert not hasattr(comp, "transform")
Example #11
0
 def _appendComponent(self, baseGlyph, transformation=None, identifier=None, **kwargs):
     c = GlyphComponent()
     c.transformation = transformation
     c.glyphName = baseGlyph
     c.x = 0
     c.y = 0
     c.flags = 0 # XXX
     glyph = self.naked()._glyph
     if hasattr(self.naked()._glyph,"components"):
         glyph.components.append(c)
     else:
         glyph.components = [c]
Example #12
0
    def test_fromXML_transform_scale(self):
        comp = GlyphComponent()
        for name, attrs, content in parseXML(
            ['<component glyphName="a" x="1" y="2" scale="0.3" flags="0xa"/>'
             ]):
            comp.fromXML(name, attrs, content, ttFont=None)

        assert comp.glyphName == "a"
        assert comp.flags & ARGS_ARE_XY_VALUES != 0
        assert comp.flags & WE_HAVE_A_SCALE != 0
        assert (comp.x, comp.y) == (1, 2)
        assert hasattr(comp, "transform")
        for value, expected in zip(itertools.chain(*comp.transform),
                                   [0.2999878, 0, 0, 0.2999878]):
            assert value == pytest.approx(expected)
Example #13
0
    def test_fromXML_transform_2x2_scale(self):
        comp = GlyphComponent()
        for name, attrs, content in parseXML([
                '<component glyphName="a" x="1" y="2" scalex="0.6" scale01="-0.2" '
                'scale10="0.2" scaley="0.3" flags="0x82"/>'
        ]):
            comp.fromXML(name, attrs, content, ttFont=None)

        assert comp.glyphName == "a"
        assert comp.flags & ARGS_ARE_XY_VALUES != 0
        assert comp.flags & WE_HAVE_A_TWO_BY_TWO != 0
        assert (comp.x, comp.y) == (1, 2)
        assert hasattr(comp, "transform")
        for value, expected in zip(
                itertools.chain(*comp.transform),
            [0.5999756, -0.2000122, 0.2000122, 0.2999878]):
            assert value == pytest.approx(expected)
Example #14
0
    def _buildComponents(self, componentFlags):
        if self.handleOverflowingTransforms:
            # we can't encode transform values > 2 or < -2 in F2Dot14,
            # so we must decompose the glyph if any transform exceeds these
            overflowing = any(s > 2 or s < -2
                              for (glyphName,
                                   transformation) in self.components
                              for s in transformation[:4])
        components = []
        for glyphName, transformation in self.components:
            if glyphName not in self.glyphSet:
                self.log.warning(
                    f"skipped non-existing component '{glyphName}'")
                continue
            if self.points or (self.handleOverflowingTransforms
                               and overflowing):
                # can't have both coordinates and components, so decompose
                self._decompose(glyphName, transformation)
                continue

            component = GlyphComponent()
            component.glyphName = glyphName
            component.x, component.y = (otRound(v) for v in transformation[4:])
            # quantize floats to F2Dot14 so we get same values as when decompiled
            # from a binary glyf table
            transformation = tuple(
                floatToFixedToFloat(v, 14) for v in transformation[:4])
            if transformation != (1, 0, 0, 1):
                if self.handleOverflowingTransforms and any(
                        MAX_F2DOT14 < s <= 2 for s in transformation):
                    # clamp values ~= +2.0 so we can keep the component
                    transformation = tuple(
                        MAX_F2DOT14 if MAX_F2DOT14 < s <= 2 else s
                        for s in transformation)
                component.transform = (transformation[:2], transformation[2:])
            component.flags = componentFlags
            components.append(component)
        return components
Example #15
0
    def test_getCoordinates(self):
        glyphSet = {}
        pen = TTGlyphPen(glyphSet)
        pen.moveTo((0, 0))
        pen.lineTo((100, 0))
        pen.lineTo((100, 100))
        pen.lineTo((0, 100))
        pen.closePath()
        # simple contour glyph
        glyphSet["a"] = a = pen.glyph()

        assert a.getCoordinates(glyphSet) == (
            GlyphCoordinates([(0, 0), (100, 0), (100, 100), (0, 100)]),
            [3],
            array.array("B", [1, 1, 1, 1]),
        )

        # composite glyph with only XY offset
        pen = TTGlyphPen(glyphSet)
        pen.addComponent("a", (1, 0, 0, 1, 10, 20))
        glyphSet["b"] = b = pen.glyph()

        assert b.getCoordinates(glyphSet) == (
            GlyphCoordinates([(10, 20), (110, 20), (110, 120), (10, 120)]),
            [3],
            array.array("B", [1, 1, 1, 1]),
        )

        # composite glyph with a scale (and referencing another composite glyph)
        pen = TTGlyphPen(glyphSet)
        pen.addComponent("b", (0.5, 0, 0, 0.5, 0, 0))
        glyphSet["c"] = c = pen.glyph()

        assert c.getCoordinates(glyphSet) == (
            GlyphCoordinates([(5, 10), (55, 10), (55, 60), (5, 60)]),
            [3],
            array.array("B", [1, 1, 1, 1]),
        )

        # composite glyph with unscaled offset (MS-style)
        pen = TTGlyphPen(glyphSet)
        pen.addComponent("a", (0.5, 0, 0, 0.5, 10, 20))
        glyphSet["d"] = d = pen.glyph()
        d.components[0].flags |= UNSCALED_COMPONENT_OFFSET

        assert d.getCoordinates(glyphSet) == (
            GlyphCoordinates([(10, 20), (60, 20), (60, 70), (10, 70)]),
            [3],
            array.array("B", [1, 1, 1, 1]),
        )

        # composite glyph with a scaled offset (Apple-style)
        pen = TTGlyphPen(glyphSet)
        pen.addComponent("a", (0.5, 0, 0, 0.5, 10, 20))
        glyphSet["e"] = e = pen.glyph()
        e.components[0].flags |= SCALED_COMPONENT_OFFSET

        assert e.getCoordinates(glyphSet) == (
            GlyphCoordinates([(5, 10), (55, 10), (55, 60), (5, 60)]),
            [3],
            array.array("B", [1, 1, 1, 1]),
        )

        # composite glyph where the 2nd and 3rd components use anchor points
        pen = TTGlyphPen(glyphSet)
        pen.addComponent("a", (1, 0, 0, 1, 0, 0))
        glyphSet["f"] = f = pen.glyph()

        comp1 = GlyphComponent()
        comp1.glyphName = "a"
        # aling the new component's pt 0 to pt 2 of contour points added so far
        comp1.firstPt = 2
        comp1.secondPt = 0
        comp1.flags = 0
        f.components.append(comp1)

        comp2 = GlyphComponent()
        comp2.glyphName = "a"
        # aling the new component's pt 0 to pt 6 of contour points added so far
        comp2.firstPt = 6
        comp2.secondPt = 0
        comp2.transform = [[0.707107, 0.707107], [-0.707107,
                                                  0.707107]]  # rotate 45 deg
        comp2.flags = WE_HAVE_A_TWO_BY_TWO
        f.components.append(comp2)

        coords, end_pts, flags = f.getCoordinates(glyphSet)
        assert end_pts == [3, 7, 11]
        assert flags == array.array("B", [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
        assert list(sum(coords, ())) == pytest.approx([
            0,
            0,
            100,
            0,
            100,
            100,
            0,
            100,
            100,
            100,
            200,
            100,
            200,
            200,
            100,
            200,
            200,
            200,
            270.7107,
            270.7107,
            200.0,
            341.4214,
            129.2893,
            270.7107,
        ])