Example #1
0
 def test_insertGlyph(self):
     font = Font(getTestFontPath())
     glyph = Glyph()
     glyph.name = "NewGlyphTest"
     self.assertEqual(sorted(font.keys()), ["A", "B", "C"])
     font.insertGlyph(glyph)
     self.assertEqual(sorted(font.keys()), ["A", "B", "C", "NewGlyphTest"])
class OutlinePen(BasePen):

    pointClass = MathPoint
    magicCurve = 0.5522847498

    def __init__(self, glyphSet, offset=10, contrast=0, contrastAngle=0, connection="square", cap="round", miterLimit=None, closeOpenPaths=True, optimizeCurve=False, preserveComponents=False):
        BasePen.__init__(self, glyphSet)

        self.offset = abs(offset)
        self.contrast = abs(contrast)
        self.contrastAngle = contrastAngle
        self._inputmiterLimit = miterLimit
        if miterLimit is None:
            miterLimit = self.offset * 2
        self.miterLimit = abs(miterLimit)

        self.closeOpenPaths = closeOpenPaths
        self.optimizeCurve = optimizeCurve

        self.connectionCallback = getattr(self, "connection%s" % (connection.title()))
        self.capCallback = getattr(self, "cap%s" % (cap.title()))

        self.originalGlyph = Glyph()
        self.originalPen = self.originalGlyph.getPen()

        self.outerGlyph = Glyph()
        self.outerPen = self.outerGlyph.getPen()
        self.outerCurrentPoint = None
        self.outerFirstPoint = None
        self.outerPrevPoint = None

        self.innerGlyph = Glyph()
        self.innerPen = self.innerGlyph.getPen()
        self.innerCurrentPoint = None
        self.innerFirstPoint = None
        self.innerPrevPoint = None

        self.prevPoint = None
        self.firstPoint = None
        self.firstAngle = None
        self.prevAngle = None

        self.shouldHandleMove = True

        self.preserveComponents = preserveComponents
        self.components = []

        self.drawSettings()

    def _moveTo(self, (x, y)):
        if self.offset == 0:
            self.outerPen.moveTo((x, y))
            self.innerPen.moveTo((x, y))
            return
        self.originalPen.moveTo((x, y))

        p = self.pointClass(x, y)
        self.prevPoint = p
        self.firstPoint = p
        self.shouldHandleMove = True
Example #3
0
 def test_appendContour(self):
     glyph = Glyph()
     glyph.dirty = False
     contour = Contour()
     glyph.appendContour(contour)
     self.assertEqual(len(glyph), 1)
     self.assertTrue(glyph.dirty)
     self.assertEqual(contour.getParent(), glyph)
Example #4
0
 def test_appendGuideline(self):
     glyph = Glyph()
     glyph.dirty = False
     guideline = Guideline()
     glyph.appendGuideline(guideline)
     self.assertEqual(len(glyph.guidelines), 1)
     self.assertTrue(glyph.dirty)
     self.assertEqual(guideline.getParent(), glyph)
Example #5
0
 def test_appendAnchor(self):
     glyph = Glyph()
     glyph.dirty = False
     anchor = Anchor()
     glyph.appendAnchor(anchor)
     self.assertEqual(len(glyph.anchors), 1)
     self.assertTrue(glyph.dirty)
     self.assertEqual(anchor.getParent(), glyph)
Example #6
0
 def test_appendComponent(self):
     glyph = Glyph()
     glyph.dirty = False
     component = Component()
     glyph.appendComponent(component)
     self.assertEqual(len(glyph.components), 1)
     self.assertTrue(glyph.dirty)
     self.assertEqual(component.getParent(), glyph)
    def __init__(
        self,
        glyphSet,
        offset=10,
        contrast=0,
        contrastAngle=0,
        connection="square",
        cap="round",
        miterLimit=None,
        closeOpenPaths=True,
        optimizeCurve=False,
        preserveComponents=False,
    ):
        BasePen.__init__(self, glyphSet)

        self.offset = abs(offset)
        self.contrast = abs(contrast)
        self.contrastAngle = contrastAngle
        self._inputmiterLimit = miterLimit
        if miterLimit is None:
            miterLimit = self.offset * 2
        self.miterLimit = abs(miterLimit)

        self.closeOpenPaths = closeOpenPaths
        self.optimizeCurve = optimizeCurve

        self.connectionCallback = getattr(self, "connection%s" % (connection.title()))
        self.capCallback = getattr(self, "cap%s" % (cap.title()))

        self.originalGlyph = Glyph()
        self.originalPen = self.originalGlyph.getPen()

        self.outerGlyph = Glyph()
        self.outerPen = self.outerGlyph.getPen()
        self.outerCurrentPoint = None
        self.outerFirstPoint = None
        self.outerPrevPoint = None

        self.innerGlyph = Glyph()
        self.innerPen = self.innerGlyph.getPen()
        self.innerCurrentPoint = None
        self.innerFirstPoint = None
        self.innerPrevPoint = None

        self.prevPoint = None
        self.firstPoint = None
        self.firstAngle = None
        self.prevAngle = None

        self.shouldHandleMove = True

        self.preserveComponents = preserveComponents
        self.components = []

        self.drawSettings()
class OutlinePen(BasePen):
    
    pointClass = MathPoint
    magicCurve = 0.5522847498
    
    def __init__(self, glyphSet, offset=10, connection="square", cap="round", mitterLimit=None, closeOpenPaths=True):
        BasePen.__init__(self, glyphSet)
        
        self.offset = abs(offset)
        self._inputMitterLimit = mitterLimit
        if mitterLimit is None:
            mitterLimit = self.offset
        self.mitterLimit = abs(mitterLimit)
        
        self.closeOpenPaths = closeOpenPaths
        
        self.connectionCallback = getattr(self, "connection%s" %(connection[0].capitalize() + connection[1:]))
        self.capCallback = getattr(self, "cap%s" %(cap[0].capitalize() + cap[1:]))
        
        self.originalGlyph = Glyph()
        self.originalPen = self.originalGlyph.getPen()
        
        self.outerGlyph = Glyph()
        self.outerPen = self.outerGlyph.getPen()
        self.outerCurrentPoint = None
        self.outerFirstPoint = None
        self.outerPrevPoint = None
        
        self.innerGlyph = Glyph()
        self.innerPen = self.innerGlyph.getPen()
        self.innerCurrentPoint = None
        self.innerFirstPoint = None
        self.innerPrevPoint = None
        
        self.prevPoint = None
        self.firstPoint = None
        self.firstAngle = None
        self.prevAngle = None
                
        self.shouldHandleMove = True
        
        self.drawSettings()
        
    def _moveTo(self, (x, y)):
        if self.offset == 0:
            self.outerPen.moveTo((x, y))
            self.innerPen.moveTo((x, y))
            return
        self.originalPen.moveTo((x, y))
        
        p = self.pointClass(x, y)
        self.prevPoint = p
        self.firstPoint = p
        self.shouldHandleMove = True
def test_remove_tiny_sub_paths_large_contour():
    g = Glyph()
    p = g.getPen()
    p.moveTo((100, 100))
    p.lineTo((200, 200))
    p.lineTo((0, 100))
    p.closePath()
    assert len(g[0]) == 3
    assert g.bounds == (0, 100, 200, 200)
    bg = BooleanGlyph(g)
    assert remove_tiny_sub_paths(bg, 25, []) == []
Example #10
0
def test_remove_tiny_sub_paths_small_contour():
    g = Glyph()
    p = g.getPen()
    p.moveTo((1, 1))
    p.lineTo((2, 2))
    p.lineTo((0, 1))
    p.closePath()
    assert len(g[0]) == 3
    assert g.bounds == (0, 1, 2, 2)
    bg = BooleanGlyph(g)
    assert remove_tiny_sub_paths(bg, 25, []) == \
        ['Contour 0 is too small: bounding box is less than minimum area. '
         'Start point: ((1, 1)).']
def test_remove_tiny_sub_paths_small_contour():
    g = Glyph()
    p = g.getPen()
    p.moveTo((1, 1))
    p.lineTo((2, 2))
    p.lineTo((0, 1))
    p.closePath()
    assert len(g[0]) == 3
    assert g.bounds == (0, 1, 2, 2)
    bg = BooleanGlyph(g)
    assert remove_tiny_sub_paths(bg, 25, []) == \
        ['Contour 0 is too small: bounding box is less than minimum area. '
         'Start point: ((1, 1)).']
Example #12
0
def glyph_to_quadratic(glyph, max_n, max_err, correctDirection=True,
                       verbose=False):
    """ Convert the glyph outline to TrueType quadratic splines. """
    new = Glyph()
    writerPen = new.getPointPen()
    cu2quPen = Cu2QuPen(writerPen, max_n, max_err, verbose)
    if correctDirection:
        reversePen = ReverseContourPointPen(cu2quPen)
        glyph.drawPoints(reversePen)
    else:
        glyph.drawPoints(cu2quPen)
    # clear glyph but keep anchors for mark, mkmk features
    glyph.clearContours()
    glyph.clearComponents()
    writerPen = glyph.getPointPen()
    new.drawPoints(writerPen)
 def test_correct_direction_same_area(self):
     glyph = Glyph()
     pen = glyph.getPointPen()
     pen.beginPath()
     pen.addPoint((0, 0), segmentType="line")
     pen.addPoint((0, 50), segmentType="line")
     pen.addPoint((50, 50), segmentType="line")
     pen.endPath()
     pen.beginPath()
     pen.addPoint((50, 50), segmentType="line")
     pen.addPoint((50, 100), segmentType="line")
     pen.addPoint((100, 100), segmentType="line")
     pen.endPath()
     try:
         glyph.correctContourDirection()
     except Exception as e:
         self.fail("glyph.correctContourDirection() raised unexpected exception: "
                   + str(e))
Example #14
0
 def test_extract_scaled_glyph_as_Defcon_Glyph(self):
     """Test scaled glyph retrieval as a Defcon glyph."""
     from defcon import Glyph
     for testFont in [self.smallFont, self.stemedSmallFont]:
         scaledGlyph = Glyph()
         for glyphName in self.glyphNames:
             testFont.extractGlyph(glyphName, scaledGlyph)
             self.assertIsInstance(scaledGlyph, Glyph)
             self.assertEqual(scaledGlyph.name, glyphName)
Example #15
0
def create_glyph(codepoint, width, contours):
    glyph = Glyph()
    glyph.name = get_glyph_name(codepoint)
    glyph.unicode = ord(codepoint)
    glyph.width = width
    for contour in contours:
        glyph.appendContour(contour)
    return glyph
 def test_glyph(self):
     self.assertIsNone(self.contour.glyph)
     self.contour = Contour(self.glyph)
     self.assertEqual(self.contour.glyph, self.glyph)
     glyph = Glyph()
     self.contour = Contour()
     self.contour.glyph = glyph
     self.assertEqual(self.contour.glyph, glyph)
     with self.assertRaises(AssertionError):
         self.contour.glyph = self.glyph
Example #17
0
 def to_glyph(self, name=None, width=None, allow_blank=False):
     """
     Create a glyph (like from `defcon`) using this pen’s value.
     *Warning*: if path is unended, closedPath will be called
     """
     from defcon import Glyph
     if not allow_blank:
         if self.unended():
             self.closePath()
     bounds = self.bounds()
     glyph = Glyph()
     glyph.name = name
     glyph.width = width or bounds.w
     try:
         sp = glyph.getPen()
         self.replay(sp)
     except AssertionError:
         if not allow_blank:
             print(">>>blank glyph:", glyph.name)
     return glyph
Example #18
0
    def test_overlapping_start_end_points(self):
        # https://github.com/googlefonts/fontmake/issues/572
        glyph1 = Glyph()
        pen = glyph1.getPointPen()
        pen.beginPath()
        pen.addPoint((0, 651), segmentType="line")
        pen.addPoint((0, 101), segmentType="line")
        pen.addPoint((0, 101), segmentType="line")
        pen.addPoint((0, 651), segmentType="line")
        pen.endPath()

        glyph2 = Glyph()
        pen = glyph2.getPointPen()
        pen.beginPath()
        pen.addPoint((1, 651), segmentType="line")
        pen.addPoint((2, 101), segmentType="line")
        pen.addPoint((3, 101), segmentType="line")
        pen.addPoint((4, 651), segmentType="line")
        pen.endPath()

        glyphs = [glyph1, glyph2]

        assert glyphs_to_quadratic(glyphs, reverse_direction=True)

        assert [[(p.x, p.y) for p in glyph[0]] for glyph in glyphs] == [
            [
                (0, 651),
                (0, 651),
                (0, 101),
                (0, 101),
            ],
            [(1, 651), (4, 651), (3, 101), (2, 101)],
        ]
Example #19
0
    def __init__(self,
                 glyphSet,
                 offset=10,
                 contrast=0,
                 contrastAngle=0,
                 connection="square",
                 cap="round",
                 miterLimit=None,
                 closeOpenPaths=True,
                 optimizeCurve=False,
                 preserveComponents=False,
                 filterDoubles=True,
                 alwaysConnect=False):
        BasePen.__init__(self, glyphSet)

        self.offset = abs(offset)
        self.contrast = abs(contrast)
        self.contrastAngle = contrastAngle
        self._inputmiterLimit = miterLimit
        if miterLimit is None:
            miterLimit = self.offset * 2
        self.miterLimit = abs(miterLimit)

        self.closeOpenPaths = closeOpenPaths
        self.optimizeCurve = optimizeCurve

        self.connectionCallback = getattr(
            self, "connection%s" % (connection.title()))
        self.capCallback = getattr(self, "cap%s" % (cap.title()))

        self.originalGlyph = Glyph()
        self.originalPen = self.originalGlyph.getPen()

        self.outerGlyph = Glyph()
        self.outerPen = self.outerGlyph.getPen()
        self.outerCurrentPoint = None
        self.outerFirstPoint = None
        self.outerPrevPoint = None

        self.innerGlyph = Glyph()
        self.innerPen = self.innerGlyph.getPen()
        self.innerCurrentPoint = None
        self.innerFirstPoint = None
        self.innerPrevPoint = None

        self.prevPoint = None
        self.firstPoint = None
        self.firstAngle = None
        self.prevAngle = None

        self.shouldHandleMove = True

        self.preserveComponents = preserveComponents
        self.components = []

        self.filterDoubles = filterDoubles
        self.alwaysConnect = alwaysConnect
        self.drawSettings()
Example #20
0
    def find_shape_diffs(self):
        """Report differences in glyph shapes, using BooleanOperations."""

        self.build_names()

        area_pen = GlyphAreaPen(None)
        pen = PointToSegmentPen(area_pen)
        mismatched = {}
        for name in self.names:
            glyph_a = Glyph()
            glyph_b = Glyph()
            self.glyph_set_a[name].draw(
                Qu2CuPen(glyph_a.getPen(), self.glyph_set_a))
            self.glyph_set_b[name].draw(
                Qu2CuPen(glyph_b.getPen(), self.glyph_set_b))
            booleanOperations.xor(list(glyph_a), list(glyph_b), pen)
            area = abs(area_pen.pop())
            if area:
                mismatched[name] = (area)

        stats = self.stats['compared']
        for name, area in mismatched.items():
            stats.append((area, name, self.basepath))
Example #21
0
    def __init__(self, glyphSet, offset=10, connection="square", cap="round", miterLimit=None, closeOpenPaths=True):
        BasePen.__init__(self, glyphSet)

        self.offset = abs(offset)
        self._inputmiterLimit = miterLimit
        if miterLimit is None:
            miterLimit = self.offset
        self.miterLimit = abs(miterLimit)

        self.closeOpenPaths = closeOpenPaths

        self.connectionCallback = getattr(self, "connection%s" %(connection[0].capitalize() + connection[1:]))
        self.capCallback = getattr(self, "cap%s" %(cap[0].capitalize() + cap[1:]))

        self.originalGlyph = Glyph()
        self.originalPen = self.originalGlyph.getPen()

        self.outerGlyph = Glyph()
        self.outerPen = self.outerGlyph.getPen()
        self.outerCurrentPoint = None
        self.outerFirstPoint = None
        self.outerPrevPoint = None

        self.innerGlyph = Glyph()
        self.innerPen = self.innerGlyph.getPen()
        self.innerCurrentPoint = None
        self.innerFirstPoint = None
        self.innerPrevPoint = None

        self.prevPoint = None
        self.firstPoint = None
        self.firstAngle = None
        self.prevAngle = None

        self.shouldHandleMove = True

        self.drawSettings()
Example #22
0
def test_contoursToZs_open_contour():
    glyph = Glyph()
    pen = glyph.getPointPen()
    pen.beginPath()
    pen.addPoint((0, 0), 'move')
    pen.addPoint((1, 1), 'line')
    pen.addPoint((2, 2), 'line')
    pen.endPath()

    shape = contoursToZs(glyph)

    assert shape == [[{
        'x': 0,
        'y': 0,
        'on': True
    }, {
        'x': 1,
        'y': 1,
        'on': True
    }, {
        'x': 2,
        'y': 2,
        'on': True
    }]]
Example #23
0
 def test_insertGlyph(self):
     font = Font()
     layer = font.layers[None]
     source = Glyph()
     source.unicodes = [1, 2]
     source.name = "a"
     dest = layer.insertGlyph(source, name="nota")
     self.assertNotEqual(dest, source)
     self.assertEqual(dest.name, "nota")
     self.assertEqual(list(layer.unicodeData.items()), [(1, ["nota"]),
                                                        (2, ["nota"])])
     source = Glyph()
     source.unicodes = [3]
     source.name = "b"
     dest = layer.insertGlyph(source)
     self.assertNotEqual(dest, source)
     self.assertEqual(dest.name, "b")
     self.assertEqual(list(layer.unicodeData.items()), [(1, ["nota"]),
                                                        (2, ["nota"]),
                                                        (3, ["b"])])
Example #24
0
    def __init__(self,
                 glyphSet,
                 offset=10,
                 connection="square",
                 cap="round",
                 miterLimit=None,
                 closeOpenPaths=True):
        BasePen.__init__(self, glyphSet)

        self.offset = abs(offset)
        self._inputmiterLimit = miterLimit
        if miterLimit is None:
            miterLimit = self.offset
        self.miterLimit = abs(miterLimit)

        self.closeOpenPaths = closeOpenPaths

        self.connectionCallback = getattr(
            self,
            "connection%s" % (connection[0].capitalize() + connection[1:]))
        self.capCallback = getattr(self,
                                   "cap%s" % (cap[0].capitalize() + cap[1:]))

        self.originalGlyph = Glyph()
        self.originalPen = self.originalGlyph.getPen()

        self.outerGlyph = Glyph()
        self.outerPen = self.outerGlyph.getPen()
        self.outerCurrentPoint = None
        self.outerFirstPoint = None
        self.outerPrevPoint = None

        self.innerGlyph = Glyph()
        self.innerPen = self.innerGlyph.getPen()
        self.innerCurrentPoint = None
        self.innerFirstPoint = None
        self.innerPrevPoint = None

        self.prevPoint = None
        self.firstPoint = None
        self.firstAngle = None
        self.prevAngle = None

        self.shouldHandleMove = True

        self.drawSettings()
Example #25
0
 def test_insertGlyph(self):
     font = Font()
     layer = font.layers[None]
     source = Glyph()
     source.unicodes = [1, 2]
     source.name = "a"
     dest = layer.insertGlyph(source, name="nota")
     self.assertNotEqual(dest, source)
     self.assertEqual(dest.name, "nota")
     self.assertEqual(list(layer.unicodeData.items()),
                      [(1, ["nota"]), (2, ["nota"])])
     source = Glyph()
     source.unicodes = [3]
     source.name = "b"
     dest = layer.insertGlyph(source)
     self.assertNotEqual(dest, source)
     self.assertEqual(dest.name, "b")
     self.assertEqual(list(layer.unicodeData.items()),
                      [(1, ["nota"]), (2, ["nota"]), (3, ["b"])])
 def test_identifier(self):
     glyph = Glyph()
     contour = Contour()
     glyph.appendContour(contour)
     contour.identifier = "contour 1"
     self.assertEqual(contour.identifier, "contour 1")
     self.assertEqual(sorted(glyph.identifiers), ["contour 1"])
     contour = Contour()
     glyph.appendContour(contour)
     with self.assertRaises(AssertionError):
         contour.identifier = "contour 1"
     contour.identifier = "contour 2"
     self.assertEqual(sorted(glyph.identifiers), ["contour 1", "contour 2"])
     contour.identifier = "not contour 2 anymore"
     self.assertEqual(contour.identifier, "contour 2")
     self.assertEqual(sorted(glyph.identifiers), ["contour 1", "contour 2"])
     contour.identifier = None
     self.assertEqual(contour.identifier, "contour 2")
     self.assertEqual(sorted(glyph.identifiers), ["contour 1", "contour 2"])
Example #27
0
    def find_shape_diffs(self):
        """Report differences in glyph shapes, using BooleanOperations."""

        self.build_names()

        area_pen = GlyphAreaPen(None)
        pen = PointToSegmentPen(area_pen)
        mismatched = {}
        for name in self.names:
            glyph_a = Glyph()
            glyph_b = Glyph()
            self.glyph_set_a[name].draw(Qu2CuPen(glyph_a.getPen(), self.glyph_set_a))
            self.glyph_set_b[name].draw(Qu2CuPen(glyph_b.getPen(), self.glyph_set_b))
            booleanOperations.xor(list(glyph_a), list(glyph_b), pen)
            area = abs(area_pen.pop())
            if area:
                mismatched[name] = area

        stats = self.stats["compared"]
        for name, area in mismatched.items():
            stats.append((area, name, self.basepath))
Example #28
0
class OutlineFitterPen(BasePen):

    pointClass = MathPoint
    magicCurve = 0.5522847498

    def __init__(self,
                 glyphSet,
                 offset=10,
                 contrast=0,
                 contrastAngle=0,
                 connection="square",
                 cap="round",
                 miterLimit=None,
                 closeOpenPaths=True,
                 optimizeCurve=False,
                 preserveComponents=False,
                 filterDoubles=True,
                 alwaysConnect=False):
        BasePen.__init__(self, glyphSet)

        self.offset = abs(offset)
        self.contrast = abs(contrast)
        self.contrastAngle = contrastAngle
        self._inputmiterLimit = miterLimit
        if miterLimit is None:
            miterLimit = self.offset * 2
        self.miterLimit = abs(miterLimit)

        self.closeOpenPaths = closeOpenPaths
        self.optimizeCurve = optimizeCurve

        self.connectionCallback = getattr(
            self, "connection%s" % (connection.title()))
        self.capCallback = getattr(self, "cap%s" % (cap.title()))

        self.originalGlyph = Glyph()
        self.originalPen = self.originalGlyph.getPen()

        self.outerGlyph = Glyph()
        self.outerPen = self.outerGlyph.getPen()
        self.outerCurrentPoint = None
        self.outerFirstPoint = None
        self.outerPrevPoint = None

        self.innerGlyph = Glyph()
        self.innerPen = self.innerGlyph.getPen()
        self.innerCurrentPoint = None
        self.innerFirstPoint = None
        self.innerPrevPoint = None

        self.prevPoint = None
        self.firstPoint = None
        self.firstAngle = None
        self.prevAngle = None

        self.shouldHandleMove = True

        self.preserveComponents = preserveComponents
        self.components = []

        self.filterDoubles = filterDoubles
        self.alwaysConnect = alwaysConnect
        self.drawSettings()

    def _moveTo(self, pt):
        x, y = pt
        if self.offset == 0:
            self.outerPen.moveTo((x, y))
            self.innerPen.moveTo((x, y))
            return
        self.originalPen.moveTo((x, y))

        p = self.pointClass(x, y)
        self.prevPoint = p
        self.firstPoint = p
        self.shouldHandleMove = True

    def _lineTo(self, pt):
        x, y = pt
        if self.offset == 0:
            self.outerPen.lineTo((x, y))
            self.innerPen.lineTo((x, y))
            return
        self.originalPen.lineTo((x, y))

        currentPoint = self.pointClass(x, y)
        if currentPoint == self.prevPoint:
            return

        self.currentAngle = self.prevPoint.angle(currentPoint)
        thickness = self.getThickness(self.currentAngle)
        self.innerCurrentPoint = self.prevPoint - self.pointClass(
            cos(self.currentAngle), sin(self.currentAngle)) * thickness
        self.outerCurrentPoint = self.prevPoint + self.pointClass(
            cos(self.currentAngle), sin(self.currentAngle)) * thickness

        if self.shouldHandleMove:
            self.shouldHandleMove = False

            self.innerPen.moveTo(self.innerCurrentPoint)
            self.innerFirstPoint = self.innerCurrentPoint

            self.outerPen.moveTo(self.outerCurrentPoint)
            self.outerFirstPoint = self.outerCurrentPoint

            self.firstAngle = self.currentAngle
        else:
            self.buildConnection()

        self.innerCurrentPoint = currentPoint - self.pointClass(
            cos(self.currentAngle), sin(self.currentAngle)) * thickness
        self.innerPen.lineTo(self.innerCurrentPoint)
        self.innerPrevPoint = self.innerCurrentPoint

        self.outerCurrentPoint = currentPoint + self.pointClass(
            cos(self.currentAngle), sin(self.currentAngle)) * thickness
        self.outerPen.lineTo(self.outerCurrentPoint)
        self.outerPrevPoint = self.outerCurrentPoint

        self.prevPoint = currentPoint
        self.prevAngle = self.currentAngle

    def _curveToOne(self, pt1, pt2, pt3):
        if self.optimizeCurve:
            curves = splitCubicAtT(self.prevPoint, pt1, pt2, pt3, .5)
        else:
            curves = [(self.prevPoint, pt1, pt2, pt3)]
        for curve in curves:
            p1, h1, h2, p2 = curve
            self._processCurveToOne(h1, h2, p2)

    def _processCurveToOne(self, pt1, pt2, pt3):
        if self.offset == 0:
            self.outerPen.curveTo(pt1, pt2, pt3)
            self.innerPen.curveTo(pt1, pt2, pt3)
            return
        self.originalPen.curveTo(pt1, pt2, pt3)

        p1 = self.pointClass(*pt1)
        p2 = self.pointClass(*pt2)
        p3 = self.pointClass(*pt3)

        if p1 == self.prevPoint:
            p1 = pointOnACurve(self.prevPoint, p1, p2, p3, 0.01)
        if p2 == p3:
            p2 = pointOnACurve(self.prevPoint, p1, p2, p3, 0.99)

        a1 = self.prevPoint.angle(p1)
        a2 = p2.angle(p3)

        self.currentAngle = a1
        tickness1 = self.getThickness(a1)
        tickness2 = self.getThickness(a2)

        a1bis = self.prevPoint.angle(p1, 0)
        a2bis = p3.angle(p2, 0)
        self.innerCurrentPoint = self.prevPoint - self.pointClass(
            cos(a1), sin(a1)) * tickness1
        self.outerCurrentPoint = self.prevPoint + self.pointClass(
            cos(a1), sin(a1)) * tickness1

        if self.shouldHandleMove:
            self.shouldHandleMove = False

            self.innerPen.moveTo(self.innerCurrentPoint)
            self.innerFirstPoint = self.innerPrevPoint = self.innerCurrentPoint

            self.outerPen.moveTo(self.outerCurrentPoint)
            self.outerFirstPoint = self.outerPrevPoint = self.outerCurrentPoint

            self.firstAngle = a1
        else:
            self.buildConnection()

        # Collect four original points
        originalPoints = [self.prevPoint, p1, p2, p3]

        # Collect four inner points
        h1 = p1 - self.pointClass(cos(a1), sin(a1)) * tickness1
        h2 = p2 - self.pointClass(cos(a2), sin(a2)) * tickness2
        innerPoints = [self.innerCurrentPoint, h1, h2]
        self.innerCurrentPoint = p3 - self.pointClass(cos(a2),
                                                      sin(a2)) * tickness2
        innerPoints.append(self.innerCurrentPoint)

        # Collect four outer points
        h1 = p1 + self.pointClass(cos(a1), sin(a1)) * tickness1
        h2 = p2 + self.pointClass(cos(a2), sin(a2)) * tickness2
        outerPoints = [self.outerCurrentPoint, h1, h2]
        self.outerCurrentPoint = p3 + self.pointClass(cos(a2),
                                                      sin(a2)) * tickness2
        outerPoints.append(self.outerCurrentPoint)
        """
        # @@@ NEW TECHNIQUE:
        
        # Determine if a curve in/out of a BCP is rotating clockwise or counterclockwise
        # Compare vectors of the point to the bcp and the point to a split location just after/before the point
        splits = splitCubicAtT(self.prevPoint, p1, p2, p3, 0.01, 0.99)
        splitPt0 = self.pointClass(splits[0][-1])
        splitPt1 = self.pointClass(splits[-1][0])
        # vectors
        vBcp0 = self.prevPoint - p1
        vNext0 = self.prevPoint - splitPt0
        vBcp1 = p3 - p2
        vNext1 = p3 - splitPt1
        # Find which way the vector is rotating by looking at the sign of the dot product
        dot0 = vBcp0[0] * vNext0[1] - vBcp0[1] * vNext0[0]
        dot1 = vBcp1[0] * vNext1[1] - vBcp1[1] * vNext1[0]

        dir0 = 1
        dir1 = 1
        if dot0 < 0:
            dir0 = -1
        if dot1 < 0:
            dir1 = -1
        """

        # @@@ OLD TECHNIQUE:

        # Determine if a curve in/out of a BCP is rotating clockwise or counterclockwise
        # Compare vectors of the point to the bcp and the point to the next point
        vBcp0 = self.prevPoint - p1
        vNext0 = self.prevPoint - p3
        vBcp1 = self.pointClass(pt3) - p2
        vNext1 = self.pointClass(pt3) - self.prevPoint
        # Find which way the vector is rotating by looking at the sign of the dot product
        dot0 = vBcp0[0] * vNext0[1] - vBcp0[1] * vNext0[0]
        dot1 = vBcp1[0] * vNext1[1] - vBcp1[1] * vNext1[0]
        # Find the angles
        # ang0 = self.prevPoint.angle(p1)
        # ang1 = self.pointClass(pt3).angle(p2)
        # print(ang0, ang1)
        dir0 = 1
        dir1 = 1
        if dot0 < 0:
            dir0 = -1
        if dot1 < 0:
            dir1 = -1

        # print(dot0, dot1)
        """
        # If the onCurves are closer to each other than 2x the thickness, reverse the direction of the inner curve
        angle = self.prevPoint.angle(p3)
        distance = self.prevPoint.distance(p3)
        thickness = self.getThickness(angle)
        reverseInner = False
        #print(distance, thickness)
        if distance < thickness * 2:
            reverseInner = True
        # @@@ Do this differently, it's not helping
            
            
        Two bugs (maybe related)
        
        - When the on-curves are closer to each other than 2x the offset, need to reverse the direction on the inner handles
        - When the BCPs are in the same direction and are on top of each other, the curve asymmetrically sets the direction (wrong)
        
        """

        # Flatten the original curve
        flatPoints = flattenCurve(originalPoints)

        # Move the BCPs to fit the curve
        innerPoints = self.fitCurve(innerPoints, flatPoints, dir0, dir1)
        outerPoints = self.fitCurve(outerPoints, flatPoints, -dir0, -dir1)

        # Draw the four points
        self.innerPen.curveTo(innerPoints[1], innerPoints[2], innerPoints[3])
        self.innerPrevPoint = self.innerCurrentPoint
        self.outerPen.curveTo(outerPoints[1], outerPoints[2], outerPoints[3])
        self.outerPrevPoint = self.outerCurrentPoint

        self.prevPoint = p3
        self.currentAngle = a2
        self.prevAngle = a2

    def _closePath(self):
        if self.shouldHandleMove:
            return
        if self.offset == 0:
            self.outerPen.closePath()
            self.innerPen.closePath()
            return

        if not self.prevPoint == self.firstPoint:
            self._lineTo(self.firstPoint)

        self.originalPen.closePath()

        self.innerPrevPoint = self.innerCurrentPoint
        self.innerCurrentPoint = self.innerFirstPoint

        self.outerPrevPoint = self.outerCurrentPoint
        self.outerCurrentPoint = self.outerFirstPoint

        self.prevAngle = self.currentAngle
        self.currentAngle = self.firstAngle

        self.buildConnection(close=True)

        self.innerPen.closePath()
        self.outerPen.closePath()

    def _endPath(self):
        if self.shouldHandleMove:
            return

        self.originalPen.endPath()
        self.innerPen.endPath()
        self.outerPen.endPath()

        if self.closeOpenPaths:

            innerContour = self.innerGlyph[-1]
            outerContour = self.outerGlyph[-1]

            innerContour.reverse()

            innerContour[0].segmentType = "line"
            outerContour[0].segmentType = "line"

            self.buildCap(outerContour, innerContour)

            for point in innerContour:
                outerContour.addPoint((point.x, point.y),
                                      segmentType=point.segmentType,
                                      smooth=point.smooth)

            self.innerGlyph.removeContour(innerContour)

    def addComponent(self, glyphName, transform):
        if self.preserveComponents:
            self.components.append((glyphName, transform))
        else:
            BasePen.addComponent(self, glyphName, transform)

    # thickness

    def getThickness(self, angle):
        a2 = angle + pi * .5
        f = abs(sin(a2 + radians(self.contrastAngle)))
        f = f**5
        return self.offset + self.contrast * f

    # fit curve

    def _testCurve(self, pts, flatPts, f0, f1, inRange=False):
        # Normalize and scale the BCPs
        scaled0 = (pts[1] - pts[0]) * f0
        pts[1] = pts[1] + scaled0
        dist1 = abs(self.pointClass(pts[3]).distance(pts[2]))
        scaled1 = (pts[3] - pts[2]) * f1
        pts[2] = pts[2] + scaled1
        # Split at a few locations
        splitFactors = [0.25, 0.5, 0.75]
        newSplit = splitCubicAtT(pts[0], pts[1], pts[2], pts[3], *splitFactors)
        newSplitLocs = [self.pointClass(pts[-1]) for pts in newSplit]
        newSplitLocs = newSplitLocs[:-1]
        # Measure these splits back to the flattened original segment
        # Prepare chunks of the flatPts, if we should be testing inRange
        #   ...a factor of 0.25 should only be compared against 0.2-0.3, 0.5 should be 0.45-0.55, 0.75 should be 0.7-0.8
        totalFlats = len(flatPts)
        flatRanges = [(int(totalFlats * 0.2), int(totalFlats * 0.3)),
                      (int(totalFlats * 0.45), int(totalFlats * 0.55)),
                      (int(totalFlats * 0.7), int(totalFlats * 0.8))
                      ]  # These relate directly to the factors in splitFactors
        totalDiff = 0
        for i, loc in enumerate(newSplitLocs):
            if inRange:
                # If it should only check a smaller range of flat points
                # (experimental)
                flatPtChunk = flatPts[flatRanges[i][0]:flatRanges[i][1]]
            else:
                flatPtChunk = flatPts
            closestLoc, closestDist = self.findClosest(loc, flatPtChunk)
            angle = loc.angle(self.pointClass(closestLoc))
            expectedDistance = self.getThickness(angle)
            diff = expectedDistance - closestDist
            totalDiff += diff
        return pts, totalDiff

    def findClosest(self, loc, locs):
        closest = None
        closestDist = None
        for testLoc in locs:
            d = abs(self.pointClass(loc).distance(self.pointClass(testLoc)))
            if not closestDist:
                closestDist = d
                closest = testLoc
            else:
                if d < closestDist:
                    closestDist = d
                    closest = testLoc
        return closest, closestDist

    def fitCurve(self, pts, flatPts, dir0, dir1):
        # pts is a list of four points defining a curve
        # Use three sample points along the curve to move the BCPs in directions dir0 and dir1
        # until the sampled points average out to the correct distance from flatPoints

        # positive or negative factor for moving the BCPs, depending on the direction the curve is turning
        f = 0.05
        if dir0 > 0:
            f0 = f
        else:
            f0 = -f
        if dir1 > 0:
            f1 = f
        else:
            f1 = -f

        closestPts = pts.copy()
        totalPasses = 0
        # First pass, coarse
        while True:
            totalPasses += 1
            if totalPasses > 50:
                break
            closestPts, totalDiff = self._testCurve(closestPts, flatPts, f0,
                                                    f1)
            if totalDiff < 0:
                # The diff crossed zero
                break

        # Second pass, more fine and in the other direction
        f0 *= -0.1
        f1 *= -0.1
        totalPasses = 0
        while True:
            totalPasses += 1
            if totalPasses > 50:
                break
            closestPts, totalDiff = self._testCurve(closestPts, flatPts, f0,
                                                    f1)
            if totalDiff > 0:
                # The diff crossed zero in the other direction this time
                break

        # Third pass, more fine and in the other direction
        f0 *= -0.1
        f1 *= -0.1
        totalPasses = 0
        while True:
            totalPasses += 1
            if totalPasses > 50:
                break
            closestPts, totalDiff = self._testCurve(closestPts, flatPts, f0,
                                                    f1)
            if totalDiff < 0:
                # The diff crossed zero
                break

        return closestPts

    # connections

    def buildConnection(self, close=False):
        if self.alwaysConnect:
            # Always force a connection for compatibility
            self.connectionCallback(self.outerPrevPoint,
                                    self.outerCurrentPoint, self.outerPen,
                                    close)
            self.connectionCallback(self.innerPrevPoint,
                                    self.innerCurrentPoint, self.innerPen,
                                    close)
        else:
            if not checkSmooth(self.prevAngle, self.currentAngle):
                if checkInnerOuter(self.prevAngle, self.currentAngle):
                    self.connectionCallback(self.outerPrevPoint,
                                            self.outerCurrentPoint,
                                            self.outerPen, close)
                    self.connectionInnerCorner(self.innerPrevPoint,
                                               self.innerCurrentPoint,
                                               self.innerPen, close)
                else:
                    self.connectionCallback(self.innerPrevPoint,
                                            self.innerCurrentPoint,
                                            self.innerPen, close)
                    self.connectionInnerCorner(self.outerPrevPoint,
                                               self.outerCurrentPoint,
                                               self.outerPen, close)
            elif not self.filterDoubles:
                self.innerPen.lineTo(self.innerCurrentPoint)
                self.outerPen.lineTo(self.outerCurrentPoint)

    def connectionSquare(self, first, last, pen, close):
        angle_1 = radians(degrees(self.prevAngle) + 90)
        angle_2 = radians(degrees(self.currentAngle) + 90)

        tempFirst = first - self.pointClass(cos(angle_1),
                                            sin(angle_1)) * self.miterLimit
        tempLast = last + self.pointClass(cos(angle_2),
                                          sin(angle_2)) * self.miterLimit

        newPoint = interSect((first, tempFirst), (last, tempLast))

        if newPoint is not None:

            if self._inputmiterLimit is not None and roundFloat(
                    newPoint.distance(first)) > self._inputmiterLimit:
                pen.lineTo(tempFirst)
                pen.lineTo(tempLast)
            else:
                pen.lineTo(newPoint)

        if not close:
            pen.lineTo(last)

    def connectionRound(self, first, last, pen, close):
        angle_1 = radians(degrees(self.prevAngle) + 90)
        angle_2 = radians(degrees(self.currentAngle) + 90)

        tempFirst = first - self.pointClass(sin(angle_1), -cos(angle_1))
        tempLast = last + self.pointClass(sin(angle_2), -cos(angle_2))

        centerPoint = interSect((first, tempFirst), (last, tempLast))
        if centerPoint is None:
            # the lines are parallel, let's just take the middle
            centerPoint = (first + last) / 2

        angle_diff = (angle_1 - angle_2) % (2 * pi)
        if angle_diff > pi:
            angle_diff = 2 * pi - angle_diff
        angle_half = angle_diff / 2

        radius = centerPoint.distance(first)
        D = radius * (1 - cos(angle_half))
        try:
            handleLength = (4 * D / 3) / sin(
                angle_half)  # length of the bcp line
        except:
            handleLength = 0

        bcp1 = first - self.pointClass(cos(angle_1),
                                       sin(angle_1)) * handleLength
        bcp2 = last + self.pointClass(cos(angle_2),
                                      sin(angle_2)) * handleLength
        pen.curveTo(bcp1, bcp2, last)

    def connectionButt(self, first, last, pen, close):
        if not close:
            pen.lineTo(last)

    def connectionInnerCorner(self, first, last, pen, close):
        if not close:
            pen.lineTo(last)

    # caps

    def buildCap(self, firstContour, lastContour):
        first = firstContour[-1]
        last = lastContour[0]
        first = self.pointClass(first.x, first.y)
        last = self.pointClass(last.x, last.y)

        self.capCallback(firstContour, lastContour, first, last,
                         self.prevAngle)

        first = lastContour[-1]
        last = firstContour[0]
        first = self.pointClass(first.x, first.y)
        last = self.pointClass(last.x, last.y)

        angle = radians(degrees(self.firstAngle) + 180)
        self.capCallback(lastContour, firstContour, first, last, angle)

    def capButt(self, firstContour, lastContour, first, last, angle):
        # not nothing
        pass

    def capRound(self, firstContour, lastContour, first, last, angle):
        hookedAngle = radians(degrees(angle) + 90)

        p1 = first - self.pointClass(cos(hookedAngle),
                                     sin(hookedAngle)) * self.offset

        p2 = last - self.pointClass(cos(hookedAngle),
                                    sin(hookedAngle)) * self.offset

        oncurve = p1 + (p2 - p1) * .5

        roundness = .54  # should be self.magicCurve

        h1 = first - self.pointClass(
            cos(hookedAngle), sin(hookedAngle)) * self.offset * roundness
        h2 = oncurve + self.pointClass(cos(angle),
                                       sin(angle)) * self.offset * roundness

        firstContour[-1].smooth = True

        firstContour.addPoint((h1.x, h1.y))
        firstContour.addPoint((h2.x, h2.y))
        firstContour.addPoint((oncurve.x, oncurve.y),
                              smooth=True,
                              segmentType="curve")

        h1 = oncurve - self.pointClass(cos(angle),
                                       sin(angle)) * self.offset * roundness
        h2 = last - self.pointClass(cos(hookedAngle),
                                    sin(hookedAngle)) * self.offset * roundness

        firstContour.addPoint((h1.x, h1.y))
        firstContour.addPoint((h2.x, h2.y))

        lastContour[0].segmentType = "curve"
        lastContour[0].smooth = True

    def capSquare(self, firstContour, lastContour, first, last, angle):
        angle = radians(degrees(angle) + 90)

        firstContour[-1].smooth = True
        lastContour[0].smooth = True

        p1 = first - self.pointClass(cos(angle), sin(angle)) * self.offset
        firstContour.addPoint((p1.x, p1.y), smooth=False, segmentType="line")

        p2 = last - self.pointClass(cos(angle), sin(angle)) * self.offset
        firstContour.addPoint((p2.x, p2.y), smooth=False, segmentType="line")

    def capRoundsimple(self, firstContour, lastContour, first, last, angle):

        angle = radians(degrees(angle) + 90)

        firstContour[-1].smooth = True
        lastContour[0].smooth = True

        p1 = first - self.pointClass(cos(angle),
                                     sin(angle)) * (self.offset * 1.5)
        firstContour.addPoint((p1.x, p1.y))

        p2 = last - self.pointClass(cos(angle),
                                    sin(angle)) * (self.offset * 1.5)
        firstContour.addPoint((p2.x, p2.y))

        lastContour[0].segmentType = "curve"
        lastContour[0].smooth = True
        #firstContour.addPoint((last.x, last.y), smooth=True, segmentType="curve")

    def drawSettings(self,
                     drawOriginal=False,
                     drawInner=False,
                     drawOuter=True):
        self.drawOriginal = drawOriginal
        self.drawInner = drawInner
        self.drawOuter = drawOuter

    def drawPoints(self, pointPen):
        if self.drawInner:
            reversePen = ReverseContourPointPen(pointPen)
            self.innerGlyph.drawPoints(CleanPointPen(reversePen))
        if self.drawOuter:
            self.outerGlyph.drawPoints(CleanPointPen(pointPen))

        if self.drawOriginal:
            if self.drawOuter:
                pointPen = ReverseContourPointPen(pointPen)
            self.originalGlyph.drawPoints(CleanPointPen(pointPen))

        for glyphName, transform in self.components:
            pointPen.addComponent(glyphName, transform)

    def draw(self, pen):
        pointPen = PointToSegmentPen(pen)
        self.drawPoints(pointPen)

    def getGlyph(self):
        glyph = Glyph()
        pointPen = glyph.getPointPen()
        self.drawPoints(pointPen)
        return glyph
Example #29
0
 def setUp(self):
     self.font = Font()
     self.glyph = Glyph()
     self.component = Component(self.glyph)
Example #30
0
    def createGlyphs(self):

        self.UFO.newGlyph('.notdef')
        missing = ''

        for glf in self.GDB.Master2Search:
            glfUnicode = int(self.GDB.Master2Unicode[glf], 16)

            print('')
            layer = 0
            stopAt = 'arAlef.fina.la'
            glfSrc = self.GDB.Master2Search[glf].split(',')
            if glf == stopAt:
                m = 1
            log = (glf + ' ' * 50)[0:20]
            if glf in self.GDB.MAPPING:
                try:
                    mgName = [self.GDB.MAPPING[glf]]
                    glyph = Glyph()
                    glyph.copyDataFromGlyph(self.srcUFO[mgName[0]])
                    glyph.name = glf
                    if layer == 0:
                        glyph.unicode = glfUnicode
                        glyph.unicodes = [glfUnicode]
                    else:
                        glyph.unicode = None
                    glyph.anchors = []
                    glyph.decomposeAllComponents()
                    if layer > 0:
                        currentLayer = self.getLayer(layer)
                        currentLayer.insertGlyph(glyph)
                    else:
                        self.UFO.insertGlyph(glyph)
                    layer += 1
                    # print(g + ' found :)' + '  L ' + str(layer))

                    gLog = log + (mgName[0] + ' ' * 50)[0:20]
                    print(gLog + '[' + str(layer) + ']  *')
                except:
                    pass
            for g in glfSrc:
                mgName = None
                gCode = None
                gLog = log + (g + ' ' * 50)[0:20]

                try:
                    try:
                        gCode = self.GDB.Prod2Decimal[g]
                        mgName = self.srcUFO.unicodeData[gCode]
                    except:
                        try:
                            mgName = [g]
                        except:
                            pass
                    glyph = Glyph()
                    glyph.copyDataFromGlyph(self.srcUFO[mgName[0]])
                    glyph.name = glf
                    if layer == 0:
                        glyph.unicode = glfUnicode
                        glyph.unicodes = [glfUnicode]
                    else:
                        glyph.unicode = None
                    glyph.anchors = []
                    glyph.decomposeAllComponents()
                    if layer > 0:
                        currentLayer = self.getLayer(layer)
                        currentLayer.insertGlyph(glyph)
                    else:
                        self.UFO.insertGlyph(glyph)
                    layer += 1
                    print(gLog + '[' + str(layer) + ']')
                except:
                    print(gLog + '[ ]')
                glyph = None
            if layer == 0:
                self.UFO.newGlyph(glf)
                missing += log + "\n"
        if len(missing) > 1:
            print('\n')
            print('=' * 60)
            print('Missing glyphes    ' + str(len(missing.splitlines())))
            print('=' * 60)
            print(missing)
            print('=' * 60)
Example #31
0
    def test_identifiers(self):
        glyph = Glyph()
        pointPen = glyph.getPointPen()
        pointPen.beginPath(identifier="contour 1")
        pointPen.addPoint((0, 0), identifier="point 1")
        pointPen.addPoint((0, 0), identifier="point 2")
        pointPen.endPath()
        pointPen.beginPath(identifier="contour 2")
        pointPen.endPath()
        pointPen.addComponent("A", (1, 1, 1, 1, 1, 1),
                              identifier="component 1")
        pointPen.addComponent("A", (1, 1, 1, 1, 1, 1),
                              identifier="component 2")
        guideline = Guideline()
        guideline.identifier = "guideline 1"
        glyph.appendGuideline(guideline)
        guideline = Guideline()
        guideline.identifier = "guideline 2"
        glyph.appendGuideline(guideline)

        self.assertEqual([contour.identifier for contour in glyph],
                         ["contour 1", "contour 2"])
        self.assertEqual([point.identifier for point in glyph[0]],
                         ["point 1", "point 2"])
        self.assertEqual(
            [component.identifier for component in glyph.components],
            ["component 1", "component 2"])
        with self.assertRaises(AssertionError):
            pointPen.beginPath(identifier="contour 1")
        pointPen.endPath()

        pointPen.beginPath()
        pointPen.addPoint((0, 0))
        with self.assertRaises(AssertionError):
            pointPen.addPoint((0, 0), identifier="point 1")
        pointPen.endPath()

        with self.assertRaises(AssertionError):
            pointPen.addComponent("A", (1, 1, 1, 1, 1, 1),
                                  identifier="component 1")

        g = Guideline()
        g.identifier = "guideline 1"
        with self.assertRaises(AssertionError):
            glyph.appendGuideline(g)

        self.assertEqual(sorted(glyph.identifiers), [
            "component 1", "component 2", "contour 1", "contour 2",
            "guideline 1", "guideline 2", "point 1", "point 2"
        ])
        glyph.removeContour(glyph[0])
        self.assertEqual(sorted(glyph.identifiers), [
            "component 1", "component 2", "contour 2", "guideline 1",
            "guideline 2"
        ])
        glyph.removeComponent(glyph.components[0])
        self.assertEqual(
            sorted(glyph.identifiers),
            ["component 2", "contour 2", "guideline 1", "guideline 2"])
        glyph.removeGuideline(glyph.guidelines[0])
        self.assertEqual(sorted(glyph.identifiers),
                         ["component 2", "contour 2", "guideline 2"])
Example #32
0
    def test_copyDataFromGlyph(self):
        source = Glyph()
        source.name = "a"
        source.width = 1
        source.height = 2
        source.unicodes = [3, 4]
        source.note = "test image"
        source.image = dict(fileName="test image",
                            xScale=1,
                            xyScale=1,
                            yxScale=1,
                            yScale=1,
                            xOffset=0,
                            yOffset=0,
                            color=None)
        source.anchors = [dict(x=100, y=200, name="test anchor")]
        source.guidelines = [dict(x=10, y=20, name="test guideline")]
        source.lib = {"foo": "bar"}
        pen = source.getPointPen()
        pen.beginPath()
        pen.addPoint((100, 200), segmentType="line")
        pen.addPoint((300, 400), segmentType="line")
        pen.endPath()
        component = Component()
        component.base = "b"
        source.appendComponent(component)
        dest = Glyph()
        dest.copyDataFromGlyph(source)

        self.assertNotEqual(source.name, dest.name)
        self.assertEqual(source.width, dest.width)
        self.assertEqual(source.height, dest.height)
        self.assertEqual(source.unicodes, dest.unicodes)
        self.assertEqual(source.note, dest.note)
        self.assertEqual(source.image.items(), dest.image.items())
        self.assertEqual([g.items() for g in source.guidelines],
                         [g.items() for g in dest.guidelines])
        self.assertEqual([g.items() for g in source.anchors],
                         [g.items() for g in dest.anchors])
        self.assertEqual(len(source), len(dest))
        self.assertEqual(len(source.components), len(dest.components))
        sourceContours = []
        for contour in source:
            sourceContours.append([])
            for point in contour:
                sourceContours[-1].append(
                    (point.x, point.x, point.segmentType, point.name))
        destContours = []
        for contour in dest:
            destContours.append([])
            for point in contour:
                destContours[-1].append(
                    (point.x, point.x, point.segmentType, point.name))
        self.assertEqual(sourceContours, destContours)
        self.assertEqual(source.components[0].baseGlyph,
                         dest.components[0].baseGlyph)
class OutlinePen(BasePen):

    pointClass = MathPoint
    magicCurve = 0.5522847498

    def __init__(self,
                 glyphSet,
                 offset=10,
                 contrast=0,
                 contrastAngle=0,
                 connection="square",
                 cap="round",
                 miterLimit=None,
                 closeOpenPaths=True,
                 optimizeCurve=False,
                 preserveComponents=False,
                 filterDoubles=True):
        BasePen.__init__(self, glyphSet)

        self.offset = abs(offset)
        self.contrast = abs(contrast)
        self.contrastAngle = contrastAngle
        self._inputmiterLimit = miterLimit
        if miterLimit is None:
            miterLimit = self.offset * 2
        self.miterLimit = abs(miterLimit)

        self.closeOpenPaths = closeOpenPaths
        self.optimizeCurve = optimizeCurve

        self.connectionCallback = getattr(
            self, "connection%s" % (connection.title()))
        self.capCallback = getattr(self, "cap%s" % (cap.title()))

        self.originalGlyph = Glyph()
        self.originalPen = self.originalGlyph.getPen()

        self.outerGlyph = Glyph()
        self.outerPen = self.outerGlyph.getPen()
        self.outerCurrentPoint = None
        self.outerFirstPoint = None
        self.outerPrevPoint = None

        self.innerGlyph = Glyph()
        self.innerPen = self.innerGlyph.getPen()
        self.innerCurrentPoint = None
        self.innerFirstPoint = None
        self.innerPrevPoint = None

        self.prevPoint = None
        self.firstPoint = None
        self.firstAngle = None
        self.prevAngle = None

        self.shouldHandleMove = True

        self.preserveComponents = preserveComponents
        self.components = []

        self.filterDoubles = filterDoubles
        self.drawSettings()

    def _moveTo(self, pt):
        x, y = pt
        if self.offset == 0:
            self.outerPen.moveTo((x, y))
            self.innerPen.moveTo((x, y))
            return
        self.originalPen.moveTo((x, y))

        p = self.pointClass(x, y)
        self.prevPoint = p
        self.firstPoint = p
        self.shouldHandleMove = True

    def _lineTo(self, pt):
        x, y = pt
        if self.offset == 0:
            self.outerPen.lineTo((x, y))
            self.innerPen.lineTo((x, y))
            return
        self.originalPen.lineTo((x, y))

        currentPoint = self.pointClass(x, y)
        if currentPoint == self.prevPoint:
            return

        self.currentAngle = self.prevPoint.angle(currentPoint)
        thickness = self.getThickness(self.currentAngle)
        self.innerCurrentPoint = self.prevPoint - self.pointClass(
            cos(self.currentAngle), sin(self.currentAngle)) * thickness
        self.outerCurrentPoint = self.prevPoint + self.pointClass(
            cos(self.currentAngle), sin(self.currentAngle)) * thickness

        if self.shouldHandleMove:
            self.shouldHandleMove = False

            self.innerPen.moveTo(self.innerCurrentPoint)
            self.innerFirstPoint = self.innerCurrentPoint

            self.outerPen.moveTo(self.outerCurrentPoint)
            self.outerFirstPoint = self.outerCurrentPoint

            self.firstAngle = self.currentAngle
        else:
            self.buildConnection()

        self.innerCurrentPoint = currentPoint - self.pointClass(
            cos(self.currentAngle), sin(self.currentAngle)) * thickness
        self.innerPen.lineTo(self.innerCurrentPoint)
        self.innerPrevPoint = self.innerCurrentPoint

        self.outerCurrentPoint = currentPoint + self.pointClass(
            cos(self.currentAngle), sin(self.currentAngle)) * thickness
        self.outerPen.lineTo(self.outerCurrentPoint)
        self.outerPrevPoint = self.outerCurrentPoint

        self.prevPoint = currentPoint
        self.prevAngle = self.currentAngle

    def _curveToOne(self, pt1, pt2, pt3):
        if self.optimizeCurve:
            curves = splitCubicAtT(self.prevPoint, pt1, pt2, pt3, .5)
        else:
            curves = [(self.prevPoint, pt1, pt2, pt3)]
        for curve in curves:
            p1, h1, h2, p2 = curve
            self._processCurveToOne(h1, h2, p2)

    def _processCurveToOne(self, pt1, pt2, pt3):
        if self.offset == 0:
            self.outerPen.curveTo(pt1, pt2, pt3)
            self.innerPen.curveTo(pt1, pt2, pt3)
            return
        self.originalPen.curveTo(pt1, pt2, pt3)

        p1 = self.pointClass(*pt1)
        p2 = self.pointClass(*pt2)
        p3 = self.pointClass(*pt3)

        if p1 == self.prevPoint:
            p1 = pointOnACurve(self.prevPoint, p1, p2, p3, 0.01)
        if p2 == p3:
            p2 = pointOnACurve(self.prevPoint, p1, p2, p3, 0.99)

        a1 = self.prevPoint.angle(p1)
        a2 = p2.angle(p3)

        self.currentAngle = a1
        tickness1 = self.getThickness(a1)
        tickness2 = self.getThickness(a2)

        a1bis = self.prevPoint.angle(p1, 0)
        a2bis = p3.angle(p2, 0)
        intersectPoint = interSect(
            (self.prevPoint,
             self.prevPoint + self.pointClass(cos(a1), sin(a1)) * 100),
            (p3, p3 + self.pointClass(cos(a2), sin(a2)) * 100))
        self.innerCurrentPoint = self.prevPoint - self.pointClass(
            cos(a1), sin(a1)) * tickness1
        self.outerCurrentPoint = self.prevPoint + self.pointClass(
            cos(a1), sin(a1)) * tickness1

        if self.shouldHandleMove:
            self.shouldHandleMove = False

            self.innerPen.moveTo(self.innerCurrentPoint)
            self.innerFirstPoint = self.innerPrevPoint = self.innerCurrentPoint

            self.outerPen.moveTo(self.outerCurrentPoint)
            self.outerFirstPoint = self.outerPrevPoint = self.outerCurrentPoint

            self.firstAngle = a1
        else:
            self.buildConnection()

        h1 = None
        if intersectPoint is not None:
            h1 = interSect(
                (self.innerCurrentPoint, self.innerCurrentPoint +
                 self.pointClass(cos(a1bis), sin(a1bis)) * tickness1),
                (intersectPoint, p1))
        if h1 is None:
            h1 = p1 - self.pointClass(cos(a1), sin(a1)) * tickness1

        self.innerCurrentPoint = p3 - self.pointClass(cos(a2),
                                                      sin(a2)) * tickness2

        h2 = None
        if intersectPoint is not None:
            h2 = interSect(
                (self.innerCurrentPoint, self.innerCurrentPoint +
                 self.pointClass(cos(a2bis), sin(a2bis)) * tickness2),
                (intersectPoint, p2))
        if h2 is None:
            h2 = p2 - self.pointClass(cos(a1), sin(a1)) * tickness1

        self.innerPen.curveTo(h1, h2, self.innerCurrentPoint)
        self.innerPrevPoint = self.innerCurrentPoint

        ########
        h1 = None
        if intersectPoint is not None:
            h1 = interSect(
                (self.outerCurrentPoint, self.outerCurrentPoint +
                 self.pointClass(cos(a1bis), sin(a1bis)) * tickness1),
                (intersectPoint, p1))
        if h1 is None:
            h1 = p1 + self.pointClass(cos(a1), sin(a1)) * tickness1

        self.outerCurrentPoint = p3 + self.pointClass(cos(a2),
                                                      sin(a2)) * tickness2

        h2 = None
        if intersectPoint is not None:
            h2 = interSect(
                (self.outerCurrentPoint, self.outerCurrentPoint +
                 self.pointClass(cos(a2bis), sin(a2bis)) * tickness2),
                (intersectPoint, p2))
        if h2 is None:
            h2 = p2 + self.pointClass(cos(a1), sin(a1)) * tickness1
        self.outerPen.curveTo(h1, h2, self.outerCurrentPoint)
        self.outerPrevPoint = self.outerCurrentPoint

        self.prevPoint = p3
        self.currentAngle = a2
        self.prevAngle = a2

    def _closePath(self):
        if self.shouldHandleMove:
            return
        if self.offset == 0:
            self.outerPen.closePath()
            self.innerPen.closePath()
            return

        if not self.prevPoint == self.firstPoint:
            self._lineTo(self.firstPoint)

        self.originalPen.closePath()

        self.innerPrevPoint = self.innerCurrentPoint
        self.innerCurrentPoint = self.innerFirstPoint

        self.outerPrevPoint = self.outerCurrentPoint
        self.outerCurrentPoint = self.outerFirstPoint

        self.prevAngle = self.currentAngle
        self.currentAngle = self.firstAngle

        self.buildConnection(close=True)

        self.innerPen.closePath()
        self.outerPen.closePath()

    def _endPath(self):
        if self.shouldHandleMove:
            return

        self.originalPen.endPath()
        self.innerPen.endPath()
        self.outerPen.endPath()

        if self.closeOpenPaths:

            innerContour = self.innerGlyph[-1]
            outerContour = self.outerGlyph[-1]

            innerContour.reverse()

            innerContour[0].segmentType = "line"
            outerContour[0].segmentType = "line"

            self.buildCap(outerContour, innerContour)

            for point in innerContour:
                outerContour.addPoint((point.x, point.y),
                                      segmentType=point.segmentType,
                                      smooth=point.smooth)

            self.innerGlyph.removeContour(innerContour)

    def addComponent(self, glyphName, transform):
        if self.preserveComponents:
            self.components.append((glyphName, transform))
        else:
            BasePen.addComponent(self, glyphName, transform)

    # thickness

    def getThickness(self, angle):
        a2 = angle + pi * .5
        f = abs(sin(a2 + radians(self.contrastAngle)))
        f = f**5
        return self.offset + self.contrast * f

    # connections

    def buildConnection(self, close=False):
        if not checkSmooth(self.prevAngle, self.currentAngle):
            if checkInnerOuter(self.prevAngle, self.currentAngle):
                self.connectionCallback(self.outerPrevPoint,
                                        self.outerCurrentPoint, self.outerPen,
                                        close)
                self.connectionInnerCorner(self.innerPrevPoint,
                                           self.innerCurrentPoint,
                                           self.innerPen, close)
            else:
                self.connectionCallback(self.innerPrevPoint,
                                        self.innerCurrentPoint, self.innerPen,
                                        close)
                self.connectionInnerCorner(self.outerPrevPoint,
                                           self.outerCurrentPoint,
                                           self.outerPen, close)
        elif not self.filterDoubles:
            self.innerPen.lineTo(self.innerCurrentPoint)
            self.outerPen.lineTo(self.outerCurrentPoint)

    def connectionSquare(self, first, last, pen, close):
        angle_1 = radians(degrees(self.prevAngle) + 90)
        angle_2 = radians(degrees(self.currentAngle) + 90)

        tempFirst = first - self.pointClass(cos(angle_1),
                                            sin(angle_1)) * self.miterLimit
        tempLast = last + self.pointClass(cos(angle_2),
                                          sin(angle_2)) * self.miterLimit

        newPoint = interSect((first, tempFirst), (last, tempLast))

        if newPoint is not None:

            if self._inputmiterLimit is not None and roundFloat(
                    newPoint.distance(first)) > self._inputmiterLimit:
                pen.lineTo(tempFirst)
                pen.lineTo(tempLast)
            else:
                pen.lineTo(newPoint)

        if not close:
            pen.lineTo(last)

    def connectionRound(self, first, last, pen, close):
        angle_1 = radians(degrees(self.prevAngle) + 90)
        angle_2 = radians(degrees(self.currentAngle) + 90)

        tempFirst = first - self.pointClass(sin(angle_1), -cos(angle_1))
        tempLast = last + self.pointClass(sin(angle_2), -cos(angle_2))

        centerPoint = interSect((first, tempFirst), (last, tempLast))
        if centerPoint is None:
            # the lines are parallel, let's just take the middle
            centerPoint = (first + last) / 2

        angle_diff = (angle_1 - angle_2) % (2 * pi)
        if angle_diff > pi:
            angle_diff = 2 * pi - angle_diff
        angle_half = angle_diff / 2

        radius = centerPoint.distance(first)
        D = radius * (1 - cos(angle_half))
        handleLength = (4 * D / 3) / sin(angle_half)  # length of the bcp line

        bcp1 = first - self.pointClass(cos(angle_1),
                                       sin(angle_1)) * handleLength
        bcp2 = last + self.pointClass(cos(angle_2),
                                      sin(angle_2)) * handleLength
        pen.curveTo(bcp1, bcp2, last)

    def connectionButt(self, first, last, pen, close):
        if not close:
            pen.lineTo(last)

    def connectionInnerCorner(self, first, last, pen, close):
        if not close:
            pen.lineTo(last)

    # caps

    def buildCap(self, firstContour, lastContour):
        first = firstContour[-1]
        last = lastContour[0]
        first = self.pointClass(first.x, first.y)
        last = self.pointClass(last.x, last.y)

        self.capCallback(firstContour, lastContour, first, last,
                         self.prevAngle)

        first = lastContour[-1]
        last = firstContour[0]
        first = self.pointClass(first.x, first.y)
        last = self.pointClass(last.x, last.y)

        angle = radians(degrees(self.firstAngle) + 180)
        self.capCallback(lastContour, firstContour, first, last, angle)

    def capButt(self, firstContour, lastContour, first, last, angle):
        # not nothing
        pass

    def capRound(self, firstContour, lastContour, first, last, angle):
        hookedAngle = radians(degrees(angle) + 90)

        p1 = first - self.pointClass(cos(hookedAngle),
                                     sin(hookedAngle)) * self.offset

        p2 = last - self.pointClass(cos(hookedAngle),
                                    sin(hookedAngle)) * self.offset

        oncurve = p1 + (p2 - p1) * .5

        roundness = .54  # should be self.magicCurve

        h1 = first - self.pointClass(
            cos(hookedAngle), sin(hookedAngle)) * self.offset * roundness
        h2 = oncurve + self.pointClass(cos(angle),
                                       sin(angle)) * self.offset * roundness

        firstContour[-1].smooth = True

        firstContour.addPoint((h1.x, h1.y))
        firstContour.addPoint((h2.x, h2.y))
        firstContour.addPoint((oncurve.x, oncurve.y),
                              smooth=True,
                              segmentType="curve")

        h1 = oncurve - self.pointClass(cos(angle),
                                       sin(angle)) * self.offset * roundness
        h2 = last - self.pointClass(cos(hookedAngle),
                                    sin(hookedAngle)) * self.offset * roundness

        firstContour.addPoint((h1.x, h1.y))
        firstContour.addPoint((h2.x, h2.y))

        lastContour[0].segmentType = "curve"
        lastContour[0].smooth = True

    def capSquare(self, firstContour, lastContour, first, last, angle):
        angle = radians(degrees(angle) + 90)

        firstContour[-1].smooth = True
        lastContour[0].smooth = True

        p1 = first - self.pointClass(cos(angle), sin(angle)) * self.offset
        firstContour.addPoint((p1.x, p1.y), smooth=False, segmentType="line")

        p2 = last - self.pointClass(cos(angle), sin(angle)) * self.offset
        firstContour.addPoint((p2.x, p2.y), smooth=False, segmentType="line")

    def drawSettings(self,
                     drawOriginal=False,
                     drawInner=False,
                     drawOuter=True):
        self.drawOriginal = drawOriginal
        self.drawInner = drawInner
        self.drawOuter = drawOuter

    def drawPoints(self, pointPen):
        if self.drawInner:
            reversePen = ReverseContourPointPen(pointPen)
            self.innerGlyph.drawPoints(CleanPointPen(reversePen))
        if self.drawOuter:
            self.outerGlyph.drawPoints(CleanPointPen(pointPen))

        if self.drawOriginal:
            if self.drawOuter:
                pointPen = ReverseContourPointPen(pointPen)
            self.originalGlyph.drawPoints(CleanPointPen(pointPen))

        for glyphName, transform in self.components:
            pointPen.addComponent(glyphName, transform)

    def draw(self, pen):
        pointPen = PointToSegmentPen(pen)
        self.drawPoints(pointPen)

    def getGlyph(self):
        glyph = Glyph()
        pointPen = glyph.getPointPen()
        self.drawPoints(pointPen)
        return glyph
Example #34
0
 def test_CallingPenBallFilter(self):
     testGlyph = Glyph()
     pen = testGlyph.getPen()
     self.drawTestGlyph(pen)
     filteredGlyph = self.filter(testGlyph)
Example #35
0
    subjectPaths = contoursToZs(subjectContours)
    clippingPaths = contoursToZs(clippingContours)
    result = shapeops.intersection(subjectPaths, clippingPaths, **kwargs)
    drawZsWithPointPen(result, outPen, guessSmooth=guessSmooth)


def xor(subjectContours, clippingContours, outPen, guessSmooth=True,
        **kwargs):
    subjectPaths = contoursToZs(subjectContours)
    clippingPaths = contoursToZs(clippingContours)
    result = shapeops.xor(subjectPaths, clippingPaths, **kwargs)
    drawZsWithPointPen(result, outPen, guessSmooth=guessSmooth)


if __name__ == "__main__":
    import sys
    from defcon import Glyph
    from ufoLib.glifLib import readGlyphFromString, writeGlyphToString

    data = sys.stdin.read()

    glyph = Glyph()
    readGlyphFromString(data, glyph, glyph.getPointPen())

    contours = list(glyph)
    glyph.clearContours()
    union(contours, glyph.getPointPen())

    output = writeGlyphToString(glyph.name, glyph, glyph.drawPoints)
    sys.stdout.write(output)
Example #36
0
def getGlyphFromDict(glyph_dict):
    g = Glyph()
    
    # Set attributes
    
    g.height = glyph_dict.get('height', 0)
    g.lib = glyph_dict.get('lib', {})
    g.name = glyph_dict.get('name', '')
    g.note = glyph_dict.get('note', None)
    g.unicode = glyph_dict.get('unicode', None)
    g.unicodes = glyph_dict.get('unicodes', [])
    g.width = glyph_dict.get('width', 0)
    
    # Draw the outlines with a pen
    pen = g.getPointPen()
    
    for contour in glyph_dict.get('contours', []):
        pen.beginPath()
        for point in contour:
            pen.addPoint(
                (
                    point.get('x'),
                    point.get('y')
                ),
                segmentType = point.get('type', None),
                name = point.get('name', None),
                smooth = point.get('smooth', None),
            )
        pen.endPath()
    
    # Add components
    
    for component in glyph_dict.get('components', []):
        c = Component()
        c.baseGlyph = component.get('ref', '')
        c.transformation = component.get('transformation', (1, 0, 0, 1, 0, 0))
        g.appendComponent(c)
    
    # Add anchors
    
    for anchor in glyph_dict.get('anchors', []):
        a = Anchor(anchorDict = anchor)
        g.appendAnchor(a)
    
    # Return the completed glyph object
    
    return g
Example #37
0
 def setUp(self):
     testGlyph = Glyph()
     pen = testGlyph.getPen()
     self.drawTestGlyph(pen)
     self.testGlyph = testGlyph
Example #38
0
    def test_copyDataFromGlyph(self):
        source = Glyph()
        source.name = "a"
        source.width = 1
        source.height = 2
        source.unicodes = [3, 4]
        source.note = "test image"
        source.image = dict(fileName="test image", xScale=1, xyScale=1,
                            yxScale=1, yScale=1, xOffset=0, yOffset=0,
                            color=None)
        source.anchors = [dict(x=100, y=200, name="test anchor")]
        source.guidelines = [dict(x=10, y=20, name="test guideline")]
        source.lib = {"foo": "bar"}
        pen = source.getPointPen()
        pen.beginPath()
        pen.addPoint((100, 200), segmentType="line")
        pen.addPoint((300, 400), segmentType="line")
        pen.endPath()
        component = Component()
        component.base = "b"
        source.appendComponent(component)
        dest = Glyph()
        dest.copyDataFromGlyph(source)

        self.assertNotEqual(source.name, dest.name)
        self.assertEqual(source.width, dest.width)
        self.assertEqual(source.height, dest.height)
        self.assertEqual(source.unicodes, dest.unicodes)
        self.assertEqual(source.note, dest.note)
        self.assertEqual(source.image.items(), dest.image.items())
        self.assertEqual([g.items() for g in source.guidelines],
                         [g.items() for g in dest.guidelines])
        self.assertEqual([g.items() for g in source.anchors],
                         [g.items() for g in dest.anchors])
        self.assertEqual(len(source), len(dest))
        self.assertEqual(len(source.components), len(dest.components))
        sourceContours = []
        for contour in source:
            sourceContours.append([])
            for point in contour:
                sourceContours[-1].append((point.x, point.x,
                                           point.segmentType, point.name))
        destContours = []
        for contour in dest:
            destContours.append([])
            for point in contour:
                destContours[-1].append((point.x, point.x,
                                         point.segmentType, point.name))
        self.assertEqual(sourceContours, destContours)
        self.assertEqual(source.components[0].baseGlyph,
                         dest.components[0].baseGlyph)
 def test_CallingPenBallFilter(self):
     testGlyph = Glyph()
     pen = testGlyph.getPen()
     self.drawTestGlyph(pen)
     filteredGlyph = self.filter(testGlyph)
 def getGlyph(self):
     glyph = Glyph()
     pointPen = glyph.getPointPen()
     self.drawPoints(pointPen)
     return glyph
Example #41
0
class OutlinePen(BasePen):

    pointClass = MathPoint
    magicCurve = 0.5522847498

    def __init__(self,
                 glyphSet,
                 offset=10,
                 contrast=0,
                 contrastAngle=0,
                 connection="square",
                 cap="round",
                 miterLimit=None,
                 closeOpenPaths=True,
                 preserveComponents=False):
        BasePen.__init__(self, glyphSet)

        self.offset = abs(offset)
        self.contrast = abs(contrast)
        self.contrastAngle = contrastAngle
        self._inputmiterLimit = miterLimit
        if miterLimit is None:
            miterLimit = self.offset * 2
        self.miterLimit = abs(miterLimit)

        self.closeOpenPaths = closeOpenPaths

        self.connectionCallback = getattr(
            self, "connection%s" % (connection.title()))
        self.capCallback = getattr(self, "cap%s" % (cap.title()))

        self.originalGlyph = Glyph()
        self.originalPen = self.originalGlyph.getPen()

        self.outerGlyph = Glyph()
        self.outerPen = self.outerGlyph.getPen()
        self.outerCurrentPoint = None
        self.outerFirstPoint = None
        self.outerPrevPoint = None

        self.innerGlyph = Glyph()
        self.innerPen = self.innerGlyph.getPen()
        self.innerCurrentPoint = None
        self.innerFirstPoint = None
        self.innerPrevPoint = None

        self.prevPoint = None
        self.firstPoint = None
        self.firstAngle = None
        self.prevAngle = None

        self.shouldHandleMove = True

        self.preserveComponents = preserveComponents
        self.components = []

        self.drawSettings()

    def _moveTo(self, xyFix):
        (x, y) = xyFix
        if self.offset == 0:
            self.outerPen.moveTo((x, y))
            self.innerPen.moveTo((x, y))
            return
        self.originalPen.moveTo((x, y))

        p = self.pointClass(x, y)
        self.prevPoint = p
        self.firstPoint = p
        self.shouldHandleMove = True

    def _lineTo(self, xyFix):
        (x, y) = xyFix
        if self.offset == 0:
            self.outerPen.lineTo((x, y))
            self.innerPen.lineTo((x, y))
            return
        self.originalPen.lineTo((x, y))

        currentPoint = self.pointClass(x, y)
        if currentPoint == self.prevPoint:
            return

        self.currentAngle = self.prevPoint.angle(currentPoint)
        thickness = self.getThickness(self.currentAngle)
        self.innerCurrentPoint = self.prevPoint - self.pointClass(
            cos(self.currentAngle), sin(self.currentAngle)) * thickness
        self.outerCurrentPoint = self.prevPoint + self.pointClass(
            cos(self.currentAngle), sin(self.currentAngle)) * thickness

        if self.shouldHandleMove:
            self.shouldHandleMove = False

            self.innerPen.moveTo(self.innerCurrentPoint)
            self.innerFirstPoint = self.innerCurrentPoint

            self.outerPen.moveTo(self.outerCurrentPoint)
            self.outerFirstPoint = self.outerCurrentPoint

            self.firstAngle = self.currentAngle
        else:
            self.buildConnection()

        self.innerCurrentPoint = currentPoint - self.pointClass(
            cos(self.currentAngle), sin(self.currentAngle)) * thickness
        self.innerPen.lineTo(self.innerCurrentPoint)
        self.innerPrevPoint = self.innerCurrentPoint

        self.outerCurrentPoint = currentPoint + self.pointClass(
            cos(self.currentAngle), sin(self.currentAngle)) * thickness
        self.outerPen.lineTo(self.outerCurrentPoint)
        self.outerPrevPoint = self.outerCurrentPoint

        self.prevPoint = currentPoint
        self.prevAngle = self.currentAngle

    def _curveToOne(self, xyFix1, xyFix2, xyFix3):
        (x1, y1) = xyFix1
        (x2, y2) = xyFix2
        (x3, y3) = xyFix3
        if self.offset == 0:
            self.outerPen.curveTo((x1, y1), (x2, y2), (x3, y3))
            self.innerPen.curveTo((x1, y1), (x2, y2), (x3, y3))
            return
        self.originalPen.curveTo((x1, y1), (x2, y2), (x3, y3))

        p1 = self.pointClass(x1, y1)
        p2 = self.pointClass(x2, y2)
        p3 = self.pointClass(x3, y3)

        if p1 == self.prevPoint:
            p1 = pointOnACurve(self.prevPoint, p1, p2, p3, 0.01)
        if p2 == p3:
            p2 = pointOnACurve(self.prevPoint, p1, p2, p3, 0.99)

        a1 = self.prevPoint.angle(p1)
        a2 = p2.angle(p3)

        self.currentAngle = a1
        tickness1 = self.getThickness(a1)
        tickness2 = self.getThickness(a2)

        a1bis = self.prevPoint.angle(p1, 0)
        a2bis = p3.angle(p2, 0)
        intersectPoint = interSect(
            (self.prevPoint,
             self.prevPoint + self.pointClass(cos(a1), sin(a1)) * 100),
            (p3, p3 + self.pointClass(cos(a2), sin(a2)) * 100))
        self.innerCurrentPoint = self.prevPoint - self.pointClass(
            cos(a1), sin(a1)) * tickness1
        self.outerCurrentPoint = self.prevPoint + self.pointClass(
            cos(a1), sin(a1)) * tickness1

        if self.shouldHandleMove:
            self.shouldHandleMove = False

            self.innerPen.moveTo(self.innerCurrentPoint)
            self.innerFirstPoint = self.innerPrevPoint = self.innerCurrentPoint

            self.outerPen.moveTo(self.outerCurrentPoint)
            self.outerFirstPoint = self.outerPrevPoint = self.outerCurrentPoint

            self.firstAngle = a1
        else:
            self.buildConnection()
        h1 = None
        if intersectPoint is not None:
            h1 = interSect(
                (self.innerCurrentPoint, self.innerCurrentPoint +
                 self.pointClass(cos(a1bis), sin(a1bis)) * tickness1),
                (intersectPoint, p1))
        if h1 is None:
            h1 = p1 - self.pointClass(cos(a1), sin(a1)) * tickness1

        self.innerCurrentPoint = p3 - self.pointClass(cos(a2),
                                                      sin(a2)) * tickness2

        h2 = None
        if intersectPoint is not None:
            h2 = interSect(
                (self.innerCurrentPoint, self.innerCurrentPoint +
                 self.pointClass(cos(a2bis), sin(a2bis)) * tickness2),
                (intersectPoint, p2))
        if h2 is None:
            h2 = p2 - self.pointClass(cos(a1), sin(a1)) * tickness1

        self.innerPen.curveTo(h1, h2, self.innerCurrentPoint)
        self.innerPrevPoint = self.innerCurrentPoint

        ########
        h1 = None
        if intersectPoint is not None:
            h1 = interSect(
                (self.outerCurrentPoint, self.outerCurrentPoint +
                 self.pointClass(cos(a1bis), sin(a1bis)) * tickness1),
                (intersectPoint, p1))
        if h1 is None:
            h1 = p1 + self.pointClass(cos(a1), sin(a1)) * tickness1

        self.outerCurrentPoint = p3 + self.pointClass(cos(a2),
                                                      sin(a2)) * tickness2

        h2 = None
        if intersectPoint is not None:
            h2 = interSect(
                (self.outerCurrentPoint, self.outerCurrentPoint +
                 self.pointClass(cos(a2bis), sin(a2bis)) * tickness2),
                (intersectPoint, p2))
        if h2 is None:
            h2 = p2 + self.pointClass(cos(a1), sin(a1)) * tickness1
        self.outerPen.curveTo(h1, h2, self.outerCurrentPoint)
        self.outerPrevPoint = self.outerCurrentPoint

        self.prevPoint = p3
        self.currentAngle = a2
        self.prevAngle = a2

    def _closePath(self):
        if self.shouldHandleMove:
            return
        if self.offset == 0:
            self.outerPen.closePath()
            self.innerPen.closePath()
            return

        if not self.prevPoint == self.firstPoint:
            self._lineTo(self.firstPoint)

        self.originalPen.closePath()

        self.innerPrevPoint = self.innerCurrentPoint
        self.innerCurrentPoint = self.innerFirstPoint

        self.outerPrevPoint = self.outerCurrentPoint
        self.outerCurrentPoint = self.outerFirstPoint

        self.prevAngle = self.currentAngle
        self.currentAngle = self.firstAngle

        self.buildConnection(close=True)

        self.innerPen.closePath()
        self.outerPen.closePath()

    def _endPath(self):
        if self.shouldHandleMove:
            return

        self.originalPen.endPath()
        self.innerPen.endPath()
        self.outerPen.endPath()

        if self.closeOpenPaths:

            innerContour = self.innerGlyph[-1]
            outerContour = self.outerGlyph[-1]

            innerContour.reverse()

            innerContour[0].segmentType = "line"
            outerContour[0].segmentType = "line"

            self.buildCap(outerContour, innerContour)

            for point in innerContour:
                outerContour.addPoint((point.x, point.y),
                                      segmentType=point.segmentType,
                                      smooth=point.smooth)

            self.innerGlyph.removeContour(innerContour)

    def addComponent(self, glyphName, transform):
        if self.preserveComponents:
            self.components.append((glyphName, transform))
        else:
            BasePen.addComponent(self, glyphName, transform)

    ## thickness

    def getThickness(self, angle):
        a2 = angle + pi * .5
        f = abs(sin(a2 + radians(self.contrastAngle)))
        f = f**5
        return self.offset + self.contrast * f

    ## connections

    def buildConnection(self, close=False):
        if not checkSmooth(self.prevAngle, self.currentAngle):
            if checkInnerOuter(self.prevAngle, self.currentAngle):
                self.connectionCallback(self.outerPrevPoint,
                                        self.outerCurrentPoint, self.outerPen,
                                        close)
                self.connectionInnerCorner(self.innerPrevPoint,
                                           self.innerCurrentPoint,
                                           self.innerPen, close)
            else:
                self.connectionCallback(self.innerPrevPoint,
                                        self.innerCurrentPoint, self.innerPen,
                                        close)
                self.connectionInnerCorner(self.outerPrevPoint,
                                           self.outerCurrentPoint,
                                           self.outerPen, close)

    def connectionSquare(self, first, last, pen, close):
        angle_1 = radians(degrees(self.prevAngle) + 90)
        angle_2 = radians(degrees(self.currentAngle) + 90)

        tempFirst = first - self.pointClass(cos(angle_1),
                                            sin(angle_1)) * self.miterLimit
        tempLast = last + self.pointClass(cos(angle_2),
                                          sin(angle_2)) * self.miterLimit

        newPoint = interSect((first, tempFirst), (last, tempLast))

        if newPoint is not None:

            if self._inputmiterLimit is not None and roundFloat(
                    newPoint.distance(first)) > self._inputmiterLimit:
                pen.lineTo(tempFirst)
                pen.lineTo(tempLast)
            else:
                pen.lineTo(newPoint)

        if not close:
            pen.lineTo(last)

    def connectionRound(self, first, last, pen, close):
        angle_1 = radians(degrees(self.prevAngle) + 90)
        angle_2 = radians(degrees(self.currentAngle) + 90)

        tempFirst = first - self.pointClass(cos(angle_1),
                                            sin(angle_1)) * self.miterLimit
        tempLast = last + self.pointClass(cos(angle_2),
                                          sin(angle_2)) * self.miterLimit

        newPoint = interSect((first, tempFirst), (last, tempLast))
        if newPoint is None:
            pen.lineTo(last)
            return
        #print "(%s, %s)," % (newPoint.x, newPoint.y)
        distance1 = newPoint.distance(first)
        distance2 = newPoint.distance(last)
        #print distance1, distance2
        if roundFloat(distance1) > self.miterLimit + self.contrast:
            distance1 = self.miterLimit + tempFirst.distance(tempLast) * .7
        if roundFloat(distance2) > self.miterLimit + self.contrast:
            distance2 = self.miterLimit + tempFirst.distance(tempLast) * .7

        distance1 *= self.magicCurve
        distance2 *= self.magicCurve

        bcp1 = first - self.pointClass(cos(angle_1), sin(angle_1)) * distance1
        bcp2 = last + self.pointClass(cos(angle_2), sin(angle_2)) * distance2
        pen.curveTo(bcp1, bcp2, last)

    def connectionButt(self, first, last, pen, close):
        if not close:
            pen.lineTo(last)

    def connectionInnerCorner(self, first, last, pen, close):
        if not close:
            pen.lineTo(last)

    ## caps

    def buildCap(self, firstContour, lastContour):
        first = firstContour[-1]
        last = lastContour[0]
        first = self.pointClass(first.x, first.y)
        last = self.pointClass(last.x, last.y)

        self.capCallback(firstContour, lastContour, first, last,
                         self.prevAngle)

        first = lastContour[-1]
        last = firstContour[0]
        first = self.pointClass(first.x, first.y)
        last = self.pointClass(last.x, last.y)

        angle = radians(degrees(self.firstAngle) + 180)
        self.capCallback(lastContour, firstContour, first, last, angle)

    def capButt(self, firstContour, lastContour, first, last, angle):
        ## not nothing
        pass

    def capRound(self, firstContour, lastContour, first, last, angle):
        hookedAngle = radians(degrees(angle) + 90)

        p1 = first - self.pointClass(cos(hookedAngle),
                                     sin(hookedAngle)) * self.offset

        p2 = last - self.pointClass(cos(hookedAngle),
                                    sin(hookedAngle)) * self.offset

        oncurve = p1 + (p2 - p1) * .5

        roundness = .54

        h1 = first - self.pointClass(
            cos(hookedAngle), sin(hookedAngle)) * self.offset * roundness
        h2 = oncurve + self.pointClass(cos(angle),
                                       sin(angle)) * self.offset * roundness

        firstContour[-1].smooth = True

        firstContour.addPoint((h1.x, h1.y))
        firstContour.addPoint((h2.x, h2.y))
        firstContour.addPoint((oncurve.x, oncurve.y),
                              smooth=True,
                              segmentType="curve")

        h1 = oncurve - self.pointClass(cos(angle),
                                       sin(angle)) * self.offset * roundness
        h2 = last - self.pointClass(cos(hookedAngle),
                                    sin(hookedAngle)) * self.offset * roundness

        firstContour.addPoint((h1.x, h1.y))
        firstContour.addPoint((h2.x, h2.y))

        lastContour[0].segmentType = "curve"
        lastContour[0].smooth = True

    def capSquare(self, firstContour, lastContour, first, last, angle):
        angle = radians(degrees(angle) + 90)

        firstContour[-1].smooth = True
        lastContour[0].smooth = True

        p1 = first - self.pointClass(cos(angle), sin(angle)) * self.offset
        firstContour.addPoint((p1.x, p1.y), smooth=False, segmentType="line")

        p2 = last - self.pointClass(cos(angle), sin(angle)) * self.offset
        firstContour.addPoint((p2.x, p2.y), smooth=False, segmentType="line")

    def drawSettings(self,
                     drawOriginal=False,
                     drawInner=False,
                     drawOuter=True):
        self.drawOriginal = drawOriginal
        self.drawInner = drawInner
        self.drawOuter = drawOuter

    def drawPoints(self, pointPen):
        if self.drawInner:
            reversePen = ReverseContourPointPen(pointPen)
            self.innerGlyph.drawPoints(CleanPointPen(reversePen))
        if self.drawOuter:
            self.outerGlyph.drawPoints(CleanPointPen(pointPen))

        if self.drawOriginal:
            if self.drawOuter:
                pointPen = ReverseContourPointPen(pointPen)
            self.originalGlyph.drawPoints(CleanPointPen(pointPen))

        for glyphName, transform in self.components:
            pointPen.addComponent(glyphName, transform)

    def draw(self, pen):
        pointPen = PointToSegmentPen(pen)
        self.drawPoints(pointPen)

    def getGlyph(self):
        glyph = Glyph()
        pointPen = glyph.getPointPen()
        self.drawPoints(pointPen)
        return glyph
Example #42
0
 def setUp(self):
     testGlyph = Glyph()
     pen = testGlyph.getPen()
     self.drawTestGlyph(pen)
     self.testGlyph = testGlyph
Example #43
0
class OutlinePen(BasePen):

    pointClass = MathPoint
    magicCurve = 0.5522847498

    def __init__(self,
                 glyphSet,
                 offset=10,
                 connection="square",
                 cap="round",
                 miterLimit=None,
                 closeOpenPaths=True):
        BasePen.__init__(self, glyphSet)

        self.offset = abs(offset)
        self._inputmiterLimit = miterLimit
        if miterLimit is None:
            miterLimit = self.offset
        self.miterLimit = abs(miterLimit)

        self.closeOpenPaths = closeOpenPaths

        self.connectionCallback = getattr(
            self,
            "connection%s" % (connection[0].capitalize() + connection[1:]))
        self.capCallback = getattr(self,
                                   "cap%s" % (cap[0].capitalize() + cap[1:]))

        self.originalGlyph = Glyph()
        self.originalPen = self.originalGlyph.getPen()

        self.outerGlyph = Glyph()
        self.outerPen = self.outerGlyph.getPen()
        self.outerCurrentPoint = None
        self.outerFirstPoint = None
        self.outerPrevPoint = None

        self.innerGlyph = Glyph()
        self.innerPen = self.innerGlyph.getPen()
        self.innerCurrentPoint = None
        self.innerFirstPoint = None
        self.innerPrevPoint = None

        self.prevPoint = None
        self.firstPoint = None
        self.firstAngle = None
        self.prevAngle = None

        self.shouldHandleMove = True

        self.drawSettings()

    def _moveTo(self, (x, y)):
        if self.offset == 0:
            self.outerPen.moveTo((x, y))
            self.innerPen.moveTo((x, y))
            return
        self.originalPen.moveTo((x, y))

        p = self.pointClass(x, y)
        self.prevPoint = p
        self.firstPoint = p
        self.shouldHandleMove = True
Example #44
0
class OutlinePen(BasePen):

    pointClass = MathPoint
    magicCurve = 0.5522847498

    def __init__(self,
                 glyphSet,
                 offset=10,
                 contrast=0,
                 contrastAngle=0,
                 connection="round",
                 cap="round",
                 miterLimit=None,
                 optimizeCurve=True):
        BasePen.__init__(self, glyphSet)

        self.offset = abs(offset)
        self.contrast = abs(contrast)
        self.contrastAngle = contrastAngle
        self._inputmiterLimit = miterLimit
        if miterLimit is None:
            miterLimit = self.offset * 2
        self.miterLimit = abs(miterLimit)

        self.optimizeCurve = optimizeCurve

        self.connectionCallback = getattr(
            self, "connection%s" % (connection.title()))
        self.capCallback = getattr(self, "cap%s" % (cap.title()))

        self.originalGlyph = Glyph()
        self.originalPen = self.originalGlyph.getPen()

        self.outerGlyph = Glyph()
        self.outerPen = self.outerGlyph.getPen()
        self.outerCurrentPoint = None
        self.outerFirstPoint = None
        self.outerPrevPoint = None

        self.innerGlyph = Glyph()
        self.innerPen = self.innerGlyph.getPen()
        self.innerCurrentPoint = None
        self.innerFirstPoint = None
        self.innerPrevPoint = None

        self.prevPoint = None
        self.firstPoint = None
        self.firstAngle = None
        self.prevAngle = None

        self.shouldHandleMove = True

        self.components = []

        self.drawSettings()

    def _moveTo(self, (x, y)):
        if self.offset == 0:
            self.outerPen.moveTo((x, y))
            self.innerPen.moveTo((x, y))
            return
        self.originalPen.moveTo((x, y))

        p = self.pointClass(x, y)
        self.prevPoint = p
        self.firstPoint = p
        self.shouldHandleMove = True
Example #45
0
    def test_identifiers(self):
        glyph = Glyph()
        pointPen = glyph.getPointPen()
        pointPen.beginPath(identifier="contour 1")
        pointPen.addPoint((0, 0), identifier="point 1")
        pointPen.addPoint((0, 0), identifier="point 2")
        pointPen.endPath()
        pointPen.beginPath(identifier="contour 2")
        pointPen.endPath()
        pointPen.addComponent("A", (1, 1, 1, 1, 1, 1),
                              identifier="component 1")
        pointPen.addComponent("A", (1, 1, 1, 1, 1, 1),
                              identifier="component 2")
        guideline = Guideline()
        guideline.identifier = "guideline 1"
        glyph.appendGuideline(guideline)
        guideline = Guideline()
        guideline.identifier = "guideline 2"
        glyph.appendGuideline(guideline)

        self.assertEqual([contour.identifier for contour in glyph],
                         ["contour 1", "contour 2"])
        self.assertEqual([point.identifier for point in glyph[0]],
                         ["point 1", "point 2"])
        self.assertEqual(
            [component.identifier for component in glyph.components],
            ["component 1", "component 2"])
        with self.assertRaises(AssertionError):
            pointPen.beginPath(identifier="contour 1")
        pointPen.endPath()

        pointPen.beginPath()
        pointPen.addPoint((0, 0))
        with self.assertRaises(AssertionError):
            pointPen.addPoint((0, 0), identifier="point 1")
        pointPen.endPath()

        with self.assertRaises(AssertionError):
            pointPen.addComponent("A", (1, 1, 1, 1, 1, 1),
                                  identifier="component 1")

        g = Guideline()
        g.identifier = "guideline 1"
        with self.assertRaises(AssertionError):
            glyph.appendGuideline(g)

        self.assertEqual(
            sorted(glyph.identifiers),
            ["component 1", "component 2", "contour 1", "contour 2",
             "guideline 1", "guideline 2", "point 1", "point 2"])
        glyph.removeContour(glyph[0])
        self.assertEqual(
            sorted(glyph.identifiers),
            ["component 1", "component 2", "contour 2",
             "guideline 1", "guideline 2"])
        glyph.removeComponent(glyph.components[0])
        self.assertEqual(
            sorted(glyph.identifiers),
            ["component 2", "contour 2", "guideline 1", "guideline 2"])
        glyph.removeGuideline(glyph.guidelines[0])
        self.assertEqual(
            sorted(glyph.identifiers),
            ["component 2", "contour 2", "guideline 2"])
 def getGlyph(self):
     glyph = Glyph()
     pointPen = glyph.getPointPen()
     self.drawPoints(pointPen)
     return glyph
class OutlinePen(BasePen):

    pointClass = MathPoint
    magicCurve = 0.5522847498

    def __init__(self, glyphSet, offset=10, contrast=0, contrastAngle=0, connection="square", cap="round", miterLimit=None, closeOpenPaths=True, preserveComponents=False):
        BasePen.__init__(self, glyphSet)

        self.offset = abs(offset)
        self.contrast = abs(contrast)
        self.contrastAngle = contrastAngle
        self._inputmiterLimit = miterLimit
        if miterLimit is None:
            miterLimit = self.offset * 2
        self.miterLimit = abs(miterLimit)

        self.closeOpenPaths = closeOpenPaths

        self.connectionCallback = getattr(self, "connection%s" % (connection.title()))
        self.capCallback = getattr(self, "cap%s" % (cap.title()))

        self.originalGlyph = Glyph()
        self.originalPen = self.originalGlyph.getPen()

        self.outerGlyph = Glyph()
        self.outerPen = self.outerGlyph.getPen()
        self.outerCurrentPoint = None
        self.outerFirstPoint = None
        self.outerPrevPoint = None

        self.innerGlyph = Glyph()
        self.innerPen = self.innerGlyph.getPen()
        self.innerCurrentPoint = None
        self.innerFirstPoint = None
        self.innerPrevPoint = None

        self.prevPoint = None
        self.firstPoint = None
        self.firstAngle = None
        self.prevAngle = None

        self.shouldHandleMove = True

        self.preserveComponents = preserveComponents
        self.components = []

        self.drawSettings()

    def _moveTo(self, xy):
        (x, y) = xy
        if self.offset == 0:
            self.outerPen.moveTo((x, y))
            self.innerPen.moveTo((x, y))
            return
        self.originalPen.moveTo((x, y))

        p = self.pointClass(x, y)
        self.prevPoint = p
        self.firstPoint = p
        self.shouldHandleMove = True

    def _lineTo(self, xy):
        (x, y) = xy
        if self.offset == 0:
            self.outerPen.lineTo((x, y))
            self.innerPen.lineTo((x, y))
            return
        self.originalPen.lineTo((x, y))

        currentPoint = self.pointClass(x, y)
        if currentPoint == self.prevPoint:
            return

        self.currentAngle = self.prevPoint.angle(currentPoint)
        thickness = self.getThickness(self.currentAngle)
        self.innerCurrentPoint = self.prevPoint - self.pointClass(cos(self.currentAngle), sin(self.currentAngle)) * thickness
        self.outerCurrentPoint = self.prevPoint + self.pointClass(cos(self.currentAngle), sin(self.currentAngle)) * thickness

        if self.shouldHandleMove:
            self.shouldHandleMove = False

            self.innerPen.moveTo(self.innerCurrentPoint)
            self.innerFirstPoint = self.innerCurrentPoint

            self.outerPen.moveTo(self.outerCurrentPoint)
            self.outerFirstPoint = self.outerCurrentPoint

            self.firstAngle = self.currentAngle
        else:
            self.buildConnection()

        self.innerCurrentPoint = currentPoint - self.pointClass(cos(self.currentAngle), sin(self.currentAngle)) * thickness
        self.innerPen.lineTo(self.innerCurrentPoint)
        self.innerPrevPoint = self.innerCurrentPoint

        self.outerCurrentPoint = currentPoint + self.pointClass(cos(self.currentAngle), sin(self.currentAngle)) * thickness
        self.outerPen.lineTo(self.outerCurrentPoint)
        self.outerPrevPoint = self.outerCurrentPoint

        self.prevPoint = currentPoint
        self.prevAngle = self.currentAngle

    def _curveToOne(self, xy1, xy2, xy3):
        (x1, y1), (x2, y2), (x3, y3) = xy1, xy2, xy3
        if self.offset == 0:
            self.outerPen.curveTo((x1, y1), (x2, y2), (x3, y3))
            self.innerPen.curveTo((x1, y1), (x2, y2), (x3, y3))
            return
        self.originalPen.curveTo((x1, y1), (x2, y2), (x3, y3))

        p1 = self.pointClass(x1, y1)
        p2 = self.pointClass(x2, y2)
        p3 = self.pointClass(x3, y3)

        if p1 == self.prevPoint:
            p1 = pointOnACurve(self.prevPoint, p1, p2, p3, 0.01)
        if p2 == p3:
            p2 = pointOnACurve(self.prevPoint, p1, p2, p3, 0.99)

        a1 = self.prevPoint.angle(p1)
        a2 = p2.angle(p3)

        self.currentAngle = a1
        tickness1 = self.getThickness(a1)
        tickness2 = self.getThickness(a2)

        a1bis = self.prevPoint.angle(p1, 0)
        a2bis = p3.angle(p2, 0)
        intersectPoint = interSect((self. prevPoint, self.prevPoint + self.pointClass(cos(a1), sin(a1)) * 100),
                                   (p3, p3 + self.pointClass(cos(a2), sin(a2)) * 100))
        self.innerCurrentPoint = self.prevPoint - self.pointClass(cos(a1), sin(a1)) * tickness1
        self.outerCurrentPoint = self.prevPoint + self.pointClass(cos(a1), sin(a1)) * tickness1

        if self.shouldHandleMove:
            self.shouldHandleMove = False

            self.innerPen.moveTo(self.innerCurrentPoint)
            self.innerFirstPoint = self.innerPrevPoint = self.innerCurrentPoint

            self.outerPen.moveTo(self.outerCurrentPoint)
            self.outerFirstPoint = self.outerPrevPoint = self.outerCurrentPoint

            self.firstAngle = a1
        else:
            self.buildConnection()
        h1 = None
        if intersectPoint is not None:
            h1 = interSect((self.innerCurrentPoint, self.innerCurrentPoint + self.pointClass(cos(a1bis), sin(a1bis)) * tickness1),  (intersectPoint, p1))
        if h1 is None:
            h1 = p1 - self.pointClass(cos(a1), sin(a1)) * tickness1

        self.innerCurrentPoint = p3 - self.pointClass(cos(a2), sin(a2)) * tickness2

        h2 = None
        if intersectPoint is not None:
            h2 = interSect((self.innerCurrentPoint, self.innerCurrentPoint + self.pointClass(cos(a2bis), sin(a2bis)) * tickness2), (intersectPoint, p2))
        if h2 is None:
            h2 = p2 - self.pointClass(cos(a1), sin(a1)) * tickness1

        self.innerPen.curveTo(h1, h2, self.innerCurrentPoint)
        self.innerPrevPoint = self.innerCurrentPoint

        ########
        h1 = None
        if intersectPoint is not None:
            h1 = interSect((self.outerCurrentPoint, self.outerCurrentPoint + self.pointClass(cos(a1bis), sin(a1bis)) * tickness1), (intersectPoint, p1))
        if h1 is None:
            h1 = p1 + self.pointClass(cos(a1), sin(a1)) * tickness1

        self.outerCurrentPoint = p3 + self.pointClass(cos(a2), sin(a2)) * tickness2

        h2 = None
        if intersectPoint is not None:
            h2 = interSect((self.outerCurrentPoint, self.outerCurrentPoint + self.pointClass(cos(a2bis), sin(a2bis)) * tickness2), (intersectPoint, p2))
        if h2 is None:
            h2 = p2 + self.pointClass(cos(a1), sin(a1)) * tickness1
        self.outerPen.curveTo(h1, h2, self.outerCurrentPoint)
        self.outerPrevPoint = self.outerCurrentPoint

        self.prevPoint = p3
        self.currentAngle = a2
        self.prevAngle = a2

    def _closePath(self):
        if self.shouldHandleMove:
            return
        if self.offset == 0:
            self.outerPen.closePath()
            self.innerPen.closePath()
            return

        if not self.prevPoint == self.firstPoint:
            self._lineTo(self.firstPoint)

        self.originalPen.closePath()

        self.innerPrevPoint = self.innerCurrentPoint
        self.innerCurrentPoint = self.innerFirstPoint

        self.outerPrevPoint = self.outerCurrentPoint
        self.outerCurrentPoint = self.outerFirstPoint

        self.prevAngle = self.currentAngle
        self.currentAngle = self.firstAngle

        self.buildConnection(close=True)

        self.innerPen.closePath()
        self.outerPen.closePath()

    def _endPath(self):
        if self.shouldHandleMove:
            return

        self.originalPen.endPath()
        self.innerPen.endPath()
        self.outerPen.endPath()

        if self.closeOpenPaths:

            innerContour = self.innerGlyph[-1]
            outerContour = self.outerGlyph[-1]

            innerContour.reverse()

            innerContour[0].segmentType = "line"
            outerContour[0].segmentType = "line"

            self.buildCap(outerContour, innerContour)

            for point in innerContour:
                outerContour.addPoint((point.x, point.y), segmentType=point.segmentType, smooth=point.smooth)

            self.innerGlyph.removeContour(innerContour)

    def addComponent(self, glyphName, transform):
        if self.preserveComponents:
            self.components.append((glyphName, transform))
        else:
            BasePen.addComponent(self, glyphName, transform)

    ## thickness

    def getThickness(self, angle):
        a2 = angle + pi * .5
        f = abs(sin(a2 + radians(self.contrastAngle)))
        f = f ** 5
        return self.offset + self.contrast * f

    ## connections

    def buildConnection(self, close=False):
        if not checkSmooth(self.prevAngle, self.currentAngle):
            if checkInnerOuter(self.prevAngle, self.currentAngle):
                self.connectionCallback(self.outerPrevPoint, self.outerCurrentPoint, self.outerPen, close)
                self.connectionInnerCorner(self.innerPrevPoint, self.innerCurrentPoint, self.innerPen, close)
            else:
                self.connectionCallback(self.innerPrevPoint, self.innerCurrentPoint, self.innerPen, close)
                self.connectionInnerCorner(self.outerPrevPoint, self.outerCurrentPoint, self.outerPen, close)


    def connectionSquare(self, first, last, pen, close):
        angle_1 = radians(degrees(self.prevAngle)+90)
        angle_2 = radians(degrees(self.currentAngle)+90)

        tempFirst = first - self.pointClass(cos(angle_1), sin(angle_1)) * self.miterLimit
        tempLast = last + self.pointClass(cos(angle_2), sin(angle_2)) * self.miterLimit

        newPoint = interSect((first, tempFirst), (last, tempLast))

        if newPoint is not None:

            if self._inputmiterLimit is not None and roundFloat(newPoint.distance(first)) > self._inputmiterLimit:
                pen.lineTo(tempFirst)
                pen.lineTo(tempLast)
            else:
                pen.lineTo(newPoint)

        if not close:
            pen.lineTo(last)

    def connectionRound(self, first, last, pen, close):
        angle_1 = radians(degrees(self.prevAngle)+90)
        angle_2 = radians(degrees(self.currentAngle)+90)

        tempFirst = first - self.pointClass(cos(angle_1), sin(angle_1)) * self.miterLimit
        tempLast = last + self.pointClass(cos(angle_2), sin(angle_2)) * self.miterLimit

        newPoint = interSect((first, tempFirst), (last, tempLast))
        if newPoint is None:
            pen.lineTo(last)
            return
        #print "(%s, %s)," % (newPoint.x, newPoint.y)
        distance1 = newPoint.distance(first)
        distance2 = newPoint.distance(last)
        #print distance1, distance2
        if roundFloat(distance1) > self.miterLimit + self.contrast:
            distance1 = self.miterLimit + tempFirst.distance(tempLast) * .7
        if roundFloat(distance2) > self.miterLimit + self.contrast:
            distance2 = self.miterLimit + tempFirst.distance(tempLast) * .7

        distance1 *= self.magicCurve
        distance2 *= self.magicCurve

        bcp1 = first - self.pointClass(cos(angle_1), sin(angle_1)) * distance1
        bcp2 = last + self.pointClass(cos(angle_2), sin(angle_2)) * distance2
        pen.curveTo(bcp1, bcp2, last)

    def connectionButt(self, first, last, pen, close):
        if not close:
            pen.lineTo(last)

    def connectionInnerCorner(self,  first, last, pen, close):
        if not close:
            pen.lineTo(last)


    ## caps

    def buildCap(self, firstContour, lastContour):
        first = firstContour[-1]
        last = lastContour[0]
        first = self.pointClass(first.x, first.y)
        last = self.pointClass(last.x, last.y)

        self.capCallback(firstContour, lastContour, first, last, self.prevAngle)

        first = lastContour[-1]
        last = firstContour[0]
        first = self.pointClass(first.x, first.y)
        last = self.pointClass(last.x, last.y)

        angle = radians(degrees(self.firstAngle)+180)
        self.capCallback(lastContour, firstContour, first, last, angle)


    def capButt(self, firstContour, lastContour, first, last, angle):
        ## not nothing
        pass

    def capRound(self, firstContour, lastContour, first, last, angle):
        hookedAngle = radians(degrees(angle)+90)

        p1 = first - self.pointClass(cos(hookedAngle), sin(hookedAngle)) * self.offset

        p2 = last - self.pointClass(cos(hookedAngle), sin(hookedAngle)) * self.offset

        oncurve = p1 + (p2-p1)*.5

        roundness = .54

        h1 = first - self.pointClass(cos(hookedAngle), sin(hookedAngle)) * self.offset * roundness
        h2 = oncurve + self.pointClass(cos(angle), sin(angle)) * self.offset * roundness

        firstContour[-1].smooth = True

        firstContour.addPoint((h1.x, h1.y))
        firstContour.addPoint((h2.x, h2.y))
        firstContour.addPoint((oncurve.x, oncurve.y), smooth=True, segmentType="curve")

        h1 = oncurve - self.pointClass(cos(angle), sin(angle)) * self.offset * roundness
        h2 = last - self.pointClass(cos(hookedAngle), sin(hookedAngle)) * self.offset * roundness

        firstContour.addPoint((h1.x, h1.y))
        firstContour.addPoint((h2.x, h2.y))

        lastContour[0].segmentType = "curve"
        lastContour[0].smooth = True

    def capSquare(self, firstContour, lastContour, first, last, angle):
        angle = radians(degrees(angle)+90)

        firstContour[-1].smooth = True
        lastContour[0].smooth = True

        p1 = first - self.pointClass(cos(angle), sin(angle)) * self.offset
        firstContour.addPoint((p1.x, p1.y), smooth=False, segmentType="line")

        p2 = last - self.pointClass(cos(angle), sin(angle)) * self.offset
        firstContour.addPoint((p2.x, p2.y), smooth=False, segmentType="line")


    def drawSettings(self, drawOriginal=False, drawInner=False, drawOuter=True):
        self.drawOriginal = drawOriginal
        self.drawInner = drawInner
        self.drawOuter = drawOuter

    def drawPoints(self, pointPen):
        if self.drawInner:
            reversePen = ReverseContourPointPen(pointPen)
            self.innerGlyph.drawPoints(CleanPointPen(reversePen))
        if self.drawOuter:
            self.outerGlyph.drawPoints(CleanPointPen(pointPen))

        if self.drawOriginal:
            if self.drawOuter:
                pointPen = ReverseContourPointPen(pointPen)
            self.originalGlyph.drawPoints(CleanPointPen(pointPen))

        for glyphName, transform in self.components:
            pointPen.addComponent(glyphName, transform)

    def draw(self, pen):
        pointPen = PointToSegmentPen(pen)
        self.drawPoints(pointPen)

    def getGlyph(self):
        glyph = Glyph()
        pointPen = glyph.getPointPen()
        self.drawPoints(pointPen)
        return glyph