<s> import os <EOL> import re <EOL> import weakref <EOL> from fontTools . misc . arrayTools import unionRect <EOL> from robofab import ufoLib <EOL> from defcon . objects . base import BaseObject <EOL> from defcon . objects . glyph import Glyph <EOL> from defcon . objects . info import Info <EOL> from defcon . objects . kerning import Kerning <EOL> from defcon . objects . groups import Groups <EOL> from defcon . objects . features import Features <EOL> from defcon . objects . lib import Lib <EOL> from defcon . objects . uniData import UnicodeData <EOL> from defcon . tools . notifications import NotificationCenter <EOL> outlineSearchPointRE = re . compile ( <EOL> "<STR_LIT>" <EOL> "<STR_LIT>" <EOL> "<STR_LIT:>>" <EOL> ) <EOL> componentSearchRE = re . compile ( <EOL> "<STR_LIT>" <EOL> "<STR_LIT>" <EOL> "<STR_LIT>" <EOL> "<STR_LIT>" <EOL> "<STR_LIT>" <EOL> ) <EOL> controlBoundsPointRE = re . compile ( <EOL> "<STR_LIT>" <EOL> "<STR_LIT>" <EOL> "<STR_LIT:>>" <EOL> ) <EOL> controlBoundsPointXRE = re . compile ( <EOL> "<STR_LIT>" <EOL> "<STR_LIT>" <EOL> "<STR_LIT>" <EOL> "<STR_LIT>" <EOL> "<STR_LIT>" <EOL> ) <EOL> controlBoundsPointYRE = re . compile ( <EOL> "<STR_LIT>" <EOL> "<STR_LIT>" <EOL> "<STR_LIT>" <EOL> "<STR_LIT>" <EOL> "<STR_LIT>" <EOL> ) <EOL> controlBoundsComponentRE = re . compile ( <EOL> "<STR_LIT>" <EOL> "<STR_LIT>" <EOL> "<STR_LIT:>>" <EOL> ) <EOL> controlBoundsComponentBaseRE = re . compile ( <EOL> "<STR_LIT>" <EOL> "<STR_LIT>" <EOL> "<STR_LIT>" <EOL> ) <EOL> controlBoundsComponentXScaleRE = re . compile ( <EOL> "<STR_LIT>" <EOL> "<STR_LIT>" <EOL> "<STR_LIT>" <EOL> ) <EOL> controlBoundsComponentYScaleRE = re . compile ( <EOL> "<STR_LIT>" <EOL> "<STR_LIT>" <EOL> "<STR_LIT>" <EOL> ) <EOL> controlBoundsComponentXYScaleRE = re . compile ( <EOL> "<STR_LIT>" <EOL> "<STR_LIT>" <EOL> "<STR_LIT>" <EOL> ) <EOL> controlBoundsComponentYXScaleRE = re . compile ( <EOL> "<STR_LIT>" <EOL> "<STR_LIT>" <EOL> "<STR_LIT>" <EOL> ) <EOL> controlBoundsComponentXOffsetRE = re . compile ( <EOL> "<STR_LIT>" <EOL> "<STR_LIT>" <EOL> "<STR_LIT>" <EOL> ) <EOL> controlBoundsComponentYOffsetRE = re . compile ( <EOL> "<STR_LIT>" <EOL> "<STR_LIT>" <EOL> "<STR_LIT>" <EOL> ) <EOL> class Font ( BaseObject ) : <EOL> """<STR_LIT>""" <EOL> changeNotificationName = "<STR_LIT>" <EOL> def __init__ ( self , path = None , <EOL> kerningClass = None , infoClass = None , groupsClass = None , featuresClass = None , libClass = None , unicodeDataClass = None , <EOL> glyphClass = None , glyphContourClass = None , glyphPointClass = None , glyphComponentClass = None , glyphAnchorClass = None ) : <EOL> super ( Font , self ) . __init__ ( ) <EOL> if glyphClass is None : <EOL> glyphClass = Glyph <EOL> if infoClass is None : <EOL> infoClass = Info <EOL> if kerningClass is None : <EOL> kerningClass = Kerning <EOL> if groupsClass is None : <EOL> groupsClass = Groups <EOL> if featuresClass is None : <EOL> featuresClass = Features <EOL> if libClass is None : <EOL> libClass = Lib <EOL> if unicodeDataClass is None : <EOL> unicodeDataClass = UnicodeData <EOL> self . _dispatcher = NotificationCenter ( ) <EOL> self . _glyphClass = glyphClass <EOL> self . _glyphContourClass = glyphContourClass <EOL> self . _glyphPointClass = glyphPointClass <EOL> self . _glyphComponentClass = glyphComponentClass <EOL> self . _glyphAnchorClass = glyphAnchorClass <EOL> self . _kerningClass = kerningClass <EOL> self . _infoClass = infoClass <EOL> self . _groupsClass = groupsClass <EOL> self . _featuresClass = featuresClass <EOL> self . _libClass = libClass <EOL> self . _path = path <EOL> self . _ufoFormatVersion = None <EOL> self . _glyphs = { } <EOL> self . _glyphSet = None <EOL> self . _scheduledForDeletion = [ ] <EOL> self . _keys = set ( ) <EOL> self . _kerning = None <EOL> self . _info = None <EOL> self . _groups = None <EOL> self . _features = None <EOL> self . _lib = None <EOL> self . _unicodeData = unicodeDataClass ( ) <EOL> self . _unicodeData . setParent ( self ) <EOL> self . _dirty = False <EOL> if path : <EOL> reader = ufoLib . UFOReader ( self . _path ) <EOL> self . _ufoFormatVersion = reader . formatVersion <EOL> self . _glyphSet = reader . getGlyphSet ( ) <EOL> self . _keys = set ( self . _glyphSet . keys ( ) ) <EOL> self . _unicodeData . update ( reader . getCharacterMapping ( ) ) <EOL> if self . _ufoFormatVersion == <NUM_LIT:1> : <EOL> self . _convertFromFormatVersion1RoboFabData ( ) <EOL> self . _unicodeData . dispatcher = self . dispatcher <EOL> def _instantiateGlyphObject ( self ) : <EOL> glyph = self . _glyphClass ( <EOL> contourClass = self . _glyphContourClass , <EOL> pointClass = self . _glyphPointClass , <EOL> componentClass = self . _glyphComponentClass , <EOL> anchorClass = self . _glyphAnchorClass , <EOL> libClass = self . _libClass <EOL> ) <EOL> return glyph <EOL> def _loadGlyph ( self , name ) : <EOL> if self . _glyphSet is None or not self . _glyphSet . has_key ( name ) : <EOL> raise KeyError , '<STR_LIT>' % name <EOL> glyph = self . _instantiateGlyphObject ( ) <EOL> pointPen = glyph . getPointPen ( ) <EOL> self . _glyphSet . readGlyph ( glyphName = name , glyphObject = glyph , pointPen = pointPen ) <EOL> glyph . dirty = False <EOL> self . _glyphs [ name ] = glyph <EOL> self . _setParentDataInGlyph ( glyph ) <EOL> self . _stampGlyphDataState ( glyph ) <EOL> return glyph <EOL> def _setParentDataInGlyph ( self , glyph ) : <EOL> glyph . setParent ( self ) <EOL> glyph . dispatcher = self . dispatcher <EOL> glyph . addObserver ( observer = self , methodName = "<STR_LIT>" , notification = "<STR_LIT>" ) <EOL> glyph . addObserver ( observer = self , methodName = "<STR_LIT>" , notification = "<STR_LIT>" ) <EOL> glyph . addObserver ( observer = self , methodName = "<STR_LIT>" , notification = "<STR_LIT>" ) <EOL> def newGlyph ( self , name ) : <EOL> """<STR_LIT>""" <EOL> if name in self : <EOL> self . _unicodeData . removeGlyphData ( name , self [ name ] . unicodes ) <EOL> glyph = self . _instantiateGlyphObject ( ) <EOL> glyph . name = name <EOL> self . _glyphs [ name ] = glyph <EOL> self . _setParentDataInGlyph ( glyph ) <EOL> self . dirty = True <EOL> if name in self . _scheduledForDeletion : <EOL> self . _scheduledForDeletion . remove ( name ) <EOL> self . _keys . add ( name ) <EOL> def insertGlyph ( self , glyph , name = None ) : <EOL> """<STR_LIT>""" <EOL> from copy import deepcopy <EOL> source = glyph <EOL> if name is None : <EOL> name = source . name <EOL> self . newGlyph ( name ) <EOL> dest = self [ name ] <EOL> pointPen = dest . getPointPen ( ) <EOL> source . drawPoints ( pointPen ) <EOL> dest . width = source . width <EOL> dest . unicodes = list ( source . unicodes ) <EOL> dest . note = source . note <EOL> dest . lib = deepcopy ( source . lib ) <EOL> if dest . unicodes : <EOL> self . _unicodeData . addGlyphData ( name , dest . unicodes ) <EOL> return dest <EOL> def __iter__ ( self ) : <EOL> names = self . keys ( ) <EOL> while names : <EOL> name = names [ <NUM_LIT:0> ] <EOL> yield self [ name ] <EOL> names = names [ <NUM_LIT:1> : ] <EOL> def __getitem__ ( self , name ) : <EOL> if name not in self . _glyphs : <EOL> self . _loadGlyph ( name ) <EOL> return self . _glyphs [ name ] <EOL> def __delitem__ ( self , name ) : <EOL> if name not in self : <EOL> raise KeyError , '<STR_LIT>' % name <EOL> self . _unicodeData . removeGlyphData ( name , self [ name ] . unicodes ) <EOL> if name in self . _glyphs : <EOL> del self . _glyphs [ name ] <EOL> if name in self . _keys : <EOL> self . _keys . remove ( name ) <EOL> if self . _glyphSet is not None and name in self . _glyphSet : <EOL> self . _scheduledForDeletion . append ( name ) <EOL> self . dirty = True <EOL> def __len__ ( self ) : <EOL> return len ( self . keys ( ) ) <EOL> def __contains__ ( self , name ) : <EOL> return name in self . _keys <EOL> def keys ( self ) : <EOL> """<STR_LIT>""" <EOL> names = self . _keys <EOL> names = names - set ( self . _scheduledForDeletion ) <EOL> return list ( names ) <EOL> def _get_path ( self ) : <EOL> return self . _path <EOL> def _set_path ( self , path ) : <EOL> assert os . path . exists ( path ) <EOL> glyphsDir = os . path . join ( path , "<STR_LIT>" ) <EOL> assert os . path . exists ( glyphsDir ) <EOL> self . _path = path <EOL> if self . _glyphSet is not None : <EOL> self . _glyphSet . dirName = glyphsDir <EOL> path = property ( _get_path , _set_path , doc = "<STR_LIT>" ) <EOL> def _get_ufoFormatVersion ( self ) : <EOL> return self . _ufoFormatVersion <EOL> ufoFormatVersion = property ( _get_ufoFormatVersion , doc = "<STR_LIT>" ) <EOL> def _get_glyphsWithOutlines ( self ) : <EOL> found = [ ] <EOL> for glyphName , glyph in self . _glyphs . items ( ) : <EOL> if glyphName in self . _scheduledForDeletion : <EOL> continue <EOL> if len ( glyph ) : <EOL> found . append ( glyphName ) <EOL> glyphsPath = os . path . join ( self . path , "<STR_LIT>" ) <EOL> for glyphName , fileName in self . _glyphSet . contents . items ( ) : <EOL> if glyphName in self . _glyphs or glyphName in self . _scheduledForDeletion : <EOL> continue <EOL> glyphPath = os . path . join ( glyphsPath , fileName ) <EOL> f = open ( glyphPath , "<STR_LIT:rb>" ) <EOL> data = f . read ( ) <EOL> f . close ( ) <EOL> containsPoints = False <EOL> points = outlineSearchPointRE . findall ( data ) <EOL> for point in points : <EOL> if '<STR_LIT>' not in point : <EOL> containsPoints = True <EOL> break <EOL> if containsPoints : <EOL> found . append ( glyphName ) <EOL> return found <EOL> glyphsWithOutlines = property ( _get_glyphsWithOutlines , doc = "<STR_LIT>" ) <EOL> def _get_componentReferences ( self ) : <EOL> found = { } <EOL> for glyphName , glyph in self . _glyphs . items ( ) : <EOL> if glyphName in self . _scheduledForDeletion : <EOL> continue <EOL> if not glyph . components : <EOL> continue <EOL> for component in glyph . components : <EOL> baseGlyph = component . baseGlyph <EOL> if baseGlyph not in found : <EOL> found [ baseGlyph ] = set ( ) <EOL> found [ baseGlyph ] . add ( glyphName ) <EOL> if self . path is not None : <EOL> glyphsPath = os . path . join ( self . path , "<STR_LIT>" ) <EOL> for glyphName , fileName in self . _glyphSet . contents . items ( ) : <EOL> if glyphName in self . _glyphs or glyphName in self . _scheduledForDeletion : <EOL> continue <EOL> glyphPath = os . path . join ( glyphsPath , fileName ) <EOL> f = open ( glyphPath , "<STR_LIT:rb>" ) <EOL> data = f . read ( ) <EOL> f . close ( ) <EOL> baseGlyphs = componentSearchRE . findall ( data ) <EOL> for baseGlyph in baseGlyphs : <EOL> if baseGlyph not in found : <EOL> found [ baseGlyph ] = set ( ) <EOL> found [ baseGlyph ] . add ( glyphName ) <EOL> return found <EOL> componentReferences = property ( _get_componentReferences , doc = "<STR_LIT>" ) <EOL> def _get_bounds ( self ) : <EOL> fontRect = None <EOL> for glyph in self : <EOL> glyphRect = glyph . bounds <EOL> if glyphRect is None : <EOL> continue <EOL> if fontRect is None : <EOL> fontRect = glyphRect <EOL> else : <EOL> fontRect = unionRect ( fontRect , glyphRect ) <EOL> return fontRect <EOL> bounds = property ( _get_bounds , doc = "<STR_LIT>" ) <EOL> def _get_controlPointBounds ( self ) : <EOL> from fontTools . misc . transform import Transform <EOL> glyphRects = { } <EOL> componentReferences = { } <EOL> for glyphName , glyph in self . _glyphs . items ( ) : <EOL> if glyphName in self . _scheduledForDeletion : <EOL> continue <EOL> glyphRect = glyph . controlPointBounds <EOL> if glyphRect : <EOL> glyphRects [ glyphName ] = glyphRect <EOL> if self . path is not None : <EOL> glyphsPath = os . path . join ( self . path , "<STR_LIT>" ) <EOL> for glyphName , fileName in self . _glyphSet . contents . items ( ) : <EOL> if glyphName in self . _glyphs or glyphName in self . _scheduledForDeletion : <EOL> continue <EOL> glyphPath = os . path . join ( glyphsPath , fileName ) <EOL> f = open ( glyphPath , "<STR_LIT:rb>" ) <EOL> data = f . read ( ) <EOL> f . close ( ) <EOL> xMin = None <EOL> xMax = None <EOL> yMin = None <EOL> yMax = None <EOL> for line in controlBoundsPointRE . findall ( data ) : <EOL> x = controlBoundsPointXRE . findall ( line ) <EOL> if not x : <EOL> continue <EOL> y = controlBoundsPointYRE . findall ( line ) <EOL> if not y : <EOL> continue <EOL> x = int ( x [ <NUM_LIT:0> ] ) <EOL> if xMin is None : <EOL> xMin = xMax = x <EOL> if xMin > x : <EOL> xMin = x <EOL> if xMax < x : <EOL> xMax = x <EOL> y = int ( y [ <NUM_LIT:0> ] ) <EOL> if yMin is None : <EOL> yMin = yMax = y <EOL> if yMin > y : <EOL> yMin = y <EOL> if yMax < y : <EOL> yMax = y <EOL> glyphRect = ( xMin , yMin , xMax , yMax ) <EOL> if None not in glyphRect : <EOL> glyphRects [ glyphName ] = glyphRect <EOL> for line in controlBoundsComponentRE . findall ( data ) : <EOL> base = controlBoundsComponentBaseRE . findall ( line ) <EOL> if not base : <EOL> continue <EOL> base = base [ <NUM_LIT:0> ] <EOL> xScale = controlBoundsComponentXScaleRE . findall ( line ) <EOL> if not xScale : <EOL> xScale = [ <NUM_LIT:1> ] <EOL> xScale = float ( xScale [ <NUM_LIT:0> ] ) <EOL> yScale = controlBoundsComponentYScaleRE . findall ( line ) <EOL> if not yScale : <EOL> yScale = [ <NUM_LIT:1> ] <EOL> yScale = float ( yScale [ <NUM_LIT:0> ] ) <EOL> xyScale = controlBoundsComponentXYScaleRE . findall ( line ) <EOL> if not xyScale : <EOL> xyScale = [ <NUM_LIT:0> ] <EOL> xyScale = float ( xyScale [ <NUM_LIT:0> ] ) <EOL> yxScale = controlBoundsComponentYXScaleRE . findall ( line ) <EOL> if not yxScale : <EOL> yxScale = [ <NUM_LIT:0> ] <EOL> yxScale = float ( yxScale [ <NUM_LIT:0> ] ) <EOL> xOffset = controlBoundsComponentXOffsetRE . findall ( line ) <EOL> if not xOffset : <EOL> xOffset = [ <NUM_LIT:0> ] <EOL> xOffset = int ( xOffset [ <NUM_LIT:0> ] ) <EOL> yOffset = controlBoundsComponentYOffsetRE . findall ( line ) <EOL> if not yOffset : <EOL> yOffset = [ <NUM_LIT:0> ] <EOL> yOffset = int ( yOffset [ <NUM_LIT:0> ] ) <EOL> if glyphName not in componentReferences : <EOL> componentReferences [ glyphName ] = [ ] <EOL> componentReferences [ glyphName ] . append ( ( base , xScale , xyScale , yxScale , yScale , xOffset , yOffset ) ) <EOL> for glyphName , components in componentReferences . items ( ) : <EOL> glyphRect = glyphRects . get ( glyphName , ( None , None , None , None ) ) <EOL> for base , xScale , xyScale , yxScale , yScale , xOffset , yOffset in components : <EOL> if base not in glyphRects : <EOL> continue <EOL> baseRect = glyphRects [ base ] <EOL> if None in baseRect : <EOL> continue <EOL> transform = Transform ( xx = xScale , xy = xyScale , yx = yxScale , yy = yScale , dx = xOffset , dy = yOffset ) <EOL> xMin , yMin , xMax , yMax = baseRect <EOL> ( xMin , yMin ) , ( xMax , yMax ) = transform . transformPoints ( [ ( xMin , yMin ) , ( xMax , yMax ) ] ) <EOL> componentRect = ( xMin , yMin , xMax , yMax ) <EOL> if None in glyphRect : <EOL> glyphRect = componentRect <EOL> else : <EOL> glyphRect = unionRect ( glyphRect , componentRect ) <EOL> glyphRects [ glyphName ] = glyphRect <EOL> fontRect = None <EOL> for glyphRect in glyphRects . values ( ) : <EOL> if fontRect is None : <EOL> fontRect = glyphRect <EOL> elif glyphRect is not None : <EOL> fontRect = unionRect ( fontRect , glyphRect ) <EOL> return fontRect <EOL> controlPointBounds = property ( _get_controlPointBounds , doc = "<STR_LIT>" ) <EOL> def _get_info ( self ) : <EOL> if self . _info is None : <EOL> self . _info = self . _infoClass ( ) <EOL> self . _info . dispatcher = self . dispatcher <EOL> self . _info . setParent ( self ) <EOL> if self . _path is not None : <EOL> reader = ufoLib . UFOReader ( self . _path ) <EOL> reader . readInfo ( self . _info ) <EOL> self . _info . dirty = False <EOL> self . _info . addObserver ( observer = self , methodName = "<STR_LIT>" , notification = "<STR_LIT>" ) <EOL> self . _stampInfoDataState ( ) <EOL> return self . _info <EOL> info = property ( _get_info , doc = "<STR_LIT>" ) <EOL> def _get_kerning ( self ) : <EOL> if self . _kerning is None : <EOL> self . _kerning = self . _kerningClass ( ) <EOL> self . _kerning . dispatcher = self . dispatcher <EOL> self . _kerning . setParent ( self ) <EOL> if self . _path is not None : <EOL> reader = ufoLib . UFOReader ( self . _path ) <EOL> d = reader . readKerning ( ) <EOL> self . _kerning . update ( d ) <EOL> self . _kerning . dirty = False <EOL> self . _kerning . addObserver ( observer = self , methodName = "<STR_LIT>" , notification = "<STR_LIT>" ) <EOL> self . _stampKerningDataState ( ) <EOL> return self . _kerning <EOL> kerning = property ( _get_kerning , doc = "<STR_LIT>" ) <EOL> def _get_groups ( self ) : <EOL> if self . _groups is None : <EOL> self . _groups = self . _groupsClass ( ) <EOL> self . _groups . dispatcher = self . dispatcher <EOL> self . _groups . setParent ( self ) <EOL> if self . _path is not None : <EOL> reader = ufoLib . UFOReader ( self . _path ) <EOL> d = reader . readGroups ( ) <EOL> self . _groups . update ( d ) <EOL> self . _groups . dirty = False <EOL> self . _groups . addObserver ( observer = self , methodName = "<STR_LIT>" , notification = "<STR_LIT>" ) <EOL> self . _stampGroupsDataState ( ) <EOL> return self . _groups <EOL> groups = property ( _get_groups , doc = "<STR_LIT>" ) <EOL> def _get_features ( self ) : <EOL> if self . _features is None : <EOL> self . _features = self . _featuresClass ( ) <EOL> self . _features . dispatcher = self . dispatcher <EOL> self . _features . setParent ( self ) <EOL> if self . _path is not None : <EOL> reader = ufoLib . UFOReader ( self . _path ) <EOL> t = reader . readFeatures ( ) <EOL> self . _features . text = t <EOL> self . _features . dirty = False <EOL> self . _features . addObserver ( observer = self , methodName = "<STR_LIT>" , notification = "<STR_LIT>" ) <EOL> self . _stampFeaturesDataState ( ) <EOL> return self . _features <EOL> features = property ( _get_features , doc = "<STR_LIT>" ) <EOL> def _get_lib ( self ) : <EOL> if self . _lib is None : <EOL> self . _lib = self . _libClass ( ) <EOL> self . _lib . dispatcher = self . dispatcher <EOL> self . _lib . setParent ( self ) <EOL> if self . _path is not None : <EOL> reader = ufoLib . UFOReader ( self . _path ) <EOL> d = reader . readLib ( ) <EOL> self . _lib . update ( d ) <EOL> self . _lib . addObserver ( observer = self , methodName = "<STR_LIT>" , notification = "<STR_LIT>" ) <EOL> self . _stampLibDataState ( ) <EOL> return self . _lib <EOL> lib = property ( _get_lib , doc = "<STR_LIT>" ) <EOL> def _get_unicodeData ( self ) : <EOL> return self . _unicodeData <EOL> unicodeData = property ( _get_unicodeData , doc = "<STR_LIT>" ) <EOL> def save ( self , path = None , formatVersion = None ) : <EOL> """<STR_LIT>""" <EOL> saveAs = False <EOL> if path is not None and path != self . _path : <EOL> saveAs = True <EOL> else : <EOL> path = self . _path <EOL> if formatVersion is None and self . _ufoFormatVersion is not None : <EOL> formatVersion = self . _ufoFormatVersion <EOL> elif self . _ufoFormatVersion is None : <EOL> formatVersion = <NUM_LIT:2> <EOL> ufoWriter = ufoLib . UFOWriter ( path , formatVersion = formatVersion ) <EOL> saveInfo = False <EOL> saveKerning = False <EOL> saveGroups = False <EOL> saveFeatures = False <EOL> saveLib = True <EOL> if saveAs : <EOL> saveInfo = True <EOL> saveKerning = True <EOL> saveGroups = True <EOL> saveFeatures = True <EOL> if self . _ufoFormatVersion != formatVersion : <EOL> saveInfo = True <EOL> saveKerning = True <EOL> saveGroups = True <EOL> saveFeatures = True <EOL> if self . _info is not None and self . _info . dirty : <EOL> saveInfo = True <EOL> if self . _kerning is not None and self . _kerning . dirty : <EOL> saveKerning = True <EOL> if self . _features is not None and self . _features . dirty : <EOL> saveFeatures = True <EOL> if self . _groups is not None : <EOL> saveGroups = True <EOL> if self . _lib is not None : <EOL> saveLib = True <EOL> if saveInfo : <EOL> ufoWriter . writeInfo ( self . info ) <EOL> self . _stampInfoDataState ( ) <EOL> self . info . dirty = False <EOL> if saveKerning : <EOL> ufoWriter . writeKerning ( self . kerning ) <EOL> self . _stampKerningDataState ( ) <EOL> self . kerning . dirty = False <EOL> if saveGroups : <EOL> ufoWriter . writeGroups ( self . groups ) <EOL> self . _stampGroupsDataState ( ) <EOL> if saveFeatures and formatVersion > <NUM_LIT:1> : <EOL> ufoWriter . writeFeatures ( self . features . text ) <EOL> self . _stampFeaturesDataState ( ) <EOL> if saveLib : <EOL> libCopy = dict ( self . lib ) <EOL> if formatVersion == <NUM_LIT:1> : <EOL> self . _convertToFormatVersion1RoboFabData ( libCopy ) <EOL> ufoWriter . writeLib ( libCopy ) <EOL> self . _stampLibDataState ( ) <EOL> if saveAs : <EOL> for glyph in self : <EOL> glyph . dirty = True <EOL> glyphSet = ufoWriter . getGlyphSet ( ) <EOL> for glyphName , glyphObject in self . _glyphs . items ( ) : <EOL> if glyphObject . dirty : <EOL> glyphSet . writeGlyph ( glyphName , glyphObject , glyphObject . drawPoints ) <EOL> self . _stampGlyphDataState ( glyphObject ) <EOL> if not saveAs and self . _scheduledForDeletion : <EOL> for glyphName in self . _scheduledForDeletion : <EOL> if glyphName in glyphSet : <EOL> glyphSet . deleteGlyph ( glyphName ) <EOL> glyphSet . writeContents ( ) <EOL> self . _glyphSet = glyphSet <EOL> self . _scheduledForDeletion = [ ] <EOL> self . _path = path <EOL> self . _ufoFormatVersion = formatVersion <EOL> self . dirty = False <EOL> def _objectDirtyStateChange ( self , notification ) : <EOL> if notification . object . dirty : <EOL> self . dirty = True <EOL> def _glyphNameChange ( self , notification ) : <EOL> data = notification . data <EOL> oldName = data [ "<STR_LIT>" ] <EOL> newName = data [ "<STR_LIT>" ] <EOL> glyph = self . _glyphs [ oldName ] <EOL> del self [ oldName ] <EOL> self . _glyphs [ newName ] = glyph <EOL> self . _keys . add ( newName ) <EOL> self . _unicodeData . removeGlyphData ( oldName , glyph . unicodes ) <EOL> self . _unicodeData . addGlyphData ( newName , glyph . unicodes ) <EOL> def _glyphUnicodesChange ( self , notification ) : <EOL> glyphName = notification . object . name <EOL> data = notification . data <EOL> oldValues = data [ "<STR_LIT>" ] <EOL> newValues = data [ "<STR_LIT>" ] <EOL> self . _unicodeData . removeGlyphData ( glyphName , oldValues ) <EOL> self . _unicodeData . addGlyphData ( glyphName , newValues ) <EOL> def _stampFontDataState ( self , obj , fileName ) : <EOL> if self . _path is None : <EOL> return <EOL> if obj is None : <EOL> return <EOL> path = os . path . join ( self . _path , fileName ) <EOL> if not os . path . exists ( path ) : <EOL> text = None <EOL> modTime = - <NUM_LIT:1> <EOL> else : <EOL> f = open ( path , "<STR_LIT:rb>" ) <EOL> text = f . read ( ) <EOL> f . close ( ) <EOL> modTime = os . stat ( path ) . st_mtime <EOL> obj . _dataOnDisk = text <EOL> obj . _dataOnDiskTimeStamp = modTime <EOL> def _stampInfoDataState ( self ) : <EOL> self . _stampFontDataState ( self . _info , "<STR_LIT>" ) <EOL> def _stampKerningDataState ( self ) : <EOL> self . _stampFontDataState ( self . _kerning , "<STR_LIT>" ) <EOL> def _stampGroupsDataState ( self ) : <EOL> self . _stampFontDataState ( self . _groups , "<STR_LIT>" ) <EOL> def _stampFeaturesDataState ( self ) : <EOL> self . _stampFontDataState ( self . _features , "<STR_LIT>" ) <EOL> def _stampLibDataState ( self ) : <EOL> self . _stampFontDataState ( self . _lib , "<STR_LIT>" ) <EOL> def _stampGlyphDataState ( self , glyph ) : <EOL> if self . _glyphSet is None : <EOL> return <EOL> glyphSet = self . _glyphSet <EOL> glyphName = glyph . name <EOL> if glyphName not in glyphSet . contents : <EOL> return <EOL> path = os . path . join ( self . path , "<STR_LIT>" , glyphSet . contents [ glyphName ] ) <EOL> f = open ( path , "<STR_LIT:rb>" ) <EOL> text = f . read ( ) <EOL> f . close ( ) <EOL> modTime = os . stat ( path ) . st_mtime <EOL> glyph . _dataOnDisk = text <EOL> glyph . _dataOnDiskTimeStamp = modTime <EOL> def testForExternalChanges ( self ) : <EOL> """<STR_LIT>""" <EOL> infoChanged = self . _testInfoForExternalModifications ( ) <EOL> kerningChanged = self . _testKerningForExternalModifications ( ) <EOL> groupsChanged = self . _testGroupsForExternalModifications ( ) <EOL> featuresChanged = self . _testFeaturesForExternalModifications ( ) <EOL> libChanged = self . _testLibForExternalModifications ( ) <EOL> modifiedGlyphs , addedGlyphs , deletedGlyphs = self . _testGlyphsForExternalModifications ( ) <EOL> return dict ( <EOL> info = infoChanged , <EOL> kerning = kerningChanged , <EOL> groups = groupsChanged , <EOL> features = featuresChanged , <EOL> lib = libChanged , <EOL> modifiedGlyphs = modifiedGlyphs , <EOL> addedGlyphs = addedGlyphs , <EOL> deletedGlyphs = deletedGlyphs <EOL> ) <EOL> def _testFontDataForExternalModifications ( self , obj , fileName ) : <EOL> if self . _path is None : <EOL> return False <EOL> if obj is None : <EOL> return False <EOL> path = os . path . join ( self . _path , fileName ) <EOL> if not os . path . exists ( path ) : <EOL> if obj . _dataOnDisk : <EOL> return True <EOL> return False <EOL> modTime = os . stat ( path ) . st_mtime <EOL> if obj . _dataOnDiskTimeStamp != modTime : <EOL> f = open ( path , "<STR_LIT:rb>" ) <EOL> text = f . read ( ) <EOL> f . close ( ) <EOL> if text != obj . _dataOnDisk : <EOL> return True <EOL> return False <EOL> def _testInfoForExternalModifications ( self ) : <EOL> return self . _testFontDataForExternalModifications ( self . _info , "<STR_LIT>" ) <EOL> def _testKerningForExternalModifications ( self ) : <EOL> return self . _testFontDataForExternalModifications ( self . _kerning , "<STR_LIT>" ) <EOL> def _testGroupsForExternalModifications ( self ) : <EOL> return self . _testFontDataForExternalModifications ( self . _groups , "<STR_LIT>" ) <EOL> def _testFeaturesForExternalModifications ( self ) : <EOL> return self . _testFontDataForExternalModifications ( self . _features , "<STR_LIT>" ) <EOL> def _testLibForExternalModifications ( self ) : <EOL> return self . _testFontDataForExternalModifications ( self . _lib , "<STR_LIT>" ) <EOL> def _testGlyphsForExternalModifications ( self ) : <EOL> if self . _glyphSet is None : <EOL> return [ ] , [ ] , [ ] <EOL> glyphSet = self . _glyphSet <EOL> glyphSet . rebuildContents ( ) <EOL> addedGlyphs = list ( set ( self . _glyphSet . keys ( ) ) - self . _keys ) <EOL> deletedGlyphs = list ( self . _keys - set ( self . _glyphSet . keys ( ) ) ) <EOL> modifiedGlyphs = [ ] <EOL> for glyphName , glyph in self . _glyphs . items ( ) : <EOL> if glyphName not in glyphSet . contents : <EOL> continue <EOL> path = os . path . join ( self . path , "<STR_LIT>" , glyphSet . contents [ glyphName ] ) <EOL> modTime = os . stat ( path ) . st_mtime <EOL> if modTime != glyph . _dataOnDiskTimeStamp : <EOL> f = open ( path , "<STR_LIT:rb>" ) <EOL> text = f . read ( ) <EOL> f . close ( ) <EOL> if text != glyph . _dataOnDisk : <EOL> modifiedGlyphs . append ( glyphName ) <EOL> self . _keys = self . _keys | set ( addedGlyphs ) <EOL> return modifiedGlyphs , addedGlyphs , deletedGlyphs <EOL> def reloadInfo ( self ) : <EOL> """<STR_LIT>""" <EOL> if self . _info is None : <EOL> obj = self . info <EOL> else : <EOL> r = ufoLib . UFOReader ( self . _path ) <EOL> newInfo = Info ( ) <EOL> r . readInfo ( newInfo ) <EOL> oldInfo = self . _info <EOL> for attr in dir ( newInfo ) : <EOL> if attr in ufoLib . deprecatedFontInfoAttributesVersion2 : <EOL> continue <EOL> if attr . startswith ( "<STR_LIT:_>" ) : <EOL> continue <EOL> if attr == "<STR_LIT>" : <EOL> continue <EOL> if attr == "<STR_LIT>" : <EOL> continue <EOL> if not hasattr ( oldInfo , attr ) : <EOL> continue <EOL> newValue = getattr ( newInfo , attr ) <EOL> oldValue = getattr ( oldInfo , attr ) <EOL> if hasattr ( newValue , "<STR_LIT>" ) : <EOL> continue <EOL> if oldValue == newValue : <EOL> continue <EOL> setattr ( oldInfo , attr , newValue ) <EOL> self . _stampInfoDataState ( ) <EOL> def reloadKerning ( self ) : <EOL> """<STR_LIT>""" <EOL> if self . _kerning is None : <EOL> obj = self . kerning <EOL> else : <EOL> r = ufoLib . UFOReader ( self . _path ) <EOL> d = r . readKerning ( ) <EOL> self . _kerning . clear ( ) <EOL> self . _kerning . update ( d ) <EOL> self . _stampKerningDataState ( ) <EOL> def reloadGroups ( self ) : <EOL> """<STR_LIT>""" <EOL> if self . _groups is None : <EOL> obj = self . groups <EOL> else : <EOL> r = ufoLib . UFOReader ( self . _path ) <EOL> d = r . readGroups ( ) <EOL> self . _groups . clear ( ) <EOL> self . _groups . update ( d ) <EOL> self . _stampGroupsDataState ( ) <EOL> def reloadFeatures ( self ) : <EOL> """<STR_LIT>""" <EOL> if self . _features is None : <EOL> obj = self . features <EOL> else : <EOL> r = ufoLib . UFOReader ( self . _path ) <EOL> text = r . readFeatures ( ) <EOL> self . _features . text = text <EOL> self . _stampFeaturesDataState ( ) <EOL> def reloadLib ( self ) : <EOL> """<STR_LIT>""" <EOL> if self . _lib is None : <EOL> obj = self . lib <EOL> else : <EOL> r = ufoLib . UFOReader ( self . _path ) <EOL> d = r . readLib ( ) <EOL> self . _lib . clear ( ) <EOL> self . _lib . update ( d ) <EOL> self . _stampLibDataState ( ) <EOL> def reloadGlyphs ( self , glyphNames ) : <EOL> """<STR_LIT>""" <EOL> for glyphName in glyphNames : <EOL> if glyphName not in self . _glyphs : <EOL> self . _loadGlyph ( glyphName ) <EOL> else : <EOL> glyph = self . _glyphs [ glyphName ] <EOL> glyph . destroyAllRepresentations ( None ) <EOL> glyph . clear ( ) <EOL> pointPen = glyph . getPointPen ( ) <EOL> self . _glyphSet . readGlyph ( glyphName = glyphName , glyphObject = glyph , pointPen = pointPen ) <EOL> glyph . dirty = False <EOL> self . _stampGlyphDataState ( glyph ) <EOL> data = dict ( glyphNames = glyphNames ) <EOL> self . dispatcher . postNotification ( notification = "<STR_LIT>" , observable = self , data = data ) <EOL> componentReferences = self . componentReferences <EOL> referenceChanges = set ( ) <EOL> for glyphName in glyphNames : <EOL> if glyphName not in componentReferences : <EOL> continue <EOL> for reference in componentReferences [ glyphName ] : <EOL> if reference in glyphNames : <EOL> continue <EOL> if reference not in self . _glyphs : <EOL> continue <EOL> if reference in referenceChanges : <EOL> continue <EOL> glyph = self . _glyphs [ reference ] <EOL> glyph . destroyAllRepresentations ( None ) <EOL> glyph . dispatcher . postNotification ( notification = glyph . changeNotificationName , observable = glyph ) <EOL> referenceChanges . add ( reference ) <EOL> def _convertFromFormatVersion1RoboFabData ( self ) : <EOL> features = [ ] <EOL> classes = self . lib . get ( "<STR_LIT>" ) <EOL> if classes is not None : <EOL> del self . lib [ "<STR_LIT>" ] <EOL> features . append ( classes ) <EOL> splitFeatures = self . lib . get ( "<STR_LIT>" ) <EOL> if splitFeatures is not None : <EOL> order = self . lib . get ( "<STR_LIT>" ) <EOL> if order is None : <EOL> order = splitFeatures . keys ( ) <EOL> order . sort ( ) <EOL> else : <EOL> del self . lib [ "<STR_LIT>" ] <EOL> del self . lib [ "<STR_LIT>" ] <EOL> for tag in order : <EOL> oneFeature = splitFeatures . get ( tag ) <EOL> if oneFeature is not None : <EOL> features . append ( oneFeature ) <EOL> self . features . text = "<STR_LIT:\n>" . join ( features ) <EOL> hintData = self . lib . get ( "<STR_LIT>" ) <EOL> if hintData is not None : <EOL> del self . lib [ "<STR_LIT>" ] <EOL> blueFuzz = hintData . get ( "<STR_LIT>" ) <EOL> if blueFuzz is not None : <EOL> self . info . postscriptBlueFuzz = blueFuzz <EOL> blueScale = hintData . get ( "<STR_LIT>" ) <EOL> if blueScale is not None : <EOL> self . info . postscriptBlueScale = blueScale <EOL> blueShift = hintData . get ( "<STR_LIT>" ) <EOL> if blueShift is not None : <EOL> self . info . postscriptBlueShift = blueShift <EOL> forceBold = hintData . get ( "<STR_LIT>" ) <EOL> if forceBold is not None : <EOL> self . info . postscriptForceBold = forceBold <EOL> vStems = hintData . get ( "<STR_LIT>" ) <EOL> if vStems is not None : <EOL> self . info . postscriptStemSnapV = vStems <EOL> hStems = hintData . get ( "<STR_LIT>" ) <EOL> if hStems is not None : <EOL> self . info . postscriptStemSnapH = hStems <EOL> bluePairs = [ <EOL> ( "<STR_LIT>" , "<STR_LIT>" ) , <EOL> ( "<STR_LIT>" , "<STR_LIT>" ) , <EOL> ( "<STR_LIT>" , "<STR_LIT>" ) , <EOL> ( "<STR_LIT>" , "<STR_LIT>" ) , <EOL> ] <EOL> for infoAttr , libKey in bluePairs : <EOL> libValue = hintData . get ( libKey ) <EOL> if libValue is not None : <EOL> value = [ ] <EOL> for i , j in libValue : <EOL> value . append ( i ) <EOL> value . append ( j ) <EOL> setattr ( self . info , infoAttr , value ) <EOL> def _convertToFormatVersion1RoboFabData ( self , libCopy ) : <EOL> from robofab . tools . fontlabFeatureSplitter import splitFeaturesForFontLab <EOL> features = self . features . text <EOL> classes , features = splitFeaturesForFontLab ( features ) <EOL> if classes : <EOL> libCopy [ "<STR_LIT>" ] = classes . strip ( ) + "<STR_LIT:\n>" <EOL> if features : <EOL> featureDict = { } <EOL> for featureName , featureText in features : <EOL> featureDict [ featureName ] = featureText . strip ( ) + "<STR_LIT:\n>" <EOL> libCopy [ "<STR_LIT>" ] = featureDict <EOL> libCopy [ "<STR_LIT>" ] = [ featureName for featureName , featureText in features ] <EOL> hintData = dict ( <EOL> blueFuzz = self . info . postscriptBlueFuzz , <EOL> blueScale = self . info . postscriptBlueScale , <EOL> blueShift = self . info . postscriptBlueShift , <EOL> forceBold = self . info . postscriptForceBold , <EOL> vStems = self . info . postscriptStemSnapV , <EOL> hStems = self . info . postscriptStemSnapH <EOL> ) <EOL> bluePairs = [ <EOL> ( "<STR_LIT>" , "<STR_LIT>" ) , <EOL> ( "<STR_LIT>" , "<STR_LIT>" ) , <EOL> ( "<STR_LIT>" , "<STR_LIT>" ) , <EOL> ( "<STR_LIT>" , "<STR_LIT>" ) , <EOL> ] <EOL> for infoAttr , libKey in bluePairs : <EOL> values = getattr ( self . info , infoAttr ) <EOL> if values is not None : <EOL> finalValues = [ ] <EOL> for value in values : <EOL> if not finalValues or len ( finalValues [ - <NUM_LIT:1> ] ) == <NUM_LIT:2> : <EOL> finalValues . append ( [ ] ) <EOL> finalValues [ - <NUM_LIT:1> ] . append ( value ) <EOL> hintData [ libKey ] = finalValues <EOL> for key , value in hintData . items ( ) : <EOL> if value is None : <EOL> del hintData [ key ] <EOL> libCopy [ "<STR_LIT>" ] = hintData <EOL> def _testSetParentDataInGlyph ( ) : <EOL> """<STR_LIT>""" <EOL> def _testNewGlyph ( ) : <EOL> """<STR_LIT>""" <EOL> def _testInsertGlyph ( ) : <EOL> """<STR_LIT>""" <EOL> def _testIter ( ) : <EOL> """<STR_LIT>""" <EOL> def _testGetitem ( ) : <EOL> """<STR_LIT>""" <EOL> def _testDelitem ( ) : <EOL> """<STR_LIT>""" <EOL> def _testLen ( ) : <EOL> """<STR_LIT>""" <EOL> def _testContains ( ) : <EOL> """<STR_LIT>""" <EOL> def _testKeys ( ) : <EOL> """<STR_LIT>""" <EOL> def _testPath ( ) : <EOL> """<STR_LIT>""" <EOL> def _testGlyphWithOutlines ( ) : <EOL> """<STR_LIT>""" <EOL> def _testComponentReferences ( ) : <EOL> """<STR_LIT>""" <EOL> def _testBounds ( ) : <EOL> """<STR_LIT>""" <EOL> def _testControlPointBounds ( ) : <EOL> """<STR_LIT>""" <EOL> def _testSave ( ) : <EOL> """<STR_LIT>""" <EOL> def _testGlyphNameChange ( ) : <EOL> """<STR_LIT>""" <EOL> def _testGlyphUnicodesChanged ( ) : <EOL> """<STR_LIT>""" <EOL> def _testTestForExternalChanges ( ) : <EOL> """<STR_LIT>""" <EOL> def _testReloadInfo ( ) : <EOL> """<STR_LIT>""" <EOL> def _testReloadKerning ( ) : <EOL> """<STR_LIT>""" <EOL> def _testReloadGroups ( ) : <EOL> """<STR_LIT>""" <EOL> def _testReloadLib ( ) : <EOL> """<STR_LIT>""" <EOL> def _testReloadGlyphs ( ) : <EOL> """<STR_LIT>""" <EOL> if __name__ == "<STR_LIT:__main__>" : <EOL> import doctest <EOL> doctest . testmod ( ) </s>
