| 1 | from fontTools.misc import arrayTools |
|---|
| 2 | from defcon.objects.base import BaseObject |
|---|
| 3 | from defcon.objects.contour import Contour |
|---|
| 4 | from defcon.objects.point import Point |
|---|
| 5 | from defcon.objects.component import Component |
|---|
| 6 | from defcon.objects.anchor import Anchor |
|---|
| 7 | from defcon.objects.lib import Lib |
|---|
| 8 | from defcon.objects.guideline import Guideline |
|---|
| 9 | from defcon.objects.image import Image |
|---|
| 10 | from defcon.objects.color import Color |
|---|
| 11 | |
|---|
| 12 | def 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 | |
|---|
| 17 | def 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 | |
|---|
| 23 | class 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 | |
|---|
| 918 | def _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 | |
|---|
| 948 | def _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 | |
|---|
| 970 | def _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 | |
|---|
| 986 | def _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 | |
|---|
| 1002 | def _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 | |
|---|
| 1029 | def _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 | |
|---|
| 1053 | def _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 | |
|---|
| 1075 | def _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 | |
|---|
| 1097 | def _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 | |
|---|
| 1107 | def _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 | |
|---|
| 1117 | def _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 | |
|---|
| 1134 | def _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 | |
|---|
| 1193 | def _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 | |
|---|
| 1203 | def _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 | |
|---|
| 1215 | def _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 | |
|---|
| 1230 | def _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 | |
|---|
| 1245 | def _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 | |
|---|
| 1260 | def _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 | |
|---|
| 1275 | def _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 | |
|---|
| 1288 | def _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 | |
|---|
| 1301 | def _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 | |
|---|
| 1314 | def _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 | |
|---|
| 1327 | def _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 | |
|---|
| 1341 | def _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 | |
|---|
| 1355 | def _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 | |
|---|
| 1369 | def _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 | |
|---|
| 1399 | def _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 | |
|---|
| 1410 | def _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 | |
|---|
| 1421 | def _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 | |
|---|
| 1432 | def _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 | |
|---|
| 1443 | def _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 | |
|---|
| 1461 | def _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 | |
|---|
| 1475 | def _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 | |
|---|
| 1546 | if __name__ == "__main__": |
|---|
| 1547 | import doctest |
|---|
| 1548 | doctest.testmod() |
|---|