source: packages/defcon/branches/ufo3/Lib/defcon/objects/glyph.py @ 1006

Revision 1006, 48.7 KB checked in by tal, 18 months ago (diff)
Handle glyph mark colors. This should resolve ticket #6.
Line 
1from fontTools.misc import arrayTools
2from defcon.objects.base import BaseObject
3from defcon.objects.contour import Contour
4from defcon.objects.point import Point
5from defcon.objects.component import Component
6from defcon.objects.anchor import Anchor
7from defcon.objects.lib import Lib
8from defcon.objects.guideline import Guideline
9from defcon.objects.image import Image
10from defcon.objects.color import Color
11
12def addRepresentationFactory(name, factory):
13    from warnings import warn
14    warn("addRepresentationFactory is deprecated. Use the functions in defcon.__init__.", DeprecationWarning)
15    Glyph.representationFactories[name] = factory
16
17def removeRepresentationFactory(name):
18    from warnings import warn
19    warn("removeRepresentationFactory is deprecated. Use the functions in defcon.__init__.", DeprecationWarning)
20    del Glyph.representationFactories[name]
21
22
23class Glyph(BaseObject):
24
25    """
26    This object represents a glyph and it contains contour, component, anchor
27    and other assorted bits data about the glyph.
28
29    **This object posts the following notifications:**
30
31    =======================
32    Name
33    =======================
34    Glyph.Changed
35    Glyph.NameChanged
36    Glyph.UnicodesChanged
37    Glyph.WidthChanged
38    Glyph.HeightChanged
39    Glyph.NoteChanged
40    Glyph.LibChanged
41    Glyph.ImageChanged
42    Glyph.ContoursChanged
43    Glyph.ComponentsChanged
44    Glyph.AnchorsChanged
45    Glyph.GuidelinesChanged
46    Glyph.MarkColorChanged
47    =======================
48
49    The Glyph object has list like behavior. This behavior allows you to interact
50    with contour data directly. For example, to get a particular contour::
51
52        contour = glyph[0]
53
54    To iterate over all contours::
55
56        for contour in glyph:
57
58    To get the number of contours::
59
60        contourCount = len(glyph)
61
62    To interact with components or anchors in a similar way,
63    use the ``components`` and ``anchors`` attributes.
64    """
65
66    changeNotificationName = "Glyph.Changed"
67
68    def __init__(self, contourClass=None, pointClass=None, componentClass=None, anchorClass=None, guidelineClass=None, libClass=None):
69        super(Glyph, self).__init__()
70
71        self._parent = None
72        self._dirty = False
73        self._name = None
74        self._unicodes = []
75        self._width = 0
76        self._height = 0
77        self._note = None
78        self._image = None
79        self._identifiers = set()
80        self._contours = []
81        self._components = []
82        self._anchors = []
83        self._guidelines = []
84        self._lib = None
85        self._boundsCache = None
86        self._controlPointBoundsCache = None
87
88        if contourClass is None:
89            contourClass = Contour
90        if pointClass is None:
91            pointClass = Point
92        if componentClass is None:
93            componentClass = Component
94        if anchorClass is None:
95            anchorClass = Anchor
96        if guidelineClass is None:
97            guidelineClass = Guideline
98        if libClass is None:
99            libClass = Lib
100
101        self._contourClass = contourClass
102        self._pointClass = pointClass
103        self._componentClass = componentClass
104        self._anchorClass = anchorClass
105        self._guidelineClass = Guideline
106        self._lib = libClass()
107
108    def setParent(self, obj):
109        if obj is None:
110            if self.getParent() is not None:
111                for contour in self._contours:
112                    self._removeParentDataInContour(contour)
113                for component in self._components:
114                    self._removeParentDataInComponent(component)
115                for anchor in self._anchors:
116                    self._removeParentDataInAnchor(anchor)
117                for guideline in self._guidelines:
118                    self._removeParentDataInGuideline(guideline)
119                self._removeParentDataInLib()
120                self._removeParentDataInImage()
121                self.removeObserver(observer=self, notification="Glyph.Changed")
122                super(Glyph, self).setParent(obj)
123        else:
124            assert self.getParent() is None
125            super(Glyph, self).setParent(obj)
126            for contour in self._contours:
127                self._setParentDataInContour(contour)
128            for component in self._components:
129                self._setParentDataInComponent(component)
130            for anchor in self._anchors:
131                self._setParentDataInAnchor(anchor)
132            for guideline in self._guidelines:
133                self._setParentDataInGuideline(guideline)
134            self._setParentDataInLib()
135            self._setParentDataInImage()
136            self.addObserver(observer=self, methodName="destroyAllRepresentations", notification="Glyph.Changed")
137
138    def _destroyBoundsCache(self):
139        self._boundsCache = None
140        self._controlPointBoundsCache = None
141
142    # ----------
143    # Attributes
144    # ----------
145
146    def _get_contourClass(self):
147        return self._contourClass
148
149    contourClass = property(_get_contourClass, doc="The class used for contours.")
150
151    def _get_pointClass(self):
152        return self._pointClass
153
154    pointClass = property(_get_pointClass, doc="The class used for points.")
155
156    def _get_componentClass(self):
157        return self._componentClass
158
159    componentClass = property(_get_componentClass, doc="The class used for components.")
160
161    def _get_anchorClass(self):
162        return self._anchorClass
163
164    anchorClass = property(_get_anchorClass, doc="The class used for anchors.")
165
166    def _get_guidelineClass(self):
167        return self._guidelineClass
168
169    guidelineClass = property(_get_guidelineClass, doc="The class used for guidelines.")
170
171    def _get_identifiers(self):
172        return self._identifiers
173
174    identifiers = property(_get_identifiers, doc="Set of identifiers for the glyph. This is primarily for internal use.")
175
176    def _set_name(self, value):
177        oldName = self._name
178        if oldName != value:
179            self._name = value
180            self.dirty = True
181            self.postNotification(notification="Glyph.NameChanged", data=dict(oldValue=oldName, newValue=value))
182
183    def _get_name(self):
184        return self._name
185
186    name = property(_get_name, _set_name, doc="The name of the glyph. Setting this posts *GLyph.NameChanged* and *Glyph.NameChanged* notifications.")
187
188    def _get_unicodes(self):
189        return list(self._unicodes)
190
191    def _set_unicodes(self, value):
192        oldValue = self.unicodes
193        if oldValue != value:
194            self._unicodes = value
195            self.dirty = True
196            self.postNotification(notification="Glyph.UnicodesChanged", data=dict(oldValue=oldValue, newValue=value))
197
198    unicodes = property(_get_unicodes, _set_unicodes, doc="The list of unicode values assigned to the glyph. Setting this posts *Glyph.UnicodesChanged* and *Glyph.Changed* notifications.")
199
200    def _get_unicode(self):
201        if self._unicodes:
202            return self._unicodes[0]
203        return None
204
205    def _set_unicode(self, value):
206        if value is None:
207            self.unicodes = []
208        else:
209            existing = list(self._unicodes)
210            if value in existing:
211                existing.pop(existing.index(value))
212            existing.insert(0, value)
213            self.unicodes = existing
214
215    unicode = property(_get_unicode, _set_unicode, doc="The primary unicode value for the glyph. This is the equivalent of ``glyph.unicodes[0]``. This is a convenience attribute that works with the ``unicodes`` attribute.")
216
217    def _get_bounds(self):
218        from robofab.pens.boundsPen import BoundsPen
219        if self._boundsCache is None:
220            pen = BoundsPen(self.getParent())
221            self.draw(pen)
222            self._boundsCache = pen.bounds
223        return self._boundsCache
224
225    bounds = property(_get_bounds, doc="The bounds of the glyph's outline expressed as a tuple of form (xMin, yMin, xMax, yMax).")
226
227    def _get_controlPointBounds(self):
228        from fontTools.pens.boundsPen import ControlBoundsPen
229        if self._controlPointBoundsCache is None:
230            pen = ControlBoundsPen(self.getParent())
231            self.draw(pen)
232            self._controlPointBoundsCache = pen.bounds
233        return self._controlPointBoundsCache
234
235    controlPointBounds = property(_get_controlPointBounds, doc="The control bounds of all points in the glyph. This only measures the point positions, it does not measure curves. So, curves without points at the extrema will not be properly measured.")
236
237    def _get_leftMargin(self):
238        bounds = self.bounds
239        if bounds is None:
240            return None
241        xMin, yMin, xMax, yMax = bounds
242        return xMin
243
244    def _set_leftMargin(self, value):
245        bounds = self.bounds
246        if bounds is None:
247            return
248        xMin, yMin, xMax, yMax = bounds
249        oldValue = xMin
250        diff = value - xMin
251        if value != oldValue:
252            self.move((diff, 0))
253            self.width += diff
254            self.dirty = True
255
256    leftMargin = property(_get_leftMargin, _set_leftMargin, doc="The left margin of the glyph. Setting this post *Glyph.WidthChanged* and *Glyph.Changed* notifications among others.")
257
258    def _get_rightMargin(self):
259        bounds = self.bounds
260        if bounds is None:
261            return None
262        xMin, yMin, xMax, yMax = bounds
263        return self._width - xMax
264
265    def _set_rightMargin(self, value):
266        bounds = self.bounds
267        if bounds is None:
268            return
269        xMin, yMin, xMax, yMax = bounds
270        oldValue = self._width - xMax
271        if oldValue != value:
272            self.width = xMax + value
273            self.dirty = True
274
275    rightMargin = property(_get_rightMargin, _set_rightMargin, doc="The right margin of the glyph. Setting this posts *Glyph.WidthChanged* and *Glyph.Changed* notifications among others.")
276
277    def _get_width(self):
278        return self._width
279
280    def _set_width(self, value):
281        oldValue = self._width
282        if oldValue != value:
283            self._width = value
284            self.postNotification(notification="Glyph.WidthChanged", data=dict(oldValue=oldValue, newValue=value))
285            self.dirty = True
286
287    width = property(_get_width, _set_width, doc="The width of the glyph. Setting this posts *Glyph.WidthChanged* and *Glyph.Changed* notifications.")
288
289    def _get_height(self):
290        return self._height
291
292    def _set_height(self, value):
293        oldValue = self._height
294        if oldValue != value:
295            self._height = value
296            self.postNotification(notification="Glyph.HeightChanged", data=dict(oldValue=oldValue, newValue=value))
297            self.dirty = True
298
299    height = property(_get_height, _set_height, doc="The height of the glyph. Setting this posts *Glyph.HeightChanged* and *Glyph.Changed* notifications.")
300
301    def _get_components(self):
302        return list(self._components)
303
304    components = property(_get_components, doc="An ordered list of :class:`Component` objects stored in the glyph.")
305
306    def _get_anchors(self):
307        return list(self._anchors)
308
309    def _set_anchors(self, value):
310        self.clearAnchors()
311        self.holdNotifications()
312        for anchor in value:
313            self.appendAnchor(anchor)
314        self.releaseHeldNotifications()
315
316    anchors = property(_get_anchors, _set_anchors, doc="An ordered list of :class:`Anchor` objects stored in the glyph.")
317
318    def _get_guidelines(self):
319        return list(self._guidelines)
320
321    def _set_guidelines(self, value):
322        self.clearGuidelines()
323        self.holdNotifications()
324        for guideline in value:
325            self.appendGuideline(guideline)
326        self.releaseHeldNotifications()
327
328    guidelines = property(_get_guidelines, _set_guidelines, doc="An ordered list of :class:`Guideline` objects stored in the glyph. Setting this will post a *Glyph.Changed* notification along with any notifications posted by the :py:meth:`Glyph.appendGuideline` and :py:meth:`Glyph.clearGuidelines` methods.")
329
330    def _get_note(self):
331        return self._note
332
333    def _set_note(self, value):
334        if value is not None:
335            assert isinstance(value, basestring)
336        oldValue = self._note
337        if oldValue != value:
338            self._note = value
339            self.postNotification(notification="Glyph.NoteChanged", data=dict(oldValue=oldValue, newValue=value))
340            self.dirty = True
341
342    note = property(_get_note, _set_note, doc="An arbitrary note for the glyph. Setting this will post a *Glyph.Changed* notification.")
343
344    def _get_lib(self):
345        return self._lib
346
347    def _set_lib(self, value):
348        self._lib.clear()
349        self._lib.update(value)
350        self.dirty = True
351
352    lib = property(_get_lib, _set_lib, doc="The glyph's :class:`Lib` object. Setting this will clear any existing lib data and post a *Glyph.Changed* notification if data was replaced.")
353
354    def _get_image(self):
355        return self._image
356
357    def _set_image(self, image):
358        if image is None:
359            if self._image is not None:
360                self._removeParentDataInImage()
361                self._image = None
362                self.postNotification(notification="Glyph.ImageChanged")
363                self.dirty = True
364        else:
365            if self._image is None:
366                self._image = Image()
367                self._setParentDataInImage()
368            if set(self.image.items()) != set(image.items()):
369                for key in self._image.keys():
370                    self._image[key] = image.get(key)
371                self.postNotification(notification="Glyph.ImageChanged")
372                self.dirty = True
373
374    image = property(_get_image, _set_image, doc="The glyph's :class:`Image` object. Setting this posts *Glyph.ImageChanged* and *Glyph.Changed* notifications.")
375
376    def _get_markColor(self):
377        value = self.lib.get("public.markColor")
378        if value is not None:
379            value = Color(value)
380        return value
381
382    def _set_markColor(self, value):
383        # convert to a color object
384        if value is not None:
385            value = Color(value)
386        # don't write if there is no change
387        oldValue = self.lib.get("public.markColor")
388        if oldValue is not None:
389            oldValue = Color(oldValue)
390        if value == oldValue:
391            return
392        # remove
393        if value is None:
394            if "public.markColor" in self.lib:
395                del self.lib["public.markColor"]
396        # store
397        else:
398            self.lib["public.markColor"] = value
399        self.postNotification(notification="Glyph.MarkColorChanged", data=dict(oldValue=oldValue, newValue=value))
400
401    markColor = property(_get_markColor, _set_markColor, doc="The glyph's mark color. The value should be a :class:`Color` object but any value that can be converted to that type of object can be accepted when setting. Setting this posts *Glyph.MarkColorChanged* and *Glyph.Changed* notifications.")
402
403    # -----------
404    # Pen Methods
405    # -----------
406
407    def draw(self, pen):
408        """
409        Draw the glyph with **pen**.
410        """
411        from robofab.pens.adapterPens import PointToSegmentPen
412        pointPen = PointToSegmentPen(pen)
413        self.drawPoints(pointPen)
414
415    def drawPoints(self, pointPen):
416        """
417        Draw the glyph with **pointPen**.
418        """
419        for contour in self._contours:
420            contour.drawPoints(pointPen)
421        for component in self._components:
422            component.drawPoints(pointPen)
423
424    def getPen(self):
425        """
426        Get the pen used to draw into this glyph.
427        """
428        from robofab.pens.adapterPens import SegmentToPointPen
429        return SegmentToPointPen(self.getPointPen())
430
431    def getPointPen(self):
432        """
433        Get the point pen used to draw into this glyph.
434        """
435        from defcon.pens.glyphObjectPointPen import GlyphObjectPointPen
436        return GlyphObjectPointPen(self)
437
438    # --------------------------
439    # Parent Setting and Removal
440    # --------------------------
441
442    def _setParentDataInLib(self):
443        self._lib.setParent(self)
444        if self.dispatcher is not None and not self._lib.hasObserver(observer=self, notification="Lib.Changed"):
445            self._lib.addObserver(observer=self, methodName="_libContentChanged", notification="Lib.Changed")
446
447    def _removeParentDataInLib(self):
448        if self.dispatcher is not None:
449            self._lib.removeObserver(observer=self, notification="Lib.Changed")
450        self._lib.setParent(None)
451
452    def _setParentDataInContour(self, contour):
453        contour.setParent(self)
454        if self.dispatcher is not None and not contour.hasObserver(observer=self, notification="Contour.Changed"):
455            contour.addObserver(observer=self, methodName="_contourChanged", notification="Contour.Changed")
456
457    def _removeParentDataInContour(self, contour):
458        if self.dispatcher is not None:
459            contour.removeObserver(observer=self, notification="Contour.Changed")
460        contour.setParent(None)
461
462    def _setParentDataInComponent(self, component):
463        component.setParent(self)
464        if self.dispatcher is not None and not component.hasObserver(observer=self, notification="Component.Changed"):
465            component.addObserver(observer=self, methodName="_componentChanged", notification="Component.Changed")
466
467    def _removeParentDataInComponent(self, component):
468        if self.dispatcher is not None:
469            component.removeObserver(observer=self, notification="Component.Changed")
470        component.setParent(None)
471
472    def _setParentDataInAnchor(self, anchor):
473        anchor.setParent(self)
474        if self.dispatcher is not None and not anchor.hasObserver(observer=self, notification="Anchor.Changed"):
475            anchor.addObserver(observer=self, methodName="_anchorChanged", notification="Anchor.Changed")
476
477    def _removeParentDataInAnchor(self, anchor):
478        if self.dispatcher is not None:
479            anchor.removeObserver(observer=self, notification="Anchor.Changed")
480        anchor.setParent(None)
481
482    def _setParentDataInGuideline(self, guideline):
483        guideline.setParent(self)
484        if self.dispatcher is not None and not guideline.hasObserver(observer=self, notification="Guideline.Changed"):
485            guideline.addObserver(observer=self, methodName="_guidelineChanged", notification="Guideline.Changed")
486
487    def _removeParentDataInGuideline(self, guideline):
488        if self.dispatcher is not None:
489            guideline.removeObserver(observer=self, notification="Guideline.Changed")
490        guideline.setParent(None)
491
492    def _setParentDataInImage(self):
493        if self._image is None:
494            return
495        self._image.setParent(self)
496        if self.dispatcher is not None and not self._image.hasObserver(observer=self, notification="Image.Changed"):
497            self._image.addObserver(observer=self, methodName="_imageChanged", notification="Image.Changed")
498
499    def _removeParentDataInImage(self):
500        if self._image is None:
501            return
502        if self.dispatcher is not None:
503            self._image.removeObserver(observer=self, notification="Image.Changed")
504        self._image.setParent(None)
505
506    # -------
507    # Methods
508    # -------
509
510    def __len__(self):
511        return len(self._contours)
512
513    def __iter__(self):
514        contourCount = len(self)
515        index = 0
516        while index < contourCount:
517            contour = self[index]
518            yield contour
519            index += 1
520
521    def __getitem__(self, index):
522        return self._contours[index]
523
524    def _getContourIndex(self, contour):
525        return self._contours.index(contour)
526
527    def copyDataFromGlyph(self, glyph):
528        """
529        Copy data from **glyph**. This copies the following data:
530
531        ==========
532        width
533        height
534        unicodes
535        note
536        image
537        contours
538        components
539        anchors
540        guidelines
541        lib
542        ==========
543
544        The name attribute is purposefully omitted.
545        """
546        from copy import deepcopy
547        self.width = glyph.width
548        self.height = glyph.height
549        self.unicodes = list(glyph.unicodes)
550        self.note = glyph.note
551        self.guidelines = glyph.guidelines
552        self.anchors = glyph.anchors
553        self.image = glyph.image
554        pointPen = self.getPointPen()
555        glyph.drawPoints(pointPen)
556        self.lib = deepcopy(glyph.lib)
557
558    def appendContour(self, contour):
559        """
560        Append **contour** to the glyph. The contour must be a defcon
561        :class:`Contour` object or a subclass of that object. An error
562        will be raised if the contour's identifier or a point identifier
563        conflicts with any of the identifiers within the glyph.
564
565        This will post a *Glyph.Changed* notification.
566        """
567        assert contour not in self._contours
568        self.insertContour(len(self._contours), contour)
569
570    def appendComponent(self, component):
571        """
572        Append **component** to the glyph. The component must be a defcon
573        :class:`Component` object or a subclass of that object. An error
574        will be raised if the component's identifier conflicts with any of
575        the identifiers within the glyph.
576
577        This will post a *Glyph.Changed* notification.
578        """
579        assert component not in self._components
580        self.insertComponent(len(self._components), component)
581
582    def appendAnchor(self, anchor):
583        """
584        Append **anchor** to the glyph. The anchor must be a defcon
585        :class:`Anchor` object or a subclass of that object. An error
586        will be raised if the anchor's identifier conflicts with any of
587        the identifiers within the glyph.
588
589        This will post a *Glyph.Changed* notification.
590        """
591        assert anchor not in self._anchors
592        self.insertAnchor(len(self._anchors), anchor)
593
594    def appendGuideline(self, guideline):
595        """
596        Append **guideline** to the glyph. The guideline must be a defcon
597        :class:`Guideline` object or a subclass of that object. An error
598        will be raised if the guideline's identifier conflicts with any of
599        the identifiers within the glyph.
600
601        This will post a *Glyph.Changed* notification.
602        """
603        assert guideline not in self._guidelines
604        self.insertGuideline(len(self._guidelines), guideline)
605
606    def insertContour(self, index, contour):
607        """
608        Insert **contour** into the glyph at index. The contour
609        must be a defcon :class:`Contour` object or a subclass
610        of that object. An error will be raised if the contour's
611        identifier or a point identifier conflicts with any of
612        the identifiers within the glyph.
613
614        This will post a *Glyph.Changed* notification.
615        """
616        assert contour not in self._contours
617        identifiers = self._identifiers
618        if contour.identifier is not None:
619            assert contour.identifier not in identifiers
620            if contour.identifier is not None:
621                identifiers.add(contour.identifier)
622        for point in contour:
623            if point.identifier is not None:
624                assert point.identifier not in identifiers
625                self._identifiers.add(point.identifier)
626        if contour.getParent() != self:
627            self._setParentDataInContour(contour)
628        self._contours.insert(index, contour)
629        self._destroyBoundsCache()
630        self.postNotification(notification="Glyph.ContoursChanged")
631        self.dirty = True
632
633    def insertComponent(self, index, component):
634        """
635        Insert **component** into the glyph at index. The component
636        must be a defcon :class:`Component` object or a subclass
637        of that object. An error will be raised if the component's
638        identifier conflicts with any of the identifiers within
639        the glyph.
640
641        This will post a *Glyph.Changed* notification.
642        """
643        assert component not in self._components
644        if component.identifier is not None:
645            identifiers = self._identifiers
646            assert component.identifier not in identifiers
647            if component.identifier is not None:
648                identifiers.add(component.identifier)
649        if component.getParent() != self:
650            self._setParentDataInComponent(component)
651        self._components.insert(index, component)
652        self._destroyBoundsCache()
653        self.postNotification(notification="Glyph.ComponentsChanged")
654        self.dirty = True
655
656    def insertAnchor(self, index, anchor):
657        """
658        Insert **anchor** into the glyph at index. The anchor
659        must be a defcon :class:`Anchor` object or a subclass
660        of that object. An error will be raised if the anchor's
661        identifier conflicts with any of the identifiers within
662        the glyph.
663
664        This will post a *Glyph.Changed* notification.
665        """
666        assert anchor not in self._anchors
667        if not isinstance(anchor, self._anchorClass):
668            anchor = self._anchorClass(anchor)
669        if anchor.identifier is not None:
670            identifiers = self._identifiers
671            assert anchor.identifier not in identifiers
672            if anchor.identifier is not None:
673                identifiers.add(anchor.identifier)
674        if anchor.getParent() != self:
675            self._setParentDataInAnchor(anchor)
676        self._anchors.insert(index, anchor)
677        self.postNotification(notification="Glyph.AnchorsChanged")
678        self.dirty = True
679
680    def insertGuideline(self, index, guideline):
681        """
682        Insert **guideline** into the glyph at index. The guideline
683        must be a defcon :class:`Guideline` object or a subclass
684        of that object. An error will be raised if the guideline's
685        identifier conflicts with any of the identifiers within
686        the glyph.
687
688        This will post a *Glyph.Changed* notification.
689        """
690        assert guideline not in self._guidelines
691        if not isinstance(guideline, self._guidelineClass):
692            guideline = self._guidelineClass(guideline)
693        if guideline.identifier is not None:
694            identifiers = self._identifiers
695            assert guideline.identifier not in identifiers
696            if guideline.identifier is not None:
697                identifiers.add(guideline.identifier)
698        if guideline.getParent() != self:
699            self._setParentDataInGuideline(guideline)
700        self._guidelines.insert(index, guideline)
701        self.postNotification(notification="Glyph.GuidelinesChanged")
702        self.dirty = True
703
704    def removeContour(self, contour):
705        """
706        Remove **contour** from the glyph.
707
708        This will post a *Glyph.Changed* notification.
709        """
710        identifiers = self._identifiers
711        if contour.identifier is not None:
712            identifiers.remove(contour.identifier)
713        for point in contour:
714            if point.identifier is not None:
715                identifiers.remove(point.identifier)
716        self._contours.remove(contour)
717        self._removeParentDataInContour(contour)
718        self._destroyBoundsCache()
719        self.postNotification(notification="Glyph.ContoursChanged")
720        self.dirty = True
721
722    def removeComponent(self, component):
723        """
724        Remove **component** from the glyph.
725
726        This will post a *Glyph.Changed* notification.
727        """
728        if component.identifier is not None:
729            self._identifiers.remove(component.identifier)
730        self._components.remove(component)
731        self._removeParentDataInComponent(component)
732        self._destroyBoundsCache()
733        self.postNotification(notification="Glyph.ComponentsChanged")
734        self.dirty = True
735
736    def removeAnchor(self, anchor):
737        """
738        Remove **anchor** from the glyph.
739
740        This will post a *Glyph.Changed* notification.
741        """
742        # XXX handle identifiers
743        self._anchors.remove(anchor)
744        self._removeParentDataInAnchor(anchor)
745        self.postNotification(notification="Glyph.AnchorsChanged")
746        self.dirty = True
747
748    def removeGuideline(self, guideline):
749        """
750        Remove **guideline** from the glyph.
751
752        This will post a *Glyph.Changed* notification.
753        """
754        if guideline.identifier is not None:
755            self._identifiers.remove(guideline.identifier)
756        self._guidelines.remove(guideline)
757        self._removeParentDataInGuideline(guideline)
758        self.postNotification(notification="Glyph.GuidelinesChanged")
759        self.dirty = True
760
761    def contourIndex(self, contour):
762        """
763        Get the index for **contour**.
764        """
765        return self._contours.index(contour)
766
767    def componentIndex(self, component):
768        """
769        Get the index for **component**.
770        """
771        return self._components.index(component)
772
773    def anchorIndex(self, anchor):
774        """
775        Get the index for **anchor**.
776        """
777        return self._anchors.index(anchor)
778
779    def guidelineIndex(self, guideline):
780        """
781        Get the index for **guideline**.
782        """
783        return self._guidelines.index(guideline)
784
785    def clear(self):
786        """
787        Clear all contours, components, anchors and guidelines from the glyph.
788
789        This posts a *Glyph.Changed* notification.
790        """
791        self.holdNotifications()
792        self.clearContours()
793        self.clearComponents()
794        self.clearAnchors()
795        self.clearGuidelines()
796        self.releaseHeldNotifications()
797
798    def clearContours(self):
799        """
800        Clear all contours from the glyph.
801
802        This posts a *Glyph.Changed* notification.
803        """
804        self.holdNotifications()
805        for contour in reversed(self._contours):
806            self.removeContour(contour)
807        self.releaseHeldNotifications()
808
809    def clearComponents(self):
810        """
811        Clear all components from the glyph.
812
813        This posts a *Glyph.Changed* notification.
814        """
815        self.holdNotifications()
816        for component in reversed(self._components):
817            self.removeComponent(component)
818        self.releaseHeldNotifications()
819
820    def clearAnchors(self):
821        """
822        Clear all anchors from the glyph.
823
824        This posts a *Glyph.Changed* notification.
825        """
826        self.holdNotifications()
827        for anchor in reversed(self._anchors):
828            self.removeAnchor(anchor)
829        self.releaseHeldNotifications()
830
831    def clearGuidelines(self):
832        """
833        Clear all guidelines from the glyph.
834
835        This posts a *Glyph.Changed* notification.
836        """
837        self.holdNotifications()
838        for guideline in reversed(self._guidelines):
839            self.removeGuideline(guideline)
840        self.releaseHeldNotifications()
841
842    def move(self, (x, y)):
843        """
844        Move all contours, components and anchors in the glyph
845        by **(x, y)**.
846
847        This posts a *Glyph.Changed* notification.
848        """
849        oldBounds = self._boundsCache
850        oldControlPointBounds = self._controlPointBoundsCache
851        for contour in self._contours:
852            contour.move((x, y))
853        for component in self._components:
854            component.move((x, y))
855        for anchor in self._anchors:
856            anchor.move((x, y))
857        if oldBounds:
858            xMin, yMin, xMax, yMax = oldBounds
859            xMin += x
860            yMin += y
861            xMax += x
862            yMax += y
863            self._boundsCache = (xMin, yMin, xMax, yMax)
864        if oldControlPointBounds:
865            xMin, yMin, xMax, yMax = oldControlPointBounds
866            xMin += x
867            yMin += y
868            xMax += x
869            yMax += y
870            self._controlPointBoundsCache = (xMin, yMin, xMax, yMax)
871
872    def pointInside(self, (x, y), evenOdd=False):
873        """
874        Returns a boolean indicating if **(x, y)** is in the
875        "black" area of the glyph.
876        """
877        from fontTools.pens.pointInsidePen import PointInsidePen
878        pen = PointInsidePen(glyphSet=None, testPoint=(x, y), evenOdd=evenOdd)
879        self.draw(pen)
880        return pen.getResult()
881
882    # ----------------------
883    # Notification Callbacks
884    # ----------------------
885
886    def _imageChanged(self, notification):
887        self.postNotification(notification="Glyph.ImageChanged")
888        self.dirty = True
889
890    def _contourChanged(self, notification):
891        self._destroyBoundsCache()
892        self.postNotification(notification="Glyph.ContoursChanged")
893        self.dirty = True
894
895    def _componentChanged(self, notification):
896        self._destroyBoundsCache()
897        self.postNotification(notification="Glyph.ComponentsChanged")
898        self.dirty = True
899
900    def _anchorChanged(self, notification):
901        self._destroyBoundsCache()
902        self.postNotification(notification="Glyph.AnchorsChanged")
903        self.dirty = True
904
905    def _guidelineChanged(self, notification):
906        self.postNotification(notification="Glyph.GuidelinesChanged")
907        self.dirty = True
908
909    def _libContentChanged(self, notification):
910        self.postNotification(notification="Glyph.LibChanged")
911        self.dirty = True
912
913
914# -----
915# Tests
916# -----
917
918def _testName():
919    """
920    # set
921    >>> from defcon.test.testTools import getTestFontPath
922    >>> from defcon.objects.font import Font
923    >>> font = Font(getTestFontPath())
924    >>> glyph = font['A']
925    >>> glyph.name = 'RenamedGlyph'
926    >>> glyph.name
927    'RenamedGlyph'
928    >>> keys = font.keys()
929    >>> keys.sort()
930    >>> keys
931    ['B', 'C', 'RenamedGlyph']
932
933    >>> font = Font(getTestFontPath())
934    >>> glyph = font['A']
935    >>> glyph.name = 'A'
936    >>> glyph.dirty
937    False
938
939    # get
940    >>> from defcon.test.testTools import getTestFontPath
941    >>> from defcon.objects.font import Font
942    >>> font = Font(getTestFontPath())
943    >>> glyph = font['A']
944    >>> glyph.name
945    'A'
946    """
947
948def _testUnicodes():
949    """
950    # get
951    >>> from defcon.test.testTools import getTestFontPath
952    >>> from defcon.objects.font import Font
953    >>> font = Font(getTestFontPath())
954    >>> glyph = font['A']
955    >>> glyph.unicodes
956    [65]
957
958    # set
959    >>> from defcon.test.testTools import getTestFontPath
960    >>> from defcon.objects.font import Font
961    >>> font = Font(getTestFontPath())
962    >>> glyph = font['A']
963    >>> glyph.unicodes = [123, 456]
964    >>> glyph.unicodes
965    [123, 456]
966    >>> glyph.dirty
967    True
968    """
969
970def _testBounds():
971    """
972    >>> from defcon.test.testTools import getTestFontPath
973    >>> from defcon.objects.font import Font
974    >>> font = Font(getTestFontPath())
975    >>> glyph = font['A']
976    >>> glyph.bounds
977    (0, 0, 700, 700)
978    >>> glyph = font['B']
979    >>> glyph.bounds
980    (0, 0, 700, 700)
981    >>> glyph = font['C']
982    >>> glyph.bounds
983    (0.0, 0.0, 700.0, 700.0)
984    """
985
986def _testControlPointBounds():
987    """
988    >>> from defcon.test.testTools import getTestFontPath
989    >>> from defcon.objects.font import Font
990    >>> font = Font(getTestFontPath())
991    >>> glyph = font['A']
992    >>> glyph.controlPointBounds
993    (0, 0, 700, 700)
994    >>> glyph = font['B']
995    >>> glyph.controlPointBounds
996    (0, 0, 700, 700)
997    >>> glyph = font['C']
998    >>> glyph.controlPointBounds
999    (0.0, 0.0, 700.0, 700.0)
1000    """
1001
1002def _testLeftMargin():
1003    """
1004    # get
1005    >>> from defcon.test.testTools import getTestFontPath
1006    >>> from defcon.objects.font import Font
1007    >>> font = Font(getTestFontPath())
1008    >>> glyph = font['A']
1009    >>> glyph.leftMargin
1010    0
1011    >>> glyph = font['B']
1012    >>> glyph.leftMargin
1013    0
1014
1015    # set
1016    >>> from defcon.test.testTools import getTestFontPath
1017    >>> from defcon.objects.font import Font
1018    >>> font = Font(getTestFontPath())
1019    >>> glyph = font['A']
1020    >>> glyph.leftMargin = 100
1021    >>> glyph.leftMargin
1022    100
1023    >>> glyph.width
1024    800
1025    >>> glyph.dirty
1026    True
1027    """
1028
1029def _testRightMargin():
1030    """
1031    # get
1032    >>> from defcon.test.testTools import getTestFontPath
1033    >>> from defcon.objects.font import Font
1034    >>> font = Font(getTestFontPath())
1035    >>> glyph = font['A']
1036    >>> glyph.rightMargin
1037    0
1038
1039    # set
1040    >>> from defcon.test.testTools import getTestFontPath
1041    >>> from defcon.objects.font import Font
1042    >>> font = Font(getTestFontPath())
1043    >>> glyph = font['A']
1044    >>> glyph.rightMargin = 100
1045    >>> glyph.rightMargin
1046    100
1047    >>> glyph.width
1048    800
1049    >>> glyph.dirty
1050    True
1051    """
1052
1053def _testWidth():
1054    """
1055    # get
1056    >>> from defcon.test.testTools import getTestFontPath
1057    >>> from defcon.objects.font import Font
1058    >>> font = Font(getTestFontPath())
1059    >>> glyph = font['A']
1060    >>> glyph.width
1061    700
1062
1063    # set
1064    >>> from defcon.test.testTools import getTestFontPath
1065    >>> from defcon.objects.font import Font
1066    >>> font = Font(getTestFontPath())
1067    >>> glyph = font['A']
1068    >>> glyph.width = 100
1069    >>> glyph.width
1070    100
1071    >>> glyph.dirty
1072    True
1073    """
1074
1075def _testHeight():
1076    """
1077    # get
1078    >>> from defcon.test.testTools import getTestFontPath
1079    >>> from defcon.objects.font import Font
1080    >>> font = Font(getTestFontPath())
1081    >>> glyph = font['A']
1082    >>> glyph.height
1083    500
1084
1085    # set
1086    >>> from defcon.test.testTools import getTestFontPath
1087    >>> from defcon.objects.font import Font
1088    >>> font = Font(getTestFontPath())
1089    >>> glyph = font['A']
1090    >>> glyph.height = 100
1091    >>> glyph.height
1092    100
1093    >>> glyph.dirty
1094    True
1095    """
1096
1097def _testComponents():
1098    """
1099    >>> from defcon.test.testTools import getTestFontPath
1100    >>> from defcon.objects.font import Font
1101    >>> font = Font(getTestFontPath())
1102    >>> glyph = font['C']
1103    >>> len(glyph.components)
1104    2
1105    """
1106
1107def _testAnchors():
1108    """
1109    >>> from defcon.test.testTools import getTestFontPath
1110    >>> from defcon.objects.font import Font
1111    >>> font = Font(getTestFontPath())
1112    >>> glyph = font['A']
1113    >>> len(glyph.anchors)
1114    2
1115    """
1116
1117def _testMarkColor():
1118    """
1119    >>> from defcon.objects.font import Font
1120    >>> font = Font()
1121    >>> font.newGlyph("A")
1122    >>> glyph = font["A"]
1123    >>> glyph.markColor
1124    >>> glyph.markColor = "1,0,1,0"
1125    >>> glyph.markColor
1126    '1,0,1,0'
1127    >>> glyph.markColor = "1,0,1,0"
1128    >>> glyph.markColor
1129    '1,0,1,0'
1130    >>> glyph.markColor = None
1131    >>> glyph.markColor
1132    """
1133
1134def _testCopyFromGlyph():
1135    """
1136    >>> source = Glyph()
1137    >>> source.name = "a"
1138    >>> source.width = 1
1139    >>> source.height = 2
1140    >>> source.unicodes = [3, 4]
1141    >>> source.note = "test image"
1142    >>> source.image = dict(fileName="test image")
1143    >>> source.anchors = [dict(x=100, y=200, name="test anchor")]
1144    >>> source.guidelines = [dict(x=10, y=20, name="test guideline")]
1145    >>> source.lib = {"foo" : "bar"}
1146    >>> pen = source.getPointPen()
1147    >>> pen.beginPath()
1148    >>> pen.addPoint((100, 200), segmentType="line")
1149    >>> pen.addPoint((300, 400), segmentType="line")
1150    >>> pen.endPath()
1151    >>> component = Component()
1152    >>> component.base = "b"
1153    >>> source.appendComponent(component)
1154    >>> dest = Glyph()
1155    >>> dest.copyDataFromGlyph(source)
1156
1157    >>> source.name == dest.name
1158    False
1159    >>> source.width == dest.width
1160    True
1161    >>> source.height == dest.height
1162    True
1163    >>> source.unicodes == dest.unicodes
1164    True
1165    >>> source.note == dest.note
1166    True
1167    >>> source.image.items() == dest.image.items()
1168    True
1169    >>> [g.items() for g in source.guidelines] == [g.items() for g in dest.guidelines]
1170    True
1171    >>> [g.items() for g in source.anchors] == [g.items() for g in dest.anchors]
1172    True
1173    >>> len(source) == len(dest)
1174    True
1175    >>> len(source.components) == len(dest.components)
1176    True
1177    >>> sourceContours = []
1178    >>> for contour in source:
1179    ...     sourceContours.append([])
1180    ...     for point in contour:
1181    ...         sourceContours[-1].append((point.x, point.x, point.segmentType, point.name))
1182    >>> destContours = []
1183    >>> for contour in dest:
1184    ...     destContours.append([])
1185    ...     for point in contour:
1186    ...         destContours[-1].append((point.x, point.x, point.segmentType, point.name))
1187    >>> sourceContours == destContours
1188    True
1189    >>> source.components[0].baseGlyph == dest.components[0].baseGlyph
1190    True
1191    """
1192
1193def _testLen():
1194    """
1195    >>> from defcon.test.testTools import getTestFontPath
1196    >>> from defcon.objects.font import Font
1197    >>> font = Font(getTestFontPath())
1198    >>> glyph = font['A']
1199    >>> len(glyph)
1200    2
1201    """
1202
1203def _testIter():
1204    """
1205    >>> from defcon.test.testTools import getTestFontPath
1206    >>> from defcon.objects.font import Font
1207    >>> font = Font(getTestFontPath())
1208    >>> glyph = font['A']
1209    >>> for contour in glyph:
1210    ...     print len(contour)
1211    4
1212    4
1213    """
1214
1215def _testAppendContour():
1216    """
1217    >>> from defcon.objects.contour import Contour
1218    >>> glyph = Glyph()
1219    >>> glyph.dirty = False
1220    >>> contour = Contour()
1221    >>> glyph.appendContour(contour)
1222    >>> len(glyph)
1223    1
1224    >>> glyph.dirty
1225    True
1226    >>> contour.getParent() == glyph
1227    True
1228    """
1229
1230def _testAppendComponent():
1231    """
1232    >>> from defcon.objects.component import Component
1233    >>> glyph = Glyph()
1234    >>> glyph.dirty = False
1235    >>> component = Component()
1236    >>> glyph.appendComponent(component)
1237    >>> len(glyph.components)
1238    1
1239    >>> glyph.dirty
1240    True
1241    >>> component.getParent() == glyph
1242    True
1243    """
1244
1245def _testAppendAnchor():
1246    """
1247    >>> from defcon.objects.anchor import Anchor
1248    >>> glyph = Glyph()
1249    >>> glyph.dirty = False
1250    >>> anchor = Anchor()
1251    >>> glyph.appendAnchor(anchor)
1252    >>> len(glyph.anchors)
1253    1
1254    >>> glyph.dirty
1255    True
1256    >>> anchor.getParent() == glyph
1257    True
1258    """
1259
1260def _testAppendGuideline():
1261    """
1262    >>> from defcon.objects.guideline import Guideline
1263    >>> glyph = Glyph()
1264    >>> glyph.dirty = False
1265    >>> guideline = Guideline()
1266    >>> glyph.appendGuideline(guideline)
1267    >>> len(glyph.guidelines)
1268    1
1269    >>> glyph.dirty
1270    True
1271    >>> guideline.getParent() == glyph
1272    True
1273    """
1274
1275def _testRemoveContour():
1276    """
1277    >>> from defcon.test.testTools import getTestFontPath
1278    >>> from defcon.objects.font import Font
1279    >>> font = Font(getTestFontPath())
1280    >>> glyph = font['A']
1281    >>> contour = glyph[0]
1282    >>> glyph.removeContour(contour)
1283    >>> contour in glyph._contours
1284    False
1285    >>> contour.getParent()
1286    """
1287
1288def _testRemoveComponent():
1289    """
1290    >>> from defcon.test.testTools import getTestFontPath
1291    >>> from defcon.objects.font import Font
1292    >>> font = Font(getTestFontPath())
1293    >>> glyph = font['C']
1294    >>> component = glyph.components[0]
1295    >>> glyph.removeComponent(component)
1296    >>> component in glyph.components
1297    False
1298    >>> component.getParent()
1299    """
1300
1301def _testRemoveAnchor():
1302    """
1303    >>> from defcon.test.testTools import getTestFontPath
1304    >>> from defcon.objects.font import Font
1305    >>> font = Font(getTestFontPath())
1306    >>> glyph = font['A']
1307    >>> anchor = glyph.anchors[0]
1308    >>> glyph.removeAnchor(anchor)
1309    >>> anchor in glyph.anchors
1310    False
1311    >>> anchor.getParent()
1312    """
1313
1314def _testRemoveGuideline():
1315    """
1316    >>> from defcon.test.testTools import getTestFontPath
1317    >>> from defcon.objects.font import Font
1318    >>> font = Font(getTestFontPath())
1319    >>> glyph = font.layers["Layer 1"]["A"]
1320    >>> guideline = glyph.guidelines[0]
1321    >>> glyph.removeGuideline(guideline)
1322    >>> guideline in glyph.guidelines
1323    False
1324    >>> guideline.getParent()
1325    """
1326
1327def _testContourIndex():
1328    """
1329    >>> from defcon.test.testTools import getTestFontPath
1330    >>> from defcon.objects.font import Font
1331    >>> font = Font(getTestFontPath())
1332    >>> glyph = font['A']
1333    >>> contour = glyph[0]
1334    >>> glyph.contourIndex(contour)
1335    0
1336    >>> contour = glyph[1]
1337    >>> glyph.contourIndex(contour)
1338    1
1339    """
1340
1341def _testComponentIndex():
1342    """
1343    >>> from defcon.test.testTools import getTestFontPath
1344    >>> from defcon.objects.font import Font
1345    >>> font = Font(getTestFontPath())
1346    >>> glyph = font['C']
1347    >>> component = glyph.components[0]
1348    >>> glyph.componentIndex(component)
1349    0
1350    >>> component = glyph.components[1]
1351    >>> glyph.componentIndex(component)
1352    1
1353    """
1354
1355def _testAnchorIndex():
1356    """
1357    >>> from defcon.test.testTools import getTestFontPath
1358    >>> from defcon.objects.font import Font
1359    >>> font = Font(getTestFontPath())
1360    >>> glyph = font['A']
1361    >>> anchor = glyph.anchors[0]
1362    >>> glyph.anchorIndex(anchor)
1363    0
1364    >>> anchor = glyph.anchors[1]
1365    >>> glyph.anchorIndex(anchor)
1366    1
1367    """
1368
1369def _testClear():
1370    """
1371    >>> from defcon.test.testTools import getTestFontPath
1372    >>> from defcon.objects.font import Font
1373    >>> font = Font(getTestFontPath())
1374    >>> glyph = font['A']
1375    >>> contour = glyph[0]
1376    >>> anchor = glyph.anchors[0]
1377    >>> glyph.clear()
1378    >>> len(glyph)
1379    0
1380    >>> len(glyph.anchors)
1381    0
1382    >>> glyph = font['C']
1383    >>> component = glyph.components[0]
1384    >>> glyph.clear()
1385    >>> len(glyph.components)
1386    0
1387    >>> glyph = font.layers["Layer 1"]["A"]
1388    >>> guideline = glyph.guidelines[0]
1389    >>> glyph.clear()
1390    >>> len(glyph.guidelines)
1391    0
1392
1393    >>> contour.getParent(), component.getParent(), anchor.getParent(), guideline.getParent()
1394    (None, None, None, None)
1395    >>> contour.dispatcher, component.dispatcher, anchor.dispatcher, guideline.dispatcher
1396    (None, None, None, None)
1397    """
1398
1399def _testClearContours():
1400    """
1401    >>> from defcon.test.testTools import getTestFontPath
1402    >>> from defcon.objects.font import Font
1403    >>> font = Font(getTestFontPath())
1404    >>> glyph = font['A']
1405    >>> glyph.clearContours()
1406    >>> len(glyph)
1407    0
1408    """
1409
1410def _testClearComponents():
1411    """
1412    >>> from defcon.test.testTools import getTestFontPath
1413    >>> from defcon.objects.font import Font
1414    >>> font = Font(getTestFontPath())
1415    >>> glyph = font['C']
1416    >>> glyph.clearComponents()
1417    >>> len(glyph.components)
1418    0
1419    """
1420
1421def _testClearAnchors():
1422    """
1423    >>> from defcon.test.testTools import getTestFontPath
1424    >>> from defcon.objects.font import Font
1425    >>> font = Font(getTestFontPath())
1426    >>> glyph = font['A']
1427    >>> glyph.clearAnchors()
1428    >>> len(glyph.anchors)
1429    0
1430    """
1431
1432def _testClearGuidelines():
1433    """
1434    >>> from defcon.test.testTools import getTestFontPath
1435    >>> from defcon.objects.font import Font
1436    >>> font = Font(getTestFontPath())
1437    >>> glyph = font['A']
1438    >>> glyph.clearGuidelines()
1439    >>> len(glyph.guidelines)
1440    0
1441    """
1442
1443def _testMove():
1444    """
1445    >>> from defcon.test.testTools import getTestFontPath
1446    >>> from defcon.objects.font import Font
1447    >>> font = Font(getTestFontPath())
1448    >>> glyph = font['A']
1449    >>> xMin, yMin, xMax, yMax = glyph.bounds
1450    >>> glyph.move((100, 50))
1451    >>> (xMin+100, yMin+50, xMax+100, yMax+50) == glyph.bounds
1452    True
1453    >>> glyph = font['C']
1454    >>> xMin, yMin, xMax, yMax = glyph.bounds
1455
1456    #>>> glyph.move((100, 50))
1457    #>>> (xMin+100, yMin+50, xMax+100, yMax+50) == glyph.bounds
1458    #True
1459    """
1460
1461def _testPointInside():
1462    """
1463    >>> from defcon.test.testTools import getTestFontPath
1464    >>> from defcon.objects.font import Font
1465    >>> font = Font(getTestFontPath())
1466    >>> glyph = font['A']
1467    >>> glyph.pointInside((100, 100))
1468    True
1469    >>> glyph.pointInside((350, 350))
1470    False
1471    >>> glyph.pointInside((-100, -100))
1472    False
1473    """
1474
1475def _testIdentifiers():
1476    """
1477    >>> glyph = Glyph()
1478    >>> pointPen = glyph.getPointPen()
1479    >>> pointPen.beginPath(identifier="contour 1")
1480    >>> pointPen.addPoint((0, 0), identifier="point 1")
1481    >>> pointPen.addPoint((0, 0), identifier="point 2")
1482    >>> pointPen.endPath()
1483    >>> pointPen.beginPath(identifier="contour 2")
1484    >>> pointPen.endPath()
1485    >>> pointPen.addComponent("A", (1, 1, 1, 1, 1, 1), identifier="component 1")
1486    >>> pointPen.addComponent("A", (1, 1, 1, 1, 1, 1), identifier="component 2")
1487    >>> guideline = Guideline()
1488    >>> guideline.identifier = "guideline 1"
1489    >>> glyph.appendGuideline(guideline)
1490    >>> guideline = Guideline()
1491    >>> guideline.identifier = "guideline 2"
1492    >>> glyph.appendGuideline(guideline)
1493
1494    >>> for contour in glyph:
1495    ...     contour.identifier
1496    'contour 1'
1497    'contour 2'
1498    >>> for point in glyph[0]:
1499    ...     point.identifier
1500    'point 1'
1501    'point 2'
1502    >>> for component in glyph.components:
1503    ...     component.identifier
1504    'component 1'
1505    'component 2'
1506
1507    >>> pointPen.beginPath(identifier="contour 1")
1508    >>> pointPen.endPath()
1509    Traceback (most recent call last):
1510        ...
1511    AssertionError
1512
1513    >>> pointPen.beginPath()
1514    >>> pointPen.addPoint((0, 0))
1515    >>> pointPen.addPoint((0, 0), identifier="point 1")
1516    >>> pointPen.endPath()
1517    Traceback (most recent call last):
1518        ...
1519    AssertionError
1520
1521    >>> pointPen.addComponent("A", (1, 1, 1, 1, 1, 1), identifier="component 1")
1522    Traceback (most recent call last):
1523        ...
1524    AssertionError
1525
1526    >>> g = Guideline()
1527    >>> g.identifier = "guideline 1"
1528    >>> glyph.appendGuideline(g)
1529    Traceback (most recent call last):
1530        ...
1531    AssertionError
1532
1533    >>> list(sorted(glyph.identifiers))
1534    ['component 1', 'component 2', 'contour 1', 'contour 2', 'guideline 1', 'guideline 2', 'point 1', 'point 2']
1535    >>> glyph.removeContour(glyph[0])
1536    >>> list(sorted(glyph.identifiers))
1537    ['component 1', 'component 2', 'contour 2', 'guideline 1', 'guideline 2']
1538    >>> glyph.removeComponent(glyph.components[0])
1539    >>> list(sorted(glyph.identifiers))
1540    ['component 2', 'contour 2', 'guideline 1', 'guideline 2']
1541    >>> glyph.removeGuideline(glyph.guidelines[0])
1542    >>> list(sorted(glyph.identifiers))
1543    ['component 2', 'contour 2', 'guideline 2']
1544    """
1545
1546if __name__ == "__main__":
1547    import doctest
1548    doctest.testmod()
Note: See TracBrowser for help on using the repository browser.